From f59fd1cd3b22e40dfeb23e3ec5e8caa361cccc23 Mon Sep 17 00:00:00 2001 From: Trevor Date: Wed, 1 Jul 2026 14:04:46 -0700 Subject: [PATCH 1/2] Migrate documentation to Mintlify Replaces the Jekyll-based site with a Mintlify docs setup: - docs.json navigation/config, index.mdx landing page, custom.css - Converts en/ and ja/ content to MDX - Adds assets, logo, and favicon Generated from the Mintlify preview at mintlify-onboarding/vespa-preview. --- .../actions/commit-updated-docs/action.yml | 61 - .github/actions/install-deps/action.yml | 56 - .../workflows/auto-update-documentation.yml | 168 - .github/workflows/build-pages.yml | 29 - .github/workflows/deploy-pages.yml | 66 - .github/workflows/feed.yml | 85 - .github/workflows/link-checker.yml | 42 - .github/workflows/verify-guides-large.yml | 24 - .github/workflows/verify-guides-small.yml | 27 - .github/workflows/verify-guides.yml | 44 - .gitignore | 19 - 404.html | 27 - CNAME | 1 - Gemfile | 51 - Gemfile.lock | 270 - _config.yml | 123 - _data/aws-instance-types.csv | 165 - _data/azure-instance-types.csv | 66 - _data/gcp-instance-types.csv | 268 - _data/sidebar.yml | 742 -- _includes/assets/images/commonmark-logo.svg | 4 - _includes/chip-cloud.html | 12 - _includes/chip-enterprise.html | 12 - _includes/chip-self-managed.html | 12 - _includes/deprecated.html | 4 - _includes/htmlhead.html | 91 - _includes/important.html | 4 - _includes/js.html | 6 - _includes/mode-content-hidden.html | 27 - _includes/mode-content-waring.html | 9 - _includes/navbar.html | 167 - _includes/new.html | 4 - _includes/note.html | 4 - _includes/pre-req.html | 35 - _includes/setup.html | 73 - _includes/sidebar.html | 186 - _includes/toc.html | 189 - _includes/version.html | 1 - _includes/video-include.html | 8 - _includes/warning.html | 4 - _layouts/default.html | 87 - _paragraphs_config.yml | 48 - _plugins-vespafeed/vespa_index_generator.rb | 169 - _plugins/html_to_markdown_generator.rb | 345 - _sass/denali-css/scss/components/_all.scss | 37 - _sass/denali-css/scss/components/alerts.scss | 271 - _sass/denali-css/scss/components/box.scss | 41 - .../scss/components/breadcrumb.scss | 54 - .../scss/components/button-group.scss | 27 - _sass/denali-css/scss/components/button.scss | 408 - .../denali-css/scss/components/checkbox.scss | 144 - _sass/denali-css/scss/components/chips.scss | 60 - _sass/denali-css/scss/components/icons.scss | 90 - .../scss/components/input-group.scss | 44 - _sass/denali-css/scss/components/inputs.scss | 265 - _sass/denali-css/scss/components/links.scss | 130 - _sass/denali-css/scss/components/lists.scss | 19 - _sass/denali-css/scss/components/loader.scss | 177 - _sass/denali-css/scss/components/menus.scss | 89 - _sass/denali-css/scss/components/modals.scss | 149 - _sass/denali-css/scss/components/navbar.scss | 298 - .../denali-css/scss/components/progress.scss | 125 - _sass/denali-css/scss/components/radio.scss | 107 - _sass/denali-css/scss/components/selects.scss | 175 - _sass/denali-css/scss/components/switch.scss | 179 - _sass/denali-css/scss/components/table.scss | 270 - _sass/denali-css/scss/components/tabs.scss | 260 - _sass/denali-css/scss/components/tags.scss | 169 - _sass/denali-css/scss/components/titles.scss | 38 - _sass/denali-css/scss/components/toggle.scss | 183 - _sass/denali-css/scss/components/tooltip.scss | 81 - .../scss/components/typography.scss | 125 - _sass/denali-css/scss/denali-helpers.scss | 14 - _sass/denali-css/scss/helpers/_all.scss | 18 - _sass/denali-css/scss/helpers/borders.scss | 14 - _sass/denali-css/scss/helpers/colors.scss | 83 - _sass/denali-css/scss/helpers/flexbox.scss | 124 - _sass/denali-css/scss/helpers/float.scss | 11 - _sass/denali-css/scss/helpers/hide.scss | 59 - _sass/denali-css/scss/helpers/margin.scss | 57 - _sass/denali-css/scss/helpers/misc.scss | 29 - _sass/denali-css/scss/helpers/padding.scss | 53 - _sass/denali-css/scss/helpers/shadows.scss | 22 - _sass/denali-css/scss/helpers/size.scss | 27 - _sass/denali-css/scss/helpers/text.scss | 24 - _sass/denali-css/scss/helpers/type.scss | 25 - _sass/denali-css/scss/maps/_all.scss | 8 - _sass/denali-css/scss/maps/color-maps.scss | 203 - _sass/denali-css/scss/mixins/_all.scss | 8 - _sass/denali-css/scss/mixins/breakpoints.scss | 143 - _sass/denali-css/scss/mixins/theme.scss | 67 - _sass/denali-css/scss/utilities/_all.scss | 12 - .../scss/utilities/color-functions.scss | 108 - .../scss/utilities/root-variables.scss | 452 - _suggestions_config.yml | 48 - _suggestions_reference_config.yml | 48 - amplify.yml | 21 - assets/graph-image.png | Bin 0 -> 72174 bytes assets/img/az-multi-cluster.svg | 17 - assets/img/az-multi-content.svg | 38 - assets/img/az-single-cluster.svg | 14 - assets/img/az-single-content.svg | 35 - assets/img/block-window.png | Bin 160431 -> 40409 bytes assets/img/console/reindex-completed.png | Bin 574822 -> 0 bytes assets/img/console/reindex-confirm.png | Bin 477699 -> 0 bytes assets/img/console/reindex-overview.png | Bin 574451 -> 0 bytes assets/img/relevance/ranktype-about.png | Bin assets/img/relevance/ranktype-tags.png | Bin assets/img/support-ticket.png | Bin 142071 -> 0 bytes assets/img/telemetry-export-architecture.mmd | 22 - assets/img/telemetry-export-architecture.png | Bin 142651 -> 0 bytes .../img/tutorials/rag-blueprint-overview.svg | 2 +- convert.py | 15 - css/denali-icon-font/denali-icon-font.css | 5106 ----------- css/denali-icon-font/denali-icon-font.eot | Bin 285948 -> 0 bytes css/denali-icon-font/denali-icon-font.svg | 3801 -------- css/denali-icon-font/denali-icon-font.ttf | Bin 285748 -> 0 bytes css/denali-icon-font/denali-icon-font.woff | Bin 128548 -> 0 bytes css/denali-theme-vespa-light.scss | 529 -- css/denali-theme-vespa.scss | 530 -- css/denali.css | 1 - css/native.css | 70 - css/style-light.scss | 18 - css/style.scss | 1174 --- css/typography.scss | 50 - custom.css | 109 + docs.json | 639 ++ en/applications/bundles.html | 904 -- en/applications/bundles.mdx | 730 ++ en/applications/chaining.html | 319 - en/applications/chaining.mdx | 209 + en/applications/components.html | 413 - en/applications/components.mdx | 259 + en/applications/config-system.html | 341 - en/applications/config-system.mdx | 170 + en/applications/configapi-dev.html | 581 -- en/applications/configapi-dev.mdx | 343 + en/applications/configuring-components.html | 223 - en/applications/configuring-components.mdx | 138 + en/applications/containers.html | 128 - en/applications/containers.mdx | 50 + en/applications/dependency-injection.html | 213 - en/applications/dependency-injection.mdx | 140 + en/applications/deployment.html | 168 - en/applications/deployment.mdx | 116 + en/applications/developer-guide.html | 318 - en/applications/developer-guide.mdx | 199 + en/applications/document-processors.html | 490 - en/applications/document-processors.mdx | 278 + en/applications/http-servers-and-filters.html | 264 - en/applications/http-servers-and-filters.mdx | 199 + en/applications/ide-support.html | 22 - en/applications/ide-support.mdx | 15 + ...ta.html => inspecting-structured-data.mdx} | 125 +- en/applications/pluggable-frameworks.html | 79 - en/applications/pluggable-frameworks.mdx | 53 + en/applications/processing.html | 434 - en/applications/processing.mdx | 325 + en/applications/request-handlers.html | 85 - en/applications/request-handlers.mdx | 53 + en/applications/result-renderers.html | 340 - en/applications/result-renderers.mdx | 273 + en/applications/searchers.html | 679 -- en/applications/searchers.mdx | 427 + en/applications/testing.html | 241 - en/applications/testing.mdx | 154 + en/applications/unit-testing.html | 200 - en/applications/unit-testing.mdx | 151 + en/applications/using-zookeeper.md | 68 - en/applications/using-zookeeper.mdx | 59 + en/applications/vespaignore.html | 67 - en/applications/vespaignore.mdx | 42 + en/applications/web-services.html | 217 - en/applications/web-services.mdx | 130 + en/basics/applications.html | 143 - en/basics/applications.mdx | 106 + en/basics/deploy-an-application-java.html | 108 - en/basics/deploy-an-application-java.mdx | 110 + .../deploy-an-application-local-java.html | 157 - .../deploy-an-application-local-java.mdx | 106 + en/basics/deploy-an-application-local.html | 165 - en/basics/deploy-an-application-local.mdx | 119 + en/basics/deploy-an-application-shell.html | 209 - en/basics/deploy-an-application-shell.mdx | 129 + en/basics/deploy-an-application.html | 84 - en/basics/deploy-an-application.mdx | 111 + en/basics/operations.html | 83 - en/basics/operations.mdx | 46 + en/basics/querying.html | 118 - en/basics/querying.mdx | 91 + en/basics/ranking.html | 180 - en/basics/ranking.mdx | 134 + en/basics/schemas.html | 147 - en/basics/schemas.mdx | 103 + en/basics/whats-more.html | 48 - en/basics/whats-more.mdx | 17 + en/basics/writing.html | 110 - en/basics/writing.mdx | 70 + en/clients/http-best-practices.md | 69 - en/clients/http-best-practices.mdx | 43 + en/clients/python-client.mdx | 4 + en/clients/{vespa-cli.html => vespa-cli.mdx} | 138 +- en/clients/vespa-feed-client.html | 198 - en/clients/vespa-feed-client.mdx | 145 + en/cloud/image.png | Bin 0 -> 140872 bytes en/cloud/quota.html | 37 - en/cloud/quota.mdx | 29 + en/cloud/support.md | 67 - en/cloud/support.mdx | 60 + en/content/attributes.html | 727 -- en/content/attributes.mdx | 466 + en/content/buckets.html | 248 - en/content/buckets.mdx | 127 + en/content/consistency.md | 210 - en/content/consistency.mdx | 113 + en/content/content-nodes.html | 586 -- en/content/content-nodes.mdx | 385 + en/content/elasticity.html | 305 - en/content/elasticity.mdx | 158 + en/content/idealstate.html | 1606 ---- en/content/idealstate.mdx | 1415 +++ en/content/images/elastic-fail.png | Bin 0 -> 10403 bytes en/content/images/elastic-grow.png | Bin 0 -> 9601 bytes en/content/images/query-groups.png | Bin 0 -> 9813 bytes en/content/proton.html | 1011 --- en/content/proton.mdx | 617 ++ ...arch.md => billion-scale-image-search.mdx} | 233 +- ...rch.md => billion-scale-vector-search.mdx} | 281 +- .../{rag-blueprint.md => rag-blueprint.mdx} | 90 +- en/learn/about-documentation.html | 74 - en/learn/about-documentation.mdx | 85 + en/learn/contributing.md | 47 - en/learn/contributing.mdx | 28 + en/learn/faq.md | 985 -- en/learn/faq.mdx | 749 ++ en/learn/features.html | 135 - en/learn/features.mdx | 85 + en/learn/glossary.html | 502 -- en/learn/glossary.mdx | 222 + en/learn/{llm-help.md => llm-help.mdx} | 35 +- en/learn/migrating-from-elastic-search.html | 333 - en/learn/migrating-from-elastic-search.mdx | 239 + en/learn/migrating-to-cloud.md | 235 - en/learn/migrating-to-cloud.mdx | 261 + en/learn/overview.html | 201 - en/learn/overview.mdx | 70 + en/learn/releases.html | 78 - en/learn/releases.mdx | 40 + en/learn/tenant-apps-instances.html | 58 - en/learn/tenant-apps-instances.mdx | 30 + en/learn/tutorials.mdx | 64 + en/learn/tutorials/e-commerce.md | 149 - en/learn/tutorials/e-commerce.mdx | 69 + en/learn/tutorials/http-api.html | 159 - en/learn/tutorials/http-api.mdx | 91 + .../{hybrid-search.md => hybrid-search.mdx} | 731 +- en/learn/tutorials/index.html | 112 - .../tutorials/news-1-deploy-an-application.md | 299 - .../news-1-deploy-an-application.mdx | 222 + .../news-2-basic-feeding-and-query.md | 495 - .../news-2-basic-feeding-and-query.mdx | 322 + en/learn/tutorials/news-3-searching.md | 561 -- en/learn/tutorials/news-3-searching.mdx | 340 + en/learn/tutorials/news-4-embeddings.md | 484 - en/learn/tutorials/news-4-embeddings.mdx | 291 + en/learn/tutorials/news-5-recommendation.md | 668 -- en/learn/tutorials/news-5-recommendation.mdx | 467 + .../news-6-recommendation-with-searchers.md | 409 - .../news-6-recommendation-with-searchers.mdx | 278 + ...news-7-recommendation-with-parent-child.md | 399 - ...ews-7-recommendation-with-parent-child.mdx | 250 + .../{rag-blueprint.md => rag-blueprint.mdx} | 777 +- en/learn/tutorials/text-search-ml.md | 490 - en/learn/tutorials/text-search-ml.mdx | 370 + en/learn/tutorials/text-search.md | 814 -- en/learn/tutorials/text-search.mdx | 692 ++ en/linguistics/linguistics-custom.html | 54 - en/linguistics/linguistics-custom.mdx | 37 + en/linguistics/linguistics-opennlp.html | 207 - en/linguistics/linguistics-opennlp.mdx | 123 + en/linguistics/linguistics.html | 448 - en/linguistics/linguistics.mdx | 313 + en/linguistics/lucene-linguistics.html | 535 -- en/linguistics/lucene-linguistics.mdx | 214 + en/linguistics/query-rewriting.html | 331 - en/linguistics/query-rewriting.mdx | 204 + en/linguistics/troubleshooting-encoding.html | 122 - en/linguistics/troubleshooting-encoding.mdx | 89 + .../e-commerce/multi-currency-filtering.html | 753 -- .../e-commerce/multi-currency-filtering.mdx | 614 ++ en/modules/e-commerce/saved-search.html | 543 -- en/modules/e-commerce/saved-search.mdx | 465 + .../e-commerce/using-features-together.html | 322 - .../e-commerce/using-features-together.mdx | 259 + en/operations/access-logging.html | 356 - en/operations/access-logging.mdx | 585 ++ en/operations/archive/archive-guide-aws.md | 124 - en/operations/archive/archive-guide-aws.mdx | 118 + en/operations/archive/archive-guide-azure.md | 59 - en/operations/archive/archive-guide-gcp.md | 76 - en/operations/archive/archive-guide-gcp.mdx | 81 + .../{archive-guide.md => archive-guide.mdx} | 61 +- en/operations/automated-deployments.html | 710 -- en/operations/automated-deployments.mdx | 412 + en/operations/autoscaling.html | 206 - en/operations/autoscaling.mdx | 96 + en/operations/az.html | 76 - en/operations/cloning.md | 321 - en/operations/cloning.mdx | 280 + en/operations/data-management.html | 278 - en/operations/data-management.mdx | 233 + en/operations/deleting-applications.md | 57 - en/operations/deleting-applications.mdx | 57 + ...nt-patterns.md => deployment-patterns.mdx} | 92 +- en/operations/deployment-variants.html | 182 - en/operations/deployment-variants.mdx | 150 + en/operations/enclave/archive.md | 47 - en/operations/enclave/archive.mdx | 45 + en/operations/enclave/aws-architecture.md | 77 - en/operations/enclave/aws-architecture.mdx | 38 + en/operations/enclave/aws-getting-started.md | 97 - en/operations/enclave/aws-getting-started.mdx | 85 + en/operations/enclave/azure-architecture.md | 82 - en/operations/enclave/azure-architecture.mdx | 42 + .../enclave/azure-getting-started.md | 101 - .../enclave/azure-getting-started.mdx | 76 + en/operations/enclave/enclave.md | 84 - en/operations/enclave/enclave.mdx | 84 + en/operations/enclave/gcp-architecture.md | 80 - en/operations/enclave/gcp-architecture.mdx | 40 + en/operations/enclave/gcp-getting-started.md | 108 - en/operations/enclave/gcp-getting-started.mdx | 84 + en/operations/enclave/operations.md | 42 - en/operations/enclave/operations.mdx | 21 + en/operations/endpoint-routing.html | 121 - en/operations/endpoint-routing.mdx | 64 + en/operations/environments.html | 156 - en/operations/environments.mdx | 115 + en/operations/kubernetes/architecture.html | 19 - en/operations/kubernetes/architecture.mdx | 11 + .../configure-local-storage-type.html | 250 - .../configure-local-storage-type.mdx | 183 + .../custom-overrides-podtemplate.html | 171 - .../kubernetes/configuration/ingress.html | 121 - .../custom-overrides-podtemplate.mdx | 123 + .../kubernetes/deployment/dev-mode.html | 115 - .../kubernetes/deployment/dev-mode.mdx | 97 + .../deployment/ecr-pull-through-cache.html | 95 - .../deployment/ecr-pull-through-cache.mdx | 91 + .../kubernetes/deployment/installation.html | 396 - .../kubernetes/deployment/installation.mdx | 238 + .../deployment/local-deployment.html | 101 - .../deployment/local-deployment.mdx | 80 + .../kubernetes/deployment/permissions.html | 72 - .../kubernetes/deployment/permissions.mdx | 64 + en/operations/kubernetes/ingress.mdx | 161 + .../logging.html => logging.mdx} | 83 +- en/operations/kubernetes/monitoring.mdx | 194 + .../operations/delete-vespaset.html | 119 - .../kubernetes/operations/delete-vespaset.mdx | 92 + .../kubernetes/operations/monitoring.html | 235 - .../kubernetes/operations/monitoring.mdx | 195 + .../kubernetes/operations/operations.html | 60 - .../kubernetes/operations/operations.mdx | 79 + .../operations/resource-scaling.html | 114 - .../kubernetes/operations/upgrades.html | 270 - .../kubernetes/operations/upgrades.mdx | 196 + .../{configuration/tls.html => tls.mdx} | 238 +- .../kubernetes/vespa-on-kubernetes.html | 38 - .../kubernetes/vespa-on-kubernetes.mdx | 30 + en/operations/login.md | 11 - en/operations/login.mdx | 9 + en/operations/metrics.html | 384 - en/operations/metrics.mdx | 232 + en/operations/monitoring.html | 799 -- en/operations/monitoring.mdx | 720 ++ en/operations/notifications.md | 35 - en/operations/notifications.mdx | 36 + en/operations/private-endpoints.html | 410 - en/operations/private-endpoints.mdx | 297 + en/operations/production-deployment.html | 378 - en/operations/production-deployment.mdx | 243 + en/operations/reindexing-cloud.html | 102 - en/operations/reindexing.html | 201 - en/operations/reindexing.mdx | 64 + .../self-managed/admin-procedures.html | 563 -- .../self-managed/admin-procedures.mdx | 295 + en/operations/self-managed/build-install.html | 144 - en/operations/self-managed/build-install.mdx | 116 + en/operations/self-managed/config-proxy.html | 133 - en/operations/self-managed/config-proxy.mdx | 99 + ...nfig-sentinel.html => config-sentinel.mdx} | 232 +- .../self-managed/configuration-server.html | 527 -- .../self-managed/configuration-server.mdx | 298 + en/operations/self-managed/container.html | 146 - en/operations/self-managed/container.mdx | 112 + .../self-managed/content-node-recovery.html | 73 - .../self-managed/content-node-recovery.mdx | 35 + en/operations/self-managed/cpu-support.html | 38 - en/operations/self-managed/cpu-support.mdx | 25 + .../self-managed/docker-containers.html | 365 - .../self-managed/docker-containers.mdx | 246 + .../files-processes-and-ports.html | 337 - .../files-processes-and-ports.mdx | 254 + en/operations/self-managed/live-upgrade.html | 92 - en/operations/self-managed/live-upgrade.mdx | 30 + en/operations/self-managed/monitoring.html | 579 -- en/operations/self-managed/monitoring.mdx | 409 + .../self-managed/multinode-systems.html | 778 -- .../self-managed/multinode-systems.mdx | 638 ++ en/operations/self-managed/node-setup.html | 134 - en/operations/self-managed/node-setup.mdx | 58 + ...l => procedure-change-attribute-index.mdx} | 217 +- ...zing-examples.html => sizing-examples.mdx} | 139 +- en/operations/self-managed/slobrok.html | 43 - en/operations/self-managed/slobrok.mdx | 17 + .../using-kubernetes-with-vespa.html | 357 - .../using-kubernetes-with-vespa.mdx | 234 + .../self-managed/vespa-gpu-container.html | 149 - .../self-managed/vespa-gpu-container.mdx | 122 + en/operations/self-managed/vespa-support.html | 170 - en/operations/self-managed/vespa-support.mdx | 148 + en/operations/sso.md | 47 - en/operations/telemetry-export.html | 376 - en/operations/zones.html | 96 - en/operations/zones.mdx | 196 + en/performance.mdx | 36 + en/performance/benchmarking-cloud.html | 322 - en/performance/benchmarking-cloud.mdx | 252 + en/performance/benchmarking.html | 238 - en/performance/benchmarking.mdx | 161 + en/performance/caches-in-vespa.html | 54 - en/performance/caches-in-vespa.mdx | 44 + ...container-http.html => container-http.mdx} | 148 +- en/performance/container-tuning.html | 260 - en/performance/container-tuning.mdx | 154 + en/performance/feature-tuning.html | 898 -- en/performance/feature-tuning.mdx | 585 ++ en/performance/graceful-degradation.html | 328 - en/performance/graceful-degradation.mdx | 122 + en/performance/http2.html | 133 - en/performance/http2.mdx | 73 + en/performance/index.html | 52 - en/performance/index.mdx | 0 .../instance-types/aws-instance-types.html | 51 - .../instance-types/aws-instance-types.mdx | 1335 +++ .../instance-types/azure-instance-types.html | 48 - .../instance-types/azure-instance-types.mdx | 542 ++ .../instance-types/gcp-instance-types.html | 49 - .../instance-types/gcp-instance-types.mdx | 1688 ++++ en/performance/memory-visualizer.md | 84 - en/performance/memory-visualizer.mdx | 57 + en/performance/node-resources.md | 142 - en/performance/node-resources.mdx | 104 + .../practical-search-performance-guide.md | 2395 ----- .../practical-search-performance-guide.mdx | 1907 ++++ en/performance/profiling.html | 205 - en/performance/profiling.mdx | 124 + en/performance/rate-limiting-searcher.html | 143 - en/performance/rate-limiting-searcher.mdx | 114 + en/performance/sizing-feeding.html | 630 -- en/performance/sizing-feeding.mdx | 361 + en/performance/sizing-search.html | 733 -- en/performance/sizing-search.mdx | 388 + en/performance/streaming-search.html | 271 - en/performance/streaming-search.mdx | 180 + en/performance/topology-and-resizing.md | 181 - en/performance/topology-and-resizing.mdx | 127 + en/performance/valgrind.html | 66 - en/performance/valgrind.mdx | 59 + en/querying/approximate-nn-hnsw.md | 260 - en/querying/approximate-nn-hnsw.mdx | 158 + en/querying/document-summaries.html | 261 - en/querying/document-summaries.mdx | 194 + en/querying/federation.html | 798 -- en/querying/federation.mdx | 589 ++ en/querying/geo-search.html | 390 - en/querying/geo-search.mdx | 319 + en/querying/grouping.html | 2227 ----- en/querying/grouping.mdx | 1725 ++++ ...e.md => nearest-neighbor-search-guide.mdx} | 1025 +-- en/querying/nearest-neighbor-search.md | 441 - en/querying/nearest-neighbor-search.mdx | 349 + en/querying/page-templates.html | 385 - en/querying/page-templates.mdx | 282 + en/querying/query-api.html | 1027 --- en/querying/query-api.mdx | 807 ++ en/querying/query-language.html | 152 - en/querying/query-language.mdx | 134 + en/querying/query-profiles.html | 479 - en/querying/query-profiles.mdx | 358 + en/querying/result-diversity.md | 130 - en/querying/result-diversity.mdx | 101 + ...lds.md => searching-multivalue-fields.mdx} | 378 +- en/querying/text-matching.html | 485 - en/querying/text-matching.mdx | 388 + en/querying/vector-search-intro.html | 255 - en/querying/vector-search-intro.mdx | 134 + ...zing-vectors.md => binarizing-vectors.mdx} | 336 +- ...-enrichment.md => document-enrichment.mdx} | 98 +- en/rag/embedding.html | 1033 --- en/rag/embedding.mdx | 700 ++ .../{external-llms.md => external-llms.mdx} | 83 +- .../{llms-in-vespa.md => llms-in-vespa.mdx} | 168 +- en/rag/{local-llms.md => local-llms.mdx} | 93 +- en/rag/model-hub.html | 565 -- en/rag/model-hub.mdx | 922 ++ en/rag/{rag.md => rag.mdx} | 60 +- en/rag/working-with-chunks.html | 299 - en/rag/working-with-chunks.mdx | 236 + en/ranking/bm25.html | 377 - en/ranking/bm25.mdx | 57 + .../{cross-encoders.md => cross-encoders.mdx} | 201 +- en/ranking/lightgbm.md | 265 - en/ranking/lightgbm.mdx | 197 + en/ranking/multivalue-query-operators.html | 321 - en/ranking/multivalue-query-operators.mdx | 238 + en/ranking/nativerank.html | 259 - en/ranking/nativerank.mdx | 142 + en/ranking/{onnx.md => onnx.mdx} | 66 +- en/ranking/phased-ranking.html | 541 -- en/ranking/phased-ranking.mdx | 341 + en/ranking/ranking-expressions-features.html | 475 - en/ranking/ranking-expressions-features.mdx | 280 + en/ranking/ranking-intro.md | 408 - en/ranking/ranking-intro.mdx | 354 + en/ranking/reranking-in-searcher.md | 345 - en/ranking/reranking-in-searcher.mdx | 338 + en/ranking/significance.md | 181 - en/ranking/significance.mdx | 164 + en/ranking/stateless-model-evaluation.html | 332 - en/ranking/stateless-model-evaluation.mdx | 199 + en/ranking/tensor-examples.html | 328 - en/ranking/tensor-examples.mdx | 275 + en/ranking/tensor-user-guide.html | 450 - en/ranking/tensor-user-guide.mdx | 340 + en/ranking/{tensorflow.md => tensorflow.mdx} | 31 +- en/ranking/wand.html | 315 - en/ranking/wand.mdx | 177 + en/ranking/{xgboost.md => xgboost.mdx} | 226 +- en/reference/api/api.html | 76 - en/reference/api/api.mdx | 60 + en/reference/api/application-v2.html | 185 - en/reference/api/application-v2.mdx | 51 + en/reference/api/cluster-v2.html | 407 - en/reference/api/cluster-v2.mdx | 156 + en/reference/api/config-v2.html | 355 - en/reference/api/config-v2.mdx | 112 + en/reference/api/deploy-v2.html | 1115 --- en/reference/api/deploy-v2.mdx | 345 + en/reference/api/document-v1.html | 1096 --- en/reference/api/document-v1.mdx | 71 + en/reference/api/metrics-v1.html | 179 - en/reference/api/metrics-v1.mdx | 110 + en/reference/api/metrics-v2.html | 140 - en/reference/api/metrics-v2.mdx | 43 + en/reference/api/prometheus-v1.html | 112 - en/reference/api/prometheus-v1.mdx | 43 + en/reference/api/query.html | 2518 ------ en/reference/api/query.mdx | 1247 +++ en/reference/api/state-v1.html | 422 - en/reference/api/state-v1.mdx | 203 + .../applications/application-packages.html | 296 - .../applications/application-packages.mdx | 191 + en/reference/applications/components.html | 449 - en/reference/applications/components.mdx | 238 + en/reference/applications/config-files.html | 294 - en/reference/applications/config-files.mdx | 207 + en/reference/applications/deployment.html | 1294 --- en/reference/applications/deployment.mdx | 880 ++ en/reference/applications/hosts.html | 50 - en/reference/applications/hosts.mdx | 37 + en/reference/applications/services/admin.html | 465 - en/reference/applications/services/admin.mdx | 488 + .../applications/services/container.html | 933 -- .../applications/services/container.mdx | 917 ++ .../applications/services/content.html | 1856 ---- .../applications/services/content.mdx | 1299 +++ .../applications/services/docproc.html | 314 - .../applications/services/docproc.mdx | 292 + en/reference/applications/services/http.html | 516 -- en/reference/applications/services/http.mdx | 469 + .../applications/services/processing.html | 395 - .../applications/services/processing.mdx | 357 + .../applications/services/search.html | 574 -- en/reference/applications/services/search.mdx | 481 + .../applications/services/services.html | 337 - .../applications/services/services.mdx | 255 + en/reference/applications/testing-java.html | 92 - en/reference/applications/testing-java.mdx | 81 + en/reference/applications/testing.html | 272 - en/reference/applications/testing.mdx | 254 + .../applications/validation-overrides.html | 79 - .../applications/validation-overrides.mdx | 61 + en/reference/clients/vespa-cli/vespa.md | 63 - en/reference/clients/vespa-cli/vespa.mdx | 60 + .../{vespa_activate.md => vespa_activate.mdx} | 16 +- .../{vespa_auth.md => vespa_auth.mdx} | 26 +- ...auth_api-key.md => vespa_auth_api-key.mdx} | 35 +- ...vespa_auth_cert.md => vespa_auth_cert.mdx} | 39 +- ...th_cert_add.md => vespa_auth_cert_add.mdx} | 24 +- ...spa_auth_login.md => vespa_auth_login.mdx} | 32 +- ...a_auth_logout.md => vespa_auth_logout.mdx} | 18 +- ...vespa_auth_show.md => vespa_auth_show.mdx} | 18 +- .../{vespa_clone.md => vespa_clone.mdx} | 25 +- ...spa_completion.md => vespa_completion.mdx} | 25 +- ...tion_bash.md => vespa_completion_bash.mdx} | 23 +- ...tion_fish.md => vespa_completion_fish.mdx} | 16 +- ...ell.md => vespa_completion_powershell.mdx} | 19 +- ...letion_zsh.md => vespa_completion_zsh.mdx} | 23 +- .../clients/vespa-cli/vespa_config.md | 133 - .../clients/vespa-cli/vespa_config.mdx | 88 + ...spa_config_get.md => vespa_config_get.mdx} | 31 +- ...spa_config_set.md => vespa_config_set.mdx} | 31 +- ...config_unset.md => vespa_config_unset.mdx} | 26 +- .../{vespa_curl.md => vespa_curl.mdx} | 21 +- .../{vespa_deploy.md => vespa_deploy.mdx} | 31 +- .../{vespa_destroy.md => vespa_destroy.mdx} | 33 +- .../{vespa_document.md => vespa_document.mdx} | 35 +- ...document_get.md => vespa_document_get.mdx} | 18 +- ...document_put.md => vespa_document_put.mdx} | 25 +- ...nt_remove.md => vespa_document_remove.mdx} | 21 +- ...nt_update.md => vespa_document_update.mdx} | 20 +- .../{vespa_feed.md => vespa_feed.mdx} | 36 +- .../{vespa_fetch.md => vespa_fetch.mdx} | 22 +- .../{vespa_inspect.md => vespa_inspect.mdx} | 16 +- ...t_profile.md => vespa_inspect_profile.mdx} | 22 +- .../vespa-cli/{vespa_log.md => vespa_log.mdx} | 18 +- .../{vespa_prepare.md => vespa_prepare.mdx} | 16 +- .../{vespa_prod.md => vespa_prod.mdx} | 22 +- ...a_prod_deploy.md => vespa_prod_deploy.mdx} | 28 +- ...vespa_prod_init.md => vespa_prod_init.mdx} | 24 +- .../{vespa_query.md => vespa_query.mdx} | 21 +- .../{vespa_status.md => vespa_status.mdx} | 27 +- ...atus_deploy.md => vespa_status_deploy.mdx} | 18 +- ...loyment.md => vespa_status_deployment.mdx} | 22 +- .../vespa-cli/vespa_status_document.md | 112 - .../vespa-cli/vespa_status_document.mdx | 38 + ..._endpoint.md => vespa_status_endpoint.mdx} | 22 +- .../clients/vespa-cli/vespa_status_query.md | 112 - .../clients/vespa-cli/vespa_status_query.mdx | 38 + .../{vespa_test.md => vespa_test.mdx} | 20 +- .../{vespa_version.md => vespa_version.mdx} | 16 +- .../{vespa_visit.md => vespa_visit.mdx} | 18 +- .../clustercontroller-metrics-reference.html | 127 - en/reference/container-metrics-reference.html | 952 -- .../default-set-metrics-reference.html | 432 - en/reference/logd-metrics-reference.html | 17 - en/reference/nodeadmin-metrics-reference.html | 22 - en/reference/operations/health-checks.html | 73 - en/reference/operations/health-checks.mdx | 47 + en/reference/operations/log-files.html | 351 - en/reference/operations/log-files.mdx | 230 + .../operations/metrics/clustercontroller.html | 129 - .../operations/metrics/clustercontroller.mdx | 131 + .../operations/metrics/configserver.html | 709 -- .../metrics/configserver.mdx} | 303 +- .../metrics/{container.html => container.mdx} | 414 +- .../metrics/default-metric-set.html | 440 - .../operations/metrics/default-metric-set.mdx | 477 + .../operations/metrics/distributor.html | 1119 --- .../metrics/distributor.mdx} | 460 +- en/reference/operations/metrics/logd.html | 19 - en/reference/operations/metrics/logd.mdx | 21 + .../operations/metrics/metric-units.html | 215 - .../metrics/metric-units.mdx} | 20 +- en/reference/operations/metrics/metrics.md | 50 - en/reference/operations/metrics/metrics.mdx | 83 + .../operations/metrics/nodeadmin.html | 24 - en/reference/operations/metrics/nodeadmin.mdx | 26 + .../operations/metrics/searchnode.html | 1324 --- .../operations/metrics/searchnode.mdx | 1296 +++ en/reference/operations/metrics/sentinel.html | 34 - en/reference/operations/metrics/sentinel.mdx | 36 + en/reference/operations/metrics/slobrok.html | 39 - en/reference/operations/metrics/slobrok.mdx | 41 + en/reference/operations/metrics/storage.html | 1054 --- en/reference/operations/metrics/storage.mdx | 1056 +++ .../operations/metrics/vespa-metric-set.html | 3236 ------- .../operations/metrics/vespa-metric-set.mdx | 3260 +++++++ .../operations/self-managed/tools.html | 2706 ------ .../operations/self-managed/tools.mdx | 1999 +++++ en/reference/operations/tools.html | 490 - en/reference/operations/tools.mdx | 325 + .../querying/default-result-format.html | 896 -- .../querying/default-result-format.mdx | 680 ++ en/reference/querying/grouping-language.html | 682 -- en/reference/querying/grouping-language.mdx | 1133 +++ ...ry-language.md => json-query-language.mdx} | 73 +- en/reference/querying/page-result-format.html | 281 - en/reference/querying/page-result-format.mdx | 289 + en/reference/querying/page-templates.html | 379 - en/reference/querying/page-templates.mdx | 312 + en/reference/querying/query-profiles.html | 323 - en/reference/querying/query-profiles.mdx | 282 + en/reference/querying/semantic-rules.html | 413 - en/reference/querying/semantic-rules.mdx | 392 + .../querying/simple-query-language.html | 247 - .../querying/simple-query-language.mdx | 148 + en/reference/querying/sorting-language.html | 266 - en/reference/querying/sorting-language.mdx | 198 + en/reference/querying/yql.html | 2410 ----- en/reference/querying/yql.mdx | 681 ++ en/reference/rag/chunking.html | 61 - en/reference/rag/chunking.mdx | 56 + en/reference/rag/embedding.html | 996 --- en/reference/rag/embedding.mdx | 856 ++ .../ranking/constant-tensor-json-format.html | 201 - .../ranking/constant-tensor-json-format.mdx | 147 + en/reference/ranking/model-files.html | 121 - en/reference/ranking/model-files.mdx | 111 + en/reference/ranking/nativerank.html | 1131 --- en/reference/ranking/nativerank.mdx | 278 + .../ranking/rank-feature-configuration.html | 379 - .../ranking/rank-feature-configuration.mdx | 270 + en/reference/ranking/rank-features.html | 1886 ---- en/reference/ranking/rank-features.mdx | 904 ++ en/reference/ranking/rank-types.html | 306 - en/reference/ranking/rank-types.mdx | 164 + en/reference/ranking/ranking-expressions.html | 883 -- en/reference/ranking/ranking-expressions.mdx | 492 + .../ranking/string-segment-match.html | 270 - en/reference/ranking/string-segment-match.mdx | 212 + en/reference/ranking/tensor.html | 312 - en/reference/ranking/tensor.mdx | 280 + en/reference/release-notes/vespa7.html | 469 - en/reference/release-notes/vespa7.mdx | 615 ++ ...uide.md => vespa8-geo-migration-guide.mdx} | 64 +- en/reference/release-notes/vespa8.html | 1273 --- en/reference/release-notes/vespa8.mdx | 935 ++ en/reference/release-notes/vespa9.html | 360 - en/reference/release-notes/vespa9.mdx | 265 + en/reference/schemas/document-field-path.html | 123 - en/reference/schemas/document-field-path.mdx | 124 + .../schemas/document-json-format.html | 1383 --- en/reference/schemas/document-json-format.mdx | 1031 +++ en/reference/schemas/schemas.html | 5009 ----------- en/reference/schemas/schemas.mdx | 3690 ++++++++ .../searchnode-metrics-reference.html | 1282 --- en/reference/security/mtls.md | 250 - en/reference/security/mtls.mdx | 294 + en/reference/sentinel-metrics-reference.html | 32 - en/reference/slobrok-metrics-reference.html | 37 - en/reference/storage-metrics-reference.html | 1052 --- en/reference/vespa-set-metrics-reference.html | 3132 ------- .../writing/document-selector-language.html | 590 -- .../writing/document-selector-language.mdx | 410 + en/reference/writing/indexing-language.html | 756 -- en/reference/writing/indexing-language.mdx | 389 + en/schemas/concrete-documents.html | 200 - en/schemas/concrete-documents.mdx | 151 + en/schemas/documents.html | 418 - en/schemas/documents.mdx | 258 + ...n.html => exposing-schema-information.mdx} | 72 +- en/schemas/inheritance-in-schemas.html | 274 - en/schemas/inheritance-in-schemas.mdx | 240 + en/schemas/parent-child.html | 240 - en/schemas/parent-child.mdx | 187 + en/schemas/predicate-fields.html | 458 - en/schemas/predicate-fields.mdx | 329 + en/schemas/structs.html | 77 - en/schemas/structs.mdx | 60 + ...lare-workers.md => cloudflare-workers.mdx} | 63 +- en/security/{guide.md => guide.mdx} | 299 +- en/security/mtls.md | 501 -- en/security/mtls.mdx | 456 + en/security/secret-store.md | 187 - en/security/secret-store.mdx | 129 + .../securing-your-vespa-installation.md | 94 - .../securing-your-vespa-installation.mdx | 54 + en/security/security.md | 15 - en/security/security.mdx | 9 + en/security/whitepaper.md | 354 - en/security/whitepaper.mdx | 194 + en/writing/batch-delete.html | 204 - en/writing/batch-delete.mdx | 187 + en/writing/document-api-guide.html | 625 -- en/writing/document-api-guide.mdx | 468 + en/writing/document-routing.html | 1441 --- en/writing/document-routing.mdx | 1055 +++ en/writing/document-v1-api-guide.html | 626 -- en/writing/document-v1-api-guide.mdx | 463 + en/writing/feed-block.html | 127 - en/writing/feed-block.mdx | 87 + en/writing/indexing-paged-vectors.html | 84 - en/writing/indexing-paged-vectors.mdx | 74 + en/writing/indexing.html | 178 - en/writing/indexing.mdx | 146 + en/writing/initial-batch-feed.html | 246 - en/writing/initial-batch-feed.mdx | 99 + en/writing/partial-updates.html | 250 - en/writing/partial-updates.mdx | 147 + en/writing/reads-and-writes.html | 256 - en/writing/reads-and-writes.mdx | 103 + en/writing/visiting.html | 502 -- en/writing/visiting.mdx | 312 + extract_redirects.py | 141 - icons/favicon-32x32.png => favicon.png | Bin feed-split.py | 260 - feed_to_vespa.py | 207 - generate_suggestions_from_reference.py | 56 - icons/android-chrome-192x192.png | Bin 1841 -> 0 bytes icons/android-chrome-384x384.png | Bin 25683 -> 0 bytes icons/android-chrome-512x512.png | Bin 4050 -> 0 bytes icons/apple-touch-icon.png | Bin 1580 -> 0 bytes icons/browserconfig.xml | 9 - icons/favicon-16x16.png | Bin 835 -> 0 bytes icons/favicon.ico | Bin 15086 -> 0 bytes icons/manifest.json | 19 - icons/mstile-144x144.png | Bin 5313 -> 0 bytes icons/mstile-150x150.png | Bin 1744 -> 0 bytes icons/mstile-310x150.png | Bin 5909 -> 0 bytes icons/mstile-310x310.png | Bin 12065 -> 0 bytes icons/mstile-70x70.png | Bin 3697 -> 0 bytes icons/safari-pinned-tab.svg | 35 - icons/site.webmanifest | 19 - index.html | 37 - index.mdx | 318 + ja/features.html | 111 - ja/features.mdx | 102 + ...html => introduction-to-documentation.mdx} | 48 +- ja/overview.html | 136 - ja/overview.mdx | 92 + ja/vespa-quick-start.html | 125 - ja/vespa-quick-start.mdx | 87 + js/handle_results.js | 121 - js/handle_search_suggestions.js | 198 - js/header_links.js | 24 - js/load-mathjax.js | 27 - js/pre_copy.js | 33 - js/process_pre.js | 21 - js/query_examples.js | 21 - js/search.js | 121 - js/search_suggestions.js | 83 - js/table-list-converter.js | 118 - llms-template.md | 43 - logo/dark.svg | 9 + logo/hero-dark.svg | 29 + logo/hero-light.svg | 29 + logo/light.svg | 9 + playground/css/playground.css | 673 -- playground/css/typography.css | 30 - playground/icons/favicon-16x16.png | Bin 835 -> 0 bytes playground/icons/favicon-32x32.png | Bin 1122 -> 0 bytes playground/index.html | 176 - playground/js/d3.min.js | 2 - playground/js/lz-string.min.js | 1 - playground/js/playground.js | 1232 --- playground/js/showdown.min.js | 5 - questions.jsonl | 1943 ---- redirects.yml | 360 - renovate.json | 12 - robots.txt | 11 - search.html | 40 - search_beta.html | 27 - sitemap.html | 49 - suggestions_index.json | 7935 ----------------- test/README.md | 10 - test/_test_config-large.yml | 13 - test/_test_config.yml | 24 - test/deduplicate_suggestions.py | 100 - test/pseudo_terminal.py | 97 - test/requirements.txt | 4 - test/test.py | 394 - test/test_queries.py | 55 - 865 files changed, 89745 insertions(+), 164129 deletions(-) delete mode 100644 .github/actions/commit-updated-docs/action.yml delete mode 100644 .github/actions/install-deps/action.yml delete mode 100644 .github/workflows/auto-update-documentation.yml delete mode 100644 .github/workflows/build-pages.yml delete mode 100644 .github/workflows/deploy-pages.yml delete mode 100644 .github/workflows/feed.yml delete mode 100644 .github/workflows/link-checker.yml delete mode 100644 .github/workflows/verify-guides-large.yml delete mode 100644 .github/workflows/verify-guides-small.yml delete mode 100644 .github/workflows/verify-guides.yml delete mode 100644 .gitignore delete mode 100644 404.html delete mode 100644 CNAME delete mode 100644 Gemfile delete mode 100644 Gemfile.lock delete mode 100644 _config.yml delete mode 100644 _data/aws-instance-types.csv delete mode 100644 _data/azure-instance-types.csv delete mode 100644 _data/gcp-instance-types.csv delete mode 100644 _data/sidebar.yml delete mode 100644 _includes/assets/images/commonmark-logo.svg delete mode 100644 _includes/chip-cloud.html delete mode 100644 _includes/chip-enterprise.html delete mode 100644 _includes/chip-self-managed.html delete mode 100644 _includes/deprecated.html delete mode 100644 _includes/htmlhead.html delete mode 100644 _includes/important.html delete mode 100644 _includes/js.html delete mode 100644 _includes/mode-content-hidden.html delete mode 100644 _includes/mode-content-waring.html delete mode 100644 _includes/navbar.html delete mode 100644 _includes/new.html delete mode 100644 _includes/note.html delete mode 100644 _includes/pre-req.html delete mode 100644 _includes/setup.html delete mode 100644 _includes/sidebar.html delete mode 100644 _includes/toc.html delete mode 100644 _includes/version.html delete mode 100644 _includes/video-include.html delete mode 100644 _includes/warning.html delete mode 100644 _layouts/default.html delete mode 100644 _paragraphs_config.yml delete mode 100644 _plugins-vespafeed/vespa_index_generator.rb delete mode 100644 _plugins/html_to_markdown_generator.rb delete mode 100644 _sass/denali-css/scss/components/_all.scss delete mode 100644 _sass/denali-css/scss/components/alerts.scss delete mode 100644 _sass/denali-css/scss/components/box.scss delete mode 100644 _sass/denali-css/scss/components/breadcrumb.scss delete mode 100644 _sass/denali-css/scss/components/button-group.scss delete mode 100644 _sass/denali-css/scss/components/button.scss delete mode 100644 _sass/denali-css/scss/components/checkbox.scss delete mode 100644 _sass/denali-css/scss/components/chips.scss delete mode 100644 _sass/denali-css/scss/components/icons.scss delete mode 100644 _sass/denali-css/scss/components/input-group.scss delete mode 100644 _sass/denali-css/scss/components/inputs.scss delete mode 100644 _sass/denali-css/scss/components/links.scss delete mode 100644 _sass/denali-css/scss/components/lists.scss delete mode 100644 _sass/denali-css/scss/components/loader.scss delete mode 100644 _sass/denali-css/scss/components/menus.scss delete mode 100644 _sass/denali-css/scss/components/modals.scss delete mode 100644 _sass/denali-css/scss/components/navbar.scss delete mode 100644 _sass/denali-css/scss/components/progress.scss delete mode 100644 _sass/denali-css/scss/components/radio.scss delete mode 100644 _sass/denali-css/scss/components/selects.scss delete mode 100644 _sass/denali-css/scss/components/switch.scss delete mode 100644 _sass/denali-css/scss/components/table.scss delete mode 100644 _sass/denali-css/scss/components/tabs.scss delete mode 100644 _sass/denali-css/scss/components/tags.scss delete mode 100644 _sass/denali-css/scss/components/titles.scss delete mode 100644 _sass/denali-css/scss/components/toggle.scss delete mode 100644 _sass/denali-css/scss/components/tooltip.scss delete mode 100644 _sass/denali-css/scss/components/typography.scss delete mode 100644 _sass/denali-css/scss/denali-helpers.scss delete mode 100644 _sass/denali-css/scss/helpers/_all.scss delete mode 100644 _sass/denali-css/scss/helpers/borders.scss delete mode 100644 _sass/denali-css/scss/helpers/colors.scss delete mode 100644 _sass/denali-css/scss/helpers/flexbox.scss delete mode 100644 _sass/denali-css/scss/helpers/float.scss delete mode 100644 _sass/denali-css/scss/helpers/hide.scss delete mode 100644 _sass/denali-css/scss/helpers/margin.scss delete mode 100644 _sass/denali-css/scss/helpers/misc.scss delete mode 100644 _sass/denali-css/scss/helpers/padding.scss delete mode 100644 _sass/denali-css/scss/helpers/shadows.scss delete mode 100644 _sass/denali-css/scss/helpers/size.scss delete mode 100644 _sass/denali-css/scss/helpers/text.scss delete mode 100644 _sass/denali-css/scss/helpers/type.scss delete mode 100644 _sass/denali-css/scss/maps/_all.scss delete mode 100644 _sass/denali-css/scss/maps/color-maps.scss delete mode 100644 _sass/denali-css/scss/mixins/_all.scss delete mode 100644 _sass/denali-css/scss/mixins/breakpoints.scss delete mode 100644 _sass/denali-css/scss/mixins/theme.scss delete mode 100644 _sass/denali-css/scss/utilities/_all.scss delete mode 100644 _sass/denali-css/scss/utilities/color-functions.scss delete mode 100644 _sass/denali-css/scss/utilities/root-variables.scss delete mode 100644 _suggestions_config.yml delete mode 100644 _suggestions_reference_config.yml delete mode 100644 amplify.yml create mode 100644 assets/graph-image.png delete mode 100644 assets/img/az-multi-cluster.svg delete mode 100644 assets/img/az-multi-content.svg delete mode 100644 assets/img/az-single-cluster.svg delete mode 100644 assets/img/az-single-content.svg delete mode 100644 assets/img/console/reindex-completed.png delete mode 100644 assets/img/console/reindex-confirm.png delete mode 100644 assets/img/console/reindex-overview.png mode change 100755 => 100644 assets/img/relevance/ranktype-about.png mode change 100755 => 100644 assets/img/relevance/ranktype-tags.png delete mode 100644 assets/img/support-ticket.png delete mode 100644 assets/img/telemetry-export-architecture.mmd delete mode 100644 assets/img/telemetry-export-architecture.png delete mode 100755 convert.py delete mode 100644 css/denali-icon-font/denali-icon-font.css delete mode 100644 css/denali-icon-font/denali-icon-font.eot delete mode 100644 css/denali-icon-font/denali-icon-font.svg delete mode 100644 css/denali-icon-font/denali-icon-font.ttf delete mode 100644 css/denali-icon-font/denali-icon-font.woff delete mode 100644 css/denali-theme-vespa-light.scss delete mode 100644 css/denali-theme-vespa.scss delete mode 100644 css/denali.css delete mode 100644 css/native.css delete mode 100644 css/style-light.scss delete mode 100644 css/style.scss delete mode 100644 css/typography.scss create mode 100644 custom.css create mode 100644 docs.json delete mode 100644 en/applications/bundles.html create mode 100644 en/applications/bundles.mdx delete mode 100644 en/applications/chaining.html create mode 100644 en/applications/chaining.mdx delete mode 100644 en/applications/components.html create mode 100644 en/applications/components.mdx delete mode 100644 en/applications/config-system.html create mode 100644 en/applications/config-system.mdx delete mode 100644 en/applications/configapi-dev.html create mode 100644 en/applications/configapi-dev.mdx delete mode 100644 en/applications/configuring-components.html create mode 100644 en/applications/configuring-components.mdx delete mode 100644 en/applications/containers.html create mode 100644 en/applications/containers.mdx delete mode 100644 en/applications/dependency-injection.html create mode 100644 en/applications/dependency-injection.mdx delete mode 100644 en/applications/deployment.html create mode 100644 en/applications/deployment.mdx delete mode 100644 en/applications/developer-guide.html create mode 100644 en/applications/developer-guide.mdx delete mode 100644 en/applications/document-processors.html create mode 100644 en/applications/document-processors.mdx delete mode 100644 en/applications/http-servers-and-filters.html create mode 100644 en/applications/http-servers-and-filters.mdx delete mode 100644 en/applications/ide-support.html create mode 100644 en/applications/ide-support.mdx rename en/applications/{inspecting-structured-data.html => inspecting-structured-data.mdx} (59%) delete mode 100644 en/applications/pluggable-frameworks.html create mode 100644 en/applications/pluggable-frameworks.mdx delete mode 100644 en/applications/processing.html create mode 100644 en/applications/processing.mdx delete mode 100644 en/applications/request-handlers.html create mode 100644 en/applications/request-handlers.mdx delete mode 100644 en/applications/result-renderers.html create mode 100644 en/applications/result-renderers.mdx delete mode 100644 en/applications/searchers.html create mode 100644 en/applications/searchers.mdx delete mode 100644 en/applications/testing.html create mode 100644 en/applications/testing.mdx delete mode 100644 en/applications/unit-testing.html create mode 100644 en/applications/unit-testing.mdx delete mode 100644 en/applications/using-zookeeper.md create mode 100644 en/applications/using-zookeeper.mdx delete mode 100644 en/applications/vespaignore.html create mode 100644 en/applications/vespaignore.mdx delete mode 100644 en/applications/web-services.html create mode 100644 en/applications/web-services.mdx delete mode 100644 en/basics/applications.html create mode 100644 en/basics/applications.mdx delete mode 100644 en/basics/deploy-an-application-java.html create mode 100644 en/basics/deploy-an-application-java.mdx delete mode 100644 en/basics/deploy-an-application-local-java.html create mode 100644 en/basics/deploy-an-application-local-java.mdx delete mode 100644 en/basics/deploy-an-application-local.html create mode 100644 en/basics/deploy-an-application-local.mdx delete mode 100644 en/basics/deploy-an-application-shell.html create mode 100644 en/basics/deploy-an-application-shell.mdx delete mode 100644 en/basics/deploy-an-application.html create mode 100644 en/basics/deploy-an-application.mdx delete mode 100644 en/basics/operations.html create mode 100644 en/basics/operations.mdx delete mode 100644 en/basics/querying.html create mode 100644 en/basics/querying.mdx delete mode 100644 en/basics/ranking.html create mode 100644 en/basics/ranking.mdx delete mode 100644 en/basics/schemas.html create mode 100644 en/basics/schemas.mdx delete mode 100644 en/basics/whats-more.html create mode 100644 en/basics/whats-more.mdx delete mode 100644 en/basics/writing.html create mode 100644 en/basics/writing.mdx delete mode 100644 en/clients/http-best-practices.md create mode 100644 en/clients/http-best-practices.mdx create mode 100644 en/clients/python-client.mdx rename en/clients/{vespa-cli.html => vespa-cli.mdx} (57%) delete mode 100644 en/clients/vespa-feed-client.html create mode 100644 en/clients/vespa-feed-client.mdx create mode 100644 en/cloud/image.png delete mode 100644 en/cloud/quota.html create mode 100644 en/cloud/quota.mdx delete mode 100644 en/cloud/support.md create mode 100644 en/cloud/support.mdx delete mode 100644 en/content/attributes.html create mode 100644 en/content/attributes.mdx delete mode 100644 en/content/buckets.html create mode 100644 en/content/buckets.mdx delete mode 100644 en/content/consistency.md create mode 100644 en/content/consistency.mdx delete mode 100644 en/content/content-nodes.html create mode 100644 en/content/content-nodes.mdx delete mode 100644 en/content/elasticity.html create mode 100644 en/content/elasticity.mdx delete mode 100644 en/content/idealstate.html create mode 100644 en/content/idealstate.mdx create mode 100644 en/content/images/elastic-fail.png create mode 100644 en/content/images/elastic-grow.png create mode 100644 en/content/images/query-groups.png delete mode 100644 en/content/proton.html create mode 100644 en/content/proton.mdx rename en/examples/{billion-scale-image-search.md => billion-scale-image-search.mdx} (83%) rename en/examples/{billion-scale-vector-search.md => billion-scale-vector-search.mdx} (55%) rename en/examples/{rag-blueprint.md => rag-blueprint.mdx} (54%) delete mode 100644 en/learn/about-documentation.html create mode 100644 en/learn/about-documentation.mdx delete mode 100644 en/learn/contributing.md create mode 100644 en/learn/contributing.mdx delete mode 100644 en/learn/faq.md create mode 100644 en/learn/faq.mdx delete mode 100644 en/learn/features.html create mode 100644 en/learn/features.mdx delete mode 100644 en/learn/glossary.html create mode 100644 en/learn/glossary.mdx rename en/learn/{llm-help.md => llm-help.mdx} (50%) delete mode 100644 en/learn/migrating-from-elastic-search.html create mode 100644 en/learn/migrating-from-elastic-search.mdx delete mode 100644 en/learn/migrating-to-cloud.md create mode 100644 en/learn/migrating-to-cloud.mdx delete mode 100644 en/learn/overview.html create mode 100644 en/learn/overview.mdx delete mode 100644 en/learn/releases.html create mode 100644 en/learn/releases.mdx delete mode 100644 en/learn/tenant-apps-instances.html create mode 100644 en/learn/tenant-apps-instances.mdx create mode 100644 en/learn/tutorials.mdx delete mode 100644 en/learn/tutorials/e-commerce.md create mode 100644 en/learn/tutorials/e-commerce.mdx delete mode 100644 en/learn/tutorials/http-api.html create mode 100644 en/learn/tutorials/http-api.mdx rename en/learn/tutorials/{hybrid-search.md => hybrid-search.mdx} (59%) delete mode 100644 en/learn/tutorials/index.html delete mode 100644 en/learn/tutorials/news-1-deploy-an-application.md create mode 100644 en/learn/tutorials/news-1-deploy-an-application.mdx delete mode 100644 en/learn/tutorials/news-2-basic-feeding-and-query.md create mode 100644 en/learn/tutorials/news-2-basic-feeding-and-query.mdx delete mode 100644 en/learn/tutorials/news-3-searching.md create mode 100644 en/learn/tutorials/news-3-searching.mdx delete mode 100644 en/learn/tutorials/news-4-embeddings.md create mode 100644 en/learn/tutorials/news-4-embeddings.mdx delete mode 100644 en/learn/tutorials/news-5-recommendation.md create mode 100644 en/learn/tutorials/news-5-recommendation.mdx delete mode 100644 en/learn/tutorials/news-6-recommendation-with-searchers.md create mode 100644 en/learn/tutorials/news-6-recommendation-with-searchers.mdx delete mode 100644 en/learn/tutorials/news-7-recommendation-with-parent-child.md create mode 100644 en/learn/tutorials/news-7-recommendation-with-parent-child.mdx rename en/learn/tutorials/{rag-blueprint.md => rag-blueprint.mdx} (58%) delete mode 100644 en/learn/tutorials/text-search-ml.md create mode 100644 en/learn/tutorials/text-search-ml.mdx delete mode 100644 en/learn/tutorials/text-search.md create mode 100644 en/learn/tutorials/text-search.mdx delete mode 100644 en/linguistics/linguistics-custom.html create mode 100644 en/linguistics/linguistics-custom.mdx delete mode 100644 en/linguistics/linguistics-opennlp.html create mode 100644 en/linguistics/linguistics-opennlp.mdx delete mode 100644 en/linguistics/linguistics.html create mode 100644 en/linguistics/linguistics.mdx delete mode 100644 en/linguistics/lucene-linguistics.html create mode 100644 en/linguistics/lucene-linguistics.mdx delete mode 100644 en/linguistics/query-rewriting.html create mode 100644 en/linguistics/query-rewriting.mdx delete mode 100644 en/linguistics/troubleshooting-encoding.html create mode 100644 en/linguistics/troubleshooting-encoding.mdx delete mode 100644 en/modules/e-commerce/multi-currency-filtering.html create mode 100644 en/modules/e-commerce/multi-currency-filtering.mdx delete mode 100644 en/modules/e-commerce/saved-search.html create mode 100644 en/modules/e-commerce/saved-search.mdx delete mode 100644 en/modules/e-commerce/using-features-together.html create mode 100644 en/modules/e-commerce/using-features-together.mdx delete mode 100644 en/operations/access-logging.html create mode 100644 en/operations/access-logging.mdx delete mode 100644 en/operations/archive/archive-guide-aws.md create mode 100644 en/operations/archive/archive-guide-aws.mdx delete mode 100644 en/operations/archive/archive-guide-azure.md delete mode 100644 en/operations/archive/archive-guide-gcp.md create mode 100644 en/operations/archive/archive-guide-gcp.mdx rename en/operations/archive/{archive-guide.md => archive-guide.mdx} (67%) delete mode 100644 en/operations/automated-deployments.html create mode 100644 en/operations/automated-deployments.mdx delete mode 100644 en/operations/autoscaling.html create mode 100644 en/operations/autoscaling.mdx delete mode 100644 en/operations/az.html delete mode 100644 en/operations/cloning.md create mode 100644 en/operations/cloning.mdx delete mode 100644 en/operations/data-management.html create mode 100644 en/operations/data-management.mdx delete mode 100644 en/operations/deleting-applications.md create mode 100644 en/operations/deleting-applications.mdx rename en/operations/{deployment-patterns.md => deployment-patterns.mdx} (68%) delete mode 100644 en/operations/deployment-variants.html create mode 100644 en/operations/deployment-variants.mdx delete mode 100644 en/operations/enclave/archive.md create mode 100644 en/operations/enclave/archive.mdx delete mode 100644 en/operations/enclave/aws-architecture.md create mode 100644 en/operations/enclave/aws-architecture.mdx delete mode 100644 en/operations/enclave/aws-getting-started.md create mode 100644 en/operations/enclave/aws-getting-started.mdx delete mode 100644 en/operations/enclave/azure-architecture.md create mode 100644 en/operations/enclave/azure-architecture.mdx delete mode 100644 en/operations/enclave/azure-getting-started.md create mode 100644 en/operations/enclave/azure-getting-started.mdx delete mode 100644 en/operations/enclave/enclave.md create mode 100644 en/operations/enclave/enclave.mdx delete mode 100644 en/operations/enclave/gcp-architecture.md create mode 100644 en/operations/enclave/gcp-architecture.mdx delete mode 100644 en/operations/enclave/gcp-getting-started.md create mode 100644 en/operations/enclave/gcp-getting-started.mdx delete mode 100644 en/operations/enclave/operations.md create mode 100644 en/operations/enclave/operations.mdx delete mode 100644 en/operations/endpoint-routing.html create mode 100644 en/operations/endpoint-routing.mdx delete mode 100644 en/operations/environments.html create mode 100644 en/operations/environments.mdx delete mode 100644 en/operations/kubernetes/architecture.html create mode 100644 en/operations/kubernetes/architecture.mdx delete mode 100644 en/operations/kubernetes/configuration/configure-local-storage-type.html create mode 100644 en/operations/kubernetes/configuration/configure-local-storage-type.mdx delete mode 100644 en/operations/kubernetes/configuration/custom-overrides-podtemplate.html delete mode 100644 en/operations/kubernetes/configuration/ingress.html create mode 100644 en/operations/kubernetes/custom-overrides-podtemplate.mdx delete mode 100644 en/operations/kubernetes/deployment/dev-mode.html create mode 100644 en/operations/kubernetes/deployment/dev-mode.mdx delete mode 100644 en/operations/kubernetes/deployment/ecr-pull-through-cache.html create mode 100644 en/operations/kubernetes/deployment/ecr-pull-through-cache.mdx delete mode 100644 en/operations/kubernetes/deployment/installation.html create mode 100644 en/operations/kubernetes/deployment/installation.mdx delete mode 100644 en/operations/kubernetes/deployment/local-deployment.html create mode 100644 en/operations/kubernetes/deployment/local-deployment.mdx delete mode 100644 en/operations/kubernetes/deployment/permissions.html create mode 100644 en/operations/kubernetes/deployment/permissions.mdx create mode 100644 en/operations/kubernetes/ingress.mdx rename en/operations/kubernetes/{configuration/logging.html => logging.mdx} (60%) create mode 100644 en/operations/kubernetes/monitoring.mdx delete mode 100644 en/operations/kubernetes/operations/delete-vespaset.html create mode 100644 en/operations/kubernetes/operations/delete-vespaset.mdx delete mode 100644 en/operations/kubernetes/operations/monitoring.html create mode 100644 en/operations/kubernetes/operations/monitoring.mdx delete mode 100644 en/operations/kubernetes/operations/operations.html create mode 100644 en/operations/kubernetes/operations/operations.mdx delete mode 100644 en/operations/kubernetes/operations/resource-scaling.html delete mode 100644 en/operations/kubernetes/operations/upgrades.html create mode 100644 en/operations/kubernetes/operations/upgrades.mdx rename en/operations/kubernetes/{configuration/tls.html => tls.mdx} (57%) delete mode 100644 en/operations/kubernetes/vespa-on-kubernetes.html create mode 100644 en/operations/kubernetes/vespa-on-kubernetes.mdx delete mode 100644 en/operations/login.md create mode 100644 en/operations/login.mdx delete mode 100644 en/operations/metrics.html create mode 100644 en/operations/metrics.mdx delete mode 100644 en/operations/monitoring.html create mode 100644 en/operations/monitoring.mdx delete mode 100644 en/operations/notifications.md create mode 100644 en/operations/notifications.mdx delete mode 100644 en/operations/private-endpoints.html create mode 100644 en/operations/private-endpoints.mdx delete mode 100644 en/operations/production-deployment.html create mode 100644 en/operations/production-deployment.mdx delete mode 100644 en/operations/reindexing-cloud.html delete mode 100644 en/operations/reindexing.html create mode 100644 en/operations/reindexing.mdx delete mode 100644 en/operations/self-managed/admin-procedures.html create mode 100644 en/operations/self-managed/admin-procedures.mdx delete mode 100644 en/operations/self-managed/build-install.html create mode 100644 en/operations/self-managed/build-install.mdx delete mode 100644 en/operations/self-managed/config-proxy.html create mode 100644 en/operations/self-managed/config-proxy.mdx rename en/operations/self-managed/{config-sentinel.html => config-sentinel.mdx} (52%) delete mode 100644 en/operations/self-managed/configuration-server.html create mode 100644 en/operations/self-managed/configuration-server.mdx delete mode 100644 en/operations/self-managed/container.html create mode 100644 en/operations/self-managed/container.mdx delete mode 100644 en/operations/self-managed/content-node-recovery.html create mode 100644 en/operations/self-managed/content-node-recovery.mdx delete mode 100644 en/operations/self-managed/cpu-support.html create mode 100644 en/operations/self-managed/cpu-support.mdx delete mode 100644 en/operations/self-managed/docker-containers.html create mode 100644 en/operations/self-managed/docker-containers.mdx delete mode 100644 en/operations/self-managed/files-processes-and-ports.html create mode 100644 en/operations/self-managed/files-processes-and-ports.mdx delete mode 100644 en/operations/self-managed/live-upgrade.html create mode 100644 en/operations/self-managed/live-upgrade.mdx delete mode 100644 en/operations/self-managed/monitoring.html create mode 100644 en/operations/self-managed/monitoring.mdx delete mode 100644 en/operations/self-managed/multinode-systems.html create mode 100644 en/operations/self-managed/multinode-systems.mdx delete mode 100644 en/operations/self-managed/node-setup.html create mode 100644 en/operations/self-managed/node-setup.mdx rename en/operations/self-managed/{procedure-change-attribute-index.html => procedure-change-attribute-index.mdx} (54%) rename en/operations/self-managed/{sizing-examples.html => sizing-examples.mdx} (50%) delete mode 100644 en/operations/self-managed/slobrok.html create mode 100644 en/operations/self-managed/slobrok.mdx delete mode 100644 en/operations/self-managed/using-kubernetes-with-vespa.html create mode 100644 en/operations/self-managed/using-kubernetes-with-vespa.mdx delete mode 100644 en/operations/self-managed/vespa-gpu-container.html create mode 100644 en/operations/self-managed/vespa-gpu-container.mdx delete mode 100644 en/operations/self-managed/vespa-support.html create mode 100644 en/operations/self-managed/vespa-support.mdx delete mode 100644 en/operations/sso.md delete mode 100644 en/operations/telemetry-export.html delete mode 100644 en/operations/zones.html create mode 100644 en/operations/zones.mdx create mode 100644 en/performance.mdx delete mode 100644 en/performance/benchmarking-cloud.html create mode 100644 en/performance/benchmarking-cloud.mdx delete mode 100644 en/performance/benchmarking.html create mode 100644 en/performance/benchmarking.mdx delete mode 100644 en/performance/caches-in-vespa.html create mode 100644 en/performance/caches-in-vespa.mdx rename en/performance/{container-http.html => container-http.mdx} (56%) delete mode 100644 en/performance/container-tuning.html create mode 100644 en/performance/container-tuning.mdx delete mode 100644 en/performance/feature-tuning.html create mode 100644 en/performance/feature-tuning.mdx delete mode 100644 en/performance/graceful-degradation.html create mode 100644 en/performance/graceful-degradation.mdx delete mode 100644 en/performance/http2.html create mode 100644 en/performance/http2.mdx delete mode 100644 en/performance/index.html create mode 100644 en/performance/index.mdx delete mode 100644 en/performance/instance-types/aws-instance-types.html create mode 100644 en/performance/instance-types/aws-instance-types.mdx delete mode 100644 en/performance/instance-types/azure-instance-types.html create mode 100644 en/performance/instance-types/azure-instance-types.mdx delete mode 100644 en/performance/instance-types/gcp-instance-types.html create mode 100644 en/performance/instance-types/gcp-instance-types.mdx delete mode 100644 en/performance/memory-visualizer.md create mode 100644 en/performance/memory-visualizer.mdx delete mode 100644 en/performance/node-resources.md create mode 100644 en/performance/node-resources.mdx delete mode 100644 en/performance/practical-search-performance-guide.md create mode 100644 en/performance/practical-search-performance-guide.mdx delete mode 100644 en/performance/profiling.html create mode 100644 en/performance/profiling.mdx delete mode 100644 en/performance/rate-limiting-searcher.html create mode 100644 en/performance/rate-limiting-searcher.mdx delete mode 100644 en/performance/sizing-feeding.html create mode 100644 en/performance/sizing-feeding.mdx delete mode 100644 en/performance/sizing-search.html create mode 100644 en/performance/sizing-search.mdx delete mode 100644 en/performance/streaming-search.html create mode 100644 en/performance/streaming-search.mdx delete mode 100644 en/performance/topology-and-resizing.md create mode 100644 en/performance/topology-and-resizing.mdx delete mode 100644 en/performance/valgrind.html create mode 100644 en/performance/valgrind.mdx delete mode 100644 en/querying/approximate-nn-hnsw.md create mode 100644 en/querying/approximate-nn-hnsw.mdx delete mode 100644 en/querying/document-summaries.html create mode 100644 en/querying/document-summaries.mdx delete mode 100644 en/querying/federation.html create mode 100644 en/querying/federation.mdx delete mode 100644 en/querying/geo-search.html create mode 100644 en/querying/geo-search.mdx delete mode 100644 en/querying/grouping.html create mode 100644 en/querying/grouping.mdx rename en/querying/{nearest-neighbor-search-guide.md => nearest-neighbor-search-guide.mdx} (59%) delete mode 100644 en/querying/nearest-neighbor-search.md create mode 100644 en/querying/nearest-neighbor-search.mdx delete mode 100644 en/querying/page-templates.html create mode 100644 en/querying/page-templates.mdx delete mode 100644 en/querying/query-api.html create mode 100644 en/querying/query-api.mdx delete mode 100644 en/querying/query-language.html create mode 100644 en/querying/query-language.mdx delete mode 100644 en/querying/query-profiles.html create mode 100644 en/querying/query-profiles.mdx delete mode 100644 en/querying/result-diversity.md create mode 100644 en/querying/result-diversity.mdx rename en/querying/{searching-multivalue-fields.md => searching-multivalue-fields.mdx} (52%) delete mode 100644 en/querying/text-matching.html create mode 100644 en/querying/text-matching.mdx delete mode 100644 en/querying/vector-search-intro.html create mode 100644 en/querying/vector-search-intro.mdx rename en/rag/{binarizing-vectors.md => binarizing-vectors.mdx} (63%) rename en/rag/{document-enrichment.md => document-enrichment.mdx} (84%) delete mode 100644 en/rag/embedding.html create mode 100644 en/rag/embedding.mdx rename en/rag/{external-llms.md => external-llms.mdx} (64%) rename en/rag/{llms-in-vespa.md => llms-in-vespa.mdx} (65%) rename en/rag/{local-llms.md => local-llms.mdx} (77%) delete mode 100644 en/rag/model-hub.html create mode 100644 en/rag/model-hub.mdx rename en/rag/{rag.md => rag.mdx} (69%) delete mode 100644 en/rag/working-with-chunks.html create mode 100644 en/rag/working-with-chunks.mdx delete mode 100644 en/ranking/bm25.html create mode 100644 en/ranking/bm25.mdx rename en/ranking/{cross-encoders.md => cross-encoders.mdx} (50%) delete mode 100644 en/ranking/lightgbm.md create mode 100644 en/ranking/lightgbm.mdx delete mode 100644 en/ranking/multivalue-query-operators.html create mode 100644 en/ranking/multivalue-query-operators.mdx delete mode 100644 en/ranking/nativerank.html create mode 100644 en/ranking/nativerank.mdx rename en/ranking/{onnx.md => onnx.mdx} (85%) delete mode 100644 en/ranking/phased-ranking.html create mode 100644 en/ranking/phased-ranking.mdx delete mode 100644 en/ranking/ranking-expressions-features.html create mode 100644 en/ranking/ranking-expressions-features.mdx delete mode 100644 en/ranking/ranking-intro.md create mode 100644 en/ranking/ranking-intro.mdx delete mode 100644 en/ranking/reranking-in-searcher.md create mode 100644 en/ranking/reranking-in-searcher.mdx delete mode 100644 en/ranking/significance.md create mode 100644 en/ranking/significance.mdx delete mode 100644 en/ranking/stateless-model-evaluation.html create mode 100644 en/ranking/stateless-model-evaluation.mdx delete mode 100644 en/ranking/tensor-examples.html create mode 100644 en/ranking/tensor-examples.mdx delete mode 100644 en/ranking/tensor-user-guide.html create mode 100644 en/ranking/tensor-user-guide.mdx rename en/ranking/{tensorflow.md => tensorflow.mdx} (72%) delete mode 100644 en/ranking/wand.html create mode 100644 en/ranking/wand.mdx rename en/ranking/{xgboost.md => xgboost.mdx} (57%) delete mode 100644 en/reference/api/api.html create mode 100644 en/reference/api/api.mdx delete mode 100644 en/reference/api/application-v2.html create mode 100644 en/reference/api/application-v2.mdx delete mode 100644 en/reference/api/cluster-v2.html create mode 100644 en/reference/api/cluster-v2.mdx delete mode 100644 en/reference/api/config-v2.html create mode 100644 en/reference/api/config-v2.mdx delete mode 100644 en/reference/api/deploy-v2.html create mode 100644 en/reference/api/deploy-v2.mdx delete mode 100644 en/reference/api/document-v1.html create mode 100644 en/reference/api/document-v1.mdx delete mode 100644 en/reference/api/metrics-v1.html create mode 100644 en/reference/api/metrics-v1.mdx delete mode 100644 en/reference/api/metrics-v2.html create mode 100644 en/reference/api/metrics-v2.mdx delete mode 100644 en/reference/api/prometheus-v1.html create mode 100644 en/reference/api/prometheus-v1.mdx delete mode 100644 en/reference/api/query.html create mode 100644 en/reference/api/query.mdx delete mode 100644 en/reference/api/state-v1.html create mode 100644 en/reference/api/state-v1.mdx delete mode 100644 en/reference/applications/application-packages.html create mode 100644 en/reference/applications/application-packages.mdx delete mode 100644 en/reference/applications/components.html create mode 100644 en/reference/applications/components.mdx delete mode 100644 en/reference/applications/config-files.html create mode 100644 en/reference/applications/config-files.mdx delete mode 100644 en/reference/applications/deployment.html create mode 100644 en/reference/applications/deployment.mdx delete mode 100644 en/reference/applications/hosts.html create mode 100644 en/reference/applications/hosts.mdx delete mode 100644 en/reference/applications/services/admin.html create mode 100644 en/reference/applications/services/admin.mdx delete mode 100644 en/reference/applications/services/container.html create mode 100644 en/reference/applications/services/container.mdx delete mode 100644 en/reference/applications/services/content.html create mode 100644 en/reference/applications/services/content.mdx delete mode 100644 en/reference/applications/services/docproc.html create mode 100644 en/reference/applications/services/docproc.mdx delete mode 100644 en/reference/applications/services/http.html create mode 100644 en/reference/applications/services/http.mdx delete mode 100644 en/reference/applications/services/processing.html create mode 100644 en/reference/applications/services/processing.mdx delete mode 100644 en/reference/applications/services/search.html create mode 100644 en/reference/applications/services/search.mdx delete mode 100644 en/reference/applications/services/services.html create mode 100644 en/reference/applications/services/services.mdx delete mode 100644 en/reference/applications/testing-java.html create mode 100644 en/reference/applications/testing-java.mdx delete mode 100644 en/reference/applications/testing.html create mode 100644 en/reference/applications/testing.mdx delete mode 100644 en/reference/applications/validation-overrides.html create mode 100644 en/reference/applications/validation-overrides.mdx delete mode 100644 en/reference/clients/vespa-cli/vespa.md create mode 100644 en/reference/clients/vespa-cli/vespa.mdx rename en/reference/clients/vespa-cli/{vespa_activate.md => vespa_activate.mdx} (82%) rename en/reference/clients/vespa-cli/{vespa_auth.md => vespa_auth.mdx} (50%) rename en/reference/clients/vespa-cli/{vespa_auth_api-key.md => vespa_auth_api-key.mdx} (66%) rename en/reference/clients/vespa-cli/{vespa_auth_cert.md => vespa_auth_cert.mdx} (66%) rename en/reference/clients/vespa-cli/{vespa_auth_cert_add.md => vespa_auth_cert_add.mdx} (79%) rename en/reference/clients/vespa-cli/{vespa_auth_login.md => vespa_auth_login.mdx} (54%) rename en/reference/clients/vespa-cli/{vespa_auth_logout.md => vespa_auth_logout.mdx} (82%) rename en/reference/clients/vespa-cli/{vespa_auth_show.md => vespa_auth_show.mdx} (83%) rename en/reference/clients/vespa-cli/{vespa_clone.md => vespa_clone.mdx} (75%) rename en/reference/clients/vespa-cli/{vespa_completion.md => vespa_completion.mdx} (50%) rename en/reference/clients/vespa-cli/{vespa_completion_bash.md => vespa_completion_bash.mdx} (74%) rename en/reference/clients/vespa-cli/{vespa_completion_fish.md => vespa_completion_fish.mdx} (83%) rename en/reference/clients/vespa-cli/{vespa_completion_powershell.md => vespa_completion_powershell.mdx} (78%) rename en/reference/clients/vespa-cli/{vespa_completion_zsh.md => vespa_completion_zsh.mdx} (79%) delete mode 100644 en/reference/clients/vespa-cli/vespa_config.md create mode 100644 en/reference/clients/vespa-cli/vespa_config.mdx rename en/reference/clients/vespa-cli/{vespa_config_get.md => vespa_config_get.mdx} (61%) rename en/reference/clients/vespa-cli/{vespa_config_set.md => vespa_config_set.mdx} (68%) rename en/reference/clients/vespa-cli/{vespa_config_unset.md => vespa_config_unset.mdx} (69%) rename en/reference/clients/vespa-cli/{vespa_curl.md => vespa_curl.mdx} (86%) rename en/reference/clients/vespa-cli/{vespa_deploy.md => vespa_deploy.mdx} (72%) rename en/reference/clients/vespa-cli/{vespa_destroy.md => vespa_destroy.mdx} (62%) rename en/reference/clients/vespa-cli/{vespa_document.md => vespa_document.mdx} (60%) rename en/reference/clients/vespa-cli/{vespa_document_get.md => vespa_document_get.mdx} (88%) rename en/reference/clients/vespa-cli/{vespa_document_put.md => vespa_document_put.mdx} (76%) rename en/reference/clients/vespa-cli/{vespa_document_remove.md => vespa_document_remove.mdx} (79%) rename en/reference/clients/vespa-cli/{vespa_document_update.md => vespa_document_update.mdx} (82%) rename en/reference/clients/vespa-cli/{vespa_feed.md => vespa_feed.mdx} (89%) rename en/reference/clients/vespa-cli/{vespa_fetch.md => vespa_fetch.mdx} (77%) rename en/reference/clients/vespa-cli/{vespa_inspect.md => vespa_inspect.mdx} (77%) rename en/reference/clients/vespa-cli/{vespa_inspect_profile.md => vespa_inspect_profile.mdx} (81%) rename en/reference/clients/vespa-cli/{vespa_log.md => vespa_log.mdx} (90%) rename en/reference/clients/vespa-cli/{vespa_prepare.md => vespa_prepare.mdx} (83%) rename en/reference/clients/vespa-cli/{vespa_prod.md => vespa_prod.mdx} (69%) rename en/reference/clients/vespa-cli/{vespa_prod_deploy.md => vespa_prod_deploy.mdx} (78%) rename en/reference/clients/vespa-cli/{vespa_prod_init.md => vespa_prod_init.mdx} (60%) rename en/reference/clients/vespa-cli/{vespa_query.md => vespa_query.mdx} (84%) rename en/reference/clients/vespa-cli/{vespa_status.md => vespa_status.mdx} (71%) rename en/reference/clients/vespa-cli/{vespa_status_deploy.md => vespa_status_deploy.mdx} (84%) rename en/reference/clients/vespa-cli/{vespa_status_deployment.md => vespa_status_deployment.mdx} (79%) delete mode 100644 en/reference/clients/vespa-cli/vespa_status_document.md create mode 100644 en/reference/clients/vespa-cli/vespa_status_document.mdx rename en/reference/clients/vespa-cli/{vespa_status_endpoint.md => vespa_status_endpoint.mdx} (78%) delete mode 100644 en/reference/clients/vespa-cli/vespa_status_query.md create mode 100644 en/reference/clients/vespa-cli/vespa_status_query.mdx rename en/reference/clients/vespa-cli/{vespa_test.md => vespa_test.mdx} (80%) rename en/reference/clients/vespa-cli/{vespa_version.md => vespa_version.mdx} (82%) rename en/reference/clients/vespa-cli/{vespa_visit.md => vespa_visit.mdx} (92%) delete mode 100644 en/reference/clustercontroller-metrics-reference.html delete mode 100644 en/reference/container-metrics-reference.html delete mode 100644 en/reference/default-set-metrics-reference.html delete mode 100644 en/reference/logd-metrics-reference.html delete mode 100644 en/reference/nodeadmin-metrics-reference.html delete mode 100644 en/reference/operations/health-checks.html create mode 100644 en/reference/operations/health-checks.mdx delete mode 100644 en/reference/operations/log-files.html create mode 100644 en/reference/operations/log-files.mdx delete mode 100644 en/reference/operations/metrics/clustercontroller.html create mode 100644 en/reference/operations/metrics/clustercontroller.mdx delete mode 100644 en/reference/operations/metrics/configserver.html rename en/reference/{configserver-metrics-reference.html => operations/metrics/configserver.mdx} (58%) rename en/reference/operations/metrics/{container.html => container.mdx} (56%) delete mode 100644 en/reference/operations/metrics/default-metric-set.html create mode 100644 en/reference/operations/metrics/default-metric-set.mdx delete mode 100644 en/reference/operations/metrics/distributor.html rename en/reference/{distributor-metrics-reference.html => operations/metrics/distributor.mdx} (53%) delete mode 100644 en/reference/operations/metrics/logd.html create mode 100644 en/reference/operations/metrics/logd.mdx delete mode 100644 en/reference/operations/metrics/metric-units.html rename en/reference/{unit-metrics-reference.html => operations/metrics/metric-units.mdx} (95%) delete mode 100644 en/reference/operations/metrics/metrics.md create mode 100644 en/reference/operations/metrics/metrics.mdx delete mode 100644 en/reference/operations/metrics/nodeadmin.html create mode 100644 en/reference/operations/metrics/nodeadmin.mdx delete mode 100644 en/reference/operations/metrics/searchnode.html create mode 100644 en/reference/operations/metrics/searchnode.mdx delete mode 100644 en/reference/operations/metrics/sentinel.html create mode 100644 en/reference/operations/metrics/sentinel.mdx delete mode 100644 en/reference/operations/metrics/slobrok.html create mode 100644 en/reference/operations/metrics/slobrok.mdx delete mode 100644 en/reference/operations/metrics/storage.html create mode 100644 en/reference/operations/metrics/storage.mdx delete mode 100644 en/reference/operations/metrics/vespa-metric-set.html create mode 100644 en/reference/operations/metrics/vespa-metric-set.mdx delete mode 100644 en/reference/operations/self-managed/tools.html create mode 100644 en/reference/operations/self-managed/tools.mdx delete mode 100644 en/reference/operations/tools.html create mode 100644 en/reference/operations/tools.mdx delete mode 100644 en/reference/querying/default-result-format.html create mode 100644 en/reference/querying/default-result-format.mdx delete mode 100644 en/reference/querying/grouping-language.html create mode 100644 en/reference/querying/grouping-language.mdx rename en/reference/querying/{json-query-language.md => json-query-language.mdx} (85%) delete mode 100644 en/reference/querying/page-result-format.html create mode 100644 en/reference/querying/page-result-format.mdx delete mode 100644 en/reference/querying/page-templates.html create mode 100644 en/reference/querying/page-templates.mdx delete mode 100644 en/reference/querying/query-profiles.html create mode 100644 en/reference/querying/query-profiles.mdx delete mode 100644 en/reference/querying/semantic-rules.html create mode 100644 en/reference/querying/semantic-rules.mdx delete mode 100644 en/reference/querying/simple-query-language.html create mode 100644 en/reference/querying/simple-query-language.mdx delete mode 100644 en/reference/querying/sorting-language.html create mode 100644 en/reference/querying/sorting-language.mdx delete mode 100644 en/reference/querying/yql.html create mode 100644 en/reference/querying/yql.mdx delete mode 100644 en/reference/rag/chunking.html create mode 100644 en/reference/rag/chunking.mdx delete mode 100644 en/reference/rag/embedding.html create mode 100644 en/reference/rag/embedding.mdx delete mode 100644 en/reference/ranking/constant-tensor-json-format.html create mode 100644 en/reference/ranking/constant-tensor-json-format.mdx delete mode 100644 en/reference/ranking/model-files.html create mode 100644 en/reference/ranking/model-files.mdx delete mode 100644 en/reference/ranking/nativerank.html create mode 100644 en/reference/ranking/nativerank.mdx delete mode 100644 en/reference/ranking/rank-feature-configuration.html create mode 100644 en/reference/ranking/rank-feature-configuration.mdx delete mode 100644 en/reference/ranking/rank-features.html create mode 100644 en/reference/ranking/rank-features.mdx delete mode 100644 en/reference/ranking/rank-types.html create mode 100644 en/reference/ranking/rank-types.mdx delete mode 100644 en/reference/ranking/ranking-expressions.html create mode 100644 en/reference/ranking/ranking-expressions.mdx delete mode 100644 en/reference/ranking/string-segment-match.html create mode 100644 en/reference/ranking/string-segment-match.mdx delete mode 100644 en/reference/ranking/tensor.html create mode 100644 en/reference/ranking/tensor.mdx delete mode 100644 en/reference/release-notes/vespa7.html create mode 100644 en/reference/release-notes/vespa7.mdx rename en/reference/release-notes/{vespa8-geo-migration-guide.md => vespa8-geo-migration-guide.mdx} (58%) delete mode 100644 en/reference/release-notes/vespa8.html create mode 100644 en/reference/release-notes/vespa8.mdx delete mode 100644 en/reference/release-notes/vespa9.html create mode 100644 en/reference/release-notes/vespa9.mdx delete mode 100644 en/reference/schemas/document-field-path.html create mode 100644 en/reference/schemas/document-field-path.mdx delete mode 100644 en/reference/schemas/document-json-format.html create mode 100644 en/reference/schemas/document-json-format.mdx delete mode 100644 en/reference/schemas/schemas.html create mode 100644 en/reference/schemas/schemas.mdx delete mode 100644 en/reference/searchnode-metrics-reference.html delete mode 100644 en/reference/security/mtls.md create mode 100644 en/reference/security/mtls.mdx delete mode 100644 en/reference/sentinel-metrics-reference.html delete mode 100644 en/reference/slobrok-metrics-reference.html delete mode 100644 en/reference/storage-metrics-reference.html delete mode 100644 en/reference/vespa-set-metrics-reference.html delete mode 100644 en/reference/writing/document-selector-language.html create mode 100644 en/reference/writing/document-selector-language.mdx delete mode 100644 en/reference/writing/indexing-language.html create mode 100644 en/reference/writing/indexing-language.mdx delete mode 100644 en/schemas/concrete-documents.html create mode 100644 en/schemas/concrete-documents.mdx delete mode 100644 en/schemas/documents.html create mode 100644 en/schemas/documents.mdx rename en/schemas/{exposing-schema-information.html => exposing-schema-information.mdx} (53%) delete mode 100644 en/schemas/inheritance-in-schemas.html create mode 100644 en/schemas/inheritance-in-schemas.mdx delete mode 100644 en/schemas/parent-child.html create mode 100644 en/schemas/parent-child.mdx delete mode 100644 en/schemas/predicate-fields.html create mode 100644 en/schemas/predicate-fields.mdx delete mode 100644 en/schemas/structs.html create mode 100644 en/schemas/structs.mdx rename en/security/{cloudflare-workers.md => cloudflare-workers.mdx} (70%) rename en/security/{guide.md => guide.mdx} (54%) delete mode 100644 en/security/mtls.md create mode 100644 en/security/mtls.mdx delete mode 100644 en/security/secret-store.md create mode 100644 en/security/secret-store.mdx delete mode 100644 en/security/securing-your-vespa-installation.md create mode 100644 en/security/securing-your-vespa-installation.mdx delete mode 100644 en/security/security.md create mode 100644 en/security/security.mdx delete mode 100644 en/security/whitepaper.md create mode 100644 en/security/whitepaper.mdx delete mode 100644 en/writing/batch-delete.html create mode 100644 en/writing/batch-delete.mdx delete mode 100644 en/writing/document-api-guide.html create mode 100644 en/writing/document-api-guide.mdx delete mode 100644 en/writing/document-routing.html create mode 100644 en/writing/document-routing.mdx delete mode 100644 en/writing/document-v1-api-guide.html create mode 100644 en/writing/document-v1-api-guide.mdx delete mode 100644 en/writing/feed-block.html create mode 100644 en/writing/feed-block.mdx delete mode 100644 en/writing/indexing-paged-vectors.html create mode 100644 en/writing/indexing-paged-vectors.mdx delete mode 100644 en/writing/indexing.html create mode 100644 en/writing/indexing.mdx delete mode 100644 en/writing/initial-batch-feed.html create mode 100644 en/writing/initial-batch-feed.mdx delete mode 100644 en/writing/partial-updates.html create mode 100644 en/writing/partial-updates.mdx delete mode 100644 en/writing/reads-and-writes.html create mode 100644 en/writing/reads-and-writes.mdx delete mode 100644 en/writing/visiting.html create mode 100644 en/writing/visiting.mdx delete mode 100755 extract_redirects.py rename icons/favicon-32x32.png => favicon.png (100%) delete mode 100755 feed-split.py delete mode 100755 feed_to_vespa.py delete mode 100755 generate_suggestions_from_reference.py delete mode 100644 icons/android-chrome-192x192.png delete mode 100644 icons/android-chrome-384x384.png delete mode 100644 icons/android-chrome-512x512.png delete mode 100644 icons/apple-touch-icon.png delete mode 100644 icons/browserconfig.xml delete mode 100644 icons/favicon-16x16.png delete mode 100644 icons/favicon.ico delete mode 100644 icons/manifest.json delete mode 100644 icons/mstile-144x144.png delete mode 100644 icons/mstile-150x150.png delete mode 100644 icons/mstile-310x150.png delete mode 100644 icons/mstile-310x310.png delete mode 100644 icons/mstile-70x70.png delete mode 100644 icons/safari-pinned-tab.svg delete mode 100644 icons/site.webmanifest delete mode 100644 index.html create mode 100644 index.mdx delete mode 100644 ja/features.html create mode 100644 ja/features.mdx rename ja/{introduction-to-documentation.html => introduction-to-documentation.mdx} (69%) delete mode 100644 ja/overview.html create mode 100644 ja/overview.mdx delete mode 100644 ja/vespa-quick-start.html create mode 100644 ja/vespa-quick-start.mdx delete mode 100644 js/handle_results.js delete mode 100644 js/handle_search_suggestions.js delete mode 100644 js/header_links.js delete mode 100644 js/load-mathjax.js delete mode 100644 js/pre_copy.js delete mode 100644 js/process_pre.js delete mode 100644 js/query_examples.js delete mode 100644 js/search.js delete mode 100644 js/search_suggestions.js delete mode 100644 js/table-list-converter.js delete mode 100644 llms-template.md create mode 100644 logo/dark.svg create mode 100644 logo/hero-dark.svg create mode 100644 logo/hero-light.svg create mode 100644 logo/light.svg delete mode 100644 playground/css/playground.css delete mode 100644 playground/css/typography.css delete mode 100644 playground/icons/favicon-16x16.png delete mode 100644 playground/icons/favicon-32x32.png delete mode 100644 playground/index.html delete mode 100644 playground/js/d3.min.js delete mode 100644 playground/js/lz-string.min.js delete mode 100644 playground/js/playground.js delete mode 100644 playground/js/showdown.min.js delete mode 100644 questions.jsonl delete mode 100644 redirects.yml delete mode 100644 renovate.json delete mode 100644 robots.txt delete mode 100644 search.html delete mode 100644 search_beta.html delete mode 100644 sitemap.html delete mode 100644 suggestions_index.json delete mode 100644 test/README.md delete mode 100644 test/_test_config-large.yml delete mode 100644 test/_test_config.yml delete mode 100644 test/deduplicate_suggestions.py delete mode 100644 test/pseudo_terminal.py delete mode 100644 test/requirements.txt delete mode 100755 test/test.py delete mode 100755 test/test_queries.py diff --git a/.github/actions/commit-updated-docs/action.yml b/.github/actions/commit-updated-docs/action.yml deleted file mode 100644 index ceee41fd25..0000000000 --- a/.github/actions/commit-updated-docs/action.yml +++ /dev/null @@ -1,61 +0,0 @@ -name: Commit via PR -description: Commit changes to the Vespa documentation via a PR. - -inputs: - commit-message: - description: "The commit message to use when updating the documentation." - required: true - files: - description: "The files to commit." - required: true - push: - description: "Whether to push the changes to the repository, set to 'true' to push." - required: false - default: "false" - -runs: - using: "composite" - steps: - - name: Setup git - shell: bash - id: git-setup - run: | - git config --global user.email "bot+actions@vespa.ai" - git config --global user.name "${{ github.actor}}" - - # Find a suitable branch name for the PR. - PR_BRANCH_NAME="update-docs-$(date +%s)" - echo "branch=$PR_BRANCH_NAME" >> $GITHUB_OUTPUT - - - name: Commit - shell: bash - env: - PR_BRANCH_NAME: ${{ steps.git-setup.outputs.branch }} - run: | - git pull --rebase --autostash --quiet - git add ${{ inputs.files }} - git commit -m "${{ inputs.commit-message }}" - - - name: Create PR - shell: bash - id: pr - if: ${{ inputs.push == 'true' }} - env: - GH_TOKEN: ${{ github.token }} - PR_BRANCH_NAME: ${{ steps.git-setup.outputs.branch }} - run: | - git checkout -b "${PR_BRANCH_NAME}" - git push --set-upstream origin "${PR_BRANCH_NAME}" - - PR_URL=$(gh pr create --base master --head "${PR_BRANCH_NAME}" \ - --title "${{ inputs.commit-message }} MERGEOK" \ - --body "This PR was created by a GitHub Action.") - - echo "pr-url=${PR_URL}" >> $GITHUB_OUTPUT - - # Allow some time for the "Merge Stop Enforcer" to run. - # This is a workaround as the 'Auto-Merge' feature is not set up. - sleep 15 - - # Merge the PR with a rebase to ensure a clean history. - gh pr merge --squash "${PR_URL}" diff --git a/.github/actions/install-deps/action.yml b/.github/actions/install-deps/action.yml deleted file mode 100644 index 500b5b3391..0000000000 --- a/.github/actions/install-deps/action.yml +++ /dev/null @@ -1,56 +0,0 @@ -name: "Setup Vespa CLI and dependencies" -description: "Install Vespa CLI and dependencies" - -inputs: - # The version of the Vespa CLI to install. - vespa-cli-version: - description: "The version of the Vespa CLI to install." - required: true - default: "latest" - -runs: - using: "composite" - steps: - - name: Select CLI version - shell: bash - id: vespa-cli-version - run: | - if [ "${{ inputs.vespa-cli-version }}" == "latest" ]; then - VESPA_CLI_VERSION=$(curl -fsSL https://api.github.com/repos/vespa-engine/vespa/releases/latest | grep -Po '"tag_name": "v\K.*?(?=")') - else - VESPA_CLI_VERSION="${{ inputs.vespa-cli-version }}" - fi - - echo "version=${VESPA_CLI_VERSION}" >> "$GITHUB_OUTPUT" - - # - name: Install dnf dependencies - # shell: bash - # run: | - # dnf config-manager --add-repo=https://download.docker.com/linux/centos/docker-ce.repo - # dnf -y install docker-ce protobuf-compiler protobuf-devel python3 python3-pip --nobest - - - name: Setup Ruby - uses: ruby/setup-ruby@v1 - with: - ruby-version: 4.0.3 - bundler-cache: true - - - uses: actions/setup-java@v5 - with: - distribution: "temurin" - java-version: "17" - - - name: Install python dependencies - shell: bash - run: | - python3 -m pip install --upgrade pip - python3 -m pip install -qqq -r test/requirements.txt --user - python3 -m pip install -qqq pytest nbmake --user - - - name: Install Vespa CLI - shell: bash - env: - VESPA_CLI_VERSION: ${{ steps.vespa-cli-version.outputs.version }} - run: | - curl -fsSL https://github.com/vespa-engine/vespa/releases/download/v${VESPA_CLI_VERSION}/vespa-cli_${VESPA_CLI_VERSION}_linux_amd64.tar.gz | tar -zxf - -C /opt && \ - ln -sf /opt/vespa-cli_${VESPA_CLI_VERSION}_linux_amd64/bin/vespa /usr/local/bin/ diff --git a/.github/workflows/auto-update-documentation.yml b/.github/workflows/auto-update-documentation.yml deleted file mode 100644 index 8f5fee7884..0000000000 --- a/.github/workflows/auto-update-documentation.yml +++ /dev/null @@ -1,168 +0,0 @@ -name: Autoupdate Documentation -# -# Takes care of updating the Vespa documentation. -# - -on: - workflow_dispatch: # Allow manual triggering of the workflow - - schedule: - - cron: "0 0 * * *" - -permissions: - contents: write - pull-requests: write - -defaults: - run: - # This ensures that options "pipefail" and "errexit" are set for all steps. - # Ref: https://docs.github.com/en/actions/writing-workflows/workflow-syntax-for-github-actions#defaultsrunshell - shell: bash - -# Limit concurrency to better handle commit and push operations. -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - -jobs: - update-vespa-version: - # - # This job updates the Vespa version in the documentation. - # - runs-on: ubuntu-24.04 - - steps: - - uses: actions/checkout@v6 - - - name: Setup Dependencies - run: | - sudo apt-get update - sudo apt-get install -y xq - - - name: Update Vespa version - id: update-docs - run: | - VESPA_VERSION=$(curl -sSL https://repo1.maven.org/maven2/com/yahoo/vespa/parent/maven-metadata.xml | \ - xq -x '/metadata/versioning/latest') - - echo "Vespa version: $VESPA_VERSION" - echo "version=$VESPA_VERSION" >> $GITHUB_OUTPUT - - # Update the Vespa version in the documentation configuration file. - sed -i'' "s/\(vespa_version:\) \"[0-9\.]*\"/\1 \"${VESPA_VERSION}\"/" _config.yml - - git diff - echo "has-changes=$(git status --short | wc -l)" >> $GITHUB_OUTPUT - - - name: Commit changes - if: ${{ steps.update-docs.outputs.has-changes != '0' }} - uses: ./.github/actions/commit-updated-docs - with: - commit-message: "Update Vespa version to ${{ steps.update-docs.outputs.version }}." - files: _config.yml - push: ${{ contains(fromJSON('["workflow_dispatch", "schedule"]'), github.event_name) && github.ref_name == 'master' }} - - update-vespa-cli-doc: - # - # This job updates the Vespa CLI documentation. - # - runs-on: ubuntu-latest - - # Make sure each job runs after the previous one, regardless of the outcome. - # This trick is useful to prevent two jobs from running concurrently and trying to - # push changes to the repository at the same time. - if: ${{ success() || failure() }} - needs: - - update-vespa-version - - env: - VESPA_CLI_DOC_DIR: en/reference/clients/vespa-cli - - steps: - - uses: actions/checkout@v6 - - - name: Install Vespa CLI - uses: vespa-engine/setup-vespa-cli-action@v1 - - - name: Generate Vespa CLI Documentation - id: update-docs - working-directory: ${{ env.VESPA_CLI_DOC_DIR }} - run: | - vespa gendoc . - - for f in $(git status --short . | awk '{print $2}'); do - title=${f//.md/} - title=${title//.\//} - title=${title//_/ } - (echo -e "---\ntitle: $title\nrender_with_liquid: false\n---\n"; cat ${f}) > ${f}.new - mv ${f}.new ${f}; - done - - # ensure links are to .html - sed -i'' 's/\(vespa.*\).md)/\1.html)/' *.md - # create links - sed -i'' 's#\(https://[a-z.]*vespa.ai/[^[:space:]]*\)#[\1](\1)#g' *.md - - git diff - echo "has-changes=$(git status --short | wc -l)" >> $GITHUB_OUTPUT - - - name: Commit changes - if: ${{ steps.update-docs.outputs.has-changes != '0' }} - uses: ./.github/actions/commit-updated-docs - with: - commit-message: "Update 'Vespa-CLI' reference documentation." - files: ${{ env.VESPA_CLI_DOC_DIR }} - push: ${{ contains(fromJSON('["workflow_dispatch", "schedule"]'), github.event_name) && github.ref_name == 'master' }} - - update-metric-reference: - # - # This job updates the Vespa metric reference documentation. - # Ref https://github.com/vespa-engine/vespa/blob/master/metrics/src/main/java/ai/vespa/metrics/docs/MetricDocumentation.java - # - runs-on: ubuntu-24.04 # Use Ubuntu 24.04 for the xq dependency - - # Make sure each job runs after the previous one, regardless of the outcome. - # This trick is useful to prevent two jobs from running concurrently and trying to - # push changes to the repository at the same time. - if: ${{ success() || failure() }} - needs: - - update-vespa-cli-doc - - steps: - - uses: actions/checkout@v6 - - - uses: actions/setup-java@v5 - with: - distribution: "temurin" - java-version: "17" - - - name: Setup xq - run: | - sudo apt-get install -y xq - - - name: Get Latest Vespa version - id: vespa-metadata - run: | - VESPA_VERSION=$(curl -sSL https://repo1.maven.org/maven2/com/yahoo/vespa/parent/maven-metadata.xml | \ - xq -x '/metadata/versioning/latest') - echo "Using version: ${VESPA_VERSION}" - echo "version=$VESPA_VERSION" >> "${GITHUB_OUTPUT}" - - - name: Update Metric Reference - id: update-docs - env: - VESPA_VERSION: ${{ steps.vespa-metadata.outputs.version }} - run: | - curl -SsLo metrics.jar "https://repo1.maven.org/maven2/com/yahoo/vespa/metrics/${VESPA_VERSION}/metrics-${VESPA_VERSION}.jar" - java -cp metrics.jar ai.vespa.metrics.docs.DocumentationGenerator en/reference/operations/metrics - rm metrics.jar - - git diff -- en/reference - echo "has-changes=$(git status --short -- en/reference | wc -l)" >> "${GITHUB_OUTPUT}" - - - name: Commit changes - if: ${{ steps.update-docs.outputs.has-changes != '0' }} - uses: ./.github/actions/commit-updated-docs - with: - commit-message: "Update 'Metric' reference documentation." - files: "en/reference/operations/metrics/*.html" - push: ${{ contains(fromJSON('["workflow_dispatch", "schedule"]'), github.event_name) && github.ref_name == 'master' }} diff --git a/.github/workflows/build-pages.yml b/.github/workflows/build-pages.yml deleted file mode 100644 index af7120f15b..0000000000 --- a/.github/workflows/build-pages.yml +++ /dev/null @@ -1,29 +0,0 @@ -name: Build Jekyll site (PR check) - -on: - pull_request: - workflow_dispatch: - -permissions: - contents: read - -concurrency: - group: build-pages-${{ github.ref }} - cancel-in-progress: true - -jobs: - build: - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v6 - - name: Setup Ruby - uses: ruby/setup-ruby@v1 - with: - ruby-version: "4.0.3" - bundler-cache: true - cache-version: 0 - - name: Build with Jekyll - run: bundle exec jekyll build - env: - JEKYLL_ENV: production diff --git a/.github/workflows/deploy-pages.yml b/.github/workflows/deploy-pages.yml deleted file mode 100644 index c06552831f..0000000000 --- a/.github/workflows/deploy-pages.yml +++ /dev/null @@ -1,66 +0,0 @@ -# This workflow uses actions that are not certified by GitHub. -# They are provided by a third-party and are governed by -# separate terms of service, privacy policy, and support -# documentation. - -# Sample workflow for building and deploying a Jekyll site to GitHub Pages -name: Deploy Jekyll site to Pages - -on: - # Runs on pushes targeting the default branch - push: - branches: ["master"] - - # Allows you to run this workflow manually from the Actions tab - workflow_dispatch: - -# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages -permissions: - contents: write # Need write to commit changes to llms.txt - pages: write - id-token: write - pull-requests: write # To create PR with updated llms.txt - -# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. -# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. -concurrency: - group: "pages" - cancel-in-progress: false - -jobs: - # Build job - build: - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v6 - - name: Setup Ruby - # https://github.com/ruby/setup-ruby/releases/tag/v1.207.0 - uses: ruby/setup-ruby@v1 - with: - ruby-version: "4.0.3" # Not needed with a .ruby-version file - bundler-cache: true # runs 'bundle install' and caches installed gems automatically - cache-version: 0 # Increment this number if you need to re-download cached gems - - name: Setup Pages - id: pages - uses: actions/configure-pages@983d7736d9b0ae728b81ab479565c72886d7745b # v5 - - name: Build with Jekyll - # Outputs to the './_site' directory by default - run: bundle exec jekyll build --baseurl "${{ steps.pages.outputs.base_path }}" - env: - JEKYLL_ENV: production - - name: Upload artifact - # Automatically uploads an artifact from the './_site' directory by default - uses: actions/upload-pages-artifact@v4 - - # Deployment job - deploy: - environment: - name: github-pages - url: ${{ steps.deployment.outputs.page_url }} - runs-on: ubuntu-latest - needs: build - steps: - - name: Deploy to GitHub Pages - id: deployment - uses: actions/deploy-pages@d6db90164ac5ed86f2b6aed7e0febac5b3c0c03e # v4 diff --git a/.github/workflows/feed.yml b/.github/workflows/feed.yml deleted file mode 100644 index 33ce4d5972..0000000000 --- a/.github/workflows/feed.yml +++ /dev/null @@ -1,85 +0,0 @@ -name: Vespa Documentation Search Feed -on: - workflow_dispatch: - push: - branches: [ master ] - paths: - - '*.md' - - '*.html' - - '**/*.md' - - '**/*.html' - - '.github/workflows/feed.yml' - - '_*config.yml' - - 'feed_to_vespa.py' - -env: - DATA_PLANE_PUBLIC_KEY : ${{ secrets.VESPA_TEAM_DATA_PLANE_PUBLIC_CERT }} - DATA_PLANE_PRIVATE_KEY : ${{ secrets.VESPA_TEAM_DATA_PLANE_PRIVATE_KEY }} - VESPA_CLI_DATA_PLANE_CERT : ${{ secrets.VESPA_TEAM_VESPA_CLI_DATA_PLANE_CERT }} - VESPA_CLI_DATA_PLANE_KEY : ${{ secrets.VESPA_TEAM_VESPA_CLI_DATA_PLANE_KEY }} - -jobs: - build: - runs-on: ubuntu-latest - steps: - - - uses: actions/checkout@v6 - - - uses: ruby/setup-ruby@v1 - with: - ruby-version: 4.0.3 - bundler-cache: false - - - name: Build site - run: | - bundle install - bundle exec jekyll build -p _plugins-vespafeed - - - name: Setup Python - uses: actions/setup-python@v6 - with: - python-version: '3.x' - - - name: Install dependencies - run: | - pip3 install PyYAML mmh3 requests html5lib beautifulsoup4 markdownify tiktoken - - - name: Install Vespa CLI - uses: vespa-engine/setup-vespa-cli-action@v1 - - - name: Feed docs site - run: | - # The python scripts below uses the Vespa CLI for feeding / data access. - # See https://docs.vespa.ai/en/vespa-cli.html. - # The environment variables below have credentials for endpoint access - - # use the key/cert files in .vespa and paste their content into GitHub Secrets. - export VESPA_CLI_DATA_PLANE_CERT - export VESPA_CLI_DATA_PLANE_KEY - ./feed_to_vespa.py _config.yml - - - name: Feed paragraphs site - run: | - ./feed-split.py open_index.json https://docs.vespa.ai questions.jsonl - ./feed_to_vespa.py _paragraphs_config.yml - - - name: Feed suggestions - run: | - ./feed_to_vespa.py _suggestions_config.yml - - - - name: Generate and feed reference suggestions - run: | - ./generate_suggestions_from_reference.py en/reference/schemas/schemas.html https://docs.vespa.ai/ open-p > suggestions.jsonl - ./generate_suggestions_from_reference.py en/reference/api/query.html https://docs.vespa.ai/ open-p > suggestions.jsonl - ./generate_suggestions_from_reference.py en/reference/querying/yql.html https://docs.vespa.ai/ open-p > suggestions.jsonl - ./generate_suggestions_from_reference.py en/reference/writing/indexing-language.html https://docs.vespa.ai/ open-p > suggestions.jsonl - ./generate_suggestions_from_reference.py en/reference/applications/services/services.html https://docs.vespa.ai/ open-p >> suggestions.jsonl - ./generate_suggestions_from_reference.py en/reference/applications/services/admin.html https://docs.vespa.ai/ open-p >> suggestions.jsonl - ./generate_suggestions_from_reference.py en/reference/applications/services/container.html https://docs.vespa.ai/ open-p >> suggestions.jsonl - ./generate_suggestions_from_reference.py en/reference/applications/services/docproc.html https://docs.vespa.ai/ open-p >> suggestions.jsonl - ./generate_suggestions_from_reference.py en/reference/applications/services/processing.html https://docs.vespa.ai/ open-p >> suggestions.jsonl - ./generate_suggestions_from_reference.py en/reference/applications/services/search.html https://docs.vespa.ai/ open-p >> suggestions.jsonl - ./generate_suggestions_from_reference.py en/reference/applications/services/http.html https://docs.vespa.ai/ open-p >> suggestions.jsonl - ./generate_suggestions_from_reference.py en/reference/applications/services/content.html https://docs.vespa.ai/ open-p >> suggestions.jsonl - ./convert.py suggestions.jsonl suggestions_reference_index.json - ./feed_to_vespa.py _suggestions_reference_config.yml diff --git a/.github/workflows/link-checker.yml b/.github/workflows/link-checker.yml deleted file mode 100644 index f669bdabe9..0000000000 --- a/.github/workflows/link-checker.yml +++ /dev/null @@ -1,42 +0,0 @@ -name: Link checker - -on: - workflow_dispatch: - schedule: - - cron: "00 3 * * 1,3,5" - -jobs: - test: - uses: vespa-engine/gh-actions/.github/workflows/jekyll-link-checker.yml@main - secrets: - github-app-id: ${{ secrets.LINK_CHECKER_APP_ID }} - github-app-priv-key: ${{ secrets.LINK_CHECKER_PRIVATE_KEY }} - with: - ignore-files: |- - /playground/index.html/ - ignore-urls: |- - /aclanthology.org/ - /arxiv.org/ - /cwiki.apache.org/ - /datatracker.ietf.org/ - /docs.vespa.ai/playground/ - /hub.docker.com/r/ - /javadoc.io.*#/ - /linux.die.net/ - /localhost:8080/ - /localhost:9090/ - /platform.openai.com/ - /readthedocs.io.*#/ - /search.maven.org/ - /slack.vespa.ai/ - /www.ietf.org/ - /www.baeldung.com/ - /cohere.com/ - /dl.acm.org/doi/ - /anthropic.com/ - /stackoverflow.com/ - /cloudsmith.com/ - /travis-ci.com/ - /www.terraform.io/ - /developer.hashicorp.com/ - /www.freedesktop.org/ diff --git a/.github/workflows/verify-guides-large.yml b/.github/workflows/verify-guides-large.yml deleted file mode 100644 index a6049e2e78..0000000000 --- a/.github/workflows/verify-guides-large.yml +++ /dev/null @@ -1,24 +0,0 @@ -name: Verify Guides (Large) - -on: - workflow_dispatch: # Allow manual triggering of this workflow. - - push: - branches: ["master"] - paths: - - 'en/tutorials/**/*' - - '.github/workflows/verify-guides.yml' # Reusable workflow - - '.github/workflows/verify-guides-large.yml' # This workflow - - pull_request: - branches: ["master"] - paths: - - 'en/tutorials/**/*' - - '.github/workflows/verify-guides.yml' # Reusable workflow - - '.github/workflows/verify-guides-large.yml' # This workflow - -jobs: - large: - uses: ./.github/workflows/verify-guides.yml - with: - test_config_path: "test/_test_config-large.yml" diff --git a/.github/workflows/verify-guides-small.yml b/.github/workflows/verify-guides-small.yml deleted file mode 100644 index c234be7891..0000000000 --- a/.github/workflows/verify-guides-small.yml +++ /dev/null @@ -1,27 +0,0 @@ -name: Verify Guides (Small) - -on: - workflow_dispatch: # Allow manual triggering of this workflow. - - push: - branches: ["master"] - paths: - - '*.md' - - '*.html' - - '**/*.md' - - '**/*.html' - - '.github/workflows/verify-guides.yml' # Reusable workflow - - '.github/workflows/verify-guides-small.yml' # This workflow - - 'test/*' - - pull_request: - branches: ["master"] - paths: - - '.github/workflows/verify-guides.yml' # Reusable workflow - - '.github/workflows/verify-guides-small.yml' # This workflow - -jobs: - small: - uses: ./.github/workflows/verify-guides.yml - with: - test_config_path: "test/_test_config.yml" diff --git a/.github/workflows/verify-guides.yml b/.github/workflows/verify-guides.yml deleted file mode 100644 index 425911a0fe..0000000000 --- a/.github/workflows/verify-guides.yml +++ /dev/null @@ -1,44 +0,0 @@ -name: Verify Guides - -on: - workflow_call: - inputs: - test_config_path: - description: | - The path to the test configuration file, relative to the repository root. - - Example: `test/_test_config.yml` - type: string - required: true - -defaults: - run: - # Specify to ensure "pipefail and errexit" are set. - # Ref: https://docs.github.com/en/actions/writing-workflows/workflow-syntax-for-github-actions#defaultsrunshell - shell: bash - -jobs: - test: - runs-on: ubuntu-latest - env: - LANG: "C.UTF-8" - steps: - - uses: jlumbroso/free-disk-space@54081f138730dfa15788a46383842cd2f914a1be - - - uses: actions/checkout@v6 - - - uses: ./.github/actions/install-deps - with: - vespa-cli-version: latest - - - name: build-site - run: | - bundle exec jekyll build - - - name: check-queries - run: | - ./test/test_queries.py _site - - - name: run-tests - run: | - ./test/test.py -c "${{ inputs.test_config_path }}" -w $GITHUB_WORKSPACE diff --git a/.gitignore b/.gitignore deleted file mode 100644 index 121832059e..0000000000 --- a/.gitignore +++ /dev/null @@ -1,19 +0,0 @@ -*~ -#*# -.ruby-version -_site -.sass-cache -.jekyll-metadata -.jekyll-cache -.DS_Store -.idea/ -Pipfile -_work/ -*.pyc -documentation.iml -open_index.json -*.pem -.bundle/ -vendor/ -llms.txt -.claude/ diff --git a/404.html b/404.html deleted file mode 100644 index 212b4fce35..0000000000 --- a/404.html +++ /dev/null @@ -1,27 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: Page not found -index: false -meta_robots_noindex: true ---- - - - -
-

404

- -

Page not found :(

-

The requested page could not be found.

-
diff --git a/CNAME b/CNAME deleted file mode 100644 index 5c7b7d633f..0000000000 --- a/CNAME +++ /dev/null @@ -1 +0,0 @@ -docs.vespa.ai \ No newline at end of file diff --git a/Gemfile b/Gemfile deleted file mode 100644 index 17e2d4e81e..0000000000 --- a/Gemfile +++ /dev/null @@ -1,51 +0,0 @@ -# Copyright Vespa.ai. All rights reserved. -ruby '>=2.6' - -source "https://rubygems.org" - -# Hello! This is where you manage which Jekyll version is used to run. -# When you want to use a different version, change it below, save the -# file and run `bundle install`. Run Jekyll with `bundle exec`, like so: -# -# bundle exec jekyll serve -# -# This will help ensure the proper Jekyll version is running. -# Happy Jekylling! -# gem "jekyll", "~> 3.9.0" - -# This is the default theme for new Jekyll sites. You may change this to anything you like. -gem "minima", ">= 2.5.1" - -# Direct Jekyll dependency. The `github-pages` meta-gem caps at Ruby < 4.0 -# (via commonmarker ~> 0.22), so we depend on Jekyll directly. -gem "jekyll", "~> 3.10" - -# Performance-booster for watching directories on Windows -gem "wdm", "~> 0.2.0", :install_if => Gem.win_platform? - -# kramdown v2 ships without the gfm parser by default. If you're using -# kramdown v1, comment out this line. -gem "kramdown-parser-gfm" - -# Work-around for webrick no longer included in Ruby 3.0 (https://github.com/jekyll/jekyll/issues/8523) -gem "webrick" - -# Get the html-proofer to work -gem 'rake' -gem 'html-proofer', '>= 5.2.1' - -# Work-around for csv and base64 no longer included in Ruby 3.4.0 -gem "csv" -gem "base64" - -# Jekyll plugins group - -group :jekyll_plugins do - gem "jekyll-feed", "~> 0.12" - gem "jekyll-redirect-from" - gem "jekyll-sitemap" - gem "jekyll-seo-tag" - gem "reverse_markdown" - gem "parallel" - gem "ruby-progressbar" # <-- Add this line -end \ No newline at end of file diff --git a/Gemfile.lock b/Gemfile.lock deleted file mode 100644 index 571e25e254..0000000000 --- a/Gemfile.lock +++ /dev/null @@ -1,270 +0,0 @@ -GEM - remote: https://rubygems.org/ - specs: - Ascii85 (2.0.1) - addressable (2.9.0) - public_suffix (>= 2.0.2, < 8.0) - afm (1.0.0) - async (2.39.0) - console (~> 1.29) - fiber-annotation - io-event (~> 1.11) - metrics (~> 0.12) - traces (~> 0.18) - base64 (0.3.0) - benchmark (0.5.0) - bigdecimal (3.3.1) - colorator (1.1.0) - concurrent-ruby (1.3.7) - console (1.34.3) - fiber-annotation - fiber-local (~> 1.1) - json - csv (3.3.5) - em-websocket (0.5.3) - eventmachine (>= 0.12.9) - http_parser.rb (~> 0) - ethon (0.18.0) - ffi (>= 1.15.0) - logger - eventmachine (1.2.7) - ffi (1.17.4-aarch64-linux-gnu) - ffi (1.17.4-aarch64-linux-musl) - ffi (1.17.4-arm-linux-gnu) - ffi (1.17.4-arm-linux-musl) - ffi (1.17.4-arm64-darwin) - ffi (1.17.4-x86_64-darwin) - ffi (1.17.4-x86_64-linux-gnu) - ffi (1.17.4-x86_64-linux-musl) - fiber-annotation (0.2.0) - fiber-local (1.1.0) - fiber-storage - fiber-storage (1.0.1) - forwardable-extended (2.6.0) - hashery (2.1.2) - html-proofer (5.2.1) - addressable (~> 2.3) - async (~> 2.1) - benchmark (~> 0.5) - nokogiri (~> 1.13) - pdf-reader (~> 2.11) - rainbow (~> 3.0) - typhoeus (~> 1.3) - yell (~> 2.0) - zeitwerk (~> 2.5) - http_parser.rb (0.8.1) - i18n (1.14.8) - concurrent-ruby (~> 1.0) - io-event (1.15.1) - jekyll (3.10.0) - addressable (~> 2.4) - colorator (~> 1.0) - csv (~> 3.0) - em-websocket (~> 0.5) - i18n (>= 0.7, < 2) - jekyll-sass-converter (~> 1.0) - jekyll-watch (~> 2.0) - kramdown (>= 1.17, < 3) - liquid (~> 4.0) - mercenary (~> 0.3.3) - pathutil (~> 0.9) - rouge (>= 1.7, < 4) - safe_yaml (~> 1.0) - webrick (>= 1.0) - jekyll-feed (0.17.0) - jekyll (>= 3.7, < 5.0) - jekyll-redirect-from (0.16.0) - jekyll (>= 3.3, < 5.0) - jekyll-sass-converter (1.5.2) - sass (~> 3.4) - jekyll-seo-tag (2.8.0) - jekyll (>= 3.8, < 5.0) - jekyll-sitemap (1.4.0) - jekyll (>= 3.7, < 5.0) - jekyll-watch (2.2.1) - listen (~> 3.0) - json (2.19.5) - kramdown (2.5.2) - rexml (>= 3.4.4) - kramdown-parser-gfm (1.1.0) - kramdown (~> 2.0) - liquid (4.0.4) - listen (3.10.0) - logger - rb-fsevent (~> 0.10, >= 0.10.3) - rb-inotify (~> 0.9, >= 0.9.10) - logger (1.7.0) - mercenary (0.3.6) - metrics (0.15.0) - minima (2.5.2) - jekyll (>= 3.5, < 5.0) - jekyll-feed (~> 0.9) - jekyll-seo-tag (~> 2.1) - nokogiri (1.19.4-aarch64-linux-gnu) - racc (~> 1.4) - nokogiri (1.19.4-aarch64-linux-musl) - racc (~> 1.4) - nokogiri (1.19.4-arm-linux-gnu) - racc (~> 1.4) - nokogiri (1.19.4-arm-linux-musl) - racc (~> 1.4) - nokogiri (1.19.4-arm64-darwin) - racc (~> 1.4) - nokogiri (1.19.4-x86_64-darwin) - racc (~> 1.4) - nokogiri (1.19.4-x86_64-linux-gnu) - racc (~> 1.4) - nokogiri (1.19.4-x86_64-linux-musl) - racc (~> 1.4) - parallel (2.1.0) - pathutil (0.16.2) - forwardable-extended (~> 2.6) - pdf-reader (2.15.1) - Ascii85 (>= 1.0, < 3.0, != 2.0.0) - afm (>= 0.2.1, < 2) - hashery (~> 2.0) - ruby-rc4 - ttfunk - public_suffix (7.0.5) - racc (1.8.1) - rainbow (3.1.1) - rake (13.4.2) - rb-fsevent (0.11.2) - rb-inotify (0.11.1) - ffi (~> 1.0) - reverse_markdown (3.0.2) - nokogiri - rexml (3.4.4) - rouge (3.30.0) - ruby-progressbar (1.13.0) - ruby-rc4 (0.1.5) - safe_yaml (1.0.5) - sass (3.7.4) - sass-listen (~> 4.0.0) - sass-listen (4.0.0) - rb-fsevent (~> 0.9, >= 0.9.4) - rb-inotify (~> 0.9, >= 0.9.7) - traces (0.18.2) - ttfunk (1.8.0) - bigdecimal (~> 3.1) - typhoeus (1.6.0) - ethon (>= 0.18.0) - wdm (0.2.0) - webrick (1.9.2) - yell (2.2.2) - zeitwerk (2.7.5) - -PLATFORMS - aarch64-linux-gnu - aarch64-linux-musl - arm-linux-gnu - arm-linux-musl - arm64-darwin - x86_64-darwin - x86_64-linux-gnu - x86_64-linux-musl - -DEPENDENCIES - base64 - csv - html-proofer (>= 5.2.1) - jekyll (~> 3.10) - jekyll-feed (~> 0.12) - jekyll-redirect-from - jekyll-seo-tag - jekyll-sitemap - kramdown-parser-gfm - minima (>= 2.5.1) - parallel - rake - reverse_markdown - ruby-progressbar - wdm (~> 0.2.0) - webrick - -CHECKSUMS - Ascii85 (2.0.1) sha256=15cb5d941808543cbb9e7e6aea3c8ec3877f154c3461e8b3673e97f7ecedbe5a - addressable (2.9.0) sha256=7fdf6ac3660f7f4e867a0838be3f6cf722ace541dd97767fa42bc6cfa980c7af - afm (1.0.0) sha256=5bd4d6f6241e7014ef090985ec6f4c3e9745f6de0828ddd58bc1efdd138f4545 - async (2.39.0) sha256=df18730073f2bbb45788077dfa20cb365ecc1b9453969f44de6796b5191a00aa - base64 (0.3.0) sha256=27337aeabad6ffae05c265c450490628ef3ebd4b67be58257393227588f5a97b - benchmark (0.5.0) sha256=465df122341aedcb81a2a24b4d3bd19b6c67c1530713fd533f3ff034e419236c - bigdecimal (3.3.1) sha256=eaa01e228be54c4f9f53bf3cc34fe3d5e845c31963e7fcc5bedb05a4e7d52218 - colorator (1.1.0) sha256=e2f85daf57af47d740db2a32191d1bdfb0f6503a0dfbc8327d0c9154d5ddfc38 - concurrent-ruby (1.3.7) sha256=4412caec3a5ea2e5fdc52076724c071a81f2c0593d83b2ac8cbb8ca63b3151b0 - console (1.34.3) sha256=869fbd74697efc4c606f102d2812b0b008e4e7fd738a91c591e8577140ec0dcc - csv (3.3.5) sha256=6e5134ac3383ef728b7f02725d9872934f523cb40b961479f69cf3afa6c8e73f - em-websocket (0.5.3) sha256=f56a92bde4e6cb879256d58ee31f124181f68f8887bd14d53d5d9a292758c6a8 - ethon (0.18.0) sha256=b598afc9f30448cb068b850714b7d6948e941476095d04f90a4ac65b8d6efcb2 - eventmachine (1.2.7) sha256=994016e42aa041477ba9cff45cbe50de2047f25dd418eba003e84f0d16560972 - ffi (1.17.4-aarch64-linux-gnu) sha256=b208f06f91ffd8f5e1193da3cae3d2ccfc27fc36fba577baf698d26d91c080df - ffi (1.17.4-aarch64-linux-musl) sha256=9286b7a615f2676245283aef0a0a3b475ae3aae2bb5448baace630bb77b91f39 - ffi (1.17.4-arm-linux-gnu) sha256=d6dbddf7cb77bf955411af5f187a65b8cd378cb003c15c05697f5feee1cb1564 - ffi (1.17.4-arm-linux-musl) sha256=9d4838ded0465bef6e2426935f6bcc93134b6616785a84ffd2a3d82bc3cf6f95 - ffi (1.17.4-arm64-darwin) sha256=19071aaf1419251b0a46852abf960e77330a3b334d13a4ab51d58b31a937001b - ffi (1.17.4-x86_64-darwin) sha256=aa70390523cf3235096cf64962b709b4cfbd5c082a2cb2ae714eb0fe2ccda496 - ffi (1.17.4-x86_64-linux-gnu) sha256=9d3db14c2eae074b382fa9c083fe95aec6e0a1451da249eab096c34002bc752d - ffi (1.17.4-x86_64-linux-musl) sha256=3fdf9888483de005f8ef8d1cf2d3b20d86626af206cbf780f6a6a12439a9c49e - fiber-annotation (0.2.0) sha256=7abfadf1d119f508867d4103bf231c0354d019cc39a5738945dec2edadaf6c03 - fiber-local (1.1.0) sha256=c885f94f210fb9b05737de65d511136ea602e00c5105953748aa0f8793489f06 - fiber-storage (1.0.1) sha256=f48e5b6d8b0be96dac486332b55cee82240057065dc761c1ea692b2e719240e1 - forwardable-extended (2.6.0) sha256=1bec948c469bbddfadeb3bd90eb8c85f6e627a412a3e852acfd7eaedbac3ec97 - hashery (2.1.2) sha256=d239cc2310401903f6b79d458c2bbef5bf74c46f3f974ae9c1061fb74a404862 - html-proofer (5.2.1) sha256=fdd958a7cbf9c3255fb96fe7cfc4e611f64e2706e469488a3326309ad007d2fd - http_parser.rb (0.8.1) sha256=9ae8df145b39aa5398b2f90090d651c67bd8e2ebfe4507c966579f641e11097a - i18n (1.14.8) sha256=285778639134865c5e0f6269e0b818256017e8cde89993fdfcbfb64d088824a5 - io-event (1.15.1) sha256=c644cdcf48254015d63f558bf4492f35471f5bb204a42180ea49752be59b30cc - jekyll (3.10.0) sha256=c4213b761dc7dfe7d499eb742d0476a02d8503e440c2610e19774ee7f0db8d90 - jekyll-feed (0.17.0) sha256=689aab16c877949bb9e7a5c436de6278318a51ecb974792232fd94d8b3acfcc3 - jekyll-redirect-from (0.16.0) sha256=6635cae569ef9b0f90ffb71ec014ba977177fafb44d32a2b0526288d4d9be6db - jekyll-sass-converter (1.5.2) sha256=53773669e414dc3bb070113befacb808576025a28cfa4a4accc682e90a9c1101 - jekyll-seo-tag (2.8.0) sha256=3f2ed1916d56f14ebfa38e24acde9b7c946df70cb183af2cb5f0598f21ae6818 - jekyll-sitemap (1.4.0) sha256=0de08c5debc185ea5a8f980e1025c7cd3f8e0c35c8b6ef592f15c46235cf4218 - jekyll-watch (2.2.1) sha256=bc44ed43f5e0a552836245a54dbff3ea7421ecc2856707e8a1ee203a8387a7e1 - json (2.19.5) sha256=218a18553e4801d579ca7e0f5bc72bafd776d7397238a1fb4e74db5b0a812c59 - kramdown (2.5.2) sha256=1ba542204c66b6f9111ff00dcc26075b95b220b07f2905d8261740c82f7f02fa - kramdown-parser-gfm (1.1.0) sha256=fb39745516427d2988543bf01fc4cf0ab1149476382393e0e9c48592f6581729 - liquid (4.0.4) sha256=4fcfebb1a045e47918388dbb7a0925e7c3893e58d2bd6c3b3c73ec17a2d8fdb3 - listen (3.10.0) sha256=c6e182db62143aeccc2e1960033bebe7445309c7272061979bb098d03760c9d2 - logger (1.7.0) sha256=196edec7cc44b66cfb40f9755ce11b392f21f7967696af15d274dde7edff0203 - mercenary (0.3.6) sha256=2a084b18f5692c86a633e185d5311ba6d11fc46c802eb414ae05368178078a82 - metrics (0.15.0) sha256=61ded5bac95118e995b1bc9ed4a5f19bc9814928a312a85b200abbdac9039072 - minima (2.5.2) sha256=9c434e3b7bc4a0f0ab488910438ed3757a0502ff1060d172f361907fc38aa45a - nokogiri (1.19.4-aarch64-linux-gnu) sha256=1269fb644a6de405057a53dd5c762b1209b43ca7424f839454d3dbc677c31a8f - nokogiri (1.19.4-aarch64-linux-musl) sha256=35c65b9ce72b3bb03207bdbe7067915019dc18c1b9b59139684bd6690fdd01af - nokogiri (1.19.4-arm-linux-gnu) sha256=a301313e38bb065d68239e79734bcd6f56fb6efaacebde29e9abf2a4735340ca - nokogiri (1.19.4-arm-linux-musl) sha256=588923c101bcfa78869734d247d25b598674323e7f22474fc468f6e5647311eb - nokogiri (1.19.4-arm64-darwin) sha256=a46db9853286e6597b36ebc6953817d15acf3a299583eb3f89fdc6f91dd63527 - nokogiri (1.19.4-x86_64-darwin) sha256=7fd17057d3e1f00e9954a74b3cd76595d3d4a5ef233b7ed9599047c204f70551 - nokogiri (1.19.4-x86_64-linux-gnu) sha256=379fae440b28915e3f19d752ce2dcf8465ed2b2fbefd2a7ca0dd497bc981a06a - nokogiri (1.19.4-x86_64-linux-musl) sha256=17dfb7c1fa194ae02fbf7c51a7afc8d278045ab3fdacfd86f91d02d7b274470b - parallel (2.1.0) sha256=b35258865c2e31134c5ecb708beaaf6772adf9d5efae28e93e99260877b09356 - pathutil (0.16.2) sha256=e43b74365631cab4f6d5e4228f812927efc9cb2c71e62976edcb252ee948d589 - pdf-reader (2.15.1) sha256=18c6a986a84a3117fa49f4279fc2de51f5d2399b71833df5d2bccd595c7068ce - public_suffix (7.0.5) sha256=1a8bb08f1bbea19228d3bed6e5ed908d1cb4f7c2726d18bd9cadf60bc676f623 - racc (1.8.1) sha256=4a7f6929691dbec8b5209a0b373bc2614882b55fc5d2e447a21aaa691303d62f - rainbow (3.1.1) sha256=039491aa3a89f42efa1d6dec2fc4e62ede96eb6acd95e52f1ad581182b79bc6a - rake (13.4.2) sha256=cb825b2bd5f1f8e91ca37bddb4b9aaf345551b4731da62949be002fa89283701 - rb-fsevent (0.11.2) sha256=43900b972e7301d6570f64b850a5aa67833ee7d87b458ee92805d56b7318aefe - rb-inotify (0.11.1) sha256=a0a700441239b0ff18eb65e3866236cd78613d6b9f78fea1f9ac47a85e47be6e - reverse_markdown (3.0.2) sha256=818ebb92ce39dbb1a291690dd1ec9a6d62530d4725296b17e9c8f668f9a5b8af - rexml (3.4.4) sha256=19e0a2c3425dfbf2d4fc1189747bdb2f849b6c5e74180401b15734bc97b5d142 - rouge (3.30.0) sha256=a3d353222aa72e49e2c86726c0bcfd719f82592f57d494474655f48e669eceb6 - ruby-progressbar (1.13.0) sha256=80fc9c47a9b640d6834e0dc7b3c94c9df37f08cb072b7761e4a71e22cff29b33 - ruby-rc4 (0.1.5) sha256=00cc40a39d20b53f5459e7ea006a92cf584e9bc275e2a6f7aa1515510e896c03 - safe_yaml (1.0.5) sha256=a6ac2d64b7eb027bdeeca1851fe7e7af0d668e133e8a88066a0c6f7087d9f848 - sass (3.7.4) sha256=808b0d39053aa69068df939e24671fe84fd5a9d3314486e1a1457d0934a4255d - sass-listen (4.0.0) sha256=ae9dcb76dd3e234329e5ba6e213f48e532c5a3e7b0b4d8a87f13aaca0cc18377 - traces (0.18.2) sha256=80f1649cb4daace1d7174b81f3b3b7427af0b93047759ba349960cb8f315e214 - ttfunk (1.8.0) sha256=a7cbc7e489cc46e979dde04d34b5b9e4f5c8f1ee5fc6b1a7be39b829919d20ca - typhoeus (1.6.0) sha256=bacc41c23e379547e29801dc235cd1699b70b955a1ba3d32b2b877aa844c331d - wdm (0.2.0) sha256=c46d9dcb6d375199ca07465bc67669ee8f041aeaa55dd7dafe6de4dd97b27647 - webrick (1.9.2) sha256=beb4a15fc474defed24a3bda4ffd88a490d517c9e4e6118c3edce59e45864131 - yell (2.2.2) sha256=1d166f3cc3b6dc49a59778ea7156ed6d8de794c15106d48ffd6cbb061b9b26bc - zeitwerk (2.7.5) sha256=d8da92128c09ea6ec62c949011b00ed4a20242b255293dd66bf41545398f73dd - -RUBY VERSION - ruby 4.0.3 - -BUNDLED WITH - 4.0.3 diff --git a/_config.yml b/_config.yml deleted file mode 100644 index 0816910903..0000000000 --- a/_config.yml +++ /dev/null @@ -1,123 +0,0 @@ -# Copyright Vespa.ai. All rights reserved. -# Welcome to Jekyll! -# -# This config file is meant for settings that affect your whole blog, values -# which you are expected to set up once and rarely edit after that. If you find -# yourself editing this file very often, consider using Jekyll's data files -# feature for the data you need to update frequently. -# -# For technical reasons, this file is *NOT* reloaded automatically when you use -# 'bundle exec jekyll serve'. If you change this file, please restart the server process. - -# Site settings -# These are used to personalize your new site. If you look in the HTML files, -# you will see them accessed via {{ site.title }}, {{ site.email }}, and so on. -# You can create any custom variable you would like, and they will be accessible -# in the templates via {{ site.myvariable }}. -title: Vespa. Big data. Real time. Open source. -tagline: Vespa Documentation -description: >- # this means to ignore newlines until "baseurl:" - Vespa - the open big data serving platform -# baseurl: "" # the subpath of your site, e.g. /blog -url: "https://docs.vespa.ai" # the base hostname & protocol for your site, e.g. http://example.com -logo: /assets/logos/vespa-logo-green-rgb.svg -# Remove this before publishing on github.com -repository: vespa/documentation -repository_url: "https://github.com/vespa-engine/documentation" -google_tag_manager: "GTM-WM6MPVZP" - -# Build settings -sass: - sass_dir: _sass -markdown: kramdown -plugins: - - jekyll-feed - - jekyll-redirect-from - - jekyll-sitemap - - jekyll-seo-tag - -kramdown: - smart_quotes: ["apos", "apos", "quot", "quot"] - -# Keep markdown files in output -keep_files: - - "*.html.md" - -# Exclude from processing. -# The following items will not be processed by default. Create a custom list -# to override the default setting. -exclude: - - Gemfile - - Gemfile.lock - - node_modules - - vendor/bundle/ - - vendor/cache/ - - vendor/gems/ - - vendor/ruby/ - - .idea - - open_index.json - - _work/ - - test/ - - "*.pem" - - "*.yaml" - - "*.py" - - llms.txt - -search: - namespace: "open" - doc_type: "doc" - do_feed: true - do_index_removal_before_feed: false - feed_endpoints: - # This config needs to be the same in all files starting with "_" in: - # vespa-engine/documentation, vespa-engine/pyvespa, vespa-engine/sample-apps, - # vespaai/blog and vespaai/cloud-site - # - # vespacloud-docsearch | prod.aws-us-east-1c - - url: https://b671e1db.b68a8c0d.z.vespa-app.cloud/ - indexes: - - open_index.json - # vespacloud-docsearch | prod.aws-eu-west-1a - - url: https://ed053d52.b68a8c0d.z.vespa-app.cloud/ - indexes: - - open_index.json - # vespacloud-docsearch | prod.gcp-us-central1-f - - url: https://a341952a.b68a8c0d.z.vespa-app.cloud/ - indexes: - - open_index.json - # vespacloud-docsearch | prod.gcp-us-central1-a - - url: https://ba97230a.b68a8c0d.z.vespa-app.cloud/ - indexes: - - open_index.json - # vespacloud-docsearch | prod.aws-euc1-az1 - - url: https://af1993cf.b68a8c0d.z.vespa-app.cloud/ - indexes: - - open_index.json - # vespacloud-docsearch | prod.aws-us-east-1 - - url: https://b42c9416.b68a8c0d.z.vespa-app.cloud/ - indexes: - - open_index.json - # vespacloud-docsearch.cloud-enclave | prod.aws-us-east-1c - - url: https://cd560c23.b68a8c0d.z.vespa-app.cloud/ - indexes: - - open_index.json - # vespacloud-docsearch.cloud-enclave | prod.aws-eu-west-1a - - url: https://b24dd625.b68a8c0d.z.vespa-app.cloud/ - indexes: - - open_index.json - # vespacloud-docsearch.cloud-enclave | prod.gcp-us-central1-f - # - url: https://ab1f72fd.b68a8c0d.z.vespa-app.cloud/ - # indexes: - # - open_index.json - -defaults: - - scope: - path: "" - values: - layout: "default" - index: true # Include page in the Vespa site search index (see _plugins-vespafeed/vespa_index_generator.rb). - # Set to false for utility pages (search, 404, etc.) that should not appear in site search. - # This does NOT affect external search engines - use 'meta_robots_noindex: true' to add a robots noindex meta tag. - -variables: - vespa_version: "8.712.7" diff --git a/_data/aws-instance-types.csv b/_data/aws-instance-types.csv deleted file mode 100644 index 1ebb09538e..0000000000 --- a/_data/aws-instance-types.csv +++ /dev/null @@ -1,165 +0,0 @@ -arch,cpu,mem,localdisk,gpumem -arm64,1.0,8,-,- -arm64,1.0,8,59,- -arm64,1.0,16,-,- -arm64,1.0,16,59,- -arm64,2.0,8,-,- -arm64,2.0,8,118,- -arm64,2.0,16,-,- -arm64,2.0,16,118,- -arm64,2.0,16,468,- -arm64,2.0,32,-,- -arm64,2.0,32,118,- -arm64,4.0,8,-,- -arm64,4.0,8,237,- -arm64,4.0,16,-,- -arm64,4.0,16,237,- -arm64,4.0,32,-,- -arm64,4.0,32,237,- -arm64,4.0,32,937,- -arm64,4.0,64,-,- -arm64,4.0,64,237,- -arm64,8.0,16,-,- -arm64,8.0,16,474,- -arm64,8.0,32,-,- -arm64,8.0,32,474,- -arm64,8.0,64,-,- -arm64,8.0,64,474,- -arm64,8.0,64,1875,- -arm64,8.0,128,-,- -arm64,8.0,128,475,- -arm64,16.0,32,-,- -arm64,16.0,32,950,- -arm64,16.0,64,-,- -arm64,16.0,64,950,- -arm64,16.0,128,-,- -arm64,16.0,128,950,- -arm64,16.0,128,3750,- -arm64,16.0,256,-,- -arm64,16.0,256,950,- -arm64,32.0,64,-,- -arm64,32.0,64,1900,- -arm64,32.0,128,-,- -arm64,32.0,128,1900,- -arm64,32.0,256,-,- -arm64,32.0,256,1900,- -arm64,32.0,256,7500,- -arm64,32.0,512,-,- -arm64,32.0,512,1900,- -arm64,48.0,96,-,- -arm64,48.0,96,2850,- -arm64,48.0,192,-,- -arm64,48.0,192,2850,- -arm64,48.0,384,-,- -arm64,48.0,384,2850,- -arm64,48.0,384,11250,- -arm64,48.0,768,-,- -arm64,48.0,768,2850,- -arm64,64.0,128,-,- -arm64,64.0,128,3800,- -arm64,64.0,256,-,- -arm64,64.0,256,3800,- -arm64,64.0,512,-,- -arm64,64.0,512,3800,- -arm64,64.0,512,15000,- -arm64,64.0,1024,-,- -arm64,64.0,1024,3800,- -arm64,96.0,192,-,- -arm64,96.0,192,5700,- -arm64,96.0,384,-,- -arm64,96.0,384,5700,- -arm64,96.0,768,-,- -arm64,96.0,768,5700,- -arm64,96.0,768,22500,- -arm64,96.0,1536,-,- -arm64,192.0,384,-,- -arm64,192.0,384,11400,- -arm64,192.0,768,-,- -arm64,192.0,768,11400,- -arm64,192.0,1536,-,- -arm64,192.0,1536,11400,- -arm64,192.0,1536,45000,- -arm64,192.0,3072,-,- -x86_64,2.0,8,-,- -x86_64,2.0,8,75,- -x86_64,2.0,8,118,- -x86_64,2.0,16,-,- -x86_64,2.0,16,75,- -x86_64,2.0,16,468,- -x86_64,2.0,16,1250,- -x86_64,4.0,8,-,- -x86_64,4.0,8,100,- -x86_64,4.0,8,237,- -x86_64,4.0,16,-,- -x86_64,4.0,16,125,16.0 -x86_64,4.0,16,150,- -x86_64,4.0,16,237,- -x86_64,4.0,32,-,- -x86_64,4.0,32,150,- -x86_64,4.0,32,237,- -x86_64,4.0,32,937,- -x86_64,4.0,32,2500,- -x86_64,8.0,16,-,- -x86_64,8.0,16,200,- -x86_64,8.0,16,474,- -x86_64,8.0,32,-,- -x86_64,8.0,32,225,16.0 -x86_64,8.0,32,300,- -x86_64,8.0,32,474,- -x86_64,8.0,64,-,- -x86_64,8.0,64,300,- -x86_64,8.0,64,1875,- -x86_64,8.0,64,5000,- -x86_64,12.0,96,7500,- -x86_64,16.0,32,-,- -x86_64,16.0,32,400,- -x86_64,16.0,32,950,- -x86_64,16.0,64,-,- -x86_64,16.0,64,600,- -x86_64,16.0,64,950,- -x86_64,16.0,128,-,- -x86_64,16.0,128,600,- -x86_64,16.0,128,3750,- -x86_64,24.0,192,900,- -x86_64,24.0,192,15000,- -x86_64,32.0,64,-,- -x86_64,32.0,64,1200,- -x86_64,32.0,64,1900,- -x86_64,32.0,128,-,- -x86_64,32.0,128,1200,- -x86_64,32.0,128,1900,- -x86_64,32.0,256,-,- -x86_64,32.0,256,1200,- -x86_64,32.0,256,7500,- -x86_64,36.0,72,-,- -x86_64,36.0,72,900,- -x86_64,48.0,96,-,- -x86_64,48.0,96,1800,- -x86_64,48.0,96,2850,- -x86_64,48.0,192,-,- -x86_64,48.0,192,1800,- -x86_64,48.0,192,2850,- -x86_64,48.0,384,-,- -x86_64,48.0,384,1800,- -x86_64,48.0,384,30000,- -x86_64,64.0,128,-,- -x86_64,64.0,128,3800,- -x86_64,64.0,256,-,- -x86_64,64.0,256,2400,- -x86_64,64.0,256,3800,- -x86_64,64.0,512,-,- -x86_64,64.0,512,2400,- -x86_64,64.0,512,15000,- -x86_64,72.0,144,-,- -x86_64,72.0,144,1800,- -x86_64,72.0,576,45000,- -x86_64,96.0,192,-,- -x86_64,96.0,192,3600,- -x86_64,96.0,192,5700,- -x86_64,96.0,384,-,- -x86_64,96.0,384,3600,- -x86_64,96.0,768,-,- -x86_64,96.0,768,3600,- -x86_64,96.0,768,60000,- -x86_64,128.0,1024,30000,- -x86_64,192.0,1536,120000,- diff --git a/_data/azure-instance-types.csv b/_data/azure-instance-types.csv deleted file mode 100644 index 9ef05bb1bc..0000000000 --- a/_data/azure-instance-types.csv +++ /dev/null @@ -1,66 +0,0 @@ -arch,cpu,mem,localdisk,gpumem -x86_64,2.0,8,-,- -x86_64,2.0,8,75,- -x86_64,2.0,8,118,- -x86_64,2.0,16,-,- -x86_64,2.0,16,75,- -x86_64,2.0,16,118,- -x86_64,4.0,8,-,- -x86_64,4.0,8,150,- -x86_64,4.0,16,-,- -x86_64,4.0,16,150,- -x86_64,4.0,16,236,- -x86_64,4.0,32,-,- -x86_64,4.0,32,150,- -x86_64,4.0,32,236,- -x86_64,8.0,16,-,- -x86_64,8.0,16,300,- -x86_64,8.0,32,-,- -x86_64,8.0,32,300,- -x86_64,8.0,32,472,- -x86_64,8.0,64,-,- -x86_64,8.0,64,300,- -x86_64,8.0,64,472,- -x86_64,16.0,32,-,- -x86_64,16.0,32,600,- -x86_64,16.0,64,-,- -x86_64,16.0,64,600,- -x86_64,16.0,64,944,- -x86_64,16.0,128,-,- -x86_64,16.0,128,600,- -x86_64,16.0,128,944,- -x86_64,20.0,160,-,- -x86_64,20.0,160,750,- -x86_64,20.0,160,1181,- -x86_64,32.0,64,-,- -x86_64,32.0,64,1200,- -x86_64,32.0,128,-,- -x86_64,32.0,128,1200,- -x86_64,32.0,128,1889,- -x86_64,32.0,256,-,- -x86_64,32.0,256,1200,- -x86_64,32.0,256,1889,- -x86_64,48.0,96,-,- -x86_64,48.0,96,1800,- -x86_64,48.0,192,-,- -x86_64,48.0,192,1800,- -x86_64,48.0,192,2834,- -x86_64,48.0,384,-,- -x86_64,48.0,384,1800,- -x86_64,48.0,384,2834,- -x86_64,64.0,128,-,- -x86_64,64.0,128,2400,- -x86_64,64.0,256,-,- -x86_64,64.0,256,2400,- -x86_64,64.0,256,3779,- -x86_64,64.0,512,-,- -x86_64,64.0,512,2400,- -x86_64,64.0,512,3779,- -x86_64,96.0,192,-,- -x86_64,96.0,192,3600,- -x86_64,96.0,384,-,- -x86_64,96.0,384,3600,- -x86_64,96.0,384,5669,- -x86_64,96.0,672,-,- -x86_64,96.0,672,3600,- -x86_64,96.0,672,5669,- diff --git a/_data/gcp-instance-types.csv b/_data/gcp-instance-types.csv deleted file mode 100644 index 0a3895f351..0000000000 --- a/_data/gcp-instance-types.csv +++ /dev/null @@ -1,268 +0,0 @@ -arch,cpu,mem,localdisk,gpumem -arm64,2.0,8,-,- -arm64,2.0,16,-,- -arm64,4.0,8,-,- -arm64,4.0,16,-,- -arm64,4.0,16,375,- -arm64,4.0,32,-,- -arm64,4.0,32,375,- -arm64,8.0,16,-,- -arm64,8.0,32,-,- -arm64,8.0,32,750,- -arm64,8.0,64,-,- -arm64,8.0,64,750,- -arm64,16.0,32,-,- -arm64,16.0,64,-,- -arm64,16.0,64,1500,- -arm64,16.0,128,-,- -arm64,16.0,128,1500,- -arm64,32.0,64,-,- -arm64,32.0,128,-,- -arm64,32.0,128,2250,- -arm64,32.0,256,-,- -arm64,32.0,256,2250,- -arm64,48.0,96,-,- -arm64,48.0,192,-,- -arm64,48.0,192,3750,- -arm64,48.0,384,-,- -arm64,48.0,384,3750,- -arm64,64.0,128,-,- -arm64,64.0,256,-,- -arm64,64.0,256,5250,- -arm64,64.0,512,-,- -arm64,64.0,512,5250,- -arm64,72.0,144,-,- -arm64,72.0,288,-,- -arm64,72.0,288,6000,- -arm64,72.0,576,-,- -arm64,72.0,576,6000,- -x86_64,2.0,8,-,- -x86_64,2.0,8,375,- -x86_64,2.0,8,750,- -x86_64,2.0,8,1500,- -x86_64,2.0,8,3000,- -x86_64,2.0,8,6000,- -x86_64,2.0,8,9000,- -x86_64,2.0,15,-,- -x86_64,2.0,16,-,- -x86_64,2.0,16,375,- -x86_64,2.0,16,750,- -x86_64,2.0,16,1500,- -x86_64,2.0,16,3000,- -x86_64,2.0,16,6000,- -x86_64,2.0,16,9000,- -x86_64,4.0,8,-,- -x86_64,4.0,15,-,- -x86_64,4.0,15,375,- -x86_64,4.0,16,-,- -x86_64,4.0,16,375,- -x86_64,4.0,16,750,- -x86_64,4.0,16,1500,- -x86_64,4.0,16,3000,- -x86_64,4.0,16,6000,- -x86_64,4.0,16,9000,- -x86_64,4.0,31,-,- -x86_64,4.0,31,375,- -x86_64,4.0,32,-,- -x86_64,4.0,32,375,- -x86_64,4.0,32,750,- -x86_64,4.0,32,1500,- -x86_64,4.0,32,3000,- -x86_64,4.0,32,6000,- -x86_64,4.0,32,9000,- -x86_64,8.0,8,-,- -x86_64,8.0,8,375,- -x86_64,8.0,8,750,- -x86_64,8.0,8,1500,- -x86_64,8.0,8,3000,- -x86_64,8.0,8,6000,- -x86_64,8.0,8,9000,- -x86_64,8.0,15,-,- -x86_64,8.0,16,-,- -x86_64,8.0,30,-,- -x86_64,8.0,30,375,- -x86_64,8.0,31,-,- -x86_64,8.0,31,375,- -x86_64,8.0,32,-,- -x86_64,8.0,32,375,- -x86_64,8.0,32,750,- -x86_64,8.0,32,1500,- -x86_64,8.0,32,3000,- -x86_64,8.0,32,6000,- -x86_64,8.0,32,9000,- -x86_64,8.0,62,-,- -x86_64,8.0,62,375,- -x86_64,8.0,63,-,- -x86_64,8.0,63,375,- -x86_64,8.0,64,-,- -x86_64,8.0,64,375,- -x86_64,8.0,64,750,- -x86_64,8.0,64,1500,- -x86_64,8.0,64,3000,- -x86_64,8.0,64,6000,- -x86_64,8.0,64,9000,- -x86_64,16.0,16,-,- -x86_64,16.0,16,375,- -x86_64,16.0,16,750,- -x86_64,16.0,16,1500,- -x86_64,16.0,16,3000,- -x86_64,16.0,16,6000,- -x86_64,16.0,16,9000,- -x86_64,16.0,30,-,- -x86_64,16.0,32,-,- -x86_64,16.0,60,-,- -x86_64,16.0,60,750,- -x86_64,16.0,62,-,- -x86_64,16.0,62,375,- -x86_64,16.0,64,-,- -x86_64,16.0,64,375,- -x86_64,16.0,64,750,- -x86_64,16.0,64,1500,- -x86_64,16.0,64,3000,- -x86_64,16.0,64,6000,- -x86_64,16.0,64,9000,- -x86_64,16.0,124,-,- -x86_64,16.0,124,750,- -x86_64,16.0,126,-,- -x86_64,16.0,126,375,- -x86_64,16.0,128,-,- -x86_64,16.0,128,375,- -x86_64,16.0,128,750,- -x86_64,16.0,128,1500,- -x86_64,16.0,128,3000,- -x86_64,16.0,128,6000,- -x86_64,16.0,128,9000,- -x86_64,24.0,48,-,- -x86_64,24.0,186,-,- -x86_64,24.0,186,1500,- -x86_64,32.0,32,-,- -x86_64,32.0,32,750,- -x86_64,32.0,32,1500,- -x86_64,32.0,32,3000,- -x86_64,32.0,32,6000,- -x86_64,32.0,32,9000,- -x86_64,32.0,60,-,- -x86_64,32.0,64,-,- -x86_64,32.0,120,-,- -x86_64,32.0,120,1875,- -x86_64,32.0,124,-,- -x86_64,32.0,124,750,- -x86_64,32.0,128,-,- -x86_64,32.0,128,750,- -x86_64,32.0,128,1500,- -x86_64,32.0,128,3000,- -x86_64,32.0,128,6000,- -x86_64,32.0,128,9000,- -x86_64,32.0,248,-,- -x86_64,32.0,248,1875,- -x86_64,32.0,252,-,- -x86_64,32.0,252,750,- -x86_64,32.0,256,-,- -x86_64,32.0,256,750,- -x86_64,32.0,256,1500,- -x86_64,32.0,256,3000,- -x86_64,32.0,256,6000,- -x86_64,32.0,256,9000,- -x86_64,48.0,48,-,- -x86_64,48.0,48,750,- -x86_64,48.0,48,1500,- -x86_64,48.0,48,3000,- -x86_64,48.0,48,6000,- -x86_64,48.0,48,9000,- -x86_64,48.0,90,-,- -x86_64,48.0,96,-,- -x86_64,48.0,180,-,- -x86_64,48.0,180,3000,- -x86_64,48.0,186,-,- -x86_64,48.0,186,1500,- -x86_64,48.0,192,-,- -x86_64,48.0,192,750,- -x86_64,48.0,192,1500,- -x86_64,48.0,192,3000,- -x86_64,48.0,192,6000,- -x86_64,48.0,192,9000,- -x86_64,48.0,372,-,- -x86_64,48.0,372,3000,- -x86_64,48.0,378,-,- -x86_64,48.0,378,1500,- -x86_64,48.0,384,-,- -x86_64,48.0,384,750,- -x86_64,48.0,384,1500,- -x86_64,48.0,384,3000,- -x86_64,48.0,384,6000,- -x86_64,48.0,384,9000,- -x86_64,64.0,64,-,- -x86_64,64.0,64,1500,- -x86_64,64.0,64,3000,- -x86_64,64.0,64,6000,- -x86_64,64.0,64,9000,- -x86_64,64.0,120,-,- -x86_64,64.0,128,-,- -x86_64,64.0,248,-,- -x86_64,64.0,248,2250,- -x86_64,64.0,256,-,- -x86_64,64.0,256,1500,- -x86_64,64.0,256,3000,- -x86_64,64.0,256,6000,- -x86_64,64.0,256,9000,- -x86_64,64.0,504,-,- -x86_64,64.0,504,2250,- -x86_64,64.0,512,-,- -x86_64,64.0,512,1500,- -x86_64,64.0,512,3000,- -x86_64,64.0,512,6000,- -x86_64,64.0,512,9000,- -x86_64,80.0,80,-,- -x86_64,80.0,80,1500,- -x86_64,80.0,80,3000,- -x86_64,80.0,80,6000,- -x86_64,80.0,80,9000,- -x86_64,80.0,160,-,- -x86_64,80.0,320,-,- -x86_64,80.0,320,1500,- -x86_64,80.0,320,3000,- -x86_64,80.0,320,6000,- -x86_64,80.0,320,9000,- -x86_64,80.0,640,-,- -x86_64,80.0,640,1500,- -x86_64,80.0,640,3000,- -x86_64,80.0,640,6000,- -x86_64,80.0,640,9000,- -x86_64,96.0,96,-,- -x86_64,96.0,96,3000,- -x86_64,96.0,96,6000,- -x86_64,96.0,96,9000,- -x86_64,96.0,180,-,- -x86_64,96.0,192,-,- -x86_64,96.0,360,-,- -x86_64,96.0,360,6000,- -x86_64,96.0,372,-,- -x86_64,96.0,372,3000,- -x86_64,96.0,384,-,- -x86_64,96.0,384,3000,- -x86_64,96.0,384,6000,- -x86_64,96.0,384,9000,- -x86_64,96.0,744,-,- -x86_64,96.0,744,6000,- -x86_64,96.0,756,-,- -x86_64,96.0,756,3000,- -x86_64,96.0,768,-,- -x86_64,96.0,768,3000,- -x86_64,96.0,768,6000,- -x86_64,96.0,768,9000,- -x86_64,128.0,128,-,- -x86_64,128.0,128,3000,- -x86_64,128.0,128,6000,- -x86_64,128.0,128,9000,- -x86_64,128.0,512,-,- -x86_64,128.0,512,3000,- -x86_64,128.0,512,6000,- -x86_64,128.0,512,9000,- -x86_64,224.0,224,-,- -x86_64,224.0,224,3000,- -x86_64,224.0,224,6000,- -x86_64,224.0,224,9000,- -x86_64,224.0,896,-,- -x86_64,224.0,896,3000,- -x86_64,224.0,896,6000,- -x86_64,224.0,896,9000,- diff --git a/_data/sidebar.yml b/_data/sidebar.yml deleted file mode 100644 index 6a2c6fb7c4..0000000000 --- a/_data/sidebar.yml +++ /dev/null @@ -1,742 +0,0 @@ -# Copyright Vespa.ai. All rights reserved. - -# This data is used to build the left sidebar and the table of contents - -docs: - - title: Vespa basics - documents: - - page: Deploy an application - url: /en/basics/deploy-an-application.html - - page: Vespa applications - url: /en/basics/applications.html - - page: Schemas - url: /en/basics/schemas.html - - page: Writing - url: /en/basics/writing.html - - page: Querying - url: /en/basics/querying.html - - page: Ranking - url: /en/basics/ranking.html - - page: Operations - url: /en/basics/operations.html - - page: What's more - url: /en/basics/whats-more.html - - - title: Learn more - documents: - - page: Vespa overview - url: /en/learn/overview.html - - page: Getting help from LLMs - url: /en/learn/llm-help.html - - page: Features - url: /en/learn/features.html - - page: Tutorials and use cases - url: /en/learn/tutorials/ - - page: Frequently asked questions - url: /en/learn/faq.html - - page: Glossary - url: /en/learn/glossary.html - - page: Releases - url: /en/learn/releases.html - - page: Tenants, apps and instances - url: /en/learn/tenant-apps-instances.html - - page: Migrating to Vespa Cloud - url: /en/learn/migrating-to-cloud.html - - page: Migrating from ElasticSearch - url: /en/learn/migrating-from-elastic-search.html - - page: About this documentation - url: /en/learn/about-documentation.html - - page: Contributing to Vespa - url: /en/learn/contributing.html - - - title: Applications and components - documents: - - page: Developer guide - url: /en/applications/developer-guide.html - - page: IDE support - url: /en/applications/ide-support.html - - page: Deployment - url: /en/applications/deployment.html - - page: .vespaignore files - url: /en/applications/vespaignore.html - - page: Containers - url: /en/applications/containers.html - - page: Components - url: /en/applications/components.html - - page: Searchers - url: /en/applications/searchers.html - - page: Document processors - url: /en/applications/document-processors.html - - page: Request handlers - url: /en/applications/request-handlers.html - - page: Result renderers - url: /en/applications/result-renderers.html - - page: Dependency injection - url: /en/applications/dependency-injection.html - - page: Configuring components - url: /en/applications/configuring-components.html - - page: Chaining - url: /en/applications/chaining.html - - page: Inspecting structured data in a Searcher - url: /en/applications/inspecting-structured-data.html - - page: Developing web services - url: /en/applications/web-services.html - - page: Unit testing - url: /en/applications/unit-testing.html - - page: System testing - url: /en/applications/testing.html - - page: The config system - url: /en/applications/config-system.html - - page: Request-response processing - url: /en/applications/processing.html - - page: Bundles - url: /en/applications/bundles.html - - page: Using ZooKeeper - url: /en/applications/using-zookeeper.html - - page: Http servers and filters - url: /en/applications/http-servers-and-filters.html - - page: Using pluggable frameworks - url: /en/applications/pluggable-frameworks.html - - page: Java config API - url: /en/applications/configapi-dev.html - - - title: Schemas and documents - documents: - - page: Documents - url: /en/schemas/documents.html - - page: Inheritance in schemas - url: /en/schemas/inheritance-in-schemas.html - - page: Concrete document types - url: /en/schemas/concrete-documents.html - - page: Parent-child relationships - url: /en/schemas/parent-child.html - - page: Structs - url: /en/schemas/structs.html - - page: Predicate fields - url: /en/schemas/predicate-fields.html - - page: Exposing schema information - url: /en/schemas/exposing-schema-information.html - - - title: Reading and writing - documents: - - page: Reads and writes - url: /en/writing/reads-and-writes.html - - page: /document/v1 - url: /en/writing/document-v1-api-guide.html - - page: Indexing - url: /en/writing/indexing.html - - page: Index bootstrap - url: /en/writing/initial-batch-feed.html - - page: Visiting - url: /en/writing/visiting.html - - page: Document API - url: /en/writing/document-api-guide.html - - page: Partial updates - url: /en/writing/partial-updates.html - - page: Batch delete - url: /en/writing/batch-delete.html - - page: Feed block - url: /en/writing/feed-block.html - - page: Document routing - url: /en/writing/document-routing.html - - page: Indexing paged vectors - url: /en/writing/indexing-paged-vectors.html - - - title: Querying - documents: - - page: The query api - url: /en/querying/query-api.html - - page: The YQL query language - url: /en/querying/query-language.html - - page: Grouping and aggregation - url: /en/querying/grouping.html - - page: Federation - url: /en/querying/federation.html - - page: Query profiles - url: /en/querying/query-profiles.html - - page: An intro to vector search - url: /en/querying/vector-search-intro.html - - page: Nearest neighbor search - url: /en/querying/nearest-neighbor-search.html - - page: Approximate nearest neighbor search - url: /en/querying/approximate-nn-hnsw.html - - page: Nearest neighbor search guide - url: /en/querying/nearest-neighbor-search-guide.html - - page: Text matching - url: /en/querying/text-matching.html - - page: Searching multivalue fields - url: /en/querying/searching-multivalue-fields.html - - page: Geo search - url: /en/querying/geo-search.html - - page: Document summaries - url: /en/querying/document-summaries.html - - page: Result diversity - url: /en/querying/result-diversity.html - - page: Page templates - url: /en/querying/page-templates.html - - - title: Ranking and inference - documents: - - page: Ranking introduction - url: /en/ranking/ranking-intro.html - - page: Ranking expressions and features - url: /en/ranking/ranking-expressions-features.html - - page: Multivalue query operators - url: /en/ranking/multivalue-query-operators.html - - page: Tensor user guide - url: /en/ranking/tensor-user-guide.html - - page: Tensor examples - url: /en/ranking/tensor-examples.html - - page: Phased ranking - url: /en/ranking/phased-ranking.html - - page: Using TensorFlow models - url: /en/ranking/tensorflow.html - - page: Using ONNX models - url: /en/ranking/onnx.html - - page: Using XGBoost models - url: /en/ranking/xgboost.html - - page: Using LightGBM models - url: /en/ranking/lightgbm.html - - page: "Wand: Accelerated OR search" - url: /en/ranking/wand.html - - page: The BM25 rank feature - url: /en/ranking/bm25.html - - page: The nativeRank rank feature - url: /en/ranking/nativerank.html - - page: Cross-encoder transformer ranking - url: /en/ranking/cross-encoders.html - - page: Searcher re-ranking - url: /en/ranking/reranking-in-searcher.html - - page: Significance model - url: /en/ranking/significance.html - - page: Stateless model evaluation - url: /en/ranking/stateless-model-evaluation.html - - - title: RAG and embedding - documents: - - page: RAG in Vespa - url: /en/rag/rag.html - - page: Working with chunks - url: /en/rag/working-with-chunks.html - - page: Embedding - url: /en/rag/embedding.html - - page: Binarizing vectors - url: /en/rag/binarizing-vectors.html - - page: LLMs in Vespa - url: /en/rag/llms-in-vespa.html - - page: Using local LLMs - url: /en/rag/local-llms.html - - page: Using external LLMs - url: /en/rag/external-llms.html - - page: Document enrichment with LLMs - url: /en/rag/document-enrichment.html - - page: Model hub - url: /en/rag/model-hub.html - - - title: Linguistics and text processing - documents: - - title: Linguistics - sub: - - page: Linguistics Overview - url: /en/linguistics/linguistics.html - - page: Default (OpenNLP) linguistics - url: /en/linguistics/linguistics-opennlp.html - - page: Lucene linguistics - url: /en/linguistics/lucene-linguistics.html - - page: Custom linguistics - url: /en/linguistics/linguistics-custom.html - - page: Query rewriting - url: /en/linguistics/query-rewriting.html - - page: Troubleshooting character encoding - url: /en/linguistics/troubleshooting-encoding.html - - - title: Content and elasticity - documents: - - page: Content clusters - url: /en/content/proton.html - - page: Content nodes and states - url: /en/content/content-nodes.html - - page: Elasticity - url: /en/content/elasticity.html - - page: Document attributes - url: /en/content/attributes.html - - page: Consistency Model - url: /en/content/consistency.html - - page: Distribution algorithm - url: /en/content/idealstate.html - - page: Buckets - url: /en/content/buckets.html - - - title: Performance - documents: - - page: Performance overview - url: /en/performance/ - - page: Practical performance guide - url: /en/performance/practical-search-performance-guide.html - - page: Serving sizing guide - url: /en/performance/sizing-search.html - - page: Feed sizing guide - url: /en/performance/sizing-feeding.html - - page: Node resources - url: /en/performance/node-resources.html - - title: "Instance types" - sub: - - page: AWS instance types - url: /en/performance/instance-types/aws-instance-types.html - - page: GCP instance types - url: /en/performance/instance-types/gcp-instance-types.html - - page: Azure instance types - url: /en/performance/instance-types/azure-instance-types.html - - page: Topology and resizing - url: /en/performance/topology-and-resizing.html - - page: Streaming search - url: /en/performance/streaming-search.html - - page: Benchmarking - url: /en/performance/benchmarking.html - - page: Benchmarking using Vespa Cloud - url: /en/performance/benchmarking-cloud.html - - page: Memory visualizer - url: /en/performance/memory-visualizer.html - - page: Profiling - url: /en/performance/profiling.html - - page: Container tuning - url: /en/performance/container-tuning.html - - page: Rate-limiting queries - url: /en/performance/rate-limiting-searcher.html - - page: Graceful degradation - url: /en/performance/graceful-degradation.html - - page: Caches - url: /en/performance/caches-in-vespa.html - - page: HTTP performance testing - url: /en/performance/container-http.html - - page: HTTP/2 - url: /en/performance/http2.html - - page: Feature tuning - url: /en/performance/feature-tuning.html - - page: Valgrind - url: /en/performance/valgrind.html - - - title: Operations - documents: - - page: Quota - url: /en/cloud/quota.html - - page: Environments - url: /en/operations/environments.html - - page: Zones - url: /en/operations/zones.html - - page: Availability Zones - url: /en/operations/az.html - - page: Production deployment - url: /en/operations/production-deployment.html - - page: Deployment variants - url: /en/operations/deployment-variants.html - - page: Automated deployments - url: /en/operations/automated-deployments.html - - page: Autoscaling - url: /en/operations/autoscaling.html - - title: "Enclave: Bring your own cloud" - sub: - - page: Enclave - url: /en/operations/enclave/enclave.html - - page: AWS getting started - url: /en/operations/enclave/aws-getting-started.html - - page: AWS architecture - url: /en/operations/enclave/aws-architecture.html - - page: Azure getting started - url: /en/operations/enclave/azure-getting-started.html - - page: Azure architecture - url: /en/operations/enclave/azure-architecture.html - - page: GCP getting started - url: /en/operations/enclave/gcp-getting-started.html - - page: GCP architecture - url: /en/operations/enclave/gcp-architecture.html - - page: Log archive - url: /en/operations/enclave/archive.html - - page: Operations - url: /en/operations/enclave/operations.html - - page: Reindexing - url: /en/operations/reindexing.html - - page: Reindexing on Vespa Cloud - url: /en/operations/reindexing-cloud.html - - page: Data management and backup - url: /en/operations/data-management.html - - page: Cloning applications and data - url: /en/operations/cloning.html - - page: Monitoring - url: /en/operations/monitoring.html - - page: Metrics - url: /en/operations/metrics.html - - page: Telemetry export - url: /en/operations/telemetry-export.html - - page: Notifications - url: /en/operations/notifications.html - - page: Support - url: /en/cloud/support.html - - page: Login Help - url: /en/operations/login.html - - page: Single Sign-On (SSO) Setup - url: /en/operations/sso.html - - page: Deployment patterns - url: /en/operations/deployment-patterns.html - - page: Private endpoints - url: /en/operations/private-endpoints.html - - page: Endpoint routing - url: /en/operations/endpoint-routing.html - - page: Access logging - url: /en/operations/access-logging.html - - title: Artifact archive - sub: - - page: Archive guide - url: /en/operations/archive/archive-guide.html - - page: Archive Guide AWS - url: /en/operations/archive/archive-guide-aws.html - - page: Archive Guide GCP - url: /en/operations/archive/archive-guide-gcp.html - - page: Deleting applications - url: /en/operations/deleting-applications.html - - title: Self-managed - sub: - - page: Admin procedures - url: /en/operations/self-managed/admin-procedures.html - - page: Multinode Systems - url: /en/operations/self-managed/multinode-systems.html - - page: Files, Processes, Ports, Environment - url: /en/operations/self-managed/files-processes-and-ports.html - - page: Node Setup - url: /en/operations/self-managed/node-setup.html - - page: Using Kubernetes - url: /en/operations/self-managed/using-kubernetes-with-vespa.html - - page: Build and install - url: /en/operations/self-managed/build-install.html - - page: Monitoring - url: /en/operations/self-managed/monitoring.html - - page: Content node recovery - url: /en/operations/self-managed/content-node-recovery.html - - page: Configuration Servers - url: /en/operations/self-managed/configuration-server.html - - page: Live Vespa upgrade procedure - url: /en/operations/self-managed/live-upgrade.html - - page: Config Sentinel - url: /en/operations/self-managed/config-sentinel.html - - page: Config Proxy - url: /en/operations/self-managed/config-proxy.html - - page: Docker Containers - url: /en/operations/self-managed/docker-containers.html - - page: Docker Containers GPU setup - url: /en/operations/self-managed/vespa-gpu-container.html - - page: CPU Support - url: /en/operations/self-managed/cpu-support.html - - page: Service Location Broker - url: /en/operations/self-managed/slobrok.html - - page: Change from attribute to index procedure - url: /en/operations/self-managed/procedure-change-attribute-index.html - - page: Container - url: /en/operations/self-managed/container.html - - page: Sizing examples - url: /en/operations/self-managed/sizing-examples.html - - page: Vespa Support CLI - url: /en/operations/self-managed/vespa-support.html - - title: Kubernetes - sub: - - page: Introduction - url: /en/operations/kubernetes/vespa-on-kubernetes.html - - page: Architecture - url: /en/operations/kubernetes/architecture.html - - title: Deployment - sub: - - page: Installation - url: /en/operations/kubernetes/deployment/installation.html - - page: Minikube Setup - url: /en/operations/kubernetes/deployment/local-deployment.html - - page: Setup ECR Pull-through Cache - url: /en/operations/kubernetes/deployment/ecr-pull-through-cache.html - - page: Setup Dev Environment - url: /en/operations/kubernetes/deployment/dev-mode.html - - page: Permissions - url: /en/operations/kubernetes/deployment/permissions.html - - title: Operations - sub: - - page: Operations - url: /en/operations/kubernetes/operations/operations.html - - page: Upgrade Vespa on Kubernetes - url: /en/operations/kubernetes/operations/upgrades.html - - page: Delete a VespaSet - url: /en/operations/kubernetes/operations/delete-vespaset.html - - page: Monitor a Vespa on Kubernetes Deployment - url: /en/operations/kubernetes/operations/monitoring.html - - page: Resource Scaling - url: /en/operations/kubernetes/operations/resource-scaling.html - - title: Configuration - sub: - - page: Configure Local Storage Type - url: /en/operations/kubernetes/configuration/configure-local-storage-type.html - - page: Configure Log Collections - url: /en/operations/kubernetes/configuration/logging.html - - page: Configure External Access Layer - url: /en/operations/kubernetes/configuration/ingress.html - - page: Provide Custom Overrides - url: /en/operations/kubernetes/configuration/custom-overrides-podtemplate.html - - page: Enable TLS Encryption - url: /en/operations/kubernetes/configuration/tls.html - - - title: Security - documents: - - page: Security overview - url: /en/security/security.html - - page: Security Guide - url: /en/security/guide.html - - page: Secret Store - url: /en/security/secret-store.html - - page: Cloudflare Workers - url: /en/security/cloudflare-workers.html - - page: Security Whitepaper - url: /en/security/whitepaper.html - - page: Securing a Vespa installation - url: /en/security/securing-your-vespa-installation.html - - page: mTLS - url: /en/security/mtls.html - - - title: Clients - documents: - - page: Command line client (Vespa CLI) - url: /en/clients/vespa-cli.html - - page: Python client (pyVespa) - url: "https://vespa-engine.github.io/pyvespa/" - - page: Java feed client - url: /en/clients/vespa-feed-client.html - - page: HTTP best practices - url: /en/clients/http-best-practices.html - - - title: Modules - documents: - - title: E-commerce - sub: - - page: Multi-Currency Pricing - url: /en/modules/e-commerce/multi-currency-filtering.html - - page: Saved Search Notifications - url: /en/modules/e-commerce/saved-search.html - - page: Using Features Together - url: /en/modules/e-commerce/using-features-together.html - - - title: Reference - documents: - - title: APIs - sub: - - page: APIs overview - url: /en/reference/api/api.html - - page: The query API - url: /en/reference/api/query.html - - page: /document/v1 API - url: /en/reference/api/document-v1.html - - page: /state/v1 API - url: /en/reference/api/state-v1.html - - page: /application/v2 API (deployment) - url: /en/reference/api/deploy-v2.html - - page: /application/v2/tenant API - url: /en/reference/api/application-v2.html - - page: /config/v2 API - url: /en/reference/api/config-v2.html - - page: /cluster/v2 API - url: /en/reference/api/cluster-v2.html - - page: /metrics/v1 API - url: /en/reference/api/metrics-v1.html - - page: /metrics/v2 API - url: /en/reference/api/metrics-v2.html - - page: /prometheus/v1 API - url: /en/reference/api/prometheus-v1.html - - title: Applications and components - sub: - - page: Application packages - url: /en/reference/applications/application-packages.html - - title: services.xml - sub: - - page: services.xml - url: /en/reference/applications/services/services.html - - page: services.xml - admin - url: /en/reference/applications/services/admin.html - - page: services.xml - container - url: /en/reference/applications/services/container.html - - page: services.xml - content - url: /en/reference/applications/services/content.html - - page: services.xml - docproc - url: /en/reference/applications/services/docproc.html - - page: services.xml - http - url: /en/reference/applications/services/http.html - - page: services.xml - processing - url: /en/reference/applications/services/processing.html - - page: services.xml - search - url: /en/reference/applications/services/search.html - - page: deployment.xml - url: /en/reference/applications/deployment.html - - page: hosts.xml - url: /en/reference/applications/hosts.html - - page: validation-overrides.xml - url: /en/reference/applications/validation-overrides.html - - page: Components - url: /en/reference/applications/components.html - - page: Configuration files - url: /en/reference/applications/config-files.html - - page: System test - url: /en/reference/applications/testing.html - - page: System test (Java) - url: /en/reference/applications/testing-java.html - - title: Schemas and documents - sub: - - page: Schemas - url: /en/reference/schemas/schemas.html - - page: Document JSON format - url: /en/reference/schemas/document-json-format.html - - page: Document field path language - url: /en/reference/schemas/document-field-path.html - - title: Reading and writing - sub: - - page: Indexing language - url: /en/reference/writing/indexing-language.html - - page: Document selector language - url: /en/reference/writing/document-selector-language.html - - title: Querying - sub: - - page: The YQL query language - url: /en/reference/querying/yql.html - - page: The simple query language - url: /en/reference/querying/simple-query-language.html - - page: Select - url: /en/reference/querying/json-query-language.html - - page: Grouping - url: /en/reference/querying/grouping-language.html - - page: Sorting - url: /en/reference/querying/sorting-language.html - - page: Query profiles - url: /en/reference/querying/query-profiles.html - - page: Semantic rules - url: /en/reference/querying/semantic-rules.html - - page: The default result format - url: /en/reference/querying/default-result-format.html - - page: The page result format - url: /en/reference/querying/page-result-format.html - - page: Page templates - url: /en/reference/querying/page-templates.html - - title: Ranking and inference - sub: - - page: Ranking expressions - url: /en/reference/ranking/ranking-expressions.html - - page: Tensor evaluation - url: /en/reference/ranking/tensor.html - - page: Rank features - url: /en/reference/ranking/rank-features.html - - page: nativeRank - url: /en/reference/ranking/nativerank.html - - page: String segment match - url: /en/reference/ranking/string-segment-match.html - - page: Rank feature configuration - url: /en/reference/ranking/rank-feature-configuration.html - - page: Rank types - url: /en/reference/ranking/rank-types.html - - page: Model files - url: /en/reference/ranking/model-files.html - - page: Constant tensors - url: /en/reference/ranking/constant-tensor-json-format.html - - title: RAG and embedding - sub: - - page: Chunking - url: /en/reference/rag/chunking.html - - page: Embedding - url: /en/reference/rag/embedding.html - - title: Operations - sub: - - page: Health checks - url: /en/reference/operations/health-checks.html - - page: Log files - url: /en/reference/operations/log-files.html - - page: Tools - url: /en/reference/operations/tools.html - - title: Metrics - sub: - - page: Metrics - url: /en/reference/operations/metrics/metrics.html - - page: Default metric set - url: /en/reference/operations/metrics/default-metric-set.html - - page: Vespa metric set - url: /en/reference/operations/metrics/vespa-metric-set.html - - page: Metric units - url: /en/reference/operations/metrics/metric-units.html - - page: Container metrics - url: /en/reference/operations/metrics/container.html - - page: Distributor metrics - url: /en/reference/operations/metrics/distributor.html - - page: Search node metrics - url: /en/reference/operations/metrics/searchnode.html - - page: Storage metrics - url: /en/reference/operations/metrics/storage.html - - page: Configserver metrics - url: /en/reference/operations/metrics/configserver.html - - page: Logd metrics - url: /en/reference/operations/metrics/logd.html - - page: Node Admin metrics - url: /en/reference/operations/metrics/nodeadmin.html - - page: Slobrok metrics - url: /en/reference/operations/metrics/slobrok.html - - page: Cluster controller metrics - url: /en/reference/operations/metrics/clustercontroller.html - - page: Sentinel metrics - url: /en/reference/operations/metrics/sentinel.html - - title: Self-managed - sub: - - page: Tools - url: /en/reference/operations/self-managed/tools.html - - title: Security - sub: - - page: Mtls - url: /en/reference/security/mtls.html - - title: Clients - sub: - - title: Vespa CLI - sub: - - page: vespa - url: /en/reference/clients/vespa-cli/vespa.html - - page: vespa activate - url: /en/reference/clients/vespa-cli/vespa_activate.html - - page: vespa auth - url: /en/reference/clients/vespa-cli/vespa_auth.html - - page: vespa clone - url: /en/reference/clients/vespa-cli/vespa_clone.html - - page: vespa config - url: /en/reference/clients/vespa-cli/vespa_config.html - - page: vespa curl - url: /en/reference/clients/vespa-cli/vespa_curl.html - - page: vespa deploy - url: /en/reference/clients/vespa-cli/vespa_deploy.html - - page: vespa destroy - url: /en/reference/clients/vespa-cli/vespa_destroy.html - - page: vespa document - url: /en/reference/clients/vespa-cli/vespa_document.html - - page: vespa feed - url: /en/reference/clients/vespa-cli/vespa_feed.html - - page: vespa fetch - url: /en/reference/clients/vespa-cli/vespa_fetch.html - - page: vespa inspect - url: /en/reference/clients/vespa-cli/vespa_inspect.html - - page: vespa log - url: /en/reference/clients/vespa-cli/vespa_log.html - - page: vespa prepare - url: /en/reference/clients/vespa-cli/vespa_prepare.html - - page: vespa prod - url: /en/reference/clients/vespa-cli/vespa_prod.html - - page: vespa query - url: /en/reference/clients/vespa-cli/vespa_query.html - - page: vespa status - url: /en/reference/clients/vespa-cli/vespa_status.html - - page: vespa test - url: /en/reference/clients/vespa-cli/vespa_test.html - - page: vespa version - url: /en/reference/clients/vespa-cli/vespa_version.html - - page: vespa visit - url: /en/reference/clients/vespa-cli/vespa_visit.html - - title: Release notes - sub: - - page: Vespa 7 - url: /en/reference/release-notes/vespa7.html - - page: Vespa 8 - url: /en/reference/release-notes/vespa8.html - - page: Vespa 9 (upcoming) - url: /en/reference/release-notes/vespa9.html diff --git a/_includes/assets/images/commonmark-logo.svg b/_includes/assets/images/commonmark-logo.svg deleted file mode 100644 index 5813a6c109..0000000000 --- a/_includes/assets/images/commonmark-logo.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - \ No newline at end of file diff --git a/_includes/chip-cloud.html b/_includes/chip-cloud.html deleted file mode 100644 index 0d1a51f0b5..0000000000 --- a/_includes/chip-cloud.html +++ /dev/null @@ -1,12 +0,0 @@ - - Vespa Cloud - - This content is applicable to Vespa Cloud deployments. - - - - - - - diff --git a/_includes/chip-enterprise.html b/_includes/chip-enterprise.html deleted file mode 100644 index dc80b54bad..0000000000 --- a/_includes/chip-enterprise.html +++ /dev/null @@ -1,12 +0,0 @@ - - Enterprise - - Not open source: This functionality is only available commercially. - - - - - - - diff --git a/_includes/chip-self-managed.html b/_includes/chip-self-managed.html deleted file mode 100644 index 7413520c41..0000000000 --- a/_includes/chip-self-managed.html +++ /dev/null @@ -1,12 +0,0 @@ - - Self-managed - - This content is applicable to self-managed Vespa systems. - - - - - - - diff --git a/_includes/deprecated.html b/_includes/deprecated.html deleted file mode 100644 index f8dc2a2060..0000000000 --- a/_includes/deprecated.html +++ /dev/null @@ -1,4 +0,0 @@ - diff --git a/_includes/htmlhead.html b/_includes/htmlhead.html deleted file mode 100644 index a1e6f1acb6..0000000000 --- a/_includes/htmlhead.html +++ /dev/null @@ -1,91 +0,0 @@ - - - - - - - - {% if page.meta_robots_noindex %} - {% endif %}{% seo %} - - {% if jekyll.environment == 'production' %} - - - - - - {% endif %} - - - - - - - - - - - - - - - - {% if include.theme == "light" %} - - {% else %} - - {% endif %} - - - {% if include.theme == "light" %} - - {% endif %} - - {% if page.content contains ''%} - - - {% endif %} - - - - - - - - - - - {% if jekyll.environment == 'production' and site.google_tag_manager %} - - - - {% endif %} - \ No newline at end of file diff --git a/_includes/important.html b/_includes/important.html deleted file mode 100644 index ab099b9369..0000000000 --- a/_includes/important.html +++ /dev/null @@ -1,4 +0,0 @@ - \ No newline at end of file diff --git a/_includes/js.html b/_includes/js.html deleted file mode 100644 index dfe01e9d8f..0000000000 --- a/_includes/js.html +++ /dev/null @@ -1,6 +0,0 @@ - - diff --git a/_includes/mode-content-hidden.html b/_includes/mode-content-hidden.html deleted file mode 100644 index 7e282d1a4f..0000000000 --- a/_includes/mode-content-hidden.html +++ /dev/null @@ -1,27 +0,0 @@ - - - -
- {{ include.content | markdownify }} -
\ No newline at end of file diff --git a/_includes/mode-content-waring.html b/_includes/mode-content-waring.html deleted file mode 100644 index 63983a7622..0000000000 --- a/_includes/mode-content-waring.html +++ /dev/null @@ -1,9 +0,0 @@ - -{% if params.mode != include.mode %} -
Oh no, I need to make a message here!!
-{% endif %} -
- {{ content }} -
diff --git a/_includes/navbar.html b/_includes/navbar.html deleted file mode 100644 index 775b1f741d..0000000000 --- a/_includes/navbar.html +++ /dev/null @@ -1,167 +0,0 @@ - - - - - - - diff --git a/_includes/new.html b/_includes/new.html deleted file mode 100644 index b0c1b5b775..0000000000 --- a/_includes/new.html +++ /dev/null @@ -1,4 +0,0 @@ - \ No newline at end of file diff --git a/_includes/note.html b/_includes/note.html deleted file mode 100644 index 7e0b5b4d75..0000000000 --- a/_includes/note.html +++ /dev/null @@ -1,4 +0,0 @@ - \ No newline at end of file diff --git a/_includes/pre-req.html b/_includes/pre-req.html deleted file mode 100644 index 1881a1f582..0000000000 --- a/_includes/pre-req.html +++ /dev/null @@ -1,35 +0,0 @@ - diff --git a/_includes/setup.html b/_includes/setup.html deleted file mode 100644 index 2c5a106a41..0000000000 --- a/_includes/setup.html +++ /dev/null @@ -1,73 +0,0 @@ -

Setup:

-
    -
  1. -

    Create a tenant on Vespa Cloud:

    -

    Go to console.vespa-cloud.com - and create your tenant (unless you already have one).

    -
  2. - -
  3. -

    Install the Vespa CLI - using Homebrew:

    -
    - -
    -$ brew install vespa-cli
    -
    -
    -

    Windows/No Homebrew? See the Vespa CLI page to download directly.

    -
  4. - -
  5. -

    Configure the Vespa client:

    -
    - - -
    -$ export VESPA_CLI_HOME=$PWD/.vespa
    -
    -
    -$ vespa config set target cloud
    -$ vespa config set application vespa-team.autotest
    -
    -
    -

    - Use the tenant name from step 1 instead of "vespa-team", - and replace in other steps in this example guide, too. -

    -
  6. - -
  7. -

    Get Vespa Cloud control plane access:

    -
    - -
    -$ vespa auth login
    -
    -
    -

    Follow the instructions from the command to authenticate.

    -
  8. - -
  9. -

    Clone a sample application:

    -
    - -
    -$ vespa clone {{ include.appname }} myapp && cd myapp
    -
    -
    -

    See sample-apps for other sample apps you can clone.

    -
  10. - -
  11. -

    Add a certificate for data plane access - to the application:

    -
    - -
    -$ vespa auth cert app
    -
    -
    -

    It is a good idea to take note of the path to the .pem files written here.

    -
  12. -
diff --git a/_includes/sidebar.html b/_includes/sidebar.html deleted file mode 100644 index 5080e39325..0000000000 --- a/_includes/sidebar.html +++ /dev/null @@ -1,186 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/_includes/toc.html b/_includes/toc.html deleted file mode 100644 index ff10df8049..0000000000 --- a/_includes/toc.html +++ /dev/null @@ -1,189 +0,0 @@ - -{% assign h2_count = content | split: ' 4 %} - - - - - - -{% endif %} \ No newline at end of file diff --git a/_includes/version.html b/_includes/version.html deleted file mode 100644 index 631ef99f55..0000000000 --- a/_includes/version.html +++ /dev/null @@ -1 +0,0 @@ -Vespa {{ include.version }} diff --git a/_includes/video-include.html b/_includes/video-include.html deleted file mode 100644 index 43af23f959..0000000000 --- a/_includes/video-include.html +++ /dev/null @@ -1,8 +0,0 @@ - diff --git a/_includes/warning.html b/_includes/warning.html deleted file mode 100644 index 7431645c62..0000000000 --- a/_includes/warning.html +++ /dev/null @@ -1,4 +0,0 @@ - \ No newline at end of file diff --git a/_layouts/default.html b/_layouts/default.html deleted file mode 100644 index e5f080d662..0000000000 --- a/_layouts/default.html +++ /dev/null @@ -1,87 +0,0 @@ - - - - -{% include htmlhead.html theme="light" %} - - - - {% include navbar.html logo="logo.svg" class="vespa-light" %} - -
- -
- -
- {% include sidebar.html %} - -
- -
-
- -
- {%- for applies_to in page.applies_to %} - {% include chip-{{ applies_to }}.html %} - {%- endfor %} -
- - {% unless page.skipheading %} -
-
-

{{ page.title | escape }}

-
- -
- {%- if page.data.generate_markdown != false -%} - {%- assign last_char = page.url | slice: -1 -%} - {%- if last_char == '/' -%} - {%- assign markdown_url = page.url | append: 'index.html.md' -%} - {%- else -%} - {%- assign markdown_url = page.url | append: '.md' -%} - {%- endif -%} - - {% include assets/images/commonmark-logo.svg %} - - {%- endif -%} - - -
- -
- {% endunless %} - -
- {{ content }} -
- - -
-
-
- Copyright © {{ site.time | date: "%Y" }} {{ site.name }} - - Cookie Preferences -
-
-
- - -
-
- {% include toc.html %} -
-
-
- - {% include js.html %} - - - - - - \ No newline at end of file diff --git a/_paragraphs_config.yml b/_paragraphs_config.yml deleted file mode 100644 index 2c72e9d777..0000000000 --- a/_paragraphs_config.yml +++ /dev/null @@ -1,48 +0,0 @@ -# Copyright Vespa.ai. All rights reserved. - -search: - namespace: "open-p" - doc_type: "paragraph" - do_feed: true - do_index_removal_before_feed: false - feed_endpoints: - # This config needs to be the same in all files starting with "_" in: - # vespa-engine/documentation, vespa-engine/pyvespa, vespa-engine/sample-apps, - # vespaai/blog and vespaai/cloud-site - # - # vespacloud-docsearch | prod.aws-us-east-1c - - url: https://b671e1db.b68a8c0d.z.vespa-app.cloud/ - indexes: - - paragraph_index.json - # vespacloud-docsearch | prod.aws-eu-west-1a - - url: https://ed053d52.b68a8c0d.z.vespa-app.cloud/ - indexes: - - paragraph_index.json - # vespacloud-docsearch | prod.gcp-us-central1-f - - url: https://a341952a.b68a8c0d.z.vespa-app.cloud/ - indexes: - - paragraph_index.json - # vespacloud-docsearch | prod.gcp-us-central1-a - - url: https://ba97230a.b68a8c0d.z.vespa-app.cloud/ - indexes: - - paragraph_index.json - # vespacloud-docsearch | prod.aws-euc1-az1 - - url: https://af1993cf.b68a8c0d.z.vespa-app.cloud/ - indexes: - - open_index.json - # vespacloud-docsearch | prod.aws-us-east-1 - - url: https://b42c9416.b68a8c0d.z.vespa-app.cloud/ - indexes: - - paragraph_index.json - # vespacloud-docsearch.cloud-enclave | prod.aws-us-east-1c - - url: https://cd560c23.b68a8c0d.z.vespa-app.cloud/ - indexes: - - paragraph_index.json - # vespacloud-docsearch.cloud-enclave | prod.aws-eu-west-1a - - url: https://b24dd625.b68a8c0d.z.vespa-app.cloud/ - indexes: - - paragraph_index.json - # vespacloud-docsearch.cloud-enclave | prod.gcp-us-central1-f - - url: https://b4f1a714.b68a8c0d.z.vespa-app.cloud/ - indexes: - - paragraph_index.json diff --git a/_plugins-vespafeed/vespa_index_generator.rb b/_plugins-vespafeed/vespa_index_generator.rb deleted file mode 100644 index 6b5d3a3597..0000000000 --- a/_plugins-vespafeed/vespa_index_generator.rb +++ /dev/null @@ -1,169 +0,0 @@ -# Copyright Vespa.ai. All rights reserved. - -require 'json' -require 'nokogiri' -require 'kramdown/parser/kramdown' - -module Jekyll - - class VespaIndexGenerator < Jekyll::Generator - priority :lowest - - def generate(site) - namespace = site.config["search"]["namespace"] - operations = [] - puts "::debug::VespaIndexGenerator is processing pages" - - if site.pages.empty? - # Drop out with an error - puts "::error::No pages found!" - return false - end - - puts "::debug::Pages found: #{site.pages.size}" - site.pages.each do |page| - # Skip pages that should not be indexed - next if ( - page.path.start_with?("css/") || - page.url.start_with?("/redirects.json") || - page.url.start_with?("/search.html") || - is_empty(page) - ) - - # "index" front matter flag controls inclusion in the Vespa site search index only, - # not external search engine indexing (use "meta_robots_noindex: true" in front matter for that). - if page.data["index"] == true - puts "::debug::Processing page: #{page.url}" - url = page.url - url += 'index.html' if url[-1, 1] == '/' - text = extract_text(page) - outlinks = extract_links(page) - headers = extract_headers(page) - keywords = get_keywords(page) - fields = { - :path => url, - :namespace => namespace, - :title => page.data["title"], - :content => text, - :html => get_html(page), - :term_count => text.split.length(), - :last_updated => Time.now.to_i - } - fields[:outlinks] = outlinks if !outlinks.empty? - fields[:headers] = headers if !headers.empty? - fields[:keywords] = keywords if !keywords.empty? - operations.push({ - :put => "id:" + namespace + ":doc::" + namespace + url, - :fields => fields - }) - else - puts "::debug::Page not indexed: #{page.url}, index flag: #{page.data['index']}" - end - end - json = JSON.pretty_generate(operations) - puts "::debug::Writing index file: #{namespace}_index.json" - File.open(namespace + "_index.json", "w") { |f| f.write(json) } - end - - def is_empty(page) - # The generated client-side redirects should not be indexed - - # they have no title and node content - return page.content == "" && !page.data["title"] - end - - def get_html(page) - if page.name[page.name.rindex('.')+1..-1] == "md" - doc = Kramdown::Document.new(page.content).to_html - else - doc = page.content - end - end - - def get_doc(page) - if page.name[page.name.rindex('.')+1..-1] == "md" - doc = Nokogiri::HTML(Kramdown::Document.new(page.content).to_html) - else - doc = Nokogiri::HTML(page.content) - end - end - - def reset_xml_pre(doc) - # The highlighter works on un-quoted XML, so some docs have non-HTML elements like - # Read and set such fields again for proper quoting and later text extraction (dirty hack ...) - doc.search('pre').each do |pre| - if pre.to_s =~ /\{% highlight xml %}/ - pre.content = pre.to_s.gsub("\n", " ") - .gsub(/
\s*\{% highlight xml %}(.+?)\{% endhighlight %}<\/pre>/, '\1')
-                end
-            end
-            return doc
-        end
-
-        def extract_text(page)
-            doc = reset_xml_pre(get_doc(page))
-            doc.search('th,td').each{ |e| e.after "\n" }
-            doc.search('style').each{ |e| e.remove }
-            content = doc.xpath("//text()").to_s
-                .gsub("\r"," ")
-                .gsub("\n"," ")
-            return strip_liquid(content)
-        end
-
-        def extract_links(page)
-            doc = get_doc(page)
-            links = doc.css('a').map { |link| link['href'] || ""}
-            links.reject{ |l| l.empty? }.map{ |l| l }
-            return links
-        end
-
-        def extract_headers(page)
-            doc = get_doc(page)
-            headers = doc.css('h1,h2,h3,h4').map { |header| header.content.gsub("\r"," ").gsub("\n"," ") || ""}
-            headers.reject{ |h| h.empty? }.map{ |h| h }
-            return headers
-        end
-
-        def get_keywords(page)
-            doc = get_doc(page)
-            keywords = []
-            if page.data["keywords"]
-                page.data["keywords"].split(/,/).each do |k|
-                    k = k.strip
-                    keywords.push(k) if ! k.empty?
-                end
-            end
-            return keywords
-        end
-
-        def strip_liquid(text)
-            return text.gsub(/\{%(.+?)%}/) { "#{ process_liquid($1) }" } # .+? is a lazy match, match only once
-        end
-
-        def process_liquid(match)
-        # https://ruby-doc.org/core-3.1.2/Regexp.html for the quotes
-        # ToDo: define the quote pattern (\"|\p{Pi}|\p{Pf}|') once and build regex using this as a parameter
-        #
-        # This is a poor man's solution to clean the data for search -
-        # the alternative is building the site and _then_ extract data
-        # That will however add jekyll build as a dependency for feeding, so keeping this simple for now
-            return match.gsub(/^\s*highlight\s*\w*/, "")
-                     .gsub(/^\s*(raw|endraw|endhighlight)/, "")
-                     .gsub(/^\s*include\s*(deprecated|important|note|query|warning).html\s*content=\s*(\"|\p{Pi}|\p{Pf}|')/, "")
-                     .gsub(/^\s*include\s*video-include.html\s.*video-title=\s*(\"|\p{Pi}|\p{Pf}|')/, "Find at vespa.ai/resources: ")
-                     .gsub(/^\s*include\s*pre-req.html\s*memory=\s*(\"|\p{Pi}|\p{Pf}|')(.*)/)  { "#{ process_pre_req($2) }" }
-                     .gsub(/(\"|\p{Pi}|\p{Pf}|')\s*$/, "")
-        end
-
-        def process_pre_req(match)
-            return match.gsub(/([0-9]*)\s*GB/, '
-                Docker: Docker Desktop for Mac/Windows, or Docker on Linux.
-                Operating system: Linux, macOS or Windows 10 Pro.
-                Architecture: x86_64 or arm64.
-                Minimum \1 GB RAM dedicated to Docker (the default is 2 GB on macOS). Memory recommendations.
-                Homebrew to install the Vespa CLI, or download Vespa CLI from Github releases.')
-                .gsub(/(\"|\p{Pi}|\p{Pf}|')\s*extra-reqs=\s*(\"|\p{Pi}|\p{Pf}|')/, "")
-        end
-
-    end
-
-end
diff --git a/_plugins/html_to_markdown_generator.rb b/_plugins/html_to_markdown_generator.rb
deleted file mode 100644
index f23a852a8e..0000000000
--- a/_plugins/html_to_markdown_generator.rb
+++ /dev/null
@@ -1,345 +0,0 @@
-# _plugins/html_to_markdown_generator.rb
-
-require 'reverse_markdown'
-require 'fileutils'
-require 'parallel'
-
-Jekyll::Hooks.register :site, :post_write do |site|
-  # Filter HTML pages and gather the necessary data upfront.
-  # Complex objects (like Jekyll::Page) don't work well across processes,
-  # so we extract the simple data (strings) we need.
-  pages_to_process = (site.pages + site.documents).filter_map do |page|
-    next unless page.output_ext == '.html' && page.data['generate_markdown'] != false
-    
-    html_path = page.destination(site.dest)
-    # Skip if the source HTML file doesn't exist
-    next unless File.exist?(html_path)
-
-    {
-      html_path: html_path,
-      url: page.url,
-      # Pass mtime to avoid a separate File.stat call in the child process
-      html_mtime: File.mtime(html_path)
-    }
-  end
-
-  # Calculate all unique destination directories before the parallel block.
-  # This avoids using a slow Mutex for synchronization inside the loop.
-  required_dirs = pages_to_process.map do |page_data|
-    File.dirname(construct_markdown_path(site.dest, page_data[:url]))
-  end.uniq.reject { |dir| dir == '.' }
-  
-  FileUtils.mkdir_p(required_dirs) unless required_dirs.empty?
-  Jekyll.logger.info "Markdown Generator:", "Pre-created #{required_dirs.count} directories."
-
-  # For CPU-bound tasks like HTML parsing, processes are much faster than threads
-  # in Ruby due to the Global Interpreter Lock (GIL).
-  # We pass site.dest as an argument to make it available in each process.
-  Parallel.each(pages_to_process, in_processes: Parallel.processor_count, progress: "Generating Markdown") do |page_data|
-    md_path = construct_markdown_path(site.dest, page_data[:url])
-    
-    # Skip if the markdown file is newer than the HTML file
-    next if File.exist?(md_path) && File.mtime(md_path) >= page_data[:html_mtime]
-
-    html_content = File.read(page_data[:html_path])
-
-    markdown_content = ReverseMarkdown.convert(
-      html_content, 
-      github_flavored: true,
-      unknown_tags: :bypass,
-      tag_border: ''
-    )
-
-    processed_content = process_markdown_content(markdown_content)
-
-    File.write(md_path, processed_content)
-  end
-
-  # First, we want to add a new file that is called llms.txt.
-  # The first lines should be
-  # # Vespa documentation
-
-  # > Top level description goes here
-
-  # Optional details go here
-
-  # Then, we want to append to that file, for each generated markdown file:    
-  # For each page in pages_to_process, we want to construct:
-  # A. section: This will be the directory level below  /en.
-  # B. md_path: This will be the full path of the markdown file. 
-  # Example: /en/reference/index.html.md will be: 
-  # section: reference
-  # md_path: /en/reference/index.html.md
-  
-  # Pass the site object to generate_llms_txt so we can access the base URL
-  generate_llms_txt(site.dest, pages_to_process, site)
-  generate_llms_full_txt(site.dest, pages_to_process, site)
-
-  Jekyll.logger.info "Markdown Generator:", "Processing complete."
-end
-
-def generate_llms_txt(site_dest, pages_to_process, site)
-  # Write to parent directory of site_dest
-  site_parent = File.expand_path("..", site_dest)
-  # Path to llms.txt
-  llms_path = File.join(site_dest, "llms.txt")
-  repo_path = File.join(site_parent, "llms.txt")
-  # Read llms-template.md to get top level description
-  template_path = File.join(site_parent, "llms-template.md")
-  if File.exist?(template_path)
-    template_content = File.read(template_path)
-  else
-    Jekyll.logger.Error "Markdown Generator:", "Template file not found at #{template_path}. Cannot generate llms.txt."
-    return
-  end
-  
-  # Get base URL from site configuration
-  base_url = site.config['url'] || 'https://docs.vespa.ai'
-  
-  # Initialize the file with header content
-  File.open(llms_path, 'w') do |file|
-    # Write template content first
-    file.puts template_content
-    
-    # Use shared method to build sections map
-    sections_map = build_sections_map(site_dest, pages_to_process)
-    
-    # Write each section to the llms.txt file
-    sections_map.each do |section, pages|
-      # Skip if section is '404'
-      next if section == '404'
-      # Filter out pages that start with "Redirect"
-      valid_pages = pages.reject { |page_info| page_info[:title].start_with?("Redirect") }
-      # Skip section entirely if no valid pages
-      next if valid_pages.empty?
-      
-      file.puts "## #{section}\n\n"
-      
-      valid_pages.each do |page_info|
-        # Use the base URL from site configuration + original page URL + ".md" extension
-        # Handle directory-style URLs ending with "/" by converting to index.html.md
-        url_suffix = page_info[:url].end_with?("/") ? "index.html.md" : ".md"
-        web_url = "#{base_url}#{page_info[:url]}#{url_suffix}"
-        file.puts "- [#{page_info[:title]}](#{web_url})" + (page_info[:description] ? ": #{page_info[:description]}" : "")
-      end
-      file.puts "\n"
-    end
-    # Copy file to repo_path
-    FileUtils.cp(llms_path, repo_path)
-  end
-  
-  Jekyll.logger.info "Markdown Generator:", "Generated llms.txt with #{pages_to_process.count} pages."
-end
-
-def generate_llms_full_txt(site_dest, pages_to_process, site)
-  site_parent = File.expand_path("..", site_dest)
-  llms_full_path = File.join(site_dest, "llms-full.txt")
-  
-  template_path = File.join(site_parent, "llms-template.md")
-  unless File.exist?(template_path)
-    Jekyll.logger.error "Markdown Generator:", "Template file not found at #{template_path}. Cannot generate llms-full.txt."
-    return
-  end
-  
-  template_content = File.read(template_path)
-  sections_map = build_sections_map(site_dest, pages_to_process)
-  
-  File.open(llms_full_path, 'w') do |file|
-    file.puts template_content
-    
-    sections_map.each do |section, pages|
-      next if section == '404'
-      valid_pages = pages.reject { |page_info| page_info[:title].start_with?("Redirect") }
-      next if valid_pages.empty?
-      
-      file.puts "## #{section}\n\n"
-      
-      valid_pages.each do |page_info|
-        file.puts "### #{page_info[:title]}"
-        file.puts page_info[:description] if page_info[:description]
-        file.puts ""
-        
-        if page_info[:content]
-          # Shift all headings to start at level 4 (####) to maintain hierarchy
-          shifted_content = shift_headings(page_info[:content], 4)
-          file.puts shifted_content
-        end
-        file.puts "\n---\n\n"  # Separator between pages
-      end
-    end
-    
-  end
-  
-  Jekyll.logger.info "Markdown Generator:", "Generated llms-full.txt with full content for #{pages_to_process.count} pages."
-end
-
-def build_sections_map(site_dest, pages_to_process)
-  sections_map = {}
-  
-  pages_to_process.each do |page_data|
-    md_path = construct_markdown_path(site_dest, page_data[:url])
-    section = extract_section(page_data[:url])
-    title_desc = extract_title_and_description(md_path, page_data[:url])
-    
-    sections_map[section] ||= []
-    
-    page_info = {
-      md_path: md_path,
-      title: title_desc[:title],
-      description: title_desc[:description],
-      url: page_data[:url]
-    }
-    
-    # Read content if file exists (for llms-full.txt generation)
-    page_info[:content] = File.read(md_path) if File.exist?(md_path)
-    sections_map[section] << page_info
-  end
-  
-  sections_map
-end
-
-def shift_headings(content, target_min_level)
-  lines = content.split("\n")
-  
-  # Find the minimum heading level in the content
-  min_level = nil
-  lines.each do |line|
-    if match = line.match(/^(#+)\s/)
-      level = match[1].length
-      min_level = level if min_level.nil? || level < min_level
-    end
-  end
-  
-  # If no headings found, return content as-is
-  return content if min_level.nil?
-  
-  # Calculate the shift needed
-  shift = target_min_level - min_level
-  
-  # Apply the shift to all headings
-  lines.map do |line|
-    if match = line.match(/^(#+)(\s.*)$/)
-      current_level = match[1].length
-      new_level = current_level + shift
-      # Ensure we don't go below level 1 or above level 6
-      new_level = [[new_level, 1].max, 6].min
-      "#" * new_level + match[2]
-    else
-      line
-    end
-  end.join("\n")
-end
-
-def extract_title_and_description(md_path, page_url)
-  # First try to extract H1 heading from the markdown file. 
-  # The description should be the first sentence (detected by a period followed by space or end of line) after the H1 heading.
-  title = nil
-  description = nil
-  
-  if File.exist?(md_path)
-    File.open(md_path, 'r') do |file|
-      file.each_line do |line|
-        # Look for the first line that starts with a single # followed by a space
-        if line.match(/^#\s+(.+)$/)
-          title = line.match(/^#\s+(.+)$/)[1].strip
-          
-          # Try to read the next non-empty line for description
-          while (next_line = file.gets)
-            next_line = next_line.strip
-            next if next_line.empty? || next_line.start_with?('#')
-            
-            # Extract first sentence (up to first period followed by space or end of line)
-            if match = next_line.match(/^(.+?\.)\s/)
-              description = match[1]
-            elsif next_line.end_with?('.')
-              description = next_line
-            else
-              description = next_line
-            end
-            break
-          end
-          break
-        end
-      end
-    end
-  end
-  
-  # Fallback: extract title from URL (last part before .html.md) if no title found
-  if title.nil?
-    path_parts = page_url[1..-1].split('/')
-    last_part = path_parts.last
-    
-    # If it ends with .html, remove that extension
-    if last_part&.end_with?('.html')
-      last_part = last_part[0...-5]  # Remove '.html'
-    end
-    
-    # If the last part is empty or just 'index', use the parent directory name
-    if last_part.nil? || last_part.empty? || last_part == 'index'
-      # Get the parent directory name
-      parent_dir = path_parts[-2] if path_parts.length > 1
-      title = parent_dir || 'root'
-    else
-      title = last_part
-    end
-  end
-  
-  return { title: title, description: description }
-rescue => e
-  Jekyll.logger.warn "Markdown Generator:", "Error extracting title from #{md_path}: #{e.message}"
-  # Final fallback to last part of URL
-  fallback_title = page_url.split('/').last&.gsub(/\.html$/, '') || 'untitled'
-  return { title: fallback_title, description: nil }
-end
-
-def extract_section(page_url)
-  # Remove leading slash and split by '/'
-  parts = page_url[1..-1].split('/')
-  # Remove .html suffix from the last part if present
-  parts[-1] = parts[-1].sub(/\.html$/, '') if parts.any?
-  to_return = nil
-  # If URL starts with 'en/', return the next directory level
-  if parts.first == 'en' && parts.length > 2
-    to_return = parts[2]  # e.g., /en/tutorials/getting-started.html -> section is 'tutorials'
-  elsif parts.first == 'en' && parts.length == 2
-    to_return = parts[1]  # e.g., /en/ -> section is 'en'
-  else
-    # If not under 'en/', return the first directory level
-    to_return = parts.first || 'root'  # e.g., /about.html -> section is 'about'
-  end
-  # Replace "-" with " " and capitalize each word
-  to_return.split('-').map(&:capitalize).join(' ')
-rescue => e
-  Jekyll.logger.warn "Markdown Generator:", "Error extracting section from #{page_url}: #{e.message}"
-  'Unknown'
-end
-
-def process_markdown_content(markdown_content)
-  # Find the first line that starts with '#' followed by a space
-  if (index = markdown_content.index(/^#\s/))
-    markdown_content.slice(index..-1)
-  else
-    # If no heading is found, return the original content
-    markdown_content
-  end
-end
-
-# Combined the logic for URLs ending in "/" and other paths.
-def construct_markdown_path(site_dest, page_url)
-  # Handle the root index page specifically
-  if page_url == "/"
-    return File.join(site_dest, "index.html.md")
-  end
-
-  # Remove the leading slash
-  path_without_slash = page_url[1..-1]
-  
-  # If the URL ends with "/", it's a directory index page
-  if page_url.end_with?("/")
-    # Convert "/en/tutorials/" to "_site/en/tutorials/index.html.md"
-    File.join(site_dest, path_without_slash + "index.html.md")
-  else
-    # For regular pages like "/page.html", convert to "_site/page.html.md"
-    File.join(site_dest, path_without_slash + ".md")
-  end
-end
\ No newline at end of file
diff --git a/_sass/denali-css/scss/components/_all.scss b/_sass/denali-css/scss/components/_all.scss
deleted file mode 100644
index 2f01f6cbcd..0000000000
--- a/_sass/denali-css/scss/components/_all.scss
+++ /dev/null
@@ -1,37 +0,0 @@
-@charset "utf-8";
-// Copyright 2019, Oath Inc.
-// Licensed under the terms of the MIT license. See LICENSE file in project root for terms.
-
-
-// DENALI SCSS :: COMPONENTS ROLLUP
-
-// STYLE
-@import "typography";
-
-// COMPONENTS
-@import "alerts";
-@import "box";
-@import "breadcrumb";
-@import "button";
-@import "button-group";
-@import "checkbox";
-@import "chips";
-@import "icons";
-@import "inputs";
-@import "input-group";
-@import "links";
-@import "lists";
-@import "loader";
-@import "menus";
-@import "modals";
-@import "navbar";
-@import "progress";
-@import "radio";
-@import "selects";
-@import "switch";
-@import "table";
-@import "tabs";
-@import "tags";
-@import "toggle";
-@import "tooltip";
-@import "titles";
diff --git a/_sass/denali-css/scss/components/alerts.scss b/_sass/denali-css/scss/components/alerts.scss
deleted file mode 100644
index 50899fa4fd..0000000000
--- a/_sass/denali-css/scss/components/alerts.scss
+++ /dev/null
@@ -1,271 +0,0 @@
-@charset "utf-8";
-// Copyright 2019, Oath Inc.
-// Licensed under the terms of the MIT license. See LICENSE file in project root for terms.
-@mixin alerts-default {
-  .alert {
-    // STRUCTURE
-    width: 400px;
-    display: flex;
-    box-sizing: border-box;
-    // STYLE
-    background: $alert-bg-color;
-    color: $alert-text-color;
-    border-radius: $alert-corner-radius;
-    box-shadow: $alert-shadow;
-    border-top-width: $alert-border-width;
-    border-top-style: $alert-border-style;
-    border-top-color: $alert-border-color;
-
-    @include media-size-down(mobile) {
-      width: 100%;
-    }
-
-    div {
-      margin: 17px 0px 18px 0px;
-      flex: auto;
-      overflow: hidden;
-      p {
-        font-size: 12px !important;
-        margin-top: 8px;
-        margin-bottom: 0px;
-        line-height: 15px;
-      }
-    }
-
-    // STATUS COLOR
-    .d-icon {
-      margin: 14px;
-      font-size: 28px !important;
-      color: $alert-border-color;
-    }
-
-    // CLOSE ICON
-    .close {
-      display: block;
-      padding: 16px 0px;
-      .d-icon {
-        font-size: 24px !important;
-        color: $alert-close-icon-color !important;
-      }
-    }
-
-    &.is-inline {
-      justify-content: $alert-inline-alignment-justify-content;
-      padding: $alert-inline-padding;
-      // STYLE
-      background: $alert-inline-bg-color;
-      color: $alert-inline-text-color;
-      font-size: $alert-inline-font-size;
-      // RESETS
-      box-shadow: none !important;
-      border: none !important;
-      border-radius: 0px !important;
-      // STRUCTURE
-      width: 100%;
-      display: flex !important;
-
-      p {
-        text-align: $alert-inline-alignment-text;
-        overflow: hidden;
-      }
-    }
-
-    // Block Level Alerts
-    &.is-block {      
-      border-top: none;
-      position: relative;
-      box-shadow: none !important;
-      width: 100%;
-      background: rgba($alert-border-color, 0.1);
-      border-left-width: $alert-border-width;
-      border-left-style: $alert-border-style;
-      border-left-color: $alert-border-color;
-
-      &.is-info {
-        background: rgba($color-status-information, 0.06);
-      }
-
-      &.is-warning {
-        background: rgba($color-status-warning, 0.06);
-      }
-
-      &.is-success {
-        background: rgba($color-status-success, 0.06);
-      }
-
-      &.is-danger {
-        background: rgba($color-status-danger, 0.06);
-      }
-
-      p {
-        font-size: 1.4rem !important;
-      }
-    }
-
-
-    // STATUS MODIFIERS
-    &.is-info {
-      border-color: $color-status-information;
-      .d-icon {
-        color: $color-status-information;
-      }
-    }
-    &.is-warning {
-      border-color: $color-status-warning;
-      .d-icon {
-        color: $color-status-warning;
-      }
-    }
-    &.is-success {
-      border-color: $color-status-success;
-      .d-icon {
-        color: $color-status-success;
-      }
-    }
-    &.is-danger {
-      border-color: $color-status-danger;
-      .d-icon {
-        color: $color-status-danger;
-      }
-    }
-  }
-}
-
-@mixin alerts-theme {
-  .alert {
-    @if variable-exists(alert-bg-color) {
-      background: $alert-bg-color;
-    }
-
-    @if variable-exists(alert-text-color) {
-      color: $alert-text-color;
-    }
-
-    @if variable-exists(alert-corner-radius) {
-      border-radius: $alert-corner-radius;
-    }
-
-    @if variable-exists(alert-shadow) {
-      box-shadow: $alert-shadow;
-    }
-
-    @if variable-exists(alert-border-width) {
-      border-top-width: $alert-border-width;
-    }
-
-    @if variable-exists(alert-border-style) {
-      border-top-style: $alert-border-style;
-    }
-
-    @if variable-exists(alert-border-color) {
-      border-top-color: $alert-border-color;
-    }
-
-    @if variable-exists(alert-border-color) {
-      .d-icon {
-        color: $alert-border-color;
-      }
-    }
-
-    @if variable-exists(alert-close-icon-color) {
-      .close {
-        .d-icon {
-          color: $alert-close-icon-color !important;
-        }
-      }
-    }
-
-    &.is-inline {
-      @if variable-exists(alert-inline-alignment-justify-content) {
-        justify-content: $alert-inline-alignment-justify-content;
-      }
-
-      @if variable-exists(alert-inline-padding) {
-        padding: $alert-inline-padding;
-      }
-
-      @if variable-exists(alert-inline-bg-color) {
-        background: $alert-inline-bg-color;
-      }
-
-      @if variable-exists(alert-inline-text-color) {
-        color: $alert-inline-text-color;
-      }
-
-      @if variable-exists(alert-inline-font-size) {
-        font-size: $alert-inline-font-size;
-      }
-
-
-      @if variable-exists(alert-inline-alignment-text) {
-        p {
-          text-align: $alert-inline-alignment-text;
-        }
-      }
-    }
-
-    &.is-block {
-      border-top: none;
-      background: rgba($alert-border-color, 0.1);
-      border-left-width: $alert-border-width;
-      border-left-style: $alert-border-style;
-      border-left-color: $alert-border-color;
-
-      &.is-info {
-        background: rgba($color-status-information, 0.06);
-      }
-
-      &.is-warning {
-        background: rgba($color-status-warning, 0.06);
-      }
-
-      &.is-success {
-        background: rgba($color-status-success, 0.06);
-      }
-
-      &.is-danger {
-        background: rgba($color-status-danger, 0.06);
-      }
-    }
-
-    @if variable-exists(color-status-information) {
-      &.is-info {
-        border-color: $color-status-information;
-
-        .d-icon {
-          color: $color-status-information;
-        }
-      }
-    }
-
-    @if variable-exists(color-status-warning) {
-      &.is-warning {
-        border-color: $color-status-warning;
-
-        .d-icon {
-          color: $color-status-warning;
-        }
-      }
-    }
-
-    @if variable-exists(color-status-success) {
-      &.is-success {
-        border-color: $color-status-success;
-
-        .d-icon {
-          color: $color-status-success;
-        }
-      }
-    }
-
-    @if variable-exists(color-status-danger) {
-      &.is-danger {
-        border-color: $color-status-danger;
-
-        .d-icon {
-          color: $color-status-danger;
-        }
-      }
-    }
-  }
-}
\ No newline at end of file
diff --git a/_sass/denali-css/scss/components/box.scss b/_sass/denali-css/scss/components/box.scss
deleted file mode 100644
index 30e217ef7a..0000000000
--- a/_sass/denali-css/scss/components/box.scss
+++ /dev/null
@@ -1,41 +0,0 @@
-@charset "utf-8";
-// Copyright 2019, Oath Inc.
-// Licensed under the terms of the MIT license. See LICENSE file in project root for terms.
-
-@mixin box-default {
-  .box {
-    padding: $box-padding;
-    background: $box-bg-color;
-    border-radius: $box-corner-radius;
-    border-color: $box-border-color;
-    border-width: $box-border-width;
-    border-style: $box-border-style;
-
-    footer {
-      margin: $box-footer-margin;
-      padding: $box-footer-padding;
-      border-top-style: solid;
-      border-top-color: $box-border-color;
-      border-top-width: $box-border-width;
-    }
-  }
-}
-
-@mixin box-theme {
-  .box {
-   padding: $box-padding;
-   background: $box-bg-color;
-   border-radius: $box-corner-radius;
-   border-color: $box-border-color;
-   border-width: $box-border-width;
-   border-style: $box-border-style;
-
-    footer {
-      margin: $box-footer-margin;
-      padding: $box-footer-padding;
-      border-top-style: solid;
-      border-top-color: $box-border-color;
-      border-top-width: $box-border-width;
-    }
-  }
-}
\ No newline at end of file
diff --git a/_sass/denali-css/scss/components/breadcrumb.scss b/_sass/denali-css/scss/components/breadcrumb.scss
deleted file mode 100644
index 2bfcae3b19..0000000000
--- a/_sass/denali-css/scss/components/breadcrumb.scss
+++ /dev/null
@@ -1,54 +0,0 @@
-@charset "utf-8";
-// Copyright 2019, Oath Inc.
-// Licensed under the terms of the MIT license. See LICENSE file in project root for terms.
-
-@mixin breadcrumb-default {
-  .breadcrumb {
-    list-style-type: none;
-    margin: 0;
-    padding: 0;
-
-    li {
-      float: left;
-      position: relative;
-      margin-right: $breadcrumb-right-margin;
-
-      > * {
-        display: block;
-      }
-
-      &::after {
-        content: $breadcrumb-divider;
-        position: absolute;
-        height: 1.6rem;
-        width: .4rem;
-        top: 0;
-        right: $breadcrumb-divider-right-margin;
-        color: $breadcrumb-divider-color;
-      }
-
-      &:last-child {
-        margin-right: 0px;
-
-        &::after {
-          display: none;
-        }
-      }
-    }
-  }
-}
-
-@mixin breadcrumb-theme {
-  .breadcrumb {
-
-    li {
-      margin-right: $breadcrumb-right-margin;
-
-      &::after {
-        content: $breadcrumb-divider;
-        right: $breadcrumb-divider-right-margin;
-        color: $breadcrumb-divider-color;
-      }
-    }
-  }
-}
\ No newline at end of file
diff --git a/_sass/denali-css/scss/components/button-group.scss b/_sass/denali-css/scss/components/button-group.scss
deleted file mode 100644
index 103c69fa3d..0000000000
--- a/_sass/denali-css/scss/components/button-group.scss
+++ /dev/null
@@ -1,27 +0,0 @@
-@charset "utf-8";
-// Copyright 2019, Oath Inc.
-// Licensed under the terms of the MIT license. See LICENSE file in project root for terms.
-
-.button-group {
-    display: inline-flex;
-
-    .button:first-child {
-        border-top-right-radius: 0px;
-        border-bottom-right-radius: 0px;
-    }
-
-    .button:last-child {
-        margin-left: -1px;
-        border-top-left-radius: 0px;
-        border-bottom-left-radius: 0px;
-    }
-
-    .button {
-        min-width: 0px;
-        padding: 0px 20px;
-    }
-
-    .has-icon {
-        width: 38px;
-    }
-}
\ No newline at end of file
diff --git a/_sass/denali-css/scss/components/button.scss b/_sass/denali-css/scss/components/button.scss
deleted file mode 100644
index 7a77785ccf..0000000000
--- a/_sass/denali-css/scss/components/button.scss
+++ /dev/null
@@ -1,408 +0,0 @@
-@charset "utf-8";
-// Copyright 2019, Oath Inc.
-// Licensed under the terms of the MIT license. See LICENSE file in project root for terms.
-
-@mixin button-default {
-  .button {
-    border: none;
-    outline: 0;
-    cursor: pointer;
-    box-sizing: border-box;
-    display: inline-flex;
-    justify-content: center;
-    align-items: center;
-    margin: 0px;
-    padding: 0px 10px;
-    padding-bottom: 1px;
-    height: $button-sizes-default-height;
-    font-size: $button-sizes-default-font-size;
-    min-width: $button-sizes-default-min-width;
-    border-radius: $button-corner-radius;
-    transition: background 300ms;
-    font-weight: $button-font-weight;
-    font-size: $button-font-size;
-
-    &.is-solid {
-      background: $button-solid-default-bg-color;
-      color: $button-solid-default-text-color;
-      &.is-inverse {
-        background: $button-solid-default-inverse-bg-color;
-        color: $button-solid-default-inverse-text-color;
-      }
-    }
-
-    &.is-outline {
-      background: $button-outline-default-bg-color;
-      color: $button-outline-default-text-color;
-      border: $button-outline-default-border;
-      &.is-inverse {
-        background: $button-outline-default-inverse-bg-color;
-        color: $button-outline-default-inverse-text-color;
-        border: $button-outline-default-inverse-border;
-      }
-    }
-
-    &.is-ghost {
-      background: $button-ghost-default-bg-color;
-      color: $button-ghost-default-text-color;
-      &.is-inverse {
-        background: $button-ghost-default-inverse-bg-color;
-        color: $button-ghost-default-inverse-text-color;
-      }
-    }
-
-    &.is-text {
-      background: $button-text-default-bg-color;
-      color: $button-text-default-text-color;
-      &.is-inverse {
-        background: $button-text-default-inverse-bg-color;
-        color: $button-text-default-inverse-text-color;
-      }
-    }
-
-    &.is-danger {
-      background: $color-status-danger;
-      color: $color-grey-100;
-      &.is-inverse {
-        background: $color-status-danger;
-        color: $color-grey-100;
-      }
-    }
-
-
-    // Hover and Focus
-    &:hover,
-    &.is-active,
-    &.is-active:hover,
-    &:focus {
-      transition: background 300ms;
-
-      &.is-solid {
-        background: $button-solid-focus-bg-color;
-        color: $button-solid-focus-text-color !important;
-        &.is-inverse {
-          background: $button-solid-focus-inverse-bg-color;
-          color: $button-solid-focus-inverse-text-color !important;
-        }
-      }
-
-      &.is-outline {
-        background: $button-outline-focus-bg-color;
-        color: $button-outline-focus-text-color !important;
-        border: $button-outline-focus-border;
-        &.is-inverse {
-          background: $button-outline-focus-inverse-bg-color;
-          color: $button-outline-focus-inverse-text-color !important;
-          border: $button-outline-focus-inverse-border;
-        }
-      }
-
-      &.is-ghost {
-        background: $button-ghost-focus-bg-color;
-        color: $button-ghost-focus-text-color !important;
-        &.is-inverse {
-          background: $button-ghost-focus-inverse-bg-color;
-          color: $button-ghost-focus-inverse-text-color !important;
-        }
-      }
-
-      &.is-text {
-        background: $button-text-focus-bg-color;
-        color: $button-text-focus-text-color !important;
-        &.is-inverse {
-          background: $button-text-focus-inverse-bg-color;
-          color: $button-text-focus-inverse-text-color !important;
-        }
-      }
-
-      &.is-danger {
-        background: #BB0000;
-        color: $color-grey-100 !important;
-        &.is-inverse {
-          background: #BB0000;
-          color: $color-grey-100 !important;
-        }
-      }
-    }
-
-    // disabled
-    &.is-disabled,
-    &[disabled],
-    &.is-disabled:hover,
-    &:hover[disabled] {
-      box-shadow: none;
-      cursor: not-allowed;
-
-      &.is-solid {
-        background: $button-solid-disabled-bg-color;
-        color: $button-solid-disabled-text-color !important;
-        &.is-inverse {
-          background: $button-solid-disabled-inverse-bg-color;
-          color: $button-solid-disabled-inverse-text-color !important;
-        }
-      }
-
-      &.is-outline {
-        background: $button-outline-disabled-bg-color;
-        color: $button-outline-disabled-text-color !important;
-        border: $button-outline-disabled-border;
-
-        &.is-inverse {
-          background: $button-outline-disabled-inverse-bg-color;
-          color: $button-outline-disabled-inverse-text-color !important;
-          border: $button-outline-disabled-inverse-border;
-        }
-      }
-
-      &.is-ghost {
-        background: $button-ghost-disabled-bg-color;
-        color: $button-ghost-disabled-text-color !important;
-        &.is-inverse {
-          background: $button-ghost-disabled-inverse-bg-color;
-          color: $button-ghost-disabled-inverse-text-color !important;
-        }
-      }
-
-      &.is-text {
-        background: $button-text-disabled-bg-color;
-        color: $button-text-disabled-text-color !important;
-        &.is-inverse {
-          background: $button-text-disabled-inverse-bg-color;
-          color: $button-text-disabled-inverse-text-color !important;
-        }
-      }
-
-      &.is-danger {
-        background: rgba($color-grey-800, 0.12);
-        color: rgba($color-grey-800 , 0.5) !important;
-        &.is-inverse {
-          background: rgba($color-grey-100 , 0.12);
-          color: rgba($color-grey-100, 0.5) !important;
-        }
-      }
-    }
-
-    // MODIFIERS
-    // SIZES
-    &.is-small {
-      height: $button-sizes-sm-height;
-      font-size: $button-sizes-sm-font-size;
-      min-width: $button-sizes-sm-min-width;
-    }
-    &.is-medium {
-      height: $button-sizes-md-height;
-      font-size: $button-sizes-md-font-size;
-      min-width: $button-sizes-md-min-width;
-    }
-    &.is-large {
-      height: $button-sizes-lg-height;
-      font-size: $button-sizes-lg-font-size;
-      min-width: $button-sizes-lg-min-width;
-      padding: 0px 30px;
-    }
-    &.is-full {
-      width: 100%;
-    }
-
-    // ICONS
-    &.has-icon {
-      min-width: 0px !important;
-    }
-    .d-icon {
-      vertical-align: middle;
-      font-size: 18px;
-      margin: 0px 6px;
-    }
-  }
-}
-
-@mixin button-theme {
-  .button {
-    height: $button-sizes-default-height;
-    font-size: $button-sizes-default-font-size;
-    min-width: $button-sizes-default-min-width;
-    border-radius: $button-corner-radius;
-    font-weight: $button-font-weight;
-    font-size: $button-font-size;
-
-    &.is-solid {
-      background: $button-solid-default-bg-color;
-      color: $button-solid-default-text-color;
-
-      &.is-inverse {
-        background: $button-solid-default-inverse-bg-color;
-        color: $button-solid-default-inverse-text-color;
-      }
-    }
-
-    &.is-outline {
-      background: $button-outline-default-bg-color;
-      color: $button-outline-default-text-color;
-      border: $button-outline-default-border;
-
-      &.is-inverse {
-        background: $button-outline-default-inverse-bg-color;
-        color: $button-outline-default-inverse-text-color;
-        border: $button-outline-default-inverse-border;
-      }
-    }
-
-    &.is-ghost {
-      background: $button-ghost-default-bg-color;
-      color: $button-ghost-default-text-color;
-
-      &.is-inverse {
-        background: $button-ghost-default-inverse-bg-color;
-        color: $button-ghost-default-inverse-text-color;
-      }
-    }
-
-    &.is-text {
-      background: $button-text-default-bg-color;
-      color: $button-text-default-text-color;
-
-      &.is-inverse {
-        background: $button-text-default-inverse-bg-color;
-        color: $button-text-default-inverse-text-color;
-      }
-    }
-
-    &.is-danger {
-      background: $color-status-danger;
-      color: $color-grey-100;
-
-      &.is-inverse {
-        background: $color-status-danger;
-        color: $color-grey-100;
-      }
-    }
-
-
-    // Hover and Focus
-    &:hover,
-    &.is-active,
-    &.is-active:hover,
-    &:focus {
-
-      &.is-solid {
-        background: $button-solid-focus-bg-color;
-        color: $button-solid-focus-text-color !important;
-
-        &.is-inverse {
-          background: $button-solid-focus-inverse-bg-color;
-          color: $button-solid-focus-inverse-text-color !important;
-        }
-      }
-
-      &.is-outline {
-        background: $button-outline-focus-bg-color;
-        color: $button-outline-focus-text-color !important;
-        border: $button-outline-focus-border;
-
-        &.is-inverse {
-          background: $button-outline-focus-inverse-bg-color;
-          color: $button-outline-focus-inverse-text-color !important;
-          border: $button-outline-focus-inverse-border;
-        }
-      }
-
-      &.is-ghost {
-        background: $button-ghost-focus-bg-color;
-        color: $button-ghost-focus-text-color !important;
-
-        &.is-inverse {
-          background: $button-ghost-focus-inverse-bg-color;
-          color: $button-ghost-focus-inverse-text-color !important;
-        }
-      }
-
-      &.is-text {
-        background: $button-text-focus-bg-color;
-        color: $button-text-focus-text-color !important;
-
-        &.is-inverse {
-          background: $button-text-focus-inverse-bg-color;
-          color: $button-text-focus-inverse-text-color !important;
-        }
-      }
-    }
-
-    // disabled
-    &.is-disabled,
-    &[disabled],
-    &.is-disabled:hover,
-    &:hover[disabled] {
-      &.is-solid {
-        background: $button-solid-disabled-bg-color;
-        color: $button-solid-disabled-text-color !important;
-
-        &.is-inverse {
-          background: $button-solid-disabled-inverse-bg-color;
-          color: $button-solid-disabled-inverse-text-color !important;
-        }
-      }
-
-      &.is-outline {
-        background: $button-outline-disabled-bg-color;
-        color: $button-outline-disabled-text-color !important;
-        border: $button-outline-disabled-border;
-
-        &.is-inverse {
-          background: $button-outline-disabled-inverse-bg-color;
-          color: $button-outline-disabled-inverse-text-color !important;
-          border: $button-outline-disabled-inverse-border;
-        }
-      }
-
-      &.is-ghost {
-        background: $button-ghost-disabled-bg-color;
-        color: $button-ghost-disabled-text-color !important;
-
-        &.is-inverse {
-          background: $button-ghost-disabled-inverse-bg-color;
-          color: $button-ghost-disabled-inverse-text-color !important;
-        }
-      }
-
-      &.is-text {
-        background: $button-text-disabled-bg-color;
-        color: $button-text-disabled-text-color !important;
-
-        &.is-inverse {
-          background: $button-text-disabled-inverse-bg-color;
-          color: $button-text-disabled-inverse-text-color !important;
-        }
-      }
-
-      &.is-danger {
-        background: rgba($color-grey-800, 0.12);
-        color: rgba($color-grey-800, 0.5) !important;
-
-        &.is-inverse {
-          background: rgba($color-grey-100, 0.12);
-          color: rgba($color-grey-100, 0.5) !important;
-        }
-      }
-    }
-
-    // MODIFIERS
-    // SIZES
-    &.is-small {
-      height: $button-sizes-sm-height;
-      font-size: $button-sizes-sm-font-size;
-      min-width: $button-sizes-sm-min-width;
-    }
-
-    &.is-medium {
-      height: $button-sizes-md-height;
-      font-size: $button-sizes-md-font-size;
-      min-width: $button-sizes-md-min-width;
-    }
-
-    &.is-large {
-      height: $button-sizes-lg-height;
-      font-size: $button-sizes-lg-font-size;
-      min-width: $button-sizes-lg-min-width;
-    }
-  }
-}
\ No newline at end of file
diff --git a/_sass/denali-css/scss/components/checkbox.scss b/_sass/denali-css/scss/components/checkbox.scss
deleted file mode 100644
index 58438323a9..0000000000
--- a/_sass/denali-css/scss/components/checkbox.scss
+++ /dev/null
@@ -1,144 +0,0 @@
-@charset "utf-8";
-// Copyright 2019, Oath Inc.
-// Licensed under the terms of the MIT license. See LICENSE file in project root for terms.
-
-@mixin checkbox-default {
-  .checkbox {
-    backface-visibility: hidden;
-    box-sizing: border-box;
-    display: inline-block;
-    outline: 0;
-    position: relative;
-    height: 20px;
-    padding-left: 2px;
-    margin-right: 16px;
-
-    input[type="checkbox"] {
-      box-sizing: border-box;
-      display: inline-block;
-      height: 20px;
-      opacity: 0;             /* Hide it */
-      position: absolute;     /* Take it out of the documenet flow */
-    }
-
-    > label {
-      box-sizing: border-box;
-      cursor: pointer;
-      padding: 0;
-      position: relative;
-      color: $checkbox-box-label-text-color;
-      font-size: 14px;
-      font-family: inherit;
-      font-weight: normal !important;
-    }
-
-    label:before {
-      border-radius: $checkbox-corner-radius;
-      box-shadow: $checkbox-box-border-color-standard;
-      box-sizing: border-box;
-      content: "";
-      display: inline-block;
-      height: 16px;
-      margin-right: 10px;
-      transition: all 0.2s;
-      vertical-align: text-top;
-      width: 16px;
-    }
-
-    input[type="checkbox"]:hover + label:before {
-      box-shadow: $checkbox-box-border-color-hover;
-    }
-
-    input[type="checkbox"]:focus + label:before {
-      box-shadow: $checkbox-box-border-color-hover;
-    }
-
-    input[type="checkbox"][data-partial=true] + label:before,
-    input[type="checkbox"]:checked + label:before {
-      box-shadow: $checkbox-box-border-color-active;
-      background: $checkbox-box-bg-color-active;
-    }
-
-    input[type="checkbox"]:disabled + label {
-      color: $checkbox-box-label-disabled-text-color;
-      cursor: not-allowed;
-    }
-
-    input[type="checkbox"]:disabled + label:before {
-      box-shadow: $checkbox-box-border-color-disabled;
-    }
-
-    input[type="checkbox"]:checked:not([data-partial]) + label:after {
-      border: solid $checkbox-checkmark-color;
-      border-width: 0 2px 2px 0;  /* creates the inverted "L" shape */
-      box-sizing: border-box;
-      content: " ";
-      display: block;
-      height: 12px;
-      left: 5px;
-      position: absolute;
-      top: 1px;
-      transform: rotate(45deg);
-      width: 6px; /* the short bar of the mark is half as long as the long bar */
-    }
-
-    input[type="checkbox"][data-partial] + label:after {
-      box-shadow: inset 0 0 0 2px $checkbox-box-partial-color;
-      box-sizing: border-box;
-      content: " ";
-      height: 2px;
-      left: 2px;
-      position: absolute;
-      top: 7px;
-      transition: all 0.2s;
-      width: 12px;
-      border-radius: 9999px;
-    }
-  }
-}
-
-@mixin checkbox-theme {
-    .checkbox {
-
-      label {
-        color: $checkbox-box-label-text-color;
-      }
-
-      label:before {
-        border-radius: $checkbox-corner-radius;
-        box-shadow: $checkbox-box-border-color-standard;
-      }
-
-      input[type="checkbox"]:hover+label:before {
-        box-shadow: $checkbox-box-border-color-hover;
-      }
-
-      input[type="checkbox"]:focus+label:before {
-        box-shadow: $checkbox-box-border-color-standard;
-      }
-
-      input[type="checkbox"][data-partial=true]+label:before,
-      input[type="checkbox"]:checked+label:before {
-        box-shadow: $checkbox-box-border-color-active;
-        background: $checkbox-box-bg-color-active;
-      }
-
-      input[type="checkbox"]:disabled+label {
-        color: $checkbox-box-label-disabled-text-color;
-      }
-
-      input[type="checkbox"]:disabled+label:before {
-        box-shadow: $checkbox-box-border-color-disabled;
-      }
-
-      input[type="checkbox"]:checked:not([data-partial])+label:after {
-        border: solid $checkbox-checkmark-color;
-        border-width: 0 2px 2px 0;
-      }
-
-      input[type="checkbox"][data-partial]+label:after {
-        border: solid $checkbox-box-partial-color;
-        border-width: 2px 0 0 0;
-      }
-    }
-}
\ No newline at end of file
diff --git a/_sass/denali-css/scss/components/chips.scss b/_sass/denali-css/scss/components/chips.scss
deleted file mode 100644
index b74a3545e3..0000000000
--- a/_sass/denali-css/scss/components/chips.scss
+++ /dev/null
@@ -1,60 +0,0 @@
-@charset "utf-8";
-// Copyright 2019, Oath Inc.
-// Licensed under the terms of the MIT license. See LICENSE file in project root for terms.
-
-@mixin chips-default {
-  .chips {
-    font-size: inherit;
-    background: $chips-bg-color;
-    border-radius: $chips-corner-radius;
-    color: $chips-text-color;
-    padding: $chips-padding;
-
-    &.is-small {
-      font-size: $chips-small-font-size;
-      padding: $chips-small-padding;
-    }
-  }
-
-  .chip-group {
-    .chips {
-      border-radius: 0px !important;
-      &:first-child {
-        border-top-left-radius: $chips-corner-radius !important;
-        border-bottom-left-radius: $chips-corner-radius !important;
-      }
-      &:last-child {
-        border-top-right-radius: $chips-corner-radius !important;
-        border-bottom-right-radius: $chips-corner-radius !important;
-      }
-    }
-  }
-}
-
-@mixin chips-theme {
-  .chips {
-    background: $chips-bg-color;
-    border-radius: $chips-corner-radius;
-    color: $chips-text-color;
-    padding: $chips-padding;
-
-    &.is-small {
-      font-size: $chips-small-font-size;
-      padding: $chips-small-padding;
-    }
-  }
-
-  .chip-group {
-    .chips {
-      &:first-child {
-        border-top-left-radius: $chips-corner-radius !important;
-        border-bottom-left-radius: $chips-corner-radius !important;
-      }
-
-      &:last-child {
-        border-top-right-radius: $chips-corner-radius !important;
-        border-bottom-right-radius: $chips-corner-radius !important;
-      }
-    }
-  }
-}
\ No newline at end of file
diff --git a/_sass/denali-css/scss/components/icons.scss b/_sass/denali-css/scss/components/icons.scss
deleted file mode 100644
index e73e88765a..0000000000
--- a/_sass/denali-css/scss/components/icons.scss
+++ /dev/null
@@ -1,90 +0,0 @@
-@charset "utf-8";
-// Copyright 2019, Oath Inc.
-// Licensed under the terms of the MIT license. See LICENSE file in project root for terms.
-
-@mixin icons-default {
-  .d-icon {
-    font-size: $icon-size-default;
-
-    &.is-extrasmall {
-      font-size: $icon-size-xs !important;
-    }
-    &.is-small {
-      font-size: $icon-size-sm !important;
-    }
-    &.is-medium {
-      font-size: $icon-size-md !important;
-    }
-    &.is-large {
-      font-size: $icon-size-lg !important;
-    }
-  }
-
-  svg.d-icon {
-    width: $icon-size-default;
-    height: $icon-size-default;
-
-    &.is-extrasmall {
-      width: $icon-size-xs !important;
-      height: $icon-size-xs !important;
-    }
-
-    &.is-small {
-      width: $icon-size-sm !important;
-      height: $icon-size-sm !important;
-    }
-
-    &.is-medium {
-      width: $icon-size-md !important;
-      height: $icon-size-md !important;
-    }
-
-    &.is-large {
-      width: $icon-size-lg !important;
-      height: $icon-size-lg !important;
-    }
-  }
-}
-
-@mixin icons-theme {
-  .d-icon {
-    font-size: $icon-size-default;
-    &.is-extrasmall {
-      font-size: $icon-size-xs !important;
-    }
-    &.is-small {
-      font-size: $icon-size-sm !important;
-    }
-    &.is-medium {
-      font-size: $icon-size-md !important;
-    }
-    &.is-large {
-      font-size: $icon-size-lg !important;
-    }
-  }
-
-  svg.d-icon {
-    width: $icon-size-default;
-    height: $icon-size-default;
-
-    &.is-extrasmall {
-      width: $icon-size-xs !important;
-      height: $icon-size-xs !important;
-    }
-
-    &.is-small {
-      width: $icon-size-sm !important;
-      height: $icon-size-sm !important;
-    }
-
-    &.is-medium {
-      width: $icon-size-md !important;
-      height: $icon-size-md !important;
-    }
-
-    &.is-large {
-      width: $icon-size-lg !important;
-      height: $icon-size-lg !important;
-    }
-  }
-}
diff --git a/_sass/denali-css/scss/components/input-group.scss b/_sass/denali-css/scss/components/input-group.scss
deleted file mode 100644
index 934733f758..0000000000
--- a/_sass/denali-css/scss/components/input-group.scss
+++ /dev/null
@@ -1,44 +0,0 @@
-@charset "utf-8";
-// Copyright 2019, Oath Inc.
-// Licensed under the terms of the MIT license. See LICENSE file in project root for terms.
-
-.input-group {
-  display: flex;
-  align-items: center;
-  label {
-    min-width: 110px;
-    font-size: 14px;
-    font-weight: bold;
-    align-self: center;
-    margin-right: 10px;
-  }
-  &.auto {
-    label {
-      min-width: auto;
-    }
-  }
-  &.is-stacked {
-    flex-direction: column;
-    align-items: flex-start;
-    label {
-      margin-top: 0px;
-      align-self: flex-start;
-      margin-bottom: 4px;
-    }
-  }
-
-  &.responsive {
-    @include media-size-down(mobile) {
-      flex-direction: column;
-      align-items: flex-start;
-      label {
-        margin-top: 0px;
-        align-self: flex-start;
-        margin-bottom: 4px;
-      }
-      > div {
-        width: 100%;
-      }
-    }
-  }
-}
diff --git a/_sass/denali-css/scss/components/inputs.scss b/_sass/denali-css/scss/components/inputs.scss
deleted file mode 100644
index c66b8a2976..0000000000
--- a/_sass/denali-css/scss/components/inputs.scss
+++ /dev/null
@@ -1,265 +0,0 @@
-@charset "utf-8";
-// Copyright 2019, Oath Inc.
-// Licensed under the terms of the MIT license. See LICENSE file in project root for terms.
-
-@mixin inputs-default {
-  .input {
-    position: relative;
-    z-index: 0;
-
-    input,
-    textarea {
-      margin: 0;
-      appearance: none;
-      border: none;
-      box-shadow: none;
-      flex: 1 0 auto;
-      font: inherit;
-      outline: none;
-      text-align: left;
-      padding: $fields-padding;
-      width: inherit;
-
-      &:active,
-      &:focus {
-        transition: 300ms;
-      }
-    }
-  }
-
-  .input {
-    width: $fields-sizes-default-width;
-
-    &.is-full {
-      width: 100% !important;
-    }
-
-    input {
-      height: $fields-sizes-default-height;
-    }
-
-    textarea {
-      padding-top: 6px;
-      max-width: 100%;
-      min-height: 90px;
-    }
-
-    input,
-    textarea {
-      background: $fields-default-bg;
-      border-radius: $fields-corner-radius;
-      color: $fields-default-text-color;
-      box-shadow: $fields-default-border;
-
-      &:active,
-      &:focus {
-        box-shadow: $fields-focus-border;
-      }
-
-      &::placeholder {
-        color: $fields-placeholder-text-color;
-      }
-
-      &[disabled] {
-        background: $fields-disabled-bg;
-        color: $fields-disabled-text-color;
-        box-shadow: $fields-disabled-border;
-        &::placeholder {
-          color: $fields-placeholder-text-color;
-        }
-      }
-    }
-
-    // States
-    &.is-active > input,
-    &.is-active > textarea {
-      box-shadow: $fields-focus-border;
-    }
-
-    &.is-disabled > input,
-    &.is-disabled > textarea {
-      background: $fields-disabled-bg;
-      color: $fields-disabled-text-color;
-      box-shadow: $fields-disabled-border;
-    }
-
-    &.is-error > input,
-    input:invalid,
-    &.is-error > textarea,
-    textarea:invalid {
-      box-shadow: $fields-error-border;
-    }
-
-    // Sizes
-    &.is-large > input{
-      height: $fields-sizes-lg-height;
-    }
-    &.is-medium > input{
-      height: $fields-sizes-md-height;
-    }
-    &.is-small > input{
-      height: $fields-sizes-sm-height;
-    }
-
-    // Dark State
-    &.is-inverse {
-      input {
-        background: rgba(138, 197, 237, 0.10);
-        color: $color-grey-100;
-      }
-      input:active {
-        border-bottom: 2px solid rgba($color-brand-400, 1);
-        color: $color-grey-100;
-      }
-      input::placeholder {
-        color: rgba($color-grey-100, 0.5);
-      }
-      label {
-        color: $color-grey-100;
-      }
-    }
-
-    // Message under input
-    .message {
-      width: inherit;
-      font-size: 11px !important;
-      font-weight: normal;
-      line-height: 1.6rem;
-      position: absolute;
-      margin-top: 2px;
-    }
-    &.is-error > .message {
-      color: $color-status-danger;
-    }
-  }
-
-  // Input with icon
-  .input.has-icon-back {
-    input {
-      padding-right: 34px;
-    }
-    .d-icon {
-      font-size: 1.8rem !important;
-      position: relative;
-      margin-top: 0;
-      margin-left: 0;
-      pointer-events: none;
-      &::before {
-        position: absolute;
-        top: -1px;
-        right: 10px;
-      }
-    }
-  }
-
-  
-  .input.has-icon-front {
-    input {
-      padding-left: 34px;
-    }
-    .d-icon {
-      font-size: 1.8rem !important;
-      margin-top: 9px;
-      position: absolute;
-      margin-left: 10px;
-    }
-  }
-
-  // Input with button
-  .input-group.has-button {
-    input {
-      border-top-right-radius: 0px;
-      border-bottom-right-radius: 0px;
-    }
-    button.button {
-      margin: 0px;
-      border-top-left-radius: 0px;
-      border-bottom-left-radius: 0px;
-    }
-  }
-
-  // Input with dropdown
-  .input-group.has-dropdown {
-    .input {
-      min-width: 0px;
-      width: auto;
-      input {
-        min-width: 250px;
-        border-radius: 0px;
-      }
-      select {
-        width: inherit;
-        padding-right: 34px;
-        border-top-right-radius: 0px;
-        border-bottom-right-radius: 0px;
-      }
-    }
-  }
-}
-
-@mixin inputs-theme {
-  .input {
-    width: $fields-sizes-default-width;
-
-    input {
-      height: $fields-sizes-default-height;
-    }
-
-    input,
-    textarea {
-      background: $fields-default-bg;
-      border-radius: $fields-corner-radius;
-      color: $fields-default-text-color;
-      box-shadow: $fields-default-border;
-      padding: $fields-padding;
-
-      &:active,
-      &:focus {
-        box-shadow: $fields-focus-border;
-      }
-
-      &::placeholder {
-        color: $fields-placeholder-text-color;
-      }
-
-      &[disabled] {
-        background: $fields-disabled-bg;
-        color: $fields-disabled-text-color;
-        box-shadow: $fields-disabled-border;
-
-        &::placeholder {
-          color: $fields-placeholder-text-color;
-        }
-      }
-    }
-
-    // States
-    &.is-active>input,
-    &.is-active>textarea {
-      box-shadow: $fields-focus-border;
-    }
-
-    &.is-disabled>input,
-    &.is-disabled>textarea {
-      background: $fields-disabled-bg;
-      color: $fields-disabled-text-color;
-      box-shadow: $fields-disabled-border;
-    }
-
-    &.is-error>input,
-    input:invalid,
-    &.is-error>textarea,
-    textarea:invalid {
-      box-shadow: $fields-error-border;
-    }
-
-    // Sizes
-    &.is-medium>input {
-      height: $fields-sizes-medium-height;
-    }
-
-    &.is-small>input {
-      height: $fields-sizes-small-height;
-    }
-  }
-}
\ No newline at end of file
diff --git a/_sass/denali-css/scss/components/links.scss b/_sass/denali-css/scss/components/links.scss
deleted file mode 100644
index b7312ed8d0..0000000000
--- a/_sass/denali-css/scss/components/links.scss
+++ /dev/null
@@ -1,130 +0,0 @@
-@charset "utf-8";
-// Copyright 2019, Oath Inc.
-// Licensed under the terms of the MIT license. See LICENSE file in project root for terms.
-
-@mixin links-default {
-  a,
-  .link {
-    // Standard
-    color: $links-text-color;
-    text-decoration: none;
-    font-size: inherit;
-    cursor: pointer;
-
-    // Hover
-    &:hover,
-    &.is-active {
-      color: $links-hover-text-color;
-    }
-
-    // Disabled
-    &[disabled],
-    &.is-disabled {
-      color: $links-disabled-text-color;
-      cursor: not-allowed;
-    }
-
-    // Inverse
-    &.is-inverse {
-      color: $links-inverse-text-color;
-      &:hover {
-        color: $links-inverse-hover-text-color;
-      }
-    }
-
-    // Size
-    &.is-small {
-      font-size: $links-small-text-size;
-    }
-
-    // Secondary Links
-    &.is-secondary {
-      color: $links-secondary-text-color;
-      &:hover {
-        color: $links-secondary-hover-text-color !important;
-      }
-    }
-
-    // Sub-action
-    &.is-sub,
-    .is-sub {
-      opacity: .5;
-      &:hover {
-        opacity: 1;
-      }
-    }
-
-
-    // Icons
-    &.has-icon-front {
-      .d-icon {
-        margin-right: 4px;
-        font-size: 1.4rem !important;
-        vertical-align: sub;
-      }
-    }
-    &.has-icon-back {
-      .d-icon {
-        margin-left: 2px;
-        font-size: 1.2rem !important;
-        vertical-align: middle;
-      }
-      &.is-small .d-icon {
-        vertical-align: text-top;
-      }
-    }
-    &:hover > .d-icon.is-sub {
-      color: $links-hover-text-color;
-      opacity: 1;
-    }
-    &.is-secondary:hover > .d-icon.is-sub {
-      color: $links-secondary-hover-text-color;
-      opacity: 1;
-    }
-  }
-}
-
-@mixin links-theme {
-  a,
-  .link {
-    color: $links-text-color;
-
-    &:hover,
-    &.is-active {
-      color: $links-hover-text-color;
-    }
-
-    &[disabled],
-    &.is-disabled {
-      color: $links-disabled-text-color;
-    }
-
-    &.is-inverse {
-      color: $links-inverse-text-color;
-
-      &:hover {
-        color: $links-inverse-hover-text-color;
-      }
-    }
-
-    &.is-small {
-      font-size: $links-small-text-size;
-    }
-
-    &.is-secondary {
-      color: $links-secondary-text-color;
-
-      &:hover {
-        color: $links-secondary-hover-text-color !important;
-      }
-    }
-
-    &:hover>.d-icon.is-sub {
-      color: $links-hover-text-color;
-    }
-
-    &.is-secondary:hover>.d-icon.is-sub {
-      color: $links-secondary-hover-text-color;
-    }
-  }
-}
\ No newline at end of file
diff --git a/_sass/denali-css/scss/components/lists.scss b/_sass/denali-css/scss/components/lists.scss
deleted file mode 100644
index 21974c0522..0000000000
--- a/_sass/denali-css/scss/components/lists.scss
+++ /dev/null
@@ -1,19 +0,0 @@
-@charset "utf-8";
-// Copyright 2019, Oath Inc.
-// Licensed under the terms of the MIT license. See LICENSE file in project root for terms.
-
-@mixin lists-default {
-    ul.list {
-        list-style-position: inside;
-        list-style-type: square;
-    }
-    ol.list {
-        list-style-position: inside;
-    }
-    ol.list,
-    ul.list {
-        li {
-            margin: 4px 0px;
-        }
-    }
-}
\ No newline at end of file
diff --git a/_sass/denali-css/scss/components/loader.scss b/_sass/denali-css/scss/components/loader.scss
deleted file mode 100644
index 0727dd388c..0000000000
--- a/_sass/denali-css/scss/components/loader.scss
+++ /dev/null
@@ -1,177 +0,0 @@
-@charset "utf-8";
-// Copyright 2019, Oath Inc.
-// Licensed under the terms of the MIT license. See LICENSE file in project root for terms.
-
-@mixin loader-default {
-  .loader {
-    position: relative;
-    width: 10px;
-    height: 10px;
-    border-radius: 50%;
-    animation-name: loaderDefault;
-    animation-iteration-count: infinite;
-    animation-direction: alternate;
-    animation-timing-function: ease;
-    animation-duration: .9s;
-    animation-delay: 0.2s;
-    left: 14px;
-    top: -10px;
-    margin: 0px;
-
-    &:before,
-    &:after {
-      content: '';
-      position: absolute;
-      width: 10px;
-      height: 10px;
-      border-radius: 50%;
-      animation-name: loaderDefault;
-      animation-iteration-count: infinite;
-      animation-direction: alternate;
-      animation-timing-function: ease;
-      animation-duration: .9s;
-    }
-
-    &:before {
-      left: 14px;
-      animation-delay: 0.3s;
-    }
-
-    &:after {
-      right: 14px;
-      animation-delay: 0.1s;
-    }
-
-    &--inverse,
-    &.is-inverse {
-      animation-name: loaderInverse;
-
-      &:before,
-      &:after {
-        animation-name: loaderInverse;
-      }
-    }
-
-    &--button {
-      left: 0px;
-    }
-  }
-
-  .button {
-    &.is-solid,
-    &.is-danger {
-      .loader {
-        animation-name: loaderInverse;
-
-        &:before,
-        &:after {
-          animation-name: loaderInverse;
-        }
-      }
-    }
-  }
-
-  @keyframes loaderDefault {
-    0% {
-      box-shadow: 0 10px 0 -10px $loader-color;
-      opacity: 0;
-    }
-
-    50% {
-      box-shadow: 0 10px 0 $loader-color;
-      opacity: .75;
-    }
-
-    100% {
-      box-shadow: 0 10px 0 $loader-color;
-      opacity: 1;
-    }
-  }
-
-  @keyframes loaderInverse {
-    0% {
-      box-shadow: 0 10px 0 -10px $loader-color-inverse;
-      opacity: 0;
-    }
-
-    50% {
-      box-shadow: 0 10px 0 $loader-color-inverse;
-      opacity: .75;
-    }
-
-    100% {
-      box-shadow: 0 10px 0 $loader-color-inverse;
-      opacity: 1;
-    }
-  }
-}
-
-@mixin loader-theme {
-  .loader {
-    animation-name: loaderThemeDefault;
-
-    &:before,
-    &:after {
-      animation-name: loaderThemeDefault;
-    }
-
-    &--inverse,
-    &.is-inverse {
-      animation-name: loaderThemeInverse;
-
-      &:before,
-      &:after {
-        animation-name: loaderThemeInverse;
-      }
-    }
-  }
-
-  .button {
-    &.is-solid,
-    &.is-danger {
-      .loader {
-        animation-name: loaderThemeInverse;
-
-        &:before,
-        &:after {
-          animation-name: loaderThemeInverse;
-        }
-      }
-    }
-  }
-
-
-  @keyframes loaderThemeDefault {
-    0% {
-      box-shadow: 0 10px 0 -10px $loader-color;
-      opacity: 0;
-    }
-
-    50% {
-      box-shadow: 0 10px 0 $loader-color;
-      opacity: .75;
-    }
-
-    100% {
-      box-shadow: 0 10px 0 $loader-color;
-      opacity: 1;
-    }
-  }
-
-  @keyframes loaderThemeInverse {
-    0% {
-      box-shadow: 0 10px 0 -10px $loader-color-inverse;
-      opacity: 0;
-    }
-
-    50% {
-      box-shadow: 0 10px 0 $loader-color-inverse;
-      opacity: .75;
-    }
-
-    100% {
-      box-shadow: 0 10px 0 $loader-color-inverse;
-      opacity: 1;
-    }
-  }
-}
\ No newline at end of file
diff --git a/_sass/denali-css/scss/components/menus.scss b/_sass/denali-css/scss/components/menus.scss
deleted file mode 100644
index 861a779ffe..0000000000
--- a/_sass/denali-css/scss/components/menus.scss
+++ /dev/null
@@ -1,89 +0,0 @@
-@charset "utf-8";
-// Copyright 2019, Oath Inc.
-// Licensed under the terms of the MIT license. See LICENSE file in project root for terms.
-
-@mixin menus-default {
-  .menu {
-    white-space: nowrap;
-    position: relative;
-
-    &.is-right {
-      .menu-content {
-        left: 100%;
-        transform: translateX(-100%);
-      }
-    }
-
-    &.is-left {
-      .menu-content {
-        left: 0%;
-        transform: translateX(0%);
-      }
-    }
-
-    .menu-content {
-      height: auto;
-      opacity: 0;
-      background: $menu-bg-color;
-      font-size: 14px;
-      position: absolute;
-      padding: 15px 20px;
-      border-radius: 8px;
-      visibility: hidden;
-      box-shadow: $menu-shadow;
-      z-index: 100;
-      left: 50%;
-      transform: translateX(-50%);
-
-      // Added
-      border-color: $menu-border-color;
-      border-width: $menu-border-width;
-      border-style: $menu-border-style;
-      border-radius: $menu-corner-radius;
-      -webkit-transition: all 0.3s 0.1s;
-      -moz-transition: all 0.3s 0.1s;
-      -o-transition: all 0.3s 0.1s;
-      transition: all 0.3s 0.1s;
-
-      >* {
-        display: block;
-        white-space: nowrap;
-        padding: 5px 0px;
-        height: unset;
-      }
-
-      hr {
-        background-color: $menu-line-break-color;
-        height: $menu-line-break-size;
-        width: calc(100% + 40px);
-        margin-left: -20px;
-        padding: 0px;
-      }
-    }
-
-    .menu-trigger:hover+.menu-content,
-    .menu-trigger:focus+.menu-content,
-    .menu-content:hover {
-      opacity: 1;
-      visibility: visible;
-    }
-  }
-}
-
-@mixin menus-theme {
-  .menu {
-    .menu-content {
-      background: $menu-bg-color;
-      box-shadow: $menu-shadow;
-      border-color: $menu-border-color;
-      border-width: $menu-border-width;
-      border-style: $menu-border-style;
-      border-radius: $menu-corner-radius;
-
-      hr {
-        background-color: $menu-line-break-color;
-        height: $menu-line-break-size;
-      }
-    }
-  }
-}
\ No newline at end of file
diff --git a/_sass/denali-css/scss/components/modals.scss b/_sass/denali-css/scss/components/modals.scss
deleted file mode 100644
index 6f37fccddd..0000000000
--- a/_sass/denali-css/scss/components/modals.scss
+++ /dev/null
@@ -1,149 +0,0 @@
-@charset "utf-8";
-// Copyright 2019, Oath Inc.
-// Licensed under the terms of the MIT license. See LICENSE file in project root for terms.
-
-@mixin modals-default {
-  .modal {
-    position: fixed;
-    top: 0;
-    right: 0;
-    bottom: 0;
-    left: 0;
-    z-index: $zindex-modal-background;
-    opacity: 0;
-    -webkit-transition: opacity 300ms ease-in;
-    -moz-transition: opacity 300ms ease-in;
-    transition: opacity 300ms ease-in;
-    pointer-events: none;
-    background: $modal-overlay-color;
-    overflow: auto;
-    display: flex;
-    justify-content: center;
-    align-items: center;
-
-    &.is-chas {
-      opacity: 1;
-      pointer-events: auto;
-    }
-
-    div.modal-container {
-      width: 450px;
-      margin: 20px;
-      position: relative;
-      background: $modal-modal-container-bg-color;
-      box-shadow: 0px 4px 8px rgba(0, 0, 0, 0.16);
-      border-width: $modal-modal-container-border-width;
-      border-style: $modal-modal-container-border-style;
-      border-color: $modal-modal-container-border-color;
-      border-radius: $modal-corner-radius;
-      display: flex;
-      flex-direction: column;
-
-      .close {
-        color: $modal-modal-container-close-icon-color;
-        opacity: .4;
-        line-height: 14px;
-        position: absolute;
-        z-index: $zindex-modal;
-        right: 14px;
-        top: 14px;
-      }
-
-      .close:hover {
-        color: $modal-modal-container-close-icon-color;
-        opacity: .6;
-      }
-
-      div.modal-content {
-        padding: 24px 30px 24px 30px;
-      }
-
-      div.modal-header {
-        border-bottom-width: $modal-modal-container-border-width;
-        border-bottom-style: $modal-modal-container-border-style;
-        border-bottom-color: $modal-modal-container-border-color;
-        display: flex;
-        align-items: center;
-        padding: 15px 58px 13px 30px;
-        width: 100%;
-      }
-
-      div.modal-footer {
-        border-top-width: $modal-modal-container-border-width;
-        border-top-style: $modal-modal-container-border-style;
-        border-top-color: $modal-modal-container-border-color;
-        margin-bottom:0px;
-        display: flex;
-        align-items: center;
-        padding: 20px 30px 20px 30px;
-        width: 100%;
-      }
-
-      &.is-full {
-        width: 100%;
-        height: 100%;
-        box-shadow: none;
-        border: none !important;
-        border-radius: 0px !important;
-        margin: 0px;
-        padding: 0px;
-
-        div.modal-content {
-          padding: 24px 58px 24px 58px;
-          height: 100%;
-          overflow-y: scroll;
-        }
-      }
-    }
-  }
-  
-  .modal:target,
-  .modal.is-active {
-    opacity:1;
-    pointer-events: auto;
-  }
-
-  @include media-size-down(tablet) {
-    .modal {
-      div.is-full {
-        div.modal-content {
-          padding: 24px 30px 24px 30px;
-        }
-      }
-    }
-  }
-}
-
-@mixin modals-theme {
-  .modal {
-    background: $modal-overlay-color;
-
-    div.modal-container {
-      background: $modal-modal-container-bg-color;
-      border-width: $modal-modal-container-border-width;
-      border-style: $modal-modal-container-border-style;
-      border-color: $modal-modal-container-border-color;
-      border-radius: $modal-corner-radius;
-
-      .close {
-        color: $modal-modal-container-close-icon-color;
-      }
-
-      .close:hover {
-        color: $modal-modal-container-close-icon-color;
-      }
-
-      div.modal-header {
-        border-bottom-width: $modal-modal-container-border-width;
-        border-bottom-style: $modal-modal-container-border-style;
-        border-bottom-color: $modal-modal-container-border-color;
-      }
-
-      div.modal-footer {
-        border-top-width: $modal-modal-container-border-width;
-        border-top-style: $modal-modal-container-border-style;
-        border-top-color: $modal-modal-container-border-color;
-      }
-    }
-  }
-}
\ No newline at end of file
diff --git a/_sass/denali-css/scss/components/navbar.scss b/_sass/denali-css/scss/components/navbar.scss
deleted file mode 100644
index 88a0e0b9da..0000000000
--- a/_sass/denali-css/scss/components/navbar.scss
+++ /dev/null
@@ -1,298 +0,0 @@
-@charset "utf-8";
-// Copyright 2019, Oath Inc.
-// Licensed under the terms of the MIT license. See LICENSE file in project root for terms.
-
-@mixin navbar-default {
-  .nav {
-    box-sizing: border-box;
-    height: $navbar-height;
-    width: 100%;
-    background: $navbar-bg-color;
-    position: relative;
-
-    &.is-fixed-top {
-      position: fixed;
-      top: 0;
-    }
-
-    .nav-center,
-    .nav-right {
-
-      .nav-item,
-      .nav-icon,
-      .menu--nav,
-      .nav-control {
-        display: block;
-      }
-
-      @media (min-width: 899px) {
-        display: inline-block;
-        background: transparent;
-
-        .nav-item,
-        .nav-icon,
-        .menu--nav,
-        .nav-control {
-          display: inline-block;
-        }
-      }
-    }
-
-
-    // Left
-    .nav-left {
-      padding-left: 12px;
-      padding-right: 12px;
-      display: inline-block;
-      vertical-align: top;
-      height: $navbar-height;
-      width: 100%;
-
-      .nav-item,
-      .nav-icon,
-      .menu--nav,
-      .nav-control {
-        height: $navbar-height;
-        padding: 18px 8px;
-
-        @media (min-width: 899px) {
-          padding: 18px 12px;
-        }
-      }
-
-      @media (min-width: 899px) {
-        width: 300px;
-        padding-left: 18px;
-        padding-right: 0px;
-      }
-    }
-
-    // Center
-    .nav-center {
-      @media (min-width: 899px) {
-        // float: left;
-      }
-    }
-
-    // Right
-    .nav-right {
-      padding-right: 0px;
-
-      @media (min-width: 899px) {
-        float: right;
-        padding-right: 18px;
-      }
-    }
-
-    .nav-responsive {
-      position: absolute;
-      left: 0;
-      right: 0;
-      height: calc(100vh - 60px);
-      background: $navbar-responsive-bg-color;
-      opacity: 0;
-      visibility: hidden;
-      overflow: auto;
-      
-      &.is-active {
-        opacity: 1;
-        visibility: visible;
-      }
-
-      @media (min-width: 899px) {
-        float: right;
-        width: calc(100% - 300px);
-        opacity: 1;
-        visibility: visible;
-        height: $navbar-height;
-        background: transparent !important;
-        position: relative;
-        overflow: inherit;
-      }
-    }
-
-    // All Items
-    .nav-item,
-    .nav-icon,
-    .menu--nav,
-    .nav-control {
-      display: inline-block;
-      vertical-align: top;
-      height: 54px;
-      padding: 16px 20px;
-      position: relative;
-
-      @media (min-width: 899px) {
-        height: $navbar-height;
-        padding: 18px 12px;
-      }
-    }
-
-    // Brand
-    .nav-brand {
-      width: auto;
-      padding: 13px 12px;
-      height: $navbar-height;
-    }
-
-    // Item & Icon (Text)
-    .nav-item,
-    .nav-icon {
-      color: $navbar-item-text-color;
-      font-size: 1.6rem;
-
-      .icon-name {
-        margin-left: 10px;
-
-        @media (min-width: 899px) {
-          display: none;
-        }
-      }
-
-      &.is-active {
-        box-shadow: inset 4px 0 0 $navbar-item-active-border-color;
-
-        @media (min-width: 899px) {
-          box-shadow: inset 0 -4px 0 $navbar-item-active-border-color;
-        }
-      }
-
-      &:hover,
-      &:focus {
-        color: $navbar-item-hover-text-color;
-      }
-    }
-
-    // Menus
-    .menu.menu--nav {
-      color: $navbar-item-text-color;
-      padding: 0px !important;
-      height: auto;
-
-      .menu-content {
-        margin-top: -8px;
-      }
-
-      @media (max-width: 898px) {
-        position: unset;
-
-        .menu-content {
-          height: 0px;
-          overflow: hidden;
-          padding: 0px;
-          border-radius: 0px;
-          border: none;
-          box-shadow: none;
-          width: 100%;
-          margin-top: 0px;
-          background-color: transparent;
-
-          > * {
-            display: block;
-            height: 40px;
-            background: $navbar-responsive-menu-bg-color;
-            padding: 10px 20px;
-            color: $navbar-item-text-color;
-
-            &:hover,
-            &:focus {
-              color: $navbar-item-active-text-color;
-            }
-          }
-
-          &:before,
-          &:after {
-            display: none;
-          }
-        }
-
-        .menu-trigger:hover+.menu-content,
-        .menu-trigger:focus+.menu-content,
-        .menu-content:hover {
-          height: auto;
-        }
-      }
-    }
-
-    // Nav Control
-    .nav-control {
-      padding: 9px 12px;
-
-      > * {
-        width: 100%;
-      }
-
-      @media (min-width: 899px) {
-        padding: 12px 12px;
-      }
-    }
-  }
-}
-
-@mixin navbar-theme {
-  .nav {
-    height: $navbar-height;
-    background: $navbar-bg-color;
-    // Left
-    .nav-left {
-      height: $navbar-height;
-      .nav-item,
-      .nav-icon,
-      .menu--nav,
-      .nav-control {
-        height: $navbar-height;
-      }
-    }
-    .nav-responsive {
-      background: $navbar-responsive-bg-color;
-      @media (min-width: 899px) {
-        height: $navbar-height;
-      }
-    }
-    // All Items
-    .nav-item,
-    .nav-icon,
-    .menu--nav,
-    .nav-control {
-      @media (min-width: 899px) {
-        height: $navbar-height;
-      }
-    }
-    // Brand
-    .nav-brand {
-      height: $navbar-height;
-    }
-    // Item & Icon (Text)
-    .nav-item,
-    .nav-icon {
-      color: $navbar-item-text-color;
-      &.is-active {
-        box-shadow: inset 4px 0 0 $navbar-item-active-border-color;
-        @media (min-width: 899px) {
-          box-shadow: inset 0 -4px 0 $navbar-item-active-border-color;
-        }
-      }
-      &:hover,
-      &:focus {
-        color: $navbar-item-hover-text-color;
-      }
-    }
-    // Menus
-    .menu.menu--nav {
-      color: $navbar-item-text-color;
-      @media (max-width: 898px) {
-        .menu-content {
-          >* {
-            background: $navbar-responsive-menu-bg-color;
-            color: $navbar-item-text-color;
-
-            &:hover,
-            &:focus {
-              color: $navbar-item-active-text-color;
-            }
-          }
-        }
-      }
-    }
-  }
-}
\ No newline at end of file
diff --git a/_sass/denali-css/scss/components/progress.scss b/_sass/denali-css/scss/components/progress.scss
deleted file mode 100644
index 8fdae9a7bf..0000000000
--- a/_sass/denali-css/scss/components/progress.scss
+++ /dev/null
@@ -1,125 +0,0 @@
-@charset "utf-8";
-// Copyright 2019, Oath Inc.
-// Licensed under the terms of the MIT license. See LICENSE file in project root for terms.
-
-@mixin progress-default {
-    .d-progress {
-        height: $progress-sizes-default-height;
-        width: 100%;
-        background: $progress-bg-color;
-        border-radius: $progress-corner-radius;
-        display: flex;
-        overflow: hidden;
-
-        .d-progress--bar {
-            height: inherit;
-            background: $progress-bar-bg-color;
-            margin-left: $progress-bar-spacer;
-
-            &:first-child {
-                margin-left: 0px !important;
-            }
-        }
-
-        // Size
-        &__sm {
-            height: $progress-sizes-sm-height;
-        }
-
-        &__md {
-            height: $progress-sizes-md-height;
-        }
-
-        &__lg {
-            height: $progress-sizes-lg-height;
-        }
-
-        // Loading
-        &__loading {
-            animation-duration: 1s;
-            animation-fill-mode: forwards;
-            animation-iteration-count: infinite;
-            animation-name: loadingShimmer;
-            animation-timing-function: linear;
-            background-image: linear-gradient(to right, $progress-bg-color 0%, lighten($progress-bg-color, 6%) 50%, $progress-bg-color 100%);
-            background-size: 50%;
-            background-repeat: no-repeat;
-            position: relative;
-        }
-    }
-
-    @keyframes loadingShimmer{
-        0%{
-            background-position: -100%;
-        }
-        100%{
-            background-position: 200%;
-        }
-    }
-}
-
-@mixin progress-theme {
-    .d-progress {
-        @if variable-exists("progress-sizes-default-height") {
-            height: $progress-sizes-default-height;
-        }
-        @if variable-exists("progress-bg-color") {
-            background: $progress-bg-color;
-        }
-        @if variable-exists("progress-corner-radius") {
-            border-radius: $progress-corner-radius;
-        }
-        
-        .d-progress--bar {
-            @if variable-exists("progress-bar-bg-color") {
-                background: $progress-bar-bg-color;
-            }
-            @if variable-exists("progress-bar-spacer") {
-                margin-left: $progress-bar-spacer;
-            }
-        }
-
-        // Size
-        &__sm {
-            @if variable-exists("progress-sizes-sm-height") {
-                height: $progress-sizes-sm-height;
-            }
-        }
-
-        &__md {
-            @if variable-exists("progress-sizes-md-height") {
-                height: $progress-sizes-md-height;
-            }
-        }
-
-        &__lg {
-            @if variable-exists("progress-sizes-lg-height") {
-                height: $progress-sizes-lg-height;
-            }
-        }
-
-        // Loading
-        &__loading {
-            @if variable-exists("progress-bg-color") {
-                animation-duration: 1s;
-                animation-fill-mode: forwards;
-                animation-iteration-count: infinite;
-                animation-name: loadingShimmer;
-                animation-timing-function: linear;
-                background-image: linear-gradient(to right, $progress-bg-color 0%, lighten($progress-bg-color, 6%) 50%, $progress-bg-color 100%);
-                background-size: 50%;
-                background-repeat: no-repeat;
-                position: relative;
-            }
-        }
-    }
-
-    @keyframes loadingShimmer{
-        0%{
-            background-position: -100%;
-        }
-        100%{
-            background-position: 200%;
-        }
-    }
-}
\ No newline at end of file
diff --git a/_sass/denali-css/scss/components/radio.scss b/_sass/denali-css/scss/components/radio.scss
deleted file mode 100644
index 78c972aab7..0000000000
--- a/_sass/denali-css/scss/components/radio.scss
+++ /dev/null
@@ -1,107 +0,0 @@
-@charset "utf-8";
-// Copyright 2019, Oath Inc.
-// Licensed under the terms of the MIT license. See LICENSE file in project root for terms.
-
-@mixin radio-default {
-  .radio {
-    backface-visibility: hidden;
-    box-sizing: border-box;
-    display: inline-block;
-    outline: 0;
-    position: relative;
-    height: 20px;
-    margin-right: 16px;
-  }
-
-  .radio > input[type="radio"] {
-    height: 20px;
-    box-sizing: border-box;
-    opacity: 0;
-    position: absolute; 
-  }
-
-  .radio > label {
-    box-sizing: border-box;
-    cursor: pointer;
-    padding: 0;
-    position: relative;
-    color: $radio-label-text-color;
-    font-size: 14px;
-    font-family: helvetica, sans-serif;
-    font-weight: normal;
-    padding-left: 2px;
-  }
-
-  .radio > label:before {
-    border-radius: 50%;
-    box-shadow: $radio-circle-border-standard;
-    box-sizing: border-box;
-    content: "";
-    display: inline-block;
-    height: 16px;
-    margin-right: 10px;
-    transition: 0.2s;
-    vertical-align: text-top;
-    width: 16px;
-  }
-
-  .radio > input[type="radio"]:hover + label:before {
-    box-shadow: $radio-circle-border-hover;
-  }
-
-  .radio > input[type="radio"]:checked + label:before {
-    box-shadow: $radio-circle-border-active;
-  }
-
-  .radio > input[type="radio"]:disabled + label {
-    color: $radio-label-disabled-text-color;
-    cursor: not-allowed;
-  }
-
-  .radio > input[type="radio"]:disabled + label:before {
-    box-shadow: $radio-circle-border-disabled;
-  }
-
-  .radio > input[type="radio"]:checked + label:after {
-    background-color: $radio-circle-bg-color-active;
-    border-radius: 50%;
-    box-sizing: border-box;
-    content: "";
-    height: 12px;
-    left: 4px;
-    position: absolute;
-    top: 2px;
-    transition: 0.2s;
-    width: 12px;
-  }
- }
-
- @mixin radio-theme {
-  .radio>label {
-    color: $radio-label-text-color;
-  }
-
-  .radio>label:before {
-    box-shadow: $radio-circle-border-standard;
-  }
-
-  .radio>input[type="radio"]:hover+label:before {
-    box-shadow: $radio-circle-border-hover;
-  }
-
-  .radio>input[type="radio"]:checked+label:before {
-    box-shadow: $radio-circle-border-active;
-  }
-
-  .radio>input[type="radio"]:disabled+label {
-    color: $radio-label-disabled-text-color;
-  }
-
-  .radio>input[type="radio"]:disabled+label:before {
-    box-shadow: $radio-circle-border-disabled;
-  }
-
-  .radio>input[type="radio"]:checked+label:after {
-    background-color: $radio-circle-bg-color-active;
-  }
- }
\ No newline at end of file
diff --git a/_sass/denali-css/scss/components/selects.scss b/_sass/denali-css/scss/components/selects.scss
deleted file mode 100644
index 2b4f2897cc..0000000000
--- a/_sass/denali-css/scss/components/selects.scss
+++ /dev/null
@@ -1,175 +0,0 @@
-@charset "utf-8";
-// Copyright 2019, Oath Inc.
-// Licensed under the terms of the MIT license. See LICENSE file in project root for terms.
-
-@mixin selects-default {
-  .input {
-    &.has-arrow:before {
-      content: url('data:image/svg+xml;charset=UTF-8, ');
-      pointer-events: none;
-      opacity: .4;
-      position: absolute;
-      top: 50%;
-      transform: translateY(-50%);
-      right: 10px;
-      height: 18px;
-      width: 18px;
-    }
-
-    // Select dropdown
-    select {
-      background: $fields-default-bg;
-      border-radius: $fields-corner-radius;
-      color: $fields-default-text-color;
-      height: $fields-sizes-default-height;
-      box-shadow: $fields-default-border;
-      appearance: none;
-      -webkit-appearance: none;
-      flex: 1 0 auto;
-      font: inherit;
-      outline: none;
-      border: none;
-      text-align: left;
-      width: inherit;
-      margin: 0;
-      position: relative;
-      padding: $fields-padding;
-
-      option {
-        color: black;
-      }
-
-      &:active,
-      &:focus,
-      &[disabled] {
-        transition: 300ms;
-      }
-
-      &:invalid {
-        box-shadow: inset 0 -2px 0 $color-status-danger;
-      }
-
-      &::placeholder {
-        color: $fields-placeholder-text-color;
-      }
-
-      &:active,
-      &:focus {
-        box-shadow: $fields-focus-border;
-      }
-
-      &[disabled] {
-        background: $fields-disabled-bg;
-        box-shadow: $fields-disabled-border;
-      }
-    }
-
-    // Forced Active State
-    &.is-active > select {
-      box-shadow: $fields-focus-border;
-    }
-
-    // Error state
-    &.is-error > select {
-      box-shadow: $fields-error-border;
-    }
-
-    // Inverse
-    &.is-inverse { 
-      &.has-arrow:before {
-        content: url('data:image/svg+xml;charset=UTF-8, ');
-      }
-
-      select {
-        background: rgba(242, 242, 242, 0.1);
-        color: $color-grey-100;
-
-        &::placeholder {
-          color: rgba($color-grey-100, 0.6);
-        }
-
-        &:active,
-        &:focus {
-          background: rgba(242, 242, 242, 0.1);
-          box-shadow: inset 0 -2px 0 $color-brand-400;
-        }
-
-        &[disabled] {
-          background: rgba($color-grey-100, 0.1);
-          box-shadow: inset 0 -2px 0 rgba($color-grey-100, 0.05);
-          color: rgba($color-grey-100, 0.1);
-
-          &::placeholder {
-            color: rgba($color-grey-100, 0.2);
-          }
-        }
-      }
-    }
-
-    // Sizes
-    &.is-large > select {
-      height: $fields-sizes-lg-height;
-      // margin-top: 6px;
-    }
-    
-    &.is-medium > select {
-      height: $fields-sizes-md-height;
-      // margin-top: 6px;
-    }
-
-    &.is-small > select {
-      height: $fields-sizes-sm-height;
-      // margin-top: 5px;
-    }
-
-    // Full Width
-    &.is-full {
-      width: 100%;
-    }
-  }
-}
-
-@mixin selects-theme {
-    .input {
-
-      &.has-arrow:before {
-        content: url('data:image/svg+xml;charset=UTF-8, ');
-      }
-
-      // Select dropdown
-      select {
-        background: $fields-default-bg;
-        border-radius: $fields-corner-radius;
-        color: $fields-default-text-color;
-        height: $fields-sizes-default-height;
-        box-shadow: $fields-default-border;
-
-        &::placeholder {
-          color: $fields-placeholder-text-color;
-        }
-
-        &:active,
-        &:focus {
-          box-shadow: $fields-focus-border;
-        }
-
-        &[disabled] {
-          background: $fields-disabled-bg;
-          color: $fields-disabled-text-color;
-          box-shadow: $fields-disabled-border;
-        }
-      }
-      &.is-active>select {
-        box-shadow: $fields-focus-border;
-      }
-      &.is-error>select {
-        box-shadow: $fields-error-border;
-      }
-      &.is-medium>select {
-        height: $fields-sizes-medium-height;
-      }
-      &.is-small>select {
-        height: $fields-sizes-small-height;
-      }
-    }
-}
\ No newline at end of file
diff --git a/_sass/denali-css/scss/components/switch.scss b/_sass/denali-css/scss/components/switch.scss
deleted file mode 100644
index b8b5adb21f..0000000000
--- a/_sass/denali-css/scss/components/switch.scss
+++ /dev/null
@@ -1,179 +0,0 @@
-@charset "utf-8";
-// Copyright 2019, Oath Inc.
-// Licensed under the terms of the MIT license. See LICENSE file in project root for terms.
-
-@mixin switch-default {
-  label.switch {
-    box-sizing: border-box;
-    display: inline-flex;
-    height: 20px;
-    position: relative;
-    margin-top: 0px !important;
-    align-items: center;
-    margin-right: 16px;
-  }
-
-  label.switch > input[type="checkbox"] {
-    display: none;
-  }
-
-  /* Horizontal line */
-  label.switch > .slider {
-    background-color: $switch-slider-standard-color;
-    border-radius: 34px;
-    bottom: 0;
-    box-sizing: border-box;
-    cursor: pointer;
-    height: 4px;
-    left: 0;
-    position: relative;
-    right: 0;
-    transition: all 0.4s;
-    width: 34px;
-  }
-
-  /* Make it easier to click the empty space to the side of the Toggle circle */
-  label.switch > .slider:before {
-    background-color: transparent;
-    box-sizing: border-box;
-    content: " ";
-    height: 18px;
-    left: 0;
-    position: absolute;
-    top: -8px;
-    width: 34px;
-  }
-
-  /* Toggle circle */
-  label.switch > .slider:after {
-    background-color: $switch-circle-bg-color-standard;
-    bottom: -8px;
-    border: 2px solid;
-    border-color: $switch-circle-border-color-standard;
-    border-radius: 50%;
-    box-sizing: border-box;
-    content: "";
-    height: 20px;
-    left: 0;
-    position: absolute;
-    transition: all 0.4s;
-    width: 20px;
-  }
-
-  label.switch > input[type="checkbox"]:hover ~ .slider:after {
-    border-color: $switch-circle-border-color-hover;
-  }
-
-  label.switch > input[type="checkbox"]:checked ~ .slider {
-    background-color: $switch-slider-active-color;
-  }
-
-  label.switch > input[type="checkbox"]:checked ~ .slider:after {
-    background: $switch-circle-bg-color-active;
-    border-color: $switch-circle-border-color-active;
-    transform: translateX(16px);
-  }
-
-  label.switch.disabled > input[type="checkbox"] ~ .slider,
-  label.switch > input[type="checkbox"]:disabled ~ .slider,
-  label.switch > input[type="checkbox"]:checked:disabled ~ .slider {
-    background-color: $switch-slider-disabled-color;
-    cursor: not-allowed;
-  }
-
-  label.switch.disabled > input[type="checkbox"] ~ .slider:after,
-  label.switch > input[type="checkbox"]:disabled ~ .slider:after,
-  label.switch > input[type="checkbox"]:checked:disabled ~ .slider:after {
-    border-color: $switch-circle-border-color-disabled;
-    cursor: not-allowed;
-  }
-
-  label.switch.disabled > input[type="checkbox"]:checked ~ .slider:after,
-  label.switch > input[type="checkbox"]:checked:disabled ~ .slider:after {
-    background: $switch-circle-bg-color-disabled;
-  }
-
-  label.switch > .label {
-    color: $switch-label-text-color;
-    font-size: 14px;
-    font-family: helvetica, sans-serif;
-    font-weight: normal;
-    margin-left: 10px;
-    position: relative;
-    white-space: nowrap;
-  }
-
-  label.switch.disabled > .label,
-  label.switch > input[type="checkbox"]:disabled ~ .label,
-  label.switch > input[type="checkbox"]:checked:disabled ~ .label {
-    color: $switch-label-disabled-text-color;
-  }
-
-  label.switch > input[type="checkbox"]:checked ~ .off {
-    display: none;
-  }
-
-  label.switch > input[type="checkbox"]:not(:checked) ~ .off {
-    display: inline;
-  }
-
-  label.switch > input[type="checkbox"]:checked ~ .on {
-    display: inline;
-  }
-
-  label.switch > input[type="checkbox"]:not(:checked) ~ .on {
-    display: none;
-  }
-}
-
-@mixin switch-theme {
-  label.switch>.slider {
-    background-color: $switch-slider-standard-color;
-  }
-
-  label.switch>.slider:after {
-    background-color: $switch-circle-bg-color-standard;
-    border-color: $switch-circle-border-color-standard;
-  }
-
-  label.switch>input[type="checkbox"]:hover~.slider:after {
-    border-color: $switch-circle-border-color-hover;
-  }
-
-  label.switch>input[type="checkbox"]:checked~.slider {
-    background-color: $switch-slider-active-color;
-  }
-
-  label.switch>input[type="checkbox"]:checked~.slider:after {
-    background: $switch-circle-bg-color-active;
-    border-color: $switch-circle-border-color-active;
-  }
-
-  label.switch.disabled>input[type="checkbox"]~.slider,
-  label.switch>input[type="checkbox"]:disabled~.slider,
-  label.switch>input[type="checkbox"]:checked:disabled~.slider {
-    background-color: $switch-slider-disabled-color;
-  }
-
-  label.switch.disabled>input[type="checkbox"]~.slider:after,
-  label.switch>input[type="checkbox"]:disabled~.slider:after,
-  label.switch>input[type="checkbox"]:checked:disabled~.slider:after {
-    border-color: $switch-circle-border-color-disabled;
-  }
-
-  label.switch.disabled>input[type="checkbox"]:checked~.slider:after,
-  label.switch>input[type="checkbox"]:checked:disabled~.slider:after {
-    background: $switch-circle-bg-color-disabled;
-  }
-
-  label.switch>.label {
-    color: $switch-label-text-color;
-  }
-
-  label.switch.disabled>.label,
-  label.switch>input[type="checkbox"]:disabled~.label,
-  label.switch>input[type="checkbox"]:checked:disabled~.label {
-    color: $switch-label-disabled-text-color;
-  }
-
-}
\ No newline at end of file
diff --git a/_sass/denali-css/scss/components/table.scss b/_sass/denali-css/scss/components/table.scss
deleted file mode 100644
index f38743cf6c..0000000000
--- a/_sass/denali-css/scss/components/table.scss
+++ /dev/null
@@ -1,270 +0,0 @@
-@charset "utf-8";
-// Copyright 2019, Oath Inc.
-// Licensed under the terms of the MIT license. See LICENSE file in project root for terms.
-
-@mixin table-default {
-  .table-container {
-    width: 100%;
-    overflow: scroll;
-    border-radius: $table-border-radius;
-
-    &__freeze {
-      table.table tr th:first-child {
-        position: sticky;
-        left: 0;
-        box-shadow: $shadow-1;
-        max-width: fit-content;
-        background: $body-bg-color;
-      }
-    }
-  }
-
-  table.table {
-    width: 100%;
-    overflow: scroll;
-    position: relative;
-
-    th, td {
-      padding: $table-row-padding;
-      white-space: nowrap;
-
-      // Alignemnt
-      &.is-center {
-        text-align: center;
-      }
-      &.is-right {
-        text-align: right;
-      }
-    }
-
-    tbody {
-      th, td {
-        border-bottom: $table-row-border-bottom;
-        font-weight: normal;
-      }
-    }
-
-    // Header
-    thead {
-      tr {
-        height: 14px;
-        border-bottom: $table-header-border-bottom;
-      }
-      th {
-        text-transform: $table-header-text-transform;
-        font-weight: $table-header-text-weight;
-        color: $table-header-text-color;
-        font-size: $table-header-text-size;
-        padding: $table-header-padding;
-
-        &.is-sorted {
-          align-items: center;
-
-          &::after {
-            content: url('data:image/svg+xml;charset=UTF-8,  ');
-            width: 14px;
-            height: 14px;
-            margin-left: 4px;
-            display: inline-flex;
-            position: relative;
-            top: 2px;
-          }
-
-          &__ascend {
-            font-weight: $table-header-sort-text-weight;
-            &::after {
-              content: url('data:image/svg+xml;charset=UTF-8,  ');
-            }
-          }
-
-          &__descend {
-            font-weight: $table-header-sort-text-weight;
-            &::after {
-              content: url('data:image/svg+xml;charset=UTF-8,  ');
-            }
-          }
-        }
-      }
-    }
-
-    // Footer
-    tfoot {
-      th, td {
-        background: $table-footer-bg-color !important;
-        &:first-child {
-          border-radius: 0px 0px 0px $table-border-radius;
-        }
-        &:last-child {
-          border-radius: 0px 0px $table-border-radius 0px;
-        }
-      }
-    }
-
-    // Striped Table
-    &.is-striped tbody {
-      th, td {
-        border: none !important;
-      }
-
-      tr:nth-child(odd) {
-        th, td {
-          background: $table-striped-row-bg-color !important;
-        }
-      }
-    }
-    
-    // Cards Table
-    &.is-cards {
-      border-collapse: separate;
-      border-spacing: $table-cards-spacing;
-      
-      th, td {
-        &:first-child {
-          border-radius: $table-border-radius 0px 0px $table-border-radius !important;
-        }
-        &:last-child {
-          border-radius: 0px $table-border-radius $table-border-radius 0px !important;
-        }
-      }
-
-      thead th {
-        padding-bottom: 0px;
-        border: none;
-      }
-      
-      tbody {
-        th, td { 
-          border: $table-cards-border;
-          border-right: none;
-          border-left: none;
-          &:first-child {
-            border-left: $table-cards-border;
-          }
-          &:last-child {
-            border-right: $table-cards-border;
-          }
-        }
-      }
-    }
-
-    // Sizes
-    tr {
-      height: $table-row-default-height;
-    }
-    &__condensed tr {
-      height: $table-row-condensed-height;
-    }
-    &__expanded tr {
-      height: $table-row-expanded-height;
-    }
-  }
-}
-
-
-@mixin table-theme {
-  .table-container {
-    border-radius: $table-border-radius;
-
-    &__freeze {
-      table.table tr th:first-child {
-        box-shadow: $shadow-1;
-        background: $body-bg-color;
-      }
-    }
-  }
-
-  table.table {
-    th, td {
-      padding: $table-row-padding;
-    }
-    tbody {
-      th, td {
-        border-bottom: $table-row-border-bottom;
-      }
-    }
-
-    // Header
-    thead {
-      tr {
-        height: 14px;
-        border-bottom: $table-header-border-bottom;
-      }
-      th {
-        text-transform: $table-header-text-transform;
-        font-weight: $table-header-text-weight;
-        color: $table-header-text-color;
-        font-size: $table-header-text-size;
-        padding: $table-header-padding;
-
-        &.is-sorted {
-          &__ascend {
-            font-weight: $table-header-sort-text-weight;
-          }
-          &__descend {
-            font-weight: $table-header-sort-text-weight;
-          }
-        }
-      }
-    }
-
-    // Footer
-    tfoot {
-      th, td {
-        background: $table-footer-bg-color !important;
-        &:first-child {
-          border-radius: 0px 0px 0px $table-border-radius;
-        }
-        &:last-child {
-          border-radius: 0px 0px $table-border-radius 0px;
-        }
-      }
-    }
-
-    // Striped Table
-    &.is-striped tbody {
-      tr:nth-child(odd) {
-        th, td {
-          background: $table-striped-row-bg-color !important;
-        }
-      }
-    }
-    
-    // Cards Table
-    &.is-cards {
-      border-spacing: $table-cards-spacing;
-      th, td {
-        &:first-child {
-          border-radius: $table-border-radius 0px 0px $table-border-radius !important;
-        }
-        &:last-child {
-          border-radius: 0px $table-border-radius $table-border-radius 0px !important;
-        }
-      }
- 
-      tbody {
-        th, td { 
-          border: $table-cards-border;
-          border-right: none;
-          border-left: none;
-          &:first-child {
-            border-left: $table-cards-border;
-          }
-          &:last-child {
-            border-right: $table-cards-border;
-          }
-        }
-      }
-    }
-
-    // Sizes
-    tr {
-      height: $table-row-default-height;
-    }
-    &__condensed tr {
-      height: $table-row-condensed-height;
-    }
-    &__expanded tr {
-      height: $table-row-expanded-height;
-    }
-  }
-}
\ No newline at end of file
diff --git a/_sass/denali-css/scss/components/tabs.scss b/_sass/denali-css/scss/components/tabs.scss
deleted file mode 100644
index 2482ae2d85..0000000000
--- a/_sass/denali-css/scss/components/tabs.scss
+++ /dev/null
@@ -1,260 +0,0 @@
-@charset "utf-8";
-// Copyright 2019, Oath Inc.
-// Licensed under the terms of the MIT license. See LICENSE file in project root for terms.
-
-@mixin tabs-default {
-  .tabs {
-    -webkit-overflow-scrolling: touch;
-
-    // Primary
-    &.is-primary {
-      min-width: 100%;
-      background: $tabs-primary-bg-color;
-      ul {
-        list-style: none;
-        display: flex;
-        justify-content: flex-start;
-        padding: $tabs-primary-padding;
-        overflow-x: auto;
-        overflow-x: -moz-scrollbars-none;
-        &::-webkit-scrollbar {
-          width: 0 !important
-        }
-        li {
-          white-space: nowrap;
-          cursor: pointer;
-          display: flex;
-          justify-content: center;
-          align-items: center;
-          color: $tabs-primary-tab-item-text-color;
-          font-size: $tabs-font-size;
-          height: $tabs-primary-tab-item-height;
-          border-top-left-radius: $tabs-primary-tab-item-corner-radius;
-          border-top-right-radius: $tabs-primary-tab-item-corner-radius;
-          > * {
-            padding: $tabs-primary-tab-item-padding;
-          }
-          &.is-active {
-            background: $tabs-primary-tab-item-active-bg-color;
-            > * {
-              color: $tabs-primary-tab-item-active-text-color;
-            }
-          }
-          &.is-disabled > * {
-            color: $tabs-primary-tab-item-disabled-text-color;
-            cursor: not-allowed;
-          }
-        }
-      }
-
-      // Vertical Primary
-      &.is-vertical {
-        min-width: $tabs-primary-vertical-width;
-        width: $tabs-primary-vertical-width;
-        height: inherit;
-        overflow-y: auto;
-        ul {
-          flex-direction: column;
-          padding: $tabs-primary-vertical-padding;
-          li {
-            cursor: pointer;
-            justify-content: flex-start;
-            box-sizing: border-box;
-            height: $tabs-primary-vertical-tab-item-height;
-            margin: $tabs-primary-vertical-tab-item-margin;
-            border-radius: $tabs-primary-vertical-tab-item-corner-radius;
-            background: $tabs-primary-vertical-tab-item-bg-color;
-            box-shadow: $tabs-primary-vertical-tab-item-border;
-            > * {
-              color: $tabs-primary-vertical-tab-item-text-color;
-              padding: $tabs-primary-vertical-tab-item-padding;
-              height: inherit;
-              flex: auto;
-              display: flex;
-              align-items: center;
-            }
-            &:hover {
-              box-shadow: $tabs-primary-vertical-tab-item-hover-border;
-              background: $tabs-primary-vertical-tab-item-hover-bg-color;
-              font-weight: $tabs-primary-vertical-tab-item-hover-text-weight;
-              > * {
-                color: $tabs-primary-vertical-tab-item-hover-text-color;
-              }
-            }
-            &.is-active {
-              box-shadow: $tabs-primary-vertical-tab-item-active-border;
-              background: $tabs-primary-vertical-tab-item-active-bg-color;
-              font-weight: $tabs-primary-vertical-tab-item-active-text-weight;
-              > * {
-                color: $tabs-primary-vertical-tab-item-active-text-color;
-              }
-            }
-            // Section Header
-            &.tabs__section-header {
-              font-size: $tabs-section-header-text-size;
-              text-transform: $tabs-section-header-text-transform;
-              color: $tabs-section-header-text-color;
-              padding: $tabs-primary-vertical-tab-item-padding;
-        
-              &:hover {
-                box-shadow: none;
-              }
-            }
-          }
-        }
-      }
-    }
-
-    // Secondary
-    &.is-secondary {
-      min-width: 100%;
-      ul {
-        list-style: none;
-        display: flex;
-        justify-content: flex-start;
-        padding: 0px;
-        box-shadow: $tabs-secondary-border;
-        overflow-x: auto;
-        overflow-x: -moz-scrollbars-none;
-        &::-webkit-scrollbar {
-          width: 0 !important
-        }
-        li {
-          cursor: pointer;
-          white-space: nowrap;
-           >* {
-            display: flex;
-            justify-content: center;
-            align-items: center;
-            color: $tabs-secondary-tab-item-text-color;
-            font-size: $tabs-font-size;
-            height: $tabs-secondary-tab-item-height;
-            padding: $tabs-secondary-tab-item-padding;
-          }
-          &.is-active>* {
-            box-shadow: $tabs-secondary-tab-item-active-border;
-            color: $tabs-secondary-tab-item-active-text-color;
-          }
-        }
-      }
-    }
-
-    // Helpers
-    &.tablet-down-hide-left {
-      @include media-size-down(tablet) {
-        position: absolute;
-        left: -300px;
-        transition: left 300ms;
-      }
-    }
-    &.tablet-down-hide-right {
-      @include media-size-down(tablet) {
-        position: absolute;
-        right: -300px;
-        transition: right 300ms;
-      }
-    }
-    &.tablet-down-toggle-tabs-left {
-      @include media-size-down(tablet) {
-        position: absolute;
-        left: 0px;
-        margin: 0px;
-        box-shadow: 4px 0px 8px rgba(0, 0, 0, 0.16);
-        z-index: 99;
-      }
-    }
-  }
-}
-
-@mixin tabs-theme {
-  .tabs {
-    &.is-primary {
-      background: $tabs-primary-bg-color;
-      ul {
-        padding: $tabs-primary-padding;
-        li {
-          color: $tabs-primary-tab-item-text-color;
-          font-size: $tabs-font-size;
-          height: $tabs-primary-tab-item-height;
-          border-top-left-radius: $tabs-primary-tab-item-corner-radius;
-          border-top-right-radius: $tabs-primary-tab-item-corner-radius;
-          > * {
-            padding: $tabs-primary-tab-item-padding;
-          }
-          &.is-active {
-            background: $tabs-primary-tab-item-active-bg-color;
-            > * {
-              color: $tabs-primary-tab-item-active-text-color;
-            }
-          }
-          &.is-disabled > * {
-            color: $tabs-primary-tab-item-disabled-text-color;
-          }
-        }
-      }
-
-      // Vertical Primary
-      &.is-vertical {
-        min-width: $tabs-primary-vertical-width;
-        width: $tabs-primary-vertical-width;
-        ul {
-          padding: $tabs-primary-vertical-padding;
-          li {
-            height: $tabs-primary-vertical-tab-item-height;
-            margin: $tabs-primary-vertical-tab-item-margin;
-            border-radius: $tabs-primary-vertical-tab-item-corner-radius;
-            background: $tabs-primary-vertical-tab-item-bg-color;
-            box-shadow: $tabs-primary-vertical-tab-item-border;
-            > * {
-              color: $tabs-primary-vertical-tab-item-text-color;
-              padding: $tabs-primary-vertical-tab-item-padding;
-            }
-            &:hover {
-              box-shadow: $tabs-primary-vertical-tab-item-hover-border;
-              background: $tabs-primary-vertical-tab-item-hover-bg-color;
-              font-weight: $tabs-primary-vertical-tab-item-hover-text-weight;
-              > * {
-                color: $tabs-primary-vertical-tab-item-hover-text-color;
-              }
-            }
-            &.is-active {
-              box-shadow: $tabs-primary-vertical-tab-item-active-border;
-              background: $tabs-primary-vertical-tab-item-active-bg-color;
-              font-weight: $tabs-primary-vertical-tab-item-active-text-weight;
-              > * {
-                color: $tabs-primary-vertical-tab-item-active-text-color;
-              }
-            }
-            &.tabs__section-header {
-              font-size: $tabs-section-header-text-size;
-              text-transform: $tabs-section-header-text-transform;
-              color: $tabs-section-header-text-color;
-              padding: $tabs-primary-vertical-tab-item-padding;
-        
-              &:hover {
-                box-shadow: none !important;
-              }
-            }
-          }
-        }
-      }
-    }
-    &.is-secondary {
-      ul {
-        box-shadow: $tabs-secondary-border;
-        li {
-           >* {
-            color: $tabs-secondary-tab-item-text-color;
-            font-size: $tabs-font-size;
-            height: $tabs-secondary-tab-item-height;
-            padding: $tabs-secondary-tab-item-padding;
-          }
-          &.is-active>* {
-            box-shadow: $tabs-secondary-tab-item-active-border;
-            color: $tabs-secondary-tab-item-active-text-color;
-          }
-        }
-      }
-    }
-  }
-}
diff --git a/_sass/denali-css/scss/components/tags.scss b/_sass/denali-css/scss/components/tags.scss
deleted file mode 100644
index b106f22183..0000000000
--- a/_sass/denali-css/scss/components/tags.scss
+++ /dev/null
@@ -1,169 +0,0 @@
-@charset "utf-8";
-// Copyright 2019, Oath Inc.
-// Licensed under the terms of the MIT license. See LICENSE file in project root for terms.
-
-@mixin tags-default {
-  // Tag List
-  div.tags {
-    display: flex;
-    flex-direction: row;
-    flex-wrap: wrap;
-
-    &.nowrap {
-      flex-wrap: nowrap;
-      overflow-x: auto;
-      overflow-x: -moz-scrollbars-none;
-      &::-webkit-scrollbar {
-        width: 0 !important
-      }
-    }
-  }
-
-  .tag {
-    display: flex;
-    align-items: center;
-    justify-content: center;
-    padding: $tags-padding;
-    height: $tags-height;
-    margin: $tags-margin;
-    background: $tags-bg-color;
-    color: $tags-text-color;
-    border-radius: $tags-corner-radius;
-    white-space: nowrap;
-    transition: 300ms;
-
-    &.is-active {
-      background: $tags-active-bg-color;
-    }
-
-    &[disabled],
-    &.is-disabled {
-      background: $tags-disabled-bg-color;
-      color: $tags-disabled-text-color;
-    }
-
-    .d-icon {
-      font-size: $tags-icon-size !important;
-    }
-
-    &.is-small {
-      font-size: $tags-small-text-size;
-      height: $tags-small-height;
-      padding: $tags-small-padding;
-      .d-icon {
-        font-size: $tags-small-icon-size !important;
-      }
-    }
-
-    a.d-icon {
-      opacity: .5;
-      color: $tags-icon-color;
-      &:hover {
-        opacity: 1;
-      }
-    }
-
-    &.has-icon-back {
-      padding-right: 6px;
-      .d-icon {
-        margin-left: 6px;
-      }
-      &.is-small {
-        padding-right: 4px;
-        .d-icon {
-          margin-left: 4px;
-        }
-      }
-    }
-
-    &.has-icon-front {
-      .d-icon {
-        margin-right: 4px;
-      }
-    }
-
-    &.outlined {
-      background: $tags-outlined-bg-color;
-      color: $tags-outlined-text-color !important;
-      border: $tags-outlined-border;
-
-      &:hover {
-        background: $tags-outlined-hover-bg-color;
-        color: $tags-outlined-text-color !important;
-        transition: 300ms;
-      }
-      &.is-active {
-        background: $tags-outlined-active-bg-color;
-        color: $tags-outlined-active-text-color !important;
-      }
-      &[disabled],
-      &.is-disabled {
-        background: $tags-outlined-disabled-bg-color;
-        color: $tags-outlined-disabled-text-color;
-        border-color: $tags-outlined-disabled-border-color;
-      }
-    }
-  }
-}
-
-@mixin tags-theme {
-  .tag {
-    padding: $tags-padding;
-    height: $tags-height;
-    margin: $tags-margin;
-    background: $tags-bg-color;
-    color: $tags-text-color;
-    border-radius: $tags-corner-radius;
-
-    &.is-active {
-      background: $tags-active-bg-color;
-    }
-
-    &[disabled],
-    &.is-disabled {
-      background: $tags-disabled-bg-color;
-      color: $tags-disabled-text-color;
-    }
-
-    .d-icon {
-      font-size: $tags-icon-size !important;
-    }
-
-    &.is-small {
-      font-size: $tags-small-text-size;
-      height: $tags-small-height;
-      padding: $tags-small-padding;
-
-      .d-icon {
-        font-size: $tags-small-icon-size !important;
-      }
-    }
-
-    a.d-icon {
-      color: $tags-icon-color;
-    }
-
-    &.outlined {
-      background: $tags-outlined-bg-color;
-      color: $tags-outlined-text-color !important;
-      border: $tags-outlined-border;
-
-      &:hover {
-        background: $tags-outlined-hover-bg-color;
-        color: $tags-outlined-text-color !important;
-      }
-
-      &.is-active {
-        background: $tags-outlined-active-bg-color;
-        color: $tags-outlined-active-text-color !important;
-      }
-
-      &[disabled],
-      &.is-disabled {
-        background: $tags-outlined-disabled-bg-color;
-        color: $tags-outlined-disabled-text-color !important;
-        border-color: $tags-outlined-disabled-border-color;
-      }
-    }
-  }
-}
\ No newline at end of file
diff --git a/_sass/denali-css/scss/components/titles.scss b/_sass/denali-css/scss/components/titles.scss
deleted file mode 100644
index 0558cee037..0000000000
--- a/_sass/denali-css/scss/components/titles.scss
+++ /dev/null
@@ -1,38 +0,0 @@
-@charset "utf-8";
-// Copyright 2019, Oath Inc.
-// Licensed under the terms of the MIT license. See LICENSE file in project root for terms.
-
-@mixin titles-default {
-  .title {
-    display: flex;
-    align-items: center;
-    height: $titles-height;
-    span.bar {
-      height: inherit;
-      width: $titles-bar-width;
-      background-color: $titles-bar-bg-color;
-      border-radius: $titles-bar-corner-radius;
-      margin-right: 8px;
-      &.is-wide {
-        width: $titles-bar-wider-width;
-        margin-right: 6px;
-      }
-    }
-  }
-}
-
-@mixin titles-theme {
-  .title {
-    height: $titles-height;
-
-    span.bar {
-      width: $titles-bar-width;
-      background-color: $titles-bar-bg-color;
-      border-radius: $titles-bar-corner-radius;
-
-      &.is-wide {
-        width: $titles-bar-wider-width;
-      }
-    }
-  }
-}
\ No newline at end of file
diff --git a/_sass/denali-css/scss/components/toggle.scss b/_sass/denali-css/scss/components/toggle.scss
deleted file mode 100644
index 89134b72da..0000000000
--- a/_sass/denali-css/scss/components/toggle.scss
+++ /dev/null
@@ -1,183 +0,0 @@
-@charset "utf-8";
-// Copyright 2019, Oath Inc.
-// Licensed under the terms of the MIT license. See LICENSE file in project root for terms.
-
-@mixin toggle-default {
-  .toggle {
-    display: flex;
-    position: relative;
-    white-space: nowrap;
-    overflow: auto;
-    padding: $toggle-active-border-width;
-
-    input {
-      position: absolute !important;
-      clip: rect(0, 0, 0, 0);
-      overflow: hidden;
-
-      &:checked + label {
-        box-shadow: none;
-        z-index: 1;
-        background-color: $toggle-active-bg-color;
-        color: $toggle-active-text-color;
-        border-radius: $toggle-active-corner-radius;
-        box-shadow: 0 0 0 $toggle-active-border-width $toggle-active-border-color;
-      }
-    }
-
-    > ul {
-      display: flex;
-    }
-
-    label,
-    li {
-      display: flex;
-      justify-content: center;
-      align-items: center;
-      transition: all 300ms ease-in-out;
-      background: $toggle-bg-color;
-      color: $toggle-text-color;
-      font-size: $toggle-text-size;
-      height: $toggle-height;
-      padding: $toggle-padding;
-
-      a {
-        color: inherit;
-      }
-
-      &.is-active {
-        box-shadow: none;
-        z-index: 1;
-        background-color: $toggle-active-bg-color;
-        color: $toggle-active-text-color;
-        border-radius: $toggle-active-corner-radius !important;
-        box-shadow: 0 0 0 $toggle-active-border-width $toggle-active-border-color;
-
-        a {
-          color: inherit;
-        }
-      }
-
-      &.is-disabled {
-        cursor: not-allowed;
-        color: $toggle-disabled-text-color;
-      }
-
-      &:hover {
-        cursor: pointer;
-        color: $toggle-text-hover-color;
-      }
-
-      &:first-of-type {
-        border-top-left-radius: $toggle-corner-radius;
-        border-bottom-left-radius: $toggle-corner-radius;
-      }
-
-      &:last-of-type {
-        border-top-right-radius: $toggle-corner-radius;
-        border-bottom-right-radius: $toggle-corner-radius;
-      }
-    }
-
-    &.has-icons {
-      label,
-      li {
-        padding: 0px 8px !important;
-      }
-
-      &.is-small {
-        label,
-        li {
-          padding: 0px 6px !important;
-        }
-      }
-    }
-
-    // Sizes
-    &.is-small {
-      label,
-      li {
-        height: $toggle-small-height;
-        padding: $toggle-small-padding;
-      }
-    }
-
-    // Inverse
-    &.is-inverse {
-      label,
-      li {
-        color: $toggle-inverse-text-color;
-        background: $toggle-inverse-bg-color;
-        &.is-active {
-          color: $toggle-inverse-active-text-color;
-          background: $toggle-inverse-active-bg-color;
-        }
-      }
-    }
-  }
-}
-
-@mixin toggle-theme {
-  .toggle {
-    padding: $toggle-active-border-width;
-    input {
-      &:checked+label {
-        background-color: $toggle-active-bg-color;
-        color: $toggle-active-text-color;
-        border-radius: $toggle-active-corner-radius;
-        box-shadow: 0 0 0 $toggle-active-border-width $toggle-active-border-color;
-      }
-    }
-
-    label,
-    li {
-      background: $toggle-bg-color;
-      color: $toggle-text-color;
-      font-size: $toggle-text-size;
-      height: $toggle-height;
-      padding: $toggle-padding;
-      &.is-active {
-        background-color: $toggle-active-bg-color;
-        color: $toggle-active-text-color;
-        border-radius: $toggle-active-corner-radius !important;
-        box-shadow: 0 0 0 $toggle-active-border-width $toggle-active-border-color;
-      }
-      &.is-disabled {
-        color: $toggle-disabled-text-color;
-      }
-      &:hover {
-        color: $toggle-text-hover-color;
-      }
-      &:first-of-type {
-        border-top-left-radius: $toggle-corner-radius;
-        border-bottom-left-radius: $toggle-corner-radius;
-      }
-      &:last-of-type {
-        border-top-right-radius: $toggle-corner-radius;
-        border-bottom-right-radius: $toggle-corner-radius;
-      }
-    }
-
-    // Sizes
-    &.is-small {
-      label,
-      li {
-        height: $toggle-small-height;
-        padding: $toggle-small-padding;
-      }
-    }
-
-    // Inverse
-    &.is-inverse {
-      label,
-      li {
-        color: $toggle-inverse-text-color;
-        background: $toggle-inverse-bg-color;
-        &.is-active {
-          color: $toggle-inverse-active-text-color;
-          background: $toggle-inverse-active-bg-color;
-        }
-      }
-    }
-  }
-}
\ No newline at end of file
diff --git a/_sass/denali-css/scss/components/tooltip.scss b/_sass/denali-css/scss/components/tooltip.scss
deleted file mode 100644
index d5ab98b557..0000000000
--- a/_sass/denali-css/scss/components/tooltip.scss
+++ /dev/null
@@ -1,81 +0,0 @@
-// @charset "utf-8";
-// // Copyright 2019, Oath Inc.
-// // Licensed under the terms of the MIT license. See LICENSE file in project root for terms.
-@mixin tooltip-default {
-
-    *[class*="d-tooltip"] {
-        position: relative;
-
-        &::after {
-            content: attr(data-tooltip);
-            z-index: 1000;
-            position: absolute;
-            visibility: hidden;
-            opacity: 0;
-            transition: 300ms;
-            white-space: nowrap;
-
-            padding: $tooltip-padding;
-            font-size: $tooltip-text-size;
-            border-radius: $tooltip-corner-radius;
-            font-family: $font-family-sans-serif;
-            background-color: $tooltip-bg-color;
-            color: $tooltip-text-color;
-
-            // Position Bottom & Default
-            left: 50%;
-            top: calc(100% + 4px);
-            transform: translateX(-50%);
-        }
-
-        &[class*="-small"]::after {
-            // Small size
-            font-size: $tooltip-small-text-size;
-            padding: $tooltip-small-padding;
-        }
-
-        &[class*="tooltip-top"]::after {
-            // Position Top
-            top: 0%;
-            transform: translate(-50%, calc(-100% - 4px));
-        }
-
-        &[class*="tooltip-left"]::after {
-            // Position Left
-            left: 0%;
-            top: 50%;
-            transform: translate(calc(-100% - 4px), -50%);
-        }
-
-        &[class*="tooltip-right"]::after {
-            // Position Right
-            left: calc(100% + 4px);
-            top: 50%;
-            transform: translateY(-50%);
-        }
-
-        &:hover::after,
-        &:focus::after {
-            visibility: visible;
-            opacity: 1;
-        }
-    }
-
-}
-
-@mixin tooltip-theme {
-    *[class*="d-tooltip"] {
-        &::after {
-            padding: $tooltip-padding;
-            font-size: $tooltip-text-size;
-            border-radius: $tooltip-corner-radius;
-            font-family: $font-family-sans-serif;
-            background-color: $tooltip-bg-color;
-            color: $tooltip-text-color;
-        }
-        &[class*="-small"]::after {
-            font-size: $tooltip-small-text-size;
-            padding: $tooltip-small-padding;
-        }
-    }
-}
\ No newline at end of file
diff --git a/_sass/denali-css/scss/components/typography.scss b/_sass/denali-css/scss/components/typography.scss
deleted file mode 100644
index d367ddb5d3..0000000000
--- a/_sass/denali-css/scss/components/typography.scss
+++ /dev/null
@@ -1,125 +0,0 @@
-@charset "utf-8";
-// Copyright 2019, Oath Inc.
-// Licensed under the terms of the MIT license. See LICENSE file in project root for terms.
-
-@mixin typography-default {
-  h1,
-  h2,
-  h3,
-  h4,
-  h5,
-  h6 {
-    font-family: $headline-font-family;
-  }
-
-  h1 {
-    font-size: $h1-font-size;
-    line-height: $line-height-compact;
-    font-weight: $h1-font-weight;
-    color: $headline-color;
-  }
-
-  h2 {
-    font-size: $h2-font-size;
-    line-height: $line-height-compact;
-    font-weight: $h2-font-weight;
-    color: $headline-color;
-  }
-
-  h3 {
-    font-size: $h3-font-size;
-    line-height: $line-height-normal;
-    font-weight: $h3-font-weight;
-    color: $headline-color;
-  }
-
-  h4 {
-    font-size: $h4-font-size;
-    line-height: $line-height-normal;
-    font-weight: $h4-font-weight;
-    color: $headline-color;
-  }
-
-  h5 {
-    font-size: $h5-font-size;
-    line-height: $line-height-normal;
-    font-weight: $h5-font-weight;
-    color: $headline-color;
-  }
-
-  h6 {
-    font-size: $h6-font-size;
-    line-height: $line-height-normal;
-    font-weight: $h6-font-weight;
-    color: $headline-color;
-  }
-
-  p {
-    font-family: $paragraph-font-family;
-    font-size: $p-font-size;
-    line-height: $line-height-expanded;
-    font-weight: $p-font-weight;
-    color: $paragraph-color;
-  }
-}
-
-@mixin typography-theme {
-  h1,
-  h2,
-  h3,
-  h4,
-  h5,
-  h6 {
-    font-family: $headline-font-family;
-  }
-
-  h1 {
-    font-size: $h1-font-size;
-    line-height: $line-height-compact;
-    font-weight: $h1-font-weight;
-    color: $headline-color;
-  }
-
-  h2 {
-    font-size: $h2-font-size;
-    line-height: $line-height-compact;
-    font-weight: $h2-font-weight;
-    color: $headline-color;
-  }
-
-  h3 {
-    font-size: $h3-font-size;
-    line-height: $line-height-normal;
-    font-weight: $h3-font-weight;
-    color: $headline-color;
-  }
-
-  h4 {
-    font-size: $h4-font-size;
-    line-height: $line-height-normal;
-    font-weight: $h4-font-weight;
-    color: $headline-color;
-  }
-
-  h5 {
-    font-size: $h5-font-size;
-    line-height: $line-height-normal;
-    font-weight: $h5-font-weight;
-    color: $headline-color;
-  }
-
-  h6 {
-    font-size: $h6-font-size;
-    line-height: $line-height-normal;
-    font-weight: $h6-font-weight;
-    color: $headline-color;
-  }
-
-  p {
-    font-family: $paragraph-font-family;
-    font-size: $p-font-size;
-    line-height: $line-height-expanded;
-    font-weight: $p-font-weight;
-    color: $paragraph-color;
-  }
-}
\ No newline at end of file
diff --git a/_sass/denali-css/scss/denali-helpers.scss b/_sass/denali-css/scss/denali-helpers.scss
deleted file mode 100644
index 9f3ec5abdf..0000000000
--- a/_sass/denali-css/scss/denali-helpers.scss
+++ /dev/null
@@ -1,14 +0,0 @@
-@charset "utf-8";
-// Copyright 2019, Oath Inc.
-// Licensed under the terms of the MIT license. See LICENSE file in project root for terms.
-
-// Utilizies
-@import "maps/all";
-@import "utilities/all";
-@import "mixins/all";
-
-// Breakpoints
-@import "mixins/breakpoints.scss";
-
-// Helpers
-@import "helpers/all";
\ No newline at end of file
diff --git a/_sass/denali-css/scss/helpers/_all.scss b/_sass/denali-css/scss/helpers/_all.scss
deleted file mode 100644
index 730905808f..0000000000
--- a/_sass/denali-css/scss/helpers/_all.scss
+++ /dev/null
@@ -1,18 +0,0 @@
-@charset "utf-8";
-// Copyright 2019, Oath Inc.
-// Licensed under the terms of the MIT license. See LICENSE file in project root for terms.
-
-
-// DENALI SCSS :: HELPERS ROLLUP
-@import "colors",
-        "margin",
-        "padding",
-        "text",
-        "flexbox",
-        "size",
-        "hide",
-        "borders",
-        "misc",
-        "type",
-        "float",
-        "shadows";
diff --git a/_sass/denali-css/scss/helpers/borders.scss b/_sass/denali-css/scss/helpers/borders.scss
deleted file mode 100644
index e69c1fa49f..0000000000
--- a/_sass/denali-css/scss/helpers/borders.scss
+++ /dev/null
@@ -1,14 +0,0 @@
-// Copyright 2019, Oath Inc.
-// Licensed under the terms of the MIT license. See LICENSE file in project root for terms.
-
-
-// Border Radius
-@for $i from 1 through 10 {
-  .br-#{$i} {
-    border-radius: 0px + $i !important;
-  }
-}
-
-.br-circle {
-  border-radius: 100% !important;
-}
\ No newline at end of file
diff --git a/_sass/denali-css/scss/helpers/colors.scss b/_sass/denali-css/scss/helpers/colors.scss
deleted file mode 100644
index aeb3a36eac..0000000000
--- a/_sass/denali-css/scss/helpers/colors.scss
+++ /dev/null
@@ -1,83 +0,0 @@
-@charset "utf-8";
-// Copyright 2019, Oath Inc.
-// Licensed under the terms of the MIT license. See LICENSE file in project root for terms.
-
-
-// DENALI SCSS :: COLOR CLASSES
-
-@each $name, $hex in $denali-brand-colors {
-    .has-bg-brand-#{$name} {
-        background-color: $hex !important;
-        color: find-color-invert($hex) !important;
-    }
-
-    .is-brand-#{$name} {
-        color: $hex !important;
-    }
-
-    .fill-brand-#{$name} {
-        fill: $hex !important;
-    }
-}
-
-@each $name, $hex in $denali-status-colors {
-    .has-bg-status-#{$name} {
-        background-color: $hex !important;
-        color: find-color-invert($hex) !important;
-    }
-
-    .is-status-#{$name} {
-        color: $hex !important;
-    }
-
-    .fill-status-#{$name} {
-        fill: $hex !important;
-    }
-}
-
-@each $name, $hex in $denali-grey-colors {
-    .has-bg-grey-#{$name} {
-        background-color: $hex !important;
-        color: find-color-invert($hex) !important;
-    }
-
-    .is-grey-#{$name} {
-        color: $hex !important;
-    }
-
-    .fill-grey-#{$name} {
-        fill: $hex !important;
-    }
-}
-
-@each $name, $hex in $denali-graph-colors {
-    .has-bg-graph-#{$name} {
-        background-color: $hex !important;
-        color: find-color-invert($hex) !important;
-    }
-
-    .is-graph-#{$name} {
-        color: $hex !important;
-    }
-
-    .fill-graph-#{$name} {
-        fill: $hex !important;
-    }
-}
-
-@each $name, $variants in $denali-colors {
-    @each $color-variant, $hex in $variants {
-        .has-bg-#{$name}-#{$color-variant} {
-            background-color: $hex !important;
-            color: find-color-invert($hex) !important;
-        }
-
-        .is-#{$name}-#{$color-variant} {
-            color: $hex !important;
-        }
-
-        .fill-#{$name}-#{$color-variant} {
-            fill: $hex !important;
-        }
-    }
-}
diff --git a/_sass/denali-css/scss/helpers/flexbox.scss b/_sass/denali-css/scss/helpers/flexbox.scss
deleted file mode 100644
index 80e77f0641..0000000000
--- a/_sass/denali-css/scss/helpers/flexbox.scss
+++ /dev/null
@@ -1,124 +0,0 @@
-// Copyright 2019, Oath Inc.
-// Licensed under the terms of the MIT license. See LICENSE file in project root for terms.
-
-// Flexbox
-.flex {
-  display: flex !important;
-  &-auto {
-    flex: auto !important;
-  }
-  &-1 {
-    flex: 1 !important;
-  }
-  &-2 {
-    flex: 2 !important;
-  }
-  &-3 {
-    flex: 3 !important;
-  }
-  &-4 {
-    flex: 4 !important;
-  }
-}
-
-.flex-column {
-  flex-direction: column !important;
-  &-column-reverse {
-    flex-direction: column-reverse !important;
-  }
-}
-
-.tablet-down-flex-column {
-  @include media-size-down(tablet) {
-    flex-direction: column !important;
-  }
-}
-
-.flex-row {
-  flex-direction: row !important;
-  &-reversed {
-    flex-direction: row-reverse !important;
-  }
-}
-
-// Space Between
-.space-between {
-  justify-content: space-between !important;
-}
-// Media Size Up
-.space-between-small-desktop-up {
-  @include media-size-up(small-desktop) {
-    justify-content: space-between !important;
-  }
-}
-// Media Size Down
-.space-between-small-desktop-down {
-  @include media-size-down(small-desktop) {
-    justify-content: space-between !important;
-  }
-}
-.space-between-tablet-down {
-  @include media-size-down(tablet) {
-    justify-content: space-between !important;
-  }
-}
-// Media Size Only
-.space-between-mobile {
-  @include media-size-only(mobile) {
-    justify-content: space-between !important;
-  }
-}
-.xs-space-between {
-  @include media-size-only(mobile) {
-    justify-content: space-between !important;
-  }
-}
-.sm-space-between {
-  @include media-size-only(tablet) {
-    justify-content: space-between !important;
-  }
-}
-.md-space-between {
-  @include media-size-only(small-desktop) {
-    justify-content: space-between !important;
-  }
-}
-.lg-space-between {
-  @include media-size-only(desktop) {
-    justify-content: space-between !important;
-  }
-}
-.xl-space-between {
-  @include media-size-only(hd) {
-    justify-content: space-between !important;
-  }
-}
-
-.space-around {
-  justify-content: space-around !important;
-}
-
-.align-items-center {
-  align-items: center !important;
-}
-.align-items-flex-end {
-  align-items: flex-end !important;
-}
-
-.align-self-center {
-  align-self: center !important;
-}
-.align-self-start {
-  align-self: flex-start !important;
-}
-
-.align-content-center {
-  align-content: center !important;
-}
-.justify-content-center {
-  justify-content: center !important;
-}
-
-.flex-wrap {
-  flex-wrap: wrap !important;
-}
\ No newline at end of file
diff --git a/_sass/denali-css/scss/helpers/float.scss b/_sass/denali-css/scss/helpers/float.scss
deleted file mode 100644
index 1c4f92a1f0..0000000000
--- a/_sass/denali-css/scss/helpers/float.scss
+++ /dev/null
@@ -1,11 +0,0 @@
-// Copyright 2019, Oath Inc.
-// Licensed under the terms of the MIT license. See LICENSE file in project root for terms.
-
-
-.float-right {
-  float: right;
-}
-
-.float-left {
-  float: left;
-}
diff --git a/_sass/denali-css/scss/helpers/hide.scss b/_sass/denali-css/scss/helpers/hide.scss
deleted file mode 100644
index 2acaa949b4..0000000000
--- a/_sass/denali-css/scss/helpers/hide.scss
+++ /dev/null
@@ -1,59 +0,0 @@
-// Copyright 2019, Oath Inc.
-// Licensed under the terms of the MIT license. See LICENSE file in project root for terms.
-
-
-// Hide
-.hide {
-  display: none !important;
-}
-// Media Size Up
-.hide-small-desktop-up {
-  @include media-size-up(small-desktop) {
-    display: none !important;
-  }
-}
-// Media Size Down
-.hide-small-desktop-down {
-  @include media-size-down(small-desktop) {
-    display: none !important;
-  }
-}
-.hide-tablet-down {
-  @include media-size-down(tablet) {
-    display: none !important;
-  }
-}
-// Media Size Only
-.hide-mobile {
-  @include media-size-only(mobile) {
-    display: none !important;
-  }
-}
-
-
-
-.xs-hide {
-  @include media-size-only(mobile) {
-    display: none !important;
-  }
-}
-.sm-hide {
-  @include media-size-only(tablet) {
-    display: none !important;
-  }
-}
-.md-hide {
-  @include media-size-only(small-desktop) {
-    display: none !important;
-  }
-}
-.lg-hide {
-  @include media-size-only(desktop) {
-    display: none !important;
-  }
-}
-.xl-hide {
-  @include media-size-only(hd) {
-    display: none !important;
-  }
-}
diff --git a/_sass/denali-css/scss/helpers/margin.scss b/_sass/denali-css/scss/helpers/margin.scss
deleted file mode 100644
index 9bbf16a900..0000000000
--- a/_sass/denali-css/scss/helpers/margin.scss
+++ /dev/null
@@ -1,57 +0,0 @@
-// Copyright 2019, Oath Inc.
-// Licensed under the terms of the MIT license. See LICENSE file in project root for terms.
-
-// Margins
-@for $i from 0 through 100 {
-  .m-#{$i} {
-    margin: 1px * $i !important;
-  }
-}
-
-.m-auto {
-  margin: auto;
-}
-
-// Margin Top
-@for $i from 0 through 100 {
-  .m-t-#{$i} {
-    margin-top: 1px * $i !important;
-  }
-}
-
-// Margin Right
-@for $i from 0 through 100 {
-  .m-r-#{$i} {
-    margin-right: 1px * $i !important;
-  }
-}
-
-// Margin Bottom
-@for $i from 0 through 100 {
-  .m-b-#{$i} {
-    margin-bottom: 1px * $i !important;
-  }
-}
-
-// Margin Left
-@for $i from 0 through 100 {
-  .m-l-#{$i} {
-    margin-left: 1px * $i !important;
-  }
-}
-
-// Margin Horizontal
-@for $i from 0 through 100 {
-  .m-x-#{$i} {
-    margin-left: 1px * $i !important;
-    margin-right: 1px * $i !important;
-  }
-}
-
-// Margin Vertical
-@for $i from 0 through 100 {
-  .m-y-#{$i} {
-    margin-top: 1px * $i !important;
-    margin-bottom: 1px * $i !important;
-  }
-}
diff --git a/_sass/denali-css/scss/helpers/misc.scss b/_sass/denali-css/scss/helpers/misc.scss
deleted file mode 100644
index b65ccb7b10..0000000000
--- a/_sass/denali-css/scss/helpers/misc.scss
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright 2019, Oath Inc.
-// Licensed under the terms of the MIT license. See LICENSE file in project root for terms.
-
-
-// Overflow
-.overflow-y-auto {
-  overflow-y: auto !important;
-  -webkit-overflow-scrolling: touch;
-}
-
-.overflow-hidden {
-  overflow: hidden !important;
-}
-
-
-// Position
-.sticky-top {
-  position: sticky !important;
-  top: 0 !important;
-}
-
-
-// Background Image
-.bg-size-cover {
-  background-size: cover !important;
-}
-.bg-position-center {
-  background-position: center !important;
-}
\ No newline at end of file
diff --git a/_sass/denali-css/scss/helpers/padding.scss b/_sass/denali-css/scss/helpers/padding.scss
deleted file mode 100644
index 027ce69f70..0000000000
--- a/_sass/denali-css/scss/helpers/padding.scss
+++ /dev/null
@@ -1,53 +0,0 @@
-// Copyright 2019, Oath Inc.
-// Licensed under the terms of the MIT license. See LICENSE file in project root for terms.
-
-// Paddings
-@for $i from 0 through 100 {
-  .p-#{$i} {
-    padding: 1px * $i !important;
-  }
-}
-
-// Padding Top
-@for $i from 0 through 100 {
-  .p-t-#{$i} {
-    padding-top: 1px * $i !important;
-  }
-}
-
-// Padding Right
-@for $i from 0 through 100 {
-  .p-r-#{$i} {
-    padding-right: 1px * $i !important;
-  }
-}
-
-// Padding Bottom
-@for $i from 0 through 100 {
-  .p-b-#{$i} {
-    padding-bottom: 1px * $i !important;
-  }
-}
-
-// Padding Left
-@for $i from 0 through 100 {
-  .p-l-#{$i} {
-    padding-left: 1px * $i !important;
-  }
-}
-
-// Padding Horizontal
-@for $i from 0 through 100 {
-  .p-x-#{$i} {
-    padding-left: 1px * $i !important;
-    padding-right: 1px * $i !important;
-  }
-}
-
-// Padding Vertical
-@for $i from 0 through 100 {
-  .p-y-#{$i} {
-    padding-top: 1px * $i !important;
-    padding-bottom: 1px * $i !important;
-  }
-}
diff --git a/_sass/denali-css/scss/helpers/shadows.scss b/_sass/denali-css/scss/helpers/shadows.scss
deleted file mode 100644
index df5a0d9de3..0000000000
--- a/_sass/denali-css/scss/helpers/shadows.scss
+++ /dev/null
@@ -1,22 +0,0 @@
-// Copyright 2019, Oath Inc.
-// Licensed under the terms of the MIT license. See LICENSE file in project root for terms.
-
-.shadow-1 {
-    box-shadow: $shadow-1;
-}
-
-.shadow-2 {
-    box-shadow: $shadow-2;
-}
-
-.shadow-3 {
-    box-shadow: $shadow-3;
-}
-
-.shadow-4 {
-    box-shadow: $shadow-4;
-}
-
-.shadow-5 {
-    box-shadow: $shadow-5;
-}
\ No newline at end of file
diff --git a/_sass/denali-css/scss/helpers/size.scss b/_sass/denali-css/scss/helpers/size.scss
deleted file mode 100644
index f9e8256199..0000000000
--- a/_sass/denali-css/scss/helpers/size.scss
+++ /dev/null
@@ -1,27 +0,0 @@
-// Copyright 2019, Oath Inc.
-// Licensed under the terms of the MIT license. See LICENSE file in project root for terms.
-
-
-// Width
-@for $i from 0 through 50 {
-  .w-#{$i} {
-    width: 5px * $i !important;
-  }
-}
-
-.w-auto {
-  min-width: auto !important;
-  width: auto !important;
-}
-
-
-// Height
-@for $i from 0 through 50 {
-  .h-#{$i} {
-    height: 5px * $i !important;
-  }
-}
-
-.h-minus-nav {
-  height: calc(100vh - 60px) !important;
-}
diff --git a/_sass/denali-css/scss/helpers/text.scss b/_sass/denali-css/scss/helpers/text.scss
deleted file mode 100644
index c56400efb7..0000000000
--- a/_sass/denali-css/scss/helpers/text.scss
+++ /dev/null
@@ -1,24 +0,0 @@
-// Copyright 2019, Oath Inc.
-// Licensed under the terms of the MIT license. See LICENSE file in project root for terms.
-
-
-// Text-Transform
-.upper {
-  text-transform: uppercase;
-}
-
-.lower {
-  text-transform: lowercase;
-}
-
-.cap {
-  text-transform: capitalize;
-}
-
-.font-12 {
-  font-size: 12px !important;
-}
-
-.bold {
-  font-weight: bold;
-}
diff --git a/_sass/denali-css/scss/helpers/type.scss b/_sass/denali-css/scss/helpers/type.scss
deleted file mode 100644
index f119365124..0000000000
--- a/_sass/denali-css/scss/helpers/type.scss
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright 2019, Oath Inc.
-// Licensed under the terms of the MIT license. See LICENSE file in project root for terms.
-
-
-// Type
-.is-italic {
-  font-style: italic !important;
-}
-.is-light {
-  font-weight: 100 !important;
-}
-.is-regular {
-  font-weight: 400 !important;
-}
-.is-bold {
-  font-weight: 600 !important;
-}
-.is-mono {
-  font-family: monospace !important;
-}
-
-// Line height
-.line-height-compact {
-  line-height: 1 !important;
-}
\ No newline at end of file
diff --git a/_sass/denali-css/scss/maps/_all.scss b/_sass/denali-css/scss/maps/_all.scss
deleted file mode 100644
index df102e4d2f..0000000000
--- a/_sass/denali-css/scss/maps/_all.scss
+++ /dev/null
@@ -1,8 +0,0 @@
-@charset "utf-8";
-// Copyright 2019, Oath Inc.
-// Licensed under the terms of the MIT license. See LICENSE file in project root for terms.
-
-
-// DENALI SCSS :: MAPS ROLLUP
-
-@import "color-maps";
diff --git a/_sass/denali-css/scss/maps/color-maps.scss b/_sass/denali-css/scss/maps/color-maps.scss
deleted file mode 100644
index 81fd300d22..0000000000
--- a/_sass/denali-css/scss/maps/color-maps.scss
+++ /dev/null
@@ -1,203 +0,0 @@
-@charset "utf-8";
-// Copyright 2019, Oath Inc.
-// Licensed under the terms of the MIT license. See LICENSE file in project root for terms.
-
-
-// DENALI SCSS :: DENALI COLOR MAPS
-
-$denali-brand-colors: (
-    "100": #CCE2FF,
-    "200": #80B6FF,
-    "300": #016EFF,
-    "400": #004CB3,
-    "500": #002B66
-);
-
-$denali-status-colors: (
-    "danger": #EA0000,
-    "warning": #F4CB00,
-    "success": #15C046,
-    "info": #0066DF
-);
-
-$denali-grey-colors: (
-    "100": #FFFFFF,
-    "200": #F8F8F8,
-    "300": #F2F2F2,
-    "400": #E8E8E8,
-    "500": #D5D5D5,
-    "600": #9A9A9A,
-    "700": #606060,
-    "800": #303030
-);
-
-$denali-colors: (
-    "red": (
-        "100": #FFBDBD,
-        "200": #F89494,
-        "300": #F16A6A,
-        "400": #E94141,
-        "500": #E21717,
-        "600": #D01111,
-        "700": #BE0C0C,
-        "800": #AB0606,
-        "900": #990000,
-        "r100": #FFBDBD,
-        "r200": #F16A6A,
-        "r300": #E21717,
-        "r400": #BE0C0C,
-        "r500": #990000
-    ),
-    "orange": (
-        "100": #FFDCC0,
-        "200": #FCC391,
-        "300": #F8A962,
-        "400": #F49032,
-        "500": #F17603,
-        "600": #DC6A03,
-        "700": #C85E03,
-        "800": #B35102,
-        "900": #9E4502,
-        "r100": #FFDCC0,
-        "r200": #F8A962,
-        "r300": #F17603,
-        "r400": #C85E03,
-        "r500": #9E4502
-    ),
-    "yellow": (
-        "100": #FFFCD2,
-        "200": #FFF4A9,
-        "300": #FFEB71,
-        "400": #FEE344,
-        "500": #FED800,
-        "600": #F7C500,
-        "700": #F0B200,
-        "800": #E99E00,
-        "900": #E28B00,
-        "r100": #FFFCD2,
-        "r200": #FFE86E,
-        "r300": #FED800,
-        "r400": #F0B200,
-        "r500": #E28B00
-    ),
-    "lime": (
-        "100": #E8FFC0,
-        "200": #D0F595,
-        "300": #B8EC69,
-        "400": #9FE23E,
-        "500": #87D812,
-        "600": #7BC210,
-        "700": #6EAC0F,
-        "800": #62950D,
-        "900": #557F0B,
-        "r100": #E8FFC0,
-        "r200": #B8EC69,
-        "r300": #87D812,
-        "r400": #6EAC0F,
-        "r500": #557F0B
-    ),
-    "green": (
-        "100": #D3FFD2,
-        "200": #A6EFB4,
-        "300": #7AE097,
-        "400": #4DD079,
-        "500": #20C05B,
-        "600": #1DAD51,
-        "700": #1A9947,
-        "800": #17863D,
-        "900": #147233,
-        "r100": #D3FFD2,
-        "r200": #7AE097,
-        "r300": #20C05B,
-        "r400": #1A9947,
-        "r500": #147233
-    ),
-    "sky": (
-        "100": #BEF0FC,
-        "200": #96E8FC,
-        "300": #6DDDFA,
-        "400": #43D1F7,
-        "500": #19C6F4,
-        "600": #17AFD4,
-        "700": #1499B5,
-        "800": #128295,
-        "900": #106B75,
-        "r100": #C0F3FF,
-        "r200": #6DDDFA,
-        "r300": #19C6F4,
-        "r400": #1499B5,
-        "r500": #106B75
-    ),
-    "blue": (
-        "100": #C0E3FF,
-        "200": #90C7F7,
-        "300": #60ABEF,
-        "400": #308EE7,
-        "500": #0072DF,
-        "600": #0065BF,
-        "700": #0058A0,
-        "800": #004B80,
-        "900": #003E60,
-        "r100": #C0E3FF,
-        "r200": #60ABEF,
-        "r300": #0072DF,
-        "r400": #0058A0,
-        "r500": #003E60
-    ),
-    "purple": (
-        "100": #DBD0FD,
-        "200": #C8B6FF,
-        "300": #AA89EA,
-        "400": #8C5BD4,
-        "500": #6E2EBF,
-        "600": #5413A8,
-        "700": #40008B,
-        "800": #30006D,
-        "900": #20004E,
-        "r100": #DBD0FD,
-        "r200": #AA89EA,
-        "r300": #6E2EBF,
-        "r400": #40008B,
-        "r500": #20004E
-    ),
-    "violet": (
-        "100": #E9B7F3,
-        "200": #DE9BEB,
-        "300": #C777D6,
-        "400": #B152C2,
-        "500": #9A2EAD,
-        "600": #842895,
-        "700": #6E227D,
-        "800": #571B64,
-        "900": #41154C,
-        "r100": #E9B7F3,
-        "r200": #C777D6,
-        "r300": #9A2EAD,
-        "r400": #6E227D,
-        "r500": #41154C
-    ),
-    "pink": (
-        "100": #FABCDC,
-        "200": #FF9DCC,
-        "300": #FF7AB8,
-        "400": #FF56A4,
-        "500": #FF3390,
-        "600": #DF2D82,
-        "700": #BF2874,
-        "800": #9F2265,
-        "900": #7F1C57,
-        "r100": #FABCDC,
-        "r200": #FF7AB8,
-        "r300": #FF3390,
-        "r400": #BF2874,
-        "r500": #7F1C57
-    )
-);
-
-// SET GRAPH COLORS
-// ALL ARE THE "500" COLOR OF THE MAP
-$denali-graph-colors: ();
-@each $name, $colors in $denali-colors {
-    $color: map-get($colors, "500");
-    $denali-graph-colors: map-merge($denali-graph-colors, ($name: $color));
-}
diff --git a/_sass/denali-css/scss/mixins/_all.scss b/_sass/denali-css/scss/mixins/_all.scss
deleted file mode 100644
index 0e449f6046..0000000000
--- a/_sass/denali-css/scss/mixins/_all.scss
+++ /dev/null
@@ -1,8 +0,0 @@
-@charset "utf-8";
-// Copyright 2019, Oath Inc.
-// Licensed under the terms of the MIT license. See LICENSE file in project root for terms.
-
-
-// DENALI SCSS :: MIXIN ROLLUP
-
-@import "breakpoints";
\ No newline at end of file
diff --git a/_sass/denali-css/scss/mixins/breakpoints.scss b/_sass/denali-css/scss/mixins/breakpoints.scss
deleted file mode 100644
index bc594d0baf..0000000000
--- a/_sass/denali-css/scss/mixins/breakpoints.scss
+++ /dev/null
@@ -1,143 +0,0 @@
-@charset "utf-8";
-// Copyright 2019, Oath Inc.
-// Licensed under the terms of the MIT license. See LICENSE file in project root for terms.
-
-
-// Breakpoint Variables
-// XS
-$mobile: 0px;
-$mobile-end: 599px;
-// SM
-$tablet: 600px;
-$tablet-end: 899px;
-// MD
-$small-desktop: 900px;
-$small-desktop-end: 1199px;
-// LG
-$desktop: 1200px;
-$desktop-end: 1439px;
-// XL
-$hd: 1440px;
-$hd-end: 3000px;
-
-// Direction up
-@mixin media-size-up($size) {
-  // Moblie
-  @if $size == mobile {
-    @media (min-width: $mobile) { @content; }
-  }
-  // Tablet
-  @if $size == tablet {
-    @media (min-width: $tablet) { @content; }
-  }
-  // Small Desktop
-  @else if $size == small-desktop {
-    @media (min-width: $small-desktop) { @content; }
-  }
-  // Desktop
-  @else if $size == desktop {
-    @media (min-width: $desktop) { @content; }
-  }  // HD
-  @else if $size == hd {
-    @media (min-width: $hd) { @content; }
-  }
-}
-
-// Direction down
-@mixin media-size-down($size) {
-  // Mobile XS
-  @if $size == mobile {
-    @media (max-width: $mobile-end) { @content; }
-  }
-  // Tablet SM
-  @else if $size == tablet {
-    @media (max-width: $tablet-end) { @content; }
-  }
-  // Small Desktop MD
-  @else if $size == small-desktop {
-    @media (max-width: $small-desktop-end) { @content; }
-  }
-  // Desktop LG
-  @else if $size == desktop {
-    @media (max-width: $desktop-end) { @content; }
-  }
-}
-
-// Only
-@mixin media-size-only($size) {
-  // Mobile XS
-  @if $size == mobile {
-    @media (min-width: $mobile) and (max-width: $mobile-end) { @content; }
-  }
-  // Tablet SM
-  @else if $size == tablet {
-    @media (min-width: $tablet) and (max-width: $tablet-end) { @content; }
-  }
-  // Small Desktop MD
-  @else if $size == small-desktop {
-    @media (min-width: $small-desktop) and (max-width: $small-desktop-end) { @content; }
-  }
-  // Desktop LG
-  @else if $size == desktop {
-    @media (min-width: $desktop) and (max-width: $desktop-end) { @content; }
-  }
-  // Desktop LG
-  @else if $size == hd {
-    @media (min-width: $hd) { @content; }
-  }
-}
-
-// Between
-@mixin media-size-between($start, $end) {
-  // Start
-  // Mobile XS
-  @if $start == mobile and $end == mobile {
-    @media (min-width: $mobile) and (max-width: $mobile-end) { @content; }
-  }
-  @else if $start == mobile and $end == tablet {
-    @media (min-width: $mobile) and (max-width: $tablet-end) { @content; }
-  }
-  @else if $start == mobile and $end == small-desktop {
-    @media (min-width: $mobile) and (max-width: $small-desktop-end) { @content; }
-  }
-  @else if $start == mobile and $end == desktop {
-    @media (min-width: $mobile) and (max-width: $desktop-end) { @content; }
-  }
-  @else if $start == mobile and $end == hd {
-    @media (min-width: $mobile) and (max-width: $hd-end) { @content; }
-  }
-  // Tablet SM
-  @else if $start == tablet and $end == tablet {
-    @media (min-width: $tablet) and (max-width: $tablet-end) { @content; }
-  }
-  @else if $start == tablet and $end == small-desktop {
-    @media (min-width: $tablet) and (max-width: $small-desktop-end) { @content; }
-  }
-  @else if $start == tablet and $end == desktop {
-    @media (min-width: $tablet) and (max-width: $desktop-end) { @content; }
-  }
-  @else if $start == tablet and $end == hd {
-    @media (min-width: $tablet) and (max-width: $hd-end) { @content; }
-  }
-  // Small Desktop MD
-  @else if $start == small-desktop and $end == small-desktop {
-    @media (min-width: $small-desktop) and (max-width: $small-desktop-end) { @content; }
-  }
-  @else if $start == small-desktop and $end == desktop {
-    @media (min-width: $small-desktop) and (max-width: $desktop-end) { @content; }
-  }
-  @else if $start == small-desktop and $end == hd {
-    @media (min-width: $small-desktop) and (max-width: $hd-end) { @content; }
-  }
-  // Desktop LG
-  @else if $start == desktop and $end == desktop {
-    @media (min-width: $desktop) and (max-width: $desktop-end) { @content; }
-  }
-  @else if $start == desktop and $end == hd {
-    @media (min-width: $desktop) and (max-width: $hd-end) { @content; }
-  }
-  // HD XL
-  @else if $start == hd and $end == hd {
-    @media (min-width: $hd) and (max-width: $hd-end) { @content; }
-  }
-}
diff --git a/_sass/denali-css/scss/mixins/theme.scss b/_sass/denali-css/scss/mixins/theme.scss
deleted file mode 100644
index 2503c45050..0000000000
--- a/_sass/denali-css/scss/mixins/theme.scss
+++ /dev/null
@@ -1,67 +0,0 @@
-@charset "utf-8";
-// Copyright 2019, Oath Inc.
-// Licensed under the terms of the MIT license. See LICENSE file in project root for terms.
-
-
-// COMPONENT IMPORTS
-@import "components/alerts";
-@import "components/box";
-@import "components/breadcrumb";
-@import "components/button";
-@import "components/checkbox";
-@import "components/chips";
-@import "components/icons";
-@import "components/inputs";
-@import "components/links";
-@import "components/loader";
-@import "components/menus";
-@import "components/modals";
-@import "components/navbar";
-@import "components/progress";
-@import "components/radio";
-@import "components/selects";
-@import "components/switch";
-@import "components/table";
-@import "components/tabs";
-@import "components/tags";
-@import "components/titles";
-@import "components/toggle";
-@import "components/tooltip";
-@import "components/typography";
-
-@mixin generate-theme($themeName) {
-
-    .#{$themeName} {
-        // BODY OR PARENT CONTAINER
-        & {
-            background: $body-bg-color;
-            color: $body-text-color;
-            font-family: $font-family-sans-serif;
-        }
-        // COMPONENTS
-        @include alerts-theme;
-        @include box-theme;
-        @include breadcrumb-theme;
-        @include button-theme;
-        @include checkbox-theme;
-        @include chips-theme;
-        @include icons-theme;
-        @include inputs-theme;
-        @include links-theme;
-        @include loader-theme;
-        @include menus-theme;
-        @include modals-theme;
-        @include navbar-theme;
-        @include progress-theme;
-        @include radio-theme;
-        @include selects-theme;
-        @include switch-theme;
-        @include table-theme;
-        @include tabs-theme;
-        @include tags-theme;
-        @include titles-theme;
-        @include toggle-theme;
-        @include tooltip-theme;
-        @include typography-theme;
-    }
-}
\ No newline at end of file
diff --git a/_sass/denali-css/scss/utilities/_all.scss b/_sass/denali-css/scss/utilities/_all.scss
deleted file mode 100644
index fd5dc3da0e..0000000000
--- a/_sass/denali-css/scss/utilities/_all.scss
+++ /dev/null
@@ -1,12 +0,0 @@
-@charset "utf-8";
-// Copyright 2019, Oath Inc.
-// Licensed under the terms of the MIT license. See LICENSE file in project root for terms.
-
-
-// DENALI SCSS :: UTILITIES ROLLUP
-
-// FUNCTIONS
-@import "color-functions";
-
-// VARIABLES
-@import "root-variables";
diff --git a/_sass/denali-css/scss/utilities/color-functions.scss b/_sass/denali-css/scss/utilities/color-functions.scss
deleted file mode 100644
index 4d723792f3..0000000000
--- a/_sass/denali-css/scss/utilities/color-functions.scss
+++ /dev/null
@@ -1,108 +0,0 @@
-@charset "utf-8";
-// Copyright 2019, Oath Inc.
-// Licensed under the terms of the MIT license. See LICENSE file in project root for terms.
-
-
-@function denali-color($color-name, $color-variant: "500") {
-    $color: false;
-    @if $color-name == "brand" {
-        //@warn "BRAND => #{$color-name} #{$color-variant}";
-        $color: map-get($denali-brand-colors, $color-variant);
-    } @else if $color-name == "grey" {
-        //@warn "GRAPH => #{$color-name} #{$color-variant}";
-        $color: map-get($denali-grey-colors, $color-variant);
-    } @else if $color-name == "status" {
-        //@warn "GRAPH => #{$color-name} #{$color-variant}";
-        $color: map-get($denali-status-colors, $color-variant);
-    } @else if $color-name == "graph" {
-        //@warn "GRAPH => #{$color-name} #{$color-variant}";
-        $color: map-get($denali-graph-colors, $color-variant);
-    } @else {
-        //@warn "DEFAULT => #{$color-name} #{$color-variant}";
-        $color: map-get(map-get($denali-colors, $color-name), $color-variant);
-    }
-    @if $color {
-        @return $color;
-    } @else {
-        // Libsass still doesn't seem to support @error
-        @warn "=> ERROR: COLOR NOT FOUND! <= | Your $color-name, $color-variant combination did not match any of the values in the denali-color maps.";
-    }
-}
-
-// same as above, but converts hex to HSL color
-@function denali-color-hsl($color-name, $color-variant: "500") {
-    $color: false;
-    @if $color-name == "brand" {
-        $color: map-get($denali-brand-colors, $color-variant);
-    } @else if $color-name == "grey" {
-        //@warn "GRAPH => #{$color-name} #{$color-variant}";
-        $color: map-get($denali-grey-colors, $color-variant);
-    } @else if $color-name == "status" {
-        //@warn "GRAPH => #{$color-name} #{$color-variant}";
-        $color: map-get($denali-status-colors, $color-variant);
-    } @else if $color-name == "graph" {
-        $color: map-get($denali-graph-colors, $color-variant);
-    } @else {
-        $color: map-get(map-get($denali-colors, $color-name), $color-variant);
-    }
-    @if $color {
-        //$color-red: hue($color);
-        $color-rgba: rgba($color, 1);
-        $color-hue: hue($color-rgba);
-        $color-saturation: saturation($color-rgba);
-        $color-lightness: lightness($color-rgba);
-        $color-hsl: hsl($color-hue, $color-saturation, $color-lightness);
-        //@warn "=> DEBUG <= #{$color-name} | #{$color-variant} | #{$color-hsl}";
-        @return $color-hsl;
-    } @else {
-        // Libsass still doesn't seem to support @error
-        @warn "=> ERROR: HSL COLOR NOT FOUND! <= | Your $color-name, $color-variant combination did not match any of the values in the denali-color maps.";
-    }
-}
-
-
-// Numbers
-
-@function round-to-even-number($number) {
-    @return floor($number / 2) * 2;
-}
-
-@function power-number($number, $exp) {
-    $value: 1;
-    @if $exp > 0 {
-         @for $i from 1 through $exp {
-            $value: $value * $number;
-        }
-    } @else if $exp < 0 {
-         @for $i from 1 through -$exp {
-            $value: $value / $number;
-        }
-    }
-    @return $value;
-}
-
-// Color
-
-@function color-luminance($color) {
-    $color-rgb: ('red': red($color),'green': green($color),'blue': blue($color));
-    @each $name, $value in $color-rgb {
-        $adjusted: 0;
-        $value: $value / 255;
-        @if $value < .03928 {
-            $value: $value / 12.92;
-        } @else {
-            $value: ($value + .055) / 1.055;
-            $value: power-number($value, 2);
-        }
-        $color-rgb: map-merge($color-rgb, ($name: $value));
-    }
-    @return (map-get($color-rgb, 'red') * .2126) + (map-get($color-rgb, 'green') * .7152) + (map-get($color-rgb, 'blue') * .0722);
-}
-
-@function find-color-invert($color) {
-     @if (color-luminance($color) > .55){
-        @return rgba(#000, .7);
-    } @else {
-         @return #fff;
-    }
-}
diff --git a/_sass/denali-css/scss/utilities/root-variables.scss b/_sass/denali-css/scss/utilities/root-variables.scss
deleted file mode 100644
index 95f0ec48ba..0000000000
--- a/_sass/denali-css/scss/utilities/root-variables.scss
+++ /dev/null
@@ -1,452 +0,0 @@
-@charset "utf-8";
-// Copyright 2019, Oath Inc.
-// Licensed under the terms of the MIT license. See LICENSE file in project root for terms.
-
-// GLOBALS
-// Brand
-$color-brand-100: #CCE2FF;
-$color-brand-200: #80B6FF;
-$color-brand-300: #006DFB;
-$color-brand-400: #004CB3;
-$color-brand-500: #002B66;
-
-// Grey
-$color-grey-100: #ffffff;
-$color-grey-200: #f8f8f8;
-$color-grey-300: #f2f2f2;
-$color-grey-400: #e8e8e8;
-$color-grey-500: #d5d5d5;
-$color-grey-600: #9a9a9a;
-$color-grey-700: #606060;
-$color-grey-800: #303030;
-
-// Status
-$color-status-danger: #ea0000;
-$color-status-warning: #f4cb00;
-$color-status-success: #15c046;
-$color-status-information: #0066df;
-
-// Primary Data Visualization
-$color-red: #e21717;
-$color-orange: #f17603;
-$color-yellow: #fed800;
-$color-lime: #87d812;
-$color-green: #20c05b;
-$color-sky: #19c6f4;
-$color-blue: #0072df;
-$color-purple: #6e2ebf;
-$color-violet: #9a2ead;
-$color-pink: #ff3390;
-
-// Font Family
-$font-family-sans-serif: 'Roobert',Helvetica,"Arial",sans-serif;
-$font-family-monospace: "SFMono-Regular",monospace;
-
-// Border Radius
-$border-radius-sm: 4px;
-$border-radius-md: 6px;
-$border-radius-lg: 8px;
-$border-radius-circle: 100%;
-$border-radius-pill: 9999px;
-
-// Shadows
-$shadow-1: 0 50px 20px -50px rgba(0, 0, 0, 0.10), 0 8px 20px 0 rgba(0, 0, 0, 0.10);
-$shadow-2: 0 50px 30px -50px rgba(0, 0, 0, 0.10), 0 10px 30px 0 rgba(0, 0, 0, 0.10);
-$shadow-3: 0 50px 40px -50px rgba(0, 0, 0, 0.10), 0 15px 40px 0 rgba(0, 0, 0, 0.10);
-$shadow-4: 0 50px 50px -50px rgba(0, 0, 0, 0.10), 0 20px 50px 0 rgba(0, 0, 0, 0.10);
-$shadow-5: 0 50px 60px -50px rgba(0, 0, 0, 0.10), 0 25px 60px 0 rgba(0, 0, 0, 0.10);
-
-
-
-// BASE
-// Body Element
-$body-bg-color: $color-grey-100;
-$body-text-color: $color-grey-800;
-$base-font-size: 10px;
-
-
-
-// Components
-// Alerts
-$alert-bg-color: $color-grey-100;
-$alert-text-color: null;
-$alert-corner-radius: $border-radius-sm;
-$alert-shadow: 0px 4px 8px rgba(0, 0, 0, 0.16);
-$alert-border-color: $color-grey-500;
-$alert-border-width: 4px;
-$alert-border-style: solid;
-$alert-close-icon-color: $color-grey-800;
-
-$alert-inline-text-color: $color-grey-800;
-$alert-inline-bg-color: $color-grey-300;
-$alert-inline-font-size: inherit;
-$alert-inline-alignment-text: center;
-$alert-inline-alignment-justify-content: center;
-$alert-inline-padding: 4px 10px;
-
-// Box
-$box-bg-color: $color-grey-200;
-$box-corner-radius: $border-radius-sm;
-$box-border-color: $color-grey-500;
-$box-border-width: 1px;
-$box-border-style: solid;
-$box-padding: 20px;
-$box-footer-padding: 22px 22px 0px 22px;
-$box-footer-margin: 22px -20px 0px -20px;
-
-// Breadcrumbs
-$breadcrumb-right-margin: 22px;
-$breadcrumb-divider: "/";
-$breadcrumb-divider-color: rgba($color-grey-800, 0.5);
-$breadcrumb-divider-right-margin: -11px;
-
-// Checkbox
-$checkbox-box-border-color-standard: 0 0 0 2px rgba($color-brand-300, 0.5);
-$checkbox-box-border-color-hover: 0 0 0 2px $color-brand-300;
-$checkbox-box-border-color-active: 0 0 0 2px $color-brand-300;
-$checkbox-box-border-color-disabled: 0 0 0 2px $color-grey-500;
-$checkbox-box-partial-color: rgba($color-brand-300, 0.5);
-$checkbox-box-bg-color-active: $color-brand-300;
-$checkbox-box-label-text-color: $color-grey-800;
-$checkbox-box-label-disabled-text-color: $color-grey-500;
-$checkbox-checkmark-color: $color-grey-100;
-$checkbox-corner-radius: 1px;
-
-// Chips
-$chips-bg-color: $color-brand-100;
-$chips-corner-radius: $border-radius-sm;
-$chips-text-color: $color-grey-800;
-$chips-padding: 4px 6px;
-$chips-small-font-size: 1.2rem;
-$chips-small-padding: 2px 4px;
-
-// Fields
-$fields-corner-radius: $border-radius-sm;
-$fields-dropdown-arrow-color: "black";
-$fields-padding: 0px 10px;
-
-$fields-placeholder-text-color: rgba($color-grey-800, 0.6);
-$fields-default-bg: rgba($color-brand-300, 0.08);
-$fields-default-text-color: $color-grey-800;
-$fields-default-border: none;
-$fields-focus-border: inset 0 -2px rgba($color-brand-300, 1);
-$fields-disabled-bg: rgba($color-grey-800, 0.05);
-$fields-disabled-text-color: rgba($color-grey-800, 0.25);
-$fields-disabled-border: inset 0 -2px rgba($color-grey-800, 0.05);
-$fields-error-border: inset 0 -2px $color-status-danger;
-
-$fields-sizes-default-width: 250px;
-$fields-sizes-sm-height: 24px;
-$fields-sizes-md-height: 32px;
-$fields-sizes-default-height: 36px;
-$fields-sizes-lg-height: 44px;
-
-// Icons
-$icon-size-xs: 1.2rem;
-$icon-size-sm: 1.8rem;
-$icon-size-default: 2.4rem;
-$icon-size-md: 3.6rem;
-$icon-size-lg: 4.8rem;
-
-// Links
-$links-text-color: $color-brand-300;
-$links-hover-text-color: $color-brand-400;
-$links-disabled-text-color: rgba($color-grey-800, 0.2);
-$links-secondary-text-color: $color-grey-700;
-$links-secondary-hover-text-color: $color-grey-800;
-$links-inverse-text-color: $color-grey-100;
-$links-inverse-hover-text-color: rgba($color-grey-100, .4);
-$links-small-text-size: 1.2rem;
-
-// Loader
-$loader-color: $color-brand-300;
-$loader-color-inverse: $color-grey-100;
-
-// Menus
-$menu-bg-color: $color-grey-100;
-$menu-corner-radius: $border-radius-sm;
-$menu-shadow: 0px 4px 8px rgba(0, 0, 0, 0.16);
-$menu-border-color: $color-grey-400;
-$menu-border-width: 2px;
-$menu-border-style: solid;
-$menu-line-break-size: 2px;
-$menu-line-break-color: $color-grey-400;
-
-// Buttons
-$button-font-weight: inherit;
-$button-font-size: inherit;
-$button-corner-radius: $border-radius-sm;
-$button-sizes-sm-height: 24px;
-$button-sizes-sm-font-size: 12px;
-$button-sizes-sm-min-width: 70px;
-$button-sizes-md-height: 32px;
-$button-sizes-md-font-size: inherit;
-$button-sizes-md-min-width: 90px;
-$button-sizes-default-height: 36px;
-$button-sizes-default-font-size: inherit;
-$button-sizes-default-min-width: 120px;
-$button-sizes-lg-height: 44px;
-$button-sizes-lg-font-size: 16px;
-$button-sizes-lg-min-width: null;
-
-$button-solid-default-bg-color: $color-brand-300;
-$button-solid-default-text-color: $color-grey-100;
-$button-solid-default-inverse-bg-color: $color-brand-300;
-$button-solid-default-inverse-text-color: $color-grey-100;
-$button-solid-focus-bg-color: $color-brand-400;
-$button-solid-focus-text-color: $color-grey-100;
-$button-solid-focus-inverse-bg-color: $color-brand-400;
-$button-solid-focus-inverse-text-color: $color-grey-100;
-$button-solid-disabled-bg-color: rgba($color-grey-800, .12);
-$button-solid-disabled-text-color: rgba($color-grey-800, .5);
-$button-solid-disabled-inverse-bg-color: rgba($color-grey-100, .12);
-$button-solid-disabled-inverse-text-color: rgba($color-grey-100, .5);
-
-$button-ghost-default-bg-color: rgba($color-brand-300, .16);
-$button-ghost-default-text-color: $color-brand-300;
-$button-ghost-default-inverse-bg-color: rgba($color-brand-300, .16);
-$button-ghost-default-inverse-text-color: $color-grey-100;
-$button-ghost-focus-bg-color: rgba($color-brand-400, .26);
-$button-ghost-focus-text-color: $color-brand-400;
-$button-ghost-focus-inverse-bg-color: rgba($color-brand-400, .26);
-$button-ghost-focus-inverse-text-color: $color-grey-100;
-$button-ghost-disabled-bg-color: rgba($color-grey-800, .12);
-$button-ghost-disabled-text-color: rgba($color-grey-800, .5);
-$button-ghost-disabled-inverse-bg-color: rgba($color-grey-100, .12);
-$button-ghost-disabled-inverse-text-color: rgba($color-grey-100, .5);
-
-$button-outline-default-bg-color: transparent;
-$button-outline-default-text-color: $color-brand-300;
-$button-outline-default-border: solid 1px $color-brand-300;
-$button-outline-default-inverse-bg-color: transparent;
-$button-outline-default-inverse-text-color: $color-grey-100;
-$button-outline-default-inverse-border: solid 1px $color-brand-300;
-$button-outline-focus-bg-color: rgba($color-brand-400, .26);
-$button-outline-focus-text-color: $color-brand-400;
-$button-outline-focus-border: solid 1px $color-brand-400;
-$button-outline-focus-inverse-bg-color: rgba($color-brand-400, .26);
-$button-outline-focus-inverse-text-color: $color-grey-100;
-$button-outline-focus-inverse-border: solid 1px $color-brand-400;
-$button-outline-disabled-bg-color: rgba($color-grey-800, .12);
-$button-outline-disabled-text-color: rgba($color-grey-800, .5);
-$button-outline-disabled-border: solid 1px rgba($color-grey-800, .5);
-$button-outline-disabled-inverse-bg-color: rgba($color-grey-100, .12);
-$button-outline-disabled-inverse-text-color: rgba($color-grey-100, .5);
-$button-outline-disabled-inverse-border: solid 1px rgba($color-grey-100, .5);
-
-$button-text-default-bg-color: transparent;
-$button-text-default-text-color: $color-brand-300;
-$button-text-default-inverse-bg-color: transparent;
-$button-text-default-inverse-text-color: $color-grey-100;
-$button-text-focus-bg-color: rgba($color-brand-400, .26);
-$button-text-focus-text-color: $color-brand-400;
-$button-text-focus-inverse-bg-color: rgba($color-brand-400, .26);
-$button-text-focus-inverse-text-color: $color-grey-100;
-$button-text-disabled-bg-color: transparent;
-$button-text-disabled-text-color: rgba($color-grey-800, .5);
-$button-text-disabled-inverse-bg-color: transparent;
-$button-text-disabled-inverse-text-color: rgba($color-grey-100, .5);
-
-// Modals
-$modal-overlay-color: rgba($color-grey-400, 0.9);
-$modal-corner-radius: $border-radius-sm;
-$modal-modal-container-bg-color: $color-grey-100;
-$modal-modal-container-border-color: $color-grey-400;
-$modal-modal-container-border-width: 2px;
-$modal-modal-container-border-style: solid;
-$modal-modal-container-close-icon-color: $color-grey-800;
-
-// Navbar
-$navbar-bg-color: $color-brand-500;
-$navbar-height: 60px;
-$navbar-item-text-color: $color-grey-100;
-$navbar-item-hover-text-color: rgba($color-grey-100, 0.4);
-$navbar-item-active-text-color: $color-grey-100;
-$navbar-item-active-border-color: $color-brand-300;
-$navbar-responsive-bg-color: #19375F;
-$navbar-responsive-menu-bg-color: #334D71;
-
-// Progress
-$progress-bg-color: $color-grey-400;
-$progress-corner-radius: $border-radius-sm;
-$progress-sizes-sm-height: 10px;
-$progress-sizes-default-height: 16px;
-$progress-sizes-md-height: 20px;
-$progress-sizes-lg-height: 24px;
-$progress-bar-spacer: 2px;
-$progress-bar-bg-color: $color-brand-300;
-
-// Radio
-$radio-circle-border-standard: 0 0 0 2px rgba($color-brand-300, 0.5);
-$radio-circle-border-hover: 0 0 0 2px $color-brand-300;
-$radio-circle-border-active: 0 0 0 2px $color-brand-300;
-$radio-circle-border-disabled: 0 0 0 2px $color-grey-500;
-$radio-circle-bg-color-active: $color-brand-300;
-$radio-label-text-color: $color-grey-800;
-$radio-label-disabled-text-color: $color-grey-500;
-
-// Switch
-$switch-circle-border-color-standard: rgba($color-brand-300, 0.5);
-$switch-circle-border-color-hover: $color-brand-300;
-$switch-circle-border-color-active: $color-status-success;
-$switch-circle-border-color-disabled: $color-grey-500;
-$switch-circle-bg-color-standard: $color-grey-100;
-$switch-circle-bg-color-active: $color-status-success;
-$switch-circle-bg-color-disabled: $color-grey-100;
-$switch-circle-bg-color-active-disabled: $color-grey-500;
-$switch-slider-standard-color: $color-grey-500;
-$switch-slider-active-color: rgba($color-status-success, 0.45);
-$switch-slider-disabled-color: $color-grey-400;
-$switch-label-text-color: $color-grey-800;
-$switch-label-disabled-text-color: $color-grey-500;
-
-// Table
-$table-border-radius: $border-radius-md;
-$table-row-default-height: 48px;
-$table-row-condensed-height: 40px;
-$table-row-expanded-height: 56px;
-$table-row-border-bottom: solid 1px $color-grey-400;
-$table-row-padding: 0px 10px;
-$table-header-text-color: $color-grey-800;
-$table-header-text-size: 1.2rem;
-$table-header-text-transform: uppercase;
-$table-header-text-weight: normal;
-$table-header-padding: 10px;
-$table-header-border-bottom: solid 2px $color-grey-400;
-$table-header-sort-text-weight: bold;
-$table-footer-bg-color: $color-grey-400;
-$table-striped-row-bg-color: $color-grey-200;
-$table-cards-spacing: 0px 10px;
-$table-cards-border: solid 1px $color-grey-400;
-
-
-// Tabs
-$tabs-font-size: 1.6rem;
-
-$tabs-primary-bg-color: #F3F8FF;
-$tabs-primary-padding: 20px 0px 0px 30px;
-$tabs-primary-tab-item-height: 40px;
-$tabs-primary-tab-item-padding: 0px 20px;
-$tabs-primary-tab-item-corner-radius: $border-radius-md;
-$tabs-primary-tab-item-text-color: $color-brand-300;
-$tabs-primary-tab-item-active-bg-color: $color-grey-100;
-$tabs-primary-tab-item-active-text-color: $color-grey-800;
-$tabs-primary-tab-item-disabled-text-color: rgba($color-grey-800, 0.2);
-
-$tabs-primary-vertical-width: 300px;
-$tabs-primary-vertical-padding: 12px 0px 40px 0px;
-$tabs-primary-vertical-tab-item-height: 40px;
-$tabs-primary-vertical-tab-item-margin: 2px 16px;
-$tabs-primary-vertical-tab-item-padding: 0px 14px;
-$tabs-primary-vertical-tab-item-corner-radius: $border-radius-lg;
-$tabs-primary-vertical-tab-item-bg-color: null;
-$tabs-primary-vertical-tab-item-text-color: inherit;
-$tabs-primary-vertical-tab-item-border: null;
-$tabs-primary-vertical-tab-item-active-border: inset 0px 0px 0px 1px rgba($color-brand-300, .1);
-$tabs-primary-vertical-tab-item-active-bg-color: rgba($color-brand-300, 0.1);
-$tabs-primary-vertical-tab-item-active-text-color: $color-grey-800;
-$tabs-primary-vertical-tab-item-active-text-weight: bold;
-$tabs-primary-vertical-tab-item-hover-border: inset 0px 0px 0px 1px rgba($color-brand-400, .1);
-$tabs-primary-vertical-tab-item-hover-bg-color: null;
-$tabs-primary-vertical-tab-item-hover-text-color: $color-brand-400;
-$tabs-primary-vertical-tab-item-hover-text-weight: null;
-
-$tabs-secondary-border: inset 0px -2px rgba($color-grey-700, .05);
-$tabs-secondary-tab-item-height: 40px;
-$tabs-secondary-tab-item-padding: 0px 20px;
-$tabs-secondary-tab-item-text-color: $color-brand-300;
-$tabs-secondary-tab-item-active-text-color: $color-grey-800;
-$tabs-secondary-tab-item-active-border: inset 0px -2px $color-brand-300;
-
-$tabs-section-header-text-color: #9a9a9a;
-$tabs-section-header-text-size: 1.2rem;
-$tabs-section-header-text-transform: uppercase;
-
-// Tags
-$tags-corner-radius: $border-radius-pill;
-$tags-padding: 0px 10px;
-$tags-margin: 3px;
-$tags-height: 28px;
-$tags-icon-size: 2rem;
-$tags-icon-color: $color-grey-800;
-$tags-bg-color: $color-brand-100;
-$tags-text-color: $color-grey-800;
-$tags-active-bg-color: $color-brand-200;
-$tags-disabled-bg-color: rgba($color-grey-800, .1);
-$tags-disabled-text-color: rgba($color-grey-800, .4);
-$tags-small-text-size: 1.2rem;
-$tags-small-padding: 0px 8px;
-$tags-small-height: 22px;
-$tags-small-icon-size: 1.6rem;
-$tags-outlined-text-color: $color-grey-800;
-$tags-outlined-border: 1px solid $color-brand-300;
-$tags-outlined-bg-color: transparent;
-$tags-outlined-hover-bg-color: rgba($color-brand-300, 0.20);
-$tags-outlined-active-text-color: $color-grey-100;
-$tags-outlined-active-bg-color: rgba($color-brand-300, 1);
-$tags-outlined-disabled-text-color: rgba($color-grey-800, .4);
-$tags-outlined-disabled-bg-color: rgba($color-grey-800, .1);
-$tags-outlined-disabled-border-color: rgba($color-grey-800, .4);
-
-// Titles
-$titles-height: 52px;
-$titles-bar-corner-radius: $border-radius-sm;
-$titles-bar-bg-color: $color-grey-500;
-$titles-bar-width: 2px;
-$titles-bar-wider-width: 4px;
-
-// Toggles
-$toggle-corner-radius: $border-radius-sm;
-$toggle-bg-color: rgba($color-brand-300, .08);
-$toggle-text-color: $color-brand-300;
-$toggle-text-hover-color: $color-brand-500;
-$toggle-text-size: 1.4rem;
-$toggle-padding: 0px 14px;
-$toggle-height: 36px;
-$toggle-active-text-color: $color-grey-800;
-$toggle-active-bg-color: $color-grey-100;
-$toggle-active-corner-radius: $border-radius-sm;
-$toggle-active-border-width: 2px;
-$toggle-active-border-color: $color-brand-300;
-$toggle-disabled-text-color: rgba($color-grey-800, 0.2);
-$toggle-small-padding: 0px 10px;
-$toggle-small-height: 28px;
-$toggle-inverse-text-color: $color-brand-200;
-$toggle-inverse-bg-color: rgba($color-grey-100, 0.1);
-$toggle-inverse-active-text-color: $color-grey-100;
-$toggle-inverse-active-bg-color: transparent;
-
-// Tooltips
-$tooltip-text-color: $color-grey-100;
-$tooltip-bg-color: rgba(#303030, 0.9);
-$tooltip-corner-radius: $border-radius-sm;
-$tooltip-text-size: 1.2rem;
-$tooltip-padding: 6px 8px;
-$tooltip-small-text-size: 1rem;
-$tooltip-small-padding: 4px 6px;
-
-// Typography
-$headline-color: $color-grey-800;
-$headline-font-family: $font-family-sans-serif;
-$paragraph-color: $color-grey-800;
-$paragraph-font-family: $font-family-sans-serif;
-
-$h1-font-size: 2.8rem;
-$h2-font-size: 2.6rem;
-$h3-font-size: 2.0rem;
-$h4-font-size: 1.6rem;
-$h5-font-size: 1.4rem;
-$h6-font-size: 1.2rem;
-$p-font-size: 1.4rem;
-
-$h1-font-weight: 600;
-$h2-font-weight: 600;
-$h3-font-weight: 600;
-$h4-font-weight: 600;
-$h5-font-weight: 600;
-$h6-font-weight: 400;
-$p-font-weight: 400;
-
-$line-height-compact: 1.25;
-$line-height-normal: 1.5;
-$line-height-expanded: 1.75;
\ No newline at end of file
diff --git a/_suggestions_config.yml b/_suggestions_config.yml
deleted file mode 100644
index 168ab9f86b..0000000000
--- a/_suggestions_config.yml
+++ /dev/null
@@ -1,48 +0,0 @@
-# Copyright Vespa.ai. All rights reserved.
-
-search:
-  namespace: "term"
-  doc_type: "term"
-  do_feed: true
-  do_index_removal_before_feed: true
-  feed_endpoints:
-    # This config needs to be the same in all files starting with "_" in:
-    # vespa-engine/documentation, vespa-engine/pyvespa, vespa-engine/sample-apps,
-    # vespaai/blog and vespaai/cloud-site
-    #
-    # vespacloud-docsearch | prod.aws-us-east-1c
-    - url: https://b671e1db.b68a8c0d.z.vespa-app.cloud/
-      indexes:
-        - suggestions_index.json
-    # vespacloud-docsearch | prod.aws-eu-west-1a
-    - url: https://ed053d52.b68a8c0d.z.vespa-app.cloud/
-      indexes:
-        - suggestions_index.json
-    # vespacloud-docsearch | prod.gcp-us-central1-f
-    - url: https://a341952a.b68a8c0d.z.vespa-app.cloud/
-      indexes:
-        - suggestions_index.json
-    # vespacloud-docsearch | prod.gcp-us-central1-a
-    - url: https://ba97230a.b68a8c0d.z.vespa-app.cloud/
-      indexes:
-        - suggestions_index.json
-    # vespacloud-docsearch | prod.aws-euc1-az1
-    - url: https://af1993cf.b68a8c0d.z.vespa-app.cloud/
-      indexes:
-        - open_index.json
-    # vespacloud-docsearch | prod.aws-us-east-1
-    - url: https://b42c9416.b68a8c0d.z.vespa-app.cloud/
-      indexes:
-        - suggestions_index.json
-    # vespacloud-docsearch.cloud-enclave | prod.aws-us-east-1c
-    - url: https://cd560c23.b68a8c0d.z.vespa-app.cloud/
-      indexes:
-        - suggestions_index.json
-    # vespacloud-docsearch.cloud-enclave | prod.aws-eu-west-1a
-    - url: https://b24dd625.b68a8c0d.z.vespa-app.cloud/
-      indexes:
-        - suggestions_index.json
-    # vespacloud-docsearch.cloud-enclave | prod.gcp-us-central1-f
-    - url: https://b4f1a714.b68a8c0d.z.vespa-app.cloud/
-      indexes:
-        - suggestions_index.json
diff --git a/_suggestions_reference_config.yml b/_suggestions_reference_config.yml
deleted file mode 100644
index 093c9bc17d..0000000000
--- a/_suggestions_reference_config.yml
+++ /dev/null
@@ -1,48 +0,0 @@
-# Copyright Vespa.ai. All rights reserved.
-
-search:
-  namespace: "term-reference"
-  doc_type: "term"
-  do_feed: true
-  do_index_removal_before_feed: false
-  feed_endpoints:
-    # This config needs to be the same in all files starting with "_" in:
-    # vespa-engine/documentation, vespa-engine/pyvespa, vespa-engine/sample-apps,
-    # vespaai/blog and vespaai/cloud-site
-    #
-    # vespacloud-docsearch | prod.aws-us-east-1c
-    - url: https://b671e1db.b68a8c0d.z.vespa-app.cloud/
-      indexes:
-        - suggestions_reference_index.json
-    # vespacloud-docsearch | prod.aws-eu-west-1a
-    - url: https://ed053d52.b68a8c0d.z.vespa-app.cloud/
-      indexes:
-        - suggestions_reference_index.json
-    # vespacloud-docsearch | prod.gcp-us-central1-f
-    - url: https://a341952a.b68a8c0d.z.vespa-app.cloud/
-      indexes:
-        - suggestions_reference_index.json
-    # vespacloud-docsearch | prod.gcp-us-central1-a
-    - url: https://ba97230a.b68a8c0d.z.vespa-app.cloud/
-      indexes:
-        - suggestions_reference_index.json
-    # vespacloud-docsearch | prod.aws-euc1-az1
-    - url: https://af1993cf.b68a8c0d.z.vespa-app.cloud/
-      indexes:
-        - open_index.json
-    # vespacloud-docsearch | prod.aws-us-east-1
-    - url: https://b42c9416.b68a8c0d.z.vespa-app.cloud/
-      indexes:
-        - suggestions_reference_index.json
-    # vespacloud-docsearch.cloud-enclave | prod.aws-us-east-1c
-    - url: https://cd560c23.b68a8c0d.z.vespa-app.cloud/
-      indexes:
-        - suggestions_reference_index.json
-    # vespacloud-docsearch.cloud-enclave | prod.aws-eu-west-1a
-    - url: https://b24dd625.b68a8c0d.z.vespa-app.cloud/
-      indexes:
-        - suggestions_reference_index.json
-    # vespacloud-docsearch.cloud-enclave | prod.gcp-us-central1-f
-    - url: https://b4f1a714.b68a8c0d.z.vespa-app.cloud/
-      indexes:
-        - suggestions_reference_index.json
diff --git a/amplify.yml b/amplify.yml
deleted file mode 100644
index 28cefb2d94..0000000000
--- a/amplify.yml
+++ /dev/null
@@ -1,21 +0,0 @@
-version: 1
-frontend:
-    phases:
-        preBuild:
-            commands:
-                - 'unset LC_ALL'
-                - 'export LANG=C.UTF-8'
-                - 'gem install bundler'
-                - 'bundle install'
-        build:
-            commands:
-                - 'unset LC_ALL'
-                - 'export LANG=C.UTF-8'
-                - 'bundle exec jekyll build'
-    artifacts:
-        baseDirectory: _site
-        files:
-            - '**/*'
-    cache:
-        paths:
-            - 'vendor/bundle/**/*'
diff --git a/assets/graph-image.png b/assets/graph-image.png
new file mode 100644
index 0000000000000000000000000000000000000000..0d09f733c61c993f80b0595ff7b9583c4996c324
GIT binary patch
literal 72174
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
-  
-  
-  prod.aws-us-east-1
-  
-  
-  use1-az1
-  
-  
-  use1-az2
-  
-  
-  Container cluster
-  
-  
-  Content cluster
-
diff --git a/assets/img/az-multi-content.svg b/assets/img/az-multi-content.svg
deleted file mode 100644
index 2a793267c9..0000000000
--- a/assets/img/az-multi-content.svg
+++ /dev/null
@@ -1,38 +0,0 @@
-
-  
-  
-  prod.aws-us-east-1
-  
-  
-  use1-az1
-  
-  
-  use1-az2
-  
-  
-  Content cluster
-  
-  
-  Group 0
-  
-  
-  
-  
-  
-  Group 1
-  
-  
-  
-  
-  
-  Group 2
-  
-  
-  
-  
-  
-  Group 3
-  
-  
-  
-
diff --git a/assets/img/az-single-cluster.svg b/assets/img/az-single-cluster.svg
deleted file mode 100644
index fee1bcda71..0000000000
--- a/assets/img/az-single-cluster.svg
+++ /dev/null
@@ -1,14 +0,0 @@
-
-  
-  
-  prod.aws-us-east-1c
-  
-  
-  use1-az1
-  
-  
-  Container cluster
-  
-  
-  Content cluster
-
diff --git a/assets/img/az-single-content.svg b/assets/img/az-single-content.svg
deleted file mode 100644
index d2ffde48df..0000000000
--- a/assets/img/az-single-content.svg
+++ /dev/null
@@ -1,35 +0,0 @@
-
-  
-  
-  prod.aws-us-east-1c
-  
-  
-  use1-az1
-  
-  
-  Content cluster
-  
-  
-  Group 0
-  
-  
-  
-  
-  
-  Group 1
-  
-  
-  
-  
-  
-  Group 2
-  
-  
-  
-  
-  
-  Group 3
-  
-  
-  
-
diff --git a/assets/img/block-window.png b/assets/img/block-window.png
index 54b5fbeb92565c5aad702e7bc4deb8460eada573..86407ba457ff7812bfdd8ecb03605392b2d7ff6e 100644
GIT binary patch
literal 40409
zcmeEtWl&ws(k?DRgF|o$?k*b*?t$R$PH=a3cMtCF4#9%EyX(eX?#}t{Id$Ie|NVEX
zW>rniT6@+^ub%FHx}P1UASaIW3GWja7#NbI#JBHYV32fRVBqd>Frbl_!8IK)uuoOy
zA|eWsA|k{J_O>SGR>ojp5@AUxu*youXucmFwmk6kBpB3roX7OOiBlqRI@W>Yf>b0h
z{{BnqTD_Q}-=Hu=u|zSn5Qr^>=+xApXcwT>G3K0V^+fiyw@}x-4m({o_QsyN$Fh7(
z-apnx(!d1s?7sbCQzP_s)KBIbTMYP&{$;2K1rll)oUaBP3*V+F6cY^%P9aVGg8>Kx
zGqdkB=fFMP%=u7N+`3|I1rsEpqP%2jJ*FZOzr$1|pMU@>6+Leot^1bXiEEsWLyx*2
zl90(Zo}ZA(G=MXfDE}>$@(&Fd>M(vZ7d%)Up@pfDL&6l75o02K5T75a@h$I9S^3GW
zyvPG+v62|L{gKD9K;RDf8@-7?+t-i;^8Mp_8Tel5SZb-=z1N(@t8NN_EK5Q^*bn=<
zB}PQ;JMtU0P5*
zGLzJtV*>vEok%A0s&BQFmciUR%N_A^5~YB&$>hOF+33*hZ7ZRNw9h5e=0~Z8Xj`b@qk=tKhSg
zo`TJF0|&c)s^{T9Ix+v%7DwuFC?$ik;Gh#8ZGy7sSQ^XVpVG^JD!fPJ0jA
zOAPVbf4~>3P;pa2d8
zV-J-p${-0`p9n_ir&3{!Sbn2V>LN13nAgD&c}!GT;UV$FB;k4n|p
zex&mR_ojSDeW!be@rL_|BNmw=RYUiLvx6ehC&-KZ8T&K%XTW77lUyR@33^s&?>9o`
z5H*`_BH;~kL#ps%6;IU7-|K~3JibD
z8B7jpeC3v3OckN3Yznt3d(;QC7fO89ToR%sZOZaADQ(#r(w!0nzqZYS**NHfRKD@b
zoEAmVP}3~ah*Hz5mMQa8&dG}(!8mdNqI6^gq`E~uDk}2Ez8}!qD%&V`<-H0SM~An6
z(k)adl-zInWBMbwtl6UML}eapUfcqQ9lcSZ(b%GWo}sKWQzT=TFNC-F=fpJk!@Rx|
z`W@39=E=)k``i#48@ng_H#}bU3CoVT0?RhbBC}+R>}mYzUly$v@C{Op6BYndDrU7v
zHZks92t#E}?NxcrU!AJ$njTt13Y6bXv*Z#c$h6}blsFZem77Iel;x!vB^c$ag@9fB
ziaDh@72P7;N*@7!c|r}nFacNr`XZPjP5M>(0ox*E;c*$$-}CYEH3zv2i|op-7FBi=0wNYDH*wQeq8?W$U^Df#|{j?chYd0}sO;&GG4_Nl)Ugw+g%yNr(qD25i
za79#NWnpd7ozuf$C_v8D^V0EJ>v;Jh$o0t`)7{B~)?M6P)m7kr`bzTJ{1W%B@kV=l^&;dB
z;d1rFC8e_Wpzd@z{~h_A_KguL7P1eL1|l0O9V#4h)qjilBiC982}vXIM6J2LlIIeX
zX`N|`={II9W?I9lfwF;52AaEfesI}HQH
zDZ}*Jblg}mQ8Cd1I3GdiW2M`X6gB8&Xkx9|YXoc9d@efFwt>Jt#%%#r)gO*
zF+-nIc<9%=9F_5jt40G^u#KBcrl+<`qz%ZQgjizF)3wvy?9}J#3`a4O5mH?CQ}?XK
z+>%cdUT!{(eYyyb<~#3bbc3W-r+ujMSD~mlQLAj{@wuDA+hm7bh+inGdb7^jZ;N*V
zE{Ip#jxG-CCI~zR08x;j>!5f1+rMOTDu0ANDP9^rh-F0aa(dJ5Vd~J=(b3R8*M+qo
zpUA5SBz>^ea@#q$)OuZA(6`X{(YrLYSpyV)Da32Cw9uPrI#A~~)ZDJ$Q><1eE+_v0
z5}(JO9WkQN*{+v1JB&^+Vvb-o(*kHR%I3;BRJ@i3R-Fu+UoHkMqE3FDOnN|hJl{WG
zSXVKZP_KrprgQ?$t(C10C&zM5Pv=g;Pr5F!FMtB5&RfErB3^GocL4)PKLZN%_on(p
z03n^o%^d0M7uLTxF$uuM$;Goj&^|`9^1;Pm*!8IaH->&)NyY0JPDsoN>EKx)vQ|9wY;c1<+;jOx20@5Z_lXa)v`Ue
zJ2OAcTJ$&wUX60#?6lEo8|ZR6zkglrbRh-au4}tCcgHMpdNQ7GoW3kS3=vhhg?gzy
zO1vvB8{Q4EcYpcVn6usVaqqFrJ(YY+fDKsd#r83JzcQx!px!rMX00^6>tEQ$x%J|UTCT1uWq7$Jj-?4nFa(Djj(gfrg6dok(miG~O
z&%4Y&8G_77&EXLc_wnZFIzxDuep217w%&y30%S_$q@vpW&MZ{~6T*~}f!Z4Pc9aW+
z_FaJlGw;2X0Z>fce)v*De1sJrUQpM<<`xlT=_nwHHLiHM7Zg^26GQF8xqz)MNjRCj
zXQj_mioTz5b!qYox4sIeDkM9}AOFPC~9NW;J;>cJauUH0JE$_Ey%`zTsY8ri5r57z`!h`;TT2D`Zrh9tML~(9{3&&!3}(szYF?88lg>GXJlM
zKhar1k_$rocVm|Jg^(UoL1GpA$1u5|Uy$`bmn%VR0(x24a+&_1uuyJCht~50kg>3^
z&^y1pq9U;!*roYZE|cnXq4MT*F@YxK{bGly(0bg+lAG8$9yl|eSYwhL<7
z8{3YnV7`YnhXvz4f=Rv`nWu|(;A`?}?GRyt2e5Oonuuo*1-$9)XzoWYa8A43c?45c
zi)6LgHbzctmv3H>+XH*{m>b(ndn}7rb%N)V?g{AgaY|6>CF?gJ)|>E+Tn{IhPQC71
zS$X*z7BI~;JI=Q2NsQ3*QfOpy^5I|0tL52bHJwP*v}qkkQC(?=^Zl$g{TtgaJ|2zx
zdYJ5VjP0U$B!P0YdDC07c+>T+#{JG>x!t9D^9;CDE7D}Oyt9Zxup0oj>V10xKbg6+
zW6+Je)MA&9@BO%aeLE{Jt*@V_+wE8(n@i$_4LsD!b@F>(z8WH8EY5Z>)3P6-Xk^{5
z%j?-eM*;X2*(*qpi?7mdm$I
zsbtgjbf88Daq|u0j-h@Pwb(yp@!Y-H3zcfNN
zgF{kOG)N|uNhqDe`h+zs_+iXbOcoX`6xol+`%wr636C5S1~E2*v1^G8y}I*0-I@1x
zN@O3jixhu<2)FmA!~NYDfn;i>qTv$7{M*Euzd`{W1hC_U$_#v`ImJ5DNkW3jFv@JI
z9G~}A0!lIHuGeen7d%Q}_ea+&=+n$*vH#}uxVJ)i_|^)Ruiqbhj18sSiuxRkh)bFp
zY?|gU!7aYw0R*Twn8o4S50k7gx>o6S>wtE{@N`x`xi|R5c5dx6G75KO-C6g?JB$6E
zgxg`3i=(n_=UuXv^Rlu1^$4YO3?bkaBVW_D^N0My>2iJX>+8*=O1vU~r}2y9+Q%Ev
zXZ4BaykT}PFNCnJdMKKJ_U3RB+csNHLr8|U?d^W`yauF^F>9cipLt?0FOM8idN!zm
zH6MopAEl?bcOlN5_WM%YAFnsY$1{a(8`)m>>aOL+KdZMk%%*>d(sSS^@%!V=NJ61wI_;RfdUva`u;e4TQCaO&m)jLLEEV|Y~)c%0R`x23qGx7e(=HOgdg$WHJA
z`FhVjx$OQPUkAiG@SVaSf&wd~i|*NE&y3tYRAtcdLAa;utJc6otl8AMe{$)gG=u95#2Q_%=F40Zy4n
z?kf9=;lstc)0aWkBEzBRV2#ON4&8da5F+rm&sy+{gY&Aom6M9FTc7fw1Vx;BZdFjNVh(!Er3Xt6Q?N!n#4A1K4r`%^79i9NP
zSeOkvSBCx)C%*gkc>v!8WU0%%;+@-Hg}WrI-Pqqa=GHFvLo*x*$81VK=Uip>MPEB@
zZEX$4>zM0G0xR5fvItN2p4?7@%U`fMB03=j0M->|^&0`vl^TsKiN3|v>mPr0tSj>5
z?372S!}X!>(Ie;nWPVg3fbn@f;P8fhQ7gaS^#P<{qOJ2i`Bfer<({>UJ`=~oT0R}8
zed|bv!__mPZkmg2MZu!$;A~+cVEL~&);&}lh+>=ctcHV#ik#W6yF?$c2#g&HUC$@9
z02YJRcA^;W0~~;Gz%+cW|yA8Y*A5S1-X}sMR)aI=WD<88#uhXG6gf#x?1m(_en#N2|k4YDI
zmd&7-tMN-@n|?aUA9k;2N5FGRLIT-|aap504!4Y?q}?7o3>B^Sl-Lui+}$?8#wK7Z
z(S_otl!{6+VWro@x{H@weNUe|ZH~A2urj#vkV-V~1((noQ(k&-DH*~IQG?cGwLdM+S$?4+=|veGo7+G7Y{MVG_dNYvytiH9J%
z>S7__`fk93BsM%x9A6S$>kYe;9R!1-AvZ7N6M0D|JD&9LMkLWmrV9$Ii%r#K
zEe>#t7}O^-uXcw#co*B8cT`3bh$7C;tN_~Z_4GgG4e5~@;HK8LaN#Ry5!H5K0}lB%
z_*ktQXHqu~W*E7|Cx2@KZ8Qj4xG`%zXYWTd6*
zCo9n@7FKy9_2P&KU#Z<2MQk|DmB@(Y{RTmd%fK)?t@J2
zqFGHWGKy%0d5yv(X(`3=>pbEJd@aCFD>RauLv)bTx7s~bAzx+
zoqgS|2C(-7Q21-=42kB4E4Q#ig)~&wp?3t5U-i-nvteDQJ-#%|PGs?pqRaE?^8)VX
zl$w}q)w?eH5T3Jp=djK=jOU9%-|!RmsJQMqteeSdb!n?YgBPeD;0W4DclJSKsnn73
zj(TJ%e+eW`@nOva>);}5A5GA_PNgM&*2PEL34n~O|L|?+mad*>SEcRfOvDU+?r{gp
z%(Wwfi5_=$oH$mGhBojv$NQN&_YfBf|BN);)f*%#jS(KZAWBNCjqR+JA2mQF-D5J8
z;re!SDrw*=EDuF09@c}#mfIS7&kANFhF&84!D)4O(`evlRL2e`VgC>!9t1fQK6Y7g
z7Y61SEKM?ve2P%G{JsTucsh)Jz8{nU$kYcW&mM4GK@pU^XYNoWPy|061#7)Xpacsw
z7I;PUy#>T_^|qRAH@oaXcK0}>BONXOoL=q-jug2AtfmO=E`VsEtB0O2vfDWzh4TQy
z@SRhvJNJj5U`UgNd3dI)d&(EyrEPtbuc#B#tu->l+g|ICUDwD_5IS|kj3>)zf|c$f
zE4>RSFi15fWA2jlx?LR;Sw0mkVlSa`SPSWbTfyr2Y}E^_P?Ll;{wi43*oD5MY!-LwCe91{(!z=nB22-
z%B__kGMx@l{6N0K3IkdBuvwvAzZNjhQ(eCCw$3Ow$+AW?WwFzVcg6~vb~8|^GWe#Z
zhlG$Fl*;XU$Z-umF%_?OPKRiMs+J?x!|Hx>Fup?Q4-3PyX6j`Idsmz{=~O}4+e4DQ
z$mGg6_uyp#Sbe+G7ViCS$~G*JZJ-qEcbo-f5G9zwVGV`wUUOMlp6=|YC~bKHPe`X$
z6DeA2(N%pxt{;x3yni7!{xDx=(xFu|3nwO92}>?k!ITA^Kc>bM!IgzLSA10Cliwr-
z^7AR8qGrLLwTNO_dD$@Xj?Wd?R9LH}PT$%l
zYJOtmKQ6&wGqUnAk?hI*P_o+T1)VLN^WSi*qS)F65&j%u9HDYBI&|-@Z7Bp~X{bk2
zP0XUm5p?428@
ztO=)<8X5Vy`}Z6bBEEWn)sdFr
zEcz+IA;@KBd+1KW{q~dPzpP||TWN`@bSd|6J?*G6&8~idp7hoQVwrI=Wx)Ym8dYYb
z{_MJ5v>sYiG6lkQ1YBhYqaH?Ni0cVpyMnxBkkD7eZ(Fc_P>>Qho_(DWraak)sRMZn
z+eb^eNxd)>OV9}K9gUcmb5n;rMR)p4@RNpPa$p{OIVU&#cNgtkB=Cua)onk+Sy9_D
z=BZ24PSGUbo=N<;h<%aq3GwBWx?;Wf#$K&+U8!=
zkcV0HfJd&6hEALE(RD6r`NHx`?5{J6;^V|QA0qm+I0MH95JYs(zsA1uUO9dWNueyV
zbjuLzC(E1#Y|#RbJjnnLJi_>>-%i?4wkAimHhQG-orq#!tmjlW6a}{Y&2i;m(s3M@
z)_`9gK~XT*z~>0(t`&J=H>8I1y7P#IJ?_RFL9Q2fWZobrcd@PPpevhpxoyZshc;c$
z#*kn>2hVjEzddw3L^w*<=H9I|I3WU};)V-{hYq2dUSB9@Qc-mUU8l6*CTlna)X<7o
zwPWZPexw563~I7Ump|aU9(*#+TLKvTBZcok(9$RD*>*D!REMDK@u2*vaw`cDM-GO>
z+JKU}0F8vjj}c{_HI>YR>*6;slI7fs4npO8tid1zeklS*Cch;67v~dmL3FtI?}~1c
zqnbmgVM9g1H_*N}$fSn(z2Ne~&jbd8K_^fBG@|=&AP(y56L0bLav;JpOZ_k0js}+u
z`h{NDw7`F51VCv3!QXHoM2=(e_1izeJ1QOcC%Rs=e4l^L;}`mQD};bIEQbB~Y4wJ!R%^{7rp(oLmbe>jDUp*z4Ayn&0|rgIJn}|_U*@qH?|QWR4Dvgf
zu>Z4gMdHilLAUGM8QkyY%DhF{+3_?fw*-v4*TFn(vn;L`|>D?lm3iOeDhq7U_9(v0b8XHZ;YU)#Vo$n&mPRzmaE~MBKp;mo<+0@
zFP?_dQ8rA*-!A!21!l&6jMJ*|dDD7b`OoUaBvpwvoEbJ=krnpII0kD?3E*oJORlC)
zW)j`?^7?nW>-%tvC;fU7%ZP6@S*G<+nYE7Wp7Ww!iBD6=)bNiWCC
z?pREBP1KtlLab@u?aok90ZXO9c1!u6DJ$V%@I&O)oq1^3`I
zAdY^zH>*~^Nn^KOxcaK}n%QfDD4=g%)7aL%Fp*6O<;z{TRbn~=_i@?`U4CtBtAIVb
z&4?BXhML6+_#|4aFGWR#CZDRawFd2vADH^o#u^&I4QETD1s_cG#9349^mNC+4GRX0
zlUTg&KRtPF<55%7r`%$QOmQ!WJK2QIl@eHj6NYai_mc#$y4
zk94JTR=auKpb@#qQeD?082}`px~i1*?j}J8cifvMt#Psa`07}^bv`k?`J_YP+0Elp
z+VL`EPff=OjR8k}ApbB~P*Wij?wmYax30Mtkc3d^=3IooY3Q4|V4BJ{VYbKU-u6VI
zpr!tGcJZB@m2)hq?-RucFU}G+7RG1}^q0md#ajDf<;FkpV&nGY>=c*I(V|ZyUu*PE
zrj(@Gm5E3OI%&#AX$$GQrb%1wJ@JPUb*~Jfed#QQ04c
zaV|lO3@=y@p>O>1jHy2kfFj{VqG*^(h2qz4(b0KzuEj`6^Op)f!7h17d=BV7Ih>bg
z(z<_<8r^$&iZJe-!DKn`?PV$96#pgrixgGmB7IiSDI7B`hAa@973mW_KdPIWPmNg=
z-}5d6MXZ{C__5Y($N0TXlb33}DmJt+#r1Q%I*Z{rM620Up1os=AK}&YFB=G5t7lmq
zjrSmVp#fDy4_YasmoDKEiP1O}_13z(kAs!`S6(08@&J0*M-i_v^F=N9EW1?xW@pqJ
zOxs_}u=NvpRdu^hI{M0z>8Swh`rs-g*GQ?CKYhPhu=iA4AC4SWw14WK=M$l4xNlHy!iwShk(Cf#yLAm))G(|NgAy+5^{Le-p^
zT4H+_$Jmj=ePOHPL=$mo
zqYG)QTFMzeR&91-Y;Mek{zx@j!!dD9k82S$3TU-Y%!6<2=g;5Fr$-pH>L0<&jZ9BO
z^*}}xjk;)JwJt-sq*oPX6=7nAH!$_@XR|4!8yQKrpB>y&eZ*Q?EJAjT$J4=Bs^IY;
zjb$Zq-YlR$G-q)F?y~rSj!#tPNy~FhLy|1a4^%Cu{&#x*TbjeAPE*lP61f{t@0>R%
zPxeI7%!p
zl^i+>SsXJJJkkmeZi6GJ09`vnt9-3xz*!@aVOD^9%mS0K0gJFOx_YCc88z#qT$>wrlB{VM)S@cy
zp`_BuE!%8-4xRj`e0{^NDqtsOc*yc_b6a*1`-ds6wp!^PL-)b^Bh9#cRnITyv4
z#b>^FFnG4m9-YQ7ONrIV$6O66qOwuUF*Ww%$1X%+3uKc=x%z?X1dSQT;BwE!yFEdp
zF?Ct&E`E-uGwOK8Y?Td$SE0`DM~l*q8NY8C>$lVWgBP-M0h;I9
z%?6EFmZvI85fGbZOEuydg956n3-<>^)(<;%)%&scUUyB^a6vC!+UlA?T*6^U^L7x0XerrERWhC2x-KbQDE7@d#zs&5;Ljs9xF_K2p_N&=<)M3Ps
zJd>B4rt&b9Y^gapnTU5at&d0+a(Fi1o>75n$3_R$-8m#xPThKf#Ri?EPb*DnspVnq
zD{Z(7T>@t%Tgx3pOfv#rcauoN<~z^>3j0}+h{SE|?WAfvS){dZ$@ID;A`f4_S9`Lc
z!m|Z}t+d(%hW;-6>{kSRKGP9g|Aca`=`n3%!c-aLXEJED#1rY3(-Pz|@NyjdWba%}
z_BmbbbB~LPur;2G6}38j#&EXNs?o!*^Y2L+4D)HFb!3J)j<)kMVkm^A#eNhCCKPj#
zoxAmE)lP=lNoCH<{lP<}EOwV9Mi)r*h>KS$AaC9`1&2ccPyWTPI>lx_e`3KVaVoYa
zHUH0vl+y&=^f#irn)>qD0~l#bjYRoJaX13P!;^0zN(6vc+8>+d>doGrju&$y^lb8y
z#2ru-&e6aPb3e!^w7wAjG6y!b=BBEQTFvAwBya8U)egbcV92w|%M&;f7!54X*0C599PO=!sd7Bfi`wU8ziR#$P%Jx$^UPf>wAAfkbaBTA|j=fMF
z6k226pk=s8SxC)zrLt;8LWV7x0!oAu)LsN>ppykH61_n&_bXazqkHj*wZ5{t%g9`*
zn~k~H8TVURaS^X2j!=__^`C0X-^=)399dgu{MQw4C4wfde~U4~egn3ZQ_OQvW6o7y
zzTy=>=XGj|r*zunGekwG!i;esNeA}&yfUu}{F=4v!*X63kA-emQPNX1!#dM|{;AJy
zu8tdj-XvC7gJe1$g4q&|PjFy$mmkL9=UM{05jAa1CX)q)=HC-93+?Wx9zlB&lbul=
zi0u*GMN@ceVj*!GTSurOc4aA++88Xly!zp&RrRP9*N^n!a-l4Yh6MjD)^~a0hre0-
z!sI;R_(52=^XWXdgXO_xDG;93YTQj$x$5$d@P|^$D1z4{<03ryNDiiG*CKBqf)Lvb
zqN+L$Bm8e(P(**F6EG$K{~OE?sa<+3&R}lCm({D$``3*#hn6o_o&u9h!;O%Dn;
zh(A29);890e|rF0QYg?4odoj8;t)VvmXyftpz-?@m5nPFt}Qy_a5*33#%6t|yVQ#!
z7O$N@QZrq=GTyM5Kg!@SJH8L=e3En8P22b=kT_tf%lh66s|`_qzI?8#-Nq?a$;_-t
zm0ogO^j>thSJE9^;Qt%rg6@1%g!V{S0Y26D-h%T;`tS3e)#oxS$~ycOtm-8g!n7&iZ?Mn={_ayibsLkpb`&;
z+D>Ux>S7?do|9VpLHYpicPBIYfC=DveL1waqi-=oWxS?%!5_D`F}dn}#+9Mh6Ao1)T~31rb=4P+FO3%Y2Lko7v`Es<
z(T@Db-{>2*${#E@vPjnDxW(YIO_FE$Eq1LZeL>YsI=Ry+{e>jCVe>RPp67l8!7`3SMvqQvy9`zNPMah`P2XsXqooDVRYW2jin
z+rMh4ov)QMx3k;STkll6nIz3C%=Xn4+ytVNw`*%sZ`8@Ebl-$2W0fPflv|(PTXWcZ
zO{y{9nyvJPlb6N-9LOd~@XWtDQPf|OZ-szCV+s5``UX)e3`U^m=$)tCt4wO9H6_$k
zlpxBR1M6sM9F5V=VldImDIR|A(318E<9rc6i-Z2$&mvRr_wuu;PP#LWqw_LZ_C3K$
zKj{gKlW&_~R*xFQ*G3w}>hxIC9;-td4OL!I-ftOfF$^9~;Fjl5rq(hUk(pMEcZ7_k`Qjd
zAqfU$qhzV3gP&CtgjHdi*7jTL6wR6^|G6pQU>WzIr0%3SLj%)HR7)U9XgzC)eS#_e
z3{N&Vg5N2qKj>}p{^WYse#9yfmz5|gunD2>6O<$zEr#;1b-ezRuK9`cdrQu>^!v)y
z`tmGNN)uMjFLlqf=F93*X1I*YQfAZ}9oj0Lccsc_w07%|mwrt)8{n-LNYLH(DB-`j
z*~WVgpyQY&BF@PA(@SFoiJ_=!poGJ`C5VIJ)Mlfmga&V!;OJ}hz9OGmMbnfk>RU#t
z$6%aJ8!U#_1MCsyPY#W@i0*&(>UxDaESUpWRG#lI_Ayy_Qq<}V%vFdbzN>}F0lu-!
zhl#Z3+O>IuY(G;*v72?%cWc9D%}g^@CmGvJ`t`2_wA=(MCCBJ6##r$?{DSD{84%qZEoWR6j(*US-f=_
zY)TqfIFTr)WMbjtBqpXz6`t!Itmi$ny-`g82>7Pcne7=Xb>W$V2#$VZ2SbTo&0
z$oEW)(c68`CCb7WXaE-@mqJ-(-nq0W<$W;g<)qK+MV0rb+1%x#wABpV#~_I+A^;s$
znwj&bbY#TWSX-m&j13k8*2pBEZ^E>ZlO@d5V_{}~k$|Ix68xpZ6f>lvqrruP6@@G#
zT+K`PG{`25Ikwu%=w=~FlNk_dla2pWEPnn(Hc~Zhv~;9&ZnC0DHrK@IWZ*6z2N`nC
z7{wyfOOT7sqrQCpnl;H7zkq2FdK~=j=UCMZt7BSju8ba7V4dsloW(
zr{+_MW$nIrPoow5%rBjDBbUs7DF1UE66EgBU-|RNDFI4DeF9&>FH_ent(NSFk?K@u
zv6VZ$8K61zv
z$fQu6?csxb`apfxaVA$mLS~x}Hk-_%Jb|)N+uI3Nl_sN=4C6B#ZwiqrmzQmBXZx>d
z%0G*b6ECBRPa-E7oq@=lO}pMK{64Nre?nX0V4@^+b{9Smp@OE=874-Lgh?z
zOg1(6>jJvQkk%?6;v)0Q!U#>8wN3u8vmmm?(s9n{2A`RL_}*Q@C~S0u^OXV5oAh&Z
zO8+Vfq8s67%AF!+ctqQE)*UL4z<(p)8u?G_fg+yMK4HH?24Tk-4+wMfgWEhW==MGp
zELt1d${LD2ROTk0;3sGFL0iNAHTM~B`}l>1RI)$rM$1X;S<)U7%dFk(cvwrC`Lm=V
z&-aP>F*wJ^+~KN>^sL2O>Stt0NlG3?Qsl*EB=iN6yE0oByCI{cFHRy#>_{t(EEnhN
z`?=PQo#%`Dv$Fm6o{J>*{Zr^;#IL6*J&ll*!OF7`J=c5`{^E&nD};v0UhdihIuHzO_d4O64Cv
z0t*ADDg@PzD`sZQgDEgKm%i7Z_+e0F-Oxa+*~I%)078*hfK7|AIm5UIili43TEHT=+(UxqN$
z%5-h=>A3R_!MH0YnanfuoC)*c)4`yk0#@%6jxBxS)4m+Q(6tCE^ezl6Aw?pbc^ML=
za4PfiH#!C@<8Hfc{Y^;@NR7JK#4ipQB#hnVW@otCgZMKBXpcXmNMaaDlGlynsEGoT
z%7Y93z-v3n^Ra}LDNlUVNilYW)LieRHiQ;_u@bIVu}T(LuugGWeHfnb@BFT#Q&ohI
zaB@8g=DE`FuS!%yEK6_+gqcg*)WF1p8NWrym&!xA3`7oUCmHB
zrVM1hN#Q&D%Q+|&?(u(9;VYU{9<+0dXG-OT>MFr|?AD}S8qLZ21lN00uNQanfg}X*
zuXWa?bW4p=Dj3$u^aJh2wiJQlabHi!GXF+NpmexD#D0(DOit#FXc}dA5T2gaSzkS&
z#5z?A+0|dmydH5_P5y6T;U^_(=eE#;)@3FrKPCiaV7x9B@Ts@wKjjVbEkR$12uKW#
zIKht3H493=`v#@p`Q|3Q}(7wMcOxGtC=YJPG{%00H7}VUJ88BBwssB~q(E)k1
zzR{FgP|@nYS)y#dpfV9mkD
zrPRfzmGYASjjz9LOR}`Prs|FQHca2#w8}EGmZgQ8l|h+FL=1Od?5=!tTyK(
zOj?y;ZI^Y5zl6cF^>)>edID|A`T6L5flKqz}s!Y?fI%|I=kh;xonf|X4Nh(
zhgIq;h)x#=B0s2oS5hi0E0fChxZrlX?1kMQBJxSp>2#Njz@m42oSFIVw&e%w__R==
zPSziSlG4C-V;J+kW{ngg5A}TKWba%B$ym|IFv87p-iq~zQv-MV-f@`i0W}#5
zB2qq&H!}MC<(a-dpH{Ot9w~Hh8?G~NKyatY;CZBqQtM`SUZAjV>8&fWV4`ukd+
zx-EzjItL`(!v)e6bEv7Smu?E62)x#K3)~&gil=khUc}KCbKsbzLAVW()~4ust7|k_
zQJ1S!#y{O0o<5Lo9L50dQ(<^~Z>Dvw^<
zma~N&g(oJQP)R$S+4k^pCpa#dvLD-BDvhpWT0xp)45B+GC+$wB2q4(az1?dL
z{k$^51zHQx(n=%qT?*nd8w9>-wmVmD!R`V9+mrNKPLIo`Ip*M}AW~*Z{REG6_b!v(
zfpsirN6r?C|EG;G)Is{)awM4VMS`2P5(tn9IKwX>Qk8WP&YHtvlnXlywu6JifbQD+
zT7|vn$*_A#d(4mOHU$C^JhV_;h;|nsM_qkA5_^39jk1c02^C2X6(i%r@b1U^gTQX^
z7n_=M9nUM#-Ms|)g+FApiPfl)CP{l+`xS?8_Qsls*brXqgKn*(RzEcS5!iH
z_$Lu_$4CNR=cRjeN;$T<;TSLLtTo43*}?MaE(wdYhZy-VR7oXu;8f{cZ
zTuO>*g=X^v(1SG9CDh{;37`rQb31l(asr-alSXlVtJ3vfEQGl8?uM9ze01gG%aD(~m
z?NNLivy}jsUlRo-*(@MK!Fy7Y-v9$UJD4a)mio(i
z08{md7!np4gCg+g=GQM$fO}vZshLqU|Fapu8Znv*d*|sO!}hi9I8^vCu9}$P`Mn(SJK1wuYw(BoX5IyDoR*`^M
z?t@%v)n(}8C_l!6z6HR4ao%DYN!Kl!bG4s0r9&Kik$OUfuo)uZx8n4?28g1$e@7a_ITyXLad&4
zXae`!?JV>x2JxC;O>Y}|!M@GZa?cz}$tYvX7R1we^X4AtS~%6G^SQivI?OrcZdTj1
ze>rcK?)H9j41xA0^m(;=6ZAeQExsMyjuR)0yFXvG>>Y4tR2C
zhP)q_X=$e6q+p2N#%f=sk1IF9UK)6aIP2@V-Y#)rxY#K?C>!JTg
z4Hk#YgzNZqZjP=u
z01Xdy%wqt;)AnXT)7D%TF7R{YvrIbMh#~k{GQH;QFTqHIUKAZP3-=U2c=#PPh@I*Q
zaz)o5mh=(Z!FWb7_J;_Q9VZ0^#q)kh6u4(=>kG;NH#hI15YZ8%d@9z#AeGeKzc)ejXDRcC+Q8oIttf6M|}2KB)NG;1xl{c09#ja;%h`^yne~
z=ul4vcLje9*ZXIM9Ly4s<92|XQ(9uK3{e@EZeQ3)zz=A{hoDv>2CA`ZYnY6s<}G>(
zt68izRhCev5RMp*Q(l{xtNxFe2tSK&`QItH5S8o9D{QtJA&Nt66Xz0!4yH02^8He2
z_r$fp816^D8JS0zZc^&=1rRMXDN|YqH9K#4@*idU{$-BiIB-L-*!xi>jZ7LTfR3*6jPs7F3aO;pe;k%c_7
z%+#}vdwG^xq81ucX!qZq<9J`uBR0N%i2l)Tgl946*CmjMB~ksM3>o6Q!qwaPi%pQw
zc_FB~<*ikHyWh(Qfa6YJPwZ8pcLLGQJRK`IhU!b~51Xc;`s=3PVC5v}#$t%Tx>zi_
zp;Lp_pd`x?k&gH`q|~?%dkxdZ>LZ%k90tSgFj-6kb~hPel`NB4^+IFM`FLWC~-UYUax3*
zBM3N_NN}+19Px7@c-*yY{g>DVcvYg`FSLPt2CT?yKqj?{NK*ukrAv;B_qFv`C#?0i
zfonHXK0~@LiyiUy$J2)Lxk$n0y(qbmD;g&bz9c}B!|S{
z;r@-bOYCL=PFl~FmcwAF|=e7aGDo#0GYY@raG%f$Qp~uN*V8v*#fkJh4(W?sxlT0oIXB$n_LvM*bBYDrs%k83N`m41@fgK9QC#YO%nO`Z1yR;F|$s
z*bTf}R_>SNa2r1DnqK9Sh&elg=;t$t)*!MB1PwqEl_~q
z()Eq7W+*$AVuBpWu+G
zf?qHDFHC~k4Amm7wx&YBD)$eJ5+)BqG&_sT(h~m|w)+co{{Pee4`rez4}qDKbcZ;i
z_aAHu>g17vvbDyTt4h=tMcu^-
zOaH|Dp`wDN1;)AA?tC5}hW>xZd#k9b+BbR>5D*X)B~(yAq`OfX32Bh-Zlt>#5kz_u
zQqm2Q(g-5b4bqJ?NH^zQp#DF9H)ouib8*fX-{-*PX6<*q`8@NPb1w4sll`^H*Uo=4
z(y&g%{xzyvod@>LA30G;(7op(m?-mPn#3&hM8zeCT76R8ea4B%Ihnt8m`vsM)f(}h
zt}`Elm|_tq+Vbw_t*)ASH~Q+abjPAMaO;6ZD%&eZoAFk&r6iz6!tZKpU0RJ2HXN7djcUUJq|Tzw;Fzp6m42?Zz6fKhn`u$wmQfZ^M@Od$=3#7~QY3
zeD7tf8P_K#_KlAqB!I63Ya3IU<^i@U%{_>F62`Z4Z>EOGc`b>Q9DJ9zGVu&W@mE>o
zxcmPD5iF2`3G(M-*XRTE`B-vrc_}aO;dM)Y3r)TMpMDyAsg+Uef;lG5bLh#G5s&DK
zMYgZE224qWazxzvlvI_+5+9%;*A0f7M*XBf*m*TUNuGM!<@GvI`p>tN8Ft};5MFCx
z#`g{kgwJYi$J}#k?GVuLdaO(|p2dV1WY&kkBW6B=R9ydy57L;pscCM4!D
zakM9J^LmI-gUOQA0elXDMU8~s#rZmreuR{sUK(ikC^}%YyCQ^5H(1Ec$IALVh-9Lo
zqfK~$ruH5B0|q`^T2&GdYDM$=BDVqcY0+eRN5>*in)c6vY&pa8@?r-FZ%r#fw)J=u
zz$7Ch5bRK|`Pu4Y{lUHwKuX4@Cmp8?>Y&8%Fwv|Dt=>Ke*j1c0~0Jm3bGt0VKra0W0bZzBlg-zdTa`Fp1QSTfZ1HI+%LD4H_sG
zsFK#X9>RcR8VF5;af2|CbvW%;<&~_eukpUYNVq8sDNeuRk|HA}^gqJw_lPeFb-q;N
zJa}z{K3@oL0uqG^PM~GpS8V=a@t^_7f=ygKJ(FBkDQWZh1bjb-f|!5fF@0xQ8H>B7
zx_W-7^5vX4Lsx!Di7b8Np+l+W*H~4s+d`v_`OI4e-3}=U&mG!Q;801P!r#FNMoM`?
zpW=I)L*y1_DA!Kl`%QqMd`iVZp;0FH*(&KwcrFJH)?2m6X_Pj@+^@b
z0CFhUTZj%&5qI9Be3f30iF$s$-3PKFsA|uOlMUzkAA>?Fpej6VbQD@2LbbgT9JKK@
z4i}RM=%@()e%wFD!x3><``yx`T!tkWbn8^nltM>gjm^GJ&Vk>%p)|sKJluM#xl*RM
ztA&d~3SWQgg=nPpwK8Ea2Arg_)8BtL?T=^x-0OvC3aqi|Zn?~yf%?_doJLHTLX~pZ
z%*gMUoTTshy-}vnLAy~kLT}>Lv;F4=#6sW5MfU3&vt+Ry#|q@_p8MugVEI&T7uI2SG5qvhU*^w{b!gU;%jgJG#yd-H5w{t
zjUbA8Z3Pl}_i?$F=!1baI^CFfJ*EY#+=T)T_
z9wxiK^D?T;G77icx8FzgBX}v)Tkr3?|4-dPFbvckP?`*7|LGDwg#X*32sG{bP+06H
z^-_OnJ74dUFQkd0=|A)e7`%h-7M6{8)Gaa?Z%}nLM;%aQn(x)vHz)k_sC@lEh7++T
z=>>Ovi{F0|flq6{ovhaB{`c|
zr~#1*DlKA}|G6yUfu^N+`4EoMW7o}q`>`F
z1qJW?(lBi)9Us_sfF!9;C;<1zz@3~#u_wQi|
z36GwGO>PkYL16&;9`9-LzhwYq{!IGcF;@U0o?zE6TA?;w@4iu%zn3NXfy{fM_5Ifk
z{jI6Gh`2aPV0`ewTunuV5|~XyTQ;040#YV1MRGLE-tQcQVrBU4KMFNCA(H^8%u_b6
z(%?}Cg=PIfQLoW0mB`VyI+Ry_3QR=u<|Uxb4RZ^U?7wl}C?81&_keZ~hn*3n?e|_K`c8ez2ZRc%@UZ_@@aLB^X
zWY6KBy+O)L9rO)R=ZDi;kTU_~RFc2{3yObKy^NBag_ArloSnDo4$HVX<}j@afxUrB
zrA6ZF@d`;01+L#c2w|Tp9uPjk^g?J0eMwRnOePus9hgc`X?dQPZ*UoPKcWqM1aQQP
zNf0h@^}n93uiH?Rks<1cp^pJR3~i(_>bm2+;ADnE4sa}oTy%l6(#F=d{IrccIe*@_
z*kQuDb!GPwl*-1;M+!A;X8rHCXC-@3L8T@rETXGF(%n#0R3sDGYPqjfHb6;F9|NKV
z_YHbUPB`g*rWVmR6hC!s)O0sO_q}k)10>)MAziZjL7X0@@vB}PPhg`_b_$9HIpz5G
zHSEGSJmLYwEmlhfDhw<>uXCcFDtM
zPXD3>;bdV1D~RD!U~=ShPCqz*v(04EpRPLkqu#y74FqKs$nXRCL|>1XrMdT`_|$ab
zIJau$I2_iEHIUu?8hxIHNlTc6@UbAIrfMnPY;TY42S4=<$$*+KE?6iUz+2#+#JCTC
zt>@|Zw0?5{M)mrp>chk0sh^-?pa*N~=or9D4#8(P&HE_u#`FuyicWi6O^#*!rZrR+
z{_oKtNi>HC1GPRtpa9Ujht1%tm#D%QeL0orez)z`84b|Pcnd$zp>6n4YIl8LANdKa
zTZSIn*tIh*wle`=iy^r#hDr2>{Ct{@9L-kv8^|K1hFyqqUl*q^6CgyfTB2^AxUW7SrGuSmGY^2N^{$<;Clp_!=}_9U6doJYw?-KF7b
zeX$E-@@`GL(Dw~PjBhZ#%L#fjwKd)tya&bz$7@=brk9}BY4UvN6a^k>O}Q91>t!eb
z$JS=J4jwF;tb4L+Z)E^!&6FsNUrqlZ%~Hd9Nk>^RQUS*Bin5tK4X@P8*Xmwfa5TP7
z;$mW7>k^cI`tEZolP_4Te#mrv#090MfB*9q!3zA`=wtt2*-4-j!TZJwIKKxS+O#*z
zaASjzDuF$a5-0yS{02HY3$0OtwXiQM1xE?T1s{r!jCXRSL&p0QpTv&hB_vM!NicH=
znxb)VYut6j995
z(P)mAzE4!yBB|}xgD~>Mu^EHi358~Jv0r^lc?1TypVRg^DvhG*a38}v)jqvS#@7tm1(tCf_PN-&xTc=HnEX!7
z$Tt{JLO;vku1!$gJkxg7H4E1NYWqXyjVQ47u~%NZGdy|*Dm-IF20!T~JMM)3b4FmA
zArey+Yk%S%w*O4e)XD)vx`n%pv^-^X`}qQ{e2d>VYjwiK5Ms!!6u>
zz3Ny}zGLxHMa17>?C){*e()qLRe0At+KV+ebbczr#$>!*eSzSqb9pJeBnIvWJzDWv
zytUw6Os--lo(DHD(0OE5xN-6epW#IqYFH0|sE+6p%Ioo}m+|`S{hTDKgzyz&fo6{L
z?vJxS)bJ+#j!(Sk5v(bTkEqZ$uEW9?0swt9e#V4*eMkmip;#C<%&ssZ|1H{3^
z-(lUK(@-!TJd+5Pk;^}`hYtZBY9s@d%RdMYnEx!`nZn7s_x`&Bf}atfMrzCN{fp~D
zLT7AcV#$P+e*!ZQ21|fOHfAq}|4$e6BlbT{)Mi+TR+MkAFG0&;d#CNwM~~_k!e=TL
z#M0dvmj7<7vfB|!Tz~8!IU_rlT&setvw>_FTkcShUW{A#~@4Mf_E
zw8b)|_gpaop5IHx<26-mn&xn0JTM@(3
z?Oe17wJspSS^&;f``dM9BXm`BLDc#HGaxvax^MflI~t-nT%1l{x^VuAhaNvwm?#qo
zmi3RrwP@|%vzAyKsxmT;*zQes2_J>T-5!4EY3MJ4MRf{kkN6S%p_7?UjDluDpoqE|9PDE
zUkxI$D7Y_)2{Y~0eVGv;RBRF801*&OM&L99uTpLrt#0)Nsl;wY_93(3yPg$I@&qd%
zh#k?NDio|r>H(rUeGoE6-njjYZ{`l3w{|B8;iy6Ff6yB86SFy4$?6y`P(vDs3+n-A
zayt}Uf#Nz01D_4$Y{Gs-!v=7J#40T&7eEZ>C~0QEfSG;HuFceM32vMXU6;{{Me+bA>$K`*HE(~g0?@JA0M1IvT7KX(4M0yy=r}_V
zHZ;t{-NZba3#NzNeJYINN083wc4S4XtYbO}qSV~o5fIlt2VQE605HUs$T$Vu2qXkd
z76pdj`}DAvgzoa1&Wkwie!U3ri@-Flx9PT@sBtM&;67LL43kb*iUG0TTP|R$-oq`^W4HZ$hv6R;-zMRne6&`|!lrlxoXAAX_!`lXC5X29C{zGk
zs_r!FkMQND4;muo0~80q8X9!wmGx?tUh)ee0{X
z>OGF}eO63ds4mLgf{Fm#C0ZZZzB>baDfpIK@HgYJzTTh>sQkrhjyCY4*trqZP;;|l
z_-D3nZ^^S6KMTVxBLgvUNu;mLE-b0Ot(E4-zdqv|M}{Tqg>oyJ1)TZY-!V#b
z{mJ_y$61l6zLZpeMmP8hZ^zDej=ya{;-R*V#Ak3IAY)J7jJPOxdGv{}iBS!I3ly*ccOKpAWS
zpuoMabi{sC+pq08J3Cjso~l(z7TER@P6HWVc|>PV&&UguIe}OZ;Qa6?Uc+S}f^cr!
zcMRO!%rHd7g;lUOw?14Bki;ryH;>S+lVfd*kqyKSjj;v(3;TTKo(u
z_F2gbHm=Kpz4}ADHe;axHhxyJ($&pF(i%1V6|3Pd7524B+AjSdx+}_3|JZCnegGReGsuTd!Yxd;5ONhFF*OS#u
zY`J_ie{&XL%x6T)Q{(6Twbw)xTxZ+OKIKmsdfT+97&?ua1s#UTHQ*u@Q}2FiYL1rqS~8AFXj=pO^%fW4rxoyf
zbCxMwpl5pM1ncBCID>l3<2v1-v2@YMvTc9=ix50ko4q&XjIj{`CYFI%`-Zei
zSL>G%fpV4qbsjeZm0#bZM~AQ|@o~j20oWvlD~71jNdl57bF9&QrH;GKlr|1q@+LY`
z`}Pt>uCJ`pEaqF^!(N&0Z60Bfe^;}tr}NCd->SCdo-&_gH)m}sPb)3;{G5JgRa_2<
zaKYs`#KgY(x#2sQwe-w(-Q5f+SSEhQJ20dw$fEpVyoDgcq&9S#o3eNL|jgM%DYBHeEdWuJ9@`0t~a^dP;p_r
zYk`i8&yL0K;4wtCB@h%5g
z)@08dEfn@in>;&{5!D9F`xk6liaAIy%Z9^`9^Q}KmUWo29q*{*3onU@1-V!6D~%iW
zlMuk8Pzyw-lsNC=#_?M6*ZIS~K5f3o8tb+C?!bAQM%4NTf9@CSmy8H@Q;CRoP^kA=
zmpgA#=M&LNP&Qkr#AXJt(ul)o`Uc7-{BLL-qX{bW<4HB4t>3`d5+_2fsKtE04W3Y>
zAMANx`C$QoYG;pdNLH3|=kg>=Os|rk!ffY$rnd(3GX-f>v!(pH!uz(bEk~;r%v5p^
zBa=+WMJ3KxKQ2+|4X)fQ4_VA+)Z6Hz`|>ASO|W=GlDq90lKNH7-#A&HwlVF)@I
z0&fmZ4u9_8lvGmiWCc8oRwywTD=Il{<;|!rdtr09Mtpp^B|5$|w?2&d+-?c4o^$hu%Q5mUv0
zvPVN&W63UF*hLd$A2L1YNzEKZ(i|l1)g_UaTJ0XcB(`14bfcpT=}$M+io-sykYe7j
z>0*rU3-WAu`n+!3OmQq(mNsUKBEr3cP5ga-t=;)JW5iT#MF8EsV<|rthL&4uH|I~O
zdY$-c=A>(St89%n*zdM?GhKCK$vu>n)Kj$m*?IO0mW3@|Me&>}dw1Hx)yMR{YFXbq
zI?$aJQQ1EZmeigtso6C3=`ChA--#}jN+glfV$X6jS8kBv$X?Oj%ye(PUE;C9@$2W(
zX7bOs{EheI^ZWNUhvaV24cl`)u5@S6s_-n~L#0ehKXlI$-%N26m1`1Ku&!bu92Flu
z7s5N-pjb;f5qpb~>&yq3sHbz=W+#oPXkfO*my
zGqcHuTIb1kNJ{l=SJ)TTO;`=R@}0M_a_I5`$t85!%!cY^DUa+k%D76B_iFW0qqfHK
z`~)Kn3|;}l4-PNyh9+mT^o_Ry8K=C`QFDuGc8UI^a-z7DxZm|=Yk75$L{c86;u5#v
z-3$ix$uKL#7r8n)vi+?>Vx>&QIEZ%xiD8-<0r;=kH<=fbo)6hLwPIJX2$mKW$8M*#
z{rv8Jhl}LtZ7vZLh5G0=qXaUo^X<}w1(spC22-0SF8a3zP`8dcH|nE^NaiSFL>d)Efzjbmpp&Dx)<<^oKqXFUtyWKhIkP7I9~jit$YpTFQo(IjNpOWWXB>0Tr$2Zn6}?{
zK&IYd#QYE!PwYq#Mddk8Z`bCzyLdlhgi=e2%Q>|_(=JNx(k1{;!7Mk^-JO1N$b!Bj
zhIZF$=Vw1@dwrG`dnjD32ns^%y#Nz!*cVn0Ym=zr4gHFGW;K|`WdL@k26#J<)=&
z^__F`xcCU2?!;ERk_vK+A-S(9wE}^SiC~{($VF5=3_Q
zd~eMqF@^Vx;M-i*n#R>&oJ8Tt6=uFYuOA4rWLM~@a+h5cDVI~umPm>xD3+kDpQsWr
z7s%{flKh~*pBrsD-s$%IcyK`fAYaR1b6sU@!*%~xk%6ge$NW%5_{Bt-(S;}l)O!B|
zOjV-nPPw1O!7qJTsa-FBq>LnLW3(THddQ7Mh>adxaMdA`Uiv&8ysas@N~3CO7{Ia;
zLtay_No?@pP-NvKv1E5wsQG??EsICzQw>
zGg84X%7G$SjVuhtinU!k?DhB0YBA1;(XS~vSl-2E$`Pn!9oGfYut$jb`((EJE|H0*
zR577Nq^DnAeRj!Pmn@2kFf^5yMr{m{9Uvv=7LPYr5cxIC{r<^ElP?@GZQQNTss|z<
zWaM5rz@`NA6<}|#U?QT|h@Jan$%En|);-uf-&A3!{H+sgwY9mj?Rf|7Njr72uQVth
zig{TxDMv|rx4cE)fbJ3PwL8G@O1#778iGQUm}wpgkXU|Le3-ud;JZsCAOB2q0gf1R
zB-s+#!)xa&Ar2>Cd}TUmluHBoWhD+2R_$@jGq_u95aAkg>|McpzodwW&6MXJ-`kaj
zP`@^j8Sm*_19596*hE$5l1rp?!s9ia5AjK4L(%nF5ehjgS^*=4I{P94{7^1Ovm&S><$}
zTG{FAr(zzdXhp5>`t1fGNR-J+le4O4z(N9mU$^p(36?q|$9x0!k2~|hpXg8hsNA@3
z4al(Zn!j@GA23Ex)+Q7i$X0m|{FhEbNzMz1<(>YRi~Wy=WhBJ}hxeC9dW^(Or6!TCu@lD=K9EhNNutv!#)U
zcw!%k8dHZ*JKJUmk
ze^uKfx#1lPaSo7*ud2h4JqBEjXZc1v`E#V963bs?$wVnCRjJdznu;b)1qA_x${Uf4
zZ(j&Oi7xn>D1&*5_}7G|{QsEnsN_UM1Ym^98O+TH?6EMID(W`S#R-64-v$!Adjy5=
zqOWEqgnx#?hD22N+hTyFO-608AeDGWOLi_SY7pClgh@LH0!Bg68q|zUyGpJo3VaCG
zFX4Q|D^B{LDFBB$h&h7?!^7oz`nTk9^+O;dKu~q_U&LO~5`II-;9tD)5EAYwHv3$$
z4*=1F#A@csCdKTIW~k+bi~1n*!8Wq%l|f4AyyI*}=MM-cPPsrl@+;)!{7o_Zdpi_n
zcvIyj^w_?uB$x^FXn&BURW~#JYkfcq<9yj~-p50P!Q!tH3Jg#G2P>We&*g+Q%lc<;
zzs8mUdmi)=v%;5m*AMvjPbVytV?5ZXS*26f7c(dYB=V-X_n*0Ca;qoiG2E*onL!JOvQ+H!8o;D^7|saaebypT^@A%5F9Gae^i*
zAlq)1K^GQ3JR<$)ptfR*J4*cALB+Zkz@^}hU-A}3l43V0Gq7_Am4M?C)
z#B2ql^3{5gaqYTG!^z?JbuSgU^}hu?XL4+{f!3+7|9uonW$jQ_>R4pqF0qBRXi9JZ
ze5Lb_YHt^7Zr4;$h<*_8d!VxeS86_p?ohC^Gs3B7kX@0U_rPO`vk6Yb9{aw_6*Q
zgvxI#Q*|{&b&TdcPUNize*8)IIE1EeHfeJ|!$d@iurM~Y1#phEXW++V0%7XZ+
z7qe%XA1orkI|ESL*{$aUIU%i5*<^`9hn>f^h7X`>tbn&$K(D6uYte~Il
zN#@nM@O1qN&!%8~KFfbsNahuGjqaAlI+7WUvfdUeW8VY!^+ozG4?;f0WPidpTNhrh
zyZ?2H+JRq2zPRJ8XDhax_XPDX&c!`GkW+L;?gkxM)WI{}bWARwgew$T
zU@qtNdss1Q_<^B=rqmBGA?TA?hA$9q(O?3yck`!iPaxjnH<5LTO2!TVI3(4C%UXfQ
z20-_*z-w9FSOJhY01O)|P4#kWq1AFi6y4jXM=Cx%7f16};NNQ^4uFBY>llDs2>}GT
zWZ(%Z7CPX6$3+PPu~$?v^2xK!Z{9o&t1r*ltXjW@sA~<-IL0_+nz~T$5^a?;G
zweID74SM`G(8(4x1S@2c1$J}oc5VaagkMICCdb)|v?rRJ1f#zac}CsYPKQpM0gO^K
z@1;BSqVtG`qrX{E-SBi9=SG=aufBBEGPma!r2BqgehrRSUS6EO0d
z?(D@!CopYxMR5*`fU>x^NUMPXj7rUm)X)wL_KBI1dY}WH^}b1fQqr5=kXze?M*SIy
z#2_=Y70gP!vY}wD#rvX~I>#Ma7ZK6>eJ=^Cnq=*Y`VYW#u<92j$I)a1?4UQG4#bI$
z$MPMAMkC)L*WC0aU_G6ur8_LGrG=(pBU!oDxwk&SkFYpsUo
z&6-7+S`(->t)RW9xpXdqi)IEasUB&~8&q0BePD-B)sa*MW
z(3?d<4s6~V8oV{s3Ct99G6b8;>#|VgmUmq^)bXsO;><{-@uW?m=6r-*3N+#!Tj&H_
z&&FnW|LCnwgURok5hh=
zgD=5=`Yu%#CsFjqJ!KA*haQ>_)5b^K+n6Ub7cNSFuVT2)2Zw-}4G-^rV~@6%T?b6k
zJx9e8#r%?Xs=g@iqoeuIQ&*Lr`|n3ir)gGx0SvsSzmsFWobgaY+7)n~Ol4{+40OoP%@t{i+3)xHWh;%Bb@P?4X>a>S^o%<4Dy$viK6Sy~$WmSvapt
zkc$6c#S$#Su~$&FuYYcz!B*guIu`c?;%UC^^oeMwB9@
zRyRLrv9Ufv3t6Cpv8v5YHPf7o2@Np|aVG0faTaD$r5utV_;A!I9t!?n)bYo;WXR9C
zetpIANW|F8(@?bGQDIpqO!Z7o6fINAs6@+R_U)3L?=GpavHCjUtly%ZI3h*XBp7lm26|iR9jl(C|F*YA)H<-OKe3-|iBsI#CuizJxOT5!
zoZv>RQO056R3&pKgKzS1rSN855Pj5lA3IP0tXgcAKHDO!s#zINclt(>+2{w}&6FVk
z3LX0+1U<1*iq*oO-j;O7_G1q#>xD2_&G1zkwlO8gfVXLVYhVw`h{6WF2J4A*M*+?3
z;!Ck$qyaNlLs>su$cM+tX0jTTn43X)T;sc7rjf4#3
zJ6e?qGl5@&V$DPkb+-^9*|9-U?N0l}PE7w(q09ne#~be0iv)@!Xk|*yvP}!^AOhO^
zBtMFWC^9RTxc5>1RIVENw$1&4H{zdIRh4JD*r#cz;#r^uVF?mReoL_a_K@K8qj&k-
z4y1MWOl~u$#8{VuxABeUj36`0&B!oYs~d7v*^+B16i)ze9Tl}$sc)_k$q>b1gMA^Dw=50&0E=ef6o=DBA5uqK3PJi9=#*fsRD
zZ($HrtXF^W1Z7d)-}jwZmram(H6>)Scx;0XkHVMIhdS8R6k+M)0i*V;cLQe4D*`cs
zryS(3v}+XS`fa{N3!(*A8C5lub1w!CXo|gY&&gW|SuzK%V^m(aoJ+Gr?rBHH9
zR5XebQV$ZeAjZ?*H
zDU#TR>^>7J;G*ns)P!kyIBTT{wFg$k9kWYCN;-!tdFlG;Z-kemTgH*~>=0Ae;v6r-{s9%!E={i;DI6htQ<3?ZjdaMvl;L2
zP&J8oN#Oi-GVg50^K#k0NQIB~Za!SE^;>Mj0=Uf3cTtEcYVt5aI>g*G!=-)?D$e90
z7->D2?z>rTT8Abr%w1z926zP#PhWACQ+Vw>0&leHSp43lL
ztpvkeq|89|h6CP;W(e^t{hVT7)^M9=aU+Km)o7JF6RGK3w4&*=Q=_r$HF=Tdf0AY_
z_6*rxrUuj51p7~YEQxyLG~-=Arcy6KiEx4}QYK$J)nnsW1ITncF$xngHCz3K0yc`VjF<=RhuOE8=wAWXfj-{<>y2(4Kg&|R
zm0{!hFMSdxeG_S1y>k3VXgm4X3x(R^w#hqtG`?t@XiY3JtkR4z%y*{PGa5s2mbElT
z0CR&TGsU$uUjO;ndYYCQp?*>5CbLqZF>ZpfxEHLfd7?S>n9>y=^hpv^M=MW6UsRzn
zot8&83^hcT?3GuZyUe@5K(^qU%Ef&&fqr3zmwVcj7
zUH(7ri+?)8xy&^F2-gM}fIowC()D;vE@L?rMSI;jEEOs4j46#rQWR@WXVm@>GwV}S
z3O2dx;}+k&zd1QB4NNi0MC;v3ZXT@F7~!Ty?5?ChU2Mmz25l1|Qhd06@l-VB#>L2M
zvQoKkc~WF?sp^jMCIfr@T}xk0M@
z9lCU>is${D%0cf`7~K4YZCyhQ3#=A}wx}d876v+3HHuahJC$y@sii*4)dcUIvKl0b
zZJ9YL$Gtfx7I?d}t1J4pnpF$AZ>Kp;hcq?E7y^@rW}c|27MA$E0_;rD=cQ*xBW5qD
zpIdxt!5w`b9Q+m#i_FT96iRH5A4}TB8MI29Fo{nG)GDh7OS>)hWSpng2KV=$zkUK@
zQ~ULDD9Jq
z!R{1nLZ{BJs&$p9XzgUd0!~XhRqYes??uxPwWDijis`1FYRMN}i9nrdkN$xSH%i!>
zF!S#7-K+a0@J%YB`qG1rf=7{1MJ)UAk~JqiKiv$ph;@GgMomVQ*I%VdLEWxK%}ES3
zfJlZD!5*W!NQ1Qv_394LZ$cBuSO|fFrr^g~*~9-{mV5!{pCQT-WcmVp1p0O8(FMFWOczxn$z3o1M|jBJG>7xnsvSC_#+dNuV2)k%GTx(E6m=yER!qKtBUdRjKw
z^$o8ttA@j&iBlhNmi`6BOb+{1?DxDENAd6{z|5ry@IIQhJPvDeou{1ULuP>?
zlF`r}s^|~H59UqZ!=L6&0lL)%t8v&gMZYF8Ad$Ur0)#ufjOSS81kymcpbYqK8Y<4<
z-Du>dqu9v*XE+(yyrp8M-j4QrMybjXsxDMGVMXUt@v{
zBBmH@o9|xcM1IQvN0ol;>yox6z&BzyVF^^^(?&o=$7f`++ZrR40L~{D9s_Km$ESs7
z&or{kU#VVj9Q>YqLnt5=V6vKNWYBK(+6LG_0PqjuqP_%)=pB&t*(rlBY6e>?_Ir+1
z3Z)1;Z<-t6osKVm=myg^fq?m9xJMu;{eTfO;nvFpz#kfjHuf>dv1V?Zx&Dz
z@0jOEi;@}Kq!MrgtnP2Uz|qO-VARM+>D}6MC-4f=X4AZarPoU8)-BtBvt)1x-rW{3
z>vM|^MV#i7pt4<`knaS_II)Z7&kg+lYs&BWMje5C{te*I@l1d`&mwUJ
zB+g3}&)kl-ivfu{Ac@;G03;r~kOb7@1GWV>rgQl)9sM~h2>Vym7qP9z)
zr+SFV${SV((Ei;bVo6ZGH$`|DV!UO05R&#X^u53vPZO+tEHbvh_nB6LrGsC=(D15G
zh{F1SrOYGqZQLy&aXA9rCWv3$3Zy3A2I#AoiQ`_TVrZse1g2s1rct7d){TAQcU~VW
zH*0(TIGYB)R|r40mHTj_l;j+yX7LtE)7aql4szDP(2N%`f>WB2zPk~*$+~(aGdNS-
zNmaKGxOp6bFMJjQ{Jt1F)N19IIM(v90Aq1yz1BhE%UcLuI0M!#sg&f_0S^rWm-?4R
ze(-U3vyH#jeBaJuKpBsqVB>l0J(g7sM%IW_G)BWgZi4vz&h?FOAArF&Hb*f9_P?J9#R}yP8nX{JZAF?yn-?WB4Sl`
z(O7#Z^Z^TPB`qO2yLkWt&8|y>78$pT!)hpzx~JxxQg`~S)dVkZ?oDig8QX_#KCN^q
zp35k5ysDC@8$>w=M8c1@s(uo9I_$p|-#er30h<8}$TR9L9>w3=G{H9q)=z*w=&p5{
zO*lmNR(bid7g*Kd0DC38{mXCI?$QIiLI#HSKi%+l!0ct`yCmPc|yb^yvO!*029
za7n9BNF1ZtV5tEvt}msIMY*xKJC`rjC52X1Vw(?uba*Rc7Yd#XCEY-3&eIz7gx3M`H`c7OM8o5JBZkukpks2<&
zoc5VBKH%D_74EsU`H=MO#?sKSF`#EA_<`eOY(Sq>mY`k9nzaNi{$8BQQ~OsLOy`Cd
zPz-*3B1d9+ckdeqV`F0s7J*lEGMElhI7au)P+_dIZc7r6;A3z_EYYk7c7%j3-ro`_?Jb?JOLs3RL6By
zj#3L_J91$y%=7kOjLZAZxk$SGgP$5V@kY4d$d)Q*_JswU#!m_|Yj
z(X_DGxbYI$41E@Q+G1~fpgN4ywJqM6NUE28&eHfuDZW1|-jqrUyK&;qgioY_d@=KG
zC-a;8nob|*8Z|$WoKRW_M-V1t(PUU<*H#40;dE*=8J$OxwyL*ty??UE_{2Lq-he&k
zqc)u>zct6)>6lrO^Mq7;rLkIp1SR6?&6XpPojko9+FaKNH_kD+9bVi
zp`^R}8Drt0&Z73>XII^n6R-S?IfE$@2QCh8qy^Z*3
zUO4tou;bE^B27}aq-Q5aw|6pWL%`kjxJwWtnr_}BtHj0V2QL)P)wX_oA;;%9MJur>
zTf#;$qs-i4b4NT?#ep`5I{2Ns^^Qrku=h#(^c;LY?fir(nVQ8j$m~(ej1iunS9B_#
z_^{Lsu2M_;(s;?$4R$>Ax*qwm&Hv?>AF>Mza{~mTbUN(<7V}2Pj00`ql{Kcn^4Er&PMgt}NcQ}XX#c;+%O+*#77aTnL&MMjS
zK9=m0Q}{il9J_kOabjd4gYUGGFxKD<>lUGr+yZCu4--u^x7)_`7->`-TnvYVgUfep
zS3lHdkky1e{u_NVyv53{Y9ekCEdA#Esd4_EzmVcP02YraEk@ug2DSEbtD3j@MEQ1`
z5yfk>~hO`aKC`7V#eoIK%^8$+dM*9(F5eS7Z$;~CM-_!qf}4NHKL!s
zf7YbYgso-;#6(pg*_Q6SHP>%ZdcDPOC)
zOIX-!+`VOU80XkhPx0-=JK3|K9a>!(`9j6!h}6_GY~J*tyaG&XcxM2tBAh
z+(0U*Cd!}ynS4>!Lho2%mu*x(GUW)D(o^ru6bAog@NwqB$k+Rk>0!
zaB9Uw4JQ@sL>s&a605I0SWqc)Wu2kdrMndlBQ9ip+VTd}ZMtrp%Z-oiJjy2c#8C_f
zcP~g(kYB!Dm%2p~{sc>4teLMTW}^AqVH_;u)kf41T}!>hy9!xk#3N1`t{c}aqd+tz
zXzm{lXbZ_1n^6*Le`MR)H=IA_mN=)ylBPu$k6NS{vt!o!Q~9_Zp%sD
zgacQTjnNC~cv>m+jo^PW>Xi>+k2U(iwM@l;XiR
zW&;Zx+P$$Ugb4p9iiYzRhH|CAx)+ImT5-Kt$z^s^PQ3+mHTpc5s_Tz?h707xhb}|H
z8+KlsHL!ACdep6rH`u#7eSh?Kc?0Op9Kpadr`_m*C1g-r^Pg7>WbYpS5|BC;=)2)mSOt=5{8Vi;-q+w|S
zyZ&ke@RO}t^3r{$)0@IS8*f&K3Jj2ooG;2dw}I*i9SCcB5RIPeehst&V7uh@d6x%}
zR$MHf6@qf0p(Wb>oBz{0*)LM)&^kae_$U+TEqZ}SyBH{Y-hktRQY1X=D&1T79zF{K
zr&~~>7j3G*>kcyP0H~n7iuA#}LgfL2HlGuyfi^?Q@qV?ih!aThn+Y$r-=G$@t6A3J
z0Jq;p)cS@v%QKKpH-SjgT&8K03@aYp#knx{hfGj9-v3xcFKLDyB8ZWs65#H_|unw3Sc!U1%
z-Y`fd7&giV0y1E#RFJ}A)P3wZQ1dr~G?LH(sOO-v=;^uMw|A)cuZ#J^S`49bFr9#mz5&%u9$y@_@&*7EmOse<
zm_^{lZH5$-{GrnG%{7oqnQ?4@;0wD*%t8)`dig$s%97Ylww)Dld<_6Pcr069>+!IPL;^U6uKz4cM%PJom^HI0sbpkpJH0`;uoDVH^W
z&_&#jKt`vc+SgsI(_CwL2uign7emY+c_d{+_{EBbN5o?AXNiMp=6a?B3czOOJTG<$8%F2QQ>!+=QWt(sC1J+562>ovB{2Jw
zg4%ZCFOXOV6S{5ls{<>av??!y<7`ckusM`Hcu1t_|RMtT79>`|=9+-f6un0A7C4ySl9H@9cxC3%A<8~ny
zgQxLKI9(BulWHbeS`8k3$PJ{T?IXzhqg5bRp;A2)lYi)ZHw&odS^@sT5dQ)bs{DSE
zyZqrbvI_|xYo%_{6!ST_mOCVY{G~6dVZFL6&EwM-dyf3i?m*U<>NM&#S;V?X1Wm{*
z^1@TzhR>iX{d^ouSl-+XQ3=Xj7W|o3knXa_Bj7?V-_ldyQBx^1`l4EmZ(RHU=21tW
zz@DL&XCWEBcL5bX(aE}^R*QXHjEq>S?%%`UPoV}?9JrleMHUhVeaM6kfc#sBONz<~aOd+~9V@Kev^
zvt|^1r_YwyzaYQkF*6U67V$m?+z6pqAZ2X^_{8Qf#<89ncN49j`a^w+4Q*r}I*r;E
zdGy6z#@Y|7Omd_>i?^W`2OcENuU+$apUGfB7&g*H
zhuG6weiQXcAS^BJ7o#?)Y#U1(JrGNEX)nvyWgFGX`Ts
z;AE?bk3-p4W_S00cOU=8C3c|^}QKM8w(`CC8x0Rj}uh3z5F+yj|1oX
z!KyJnve>4TP3KJQfG@r`Z^}{lK8Qz%{lCgP_kX7MIF9F1M>i2
zW{b>W!@_w#u@U!TwCz21rj^vC0=Y!vKvX7E6rMczI36v>JhqeG;AY=SPEEJTb7
zDYa6Em@ObO|A!4BJwbD4l2Ip0rogSH_RrXR9j&FAnWOYgre*8#AGP|}JSakZ~%jW&FLG{~?M|_|yHUZeF1rHr$<#(0hDQ_EFSv$>Po4ngz~jGeR+?sw
z(1cEc&Wk~h(}b|bP|83$1Du+;qblmeWtXm7bu1QPnKMnEfsrD!x=mAl10W2rlF!h3?`Iv+=6>xAsR9(7ntG$-Cq$?Ib8U+=T~F^bPJnI&bKOwMN=8pb}_YUJ&VZtknY5>xpX`{qBul6MvypKA_WUc)cyE$Or5VOureD@r%z
zdY>p77{XI8xv4R%_kXu7{LCNmi1YD(o-_{O0zcHO|&z8Fnec6=;NL
zdYh%F819dA@zXLuw!Q`0gO2n>^AlRMx^&Wqbhg_y$Nc6_)-t;PU9529RnZ3|8R=&_
zW38VzMN5x?W!^3}HCu{@bY94O}tHHD5FZ=-b>E>jn`MM7zN?7KoJM?DkY+k0m~
z)}O|3U;Vc)A$cRU*5#5P!O#u(NxE0SBx|Ot3a(wBUA1u$IL54g^n_?xDq8`v^soY
zkkcoxzKNAGnQT(BlDF-9V}KVpGaXPU!KGrGQiSf$VT9+J3vM8g_#5RC|EYhrtKuU`
zz?Yq*iq~(2`5KEX&O7M(bx((W^b%7-i}YWFjV1=l_}e?$4`1|}`)43)o=D&Lt}j*)
z$7Uu;XLy;K`HM2e`~b@OfSG`3d~J+xlmJ8SDm_qVn3qdQoY62_W_V^>fi};{JpR
z+ZJ?f>2%+XL~pu{LwMno=InIal%S$puVB_@$Z(Sa42GG3v88v{Ht)wpEth60KgVbv
zIi{b5nX`Xic(EHVUEo4Mdd>w!9pi!pvjX&ih8t+@*X8Hc6}yIeRPIb1>TYHzqN4S7
z(1~Y`}0#5x4A0lZz

literal 160431
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

5EV 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%0A(%z6I7;(!%e51`+NF!*8YI2mWfU4 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 diff --git a/assets/img/console/reindex-completed.png b/assets/img/console/reindex-completed.png deleted file mode 100644 index 17f6a050d36c84b321ac42a1687dc8c1d78ebfcd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 574822 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 z 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�SYcAj(+cFanjr5Ve@OXj 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

-0NSrm@kU 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

{v(^M$bR_5wI}?G@pDp3kHyhYih=4~{ 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 a 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 Q0r*)Rvi=o)(Dlau0j7cwtpET3 diff --git a/assets/img/console/reindex-confirm.png b/assets/img/console/reindex-confirm.png deleted file mode 100644 index 9a7cc0305ecc5127a37f8082017c3dc90d09e49f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 477699 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

Ld9VWDGf0+mh)-dssDZXb6(6FKyDkimI>~*gs*B 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�E$l7S;PW_kDoNQx+mL#u+NlCg;s`)QH8IfSqz0 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%0ARbOF;s17Vga;yKoN~GSE$LN>@Mji20lk ztwC2akG8D#Z&YWHWHQb~dbYO63sLcKh~LyL^Y;GFLUs7p<*W&;`DO7Xvn9oaj&$b8 zohonUcv-(3C|hFFs#Td}xn+ifv%ujec1t_w^!xf=)Z_^mcO_#*clYA-P+Fyd+@s%{ zcI-$5k@u^aLCt<8VEeFv)aI-m&f@0qcX;vAJNJqLuOEP;dR?Nf}$ zl`B>bi6h6Ad~+piSw}nv&ze@wShE8x(zIrN8Abq?(3^B<72QcgFle$_21@6QIvkZO zRIT5xC18`u zvBBl^X+zu+%7>G@zag7(wcPy)Ko?YeAu3;%rKHA4e)U{Bly#tE?@3cSJCo8<-P6-k zRRJ6R@L*KI-j{6u8Cmv1tJD%pSZKW#P~?)5BweY^h>_6FEG~ZMA)3(DuS?R+y#CpW zl3(MqY9)P4OcE&D{^dCSr=ql7IwnV})OBwq8AE(3SlrYkUE%WU@R^J;2aX5Fbg;i-B?_5aQ(xH@8eS_rWf?oplI-h$T7M50-Aa`G2wP-9))oe0Dz_moJJ z24XnExv%_^kdTn-toi^-^D_Ws+tKY&T@V?@WW87;yX>6{%Knh5(ouq75IbG>0bhX8 zaylFUd^8?$LmM1dRmhdZIG+G{;CKn>A^q#i!YZI5n+;k_@gP-U2BRF|R|`}|Z=t&i ztmxx$E|^!J{L&Yu$zkg;-cc;#R}RAX`1lkFL%&ZO6j7^-5|If{JhUl6C3-9Hw|8}O z^HvoG&Xn!#-?@}q4;&4)Scm3L*`%HfW3|3Wvvi>FHO$Ez#oY$S)#3noPH?z2F=VrC zm)lD?y#DwO!e)aF+9aXv`GP^}BQ9z2l=KZeyJOr?#JpDJUtCyG-9Sq9=(X2t1eLGH zfC{s_HfZy+UJ_kL%7E2rV%0<=Viaf@7+Zs+qgU)dq)Kq>2tNbK6eO#El=h+;-YTjR zwCr1#-(AVc5P@Ha1=+L;Quj9Y%Hf$@hz+vZJBP1HgG#jzZ0a2~gQw!JO#_aO?L>^k z7p3Vd;LaTBewF^0{Q!$KwbD67bgdpgo}_Abe*{M=m7I4C-N0K4^{=MgB4J@n?|9nb zd?z~}B`Gi{$B$QhGW5#QLUwh<>AG)J^7}BTjXT1@lKpCWu0}GEZQ(CbDGobr#g};H zYeoVCOW$@6-(g-XrBzk~Os}J9sZ}PiprHch!0vL;kcMygy`uatafhV2IX=)$l-G5u zHn-URj)t9gERZF6kO`p7)!duvQ^E&-oD=KRSvQ zWV^*j>{dB1omyqq{gEJo?lm(X`pm0&w+NesxW2y7z|O8)_NBzugCL`SC>7rl!ezSz zugCf9GBi(w-#NX{Dn44!8bWN*cholzkrf1A}iCkmYWQt zRJ6q{K-E&No18P;T4LT)099aJmn`V-0HEiJGD{BdxMUNPzbN{x44?E<4yK+oz4tYU zm%YdFKf1?vxoq74rL%^}jAS?pnSm^`0AoH2s^P)xqKpzeFfpHlGP3bR47{RiJ-i`` z>sw1uqqq1Y0tdJ*wlUMl`e>0v5K#SR1r?lzaDoa|Fp+ob(5;XYQ@r?mZ;l~ypI=ex z#QZ16j-#|Un>e5V6esS?^Rh5^2Oj8FCTudo>kx9-e0u|K=5sD$?FDu*xH{GOYgy*j zoediP02W)G)N%c627ikuzVue2f>T>%px)89H|4l%oI*>BA%MGGTwGiWfQ#~Rz~vy3 zrpL-MB&bVb7Kf_+8FOhLzph2k3Q9$Z!JTA+3Jn+MDCF4;4uT#os#{DMVng8}EOJlN zEyeja+P6E3db*J-FdHVH`aQ~+RPUBs+wTs?3 zSA4x%Nd8*1L7K809lMl|DA>R+|6rS>jz8l_D7!u9#PaP_M?|P_KIHHx$$@da(|0EahputiD(zO;BZi!Lvg9~W z7oE5+oHnV^u~wkuvCWDmQ35dwaYIy~z?%w3i9P@pSHY-2eA$!aPRSz`Er-_PqKGao zd4^XyP1f@Yel2vi6KM$Bi{2#&F_t`&yhwSYH5cEUXg4j8j!6Vk!8!?Q z#m5F#R$zjXF{gm{*T$FtvbE)=<`4hUxO&7n8@Q;o&o&;WhCXVuBp)!eY1Niw3NaW8N5! zN9YF7KSM{hfWL7fG<#@flp>rmrU<0i<8%^b6M5g9+eH+<6g`D z=8s-|cD=^5kc@%4>(+8fTulA81C+mE2e4(8`nY3Cx~E&yramcPsh7nOp#Glf9Bz@* zHsK_wp5uIAQeg4Grar3CgA8tSNPbpP(a2>?Q4C$r{V+^4=#;;5uoG-8CqYstGGQH; zKw}T1-+NdIljtaJ9!l7~(U#43IKG36BDxmM<)rQ_y!Jl3F%lbaeYLSa?6l+go}M4D z5G|o~&cW1ZCFhtscQR;F>eE!1q2;A7fob6IpQs-Wm?oz;r_p^dUHiCg5xfhIkCW0e zba2Yc8c7kyatScov0_J3CR1y!dvd_;<5R?7!o%$<@VVUFAhHW^Yw_UDLq9Kq zr{L$cl`31yF9Fb0N&2eqbW6GoH3sVIR4XiTG49(o4Ezn-;MPA^5`YQBES>x2ONA8b zmelwxK80+ub(%X<+gh1ZDhl0XOGyRH-}43zN;Ci!V5UQ{*USAoQ{2jj3T^5^vD;6F ze&w~5uViQll5e3~JSJZMvM`^)OJw9`XFnW@)_hQiDXoY~-{^H3?1@QnyGPyvEKB^7;M)S~0ZKW6<{rPiVmsM;+0>7ZTe?%YSbEqXmIwnsbaI_Cu21Ba% zRq!gq+&l~x)&-veM!{FOb7t=}StytqV!7XvC;00&HVS36;|CGGU(ChLYwwAqx$k0& z#`Kf~js@0TNYa2kcdVe#1E`zk=N{%Pdqebk3xeJR;3J}weM_38QI;O8emO*IJ2+hD zK5a(u1XJHyijjDNpYpNaMeK*s$`@L5V8(?AMVp(O6AdW9ab8GPKDU00&|Y;J>1PWc z1XY4^FU`>ilECq{BVVtPpXrO&@q9ktqrvM z#;W=IVo*`uUuCx)5c*bwocQ7Vbh9mmk?N=cF4roz1ZdI-+xU}D0uQS8%C~U$@|VX{ zQb~s_oE<%Is$X1*?-$29bB0TwXp)JFR{QnSQa@5Ih@1B>j69G;2NBKSuAq#xT^Nu7 z<7t+HjIV2haqO=iVOW`e1es6#hSiSxdz1KqM&^NQ08A#SuPz$@8pX;3B=OIUrS|4w zuF)4<%PcCr&Q*PW%I7Jn@$0P*siqWK@(OUo_c#fQ!0_`Ml&p}lp!XPCT1vrqIziKw zY)3A>L@AnsWaVjQL`G4tGit_rg@3`m_c*+U7PHdUt^=H!`B7T^CJql^F$1^SyBO>! zf~t8x5V`Ga20Q-wF@7}udF_wMJ8_!NxXOP4ZQ<_IjGu05I)AX>#e_eTuGF>Uq*~e; zS`D(u#)WCw_9BPdGXORRk^TWJv~^ROj=GYsEP3O*OC(pm`uYIICErrX;+2R&8M%tyS;O||r}q5lA^qRI^Z*V5L1`#zqxe<{UammC0S zaX7|iTh_u_bdg(=$TltUlWg={uCR1<%*)d~%lW^0gCxlF?sDSbn#W$R)aZAfb zzrt@9$3}2|x8M6w^IFxt-vlV0agaSXwQd3X)v@^1<^8IzxOrmC`tQx89stvmL$4cx zdF}zQiqr>{z>UGwRL|+dKyd~Hq3x@8_nfg342KsJQs7FJ_&$V;xc4y3SxNkmv&$h(s<2{pM@Ey=!rNXfiVjg?TZPt z3Hnf@0!SbN{gr$Tay5!W;ad+}{}z58{@H8&6$lLMb={Q#)U+&Ew0@bG6*3=CracHh zwI+BYctBM7yPo^299OBF1o!vyO!x2shM+B16M2wbg6ThkEpFu?s0D|;*h21ecOInyNB&JzHWDtS>^e9D{me9bH4NHro;UUVA&tY z9!QVn0}fWj8aJv9+-qD)n8ZzX*nF_Mc>il*2`tM0WMiZQPP<-(B*(^6Y`^htAKrMC z=PUKz!4%GB{QsmH_JXg zn$~tZ4+>$lH2b-(LjO0OYXU~OA?97bjg1IeQFzV6#yv&0csWg&(KEtmkMbZ6DFc9t zbM4MD_15bl-NYe_W|f7>9!193iOx0SEU^NX7io~E0@oC-Hm#oJ8T-H+wp?Bnow-8#XBnoHc=wka7kLAqhY;*MPnc=|zk z`9!Jo0K@E5Xc1<5p1DM4Yafy%rKP290cpP-19vMs?l@&T+L@Es<#1EW1Yj~1GO!ON ztG}N8`}up{ixpGZSaIO(g&G=QDicMr7pfhkISC~tMPT+aPpYfC?SS9xza2vkxp~dqaO8;phcp zvR6eB)FTHE%C^kel=tg>vm*`7Rd~{0oT&fbNcu9^^V+wcawub})LSC&clt!XpcE0N zH=uPQc1c95ExwqY6Pj3NkB{}&1d#MKA%fSJ_i@XXAwqfyfTU(F4(kdloJhv}L6ae0tAvTjlJZCOg zlJ#adU^lx(y#%fFy~JcPu;F*qW*=!HYc7CUXn3CpwrVCz^IT4mcA2O_?p29O-QycL z2;12RGvjX`Y{H6j5n#^aIWaVUJ$jbNTxdl$ z0YV#?1_l>w9=clIw%8<#qv3(FLVUustmn{_-6#=LHUn#)6Z{nE1UxQ`Hiv^}RjovR2KeK_Muy`vmF zb16e7d_hb%O!)SWD8=JVfQ*<*zwrQEO8C-;cBI_@6^6%Cx8YVyd*q|!-V#*}K-~B2 zAL^?pYOgnq&CaeldgIf9`s@MvI)wrVG8wet99DbiAB>xBN1vA!Tfw%M_^C0$XeOy+ zfTPPJu=;_^25LFkSN>yU=-yyzSE|-rQaT4g!5$Y!C~|=NYI7SS=Lq|i9y^CTVTLy~ zH4V=7x+VHe(;foJhyw5VHVO9a-f&T+s}-J^0?BwMIK4;KybRyGPHL-&&lWEZJX{6i z&nu>(mMcpEB*cEx6gK6*Q2+B%2atdTJQqJqh>3|w^L?rGnPDp!$3O~p*^sTk(A(1M z#5~s4HVFpxGSS&byFFW3%RnVE*j3$2YQj<-aSlp^SSt6qEYF0m?@5$3-ybMlOSj8c z514-yP~leuR7{c#>kPQ-`w{(tM}K{HH^P5n@QmJUkmhb>ak0Re2gM~N>)hnoENYjY zV@adSmYxrXMHY#s8<(mH1oG-858UT;1<+Xhu81Q(4m@W^O-L0|gWV<6u12Jr76&`q zarOR!@P?v}sbT~u+xV$I!o+1=&@ZiQp48R%?cNOrIUh%)A10+%Y8pT=;!ZCXzrGK* zgbI}DM2lLEbRWw`p_0Xd2Cd;b*Yo!A*g~FkMuN%Ud?@Ba9x;9K0@2!B`{i&O^r_-* zb&vK_MpHqHoP;9#mWSY=pVie3@9GNH1f=<(0`HT3_0eLjF8&{hq+-d-@++v6xfcHqm~rH3^VqpQW;?_X6n-YyCpIMk?}~ zKO~jlt0_EEfvDz!%cq zL5HY3k5vyj&vbZXsOT`|(hqNFA(=`H9wI(eD)$NOy^v7Ms zJ-@#5kcy}>)lX9!wX_=Tvl)^%J|8_L%B;T+{w2mok(v#>qI>wHNOEjN)oEEW%6%T* z-q!ZHrD!Eh#cLvlZ#&B`(WAP5JlH9u%gmkdiFwm zsP4M#Y|^edF?_v8XxRFQe%}FaHvvcDPXBiX5)P~q`IS+F_MV=x1Y-~fJ0&o>Rv@OZ zW6@c2^ArTpkX(HxQgj>}sl;5KZbePZ`7i<+VxCOR^F<@nMdms2{`pU+@iOUbho|16 z9HcxE8Zhxjwd}d5OzL3|pjca{=>Igh8))zKasknowd1^iH+C;}zY4}zzM&WvQo-`% zn6&4fsYEV~yAdbKqGm7VurrFXrQ$mcCb_DmainiZ$TZwLx=3br1-|8S%^7HN2Y3clk);i5{aRJ`PHQSj&EH zQ{h&%Y2DFYd6lT<+g~6loJ$Yp$X$BFA#g0co_!?**0zX7TY6cW=5azN7AjASk zXGbmfu=lSsK*sn2eqid>*>@2&tii#1d}wfOdf79GF$MP(%zv891BV$t2f~L)C4Xh@ zd`>~tuiR)lvB*S&IWNhmt2CM)h}M+j+`zrKa3c6)b{T0ZEbY5Bry%YcRS7-&vMINB|L zg4VL85;ZXxD+=qUTy69jT>yg(5IW7Eo}$JfoRI+&t=JGgJ9&ygZ`4mi6xT| zc75F*hWLy_VxTIU6uM@%p>e#KPF*uRoc``@w4Av{*yw zx_q|SM%pml%J+>h`Pa|=5{UVUFH;k>yl`J@npZh+WhBV z1(kwQA+#oW!tv^WQFOrkm-UwvdXIoHrV18wDbv8tHI%Tw*cnmq;!kR-PtyEIdfWRm zD!xxn2+5_#b(rAJ>h`Xy@EpG}iBz@%G>ZRLal0FBx*Qj$d&N{t@bX8iJB;0jY!mc9q{$uPQ%VJ=?oG|iantvxEdY+WFsOhIX^9a zaTtnTp^7TqM9~VqjDOEi$l3jme;IdNaZT*`CD!hmAJ$gM+IS44zge!0zF#;1f&g6; z9y4Te*tCy5ShaMdnL%qGUxpZmt$Q}W@rfDaGJfb~LH}2EO)~_vIt5m}u z$S9&c2F@^KkIgSr`j_jtE+N6tt#kLQH{(}+s~o)Qm8NuoYh*3gYgWa~YwIR90SP}o z&33VdGzdJcPh|zyhWJ(}13ice^_9q_6y%Z;09AOaOBGHdV;0Z&Gz6`bbOEwGA#iZ7 zLm2&bf=JB(>0rw9C#{}LfuP|JfqD`QH!fO|7|g_Pmdz8N5kS)#oADk2Z7tA&}vd03ec!^E*?> z=8u)`+$rB>4ix&p1Z{)JD`82^l|k$`_H#Lk@H_}KAKr+*e02&@7REO|k%8=X9f(msa z4$U(SUnJXmd;+DYfLrg+l4M{TN(-<+rf|7kc2p6};ZBxeyxEcxqTBy0^XyP^sILe# z?3yUFsPI`4Q+h_{@&%Wb{rB12N|VP=pIIdEVojdURf7SYcf@IFK-DT(R#VFaC7e;E z`?dmTF~ruXp@+X5ACoWoykOuJw77&MqGJMY&_FV)s-_rNL;;Wu@cc+us9&O2OR<$A z4ZO2%5DR;Jc58T@#pjw1l(GlMf%bhs*lSmCP?<61%>Dk$ll2NG@o&i3*hzdA^Sm;(%6jTHR zq$s1PNKuEP5D@7tQ9=(85djqi73p0CR7&Wbgd&2J&O^!tIOg860Oa{|+&4iDdj-dEdSkq-g^5sOrW za|;lOR%J+U<<|t_fC6%39Eku|R|qadvQK=?Y3jv7+p0p!1lPe-(=A$+YfyL!N_igG zx8JMuoOz2tIrnCLuc@Xj(p>pd09jW0hjZVBYE4SU*ffGxYj*YJ{;|{~>w7@1pAzpw z8yt(hapT5>8HO42`!dy8;TPoN3(#=tKu+EwQv%ht9=q2E9rm1FO-H{>L6trlYv3Cy z2zZW$->v9)3GSLP%-hzNkgmtj!ZDWn$1PcoH6U~e0yiu`m`}VyD(sKJ!2<;>U zL(>@w^>7tG!tF-|od({yMCG9~1Wt$d`j3cC0z&w}x!+??TOZ5J)YjIOunyoLyuBP3j%D+!xqSf}*@>u{T27_KmFv#_H7R@o*BbLc&$U zfH{rCHXN+S$K@f@F$Ja)&Y$MxE+8ascZ1dmOk)(SDvrZ0TzV9U2F6;`WBHc=r3?5r zI89;7p&TldrRX<(@PM+*yUG!Ef>5tKR-X$-3*SFSYyDdok#3mpM5on}Wt*g57F;B1Ef#NJA%B03z4x*(bX`4yb<; z>Vcmtx9{t?u5#1pf<}m|6XEGvM{S64@QJz6-QMk`l~=luFiR28TA-QrGKy>#;=3jL}ORsswyfK8Vas? z#TtKB14Q>_^k#d)VXbp)1at}C<3y@axb`2i*JTl#Rx{thC{TIDc%;Te{`wf#=sNsP zG&ot1RXR!0LDeDU<4(={bvg-JfmJu-&sgmoP@Dy0Q?U1)L*+iG0M=Z2&&WvfBWps6 z|H#_HIrz6-pL4&oPA5bk0h1tznVzFVppONHVB~EXu(_3)UcF;RsfX@l8^!M$JJ_+T+jm3N|uYr}>PaeZSLv7JghI%SiUM zaRON*jB}k`q@B89yWR{HlkGNO^~=?)?9FQ8?>;G@_i&o3rh%P{#zp1IQ{yJ)ej*3% zzV^JX^^Lxr3(4ZnVp)wtG6>Zd4l=iU?g34o?jv(CPj=ab^L+p@c zrF=+z3I(FaYafn1VIcv*=+V#>^S=6bA4WIeY_MstH!5-;2{^&~W)}`lSW3nzLSpxl zEA0V455|3go5JLe!Z)22{*zx=hT=VQVA$R1g68)(LcL&crILR8mettW@7J$8Gq_d- zfjNSw@yuH*e;4+DGP!9@zs(X`$1pf1`Igm*=s5DLpFyg}b%I~K^;oxtvPzdv<{hsq zACLKKk8RPg+c!=RelG%$PJW1nQ#fCJ#G~%@W7`%_Y%*WOga5e4X~{kk+HX*aK6_|8 z7ajhmT4eJGd^Enl`uOvrQ-|h>0JxPJNQ)ZB8`O9fc&M+XVDHN0cN}{?Ct@i70HET| zqQb&@ycB-V2PWsf7dzG;z~l#6*9{C_X+YiFmR%fnmxYwgEQo>@^uJo)|2W9D6K>es zU%berA}Y^VN0wjyQ*8IY*&(u%JY3+B*jBd~bb`CXDosZPBd~iK)JoL+Br)boFfJ+J zlVi9e`_dd}`yc4ttU?cvZR9w+eazFudA8f>ZMMip{=w|y`|RGS&8VP|WC%Om9ygx% zzj=tip7UB|9U;V_^+$$J=7zj)AkWZ-v`#t&|@>9i79uRP;=LT-p}Rkhb3c3?_< zPSFBAlI*YTZUd`DXF2$WZz)%OvlToR(9dc<8z&S?Ot#`Uh3k={PMhbxg<^mt4wxCCF(~drqLXk8(@Ng27Heuj~M-Z zfB{I{LsiM3>|O5_fr$|#7JkacbkCbQ`nUYo-L)uk<%{WJ=oh1duLJL#R+ULQ3ewEo zC#TZ!d+cPZ9|!n~ZQI>av?;HzbpNL|=II(Wu6iY?U@9x;tzN4B%#KNawv8rp(QL0! zs>yHGo=br{!1xKQWBrH3KH1lS<6)uR0z5;ZWru1+c&;dH9w)7TP8q~xGZBm;sZ{cK z&A#zhKeIWG9}0eMGCmRaL0aLxV%54jTM(=tp3&RiWliKA`Y* zl5b$i8c+N&^Q-hvmKxBpj&*GxZX76IO8*bm`s9t)No3M;+W*)8*_(q5W!$d$<$q*R zK;*h|d>&)8E^}qgM72}ymlm|&K%!ztDu@ZxyL($xfY74##s-&45O^#$S;<`px3}3B zOH6va{ZP7j!9F*qQ_qhcJBvG#74PsuwQ`1gM3qfk9{Fk+owWbX&6_vh7n<$a`Gl`< zOV6ogB3_~36qj)zAY2Wem8^uorY01VD$RVR{KzSO1(XA4|fTiM5 z;D_PH;U~b@dVtNcX)fDz;HPGxM1rZy9fF5{V)K471i#o&D!`dTsoGbq4}ecGj}*zE z%N}U_g({kmYS#KfwOS+Iof0ma6j>w^>7ELQnVmrvL=jY$KPM7`tkSnx)a-ql+Ik= z1w=>x)7eNr2^bhhggBUDLvZ#7+JjGK-)fV=m@dQ+k`L$zKOU8mWSp_i!F<^72y+W# zh%2!QsZAXm5VYHOCgH$^P#tgnW6ux%1Z?|#)5*%s2wXAX)+zG=^%8t)t%zF!_8)DZ z+e*Ozpix~|?2`D7<6l=^xrn&#{NmbS!Jvmr;lY&vJjaxH!O6ka9Xb2k|M*T@g1>A! z=gm1E0e{wQmt!{F-48H4h+vpn&8t#{XZwWUV3&uL?$*!>^sPA?_e)-ReiRP7z}7wL#1YZQZ59@k7 zeK$_^i)QZWPx;C)0!GH$jRFi9;@fv6gXv1%=&!qf0+xK)q(3fJmmPziKB6^Tv%CoDs7K&kxuu4MeJ!<>rEPfZz@xthH^Xz)GZbA}jX-$Avp>Hyc=cpK5a-6weCv(_ zK26t;%9(TeE)NwtL?8$@+0qIG0fu+fxVN|apn|>(vPm;fp9DR(m@7y-tMQ$0FHuc@ zim2XLTlYB)k+UoHe5c(-UQxlziwaSCWF=9dI<98 zVgo?qqzk^q{TU?8;+GZ$-ji{;{@`=JYSXr&9GIcsp>4NMgEV-Z|G?k#ZFD=7$po{F zq~tS){sa-}wSw)KxdVPp8{>rhib-|4&Gpr^^(J=FkC`S~sbLlE@&WI~{frB4j&p7L z_DlxdGfBWuQXw0;evT2JG60$_&=hM|gf_>$_{YiA1?))?{-Al+{$*l@;p_a9O2EEIU7aHmVG_fe_v+n zwpNAB*MaW&$=A^o+!k86vflPf3v{GJSDHD}(X%n-QU_sqM=v1*NE zuH(&|diu*>P6u;W?z1;y7QgyH+YloQX30m!+qEYL_Y&AO?dy~~w9Kdq>b8XW{5vDI zTeCcgdq}7)- z4>$`@cC$$IcWXp-`R}oTNGIo(kGDKp$;3n-6gg<+^(ll$u!Z(|@xHb$QDJ!0khAsX z1AKJFYvLwl(A*A~O6tEdScjyB$Meo5q-E?4@y;x(f`5Ai0%d(vT5bHmvI|#(z*#YdKl4hyE<1XQZTAupO1SQbj#a zV_B?fOoc>BojBYnsmMGP>ef-sP{-Y6E#4j~wW+2ODlPn_)ODEBT7!O?vuJ1&f043e zIrTGN5q!>+Tvh6o@)4xA9@pQH2b@zg@O4(}jY_C;HDn6Z)R8n<(jPB25>?4;?=l&R z(>iOttmQ3%_R&;nJ z3>7?#IS(CCs4iE%9!C^%;8eGz+g?>CbxKiOc1mZ*YXl28jrX=}e?QS_*uh#6XOcu`o3A>MLMJ=UQkj60%=lvk47oIUg9|C`xPX z?NXA+a;1iHpFX3$*8c0=al5XsZw^UO#eQ3*yS@>ds&!m{8Cqn!vUkR~_maP4j~Vl& z^pWrs?OQG`8qycl9-u7g+DxnYZy(?y7bE;c`sk#vV9k_D3IDf#MvPaeQj*y(QSqNn zPy z#l^JjYMdluW}(N{Nro|q=`Hd}JC#;>ZJ=g8Y)N{g!Y{kei~gfVgd$LPb-MF;m^!lj zM^pzR8_jk1_sUZWVhc&mrrS6n(k^p$bq&*(3Us^+QIlsL5BP`G9xGQM9Y<-+zjsqc zWn|^O!Xh#OZd0B-KBHD+jeWRE8@9EQga3wv&RLclsQaLNu-)1iAr0r7*h(7#rX#R2 zGfU8R%~M7Zl#AS(j?MIC7PB}PsT18(owA{UGEWI1>VKuDEj*#1d{y}EL@gPle)`Cn zb0EcNY{qTJjuc>ivKf(oefg8greEP9-UQQv`1X26vy( z_KgxP>g7%)td?RD?)Up==Vg^$#j?f_DLUrZwVT^1F-Wb`5i~!m)r3*AK~qxBCBur< zniic>epyG(n)Fba$DKj@M zn5eI-QV~|UM81Q0_`P1^-Sm3nWxLg@;v3cMS{z1BWs5WWQ24Va+%(&eF#;WK}VLO;Ae64*&q-DTfqCvs=NyMS> zWbdbvJ{@X)HWXz=g*VwjzHxXnUFYVYX*b3o&FPteb@poMzOtTbO;S+5V-PJYoN7Zg zV-qiM<;<$d>%2qa=}0D$7QM1$Rz>#pIsfdFe3$EPH5b=Bi@ws3;nuSZar1z6#om;< z`w@bY;qEC_-m!x8i&umZZQVB66)RwmM&ETnzb8tZZ_mYnn6aRi;fQ!+##ty?m)720 zKy%UB^KfiMrpO@JS~Wo=?k4M-_DI=M&{}G8I9o0#^un(l14~iGBMsAe%wj#H-M(>w~*z0Y!ortc$%nEO)7-vy+k%&_gJ%#8@kX|Y`^imT+zCTJLb5%UqCh3D zoLRc`+_s~$?~{nFJEZE;a;Gr!V19vtE-O2TlZO>}(SXf}{12RW0&s(ImkT{`piojn z(7+_+%r6DV@If2vh;<7s6_AY6+c(L=36;OIlK)7xez_~_f|g{LdJ^%fEv#wGwJQyhTiixrz&SyK>P-(op)d3f|Qh0a$aVaoy+~j z3XwGV!=2)EO~MdPtT3C)z<6A)uX4%euVf!=Cpz@EC}GwOyC2{Ctb&w9e4Ti7j=Rb` zWkq~-}l`tuGoc3x-@#6isqTD5ME z;_elTFV9$4j@>Nr+=m6}l$7qP;CB_O)Sk3-r^8EAic#{_) zW?k0qruj$^8lLhyv2kE)2&P@oR(x$v(PP z<{{%`?%HgP$SEg;8VMQ6npab!bGA2_mh|)*C;kUXIw$Me%)Z3 zO@U3eA4!=UD_(u)_Y8{ zni+a@_4XUuI3o z6x;EJ(UOFE>IMe}r{4Y^N3Melmh~>^LKfOFWZgg(51&r~uFdYv=u89ldUi*GlfdLQI36{mZ-*}rE4W$qBMqd0X7#87oRfx5!=9Uh} z-`2utfAp&F)Rw&!>?ZXgjI&G`)IX+XDy8A`7QoXOJ_oV;5{U!Bs|S>>VO8L>r1M)g zCK%ApyD@ioV;c=ME&X3n>FsI!u5p@Ytp{pK`be5*Ugz1k3MCGAc{M(k#@;B_jnr-z zQlXtiL3$2KuAqx`GkX(|JPL4L5F8U*U%VhpA=QrRNe0{}Jb4jQw)YKCHyAy_v& z_j@tV%6-xIr|f$B$I4G>?iWvW)dE@Z5`h^dbIevjOND$6fKLxo?-hVZ?LOti%nAr! zwSM?=*O{Dt@3SPOO-ziF_PwQ|U^Q-~->KOdpxDotogMJZeg{?CC#-h7d4#S?KN^K3 zs|-~*l_JbixQ&WhSU*j#Ib5S(T6awLzC;mGU)~IV?5$(*(K|Rn$2!|SPQM>9a{D3LiBAp05uJg@f^7}WQ&#|`clR2 zo^!XtegHLtpI6a?TXoq7Iou! zPn$1IDMyw{$}q4x57`3aG87|nV#(K{c163$gWOjbQLO|hWQ zkZP7bxnp+_rUE~5-!y(CA7m$Yabk~BezP9Oo5}bpE?XTw{(6qfq4|6@CbM!nk1rLs zZAkXIv%YjpHC|BiFfC2B(>i`;kt!%)czm9;lXgaCcAXV0vNBDXs)#cDB`}bJ$U-aT zD`V#F(@3cv$mqtp4`E3nS(dTzmD@pSEb_$}*CCC%+x}s%j6zl$4aE-!HVZ$Ra^TE$ zPSnKBcJ5*?g$M1|A+f^J&KfS0OF6YA`31Fg8(|_WpMLe#bX-%G8Lp*-yHU+@Qs2j{ z>Z8LQl@!@R4)z;yu%Hq4+;L{27I;%n&dV`xyZ^X{JNQ>XaVQ*g;8VCFTg@{)^6S=Z zx8?404PT2D-BDHEwLN%t&phYK!$PC|e`jPOQieQJf$?3+Qp2Y|!%47y5xH7lw5 zNfy7A9CB`s_F4RK^*EbLv7#i?(H7_Xrb*|we>!MM=+h_MDD?xIEm6Ek0)7j623Hod zxW1CeQ3IDoxTd9sFURzveV=>I;2|Oe1K!T4z@@ZfHRVHjDJ&BAn7F5ps%VY>JRIrK zh!ywLT-9}q$bXVjo&euRP{rq#scUARa0;dVS*XKP^PxdBB-_GjZP4l~CXtrF^$T@+ z*j4TP_-13LcZ`WpOPVJk)X>;~vWn>tQ9gJX;&owWb*-Fx!aLLzvcP=m+8I^zPW5%3 zocCm1${FMI#0 z7=IfsD{4UatW10Sa^ccqkZ3SRC8Qa%BH|O^MW}9RQneJVXsGnz>{v(luwp%S`YW*K zn+qNI9t!PZsj|=1_i9(0C6>`V5?x$e!HrIqDNAGf>9CltO10)~+?84drz9KGh;n&r zO65K)?j_ZdJhJOU7APgCL1(V$mdhche$<&PD{tr2+#T|5>J=dAFN#PhN2q%_+pRX*QxPy}S>`YqIj=kOa!j%}=8Lau^$)@k{DN&2HqL6-sB zaHEfp(|h6SfA#;;H*eg~Z!j;60KLThnyR2Px(AS_hTV2&_G}vtdCZo%8hz3AXLIVF zin%Y=6Pg2S@`i68O{Lv{by^{jhE9Evdb%2bn!Syf4(b;R^@_0|oS)9Ax2RssLcDd< z3xR^2FJpMctQS2>mhm*}8Au<3;5LTYFeRaim4TRxffl_}DMh1LUim{l)R9|}RVR^G zAl7kDM8FsjsEgI*4^Uh(FIk*OvqsTHoSXQH`sb9Mk&~#`jtB4n%Zh2oN6;_CUi3Yy!@kKFg--9b}b)4%)NpdD;;lvv4&B^ zm~Jn7Q%FWXW<3PmoN#8cp*q#4;iW$m5Pat*S!QBlhn7i=65CH5xiBkYXLLU2Wu%}v z+mv36f2tv~5vAQq{8o!~7m!lw5o{Pb`>w6Sio`9IgrmmlSz2CquwUxQ>`_g>dONzg zcYJ@445ufJaAtONTsY>AHdlr5E>B;zr4KdoLt4h;g&<#$y852AX)~^asW*hY+->5o z{LH1?L!ii&;xcKuq)t^Sh^x(Hneaw3tQo9illo>z(36))@(G$6F=Zb#@fgY|W&GVT zg8eorze8|$9E+{jmCYhDA+jj4jl|*zHhNL~-2)OA{YAcv0z=FTOw}^(Y1zKH z$Z8W9>3QmtK>GG@sGjs~zE9-lraF1^2v1c|7o6g7Dp!EDZ!$?Xxz;Hy36 zCB~j1X|FMFb*78tU%*a&~=!a}$|fxp_e;jT~6G{5Q=Rpt}hiRm0Uys6*%l$_lHTT#7P z{7C=f07#Sq6w!snmRh9TFI?|*as+M~wep_c&`;43$Q7HhkBXb@%B`e0~lhbv=!~-SXA|!fGT4^dbO;HG7Aoa07pDXkhun=&c`XuP!UeCtFPl zf3}3ugaR}s<5@AC=#xfCVyL;SdKV${X7{RDCgA)vvS%! zbPPI3CJzZOB?7w9<7=isNXewI9m|Zvi0WV^c9$(77qw=zD$2R%$>!Q?=`xQC@Ozg0 zXPm86z2zA!qY*?^km4+&j9|DfvShIbpj+uGYp|s~G-92a!IsY1n03KC!he`jRiC~w zS~c^9j1`C^Qu`_%RaZYi26pn#I1#lQRgi<`q@0?ZX>9<16HR{LGzjdi!v(GiepE5X zOgB7D#IBQx3AdjaUrL!8OcRA!ePwYxTTa-v3&<#x#6_`Zg~&cL1X;(xrIeojrDAy+ zPbNo;(dp-E-Ilq!9Hls09x*Kdq4{!!Xe0~Dj@f;r*IcC@am;da^JKq(wTJME<2*iy zbryew>G%vHA-s`b5TUNOiah0Mv`DRC)gPIc4YUSZv*7&AF-6% zEAcx!W_#hVl(9%>>d`;S$th{P6y(-YqzLi8NXLr;O3!3qx z&bf7-m~h?P&lP2z@s}J*yM3+@uZFmbKkUtO#n9f%PY&lLLszAVsL6N)Z166ZhuK52 zmY3297ciOg-H_joFhj27rxGhIYHQ|HWl9OZ2ChUFx{{zCfCGC`gUxEnu^(0y&YeZc zr80DvQtuNZs8ih6U&kCmmp`=Mn6bgbRWBZC%Tj!p=t|M}eiXybL`L)5eKv0p^wQKxB8>(Ne z23VK221;44h8t~LbsnlV8mOuGtqe7ENCO9bqp9ItO`d}}Ke|j<7|hd<;!5nqzVOptvg(B8 z%2!cZ+t|%QF*IG)1+`LJ;r`d*tF_wjah}x( zI^vQQXDQkc3E&N$yD^jO>L=n2!`pLq6|~nt%UdJ8EUu8usAT|0kjtG0Nweu*wXs&w z{~4xlZ(1>xI^nINK$!N2tAa3{4K}|gcISu7=`MeSY2I%?7pAWz)iKxQQy$+c9PbG- z3Lux&K%MVN;e`!D?2Xv-3W(|4+fpTo)54-!hS$gIR!p)-ZWD6=$tk(LRJOU(@eW?#F7u}M-gNKi!_ew~v4DbCx4h#gzcG7!E z8=*E8G=S>(Qez}(9#d|kBT_kv)EdROj~0fRC_i*6AJ^3;0(FI3Cjb1t53TMJJ1WiZ1&R(5p-Qvn;MB#-UVN2lxIrpq)rLh zl{c|EErR^LMqyVA#uvi7YnL24yHTLy?#GIKKw_bXKSzX(qE1g2!7i$sIgr*qA!tWc z6Vn77jt0=nmKV$>V(_VFbA(}x*u#ukaQ{bw4?|BlYET^ zjfwu&p^;K{gbP}9z2NQ~%xFH7@qt-eI*%o{lTiFVy~b;GAM{$@OzE0)bOJtOI&0zi zADri0EBjLY##ssLRub`Ci)*xzOa|gflqG9qU+7rbXl%y7D@zk>f+O97?I1#5yQ2 z38Z&xgA(!mqbdCJ4P_n|0k0&suSIyLj(;;%NC=o4hy>D%j#2$(CzNML4o?ocVi&xH z2x;rl2)q6zl*SwwY%a!;= zx@NBQzYvPNI>b*HWHA>7irVI;WQ^3NIDa;4N50^m1$oer){~n%FaLEAE*uGNdIoI# zM8T4rdNY8o2vw^3^3%qNiB2mjIA_@-!PE-+b$~TUjaUvgCL?ML8MVM)#CFg0<-S62 z7evlS1iOK1YR6EhTU&>KeyXy}nL>P%YQP1@%$ZOhZ7`6?eCO_snROXf%h{fk<_vnM zRdYlg@7~xH54v0sxh!REX6MA~JHkFuAG~Q*Q@En?M^SKK*AhrCRlU(W05MF;-$tsm zi2I~JSvn(d@@)WW#-)w9gkaZ9I=Bg>4Iy|Tg{+Shg?B*?D{j>>Ojb?bCO+M(p#_uG zfQqeD=#+6Ft1o2;bGfrn_M91{lvF*Z8q5uJ;Pb8irKRlGfGdAhW&f-C1lTfqZkm6S zYtEe9_QmuMp7Z`s)?_8y+g0W7&z_7Y=5^_K8Iw5U;om1vrO-?>y7`StYbRL?UI(9` zc{5s`5w|B3I~xbh*rp+_f?s6bD}J$bb;zkYx`@>~84QclHRs%zAKt!(++_}+x+Lw1| zm}^aEWwLzUj+6X~rV%b9WeAH8BTEaUs%#K=qk`sOMe8eGhAcqdet$#P>d-7d?Y$iG zy9LplyulxKT7RIe)zK=3fR`Xx;8vy&0pj7{%-~F2c*PAiAk^X9@T8I8no3c|J%v)o z!f|$?{3Cs9rot-6P&_F7k{_dMo>J64y~7*e&jr)L$Zu;9oaWqs4Mi4eF3NRgS3*@$^z);;2eMC0$m(U%TB)+zl%14DlPb0U)0d$$VZxjgR1G=&z` z1}nfw->os1nF!PHop{wxf)`Jbf~` zYR1|Fvf6A^qW;WP00OR(FYO?RhL$6E{~_} z7K#dYCoZi>(g&9-2I=2#gRDVxXLCLh&W0_MLbDzc5hkI&_HiZ$>jA%lQ@V_e%-l5FZ zQ{DlYyX>?Vxtx>YZ*lIRxSS|av`k#u%A=vp^a8~1+fA`b>~_2AJ=Fhl#C&HC(%JJ% z_%)AhMfm;6c(7?s0*zU=$G!b~c>3ix^>C?Y{yvT%mq1uv0W%h8HJji5%$7Qd+Zr1B zZ)H&N`OGWC!@s4Z`FQZ{09rw^uj$wRy5V;+Xft)M8A-L-ecbtYHzeF^a;u(DTw$Cf zJsA@)b?ENmw56l9*>3Gab}?NB4w+PE(poqoYA14`zQ#m~@a>S#d!FJ;aVIC2C@Wx3 zh4RbR+P>N$q^KHP>uB(>6B@1Mpa1fr>l2`HnYD)W0%MS_joP$|TlW$zk~w-9SkX)L ze0-hj&<}^=9n2Aomc7X2QzP^h&y@$&Ge`tQ;E>8G^T~JWs(s(z$<*jgkvK1F7N##| zXCcS@rjS+#0ViHac)?-ziO_!yWK&G9Jv-YvQ2C5*eG7&1s91-l!OcO}aJWdH#$7ge zpEEwILOmj?z-H`((K=cM_E;?!&hJ4lKVLsb0TZ-@W#wKVCZZMkIAZ*Sg%qI> zboc4(6R!yabZv!(B#ltC4A2iwVvPRiA?hA`_4bYyW1yvNw)4|xYjjiK>8O7bWFuO{ z*Xz%en!hi|Xmq+S(bu@tQK3VWO5$sD(6cKf{12^`Gi;Vmh0M(!SK#uLW9_T!urUpF z{}n=DQDN36YW0L3MmBO*!q(TT8#W{d^78e})E0X8JxxixVc2!dRfu$_dd z!Cb$TwB3&N_`-ipHyq!*+~w^p(}~Mq`lA7B0A^Y20o{2=<=`%XV3&P=XwQC9k1FfV z!?_tm~2abktY7Fe~U@8p%ei=hmGvFn5ge$e^>J~y^o$5u3Mt6HwC=f(asOae%Fb@ zMeTGJ6MwuMseJPM$ViDEZGG+4`wVje=p4ns(F|y}m~}{*lxtbb2Wxgt^^MY0;Qr(~ z_iu2OTYcNrv{{RR$$twCL8vwRafcPpjm!D8%?`mX7-)tw;HF6{spZo&vVEjnLEu}# z9l{!u51paIl&8mh^6|6D#53vHz2#--=yF6oi}*MvKQ&kWN7&uzD<9lKpyCYX$x@51 z{cd@daPD#^EheV3)&VxYtw=dusv@BhIWx6MUs!o0MWKhcttnpEKZ=X-^5PpK;Kftm zfN;M#&ymWImxMveE@iRFBm!>vDNs=_jYa-RauPcQiO9xWX}2jnX5HFnI5kVh+XBN~ z3)~T?eR7r6icA}jW|7QR{N<9;G<8WEL*p29K1X{z+^4n5Pktn8Q_DCl5K7I;xkKqZ zMR>2}+g0M1y|i?LltY~ov12u>j<)Dd^eI~Y%7hze*DO<4LN_n zOe~{%CjKkNCY7nYhPZS-Xf`X-Df80?Q)V!1f1%7p{i`YaHn#3#CtMtF7P!pzRuHta z5JdkMYxC|N2(O>7;8*=YcpXTp2H{nGGrYpB_KWeD{1INIe>S{|^F4Vopl%2oRHr7( zLn{?tMZHtXdcZR@{OI3sjNrOUi)O4wYa}O%MOIfSvte>VjEh|49YX(E;U`5Z!+akJ|y!;cQ;UFLmFeDp0e46uEa9ogbva3*$M_9le$ zFS8ET?L%sxmV24!ehHZ47--xy<|-CBlA)ScJg>MyhSpy9ep7E=wf0ZVe_7j}+vl!| zlr`fm+eB8-L0Cm)Sbn;D`Kt(1jd~{*i5#A4tF`C874o_Yvg^_4*RgX`@5k-xZiPQ% z?=+!%jl0#7XM;v4Iq#qxgn(3>EzK*FqIYw22wTgK7eTb0J;#xf)6dRNA=pswVLEbm zZG6g2DQ?eO0WJCD^;bR>bip#4CZ&kUgftRNBvYTx@8|`A*pQ&i+Ke0)dab#cz}U|| z^EiK)G>Q=MU5h}*y34B!<)2#{d~~OrKTlr52QA%lyyA|yeFD?Th7*SY3M_n= z+7yZUJ=iT!7_ve_AFCFkIuqffge9`Frk>fUG&WvuM5MvVK$T}mKypp>FL|KO4A}Z{ z#FEpfYqHg+ODp$*YTBfZLl=-*Ese)~b^zCNZEHPspR3n>r2JU~%&?^EsWMApWv6Dc>?r{*db#ece9s zpuU371W!I&$fFXVMJ<~M2n?K+^c%}^h8pPNc@6d(VxX2a_f$?>9Tuk+;RSK2a*dt) z9YyRrcIr=jmLW_^vl^qJx-Ev5_fU8P=+{lm!eEnc|va zJj#jPJ5(0%LE;@9I%Q%eUlxo{*D14djJbRCu35k;)(pZ>Fsw7?9j;cPy*>@gZF9!q zhMuR@)Isvh%?$d*`jy}1S>Vuw=prgMV<1$7wb{kKRTR%*XPy#ttD&BMI%`L6hu;&0>;95?kgl`#P%W^Yub zQ^+Hxywi?Jj>7md)bJ^hK#oKC`YBOUMtBf@k=-oS0uam}pqj%fSsmn}haHz?yG7{J zStwEV-ArxUvP4FurbeO%G+V}4je+5umb1*2n?}43GX5I$_?jzo3EHyoA-SL=tPqzV zBH_fUdw$rXVc=4*8#!dxvEve+NcQM0Q21kAM>v`v^Y_5aJ`NxR_=-wtkQB=(uU?Vk z(>n(IPqXKEh$3=MtU?)ei>n4|H+*&yml3mtu=to6(uPhnSgFOYbCH z0(LSiG$+}c>l&w1*7ohchKF>XZ=RF=C9f3qWUF7*S4x3%avS84li8t{4jR|;fR@3v z#iu_h{OdWjdvB|-c(Kn4HGTCwAaU#{gUU}=Hc&F&rne#ydm(D9--;U^aRtM-rOpbS z;!mA-xW#KLF1ZAa*=|Q+>#uD5E67_#U%uSfVSq@PFTn=(Wo_-*lsObPWbuFT+6j~o zcp%Pe0eyI!5X|+oTW<_XE^y;w_ZbYqbA<}H8oH#5v1!Q5ODGAU3ByfQp8ZVlXc_t& zR!RP_Ykbk5dvoIHa{vgpl*I^`8JFR@&M?=tD~F}k<>C0z;b%&b4^7j&BzV1dfLp=K z2del+)JuNPuf)~m@GmMmiI=Zv1{5R%CB*x!l&+3b^5^I>O7`_qouR1@B+k!kF9b&0 zf1L=C87OdHrG%}^Z;lo{(6@nw`+-y? zjI-TP^iEGzPYqBwUVh&WPrik3mD7?T`FkFV5$l~*&EC*-vEm2 z&&hlFLv>bmd}W6s`=XowPNsjl+ioZUj0D=n4*x}B^&B$3c3eZ>#?8y%*Os!SjDsJ{ z=*W6YTW2iN);Ye+M#o5vdRk9oYjd4!Z0)o4Dx3Nn2|kpYj$(J{JKC6W_i7D}`vT73 z%~riBfhu{imNH$~+){IB$qPZ9;j?1Df`wZdmQM@pPtfM>=oWXA4q}?i<(gUGu5Xr9 z*d4fGMQ|zl9HRY8Q`O?St4QbBoI~`!x7E%C4@Yk`PCjchdS`W*RqaI>D!3n0{GyOe zhYATA2G9?vk$purHGYQPWW0|!@=PNWw>ZNks4W@2iCUhBa~13Bl#$jLM#4U{syvg**n^v)CDq%9lm1 zH8p=XUQ+U0Z@+R`*23gJq@JzdzS!!KhRIs3VCo{%!wXm|gn?SltzGoPZ|N@Es9fK~ ziQs>jr~fB1-R_dC!n?qx(>VLFvp3YQLJBLobJM|~M0nrI^hxorU(V*h_5v!leh?y2 zu0Nh7b(>Ut37}U6<PAp-nWsE%#I>uWJ2y73^L<9A zLSXgv5dozQVWO>&@ht1|2S4N|<%}UPTyjPG)N4C@t$Umw1iCK}Gc$}rEZ-nphjx7n zEZX3FklEn}RQs+mgWV2Z%ia5=$E^-4Y*e)YNcn=x-1FjeYq zr8~9a&ehxRBYTB5&3F;nu@RFw9lotbTX)4uyr&dDfvSZ7y{3bRE@qZwtKv?a$+bk0 zkjy~Qed;2$<~Kuvw&>|jG6QDY;}oImx`GM z7?er?;FpkOGTW#}+cJ3RFP?qV{oo$3^r#l|Ju!VlR)a6PZZ}ZN{{S3;ycXZCEy1ft z{~)#0f0ERS-L{e8)-dRZ3 z@&9BJauLidegAYquM+pn;vio0tgePj3H3W4_SvSQ2dH%1S6+-9@DB+4Z-#&-;MQOb zyeN3&O&^k9bkfgns<_5mOu*CYfWE%M{Bm;dKddUY#C{ck1BdL$;KP6Vxqben%eV~6 z{w5O584t^Hu$g6Rf?)j$G_Pe$9NPq^xqe^X!AK;m0~rlc!nyaB|$~Y+B7HABBx|uO@FZ260_U# z|4{el@lfvlAMi<(C@M*krGyYlA-hsZLL&Q;Y}sWSj3ud@vSuv|O7@-XX0+OkvJJ+N z?CZo}7|T50%Q@#hExM0>_j#Vz>-nb_FXz6m>-u~@-}h&Ke>UlxfG^vw=qJBJ995gSw`75tXFQP#f#<KzewP3bv;O94KYz6!$^GX0f}(w2 z?KGHiD}UOz_QAgADl~fgB1Zx9;`kgCO8tFnuK`jUNfGtOw>T&Z81g^v*g}~BRWT!# zkA?Wjb+6yoGn=ep{+&5K$_OPjV?^=J6|gCcfrN9`?U2@j)>2FVmyWlk%gB++6l7D&OIQN7Ilk|G z946U`2Yw4}8C5Oagw99$vyHlYNLNgW5?_vV#dsHubfJ1Ds1O#`y#yO0{QO;Xp$IxW z&b^_aVIf7jr84*ZGBw(pNJ3x3=2vG=OuV=2#Uy9X6qQp=!q5AA2?#7_E~~jqfU1+< zy7nsls`h@Zp6iO5p$?Rycm&V}%~OUTw>aZ7TefxEW+M?=w>Ypq@qJCCnIb54rZrMI zt3mx@yLJ2)-?xaF1v^YnD_yvunkm)aQfn`B*Ey~ww zro%-xBRkZUJYQLJ8?zAK;Jv)>mgvNhiOb#arpr@J0^2p)Iv8KNaM>1*Gv(p;WQ7{6 zIyEsI38jIwS@_Antj$6puM8O)?Rp9^OQAr^XAh)KWq|fXQO>~azI^K`@Q_WWz`ucs zzX1dM1bXiwUMF|1KX{!$bJ;dChU`M{5f;Lw@eJkXXK0KG-GFdWFVP`&2WSL6#n zBP9}stceN3FbgPfu6JI15Cn%@(qK9AY)@L;x^A8Q7j+9_b1Ff|5m5U_%!^p9yQfon z(wo4nT?w-Y#Hq&!l@AVX@KOH@ds&|-MU%j?8GOL*;|W)ArEHzlr(06MmBvQI#vZ-{ zedFT|Xv{WZ0r?IXzWP57t(M;141Y8gI5M)-au+bL5(GB%E7!7k0dg2gFS6!4tkxEp&Yw@_w*6T}YOME%u~*1w|R z_!k$zSF~z3%|(zSYd2owO<@i3b9AAP|3jnIiv-`@q zE+gRam2SQO>{U`pDn;*MDGC|j%A|UD95_VL4)KyH#vlKQb6eKmURC!%I<{5p#NUlc zUp?+Cg<8ib=*QgzgfD9rD(%`ZSKasi9 z#O!v{{spm?%|9}5$~#Tpy_V|#`bfecX~e9XWbq4M8HXdE7f&L;;PZICKs*MnW?vlP z9Y~c30d(rSM?*m!p$2v6^c?w#V0DVEi&l*5SjSinY=+)YF7Xki%!yfH=3W;EMrpNW zI3U5BvPZjr9;8r)$0E@42&-_Td-3TI1lL;{U!~T_g@o+>W@AIrHDVF4b`%>6OL1}t zr1`@Yg%vhUSqeE5OS-j=_bCD+VtwD#a@QN=dZ(0 zopF3H320ZRnEDU+IsiM`FREh?+9&3jO;@N*uRyA`Q*y&kom;T%Dbe%JUu-U@FjMu| zURV3$Y5sWNwV`qb$`)mIjqqfpn)01NG0z@=0U)~YXTfqP;4pZGTcv~WBkJj(`0m=@ zAK2*sK4YPcabvZfNT7f(UTIKNoG%O12Z>p@0P-+ zM7h>p^N~c9`qb+qaENlVf-mKT0G|*{c^fK@fRI?h%#B z6g=<>v)koI3bh{|0fHTatOFx}FH{7Ix2ko9p6m^4zGYOgmDZom@>sMNZ~Pq|E8 zd73}--eye$u9ibaqasrdf4VfPFKpQM0JEi8c4xkpb zG28}H2r26*k+R`Qn&&oG+UC|vXR<}(1mFc?ZWjTg!Cxx#)1Ty6sN33|qe|$+)w$;m z;I_dA_DAjoGUaujj=kX91kAB90ITNDUznZl0j;J!6ax|pbL+2Bzwv7nst%xV!nU|m z!q$XJ8=2pdzM7i*sVkg#g`$QS&tXA-MzP#Ei@%k%)J7M$jHzba2Nd>$A1sWc!h!%( zsIZs^NQRt)j=Dv9!^><#gm9~|SITJE#sI==^JU`$(*G=o-zauam19I})uid82RuT; zWe{-gN*dgsX^v_)ekkS)BMl+N{>rR=0i>%1x%wZBf7EipTN0TIFU%@58(I*Vrq^3N zx=P^z3?F)N1*ujE(c8bx9Bc?Sw-O+yf<=L7 zqu->w)xgaU_*~ys2Y8`G40i7Kom>Z3o(&7T(K7$241a3Lf7JaCO<0=ki7zb{d z1V9MO|sN#B7;{Ar!Jehto$_Sn-x1VZlZL#Ww zymHW~Il6@{63)9>RNBG{@uD#%1RvK{Q382M@5W4%^-#i;MkQvK6xXPXFV8R&vN^o( zau%ajaIn{<%6PqAlF%-~FNhq>9k8_1Z7#}VVAEF-(lVs=0}|!*WrxNokmdd( zfUQkgHx^z$UL%01bKfRLzklA}?^XN!RZ3HAG9ZFq9nR?3u8EW>J>>Es-%Hcyj|7A5 z=tsT6Wi?w575xgN$_Grx+^%xAqBoQa)7DWb(#i!$OMe?&*>%y$&SR1XVPyz^A`7?4 zHg;}=wPab*X}nEu*Wk`vn7mhve_iKZ>P{lHX06H{1t#E|8v>IMfaeuIJeIhohN1hG z=sB_L*;m|xKZRlF4_AZ0bVLk+ zz<}NE$Z%@JvcPJyFh&mNx{|g9N94<2Or1U(ODsBLk};(nUY>t4@ecw{b|e6+OAta8 z#5#HWulE8V)N!=#e6WsIy|>55<0D%_nB60GPD|%`ut)JIMS?$TC~x;Wn@C#!*zgVT z_GqCJ$Z33~Fn`PP`=5M|LphSqS=WhAqV4s0ouZqfyMtMW8fQ|KM!HqnS6@jPKn(4v zHxC}5q?l4z_og-`ru~JR|K}HmxG%5>)pZL@n7LIM3-^a|W{`c|-#h{)C1-KpiQg!@ zqamPiAK%bL99u8a{1rd_7uXNI03c2$u4tT{uu#n^?De`8?We-#lL3L;dwF~N?t#E0 zxYvkAp#Rwj6%gNz9WUEULA{RyBn_zIVqlklJWh111kHbW;5YAd9oh|anWvbBZDPnQ zx3)eg)3oi?9%0woCmK>8hY`x?Gah1XsW+u|K6eIpTFIg=5*WMRAXWO*R;j*$BKNkb zZR%jb-AcO#fxATh^)K2GK&3J@g#nOvT)21Y+RS+XODA`kF;fE4$~;Fk%H7luw`Sf# z+6L+y9CM+2bJ_%oD2hOYB2DJ=(hcFX=C#4q297OH6kAgD@Q-9Ud?z2Np6%KOBx3VY z|G*Z?yO7yU6Uf>BBRmY4hq-Q6g{>fdeSuRth~|?%@6YKJc$Wn-dp-$dbH4SSJxtE^ zeQKi3R`6el>Iwo6`z-K<54G*r+MvvF`;?;OW^H{D#7Q6ZGz&eVUF05A`O-9J=VZH4 zu;kq6BU!Mh{{Q9g%tzk#zhd;|rE?~p&mvF~4E1z@@&vOyXc>AGa(_#NCO8C3=`yh6 z<{dQc(rD@1^rPP-=|DQRnV<*KXN_mz&ho{z@p-&JF2|USH-sqK)&x8DzAMZet#gZb z2~lZa15dC~X6Ua&g!zxR=GUBqKOkcHAYoPLiVNG~8D@IyWxcoiHDIr` z+aBJ?c_fq@io=9c{vAAB5HSY{I%^dFJoprTu4b=2w^G;R%$W_#roZAwubba)%oD(V zmb;YG20kDsLW+1H5ka$_i~65^m}6kFtTni)z!u}`|IFG;i@CGVhwb4m>0yy4=QLvD z*)h{q!;h3R&Fl$msZ!6$4rAKt1G33SYWSM~Ri=23432cEIS0ot9Guk?A?K=T&W9a% zi5CJLyDul37m`kRfG(c9km+LHm{8^yETtT1m88`Y0L)iW;ybs(=|fEvR2n(+pVd@u zQ~%wnl`zn~it%)OeWmMTX)CYlQ@1GRoT)+RRZs1g;neHfnG?d|S{iuT_;&Fh8Mc=Z zDz8vgAZA1GY5}qYYM>CoXSiN&pnU!+hj(q-$bo`Y#S{;a#qD8B8dEX6_3dp&*psZ8 z*-SijT~O4U6De6Aae1!}0sEQjSN9 zzaM)}UCVx9&p!R%m|iLD-jaV;u4|0ue$=$HQHy9(%jq3t@z!`_;ispZPIaNL$S`5y zXnf;Sae~Z~=n~?D#=^AEIak<-WgCt_D zBT}uR-JI_3@?!NtdZ-4qwYC3FxT-g1(5>`{rWfArtVGdewW}STk?e|R<8|mTxTbwm zKR}|`n9PtvV5M&jFHx>pnz)blAP{CN^@M|Xs|?yB3rdW0Bcc~9g==DMZl&qB*^Tr@ zgcsn|yRkS0m(pIQc1_fKT!2PvMl`_c`fe}PJ3{>4_`Xw_dD>TQZdoOOoklm$af6N+ zB}(U8Wn4~k`Ct)twX%t^%XI?A&sN`#+fbziSFQC>C_T{d$uTeX+8a0Z%B-W@6LV$W(*l zK&>}?;IdAcyG!_qmn<62=PF6!2$+(s;Q`(56_yr2=Fb#1z*GkY7aCVd-WuX~j z5I%F(m-@~+j;7V2>QOvrGH)|h$Co&s4KMSE_huI}4P+?jJz8O%(bJ;nAOB%ev4ijQ z>YIY-xV%|ZD%(WaYoh!`m9YNR2co=HWp?QUFRQa7iIbcPfJaC#vphK(a4GMy%iLPM z^B@<)gJ4+5^(rcPOp4sX%ai`~d=^4{uK2lO5)0o>?{}qdUz8NG8|R8)1e}P%Hdor) zQ0cFh94(sL13(A=?j_JKeJNgpemubhL>$PV<7~cj)T5xH#8~r#*bt_$r<;!IF8YdT zqPXOjr{NTH-8b&^WguW5NiM%B5E>}W(@u7AN%C6_T_)x#r@o7>5O>z(2rGDDhG zk6NO&y#uy(UBJ=ecoZ@z32EM(7pN*HSSApO^X6UUP8Bt2=h_Fhg)ETxPLw5CMOV%( z@V9*E9PPM%z+JoVnaY#WR08FgM0#2`uXP;HIWbX*{9BRuUiL-0Z3PqK#QFgHs!7V? zOfDfIRk{Nwu00TcHo)J@1g7jmLu?J??3N1oDhgv43)^d#0;YjU^c1ew16Bp8Z z*canJN$t|VAKPx7OlZT4MJ5rhsdmMS_fyw7#S7*bm2E44?_;b`<;ClDC~PA#ALYUY zASR|<(N*1WU036?u%{xo%?tCE#s#d0f?&NZ(Gh2dwwm40@flO=c`I&FWYbl2T~yO% zq58U?oGU}{2Rh%$t5H|>fz4|-S;}@$ws0nbGP&jMdw|>@~$wP z+24a0|1~9cNA4h1v6e=uuPLLcx9xi=VtOl530~JfV15b3prI+msL^J8VV-h|F;=r| ze(Sd2P`?MPqd`NYdp06E_Y2z9i{bmD%SWzNrCT4r9WRCSAn2B8b|h&8KXr%@>-}^u zF~NI4JRMtsf7roEq7PSmOYisw&F8+_Yi;3@T=}^tKt?5qa%-26fRA$VR5#OFyaZ`C z^JmI0XjYiGQZy2P^8tIIT26Q$*m=Q&g*`f_Y3W%$3UaP)_du0Qx^N z17lf4><-dqX2w*p*~F5{p^bpk!kbP422n_aB5zHx6j+iO!b7CKoEQTJrh*mlN=ZAA z(lH6Q-@fA0@Om?-W5`j?&4UJrbEN+=tbd@l=uhP)5g&Z)LOZI4}zjUZdE9=CEEiv zRzjreu){xmjL#(>YCv3FX_>dL;F>qJo4;qbs$1#$Fbh|Xp2|o~P-Pa;p%;N0<$2_1 z^`Sn3?QV9}ty#d=WL7k%0X3Au$R28nvEA#!4Eo=NnSHKfe%G$B1S1Z01=WRkkL>9> z_FBzVGZsy^X9GIn!3*(@2x}l+WzwL0FJ|x{Nn(68xx^@PDpFEIJ@|~ngE@hzf#U18 zJaNUl>?DsLnxdN|D^t~s9QWwn<(e5r-qDp=k`*b|>)s&kIC!UDIF0yQ3G0HMnQdg{ z5*j79u$!kxN@Ta4CdPecO6$c{1f?q&Jq z#+2*ww5?RK+|n|kT3VV8&sL%d@bfQU)!*8sPJib7g9v&WX$2R9AgNUnD%F!HIK_2} zUh0js3e`LI1hSBrY_gQhJhh?eJUYhz8_1TBHEU5v3k<+7p3T5+-@ zwxhzurOAtkvGFK8$3CKnQy|5k&!$haw{h6oMM`gbfB%9VM@0qUJMa8MlWH`$i?@&#m?Ou@#s*>{txhn6Xtmfv)j$sCrn1@NEU{Tx>=s6g0M7rfQP?Bz0IaSI@9hxF?2i;)eUp{zJFf!c;i0Y47B; zKzjv_(e)|({Hr6tJyj`q0k)D4q~zz7XDm&;E>6z{TR+1m?MTB?Lo1O2Vls}g8u;|v zOtSFthmPGZ!_8)Ye>hgbthvcXgtV(;NrW}tkS5rhr|3)twTGVbfl5R7{6Z}K3Y}>5 zs5Agp?pZD?F^o7EndES;lIM_=(xY-F52XeFoa<`anOyXUSjo^;lBB)I>ybY#guC@l z(SwMcALHJuHULu#t7We>#7l|5eF<|m^Aa~+Q9nK7c^9iPiyb!58)_dS;L_`_=(KfA z-Ck~u%OidY&4aH9y(*0`N>%_w>No->qfU7DXalxF#` z)c8usOBQxilfhipLxsAfngoU=*$=HQu&n)?0mzOWSv{4y)7~T*ck?>h>4)fe_b7e0 z7WZsP-ok9g55AK_v6^+4N1{ET`kuxoohps1YW|Ff?Ys@v=YPv{caj8_X=F(Y16qbH1Wlh@K2`8L8NpFggRJ|WxxX5dH>sADavVhl^ zvJWOS8$2&oJlV}nt0m;BG=%n2VV8c-l9SoT-;7$Sob0L)dI9h3w9IL3XfSe0IAfsw z!64X$0=?!?5!2J_I|=er^%+gpJuY;kxaKA7=TvuVI?*=unv%($;V7_%9T*<|3@hAC zcgx#^vwV6td18I%u+&Xn@~}RBtl5vWGgST} z+i{Rw=$V-417%4*T1}xa(t~XVdw~PS7;Uf1tAr7P)igB3CC+bAKTnmmnv2{uKRs_X zu|So>6B2g}SVIDxyX_D~*GrQCEW7xP(nC@!93`vOlJ<4Lg~z#SbCe^uc2!tsn&uGR z5w`KT z6X4Nt0ejPLw`yw}w6DzHz_wukNr$4V7^{;vu`yv#jEvGE5yyEjo^zSP?(g0?ACJ4f znS#IvMT!87;A3m{Qng|U)g9asIK|ix=;(G|8<(f!q(at z)gDe}ba7$%VYTQO1iCc=aVwXQAdfgP_W|XHOtmP1ap+~IgXD`;)f|_aYN}>#*z+UI zJ@C+o$O!Qw5NY-D#`1|)Of*bH@$k{UFnldnoRRKRg|Rb8jj=lbHzoI@-8~-B+_cZ^ z5yZl@+*GTk+mo&YAMz|sqp{m#T2H8Uy4wmS zbzsmAoNf7NH6qq*t^U4BaJ|9ZIbij-){_Est=r?Rh*hnU8F9!tB;egSu~V6%Yul5Z z5g=~5!B9p;*>V&zQXLz{O+`LvR@GikVzXE6E|WfoX%S(MPsf&-nrJrD4U;D)C%tE> z9-iy6HO``0N=Z;^Oo~?X{od}&uYgt*Pp-J0ubCh{B;m$} z(>goES;IOkeYPScr^DTGfneycI=YoUx>wp`>@ALnXs?L-wAf%5oX#QJx66j;aCd6F zeGfQt#C5%n+AvsDhWV({6t?qh5-@Aa@opfPiAYxjEnp16S6t?r^X<^&q?=@N$YFJ2 zO%unRJ7yFTU@A`M$HW#-mqEoy8u_DJKdV-0K;`@?48hc2$X)q4W5TPY$+^dXMkGg*P<1Ri4Uz>T#(X+_y^-o?W zC0nS`6CJItY-g4PBh`YHAogb{HRJ`Q)0ub*gHR)mA!&HSmaP{ReMahfo6kHOOOpfY zKTr`=;EI(XdS_Z$YgqO*l=WR3VQZHsKbU9b%is)Gq}) zlksi#?sH7DC0cCZIt(FU_%)2!wG!|Bn{j*YC{y$;5$K1WatYNw^>2z2N?34@jjGn& zOLHEO;PGCEEc11N&IRJClQWjg=9^(R*u!0N1pBY z1kl=6x|Cd$-SW1jHe03&+NueHtP7du=^T!Ve>2^iuh@JeW3?Knsusg468j81Dt1mt zzKegKA9fy;Y8dj(ZiabKkbt2Nx2-6yAf9sla!cHyR(oPPNUqpLFRYGgPc3>7?1`i9 z(`Q5_5zK`?9Z2X}a629TJBR>p1o+jwIX6 z9~XnRg&qqF@=gkW)?HwoIb&``?0DqS*^kaJm_7+2ImgMf3QCH``L|WTq;&1{@ueIW z1BxyK>#8z6N(low|ETceM}H9IB*>DE#D(;&=s-cO-D=}JFoowOnd&k&lUu*^^wsAg zrNM$eeXi98QhkCT*`;2y+0hTk(eFstDBdn7zoMiDh=EqKcPunLfHg zsH|yWs6b{jm4Itp4r|Rqks^A1kM+=G-$N3P*D^ZLH)Eke)A7fVhJwD6Z>I0Jq$&tK zJ>gtAcU-V&p~IHOR7<11TxBSEL0Yk=d)Jk^H^6Z-P15G3&{=ofhzuK^zC9WrzZAYQ z+()ce2tFe(h*%Y0-e3D@Q!>qeWgCLyrAN*E?m6r=QlDz>CG{TmrUe*WLZ2@Tfz8Oq zGO^KByj`jEzDv;J%nq-RA*(Y>J4ATVnqJrpJipMs0WxfkuZ2)YfeM6I2rcP6!rw~<6DxgN zupps>Mag$*vx6buNluuroE4}ciPzm77`_-3Tm5D{V>ov^HU*W#P^(f z0%DBBPYe`Bw&sH{z+Sj*3i6^wFYO5&iMQnXZVS^ zWKEz$*2jbdrvE@{5TzE!$AKQ)o6yS*=Y8n^(TK^H2)Vpx$X(5=7#7E@h`3l=yxx>> z&fdWfx8^C$SRV~#wSTtT{omREX7R4 zzV(+`Ubx4&3CK280zxm$PXPUx`5`*U0E^x@e&Asa)y|xHVc$Ia|H)G;10H3t&c12U zf{Ky7urJ5??eYP_8Yt-8e#SUqyvLRYYEn2Z&Okvu$N}t@_@VB}&>z(<*4BWJ798_X zasI7M9P>Wtm>-Gq-%O`0;B+`JuHk#%x;NAX+tKjkY?A4l4>O4J{a!PVGIUFH40W@V zDlN`~KAnrAVOTj1f%PLt{<}xsG5x&2yhLhwn0DHhRm?IaV*h;J=ly_zF@M)o0X1mY zc~h^rNA49Av!u+VdHfh2s#QFRKPfYpY>}(Ii69#=H!j-AAeQ&*i}+3^QhTvb8tRqF ziB5GMfsB)%!XeYQVLc+dYm3eKyFCwsmZ!J~22j@q@wb2itI5HDAA)}DOTV!U!ZRU* z<@Z*b2$EWOUcm@s*up$UhFUuHXB|%|tV^=P-&;prI_r%(Bs-=J3`FnK21`d@)*EPO zfSi03>T@r;Z7bd5!$W>qLwU4=8|e0xod)U7e~FU0t0ur~BUjS*e;9*j=qcP)UJL zf%$)=PZpbR+YYGWeE;8tIm^m``WRe+&4446z^PdIQ{Krln%Yj=pb@Bl&JNhqv6CjT z44VJhhwz`&)enasDzF6=+g$ApWuDk^vt1^P1Wo-!$bVLyH<$PKXkSB0*ihrekrk8JB`zic*T*QHaWUOe5) z4S*jx6g(-bUro=6D zdGrH%f%p7*aSm0fOHsMPAc$yL>&USit> zkxhZM0|R3}M8LRU$vN^NYcX~TLFp!+tLNR{^y-}woAhiehg_G*>54Vx*9--P6*WD~ z6q@U|?85tv`Z~ULCLZN%@%@Vn;M+8=Tmuy-o>j_DTr;8`92qKEIokL=^d=%G7wu4p z8s0bUTthO%CY(_sTpi*i-IbU>KLXlH;_e27nFWp%uRpHD-D^o&3qE3* zN$}tav^?Wea75_1*9C8KV}e;-dGce0%&Pi@FDKQ9o`*L_Z`~FcC>8bE6ojwgWhcKY zM<$v;qBn`dF7p9HuJ?ohP~{*r8)6)-%}K$$7fR!o?E3|sG*#v}5T{ARbboQ~6mNGQ z(I;rlcTDHvimuPTt?E7E6piY6Zap+I)WBoztu1(FrYbp>K%ZL-*3aNvqv8N#)7Q~t z;uP>iP*cj11A#y>L3v_}Tf$l#zt&{gUkIq);aKRT}AlQ~{aiytid;`6?ZWx=#q%I>0aOl)(7N=jf% zT&rzKySroglm0}!taJE^^+%t|j|E}zj{VEB0(jZMGXR1n+g;?TN_|M9LT2dC7S45E zvgCVCgRM1Q&lQ7I9Klpc-PDus2Ejk90Z*uJt(x^V6-5{8vqq7EBUgbb>bt}J{NAG7nAl=CP`)1L+6upA$Ub5{cC2%!2kk^*B;Wa>G4sRd% zU5V+XC5~KkE2;c1eU5}a`)Fx6(QA(yC{TGj)!7QaP+ie)6;H1M-v6>Rt?qY*bpg2V zm0s{L(+fc3(@v&l(rAE4wZzb{d*$$cgZ88RL}d<1e#ts7j{ll%{fXckC=}(}6-SDa zhk`Q@vt34>YM}b-sEP#7JxeA_IwK5klAa?Y5$rr<=O*gU7Jf)?cM&OY zn4g&KAe9U?xhR>nM-^Su?UCO$H!h?Q9DA7Oio``y!~z{6^HFAT*5l=P_$!vG#jOdE zGSVIm!w9{>$C&ugrOf>eQ3iU+AYywEMIXh$!_HHq8plhuMAaLYcG18KAyqbN>F&NH z1zfB`jWfP3F0$Kqu?m5fR387}xTB!U?>X4uI$0D+iy%mIO_bP2C|49;x=lPnG_2Wm zP#{&6dh)7&&_=KUAdgY6Z#<&)P+v=$g83fuTAE!sn&RZ;wN5GJ8K*uBn=x=x~5YI=G22k-a8+&a~cm!iW16ap~ST1%MRnW&neJGt2mL~PIkfN$(+gjXRgiX6D%L zUp4ul=54i$?R*Dp5KiB7j6V)8ifm*5lHgo>+wuKa^rgoPZFKNGPyqGa?CXsH07}>b zhHO2cVjW8-E3AP-iJtam0RQwlT2O1RJ0km;egX8I8GTFhiX2N<;xpyN?PTy0Plc@< zN44SmFlV0i6MVeWVdLTS(!D!ajHc(Y1t+0&TXhwVy9iEnOTDgIJ&i5YFP`k_eqjhM zILf_)(Xr=U64Jzl^FZxdJ#%!eD%(;Sn<%O|#x25g#Kg#c|6UA(_bOht7N4+GIDHY7 zDf>j$Vpw6YkjGni&U2g>*SFS6|01KfTT<2A5Vdpslw1Z;qaipW#TP##UtOyzGcP@+ zqd(4?3+k&yl=$!9LzD)BQ3BVrQiChoyZ~sDoC8xFb zcN(+ULlP2!*&WZ~t-R(}Wxqttz3(D(TCma5Qqd&C{ zbdn{fe#?OkT(jX8yomBdD=FAigU znF$(`y@vx3sSbT&#IsP~1M8^?CMD|6`qEaStR_LJHUYUBU^walK)LM8YC@C=oy29D zxU0n;={yeP+tc#1uL9^BdLMX{xV9Xwkvu#%Z0rWundz&(Gl$xIAhwILyFwcc)}Y_N zai}+$Y8-T~EeqA!lS;O_T1Eu+Ncm0%+8izMmD63%VtM?ELCN9%5O&qo;$D8cx0D1v z!ExZNETMQ(tH3AjRJ z{ly3@UQ1K3Y9%4IC-n||`{F@WL66JcUp@#J4(%T)_YaonoXFhfJIQ^>`D?ET#309k z$LZc6dbHn>+LY&>3782qy$n2%l;ZH%v#-3@y@{4z@Fo)ZX$JS!PiLJd?haXwx5Ob06P5xr^K8GzxpnqQ@Z^YRGAJh$Xa56^3Pb=bZV2BCF{jr?9ge>vB4XHozkL<(dGtR%~rDJPjA8^Hf7!9;n+ zA8xo-=S1Z|^ZxnFLPPq>#k|G0wX#yggW@-P=0pvGX-w_A^WRIR(&&IiNDGfp4}6rz zceJVMuQH7duOu10mRq>MgWySc;vU6jx3D%`Zsu5LU_=nDRHJ<adas&;Ig|0MJmSye<4yCeMC_#dwAI>fXvzF^M%{q8s$2i>^R;QUg-@#4Sb{s zEzQPo(6&)edK6S(fk*LJWLl>$JTFp$N)@m%o~qTvL6-&s35aP|!b&ihl!y^yvq?+U zOjt@%^%&xpb$%KrJaoPUzrq_9H@h05=q2xmO%RzmwlwYI^;WVtImif9?$-v^Qg4G! z#JarM!}+Oc-Rc4?Fq?&7_==uS)btV#aB|y(+}-w7NC43P&VAtK+V`g%)!yqnNqOta zSOG<3k;^nPnvuy>$1kh*jPGRD^l{?-JIU+N@BT~B&rr)1n63P$Q7o8t?Od*mq#fd~ zs;I8$O$}MT7w7Xv+^|Z3MZ~4ZvCoPAcMpnUs}a&8T2t~uOG3jJlzdRCy6ULR7@U2k zy2^W)$!wvGSg|@)!9Qps=2gwI+_(c&(}I^vG4_@N%k;RTWZk)hC>3kRl~p5@Iy6Jm zPHhU`clvt(*#`iGR*dU)7yuAClgrVJX=&X=i&&7(G3emP6a$?enUW1&hi{~>w;UP% zm{V4C#h}2d)k1q!?LFU!6BHnH_f~fZBKFj4z8~olL7Z8j4J33!Hw#;9(THL5tRIAsVD}=fZCPi8% zM!KPhGl1qyrk15z`pWq8SP<}rc!1qvMuTYaDj0UWg_y9NC6pDW5y(scYvf|QA zpB*9CwpnhW_W@FPc{@9C?YN?}xSsS`$+3xOa;4td;))S&3=C!+ldbrqpr|_3#2GrJ z_Tjs{O5RlvTL|<-P%0f+&&?S?6XdQX@!%g~l%)Kt%jfMp<$#h(q}OT_U`e3MeY2ECw9WfxrPxW zR%uFn2200g{hX?7rO3H!^Ll0YvhB#SDdt*DCh_IegzzTGl5z~ogw){`t!%e*i%>JF zn9Ry4qCO}*&VIq*BcD%Su#+9L2eDoKN&4^d1)NYegEIn4J_xcIeNXsJ6`)UE5O(8% zYWH)1(Zk@6K=oInDN`t#YQ@LM8VG^j$>*?#w5p_+anb}%L)i?qzD^D!@+!=9E#rN! zv61+|UU4td_8yc6&HR|Glpb(#de$P@%de(OLi5Y7~naXLNEg< zS}geuFHDHZEf8E)#{#NtBzG<@FUfI7QeI(?&Z0v|LI36Y?m8ti*J8rf|7s;Ta2^x^Dt+iM()%nv&woCT*xp|K@%w{iF z;e`}I^J`yk=`}%8J7aGG&#viHSG{LsWgi=InJITXll1YK+Obr}DT^^!8e76N9OmTl zShZ$f%$J%cIoVZpx=R!n9O$)A6PvbAb0D^(S7oZ^7)zw8-OmvbzC6whwF?0sziY0^ zQwF!j7W>$A0@RfngLDkk6H(7TB4_Tdc=@b#i7*zpV*qHB<@PXAbgY90)h{KW7;o98 zl@}4P_4Das@!{6w*#YiUmd=W*IMFFn$O(9Ux1Q?)S3&;G!sFP|H#>}l!mCg#X<&lY z!|5_jzz|OYDgMxDn@TP9pn&PpH;ANe`DMB&J;ZJ&qdxV->#LwK+xae9Hk|c(g$4#* z1e!1nMpMa)$t!&gy^!cI@9?#;OYiCHii3Y5&n((Sf9wzCj_P6nA${g5% zXaAsRV7Sr=WbRItB=_v5aHWON_WCagZr`oU6E1p))S_yixaH6lD>VN6>e8joOt`l% z@x{a^H+r5dueW7=k3oHM-=Q#t5orJ$p0QfZ4GrOSoZR&wta{`jLBxV3Ay7hhvEL-M z4xp5KjVsu-;A)?(Pnxi=1^fV&6B)9e?EO+*9yfB=3R>MA7rUn>UVvr2VjzE{XSX~V zvYwhsVF563H;u;(BX)W;3<~e7B~)Jt@{d8Sg^|fDQA+Xy@Bto`c&PYc$@`g2q+luiE@;u?i5%r2r&Ob9B!&6=d-r67uz&qz@vUqnJ$WRIpyj?+g9!d z z3rHxtDEop524Z!?BiagXxd-?dIoAhb(R%W)E^Z-5UllMg`q42H!0AYajL0V-mfw8a z?&h1dLw^RFiQqXbv|yJhVr=6TcH`LcpW%#AEC{!DF|s6Qm8Q-K@IxKKiee)PN1@C; z)C&t+OO)CAqVlC`SnJD)xN?$2SEW;?4Hhh5iJXsojc%=a8-D{aam2MW8Cy0R-x6+CpH^qrfv;Fz)rCZgc8t^#@KooSMm`qx!15o&_3-F~5<6(h@8E^=BF6^p=rR@p80N^{*rqy8-+ zpR(l8yPwCtTqCPb$!a?1U*IB;W}1;X@Ep7iy6smoM z%C=HoI}8ENewKgh(s1tpsl1nUv?lyitfm^atn~{R4TGvHE+T9qQ4OW2i=FVEvbV!m z0*H=Wy=xhjXM6dH!402W}X9A7fnJ$%G_B^+b%HH z$IEz?Pm8+KvrVuEf_a4ZLCejIHeep%i#`Z+Ip7o|3qS-oIVAwQ_kXBad>c0Ybr=8a zM700}I(&hvow92mj~~eP3-o?$slow|VQ8d7y$NowIsaW>3Gn^({&*XkDSnvWzugyW z0mhP*V@1orx<-sdX&TjCFRwt4Ypy3ESY2(}+~=Oiq*nEXxwg6JIkE*`0By1~qX~PU z5UvUe;r{3%`<1-UUr_Xog#NM|Wt0Wi4=m5ruo3LE!7%LMPAwM$+6M(L2F>$T3PDTF zV+rBblG8n9@%{tl#d2o02->Tf`kh6Gh24nj$F?jWVr4q4`fcxT7`3m8_#DF;VF>f^HtjEQ9x6Hb+au_CsY93|D!vRWgF66H zb9Oy`U9o=4EF7H5xF~JbYp2$~Qak;x1`rHMfec!FV7A{QQt$w2y!6vai3rxL38x25 zGa*C5Y*M{_A>}#bGu_nZfvFE}Q6xTDV?N6viCeB6-*YKwJu~U>6N>^>EWjM}WB=Sr zEztSZSt7TYjy#B1KVw0oVY~eqEsuSXwVYbL(Vr!a$JBf$;nOHARm<56*^C&i8mbVr zqOR0rEfDIuDV3Zx5`Q_)T>*?urla#sC&-1P=L!=wKmG*?GN_Cg3OCWg{yI zK4&{@40vE(YpLtwgqt6$sBg*~x@+XAcyWQ98)h_H2(|3vYr$0h<%PMi)}T>|-Ifv@ zc0H$6+d-4pcx9lA0I-o=IQ!L8ccDb!6OahxTZi$qpTIb3wPs=n8u-qXB{7Q6e9~DE z2;EHoI7jGBU-4X7PK@*y2Eh9CcowOuANi*+mk1*b6eH1ed{)_|C<#Hg#!s*PhT2+H ztTizsJ7Wjf8_cBKDy!J_Lwo{7Das%=+v|K@HDl1dZ7qRi9q`roBVYTI?oUF6#oE!T zB_eY6F(*1y$-)P{`4I>QtO=~%Pe}Smf_q=tY`PA+>I@=p4vYnMsGYsQs;N zUx>|o;iF@;)H+n9(lxGQ2W`K*dQXOe+WEqkctBwQL%=U4GOXuc_ih*(xT4Dj>fnQG zW%PBE={)tA=E_F&g#y0s8ay`q^&0n=q$(U=NK*pmpJTO^5!vxW+o+yXbg_up?VDGk zVsz0yd8Y65e%F$n^g|UW<3=Kq^XDrw%#Z3c25@yvtPyiJ?Yv%#!As zS$lWqd?!?{N|7VW&Bms~kuy1Fh7~zC7GtuR{hm{OKHtyhQ$D`m-+yY`>-l&+?)y1- zVsKSLAK9s^M9|?v-0ydF@JZAX2vr8m8-fJ^98R3x!37iKNq+4JIy8kadM?2Cht%rH z*8Xe6{qv|s>d)I1-=+2)Y?P&14g=YgyLV&r&0~yDL3znUmVf&2qbG)@j?N0Q`bXN% zrvzuNRM0)O-^BT>-|X_L2wv2xA|T9xs$7`eyuUpI2F`NAqMP-;X`IVfOZSR+3p>)c zF@COb^akThv752zevJn|3TQpo^FSZ_PvgoT;n@7AC!t@Gj0DG%?I-?94C?Tnnea%l zdUePCT%xh*Z+YpuRy9Jm#P{fbs-5{D|3;lz7_mJf07q;SdT#-^hJp3cFaHpRZ`nU@ zi)1C%K0J0{uzkfdP$qQr^jkN`_)kqWaZs3szBIbb$X_;y;hqx@5|N$d zF1CxgUk1!l3+M6cLj7WinO=t6{RcjMgsV7gt~n(um@KHCh{90 z%D|Jo_rh7Xb8tY-qdzk;VgSti2^k!b$9lTE8 zxdU+Su3>xqJ^#3ad;w8>v`kbnXv(*Am{m-)(?9=O8kp9Q{AKvYXS&Cr6aes^#(?Cz zFAh?HH%yUZW^to8n|ub=wdH0Y>umMNtLGXh^R5y@s}8i+z>TD+GnBQ@kryr_%b-Sh zFNo{coOw>!(BtZ}3Ut#6Ck!6`lv9Qu`c=nF*YN$6F3B2r^`5viH$GT1giHN^l3Co2 z6#q4+OM?o#_>-84`n$4Sdq6!+rS!{FcIU8XVo|>6nKd=GUN(=JqK#?-v*jC54Qs1x z`#|qqVo9!J6@O5oC^bi3KTc?043^H@Sqcq3CWo_}A7PdKX__Vt*=UV7QvUy4lB428 zJfxU3Z6%y$p7u7lbL{h7Nym>NDWNCj6O*hvcEA4s7(L2mr^E6MZTBVMO@>pa;9oki z*}6S~JOB4ct^0CgMs~(M;v9eZ3~f(TkhXfB;Nk)A`cn^nY)AszI-eajDD&O;(-`I7 z(nPL*Jo2A8x($)0Qui6dORavW4rkXNWRdqr!689MttF=;aPBV{By>` zf#asoZ`1lor1>yR4_%uCbt#+=!(4wBhQ%LP3|@q6=^?z_s|p5hNZJnipz}Y|1cYw| zd{6_h0?q;ESz^L}5@oRL*`~!9C7~M}E$ndLMK_M4KL%D@?;g=a?Ff`;Qd%kp5jw?d zZvV_LSM^&!3;&A|`5?qh1hML&o^TS+{avvD1s0u?HSB;xl>#utJ9a?(Kc%2C$n1dB zA{buy6=*+z+nR(^A)Cy{7z|+=d;jY7pTR{MdUm@&fe5EHueFtbnp^natj`1iVu@Gw1~6+QoFQ@uy9Bl<+!E$3YOb!l z`kyQ$uND3KV$m`nY#{V8D}C(0_IdUTza}S;xrwy<)S4u`TIk41{j?78 zzu1BcIMt(Te|wibWSvdn*fZcV>v@Sg7AJx8i)B>OK@RI8AoOabh@AfP?;#YLkR8@Cu+-_;-z4&hY)X z*0$WazVAD|LCS96-z@Jh|M^r{{oWW5u|FWr7-N!(9{y{#tnmzlf>0LHN2JaJ$ z|Aq8NzZs6cMgj!KY`i;25W9Rrn4-GGE3J>v-2wc{?|bRn&?!CT;7Z^{8$+o-V~r~# z6?#Rbi6}ox4wsN7aTbG#d8L7c6;BZbiG(zIYSo!b;f7jYlj~-#`VDQB0`^~F?K?|n zLHJPn`;MrYJwKdZUs+YdEAG%oWb8p34(Y*5L-nn%5 zELz+CZqD?6)11QReN*>CCH2x3!_dfvt`a}Z9+c3I1mHoGl?<}P1_O;!FG2k}K1OBt zKk0hffznDOzZf%{5_eUmo?W~Ku>cyR(?5L5KW$!v%m_7z<8Kt7i2U3+I*A(=|q8M%&LbL^p_VFPAj z8vN?iywm{i!PUxG5dKk|N~JlBi7f@w>VXG_dwT~=BW?mIc|{mAE$3B({^Rbm1OM8> z@oR1T^eEV@&pCM8^8J}|a||ZR(@`naYc9xlzk#CvYJ>B-DOmj+g@Cz|nQ%bcBGGT5 zNRRv2>ZM-7NOt(Ie>;872p!OiIBH9|ZCFzl#6ExCZB_lrdQE>~!5^aQ{vaM6KTvq5 zL*UG7AL*Cp8b}4d2(5hz`8^|Q!&;F$rH}Ge7#jRGOm1srenD=XQg)AU9oU`;TeM+-_FQZX&4>rCQe&u zbhat5!QRX1@zRBm`U4I3Gb&`_RTaonx}s~MOm443Rz-3@>YzqWCH4fU6q&`kxix3x z(tItSH&|S3d&uP;MEcB_UTNAKYRV1e=H}r8p<)95 zH2QCJ1t8$Ii;KCcc@)1uSp&j?<>54ED7p*{KQD5A*YR~%e25+Yc&I9biU>FajPt_G zGXKyO;Gsd&FUqrk#M|W#7s%EmH#$Aog|insCHjb_nv;QrVQgUbrN;g=h33e`(BmD1 z27JPoxkG1ck~`bl3NzGtvI4vJh31xfLEHeX+;+2fhPl7ZAL}aT)$7N(t`hE{xMi_3!&=4=pzdn;UvyQN3}E8@XfaVpU0^3UDH{t%Ia zz?t2)NnEPqwz17l!e|3+67Sja%n>XlioCEGv480bSYkViR44>CY<_jUt(U zH6wPsPea-ysP==~WcE?^Ifd2_#ndW>4n5YYYre4vVqU3uDmsBZdG{6RX(vYx(JVIWcnBCxDd}lvxJwDZHw`@&g-2vrA@Y#JJDICXuc0v)5?($V_ zzTvx~4I(k>D|3X#V@F zlIO>-jOHJ~EQSTe!eP*UcU6O#&%Txetr^zU)|S)hB*D8kQ!HGc#!LhMZv7%VGRcg&J!X)*h z`>|K4?n&GQjn)sO%jH)j5d-q;;??l!r&ro(!_qA)wN4Z%zWib&?XR*Huf`ZTc{Poj zr@?kh;50?`hfo2pv%3ZJfCNOn2(K6tx%0Ru(;Nvk$L^GJD1EpBUmO7Qjud6y$WR9Z+jOECN zUgV}DW1Q38&e|$8iQ||vqc7G;%M}`HFuUCHEHI3btdTqY;4vxNR5HP$~s#;Ap{XLCD>5in0Xinfs@ zb!G>quFCqzl?r{)Ui;0_22K3J-H_CEw~qUzh!+hzw51_}Jng*YDZb`@WFptgt~Z9| z$N8eJr>lEsx9Or#lrK}_&kyzo{UB3>lk+J0*qV_-TB=jqCnr^Dm{HcTgRa%cuFmAZ zXrF0=O|QDN(5MUw;Y$ZGEfUZ0GK;HmJ&p%iX7Jy<^CR zeix&k9WRnxgiT(8D;0mL&UxWb++KI=kGEo5)BZ|h$N?WvZew5HNc0X*Ws& zM@`Vn;;qdf0AXprIh z;3l(g-s+@~2`KOWp)%TwPA5x(TVW1ibko%RCER!m$iqKBx*ujN0Z%{fF9EGG0&B^a zQKe7S7w=-&wX04IpK&U)nj_Q!!6b%1-is@%7}X1)#4k^0VZ%a55o`~Mw``y;9`fAV zMRx2xzTV%5_(%)L@a0`No)pZ=*-)AXUWLiBc-B}1<@sWqxFxt;@vc&|HhLr@ zy}jkw(zjog2dI}7MkgMR?hG@AJ67j<1x=b&XOd>LYz7l1Uu?8KuoS{xdu#BzF-L!z zk|`E`1`4Ue?4SxAiA=(|AcBu|oTSen?Z97K1A&iD>P;UK| zFHsMXp7BVB0EN-A?L?O4q5^4v4`NnXeN1UFJbmWrl5^LjXpYqzg&a~BCF7=Xeohzb z)TZ3Iug#R0*_X!8s;I z1$*>~g}r@d*!UBejeTo;VH829$U_EE7x_)=<;`n7!FU;PC0U9N8*t4Mhb{!gh-*ch z`0lcSShQzmA_S^8^29JwR@JelanmO6JXg%3Y#;N~$MzQv)E2v(Y*x@F-TLc!@ZV>E z`GvKn!*@p!cB(mP$5!h!=P*z2KW|UV%-dY`e51q6@AE*h{spK%{{^VZynTb`g1S+Y zR^;K3FTIr(aoK8%vIt2M`ZKN8_GQ^$bS|G3vrV6PZWgo0srp)7Et~%Y7XCN6F~x4` zwwA?IbIzE~TRBu;mP0ad>qDmXg$&N_hVR*`?uirIJ)PwGO}6qTzOHf$P0oPjB649Q zivk0Eza4tmxscGEi;7u9j_kS!+({)sO~Xi zH^$4-`tj`1#w5M?Bm;dW(VM?0*WVlVu_{7d=ubwn{5I*RTzg?1)cUN-SFu{|QHLsB ztMhGchjwjWi3cX!8U^whnJJebC`d7@3udR3XlO$*?-mj5eE?YeRfDD*#80Z`<~?3^ zx`lwCHE+gYDrW4xd(=qV^{@)?r+t;n6>Cf;Se#;XSXY8!*m7E1tm1EEg&|8- zozh*-U6WzQsO(Ymiszn+)o!cgk8n*B2tfK6Rko&ADrQCI!6BCyZUG+rt>{a1ac$te z;4}B^X@>%A-sB?|m z3vc^pkOGrxKcr{t(^*bgXn})`B0}p$ly7FFMW5Z2wdIwHOQOA{o1@FSSXW|YI*7(1 z_js01Gq7}SUg(Sqs$RRZQX@84*uPH2Ouui=Phqb0>!lMHUxKcKrE~lcYzDO;2$mZ_rzHd6Q~( zpq{@y#@^oC^SQBq*Vd}NdPN@A?P|4Jqe^``Yj#TL`}T7QLw~O@t=vtKk(~$Zt@ejW zadVJ7e3y{@*dv_4%|Qt)K@^r}$&lcycCW&>CPkX|$ra!Vt#%9i8}GCW{K ziN3bbeXX`V;<5%Z^YKy&U~!g%b30x&I%z-=bEo=w*hD5{MV7W(iovAa$!$uDkv80@ zL|2LLR&?;Xsz~*-1P5ItA<};aKCTZ-w~9CRpCe5N(*@*&cDhmLyR5WvE6(wQkIF&1 z5S^7#818=4nA7Ut7$RP84pllimS6CVem1d`>v?QUuYRNlqLP%M>YUOT)tKA>=ELbX z4=x6%rH~gQIgaHIVdT(qt&pVOYL{n5ncTcy?4Is_BEGRNh9&Pde7e0Trz}IYE#0cE zWZE-95YRH$5b*xoyZN*LKhPg|)$?hAZ6Xu6&xXLOCg)gC2`FOn0i;@R)Lzk?JCGNG@x+;r1ZYP}e z(>N#AV_I@mqG9c-T#)W+G3snpV2n9atQ2rs06fIKH0q9I1>j3{8RhR#m&b+3m9m~Z ztQvA~ZbJ?o_xCL;K}xu$7Xy7TP=JpOU5pUc$&~m_`}m`#Y?*6oEK|g!ZXD7`xjVu} z^p*vx$nS|=R~^PXBq)i5+squF(y;8#^;nInW!8o8a9x@FT?3eE0lR8yy~) zU1+ZSdr{G<_`Hgz;GLZXF}tl@)DK>rsWQ2l+tN{~(HH7AZH2Pwx8Oe^HOL87nT6j@ zMrt^C?onv65`;>U6cJ;>ppfbW^QR4RKd@y(nVHa0Q-qIZ=#OO9f?UtL#+<*X!$T&E zafzWK^T9LOcdJ?T*QYOCM1D)CJ&7rZfZN!U^pn!9sO4jAvzNO7Ip+*dtgj{zR1r@Q zZMhAYOzv)w;GjarXF-C4GhHzv9)9GJ+`%#3T%lx6W|(2_@N08uanJUf(sXW^I96-a zHYqBeU!LW{d2n?ZvT^m=MUb-MAP6sCBI$(KZURP+m@9E^(5QOzD;ubuU{_o%)k>r5ZDM>qG`$`rF>JOVUj}jYihb}^<_-( zl$CCk2{|hZ#3=s^JAWm_ymf!h{4Gq41lWCI5VPRAFX$h6{sm{k2-H^+K>Ak`1+2{vPPFaJub>J!ep9S3Y7xbqMXZ^GxS8lLZ$zQ+O<*d*DD5#)s1wKw^_Pf-mDy?t%$5L7pnQ zB_UJ;&6jt=I%CG4r{c?}UR1Zy`Hca2k|7_L zF&M>U{wOufdE#y#AuonJ#~?S71m!F@Sv9Bnxi3+3Lx*C#C^_zeX~7gWr_Oo&j-4x+ z>qYi|(x4?VH>@Pk;LQ#ikrlbGudkpgJ>@&{&%VxVDke?6U~W~K`$FIzRtrACNf3-1 zDQfctf|->3rch2kxZh83)u=`>c<6B-VfH9@&jva>dD<0RKR#?dDbEFYf1~l+%stLC zwOl8p!)Tk?M$QPfOu(l`VQ2f8vomZecCK%-+c{v$=;xfPE`t*r*}pfvBPS5nPVPt> zA(#c()6UsHGz0CUvUTSgZTj8%4yB|KF4$@N7kKEd);Q8)&?+n#uma}g>02nX3%B_- z2~l;y)7pl9$|+mTDZSxO?3P@nwQVvtK0?~fS0z%m-&du55Kk+3SFDzd>?G?XdqG49 zpgI_!op3Tr8Q;TWyj9dSYN+7bkz(8g-0}$lOFmf1X?)h*4UZlFT_(KPr>C}u-81b< z!NVK`D{{2?y=v$2=x)Zb+t$Yqh{pA?nS=Vuyly(byTnmR&6$+=?BdXiHfOJRL-Irx z`C<7)EZ#9iRxqe!_o`EEcH8NMzdi_PEWVTfK>dTh^TYURo)Qb~%xSf@dQlfT93OfB zJ<0%M87W3Hy|SeHm;uccC)x73DSXoh-HDX7H5RYBCTFINrv0c*g4w3QEXts>(+ie$ z>D1`&(F$xmef|15W;ct3^r43`Y1EsBldn+r5?yH*10=S{8Vy^l#}-TY8*w_|j7<91 z270|BX0o~OJRRJoYoy}>)mydckLi6Zqq!I(7~f(~+pD3z+unh91wBt6J8|>k-H`U8 zeQbJCNoAi=h(52z)G!g|fFZXG8u8naZfYT)R-^_i{AG3a>*S0iG6c@aH{#`2qkQj% z;`!rI81Jd&(j>*h$`GoFK$j3a<;fwDxmi<6TmHm4G|=dC^3*UzO!GLOt-LES`QXSf z`$pR=z2wZzU-+Nq4$!}XFm}yp*n->eaivoR~@!K>wCPeXbo) z*O$FpcF;*o;C0qk*&KnYfu0x2eM*qN3Gh1$@wO%|A0P?8h0uUzrlhNK-)bNZ&gqkP zxq*0=UZ@}8jEX98CK+NCr)J>R7!smoCK|rK+spv>3~*8tPhNcr^pzw{rZJJTA0IHQ zt&H+x8-h8@nrfYjB;ab}4IM%6*M8)GxoLRWadIfkIkDV(-6hQDzfIfStn|kZagbT+ zo)VL3#oN=1)zT|2*hBSDUyHH4VkS#4#%jm3NJbj5HH~7*)%p1nuX_k6Jzmc=DFMSG z(i=j*zER4dJuQ|e_Z4IH(2cu4!e(bbE;p)~J3o~+Fo{l@RAV@if@S=I<=M+5vj=jy zdnS_!v#nf<*B)h)N7=ljHV0x$LK-`zEg3p{8T0W zPTmJu@_iVgfO|NfyDin(zC2e~P`$DU+@*FiwWaJsLVXJHp*2^3^2-})Wo-ZZlv#t8 zfDv?7abUW!fI$a|Y${MjonTj*N#`3ie0}fqJA6=sSDteyJAswZ7{SoCMUv*QQCFMr zL8D&zvPRrq=8Rh4T<6s7HivXU(A4_TNw1HhYJq=n_-*%xtwT)wcQYm!I}5Goyh*Q& z5lXW*c|3xN6mXE%EjI7@F8ny=Bx6W4YkxY~aw4P6p@mEcmgg5^*HmBw`;$Tyo?R^a z6)T!RA%Q0KMIPB9n7J}$PX;N|!oEZ|qPbAtC^WG;S0_CaWWABFIYkuwGtDLmC?ZeC zevv!CWj_4(jAOOmAt>4q?YUiKhno7K=;8)B3&aR0tUUo?hUUcJOvCNtM@cH3@l!|L z-137L}AvcjA-vatcgPJ0tRnMfKGgueN*+!kq1Dv)??D|*zLj>NDNsMWT}b&=X`@e%R0Cx>PbNUVd0 zoF4b7l($5P#;<*aZT0;8PbxFr=5N9g9#Q??2RxKVxAS2+rltXKOT@m z$@#0D{MC$nimT>k9jz-js$;d<{cD$6b*5a|M1RXrgRMlo!&0ZCz-*z`i%>jmuT zTWp>65t+5tL;^u;3PF1c-X<-mb;`!{Q5Xm#WnXv{MhxCE<`i%myv6I!K$RX25#JZV zTMHT1@hodpva_{k3{n<_>IeRQ%8*iZ(dZKlHP<^l&!4Cw-uQaSpf9A6_HJ&tC$z(Y5Ix$P%Pwt>kR`ol7l|(w02XWQWbY2fdZkq+WJTExR zKRAmm^cch&=cYHvWAHFy9L9I*w;}Azh@F*4e&1*fK6J{<7O9{r z$okUTV>CA|Lsg#p)JKi7LmEcXWuGefI8Qz?&3FEKJ*7#}seIk+GAvs=b%ks7tdanH zO_ucKdK>LN8Ko~c6+d@zj(?rRTe! zKjy-L%zSyfBMhNwX`LV}YJ>!}1!eCU>67apmUHtBC*)9?)v)0jz4!51daO>8j|%96 zT&WC=Hc0H3GhI`o*q37tw;UD}<8vVIDLIY}jhm9+sb;@!Sq@~9ZWT=QlW=t|y|i7g zP~z{j@y=^6>&D`%2%)1rr-vijC9`{Obi4ZmjP*1m6E1t~@62z9Cm!%iWBRA=HN(Pd zW83DoQT${Z-3ObJocSy2n=Q4`0v_F&yM9zlbFQ2&;4}ZWIc{h;yox%W_9Xy#iAvab zTW8u-0JF0&E6Ygd;=Ls2(#w3GE_=RNwy@7(&iM7{l>CRLB)vN{e?vuq?wRXvTa-5H z=n}BmwdgT(mzo^1h{4TO3 zo8n*CY#52+-$8Ub$GPdCKdB-s@<#iz4F1m|E0+#*D6m1^AnhG!i;py?WD z+wo1(HLA5GJB(fM5EBtBEwZ^aCzF(^|Eiw>GCGGlLW1VXJQIhUy&Y8WnNj|g7TcG;r^C5q=RQoc1}iV`Kt6}olXYX`}EKm#%xe%J*a8sI+GAE z7zvwe1tXuMrTN|&t;@M7m+I~4CL*R{22I`$q1JTm!J58!SQxX@qyuNitMe1I zky9IIXPU51aXmK_Q!=$(P5EmhqYOBwg;CT+gEpYT@$PEanYwg!#4i%exV>VB#E`ch z)56ywdQYS1z#Qrq;t8s8gVm7)og=VuLWX{u-IPa5u^9-N1H2!a!JGl7ONrqE<3Qip zEzozCDkZo3J zXR;2qlr-Z#5--FXafV}9!d{Y52{BhBg)$@zoz%-J+zr_enCui@SMbMN;H1yxM!7Ye zQki)`Y;py=`dvAxZM4@dtiP?2bYq_t!^uoHwXC7X_kWQaN?d#Eeb2jxBX_khClk{A zPbgKFz{hoKTQu5Y+u~ynlSwFFvRwMeY3)#(l)Q<@RlDRtQSEn-qy0TL=w2loxG9P} zk98vFmDLYPm5j<+jE;;D+n;eT+^4H@7J)^gaN|i&4xzG$enW z-0rWm^NAL;)+u}@5k2kG-(xd%RAGc$c?HE!wa_g>0E?^-w0Q6R9C4`&AUf#o@w~HF z4OX6;%y~1|Wa-NtFbCPXlQ?8ZpF9;FWl)rZW~-48=;`U$BUzCnvgJZ?0}oYd zYE63{1*euVMsBQzr}HS?cnb`u7bIS=3)u9{;5}a>Rg-h~L1>V^9WZp{cw1waB8Z7- zAT6?`)hC9VNy;e8R7H4G2zeYgHw)*7hM0#{T}Sw*21+9ezf$;Y5yo6fd zSE;QxD-P}S`TF-wh1@3nI&Q(b#XpJH+HV0>G38gC!6M1t)B#K(N@GW*cit1ehWgAyTb2@i<{~peW>R z3c%6Jfo}lKJt}%<6&HX?L`&w@ODD>SljI^yLZ?leY0#!HzwF&nKyRTylFT&WvbUx! z3DY)nyzf*59td{7_*=ijP;?L)St+t(yZR|0K{^(rMK&o)+Sp`zcIL!|mQzZjA9BrT zA>`Q^Dfs7VkV7*L0Ma*h-g*R-B%gKvZue70`i{A2kuCR&Z=1#gp0I5jtYZ((1lh%! zCLk!q^mz8RTH_fP3O^^T8z(=B68fx|NpOJxOy*Oe>;gH$^2GMXlPkC+u65 zy(QO+7eZGZBoeIR4|tfljDT>(n*NGAv%;0ad;W8)_KneBuE2?PB6vn-XNA=pXAP6Eju|j*VLSy;Va|ykujpf-YV`uCrt;H4? znij0{SrKs7_qPFO9f-9qE|4VVO4*gM)!1b%#N@_#T4Y-q%sRnhgvLR5%F6%G9O+WX z8!^Z7V1&W@Qa{r-D=(~Fh#-J_&)***lj0x!Q! zm$J2{TW%Pi_;`B{g>pE`F?xJwr#W9YSxt!Tqip4uOK_XtRKTh{TZ%FoiWn`Nbo9P% zWx@X84_b6QGo&WBw#p7HvlN!D%A?LK3=Z=o&^RVt`E{o=5D6R$L=h~-@!{Ct#f<;? ztVc(Ir*rfIVQM@?x+Zx{@!IQ7#n^=+jHW6vSz~R5Wjd71x#pY^EHc4}MN>1n0rn^R zD_@+dEu3m-Oq!-pnUVX5_S@g!c4O z?FeaxJOLI?yX=jor}?(ZbQ}VDL8jbyI>J5Nm{T(SjtOS>08-YE{ccMuLUgWbPHT{R z)&2}Ixl0jvX*y{j&3Ho^GzM1rW~Yj|Mxos6!+v3tg~DqbfD>b)STik(Fp$oqok|(g zJ(W-duRKQU((cq!xVb57*a_U00Y)V#!{$WQWKB?F>oLt&ez_C(`06WIl^3nZf(R?x z75YbK7qKIKEO=;qWb7G`t{6ZSWs8ARz!au;7p(s^ci^^*9omU}UdY*iw@85%9bFHo zaURxrB!$rTzOSv@OaZ406p?O{2c^;v5TuiLfg`eBS>1I>3F9<^R_sAZv*_&78tp9D z#d|V2YNusO%foc#k9HMoRX+@VxNGbT?*JGgDE@GF0W?%-iEME#jsUe{Cb~(B-&t@J zj52`gN)fz&Z)aozh?)&QpSGDkZV7}ouwBfRrwUx81Gvp5<*;f`S}1e0R;zq8v5vbN z2(l5xF2~V<{&jXHm2X}FE8cx^xOQsltEBTwvcq*Pou6Cec}0pdkT?{LZ(7^QQ9u(< zqiI1$c!a)acw4urunn&_2wW#X1Tz!cL~pE=3{c2I@E+5iMR@9+rxe*Z|D`&ewb2AL z*3p0&>Pa4zJF>n;@r~;JWec76Nn}ew@w;2Fs*WSHB6tx(c0G({O3njk&O7T%Awjsn z0WsG>8_;R5z5!7DHLTi;MnVup$S;au)jquKL%-9D;GSTgzc#?NkJFijJMvv<5b1jP z2}4*l^FydSf*AfFkx+!pQwT0g<7IrSveip}ud;MH(1sqcx@i1cz^dh-J>op)PfYJdGb1XiWWmgtZ=po3{h?R!E#DG^F zl_xHQS%CDSy;5gMr0L;KvKbFiXyG;byT3yMBAm+U>6HGbs}KO(mw8uV98CGF2~hA$ z7$rs~k)$x(wd6ld!cS~!0TJXIknEL2dLH_<5DAIw?Erm^23Hm}$-%gnJrde;M>&XW zLgV9x`<+s9$AA{u`EEQyrb!XU93PF?0OHCME<;}~ri>FfRDT|n8Jsd?7tmF8A2n?a z$XN)KdSSyR&slY?o3Mi4etk0TnZzMuWX;sHLcSLnQEasX#%lhoGZ|&i-F z;Sf~RU=(P$;?$dqOFftBIAUDVbe}lXGN>3-WWgJ(_C%AS&_ziJ{vv6rAn`DzkexzS zsv5zxF4Q;B@yu9cEbVerj^9|Pp*=IxDh3U@t0OX!E21I4FER0q^vVvL1NzU)x&Foy zTr$8epQDg(Mb5zxiqzl==fd-Rg|yTIwCuKL^;w3>(QXpZ3!aEK7U5|pq4%H5^mJb^<>41E)5us@DHYsj8|>Z{WxR1#A~hoY)tHR;?9!S^;lGHpF}|=W%$$yRDKD z@cNj56JXpB=ZOnkE$(V3pRw846&|NpLgc z(|3u-K*qKpp?!f##g~V0V#5fp(S&e%Ix@x3V-&;NUyAVRO5X|#aB;Y=g;4Ff3s0^=1&!}q0Ade*2;YW$DSPS~G(_fnL2X{ty(=Dxj%|t+JN!@zi ztySftIUqI7(yPkhjn<|%UfrzA&_;(VtTXiaZ zVKUH{MgX6DDp7L*T?an$-z@b9*@D#;kVxWx2+kn%@&+<+A4J+ z#{p{WVh54_NUP#&DWK{61aqw<=a#P{;@&3UxAK{Na+=9UA=JJO$$aq56%g9$^^zKM zr>id18NCgm^_YQS#)m+KCPWl0;GBSuDo7$*o)(AkBz^I@;U@<7zum;BvNLsY0M?y$ zw_8v$?zpC=N8ofHWHKGrb{xJEcniH!J!28pjg80{-y09>r24?KVk%X~n)fZA65&eJESZdz$C3lJ#9gDX z`1{RgVCP~2vJN~5)}n<6Kj0Dx_U8= zfbryo>O+UcPzCA(j4>{xKICp1Uz@#=+;F^a^?u84uD@>-ftR&a{oa+8702)P$tx#Y z19H*iHMbbMgh7ZB+a!ZhHdpWd;N!>aEeA)bmw~tXk_O+{ED48quJT)`rST;OCpLxX zvowh`_3)`#u|c=7^!qw4Km9DuRrcfg|>$0hc9zO^4h2 zz#;T111EZfLY+Rh(b))ewu0dMjjApF4=Azo$f{qG20SZQ;*dJXw#f%8nnc+R=GURy z8cc+qP^Z{iO*1GlB2G(0tlrW^>1;2e%TFFYb}2vIPyjnqD*G*Sv<@UJzXzy4{bNX3 z>MZfwac@a%1uo2^W6)%K2-W6bt+>?v@(R-H3L1e`RF%Y+eFYIW zKMf@8b%OiIPapaZo|ZF){&i*9N)%m+whRCVr0v!x4!Im(iZ$arN=)6Tk_`5+ zq;{^40cx89;jPp_@_J~BSPHry^rt0)0*s-@lWV``T_Icht-tk=UesPPI?fm{nj} zi4mq(TK!SU*-BNqF~bXu1f}pgX5dleQT><3Na8BNoC!n z6<71~eGdb}0|tBX8e~7C@CKySi(9`&j!P*-xQdoSo0&3Jp!;_@01GaCZzg(B921h4R4 zM2rlPenexb6k(@xU-)^msdKAKfY0d%T4^5|bGiaLxgdNz?GnE9lSZQl{K7IR=zDt* zx@uCN>VoMvPojC;uhDa*;)2MQO*#+7U+nn3>OjQ1YDbX4&N0@f1A|Sxc^-67tro3P zyrRgP^)IpUIqY&`9%dgLGl+!;j>NA?Dt8R}`ie<}_M{Fxh5nwgUvVjHG}yQ{ZP$-U#Tt#bDRKdoiB&r<^2Ri&{a)I6S6kBaz6;Jwwzz;)tU7sE>hw6vNE#8MO$O@;Q8efx?Ee#CY@gB5N zPzP5_+6wrBN7Z5ku;U{6HiQcA+3p8|`t^|4)clSkVNmsBKu4VLsdI@rhOD-H%)Dxy zWwaUHm=dk9vfX$=*0{X>R>j%DNL?xDMV07FONRGS4cTjM4W=4%$~bMF!0Ri$a*nPQD#Z7>{Evx5c_OZ!o$yCDnR;0fAnEBSEBpXVQR$1!cNFpvkcyE7pPV z+Ggx>3UUybnbk3y)ja_0N0H81@6wGF(4+06On|1gguIlt%%~#^!Yuw;EM*l;0r@+#^cAz&C*rpB{cs?Au0^$AEBpcq0;(>K6I*Y2E^o(Cm(LJ8 zpRQU_8Ljk$QvSKUZ+I9h^)WOlRq&Pr0+LxtC`Afig_0G`bTwq=3UV8xD z&jX&-q#du@{1OLF{4G#s>|xJc%;>-<&-VDNOre!yX{a;CajjtEw$ZV77d_@kPmRQ) z=ZuZVt!LxKI=f|hdwF+B;>C22vx@nR|Ci?{|W1ogN7YQ)qnnMPC2m$le1HUzLT=YkG z(4D%+C}wof{dYRKfrJe`QaU*3{xyYUJd`XJ4;mnxm4^E*$Hz5m4aALFxDT{2GB;)w z*J5{=^`#HKDs97N9Yq<|0bkOo%Fto^QpgG^srOD5I~=y)Bmk33+`qmy9qW;3IE>fZ zJ(qDtaIn88-lazAY0E7$MdXlR=zJNP-rP%m^y&3>@dCc$*QlG*B(=G|LP1~>N!*&J z7(Q;)*e`fkH=YSW8|W+D@2ez~33kZ(mBkIvyg~IoD%PM571Xk7EOM zFfvsW246Hs%bnw`dG=K#YVo#@?p0{|)!rrv$CB`Vim3>sCFBm>+NiFJm&|=&3)REO z6!8s8rV(ti8BhgyZLpAr@*;K#u2M|QL+yOl-pikW;awqsoHks*7g-7kTYKvZeTyuz z8Ay%X17rOd(LB>4jWobWxW}T`@^7x+uZSB5g}5kpwfg_!QPS4ls-SDJ&Uy=@bzsbz z+(8w{KNyHK=v0Js#`GA*C|^tg{Dj%0x}au-f+-BKuMOw_ST9UbJOER+pqD63-rHXI z+^x{;Zc_^-2v3--Oexn}P}q!JtZ`!F2;Imz@KX@%gWC2AR~AyT>jBV*z}ajE&gKK= zLE!ev<~7I7*L;Ov{F(SxxQcFhu1yuaKc9wem3Zst1SQ5D1^hGr3zss0?!In$&Be@) z-WSH1v(p_C-}D{sfv_br*>2c`BX#{4*FANaAhyvHBERyqHCo$t~2e$^jP zn}Ubw{pByeP=e(j7}*ZKynW}L9|*D*I_n>N9_(3=!M^>vq6^@7uqd_tyM|H`=0kiP zd|v(I;S2n+6&$44eEz<~_k+GwmiPan`|aQGuyQL%42Gwk_yILq$T#xk@(c6L_lMMo zwZ9W*AKe=6FL9+KLf(F@w7sb;dd$K06_BT=TyXE^qxKIE!ziV=#yxc#ukNP!$FR43KKcHp`d!YUw zd++_%RMssH+W-XtM+H=>W2Y-sS^&F9aUitN6zRQ&9uW~MI4aVlskDR;T4(`65l}!t zAcPhIBAq1EP}07KXN-5=_s-+^2i#xygyfvP_gZ`Pwf0_d>z+T0vazYBFbG&>6O3cq zcRDuS;>AtAJkCexBN?4fQ2Ou=%ynAHWSNQ{vp z=l6&0;()N~e^%K9SR8Upscmc^ssJE*p|^I4_u6K(mg^IQJw51#qShyGY#ra-G_*S~Lke-iM2M=s$f5!=6!$R1#( z1IM}98`ra?>-r*+fEH>0y3cjh069&e(@ohXsu8epktv>^{`E}&$IT#O@DZqL+iRFWIvB*HR_l zQCk4e2lNC<>pN(XGQiAUd-@803Mv~EOTdRN*Y0M!(DIG|p`NA0R{n~LX}1RUn5^y| z{(T|yz#cncn*^`^58?k{`v3n2`P;xmzj?*xu_qebTF6ZxVfQO65eR@>KJIG57SOVvE3Lmf;`)Bka(FI)ci{W^|R{fD@ z0o2C@L))MIeR=<78Fpk~kNt?Z=Dvqt9D%}nO`1%Z_P=9mTjXzB69xI*U{BNqxKo&C zFMwS1PuKg`MwbB=ZH=heJGLDj2zUfF3a%<_i{__8cmp8Qc0FPTAnHnijmtsXI_mv| zoY_Cg%K_vROv=H1pMeKZPibq}wwU115uQ@De*?TwKGH9SiwCkuPmp@9$jH!- z29qTAOSpdk(5l$=Zq@G(M`{9tQTB}d+I6w{0ItJ%*<1X7QKTPo>wkavzbMl79sd_a z`l%%QzbF!5R&aX^hFAz`24=gp9)Mo%q6xy!cc&&5x5*r%HMKy^gbkd zGRBcwd)+hZ1p3ak*Y4M}=2V*nORF#uysl0KRT%#=4!3(-y>kB3=jZ2tx;B6|C(v3C zgSg+c|I-XCqXGEBte-PW{g>VcMt9h12A8iaB=v+n=liV&PfB9~zu1xm=mMq(m@A>z zN`ADE%D4)2K6tM1PE^%@Vzx~?f18k^zN7{)WnB({jSi`={jz38CZ%_XtPPA=+A(n5C zUtoi+Jp)uI%cgqNC;!{4KQIU6w+2$ZQhMyagOMu_d?8S#GsE|b+o5d%(1a}Fb5rZ@ z>_z4Q%QDsv;`lG9`(KESq;55Qv+0%wT8{liSf^hA8~S_wIS3oM|GF#108ONF?0@+K z@T@JKdsWUi_k-tt{KQ5V8wr2?U^D|PTPmEV2gF82&=xANk7gu(6%~5`Ng+G-tP%dB zA<_V_Asv?=xqRoBe+6UTY2ZxM{MU+eW^Bka2moD`3oG&H{`<>+VO9btfDGi`BliQ| zi7iQ7T_)rP8QkGDQtM-@1*1w0lsL(m85ddmFT=*RVo!THweG079%$J?HY z%QmC{Z6pFzylmF|n7|Ed{?>>=mE70ES-@3i6;FiZ))R6Jk|wkD)Bn`CmXZc5t>;iL zW07ka8#yUj;n^wY99&Z-@fINZ?6@0#eYSwYL`>p+9t_S#7_e6Hl;ns-eA`U@;&{vO z%a%CnI5ExP2hURWn=)4C@Sd#N8YbGJ#0gVmKevBm>KeR`wnib4t<@RAH6d%hZhiTB zsSnNeoqk(4(~~nCMO$O+KQh_jckD{6^w;j&C8jHz%$iVg;JC~h(28kYA})flSt%f8 zCJ??M{a@(o`UL=&F78}?JJ`7X2m!ts@ekV$TwOSsthZ9_V&f= zu%OJ9U9Aon;5LilTE8|)`*h9FL9@3uHI6k1BvNL$eL2zCiRaFft5H0qhKQ=g2fQhH z{V9zRJ?l#a7{dM!o+SGKv_1X!_-0E)xpH`3HFi0UL>_N(RJrqUU%B^}ulI3P(6s_l ztP|a>%`CqVxLT@#rg2RmXni45Jz(iR%>Ki}KRwCS!FdN3xp!PT*pG2452aC&whgmN zZq}5MnkN;CQ;qm5aUy|oHq}=xIug&?56_=pJ%dW<;Q{U>^@$U2;>JJ@gG(2b*< zz;7Mlol;K!2Cwq}^g6T006Nj?038p>=Vq18^bqdvIiQZ@5cPsZ0!L&jkyzFwvv|nZ zd0$7i3}|!v%_ggt2TsMd>}Uh2syL5IsQC7t#^-p=$8N`I!aepG#od;zonF?m7ighC znidPJ5$(%4Z$kxZE?7usC{WMy2%h{WyXL=}Vvx!$9a1i18Zigd>(k3DAbsl{sYB%6 zJoBu6hc>gSqfs%X4uKpS?s*)=DQTL1hG|;VaA6JShr;Llopm;oG+w?GOvHpzVi9n- zFQkkDn*pTV zoN0k&>VlZzTaJUnVKnM9m|t)1mjVF&Uk)ru?zptixoo@Yvp^?q^z5vwis$h9@6r=A z>41o?U|o+7R}s5GR8Co6_}_drxFw!2z9sTM zNM-@hJv?5mBI+nW{C8kTLx?G$yg;u?@pTE5;<@&<$q!$WoikMN3GQ#X+8*nfCHT=( zj-SJeU#x#h$jla0K7F)?I%}4h@UFE#M?78bqkG%YS4hlutIDu}H7KcSwZN$}ZLt;- zX^kkQAyD~Rt}D)Fw#0p~I4*#a@9vU)1M&i|2wMvw-eO%>E%695)Z=@Pz#=T1WGrx- zo5+-7`y230#*^6_PV;`RcB1}?558^nh6Sl;MYHwl4_ogl2f!i)cb}PmwFXR_ufn-^ z5o^toAINljO#1k%Gd;sAGYZ_QYjh`^#m)ilmmWlF3TH}A}o zD;tY{aI3`1Lm#W$o>?KT8N5^xy0Y-++(=CdZ*u>O!oqSdq8j)bIOVM0$)n6&qG?-@ z1yV+4MzEn2PCk+8*V5LeKESQ`wcas&A8LhWg^#>}kC=tbJq<5d@wm>m@Bg-6kyN0y zB_VB4nujg>PT$+IOft^36$=?Xgnxo(*0L7rw=^NHuWt~>um%pCg>hi~McWU4WYQ;| zM3(_IgR}g3XYHG_Q=Y*XfKVs*jjy1El#ve8IWCzuh~FZmt#T6 z$;s7iF@V;mZ{qp9(F_{KX7Xi(jIq$t#t_0l`JPQtIMU>;`G122&QtoH_O7?3Z^}_*oSj zJ*V)!EA8U^Y6Y~Rh~GRbMa;%J1(p_Z2(#fVJt4X@KHq}Ud0}P}<*_;W*$oy}6S3Ob zkV#-fh^c#B5^uS=+#QxS7(DiQ&P*#Pe`BwRVn6R8`K|yZD7tUQ0C0OWwP%$~XyIyK zx!Cfy8f2MRd1XzEX{8qS?J8ibJV{qxUtAqhS(~3Rh7CtB>7Q0f_Ct<$Mau*E4Decm z=Z)s_v+d7WWtyX0h#Pl?AmN9#!tUGn4wj2+Ra4gTKv>2MJ||tq{;rIau{xCe6cfDs zApOqrWI8=qx@RSoRMVUQC6LUZJR>Bf@%^3R(KdHGS^#xW#{^0jv);Xh?r+*nU#e!-Bi6{NsMkbPS^69v z)mQtZ>Qeae2_^M>f;ue%7jSP9hjuf=57qi@Qs)w=p<0QLY7DcJ`XHxYSq8rsig6kEjcBag{uzBkeUSPIg0=wkeqPpt}#KJL3FBUyuh|4DCRQ zH66i&fuF@wkcQ!m#r(Mh*=th?qY5{%0b;=p%QtOTQ;(*PP#Icvy2S%la?|P)&%$kX zx+tP*1NGmN4q`lSXmnnZ0nI9TP)3toatEMZ4xym1g}QLJ%9B0JL!L8MAQ*H0pjfyI z-XA`TSe`PXb&zq0Vz^?AjAf_@tzgrcXbASaA8r^8QM*D?ub-+KQhFOJ!;$~4<7oY2 z{>H4j8BwYug1^*5*|j%!;j24~asaEfXHwio&Uy`t_PbERsL8N5ByN1dXN6efqrCiM zOI!lNX6e~32zc?3iXQSLa20g@+gJvr#!n|xb^EZ1ogR$!#>k2c9@$MNjJpF&%86!tPX|%aV6;)jl zFj}9e<~Ww75uERS@w)Nu+nZ05JimUpc7`$&edDNc^wB`_Q!oZ6{s(Klg>|vxSdb5^ zRHnVI;4g zO)PQzToz$qka)2^hG(jCb6Ze@u#6!*)C6r6#^Ww*Eemny| z)2`*#CnCNQ8v?5A;J{NCgi+>cu&Mx;HniBht}Tv784l4iv&Q<6m5!&H zfKrHA?b%Oc!W!;h8_7d!Q&~(3iv+SYQ|a(Z0mN3z&YrQuYlVewjl&y@&r9!J%rQt^ z*eTx7GqYIXbAVnPWfZi|IAxetaUq6EOAs75?{J^jeRp|HD0E}{@jY^0pk3sX zzRp~G%c-`_&0xW|Pxz`@r`L;)MNoMJrDqSuYc;+`YRdWt%-aPw!CxRLr6o=VIhxqh z86eo$+fp6#a4(OcO^og5JqJz=ZiI#uwld49CC!j>dJ*Q+4!Sj_nN#WbjaOrjIFG#1 z^lRoneKoBeW_u$&jY>1^nG=DI4=RBkYS)usw>RnznCivK#jA$pk{X#b8M?lrRX zvPUApEs(G{a4$ngSS6y`+N<3k1GUJqcYq%8f*f1jEGd*$tJFdOZxBditGBsWp2MKO3}YL zeNKC+Tp>FSQSn@-tmMLu;4ANtt2wj^#g$k$P}aq6(GV4{O&v&z3M+B3>go`5u8&B+ zEDpM*+bSyX!dmGed1dB+mUK$U+5N(Yaw2jz20xV!IhBd<;y z_WO*zZDNM5mZn}fIIQo9Q^I-Hh8y+{LJcbbO6cbQ$b)@NUM!tan8&CD>WkW-PUyYf zpcI9!kt;x04l{+0*X4EmZ+1`Oom&bDGHb*2YaZaXwSH0F_6Hs`Q|6~dTQh_7DbqqP z;Ej=Jad*+O{@@dbH2!oGU!x4op;ncg-8LS_9~R1Siym)@-Ed)x<#y=wh zomiUqxLlA;BIK0!tYMiQ+?J3{tB+dWW^-2YYyBI3Q*`2O*d;opMpF1`js#!@Bl3q#_C#iWPN2ljp;j{*m7N);ksO1h? z+B^YDU;y_-8tX16%T{(ax)aa)O`0r8?@#MY>UqAexDARqA*U`(xxK26=N=gR+>xe9 zIwCCdRM3?m{U#s&WQ<|NlD}yS!k^jpX1F2Jo!4E}k7`nVJ5b&O2->Vwa8DY4Bw8le zvS@NVFJX(fmM7Ybqb?^;woXvSA~?#ypDYCoKo%30;4!*g{4tzKksqmw*99TMIkld_ ze~x4ye;!OQjt>S4nCLfiPYqWGH-cC|u;8g#2UV^=&RYU9*6tKoz8xK5`p7CBd;$W# zZPMfWTGpD}X-!)rQLi97q;?|3#gRuCG$`fkm8Fk=z8h|ce!`e)0Qv5@GdJwt!5N?H zGGLiwX!-~u;1QJ_fjW!$HcJSx6;KdRG!_O$+!I~nWu67SkunW%ALuh(^W$xF5_oDM zU@yQ~>ocCr&l`wdD3HvimSQw>&tW>EJ_qj2%lo?Y_L3g5D#F#yB9eS8hJRyb(sEs; zGMSutfs4R{;6q4uBJlaEJr1<-*7!DZ^XlBQ|=gi=MP>cSdr#bkB0v;imrCU^q`y&W5 z!y%As{tnvE{R~E8k$w0fO>1+rWn^jgECm8HOI(-BpOM8I z?xVe-LoI0=*T55&M5HSa8Ck+_Q!5MLUH#@WPyUTN2GLMLHd-v&h(bLq7&r8IFN&#p zaNQ=bmR2q)Jy)?ic?Wi>%jcj;1K)@-hc`h}TL!P?%J2`**RG=C29)ng7-Kx6jQvN9 zAlO^)z}zT<#9^+NBnlTedF^vqbU_Y%?H@PwnZSO2 zG7$TGAVJl9li? zMtOFx?(KuW7dH8XfwDa5eS|1d33u9k2x!jMd&m#`)+Bx)gYxc5T#>}GB1|fPp??|+ z1U1h`i{68mlsMrbVC zCj80n4+a87ZVweDL`#lX_Fu(aPgiQ%@iy52410M&g!>6;1bs~GV^4aGy@x0l|KZu4 zkU?>qq({2;-0kqD21=W;Bsu!R7j9TsaWKNmvcS^`wU5yz)EVCNMWVkTIPbQz(dv03 zNzf)xd8BD}O@6sk={eFaeBT>zmvi^~GoHg0i(1<6E-a8RDuVJD5&o4n6M@12s|OI( z=LC$Imo38r{5b|vSQrELPYCehQJeMK5HlN_5SaA|+7=_34FpLP9QvT{&+kjxBhE~# z^uX<;5)UbgqRKr9D_~YY-w_SxLXsI`e}_dfb*wQaVj)tj%_o_+D#>|-JD5X%JMQJF zI15$UGk!ZRnrpUpnQm^?++9h3w=R8eEtj(8rdz`Fx7%JO>wY8FhOaK0)>-sOC1z*4 zuEDR0nw$qN6-JAyMA1X*g%;@VD61*nH@qD|hbudGNw7pw z3>D|i4)Cf+bUN{cs4i@Ld>)j4`4cJ}rqUa7d}9;5~y!21ToTA)La{F)o`8ysG__+v0TR(%O|ce z+W0U~ZGlL6NzxD&yOZNWHaCYrON}IM*3%~fnS0mHS2Msz!@J*q+7oHa`}lmo?0a$Z zx3#VrR|2L*MDq=MDOPLZ)&EB8r315 zs;_(GV){vIBSbZEsQ3rA~#&>?ic*lu-r%0^lHhJ{D8OwUYGm8M2_Fw4i&v%-~804WU7}oaf$H}9j8`n zVr}U`eRCek7o_S#Po$s?xF2Lwi0v3)u6p*Fl zd2@5!S9-?GFu~b?v)tXDtOg@i&zmB_m67bb0C1&doi?86=o5`cF?NT!mu!k#8(b}8 z0_K#P8{dY=REr99Sr&R-2@ky9MY^L5imo#=Q>j* zsdARpiYJ~6+z=me>N{QP2@Z_PmMr3aHe2!M>K{WYFL$0FbKw^s_S$*mHu(H%2q1?> zQ0T{Rq-4C4tUN4-f-tG?4uJWs&YOSE95`W|T!b_P$1qZ^VfX z(Iawn&Vv|qes$0;N(8H>b2vQ(qiYRfuD7kJsBOIKiR-|aeJ-Lrx^1MmH)X=rZNJpP zsS*+1ll^GF7}T!(+3r>4j5F3-Sq$;QjJ0<=n&Hx$qs*PQ*1Kgkl?3AR=$MQ0}r)d1s6sf^E%yGDjcT9Wlq@2~XKiSZeGE$4)ZW9`| zTV8Tkl|4;aoKsBMJuw9PHhqkwS!BDlH4rIHNd z)*mU4jN*>*99%MPsLh?=Ch8tp!9M24w7jfp##*LofeQYFb7q$KMV8-3tGNNuB%C?4zzYiEgPm@1j1#RSN z_65xE66fRc3yA0mmW7`=j;v1=Ct>7V#brM*MFYF;^7GQK;(ce{O=d<4O?9L#mb|oH zx?BBiVxDdvBd!g1dlUUc@KiN&IkJPt^+;ikz0#)i)O^UdQjFJ>5PI`LC6m03d95wZ z>OVf(*BroxrOs1_M$+|=*MobBdb2M$fr~@bk2rh(S*ust+4E1S;m1<@=)xL6B3Mn~ zM}0wtGZ5~1av9>IAbat7)xPD^iut-h+)8;zGA(^Ss!?ax(uHK-l(&8~WAI!+7LRs2 z5Lqq$s$FnbH2n1;MF#(7s(LvsXY<0WIBPw96#0<|I2k_rohkHCKjFz!_YWDQsge-^ zbD#a^XzjY%sQedYgQJ-j!sWX`E*rn`xkx^J1vEP}QtH>$5LU6AmbSiLKZ)Fz*YMu; z)7)#3ePV#OVSH&=R}p;3WVgP4rXHAZgRlF3LJ3~?w47y_qxovxrxASAB<_2QJ?S%tI%2zo9@0q1S?*nGc280lJR8^;wrMt zd+au%_PpdRQ#CJ9jfzfepAo2&mmB<0Kd~#VI)u?0@>%P(Xnn3SOFIMs$Cp;Ej&>pP)>} zeb!RVHL|PX9=5FIOmSzB%wPrFT=D^@cJoI3QBjE2uAO{$y@7iSeu8U`-_m(w;z>e; z_XBvao3^d5@$~AKcU;T~CWGsa)zpGhKX7=5VK`tp!%?H{c9fxM`Nd!4)HvZlDj=Dgy(o967)7=6SuooSkAM)4rZKAVy& z@e#$OulRl4_D*-6IOdldJ8&cF0>I*%!6Ul8Q0a>@F`t!pADvzA@pt7G@FJGsKz&3< zI9*JUfE*u6~NG z1UI4<*V!?KH#`?IjvkKcUW|7Se(q7Y_LjpwY9ZOPvH+6qBjSpz>)d#4OqH z=%7C0d1wb5hCr`c<>~BH4=mmF=Sc&D-1i9AGN54ywe4osy1#%iW0wr5VXE>*q&xU2 zYcU5K4N6{EpT$X$yR&$h=xf*J;^i|nZ|*aWx?u9Be@UuJgACANHT!PGWN-^-;2YP& z{YCyWzAB1u8&D&7e^-zNs~a4@a=`zscoW|pJTBH}d^65Po~7DuRo6TAh`R>w=cCW4U5j^s@U(L*4`5;G z;NkFzkhK|7RC!W?(@Iiy_DPJ?hoh`Tpm}}eZ^y21mK2XKzr_SBXT!9@oA;_jgiT5k zkK``WD6G}(J-LO;%Tt|%u?t)b`dh%bHrI-n&xOJ28!iRXd7q)4bO_;5P2|}>={8|+ z-*X+QcpK0S^)hNwoqkyhF@s1{JAF{P8l*f}i)C(l*~)~>_ZNAZo>mm*>xfI2ujg-= zbA;kfU*w^HxGy>-qjZT7SX^}Hs%8$HI3^hQ*~H>JJ!vjbCTDik^@*UIPn5#m%{{P! zVR5b&L#!oYuh`l3Q~jVTafb=BcXfGXyNG))xPxq*X@0&v0n^OzP)6GzsuY&xRv$9M zm@uuxDChY?t2!M*tijZ{!6MY-K|&R@MCU4G|H&KQ27M;Uo1{K`%8uqc{n%w-bi34r|KX_gTiv8Go@IBsItG8+(z+kCT z^DhQXaA#-@ZM#f}o(Ek>`|D%M@EZNABM$ z&--2Y?NieW<+enagrG#1t6_>VK|fVZ)1Lpj(o9lJ`+~;&VWI{9Yo0bb&uIsF10VRU@ykw4w%*~k1)(}BS3z+ zDtKA2RE-PVsP6N->1T7DSaBHL#z9`>aRHk2?s3LUvZN1u(637|{hQs@zj#soWqG!Y zyzoR#j8;%vgI&>e`esWi&>WKWe($!2hKE8j zp3makiZX1nfqMZkQQE#)eO>PRcPuPV$*thJ8Ywa2;zeCG@L(x={yzT-2jP!>XBU<` zl}Arr&;@5AcghgGX8O)(Y_oqE-3a0Fe}Gp?Xi-DG0SWO;Of6Mf<>1PQ&N>>=rbFEZ zAT^4O64tX#`dmbrkw9@&w4w^kcTL)yMP@*9M#-l_nP$$XyHt3jBA5r&`59e*L@HS+ ztu{RoB6roBgBLa*aOP~kIu`grGIus4&k2>L^-U-Og>KpOBsvMi5r-D@v%^iH`iJa@ z&?qkcBFT?Cf9Hzbc#IKjXY||)J7LnxLCO&H{R8uJHlP(RNSA& zb;X@-#phhR`Ni9lP}qUPELnI5iSu?oV-gz*QT&Q zf`f)%-uP`ERO8P2>~G9^et#PS1D`4)2RlC)tzItB&36@De9sJ8k!)5dNeEv;)yJ z%ZtDvG0a^A6W2I|_#IVmqc+)twxZ=m3Ld$i;h_}Leu$?9hAntx`2eot_B_gR-70&+ zlW;`%;yK^mkEKwa$H`7r_+pY@=4Qs~$0j)q`OC!LKJ5g(+svW}S|B_&7M*CG*J^~d zd#8f_N&QNIB1efGdCybR#nXS(RjOG5RXH(U_lm6|zZp?8bfSzUKXHf-VPRg?Z%b|5 z+|YQt+UQLr%SX%vFADO!7j1E5DJ=j9B%<$eE1i*MFfT|K##eW5+I!6RtMR;2yf_k~ zK4gb#0C%Rh?6COM=1dC4)-t5m7c^D(^X*{(yIja|hohofW(e>l7qsMCA= z2I?lqxgD&Z^El__DHs+RT-6EJC6YDO$Guo23hR`o7}?K6{yg(&=mJosf%ve2TGxx- zd&g<1Z^saZI1?)neI4k=2|V|FE^5!XjJF8)Uho4$fF*6?wm@Gd;9vGYduhmxagI|b4R>fM|h3W!s(IwwMI9# z$yq1<^X+zZjZPcEBi1F39_v}vVhMT@5aI#&i+uUOee!O|#+$bqnwu{}zre9@q*YDx zD*Cxk1yBiu0p-le9$B6&*D5?kx6tf68{XHDY$yg=0#`Ir%tp=<{-(v@&EFD=e__Nl^;_4TG+GY^Xj8Ys(2S@ zTq0?~snp9&+)QsouAv7_T?gu1$lo4~BlS(6uL&rl3OcL^sy+7_S`1A%*6dg&N;A)d zRdV*8-rWhIHQ5N8WGcH+-C>$y5w>yY&!r^AQTw*n>C%UUMSXcu3AJV~r7})+u0`<< zRy!~E*=4yy2&yTRc8fDF%N5jg!YQgAS+HzJ0rup-LhP|+J9e)UL*j9&)V!|;Q z>$xZ0!+4*JmA;zDM^Bj*Vd-}=EnxY$GNviG&Augeec(M0r_^ZBeE%)m9HQfnb7}Wxyz5zEvSi{y^2S3FV6p$>S72$BDUlBabLEvLqxIJl*vh(8G z%|v|(^+O+waapagtFX(Tvn5b2-gn`XLYOn(MzAoJvfL!D1y5cXH`lKGtnehUKG_Fo zKzi)tr`aq8Cj$&^?RF#}!K*eYieM$OeboK=6;Us^Nch+Oo3_DuUlF*h0!cha zx8V34$|!2=bek=8*{UvC+?d?VsH_~&ESAWuO|ThU3sb$Qg(DlC^ZEK=U0TU#w0)+B zSt+^R*Iy|ylwGjK7u^a5L-8FTddn$;nqOvhU^X`jp6oZRX&SkAe{!#DLh;3X>Iu`&J zR(20sCz*<`%@0UlIPz%=kT6Mg6j}2lK4!i+r1&i1%(iw}Fe-Zyse?y+&a6 zztKEE^E3`v`?h?$5XL8Vu3yITU=2T48wSRz<)57k?yX+gjNEB@mN_AXFHZ-@*;HD} zT`F4$L7OJgfTApFIIOM)&A6!_rQ%lCu*EB zu`KbI#oK8`_fLoeV*eZJ`*JtKWs$7~N>2jJo?G=rxv)k%km4W+3@|2f$F-&+~SifzgX( zv`B#8cWm5a9L88B)~dbLdM^81G~J?YCs@c-p4O3iIcK&+2jX<}L+v$z10}q75KH#s zBuIqm%aaCWtzjim1Cw<11aIF01+#wZ$luUD0pr%;jl)(>V>uvuoTji>3tU(7Ms3jI z+UVt~!|>~ihlk>NLoDpv#ulddRot3&%F39OciEvUgW%d_+t_c_7@s_bDmY(Xe z5+RBKIuIxW(%WMmouj{|nlLU&(C`h{6j@5w#EhJ|nmQa&;`iYcA_J~Xf`i?xy%7E) zxRGjfy#(w7XVh`7;zk{MdIS?QH-d!pO7FK(cRQL3c~<1-=(P6^P$S+$Hd^S$_4b;t zFXrq@>7c()1pL^+vz}J0vmTSYW-kAUow~)xV9WysbE!ba2E%j7-RQ&9Bn6ops#>?s z5r8XH=2Qy$8s^(%st9x!D!rTl=-drq-FlW7%5>XBMpCN*GqG`vAL2IAD&`_OxftW26B1+SJnxh z2uVdu!@V51YTB>~uO&DW_hRShrYFB7EKV3SLag~6=#8!cV=-%nwt~$5gDW$CPT|}q zdj=J&=IY|x62j14`s%*DS92n750n*V!V!qpkgDuLS}X~Ie6eAMN0r&O5?mV!1|AFu z>?XQRosVX5?F9;NWvyR(E()xMc$@8Dbn@{Q=fXE_iqz&4t6`Uq=5k|Wdvb$aLPW6; zWJu{#z=tGF_7mK@(RMY8#l9uc<4W74tkq%Ud_FJfn(4TEi zY4P{lV_9LjLg(Nih4`5-nA%_g8UpM|fzM5pQikR+kj-4$K$uCShmlzok zbKXQo+1eP_9YZ~gCM#Q4EI9y7q?^he6G2-}Va*@miIMy$fIAr2Uvy0HMmKFto2tS? z)QDK=g5A(*ua9~cWy6K+Uha8)z4K;d#}lDW3~=sbbK|L)W^*35mx*zOL~id8Uh-jq zb@|!M{mx5HCSR2Lsqnpr3$&B;RL=+3A~nsjs+(Uwcg9Ab1NBI&XTbH1%SQ5snb*;P zZc@$thLoUvdfCF5BUArqzhTw;aMs-Y1P8 zQhM`VQmt8aYnLKTIEg< zuxeOGc2s;g)fKP|6f!cH#XzCneKfCnYayD_+8D!E1(a)F>CjW)<3E>p-tBeIT3D4} zzL}h`Ke{<~?S}r&2S6P?(G};RzKhNrpcf5rb=rfOqz}$WBN}v{YFQX&sC42K9>+ZS zJ$+LD7Oq=B9XEt?votj*wtE>5)7MDo2bF#Vpp@w=GeH0OkAEFH*9k%_K!y`P7VNZ@D9L?x|yemg;Lwp36vO|Bpt%Nw3EryB6^% zqO^HwV@)Wl!%#hfe*Zo^FiAIi^RA?;me(F~EZXh`-0w1aA#5c=^*K^|6oq9wKSjWtZx(^h3>N{gRfFZLpm*L!<%I{kq`o`Lv5L5xi_;c|Em|GSw0r2W3G z1zID8NJHH1sa71imJarw{qV>>WP0ea0AjyqaGG9v$(}n-7((f}N@+|DO?0HispAdl zr2b~DVNK9t1CRx63!N1gHAPz5$0K`U4y>7Pn{m*qsEz2b%Hf9Vv?)Kg&K;LD_dFVb zn9b`@IoNmcLyIPEKj=^-MSE|f&MAp&Q*NSc-5;=-Pa7#6u4*Kv`-x+W3@V1(4a_ z#2j)TE#TKpMYO|R^H6-P@ zexl{ViYBfE)hJc~-t0(IZa;Vq7t?Ud zA^N@J=Ynzv{imIVZ2+Z|^6RzOmU029MH_y(D*{zcpRebu=1D&1p4k;I*BCi&SNmzZ zWAyEoxb#HtCseyZNV#WKiIaW(ZyW-S&Xy30cb8s08Y5_+<#9`2{B{YyoJDayLGq59 z^?y84Q1HAN1lKY3*>*IlZ>%+#+g7C5yM9ikNU{KauV$jfcK>Ghcr!YvUthr(=SDEF zxphC|%(l!fjHGEBME@2u3phQ~QXglf`(5KX#_$y$hNbHX?mTc zI%3Nf-R%+yJ}&k(oeOkO>jjo-sN^vv1Bo!${p1c1U7c#2dqwfd+BgVLExt~I=HJG| zmH-BBXah3`j=(ag;@H#ROni|b?uf9L#k7>*>0TNzK~3Pt*pyQ;EzdY5+KV24yBRMn zFl&kq10#4Xfdic}ozZOxs2ia%_v&&Kn4V$5ctV5#&qf)Vg~l&`K{Do1Q1ENJt2t)PQlzCm@hP1$(c0toG#4ttiN|@ z55*zm?J`Dk4*o1%{-}dQybT`OxrgVFR#6=k3>GT)S&rMYy}?_l-=)W?|E##C`LSUj zbUIu_*tDBUI(i{yFe7W8mJy)gZHEnAJ!k5&-WanMF`A+a8;ev)mK`=oEq(kgumis~ z-~1V}zd%mWdK?J}ti_Ikx?$R|@;c5xE`e3~*WA_@#+Ygn)o;8nxzyN#pf>kx<&m78 zqG~~PT>H`Kr#}v(-SQhyWLaO5>pb7FWg+R(xi`1!TfSnI9(qtl2nGTVx%iGGQbj~r z+SB!VCF;%(TnEejtusADJ*)H@TzJB0Zih(+``+HGIpvXjMUb06S+}NY?qY)Mm{dhS zrWoTr_69{Su)kBuE3Y?F9<^aI=tW3!>?HSV7ak65d9+X`yShqa4&cQ7@$qlI%H*yU}7LVi2cJQBc-Ux5#TlQ%S>BBuM!TLB}s*0v0KQDj> zmxcGe<-q}kqNM|CgS7>n0ePGCGB)neyZ?{9w+@SH`~Jt35CrKGkVfeer8}gggrQ3* zrIC)Iqyz*MkxrHF?k?%Bp^+Si9y)%9`?=tK-*{i&&-459`#kf<%$alcS!?aJ*4}&V zwO(7=PRidVDZMZH`N`|O0~z%D^?x@BN23KM;UeMPx2|U9tMq_hAiJ+$*#)9^>u+eZ z>Yr;g9+Dww6e#cvGdIiB%Ox12;73Hcvlr59khLm|9rHEj1(i%#Nl!StL@B5R` zyRSrt$wvcB>mIHIgi)NDX1ld8dOuKqG;|RzY>)y=OEdN0J5%DBQ2$U;$Mli?wb75V znOL>wE0iUfuYg)UC^(CSIom9vPha4GfCHWw>ywamgS^-0I|+7Z55q}Pj~dJCC{R9z zH617)sC+F@$xxt*NhcHljaWanG(^@6z?8>OA>)1B!D!%t#o@3yIt|$?^vGshV#@Bw zIYO3CnSX3Pnyw1z{@QotIQUnk+~C!vhLRAa$ow~F8Yk~>xvUayqv@H~gI==m+$sSg znY~Vi?W1eYYnE_3SX*of^lfgE(}(0LmBk`(qG9<%JhCe&;)|b*ZwtR3ekn{Z^1vFl zzmv6kG}j2OzYsR(?fr}}L1902S~$Idi=FM0zZ-T-XAi1wP5^S#Fm6%bhyitiDyoD1 z5KiE*6+Iw7IJX$Nh(J0j^H$pF;n3{j&7LjC`9eQabo@@MyIfr(qqP1BkOj9cvDzrW z#n*yQ;0}xHZ6^)3AdotGdM(`B0Jd^GGkkEa?|HtInl*SU#nf+t%UU7lbkai90+gv1 zCtw^DC*UHhkey@pg-yR^FG<;9nh(7JUbdOv=cu4+Jk-8l`-QR143AD;Hghn~Tesa( zc%;M(O_j+e6YUTUyzT}CY9h~t;fkOLf(8j4eSAn;tu3XG&U{eNuov*jL$Jbg){~81qSk1w zs6)0*JzvKbaDThOn?Sh-R?tD=Zeo_PXGtBDZ@vtMa;z{~sv+Ji0F+w>915?iaH@BAIwIS%H!c|9?Yje(f8Hbl4?&l~`4oH7IIEum;9P%Fx;y}HhuqB?)f(>#y3M{@6tFiy<(>)Joak%vOi- z6w6MT@s+7^=%2)E=~suYzG@G?EVDCx#BMP=Bd7q);_5uktk<-k%1%PlxGWV!c_%zk zd0GnxwGTYxAG=@P$K@xrs`$Hx8+zvsdV^lv|0ObCUoKM@^CuH6iMZppaZmJg(Ez zDZNB&k@l>)%!J1;`bX=D{*cPm`4pI~#8|ScmA^zlV~XdoeMIF;TFQQQ`;pZY5_feS z6_rKKD{b@`Goap1?`Z3Q2)$L}F|sRAg$`997bd~det=R0=k;UhOXA1FuLzEfgud`B zV9_pnXvw2TmGA~-zv^ILcsT+BWHL`npz|ZkU%WXlVf59OE-B^e^hvqgRK1807O)h$ zIlr}1?2?o9UW(ahYqZHBCYJF=Hg18>qO!c_f?kEqD8E!Kg@=427z7%z6xml|DNfh8OxI7vAWV3;oti)v`>YjKDs&hrQ2Z0toV3LzvO)y&9G{9d zj~9*2Wz#q+xXkRGo}>kyRh!gk<%Gt>0H7G+D42(@0Njm48#?q_XM+nNPrYXaAcCT1 z`R7ug1+}>P{wzAFJBPOwlTLXYSzdQP7CaHsV00llGtB9AieP3Y1FanbN&JMXf(ws2 zXjO(9ctLV`c^mK9W3sF%ti>ssMsgl60x5ou-7UAA?=xpCeN(R{pEQdEJFLs96YE5n zoviUmh;}`-^zwTJEWWK9k6S$agzb)YfIRQkcUy7{VQ!kzy3h@jtoyauHyx#V4h>u2 zD(tTSFpl_JD-6R)=DR5x*#)^!pNupwr~q{FK2%ESodEB6w%ur%rQ-%Sx8HMuECILC zJW|e6yt?C@>flwL)u*t*ufZFm^3@Rk1S~G?5f_YQbgi-zQ+BZXY>|uQWXb*c4Z_$q zt^mmXNj_od#qyg|jIyw=fOQ7Ns_(3n%>I}y=YDRG+DkOwXg*0n@I%OnOKr*XD2Twr z;H9&k-+2Aw0;?ogbCSiJ*lX<)=eNkMhKNpfFhh1WjHd%Gthz(zGwe=@6jSMTQb1mI zdgrLJJTwFP3?rjs*#4e;k`Rq0J1;~CrHfvIVh27ni(9p!LU2*iv;Q2_n#kv%H7S&S z#%%p}1t8y9l0Ao1Xu=b?sVF3_cA5Cp0NZLmF5Vl&?Y}7SJlv|oG8V!Db&YB%# z-cu;f8eg-TPn7Ra-F*hC33k*zOs>k+Qwe`I^39|-cJU4S{Cc^vw*~mg{DvG5JTrCF z-ViR`VUZ{3R^?EKlH|D(*HE12n#BSIjD@lxitYqFpdb}FOe2&etRVE6$`OgLk_X33+ScE{{bG`^EMZAE^6qZl_cIi=jlJ0@k zH12YS*@-%{W`MG$)mXuJ6g;jrs}va@v3K#$DsAf&wY6eOych?+ITCUbZAizpMM?0mhi5lFx3F&i)!y zko6&}%<9a)!<+9mMWA!)O{G{xl_Ab-)#FJvxd;GjU_{+Fk;~Fm*6X1Qmp;qK+}&lM zJQfeZsY=^FOBRcQYwBHQcJXI^VD}XZErNpg^?08~N{2C%+7nhfjUvWJ6nysm!j{qe z+#XJ$b7x!D$a3%m_k-R9-^~LnLJu{Nr=I`;<(>0JzSD{C?U(ftV`z=j(tCATflMJe zN(WzLR354|svm+NTHY^(QFE43rAMk!#zk`195FuKb5zs|c-pu0x-oCtr?WP;EDntF z(yO=STLgGyK9}&vnMtD_@(ckv7B}(^R(VBlp{Q@xryPE{LvtbIb8#Zu$Aadk4vR?W z$8?^3zAKuUVPW@#zK6LX502&NM)mjT>??xVhidlfntI(FHwVSJo9om;2|T*ymU_xb z!LX>PT(mk+FqYq@xkY_Wac5r@Vp}WvN-o0d&-phtnX)0X7~gWSPs%s$+8pgjdWHbw z{0vrB|7)PO0l)t9Qz7u3QP3L+(6*4fX}q4=ncH48A3s*effEUzvq625J5ZTHhG^&$ zmUcQSU_YJ1kLrFK!?)i+ZaP7-U3Vuz_L<2GHlv}Ht(D2ifU?8PXZsk{+Fn^z=3XNG zW#S5S*24GCcYUQ|cbYX$QUb#1EM*w+{2vjI?OijAqPwrCXZhc|5EWZYOd$ zMQiSNgWpZ%Yu^f6;tANgxsBz%OC8gQ9D0KkJMhVX6XldjmRmg^=%e`N~xOQ}WpDk+TD^)U-`|MrHnRdH}_Bj{* z=(#Q`1ICAeX~8-TVR*Tg_67O&7;yCKT1_U9X?M%ItdzoULp%@qd6PK7&>l8MJ~mdF zM=^8_S@*M-6XXG>YzkS}Bl>+E#;5>XXtjLC=X)8URT4#TV?jM_@@YUr%hRrB$S*KJ zwzZd5SAFF;TQ}`75#zH>R`lAqtY4i%KRw6RQl2?HVdoYAVJ0$z04&U2W97&eWqD+y zy)HyqU#d-Y$G%$a{Q)Q8xAB3J6#$`8XhghDIS>Hy_@Y)E4Re1B3iAatbVadE%?MVz zOU?pGmnYgCPN9iW3Ty|Pv(Q*zzXq+?e9Ve=T!Syp|FP%|??(tgz;AzK1juXI{KTUF z-h@%_@3I|!qk}7D2Z$mvTn_e4;4Qsw12#fz)?~#!KWI5DwVyu{=BDJ{Z&YtgNcCwOY>6 zykFm&LwRLEf0&N`77O<(P8AV$_NM^g##Z#bevYaiFL4%kASO8+mrshU;2xeH6}xRO z_-*ca-9*#@rXc)8A}IU&JtgK*CfoOI|6e~1Bppsk@t;YRmkW2uZRQh3h z-OM|3!3SN64~hSY3K6MGWJqc7r|Tkgd=Wq)ZeqtE|HmUr2naw{jU#}OwQCNfwwsLs zzi>5})NcM$2PZ!UWSV<>Tj;78r3IiaLe3qr(DkP_UNZOz0q9dZd3kw{bzZym#qx^o zokai;`s%SiiLGB0XoVj&vGuwZ%MSo0)TNhMe)CU{{HX;mkqw{;JMKliD>_7EE@7=Q zdm1}`_QHSvX8#aiF2Ul>knZm)lpz3Ih%A?1!}$lg|3btO0JX%LWZ4>f&B%LzEE-K` zoPUz&)x7E|y5j8!WV%kXb~7VgJwcxcsLsN~L(?n8{_`VOH(pz#)+IP;EcWKLr??N; zWpxWTc6Puoic@I-Ae+f`caZn@m#*Hq?4f40w6H7r{##358G(1ImfwM1BT^XwJGT6C z^bh_9lk1B6|70Z@6?kc|S10rJmeOAkp{{xU`xb6^O8{N?mR#Waot>ASal-PU+TH(r z*FOq1*#}6QQtxm3!t^`m{k(uvbLxdD?o#o~~iPcO3yGZ~=jfTKBrmtGE3FRrK0`FpaC%G;FWlN%=s8GEVRx z-Nm1U>K%HinYR1P#8+>i4+NCkhy5YmRf~Uqw_^Y*)T;l)ke=y;_GANPvfT9uI{`9jT_3EF$KKy@6)Mus*Q!X*28d#*v{vI+QEa_CFNYjWNW{$Vql5_ECH&lNZa&?AoOJw2 z(ddbRcit~%(D;4Ifh|G_oSHXrRJqOYi~VsE35^Qi96P8R@k+5)MSv#A0{c>bwL|!7 z0j*G|N565UgG-m*f2y1kdS&?krdLY6fH1pUpoJS(cX|N^V#56=cK`Wj@*>coPVs_r z*OEh{256uR>AqIge}4G?tj+rp$8)8%?K=0zd+@RZgb(5a%nYw&^WPg;b-x_5X1UVa ze`iDKIdFl=;>{r0FLsA_(WOqP#RW%7UcK`f;QZ5Lx6poBF=3bZzqMa9uOXe4Xn_uu z^f4~G67D~l_f`L)8UZ@Nk52m&*Hcy#urLN|ve9ds`|pwvNgf2qRzD_8^Slzp_N6F1 z@Psgm<_@|9w{Vhkups0i3tm8s}Yc{(H@x8R2islxu$f z@W`L9i39=4jgPB$&2Q%=O1+C^zRuYG;!1v{hws`i*-5f5vRRcA%Y3Qf$j&7jVI)b{ zY9%Xu>Cr4hsB_SMLz}$LpuW<9zZ)g^lz@j&7KmOA{3k~KU*WZ{N&mZMfNS2WfRr*H z)LbKxe+15&{!uiklf(R*(y`1kNL^t8{JR$ZWo-M)==ZPc_wf=HdvJm<=l0d>!Y{|J zn(@P5ch7%;SGSJ`ng}g6{dT1b|4IhnfhewJ*Lhhk5@Ri*M)7ODKqNWmaw!U5He9;a zj}-tip6I{m+J92?{~+V9J;h&j`u{=3t6xdy|4+#H>%%+#MN5@5FTJ_B+aT%hRIK`5 z4t?`a(z|}uyzT>MVpJP(Av0aOoxdOo{|gabB2<^tII;IG*Ef>U1C1hXYd*Si9DjD_ z|7l`gBI!UX{PddB%^Sb_#^k9>t34q!I=i;oD>?szGw!?`8hNb+N3S8PF3(i>aqCy* zfch85BqDmrmhbzRYqtKat-rWOCE)0;A~{p-f(QTh=#_vH6ah9?Aj^AK%T&h8JxxDL zrK(?@RV%3h@9Z+OD=fNtr$`pya#iv4t_Qk*7=W%lYH}^Wd3n^MuInd)F7rL43%akD zJ=YHTAKLO}2AVMSZlS#C1_RSQGj+5iXYzs(e`MaTy`MD-IOiq6Tzy{mj1z7*hsLdC$^!(X+qZ2A@x?Xf22(C=@IWh%l zBDH!2Y2xEaAY69!1pJYh91*la< z=(3Qx>fm2(jS>myyMP;SWqv+jz=sEP3*P}I^vVCG8no;XfGI(UTQrR3N=2jLm%0bn z?Y{Nlx~G2a%Tq&uGR4K-=OaH!qr|_YR_yI76Mk>SB;m}>=H{xH+qd5`3JH0XamT>o z{E^6~>eK!G{Z%IizcLDY!aj~{rGFIQVNdP~d-o0mz!parRYm(KQw*cyyo2rn)g|j) zD}83$A6m^8+jv#Yv}PCPgeauYQqB!vDn zEbyH!QF^ujglD!6@vr0rbK=;2;~}cGQAO75O;;gLQp0H?E2#04pl9 zq@;u)8@J_ay29DlF8y+=H&ekVKn5sG< zW!CE1>MO^p7o)kiMh0f`2wAkgFlL7y-&-VdLU{Mi2?RYkI5v1bpiugv745)xN)ADp zatjVscUW6MPOy9NER~UkB}|OrT+`!ZUmhqdQHAf>cQw)VQDc2!oB^d46&0N-&XHf) z+22adn^zK$7w^cl1@o1>8Z2l5tFrh3Dr3;O)C#$fH5wTM`@H}@eU;og40n}@iLQ}E z!4v8j@VHBlq$t4~_Duy61VYpIgO-xgUs#f|d30qiRx?E{~@IMqtH5PEM7`A2W+~A^KCdfx|6i zz=Z0qE$;^(Mt=PGJu%!(h7t{Fa&mHbn!ntk7sy3clH_-Kl7c_Wth{{Y=F}=bTU^m0 zq@|}!99b?rZY<$eow??H%z&Q$%vf}yGm_IhG>Ja0a(wx1(Spl=aa<{Y`{aM5@Ko}r znGSsd9KleTxkjPPrF3YUOq4y40Q}-KazV4flddktkq_T99dHtZeJmQRi2@dnkF}<; zs)L-e<0F~iT%Hwp?4jdjq-Ach7q!?XeYDE^;y5IA9%ouTal(pZ{2tF*yOTC8hx_{r zvUt^V>la~Yq+C{e5AnbcdUK0xz@D3Nx_z(NZiY!Kl%N$bg<(A{u__B=&!Wo#_gwWmwx1g^#wAWH}Ge~ zjTp%aTx`KqJ|M;3r+^Ops*2BfHPF23+Y~Pu@J3FI?bPMFTfz;l>j$p9(37xK(La%J8^Y#M$r?laoICxfe zvD}94?%q9I*iP!?-KcKM_HDpivi3smM5zKB!a5eaKh{Od{C^H2KRx4bem{-wa=0_C zFHrj_7wm_?HSs5{zQxf1_r(=<=`KTj{CvakD)XvOet!HQE3280W6peNMRe$$csL%y z;C%@qb!FQ}>(sU1%+zVSyg3~WtJ??8oOTm#r&=(_W(e(u@jyb&#`b)C#F9+7x$Bh_ zlbqY0YP$HzN=t9?_Obylw~M<$NU4*-n@%gM09x>ZyA}Gv;pikdS)M8 zYJ)W>6a*mBf(i=@8CfhdWg{;Z+jiElQI;ATZzj-%BD?mS%{6$fN4kfP0cI=v^r@DD!+}&$ByLHma=K~=S$Y&*?7b96CjH>>}zSy3phubO%d~j=6O&p7%Y2VD5 zXkUwlICwy+d7&bP{w!EXL9y>+As*hI&Ix6kEPq>m2=Dip*bix+OpG#Ui8%wektV=r z;H>(5#q^$U?}b8Q^7~va&J#@b7u&HS=~)}Qii$^YiG_P_IPDSdXyBJi57K6bbayAe z*xTR7dYBhrbdN<97%9L-_rmw>9UWJ}#KbXr0Sk$VRxH@0;ZS>Jof=sM1wtyF z*5Jok_4WGBw3(e*hC;?Ya5k1&>k(*OIZnG$tP|ye%Ovp&*5kcQ{jf?l{rXkgm=Cdx zcAz31h@@84u^6J*25;M`&KkE^b4?WjJ28`c^cVE zgpGXsjvoo&R+6N<&QSZ;dyc`H1#j5p)uD9URfymao;mHGp8je>_WXIr4nd*|;lca9 zCJURFO8h5jU%ssOc~qX`#KdU6W?|X;v{~j|xt_iC#Na&i?RK|{)ly|b7&tmi*z+g{ z;$OspLW@qS(^qlePxAR88~6j1#J#R#ik!~QBXvtA($?$9v*wV2U~LRMoPWZ|^+4@@ zIxV!y_q9jzX?1nA-x>E~{qg*}h7|NH%O>4^sCxmsE)DOp=Z0WywTOlm@27&JP^*o* zKFP#*Z`N0ZYaSzx$+q8>5-6S%GDF!oyGw^EFgAaLcs4sRk=ag_cUxeWMMf`maOwE< zYoy(SO^=?4)#6acK0&l#zO5Y7_8kJ=W6h3Sy|fFPdU9go0pr~8z21g1oC6jen(?-Z zCy*xFA6Ta3Vp_YWzEuLB%H`ZC&huivFXe7$l4}s-xK>z}w7X}89MQ($y^&N@4D%?v zz2u7KWEJx=j%LE&=7?AGdE#b-DO-rOK=wQ@%F`jE_aYewB&La{3jbxu(L`9D{j%~+ z*kaDQ<<1{wG2Xz@0LSi8QG3 zKCdtpsvEt7cQS-!fv`0bgOvLy(sGdRNR-`eb4C}((8;_JWl&VW@w;>-pEg()BTxgo zu-YhJ{WRT)$78B)I(g>Z+gQpi273A(neA%0m6b=z$&DBjmab>jj+>wZ(#>V&yL^Ey}cJvQSB(v0y?&bGwb)1xuaIo6)Cdm6v?5B?O_S3)6{u&bQ?*tQg!^V z_X630qmaHHiQ=EE4j^6*o3n7lfxClW(@hVTyvfw_bcV(31KGPe8Nq`F17f<V z_nrp@_LEqRo&>gb-wu}O)>P79UQp6kF6RyHb2}gb$#u!qLJ!{x*9O!4cx30mKe#dO zZ&;#JBqe3sE$B|a+d<0mQGOFoVZrL)aJ80nL|JeDdj=gn)UI%J+H)xM8}b4OgjRlki{!qEzcf+C%(>+}N) zIxRwLBvVxBU4h3eAXp^t4a-}_6rbyt-!AA@?=8GWPwFHorD zvH|Pe>)k9we=#y(NViuD(PR@)S>WC7GVM#f6(7ty-=9J=V^QF>ktk4Zu>D4R?kL$= z{jFZZoa4Qd2M@94iRX;%?2?(m`-{hu1c0E*T+wxjDg8bl4!rrom)HPQp5B^x8`#CxM z^ZLEv`{mkqC#G29ry&Q@1H&{;klm<>2~sq6oaNNr#5rbc{O`Blqx-VI@8nd{T!cBH z1pzQxn@*k8j_RCJUrFB3n zvOVr09g}>yY}+lNXA>Wav(h`i**uf8@^;vCv+j&~rgLY5lN2}^5hrEB(iq*I3L%!Q ztdNGWmU=H~gUD$_fUc5mY12V=u}6SS)^C+BtBn5Qh}n_ePdrlLrSsL?Gsh45<9 z1sR&u1QyH>qH^3t@HFrmpXAAj+ubJ4q9je-XfG0J{D_tA!UNO_-W~h!mRhDQX3%9)s<-EOs|-iGlgaMzLEoCOXW$XTcGK*+z^y~nj{aTU&SbrFr!r}>!%+O5WwzKHMFcq9Ql|R z%|EI2pt%`3f2rQQdV6|C;On2kf;d%R7;AO&Rj4;aG^{ z`4H!Vg)pMq)ZX)zgc>wej#B5cOj83gC_D|TB=4QpF!DR5ERrbD)2WfOh6vAD-D%9m z@iS~ho7ulX^t!vgEeXNLEbvRSi`*^Wl#!8(m-8oCBiSR4I1;x27>}fE(>{UVd`8A> z_j^s2c^&AxYjI=w4vwqh%qB;yeaLs6o=5~RlZg=mF+6Lzi#ePs)JY2hK;RA_wAlf`KR$Fwe&-x)QL2dgBnfDu2dvnuwcv^ z4H2Xij;1{If!+ky6hGo5o3&4@poyI+-y^hv(b2Rn?>X|bcf8c1g!9jJ<`Ye4cD(K6 zB-}T-0-oBF`y`VEZ(!5+=}@5izCUV504sU?00uNxa@6v7eM!P9eMv5f#7vA~ozeP6 zMeviBQVo{i^MY_BYp#j9<_p*3gi7O7%TOcIrlo+DM7l6?hSM4EhP!QGY7PeFneZLm zFucHH9F6dzi-wfs1#m*R@Sm1kfBcy$+i?0W5_3YiwY&T0nugL9Sg(YVLnc1%ULMf} z=c2<+wb1T=8**_{e-XxgkvU#~+zJ$Lt1F0d`7Q!AcW|@@!ay5VPnhc_bWS+9>+xWX zmCOC(x}>Vx*v65OYA-o5rqk+X^c*y&Jl;j$saIWE$sg%$c)Or9-DasX|H4)bq zzlm=I!R)%deVf{q;iLP*QZL}-S}XXDaj~O#+Y!3tOSjX*Bq7Y?+Gj+ly7IbnxYf^_ zAF%NFY@wu%0z@zbHiTe)3oAahA>5%utU>`G@r7Qs0p$#)}ebaRA~{@Okk#RyIx= z6?C6HBB)e`j|J+xIllUbreC#y<7Rx6Fm)*Jx}C13m|1z`o1JS(p>Shyv?`InL=J9Q?RxE9qB!K&3g3!0pP< zo}`x#=jk@ITD=&~Uuk%3z5`Fatt0%vhAhF`S$n;l+SHE`-^r}pt6?*!n-@DGkI1N8~04qFF>-kX;6RCoReI< z0|$y|V6AbNT74xByky#s%14*gM;$LixNV@nKYXfdv4nlb@uU95z?R_f zDv>D9y&@d!Ffj=dW~*5^FkNQP<0r=?r#5UF<$BAaG0>jzrZ`+6goUfV zuLc%Ex(JZ#(p?ctLNFPK@{i;Ge!M%gs6$E6ikg@8E#|(VaW)L)0sIB8hzEK!W&YK?5=XsWHW%*iR$@$RB=4l@VRtTSA_a49b-&}58t*-&M?5&K zn$*(0r&CqFNxxNnUKpLMOo(T|EE1^AcotG<*uL2--hsa0XoumI4p~4|G2yXn;Z5_< zK=qbHZEUJb+@p@0(n%#$Fo+gI=xFZORcLI=Bg=eD11QKLmJbZ z*nvRO?Nl*#kH==5Uq3nbk1L2jg+jf*-S_e&6$L`;yb4~GSMZ5vEZzxhw&EHN7BSVJ(0Z^?2R^|% zrmc2g^I0duYip`?I!HXrQ<$_N_oeUo?)Nm_$D&4vOaR9QwFd_$iwyhzXgvR7ZuOAy z@j@$!?d(2Jc-3udc|Gi|R8~|TOLG46fUxA|<$y=n#!->#F0Ga0rtM#YS3vrnNi)b& zhTJ%l@;FT8Z#9T-j=_NS%9`bA;H7`y+G%C0FkV{dXHK2@hKfo^l@+9opB|cY*qqryAg}pu$DSiWgCP6$A$Ik22tD_)79C2^W*b1qw>^&3Kz#v&%H4Mrq?aC_F=q_(uGNd zA7$2w_POI=VEbuO4~Hp+N{_O|#Stt^p{ig7Et(ES^A~gOvK1xI?<~o=aue#|6A~WL zdZV9x4JO*FlaDL!c=6FaY0*aCzkuLH`{#BA=hj%Ew>otqNb2C@kAw}Cw`+SC%1F(O zM>r}S3D?E3BLKB4wB6rXDr#uD=s|vfH5FwQh{yc`R^_nsBuL+mVZYX8S4+o{!azmp ze6myv$*hiFn8O-;ek#fKgU5NbV7K7h%FZ@O1h!)%Br*4(KM_Fea!#l%55lv_6IXpT zD3+ne^h%k?mj$VnV-M?qhChkGiPcL~@hP;yV%j(^{K*sXyNAn7kEPFrn%8+r98`>9t2IlPBJ< z@OtSsk_B20*Nfld-wpq^T2^6P9?ZnEOxLZ_NFC2bowU1Sfyn*ZhErj|d&l!&BUix5 zIgd`Ky^O~B%QB&gBVfF$q)LuFr+QuSUVcodHE(YOV<7s0^BE?JrRFu?|Cc~C`{T79rn_z47pX|{eu=mp#E>P)t`DCyu1I-QT`D; zCxa)8a!{<{ zJn(~h$vL4^vRbN-M)VGuOHoR~Bm&g;JN&oufXxFE5^`MibLLV_-{HolK%JphW0$aI zW8z@3jhV{NitQX<3K4sE^(C<5AM{q9>>siE77X#0Q{BxIveQD(DlRGp6`~4FBVhJY z2=A|o+q8!fjOhecLR$t%Ud(@Km01rDrVttEmzhew{eG?N^;~6r767S;Fp}MP%*b zwsB>WzQE)kKVU&s7r*5Yd+9ToM)yLQS7#ocZ#Qku!;luW$y4Hrc4&&yJ!LB~W1s=XOr&AMV3FtEmh0j0dA09M<*Q8v| zMRIV}cVM5GDreRXZz^AmFh#*}B+B9@BiX_O2w zE1lu)YQEmKw3pf6AhZ&`})7x5FJjjuJmqUmiuBSA2dplqxb;@Mw(t`gy6 zD46JABN)T&IA?QdVtN_BkB^dHfEwX`ba^10tctvIE5YFT*pTJ6a)F&o$WH_M<7E5t zJ7p2l7PkTtSrJWFWaazoZRLh9mw5$)N_{~-9#Tp$fiZb|UQoD&XrW5Dt{W_CeSKpX z)Ya9af=um>Nr>I0i)$-;k?xUj=LInhs{8qArt98n$_$pWMP*>AVEwbo75XYM9D{^? zJ``X;Ez=NCmKZ^nY}FFs+jom~OEYw%czZ}7MkV2Ku2t&N1+33EDA<-F_rj6{22ti) zFw~9kxaw(bX>xP!Y=mb>7Me=nGhwu_MYgxvLLBAGBw=fEL#1+G7??i59np%yg2MrZ zdLjtY&(GzmwbIi~GXQk=3$?;>Yi{}qaq9EaU41Q$K+C|w-p;#XIoR9w-*}p=zCfW+ zl_*vhB%Q_EMG1zuD{&2nXMLXMQjNAGi>KcyF1`{nYZzdjxR;feXCJ$U&6Q^qXyo?x zzo27nOZ4_bnbNvfU|sxOPU zoQR6P*P;s6=%$$PF*f4gAS8qhuOQ2X89GoaU5pQXW*4SlVA+-Nkl(O9d)-5W8EEJb zw4?!xu=&vyywW#PY^n$^FA9>4oQviJI8{}2Di2Z&j4J8`VrS7iA5=bomj!DE=G}WV zk@FZb85UH*^s8q$1Pu#hyM%8mDx6sw?tKD3Udb9!w4K3{j1F<{Coe%)R=%IAsX6W~ zqRURy&?>CAQRqi)=6fjgOqr?aSO~|i`My9nq=KP~Fkq9Jbzgf5!F2n)&LhUq@UhcL zqS;P?Fo#w4l8^x3vG~`tx11HI+bd$@V|}c)IeN=BIl$igdsq12u1SDTlUO}>*my`? zAp7x-`+sip`CfVJ&I2U6=4 zFPVTo8^|~iAfH1DMQv@JE@@p;rO2$5RG0AZ9a@$yy=ffK+!tcVywCGYZJt{(t}I%y z8|C73m!dY@q?CDeoU7Ld(~eyD=YsSvDI(O04X4^T$P@2PyYBtG&EVN(o(V&=DTr!% z;jT;Weo(o53Xid(DF+wH3-q7_V1e}M)-4nVx8N3er_ze`P$D1|phS*hFjwO}O}^&r zXT`#Ue0*iQ!uw{JAJcWY(+>$=G?Cb9`bU)9M*a37LIq#Xu>KK??pITj<3LpTDt}-D z6f~tzhbt#Zi!mmR=SVicQ026Fs89%pkAd7yP!c4)192=@lmpH9`1$uZ(s{T)z{`*) zY@YaqyF6T+y*T&O`na)-yOASxxd;ZpV~_XY!Gw6?B4!KiVamwd5_oiq=TSVauD*TP z+asa6`J~)dY886z=ppI#7snK^7XC!a6CgSk`H4^?*|}`8IN3lT#iPDrU9U#tbN2GR zm2n^}<(AFNtWoYE-v2WkE%|pEV;F-N z-=;7NS(?dhCm zk!e_GcgJ2D?8oX|yNyV0hKj&e*v_0%J-8invr>@yZa^_F!ilXr_45@xJiNul1~PhK zW|EtTE!%jWhdfX&t{G77!-Lp{+Q+)bFEcB?ni{e0;>E|=FM?Brv-+IJ7v+ih9r2L_ zkwa%SD81;Jh`hg|w#a*#JHQ#>hI^*c3Qfxb6M1EHYu*nXu@!QOK}NPwZ#%m=rjdL|Z%vVz4e!2bwMe#-5asr5t|9rgU3&R$ zDYW!+vPiBD@A(3L$izWYU4Fe{pKV3d>ERL71v^{v))xyr;0QFh+ewSW3ROhGp|5lrSU;o6(Ud?`nGV$|G-(;% z7{%_;&zz>^%Y(EC&a%u17J5Lhv7es7tx;73eQLK`(jU|>FRx?)St94LpB}K*1PF$H zapX-2GK`DeU_uSRu@VDS5Q~2~g+tU6+Kv$GOLFxRm=MavDCWmUvZEw(24GLg2c6Q* z3#RjGYEEtS--U|Jl-H@3lL+(%l%bcw63IA)p9C*js4oHYK0+fl%^C?LVGvcR@UyqQ zz{C(e^9}D~aeFK$xy3|4yKX|S`1cW zk}-hfzUQLM-!D-NrFyC;MW;&-Y>47)8-v1*cN&UDHvIhtd_- z*CV@*H7+;Ws#p%=aKpEgC-e1cRXHD3}Y83;r7+#Z5@P(3A?*dY7}w zlN^*47iNJX#6C{!TAI;Z{d_Kq@s||)6I}2Qa{3@ua=43mpkh-Y8$D?-r_z&*dHW;o z6?r4_hbyf#D_4k3!>yPb-5rp<+x;eMikDRM6Q-VpKtMBA$n-6^F6k$TI40 zBh6;cRCQfftvR)_hA%hQ_ov@g6UF~g?`g4b0(+yRtKnofqq}{6&g0U20DDjo1v`Eu z4x)E^5Lh@sT9!OcXtX%)d48;Ko4g-bun*&3J#cmVVtY%6c^)ma1Dh(#mevyc=Id)gA|ByjC@&AwCBdco_^?n4ePQnK6^idyAW&3Gop)TN zHNv`vgKdFg$6L1B$mRPK*zvEew)6n@yf7H~9?~`X9d!$PW^dBWIwXXW^Jh+DQMp!o z)+^I>;Sg=>ViIltW)`MFLuRmtT`X9 zgNk`x)(@>&h;kvhb6bzDogq_q_Gw4tm$#qTt`F;LeI(=%-RE(q1BHX-ktU`aktaOg ziu2^97(d$tE0Yk>(9ldazOJbYY}r=Tyjx`B-ToMydIZPV%WiWFyWx`Rg2bQyXj7!1 znTp~~KSA%vT(AbS*sF@;^taiCwxQfHjSl3&gxCrxGu)bYY`1ILOTmk2@~*vHEAzWE zwNJpTQ?6lT!o(j`O2Ks#{^7*IpSId=FykNY!{d0r=!PGV@nmc*BjVYBq+*RVHG!l^ zc@rn;r08lQ*3+dL4AaGvgN-%21gkOchrS|}$B*I@q?Tq1wJaeUm07K^GXr zQ)GkTm4_w}M2vF$nwI7|WH94-pmr>Vlu4&>O8lX?^uWlx`8(o@VPHh_+q*a#bVxSy zt>CiHi*G9OoTNP+%u?#U@zZdMKj)C<@wN@%@U+cgu3pPBEjb=nk4qUBN# zbXi>Rct@1vJ4yt3>|Bfr9SgY)&84NXe(_v`r)e&n(~-7iytF-J(MDRIO(z2<6mdZ; zDO1+bZAR~ff^WpRs zj%ljvjU0lX=I_<)j+>JO zwub$-rjtgV4`^wRYwkA)wORfgE&iNB>b(l=Q}Q2&Xd?U}3>XPmm2xXT-NSOpu`(6M z2zjECAzM{%Ju8s14hR}078^>?YCbD!mx^9tzG1Zn zt_7QU2i^|jYw@))@e^l4Xp%?efi>tLqio>gD3RZl5_y%xJ>#gZ9_6}o_}-`BULi35 zsl{%N5AeH(i1;SY=UK#qVa5F7LVB4nGXDAUei|jAiu!{CxcU1NAspe*K%SGGR`f}b zklWGgQoA3CD+0401B4%lwU8UWm3nu=)XU3=*}or$6MuwsGDCk*riL^5`Jz-wNMqqP zc{7C5#v1&fccNLmnU9Fay!ko*uro9GbbU!KK_J|&@LP6yxw>wCZCZWaw?1{iTX6*J ztu#Nd%>Z_jiIDGMDRCh+dVw-r3!=Oxy_ECEgto zk()v~{cOYWk*#>bs%nm9Q(f-5=P>sK$YPK91Hq}C`Pd}ZydQ2p%wDMJ?>b1)DYN=c zKL>y7!8d`mTtpc5KB3oi(#5mFCP5_~V1?oLoW}~ih>BH+Md7=Ri=vEzvQ#IdPpE#p zHRolzu69eEWV|5&50A=xhpPWDMFx(sFBLEOWvUPb1!d$Y)~?FKW#KUR935ZdCE_+0 z!VA^FXE3kv{np(TS9RkN0hxUsy;dQ0JKfv)padyj^AE$k8U{~w z-f#QaMpJKeo?wxa9MoYm*T=lRL%kTsz3$-PL`ZZ}vxdkID^$^Rc$#;lZ?)6QwSa*Z zjaYC4v(H(({0QB@Fc}e@pHNl^^g%zl01Ep5D!cM{sMqhGr3@mIvM)0T$#R9zSc*Zi zSH`|%%|6I5*$I=eRI)EgxkLt88fHS)ERpQSUiPiB@4t`Vz2Ee`_uKcrzW$myW9D-{ z=RD8nJkR@ipK}EJwrMJ+C{&!B@*Ro3YTR5MUOUlI$^5J^3?2KWJg@n_d6(VIVKfLm zcKIbvq2TMsglbg5V5x%yil7R_ffMB>6{$WWqLeGg(sIQ!7;W_^0pbt%WyNuHIIxQ^ zhjIk4qyl`&PkTW5q3e5u&<}MkJI{#^CL?xn;(-BC^CY^V_Hl)y>G1c6aF{W4*);jW zQAE&WJKST?t-TU^&t#k0*iI>aX^u@(cNd_2f)YiO`5%zhM9c2!TXx1TZWAGw5b%aT zA2YRT>^1iGItC3$@0ZDZ&f{St#B_FV72;l_2Oij#WWn?Ra3N7CqxeR9bKJqx{LFdV z7237TW{}4vbh;E&P>$E@-4^Jx?&7P^t$p1gNef9|xGO{GJ9IDnw*dZVQjX+RY~T1) zL{KiZ=u=GD1iVU%qd6Vid{3tg!fxe+!GXtS&kV3qxnQ_-F!K(XGfPVdfaSB!1zfxz zy<+#)(aEW&3G1DZ8=p@O16w=S25+<)#9r71;sfdP1i)bNtd;GB`7{n-!ZlEQ!^N=6 zm%9tXQ!CHH>h%;p8aC}dn9nkyOaaMYYSp`APBiD-{Eo%O-aLC&e;wM>*O%j1Xx6hc zW?C)uUTV@a%TLz3-1zpmc6&>F9_0P5h3C?%TBRgs_wr!}S<;}=x8aFvsnGV)VDeg$ zF&G)^$NjC+D77>xup=__j3`dK^Nl*=%u2QltgeBbj0{0Fpz|F`8oFEY`g3MKykRlW(!o+*d#P`mn@BiB&7ExRWd6(c72zyoM{+9P1iu zXoZUUsjci}(v|PhK?Rs(pa=PhVzY9{So8D#lT2g2Xkkvu3VcKuTVpf(Z0LjB;!GNA zy~0}_>eymav%OfmU}9RW$wCRm#z&J0{*&4unY0`cGe?2sGLo3ZrB=NEE0O`ieEbcv z1~1&yDrK>RpGw!=u4g46fYVwSjScS6oAWXhJhw3(>DJxJ?YKTLYp6Pp5dE3*l6$`y zpoArJ&kF2Zb?;Jp@?-&#+?s$c*#jm9>p)+#PoFU`7e>lkS3KN@rQK5AiNheZxI%)c zTi8~vHz7N^dnkmw*1?mH#2{LI`M25G&Xqy1k#p$u`zyg=P(w&a&>e11Q0UQ4;s+wk z#(FXJ*fC}^b@4ia{k+f^5_XLq3w+Lr96d< zOnlXTrK8(HripCeh#XkTP2Z`{<*U0J7g&5SbSGMY+)>(lc<1p{&xP0v7cZKY@LQyv z_1?rMOssv^I#n-^)=Yavo4{QTVc%J}N1#rE;=**fvOMnJT*cnYQ~=?BcO9rSD*)fl zYO=qz+#3_>rXn-svr#_M2=vI@P^*Wsqi+)}hh(1LO0yWBSzvmU_eO;H!8jp%eF-*( zFzsHyn(Kw&cn5afvyYAERcq0Abj-6bgY>^Dcst?csY0Fu?3c6iU+0eH>{98`H=#rh$jd~fhQ7&ICXN(S8{TTV?>y_DZ@%$18Wi#bZ=;{IxGW6o zY;$>b7c%V#+T@bImurqC6jMtZ45qMw;iu?d+8VWX~Ne~ zKfV!{vBU-57u5xhHr5%#CQJ#OK*Lam#C5t6VR1JgJw|UN?W^*;fqR8AzD*4c=-5=c zp*RRz1fvZ98ql)YLE6T12n8A~MPo59S3W#*R|FQ|XY2DZHtvQ?%)vlEObv}zib{3T z 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 CA^^_- diff --git a/assets/img/console/reindex-overview.png b/assets/img/console/reindex-overview.png deleted file mode 100644 index 0d10d614c5a7f1c8edb7a50976995a4375ab241e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 574451 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 WuwH`2EXGms~3~McX3r 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

=0|l! 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/prometheus/v1/values"] - logs[("Log files
vespa.log, access log")] - end - subgraph otc["OpenTelemetry collector"] - direction TB - collect["scrape metrics
tail logs"] - e1["exporter 1
OTLP / HTTP"] - e2["exporter 2
Google Cloud"] - end - end - b1["Your OTLP backend"] - b2["Google Cloud
Monitoring and Logging"] - mp --> collect - logs --> collect - collect --> e1 - collect --> e2 - e1 == "push (HTTPS, your auth)" ==> b1 - e2 == "push (HTTPS, workload identity)" ==> b2 diff --git a/assets/img/telemetry-export-architecture.png b/assets/img/telemetry-export-architecture.png deleted file mode 100644 index f6adcd0e1891787d00343129c509b195c0505df9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 142651 zcmc$`1yq%56gGGOX#_!$kVZiP38g_wLIk8kX%LVu>6UI05fzY9q(iy|B&55$yQFjW z@!mW0&#Zse`e&_KGiTj(>&SP$^M3EU_fwl-g=bP%amaBH2*g$CC*n#7#Fcpj0z(oD z1O5*|h-Wwa1I1oRN(_*D6MOB>R*C$*wRE#c#w-57MB50D*gF-&iqzB=e zZ9Yv&F*W}WAJO+FTRd!b zUt({_{`vE6weHZqRu@_4C;QEZOYO3M4(z8&pD;xK77_pNwO`-ff0c&r-`5F`K8@6W zZ>dC*wh;dJ8lcCp`|l0${U9HT|K3o1WbpsVkNikKXMI^bh5p~?_9#DA$ub#YdOKEL z=$3>3?7y4aE85w^6IJMbLoS@Ctri^g?`t)Rup%3Eu6fJ*$uEn@|Nbv|ZhwDtP`LK< zwR3g}`u|oTQwneXzdsSQN{Ghy-y12ezeFPbdncWW-kG#}h#kdA{O(ygFIoY$s z2s8vQ?{KmvkMQ-<<&_UnJ6OMen z_lUg(@8aOBS`=|BeLFj&YyVhq1u;E0=id=m`_MM@iw-d{@zHuA_1m{Lw>#|Qqa`1_O(n0i znIX^BD#u1F59Qq&E&h`oJ$JT0B>dz_*;Rz|rAJ4yjE;`a_C#2eL{H_E&StZ0D>JC?2!0HjWkxAQ08A4lZU)Bk7*9@ua8% z0-8^56JrfM&*ynH~0_&GF$ zJ2{yXJ}F6OI9h#b{nBUwfk=}jv)ul(*yTkXPEN+%sr6@ZAdmAKqi=OJWm5#@n~peo zoB5V|vI!?*394p9goIowe|Oy;sZbU52ewPfg$1Io-;8KjH~CRf-LHCjivJR>-(BwU zT}ufTm!yw+l5aWYiSP~#6mUIQf4OasiNB7bsTo&cM*c`t$65a_w{-*pVKMT9K-cp) z_uu`ZAFB#m`4I)>28WHF{EV%*`1`u9@JM-aGc$ffk|Mo~pWhyrNA z5cE<9flqEfunk?7o?jV3y|~ywK?LGUq$VY`{JIa7iQFr3wDW=!QNo#6!mItyPo}G_1)Qjy4qlARY z3h@27}v)wUS>Xpw0~sa$#aF|R!X zRV=Hvl-=&5m&V4a+1V5a2PLn1QtqLm2^hdWv9Y!7t;`!Od%ePcarTGQYO*S{Ir7i+ zv>v9guyq*O!?G6x5kq-XO*0MOuaU$Ct*>jfynMhbAdq6egpY;4X}Mj~+efWGRdZLX z;x#1!m7th(FsY*>w~d{hUsl%j3dem31qBmlmtvwX_UjsWunrPhS}DC#2DV!|0izdE zVaw`sJ_m;z0<0_qR1_+y9{umrPOq^~?lUtnHAT^XC%olGcJCg++c-_c!-o&cD)^H< zP6x4YR>LmND;ruf6~jl_qJP`$uilS;stuX!zZ>tHycPZX`N-5?1?L9o>dqDDjt-ktwWR*PsTLj2x?xsKd%w;cO zYd6_^B6sEB6?6+!!JPBVpFX|s(ak5x+UvEDSCs7OE{ zsHxemU>?@R^D*15_DjTUP(5-D`}OPP*_Kz}P90{RUhh|h9Lv!XOW_Y}Q&%!nGD}zm z1V~^hZ5CxI26M)OSNgu|&HkEsaj2@iu&|)<<0aa``myH1*7N6lW%i6$PY)z$SuF7C z8@RgYGNHs7%+z1b{zTcAegwzF=G^sx<2rAU51O07WR;V?bskj}6Y}U~sauP>l#4HH zjf|9waV-=W79hMS1w5~zellz}uw7OtYPC;E>7-*?%^v08cQDgU6?p~~X{9d~fv~cY zTlLV&&pez-S40c9J4Z}M3P&I-S4$hSf@bWePyXgZ@A93jZ&y`6D)2ZZ7_f3PH6??C z&;Q_IA!lw^ON*F+!GnvVxw~*+<|?;@Bnypm&BhiMZ(`+%Lc?(1A8>Px^!T%=ovl%T zjp)vd5twvclL#N%n79=j!^}f3T&wWUw}*q6lFBw4tLtcZab~l(p5eu?JXu|d)%?hj zG}C>runR1Thpkt#Z5T*x03tFJ1&Jw0TL{+G0<-E1w5CnysR_AI%uN|L_3? z@tNB}wChVkclIY-aXLcS)iz7dkWIw@9Z>;$ogb_HQfmXRxjFOR))ZyVi*hJYSl{KF zY46`hBMQFK_QBE*ZcoS6)UU|{PCl-Fd}%4;XsPv86BDVAadC4!sm;7z7vw*F*um{Z z+>!D0FE1x+ilj~}wHzq0nel=ShmMPjd*#Z}&xTa8U$aUx!-(xMYqy>>IT^nI{Qk1Y zA3q*c9ZsVDYF1ZEJ3e-4=<2#5!a~)tp*mm2ow>+c z{>@#2eHaM>!f0?jpL|oApPxteZRAoYDkj_Iz{BlH4HVv-XuDSN-qoe}O{k1v@fWt_5V2)w+BfZmL9R+S}hRTUuJ`7utBD zVq(w=%CaRbW1fB!BVAg4MWCz8R%Et-4-M}INi5AhX`PAm^c1(XoV)^~HYc+~=_gNg zS#)c#Vf~~co2KE=)i*W0SSpBV&gehjKU)$W+g{6a^J}J}+Qh=z7L8^Ik4n@H8Xj(Y zBJbq~rPY1Aj*dlF2p76*kIT5@hTzQne9-u~yH=UaRRi0Mx&a43F_(^i+a4aLS-%MF zTLG^NCa9n*@%MhLM!tMl*qW@_*ZY9or1L55*++f{3LQ7g?9s@P@^a?oye!8BsaM+`qIMH-SU3FeDJH85&C(W^cvm4;(8z1G;8NjT|sNgOVUK)_zY zdK`E#sJTxU{d*cV-9np52D&Ni3Fp&|U@;K|9aciIv`pwS{`vU@vh+z1tW+KLuqxx0(wSL?90_?;^ZZGPZgG=o1mr(+VwM47F+ zhSiwbC8>{UHPj10l0!+CMlS_>%h?RIgbT`UFsoVIqzVj_7jr&hCZ95=i^4)dsX3aD z9?JBXMCchBhA*@UOF<HbU~kkJ@@v>ZL_g(a@CP)?V%++1QPJZ z$GanrYH>6N+|sJ5C}d)-u2EfXH+;v!Uip4g=W|>fJ&OekgB&?F!ugDspwf}D$HfBi zYADw(z5&{2^%y*)C<-J4N-c)OlM|aD@rsO$O!yiL3yX>vhn#-B4;rK2&#!JyUU;P4kJk#2 zP^~+ub;Ox?KojTA9fNO%=J*z}_wVP}y}@#F+TZEeE4N!0TVCeYC(;P9!ArO78EI8EucLfkLTxDsZ*+1<&td;1-5 z3;pXYN;vKnPKQGyHKu({4ZTW}!He57%C4OlO!}rTUZhQK0LOe1@lPW}o>|gAD}ZLg zQQpZiG+dm=_+F|fw9f#H5qrz?Nn78s0;y!yfUHd(bk#k ze$}kkVR&GG44?+yNn(vcFX<2)1A~qR)kEW;Cf1yNQ5II>&BNJQqL_T;K0nr*H*eNg ztL*;~e+~ro*_Ze7RR$&|SwvXO@qEh1BD@;5grZ}0CD ziGBb2RVAidVdT?8TVA{Q8$?9&bH?|YD*L`BC6&Ff9B2f*ar0)yrE+s;r=$ieE32ZW z*Xh9{{pX6`(nEK-RhbiE*Rfj+7apzUdC|l?-TBQpm1@}RNXnbfh>GShvo`h&Yh6}C z(GnXmI5=S4_T>??pG|Q|nC-dMco{tlGwOWBhnN^&G|Xqydn>%-H3=c(|I9M3Ay6KhIKHl~n#-|Kb+h#pfNp13T;?SKB9dxhIi5@7 z_+#rK0W9506H<3b|123vb^D54R1^pB_90a<;J&(5{Ri<$M_pgxQQBn92(x0dcJmKO-W0=cP27J;gT2fMg7jC*W`Q&W7 z!J26zdS6j?T`8v2$^7WZnHC!9;ieIdkPxAE#ZkIND!*;mVlefMxi*xavyE$4tzyMQ zeP$+lAVN5@VH01SvlWUfGu>}3Fx}2Fi)2hQ zJ^l3XyMD`Co#C2un$GxOW$Y5*UUfN|_lC=M%;g^);TDsx zapB>_IJgTwc^XfWH6`!`Qi949+%vA!C}Q0&+9*7@qUMx>ePy^Q5;h86`-A_z*bzzA44;||xyYn~yY zSF<+nF=ejPY3v7H0Os{)@0_ZAq?Y$3r*JH;go#$Uu(Xtzi99qUWFJEXN;z#9fS-D2 z-`4hy4i2+^nlN%MEU3{zk@CM9Lx*e+)`@A|@3F8n@l0j=vict{?NFJ;Yw)h zL!v-{@D&7@m;(A1KOecNB}rdzVA8w#O>i?QF0L@*KC;A5*fP=uglgy556W86vT1~4JqVkmi|NnD*dgA%0oq!ul&YI<>k9& zrp1Yh>dyo5E&Uhf=PPjYS7nBBV?GwS~+0D^yDnc$HO} zC;Cb7P3MGu@`&+$2An5#D-num@;?RSaqFDh#*~+q(StxC4^$P5BlW@RN?|x-C@m&F z*`d_GKNpz~J=IWaZ%+@e31)zA8QCuDf;BTU6H#KR`WS1W?!ZCyh4eQieTLtRikBA? z{tHl*i*00|10O$F=gZtFADCHwmDjs3PZyP$t^VbC5kK1IEjpScyRaX>ehKiGxbx%P zI>^p z>Cxib)y>Q-EcKu$sa&YRMLbSZ_Ao4flPOeVirg2;VtViZ14>U%8B!&Y`EbT0_=T54 zJjdJ^Dy{#5@C7f9rCQD?9iHsv?bQhZ5#Cr3!dP^wbk=CjfN9@ArB+ur5j>1v9k$NM zAVBie>gFnYMma18WbG;|@NGRA%+bVZBVi>K^39=z#_B&J7qb=nJti`eo#zvm@%PraKdb(WcOsIUbyd2un#rCT|!=A&qT@UZpAj!s+(i@O0Kqt1BM+OE0EHgkiykm6J^*f557MUEP(YB?<=Otm*ux z!zCSV2+D61u$Bzv<~oojfK>zs{~petApphy#upy91I<_cPq^Zr$amGdZF%?tTPLI# zNBRPFV`JA34jh46v~+eh?#wOiAW46>^?2D*|7d@8B{cxj2Uq(?!WH8H^(4b~qL{Dt##1Kwx=#P#XUV~tEyh8X31=WoaW#43Hbynm@qL9 zZ=$MbFLrv|Qci0Zla#zkbUm8Gc9zlkc!7|VJxM@Na9rPL082umvCN4LO+Al+mT8p* zHbn@DE_h4|CON0!!NDXS0`WJDwvUk}&tInWg6xs&+ZVBI&?)Nb#SYc8L3z5#c0u}Q zDX$KW91Upq=0k+l+7iXT;T8`zF!Mc5Q4qT{zuVjKlrqemnm^o+)4w=7bl7YIcuwuf zi*CP^TOV-MORph77_{7EHUFi}%@CtDV>7{_q@+%)tCV;B>?)n8>ozB(^o)(u9&CO} zNVxxj_58+Gb?J04-aLYa<|oOYr9i{Us!I{NsA-@hFHC;l2o{G^mcfrU0)XB2)G>2= zczm1!nz&hvlgG*H#2tEYtt2JFcHbgXnw1p zE`2mZw`-1QB(JiZYr@|rYALVapIb^?(m&ag_*V*I(JUc^%FtitA;-voR1Bay@`18| z6dfL#Tp$o;TM5X<+SR4`tyP8EdTVfdX9pC_AFZd1fNMm%oE)vf61~0zl0Ez|J(zO? zXr*Dt=M9ZQ%P**j!wDkAK z75)W1rGBcmy3;a_%f7M9woCtDy;SAG%E~GNc`}-tWsE48Kgp&MH35Q~{T3^!*O%5P zruvqZmi9c~Y`}sxUIG6G2q6LZ8{t!)n(6ks_DeP4Jd+=jlStqG;2v1 z;+)_4`|UMY*Lq|XzPL;C6LW2-Ye<6w3t>4{^6O})D+66fDMRH>nEB-RRRAIZ)L!u! zYDVALU3Twg>pa2)0YftMrd;@_YQB78uGlRC3SRxaV0ziQwk+ZjEA2P=`5gJBr2`cS z-%aeR_IkI;DI3NrxjfGud`6diW~=+YN*lI+-1$5yq7|o@ijgUJux%qH;+?k`&h={K z2bt%oC9Tl$ZP_goZxBM$#`zidTC&W~_(9Mx3^@}|W2&h!>mBSOHb zwKyz1czOce>SeK8~7 z#;PMksD2e&O`rmo?jheq?BZ0ueanN=(mXL*bu6GiQEV>#MB2ss>`bhzj5pa_T{q{t z+AAdu4ZpH7@vq-aL~p-SXE{DC%^MW@ayycGX?AxZ4i2}EECay{kj*30()}U=P0gpgIw8eIbP1RIX>Iut%#FMKn zQ(x^y1|{C_+c$JA?S!57hn9G*iDa_}8!B40U&7wJq2uKrO*LvuL-HEm%0GjF{r#9_ zF3LLh8Ka))o0#}6c1%ofY+!?gTivBw@fxkfS{P~1tdH)~3qI0Z_P__a`}pl=SE7Of z=9v|;H%Uq8T$2Qq+0d{b(Z(m-8ge#%hRDakeRKL>7NEFTG?rCpxwld3vT0x%(30A| zG9?t@t4!Udy}z*0FjuLb=u(rAFv5C~Wk@s~p8fGHu$@app0+8k%PdiCVS9V~*PG)g zK#$T34U5c|f}x_XSzIg+r%k5>E_ATdqzg6z zXo{HcN86Y%ld%#sII7?;5h5)DWu8DdVHy8A;r(HxJ=7>DyJl@*8pM@x?R`adiNy{= zgg0zZjBsgbxcKU@Q4G>H=RCPMmAARPc#IHnap42|Pi~y<0oTKagoD|~M2PO>$G7^p z1K{HK{?c9e^Jeg6Sz!eFBG|Gtm>+y5T{hW~G368~$T(o)}lHQnuj zG7os!|GW?gI%S^e;O-=}@&p0Kj}_Set<|2__yk%{-qcc>Rlx6pHYR9te_x7^%({u~ zaQ;bDtVjKOnU)*9eQY%Rt_2x!JHPavO8j?~|97(_$lFIg)dwX=Kn1=b0_h65dyIW5 zlMvr460n^WWMX3K29gY5(rk6{8f+M(Lf50b2|iCyWGxCZE0Mbs>Gy9?0s#(hAaqhm zb9zOOO7Qz08nd7vzzuJFgWe}Mp7I@Rge67rt>DPL-dHtdikC@7+KE?!L)*YKu72Krm|AA_^|Nx3dXNrzt9jMxBeE1Fk7hiH!lOF z5|N}^YxQcVvkx3~4-gNwcbcbA5cTy<>~H%FE-&UFzrcaetM}6f>xj!X)NBdlMR3_o z5hAAtIY={s!3v&8Pc0hk`;_nTkO<&^{VMbp&!z}NwEpGAb?{BJiVt?S^|=iU*>EGJ zBB@@k8~4_>uk_gtm)Wu*l1!CJFzn=gKpMntL&H4a)R*~UA>c&S1h5i;0LAWNqkj+#p$vna z*~kLtoo0YkQE674$}>kh_6P*{LLZaTG;STx~#_`|LBnpa(CQUs^v|5O;!vw0wUH>Q`UJoe zujd&B_dX-Pqf=^sl2=L~7c%f#l=^Bq$G97b+PH#g0qS3Ng; zd?-0w_h_IUtW2oO-5@5OYl-R$3a<`>=ho73A8E}(YwHFkaRuE$5?Ct0+9zv5;A2t< zIum_=rj7J0Zrr$0_m9utUFn37j94ddbL$vV)o+gQt>1S(-!I57D*9FJwsJHV`2J58)Qb6FqERgFdwWeW^CB?XP%e?p;JiEE0oj&qrh~F8;52zj8of@P!zYX*DC> zh>2w_PjFEB4AJi!z8C{PI}>)jL@AN@E`>2~*lMX08}ViQ0Ag#(5)$Z=;jRRqK0QZx zgYDJ~;__=DDF`a$>szO!r;{S`UH2*WRtF@E;=uH6PprlGF0b2Lo(KNvbST?oKRE@s zTlHaNGC-BH548gDR{TLd70;ohrL{&yAT2iIj##9f3+aw@TWatbj;4Lp*-e$w2j?ut zN~qgnt>36H4UBw&GtA*}asxJkmi1WH-2V}X=q^tH5L;q>DLPm2&}5b!jC}n;DLG`p zFdyVZnlfwLb4_wmu*VTUgRj@lK}bjgfLd$Y9rDtxk;3bqM>7wLsk`hKvwtQ(?&0r} z(qCw0>;5jUK2yMvC#5y+ARBjZ2jOxpU$5?Uu_ZorL=!M51R|O-vL0MRgT;2EGWX+v z!8ul~qvT0!0uVsevWNW=1R_P~dd^NzM4BTuw$Bd}UnLN`KybvM>kD)sP*HFf7mbg0 z+Xh-XI@Cv=jUAI^0*`(iP=#MmrZVT2SJB$mR$$O*ygvHv^b7?VPdVD_8{*16JwFuQ zfye#W)b!KflAD{GLERgIq6oL=`}T$n0q783@!hU4BLV|>W+pHF+~a7w^mR?w`vm@C ziH*Yi+)Z3eOilNTW4jzndnczxdEpL;wZZ!;7d4P3xyhno-hEtkvPxQYS#x5Z5?L_% zBBns-5FrBTt9MqrplRuA>-+1+=gNl*6U-t5|8f95m|qn8>qpP1B`ii2`4=X!!J zF2xp5=|*$1e>&dZkx}qZ%o1C^>)I9c`{43v&`OE-ivUWe<$R6sJT~G%xGcogh-`w< zOdAGwB?11X${;j?>5bA_Nyz}g`NE}(cAEf&caFBo!ls{LW3lI~hd{6EW# z%tZb#XGQ`vO4fNSCMtBDEv&6WA>7%MBB2cVftN2|&iu<>G_v{L28Hm4J}>mV;W<7A zY;(i}Z3emlXxTO=uPN;8|3t;bMRo~a5<;)Q2q!G_y2N%rOgL*!1C?yF*c{#7{^9N0 z*+ti0&N`` zqG3#Qj|6Hjl`0ui7a14#&~w3hi|N(X>>rWP+&^*{Vnal?0pb!!0tj82M5QD+ zER3+e4D`hUixJsyZ9}66WMGHeE_He&EV+Qnc$=YhdTWYG=0m%}aSs!uHXuZkty_zW z$Tw(YKvr_YLsdX%eu47!Q0zG`wqu^$9xj->hK!{?_=G2XaU=lx$5kM_2^)QkbhFek z?5vQE0~H}}uu)^j75aDj#vJtK_PE37?bO%ZNneguvz$~bUd6l*0x;;hKIKIL7n<@o z#N~I|Oe$sG>iu@ByQ=&aFg7d*nW_%nQy+l#r@z{7yS<%$RzUTCO24S{)&izj^U#)c zcywb4VkaD+nN*!0h{u@k9NDs_$?N*94MhVyiqQ2O2|xZbHB^>J?RBP&rTqc|6V^2@ z)RY35jxtCI9GoNxh#|n9Vz-`@NBTAVW2LYha8{8qA`m3Sz6rdP4lYakeiacE^3F+} zi-$)FkU;V1P>RHwAA2VVAG+>43}Ryal{~RAF@SIXh+(C%@SnVSN_x0i$p!(7u+}AI z02b~i8CA0-kV}Kg_4C91skLm*3I}F%&|#4gPNe<`E)vcfvVcuE)#`ywiueifWOloG zM2sVw))6g&hGiWWnYl!4Z&rL<=&a`CAdk)mWOcK znryD~?Qy<1Zq-|b(t>0PydEb+LPvTtm*+Xmpn6Je9rue{j%MnucU5~`1QXZuLd4De zeBX_fTh@VM04T(CYGloIp{~~)i-!>M=5k+emKN{#cK(~zN!@n-FRi1LF^F|lH79MX za4EgJx(Hks7VVRa(MS;T=j;YD+4QX0sHIH%(rWur-+*R>-39>Q$Dr*h&?5*pb2zRm zU_Im?Ny+Drd>F_)N#S}!-27kg2o z&eGi6gUAi(-`Wg(D<{wY0ptd=wNGPSkYXYXoA4Ar9nCHGJaj!YxvQ*2fDZl`=)HA; z)EC0V<~?77qo2lhLx7XZF-z|0)4I-tKokTk>$9WdB^R?~Tm(`t9kc$9J15oHq$BC> zo-*pFI(E3zoM+JbzQ@cknv8$(0|RV+WXOSrppBAp!@MdF8Rqn=J#$4`YOr4H?#{yD zbIDwgW2&q3*xqfU9~eB}>*Llxj!_reIay;%55GPH#|4~J2r#;kx`F#m!D}OCa{51n zmacVx5m{&~cl`o|o}L~Twg#Ea;K{(g2Zz_iDQL#&2mGhUE?KW1axJ$0sKIuF3USs(jid2+!0mH(UK1{2?QT;Li~XF(iWP>r=?7 z&FlSIt)fWS82X)s5jK#BWykmkO;*RlLALmiHs+~n?iBc)gZ$oAz+i+fj#OuMdQv&= z-t9hrA^UNg0vwk#hiW^T*pJT5Kh6jIi))N6*;g?)1FZ>3SP`brn(+dQwd>1JdAwkxrRo17>{v_b8J9*+i8mO zcN6xPK_`B9a3JB~zul&>q^w+QnN=Ws2o1>Qw4@5R26|uX(oA%QN z=g5uXatCIKbRyHyeu-~PO4pGp{K3H$8etv@Cjra7aQZZN;k)(m6)Leqk7;RsN?AL6 ztofr>Q&Lw~u_cdK?n#ZlMy^}quDn#@i?Q;>r7$gmT9}Px$nmTU3LaM5Nv2kD`Rj}y zbkJ%ijI@|59Y=e1ISA)ghz%A2o z?r8&`ybk$QNfGbJ!I3M)wy%qZ__)8k$;%^Q)qZ!m{1nn-$2+O(?Vos&#uPU8?C z-F#VwqN29Nqb~-+Z^aNBh5XE~faDw_3v*`4-pX)60z2(v0Bx>^_}vwjZ6D-{bI}k2 zm91~TmJJ_GWUu~lz3c4!Di2FVL1+u;Z;S&--Z)z*!1QCRMn@+fdY_%Y(9)u7kH`+E z(_k-|YhIXZhC2A-4sTvAh&yv2$Rl>U-hVDpS1h<9vRsi73i}mKO2FyiL!@IY;MB|M zh>1S0sxZowc|&DugNRa-JS}$%S34#fC&Vhs`z$`waVMMYpS}8iiwm;#)u+4r1J&XG zE;KR`(2oeGdh`Tqo{rvdd&DF!OXc(3uDc`@Q3ER6)_h@emzajo%LGfG{>#-wR* z%aIupU9WR$;P-$U4WV@;pGH+yG9&#Gpo&T6>gN;VT3YdVW>V1%F%R7qmq;>mx_Ibtr|?2X?^kab+Ghhx7wh^DP;FB zn4#%n@-{fhNI_Z=?dwOy&CQ8Zq_YK{RI23f%5O|m%qknXnZuHh3lUpt_y7EA)Dx7h z?ZZQeahxJEhGD4~Etm>Zl{`dlhmv$KI#F}MueB6uDCSgpy$X{CK%6GDx?V4rdF zLJ%9EQkVowZWRNBl=6#nCdF}2D%w^wb+3o4@**JK=I9f@`zYhACac2}&m)o9+9cb1 zP-FF^=7@&_`E?B+Z*qa90F2G_Tq>yI= zY5Y!VTih91;eWuL0Qh=K(o`8zXFJDB)_Q~4rez)i#K`#2Y~vMBMRC!rnxS1fAE8=I zo+S)YYX>++t8A*Jj}zk{@Tmmv`3VCoEO6boNYE}1BQ`+he1utWaYD>gj=#FIN=O8# zs@_%C?UyK4wfwZ|N*cP$wX+CAE6Nalj62}gl zMFz{#HKmD7r?b*V0D)E=t4Ts-9MrB14g5trT7MNzA99YAS_SRozj2>)dW+Nyetd8C zd~U>T##<)c?ndCG!|WFaP+|ZVgb@_a14jy{h)~NQhv>L>JeV>+9-POpgKh1% zkS3>E8ORj5^`{Oq+95y~`i&&6_xOaiztI9mRA#DaU+Y@*=g<1x_BXVz`+Rna$&pE` zplX_3Tx5A&>b5;Sb6q_{RrT4kFYH0?mL}@x^M~(KDou=y9m5%|6g?BoKL0LZ0{e28 zD*Z%=r}+Cujq^LQQ+xSiw`a$dVz)fT`uG2SH9=%fg#0dme29DVdGAANEb7AtT3I_i zFhT)ao4Z><#cO5pYMqc-d-Ltv9T-^)3BLZ=uXg1#whB`WosiC9*y{(MWR04dGSPl9 z6Qtz`7N#%&$LlGt;eX%UqKu!M*)UbRs|CGoFM!&}$S5c@%l-cQL|ygvjx3(e_>A1Z zg5i;o`sVNV5ia|akk@3ein3ySrgg32bkOk(BH0B6H#>D1qG$RuWVl8n0leuycNtW( z1jCLf^BXyjBh0?Jg-2*owoW|RclhKy`-Bv}u!}(uE=lgnqnaAa)l(#N z`QrKaE9WmIQnRpZ{qKRFqKH0YYimY77if>Q7Zu;1jb(J_YBTFUr;A!0cO1&4=O0Ss zwbl8U+}+SXX*re|AlT|#y7qYjIXckx!Gk|im9in=5i<)luMH~9du1O8fgnuuCKO~5 zWcbJa6i^bbb}0l2dUCZV+J`e_%LP8tYLm*raWn38)1S zPua7uBwhG3RkqVbp5O&ih+jbwQndh-_}Z%0atuYk-0RY6KuAaK?$lTXLzIGZ?-uUB z;cB!%fj7VlsHk*dPR@(6RqPx=3`D^fbDviKE`iQJOHkv1HnYBtB{JDz|7Q_PKcQqZ z6UVxSPjuO=*kR36e^5;uP$WJjKQ_ov3tPw>kU$|L;@2;&n;*tXEHmuKT3SdU&LKHh zfsFk;-_6=~p8a*L_&N1hQO5Wswb!f5dNR6s~cdT+!;csb{* z0z~d$5~nP`3W(Ni1~aR<1OZcxVzbcJKW6-B#tubR6Ji=QHlml=8OXiQX{D8a&5rT* zcvI=rg@wWq5oqwge{-$XXkCX0j)CFARxwPNtNxsaL&P zJpqBErwf~B$L2*C6@dg&bK5ho5W7~}Y}K48+#Nh0!9Uj@dE1)FcwVF6nWs~3*OxSd zkZt^!=oWrU0F>={WMtyWb_b8SqE2o(^BE{I^50IgxSP)Yhr(|Rv5eh0H4aa(#pzG> z`?TeH3sQL|-L{Nob|ApHGZPqUb9TfB13t+qDX4H9kU}_eAZn%eD%MpG5|AFnUCLJ@ zJ7B5+hDMyW8%f%1k8C)BpZHOKg#b!WXk`RjjYb5$)H4*MzX6F(vYW2Lc?s_FyVGv|Nv!7zT}08Tq1Lj?yQPFdO7(RPEx za1|da#D?+RCv#F6c=MtP2 zvo`2&f29T#bFK5VgJ($}=-pdz-$O!hVEz?mW_&@6;1f%l*_zUsGgl;mkot9|1aL#YVss>Pw{LSMaXaLa}gYy&Z zkDM#YAjN(Yo%=>6BP(0A{ai0>XYPiDr6oKbYAhe5Vy@${kBgM>LE;10D8j-jp`?TZ z;NSaqw8?DkF*B{lD7zUAsj4BJIA0h{%!rT7rMuUoD6P%%7>En|W*UGeSPu!EEHz8yWnd}~hG`ALM`>@y&bCA;i!EccpfzO1 z&aC0JGBzhCbrwx-<+*YUaD!egi>!~4 zpd*H>Pq7@ggis;65M%p(echtq^~!hWt!ni8^`hd1)z!cGo0UEiS_rU$UIN2rk`U`r zoI4n8YJ5?){n&l4$KY4vofM!K5WTGrA&%p;I)yMrK3vAtOMyHT9QjF?r8_Jj?IDhx zT~wRfHOqH#%PK2T*f;js)4UP7*%~{1LYHS!^GhVi_=WqQ4svB483~p5Rh5|#(fi_2 zlBIY!0VWZP&KU=C!04$=+gsSGAY8thsCgX7ZRTYTgN2|41Cl6(jEE?BMKwoar|Vx~ zSW3$I=(Zu}6b+1Pap;Kh zGGo31D+D=sz-@i;EZw{N4xBc%oX_|DR9jDpg2j=^LS%~PGmleWMP#}CRMRbMSq0Nr zx-&u;@*2p(EwMVM0%qwtrmPJan0$vdB0v9dMYrnrF0S0_D{0@_Ay9mI+@%FFUoW}d z=-3#HDDHp+YF!jTrlF1;jvB~X(gO|(^U)a!9N)w5j5U=X2kg}k=Ez*57?)Zz33zf_OvIZ2vKp_nwl*fn zHA~zhISd1^=G;{WKp6*}kP1NW_^S!4s}Ng6dL??Gav}{=P*yM~FQsePDqio@!S&cZ z&+H*(gbuUrt}CND*Fj{(#oBIc3dbe9Hl4cbBtbfq(f9D^{7}MdDz$=i?jPAAN_J{b zfq#Ac-qs>q06$1vUZw0?dVI&JEy9#c`+9=X~Y`Ue3iZJesS6kBl7z%^f)Lv)=)IyRl zlWSp--2(y=nDr3Y_7y$77Aivs1MOj9EJ2OmpmHEnVO7#<3zWkXt@ZVNkb{dJ*^I)%TV|?kDn{1Ufi)hmVI<{X2B4?h9#fy<`)Brt4do4$V3CQeU$E^TRPiI?d-&^S7Z1ALuBDfA1T{HVuH z(Sw5}9XN32ls{Ef$F}6?=*-Ig`L!nJGT^2Y$jG>`PiBA0%7{b80QT}4krNR?%en^5 z5!A91SCEk?)W5-hTs<%kI#OqeCdlocIfHCjVP+GBLDlZUipf7?=8#={_|Q9#hHf{{paQS7M1V6 zH~#dbsFk&Wf0AOk{2ku7 z&XZlzG7ko69kf!+cU5YXgi$yDdt9SE^MPrSYs}rRdixPRlFSOY{iW||mwDz;w0NCg zole`3@y2tnKZ_&AE4Lg@803)p!%^85&}YU;wl-i) z+PzxVaa8ZX-9Y}$n6x8xlxQrpbWCe#^9kON0b%qv$wNPh*AMO-skh(0>7&6Z$@%omnUjK^sARRd zEtwQClO)QE<>U9AL*Q=dp->bLI&=P?UW$IQGLK)r0;NOb{>#$b+qZog$&?-*UY<&d zp9rDb{j=06SeVHL_~dJ{XT=!d`wTu9(IRM^T%Xmp2PrDJxTuai)}Nupjs-mY9l-oM zqQNM%)~Uj>?2%)*CDb&U3{z@Y7R`A1mb?)~IZ!QRT`%7RSHy?_Kxip_6C7O)cpBrEAiyVcx4j(x7WH zoyQaRnAebQ`ARU~C>^RstD5pCwpbA@wHe>`8Ra*Suam3#XZek^6F`spI2 zboTU(pPo-far>pPxt@z^vtP3$p5(%Gl2csg_GR#TpVoA@ZgJ?<@Yfph)vQ`x{=9P< zHa<#sQ;grH3^kmo<+Aonc%eN+D>xsYk^Oa)wQ0?|hqCKfsW?{Ya z*7&RBpWDLKUXi>C>h8ZrOB&RpfOeIO;hTt5T|!?taqljyzLtHA{}%?CL60&dOJ9+cG@C-)cV>cSfY2 z7Q87z-qA};>ECK28$HWhD~S}WXn}3PY}IZJqvYoGS5{Mx*66RS(sK? zEVHZ`^H1HO9#u7VJ<@4u+i5w6X&L)j8GBAIzMV9_C!IDWBO)U6ua;!C_^WWyQ;ybB zIk_}Yv<-8J2hmp>j@EMQZogDg$99OJw0;>CkH5nAmTQ>iejCjr%m;RON)PZGe1j=O z|NCfKMMVoGZDR#ZeI;eW!urw&6%-2};%9F2H3YK_;0z09;J@oj=%9`FG$Q1q(CRR! ze>b<9|BRQ^9DgO-yHmH8t;{?fUsIQ>TLDdCY&0Ujt<%2<1%r(5?H9whQHHNn5}rIf z-(dKAj3#cHy~(UrP&HH1Hfz#d%M8Qk3E{MW@AmGUDY|vWb5^AXu#Y(z1B~B=>`6Z> z4KK1FF&)_mbSxm{RPoQq!ckNwOv$B_)iw$`$-I^<61Gcsm+k4e^dLKpCF12(juvtY zcD;Pj!D~<2Qfe*hne7z*+i#WiqQ1Qo3`YJ8-zw#vI>EjxQtvytKN`16bAL?2E+D!H zCD$1d8=t&EArT?Yed{Z}%{%^|*oN1gys=JF`_+mN22=zGHEt9;-luvS_ufX3oFn5NuA=hIuddc_M|ZCk8;QdC zH>ymw;1OZbnTo|h3v5Ut5YL(hmPptOKAsFZZ)07FQpba%%f-EjAK&iStIUF+Vv z*6)wYT7$@<5FkG3|nGhpbhUeTd5W)eCaididNrR6Hr)lLSPKYH=S|Vp7zu=eXZTo%ek|yhR(BG$_@x20%fwdt$>CW8y{%)0&Azozqce(?3Kh{l5Fv2wl3;{W8Dczurx0|nWF z5!?)0bZpg!E?(oGBzb0f;%EVZry7iJc2t5#G~&xnF4H?iR(0E&J1RS)>unOA|6?|t z^->SAk)Ik;%=8FE z1-pN%k?Xb+v6)GD-(92d2sRv&ZJb{jZ3>e9Ctk=hk@;2b2PmkNle2W-=>&?l)z7Dx z5KGhz4ULeK3BO4+K!@}322Zs&B^W;;h>}Q2GF1xnYnB(t^5((n)3L{yvGAuAx!jaF z|F4U1yEnt=c*IQxA&1NwtXCeR|1Bxuv-jrJns;SujnjTgNp?o>6rQBwjBUdv_%xM6Jb`F)`>7R>)$9ow+mW6~gQ`SkeIMvB^)8 zjYhLO2n){(nMG!*D#AAgi8OKPcS6|i(s)n(k6W6TYJTZ-w#aksVtGDVGc&u%BD5}3 zjR^V5@$%j=C#>E3Gko;yv&8C3tQ;!opFNX6^&xV+R^~^@ayH^9%FT_D+(`e8F>Q#q zY@A*moqTjq<0C;57NcUXdjy9EYd2gFn~3%V%zq2iC9k&IcjF@e_!9@4(IPLCxff(X z$g8Yfjl$YYO@BUd=v338d^)@PhswVk%l`ZPNi}jB5{&7J%MopR`%j0pj;u<&Wl=Uw zi3E*|vsK!pRuT+n@`_xpANdK`U&1?ek!_F-POT@PY)Y!I%PU&44v%WG*H>%Ecif{Eo*2xw34#^%lZGN@^MptVyoj8r=4e^h>$6E@+v~xEq1Nr!BJ>lpk6^ zt$F{W@pWmwbWHI<)Z|KUociM=PkvZuhZ^>zr^+gF1D3&`l1)EE+r^y{mV3LWByBpI z3dhPNb8K{l_tMN<&~&+Rv{5Gw?~$!fFOe;*nj@B|<_VANZym_l(*1=|&Q+tQP=7C# z(PtQ(=H`SpxvSQJ&Af%Oj&9m6E9ZZ zW#gGe?)3wif7gmz=(t5*j017%;xo!tPW^+((oyUeI5GYmEc&C;a#HSfZ|;#QxjEA1 z6)ri+#Q#jH_{(z3u_2@S!(cqMoNvB~8&5tx;%|B&m5_CgNbiTl1q z3{kwK8qIjZzN5>rn-Bj3ZVkU{@#N*Zenl;g4_i&=ZXZ1s;Lb3d+jnldK;SFM>Y%O7zlEhEuRW&A@zh>CwdfNv#1<=x5&ELQO`W9Sw+h8Vo9!Xu+6 z^e26ZrRn27D{%g4zV_e#mSI&?w@`Y*BY50Rysp}l>tw?3?s?-S>;2Tn@6(ZK@7-C% ze1#-Fmgxms7jH)W@NPH;Z2Y-MAlLRGbY=2g_Q8sVeO-Ao35p+4;Sbm)Qy-Ch{x_^J z_8f=wY(p%cN=oLPO|W}>;gBLdE7@3s;CFW8=zBbu3#ZZ@%cH;Rl@UvFg~A_^imc3? z?rOx_*gtvj_O%*x-XC_3=n+oK#BqTw-c!odfl=K)0v_6fTc&;?oWq^>)(WNHAK!2( zJQ&CuA-(sdy$y^Me($b+zUorjrv@@^YVT*IHA;;nl8I-KhxS1qHUvZ=denPQJ#S!G zs+sNh`DTIPzmU!WA-lnt)4b+GYWaP*YvtS#C4*=00B(u)_2t>RiR;{cDsfft)ABIn zT~4yi2otyKWD6MipBZPLJsXyC(j^bA#b3NxZIKFLiCU<$dHk7{ji<|7p)C(3XK*t( z$|Z=!9UsDuJ_&+%Vw0FzHyAPwc=93GO=S(nAKyjfAKD8sb_~Q2sz3aa9>~>R^X|J` zu~=jtE3=#Ob&B{OKTWjpR6C`LIeL>~1$!V@gxk$c`eSC7z4_?Br1Tz1a#cr;gs1-& z{w0#S8zVH|zyaiHB28ga*RUC&Plc?U$LqmM&=;WzT)HOOetxl&usGfAhLn`E8m!0X zA8fF3Ur_Kv0&Y6+Q>4w%W7FL-*OQI^WO#l2iE{Tfx44QYf6tQnp90V@Y%O6^zj9$d zUE2;)&GS#yho!EYKi9sJRB=%rESQX~;-6pJ?> zzF58Zyg*P^!6b08QwJSC_Qe%JI4uyc+`1Jg+@E1To}m4&$F!laDYby5YaZ7tThr@t z@xWbR0lTb)Q()x!rJ!sR1u{<4$Hfu=FZL_lu4zJMXQ|+DC5Y>TM~>sT3Va5*Wcw@E z-uy1K=igWG*WXv9{L6vi-6d47dS&LvPsSO|dEuERWkRNyl!ChVpv{t5pXa`#CZJ=0 z5REj7@RVd5ZGyf`{0hs|7M1cBxQ`0iZ{o2Jy|$QE-P<1U`H~-h5Mn9W04>Wf&MaQ< ztk9a<-b(uKCoTkXkGO*;4!l#bd#CF($|8L5GY?#vY16}QRZp;{ z93wPFfzA`>=j2Sk22T^Srx3h`<&tZ(n~>{$v&4M#@)qI6mn!orsx&_`t8WUv_;{9Z zP*KpnnD{OcdTK&r5D#!1m+L|s57`cl?RH+DhW9i8#U5Pu#8U+^wGkYk!-bh6tJ<+n;(=bqOUr?R67dQpDv1u57tUgf%AJHfuQDa6;DSW3e=De zvq+#-GFXi-;6VPzwZ2E*A4EtZ=oLNiOXSu#7E=aPcE5fd*-4K0ym{u|J&+*d_hv+nz6{lIhVxbNS{F`i>7H7D`t##eMTQT)=Cu`pY~C*x|D zE?}Kw+>}ax<3A<{O&saLC#wEXG*d;};EbJnDUY8$8LjE7>N939l)U1XU#%qy%dQWs zB?!sF=PKj?4>YwsVC|Cr+YJaJrO&;E!4)i*@1EOd;p3M!S(kx;$;ZzZMhD-e&+$na zn|kM?5gIPTS}uo}Guo;AgWX|RZ=$zyuf}pT_zYf>g#6=e$QG#5{jNs8ORf>|D^R&X z_A}tJu{qKr`xIJ7j{@cPq3O%jOUvs>l4LT{#n+6Ya$2HXSquT~{+x}5F&l%{rRK$7 z%!OVfhPC`?zxczNeTc*nWTy#KgeknqNNOipGJuNV+sg>N-G4aPV=RD&uDRN1#12%( z(A?EVGb=_0Q!A1&`r`6~Q6JgWe|Ivo46&J8NhYe*#5BLR&6LKP;=GiJsV|v1qZ1n3wp&W8a!yo$lQMmO%sz7AVr1@3qR0Q_BHr}&UtD|b%CVn6yg7Z;>aH2gZwg`j z@}>s9ybv|Zu)d$2DGh#4_oc+lA4?|rF+g9@x4F5;bXnA3^ZkWiJrE1KnB45yN+J-2 z@402#KluO5!V=VEYguFr?oFCKIpVg=C-VvaCC{U0X!}n0vd?>y$X{>Xm1LRMrH?oH z@?)(2qhURafBo`}l!|A|`FZ6_#GNI6O5RT&GN#tD!sTwA$}#pe4%uCE%%=1wyBM_= zxbhY(&DtsbTW@GLeH+_=-1@-V(hOU|aXI_IXO0Kee`Vj8mvp@!l;1vaH(NA)ylWy? z*MWNNZT^;b-PgeGQ2o49f<+8r4%&Za*ZZ%eGkoWJp!h%b8C26SarbR8z2kEd&8Vt- zE$5dWw6dU?d(F~&jB~cN$ff-XB&dc|*(vj+10s_6BU^nVdT;RCj{Ovn_vQ~k~ zjDLU!)bRe95dh)*|Hpz$e39r?W}EyQw}v0->f+NK8Sz2f{x+g}dXTQ&<< z6X|@ozApWX;NMD;u=9X3n&7XW$99zMWi#+q@-5n zh>u3Be5R- zT0Sm1c>etPuY*I3_3-UVhkfn5_K$@l9q-)4$S$-t_A%a>?u%D;1@98^=)dhA+8y( zqeIo1B)6W{87b{a*F;}_r>rRKb4SmAWwYc>4mI?cHGIDEq@jK6frSM>XzJ(Z+j(B3 zYfO-1x7lAX3JwYyz-}cbx`43h8T0->G|;NY$A9LJqiaH9VsM1;yD5KB_Y~!1Uf129 zZIa`b#;Y6sg%;${?G=}yA^2QVQ_jZbJZ_-}^0+5HkSEy>j^-LLL5pS-Ubygv>-xH< zva)jPWNlX7o7#GK1eedA7FsQPQgXZ>bvct?AsD1c-!y*N>926(dwIr_JW=xxyAyMsnp5S8wT|FgVD!8o(I>i#Ycb8;kyCmkU1)wZo9P=;}j9}ah@ zJShMC`7Rvts;~Z-wO&bpdNbYd8?WqeeJnz7l~gMJ_lN!3TC~&2QPJ+KAgZ!Q6_wW| z0Vhvlofn^Jxva24b60gqYU=s(=hvNfMg*xv5vLr6*RBc+dt6?ep0oNSSyQ2>(bzsV zIIE?qx#joa9#-;RYj+pM=-s{~%=%VWrZgO&DJ7XwO5Q{bMGK9Y@^`Yu6aJNix zYM1z1xlSEeydQ*9bZh;3q&ksomPJt~M`K1JhvwVM9J76xVj4sRSV2x8-d0>)vXY?1B%myml=Ihu1TnJtr0W)9lzV|5iu{TTI^2Oy@onBc-5l1-c;Lz&?h~ z-`~b>sQ0jFt3Pv+l9y4amOFYhlE};8THyLk&C)n-G=O@F!ml?u1dDn`TF5>1_l!_yAT~zg(vc5wKmNN|)7gxf>MIZK6zzx#cTEMeBzfsB82j!O_vx#n$9& z&Tbaf#gmf{EK6nxQ&ZE)EJ?gPU}Fb|3BJE~6^@C;J%3#mC40SkqrE-;#}8i(%M#J= ze&mYbb^Lv_l2`Yu=3s|RgebR7 zCrg&|>YAFuf?1YtNk4x4`DbeCxDv`ayl718o?_coOUx0{UwpgRaRe?hKA>#|j?xf} zM;IM<>t5Z#Jw4JX8(S~irHq~Yfs*{39CsPU5-*U*^n#tqh}^ZrqE}BQ=aLmfV@$df zjliq#&TykBB)5a(hI6#rPl_-{LEsvZ+j-~+2{-BLl6zOPwJlbLOJc^3x3P}Yf)<0s z$jx}|z!>ptg^?dWM#{Daefn2ENjAl~U^(!>4;Kz$r&@S1-PrQQMN{>%U3=Gx>7zwG z)#J4QOyJkAZCS{zvs3j3X%_i?{e4WGhn`w4r1KW@99K`w!V~QdA4LeTVUfZ!M zTeDh5i0+IYs6C)2>h(@yIviJl>h)u(~&W?AK@@4*NMQi)xuC?1l^ zxhK#{RN_k~NcOHLEiBJ^YS6=_pX3mnsxf&TzN1*|k?CAYGGk&&%DHz7T}E$rMlcqF zz61@e40Ssg)RStvp3>66-R|Gu2bOt!31k%%rq-LYOn5_gbRv=(xlwyM@G0Zn%I%Y0 zlF{U-69}Z2d}>83xM}^?%U6#=II|9&;c^co#tfPz^qarJteA2MZ23-(faoBtXH^)+ zmA_BZLGMt_rOS5(A!<9@;)mkK9BrSXgM=9rB)_hm-qDR;J`%^Mw`?l*yA0 zV_dMT7S&(i6tx^Urj)gvp`s$qc8lXfs>vC%%#!b2DQ>Vs&9aUZK^bM0dodg9E^@GZ z0iw&{{UYTT`B9;qlKAP!G1rY1%}mklM+yoD($n^vbJoQQaS|SK!AS^5KK;8 zb$j5m;t(C5@!69tZS9r(wjYl+re-)ZcVBs3V^dqgFZ>XW`>FkfP62yxd3$^9tDi6@ zHB8v}?6>R&1}=LmR;VkI$c@$_UOVH1t_XNIRIlIC=vZ{D%|>kuWf1+@U&{K`4F69Z z9ToU@hc^U0grt-{|Ev9!$eW@e8a`v=!=ro?3z;tk0(SG+jwQLdKU()*rhTn#Hi~t! z3_FSf7i%uCR6jD?x>TBu!Mw_1K=AaY4^YV~L5Ricy_NH%$l=q#XdQD0ClInKk7}mHF{GI6Pt<&k3?6;TO zlK((kF|h3?$^6q&)8A&MiLo}iDYN;5eKGd9!)Vgk2K#q)g0uGK9diX5uZ6?W_|V{b2g>`-a2V z5pSJ;OeN%V)4Nm25Sw-#b^3yIc=`=#jm!|}@-6}C0IcIqKc!yXGTk$^r-5T8BbgFo z>+7Anv*#tWZM;}|Uzv&5I?c8DucAim1tEnn0=a*I?{O^B^uat=&@Afe1VStM5zuTa zy4K22Aylvj8N>IA3X<%kO{8`G>;Gl49u8yaDfA8rl&xLfd_andeH!{-F zRJl25YU1 zudHTa1P&KO@{FOCf2|8w;@H0u$1SYIC}KygB)(&rzRnSxmEBUe*%Jw1W|F&k3GfUvx_!6M2rb)%T(zbW8$;I$Z~b6W>= zsa-$PN;OT~+?EicU3Y7HN-L(w9798|i0zIuoU1>DFS#?aa{;-7c3Qz^4#xU$ zmA+?>-PLrp^~!Q<9W+kZnisN|{i}y&vx$RhM~_uh1Uz*i?E~C!+M~*YR-rd+dYbri zm;-hg*)XV}s0bpCG;1keK7K^+j-A9bm)iFlo^O{pka}>nuP5oOCI{E($O2dLZTnaG zzLcU*Gm=xjhOVsi?nvoQv^kcsI`s`!XY_9SP9Pr zN>ekCG%|J{xmM@{n#Ajc14-z@Q{Z9O9ryb60?dlqY}>`JtQu*Jo`rm2sCcFDU^2=d zhMjQd>NmQKEb0MZGK%4+%YIM;9$c=!Wn#0-Co!kU z3PyXj-kR@sXeuW~%R4(0>U`8^LKg34k8rZG^6TpNS0Oz!%C~yWU=2vxRaI1=Xa3+_ zfqVVtf)V|uI2cm8qZ5P{kqk6s|0-EpjF^*r6W+mYbu{A2$!@(eUh!)#2x<~yjHC}e zr`M;LPVQ>0g1z$tG%W3@_u;O%tEE-^r{}{$DwU?)qoP5aSSjFf4**sR4qT=4pLh9c zErtZvAeTp9xna?t?Wu`IHh8W{(f70{TkU>f%5Z zJbZUAuT4a-g3k2eLvcu9d@S8g{1`;t@95PD?yODi8j?KpBGKziAm5p@1_$DQ7EJK{ zdsm4gJB~QlbqU-nP35*v+!z31PE}-eQ)t|!v!@3w^rq?x*33jxS(rPiOfKS{3p~xD zF{EVCs58u4`v8CV!6ULBFzDkkXlh*JZ=|JN94Igor4vYp!Eg>Zbm1_v#Z%)I@YtAJ zSoq^PayFQUB9fYVbFWdUq7i}u^X=Q>T1K1mdZN*TjyE&`KB|?hlY>IlBR4k&$%BES zlIbsL0KCNj;(uzM`H_?q*)BQO(wVrrG3sKq-mRSVtIwR1bEt+Jxke!EE9A-BE*1vK zBM$NIx;6r+O-ZU_%PV-%PP1WyN3ND-Yeq1m&mW#QIN(aiy=5$mCuB6YH(;r! zgJiIB;k0fO9|v3ROHIr6c6WzlQ`9abw~O3DSL_I$?DuVy{i!b7eU)+B#)c3CD=y&e zHftYOM)GswJ1(Y#O+9&$zum%%`rHTCs13SKd$Y>W#(*maE`$MNbAD%d;|5s7g~g?% z{t{bJaH#G0MGiF=1$m9ycFui#0APhx5@lAsmzKFN9dOzPfsR419^ZyvvjsPN20V(Y z)}Clq+4|0rsnJ~9$~n4+{VRw!Icj1b%&gZNldZRU#zd~WS=}*edp+Ay@(G7e;02AZ zo+gp!b`V1J!`Af=iD~(!14njJ(<<&87zl67osf6&sb`NHP~I{|QH@SdQy`C)#)<}9 zVT4)SY?o4o{W(ZNB6-9M_=|>GSBl2kNm0A}kmEQmBpQW_?Oj)t*vnTu5P~#AY;SsX zN;$qD!o#~ED6694Yl-dLud$OQZKlaBXO|qA^3?&OxbamKDz^xIjkz-on0cxF41kFn zzsR|5_GbFVH&`9i+978#>3nf~fSJP?@=9-nko8^E;gn?Y?j$FTgewJOKvAFcZCK`( zm;)lf(%S-E92zi_We%VsGpoUvU6*c+w9Q(r>Ro+V%LgOIe36&DFVYKzucA&Uky~jp zZ%bZ#<)4(4usAN~VCs`~k$2A0^BRf;h}{i<9BwOcw2z0_vUCB3V-<#usO+*UsBmmX zJrgSS6FED3^iaMDgOcQtW7()h(MaFI&P$v#-mP)N=!gTe@j~UR!(e?vLc;DW2_COu zbTH3lWoUYkRn^d_@!^)ZsJ4`U{Ku;Zb_+LHosZL6ZSCP&z{&g!Bh-?J|FVQ%Liyi4 z@+s9yORt4|B+Rm}{BYf@7+$3#0l4&M_-#!q*a|4c1r83<0x7`3kwVNE4FNe`?(yTr z+>Ej^vcnxz`$8^bB|yE$MT-T9l>))H@UlzG9I>3MzQ8>NFTK7@1`3L4Weqch1%Nw7 zyMLbf(hHX=7z^p#Q#_;r&oEHJ8@Imx{P4JLw*9)_v-mn5VCrgUg-B?=G(RL2zYlZ` zY{Q?KnZB!mqBhg>Tv;%2Xs8%oB=e(i^R2!#M#^f%!9W;1)6nqiT3`l-1*Flg>5>QZ zvdM@4s{YtKB;FTf+g=_HFGox!xT&%E=C1{C5<|+L(yR|jtDUtIZOb+J(!lqwgY6! zkYT=-Qe!jS?+MwG(azjidbOh{G4&{67!Uc|pgs17V@Vn+L3HASi5FUwnYmnASp~ea zb3-$?BptVd05i;_otEwC&bMNJ5imEe^+ZX^-;(wkbi#KMNbHVV0ET0Dx4B6S2~>WV z%XJtv7-SL&N6E;Zd@ZX7@jr+Xbt{PA(9qWDMt0jaVJd9j{ARm^M%9oNw+;wpcK7u` zly3a_OfN$G;JLr#@oBtRK>lxXZa~Tf$Z{XKQ<9M2?J$~iOkzG@!7vVRF*!d!LBry; z^MWdAV`&;I>!Y%hV`kv42FjS7-G~PQe@k~ta$sm;N+EOSKYiz3jW|DEJ#h&#%H;uNIFNRqQ&LYV<*SA#ZC^j&wAB><{0DCt3?i0)@XDpm*ze`z^{%B0mC-2)o-UtfQ^Aj z+XPr&>iT6lA=Q^J@87>K0s1_IK){dti!xqkH+qU*fqQ|pQhNiqu6$wmAu?`mtwRwY zy2^GZCke25-t+^1%f0k$OMO{v^d#)`=*`Zw;_>&~JI|qhkbt?##Wnp^!}42d>g8<2 z={OO45ho{>v!_pi><$Oev!A4x;c`K?wW)aKD|*PH85okbm>M9X>jD(Qc|NU8u}{1e z>Mt-Og4k_P^*)TWF~2h;B!5?N$7P%1*{f2bY=qaUd7kIjXD&pX|A!pmQ2tiG-cEgu zP*iIj5bJj#MwX50KlpwXk{O&E$bM^7&9%YIto`?}3(4T-0dE%$d&E#Z1jqNyF+8g z4M6e{j01hkRpDdm0({%Sxod;&`x|jgpNfJZT5DL9d*$e{U+*?CHy?+fI}sl+`aL!E zy%$qO)B*MY^Fbsubf#=?))nvW-MjFvlJ>=GD%RGzQNO*InKKrOKgTZ*O}1ueT3)DxMVeElrKOFtLcb=saMk;n`ge%Sr-FX%xFhjt6dA$SBSY zAAuIv;->Uj0}>Och_!%K97QxeOEMMf!Z=e$hk49HEk+;E!-6B-R|>-L%!u}hm|TC1 z*pEPBKHC!U1c*eK(bKTl(>zdYeVd$J(Z6q?%*n0HwUJfTp1Ka7m?5&hpl|f;**I`x ze1j0^a9;_epo4=fbFICiZAM;KfarNSc9oa+!aH!WkpG!f?*X@_tWrqzZlXp8Bq^L2 z3i$!U&Bg3!6LoEA38IcKtgZ9+4R_&ud%FujpEs_T{ahKw+$>B0p@<31<3*DT!JbhV6hJlKRN9$ymN=Pp;!$3vc^M z?r{hQyI!6;hRtfUjB8@}+x>@AGbe|0k3N4!F#XW_-0)lWuWN+c+5ZU_j^p3WUd?oT zchc1Aa(2s)5#1nfy8-|`OvazS%Rpl!rArm$+G=G8_FWs#DJxbDzWu&ccj2}{k?5y; zl}b)dCJ$VIxNPtEUUP$MQ~^>FSXxyajTIqaK?qM)7sApSkCIdZ=fBMi@VUKcUqm5` ziCKmN5x{o^p}u}KChp{MpshsDj8{QNK1Yyh%4j1-{EMg{n~*~^NCR)&!WM~CZS zl0U%KfO%Y>VajvetH;%PK-69?+P>FZQPSY-v1$c)p}#&JspV8bEGUKQ)rIG^D|f4cDvt8`f7*m`c&rGDWc2!`FZU za53Nu>*^g?%c9YJIvmeTfWjja{_)>|w1s$Ja~%g(t(JTFZ!5pfSW>>+=2U(#crZC% zvdzcWz0=G?a#<3MD}zAVz?6%~D&Ho|gcno=bFHUnAw_RZeffmr_6Wf}2+6jy!@rnL zc1fYcC4PP9N)U?=TQSTXfUnJG|3F|?YGQuD<$nSmwUermDsw3S=c8lYKMk~ z`sq^R!Aw)TrMw|+C^LogFS$aNB@pX0%a6sJ1Q1gP65P8wJ{PKbjBHrtV3r~Lx66as zEu=k69+DwENa4ZHV%H-f!ZC8Ated7koC}_s0bX=DHzVK4v@B_YgwGJ@zmw9 z81((H5zHP(!DJs6hheR_cHiYDC#U0-ABw@a=-@*HZ@W)e916)8Bs-lwX1n`GZ*nHK zIL>=h>3?h~8;3qC^yZ+9iTkx8jrP@@jgE&7WInZcoDFnhj>ZodylSdKRgvPy$KSoM z`Wv%bAWD~>KHesZ8eXwq+D`={gPHN30+xw>T;W5lIIY=C zHA{<0R>~^!4P_Aq&!c2F7Ctt>alJAhZrY+2#ceYp$c1BYM+*p zl{J+tc_h%499OlwRzLMt2!>IOdytOSty)b(x;M8{q|HK4Qs1Yyr1bxd9k)jRU$Nuo zqOAcVPREIln7g=5V_hfg%Ov?}cz6P;s~?r~U;Nh#@V@G*%MRrR&|T!ce(3ohoX&?4 zu%I|v2lv(a5aiNEEc;^k7Np?DNh)8ex_Lmw8CQ1z*QHa{d`qq4IP-gBBgf%>ABG*A zIeHF=Va$FH%dl;GyL!2^FaT$;CF4A;;Rqw-CpxP`8lY#0DpwR25n~5mru?I-#dpM- z%6E?6@9&Esu)}RE0mHWUEP%`h#HOBSXmA+bwQ!xDq$tG&xXinNHHe0-$b@tMXID-^ z-6bjN*b6EG(^jYhy=ukO+w>$vM$v1m6D6SLSbYq(!n9! z$Fh~cAb_yYbv)=T;J7s`2d=wHhny?Sr=vaa3i(!1l_}joY{J$AS4`=jBa< z#JbbX-o60WNq|Q{NUXoyzg9lX0=Z1HeAlu~u;<}n#+v;i03ra?*iV_Cyy>q%M6~OI zJni!NkD!q{xjUsazk+Sh1*zobTd#L-;^7P`+NO!7d^r9%1v2cz!`pbc9t_AGy^dm& zOrF2U_o=Rq0qz3Nlawhmn|JVd(Qi(O8tp*!Crx|gRAsvQ9DJIUbBPRCb=c8@5~g;+ z&KffOyMTv2*jDKTI*hq_;ONBfPtU&FXhZW=u%tk?A_iV_@Gv6B`k6Z~4RTL-L&t{BJLJ@UJyu??szYVUBP70Paur|?k^ zVzS&;L|p;5i;lZ&{sO6g@6E4ub%dZahz|)GG29F>%@;2y6hJgWJFI#WS>wgaFf{`O z*QK?b0cah1=4A!*M^7E+cNuPL$s`Ox^>|}eoDf9AOU_R_%kt40)P4@Y55GGzeWayU zibgE&Mgws=qiaIvj1`7l!1)Pc`yLeSejA4j4n}^^e5fH5qsUb}ez)Rig~`77w>#W2 zHSU(P%_FWf7#NZW{jAk~wlHkD>pZ!IyR0Z(`i@u-^#o5PCo&W*@MjNy( zXkNdqGDv_F#*rH{*3OxwMn?vsf409Lg%N-P`C|Z>F_2oNq^4fyXsjV16;niPSAJOH zyx9c0H1@!`Dn5F|;ePKO4)ys`x27b=*3uY7d$bxZZ92_?Q?qUk+^+0qX-e;sUO_MGr^EN0q{MoqqrK;J?hv z!~mufdAZe(PrqC`_5H)B3)NE!^YPNcICNoF>G@KD&!>J|<)VC@b9?dT%~IyikDrOi za(_Q__IhC81KHKa>s?3`aY+kLV}iov%DE%wR!*bYSmB-FmXazb*EV5tD~JAoA3q*M zE!~Nbhpg5Ua1XCZr>3K$WA~yn-W$CDm~KU>7%K)k5nR&oWT=ySrS?m;{W;`j(In+i z@W{>M#RD_P7ZVp}=*=oK4tkSK!Ng*Z8pO?`KQ*dN(iefWC57KD zaMpgvzhw^ujO}~}BZyJT+pG)2nD&k(&Oe)*NiWj`Hs^nbog=qo<>C2R)kjI57eLN_ ziw0BUHKtRPHW~^&TA{wa7w~}7D|d7`5K2c)Jh(Yjc+>`Rg)rqNAw8W!QWC|?B6`<# zSIv`zK{E}5-$on!;Ow;zmma*%xPF!W0~FI|=tc60-r*B?!sJv_TQQD>TiU2{;&&X% zSau$s@IEaU_xyZ{DCdX>s!x96)k=~XI~&khqkvcF8z{7R z80wZ+XftC|vA;-;?z6SclarU%S?V1Fzt$KdM(st`E1uIeK?s;QbB+0sM&xGLkHIcJ?TTv6yyu0`!VMC}4pa$>*LljNWju zM!0|lC5+&DS-3G|mG;TU=gjzQRB|_UU|QL1V4%uN!f7yu)iU0=+Zw$?49%^rDos%W z8MW>0n7SBgP;E*I$skaNJ@;7qE-&}`u&pgaA;H`2Wty+IT}a<`bo?P)`T9x~?O>(c zPAhn}f(_tezT zv;;PEg2zp8J9v6|D0PcH8U%GTjRz-ynM0~&f z{25qQ2Gp_>q%IQ7BBe5ZbEut>z2$!0z3PGQ57}Q8kvvT1WRWx4FW;gC^KWG3bWpdj4-%S%5e4(D0&G)PvA$*?845E7gAu@*_6Ja*6Ca5(5>ZxfIoi_#(b~71h-p* zswOcZ0aymBsA%xV3t>Ng8`HbqZ37uAX4XI7Rnis)@|aeJ9Da3mMScEk1CBse)|-Wu zwb8KbW%Avqe1#~UsO6kHdX{enasj>(B9{Aq1uIpk=^0Mc`C)G2>V`dY>x&s2SuC8K z56{&-l9nc;836x=*KK-HSXdn-@dCkb!IKA7wOV67S~i8qMc-z-NKi!6axxGBwEk3U zX}E-rK~#DQi@lFP5E79rCnY4n>m+$VOe>_!{l)h1g7wGB^>HRtU_U=5pPnA|ih*eLG3 zc_Vi2oVDFDNKtT?nUag$r6z6)`wb&x2TxBOZx^wb9AGg6@(QgDvPMu^gl7?7Tq1v2XaS1gNgn0D#~jX7-Q=}^Czx=0N=&>Q8YClpX>8+LDFowxH;_uY*3QLo*XrMm9#f~|Jtto}2Ok9{#W;zoW^SeU z?Cf_TA=eAd)!P*WM-2NswX%#Rs%akcSay6P1I_j!%Sg?~O-{4zsMzUgDSvw5O4!Ja z090NiANPWCCM9$P@#5j*y8{7M-$Qt#tMhq(;fjOTih+^QWhT`5Y*MX*y**yzj@NR}S>1rC8EiRoUCZ7H775fG}SSs;T8e&4^@g52^ z(N5UmrggHE41JVD}KQ^sB&pAQJY+ z?w$(d^%50yCEPj=%MO>CEXk7QlTSVgnJH$bIHCDcsoqn8W-hXY3^CxTEj|4`wj z=c(|#tP{nY*0t#kFL-)-+K#ye2$^Tuke_~6<*||{$1We99s>PAQ`s3FL=M4maYSlJ zs~!a_b7BQI2Ib_@_7R7Zj6pw^{8zV?lZNZvcEr!CC8hS+LC^>fugt<^o(3=MjXGK) zN9)k5A=aYq=(*=A{xG0;Bqfi%0-@_-xn8|xZE zKKWy-yvYP#xVU!zTx&AY?AgLQJlt@2yw}LC4O%b!;6BOONAcC}-Xvwr*S2bluq$oG zmo$}6ls587-tm7NK^Nhxj@bh? z3wths6id2qAsoC#joBHMp|{Na)?*r4$=p9y)W-Np zAw7n)B8yS{FQ$6$&sUV78FfAqVsJf@F4sG;_K+r+P=IP}% za}2vRU0K6#q2qh`W2fncHU8e!5tnygs$6EM9cIHR{w92rrtD@7=brgBy?cGI&0x}# zs^GU3|D&{9t-iI@&)a+Z`t|5KW4)s5%`p%fSz>vNMq+H#}Lj{x!BOVsl>l*&8{de{+aZ|XI zK0X)HGE{q#h*SZm93EFzE+&6gzi{64zO!@W$Uzz`k7F0n(n5)SiW!4`+KZ=w$suDX zR8UP}>g4j(k5L+}?pK%0N^R=v;gKcM)46=;?`N?W!Q}BCXD0kM4i|SJgZUnQ;D3Fk zSq`>W^v5Kc_mQKc5|DkzJ&R}KG4H3&A6z^s`AMvWE-mdWS{*lCf~fyHM~`Z9YD)IW zlZjuS;t^dBGLoGH`mm`AP7Hf{PPf$lT(0-;LDHHkL=-60I{Su&Q1Cin^X#2ZLU+=6 z>l6MuluN3jds*t&QnMYhw(NR-7dR`;yy%WxD!DoH`SWMfrIa52tnV+LS?~EcS+r;M zXk{859nTt;BVV}P9ZA(dXO*aF2Y{fTAAU1uPXW@+ZocCaR9@V1>GZ~IK@){W_UR`O zzM3F*nRKy%l&f#E{fGR`Z1Cy#eoSvKE7z| zh>PGZty!9IW{nOHkIS|)*e>BMnsf+f@yf}Wr-{7Au~=PO?e_4s>BM^(Zuii_Az@1) zxvw%*lDd0UUMjzjx$~djq?84}dGm!~6a3Z-(ZtQiFBNLiAH$5%=a%ynPnLH21mc@Y zW5ZNi)g2XThlXSZq^9!PkuNCQFe>UsLZgmT7Gg)6zHH){-^S@L#kskypbwl3_kv8{ zX7U#qjGLS2_%5!mpFr>y&>4^pjb2Qd*-??M%l+33;OEe6`G$T^l)bo6tg*^C*gas~YR#Zgn6CQXTC>y+l88i-6Sn!S9-=>V&7o#2ukx^F#>N^FN8Q3` z20^?3?DckFEexi8fJ8E~E4`wm%UZE}`L4Tr!#^V_;`emzZ`lA`-TqkL;aamNj1lO^8&+wyQOkDwo_ zlcsawm+bB;hkO-j`vT1#*Vx%FWV?RPYBePDx5K(RmG(~59GIn}V46AW!}h+u)zqe2 z3%}#Do)?Zz6jr1s*9bSQo7@^f@2F6IlXO}Suc?%_P&eY6ogG|#VbGd7b)NP+f6=-5@NYp``pCqNhhHnvOrv2!E^yPc4J9iEm8XF5s9MTCi6~32b);QNcl|e7k`bit8_IaKs zwMq_Aihf)6yP?!xvX{E0umYs3w{6jPI;rXX{=1zkI0*R}Qt&?$Zg|AhJo#`Q0|lp0#_*e8lnz-Q zn0d6X%EyR2;=KPLCeydX_9P{pV@?(#4_s)f7$xl>Ww!o)w75&1Q%pu85u>AgMCZ;_ z0H+!TY45jsht|)0V4Y4RrVSfz|ITr1aWz#_@J~IHYBuVG%WDq6q)vfe&np0+iRKI~ z?wo>>DQ;Jvr1jiIEK5~>Dc^18K2c6`b#`{XV>ciEs%VC|)t;dj(~irF3P+9N%CpQb z|9%SKFa%=;*a+&t!7qD7oz|(f-L~ZXRz_@^DGa=T4OUzqb-7@A<+=|rCVLx&$|e67 zdv6()<<_+gU$%&Viim)e0!k|>C82c!0&Zi5e6*uIvxEx9f z9rjr;iHwd{>_~I93o<-z$ZBZpQ7w~T5Gj{lFX`?!`|(54cCFkC(BK4(25*8e|7#Fl zG|8zsB{@pOaUSF??71Dc3yV@6GDy}(E6sj#{WfCu;(P+Ia0PQ=PY zwLd@vX3IG)iKdgh)~k?D6>G6DeGcZP>Lck$fGS8bgq}a|ewQVC)15nx-D(cft*$^O z?QK_b5mnu;c;i>GwpU>7Hg|VHZwDnLw3S|v;vJWjmEF2~7jkYDi&yPzZNZ$~SG8Y!3qS$1!)}dYd{{mc;IIhpT)~9W z+qXAi0mxbQu0j#m)XFd;L?sa&(ki-$UMW)~fB0}~DXZ&3gufdUomEFUkiD3R;y%0d ziUOOLmlkqZoh)nwy|W@N&c6Fn03ufxa=>8r^9y@545pX(WvLC-oj%A`wS)}z7!+F% z-#}Osf$3BinFaAch0U8*VUfx zqHQm5cfrO2s<{mdibTo%S=ETp8IL-{hM>>R>l6Y&7&8Ml&9u+;UL6EL7yuu@>4JsQ zn5>Cg87(bTHP&ashhj9g_V^PfFpUij7w0>RiACT=K630E6WP{nilqY3fXL)*^W1KW zva)jb@+$7pnakPwTl;;YPn=!NFXaj_ml;pk85n#52rF+86YDb47SL5@H8!jiqSdJ^ z9v@g(E6BTw^5nARC4*E0d?tj}ngh#FY$ykSjPc0U&W=WBY$-6{#2A>(=Qt04F(NQZ zY%C2Vh8*nI;*Rvc1(Ltf?^3q%L2AA0J^29<$JOF}`7$#Ketu6f^0>Ki8?`LCNqmNX zO0QDZNa@PKtY_VWcb$VD^zFs3} zq*^**3-nzNW|vId=8W;6ydijY;HwR0J6f@8bB8t>qYjp9dq}ej6+s`=6xak4b_2eC zO5^vHgm%_(|Gr14V|o+WX&66aicH_T>-*81?&iemL_#7jSt1;QUrU+Rv`8#}EbCvt&X- zx|H;TH*ef{F__C0PU&t1BTx7s=m6{v{8uxp;#V?$K1-6u>g#}RF2Iblix)2zXnZ$i zA-Jt9k{hx7h?0^5bVF3sA|R;eFL*4Dm2>d&e)YGX00e(}IzUQV+B+|gcy)xo2j;0< zE`}2(^wN9go9As1W{Ir+-@kwV0KE@??>7)OcWRtX=y$VHAZxh&_>c}~+)FiPe=4D8 zU|lqJNK>b-gVuX#d2np}rYH&Avx`gjfEbNITSj@ELhRn}wCDo+`Q41C|KWU2=!IRED`m?oD><~I;}ml~gLGRi{;2+H z?D_sZHa@=UGE`Rcx6&H z75Pu-gN@dV_O>PQxdY6&hLoF5m|$H{UiqLwX8Gq=uJnF`J_F|G@BK{H*7`T*h>J{*==VMQe(>@!@k%Zf=f_T1i{3L<}a)Lz&QIC7fDzgvo<-^T|BcQ*PPdi!&| z5@@_+S;4@`S+%jfjnFh}<43?hk;KGBQj=3qe6d*2sVk7Ou66dHq@~qvi?D)Z6oe}J zwQCO+>WSG;GYYY)3u=?mY3I^XQ_X;VLijKPgIf?E-t`v%2hH1CPaNv}pd9c%_yNo% z{dYm|Ar^roe3g{X@o8mcSHX210dQTdmK5+$k(@ye7Qi2e3*zHASVN=j;{jE6cUSB# z)a8a$YHPmd^*|{6ES;3S0MqW>f*&+7&(96(`E`BMV#v2y$J|?=N2w!x? zp8%ARy=TAVK+keVH^A!4%18~QRdvR5i9(Be0Aq_xCqDoamnsdf8I62-vyZdDNGSse% zR@!4!;}Uo;JgKLR~mNxulN)8B*R%~(3F5q7Qm48S=^7_1ak^&&7 z|Jae&T3WXWIjl*aJqrK=7a?T3;++UJz?MA(7!8gWXj_E) zg5UwD3WE{D+%Jz45K0S-gPYq|sLpVAb>#v2XQT{Tph8hYiqj_+VWCS@frl7JZZf!l)0llN*t=ea9{9SSr4)5A|j7|r~S1% zkE-efL}z}VKHZsJSTNuCX#$Ue^Z=Op78MpAZa(8*DmyF-f=XYtaTk|f6-C3LvcJw9 z2Ad5+EEdP!*b>@gyFWe{ZMvD&~+6duxWSw0Ub0R=K04HQ>W)ty^EeBfYDtrL5zs__~u*&&(-0EM}^3WL1W)JUR-U|Q7` zG%IwwoFiE_e}u$32Go1)gGGgG1=``^C>rImJ`)f6YSpflVR7g_a8>^NCcs5xOhvT0 zxhC)$A@T9T%E~lwtYjtxJ8N*`-t3z!_*((z;eLq`*3@fD5$r2~)gNTD=D-C6{@aV$ z(7CQYd1-~0G};5yxSlI`QH%zCwyqRMSj@=D_7;Ym56J=qmzC1wF_~#(7PkBLuEn~^ z=KsqfiO$FM=*>bZ^YnICB=E1$-37BP;>0yDFi%to%Bd-VpYeg?m(Eh33RDsF7a4d; zYux*}7*Jo(%1Cb5SBjW);JR2R9ivW;Y!Z7H;Tm*MfodF%CnzBiSH9ipfXAR^87_4M z*fEq-i6TJ;5FXY0cQ#Is+6ebL@F0o7ZaK$J*i~cb%M;H1G3A&^5x|W_BIzWZafux_ zD1gW$IgSfnUJdbK56!Spdq_VXj;$Oi+C>?3y+FsOW4#TRivExr?zvns)qXZQ@}2Tk7H z__KAotYMjUH7OG`*mK7LdIv@uj2v%bB(nwXc(?_l&(Qn^q&Ox;XLV?L?b3j7OHP$gR zi{fP?J3Bil0@@DO&jV8;&vVqsy$JDB7$h?c?q z(Mse2|9S&i6)3=~XCTYG5FHZE?>&W)Cw8eq3`v>O?OoFV8A&ysed4W$TC`OIRs(c& z!!}TUrmQNvJB!#xBSm+<_-$W8pU{PS#;=u=m>_wN> zZ43hEjD0(4Dx6RXQJHvRZX&7m5`%g@rV%arXU7{w1}>%s#nER+wJuaZ^J!v(gUDi4 zVpM+5FZS`2EI2e-dt;i7V?$)ip8ayh&wTs(w6x<`a%9_f77JHDI}bF9b30{)nU-r` z11J&ZnN9%>E!8Ui?7+Rg1~3qf=@%k5a^UcKTrnPuDXfWHvJmyRq92GXY&zVWc_K`q z@#2L;qhERchy~aW+0}|DKBg<+ZvG|IF$&}D4Nmy?g$J4%{bRIjj=|p)1}htFleo9J znX1{OSI408g<<9raDwtJFW6x6w!@xLUVe3S5m2ULaTV;@j9Sf)$Y0t5#Rtg~hU3m) z3V|$Yeeih0TtG_L=Y)wzm)(X=_2A^v6|cUhyH6oDZf<^5S0Ev%teoAkiTyBuYA*4) z;Gpd(kYj!FX_WoDg-^}3Y9)z)f`RCJ$x3zEn#x1VDm*^^vl06(JUp2!LJ^>pQo_XJ z*mW$jwcA*P?GCmAjn@iC^!yz1Nwu=k-zsc-HUDKQZ|n~IW%yd!+UDTo1NKxDV;&J^ z8S(C7+wBU-hM{6~0V+K)YD~mHyi3vfk#@=k!M?d1#q4Hj{ouL5j_}F zm@6ItFV0~3OApvek*0tit9bT1IQ^-O?BKY*cv19_bc#tJIT*NhcIzS+H9I>tFk8^( z$~cz8=Kab_JC#juwxzagKp_wevXq0jEsJs^KYm2ofFPz3YhSW-itI~3Cex+N%z{>95l%5b7*!Lecl4Yo>D>%w5 zMsha3`-nfedmZ4egeycJ8@dyd7Y9urgRJMiwb$1i^TpWIC0gcF7CLYKH=Ize2SOMs zoV5BgO&`Z}K+S5W$(!FG+cK>(+D+#GU7?%<@>Y7mSsCxqLG9VmLrO}aSWf+x-}%{R z+Gw&6VgeNA0eff;7|p2hRf%v{*H8G%DDa6DcQd-zY{5i=P9}TspjvkIoKK6*B%_D9rK?M( zH9ud_$l5T+pPKO}oSvETh54<$(L6AG5RU2~cCyj%v|tXjBF()29|!vFOp~|{yfvz=PkPxkwntC;&TqL4X(Yx7HSOP%UdvjH8L!ew%UY;zgB)eN~MjFmK@zH8=#K3CFJ}bTK zo>t~6yPD$a+@zPPnuKxn$i)ch%+G2q%~1)SySsy|qiu(bW>!{KuX~>?A2omfeo1by zVmB@G5cV&{Vqq;?ahI9H#-s#|NC*6B$U}hv)6u{NL-A4Gh%w333LFxAi)^`o$mn$G zOr6o@7gr7cs~~al@c5eoRJ;QNAJEc90e_55IDWzPxz`?54D-$!*QXIm6QBfV=jTx{ z=a~D}jT`dX)fEufzWVq$3i7k1l$9GkU<*RF(tB{DsC%zNoC$5j1~)b~AVGbzzeEPF zEJFw(ylNP}{O(|F3$sStWGV4#Y2&(9J*I&r$4>Qe@#RwvjX0sXIqk~r&ykUV1v7dO z7lAvAISTniX0}yNo^BUz?A63VLDS;*PQCgcopW8&F!60>F-^CxlGZRW5%C*B(-Pu& z!I#j`#IiWHz3zp%DsxBxX^mh*H5Py5t-7mpR^bwS8`d1}-8OfS)9I2)Eh~HJ{&qT( zG=}|;lYaFr;g@(H11^56nZ`ElWek$sf9Wd=^K}|GABY9FAy4l2PmjEVon9Fh^a_{V z{)d>83DNd7vJ3eL38rCT1VpO6D)r1k2#*B>OIBeuvK__KDxhpLp@%00C=3Yvz zS1qn|eY1by1zzSrmN*d+QEMY9fU5An{aJAU-BG($hdvA(enoG5osN!f%yF9#`k{@t z8m5UivD!eS^Q z=D6+%^#RF}s(37}~eK{QrrO=uJlNXQ{UnWuJ!4fz`VB`|ng z4C2WC(&gRhm98Yn;WlBR+Hmm*~%>0 zr9R5&uO5QuBRm%uI#1FHKF2;-T-0v>?AeZ%nK>$15NqH!RNKEGCz)9%em#Tf-4^Nc zN?5pGN-mI?yPdw)F^@BWX^}2w2J9G^cl<9GX|m8qKyAU;yuvW%p?gT=_9KO~^#NV3 zNtDn4^w-D}K3L9+yh%(fr(F$Kes*^36tl0wHeRSwiP@^Fps4sQmaR(~4-yxAwKu2P zilL)e)Yjt7J=2o9@(+rF4W^fzgak=81wbsB zMwJL3Uu=J^+R&Dj^Zz$#CCmZPXnX7d)=2SkU32ivVt*Dl!0NE`5M4I6wY}+zUv)fM zT@WS)_s*xj20%o}R{aM3R@$Ll`St^Wu}?-X^0nL8k>qCbEZwcy%3nkIzA6Nu=K&I+ z#S=ktfZN-BA(ij}xJppD0^EN!R1Z9XSQuQ9h2w+hK~(SZu5dZ2@Ap7L2kaQuygBgk zfP8^0G|e%Wvt&PfW;Y~0pPMTH6c3;pd4+~rz%?Q5NiKYq>tyG;SV90fJ$>Z6cTb7z zw5~l63`jN}h4|ba;`7cLCl_^wsIrs2+5$jMTxV1iFfLb{S7UU1g1vpB-FgC({3pQT#VzB^|ROR5daMV zls5YD=_Y1UvcvT9ve#F25^nBDAP|ASgW8DvD4!U$b{Sfk{eN^-E`Y{{$m?-|^eaCeh6hovKER$7}kA~SF zMkv=Z$?b}y-XY1F@T_p)7N8x#H2jw-=K^Tj2u)IMkKW^`SJrM>kU_r%y)tLN6b;W!r<4c`K|L1R!1^cRw8 z4z;%RR@JkYqu4A+A+8lMS_u|8aR}6Io5(6K@xqKV8aSztG&8#j{YW0RM{QpRtpY^t z27pqdeE{QgZZ$K(DpQ?fV=Y}uV~%-6k?J@{7jHGU(#4nD^VoD^6zMI9c%lH&A_yG3 zZ?04=@^~x5z6e|aD!8GkDPON=#bDXyS9f{qAmoCYLdNj3qZKD$69FW&d>Z%TW}H}4 zi7`RgCxX{=Lx9Zd0u3g!mbu*S(1RDtHo%OJkymp_2bHS#fx3r9f>$0lsV)QTck}m5 z*XGfB#8F+3JZ=&t!ifFNMSn2UG`g0ODVc#}v?|ut{+HJhi;|U(15=CS*2FBjt4A@g zLXQyeI1BBd!N7mZ85>%>SU&j3Xi-K!nySk0Gd+GFAeR7Dn_E z13s@FN+>lP0DpvVQM)CC9(bF~eD2Qq)zx|eQ^#iLaUrAq`Vx&{*tpH)gQ}A~-HVql zaTw(pkG_QZMHg@&EZJ?=-bY5VB3fD>yV0L@qSayDsRfFAw@*4AoMc;-c3Xfm%qHi<^q2f3PY}6 z=Au$0lR6pp~=4I9xQH?f~)r01A8D z-8oS}k9+`nL`zFLCyhEHBEo!YRswDeRLfUDz~CtN=4)EU4Jh<3S~h$qd`M00F+F`B zaEpy?bP~=g!l|j%%#PDct;cs?3bVI)qEkXJmpHh*xv`M~gdwC8e3sS#o$hh44>Abzj%@L@%ZDh(#lKwLsIKCrQytRVv?$p|v7(nGqC(b#tBGS^jNlBUU_mv3z-_oQUXQ!TIM= zmR}oFJV*HUckg;p@4QO0%o(rq3};v%$Z9XKr=CeM-ZEhK3Wf7m>TX>G=ABQ|1`6iL zp8xfvp?KvvR5xA(ClNYKZCV?e0h$Eeng_F$%a!d`YmSIAN@%gLL_QC3?K7EYebreHuI0wl5RDRRxuwOOKH@<&keu!-h`l-Uefj}WtLU6E&ToV`|&?fJI zB&p1(eE6Ul4yWhm2gX!zl6Z+6H_(vO0Ps)E zPmkHIp>IPGIS*t6E-#BqOXEUXntWDVBC?JDV0e7W;Q+M?0j3^PNibLh>IAas`^Ls^ zB=rEg$q2v<_ypr)DmRm4wIcsf^nac)ItZZlzmzK?4|;t6xgruLC87OG%>0d-DE`B- z{XhO?{g}!N(@T=F8ZT(yD8h?ON2@b^jIKogBlAf_utP@Ksv$!_5+lDU%RRW5j$-_7 z_kMoO2lVJ=s`d<29cy|N$NgM3SP+!0EvpUIyBq#M7Topt3IskY5Zwrvt#?sTPR7E9 zw~trvN?Vujhg4Tb4{T7x3L{Y(s1fT37hnEyh&fRk8-`otD&EWe7qDn%`!XyX59SCg zm+$lgcMouQb;N3)UJ=C>T}0>|H9kGTy!o{dcnteT^vuCg+0Rqmzlgz8SUt${{EJcc zyz4VyJZVy@DO)|=y#L+qP-iU|J`aTy?JLSw=UmURW}3YS?Sc=Wza; z3vi(^3vE67+b7n-%{FQC-nN)Z5m*O<3_e|r-Ysc{u4QlU_ior^MfaD)1pNO>hp6Z! zR<88Mwl2Tiq2@+<$)^q}U&Ek7S(|UJe$S}NZahW2yyu6YE_T}24IktmKhkDzfei#nk)%9$RB}-a5lPy-I zzCfcTl0Rbhf^l=tE2Fn>Ma6Wwn}SiO$+uaU@`1e>iqQTjX+U@cOdnGzI$G+=3Ax9X zIQl+pwkcac~5;`Tg0zkz0{ zzh-icv?0N#MQ{H2gT^%0e5l3Og3dJ48)L^o5kFZv#@ODf1VI}59}$vs`y#e+raEAz z5g3)s@#^*h0h>cvP@&*8wDoDxz+kuE#X-i$ur^P^@8CbR_~Z(INq@w6?k6_NsVQ@j zUEWh5>CrbKtw;Z0&f#WOUe7e8cZgC+r#?m*PsTi=_Xv~vo)Sw%@x`AYn9I@o*Stqe z8i*YAyBmX^J`C8DZ@-g$zY`p#d^s}0a~u7=^Gu&5-Y0>ah~nvY7XKbv?<_xI3_^!2 z@0HJ_){#;5N}^%6`PMwm&FRCi3rS-lRyp+s>%BJUCiGOa^H@mVrK2Z*eKoRpD@4Qh zCAXY=i|v8DI`uDZG0~1g#cms_W%2C}h<{ODiSFGEnI};{r^_Xgt8=LM@w%{e&#!485R=9o zX6O?R`^DXM!T(oirm4~fK7ROh?WRiqFY2FTPZ)Irqh1DF@R)cdtc*tamRzrhitj$B z-uOhQ>?)Ub2ODghszh$Q?}BjoM|oiZNxVbzQ1kZh9j}gv4A^&NdP@q)ITXA<{}!{$@o zZ*6;}6-zz$tz44f7PNU`8oDQE*#6>$lDpYzrH24Z{+*5&|C$lyO6lwwo!ZsKGK?-8v3Bf@&h!|2Il7Lmxgh) z(iWSAdxAq{1>TfQ0cYuu*-9abn3>f+@#5;IEUAm zGj!Wi8kLCH7GKAvr9BnTyZDQVj>>(cOv&AHis+(O#mo|^MajDxau1(-eprznpF@pZ zBD9Zd$&HW9BS+y5DDc0)#UYyL=vE*#Ao&lX@|-UB#;^*%mW{|YWlv1W8B*G>FBILQ zR?yY^Vp=%W1m+JINU%@|Jh<-loO(A+(E1K8Rk~~Sppi!N!ltZ96Dm+@Yo#qcYR>&K zx*?s)f;}d{pJg2(2qcE1M|4^(U!Ui{V)Yvvj=~<-CnqeC%W$tB*i#xIA9h|a;w^Oi zqL#irw?x_6A8MO@^7s!^zJ~jYCHf z)gsIs8frqZaQ`k<`|Rh)&=^&dZRcg4I2UgM=dZc9(y{rTO3P|}BFS=hUlDODUAcCx zfXc}FS@CDt7&Of4u#-D)4k(9Br|n746#tfKlA`o(UZ(vfE#DuLF7U_KZ)D1jTu+ zJtcElHa@$#-neRnb}pwcB7s)s;yd{Ow6|PHDd=Adr&z=LlOQEYg%#Dts#%EVV@}F? zVT+-CR_7!A$vYng#sj>$S>3P;HZg-i$z5jC(bJ=6ijF}>2B&i_*Yn7#HCAz zyRqvH_Om4CX5ZZ^B;jszTs(J4L1(LwH;++Qi#W&e4Q|qhut~0<*w?x_1vI;fK zic2NZQ-j|mF%8C>aY-7i{^K!vQ%tRz+PxDvCod|&{9Nef?S&}HR*xjkN1d!vTD!_} z?jdnfgyGl5s+h_g6x=O{CKSi2nQagC!VA0t=RPVvShSu#luTrlkkx8Y?ZuJNDaNCu z&AeWP*By?Z6(?p-?(a+9+p+a}iXi11>kY^Av{J9exd`^{-<3`LtLCrvR7wAwLszY# zQWTJ{w)H%w5s6Td$c;*JeALP)6^liFNk{lvV%2TS?*j1e7UR8yAN?xEXx(;(Z>Z=<+d)#2Nk#4lB@a{ zI@{W4y4rjUBKvi67-IU{#63-2wR2t)XQ5)#OdlwR-QXkrR~#Cczt(!ry=9ZvB_^Y8 z_4vWZNPdY**rz?hoHXoqG$rl35WYvW*|GNxlO8LnWm#k~XNr6+7vZfC;pMcU$DqqK zP#)_qE7Lc#%A&~%ca6Uh4pJ%o%P(S6&N@R`hfnRzH%I*oKP&{%dtkZLl5_ACaW{6# zZ|iMKOf6!>*ZiEy`BqQB=|ttCxO9~?I`Ti#GKi6Y+Cck)IV2M|EUT!6HWrTaTt8o1 zI6JptOIhx`Hu<~=H|;sj&>lzq5w-E7l!5dxl**iVj?6|UNbdkY{Oj2$<1d@Vx13qR zsVuEWN7!4D{&qKLTrVc1X6zlWX=6I;8>Wv>q?PSWzEtUSE2%Q#x8KE}03#wCv}#`0 z@zS$%E54$;!qp4Q)>h{7Fpai2aWQ%Gywzf{2rrAjVft4^q8a^rGzLRShf^&s4^;-K}yX zQ1Oa6e5seiTeo_rEO1)c?G5}_E&KjeW z#tTLByUeJQjF;Tw*P@4J8zmZ%KoN@PF|k##^*HJYUY!=x8>XbH+=~M{6n^%Fx3mne z$3Ocyudm?J(Q#pnOD1ALm1>NWrb$syalinK=j6Wz>)j1zv$utujvjR^W^xt{LTb55 zd^VJ?(`L-;#B@LNT{$#o#F8{t* zCqEix!BnHgb;`tiF6b?3&(4{sq@Lc#_Z`Ymfg)EQfR0i)$LM|@`6LQ;`F_xazdv1nL2~8q zPj|ilUI&GGe8cVD-`}Z|oM-#{QytGm^S?j2x?P?9`xENI&E|7|fBfGI{#l3rUBf@i zpiUxKi2j)Gb+`8La4^^T<|vwot3r}5y%XHsw>~Okpis?hN#sJjymGe*SB=bRQSEo| z?b}vvF@)c{*=#(1=r&A_a@BWt*B~3#U!TygupL@&IJjT zRor2yeD{h}>56%dqLaQg8cMw)!5Kf3TCQ{P!^8LKQ17P`31`FHdvnG?=vuEumuOf_ zOxExmis$pU27|xS{=Y}ye@{K?|DNX5ve?_pkF@82HZ|0sZ`nTVieqtbJhMJu=TXLR zo`*#jV>&50Sv2<|3N=f1M`UG^Xm8Jv@+t}ow7jcP=hv^iA-ADrv^hCoJ`eNdX+p@Z zT=^g0K9OZ-XK$Pph7%^Hu^Lq9%yt!376`_mfNh!h>wswn+|y4zj?M#qb?fgX*2qrGTwVU-Uo6qrVm9ZnQvKr*;qtQ$SM(BO<^UlNXBq)++V1t_Hv!C<- zLX4I`zVpqO$Ylbu9-y==?6zhj$i^#i-~4&Iv@fUQ!5lVpuuiV+AO7427xy8ko|hk6 z!r>WW?=vwehs-W58NtVc*uZ?jEl-{g9eO=MUIrZvEu5^pqT-`68cItH3|>&jIM(c+ z+cti2zNxp`cLmiT2nKUF)>p)cq$x-SUibF3@GM z3QvrB>|>RA9oB;6?Vn3O`SZKzU0gnM<*ea)_YEAm|Ngebc2vJ>*(z%e7Z=UG?ec220wQ)6ey_0`M;m=+nVqlJ1F70JC zy^MSi)x4Iia)61sXKteloBi@ScnY(q{R@A#n5VIzQ^_!{=?*Fg@gQbV15@`0rXGN< z!1FwqL6K-9uYDnmN=7<0wXU&h^yqT)7>s1Q~2%u6ZNdKeh`C*UQE+Sq~v))7T_((FD zOljnVkXRr)axclCe=8&f2=S;tOOlI8{ESv8Q!+Ihgp6li0QoZX73>QoBYbKm4VR~o z1HmmwR;fG`@_EIB;OWZT3oh<4Oxp7r&TG$WsU37zPz+qi_1--~-vc-BmmlbGw?58}>TE@BZ5P68;MYdFWc|g3azFtKzAa($HtF^-- zptPP$P3;#s9_R>#r0H^mX2F|#B5(dkQ1HrVcSwS>qA5HWiX;o#+r~iARsWYawBht> zt;lYPkRq~_EfP7<8r~+sVZ9QWmzVJOVehlED{>!yk^OPV0cTYKTi!h#iB^skci}E2)mU*>QX=TqsJS$ zP^%RD=Oth1Y|pE(oBjM48yB~8dQ^=_UjZV`u4FBponpxX)zl6S4mpauSpkF`hSox` z+@V)7FnV#-+9Sy@?)}cZhC`)p&W{QzM#2>%DjBL|$ORPfAq;^@Ew6H~!Y)F1dz`{y z<1Ukikx>fLdB%aG!h1bfm>hlVP^fhFb%;p7}+wpx14 z#?Jl=hUfOivWXynL}@|xAg1BkSZYWL^zzA2tzJT8Y8a6z5(OyN?(x29Dil(4=3H#O z{!9;92a@F9hpL0nZH`h1*?6n0^jaoYS+f4URbLzj&C10(dGG|nL#56Y zwl$kwG#_@&TURA~x{))i4ZcIBtiMEtWJsQA3ao0T{Z7y|T$W1E{Xs-)UdNeNOGInB9#dh@ zEGZH(wA{gpo~75LtP=@lhKSeJw#fN-^YiiX{kd7KG}cL)Z!VMP?$X`AFF!TqrW5(* z?)h?~6zL2KgYHkYe;zH~iADOtWxw9w2WiFXyxuj?Y!0zCD+RDI&#z))_9&S;eKs7t zKerYyH|cy7MI}pP47+CjNh)n%xjT`#gRcHD;wrqvW@W|gh}zCD=+DdsBfq=MDy-Z2 zxb`{Tf>M;&P1+(X@chscBu9i7-ah^w^TeDHib*P5@6jk=2{uZ`E) ziOSXX{kL!5&aQFd4i4->gEc{+D9hf?l&HEmr9g=oJrn$SnUuPRC`8u(fmia;C{)5hkWGRu)}@kj$o{F!5-~YJa*!Ar6tV zICRg8X4{?-Je@v|3aUXY3|?b}`>6NL-eQXd=Z=J0VQ@8e4i?t4c-$OennpLoMa*^u zrLx&W`0ESh=4_d-6VIDlT6X6sxm`m?k0cwfTFmQu7L%;zeDEAP%`{(mrNz+}Y1#wF zkzB}lgtNo&9+v;$kK_+N1$rYz#E|z(AtVX?KA9Z$9#Y9>2epKsLlGemTJvkrKLgN2 zKBe3u5{_9aCK2anlkthc*%sn-lkrTQ$VeTq+%OQd!uiNJZ&lZ7NxFYI&*pU~g;f|7 zn2lO{VI}naNR8Q9&T}9WI@%|esC7=>T`f(v*zsz2JWyuV{}H5B3e|Fnxhj>ELQ4ay zm-@5hXj_aSfh0C}_Z*L`Vx?-W;zo%+axHV7rfH=kAKSSF71X4r>3?*XW>BH#@)qU2}nN| zeau@D7Z-6ZJuRUN@MJdl9M(ZSsk-=J{RbeCHF$*ujEZro zVPE>k={;0XDD+X4ulEv{dj0y#%IYc@ecb9zfh(VdP_D21sF_f8k40mio=^Vvy z9B^u(U>a29<{#r$B)z#?ojY6@IkaZ8)F*6OJ5Kw(e@U^}UKpw_^2aBO^dqfTg=_f% zS7MX0v@DwMRLQiNB+LRWh(q&HZDK;RztmI^R6z5eQ0{J~d_LdS9#NKQtxE2}ww!(! z5wX?UO6ulJ_0I1Z=5{)I1z%iKX_ydBmhm#!9iOS0u|LphE<9qSU``xy=tFkuu?%;1 zdUvsnZ06hfjYmtwL!?VhWSI3O`9VS5!qA=>nWS`rcuagCe`@!46p^o@nI|TwJ2XQ&v zWz@Muujl;>FRlH>ZJ&FDnrAz&R;+H{yqUN^aO)^O0opej1id#XAfCf2L*GPbBK0FHav_$b?{`Yfr-mk8t{gm<+3PC`m6+sp?O! zT)9$xV$I5IB(DT!t+ylQoc(f6&(@Bk#Clcl^Kx_Tnln!5Drj(WNLX?d$HAR2Z)az} zXQYch>*y%&H&rW59W!V+-t)rg&+@<-CE1(uG3`!_pd!3Q$l_YO@J zPMK^$Qve8?A0B$KLmXDRFAQ!! zk3W9D-R#VG1z;vz9x(!r$hf*UrBqvvF0#EK_N_JoSk zTHa*EYZYR%#wdx9<8**Juc&Wh$`m|sF#tdMetu0@-dXhBEU5-B1={%LSS>pb zvQGL*_9V54&pQ9=M)M~;DuyqI{IrqF{k=Hp5@`61EnLd!FHA;n7T%GJBbHcaoe zeDL$@^TXrT|L*f{omi;Ce)m1*y{DNF1m@+cjQ*;}ntW-=?XZ8S*Z#2_ty5uaaH-0U zn#o9Bt^U1!+FZ;%MuYpD&Snvzw>7)ppTv(XgmtD^Ae?7$ z^z&$|6^GmDaFOQH<;&kk*Ff6{t<_&$9$fFnRkNiAoGTyz6Q3zP&3;GE&_ERBTGZm0 zU<)czz~eGARG9QK84X1qE+ix?mYG#sud=5Lg}zUhOl8oC#O>-y)-}rdESw*Qt-srV zy*s-WKX|9?-cvIgUJ_h6^`9!$`x9gb2jh~7e3uYCI5#y#$6Y%LX{j#YGt#)6acpfx zgB|3S0YBu{xHvc=NHnvz&aH8z&=DOjU2T7fjf115YrSM|1<@TCYZDxtu-O0=wmSFeY8=i~*{@A5LD8%h^o@nmY#& z33=aS+!?pg3|TRN`WM&tm?x4qZ|2A8uC3W2AOQ}wEP`GguGdN-i?cTF+r6{MO~u6Y z>g3SsLup_WQC6<2D+l~7mXA-vD}E4sfu7gnUr9HoEMP%l?c5!m^4fZOB#I4Wkd3;1 zf5H#eJE#Wcg&zzLJKC7XjQKZvhJ$ll2(N+izt2XW2g~pbqg?FpE-+gcjswW=Y zzi+iUEkpRsQ5ftR#G8@7oMFz0QvG|;LQho_6FU2)j3${fvvbf0uP2`C*|qD}B_V(L zJyyc!?(WCcJCeR$xg5+tjz-L;zTxbypGBFBU;A>B4d7X(L~_#Byp@cZS-Mz4O}b`N zXDdurnBd>nNgr%4^pBM%7nx1jcSIG+1DqrQ?)mOoC0Adv;OHyHD*;w7 zgUG-yI7@v0hW+W&boL4B)z6K{cl*bMG7 z&UCOC_{;_o5j0UDdaMIQ`rISW%pz%(_@IJE+8=+@OX~6!FFml1#m1v*6;{+d$%0)2 z5T}qGIl^Rn3g7Sd#k;JRCTk6=NE1&_-R#$^Kd?I=$HJJBv)bpz>a7ncF%aL7*3ZJq zss~y(?jCQ8&RlwZ$Cn=+H@7!i!54Zsq(a|TVQSe|F6pEyFrX5j#VDHA&*5fP$1_%L@qPJjf#MhSVx0~}8lgkz z(a-MqI|ZaAOE9p+ej#pcv-2olQ+KD6LlfuTz0BucV!!fgF@YUhB7Yj6w{GQskFEF4 zAI~tac7pWlmk9(CnQe{&X8^nxYQ*yd&vbn62o1XmLK}y`?myo2OjB*7hOVkIXsDp? z8WJ%q;>#!d4%9BEjwO={&o>a4#C)y*@fgHnpQd3y?95Osz9zJ0advt(yG$@D1DX~G zLVlM1T~58}2D_5Sox{UI4+M6kmIp+!FUG-q>XGv81lu=Z{r&`{%=SA*O6oi9jQ&gp zeH@$b#ov2+8vaOGH(EdH;@+2yW0!$TwRq)nkuTaAUqweBglU3r98RG(Q@3sc#%u6G zv5g4s-c7+Eu+HRhJ31S{I#O(Hw%{_jg zV^lf;?r$PAC5VxcK1br)dM^PYY_@1{p6z$-g(h9NdHH?sNNZ_@eHK5Ha5{2MB49n1 zns7UpP3lJmz~EBizEtA3lcvUTT@XVJWGx{!RzhfkZ&-G|>V-&AvA<})$Lw@?EVmF_#%I!G+I1mss|$TFap z6mr;8hDbC=OKK6U3j^;5$5*>NiR2Lbedl3o7x?upu8H<2?%|4{!`}{(`QVH}W)h5PTVicRy0S`;c;i(>9L1xL{nhc&SutA8&u3kmH%7 z%@`Q3qPH_*Liu^Ac{TWnEXMzbz4r{Na@(Q>w|dlLK#vI&1q>j90tSL2K}AKfl9QkU zk_05@aO9{*khGC3AUO$077;3Ag{_&`~x4c}~eBWAg z%{AtjW6rs|pgOv_!Bs8WLAE20^$_pxZm;17?oIWH3MzY8cSxTO2+GLtCDbLb$yzQc zV!S|Nb;`)Ev*`)I<%5F@>d`kYJI-|kUM)H=RuL{7E+E*(LQfl~a6L@Ty*ToSh=yEE z7#B-m24|KzckEMmiQw}SYBcoRm(iQr%&a2H=hz~Q@5n!<6z|7`gFNd2juXW;2?Rk# zH>YG)hvx6534Ts%?Ue2?&*=1N=DqE>Fz<;I%*CUdw-5OAc#E*c2>=Eziu~D>yeRmQ zS0cbTy<@B`M=o+5)h{Y4s?#CEcXY*{!}+*c2#CL#@h+b7F1V&ow9Jp6cGbm5G*LS= zRh9EBqnkN>Ui5t(Zp-mC8c-9jImspwuyQYN`NvZe=8N02l+!+uKm|1MbNgB5mog_) zaFMdSD|<>}6Qdl4Lx-rLl0swItkN%Kek~3+nG69P`|myY^8Je4Q*A@k2uF-|nRXQv zZRx8O->IRIFWZjsX>mq-mN2|h4Y$ZMV>%#YLU!-HXfbiC@19zE&&r|u3>UMXH!%dN z!g3Dsj=bq$Le%wb;}+&}WDmeO-_KiZGjSdq>v)m6XXml}RGLbEqwEf+)#Xn1n;!&2 z*`-vkhqvu!af>%>;vJM7F5wd{YK76#2Rv^gWTaU;dX0tC5> zWN}s1HH7@pXS6m|baY4^IdXSlx-PY+m*>p4h&$JxU2M7~>pLBHy`3{Ux-U4LUE}@3 zNQRT9Jcf;5X}MMU7u_oP1r({O*^(Cm^_t&g8wEaWzx(c4f>Nrgn}~3CwU70p)g8A| z8N_Gf!l{bMVUnhc1PbKaJ&3oM9bwtHIgX>}tyytR_M7W5;yTpO#hOIZ?!|341l|r5 zy9V<*3bb_LptoD)iTkCOSC1{JPxKMMfKH$*UZLs$^f*n#y}=17NG# z+EsB~X5UQ%#W0W`xIm_nfEvPzXaH5HO|Y;ZT1YPU(vdE?{MRp$O>Si|HycE zSm}#TB%uI=%7&CzM8uY-6(x?GwC*e|=DPU|`)!TaYh*jdnWT40q&JgSR2 z7D!BW*YR&ZJ(fE?ap4G4cxI@CA)Sn@?9jXC_Kb4Yom#8H&<}hZ|0xu(?__qYt#tki z4LW^4+0zV4#cn$CsTw@sNWV^WZ=thpE?gB>W7R^t7WuTFMVr}8Mar%`o)or-lW(8X zj<}LwFH<_|l9^KRG7O;cuQ~O7Y_h+;xk+kg5B=z{xSet+ZL3|h~1VR|sS zNpt+LsHoNzX^M;K+LCA>31&Dml*+AB;ed6J107hOHT)O+p#q9uBafU+Q>ByfzL&?2 zPhc8)YkE-(N66=ke>%-IDY3Ekxu=Izm8xF++?kP;lM~=B(R%y{Ut(qN-Osm~k8u3$ zsi0cq_qMdsukuV)kw248pt;?)ZF$1-t>tVo!NLP^)~CH2mo10tE3$_M6BP>eKmY5L z-Bo=1%a2>{x|xd)t}x6A&YoBlGjPh-TL?QzfZdO7$`wz-8(!Dgu(~kpecdBi z=U}TQLL?8S@fa`-aldX*E-8|XblGksOt~?DDgXD+b8uEc?@Ws{&)q*&A1jr|egDgc z?M$j=+JI1Z}+^K&&<5-r_YGdxO&*{VLNTuS*x<^&3uIsPr<&GLo_~zofkbW0W!GTUT(c+OoLIy_9X}Ef z6s~R0l|U@ucej*7v*3l>zLVN{Ox8Mu^{wx1bCR!vn&vT(zt}R$rSU#Eak->}bL?Rn zlnu>7UvKBz9sBrbr;5tA$hq+TT=RmKF`ENyNx}i@xn`iJX{kTC#@;wB?`XTXgR7u< z#3yg}HoDn2mLC`9JEW(s99A^#@`V?cY0v_3ushXr+8>zUZPWGPr3cr`p7--|@EWzo zNQZW2JT{#fQmC8kt?LgKoDs>;(-N{-YGmSoQtB;~O?GIWb<&Xh?NlZC{=! z6&_5lc`LG5Yo78U`u(K1gxZ}nvtC8OS-qK|uAj>*x8U(wI)!n!EKJonGZ_eH8FXl} z%{Ql=W7}j8c%6MD7i04Bqvx_Bq(j-W=Sq>MXVm}Um5GS0J6DQEvuk*$YxSJtWVs~5 zG{2;z3|Ns|mm{AdxHsOI;}n+(az<4Y!3rQ{ZeeSe?VF#UkMiB=*|u=~-@g(NGac^m znwzZTKqRwHERE@&@Un&C``ltGDz>8l+_RG<1HmO~*e5%>dF5GiT>)5-vksao{#A5QF6rvCqa?m%F5cASPHXgrqwg2d6%;rmSX_{9 zpR3s^{Ms>o&g-eDQ`@gubR4#sYG=7$^dvOxMo0OF3c#Ia_f?mCf zWa!MXk&`p2F<OA3IOf@bgK)*R{gHh-OqDc7gcemy8LKQO;%B&-SfepJ$ z7C*MWx*Ud-hee_%dIMHc552?nSqk!JZbNQ$Xt>N9sZvi4qa!~N?eRD*@g*#LAMG|* z2a_SK;Bh}*BR_IwuCSSM_S7kLktdSbRcrw-Pbw{;_3DqO{Pzo;LXB2u35yua(s8bpGM=H+RX$ zZ`YL&;BRa_@_V4eRFQ#Sz;GLvOz68;6WxVk6T$arI=hi`@EDy%Txs$1bM93pd*l(J z47l~aRHy!HG2ADB=VrBJlCx>eYV++t8F=*4@}2_Sx&_q=oX|5M)4(sx^^E4rf=^*r zf9ryd$gf*6gwvKULQO?W&T00|Tr(98<I30D z*)giMc(@<0vIPSX)ez zs6viH->^sz_EdHiFvLIJ)};Ua!$kJZuV4*RQDMy7{1gGmaVh!-YJWcjt7|vY5J70g z6pj3d`1ljoFh}(8ch8mi{(lP=jUU@h|GtU1F;OBQMX)xvLNDB*wu{%g;n|*kTy%?* zvlvGU2iabziyT$A^Pfc%*h*Fd26bPzI2h8 zA(aX`3ECP(uyvr#6VReYuj0k{%un}m65Jo)FZ@w7(7C)(P1JXWhgZK=V0m%e6iw?O zzP(NqR1bf+5qS;CEIp>{lun;2f5#=ZFqCwSKoSm>^jnA^)0Vjei%i9E2umLnE#eAh z5hHY0

name: root-selfsigned kind: Issuer EOF -
#i%;C$GfF%ewCxL1H48;j^LekRWshKv`a1GVHopa-^16jk1r$oe!fadb zmf5;@Gj&i%HSegzb%%7fDo7IF@Gg#PqN2m#oiVdO%;f!<+F7Agg(S1cBldP;b2eV%m!xJd!e8t=IHH~64`e%PpLO5nOGv7Cx66#9K9 z9X%ORky!u@s1X)6iY%#;it*)r5Ln&wQ-B5CgD)XFNs)l7%;2HJ*nO3U~%a6uO#lG|Wl6YyI zHL~e?B+I*&f!W|nv_Dm$dE7;wE$Cu<;r#i5q*PiqsEh!~>&H4apBu3`P>fg$sqt@t zXUwxKhtZA1t=4UyTGqX5wW&XPxRJW93g)S=4qe>0Ygdro+?_nDn$ICjBew|n*DCc7 z<}pkg)x{+$3F5c)-j1CHwb3as>PSLZ&5d0f`25Tq9Mu6aW6dms+^fz)cOM08+8M^o z6uxIG#5%}6@Mx8Mx|)*=K94X9w`-K{0R{eQRJ)B-Py&;)`bn|Y0>^O3Y64;%1-=-v zoeOJ=KjBWKHso9+i)7XC{4H|NuLS7;;>0Si>K46X-%I-m5TsOv?55?60!|8cR2 zPv;SY57os*#Y!)qDE=$bw3~lnzCU5;TU0V|8MVC{WUH63hDgzq8HJ)JS+(k^-^Be- zC2rhyd;mmO-1!4`2=wZ6OjvF03Vz=x9ch_j;ZQ#>#-YlcZAr&cf^Zc7Y^2k4@E*Cq z=Z+{|D)4&@o19Q)RgvEd$O#g(N)-vqw|hAsbkcDdCrxzf@yE3h^=RW1tG#h;r8^g| zmvy1Y)BdwP=WB%8uDa7XNBDDuOHSIhw6s8@V<2WF*jqK6c)?ugo69=E7sAd3S946w z&2M!Tg!YUIHom$09CX;5jEp+m8^P!L_cSEP@svA~^cfxv_x2V|>zz}o6%YDnzee~D zwOalQ3Nadd$KQJ?K}1+egMd1t*jpN;dGG*;ZNqKrWIxgZtwcNPk9(#2Ml(8)m_{2GHXl8_7$bGqrg7Ye`W zF6X$Oerq-B@h2ygI7RoJ_?}|cD;}?&OFhu}*f(tb`muq+Rq0Ad3~_ZJ3rcjg@mC(# zPAHC7NdlgI!|lu>HZ$B*kBn6HFRdp}o}gW{FTw9ejzpMQS;pPLt#vVjlh zQq(K#8(tj*EW47Dt{=OCBt%ub%w>||cA5vlqZ3Zm5*bO5$9{aNN#X&VvA9Z3$b?Og zYG^UwGN+CxrKlUUzmI*-jS zLpxCO_XGYsyb;K}0iM+;DGBY$++%mlT&*^r9no=InYZS*`4t%5Y~0eBe=%OS#_9m? zAv%h8{;z;-l*6J14MSq!oGuYHDE@ODV59E)M_()+2Mt3j&WRs)-c^bz8j?yNRT_)4 z$ZuWUlx?PyAX{7?BVp*9BqD8!bA)8b)g&d8jE$d?)%g72(~4++Ldvw0P+^Bint!M% zHKR4hB;vq1r+T~!NmN-5p1lfUP9C#jv(IQ@?u@%5D)7wNALva>Q@hv#GLfibd+>?_`qe1!C8$i@gm> zGi9iK-u41NeTM3NoPmb)USDc-qC(|CGs(jzPaxP+1#Jt(Dm;u5ai6hOb=!AdebR=M zS*+CJWh{p;^FFqp5!Ri9DcMFL5QJ9uKn|y}57{8fYAWa!)wjRQmluW^9cLPjA{-Qf z)F&T$pvaG|nsQ(ZbZuxL#Zf>;;M~&rONvOdh!Bo%8mKy%q=|d7tUQ2@5|6Y zaO+mzOTN9B2h>A^mI0c^5n#Gg)^D5)y3L5%0ix|Zyo*SbbZY|Y3mmuM2CqRJoJ52~ zV;#a@w{b1WY}hn?6Zz5m0b3|HSczW(m5o%F>f^`7ob;1DzBACrrY50**>NQ-yfF#4 zZd@;LFkbjo#8;nhuO=0~;!5NjL}~?LMLbFPEYB#Bf(WZ1ipx`?C&$e=46>C#FKV>g zK8fKduZ4K0<>0lCOj-NL$FebQ1{E*7h>pOQ)qc1>VH@&E9!PS<7C2r^kYnwpb%IvY zi(p95g#6AcZp;Z#UXf%wl%2ZDZ`M(~Bi26375uV3QuGTmP5Hs-oP}r*+|8WoyJnLWs z*LIi+rirLuAPk8scIO)^WojDwJz+l7VgHY6rrztzu6BuQ+QcoC3mV8~l2hQ%(H0F! zKfy&tZL5ROyJcM3N%^psJfXl*U0qfps=~RdeY7zNO@WRRd5-hagqMbGObikrk&x#H z-#$+1IZsX&P%O!VBCRe)k_7Qzk-nN2AYHWt+Xu^?>b^piilVV2MpCbLRFG4nsiZm0JnP{Pe!>dY z2+m!N^fLVVDr0111hKw3MgnBe^S+MZw@jp05u)s8KYjW}cZnkhs;@wml)=APaY%ld zpDG9zv`@oX;$pK}aDmk);fjT_@O0t>3Z<6lYbmbW2>iqBhRztZW=d!}=Sz~>hBtTR zh$-eT8t;T&jIVRHac88dQzZ02`h&GxnK47MHJUjPJ81NXxRL428xXp`u&}T$jeH- z!s}etb=T*Nv+|q%e9xS4w}^8Kqi)*ON~kb|=CxxF#B8|qL@j>-BY$R<-k+A1ecuN0 zM6l4ZXqJiaC6ctw&kucPX&7ZW+!wD|SWQ;;)Zb)fCBF|0yfW@+Md+kU^6p3iDuQ?I z8JzbC<}(SrVg2$!Q4W!@5P=V@G*DiU5?zQb)t}TJ*Ur($+5QO>I=?6)HS zy{rI%rx2;U`fwEjA96WCtr~^DQifV4q(E|v&OF^*Tphy<`JD=W1Jn)w z-QE=K^)&Pr(hU!j-@X=6T3#}S=2GQ=G6R+vbi5wwL#Que4Z|%LpU|v&g~Ostp`VtbZwY7=0`q7GN=l=Dtm5s`r?cDXx{s^8& z-baS}=AMSDCH(tOPbYI{YX9KWOZ%ERr}2Jux{1LH{cRVH-CIYwL(A(A%fDpxa9A~R z_s_c7ofVsVO=n+x)03<-99!MMd*;k7(K*eA;P4#@=*KDZavkLhAk1*^MRQ+uhHVn! z3;ikv?ZOhABja(EJ{JqMi`lkPDBE6<4P#j1Z;B653R{{nN^+X5KTvJtS8XI^Gh|CS z+}*2~Bpxm_b4ud4a7Vaps%MVBTuPkhI?AQsTj0{XJyAK&{^uUPOsI_I{I;$OtD=f6T%%DG59!rIrDf3M&T z$=B&)TVeg=>%;RC*t4~-|NVpiUoOu8Ms)mA`i{5bj9cDJmL+{(KC` zNAX@Kw4nFT^XpOV?UM!*KePNjv2ka2t!lrcGsOubLM_pQo`9Ng;p~;$xj~p3gIK_ZE5ACzWZYY;F#Ve)y#2mv9 zN>)_VlApwvi|!U({Fcl1T?YZtjr+rR#en;MCnGEK1#M& z@Tuh=k3601F;!|w-`DWgR00J9Luuhfi3$>ypsB>DUNV|3t*t}#373-9a>Q9>7@Mj> z?wVOuC+udah}W;bLPHX=@v-yux@)j1+Q1IQe8;c94WwGCxn{seNL&%Td!sLh~3uozX`Z?aZ+RlT-9+>6I=-)a>zdJl>A3}3u@>y}UX z{dW!*hl}Nx^&pOrCLGWofeV?BgdWOtl>NDzc5%frw{q2F}-n3nvM$ zC?`Fk2^sFN7JiYOJbJAEMRa%R&S5l32A9&$*g7}Y6<{$vaG67G7U0_s{dB)}GYfi# zA=Y@-R~2$R>YE-t0w#rxo5Sm&6^KNa-WwkDS>6`oi@(ZQqZk+3Q`P6y7U#xmHJy#> z1JC%LTMBeu(D%`|Y;X=&dpTlnIo4h(Uj}CUItE&-tftM~{CoXlZquKOz-IDpwsai9 z98up+@a4Q`TcH;iom(1ea7H~^k>!HLSCP~Lr{zTELG<7Tcu5#)GI{+iN?76yA8te& za^?=-e*8q^Yjau=(BCmJVsVKIhU_MtOK10Qcl-Bk)Hb>+$P?V^$Um6;`SWM({#+b9 zXivQ%;xx8P(JnvBu23OSU3~$`#0YQiI(|#`n_ISQaeI~9UA{Z zCO!NSNotjT6xmyLUk^m03^FmMilQPpShrTmc7+EIUWeJIcvtu*p8%VJU6p_MD}O!e zh?7;{e7`s|dhxu7SMI%H%sd>9#UJEerdNu;d5xEMC*9wTjg94f|GK$-9rz|e<1fr%ezzHc!q`=3hS0sghuBCP)Sx$AY$?3%D>Fz-@b_{BsI!MT)75!mpU+j z+GgwcM~^%QQP1nOw2Ug>FYe@yh)!~G*%*6v%unfEg7a!bx&J|OfU}0VE?l@EoXG_a z3}(ycx0jJI3{^Al@%+EOHvjsi*81kIhq}61T%h5%$QE%)$$FP{A`JzOP0Ru1K*}N6 zqo^-=XDN#6!CS>{ev(!xsMa#T3_|EvV}w`RU%|6LXky1!P{_=oA)v&`nL;lN?Z-)_ zGvx)iR_a;9pD(P&c!$g^ju*zOWMuEGGR3P4Ek4Lh2t+a}{GElZ!j2tcN|WvR_95ik zaO{{!`2g}8xqa;Nwhv0`1Np|)Fg(~`dZ32taNT;GXjVNMJsH%rx#?$?=jx!D(ZO6u zgWtbQ5e_$s^{M1p#UQKoh-UsBs|ew9 zPV{ob&=UAc)KhRk(wqlsf@jqJm;DR+!3n{xcb2{h&mq-MEb&-8+EQ~GkP9PLc|CA% z`4=O@jx)4zBZHqsYV%N=b=k2R?sV|IV2*Fi3~>o$n#>I8#8&R^=$Hb1c%j^XFQm@e zmLts~7|SeuM(N6xN1Jvyh%#K9br=Yqos9Z+@9%uH7USHUZ!*?yBB^VQy6#{I0hc50iBnO#7>6G6 zA)VIP+JuTg`W-tiOb^D@F)chlbm$NS>w0x8?=k4RF-C%p$0B}QMCTeU9MW>)2f#VWbn~rxhGnlZ;%GMnn-4#`T!)ttgxyyuFpEq}~+~1UncF!=;eOV9Y z&4SOaC$OG0jHzwF6shXnIJ)WScIiw!ipi=5t&H3Qw{zdcI?SpW>^*VwCT9HE4%v<+ zspme?|M3Vm8Z#vOK{abAK{m#~s~y!y2lsKR%Tp~zTleAmWi4O9;Ky&XqaiFECm%XE zXh=;g8#Eces*TBK?l@VxV!tzo6%1#9DCf)X7>VPyZ&k45=ruPMwUQ2{oUy_=kv!)u z!>(s5&j{;%aJf5pR>sbuW_;Djm(ekZ15VL_z?4hxD#8n2McW5(q1NwH5^RMrWauZ7U%~H-shsAIko;3H${mtR*KR2-FiDW@rE1%JO_2LLb`(E8+L|@9>a`+xas1=C z85pDQ%Oh5W6g$<8Tcg!)prkn{dmHWajhW_wf%vDhy8c=PT*+NgqGGAzwna~>vTP;} z6_=KhA+sP+vWDs>P(Z{L?=a8qCE&Y@LQ$qAu#|V?|4Ss*`HiHhbxnYAE~BMM@+uDSR}7ewoR=G3d9;D8JhY8N zO6%xRW7Zby;YOOe*?B1k{ zcO1CkDc~*@5=tGalZgWd(M49ZZbj4iwolLfm7Sea zLxpoUEA{N>jQKjeUOO(Pu=$(T=4W`-{;ZAJrcXcBC2;_rI3$}({d`F5Ty26eH%fE;r3i>XatBMM^Ib22kGXBxmQyg5X|ojZ!TX1N5Dp5u6kd8qMq zajW^Jg#k(|Dk>d|yLv}jjM`5`Ck#Sr! zvR=f*l&^k^WRYx}n3#w)5*1}_c&#Es#SPdyzM9{1wu9>i^I&V%Nj6WKKb5`v=9^MA zLwlU;R`=0b-Ntk-AB%CxICxdo7R_8Un%sAN{>%iBR);#rUJz0A-Br5uy&?rU73sU$ zC1>X5+Uw5EcbOm(Kl`6U#I#}HgetAokIr*rw;_i6I{mva;K{=8_Xett<}*XRswmiW zn<_3Y9z-&U%~Q-{v-txIF2eNqa?iFjEq9oY%+yj-%5BIxG+FYvjvTqH=`45|cex*P zLrTZtavhJ_{A)BY1}!@Q%OM+7KQETs$DkR*P}^7{wQy4h~dVi@uPue@AbKNect^gKEh=OG`ELJs{ZF49}icI&*jXw4{A zJKFXG8XC+%UA(orWUfMb-84Mi`pB%8Jy9*k2{g1P^B{ruo%wCZaBcSE9A#l;)rFI1 zXJ%H$aFDN0XbeI8-Qh8ApG=r+N>o6v8)>BX>x0h4kx;{N;eEu z;QMuQvY+s(If9paiT&{PZxmBC2<$5>jZ1}VW5HE|m~~;IG$_`r3`xEXn+tqL4sjK> zD?Hsf-Z0kDX3<}5ls_dcjL1c!v$IP6y$u^&F4(NRcTUT0k6c|Lpy3^Doe3cD^nHGa zP(!~K+o0nY@W$u%B~JA@CQbF%l+|Tir7ApB%e1*r9+Q0Fh+siKs-?fxCv;DL+Qv2X zn2tUkHi~%D1>h82t`dm*A{fRDLweOw=_&Svk&Z&K5g;eRdkk?vir>j)sTfSW@%Hoc z+r4`?^|}36?iKnoKclq!2U^Tu-P6)S9i)R8slL@~%N9P%Z4G2j4-f!uI$shmSJ!4T z=Vvi)rid_RuybNN!%z}1W^{u0!3$=Q*F7CXuu8R{D=|DH22lv}+?kBDG{`E#QI-Cv zy!tM_y;CSK8Z{g@Fv6R7sm9k^zQW(j*fj@f*fE)B8Dz=lS#3Z*GLAfr)o8`jlK0cH zA=|lnqNnG>y7KLn@QX5L*I#*fREg*&qK*j~ooB#-jtk!@l9UOva;73iN7eK3u|z6Jf;4a#wA%*twowg+ZTjePpNXX6O?hw73nL zZLbQ>@&lsltqKt?pPH*YFH`d~m|On|j4r${S5Uyspf&Sx?BlJ^uWM1yrpPClElgJ0 zqTs{IZR$K8sy<$3`o~i_1Pzb(mLj;+Ojfo03SXsP9TF7Xf9Bx!O{jNN5qY|RNJ+KA zx3nsAYeKHz=-hjSq#cQh$^JlnRDsny2XwWxqWCS}^BA>$Ds{>M$6~vGclbU1gx*R+ z^o+4mXLtSExJI@*tmD=4qHcn6T1nz9E$^6a%7=6M-=hEMcz-nOkn&Yd+tE`Dta6BF zWIJ5Y;-*GVh~kb2_PXJ^whB-;A-!ux>rK-KMCHxCa##DV)xDJ!!``1|GOJHG4p z!tIKy{`T9yBAc7{_g0S{$9jm-M&0mmTIN5&J^h{l_*D=~QOqchzPWMpX1JHru=)h; z@^thI=-H!_k1z|-&=A}XlucAuksZ0Yy?$}J6QW`I0S!7$T28g&wlIn6(2G&1dWMoK z7PAsx9D5oy+3zeJBVk|Oav|=3*U_oP`J}%(JZ^kV>Q3%`QCX``p-?fb#DGCaKr==n zN3pjepM#;_hf}>du_LeWme0{DL}mZ5hCP))ir~_dhZYv{^OJtiGxk~p)l^m8h7XIY zR8P2zmEO89F&-0^Yv&Lvtn~$W%zogk^T$Vw?8;w{-R?89tV*WSusL(PuYw&HlJMYM zdr8T4tkJD%SQM%@)fvija$RN?p$9Z&G0Xm1OL%#_~-r4BqE_4h(|;Q z-aj+ip0AToe7_UW2j}nxD52W?J;|>~J|WM&_VxceiuFHM@>1&v&MQEs4#HkgSi%#5 z(+|h0z2HA~om;qEC(Pt7=`jWt zQ4|Yv_wZ;yV;_s2GN;G?ylezTMHr4lhs;jMFj}MH_7;llr zSbJ`Ad+t=c%|tCcN-~ZLyBN9Ho|pUq&MqFDosd}yp-A7`h5}`zV3x~CkPZNHRCjks zitMEo5(OZp)bI`oS(I}oHpLb}DEk|azkfdrnbo3jO(=+u@~0(`I$jhzLI*|?lm%{# zppo_fuBSuIX(C(yRED&qZ4i(tAT+e&SjH(;ZZ)td2r$km9Km=5nml=p~^EAW20-Ch7 zY4;PZ<@2*+Spb5u{Rq?&)N{qtaj}5{%W__oPFC6T>d=KlOd&{jip8F-uBbpPsGtNJ z0fYJ)?(%k?)ivM*!iD3MgfC>^?QGVNs*5~`mJ~48Ne~U|O%LQq*5ohG29_2yLXw!Q zW{u8k3=rRxYnP6o!JeQjX zEE0zO)RuS@vV#Yyi*r8Cl`0Mq@PNn^<{Z_^mk=qW>(1kX(<=XfSj3q4WQhIZ0y#k7 zLIJ@dhK=$PTKpmt7FO)bU=Km>)9NyqRoS1U=@P~JBW|gLBO=#@(x3ZANt@9Y==-18rfb9UmqzsBe##9KdVfxaVX^6?Es25pm z8JXyQn*O=h7knn6$OcUdolMG0j?cTFe*OA2_rvcOV@rWcu>lKFo|Wyly2vbSBJKlnO7RdTFCS*&7X@M1#Kcb z6O8}fyLp+hQYtIlRz*Qa{6|C{c4c8^hE9b|HQ&xQScC51!H~<(YHLC-=18^KPQHyB;@Ro{ z9A|>Hh3>%oILm89RRUHir5Snpph)Rj?{tu{7rmFMP%*F8d3P8IMtE zvEVxk$FoRn zys3BM9AYvX*|q!kzyN@W+<^e7#No}<>pszxmmBEn3BC;;>gN2IKfWT^Kb9|~OLg@0 zT&L{K6ECWl?a7wLZr-LnkJEJ9jcC+P+Znm}mjy zW3)+kE_=x{pi$Nb-|tTj;EfOHjdza5eu6%L#J5z*lh5z(8ylOKYzyFwG5+|^=J7pq zUG_aN?dD9!vGxOk(gr0(-T#M~F;ERpj+Jfmr-k{y;`R`lbPi6Lu z?=h{^P)YXC!N1;oFLzbBG1hJ?8KVk6?zA`>x3|=!`Avy@>%LP15tvEFycWCG{rn4F zFSeMDx(DFWuxVw9dqPS;aJ33^VZ%AiVHg5i*8l=NX66NFyFT(1vSO*<%ykR*fd`-; zl_5u~lr~v?Fr0B$Rm$y)Om(xL9%mq1ax=XfG}yQIwq3_$|&(T4G)}@C>MtgQtDD#2q6sl4PS%zgT_TxUOpQ030X9|jttfH(;$ba3WeTQ z8o|^oL+3{15_OD>l1XrpzEw~0Yy2&9VxP04Z3%hSQP5`^4t>*dq370PM+)ej?bMTO zqqgYzsbxvseVFBT8HrhOtAg24M<|cN`OSJ<2sD7YkB|Z2T-|UK8j9VIphSrVA*_6* ze=0CU185-o_;z3PWSuMPTD3=UzX(bKK!Et6lPDYV9;9j?xjOy2*e+^2Ukc)j15=j) zD3_T2Tl<-DVR_hZ3NkVOrAW$|7cW|m8L{`6^_qQqb$NWqolA&nG5$Oms=bqgoxQK; z#zk~b@8`CNPsOYG^lrT%M$a>}2MxH|(3Ha)w>V{~&r-K7Ydzpy-~KxMYKf2!Rtfg2YrDRO)ob>_&h zmWL%bCp8_0Zr35v6DRAt*cuXyz6B%?_53{;Nh)Zm0c6xH<}m(}r(u<(gUxy#;s)0| zp6k`Ve73qS<87yn)<22zwi@6DPFJ3Z=F{zeiE_dNP_q}KE-(U@zaV!>dAZ7H+hV+2 zTxW&PF;TZE61x)?YpSm+%e0#wu?^eNpPuTs{{wBE@6jF1C?54HaHfHMtEpf<5dyjM zTj-fkC09ARD_*drh6!#S0$(gp{sUKN^cJ%6ANHJ3f2&9JyTH^B{1h+EV1ndC9q4O> zU$@5E(VOQ~nD$*)k~XzU$ugXat_-XfA619F4b{eYp*%1KaNr~&FJxhWGot&$rhc!o z#OW_iU@-1|v?*vgHWz-8?m;W3@|#~Gi}~-_kuFC0ZEcJ@0V1D1rLZ0EOF>fg1*=yc z$<;Z`Z;fUHWALGls7@%S_Z&QUkdSzAPiYn}w(B5xC&UXZ@xY1(E>v!#05-WJ#c61b zs~B-*XMh!sR9^`@dN@0B@bZ+i48ny8 z=~-EF-6b3Z;4m+e<|Cy{hUh1#L;_HYaUn;z%2T`c;g|jc{N96CuU{v)S+&5S0EbiI z&7HeQNS?rEaH^j-WdF%bf=#>8EbCK&wD>KfwJ^bZum|Bcd?m>n+5Aq{Rx-*0b>~jY zm84mi)ARlNoxfiYXRsD+P=YWrG8I)FVW4xvtC3X3r7}1>`Vhto_C*O*=8A|WF}FG? zCW_o_2gp`Z&JxmAyjdm!%fRf#xk4)Zs?!9O5NVIZ3{s5hKm$iYBD=}X5&vHX|#A66N9fAkiL zD}j$3D)D%D?OKN)GgKguX!O_W7*oo`Nj=!FUw?{GLqn4x9`W&&^Rx)*YURI0J<`z! zX;v07u^XF{7X+7$R9qij>FMvsMBW`Ys4qhqdb{hc5TR~H#*AFePKt`ul`F=OW8Wg8 z(ufEpW--#bcy)!L;qAn>v-?lfLA$+&JSZ*g-o24Q=K`Q$bES^$GllX+es0VN#T9x# zEGTy_z?2QzvLm4Oy#=i(`qEbJJLG@@J||F_g4}m5u|i%Mat{Li-kXqZ+&S|1-@4r9 zeY;27ZI!7n&c*pc%9w22p&B#HqC)X*fMF+D4sSAONiS$j=}62qR~i^;xrv6CF{p5f zU0J?}E^Y>u6+?qHikm1O4w3ggo$=wrIaCHbvl@M;^cjOiU*qEv43*r_-ufA+MR2IE zXgP+4=elyxCwJf0ExF8^U8%Sf%%D5Hh4v;9fspX6VT{1?Z&7jF=COsdBNy0-(p(X5 zTAyVof!OyB-qzbvSw$t`n)?F0c}inySHuPL^%(!k0_d5AQj8RkPw}=>c6-gilaSoH z%XX{j0XC`d0dZ*mlb;QG}sd-?5o<+Vzk z4B7isvmJcMstuA=^S+FczbPsi{?8^7r;luEE?1$t50|Uzwao&^u{PQD=D|>Ok;CzDp?kbZgC=h{dWYA6heI z?YXgI!58W@OFcvVNjw}`;Hdenl8es=O4Y}8>po-@AyH!5O;-=oYE9QcL&GIvBYTej z#yeBUE{wMEWm`ZEe(pJWwzy2|@iX)D^TQBlQkkvi+J_pFLisIho*msmIrXJ&(bm>p z7=it)9)5e{#>dQTeX2RL{vI^CUm#;#TX$BAft%j7 zJsOJlMzm48P3qMBIqYXTtR=`|M2R{l!@^-eZ!buIjRyl!()SPfyw&s8W7MT3e~8~gsm!c#hlX0SV$kXC zq^_>$wm6)ty(dqeQB|sx#YQj)^ZjjxaF&ACHOMlPJlsA7JJZP9@ z(xsfNW^dSAAqBbgV-y_6@OA#a(U541s$=v@VZXcfkwkX->kC9IQMOY~wUfPtCwY0L zd3hU$zD5L*Vw$qCXq`R6uZ>D*tiH z;cXi_Z1O?TRvnZA$?`ByG^G2Q6m0`zYqS@A z0uib5*jH``4Si1(=WSJkv3APFKVCO+6+UQuXW3StYi>R>ICWN6bpu6~-1%j*po$>n z;U&kiHYSN0y>*}XVE){K={OUxV`b>YfG0c0lVD91I1_~{3jws8>dENZgzEKHs3R3C ze2PBSo`E#}FSv}uYUm~GLbjwXu1122qPaxAt`XAGR`vV=%Hg~dH$U9&E}>CGxXR@8<BJq+=sfHpW~FJ+%2*zugiE9b8J236gh0(F~y~$0tsn#1ram;6a(isRzw(c1+M zCcX8<*ME%=1d0{f#tJOKW@u;#9$B74wY~|xrJ{@7ofTloVCzT+7q@p-M#LZ`O8=k) zk^tgx<(BONQ8L1zG3Xd4PDMADlv`06s0rP&_GvoO(igaG9$MoQCF^22YGGq2I61oU zkEN84u6Y^1a0HGKqg5gDz8Um#M`$B0CW|&Xr~Wp z7@$=0uVj@elG@;S`4`1ogDhB31>$RHyPCiNYzP%R$Sza!XkjeRG?;%zkh5(aMPLtX z13hfHHvZv|j2bVf{~mfA)rNs>HYdX1{i~6xHEXEkE-cC zdE1b8P_8-w-EbpR7Kg z9L$rHgo7a!x{%Lr?Ieok-8r~0F43DJ4f|n)xL9025USCWgVewiw`u2Ciq$B6`JL0` z4^o2s^X!eKUY?f><>zT)4R?sk&=ui%`29SZ+DMESEGAC&TW45YrCeesLF0$l;7d}K zG;Ae=SKf*vr%Mb%F*!J8cJv^5LvL|~#20>rtMbG!d%p}u3$HXM?_hX>+HB3Y&-YQ% zDL8~KD(Zz!a_Ag@l5bxquW3M-T$c!Nir95*p`x7aDRKPXy##cc_xGV(RxN+YOpAmE zAttoAw&(WlFEe;e@eU$z<17*4gSm+#f27hsAL+q_iSEt7yj$2+Gm(WQAC>#I=SAxH zVPg2Cya#PvUv=h-4o=ndgGg?z1rJqD!foxq@873}8VsY4%Y-GBmFdCrUlLxP_5hZ? zGpGki%|#6jmQ0%oZ5qb*+g4WDSj3<}0MvIZ&ijt#U&g$rBN+tK>1v8iS4s$gem`lW%Zs-C(%f+EH zOwrDhI;Z!e)|tz2elqn$H>v(W8%1LA=x-qfgk4WSRmDjY{g!k~WJ)q&05*MOeQJx5 z!9aT^K2Too!O~(Ji8$uiu-Q#j8AE!Rg&(AZi2(teJpA1ddc?@c$T8?ubICG1a;;J0 zr>6+iks~K#gir{5hjTKmMinsv;p^h~99*OfhcO!WC9{pub3!k z&DHI(xq-Gug6PgCRU0Qa|ARiJ-KBSiU_;E$_dTT(5yv6c@6q;blR{A@l z#Za&cUY&9y#-QOn`tApi;|_KNDw#RdtWIG5#ux17_aKr4xse6$A1@JLje1Icgsub0 zId(&fnkSus4?f9`!yxs*%ssJ3_7`${>rbG%K1UwE+pd1#z5ZJH==@IO&NY zQu?rQZw_KKjL>Hvo18e1(p~E6$7jM=6?*Zq>!$g#l9E{-fmw<+je2}L*&wO9MU4PYwK|q(}awx!j^p&ru9?* z`6CMA z^x%>0Do8COUJx7o#sxU6qRZJ0^{okL)_`duw@{Pd8>6-El0VrVZ|3EdMJrr8*U?4& zW|o1*dlq;L36!%gYJsxa`Fd(s%dGa!KXjUAkZt<<5tCoYpW0{p9I7O1)BJt z!3P)5$*IMgx#oFmzg@-*Z%(dXwEeL+Gc&8N@C%u4R0}`B?zUL5?t?FUlZ&o0HtYKH z-|Ah?t0)$IFw>wE#r^OOor=mt6IQ@R*>zJ5B&|@PuMJ6e{XI8QRCcWX zT<@Y=kBtDxdW}9gQMkT&bgN4t;7Y^`B5toPigFM21?pYO?m+ zWZMKpkS&a9(qu}#Xnhn-GWHQ$#GHq83lzn8KL+6)0QoM6v{2$I8 z#sc7=rokHV2Nk3OPuJJ_;6-_-CM%`TA6`?e_VcqY{pE#GqLB!q!&7RT({U~(uiGF* z7MWN(lJG|WB_&G1Pqy3);Dl2CCY@OAz_%{FN`Jw%-y=Mewia3lpc#smCDN0)A*3~( zF35sv|L-BwaCj3%y?DXW!?`7(yvQZy*t$!xA~466dYIx&t05EnsS$rPtc)=!oPkOx zbZt>}KfJk{PofzX=KaV;hYy25kq}+)BKa7bnH~|sSHyGYgYxa%-LdI9?wFBs=G1qf zvoAx4Y!Y-T>Gm-(r5+JEJ~Z%6&fgPnxQRUJIY4=O{%N_}?|*xtr2?~XVVar6m%G?y z;CWndA*dzbCp+kUWRGR9c;MQT67SgZB~#y>gsPmJ)gY{%zYGmM#jpi=rzr3@NHm*( z68{I@Y`W84ZZA{#9em;)Qyds?kCA56n`o=#&ny)tppFTGD(82>(i;&07*@R41){2P zq8l~Bj%oN?F2&fpOT4Bt27yqeo)jZz8{M@h*JLaY{|9^T8B|r;b`2g=Ynx~TCXxy$ zK|n>42q-ilIfEdf4S?hfl2KbxNrE7fqvRwxgQ6fHAmIR#l_WWX9 z_0?B1@2RJrqUCV*+4sKhE3RuTIf8{?Jj;$zV$YGa_ch6g#E2{GBZ9x5(+%JhdIPQj z%we+=H%xl*x7P{EmUNw+5`O7ZZC!Hv%Q?M>m3wfU^A*IwJW+*g^k=5OtNW2|3YKZ^ zIB!N)k{X9uVMJ)-y=-d+QpT2Vj6E_c`U9Jns9(Wgt+lf*C+jHH@DpkO8bAo$n1uD3~jp)-k*5om@D%DYS8AujtjQ z<@;p2IG1X)4D}3xbo-N%*=x_4BIfgu~o(fkB)WttoL=N&sLBd-~wE=Q8gE1Ieg$}N0f=Sy;%o6ft8=eoAO#4Vu+ZLDr8 z$l|%Z1QUaD0IU0tpEI18y72t9E|FLl#x7s_vy_Ld6arXwuwE&CPW*yoUWi+k@B=00 z#L#t8IUgR*)j#tr`q?EE<7IgY_zn_}5rk_63?SVQFvT$seT41dZyv&}dp4TS3=`KR z$s%r(V96Sn3kGj*bW!gk@zP?yF@1wSJt={y)HfmY#D%=ReAx}p`9x8v8CZ!wiKn}t zc)F4qbHjH6LY>ra-BKBtnj-cHiKH24&fB%kUG@l{SwEG0{v)st5Lt-_f0sIA@TOde~8UP>T>|w#tVjzA2hA4 z7)X6L@Hiv46Z?*2dN(TS>yCmwM~O>l{rXc%x`>3&pP}~gDljlG{`ZO(u&zSJlyoPH0$@l*+fAi#kB}tMM`$1lqkx|Q!VR?16@Yl0hV$$i6YYHgd zMUvd}>({z>Uo%SR*4!`p{p(NFk3J7c|8Jkkgg24;#D*s(7)K%{rYt9l(jjf+dDdf z`NY0{7lAv`9Ul~dJ8#JIs=n*TVA6fLm)T;ATNWNNiKkYA!Q-_4e3!t|yoEJkS=jY2 z27&qEx&P5YP+OK39HQSw8vX};%lx3Svh>(!IfaGU*`^quokmsDY@kFy_P8Wjf7^Fsed?O5 z_uk)UhWKg`lSNax!i47vHg7_hOLe8{Y9$`asUhsONQ{B>aZ; z{Wg+2vAGz_zeME$-hJ`0#;5kqPN|9~UwfWGC$P`KYAiJj4CQZxOJn^(i2p^@cZ*y9 zubqFAm-o|sR!#Y>!CAIleeawPA5r}n@D0y~Hw?QG@BL(Yfn8spY&+4eMOnV!L7eNq zD6W_nt*txnp=QJq4~&c?iqjtd@%r*95OGh<#h?@&MQ_^m4C0@wepyZF$Gab_ES2p%K78h- zgk86d2XdV8$^>FT;-!NwI!qS0GV50+xx2fAx2>i=i6ps?*zNwxM;j!9f+oGU@+93X z?2d~l>y69Kwzj|j+FHD~=&)5~x576u-HYmn6}dLMUAJgPu%Ff4+~$8*W;93TrGtsh z&}VyzCTF{SjC8`!hQHlXFsGb7(++#dRQ_Hr%bNMr)TFGNk-tLk86~eQ%FVM9%1^)1 zo_RUnvRBT|eM|X+#gv?TwVCJeb2~Y==3b)YqQ$^LX3Ia9|B}qj9E`oy{3+xgx*dGw z-fn-H1zovcv8Aul-~3c}_bHP3X*waffQ3O0Z<2ZHxg*RMo)rK27#GfnsoF*!q$)3C zA(>uzbm2dgcEG zqw?!j{uuW0_mAv<;~@TenV-T#`t@?#gB|?)N6n|$m%mEzg0d`il> zH*s;-{b2GzP?-pjGFZLz5*-%I)!cVS{zYuRG=7*e48G(hsqAS(!>b#?3(0Nl-+z^r zr+U^2^fwp8rvwEB{bF4=_)@_ZF*Y1grlP9)>ihTGmuyCFpcJWT@8D3#XGl^btaLtm zwwY@dl7YCD1uCe`t<28MG=9y}CobkjCA=`oMNaQMX)|VPRJsEfO&*)6iU94X%)-i8 zKZAt0j~IA2RKVAGM9qCq`Ff-Vi*i+ zU)dZUa63qxkw5_J{Z-R9L@I!=DsR>Mb9Z_I>v(|D+AYNj|j)r_R(36gbO+0-O!81YT&fAq&k3=I|O z)qIKasjg+++?*E|n`}Y32@c;BBfiX>oJt_OLIe7-!{T^6#ADmnN}+_~$aqF?>b^s- zvrkQD=S={y0n7I|jJ^E!?a{jzgDyG!yq7#o1UdsvZ|(w*u85}BHH_$?EdS^1+?*OY z=@ynO%d|k(?ZS==JmSM5iW{!BbeFT`F!me`tm5eRk2jWA1J!_7w;oH~X5B0^v3uQF z+elDZ-f73+5di&J4lIAE^fEe3nIq>120xw0?rq9WYof&ca+_%zt({w$FA(v$a`C;M zPFu7}@}k(aNx=POizwW_V;7Z=eAden++1@8A)bz2*4I%WRXx3Fy$FydRLSn3vZ4i$ z{#(QT{{EQH(pf|G>zaO$q^(`}=57U8d4bbZ9@^w}p?oTS1gOVHDBQ?KfN?OhbXo5e z195;0ZHh$AN*2KVtgTOG=U#jm9lifYn~v+3J3d}4ExS^e=gL|``68Iv*w}ifK*t`K zWIM47`UOTwTKuPb1=GCK#eGUqTWJHMa^@A2=BB3kW;rXb?hlr0^UZn7ivo8)(c2&V zg7E=-2~kYFH8gL$e{4nAfyaA>AuqH6XgA0q*h0*D(f)meb{e}x;p(J2@i8dTMX+#k zHYaI0t$lP7q99#0$h%uDjJoFoKfi;irL;$n#sEHubR-9y(qAF@&TRi&$GhG=HhFX0 zMIIy@abQ4cN|G@i-$5hf@^`W-n}QFmGumDZh&Mae1ZjLJ2E7_=O%)VpqtN&LvDEA8 z$3RYQyP@9@-ikt+vaoQmN>P@6!{MLK{12+4e@N8fGsd+6`0Q0&F*?y)3lBdDCsJ#1G>OLB&HLC5nLsx0%0}UFmr| zF0W@L{q*S*?d!*Z02X!KEDd2e z12%P_K#6;>)-AzPYudWD`pjmNXqW!J9UY0tm>RR0E*K@(# z6ZA!UwsB}*(a~|5@nwKKSQ7Mxg<)usjApqRWNCv+M!wKpMu2)95eukWvGHm zw^Ero8IvoUn*jkWNjn=+EcfT1GeCB}#b8LxP1Azzdv~})F!v}`2(2QQ7qw*?w0+mM2dmC`(H<@+MzQ&6BYwH5emc9)!W-^OUUY zX*Iloy4f8{8t1Fk)e>ky1pV_nmsi|~w$s}?Ix2O+HYue#Fw}wC!Dl2K zhJwVc`>w_^7gL6crLq}NeQ>`9yql|DnnL}zb`ez?6loUG~uF`dP%R0+E8n2rJ~ z50{_cSb^bBs4Fd7n>_@nN`v_y)leAmAm{k|9}bDwL5j>?UY(*8yHF1mTOhDwX$4kp zu#tU(g24G58UsFx>(~-zWdhh$>j|@i+dR4JYup(|I?eQ1LCu(6dRfWq1XosJ?yU+P z>=?#3@$ujR7FwvHD@&4#;GdoUmg00hGvemJxfun&*{A|5eMp``u}L2zG4jd2hV`G_ zF=LzUvK~>%C-s=b_;NyGm1Mq;Fp|-tbBq#@eFO<&)|HRE%#J5M=3Asw8Ce?lmP$A- zP1ZDH;$E4f_&k|+^G6Ji4Ty*OofgL_00LVKra{Kgc1Z4u8IKqivAp~w)R?uy73{;q z!&lnMo+QI^`DGr;4gKCl2Ixa_%M2!#0EF-vQa=iSF}N}(=y&W;dBK3ea%onz4O7n2 z&!45yrd5Hp1!bRXO}NWHW;IXQz=ik{HupNDsEm^(UiMVKbdprOe_sZ}X72Dof&S}% zl$@6sFiLlRefHKX<-Qiw$0TDsJ@-O7l)rL$pn3|u^iuoQx`p}qmXamG+vj{OW^RWb z;n=t!=UV^CJ$5|wM}S(~Fmt7ZVwHC{^gjDN=RhE?1QcPoEgpp>FMXc5o}fP-*xB&= z+K+Y$`RjKiaLz@+Jk`Et@5;BBJDs||v)l%ei+Qk)OOQS_Iw-EQGNzXI?d?Zk(~b`l zvq)wr{ahaHaisJ0FUggFUeirEq?i_(Ug_fsMrwa%6=!OMFT*pd%M>+5J?*4!!m!6VpADEh|nPZhUacyOc%D-dHd zmMLBZGAXebF)zE3^(}{qik9q4C_XUQOc=T*FMuxg#N%{eeU7$Ieshmd226!f*XO(*l(*8ZUo$IfEfgNh2E9C_it{%W=FU5RSj()ia~)OiRNot&Lb z7TaRPb^3X6kh}Ae!}ymn>`Qnd9g!^?A;=649{$q}KZ@3}4aq&cxeHDV0CPgx^(2Y( z<^u-*Jo`<%O>;U+ot8=D{uH>FIYeubG{tSkM)hwV^VZOU)Wn+j9?GhAS>b|>gPl59KS7e8IU7pD)oP4;sHlfW>Y{OTuIl)uP+RUpmDV$F_{qO$;#FeD$f2fbFo#^<__om%Ugii_;=kTmlCjGZ3VJyFekSn}(#yeXa} z!|=e7H_UXH_emz~oHS$U=0=rur^zEtTUU}O(pWd84^>sis{+{>q3$>Qe13UgfFY!f zRW&7LI3gmU`lUFPPO^HIces43YhEbdl)}BbOg^6 zG2`Wi;q;*?ol^OAN|njsL&po0e0}*n=9eG#v<{oAcA-RIcJLtki7GmmpL)JRW~^r| zl?53n+0WK=R|YJ<7zpi(&K?lKpKw`x&#El6c=X3Uas`FZh@ay&CP761p{Lo{$h?cw z(~vsJjc3!W>*1dD^Q6fp&fuUrZ+rNA)8~B?&bsRX^YhlpMk6BzqLU4^W0FN(5oN*d z?&}dE7O`>KK}WvBXyBAIgclnU`^;Zd*3}oR5Uj@_g zz)%9|@R5+qB=hGlUOca)>b4rLKPK{n<`g%#7*fsit@HPPM4k0yFrWEOm)a`-cDSp+ zb+-6hT|=T$l~@0lFOsTlmJtzx&hJfij(B`b>L#xj2;aM>+y4`@vg~CT0O#4QzaBL9 z$>}zN)_%r&oi;!?xzJ^GNVD+Sb~huG5#5x2uB;sQ*oMxEQD^Qx ze0^SZb#)??W7qzyJr|?j;Nmd-?iLv8)jYYuScmyb{oQ08G%hfOq?_f1=kF=Gv>=h$NBPN|yk zjp<6N{yolRA3aRu;=A)_%JgTdS9T$`a8B+0+m?(BUc1{{1wC zM_4M~PcJNRdQY%Ix-(R_`u>&$l5bYaVG*fU=aj+LY&6+?eEAEKH{<$y zPrbYvpX^I!)P3W@NTeZ7pKO9HNeABKwSw8h)jz2yg)nRQ}sU=AsAx=)5>t>Oc zRxT0EBEBz?m$-FzK$b=*+^(Y>EGGaBZoc#{nVziWC}Y;?WepL%GV@^$DROx1_}|kQ z8e>ro#gex4x~6|xLi&{=xIAFezvj(9S;t9v^5jW*0mmM_(1zC+-jvfcVsMWlReK%V z_3mOl{}ijT=VOHuhkgC|=gkV}9DZ2zJ1&pJn?dwixG16;0Uy#hug8y25HLY6`r>Zc z#6;%cOC$DoWcY3pMju)!{=;MN(HIN@AgZT!Ndp_D$90pdzhTpxrU;Z}EUG4-+(V}@ zBM2OmM9i1=C=oR;x=j#q-8ey;Pdj7APKsw%OAFO@S(ZWCW*$_h2Xf=OJVwTq5jJ2M{9m10{yJ(htz{d z?mE7T25K;kGPJjRVL~|H0$8D9r$E%m@v21wSy3yMHNh6V@U{CTKHHGqXYJMqZrQvth9Zcm9V=7}G#Xu_NP zs*f=zlR_~-x(GM}opqbm5>=7wU{!6c6egOW*xBEbse7}3OlX5w98{k#8Gy`|X%G@> z99nl+D3raHS^=-DoF;PUsuD>s<_K>oDY%?K7Xqa6#`vQAZQ~`d0bf4_l{zuf?^m@ zxNz>y)#c@6`~WdxlaK}gHwIL43~fr5dIC=fI$1|qKHo|E(`T}D^;*ACeZBkp_l6@e zKBWqCL#d3qX4Jk#$B@m^*M%-W?#Ldw$74Htij>iC_if&S=f1h&JYR-=v}+~OXAB)D zEeAeXf-_zStXNVe9eeHUc;8fR%G5W#+3qn(ts+&rW$zgqf8;Kr;&fkvIL`*W_=^MG zl>iR9$Rw2tqM+yG6e=_>1aG_4+pFex_&}pPbd9_$Cg8I$-QKs|fKQUeab#OQFt$q>xaJNSDqfiGD?Iu`ri(_hl$tUG#~Jy2pW9Q-+N!E5L+jJ%GI?iYoY=i5`BqsJ zm2&bCz0cJ7M+QVl^5>uA{NHVbpr_IWLQ1EW)J`@+awXmQ-EE>|t+2YNMU-?T`~r`< za&0H6eJ?8{net)|2t29#h~5mTPt`|gBeX~ah3F$n|DomiU$2kyskZy~Vo3TRy$}_f ze8KSeiK;3pFCtV;H8~3$fupHUZi%^~? zS{B#`h~1N2)+;e}n8l*JEB9+vt_hXRh+?M5cV^@>6PBK-~W*65vD+lgKX%9lh>6z@E|JO`_fVzsV>;>$B^lm`!LBs4HG zd;l$Ws$c9y-I@du_q0kbaltYaE16L)6IsbzC^F|#6BHqpFEC!Xa1Epr&85yoHZ!+V z4j$@9_jh1eO&iz(RSf>L#7gYJZClzUFxH^|vM`!_he1NXVIK6~>FBPk;s$f>RGpzg zRT_Dbq@*OK)Q+P)1Gln5RK#I_G(n1AHMOh(l&MF}r%&>*gcwx{0tO+CA1>suI*wZD zx^3LFXb%MCJIh4HA=ILc+Ea+{PJ4&i$-RI(8Hng;Rvegf9U#P(OEMeWA?~f(}F&eJ-I^u^(&_ z7=;(WC@IIs%1YksDf~SIaZ(sC^f!>L~Q z_DloXGrlqA?8vK|rg@`QAD+JDJ`@a2Q={g%aR%TMbD{G(xwZ2Z+n7=xm$>}`IY^a0 z9z|m7m3axG4MLQG8E6Dfq&_6i3aA2mhq@HLjy7x zg>RyL5o3bj(zpj)kYSY;2o*4?Yqrx_{9|*m{g_jHX^&0YI(Q#M>LNCC7>JT8Msm9)MB_(JVBvOYLkBztW^2^?E9<>#RdB<{98F-pJ$oa4b~FRTX`OqRM5qysk79MQ$YMPar|BaQA8b_s{0Ri8_re zQrbTH0a8096;(_Ct1C}E>KhWtQERzKg8`2W#GG0M%3+s@S~4n}WBxa~tM6VU&dz0r zOB#OG<2SNJRv>+lVeg+n7!V#B?N~U18hhrKC21KMplGDBb!#t&UAECgVng)tatv=Fv59e5 zn$(F+4B)plZUwW2&Cls;h?kRuAO8cHM4h}I2lrsEA_>LC4kMTz7PWdcltPt%l$t#A z>`DatmO*c6H!=q$WMB^;KEwdl9!!(T!P8rOa)yXy+Qo|(86={j0xsSD(0+QU@r_i2 zk$1mFM95d;R=eIh(4TJK{&l^Z-*M3s&20t=;NKo}b3LP{w;M&7Q9-%I1&rdr_x}UJ zp{w{Z;=V3ITMm1onOg+nQ1W%f^|dvmF_EX8OGQ1n0X*@2E7)dM#>Nz|gHK}jzEorC zvuDom#x{hGBwf2k*^;2Z=|2DLAzwc%h-4bd#cVfib(2QHwLWXy(Z`gS2(jQt2C5vF zt%!%Lps>17%h3xBq)DjkWOfU8pqvObyu%+WwKG;6keU(wDs&?-@2L(+JPX@EhwEs6 z6-|{U;fdfa=&&I`MC|NA^f(ppJzZH` z5*kid7ACuOP+2XDTUbW!kBNwnymY^j1C&K&-z`5_)gn#i>HX~IUlRMQBDQZYco~pguUNKCs=sCmxV-ySOArcwA4zAc-d}`fLQ1} zG%$T43L>}O23$fl>T6%5WzHFN@2iR-YQ>S`v?93+`922#--~L!=s{;J3%qtzU_nPR30F)Jo zYH74dC-*(nt!P&C`>}fS-#se4!UiC^O)j=KLMz!XK0XlIn9(%++_lEsM86&VNA2b~ zX9n%Z%5Pm=^n>)OgM>F+9Qf0r+k%}otoXWj ztJ&-m7ZM>|twzNnV5|DQdyHs!TGu!uWmAedhVlbZ{)c$4O|D^_gD+(`Kfoj9QwSXEBT=^kGbjt}7zVVX{>vN^IF6z}e z^?Qf=5&d3rSqmZBQ7R$$cK1-m^{n$rQ~9sf*5Uq+yfXEMD|4B`!osN?XvJtM#d#7a zmKV%VC!p(*{jKvC^QCzg}jM8haF&rsKKXVd!| z*b1@Z1NpkOwryQH-k6;Ef$kfdr^+a4O=;TJmUS>#J$|4YK@0ikjM=B!+Ld%$ zAQGE@G`>7c<#-L|PE|uAn1BYLMX5(-XJxIu;UVo8?rbX@emj%_{^Lta;*`g}6fM=4o#80z)%fULl25(d75Z%~ zebb1Vf@;yYG)9#E^oe`31IJNz&_U{N=^%a8pt9>Mi5sR@BW!gwkV93dwA4klnN6y; zLldP^2p-1{eO{J+c}7c})PV^oRFIryob~VCy$4!#zsx-%-kJ?VM0c{tPAzJq8b>(W za_CnSQeYMKet>VdAwtD^{@vD1KF3Ar@CL||7F}RTg3aU>FX1ukkwjDFd_9In?%5Dw z1~LWidOAmAUqA=%unLB=572RpD`J9_}P*|I- zz0!Z5h^ykGRFjlH-lzb(8d~#1T4rhIpRc6QR5v!%H4+uaJ5~FoYBuaCj+x6nFc~8o zB}{gQapq9v`<@rZ1~M>6R2|l>J(A7@IEpgLYInR|C9jrf?>*jBJ9ir3LNdT=I>ifwRg54a9@BtU7w2 z@r&syatuDUBYk4m2xFk;^OvObxt{BZ$>KKxI9#2*uRvKkojkvlgTsu#2;I0L!)4;| zvZD24){S$O-@z7})F$1X+oco6)g=la$+b@)f2_-L_mq= zu9chdeDz^I+JXJ~`Fti8d_d!AO#Vdj`?gb%iK#v48;NH-D&)qq3kymp0U~)|kZ`A< z16`-6&X0}hZ;w=m>oqQY{VL{lf~iq17RH+wc9XjRW) z-OG{Ay7ZZ*mTZ~6WaLqOH}k|hRpkPny>p9;?6dQY-(`9B?Mc?9rsF&K232Tb*M_zl z5FQ_}T#8sN-7zyipS?n};}>G1ot$kTwZ6WzTcQbd{Y!|XMJMkxgtLw*DyM6UP_j&` z*1bY>?WF;?labxKPrhb4%YF>D6eJRI;HCU`)sIZ*724(?7XRO zId4@f(9~p{TOyctx2#>`?e_qE#n-)!@3Xi3Lxmu~7NeL|H!3h{C57^-MGzO506x%R(8vg;%@KIttLD!7m$Bm+l!-PjwX_RY!bs zyb2B~r|?x#I+|x+wdB|Y1hgENwAEuKMmJ9InBJluPSG)vnp9Z(NIb?nNk32z(JBmC zMVErA3Z#*_EDn^S3gUlWUMWDiH$k&toRJst)VQ}QKcbwvv4v5sulH_)cZs|6NbWvT zClUKWNjLMv#eoC;YY4Hq;xal80d)iY;Aa*+S^7fBJa0d-+C@tdkmKR8HnSHOwi1v9 zv*HV-?NmDl;)$Zo3=#!nS#@bCsb+Kq{HOF8F&OYq4DU-{-&HGt#a3fnggX{E)X0~PcJfwCLE*MK&C8HbDzm}S(-Dp;}es^x74^2camo=rz_ay*L zG%CrJ^v&N$x1-|GJ34@(0~w+0@0~V0HkLfo-}?gH%h>S)(hY*EW)r<-Rg?PAVDJ7e z9l}jRNvkd8clhYhWW+C7<asc zuPE|xT2ID%6xoqe-3`Um_6Mi3DS!eI(-=p!31tmOxzqC8{@omVwmuRQ6chv(kqCR| z;oNIESc7gWkEa(UDzy-0+H9w}c18j{Bl0I-iV+{7=RB!jeH&!py~;8RKIQe+*Du-C zARMmrRHLMGZPYVH+oy6W7`_a8EwG=P%IyHaur7WQCaGm4ZgUf!L32flL#7!(wA2cB zifUu-u}2hf5OjS!N*?1TFj`x-Tq6KnfRHwhl54Z==K^?A-o1-*AWA2DItrVLrewMO zj2%}NX~2Zu!Os`HMg;j6FFHC1yYJUBnVbdEnL6H}`4v(8FIk;TMj7r4QOz6A+Ehi! zz9mlPEP)`zb%+jy1~tKH980vLf_bJkQ)c4<=lXgq$Ny$SxO0x>pjk(u%_FQO#@+jg?lWM;stC|q zK6+RXLM^2mD`k?hvVQ&ogBIm(jwv#5ekGSIERSkxvP8kE{4|V5jEZd=Iv ztg6VeMgP7eZ#{o4WJX}+$90pgpLys{fRslSLI%=4f-BjhZ zq%T7N+s(D;*9^!n=x2c_1G1h44giNOhflZF*J1ug461cw6M1J(&>ze*=}bU|+OZZ2 z$3B^V=P3#!9yU7Sf_bQ^I`lob1qkY7FG zZ$Y&bWvh2v;}7oK=?>hV3ktcWD4D8`JHlfKOqaV3pgc!->u?(iX8`G8jmc#jTwR8b zcHIPuHxZ0XlBm!7|Q7 zH@xe$ys6dGOgcB>J8~QoWkSA9?vHQzSyruH=>Jb+Sd3vck6VY$HMG1_9_^&31Ry&$ z_HUw-1`O|k1N$hcslQK7r?DCs7=XMi5H^U6Vk;PlLMHeB$-^u4>XMm8w)B~i#N&@c zX7}~2Wc=^@c^bvOf;`V*WtdqE%l@m6gCRSivf15#{s|RsuYs{KwGvD8{!C_57kBSYip1oLmePT=lip6I zxVh(^T~Sc3K`AHU_D2@*$OQn*=Rwm8X)9~Q%t&?#P%|KAchuTd{CFei5 zaqdWwl;_{5gP?vbS;KzDW-8on=MB=;Z#bZC2X7C2vH<^1Xi){`Q+OhY;T?O&Kya_* zK`DAyvJn=ENcuqHE{7JAUo0|drq+24kV4>+c$q&cf`i4wZbe0dKn4Tvo@}g6het93 zB$n7ISwEETM=>R1cPE==BofLjuc71O5& zNs<)*xrXDgb-81c&v?``)<;ktuQ)xxgj$^ zh1kx0Vq-Un>9uO4mF75-$RNvvS$Q`aR~N@hOfX`=RdhagyQrCzx!p0$O_?Jt*s&|k z*w-h$D5l)&$0y&HH8q(7j;XR?=k4!jbdWS_2X0x~?IKZs<|&VUk%XB|ruIZ7eK@Gi zA9WcSEYM(gr0^*G3?2vJzy9)nx%2a(rwPYsGp?K+JeAedYMj?k&UNN78e1*0wYzS#)1}UslmE3r@UX~h16mU0wH*oyhyQvpw|8#!kw-)Qr@fp^PV$a| zX~|5Pw07R5MJ7_4%TkeDKxpXDe3$qk9i@hFEg9Maq!x}E zaKHR|9Xjxz*~>Gh|1)MO{rB&)1zP8)S8?`<*ZBhv{%iE|pVM3aGr0Np_W#UiKHK%5 zlkndk_-noXdkueY8(+CQ6r_dt;7$%<>k^d-ha3qmz116;kYaA^ot_z(8~)kz%jgi>%OILxB5A532oU11ntpI&S4QS z!};0+!$ljbUwnkvmG#cJ(rq<2p?^`d|L00BFm3Ubw12ocd>;&8t{$4#l$RG@UVhtw z3+9*FJ4W~p6ic$7Kd<@j-Nw-VU2X=!TzibhPrZJ4b@K9;sEU&|Kh7uMnvvnfvo&Un z0ha}MEC-Bs@4BZq>4r~`&b1z~4!aVBQx$p5Rq?-171rdx&pj3aTjPI;7}igUe&6unExyC+217PG`^oPg&kYW*2J@aE{q*_%eQu#iu4KC< zMt6jrt+$b0+;08+!2bDX=Z>8`ocKrcXKrq}YV!^p zA$3D*H|a=`3YfD%aHZuVwh3v#0+5?cgISLF7szO-t!#eZ8CY6`rbGrJ}c2=fQ&qvJLk3_KBI9 zP1v<+@n>WbvIZ3_R9()fQVfcoWHi7%}QQsCE+erA;bdz`|(d-r0z8F_fr>VmnHAgUVUwe7r= zy?p_|5A+h4sRN%u2}e=-%d>JAtyc1bf*vOJryY*>lMRRK!!o}bgBvq(WnrwXz2cq9 z@A$7=WvtVzj0}2W1_|6zQ~85!RJzm<0SvyV`yBBnib6h@_YHszHNemBw1i<}^kWDg zU6~YOW|qL(`Jpg*uiLx=(umpFmjEpwSDbx;9zje-#(CuAtx~S*D+|O@F-QQH0y#m? zW?L|>DO|nkhQ57EqS9F)_#D@0DJdy0oAk zusEJqXC=QHbK{Q5QaXO~8>>@P4+sqc2*7IOrO(XGHDloxLVDN48XFtgJ3$^{2;vLW z$kL(PESk5TYy4G_LSX&{=Cu2lG3qAv;l!`_THXV9g(3Gkzu1}nDymb5N7GVLq@i6y zwrUWF2dj3C0aY@_<2;U_Wc_CUtX=%a4y_3J#<>PZohzZkqRf)LoC;w=SLCSmfyHMtX%k|_ffYho-m(3|D|Qg$J;y2#xUNe z!L_sr{p|i4@t@pP?UO2A{av>a@8=w}lxN`f$Q!(o5 zg(&CM#r*+xKp|kZpNE!C(CG$LnEhh)>Vi?;o197Ljf^}A)K`{v)o#+hXW+<$*F1p& zyCmh|!`J<2ou;n=hNdj;P2Gr|HMBPm3=R$^VuwQMswqJsrfYJgz-~$#B@HP*(}KYx zHrRfxi*U&$#OX~;@+00W+X-}@Diyb}yrLo@=$_ovqzLR76BE<*Tek>RTb15YXYeh4 zYaC`jcMjT=V+;L0LZkRrIJ>(kDAYDL*1^p8+Tmt@9o7SAQNP$1FaC<>PmZ4aWACw( zpc4ShRiX)7$*VU9=s+s$PSaGNySQtPX?@Kx3WS8g=|q-DleV<5W?O7CE|x_Uc0FY- zv#UTk46?^yUcp3Vw?!2VdoMp)W;b(<74UFz&`yTb#r5_51MYDVNC=v`3i;3prC~JY zx}RzBj951aGFUh`+9-t9B8f-36{sn}hfV(=mkLI$nOmKes1pr2}VcTNxlt%`tUxG9@kZcHH zB(}o`4n*bW^8yW~>Ehx->%+#!r#{x2s)W71d(R%Aw4=f13?5#EuCsj?e&w`ymWE^& zz=Rb2FqpGMoF)v3G*EX83=A;Q(~E>~n>1zIs+jMxYAD#tho*wQ^wIGzQI9&WLK0^I ztkM1q-O!Pf-Bv&>5}otBFAEC`b$CNxaCNQ22F8NktczQU+%5e#UTYnm4qysa?BvtumYZ{=IO2 zXq_IXcXES{42}Ticp|PR1%F@tB0|AuvQ)OYS|)a*J67V57d2ngi1qNco%@fi7wn#p zl#&9Q=@e8qytwM2bIXc{$Eudr()aEWmq8OlU~>}Z#fzRxgc8);;%CcE7GF(Sam!-qK7YS;kSdNl8k=>xMl4-=l0ovjQZ1#p81fOasQQ@@@GYNP0} z)zsIwvZfcqgC8mADlS)YTpYjP4fvn4sPi`=W9|@;KzLPcY;06o0G{pD?{Soa1v4&{ zaTH#OauI@+GaKuxVSiY{o`@YgD}m3$7J;VZBg5=&cn^#5l!rMh4g&Y5BWE<1CcDXy zv=Bi+6PNX^c2Z0Yv48ff9^oronv<4iEEtb{{Gz zCp3V{tFfKzkF%AO<%K|kczo^2JGmY*RPMcTS1wx1)JYgS`L5P zO)0}T2jplig9L`o(4`2jmD##`_wHVx#2 zXjlee<3dkrl|P#gp@_Egf(qr4BP>|S&7W05m(Y&W5UPSn*3Hr2a{6g5u+XA}*#ho* zjG*K%EB*8VWEIVi+R6sZ&YHD-cy@?}dsm_dly&+3weS&Gd`)}5Z;SgVQlW-XvYHNV zBA8vfIDx0U6_CN8b)D;wb35x<;zYM~dFW9Hq|DZc`2XwY{ zp6An61TPu4pLgdL_n{?6Y_93Mdr6SgepTw+#@y=knmss5Z_q>d_S&OAMhKhX#Sx6p zC*uM4L@J%%vwOGaNOlRtA8e}X;rP%JEHPcp)<`A%?vwpA8vVqsUHIAWWAyF)W4~As zujp8uw8IJR&&R&9ahRxnd3FK2!Ldt_o<2r)T_>3Uajs2^^uI-ff>z?KE2d4c&xm-V zE{Ic{J46QNzt?3owRC;)=Hn+%lJI4Kp>ii&2e2q5V#oBJz@UTPQ@#IVkN6xs< zkxv1gm=B}m|KbJVELwk@<>3K`Pa5!3Tw6zqi?29is-v=>St+h%SAT5MD6Xb&h-RE$ z()-+n`1HHDC#zi03wL7U(M~@v#7zXz&4kmGW;8CN2Bep@f zC)Nq2^eRlBT_6?(*XyzK|F|{p4lx8c8oV7e zj1maWIfWlRxvjIsrht4g5TU-siAt5Ch1Q#1Nd==LR)MU}i|nzH2_4 z$vx`x4l0;i#wIg>O(r7f83a%LM$&AwyylS`*teJWC4iqT6<_S$%KQir0y{Bflzw?$ z|Buy?+C{RtqRM&gbwcd=!p8;dS+3R(RypA94+@jn{H>1@7oBTPM&vI^J4 zzUo|3Tx`A6ZAazv($6o{JN5Dbt~{QMuh7V64HT14%FK5 zIxtY5x>Ig=&fmvJE=*fZUS9rAzlRadpYt1lb1Ls^YK_0cKFaC zNU!n0*k426iK|0dS@|9w5+;h>pX{Ro>(JR?#S8j$;aVq*0j1$L-hR96TRE%-VH4uh zZH|2Ux>F>4`xRhKh*(IzKd1h0aq+PyRHo*g@tlOk#*8-x_#z@A;^ZRIwru-DQ&nd% zsqOvCB$tgzmIdM(t{Za^eo0$@B1Q2@UWMm_$?>YfeOFr8kfQ~lmyWQ!wzIRSbpzyJ z7`Hv+Q~6MbeJh`40Mpb*4)uTjiLB+IzzS&6{rq^_PNcln4da8qH3A}S;ap8S+vO_Y zF3rjtm3JuSb=!sjS8x`22Ds&*Az}*5$qR>t(KmP|@kWB=N|)3NYU4-!IGC9&irB+g z7b#r=v-tkLdpGuibL$~Y7N;wi*rZx6Ed@Y5=^j{RfDhPdm>Rx!*RC(^Z|`u~j^Dso zz%{f9F^ZlK7za47PPxdPGL-gBdk3tPWrw)+>dXnHLen$UvH#2!|jr5cw5w5VBo*e<;zgqYd(`3=;5SD$>%PPxc;LOcMcqS}_GMTVoe?Ed!DsZ$Vsih$pVN2HJFp7^qxpegc9j4ZO7YK6KB zwQ%~Arg%9z0Bb(s-jRqF4!*j{uzvRL7+9w)N)|?%#Sz|G?$VSCyZi?72ogn!eNL%F`b4l`t?Wg8I2xfs#5k<}8X zTl*sof|Y86%^9$qXbOW{NI!}q%n;-@6LF}}Kg>xbu)JW6=B;1Vgd;eH5eL$@my7Pn!Fm zO{p~aEA6!AK9_gJ5z!K@33OAwqyo6Tb|Vi`5~|EYhjqDlcxv>PX4RnhIfF@|K?d&_ ziAJ?P<%D(jV$Pv`!Y%<3eY;8|9Hq7l#AWFYMb>!O8EjX2IeapGz>_sB`g=TSb?dFB zdWR8&x9?QK1wj0*I~V{s$HOc+nvJx z`A1v3KP(2>_Agjqq)&Rg@a0e27p7g8GRnVyZp|I4iTXa``}FDN;&*(E`oMcB@vAVK z9jz3o3R7ee3%wbIW+u7%f$R^b0c~KN0%ZZE4DJ10y2&=DQPH8la<2E+9GRp&0tVzE z(7A7irAY?X*?Qu?d!ft8b#t_a42(?XjukUYpN5i{d!|3c9MWKP%mw7Gg8)GG(@_sa-~A?0>&&w}9%=vKutesM(IRa7URQ zX+|eF0279VLO}_t;(Td|iRWRV6gl?x5V}r5FU*q6X%-G3f?{yDxZjNV z1P<|JF|!5sp=m4DS6u)QxFKf?4JFEpq%Sgf>mhzLy}C3>(_=32chQ|Q%>+h4YdUxK ztQXcQXqyEFnrD|b+IOM6BHmHu=@oJBQM3;1EsFc!1f`&Wix zVbku@*BL&lxSc|>6biCS3%RYFCMvyL%c0GiZEj!uW>wN&i)Ro%p)=PobG`YCq0-(44a^&YIHb{ZYZ1p) zG&H!OpX%;5s0cVvcu_nYTN-Li*a0|5zf!;^F^!bVCO8~uiB*puzlB!w2g)H+)Qd2- zRlrZz@!BgUriDaLJ5+Tv6!D*1RP>8_WGklI&|zYz)dJ&d0!%v#0nF(HyK<<-qAe-@ zwgJZ>CpvmM-Ux_7H340Xf}K?nY~o=gY_B&AGqx%C_#(2wve6? zFe@pBQ7Hqyh(LrTHHK!cW7=3YE}Rh;Y=(|86mu3@|6iF$gBEQq6^X@jx(6}Ivx*#(oHI>fc%=3oJxz}r{o$;os$>@suEB<}+0c3y$_!J^4&U7g1r`4mTOzDNTFe;ZX;3Q0+Ae{<>=?Ie}SSTu4HU z%Ndfye{a-Kk9N*BO>1ma$1WO;(YF&`Xa);4ZTvBTSc4N&+fl0`z`(?W-OarN;r_ac z=zU?v6Wd?=X3$SfR{=UKl>b`s+GU!^6X6snlu!9h@qy zys@e=VbQKX0suqk+IOz*5-r%l(|t3hU}mD%5A7vhYmBW352%yXZ&=KWVbYwo#mT=5 zCPVW^)c~{?i}%Vlyj*c!g%!IOPhh-N%g6MNE}E};*eFHef3q4~PMkc6r8YPGmHJh#RwmR?j{)W=d z)wLp6QV$q+#sQs_!;c?7o-Jfwe1&*-q*XNY`2y2TyB15CTo0A#9ntlS$vTem4x2wx z;oVkokNu%GOauR)a4S@Q}-C<=xk^PJ(sZm%+qc;Jnr1su@-K|a0lPN5$jG?w)?0~oTg z&pTr8{@_71A{f(kC3<%*AUr=_Cx=Pp4^T?{F%-NRuW#O@+(j8jun8Uoc$m|I#GSBzD<` zfL(>-NW)r@ALh?Gx36B@XdjJd3-AzE_b;IQ9|fy6X3ndPAjD>fg{`0+1np<5reqZ4 zIKaIO9;aR^THhr{YCghMVcQ3OGMvn%kSOD=mk`ey;8dZat{#a`092~0Dv8L1R*QzS zuZ&h5wPz#=U8u}=?b;PTyOaT&p+SiFW1uPp2C)!RsUjJf14szAN0FJC>Zq$~$iG!q z<^TYX9the_P5iXO5yB0{0P)_fWmiUpM@S)nM+v$y-Ji&1p)v<(pa$Rt9RvV{C?Sux zPcZ}vee`V-=kap?d;2!H28G_a&v*d{qJWCHw5sYHBBLU9u9X~rafm1{F{g6zVhC=n-t=y<(EOxH_-F^FM4_vW8*%C=~{I*hwe?#f$QK~FJ~|iork9$xg1yt z90Brv06O*dFp&*AdrK3ja?;8E-QGv7D(+b-)~5;DfA1(}15vfG05!L;j-@2-S@g6g zKU&B}t`#it>}NDq{}VN!7mT@PeX2^cqta#P8H6;9h^vZEw^*anOL&o<1iTIJ8A@Vt zxBb}Vei{BXapsww_n4My0OIyqWr%fH;ge+v-GE`~&~n|IGqSB<1~D!1sOXZVy3u+{ zJR{r?2m{4IZND)gAKEHl>9B!C1Uq?9`4#d9Q9cL6x4JS>&L_YSP5+GF3=Y}V0Vl)= zCc4*0U=>G?1dkj+~2XkcykNQA}@TrYVYZ zeC2BRm-9uLK6HF8t;jy1@<1htB}fqoD`% zyg9Kj-A)5Q?-7*>C=x$LSa;&%Ztu(&vzu91Vu8aL7u;NtZ9n!J1k&J9)eg;TG#xv5 z(B(bizz3?4-l*>5?v)f7yib~m7j7Y6)ghRIS03niEW>6pw3Bj-9*_N1u*)p<%a<=c z(L8DKQ+NFmYyqCqaE`AYt;CRYqvtpRb+NKP&Mrj#tJo$nK4Sfwc+}==%CN9F_$!p zzH*!}qxU!MHszCQ&#_B@6o*sg>b?$hw;&9=>B0lukIaGO^1l&Ar2L;zlIiOENFjns zomd7!lg|qRmSD0t(V>9hy=TWhpt$fPQM^toKi^>0n!g@_&q#`!{V#+h0@a|E*mQ9+ zU@M$lSsx?X=`{EuMPj(XhqSl;IpV~t3FIb5D;kjXpxM}pM?|o|CiI4}vKho95C^4ZpySIj_7!K_PV;d5{}DXML|PXm$by(02=^J!eQM=Sy6Ep z{u~4)_ntk{a7DQ`r-2zuj2#KfMn69*eyyWGvw5^VUk~7fPZ;6AZ`_*4u|)tM`abuw z$6xPAYh5Si+M;b%49#Zi5yBGP8sR^&0oVi1*|gc|(S*oIF6t#7h)-yL14Wud<_Sk% ze>!@0<#QPM*tI#7{QRm%fCcQ)eUdlxjhWOwAge5!@KxqK`u}A;a{b!eYI_in#oL-7 z(`YsMXBt0zAkAhMuS;D7y5;kHCA{6(sMXm zZpVm=r=YD`+&9xLKB)cL4gXOKo{k|M<4hUCanU-#?TI=yB1c;4V-L}QT%PFFN?El~7BG&0)v%E&(J^Dk|_Gz%N3c#AiRfweU zq;NS8p19czGemjtQx(pMRK;<|877Im*cyO>8fdm|G>xG$-b^YMz%hw(rwys#O}!@l z)p!4{F22W%=03ag9x1E#VLx(~3t=02bVerv%i7B!2uJ3_=r^-EP3IsG#En zC8p}i{_!q?!~p6j6azxV@Ov2)w6|8VQEH#YCg{_U(GbKPPYU>%O{Ohw|INYGm#mlDpJh(UP}RpE}qIOrKiDUeU_TuL`?ysfpxRgl-XJUZAE z7vWcLTJJpF`UX~_b)DtLy1GFyZioZ^g6?l^HH%o{{vcj~>Y>DJz2=&RH`mVj!qv+?*aw?&; zB$T@w_cuoOp5&FHk|qtU#;t6lDz@C2Z-Edq(15zbChSl8?fW~dB=5;Y^Rfp! zjff&|p7ue5a`%XQY24@>zNkN(8DbH_q5x9@PKpi0@}8T6o5?LkftSB9Z1GaH_IT+>T_0Z7x)*O1)wHdl4$>wUEFE)6d~>JB?(?Y}s=r z(vlzHTq)uVW{AZ(qfBLKs59)O%!*mWY(I(Qz1VMzrrNaTL(0O%_~QO&^zYF8!^K*V zZ4SF5NxR>L(zcI?rqC%OWGgEuz&d(K8y0d|c*ro*6N>>LOL}%>vt76+wUUMCDpDb_ zp{j()UA@%;cmrMW9xAMu5)N<*3J6p+7ODAwDpw>-CeoYPw@=Tc&toBgBT1cV+N(9U zf!yot?8KH^n(3Y1w73w}K+uR8V%ty)RS>=zBnv+koxjcPwN{fMJ4e(uZQ6t+8_cmR z^Xtf#`p3;o7MPIB!k}Kx(e@;L8KQk5P(fI9Q48b@V{4ebombcOxM<}2g4(Q|%!nrG z6_f*vW@VI7KOs&6XFBV@;DTsYoD^d-ZEF4VduO@J=%73*a~~fcz>fz*zrx$pn_W*| zJ`pEq06xZbc%fJu{L4#A70|(xkQ6mA%>(%Zj;Yn3lT4bxlj_kUVN6>Og*X8;Fa|lC zdUcY?+vi36Od3dZeQS3K=UL;=ua5J#6FivkbVP_nRk_#!p3ah!J-m_>x*2}4^sqCK zKqh!Xh;l$dU#_IbxRi9=%hda#)(1)?qOEv6Vcq2a?7k+y0~BR5D>twP&tv@D+)>9% zwlFg8!9zm;gnQ5eJim(bgPS18!9jECndJk2L;LyXQIR^bF5M}BqVsxtoJ(nj>IbzY9kdOr%`{t_|ScEAtt+T}MqgX?sGQ0yM6F{00yqQtgm~oThqi z&6+g>5Ear;q$c3zA@QGv5hV?ElMSS1ZaU&yHgCoXZEfQ6-(~V%?Gg}O6sk%D?j36E zyl(A=L1(6tc-s%LZHdQxaG~F(oYC@_vIzeWx)BA0R8ZYU8zxr`33>^5`0!)Nrhh|6 zN0%^rEFN#g)b!4|xh?qi*Ti=|Diq?}x9`){K%_Pz!+iTzyQruLf*iHf;^$)LBxb@u z;dxeU!t=hQd>esL`M`k#ITh{1Wh?6bDX^M0$-isY`<)`cyKnTHm=IMX zwQi+kIoLk>9S2V#f%#WlCTuP#k6;5hh_}Kg48LuLLIrNP_PH)>M#K#Y^#N|=r~Ypn zz|;^)P*`-7JE`58ycc|1@NpTVX09*&{rpIj1Q3?{>70Tv{ib3xD+@?HFyxLI_>qks z-;rI;*{pA_zFxv`8+qQ^e^_Nk*k_;BLJIOMdyT)bhQ52{s#OHZHU{>qnR3|^z_o|K zOJoW#Ov;xoy(WDff!~mH%@dQfH=vrtA3|ay$*=Q>y|NY2=j^uRgmb7j5Y6Bg5et`Y z!(}NUg1(XLKYOXD|K7mL+BGIQIT={#RG?@J3K9OoeRCH|pIoRYDUpFt_5jb{<>lq_ zblpr}b;r;s?zpIpt0WQ;I9BrDFhj(=A$S;pxDHW;W5H^b)tt%Osho78G@xxk*{O8& z*$Be1hb^W))#mTUr<**ehxaZfNVzaUik+RhbtrHlVhJNPGE6HJRfMYl8wKSVC4nNl z=Fe_0Z`bP2J)Yyl$%6$|Xxfb#E2<(I5tC7bLo=kYV^IGn!$prEi7{#dRF%Op73=f~ z%QSR5yZ;g3=eK2^ed_DGou*^c)9LrUo5`GOSwTeuCjrbWzd@*Oz|1i%go!^#Ib3(AU%Fv5icOsRTzlVhta1@&T_HJ7xHNRRbvJq@q$H z@{5^S)I>WSg>lZ`PzK2#@;LMG+}P|IFx9D5$_$619F}X373gj zem%9@>yNaggbnn6{<+^!$<0)__4<=d9`Fh}iHc}Ly=DAi>&VNltg53;nt|xLjyPWx zN=fMCy~v)JMb`yYdIHuAX*M^iZF5XsWdg1R!Kn6n!abAZslc0!3AHF0DB9=#H2-3W zg^jm)d>**`j} zs+W~Jb!MAPJuT_=_~$z6^2W=bq4&!iP5@q0OdM|Gm?gsqo%zj7Uy}1Dq~7Mu!eM1) zvZQ=dtRGbc4i@S~)FH-PxW(V*fo7LMj$}U_4J>jxMdm~!XUTMxUtI)SC%*otKUYJZsMD*XP51$`)qzJko@2CJjPNK>{5OocE;*4- zgm3^voduDLsB*(m0IO&7Kj8>*OGxOV-iH<`oQU;G*qqJ~jva-tA0+osW}4zU2>lNT z8+%E2yy+^1L=xCEQ4DHX`n431=#i!v;4#5VaqEABEyPF?z6I|tHeE!)!`g5?1>m2C zSy7!Itg#v(?k07P+rvY4wJPD!>vJH$A^NW9*jNaa1IcB$u{oYb{DFOmzZaW$G@NbD zAoCs6od%@EwDVQ&?Av?HO&~+DW8)R92FOW*hBukbY3Kk{+8XdTKH@MQmNz#d<~&g$ zJU%t0irWOg+j205k~AT9_>6wr_nRWgzlfmjhSq>D7J>c?(ag{?=s zp~B!o*1I@OgHj~rxLr*A6j~H!BAvhbeVPOZCOT5M=g+l{dhlctmO`<9Rz@|ZKBN^0 z$*|o?4*u^`P1ygI%JBrKtS?Cvxf-@3FsMMhb+Ua)G$;C z0D3-)!m78W`; zx_$=pt9_5#6Ry)P#YU0wM@|&>x98`7s|rp)c!aFG=IR{M#sDO3zvPyzasr0=YdWwL z^YR&~cmY&E8<@KcbXEMI{yMiOdSeq zGo3@s1(Sv4wnA5;)@TC_QAU{X2?IT`LrK91I-<-1DhFvj5y<_X5CXBY{VFnW|G1qAFzlt^+&M4rXxSx^Mfu$fHGi%+ z(Z_l0AHk@9|MuO}$aU$s?XaKDscnC7t7_BTXXSQ(t)6&Oxt}ZdwA{^O`|CcRbbM*x z;@a&mckjpkXCXJ*61L6Z>auzzo`{te@9ljd1M0#*G&Gcm^Kh(Vil4oB5epQH8?rQW zb93yXwCEWb(Tz{SfI%3M?x1JE5_Q3%96v!eEV0TRyHjQrZ_-Mtz3ZPjxJSJ5^H+1L@2t0olqj^Zx;Mt8sEWXS1ThQ}<5k@R{}T6o|MqlsDaA{&0jAIcTZy+N zL%r21?kB(scmjsbj!U?Di(6}Bw*qIZR_l}B-!^%yGOvLy=09v%moo#E8!NDC;qFB;u zGHLLFBThHBiR;^E)W=qPyTV#V{jvR1gOh*x<`@|n!7Huhb@Es}qHW&Jr?$w}Omc>W zg@wG`6Lgf!q@mUvwY3}Be0_7+2kPf<@t%W7Kw+{4rSLZ=$&E~cr@`QBl1FiaTcvg`;OA!!P3lXdP!|?OdX~xtG(3-C#j-D$3HTprP`ucxQyOMKH=59dUp_yEMr$8ERq zsM$ON(<3X%9IRb*=P)6i++TmCTA!j^8_q(~2Vl!bRRjhw#8NT2%#Q;d(ALozW|e)6 zA)i;MGs$8E2&-x_zi?^v5k>j$%~q6s9;{*wjr&CKNBpR>z5WcZ#*|hJ-wHn~;l?GT zLNZu~0thQtjw_BNmlKhxYR<-vj*eG|8gf2R8CvujQ#zV}gkDilU)N(Ter4iO25S*| zNg~%LuO@2x3_EoS4`rPwg)x3Q_xlA_z)=KFDKf*$fhFm^0jz6dpc+Zy(q2g^sEEgz zAyPfaDhUR%xP5)gtJCPNX^{%M3CvX%w}0KMukbCDA_<%W?tf&Q11g(Gc>P~uIeW*q z6sY@#zof|c)B&4;Ut8kGJiKoxp0LV!&<3CuT3KLeL>4@Nt}!b;{Ugd2lm=K&Y+Ll< z0~fw84nQ;rIzs;fMc09iIIpb-0+IYOz7H^254Q|-HWB|})sckGTYCk#=_z8afso*c zNDN0Rk^XEJ=N&-mG9?fpBSQP?*?VqoT3cNRi-)gBHnu|OTxBl~ssToLfe;dS(nvjt z;li!kQ{YH7Q2WLk?A$AqPO@bSqkh1`x6Un2%+K@&7q?xL5-&#zLox(mN*qG#K^67% zeViIC@Vs}2CEfvo?5u&B5lUGE0tei@J#q_?3}K$LOVFb2C^c(c$m`8k@V~7=xfZ@K zH?8&7gkZI3Sp zTp!Pq86xB>;68@c0*4Q)Lh%F!H4?r4PGMnT_X#oEq0dOde7c0kx<%u_P+-{SJ7i8# zDz>z=w3wEWN#lq@A|PKKji=`XBdTGPS13wNlJr+)EKKL9mvWByH4s|0mH=hCgOhUjk zaBD3aSA77{sYBNQg%mfp-5O_g^#Cxu@Z`M?#+%?Q2+&xn6ahM4R)vpL&}1%*g9<^I zGFT9dhlQbZ+c73{>Xb2N`W(ML*t;*D9zrs-SY*%@X^%=)UMclWt%Z{(cm3RT8ylg^ z-8~hj@LyC;pFVvn51Y0h`_7e=qxHvA)xnO5w9-m#PZUuOQPg(;Ac((FY*^@gOG*@D|eJ; z658kEqn)cS$C5G}O>hv93vxJ!;uCTV0ox(`Wq2^1$Gzi!^aJZ`v%oCxre>9kjb;z> zyRUD-xuGFl#6omH>L(wonCotCZvH=m9)5)o7qNmDsgOtwIKUZHaUD_ih5DU*EuSzb z!~6msY2h=g`Au!LgVxY9Z6BU5w_edAqJDqbZHATvc$SlL#)%pC zv%bD9DH-_e?L{p3VbGqvSm_MjCKVq<&05V2@xDrQ63pa1ZK1tnc)j`>-^Goh+E4KJ zx(5bKNB2{5?t6F$E|xim!|yfp!+PBU&-+#4l&ySA`;zCKycxnFxGoBk1RGS1;MstK z%Ng}CJRW<>FRh^g)pTc-Tnf5&7w($-o4|q;O{EC~9L71Pf_B*dPz>AK+g@YOCo_O0 z4sPKc{ey#A(Mb~moAMg+H&DvF5Y4Fga*VE zmIK5m3ja%*LEPsMNN~0nv5jxDhPL8fQB9kVfq}vHVIQ9We4a4voGkgo%y*eNIlP#) zS2RCZ(b}5ZxCk1!C}?_o{9qH-V`5iRYrMoqX6<~J1#GX;V=7`LgT&OQDP6KkpLXWY z&-M0j{-m^sa1D@8!$Aa27V3;>vUv^y4Mryk*YRGENW z^RY6bZ^KAxIGe!Eo$mso)Wc;N92^}rC5l*7I z^d!}b2sqeb^l-=t2mw*TH;#eaG1;mvX4QQkL++Pe9*g60eH`QS@U-t!1aj0Z<>Hs| z^*yFA1z4u@>3G>fb}uR8pXsl-HoW^kFCbB6!HU`^418f9ax`D69wb(TeZ2DiY#HA_ z`wc$+-DQs(gGPbpfEV^t!x=K~ugCs+fSNp}K$piJhN-v^XHTAg{?cO38QNj%#OEPnMh=U2;69YwnY4KM5htNMYM_;Fx$z(&I4uGzp6 zi4K$(JvbD2&kq@4O7pccn83}`k8_aa)4=1A7z>>zY%cxlA@C;iOW58K1Rrd|4C4U7 zglsoy#M8}Nqu~}X2ofRY`*aIv2OK>fGIlI%(GW&`vXzO+RN?biAXf?q)MbJrSjzGm zs9+i=iAtWW<#ag^jc*c0! zHUnY^JJBv(BjcZO8089~E-CGFPcMno;`bZ z0Q$>_vjhrAQ1tBm_ALT^KJjb7<|}HFGa2_v(38yl{+_PKb(J9*>Lwlz%J>ls+qWAJ zE!hg8+L%Pc-cKZQxE_$?h6+O=9H>;KE!R>B6Pd)WXF>#4czqMVju~b%q91;T>EB`2 zKPVl^P#J>Kj{9s48qlq1PcfFN-7YmyL`cmoETDf6FJTPZkeMdfjRHr*+__=|Pw-vX zD$szbGqAHKU}G=Dtb|-AMzkm{;kuKtd;8Y8CO52sCy0#!` z2m(RCQfJ(wL5O9eBllb0zGi2q26S$ibxm1k(*idfhba`ULu=Qrg`VWoK)t9lh9poP z$9Oq}iUN0g>_N9-1HbhFH>hXIV0DgNIgrg?kh=3{2h;C6PKoV+%KmT@SO}wM@$#~= z2dDZ-E>##~DDZsnD?3rt;K0228OMgRXGF>lvO3@XSwMBi5#-RgHDF$zXV)pHTb{-e zx`A?j0}db*WlupAyaMdEJz*on#pp8)nPLE4W}~_30bGSfQ(a7%Lfif;vQT_aLc9wS zWS=hc{tL&lqC=rF`>c6!MdpTI^&aIF3oLDU&{Cr}hL6N~RBitxt$C&V>y+ci638U? zB<0%6*S}tA3gyDdUm+h?#k}iRkfBf-|3=@k{Hx&J?|)0796seoj$lD0qCn4bZoFwG z4apUf4xKoS7+p9zHi_)&XzB)bIQsYbv%fxA%=-FuUqB+Lz~gtI7gIu14(TQRD1t__ z$Sz7E|L?m(e#EbEOra$1{QaFNH|Zb$$CK~M{ol9y|LUs$|Bw9t-o1P=3~mL#I=(dk z>QtP@mfXy0+iTabsTr$V2M(+_CiVL;Ci3Ftg=O~ccdASd3t}*Zxb=9&v7!s5D1LAl z)CRAqK;Qv9BU2ufG0%8yf3VswA``7SRQJd3h0o9Fp3OE8w61u4fZtz-UAR z!Zm!(X>>Sn63SN9YX+3_2Yw&=p|W@H-kpy3OZC=n8_L93A;vAL89P_ptp9x`8+8b; zy>+W{w1yRa3`o{Q1p~?j=il#?ZST-fEWx7!Jj!EYp$T+imRdt;kok2augUj)NqK`2 z(tl)RN=|ICc1RGPYSd5PKu?K%`0Jo|JRke8<=ob19(aeykSA&1#7^N8PX7Lcia+6X zf^1Ira{2l{9ja>NK1fMq`}OkNG`k0Yj==Z0f$k4ua2F8E>6}VdWbXWZkF=G5C7UT& z3XQU-bWvKket+?JZglln1iRalll&{vg#u$7V%b4acT347*gQp+SF|nchwl|9w-p6z zo(m5}ewKc@m6@5B%H%k@CHoprVWq-~M8D|c@lzW@PKXB3@#?LjBnJMsM1k~wg=G^( zTdZNx{_NwxEr{9_O5}yAy!P{6XQ#Ygf8BqQRA)5S>9R^I8>4Bs)tC#8^&T8%{a1eZ z`U`CKrp6>NrH`c(W7C2%7ke>HQBm?Q$Bd-NMdCOAYXJKnhl0u}{9tp`T4_(dT;W$~ z$7O4>YO7>Jd-4esUh*Q+ z?t?q}7@4WL9hZu-XnAh<>Z;{~pw56~ktv;-mDNvhgdf^%u~|T?v86pa{VzK55f=>c zK6EgRV`SiCMFpw^bV{?ml1nCC_sJhq8l-VM;&%cRsD8BbBnLx_oO$2p5NNNGWz?7d z!mn!x0J>aSQ6Z0e0n2mzkCm-$1MbK$D--!!1?zEm3xtX>?R?238WYSYOu6**^u(lz zScQ_vXyml^=AhbTN3YESm%oVp)p75-OvYU5_YxsY9r&Xq9T}NOy2C_t3g*19YTlX5m1%m*rlo#(k%|1)&4XX^e!P9XB%4T% zFeP(c(#7@oPyG77$;|)bgZ(=&{ST7;|Jz53Hj6D^T7lQq6yP$7^-K0gEQl*FHr>I#`aW1x=X?_9vjSQsC$J zQx1Qaf!zwR-zp00GA&bfCFHZUPt-e3R8)iO^GQeu&RcT13&1*F?AJH{Cm3)J`X-~k zDoHi0i}P>HH!MFq&^#>xa}7B!uc7tV#Q}OeBHOo6zH=p6oIihcGWe31&4082qmri~ z*M5raWc06BUEBS;F*Ir6>J&BF9c^6FwsEHVe?6freyp;GZY&4Y#x6tbKsKCQ*iKAe)gSmc$I zv=@fY2vh|szp`aVM8<`8(~PB=aPO}_RP|Qbz^VeP$-tDsQhDL}P-8o~p65vC#xd6{ z85ji?OLy&{G(Z3AE49>BzIXtq z40fgxyEf7DyQSYM4h%vsmmu605OJ5z^-*Z35-M|EQM(Zj>?T;%_0vmicCcvMUf%&t zfrK)wWK>vBf0YkskAY#=&A>l?)v&^85LQZKF}aU&uA{v83^sjxT2Hr5`$xBjoLrk~ z-a7iwqa_YJ1h%EXDgxOw4QPZNmtd+&)aWyGr|8PS4toOa2*7Xv)*XkVQH#ROhO9xX zJT=t{6^Le*RgxFW13-vy zZaG%{E5m1+VBpK7=wZ2>3Rs?9@NZn7r3Td-AkM2`q*4n2YF75)BQ!I=uLZ!SBakZ? z_2E<#4EKc$%LbuHcw01RX}Jwtqyp@$j<>srovf0Sly1##)g4lY2osw%Y2@ZICg{Dj z*R8Hr&`i4Ycy^jS4174X-C0-;5H#{#&cK}(pk#tJ1A{1!lx|rAimdM}Tnh`IX>xy| z`fZG93a&CZXfiW?w|MO4irYP%of;!!W#vp&e3<8Gzj5fK4>k|I+`PrgsH&K0VKg(; z_3^qaxHmY@_UC9OcN@9WCM4DZ>j(sD7W*ccP33vU`d8b3pSe^hjre#XruwRLPK-Q( zlTwm?u#!PHTUQ!Xx}ashS)ug8t`ww-A4{Wdl>Zz0dKV_wOj+FArqjyixZLFfW2akq-eX@Qc55;2D8bls_qhd&#}P}YBvT$X=)RO z`wcLZIGj zKn&*2TfAb4U1*8D+Ett;eX#P&7Y+2jnz#P2K{vYREK3vw%hB(o=+J z?WnNmuhGNs<#DQFjB1AQ*ALTl!0@?6GQD7Eq2ILtMUplKDEtP497^+BEC-*OdBj|- z=vCd6w>V1|y6|Dk694sdP?Twk0?h}~DBC*VJzEOX7_mK9__j>_% zL9*grJQnO4uZRt?Qo+u1iw8HW0KyiqEd`XAR+lr~DitK)ewQv^xCuHItEBStF)A_f z$P<1X@OcDe`tMwYpR`7Bnvlj^mPT{P$vK-JeVeFRdZQ>5w|&V_2>sh;RQG^f&aJ?8 zD4p>%T2c=l$wzNNmn<2?)uD9 zLw%(Fspgq;gDF$iDe3cLLRYpcURuIwQ&rzrQ1S0_yLdAYWk;K|7&mvwhsDm$h4mb} z(ZNK}GdFB?kiYb`2d;GMN1oV*&pVUJFXWeP+x(Zj43%+h8$4 zN`YJvvXivfu1gsTI_at7m!JEo^pvT1*j~DHBdeH8z)VuuteibKA;I;*g7b}eo`WnM z5yzHm#sYa>uE_LiuLlnh@SdVQdc08hzKLgFY+R99f4@o5o^A|AdfMt&qkIMthdX)d~iH?E^2J6bn4!t#dU>gI!wv@r?{wZ0vxa@2j4i&5%RETJ#8uu03C?x&R}J?uz_c-8^X4lV!OgkT62_@& zahg{i|KM>?uH2kDWlf+yqzW*BGqTU)7#SEehGwKa`L{i9%&{{{a+YiUFBdjB?38d| z6*pW1pX~MKv+KP19wlUjae{VMWFx0Labw8-;eR5*k3&ySa<}Lc!m4NN=T+lB+$Q&{ zL|f%Paq;4?$F5b?)oasi2Jh`EK~?fE_JpT3&iUQLQfO=^+DC<=WOi}xi<Jl2Gqf$J9KezSN{?v*z?r zfUz*x%(6~_I7P)k52B+>^AuUqxuW%yuP7`$sb6yTDcZ=emK|ph>?LhF3i3852nAMf z?lHopgyGTogdwrO#L4dAi;F_evs)37dAT!~daq_o@_gCGGvp0t2vbqw_ zAzndL8d;9K))>GJa* z#JK7SNhQ=3N)c}(e*TmzDpo~zHKuV0qMRd>^oE8Ed0V0S>F){B__O06wvg?SPqzCz zQ$pv6e|TYzk4jO#&%hns);*yBkTr)om5W|ho^QPz6s_7^(boCO*GoF0Z@%=e)k5RG zdh|iev2Qff2BI|%scU5{M!^yU3#QCa;^A<*k@oP@r@x?k;o{`ffHJ1x-R(^nE_f!f z@I%L4!w?8XbzH_hEstSna%Lx)&FZ6z00%&hu8PWS^&iUqsHh<+Asr3&xyfMfnz=TN znY5yn-niUSCmV=wubt*TJQW5yq9I;~A37`YuW)p~e&vU{R|@lpW6RqBbGj`*+*ey4 z$I}!>wH|Js_JP>q;k>G+Sak|*TkXx|*Y=RGNT5Dzf+72L{&23%QMIva9T8*J?&i|3vR zdUrgCn;UWZ`1<@wleUz!U@e^MG=tJA+$GY-pd)11u8xK?iwAu?mSZ=dnS?UoD!8i$ zxCOF4IBl_&FLSnS7vKoHs~Y=L{@Jq~N1P6;x-O)tpmaSrl`rP_<322&BA^aZxOGbi zJl-B4S$1~QCnVP%bC6oxu{T8{JKR5*#k|ABKv`oMgP}sOq%b*foZQ}G2`kbO=Q=I% zjA`Nl(6jtZg!5<8i7K~y=`|Q(jo&B{z=5Q#las!qG?Eu!L0?wyVyG8u4x>XpTqm`* z?DbMU^}!q!l4-Gr3H%c@L!7a1HFn*WHsQ+@T|(w0X5@r55;3yX6ns6H`+^_)eRvt);=rY2T{oe-Ij!f@G^#tOm1SqqCZF(k3B*y95% zIE;HM`HFN2teZ2!w2PfR->Q}NyoygGGoH<{Zyo97eZoAMX)qT~TRod*YU2(?L_Asw z2!bRfT`{L3)m1D`$S&5S+e4P-drhcW!HS`$)Wdmt&dzc&gXy}vg5?GPS%$fM;%%s4b;$Yy7O3L9Te)8kR69yKw!#Y z=42&~CgZc~aE&?NnoYlztxe6Y<-oDLyrR7(?+fR+aM9SQr;bOO_E`d15)SqPkq#4y zC#rhK#-n3PpGejw7EV z_Ku|m*2k~IQ_k{6K7Nzks#n<7*?70qpe90o(4#B;s@T*1ZFevL41Hv^$ObyIy%rYi zgRAXX%OUYtTyTOfm)7;gc@3TH>k5^X{A6`>oTg2ENMT&D?mnq6=&R}v8`#>|D7kIO zU@C!7_MneuMPP^x5=o|-C;O&0tg}IH%)~BX2ockT5mi%6g z++Ml0si>Jm+uxA7ja)uO=t`mW$s##<`D!%QTesOMP^s$xCO>*(WH>XYrV=VGPg?4D zO97pSwYOF|F>JFtzAV&rft>7a($U0ycrjpU5`{d?jd;w)78I=z`V>d);*j5EK1|>5EI^o~jgQVrVyR_-R&Yb`Q2;=((z64pB z%J6Yc7^pY9u0qXxtv->pJQQ%rpNLAa^YiA-0jQ!YR2G8lfHR;-(L-AU(WKJtHJ)NO(PSRidO=srT z8n?YQoGEspsh5p6nJM**>izKXd5v`leQ_DZzR?!fuIXT#vG+)}*?ol-0KJuuZrph8 z?%lMM+048=m%AffDr|&D!3`z%&2Q}CL~n46hwYO+A*!s+z_jBuTQAigD1YDFEOWKl zHucLw>`V(JA#rR@y1TX>E6Ti;QL^ev7qG666?}F)GkCx0hSC&#p;%!q%>>)q57A7{ zPX7&0rB0l9EA8p1&F(DtW!H}0k&%#BubN2hS)@k?cmUIEUC%`)LU;LZYvDp49lhg_ zMBV*O-q;mE2ecNA*9F;$9A(sTV{c{gooLgQ$@7rwA|T1w)oFBNvIx@DihDi6>6*FHH(T`-4PNgU!VF zbn~d6WEHKV;=gz=Ee?E3smjwH@~OubPiE$|83>yrZ>=vpc22OZ{aRx`oEF8H(vDgU zgWKz1e5{T2?2kCmMwz|8<2u;oozI`z1WM+Z!;P_`wN{o|3^ z2p9{&Kj);PfLDp{Nu%7<2KvLBiJ(GSN7OX0<@~?G1bwL<)sp*=q<*p65vQrtuoh3H z(VWraT6Y~Q9G4cTz!8dyq_AtkJjAaE^WDGWB&yJTz0WNT&Vhb=l^=nQz&$a3Q z;4^7*S**B{l4gZE}uj-WrlivLdH~leYP~H6Z-;of@RI5LnE!{f`3#Y>fkB4T%A|i|j>V!SI z;7<^8tO&J2Y@F6oU9qQ`P4{WvuU!JAP?rNgTThoG1xT4UsjN2Hkd7>XCgHD8cK)sj zN>3k-b5&(`D;waX)o_nKhLef|2q@MI4z=W?Fz_x5*964}57tHND?sX5RndnxM8eL5rnOaf94S!F<;i~>OkGHednpAKjb0z0qEvbMw7lJh|kmp07vii(5I zqwNJA&A`}j7IWbFD_6;>y*nZ#Bqis+7>&FC8Q?KEG}U9rYzV}g(eCo*?OPuIF5Xp? z%x8bGFM1G)K-|f;uQ4g#FJODZhYx?C-HwuFU?)cqFqc-^xEQ776KWMzfIvEUnLHuj zf2NliQb(+q}eevF&;!$>OJKjV_eq5ESF@OV(AKKhol*ntD@ zWJ8||IZf@I?Q|1mOwa{JUE;MsI;SWZZwW|fU&AtYJ?q7##at%gk=5|o+3F~Pr?3m) zI&h)rbHQYBZm66Jw=FQd$^0c}#Xcu-*Tq=C$(R9NiDz>j9@fsK7HNMIP!r?)uK+Po zeNcE2xphjP{=sL0*53q4osZFpuiDxzt4T=1Fp+6X<}<)tYEa(`{Loc%VXu#CK4y_8 zs)5(?2?VBCNS?o(U#`AB1u7;E9h z&G%(p$4Lo1%(@jYG?`^cF>=GC3jZ%UB93lbC2U^ye3@SekJvK^ec*W@*rH-$yc`_L zE-oqzAhj^8#y????GgK%&@a@*YLp-J-o(~MJN>b!L=T%-4v_BbSxHtyc!Vzh`OXpZ zmxj9}7X;NL=l9Ei@-=PE&Sg6MmjVV{fXSJ0JVfIbk4{WJ;Ja>!KFCF&#AK7TLTChm z|Fm^l@!eiQc#PF5`H*Ez`agZDImG7fBZ$U(VZ8ikS0hk+Mb`apJqRvy4 zl}+Bj+5~sHL3RwgW{pp8O~F_9xs;k%3(I&5B(`*Q>kXO!=ML&MJb`Ss)ym` zn~shncfGc3*%F7ILHMCRd2mQD1oFY8cro-Qr}BET|W-Oe3&-w|8Oa4fYdSoK|Q1wCJmgSg;QJA;qK zI*h=YTI(%`9zS+6|9GdREmzFb7FB}k4r|!-K>Ai~mLSjfeG)!hxsc%cE$IclRV7r6 z<8)q3!S?w0_Ujp#3q-ObLj z^I6Mt+MCU}E?jOSl@NqMt}X#dFEDoCH9#BHbzjsj8^pCgzs=ds@s`00UoqaRp8_)KfS*va?rI@QZ)HA- zFGEV@pc~`utJ?xWQA&He)QYraTFQsC_c9B|6AF;Cr}E4TL6e742hWzhG?}YY62LAD zHXs;9#VC^~1-3{#3ulw>Ri#MtyNvzGP$lm7}Bm4l|mA!wHwkFd#tKJJ{C~|6q#@Q-YJD zBeut{)$CzoJ<<}5>TRn-o~As{+5cz()Dl)mTm1xY1UN@4Yn0?!s8^qG;AHX(9>c&<&Lxg{2lb)rMl*Cly{Yzbh zIJuz3(S370f{WRKoYOvdb(L=!1VR@Cprk7ksjbLs@axyt@j4d5Ub}bOZL4jbWfEGS z)MQ1aGS0a|fwL(4Y{qDZpWV3wPRs|x-e3;cU>-K{xoplqbTRe)7uk& zJeK~ys{6{Is{5ergGeK#k^)kqfTT!CC`hBUNQZKO1A=sybPEy&AR^tRgoL!x(nv~o zcRhPg)&=+OyU>PnUCbz7+r)RIeMa@Kav7l1nW9AvG$ho3UWH zV%wG@ZP+VMBS&XuA>r67=P=%wBHd+M6FM;Ei#)cts%q>PKQ&dbd%YNSML8Fl096hq zKg#$RK0Bh2b}tH(O&PotM+8tf1|Pc)w~@E+J|o7*=X^PO(l!T}jx#-D7p4sD6gO^d z?CkK4d#Pv$D==Ayed8&RagqD_2|A0kyY6kHVV-4~+g(d7+vs%gDbgZd4@cS29vU)$ z=!${q787~*B#1|`Lg=pvkv%Vc&riV4^W~SUynHu%!95f+#61sokCy4lwQLii#pPwQ ztp#u>w1S0P6T_o#Jr0ovvt@eVrNAwH{IeKq(U+-VJPN_J^zqMv=V!?@{1z8u6Gmff zlO4v%o}fS79@@~CK-iqRI{#!Ry8L8|mB+IBUhl{cPC$c9o3Ep6vPP$8Zri>2{4+L> z&#ip`#>+4v=i9{N5)<`iW;LDy3kahTfv(Z`y|llxyUV4PJTudH0X=$a_i%ol<~T1F z?z5@S;FfqxG+S&O$jya@IRZhjJ8|x*vXEqd2$eTft6)S!SsKz_fB2xj;__th&pOu+ zW5>zZHn(lBiTF9HxHNirTJI1jb1W>$&25vzgG7_6-t-^UtnOv2?>~N2ir@A1U7{>; zgp`bOMRU1rWF9+#u`U<1%IU0#8D34flPm;}?SZfG$pb>VzhM_Rz9;bPSl9z^;G(l> zRGzpb3T-n%$K&*QB>oyh{Q36ElJ*%(J7iD#?~2jOL`W?{jZdsrqTqQk60(>TRq?A8 zfkPNYS}-UHxGJfQPTca&#*~K#1%4l@~V^ zipH+OP#q4`{{qNF`Py;~*;=t~F(vET8|IshSwc0l0%Q-|s)4=0dNYSi)Jsk4Tb z+pQNuD3yFuIYIQpEkm)kA@VRjD@nwuSyuYWd}DB`bXDPVkHZ+c;o)@SmSU%ezksv) zXJgRGns$?1t6~y;2nxlJ4kR}Sd9_9*%wd*D(kFRJK z?a8OP44@y+5?Ztjk&wA;CToMXDOHW4(K1v{4`;6Yv$Lmm{u;&YTx@K`Z(eJLq3dQm zj&;JPE9^4jwMMVkf@I=8}N{^+*1#&lKB>R44tqgZ@=f;{jrUj^gEzsJ?GnHfQ4 z0rIsXry}fsYPG|1GE1{2IZ(+1IR$5fvRYnnMj*#?esyH9p`rJt^J{R|i6R0z{Bb$6 zP^|-L23at{svF(@a-}mU3ZyXyR(~0bTJv(9-V3-Ff z=-2H(WA^EnRM%`f|8P7tK2LLo=I8Qjw02(Cpj}HL9xYEk>h3HR6u?y@$+bxP9Txq?ap~o0XY^;^3%nP3YO;HYB%fDYSRu?bXxuo z|MMP*J3)t2-NdW%{B&In?fO}ox5k`Gp7UN6|J^qLin*qx3P)mR+g%#uSbXT`AELk$ znxyWoJ-==;(D}uR1_BS?2ghJ{m#X(~P~&AjN_?RPU<~UBh$y}h0&;ijqIq4<+7sT# zP}=rwWTQYE(tprr#WQ^Xv0tAIy^^M@tLy8*Mu^E0c)HNl9ja@*W5BgnwI6qlt$=n8 z3_la&*wF?54fId1Gf|@Om}${x_2?!tG*Ro#)QY6qRnF6Uap12ceQCJ*C8gSZwb6gd^#a$SHPUpaA1u(uXyOCb^V04T! zIA(CjQVMY=xzKH!7I5kPoAS{Pa#*LAz%9swT2m}DIy{F;w~xRB+~imx2Y#^i+0S1P zPJ@1DHQXW#^%Dq}1{AoIRHvIeR>+Ql>H(dcw6vw)xla?;Dv)MaGU~4P?|(=0l;EuI z*e|4J8`QoArqzDK&<~hxI}^Da0M{U=-E?8lX`x#KJOgwD^+2!-YG?$YrkJF0dK3;F z;VdAxt=JZK3UeO3q9Ffx0TZj6jQxNg3#%eF9cA z3R-7HYzzyaV=)Dy5A(ehgP%J!kf}!lViqw7hO6>MYZU9cG%NA{t|e9h+}Bc zg-MV&gLX=2_}yC`ccX{;tFM&Di|ETRRKye#Gz0>%gV53?2^7F7S^S26-p7xIm*`G{ zK=tTLyJ?BOo7I~DB9EQ(k*e*&n{B6Flj}Z4mq{@0OOYs(L^aVm`KdDX#)z|*6o=CVs36OLF`lwilR-9_s$>Oh=Cq(n)d<)4RamK z7VdzM_|lb*uYe8XF)n)-9nCqI?O}pH0|fLnK2yay0G~F08c7r0zP+ENZedAwojjDs zZBG^c+7lO#wm2e6aZfflBYcSL-MMtdVC;|YI<?RM^5#~F ze^&YT?|M8?gQZozaTz)YnQpJ%jpIr-$XMq0D>9jANEFF}NgAn{nVEIa{@e^-0a_db zlpPdwVw5#CAT#n<3jIoNx|Jgp?kCG!?}yh5&rfC)6UZ9eWGp2u&2ZO|-PgC$Ow?d7 zzq-(7N*+Stni16QI(;ydN2$N{HIrw2jSE(S?)%iygh-aYa`CLQ7!8aupD1 zm6}~m@^Jh-?%?9$P?xQtv$54?ZN7>FoLtLx*P!U+O5ZIKg}7Xw)YQv}{&%jS6kP+j zCD1e-&X4k}&r~U}$a}~^&UB|#1Zo3)pem@pB7KA;v?)Pon?OqDv0u0n!PMr@BP*-b zjicrBM%HAGJMyBi4xonY2i3e&eOV^uP^e%Y%2U{z;nk9*)?%TX+{lJ0qV5wD(rlAEoLT72&cNK8$^C}5a` z+3x|&@$s6%FJJCjh%SM?@#_R=L8F_J4o@chqv247p^RNghDF9ts-Q4Lv=0 zzrWDt4pzY8`-9pNLPA;jiJ-{WyHdd&BXC!t$2A875yRS*-G6@lM(=Qq@&*432^(o# z?d?i>N)1zQG(JiHR8E1yQK}0Ud`U@2bk9G9DZ(M)=Dz6T!;sE7YyBN=TW7C0M*ljy zU^%<*t19Y3wtjnvF(tv}XWsA98Pq;GeKomG|7=M$yRf+UftHq+TKs(}X~*5Aha69N zWu}XQf>K-~!#oZOFP4~AbLF<-XP*6L;9SeSzii0?@pC&%`1h)e|RrSm66hkX(c#-C-eNYU(}CD7`$LUd5!YEF+6&nltC`AMw`W zXzqPXjEP`oWo2MuQbHYL;&@(-0QKsgs5JO^J(mR^M4+EN^DQOsvx~rH5+Vfc&A2|L zvTFW*#2L>dreCIml#t*C!+~<3O;Zg9l4CMbJOV#;3lx_pS5|yE^<1aGQ7(?&yvZxJVKRVj@+P!idJiM0IF?Pgo!vMhG63}>0H5mAYhbx88qt7NLjw7bB z)zC+=Yz`I{Dm=W}>9sM!DV|_vD)F=O^q($Z^gg-mkN4|OLluso;|c;gi^6<;@7;`* z7ZeoyWH+zu7f9SwQKVvSe#>#~gcLD3Gvhm-M1zGWcR##FL_Z!_dIt|?*ce(_y=n?I znfmh!8&PQ9t9>&Te`sjvPZLGUlE*kel&LzOv4ia{9S|-LOiUDo14eR{Uv*t60knqz zZ`f?jkUld_hbgxgAb*W}Y1Jn)^R?MvCk)XYK3M&#hd^)@MC{UKWva75l;@`q-^s`_ zi_Oc|ySZV6*J#WG#g$m%wGun4BDmFV#N96Wkcdnz*D4F$NZ{!Kb5J){MHLi!j0Ht6 zjjwQo;8cT^A!cL@U}XG56(q%2y`%`&6c&pK{wxTOO)f0>MbMt|*e{3^*8I?r%(V8> zd;(UpyxhQMZIr&Xvj(R~i;#jsU}y1i1gPdWjMuP$*PQ;9#)t48ug0-Rd|;93=>(NK z%r}xS5j%@~xSpOE8Y7fPC9-;;L-Xg4=Y8WpSYX?}4-622y_b<{=jRB7r)Q#y zy1KVNf%tt8Czr1J@Zq@Jc9zlebeEMw_kb2|RkoV zQOC<yvo#4!a5TPVI?kzn0S0|#yqfm(=%L|AsSE#$OchN{w!W!l z8t>NDo9?H(7eJW0Q2zJUmhF`D_FNfURU1dgth-TeTi+xDBft+T`0syJ^*qBw8+t%= ze`ez+vUv(MYQMT{i#5VfhC6g!9UaoPwq#IF8sc?+Y~t+VK$0(R)*a=FWeci!&hv>X z(AbqC_F)!MYK=pk6%CLeB3}H%120t}O*-BMBp;zsyVg^#g?=3KeB)!Nr zfc(1*d=4Hs!FzG>@u4D4sxJW!;u5WeBqTHm+CCHK)Rjdbq@?aONUT}=`CLO=Ocg#} z=M9_BpP88e7*cR0-S0~B$SD`@-M>6+BGM9*Tg+35#2>5 ztff}V7+Ix+w{8i1RA)muL@w1Fnlcd4jbavQg?;>(Z{xJ@tT)i&{)lBEzo88 zJW;pV*cx{h*Qd@-IS|4>*Vyf)XXRpeC+2mxu`exPbnYn_sE<#I!#y&ET4+mKBI?HH$ue5Hi38i6Pbkf z@4-SY%PPgfhyp#~Q0N~pT^x>pnIP4w7KE*P zQUz?_=fB$m1PV85;JDsIExmo z#{-@?n=eRFNy$elnh@-^V)EIoZ{LJ*yM8=`k~48W z;_rZBBh4o+wd_sJp11rM>FD$m1lDudn0z~vPB9@gR-~!$+43^t+{2LKWv-9b+Yk}(z8r#Nm?OY@zB)OZA_T`wvDkTGID2qVnYai`Ti_SB@L#Zr5u!3@C1oyiM2Wj^vnMoo8<8rmN6DbWk+!i-V;$DQULb`E)AZa~V5m8WwH1PnqEPVa{PltoS9i3u zwED74D|Cs(YY|Y06vCz^1aoM)U;ZHkK@)Os3w(VCwaE4Hj=G-IS_}lh7jB5y`X6<{ zRG!EmF~~vGorxGrOH24JzWp+}rWGX)0!TWoavK)yFZKw;$=+|yaC0C2A`sR7)0C6r z_Y$$OQo=+^Ma6QWt0oT9)BEc;tJWXolQm;_7ZixkbsT4pEdEJ^C>!8nIaCOh47&YGhg1Iu&hi{v7PbY;NWhn zhjMRsxflZ)ss&|^v%UHPkpg%riRp@l!2Ju#O4GUK+rCTcFA@NVeB;(!uGCAFsl{Vu zMIy2tl7WWG+Z_dTO)0psNS#c7$1G@LR*O+?pla?YByxhwy=d)M8tc&>t-8tY?(VgP z331f}NN&1VHAi?noO1eKS!G~RV^P&+_QBfLYeEwB(2=8io+ zPP+_^Z2$)zI3JK8>KX!B>ffkYNryS(ftF3qrpvBGe&zlw+C(W-T|$6$X#hV3bFg5u zq@>g@Ei3-$K7Cr9{OxE-FsO|ABQrm^**@0?%$)zN#9FtelER@HL2pt7SF-EgI?FlHe=Pi~z|! z=>JIsUcuL6p=s>TiT5+n+b2Xk7TwLY`S8YGPC?=7Ze4?N zZeCtbn!@8et3H_-jr>~~k32kzJWm;P8q7v>Wo*zV< z@D~@q0xrvRuz&)?qE5S|C1m9|#qQbUvW~;oPS(RitR+hm#ekJAaLxW@tYc%MMpjmN z+wP#R@(Xt_xsUQl&qF-a!i_jCE^cFEgG^fg*93vgzaf{TWZ+qu*l3ER8$-+w5_)KQx{=W(dTTsnWcv!D`e zlWXm9=_P&N9UH%Y@z2ca7^$bHkE~pU4_wX^wT#Z292^Mcs*Y=j$<687dLj=28i4${ zd%H?u5#jz~A`&4|0W|Y3PY#TXzN#V65eHQ1)sKx` zCL)R`EG@m_TQcD#?nf))d1BBOKy2QB{7NggN*rJSY6nLRm3fxC&Y4oS$lAQ-bSZ^l zoWer?ze}P97*+z!Y0Sw2sD5MSyC&OSD;_qrQRLWv$CQwy`v>r0ygO3 zLor0Z$1-(4OwPUGN5S}7)&&2VnlTDRoTZL0!Ox~Rh?^Nrw#sxJWinP-;6DwFcYz~C zuY5dbBL9%XZVF0%Ip(vfc`DvCqG1oYTWju-PLMNVzK*tg0jiM0EiW%j`I#aep~CU^ za9;(J8fNCQaUBQIU10DmZ;(?hxiY=R@y*>O)UB+?23GRgUmdST@bUFJeDT^!+NEXa z9m*^q&FmV{4A4GjU6j%Exb?@SeD9x z#o<`{RXfJmj^e(qyxW8Nhf_@Hb zUo6p22T_p!$E~AXsl;WZetFc#Ckb*LmobIV0oNx}%bkU<8$a25oM&Ur!f2tq+kqgb z*QM`OHNYlwtM^G~tepDl9&agvr6BlL&OlMWJ9<+UNUQ*&%!|FOx`JZ%W|0Gnl@OMo zL#}KOE4A%DwJ!V?bX>xw{46n>RGr75PW$_z>@E|4tHiAaq(E=V^7=?T3O$ zE$!?9>2EeucYs>aG0}OenBGHAjVZa0-1RJWSl-u`T+7oLl)MpRp7#)M@D&0q~D@5B8splk->YGXj>@;_fk%j@QTbY(p5 z2ch+?ktN7;3Ek0ri{H@0YrOcYqCS4R#sen>>VX~D$LoNy@SC2Q$q{y%gSj9D+`QZn zY)p7oWj2qA4dx!=@2?{@zXZaz_7${p^1N;EFAEUzMr5u2~Q~RJE1ql9xT1MdXV46DoR&uyc%JKH(_b$BN z9!M^re`awGx&g=HE29;1NRRX-r+~YVGOV9p=}dfIXg$%%W(BI$g~m-6z>-r{-LsJT z7iN(zf_#n6x6NkfT!UIN#K!JmGSGz0;5NTEG!V?rs-k11qg{6Y>-7+$gVvgWQU5!j z-hm~%38_q9fKZBWx9$SE?$f$AJ}4lOki<$B% zl1NcDoN475DpTOMC>C3uD+9cB|Gu8hsx~78Y>kbIQuaH{fT^%WMBHX(MS-plEHdx) zg7AzGSZ3MB=e^2bbK@5lfh8P;m z5!1~`aK_Voi%9kw4>~wFbuEKQ3ItHhVzO~I`pN$`KyxD4M?zp-p>b()Yu1iZ(1sgI z+~6(4w&x!+2npfcy?Yy|GfEty6a0^Jp2i`bNHb7nZ3giSleV~Z3afZPnmA`rtyK*$t+EAp|pC?RY&M8I1AGF?tqazge5)d)~J1Q6|tpO%s$Y_{k4!;f(g z^z;Vpko;lSbw@Vun-KOkKiK-j?+Upc<}$tGk=An zW6E6pYFcQb3IqO^iI0z#ci2Qwr0P2JXORG6FT6xyEJlW@0;~g6)vNUw;IWF_4_zVF zl3=RKB%HoRo5*ddcn8kdD=@5s%i0cCuZ>;>okCJ5+xM5|)};D|g&kn{^UbiUm7)=T zz*^VT)GOe(!=T!EYGpX4wWFHY&W;DpV0YENL>T2J891^2p@aK6;ye#)QMk}ziHByS zx{{J#0*?*XE#)DBj_ zyR(aYit8)CgAnk(WwtN8IGHIzNj5(b_#TTSR9^el+mmc-vue-dddbem+Uaau@is)s zFp&EJBsDlVIL`O|E8aKH;dLn8zb^)N8cYX{GFz-!|4%%Cpk`wu{e5X~+aX~Nx_nLu zbv2?uVy~X-^GXU;;oumYfo4Z@7IOnc@V}|6X5VQ8g9svF=&M|6F$+Yc2;OojKUVWf z7Z{5x4=Tz~UISWug`CuIK4Ku|&s2GJJyyOaJC^B-wfM88Xlnme0v6?)-gpK_^|_lT z6*SlW#fB=0P~a($weg(~8jyEtT7e)> zK?8K?B|UO}#-kllxXeRCT>iA?IKY57e4U@`Uf-z*gtBVm)sX`KWG1Fm>SAUKzkdN1 zsR|VDo^YLPwP}K_%iPj6GQxnV^g5tj!#Gtms854z-^wHfhBgpQGz}HKy2;L-v*ix) zcY=U58>|&VqHMScDh&HwJK>$cNEe#*aDYiHD9%D>S(`$U=!;$MwoinpgZvUUq4Uox zY0-Euf3l(B6Dg_2%XR%5kW&wUSSAH3Pio%jjp{>wT={>C@#4j*p9G7@F}X@0Lb|HvqEcI+d~x<25?!BJVmb@F5V)M z*ZFlc|K2%#?MQEZ9JA1FUS+iWNaDwj>FrlX2v$d2-c_pe zGW_clq}1~U^7MF~wc0?2D*QuaBp29~OO7?Lv0&HWNf?+vzt2)gkq&t;O!9a8NJ*(k z|6)5dzGGxE{_lf9s!8hKR1^BZh6Vj=GvU`>ll}{r(7(aU_}`nuYDhQ!rQguM(HivM zuE4MSFW*Gga_K_mqML4tgwZ4athVcnjM=dvycJmr@Gr(swYRWs)vbjVp9dXx(V}y3 ze_wsf+)=5*CB=6oQBDf~d|RDsz;P~}(+B++XQq^`Dv4#=W#1DPSC#az|NYo1G4)Ec z=%4@d`oH;f|0hfMzk3tryABxttX`RhHizVYi_M6s_O24aWc2q8D0P;_V3j)-*Y!O| zAC`W+zvsk)cSSq6XJqWxKi^i7y!^J(JAg%%E!Hc($NS$ck&?R6^8efN_MGGB^b$yS TiIrNStGDDHK9b6RVCeflrWf;~ diff --git a/assets/img/tutorials/rag-blueprint-overview.svg b/assets/img/tutorials/rag-blueprint-overview.svg index 6fb8c1e4af..f6a9d0c7b8 100644 --- a/assets/img/tutorials/rag-blueprint-overview.svg +++ b/assets/img/tutorials/rag-blueprint-overview.svg @@ -100,4 +100,4 @@ OTHER DEALINGS IN THE FONT SOFTWARE. font-weight: 300 1000; src: url("data:font/woff2;base64,d09GMgABAAAAAPcIABUAAAACJSQAAPaKAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGoRdG4GeGhyUaj9IVkFSgw4/TVZBUoEQBmA/U1RBVIFiJzwAhTYvgSgRCAqCuVCCjUsLhR4AMISZWgE2AiQDijQEIAWNdgePWQwHWwsLciPUuFunhKfbEACk2ixr6r9nDdsY6H3rPE+lIgbUNXjTYym3Y+cs/G8s+f//T04mI+62cmurr8/zDwBolAYqhV6iCC9ea1taRa2L9zFaa22tnLWyn3utEZwwYYsTW6ObuTAjhiBIdOWC66Ku+pJCUbGLUPeuHKAogidBrZwJ7htKBdOCaUFSI2+ryeH3awTm6nhkeaSsOY6Erj/XQXFDex2aga7YsWsGagQVF1SInrBleec8pxXl8/3pNyqNKomD0iXlX0ae7cEFn6e/zDwuETSwGPaXJHHfh7iC5aNPEMsocnxRpk/MUCaIIn94F8WY8v2LV4Y4ckRqnGA31VWSWs/+NBiMC29mWELaanLCVVp7Mr4QUd3XR2RV91gLvpkvEEfdIJ0WNcTr5t+9kQkxQAgBGREwAQyIbBUMsmTJWCJimMuBLBlbNzhWdJcqurFILSojgCLYFAQsWmX9fosLcYUQSd4Av9z+7Sp2He9d1bteXuQutl2t+pbFGCWgRAoWFpFmMaK/wnSIjR9MbBCrYPg6/f8zRjKIXfmB+uBm89NJUnwIa2H1zF66t2A44Pnvx2jnvr9qWRSYTvNEtJBVQlNPkcrGHTKhkwkR3sA71U+O7Ti2BciWJdkycBxum9LGB4w6hI/P+yKGvUG7dkAlSBr4/v+QvDPz39tG0oS2W+vYGvB6yvE9oYCFJKh/cK7MM+kR0TGlcG0KSSkFHJCcHtsJNylevh1giejHfvfSN0kfTZosFDGJVLFMJRNK10xi2IE67V/bLtsu+MRvp37g8UM38XBJO0XKNPAkmWCEDJTMTMQKC3/AP19n9YP6hcgNZdJMk28WPu1593ZINQQPPfBt2v9DtE1V1/Sqy+8zmW2PmK1oGlFCgujAKMOIOOIhZP5tav/3DqBk2ZYdWODsI+L2IxW9NYoDH+j0WGLJnM1SwiZJFs7wJZjKWVUu0CAWkIvQrrJMYrIILJNsLqKu2NDS60f83X0THH6BuA9z+p80GgEb4sRJHuEC8/HauL9NF+i4V0A+A0H7ABuwY0unilLbqtr3gQ0xnGEhiMba7L2oImapkaKmxqROc1apQGAKEBbIigsAYx9o2X+CtcAQkKO//vSd/k93zox3HTdNCiooeJHtE6SvZ9hExetPJ06fvxh7fHwcxydJHB8ff0meJE+Sx5NkPB6vFSiK/0cABLi2iT4mFm05Ay0RSuGCZf7X/UenvyVt6lBm9Ol7qfGbt4AxQ3P7L+mV8H8oCSrUDdeb+c0FOPn/v828r/X3oDpCmBVFJmsiSFiaEDlb2qUuw+/Mr2IhKySDR9wBnU4JB4h2e+5Xutf29m9AENALSiFWlyu6tOXdfXtGq7UMLOuHQEZK16QqqcqkqPI//zqtX4kWgZuAwZQ0MC7vCWy/JyD5vZrq6fZWb++Z2SY9CfSkJ2whjCMkcEBgh2ZGSICFAEMfQ3BKdTyt2UnmnJQpZOrTU/FTgUgIXCDJGnCmeJItntZwqh1vycyWVv5a8WzN/mX/Zn/+9nP2t/Tfnz3/+/218P/P0mYz+XO3z+5SuopCOHReonDRqxTTORn+tu5opakIh8VYUDmxuKAUQnFQgv+bWkVVdveue7M35b7djG4A2xABO3gESr9+qVT6Kjm13bbV0eMJ7p7sScElT5DVvSFFFCIKKQNyKT2+d4QcgImwY8fgAXr2tflloWaDW31OclnqGwZawL8Qg/DX9HtD2JCN85H+iX29s5nXsKsr4/DICJPCqj8v+xzJOqAo1LbIeEVFKHqBRRKoJaF5HAhZHwtgk90MB6Fp6QwpehPLAih/ku5u8xRVyqcK4j+xpv/n3HG6mTZjNmMNTQgSmCoiArKLBHx/NZlqkpDA6YcrGMsk069t/v/H1P9jw41p99sxLlysISDS/6E8if/Oxm/gyquKiIr68LiR//2eZP/vr+0liEgIQURkkNlSunX7J9v7+Dmec5IkSVbWGGslp+5987quNvNjjDBOLVPelGw6ZM4KXrQv9rXd7DDC2mEkd62WuaplWMCe+i9inEGUnAMV3ZYh+orZc0PzJwRFE2ANzAmXBIEZdPUQNdQ/YBFMaOPCgAwmQJgzwpYDTtxYrRiuXLGON3z5IUgoIkVDRIREaci2VXIFJA+QAkCKACl12lWqodTahT32ygGBUtej0iBdqEMmNNUES7bNlUuBfM1hpTXsdcRKV0GoGzzlpVzjLb+6o4DAxFQAIhlSYKKBQEIN1HpWJi6Y0smnA6SwG0LThQFOAbFsaBwvgMhucqSMowHH++m4e+n/bsBMUdK0zuRkrQRBFLWq9lEbNwRTkgmShOsERp0tq+S9FrRe+/TTTlLJ5KmR1EwrLcIdakNNAkjFqZ7ih19ptsD7oQYOkEAxAGtn4ySHJ9hBUqyu5Qs8X/mdP93k4S6cb6FoG1l9P97FfVK3U3f7PJgJJPvojcfUcHkRDT7Q1PALiBd5DHmJnUwWPxtjhyNbJEeJlMrqnCFUxUgmZE9z7ZXjxL1iMrm4OLoaAgKgCmQYQCl94/u4aIVDD7H2XqYC3O5DCwM36EEPBgZO2NpaAbofZKgqs7EGBYffCQkQQSRZtjylah103DmXNeroT2AAyNCgowpr9nN/conLXOMWd2mhlbZ9EiQe04d05zlEXbZf1DbqivBZ7KQR+/ZBKqAQpGsPu5l4iVSJVFhZKblZJrJPExmUyM/FchsOiD/Fk+JXcUSCw1Xxv+iUKK/2SQg8MMgk4q2ReOS9hC510Q5JbpX4RiZv+dIeVantNVsrytIexXrRr+3n4rU9lIk78ar4ruw7catwRloU8gAA4A3wMjh1l6iogs7ML6zGLDdsA3+Vf3dOl8FH5X+C66HCvM7Ai+V6UK5A9cjl5zvjVlS8K6frY6TYQU0l+Z8+oL47UaGVXzoCmrJ7DWir6q1HZsBzYE85o85Aj/cNeUlNHmRCPlD+fyMO1j9QXFbVK6qgKXjb78kvKldaDjTPKMvU7zqB/BTUCH2pWKI4BA7ABv7MqQbA7ZiH8ZaK6hxoOjTPgc9HwQ1WlPIOW+8q/1zHGKZtCGmC16PKDslq7apqWcwJel69ec1I9R4XQMwM0GwByCr7Hs8HrjrlegQsqQFw4w62ba0flCWDJajJNjPb9enLCpR7zDJ5RfUnkwha7Xhy1WjwMHDb5iF//VsmhETHtC8w2ZEUGa6e7Ksgzq0JPlO3acQLCv+RxJ2MgCKmNbd751lv8obUY9S1Feq5b2PUPVfs5durPiN5jxDzydGhlb/tHIWuLa0ngrWrCIiXsqMs9Dr0MUz5uhW1Uy3GWHVnKHOOGbXiu8Np99t0t9UXcffV693AvMZ9sxRacBtqy3OelvRd3aZHadI85tRvmg3PFVZODnkZOBkL+plZEbwpWmW1uBLzHKd9MQbex49GRI6MaNTL1UuyF9tuyYoL6dCuU6/k+UQfWQ/vgvwf9sNaEjnfzlAeTEJv2wiIYKXxIzCB5pyIXzHXx8wlalgRz7hbSpV3Ke4+ozBQSlyli2P+vTyghho9SPBgnPMzhizu8k7R14Y/cwsJv0ka/vIMsheNYQyDOZdr/xodfvbn8C309NfvMLYOYjpct7Ps2W9BM7cHvwcer4fs5sByu2wc4o+U5NXQUeDD/XncpNcVtSIqpRzX8atrC0TsKGmt0xqq7PNgd+ygmdt83hNu/5upfvEzcF2LvVeHCAuqFu7J7Xi5NLvnBMoyrmnMyqv5dmsan9WlEe14EsTCvqIWx6SOWKW21a2uxkDwEh7Q90NRGAiuCWMtllU+4rfgmT37zf4kz2r8koc0uuYbWzArPtnb/BGhOf9pXeMo5ipHQh1fNQv1jsFcX4QJQYvd83GHM0YsdwT3S6D2NJvcXJoZFov6NUw9HXrJ2wUjfTMvOCsox2fbNqKBtor3o/e+N/FgPWPEJvB/n2jb2LTeAdIYN1S/hgklvgFqBqI8qfs4nc+wSbmnPETvY6FjGc3DslN5cEVSX43w6qpxWMofUc1lkwVt51cgIr5rdBbxck57XGMF/JIM3ph01KmwzUl7tXnTuaNnXBNyio11CuO9vYu4Is8jybyL8IbK6GEf97O22FeqFVaUDCercAQMAbJuNo7+n4Qwy9IiMQVplV4Jycalp7j6GlDNp4BLiU854Lu6vvowfba3j13YDde+B5C7DCtIJxozxCqzj9rnfuvWmzCeii+KA155uS22G1R5r33HbsUcxYieo1VC8eMoj2wz1SEDLnJga+2yB964XzwnbD+2BwAITpQUW+Mmb0iN1+0+e9ges6ftr73iKtVBEsoMHCZuDYUDJaUxRJbgFLLUlg0a0Ph1yD06ffv9BuiNt22pPWpP2R9tGR+o5vj9juy4QAF0XPkRSbNVUbiau0o3eUEBjOPRzYVvHPgmwD+BO5v8SY7tJM7IWasCVJGqeFWmKk9V7ux1jt6bvuuccxqdu87DpBio7H/i/AtA9KBRQ73dBs3CxsHFi0lIyYDBa+k8DvUxxBhrbLHHEVeE09/5nWNLuevkguiXdKbyu7hs4m64TXfRpZPC/xR26zV/3xBg9FNtuVMcw/SjvhU5KjW2ZGr/w+Rl8DbdjwS10YVkKl4W/ev03O/xoCe95I0niVe02GFflEgx9qD4auTfUXvdLp1nejFgnHmt9c6DMatimNf2Bt6fzdgLfdHre4v59vjeL2yipW4b6qlMUlYWr3b1RV3xGnZr958qn+3FJtprvvvs/hS/74umAAVY8jQnERN8kGAhomyiE0mUJFmKVCSk5Nhi68jzFChSGq58zTTXyuXa66BjM5FkVrpqrdygvJb9WyW8MqEikdvGnL5F/YElmEzviZa7dh3Xd8M3dtP39RYeyepavGVasTUwf+6lP7fn/yJebFLkVge39O1ux5fbvg53rNP92gUvS3K7HqAedk03tr9ZqU7uSn8jumqnjqSsjDXd3KJuqbs97Jmnyju22p0b01476ntT/6hsAIj3Jp28izKHGRTnX5tcV+m8vmpn+LGcZyBtN6NN6S56W3BaPqkQBHEaxTiol8WNuE37hQqyZqcUe8ZUtkgkB4k1vlcTj4ZFNQyHerWXvUDxsXUCSuveqSJBVmSemnYWUxObIrVjVngTlJgnLcFeYCGs+u4X29PV4LuVGL5zkv+Fbok97Czgc43NEdmt/o1bubVpbdIFyL5Q0YacvYBaxFRbFUO4FrIzxQG9EsKOX2Glq7+QCDRYFsWakERyChIC6iTs1MU2zCJDkfGHY00Hix0VmKsMyhqXj9Z77IVdskFOIo17+r7wCARmeaDMQUKvxZwwitlL8p+AWiqD08euGrGilzGos+dmKKiG/NCWCHuHupQ8iOcqG5UwmkfJqbCpsGozINkEwxJkGCjvAWxpe0eqdEBXa6/EQDDEbESzAGWmWfPVGN7YBYOAUVO/rIaeXgPpPdTU1V0jTSzUpVufflJDRpD9ih5b67bggiMTexdhERyBBxVKlRwsq9LKjsoz5PhaBQOTQBDq+ep0hZVXzYmXFCq0IDHLtcIyQEo/jzFNMM+cnsX0GcwS5oAcb8F6Ht5Vxb77onj7D+fwEb5EYsSKEy9BknwFR8njKJIrb2skwoYbPvBdMCYqi0GsOPESJC1FyqcCjpJP4xYn3rR34JF+L2MlLrXXgU6SDULDNIZxEybJzJGbp6A6iMGwjzZiBBalBMCioMSrDtFJcsol6pTGxLgJk2TmyM1TUB3EM/ksCk4iP2mv8IHv/Ji/9QIEJvIVg1hx4iVIkq/gKPk07jzmC4RH9B168e+lea/mqjkAR2VVOKpkYq4dHXVC0lNfgzSMMeMmmoTMHLl5CipE2JBnREtPhA1gmBQdyEDAKoonppLL210X7iDe+DxN7sn3e3SxP9yVeKRbqceeqSUdgR0waMRBo1464rVJx/w3ok/75uaMHxTOUiHAXfnRgF+joxEHwscLz8RcJ56NUC863k5b18tgm+pMowH0dSbwCmnaI9ZWu6MOdJLU5a4bffpJG4w3VDZMIxhr3JgwSWaO3DwFFeJmOPLHStQbo6nWao/Qpl2HTpK6InWjTz/pBrEhbViMNFY2Lk1gkswcuXkKKsSzwT+WG94waNK6R6ztyhUdOknqcteNPv2kDcYbKhumEYw1bkyYJDNHbp6CCvFf3CxHaYY6GJrWwu7X6tgjqo3aq3Wgk6SueN3o00/aYGJDZcPaCMYaVxMmk2GO3DwFFeL5FMbg8kakmlMuJ8wTgMubcAIAzFMpJv3NoKmWavdZK1ztirbaW3Wgk6SueN3o00/aIA2VDdMIxho3JkySmSM3T0GFeFIQMguNMLCZMLUUD58ZC6sJla3OQ9NacY9EW+2iQydJXZG60Ve/km6QGioblkbYqG6s2rgxYZLMHLl5CqqD+DJ8j7dtgxb+HCSQUVDR0MdBuvQsps8ggXuWkG05J7ayslYV1SpZnVdNphZH97NqldqpA50kdUXqpqeqT39SGtSGSsNGjDUuJkySmSM3T0GFeLIoOHHpkJBRUNHQp84Y2HGELj2L6WfAuJIJmWIpHj4zFgmCWZJtPPsyB+GYk7RSrE7IXLG1yo07D5688o7kA9/8+uWP9QIEFgyRmGIRJ16CpFIitpTXVb4CJZX5qkDl6txrMt2VfkNzLVnurxVqT6mDOpWkLnTr8aSnnfTpTyoNpjdUNpxipJf0Gm8aqzZuTJjsm4PMHLn5FFAdxONUcFI5FdOgM5gsdm5wnuQjEIqKc1uO9Jf9wZEWEjIKKhr61EKbOmO0qBoTGjRpYdGOHRlXMikzFUvx8JllHmTRMkkQz1LZsufAsZWjWmU1Ya6t1go37jx48po38eHLj7/1AgQKbtMARWNzIi0GseLESyjRLankUoq00kWGTFnljSpfgZLKWlXMXKWuztLUXe03NNfi635urWXtKXUYnSR1UTf14Imn9bXqF1JDDUsj0mik1+pNY47GMWGyb5CZIzefAqqDeJwKSi3ToDOYLHZuJA++QCgSry8KAyNjE9Nj9g4uRs0Qbqjfw7AIkwZNWli0safDOHTpWUyfAUNGuJYwziQxUyzFw2fGnIVlE8AsPbJSy1lbwWa21uxU9l3n4JWjzIlahdU5+3KhNSVhrmJtbnDnwZNX63LnrfMxfPMbO3+sFyBwQVhIvNCBCxPhImwQ2UZ3UV3bFC9abU4kxSBWnHgJJVJSKbkUKXVppaWXLjJkyirbV84wtrS12ja1vdxWOygvWH6/CqRCqSilnVKxT8mttPpjGlC2xsBYwsVLYmuS901O+STKl1HLNENnMFnsnMTcYB75KhCK6rhLetcVqUxez1d/7AoxeLZn8ignpmabWy1SLFtV2xbrdlrt+o79d3ApOCPfDEmQUVDR0KdOGNhxoEuvxdBnMK5kEskUS/HwmbGYAHOI5wgnK1sNYa7u1go37jx48so7kg98+fG3XoDABZtChy8M4SJsEGmTaJsTpRSDWHHiJZRISaXkUkYtdc7SqqUbGTJlle2T05aUtmrb1PbyEst3VCAViZ2VVJVWpipUrs5Zk3aXfkNzrb7ayzpEJ0ldwbqphz1WPSnrjfdU6/PVD2mDwzdUNkwj9LLV62pvaEyNmzDpG5k5cvMUVAfxiZXzCeoDJ1foDCaL/WxTT7mKLxCKFpslUtdIZXJ9hYGRsYmp2ebcgpadWlXbVuvbrrx4BeglSZ31/wapUWnBQDbF9Ep1pl/zKkW/GrRCUaRM+aXi7cf26NVvLztoqDocdtIpYrcnlC79S4e9MdKJfVmlF22v+lzuj9+uedPl3jg4dNSw2cgw0EGxyTAxkWFDhmPj4BCiYAAuMXgk4JO2cjI/icOEGhYLgGUZQHpADLaCEQyHHZTsNo+Dk4ALKm6bzCrFsLiAqwx85TYZV14YrLNJePPBVGFT8BMIIwhaqsGSUtqbgAVxJ2io3wvZZDPam4OJ5IC2bhjqsQV6ZUHIBp0cGBmxNYzajk4uuCbBNvU3jHyFSBTZCaF4AFICjNK9CBUqaR4tUR02lbua4dzTZcfM5mT2fRsDp3WjHjbhTsB5ams6rx8VKagMu2C/9gbOhMlw/IwrfuSMk1CMtMaVB/KV3z1GM83ydnMtQWm1c5hZ/hMIWmtHgfZjB7Q46FRuX+UakArJIeqaY5S1vtG6yUOVp36AG6+PdDLcJrJ4NYVJhHfaMHvEaBKRKLw5n7iou0OMekdjvofBYmiWDYNgEoakG4SeTLDJL6QHDcPqoXGOKqjQsAvtQdCnyU9rg2g0Luo/J0BJ5K5Okt3QPfqUOg4eCgY9izHof0j7uQBKIdG2CaQBvUPKGEJ5/M3CQke/U2lhS706U4cz4d86jpNA9utOp0ej8iQqWOh3OkOMzvAQRqLv2qiMhWzZQ56M4nwEI6J+ZJu09u7zvOirDw/qaQLsF7MQkOEU0rMYowKoz0QDLWen9I7qzAIR8/WjHZ0FUIjatUUxDnWyANQLA6SJd9u4Ntav35w6EkZvM0gMnEbR12Y1hgbxTinqJPK+6ju6ZnkmJ7Z+zdT6T+M9sjY0B2mPet56krp29TZ7webV1ppp4vZqbkbqx3WtaakftWZNKhqojjuSyrZqf4VZqu9fFhcHlT3NqRESZe3z3F5jAv78sUbNq5SxX/T1q9avgmbUsgzg60b8/FC3yjnJ1xmG28S6Y6bMxF/dWBkOsTFSsaqPXKfh0y3Av6NsF941AagsSBsYwaUKS8eocUqqLaZVW+tkpC2IfZfvkIU2QvxBQ8ydyQuHc3vsroC190bpuMXT90cuoErfu4AKAIzrhQlLM62yARKYkF5byg4A1cHDVXg1UgFGay4vjETDycYjr/YcnAhFlBkB3+VsPM9kMYajpb6BwfTWiBbSa3Hi54afVhyxfGKZCITmNBu3+QpwG5Buf/wGkP8hb+4TqxrfpbLs6yfDIwUSmCZeODz8nLSuSRM1lyAoemLvLL4uOydnkBBajHiCowDawSepXlmIKFW0Pe6NARrLZy+xFGeS28gpppj36CweDhPfGq/dbQSpW6VRU85DdJy4fID1K5OWVM6tsk0wsO6hX9YJ3czJZ8Q2Zax8eJ3eeuURxK4GCSs1B5M0f4e4/ok8/6jzA93EzrEzKK19n1lE6wlbhv9+Se3LVKa970M/O2Hq8hw5rGKdMDBM9171IqhK7/LA0u/pzZfoKU7qHpoxr9GRx2IIJG4V26Rt6cIha/XldXbVq5yC6qoKb6VPljU6ErKbNV3FLj0ZFYM6E9ChUX2OOwqAdL3whd2OMFqFqanee7s4qTy10z98lUqF7VwKkK5NPWWdkqbDchVGjWPlQE5ava6N3iY03s96wuMRm7iyphjpu9pRDGKDlUqaRCjkJliQm8ZqMdx2e85bmbJb6oxoJaYQH1YcC11GC4biFObRDq5F9BQ2c0HMFzQBSMvR7B+2ocn6OQCOP9UfYy7ZIyrx5HcYL2KGKHOjiF8IEnvx22qQ+Zh6tOOZ/cJJUs9oArCaT/EBhuO8j2ewkD/sdS+0j8Vm8IG9Cu4gJulUhjI0sH4iaMPGjBjXqwWfhh9KtUGlRmG1QyimJ8DkJKaUlFCDToWtikQ9GysOAosUJAcZHK1ybgpmFBWIBrSS8ugWwoIXlZHG0waUV0cOIydLp0tQTBGDBrXsGkVAuFzUmgWQGPVpQjWkUicfrw4mYWQlejAWr+CT2b2+mkw90PLVJ8jTeODf3kDb607VwL/Xs4kKmn+9JQPAzP00AYLW+8+sAjrEHj/27OEO1QDPH//1AaNP8wwMxlh6y4u/x2e8NZM2ZLufKjhkQLaG8jwoK0+FHGgNeVEARZCIkil7Z56Oq1S1yMH0f+d0WbfUokyA+4OutVnZDosPMre3w8MXOx6eaejEzCxkgUqdGj7zBP+ftrsRMEgA5+BOmOiY2Lj4hMQvrFaQ56oeB1r3rvlH/FbLZ/EI9nmALZhiBe6GK7xeLnSH8ij0sCCCS9xnwN4nPVKOIQPDc1HYmFcyQ4V5OGrS4lMNXg1r5f2pBGn6FP30ggvN9a9jf7XHIV2B0QFixkQxbynfTerGZC2skUmsO9tEC0jc8l1F3aDvBK+8stV4AbVlya4XJahZTJCfBEivb7jF076Vo7WRuPa9JeB6y0e+8td6UKAiGN9HagAqV8DUc1H1FXX5mOlwB5s5iiSAXhi0XYHcP65B/iCPSaaaeX5toIIsf6V76oq2cM0wsXc4vEmEWT+Mwyw494R3pS9odO+KFvxBX2MHn4EEMyL+HGraILigQqwVPdehgY2CAvCopKLWZmMI4uGrEdTRTxx11XOx+hpoqFGPNcOQIkArqg1aI9/B+9C5QOy0My665BcNfnU5/nd25otc0eiqa6674aZbbruzZtzq25NV3OH26THHE+Oh+FqIJXqy50ncoPpQKny/Gp4KU6WKJWAdX206RLDQhusSjTXR1KXy3nF/UwuXKdDyHfg3XaGNttq9Iz85/bXlap11cc279Efu8ni3ftyiMRiLVnAfMwR2kKAhmJTmIsNdJ6NXIAXojjidprcja/DKglqsw+YdjZz8DqIq8JeOk9MgP037IKTuYvUuuJLfOaAOss9f7xSoL/jeIdjBDYuwC2Y7CyAlSf5QJdr7IViDpCpDeMhJWZTgC/iEpFwypDrv/BV3gBf+6zTX7wr0Rl7sTPN2l198tFWCpizWDTnrPKSdhf/4g+gMXvLwTGhIq4e915cscq4dwteaYeqUvvq6hnVLrRKrwciHDEUI6B/0wUA+EGV0AwYbmfboZmxd3bp65dL7+L8Nnq07z7crb+bgkuaWQHgNTkdZUx5lFPJWWksBKEGR1yeDAA8ZbWahrZaNyreysxyZR6FvV61UdTCAYmBPCh2i0AKsSHNnpgGzS0JqupEG7roLBJVgahm4xSGhl6XXEWApgFCe4L1VWriiWGujlyhk3GIOAVMtdawQlImB4UtAZtgBuzbWYYABHwwFvEWohTOQ/kNokghkQmg2oEIBQosJgTqI0GpEoAmENitsrqAGoMuAn0T2ygAWB7B+s+wpTTRbu1+5ZZeWt+KEOiMk17286NJs1hxcaqkFMDz1dfpf2aUsR4KXznECOtXvtVVtwc73Q7JhNiU5qzilk2z7zkl/zE8B2pWhBRzavXoMiuKnAbR9lICb8L7SYg6AFCANDJw+758yDpg4A1DvAdXG6pBHoh6gXWx6jpR7rqsDF2vcMi/RKQPKxy3bxjA8doQ/nAbO7ZtNXbDQKVdd5KY1h7f0icb87FV/X7+QPoEkUZucBNSHgIrLkIytF3abNXAKByBmzFOO2tMlw4df8GiDAEAws5WO+eRqSoC1+fhnwTpQajeLn/iI5AdV79vJmTJx5SW4thdgsV2C5k5xoSMBbGSf2l2xjD633umw/aoH4bOvca1UBx3UZwxUWyYJgPycFsUCr2LMRifyEJo2WIRA6R71I56G8yM2OczlR9R/y2tdLGdGGAAe9yRaI4sActQ7tkaYINc6KddGNEDBpuXgE0AkXZ5KB4lhICCe3VTKwzHzeCWUDA5fzZD8NRJQih51XPUwoGhPX6fvesL1jkze4DvhxnBPuCnVx9ycaWjoceGA+jodXqVldjeDH7C6hGx7Q+iwphv9tt3d/6c4URth8inqhoVOPZLg5zwqLql1jxCuoanJu+dybssQcIWhfujHfuXXgSiGUlGTZD6twW3htnH7VV6VVV211UwdqWtprvSGZqz9pHZE19SpsxTogMusyK6sMMQPovriAE0oJbVPZVXx00vVofLf7T+Xm+IDmHdsvtvluoudqjzddedr8zpfTmWpUOUPEYCvfp7WnjaeJp6mnbpO7IwL8h+P/9EYKlEJCGBtkzPN8+5uGRovrq/uGb+yL39p88BHD730yD0tnprQ764+Q57501v/959BoxLM/f0rht33VZMff1udzj+80OobhAGfzPhs1t9embegwhPTgUBJjgDfyPtG33Mb/+Bb+kmsUq+zzjnv+fbtXwoadFYqRAMdAxMPn4BQETmIgpKK2pdWMzGzgDmVmLSWG3cePHnJ48PfegEChQkXYYN8m8WIFSdegv8pkC5DpizbFNpOaKl2Hbp0kug2Od3ixEGPg9GHdQKAd2mv0GFXP6bAQ95Di3oM8UfYJ8KtFJV7uU30TooP/4+/l9LnxPbRB2lb+Mv8o1Q0J02LPkmV4WdHn6W94T8pLxoNykqhC6hNOxznZp63IJb/BIDV/zWqtgeki8cS/huOn3tMlkeFiGZKUFs+liTRGymq8H5GHJ38kUUC8eNf3EGYSyOmb2dAVuN9iZQHGN696CxLC4qxNdZJhOOf1ZFVEQFkS9Agge8BtLlQue3/zFIeg9Q3Hf4SaYIswl1ycRbaGwnRQdz64aVMkMCePQaNW2QGxw1w148fOB27Dls9V30kbl7z+GJ3A2pxZBH3aOUOgPyfztcjaDKJbEuC+Q7allishQQ5SCQJfKYJrEp92UWcYSIBZ4SAuYSwipl9hkJRRDAiZaHdLAwOcIaP2BGqUrvNiW96qafP0HiSSrZE82PXViyJpCVXcTl1G+/YYnBFQlFHyD7ve7nHAUmrXkAQcXjMjG0bpynSsTEcRTsoSeI+TrBUYV640jWUxY6QXtrBOzt7skyHeEeWfX0KcJ3dwym6+ClnS6Z3sEIxFNV1y3rhBZplGIzIdUXBkWcY1nmRU534WThLFjk85NDsT51lZZknkRfMFU+s+y7L4wzHMSjKIiiC46yEor4vGJERLSUl5zn8CsWQDE62W7RxiqJBSuN0n2Ipj4Xp53E8DlE0wvUYjQidYXwTubcO6dO0SfgkNb/gCzues/KilRfLqXSgWKmUuZEVLIuwEATTsyWyn/s5wa5wCmiUCgdr4NtqEGcqetcsscpXY8WrebHpHaKtY8Fh6INcxPwu1g9+szQl9rKy7rMucYKXEdtGn5R23+kv0RvSRsqS7tFgXhG+QAWNC3PIS2/wuoTCckFEKGEEQhkkDHV6LZNXjVIAIzOkUgB6/NmqgV83C7PiPAb9FQp1Z9trd0ymAlPWa0QCp+FRds7VHxmFV4AwgjnfCx3uJUBEmAKAmTSmipMsv77AUEaBl64wy15vkZFNTwVbxp4EfQEqSOrhYbSpwIC0cGB6HPiaAFSFcdwmOB4YoglGowf80lVTmFgRUfAx3Dh0+SX8eEgPMOnOFJIxqJ6jZZB4uEje+UO0wJv7hAAXkL2YceD5ilHwOM6ZRXWY/TAIDdxDodkliV6QwkHwe2OvVHNZoTT8LrbajvNYzsRl1cyOGMzw8RopqyO4IYpcCbliRd14Az1jLZCrqxI8hWOOMRwG+jrThmAsLnLFcKNKE+ECFYjQ+DZ6r2SmetCzsYp46y+FKYeiYfgtsFT9hDwymKRyONTxPIeOc7VO7GxeMfFPDSZKsL3mNC59iaVfGx97NaMVAZi5QIpedfWSEMAFEJ+Ujc5Gz+NKdXRex3MStjxQC0TUrTlS+8f4jBXx2ftpLDeF8uOpxV4MVepzULZ3yGpFXTpkxAZXQC+UW4bJRGzVVp5RMIPVkec0G+sFayvRfOk8+FRmxa6vJvOLqKTICw1pjE0BZDIjJ81d3ILe3YEp5uutPrEyLJEBBrQdP2vwdFZnrXSU3sk3hIFSvp5bgv5Y+6WVcZDVaLVIrlKmZkIrZ9GWIuAcwkO+uHWB2EpfgdibKKUawyrI1s9E5Varwz4nXThAxBN6KL+feWlV6zx8ArINVhXW7FU7etnRFoFoMwDU1dPHW65zJQ0/0575rJGS3Jnq4iRQqgjULvHB/BHyWyiOEa80/UqchdmizWx1Hq+IuWHscifNviEgXAlf2Bmgow8QG3mvAq8vrsAq2PAzXU4RjYXXEYvxenGo/BJy3gDGooxTff0UYWGYi5N+x0HgyXUMZjIdH135HpDtFFjOuEL2z36mz3n60HSjCrWFphqqay/4OwK6zr8NSK/2N4jKLLhRF/et40xBZijds8xxxOvFhCK3neRp6yS3g9e1OVv12IUiH7hWXDbYN2QgLM5tguAMskyI68UEvPAzrZQolxVehwVEJqKYDeqCl4kePNWnCjDqWq/w55uFpPSQRqLRl+jmxhYZfRzGm3hoEfMKhPbmuNdOkh7yNoaD273TJa14vhk9U48ECS/Ks7WTk5s63JqzmpPOzJ1ujAutAOa8gI1menbVVobm4NqDCKmrVEregF6aBBt1MbzAShmhDa/jPfGGH4FiKgSz/TOUcFyieBpVbywXSLlO4sYKkaQN7XyfJXFEXjbHhxJ211uUrwyhSIDYDLDKJk1qN08PfZE13pgaIanfysXbLCNjk6OJD5hvRLYMk0fbPL2VI5656xd4ifhkDPnN54DKpxysaSkKI/XW+BmI+/Kre8P7LIv0bwAITxLK+DIFne6fBuzfwI5a2mFuJ1A9npbDauv41eFnYhf8BCzJhEDmAkHFSSCaOWARNzFIdnJZbSX6Ecn6fVN1CK1QxaULz/ILRLV7XvQSeTW778mo/OAom+GjVqwdUeo4M1NLYm9sxGZ/xHkzQWpS3UOl6L8DiUMq7kU0AAlQ9YTTwsdbQdgxAmIR4z2kyHKgAiGtum+F7wXn60KVteCGgBep0aPhZSKCFjDzAibJ9SRbGov1Z08YUvoEVmNCqu5qr2soaUOyMTlOXnVF07GyO8GyERvLFFZxYG6aeL9ZDfvsRKCFoT1Zte+fzt4IBcYaZrqey0dvPMBXs06i7N7uhGdytlBYYdbydJdWrLysT8Y3syA8e+JYu3mlLNnmyErriUgGQCyln2s3h7skZ5uuFgjIKalzTQSa/Y8zrS4p3QOkSnL0sDkNOA40uQlODZGIWn5o4eBJrS/2LoWiA5YoV5P+Vi0Ci1uq3uS6WIHr8DVQ9DpQE1dPVZN/eRKkL+RGYoSVKSr4VEbnY7Ayf8weinM2VKYk25obxnWE5Bve7wNRUdjyOJwGe1oZkeuUsz2WSuQkU4POYzTySZlrLGpN/+Ex+rYAopuW/KDAMmX4wQin6M0OGt1gjw3CWTE4prlNznHO8GzTe0/3JK9y9wQdaxmstndMzTNnelPX7n2x/GHTO2ZjawtaBvG8RTNOFWH7gIQvy3lCv55vXKI6wVqE5doAm8hF9WqzodKgBKHdh3P0tBX7sTI+hLld0tt8Qt2yu+tyAC1M5b3FWEt04XLCmFqe+yjKJdszfPSGXm0qsS4JywklB3EMH9LAgbuVKKpAsWKTn1eb00HOsgUXypt9jd0EgQV5oVPUbaWtY+xukqctZ7hSRfXYhHEUwP73LiZgfTE3O5rKt7fbx9cytnm+2r3okM1Yyaxdv1MIBF2Net0E1Nz3qqcCPXyBbepyu2ztldVK0ILvCPdQK6V1kAw/QQeA762W79EX24wPWKbfy41QDhF7lTHqymOeTmkW/Djjq+PhIjnLA/iUqDdu6yjlmnrWd1ZbxxDjKOiSePgG9RlBo1RsqFymUPRUSaUL55DIj6pdQDLfBsTz0z5H67BFGQ5K5XbW+S/p8JL57GIK0RmvKlPw92GmbcBh3uja2fj6i8YuU9yV+JjtVdJ5GluXoYXIDWIDf+08IDmM45DTzbrdm3mWTYfZQreXyDpEsnrSVU4dUVilAiHwRVfpkRuy0rxNfpXq++vTH7jrTGiVCaKBts0Dh/ygpuyM35fK1erbD0D8ylBYmLOLJqTGyuGVK0a9BTpXUfomvoXuE5PnJJfuWl2pXMYsRXW4HwQJYwaOUzXCrxOupraOmWWtye1vpYxMyZg2uvqYKMtj8Alsjdwu6sGJo//IdrYrxBEThsqTXdO+ANUw5xHhzzdxYqsB+h0GNCyrO9tzTe++L4aahx74QkkKyw9DRc+P7F9vXucCU/xpX5MxBc3jDCD1aa241PNkdlVGNjmLukCJbQvlwPs488wC6nK6BUqewRZSh067ikXZeLyCfoKCwkrXQbAp7iT0Y4Fpa/QO0UWvhoKPX5m3knC4lbuIwRwJUAU3s31nSl5Y4giXWUmojx4w02LkyPILfJSR7ojux/ZuWdCBmnAxuEIbtfAxwMEhW+sICQ0tEmO5cLBwFOp+dPiVDSTD3LpExGRevdMOxLQnN8B0ohOYjaG8aI4BoESgS7Ms9Z0Fj8Jtx9syxDfOhXuoLoTLS8Ym5oOnEBrX0zAsMk0tRjwqDMJG4Hy5pAJwwuYhKig7Cb8f4LO5edL7irPTbsyqthO7OB3kHDq1WyJNYLU3KzkOKUH1XJVs6nOKdm3fDyloDNgQ4wKJbw/mde2wYctu4NEc4nqdkBDoyoGXrLwpQFqMFLmRitdvQ15XCZW1U7wFsnUzGFG+NLfJJqWDyj0Habp0sfIt/3Jd8YUnze3i489FtwF+BKp5ACOv6bUttJW2CybcFooMSHtu5KjtwATuXYRcpz+x88CRcEBFceA//4OF8F/wQpMSf3uDoW7CLg5Bi1mqaNbu717bDdB1Vn50OwzYTyF4TPtxmj8UpCT8+68AwsTj1LBPCBJ3t674iISKSnFT7fHxnHRVaJBHlHxosDDElCUpmR4erSwyKTm5ydCBvwv7uo9cjuCemI4UgsPpcvJVuk5huMFWzSFUfw8PoQNSBDC7aW62TNeLU37z9fZtpcoi3f5bstIS2h1077GDp7YyPd0SguAed6fEV662eLbtkH0PQuNHCS+r3sA7cafRLuQKE3NfpZDAYz1EmH7yrAa+gzjxxKh2IYg3NqsbAdt1/HdG31HRKOlvigeoDn/28dBHFBKmo8FwVliTOr5tCVejDHF+UkWl44fqjBz0yvXGRkznFbKszHmO6sDzQLOQgCZaQeax4r9YWgLoz72b35JAtMqZ6RUfE98U6E4o/EdhfvCx7tM+gtSWHkXON+MG8iCgEMzYN/5xYxR94WTPNhAWLnq9j4SljGLjUchyRqC6+OY2uGhXmDah2WTWmB8HQtonT9bNRwgBoln8p6Kr0VXhG5eFIENgImDDiHzhQGNpohwNinFNa/qleB2abcJm96KybVAz8cD/tsoT3tVb3WSfxkv2AW+o4TsVgdlLilf5hOHB6zMNCY0ymekwaxcthrSBgzKxCbQYfFmEe+5rswZXqQ4CliVtiKysnf0lz9n1VgQgitHpj9SHcp0iNxDEaW3bGGInTH95YDREk1zmm5rwWhjpei1MzXwtjFbvNiexED80WOisv2RVlul1Gaq9eNBBR/lolksJ2zCZ7oynItt9f/n/Jizqi6Pk+TXHGHDepK4Ydc521c9GtFc9WU4YXo/ucQecbHRXb43qScEd5l2PC3KCBDRZWQn5ye4uXBs1zYE3+Ny+fjNa2ntymItwzxrIz5kWm9hAN7fH8iQlvb1CGBfWdBFM4air1j7bg/5Tp7qhDmDCPtTo0NmzWQd0AM5Qa8ZbkRsjQ5vRpZUieZssnHNr+pwkQpPQ5v2nThmmDrrS8TaCgdhoef39F+Sqa8YUbuThrQL5427jZEQqU2neV/+jb1kjlV0mA4au0JiCt27W2/SWvXi2Lfx9iW9diCMHYNE6yZ2FaUtQTy8VNrMJwtmImE1ARKzkEMWkfdDAHQ/b1sN2Iowm+gZ4H92ufle6cT/q25G88ct8lX1L77WqSIKlbEGuj4aVpNao0kn2Db2FTi7zvgXE0iByi6scvv6AA1PqyynPWxcNV1gqU745FkvhOSmM9q+i5mXpjN+9EtlhY+yBG+Q7eSAYGTyze4fL4nfyWQkPP2e2NmhEYQPEw2cWJKloIns62kDCczhXaGVIYu9R4vRMNT7cydq24qoS+PS4xfyrorpWIXtWGfw/Uht1Suqrm7zMOElmsMtEglCUsRrUxbpkGgA3qMGuXioBY4G3eBmQ7u3a6WC7nV/62xDJzACJEpHfeVxanVf0Nyhe5+4th6Ddl1IRm+ZUEKXYRyVo6spWt83nM3HANAzMXmFvF5dT/s9egtC4AQHXI1YLyp/1kOVEiLLGTzLZHo16mrPEFMaN/IE6bqLwmbeKHoKDOcA2aFEZwdSWwom56qK8kUbIMXrMVAQ38hchdezA8RUBiqTkjZqri2BKR84Tusi1Qm12t9bbIyJxCB1DQInIWaFTQO/UDLDBcXTRlXCnwev9gGZarEE9KVOyONcTFEkrRgvma7mXumJgPbpyNGCX37Uusl5DNrWOTM5lib0KIAkwrfEqMo8rKaiX4MD1GxNHge8gxliFx8smmSKSnLZv2MHl/hCcqa+t2jHKeDzsSqS/tOvO+uX3GdqADsGz7/IFZ+1s4E6n7zGxIfuMGcxbOHliM/aVNJEysz/YdK0ZKP9pUACEdf36OOw2uUWwsEd1zlEusrEk8wSghHLI0COjWlzOe6utak2QIxGujyaPiAEGwU7OXhyBAxYq4DqavbixWQqqXWlo96gX9WfzB2/qfjCXTbYWepWcxB7/Q6vw41XFS7TkewIvsVSNWAoqjsX77KpnkYFUgTmwKEj/BDUAIJfRp8wc6DTgkBzBKqhbDUq9cCJG5IBDe1chwXD5fXvOgXhErzXH38C7IWovjHbsALKI/869xyRD17COO8KVKWw7MV6jbXKzRnlrIGI0Er7IiSTH3wfdMsQQakaOG4q4dpJuc//jZiMLvUXy9XYn5uJow6LrbMibFcXZxDGRcPcpXupppMF/+J8J4J+iBVRDaX6pMBnYZPdHcuSuSZhCuVGHnHHgRnpIZbCgWyNmHkQv7I0lscscOzipPvs0jUOfXOqPN62lmDT2n4FqImTFDJ+lJZgHhByWYC77ycIro15mG0nr7QM3uDv/YoP+Njhk5WwvQlY7tOcvOwF295diqPoGEFxnKU87iAUo0MQ/bWqn0t2anvtEJ3T/7wU6niPl7d9fES5fVoLfo3zBCs/vXP7XL1sPS7iPqjC6mtXqlHwhAm6eb+BsQS5K3gzhidOhoVRwv/YeSMkIVvxgTP74PQaFWD/yCFURgZ2UrL6kTpb2qu/t93ukypX/N5EhmVShBrOXFua1KikmGuRqR8hQVoJHPAOtJjOJsn/QIT12TqhoXQ3PlLLPEwyulMczdM35PZ+JdsGAy037/l+Thxc+IabaLQIWr/OS9FgIgeXr0yl/EaLMMEzUX0YkQZ0/JJunuN6q/cs52YsxSl1XIUx8l80OkOJ6EPAHEtuNuaIOL0pkasAUJX7wDt5xRqlesZLdeFoaC8IMCohAPRjSus2AEEJZ1I8LphSMwdoCF7H7dZPP6VDGxrMkfyHARmrqyM028+Lzm5CkZrXV7cPrbBjtNu7uf/nO7Y3voWQJ6Xa2a5sD4Z7pxSKD5mG+kH82f35XIkjo0w19DBnNEYmZQ1ZlKJqcY6uRrLZjKXs59FgdGZMAlwfP3hzPDkqgUPattcgBqsccDGTNf6cRgTLv2ek2KrGNRFChgIbcx9Y0ZhcghvJlLtGNi7w4tr9km0J6CZg4l1b/cNK/uwGWYHfuD/VfN8voMfB5f+hSe8NpFbXGWtKic68gfRDyToOCwQ5WaXhxeDbbLiHk/GRR7zxjvCQ0Q+TiosehChL+C8DNhHAOdr2mirqALIM3CQwizqORZVXtso5JdmEpYFuoqBwTkai07rQ+yuLHfzeTjySJbCLkVMqftKvN203bNXfc+sfy70LtjHmj9R54GxAgDAZgHAnR2X27G8JdjXAKdvT39TskVqvYFZKKdHw5Oyut3KKgFr5cVOHilRpTWXXPXGhjyBpj4Q9Q8LRvPVHHKpN5ZZ1aKAwlkrJ5JZ937jrgDYa6egM6ROM9IMAoaQTL/dKW0sul0pZyfyNYYonSmmlUJC6rjoIEn0BCNN9q5bWXVwscZbUWY2vkoo2f+XFHiKMrziq2kdBKCYwDWv7pMBUo+9Wnaoy62wUlPJ0SklFRW6MyBw9eCSvuE7pE5ctXLPcwtVZ5skNjqg2fVx7eJPT8/LtA4St1i4pvnMLDV5T5E8bSVkHADTb6AMbcBRQMIaVDeAHpM7R19nKbKIiiWrE5XBxhkTMQhbVrd+32cFYw1JzidVvr3dKZM8GjCwQGIeBISk88fjXEVcfUsmpf2idkW6Z3vRItwSf3HisvqusjTlV9QCZNth7yGjLulCMN6RCO0gbx1dNv0liImMXMLZM7+ed2bthaJVwsqi8/AYuS82vjQmdxpXglWhMMqR1FJZ0lqAAsCmhzHJOnAwx6waai80GwNxTOQ0DdHLfNuSefuhEhqvXmZ8/G8yygsrM95bs3i1BwW42HHM/SXE949p7tR3x5MEBn2LVMLSu8adPBda56qpwWvXvbofroaPAUpjSdJTJLt/bFKDqLRgsuyCoqhxozGuCD+7PAUrJHB/cNjxdvM/WpE7GEuioOalOqOqVF5RiokcmxAWuZqNXtTcns4o17H0Aa0pAuQY++8DfQvAyuyN8zmb/zCBMrQLaGX6MkRKMYhzjxvi8PGoPLRDeE4imkjIbAs5qGBqvfHS2t8qZ9WUKN8A3luvNNZ5xRV/TF1UUi+yq7ugO6kGY5EofP7S035cE8JarP86rduy4GlWOvXVaJtezDkk2acq+eSq2iM7NLiSyjEdhAoKsM1k3l3PZ7jvrwLDxcRoyD1XZmlb7HMGgcMY2uIXtED1gXwYvMY7q8FpeRYFXLAxnvnFkxY6HJ5bTCAI3lrk1YYZ4qASSB6oIsogFwl8Gd8IR1kWXc0FOE9/F4jeI+RY153LHCsXy6pPgKDbFhQS4FVCZU6mpgpfzUxXU4RJ5gCCRWiIiw2srYp0m7h30rQ2v8q15vd31DK9gK6O7JWzTVavI/JLwrV1Wu1Wa0hIyEQLd0UFY/2vBM00v6LNv0Py4wAPQCXe1mCtoABBfCwy7vVNOlxuDYmN3nG7EHxxvfbiqfGnEt+8IhS+7IeIS2ojqLrSVwWfXfcWYmxNeHax3qlnLgvsWk5vlrwlnBgBipA/tD+PD03vCmkMyDu/NgnVVAJ/RvruRadbX6PbCxwaj9bLtfHhR9T7EGGVYfYvWasuKuirdnaTogw/yAgzFp8NGdx/0fi8VOrbJ5LvZP2aLvP7zE/Axx3hZYi0fKT3z4ynniI9KayNaIKcoV8/w6jZbvl0S4Jl2t/pDL2GCEPZLCVpkU5dA61CWxSdd68ennicaWHS4aL3KNjrh9ACUXvF3CdX4lOtBqmP2mlCaTyal7128QeNQe+ud3SBiSSvh6q2DeIxyXyqUOW3WXIWLJUP6hWdkoBTxU+u5WUbCqJeRnSnuH/msTzi9EIuVitczzVooLFM+9h86iCQ7hLXdzcdxtq7HhqhcVFYu8q1e5mT0zxjSZ0UGjIwxTyNHtbIMfEtQb/LyLnICufPMD63kWnn69DdZ59qkwf7ii3FQDZRrU1es0+7z2ShGQmUNQaYhUlftQ0VPFz4hqU0E6O2Bab95gXr9wzw4Q2UTsIXGjalw1oZpQ0nc3N8PCwKwU/SPrgAYUI9AwYwPEfIjEA/RzOCqFQU6BtHKi1aI7RXeJVxf67I5xRtjwjOVV49P0sA3ZaUX+Ca5VblVslq/+oMs8T/Yd5gglJ/yh+HGFFiN5DOpZ/qhgjD8mGJ/ebz7CsTlsHOzJ+la5TXfQ/BK38b6y3xhR5d3QevkdkslLRImECFuyCifFq4TLBNO8wae1HwDH0Ud0yZnhgUDfzRecYmOxyXoyn0y26GTQvV2ejh//Gc9MvljUQOKRHsBkcVC7MWgMkZ/B9td5P25+hG+Jhwisr+fQXXiwXR/KCgN7aCczCk7dZAZX9eNZQKsECKYeIYsPJWAwFgP5sGkvkUukq22I192tzhZ76+5xtfJve6utrqgtRmQT8DSzC05ZM7YMXN1Jpq7wApNw2pa15eAMIBT8O8r4UHaqcYWt4pX8h2f467XOfNWKQTsXe/milOQkCgZRbWF3ZdPq0tqcrcJPaHy01iGoXjFkd+tswQ6hjNUuiFtNY2aXkBhqNeDDki2Gj3JAiapn0i9UPg0mKGIRpTPkeT65QpVJPpV8In5IFXcWYW2+I6zzZncY9SzSAyLX7t9/WRn6HEfDqXYkKWHwZ7L0szIL8Gar1yTuJfv9kQMtC+S/L0aexv0k8d0U6LvskAqMhR+IPADGGE7myvNz251SWLSSEu51s1ZuWJwjhk8cUFSlnknsu8p0Mg4qhxonCVil33Z6k/6r99sCELU3uBQMwHlYmyT/pgi7XR69pR0+5LJ2W10VkqSBaL770S7EsEnKpNQse1/fNeepOXfIXkcU2rjRi2eZW2MVYuaBivukxRPjE7tqjdTtcEuFYGirrjYKXj0yBbclReKaw3WiYkWIlP0/jYE6YkuvG2cB0TfJXB4pVFaQeFzy8ir4clUAbCE8CSZdT1vs8x+a4EijV6GxRnSqdMmvuUxHRriQuOS9c02tq42Od/HTW7fUr0N2t+xFbY0KKl+JK/t9tZiLuqp/9Srqvo5wT4Z/UZYP1P+A73okHq9Hwsjls0ySQQwtXDwtodGAA82BLwXKnJIqk1Kta845xTnD4ySCR8A+ELgZJevhCP0jlreJaRy2aQJsr9tWJza4/UydJShtNgjl/lXblso9fiVuB2Zhk9+vF268MvccG7gbjWVEbMyrdu46H8S3ctFSSMLNI3zNvv0NIYDU6SqvatYmmopsumvyCvtKVcxKo0tBD//IqUZBE/49pcrHRyTDxq1mfozlF2ig5znNgIajetSatyIJd3G69TtrUVyjqC7pVCcCWS9bB+LH36DtN0oDu9PyjTpHNf8uYaWtL1nc0WkCUBzFPm5S0KGNNQbsumvyqGOlqgGkySWgh//mc42CUs28GsRba7ZadejAWQ+ZlUqDjChylxmCuDC/8oCsOLZmTUlbzouEYJS+3amM5nYMmXaxw13WlWCyrqazTvCo4sp0BdcG9bK3kUgRnIHd2yE/QV1UL3hfUu1s9kG11xJsQEWuDDa5hD81O/+U37+WPiFh4YXHesdCJmZo04GlTMx6VioSCx+vz6rpLGS4v/vnay7X8NDQMLC0gfFs/TeMhZZ6Pu8Ae78tKDdqWKKxs9f4T9sCoBaiSdpP/26sGfusnEdjBb9WHRatmp+/QWMofy3eJzzzwOUnYlTgKRnfAkcb1GVlDSo4auHb4FiDpiyw1o+n42EFravJ6cPp8mI+59oNm2vacLrMyef/EwZmpG2y+0IgZCOpN/Gi3vA1jD0aQza9vgjPsfq8SAjaOXSxubXFLLDzjZp5bMmSMkfawL1xqwzscudyLneGbap8G7D3WG0nBncz63b/Nyp6+8tNvY19WJJH2fw677nGnrX/+c5FfR30IexltRnyn1+dbd73Evj0dTxPyGsQ4LFuybZtxUpexY4dKB2jsKZqtNUwCitLlm98rFeomd20IRckQsBAO401X27pLdCUqY4FhOZxo+jp9WIkJ1jxp1wguO2xQcJAMq3lWWTc3pzmCcVDYhG+TyJ4dMODjbyBj+wiyc+IgqymOT8gc+iR1D2YF9dv8IRspLv3AFpp0QK2jbvBZ8YMeiLCe3SJh6tgX36StyAx0a49Pd09NmbtVfjkk2AZx5IUrxHFDDMed1ubo7iB73W33k6/yR03qmLGMcXmrG1a5k6YlXHdmGpZ1joFDJJxqdd7AWs3htZbeHCHeD3H+g1orzYaa0pN8uYXCmKCjKHZVmzC2v+OD6uZR/8BoJA9nw0Qtbhn13E3mZVakuM8KqpMXnijoFJ1UmlLYe6OlX7HFT88dQlwPav3cklKxNi7N9iJBcEF6Mu3a8pfPs8ib8CU6yVjzQO9Gnj3pAsTa1YBqjKTdBl5qQ8jTWz9yV2gaJUzybZrHhi81wIisbClXv+IDYR9PGKSTf7qcDdvZvI9yGj1N4Ee6NXCwIUCOlLFCTrbfUwJWMi75Z3UbnkdPVP6gA/xdDwtYPAuhsS3+phvskNWuLOzyypwCkgPYmx2yWyZFu/eJssJESYBHSEnE2STqEzS5s09W+eRpX8q5UFlMNTV66VlEoJf15PQl01clQinaJO58uST4ViyiZLIiCwNkY6XPFi6R1RLhqAuRRvUomgSE6ME3MVgbLGsyB+Ay3kehoKGWhNSylSdyk5VXtVlJSWAYbIDOWlFvSAZlc/KFp8lUYPiGdGwcOAbLNmiMKlgvSldAq2HNpDxLp2BJ1PcHDPKb+C381ofKxylVzATzAQrxghtJnI5ZDKt7ePWigckk5KxE9jC2FMLIk+pHWb5SnDVOdKHkiWypdJxoIpMTpDZObZcYihkNflb1pj8GJKpKTuyJmFLONP3Sk2+5lVbbgkFNaaEJXE/qz8oUdN2vad2Oc6C3gKNCBjH6pYv3DTsugpzbAJgkck5KmFV8V7XI/Cm6PtJG4SPs6lIsharIPkKhvzoc3myFuNK63bbvWwvCi/EXbgOZbl5Al6k65VUxATvCAhXHmg8viSpazcNmYaq0USlUDB5WjglL7ijRdlhxB06dTR3nsjUMH/fbZX7CMHXwN5cX7Z3FZsDksqdwrfipykEBm1OfU2dNQB6yInPIYz/yocHxj6aZPIaftzfMc+QYFuUnoDM5Lx+Q6OmOkrUL7BM/Kb5+S42+5XgOPWrF4RY9/kXQ+KmilmqcmVSMSafp6/jaeubW5sbtM5qKqRPcGUyami4ewS4ddESFr9/j4Fn+CPn8ztiBnWunKVj7USMdbF4TElolweCAbk8Rt5C9G7ekKr23mb+JUGr9ZyS/82eMpIMCJ93bZznsiZk/PtoHO9+XVwG20KSMr1IZl39ghn4vel3rOnjygNqYdPh+W1MsWnARmjyKYZVj2mv/DSsfkoSKbzLZ3fOAeoClsJJ1F+9BjITRccXpTiLiZXRVy1mvJzw7SgOyy4q6z7rcBdb/DpZWN8teLr7lsUvK4kbobihC8bMcf32WH1nKEgiRAj08IDPl+uAK7kRTNzU5cr2z9ditzWVDJVZm2qswLfBFQW0FQHFJeE1urYqu2diQKCNDc690u9IocsZrHDIrVul2wXtuPJD2wSZukP8AwmDgzzoh8FoJw4jhLzb4CbX/BG7Ndrs1EmJJO5VM3P3F+n9OLK+PbuqEtY2lk/Y+Sl1hcisHNpIZ4Qusprldr9KmirP6al+NIZmoS895pmsbEfV08BVgqmpubnJxCRGOWwibI3Ce3hHKpVIqZNLS4eGwrSI4lHLtgae2utniFaq+M+1kBAdDjTTtF5GeAqoKKOjBUxmwbbXTrxgYy3GXZWylOt7rDHd/AJy9PXZaRANa5qr9P7wTUyOTnpS8FDeVSnMzeYL6EjpETTb4IxA93tNDydhT9x5pz9NTiX4QCFH7ROyzhb8kfX5nHEFVGQl++Pu4BgMbDAoN2n5BehzVXkZXx0hb3fqw6C6KGXWNXlnys1tWWeuQrotdIXylth4QhLXjRnZ/s7bxwWGP2QVDuBbMirHRI6ODgyOlMkxsQDEqXkwUEBHRuOj/HrdC5qsf54ouC9Oj6xcujJCpGQkEgpP1TEjPkAAlFY2uX5ANeNQxUMao9EH3cVCLpUnrXqo5S3DoEx288ZOgTgnn0HE2WuCyneMWIuTbjQQI1nlExrq6vRUqYSKc3GkBz00MVHEUbKRzEDL/izK6o1J64jDmsoZbHf4lfAop5r9FWtQCXqU/dJOn4QhsgXU9CEL9QlvfaacBjDIfHLLo5Z4j0iPpO9pLV9q12Gl5QIYv5/GKOTNwcHK1j301RiCix/dvBPA8TK8h+fXXFm0SGEPkpgkdUX+dk3dkdXLVs+nMrcV2U4mAva9+uaNgocA3+P86zwidQoJra/5+T2xoBr3E+t+WgrQhhzyjA545ItjI6HFytVA7Crr+b/okFyZzIB9v9BAWiCSLInOl/ZM4O4fN5BW8+zv/X1n+hAYYBE8jfPwUaJQJEQTK0AIoqImIVSqYC/Mo4lZLElzxlEAyH1cxNe9ZLO9pOOLHlfzxCI+l3x499QailjE45I2zy9aCxgcIdSyhlau3orM4giS5VLDukIL8bvW+S56DG3Qi1j86teUmvddfC6ZcCWJvu41fWk/j3PuBJ5F8+SwjIfZUgpI/iLSKAL2Qif4FkFhspAUsYDVQqg9aJutPpZzLO7yhuwplSsq6ix588tvtCXNus7kMUHVq6NNcSNsT8DkZ/yYjly4V5bbt7ya5bF63kIbUDsnm7On4c3ev99YJ95cFJ7aApidDSqUTD8PExQHV68rbmK50jgqDauK+cnpnorCJiuH94L88GtHK3FkFwqq7hevodwqPLYGtVY2NVVaELsgKUCRJltJGGF0Ugt8BDAeN1q24KxiRR+EmErVzogj4zWkvBcoYdt1/53WgFzFHE70JeEWklQiQ9cRmQ6YWjpyCdukz6o+xCcmf7uK9NGKihrAORJaCM4wqwv80KkGZAvJdrMxrucowGCV1U84Vn40is9YwYCCY7Gne+Eqc5yygWqD0AYT7A7iuRVest6eEpPa6Mo1MaiH5jm5FsM/mO1nhVUqOaVubUziKE1L3iOhOU2TAOwDj3ergLE5NJZSDvrV4raS9rIuT6+a3PnJeBxZXWC5wjsTRhmDn1VtH6oB4iVtCM4UtZb3osH2VtieVU5FjFQh5qCBM86BhGiGUqFUMlgW2AIsnUPpPBUV5Sp+yjD+z8HoimViZoSKb8KhKOnUoh22g7gtsTe2znHxsTpWqb2Sk+B7dQadJHIuK3Yaqij/Wgs8JBFSCKKfqZX6a1tq4kyzzBuRFRtrKJuVqK0xjGW4OC6FqwIqForTorSViQbGBSd6kygPNvm4D9yQtPnvtRNXrwcDe1Tp5qpaieZHSjDAgp0N8nM746hQs7Is0Ka2JYrKip31HbC/uqvMuLL7XherQRu+WMkFgnO4T09XB9F0g0ktQADnjF5LdL3dprj4KgpbUm4K40Udxw/YVukGLooqZNWq+tS/vo/o2808BA7rioWYFzj234EaxAWwMAw19nwhqQRrFM2AccEHtGudMStNb4qlbJEKY624mpfPuYgpkl0KsajSmPxhFS49cvmejPgZr+LjNiHP5U72qsOBPnVxwknVKSurHb+5NVmxOq21BCy45o9TYlddiyeMEy9/I52VeLmVMOjRM8z2ZKcRUC6U1UqdQf5AbGdYNuCpaFaWaYJk1a0COhK4EBL0YUmOxRTlr78qavXsjPE65jIvaFJNNWmj4r+bHkGmhXFIj8axtl/pHkJjS5cb/Y6WditQsptoNxT8NXVcyKuo1jiWpLAxkKmPM8ktr032vRH66TUMWYXsfur0Q0frHpIplpn1SVzMzRkXEB1HKlxlTiVS8e6SuJrmsSFPvUzjUt84cOeoSHevmQRSCCCx9GUfU2v66DMeJrUks3A7+0OsMrLmgyn51CamEfk/Jv/mz3fgN3JPq9mY6nePGO1+1gW5Ak24EKDPOiE3o2s5X3hZDoMJQpK+AJUpku2yKD+XkkBwvgz9lwLX/IgEeO6e+Z++TmWPfyJErYD22b9+a3xsXPpfDlvXdMhTA3/+fORJnftrlqklf24ZWyRadz+CqdOgk5s6Z7FQcVqldwAHwB6VBI+7ETXtKBQXHLTRcEQUPFbE3GG98eWLeP7dRF21dz7A4GcnLY+AnZeJ56q98Nf3T3ypPEKu3KVlv4+cNC5Se8WqSNmwAwx6ZUb9T7KgY7hMGfGIZ00QfJ9w8fp1wRLZBzPCB5j3zD7SVqEDQVNbxUaJTPb9gY889rmnTa9fz10NLB2nrgrh44yr/xIdj+0ThKzASBLUn0o1xyMyGX6j81+FM7aZmv/Woar26UXqV8yYIoat15XoWXh4mo5iOkOcTjIvKGUgXaVe8gUEkFeJxMUfdCiRwCUH/X1cIHvlqTNUbZmR9uDjMVLDxxAy8dB0Ni4WBgstsHzxsx6mUrfxtAJfy7mqg31KWSZITUAdYTOA3Msgw7luDK9UiKghVjzyjEYD4BPuYZavlGYnfPVEJQHVWOorUSNcr+3qgkuZyaee9aOdcSEZPGBSQiNvD/OC70l4EbMM/rmS3PwXZ2Ez/eF2nCqu0nPlSP/vAzXvGOr/ieWt+ig/RFEwsJU/XdDrXrsIISMNW9/ZIM6KpvKgXKzWSiTSh2Nf1qjdEmT61GZALS2CFwR1HrqHni5PgbLD73eASBBSgB55jc10vP7hSkfA9NJDRX6x+rQJlXj0wavzbOgekgF0vdMlVw38T4tu4HvvB/w6XiKgTPqEuwqTm32gPlDQSJWp330XyS3OEvg29zuPcYRAR+4WpyroR3AvRhVCp/9Mr1M9Wo+t1zyyYr9Mwlomo1eAc4ktyp1ZbHYmZh2Q/oB/nmVumHCnmFjI73iWacUktyjEM7GUAXW7rEVh2nZhYSCFqWFiTaYhsYHzSWLrt96UJt9KhJiqP7yqt7loA0w1fNffI1uGHmcRo8Qtt7O1GS1OWeD9oPITHc3Bn0JMCRShZrGHFcKSkPU0i4IKwfReer6Drv1EeVDBT+8je3snkU2MjGOWyeDS6J2Ys+44cP5kDCuu4nwBVAta+uo03YLqSXBW+XHOHQO+XaAxCWQtm1YHPg++MImXGsx4GCO3AXpV8Zporjcc7nTqidgC5FibtzQp0dmllOrfLaT0h0u0X3CTyaEKfvsVL9R01uRRyPxG2CALeBSmUCwdgI0yv1dhfkPwwZsPBGzTHxaY/8Z8vsH0O5x6L0Cl4d03KlaC6h1CjSGHZjDrr0v5dReDMcbqzhx43c+5FU+ZHUbFLVngegTBtDz9BERdJylUHNkKjbmfDgAFlBSJFrHA85MY0+EyLorBEhi7TmJjH+N15H07iS3UmpK1GNSPogZxw7/MDLD2pIdZOHIUm+my/j9kF/BXgcRLicNzTCMWsn+QNMtL+y/TO+++I9B08hKJI8fwAue6Nv2H9L1pPLfAHV2pukqaWngfmLeORIgQ5R7T1p+OShnZbhVGBoRtQ6yZx/Pb8Otb1i8HohuflPb5Q0DyPxIxQvD+JPNq/iuBSGRb0YUDR3zlMEwiPD4UxLV3hFUGd/7+5XL3j8cPbJv1s5f8PQiyLZKch0AqD61tH/h7KaPNco32xYv3Rnc3UYT4OMmRf4rOou39UyX3vvQD4iz9awmkNktqa4Sk5s5FbUv/HoBaLejPnr33Ms/rUzFYdKocuIKiJAUUnIOQ61EEfAp0gShU+DGOjr+ekWTEmMnDJCYDD3kW8zQvyo/wKt5jpYBXUMQIg0UMKP1GWFcKXdchKNTzowmyklrQbEJug2ysFTqElHTsr8cDfOQ6yBxLKpT6hKjr9I4CR8izi6S+Sh4QIFce61kSz2vWC26CNqLYAc43w06cANt5RK3d9kp27WFILNWvwRxzAVt4+Ho/I+Tp7xfXHj0t+pGNNdlgdVHa2QjGSFQHJz6Q98qSNOC/XT65O/Etb+22VfHjGe5KcFMO5RJ+JLxkpu7o81ZzXA2zDdcId929ZUKWWYu2IgPVSLJbeH3+Qeuy/C0aHl7GSKHIPtB0ncwg4wk5wonp8dzjZDoZjmS5t4a+6gCZ7FeEiv8Y9/MaXoZq03d11wWuSuJZgHvdZWOv6sAbyWuo/vxG/Cgt3Hd9tY+6d8KDra61ucVaRuiFgBAmVV2blXHDySi/IGpnSdAhWpBtb5JL56So9qNCj23GQK0Cw/+/Gq4d4oTWHgiPb2s8YKBVQ6H5K4esPZ5mSlLURZKjfNpcSDrJy8RhtK52T41r/UpcOJzeZw0BSBkw7YFq1kzILRE1i4/Lr0nr9LxlT3zDf9C2DhatnN2H8/NZETWw8NLHvrbyRluMCr69wOpRRBampoghRonx5eJrvTlr1J12JJKv/fKqLj4z+WeUbdV4iFuzNAYywF1gF2h6w3bg2XEmtb9G/VlxlUIdc7oDJdkfJaFijVdarww4y7xBdVKa2yl/MSM/+zIy58fUTks0/l8q/11ZWwoagJILsvtF6Xm5DQnZUjFCSLvp4mjE0j7bKZKKX79qNcVbPGaeRyNln93Eo+HXDm3j0kEzON8mvBTTchVGpejNFLfgj/tDm8rcdMlEveMIIsaPFWIl+UHOHZ+f2CWhipes5HUnB9R/Gwprf3gizbHL/1r5HuimI3WMpH9sAQHpxuIoKK9wZ91in0/o8Jmo8kK7mLJCdUXZEnVbnSvKqnY7VGuqMrm2rSzNWfnpj/5JEl7jX/PQuT/GdFOv8wyXbxC7uHc31Xt2X7oUDGx5nWGxWxjFNUe/HAwg6xiGySF+C7dZmvcRxRISXbF7tQFd0BDuBDuA4DEsJai4F4LY0xdvDfzZcxFAJ6ehPLp9m++319E9OuaCbOeOLBgvLRawFfeHeuqh9bU1tXrWVlMLpAoFjnMrmFTauSA0R8JRfq3e/QFFqxl7Tt2JotH2/51tgOjoZ0h5Mvnkk/7x/JVpQIYoq6SCI1DAzxKaVBZridtiDyNNWmBkC7qn83m5/Kyy+lZb89WdAbGTwvR7U7gAK1Pf9rvHSsZKB0kcEq31pROapLZycjfRoR7kd9jbba3Wv3wKjOP73ynl5tiWeEgB+1koFwKwyNCRXlsZCMR2CEJN0FmIl80DY67WB1Nl4BWh7RXrcETkXtBY0AZkUAUuSGeS5d51wBBYgFFIQWtBMxDFbFNgNl/b+7T4TWvYVfktGm+b7LB8aKPeU5Qrb5l/x+adeWFLUU7v2RjK34ElmJxBV7BqOJoHSlck9FlAQm5PYtHMk+sKrxjueqeIdAFaSn4cohV4KJki6Q9a5L50k8etAIuN823CK17pXQaBCSK9mOLAxV2yK1QkluUkfUxKUuSsFB5QSiU9j/bSf370/2ocZndFlxZ6r+1SCp1Fi4J+O0k1mXUEadmShBhsO2p9VBeqEf6s7XRbVQSLay8rM+Fkf7kNy10qDudYKGZMo/KoUVQeqBrR3t7ebWWiiDap/tLPhT0prSpTnjByQnd1+Vg6sOjFQRLG1MzA7txvHpLClRMeEqcdjcmyrj5L8v8Ha4uhuakWYmlZRIc6TzOiWC9qEVODzqk3Hqzesua6Go0PDUWngKkWXcLUYxqA4OioUU2UpLthhcctVdIqowG3g9g8OljL5m+YHq8ue4cdSZaRHNhC0rKwlxCN6IxlEw6Mlq16/JJeolNhZv5+zpSZGRyz0HTqRJWl1FEtzlGRmNgOTo16hMB7e3uKmdlMoz1O6zM2C3CVfMecBm2HXI60wn2u5JuEJ6K3tED8kL+Rl2OqAqwMyBCfRlkl1bJinUHljYIwPImXlzCof6C3sk/Nv5Hq2180qtnPbRz9ZWB/wQQ4Tx4yPYKNTjLHURfVNTW99uD8lZukTpFdYwm3fX9WHislRx1c0o5FTcAfALuN6Wxtyhiqu3EUB4sRDGxbUxZucQ/LAk1qgzxUPGZtM+cWpymg9dAzrq6ivLsLqGXEhn1lU8kjIWm/N9IGecqzgvdlyLimiumFKpY93GQ19KQO2ySpJwte04vctaK7SRitKK3ruq9dtvy9+0XVKUk/vziMVFfZeUYLBQ2kYbZwnDHkdNXG3NmR4AhZA21VxxQhTWI31nH1A0FaGVHHt+oRWToC10WXKSO2dvcA8PBzgjpuxiPGr+CxDk4LFxcWa4T6jW0//LBU18tNe135OAcr6GHDmFpYYgTYhbcNOjLt23hmIWzO/FwUEAfJ/89ZOQydyQZ05+sXe8uXNR2q8czOemrrZguI5Y2Haj2LPdcYo12wvT1y3CnJfHw+VOC4jLQjfNwmqHrirRqxuQtyMuQMVKBl4r0W9i8YpV4hVZhEojGl+FHBPYdPydngtRiJEBBEYDt5ysS46039o75wAND9UdIg/FaScqy0SGNfNPCLI8O6subYSqu08lqD8GlzlKl+ULUDun/VYR+G2RxlKO6HdijXdd4ZY63rIeFLaFOziaVhYV0cCdGG+nrDMr5kd7tAzFiYFrK5Zh6iG6ESg8wpupS4FNjB5cEWHlZy3Ni6mOvQnfS7lJ14AqIhmc1pAYOLP/JYLm4CoMz94lFcl33Un5XFq5p823t7bUY+ShK/OADy4ZMcp3FKPeUeKUvLImVIdE9Oz5d0z+EBzey33EgsT+QRNaP1GloNNJAW4hdMjplDyvCK3U85Z7/nRsofRhAJhMylPGbgfKPJoDMQEINrBnRnO4b8B41FZBkM9189Zn7KVet3XDkGHCUrfAtU3cq+3KLyz4D45NRnUjr6T+HV3AWGuoYCLm780wBLr9k5TR/f3MfpB/4QST65Ff9Lp21obm1p6DyqyTMlXYIkaIWjbt3R09tjpoMgfSq5u5t7e5tBnENBTwhxtbrpZ2llbmGQGdVXBJlgoTn5WwcPjY8P7Vx4IrVhNzwWphcrePm7IDOHh8fGhzpfNAhOvWda4elnknabAXNkNZVH0qPPD2xl2aIYgu0B6BeeUSMrSdcLsLYOPB+lr8ZglaKLtiBQsPdZOfAdy+/+b6qdr/M+6yERT58i5wILKaqUmlpI+/5SoSfpCDj2v/N7/1wF8HQVnx0i4gMijgZuZ8S8F9ZuZ9meGiEYsRan13YAhC+aq/Odu1o+eN5wpxepdTOxf13CXVL7Ql2xaCJDTWGD0cgEodVfPTO8EUs27ZbrPtCK+WLtBzrd119Kvn5C+0/AkW4/2MJmaf95dZ1+4AofaJ5V0o6HiW4R6tEt1a8BKOMhElkLOmP/O+P/Bkzjikr3U0WD49TfitVfwRAVY1uAH6Yzf9sjGIKyELYqS1tyhohPEHEk+MjjLHZajfSoK56l45OF8o/l/ByDZeUiVb5/vUApW6FzJm20+4ZEymEI1BURK8iomS0GbdFLJ/4PNqDLwwKewx3vVZETVXHMTtUqE9W2J58aix82XE4JXbWtpVHJllfO/wu1gT4DA7ZXdpqfCNJ1yh5OM7/58VBS8DEeOdq2uH69p7CPjxRU9Z+KCM6cXi+z9/XaGQolsFO0/fZHA40GYz5XTB7/WEZbSmdG3Em9h7C8y8pEr7r/nhNodJP+yZnZmcm0Pg33skPfy6a3z4CSyWdZJIPQMnGNY/X3v8MLrMFUe4hcgJ1jquAUP6Uk1bPnNLyKNheqx9ycU8C9GtFfN6ynVI6EUlvtr1OzPAGquKCq7p3jJJxPO8+OkI3WHO83MOWmm6Ss9dgr2Wj2x2pRf3cV7ZATY4Njnmr7cLcrrvRgj62Z+sc8eMwh0fvZ00DjYTAEjBaMhhgQBAxeR9ISf7woOnbEF/Emx3rv029vyJloXGHpaLNQA9msnYSB1NgoKGRenIWMre1mh0EnvexzNFCZ5ZzlBPGOV3bUc9Ado14p22b35yURFIYIU6Gi9uoPxnRygmzjoH7el3Bon4Wv3n6MdalvXU6N80B6YYqUS/JlUjbjqcf/fFgoHhwcPhnhPT0f4gKDZJR1vxEU7HMvsRDaa5zp2b23t6NOSeKmSujeztU9U6XYJqt1DxZFIa9HjWIQL5fy9blUVIQRF3MEWT1xByhXo13SV8pvrPJLx/3xZvRE4ko8V4mbLikU+O7Ebm5UmZxGx5a12fU4HasDpG0amdPrgUyyTzkGPeHqjeXHzTYt/UYhDwKv1bS+gpc5Iju7UWz5PNj+OUjKYAyO/oF+QE/OeqX+iV/b3Fk4KhV/xfPaoQ6boyNQpC+cxD1MIgYEMmGVf0dPmIp86o6Iv1NWoi+fsjLRw8NDI0UkR06j41NTZSrZnlBmcSgWLedqTsDYkXeYbUFue8Ns13ujMju2Oh2QiRQ9oz04KOWHvaOlucXM0TO3XyqUb8udq2X8+9XzSkXhsTQGuqkykajU8mAgSmYK2SS6WYaZDIyW95V2XjxVwkMyYliYVlGhXIew8gzlmoeIO0q7vIO+gc8IaO38CsgDVqtvxVJJRNN46iWr6Ed2pSjHjMWOv76s/iXQH/nZgQ6pak++6BeeitGS3bhnPlxW95IkWA9Iv4ZPtsafPXXzRrlSF9kahmyPm8Lt2yN6NA93XIaGNHwSpJgMxtuJx0Hf5MgkMEhuCFFv4L2SeEnsXX/NCi/2jNruzfRletn8XtLPDBrtXHm7bJ3ikS581ZhSry7Uvw62jXD5Ij6+7ce8NHJFVTPQNDpNw8r+agqIyBWwRZyQr86ufLuB3bB2uotWaDukNWEwKNMlZI899chQV9fQoCf5IhEzvmws/MSHF00Xe3A6gVja9ktBTnkqqbULbxTemxoqRYmonEqMaTHuc/P33X/cj6FUMgi+sR0SE4b0WnPHtKhEhJkiOLGMzMwsninhjezYIeciKUjcF+qpKdtKV0uvuLIls0sWilWAgBDBVXBzj0HHqGLX5BTez/9uWv689Pxj/9yLIirpSFzVSbpLIpVS0RqUogne3+LWaFNvf14vsigJGfYBBAZBACCUABoAkfg6Yc4rAgUV8wVqFFCtptq4Rt7nFyw7tYv5UM3PUI1t3OMSKYCBGkU1tstGtY0/B2dUUEEFFXRjkuUyDdQooBrbHW7L1yMPz0NUxaZ8Q6u1Rmt1D/ss0jgYHQCLdVbFVqu1hj22uHO3nlrVTBNarTXs8WVqr71xjkog1qxVWq01Wsse69UqrdYa9thoZ3Wc6ntqVPXHtIcEs/bVoH1rvzpOxcYFYP5pxJzmDBe5VP+lBnYa+utV8GuCXZ7lc/6ot0PKNXgs6LemsRZkfmiyVKihll3sYZ8d3K65O+W4+PVxq1Xmdf9sTFA6Fnq+mZVk6upAyLnL/dSwotVao7W6h32fmG9kiVY315REen16m1pVQo5Waw173HFUW2arblbFonxDq7WGPe7mrvgSuiVS41Pv7dlugv7HPChxp+D4YxVvodLL22puzVS/tqtxy79phwJzfnZ7uJadkZ/TPb6GkvOxZ+7YL92ucYiCfuX49e5QQwZgAfylaxeUtNuSLyCI3Y3kb2MRfgh6XiVWTIfw660Q3oSm3Fap+QLClbWhptxWx+atAbGTPtn3KCQnHbI9PL7qd/juM7V8YA5Fvcqw7eDQNWwT37EB8DuM3/xquFF0X0bm7+i2uLDtFv4GrSwv1lXZRH9amRUgmdJO4OgadT0nIt/JyrklRcd5uTo2at3nYylXR6/tDPAaz7AjEiF89i21mKP1CMf+5EaF4NJfDJU3a4cKQq25sRBmzeKVEC/YZIQJsj62WCAIC3q38v7JWAi26938XDwjwd4vlZyOg4rMSDt26c+RD63crWarXm+spPFad/XK/3KhGnSff1cPa5vOJIDeNnUfbA3RMkWeZPTRyI47yb0VG3J7jhZfzPotfv09RpO7HVpzeCj199Y1CzhCy3DE1X0NcWCGbHO2yS5Aj//t6HiPTNZ4crLZlsC29ibxjrg+7/u441hQeSRypRMQ8HW+VOPJr2vDerHG8Xdda/UwoPFteetSH8nydCfdGc3HAbBjSAftQl+BMWAm52sB7EfYdTg0/w//3JkEGaH3A+814iKSl3SLXE/+P/lmIcqKF7YUXkPZRDVTX6XO0zbS9fRrjP8xm1kC1hH2ICfOaeVMcNZyHuY8yznD7eBe5f7M28Tn8Q18Lz/L7+P/K/he2C9cLtwuPCicE14UfilaJgbElyUdkiHJB5IfpAVZ9iVK3k7St2RDspvyDaAEfB48B14Gb4B/QW0KgWKfMqJsVI6qkPVX1f9pXtY+rPPo9ukP698yPG8M+V+lOhOqvdd0wPSZ2Wfebb5g/ttitWy33IA1cC18HP7Wut2WtmN6zzjGnUHnD65H3XXu/4o2FGuL3yxNl33iWezFenf5Mr7v/Q8EygMfBr4P3ArSgoqgKxgLNgfHg3cFdwSPBb8IoUOq0IrQ5bAzfE8kF+VG76t4seKzmD7WG9sXx8Un4i8l1In+xIlkRfLjymcrv6rSVJVUVVV9UR2ovjtlTr2d+ietSD+W/iYjyupzshpbza+12FpH7Vjtx3W6uqq6/9WL6++qP99ga3ik4YPGNU3OJmdztMXSSm4Ttn3TfqiD2rERkQtZOtd33Ztfkn+f8z2u+6XuL3su9xncDfU900/vH+zf1n9hwDqwjbfBX4OuwenBS0NLhk3D10ceHh0aY439b3xmQjTx3sS3E/9N0iYVk+7JxOSnUzunXdOJ6fbpfldctahq0e8zhBnxDDwTmmlYrJ9VzL43++2S55b2L12+dPvSg0vnljUuL1oBr7i24q871O+4sDKyiuewqh8BoJkz1X9m/tsUeX8BgNUBTi+gr+sCAKzaxKEXRtZbctY8g/H176YN8cfpWcRwpWYuZePzr2f5vn4jSPQ6KJ+v/ntLGzKifb4Q9C0BM32QtNVbxYpbzk24fPrzFL6UgWL7x0Ii8ycts6i5bzXCwlvphs3X5ysy6zRn+F1yb0/TAqiYS1j/+vAP1A9fHC++Aco7v+XYf+y8bfxNvWN6WHRWdR+CgKq3Co+f1ETnvgXN8q+Xipr+sDXRbP4bq1liP956gSMqify/WzY3ShZkOAuv3u+maPxXXOCM6BXvmNDXwXqjf6yEKCUSBXOb2NbzRzy3jruaxgC5dLEH/1CLpFNpMCnvtOxLb0yI5MJMMiGqYuGPa/Znrqpm9p8426Y0iOC7ByU7w0tBQL/fEsNnHhx4/WmGMdqjWN8qhfiHW8p42O37KHL+CqCqMZ95oCiSRFdTJLg6IsGt/Nen6O5WP8YWnBUlzXj0OjsmJsBfgiigAv2w3fO3J0ho/OpDJlSXWb/4pHRz0g5aSsEohwYI/f788ET5XgK3+v8Hrgk+j9GixRM8zarXe/aCOMSra766/me3FHIKr8LOxkJ7Utm6beb+joKGs1hlkkLx+h8dnG2nOrPRdglEWC4qMm5Vb3blWDUMFmdM725hmwlkizV/sQ70KPb5hVBEarU81SIVf37RABJfpAbsmBzRBeiRUF1d86sGRZ9oy0xOIQq8clb8rq5SpSOZdFFyAJu9s3FSKWLufzlHu4dM+8pXZvDgjZuH3GY+GNgYDj5Mdo4IeH6JBSF3HwcfPCZN1MHXo9feDJgxjos3x522DCoAx8CCqJkRMjM9/zS1e0oCCotWFpYLbk3rjuTUQ9zepxYOdNAB6TMMQVS4cbTIOZTfjHjmXxaQDEexnHuyLWAZPFK1Rql365kdv+sJlZEYTpi4U3QfbbaCeWmpZ2ZKNYVE+gV/qzhWN/iSPQ6ZiTshzciDgAyIkfyEAhI6EPXyBoRV78/8yuhACwKSGKaaZzdQW0Ujb39JZhMSxQ5lTkjJzrpphlTkP78BJVsimbtz/ufhgGkHndxI2Q6QfJDRrzyfZlwZ3k8oFiHEjuoLx3pUCbpEMmTMZzgvACyN8RCUFytmh/rwq2jA5ZaAn7B3e7FQ3yOatuvUVRTFVyA/L144n+p3AwiOEj++w2J4fggblRTdfDix60vBtqL6+j/UDZpTFYi7vTbtSrWhnKSW9Elg4xoNqyDRXEciyZI375F8aSQAWwtIubyn81KtEzsXYSZyzp3bvZvdY9IJjFr+9cNeBZKRpbdJR/tmoBeNh42MaeGU75q0zWr21qsHCunceX+pgDQe3Bz1TBgtxP9xcHheZqpjaVryoHFiKSGAwoWlMQP4W6vsH3vfQIKSJ7RQWCTAdNfreF2AgoCW0u4/7F2gAiYiMAFW7sakE23ki7/qS5Zu/zT3p6KwF9I0URenc29GF5xnR26YxEMJxx9cGngLEU1ZxtORMH5wYWVjKeS9hv+Px8Pun/YWkiL5zj9C4fDc//EQNjeDMqIXkqNPT9ClLJ8e/RAxxhMsnZsaIJBAoY0hMK6zHx8W6HT32a8ggGdZZKw0rAAxgCmglQ0YwOnooVNuehgmwGiwdQvAeY54+ZOZ97PRaS8IKW3OC8jbNyijzhFAzdG0psHg6kLyzjIgRaaGoGrubTfl49wTVBHlMvChYvU/Ycz73w9z/+jxsLfEZnPEbhl1kgCqnoscR6yNkrmPYna984rLu57VSSrduSFH916UGua1rzmd0RFeh4/0wn6f4SI9F1IpoeLme0kcyuwPqcxkX7hS0mvo9hsxNKkHlhU8gZ4BD2a6lcZ225bYhvMGR/Ipd8Pf5UcQEIEoMAgJZE5k09VViIautThi0lvLAQeOb1csEvjWzcK+zQlmZkQiMo4gP5mxUQseBMLL9HcHKvIrWtzJqwlbuZwbC9rFMzydab7OcYVF5PTdVUa909SGrrkuedI+JRFQvNezxF58mvoZp+az7r74XSpiAeWeZ+mKJMIOuhlYCIwLEbV16JaeX2w++bOu3k1Z8tY1SxuBUIzjOBTfsmzT7LojkrBGeyjQ/SgHE1cIi2uFG2fmL5uVYDLHrpSJUUTaX2kvZ8zocf71lYpjlawMhjL9PLHAmTAvcbbxD8F/uh+iUIanbGRYgVQCpZwyChRqAEMoR1jLc/d+4+ZG/FeQ6A8HSGXbPhvtvhzLIJxxLL5fSFjwZbT6ZhNF0UBXFTCYvcLu901O438D0mr0A8Vsi1AjJMmTXOdrh8oUa9FWHHv0Qp/IWCDo85F7CZeWYNFT9m31A2ZbGAt/nIBbGE4UG3LfCtjwlbroC1czh+/zvaUq1/Ph1AKDhoHpfcale6Te1+iLHxgma9R8Y/m7CYJBwfaDNIgt0U7jCHmtwf3+35+ZhOKdorO95iGVzfYslIOE2+GFFsb7LpTdQUCTBXOHU9bNETQKgb0EyV6rIh7h9dINA+xjr0e7zCyl60rEFtkWbOE9OChj+Hmcd953kgA+rdq83TMjkZJ1Haasv2DbftLN+NLcayHnKxKodvIa+fjDxLW41mAWMlpWwyAI7djMiS2SlCCIaGKaAgaMZ6XMcwUsnwbH8Y/yn047ryUPoUu9ZLu+o2eCFTTZUSAURqK6J7+SALWaORZbmZTBIyK3Xd1aSrIUmDoXjXFvxUDXNJitaVNV2fhIMeC2jNeSGYaOxtO0zh/pp+0ZWdq/P6sevXkX4vddLQMwwEt3VM04dOyW9IOUKjLg+rX1dHr6smpmwY1llWm0R83/cX8REhshEkk+SOyLt5Q9DXhLK8Z4yE1KJvTYzuXaOgxl9se6ufHx0Ri3pt81tGihWrJ82uPXcbHOfnrK+tVPflHvja8ivraemTHQw+p+KQOkWK6fhdr+77VpyuNA4/67jlOoQJKPUGXsbkW8u7wU1zbwnwNzgn4Rl8upa+oR9/s8InLiM0yBuVq7jdCIgDhkrXH0nr4zY45Q+guj4piWmOPcWgIwdQqyEXrcDK4j/qG12gv9czaLdWwemLE6IgSJH1m7GnZo+TytcQoYP7zzZk9favrZeVn+0ca90zBi7m8lJQBTJ+HQBN+rCsnKAR+SC3OfRwAXY+tFjYwdkM3RTCYmXOW+i2PxDRhYbTljVtSC4yKyF3kAwnL+RDqO7+mm7QUmsW/WmAk5cl7fglP8jQUS0YB0bdZ77kxKhpd0q1zYM+vykIvY5G1ZlmTBmmrZbPt4er7LyYpKYhbXS3g5dCqBYOriAwmW2x6QSdtuWWYH9q5XmxHgzXWhYZ84xi79QrPU6aP04iSDHjVKa/NHdItq4GfLiFSEaePRNx5JFShSOr4OhjNVeUb3RpY8V/8XkGbh859JAqIUdPw4okqOugXn5x9cBJ6/OT+CYqdfbl8pBoXEY3+YNDlOL4gpxbXilrHjJWQaJKoojP0Bm9kWKR7lvc4npp5dgM2iuqoD0VjnuD3kQ9pdvkKD0u3ES3ArJu8l+EMiLXl++uN1aniNr1tTW5tDkgC2mSrgV1/h0uA6J1iHWAYTtjbnTdcuq+xytNF88xWEc0xf4ovHwmrh2Gb+rGyebHXYINLwIrfuDjQZaXTOy/v98UrRkCVvvEmofBPMpXWsvLMksxM1WkYGe+kfqGG0OzP97vcpyZzFgWQx/Xicmq7cukoSeIHgw5FvpME2JxMju+L8xwNuxLJReyHEPcDyOVVKd+IHx/CJWf3XX3xu02Lgx+AmOWTre38+/xRahgL1fwiz9+YZ00/6k/n6JPAAfjvmTxFx56x9XMwXnv8FW3ISufsMXK9V0G78YjIN7Mbo4vrm5vYmyzr2ExdLkwNAXEzSlXdX3u7kXK7sAh8THdmtO8Lw2UM8NyOyu43oQS+Uqz//U/k/Mndmp3F+anb91BBuaofHHZnGwTCYXWXmYWEkehiukxabI+R+ha0My5tlZc3J1eso3YMiK0XJ2L75rovF1siSNlCPOg5m/u6Cd12X2fA9jJhCWsqm5v2hZTqr7UUuCAXgLaHjwp+pBmuTT7PYZej4W6eEzb0qJtSHp5Xbl+zr71SfrJN9FTwQQF+CsqMgDH/hbd6ubSH/hKKPN1xzxeyXp3Irw7QelsCztS6PlJCFmffnBh78i72dcZFoIUO2yfujB2hIOzHzb8emCqbVB3NVYm5EDy2sqVI2+14qsJbqdFytxw4/LJYSDAEt5xCVveMCUeesRFdzOzjOUnCAD+bgf5OZ98PnjH8xNPx82Ou4yY5le0Fr/pEFvzo3ztYtk7zObcSR7/h/EzBzEhah1fmfnRghPTyz8SYJ2HHvh44VcmAq6V40UTPXrEB0t7SNOWf2577CnBJPprNtB/fBX52RxzNVoOAQ9wJ5P6R7vmIwGOWl3cu84/iJWENdc6eJizP/eY1BaCJQZdz1rl9g0MiELEJK/0DAwfjm9I3dDddugkiZpzeQsyYm45Lka69l738KVBojwsIDcGFC+HKXXMDC6udqQcg11x/dGUMQjOEkvpLWEasOjAmkZ2fDg0AVBHmWoWck52vWsaPtE82gQiTe5reZIRN+dYa3ZXtkyHtLhALTgsNfeX6huzYJFCwXEVW9NWh9BnculA+F7Kp850Y1Fy2ZaZtnIJIP4kKWSrWjAtiiFV1M8cauZHcZzS4WOOdVh9xIW1j/5qItfQlq3fNc+/6Gdoj44CdHkXFVz34WDZCo6mdjY9tcW/nEutd5ZX/mmmowp5l6itlc5Tt4U7D3m9l1J7BDhwrnvnIufrR0+7a2BAKbEsFGkWYzLFWCo5p/9wjD4eStRTyYH+KQnYikiCyZscnyMIzi1jZm2OV0snW2GVfkbN/SLden1xpkgHnJAUpgv62Aseh6Pk/m3/VHbRnt7alqea6jCxRZs1JBz1DbNNQNDg+5usFEDsTq3kauCIJkea427CqINkB9yzAKAyxs/GNeMIwDU3cv2XthD+dCQc3vdMJM3FNXQasIjmGVKMY/cvYALlpQ7GaxCHpEpaGlwbzUc97C4xkPBwKPHdIRT2JltFAygkGrvMpqIyOdbJNEpeIQtQ1Wa1lNqnB5/BUkkHtMKhtLK3470SpYpFSuN2jaCkcexgfoA4PX89onpKFk00fEG6BZ4SeKHcrIk9VyHh+eXQh4qFLZMte1MD6Gd9ki+9EXJex/zvjj7U/fhJ+/wSyy++WDiSLRRrr38GyUMW2+4VhN1CNNCQ23ieTuqlaO9hCzunozSS5+TTKD9qL+zVEcjWGXUw/3UBTpuT3WAOa7qt+qlBvQJkYJLo1iyR5eK0Bp43O+II/V2RoMiHDpBhzV//+iA6uDd54pFv/eUBlE4scPPtqlZ9A735qfc/QcqRI8xZQIa41RyrtW2lZ47SZf7fps7wIH6+YWSQS/WefiautZFZ/eBea+h04py5OPMAHkHZHA5pm4nfZ7RoFjKsRXd1NIJCr9/NSFMGSK7XemR/Of7Ys8r1UJ2fHj/sBOtjN2SInW+YNffM/LiuKWobtl4ZWr0JoXE+GJ5q2YwFzWAozoGGdw8FtShbkW1BFsy0klnuqQ2rz29twIgc6odXUBJSlKDMaHTjo9+egr/pVld4zKNlCC153LX5yxfUqGzbu/kASZuliskGayywIda1VjJpQFVIZ241RSBBon0ZZ/EHA4AMf2R7gxCa0URdVUKp/mQb9POHr/rIm+5Pnbh5MlkU2FUNjH5838BqndWSIJkqLUMOv1B5MucY8j4Mf/sY4w1pS2caNiiXMy4wlZL+bzdVazXdvygPke4h33NEitsFiG4xIghW8zI0xYG1/bSykg1tIaCxRpKyxcVcFM/9SLChSsB3tniuZkc26YI7OsYW69kRxkbs4DuQMdVdQxhTa8D9PRryaBoleFGR8wcJ6hQ2EUfJoByTGY/9HxeDTKDPRZYErddanwtmtEgH5bmZ4JQkYQnNPbVsO2tbGfacDgFKbvDJjt080Ts7tWjcP1R9FIMJgQiigvSzbg6KE//RUaMEutjg+SzWwjGf6vlVDx0xd+SXVmg7XrtYuscM8prcRf5UC6EEPAwNkcNf7vl54nHMe0cm+pwUJK9Q87s5dsbauz/xR7iAgJJDwAz/gh/EMlSpAFdYIfF624Kyx4/UFs2+yOr/Uul0HQ36tYJEU5/UHezT6+Um8n6B5amhxggvGUogVVfmHLpOFwerXDvW6xp1RKndUy6l27ax+gxjoNAdz5TiagtTFqrAv1XLlJ0EeK4Si3kbGEsGBOq32C+nqPlisT4cOJylglyjV6JYkbDqJvUmXBufaFBIgXUxQJk2LzMPbZ2qb/g08/ZaSoqp3Oz9nhJ4aa3VHuc8Ps2u/f11+965kyWcuIzLNf583OuWsElJH5lsWeIoK8smLIBu+PGzTCmHAxnic2xxJICT6pbUbdr+8zksbkmmJIzcwfH9W/XEMhCWovVsBcWNrAZVQ/ZSsm7Ac+TN6YVnYZXtbYfKhOl9fQEw1JUfWJxAk0Kxtu4G9NGiisdfc3KjB3mgC8y4NWWwMK7YtVvBWxT+AZRqIxtDmWLXQLg0Nye6qmm8VvevGNvqVKYf/73RSEfaPVHyx9PQmWWap+F9OXEvLI0l6A72b8oyRyBPVrNwVJ0pPZOdtf/PRNvz+CSiUccJz82eRBNPQosVcLhNbKNZM47jH1UO/LlJP6FMs+SqcRnGK1OmMPkm69AyylhOdBVxEQCpmxYCudKu/yLA4RRNL4e3qMbyBrRVsxQfwPE7tcvj/dVXTdgIxn3iXkvcT8zX6OdkG3FE24F2XkyVSfWmWa19OYc1iYS2oc9zbTiR501T5o9EeNxt0z3nbj5rwRcT793lH7em+RmCm/zpkd4JThO0yEK72+CgyLUj1uzelwRPIXjlsJ8fJjCLnDITfTZ13tXkw7UePmJjgJYaVa539GeQ8hvzX0GeRHxo9DFjmcJF+A/i+0FQBeQyhxyTo6MLP9UrDtFw9GZblqs93w7OoyuhfAfLscsOpjpyyq5W180DFAEFUWenXO0AdGDEAS5GwrWAeOjAoIv6AfCMbYvrmzE1rxxCl+yJz1j0J4Kb3yy+/PwpCInyaf/7cmxWjPg37knrxgnCfAfC/Okrque3eQTiNsTR7+o4Mmccz6zZ1dX5jIdy0dQeAJlv4/eM4nYgjsvUGxHzMMcKogyZNti9tpJjiBFh/rS6jge5hD/zkorQw+90f/X1q24zfa/dE68v+k4zkaA/ODNVZS4A5y1fpxngAxvRTs2HXd5Kuzsytebqv7/b5ExLmAPXz60+/J4s9bBGEr+FFgZpD6PmLWnJ/ZqN2s120IT4k8WLdTY2irl5vQ+wG0Sta0qCl4dRWFF+rUizynDxj6i5Omhj3bn+H5yVXfTiENJfDfEQqLcp3vX2c4XumH9329OpKFjm8Cx+9wHGvE0Z1p3B0tsR0bPqxiNMsLbPAW1ZKE7VT175CY+v/bSaythR0jWMQYnX3PS9SKjDjtdVK1H/lf1DP7OCjssk9SsPynVet+p62vlzEEB56llHB2+8eyWN5aRC4tdJ2CVODUjVwuW6fI324FXtgZGTncu1dcFfMFNuCLw+qHC7OpLew9H4qbk/8n+8hsGMrZsdmV00zlT73zu+TxmPCDvI+l3ftIhz/KIgyKlfdVPl35KPAjfOrHn4DMjjharqCAFbCuVnhweS8xK9OvHQi939xk2xLdQriQcV5a7ZKt+XZJmT1SvpegTcPZfHdKpjL9w4RSMibMsxBGJJIp1nZVVPfec6qFG7hUWCh7NzLLJXdw+ggDgH7h3fD/xYl7RpclL4ldvmNbXKMRed8qfXKs2sxuCcSHOvEAZQvZYGKZ+UEiCnxU2IYkT505CUWhf3ZOxCxRyJVDJl5QuWoxeFqkc9NaJ7dm/MYeld5hDkUqFctEeEFuK75snikyeMte3vJ73G5UjmXgSOEsYO5d5KESpPDtUhtMmB8LLYk6CsDyUb6uUT67ne8/jruSl3l0cyRQCsaU3umfF50ws3h08+OfL/GVq4dHuXUY9TheFWJrRz4UZ/PdvnvnDK6mGLbekDqbFplq+iDlqAZTwNxZxtvX8Zj13COWCdMYyTxfSuZzxFL0o/TCsVSrYG0dF6Q2imVy6H8L0KgRSWjZRfyFr6k1I+ohqS4N94icqZLV6UtdlehUo3ivNpupFnTBkJaE/02vNqRtxSPnO7cEIPPKp0LydSJ6WMP2mPYLhiIZ/q/Pnga3CAvPijTWP04SrCiuEHf2U3J/sD86TrPSbs8lce6voWEvSNdYSjzH4YJ/8QMXSihyvc0flfwqIfm+Yy5Dy0mkiHLP/+G954ZWBJnYFNC6lN0nktovbu5kOgwNtXJKYdBt7mxp42WNiPcIArBN1/CiogwR97fd6lDpf96zNOwrvx/mdue+uRf2qnSNpcap+d0ScfHfXVjaEa8wlfQGVukM+x4is5Enxv5wTiwymrnbp/oJDvIxyP62przShfa9Yf+KA3nw5KdlQ4wa6h89rFo4YNH0VHnj5eK1ctnK8JnIiCrErDxNtHwhZFzuvwxaTGuZzC3TdlxPC173Q7NH+qUl+mH/ufep4p6qWVWIoHnri0WKksohKB6saAYTLpeOlmimmG4hfrc7O5K1OthgHwCbrtR9VecBOadojvLhQ2vks8RMxHCcwGARmjQXe1e1zApYHTJYmGEBc8b6yNjN1DhL0HeGeglrlh66ksDQxnJPAin7ZihT2tjCwYwhjVpp6kFSyWcvu4f8lYfEaiCdgFnNsOCPmc9Xsequompax5iI5pLTHrbOMJFeXbids3RgF86KF4FlslcAbBrJQysJBQbRuVF+I5K00Xjc/DfHC4IkSyBYxLu+7gxlbcNC6LGOUCVsTtGYVTl7NXndwY5SFpX+CRTAAw9cBE4f1x7XPh73B3SwyLFbQnomMeqLmSdaZ2Kz6LiGNwnJU7u4Bm2u3ghOuHi+QLDy7TLHVsAq8nGzqht082JcxvYrKp1piN0VIxKXvCjScEA8PYdhKS7joEQL+0yi5a3wGoGjPAw/rnsDYUZclGMDODjLbuOLFdvZF1o21YKx+dHlpqOWvmAnIGcUb6uT1LkwHHZiS+Zh9oMRg3a6WZb0VoKPcELMjURVYcv1S5+BtV6jmablpVv0KuSdi+pIZ6CzU9N2Ktnd4c1w6Iy4ZNmYDVPgNnNrEe5ZLQWdiBwUDw0HCo8tC3DNrQTOGJu3dZuztxueTJRRNdOw4eNyubAOtmJN0p0FrQZoSxHFY7zid3rGq8dNlYcdm715aqOc41hAS8hQGn/ujYNxy8mHaJSULOBsFrpQWxCgH3AIXOaqdhjEUdL6p9ygbx4nyb1xda9/RQMSu3SWCAldO/ZtT7WtNs83moJMiW/tqPEkfQQsQCDBGnDUHRB+1oAK6XKxGc3zk4ta4MUyUcUn8HnTdKGKs7zqBH4OR2FfTBWrK6w4e+UvkJg7L5YWTATJcZU3V7GlYAXVl6KVmNSPjMdj1amJNLI+xdM4+j2YPI+KbkT/GIfhloL7rUjEGwxq/1RGn49nLgN6zIn1gwj+SXk94/XFcTJjtQk/B5RPvfYlasL9t0A2/PDSrJRxWIZXVfbfP4Vp5ldX5PEX3WTBlcn8F+Z8CfMh4nK2oBbQUv7eUBfY78HQ5IhrzTtRdaAID5e9RWB5pYunOUQkSYbpi5auFe/9AdEFmO54F45b8mipxlRdZLuYsBDRAWvrosK2WhY5dIBqr50L3RFep9PIJJjP3n8mSexs8hZBZisOVKOTPodDifgpUub6gmhqJHGIRF1Velv2WAzBodHpxfH2/eh7PZd+0GWLHI10NsQ1oEgoV5kgZNdxyP9ce0+6h5H3VaVyOR3v/wwavgoSif594/DTlwjgHyUu6aysyU9OTY3mGxs9WnWfegE/7LXfeNwqP2mnYz3ifUriAIAEDjjtUYf90F+SLczA/L51TVv6GzRlsueKl4xvPcyOpoQIZjjyDkpygjokQ25kIrqUzaY29aPNKLtwI8FpGiDcMg2SttxAdFC11GZ9+yCra4pD6EZeX8YgXbUt+6kXEXZt+b/fef3TYYQbjVqT46ELWxK5ChXemuxqLcRm+O31ln/puUpWB6aq4/3+iCDbWIvNA8ncuT9EaDmu2rr/f/Plgi1RfKB05dQm9Lj1tmp5YScXD8dFpshzk0YDFJ12dhp1kHo6T/uFGY7E/8W5nutO9dE+vhJrrdncPk/ZrHu0qKpjQSgiFhSPV/HKXsrtIwjyFGPY+V00PB4T1WXKbVUDLn7udjkLXetrp+AAuq6OxGwffgRxa9vV5Mixxn4dp70h5r4ukCgPJ9Z0B/8OZ6qi4MRTVEGuowkFEQO8LCmvPbBwFuA6zCZ7sq+JbT2DAdv1BnHdth/LDkBVptpDSceZpsL93nSDxpE4goyix028sorMaq+FVgCb9IPOu4/IY/qiEY1B5SooHtcFxCkv8wpOVQweSp/9JejYYJaoxSO31kPAtjDHODkluGxH8cHFDQ89rnTSAAzYp7tc4NTBjp/ppdutO3Cpr5zklou7jMz8TV8m+97BhhUlU2MTSpR0XeL82psHmF/dsPqX/PR6s7Bq1Kb5X7hVZGufozwzvBhOhvNa6x8erGvjaeQ7bhAhHr8BBhvMtaDgvTKqD1IKW/o78jfXCBzLKrCjW5AmyaU9zJSkzuFVqwRzE7BRzjPbly+3Zia/dOpPnFE3smWWphD/4cufu/6i2oL5pxy8q/p/j8sXObYpx2FNt+dMaDSfS/SjBZ8XH9npliReQr6Qze96lvzkdv+qAie30ydFej6fAl/2KDAhryMewlpwSAOEf93ubflD8oc9kiCUhYGnR5yEfIup1jxp4+EP7sBjsaNztw6rrnFAix80QTG9KOuX8ccQgC39LQkUYnNWy6e0Qrm8XCM264TqVpCESYzP86ntwMDkDO/qhUjiwLu2ePd2B/JzPSWzjmQlOCY11aAWQCyLGvRx/uNXv/yUOkyxZt7fyxlZwbAVcZjvHXiJ75c76wZXQ2G/CxNwmB35ZkdEhE4XRN69BN/MdMFq1GrE3Y68rVMUzXA0Rd3cQbmKqMcUmArLBl31VoXt+7uFZM1jF2+mFUtnIc4+Bo7MEOUs26zTC7B47uNAG6x2FzQ7jDgM9/aKgrRrxDl9cy9JY89FH/hUT420lwevuB4yWhK1YchhL3HSSbzaKYtntyik8rxRLR0fsNj/Nay9Mk1uGOAVFrbRP+Gxc2OFJOWSHrZuffgVvS7nMjvAx3DgeoVZ/uNSSt3SVjTxJVNvp1QOu7Hv4osX4SkO+ib8OfDyaHejuANPD/rdJAvF3XcrkbOpqGgt9/iE76fTXaHLYh9XxKaqWsg7YOQmfYPCkG0t0o9vw6WSUXpkMR8Zsqg00y+KJDHfJMtS8T65vKpAIxcYDXKM7HTjc1zLAr+ik1/DsG13bI4PBI2EJqbTJcXjizMQeLIXRJmPJ8H5eXT7SZnEsMrMisE1KPTMtaMikFUve0jTnKiQOYvtn58TpaPit0b52rL2wwnzJk1QrGMiQbg1DkyTveHl541CKkusOEyo/HDSEivRN880SCi24DsV+NaYx5BLbEjCiX606idwsAgW9StMsCXLDQwG6+pofFiXHRS61pzKs8qYx5TDJHxn4jXiEO/iqIoEJmQNP5hrtBIaOHRLlraOM0p9mWcokR0Iutacj+yYxTx2MN9df1xWpaTe7KiKxjljl5yveW3vZVGAbYBndz1VNUVvGAk31ynfJ+3waqNFBIjSUlBGp7OTrXFQ1SWbFZT2VMfZEHgnE4XCgW5RkBv3SGQMgLHZ7/ZthmNNHy5PKd1WwtTDkIJL+4D9HRpOPgp0LgKfWaiMgoxh+IUrXKIRs9TkrUVPUTp7H3jcWDcUbKM/4AZdYx76208Y+n0m7mdJLnl1eI8Bt7gvFZP3tLO3KQJcBNIOnQHw8T/5eizkOC7AdgcHPUqnOP8lldvlGdzVZ78866i74k/M6n5rxWx4wRQqMW42G89W8Hyfr+4oiWWLj1ejylhcChmKyWkOSwlE7XxZRi727UaZ4zHiheoOI+2gHxCY2rKEe7PnL7bM1ZSXTv7Sv7UDlV/sKZ/tyUF3qKrYJ0s6OITrcepC8vEJw17TyYQcdRspJGZLCZpJCiOJLKslw5tedzAiUNip9346YbsFtJdUVZlntzjc3+eEzV7kVemiq1aBj4utctkK1GB58wS+LQmV80eFXCMo6CvwatQcIzlgUOlKMy2UW3y/AHLtQ7Qh55IvbIjTdonXQAoC/OD0/ubsSrQMPDP19BFXWXjjy3jIE0xSnFwjjgEppZUMEvZC8fxJZHzCH1ps2DjegYBBDhuJwWCcdTbKbU4yjRDpQqfw5e8NDeEhY0FWFOwfPxVX5399tZBfngrRN2wIs2GBQ6cFtZaRVZGCEiWuCESlnaUL6ohPHuX6vGv9XUFmWnqqTGjlvItFbY3V/Nw8qh984BJvVmiJY8nfcpA7HAaEDl2yMjfuOIamWnEIMGlsdiVJ7FoTgszEdGty1opmGF6Ubq+eCioaa2yH3uuiy8WrH7gEfZM0bn63igfz8e6x2q+F54eNbCqRYRuyHXdIiptQxpJxxQJte2jGyUpkKd40kV9mECBjJG0q18ZI4x48sLSB2LgaBNna4Ene9sZkus1CiZIkMnj1/3YVgWVky0vRMWNonSx1Zaph8I+3C1Uz/XF2Pt/76Gg/zkD/2qsghXVt6dbreTuIniS1YlGe6AMT23TTu7BjImUodv9Cvs+QcIBqTcEkScF/yZm7D8USyp70XbNSrziadUaErpmwaIqq6YZR+fuCSBiEYJP2nQFl6r1BaopNjkhoco1zwk5mPj3sfHTm/ZPl4E6R+na9NnMkJCF1XS8nSrSn2aEdtBuRMDsq35AWLdmy1Ayc1p/m1ideUE6eZ94DyBKDu6kisSScajgU42GiWtmR5sQ4PYOp7wi/Jnt2T2HkcKBhKzyX4ko//ckvYg5UekNlJAPgWSQHL6bxnw8mz3JkUHR6eQGWZHZudLD42FErJ/ZkcsuBJUGuzHT4EgOwoJ7NlMUyWAl3XUw4a4bnTo7RgigW94sYx8uKwGKVTso8A/sYyQo2kneGpScDmaE4p++n2IYO154eC2X2oLVDSKW09hkRJkEHjxJLjEi7aocGkmT4cgAssY1Uai2MsIKsKTJWufeje2fsX387KEh1z36gZDjNJac7XvZx4PVDg/NZQqE/MXBRq0o4AxS51eFyJSp3U3SYHa3q2VAqIU2hPSeR050G2F6v6vIRjIDRU2zxQN1yXX6bs0G/YUrrWL5FsSHGSotHy+WreC2X1qw2rfotaqLUhbsjVvUPGYbFi1km5XUsBUd4gkHDjIR622JKnfNuH56e+rByutB+nH9zZQr/l2G7rpfuOMPr2gffDC7Pza7E2TxKn94bRZtSKPTC/i0G5u6mqpEuF+Os8UhjKJwIus+gYUqiKH6oyrYIvAjvaYRrNcMeHE9Dn7TtHbK4w5gP9oLpZhhatJPMg9sHKAyWs2U2TBCuNVMnK3Lr7BWG1MBynVT8qx9NI0vV2EU3ObCF3D+jgccYcLDST02mU19g6VVk9DZ2WHktMeaus2+vhVgWTBa9Ffx7tWhy3jeo9kvZgnvEnxhPp4XSGMPL5/1uYFka3cDF4FJlNbAMHV2xTMMwnXzH8OWlXbhaPsqZLTGaoQ4wPjUaTPy76WYqnU5R2SZ39Y1u/rMu8HJ5MTFP1M2BWC0E4MTH9M5Kw8/AgFBXbxYJu0GWHhiWqyvAW3zM8b+9d+mfwjy3V+0jeF02bHabcjS5ELkimYcxxSqXzuOAv1UDJIXv+OwOC2HxZDmfGlYPOYaBTOhdURyrtZzGike5lhwNP6V46rnNwZXJwR/SzSHnUN/XKs1uzD2dw7lM3IHkGK2YjtQ6VKLxOvfsoIebf5aH1T4AK+iejLSXWm+dk8/X9aemPZuCM98/Ln9uPJ6O7C39q/qWiLxy7Vs+r9sdK5i9HxSyC1/9XcwemN9rY6wMnXJ9/tKwHNdp3SGA5LqvVY6PhJzb3gu8aFih3j5UYgOzPlfCIZ/53b7sbs97DfXp7Iczmaeb7fy3ZGJTi+ewFN4gzkE9vHE+uNxsta1q1qCbV10oaUYQKJh0s9oawrfM5Gc0kkZM57COPk38RpcgGyWUtzo9P1xczhir2K2nL7u1AnLNDQHfN5kM29WQTR9s7ewDb/LLHuN4p68ngHAjnSgXfIN5tmHmz1/gwMqUZD/doOb7ikyfM00NfdBlD+9srA5LtOn51e1tIAmKKBRom2RbPw1+6bmkfREcomDL1U3evn5wlzwegzm/Yosl7fs5CPkScHQaxQMUSEltp5ojPUE6LxJA2WC/rVIN/5pBifY57aSHXWQVZmxn3SAQ9j+ihjnbnnSsp63fNKPl/FWMASSjMqV3d89QQMk5G6HvO/TJDSgOhsORaOxXiAWuMWbwme69afaF016ado111ZRHquvbSHEdZXm22sDSG2dxBYM8BL4IiiDYKgW2kXYR8Kr9Aahy2VP/6PeH2373mHT6NvkP2sp++g/9crfkS+1jXViYwqVn7tsSZ/inf/GBeWgU7HPFL65p0qiHtXhW33o6+duP+UluEYsGvWKfJd7YaUC9FUFNj9DWxBvNBG4ivO5wRzBRClmo2nqlQiOX9F91gti+x1XJhCcgFCZF7tTBGEFeekfjmWcQXMJkO+qP5xu3aATwEcYOkviPXVc/JhuM7LVx1Um7ieu4OoNuWnjtAmmd5JdoveG/qwtQyYAPLJk4ivKSxFJNf7sx0Lc5YDFXahqBF3R6FpYYUtiAXC+hodK43273UwWI0ZKy0xvUKGbhiiOkUcgVw9ANbCA3lmx2l0qicOuIDvuYbq0++0+VTO4cDpPI1SgbzxfP5uzfv4oA3vWu50L/WcKLwibJcGbLHtBB2dTDfl7zoYc5tyzbWlq3WrcYkHpcg9LLvlguYY+i4V41tC3dkZXJlA9SowKMKk8S3QnBtOqk2XHsO/WXhrP4sgK0LTl26HFiP+yjXLfzbBHfQW5kSKrscqoTp3V/WCK7HHF6GdY7ULjL0Al9leWJdloiiDmO4pLAYVXe3wsGG9PROh8FIisS57SuG5M+KweCHuXTpahlzOATVLApGr6qMOTCYGN5HbAxUUYULEvgURE4CBSuw5pZxX0sA7nDspbdy2BLSitqDVe8zaSzKLqD1nV/K6oHLSRVKG00XPSm9fmYpHkJjMhzXNtWDq7VNkPFm72AxJaUBw64NSHFNHcTKpWiaAIt4UeXuuw62gG5ag8mIKdl96CSZIwPAxGrHlDEZevNaMJVdxkQaFjmJMPxughonsRdX+DRZ624m4J6EwkwSEW9Xi1AB5s1QWh1vHi85nVHVFFD75ZXBfUNgaxCRcXCGwukATY9Xtb1JNFV3l2IdsLWNslfP0+sxmHtPLuJJbHZh5O5+dUwvbJ5MRkM+Hfag/4B0mJYW6/c1g4zwSN+QZb4wqomyywcXu6L5hfYOf4oIghBhbHJ8V31hoW3C9SCDNYQNivz4ec6UHsCfgOmRV3P/nLq64dRk6OCjnj/r6R0T0YCaDGvPjmfzeZ6OylliEKJtP2UlUgBNn2ongje0FRFNLpCmB7cxnHyptXqi9f3gphyW8plMdlub+TMJVZNHLmk6+3y4om2dgnPk3FcoTZNZPW5u4FjAR8xSveZ4krPdNzv7iwk6x9WLNklyfwHXP60K/AjT0JmqePXFgMChoX9b6Tj+PUf/eLhaCLuZeMCVESfzd4DhYqHwLsNmlMMr4k8SxD2BY2eFBXNudVyVfLvVx20xq4PVHuvNbnfj/0vJe/JjiTbDvHoFaVq+sxomrsO5G6J9Rt9gYZknU+AUjhhX5jSWAJ1rsuyoA33SaPIlCRZ2W9zq2JZzoyjpGU63dHEuseRMIgwgihJylBxgKrM7dX0WJ/TxeuK/VGZlJr3ZE9YB5pAVDAiZiI0DQlf0DU7GNCWW/lQ+N4Fw3YGrI/l0FsVoSOy+RGFjProvWaeJStOixw/FvHT+Tug10OOd2W1hMA/rxt63XJqUWXz7lFOUyVVK4d3V6ACdXs1bX5MoIM0/4X+H3thUOSidY1l1WZxNyzUm9zUjRc5Zg+sqCALqxxNLCfmErHHC5tT7jERDf2QvVoaS3VpeLz/Y9WZ/U+13Oifl5mfnrRktKZQ7GQ80NnWOq/p1EJN75FY3dml7tWUvdJRqs6CIlZvt5yq+wXMVp3dM5hoXEYpQbru9e2RBWlQEtPjKAHLv793u/IQOmQac9b2qefo8s3+21ou+jfXCj899XbNOO0PWbYYrpTVz+l9X/InS8yZ208dpumw5dWz6KchuBhoce2n21NRadjhznCsWWu4/j0pGi9iTFn71uZDNScuZdM5s76PB5cOGLcXajofyIYhwdZZvGWEGMkKux1CMczXvW4URx3qqI2rIGB/9h3vHGR3say0x7xfMYzxLd8ZAfuoyjocAEJQkkpkgFQmCxil9c8ncEVMm/94/+WZ7K29Y+/5cudLE5K8U372/sQ0D21+rywoAyLSY7OFfKCyFEyKkxTyRZqcAbyFcymwldClRObCg9hGtMPmIjkfjcFKth2N52r+hdhKr9BYtV5YyqgtSO32hIRyXMJdKGEy4oVTbkZ8exi8dUYyvJier7tZ2mEA0tIivqYPJpyTEJPOes830ows0BCskheu9Hpp2rUXaPS60iQTxkyUxZhFq7n9SpoptW7qkp+GgLhHj9Ip7I0Lw7VIa7GvsXsBcj4a1x+X65VDiattX9jjl8pz5FE+7aJchWiDzMgEiCW1J0uvrohv7+p5FUtzNZkfWxPm33PhtvNKvWQlZCZNBeIiDLSxQVtiDe3v/4ShM/vtCUMyUNw5+3Wmc58RNTu2cIQqs5ZF/o+2p2wy4fQSM1UU4XORUdJpvIF7QiMRCKYZitOiqjfSVVA8OSTFibmKEM4CobDB0SRBbwbBl7e+prHNK086ocOljjq29eDXBQqq+SyI7tc1ZqxgikemN4g8XA3BKZrwZX3dqPCnDBvrWi73VlGxRkF3UciqRMpmJuzK7dAPl1ggTH3M0CeXKruS4Ta2JOjd+1A2JXpr9eQSCFIhyB90CUlvMmYdDNYnEVx+x1b1IJbTqDgxD5l9srAUxooVjuR1Mwwg8EKL5uArj/xnWSTBBw9gbDfdvwxNqlKjasvsmxjrLbLlXt65QZOBse50i8DV3YFpJ4HKfZmGs6ncVv4rUrsffSnoqc5msplFO3zHoThvkL5k5G8/ZepccnV6hwNyrAipVYyvxn2LPif8drLCBB6uXRWWJZmZRIZA6xw1Vpy2K42tlQZaklLIavF2z5bKLAnoeilK3kpmXrenqVS3o43q9EY7f2Eq7+zc2ajTqBOlYeFx/REWMGxtwC/TBDmPx5NMqXhixRMIdmRSQYOpdDToWkG65SBXVwLBAR1OSxGHWYnGB2ZILTEjcoHl1PTPfiU3WF31PMusWpyYhQoRkMuMy3bpo6/QUOTqz0sAuj2rAkCZRd7LbqC477niaikrG067URmiXtyvHbWDmdRZ4CFfMEBTP6wcrh828XmcjdIuN8lSLh2VpOn2JQxktOBZ0BySYNRnBCAwhO72GrZozhAlKub79tEt9WRq6KeCLcYIRuSpJGSbTdR3xgLC+pmodaZAl/D7YKMpuMFyaQ6D6HR+6IuudPFgC9iqr/JoJ61TBPMYzsbzUH1K7GPpOtcGg/bHtQHPFW0J7z1HpdH75JYjgiq1+vsUK2QrnOMXon2EL1l2WvWc2d/in3hhm8I0UiSIThk5PIYhbCm15/PZxaSrgapgZCUfDnwpCmsa3haoeGgPu0vcUVYFvdtIhq0KkRHwIoSbOXUbrWn8CHTvRjiB/tFRkd27ej5ZcjQ9DpJvq004tzVduC2boiTINhMBpXe7T18a43y+FtiarLqRMUy5C5wjUGg22lmaZnHioWfbIf1oar9a0OeYyaHRsjX5LpjCN2CPPTDQClo1ZU0V3cCjP2b1TcPFfL5YYBL2lnJK1XQN1Yphm8bSQw1cDFU4Alu/CFbKpcI+xzAc25gbvqKeosq6ZfzM9hwKoMCm1svKvfFwMhl1XauFkQWB14vAhal0hmkbijZ2XVOBC1dXXzgWFUUiK5ODosf9R0rlSefL6oaly6qS7TEMgSFueedaXEDgWQZrOamqH4SdQpAQHOj9Yvv/evm87oKReqkBL3wk3zpACjAj7qvCh+6BF/60bnU6cRhYMk2cVNfDpvqLGdUG9YL4V67DM28ZmtBEgBdkLaojBEUR8xcrVFSyAwGDYJDZUpWmKVk5VlSNgIJBtLnn+RbzU1WksNt38NAYbLM4DFWQV2Gwkm7wLVXC6abZwXTd5LgvnyDJSjdZ/AWaJCH/Pc8RJZGTTzdQXJOi9JXueMpZFhOsEADIBmwNjcpYynKZGi1USHs0cUwjT0yGMafqGjvVxHgEZaz2KOEu1AN669SYDCsFj7zZ37SFQlEESHMHBM+FJvTmcZz7tYvHLriuNHthA0FOZ3ukHkBjYBzJFC6OcF8V/3VWFIvENoahJJEeGsYlsLnfz3msGfrL0Tfzdjr39+6aOOg8dsyLjjV6xnFLg9I7Be0VOE0z/ZSs6kZiCxQhYod2QLXQRHgwoJEO6OnBQGpzFQ0/yTMMy/HgGPOY+unagj01XWIETZbYROeGAfsLqgklXDW8iTX/W7/fkXA1CNC/j3+uTb+mXujfXQi6SaZ4QyAFtHOGEd6wtB2WmZ5NY3XFcxnWYGz7Ev1R2G0biHAhcQMDwV1bF4D9GU6nINEEXoMpuJOgVTSG9DmhuT7uHLiXQoDBPYvVipfAkr6u98Bj6VM+941UQaCtc0dPkvzKWbZtKCLHMSyMT3kSqYuXUVBN5V1bRQmdto7FjskdXkCqY0kch5HxcRMF85kbzzovu8tSpOSzLi8sFH29/LHLpdPUhrqHCHJuQ0K/4+3071f3DgyD/fDSLkme5iL0t6WEkSASCczpSCAljuQbW3ilSLJuCkieV+SCFFUn6neF9iwnY5lGUuM89qvE3MKKFxhZNmwZOQKOlTIuowXln3BcVTrREj0oE9FavAZxjglEJTdIqLA1DowlOV7QC5VKFfGAd3qihRSLY1uiYC/83Ux3R0wkTXut14cQGd0KQy9dsASGk/aCtHmmz0dRCKW8D2K2sRLF4Jzx/WgwLp5NMqxgSro+20zrMuq0Sr1TJ9vcuY/N4BUiV0qdWc8QDTnAuspGdV0wnjjCS4pMvNM3ytuHS6/iYu88tENwWSvb9M7S8yNdEhWt4ABsNy4WqBoppZAHGFLyJ//Zn/6/86p5YToZvNze+wA7vJ+RyZQ8fu6B/T6I2NoU7N79Yn3xIbeORjKFZ8QfA+9VzhthojXE2a74Jsocg4uP3+KvpsMgB8bfnD88blQq+2r9Y8OcQIpottEPRM+jAJC54kFg5A8Btk+tZt+LxovkfOW4t4kt8Lnzp5lDefnE/waXotFnYUDNbRUT3j72xGn16+7pV2YKt04fMYCH3OERDbokau9HdheswdUGm2y0RB7pvc0fm0s34/JJw1zQ89SI/hNLoXJYyFdwqQsAYYCfXeWapjw4xJQrc0MOHTooRq/WEh7ELP4UIlylF1c6neGYANYa660kj6I3DwpfXoOgkN8dQqQWw9HQNPX7bD6nc+CP42OBzXgHDbE7fz7O4E7z5sLgFRdP9JVGazEaC6+5onTpHFYSc/hKPmPm5HWkKYfHhEzyJL56CD6cUiiyX8DiBcTjKlOKfUaVVSf2ysy912eH+ZpCA4wj1Ckz+Kgt8CzvPV6P++21g4xON78JUu0zpIRt6DdQdaAMDi4RkEZreBI21WENElokYh8Zjqe9HSuKOoPxJQYgydywoKSO+GtYSNddSzJ2yLNxVj0PmlMvOe3YaTW12CcW+oWj2YbAyV0mtm5yHC6Vifgbi/vy2f6hR3E0DyUIPpzJ+JFCpg0GUxSpqs0kDO4b2JfNz8lqs9k+B0l+0Jyend6TQICIYIHlNybMZCpUU3dFhGbTHAp63WeNY9KNsUMUGg8htnkPKPGcFbhmQwzPeOs2AkAqe7rsmdvXhSVtlsnnZr39+URXx61fK4hcCl0sx6LYhZWe4zGzqa3TQaF2LiPjwRhFWko7g9DsKpamGaYORqIZZDVbqjk/KXGtfYRJeyd1xqbbu5bQuYLsZz3/x7LT3mWiPZiIVx2LPUfrMdlZowSjJBKRYMtGQYpAERVF7cvRbDI5DLuYeLUEXdskEiSxauMb3cDY+EOLLb4ZCoSulotUV4gqhu0Q3wMzJ4EEqyBI8zx13eUtuUe+I9EUcZJmOzqz5U8nHlgMzSSItXe9Wc8fbdqQNFaippkWBRuYOqeWbkwZzxD8lNhquLV7vz0SydeaLKvaafao1+3eEyA1X1myfmQhhyzqh5I5tfeu9LVnbh5XD9PjPAY8EUbOJS+jWxOVYHgRHg+ZA6/sNeufHc3oqus4YTZaMqKqWnw+xv6Qd1XBsXiM3ryxKWQy19loRZHVcko7h/f1UZn1BgvMPS2fU/R0W+oiqGj7SBPwe+qGKz65pcEjChTZzTl0Z5bs8efUsyUS0404q5/fVpDKZG6WP3Gxt39+9gOfkySd/vj08ub29nFic/QLCP+viSGge76xMzSIP7y+6bt172Yvl+V6+KfenJ0blQOUm8Dci/KlcH7YmFzbTmt6gdJLa2PwD/eE3vO/ji4f7qfGikccFI3FcLJAsE1tDfOXEg2FprMkHP4O/xpc2Bsy5dIZKcKik40nhd7ggfy051qG6TWaRnvkMPCIJfM45Mgt3ZZD0KlnToDyYDmc7ljVg2Vxx/+/yZhbLhnnWcJ5WN9J4QQD0g+8+LiufgmvpujDFVWWoUsqfkssiYTEjmaKQJRUXe0Kxdlk8lwXRJF9pjajeKx47IKAo5x/bDn3VVG5S+gGhP1stkPjM5drvP90AnDD9PwqeUAAvMxyNLYykjCYgUiyUWWJiROljlb6kBbQxki5cvZH0n1mkWliELDUC/+9t/zKpUE6XdgyundHjP9f99GokTMGs/qtrSu8LNNMw+wIOAx8qUnoSChfiZJmGbP0XlKEAzozbkdvBU7gzeaZtZtWLmWKuDnqrCryawMTR9u9SLzkVBZmODzGsdWhgN6ap7wZFbxLBocHTTHRoVF/SxkRWy6uFvq78M6Ij1ol+zHaBZYiyI0FfGAYVApanDN+NvfWOLbgNRsMLJ8GCguVkA3tvqhKopaGezUWsSBhV9gJpdIaK9XTShAltqF4NZ1fiaatmZt28tsmvcK89nGtg2nx7ml807usV2vq4G6yI0BlUSZa4bPmPlGCO7PY04wxy1D0Y+yNpZWViQF0fVokLkwH/oXdMwJAIQwth0mrXH27uJkKZPboKU20tQdfBM0Vsj4iVeicNTp9M05S4M2GdhtdJr1QuIxajZx9CAS/I2buE2nD7xYr03SjXiy8R5tieGqY27wfX+XYg6oVBoRJe0IsRadkrypiqZJqcPpfROTMoCi6P2yYQ3fn5/uHw/wn2+5QLARCCcZO6Nvxg1NN7WnvajyrV9iu5TXK9kO7DSyT0LN7NCP4Z/5Rez/VRRMAA8s+MBaFIjCujGK5w8Om4N7/PCbV49xXSbbyFS0ZTsZe+dld0IioNLPrHAQZYz9+cAYGBpb/dU3nXK9iuU+JAR36RxU3N9ek/V7ye38dWE0xHK18u2gSUVp2pIWNIk2lzgKBjauiotM5Vd/DKVYrXuKmV3HUfPdnihkWQRWQLf02fiWxinKL7hygDgW9/alNwbyx8Ieb0egQigxkfUpxK7ZNW4LfIT8UFeJIoVIpWL0eGdFZO2cFTePg3UsZTaagkt1TjbZzwjtnTSY48v3Xnq4YBlNZS6Nt24lae5hkAC9ahrh4sqrObr32EOeK0vcWnVcfcCNi5k6mNhKhfOZUwQh8PS3o+idXPneKUmbcy3/7EXS00X6VkrgjSIyAi/IhF8yh52F32PiYdxsC90HX9CgDE2vx7d4dTJkFfNVwHJVViWaklBZEsHOKwU7PKsGVfnrhGgNWn5jG5q2KcCJjfAMmOCYcd3tGiJyrHBw7XBVOsC1eCy1tS4G3CbdzorJLo9Uqane1lT/lUb+6ZAubAgImSxVc1BauMS5/YLHHFwTQrEa68FPEtht2dkGVL/r9um46ES5DbPuDDIdBYup9L5w1O+OqSH7X0SssbHuhk/iixv7QL1VPU52sYGm3ZU0y4jvrMoZt44EZtJfQ7h0Pf22Aw0TPwcutaHSUhksUFzeqNRRrrmWxx5WCi3ofTyPh3Now3TBY7rwwnUxOq6hiYc3zhe01wD79yU9oUuGshlX7NwdzbD6/cHJmtHyckyi5G6jB8Iw7ynXAPLh7l0OnvPc8Zgx1RXH6jou75AMAKOk44Dg2YktYHituVO67Xrx8RnhAjE0FcUAxIDDaqKHNZ0yS3xOwFFYzy4Ahg7mV8PVK7/9PxoTG8YboXXpDqbLqLnLf1RSxb2SppAI2FvhG7/OI7AZ7r7j3kUaBpW5a8KaySkkiWKXe4NWz6+ZfmtXxRGJqSThmpNFmQPV9ABqyfc2J5GJg9Tl/1GiMkJzijRAKIkIrASC5Vuc6aZ48yjpjMArdyg18ZvlORybAoFvxZLmQP4LXprDOPE2TONoynjmmpDIVxZ4zB/08SzugamFuCtgMZbqlW/4obUGEvoIcaZnLEM4wQe+wwyHMJIF8OQJ4q7KS3kj7PB4/mpYkufYMVi7hO8vXcKhkibBYaxhUK8MjVexIT5I49C3GK96OdVu3WO71D995tnST3amPf8+2IDwqctQa9ehaSiKApVp1XUehq41Jlk2hw6m8FwQ+3xOQRbzbw1ALzgVbY6IP7A0gmtAGlZ39ynVNN5Znqsma2mWCPK+bIm7pFZfA830uGe5I6BGvjtMYCaWKY0igB1gscSzQIqpVsnJvRD0TX2v9dqdrxdDMNOKvUqCGcVhrIOlYtM4Z/fb+UBZ0uypKKJQStKDYKmvjjKkB0iJ4SyPZNI71mDfdSzkIok98MpBUTabPPrBlHsrQtJUoCnx38wWGYlWjO/jBnxdZstdxOAIDUyRVj84/Cb7WDE0Ph6PhcGCBLNbtKCl2jXpRLm0f1VIWZalZKVcqVb5HGtxW+6m3lKDpRdEdnpu1yVJx5ehydI9PDGH5UTfLimIwngx6m+iNJALYx4Jpj4qpJNM/oGQJiKz60QzVU2qV5r7f9tFsY1KLSJgjREFKl+EYZyNkrXlHMDy/bLVMjEjBDJN5bVN8Ks/zyWzRg1Jos19UizUn3iv13ZRz6Oo7ubfStj8iSaHCRI8GK5qhqFICIhfU3kFwigLKv+XLmyRjgnUriageaaquV8UunbLgijGCajjsXLGtWcqbGImjCAxDMZZOeAEvILWpXmQrosIiIx7OlF5JGgd0SYvNxwsxzWCXiXhYNGhlZNWx64mlEqwgYVBMwo6FHHe54UuKUL/3U1dKi1/ds5YmUEjPsc0EN+1nvcSN8hGeg3b2Nkm+EHd1Lo0P8NceFt8GkZxfZC/XBB/xpBcn5IbllaWarXCwKh2WInF4stvk2O95jmVIjgKMjIZN95m7k/3iiKUpEuPzb8+3casg6mRkL6VaA/iMlrLnMUNxr05CygzbTw5inceq1U+9QZdymSy8JtUkvGBZhqbw+pbEmMq0vRZVmLd/sDcx586s6upW8b7qVUpn8OrD5ofpxBPwMy9AxJen9uKF8viJln5cOmv2YvGmbngxpCJZcjk7elw3PKdpichd7eH3ayb31+JKw9LK8qctA8hB1qi2B4nWKS0HWRpnmToGvJlwyYIgR95+2jguRR0cVjiORh6c6jYHQlxTnzsXqbaCfRKH/5st85/lZvN+XBAkh8lRBfE/OJrCyw6a6m9r7Z9Fmv6J/GDeG6o0DjwDTMHCRQoU0XbWpZ/itkE9iet1I6pav1lOhlAyJLBYamgtHLtT1x97XcOBz0sGCNEYMIwMOoQn6bAo3eQvS3LnWS/pD4F1WyFbudNN+UbJ5yAILuNG2A0qcj5fsssUXu2sA+NhvBKShWfbjKTKT3ysoriHuLau4VQhxjnc1kkkgmTaQpVzkhSaZk/h1yCsQmcA7BXdmJEV5ac8Vh3axVlZlQ3nXkjz1b0nCW2VZxzTgp1Nl7VFORvMd7Bxu5I8+AWrYPUG9S4kEeiZtJq28VCw7sDvYFipWT1DYjLsWRiz+5hg1HVt26DwvOc4tmN7YRSNiq4jCuhKwZ+V6hWWDnKoTFb9mTegUNQqsqS2nZxJnt1Npk/kmiJaRJhyHFRjgulrbSgpBy1oVaCDK5ZFgAbOPDuGG6ZumJah3g/yLcTrMoyQEgemWKuwT4WgOklag2gvMwHZWUgP+n3kQ8VgPF9goshXckcWxRLCrZQ+PSo2vlypSKca+IxKVqahl3a878sb7iVvIYPNjGd5ZoHHseHi0Y0t0jo+oI3VgjQtsrRFLFqr+IGxIrEdW46Y73oFLmtm2/4qmWvy+a7iXpDl+Ni/5Pv9XipSYGxbQDFiBdaA1rqUM1Cd9vEJErx6mQ5r0vHpqdEuKztg4MAOVRCYSK+9ixM4jCxx0FnXlUnQEsaFKozzkX7IiGsOCcGwvRRtWzp+pmLuaU5oWSqbprvFaeCpogIKrwJbn+mcRMd+haKsym8sUV6taI1RwpwPtwejA53FpiBDitNGPgGaV7FISWKZGkatOqqekgVA82tAxdVkmztWjPoyn29Q2v+G4Xqdc5wzJOsvGgjjcKnozcSJZDqWbll0ZSOcG+fYlKzX8HqpFHpavFZjJUWW0ts985erWHl1Jj517b2GN8XouGc26xjUuUdD8DU8VPKrEXBO7u96nE88PItkeAzdaHTawlaX+KOoklkVzQqSXlEoJw9y517K5THN9Hd1dI8sh6mWgGNXGipjqUFv9rPThdXlOZOx95bCERjOqE4LgUpVw1wCD7rrK60Lc0Vyy9DBmKlDqmvamgY8sxLZ4cYfnBq1aS4yFImiEHddBhiTpw0jyc0SNoCYCvaA2jKtQDkIW/8UJwgJAPDFrWBoJhpdKRZyMtPm0NcAghNFm0vRsuGGim7liO2FOS8bsUFpeHVpnFkP4yiI+spkAO0ZHQ+bOGEahtX6VGTYEz7NrAUgrFWrzCROZpa2oNCaLE/6l03YtOQBug3VPVPgNYIfqv7RBNLzfsSzBiuSwKt60kChmoxqK5opkqhyWwmP8azS9iTsmroWVXw1Ndjx+1LBJh820cNOGGXCgklz8geqBdSWsNsSPNEFp3leGHumgNYMs+/Yc0iOhB368DGuRlCK6we+Lrkvw9KkF/qy3VpLfEfhlXZ2KWheN6Mr+n3m4bPPjhiyDEadCr8SVIPPcXd5WBGX7bfhUz5k4J0JuH41YlOQJVufESTPs416tEpj6OW2xipPbNqCPW8C11FGUgRaXfLSKPSaSbp+aiRDRFDBetcnvBVBR9HsCW9WHlN4AYwx524bftJDek4zVqx9IxAVh7ryJNNHugJFKq23h/Opw0eHtUha4xPTd48N1qHv2deHoQWUh8pzum1xMApQ0tybtpaHUv4TiorvyvuNt8P9rzfu8iV189h65uhl17hccUXua7aW45h+H8e9m433wf3n5ssXlfWja9zhi4RaQMsAjjSsSLyBlCjvZpkxFCSdSyRceWSU6Cv0husZl/ytk8/U/Cue50fuqX3nYAP7oLLOixnZrHTajAI8LRsRe2XYRVXcSHqDBAcQJ75I4CS5JbKn4lbdNA/vLdn2onmBOqpYZ/PuG4vTU/dLfdxiRgPOCzz9dfCGfK9PxTHBJDnJE4188WidZ74WCbR5nyDJJf8nNE0SjVBTzMFGfHN0LAkyj5ZVL7hcZx27mzAYZkZ5jTTlO/MZUeIKjJCj8ucgswHoFXrbfY7LfmW51Ynywfak8PLVsycEoEK2gtlAHma2dslb8iddKQ596ieTlvjyc6C12N8w/FJMe1IMJW6J+nmG/tf6TJRh8owLU3VHS+J/U+l09le3dgN+KdR7lna+2LpC7fAlsHKem3PTQ3T/fKEQMNdd4vz9cKT5aSvNd1/8JgIslOclCtd93w9pikm9XKI+xNATzprox7HeSFDZJoTw2b0goN5dP0irs7/MztyKg9Nqh9c+by5UdrUkkaRSDTMb0ll8C+pbTqb26PvGDVSRFHsCr8MHvoXVZHreVBVJosvj9kZjTCKqC4+vktB8aQjz18dQNKl6SWHrwJAXhnHjpIvxnmSyrHHl6hjGw2GRlDLKYKz4+fhA1v3RObw0bdDI9KHgFTs9aZDv3oqZ1bYRXcPdNtNXNWN9EDwEdEZvEoIV9XrMJbaQasChvxcMe2m3265jqxLAQuIbEo3w82vp0yqnqZ/C54GeKw1X/L2elmTH7DRJti/7q3q72017g4A+zgclH4+Tfknr2dl4m5Vsf4Iz3iNxoWmzsBS4N/Uw6OflbfKvP390XJBR01x51H8maJMz/uWJK5v54aDkK3Epm682JMc9xjqOr0TbweeHWGGTZDM8r5VBbibAkujiiOYrQT5fbxl+Es+jA4VOIpEekONhHIeHYHGfNznDyBWsolypgFg6k7fXKgWV9zJ0OpqJtascEbfXdFynsVjxmfcadMoAJnyUfUIy9To7Pll1mToumTwOhqrnocv02VfbT3kDwVAIpMwbLXCbiU9pXsqfXfRPK0OPrhf+X3J8pVFqv9d7muaY/eaHQf8qdUSBoVAw4EtpzTx+qFTCasDJtxxcOlR4LO0MUe9y7umDpzBxfr62k8Y1R0fkrx7fPt6aPUUI3NEnN9VVzpLP/fsN3djqWwKJFMs6y9Zl7Gy2A7SYd+CzMS8FQIjLbvxmEilQvbSEUUoFS/5WWzUM4rZqt8iI1wdDuW1/ynXhS9vsWmlqYc8WsXbFIgjrTiYKAEIUGS05O1mVD/ioX7k8Dc//5dnrUTJfomunCtkDKVMlF/M1bdo2/f46Rzeg6kDBJHSF+QPNU9C6On2ZpirdBH8GXenCa5R3Rsll8Lf9VOEqOlQ4sRApSdA2txV3QxKDw5ae49iheHNIh6RmL2/TXwTGeGF/y6d3PFPFmnQaxj913QmHVPDS/JZBY/XibtmtVEZnC94Mu2mWpl253myLXtKbHMwJbYQrS2giQS4Om6h2LFjhp3G7tSgtbFNsT0cfak7YBcm1eTsRCb43tmkgcSoza1U/hI5GoXDWcCORVAOXEotOwOqDE7PPhegf5EMASo8IttDupu4Hj5X8bX5Xko3EGURUV00WZPc57LioN9kb77Eso5WoUXgbfaHnhFgP90xrmHEJ9Q6VLNfYxiRtmgOPZhvmcnRZ+VsNSSHuPl2Hyep/q/PomGGMsW5spPN9lmOYSgphe0q6645RgJh+rrCGYUwIRhsRye4qykC9+W0y2NS9K3jJY5X2M4nMS6DCRTe0aF/YP5TM316C0ii5cgklQtPB7qDpGv//zHDV/zsMiVSwDw646T6Jfto9WwXfuyZwpVXuocpsEpP867vB39oe7aGRFLUVqW36rJacEauqAESEHgRbSlkaAg0gl12jzHxIUhQJQzWw6jw1Hq1FJf4mfvyWbP3aiS1JFGmKwNCj73nZsSijP7Fs1FW/7jp0hs7sJqH6Sa1er+WyR0bu6IJlButIf2JapilLoiCMSzsEcu8KbjJP/PRggHfqTTduPeI4FkXSqWTjcjcJzeX99pc0Itls4Nhw7jNCCzK8OQUfPPXklTh1AV3tieeO/fFxfr7AKXqPLeA9rV3nag2SJADg3MKIdKBrawIYsq8+s//EPI2gGwfEovA6ilsHTkDxeHQrVgyRx3UCzzXdkTLP4TIRSze3ze1wLEU03AvhuUbc9QKNJggqHA4Ck/o6hqxv67rL1Bs/XNvg8BRUii6mx5LAC0rdVGUH2le3LQ4pyFDh47UXAl9gOAz3tPy/S+6pNCopmt7KqYNg/NgxvKxp6q9OAwvbkqzq9Up4q2BYnlcMx/c9YHYwNG4k6riQ15+1OnQ/3X89PBWrL8cFlchrjoWlpTVvCAQj9xK2wzumHbY6et73wadSDUZxsqapwOygzW5kFPY77LpSHQYFRonE+fl1s5TEGwECrYvz6eP93d3V6ajffkLJrfaUbDYw1FoU+kcswxDw9IDYkvrGQKU5lKoSnWAr5M9HkIFmojzrJIlXLO8aN7E9JIDEsb0CTktuL9PPfemWBB1oAk0at6bpbRRByuNZh9iS+qTrdcS7qDYfSf58BCmLg9AG2rhj35vrqIrIsS01u5J2G99yJJ4XM/XPMyzj6lWpLW8ZkztInnrB0xxMx60ynGDwKucdBmHiRZdo3Xz3H3Ia1wBQxHGkwN1Z53fmHoctCTr3DEmILsYvXJydng7TVuyIPrR5cBdJx4J+rG5ficMgsPUWtDnelCvjkFIjFLGKXa/LDig0muf4vLGmBCAQlSmdcI+MZXkhvH0zXeb9YrJAa7OTIUBZJuXaQYiDCvLyHezcq1+NQ4kETHCCKJNGoNBkIh6PNp6YsNCjTidxlew7T/m1bItD3vaE+sTwt8a9wrgWb4ClbDZfKFauPE4ieI3JwqQluPbv7rs/+llxhLLXa1XzmUcPQJMpN7q9AWTOTkzcm2koHIrCLPDCPKTc1WybwRJw7ZOZtdc/XFlkMgUcYMbYu2A403Gt77wAVTYBuPMXLdQhjL0KD+2NR9/hQNgdZsrLVciUltaKfOcTCRu9vMb4Qq5RyV126OP/+HP1KJEkrN1jRqARmX2vihhYtURH8cgvoY6ahyLXyzwD1LNXIGB01z/nhCiCIBWFS0WqHkjjMOdzktGN/Brh+8/s8vj9peha1z/9p9fnniaxLC+Iykp14mNoKKz1oVqJzxGkOMSP2J/e0G732qO2Wq4VmVGN/m8PI9M7aux/J8ZWWsdRIlMGweB7njMcIzDQrZjTCp9mftk1Xh5vGIcbK9N2IhQIJ0TAJk+aMRC438gAb7VNFlJmC8jPFabDwnwYmHxpP15nRxzY26cnNzpNG2VcVxWJI6A9GG6FUwgCzzEUp2mKJPDYhuCMSj+jh3oJlGAlBakkWVF1J+7lbeGIoXzyciNsS5dlLdkMTwI51dh3ZR+1bVMYanunBCo2xleKLuu64XLP6iqKOsx4O8ePRaiZ9munNQ1LbUNpkPZEDkWSZi6rlvH/qc3u4IKsyDKPVpWXaL0Z7xmGaWiqIut0G644MLRpYKLo57FGtocQwLW+ZNuWrskCywqqadKndRn40XE6am+zkmZ4nZy7UQXrUEMMRtXQ9WVj8+QZWZJV3XIcW+E4e07ECRxy19WwWZm6REDOfYujAiXsQSLHSjI3UR1kCoHVKiZ6nfUuzE/OBlOalitDBE8ZoWOops4wDMuyDIFgFN+ESll6UZIliaM30bqzLqAVaUXXTV0g2q7s243ptlo9xJ/I6nnZGdeSYtYXgEkMUxM21aQpVciPCl7q3sWlzirtzbO/0yF4cJpbJz7ZWov8NSDSUu+AneQop2egUxGuo3CtWsoR8ZUg8Evi1FHwZuiUGjOzPl+fE2CpagYP5WzXvWvTL/fJZoalCbTQyZelLGuKyLIcp4LVqUKyH7S47RL9JEBc96QitaTGyVfeLmPZn0NRcvNdf/aut0BX/vcV+vY5lRzkVkKx24+m67KPFA0EaYSX/DgOsU+aGNaMr4RJEhO3qUaDaitxmhpPTeiPurW8fjOX4Mi96N6K6psZ+SE80t91Dq5nRcGChv/3DH1t9tdeWyOzKFzRZ2Mf+22Bs+u5bC1EvGObKm8qMo+D7OWiXxRZYEREdf39PUjHwr5PSJn6driMmcAbyhkgztN/gOrFbbRw9yXbcXQKygOjh4k8TDjfQn5ixOshVALfR6vfWEffbufRLsbR3rkvD4K3yVtPqprVKZxbIAiT4sZLb9W3Xusk2T3hk8Tp7FrI73q78tcP9PrTcy76F29ibK6xP0n3LfWVx5FU56daUqsiroZ8X+fNVCInyuLNkmJMdFZPebIokgSSJsHLfq+f+C8oiiEbJJnNyGxGIhWqjnsU9rIIZ9NuW5Kf4Z9j/6z7z17HaDNlSSWcYFxGMjLIdI1OJwnQ6S1Nt76Ad9ww9hYlWVGJ+tjVAZ4cIOb+xX4KwIzoCtKwbWN6xU78XluGH4xJh7HI/IRQmxd3nERtB3NpMlU6lkQe/kvsn7VVjlB3H6+WqzWxi/t4gmJZPolclVeZeikyKPiFMTwdhrGeb/ab+2EkNOLwgPY3mZadHki+isEeSClLT+o8RHph7wpdryV+d9JNkHEQLFBObWwG3RIR6pEVG0LJoSVoMfwf2S/CJS/QjOT3h7FGNuGA3iYzEjXNpO9wOdKPr7w0mLaqQlRTl9Hc05hvXPvaoR3EzO7WUhIvVElFShwnFt/DEt88lKXBtKBjt25JQLRp3WdRRqspsxhzVoUzb6PwX7S4osMrhjnf/QnGslhKYRvaoOygF348cCgocqMFKz3DwWblcm655NF9Yyv0vBOx0Q8uhotNdKKGl2RiLdxEEAgCC0e7AcPumbLgit1wW9JpY2vtEUOSSNS3hlo9c7sn7hrOjIpraY6Wx7Is7XI3RmPPI4Uw9HVumwhNv2HruqpsfPoum0hOrxBmw5KLbHV3xJ51ARzVJQzsz5Z3l6HX09ntCtthDvai1skVtvB1izposMBRq4G8HeA4m6eNiJETKFuru7L57S/JudufvoGgJwLz5+AwbEwUxx3VPrio+VVMnyfjUx1kVXj7C6zGb7gpF1ztavyJdBHQ8p7h7vFj69/+srv98euPyenZRX4OwoHsWm+UHdIwSxbIeVQMju5gZ/JWda66rUuzmRZaUZIWjsC06hfGxBIpeFAPDUMwRMgJNKQ90YKaeHTUPmnWcTNj0DSlFsCBYq3MxECLCvCmmCvulTHAxiKhOJNnqa2pWASrul5LIv1rDWFEi93kQHBCOp2ohREy4akGBq06Lz9K+rzuYE28MEprw9kd/SVyZETdXfMM1uyPZJloxtbyHQFhIvJfmSgSN2/GLx8/JEwN5B2cXJpslD1BzsLSov9a9o3EmSjLfwhlktSt3Ds2MlazYQ5BPfccDiR4xAVBMIlxH7+Jp0GA5aYm2gxG/5/EGYsmlEupIpw5qz42w4Ngamx0Zv04l8rAogo8m0KM7KWPEtG0F04WsHmryfPOEFvop1bN/v1EK2nXW6oKWV8ihQMWa5raWAWYpT0tHJLSshXlb4663R/ZKQcb9Xt9mbGBEe662++tWj/MdpTvfxlzs1W+wCcXY4qccMEp+MGXsJJ5PBhn+FZAt0iJW7AhXekaqtUagJ7UBUTvdpMcR/reEYskZM5k4yWD3r2l/InsBejpdjxiRy863EHMfVjBaLXh3BoitK4lsOyDoGFr4B0cJtyPetFEp4qlBZrj5KgcKTkEJTjd81tdPVKhkTZarV49NJQBIvwvVlnd7WJ1Z9WgcgONH75VJnuEHl00PQHpLA4sGlfpTM57SM/+8KAVW+AHRzYa5AENgTUBAuBCx+I+U9whSH1d0XSZhUEK5knJDYPAB1aXRT06NKMUvftF93cUS0fTIHjN3vft5zHx/3vtwwZT0Z4IgfY0jntoVB4CoIdpPMcBbKFFjEiu75DYmluSIltnA7M1r+YOCjhj+T17xODWeWfvrLlcC6PToNgPV4AYLdKY1NWECghnKgz+1VUS46ennxujdNQXuvCQoFxaRVc6AsrszpLg8kRnQPODnzaDo5iBgUCZf/sQRtP5RTyYH6GhYCvyBSnY0oz9VaeANLcFhVdd0x95L+jY9GQ1osjqdntXLHripY9sCDy6liJwliUzI/4q92zbjxHDQ5oBpuMm1kMUyQ4RWWp0rYS+9ZEiVkVnhPp0xzai0eSQJWkROa4FGik3c55hOL+34b300cD1PpdQS3qDlL7vWCoHNaAiyFsdz2Nm9cejIjBchYP9hVDJmiKMw4X8iPeSDFRLhteNomlBzckP1Y0VLmlLry/a3MLD0ghxOlr3f/yefxkZ1A50DgVNf+RvMamzXVqNili3PREzBZU4fm7deAMB50jEeHsrA3/saiQETSnKt9S1tJjHpY0ZimzB/3nVoJFdBT2R6UP98aOAi4t0vySszl9ZU5efmg0AdwP7gVpgN3CQ2Vmfinls1U7Gnze+GJ+aUVz8fs5DHzAPtIGwYagpqOWQjf+Rr4+MDvZNfYfIaZiri3YOTy+eyDeHPS4rDMNmvUIqlUmDAWAL0Aw0Az1AuwkdvlIfwSwb9AN3LGyX96i4D4kjkaaoAiUyubrb3768BI7dT7SVsFVw1p/I2TgZv3GK0AGXL1dnf7785tnJFULfYIQCYNEuZS+3CQShh45LVeW4JVUOKlfjGYxNtNuDG8QCKkyBAbptXiQBkvANwd8San66hc43uCHOWm9eJ0i5+IMDGrp1WY6zMC4e6uYlwAirLOrfHLj9+Hp4b0c/V3GnMsFxJzJFxd4uXWw6oLlGj7o3cR9deHuH7/JyE27vIwjT4XNp2r5lQr/HR/ubMxe3SVJ6eP+cYbwY46kvUKoGZNk442+RuKcw1VW/A60qoy5lJvGbYRRCyeQXB3mIGuuEkhOCSab2PXAppLgNg9lfXvN9z+8Us12O5/5APsQgjICiu2hF0RVBRZbYHqk0haC733QiVI0wDheVqoq21AzKtKQWkASXYe4WCwzJSNl7rbsp+iZflePtYdGKXDhwMljgKVrb7wt+mqSVZGmEcYgCBnVS8JUwwiekOgsVs9lktmf3HtNRjuipBwNGlT0X09UL52tYTbns3lB/dDc1ccz7CPcy91bkJVCvCG2c8QypwNkzu9y7KOrcYa+/cMS4pdPb3Xzdm8ibuNyJHfnGc93kclwzhMOZ0LlNc6/xtorb3Xx5j146uikcPi3Qa15qnG8cC2wXehv2sUMvyjWEcx7nPc90CoXDqR6lDyNNk8Zj45lpzhLIYP36vrY99Aw7BV/731jgBIYWzrKioUCV/Hj/IBrMNLTL4aJces7Y24fhbsV1K//+ZTAClpMBvsgq5yg5pR+oeGnRNHwQzm9+hABSYxpArNyl17kXRdSHBBHc6JqMlLmeQeWrfwSy/qIGWXQiy+vc2yHnz+xrd54YCxf/LWYK8RHxqokx1bp8uE0eIOK5uVrXyY8u4TMj+Vf0d1GHi5s7Qt3xWtsK3C13S10uriobrgRii8MsELfu0l/A+fE2KX/kpaOKXPPp8X/Kqut6ZzoXZBqvjRcB/lR7Yqc2338XRUmKkb5952B6ukMSa9BM4xkSRfkek4y4izBuvqoyBcr71LiNw0Fnz7/SddEbEGRextDXwa4SZm1D3ce8gzXW5Uvq5v89gPZeXqLvf/ouSe7cAeYwEsRYAcSDdO8JYYIw8lI+mS150+SR2pTydGaBgowBsXdGtnGrDtkcfgLvSKJQYsxoBPz1U2psDC+IIl/AK4sqH+bMfE57qpSp5nEk/fOCYru9o2B0hwJN94p6Bmw+Ol5bboHzs3R+wc90GS0dzsmPV2PnAMDD1sQemF484Qv7vScEgHrxnfHCU+og7vH4guGNHyiDJRLNh5KVxS3v/P9QMZ+RCELK58vq4gY1PKIjARXHQWyfRHknXuodiaJUy4lEEz6+sME2UnVTlOTOVBaCyZrAUWThLBsRLdS5u1bqbn075ZkbyQI1a7RyyIJ1pr7WhA5Sarh0hzZOJ4rZEmusPHf38PQ00csp2Dl+vI7upULA9JyD+acc7jUnk1E/PXo3GYZcwE9axFOUxgPiEdEZRvcES5nIrVbK1lBqVRIYl39O6IRq27U3Xxvn63yjX1pajoUbkSDWC5+/dOPklWGJ/OK8M7RSGetL0YpSpvLvIChymBqs5nWPPGfbusLSnCRS1eMjUVK0rLmhdjpRO2Tj2c4CJ2mOw3fkB64iy8D5O5gWnhW2jtPnmi1lTYGDA0r3qNVkF/bAtel3IoEp0SrK2xi+LgOHqUPsnnoE1RkniIZXH0M+xi2Qj6zUxvqJlW/zTF09vi1Ld0WuQVvdv/57IvM0PJGGOr116zNLqy5/KJ67mwSv/2/PixfWje5gcn55riwceGztaBNZBF7d7uvFAVdUNxzK3ak5OGSPpA+q69OXwPENEMz+zMUpb7hyDIYYx9/M7UA2dk7FdMsoRoXBE4iJs++mVrEj09Db/p8fZvRgebZu+CfxlSCwNIkdROB4k/TQ4AKePcxqimzsRCuMpqmhDBIWWRX2sIdbUTBzFHYGYv/bBqPK3YvFxeaqCSlaTk9FOJoMI1m3lzv7zNpIz2VJDEWgHTWEznF/IAi1zZwBUzzjDp7ZvyvQfJppfesbTQwvm5U6Ltj96heeTYdpK0Rn7fbdZ5tmcGof9HtWlxyL9K2zhJnNN5FFlJhdZgnuyvVg5losZ6o7Sie+MTiOzsjDKUOeOctxMgdt8bPcZSl8M7P78kMggpdvWGZoUP0DQs8PK2qUoAhDYZjejk8IrmiiKQ6qX1wUF5NkIXBw+JOnz/OnJ0ozMQnGo2eqSDuBeyO+mJRKAfYLp3lZB2wLU7TxQ++ksksZHjRVo00nDE0z2g0jx/AHrqt1zvMcX7mVsVrL3hGZdasHKappqCxOCFjtkyfXaFmUFVXnKSnpqk3KiTlqWvPMdZVzLAoIbgx6G2+muSY8WfY2serIgN32nxEYKlCuE7fXvoLDCxiNI2hDmNYnjIxsjTg0hgPzu4sQLe8olG6p5yxAn5glsM/wQZkXV9K0lzYtiYRfweMT3Lo18q7H43Z7fFFUMIFTCrGhOdU+uWs0wgGt1PHnLfLY2JjhUKUMohRLKB3Xl24a+pb0ZD0/DExxR3j1nXhejPtFHsgU1bYioCua5QWyRqpOe/Ed/5YrAvtukt8e7leO4VBhlTbC8OHyInTRyAua2gHfDxUOi4yZU4MxyzE00bA/BInZrpNy1SXbHtowBk6QyIYuNXFpfVhtBEIKXGtcjNa6C4mCJ7ONdwRhgCShXspmRth3Mzjq0CAK+pfNVCrmF8seRPN0f8FqOJHEsEN6qfItz9/LeOA4V/Xo5D2KZliOqqStkvT8NexFaV0solebkeRcxhGJ3m/Dvgzo0IBaoMe66wOtcziZf/p5y5bB7Sc1Tdd/a+1qAgZtA//HrGlVMV++HcetnXtxCrx8ayV/2HThy0Sp8uFztmMjAczrm0y8oLd4BxjQxMWjAANbv/u60+kY5c0ZfTrfkeNX+lcYIPDwVnI+HltzW5ZIk6xxfv5ehUOEHHx5sM/xgoitMIGtZTW6TRlFen+6zjEUpW673S2V51f+HxtFTca3KbwP/7cZyK6DjMu8YCe90ZIPVboO+hYSTdiWdmi4fNQmxT2+WNVFhGCxYz6yxQwtgSepWkqjUdNgRbAKVp+mxHx97aHBlbt2Xb/7YODyxyXp4e6e7u6+ulQjbAQ5QMFWGrGqqPqErbIEDnigW9dveriSiI7oko35/qJE0rSrSngGRviZpXjmYLK2MyHf2593MJgxsiU6BV+qhloknds6VeULOfn/QT/wjG+XLrRm+PMGIoBUBnMalU6+ihY61YT58GzZa5IpMAK4l47bA7cnUVt3PeG1pYD7prpINUz8UC8Gdk/PaLeTwBnAknkLZ5G39AMu+emfRQBdgvETHJcyo56xttXkh9gizbZMQyNe+9LU1AiZxje5FgGGorGpTBpaOG1WTyM5/8HqjkCP1Ymmh+OrabMpjdEaXbIIcmKusz8eOhuCxFIAwstwqGzK5Gz4SdxXAJmcirZHFL5q+t3VwFbboMQ0onhtAIqtTgmoPG5KeJrtB4Ln6tLdfJ11Ykpofa/1zsciM0ugoXZu6vn9Tz7Bm2WTTjIWLJ+YQGyKqgcUkqDyPJUo1lJsjku6n2Ik7E6hCRQspHcyGHMrbX6hF1cIpnG6f4eyUpr4clwiYJPtSa3GnKqaUtLpW07qseiPj3WdPyWxa70edowuvNi/MjM9YHjLOneMGMHYF0+1H+PAaTqMxmuNKBG9rJMBDx7UJDzN9gPBc4eSu3oWrRm0PN66b67kaTqWSn4s1FP9H9/ZqMtm4cWo1cmJVOq1g1LrJYL3xAYZtD/Ieh+brDbZhkamhHFZB6OYDSc2w8ed9nUz52JZ/Cnn+ZxZLuEXakmP+gACWdhw0HnuY2scXhm6pjMRfAOrq7sMMHqAY+WRQjIMr4d5adTE/x1JZFeQtVB955lyGbtkub/iYTY2btC1gSCP6EmMtmx0i8ubDwYJYMtoXnJ43hHO3oev9BGa4bYE/J77Ipeom4dHtUMxBUJbuC64L38DCXKZZuSlpDI+a/P6/eznL0vuXYjp83uSrru9tZYZbdI/lqTNcTTVqo+xE6klirkzyfhxv7g8hBl9xkI7exTnK5MnGSibFR4OELpbRNen3e6cuu2PGoRSdLkpXVGryINSo2FgZ0wGoOyquokWOYxMXpo4cCW6kFIlSTCwyHKVJ3dDnpA7o8OzBP3O2H4Z72kgTKNEkAX07PW3k8loKmn0397i2HzFOQnMnqqKOXuIENGgQhL2QBSS+KFqIvVG7vuLH+CDZb00+ybizYyc2e6wVJf1ljwFp8/+EhwIbe8yx1OO/A2Hj2UuJdLS1uY7wz/obNXYgal+RVc/w0LegqZHL0l1uDo3NyZMDa3nP7z08fVLnXOqaVi64HOIaT8LfxfmbS1AMElivZCT7CFeTDKpun5efJ85wrrp6/YLe6zAx+oxOdi1eg2sNrQ2zNWPFkTFH2yPPeNYVYVmHZh5XjlVpBmc1UPYukiWtzEIovUaLoXVlIZR1NrtqLTN9uz6j3z56dVlP6zTVf0f34ATSXHDnqeth4misLDSXyGdKXyA1UpGgIGZs4oxK5SJEzRnhepaxD5GGuy0jlFu5pPFW8RnmNVKih70r9wYpsLt1SuuD4WwiW1OELvSAFEESWSorWaC2EEoHVJr6dzQdAf44CijtrpvRbKlyCs3WaPLVfd0BYl+n8wpxhHigNEqGAN88Mdp4ZW+8ahIu3Tb7as0RVJtliN2Lt4d8tAAFe4QzqTdxTrJfS/Mu579OTgw2gh0s12m4uk93/6nGozmvjpaqn4TDEhH1HJDPrRm184F2XiiP5R5miahYr702jrfHOqlytAysmzmnumGjsBv+A95pWrVZJsXDneYJoYh+V1eEnm+PMy9RmZ5eAc2VZbvylSOaxpFOwAOKbIYuFxDE20MJ4ozl+7KeHRgA+dtF39MI4gWzA5wYdudLvFIxA8Zh0AoKUmJzli+9CRFG7414FQd8Bpx9IAKnAtxD3qPb5TEzaGhbLpKqnXrPJ9t0VJuOKdbTvwgdA2Dq0tAEVjwIRClxn7O74f8YabwbYCmA8fWqtbgwFaKTh/B/3oGZrQ1SsbRlEqsY0/Am+4B40won1BdaAbVJwSnao+HdLNHk6Uty9d0WWAI13Sn8aZ3AqHdgSUqOLfiSVaM28Wg0zFA4OOKK+kWgF9Fvhk6cKC0iZaMcKzrM2XrAAdfTRQjDrTTrQxgSHyapZg4coPFQK5ojIs5q0eugJD4OP+2C5lMy+sRJiRKWU1FoTgVtVfRtx0iJGOdIU9IK0vk9C6n11NMNpGImEl5zleF4E1/kYhRtQ9IstDsDuXmN/Q2JU3bISbU5SK/LO9xvXaMLP5oJE5zyQxAqAFo8UMlBW9MYIa+DjqIBGAXkXAUZSc27L6Hf5SbXsIfgFSWrUrisEHHexRf6VCfuQuF8zQhZhhrhhaLEH3H4DOJkhZEbURQHZjrJBYH7kFvgAGv/TC5j0MgeaIZfl9X8CK1AoOajJamrqWiCa+1dnA5uRrBEsdaEGJZkXJHk/nLNgZSxDZdogB34V4BDFymjtXpO5U5Ba6sLsDd3KTTqxmSaNM3k2a79rHTytCiCzpT2/XU0NntaAKH4thAgF4JYWw1BWXQlk6YCAqUkVR9v5rCtK4x1vqxDFZz9Ogmsb+8NYxtMm7vFSRFkfgGPyRMNBHIxqHd/r1apVSjFFUd06xKEkVBHM5FIzY7U1/5/SxobE7wRisO9P4gfcxGP2AgI9oN+wzPekRDYKmb3hsESjUFMJXT7XXuaGh4Pf4AtkpQwkxVmGnQ77U2pGuilOOjRYs71WIFIxDUIoQxaJBkCn6WZN6X13Cxt0XhP/CfMtMajS4SlIREsUqmwF2uUZY/AkWaXqCbGWsLOexOFMWI3zGH8zAlz0WbhE/ZZi/Kmx+TdCyopckuQ0/kqmU3dZbIOBG5uZmOAqxkbV3JKR6mhLWNKZcGvGxOAl40Cqk2b+p6nAiWerMhL8DzDk3x9JMsmgMcMP+/gB1wcoAw1mwvzAZ6hw9D3Siy3RwfthdmA9Xx74pw5Fco78cw3pNsxDVbUm0X9jiaco2z5j6RBpAYW2yRP4paDhSXkiVzpk/R1TWdibws2EAkLOB+x0rEdTAdwmgTzrLWbYvlkDcjwd+f0b8betpbIFWmlYMvObwzuqisUrt3EBsh2419vFXM3nmYgJoAwZ3xJeyMZ+BYtLHr19KDPS5HK7g7cTjhmL0HEe9FtP2LHoQRN1xq26LwmYdF0niMqhkWTJUYpnH/Jb5yfju9VqZM1w2yGlEStXaVsCf41a6ffbd6KUhRFy5v418jqf/66cvRKvm1d2KB/onrzf7Sc4e69uaIgw8vw8j1TKKpPwkq8c+8M/73GwAwJpghlOujlRiuWAaR3c7e+hET8S+n3Dao5EnrjGMfB+YDfog7h/kqnZtVzsCPi9zX6fZ9IZQLxtXi14MJxcj+LHhQNGED7XuT+diBvglxKZwzejbLXHeImguSk+h/f0aPHB319j2pd7d3NTb9o4PSt1iqu31C5XJH+sa3qtgrh6A9JhGJ+NpxJvlmLjbA9/ApiHcVKR1fOruZSasKgWdZrgPVHVOys5b8LrrY8S4A1jt1PE/LKVUduX1PqkuZuxq97PngvfPHq4uLs/8cJGJxby23UqINz8ZS8t6OKnpF7ED0/BuxDu/Jez/Sk163RK2MPzbXqCiKpnGwmMtmM9bRQLUaDxCoESbWEUoKwcX64U0ROHZAHNfBarmU5nEUUZsfla2eO1YGUjgByaUghBLsti8Y37TO+ts4iqG1ye6/yEdd8pFBNzFIqdlKCKkEJNDmgxQR6N2FpZXve56j0vDseFhYj+ADVZGQSkaEmOASJHiZg/rXP4Dh4zd/szGfTyfjwaDr6Tr+f7VavQ63SVxwJE8MT942mVFXFBoqAwOYNy64lEjsKAeJOecubljoTRZPp8wQ1qFpvHnGzZJZ1o1CR+Xok9I7JEESW+fjCo7EiEsH9t+GktmzDJFEAMarFoKzPXcrE3uzkrO63Oxl1Fa47iAQbEbATNEbtEBJxRmNNw0pbM/0NRh50RDqUrulyHUc8u5KV1XlzAqSDiVfZW8013TDNHxdbT8q9QYtsu49ScNhmplknsfdnoo8f9zVNuvFaIbQtH0hZJiXA33ewt5kqK5wlvMZrMbhS6d+A6GXvMZzzX2zYvk2evvp+6v0+J1jpiqqwf0o57H20onlsNF4VC6Pr4xG1mMUpeC+mSNSgUHgP+I8LEKtxU4GF+OUDPP/JFAEQTGMQrQ97bu12ngv7tDkvR7aOLZJ7OCiJPiWarrhgJ/6tAhwLlC6bYAz/9mIfeIIwaPdszHPluMnXf+MJsn7PVR4ruV69rTpxJ5q+fFwe9govzqof8bFKgEEaG4BvdUHV+VLqk+lW6B+3Jaybg8yA+K2zyUqU82onmU+MfnH7m9IVrBsxyspc+NaAs+xNAmYnlQu6YFS94D1teaNresJYjASPSN8yBIWTHVmoGK2ad7AwFIwgRjRZq7+u6JqFW5SJgaaqlwBAI072rPPG5WNb6eFTB09k0Jdpk61zmPULiA7amMqExWtbq02mrkMgwiGM7JpWsGZaywWq/XGi/mefd7ofYFE8yaNYlsiQ5Vbfnad6+ds27LUkm0aqt7qZjTnDFAlVDrTgK0LaaE0rXiJWxo6r5HAVmLPnbGD3b1XyheKAJMyg1QWhwMKPk+9H3wQIJnUtidC9FiWBK78qK6utrZj74Ryk9iA1LG0lWQQFOfE5sfT2diD6iOBofHiXmdnR8fS5EpJp4H3kEJ7tPGskpZgtxMztbC9qzjFSIMx9FqQuqksWJqW7XeJMGEo88RwZ4OmiAI3WuObNCRpzaIQYk6iFQzfmqjuhikdJqaM12S9lNkmAmNUDPtA+zUdL3pJop5eU9cak3TVep2f9gqC5NTUOknhX02M6vq3UAAAo4pCjcnzoo1RCWwyG+7B8w+4dxIjGa1okZtcVhNzz2HQAi8S6yznTRDDbnHpkTQF+Wxye+BeV8l8iVoP3uZcZqYXCCWtqR66gU70/WeaRWf3yEQyKR9GQ7kzUQ3XEZAh2R7Z4omVYlO2nPiKEZ7R8uPQhwJf0OrtPPGxCLqHzIwBXrI/TTTv1VfErIaJ2/P2K3dCA3xmdjZKoxcSTkXkh3bVC+fOUJsDzz42UJcCNW9GZL9AQKIC7mycjZufCO5htf0oFHocGqNo+/fNpBTarmvyuAJmUtUp7Pic4RrJa1p4Ob90gzX5/58HtZht46mCi02cN7Qh2hhDFF6Ad90IP8PAjJzLddpkeGncOPsYmeowu8WOywdZo1QnxLJXzhxd3AymUkYsox5vmGJGypf9pVCStPna2d1aou4jXVjnwth1pEXZmrz9AGgN3RDaCjFIN3C1LORe9q9l/szkn3FjIF2b39DOBl9bw8J+XySFK+U6yxf3Ga911V3gICRSpKpARu2KKr/hBUGdbUyzitL+z09oTN1JCoPPv1SHhHOGfAH2NhESM1Kh9ke6lsjGQazp88kNCWww66TND6YS98HdztiDyLIFiXJ03K1/iB5wbrpdVASV00faFb/rRcAKuPv1Fs40UiFul4TXpemmMZBiC5rUZpeLzlZ4sl7xnH4u2Pke97r+7hy2pV5zJTjs6hmn+gknEK5cLEtIFJBV+do3KNel6abZm2dCdVdgZZcTz9ZHUwhAqM/+pTjKWb2kuq6pRC8ls0sH55xYL248Wjjzq81M5Kzvg4b4WQwV0jNxLU0iwZ6Rsvo9Xl2o5/PnAxkKQjwsqBl/v4I8VKNsciCqdZQw9IkhAtSlFUxsN1X5HFOuOfTnF9yr4w4kkNB5DhxtxT2zC6veUJzWC3x1GV1clAZXOup1ApHAkcRUtC1bP4Gyb/kWpY2ympEk9O850B/9VZKKXAQv0Lx8P1a/Ons+GT8xgJBRfgy694komI57noyDMWsOYULo1StlZ2GxWnBt9a5GnJhq5RBcpU52YG10cSuyVONAX6gdCKkzbM0/DRZXGqzkjbaAE7F2arGnm1VjqOcdieiSJ+t5MzppPrvclUY3EYnKjFu5c16nb4xLKgWsBaA2NLosi9poZ/KZt0li7sDdf35tAjNyyZt3XyneTbp4mdmqTcHUovEnMm6CJv+3a8FKxjV1bXfkPL94wcvqCzqvj47I/sXeeuFQvJhdn/WSBEOk146RDq2g1QN1Q6m+JoFPQgh6N95pgO027DlAkCgi9O1pSM9EAy4cWNZmi6yeE0tltw+rFMXLBmbTWa+lkXfhVlszy5HQCYMvZNHkizAA55mo7OS6yhu/pXrxIhpcYqP9Favt2kkseYrMk8SOfTeCrtpEW7kwx9ZusRBntm3bst3EqjSyo24Gyt6iC42grYdOhPiA/j9l9HmCAtQba60b4uPSphDyx3CSM1tEfHVoNfeN9lNH6SHu3BSIpkdB9yNfg3I7aCzWf64kYSnAEIdlpAy2XjK/uLCKKGDNtcqOc8PCRRZeORtpZrlMtVxq22yMSeRwZNjIBJBYC+wz11dXt8/e+gmq7uiHNeVx2D5ctUA++1Pk/C5H/QvRJPQXkANv0DXr69hSFdsOQzGEofknMbuVuvRVjMae4bI9GKzhyU4Z10NY+E4r3vDj2EYxJOW21t3+Hq8UMRo5dZ/Ba7isrbZObYu7fBsELeH6+NK+i9XJjx5oWGhznsyXDmqTPVPgGAKXTbh9Cu3F3O6hw870g+Da+FhObAF/TmHSE3CJ5aiW3qpeSiYsAIUKTi0ioVI5xd9aiDdKrKI/LnMMq46anbb3pxg51wllJ+dNu0aoE/ETNsOBvkJm306Ffiuf1wWa7Ir4lauri+GoI+OVsdiyeYwDyVYrC4dDYQtq5x11OM19lKmKvSA5ZVMOF8R8hUlMtVJmP/1dsUJ6SqwglyNd84n+WlCtquoP6BG3x7vtNnQufw5YqD4xBaiSUYv1Avb9eWH76yP/edlXy3fTZ4aPr/DdWL2+I8H8fz+FYtnuQu2EC3htMgmsTYwc2VSFp8JZQVXI2AL2yO3PjonC3hRZgoctNOKLnRV0n0iWrewr3y4CBoAiEdG8/Y4vfaSBqqJLHuhiW25Z802kjlgeA1+N50Ore9AOnVpbmubYrgo4N7fP4pLzc4Nv4Fw/QvXvztnA20mDE3ThXaWQ0RwJcdLpzLahcRSBIVvXZv8E5g60xS+lFPGBr1uVUIO34wi/5mUnuadQhhMS4oMy9mBFS4peCG5GQd4hWnxjKIsT5817f3F/Nh7PloS/SFl6fet8w8eWdw5s+kLBnYPGYFPd0KNL2bIwegGHHlyTynK2HBW9yAuiThCWwl7L7+LJu8e84Z+PxrP1ScEvRargBasSgXC5Uvi1LvHcUYU7XEYnbuhPfN5D28LlIga0t/QUDmruFPTGI3xXnT4Sudq5an4vFeuCSi4T0Imch0ub6tJetziyQmOfNfyyvYqv1ciKDhUvRiw0tdxzPBqRtwMSIGBmLejzYwad8F8XHEk/i54UcDCbF0fN/cy2c19i88WeljQevzVgjR3dXv3Tx4bdpAdFZ9Zs9uXAJGW+7ci19tEcT4/wUrYI9PJiP25gLLE0TcADwwNDqUUdr78nqIalfXlM1ag4lJhrvt1m8tin3KwFQ0cl7ZY0Wi1bZjMaoaIpN1J6/soBeoWyuaOqmyoVk9HQzmVjc33+8qB/xQC+zbVtsnOyqvxt7HxalIrCIi3nH+tmVz70HCRKFTo0ZGudoniJZ21JqhVdMWzoEguHkgMYIqNJ2+cWXT7U4wAbo6hgFW1mTn1+1e1R2hcUgchgPlLDdHIgtW2ybACwRU6ilagCjX05/8bOd7/iQx9IG+fVd8CkP46TOMXSlC1xI7/8jmUzeDI3BZ7Bfy8KpOpIwGgt+94g0K6JXF0JR1zNXJ5jQzTL8KKsylw1a05faDadT4PSoH0fkqEedCNXE8tYaEB6+nuWwE25fVg/oN1dKHoRaNjbdRSWz1Re27Nt1BHEuOp5oecaMlk03TThc1lbL5o7kW/3TbCa9y8Gfqdj1RigZZbp27GoENYuRMNIwpmDETlJRLhv6OMpdODPaDPxKYOxk6mXUXV0H/uvV21jvK7feGaXYikSKZOl5yvcg2raSlILpLQocQyc4tCGV+8Z4DmKqJxPsvl6h1Dw0RSO3oyBZjkSSp5uYvXndXdoFImdSpnQS5L4nOTyVzvBF3mx6xEtqYV+DeDcQlIfYpStJWPA12aZz9FYwFgr8WSOPmHrGRJ+6isex/GCJDFT7RAHvIuBd8a4mBOdq1sQOT7Q+mM+thxH4hyH/EaPhlor4399+Mroznd5QdYY+D3p0UjZRV7ZTJVmNpPSL/u+qwRLeC44aqHX///Xo4ywWi0r331J1VQJ/xKah3RFbBSsnMeC7ebC7MqcpYfcicdF9gFVbGbyGV2S5P9RJUccMMRp42Udx4wX4fb4Wl/H7hvtHYZC6aGbiQJDMnjDroHV2OVrw3fke3DErrvD/vnNzf0zzMz625gZxF2/TLo13cb3Co2uwvADM/gCKB8qILzmnOuNh4PhMJBZxG03gtvuRz4KOEZr9dJhztwwxm7XrEzXHJlil9fxE8/99pu2D063vWzWwRqCE49rIFrH8+33MFy7f7YzGPUiNyh1DCx9OQq78yPdNbYh/qAWBsae3jvKmveatfD/vN7qmZERcfWN857ZkyujrgiCdxRMvriaOcv/m9vEj5G5xSrOELfgf/pyMuj3ev3YVGWZblLTMzUbBXurnKmU0pJSE9qdwtFV3Rwdf9wxHflU1779DfPPDtvXRJrE6vnDEzb4h5K9bJgOXHcYlSUEO+zkw/k6E3DsqDuXJzxggrmW5o/rxVAKWYauE7iL/E9vvcoHWfO4eHjpz/K4M5AaHhTDq2kA5PBglzvPbEh+/FsyfpAUf8nVVOvuFwBvsBJD8/L8xg/wXN9rA8EV+hmf87gqJDClq0WU5kRZMGLqSldJmmJcVugbYNHjgA8yEBMzGXz6M+z9aSi/b1CMeL2CoFoaNwD0yxslOSw0qI/Icb4XBH5ktSlQh0YHNrZooOXsRz8Pvqtx/C61T9C+CKnmjJwqm2zmSdeplXX+E1+upPkdAbf7app7h8MHEixzukc3ZPG8KNopxb7TbS0nWxSzmswPKk0ocNgYU62DiX761ft7fFBI9O6LpaJJm5rDo0JURhG5g+30O2zA6Rcva1ons+E1keZ87AlvScPBQCo/9AQ4bJs8Vs7ljqwcf4LEncHL504bL/uETJvLttpwTePmB/F448ctLjgNooGPMKx9+HxXEeGs7v9ZW7KoTB3c+fW8DOdWWnajP6vmBwucrUuh6k21csw9P4NvN0paf4xZMPV9dny+xVcsjiOw2QOPRAKGE1H/8n1z9bMYKcoMfpRQXXNtQ6vcV73Q5RudWvPXLG7vcm0d8I7CyPQr3UWEt7s9LhTs/LAmv3OQpuGQx45S+ctu7lEGMHlmwBG88E31Qr/bwkJ5B9miN4rr4ZmvE4pA2fWB8p4iGcoxiiRwBKJl2KFMMeZNrlCYLUta7c4NHWPrXPSz7kK3/apEXC3L8V7FkafiIYtzKo8t34/j6L6RVWWxvSjDQnvp62saSTNIKpSp8MF8YJBTqRXn4/bKDY9u/t27TOKZGiDTlPNl9LxIzn95Af8Q6V97WxzRKgNKAcoXcxDpDFmJ0PPZ0lZdO2Av0S8OQfcn5WwUEJkU+hRRBQGxrXvTfmwqz1PnYK6GlRn0OD/dmLETQQCPLXhZpLH5bo5KS5ZirKGntUkeDLrie8ynyVgot2cMfJLaP/a+kqsXzu7gEvd+dMi6v9mcjYLDVFKdPElI5ru3svfcsmgUGxDiBtOMXa76YwB11Ngwn+k6KIBAOcyIxYNsLbz0ggWUEsT1fbVuYVbU6/qG+POEMTBdads42lhj2xaznTg6cnWxwTikYrDWymgyh1AMbkxj6fmm7bIFgUPlvGb0zutOBPfc2wAukagKOeQnmnlfZHf+4CR5MGrqgMdXJYphWJFBq1b9ILuhCHjh9dLT0Pu7D/ZTZsH02Bpz28Ch5r5PnW7mF7VEvkyr4+q6pm1Ic8QwuAEmkBZPVDgPrQaADaSmn1Iwnn2u8XVNpd7pG7l3rotQhNkOXeGjzsEgkvyQIBMvTgHvEif8BhAazlCf/DAGTPXRIsedHJEKzD1MCzlCznBX3UwR2f1iw5t3Uu6QFdLZo2DYE4LzvzTD0ReGKpJgxtxq8VEZZGXsTxn9HHIx2+eIff9jNhVzJ5MRVC3DKN4g/AnaaOD4b+D5kiu0bzJQuoxieR7kNIO177z4KXMWmbH7pA3d88NoHe+jt2mkvANHQvpvn1yhID+SzFn0SdXFySuxB+GvfLESBr2I2aPTcSKIWV6Zn55Kpb0rcmQcHty0toicxMCbDMKV3XT9ealTJl5aWb5DG1KNQgLo282CcAhivJ4qwQCfdxlOrbaC2FN0BdeGVMk+cDWvkfnb/jb2pGM5foo7X1P/hYBrxRRgf37RUwootu2sV+oAIhVDDUScvSEB0isHvL9NQAAOvPUzvrNaz1Gc/6cg6DQAvvjzLTqS1l2A6pMlYeroHQA1yJQGs38HMeSpDZ4JZ0aoGrcVN/6dxgQLX6DMseTDiwsxIx+JYdZP7eWueDABv/Ln56PynSBtoSudK5Io2coNroMO4lL9oK+0tth65VwtFE7HdLRg2WHGZI3TP2lwIWu9HBaik/tD3mLDL6PZ+Y4fn7mvr5L6tZCVvyHC5gR8e9j8XYXWrjuPEKCEy173tXFQkrg+j3C+Y0MyS3WcjNoPLhYVzMHWxJvxzrJHyCwkyopEkYziB4/mnM6OCBgRy+xkASUe9o7Zs0AWwkZZLrvO6vVknZ3RE6thUdrreLSj1X7K1nb8U4/LYWvPWTO4lKoymKOyRgtLtkqvZcAxGucdFmCPuoRNEeYszW/GcpVVnQ+PJOJhv+QjYXV0I+JgpLMryItIhhrbTjHSrCfw4mTtilp/V+WEGyzsrihVeUdvWLWfs+YlgPl/oYCl0fEJvVwtWIteF1G9nQiOZD/S/C96m6UgsNQGsBwWyBKZDRMxc6YXrB53RNo6bFfYTlUqdoE9ZL1Mh9kzJtvNQLQJsCoDluxnH/M6MYPZRClLgAFQ0tl00no/WC5rDLfvhfCx45Qv+7IbIHt7WVT22QMRu60MFWjYQ+YmtOLOW5rfORqOYZbgYR5h3MWRrs5SWbFeGYnxc9mpx5Bz/+0KAG5KDxIH4Kv7sUrJao7n1cUumQoANwL4bSAOHoTAYABKhDEYXrGDo5eiIeGaowuqA80MYwA132ZRv2JCUEWDwqAIwbjYQhGUkyjYlWgT1lUkHtO6JgGzkia9MhnYDEPb4I4g27EYW1JUsZ3YjW6VGA+5KgQTetlbL/uwmNjmMJR8xlHUcoNj0PKdd/zhPf17otdWJ2FOPHYKSn5BjObEmNPQ8qwzsCYU/1kYFQO4K8zB8bMX2iZNilS5uGBmFm5SmZIdKK+00GDRpjKdbFspRURInRBUaEdZL0SctubXdSGzQzGOal1CdUxDPKyce8ej4ev0s4yFD+Qq/R8IJ5CgX4d4Ihp4ByDIBFI6X/2EXfuKCspQd1zVUx+6mBiT3knKkf6BKJe2HapmSMkTWNY9sBUpS1XkU1qlWFO3pMyOgu7VKxmmckjhNHArcx/v0N7XVPiSPOLvT9xXinnH9q6WEpPi7B5SKhcDe8djOlM7NxFKV66TySEjPRPVR/tFclnrTkHEvFU75WWzW3kFv3OVj93Z2IX8wqenfvCJwKm466ii61W8Y5X2YZg/avtloQtgwHdTqPTXEYFgwNRTnnrilngJllJaJZ/ahXlerz7tRNSCrKLNUpaM6oYUeW/7jgGDbktRUqRwOvf96hGpVur3iy7x/eRs9c4hn4JDErjDQ7o0GbJkyubJS451vEVteYgpH41FlP+Hc1GefBlEUq1FigLfU5FCO5UoFiqIqG2kMqXKVSgvUnxU0832uOC9i079LRhvz02hQhqkg7PORU3QF6nh69RSMazRC+rIgYEp4ahQWJTp8XqA33wnk+T1jhq5HsOH6Xm/reHVWMNDJBYJCjRwWvyu2T3derRpx/DqaKBgQOwJGL8wtoemckfEmGYC/484v95lVyy1opDPjrhvFpeEzD5lXf9twFKtxiPHY4NXnAPcjXrplYPeGLPPXvtVie3ydYLEuj/qEf6wXL5QLJUr1Vq90Wy1O91efwAZluOHgogkWVEx0XTDtKjtuJ4fhFGcDM/jAsXouG37hOjpk+ZCn77Yh+MZjlijSBpXSKvugkI0Y/8DNMMBJBBFh3vKMwJRhnUbgDOc8poMBMlCVNhRGQ4gQbIQZS3XWQXegF/DMIhDtRkFVB3wwvPB8yEpzqFqZw+GT2aW3TlBGEX+un2LDYQGkuY24PbA5PkQKqx79+Zxfzv9XEGzAr+Cb2HT8wcC5B8Sl6eIC4QSUtLN+yTQdPVAViFLrZGesxFvpc8lGNCew0IK150hOCv5t7bdacuJVbOSyEqc7gwrWZm5i2y+6n4TuSkZCplOhZ6QBQcwwuvwKHFnO+wCHE+ZkUy3QO8qZCCtYE22LJWMtcD3y5BDvIQBc3t5HkpQKzPXp5nYCFcO9Tk/sPLNfI5W/ML0MCMiqHcBta+oDWqJRuQRdQR/hldgfVqPQXsXaP9K29CWdMQeceZdzHsdm1e009LDn196KHmYvxDfnplUjgpkuTvzimWJ5KSlODtNMG/6/CvyakpYd35Fa7wDAAAA") format('woff2-variations'); unicode-range: U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD; -}@import url("https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.2.0/css/all.min.css"); p {margin: 0;}per chunkper chunkhamming distunpack to floatoptionalDocumentSplit into ChunksChunksText IndicesEmbedding inferenceBinary VectorsQueryBinary EmbeddingFloat EmbeddingMatch-phaseMatchfeaturesRankfeaturesMatchfeatures2nd-phase rankingTop k(GBDT Model)1st-phase ranking All matched(Linear Model)Global-phaseTop kChunk selectionTop n chunksLLM / Answer Generation \ No newline at end of file +}@import url("https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.2.0/css/all.min.css"); p {margin: 0;}per chunkper chunkhamming distunpack to floatoptionalDocumentSplit into ChunksChunksText IndicesEmbedding inferenceBinary VectorsQueryBinary EmbeddingFloat EmbeddingMatch-phaseMatchfeaturesRankfeaturesMatchfeatures2nd-phase rankingTop k(GBDT Model)1st-phase ranking All matched(Linear Model)Global-phaseTop kChunk selectionTop n chunksLLM / Answer Generation \ No newline at end of file diff --git a/convert.py b/convert.py deleted file mode 100755 index 3093c932d3..0000000000 --- a/convert.py +++ /dev/null @@ -1,15 +0,0 @@ -#!/usr/bin/env python3 -# Copyright Vespa.ai. All rights reserved. -#Convert from jsonl to json - -import sys -import json - -docs = [] -for line in open(sys.argv[1]): - d = json.loads(line) - docs.append(d) - -with open(sys.argv[2],"w") as fp: - fp.write(json.dumps(docs)) - diff --git a/css/denali-icon-font/denali-icon-font.css b/css/denali-icon-font/denali-icon-font.css deleted file mode 100644 index 2380362fb5..0000000000 --- a/css/denali-icon-font/denali-icon-font.css +++ /dev/null @@ -1,5106 +0,0 @@ -/* Copyright 2019, Oath Inc. */ -/* Licensed under the terms of the MIT license. See LICENSE file in project root for terms. */ - - -/* denali-icon-font Webfont */ -@font-face { - font-family: 'denali-icon-font'; - src: url('denali-icon-font.eot?1617868027209'); - src: url('denali-icon-font.eot?#iefix-1617868027209') format('embedded-opentype'), - url('denali-icon-font.svg?1617868027209') format('svg'), - url('denali-icon-font.woff?1617868027209') format('woff'), - url('denali-icon-font.ttf?1617868027209') format('truetype'); - font-weight: normal; - font-style: normal; -} - -.d-icon[class^="d-"], .d-icon[class*=" d-"] { - font-family: 'denali-icon-font' !important; - - speak: none; - font-style: normal; - font-weight: normal; - font-variant: normal; - text-transform: none; - line-height: 1; - font-size: 24px; - - vertical-align: middle; - text-decoration: inherit; - text-rendering: optimizeLegibility; - - /* Better Font Rendering */ - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; -} - -/* Modifiers */ -.d-icon.is-extrasmall { - font-size: 12px; -} - -.d-icon.is-small { - font-size: 18px; -} - -.d-icon.is-medium { - font-size: 36px; -} - -.d-icon.is-large { - font-size: 48px; -} - -/* Glyphs list */ - -.d-5g-solid:before { - content: '\ea01'; -} - -.d-5g:before { - content: '\ea02'; -} - -.d-accessibility-assisted-listening-solid:before { - content: '\ea03'; -} - -.d-accessibility-assisted-listening:before { - content: '\ea04'; -} - -.d-accessibility-blind-solid:before { - content: '\ea05'; -} - -.d-accessibility-blind:before { - content: '\ea06'; -} - -.d-accessibility-braille-solid:before { - content: '\ea07'; -} - -.d-accessibility-braille:before { - content: '\ea08'; -} - -.d-accessibility-circle-solid:before { - content: '\ea09'; -} - -.d-accessibility-circle:before { - content: '\ea0a'; -} - -.d-accessibility-cognitive-disability-solid:before { - content: '\ea0b'; -} - -.d-accessibility-cognitive-disability:before { - content: '\ea0c'; -} - -.d-accessibility-deaf-solid:before { - content: '\ea0d'; -} - -.d-accessibility-deaf:before { - content: '\ea0e'; -} - -.d-accessibility-hearing-solid:before { - content: '\ea0f'; -} - -.d-accessibility-hearing:before { - content: '\ea10'; -} - -.d-accessibility-sign-language-solid:before { - content: '\ea11'; -} - -.d-accessibility-sign-language:before { - content: '\ea12'; -} - -.d-accessibility-solid:before { - content: '\ea13'; -} - -.d-accessibility:before { - content: '\ea14'; -} - -.d-accessible-solid:before { - content: '\ea15'; -} - -.d-accessible:before { - content: '\ea16'; -} - -.d-activity-solid:before { - content: '\ea17'; -} - -.d-activity:before { - content: '\ea18'; -} - -.d-add-circle-solid:before { - content: '\ea19'; -} - -.d-add-circle:before { - content: '\ea1a'; -} - -.d-add-solid:before { - content: '\ea1b'; -} - -.d-add-square-solid:before { - content: '\ea1c'; -} - -.d-add-square:before { - content: '\ea1d'; -} - -.d-add:before { - content: '\ea1e'; -} - -.d-ai-solid:before { - content: '\ea1f'; -} - -.d-ai:before { - content: '\ea20'; -} - -.d-airplane-slash-solid:before { - content: '\ea21'; -} - -.d-airplane-slash:before { - content: '\ea22'; -} - -.d-airplane-solid:before { - content: '\ea23'; -} - -.d-airplane:before { - content: '\ea24'; -} - -.d-align-bottom-solid:before { - content: '\ea25'; -} - -.d-align-bottom:before { - content: '\ea26'; -} - -.d-align-center-solid:before { - content: '\ea27'; -} - -.d-align-center:before { - content: '\ea28'; -} - -.d-align-object-bottom-solid:before { - content: '\ea29'; -} - -.d-align-object-bottom:before { - content: '\ea2a'; -} - -.d-align-object-center-solid:before { - content: '\ea2b'; -} - -.d-align-object-center:before { - content: '\ea2c'; -} - -.d-align-object-horizontal-solid:before { - content: '\ea2d'; -} - -.d-align-object-horizontal:before { - content: '\ea2e'; -} - -.d-align-object-left-solid:before { - content: '\ea2f'; -} - -.d-align-object-left:before { - content: '\ea30'; -} - -.d-align-object-right-solid:before { - content: '\ea31'; -} - -.d-align-object-right:before { - content: '\ea32'; -} - -.d-align-object-top-solid:before { - content: '\ea33'; -} - -.d-align-object-top:before { - content: '\ea34'; -} - -.d-align-object-vertical-solid:before { - content: '\ea35'; -} - -.d-align-object-vertical:before { - content: '\ea36'; -} - -.d-align-top-solid:before { - content: '\ea37'; -} - -.d-align-top:before { - content: '\ea38'; -} - -.d-ambulance-solid:before { - content: '\ea39'; -} - -.d-ambulance:before { - content: '\ea3a'; -} - -.d-android:before { - content: '\ea3b'; -} - -.d-angular:before { - content: '\ea3c'; -} - -.d-apple-ios:before { - content: '\ea3d'; -} - -.d-apple:before { - content: '\ea3e'; -} - -.d-apps-large-solid:before { - content: '\ea3f'; -} - -.d-apps-large:before { - content: '\ea40'; -} - -.d-apps-small-solid:before { - content: '\ea41'; -} - -.d-apps-small:before { - content: '\ea42'; -} - -.d-archive-solid:before { - content: '\ea43'; -} - -.d-archive:before { - content: '\ea44'; -} - -.d-arrow-back-solid:before { - content: '\ea45'; -} - -.d-arrow-back:before { - content: '\ea46'; -} - -.d-arrow-block-down-solid:before { - content: '\ea47'; -} - -.d-arrow-block-down:before { - content: '\ea48'; -} - -.d-arrow-block-left-solid:before { - content: '\ea49'; -} - -.d-arrow-block-left:before { - content: '\ea4a'; -} - -.d-arrow-block-right-solid:before { - content: '\ea4b'; -} - -.d-arrow-block-right:before { - content: '\ea4c'; -} - -.d-arrow-block-up-solid:before { - content: '\ea4d'; -} - -.d-arrow-block-up:before { - content: '\ea4e'; -} - -.d-arrow-down-solid:before { - content: '\ea4f'; -} - -.d-arrow-down:before { - content: '\ea50'; -} - -.d-arrow-left-solid:before { - content: '\ea51'; -} - -.d-arrow-left:before { - content: '\ea52'; -} - -.d-arrow-right-solid:before { - content: '\ea53'; -} - -.d-arrow-right:before { - content: '\ea54'; -} - -.d-arrow-skip-back-solid:before { - content: '\ea55'; -} - -.d-arrow-skip-back:before { - content: '\ea56'; -} - -.d-arrow-skip-forward-solid:before { - content: '\ea57'; -} - -.d-arrow-skip-forward:before { - content: '\ea58'; -} - -.d-arrow-sort-solid:before { - content: '\ea59'; -} - -.d-arrow-sort:before { - content: '\ea5a'; -} - -.d-arrow-up-solid:before { - content: '\ea5b'; -} - -.d-arrow-up:before { - content: '\ea5c'; -} - -.d-arrowhead-down-circle-solid:before { - content: '\ea5d'; -} - -.d-arrowhead-down-circle:before { - content: '\ea5e'; -} - -.d-arrowhead-down-solid:before { - content: '\ea5f'; -} - -.d-arrowhead-down:before { - content: '\ea60'; -} - -.d-arrowhead-left-circle-solid:before { - content: '\ea61'; -} - -.d-arrowhead-left-circle:before { - content: '\ea62'; -} - -.d-arrowhead-left-solid:before { - content: '\ea63'; -} - -.d-arrowhead-left:before { - content: '\ea64'; -} - -.d-arrowhead-right-circle-solid:before { - content: '\ea65'; -} - -.d-arrowhead-right-circle:before { - content: '\ea66'; -} - -.d-arrowhead-right-solid:before { - content: '\ea67'; -} - -.d-arrowhead-right:before { - content: '\ea68'; -} - -.d-arrowhead-up-circle-solid:before { - content: '\ea69'; -} - -.d-arrowhead-up-circle:before { - content: '\ea6a'; -} - -.d-arrowhead-up-solid:before { - content: '\ea6b'; -} - -.d-arrowhead-up:before { - content: '\ea6c'; -} - -.d-artifactory:before { - content: '\ea6d'; -} - -.d-arts-and-crafts-solid:before { - content: '\ea6e'; -} - -.d-arts-and-crafts:before { - content: '\ea6f'; -} - -.d-ascending-a-z-solid:before { - content: '\ea70'; -} - -.d-ascending-a-z:before { - content: '\ea71'; -} - -.d-assignment-check-solid:before { - content: '\ea72'; -} - -.d-assignment-check:before { - content: '\ea73'; -} - -.d-assignment-priority-solid:before { - content: '\ea74'; -} - -.d-assignment-priority:before { - content: '\ea75'; -} - -.d-assignment-text-solid:before { - content: '\ea76'; -} - -.d-assignment-text:before { - content: '\ea77'; -} - -.d-athenz:before { - content: '\ea78'; -} - -.d-attachment-solid:before { - content: '\ea79'; -} - -.d-attachment:before { - content: '\ea7a'; -} - -.d-audio-description-solid:before { - content: '\ea7b'; -} - -.d-audio-description:before { - content: '\ea7c'; -} - -.d-aws:before { - content: '\ea7d'; -} - -.d-baby-stroller-solid:before { - content: '\ea7e'; -} - -.d-baby-stroller:before { - content: '\ea7f'; -} - -.d-bare-metal-solid:before { - content: '\ea80'; -} - -.d-bare-metal:before { - content: '\ea81'; -} - -.d-battery-charging-solid:before { - content: '\ea82'; -} - -.d-battery-charging:before { - content: '\ea83'; -} - -.d-battery-empty-solid:before { - content: '\ea84'; -} - -.d-battery-empty:before { - content: '\ea85'; -} - -.d-battery-full-solid:before { - content: '\ea86'; -} - -.d-battery-full:before { - content: '\ea87'; -} - -.d-battery-half-solid:before { - content: '\ea88'; -} - -.d-battery-half:before { - content: '\ea89'; -} - -.d-battery-low-solid:before { - content: '\ea8a'; -} - -.d-battery-low:before { - content: '\ea8b'; -} - -.d-bing:before { - content: '\ea8c'; -} - -.d-biohazard-solid:before { - content: '\ea8d'; -} - -.d-biohazard:before { - content: '\ea8e'; -} - -.d-blood-test-solid:before { - content: '\ea8f'; -} - -.d-blood-test:before { - content: '\ea90'; -} - -.d-bomb-solid:before { - content: '\ea91'; -} - -.d-bomb:before { - content: '\ea92'; -} - -.d-book-solid:before { - content: '\ea93'; -} - -.d-book:before { - content: '\ea94'; -} - -.d-bookmark-multiple-solid:before { - content: '\ea95'; -} - -.d-bookmark-multiple:before { - content: '\ea96'; -} - -.d-bookmark-solid:before { - content: '\ea97'; -} - -.d-bookmark:before { - content: '\ea98'; -} - -.d-branch-solid:before { - content: '\ea99'; -} - -.d-branch:before { - content: '\ea9a'; -} - -.d-briefcase-solid:before { - content: '\ea9b'; -} - -.d-briefcase:before { - content: '\ea9c'; -} - -.d-broadcast-solid:before { - content: '\ea9d'; -} - -.d-broadcast-tower-solid:before { - content: '\ea9e'; -} - -.d-broadcast-tower:before { - content: '\ea9f'; -} - -.d-broadcast:before { - content: '\eaa0'; -} - -.d-browser-bar-chart-solid:before { - content: '\eaa1'; -} - -.d-browser-bar-chart:before { - content: '\eaa2'; -} - -.d-browser-code-solid:before { - content: '\eaa3'; -} - -.d-browser-code:before { - content: '\eaa4'; -} - -.d-browser-cost-solid:before { - content: '\eaa5'; -} - -.d-browser-cost:before { - content: '\eaa6'; -} - -.d-browser-delete-solid:before { - content: '\eaa7'; -} - -.d-browser-delete:before { - content: '\eaa8'; -} - -.d-browser-open-in-solid:before { - content: '\eaa9'; -} - -.d-browser-open-in:before { - content: '\eaaa'; -} - -.d-browser-setting-solid:before { - content: '\eaab'; -} - -.d-browser-setting:before { - content: '\eaac'; -} - -.d-browser-solid:before { - content: '\eaad'; -} - -.d-browser:before { - content: '\eaae'; -} - -.d-bug-solid:before { - content: '\eaaf'; -} - -.d-bug:before { - content: '\eab0'; -} - -.d-calculator-solid:before { - content: '\eab1'; -} - -.d-calculator:before { - content: '\eab2'; -} - -.d-calendar-date-range-solid:before { - content: '\eab3'; -} - -.d-calendar-date-range:before { - content: '\eab4'; -} - -.d-calendar-event-activity-solid:before { - content: '\eab5'; -} - -.d-calendar-event-activity:before { - content: '\eab6'; -} - -.d-calendar-event-available-solid:before { - content: '\eab7'; -} - -.d-calendar-event-available:before { - content: '\eab8'; -} - -.d-calendar-event-busy-solid:before { - content: '\eab9'; -} - -.d-calendar-event-busy:before { - content: '\eaba'; -} - -.d-calendar-event-solid:before { - content: '\eabb'; -} - -.d-calendar-event:before { - content: '\eabc'; -} - -.d-calendar-solid:before { - content: '\eabd'; -} - -.d-calendar:before { - content: '\eabe'; -} - -.d-calypso:before { - content: '\eabf'; -} - -.d-camera-solid:before { - content: '\eac0'; -} - -.d-camera:before { - content: '\eac1'; -} - -.d-cart-shopping-add-solid:before { - content: '\eac2'; -} - -.d-cart-shopping-add:before { - content: '\eac3'; -} - -.d-cart-shopping-remove-solid:before { - content: '\eac4'; -} - -.d-cart-shopping-remove:before { - content: '\eac5'; -} - -.d-cart-shopping-solid:before { - content: '\eac6'; -} - -.d-cart-shopping:before { - content: '\eac7'; -} - -.d-category-list-solid:before { - content: '\eac8'; -} - -.d-category-list:before { - content: '\eac9'; -} - -.d-category-solid:before { - content: '\eaca'; -} - -.d-category:before { - content: '\eacb'; -} - -.d-certificate-check-solid:before { - content: '\eacc'; -} - -.d-certificate-check:before { - content: '\eacd'; -} - -.d-certificate-key-solid:before { - content: '\eace'; -} - -.d-certificate-key:before { - content: '\eacf'; -} - -.d-certificate-ribbon-solid:before { - content: '\ead0'; -} - -.d-certificate-ribbon:before { - content: '\ead1'; -} - -.d-certificate-ssl-solid:before { - content: '\ead2'; -} - -.d-certificate-ssl:before { - content: '\ead3'; -} - -.d-certificate-star-solid:before { - content: '\ead4'; -} - -.d-certificate-star:before { - content: '\ead5'; -} - -.d-chart-area-solid:before { - content: '\ead6'; -} - -.d-chart-area:before { - content: '\ead7'; -} - -.d-chart-bar-horizontal-solid:before { - content: '\ead8'; -} - -.d-chart-bar-horizontal:before { - content: '\ead9'; -} - -.d-chart-bar-vertical-solid:before { - content: '\eada'; -} - -.d-chart-bar-vertical:before { - content: '\eadb'; -} - -.d-chart-big-number-solid:before { - content: '\eadc'; -} - -.d-chart-big-number:before { - content: '\eadd'; -} - -.d-chart-create-solid:before { - content: '\eade'; -} - -.d-chart-create:before { - content: '\eadf'; -} - -.d-chart-donut-solid:before { - content: '\eae0'; -} - -.d-chart-donut:before { - content: '\eae1'; -} - -.d-chart-downward-solid:before { - content: '\eae2'; -} - -.d-chart-downward:before { - content: '\eae3'; -} - -.d-chart-edit-solid:before { - content: '\eae4'; -} - -.d-chart-edit:before { - content: '\eae5'; -} - -.d-chart-flat-solid:before { - content: '\eae6'; -} - -.d-chart-flat:before { - content: '\eae7'; -} - -.d-chart-heatmap-solid:before { - content: '\eae8'; -} - -.d-chart-heatmap:before { - content: '\eae9'; -} - -.d-chart-line-solid:before { - content: '\eaea'; -} - -.d-chart-line-time-solid:before { - content: '\eaeb'; -} - -.d-chart-line-time:before { - content: '\eaec'; -} - -.d-chart-line:before { - content: '\eaed'; -} - -.d-chart-metric-solid:before { - content: '\eaee'; -} - -.d-chart-metric:before { - content: '\eaef'; -} - -.d-chart-multi-metric-solid:before { - content: '\eaf0'; -} - -.d-chart-multi-metric:before { - content: '\eaf1'; -} - -.d-chart-pie-solid:before { - content: '\eaf2'; -} - -.d-chart-pie:before { - content: '\eaf3'; -} - -.d-chart-scatterplot-solid:before { - content: '\eaf4'; -} - -.d-chart-scatterplot:before { - content: '\eaf5'; -} - -.d-chart-threshold-solid:before { - content: '\eaf6'; -} - -.d-chart-threshold:before { - content: '\eaf7'; -} - -.d-chart-upward-solid:before { - content: '\eaf8'; -} - -.d-chart-upward:before { - content: '\eaf9'; -} - -.d-chart-wave-solid:before { - content: '\eafa'; -} - -.d-chart-wave:before { - content: '\eafb'; -} - -.d-chart-x-axis-solid:before { - content: '\eafc'; -} - -.d-chart-x-axis:before { - content: '\eafd'; -} - -.d-chart-y-axis-solid:before { - content: '\eafe'; -} - -.d-chart-y-axis:before { - content: '\eaff'; -} - -.d-check-all-solid:before { - content: '\eb00'; -} - -.d-check-all:before { - content: '\eb01'; -} - -.d-check-circle-solid:before { - content: '\eb02'; -} - -.d-check-circle:before { - content: '\eb03'; -} - -.d-check-solid:before { - content: '\eb04'; -} - -.d-check-square-solid:before { - content: '\eb05'; -} - -.d-check-square:before { - content: '\eb06'; -} - -.d-check:before { - content: '\eb07'; -} - -.d-cicd-solid:before { - content: '\eb08'; -} - -.d-cicd:before { - content: '\eb09'; -} - -.d-circle-solid:before { - content: '\eb0a'; -} - -.d-circle:before { - content: '\eb0b'; -} - -.d-cisco-anyconnect:before { - content: '\eb0c'; -} - -.d-ckms:before { - content: '\eb0d'; -} - -.d-close-caption-solid:before { - content: '\eb0e'; -} - -.d-close-caption:before { - content: '\eb0f'; -} - -.d-close-circle-solid:before { - content: '\eb10'; -} - -.d-close-circle:before { - content: '\eb11'; -} - -.d-close-solid:before { - content: '\eb12'; -} - -.d-close-square-solid:before { - content: '\eb13'; -} - -.d-close-square:before { - content: '\eb14'; -} - -.d-close:before { - content: '\eb15'; -} - -.d-clothes-solid:before { - content: '\eb16'; -} - -.d-clothes:before { - content: '\eb17'; -} - -.d-cloud-check-solid:before { - content: '\eb18'; -} - -.d-cloud-check:before { - content: '\eb19'; -} - -.d-cloud-data-network-solid:before { - content: '\eb1a'; -} - -.d-cloud-data-network:before { - content: '\eb1b'; -} - -.d-cloud-disable-solid:before { - content: '\eb1c'; -} - -.d-cloud-disable:before { - content: '\eb1d'; -} - -.d-cloud-download-solid:before { - content: '\eb1e'; -} - -.d-cloud-download:before { - content: '\eb1f'; -} - -.d-cloud-error-solid:before { - content: '\eb20'; -} - -.d-cloud-error:before { - content: '\eb21'; -} - -.d-cloud-service-solid:before { - content: '\eb22'; -} - -.d-cloud-service:before { - content: '\eb23'; -} - -.d-cloud-settings-solid:before { - content: '\eb24'; -} - -.d-cloud-settings:before { - content: '\eb25'; -} - -.d-cloud-solid:before { - content: '\eb26'; -} - -.d-cloud-upload-solid:before { - content: '\eb27'; -} - -.d-cloud-upload:before { - content: '\eb28'; -} - -.d-cloud:before { - content: '\eb29'; -} - -.d-code-programming-solid:before { - content: '\eb2a'; -} - -.d-code-programming:before { - content: '\eb2b'; -} - -.d-code-solid:before { - content: '\eb2c'; -} - -.d-code:before { - content: '\eb2d'; -} - -.d-collapse-fullscreen-solid:before { - content: '\eb2e'; -} - -.d-collapse-fullscreen:before { - content: '\eb2f'; -} - -.d-collapse-horizontal-solid:before { - content: '\eb30'; -} - -.d-collapse-horizontal:before { - content: '\eb31'; -} - -.d-collapse-vertical-solid:before { - content: '\eb32'; -} - -.d-collapse-vertical:before { - content: '\eb33'; -} - -.d-collection-solid:before { - content: '\eb34'; -} - -.d-collection:before { - content: '\eb35'; -} - -.d-comment-announcement-solid:before { - content: '\eb36'; -} - -.d-comment-announcement:before { - content: '\eb37'; -} - -.d-comment-blank-solid:before { - content: '\eb38'; -} - -.d-comment-blank:before { - content: '\eb39'; -} - -.d-comment-forum-solid:before { - content: '\eb3a'; -} - -.d-comment-forum:before { - content: '\eb3b'; -} - -.d-comment-help-solid:before { - content: '\eb3c'; -} - -.d-comment-help:before { - content: '\eb3d'; -} - -.d-comment-text-solid:before { - content: '\eb3e'; -} - -.d-comment-text:before { - content: '\eb3f'; -} - -.d-computer-code-solid:before { - content: '\eb40'; -} - -.d-computer-code:before { - content: '\eb41'; -} - -.d-computer-secured-solid:before { - content: '\eb42'; -} - -.d-computer-secured:before { - content: '\eb43'; -} - -.d-computer-setting-solid:before { - content: '\eb44'; -} - -.d-computer-setting:before { - content: '\eb45'; -} - -.d-computer-solid:before { - content: '\eb46'; -} - -.d-computer:before { - content: '\eb47'; -} - -.d-contacts-solid:before { - content: '\eb48'; -} - -.d-contacts:before { - content: '\eb49'; -} - -.d-container-optimize-solid:before { - content: '\eb4a'; -} - -.d-container-optimize:before { - content: '\eb4b'; -} - -.d-container-solid:before { - content: '\eb4c'; -} - -.d-container:before { - content: '\eb4d'; -} - -.d-copy-file-solid:before { - content: '\eb4e'; -} - -.d-copy-file:before { - content: '\eb4f'; -} - -.d-coronavirus-solid:before { - content: '\eb50'; -} - -.d-coronavirus:before { - content: '\eb51'; -} - -.d-cosmo:before { - content: '\eb52'; -} - -.d-creditcard-solid:before { - content: '\eb53'; -} - -.d-creditcard:before { - content: '\eb54'; -} - -.d-css:before { - content: '\eb55'; -} - -.d-currency-bag-dollar-solid:before { - content: '\eb56'; -} - -.d-currency-bag-dollar:before { - content: '\eb57'; -} - -.d-currency-dollar-solid:before { - content: '\eb58'; -} - -.d-currency-dollar:before { - content: '\eb59'; -} - -.d-currency-euro-solid:before { - content: '\eb5a'; -} - -.d-currency-euro:before { - content: '\eb5b'; -} - -.d-currency-revenue-solid:before { - content: '\eb5c'; -} - -.d-currency-revenue:before { - content: '\eb5d'; -} - -.d-currency-yen-solid:before { - content: '\eb5e'; -} - -.d-currency-yen:before { - content: '\eb5f'; -} - -.d-cursor-arrow-solid:before { - content: '\eb60'; -} - -.d-cursor-arrow:before { - content: '\eb61'; -} - -.d-cursor-pointer-solid:before { - content: '\eb62'; -} - -.d-cursor-pointer:before { - content: '\eb63'; -} - -.d-dark-mode-solid:before { - content: '\eb64'; -} - -.d-dark-mode:before { - content: '\eb65'; -} - -.d-dashboard-add-solid:before { - content: '\eb66'; -} - -.d-dashboard-add:before { - content: '\eb67'; -} - -.d-dashboard-remove-solid:before { - content: '\eb68'; -} - -.d-dashboard-remove:before { - content: '\eb69'; -} - -.d-dashboard-solid:before { - content: '\eb6a'; -} - -.d-dashboard-tile-add-solid:before { - content: '\eb6b'; -} - -.d-dashboard-tile-add:before { - content: '\eb6c'; -} - -.d-dashboard-tile-remove-solid:before { - content: '\eb6d'; -} - -.d-dashboard-tile-remove:before { - content: '\eb6e'; -} - -.d-dashboard-tile-setting-solid:before { - content: '\eb6f'; -} - -.d-dashboard-tile-setting:before { - content: '\eb70'; -} - -.d-dashboard-tile-solid:before { - content: '\eb71'; -} - -.d-dashboard-tile:before { - content: '\eb72'; -} - -.d-dashboard:before { - content: '\eb73'; -} - -.d-data-science-solid:before { - content: '\eb74'; -} - -.d-data-science:before { - content: '\eb75'; -} - -.d-data-source-solid:before { - content: '\eb76'; -} - -.d-data-source:before { - content: '\eb77'; -} - -.d-data-storage-check-solid:before { - content: '\eb78'; -} - -.d-data-storage-check:before { - content: '\eb79'; -} - -.d-data-storage-lock-solid:before { - content: '\eb7a'; -} - -.d-data-storage-lock:before { - content: '\eb7b'; -} - -.d-data-storage-multiple-solid:before { - content: '\eb7c'; -} - -.d-data-storage-multiple:before { - content: '\eb7d'; -} - -.d-data-storage-network-solid:before { - content: '\eb7e'; -} - -.d-data-storage-network:before { - content: '\eb7f'; -} - -.d-data-storage-setting-solid:before { - content: '\eb80'; -} - -.d-data-storage-setting:before { - content: '\eb81'; -} - -.d-data-storage-single-solid:before { - content: '\eb82'; -} - -.d-data-storage-single:before { - content: '\eb83'; -} - -.d-decline-solid:before { - content: '\eb86'; -} - -.d-decline:before { - content: '\eb87'; -} - -.d-denali:before { - content: '\eb88'; -} - -.d-dev-ops-solid:before { - content: '\eb89'; -} - -.d-dev-ops:before { - content: '\eb8a'; -} - -.d-device-mobile-comment-solid:before { - content: '\eb8b'; -} - -.d-device-mobile-comment:before { - content: '\eb8c'; -} - -.d-device-mobile-graph-solid:before { - content: '\eb8d'; -} - -.d-device-mobile-graph:before { - content: '\eb8e'; -} - -.d-device-mobile-notification-solid:before { - content: '\eb8f'; -} - -.d-device-mobile-notification:before { - content: '\eb90'; -} - -.d-device-mobile-priority-solid:before { - content: '\eb91'; -} - -.d-device-mobile-priority:before { - content: '\eb92'; -} - -.d-device-mobile-solid:before { - content: '\eb93'; -} - -.d-device-mobile:before { - content: '\eb94'; -} - -.d-device-multiple-solid:before { - content: '\eb95'; -} - -.d-device-multiple:before { - content: '\eb96'; -} - -.d-device-tablet-comment-solid:before { - content: '\eb97'; -} - -.d-device-tablet-comment:before { - content: '\eb98'; -} - -.d-device-tablet-graph-solid:before { - content: '\eb99'; -} - -.d-device-tablet-graph:before { - content: '\eb9a'; -} - -.d-device-tablet-notification-solid:before { - content: '\eb9b'; -} - -.d-device-tablet-notification:before { - content: '\eb9c'; -} - -.d-device-tablet-solid:before { - content: '\eb9d'; -} - -.d-device-tablet:before { - content: '\eb9e'; -} - -.d-diamond-solid:before { - content: '\eb9f'; -} - -.d-diamond:before { - content: '\eba0'; -} - -.d-dna-solid:before { - content: '\eba1'; -} - -.d-dna:before { - content: '\eba2'; -} - -.d-docker:before { - content: '\eba3'; -} - -.d-domain-solid:before { - content: '\eba4'; -} - -.d-domain:before { - content: '\eba5'; -} - -.d-download-solid:before { - content: '\eba6'; -} - -.d-download:before { - content: '\eba7'; -} - -.d-drag-arrow-solid:before { - content: '\eba8'; -} - -.d-drag-arrow:before { - content: '\eba9'; -} - -.d-drag-handle-solid:before { - content: '\ebaa'; -} - -.d-drag-handle:before { - content: '\ebab'; -} - -.d-dragonfly:before { - content: '\ebac'; -} - -.d-dropbox:before { - content: '\ebad'; -} - -.d-duplicate-solid:before { - content: '\ebae'; -} - -.d-duplicate:before { - content: '\ebaf'; -} - -.d-eject-circle-solid:before { - content: '\ebb0'; -} - -.d-eject-circle:before { - content: '\ebb1'; -} - -.d-ember:before { - content: '\ebb2'; -} - -.d-ere-solid:before { - content: '\ebb3'; -} - -.d-ere:before { - content: '\ebb4'; -} - -.d-escalate-solid:before { - content: '\ebb5'; -} - -.d-escalate:before { - content: '\ebb6'; -} - -.d-exclamation-point-solid:before { - content: '\ebb7'; -} - -.d-exclamation-point:before { - content: '\ebb8'; -} - -.d-exit-to-app-solid:before { - content: '\ebb9'; -} - -.d-exit-to-app:before { - content: '\ebba'; -} - -.d-expand-fullscreen-solid:before { - content: '\ebbb'; -} - -.d-expand-fullscreen:before { - content: '\ebbc'; -} - -.d-expand-horizontal-solid:before { - content: '\ebbd'; -} - -.d-expand-horizontal:before { - content: '\ebbe'; -} - -.d-expand-vertical-solid:before { - content: '\ebbf'; -} - -.d-expand-vertical:before { - content: '\ebc0'; -} - -.d-expire-solid:before { - content: '\ebc1'; -} - -.d-expire:before { - content: '\ebc2'; -} - -.d-explore-solid:before { - content: '\ebc3'; -} - -.d-explore:before { - content: '\ebc4'; -} - -.d-external-solid:before { - content: '\ebc5'; -} - -.d-external:before { - content: '\ebc6'; -} - -.d-eye-close-solid:before { - content: '\ebc7'; -} - -.d-eye-close:before { - content: '\ebc8'; -} - -.d-eye-drop-solid:before { - content: '\ebc9'; -} - -.d-eye-drop:before { - content: '\ebca'; -} - -.d-eye-open-solid:before { - content: '\ebcb'; -} - -.d-eye-open:before { - content: '\ebcc'; -} - -.d-face-id-solid:before { - content: '\ebcd'; -} - -.d-face-id:before { - content: '\ebce'; -} - -.d-facebook:before { - content: '\ebcf'; -} - -.d-facial-recognition-solid:before { - content: '\ebd0'; -} - -.d-facial-recognition:before { - content: '\ebd1'; -} - -.d-faucet-solid:before { - content: '\ebd2'; -} - -.d-faucet:before { - content: '\ebd3'; -} - -.d-file-add-solid:before { - content: '\ebd4'; -} - -.d-file-add:before { - content: '\ebd5'; -} - -.d-file-edit-solid:before { - content: '\ebd6'; -} - -.d-file-edit:before { - content: '\ebd7'; -} - -.d-file-minus-solid:before { - content: '\ebd8'; -} - -.d-file-minus:before { - content: '\ebd9'; -} - -.d-file-multiple-solid:before { - content: '\ebda'; -} - -.d-file-multiple:before { - content: '\ebdb'; -} - -.d-file-search-solid:before { - content: '\ebdc'; -} - -.d-file-search:before { - content: '\ebdd'; -} - -.d-file-text-solid:before { - content: '\ebde'; -} - -.d-file-text:before { - content: '\ebdf'; -} - -.d-filter-add-solid:before { - content: '\ebe0'; -} - -.d-filter-add:before { - content: '\ebe1'; -} - -.d-filter-funnel-solid:before { - content: '\ebe2'; -} - -.d-filter-funnel:before { - content: '\ebe3'; -} - -.d-filter-horizontal-solid:before { - content: '\ebe4'; -} - -.d-filter-horizontal:before { - content: '\ebe5'; -} - -.d-filter-list-solid:before { - content: '\ebe6'; -} - -.d-filter-list:before { - content: '\ebe7'; -} - -.d-filter-remove-solid:before { - content: '\ebe8'; -} - -.d-filter-remove:before { - content: '\ebe9'; -} - -.d-filter-vertical-solid:before { - content: '\ebea'; -} - -.d-filter-vertical:before { - content: '\ebeb'; -} - -.d-fingerprint-solid:before { - content: '\ebec'; -} - -.d-fingerprint:before { - content: '\ebed'; -} - -.d-first-aid-kit-solid:before { - content: '\ebee'; -} - -.d-first-aid-kit:before { - content: '\ebef'; -} - -.d-flag-racing-solid:before { - content: '\ebf0'; -} - -.d-flag-racing:before { - content: '\ebf1'; -} - -.d-flag-solid:before { - content: '\ebf2'; -} - -.d-flag:before { - content: '\ebf3'; -} - -.d-flask-solid:before { - content: '\ebf4'; -} - -.d-flask:before { - content: '\ebf5'; -} - -.d-flip-solid:before { - content: '\ebf6'; -} - -.d-flip:before { - content: '\ebf7'; -} - -.d-flower-solid:before { - content: '\ebf8'; -} - -.d-flower:before { - content: '\ebf9'; -} - -.d-folder-add-solid:before { - content: '\ebfa'; -} - -.d-folder-add:before { - content: '\ebfb'; -} - -.d-folder-bar-chart-solid:before { - content: '\ebfc'; -} - -.d-folder-bar-chart:before { - content: '\ebfd'; -} - -.d-folder-favorite-solid:before { - content: '\ebfe'; -} - -.d-folder-favorite:before { - content: '\ebff'; -} - -.d-folder-key-solid:before { - content: '\ec00'; -} - -.d-folder-key:before { - content: '\ec01'; -} - -.d-folder-merge-solid:before { - content: '\ec02'; -} - -.d-folder-merge:before { - content: '\ec03'; -} - -.d-folder-minus-solid:before { - content: '\ec04'; -} - -.d-folder-minus:before { - content: '\ec05'; -} - -.d-folder-multiple-solid:before { - content: '\ec06'; -} - -.d-folder-multiple:before { - content: '\ec07'; -} - -.d-folder-open-solid:before { - content: '\ec08'; -} - -.d-folder-open:before { - content: '\ec09'; -} - -.d-folder-solid:before { - content: '\ec0a'; -} - -.d-folder:before { - content: '\ec0b'; -} - -.d-footprint-solid:before { - content: '\ec0c'; -} - -.d-footprint:before { - content: '\ec0d'; -} - -.d-forward-circle-solid:before { - content: '\ec0e'; -} - -.d-forward-circle:before { - content: '\ec0f'; -} - -.d-functions-solid:before { - content: '\ec10'; -} - -.d-functions:before { - content: '\ec11'; -} - -.d-germs-solid:before { - content: '\ec12'; -} - -.d-germs:before { - content: '\ec13'; -} - -.d-github:before { - content: '\ec14'; -} - -.d-gitter:before { - content: '\ec15'; -} - -.d-go-green:before { - content: '\ec16'; -} - -.d-golang:before { - content: '\ec17'; -} - -.d-google:before { - content: '\ec18'; -} - -.d-grid-solid:before { - content: '\ec19'; -} - -.d-grid:before { - content: '\ec1a'; -} - -.d-group-solid:before { - content: '\ec1b'; -} - -.d-group:before { - content: '\ec1c'; -} - -.d-hacker-solid:before { - content: '\ec1d'; -} - -.d-hacker:before { - content: '\ec1e'; -} - -.d-hands-clean-solid:before { - content: '\ec1f'; -} - -.d-hands-clean:before { - content: '\ec20'; -} - -.d-hands-medical-solid:before { - content: '\ec21'; -} - -.d-hands-medical:before { - content: '\ec22'; -} - -.d-hands-wash-solid:before { - content: '\ec23'; -} - -.d-hands-wash:before { - content: '\ec24'; -} - -.d-hands-with-water-solid:before { - content: '\ec25'; -} - -.d-hands-with-water:before { - content: '\ec26'; -} - -.d-handshake-slash-solid:before { - content: '\ec27'; -} - -.d-handshake-slash:before { - content: '\ec28'; -} - -.d-handshake-solid:before { - content: '\ec29'; -} - -.d-handshake:before { - content: '\ec2a'; -} - -.d-hash-solid:before { - content: '\ec2b'; -} - -.d-hash:before { - content: '\ec2c'; -} - -.d-heart-health-solid:before { - content: '\ec2d'; -} - -.d-heart-health:before { - content: '\ec2e'; -} - -.d-heart-like-solid:before { - content: '\ec2f'; -} - -.d-heart-like:before { - content: '\ec30'; -} - -.d-help-circle-solid:before { - content: '\ec31'; -} - -.d-help-circle:before { - content: '\ec32'; -} - -.d-home-solid:before { - content: '\ec33'; -} - -.d-home:before { - content: '\ec34'; -} - -.d-horoscope-aquarius-solid:before { - content: '\ec35'; -} - -.d-horoscope-aquarius:before { - content: '\ec36'; -} - -.d-horoscope-aries-solid:before { - content: '\ec37'; -} - -.d-horoscope-aries:before { - content: '\ec38'; -} - -.d-horoscope-cancer-solid:before { - content: '\ec39'; -} - -.d-horoscope-cancer:before { - content: '\ec3a'; -} - -.d-horoscope-capricorn-solid:before { - content: '\ec3b'; -} - -.d-horoscope-capricorn:before { - content: '\ec3c'; -} - -.d-horoscope-gemini-solid:before { - content: '\ec3d'; -} - -.d-horoscope-gemini:before { - content: '\ec3e'; -} - -.d-horoscope-leo-solid:before { - content: '\ec3f'; -} - -.d-horoscope-leo:before { - content: '\ec40'; -} - -.d-horoscope-libra-solid:before { - content: '\ec41'; -} - -.d-horoscope-libra:before { - content: '\ec42'; -} - -.d-horoscope-pisces-solid:before { - content: '\ec43'; -} - -.d-horoscope-pisces:before { - content: '\ec44'; -} - -.d-horoscope-sagittarius-solid:before { - content: '\ec45'; -} - -.d-horoscope-sagittarius:before { - content: '\ec46'; -} - -.d-horoscope-scorpio-solid:before { - content: '\ec47'; -} - -.d-horoscope-scorpio:before { - content: '\ec48'; -} - -.d-horoscope-taurus-solid:before { - content: '\ec49'; -} - -.d-horoscope-taurus:before { - content: '\ec4a'; -} - -.d-horoscope-virgo-solid:before { - content: '\ec4b'; -} - -.d-horoscope-virgo:before { - content: '\ec4c'; -} - -.d-hospital-solid:before { - content: '\ec4d'; -} - -.d-hospital-symbol-solid:before { - content: '\ec4e'; -} - -.d-hospital-symbol:before { - content: '\ec4f'; -} - -.d-hospital:before { - content: '\ec50'; -} - -.d-hourglass-solid:before { - content: '\ec51'; -} - -.d-hourglass:before { - content: '\ec52'; -} - -.d-information-circle-solid:before { - content: '\ec53'; -} - -.d-information-circle:before { - content: '\ec54'; -} - -.d-infrastructure-solid:before { - content: '\ec55'; -} - -.d-infrastructure:before { - content: '\ec56'; -} - -.d-input-solid:before { - content: '\ec57'; -} - -.d-input:before { - content: '\ec58'; -} - -.d-instagram:before { - content: '\ec59'; -} - -.d-invoice-solid:before { - content: '\ec5a'; -} - -.d-invoice:before { - content: '\ec5b'; -} - -.d-java:before { - content: '\ec5c'; -} - -.d-jenkins:before { - content: '\ec5d'; -} - -.d-jira:before { - content: '\ec5e'; -} - -.d-jive:before { - content: '\ec5f'; -} - -.d-key-circle-solid:before { - content: '\ec60'; -} - -.d-key-circle:before { - content: '\ec61'; -} - -.d-key-solid:before { - content: '\ec62'; -} - -.d-key:before { - content: '\ec63'; -} - -.d-keyboard-backspace-solid:before { - content: '\ec64'; -} - -.d-keyboard-backspace:before { - content: '\ec65'; -} - -.d-keyboard-capslock-solid:before { - content: '\ec66'; -} - -.d-keyboard-capslock:before { - content: '\ec67'; -} - -.d-keyboard-command-solid:before { - content: '\ec68'; -} - -.d-keyboard-command:before { - content: '\ec69'; -} - -.d-keyboard-control-solid:before { - content: '\ec6a'; -} - -.d-keyboard-control:before { - content: '\ec6b'; -} - -.d-keyboard-option-solid:before { - content: '\ec6c'; -} - -.d-keyboard-option:before { - content: '\ec6d'; -} - -.d-keyboard-percent-solid:before { - content: '\ec6e'; -} - -.d-keyboard-percent:before { - content: '\ec6f'; -} - -.d-keyboard-return-solid:before { - content: '\ec70'; -} - -.d-keyboard-return:before { - content: '\ec71'; -} - -.d-keyboard-shift-solid:before { - content: '\ec72'; -} - -.d-keyboard-shift:before { - content: '\ec73'; -} - -.d-keyboard-solid:before { - content: '\ec74'; -} - -.d-keyboard-tab-solid:before { - content: '\ec75'; -} - -.d-keyboard-tab:before { - content: '\ec76'; -} - -.d-keyboard:before { - content: '\ec77'; -} - -.d-label-solid:before { - content: '\ec78'; -} - -.d-label:before { - content: '\ec79'; -} - -.d-laptop-in-house-solid:before { - content: '\ec7a'; -} - -.d-laptop-in-house:before { - content: '\ec7b'; -} - -.d-law-building-solid:before { - content: '\ec7c'; -} - -.d-law-building:before { - content: '\ec7d'; -} - -.d-law-scale-solid:before { - content: '\ec7e'; -} - -.d-law-scale:before { - content: '\ec7f'; -} - -.d-layers-solid:before { - content: '\ec80'; -} - -.d-layers:before { - content: '\ec81'; -} - -.d-layout-one-solid:before { - content: '\ec82'; -} - -.d-layout-one:before { - content: '\ec83'; -} - -.d-layout-three-solid:before { - content: '\ec84'; -} - -.d-layout-three:before { - content: '\ec85'; -} - -.d-layout-two-solid:before { - content: '\ec86'; -} - -.d-layout-two:before { - content: '\ec87'; -} - -.d-leaf-solid:before { - content: '\ec88'; -} - -.d-leaf:before { - content: '\ec89'; -} - -.d-lightbulb-solid:before { - content: '\ec8a'; -} - -.d-lightbulb:before { - content: '\ec8b'; -} - -.d-line-style-solid:before { - content: '\ec8c'; -} - -.d-line-style:before { - content: '\ec8d'; -} - -.d-line-thickness-solid:before { - content: '\ec8e'; -} - -.d-line-thickness:before { - content: '\ec8f'; -} - -.d-link-solid:before { - content: '\ec90'; -} - -.d-link:before { - content: '\ec91'; -} - -.d-linkedin:before { - content: '\ec92'; -} - -.d-list-check-solid:before { - content: '\ec93'; -} - -.d-list-check:before { - content: '\ec94'; -} - -.d-list-solid:before { - content: '\ec95'; -} - -.d-list:before { - content: '\ec96'; -} - -.d-location-add-solid:before { - content: '\ec97'; -} - -.d-location-add:before { - content: '\ec98'; -} - -.d-location-current-solid:before { - content: '\ec99'; -} - -.d-location-current:before { - content: '\ec9a'; -} - -.d-location-edit-solid:before { - content: '\ec9b'; -} - -.d-location-edit:before { - content: '\ec9c'; -} - -.d-location-not-found-solid:before { - content: '\ec9d'; -} - -.d-location-not-found:before { - content: '\ec9e'; -} - -.d-location-pin-solid:before { - content: '\ec9f'; -} - -.d-location-pin:before { - content: '\eca0'; -} - -.d-location-point-of-interest-solid:before { - content: '\eca1'; -} - -.d-location-point-of-interest:before { - content: '\eca2'; -} - -.d-location-remove-solid:before { - content: '\eca3'; -} - -.d-location-remove:before { - content: '\eca4'; -} - -.d-location-solid:before { - content: '\eca5'; -} - -.d-location:before { - content: '\eca6'; -} - -.d-lock-close-solid:before { - content: '\eca7'; -} - -.d-lock-close:before { - content: '\eca8'; -} - -.d-lock-open-solid:before { - content: '\eca9'; -} - -.d-lock-open-source-solid:before { - content: '\ecaa'; -} - -.d-lock-open-source:before { - content: '\ecab'; -} - -.d-lock-open:before { - content: '\ecac'; -} - -.d-lungs-solid:before { - content: '\ecad'; -} - -.d-lungs-virus-solid:before { - content: '\ecae'; -} - -.d-lungs-virus:before { - content: '\ecaf'; -} - -.d-lungs:before { - content: '\ecb0'; -} - -.d-machine-learning-solid:before { - content: '\ecb1'; -} - -.d-machine-learning:before { - content: '\ecb2'; -} - -.d-mail-new-solid:before { - content: '\ecb3'; -} - -.d-mail-new:before { - content: '\ecb4'; -} - -.d-mail-solid:before { - content: '\ecb5'; -} - -.d-mail-spam-solid:before { - content: '\ecb6'; -} - -.d-mail-spam:before { - content: '\ecb7'; -} - -.d-mail:before { - content: '\ecb8'; -} - -.d-map-solid:before { - content: '\ecb9'; -} - -.d-map:before { - content: '\ecba'; -} - -.d-mask-ventilator-solid:before { - content: '\ecbb'; -} - -.d-mask-ventilator:before { - content: '\ecbc'; -} - -.d-medal-solid:before { - content: '\ecbd'; -} - -.d-medal:before { - content: '\ecbe'; -} - -.d-medical-clinic-solid:before { - content: '\ecbf'; -} - -.d-medical-clinic:before { - content: '\ecc0'; -} - -.d-medium:before { - content: '\ecc1'; -} - -.d-megaphone-solid:before { - content: '\ecc2'; -} - -.d-megaphone:before { - content: '\ecc3'; -} - -.d-memory-card-solid:before { - content: '\ecc4'; -} - -.d-memory-card:before { - content: '\ecc5'; -} - -.d-memory-chip-solid:before { - content: '\ecc6'; -} - -.d-memory-chip:before { - content: '\ecc7'; -} - -.d-men-solid:before { - content: '\ecc8'; -} - -.d-men:before { - content: '\ecc9'; -} - -.d-menu-dialpad-solid:before { - content: '\ecca'; -} - -.d-menu-dialpad:before { - content: '\eccb'; -} - -.d-menu-hamburger-solid:before { - content: '\eccc'; -} - -.d-menu-hamburger:before { - content: '\eccd'; -} - -.d-merge-solid:before { - content: '\ecce'; -} - -.d-merge:before { - content: '\eccf'; -} - -.d-mic-solid:before { - content: '\ecd0'; -} - -.d-mic:before { - content: '\ecd1'; -} - -.d-microscope-solid:before { - content: '\ecd2'; -} - -.d-microscope:before { - content: '\ecd3'; -} - -.d-minus-circle-solid:before { - content: '\ecd4'; -} - -.d-minus-circle:before { - content: '\ecd5'; -} - -.d-minus-solid:before { - content: '\ecd6'; -} - -.d-minus-square-solid:before { - content: '\ecd7'; -} - -.d-minus-square:before { - content: '\ecd8'; -} - -.d-minus:before { - content: '\ecd9'; -} - -.d-monitor-solid:before { - content: '\ecda'; -} - -.d-monitor:before { - content: '\ecdb'; -} - -.d-monitoring-health-solid:before { - content: '\ecdc'; -} - -.d-monitoring-health:before { - content: '\ecdd'; -} - -.d-moon-solid:before { - content: '\ecde'; -} - -.d-moon:before { - content: '\ecdf'; -} - -.d-more-horizontal-circle-solid:before { - content: '\ece0'; -} - -.d-more-horizontal-circle:before { - content: '\ece1'; -} - -.d-more-horizontal-solid:before { - content: '\ece2'; -} - -.d-more-horizontal:before { - content: '\ece3'; -} - -.d-more-vertical-circle-solid:before { - content: '\ece4'; -} - -.d-more-vertical-circle:before { - content: '\ece5'; -} - -.d-more-vertical-solid:before { - content: '\ece6'; -} - -.d-more-vertical:before { - content: '\ece7'; -} - -.d-network-mapping-solid:before { - content: '\ece8'; -} - -.d-network-mapping:before { - content: '\ece9'; -} - -.d-network-platform-solid:before { - content: '\ecea'; -} - -.d-network-platform:before { - content: '\eceb'; -} - -.d-network-policy-solid:before { - content: '\ecec'; -} - -.d-network-policy:before { - content: '\eced'; -} - -.d-network-resource-solid:before { - content: '\ecee'; -} - -.d-network-resource:before { - content: '\ecef'; -} - -.d-network-role-solid:before { - content: '\ecf0'; -} - -.d-network-role:before { - content: '\ecf1'; -} - -.d-network-solid:before { - content: '\ecf2'; -} - -.d-network-workload-group-solid:before { - content: '\ecf3'; -} - -.d-network-workload-group:before { - content: '\ecf4'; -} - -.d-network-workload-solid:before { - content: '\ecf5'; -} - -.d-network-workload:before { - content: '\ecf6'; -} - -.d-network:before { - content: '\ecf7'; -} - -.d-new-rectangle-solid:before { - content: '\ecf8'; -} - -.d-new-rectangle:before { - content: '\ecf9'; -} - -.d-new-star-solid:before { - content: '\ecfa'; -} - -.d-new-star:before { - content: '\ecfb'; -} - -.d-news-solid:before { - content: '\ecfc'; -} - -.d-news:before { - content: '\ecfd'; -} - -.d-newspaper-solid:before { - content: '\ecfe'; -} - -.d-newspaper:before { - content: '\ecff'; -} - -.d-node-js:before { - content: '\ed00'; -} - -.d-node-solid:before { - content: '\ed01'; -} - -.d-node:before { - content: '\ed02'; -} - -.d-nodes-group-solid:before { - content: '\ed03'; -} - -.d-nodes-group:before { - content: '\ed04'; -} - -.d-nodes-solid:before { - content: '\ed05'; -} - -.d-nodes:before { - content: '\ed06'; -} - -.d-notebook-solid:before { - content: '\ed07'; -} - -.d-notebook:before { - content: '\ed08'; -} - -.d-notification-add-solid:before { - content: '\ed09'; -} - -.d-notification-add:before { - content: '\ed0a'; -} - -.d-notification-disable-solid:before { - content: '\ed0b'; -} - -.d-notification-disable:before { - content: '\ed0c'; -} - -.d-notification-minus-solid:before { - content: '\ed0d'; -} - -.d-notification-minus:before { - content: '\ed0e'; -} - -.d-notification-on-solid:before { - content: '\ed0f'; -} - -.d-notification-on:before { - content: '\ed10'; -} - -.d-notification-priority-solid:before { - content: '\ed11'; -} - -.d-notification-priority:before { - content: '\ed12'; -} - -.d-notification-snooze-solid:before { - content: '\ed13'; -} - -.d-notification-snooze:before { - content: '\ed14'; -} - -.d-notification-solid:before { - content: '\ed15'; -} - -.d-notification:before { - content: '\ed16'; -} - -.d-npm:before { - content: '\ed17'; -} - -.d-okta:before { - content: '\ed18'; -} - -.d-onboard-solid:before { - content: '\ed19'; -} - -.d-onboard:before { - content: '\ed1a'; -} - -.d-open-caption-solid:before { - content: '\ed1b'; -} - -.d-open-caption:before { - content: '\ed1c'; -} - -.d-openstack:before { - content: '\ed1d'; -} - -.d-opsgenie:before { - content: '\ed1e'; -} - -.d-output-solid:before { - content: '\ed1f'; -} - -.d-output:before { - content: '\ed20'; -} - -.d-page-first-solid:before { - content: '\ed21'; -} - -.d-page-first:before { - content: '\ed22'; -} - -.d-page-last-solid:before { - content: '\ed23'; -} - -.d-page-last:before { - content: '\ed24'; -} - -.d-page-top-solid:before { - content: '\ed25'; -} - -.d-page-top:before { - content: '\ed26'; -} - -.d-paint-bucket-solid:before { - content: '\ed27'; -} - -.d-paint-bucket:before { - content: '\ed28'; -} - -.d-palette-solid:before { - content: '\ed29'; -} - -.d-palette:before { - content: '\ed2a'; -} - -.d-partners-solid:before { - content: '\ed2b'; -} - -.d-partners:before { - content: '\ed2c'; -} - -.d-pause-circle-solid:before { - content: '\ed2d'; -} - -.d-pause-circle:before { - content: '\ed2e'; -} - -.d-pen-tool-solid:before { - content: '\ed2f'; -} - -.d-pen-tool:before { - content: '\ed30'; -} - -.d-pencil-solid:before { - content: '\ed31'; -} - -.d-pencil:before { - content: '\ed32'; -} - -.d-people-distance-solid:before { - content: '\ed33'; -} - -.d-people-distance:before { - content: '\ed34'; -} - -.d-pets-solid:before { - content: '\ed35'; -} - -.d-pets:before { - content: '\ed36'; -} - -.d-photo-solid:before { - content: '\ed37'; -} - -.d-photo:before { - content: '\ed38'; -} - -.d-picture-in-picture-bottom-solid:before { - content: '\ed39'; -} - -.d-picture-in-picture-bottom:before { - content: '\ed3a'; -} - -.d-picture-in-picture-top-solid:before { - content: '\ed3b'; -} - -.d-picture-in-picture-top:before { - content: '\ed3c'; -} - -.d-pin-solid:before { - content: '\ed3d'; -} - -.d-pin:before { - content: '\ed3e'; -} - -.d-pinterest:before { - content: '\ed3f'; -} - -.d-pipeline-solid:before { - content: '\ed40'; -} - -.d-pipeline:before { - content: '\ed41'; -} - -.d-play-circle-solid:before { - content: '\ed42'; -} - -.d-play-circle:before { - content: '\ed43'; -} - -.d-plug-connect-solid:before { - content: '\ed44'; -} - -.d-plug-connect:before { - content: '\ed45'; -} - -.d-plug-in-solid:before { - content: '\ed46'; -} - -.d-plug-in:before { - content: '\ed47'; -} - -.d-plug-outlet-solid:before { - content: '\ed48'; -} - -.d-plug-outlet:before { - content: '\ed49'; -} - -.d-plug-power-solid:before { - content: '\ed4a'; -} - -.d-plug-power:before { - content: '\ed4b'; -} - -.d-podcast-solid:before { - content: '\ed4c'; -} - -.d-podcast:before { - content: '\ed4d'; -} - -.d-power-solid:before { - content: '\ed4e'; -} - -.d-power:before { - content: '\ed4f'; -} - -.d-principles-solid:before { - content: '\ed50'; -} - -.d-principles:before { - content: '\ed51'; -} - -.d-printer-solid:before { - content: '\ed52'; -} - -.d-printer:before { - content: '\ed53'; -} - -.d-processing-solid:before { - content: '\ed54'; -} - -.d-processing:before { - content: '\ed55'; -} - -.d-qr-code-solid:before { - content: '\ed56'; -} - -.d-qr-code:before { - content: '\ed57'; -} - -.d-queue-solid:before { - content: '\ed58'; -} - -.d-queue:before { - content: '\ed59'; -} - -.d-radar:before { - content: '\ed5a'; -} - -.d-react:before { - content: '\ed5b'; -} - -.d-rearrange-solid:before { - content: '\ed5c'; -} - -.d-rearrange:before { - content: '\ed5d'; -} - -.d-recycle-solid:before { - content: '\ed5e'; -} - -.d-recycle:before { - content: '\ed5f'; -} - -.d-refresh-setting-solid:before { - content: '\ed60'; -} - -.d-refresh-setting:before { - content: '\ed61'; -} - -.d-refresh-solid:before { - content: '\ed62'; -} - -.d-refresh:before { - content: '\ed63'; -} - -.d-reroute-solid:before { - content: '\ed64'; -} - -.d-reroute:before { - content: '\ed65'; -} - -.d-rewind-circle-solid:before { - content: '\ed66'; -} - -.d-rewind-circle:before { - content: '\ed67'; -} - -.d-rotate-left-solid:before { - content: '\ed68'; -} - -.d-rotate-left:before { - content: '\ed69'; -} - -.d-rotate-right-solid:before { - content: '\ed6a'; -} - -.d-rotate-right:before { - content: '\ed6b'; -} - -.d-rss-feed-solid:before { - content: '\ed6c'; -} - -.d-rss-feed:before { - content: '\ed6d'; -} - -.d-save-solid:before { - content: '\ed6e'; -} - -.d-save:before { - content: '\ed6f'; -} - -.d-scale-expand-solid:before { - content: '\ed70'; -} - -.d-scale-expand:before { - content: '\ed71'; -} - -.d-scale-minimize-solid:before { - content: '\ed72'; -} - -.d-scale-minimize:before { - content: '\ed73'; -} - -.d-scan-solid:before { - content: '\ed74'; -} - -.d-scan:before { - content: '\ed75'; -} - -.d-scissors-solid:before { - content: '\ed76'; -} - -.d-scissors:before { - content: '\ed77'; -} - -.d-screwdriver:before { - content: '\ed78'; -} - -.d-search-job-solid:before { - content: '\ed79'; -} - -.d-search-job:before { - content: '\ed7a'; -} - -.d-search-money-solid:before { - content: '\ed7b'; -} - -.d-search-money:before { - content: '\ed7c'; -} - -.d-search-solid:before { - content: '\ed7d'; -} - -.d-search:before { - content: '\ed7e'; -} - -.d-security-priority-solid:before { - content: '\ed7f'; -} - -.d-security-priority:before { - content: '\ed80'; -} - -.d-security-shield-solid:before { - content: '\ed81'; -} - -.d-security-shield:before { - content: '\ed82'; -} - -.d-security-verified-solid:before { - content: '\ed83'; -} - -.d-security-verified:before { - content: '\ed84'; -} - -.d-send-solid:before { - content: '\ed85'; -} - -.d-send:before { - content: '\ed86'; -} - -.d-service-error-solid:before { - content: '\ed87'; -} - -.d-service-error:before { - content: '\ed88'; -} - -.d-service-setting-solid:before { - content: '\ed89'; -} - -.d-service-setting:before { - content: '\ed8a'; -} - -.d-setting-solid:before { - content: '\ed8b'; -} - -.d-setting:before { - content: '\ed8c'; -} - -.d-share-arrow-solid:before { - content: '\ed8d'; -} - -.d-share-arrow:before { - content: '\ed8e'; -} - -.d-share-solid:before { - content: '\ed8f'; -} - -.d-share:before { - content: '\ed90'; -} - -.d-shuffle-data-model-solid:before { - content: '\ed91'; -} - -.d-shuffle-data-model:before { - content: '\ed92'; -} - -.d-shuffle-solid:before { - content: '\ed93'; -} - -.d-shuffle:before { - content: '\ed94'; -} - -.d-sink-solid:before { - content: '\ed95'; -} - -.d-sink:before { - content: '\ed96'; -} - -.d-skip-next-circle-solid:before { - content: '\ed97'; -} - -.d-skip-next-circle:before { - content: '\ed98'; -} - -.d-skip-next-solid:before { - content: '\ed99'; -} - -.d-skip-next:before { - content: '\ed9a'; -} - -.d-skip-previous-circle-solid:before { - content: '\ed9b'; -} - -.d-skip-previous-circle:before { - content: '\ed9c'; -} - -.d-skip-previous-solid:before { - content: '\ed9d'; -} - -.d-skip-previous:before { - content: '\ed9e'; -} - -.d-skip-solid:before { - content: '\ed9f'; -} - -.d-skip:before { - content: '\eda0'; -} - -.d-skull-solid:before { - content: '\eda1'; -} - -.d-skull:before { - content: '\eda2'; -} - -.d-slack:before { - content: '\eda3'; -} - -.d-snowflake-solid:before { - content: '\eda4'; -} - -.d-snowflake:before { - content: '\eda5'; -} - -.d-soap-solid:before { - content: '\eda6'; -} - -.d-soap:before { - content: '\eda7'; -} - -.d-specs-solid:before { - content: '\eda8'; -} - -.d-specs:before { - content: '\eda9'; -} - -.d-split-solid:before { - content: '\edaa'; -} - -.d-split:before { - content: '\edab'; -} - -.d-splunk:before { - content: '\edac'; -} - -.d-sports-baseball-solid:before { - content: '\edad'; -} - -.d-sports-baseball:before { - content: '\edae'; -} - -.d-sports-basketball-solid:before { - content: '\edaf'; -} - -.d-sports-basketball:before { - content: '\edb0'; -} - -.d-sports-boxing-solid:before { - content: '\edb1'; -} - -.d-sports-boxing:before { - content: '\edb2'; -} - -.d-sports-cricket-solid:before { - content: '\edb3'; -} - -.d-sports-cricket:before { - content: '\edb4'; -} - -.d-sports-football-solid:before { - content: '\edb5'; -} - -.d-sports-football:before { - content: '\edb6'; -} - -.d-sports-golf-solid:before { - content: '\edb7'; -} - -.d-sports-golf:before { - content: '\edb8'; -} - -.d-sports-hockey-solid:before { - content: '\edb9'; -} - -.d-sports-hockey:before { - content: '\edba'; -} - -.d-sports-mma-solid:before { - content: '\edbb'; -} - -.d-sports-mma:before { - content: '\edbc'; -} - -.d-sports-soccer-solid:before { - content: '\edbd'; -} - -.d-sports-soccer:before { - content: '\edbe'; -} - -.d-sports-tennis-solid:before { - content: '\edbf'; -} - -.d-sports-tennis:before { - content: '\edc0'; -} - -.d-sports-volleyball-solid:before { - content: '\edc1'; -} - -.d-sports-volleyball:before { - content: '\edc2'; -} - -.d-spring-solid:before { - content: '\edc3'; -} - -.d-spring:before { - content: '\edc4'; -} - -.d-star-solid:before { - content: '\edc5'; -} - -.d-star:before { - content: '\edc6'; -} - -.d-stethoscope-solid:before { - content: '\edc7'; -} - -.d-stethoscope:before { - content: '\edc8'; -} - -.d-stop-circle-solid:before { - content: '\edc9'; -} - -.d-stop-circle:before { - content: '\edca'; -} - -.d-stop-urgent-solid:before { - content: '\edcb'; -} - -.d-stop-urgent:before { - content: '\edcc'; -} - -.d-stop-warning-solid:before { - content: '\edcd'; -} - -.d-stop-warning:before { - content: '\edce'; -} - -.d-store-close-solid:before { - content: '\edcf'; -} - -.d-store-close:before { - content: '\edd0'; -} - -.d-store-solid:before { - content: '\edd1'; -} - -.d-store:before { - content: '\edd2'; -} - -.d-subdirectory-left-solid:before { - content: '\edd3'; -} - -.d-subdirectory-left:before { - content: '\edd4'; -} - -.d-subdirectory-right-solid:before { - content: '\edd5'; -} - -.d-subdirectory-right:before { - content: '\edd6'; -} - -.d-subtitles-solid:before { - content: '\edd7'; -} - -.d-subtitles:before { - content: '\edd8'; -} - -.d-sun-solid:before { - content: '\edd9'; -} - -.d-sun:before { - content: '\edda'; -} - -.d-swap-horizontal-circle-solid:before { - content: '\eddb'; -} - -.d-swap-horizontal-circle:before { - content: '\eddc'; -} - -.d-swap-horizontal-solid:before { - content: '\eddd'; -} - -.d-swap-horizontal:before { - content: '\edde'; -} - -.d-swap-vertical-circle-solid:before { - content: '\eddf'; -} - -.d-swap-vertical-circle:before { - content: '\ede0'; -} - -.d-swap-vertical-solid:before { - content: '\ede1'; -} - -.d-swap-vertical:before { - content: '\ede2'; -} - -.d-sync-error-solid:before { - content: '\ede3'; -} - -.d-sync-error:before { - content: '\ede4'; -} - -.d-sync-solid:before { - content: '\ede5'; -} - -.d-sync:before { - content: '\ede6'; -} - -.d-syringe-solid:before { - content: '\ede7'; -} - -.d-syringe:before { - content: '\ede8'; -} - -.d-table-column-solid:before { - content: '\ede9'; -} - -.d-table-column:before { - content: '\edea'; -} - -.d-table-row-solid:before { - content: '\edeb'; -} - -.d-table-row:before { - content: '\edec'; -} - -.d-table-solid:before { - content: '\eded'; -} - -.d-table:before { - content: '\edee'; -} - -.d-tag-filter-solid:before { - content: '\edef'; -} - -.d-tag-filter:before { - content: '\edf0'; -} - -.d-tag-lock-solid:before { - content: '\edf1'; -} - -.d-tag-lock:before { - content: '\edf2'; -} - -.d-tag-solid:before { - content: '\edf3'; -} - -.d-tag-unlock-solid:before { - content: '\edf4'; -} - -.d-tag-unlock:before { - content: '\edf5'; -} - -.d-tag:before { - content: '\edf6'; -} - -.d-target-solid:before { - content: '\edf7'; -} - -.d-target:before { - content: '\edf8'; -} - -.d-temperature-solid:before { - content: '\edf9'; -} - -.d-temperature:before { - content: '\edfa'; -} - -.d-text-bold-solid:before { - content: '\edfb'; -} - -.d-text-bold:before { - content: '\edfc'; -} - -.d-text-center-solid:before { - content: '\edfd'; -} - -.d-text-center:before { - content: '\edfe'; -} - -.d-text-color-solid:before { - content: '\edff'; -} - -.d-text-color:before { - content: '\ee00'; -} - -.d-text-italic-solid:before { - content: '\ee01'; -} - -.d-text-italic:before { - content: '\ee02'; -} - -.d-text-justify-solid:before { - content: '\ee03'; -} - -.d-text-justify:before { - content: '\ee04'; -} - -.d-text-left-solid:before { - content: '\ee05'; -} - -.d-text-left:before { - content: '\ee06'; -} - -.d-text-right-solid:before { - content: '\ee07'; -} - -.d-text-right:before { - content: '\ee08'; -} - -.d-text-size-solid:before { - content: '\ee09'; -} - -.d-text-size:before { - content: '\ee0a'; -} - -.d-text-strikeout-solid:before { - content: '\ee0b'; -} - -.d-text-strikeout:before { - content: '\ee0c'; -} - -.d-text-style-solid:before { - content: '\ee0d'; -} - -.d-text-style:before { - content: '\ee0e'; -} - -.d-text-underline-solid:before { - content: '\ee0f'; -} - -.d-text-underline:before { - content: '\ee10'; -} - -.d-thumbs-down-solid:before { - content: '\ee11'; -} - -.d-thumbs-down:before { - content: '\ee12'; -} - -.d-thumbs-up-solid:before { - content: '\ee13'; -} - -.d-thumbs-up:before { - content: '\ee14'; -} - -.d-time-alarm-add-solid:before { - content: '\ee15'; -} - -.d-time-alarm-add:before { - content: '\ee16'; -} - -.d-time-alarm-off-solid:before { - content: '\ee17'; -} - -.d-time-alarm-off:before { - content: '\ee18'; -} - -.d-time-alarm-on-solid:before { - content: '\ee19'; -} - -.d-time-alarm-on:before { - content: '\ee1a'; -} - -.d-time-alarm-snooze-solid:before { - content: '\ee1b'; -} - -.d-time-alarm-snooze:before { - content: '\ee1c'; -} - -.d-time-alarm-solid:before { - content: '\ee1d'; -} - -.d-time-alarm:before { - content: '\ee1e'; -} - -.d-time-history-solid:before { - content: '\ee1f'; -} - -.d-time-history:before { - content: '\ee20'; -} - -.d-time-lock-solid:before { - content: '\ee21'; -} - -.d-time-lock:before { - content: '\ee22'; -} - -.d-time-solid:before { - content: '\ee23'; -} - -.d-time-stopwatch-solid:before { - content: '\ee24'; -} - -.d-time-stopwatch:before { - content: '\ee25'; -} - -.d-time-unlock-solid:before { - content: '\ee26'; -} - -.d-time-unlock:before { - content: '\ee27'; -} - -.d-time-update-solid:before { - content: '\ee28'; -} - -.d-time-update:before { - content: '\ee29'; -} - -.d-time:before { - content: '\ee2a'; -} - -.d-timer-solid:before { - content: '\ee2b'; -} - -.d-timer:before { - content: '\ee2c'; -} - -.d-tissue-solid:before { - content: '\ee2d'; -} - -.d-tissue:before { - content: '\ee2e'; -} - -.d-tools-solid:before { - content: '\ee2f'; -} - -.d-tools:before { - content: '\ee30'; -} - -.d-transfer-solid:before { - content: '\ee31'; -} - -.d-transfer:before { - content: '\ee32'; -} - -.d-translate-solid:before { - content: '\ee33'; -} - -.d-translate:before { - content: '\ee34'; -} - -.d-trash-disable-solid:before { - content: '\ee35'; -} - -.d-trash-disable:before { - content: '\ee36'; -} - -.d-trash-solid:before { - content: '\ee37'; -} - -.d-trash:before { - content: '\ee38'; -} - -.d-tree-structure-solid:before { - content: '\ee39'; -} - -.d-tree-structure:before { - content: '\ee3a'; -} - -.d-tumblr:before { - content: '\ee3b'; -} - -.d-tv-solid:before { - content: '\ee3c'; -} - -.d-tv:before { - content: '\ee3d'; -} - -.d-twitter:before { - content: '\ee3e'; -} - -.d-unarchive-solid:before { - content: '\ee3f'; -} - -.d-unarchive:before { - content: '\ee40'; -} - -.d-undo-solid:before { - content: '\ee41'; -} - -.d-undo:before { - content: '\ee42'; -} - -.d-ungroup-solid:before { - content: '\ee43'; -} - -.d-ungroup:before { - content: '\ee44'; -} - -.d-unlink-solid:before { - content: '\ee45'; -} - -.d-unlink:before { - content: '\ee46'; -} - -.d-upload-solid:before { - content: '\ee47'; -} - -.d-upload:before { - content: '\ee48'; -} - -.d-urgent-solid:before { - content: '\ee49'; -} - -.d-urgent:before { - content: '\ee4a'; -} - -.d-user-add-solid:before { - content: '\ee4b'; -} - -.d-user-add:before { - content: '\ee4c'; -} - -.d-user-admin-network-solid:before { - content: '\ee4d'; -} - -.d-user-admin-network:before { - content: '\ee4e'; -} - -.d-user-canonical-solid:before { - content: '\ee4f'; -} - -.d-user-canonical:before { - content: '\ee50'; -} - -.d-user-cough-solid:before { - content: '\ee51'; -} - -.d-user-cough:before { - content: '\ee52'; -} - -.d-user-data-solid:before { - content: '\ee53'; -} - -.d-user-data:before { - content: '\ee54'; -} - -.d-user-doctor-solid:before { - content: '\ee55'; -} - -.d-user-doctor:before { - content: '\ee56'; -} - -.d-user-group-solid:before { - content: '\ee57'; -} - -.d-user-group:before { - content: '\ee58'; -} - -.d-user-id-solid:before { - content: '\ee59'; -} - -.d-user-id:before { - content: '\ee5a'; -} - -.d-user-in-hospital-solid:before { - content: '\ee5b'; -} - -.d-user-in-hospital:before { - content: '\ee5c'; -} - -.d-user-in-house-solid:before { - content: '\ee5d'; -} - -.d-user-in-house:before { - content: '\ee5e'; -} - -.d-user-location-solid:before { - content: '\ee5f'; -} - -.d-user-location:before { - content: '\ee60'; -} - -.d-user-mask-solid:before { - content: '\ee61'; -} - -.d-user-mask:before { - content: '\ee62'; -} - -.d-user-minus-solid:before { - content: '\ee63'; -} - -.d-user-minus:before { - content: '\ee64'; -} - -.d-user-nurse-solid:before { - content: '\ee65'; -} - -.d-user-nurse:before { - content: '\ee66'; -} - -.d-user-org-chart-solid:before { - content: '\ee67'; -} - -.d-user-org-chart:before { - content: '\ee68'; -} - -.d-user-profile-circle-solid:before { - content: '\ee69'; -} - -.d-user-profile-circle:before { - content: '\ee6a'; -} - -.d-user-secure-solid:before { - content: '\ee6b'; -} - -.d-user-secure:before { - content: '\ee6c'; -} - -.d-user-single-solid:before { - content: '\ee6d'; -} - -.d-user-single:before { - content: '\ee6e'; -} - -.d-user-virus-solid:before { - content: '\ee6f'; -} - -.d-user-virus:before { - content: '\ee70'; -} - -.d-value-all-solid:before { - content: '\ee71'; -} - -.d-value-all:before { - content: '\ee72'; -} - -.d-value-average-solid:before { - content: '\ee73'; -} - -.d-value-average:before { - content: '\ee74'; -} - -.d-value-exclude-solid:before { - content: '\ee75'; -} - -.d-value-exclude:before { - content: '\ee76'; -} - -.d-value-maximum-solid:before { - content: '\ee77'; -} - -.d-value-maximum:before { - content: '\ee78'; -} - -.d-value-minimum-solid:before { - content: '\ee79'; -} - -.d-value-minimum:before { - content: '\ee7a'; -} - -.d-value-sum-solid:before { - content: '\ee7b'; -} - -.d-value-sum:before { - content: '\ee7c'; -} - -.d-verizon-check:before { - content: '\ee7d'; -} - -.d-vespa:before { - content: '\ee7e'; -} - -.d-vials-solid:before { - content: '\ee7f'; -} - -.d-vials:before { - content: '\ee80'; -} - -.d-video-solid:before { - content: '\ee81'; -} - -.d-video:before { - content: '\ee82'; -} - -.d-virtual-machine-solid:before { - content: '\ee83'; -} - -.d-virtual-machine:before { - content: '\ee84'; -} - -.d-virus-none-solid:before { - content: '\ee85'; -} - -.d-virus-none:before { - content: '\ee86'; -} - -.d-virus-solid:before { - content: '\ee87'; -} - -.d-virus:before { - content: '\ee88'; -} - -.d-viruses-solid:before { - content: '\ee89'; -} - -.d-viruses:before { - content: '\ee8a'; -} - -.d-voltage-solid:before { - content: '\ee8b'; -} - -.d-voltage:before { - content: '\ee8c'; -} - -.d-volume-down-solid:before { - content: '\ee8d'; -} - -.d-volume-down:before { - content: '\ee8e'; -} - -.d-volume-mute-solid:before { - content: '\ee8f'; -} - -.d-volume-mute:before { - content: '\ee90'; -} - -.d-volume-off-solid:before { - content: '\ee91'; -} - -.d-volume-off:before { - content: '\ee92'; -} - -.d-volume-up-solid:before { - content: '\ee93'; -} - -.d-volume-up:before { - content: '\ee94'; -} - -.d-wallet-solid:before { - content: '\ee95'; -} - -.d-wallet:before { - content: '\ee96'; -} - -.d-warning-solid:before { - content: '\ee97'; -} - -.d-warning:before { - content: '\ee98'; -} - -.d-wifi-disable-solid:before { - content: '\ee99'; -} - -.d-wifi-disable:before { - content: '\ee9a'; -} - -.d-wifi-error-solid:before { - content: '\ee9b'; -} - -.d-wifi-error:before { - content: '\ee9c'; -} - -.d-wifi-solid:before { - content: '\ee9d'; -} - -.d-wifi:before { - content: '\ee9e'; -} - -.d-women-solid:before { - content: '\ee9f'; -} - -.d-women:before { - content: '\eea0'; -} - -.d-yahoo-open-source:before { - content: '\eea1'; -} - -.d-yamas:before { - content: '\eea2'; -} - -.d-youtube:before { - content: '\eea3'; -} - -.d-zoom-in-solid:before { - content: '\eea4'; -} - -.d-zoom-in:before { - content: '\eea5'; -} - -.d-zoom-out-solid:before { - content: '\eea6'; -} - -.d-zoom-out:before { - content: '\eea7'; -} - -.d-ascending-bars-solid:before { - content: '\eea8'; -} - -.d-ascending-bars:before { - content: '\eea9'; -} - -.d-card-american-express-solid:before { - content: '\eeb0'; -} - -.d-card-american-express:before { - content: '\eeb1'; -} - -.d-card-apple-pay-solid:before { - content: '\eeb2'; -} - -.d-card-apple-pay:before { - content: '\eeb3'; -} - -.d-card-mastercard-solid:before { - content: '\eeb4'; -} - -.d-card-mastercard:before { - content: '\eeb5'; -} - -.d-card-paypal-solid:before { - content: '\eeb6'; -} - -.d-card-paypal:before { - content: '\eeb7'; -} - -.d-card-visa-solid:before { - content: '\eeb8'; -} - -.d-card-visa:before { - content: '\eeb9'; -} - -.d-cash-payment-solid:before { - content: '\eec0'; -} - -.d-cash-payment:before { - content: '\eec1'; -} - -.d-coupon-solid:before { - content: '\eec2'; -} - -.d-coupon:before { - content: '\eec3'; -} - -.d-descending-bars-solid:before { - content: '\eec4'; -} - -.d-descending-bars:before { - content: '\eec5'; -} - -.d-descending-z-a-solid:before { - content: '\eec6'; -} - -.d-descending-z-a:before { - content: '\eec7'; -} - -.d-drag-solid:before { - content: '\eec8'; -} - -.d-drag:before { - content: '\eec9'; -} - -.d-gift-solid:before { - content: '\eed0'; -} - -.d-gift:before { - content: '\eed1'; -} - -.d-hint-i-solid:before { - content: '\eed2'; -} - -.d-hint-i:before { - content: '\eed3'; -} - -.d-line-wrapping-solid:before { - content: '\eed4'; -} - -.d-line-wrapping:before { - content: '\eed5'; -} - -.d-question-mark-solid:before { - content: '\eed6'; -} - -.d-question-mark:before { - content: '\eed7'; -} - -.d-rewards-solid:before { - content: '\eed8'; -} - -.d-rewards:before { - content: '\eed9'; -} - -.d-snapchat:before { - content: '\eee0'; -} - -.d-voucher-solid:before { - content: '\eee1'; -} - -.d-voucher:before { - content: '\eee2'; -} - -.d-whatsapp:before { - content: '\eee3'; -} - -.d-yubikey-solid:before { - content: '\eee4'; -} - -.d-yubikey:before { - content: '\eee5'; -} - -.d-delivery-fast:before { - content: '\eee6'; -} - -.d-delivery-schedule:before { - content: '\eee7'; -} - -.d-delivery:before { - content: '\eee8'; -} - -.d-magic-wand:before { - content: '\eee9'; -} - -.d-network-base:before { - content: '\eef0'; -} - -.d-open-data-network:before { - content: '\eef1'; -} - -.d-secret-question:before { - content: '\eef2'; -} - -.d-user-lock:before { - content: '\eef3'; -} - -.d-user-reviewed:before { - content: '\eef4'; -} - -.d-user-setting:before { - content: '\eef5'; -} - -.d-user-unlock:before { - content: '\eef6'; -} - -.d-delivery-fast-solid:before { - content: '\eef7'; -} - -.d-delivery-schedule-solid:before { - content: '\eef8'; -} - -.d-delivery-solid:before { - content: '\eef9'; -} - -.d-network-base-solid:before { - content: '\eff0'; -} - -.d-open-data-network-solid:before { - content: '\eff1'; -} - -.d-secret-question-solid:before { - content: '\eff2'; -} - -.d-user-setting-solid:before { - content: '\eff3'; -} - -.d-magic-wand-solid:before { - content: '\eff4'; -} - -.d-user-lock-solid:before { - content: '\eff5'; -} - -.d-user-reviewed-solid:before { - content: '\eff6'; -} - -.d-user-unlock-solid:before { - content: '\eff7'; -} - -.d-discord:before { - content: '\eff8'; -} - -.d-password-lock:before { - content: '\eff9'; -} - -.d-password-field:before { - content: '\ffa0'; -} - -.d-password-field-solid:before { - content: '\ffa1'; -} - -.d-recovery-plus:before { - content: '\ffa2'; -} - -.d-recovery-plus-solid:before { - content: '\ffa3'; -} - -.d-recovery-shield:before { - content: '\ffa4'; -} - -.d-recovery-shield-solid:before { - content: '\ffa5'; -} - -.d-figma:before { - content: '\fff9'; -} - -.d-framer:before { - content: '\fffa'; -} - -.d-sketch:before { - content: '\fffb'; -} - -.d-abstract:before { - content: '\fffc'; -} - -.d-adobe-xd:before { - content: '\fffd'; -} - -.d-password-lock-solid:before { - content: '\ffff'; -} diff --git a/css/denali-icon-font/denali-icon-font.eot b/css/denali-icon-font/denali-icon-font.eot deleted file mode 100644 index 107cc10a834ed1fd4fb93d3b3b8e417aa9fa750a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 285948 zcmeFad7xF(|NsAbz1F_x+~M5e+A%qY@_Dw=a>h^uE{kjdj|yP-)_$$M*Lt0O&OJETp>n_^ zWd8|;7|+0xxooX&z4ZODS8p-iepq@@>anFqtZW)VBk4REM&sxlY6{M#33%U>MtM3J z8%EJ-*k&|ch;4?^`B;)j{jubHx&YhTt+S~)6;lb;{5SbwGh3(#nofpFI`!#ya`yEF zmttWHEbZH~PqWf?Im180`#E^u|LpO@CQdp0Qxs9`HlkCW89nZjQQwd3i1%aXkh!P9 zn32Oqqz{SSfNghx$}w0F%As9huLJpG#!sFy>!ZWJBEOwPG5f|%ID43XQuuD7(-+|V z{_(@6Obi(-HCUgF?X%AtHhyIK+n?P;PUIT2@I@0RTrl~n@9w#hoR}tP-%wk2vkP02 zlXUpk!@oK?UTy4x`hQLvZwa1U(Q(K~#$fu_7C5iMUAPA>k9T)J8^{KoQZwawoh4-X~HFmiTI~-gEz+ zr}w0B@ z(6IBvW`r#YTN(CD*ygYgupJ`_l0kVw5cTmR`V)pSL?8u_B9WqyqLE^df)q<}6i*3w zO{64BrW8b&(kLA%gEA?LvMGmh5u?hd0xF~;YCsLC5j95Lnj#fbGeoGGQz?~EIaN?4 zQWdqJmQ+oxs5P}g^r|hjqm$@lYLCP|hVC-tJ<2txLu zzSIw)#{o2uPNy?y5Dlg?kX0M~HF)O{7U) z$Z|3QnHSN;GzIArx|A-XsWgo)rz>bWT}fBb)ii^yp=;?nnn|Mt9Raw4Cmx`)CENr2FXsdXQGpL-a5` zLaXUfdW;^YHS`2MNl(#QdYYc0XA!Ai;U&t3BU5qpZ=@Pz_FGHG&G!5x;q$_wj zUx{=TU(GX+uHkF>I;5FAi)ZuoJcn=K8~G-l%ky|X-^>emAur-vcro9~xAE<~gzw-x z`7WfTyo~SWdw4n0y?h_9Kw8Q7^8@@KuR?l=ALd7RH9yLa@#DOPpWr9?DPGG@^E3P` zujA+Vd0x*O_ywdF`6b?n^fJG~ukt2-jbGTlgKMclkZu%J1_B{2^~c z`UvS`q)&J|f6AZn=e&cz;4k?rq@74#BYlJPEz&Ne?~q)iTBO}b-y`io`hoZIkNgw= z%=`Ek{*`~@{ro#0;6L~vAL2Sbj0+Zvh!F=VM110xPzjST^!w8#94SHq5{VQg(Gnv; ziN$N2#7lxCN|Gc?ilj=Kq)UcmN|t0xj^s+78fh!-!YBAtaa1ZgPJFc~gq%Lo}Mqhz#%JLFEeOP0zqxm)g$<#MmwCo5#7+%FHv zgR)8fJj65srXY#r1kS~zFMEVM8 zr+h8n$hWc!={qD>YGt>4FME)FK-!D+BhpXuv+R>!f@`KG`Wnj+J{G&GG&W7EVmHN~cxDKX7WsVOt%rovPrRhbs1B~rC%Wm=myrpB~2 z?aWE$WYgYsKTY_No=ClrdLx}?`k20^pXqM~n1M*An={NH zGuWJI&N4&HP%{i^xH;R5KpKfO%8WK+kj^n<&ACY9kj5jOhcp3cBGM#tzPZ3mHW!+U z%*AGkxx`#*E;CcjG;_JR0%^Lr(p+V(HZ#mM=2~+d(o8c8X|}oE%rQ5Z8_i8-F486`?PiI&1L;n4msyIm4C!ujk6CW+HTRhnW~I5`JYXI)tIR{@ zVe^PtZ5}m`na9l<^MrZQJZ09Jr_D3wS+mYOXP!6f%?9&=dC|ONHky~sE9O zH*c8D=1udKdE0C;@0fSZduFS7-+W*`G~3Ka=411T*={~HpPA3i4)cZi(tKrhny<|_ z=3BGNd}mx!Yj&IO%^vfE*=v3@KOy~$v=8YQq+iW%X21E}958>FgXR!YojHsT(HNH{ z<2X);<8%B@s1xRdBSjzukRp+yoMBh ziXGLvp^E+7)D2TGmL?aYd5is&)s0Xvz9tu?ve?nS8>wK#O)fsUws>|lY=BWXxiN}o z2Vnz@%*l;aJUb3Hz-XP^c*V05U;~Wc$xT!|I|(+xsGeMWx^4076xaY`eRA-S$jwzSiy${o!F+<;d<8QL zatjp9Fvu-bJi7=sz`TRp1`1{%{Z3@r+;$3PT;!gl zVCF^c$%<#UhYc_vBe#QsnHjkq70l7dJw?H6joeNO=56G5RxpDjw~KgmQC&j z3g+A7PFBd)`$7eCaB?qFFdHZLVg>VZa;GSmp_6-wf|)wGmnxop8Ek<0JGoO8%;d?P zreID_?&S(*_vBuoV4hFzbOkeha<5b{_b2x%1yKNUuT~HbAa{m>$N;(5D2Ne|d#!?K z0lC*H_MsY?26Asu5OE;)M#Zylf(;OdAa|~U zhy=Ow6vQUTov$EDLGH~8;uYjBP!PEwccFq92DytAvh8t;g182`ixosT$h}oTtb^R! z6huA9y-|qL5zjm`xHc5$X%fz?n3TL1rZo>?^h6uA@>0VQ5kX{R1lvbca?%j4Y>~~ zh}n?)u!86fxsND_rK}3h#M-{|&$bC#fl!x5M6~ueUU85lKL+%p_VnF0RsUR9e z?o$fcwpptnLPYM<3Svd%KBFLNMDDW+;z#7JQxHia_c;YIC32rv5M3g7y@EIsxf>Kj zoXCAaLF|d#7ZpUI$bCscJc`_n3L;bFzN{cdMeZvKqE+O+svvGf?j{8hEOK8{5X&O> zbp=r^a^Fx8-y(Ojf=Cy+Zz_m+k^7c{=oh(fD~N-UyG21njNEq=#Ky>dS3#7F-1ijw zWS_fLLFA0w_Z7s@$o)V;EREa`70=!V8<1_Mj}*`T7&bu6joeQZ&)yCjAQDIJrwU?n z_bbJ-cftmU?2-Gmf*2pU-zbRnk^8OU z*}Gr^#Q(_sPVsCPHb6X(+*$?kL2`F1h!m3hz2a@N2R1#1YBet01CC?vDy$ zi{$>KAj(MY&kEv=^0#j}5f4G@bY_cz6}_rnH=Pm=q);@Jma1H>%J{X_BW zgRlYOnB*Q(Ji87yKx~uT!-}<0bq~Zlsg@Pb7T5q$P^vWw;-OUQD2RztZHPiUpgslB zQmXYU-Zr7I0V1eW8>S$ZO10q%VyaXdp?G!xHbA76Y9kfIT&XrnLG+bsqZPzqsWwJI zM3!oU3SzTV8>`sL)y64^*HUe~g2*k^CMe!EiLe2pxm25^Ag)Wb$qFL8RGXq8)=Rai z3ZlMLo2DTCOSS0=BEeLfp&%wqwV4W{!&IB4AWlrR*$N`YRGXt9c1*Ro3ZlqVo2MY2 zOttw6BFj`;pdiLfwS@|z%~V^Y*ymqs8z?xnRNGKtd!8F9_F2{1#tPf>+(cn}o|`J3 zT?`wrJ?G67&n|%t?2Nj$x#HQSumM|NC{sMU95!I<7ZnOyzo=B$`bCw()-PHpZ2h98 z!qzXU6}EoSN@434trfO@(MDnG7c~l7zi6wl^^0~2TfaC-Ve1zsD{TFuy~5TnIw0 z_F&k6t-qY9c=lPa0bAc0qImXD*nq7c4O7_q(Qt*WADyju_6XR3txt_qJbM&uz}CM; zE1o?DHel;(=O~^%7B*n(cjqc>{cfCMpQ^7NudwyI^AxszH$h?RcM}!1em6;B>v!iX zZ2j&6#XHty*nq7MUZ{BXMX&+4gI=t#^~Wg+TYtPnVe5~VDs27nGKH-_PF2|Y<1~e> zKVGhIXXq6QTYsFcu=U3)6}J9(mBQ8^uU6RlyI-Pw*ELvVe5~x706|d4s~%H*Zwf`sPgvTi=|ku=UM(3R~Ztudwyan-#XcxjzlVIY<+XF!qzu$RoMFGZ3!ctFQrI1>L0Z)zH@zo&kMb@$5HX1HJ~jSz+6s-cQm>sNj1nRJ%=K+qXVa*!HcD z6}Elr6NPQx+OF_I=%)(XzV(^Hw?IEv*!Ha*3f~I-LSfstzEs%ut*;ceeQT$}cR;^Z z*!Hb&6ut}kt>W3cU<0=O>^p^*L0yGyKdV*v9_Vg`mqWi-JbMpp!1qFbP}ugly$Y{@ z{-}8NPp|>6g#N6s?SK0eM&GObMd1gbzbd>6`kUg}`(Xoq2>QFi4?_7Fv zsIcvehZKGcTBoq>i-#3PUqp3182xZJD~!IlTNFk=+-($I3w0EJ8XBVTGfH?U;};=ny&C$&i72XbQrtqiG5`{m5Hdpv_XsN^BT|w@^I0C2YW7L#q|fZUr0gH_+CKXSab3_*-a=;@NFs1KtH~r+D^BumOJuJz4SW z_OJoF&<=`ccZ3bN7J7=}*_~hmM(5hyS@G;HumOJ$Jyr4SuCM{`fp$|oyE|;aKR|mZ zycgP2;UA&B6#fa?Tj8IfrzyM-+DGAEpnVno71~eX-=O^!-VYt1@bA!p3Lk);uJ9kw zGZa1u9i;Ff=wOBGpl2$471RbIXLx(DY6}yKiVxYqnaiC`_o;?CKkPzrdMSRdv ziuj?U6$yooQSht_?LJ5G?6I(cjpy$^SMls|uz`d_$19$F9&8{H&dI4k)F_J73l?Cr$}$;bBde>eO{41(DjP+g>F!!AM^!9`a@q-WB~Lf zMFv7QDsnpXWkt?_zM{w==&OnhhHg^iOz3NhXTJ^`$XU=g6wlra8^{pon~G<@1sljv z=-Y~CZ-EVD81x-QhC|;~e^g~6)K({G!4)i0%+va1~ zK*mBpQRH0cc16ZPKUHKr^fN`ygMO~a1n3S$CPKeZWD@jCMb3wQrFiyE*g!6Teyzx4 z=r@X72>n*^>|L;dTm=12k&B_OB2%EXid+KSt;nU&?-jWWx<`?z&>s|;2HmU3<`tpCVU5e^KOW=&y>*fc~b)HPHQvTnqhO@$3Vzfm{duL-Fi`uz}2k z9#Ui$v`&%P(8G#cPd41`A#8c<|0G*XdUp;3z528~wac4&+uOQ1nT?tsQBawjxSk-MPriY$dD zD6$NisL0*WBt`ClCM&WWnxe?P&{Re4gQh960-CPKN@#{6_d_!kc>tQF$b-;qMOHy` z6nO}mt9W)EY#~CXyU!llz&`QO#t6&3p9@;{Y z_0X1zY=BlP@&dG#A}>N)EAkSwjUpSNHHy3pZL7#D&~}Qv3Oz}YP0*7Sc@5fLk=LOe z6nO*MQIXBiQxtg<+DVbOpq&+Y8`?#YEznaHc?a56k$0ip6nPKYU6HNO9*VpV?WuTn zFW5jnfc948L+ELWY=ibu(;zGwOQVTs#k=@V&QVMV^ajQFpf@VU54}k-q0qUC34_j4OgMDDVj`e7D<%M4pm_E|*uX?W z7bzwRdW&MBp^Ft01HDx-LFjFYXWtGRm{{l%#l%7HP)t1ZPQ|nDf(=XpbgAOm%U}bO z2)$b|Nzi)~lMG$1m=x%}if7*k8<R~YZcQN z`m|!2K%Y@eQ|PmbDTc07Of%?niYb9UubAf0^@=HlZct1a^aaI~Ltj)(1@tAwR6;i@ zp8YaxV5*?6D5eGURmHS~Zc;q^HQ2yZLtj@+E9e`FX${@1m^RQi6;lI!OEGPsZ!4x9 zbcS;1-eZ!ouD5nrZem+ZEFl`l({NK|fPG`*Ya9bcgOxOb_T6is=dcQt|AsU<1<&x>ND&uVDkz8~Tmn z+26tj<}~Oo#q@!Gr>pqQb2@adV$Oj6sF*>} zpA<6~`m~l`PB9li;}tU* znxL2qp^1vQ2%4mri=oMinF38w%q7rN#as$aQ_N-1bj3`CW+-MFG*dB`L$eff1vFdn z>>Sv@Oo!$w=1ORuVy=SbE9PowfnsJr3l(z>v`8`6LK`UNI%q@1vm3z%W+t?;VrD^` zD4yLEHZZfH#frHe+DtKXpe2gA0oq(KH$qDla}%^oF>|5iif32A24)_#QZe(PRf@S8 z+Cni4pe+@%5L&I6MbK7?xdqxXEyDR2CXb;7# zfc8|(N@y>|+z;)omlS?Jk{SqB}VnCGA) z74tlFlw#IHM=NFnbc|wNfS#k67olSn^Ahx2#cYI*Q_RcI@rro`dY)olg-%e+Cg?=P zyat`5nAf4_E9MR81&Y}WovfHQp%*IVE$Bsxc^i7MVzxl1DCQmLC5m|$dZ}XGgI=bX ztJD@Wa^96L4V!njVR?Jt>>lL#TI!7^ILvK*bH_#gu^DXoy#q5I4Rm^wLd5UqN z^A%GIy;(84p$ioAJ#?XB_COaY<_G94irEWYte796w<_i*=xvH;-wqp?pP@?>&%OgT zF#Di)DxQ58Y+!zYE>%2x8EjyFh2E{0-=Oy>W{nm|Cjfm_@$5~offEURP4Vn4uz?c=wS5UV z(NNo$fD;3?eF=EB?MuK3LTz6Ho^AUQaAKjhF9FZCeF->mP}`S)6A!h005}QIFBB&c zYWo0ilAyK^04Eu`Q*lzDwhsU&75a_hq(N;T08Tn|m*QkVzf+t{=w8Lig8ryD+0dU9 zCkOho;^ad2DNY{r7sbhk{;D_y(BBlN5V~J+ilDzMP6MJJa9oSi5Q^hkoJLR_*WxsW z;a!w1w<`Sjuw-_ zUZR$30JIuqs)pSP`?Ok1_6w5K8f9+N4p`ktR0F#$%Fq_aZ;Sle1;9?ClheU2qV^N< zWk(#N!*YDNu`|BN7z(!Fi;L6oCB=MvA#ohOe28tktt0A={d>$L>gfR3y&3>)+Z)^U z#`4~~h)x>}ux+0bFc&|ojWYE`Is2h}{YMZDC?^`&4G$A6Avz-lKnDfD0(a+iS4jwM~ewn?f|RGtn&MJ-dYH`Z4$+Yiu`XC(#Ya=SJjr z(@LVbyNTv4Aeujz=w`5BGSNbmYZ3NYgzavDeajA_#nXvy#j@K_rrR5U4Ma;&r#n!V zJ5lC4_YvJSj%Zmou!`vJ5k&XQ1z5fu_PyBe-YrD;Vf~79qLtZT6Vd&h0k(Ny4bg+x zf0YB~5IxijEGBw50FcjW9P?3Z^H^)5$4kIoqBYBjp1?j&BEP3X0qVRq9-wSb6BtAE z4BkHrU5Dd8HxTS1dj25M`gQn%Q$E;D^b(eDL>XT}9-A zK5t>!TiEYyY`di!(L3b;+q^pl>?C>*$A51v(bgC+o#=h+^ZtIK4{*E>XX1-KO}sDl z&_~$zW4!;Q9e(+|1HOoZI(&+JKEwOZ@qP!&vtt9%7qGu{z&4_<78C89Nc7EQqFr-| zzRL&L@4Joo#!m}84LAay2<|{sU@flX(~0(C{a)npqXV%0Puq!p#`1kziGB?b{f2$^ zcO&|JIne>E|6?Q3p(aFiD{%!JNQ_R)_*9lL%VuXIz66qvFMMEm=sM!?Cd3h)i31>V zFmcpW;+SmW;1c54WUv$*B#xUy9N&&Op&THO#1UW@aT2yq#`eiuh*KsKr=|leOU1gh zF$MQ82l7} z4B0OWaLc*G)yT6NrO|0r>`bHqXAe&JP5}aJe2q>>@x&91ltTnS%zWXVc2eXU*fZq0qQtnDe*`g zYb3TEH5hCm9t|Ch_hZHZRfMcVQb`r}%EP#XZ>W-p<7LVcCkc z#1Bj*ei++5(i%WlWB*41u0QhM0E`10h@YQAyuJh2O1uH(cp(|=!h^9hiC<~~rh+ZR8%w}afbzXOnD~`=;#UIz zd2hn|*JA+kdjsX$Je2s&F~o0S`CA8x-$veBucCi}*|IyA$>LrW+o-=DGVz8S>7Oeg+zAn|V=uU8zBFNICi6QlEyJ$Iv#L?-2~-miv5}*&th!T4EbRkCnd0( zV|nupB&8@*SpY!Gq2*XtF@~gaIZ2BWl4=~gdL2ot#U!nvZFZB?bOvik+9i|V+9fB? z1X$jF9KiAp@nA5(_8n1!>G9Bx$oJ(@m6p|U^ z0QR{C_O;0WI^;1EI;$JlMlyRQ$@SQN4%%hTE|MFN-wnw7#+@WL!Jdo#=FK3vIhkYu z@?N-`1Y<3^Wi83AINoh*NN(>8Hj~_e?e3aRvJ}T$hU4Gegyf!3lI4@hKDZ();z?GH zAh{plnk5fr0~~YJ7Ltds-@`cGBPC!P9wtFvk8U7&Y#+%Qynmt>K)IgWPV&@X5{#W> z?P`*zhmt%K0LbH6tbeWr9^`08vVIEKO|k*o;+iEd>?C;!`ESJWUdFbsAm3M)kZi(n zUt3P{dN-0c#*u8sao)@)c?)^Ig}mR|K=L+@_4Z_vEinM^-V09Dw|{qK;d! z{QVM=53)%1ymvQ|)MDN4H6$2M38E45Lp!h; zANnuH2mHwUr==u6Pa)Y?0&u)vR+0QVhvc_0B>PdG-v^Q$7)tWTIFf^*0C^w6@ahP|tUJ7m40Q%;@l$Mr!9Fs$ei?i^XB_+rYDhaWzBOe0e)4>)pIoK~3dE|{DlivYsB~yUqg(JumA+MsnWEw0W)2J7jCfK)WYcj>5 z0C_c=4i1tjnMtNO%3g|XFsV1?$h#b6sTd5fPsL6$mC!2WSG9ypi+D0Ev95YGnO0lK zv_bi5R+4Eqg-m`>d`LNw zOb_JKb03*r`^ogicBf4x)2B7qN~SOJ>NgPVAk%*?nF0A=H<^LRW6)4CXCl8dvF@x5 zWJW+oW1Dlz$zV)m#vzaKIR1onWF~gQ2UjTDBy4v+-e0hd%w&||LgaVh8ZuL2$Xq&= z%w;G8t}W*Bc4V%=`sr)QT)BYE)ee~%cz;bVGS^NgGZXf#zGP-2-|JhDnKPIS#zE#r zEW2qbnYk$6JZy6_)-Py6_Cpo35XW4E{BK!KX7NNax8gXrVcqRmcl#1DOJLuD<#%HL zyHLiv_L5nO_sft!#zOe}QMjMtAAY&+T>N@de?$~=3yTWda9KipQYM#HR#mp)^1NKX z&+m)oykJg3PC{i_X<`P)`=dA~7h9HB)o^KIQer}0c~LRvVneJfE3NW&&8t90c4y8n z8h{AXWM>TJs|JinPwPK;Kx#(cn5dGn=20NG>X#*0iW`W!tvtNr@>biAm|Rhll$jLQNE3+2@t4VSQ&;HZE#9Vc3eA z=EY@Y#m#F9GSjlM(lQHHOpA|6NsLVDGd8b5)3j_`)iC>lU(Z{QUv=t9!)XFtM|0^` zx|<#(Oe|1Vzb`SV%vK#WOUjg_L{t_3lj42;+@iv0_QfYP&%|%9)<{tywvnP@Nh)T4 zb1ctfyL0nI>|B_HhD2UR7TRSYzW6LoEUheW#)Y{diKRF|v3T3SuFMo{T@-KE7or{i z-rxsYO=#36!AXges6;2aab~YZ<%6nNGUHn2m9^$(xp}FH>AtYA5_xcUWMtonyp+g3fjmx+ z%#BQsNp92ow48{D$h<&GO_Pwgz`*3ZWc=@=vf@wg%(>F2VXNNAJT|hrL1vbiszDVk zDzZx=lZUj(D?@9h`@_@1lai#QdvR7pL{k4rHC?ahmKe=Zf$Tu@sV7CJ7$-5V_iaVz z4aAS>2C^bfeP&p6YKTdQy4mXKefqglJRsDLz?F$d{1(TEfBiam$ zZ8E`IQ}=3^BxXf!v2~tr%>WT*`0!s_hcx_tTDAS1uRqly3BNCVwD2KCl@;a1Y%5q< zRhpETTqU(dJu4HVIJcN9lj3p<%h6wa z#;QGKMeM`nszz`HisE3rwb8X;dt2HqWOZr*7nFfiPAyQQUeoF#Ik&ofb&kH~z|3X) znlr6_3p1SyD)RFy3b?K^zr27!-Qxx2-V*r;mC9||p*kn0x^AA@c0t`~x&f*ZZtH{J zMqN(uAAI?yBSJ7{x~_l9yCpVLz&rDX5%1tKPzYtbGzT z_wqo)9NS!0=NzkR?y+XcwN=o%=N@gN-1@f2wZ{M4VD)*}raZd2Y^-md4lQ%n9c{=T zvCwO{mbuT?w<%iBYeu`>|IoTW{l})ZiM@7Sk9Lly3~Gm#_3m8paD01#t*Wf3EEa$1 z5iiP1NJ{ki^XxBe=hJzxz!<;wd9p~3RY z@x$GR=QlEO!LaBgQy5xklA^>8S!Q? z!{ONIjT@t5*~tjLIU*TXtlVV#HwtYAQZ1uf1co?7i7g7)Vt9`h_;SL*R zY$cj&Z4;A1N-;RG_t2`!q6$bsG~@bKgmFqU_7@ga;U-(&R&e1%er=w^Oy(jY4|k|RQbLB>gJn$@tmKZHBPN7Y@`G%O`Lu|Yz3 zWkh^~#F&)OVv*v|l$gW@@e!5b2@MjXQ^J~>6_E*HPHak2G=A&-)6P9s-eSM-CX3)*?D7CDsW`hzzFu zsa;OP@8n&3T)Uu6Y}+LIG1|(s_bq#4D`szig7&c6oKl(#77vVl=2Rm8fPfV)H;Wxh-MQS5f@XKTf){0j%(_E|4mDqs;zb?dcJOS!OW9)g!wM~~;YFo>DQ;a_g zfaZpqR+QJsUx$^=@N@qd0pSk=*wc7yXbCUI{-wBySC%GvXUpEs;lQxVcs9mEKiILs z-bns2kQ*3&(#l&0rNzZ0ee(l$oEG^?zN*pya+``4y{b+d{e*J6p@seMm?%AtZOl-9K*Wikx4^Z z=3-bE>SteuFUB9@NK%Sv)}v{5c_4Y%6*F3lh{&tEJ2{xwym_8&>ZaYB;(su?zRjki z%|huU>W4NvGT@EF2&KNR^)2Ot<0jw|ms?n2hsFu~^X3lqd}QQH2_@ryl-+j%#17LwN7tNnUsM z`Hjz?fSbSV-36E+cpcqd{V;LB-OH!fNL&PLKgY$Otv4A-tV*m@%g$-y(B0udyf!93 zm>!#2*(f$7q*>eQD1T;jTy$7U@$FHOA^zOpgR}8R1|o|hmnJ!pQL%Zx?v8M2zmWrz z!a|wLtYqYc`!ixws~W}ne8pMW)ls1t^*KaF`I4gJR$Lp9sEDZW7~kDVCOSGA6BT4q z+K=B43(rl(u{;aY%Dkm;1CN|9<7M@>`mf6_xXJkf2I@rH?dRJd+Ml6T)UJ_Lh2xAWQ zKYvzCrY|Ov+sDQEO8b@CTZ+zg&-Hw+(QzK9x64+XVCtuSab=jmz{R-8)ZZ?bm7G$7 z|Gf0js95y<{Udxa!2l{XDs+^S7!?S{_(tr<3e#V`WhuOCOWpclW{538-LsJ~A(=sL zzXgkrUdN}K=@<=W+mUSnJiy+iac{yOY`{6qi{hjGA;IKT6?qqwOu;qSJGm&U?JjM& zvXCz>>02IJ)UBj?ux(KMD*YnNq`as>1g>AXm^7h$b{tjX{hgCIFF25fn?+($9L8N(Z=yI)21g~D ztHwC-sR_QW?E<-}5;4Xf7atqa_BaSU(R*9uo>Txl~f^uh4&L}R22@59vcKHCgOj1!G~>LUKMiMQ?1^L}J(*YU(9 zI^oDJF3-F2WuTb2#2r2R|ErVC2<$5Ajj8kf>oj3GR&`{~bXkMz}r2y;avGKJi6-A9%f zm?P8jLh}#j&76BE)?SZg4_?>)XRQ~e1dnY{S8?LH=@`@=KaH{oPc`vBoPxMuPO#uF zC6tZ-apZFkjgpPW4xJ*I|9NcA!$!CDJ-&$b^T!RC*JNQX+?-lsR&XI*LATIdw8EAQ zeJ%r6`=kV4mc1}|gSBA2M+b{+Jj3tH^Pz8g@r}lugqsEnjuFVgoawZvyBT}b&1>PDiQJd;@`bO9C<^*HDgqhQJ<+GS@3<~L6O$BP4y#YA z^6=Ey&@O_bC4}g~aE%D@uB-MM(G)YE>u4!GgEL%oJbu~UPHtRM3}1hg2Z zF#%(FoIi9yoIl(=65uKD28`vg&byZ6lm#)?xAP}E;_#-2HH@t*Fy!}!8dwAq-h>N} zu>ml9is3Zq-bZ`}!Ay)(y?a!>2Vl01AgA>w^Q4~b3og*}6fP3}=Ov-%XY~$?NQv-A zM9&@&ttT~l!0hMoK}-?|&5M!2eQ&&tcPo7hT-AO`Ah0 zf!vhHO>Oc_r2o9=P{--n({Vzh&x;DndvY%R+?g-pC4abF__^Im26NvKKZiMikSprO zwdvo6O&~chu&I;J9~l~U=bd4gMsq}$x@SWIP8j<`KL6Z~f^A(iw($adyO8?OU>b`0 zBqdfhg9`q8L=@LF&FpADrnazBqX+Bb@r%v{;ar1qSIrI z?-E{f>+H|mUkjrvc`y?WXx z)9o@}c<7CFn_@C@GlG3{s`InjG)v{UU;;;_W=DtGcIg_7ipq?dGY2nG!Pw~1FQT(k zF`Wsn*kGH5Pw6{G(4;+%NTKWZvka_C-bQ8}O~muby>4&&z>XS1-RcrJx{H zuB|@}IZh1eQkPK~B{7lNN8<_Wy<^+Rcm0^kp5ub!4qiVf)PpC**-3(TeQ$eYdVt<# zCy#$BSKXi!6)WPY6E2lZE$-0Bz-9G?U1aCLi?pbLx(fm)USa$DkulWq^*C`W{;49E z0{=}#{#1{@X}b@E^>i$X^MRgcN2Iv) zdLz`Fzq~gK4?b^O%$$tZx@8CK2>5_CC)wAaeQ&3Q_}>?CmmYRm-DCEZ_t{PB0tlAi z-;r^FmcfolagF$=a>SJ-k~`ogDLav#`9Bx&tluZy!GPq zTWoK;Z`yIO_GsK)+fygo({xsi;@D^lSD_UP{ruFK@r*i&1E=v#rNG%)4)(*{w68&5yce{j?DiD0|zi2;yUT z&#G^Kv#^opl<+|KVud@Hv_aQ?A!j>A|afZ1ykqB!lo+L_}#H?Tfz+c7>d zq+adx6C>=x6{s@$?84*vtAyCDTHiU$^U1+u-M((9_H*0swTZpfuJ6-F%8-pnO8Ng# z4(pD8EsH;=BIg9nOYrtNvV$K6*v8zE?NoSSe22KL1z_Kb|;=`%77D`%fY-$BvckX#1?kb2Ih#gyZkg7`CFZe2G;#iIqj@ggM^(Bb^W9 zwBzpyBjFVs2WOa|*o5Qiv)dld&&3bJbfPQALO*uB4YC8}n zn0Dag-4|>JLrClBX*>S*fVM*DsQ!YUn_@3D!TEm;Bk}A1aN(-Ik{#>wS{A)F{eLJA z;~^;2Bj>*;S6x2m*KOxz-c|j-C?oe+Q8xquog>d=)sH=KF+xbi-XO~>|1>tL?~Xdi zo92bPG_obRCmaKrg*pbRd-u4ZPF;q*8}e86W6vZ0bS3_ZU}M}7dwHKg+nHrWWuZR@ zpBj2M5jOQP{)NJpyBsKrgC_HU|Ps>y9tHGy)ZEYdjB5GX&57?!jA-8XMt>K73!?XhCg0w?# zr4|&oY*}o?$21?Lo3`%bq_ z<~^r_zoWaJf_PG)HJ(Kpjku(}liDtwlnBpgE9mD1Rppfxg++PzR3054pV8ab418K2 zExx3jVh+hI#ODNP4u6%e-pUK{+$_Ojcynp6A^U{r^E-6a&n4uXI(6YJ)s@nb9swBhA>7C3%-^dBK)!@Pcg&HpVs(aEJ>= z5CS1FNr1REP1piia6(EpCk+IGDWxetq12>ln--k>g`cD}OPkbbQ^)i2f9Kx!X4A+P zB*9|ly*D%OopbKJ=brscx$E5co06Qih@0*0^hf1b=%?Z4W-jLoo#{T<0O}CmMLaW^ zfG0f=@`fzIjF|#ilH89+YF)yTB8x3;H>CM>pbLFE0aK6sQSBN<+i#YF3G}Hqu}R5f zk=RCy6s<=aXww7hOtwx~X8sZ0+76T1vKgK0?caJhl9jvexkoZv{zm&hDUL!EW^V|mdSpKap3>( zn@kpVAv^;imlW=k!R*6b5O`YtQ1)ogSUS06Q!aS6OsTy;zmF{*hMP`RRi~;Q3fJ{!k7fJn zBP`m`vaxI3+}=pA(bQ}WNkM-g7k4?`wa!|9u(@xM6oiZ4BlWg4`_({wq_MkszSqZ& zmp(|}Tdu;I^&z$f>1+s$0iEby0ueJ5P-Nw?QivJ>;d!V*NdG*Jxdz?5>}-I?AZ2(E zQYa|ZMaDZ!k@`SgAXHncpo=|1J=`{z%@^~}%7-N)qr% znGn`{nwo{>74GZ2)pp@qQ)8CF_fH)I?3X zh9sA9ZhxzQkxO<}u2tePrzK9miwH+leR{6XfKkfBX@M0D3fOG9|DVk8% zNn9+TjdP5Zh|ew^tF?%eycXWBdoWwErlCqw*m)L;Y!JRBz5$R@OHw7t*+QBW=j1Le zJ+J({^86ite|6Y%oTkBXPx!3x#@}D<8#?^QA1$kfLX5u-;C-Nihb-1EQ zubSZnpBF%^c0}yEC~ro=!9it2p(3OZAxvPm-4uorWdr2U6{{m1gSA~)Y7np_HD%RS zC)U~_7!VIBCY!TW&1#2x6Vm;OUg_NPva&}>_}NST1P-L@-X=NI)$Aner?c5b*3Y-K zEQJTLmnQtRAF1_v@nYDNXdVP=B77eN^C5K{P+1G0q>`}^q;VuB2pcHiDVk;>qu)W# z*0S{MS|&hZ`9tibZTXv|d-J<4#2L=Qrt{1h?{w;ISu|42k8)|rM3&F*(tw9ngY>=H{rWsmIMk;~LM z6|+^ktbz+a_n|JxJ!o*Hym9P9?QMP)bOD5!of0^QQ9Y0Mq2vuk@5 zh23Yb3qx#^kZP{uP4~9cG~@1`jGbjX!~|w|$kG?JpJ+dM@#UGG71qA5onmuXCp7&T z9xCwJhj|D9;9V1AOYBA8DY%(a3V2lkuG$hNVCjBg`IxGG1jykf;`tsni)K&w3kVrB z;*gF351KK^s)+^cGv1v2jCFEAH&uOz`tZvyvIu*$`m+T5w2nCZV24;k#1n-XHb>!{ zEXDpFrWcLCW>OfVai5}SdsSlS0Gp`iiC~EMIH|@s&^h0VY+SAd!J*y+TUs4 zXPs?TAENRx`ZT+!>cforbPVz^r$5EEfQf<2Cpj)LY(Ee~@sGx0#s{&;i=PI?XMA3lz?OaTe9sklMNkhF>%=#s zN9pugaMiQIhjaqk`w8wtV$c0XReys>xmwwMf_;k{g_fOg%j7)5f8yRF=xrBDFO=5a~IiJ^|_VzPI5OtmNdC#G4?d8$U7U%O06XjYRkBE^eq8lb(9Eh zu`YT5x0ifw@UuZG1yiHwlo5(spaAa5OKinyj<~b(ie2`@;~yoaA&!PTSk8vatD39+~d)%gqwSZ%%`hJv8l7 zVVw2(IkVMihKX{X;V;lw#KFNBpXB-mG(=su2*9N;XJI=nkwL1(;$7T? zBjLdi6i4FSkG`Uhq4c2KKx1g|XtHY|JiC0z^d-hel%1gw#S{?ZC_Tja6pGfs8k8c@ z*kJYPDq~h{XbZ9=|-)k84ad<$Cqp=@Sd=Jc(S$1E^(wnw03%{lP zj^@C$Z6!Bt-OXwYt7N_TEU?}4aLsHVNdq$sQ29!maiB{Ws<2mLxm5pj)Ey@x6 zUX0^(w5Ib^Y5Lge>*4*d&B>6~Apzx}7*y_0oU*cR*5m57%N9KB^Fe<3``Rz|spkHZQRf~{$x zL5&dPJcRrqB7!`1*&ItJT@)ouF}`VLg=Ly_+)-Fz$k0pw2C{f1T~B`K(@0a=dU~s> z`3JSXQ4kXG85rD8pVp3cuWSw_jy`>xKNppx+qzjKJa}V!!f95mUWeB#GqW}9=?q7t z-+o$Ex4ynbNlXrDFDm$XgwYK?$ND#3(sbK19}m~F{M)y7#z(_FW+>3iqB;X1cOva? z4%T`}78ZUA!pjG`Ho(iiwB~YwTM?;IiE_0PFy7|Uxlj>57?)vQGH}h36l$T|{$VyM zWe{6^LEQ5n(UJcCFuOv{ui4GMcje&jk*o6W69gFigi}e@nd&@u&gY~U^C+-)GIMWR zz-jX}F1`R|aCXfhRO$OyBaW0Sv>JZbL0JuIQO}nmLGR_OCLD?qE&ME74B_*jE(~e#UyDT znF{tYcJzQza+58xpG4webGu!#_QOI#10ss>ujlNpEw zGVBu*LL(H>2@=s2&HB)T!IwcB4(Us#MpqhZxG#%EwS9)M#q71$WI8ZO( zn~_qKi_fz_{g#|aqTy+ZRp_NXij+;WClYo=cxi?>%sf?K*yRdGJZ7Hl`YO?yM&1LM z#5@$SC;`NnmlA5sK!_qQ9MQ0*m$aSEv*3g*=ORDk`x8!5EAR zDgnG*BMestxTA}-1S_J>WxOMm85u3u&LD!yUs?a!FW=Y{tqTNhx^%}4NB_02!Kw94 zynh*!*Y3Lf@?C3Xw(Qo)-5X8Pt_>#BhFy|rqm;j7mwm(f=)w0L_?uG zjwhz|rvRDm{N~yLwtk+ohD2g=nnTibN$gDD)@7;m;ucdgz`skV5*kS{*) z;RB0P%eJ=T|GuYNdDQgNw8Ea|krz*Quj=Yr)y*Ce?=)IV4lG`LV2QSaZc9=TmkYYU zy{woY0az^);9U!ARwGhk3;Z)z)f}jyK*Rt7H}(HxI1r1p1DC}Sc-KJD@5{9vnm>s^ zVj#sGIsr3J0zk?N5K9;CK=Gix`0Y&hWbQrmADcHSVm;_pTeC-MDhX-QS|;{Pzz47= zF^64_>J%S8f+`p{^B>@=Reqdl=l@M17SaVB{mytEr%Y}b#Bb0VQ3hjf9WKG3j@`|@hYawdF@f+FBmF)O9%B4>wU=F_6$5~4?v z84$sU?#$#swz5dFA#m|uOaO`_NAU8DpoRA0o{8T0o<#5Do;`9`8Tk1(*h^>I@m1~h z_`LT;fG1*U1wyaB+TO0i`y6_E4WM$_ftqjDe7EMu7-cA#&;+G~h0A)#=d`2{;Q@zn zCPZ?dTTaA>Sqg%E4t{xZ-ct&5jwJnI#3!U6KOsNGj95&cqlq7| zLer$%rs=?p4H@6%UyVv4iJt#BoytLhkTN0CO|eLDOWYndIxJ{h(N{J6)hG;s1UN`- z+>D3_k}PBs0K$O+;3U-1KNNJrzclMK*(F(msBMCzW3KI~Yw0B`Qd>O1YJ&~2nA2oI z5d*J36bZI_T&i>^>QSvSWA%-Z02E<`n2FgUjY%e{-OT#um@<|*{gzgnG;VR$Tb*o9 zk6B??hl4GMOfkzQdn_6D+w6AL+u7(RMcM^rtyz{O8Ko2&qX946Ma=H5d)_|WC| zhlf^Nx%0AHetM~y+0A=?a`5Yq*-(&6z0xN!hg6qtjy5PJlUf_GIIK2@*r#*u zXtMpxVv$W2>q{?LE$YiJs|c_#v0qqa_~)%JynyU~yH%0R_F9wK zmCdq^c`as>>}rfQCH-cz8f*+SSarGJz4CL|apxc>WeKcd`tslrg`DY=7)oJ7a6b_j zC2s+i8Jx%>UX5?I6u2f+?1(2uRm@HaNz>DgSecHNo}Ri5yBBBnW)|<>P}kGb)|vlg zAR6t6M%hcjrY5}q1L`(0cCfSKmYO$>uAl~ z8dczoG|I5RT^IlPf=eLt> z_*bC2c(xDH4`uweg3nbvo%phuTy^?CsD2~>#VutJ3?b|lSrNsDYhSyHBI)qVaxl)F z1heKKU>7Ec3br_zYg<@3W01{jm0R#kNs1iKH-!bLfOVR_E z5F>k@!zqpcW;ujR1&Au*X%A|L|HS096yPg423RziP?vz6QA|bULkeS+zenoB!^-AQ>FBW4+UxHOC$lH!t9%^cbWUT5$?^voIN=fR z7$@KPaKe+c!{yJaf|TL+xbQfcJ((GBnwThmObJTw7^jRcQAxOxahjNTto%{aFcRy9 zVtA3F>kA=U9YUQsz|u#)_cH2txA(G_^Z#{9es)3rf4{*jqdWK9{jb`K2<|lV`SiT} ze(>uEB-!^NLW@Tec4M!F5C%(OC@Z5lSr*lKtU)u?Yp`;mj5gy`4u&E~wmf+fOai+V z&j+A*Uu?tNbOaF%av3Bkk+KfOE!PA-LZP29ElN~+GZe&jj_6^rm9x7Lkvn&>Bhj*D zP0*6NBiCHJ`pMO`><-7`xriWf1lDY6`F!WjzNQ%2h=#Ac>B?cevshE#&Q7<@6L$?9 zyfPY8un61^o7L|Obf9+3;*NmRkBW}&bQT{)uRJ*5ihFF@kO)C%OC>97+&k*D%f5|A z(v9p(2v|?`^`-vCIkK-25wr1z)RB!oX>}xENnhGap<=^V4&#rGdM`~|0ufuR$?j-m zgZ;^klVd?!%gFqdchLzwlv zC>y?Q9>9)DdGBOr2|;m!X_BlG6iH7d+6KthzSQM|DDAz>R_C=h+Uxy}IbOAsN?PEh z-d1mGw)^TFjrJ|Rdat+6&oUc*Ej^K2X(Vs;)@>Z$%GNGk`smWdYuVOudD0I%EPmRq z`mAB1{q^6<<*CS_!r_*6p)C5OQ_LSQO?NMhYOxN{y`IM6@QH?WY+ z+wljdoi?7ixPR_8e`t6(
  • 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

    yJoK;Mr8!ZZ=sF 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 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/css/denali-icon-font/denali-icon-font.ttf b/css/denali-icon-font/denali-icon-font.ttf deleted file mode 100644 index b2ff262c85b100cc838511bdb88feca2e810bf33..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 285748 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%0ACNuey~wZ`m2A+hkKi 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

    $X`Mn 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 P`EEV4ie0}y`sn`vHyQO} diff --git a/css/denali-icon-font/denali-icon-font.woff b/css/denali-icon-font/denali-icon-font.woff deleted file mode 100644 index fc40555b95b72cd332b983b365954b42cc6d0d52..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 128548 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%0AB4Z<7)x;D97L=%^;d^ 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 zdiv{width:100%}}.bg-origin-padding-box{background-origin:padding-box !important}.bg-origin-border-box{background-origin:border-box !important}.bg-origin-content-box{background-origin:content-box !important}.bg-clip-padding-box{background-clip:padding-box !important}.bg-clip-border-box{background-clip:border-box !important}.bg-clip-content-box{background-clip:content-box !important}.bg-attachment-scroll{background-attachment:scroll !important}.bg-attachment-fixed{background-attachment:fixed !important}.bg-attachment-local{background-attachment:local !important}.bg-size-auto{background-size:auto !important}.bg-size-cover{background-size:cover !important}.bg-size-contain{background-size:contain !important}.bg-position-left{background-position:left !important}.bg-position-left-top{background-position:left-top !important}.bg-position-left-center{background-position:left-center !important}.bg-position-left-bottom{background-position:left-bottom !important}.bg-position-right{background-position:right !important}.bg-position-right-top{background-position:right-top !important}.bg-position-right-center{background-position:right-center !important}.bg-position-right-bottom{background-position:right-bottom !important}.bg-position-center{background-position:center !important}.bg-position-center-top{background-position:center-top !important}.bg-position-center-center{background-position:center-center !important}.bg-position-center-bottom{background-position:center-bottom !important}.bg-no-repeat{background-repeat:no-repeat !important}.bg-repeat{background-repeat:repeat !important}.bg-repeat-x{background-repeat:repeat-x !important}.bg-repeat-y{background-repeat:repeat-y !important}.bg-repeat-space{background-repeat:space !important}.bg-repeat-round{background-repeat:round !important}.has-bg-black{background-color:#000 !important;color:#fff !important}.is-black{color:#000 !important}.fill-black{fill:#000 !important}.has-bg-white{background-color:#fff !important;color:#000 !important}.is-white{color:#fff !important}.fill-white{fill:#fff !important}.has-bg-brand-100{background-color:#cce2ff !important;color:find-color-invert(#CCE2FF) !important}.is-brand-100{color:#cce2ff !important}.fill-brand-100{fill:#cce2ff !important}.has-bg-brand-200{background-color:#80b6ff !important;color:find-color-invert(#80B6FF) !important}.is-brand-200{color:#80b6ff !important}.fill-brand-200{fill:#80b6ff !important}.has-bg-brand-300{background-color:#016eff !important;color:find-color-invert(#016EFF) !important}.is-brand-300{color:#016eff !important}.fill-brand-300{fill:#016eff !important}.has-bg-brand-400{background-color:#004cb3 !important;color:find-color-invert(#004CB3) !important}.is-brand-400{color:#004cb3 !important}.fill-brand-400{fill:#004cb3 !important}.has-bg-brand-500{background-color:#002b66 !important;color:find-color-invert(#002B66) !important}.is-brand-500{color:#002b66 !important}.fill-brand-500{fill:#002b66 !important}.has-bg-status-danger{background-color:#ea0000 !important;color:find-color-invert(#EA0000) !important}.is-status-danger{color:#ea0000 !important}.fill-status-danger{fill:#ea0000 !important}.has-bg-status-warning{background-color:#f4cb00 !important;color:find-color-invert(#F4CB00) !important}.is-status-warning{color:#f4cb00 !important}.fill-status-warning{fill:#f4cb00 !important}.has-bg-status-success{background-color:#15c046 !important;color:find-color-invert(#15C046) !important}.is-status-success{color:#15c046 !important}.fill-status-success{fill:#15c046 !important}.has-bg-status-info{background-color:#0066df !important;color:find-color-invert(#0066DF) !important}.is-status-info{color:#0066df !important}.fill-status-info{fill:#0066df !important}.has-bg-grey-100{background-color:#fff !important;color:find-color-invert(#FFFFFF) !important}.is-grey-100{color:#fff !important}.fill-grey-100{fill:#fff !important}.has-bg-grey-200{background-color:#f8f8f8 !important;color:find-color-invert(#F8F8F8) !important}.is-grey-200{color:#f8f8f8 !important}.fill-grey-200{fill:#f8f8f8 !important}.has-bg-grey-300{background-color:#f2f2f2 !important;color:find-color-invert(#F2F2F2) !important}.is-grey-300{color:#f2f2f2 !important}.fill-grey-300{fill:#f2f2f2 !important}.has-bg-grey-400{background-color:#e8e8e8 !important;color:find-color-invert(#E8E8E8) !important}.is-grey-400{color:#e8e8e8 !important}.fill-grey-400{fill:#e8e8e8 !important}.has-bg-grey-500{background-color:#d5d5d5 !important;color:find-color-invert(#D5D5D5) !important}.is-grey-500{color:#d5d5d5 !important}.fill-grey-500{fill:#d5d5d5 !important}.has-bg-grey-600{background-color:#9a9a9a !important;color:find-color-invert(#9A9A9A) !important}.is-grey-600{color:#9a9a9a !important}.fill-grey-600{fill:#9a9a9a !important}.has-bg-grey-700{background-color:#606060 !important;color:find-color-invert(#606060) !important}.is-grey-700{color:#606060 !important}.fill-grey-700{fill:#606060 !important}.has-bg-grey-800{background-color:#303030 !important;color:find-color-invert(#303030) !important}.is-grey-800{color:#303030 !important}.fill-grey-800{fill:#303030 !important}.has-bg-graph-red{background-color:#e21717 !important;color:find-color-invert(#E21717) !important}.is-graph-red{color:#e21717 !important}.fill-graph-red{fill:#e21717 !important}.has-bg-graph-orange{background-color:#f17603 !important;color:find-color-invert(#F17603) !important}.is-graph-orange{color:#f17603 !important}.fill-graph-orange{fill:#f17603 !important}.has-bg-graph-yellow{background-color:#fed800 !important;color:find-color-invert(#FED800) !important}.is-graph-yellow{color:#fed800 !important}.fill-graph-yellow{fill:#fed800 !important}.has-bg-graph-lime{background-color:#87d812 !important;color:find-color-invert(#87D812) !important}.is-graph-lime{color:#87d812 !important}.fill-graph-lime{fill:#87d812 !important}.has-bg-graph-green{background-color:#20c05b !important;color:find-color-invert(#20C05B) !important}.is-graph-green{color:#20c05b !important}.fill-graph-green{fill:#20c05b !important}.has-bg-graph-sky{background-color:#19c6f4 !important;color:find-color-invert(#19C6F4) !important}.is-graph-sky{color:#19c6f4 !important}.fill-graph-sky{fill:#19c6f4 !important}.has-bg-graph-blue{background-color:#0072df !important;color:find-color-invert(#0072DF) !important}.is-graph-blue{color:#0072df !important}.fill-graph-blue{fill:#0072df !important}.has-bg-graph-purple{background-color:#6e2ebf !important;color:find-color-invert(#6E2EBF) !important}.is-graph-purple{color:#6e2ebf !important}.fill-graph-purple{fill:#6e2ebf !important}.has-bg-graph-violet{background-color:#9a2ead !important;color:find-color-invert(#9A2EAD) !important}.is-graph-violet{color:#9a2ead !important}.fill-graph-violet{fill:#9a2ead !important}.has-bg-graph-pink{background-color:#ff3390 !important;color:find-color-invert(#FF3390) !important}.is-graph-pink{color:#ff3390 !important}.fill-graph-pink{fill:#ff3390 !important}.has-bg-red-100{background-color:#ffbdbd !important;color:find-color-invert(#FFBDBD) !important}.is-red-100{color:#ffbdbd !important}.fill-red-100{fill:#ffbdbd !important}.has-bg-red-200{background-color:#f89494 !important;color:find-color-invert(#F89494) !important}.is-red-200{color:#f89494 !important}.fill-red-200{fill:#f89494 !important}.has-bg-red-300{background-color:#f16a6a !important;color:find-color-invert(#F16A6A) !important}.is-red-300{color:#f16a6a !important}.fill-red-300{fill:#f16a6a !important}.has-bg-red-400{background-color:#e94141 !important;color:find-color-invert(#E94141) !important}.is-red-400{color:#e94141 !important}.fill-red-400{fill:#e94141 !important}.has-bg-red-500{background-color:#e21717 !important;color:find-color-invert(#E21717) !important}.is-red-500{color:#e21717 !important}.fill-red-500{fill:#e21717 !important}.has-bg-red-600{background-color:#d01111 !important;color:find-color-invert(#D01111) !important}.is-red-600{color:#d01111 !important}.fill-red-600{fill:#d01111 !important}.has-bg-red-700{background-color:#be0c0c !important;color:find-color-invert(#BE0C0C) !important}.is-red-700{color:#be0c0c !important}.fill-red-700{fill:#be0c0c !important}.has-bg-red-800{background-color:#ab0606 !important;color:find-color-invert(#AB0606) !important}.is-red-800{color:#ab0606 !important}.fill-red-800{fill:#ab0606 !important}.has-bg-red-900{background-color:#900 !important;color:find-color-invert(#990000) !important}.is-red-900{color:#900 !important}.fill-red-900{fill:#900 !important}.has-bg-red-r100{background-color:#ffbdbd !important;color:find-color-invert(#FFBDBD) !important}.is-red-r100{color:#ffbdbd !important}.fill-red-r100{fill:#ffbdbd !important}.has-bg-red-r200{background-color:#f16a6a !important;color:find-color-invert(#F16A6A) !important}.is-red-r200{color:#f16a6a !important}.fill-red-r200{fill:#f16a6a !important}.has-bg-red-r300{background-color:#e21717 !important;color:find-color-invert(#E21717) !important}.is-red-r300{color:#e21717 !important}.fill-red-r300{fill:#e21717 !important}.has-bg-red-r400{background-color:#be0c0c !important;color:find-color-invert(#BE0C0C) !important}.is-red-r400{color:#be0c0c !important}.fill-red-r400{fill:#be0c0c !important}.has-bg-red-r500{background-color:#900 !important;color:find-color-invert(#990000) !important}.is-red-r500{color:#900 !important}.fill-red-r500{fill:#900 !important}.has-bg-orange-100{background-color:#ffdcc0 !important;color:find-color-invert(#FFDCC0) !important}.is-orange-100{color:#ffdcc0 !important}.fill-orange-100{fill:#ffdcc0 !important}.has-bg-orange-200{background-color:#fcc391 !important;color:find-color-invert(#FCC391) !important}.is-orange-200{color:#fcc391 !important}.fill-orange-200{fill:#fcc391 !important}.has-bg-orange-300{background-color:#f8a962 !important;color:find-color-invert(#F8A962) !important}.is-orange-300{color:#f8a962 !important}.fill-orange-300{fill:#f8a962 !important}.has-bg-orange-400{background-color:#f49032 !important;color:find-color-invert(#F49032) !important}.is-orange-400{color:#f49032 !important}.fill-orange-400{fill:#f49032 !important}.has-bg-orange-500{background-color:#f17603 !important;color:find-color-invert(#F17603) !important}.is-orange-500{color:#f17603 !important}.fill-orange-500{fill:#f17603 !important}.has-bg-orange-600{background-color:#dc6a03 !important;color:find-color-invert(#DC6A03) !important}.is-orange-600{color:#dc6a03 !important}.fill-orange-600{fill:#dc6a03 !important}.has-bg-orange-700{background-color:#c85e03 !important;color:find-color-invert(#C85E03) !important}.is-orange-700{color:#c85e03 !important}.fill-orange-700{fill:#c85e03 !important}.has-bg-orange-800{background-color:#b35102 !important;color:find-color-invert(#B35102) !important}.is-orange-800{color:#b35102 !important}.fill-orange-800{fill:#b35102 !important}.has-bg-orange-900{background-color:#9e4502 !important;color:find-color-invert(#9E4502) !important}.is-orange-900{color:#9e4502 !important}.fill-orange-900{fill:#9e4502 !important}.has-bg-orange-r100{background-color:#ffdcc0 !important;color:find-color-invert(#FFDCC0) !important}.is-orange-r100{color:#ffdcc0 !important}.fill-orange-r100{fill:#ffdcc0 !important}.has-bg-orange-r200{background-color:#f8a962 !important;color:find-color-invert(#F8A962) !important}.is-orange-r200{color:#f8a962 !important}.fill-orange-r200{fill:#f8a962 !important}.has-bg-orange-r300{background-color:#f17603 !important;color:find-color-invert(#F17603) !important}.is-orange-r300{color:#f17603 !important}.fill-orange-r300{fill:#f17603 !important}.has-bg-orange-r400{background-color:#c85e03 !important;color:find-color-invert(#C85E03) !important}.is-orange-r400{color:#c85e03 !important}.fill-orange-r400{fill:#c85e03 !important}.has-bg-orange-r500{background-color:#9e4502 !important;color:find-color-invert(#9E4502) !important}.is-orange-r500{color:#9e4502 !important}.fill-orange-r500{fill:#9e4502 !important}.has-bg-yellow-100{background-color:#fffcd2 !important;color:find-color-invert(#FFFCD2) !important}.is-yellow-100{color:#fffcd2 !important}.fill-yellow-100{fill:#fffcd2 !important}.has-bg-yellow-200{background-color:#fff4a9 !important;color:find-color-invert(#FFF4A9) !important}.is-yellow-200{color:#fff4a9 !important}.fill-yellow-200{fill:#fff4a9 !important}.has-bg-yellow-300{background-color:#ffeb71 !important;color:find-color-invert(#FFEB71) !important}.is-yellow-300{color:#ffeb71 !important}.fill-yellow-300{fill:#ffeb71 !important}.has-bg-yellow-400{background-color:#fee344 !important;color:find-color-invert(#FEE344) !important}.is-yellow-400{color:#fee344 !important}.fill-yellow-400{fill:#fee344 !important}.has-bg-yellow-500{background-color:#fed800 !important;color:find-color-invert(#FED800) !important}.is-yellow-500{color:#fed800 !important}.fill-yellow-500{fill:#fed800 !important}.has-bg-yellow-600{background-color:#f7c500 !important;color:find-color-invert(#F7C500) !important}.is-yellow-600{color:#f7c500 !important}.fill-yellow-600{fill:#f7c500 !important}.has-bg-yellow-700{background-color:#f0b200 !important;color:find-color-invert(#F0B200) !important}.is-yellow-700{color:#f0b200 !important}.fill-yellow-700{fill:#f0b200 !important}.has-bg-yellow-800{background-color:#e99e00 !important;color:find-color-invert(#E99E00) !important}.is-yellow-800{color:#e99e00 !important}.fill-yellow-800{fill:#e99e00 !important}.has-bg-yellow-900{background-color:#e28b00 !important;color:find-color-invert(#E28B00) !important}.is-yellow-900{color:#e28b00 !important}.fill-yellow-900{fill:#e28b00 !important}.has-bg-yellow-r100{background-color:#fffcd2 !important;color:find-color-invert(#FFFCD2) !important}.is-yellow-r100{color:#fffcd2 !important}.fill-yellow-r100{fill:#fffcd2 !important}.has-bg-yellow-r200{background-color:#ffe86e !important;color:find-color-invert(#FFE86E) !important}.is-yellow-r200{color:#ffe86e !important}.fill-yellow-r200{fill:#ffe86e !important}.has-bg-yellow-r300{background-color:#fed800 !important;color:find-color-invert(#FED800) !important}.is-yellow-r300{color:#fed800 !important}.fill-yellow-r300{fill:#fed800 !important}.has-bg-yellow-r400{background-color:#f0b200 !important;color:find-color-invert(#F0B200) !important}.is-yellow-r400{color:#f0b200 !important}.fill-yellow-r400{fill:#f0b200 !important}.has-bg-yellow-r500{background-color:#e28b00 !important;color:find-color-invert(#E28B00) !important}.is-yellow-r500{color:#e28b00 !important}.fill-yellow-r500{fill:#e28b00 !important}.has-bg-lime-100{background-color:#e8ffc0 !important;color:find-color-invert(#E8FFC0) !important}.is-lime-100{color:#e8ffc0 !important}.fill-lime-100{fill:#e8ffc0 !important}.has-bg-lime-200{background-color:#d0f595 !important;color:find-color-invert(#D0F595) !important}.is-lime-200{color:#d0f595 !important}.fill-lime-200{fill:#d0f595 !important}.has-bg-lime-300{background-color:#b8ec69 !important;color:find-color-invert(#B8EC69) !important}.is-lime-300{color:#b8ec69 !important}.fill-lime-300{fill:#b8ec69 !important}.has-bg-lime-400{background-color:#9fe23e !important;color:find-color-invert(#9FE23E) !important}.is-lime-400{color:#9fe23e !important}.fill-lime-400{fill:#9fe23e !important}.has-bg-lime-500{background-color:#87d812 !important;color:find-color-invert(#87D812) !important}.is-lime-500{color:#87d812 !important}.fill-lime-500{fill:#87d812 !important}.has-bg-lime-600{background-color:#7bc210 !important;color:find-color-invert(#7BC210) !important}.is-lime-600{color:#7bc210 !important}.fill-lime-600{fill:#7bc210 !important}.has-bg-lime-700{background-color:#6eac0f !important;color:find-color-invert(#6EAC0F) !important}.is-lime-700{color:#6eac0f !important}.fill-lime-700{fill:#6eac0f !important}.has-bg-lime-800{background-color:#62950d !important;color:find-color-invert(#62950D) !important}.is-lime-800{color:#62950d !important}.fill-lime-800{fill:#62950d !important}.has-bg-lime-900{background-color:#557f0b !important;color:find-color-invert(#557F0B) !important}.is-lime-900{color:#557f0b !important}.fill-lime-900{fill:#557f0b !important}.has-bg-lime-r100{background-color:#e8ffc0 !important;color:find-color-invert(#E8FFC0) !important}.is-lime-r100{color:#e8ffc0 !important}.fill-lime-r100{fill:#e8ffc0 !important}.has-bg-lime-r200{background-color:#b8ec69 !important;color:find-color-invert(#B8EC69) !important}.is-lime-r200{color:#b8ec69 !important}.fill-lime-r200{fill:#b8ec69 !important}.has-bg-lime-r300{background-color:#87d812 !important;color:find-color-invert(#87D812) !important}.is-lime-r300{color:#87d812 !important}.fill-lime-r300{fill:#87d812 !important}.has-bg-lime-r400{background-color:#6eac0f !important;color:find-color-invert(#6EAC0F) !important}.is-lime-r400{color:#6eac0f !important}.fill-lime-r400{fill:#6eac0f !important}.has-bg-lime-r500{background-color:#557f0b !important;color:find-color-invert(#557F0B) !important}.is-lime-r500{color:#557f0b !important}.fill-lime-r500{fill:#557f0b !important}.has-bg-green-100{background-color:#d3ffd2 !important;color:find-color-invert(#D3FFD2) !important}.is-green-100{color:#d3ffd2 !important}.fill-green-100{fill:#d3ffd2 !important}.has-bg-green-200{background-color:#a6efb4 !important;color:find-color-invert(#A6EFB4) !important}.is-green-200{color:#a6efb4 !important}.fill-green-200{fill:#a6efb4 !important}.has-bg-green-300{background-color:#7ae097 !important;color:find-color-invert(#7AE097) !important}.is-green-300{color:#7ae097 !important}.fill-green-300{fill:#7ae097 !important}.has-bg-green-400{background-color:#4dd079 !important;color:find-color-invert(#4DD079) !important}.is-green-400{color:#4dd079 !important}.fill-green-400{fill:#4dd079 !important}.has-bg-green-500{background-color:#20c05b !important;color:find-color-invert(#20C05B) !important}.is-green-500{color:#20c05b !important}.fill-green-500{fill:#20c05b !important}.has-bg-green-600{background-color:#1dad51 !important;color:find-color-invert(#1DAD51) !important}.is-green-600{color:#1dad51 !important}.fill-green-600{fill:#1dad51 !important}.has-bg-green-700{background-color:#1a9947 !important;color:find-color-invert(#1A9947) !important}.is-green-700{color:#1a9947 !important}.fill-green-700{fill:#1a9947 !important}.has-bg-green-800{background-color:#17863d !important;color:find-color-invert(#17863D) !important}.is-green-800{color:#17863d !important}.fill-green-800{fill:#17863d !important}.has-bg-green-900{background-color:#147233 !important;color:find-color-invert(#147233) !important}.is-green-900{color:#147233 !important}.fill-green-900{fill:#147233 !important}.has-bg-green-r100{background-color:#d3ffd2 !important;color:find-color-invert(#D3FFD2) !important}.is-green-r100{color:#d3ffd2 !important}.fill-green-r100{fill:#d3ffd2 !important}.has-bg-green-r200{background-color:#7ae097 !important;color:find-color-invert(#7AE097) !important}.is-green-r200{color:#7ae097 !important}.fill-green-r200{fill:#7ae097 !important}.has-bg-green-r300{background-color:#20c05b !important;color:find-color-invert(#20C05B) !important}.is-green-r300{color:#20c05b !important}.fill-green-r300{fill:#20c05b !important}.has-bg-green-r400{background-color:#1a9947 !important;color:find-color-invert(#1A9947) !important}.is-green-r400{color:#1a9947 !important}.fill-green-r400{fill:#1a9947 !important}.has-bg-green-r500{background-color:#147233 !important;color:find-color-invert(#147233) !important}.is-green-r500{color:#147233 !important}.fill-green-r500{fill:#147233 !important}.has-bg-sky-100{background-color:#bef0fc !important;color:find-color-invert(#BEF0FC) !important}.is-sky-100{color:#bef0fc !important}.fill-sky-100{fill:#bef0fc !important}.has-bg-sky-200{background-color:#96e8fc !important;color:find-color-invert(#96E8FC) !important}.is-sky-200{color:#96e8fc !important}.fill-sky-200{fill:#96e8fc !important}.has-bg-sky-300{background-color:#6dddfa !important;color:find-color-invert(#6DDDFA) !important}.is-sky-300{color:#6dddfa !important}.fill-sky-300{fill:#6dddfa !important}.has-bg-sky-400{background-color:#43d1f7 !important;color:find-color-invert(#43D1F7) !important}.is-sky-400{color:#43d1f7 !important}.fill-sky-400{fill:#43d1f7 !important}.has-bg-sky-500{background-color:#19c6f4 !important;color:find-color-invert(#19C6F4) !important}.is-sky-500{color:#19c6f4 !important}.fill-sky-500{fill:#19c6f4 !important}.has-bg-sky-600{background-color:#17afd4 !important;color:find-color-invert(#17AFD4) !important}.is-sky-600{color:#17afd4 !important}.fill-sky-600{fill:#17afd4 !important}.has-bg-sky-700{background-color:#1499b5 !important;color:find-color-invert(#1499B5) !important}.is-sky-700{color:#1499b5 !important}.fill-sky-700{fill:#1499b5 !important}.has-bg-sky-800{background-color:#128295 !important;color:find-color-invert(#128295) !important}.is-sky-800{color:#128295 !important}.fill-sky-800{fill:#128295 !important}.has-bg-sky-900{background-color:#106b75 !important;color:find-color-invert(#106B75) !important}.is-sky-900{color:#106b75 !important}.fill-sky-900{fill:#106b75 !important}.has-bg-sky-r100{background-color:#c0f3ff !important;color:find-color-invert(#C0F3FF) !important}.is-sky-r100{color:#c0f3ff !important}.fill-sky-r100{fill:#c0f3ff !important}.has-bg-sky-r200{background-color:#6dddfa !important;color:find-color-invert(#6DDDFA) !important}.is-sky-r200{color:#6dddfa !important}.fill-sky-r200{fill:#6dddfa !important}.has-bg-sky-r300{background-color:#19c6f4 !important;color:find-color-invert(#19C6F4) !important}.is-sky-r300{color:#19c6f4 !important}.fill-sky-r300{fill:#19c6f4 !important}.has-bg-sky-r400{background-color:#1499b5 !important;color:find-color-invert(#1499B5) !important}.is-sky-r400{color:#1499b5 !important}.fill-sky-r400{fill:#1499b5 !important}.has-bg-sky-r500{background-color:#106b75 !important;color:find-color-invert(#106B75) !important}.is-sky-r500{color:#106b75 !important}.fill-sky-r500{fill:#106b75 !important}.has-bg-blue-100{background-color:#c0e3ff !important;color:find-color-invert(#C0E3FF) !important}.is-blue-100{color:#c0e3ff !important}.fill-blue-100{fill:#c0e3ff !important}.has-bg-blue-200{background-color:#90c7f7 !important;color:find-color-invert(#90C7F7) !important}.is-blue-200{color:#90c7f7 !important}.fill-blue-200{fill:#90c7f7 !important}.has-bg-blue-300{background-color:#60abef !important;color:find-color-invert(#60ABEF) !important}.is-blue-300{color:#60abef !important}.fill-blue-300{fill:#60abef !important}.has-bg-blue-400{background-color:#308ee7 !important;color:find-color-invert(#308EE7) !important}.is-blue-400{color:#308ee7 !important}.fill-blue-400{fill:#308ee7 !important}.has-bg-blue-500{background-color:#0072df !important;color:find-color-invert(#0072DF) !important}.is-blue-500{color:#0072df !important}.fill-blue-500{fill:#0072df !important}.has-bg-blue-600{background-color:#0065bf !important;color:find-color-invert(#0065BF) !important}.is-blue-600{color:#0065bf !important}.fill-blue-600{fill:#0065bf !important}.has-bg-blue-700{background-color:#0058a0 !important;color:find-color-invert(#0058A0) !important}.is-blue-700{color:#0058a0 !important}.fill-blue-700{fill:#0058a0 !important}.has-bg-blue-800{background-color:#004b80 !important;color:find-color-invert(#004B80) !important}.is-blue-800{color:#004b80 !important}.fill-blue-800{fill:#004b80 !important}.has-bg-blue-900{background-color:#003e60 !important;color:find-color-invert(#003E60) !important}.is-blue-900{color:#003e60 !important}.fill-blue-900{fill:#003e60 !important}.has-bg-blue-r100{background-color:#c0e3ff !important;color:find-color-invert(#C0E3FF) !important}.is-blue-r100{color:#c0e3ff !important}.fill-blue-r100{fill:#c0e3ff !important}.has-bg-blue-r200{background-color:#60abef !important;color:find-color-invert(#60ABEF) !important}.is-blue-r200{color:#60abef !important}.fill-blue-r200{fill:#60abef !important}.has-bg-blue-r300{background-color:#0072df !important;color:find-color-invert(#0072DF) !important}.is-blue-r300{color:#0072df !important}.fill-blue-r300{fill:#0072df !important}.has-bg-blue-r400{background-color:#0058a0 !important;color:find-color-invert(#0058A0) !important}.is-blue-r400{color:#0058a0 !important}.fill-blue-r400{fill:#0058a0 !important}.has-bg-blue-r500{background-color:#003e60 !important;color:find-color-invert(#003E60) !important}.is-blue-r500{color:#003e60 !important}.fill-blue-r500{fill:#003e60 !important}.has-bg-purple-100{background-color:#dbd0fd !important;color:find-color-invert(#DBD0FD) !important}.is-purple-100{color:#dbd0fd !important}.fill-purple-100{fill:#dbd0fd !important}.has-bg-purple-200{background-color:#c8b6ff !important;color:find-color-invert(#C8B6FF) !important}.is-purple-200{color:#c8b6ff !important}.fill-purple-200{fill:#c8b6ff !important}.has-bg-purple-300{background-color:#aa89ea !important;color:find-color-invert(#AA89EA) !important}.is-purple-300{color:#aa89ea !important}.fill-purple-300{fill:#aa89ea !important}.has-bg-purple-400{background-color:#8c5bd4 !important;color:find-color-invert(#8C5BD4) !important}.is-purple-400{color:#8c5bd4 !important}.fill-purple-400{fill:#8c5bd4 !important}.has-bg-purple-500{background-color:#6e2ebf !important;color:find-color-invert(#6E2EBF) !important}.is-purple-500{color:#6e2ebf !important}.fill-purple-500{fill:#6e2ebf !important}.has-bg-purple-600{background-color:#5413a8 !important;color:find-color-invert(#5413A8) !important}.is-purple-600{color:#5413a8 !important}.fill-purple-600{fill:#5413a8 !important}.has-bg-purple-700{background-color:#40008b !important;color:find-color-invert(#40008B) !important}.is-purple-700{color:#40008b !important}.fill-purple-700{fill:#40008b !important}.has-bg-purple-800{background-color:#30006d !important;color:find-color-invert(#30006D) !important}.is-purple-800{color:#30006d !important}.fill-purple-800{fill:#30006d !important}.has-bg-purple-900{background-color:#20004e !important;color:find-color-invert(#20004E) !important}.is-purple-900{color:#20004e !important}.fill-purple-900{fill:#20004e !important}.has-bg-purple-r100{background-color:#dbd0fd !important;color:find-color-invert(#DBD0FD) !important}.is-purple-r100{color:#dbd0fd !important}.fill-purple-r100{fill:#dbd0fd !important}.has-bg-purple-r200{background-color:#aa89ea !important;color:find-color-invert(#AA89EA) !important}.is-purple-r200{color:#aa89ea !important}.fill-purple-r200{fill:#aa89ea !important}.has-bg-purple-r300{background-color:#6e2ebf !important;color:find-color-invert(#6E2EBF) !important}.is-purple-r300{color:#6e2ebf !important}.fill-purple-r300{fill:#6e2ebf !important}.has-bg-purple-r400{background-color:#40008b !important;color:find-color-invert(#40008B) !important}.is-purple-r400{color:#40008b !important}.fill-purple-r400{fill:#40008b !important}.has-bg-purple-r500{background-color:#20004e !important;color:find-color-invert(#20004E) !important}.is-purple-r500{color:#20004e !important}.fill-purple-r500{fill:#20004e !important}.has-bg-violet-100{background-color:#e9b7f3 !important;color:find-color-invert(#E9B7F3) !important}.is-violet-100{color:#e9b7f3 !important}.fill-violet-100{fill:#e9b7f3 !important}.has-bg-violet-200{background-color:#de9beb !important;color:find-color-invert(#DE9BEB) !important}.is-violet-200{color:#de9beb !important}.fill-violet-200{fill:#de9beb !important}.has-bg-violet-300{background-color:#c777d6 !important;color:find-color-invert(#C777D6) !important}.is-violet-300{color:#c777d6 !important}.fill-violet-300{fill:#c777d6 !important}.has-bg-violet-400{background-color:#b152c2 !important;color:find-color-invert(#B152C2) !important}.is-violet-400{color:#b152c2 !important}.fill-violet-400{fill:#b152c2 !important}.has-bg-violet-500{background-color:#9a2ead !important;color:find-color-invert(#9A2EAD) !important}.is-violet-500{color:#9a2ead !important}.fill-violet-500{fill:#9a2ead !important}.has-bg-violet-600{background-color:#842895 !important;color:find-color-invert(#842895) !important}.is-violet-600{color:#842895 !important}.fill-violet-600{fill:#842895 !important}.has-bg-violet-700{background-color:#6e227d !important;color:find-color-invert(#6E227D) !important}.is-violet-700{color:#6e227d !important}.fill-violet-700{fill:#6e227d !important}.has-bg-violet-800{background-color:#571b64 !important;color:find-color-invert(#571B64) !important}.is-violet-800{color:#571b64 !important}.fill-violet-800{fill:#571b64 !important}.has-bg-violet-900{background-color:#41154c !important;color:find-color-invert(#41154C) !important}.is-violet-900{color:#41154c !important}.fill-violet-900{fill:#41154c !important}.has-bg-violet-r100{background-color:#e9b7f3 !important;color:find-color-invert(#E9B7F3) !important}.is-violet-r100{color:#e9b7f3 !important}.fill-violet-r100{fill:#e9b7f3 !important}.has-bg-violet-r200{background-color:#c777d6 !important;color:find-color-invert(#C777D6) !important}.is-violet-r200{color:#c777d6 !important}.fill-violet-r200{fill:#c777d6 !important}.has-bg-violet-r300{background-color:#9a2ead !important;color:find-color-invert(#9A2EAD) !important}.is-violet-r300{color:#9a2ead !important}.fill-violet-r300{fill:#9a2ead !important}.has-bg-violet-r400{background-color:#6e227d !important;color:find-color-invert(#6E227D) !important}.is-violet-r400{color:#6e227d !important}.fill-violet-r400{fill:#6e227d !important}.has-bg-violet-r500{background-color:#41154c !important;color:find-color-invert(#41154C) !important}.is-violet-r500{color:#41154c !important}.fill-violet-r500{fill:#41154c !important}.has-bg-pink-100{background-color:#fabcdc !important;color:find-color-invert(#FABCDC) !important}.is-pink-100{color:#fabcdc !important}.fill-pink-100{fill:#fabcdc !important}.has-bg-pink-200{background-color:#ff9dcc !important;color:find-color-invert(#FF9DCC) !important}.is-pink-200{color:#ff9dcc !important}.fill-pink-200{fill:#ff9dcc !important}.has-bg-pink-300{background-color:#ff7ab8 !important;color:find-color-invert(#FF7AB8) !important}.is-pink-300{color:#ff7ab8 !important}.fill-pink-300{fill:#ff7ab8 !important}.has-bg-pink-400{background-color:#ff56a4 !important;color:find-color-invert(#FF56A4) !important}.is-pink-400{color:#ff56a4 !important}.fill-pink-400{fill:#ff56a4 !important}.has-bg-pink-500{background-color:#ff3390 !important;color:find-color-invert(#FF3390) !important}.is-pink-500{color:#ff3390 !important}.fill-pink-500{fill:#ff3390 !important}.has-bg-pink-600{background-color:#df2d82 !important;color:find-color-invert(#DF2D82) !important}.is-pink-600{color:#df2d82 !important}.fill-pink-600{fill:#df2d82 !important}.has-bg-pink-700{background-color:#bf2874 !important;color:find-color-invert(#BF2874) !important}.is-pink-700{color:#bf2874 !important}.fill-pink-700{fill:#bf2874 !important}.has-bg-pink-800{background-color:#9f2265 !important;color:find-color-invert(#9F2265) !important}.is-pink-800{color:#9f2265 !important}.fill-pink-800{fill:#9f2265 !important}.has-bg-pink-900{background-color:#7f1c57 !important;color:find-color-invert(#7F1C57) !important}.is-pink-900{color:#7f1c57 !important}.fill-pink-900{fill:#7f1c57 !important}.has-bg-pink-r100{background-color:#fabcdc !important;color:find-color-invert(#FABCDC) !important}.is-pink-r100{color:#fabcdc !important}.fill-pink-r100{fill:#fabcdc !important}.has-bg-pink-r200{background-color:#ff7ab8 !important;color:find-color-invert(#FF7AB8) !important}.is-pink-r200{color:#ff7ab8 !important}.fill-pink-r200{fill:#ff7ab8 !important}.has-bg-pink-r300{background-color:#ff3390 !important;color:find-color-invert(#FF3390) !important}.is-pink-r300{color:#ff3390 !important}.fill-pink-r300{fill:#ff3390 !important}.has-bg-pink-r400{background-color:#bf2874 !important;color:find-color-invert(#BF2874) !important}.is-pink-r400{color:#bf2874 !important}.fill-pink-r400{fill:#bf2874 !important}.has-bg-pink-r500{background-color:#7f1c57 !important;color:find-color-invert(#7F1C57) !important}.is-pink-r500{color:#7f1c57 !important}.fill-pink-r500{fill:#7f1c57 !important}.m-0{margin:0px !important}.m-1{margin:1px !important}.m-2{margin:2px !important}.m-3{margin:3px !important}.m-4{margin:4px !important}.m-5{margin:5px !important}.m-6{margin:6px !important}.m-7{margin:7px !important}.m-8{margin:8px !important}.m-9{margin:9px !important}.m-10{margin:10px !important}.m-11{margin:11px !important}.m-12{margin:12px !important}.m-13{margin:13px !important}.m-14{margin:14px !important}.m-15{margin:15px !important}.m-16{margin:16px !important}.m-17{margin:17px !important}.m-18{margin:18px !important}.m-19{margin:19px !important}.m-20{margin:20px !important}.m-21{margin:21px !important}.m-22{margin:22px !important}.m-23{margin:23px !important}.m-24{margin:24px !important}.m-25{margin:25px !important}.m-26{margin:26px !important}.m-27{margin:27px !important}.m-28{margin:28px !important}.m-29{margin:29px !important}.m-30{margin:30px !important}.m-31{margin:31px !important}.m-32{margin:32px !important}.m-33{margin:33px !important}.m-34{margin:34px !important}.m-35{margin:35px !important}.m-36{margin:36px !important}.m-37{margin:37px !important}.m-38{margin:38px !important}.m-39{margin:39px !important}.m-40{margin:40px !important}.m-41{margin:41px !important}.m-42{margin:42px !important}.m-43{margin:43px !important}.m-44{margin:44px !important}.m-45{margin:45px !important}.m-46{margin:46px !important}.m-47{margin:47px !important}.m-48{margin:48px !important}.m-49{margin:49px !important}.m-50{margin:50px !important}.m-51{margin:51px !important}.m-52{margin:52px !important}.m-53{margin:53px !important}.m-54{margin:54px !important}.m-55{margin:55px !important}.m-56{margin:56px !important}.m-57{margin:57px !important}.m-58{margin:58px !important}.m-59{margin:59px !important}.m-60{margin:60px !important}.m-61{margin:61px !important}.m-62{margin:62px !important}.m-63{margin:63px !important}.m-64{margin:64px !important}.m-65{margin:65px !important}.m-66{margin:66px !important}.m-67{margin:67px !important}.m-68{margin:68px !important}.m-69{margin:69px !important}.m-70{margin:70px !important}.m-71{margin:71px !important}.m-72{margin:72px !important}.m-73{margin:73px !important}.m-74{margin:74px !important}.m-75{margin:75px !important}.m-76{margin:76px !important}.m-77{margin:77px !important}.m-78{margin:78px !important}.m-79{margin:79px !important}.m-80{margin:80px !important}.m-81{margin:81px !important}.m-82{margin:82px !important}.m-83{margin:83px !important}.m-84{margin:84px !important}.m-85{margin:85px !important}.m-86{margin:86px !important}.m-87{margin:87px !important}.m-88{margin:88px !important}.m-89{margin:89px !important}.m-90{margin:90px !important}.m-91{margin:91px !important}.m-92{margin:92px !important}.m-93{margin:93px !important}.m-94{margin:94px !important}.m-95{margin:95px !important}.m-96{margin:96px !important}.m-97{margin:97px !important}.m-98{margin:98px !important}.m-99{margin:99px !important}.m-100{margin:100px !important}.m-auto{margin:auto !important}.m-l-auto{margin-left:auto !important}.m-r-auto{margin-right:auto !important}.m-t-auto{margin-top:auto !important}.m-b-auto{margin-bottom:auto !important}.m-x-auto{margin-left:auto !important;margin-right:auto !important}.m-y-auto{margin-top:auto !important;margin-bottom:auto !important}.m-t-0{margin-top:0px !important}.m-t-1{margin-top:1px !important}.m-t-2{margin-top:2px !important}.m-t-3{margin-top:3px !important}.m-t-4{margin-top:4px !important}.m-t-5{margin-top:5px !important}.m-t-6{margin-top:6px !important}.m-t-7{margin-top:7px !important}.m-t-8{margin-top:8px !important}.m-t-9{margin-top:9px !important}.m-t-10{margin-top:10px !important}.m-t-11{margin-top:11px !important}.m-t-12{margin-top:12px !important}.m-t-13{margin-top:13px !important}.m-t-14{margin-top:14px !important}.m-t-15{margin-top:15px !important}.m-t-16{margin-top:16px !important}.m-t-17{margin-top:17px !important}.m-t-18{margin-top:18px !important}.m-t-19{margin-top:19px !important}.m-t-20{margin-top:20px !important}.m-t-21{margin-top:21px !important}.m-t-22{margin-top:22px !important}.m-t-23{margin-top:23px !important}.m-t-24{margin-top:24px !important}.m-t-25{margin-top:25px !important}.m-t-26{margin-top:26px !important}.m-t-27{margin-top:27px !important}.m-t-28{margin-top:28px !important}.m-t-29{margin-top:29px !important}.m-t-30{margin-top:30px !important}.m-t-31{margin-top:31px !important}.m-t-32{margin-top:32px !important}.m-t-33{margin-top:33px !important}.m-t-34{margin-top:34px !important}.m-t-35{margin-top:35px !important}.m-t-36{margin-top:36px !important}.m-t-37{margin-top:37px !important}.m-t-38{margin-top:38px !important}.m-t-39{margin-top:39px !important}.m-t-40{margin-top:40px !important}.m-t-41{margin-top:41px !important}.m-t-42{margin-top:42px !important}.m-t-43{margin-top:43px !important}.m-t-44{margin-top:44px !important}.m-t-45{margin-top:45px !important}.m-t-46{margin-top:46px !important}.m-t-47{margin-top:47px !important}.m-t-48{margin-top:48px !important}.m-t-49{margin-top:49px !important}.m-t-50{margin-top:50px !important}.m-t-51{margin-top:51px !important}.m-t-52{margin-top:52px !important}.m-t-53{margin-top:53px !important}.m-t-54{margin-top:54px !important}.m-t-55{margin-top:55px !important}.m-t-56{margin-top:56px !important}.m-t-57{margin-top:57px !important}.m-t-58{margin-top:58px !important}.m-t-59{margin-top:59px !important}.m-t-60{margin-top:60px !important}.m-t-61{margin-top:61px !important}.m-t-62{margin-top:62px !important}.m-t-63{margin-top:63px !important}.m-t-64{margin-top:64px !important}.m-t-65{margin-top:65px !important}.m-t-66{margin-top:66px !important}.m-t-67{margin-top:67px !important}.m-t-68{margin-top:68px !important}.m-t-69{margin-top:69px !important}.m-t-70{margin-top:70px !important}.m-t-71{margin-top:71px !important}.m-t-72{margin-top:72px !important}.m-t-73{margin-top:73px !important}.m-t-74{margin-top:74px !important}.m-t-75{margin-top:75px !important}.m-t-76{margin-top:76px !important}.m-t-77{margin-top:77px !important}.m-t-78{margin-top:78px !important}.m-t-79{margin-top:79px !important}.m-t-80{margin-top:80px !important}.m-t-81{margin-top:81px !important}.m-t-82{margin-top:82px !important}.m-t-83{margin-top:83px !important}.m-t-84{margin-top:84px !important}.m-t-85{margin-top:85px !important}.m-t-86{margin-top:86px !important}.m-t-87{margin-top:87px !important}.m-t-88{margin-top:88px !important}.m-t-89{margin-top:89px !important}.m-t-90{margin-top:90px !important}.m-t-91{margin-top:91px !important}.m-t-92{margin-top:92px !important}.m-t-93{margin-top:93px !important}.m-t-94{margin-top:94px !important}.m-t-95{margin-top:95px !important}.m-t-96{margin-top:96px !important}.m-t-97{margin-top:97px !important}.m-t-98{margin-top:98px !important}.m-t-99{margin-top:99px !important}.m-t-100{margin-top:100px !important}.m-r-0{margin-right:0px !important}.m-r-1{margin-right:1px !important}.m-r-2{margin-right:2px !important}.m-r-3{margin-right:3px !important}.m-r-4{margin-right:4px !important}.m-r-5{margin-right:5px !important}.m-r-6{margin-right:6px !important}.m-r-7{margin-right:7px !important}.m-r-8{margin-right:8px !important}.m-r-9{margin-right:9px !important}.m-r-10{margin-right:10px !important}.m-r-11{margin-right:11px !important}.m-r-12{margin-right:12px !important}.m-r-13{margin-right:13px !important}.m-r-14{margin-right:14px !important}.m-r-15{margin-right:15px !important}.m-r-16{margin-right:16px !important}.m-r-17{margin-right:17px !important}.m-r-18{margin-right:18px !important}.m-r-19{margin-right:19px !important}.m-r-20{margin-right:20px !important}.m-r-21{margin-right:21px !important}.m-r-22{margin-right:22px !important}.m-r-23{margin-right:23px !important}.m-r-24{margin-right:24px !important}.m-r-25{margin-right:25px !important}.m-r-26{margin-right:26px !important}.m-r-27{margin-right:27px !important}.m-r-28{margin-right:28px !important}.m-r-29{margin-right:29px !important}.m-r-30{margin-right:30px !important}.m-r-31{margin-right:31px !important}.m-r-32{margin-right:32px !important}.m-r-33{margin-right:33px !important}.m-r-34{margin-right:34px !important}.m-r-35{margin-right:35px !important}.m-r-36{margin-right:36px !important}.m-r-37{margin-right:37px !important}.m-r-38{margin-right:38px !important}.m-r-39{margin-right:39px !important}.m-r-40{margin-right:40px !important}.m-r-41{margin-right:41px !important}.m-r-42{margin-right:42px !important}.m-r-43{margin-right:43px !important}.m-r-44{margin-right:44px !important}.m-r-45{margin-right:45px !important}.m-r-46{margin-right:46px !important}.m-r-47{margin-right:47px !important}.m-r-48{margin-right:48px !important}.m-r-49{margin-right:49px !important}.m-r-50{margin-right:50px !important}.m-r-51{margin-right:51px !important}.m-r-52{margin-right:52px !important}.m-r-53{margin-right:53px !important}.m-r-54{margin-right:54px !important}.m-r-55{margin-right:55px !important}.m-r-56{margin-right:56px !important}.m-r-57{margin-right:57px !important}.m-r-58{margin-right:58px !important}.m-r-59{margin-right:59px !important}.m-r-60{margin-right:60px !important}.m-r-61{margin-right:61px !important}.m-r-62{margin-right:62px !important}.m-r-63{margin-right:63px !important}.m-r-64{margin-right:64px !important}.m-r-65{margin-right:65px !important}.m-r-66{margin-right:66px !important}.m-r-67{margin-right:67px !important}.m-r-68{margin-right:68px !important}.m-r-69{margin-right:69px !important}.m-r-70{margin-right:70px !important}.m-r-71{margin-right:71px !important}.m-r-72{margin-right:72px !important}.m-r-73{margin-right:73px !important}.m-r-74{margin-right:74px !important}.m-r-75{margin-right:75px !important}.m-r-76{margin-right:76px !important}.m-r-77{margin-right:77px !important}.m-r-78{margin-right:78px !important}.m-r-79{margin-right:79px !important}.m-r-80{margin-right:80px !important}.m-r-81{margin-right:81px !important}.m-r-82{margin-right:82px !important}.m-r-83{margin-right:83px !important}.m-r-84{margin-right:84px !important}.m-r-85{margin-right:85px !important}.m-r-86{margin-right:86px !important}.m-r-87{margin-right:87px !important}.m-r-88{margin-right:88px !important}.m-r-89{margin-right:89px !important}.m-r-90{margin-right:90px !important}.m-r-91{margin-right:91px !important}.m-r-92{margin-right:92px !important}.m-r-93{margin-right:93px !important}.m-r-94{margin-right:94px !important}.m-r-95{margin-right:95px !important}.m-r-96{margin-right:96px !important}.m-r-97{margin-right:97px !important}.m-r-98{margin-right:98px !important}.m-r-99{margin-right:99px !important}.m-r-100{margin-right:100px !important}.m-b-0{margin-bottom:0px !important}.m-b-1{margin-bottom:1px !important}.m-b-2{margin-bottom:2px !important}.m-b-3{margin-bottom:3px !important}.m-b-4{margin-bottom:4px !important}.m-b-5{margin-bottom:5px !important}.m-b-6{margin-bottom:6px !important}.m-b-7{margin-bottom:7px !important}.m-b-8{margin-bottom:8px !important}.m-b-9{margin-bottom:9px !important}.m-b-10{margin-bottom:10px !important}.m-b-11{margin-bottom:11px !important}.m-b-12{margin-bottom:12px !important}.m-b-13{margin-bottom:13px !important}.m-b-14{margin-bottom:14px !important}.m-b-15{margin-bottom:15px !important}.m-b-16{margin-bottom:16px !important}.m-b-17{margin-bottom:17px !important}.m-b-18{margin-bottom:18px !important}.m-b-19{margin-bottom:19px !important}.m-b-20{margin-bottom:20px !important}.m-b-21{margin-bottom:21px !important}.m-b-22{margin-bottom:22px !important}.m-b-23{margin-bottom:23px !important}.m-b-24{margin-bottom:24px !important}.m-b-25{margin-bottom:25px !important}.m-b-26{margin-bottom:26px !important}.m-b-27{margin-bottom:27px !important}.m-b-28{margin-bottom:28px !important}.m-b-29{margin-bottom:29px !important}.m-b-30{margin-bottom:30px !important}.m-b-31{margin-bottom:31px !important}.m-b-32{margin-bottom:32px !important}.m-b-33{margin-bottom:33px !important}.m-b-34{margin-bottom:34px !important}.m-b-35{margin-bottom:35px !important}.m-b-36{margin-bottom:36px !important}.m-b-37{margin-bottom:37px !important}.m-b-38{margin-bottom:38px !important}.m-b-39{margin-bottom:39px !important}.m-b-40{margin-bottom:40px !important}.m-b-41{margin-bottom:41px !important}.m-b-42{margin-bottom:42px !important}.m-b-43{margin-bottom:43px !important}.m-b-44{margin-bottom:44px !important}.m-b-45{margin-bottom:45px !important}.m-b-46{margin-bottom:46px !important}.m-b-47{margin-bottom:47px !important}.m-b-48{margin-bottom:48px !important}.m-b-49{margin-bottom:49px !important}.m-b-50{margin-bottom:50px !important}.m-b-51{margin-bottom:51px !important}.m-b-52{margin-bottom:52px !important}.m-b-53{margin-bottom:53px !important}.m-b-54{margin-bottom:54px !important}.m-b-55{margin-bottom:55px !important}.m-b-56{margin-bottom:56px !important}.m-b-57{margin-bottom:57px !important}.m-b-58{margin-bottom:58px !important}.m-b-59{margin-bottom:59px !important}.m-b-60{margin-bottom:60px !important}.m-b-61{margin-bottom:61px !important}.m-b-62{margin-bottom:62px !important}.m-b-63{margin-bottom:63px !important}.m-b-64{margin-bottom:64px !important}.m-b-65{margin-bottom:65px !important}.m-b-66{margin-bottom:66px !important}.m-b-67{margin-bottom:67px !important}.m-b-68{margin-bottom:68px !important}.m-b-69{margin-bottom:69px !important}.m-b-70{margin-bottom:70px !important}.m-b-71{margin-bottom:71px !important}.m-b-72{margin-bottom:72px !important}.m-b-73{margin-bottom:73px !important}.m-b-74{margin-bottom:74px !important}.m-b-75{margin-bottom:75px !important}.m-b-76{margin-bottom:76px !important}.m-b-77{margin-bottom:77px !important}.m-b-78{margin-bottom:78px !important}.m-b-79{margin-bottom:79px !important}.m-b-80{margin-bottom:80px !important}.m-b-81{margin-bottom:81px !important}.m-b-82{margin-bottom:82px !important}.m-b-83{margin-bottom:83px !important}.m-b-84{margin-bottom:84px !important}.m-b-85{margin-bottom:85px !important}.m-b-86{margin-bottom:86px !important}.m-b-87{margin-bottom:87px !important}.m-b-88{margin-bottom:88px !important}.m-b-89{margin-bottom:89px !important}.m-b-90{margin-bottom:90px !important}.m-b-91{margin-bottom:91px !important}.m-b-92{margin-bottom:92px !important}.m-b-93{margin-bottom:93px !important}.m-b-94{margin-bottom:94px !important}.m-b-95{margin-bottom:95px !important}.m-b-96{margin-bottom:96px !important}.m-b-97{margin-bottom:97px !important}.m-b-98{margin-bottom:98px !important}.m-b-99{margin-bottom:99px !important}.m-b-100{margin-bottom:100px !important}.m-l-0{margin-left:0px !important}.m-l-1{margin-left:1px !important}.m-l-2{margin-left:2px !important}.m-l-3{margin-left:3px !important}.m-l-4{margin-left:4px !important}.m-l-5{margin-left:5px !important}.m-l-6{margin-left:6px !important}.m-l-7{margin-left:7px !important}.m-l-8{margin-left:8px !important}.m-l-9{margin-left:9px !important}.m-l-10{margin-left:10px !important}.m-l-11{margin-left:11px !important}.m-l-12{margin-left:12px !important}.m-l-13{margin-left:13px !important}.m-l-14{margin-left:14px !important}.m-l-15{margin-left:15px !important}.m-l-16{margin-left:16px !important}.m-l-17{margin-left:17px !important}.m-l-18{margin-left:18px !important}.m-l-19{margin-left:19px !important}.m-l-20{margin-left:20px !important}.m-l-21{margin-left:21px !important}.m-l-22{margin-left:22px !important}.m-l-23{margin-left:23px !important}.m-l-24{margin-left:24px !important}.m-l-25{margin-left:25px !important}.m-l-26{margin-left:26px !important}.m-l-27{margin-left:27px !important}.m-l-28{margin-left:28px !important}.m-l-29{margin-left:29px !important}.m-l-30{margin-left:30px !important}.m-l-31{margin-left:31px !important}.m-l-32{margin-left:32px !important}.m-l-33{margin-left:33px !important}.m-l-34{margin-left:34px !important}.m-l-35{margin-left:35px !important}.m-l-36{margin-left:36px !important}.m-l-37{margin-left:37px !important}.m-l-38{margin-left:38px !important}.m-l-39{margin-left:39px !important}.m-l-40{margin-left:40px !important}.m-l-41{margin-left:41px !important}.m-l-42{margin-left:42px !important}.m-l-43{margin-left:43px !important}.m-l-44{margin-left:44px !important}.m-l-45{margin-left:45px !important}.m-l-46{margin-left:46px !important}.m-l-47{margin-left:47px !important}.m-l-48{margin-left:48px !important}.m-l-49{margin-left:49px !important}.m-l-50{margin-left:50px !important}.m-l-51{margin-left:51px !important}.m-l-52{margin-left:52px !important}.m-l-53{margin-left:53px !important}.m-l-54{margin-left:54px !important}.m-l-55{margin-left:55px !important}.m-l-56{margin-left:56px !important}.m-l-57{margin-left:57px !important}.m-l-58{margin-left:58px !important}.m-l-59{margin-left:59px !important}.m-l-60{margin-left:60px !important}.m-l-61{margin-left:61px !important}.m-l-62{margin-left:62px !important}.m-l-63{margin-left:63px !important}.m-l-64{margin-left:64px !important}.m-l-65{margin-left:65px !important}.m-l-66{margin-left:66px !important}.m-l-67{margin-left:67px !important}.m-l-68{margin-left:68px !important}.m-l-69{margin-left:69px !important}.m-l-70{margin-left:70px !important}.m-l-71{margin-left:71px !important}.m-l-72{margin-left:72px !important}.m-l-73{margin-left:73px !important}.m-l-74{margin-left:74px !important}.m-l-75{margin-left:75px !important}.m-l-76{margin-left:76px !important}.m-l-77{margin-left:77px !important}.m-l-78{margin-left:78px !important}.m-l-79{margin-left:79px !important}.m-l-80{margin-left:80px !important}.m-l-81{margin-left:81px !important}.m-l-82{margin-left:82px !important}.m-l-83{margin-left:83px !important}.m-l-84{margin-left:84px !important}.m-l-85{margin-left:85px !important}.m-l-86{margin-left:86px !important}.m-l-87{margin-left:87px !important}.m-l-88{margin-left:88px !important}.m-l-89{margin-left:89px !important}.m-l-90{margin-left:90px !important}.m-l-91{margin-left:91px !important}.m-l-92{margin-left:92px !important}.m-l-93{margin-left:93px !important}.m-l-94{margin-left:94px !important}.m-l-95{margin-left:95px !important}.m-l-96{margin-left:96px !important}.m-l-97{margin-left:97px !important}.m-l-98{margin-left:98px !important}.m-l-99{margin-left:99px !important}.m-l-100{margin-left:100px !important}.m-x-0{margin-left:0px !important;margin-right:0px !important}.m-x-1{margin-left:1px !important;margin-right:1px !important}.m-x-2{margin-left:2px !important;margin-right:2px !important}.m-x-3{margin-left:3px !important;margin-right:3px !important}.m-x-4{margin-left:4px !important;margin-right:4px !important}.m-x-5{margin-left:5px !important;margin-right:5px !important}.m-x-6{margin-left:6px !important;margin-right:6px !important}.m-x-7{margin-left:7px !important;margin-right:7px !important}.m-x-8{margin-left:8px !important;margin-right:8px !important}.m-x-9{margin-left:9px !important;margin-right:9px !important}.m-x-10{margin-left:10px !important;margin-right:10px !important}.m-x-11{margin-left:11px !important;margin-right:11px !important}.m-x-12{margin-left:12px !important;margin-right:12px !important}.m-x-13{margin-left:13px !important;margin-right:13px !important}.m-x-14{margin-left:14px !important;margin-right:14px !important}.m-x-15{margin-left:15px !important;margin-right:15px !important}.m-x-16{margin-left:16px !important;margin-right:16px !important}.m-x-17{margin-left:17px !important;margin-right:17px !important}.m-x-18{margin-left:18px !important;margin-right:18px !important}.m-x-19{margin-left:19px !important;margin-right:19px !important}.m-x-20{margin-left:20px !important;margin-right:20px !important}.m-x-21{margin-left:21px !important;margin-right:21px !important}.m-x-22{margin-left:22px !important;margin-right:22px !important}.m-x-23{margin-left:23px !important;margin-right:23px !important}.m-x-24{margin-left:24px !important;margin-right:24px !important}.m-x-25{margin-left:25px !important;margin-right:25px !important}.m-x-26{margin-left:26px !important;margin-right:26px !important}.m-x-27{margin-left:27px !important;margin-right:27px !important}.m-x-28{margin-left:28px !important;margin-right:28px !important}.m-x-29{margin-left:29px !important;margin-right:29px !important}.m-x-30{margin-left:30px !important;margin-right:30px !important}.m-x-31{margin-left:31px !important;margin-right:31px !important}.m-x-32{margin-left:32px !important;margin-right:32px !important}.m-x-33{margin-left:33px !important;margin-right:33px !important}.m-x-34{margin-left:34px !important;margin-right:34px !important}.m-x-35{margin-left:35px !important;margin-right:35px !important}.m-x-36{margin-left:36px !important;margin-right:36px !important}.m-x-37{margin-left:37px !important;margin-right:37px !important}.m-x-38{margin-left:38px !important;margin-right:38px !important}.m-x-39{margin-left:39px !important;margin-right:39px !important}.m-x-40{margin-left:40px !important;margin-right:40px !important}.m-x-41{margin-left:41px !important;margin-right:41px !important}.m-x-42{margin-left:42px !important;margin-right:42px !important}.m-x-43{margin-left:43px !important;margin-right:43px !important}.m-x-44{margin-left:44px !important;margin-right:44px !important}.m-x-45{margin-left:45px !important;margin-right:45px !important}.m-x-46{margin-left:46px !important;margin-right:46px !important}.m-x-47{margin-left:47px !important;margin-right:47px !important}.m-x-48{margin-left:48px !important;margin-right:48px !important}.m-x-49{margin-left:49px !important;margin-right:49px !important}.m-x-50{margin-left:50px !important;margin-right:50px !important}.m-x-51{margin-left:51px !important;margin-right:51px !important}.m-x-52{margin-left:52px !important;margin-right:52px !important}.m-x-53{margin-left:53px !important;margin-right:53px !important}.m-x-54{margin-left:54px !important;margin-right:54px !important}.m-x-55{margin-left:55px !important;margin-right:55px !important}.m-x-56{margin-left:56px !important;margin-right:56px !important}.m-x-57{margin-left:57px !important;margin-right:57px !important}.m-x-58{margin-left:58px !important;margin-right:58px !important}.m-x-59{margin-left:59px !important;margin-right:59px !important}.m-x-60{margin-left:60px !important;margin-right:60px !important}.m-x-61{margin-left:61px !important;margin-right:61px !important}.m-x-62{margin-left:62px !important;margin-right:62px !important}.m-x-63{margin-left:63px !important;margin-right:63px !important}.m-x-64{margin-left:64px !important;margin-right:64px !important}.m-x-65{margin-left:65px !important;margin-right:65px !important}.m-x-66{margin-left:66px !important;margin-right:66px !important}.m-x-67{margin-left:67px !important;margin-right:67px !important}.m-x-68{margin-left:68px !important;margin-right:68px !important}.m-x-69{margin-left:69px !important;margin-right:69px !important}.m-x-70{margin-left:70px !important;margin-right:70px !important}.m-x-71{margin-left:71px !important;margin-right:71px !important}.m-x-72{margin-left:72px !important;margin-right:72px !important}.m-x-73{margin-left:73px !important;margin-right:73px !important}.m-x-74{margin-left:74px !important;margin-right:74px !important}.m-x-75{margin-left:75px !important;margin-right:75px !important}.m-x-76{margin-left:76px !important;margin-right:76px !important}.m-x-77{margin-left:77px !important;margin-right:77px !important}.m-x-78{margin-left:78px !important;margin-right:78px !important}.m-x-79{margin-left:79px !important;margin-right:79px !important}.m-x-80{margin-left:80px !important;margin-right:80px !important}.m-x-81{margin-left:81px !important;margin-right:81px !important}.m-x-82{margin-left:82px !important;margin-right:82px !important}.m-x-83{margin-left:83px !important;margin-right:83px !important}.m-x-84{margin-left:84px !important;margin-right:84px !important}.m-x-85{margin-left:85px !important;margin-right:85px !important}.m-x-86{margin-left:86px !important;margin-right:86px !important}.m-x-87{margin-left:87px !important;margin-right:87px !important}.m-x-88{margin-left:88px !important;margin-right:88px !important}.m-x-89{margin-left:89px !important;margin-right:89px !important}.m-x-90{margin-left:90px !important;margin-right:90px !important}.m-x-91{margin-left:91px !important;margin-right:91px !important}.m-x-92{margin-left:92px !important;margin-right:92px !important}.m-x-93{margin-left:93px !important;margin-right:93px !important}.m-x-94{margin-left:94px !important;margin-right:94px !important}.m-x-95{margin-left:95px !important;margin-right:95px !important}.m-x-96{margin-left:96px !important;margin-right:96px !important}.m-x-97{margin-left:97px !important;margin-right:97px !important}.m-x-98{margin-left:98px !important;margin-right:98px !important}.m-x-99{margin-left:99px !important;margin-right:99px !important}.m-x-100{margin-left:100px !important;margin-right:100px !important}.m-y-0{margin-top:0px !important;margin-bottom:0px !important}.m-y-1{margin-top:1px !important;margin-bottom:1px !important}.m-y-2{margin-top:2px !important;margin-bottom:2px !important}.m-y-3{margin-top:3px !important;margin-bottom:3px !important}.m-y-4{margin-top:4px !important;margin-bottom:4px !important}.m-y-5{margin-top:5px !important;margin-bottom:5px !important}.m-y-6{margin-top:6px !important;margin-bottom:6px !important}.m-y-7{margin-top:7px !important;margin-bottom:7px !important}.m-y-8{margin-top:8px !important;margin-bottom:8px !important}.m-y-9{margin-top:9px !important;margin-bottom:9px !important}.m-y-10{margin-top:10px !important;margin-bottom:10px !important}.m-y-11{margin-top:11px !important;margin-bottom:11px !important}.m-y-12{margin-top:12px !important;margin-bottom:12px !important}.m-y-13{margin-top:13px !important;margin-bottom:13px !important}.m-y-14{margin-top:14px !important;margin-bottom:14px !important}.m-y-15{margin-top:15px !important;margin-bottom:15px !important}.m-y-16{margin-top:16px !important;margin-bottom:16px !important}.m-y-17{margin-top:17px !important;margin-bottom:17px !important}.m-y-18{margin-top:18px !important;margin-bottom:18px !important}.m-y-19{margin-top:19px !important;margin-bottom:19px !important}.m-y-20{margin-top:20px !important;margin-bottom:20px !important}.m-y-21{margin-top:21px !important;margin-bottom:21px !important}.m-y-22{margin-top:22px !important;margin-bottom:22px !important}.m-y-23{margin-top:23px !important;margin-bottom:23px !important}.m-y-24{margin-top:24px !important;margin-bottom:24px !important}.m-y-25{margin-top:25px !important;margin-bottom:25px !important}.m-y-26{margin-top:26px !important;margin-bottom:26px !important}.m-y-27{margin-top:27px !important;margin-bottom:27px !important}.m-y-28{margin-top:28px !important;margin-bottom:28px !important}.m-y-29{margin-top:29px !important;margin-bottom:29px !important}.m-y-30{margin-top:30px !important;margin-bottom:30px !important}.m-y-31{margin-top:31px !important;margin-bottom:31px !important}.m-y-32{margin-top:32px !important;margin-bottom:32px !important}.m-y-33{margin-top:33px !important;margin-bottom:33px !important}.m-y-34{margin-top:34px !important;margin-bottom:34px !important}.m-y-35{margin-top:35px !important;margin-bottom:35px !important}.m-y-36{margin-top:36px !important;margin-bottom:36px !important}.m-y-37{margin-top:37px !important;margin-bottom:37px !important}.m-y-38{margin-top:38px !important;margin-bottom:38px !important}.m-y-39{margin-top:39px !important;margin-bottom:39px !important}.m-y-40{margin-top:40px !important;margin-bottom:40px !important}.m-y-41{margin-top:41px !important;margin-bottom:41px !important}.m-y-42{margin-top:42px !important;margin-bottom:42px !important}.m-y-43{margin-top:43px !important;margin-bottom:43px !important}.m-y-44{margin-top:44px !important;margin-bottom:44px !important}.m-y-45{margin-top:45px !important;margin-bottom:45px !important}.m-y-46{margin-top:46px !important;margin-bottom:46px !important}.m-y-47{margin-top:47px !important;margin-bottom:47px !important}.m-y-48{margin-top:48px !important;margin-bottom:48px !important}.m-y-49{margin-top:49px !important;margin-bottom:49px !important}.m-y-50{margin-top:50px !important;margin-bottom:50px !important}.m-y-51{margin-top:51px !important;margin-bottom:51px !important}.m-y-52{margin-top:52px !important;margin-bottom:52px !important}.m-y-53{margin-top:53px !important;margin-bottom:53px !important}.m-y-54{margin-top:54px !important;margin-bottom:54px !important}.m-y-55{margin-top:55px !important;margin-bottom:55px !important}.m-y-56{margin-top:56px !important;margin-bottom:56px !important}.m-y-57{margin-top:57px !important;margin-bottom:57px !important}.m-y-58{margin-top:58px !important;margin-bottom:58px !important}.m-y-59{margin-top:59px !important;margin-bottom:59px !important}.m-y-60{margin-top:60px !important;margin-bottom:60px !important}.m-y-61{margin-top:61px !important;margin-bottom:61px !important}.m-y-62{margin-top:62px !important;margin-bottom:62px !important}.m-y-63{margin-top:63px !important;margin-bottom:63px !important}.m-y-64{margin-top:64px !important;margin-bottom:64px !important}.m-y-65{margin-top:65px !important;margin-bottom:65px !important}.m-y-66{margin-top:66px !important;margin-bottom:66px !important}.m-y-67{margin-top:67px !important;margin-bottom:67px !important}.m-y-68{margin-top:68px !important;margin-bottom:68px !important}.m-y-69{margin-top:69px !important;margin-bottom:69px !important}.m-y-70{margin-top:70px !important;margin-bottom:70px !important}.m-y-71{margin-top:71px !important;margin-bottom:71px !important}.m-y-72{margin-top:72px !important;margin-bottom:72px !important}.m-y-73{margin-top:73px !important;margin-bottom:73px !important}.m-y-74{margin-top:74px !important;margin-bottom:74px !important}.m-y-75{margin-top:75px !important;margin-bottom:75px !important}.m-y-76{margin-top:76px !important;margin-bottom:76px !important}.m-y-77{margin-top:77px !important;margin-bottom:77px !important}.m-y-78{margin-top:78px !important;margin-bottom:78px !important}.m-y-79{margin-top:79px !important;margin-bottom:79px !important}.m-y-80{margin-top:80px !important;margin-bottom:80px !important}.m-y-81{margin-top:81px !important;margin-bottom:81px !important}.m-y-82{margin-top:82px !important;margin-bottom:82px !important}.m-y-83{margin-top:83px !important;margin-bottom:83px !important}.m-y-84{margin-top:84px !important;margin-bottom:84px !important}.m-y-85{margin-top:85px !important;margin-bottom:85px !important}.m-y-86{margin-top:86px !important;margin-bottom:86px !important}.m-y-87{margin-top:87px !important;margin-bottom:87px !important}.m-y-88{margin-top:88px !important;margin-bottom:88px !important}.m-y-89{margin-top:89px !important;margin-bottom:89px !important}.m-y-90{margin-top:90px !important;margin-bottom:90px !important}.m-y-91{margin-top:91px !important;margin-bottom:91px !important}.m-y-92{margin-top:92px !important;margin-bottom:92px !important}.m-y-93{margin-top:93px !important;margin-bottom:93px !important}.m-y-94{margin-top:94px !important;margin-bottom:94px !important}.m-y-95{margin-top:95px !important;margin-bottom:95px !important}.m-y-96{margin-top:96px !important;margin-bottom:96px !important}.m-y-97{margin-top:97px !important;margin-bottom:97px !important}.m-y-98{margin-top:98px !important;margin-bottom:98px !important}.m-y-99{margin-top:99px !important;margin-bottom:99px !important}.m-y-100{margin-top:100px !important;margin-bottom:100px !important}.p-0{padding:0px !important}.p-1{padding:1px !important}.p-2{padding:2px !important}.p-3{padding:3px !important}.p-4{padding:4px !important}.p-5{padding:5px !important}.p-6{padding:6px !important}.p-7{padding:7px !important}.p-8{padding:8px !important}.p-9{padding:9px !important}.p-10{padding:10px !important}.p-11{padding:11px !important}.p-12{padding:12px !important}.p-13{padding:13px !important}.p-14{padding:14px !important}.p-15{padding:15px !important}.p-16{padding:16px !important}.p-17{padding:17px !important}.p-18{padding:18px !important}.p-19{padding:19px !important}.p-20{padding:20px !important}.p-21{padding:21px !important}.p-22{padding:22px !important}.p-23{padding:23px !important}.p-24{padding:24px !important}.p-25{padding:25px !important}.p-26{padding:26px !important}.p-27{padding:27px !important}.p-28{padding:28px !important}.p-29{padding:29px !important}.p-30{padding:30px !important}.p-31{padding:31px !important}.p-32{padding:32px !important}.p-33{padding:33px !important}.p-34{padding:34px !important}.p-35{padding:35px !important}.p-36{padding:36px !important}.p-37{padding:37px !important}.p-38{padding:38px !important}.p-39{padding:39px !important}.p-40{padding:40px !important}.p-41{padding:41px !important}.p-42{padding:42px !important}.p-43{padding:43px !important}.p-44{padding:44px !important}.p-45{padding:45px !important}.p-46{padding:46px !important}.p-47{padding:47px !important}.p-48{padding:48px !important}.p-49{padding:49px !important}.p-50{padding:50px !important}.p-51{padding:51px !important}.p-52{padding:52px !important}.p-53{padding:53px !important}.p-54{padding:54px !important}.p-55{padding:55px !important}.p-56{padding:56px !important}.p-57{padding:57px !important}.p-58{padding:58px !important}.p-59{padding:59px !important}.p-60{padding:60px !important}.p-61{padding:61px !important}.p-62{padding:62px !important}.p-63{padding:63px !important}.p-64{padding:64px !important}.p-65{padding:65px !important}.p-66{padding:66px !important}.p-67{padding:67px !important}.p-68{padding:68px !important}.p-69{padding:69px !important}.p-70{padding:70px !important}.p-71{padding:71px !important}.p-72{padding:72px !important}.p-73{padding:73px !important}.p-74{padding:74px !important}.p-75{padding:75px !important}.p-76{padding:76px !important}.p-77{padding:77px !important}.p-78{padding:78px !important}.p-79{padding:79px !important}.p-80{padding:80px !important}.p-81{padding:81px !important}.p-82{padding:82px !important}.p-83{padding:83px !important}.p-84{padding:84px !important}.p-85{padding:85px !important}.p-86{padding:86px !important}.p-87{padding:87px !important}.p-88{padding:88px !important}.p-89{padding:89px !important}.p-90{padding:90px !important}.p-91{padding:91px !important}.p-92{padding:92px !important}.p-93{padding:93px !important}.p-94{padding:94px !important}.p-95{padding:95px !important}.p-96{padding:96px !important}.p-97{padding:97px !important}.p-98{padding:98px !important}.p-99{padding:99px !important}.p-100{padding:100px !important}.p-t-0{padding-top:0px !important}.p-t-1{padding-top:1px !important}.p-t-2{padding-top:2px !important}.p-t-3{padding-top:3px !important}.p-t-4{padding-top:4px !important}.p-t-5{padding-top:5px !important}.p-t-6{padding-top:6px !important}.p-t-7{padding-top:7px !important}.p-t-8{padding-top:8px !important}.p-t-9{padding-top:9px !important}.p-t-10{padding-top:10px !important}.p-t-11{padding-top:11px !important}.p-t-12{padding-top:12px !important}.p-t-13{padding-top:13px !important}.p-t-14{padding-top:14px !important}.p-t-15{padding-top:15px !important}.p-t-16{padding-top:16px !important}.p-t-17{padding-top:17px !important}.p-t-18{padding-top:18px !important}.p-t-19{padding-top:19px !important}.p-t-20{padding-top:20px !important}.p-t-21{padding-top:21px !important}.p-t-22{padding-top:22px !important}.p-t-23{padding-top:23px !important}.p-t-24{padding-top:24px !important}.p-t-25{padding-top:25px !important}.p-t-26{padding-top:26px !important}.p-t-27{padding-top:27px !important}.p-t-28{padding-top:28px !important}.p-t-29{padding-top:29px !important}.p-t-30{padding-top:30px !important}.p-t-31{padding-top:31px !important}.p-t-32{padding-top:32px !important}.p-t-33{padding-top:33px !important}.p-t-34{padding-top:34px !important}.p-t-35{padding-top:35px !important}.p-t-36{padding-top:36px !important}.p-t-37{padding-top:37px !important}.p-t-38{padding-top:38px !important}.p-t-39{padding-top:39px !important}.p-t-40{padding-top:40px !important}.p-t-41{padding-top:41px !important}.p-t-42{padding-top:42px !important}.p-t-43{padding-top:43px !important}.p-t-44{padding-top:44px !important}.p-t-45{padding-top:45px !important}.p-t-46{padding-top:46px !important}.p-t-47{padding-top:47px !important}.p-t-48{padding-top:48px !important}.p-t-49{padding-top:49px !important}.p-t-50{padding-top:50px !important}.p-t-51{padding-top:51px !important}.p-t-52{padding-top:52px !important}.p-t-53{padding-top:53px !important}.p-t-54{padding-top:54px !important}.p-t-55{padding-top:55px !important}.p-t-56{padding-top:56px !important}.p-t-57{padding-top:57px !important}.p-t-58{padding-top:58px !important}.p-t-59{padding-top:59px !important}.p-t-60{padding-top:60px !important}.p-t-61{padding-top:61px !important}.p-t-62{padding-top:62px !important}.p-t-63{padding-top:63px !important}.p-t-64{padding-top:64px !important}.p-t-65{padding-top:65px !important}.p-t-66{padding-top:66px !important}.p-t-67{padding-top:67px !important}.p-t-68{padding-top:68px !important}.p-t-69{padding-top:69px !important}.p-t-70{padding-top:70px !important}.p-t-71{padding-top:71px !important}.p-t-72{padding-top:72px !important}.p-t-73{padding-top:73px !important}.p-t-74{padding-top:74px !important}.p-t-75{padding-top:75px !important}.p-t-76{padding-top:76px !important}.p-t-77{padding-top:77px !important}.p-t-78{padding-top:78px !important}.p-t-79{padding-top:79px !important}.p-t-80{padding-top:80px !important}.p-t-81{padding-top:81px !important}.p-t-82{padding-top:82px !important}.p-t-83{padding-top:83px !important}.p-t-84{padding-top:84px !important}.p-t-85{padding-top:85px !important}.p-t-86{padding-top:86px !important}.p-t-87{padding-top:87px !important}.p-t-88{padding-top:88px !important}.p-t-89{padding-top:89px !important}.p-t-90{padding-top:90px !important}.p-t-91{padding-top:91px !important}.p-t-92{padding-top:92px !important}.p-t-93{padding-top:93px !important}.p-t-94{padding-top:94px !important}.p-t-95{padding-top:95px !important}.p-t-96{padding-top:96px !important}.p-t-97{padding-top:97px !important}.p-t-98{padding-top:98px !important}.p-t-99{padding-top:99px !important}.p-t-100{padding-top:100px !important}.p-r-0{padding-right:0px !important}.p-r-1{padding-right:1px !important}.p-r-2{padding-right:2px !important}.p-r-3{padding-right:3px !important}.p-r-4{padding-right:4px !important}.p-r-5{padding-right:5px !important}.p-r-6{padding-right:6px !important}.p-r-7{padding-right:7px !important}.p-r-8{padding-right:8px !important}.p-r-9{padding-right:9px !important}.p-r-10{padding-right:10px !important}.p-r-11{padding-right:11px !important}.p-r-12{padding-right:12px !important}.p-r-13{padding-right:13px !important}.p-r-14{padding-right:14px !important}.p-r-15{padding-right:15px !important}.p-r-16{padding-right:16px !important}.p-r-17{padding-right:17px !important}.p-r-18{padding-right:18px !important}.p-r-19{padding-right:19px !important}.p-r-20{padding-right:20px !important}.p-r-21{padding-right:21px !important}.p-r-22{padding-right:22px !important}.p-r-23{padding-right:23px !important}.p-r-24{padding-right:24px !important}.p-r-25{padding-right:25px !important}.p-r-26{padding-right:26px !important}.p-r-27{padding-right:27px !important}.p-r-28{padding-right:28px !important}.p-r-29{padding-right:29px !important}.p-r-30{padding-right:30px !important}.p-r-31{padding-right:31px !important}.p-r-32{padding-right:32px !important}.p-r-33{padding-right:33px !important}.p-r-34{padding-right:34px !important}.p-r-35{padding-right:35px !important}.p-r-36{padding-right:36px !important}.p-r-37{padding-right:37px !important}.p-r-38{padding-right:38px !important}.p-r-39{padding-right:39px !important}.p-r-40{padding-right:40px !important}.p-r-41{padding-right:41px !important}.p-r-42{padding-right:42px !important}.p-r-43{padding-right:43px !important}.p-r-44{padding-right:44px !important}.p-r-45{padding-right:45px !important}.p-r-46{padding-right:46px !important}.p-r-47{padding-right:47px !important}.p-r-48{padding-right:48px !important}.p-r-49{padding-right:49px !important}.p-r-50{padding-right:50px !important}.p-r-51{padding-right:51px !important}.p-r-52{padding-right:52px !important}.p-r-53{padding-right:53px !important}.p-r-54{padding-right:54px !important}.p-r-55{padding-right:55px !important}.p-r-56{padding-right:56px !important}.p-r-57{padding-right:57px !important}.p-r-58{padding-right:58px !important}.p-r-59{padding-right:59px !important}.p-r-60{padding-right:60px !important}.p-r-61{padding-right:61px !important}.p-r-62{padding-right:62px !important}.p-r-63{padding-right:63px !important}.p-r-64{padding-right:64px !important}.p-r-65{padding-right:65px !important}.p-r-66{padding-right:66px !important}.p-r-67{padding-right:67px !important}.p-r-68{padding-right:68px !important}.p-r-69{padding-right:69px !important}.p-r-70{padding-right:70px !important}.p-r-71{padding-right:71px !important}.p-r-72{padding-right:72px !important}.p-r-73{padding-right:73px !important}.p-r-74{padding-right:74px !important}.p-r-75{padding-right:75px !important}.p-r-76{padding-right:76px !important}.p-r-77{padding-right:77px !important}.p-r-78{padding-right:78px !important}.p-r-79{padding-right:79px !important}.p-r-80{padding-right:80px !important}.p-r-81{padding-right:81px !important}.p-r-82{padding-right:82px !important}.p-r-83{padding-right:83px !important}.p-r-84{padding-right:84px !important}.p-r-85{padding-right:85px !important}.p-r-86{padding-right:86px !important}.p-r-87{padding-right:87px !important}.p-r-88{padding-right:88px !important}.p-r-89{padding-right:89px !important}.p-r-90{padding-right:90px !important}.p-r-91{padding-right:91px !important}.p-r-92{padding-right:92px !important}.p-r-93{padding-right:93px !important}.p-r-94{padding-right:94px !important}.p-r-95{padding-right:95px !important}.p-r-96{padding-right:96px !important}.p-r-97{padding-right:97px !important}.p-r-98{padding-right:98px !important}.p-r-99{padding-right:99px !important}.p-r-100{padding-right:100px !important}.p-b-0{padding-bottom:0px !important}.p-b-1{padding-bottom:1px !important}.p-b-2{padding-bottom:2px !important}.p-b-3{padding-bottom:3px !important}.p-b-4{padding-bottom:4px !important}.p-b-5{padding-bottom:5px !important}.p-b-6{padding-bottom:6px !important}.p-b-7{padding-bottom:7px !important}.p-b-8{padding-bottom:8px !important}.p-b-9{padding-bottom:9px !important}.p-b-10{padding-bottom:10px !important}.p-b-11{padding-bottom:11px !important}.p-b-12{padding-bottom:12px !important}.p-b-13{padding-bottom:13px !important}.p-b-14{padding-bottom:14px !important}.p-b-15{padding-bottom:15px !important}.p-b-16{padding-bottom:16px !important}.p-b-17{padding-bottom:17px !important}.p-b-18{padding-bottom:18px !important}.p-b-19{padding-bottom:19px !important}.p-b-20{padding-bottom:20px !important}.p-b-21{padding-bottom:21px !important}.p-b-22{padding-bottom:22px !important}.p-b-23{padding-bottom:23px !important}.p-b-24{padding-bottom:24px !important}.p-b-25{padding-bottom:25px !important}.p-b-26{padding-bottom:26px !important}.p-b-27{padding-bottom:27px !important}.p-b-28{padding-bottom:28px !important}.p-b-29{padding-bottom:29px !important}.p-b-30{padding-bottom:30px !important}.p-b-31{padding-bottom:31px !important}.p-b-32{padding-bottom:32px !important}.p-b-33{padding-bottom:33px !important}.p-b-34{padding-bottom:34px !important}.p-b-35{padding-bottom:35px !important}.p-b-36{padding-bottom:36px !important}.p-b-37{padding-bottom:37px !important}.p-b-38{padding-bottom:38px !important}.p-b-39{padding-bottom:39px !important}.p-b-40{padding-bottom:40px !important}.p-b-41{padding-bottom:41px !important}.p-b-42{padding-bottom:42px !important}.p-b-43{padding-bottom:43px !important}.p-b-44{padding-bottom:44px !important}.p-b-45{padding-bottom:45px !important}.p-b-46{padding-bottom:46px !important}.p-b-47{padding-bottom:47px !important}.p-b-48{padding-bottom:48px !important}.p-b-49{padding-bottom:49px !important}.p-b-50{padding-bottom:50px !important}.p-b-51{padding-bottom:51px !important}.p-b-52{padding-bottom:52px !important}.p-b-53{padding-bottom:53px !important}.p-b-54{padding-bottom:54px !important}.p-b-55{padding-bottom:55px !important}.p-b-56{padding-bottom:56px !important}.p-b-57{padding-bottom:57px !important}.p-b-58{padding-bottom:58px !important}.p-b-59{padding-bottom:59px !important}.p-b-60{padding-bottom:60px !important}.p-b-61{padding-bottom:61px !important}.p-b-62{padding-bottom:62px !important}.p-b-63{padding-bottom:63px !important}.p-b-64{padding-bottom:64px !important}.p-b-65{padding-bottom:65px !important}.p-b-66{padding-bottom:66px !important}.p-b-67{padding-bottom:67px !important}.p-b-68{padding-bottom:68px !important}.p-b-69{padding-bottom:69px !important}.p-b-70{padding-bottom:70px !important}.p-b-71{padding-bottom:71px !important}.p-b-72{padding-bottom:72px !important}.p-b-73{padding-bottom:73px !important}.p-b-74{padding-bottom:74px !important}.p-b-75{padding-bottom:75px !important}.p-b-76{padding-bottom:76px !important}.p-b-77{padding-bottom:77px !important}.p-b-78{padding-bottom:78px !important}.p-b-79{padding-bottom:79px !important}.p-b-80{padding-bottom:80px !important}.p-b-81{padding-bottom:81px !important}.p-b-82{padding-bottom:82px !important}.p-b-83{padding-bottom:83px !important}.p-b-84{padding-bottom:84px !important}.p-b-85{padding-bottom:85px !important}.p-b-86{padding-bottom:86px !important}.p-b-87{padding-bottom:87px !important}.p-b-88{padding-bottom:88px !important}.p-b-89{padding-bottom:89px !important}.p-b-90{padding-bottom:90px !important}.p-b-91{padding-bottom:91px !important}.p-b-92{padding-bottom:92px !important}.p-b-93{padding-bottom:93px !important}.p-b-94{padding-bottom:94px !important}.p-b-95{padding-bottom:95px !important}.p-b-96{padding-bottom:96px !important}.p-b-97{padding-bottom:97px !important}.p-b-98{padding-bottom:98px !important}.p-b-99{padding-bottom:99px !important}.p-b-100{padding-bottom:100px !important}.p-l-0{padding-left:0px !important}.p-l-1{padding-left:1px !important}.p-l-2{padding-left:2px !important}.p-l-3{padding-left:3px !important}.p-l-4{padding-left:4px !important}.p-l-5{padding-left:5px !important}.p-l-6{padding-left:6px !important}.p-l-7{padding-left:7px !important}.p-l-8{padding-left:8px !important}.p-l-9{padding-left:9px !important}.p-l-10{padding-left:10px !important}.p-l-11{padding-left:11px !important}.p-l-12{padding-left:12px !important}.p-l-13{padding-left:13px !important}.p-l-14{padding-left:14px !important}.p-l-15{padding-left:15px !important}.p-l-16{padding-left:16px !important}.p-l-17{padding-left:17px !important}.p-l-18{padding-left:18px !important}.p-l-19{padding-left:19px !important}.p-l-20{padding-left:20px !important}.p-l-21{padding-left:21px !important}.p-l-22{padding-left:22px !important}.p-l-23{padding-left:23px !important}.p-l-24{padding-left:24px !important}.p-l-25{padding-left:25px !important}.p-l-26{padding-left:26px !important}.p-l-27{padding-left:27px !important}.p-l-28{padding-left:28px !important}.p-l-29{padding-left:29px !important}.p-l-30{padding-left:30px !important}.p-l-31{padding-left:31px !important}.p-l-32{padding-left:32px !important}.p-l-33{padding-left:33px !important}.p-l-34{padding-left:34px !important}.p-l-35{padding-left:35px !important}.p-l-36{padding-left:36px !important}.p-l-37{padding-left:37px !important}.p-l-38{padding-left:38px !important}.p-l-39{padding-left:39px !important}.p-l-40{padding-left:40px !important}.p-l-41{padding-left:41px !important}.p-l-42{padding-left:42px !important}.p-l-43{padding-left:43px !important}.p-l-44{padding-left:44px !important}.p-l-45{padding-left:45px !important}.p-l-46{padding-left:46px !important}.p-l-47{padding-left:47px !important}.p-l-48{padding-left:48px !important}.p-l-49{padding-left:49px !important}.p-l-50{padding-left:50px !important}.p-l-51{padding-left:51px !important}.p-l-52{padding-left:52px !important}.p-l-53{padding-left:53px !important}.p-l-54{padding-left:54px !important}.p-l-55{padding-left:55px !important}.p-l-56{padding-left:56px !important}.p-l-57{padding-left:57px !important}.p-l-58{padding-left:58px !important}.p-l-59{padding-left:59px !important}.p-l-60{padding-left:60px !important}.p-l-61{padding-left:61px !important}.p-l-62{padding-left:62px !important}.p-l-63{padding-left:63px !important}.p-l-64{padding-left:64px !important}.p-l-65{padding-left:65px !important}.p-l-66{padding-left:66px !important}.p-l-67{padding-left:67px !important}.p-l-68{padding-left:68px !important}.p-l-69{padding-left:69px !important}.p-l-70{padding-left:70px !important}.p-l-71{padding-left:71px !important}.p-l-72{padding-left:72px !important}.p-l-73{padding-left:73px !important}.p-l-74{padding-left:74px !important}.p-l-75{padding-left:75px !important}.p-l-76{padding-left:76px !important}.p-l-77{padding-left:77px !important}.p-l-78{padding-left:78px !important}.p-l-79{padding-left:79px !important}.p-l-80{padding-left:80px !important}.p-l-81{padding-left:81px !important}.p-l-82{padding-left:82px !important}.p-l-83{padding-left:83px !important}.p-l-84{padding-left:84px !important}.p-l-85{padding-left:85px !important}.p-l-86{padding-left:86px !important}.p-l-87{padding-left:87px !important}.p-l-88{padding-left:88px !important}.p-l-89{padding-left:89px !important}.p-l-90{padding-left:90px !important}.p-l-91{padding-left:91px !important}.p-l-92{padding-left:92px !important}.p-l-93{padding-left:93px !important}.p-l-94{padding-left:94px !important}.p-l-95{padding-left:95px !important}.p-l-96{padding-left:96px !important}.p-l-97{padding-left:97px !important}.p-l-98{padding-left:98px !important}.p-l-99{padding-left:99px !important}.p-l-100{padding-left:100px !important}.p-x-0{padding-left:0px !important;padding-right:0px !important}.p-x-1{padding-left:1px !important;padding-right:1px !important}.p-x-2{padding-left:2px !important;padding-right:2px !important}.p-x-3{padding-left:3px !important;padding-right:3px !important}.p-x-4{padding-left:4px !important;padding-right:4px !important}.p-x-5{padding-left:5px !important;padding-right:5px !important}.p-x-6{padding-left:6px !important;padding-right:6px !important}.p-x-7{padding-left:7px !important;padding-right:7px !important}.p-x-8{padding-left:8px !important;padding-right:8px !important}.p-x-9{padding-left:9px !important;padding-right:9px !important}.p-x-10{padding-left:10px !important;padding-right:10px !important}.p-x-11{padding-left:11px !important;padding-right:11px !important}.p-x-12{padding-left:12px !important;padding-right:12px !important}.p-x-13{padding-left:13px !important;padding-right:13px !important}.p-x-14{padding-left:14px !important;padding-right:14px !important}.p-x-15{padding-left:15px !important;padding-right:15px !important}.p-x-16{padding-left:16px !important;padding-right:16px !important}.p-x-17{padding-left:17px !important;padding-right:17px !important}.p-x-18{padding-left:18px !important;padding-right:18px !important}.p-x-19{padding-left:19px !important;padding-right:19px !important}.p-x-20{padding-left:20px !important;padding-right:20px !important}.p-x-21{padding-left:21px !important;padding-right:21px !important}.p-x-22{padding-left:22px !important;padding-right:22px !important}.p-x-23{padding-left:23px !important;padding-right:23px !important}.p-x-24{padding-left:24px !important;padding-right:24px !important}.p-x-25{padding-left:25px !important;padding-right:25px !important}.p-x-26{padding-left:26px !important;padding-right:26px !important}.p-x-27{padding-left:27px !important;padding-right:27px !important}.p-x-28{padding-left:28px !important;padding-right:28px !important}.p-x-29{padding-left:29px !important;padding-right:29px !important}.p-x-30{padding-left:30px !important;padding-right:30px !important}.p-x-31{padding-left:31px !important;padding-right:31px !important}.p-x-32{padding-left:32px !important;padding-right:32px !important}.p-x-33{padding-left:33px !important;padding-right:33px !important}.p-x-34{padding-left:34px !important;padding-right:34px !important}.p-x-35{padding-left:35px !important;padding-right:35px !important}.p-x-36{padding-left:36px !important;padding-right:36px !important}.p-x-37{padding-left:37px !important;padding-right:37px !important}.p-x-38{padding-left:38px !important;padding-right:38px !important}.p-x-39{padding-left:39px !important;padding-right:39px !important}.p-x-40{padding-left:40px !important;padding-right:40px !important}.p-x-41{padding-left:41px !important;padding-right:41px !important}.p-x-42{padding-left:42px !important;padding-right:42px !important}.p-x-43{padding-left:43px !important;padding-right:43px !important}.p-x-44{padding-left:44px !important;padding-right:44px !important}.p-x-45{padding-left:45px !important;padding-right:45px !important}.p-x-46{padding-left:46px !important;padding-right:46px !important}.p-x-47{padding-left:47px !important;padding-right:47px !important}.p-x-48{padding-left:48px !important;padding-right:48px !important}.p-x-49{padding-left:49px !important;padding-right:49px !important}.p-x-50{padding-left:50px !important;padding-right:50px !important}.p-x-51{padding-left:51px !important;padding-right:51px !important}.p-x-52{padding-left:52px !important;padding-right:52px !important}.p-x-53{padding-left:53px !important;padding-right:53px !important}.p-x-54{padding-left:54px !important;padding-right:54px !important}.p-x-55{padding-left:55px !important;padding-right:55px !important}.p-x-56{padding-left:56px !important;padding-right:56px !important}.p-x-57{padding-left:57px !important;padding-right:57px !important}.p-x-58{padding-left:58px !important;padding-right:58px !important}.p-x-59{padding-left:59px !important;padding-right:59px !important}.p-x-60{padding-left:60px !important;padding-right:60px !important}.p-x-61{padding-left:61px !important;padding-right:61px !important}.p-x-62{padding-left:62px !important;padding-right:62px !important}.p-x-63{padding-left:63px !important;padding-right:63px !important}.p-x-64{padding-left:64px !important;padding-right:64px !important}.p-x-65{padding-left:65px !important;padding-right:65px !important}.p-x-66{padding-left:66px !important;padding-right:66px !important}.p-x-67{padding-left:67px !important;padding-right:67px !important}.p-x-68{padding-left:68px !important;padding-right:68px !important}.p-x-69{padding-left:69px !important;padding-right:69px !important}.p-x-70{padding-left:70px !important;padding-right:70px !important}.p-x-71{padding-left:71px !important;padding-right:71px !important}.p-x-72{padding-left:72px !important;padding-right:72px !important}.p-x-73{padding-left:73px !important;padding-right:73px !important}.p-x-74{padding-left:74px !important;padding-right:74px !important}.p-x-75{padding-left:75px !important;padding-right:75px !important}.p-x-76{padding-left:76px !important;padding-right:76px !important}.p-x-77{padding-left:77px !important;padding-right:77px !important}.p-x-78{padding-left:78px !important;padding-right:78px !important}.p-x-79{padding-left:79px !important;padding-right:79px !important}.p-x-80{padding-left:80px !important;padding-right:80px !important}.p-x-81{padding-left:81px !important;padding-right:81px !important}.p-x-82{padding-left:82px !important;padding-right:82px !important}.p-x-83{padding-left:83px !important;padding-right:83px !important}.p-x-84{padding-left:84px !important;padding-right:84px !important}.p-x-85{padding-left:85px !important;padding-right:85px !important}.p-x-86{padding-left:86px !important;padding-right:86px !important}.p-x-87{padding-left:87px !important;padding-right:87px !important}.p-x-88{padding-left:88px !important;padding-right:88px !important}.p-x-89{padding-left:89px !important;padding-right:89px !important}.p-x-90{padding-left:90px !important;padding-right:90px !important}.p-x-91{padding-left:91px !important;padding-right:91px !important}.p-x-92{padding-left:92px !important;padding-right:92px !important}.p-x-93{padding-left:93px !important;padding-right:93px !important}.p-x-94{padding-left:94px !important;padding-right:94px !important}.p-x-95{padding-left:95px !important;padding-right:95px !important}.p-x-96{padding-left:96px !important;padding-right:96px !important}.p-x-97{padding-left:97px !important;padding-right:97px !important}.p-x-98{padding-left:98px !important;padding-right:98px !important}.p-x-99{padding-left:99px !important;padding-right:99px !important}.p-x-100{padding-left:100px !important;padding-right:100px !important}.p-y-0{padding-top:0px !important;padding-bottom:0px !important}.p-y-1{padding-top:1px !important;padding-bottom:1px !important}.p-y-2{padding-top:2px !important;padding-bottom:2px !important}.p-y-3{padding-top:3px !important;padding-bottom:3px !important}.p-y-4{padding-top:4px !important;padding-bottom:4px !important}.p-y-5{padding-top:5px !important;padding-bottom:5px !important}.p-y-6{padding-top:6px !important;padding-bottom:6px !important}.p-y-7{padding-top:7px !important;padding-bottom:7px !important}.p-y-8{padding-top:8px !important;padding-bottom:8px !important}.p-y-9{padding-top:9px !important;padding-bottom:9px !important}.p-y-10{padding-top:10px !important;padding-bottom:10px !important}.p-y-11{padding-top:11px !important;padding-bottom:11px !important}.p-y-12{padding-top:12px !important;padding-bottom:12px !important}.p-y-13{padding-top:13px !important;padding-bottom:13px !important}.p-y-14{padding-top:14px !important;padding-bottom:14px !important}.p-y-15{padding-top:15px !important;padding-bottom:15px !important}.p-y-16{padding-top:16px !important;padding-bottom:16px !important}.p-y-17{padding-top:17px !important;padding-bottom:17px !important}.p-y-18{padding-top:18px !important;padding-bottom:18px !important}.p-y-19{padding-top:19px !important;padding-bottom:19px !important}.p-y-20{padding-top:20px !important;padding-bottom:20px !important}.p-y-21{padding-top:21px !important;padding-bottom:21px !important}.p-y-22{padding-top:22px !important;padding-bottom:22px !important}.p-y-23{padding-top:23px !important;padding-bottom:23px !important}.p-y-24{padding-top:24px !important;padding-bottom:24px !important}.p-y-25{padding-top:25px !important;padding-bottom:25px !important}.p-y-26{padding-top:26px !important;padding-bottom:26px !important}.p-y-27{padding-top:27px !important;padding-bottom:27px !important}.p-y-28{padding-top:28px !important;padding-bottom:28px !important}.p-y-29{padding-top:29px !important;padding-bottom:29px !important}.p-y-30{padding-top:30px !important;padding-bottom:30px !important}.p-y-31{padding-top:31px !important;padding-bottom:31px !important}.p-y-32{padding-top:32px !important;padding-bottom:32px !important}.p-y-33{padding-top:33px !important;padding-bottom:33px !important}.p-y-34{padding-top:34px !important;padding-bottom:34px !important}.p-y-35{padding-top:35px !important;padding-bottom:35px !important}.p-y-36{padding-top:36px !important;padding-bottom:36px !important}.p-y-37{padding-top:37px !important;padding-bottom:37px !important}.p-y-38{padding-top:38px !important;padding-bottom:38px !important}.p-y-39{padding-top:39px !important;padding-bottom:39px !important}.p-y-40{padding-top:40px !important;padding-bottom:40px !important}.p-y-41{padding-top:41px !important;padding-bottom:41px !important}.p-y-42{padding-top:42px !important;padding-bottom:42px !important}.p-y-43{padding-top:43px !important;padding-bottom:43px !important}.p-y-44{padding-top:44px !important;padding-bottom:44px !important}.p-y-45{padding-top:45px !important;padding-bottom:45px !important}.p-y-46{padding-top:46px !important;padding-bottom:46px !important}.p-y-47{padding-top:47px !important;padding-bottom:47px !important}.p-y-48{padding-top:48px !important;padding-bottom:48px !important}.p-y-49{padding-top:49px !important;padding-bottom:49px !important}.p-y-50{padding-top:50px !important;padding-bottom:50px !important}.p-y-51{padding-top:51px !important;padding-bottom:51px !important}.p-y-52{padding-top:52px !important;padding-bottom:52px !important}.p-y-53{padding-top:53px !important;padding-bottom:53px !important}.p-y-54{padding-top:54px !important;padding-bottom:54px !important}.p-y-55{padding-top:55px !important;padding-bottom:55px !important}.p-y-56{padding-top:56px !important;padding-bottom:56px !important}.p-y-57{padding-top:57px !important;padding-bottom:57px !important}.p-y-58{padding-top:58px !important;padding-bottom:58px !important}.p-y-59{padding-top:59px !important;padding-bottom:59px !important}.p-y-60{padding-top:60px !important;padding-bottom:60px !important}.p-y-61{padding-top:61px !important;padding-bottom:61px !important}.p-y-62{padding-top:62px !important;padding-bottom:62px !important}.p-y-63{padding-top:63px !important;padding-bottom:63px !important}.p-y-64{padding-top:64px !important;padding-bottom:64px !important}.p-y-65{padding-top:65px !important;padding-bottom:65px !important}.p-y-66{padding-top:66px !important;padding-bottom:66px !important}.p-y-67{padding-top:67px !important;padding-bottom:67px !important}.p-y-68{padding-top:68px !important;padding-bottom:68px !important}.p-y-69{padding-top:69px !important;padding-bottom:69px !important}.p-y-70{padding-top:70px !important;padding-bottom:70px !important}.p-y-71{padding-top:71px !important;padding-bottom:71px !important}.p-y-72{padding-top:72px !important;padding-bottom:72px !important}.p-y-73{padding-top:73px !important;padding-bottom:73px !important}.p-y-74{padding-top:74px !important;padding-bottom:74px !important}.p-y-75{padding-top:75px !important;padding-bottom:75px !important}.p-y-76{padding-top:76px !important;padding-bottom:76px !important}.p-y-77{padding-top:77px !important;padding-bottom:77px !important}.p-y-78{padding-top:78px !important;padding-bottom:78px !important}.p-y-79{padding-top:79px !important;padding-bottom:79px !important}.p-y-80{padding-top:80px !important;padding-bottom:80px !important}.p-y-81{padding-top:81px !important;padding-bottom:81px !important}.p-y-82{padding-top:82px !important;padding-bottom:82px !important}.p-y-83{padding-top:83px !important;padding-bottom:83px !important}.p-y-84{padding-top:84px !important;padding-bottom:84px !important}.p-y-85{padding-top:85px !important;padding-bottom:85px !important}.p-y-86{padding-top:86px !important;padding-bottom:86px !important}.p-y-87{padding-top:87px !important;padding-bottom:87px !important}.p-y-88{padding-top:88px !important;padding-bottom:88px !important}.p-y-89{padding-top:89px !important;padding-bottom:89px !important}.p-y-90{padding-top:90px !important;padding-bottom:90px !important}.p-y-91{padding-top:91px !important;padding-bottom:91px !important}.p-y-92{padding-top:92px !important;padding-bottom:92px !important}.p-y-93{padding-top:93px !important;padding-bottom:93px !important}.p-y-94{padding-top:94px !important;padding-bottom:94px !important}.p-y-95{padding-top:95px !important;padding-bottom:95px !important}.p-y-96{padding-top:96px !important;padding-bottom:96px !important}.p-y-97{padding-top:97px !important;padding-bottom:97px !important}.p-y-98{padding-top:98px !important;padding-bottom:98px !important}.p-y-99{padding-top:99px !important;padding-bottom:99px !important}.p-y-100{padding-top:100px !important;padding-bottom:100px !important}.upper{text-transform:uppercase}.lower{text-transform:lowercase}.cap{text-transform:capitalize}.font-12{font-size:12px !important}.bold{font-weight:bold}.flex{display:flex !important}.flex-auto{flex:auto !important}.flex-1{flex:1 !important}.flex-2{flex:2 !important}.flex-3{flex:3 !important}.flex-4{flex:4 !important}.flex-column{flex-direction:column !important}.flex-column-column-reverse{flex-direction:column-reverse !important}@media(max-width: 899px){.tablet-down-flex-column{flex-direction:column !important}}.flex-row{flex-direction:row !important}.flex-row-reversed{flex-direction:row-reverse !important}.space-between{justify-content:space-between !important}@media(min-width: 900px){.space-between-small-desktop-up{justify-content:space-between !important}}@media(max-width: 1199px){.space-between-small-desktop-down{justify-content:space-between !important}}@media(max-width: 899px){.space-between-tablet-down{justify-content:space-between !important}}@media(min-width: 0px)and (max-width: 599px){.space-between-mobile{justify-content:space-between !important}}@media(min-width: 0px)and (max-width: 599px){.xs-space-between{justify-content:space-between !important}}@media(min-width: 600px)and (max-width: 899px){.sm-space-between{justify-content:space-between !important}}@media(min-width: 900px)and (max-width: 1199px){.md-space-between{justify-content:space-between !important}}@media(min-width: 1200px)and (max-width: 1439px){.lg-space-between{justify-content:space-between !important}}@media(min-width: 1440px){.xl-space-between{justify-content:space-between !important}}.space-around{justify-content:space-around !important}.align-items-center{align-items:center !important}.align-items-flex-end{align-items:flex-end !important}.align-self-center{align-self:center !important}.align-self-start{align-self:flex-start !important}.align-content-center{align-content:center !important}.justify-content-center{justify-content:center !important}.flex-wrap{flex-wrap:wrap !important}.w-auto{min-width:auto !important;width:auto !important}.w-full{width:100% !important}.w-fitcontent{width:fit-content !important}.w-0{width:0px !important}.min-w-0{min-width:0px !important}.max-w-0{max-width:0px !important}.w-1{width:5px !important}.min-w-1{min-width:5px !important}.max-w-1{max-width:5px !important}.w-2{width:10px !important}.min-w-2{min-width:10px !important}.max-w-2{max-width:10px !important}.w-3{width:15px !important}.min-w-3{min-width:15px !important}.max-w-3{max-width:15px !important}.w-4{width:20px !important}.min-w-4{min-width:20px !important}.max-w-4{max-width:20px !important}.w-5{width:25px !important}.min-w-5{min-width:25px !important}.max-w-5{max-width:25px !important}.w-6{width:30px !important}.min-w-6{min-width:30px !important}.max-w-6{max-width:30px !important}.w-7{width:35px !important}.min-w-7{min-width:35px !important}.max-w-7{max-width:35px !important}.w-8{width:40px !important}.min-w-8{min-width:40px !important}.max-w-8{max-width:40px !important}.w-9{width:45px !important}.min-w-9{min-width:45px !important}.max-w-9{max-width:45px !important}.w-10{width:50px !important}.min-w-10{min-width:50px !important}.max-w-10{max-width:50px !important}.w-11{width:55px !important}.min-w-11{min-width:55px !important}.max-w-11{max-width:55px !important}.w-12{width:60px !important}.min-w-12{min-width:60px !important}.max-w-12{max-width:60px !important}.w-13{width:65px !important}.min-w-13{min-width:65px !important}.max-w-13{max-width:65px !important}.w-14{width:70px !important}.min-w-14{min-width:70px !important}.max-w-14{max-width:70px !important}.w-15{width:75px !important}.min-w-15{min-width:75px !important}.max-w-15{max-width:75px !important}.w-16{width:80px !important}.min-w-16{min-width:80px !important}.max-w-16{max-width:80px !important}.w-17{width:85px !important}.min-w-17{min-width:85px !important}.max-w-17{max-width:85px !important}.w-18{width:90px !important}.min-w-18{min-width:90px !important}.max-w-18{max-width:90px !important}.w-19{width:95px !important}.min-w-19{min-width:95px !important}.max-w-19{max-width:95px !important}.w-20{width:100px !important}.min-w-20{min-width:100px !important}.max-w-20{max-width:100px !important}.w-21{width:105px !important}.min-w-21{min-width:105px !important}.max-w-21{max-width:105px !important}.w-22{width:110px !important}.min-w-22{min-width:110px !important}.max-w-22{max-width:110px !important}.w-23{width:115px !important}.min-w-23{min-width:115px !important}.max-w-23{max-width:115px !important}.w-24{width:120px !important}.min-w-24{min-width:120px !important}.max-w-24{max-width:120px !important}.w-25{width:125px !important}.min-w-25{min-width:125px !important}.max-w-25{max-width:125px !important}.w-26{width:130px !important}.min-w-26{min-width:130px !important}.max-w-26{max-width:130px !important}.w-27{width:135px !important}.min-w-27{min-width:135px !important}.max-w-27{max-width:135px !important}.w-28{width:140px !important}.min-w-28{min-width:140px !important}.max-w-28{max-width:140px !important}.w-29{width:145px !important}.min-w-29{min-width:145px !important}.max-w-29{max-width:145px !important}.w-30{width:150px !important}.min-w-30{min-width:150px !important}.max-w-30{max-width:150px !important}.w-31{width:155px !important}.min-w-31{min-width:155px !important}.max-w-31{max-width:155px !important}.w-32{width:160px !important}.min-w-32{min-width:160px !important}.max-w-32{max-width:160px !important}.w-33{width:165px !important}.min-w-33{min-width:165px !important}.max-w-33{max-width:165px !important}.w-34{width:170px !important}.min-w-34{min-width:170px !important}.max-w-34{max-width:170px !important}.w-35{width:175px !important}.min-w-35{min-width:175px !important}.max-w-35{max-width:175px !important}.w-36{width:180px !important}.min-w-36{min-width:180px !important}.max-w-36{max-width:180px !important}.w-37{width:185px !important}.min-w-37{min-width:185px !important}.max-w-37{max-width:185px !important}.w-38{width:190px !important}.min-w-38{min-width:190px !important}.max-w-38{max-width:190px !important}.w-39{width:195px !important}.min-w-39{min-width:195px !important}.max-w-39{max-width:195px !important}.w-40{width:200px !important}.min-w-40{min-width:200px !important}.max-w-40{max-width:200px !important}.w-41{width:205px !important}.min-w-41{min-width:205px !important}.max-w-41{max-width:205px !important}.w-42{width:210px !important}.min-w-42{min-width:210px !important}.max-w-42{max-width:210px !important}.w-43{width:215px !important}.min-w-43{min-width:215px !important}.max-w-43{max-width:215px !important}.w-44{width:220px !important}.min-w-44{min-width:220px !important}.max-w-44{max-width:220px !important}.w-45{width:225px !important}.min-w-45{min-width:225px !important}.max-w-45{max-width:225px !important}.w-46{width:230px !important}.min-w-46{min-width:230px !important}.max-w-46{max-width:230px !important}.w-47{width:235px !important}.min-w-47{min-width:235px !important}.max-w-47{max-width:235px !important}.w-48{width:240px !important}.min-w-48{min-width:240px !important}.max-w-48{max-width:240px !important}.w-49{width:245px !important}.min-w-49{min-width:245px !important}.max-w-49{max-width:245px !important}.w-50{width:250px !important}.min-w-50{min-width:250px !important}.max-w-50{max-width:250px !important}.h-minus-nav{height:calc(100vh - 60px) !important}.h-full{height:100% !important}.h-0{height:0px !important}.min-h-0{min-height:0px !important}.max-w-0{max-width:0px !important}.h-1{height:5px !important}.min-h-1{min-height:5px !important}.max-w-1{max-width:5px !important}.h-2{height:10px !important}.min-h-2{min-height:10px !important}.max-w-2{max-width:10px !important}.h-3{height:15px !important}.min-h-3{min-height:15px !important}.max-w-3{max-width:15px !important}.h-4{height:20px !important}.min-h-4{min-height:20px !important}.max-w-4{max-width:20px !important}.h-5{height:25px !important}.min-h-5{min-height:25px !important}.max-w-5{max-width:25px !important}.h-6{height:30px !important}.min-h-6{min-height:30px !important}.max-w-6{max-width:30px !important}.h-7{height:35px !important}.min-h-7{min-height:35px !important}.max-w-7{max-width:35px !important}.h-8{height:40px !important}.min-h-8{min-height:40px !important}.max-w-8{max-width:40px !important}.h-9{height:45px !important}.min-h-9{min-height:45px !important}.max-w-9{max-width:45px !important}.h-10{height:50px !important}.min-h-10{min-height:50px !important}.max-w-10{max-width:50px !important}.h-11{height:55px !important}.min-h-11{min-height:55px !important}.max-w-11{max-width:55px !important}.h-12{height:60px !important}.min-h-12{min-height:60px !important}.max-w-12{max-width:60px !important}.h-13{height:65px !important}.min-h-13{min-height:65px !important}.max-w-13{max-width:65px !important}.h-14{height:70px !important}.min-h-14{min-height:70px !important}.max-w-14{max-width:70px !important}.h-15{height:75px !important}.min-h-15{min-height:75px !important}.max-w-15{max-width:75px !important}.h-16{height:80px !important}.min-h-16{min-height:80px !important}.max-w-16{max-width:80px !important}.h-17{height:85px !important}.min-h-17{min-height:85px !important}.max-w-17{max-width:85px !important}.h-18{height:90px !important}.min-h-18{min-height:90px !important}.max-w-18{max-width:90px !important}.h-19{height:95px !important}.min-h-19{min-height:95px !important}.max-w-19{max-width:95px !important}.h-20{height:100px !important}.min-h-20{min-height:100px !important}.max-w-20{max-width:100px !important}.h-21{height:105px !important}.min-h-21{min-height:105px !important}.max-w-21{max-width:105px !important}.h-22{height:110px !important}.min-h-22{min-height:110px !important}.max-w-22{max-width:110px !important}.h-23{height:115px !important}.min-h-23{min-height:115px !important}.max-w-23{max-width:115px !important}.h-24{height:120px !important}.min-h-24{min-height:120px !important}.max-w-24{max-width:120px !important}.h-25{height:125px !important}.min-h-25{min-height:125px !important}.max-w-25{max-width:125px !important}.h-26{height:130px !important}.min-h-26{min-height:130px !important}.max-w-26{max-width:130px !important}.h-27{height:135px !important}.min-h-27{min-height:135px !important}.max-w-27{max-width:135px !important}.h-28{height:140px !important}.min-h-28{min-height:140px !important}.max-w-28{max-width:140px !important}.h-29{height:145px !important}.min-h-29{min-height:145px !important}.max-w-29{max-width:145px !important}.h-30{height:150px !important}.min-h-30{min-height:150px !important}.max-w-30{max-width:150px !important}.h-31{height:155px !important}.min-h-31{min-height:155px !important}.max-w-31{max-width:155px !important}.h-32{height:160px !important}.min-h-32{min-height:160px !important}.max-w-32{max-width:160px !important}.h-33{height:165px !important}.min-h-33{min-height:165px !important}.max-w-33{max-width:165px !important}.h-34{height:170px !important}.min-h-34{min-height:170px !important}.max-w-34{max-width:170px !important}.h-35{height:175px !important}.min-h-35{min-height:175px !important}.max-w-35{max-width:175px !important}.h-36{height:180px !important}.min-h-36{min-height:180px !important}.max-w-36{max-width:180px !important}.h-37{height:185px !important}.min-h-37{min-height:185px !important}.max-w-37{max-width:185px !important}.h-38{height:190px !important}.min-h-38{min-height:190px !important}.max-w-38{max-width:190px !important}.h-39{height:195px !important}.min-h-39{min-height:195px !important}.max-w-39{max-width:195px !important}.h-40{height:200px !important}.min-h-40{min-height:200px !important}.max-w-40{max-width:200px !important}.h-41{height:205px !important}.min-h-41{min-height:205px !important}.max-w-41{max-width:205px !important}.h-42{height:210px !important}.min-h-42{min-height:210px !important}.max-w-42{max-width:210px !important}.h-43{height:215px !important}.min-h-43{min-height:215px !important}.max-w-43{max-width:215px !important}.h-44{height:220px !important}.min-h-44{min-height:220px !important}.max-w-44{max-width:220px !important}.h-45{height:225px !important}.min-h-45{min-height:225px !important}.max-w-45{max-width:225px !important}.h-46{height:230px !important}.min-h-46{min-height:230px !important}.max-w-46{max-width:230px !important}.h-47{height:235px !important}.min-h-47{min-height:235px !important}.max-w-47{max-width:235px !important}.h-48{height:240px !important}.min-h-48{min-height:240px !important}.max-w-48{max-width:240px !important}.h-49{height:245px !important}.min-h-49{min-height:245px !important}.max-w-49{max-width:245px !important}.h-50{height:250px !important}.min-h-50{min-height:250px !important}.max-w-50{max-width:250px !important}.hide{display:none !important}@media(min-width: 900px){.hide-small-desktop-up{display:none !important}}@media(max-width: 1199px){.hide-small-desktop-down{display:none !important}}@media(max-width: 899px){.hide-tablet-down{display:none !important}}@media(min-width: 0px)and (max-width: 599px){.hide-mobile{display:none !important}}@media(min-width: 0px)and (max-width: 599px){.xs-hide{display:none !important}}@media(min-width: 600px)and (max-width: 899px){.sm-hide{display:none !important}}@media(min-width: 900px)and (max-width: 1199px){.md-hide{display:none !important}}@media(min-width: 1200px)and (max-width: 1439px){.lg-hide{display:none !important}}@media(min-width: 1440px){.xl-hide{display:none !important}}.br-1{border-radius:1px !important}.br-2{border-radius:2px !important}.br-3{border-radius:3px !important}.br-4{border-radius:4px !important}.br-5{border-radius:5px !important}.br-6{border-radius:6px !important}.br-7{border-radius:7px !important}.br-8{border-radius:8px !important}.br-9{border-radius:9px !important}.br-10{border-radius:10px !important}.br-11{border-radius:11px !important}.br-12{border-radius:12px !important}.br-13{border-radius:13px !important}.br-14{border-radius:14px !important}.br-15{border-radius:15px !important}.br-circle{border-radius:100% !important}.overflow-y-auto{overflow-y:auto !important;-webkit-overflow-scrolling:touch}.overflow-hidden{overflow:hidden !important}.sticky-top{position:sticky !important;top:0 !important}.position-relative{position:relative !important}.position-absolute{position:absolute !important}.position-fixed{position:fixed !important}.top-0{top:0px !important}.bottom-0{bottom:0px !important}.right-0{right:0px !important}.left-0{left:0px !important}.top-1{top:5px !important}.bottom-1{bottom:5px !important}.right-1{right:5px !important}.left-1{left:5px !important}.top-2{top:10px !important}.bottom-2{bottom:10px !important}.right-2{right:10px !important}.left-2{left:10px !important}.top-3{top:15px !important}.bottom-3{bottom:15px !important}.right-3{right:15px !important}.left-3{left:15px !important}.top-4{top:20px !important}.bottom-4{bottom:20px !important}.right-4{right:20px !important}.left-4{left:20px !important}.top-5{top:25px !important}.bottom-5{bottom:25px !important}.right-5{right:25px !important}.left-5{left:25px !important}.top-6{top:30px !important}.bottom-6{bottom:30px !important}.right-6{right:30px !important}.left-6{left:30px !important}.top-7{top:35px !important}.bottom-7{bottom:35px !important}.right-7{right:35px !important}.left-7{left:35px !important}.top-8{top:40px !important}.bottom-8{bottom:40px !important}.right-8{right:40px !important}.left-8{left:40px !important}.top-9{top:45px !important}.bottom-9{bottom:45px !important}.right-9{right:45px !important}.left-9{left:45px !important}.top-10{top:50px !important}.bottom-10{bottom:50px !important}.right-10{right:50px !important}.left-10{left:50px !important}.top-11{top:55px !important}.bottom-11{bottom:55px !important}.right-11{right:55px !important}.left-11{left:55px !important}.top-12{top:60px !important}.bottom-12{bottom:60px !important}.right-12{right:60px !important}.left-12{left:60px !important}.top-13{top:65px !important}.bottom-13{bottom:65px !important}.right-13{right:65px !important}.left-13{left:65px !important}.top-14{top:70px !important}.bottom-14{bottom:70px !important}.right-14{right:70px !important}.left-14{left:70px !important}.top-15{top:75px !important}.bottom-15{bottom:75px !important}.right-15{right:75px !important}.left-15{left:75px !important}.top-16{top:80px !important}.bottom-16{bottom:80px !important}.right-16{right:80px !important}.left-16{left:80px !important}.top-17{top:85px !important}.bottom-17{bottom:85px !important}.right-17{right:85px !important}.left-17{left:85px !important}.top-18{top:90px !important}.bottom-18{bottom:90px !important}.right-18{right:90px !important}.left-18{left:90px !important}.top-19{top:95px !important}.bottom-19{bottom:95px !important}.right-19{right:95px !important}.left-19{left:95px !important}.top-20{top:100px !important}.bottom-20{bottom:100px !important}.right-20{right:100px !important}.left-20{left:100px !important}.top-21{top:105px !important}.bottom-21{bottom:105px !important}.right-21{right:105px !important}.left-21{left:105px !important}.top-22{top:110px !important}.bottom-22{bottom:110px !important}.right-22{right:110px !important}.left-22{left:110px !important}.top-23{top:115px !important}.bottom-23{bottom:115px !important}.right-23{right:115px !important}.left-23{left:115px !important}.top-24{top:120px !important}.bottom-24{bottom:120px !important}.right-24{right:120px !important}.left-24{left:120px !important}.top-25{top:125px !important}.bottom-25{bottom:125px !important}.right-25{right:125px !important}.left-25{left:125px !important}.top-26{top:130px !important}.bottom-26{bottom:130px !important}.right-26{right:130px !important}.left-26{left:130px !important}.top-27{top:135px !important}.bottom-27{bottom:135px !important}.right-27{right:135px !important}.left-27{left:135px !important}.top-28{top:140px !important}.bottom-28{bottom:140px !important}.right-28{right:140px !important}.left-28{left:140px !important}.top-29{top:145px !important}.bottom-29{bottom:145px !important}.right-29{right:145px !important}.left-29{left:145px !important}.top-30{top:150px !important}.bottom-30{bottom:150px !important}.right-30{right:150px !important}.left-30{left:150px !important}.top-31{top:155px !important}.bottom-31{bottom:155px !important}.right-31{right:155px !important}.left-31{left:155px !important}.top-32{top:160px !important}.bottom-32{bottom:160px !important}.right-32{right:160px !important}.left-32{left:160px !important}.top-33{top:165px !important}.bottom-33{bottom:165px !important}.right-33{right:165px !important}.left-33{left:165px !important}.top-34{top:170px !important}.bottom-34{bottom:170px !important}.right-34{right:170px !important}.left-34{left:170px !important}.top-35{top:175px !important}.bottom-35{bottom:175px !important}.right-35{right:175px !important}.left-35{left:175px !important}.top-36{top:180px !important}.bottom-36{bottom:180px !important}.right-36{right:180px !important}.left-36{left:180px !important}.top-37{top:185px !important}.bottom-37{bottom:185px !important}.right-37{right:185px !important}.left-37{left:185px !important}.top-38{top:190px !important}.bottom-38{bottom:190px !important}.right-38{right:190px !important}.left-38{left:190px !important}.top-39{top:195px !important}.bottom-39{bottom:195px !important}.right-39{right:195px !important}.left-39{left:195px !important}.top-40{top:200px !important}.bottom-40{bottom:200px !important}.right-40{right:200px !important}.left-40{left:200px !important}.top-41{top:205px !important}.bottom-41{bottom:205px !important}.right-41{right:205px !important}.left-41{left:205px !important}.top-42{top:210px !important}.bottom-42{bottom:210px !important}.right-42{right:210px !important}.left-42{left:210px !important}.top-43{top:215px !important}.bottom-43{bottom:215px !important}.right-43{right:215px !important}.left-43{left:215px !important}.top-44{top:220px !important}.bottom-44{bottom:220px !important}.right-44{right:220px !important}.left-44{left:220px !important}.top-45{top:225px !important}.bottom-45{bottom:225px !important}.right-45{right:225px !important}.left-45{left:225px !important}.top-46{top:230px !important}.bottom-46{bottom:230px !important}.right-46{right:230px !important}.left-46{left:230px !important}.top-47{top:235px !important}.bottom-47{bottom:235px !important}.right-47{right:235px !important}.left-47{left:235px !important}.top-48{top:240px !important}.bottom-48{bottom:240px !important}.right-48{right:240px !important}.left-48{left:240px !important}.top-49{top:245px !important}.bottom-49{bottom:245px !important}.right-49{right:245px !important}.left-49{left:245px !important}.top-50{top:250px !important}.bottom-50{bottom:250px !important}.right-50{right:250px !important}.left-50{left:250px !important}.bg-origin-padding-box{background-origin:padding-box !important}.bg-origin-border-box{background-origin:border-box !important}.bg-origin-content-box{background-origin:content-box !important}.bg-clip-padding-box{background-clip:padding-box !important}.bg-clip-border-box{background-clip:border-box !important}.bg-clip-content-box{background-clip:content-box !important}.bg-attachment-scroll{background-attachment:scroll !important}.bg-attachment-fixed{background-attachment:fixed !important}.bg-attachment-local{background-attachment:local !important}.bg-size-auto{background-size:auto !important}.bg-size-cover{background-size:cover !important}.bg-size-contain{background-size:contain !important}.bg-position-left{background-position:left !important}.bg-position-left-top{background-position:left-top !important}.bg-position-left-center{background-position:left-center !important}.bg-position-left-bottom{background-position:left-bottom !important}.bg-position-right{background-position:right !important}.bg-position-right-top{background-position:right-top !important}.bg-position-right-center{background-position:right-center !important}.bg-position-right-bottom{background-position:right-bottom !important}.bg-position-center{background-position:center !important}.bg-position-center-top{background-position:center-top !important}.bg-position-center-center{background-position:center-center !important}.bg-position-center-bottom{background-position:center-bottom !important}.bg-no-repeat{background-repeat:no-repeat !important}.bg-repeat{background-repeat:repeat !important}.bg-repeat-x{background-repeat:repeat-x !important}.bg-repeat-y{background-repeat:repeat-y !important}.bg-repeat-space{background-repeat:space !important}.bg-repeat-round{background-repeat:round !important}.is-italic{font-style:italic !important}.is-light{font-weight:100 !important}.is-regular{font-weight:400 !important}.is-bold{font-weight:600 !important}.is-mono{font-family:monospace !important}.line-height-compact{line-height:1 !important}.float-right{float:right}.float-left{float:left}.shadow-1{box-shadow:0 50px 20px -50px rgba(0,0,0,.1),0 8px 20px 0 rgba(0,0,0,.1)}.shadow-2{box-shadow:0 50px 30px -50px rgba(0,0,0,.1),0 10px 30px 0 rgba(0,0,0,.1)}.shadow-3{box-shadow:0 50px 40px -50px rgba(0,0,0,.1),0 15px 40px 0 rgba(0,0,0,.1)}.shadow-4{box-shadow:0 50px 50px -50px rgba(0,0,0,.1),0 20px 50px 0 rgba(0,0,0,.1)}.shadow-5{box-shadow:0 50px 60px -50px rgba(0,0,0,.1),0 25px 60px 0 rgba(0,0,0,.1)}body{font-weight:400;line-height:1.5;font-size:1.4rem;background:#fff;color:#303030;font-family:Helvetica,"Arial",sans-serif}[disabled]{cursor:not-allowed}hr{background-color:#d5d5d5;display:block;border:0;height:1px;margin:15px 0}.accordion{width:100%}.accordion details>summary{list-style-type:none}.accordion details>summary::-webkit-details-marker{display:none}.accordion.has-icon>details>summary::before{content:"";height:20px;width:20px;right:10px;position:absolute;background-image:url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0OCIgaGVpZ2h0PSI0OCIgdmlld0JveD0iMCAwIDQ4IDQ4Ij4KICA8dGl0bGU+YXJyb3doZWFkLWRvd248L3RpdGxlPgogIDxnIGlkPSJMYXllcl8yIiBkYXRhLW5hbWU9IkxheWVyIDIiPgogICAgPGcgaWQ9ImludmlzaWJsZV9ib3giIGRhdGEtbmFtZT0iaW52aXNpYmxlIGJveCI+CiAgICAgIDxyZWN0IHdpZHRoPSI0OCIgaGVpZ2h0PSI0OCIgZmlsbD0ibm9uZSIvPgogICAgPC9nPgogICAgPGcgaWQ9Imljb25zX1EyIiBkYXRhLW5hbWU9Imljb25zIFEyIj4KICAgICAgPHBhdGggZD0iTTI0LDI3LjIsMTMuNCwxNi42YTEuOSwxLjksMCwwLDAtMywuMiwyLjEsMi4xLDAsMCwwLC4yLDIuN2wxMiwxMS45YTEuOSwxLjksMCwwLDAsMi44LDBsMTItMTEuOWEyLjEsMi4xLDAsMCwwLC4yLTIuNywxLjksMS45LDAsMCwwLTMtLjJaIi8+CiAgICA8L2c+CiAgPC9nPgo8L3N2Zz4K");background-repeat:no-repeat;background-size:20px}.accordion.has-icon>details[open]>summary::before{content:"";height:20px;width:20px;right:10px;position:absolute;background-image:url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0OCIgaGVpZ2h0PSI0OCIgdmlld0JveD0iMCAwIDQ4IDQ4Ij4KICA8dGl0bGU+YXJyb3doZWFkLXVwPC90aXRsZT4KICA8ZyBpZD0iTGF5ZXJfMiIgZGF0YS1uYW1lPSJMYXllciAyIj4KICAgIDxnIGlkPSJpbnZpc2libGVfYm94IiBkYXRhLW5hbWU9ImludmlzaWJsZSBib3giPgogICAgICA8cmVjdCB3aWR0aD0iNDgiIGhlaWdodD0iNDgiIGZpbGw9Im5vbmUiLz4KICAgIDwvZz4KICAgIDxnIGlkPSJpY29uc19RMiIgZGF0YS1uYW1lPSJpY29ucyBRMiI+CiAgICAgIDxwYXRoIGQ9Ik0yNCwyMC44LDEzLjQsMzEuNGExLjksMS45LDAsMCwxLTMtLjIsMi4xLDIuMSwwLDAsMSwuMi0yLjdsMTItMTEuOWExLjksMS45LDAsMCwxLDIuOCwwbDEyLDExLjlhMi4xLDIuMSwwLDAsMSwuMiwyLjcsMS45LDEuOSwwLDAsMS0zLC4yWiIvPgogICAgPC9nPgogIDwvZz4KPC9zdmc+Cg==");background-repeat:no-repeat;background-size:20px}.accordion summary{background:#f8f8f8;position:relative;font-family:Helvetica-Bold;font-size:14px;color:#303030;line-height:18px;height:auto;padding:15px 15px;cursor:pointer;transition:.3s;border:1px solid #e8e8e8}.accordion summary:hover{background:#fff}.accordion .content{padding:15px 15px;background:#fff;border:1px solid #e8e8e8;border-top:none}.accordion.is-inverse summary:hover{background:#727272;color:#d8d8d8}.accordion.is-inverse summary{background:#454545;color:#fff;border:1px solid #f8f8f8}.accordion.is-inverse .content{background:#303030;color:#fff;border:1px solid #f8f8f8}.accordion.is-inverse.has-icon>details>summary::before{background-image:url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0OCIgaGVpZ2h0PSI0OCIgdmlld0JveD0iMCAwIDQ4IDQ4Ij4KICA8dGl0bGU+YXJyb3doZWFkLWRvd248L3RpdGxlPgogIDxnIGlkPSJMYXllcl8yIiBkYXRhLW5hbWU9IkxheWVyIDIiPgogICAgPGcgaWQ9ImludmlzaWJsZV9ib3giIGRhdGEtbmFtZT0iaW52aXNpYmxlIGJveCI+CiAgICAgIDxyZWN0IHdpZHRoPSI0OCIgaGVpZ2h0PSI0OCIgZmlsbD0ibm9uZSIvPgogICAgPC9nPgogICAgPGcgaWQ9Imljb25zX1EyIiBkYXRhLW5hbWU9Imljb25zIFEyIj4KICAgICAgPHBhdGggZmlsbD0iI2ZmZiIgZD0iTTI0LDI3LjIsMTMuNCwxNi42YTEuOSwxLjksMCwwLDAtMywuMiwyLjEsMi4xLDAsMCwwLC4yLDIuN2wxMiwxMS45YTEuOSwxLjksMCwwLDAsMi44LDBsMTItMTEuOWEyLjEsMi4xLDAsMCwwLC4yLTIuNywxLjksMS45LDAsMCwwLTMtLjJaIi8+CiAgICA8L2c+CiAgPC9nPgo8L3N2Zz4K")}.accordion.is-inverse.has-icon>details[open]>summary::before{background-image:url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0OCIgaGVpZ2h0PSI0OCIgdmlld0JveD0iMCAwIDQ4IDQ4Ij4KICA8dGl0bGU+YXJyb3doZWFkLXVwPC90aXRsZT4KICA8ZyBpZD0iTGF5ZXJfMiIgZGF0YS1uYW1lPSJMYXllciAyIj4KICAgIDxnIGlkPSJpbnZpc2libGVfYm94IiBkYXRhLW5hbWU9ImludmlzaWJsZSBib3giPgogICAgICA8cmVjdCB3aWR0aD0iNDgiIGhlaWdodD0iNDgiIGZpbGw9Im5vbmUiLz4KICAgIDwvZz4KICAgIDxnIGlkPSJpY29uc19RMiIgZGF0YS1uYW1lPSJpY29ucyBRMiIgPgogICAgICA8cGF0aCBmaWxsPSIjZmZmIiBkPSJNMjQsMjAuOCwxMy40LDMxLjRhMS45LDEuOSwwLDAsMS0zLS4yLDIuMSwyLjEsMCwwLDEsLjItMi43bDEyLTExLjlhMS45LDEuOSwwLDAsMSwyLjgsMGwxMiwxMS45YTIuMSwyLjEsMCwwLDEsLjIsMi43LDEuOSwxLjksMCwwLDEtMywuMloiLz4KICAgIDwvZz4KICA8L2c+Cjwvc3ZnPgo=")}.alert{width:400px;display:flex;box-sizing:border-box;background:#fff;border-radius:4px;box-shadow:0px 4px 8px rgba(0,0,0,.16);border-top-width:4px;border-top-style:solid;border-top-color:#d5d5d5}@media(max-width: 599px){.alert{width:100%}}.alert div{margin:17px 0px 18px 0px;flex:auto;overflow:hidden}.alert div h5{margin-bottom:8px}.alert div p{margin-top:4px;font-size:12px !important;margin-bottom:0px;line-height:1.25}.alert .d-icon{margin:14px;font-size:28px !important;color:#d5d5d5}.alert .close{display:block;padding:16px 0px}.alert .close .d-icon{font-size:24px !important;color:#303030 !important}.alert.is-inline{justify-content:center;padding:4px 10px;background:#f2f2f2;color:#303030;font-size:inherit;box-shadow:none !important;border:none !important;border-radius:0px !important;width:100%;display:flex !important}.alert.is-inline p{text-align:center;overflow:hidden}.alert.is-block{border-top:none;position:relative;box-shadow:none !important;width:100%;background:rgba(213,213,213,.1);border-left-width:4px;border-left-style:solid;border-left-color:#d5d5d5}.alert.is-block.is-info{background:rgba(0,102,223,.06)}.alert.is-block.is-warning{background:rgba(244,203,0,.06)}.alert.is-block.is-success{background:rgba(21,192,70,.06)}.alert.is-block.is-danger{background:rgba(234,0,0,.06)}.alert.is-block p{font-size:1.4rem !important}.alert.is-info{border-color:#0066df}.alert.is-info .d-icon{color:#0066df}.alert.is-warning{border-color:#f4cb00}.alert.is-warning .d-icon{color:#f4cb00}.alert.is-success{border-color:#15c046}.alert.is-success .d-icon{color:#15c046}.alert.is-danger{border-color:#ea0000}.alert.is-danger .d-icon{color:#ea0000}.box{padding:20px;background:#f8f8f8;border-radius:4px;border-color:#d5d5d5;border-width:1px;border-style:solid}.box footer{margin:22px -20px 0px -20px;padding:22px 22px 0px 22px;border-top-style:solid;border-top-color:#d5d5d5;border-top-width:1px}.breadcrumb{list-style-type:none;margin:0;padding:0}.breadcrumb li{float:left;position:relative;margin-right:22px}.breadcrumb li>*{display:block}.breadcrumb li.is-active>*{color:#303030;font-weight:bold}.breadcrumb li::after{content:"/";position:absolute;height:1.6rem;width:.4rem;top:0;right:-11px;color:rgba(48,48,48,.5)}.breadcrumb li:last-child{margin-right:0px}.breadcrumb li:last-child::after{display:none}.button{border:none;outline:0;cursor:pointer;box-sizing:border-box;display:inline-flex;justify-content:center;align-items:center;margin:0px;padding:0px 10px;padding-bottom:1px;height:36px;font-size:inherit;min-width:120px;border-radius:4px;transition:background 300ms;font-weight:inherit}.button.is-solid{background:#016eff;color:#fff}.button.is-solid.is-inverse{background:#016eff;color:#fff}.button.is-outline{background:transparent;color:#016eff;border:solid 1px #016eff}.button.is-outline.is-inverse{background:transparent;color:#fff;border:solid 1px #016eff}.button.is-ghost{background:rgba(1,110,255,.16);color:#016eff}.button.is-ghost.is-inverse{background:rgba(1,110,255,.16);color:#fff}.button.is-text{background:transparent;color:#016eff}.button.is-text.is-inverse{background:transparent;color:#fff}.button.is-danger{background:#ea0000;color:#fff}.button.is-danger.is-inverse{background:#ea0000;color:#fff}.button:hover,.button.is-active,.button.is-active:hover,.button:focus{transition:background 300ms}.button:hover.is-solid,.button.is-active.is-solid,.button.is-active:hover.is-solid,.button:focus.is-solid{background:#004cb3;color:#fff !important}.button:hover.is-solid.is-inverse,.button.is-active.is-solid.is-inverse,.button.is-active:hover.is-solid.is-inverse,.button:focus.is-solid.is-inverse{background:#004cb3;color:#fff !important}.button:hover.is-outline,.button.is-active.is-outline,.button.is-active:hover.is-outline,.button:focus.is-outline{background:rgba(0,76,179,.26);color:#004cb3 !important;border:solid 1px #004cb3}.button:hover.is-outline.is-inverse,.button.is-active.is-outline.is-inverse,.button.is-active:hover.is-outline.is-inverse,.button:focus.is-outline.is-inverse{background:rgba(0,76,179,.26);color:#fff !important;border:solid 1px #80b6ff}.button:hover.is-ghost,.button.is-active.is-ghost,.button.is-active:hover.is-ghost,.button:focus.is-ghost{background:rgba(0,76,179,.26);color:#004cb3 !important}.button:hover.is-ghost.is-inverse,.button.is-active.is-ghost.is-inverse,.button.is-active:hover.is-ghost.is-inverse,.button:focus.is-ghost.is-inverse{background:rgba(0,76,179,.26);color:#fff !important}.button:hover.is-text,.button.is-active.is-text,.button.is-active:hover.is-text,.button:focus.is-text{background:rgba(0,76,179,.26);color:#004cb3 !important}.button:hover.is-text.is-inverse,.button.is-active.is-text.is-inverse,.button.is-active:hover.is-text.is-inverse,.button:focus.is-text.is-inverse{background:rgba(0,76,179,.26);color:#fff !important}.button:hover.is-danger,.button.is-active.is-danger,.button.is-active:hover.is-danger,.button:focus.is-danger{background:#b00;color:#fff !important}.button:hover.is-danger.is-inverse,.button.is-active.is-danger.is-inverse,.button.is-active:hover.is-danger.is-inverse,.button:focus.is-danger.is-inverse{background:#b00;color:#fff !important}.button.is-disabled,.button[disabled],.button.is-disabled:hover,.button:hover[disabled]{box-shadow:none;cursor:not-allowed}.button.is-disabled.is-solid,.button[disabled].is-solid,.button.is-disabled:hover.is-solid,.button:hover[disabled].is-solid{background:rgba(48,48,48,.12);color:rgba(48,48,48,.5) !important}.button.is-disabled.is-solid.is-inverse,.button[disabled].is-solid.is-inverse,.button.is-disabled:hover.is-solid.is-inverse,.button:hover[disabled].is-solid.is-inverse{background:rgba(255,255,255,.12);color:rgba(255,255,255,.5) !important}.button.is-disabled.is-outline,.button[disabled].is-outline,.button.is-disabled:hover.is-outline,.button:hover[disabled].is-outline{background:rgba(48,48,48,.12);color:rgba(48,48,48,.5) !important;border:solid 1px rgba(48,48,48,.5)}.button.is-disabled.is-outline.is-inverse,.button[disabled].is-outline.is-inverse,.button.is-disabled:hover.is-outline.is-inverse,.button:hover[disabled].is-outline.is-inverse{background:rgba(255,255,255,.12);color:rgba(255,255,255,.5) !important;border:solid 1px rgba(255,255,255,.5)}.button.is-disabled.is-ghost,.button[disabled].is-ghost,.button.is-disabled:hover.is-ghost,.button:hover[disabled].is-ghost{background:rgba(48,48,48,.12);color:rgba(48,48,48,.5) !important}.button.is-disabled.is-ghost.is-inverse,.button[disabled].is-ghost.is-inverse,.button.is-disabled:hover.is-ghost.is-inverse,.button:hover[disabled].is-ghost.is-inverse{background:rgba(255,255,255,.12);color:rgba(255,255,255,.5) !important}.button.is-disabled.is-text,.button[disabled].is-text,.button.is-disabled:hover.is-text,.button:hover[disabled].is-text{background:transparent;color:rgba(48,48,48,.5) !important}.button.is-disabled.is-text.is-inverse,.button[disabled].is-text.is-inverse,.button.is-disabled:hover.is-text.is-inverse,.button:hover[disabled].is-text.is-inverse{background:transparent;color:rgba(255,255,255,.5) !important}.button.is-disabled.is-danger,.button[disabled].is-danger,.button.is-disabled:hover.is-danger,.button:hover[disabled].is-danger{background:rgba(48,48,48,.12);color:rgba(48,48,48,.5) !important}.button.is-disabled.is-danger.is-inverse,.button[disabled].is-danger.is-inverse,.button.is-disabled:hover.is-danger.is-inverse,.button:hover[disabled].is-danger.is-inverse{background:rgba(255,255,255,.12);color:rgba(255,255,255,.5) !important}.button.is-small{height:24px;font-size:12px;min-width:70px;padding:0px 10px}.button.is-medium{height:32px;font-size:inherit;min-width:90px;padding:0px 10px}.button.is-large{height:44px;font-size:16px;padding:0px 30px}.button.is-full{width:100%}.button.has-icon{min-width:0px !important}.button .d-icon{vertical-align:middle;font-size:18px;margin:0px 6px}.checkbox{backface-visibility:hidden;box-sizing:border-box;display:inline-block;outline:0;position:relative;height:20px;padding-left:2px;margin-right:16px}.checkbox input[type=checkbox]{box-sizing:border-box;display:inline-block;height:20px;opacity:0;position:absolute}.checkbox>label{box-sizing:border-box;cursor:pointer;padding:0;position:relative;color:#303030;font-size:14px;font-family:inherit;font-weight:normal !important}.checkbox label:before{border-radius:1px;box-shadow:0 0 0 2px rgba(1,110,255,.5);box-sizing:border-box;content:"";display:inline-block;height:16px;margin-right:10px;transition:all .2s;vertical-align:text-top;width:16px}.checkbox input[type=checkbox]:hover+label:before{box-shadow:0 0 0 2px #016eff}.checkbox input[type=checkbox]:focus+label:before{box-shadow:0 0 0 2px #016eff}.checkbox input[type=checkbox][data-partial=true]+label:before,.checkbox input[type=checkbox]:checked+label:before{box-shadow:0 0 0 2px #016eff;background:#016eff}.checkbox input[type=checkbox]:disabled+label{color:#d5d5d5;cursor:not-allowed}.checkbox input[type=checkbox]:disabled+label:before{box-shadow:0 0 0 2px #d5d5d5}.checkbox input[type=checkbox]:checked:not([data-partial])+label:after{border:solid #fff;border-width:0 2px 2px 0;box-sizing:border-box;content:" ";display:block;height:12px;left:5px;position:absolute;top:1px;transform:rotate(45deg);width:6px}.checkbox input[type=checkbox][data-partial]+label:after{box-shadow:inset 0 0 0 2px rgba(1,110,255,.5);box-sizing:border-box;content:" ";height:2px;left:2px;position:absolute;top:7px;transition:all .2s;width:12px;border-radius:9999px}.chips{font-size:inherit;background:#cce2ff;border-radius:4px;color:#303030;padding:4px 6px}.chips.is-small{font-size:1.2rem;padding:2px 4px}.chip-group .chips{border-radius:0px !important}.chip-group .chips:first-child{border-top-left-radius:4px !important;border-bottom-left-radius:4px !important}.chip-group .chips:last-child{border-top-right-radius:4px !important;border-bottom-right-radius:4px !important}.d-icon{font-size:2.4rem}.d-icon.is-extrasmall{font-size:1.2rem !important}.d-icon.is-small{font-size:1.8rem !important}.d-icon.is-medium{font-size:3.6rem !important}.d-icon.is-large{font-size:4.8rem !important}svg.d-icon{width:2.4rem;height:2.4rem}svg.d-icon.is-extrasmall{width:1.2rem !important;height:1.2rem !important}svg.d-icon.is-small{width:1.8rem !important;height:1.8rem !important}svg.d-icon.is-medium{width:3.6rem !important;height:3.6rem !important}svg.d-icon.is-large{width:4.8rem !important;height:4.8rem !important}.input{position:relative;z-index:0}.input input,.input textarea{margin:0;appearance:none;border:none;box-shadow:none;flex:1 0 auto;font:inherit;outline:none;text-align:left;padding:0px 10px;width:inherit}.input input:active,.input input:focus,.input textarea:active,.input textarea:focus{transition:300ms}.input{display:flex;flex-wrap:wrap;width:250px}.input.is-full{width:100% !important}.input input{height:36px}.input textarea{padding-top:6px;max-width:100%;min-height:90px}.input input,.input textarea{background:rgba(1,110,255,.08);border-radius:4px;color:#303030;box-shadow:none}.input input:active,.input input:focus,.input textarea:active,.input textarea:focus{box-shadow:inset 0 -2px #016eff}.input input::placeholder,.input textarea::placeholder{color:rgba(48,48,48,.6)}.input input[disabled],.input textarea[disabled]{background:rgba(48,48,48,.05);color:rgba(48,48,48,.25);box-shadow:inset 0 -2px rgba(48,48,48,.05)}.input input[disabled]::placeholder,.input textarea[disabled]::placeholder{color:rgba(48,48,48,.6)}.input.is-active>input,.input.is-active>textarea{box-shadow:inset 0 -2px #016eff}.input.is-disabled>input,.input.is-disabled>textarea{background:rgba(48,48,48,.05);color:rgba(48,48,48,.25);box-shadow:inset 0 -2px rgba(48,48,48,.05)}.input.is-error>input,.input input:invalid,.input.is-error>textarea,.input textarea:invalid{box-shadow:inset 0 -2px #ea0000}.input.is-large>input{height:44px}.input.is-medium>input{height:32px}.input.is-small>input{height:24px}.input.is-inverse input{background:rgba(138,197,237,.1);color:#fff}.input.is-inverse input:active{border-bottom:2px solid #004cb3;color:#fff}.input.is-inverse input::placeholder{color:rgba(255,255,255,.5)}.input.is-inverse label{color:#fff}.input .message{width:inherit;font-size:11px !important;font-weight:normal;line-height:1.6rem;margin-top:2px}.input.is-error>.message{color:#ea0000}.input.has-icon-back>*{float:left}.input.has-icon-back input{padding-right:34px}.input.has-icon-back .d-icon{align-items:center;display:flex;font-size:1.8rem !important;pointer-events:none}.input.has-icon-back .d-icon::before{position:absolute;right:10px}.input.has-icon-front>*{float:left}.input.has-icon-front input{padding-left:34px}.input.has-icon-front .d-icon{align-items:center;display:flex;font-size:1.8rem !important}.input.has-icon-front .d-icon::before{position:absolute;left:10px}.input.has-icon-front.has-icon-back .d-icon:first-child::before{left:10px;right:unset}.input.has-icon-front.has-icon-back .d-icon:last-child::before{left:unset;right:10px}.input-group.has-button input{border-top-right-radius:0px;border-bottom-right-radius:0px}.input-group.has-button button.button{margin:0px;border-top-left-radius:0px;border-bottom-left-radius:0px}.input-group.has-dropdown .input{min-width:0px;width:auto}.input-group.has-dropdown .input input{min-width:250px;border-radius:0px}.input-group.has-dropdown .input select{width:inherit;padding-right:34px;border-top-right-radius:0px;border-bottom-right-radius:0px}a,.link{color:#016eff;text-decoration:none;font-size:inherit;cursor:pointer}a:hover,a.is-active,.link:hover,.link.is-active{color:#004cb3}a[disabled],a.is-disabled,.link[disabled],.link.is-disabled{color:rgba(48,48,48,.2);cursor:not-allowed}a.is-inverse,.link.is-inverse{color:#fff}a.is-inverse:hover,.link.is-inverse:hover{color:rgba(255,255,255,.4)}a.is-small,.link.is-small{font-size:1.2rem}a.is-secondary,.link.is-secondary{color:#606060}a.is-secondary:hover,.link.is-secondary:hover{color:#303030 !important}a.is-sub,a .is-sub,.link.is-sub,.link .is-sub{opacity:.5}a.is-sub:hover,a .is-sub:hover,.link.is-sub:hover,.link .is-sub:hover{opacity:1}a.has-icon-front .d-icon,.link.has-icon-front .d-icon{margin-right:4px;font-size:1.4rem !important;vertical-align:sub}a.has-icon-back .d-icon,.link.has-icon-back .d-icon{margin-left:2px;font-size:1.2rem !important;vertical-align:middle}a.has-icon-back.is-small .d-icon,.link.has-icon-back.is-small .d-icon{vertical-align:text-top}a:hover>.d-icon.is-sub,.link:hover>.d-icon.is-sub{color:#004cb3;opacity:1}a.is-secondary:hover>.d-icon.is-sub,.link.is-secondary:hover>.d-icon.is-sub{color:#303030;opacity:1}ol.list,ul.list{list-style-position:inside}ol.list.no-marker,ul.list.no-marker{list-style:none}ol.list li,ul.list li{margin:4px 0px}ul.list{list-style-type:square}.loader{position:relative;width:10px;height:10px;border-radius:50%;animation-name:loaderDefault;animation-iteration-count:infinite;animation-direction:alternate;animation-timing-function:ease;animation-duration:.9s;animation-delay:.2s;left:14px;top:-10px;margin:0px}.loader:before,.loader:after{content:"";position:absolute;width:10px;height:10px;border-radius:50%;animation-name:loaderDefault;animation-iteration-count:infinite;animation-direction:alternate;animation-timing-function:ease;animation-duration:.9s}.loader:before{left:14px;animation-delay:.3s}.loader:after{right:14px;animation-delay:.1s}.loader--inverse,.loader.is-inverse{animation-name:loaderInverse}.loader--inverse:before,.loader--inverse:after,.loader.is-inverse:before,.loader.is-inverse:after{animation-name:loaderInverse}.loader--button{left:0px}.button.is-solid .loader,.button.is-danger .loader{animation-name:loaderInverse}.button.is-solid .loader:before,.button.is-solid .loader:after,.button.is-danger .loader:before,.button.is-danger .loader:after{animation-name:loaderInverse}@keyframes loaderDefault{0%{box-shadow:0 10px 0 -10px #016eff;opacity:0}50%{box-shadow:0 10px 0 #016eff;opacity:.75}100%{box-shadow:0 10px 0 #016eff;opacity:1}}@keyframes loaderInverse{0%{box-shadow:0 10px 0 -10px #fff;opacity:0}50%{box-shadow:0 10px 0 #fff;opacity:.75}100%{box-shadow:0 10px 0 #fff;opacity:1}}.menu{white-space:nowrap;position:relative}.menu.is-right .menu-content{left:100%;transform:translateX(-100%)}.menu.is-left .menu-content{left:0%;transform:translateX(0%)}.menu.is-active .menu-trigger{pointer-events:none}.menu.is-active .menu-content,.menu.is-active .menu-content:hover{opacity:1;visibility:visible}.menu .menu-content{height:auto;opacity:0;background:#fff;font-size:14px;position:absolute;padding:15px 20px;border-radius:8px;visibility:hidden;box-shadow:0px 4px 8px rgba(0,0,0,.16);z-index:100;left:50%;transform:translateX(-50%);border-color:#e8e8e8;border-width:2px;border-style:solid;border-radius:4px;-webkit-transition:all .3s .1s;-moz-transition:all .3s .1s;-o-transition:all .3s .1s;transition:all .3s .1s}.menu .menu-content>*{display:block;white-space:nowrap;padding:5px 0px;height:unset}.menu .menu-content hr{background-color:#e8e8e8;height:2px;width:calc(100% + 40px);margin-left:-20px;padding:0px}.menu .menu-trigger:hover+.menu-content,.menu .menu-trigger:focus+.menu-content,.menu .menu-content:hover{opacity:1;visibility:visible}.modal{position:fixed;top:0;right:0;bottom:0;left:0;z-index:400;opacity:0;-webkit-transition:opacity 300ms ease-in;-moz-transition:opacity 300ms ease-in;transition:opacity 300ms ease-in;pointer-events:none;background:rgba(232,232,232,.9);overflow:auto;display:flex;justify-content:center;align-items:center}.modal div.modal-container{width:450px;margin:20px;position:relative;background:#fff;box-shadow:0px 4px 8px rgba(0,0,0,.16);border-width:2px;border-style:solid;border-color:#e8e8e8;border-radius:4px;display:flex;flex-direction:column}.modal div.modal-container .close{color:#303030;opacity:.4;line-height:14px;position:absolute;z-index:500;right:14px;top:14px}.modal div.modal-container .close:hover{color:#303030;opacity:.6}.modal div.modal-container div.modal-content{padding:24px 30px 24px 30px}.modal div.modal-container div.modal-header{border-bottom-width:2px;border-bottom-style:solid;border-bottom-color:#e8e8e8;display:flex;align-items:center;padding:15px 58px 13px 30px;width:100%}.modal div.modal-container div.modal-footer{border-top-width:2px;border-top-style:solid;border-top-color:#e8e8e8;margin-bottom:0px;display:flex;align-items:center;padding:20px 30px 20px 30px;width:100%}.modal div.modal-container.is-full{width:100%;height:100%;box-shadow:none;border:none !important;border-radius:0px !important;margin:0px;padding:0px}.modal div.modal-container.is-full div.modal-content{padding:24px 58px 24px 58px;height:100%;overflow-y:scroll}.modal:target,.modal.is-active{opacity:1;pointer-events:auto}@media(max-width: 899px){.modal div.is-full div.modal-content{padding:24px 30px 24px 30px}}.nav{box-sizing:border-box;height:60px;width:100%;background:#002b66;position:relative}.nav.is-fixed-top{position:fixed;top:0}.nav .nav-center .nav-item,.nav .nav-center .nav-icon,.nav .nav-center .menu--nav,.nav .nav-center .nav-control,.nav .nav-right .nav-item,.nav .nav-right .nav-icon,.nav .nav-right .menu--nav,.nav .nav-right .nav-control{display:block}@media(min-width: 899px){.nav .nav-center,.nav .nav-right{display:inline-block;background:transparent}.nav .nav-center .nav-item,.nav .nav-center .nav-icon,.nav .nav-center .menu--nav,.nav .nav-center .nav-control,.nav .nav-right .nav-item,.nav .nav-right .nav-icon,.nav .nav-right .menu--nav,.nav .nav-right .nav-control{display:inline-block}}.nav .nav-left{padding-left:12px;padding-right:12px;display:inline-block;vertical-align:top;height:60px;width:100%}.nav .nav-left .nav-item,.nav .nav-left .nav-icon,.nav .nav-left .menu--nav,.nav .nav-left .nav-control{height:60px;padding:18px 8px}@media(min-width: 899px){.nav .nav-left .nav-item,.nav .nav-left .nav-icon,.nav .nav-left .menu--nav,.nav .nav-left .nav-control{padding:18px 12px}}@media(min-width: 899px){.nav .nav-left{width:300px;padding-left:18px;padding-right:0px}}.nav .nav-right{padding-right:0px}@media(min-width: 899px){.nav .nav-right{float:right;padding-right:18px}}.nav .nav-responsive{position:absolute;left:0;right:0;height:calc(100vh - 60px);background:#19375f;opacity:0;visibility:hidden;overflow:auto}.nav .nav-responsive.is-active{opacity:1;visibility:visible}@media(min-width: 899px){.nav .nav-responsive{float:right;width:calc(100% - 300px);opacity:1;visibility:visible;height:60px;background:transparent !important;position:relative;overflow:inherit}}.nav .nav-item,.nav .nav-icon,.nav .menu--nav,.nav .nav-control{display:inline-block;vertical-align:top;height:54px;padding:16px 20px;position:relative}@media(min-width: 899px){.nav .nav-item,.nav .nav-icon,.nav .menu--nav,.nav .nav-control{height:60px;padding:18px 12px}}.nav .nav-brand{width:auto;padding:13px 12px;height:60px}.nav .nav-item,.nav .nav-icon{color:#fff;font-size:1.6rem}.nav .nav-item .icon-name,.nav .nav-icon .icon-name{margin-left:10px}@media(min-width: 899px){.nav .nav-item .icon-name,.nav .nav-icon .icon-name{display:none}}.nav .nav-item.is-active,.nav .nav-icon.is-active{box-shadow:inset 4px 0 0 #016eff}@media(min-width: 899px){.nav .nav-item.is-active,.nav .nav-icon.is-active{box-shadow:inset 0 -4px 0 #016eff}}.nav .nav-item:hover,.nav .nav-item:focus,.nav .nav-icon:hover,.nav .nav-icon:focus{color:rgba(255,255,255,.4)}.nav .menu.menu--nav{color:#fff;padding:0px !important;height:auto}.nav .menu.menu--nav .menu-content{margin-top:-8px}@media(max-width: 898px){.nav .menu.menu--nav{position:unset}.nav .menu.menu--nav .menu-content{height:0px;overflow:hidden;padding:0px;border-radius:0px;border:none;box-shadow:none;width:100%;margin-top:0px;background-color:transparent}.nav .menu.menu--nav .menu-content>*{display:block;height:40px;background:#334d71;padding:10px 20px;color:#fff}.nav .menu.menu--nav .menu-content>*:hover,.nav .menu.menu--nav .menu-content>*:focus{color:#fff}.nav .menu.menu--nav .menu-content:before,.nav .menu.menu--nav .menu-content:after{display:none}.nav .menu.menu--nav.is-active>.menu-content,.nav .menu.menu--nav .menu-trigger:hover+.menu-content,.nav .menu.menu--nav .menu-trigger:focus+.menu-content,.nav .menu.menu--nav .menu-content:hover{height:auto}}.nav .nav-control{padding:9px 12px;color:#fff}.nav .nav-control>*{width:100%}@media(min-width: 899px){.nav .nav-control{padding:12px 12px}}.d-progress{height:16px;width:100%;background:#e8e8e8;border-radius:4px;display:flex;overflow:hidden}.d-progress .d-progress--bar{height:inherit;background:#016eff;margin-left:2px}.d-progress .d-progress--bar:first-child{margin-left:0px !important}.d-progress__sm{height:10px}.d-progress__md{height:20px}.d-progress__lg{height:24px}.d-progress__loading{animation-duration:1s;animation-fill-mode:forwards;animation-iteration-count:infinite;animation-name:loadingShimmer;animation-timing-function:linear;background-image:linear-gradient(to right, #E8E8E8 0%, #f7f7f7 50%, #E8E8E8 100%);background-size:50%;background-repeat:no-repeat;position:relative}@keyframes loadingShimmer{0%{background-position:-100%}100%{background-position:200%}}.radio{backface-visibility:hidden;box-sizing:border-box;display:inline-block;outline:0;position:relative;height:20px;margin-right:16px}.radio>input[type=radio]{height:20px;box-sizing:border-box;opacity:0;position:absolute}.radio>label{box-sizing:border-box;cursor:pointer;padding:0;position:relative;color:#303030;font-size:14px;font-family:helvetica,sans-serif;font-weight:normal;padding-left:2px}.radio>label:before{border-radius:50%;box-shadow:0 0 0 2px rgba(1,110,255,.5);box-sizing:border-box;content:"";display:inline-block;height:16px;margin-right:10px;transition:.2s;vertical-align:text-top;width:16px}.radio>input[type=radio]:hover+label:before{box-shadow:0 0 0 2px #016eff}.radio>input[type=radio]:checked+label:before{box-shadow:0 0 0 2px #016eff}.radio>input[type=radio]:disabled+label{color:#d5d5d5;cursor:not-allowed}.radio>input[type=radio]:disabled+label:before{box-shadow:0 0 0 2px #d5d5d5}.radio>input[type=radio]:checked+label:after{background-color:#016eff;border-radius:50%;box-sizing:border-box;content:"";height:12px;left:4px;position:absolute;top:2px;transition:.2s;width:12px}.input.has-arrow:before{content:url('data:image/svg+xml;charset=UTF-8, ');pointer-events:none;opacity:.4;position:absolute;top:50%;transform:translateY(-50%);right:10px;height:18px;width:18px}.input select{background:rgba(1,110,255,.08);border-radius:4px;color:#303030;height:36px;box-shadow:none;appearance:none;-webkit-appearance:none;flex:1 0 auto;font:inherit;outline:none;border:none;text-align:left;width:inherit;margin:0;position:relative;padding:0px 10px}.input select option{color:#000}.input select:active,.input select:focus,.input select[disabled]{transition:300ms}.input select:invalid{box-shadow:inset 0 -2px 0 #ea0000}.input select::placeholder{color:rgba(48,48,48,.6)}.input select:active,.input select:focus{box-shadow:inset 0 -2px #016eff}.input select[disabled]{background:rgba(48,48,48,.05);box-shadow:inset 0 -2px rgba(48,48,48,.05)}.input.is-active>select{box-shadow:inset 0 -2px #016eff}.input.is-error>select{box-shadow:inset 0 -2px #ea0000}.input.is-inverse.has-arrow:before{content:url('data:image/svg+xml;charset=UTF-8, ')}.input.is-inverse select{background:rgba(242,242,242,.1);color:#fff}.input.is-inverse select::placeholder{color:rgba(255,255,255,.6)}.input.is-inverse select:active,.input.is-inverse select:focus{background:rgba(242,242,242,.1);box-shadow:inset 0 -2px 0 #004cb3}.input.is-inverse select[disabled]{background:rgba(255,255,255,.1);box-shadow:inset 0 -2px 0 rgba(255,255,255,.05);color:rgba(255,255,255,.1)}.input.is-inverse select[disabled]::placeholder{color:rgba(255,255,255,.2)}.input.is-large>select{height:44px}.input.is-medium>select{height:32px}.input.is-small>select{height:24px}.input.is-full{width:100%}label.switch{box-sizing:border-box;display:inline-flex;height:20px;position:relative;margin-top:0px !important;align-items:center;margin-right:16px}label.switch>input[type=checkbox]{display:none}label.switch>.slider{background-color:#d5d5d5;border-radius:34px;bottom:0;box-sizing:border-box;cursor:pointer;height:4px;left:0;position:relative;right:0;transition:all .4s;width:34px}label.switch>.slider:before{background-color:transparent;box-sizing:border-box;content:" ";height:18px;left:0;position:absolute;top:-8px;width:34px}label.switch>.slider:after{background-color:#fff;bottom:-8px;border:2px solid;border-color:rgba(1,110,255,.5);border-radius:50%;box-sizing:border-box;content:"";height:20px;left:0;position:absolute;transition:all .4s;width:20px}label.switch>input[type=checkbox]:hover~.slider:after{border-color:#016eff}label.switch>input[type=checkbox]:checked~.slider{background-color:rgba(21,192,70,.45)}label.switch>input[type=checkbox]:checked~.slider:after{background:#15c046;border-color:#15c046;transform:translateX(16px)}label.switch.disabled>input[type=checkbox]~.slider,label.switch>input[type=checkbox]:disabled~.slider,label.switch>input[type=checkbox]:checked:disabled~.slider{background-color:#e8e8e8;cursor:not-allowed}label.switch.disabled>input[type=checkbox]~.slider:after,label.switch>input[type=checkbox]:disabled~.slider:after,label.switch>input[type=checkbox]:checked:disabled~.slider:after{border-color:#d5d5d5;cursor:not-allowed}label.switch.disabled>input[type=checkbox]:checked~.slider:after,label.switch>input[type=checkbox]:checked:disabled~.slider:after{background:#fff}label.switch>.label{color:inherit;font-size:14px;font-family:helvetica,sans-serif;font-weight:normal;margin-left:10px;position:relative;white-space:nowrap}label.switch.disabled>.label,label.switch>input[type=checkbox]:disabled~.label,label.switch>input[type=checkbox]:checked:disabled~.label{color:#d5d5d5}label.switch>input[type=checkbox]:checked~.off{display:none}label.switch>input[type=checkbox]:not(:checked)~.off{display:inline}label.switch>input[type=checkbox]:checked~.on{display:inline}label.switch>input[type=checkbox]:not(:checked)~.on{display:none}.table-container{width:100%;overflow:scroll;border-radius:6px}.table-container__freeze table.table tr th:first-child{position:sticky;left:0;box-shadow:0 50px 20px -50px rgba(0,0,0,.1),0 8px 20px 0 rgba(0,0,0,.1);max-width:fit-content;background:#fff}table.table{width:100%;overflow:scroll;position:relative}table.table th,table.table td{padding:0px 10px;white-space:nowrap}table.table th.is-center,table.table td.is-center{text-align:center}table.table th.is-right,table.table td.is-right{text-align:right}table.table tbody th,table.table tbody td{border-bottom:solid 1px #e8e8e8;font-weight:normal}table.table thead tr{height:14px;border-bottom:solid 2px #e8e8e8}table.table thead th{text-transform:uppercase;font-weight:normal;color:#303030;font-size:1.2rem;padding:10px}table.table thead th.is-sorted{align-items:center}table.table thead th.is-sorted::after{content:url('data:image/svg+xml;charset=UTF-8, ');width:14px;height:14px;margin-left:4px;display:inline-block;position:relative;top:2px}table.table thead th.is-sorted__ascend{font-weight:bold}table.table thead th.is-sorted__ascend::after{content:url('data:image/svg+xml;charset=UTF-8, ')}table.table thead th.is-sorted__descend{font-weight:bold}table.table thead th.is-sorted__descend::after{content:url('data:image/svg+xml;charset=UTF-8, ')}table.table tfoot th,table.table tfoot td{background:#e8e8e8 !important}table.table tfoot th:first-child,table.table tfoot td:first-child{border-radius:0px 0px 0px 6px}table.table tfoot th:last-child,table.table tfoot td:last-child{border-radius:0px 0px 6px 0px}table.table.is-striped tbody th,table.table.is-striped tbody td{border:none !important}table.table.is-striped tbody tr:nth-child(odd) th,table.table.is-striped tbody tr:nth-child(odd) td{background:#f8f8f8 !important}table.table.is-cards{border-collapse:separate;border-spacing:0px 10px}table.table.is-cards th:first-child,table.table.is-cards td:first-child{border-radius:6px 0px 0px 6px !important}table.table.is-cards th:last-child,table.table.is-cards td:last-child{border-radius:0px 6px 6px 0px !important}table.table.is-cards thead th{padding-bottom:0px;border:none}table.table.is-cards tbody th,table.table.is-cards tbody td{border:solid 1px #e8e8e8;border-right:none;border-left:none}table.table.is-cards tbody th:first-child,table.table.is-cards tbody td:first-child{border-left:solid 1px #e8e8e8}table.table.is-cards tbody th:last-child,table.table.is-cards tbody td:last-child{border-right:solid 1px #e8e8e8}table.table tr{height:48px}table.table__condensed tr{height:40px}table.table__expanded tr{height:56px}.tabs{-webkit-overflow-scrolling:touch}.tabs.is-primary{min-width:100%;background:#f3f8ff}.tabs.is-primary ul{list-style:none;display:flex;justify-content:flex-start;padding:20px 0px 0px 30px;overflow-x:auto;overflow-x:-moz-scrollbars-none}.tabs.is-primary ul::-webkit-scrollbar{width:0 !important}.tabs.is-primary ul li{white-space:nowrap;cursor:pointer;display:flex;justify-content:center;align-items:center;color:#016eff;font-size:1.6rem;height:40px;border-top-left-radius:6px;border-top-right-radius:6px}.tabs.is-primary ul li>*{padding:0px 20px}.tabs.is-primary ul li.is-active{background:#fff}.tabs.is-primary ul li.is-active>*{color:#303030}.tabs.is-primary ul li.is-disabled>*{color:rgba(48,48,48,.2);cursor:not-allowed}.tabs.is-primary.is-vertical{min-width:300px;width:300px;height:inherit;overflow-y:auto}.tabs.is-primary.is-vertical ul{flex-direction:column;padding:12px 0px 40px 0px}.tabs.is-primary.is-vertical ul li{cursor:pointer;justify-content:flex-start;box-sizing:border-box;height:40px;margin:2px 16px;border-radius:8px}.tabs.is-primary.is-vertical ul li>*{color:inherit;padding:0px 14px;height:inherit;flex:auto;display:flex;align-items:center}.tabs.is-primary.is-vertical ul li:hover{box-shadow:inset 0px 0px 0px 1px rgba(0,76,179,.1)}.tabs.is-primary.is-vertical ul li:hover>*{color:#004cb3}.tabs.is-primary.is-vertical ul li.is-active{box-shadow:inset 0px 0px 0px 1px rgba(1,110,255,.1);background:rgba(1,110,255,.1);font-weight:bold}.tabs.is-primary.is-vertical ul li.is-active>*{color:#303030}.tabs.is-primary.is-vertical ul li.is-disabled{box-shadow:none}.tabs.is-primary.is-vertical ul li.is-disabled>*{color:rgba(48,48,48,.2)}.tabs.is-primary.is-vertical ul li.tabs__section-header{font-size:1.2rem;text-transform:uppercase;color:#9a9a9a;padding:0px 14px}.tabs.is-primary.is-vertical ul li.tabs__section-header:hover{box-shadow:none}.tabs.is-secondary{min-width:100%}.tabs.is-secondary ul{list-style:none;display:flex;justify-content:flex-start;padding:0px;box-shadow:inset 0px -2px rgba(96,96,96,.05);overflow-x:auto;overflow-x:-moz-scrollbars-none}.tabs.is-secondary ul::-webkit-scrollbar{width:0 !important}.tabs.is-secondary ul li{cursor:pointer;white-space:nowrap}.tabs.is-secondary ul li>*{display:flex;justify-content:center;align-items:center;color:#016eff;font-size:1.6rem;height:40px;padding:0px 20px}.tabs.is-secondary ul li.is-active>*{box-shadow:inset 0px -2px #016eff;color:#303030}@media(max-width: 899px){.tabs.tablet-down-hide-left{position:absolute;left:-300px;transition:left 300ms}}@media(max-width: 899px){.tabs.tablet-down-hide-right{position:absolute;right:-300px;transition:right 300ms}}@media(max-width: 899px){.tabs.tablet-down-toggle-tabs-left{position:absolute;left:0px;margin:0px;box-shadow:4px 0px 8px rgba(0,0,0,.16);z-index:99}}div.tags{display:flex;flex-direction:row;flex-wrap:wrap}div.tags.nowrap{flex-wrap:nowrap;overflow-x:auto;overflow-x:-moz-scrollbars-none}div.tags.nowrap::-webkit-scrollbar{width:0 !important}.tag{display:inline-flex;align-items:center;justify-content:center;padding:0px 10px;height:28px;margin:3px;background:#cce2ff;color:#303030;border-radius:9999px;white-space:nowrap;transition:300ms}.tag.is-active{background:#80b6ff}.tag[disabled],.tag.is-disabled{background:rgba(48,48,48,.1);color:rgba(48,48,48,.4)}.tag .d-icon{font-size:2rem !important}.tag.is-small{font-size:1.2rem;height:22px;padding:0px 8px}.tag.is-small .d-icon{font-size:1.6rem !important}.tag a.d-icon{opacity:.5;color:#303030}.tag a.d-icon:hover{opacity:1}.tag.has-icon-back{padding-right:6px}.tag.has-icon-back .d-icon{margin-left:6px}.tag.has-icon-back.is-small{padding-right:4px}.tag.has-icon-back.is-small .d-icon{margin-left:4px}.tag.has-icon-front .d-icon{margin-right:4px}.tag.outlined{background:transparent;color:#303030 !important;border:1px solid #016eff}.tag.outlined:hover{background:rgba(1,110,255,.2);color:#303030 !important;transition:300ms}.tag.outlined.is-active{background:#016eff;color:#fff !important}.tag.outlined[disabled],.tag.outlined.is-disabled{background:rgba(48,48,48,.1);color:rgba(48,48,48,.4);border-color:rgba(48,48,48,.4)}.title{display:flex;align-items:center;height:52px}.title span.bar{height:inherit;width:2px;background-color:#d5d5d5;border-radius:4px;margin-right:8px}.title span.bar.is-wide{width:4px;margin-right:6px}.toggle{display:flex;position:relative;white-space:nowrap;overflow:auto;padding:2px}.toggle input{position:absolute !important;clip:rect(0, 0, 0, 0);overflow:hidden}.toggle input:checked+label{box-shadow:none;z-index:1;background-color:#fff;color:#303030;border-radius:4px;box-shadow:0 0 0 2px #016eff}.toggle input:disabled+label{color:rgba(48,48,48,.2);cursor:not-allowed}.toggle input.is-disabled+label{color:rgba(48,48,48,.2);cursor:not-allowed}.toggle>ul{display:flex}.toggle label,.toggle li{display:flex;justify-content:center;align-items:center;transition:all 300ms ease-in-out;background:rgba(1,110,255,.08);color:#016eff;font-size:1.4rem;height:36px;padding:0px 14px}.toggle label a,.toggle li a{color:inherit}.toggle label.is-active,.toggle li.is-active{box-shadow:none;z-index:1;background-color:#fff;color:#303030;border-radius:4px !important;box-shadow:0 0 0 2px #016eff}.toggle label.is-active a,.toggle li.is-active a{color:inherit}.toggle label:hover,.toggle li:hover{cursor:pointer;color:#002b66}.toggle label.is-disabled,.toggle label:disabled,.toggle li.is-disabled,.toggle li:disabled{cursor:not-allowed;color:rgba(48,48,48,.2)}.toggle label.is-disabled>*,.toggle label:disabled>*,.toggle li.is-disabled>*,.toggle li:disabled>*{cursor:not-allowed}.toggle label:first-of-type,.toggle li:first-of-type{border-top-left-radius:4px;border-bottom-left-radius:4px}.toggle label:last-of-type,.toggle li:last-of-type{border-top-right-radius:4px;border-bottom-right-radius:4px}.toggle.has-icons label,.toggle.has-icons li{padding:0px 8px !important}.toggle.has-icons.is-small label,.toggle.has-icons.is-small li{padding:0px 6px !important}.toggle.is-small label,.toggle.is-small li{height:28px;padding:0px 10px}.toggle.is-inverse label,.toggle.is-inverse li{color:#80b6ff;background:rgba(255,255,255,.1)}.toggle.is-inverse label.is-active,.toggle.is-inverse li.is-active{color:#fff;background:transparent}*[class*=d-tooltip]{position:relative}*[class*=d-tooltip]::after{content:attr(data-tooltip);z-index:1000;position:absolute;visibility:hidden;opacity:0;transition:300ms;white-space:nowrap;padding:6px 8px;font-size:1.2rem;border-radius:4px;font-family:inherit;background-color:rgba(48,48,48,.9);color:#fff;left:50%;top:calc(100% + 4px);transform:translateX(-50%)}*[class*=d-tooltip][class*=-small]::after{font-size:1rem;padding:4px 6px}*[class*=d-tooltip][class*=tooltip-top]::after{top:0%;transform:translate(-50%, calc(-100% - 4px))}*[class*=d-tooltip][class*=tooltip-left]::after{left:0%;top:50%;transform:translate(calc(-100% - 4px), -50%)}*[class*=d-tooltip][class*=tooltip-right]::after{left:calc(100% + 4px);top:50%;transform:translateY(-50%)}*[class*=d-tooltip]:hover::after,*[class*=d-tooltip]:focus::after{visibility:visible;opacity:1}h1,h2,h3,h4,h5,h6{font-family:inherit}h1{font-size:2.8rem;line-height:1.25;font-weight:600;color:#303030}h2{font-size:2.6rem;line-height:1.25;font-weight:600;color:#303030}h3{font-size:2rem;line-height:1.5;font-weight:600;color:#303030}h4{font-size:1.6rem;line-height:1.5;font-weight:600;color:#303030}h5{font-size:1.4rem;line-height:1.5;font-weight:600;color:#303030}h6{font-size:1.2rem;line-height:1.5;font-weight:400;color:#303030}p{font-family:inherit;font-size:1.4rem;line-height:1.75;font-weight:400;color:#303030} diff --git a/css/native.css b/css/native.css deleted file mode 100644 index 1bb163160b..0000000000 --- a/css/native.css +++ /dev/null @@ -1,70 +0,0 @@ -.highlight pre { background-color: #fff } -.highlight .hll { background-color: #fff } -.highlight .c { color: #999999; font-style: italic } /* Comment */ -.highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */ -.highlight .g { color: #808080 } /* Generic */ -.highlight .k { color: #4ab625; font-weight: bold } /* Keyword */ -.highlight .l { color: #808080 } /* Literal */ -.highlight .n { color: #808080 } /* Name */ -.highlight .o { color: #808080 } /* Operator */ -.highlight .x { color: #808080 } /* Other */ -.highlight .p { color: #808080 } /* Punctuation */ -.highlight .cm { color: #999999; font-style: italic } /* Comment.Multiline */ -.highlight .cp { color: #cd2828; font-weight: bold } /* Comment.Preproc */ -.highlight .c1 { color: #999999; font-style: italic } /* Comment.Single */ -.highlight .cs { color: #e50808; font-weight: bold; background-color: #520000 } /* Comment.Special */ -.highlight .gd { color: #d22323 } /* Generic.Deleted */ -.highlight .ge { color: #808080; font-style: italic } /* Generic.Emph */ -.highlight .gr { color: #d22323 } /* Generic.Error */ -.highlight .gh { color: #000000; font-weight: bold } /* Generic.Heading */ -.highlight .gi { color: #589819 } /* Generic.Inserted */ -.highlight .go { color: #a0a0a0 } /* Generic.Output */ -.highlight .gp { color: #808080 } /* Generic.Prompt */ -.highlight .gs { color: #808080; font-weight: bold } /* Generic.Strong */ -.highlight .gu { color: #000000; text-decoration: underline } /* Generic.Subheading */ -.highlight .gt { color: #d22323 } /* Generic.Traceback */ -.highlight .kc { color: #4ab625; font-weight: bold } /* Keyword.Constant */ -.highlight .kd { color: #4ab625; font-weight: bold } /* Keyword.Declaration */ -.highlight .kn { color: #4ab625; font-weight: bold } /* Keyword.Namespace */ -.highlight .kp { color: #4ab625 } /* Keyword.Pseudo */ -.highlight .kr { color: #4ab625; font-weight: bold } /* Keyword.Reserved */ -.highlight .kt { color: #4ab625; font-weight: bold } /* Keyword.Type */ -.highlight .ld { color: #808080 } /* Literal.Date */ -.highlight .m { color: #3677a9 } /* Literal.Number */ -.highlight .s { color: #ad5d03 } /* Literal.String */ -.highlight .na { color: #808080 } /* Name.Attribute */ -.highlight .nb { color: #24909d } /* Name.Builtin */ -.highlight .nc { color: #447fcf; text-decoration: underline } /* Name.Class */ -.highlight .no { color: #40a0a0 } /* Name.Constant */ -.highlight .nd { color: #ffa500 } /* Name.Decorator */ -.highlight .ni { color: #808080 } /* Name.Entity */ -.highlight .ne { color: #bbbbbb } /* Name.Exception */ -.highlight .nf { color: #447fcf } /* Name.Function */ -.highlight .nl { color: #808080 } /* Name.Label */ -.highlight .nn { color: #447fcf; text-decoration: underline } /* Name.Namespace */ -.highlight .nx { color: #808080 } /* Name.Other */ -.highlight .py { color: #808080 } /* Name.Property */ -.highlight .nt { color: #5aa815; font-weight: bold } /* Name.Tag */ -.highlight .nv { color: #308f8f } /* Name.Variable */ -.highlight .ow { color: #4ab625; font-weight: bold } /* Operator.Word */ -.highlight .w { color: #666666 } /* Text.Whitespace */ -.highlight .mf { color: #3677a9 } /* Literal.Number.Float */ -.highlight .mh { color: #3677a9 } /* Literal.Number.Hex */ -.highlight .mi { color: #3677a9 } /* Literal.Number.Integer */ -.highlight .mo { color: #3677a9 } /* Literal.Number.Oct */ -.highlight .sb { color: #ad5d03 } /* Literal.String.Backtick */ -.highlight .sc { color: #ad5d03 } /* Literal.String.Char */ -.highlight .sd { color: #ad5d03 } /* Literal.String.Doc */ -.highlight .s2 { color: #ad5d03 } /* Literal.String.Double */ -.highlight .se { color: #ad5d03 } /* Literal.String.Escape */ -.highlight .sh { color: #ad5d03 } /* Literal.String.Heredoc */ -.highlight .si { color: #ad5d03 } /* Literal.String.Interpol */ -.highlight .sx { color: #ffa500 } /* Literal.String.Other */ -.highlight .sr { color: #ad5d03 } /* Literal.String.Regex */ -.highlight .s1 { color: #ad5d03 } /* Literal.String.Single */ -.highlight .ss { color: #ad5d03 } /* Literal.String.Symbol */ -.highlight .bp { color: #24909d } /* Name.Builtin.Pseudo */ -.highlight .vc { color: #40a0a0 } /* Name.Variable.Class */ -.highlight .vg { color: #40a0a0 } /* Name.Variable.Global */ -.highlight .vi { color: #40a0a0 } /* Name.Variable.Instance */ -.highlight .il { color: #3677a9 } /* Literal.Number.Integer.Long */ diff --git a/css/style-light.scss b/css/style-light.scss deleted file mode 100644 index 035c5c5e49..0000000000 --- a/css/style-light.scss +++ /dev/null @@ -1,18 +0,0 @@ ---- ---- -/* Overrides for light theme only */ - -/* Variables copied from theme */ -$color-brand-100: #FFFFFF; -$color-brand-200: #D0F3DE; -$color-brand-300: #61D790; -$color-brand-400: #2E2F27; -$color-brand-500: #4D4D4D; -$color-brand-600: #000000; -$color-brand-700: #0246C9; -$color-brand-800: #5E93FB; - -button.section-searchbar { - background: rgba($color-brand-300, 0.13) !important; - color: $color-brand-400 !important; -} \ No newline at end of file diff --git a/css/style.scss b/css/style.scss deleted file mode 100644 index d492a88d2e..0000000000 --- a/css/style.scss +++ /dev/null @@ -1,1174 +0,0 @@ ---- ---- -/* Import helpers from Denali */ -@import "denali-css/scss/denali-helpers"; - -// More media size selectors -.hide-desktop-down { - @include media-size-down(desktop) { - display: none !important; - } -} - -.hide-desktop-up { - @include media-size-up(desktop) { - display: none !important; - } -} - -.hide-hd-down { - @include media-size-down(hd) { - display: none !important; - } -} - -.hide-hd-up { - @include media-size-up(hd) { - display: none !important; - } -} - - -/* Helpers that need to be added */ - -/* Variables copied from theme */ -$color-brand-100: #FFFFFF; -$color-brand-200: #D0F3DE; -$color-brand-300: #61D790; -$color-brand-400: #2E2F27; -$color-brand-500: #4D4D4D; -$color-brand-600: #000000; -$color-brand-700: #0246C9; -$color-brand-800: #5E93FB; - -.has-bg-brand-300 { - background-color: $color-brand-300 !important; -} -.has-bg-brand-400 { - background-color: $color-brand-400 !important; -} -.is-brand-500 { - color: $color-brand-500 !important; -} - -/* TODO: Border radiius circle and pill */ -.br-circle { - border-radius: 100%; -} - -/* Gutters should be applied to rows */ -/* Add a gutter size override too. */ -.is-gutterless > .col { - margin: 0px !important; -} - -/* TODO: Add additional flexbox helpers */ -.flex-wrap { - flex-wrap: wrap; -} -.align-items-flex-end { - align-items: flex-end; -} - -/* NOTE: Add font weight override to nav items */ -/* Add loop for height helpers */ -.h-50 { - height: 50px; -} -.h-40 { - height: 40px; -} -.h-35 { - height: 35px; -} - -/* TODO: Lineheight Helpers */ -.line-height-compact { - line-height: 1 !important; -} - -/* TODO: Add overflow helpers */ -.overflow-hidden { - overflow: hidden; -} - -/* TODO: Background image helpers */ -/* TODO: Button full size at breakpoints */ -/* TODO: Navbar overflow */ - -/* Add sticky */ -.sticky-top { - position: sticky; - top: 0; -} - - - -/* Styles for the site */ - -/* Navbar */ -.nav { - font-weight: 700; - z-index: 5; -} - -.nav .nav-left { - padding: 0 !important; -} - -// Responsive design; downscaled nav bar for small desktop screens -@include media-size-only(small-desktop) { - .nav .nav-left { - width: 160px !important; - } - - .nav .nav-responsive { - width: calc(100% - 160px) !important; - } - - .nav .nav-item { - font-size: 1.4rem !important; - } - - .nav .nav-right .nav-item { - padding-left: 6px !important; - padding-right: 6px !important; - } - - .nav-item .d-icon { - font-size: 2.0rem !important; - } -} - -.nav.vespa-light { - border-bottom: 1px solid $color-brand-600; -} - -input.section-searchbar { - font-weight: 200 !important; - border-top-right-radius: 0px !important; - border-bottom-right-radius: 0px !important; - color: $color-brand-600 !important; -} - -button.section-searchbar { - background: $color-brand-300 !important; -} - -@media (max-width: 999px) { - .nav-item { - a { - color: $color-brand-400; - } - .toggle-arrow { - margin-right: 1%; - font-size: 1.2rem; - height: 1rem; - } - - } - -} - -.nav-item.section-searchbar { - padding-top: 14px; - z-index: 9; -} - -/* Cover */ -.section-cover { - height: 530px; - - .paragraph-header { - font-weight: 600; - font-size: 2.0rem; - } -} -.section-cover--image { - background-image: url("{{ site.baseurl }}/assets/cover-image.png"); - background-size: cover; - background-position: center; -} - -@media (max-width: 999px) { - .section-cover--video-modal { - width: 650px !important; - } - - .section-cover--video-modal iframe#modal-video-frame { - width: 560px; - height: 315px; - } -} - -@media (min-width: 1000px) { - .section-cover--video-modal { - width: 975px !important; - } - - .section-cover--video-modal iframe#modal-video-frame { - width: 840px; - height: 473px; - } -} -/* Printed version styling */ -@media print { - #sidebar, .chip.chip-cloud, .chip.chip-self-managed, .chip.chip-enterprise, d-menu-hamburger { - display: none; - } -} - -/* Guides */ -.section-guide--item { - min-height: 320px; - transition: 300ms; -} -.section-guide--item div:last-child { - transition: 300ms; -} -.section-guide--item .section-guide--item--button > p { - color: #005A8E; - transition: 300ms; -} -.section-guide--item:hover { - box-shadow: 0 1px 3px 2px rgba(0,0,0,0.10); -} -.section-guide--item:hover div:last-child { - background-color: #1A7DB6; -} -.section-guide--item:hover .section-guide--item--button > p { - color: #fff; -} - -/* Users */ -.section-users--user-column { - align-items: start; -} - -/* Blog */ -.section-blog--image { - height: 150px; - width: 100%; - background-size: cover; - background-position: center; -} - -.section-blog--box { - height: calc(100% - 40px); -} - -/* Footer */ -.section-footer--copyright { - align-self: flex-end; - align-items: flex-end; -} - -/* Fix anchored links being hidden under navbar */ -h1[id]::before, h2[id]::before, h3[id]::before, h4[id]::before, p[id]::before { - display: block; - content: " "; - margin-top: -75px; - height: 75px; - visibility: hidden; -} - -.subpage { - p, ul, ol, dl { - margin-block-end: 0.5em; - } - h2 { - margin-top: 35px; - margin-bottom: 15px; - } - h3 { - margin-top: 20px; - margin-bottom: 10px; - } - - h2:hover > a, h3:hover > a, h4:hover > a { - opacity: 1; - } -} - -/* Hide tables and mathjax divs under navbar */ -table, mjx-container { - position: static !important; -} - -body { - font-size: 1.6rem; -} - -figure { - margin-bottom: 10px; -} - -figcaption { - font-style: italic; -} - -code, kbd, pre, samp { - font-family: Menlo,Monaco,Consolas,"Courier New",monospace; - font-size: 1.4rem; -} - -// Must use the same colors as in the code highlighter (see native.css) -pre { - -webkit-font-smoothing: auto; - overflow: auto; - page-break-inside: avoid; - display: block; - line-height: 1.42857143; - word-break: break-all; - word-wrap: break-word; - white-space: pre-wrap; - border: 1px solid #909090; - background-color: #fff; - border-radius: 4px; - padding: 14px; - margin-bottom: 10px; - margin-top: 10px; -} - -// Make it easier to find links in pre-elements -pre > a { - text-decoration: underline; -} - -.pre-hilite { - background-color: #ffff99; -} - -.vespa-version { - background-color: #A9F5A9; - border-radius: 4px; - border: 1px solid #1DAD51; - padding: 2px; -} - -// Used for services.xml and schema config overview -.pre-config { - color: #303030; - background-color: white; - border: 0px; -} - -// Override the underline in pre for this class -pre.pre-config > a { - color: #303030; - &:hover { - //color: darken(#303030, 5%); // Darker on hover - filter: brightness(30%); - } - text-decoration: none; -} - -// Floats on top of the pre to copy from. Copy appearance from pre -.pre-copy-button { - position: absolute; - right: 0; - border: none; - border-radius: 4px; - background-color: #fff; - cursor: pointer; -} - -.pre-parent { - position: relative; -} - -// pre within figure is generated by liquid highlight inside the explicit pre -figure > pre { - padding: 0px; - margin-left: 0px; - margin-right: 0px; - margin-top: 0px; - margin-bottom: -10px; // correct for mysterious extra whitespace after the inner

    -    border: none;
    -}
    -
    -code {
    -    padding: 2px 4px;
    -    border-radius: 4px;
    -}
    -
    -// Rouge-generated pre elements have code inside, make look like rest of pre */
    -// Revert the padding from  elements in text, no pad in 
     */
    -pre > code {
    -    padding: 0px;
    -}
    -
    -// Sidebar (TODO: Not used any more?)
    -
    -.collapse-parent {
    -    cursor: pointer;
    -}
    -
    -.collapse-all {
    -    cursor: pointer;
    -}
    -
    -.collapsed {
    -    display: none !important;
    -}
    -
    -.denali-theme-vespa-light .tabs.is-primary.is-vertical {
    -    min-width: 325px;
    -    width: 325px;
    -}
    -
    -.denali-theme-vespa-light .tabs.is-primary.is-vertical ul {
    -    padding-top: 5px;
    -}
    -
    -.denali-theme-vespa-light .tabs.is-primary.is-vertical ul li.tabs__section-header {
    -    white-space: normal;
    -    height: auto;
    -}
    -
    -.denali-theme-vespa-light .tabs.is-primary.is-vertical ul li {
    -    height: auto;
    -    a {
    -        color: $color-brand-400;
    -    }
    -    a:hover {
    -        color: $color-brand-600;
    -    }
    -
    -}
    -
    -.denali-theme-vespa-light .tabs.is-primary.is-vertical ul li:hover {
    -    background: rgba($color-brand-500, .2);
    -    box-shadow: none;
    -}
    -.denali-theme-vespa-light .tabs.is-primary.is-vertical ul {
    -    margin: 10px;
    -    padding: 0;
    -
    -}
    -.denali-theme-vespa-light .tabs.is-primary.is-vertical ul.show  {
    -    background: rgba($color-brand-500, .2);
    -    border-radius: $border-radius-md;
    -}
    -
    -.denali-theme-vespa-light .tabs.is-primary.is-vertical ul.show li.tabs__section-header {
    -    margin-top: 0;
    -    padding-top: 0;
    -    background-color: $color-brand-400;
    -    border-radius: $border-radius-sm;
    -    a {
    -        font-weight: bold;
    -        color: white;
    -    }
    -}
    -
    -.denali-theme-vespa-light .tabs.is-primary.is-vertical ul ul.inner-ul {
    -    padding: 0;
    -}
    -
    -.denali-theme-vespa-light .tabs.is-primary.is-vertical ul.show ul.inner-ul.show li.tabs__section-sub {
    -    margin-top: 0;
    -    padding-top: 0;
    -    background-color: $color-brand-500;
    -    border-radius: $border-radius-sm;
    -    a {
    -        font-weight: bold;
    -        color: white;
    -    }
    -}
    -
    -.denali-theme-vespa-light .tabs.is-primary.is-vertical ul.show ul.inner-ul.show {
    -    margin: 0 14px;
    -}
    -
    -.denali-theme-vespa-light .tabs.is-primary.is-vertical ul ul.inner-ul {
    -    height: auto;
    -    margin: 2px 16px;
    -
    -}
    -.denali-theme-vespa-light .tabs.is-primary.is-vertical ul li.tabs__section-header {
    -    height: auto;
    -    padding: 0;
    -}
    -
    -.tabs {
    -    position: fixed;
    -    top: 60px;
    -    bottom: 0;
    -    left: 0;
    -    z-index: 3;
    -}
    -
    -a.tab-link {
    -    word-wrap: break-word;
    -    white-space: normal;
    -    font-weight: normal;
    -}
    -
    -dt {
    -    margin-top: 10px;
    -}
    -
    -ul {
    -    list-style-type: disc;
    -}
    -
    -ul, ol {
    -    padding-inline-start: 40px;
    -}
    -
    -table.table tbody th {
    -    font-weight: bolder;
    -}
    -
    -/* Ensure the content is not too wide, hence not wrap under the sidebar, using white-space: normal */
    -table.table th, table.table td {
    -    white-space: normal;
    -    vertical-align: top;
    -}
    -
    -table {
    -    margin-bottom: 20px;
    -}
    -
    -.denali-theme-vespa-light table.table th, .denali-theme-vespa-light table.table td {
    -    padding-bottom: 10px;
    -}
    -
    -// Copy thead style for use in table subheadings to make look similar
    -.trx {
    -    height: 14px !important;
    -    border-bottom: solid 2px #e8e8e8;
    -}
    -.thx {
    -    text-transform: uppercase;
    -    font-weight: normal !important;
    -    color: #303030;
    -    font-size: 1.2rem;
    -    padding: 10px;
    -}
    -
    -
    -/* Search suggest dropdown */
    -.dropdown {
    -    background-color: $color-brand-200;
    -    position: absolute;
    -    margin-left: 0.4em;
    -    top: 35px;
    -    left: 0;
    -    right: 0;
    -}
    -
    -.dropdown.show {
    -    display: inline-block;
    -    z-index: 7;
    -}
    -
    -.dropdown.hide {
    -    display: none;
    -}
    -
    -.dropdown > p {
    -    padding-left: 0.83em;
    -    padding-top: 0.25em;
    -    padding-bottom: 0.25em;
    -    text-decoration: none;
    -    display: block;
    -    user-select: none;
    -    margin: 0;
    -    border: 0;
    -}
    -
    -.dropdown > p:hover {
    -    background-color: $color-brand-300;
    -}
    -
    -#selectedDropdownElement {
    -    background-color: $color-brand-300;
    -}
    -
    -.suggestion-doc {
    -    font-weight: 500;
    -    cursor: pointer;
    -}
    -
    -.suggestion-term {
    -    font-style: italic;
    -}
    -
    -.suggestion-separator {
    -    margin: 0.5em 0.83em;
    -    border: 0;
    -    border-top: 1px solid $color-brand-300;
    -}
    -
    -.filepath {
    -    -webkit-font-smoothing: auto;
    -    overflow: auto;
    -    page-break-inside: avoid;
    -    display: block;
    -    line-height: 1.42857143;
    -    word-break: break-all;
    -    word-wrap: break-word;
    -    border: 1px solid #404040;
    -    white-space: pre-wrap;
    -    border-radius: 4px;
    -    padding: 14px;
    -    margin-bottom: 10px;
    -    margin-top: 10px;
    -    color: #808080
    -}
    -
    -
    -/* Query results */
    -.search-result-list {
    -    list-style-type: none;
    -}
    -
    -.search-result-item {
    -    margin-top: 25px;
    -}
    -
    -.search-result-link {
    -    font-size: 0.85em;
    -    color: #3c763d;
    -}
    -
    -mark {
    -    background-color: white;
    -    font-weight: bold;
    -}
    -
    -
    -/* Boxes on index page */
    -.denali-theme-vespa .box {
    -    background: #f8f8f8;
    -}
    -
    -
    -/* Typography */
    -.denali-theme-vespa p, .denali-theme-vespa-light p {
    -    line-height: 1.4;
    -}
    -
    -
    -// Notes, information, warning
    -/* ToDo: Harmonize colors below with new color scheme */
    -.vespa-notification {
    -    padding: 15px;
    -    margin-bottom: 20px;
    -    border: 1px solid transparent;
    -    border-radius: 4px;
    -}
    -// Make links stand out in the note section, in some color combinations hard to see
    -div.vespa-notification > a {
    -    text-decoration: underline;
    -}
    -
    -.vespa-notification-note {
    -    color: #31708f;
    -    background-color: #d9edf7;
    -    border-color: #bce8f1;
    -}
    -
    -.vespa-notification-important {
    -    color: #8a6d3b;
    -    background-color: #fcf8e3;
    -    border-color: #faebcc;
    -}
    -
    -.vespa-notification-prereqs {
    -    color: #8a6d3b;
    -    background-color: #fcf8e3;
    -    border-color: #faebcc;
    -}
    -
    -.vespa-notification-warning {
    -    color: #a94442;
    -    background-color: #f2dede;
    -    border-color: #ebccd1;
    -}
    -
    -.vespa-notification-query {
    -    color: #7ab648;
    -    background-color: #e8e8e8;
    -    border-color: #e8e8e8;
    -}
    -
    -.vespa-notification-video {
    -    color: #303030;
    -    background-color: #e8e8e8;
    -    border-color: #e8e8e8;
    -}
    -
    -.vespa-notification-new {
    -    color: #8a6d3b;
    -    background-color: #fcf8e3;
    -    border-color: #faebcc;
    -}
    -
    -// Quotes - override Denali settings back to default + italics
    -blockquote {
    -    display: block;
    -    margin-block-start: 1em;
    -    margin-block-end: 1em;
    -    margin-inline-start: 40px;
    -    margin-inline-end: 40px;
    -    font-style: italic;
    -}
    -
    -// Hidden anchors are used in headings, visible when hovered over, or parent heading hover
    -.a-hidden {
    -    opacity: 0;
    -}
    -.a-hidden:hover {
    -    opacity: 1;
    -}
    -
    -.querystring-x, .yql-x {
    -    background-color: #e8e8e8;
    -    padding: 5px 5px 5px 5px;
    -    border-radius: 5px;
    -}
    -
    -.chip {
    -    padding: 0 2rem;
    -    align-items: center;
    -    border-radius: 6.25rem;
    -    column-gap: 0.9375rem;
    -    display: inline-flex;
    -    justify-content: center;
    -    margin: 20px 20px 0px 20px;
    -
    -    span {
    -        font-weight: 600;
    -        font-size: 1.6rem;
    -        line-height: 4rem;
    -    }
    -}
    -
    -.chip-cloud {
    -    background-color: #baedce;
    -    color: #2e2f27;
    -}
    -
    -.chip-self-managed {
    -    background-color: #b7e2f1;
    -    color: #2e2f27;
    -}
    -
    -.chip-enterprise {
    -    background-color: #97c2d1;
    -    color: #2e2f27;
    -}
    -
    -.tooltip {
    -    position: relative;
    -    display: inline-block;
    -}
    -
    -.tooltiptext {
    -     top: 120%;
    -}
    -
    -.tooltip .tooltiptext {
    -    visibility: hidden;
    -    width: 300px;
    -    background-color: #2e2f27;
    -    color: white;
    -    text-align: left;
    -    padding: 5px;
    -    border-radius: 6px;
    -    position: absolute;
    -    z-index: 1;
    -    margin-left: 30px;
    -    opacity: 0;
    -    transition: opacity 0.3s;
    -    line-height: 2.5rem;
    -}
    -
    -.tooltip:hover .tooltiptext {
    -    visibility: visible;
    -    opacity: 1;
    -}
    -
    -
    -/* General TOC styles */
    -.toc ul {
    -    overflow-y: auto;
    -    list-style: none;
    -    padding: 0;
    -    margin: 20px 0;
    -    padding: 10px 10px;
    -    font-size: 16px;
    -    color: #333;
    -    top: 20 px;
    -    right: 10px;
    -    flex-direction: column;
    -    flex-wrap: wrap;
    -
    -    max-height: 80vh;
    -}
    -.toc ul h2 {
    - color: $color-brand-400;
    -}
    -.toc ul li {
    -  margin-bottom: 8px;
    -  text-wrap: wrap;
    -}
    -
    -/* TOC link styles */
    -.toc ul li a {
    -    text-decoration: none;
    -    color: $color-brand-400;
    -    padding: 5px 10px 5px 7px;
    -    border-left: 3px solid transparent;
    -    border-radius: 5px;
    -    display: block;
    -    transition: background-color 0.3s ease, color 0.3s ease, border-color 0.3s ease;
    -    box-shadow: none;
    -    white-space: normal;
    -    overflow: visible;
    -    text-overflow: unset;
    -    word-break: break-word;
    -}
    -
    -  /* Widths per heading level */
    -  .toc ul .toc-level-0 > a {
    -    width: 240px;
    -}
    -
    -  .toc ul .toc-level-1 > a {
    -    width: 220px;
    -}
    -
    -  .toc ul .toc-level-2 > a {
    -    width: 200px;
    -}
    -
    -/* Hover effects */
    -.toc ul li a:hover {
    -    background-color: $color-brand-400;
    -    color: #fff;
    -    white-space: normal;
    -    overflow: visible;
    -    word-break: break-word;
    -
    -}
    -
    -/* Neutral styling for TOC headings */
    -.toc ul .toc-level-0 {
    -    font-weight: bold;
    -    font-size: 18px;
    -}
    -
    -  .toc ul .toc-level-1 {
    -    font-weight: bold;
    -    padding-left: 10px;
    -    font-size: 16px;
    -}
    -
    -  .toc ul .toc-level-2 {
    -    padding-left: 20px;
    -    font-size: 14px;
    -}
    -
    -/* FAQ-toc */
    -.toc.faq-toc ul .toc-level-0 > a {
    -    width: 100%;
    -}
    -.toc.faq-toc ul .toc-level-1 > a {
    -    width: 90%;
    -}
    -
    -.toc.faq-toc ul .toc-level-2 > a {
    -    width: 80%;
    -}
    -
    -
    -/* Offset anchor targets so the fixed navbar doesn't cover them */
    -#page-title {
    -    scroll-margin-top: 70px;
    -}
    -
    -
    -/* Active section highlight */
    -.toc ul li.toc-active > a {
    -    background-color: rgba($color-brand-400, 0.12);
    -    color: $color-brand-400;
    -    border-color: $color-brand-400;
    -    font-weight: bold;
    -}
    -
    -
    -/* Container overflow */
    -
    -.container-overflow {
    -    overflow-x: clip;
    -}
    -
    -.content-overflow {
    -    word-break: break-word;
    -    overflow-wrap: break-word;
    -    word-wrap: break-word;
    -    max-width: 100%;
    -}
    -
    -/* Formulas scalling */
    -
    -.MathJax {
    -    font-size: clamp(0.6em, 2vw, 0.8em);
    -    max-width: 100%;
    -    height: auto;
    -}
    -
    -.CtxtMenu_InfoContent {
    -    font-size: 1.5em !important;
    -    width: 100%;
    -  }
    -
    -mjx-container[display="true"] > svg { // excluding display="inline" and others
    -    width: 100%;
    -    max-width: 100%; // limiting the width just so it won't get too big
    -}
    -
    -mjx-container[display="true"] { // excluding display="inline" and others
    -    display: block;
    -    width: 100%;
    -}
    -
    -.equation-container {
    -    height: auto;
    -    overflow-x: auto; /* Allow horizontal scroll if formula gets too wide */
    -    display: block;
    -
    -}
    -
    -figure > div:has(> .MathJax) {
    -    width: 100%;
    -    max-width: 100%;
    -    height: auto;
    -    display: block;
    -  }
    -
    -/* This excludes tables that are not inside other tables */
    -table:not(table table) {
    -    display: block;
    -    overflow-x: auto;
    -    white-space: nowrap;
    -}
    -
    -.table th, .table td {
    -    min-width: fit-content;
    -    word-break: normal;
    -}
    -.col{
    -    min-width: 0;
    -}
    -
    -/*Grouping Interface*/
    -
    -/* Container styling */
    -.vespa-grouping-interface {
    -    font-size: inherit;
    -    max-width: 800px;
    -    margin: 0 auto;
    -    padding: 2rem 1rem;
    -  }
    -
    -  /* Query bar styling */
    -
    -  button.button.is-solid.is-small.run-button:hover {
    -    color: $color-brand-300 !important;
    -    background-color: $color-brand-400;
    -  }
    -
    -  #refresh-btn {
    -    background-color: transparent;
    -    padding: 0.5rem 0.75rem;
    -    color: #555;
    -    border: none
    -  }
    -  #refresh-btn span.d-icon {
    -    font-size: 1.5em;
    -  }
    -
    -  #refresh-btn:hover {
    -    color: $color-brand-400;
    -  }
    -
    -  /* Table styling */
    -  .grouping-table-container {
    -    overflow-x: auto;
    -    background: white;
    -    border-radius: 12px;
    -    box-shadow: 0 4px 16px rgba(0, 0, 0, 0.06);
    -    display: grid;
    -    text-align: center;
    -    margin-top: 10px;
    -    justify-content: center;
    -    width: 100%;
    -    grid-template-columns: 1fr;
    -  }
    -  .grouping-table-container.loading {
    -    display: flex;
    -    justify-content: center;
    -  }
    -
    -  .editor-container {
    -    max-width: 100%;
    -    margin: auto;
    -    margin-bottom: 2em !important;
    -    background: white;
    -    padding: 1rem;
    -    border-radius: 12px;
    -    box-shadow: 0 2px 10px rgba(0,0,0,0.1);
    -  }
    -  .grouping-table-container table.table {
    -    min-width: 100%;
    -    min-height: 100%;
    -    display: table;
    -  }
    -  .grouping-table-container table.table table {
    -    min-width: 100%;
    -    display: table;
    -    margin: 0;
    -  }
    -  .grouping-table-container table.table td:has(table.table) {
    -    min-width: fit-content;
    -    overflow-x: auto;
    -    grid-template-columns: 1fr;
    -    padding: 0;
    -  }
    -
    -  .grouping-table-container table.table table td {
    -    min-width: fit-content;
    -    min-height: 100%;
    -    word-break: normal;
    -  }
    -
    -  textarea.yql-cell-textarea, .editor-container textarea {
    -    width: 100%;
    -    font-family: monospace;
    -    font-size: 14px;
    -    padding: 10px;
    -    border: 1px solid #ccc;
    -    border-radius: 8px;
    -    resize: vertical;
    -  }
    -
    -  /* yql-cells(grouppung) */
    -  .yql-cell {
    -    margin: 1em 0;
    -    padding: 1em;
    -    border: 1px solid #ccc;
    -    border-radius: 8px;
    -    background: #fafafa;
    -  }
    -
    -  .yql-cell-label {
    -    font-weight: bold;
    -    margin-bottom: 4px;
    -  }
    -
    -  .yql-cell-textarea {
    -    width: 100%;
    -    min-height: 80px;
    -  }
    -
    -  .yql-cell-button {
    -    margin-top: 6px;
    -  }
    -
    -  .yql-cell-button:hover {
    -    background-color:$color-brand-300;
    -    color: $color-brand-100;
    -  }
    -
    -  #query-link-container {
    -    display: flex;
    -    justify-content: flex-end;
    -    margin-top: 10px;
    -    a {
    -        font-size: 14px;
    -        color: $color-brand-400;
    -        &:hover {
    -            color: $color-brand-300;
    -        }
    -    }
    -  }
    -
    -/* View as Markdown button styles */
    -.view-as-markdown-button {
    -    display: inline-flex;
    -    align-items: center;
    -    justify-content: center;
    -    gap: 6px;
    -    padding: 6px 10px;
    -    background-color: #f6f8fa;
    -    border: 1px solid #d1d5da;
    -    border-radius: 6px;
    -    font-size: 13px;
    -    font-weight: 500;
    -    color: #24292e;
    -    text-decoration: none;
    -    transition: all 0.2s ease-in-out;
    -    white-space: nowrap;
    -    line-height: 1;
    -    min-height: 34px;
    -    min-width: 34px; /* ensure square base for icon-only */
    -
    -    &:hover {
    -        background-color: #e1e4e8;
    -        text-decoration: none;
    -        color: #24292e;
    -        border-color: #c6cbd1;
    -    }
    -
    -    &:focus {
    -        outline: 2px solid #0969da;
    -        outline-offset: -2px;
    -    }
    -
    -    svg {
    -        width: 20px;
    -        height: 20px;
    -        flex-shrink: 0;
    -        display: block;
    -    }
    -
    -    span {
    -        font-size: 12px;
    -    }
    -}
    -
    -/* Icon-only (no text) variant */
    -.view-as-markdown-button.icon-only {
    -        padding: 6px; /* tighter since no label */
    -        min-width: 34px;
    -        svg { margin: 0; }
    -}
    -
    -/* High contrast / dark-theme future-proofing */
    -@media (prefers-contrast: more) {
    -    .view-as-markdown-button {
    -        border-color: #8a939b;
    -    }
    -    .view-as-markdown-button:focus { outline-width: 3px; }
    -}
    -
    -/* Responsive adjustments */
    -@media (max-width: 768px) {
    -    .view-as-markdown-button {
    -    padding: 4px 6px;
    -    font-size: 12px;
    -    min-height: 32px;
    -    min-width: 32px;
    -    svg { width: 18px; height: 18px; }
    -    span { font-size: 11px; }
    -    }
    -}
    -
    -.osano-cm-widget {
    -  display: none;
    -}
    diff --git a/css/typography.scss b/css/typography.scss
    deleted file mode 100644
    index f6a9a0ca6a..0000000000
    --- a/css/typography.scss
    +++ /dev/null
    @@ -1,50 +0,0 @@
    ----
    ----
    -@font-face {
    -    font-family: 'Roobert';
    -    src: url('/assets/fonts/Roobert-Medium.woff2') format('woff2'),
    -    url('/assets/fonts/Roobert-Medium.woff') format('woff');
    -    font-weight: 500;
    -    font-style: normal;
    -}
    -
    -@font-face {
    -    font-family: 'Roobert';
    -    src: url('/assets/fonts/Roobert-MediumItalic.woff2') format('woff2'),
    -    url('/assets/fonts/Roobert-MediumItalic.woff') format('woff');
    -    font-weight: 500;
    -    font-style: italic;
    -}
    -
    -@font-face {
    -    font-family: 'Roobert';
    -    src: url('/assets/fonts/Roobert-Regular.woff2') format('woff2'),
    -    url('/assets/fonts/Roobert-Regular.woff') format('woff');
    -    font-weight: 400;
    -    font-style: normal;
    -}
    -
    -@font-face {
    -    font-family: 'Roobert';
    -    src: url('/assets/fonts/Roobert-RegularItalic.woff2') format('woff2'),
    -    url('/assets/fonts/Roobert-RegularItalic.woff') format('woff');
    -    font-weight: 400;
    -    font-style: italic;
    -}
    -
    -@font-face {
    -    font-family: 'Roobert';
    -    src: url('/assets/fonts/Roobert-SemiBold.woff2') format('woff2'),
    -    url('/assets/fonts/Roobert-SemiBold.woff') format('woff');
    -    font-weight: 600;
    -    font-style: normal;
    -}
    -
    -@font-face {
    -    font-family: 'Roobert';
    -    src: url('/assets/fonts/Roobert-SemiBoldItalic.woff2') format('woff2'),
    -    url('/assets/fonts/Roobert-SemiBoldItalic.woff') format('woff');
    -    font-weight: 600;
    -    font-style: italic;
    -}
    -
    diff --git a/custom.css b/custom.css
    new file mode 100644
    index 0000000000..fc31b6ae9c
    --- /dev/null
    +++ b/custom.css
    @@ -0,0 +1,109 @@
    +#navbar {
    +  border-bottom: 1px solid color-mix(in srgb, currentColor 10%, transparent);
    +  backdrop-filter: blur(12px);
    +}
    +
    +html:not(.dark) #search-bar-entry-mobile svg {
    +  background-color: #525654 !important;
    +}
    +
    +html:not(.dark) #assistant-entry-mobile svg,
    +html:not(.dark) button[data-component-name="theme-toggle"] svg {
    +  color: #525654 !important;
    +}
    +
    +.dark #search-bar-entry-mobile svg {
    +  background-color: #d0d4d2 !important;
    +}
    +
    +.dark #assistant-entry-mobile svg,
    +.dark button[data-component-name="theme-toggle"] svg {
    +  color: #d0d4d2 !important;
    +}
    +
    +mdx-content {
    +  line-height: 1.7;
    +}
    +
    +mdx-content a {
    +  text-underline-offset: 0.18em;
    +}
    +
    +.vespa-table {
    +  display: block;
    +  width: 100%;
    +  max-width: 100%;
    +  overflow-x: auto;
    +  border-collapse: collapse;
    +  border: 1px solid rgba(20, 130, 76, 0.18);
    +  border-radius: 0.75rem;
    +  font-size: 0.92rem;
    +}
    +
    +.vespa-table th,
    +.vespa-table td {
    +  min-width: 9rem;
    +  padding: 0.7rem 0.85rem;
    +  vertical-align: top;
    +  border-bottom: 1px solid rgba(20, 130, 76, 0.14);
    +}
    +
    +.vespa-table th {
    +  background: rgba(20, 130, 76, 0.08);
    +  font-weight: 650;
    +  text-align: left;
    +}
    +
    +.vespa-table code {
    +  white-space: normal;
    +  overflow-wrap: anywhere;
    +}
    +
    +.vespa-table__cell--center {
    +  text-align: center;
    +}
    +
    +.vespa-table__cell--right {
    +  text-align: right;
    +}
    +
    +toc-item {
    +  border-radius: 0.5rem;
    +}
    +
    +toc-item[data-active],
    +toc-item[data-active-deepest] {
    +  color: #14824c;
    +}
    +
    +@media (prefers-color-scheme: dark) {
    +  .vespa-table {
    +    border-color: rgba(97, 215, 144, 0.22);
    +  }
    +
    +  .vespa-table th,
    +  .vespa-table td {
    +    border-bottom-color: rgba(97, 215, 144, 0.16);
    +  }
    +
    +  .vespa-table th {
    +    background: rgba(97, 215, 144, 0.09);
    +  }
    +
    +  toc-item[data-active],
    +  toc-item[data-active-deepest] {
    +    color: #61d790;
    +  }
    +}
    +
    +@media (max-width: 768px) {
    +  .vespa-table {
    +    font-size: 0.86rem;
    +  }
    +
    +  .vespa-table th,
    +  .vespa-table td {
    +    min-width: 8rem;
    +    padding: 0.6rem 0.7rem;
    +  }
    +}
    diff --git a/docs.json b/docs.json
    new file mode 100644
    index 0000000000..e93d6a4bd3
    --- /dev/null
    +++ b/docs.json
    @@ -0,0 +1,639 @@
    +{
    +  "$schema": "https://mintlify.com/docs.json",
    +  "theme": "linden",
    +  "name": "Vespa Documentation",
    +  "colors": {
    +    "primary": "#61D78F",
    +    "light": "#61D78F",
    +    "dark": "#61D78F"
    +  },
    +  "background": {
    +    "color": {
    +      "light": "#FFFFFF",
    +      "dark": "#2E2E26"
    +    }
    +  },
    +  "favicon": "/favicon.png",
    +  "appearance": {
    +    "default": "system"
    +  },
    +  "fonts": {
    +    "heading": {
    +      "family": "Roobert",
    +      "source": "https://vespa.ai/vespa-content/themes/website-wp-theme/fonts/Roobert-Medium.woff",
    +      "format": "woff"
    +    },
    +    "body": {
    +      "family": "Roobert",
    +      "source": "https://vespa.ai/vespa-content/themes/website-wp-theme/fonts/Roobert-Regular.woff",
    +      "format": "woff"
    +    }
    +  },
    +  "navigation": {
    +    "tabs": [
    +      {
    +        "tab": "Home",
    +        "icon": "house",
    +        "pages": ["index"]
    +      },
    +      {
    +        "tab": "Guides",
    +        "icon": "book-open",
    +        "pages": [
    +          {
    +            "group": "Vespa Basics",
    +            "pages": [
    +              "en/basics/deploy-an-application",
    +              "en/basics/applications",
    +              "en/basics/schemas",
    +              "en/basics/writing",
    +              "en/basics/querying",
    +              "en/basics/ranking",
    +              "en/basics/operations",
    +              "en/basics/whats-more"
    +            ]
    +          },
    +          {
    +            "group": "Learn More",
    +            "pages": [
    +              "en/learn/overview",
    +              "en/learn/llm-help",
    +              "en/learn/features",
    +              "en/learn/tutorials",
    +              "en/learn/glossary",
    +              "en/learn/releases",
    +              "en/learn/tenant-apps-instances",
    +              "en/learn/migrating-to-cloud",
    +              "en/learn/migrating-from-elastic-search",
    +              "en/learn/about-documentation",
    +              "en/learn/contributing"
    +            ]
    +          },
    +          {
    +            "group": "Applications & Components",
    +            "pages": [
    +              "en/applications/developer-guide",
    +              "en/applications/ide-support",
    +              "en/applications/deployment",
    +              "en/applications/vespaignore",
    +              "en/applications/containers",
    +              "en/applications/components",
    +              "en/applications/searchers",
    +              "en/applications/document-processors",
    +              "en/applications/request-handlers",
    +              "en/applications/result-renderers",
    +              "en/applications/dependency-injection",
    +              "en/applications/configuring-components",
    +              "en/applications/chaining",
    +              "en/applications/inspecting-structured-data",
    +              "en/applications/web-services",
    +              "en/applications/unit-testing",
    +              "en/applications/testing",
    +              "en/applications/config-system",
    +              "en/applications/processing",
    +              "en/applications/bundles",
    +              "en/applications/using-zookeeper",
    +              "en/applications/http-servers-and-filters",
    +              "en/applications/pluggable-frameworks",
    +              "en/applications/configapi-dev"
    +            ]
    +          },
    +          {
    +            "group": "Schemas and documents",
    +            "pages": [
    +              "en/schemas/documents",
    +              "en/schemas/inheritance-in-schemas",
    +              "en/schemas/concrete-documents",
    +              "en/schemas/parent-child",
    +              "en/schemas/structs",
    +              "en/schemas/predicate-fields",
    +              "en/schemas/exposing-schema-information"
    +            ]
    +          },
    +          {
    +            "group": "Reading and writing",
    +            "pages": [
    +              "en/writing/reads-and-writes",
    +              "en/writing/document-v1-api-guide",
    +              "en/writing/indexing",
    +              "en/writing/initial-batch-feed",
    +              "en/writing/visiting",
    +              "en/writing/document-api-guide",
    +              "en/writing/partial-updates",
    +              "en/writing/batch-delete",
    +              "en/writing/feed-block",
    +              "en/writing/document-routing",
    +              "en/writing/indexing-paged-vectors"
    +            ]
    +          },
    +          {
    +            "group": "Querying",
    +            "pages": [
    +              "en/querying/query-api",
    +              "en/querying/query-language",
    +              "en/querying/grouping",
    +              "en/querying/federation",
    +              "en/querying/query-profiles",
    +              "en/querying/vector-search-intro",
    +              "en/querying/nearest-neighbor-search",
    +              "en/querying/approximate-nn-hnsw",
    +              "en/querying/nearest-neighbor-search-guide",
    +              "en/querying/text-matching",
    +              "en/querying/searching-multivalue-fields",
    +              "en/querying/geo-search",
    +              "en/querying/document-summaries",
    +              "en/querying/result-diversity",
    +              "en/querying/page-templates"
    +            ]
    +          },
    +          {
    +            "group": "Ranking and inference",
    +            "pages": [
    +              "en/ranking/ranking-intro",
    +              "en/ranking/ranking-expressions-features",
    +              "en/ranking/multivalue-query-operators",
    +              "en/ranking/tensor-user-guide",
    +              "en/ranking/tensor-examples",
    +              "en/ranking/phased-ranking",
    +              "en/ranking/tensorflow",
    +              "en/ranking/onnx",
    +              "en/ranking/xgboost",
    +              "en/ranking/lightgbm",
    +              "en/ranking/wand",
    +              "en/ranking/bm25",
    +              "en/ranking/nativerank",
    +              "en/ranking/cross-encoders",
    +              "en/ranking/reranking-in-searcher",
    +              "en/ranking/significance",
    +              "en/ranking/stateless-model-evaluation"
    +            ]
    +          },
    +          {
    +            "group": "RAG and embedding",
    +            "pages": [
    +              "en/rag/rag",
    +              "en/rag/working-with-chunks",
    +              "en/rag/embedding",
    +              "en/rag/binarizing-vectors",
    +              "en/rag/llms-in-vespa",
    +              "en/rag/local-llms",
    +              "en/rag/external-llms",
    +              "en/rag/document-enrichment",
    +              "en/rag/model-hub"
    +            ]
    +          },
    +          {
    +            "group": "Linguistics and text processing",
    +            "pages": [
    +              {
    +                "group": "Linguistics",
    +                "pages": [
    +                  "/en/linguistics/linguistics",
    +                  "/en/linguistics/linguistics-opennlp",
    +                  "/en/linguistics/lucene-linguistics",
    +                  "/en/linguistics/linguistics-custom"
    +                ]
    +              },
    +              "en/linguistics/query-rewriting",
    +              "en/linguistics/troubleshooting-encoding"
    +            ]
    +          },
    +          {
    +            "group": "content and elasticity",
    +            "pages": [
    +              "/en/content/proton",
    +              "/en/content/content-nodes",
    +              "/en/content/elasticity",
    +              "/en/content/attributes",
    +              "/en/content/consistency",
    +              "/en/content/idealstate",
    +              "/en/content/buckets"
    +            ]
    +          },
    +          {
    +            "group": "Performance",
    +            "pages": [
    +              "en/performance",
    +              "en/performance/practical-search-performance-guide",
    +              "en/performance/sizing-search",
    +              "en/performance/sizing-feeding",
    +              "en/performance/node-resources",
    +              {
    +                "group": "Instance types",
    +                "pages": [
    +                  "en/performance/instance-types/aws-instance-types",
    +                  "en/performance/instance-types/gcp-instance-types",
    +                  "en/performance/instance-types/azure-instance-types"
    +                ]
    +              },
    +              "en/performance/topology-and-resizing",
    +              "en/performance/streaming-search",
    +              "en/performance/benchmarking",
    +              "en/performance/benchmarking-cloud",
    +              "en/performance/memory-visualizer",
    +              "en/performance/profiling",
    +              "en/performance/container-tuning",
    +              "en/performance/rate-limiting-searcher",
    +              "en/performance/graceful-degradation",
    +              "en/performance/caches-in-vespa",
    +              "en/performance/container-http",
    +              "en/performance/http2",
    +              "en/performance/feature-tuning",
    +              "en/performance/valgrind"
    +            ]
    +          },
    +          {
    +            "group": "Operations",
    +            "pages": [
    +              "en/cloud/quota",
    +              "en/operations/environments",
    +              "en/operations/zones",
    +              "en/operations/production-deployment",
    +              "en/operations/deployment-variants",
    +              "en/operations/automated-deployments",
    +              "en/operations/autoscaling",
    +              {
    +                "group": "Enclave: Bring your own cloud",
    +                "pages": [
    +                  "en/operations/enclave/enclave",
    +                  "en/operations/enclave/aws-getting-started",
    +                  "en/operations/enclave/aws-architecture",
    +                  "en/operations/enclave/azure-getting-started",
    +                  "en/operations/enclave/azure-architecture",
    +                  "en/operations/enclave/gcp-getting-started",
    +                  "en/operations/enclave/gcp-architecture",
    +                  "en/operations/enclave/archive",
    +                  "en/operations/enclave/operations"
    +                ]
    +              },
    +              "en/operations/reindexing",
    +              "en/operations/data-management",
    +              "en/operations/cloning",
    +              "en/operations/monitoring",
    +              "en/operations/metrics",
    +              "en/operations/notifications",
    +              "en/cloud/support",
    +              "en/operations/deployment-patterns",
    +              "en/operations/private-endpoints",
    +              "en/operations/endpoint-routing",
    +              "en/operations/access-logging",
    +              {
    +                "group": "Artifact archive",
    +                "pages": [
    +                  "en/operations/archive/archive-guide",
    +                  "en/operations/archive/archive-guide-aws",
    +                  "en/operations/archive/archive-guide-gcp"
    +                ]
    +              },
    +              "en/operations/deleting-applications",
    +              {
    +                "group": "Self-managed",
    +                "pages": [
    +                  "en/operations/self-managed/admin-procedures",
    +                  "en/operations/self-managed/multinode-systems",
    +                  "en/operations/self-managed/files-processes-and-ports",
    +                  "en/operations/self-managed/node-setup",
    +                  "en/operations/self-managed/using-kubernetes-with-vespa",
    +                  "en/operations/self-managed/build-install",
    +                  "en/operations/self-managed/monitoring",
    +                  "en/operations/self-managed/content-node-recovery",
    +                  "en/operations/self-managed/configuration-server",
    +                  "en/operations/self-managed/live-upgrade",
    +                  "en/operations/self-managed/config-sentinel",
    +                  "en/operations/self-managed/config-proxy",
    +                  "en/operations/self-managed/docker-containers",
    +                  "en/operations/self-managed/vespa-gpu-container",
    +                  "en/operations/self-managed/cpu-support",
    +                  "en/operations/self-managed/slobrok",
    +                  "en/operations/self-managed/procedure-change-attribute-index",
    +                  "en/operations/self-managed/container",
    +                  "en/operations/self-managed/sizing-examples",
    +                  "en/operations/self-managed/vespa-support"
    +                ]
    +              },
    +              {
    +                "group": "Kubernetes",
    +                "pages": [
    +                  "en/operations/kubernetes/vespa-on-kubernetes",
    +                  "en/operations/kubernetes/architecture",
    +                  {
    +                    "group": "Deployment",
    +                    "pages": [
    +                      "en/operations/kubernetes/deployment/installation",
    +                      "en/operations/kubernetes/deployment/local-deployment",
    +                      "en/operations/kubernetes/deployment/ecr-pull-through-cache",
    +                      "en/operations/kubernetes/deployment/dev-mode",
    +                      "en/operations/kubernetes/deployment/permissions"
    +                    ]
    +                  },
    +                  {
    +                    "group": "Operations",
    +                    "pages": [
    +                      "en/operations/kubernetes/operations/operations",
    +                      "en/operations/kubernetes/operations/upgrades",
    +                      "en/operations/kubernetes/operations/delete-vespaset",
    +                      "en/operations/kubernetes/operations/monitoring"
    +                    ]
    +                  },
    +                  {
    +                    "group": "Configuration",
    +                    "pages": [
    +                      "en/operations/kubernetes/configuration/configure-local-storage-type",
    +                      "en/operations/kubernetes/logging",
    +                      "en/operations/kubernetes/ingress",
    +                      "en/operations/kubernetes/custom-overrides-podtemplate",
    +                      "en/operations/kubernetes/tls"
    +                    ]
    +                  }
    +                ]
    +              }
    +            ]
    +          },
    +          {
    +            "group": "Security",
    +            "pages": [
    +              "en/security/security",
    +              "en/security/guide",
    +              "en/security/secret-store",
    +              "en/security/cloudflare-workers",
    +              "en/security/whitepaper",
    +              "en/security/securing-your-vespa-installation",
    +              "en/security/mtls"
    +            ]
    +          },
    +          {
    +            "group": "Clients",
    +            "pages": [
    +              "en/clients/vespa-cli",
    +              "en/clients/python-client",
    +              "en/clients/vespa-feed-client",
    +              "en/clients/http-best-practices"
    +            ]
    +          },
    +          {
    +            "group": "Modules",
    +            "pages": [
    +              {
    +                "group": "E-commerce",
    +                "pages": [
    +                  "en/modules/e-commerce/multi-currency-filtering",
    +                  "en/modules/e-commerce/saved-search",
    +                  "en/modules/e-commerce/using-features-together"
    +                ]
    +              }
    +            ]
    +          }
    +        ]
    +      },
    +      {
    +        "tab": "FAQ",
    +        "icon": "circle-question",
    +        "pages": [
    +          {
    +            "group": "FAQ",
    +            "pages": ["en/learn/faq"]
    +          }
    +        ]
    +      },
    +      {
    +        "tab": "Reference",
    +        "icon": "code",
    +        "groups": [
    +          {
    +            "group": "APIs",
    +            "pages": [
    +              "en/reference/api/api",
    +              "en/reference/api/query",
    +              "en/reference/api/document-v1",
    +              "en/reference/api/state-v1",
    +              "en/reference/api/deploy-v2",
    +              "en/reference/api/application-v2",
    +              "en/reference/api/config-v2",
    +              "en/reference/api/cluster-v2",
    +              "en/reference/api/metrics-v1",
    +              "en/reference/api/metrics-v2",
    +              "en/reference/api/prometheus-v1"
    +            ]
    +          },
    +          {
    +            "group": "Applications and components",
    +            "pages": [
    +              "en/reference/applications/application-packages",
    +              {
    +                "group": "services.xml",
    +                "pages": [
    +                  "en/reference/applications/services/services",
    +                  "en/reference/applications/services/admin",
    +                  "en/reference/applications/services/container",
    +                  "en/reference/applications/services/content",
    +                  "en/reference/applications/services/docproc",
    +                  "en/reference/applications/services/http",
    +                  "en/reference/applications/services/processing",
    +                  "en/reference/applications/services/search"
    +                ]
    +              },
    +              "en/reference/applications/deployment",
    +              "en/reference/applications/hosts",
    +              "en/reference/applications/validation-overrides",
    +              "en/reference/applications/components",
    +              "en/reference/applications/config-files",
    +              "en/reference/applications/testing",
    +              "en/reference/applications/testing-java"
    +            ]
    +          },
    +          {
    +            "group": "Schemas and documents",
    +            "pages": [
    +              "en/reference/schemas/schemas",
    +              "en/reference/schemas/document-json-format",
    +              "en/reference/schemas/document-field-path"
    +            ]
    +          },
    +          {
    +            "group": "Reading and writing",
    +            "pages": [
    +              "en/reference/writing/indexing-language",
    +              "en/reference/writing/document-selector-language"
    +            ]
    +          },
    +          {
    +            "group": "Querying",
    +            "pages": [
    +              "en/reference/querying/yql",
    +              "en/reference/querying/simple-query-language",
    +              "en/reference/querying/json-query-language",
    +              "en/reference/querying/grouping-language",
    +              "en/reference/querying/sorting-language",
    +              "en/reference/querying/query-profiles",
    +              "en/reference/querying/semantic-rules",
    +              "en/reference/querying/default-result-format",
    +              "en/reference/querying/page-result-format",
    +              "en/reference/querying/page-templates"
    +            ]
    +          },
    +          {
    +            "group": "Ranking and inference",
    +            "pages": [
    +              "en/reference/ranking/ranking-expressions",
    +              "en/reference/ranking/tensor",
    +              "en/reference/ranking/rank-features",
    +              "en/reference/ranking/nativerank",
    +              "en/reference/ranking/string-segment-match",
    +              "en/reference/ranking/rank-feature-configuration",
    +              "en/reference/ranking/rank-types",
    +              "en/reference/ranking/model-files",
    +              "en/reference/ranking/constant-tensor-json-format"
    +            ]
    +          },
    +          {
    +            "group": "RAG and embedding",
    +            "pages": ["en/reference/rag/chunking", "en/reference/rag/embedding"]
    +          },
    +          {
    +            "group": "Operations",
    +            "pages": [
    +              "en/reference/operations/health-checks",
    +              "en/reference/operations/log-files",
    +              "en/reference/operations/tools",
    +              {
    +                "group": "Metrics",
    +                "pages": [
    +                  "en/reference/operations/metrics/metrics",
    +                  "en/reference/operations/metrics/default-metric-set",
    +                  "en/reference/operations/metrics/vespa-metric-set",
    +                  "en/reference/operations/metrics/metric-units",
    +                  "en/reference/operations/metrics/container",
    +                  "en/reference/operations/metrics/distributor",
    +                  "en/reference/operations/metrics/searchnode",
    +                  "en/reference/operations/metrics/storage",
    +                  "en/reference/operations/metrics/configserver",
    +                  "en/reference/operations/metrics/logd",
    +                  "en/reference/operations/metrics/nodeadmin",
    +                  "en/reference/operations/metrics/slobrok",
    +                  "en/reference/operations/metrics/clustercontroller",
    +                  "en/reference/operations/metrics/sentinel"
    +                ]
    +              },
    +              {
    +                "group": "Self-managed",
    +                "pages": ["en/reference/operations/self-managed/tools"]
    +              }
    +            ]
    +          },
    +          {
    +            "group": "Security",
    +            "pages": ["en/reference/security/mtls"]
    +          },
    +          {
    +            "group": "Clients",
    +            "pages": [
    +              {
    +                "group": "Vespa CLI",
    +                "pages": [
    +                  "en/reference/clients/vespa-cli/vespa",
    +                  "en/reference/clients/vespa-cli/vespa_activate",
    +                  "en/reference/clients/vespa-cli/vespa_auth",
    +                  "en/reference/clients/vespa-cli/vespa_clone",
    +                  "en/reference/clients/vespa-cli/vespa_config",
    +                  "en/reference/clients/vespa-cli/vespa_curl",
    +                  "en/reference/clients/vespa-cli/vespa_deploy",
    +                  "en/reference/clients/vespa-cli/vespa_destroy",
    +                  "en/reference/clients/vespa-cli/vespa_document",
    +                  "en/reference/clients/vespa-cli/vespa_feed",
    +                  "en/reference/clients/vespa-cli/vespa_fetch",
    +                  "en/reference/clients/vespa-cli/vespa_inspect",
    +                  "en/reference/clients/vespa-cli/vespa_log",
    +                  "en/reference/clients/vespa-cli/vespa_prepare",
    +                  "en/reference/clients/vespa-cli/vespa_prod",
    +                  "en/reference/clients/vespa-cli/vespa_query",
    +                  "en/reference/clients/vespa-cli/vespa_status",
    +                  "en/reference/clients/vespa-cli/vespa_test",
    +                  "en/reference/clients/vespa-cli/vespa_version",
    +                  "en/reference/clients/vespa-cli/vespa_visit"
    +                ]
    +              }
    +            ]
    +          }
    +        ]
    +      }
    +    ],
    +    "global": {
    +      "anchors": [
    +        {
    +          "anchor": "Changelog",
    +          "href": "/en/reference/release-notes/vespa9",
    +          "icon": "clock-rotate-left"
    +        },
    +        {
    +          "anchor": "Vespa 8 release notes",
    +          "href": "/en/reference/release-notes/vespa8",
    +          "icon": "list"
    +        },
    +        {
    +          "anchor": "Vespa 7 release notes",
    +          "href": "/en/reference/release-notes/vespa7",
    +          "icon": "list"
    +        }
    +      ]
    +    }
    +  },
    +  "logo": {
    +    "light": "/logo/light.svg",
    +    "dark": "/logo/dark.svg",
    +    "href": "/"
    +  },
    +  "navbar": {
    +    "links": [
    +      {
    +        "label": "Blog",
    +        "href": "https://blog.vespa.ai/",
    +        "icon": "newspaper"
    +      },
    +      {
    +        "label": "vespa.ai",
    +        "href": "https://vespa.ai/",
    +        "icon": "external-link"
    +      },
    +      {
    +        "label": " ",
    +        "icon": "github",
    +        "href": "https://github.com/vespa-engine/vespa/"
    +      }
    +    ],
    +    "primary": {
    +      "type": "button",
    +      "label": "Console login",
    +      "href": "https://console.vespa-cloud.com/auth/login"
    +    }
    +  },
    +  "search": {
    +    "prompt": "Search Vespa docs..."
    +  },
    +  "contextual": {
    +    "options": [
    +      "assistant",
    +      "view",
    +      "copy",
    +      "chatgpt",
    +      "claude",
    +      "perplexity",
    +      "mcp",
    +      "cursor",
    +      "vscode"
    +    ],
    +    "display": "toc"
    +  },
    +  "footer": {
    +    "socials": {
    +      "github": "https://github.com/vespa-engine",
    +      "linkedin": "https://www.linkedin.com/company/vespa-ai/posts/?feedView=all",
    +      "x": "https://x.com/vespaengine",
    +      "youtube": "https://www.youtube.com/channel/UCVXw_f6UHff8-V9FA1LMIiw"
    +    }
    +  },
    +  "seo": {
    +    "metatags": {
    +      "robots": "noindex"
    +    }
    +  }
    +}
    diff --git a/en/applications/bundles.html b/en/applications/bundles.html
    deleted file mode 100644
    index 65eb0289db..0000000000
    --- a/en/applications/bundles.html
    +++ /dev/null
    @@ -1,904 +0,0 @@
    ----
    -# Copyright Vespa.ai. All rights reserved.
    -title: "Bundles"
    -redirect_from:
    -- /en/jdisc/developing-osgi-bundles.html
    -- /en/jdisc/bundle-troubleshooting.html
    -- /en/bundle-plugin.html
    -- /en/components/bundles.html
    ----
    -
    -

    - The Container uses OSGi to provide a modular platform - for developing applications that can be composed of many reusable components. - The user can deploy, upgrade and remove these components at runtime. -

    - - - -

    OSGi

    -

    - OSGi is a framework for modular development of Java applications, where a - set of resources called bundles can be installed. OSGi allows the developer to - control which resources (Java packages) in a bundle that should be available to - other bundles. Hence, you can explicitly declare a bundle's public API, - and also ensure that internal implementation details remain hidden. -

    -

    - Unless you're already familiar with OSGi, we recommend reading Richard S. Hall's presentation - Learning - to ignore OSGi, which explains the most important aspects that you must relate to as a bundle - developer. There are other good OSGi tutorials available: -

    - - -

    - JDisc uses OSGi's module and lifecycle layers, and does not provide any functionality - from the service layer. -

    - - - -

    OSGi bundles

    -

    - An OSGi bundle is a regular JAR file with a MANIFEST.MF file that describes its content, - what the bundle requires (imports) from other bundles, and what it provides (exports) to other - bundles. Below is an example of a typical bundle manifest with the most important headers: -

    -
    -Bundle-SymbolicName: com.yahoo.helloworld
    -Bundle-Description: A Hello World bundle
    -Bundle-Version: 1.0.0
    -Export-Package: com.yahoo.helloworld;version="1.0.0"
    -Import-Package: org.osgi.framework;version="1.3.0"
    -
    -

    - The meaning of the headers in this bundle manifest is as follows: -

    -
      -
    • Bundle-SymbolicName - The unique identifier of the bundle.
    • -
    • Bundle-Description - A human-readable description of the bundle's - functionality.
    • -
    • Bundle-Version - Designates a version number to the - bundle.
    • -
    • Export-Package - Expresses which Java packages contained - in a bundle will be made available to the outside world.
    • -
    • Import-Package - Indicates which Java packages will be - required from the outside world to fulfill the dependencies needed in a - bundle.
    • -
    -

    - Note that OSGi has a strict definition of version numbers that need to - be followed for bundles to work correctly. See the - OSGi - javadoc for details. As a general advice, never use more than three numbers - in the version (major, minor, micro). -

    - - - -

    Building an OSGi bundle

    -

    - As long as the project was created by following steps in the - Developer Guide, - the code is already being packaged into an OSGi bundle by the - Maven bundle plugin. - However, if migrating an existing Maven project, change the packaging statement to: -

    -
    {% highlight xml %}
    -container-plugin
    -{% endhighlight %}
    -

    - and add the plugin to the build instructions: -

    -
    {% highlight xml %}
    -
    -    com.yahoo.vespa
    -    bundle-plugin
    -    
    -    {{site.variables.vespa_version}}
    -    true
    -    
    -        true
    -    
    -
    -{% endhighlight %}
    -

    - Because OSGi introduces a different runtime environment from what Maven provides when running unit tests, - one will not observe any loading and linking errors until trying to deploy the application onto a running Container. - Errors triggered at this stage will be the likes of - ClassNotFoundException and NoClassDefFoundError. - To debug these types of errors, inspect the stack traces in the - error log, and refer to - troubleshooting. -

    - vespa-logfmt - with its --nldequote option is useful when reading logs. -

    -

    - The test suite needs to cover deployment of the application bundle to - ensure that its dynamic loading and linking issues are covered. -

    - - - -

    Depending on non-OSGi ready libraries

    -

    - Unfortunately, many popular Java libraries have yet to be bundled with the - appropriate manifest that makes them OSGi-compatible. - The simplest solution to this is to set the scope of the problematic - dependency to compile in your pom.xml file. This will cause the bundle - plugin to package the whole library into your bundle's JAR file. Until the - offending library becomes available as an OSGi bundle, it means that your - bundle will be bigger (in number of bytes), and that classes of that - library can not be shared across application bundles. -

    -

    - The practical implication of this feature is that the bundle plugin - copies the compile-scoped dependency, and its transitive dependencies, - into the final JAR file, and adds - a Bundle-ClassPath instruction to its manifest that - references those dependencies. -

    -

    - Although this approach works for most non-OSGi libraries, it only works for - libraries where the jar file is self-contained. If, on the other hand, the - library depends on other installed files, it must be treated as if it was a - JNI library. -

    - - - -

    Depending on JNI Libraries

    -

    - This section details alternatives for using native code in the container. -

    - - -

    OSGi bundles containing native code

    -

    - OSGi jars may contain .so files, which can be loaded in the standard way from Java - code in the bundle. Note that since only one instance of an .so can be loaded at any time, - it is not possible to hot swap a jar containing .so files - when such jars are changed - the new configuration will not - take effect until the container is restarted. - Therefore, it is often a good idea to package a .so file and its Java API - into a separate bundle from the rest of your code to avoid having to restart the container on all code changes. -

    - - -

    Add JNI code to the global classpath

    -

    - When the JNI dependency cannot be packaged in a bundle, - and you run on an environment where you can install files locally - on the container nodes, - you can add the dependency to the container's classpath and explicitly - export the packages to make them visible to OSGi bundles. -

    -

    - Add the following configuration in the top level services - element in services.xml: -

    -
    {% highlight xml %}
    -
    -    
    -        
    -            /lib/jars/foo.jar:/path/bar.jar
    -            com.foo,com.bar
    -        
    -    
    -    ...
    -
    -{% endhighlight %}
    -

    - Adding the config at the top level ensures that it's applied to all jdisc clusters. -

    -

    - The packages are now available and visible, - but they must still be imported by the application bundle that uses the library. - Here is how to configure the bundle plugin to enforce an import of the packages to the bundle: -

    -
    -<plugin>
    -  <groupId>com.yahoo.vespa</groupId>
    -  <artifactId>bundle-plugin</artifactId>
    -  <extensions>true</extensions>
    -  <configuration>
    -      <importPackage>com.foo,com.bar</importPackage>
    -  </configuration>
    -</plugin>
    -
    -

    - When adding a library to the classpath it becomes globally visible, and - exempt from the package visibility management of OSGi. - If another bundle contains the same library, there will be class loading issues. -

    - - - - -

    Maven bundle plugin

    -

    - The bundle-plugin is used to build and package components for the - Vespa Container with Maven. - Refer to the - multiple-bundles sample app for a practical example. -

    - The minimal Maven pom.xml configuration is: -

    -
    -<?xml version="1.0" encoding="UTF-8"?>
    -<project xmlns="http://maven.apache.org/POM/4.0.0"
    -         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    -         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    -    <modelVersion>4.0.0</modelVersion>
    -    <groupId>com.yahoo.example</groupId>
    -    <artifactId>basic-application</artifactId>
    -    <packaging>container-plugin</packaging> <!-- Use Vespa packaging -->
    -    <version>1.0.1</version>
    -
    -    <properties>
    -        <!-- Find latest version at search.maven.org/search?q=g:com.yahoo.vespa -->
    -        <vespa.version>{{site.variables.vespa_version}}</vespa.version>
    -    </properties>
    -
    -    <build>
    -        <plugins>
    -            <plugin> <!-- Build the bundles -->
    -                <groupId>com.yahoo.vespa</groupId>
    -                <artifactId>bundle-plugin</artifactId>
    -                <version>${vespa.version}</version>
    -                <extensions>true</extensions>
    -                <configuration>
    -                    <failOnWarnings>true</failOnWarnings>
    -                </configuration>
    -            </plugin>
    -            <plugin> <!-- Zip the application package -->
    -                <groupId>com.yahoo.vespa</groupId>
    -                <artifactId>vespa-application-maven-plugin</artifactId>
    -                <version>${vespa.version}</version>
    -                <executions>
    -                    <execution>
    -                        <goals>
    -                            <goal>packageApplication</goal>
    -                        </goals>
    -                    </execution>
    -                </executions>
    -            </plugin>
    -        </plugins>
    -    </build>
    -
    -    <dependencies>
    -        <dependency> <!-- Vespa dependencies -->
    -            <groupId>com.yahoo.vespa</groupId>
    -            <artifactId>container</artifactId>
    -            <version>${vespa.version}</version>
    -            <scope>provided</scope>
    -        </dependency>
    -    </dependencies>
    -</project>
    -
    -

    - To create a deployable application package, run: -

    -
    -$ mvn install package
    -
    -

    - The bundle plugin automates generation of configuration classes by invoking the maven step - generate-resources - read more in - configuring-components.html -

    - -

    Including Your Own Maven Submodules

    - -

    - You can include your own Maven submodules as dependencies within your Vespa component bundle. - This allows you to share code and functionality between different components within your project. -

    -

    - To include a submodule as a dependency, add it to your bundle's pom.xml in scope compile: -

    -
    {% highlight xml %}
    -
    -  your.project.groupId
    -  your-submodule-artifactId
    -  compile
    -
    -{% endhighlight %}
    -

    - Replace `your.project.groupId` with the actual groupId of your project and `your-submodule-artifactId` with the artifactId of your submodule. -

    - -

    Including third-party libraries

    -

    - Include external dependencies into the bundle by specifying them as dependencies: -

    -
    {% highlight xml %}
    -
    -    org.apache.httpcomponents.client5
    -    httpclient5
    -    5.0.3
    -    compile
    -
    -{% endhighlight %}
    -

    - All packages in the library will then be available for use. -

    -

    - If the external dependency is packaged as an OSGi bundle, it can be deployed - as-is by setting the scope to provided: -

    -
    -<dependency>
    -    <groupId>org.apache.httpcomponents.client5</groupId>
    -    <artifactId>httpclient5-osgi</artifactId>
    -    <version>5.0-beta2</version>
    -    <scope>provided</scope>
    -</dependency>
    -
    -

    - Then, add the jar to the components folder of your application - package, along with your own bundles. In this case, - only packages exported by the author of the library will be - available for use by your bundle (see the section below). -

    - - -

    - Exporting, Importing and Including Packages from Bundles

    -

    - OSGi features information hiding — by default all the classes - used inside a bundle are invisible from the outside. Also, the bundle - will by default only see (all) the packages in the Java and Container - + Vespa APIs. If any other package is needed by the bundle, then it - must happen in one of three ways: -

    -
      -
    • Some additional packages are exported by the container and may - be imported explicitly by a bundle
    • -
    • In addition, any deployed bundle may export packages on its own, - which may then be imported by another bundle
    • -
    • Finally, the bundle may include its own JAR libraries
    • -
    -

    - One can export packages from a bundle by annotating the package. - E.g. to export com.mydomain.mypackage, create package-info.java - in the package directory with: -

    -
    -@ExportPackage(version = @Version(major=1, minor=0, micro=0))
    -package com.mydomain.mypackage;
    -
    -import com.yahoo.osgi.annotation.ExportPackage;
    -import com.yahoo.osgi.annotation.Version;
    -
    -

    - The Maven plugin will place such information in the manifest of the - plugin JAR built to be picked up by the Container. -

    - Note that this may also be used with bundles that do not contain any - searchers but libraries used by other searchers - a bundle may - just exist to export some libraries and never have any searchers - instantiated. -

    - Bundles may import packages (exported by some other bundle or - by the container). The maven plugin will automatically import any - package used from bundles it compiles against(i.e. maven dependencies - with scope provided). -

    - As mentioned above, each exported package has a version associated - with it. Similarly, an import of a package has a version range - associated with it. The version range determines which exported - packages can be used. The range used by the maven plugin is the - current version(i.e. the version of the package available at compile - time) up to the next major version (not including). -

    - To learn more about OSGi manifests and bundle packaging (e.g. how to - include Java libraries and native code), please refer to the OSGi spec - at the OSGi home page. -

    - More details in troubleshooting. -

    - - -

    Bundle Plugin Warnings

    -

    - The bundle plugin will emit warnings for the following common issues that may cause problems at runtime: -

    -
    -
    [WARNING] This project uses packages that are not part of Vespa's public api
    -
    Only Vespa types that are in Java packages annotated with - @PublicApi - should be used in application code, as other types are not guaranteed to be stable across Vespa releases.
    -
    [WARNING] This project does not have 'container' as provided dependency
    -
    All application bundles must have the com.yahoo.vespa:container artifact as a provided scoped dependency, - to ensure that the generated 'Import-Package' OSGi header contains the Java packages provided by the Vespa runtime.
    -
    [WARNING] Artifacts provided from Vespa runtime are included in compile scope
    -
    This makes the bundle unnecessarily large and may cause problems at runtime, as these artifacts will be embedded in the bundle. - Run mvn dependency:tree to identify the source of transitive dependencies, and add the necessary exclusions in - pom.xml. If an artifact must be included, e.g. because a specific version is needed, an exception can be added - with the configuration parameter allowEmbeddedArtifacts.
    -
    [WARNING] This project defines packages that are also defined in provided scoped dependencies
    -
    Overlapping Java packages between bundles will usually cause problems at runtime, because the OSGi framework will only be able - to resolve classes from one of the bundles.
    -
    - -

    Configuring the Bundle-Plugin

    -

    - The bundle plugin can be configured to tailor the resulting bundle to specific needs. -

    -
    {% highlight xml %}
    -
    -    
    -        
    -            com.yahoo.vespa
    -            bundle-plugin
    -            ${vespa.version}
    -            true
    -            
    -                true/false
    -                
    -                true/false
    -                
    -                
    -                
    -                
    -                
    -                
    -                
    -                
    -            
    -        
    -{% endhighlight %}
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    ElementDescription
    failOnWarningsIf true, the maven build will fail upon warnings for e.g. using Vespa types - that are not annotated with @PublicApi. - This should always be set to true to ensure that your project - will compile successfully on future Vespa releases. - Default is false
    allowEmbeddedArtifactsA comma-separated list of maven artifacts to allow embedding in the bundle, on the - format groupId:artifactId
    attachBundleArtifactWhether to attach the bundle jar artifact to the build. - Use this if you want to install and deploy the bundle jar along with the default jar. - Default is false
    bundleClassifierNameIf attachBundleArtifact is true, - this will be used as classifier for the bundle jar artifact. - Default is bundle
    discApplicationClassThe fully qualified class - name of the Application to be started by JDisc
    discPreInstallBundleThe name of the bundles - that jDISC must pre-install
    bundleVersionThe version of this bundle. - Defaults to the Maven project version
    bundleSymbolicNameThe symbolic name of this bundle. - Defaults to the Maven artifact ID
    bundleActivatorThe fully qualified class name of the bundle activator
    configGenVersion The version of com.yahoo.vespa.configlib.config-class-plugin - that will be used to generate config classes
    configModelsList of config models
    - - -

    Bundle Plugin Troubleshooting

    - -

    - A package p is imported if all of this hold: -

    -
      -
    1. Using a class in p directly (i.e. not with reflection) in the - bundle
    2. -
    3. There's no classes in the bundle that is in p
    4. -
    5. There's a bundle that exports p, and compiling against this bundle
    6. -
    -

    To debug, run

    -
    -$ mvn -X package
    -
    -

    - and look at Defined packages (=packages in the bundle), Exported packages of - dependencies, Referenced packages(= packages used). - A package is imported if it is in Exported packages and Referenced packages but not in Defined packages. -

    - - - -

    Troubleshooting

    -

    This section describes how to troubleshoot the most common errors when working with bundles:

    - - - -

    Bundle reload

    -

    Bundles that are uninstalled between re-configs are logged like this:

    -
    -INFO    : qrserver         Container.com.yahoo.container.core.config.ApplicationBundleLoader
    -  Bundles to schedule for uninstall: [com.yahoo.vespatest.ExtraHitSearcher [67]]
    -
    -

    And in case there are none, it shows the empty set:

    -
    -INFO    : qrserver         Container.com.yahoo.container.core.config.ApplicationBundleLoader
    -  Bundles to schedule for uninstall: []
    -
    - - -

    Could not create component

    -

    - The Container fails to start if it cannot load bundles. - Example, using wrong bundle name in the - multiple-bundles sample app: -

    -
    - <services version="1.0">
    -     <container id="fibonacci" version="1.0">
    --        <component id="com.mydomain.lib.FibonacciProducer" bundle="multiple-bundles-lib" />
    -+        <component id="com.mydomain.lib.FibonacciProducer" bundle="multiple-bundles-typo" />
    -
    -

    Looking at what is actually deployed in multiple-bundles:

    -
    -$ ls -1 target/*.jar
    -  target/multiple-bundles-1.0.0-deploy.jar
    -  target/multiple-bundles-1.0.0-without-dependencies.jar
    -  target/multiple-bundles-lib-1.0.1-deploy.jar
    -
    -

    Error in log:

    -
    -[2020-01-23 14:28:01.367] WARNING : qrserver         Container.com.yahoo.container.di.Container
    -	Failed to set up new component graph. Retaining previous component generation.
    -	exception=
    -  java.lang.IllegalArgumentException: Could not create a component with id 'com.mydomain.lib.FibonacciProducer'.
    -        Tried to load class directly, since no bundle was found for spec: multiple-bundles-typo.
    -        If a bundle with the same name is installed, there is a either a version mismatch or the installed bundle's version contains a qualifier string.
    -	at com.yahoo.osgi.OsgiImpl.resolveFromClassPath(OsgiImpl.java:74)
    -	at com.yahoo.osgi.OsgiImpl.resolveClass(OsgiImpl.java:65)
    -	at com.yahoo.container.di.Container.addNodes(Container.java:228)
    -	at com.yahoo.container.di.Container.createComponentsGraph(Container.java:217)
    -	at com.yahoo.container.di.Container.getConfigAndCreateGraph(Container.java:160)
    -	at com.yahoo.container.di.Container.getNewComponentGraph(Container.java:84)
    -	at com.yahoo.container.core.config.HandlersConfigurerDi.getNewComponentGraph(HandlersConfigurerDi.java:145)
    -	at com.yahoo.container.jdisc.ConfiguredApplication.lambda$startReconfigurerThread$1(ConfiguredApplication.java:275)
    -	at java.base/java.lang.Thread.run(Thread.java:834)
    -
    -[2020-01-23 14:28:01.367] ERROR   : qrserver         Container.com.yahoo.container.jdisc.ConfiguredApplication
    -	Reconfiguration failed, your application package must be fixed, unless this is a JNI reload issue: Could not create a component with id 'com.mydomain.lib.FibonacciProducer'. Tried to load class directly, since no bundle was found for spec: multiple-bundles-typo. If a bundle with the same name is installed, there is a either a version mismatch or the installed bundle's version contains a qualifier string.
    -	exception=
    -	java.lang.IllegalArgumentException: Could not create a component with id 'com.mydomain.lib.FibonacciProducer'. Tried to load class directly, since no bundle was found for spec: multiple-bundles-typo. If a bundle with the same name is installed, there is a either a version mismatch or the installed bundle's version contains a qualifier string.
    -	at com.yahoo.osgi.OsgiImpl.resolveFromClassPath(OsgiImpl.java:74)
    -	at com.yahoo.osgi.OsgiImpl.resolveClass(OsgiImpl.java:65)
    -	at com.yahoo.container.di.Container.addNodes(Container.java:228)
    -	at com.yahoo.container.di.Container.createComponentsGraph(Container.java:217)
    -	at com.yahoo.container.di.Container.getConfigAndCreateGraph(Container.java:160)
    -	at com.yahoo.container.di.Container.getNewComponentGraph(Container.java:84)
    -	at com.yahoo.container.core.config.HandlersConfigurerDi.getNewComponentGraph(HandlersConfigurerDi.java:145)
    -	at com.yahoo.container.jdisc.ConfiguredApplication.lambda$startReconfigurerThread$1(ConfiguredApplication.java:275)
    -	at java.base/java.lang.Thread.run(Thread.java:834)
    -
    -

    Make sure that the jar files (i.e. bundles) are actually deployed with correct names per services.xml.

    - - -

    Could not load class

    -

    - If a component is added to services.xml, and its class cannot be found in the declared bundle, - the container will fail to start. For example: -

    -
    {% highlight xml %}
    -
    -{% endhighlight %}
    -

    - The log will contain an error like this: -

    -
    -java.lang.IllegalArgumentException: Could not load class 'com.example.MissingClass' from bundle my-bundle
    -
    -

    - If you see this error, please make sure that the class actually exists in the given bundle. Also, verify that - the id (or class) tag refers to the component class, and not e.g. a java package - or the bundle name. -

    - - -

    Class not found

    -

    - All classes that are referred to in a user bundle must either be embedded in the bundle, - or imported from another bundle by an Import-Package statement in the bundle manifest. - When this rule has been breached, we get one of the most commonly seen exceptions when working with OSGi bundles: -

    -
    -...
    -exception=
    -    java.lang.NoClassDefFoundError: com/acme/utils/Helper
    -    ...
    -    java.lang.ClassNotFoundException: com.acme.utils.Helper not found by my_bundle [29]
    -
    -

    - For the bundle-plugin to automatically add an - Import-Package statement to the bundle's manifest, - that package must be exported from another bundle - that is declared as a 'provided' scope dependency in pom.xml. - If the dependency that contains the missing class is under your own control, - make sure it's packaged as an OSGi bundle, - and export the package - from that bundle. - If not, the simplest way to resolve the issue is to embed the dependency in your own bundle, - by setting its scope to 'compile' instead of 'provided'. -

    - If the strategy above does not resolve the case, - it's most likely because the class in question is loaded by reflection, - e.g. Class.forName("com.acme.utils.Helper"). - This is quite common when working with libraries for pluggable frameworks, - for which there is a separate troubleshooting doc. - -

    - - -

    Slow Container start

    -

    In the vespa log, a container startup looks like:

    -
    -[2021-01-07 10:13:35.325] INFO    : container        Container.com.yahoo.container.core.config.ApplicationBundleLoader
    -  Installed bundles: {[0]org.apache.felix.framework:6.0.3, [1]container-disc:7.335.22 ...
    -...
    -[2021-01-07 10:26:57.291] INFO    : container        Container.com.yahoo.container.jdisc.ConfiguredApplication
    -  Switching to the latest deployed set of configurations and components. Application config generation: 1
    -
    -

    - The container is ready at the last log line - note the long startup time. - To get more details on what the container is doing at startup, inspect the ComponentGraph debug log. - Find the container service name (here: "container"), set debug logging and restart the container: -

    -
    -$ vespa-sentinel-cmd list
    -vespa-sentinel-cmd 'sentinel.ls' OK.
    -container state=RUNNING mode=AUTO pid=246585 exitstatus=0 id="default/container.0"
    -
    -$ vespa-logctl container:com.yahoo.container.di.componentgraph.core debug=on
    -
    -$ vespa-stop-services && vespa-start-services
    -
    -# Find DEBUG log messages for component creation, like:
    -
    -[2021-01-07 10:13:37.006] DEBUG   : container        Container.com.yahoo.container.di.componentgraph.core.ComponentGraph
    -  Trying the fallback injector to create component of class com.yahoo.container.jdisc.messagebus.SessionCache to inject
    -  into component 'chain.mychain in MbusServer' of type 'com.yahoo.container.jdisc.messagebus.MbusServerProvider'.
    -[2021-01-07 10:14:14.082] DEBUG   : container        Container.com.yahoo.container.di.componentgraph.core.ComponentNode
    -  Constructing 'com.yahoo.search.query.profile.compiled.CompiledQueryProfileRegistry'
    -[2021-01-07 10:26:54.669] DEBUG   : container        Container.com.yahoo.container.di.componentgraph.core.ComponentNode
    -  Finished constructing 'com.yahoo.search.query.profile.compiled.CompiledQueryProfileRegistry'
    -
    -

    In this particular example, query profile compilation takes a long time.

    - - -

    Unresolved constraint

    -

    - If the bundle has an Import-Package for a package that is not available at runtime, - the OSGi framework will report an unresolved constraint error. - The symptom as seen in the log is: -

    -
    -org.osgi.framework.BundleException: Unresolved constraint in bundle my_bundle [29]:
    -Unable to resolve 29.0:
    -missing requirement [29.0] osgi.wiring.package; (osgi.wiring.package=com.acme.utils)
    -     at org.apache.felix.framework.Felix.resolveBundleRevision(Felix.java:3974)
    -
    -

    - This means that the missing class resides in a 'provided' dependency referred to from - the bundle's pom.xml, either directly or transitively. - In order to make the dependency available at runtime, there are two options: -

    -
      -
    • The easiest is to set the dependency as 'compile' scope (instead of 'provided') to embed it in your own bundle. - This works fine in most cases, unless two of the dependencies need two different versions of the same library. -
    • -
    • Add the missing jar file to the components/ folder of the application package, - along with your own bundles. - The maven-dependency-plugin has a goal called 'copy-dependencies' to help with this. -
    • -
    -

    If the missing jar is a transitive dependency, maven can help visualize the dependency graph of the project:

    -
    -$ mvn dependency:tree
    -
    - - -

    Multiple implementations of the same class

    -

    - When two bundles interact via their public APIs, it is crucial that both bundles resolve - each and every participating class to the same Class object. - If not, we will get error messages like: -

    -
    -java.lang.LinkageError: loader constraint violation: when resolving field
    -"DATETIME" the class loader (instance of
    -org/apache/felix/framework/BundleWiringImpl$BundleClassLoaderJava5) of the referring
    -class, javax/xml/datatype/DatatypeConstants, and the class loader (instance of
    -<bootloader>) for the field's resolved type, pe/DatatypeConstants, have different Class
    -objects for that type
    -
    -

    or:

    -
    -java.lang.LinkageError: loader constraint violation: loader (instance of <bootloader>)
    -previously initiated loading for a different type with name "javax/xml/namespace/QName"
    -
    -

    or (less frequently):

    -
    -java.lang.ClassCastException: com.acme.utils.Helper cannot be cast to  com.acme.utils.Helper
    -
    -

    - All these error messages indicate that multiple implementations of one or more classes are used at runtime - - possible root causes: -

    -
      -
    • Two interacting user bundles embed the same Java package.
    • -
    • A user bundle embeds a Java package that is exported from one of the JDisc bundles.
    • -
    -

    Usually, the "duplicate" package is pulled in by the user bundle transitively from a library dependency.

    - - -

    Multiple implementations example

    -

    - Let's take a look at an example resolving the duplicate javax.xml.namespace.QName class - from the error message above. -

    - All 'javax.xml' packages in the JDK are exported by the JDisc core bundle. - This means that they should be imported by user bundles, instead of embedded inside them. - Hence, ensure that there are no classes from packages prefixed by 'javax.xml' in the bundle. - Find out which library that pulls in the package: -

    -
      -
    1. Extract the full component jar, including any embedded jars. - One tool that does the job is rjar.

      -
    2. -
    3. Search the folder where the jar was extracted for 'javax.xml' classes:

      -
      -$ find . | grep "javax/xml/.*\.class"
      -
      -...
      -./my_bundle-deploy.jar/dependencies/stax-api-1.0.1.jar/javax/xml/namespace/QName.class
      -...
      -
      -
    4. -
    5. Find out which libraries that pulled in the offending classes - here it was stax-api-1.0.1. - Usually, these libraries are not pulled in by the pom as direct dependencies, - but rather transitively via another library being used. - Use maven's dependency plugin from the application directory to find the direct dependency: -
      -$ mvn dependency:tree -Dverbose
      -
      -[INFO] +- com.acme.utils:jersey-utils:1.0.0:compile
      -[INFO] |  +- com.sun.jersey:jersey-json:jar:1.13:compile
      -[INFO] |  |  +- org.codehaus.jettison:jettison:jar:1.1:compile
      -[INFO] |  |    \- stax:stax-api:jar:1.0.1:compile
      -
      -

      Observe that stax:stax-api:1.0.1 is pulled in transitively from the direct - dependency com.acme.utils:jersey-utils.

      -
    6. -
    7. To exclude stax:stax-api, add the appropriate - exclusion from the direct dependency com.acme.utils:jersey-utils in pom.xml:

      -
      -<dependency>
      -    <groupId>com.acme.utils</groupId>
      -    <artifactId>jersey-utils</artifactId>
      -    <version>1.0.0</version>
      -    <exclusions>
      -        <exclusion>
      -            <groupId>stax</groupId>
      -            <artifactId>stax-api</artifactId>
      -        </exclusion>
      -    </exclusions>
      -</dependency>
      -
      -
    8. -
    - - -

    Multiple implementations example slf4j-api

    -

    - This is similar to the previous example, but logging libraries are maybe the most common problem teams encounter. - Here we will see the symptom, use dependency:tree and add an exclusion. The symptom: -

    -
    -java.lang.RuntimeException: An exception occurred while
    -constructing 'com.acme.utils.Helper in acme-utils'
    -Caused by: java.lang.LinkageError: loader constraint violation: when resolving method
    -"org.slf4j.impl.StaticLoggerBinder.getLoggerFactory()Lorg/slf4j/ILoggerFactory;"
    -the class loader (instance of org/apache/felix/framework/BundleWiringImpl$BundleClassLoaderJava5) of the
    -current class, org/slf4j/LoggerFactory, and the class loader (instance of
    -sun/misc/Launcher$AppClassLoader) for the method's defining class,
    -org/slf4j/impl/StaticLoggerBinder, have different Class objects for the type
    -org/slf4j/ILoggerFactory used in the signature
    -at
    -org.slf4j.LoggerFactory.getILoggerFactory(LoggerFactory.java:299)
    -at
    -org.slf4j.LoggerFactory.getLogger(LoggerFactory.java:269)
    -
    -

    Running mvn dependency:tree in the previous example gives:

    -
    -[INFO] +- com.yahoo.vespa:container-dev:jar:5.28.29:provided
    -[INFO] |  +- com.yahoo.vespa:jdisc_core:jar:5.28.29:provided
    -[INFO] |  |  +- (org.slf4j:slf4j-api:jar:1.7.5:compile - scope updated from provided; omitted for duplicate)
    -...
    -[INFO] +- com.acme.utils:smartlib:jar:1.0.0:compile
    -[INFO] |  +- org.slf4j:slf4j-api:jar:1.6.6:compile
    -
    -

    - See that slf4j-api is no longer provided from container-dev, which it should. - To fix this, add an exclusion on the offender: -

    -
    {% highlight xml %}
    -
    -    com.acme.utils
    -    smartlib
    -    1.0.0
    -    compile
    -    
    -        
    -            org.slf4j
    -            slf4j-api
    -        
    -{% endhighlight %}
    -

    But it still does not work! And we can see why:

    -
    -$ jar -tf mailsearch-docprocs-deploy.jar  | grep slf
    -
    -  dependencies/slf4j-api-1.7.5.jar
    -
    -

    Something still pulls in slf4j... Other candidates:

    -
    -[INFO] \- com.yahoo.vespa:application:jar:5.28.29:test
    -...
    -[INFO]    +- com.yahoo.vespa:zkfacade:jar:5.28.29:test
    -[INFO]    |  +- org.apache.curator:curator-recipes:jar:2.4.1:test
    -[INFO]    |  |  +- org.apache.curator:curator-framework:jar:2.4.1:test
    -[INFO]    |  |  |  +- org.apache.curator:curator-client:jar:2.4.1:test
    -[INFO]    |  |  |  |  +- (org.slf4j:slf4j-api:jar:1.6.4:test - omitted for conflict with 1.7.5)
    -...
    -[INFO]    |  +- (org.slf4j:slf4j-jdk14:jar:1.7.5:test - omitted for duplicate)
    -
    -

    - Added the right excludes for application and used mvn dependency:tree and verified that all references were gone, - except the ones for container-dev. Still found: -

    -
    -$ jar -tf mailsearch-docprocs-deploy.jar  | grep slf
    -dependencies/slf4j-api-1.7.5.jar
    -
    -

    One can make it work by managing this dependency explicitly - add this at POM top-level:

    -
    {% highlight xml %}
    -
    -    
    -        
    -            org.slf4j
    -            slf4j-api
    -            1.7.5
    -            provided
    -        
    -    
    -
    -{% endhighlight %}
    diff --git a/en/applications/bundles.mdx b/en/applications/bundles.mdx new file mode 100644 index 0000000000..5b84657f64 --- /dev/null +++ b/en/applications/bundles.mdx @@ -0,0 +1,730 @@ +--- +title: "Bundles" +description: "The Container uses [OSGi](https://osgi.org) to provide a modular platform for developing applications that can be composed of many reusable components. The user can deploy, upgrade and remove these components at runtime." +--- + +## OSGi + +OSGi is a framework for modular development of Java applications, where a set of resources called *bundles* can be installed. OSGi allows the developer to control which resources (Java packages) in a bundle that should be available to other bundles. Hence, you can explicitly declare a bundle's public API, and also ensure that internal implementation details remain hidden. + +Unless you're already familiar with OSGi, we recommend reading Richard S. Hall's presentation [Learning to ignore OSGi](https://cwiki.apache.org/confluence/download/attachments/7956/Learning_to_ignore_OSGi.pdf), which explains the most important aspects that you must relate to as a bundle developer. There are other good OSGi tutorials available: + +- [OSGi for Dummies](https://thiloshon.wordpress.com/2020/03/04/osgi-for-dummies/) +- [OSGi Modularity and Services - Tutorial](https://www.vogella.com/tutorials/OSGi/article.html) (You can ignore the part about OSGi services.) + +JDisc uses OSGi's *module* and *lifecycle* layers, and does not provide any functionality from the *service* layer. + +## OSGi bundles + +An OSGi bundle is a regular JAR file with a MANIFEST.MF file that describes its content, what the bundle requires (imports) from other bundles, and what it provides (exports) to other bundles. Below is an example of a typical bundle manifest with the most important headers: + +```java +Bundle-SymbolicName: com.yahoo.helloworld +Bundle-Description: A Hello World bundle +Bundle-Version: 1.0.0 +Export-Package: com.yahoo.helloworld;version="1.0.0" +Import-Package: org.osgi.framework;version="1.3.0" +``` + +The meaning of the headers in this bundle manifest is as follows: + +- `Bundle-SymbolicName` - The unique identifier of the bundle. +- `Bundle-Description` - A human-readable description of the bundle's functionality. +- `Bundle-Version` - Designates a version number to the bundle. +- `Export-Package` - Expresses which Java packages contained in a bundle will be made available to the outside world. +- `Import-Package` - Indicates which Java packages will be required from the outside world to fulfill the dependencies needed in a bundle. + +Note that OSGi has a strict definition of version numbers that need to be followed for bundles to work correctly. See the [OSGi javadoc](https://docs.osgi.org/javadoc/r4v42/org/osgi/framework/Version.html#Version(java.lang.String)) for details. As a general advice, never use more than three numbers in the version (major, minor, micro). + +## Building an OSGi bundle + +As long as the project was created by following steps in the [Developer Guide](/en/applications/developer-guide), the code is already being packaged into an OSGi bundle by the [Maven bundle plugin](#maven-bundle-plugin). However, if migrating an existing Maven project, change the packaging statement to: + +```xml +container-plugin +``` + +and add the plugin to the build instructions: + +```xml + + com.yahoo.vespa + bundle-plugin +{/* Find latest version at search.maven.org/search?q=g:com.yahoo.vespa%20a:bundle-plugin */} + {{site.variables.vespa_version}} + true + + true + + +``` + +Because OSGi introduces a different runtime environment from what Maven provides when running unit tests, one will not observe any loading and linking errors until trying to deploy the application onto a running Container. Errors triggered at this stage will be the likes of `ClassNotFoundException` and `NoClassDefFoundError`. To debug these types of errors, inspect the stack traces in the [error log](/en/reference/operations/log-files), and refer to [troubleshooting](#troubleshooting). + +[vespa-logfmt](/en/reference/operations/self-managed/tools#vespa-logfmt) with its *--nldequote* option is useful when reading logs. + +The test suite needs to cover deployment of the application bundle to ensure that its dynamic loading and linking issues are covered. + + + +## Depending on non-OSGi ready libraries + +Unfortunately, many popular Java libraries have yet to be bundled with the appropriate manifest that makes them OSGi-compatible. The simplest solution to this is to set the scope of the problematic dependency to **compile** in your pom.xml file. This will cause the bundle plugin to package the whole library into your bundle's JAR file. Until the offending library becomes available as an OSGi bundle, it means that your bundle will be bigger (in number of bytes), and that classes of that library can not be shared across application bundles. + +The practical implication of this feature is that the bundle plugin copies the compile-scoped dependency, and its transitive dependencies, into the final JAR file, and adds a `Bundle-ClassPath` instruction to its manifest that references those dependencies. + +Although this approach works for most non-OSGi libraries, it only works for libraries where the jar file is *self-contained*. If, on the other hand, the library depends on other installed files, it must be treated as if it was a [JNI library](#depending-on-JNI-libraries). + +## Depending on JNI Libraries + +This section details alternatives for using native code in the container. + +### OSGi bundles containing native code + +OSGi jars may contain .so files, which can be loaded in the standard way from Java code in the bundle. Note that since only one instance of an .so can be loaded at any time, it is not possible to hot swap a jar containing .so files - when such jars are changed the [new configuration will not take effect until the container is restarted](/en/applications/components#JNI-requires-restart). Therefore, it is often a good idea to package a .so file and its Java API into a separate bundle from the rest of your code to avoid having to restart the container on all code changes. + +### Add JNI code to the global classpath + +When the JNI dependency cannot be packaged in a bundle, and you run on an environment where you can install files locally on the container nodes, you can add the dependency to the container's classpath and explicitly export the packages to make them visible to OSGi bundles. + +Add the following configuration in the top level *services* element in [services.xml](/en/reference/applications/services/container): + +```xml + + + + /lib/jars/foo.jar:/path/bar.jar + com.foo,com.bar + + + ... + +``` + +Adding the config at the top level ensures that it's applied to all jdisc clusters. + +The packages are now available and visible, but they must still be imported by the application bundle that uses the library. Here is how to configure the bundle plugin to enforce an import of the packages to the bundle: + +```xml highlight={5-7} + + com.yahoo.vespa + bundle-plugin + true + + com.foo,com.bar + + +``` + +When adding a library to the classpath it becomes globally visible, and exempt from the package visibility management of OSGi. If another bundle contains the same library, there will be class loading issues. + +## Maven bundle plugin + +The *bundle-plugin* is used to build and package components for the [Vespa Container](/en/applications/components) with Maven. Refer to the [multiple-bundles sample app](https://github.com/vespa-engine/sample-apps/tree/master/examples/multiple-bundles) for a practical example. + +The minimal Maven *pom.xml* configuration is: + +```xml highlight={8, 18, 27, 43} + + + 4.0.0 + com.yahoo.example + basic-application + container-plugin {/* Use Vespa packaging */} + 1.0.1 + + +{/* Find latest version at search.maven.org/search?q=g:com.yahoo.vespa */} + {{site.variables.vespa_version}} + + + + + {/* Build the bundles */} + com.yahoo.vespa + bundle-plugin + ${vespa.version} + true + + true + + + {/* Zip the application package */} + com.yahoo.vespa + vespa-application-maven-plugin + ${vespa.version} + + + + packageApplication + + + + + + + + + {/* Vespa dependencies */} + com.yahoo.vespa + container + ${vespa.version} + provided + + + +``` + +To create a deployable [application package](/en/basics/applications), run: + +```bash +$ mvn install package +``` + +The bundle plugin automates generation of configuration classes by invoking the maven step *generate-resources* - read more in [configuring-components.html](/en/applications/configuring-components) + +### Including Your Own Maven Submodules + +You can include your own Maven submodules as dependencies within your Vespa component bundle. This allows you to share code and functionality between different components within your project. + +To include a submodule as a dependency, add it to your bundle's pom.xml in scope *compile*: + +```xml + + your.project.groupId + your-submodule-artifactId + compile + +``` + +Replace `your.project.groupId` with the actual groupId of your project and `your-submodule-artifactId` with the artifactId of your submodule. + +### Including third-party libraries + +Include external dependencies into the bundle by specifying them as dependencies: + +```xml + + org.apache.httpcomponents.client5 + httpclient5 + 5.0.3 + compile + +``` + +All packages in the library will then be available for use. + +If the external dependency is packaged as an OSGi bundle, it can be deployed as-is by setting the scope to *provided*: + +```xml highlight={5} + + org.apache.httpcomponents.client5 + httpclient5-osgi + 5.0-beta2 + provided + +``` + +Then, add the jar to the *components* folder of your application package, along with your own bundles. In this case, only packages exported by the author of the library will be available for use by your bundle (see the section below). + +### Exporting, Importing and Including Packages from Bundles + +OSGi features information hiding — by default all the classes used inside a bundle are invisible from the outside. Also, the bundle will by default only see (all) the packages in the Java and Container + Vespa APIs. If any other package is needed by the bundle, then it must happen in one of three ways: + +- Some additional packages are exported by the container and may be *imported* explicitly by a bundle +- In addition, any deployed bundle may export packages on its own, which may then be imported by another bundle +- Finally, the bundle may include its own JAR libraries + +One can export packages from a bundle by annotating the package. E.g. to export *com.mydomain.mypackage*, create *package-info.java* in the package directory with: + +```java +@ExportPackage(version = @Version(major=1, minor=0, micro=0)) +package com.mydomain.mypackage; +import com.yahoo.osgi.annotation.ExportPackage; +import com.yahoo.osgi.annotation.Version; +``` + +The Maven plugin will place such information in the manifest of the plugin JAR built to be picked up by the Container. + +Note that this may also be used with bundles that do not contain any searchers but libraries used by other searchers - a bundle may just exist to export some libraries and never have any searchers instantiated. + +Bundles may *import* packages (exported by some other bundle or by the container). The maven plugin will automatically import any package used from bundles it compiles against(i.e. maven dependencies with scope provided). + +As mentioned above, each exported package has a version associated with it. Similarly, an import of a package has a version range associated with it. The version range determines which exported packages can be used. The range used by the maven plugin is the current version(i.e. the version of the package available at compile time) up to the next major version (not including). + +To learn more about OSGi manifests and bundle packaging (e.g. how to include Java libraries and native code), please refer to the OSGi spec at [the OSGi home page](https://osgi.org). + +More details in [troubleshooting](#troubleshooting). + +### Bundle Plugin Warnings + +The bundle plugin will emit warnings for the following common issues that may cause problems at runtime: + +[WARNING] This project uses packages that are not part of Vespa's public api Only Vespa types that are in Java packages annotated with @PublicApi should be used in application code, as other types are not guaranteed to be stable across Vespa releases. [WARNING] This project does not have 'container' as provided dependency All application bundles must have the com.yahoo.vespa:container artifact as a provided scoped dependency, to ensure that the generated 'Import-Package' OSGi header contains the Java packages provided by the Vespa runtime. [WARNING] Artifacts provided from Vespa runtime are included in compile scope This makes the bundle unnecessarily large and may cause problems at runtime, as these artifacts will be embedded in the bundle. Run mvn dependency:tree to identify the source of transitive dependencies, and add the necessary exclusions in pom.xml. If an artifact must be included, e.g. because a specific version is needed, an exception can be added with the configuration parameter allowEmbeddedArtifacts. [WARNING] This project defines packages that are also defined in provided scoped dependencies Overlapping Java packages between bundles will usually cause problems at runtime, because the OSGi framework will only be able to resolve classes from one of the bundles. + +### Configuring the Bundle-Plugin + +The bundle plugin can be configured to tailor the resulting bundle to specific needs. + +```xml + + + + com.yahoo.vespa + bundle-plugin + ${vespa.version} + true + + true/false + + true/false + + + + + + + + + + +``` + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    ElementDescription
    failOnWarningsIf true, the maven build will fail upon warnings for e.g. using Vespa types that are not annotated with @PublicApi. This should always be set to *true* to ensure that your project will compile successfully on future Vespa releases. Default is *false*
    allowEmbeddedArtifactsA comma-separated list of maven artifacts to allow embedding in the bundle, on the format *groupId:artifactId*
    attachBundleArtifactWhether to attach the bundle jar artifact to the build. Use this if you want to install and deploy the bundle jar along with the default jar. Default is *false*
    bundleClassifierNameIf *attachBundleArtifact* is true, this will be used as classifier for the bundle jar artifact. Default is *bundle*
    discApplicationClassThe fully qualified class name of the Application to be started by JDisc
    discPreInstallBundleThe name of the bundles that jDISC must pre-install
    bundleVersionThe version of this bundle. Defaults to the Maven project version
    bundleSymbolicNameThe symbolic name of this bundle. Defaults to the Maven artifact ID
    bundleActivatorThe fully qualified class name of the bundle activator
    configGenVersionThe version of *com.yahoo.vespa.configlib.config-class-plugin* that will be used to generate config classes
    configModelsList of config models
    + +### Bundle Plugin Troubleshooting + +A package *p* is imported if all of this hold: + + + + Using a class in *p* directly (i.e. not with reflection) in the bundle + + + + There's no classes in the bundle that is in *p* + + + + There's a bundle that exports *p*, and compiling against this bundle + + To debug, run + + ```bash + $ mvn -X package + ``` + + and look at Defined packages (=packages in the bundle), Exported packages of dependencies, Referenced packages(= packages used). A package is imported if it is in Exported packages and Referenced packages but not in Defined packages. + + + + +## Troubleshooting + +This section describes how to troubleshoot the most common errors when working with bundles: + +- [Bundle reload](#bundle-reload) +- [Could not create component](#could-not-create-component) +- [Class not found](#class-not-found) +- [Slow Container start](#slow-container-start) +- [Unresolved constraint](#unresolved-constraint) +- [Multiple implementations of the same class](#multiple-implementations-of-the-same-class) + +### Bundle reload + +Bundles that are uninstalled between re-configs are logged like this: + +```java +INFO : qrserver Container.com.yahoo.container.core.config.ApplicationBundleLoader +Bundles to schedule for uninstall: [com.yahoo.vespatest.ExtraHitSearcher [67]] +``` + +And in case there are none, it shows the empty set: + +```java +INFO : qrserver Container.com.yahoo.container.core.config.ApplicationBundleLoader +Bundles to schedule for uninstall: [] +``` + +### Could not create component + +The Container fails to start if it cannot load bundles. Example, using wrong bundle name in the [multiple-bundles](https://github.com/vespa-engine/sample-apps/tree/master/examples/multiple-bundles) sample app: + +```xml + + + - + + +``` + +Looking at what is actually deployed in *multiple-bundles*: + +```bash +$ ls -1 target/*.jar +target/multiple-bundles-1.0.0-deploy.jar +target/multiple-bundles-1.0.0-without-dependencies.jar +target/multiple-bundles-lib-1.0.1-deploy.jar +``` + +Error in log: + +```java expandable +[2020-01-23 14:28:01.367] WARNING : qrserver Container.com.yahoo.container.di.Container + Failed to set up new component graph. Retaining previous component generation. + exception= +java.lang.IllegalArgumentException: Could not create a component with id 'com.mydomain.lib.FibonacciProducer'. +Tried to load class directly, since no bundle was found for spec: multiple-bundles-typo. +If a bundle with the same name is installed, there is a either a version mismatch or the installed bundle's version contains a qualifier string. + at com.yahoo.osgi.OsgiImpl.resolveFromClassPath(OsgiImpl.java:74) + at com.yahoo.osgi.OsgiImpl.resolveClass(OsgiImpl.java:65) + at com.yahoo.container.di.Container.addNodes(Container.java:228) + at com.yahoo.container.di.Container.createComponentsGraph(Container.java:217) + at com.yahoo.container.di.Container.getConfigAndCreateGraph(Container.java:160) + at com.yahoo.container.di.Container.getNewComponentGraph(Container.java:84) + at com.yahoo.container.core.config.HandlersConfigurerDi.getNewComponentGraph(HandlersConfigurerDi.java:145) + at com.yahoo.container.jdisc.ConfiguredApplication.lambda$startReconfigurerThread$1(ConfiguredApplication.java:275) + at java.base/java.lang.Thread.run(Thread.java:834) + +[2020-01-23 14:28:01.367] ERROR : qrserver Container.com.yahoo.container.jdisc.ConfiguredApplication + Reconfiguration failed, your application package must be fixed, unless this is a JNI reload issue: Could not create a component with id 'com.mydomain.lib.FibonacciProducer'. Tried to load class directly, since no bundle was found for spec: multiple-bundles-typo. If a bundle with the same name is installed, there is a either a version mismatch or the installed bundle's version contains a qualifier string. + exception= + java.lang.IllegalArgumentException: Could not create a component with id 'com.mydomain.lib.FibonacciProducer'. Tried to load class directly, since no bundle was found for spec: multiple-bundles-typo. If a bundle with the same name is installed, there is a either a version mismatch or the installed bundle's version contains a qualifier string. + at com.yahoo.osgi.OsgiImpl.resolveFromClassPath(OsgiImpl.java:74) + at com.yahoo.osgi.OsgiImpl.resolveClass(OsgiImpl.java:65) + at com.yahoo.container.di.Container.addNodes(Container.java:228) + at com.yahoo.container.di.Container.createComponentsGraph(Container.java:217) + at com.yahoo.container.di.Container.getConfigAndCreateGraph(Container.java:160) + at com.yahoo.container.di.Container.getNewComponentGraph(Container.java:84) + at com.yahoo.container.core.config.HandlersConfigurerDi.getNewComponentGraph(HandlersConfigurerDi.java:145) + at com.yahoo.container.jdisc.ConfiguredApplication.lambda$startReconfigurerThread$1(ConfiguredApplication.java:275) + at java.base/java.lang.Thread.run(Thread.java:834) +``` + +Make sure that the jar files (i.e. bundles) are actually deployed with correct names per *services.xml*. + +### Could not load class + +If a component is added to services.xml, and its class cannot be found in the declared bundle, the container will fail to start. For example: + +```xml + +``` + +The log will contain an error like this: + +```java +java.lang.IllegalArgumentException: Could not load class 'com.example.MissingClass' from bundle my-bundle +``` + +If you see this error, please make sure that the class actually exists in the given bundle. Also, verify that the `id` (or `class`) tag refers to the component class, and not e.g. a java package or the bundle name. + +### Class not found + +All classes that are referred to in a user bundle must either be embedded in the bundle, or imported from another bundle by an `Import-Package` statement in the bundle manifest. When this rule has been breached, we get one of the most commonly seen exceptions when working with OSGi bundles: + +```java +... +exception= +java.lang.NoClassDefFoundError: com/acme/utils/Helper +... +java.lang.ClassNotFoundException: com.acme.utils.Helper not found by my_bundle [29] +``` + +For the [bundle-plugin](#maven-bundle-plugin) to automatically add an Import-Package statement to the bundle's manifest, that package must be exported from another bundle that is declared as a 'provided' scope dependency in *pom.xml*. If the dependency that contains the missing class is under your own control, make sure it's packaged as an OSGi bundle, and [export the package](#exporting-importing-and-including-packages-from-bundles) from that bundle. If not, the simplest way to resolve the issue is to embed the dependency in your own bundle, by setting its scope to 'compile' instead of 'provided'. + +If the strategy above does not resolve the case, it's most likely because the class in question is loaded by reflection, e.g. `Class.forName("com.acme.utils.Helper")`. This is quite common when working with libraries for pluggable frameworks, for which there is a separate [troubleshooting doc](/en/applications/pluggable-frameworks). + +### Slow Container start + +In the vespa log, a container startup looks like: + +```java +[2021-01-07 10:13:35.325] INFO : container Container.com.yahoo.container.core.config.ApplicationBundleLoader +Installed bundles: {[0]org.apache.felix.framework:6.0.3, [1]container-disc:7.335.22 ... +... +[2021-01-07 10:26:57.291] INFO : container Container.com.yahoo.container.jdisc.ConfiguredApplication +Switching to the latest deployed set of configurations and components. Application config generation: 1 +``` + +The container is ready at the last log line - note the long startup time. To get more details on what the container is doing at startup, inspect the ComponentGraph debug log. Find the container service name (here: "container"), set debug logging and restart the container: + +```bash +$ vespa-sentinel-cmd list +vespa-sentinel-cmd 'sentinel.ls' OK. +container state=RUNNING mode=AUTO pid=246585 exitstatus=0 id="default/container.0" + +$ vespa-logctl container:com.yahoo.container.di.componentgraph.core debug=on + +$ vespa-stop-services && vespa-start-services + +# Find DEBUG log messages for component creation, like: + +[2021-01-07 10:13:37.006] DEBUG : container Container.com.yahoo.container.di.componentgraph.core.ComponentGraph +Trying the fallback injector to create component of class com.yahoo.container.jdisc.messagebus.SessionCache to inject +into component 'chain.mychain in MbusServer' of type 'com.yahoo.container.jdisc.messagebus.MbusServerProvider'. +[2021-01-07 10:14:14.082] DEBUG : container Container.com.yahoo.container.di.componentgraph.core.ComponentNode +Constructing 'com.yahoo.search.query.profile.compiled.CompiledQueryProfileRegistry' +[2021-01-07 10:26:54.669] DEBUG : container Container.com.yahoo.container.di.componentgraph.core.ComponentNode +Finished constructing 'com.yahoo.search.query.profile.compiled.CompiledQueryProfileRegistry' +``` + +In this particular example, query profile compilation takes a long time. + +### Unresolved constraint + +If the bundle has an Import-Package for a package that is not available at runtime, the OSGi framework will report an unresolved constraint error. The symptom as seen in the log is: + +```java +org.osgi.framework.BundleException: Unresolved constraint in bundle my_bundle [29]: +Unable to resolve 29.0: +missing requirement [29.0] osgi.wiring.package; (osgi.wiring.package=com.acme.utils) +at org.apache.felix.framework.Felix.resolveBundleRevision(Felix.java:3974) +``` + +This means that the missing class resides in a 'provided' dependency referred to from the bundle's *pom.xml*, either directly or transitively. In order to make the dependency available at runtime, there are two options: + +- The easiest is to set the dependency as 'compile' scope (instead of 'provided') to embed it in your own bundle. This works fine in most cases, unless two of the dependencies need two different versions of the same library. +- Add the missing jar file to the `components/` folder of the application package, along with your own bundles. The maven-dependency-plugin has a goal called 'copy-dependencies' to help with this. + +If the missing jar is a transitive dependency, maven can help visualize the dependency graph of the project: + +```bash +$ mvn dependency:tree +``` + +### Multiple implementations of the same class + +When two bundles interact via their public APIs, it is crucial that both bundles resolve each and every participating class to the same `Class` object. If not, we will get error messages like: + +```java +java.lang.LinkageError: loader constraint violation: when resolving field +"DATETIME" the class loader (instance of +org/apache/felix/framework/BundleWiringImpl$BundleClassLoaderJava5) of the referring +class, javax/xml/datatype/DatatypeConstants, and the class loader (instance of +) for the field's resolved type, pe/DatatypeConstants, have different Class +objects for that type +``` + +or: + +```java +java.lang.LinkageError: loader constraint violation: loader (instance of ) +previously initiated loading for a different type with name "javax/xml/namespace/QName" +``` + +or (less frequently): + +```java +java.lang.ClassCastException: com.acme.utils.Helper cannot be cast to com.acme.utils.Helper +``` + +All these error messages indicate that multiple implementations of one or more classes are used at runtime - possible root causes: + +- Two interacting user bundles embed the same Java package. +- A user bundle embeds a Java package that is exported from one of the JDisc bundles. + +Usually, the "duplicate" package is pulled in by the user bundle transitively from a library dependency. + +#### Multiple implementations example + +Let's take a look at an example resolving the duplicate *javax.xml.namespace.QName* class from the error message above. + +All 'javax.xml' packages in the JDK are exported by the JDisc core bundle. This means that they should be imported by user bundles, instead of embedded inside them. Hence, ensure that there are no classes from packages prefixed by 'javax.xml' in the bundle. Find out which library that pulls in the package: + + + + Extract the full component jar, including any embedded jars. One tool that does the job is [rjar](https://github.com/pojosontheweb/rjar/). + + + + Search the folder where the jar was extracted for 'javax.xml' classes: + + ```bash highlight={4} + $ find . | grep "javax/xml/.*\.class" + + ... + ./my_bundle-deploy.jar/dependencies/stax-api-1.0.1.jar/javax/xml/namespace/QName.class + ... + ``` + + + + + Find out which libraries that pulled in the offending classes - here it was `stax-api-1.0.1`. Usually, these libraries are not pulled in by the pom as direct dependencies, but rather transitively via another library being used. Use maven's dependency plugin from the application directory to find the direct dependency: + + ```bash highlight={4} + $ mvn dependency:tree -Dverbose + [INFO] +- com.acme.utils:jersey-utils:1.0.0:compile + [INFO] | +- com.sun.jersey:jersey-json:jar:1.13:compile + [INFO] | | +- org.codehaus.jettison:jettison:jar:1.1:compile + [INFO] | | \- stax:stax-api:jar:1.0.1:compile + ``` + + Observe that `stax:stax-api:1.0.1` is pulled in transitively from the direct dependency `com.acme.utils:jersey-utils`. + + + + To exclude `stax:stax-api`, add the appropriate `exclusion` from the direct dependency `com.acme.utils:jersey-utils` in *pom.xml*: + + + ```xml highlight={5-10} + + com.acme.utils + jersey-utils + 1.0.0 + + + stax + stax-api + + + + ``` + + + + +#### Multiple implementations example slf4j-api + +This is similar to the previous example, but logging libraries are maybe the most common problem teams encounter. Here we will see the symptom, use dependency:tree and add an exclusion. The symptom: + +```java +java.lang.RuntimeException: An exception occurred while +constructing 'com.acme.utils.Helper in acme-utils' +Caused by: java.lang.LinkageError: loader constraint violation: when resolving method +"org.slf4j.impl.StaticLoggerBinder.getLoggerFactory()Lorg/slf4j/ILoggerFactory;" +the class loader (instance of org/apache/felix/framework/BundleWiringImpl$BundleClassLoaderJava5) of the +current class, org/slf4j/LoggerFactory, and the class loader (instance of +sun/misc/Launcher$AppClassLoader) for the method's defining class, +org/slf4j/impl/StaticLoggerBinder, have different Class objects for the type +org/slf4j/ILoggerFactory used in the signature +at +org.slf4j.LoggerFactory.getILoggerFactory(LoggerFactory.java:299) +at +org.slf4j.LoggerFactory.getLogger(LoggerFactory.java:269) +``` + +Running *mvn dependency:tree* in the previous example gives: + +```txt +[INFO] +- com.yahoo.vespa:container-dev:jar:5.28.29:provided +[INFO] | +- com.yahoo.vespa:jdisc_core:jar:5.28.29:provided +[INFO] | | +- (org.slf4j:slf4j-api:jar:1.7.5:compile - scope updated from provided; omitted for duplicate) +... +[INFO] +- com.acme.utils:smartlib:jar:1.0.0:compile +[INFO] | +- org.slf4j:slf4j-api:jar:1.6.6:compile +``` + +See that slf4j-api is no longer provided from container-dev, which it should. To fix this, add an exclusion on the offender: + +```xml + + com.acme.utils + smartlib + 1.0.0 + compile + + + org.slf4j + slf4j-api + +``` + +But it still does not work! And we can see why: + +```txt +$ jar -tf mailsearch-docprocs-deploy.jar | grep slf + +dependencies/slf4j-api-1.7.5.jar +``` + +Something still pulls in slf4j... Other candidates: + +```txt +[INFO] \- com.yahoo.vespa:application:jar:5.28.29:test +... +[INFO] +- com.yahoo.vespa:zkfacade:jar:5.28.29:test +[INFO] | +- org.apache.curator:curator-recipes:jar:2.4.1:test +[INFO] | | +- org.apache.curator:curator-framework:jar:2.4.1:test +[INFO] | | | +- org.apache.curator:curator-client:jar:2.4.1:test +[INFO] | | | | +- (org.slf4j:slf4j-api:jar:1.6.4:test - omitted for conflict with 1.7.5) +... +[INFO] | +- (org.slf4j:slf4j-jdk14:jar:1.7.5:test - omitted for duplicate) +``` + +Added the right excludes for application and used mvn dependency:tree and verified that all references were gone, except the ones for container-dev. Still found: + +```txt +$ jar -tf mailsearch-docprocs-deploy.jar | grep slf +dependencies/slf4j-api-1.7.5.jar +``` + +One can make it work by managing this dependency explicitly - add this at POM top-level: + +```xml + + + + org.slf4j + slf4j-api + 1.7.5 + provided + + + +``` diff --git a/en/applications/chaining.html b/en/applications/chaining.html deleted file mode 100644 index abcd2b3c23..0000000000 --- a/en/applications/chaining.html +++ /dev/null @@ -1,319 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Chained Components" -redirect_from: -- /en/chained-components.html -- /en/components/chained-components.html ---- - -

    - Processors, - searcher plug-ins and - document processors - are chained components. - They are executed serially, with each providing some service or transform, - and other optionally depending on these. - In other words, a chain is a set of components with dependencies. - Javadoc: - com.yahoo.component.chain.Chain -

    -

    - It is useful to read the federation guide before this document. -

    -

    - A chained component has three basic differences from a component in general: -

    -
      -
    • The named services it provides to other components in the chain.
    • -
    • The list of services or checkpoints which the component itself - should be before in a chain, in other words, its dependents.
    • -
    • The list of services or checkpoints which the component itself - should be after in a chain, in other words, its dependencies.
    • -
    -

    - What a component should be placed before, - what it should be placed after and what itself provides, - may be either defined using Java annotations directly on the component class, - or it may be added specifically to the component declarations - in services.xml. - In general, the implementation should have as many of the necessary annotations as practical, - leaving the application specific configuration clean and simple to work with. -

    - - - -

    Ordering Components

    -

    - The execution order of the components in a chain is not defined by the - order of the components in the configuration. - Instead, the order is defined by adding the ordering constraints to the components: -

    -
      -
    • Any component may declare that it @Provides some - named functionality (the names are just labels that have no meaning - to the container).
    • - -
    • Any component may declare that it must be placed - @Before some named functionality,
    • - -
    • or that it must be placed @After some - functionality.
    • -
    -

    -The container will pick any ordering of a chain consistent with the -constraints of the components in the chain. -

    -Dependencies can be added in two ways. Dependencies which are due to -the code should be added as annotations in the code: -

    -
    -import com.yahoo.processing.*;
    -import com.yahoo.component.chain.dependencies.*;
    -
    -@Provides("SourceSelection")
    -@Before("Federation")
    -@After("IntentModel")
    -public class SimpleProcessor extends Processor {
    -
    -    @Override
    -    public Response process(Request request, Execution execution) {
    -        //TODO: Implement this
    -    }
    -}
    -
    -

    -Multiple functionality names may be specified by using the -syntax @Provides/Before/After({"A", -"B"}). -

    -Annotations which do not belong in the code may be added in the -configuration: -

    -
    -<container version="1.0">
    -
    -    <processing>
    -        <processor id="processor1" class="ai.vespa.examples.Processor1" />
    -        <chain id="default">
    -            <processor idref="processor1"/>
    -            <processor id="processor2" class="ai.vespa.examples.Processor2">
    -                <after>ai.vespa.examples.Processor1</after>
    -            </processor>
    -        </chain>
    -    </processing>
    -
    -    <nodes>
    -        <node hostalias="node1" />
    -    </nodes>
    -</container>
    -
    -

    -For convenience, components always Provides their own -fully qualified class name (the package and simple class name -concatenated, e.g. -ai.vespa.examples.SimpleProcessor) and their -simple name (that is, only the class name, like -SimpleProcessor in our searcher case), so it is always -possible to declare that one must execute before or after some -particular component. This goes for both general processors, searchers -and document processors. -

    -Finally, note that ordering constraints are just that; in particular -they are not used to determine if a given search chain, or set of -search chains, is “complete”. -

    - - - -

    Chain Inheritance

    -

    -As implied by examples above, chains may inherit other chains -in services.xml. -

    -
    {% highlight xml %}
    -
    -    
    -        
    -            
    -            
    -            
    -        
    -        
    -            
    -        
    -    
    -    
    -        
    -    
    -
    -{% endhighlight %}
    -

    -A chain will include all components from the chains named in the -optional inherits attribute, exclude from that set all -components named in the also optional -excludes attribute and add all the components listed -inside the defining tag. Both inherits and -excludes are space delimited lists of reference -names. -

    -For search chains, there are two built-in search chains which are especially -useful to inherit from, native and vespa. -native is a basic search chain, containing the -basic functionality most systems will need anyway, -vespa inherits from native and adds a -few extra searchers which most installations containing Vespa backends will need. -

    - -
    {% highlight xml %}
    -
    -    
    -        
    -            
    -            
    -            
    -        
    -    
    -    
    -        
    -    
    -
    -{% endhighlight %}
    - - - -

    Unit Tests

    -

    -A component should be unit tested in a chain containing the components it depends on. -It is not necessary to run the dependency handling framework to achieve that, -as the com.yahoo.component.chain.Chain class has several -constructors which are easy to use while testing. -

    -
    -Chain<Searcher> c = new Chain(new UselessSearcher("first"),
    -        new UselessSearcher("second"),
    -        new UselessSearcher("third"));
    -Execution e = new Execution(c, Execution.Context.createContextStub(null));
    -Result r = e.search(new Query());
    -
    -

    -The above is a rather useless test, but it illustrates how the basic -workflow can be simulated. The constructor will create a chain with -supplied searchers in the given order (it will not analyze any annotations). -

    - - - -

    Passing Information Between Components

    -

    -When different searchers or document processors depend on shared classes or field names, -it is good practice defining the name only in a single place. -An -example in the searcher development introduction illustrates an easy way to do that. -

    - - - -

    Invoking a Specific Search Chain

    -

    -The search chain to use can be selected in the request, by adding the request parameter: -searchChain=myChain -

    -

    -If no chain is selected in the query, the chain called -default will be used. If no chain called -default has been configured, the chain called -native will be used. The native chain is -always present and contains a basic set of searchers needed in most applications. -Custom chains will usually inherit the native chain to include those searchers. -

    -The search chain can also be set in a query profile. -

    - - - -

    Example: Configuration

    -

    -Annotations which do not belong in the code may be added in the configuration, -here a simple example with -search chains: -

    -
    -<container version="1.0">
    -    <search>
    -        <chain id="default" inherits="vespa">
    -            <searcher id="simpleSearcher" bundle="the name in artifactId in pom.xml" />
    -        </chain>
    -        <searcher id="simpleSearcher"
    -                  class="ai.vespa.examples.SimpleSearcher"
    -                  bundle="the name in artifactId in pom.xml" >
    -            <before>Cache</before>
    -            <after>Statistics</after>
    -            <after>Logging</after>
    -            <provides>SimpleTest</provides>
    -        </searcher>
    -    </search>
    -    <nodes>
    -        <node hostalias="node1" />
    -    </nodes>
    -</container>
    -
    -

    - And for document processor chains, it becomes: -

    -
    -<container version="1.0">
    -    <document-processing>
    -        <chain id="default">
    -            <documentprocessor id="ReplaceInFieldDocumentProcessor">
    -                <after>TextMetrics</after>
    -            </documentprocessor>
    -        </chain>
    -    </document-processing>
    -    <nodes>
    -        <node hostalias="node1"/>
    -    </nodes>
    -</container>
    -
    -

    -For searcher plugins the class - -com.yahoo.search.searchchain.PhaseNames -defines a set of checkpoints third party searchers may use to help -order themselves when extending the Vespa search chains. -

    -Note that ordering constraints are just that; in particular -they are not used to determine if a given search chain, or set of -search chains, is “complete”. -

    - - -

    Example: Cache with async write

    -

    - Use case: In a search chain, do early return and do further search asynchronously using - - ExecutorService. -

    -

    - Pseudocode: If cache hit (e.g. using Redis), just return cached data. - If cache miss, return null data and let the following searcher finish further query and write back to cache: -

    -
    {% highlight java %}
    -public Result search(Query query, Execution execution) {
    -    // cache lookup
    -
    -    if (cache_hit) {
    -        return result;
    -    }
    -    else {
    -        execution.search(query);  // invoke async cache update searcher next in chain
    -        return result;
    -    }
    -}
    -{% endhighlight %}
    diff --git a/en/applications/chaining.mdx b/en/applications/chaining.mdx new file mode 100644 index 0000000000..6ec00dc00a --- /dev/null +++ b/en/applications/chaining.mdx @@ -0,0 +1,209 @@ +--- +title: "Chained Components" +sidebarTitle: "Chaining" +description: "[Processors](/en/applications/processing), [searcher plug-ins](/en/applications/searchers) and [document processors](/en/applications/document-processors) are chained components. They are executed serially, with each providing some service or transform, and other optionally depending on these. In other words, a chain is a set of components with dependencies. Javadoc: [com.yahoo.component.chain.Chain](https://javadoc.io/doc/com.yahoo.vespa/chain/latest/com/yahoo/component/chain/Chain)" +--- + +It is useful to read the [federation guide](/en/querying/federation) before this document. + +A chained component has three basic differences from a component in general: + +- The named services it *provides* to other components in the chain. +- The list of services or checkpoints which the component itself should be *before* in a chain, in other words, its dependents. +- The list of services or checkpoints which the component itself should be *after* in a chain, in other words, its dependencies. + +What a component should be placed before, what it should be placed after and what itself provides, may be either defined using Java annotations directly on the component class, or it may be added specifically to the component declarations in [services.xml](/en/reference/applications/services/container). In general, the implementation should have as many of the necessary annotations as practical, leaving the application specific configuration clean and simple to work with. + +## Ordering Components + +The execution order of the components in a chain is not defined by the order of the components in the configuration. Instead, the order is defined by adding the *ordering constraints* to the components: + +- Any component may declare that it `@Provides` some named functionality (the names are just labels that have no meaning to the container). +- Any component may declare that it must be placed `@Before` some named functionality, +- or that it must be placed `@After` some functionality. + +The container will pick any ordering of a chain consistent with the constraints of the components in the chain. + +Dependencies can be added in two ways. Dependencies which are due to the code should be added as annotations in the code: + +```java highlight={4-6} +import com.yahoo.processing.*; +import com.yahoo.component.chain.dependencies.*; + +@Provides("SourceSelection") +@Before("Federation") +@After("IntentModel") + +public class SimpleProcessor extends Processor { + @Override + public Response process(Request request, Execution execution) { + //TODO: Implement this + } +} +``` + +Multiple functionality names may be specified by using the syntax `@Provides/Before/After({"A", "B"})`. + +Annotations which do not belong in the code may be added in the [configuration](/en/reference/applications/services/container): + +```xml highlight={8} + + + + + + + + ai.vespa.examples.Processor1 + + + + + + + + +``` + +For convenience, components always `Provides` their own fully qualified class name (the package and simple class name concatenated, e.g. `ai.vespa.examples.SimpleProcessor`) and their simple name (that is, only the class name, like `SimpleProcessor` in our searcher case), so it is always possible to declare that one must execute before or after some particular component. This goes for both general processors, searchers and document processors. + +Finally, note that ordering constraints are just that; in particular they are not used to determine if a given search chain, or set of search chains, is “complete”. + +## Chain Inheritance + +As implied by examples above, chains may inherit other chains in *services.xml*. + +```xml + + + + + + + + + + + + + + + +``` + +A chain will include all components from the chains named in the optional `inherits` attribute, exclude from that set all components named in the also optional `excludes` attribute and add all the components listed inside the defining tag. Both `inherits` and `excludes` are space delimited lists of reference names. + +For search chains, there are two built-in search chains which are especially useful to inherit from, `native` and `vespa`. `native` is a basic search chain, containing the basic functionality most systems will need anyway, `vespa` inherits from `native` and adds a few extra searchers which most installations containing Vespa backends will need. + +```xml + + + + + + + + + + + + +``` + +## Unit Tests + +A component should be unit tested in a chain containing the components it depends on. It is not necessary to run the dependency handling framework to achieve that, as the `com.yahoo.component.chain.Chain` class has several constructors which are easy to use while testing. + +```java +Chain c = new Chain(new UselessSearcher("first"), +new UselessSearcher("second"), +new UselessSearcher("third")); +Execution e = new Execution(c, Execution.Context.createContextStub(null)); +Result r = e.search(new Query()); +``` + +The above is a rather useless test, but it illustrates how the basic workflow can be simulated. The constructor will create a chain with supplied searchers in the given order (it will not analyze any annotations). + +## Passing Information Between Components + +When different searchers or document processors depend on shared classes or field names, it is good practice defining the name only in a single place. An [example](/en/applications/searchers#passing-information-between-searchers) in the searcher development introduction illustrates an easy way to do that. + +## Invoking a Specific Search Chain + +The search chain to use can be selected in the request, by adding the request parameter: `searchChain=myChain` + +If no chain is selected in the query, the chain called `default` will be used. If no chain called `default` has been configured, the chain called `native` will be used. The *native* chain is always present and contains a basic set of searchers needed in most applications. Custom chains will usually inherit the native chain to include those searchers. + +The search chain can also be set in a [query profile](/en/querying/query-profiles). + +## Example: Configuration + +Annotations which do not belong in the code may be added in the configuration, here a simple example with [search chains](/en/reference/applications/services/search#chain): + +```xml highlight={9-12} + + + + + + + Cache + Statistics + Logging + SimpleTest + + + + + + +``` + +And for [document processor chains](/en/reference/applications/services/docproc), it becomes: + +```xml highlight={5} + + + + + TextMetrics + + + + + + + +``` + +For searcher plugins the class [com.yahoo.search.searchchain.PhaseNames](https://javadoc.io/doc/com.yahoo.vespa/container-search/latest/com/yahoo/search/searchchain/PhaseNames) defines a set of checkpoints third party searchers may use to help order themselves when extending the Vespa search chains. + +Note that ordering constraints are just that; in particular they are not used to determine if a given search chain, or set of search chains, is “complete”. + +## Example: Cache with async write + +Use case: In a search chain, do early return and do further search asynchronously using [ExecutorService](https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/concurrent/ExecutorService). + +Pseudocode: If cache hit (e.g. using Redis), just return cached data. If cache miss, return null data and let the following searcher finish further query and write back to cache: + +```java +public Result search(Query query, Execution execution) { + // cache lookup + if (cache_hit) { + return result; + } + else { + execution.search(query); // invoke async cache update searcher next in chain + return result; + } +} +``` diff --git a/en/applications/components.html b/en/applications/components.html deleted file mode 100644 index 933093f1ee..0000000000 --- a/en/applications/components.html +++ /dev/null @@ -1,413 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Container Components" -redirect_from: -- /en/jdisc/container-components.html ---- - -

    - This document explains the common concepts necessary to develop all types of Container components. - A basic knowledge of the Vespa Container is required. -

    - All components must extend a base class from the Container code module. - For example, searchers must extend the class com.yahoo.search.Searcher. - The main available component types are: -

    - -

    - Searchers and document processors belong to a subclass of components - called chained components. - For an introduction to how the different component types interact, - refer to the overview of component types. -

    - The components of the search container are usually deployed as part of - an OSGi bundle. - Build the bundles using maven and the bundle plugin. - Refer to the - multiple-bundles sample app for a multi-bundle example. -

    - - - -

    Concurrency

    -

    - Components will be executed concurrently by multiple threads. - This places an important constraint on all component classes: - non-final instance variables are not safe. - They must be eliminated, or made thread-safe somehow. -

    - - - -

    Resource management

    -

    - Components that use threads, file handles or other native resources - that needs to be released when the component falls out of scope, - must override a method called deconstruct. - Here is an example implementation from a component that uses a thread pool named 'executor': -

    -
    -@Override
    -public void deconstruct() {
    -    super.deconstruct();
    -    try {
    -        executor.shutdown();
    -        executor.awaitTermination(10, TimeUnit.SECONDS);
    -    } catch (InterruptedException e) {
    -        Thread.currentThread().interrupt();
    -    }
    -}
    -
    -

    - Note that it is always advisable to call the super-method first. - Also see - SharedResource.java for how to configure debug options - for use in tools like YourKit. - This can be used to track component lifetime / (de)construction issues, e.g.: -

    -
    -<nodes>
    -    <jvm options="-Djdisc.debug.resources=true"/>
    -</nodes>
    -
    -

    - Read more in container profiling. -

    - - - -

    Dependency injection

    - -

    - The components might need to access resources, such as other components or config. - These are injected directly into the constructor. - The following types of constructor dependencies are allowed: -

    - -

    - The Component Reference contains a - complete list of built-in injectable components. -

    -

    - If your component class needs more than one public constructor, - the one to be used by the container must be annotated with @com.yahoo.component.annotation.Inject from - annotations. -

    - - -

    The SystemInfo Injectable Component

    -

    - This component provides information about the environment that the component is running in, - for example -

    -
      -
    • The zone in the Vespa Cloud, if applicable.
    • -
    • The number of nodes in the container cluster, and their indices.
    • -
    • The index of the node this is running on.
    • -
    -

    - The two latter can be used e.g. for bucket testing - new features on a subset of nodes. - Please note that the node indices are not necessarily contiguous or starting from zero. -

    - - - -

    Deploying a Component

    -

    - The container will create one or more instances of the component, - as specified in the application package. - The container will create a new instance of this component - only when it is reconfigured, - so any data needed by the component can be read and prepared from a constructor in the component. -

    - See the full API available to components at the - - Container Javadoc. -

    - Once the component passes unit tests, it can be deployed. - The steps involved are building the component jar file, - adding it to the Vespa application package and deploying the application package. - These steps are described in the following sections, using a searcher as example. -

    - - -

    Building the Plugin .jar

    -

    - To build the plugin jar, call mvn install in the project directory. - It can then be found in the target directory, and will have the suffix -deploy.jar. -

    - Assume for the rest of the document that the artifactId - is com.yahoo.search.example.SimpleSearcher and the version is 1.0. - The plugin built will then have the name com.yahoo.search.example.SimpleSearcher-1.0-deploy.jar. -

    - - -

    Adding the Plugin to the Vespa Application Package

    -

    - The previous step should produce a plugin jar file, - which may now be deployed to Vespa by adding it to - an application package: - A directory containing at minimum hosts.xml and services.xml. -

    -
      -
    • put com.yahoo.search.example.SimpleSearcher-1.0-deploy.jar - in the components/ directory under the application package root
    • -
    • modify services.xml to include the Searcher
    • -
    -

    - To include the searcher, define a search chain and add the searcher to it. Example: -

    -
    -<?xml version="1.0" encoding="utf-8" ?>
    -<services version="1.0">
    -
    -    <admin version="2.0">
    -        <adminserver hostalias="node1" />
    -        </configservers>
    -        <logserver hostalias="node1" />
    -    </admin>
    -
    -    <container version="1.0">
    -        <search>
    -            <chain id="default" inherits="vespa">
    -                <searcher id="com.yahoo.search.example.SimpleSearcher" bundle="the name in <artifactId> in your pom.xml" />
    -            </chain>
    -        </search>
    -        <nodes>
    -            <node hostalias="node1" />
    -        </nodes>
    -    </container>
    -
    -</services>
    -
    -

    - The searcher id above is resolved to the plugin jar we added by the Bundle-SymbolicName - (a field in the manifest of the jar file), - which is determined by the artifactId, - and to the right class within the bundle by the class name. - By keeping the searcher id, class name - and artifactId the same, we keep things simple, - but more advanced use where this is possible is also supported. - This will be explained in later sections. -

    - For a reference to these tags, - see the search chains reference. -

    - Example hosts.xml: -

    -
    -<?xml version="1.0" encoding="utf-8" ?>
    -<hosts>
    -    <host name="localhost">
    -        <alias>node1</alias>
    -    </host>
    -</hosts>
    -
    -

    - By creating a directory containing this services.xml, - hosts.xml and components/com.yahoo.search.example.SimpleSearcher-1.0-deploy.jar, - that directory becomes a complete application package containing a bundle, which can now be deployed. -

    - - -

    Deploying the Application Package

    -

    - Set up a Vespa instance using the quick start. - Once the component and the config are added to the application package, - it can be deployed - by running vespa deploy. - These steps will copy any changed bundles to the nodes in the cluster which needs them - and switch queries over to running the new component versions. -

    - This works safely without requiring any processes to be restarted, - even if the application package contains changes to classes which are already running queries. - The switch is atomic from the point of view of the query - - all queries will execute to completion, - either using only the components of the last version of the application package or only the new ones, - so interdependent changes in multiple searcher components can be deployed without problems. -

    - - -

    JNI requires restart

    -

    - The exception to the above is bundles containing JNI packages. - There can only be one instance of the native library, so such bundles cannot reload. - Best practice is to load the JNI library in the constructor, - as this will cause the new bundle not to load, but continue on the current version. - A subsequent restart will load the new bundle. - This will hence not cause failures. - Alternatively, if the JNI library is initialized lazily (e.g. on first invocation), - bundle reloads will succeed, but subsequent invocations of code using the JNI library will fail. - Hence, the new version will run, but fail. -

    - A warning is issued in the log when deploying rather than the normal - Switching to the latest deployed set of handlers - example: -

    -
    -[2016-09-21 14:22:05.387] WARNING : container        stderr     Cannot load mylib native library
    -
    -

    - To minimize restarts, it is recommended to put JNI components in minimal, separate bundles. - This will prevent reload of the JNI-bundles, unless the JNI-bundle itself is changed. -

    - - -

    Monitoring the active Application

    -

    - All containers also provide a built-in handler that outputs JSON formatted information about the active application, - including its components and chains (it can also be configured to show - a user-defined version). - The handler answers to requests with the path /ApplicationStatus. - For example, if 'localhost' runs a container with HTTP configured on port 8080: -

    -
    -http://localhost:8080/ApplicationStatus
    -
    - - -

    Including third-party libraries

    -

    - External dependencies can be included into the bundle. -

    - - -

    Exporting, importing and including packages in bundles

    -

    - OSGi features information hiding - - by default all the classes used inside a bundle are invisible from the outside. -

    - - - -

    Global and exported packages

    -

    - The JDisc Container has one set of global packages. - These are packages that are available with no import, - and constitutes the supported API of the JDisc Container. - Backwards incompatible changes are not made to these packages. -

    - There is also a set of exported packages. - These are available for import, and includes all legacy packages, - plus extension packages which are not part of the core API. - Note that these are not considered to be "public" APIs, as global packages are, - and backwards incompatible changes can be made to these packages, - or they may be removed. -

    - The list of exported and global packages is available in the - container-disc - pom.xml, in project/properties/exportedPackages - and project/properties/globalPackages. -

    - - -

    Versions

    -

    - All the elements of the search container which may be referenced by an - id may be versioned, that includes chains, components and query profiles. - This allows multiple versions of these elements to be used at the same time, - including multiple versions of the same classes, - which is handy for bucket testing new versions. -

    - An id or id reference may include a version by using the following syntax: name:version. - This works with ids in search requests, services.xml, code and query profiles. -

    - A version has the format: -

    -
    -major.minor.micro.qualifier
    -
    -

    - where major, minor and micro are integers and qualifier is a string. - Any right-hand portion of the version string may be skipped. - In versions, skipped values mean "0" (and empty for the qualifier). - In version references skipped values means "unspecified". - Any unspecified number will be matched to the highest number available, - while a qualifier specified must be matched exactly if it is specified - (qualifiers are rarely used). -

    - To specify the version of a bundle, specify version in pom.xml - (we recommend not using qualifier): -

    -
    -<groupId>com.yahoo.example</groupId>
    -<artifactId>MyPlugin</artifactId>
    -<version>major.minor.micro</version>
    -
    -

    - This will automatically be used to set the Bundle-Version in the bundle manifest. -

    - For more details, see component versioning. -

    - - - -

    Troubleshooting

    - - -

    Container start

    -

    - If there is some error in the application package, - it will usually be detected during the vespa prepare step and cause an error message. - However, some classes of errors are only detected once the application is deployed. - When redeploying an application, it is therefore recommended watching the vespa log by running: -

    -
    -vespa-logfmt -N
    -
    -

    The new application is active after the INFO message:

    -
    -Switched to the latest deployed set of handlers...;
    -
    -

    - If this message does not appear after a reasonable amount of time - after completion of vespa activate, - one will see some errors or warnings instead, that will help debug the application. -

    - - -

    Component load

    -

    - At deployment or container start, components are constructed. - Construction can fail - to debug, enable more logging (replace "container" as needed with container id): -

    -
    -$ vespa-logctl container:com.yahoo.container.di.componentgraph.core.ComponentNode debug=on
    -  .com.yahoo.container.di.componentgraph.core.ComponentNode   ON  ON  ON  ON  ON  ON  ON OFF
    -
    -

    - Look for "Constructing" and "Finished constructing" in vespa.log - - this identifies components that did not construct. -

    -

    Model downloading failures look like the below and are caused by a fail to download the model to the container:

    -
    -ERROR   container        Container.com.yahoo.jdisc.core.StandaloneMain    JDisc exiting: Throwable caught:
    -exception=
    -java.lang.RuntimeException: Not able to create config builder for payload '{
    -  "tokenizerPath": "\\"\\" https://huggingface.co/Snowflake/snowflake-arctic-embed-l/raw/main/tokenizer.json \\"\\"",
    -  "transformerModel": "\\"\\" https://huggingface.co/Snowflake/snowflake-arctic-embed-l/resolve/main/onnx/model_int8.onnx \\"\\"",
    -  "transformerMaxTokens": 512,
    -  "transformerInputIds": "input_ids",
    -  "transformerAttentionMask": "attention_mask",
    -  "transformerTokenTypeIds": "token_type_ids",
    -  "transformerOutput": "last_hidden_state",
    -  "normalize": true,
    -  "poolingStrategy": "cls",
    -  "transformerExecutionMode": "sequential",
    -  "transformerInterOpThreads": 1,
    -  "transformerIntraOpThreads": -4,
    -  "transformerGpuDevice": 0
    -}
    -
    -

    Check urls / names, and that the model can be downloaded in the network the Vespa Container is running.

    diff --git a/en/applications/components.mdx b/en/applications/components.mdx new file mode 100644 index 0000000000..1e17926658 --- /dev/null +++ b/en/applications/components.mdx @@ -0,0 +1,259 @@ +--- +title: "Container Components" +sidebarTitle: "Components" +description: "This document explains the common concepts necessary to develop all types of Container components." +--- + +All components must extend a base class from the Container code module. For example, searchers must extend the class `com.yahoo.search.Searcher`. The main available component types are: + +- [processors](/en/applications/processing) +- [searchers](/en/applications/searchers) +- [document processors](/en/applications/document-processors) +- [search result renderers](/en/applications/result-renderers) +- [provider components](/en/applications/dependency-injection#special-components). + +Searchers and document processors belong to a subclass of components called [chained components](/en/applications/chaining). For an introduction to how the different component types interact, refer to the [overview of component types](/en/reference/applications/components#component-types). + +The components of the search container are usually deployed as part of an [OSGi bundle](/en/applications/bundles). Build the bundles using maven and the [bundle plugin](/en/applications/bundles#maven-bundle-plugin). Refer to the [multiple-bundles sample app](https://github.com/vespa-engine/sample-apps/tree/master/examples/multiple-bundles) for a multi-bundle example. + +## Concurrency + +Components will be executed concurrently by multiple threads. This places an important constraint on all component classes: *non-final instance variables are not safe.* They must be eliminated, or made thread-safe somehow. + +## Resource management + +Components that use threads, file handles or other native resources that needs to be released when the component falls out of scope, must override a method called `deconstruct`. Here is an example implementation from a component that uses a thread pool named 'executor': + +```java +@Override +public void deconstruct() { + super.deconstruct(); + try { + executor.shutdown(); + executor.awaitTermination(10, TimeUnit.SECONDS); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } +} +``` + +Note that it is always advisable to call the super-method first. Also see [SharedResource.java](https://github.com/vespa-engine/vespa/blob/master/jdisc_core/src/main/java/com/yahoo/jdisc/SharedResource.java) for how to configure [debug options](/en/reference/applications/services/container#jvm) for use in tools like YourKit. This can be used to track component lifetime / (de)construction issues, e.g.: + +```xml + + + +``` + +Read more in [container profiling](/en/performance/profiling#profiling-the-query-container). + +## Dependency injection + +The components might need to access resources, such as other components or config. These are injected directly into the constructor. The following types of constructor dependencies are allowed: + + - [Config objects](/en/applications/configuring-components) + - [Other components](/en/applications/dependency-injection) + - [The Linguistics library](/en/linguistics/linguistics) + - [System info](#the-systeminfo-injectable-component) + +The [Component Reference](/en/reference/applications/components#injectable-components) contains a complete list of built-in injectable components. + +If your component class needs more than one public constructor, the one to be used by the container must be annotated with `@com.yahoo.component.annotation.Inject` from [annotations](https://search.maven.org/artifact/com.yahoo.vespa/annotations). + +### The SystemInfo Injectable Component + +This component provides information about the environment that the component is running in, for example + +- The zone in the Vespa Cloud, if applicable. +- The number of nodes in the container cluster, and their indices. +- The index of the node this is running on. + +The two latter can be used e.g. for [bucket testing](/en/applications/testing#feature-switches-and-bucket-tests) new features on a subset of nodes. Please note that the node indices are not necessarily contiguous or starting from zero. + +## Deploying a Component + +The container will create one or more instances of the component, as specified in [the application package](#adding-component-to-application-package). The container will create a new instance of this component only when it is reconfigured, so any data needed by the component can be read and prepared from a constructor in the component. + +See the full API available to components at the [Container Javadoc](https://javadoc.io/doc/com.yahoo.vespa/container-disc/latest/com/yahoo/container/package-summary). + +Once the component passes unit tests, it can be deployed. The steps involved are building the component jar file, adding it to the Vespa application package and deploying the application package. These steps are described in the following sections, using a searcher as example. + +### Building the Plugin .jar + +To build the plugin jar, call `mvn install` in the project directory. It can then be found in the target directory, and will have the suffix *-deploy.jar*. + +Assume for the rest of the document that the artifactId is `com.yahoo.search.example.SimpleSearcher` and the version is `1.0`. The plugin built will then have the name *com.yahoo.search.example.SimpleSearcher-1.0-deploy.jar*. + +### Adding the Plugin to the Vespa Application Package + +The previous step should produce a plugin jar file, which may now be deployed to Vespa by adding it to an [application package](/en/basics/applications): A directory containing at minimum *hosts.xml* and *services.xml*. + +- put `com.yahoo.search.example.SimpleSearcher-1.0-deploy.jar` in the `components/` directory under the application package root +- modify [services.xml](/en/reference/applications/services/services) to include the Searcher + +To include the searcher, define a search chain and add the searcher to it. Example: + +```xml + + + + + + + + + + + + + + + + + + + + + + + +``` + +The searcher id above is resolved to the plugin jar we added by the `Bundle-SymbolicName` ([a field in the manifest of the jar file](/en/applications/bundles)), which is determined by the `artifactId`, and to the right class within the bundle by the class name. By keeping the `searcher id`, `class name` and `artifactId` the same, we keep things simple, but more advanced use where this is possible is also supported. This will be explained in later sections. + +For a reference to these tags, see [the search chains reference](/en/reference/applications/services/search#chain). + +Example `hosts.xml`: + +```xml + + + + node1 + + +``` + +By creating a directory containing this `services.xml`, `hosts.xml` and `components/com.yahoo.search.example.SimpleSearcher-1.0-deploy.jar`, that directory becomes a complete application package containing a bundle, which can now be deployed. + +### Deploying the Application Package + +Set up a Vespa instance using the [quick start](/en/basics/deploy-an-application-local). Once the component and the config are added to the application package, it can be [deployed](/en/basics/applications#deploying-applications) by running `vespa deploy`. These steps will copy any changed bundles to the nodes in the cluster which needs them and switch queries over to running the new component versions. + +This works safely without requiring any processes to be restarted, even if the application package contains changes to classes which are already running queries. The switch is atomic from the point of view of the query - all queries will execute to completion, either using only the components of the last version of the application package or only the new ones, so interdependent changes in multiple searcher components can be deployed without problems. + +#### JNI requires restart + +The exception to the above is bundles containing JNI packages. There can only be one instance of the native library, so such bundles cannot reload. Best practice is to load the JNI library in the constructor, as this will cause the new bundle *not* to load, but continue on the current version. A subsequent restart will load the new bundle. This will hence not cause failures. Alternatively, if the JNI library is initialized lazily (e.g. on first invocation), bundle reloads will succeed, but subsequent invocations of code using the JNI library will fail. Hence, the new version will run, but fail. + +A warning is issued in the log when deploying rather than the normal *Switching to the latest deployed set of handlers* - example: + +```txt +[2016-09-21 14:22:05.387] WARNING : container stderr Cannot load mylib native library +``` + +To minimize restarts, it is recommended to put JNI components in minimal, separate bundles. This will prevent reload of the JNI-bundles, unless the JNI-bundle itself is changed. + +#### Monitoring the active Application + +All containers also provide a built-in handler that outputs JSON formatted information about the active application, including its components and chains (it can also be configured to show [a user-defined version](/en/reference/applications/application-packages#versioning-application-packages)). The handler answers to requests with the path `/ApplicationStatus`. For example, if 'localhost' runs a container with HTTP configured on port 8080: + +```txt +http://localhost:8080/ApplicationStatus +``` + +### Including third-party libraries + +External dependencies [can be included into the bundle](/en/applications/bundles#maven-bundle-plugin). + +### Exporting, importing and including packages in bundles + +[OSGi features information hiding - by default all the classes used inside a bundle are invisible from the outside.](/en/applications/bundles) + +### Global and exported packages + +The JDisc Container has one set of *global* packages. These are packages that are available with no import, and constitutes the supported API of the JDisc Container. Backwards incompatible changes are not made to these packages. + +There is also a set of *exported* packages. These are available for import, and includes all legacy packages, plus extension packages which are not part of the core API. Note that these are not considered to be "public" APIs, as global packages are, and backwards incompatible changes *can* be made to these packages, or they may be removed. + +The list of exported and global packages is available in the [container-disc pom.xml](https://github.com/vespa-engine/vespa/blob/master/container-disc/pom.xml), in `project/properties/exportedPackages` and `project/properties/globalPackages`. + +### Versions + +All the elements of the search container which may be referenced by an id may be *versioned*, that includes chains, components and query profiles. This allows multiple versions of these elements to be used at the same time, including multiple versions of the same classes, which is handy for [bucket testing](/en/applications/testing#feature-switches-and-bucket-tests) new versions. + +An id or id reference may include a version by using the following syntax: `name:version`. This works with ids in search requests, services.xml, code and query profiles. + +A version has the format: + +```txt +major.minor.micro.qualifier +``` + +where major, minor and micro are integers and qualifier is a string. Any right-hand portion of the version string may be skipped. In *versions*, skipped values mean "0" (and *empty* for the qualifier). In *version references* skipped values means "unspecified". Any unspecified number will be matched to the highest number available, while a qualifier specified *must* be matched exactly if it is specified (qualifiers are rarely used). + +To specify the version of a bundle, specify version in pom.xml (we recommend not using *qualifier*): + +```xml +com.yahoo.example +MyPlugin +major.minor.micro +``` + +This will automatically be used to set the `Bundle-Version` in the bundle manifest. + +For more details, see [component versioning](/en/reference/applications/components#component-versioning). + +## Troubleshooting + +### Container start + +If there is some error in the application package, it will usually be detected during the `vespa prepare` step and cause an error message. However, some classes of errors are only detected once the application is deployed. When redeploying an application, it is therefore recommended watching the vespa log by running: + +```txt +vespa-logfmt -N +``` + +The new application is active after the INFO message: + +```txt +Switched to the latest deployed set of handlers...; +``` + +If this message does not appear after a reasonable amount of time after completion of `vespa activate`, one will see some errors or warnings instead, that will help debug the application. + +### Component load + +At deployment or container start, components are constructed. Construction can fail - to debug, enable more logging (replace "container" as needed with container id): + +```txt +$ vespa-logctl container:com.yahoo.container.di.componentgraph.core.ComponentNode debug=on +.com.yahoo.container.di.componentgraph.core.ComponentNode ON ON ON ON ON ON ON OFF +``` + +Look for "Constructing" and "Finished constructing" in *vespa.log* - this identifies components that did not construct. + +Model downloading failures look like the below and are caused by a fail to download the model to the container: + +```json +ERROR container Container.com.yahoo.jdisc.core.StandaloneMain JDisc exiting: Throwable caught: +exception= +java.lang.RuntimeException: Not able to create config builder for payload '{ +"tokenizerPath": "\\"\\" https://huggingface.co/Snowflake/snowflake-arctic-embed-l/raw/main/tokenizer.json \\"\\"", +"transformerModel": "\\"\\" https://huggingface.co/Snowflake/snowflake-arctic-embed-l/resolve/main/onnx/model_int8.onnx \\"\\"", +"transformerMaxTokens": 512, +"transformerInputIds": "input_ids", +"transformerAttentionMask": "attention_mask", +"transformerTokenTypeIds": "token_type_ids", +"transformerOutput": "last_hidden_state", +"normalize": true, +"poolingStrategy": "cls", +"transformerExecutionMode": "sequential", +"transformerInterOpThreads": 1, +"transformerIntraOpThreads": -4, +"transformerGpuDevice": 0 +} +``` + +Check urls / names, and that the model can be downloaded in the network the Vespa Container is running. diff --git a/en/applications/config-system.html b/en/applications/config-system.html deleted file mode 100644 index 957b2278a9..0000000000 --- a/en/applications/config-system.html +++ /dev/null @@ -1,341 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "The Config System" -redirect_from: -- /en/config-system.html ---- - -

    The config system in Vespa is responsible for turning the application package into -live configuration of all the nodes, processes and components that realizes the running system. -Here we deep dive into various aspects of how this works.

    - -

    Node configuration

    - -

    The problem of configuring nodes can be divided into three parts, -each addressed by different solutions:

    - -
      -
    • - Node system level configuration: Configure OS - level settings such as time zone as well as user privileges on the node. -
    • -
    • - Package management: Ensure that the correct - set of software packages is installed on the nodes. - This functionality is provided by three tools working together. -
    • -
    • - Vespa configuration: Starts the configured - set of processes on each node with their configured startup - parameters and provides dynamic configuration to the modules run by these services. - Configuration here is any data which: -
        -
      • can not be fixed at compile time
      • -
      • is static most of the time
      • -
      -
    • -
    - -

    Note that by these definitions, this allows all the nodes to -have the same software packages (disregarding version differences, discussed later), -as variations in what services are run on each node and in their behavior -is achieved entirely by using Vespa Configuration. -This allows managing the complexity of node variations completely -within the configuration system, rather than across multiple systems.

    - -

    Configuring a system can be divided into:

    -
      -
    • Configuration assembly: Assembly of a complete - set of configurations for delivery from the inputs provided by the - parties involved in configuring the system
    • -
    • Configuration delivery: Definition of - individual configurations, APIs for requesting and accessing - configuration, and the mechanism for delivering configurations from - their source to the receiving components
    • -
    - -

    This division allows the problem of reliable configuration delivery in -large distributed systems to be addressed in configuration delivery, -while the complexities of assembling complete configurations -can be treated as a vm-local design problem.

    - -

    An important feature of Vespa Configuration is the nature of the interface -between the delivery and assembly subsystems. -The assembly subsystem creates as output a (Java) object model of the distributed system. -The delivery subsystem queries this model to obtain concrete configurations -of all the components of the system. -This allows the assembly subsystem to accept higher level, and simpler to use, -abstractions as input and automatically derive detailed configurations with the correct interdependencies. -This division insulates the external interface and the components being configured from changes in each other. -In addition, the system model provides the home for logic -implementing node/component instance variations of configuration.

    - - -

    Configuration assembly

    - -

    Config assembly is the process of turning the configuration input sources -into an object model of the desired system, -which can respond to queries for configs given a name and config id. -Config assembly for Vespa systems can become complex, -because it involves merging information owned by multiple parties:

    - -
      -
    • Vespa operations - own the nodes and controls assignment of nodes to services/applications
    • -
    • Vespa service providers - own services which hosts multiple applications running on Vespa
    • -
    • Vespa applications - define the final applications running on nodes and shared services
    • -
    - -

    The current config model assembly procedure uses a single source - the application package. -The application package is a directory structure containing defined files -and subdirectories which together completely defines the system - -including which nodes belong in the system, -which services they should run and the configuration of these services and their components. -When the application deployer wants to change the application, -vespa prepare is issued to a config server, -with the application package as argument. -

    -

    -At this point the system model is assembled and validated -and any feedback is issued to the deployer. -If the deployer decides to make the new configuration active, -a vespa activate is then issued, -causing the config server cluster to switch to the new system model -and respond with new configs on any active subscriptions -where the new system model caused the config to change. -This ensures that subscribers gets new configs timely on changes, -and that the changes propagated are the minimal set -such that small changes to an application package -causes correspondingly small changes to the system. -

    -The config server assembles app config -

    -The config model itself is pluggable, so that service providers may write -plugins for assembling a particular service. -The plugins are written in Java, and is installed together with the Vespa Configuration. -Service plugins define their own syntax for specifying services -that may be configured by Vespa applications. -This allows the applications to be specified in an abstract manner, -decoupled from the configuration that is delivered to the components. -

    - - - -

    Configuration delivery

    -

    -Configuration delivery encompasses the following aspects: -

    -
      -
    • Definition of configurations
    • -
    • The component view (API) of configuration
    • -
    • Configuration delivery mechanism
    • -
    -

    These aspects work together to realize the following goals:

    -
      -
    • Eliminate inconsistency between code and configuration.
    • -
    • Eliminate inconsistency between the desired configuration and the state on each node.
    • -
    • Limit temporary inconsistencies after reconfiguration.
    • -
    -

    -The next three subsections discusses the three aspects above, -followed by subsections on two special concerns - bootstrapping and system upgrades. -

    - - -

    Configuration definitions

    -

    -A configuration is a set of simple or array key-values with a name and a type, -which can possibly be nested - example: -

    -
    -myProperty "myvalue"
    -myArray[1]
    -myArray[0].key1 "someValue"
    -myArray[0].key2 1337
    -
    -

    -The type definition (or class) of a configuration object -defines and documents the set of fields a configuration may contain -with their types and default values. -It has a name as well as a namespace. -For example, the above config instance may have this definition: -

    -
    -namespace=foo.bar
    -
    -# Documentation of this key
    -myProperty string default="foo"
    -
    -# etc.
    -myArray[].key1 string
    -myArray[].key2 int default=0
    -
    -

    -An individual config typically contains a coherent set of settings regarding some topic, -such as logging or indexing. -A complete system consists of many instances of many config types. -

    - - -

    Component view

    -

    -Individual components of a system consumes one or more such configs and -use their values to influence their behavior. -APIs are needed for requesting configs -and for accessing the values of those configs as they are provided. -

    -

    -Access to configs happens through a (Java or C++) class -generated from the config definition file. -This ensures that any inconsistency between the fields declared in a config type -and the expectations of the code accessing it are caught at compile time. -The config definition is best viewed as another class with an alternative -form of source syntax belonging to the components consuming it. -A Maven target is provided for generating such classes from config definition types. -

    -

    -Components may use two different methods for requesting configurations -subscription and dependency injection: -

    -

    -Subscription: The component sets up -ConfigSubscriber, then subscribes to one or more configs. -This is the simple approach, there are other ways of -getting configs too: -

    -
    {% highlight java %}
    -ConfigSubscriber subscriber = new ConfigSubscriber();
    -ConfigHandle handle = subscriber.subscribe(MyConfig.class, "myId");
    -if (!subscriber.nextConfig()) throw new RuntimeException("Config timed out.");
    -if (handle.isChanged()) {
    -    String message = handle.getConfig().myKey();
    -    // ... consume the rest of this config
    -}
    -{% endhighlight %}
    -

    -Dependency injection: The component declares its -config dependencies in the constructor and subscriptions are set up on its behalf. -When changed configs are available a new instance of the component is created. -The advantage of this method is that configs are immutable throughout the lifetime of the component -such that no thread coordination is required. -This method is currently only available in Java using the Container. -

    -
    {% highlight java %}
    -public MyComponent(MyConfig config) {
    -    String myKey = config.myKey();
    -    // ... consume the rest of this config
    -}
    -{% endhighlight %}
    -

    -For unit testing, -configs can be created with Builders, -submitted directly to components. -

    - - -

    Delivery mechanism

    -

    -The config delivery mechanism is responsible for ensuring that a new -config instance is delivered to subscribing components, -each time there is a change to the system model causing that config instance to change. -A config subscription is identified by two parameters, -the config definition name and namespace -and the config id -used to identify the particular component instance making the subscription. -

    -

    -The in-process config library will forward these subscription requests to a node local -config proxy, -which provides caching and fan-in from processes to node. -The proxy in turn issues these subscriptions to a node in the configuration server cluster, -each of which hosts a copy of the system model and resolves config requests by querying the system model. -

    -

    -To provide config server failover, -the config subscriptions are implemented as long-timeout gets, -which are immediately resent when they time out, -but conceptually this is best understood as push subscriptions: -

    -Nodes get config from a config server cluster -

    -As configs are not stored as files locally on the nodes, -there is no possibility of inconsistencies due to local edits, -or of nodes coming out of maintenance with a stale configuration. -As configuration changes are pushed as soon as the config server cluster allows, -time inconsistencies during reconfigurations are minimized, -although not avoided as there is no global transaction. -

    -Application code and config is generally pulled from the config server - -it is however possible to use the url -config type to refer to any resource to download to nodes. -

    - - -

    Bootstrapping

    -

    -Each Vespa node runs a config-sentinel process -which start and maintains services run on a node. -

    - - -

    System upgrades

    -

    -The configuration server will up/downgrade between config versions on the fly on minor upgrades -which causes discrepancies between the config definitions requested -from those produced by the configuration model. -Major upgrades, which involve incompatible changes to the configuration protocol or the system model, -require a procedure. -

    - - - -

    Notes

    -

    -Find more information for using the Vespa config API in the -reference doc. -

    -

    Vespa Configuration makes the following assumptions about the nodes using it:

    -
      -
    • - All nodes have the software packages needed to run the - configuration system and any services which will be configured to run on the node. - This usually means that all nodes have the same software, - although this is not a requirement -
    • -
    • - All nodes have - VESPA_CONFIGSERVERS set -
    • -
    • All nodes know their fully qualified domain name
    • -
    -

    -Reading this document is not necessary in order to use Vespa or to develop -Java components for the Vespa container - for this purpose, refer to -Configuring components. -

    - - - -

    Further reads

    - diff --git a/en/applications/config-system.mdx b/en/applications/config-system.mdx new file mode 100644 index 0000000000..244172ea43 --- /dev/null +++ b/en/applications/config-system.mdx @@ -0,0 +1,170 @@ +--- +title: "The Config System" +description: "The config system in Vespa is responsible for turning the application package into live configuration of all the nodes, processes and components that realizes the running system. Here we deep dive into various aspects of how this works." +--- + +## Node configuration + +The problem of configuring nodes can be divided into three parts, each addressed by different solutions: + +- **Node system level configuration:** Configure OS level settings such as time zone as well as user privileges on the node. +- **Package management**: Ensure that the correct set of software packages is installed on the nodes. This functionality is provided by three tools working together. +- **Vespa configuration:** Starts the configured set of processes on each node with their configured startup parameters and provides dynamic configuration to the modules run by these services. *Configuration* here is any data which: + + - can not be fixed at compile time + - is static most of the time + +Note that by these definitions, this allows all the nodes to have the same software packages (disregarding version differences, discussed later), as variations in what services are run on each node and in their behavior is achieved entirely by using Vespa Configuration. This allows managing the complexity of node variations completely within the configuration system, rather than across multiple systems. + +Configuring a system can be divided into: + +- **Configuration assembly:** Assembly of a complete set of configurations for delivery from the inputs provided by the parties involved in configuring the system +- **Configuration delivery:** Definition of individual configurations, APIs for requesting and accessing configuration, and the mechanism for delivering configurations from their source to the receiving components + +This division allows the problem of reliable configuration delivery in large distributed systems to be addressed in configuration delivery, while the complexities of assembling complete configurations can be treated as a vm-local design problem. + +An important feature of Vespa Configuration is the nature of the interface between the delivery and assembly subsystems. The assembly subsystem creates as output a (Java) object model of the distributed system. The delivery subsystem queries this model to obtain concrete configurations of all the components of the system. This allows the assembly subsystem to accept higher level, and simpler to use, abstractions as input and automatically derive detailed configurations with the correct interdependencies. This division insulates the external interface and the components being configured from changes in each other. In addition, the system model provides the home for logic implementing node/component instance variations of configuration. + +## Configuration assembly + +Config assembly is the process of turning the configuration input sources into an object model of the desired system, which can respond to queries for configs given a name and config id. Config assembly for Vespa systems can become complex, because it involves merging information owned by multiple parties: + +- **Vespa operations** own the nodes and controls assignment of nodes to services/applications +- **Vespa service providers** own services which hosts multiple applications running on Vespa +- **Vespa applications** define the final applications running on nodes and shared services + +The current config model assembly procedure uses a single source - the *application package*. The application package is a directory structure containing defined files and subdirectories which together completely defines the system - including which nodes belong in the system, which services they should run and the configuration of these services and their components. When the application deployer wants to change the application, [vespa prepare](/en/reference/clients/vespa-cli/vespa_prepare) is issued to a config server, with the application package as argument. + +At this point the system model is assembled and validated and any feedback is issued to the deployer. If the deployer decides to make the new configuration active, a [vespa activate](/en/reference/clients/vespa-cli/vespa_activate) is then issued, causing the config server cluster to switch to the new system model and respond with new configs on any active subscriptions where the new system model caused the config to change. This ensures that subscribers gets new configs timely on changes, and that the changes propagated are the minimal set such that small changes to an application package causes correspondingly small changes to the system. + + +![](/assets/img/config-assembly.svg) + + +The config model itself is pluggable, so that service providers may write plugins for assembling a particular service. The plugins are written in Java, and is installed together with the Vespa Configuration. Service plugins define their own syntax for specifying services that may be configured by Vespa applications. This allows the applications to be specified in an abstract manner, decoupled from the configuration that is delivered to the components. + +## Configuration delivery + +Configuration delivery encompasses the following aspects: + +- Definition of configurations +- The component view (API) of configuration +- Configuration delivery mechanism + +These aspects work together to realize the following goals: + +- Eliminate inconsistency between code and configuration. +- Eliminate inconsistency between the desired configuration and the state on each node. +- Limit temporary inconsistencies after reconfiguration. + +The next three subsections discusses the three aspects above, followed by subsections on two special concerns - bootstrapping and system upgrades. + +### Configuration definitions + +A *configuration* is a set of simple or array key-values with a name and a type, which can possibly be nested - example: + +```txt +myProperty "myvalue" +myArray[1] +myArray[0].key1 "someValue" +myArray[0].key2 1337 +``` + +The *type definition* (or class) of a configuration object defines and documents the set of fields a configuration may contain with their types and default values. It has a name as well as a namespace. For example, the above config instance may have this definition: + +```txt +namespace=foo.bar + +# Documentation of this key +myProperty string default="foo" + +# etc. +myArray[].key1 string +myArray[].key2 int default=0 +``` + +An individual config typically contains a coherent set of settings regarding some topic, such as *logging* or *indexing*. A complete system consists of many instances of many config types. + +### Component view + +Individual components of a system consumes one or more such configs and use their values to influence their behavior. APIs are needed for *requesting* configs and for *accessing* the values of those configs as they are provided. + +*Access* to configs happens through a (Java or C++) class generated from the config definition file. This ensures that any inconsistency between the fields declared in a config type and the expectations of the code accessing it are caught at compile time. The config definition is best viewed as another class with an alternative form of source syntax belonging to the components consuming it. A Maven target is provided for generating such classes from config definition types. + +Components may use two different methods for *requesting* configurations subscription and dependency injection: + +**Subscription:** The component sets up *ConfigSubscriber*, then subscribes to one or more configs. This is the simple approach, there are [other ways of](/en/applications/configapi-dev) getting configs too: + +```java +ConfigSubscriber subscriber = new ConfigSubscriber(); +ConfigHandle handle = subscriber.subscribe(MyConfig.class, "myId"); +if (!subscriber.nextConfig()) throw new RuntimeException("Config timed out."); +if (handle.isChanged()) { + String message = handle.getConfig().myKey(); + // ... consume the rest of this config +} +``` + +**Dependency injection:** The component declares its config dependencies in the constructor and subscriptions are set up on its behalf. When changed configs are available a new instance of the component is created. The advantage of this method is that configs are immutable throughout the lifetime of the component such that no thread coordination is required. This method is currently only available in Java using the [Container](/en/applications/containers). + +```java +public MyComponent(MyConfig config) { + String myKey = config.myKey(); + // ... consume the rest of this config +} +``` + +For unit testing, [configs can be created with Builders](/en/applications/configapi-dev#unit-testing), submitted directly to components. + +### Delivery mechanism + +The config delivery mechanism is responsible for ensuring that a new config instance is delivered to subscribing components, each time there is a change to the system model causing that config instance to change. A config subscription is identified by two parameters, the *config definition name and namespace* and the [config id](/en/applications/configapi-dev#config-id) used to identify the particular component instance making the subscription. + +The in-process config library will forward these subscription requests to a node local [config proxy](/en/operations/self-managed/config-proxy), which provides caching and fan-in from processes to node. The proxy in turn issues these subscriptions to a node in the configuration server cluster, each of which hosts a copy of the system model and resolves config requests by querying the system model. + +To provide config server failover, the config subscriptions are implemented as long-timeout gets, which are immediately resent when they time out, but conceptually this is best understood as push subscriptions: + + +![](/assets/img/config-delivery.svg) + + +As configs are not stored as files locally on the nodes, there is no possibility of inconsistencies due to local edits, or of nodes coming out of maintenance with a stale configuration. As configuration changes are pushed as soon as the config server cluster allows, time inconsistencies during reconfigurations are minimized, although not avoided as there is no global transaction. + +Application code and config is generally pulled from the config server - it is however possible to use the [url](/en/reference/applications/config-files#url) config type to refer to any resource to download to nodes. + +### Bootstrapping + +Each Vespa node runs a [config-sentinel](/en/operations/self-managed/config-sentinel) process which start and maintains services run on a node. + +### System upgrades + +The configuration server will up/downgrade between config versions on the fly on minor upgrades which causes discrepancies between the config definitions requested from those produced by the configuration model. Major upgrades, which involve incompatible changes to the configuration protocol or the system model, require a [procedure](/en/operations/self-managed/config-proxy). + +## Notes + +Find more information for using the Vespa config API in the [reference doc](/en/applications/configapi-dev). + +Vespa Configuration makes the following assumptions about the nodes using it: + +- All nodes have the software packages needed to run the configuration system and any services which will be configured to run on the node. This usually means that all nodes have the same software, although this is not a requirement +- All nodes have [VESPA_CONFIGSERVERS](/en/operations/self-managed/files-processes-and-ports#environment-variables) set +- All nodes know their fully qualified domain name + +Reading this document is not necessary in order to use Vespa or to develop Java components for the Vespa container - for this purpose, refer to [Configuring components](/en/applications/configuring-components). + +## Further reads + +- [Configuration server operations](/en/operations/self-managed/configuration-server) is a good resource for troubleshooting. +- Refer to the [bundle plugin](/en/applications/bundles#maven-bundle-plugin) for how to build an application package with Java components. +- During development on a local instance it can be handy to just wipe the state completely and start over: + + + + [Delete all config server state](/en/operations/self-managed/configuration-server#zookeeper-recovery) on all config servers + + + + Run [vespa-remove-index](/en/reference/operations/self-managed/tools#vespa-remove-index) to wipe content nodes + + + diff --git a/en/applications/configapi-dev.html b/en/applications/configapi-dev.html deleted file mode 100644 index 397461b28e..0000000000 --- a/en/applications/configapi-dev.html +++ /dev/null @@ -1,581 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Cloud Config API" -redirect_from: -- /en/cloudconfig/configapi-dev.html -- /en/cloudconfig/configapi-dev-java.html -- /en/cloudconfig/configapi-dev-java.html -- /en/contributing/configapi-dev.html -- /en/contributing/configapi-dev-java.html ---- - -

    -This document describes how to use the C++ and Java versions of the -Cloud config API (the 'config API'). This API is used internally in Vespa, and reading -this document is not necessary in order to use Vespa or to develop -Java components for the Vespa container. For this purpose, please refer to -Configuring components instead. -

    -Throughout this document, we will use as example an application serving up a configurable message. -

    - - -

    Creating a Config Definition

    -

    -The first thing to do when deciding to use the config API is to define -the config you want to use in your application. -This is described in the -configuration file reference. -Here we will use the definition motd.def -from the complete example at the end of the document: -

    -namespace=myproject
    -
    -message string default="NO MESSAGE"
    -port int default=1337
    -
    - - - -

    Generating Source Code and Accessing Config in Code

    -

    -Before you can access config in your program -you will need to generate source code for the config definition. -Simple steps for how you can generate API code and use the API are provided for -Java. -See also javadoc)

    - -

    We also recommend that you read the general guidelines -for examples of advanced usage and recommendations for how to use the API.

    - - -

    Config ID

    -

    -The config id specified when requesting config is essentially an -identifier of the component requesting config. The config server -contains a config object model, which maps a request for a given -config name and config id to the correct configproducer instance, -which will merge default values from the config definition with config -from the object model and config set in -services.xml to produce the final config instance. -

    -The config id is given to a service via the VESPA_CONFIG_ID environment variable. -The config sentinel -sets the environment variable to the id given by the config model. -This id should then be used by the service to subscribe for config. -If you are running multiple services, -each of them will be assigned a unique config id for that service, -and a service should not subscribe using any config id other than its own. -

    -If you need to get config for a services that is not part of the model -(i.e. it is not specified in the services.xml), but that you want -to specify values for in services.xml, use the config id client. -

    - - - -

    Schema Compatibility Rules

    -

    -A schema incompatibility occurs if the config class -(for example MotdConfig in the C++ and Java sections above) -was built from a different def-file than the one the server is seeing -and using to serve config. -Some such incompatibilities are automatically handled by the config system, -others lead to error. -This is useful to know during development/testing of a config schema. -

    -Let S denote a config definition called motd which the server is using, -and C denote a config definition also called motd which the client is using, -i.e. the one that created MotdConfig used when subscribing. -The following is the system's behavior: - - - - - - - - - - -
    Compatible Changes -

    These schema mismatches are handled automatically by the configserver:

    -
      -
    • C is missing a config value that S has: - The server will omit that value from the response.
    • -
    • C has an additional config value with a default value: - The server will include that value in the response.
    • -
    • C and S both have a config value, but the default values differ: - The server will use C's default value.
    • -
    -
    Incompatible Changes -

    - These schema mismatches are not handled by the config server, - and will typically lead to error in the subscription API because of missing values - (though in principle some consumers of config may tolerate them): -

    -
      -
    • C has an additional config value without a default value: - The server will not include anything for that value.
    • -
    • C has the type of a config value changed, - for example from string to int: The server will print an error message, - and not include anything for that value. - The user must use an entirely new name for the config - if such a change must be made.
    • -
    -
    -

    -As with any data schema, it is wise to be conservative about changing it -if the system will have new versions in the future. -For a def schema, -removing a config value constitutes a semantic change -that may lead to problems when an older version of some config subscriber asks for config. -In large deployments, the risk associated with this increases, -because of the higher cost of a full restart of everything. -

    -Consequently, one should prefer creating a new config name, -to removing a config value from a schema. -

    - - - -

    Creating a Deployable Application Package

    -

    -The application package consists of the following files: -

    -app/services.xml
    -app/hosts.xml
    -
    -The services file contains the services that is handled by the config model plugin. -The hosts file contains: -
    {% highlight xml %}
    -
    -  
    -    node0
    -  
    -
    -{% endhighlight %}
    - - - -

    Setting Up a Running System

    -

    -To get a running system, first install the cloudconfig package, -start the config server, then deploy the application: -Prepare the application: -

    -$ vespa prepare /path/to/app/folder
    -
    -Activate the application: -
    -$ vespa activate /path/to/app/folder
    -
    -

    -Then, start vespa. This will start the application and pass it its config id -via the VESPA_CONFIG_ID environment variable. -

    - - - -

    Advanced Usage of the Config API

    -

    -For a simple application, having only 1 config may suffice. -In a typical server application, however, -the number of config settings can become large. -Therefore, we encourage that you split the config settings into multiple logical classes. -This section covers how you can use a ConfigSubscriber to subscribe to multiple configs -and how you should group configs based on their dependencies. -Configs can either be: -

      -
    • Independent static configs
    • -
    • Dependent static configs
    • -
    • Dependent dynamic configs
    • -
    -

    -We will give a few examples of how you can cope with these different scenarios. -The code examples are given in a pseudo format common to C++ and Java, -but they should be easy to convert to their language specific equivalents. -

    - - -

    Independent Static Configs

    -

    -Independent configs means that it does not matter if one of them is -updated independently of the other. -In this case, you might as well use one ConfigSubscriber for each of the configs, -but it might become tedious to check all of them. -Therefore, the recommended way is to manage all of these configs using one ConfigSubscriber. -In this setup, it is also typical to split the subscription phase -from the config check/retrieval part. The subscribing part: - - - - - - - - - - -
    C++ -
    {% highlight xml %}
    -ConfigSubscriber subscriber;
    -ConfigHandle::UP fooHandle = subscriber.subscribe(...);
    -ConfigHandle::UP barHandle = subscriber.subscribe(...);
    -ConfigHandle::UP bazHandle = subscriber.subscribe(...);
    -{% endhighlight %}
    -
    Java -
    {% highlight xml %}
    -ConfigSubscriber subscriber;
    -ConfigHandle fooHandle = subscriber.subscribe(FooConfig.class, ...);
    -ConfigHandle barHandle = subscriber.subscribe(BarConfig.class, ...);
    -ConfigHandle bazHandle = subscriber.subscribe(BazConfig.class, ...);
    -{% endhighlight %}
    -
    -And the retrieval part: -

    -if (subscriber.nextConfig()) {
    -    if (fooHandle->isChanged()) {
    -        // Reconfigure foo
    -    }
    -    if (barHandle->isChanged()) {
    -        // Reconfigure bar
    -    }
    -    if (bazHandle->isChanged()) {
    -        // Reconfigure baz
    -    }
    -}
    -
    -

    -This allows you to perform the config fetch part either in its own -thread or as part of some other event thread in your application. -

    - - -

    Dependent Static Configs

    -

    -Dependent configs means that one of your configs depends on the value in another config. -The most common is that you have one config which contains the config id -to use when subscribing to the second config. -In addition, your system may need that the configs are updated -to the same generation. -

    - {% include note.html content="A generation is a monotonically increasing number which is increased - each time an application is deployed with vespa deploy. - Certain applications may require that all configs are of the same generation to ensure consistency, - especially container-like applications. - All configs subscribed to by a ConfigSubscriber are guaranteed to be of the same generation."%} -

    -The configs are static in the sense that the config id used does not change. -The recommended way to approach this is to use a two phase setup, -where you fetch the initial configs in the first phase, -and then subscribe to both the initial and derived configs -in order to ensure that they are of the same generation. -Assume that the InitialConfig config contains two fields -named derived1 and derived2: - - - - - - - - - - -
    C++ -
    {% highlight xml %}
    -ConfigSubscriber initialSubscriber;
    -ConfigHandle::UP initialHandle = subscriber.subscribe(...);
    -while (!subscriber.nextConfig()); // Ensure that we actually get initial config.
    -std::auto_ptr initialConfig = initialHandle->getConfig();
    -
    -ConfigSubscriber subscriber;
    -... = subscriber.subscribe(...);
    -... = subscriber.subscribe(initialConfig->derived1);
    -... = subscriber.subscribe(initialConfig->derived1);
    -        {% endhighlight %}
    -
    Java -
    {% highlight xml %}
    -ConfigSubscriber initialSubscriber;
    -ConfigHandle initialHandle = subscriber.subscribe(InitialConfig.class, ...);
    -while (!subscriber.nextConfig()); // Ensure that we actually get initial config.
    -InitialConfig initialConfig = initialHandle.getConfig();
    -
    -ConfigSubscriber subscriber;
    -... = subscriber.subscribe(InitialConfig.class, ...);
    -... = subscriber.subscribe(DerivedConfig.class, initialConfig.derived1);
    -... = subscriber.subscribe(DerivedConfig.class, initialConfig.derived1);
    -        {% endhighlight %}
    -
    -

    -You can then check the configs in the same way as for independent static configs, -and be sure that all your configs are of the same generation. -The reason why you need to create a new ConfigSubscriber -is that once you have called nextConfig(), -you cannot add or remove new subscribers. -

    - - -

    Dependent Dynamic Configs

    -

    -Dynamic configs mean that the set of configs that you subscribe for -may change between each deployment. -This is the hardest case to solve, -and how hard it is depends on how many levels of configs you have. -The most common one is to have a set of bootstrap configs, -and another set of configs that may change depending on the bootstrap configs -(typically in an application that has plugins). -To cover this case, you can use a class named ConfigRetriever. -Currently, it is only available in the C++ API. -

    -The ConfigRetriever uses the same mechanisms as the ConfigSubscriber -to ensure that you get a consistent set of configs. -In addition, two more classes called -ConfigKeySet and ConfigSnapshot are added. -The ConfigRetriever takes in a set of configs used to bootstrap the system in its constructor. -This set does not change. -It then provides one method, getConfigs(ConfigKeySet). -The method returns a ConfigSnapshot of the next generation of bootstrap configs or derived configs. -

    -To create the ConfigRetriever, you must first populate a set of bootstrap configs: -

    {% highlight xml %}
    -ConfigKeySet bootstrapKeys;
    -bootstrapKeys.add(configId);
    -bootstrapKeys.add(configId);
    -{% endhighlight %}
    -The bootstrap configs are typically configs that will always be needed by your application. -Once you have defined your set, -you can create the retriever and fetch a ConfigSnapshot of the bootstrap configs: -
    -ConfigRetriever retriever(bootstrapKeys);
    -ConfigSnapshot bootstrapConfigs = retriever.getConfigs();
    -
    -The ConfigSnapshot contains the bootstrap config, -and you may use that to fetch the individual configs. -You need to provide the config id and the type in order for the snapshot to know which config to look for: -
    {% highlight xml %}
    -if (!bootstrapConfigs.empty()) {
    -    std::auto_ptr bootstrapFoo = bootstrapConfigs.getConfig(configId);
    -    std::auto_ptr bootstrapBar = bootstrapConfigs.getConfig(configId);
    -{% endhighlight %}
    -

    -The snapshot returned is empty if the retriever was unable to get the configs. -In that case, you can try calling the same method again. -

    -Once you have the bootstrap configs, you know the config ids for the -other components that you should subscribe for, -and you can define a new key set. -Let's assume that bootstrapFoo contains an array of config ids we should subscribe for. -

    {% highlight java %}
    -ConfigKeySet pluginKeySet;
    -for (size_t i = 0; i < (*bootstrapFoo).pluginConfigId.size; i++) {
    -    pluginKeySet.add((*bootstrapFoo).pluginConfigId[i]);
    -}
    -{% endhighlight %}
    -

    -In this example we know the type of config requested, but this could -be done in another way letting the plugin add keys to the set. -

    -Now that the derived configs have been added to the pluginKeySet, -we can request a snapshot of them: -

    -ConfigSnapshot pluginConfigs = retriever.getConfigs(pluginKeySet);
    -if (!pluginConfigs.empty()) {
    -    // Configure each plugin with a config picked from the snapshot.
    -}
    -
    -

    -And that's it. When calling the method without any key parameters, -the snapshot returned by this method may be empty if the config -could not be fetched within the timeout, -or the generation of configs has changed. -To check if you should call getBootstrapConfigs() again, -you can use the bootstrapRequired() method. -If it returns true, you will have to call getBootstrapConfigs() again, -because the plugin configs have been updated, and you need a new bootstrap generation to match it. -If it returns false, you may call getConfigs() again -to try and get a new generation of plugin configs. -

    -We recommend that you use the retriever API if you have a use case like this. -The alternative is to create your own mechanism using two ConfigSubscriber classes, -but this is not recommended. -

    - - -

    Advice on Config Modelling

    -

    -Regardless of which of these types of configs you have, -it is recommended that you always fetch all the configs you need -before you start configuring your system. -This is because the user may deploy multiple different version of the config -that may cause your components to get conflicting config values. -A common pitfall is to treat dependent configs as independent, -thereby causing inconsistency in your application when a config update -for config A arrives before config B. -The ConfigSubscriber was created to minimize the possibility of making this mistake, -by ensuring that all of the configs comes from the same config reload. -

    -Tip: -Set up your entire tree of configs in one thread to ensure consistency, -and configure your system once all of the configs have arrived. -This also maps best to the ConfigSubscriber, since it is not thread safe. -

    - - -

    The Java config API

    - -

    -Assumption: a def file, -which is the schema for one of your configs, is created and -put in src/main/resources/configdefinitions/. -

    -To generate source code for the def-file, invoke the -config-class-plugin from pom.xml, -in the <build>, <plugins> section: -

    -
    {% highlight xml %}
    -
    -  com.yahoo.vespa
    -  config-class-plugin
    -  ${vespa.version}
    -  
    -    
    -      config-gen
    -      
    -        config-gen
    -      
    -    
    -  
    -
    -{% endhighlight %}
    -

    -The generated classes will be saved to -target/generated-sources/vespa-configgen-plugin, when the -generate-sources phase of the build is executed. -The def-file motd.def -is used in this tutorial, and a class called MotdConfig -was generated (in the package myproject). -It is a subtype of ConfigInstance. -

    -When using only the config system (and not other parts of Vespa or -the JDisc container), pull in that by using this in pom.xml: -

    -
    {% highlight xml %}
    -
    -  com.yahoo.vespa
    -  config
    -  ${vespa.version}
    -  provided
    -
    -{% endhighlight %}
    - - - -

    Subscribing and getting config

    -

    -To retrieve the config in the application, create a ConfigSubscriber. -A ConfigSubscriber is capable of subscribing to one or more configs. -The example shown here uses simplified error handling: -

    -
    {% highlight java %}
    -ConfigSubscriber subscriber = new ConfigSubscriber();
    -ConfigHandle handle = subscriber.subscribe(MotdConfig.class, "motdserver2/0");
    -if (!subscriber.nextConfig()) throw new RuntimeException("Config timed out.");
    -if (handle.isChanged()) {
    -  String message = handle.getConfig().message();
    -  int port = handle.getConfig().port();
    -}
    -{% endhighlight %}
    -

    -Note that isChanged() always will be true after the first call to nextConfig(), -it is included here to illustrate the API. -

    -In many cases one will do this from a thread which loops the -nextConfig() call, and reconfigures your application if -isChanged() is true. -

    -The second parameter to subscribe(), "motdserver2/0", -is the config id. -

    -If one ConfigSubscriber subscribes to multiple configs, -nextConfig() will only return true if the configs are of -the same generation, i.e. they are "in sync". -

    -See the -com.yahoo.config javadoc for details. Example: -

    -
    {% highlight xml %}
    -ConfigSubscriber subscriber = new ConfigSubscriber();
    -ConfigHandle motdHandle = subscriber.subscribe(MotdConfig.class, "motdserver2/0");
    -ConfigHandle anotherHandle = subscriber.subscribe(AnotherConfig.class, "motdserver2/0");
    -if (!subscriber.nextConfig()) throw new RuntimeException("Config timed out.");
    -// We now have a synchronized new generation for these two configs.
    -if (motdHandle.isChanged()) {
    -  String message = motdHandle.getConfig().message();
    -  int port = motdHandle.getConfig().port();
    -}
    -if (anotherHandle.isChanged()) {
    -  String myfield = anotherHandle.getConfig().getMyField();
    -}
    -{% endhighlight %}
    - - - -

    Simplified subscription

    -

    -In cases like the first example above, where you only subscribe to one config, you may also subscribe using the -ConfigSubscriber.SingleSubscriber interface. In this case, you define a configure() -method from the interface, and call a special subscribe(). The method will start a dedicated config -fetcher thread for you. The method will throw an exception in the user thread if initial configuration fails, -and print a warning in the config thread if it fails afterwards. Example: -

    -
    {% highlight xml %}
    -public class MyConfigSubscriber implements ConfigSubscriber.SingleSubscriber {
    -
    -  public MyConfigSubscriber(String configId) {
    -    new ConfigSubscriber().subscribe(this, MotdConfig.class, configId);
    -  }
    -
    -  @Override
    -  public void configure(MotdConfig config) {
    -    // configuration logic here
    -  }
    -}
    -{% endhighlight %}
    -

    -The disadvantage to using this is that one cannot implement custom error handling -or otherwise track config changes. If needed, use the generic method above. -

    - -

    Unit testing config

    -

    -When instantiating a ConfigSubscriber, -one can give it a ConfigSource. -One such source is a ConfigSet. It consists of a set of Builders. -This is an example of instantiating a subscriber using this - -it uses 2 types of config, that were generated from files -app.def and string.def: -

    -
    -ConfigSet myConfigs = new ConfigSet();
    -AppConfig.Builder a0builder = new AppConfig.Builder().message("A message, 0").times(88);
    -AppConfig.Builder a1builder = new AppConfig.Builder().message("A message, 1").times(89);
    -myConfigs.add("app/0", a0builder);
    -myConfigs.add("app/1", a1builder);
    -myConfigs.add("bar", new StringConfig.Builder().stringVal("StringVal"));
    -ConfigSubscriber subscriber = new ConfigSubscriber(myConfigs);
    -
    -

    -To help with unit testing, each config type has a corresponding builder type. -The Builder is mutable whereas the ConfigInstance is not. -Use this to set up config fixtures for unit tests. -The ConfigSubscriber has a reload() method -which is used in tests to force the subscriptions into a new -generation. It emulates a vespa activate operation after -you have updated the ConfigSet. -

    -A full example can be found in - - ConfigSetSubscriptionTest.java. -

    diff --git a/en/applications/configapi-dev.mdx b/en/applications/configapi-dev.mdx new file mode 100644 index 0000000000..2c2863e4d5 --- /dev/null +++ b/en/applications/configapi-dev.mdx @@ -0,0 +1,343 @@ +--- +title: "Cloud Config API" +sidebarTitle: "Java Config API" +description: "This document describes how to use the C++ and Java versions of the Cloud config API (the 'config API'). This API is used internally in Vespa, and reading this document is not necessary in order to use Vespa or to develop Java components for the Vespa container. For this purpose, please refer to [Configuring components](/en/applications/configuring-components) instead." +--- + +Throughout this document, we will use as example an application serving up a configurable message. + +## Creating a Config Definition + +The first thing to do when deciding to use the config API is to define the config you want to use in your application. This is described in the [configuration file reference](/en/reference/applications/config-files). Here we will use the definition `motd.def` from the complete example at the end of the document: + +```text +namespace=myproject + +message string default="NO MESSAGE" +port int default=1337 +``` + +## Generating Source Code and Accessing Config in Code + +Before you can access config in your program you will need to generate source code for the config definition. Simple steps for how you can generate API code and use the API are provided for [Java](/en/applications/configapi-dev#the-java-config-api). See also [javadoc](https://javadoc.io/doc/com.yahoo.vespa/config-lib)) + +We also recommend that you read the [general guidelines](#guidelines) for examples of advanced usage and recommendations for how to use the API. + +## Config ID + +The config id specified when requesting config is essentially an identifier of the component requesting config. The config server contains a config object model, which maps a request for a given config name and config id to the correct configproducer instance, which will merge default values from the config definition with config from the object model and config set in `services.xml` to produce the final config instance. + +The config id is given to a service via the VESPA\_CONFIG\_ID environment variable. The [config sentinel](/en/operations/self-managed/config-sentinel) sets the environment variable to the id given by the config model. This id should then be used by the service to subscribe for config. If you are running multiple services, each of them will be assigned a **unique config id** for that service, and a service should not subscribe using any config id other than its own. + +If you need to get config for a services that is not part of the model (i.e. it is not specified in the services.xml), but that you want to specify values for in services.xml, use the config id `client`. + +## Schema Compatibility Rules + +A schema incompatibility occurs if the config class (for example `MotdConfig` in the C++ and Java sections above) was built from a different def-file than the one the server is seeing and using to serve config. Some such incompatibilities are automatically handled by the config system, others lead to error. This is useful to know during development/testing of a config schema. + +Let *S* denote a config definition called *motd* which the server is using, and *C* denote a config definition also called *motd* which the client is using, i.e. the one that created `MotdConfig` used when subscribing. The following is the system's behavior: + + + + + + + + + + + + + + + + + + +
    Compatible ChangesThese schema mismatches are handled automatically by the configserver:
    - C is missing a config value that S has: The server will omit that value from the response.
    - C has an additional config value with a default value: The server will include that value in the response.
    - C and S both have a config value, but the default values differ: The server will use C's default value.
    Incompatible ChangesThese schema mismatches are not handled by the config server, and will typically lead to error in the subscription API because of missing values (though in principle some consumers of config may tolerate them):
    - C has an additional config value without a default value: The server will not include anything for that value.
    - C has the type of a config value changed, for example from string to int: The server will print an error message, and not include anything for that value. The user must use an entirely new name for the config if such a change must be made.
    + +As with any data schema, it is wise to be conservative about changing it if the system will have new versions in the future. For a `def` schema, removing a config value constitutes a semantic change that may lead to problems when an older version of some config subscriber asks for config. In large deployments, the risk associated with this increases, because of the higher cost of a full restart of everything. + +Consequently, one should prefer creating a new config name, to removing a config value from a schema. + +## Creating a Deployable Application Package + +The application package consists of the following files: + +``` +app/services.xml +app/hosts.xml +``` + +The services file contains the services that is handled by the config model plugin. The hosts file contains: +```xml + + + node0 + + +``` + +## Setting Up a Running System + +To get a running system, first install the cloudconfig package, start the config server, then deploy the application: Prepare the application: + +$ vespa prepare /path/to/app/folder +Activate the application:$ vespa activate /path/to/app/folder + +Then, start vespa. This will start the application and pass it its config id via the VESPA\_CONFIG\_ID environment variable. + +## Advanced Usage of the Config API + +For a simple application, having only 1 config may suffice. In a typical server application, however, the number of config settings can become large. Therefore, we **encourage** that you split the config settings into multiple logical classes. This section covers how you can use a ConfigSubscriber to subscribe to multiple configs and how you should group configs based on their dependencies. Configs can either be: + +- Independent static configs +- Dependent static configs +- Dependent dynamic configs + +We will give a few examples of how you can cope with these different scenarios. The code examples are given in a pseudo format common to C++ and Java, but they should be easy to convert to their language specific equivalents. + +### Independent Static Configs + +Independent configs means that it does not matter if one of them is updated independently of the other. In this case, you might as well use one ConfigSubscriber for each of the configs, but it might become tedious to check all of them. Therefore, the recommended way is to manage all of these configs using one ConfigSubscriber. In this setup, it is also typical to split the subscription phase from the config check/retrieval part. The subscribing part: + +```c++ C ++ +ConfigSubscriber subscriber; +ConfigHandle::UP fooHandle = subscriber.subscribe(...); +ConfigHandle::UP barHandle = subscriber.subscribe(...); +ConfigHandle::UP bazHandle = subscriber.subscribe(...); +``` +```java Java +ConfigSubscriber subscriber; +ConfigHandle fooHandle = subscriber.subscribe(FooConfig.class, ...); +ConfigHandle barHandle = subscriber.subscribe(BarConfig.class, ...); +ConfigHandle bazHandle = subscriber.subscribe(BazConfig.class, ...); +``` + +And the retrieval part: + +``` +if (subscriber.nextConfig()) { + if (fooHandle->isChanged()) { + // Reconfigure foo + } + if (barHandle->isChanged()) { + // Reconfigure bar + } + if (bazHandle->isChanged()) { + // Reconfigure baz + } +} +``` + +This allows you to perform the config fetch part either in its own thread or as part of some other event thread in your application. + +### Dependent Static Configs + +Dependent configs means that one of your configs depends on the value in another config. The most common is that you have one config which contains the config id to use when subscribing to the second config. In addition, your system may need that the configs are updated to the same **generation**. + + +**Note:** + +A generation is a monotonically increasing number which is increased each time an application is deployed with `vespa deploy`. Certain applications may require that all configs are of the same generation to ensure consistency, especially container-like applications. All configs subscribed to by a ConfigSubscriber are guaranteed to be of the same generation. + + +The configs are static in the sense that the config id used does not change. The recommended way to approach this is to use a two phase setup, where you fetch the initial configs in the first phase, and then subscribe to both the initial and derived configs in order to ensure that they are of the same generation. Assume that the InitialConfig config contains two fields named *derived1* and *derived2*: + +```c++ C++ +ConfigSubscriber initialSubscriber; +ConfigHandle::UP initialHandle = subscriber.subscribe(...); +while (!subscriber.nextConfig()); // Ensure that we actually get initial config. +std::auto_ptr initialConfig = initialHandle->getConfig(); + +ConfigSubscriber subscriber; +... = subscriber.subscribe(...); +... = subscriber.subscribe(initialConfig->derived1); +... = subscriber.subscribe(initialConfig->derived1); +``` + +```java Java +ConfigSubscriber initialSubscriber; +ConfigHandle initialHandle = subscriber.subscribe(InitialConfig.class, ...); +while (!subscriber.nextConfig()); // Ensure that we actually get initial config. +InitialConfig initialConfig = initialHandle.getConfig(); + +ConfigSubscriber subscriber; +... = subscriber.subscribe(InitialConfig.class, ...); +... = subscriber.subscribe(DerivedConfig.class, initialConfig.derived1); +... = subscriber.subscribe(DerivedConfig.class, initialConfig.derived1); +``` + +You can then check the configs in the same way as for independent static configs, and be sure that all your configs are of the same generation. The reason why you need to create a new ConfigSubscriber is that **once you have called nextConfig(), you cannot add or remove new subscribers**. + +### Dependent Dynamic Configs + +Dynamic configs mean that the set of configs that you subscribe for may change between each deployment. This is the hardest case to solve, and how hard it is depends on how many levels of configs you have. The most common one is to have a set of bootstrap configs, and another set of configs that may change depending on the bootstrap configs (typically in an application that has plugins). To cover this case, you can use a class named `ConfigRetriever`. Currently, it is **only available in the C++ API**. + +The ConfigRetriever uses the same mechanisms as the ConfigSubscriber to ensure that you get a consistent set of configs. In addition, two more classes called `ConfigKeySet` and `ConfigSnapshot` are added. The ConfigRetriever takes in a set of configs used to bootstrap the system in its constructor. This set does not change. It then provides one method, `getConfigs(ConfigKeySet)`. The method returns a ConfigSnapshot of the next generation of bootstrap configs or derived configs. + +To create the ConfigRetriever, you must first populate a set of bootstrap configs: + +```xml +ConfigKeySet bootstrapKeys; +bootstrapKeys.add(configId); +bootstrapKeys.add(configId); +``` + +The bootstrap configs are typically configs that will always be needed by your application. Once you have defined your set, you can create the retriever and fetch a ConfigSnapshot of the bootstrap configs: + +``` +ConfigRetriever retri ever(bootstrapKeys); +ConfigSnapshot bootstrapConfigs = retriever.getConfigs(); +``` + +The ConfigSnapshot contains the bootstrap config, and you may use that to fetch the individual configs. You need to provide the config id and the type in order for the snapshot to know which config to look for: + +```xml +if (!bootstrapConfigs.empty()) { + std::auto_ptr bootstrapFoo = bootstrapConfigs.getConfig(configId); + std::auto_ptr bootstrapBar = bootstrapConfigs.getConfig(configId); +``` + +The snapshot returned is empty if the retriever was unable to get the configs. In that case, you can try calling the same method again. + +Once you have the bootstrap configs, you know the config ids for the other components that you should subscribe for, and you can define a new key set. Let's assume that bootstrapFoo contains an array of config ids we should subscribe for. + +```java +ConfigKeySet pluginKeySet; +for (size_t i = 0; i < (*bootstrapFoo).pluginConfigId.size; i++) { + pluginKeySet.add((*bootstrapFoo).pluginConfigId[i]); +} +``` + +In this example we know the type of config requested, but this could be done in another way letting the plugin add keys to the set. + +Now that the derived configs have been added to the pluginKeySet, we can request a snapshot of them: + +```java +ConfigSnapshot pluginConfigs = retriever.getConfigs(pluginKeySet); +if (!pluginConfigs.empty()) { + // Configure each plugin with a config picked from the snapshot. +} +``` +And that's it. When calling the method without any key parameters, the snapshot returned by this method may be empty if **the config could not be fetched within the timeout**, or **the generation of configs has changed**. To check if you should call getBootstrapConfigs() again, you can use the `bootstrapRequired()` method. If it returns true, you will have to call getBootstrapConfigs() again, because the plugin configs have been updated, and you need a new bootstrap generation to match it. If it returns false, you may call getConfigs() again to try and get a new generation of plugin configs. + +We recommend that you use the retriever API if you have a use case like this. The alternative is to create your own mechanism using two ConfigSubscriber classes, but this is **not** recommended. + +### Advice on Config Modelling + +Regardless of which of these types of configs you have, it is recommended that you always fetch all the configs you need **before** you start configuring your system. This is because the user may deploy multiple different version of the config that may cause your components to get conflicting config values. A common pitfall is to treat dependent configs as independent, thereby causing inconsistency in your application when a config update for config A arrives before config B. The ConfigSubscriber was created to minimize the possibility of making this mistake, by ensuring that all of the configs comes from the same config reload. + +**Tip:** Set up your entire *tree* of configs in one thread to ensure consistency, and configure your system once all of the configs have arrived. This also maps best to the ConfigSubscriber, since it is not thread safe. + +## The Java config API + +Assumption: a [def file](/en/applications/configapi-dev), which is the schema for one of your configs, is created and put in `src/main/resources/configdefinitions/`. + +To generate source code for the def-file, invoke the `config-class-plugin` from *pom.xml*, in the ``, `` section: + +```xml + + com.yahoo.vespa + config-class-plugin + ${vespa.version} + + + config-gen + + config-gen + + + + +``` + +The generated classes will be saved to `target/generated-sources/vespa-configgen-plugin`, when the `generate-sources` phase of the build is executed. The def-file [`motd.def`](/en/applications/configapi-dev) is used in this tutorial, and a class called `MotdConfig` was generated (in the package `myproject`). It is a subtype of `ConfigInstance`. + +When using only the config system (and not other parts of Vespa or the JDisc container), pull in that by using this in pom.xml: + +```xml + + com.yahoo.vespa + config + ${vespa.version} + provided + +``` + +## Subscribing and getting config + +To retrieve the config in the application, create a `ConfigSubscriber`. A `ConfigSubscriber` is capable of subscribing to one or more configs. The example shown here uses simplified error handling: + +```java +ConfigSubscriber subscriber = new ConfigSubscriber(); +ConfigHandle handle = subscriber.subscribe(MotdConfig.class, "motdserver2/0"); +if (!subscriber.nextConfig()) throw new RuntimeException("Config timed out."); +if (handle.isChanged()) { + String message = handle.getConfig().message(); + int port = handle.getConfig().port(); +} +``` + +Note that `isChanged()` always will be true after the first call to `nextConfig()`, it is included here to illustrate the API. + +In many cases one will do this from a thread which loops the `nextConfig()` call, and reconfigures your application if `isChanged()` is true. + +The second parameter to `subscribe()`, *"motdserver2/0"*, is the [config id](/en/applications/configapi-dev#config-id). + +If one `ConfigSubscriber` subscribes to multiple configs, `nextConfig()` will only return true if the configs are of the same generation, i.e. they are "in sync". + +See the [com.yahoo.config](https://javadoc.io/doc/com.yahoo.vespa/config-lib) javadoc for details. Example: + +```java +ConfigSubscriber subscriber = new ConfigSubscriber(); +ConfigHandle motdHandle = subscriber.subscribe(MotdConfig.class, "motdserver2/0"); +ConfigHandle anotherHandle = subscriber.subscribe(AnotherConfig.class, "motdserver2/0"); +if (!subscriber.nextConfig()) throw new RuntimeException("Config timed out."); +// We now have a synchronized new generation for these two configs. +if (motdHandle.isChanged()) { + String message = motdHandle.getConfig().message(); + int port = motdHandle.getConfig().port(); +} +if (anotherHandle.isChanged()) { + String myfield = anotherHandle.getConfig().getMyField(); +} +``` + +## Simplified subscription + +In cases like the first example above, where you only subscribe to one config, you may also subscribe using the `ConfigSubscriber.SingleSubscriber` interface. In this case, you define a `configure()` method from the interface, and call a special `subscribe()`. The method will start a dedicated config fetcher thread for you. The method will throw an exception in the user thread if initial configuration fails, and print a warning in the config thread if it fails afterwards. Example: + +```java +public class MyConfigSubscriber implements ConfigSubscriber.SingleSubscriber { + + public MyConfigSubscriber(String configId) { + new ConfigSubscriber().subscribe(this, MotdConfig.class, configId); + } + + @Override + public void configure(MotdConfig config) { + // configuration logic here + } +} +``` + +The disadvantage to using this is that one cannot implement custom error handling or otherwise track config changes. If needed, use the generic method above. + +## Unit testing config + +When instantiating a [ConfigSubscriber](https://javadoc.io/doc/com.yahoo.vespa/config/latest/com/yahoo/config/subscription/ConfigSubscriber.html), one can give it a [ConfigSource](https://javadoc.io/doc/com.yahoo.vespa/config/latest/com/yahoo/config/subscription/ConfigSource.html). One such source is a `ConfigSet`. It consists of a set of `Builder`s. This is an example of instantiating a subscriber using this
    - it uses 2 types of config, that were generated from files `app.def` and `string.def`: + +```java +ConfigSet myConfigs = new ConfigSet(); +AppConfig.Builder a0builder = new AppConfig.Builder().message("A message, 0").times(88); +AppConfig.Builder a1builder = new AppConfig.Builder().message("A message, 1").times(89); +myConfigs.add("app/0", a0builder); +myConfigs.add("app/1", a1builder); +myConfigs.add("bar", new StringConfig.Builder().stringVal("StringVal")); +ConfigSubscriber subscriber = new ConfigSubscriber(myConfigs); +``` + +To help with unit testing, each config type has a corresponding builder type. The `Builder` is mutable whereas the `ConfigInstance` is not. Use this to set up config fixtures for unit tests. The `ConfigSubscriber` has a `reload()` method which is used in tests to force the subscriptions into a new generation. It emulates a `vespa activate` operation after you have updated the `ConfigSet`. + +A full example can be found in [ConfigSetSubscriptionTest.java](https://github.com/vespa-engine/vespa/blob/master/config/src/test/java/com/yahoo/config/subscription/ConfigSetSubscriptionTest.java). diff --git a/en/applications/configuring-components.html b/en/applications/configuring-components.html deleted file mode 100644 index 8ecdd844f0..0000000000 --- a/en/applications/configuring-components.html +++ /dev/null @@ -1,223 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Configuring Java components" -redirect_from: -- /en/configuring-components.html ---- - -

    -Any Java component might require some sort of configuration, -be it simple strings or integers, or more complex structures. -Because of all the boilerplate code that commonly goes into classes to hold such configuration, -this often degenerates into a collection of key-value string pairs -(e.g. javax.servlet.FilterConfig). -To avoid this, Vespa has custom, type-safe configuration to all Container components. -Get started with the Developer Guide, -try the -album-recommendation-java sample application. -

    -Configurable components in short: -

    -
      -
    • Create a config definition file
    • -
    • Use the Vespa bundle plugin - to generate a config class from the definition
    • -
    • Inject config objects in the application code
    • -
    -

    - The application code is interfacing with config through the generated code — code and config is always in sync. - This configuration should be used for all state which is assumed to stay constant - for the lifetime of the component instance. - Use deploy to push and activate code and config changes. -

    - - - -

    Config definition

    -

    - Write a config definition - file and place it in the application's src/main/resources/configdefinitions/ directory, - e.g. src/main/resources/configdefinitions/my-component.def: -

    -
    -package=com.mydomain.mypackage
    -
    -myCode     int     default=42
    -myMessage  string  default=""
    -
    - - - -

    Generating config classes

    -

    - Generating config classes is done by the bundle plugin: -

    -
    -$ mvn generate-resources
    -
    -

    - The generated the config classes are written to target/generated-sources/vespa-configgen-plugin/. - In the above example, the config definition file was named my-component.def - and its package declaration is com.mydomain.mypackage. - The full name of the generated java class will be com.mydomain.mypackage.MyComponentConfig -

    - It is a good idea to generate the config classes first, - then resolve dependencies and compile in the IDE. -

    - - - -

    Using config in code

    -

    - The generated config class is now available for the component through - constructor injection, - which means that the component can declare the generated class as one of its constructor arguments: -

    -
    -package com.mydomain.mypackage;
    -
    -public class MyComponent {
    -
    -    private final int code;
    -    private final String message;
    -
    -    @Inject
    -    public MyComponent(MyComponentConfig config) {
    -        code = config.myCode();
    -        message = config.myMessage();
    -    }
    -}
    -
    -

    - The Container will create and inject the config instance. - To override the default values of the config, - specify - values in src/main/application/services.xml, like: -

    -
    -<container version="1.0">
    -    <component id="com.mydomain.mypackage.MyComponent">
    -        <config name="com.mydomain.mypackage.my-component">
    -            <myCode>132</myCode>
    -            <myMessage>Hello, World!</myMessage>
    -        </config>
    -    </component>
    -</container>
    -
    -

    - and the deployed instance of MyComponent is constructed using a - corresponding instance of MyComponentConfig. -

    - - - -

    Unit testing configurable components

    -

    -The generated config class provides a builder API -that makes it easy to create config objects for unit testing. -Example that sets up a unit test for the MyComponent class from the example above: -

    -
    -import static com.mydomain.mypackage.MyComponentConfig.*;
    -
    -public class MyComponentTest {
    -
    -    @Test
    -    public void requireThatMyComponentGetsConfig() {
    -        MyComponentConfig config = new MyComponentConfig.Builder()
    -                                           .myCode(668)
    -                                           .myMessage("Neighbour of the beast")
    -                                           .build();
    -        MyComponent component = new MyComponent(config);
    -        …
    -   }
    -}
    -
    -

    - The config class used here is simple — see a separate example of - building a complex configuration object. -

    - - - -

    Adding files to the component configuration

    - -

    - This section describes what to do if the component needs larger configuration objects that are stored in files, - e.g. machine-learned models, automata or large tables. - Before proceeding, take a look at how to create - provider components — - instead of integrating large objects into e.g. a searcher or processor, it might be better to - split the resource-demanding part of the component's configuration into a separate provider component. - The procedure described below can be applied to any component type. -

    - -

    - Files can be transferred using either - file distribution or URL download. - File distribution is used when the files are added to the application package. - If for some reason this is not convenient, e.g. due to size, - origin of file or update frequency, Vespa can download the file and make it available for the component. - Both types are set up in the config definition file. - File distribution uses the path config type, and URL downloading the url type. - You can also use the model type for machine-learned models that can be referenced by both - model-id, used on Vespa Cloud, and url/path, used on self-hosted deployments. - See the config file reference for details. -

    - -

    - In the following example we will show the usage of all three types. - Assume this config definition, named my-component.def: -

    -
    -package=com.mydomain.mypackage
    -
    -myFile path
    -myUrl url
    -myModel model
    -
    -

    - The file must reside in the application package, and the path (relative to - the application package root) must be given in the component's configuration in services.xml: -

    -
    -<container version="1.0">
    -    <component id="com.mydomain.mypackage.MyComponent">
    -        <config name="com.mydomain.mypackage.my-component">
    -            <myFile>my-files/my-file.txt</myFile>
    -            <myUrl>https://docs.vespa.ai/en/reference/query-api-reference.html</myUrl>
    -            <myModel model-id="id-provided-by-Vespa-Cloud" url/path="as-above"/>
    -        </config>
    -    </component>
    -</container>
    -
    -

    An example component that uses these files:

    -
    -package com.mydomain.mypackage;
    -import java.io.File;
    -
    -public class MyComponent {
    -    private final File fileFromFileDistribution;
    -    private final File fileFromUrlDownload;
    -
    -    public MyComponent(MyComponentConfig config) {
    -        pathFromFileDistribution = config.myFile();
    -        fileFromUrlDownload      = config.myUrl();
    -        modelFilePath            = config.myModel();
    -    }
    -}
    -
    -

    - The myFile() and myModel() getter returns a java.nio.Path object, - while the myUrl() getter returns a java.io.File object. - The container framework guarantees that these files are fully present at the given location before the component - constructor is invoked, so they can always be accessed right away. -

    -

    - When the client asks for config that uses the url or model config - type with a URL, the content will be downloaded and cached on the nodes that need it. If - you want to change the content, the application package needs to be updated with a new URL - for the changed content and the application deployed, - otherwise the cached content will still be used. This avoids unintended changes to the - application if the content of a URL changes. -

    diff --git a/en/applications/configuring-components.mdx b/en/applications/configuring-components.mdx new file mode 100644 index 0000000000..11f6cf4534 --- /dev/null +++ b/en/applications/configuring-components.mdx @@ -0,0 +1,138 @@ +--- +title: "Configuring Java components" +description: "Any Java component might require some sort of configuration, be it simple strings or integers, or more complex structures. Because of all the boilerplate code that commonly goes into classes to hold such configuration, this often degenerates into a collection of key-value string pairs (e.g. [javax.servlet.FilterConfig](https://docs.oracle.com/javaee/6/api/javax/servlet/FilterConfig)). To avoid this, Vespa has custom, type-safe configuration to all [Container](/en/applications/containers) components. Get started with the [Developer Guide](/en/applications/developer-guide), try the [album-recommendation-java](https://github.com/vespa-engine/sample-apps/tree/master/album-recommendation-java) sample application." +--- + +Configurable components in short: + +- Create a [config definition](/en/reference/applications/config-files#config-definition-files) file +- Use the Vespa [bundle plugin](/en/applications/bundles#maven-bundle-plugin) to generate a config class from the definition +- Inject config objects in the application code + +The application code is interfacing with config through the generated code — code and config is always in sync. This configuration should be used for all state which is assumed to stay constant for the *lifetime of the component instance*. Use [deploy](/en/basics/applications) to push and activate code and config changes. + +## Config definition + +Write a [config definition](/en/reference/applications/config-files#config-definition-files) file and place it in the application's `src/main/resources/configdefinitions/` directory, e.g. `src/main/resources/configdefinitions/my-component.def`: + +```text +package=com.mydomain.mypackage + +myCode int default=42 +myMessage string default="" +``` + +## Generating config classes + +Generating config classes is done by the *bundle plugin*: + +```bash +$ mvn generate-resources +``` + +The generated the config classes are written to `target/generated-sources/vespa-configgen-plugin/`. In the above example, the config definition file was named *my-component.def* and its package declaration is *com.mydomain.mypackage*. The full name of the generated java class will be *com.mydomain.mypackage.MyComponentConfig* + +It is a good idea to generate the config classes first, *then* resolve dependencies and compile in the IDE. + +## Using config in code + +The generated config class is now available for the component through [constructor injection](/en/applications/dependency-injection), which means that the component can declare the generated class as one of its constructor arguments: + +```java +package com.mydomain.mypackage; +public class MyComponent { + private final int code; + private final String message; + @Inject + public MyComponent(MyComponentConfig config) { + code = config.myCode(); + message = config.myMessage(); + } +} +``` + +The Container will create and inject the config instance. To override the default values of the config, [specify](/en/reference/applications/config-files#generic-configuration-in-services-xml) values in `src/main/application/services.xml`, like: + +```xml + + + + 132 + Hello, World! + + + +``` + +and the deployed instance of `MyComponent` is constructed using a corresponding instance of `MyComponentConfig`. + +## Unit testing configurable components + +The generated config class provides a builder API that makes it easy to create config objects for unit testing. Example that sets up a unit test for the `MyComponent` class from the example above: + +```java +import static com.mydomain.mypackage.MyComponentConfig.*; +public class MyComponentTest { + @Test + public void requireThatMyComponentGetsConfig() { + MyComponentConfig config = new MyComponentConfig.Builder() + .myCode(668) + .myMessage("Neighbour of the beast") + .build(); + MyComponent component = new MyComponent(config); + … + } +} +``` + +The config class used here is simple — see a separate example of [building a complex configuration object](/en/applications/unit-testing#unit-testing-configurable-components). + +## Adding files to the component configuration + +This section describes what to do if the component needs larger configuration objects that are stored in files, e.g. machine-learned models, [automata](/en/reference/operations/tools#vespa-makefsa) or large tables. Before proceeding, take a look at how to create [provider components](/en/applications/dependency-injection#special-components) — instead of integrating large objects into e.g. a searcher or processor, it might be better to split the resource-demanding part of the component's configuration into a separate provider component. The procedure described below can be applied to any component type. + +Files can be transferred using either [file distribution](/en/applications/deployment#file-distribution) or URL download. File distribution is used when the files are added to the application package. If for some reason this is not convenient, e.g. due to size, origin of file or update frequency, Vespa can download the file and make it available for the component. Both types are set up in the config definition file. File distribution uses the `path` config type, and URL downloading the `url` type. You can also use the `model` type for machine-learned models that can be referenced by both model-id, used on Vespa Cloud, and url/path, used on self-hosted deployments. See [the config file reference](/en/reference/applications/config-files) for details. + +In the following example we will show the usage of all three types. Assume this config definition, named `my-component.def`: + +```java +package=com.mydomain.mypackage + +myFile path +myUrl url +myModel model +``` + +The file must reside in the application package, and the path (relative to the application package root) must be given in the component's configuration in `services.xml`: + +```xml + + + + my-files/my-file.txt + /en/reference/query-api-reference.html + + + + +``` + +An example component that uses these files: + +```java +package com.mydomain.mypackage; +import java.io.File; +public class MyComponent { + private final File fileFromFileDistribution; + private final File fileFromUrlDownload; + public MyComponent(MyComponentConfig config) { + pathFromFileDistribution = config.myFile(); + fileFromUrlDownload = config.myUrl(); + modelFilePath = config.myModel(); + } +} +``` + +The `myFile()` and `myModel()` getter returns a `java.nio.Path` object, while the `myUrl()` getter returns a `java.io.File` object. The container framework guarantees that these files are fully present at the given location before the component constructor is invoked, so they can always be accessed right away. + +When the client asks for config that uses the `url` or `model` config type with a URL, the content will be downloaded and cached on the nodes that need it. If you want to change the content, the application package needs to be updated with a new URL for the changed content and the application [deployed](/en/basics/applications), otherwise the cached content will still be used. This avoids unintended changes to the application if the content of a URL changes. diff --git a/en/applications/containers.html b/en/applications/containers.html deleted file mode 100644 index 001aa22a3b..0000000000 --- a/en/applications/containers.html +++ /dev/null @@ -1,128 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Container clusters" -redirect_from: -- /en/jdisc/index.html ---- - -

    Vespa's Java container - JDisc, hosts all application components as well as the stateless logic of Vespa itself. -Which particular components are hosted by a container cluster is configured in services.xml. The main features of -JDIsc are: -

      -
    • HTTP serving out of the box from an embedded Jetty server, - and support for plugging in other transport mechanisms.
    • -
    • Integration with the config system of Vespa which allows components to - receive up-to-date config - (by constructor injection) resulting from application deployment.
    • -
    • Dependency injection based on Guice - (Felix), but extended for configs and component collections.
    • -
    • A component model based on OSGi which allows - component to be (re)deployed to running servers, and to control which APIs they expose to others.
    • -
    • The features above combine to allow application package changes (changes to components, configuration or data) - to be applied by Vespa without disrupting request serving nor requiring restarts.
    • -
    • Standard component types exists for - - Application components can be of any other type as well and do not need to reference any Vespa API to - be loaded and managed by the container. -
    • -
    • A general chain composition mechanism for components.
    • -
    - - - -

    Developing Components

    - - - - -

    Reference documentation

    - - - - - diff --git a/en/applications/containers.mdx b/en/applications/containers.mdx new file mode 100644 index 0000000000..1ecad7ef1c --- /dev/null +++ b/en/applications/containers.mdx @@ -0,0 +1,50 @@ +--- +title: "Container clusters" +description: "Vespa's Java container - JDisc, hosts all application components as well as the stateless logic of Vespa itself." +--- + +Which particular components are hosted by a container cluster is configured in services.xml. The main features of JDIsc are: + +- HTTP serving out of the box from an embedded Jetty server, and support for plugging in other transport mechanisms. +- Integration with the config system of Vespa which allows components to [receive up-to-date config](/en/applications/configuring-components) (by constructor injection) resulting from application deployment. +- [Dependency injection based on Guice](/en/applications/dependency-injection) (Felix), but extended for configs and component collections. +- A component model based on [OSGi](/en/applications/bundles) which allows component to be (re)deployed to running servers, and to control which APIs they expose to others. +- The features above combine to allow application package changes (changes to components, configuration or data) to be applied by Vespa without disrupting request serving nor requiring restarts. +- Standard component types exists for: + - [general request handling](/en/applications/request-handlers) + - [chained request-response processing](/en/applications/processing) + - [processing document writes](/en/applications/document-processors) + - [intercepting queries and results](/en/applications/searchers) + - [rendering responses](/en/applications/result-renderers) + + Application components can be of any other type as well and do not need to reference any Vespa API to be loaded and managed by the container. +- A general [chain composition](/en/applications/chaining) mechanism for components. + +## Developing Components + +- The JDisc container provides a framework for processing requests and responses, named *Processing* - its building blocks are: + - [Chains](/en/applications/chaining) of other components that are to be executed serially, with each providing some service or transform + - [Processors](/en/applications/processing) that change the request and / or the response. They may also make multiple forward requests, in series or parallel, or manufacture the response content themselves + - [Renderers](/en/applications/processing#response-rendering) that are used to serialize a Processor's response before returning it to a client +- Application Lifecycle and unit testing: + - [Configuring components](/en/applications/configuring-components) with custom configuration + - [Component injection](/en/applications/dependency-injection) allows components to access other application components + - Learn how to [build OSGi bundles](/en/applications/bundles) and how to [troubleshoot](/en/applications/bundles#troubleshooting) classloading issues + - Using [Libraries for Pluggable Frameworks](/en/applications/pluggable-frameworks) from a component may result in class loading issues that require extra setup in the application + - [Unit testing configurable components](/en/applications/unit-testing#unit-testing-configurable-components) +- Handlers and filters: + - [Http servers and security filters](/en/applications/http-servers-and-filters) for incoming connections on HTTP and HTTPS + - [Request handlers](/en/applications/request-handlers) to process incoming requests and generate responses +- Searchers and Document Processors: + - [Searcher](/en/applications/searchers) and [search result renderer](/en/applications/result-renderers) development + - [Document processing](/en/applications/document-processors) + +## Reference documentation + +- [services.xml](/en/reference/applications/services/container) + +## Other related documents + +- [Designing RESTful web services](/en/applications/web-services) as Vespa Components +- [healthchecks](/en/reference/operations/health-checks) - using the Container with a VIP +- [Vespa Component Reference](/en/reference/applications/components): The Container's request processing lifecycle diff --git a/en/applications/dependency-injection.html b/en/applications/dependency-injection.html deleted file mode 100644 index 08e78b9be5..0000000000 --- a/en/applications/dependency-injection.html +++ /dev/null @@ -1,213 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Dependency injection" -redirect_from: -- /en/jdisc/injecting-components.html ---- - - - -

    -The Container (a.k.a. JDisc container) implements a dependency injection framework that allows -components to declare arbitrary dependencies on configuration and other components in the application. -This document explains how to write a container component that depends on another component. -See the reference -for a list of injectable components. -

    -The container relies on auto-injection instead of Guice modules. -All components declared in the container cluster are available for injection, -and the dependent component only needs to declare the dependency as a constructor parameter. -In general, dependency injection involves at least three elements: -

    -
      -
    • a dependent consumer,
    • -
    • a declaration of a component's dependencies,
    • -
    • an injector that creates instances of classes that implement a given dependency on request.
    • -
    -

    Notes:

    -
      -
    • - The dependent object describes what software component it depends on to do its work. - The injector decides what concrete classes satisfy the requirements of the dependent object, - and provides them to the dependent -
    • - The Container encapsulates the injector, and the consumer and all its - dependencies are considered to be components. -
    • - The Container only supports constructor injection - (i.e. all dependencies must be declared in a component's constructor). -
    • - Circular dependencies is not supported. -
    • -
    -

    -Refer to the -multiple-bundles sample app for a practical example. -

    - - - -

    Depending on another component

    -

    -A component that depends on another is considered to be a consumer. -A component's dependencies is whatever its @Inject-annotated -constructor declares as arguments. E.g. the component: -

    -
    -package com.yahoo.example;
    -import com.yahoo.component.annotation.Inject;
    -
    -public class MyComponent {
    -
    -    private final MyDependency dependency;
    -
    -    @Inject
    -    public MyComponent(MyDependency dependency) {
    -        this.dependency = dependency;
    -    }
    -}  
    -
    -

    -has a dependency on the class com.yahoo.example.MyDependency. -To deploy MyComponent, register MyDependency in services.xml: -

    -
    -<container version="1.0">
    -    <component id="com.yahoo.example.MyComponent" />
    -    <component id="com.yahoo.example.MyDependency" />
    -</container>
    -
    -

    -Upon deployment, the Container will first instantiate MyDependency, -and then pass that instance to the constructor of MyComponent. -Multiple consumers can take the same dependency. -One can also inject configuration to components. -

    -{% include note.html content="A component will be reconstructed only when one of its dependencies, -configuration, or its class changes - -all which only occurs when you re-deploy your application package. -Reconstruction is transitive; if component A depends on component B, -and component B depends on component C, -then a reconfiguration of component B causes a reconfiguration of A, but not of C. -Reconfiguration of C causes a reconstruction of both A and B."%} - - -

    Extending components

    -

    - When injecting two components when one extends the other, - the dependency injection code does not know which of the two to use as the argument for the parent class. - To resolve this, inject a ComponentRegistry (see below), and look up its entries, - like getComponent(XXX.class.getName()). -

    - - -

    Specify the bundle

    -

    -The example above assumes the bundle name can be deducted from the class name. -This is not always the case, and you will get class loading problems like: -

    -
    -Caused by: java.lang.IllegalArgumentException: Could not create a component with id
    -'com.yahoo.example.My'.
    -Tried to load class directly, since no bundle was found for spec:
    -com.yahoo.example.Dependency
    -
    -

    To remedy, specify the jar file (i.e. bundle) with the component:

    -
    -<container version="1.0">
    -    <component id="com.yahoo.example.MyDependency" bundle="the name in <artifactId> in your pom.xml" />
    -</container>
    -
    - - - -

    Depending on all components of a specific type

    -

    -Consider the use-case where a component chooses between various strategies, -and each strategy is implemented as a separate component. -Since the number and type of strategies is unknown when implementing the consumer, -it is impossible to make a constructor that lists all of them. -This is where the ComponentRegistry comes into play. E.g. the following component: -

    -
    -package com.yahoo.example;
    -
    -public class MyComponent {
    -
    -    private final ComponentRegistry<Strategy> strategies;
    -
    -    @Inject
    -    public MyComponent(ComponentRegistry<Strategy> strategies) {
    -        this.strategies = strategies;
    -    }
    -}
    -
    -

    -declares a dependency on the set of all components registered in services.xml -that are instances of the class Strategy (including subclasses). -The ComponentRegistry class provides accessors for components based -on their component id. -

    - - - -

    Special Components

    -

    -There are cases where a component cannot be directly injected to its consumers - example: -

    -
      -
    • The component must be instantiated via a factory method instead of its constructor
    • -
    • Each consumer must have a unique instance of the dependency class
    • -
    • The component uses native resources that must be cleaned up - when the component goes out of scope
    • -
    -

    - For these situations, JDisc supports injection, and optional deconstruction, - via its Provider interface: -

    -
    -public interface Provider<T> {
    -    T get();
    -    void deconstruct();
    -}
    -
    -

    -get() is called by JDisc each time it needs to instantiate the specific component type. -deconstruct() is only called after reconfiguring the system with a new application, -where the current provider instance is either removed or replaced due to modified dependencies. -

    -Following the earlier example, declare a provider for the MyDependency class, -that returns a new instance for each consumer: -

    -
    -package com.yahoo.example;
    -import com.yahoo.container.di.componentgraph.Provider;
    -
    -public class MyDependencyProvider implements Provider<MyDependency> {
    -
    -    @Override
    -    public MyDependency get() {
    -        return new MyDependency();
    -    }
    -
    -    @Override
    -    public void deconstruct() { }
    -}
    -
    -

    - Using this provider, services.xml has two instances of MyComponent, - each getting a unique instance of MyDependency: -

    -
    -<container version="1.0">
    -    <component id="com.yahoo.example.MyDependencyProvider" />
    -    <component id="my-component-1" class="com.yahoo.example.MyComponent" />
    -    <component id="my-component-2" class="com.yahoo.example.MyComponent" />
    -</container>
    -
    -

    -Upon deployment, the Container will first instantiate MyDependencyProvider, -and then invoke MyDependencyProvider.get() for each instantiation of MyComponent. -

    -A provider can declare constructor dependencies, just like any other component. -

    diff --git a/en/applications/dependency-injection.mdx b/en/applications/dependency-injection.mdx new file mode 100644 index 0000000000..9107dd1b76 --- /dev/null +++ b/en/applications/dependency-injection.mdx @@ -0,0 +1,140 @@ +--- +title: "Dependency injection" +description: "The Container (a.k.a. JDisc container) implements a dependency injection framework that allows components to declare arbitrary dependencies on configuration and other components in the application. This document explains how to write a container component that depends on another component. See the [reference](/en/reference/applications/components#injectable-components) for a list of injectable components." +--- + +The container relies on auto-injection instead of Guice modules. All components declared in the container cluster are available for injection, and the dependent component only needs to declare the dependency as a constructor parameter. In general, dependency injection involves at least three elements: + +- a dependent consumer, +- a declaration of a component's dependencies, +- an injector that creates instances of classes that implement a given dependency on request. + +Notes: + +- The dependent object describes what software component it depends on to do its work. The injector decides what concrete classes satisfy the requirements of the dependent object, and provides them to the dependent +- The Container encapsulates the injector, and the consumer and all its dependencies are considered to be components. +- The Container only supports constructor injection (i.e. all dependencies must be declared in a component's constructor). +- Circular dependencies is not supported. + +Refer to the [multiple-bundles sample app](https://github.com/vespa-engine/sample-apps/tree/master/examples/multiple-bundles) for a practical example. + +## Depending on another component + +A component that depends on another is considered to be a *consumer*. A component's dependencies is whatever its `@Inject`-annotated constructor declares as arguments. E.g. the component: + +```java +package com.yahoo.example; +import com.yahoo.component.annotation.Inject; +public class MyComponent { + private final MyDependency dependency; + @Inject + public MyComponent(MyDependency dependency) { + this.dependency = dependency; + } +} +``` + +has a dependency on the class `com.yahoo.example.MyDependency`. To deploy `MyComponent`, register `MyDependency` in `services.xml`: + +```xml + + + + +``` + +Upon deployment, the Container will first instantiate `MyDependency`, and then pass that instance to the constructor of `MyComponent`. Multiple consumers can take the same dependency. One can also [inject configuration](/en/applications/configuring-components) to components. + + +**Note:** + +A component will be reconstructed only when one of its dependencies, configuration, or its class changes - all which only occurs when you re-deploy your application package. Reconstruction is transitive; if component A depends on component B, and component B depends on component C, then a reconfiguration of component B causes a reconfiguration of A, but not of C. Reconfiguration of C causes a reconstruction of both A and B. + + +### Extending components + +When injecting two components when one extends the other, the dependency injection code does not know which of the two to use as the argument for the parent class. To resolve this, inject a `ComponentRegistry` (see below), and look up its entries, like `getComponent(XXX.class.getName())`. + +### Specify the bundle + +The example above assumes the bundle name can be deducted from the class name. This is not always the case, and you will get class loading problems like: + +```txt +Caused by: java.lang.IllegalArgumentException: Could not create a component with id +'com.yahoo.example.My'. +Tried to load class directly, since no bundle was found for spec: +com.yahoo.example.Dependency +``` + +To remedy, specify the jar file (i.e. bundle) with the component: + +```txt + + + +``` + +## Depending on all components of a specific type + +Consider the use-case where a component chooses between various strategies, and each strategy is implemented as a separate component. Since the number and type of strategies is unknown when implementing the consumer, it is impossible to make a constructor that lists all of them. This is where the `ComponentRegistry` comes into play. E.g. the following component: + +```java +package com.yahoo.example; +public class MyComponent { + private final ComponentRegistry strategies; + @Inject + public MyComponent(ComponentRegistry strategies) { + this.strategies = strategies; + } +} +``` + +declares a dependency on the set of all components registered in `services.xml` that are instances of the class `Strategy` (including subclasses). The `ComponentRegistry` class provides accessors for components based on their [component id](/en/reference/applications/services/container#component). + +## Special Components + +There are cases where a component cannot be directly injected to its consumers - example: + +- The component must be instantiated via a factory method instead of its constructor +- Each consumer must have a unique instance of the dependency class +- The component uses native resources that must be cleaned up when the component goes out of scope + +For these situations, JDisc supports injection, and optional deconstruction, via its `Provider` interface: + +```java +public interface Provider { +T get(); +void deconstruct(); +} +``` + +`get()` is called by JDisc each time it needs to instantiate the specific component type. `deconstruct()` is only called after reconfiguring the system with a new application, where the current provider instance is either removed or replaced due to modified dependencies. + +Following the earlier example, declare a provider for the `MyDependency` class, that returns a new instance for each consumer: + +```java +package com.yahoo.example; +import com.yahoo.container.di.componentgraph.Provider; +public class MyDependencyProvider implements Provider { + @Override + public MyDependency get() { + return new MyDependency(); + } + @Override + public void deconstruct() { } +} +``` + +Using this provider, `services.xml` has two instances of `MyComponent`, each getting a unique instance of `MyDependency`: + +```xml + + + + + +``` + +Upon deployment, the Container will first instantiate `MyDependencyProvider`, and then invoke `MyDependencyProvider.get()` for each instantiation of `MyComponent`. + +A provider can declare constructor dependencies, just like any other component. diff --git a/en/applications/deployment.html b/en/applications/deployment.html deleted file mode 100644 index 0d8ca78535..0000000000 --- a/en/applications/deployment.html +++ /dev/null @@ -1,168 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Deployment" -redirect_from: -- /en/deployment.html ---- - -

    In this document we explain various aspects of application deployment in detail. -Refer to application deployment for an introduction.

    - - -

    Convergence

    - -

    After the deployment command has succeeded, the application package will take effect, but this does -not complete immediately in the distributed system that is your running application; -it happens through a distributed convergence process that you can track from the command line -or console. Refer to the deploy reference -for detailed steps run when deploying an application.

    - -

    You can get the status of the last deployment by using the status command:

    - -
    {% highlight shell %}
    -$ vespa status deployment
    -{% endhighlight %}
    - - - -

    Rollback

    -

    - Hover over the instance square to the left, click pin, give a reason - - this will start the downgrade process: -

    - -

    - The pinning to a new version starts a new deployment, and can be rolled out as a normal rollout. - To speed it up, cancel system and staging test jobs by clicking abort. -

    - -

    Skipping tests is at the application owners own discretion and risk:

    -
      -
    • - A system test on this version has already been run on an earlier deployment. - Skipping this can be considered safe, for that reason. -
    • -
    • A staging test has never been run before when rolling back, this path is untested.
    • -
    -

    - Of the two, the staging test takes longer to run. The user decides whether to skip testing phases or not. - With this, a user can control whether to immediately roll back a version including test phases or not, - as well as rolling out to production zones in parallel or not. -

    -

    - After the pin to rollback, make sure to update the code repository so the next deployments is in sync, - and remove the pin for later deployments. -

    - - -

    Follow-up steps

    -

    - Generally, to roll back an application package change, - deploy again with the previous version to roll back to. - The above section describes the fast-track rollback. - The alternatives are: -

    -
      -
    1. - With automation: Revert the code in the source code repository, and let the automation roll out the new version. - You can speed up the deployment by skipping tests and clicking "deploy now" in the deployment graph in the console. -
    2. -
    3. - If you have trouble rebuilding a good package, you can download a previous package from Vespa Cloud: Use the - console - to pick the good version, download it and deploy again. - Hover of the instance - (normally called "default") to skip the system and staging test to speed up the deployment, if needed. -
    4. -
    5. On self-managed instances, regenerate the good version from source for new deployment, - see also the deploy API
    6. -
    - - -

    File distribution

    - -

    The application package can have components and other large files. -When an app is deployed, these files are distributed to the nodes:

    - - - -

    When new components or files specified in config are distributed, the container gets a new file reference, -waits for it to be available and switches to new config when all files are available.

    - -Nodes get config from a config server cluster - - -

    Deploying remote models

    - -

    Most application packages are stored as source code in a code repository. -However, some resources are generated or too large to store in a code repository, -like models or an FSA.

    - -

    Machine learned models in Vespa, -are stored in the application package under the models directory. -This might be inconvenient for some applications, -for instance for models that are frequently retrained on some remote system. -Also, models might be too large to fit within the constraints of the version control system.

    - -

    The solution is to download the models from the remote location during the application package build. -This is simply implemented by adding a step in pom.xml -(see example):

    - -
    {% highlight xml %}
    -
    -    
    -        
    -            org.codehaus.mojo
    -            exec-maven-plugin
    -            1.4.0
    -            
    -                
    -                    download-model
    -                    generate-resources
    -                    
    -                        exec
    -                    
    -                    
    -                        bin/download_models.sh
    -                        
    -                            target/application/models
    -                            MODEL-URL
    -                        
    -                    
    -                
    -            
    -        
    -    
    -
    -{% endhighlight %}
    - -

    bin/download_model.sh example:

    -
    -#!/bin/bash
    -
    -DIR="$1"
    -URL="$2"
    -
    -echo "[INFO] Downloading $URL into $DIR"
    -
    -mkdir -p $DIR
    -pushd $DIR
    -    curl -O $URL
    -popd
    -
    - -

    Any necessary credentials for authentication and authorization should be added to this script, -as well as any unpacking of archives (for TensorFlow models for instance).

    - -

    Also see the model config type to specify resources that -should be downloaded by container nodes during convergence.

    - - diff --git a/en/applications/deployment.mdx b/en/applications/deployment.mdx new file mode 100644 index 0000000000..4d80ca5f7a --- /dev/null +++ b/en/applications/deployment.mdx @@ -0,0 +1,116 @@ +--- +title: "Deployment" +description: "In this document we explain various aspects of application deployment in detail. Refer to [application deployment](/en/basics/applications#deploying-applications) for an introduction." +--- + +## Convergence + +After the deployment command has succeeded, the application package will take effect, but this does not complete immediately in the distributed system that is your running application; it happens through a distributed *convergence* process that you can track from the command line or console. Refer to the [deploy reference](/en/reference/applications/application-packages#deploy) for detailed steps run when deploying an application. + +You can get the status of the last deployment by using the status command: + +```shell +$ vespa status deployment +``` + +## Rollback + +Hover over the instance square to the left, click pin, give a reason - this will start the downgrade process: + + +![](/assets/img/pin-version.png) + + +The pinning to a new version starts a new deployment, and can be rolled out as a normal rollout. To speed it up, cancel system and staging test jobs by clicking abort. + + +![](/assets/img/skip-tests.png) + + +Skipping tests is at the application owners own discretion and risk: + +- A system test on this version has already been run on an earlier deployment. Skipping this can be considered safe, for that reason. +- A staging test has never been run before when rolling back, this path is untested. + +Of the two, the staging test takes longer to run. The user decides whether to skip testing phases or not. With this, a user can control whether to immediately roll back a version including test phases or not, as well as rolling out to production zones in parallel or not. + +After the pin to rollback, make sure to update the code repository so the next deployments is in sync, and remove the pin for later deployments. + +### Follow-up steps + +Generally, to roll back an application package change, deploy again with the previous version to roll back to. The above section describes the fast-track rollback. The alternatives are: + +1. With automation: Revert the code in the source code repository, and let the automation roll out the new version. You can speed up the deployment by skipping tests and clicking "deploy now" in the deployment graph in the console. +2. If you have trouble rebuilding a good package, you can download a previous package from Vespa Cloud: Use the [console](/en/operations/automated-deployments#source-code-repository-integration) to pick the good version, download it and deploy again. Hover of the [instance](/en/operations/automated-deployments#block-windows) (normally called "default") to skip the system and staging test to speed up the deployment, if needed. +3. On self-managed instances, regenerate the good version from source for new deployment, see also the [deploy API](/en/reference/api/deploy-v2#rollback) + +## File distribution + +The application package can have components and other large files. When an app is deployed, these files are distributed to the nodes: + +- Components (i.e bundles) +- Files with type *path* and *url* in config, see [Adding files to the component configuration](/en/applications/configuring-components#adding-files-to-the-component-configuration) +- Machine learned models +- [Constant tensors](/en/reference/schemas/schemas#constant) + +When new components or files specified in config are distributed, the container gets a new file reference, waits for it to be available and switches to new config when all files are available. + + +![](/assets/img/config-delivery.svg) + + +## Deploying remote models + +Most application packages are stored as source code in a code repository. However, some resources are generated or too large to store in a code repository, like models or an [FSA](/en/reference/operations/tools#vespa-makefsa). + +Machine learned models in Vespa, are stored in the application package under the *models* directory. This might be inconvenient for some applications, for instance for models that are frequently retrained on some remote system. Also, models might be too large to fit within the constraints of the version control system. + +The solution is to download the models from the remote location during the application package build. This is simply implemented by adding a step in *pom.xml* (see [example](https://github.com/vespa-cloud/cord-19-search/blob/main/pom.xml)): + +```xml expandable + + + + org.codehaus.mojo + exec-maven-plugin + 1.4.0 + + + download-model + generate-resources + + exec + + + bin/download_models.sh + + target/application/models + MODEL-URL + + + + + + + +``` + +*bin/download_model.sh* example: + +```txt +#!/bin/bash + +DIR="$1" +URL="$2" + +echo "[INFO] Downloading $URL into $DIR" + +mkdir -p $DIR +pushd $DIR +curl -O $URL +popd +``` + +Any necessary credentials for authentication and authorization should be added to this script, as well as any unpacking of archives (for TensorFlow models for instance). + +Also see the [model](/en/reference/applications/config-files#model) config type to specify resources that should be downloaded by container nodes during convergence. diff --git a/en/applications/developer-guide.html b/en/applications/developer-guide.html deleted file mode 100644 index 581995897a..0000000000 --- a/en/applications/developer-guide.html +++ /dev/null @@ -1,318 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Developer Guide" -redirect_from: -- /en/developer-guide.html ---- - -

    This document explains how to develop applications, including basic terminology, -tips on using the Vespa Cloud Console, and how to benchmark and size your application. -See deploy a sample application to deploy a basic sample application, and -automated deployments on making production deployments safe routine occurences.

    - - -

    Manual deployments

    - -

    - Developers will typically deploy their application to the dev zone - during development. Each deployment is owned by a tenant, and each specified instance is - a separate copy of the application; this lets developers work on independent copies of the same application, - or collaborate on a shared one, as they prefer—more details here. - These values can be set in the Vespa Cloud UI when deploying, or with each of the build and deploy tools, - as shown in the respective getting-started guides. -

    -

    - Additionally, a deployment may specify a different zone to deploy to, - instead of the default dev zone. -

    - - -

    Auto downsizing

    -

    - Deployments to dev are downscaled to one small node by default, so that - applications can be deployed there without changing services.xml. - See performance testing for how to disable auto downsizing - using deploy:environment="dev". -

    - - -

    Availability

    -

    The dev zone is a sandbox and not for production serving; It has no uptime guarantees.

    -

    - An automated Vespa software upgrade can be triggered at any time, - and this may lead to some downtime if you have only one node per cluster - (as with the default auto downsizing). -

    - - - -

    Performance testing

    -

    - For performance testing, to avoid auto downsizing, - lock the resources using deploy:environment="dev": -

    -
    {% highlight xml %}
    -
    -    
    -
    -{% endhighlight %}
    -

    - Read more in benchmarking and - variants in services.xml. -

    - - -

    Component overview

    - -Vespa Overview -

    - Application packages can contain Java components to be run in container clusters. - The most common component types are: -

    -
      -
    • - Searchers, which can modify or build the query, - modify the result, implement workflows issuing multiple queries etc. -
    • -
    • - Document processors that can modify incoming write operations. -
    • -
    • - Handlers that can implement custom web service APIs. -
    • -
    • - Renderers that are used to define custom result formats. -
    • -
    -

    - Components are constructed by dependency injection and are reloaded safely on deployment without restarts. - See the container documentation for more details. -

    -

    -See deploy an application having Java components, -and troubleshooting.

    - - -

    Developing Components

    - -

    - The development cycle consists of creating the component, - deploying the application package to Vespa, writing tests, and iterating. - These steps refer to files in - - album-recommendation-java: -

    - - - - - - - - - - - -
    Build -

    All the Vespa sample applications use the - bundle plugin to build the components.

    -
    -
    Configure -

    - A key Vespa feature is code and configuration consistency, - deployed using an application package. - This ensures that code and configuration is in sync, and loaded atomically when deployed. - This is done by generating config classes from config definition files. - In Vespa and application code, - configuration is therefore accessed through generated config classes. -

    - The Maven target generate-sources (invoked by mvn install) uses - - metal-names.def - to generate target/generated-sources/vespa-configgen-plugin/com/mydomain/example/MetalNamesConfig.java. -

    - After generating config classes, they will resolve in tools like - IntelliJ IDEA. -

    -
    -
    Tests -

    - Examples unit tests are found in - - MetalSearcherTest.java. - testAddedOrTerm1 and testAddedOrTerm2 illustrates two ways of doing the same test: -

    - -

    - Read more in unit testing. -

    -
    - - -

    Debugging Components

    -{% include important.html content="The debugging procedure only works for endpoints with an open debug port - -most managed services don't do this for security reasons." %} -

    - Vespa Cloud does not allow debugging over the Java Debug Wire Protocol (JDWP) due to the protocol's inherent lack of security measures. - If you need interactive debugging, deploy your application to a self-hosted Vespa installation (below) - and manually add the JDWP agent to JVM options. -

    -

    - You may debug your Java code by requesting either a JVM heap dump or a Java Flight Recorder recording through the - Vespa Cloud Console. Go to your application's cluster overview and select - export JVM artifact on any container node. The process will take up to a few minutes. - You'll find the steps to download the dump on the Console once it's completed. - Extract the files from the downloaded Zstandard-compressed archive, and use the free - JDK Mission Control utility to inspect - the dump/recording. -

    -Generate JVM dump -

    - To debug a Searcher / - Document Processor / - Component running in a self-hosted container, - set up a remote debugging configuration in the IDEA - IntelliJ example: -

    -
      -
    1. Run -> Edit Configurations...
    2. -
    3. Click + to add a new configuration.
    4. -
    5. Select the "Remote JVM Debug" option in the left-most pane.
    6. -
    7. Set hostname to the host running the container, change the port if needed.
    8. -
    9. - Set the container's jvm options - to the value in "Command line arguments for remote JVM": -
      -<container id="default" version="1.0">
      -    <nodes>
      -        <jvm options="-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005" />
      -
      -
    10. -
    11. - Re-deploy the application, then restart Vespa on the node that runs the container. - Make sure the port is published if using a Docker/Podman container, e.g.: -
      -$ docker run --detach --name vespa --hostname vespa-container \
      -  --publish 127.0.0.1:8080:8080 --publish 127.0.0.1:19071:19071 --publish 127.0.0.1:5005:5005 \
      -  vespaengine/vespa
      -
      -
    12. -
    13. - Start debugging! Check vespa.log for errors. -
    14. -
    -{% include video-include.html - image-url='/assets/img/video-thumbs/deploying-a-vespa-searcher.png' - video-url='https://www.youtube.com/embed/dUCLKtNchuE' - video-title='Debugging a Vespa Searcher' %} - - -

    Developing system and staging tests

    - -

    - When using Vespa Cloud, system and tests are most easily developed using a test deployment in a - dev zone to run the tests against. - Refer to general testing guide - for a discussion of the different test types, - and the basic HTTP tests or - Java JUnit tests reference - for how to write the relevant tests. -

    -

    - If using the Vespa CLI to deploy and run - basic HTTP tests, - the same commands as in the test reference will just work, - provided the CLI is configured to use the cloud target. -

    - - -

    Running Java tests

    - -

    - With Maven, and Java Junit tests, - some additional configuration is required, - to infuse the test runtime on the local machine with API and data plane credentials: -

    -
    -$ mvn test \
    -  -D test.categories=system \
    -  -D dataPlaneKeyFile=data-plane-private-key.pem -D dataPlaneCertificateFile=data-plane-public-cert.pem \
    -  -D apiKey="$API_KEY"
    -
    -

    - The apiKey is used to fetch the dev instance's endpoints. - The data plane key and certificate pair is used by - ai.vespa.hosted.cd.Endpoint - to access the application endpoint. - Note that the -D vespa.test.config argument is gone; - this configuration is automatically fetched from the Vespa Cloud API—hence the need for the API key. -

    -

    - When running Vespa self-hosted like in the - sample application, - no authentication is required by default, to either API or container, and specifying a data plane key and certificate - will instead cause the test to fail, since the correct SSL context is the Java default in this case. -

    -

    - Make sure the TestRuntime is able to start. - As it will init an SSL context, make sure to remove config when running locally, in order to use a default context. - Remove properties from pom.xml and IDE debug configuration. -

    -

    Developers can also set these parameters in the IDE run configuration to debug system tests:

    -
    --D test.categories=system
    --D tenant=my_tenant
    --D application=my_app
    --D instance=my_instance
    --D apiKeyFile=/path/to/myname.mytenant.pem
    --D dataPlaneCertificateFile=data-plane-public-cert.pem
    --D dataPlaneKeyFile=data-plane-private-key.pem
    -
    - - -

    Tips and troubleshooting

    -
      -
    • -

      - Vespa Cloud upgrades daily, and applications in dev also have their Vespa platform upgraded. - This usually happens at the opposite time of day of when deployments are made to each instance, and takes some minutes. - Deployments without redundancy will be unavailable during the upgrade. -

      -
    • -
    • -

      - Failure to deploy, due to authentication (HTTP code 401) or authorization (HTTP code 403), - is most often due to wrong configuration of tenant and/or application, - when using command line tools to deploy. Ensure the values set with Vespa CLI or in pom.xml - match what is configured in the UI. -

      -
    • -
    • -

      - In case of data plane failure, - remember to copy the public certificate to src/main/application/security/clients.pem - before building and deploying. This is handled by the Vespa CLI vespa auth cert command. -

      -
    • -
    • -

      - To run Java system and staging tests in an IDE, - ensure all API and data plane keys and certificates are configured in the IDE as well; - not all IDEs pick up all settings from pom.xml correctly: -

      -
      --Dtest.categories=system
      --DapiKeyFile=/path-to/tname.pem
      --DdataPlaneCertificateFile=/path-to/data-plane-public-cert.pem
      --DdataPlaneKeyFile=/path-to/data-plane-private-key.pem
      -
      -
    • -
    diff --git a/en/applications/developer-guide.mdx b/en/applications/developer-guide.mdx new file mode 100644 index 0000000000..a6636af0e5 --- /dev/null +++ b/en/applications/developer-guide.mdx @@ -0,0 +1,199 @@ +--- +title: "Developer Guide" +description: "This document explains how to develop applications, including basic terminology, tips on using the Vespa Cloud Console, and how to benchmark and size your application. See [deploy a sample application](/en/basics/deploy-an-application) to deploy a basic sample application, and [automated deployments](/en/operations/automated-deployments) on making production deployments safe routine occurences." +--- + + +## Manual deployments + +Developers will typically deploy their application to the `dev` [zone](/en/operations/zones) during development. Each deployment is owned by a *tenant*, and each specified *instance* is a separate copy of the application; this lets developers work on independent copies of the same application, or collaborate on a shared one, as they prefer—more details [here](/en/learn/tenant-apps-instances). These values can be set in the Vespa Cloud UI when deploying, or with each of the build and deploy tools, as shown in the respective getting-started guides. + +Additionally, a deployment may specify a different [zone](/en/operations/zones) to deploy to, instead of the default `dev` zone. + +### Auto downsizing + +Deployments to `dev` are downscaled to one small node by default, so that applications can be deployed there without changing `services.xml`. See [performance testing](#performance-testing) for how to disable auto downsizing using `deploy:environment="dev"`. + +### Availability + +The `dev` zone is a sandbox and not for production serving; It has no uptime guarantees. + +An automated Vespa software upgrade can be triggered at any time, and this may lead to some downtime if you have only one node per cluster (as with the default [auto downsizing](#auto-downsizing)). + +## Performance testing + +For performance testing, to avoid auto downsizing, lock the [resources](/en/reference/applications/services/services) using `deploy:environment="dev"`: + +```xml + + + +``` + +Read more in [benchmarking](/en/performance/benchmarking-cloud) and [variants in services.xml](/en/operations/deployment-variants). + +## Component overview + + +![](/assets/img/vespa-overview.svg) + + +Application packages can contain Java components to be run in container clusters. The most common component types are: + +- [Searchers](/en/applications/searchers), which can modify or build the query, modify the result, implement workflows issuing multiple queries etc. +- [Document processors](/en/applications/document-processors) that can modify incoming write operations. +- [Handlers](/en/applications/request-handlers) that can implement custom web service APIs. +- [Renderers](/en/applications/result-renderers) that are used to define custom result formats. + +Components are constructed by dependency injection and are reloaded safely on deployment without restarts. See the [container documentation](/en/applications/containers) for more details. + +See [deploy an application having Java components](/en/basics/deploy-an-application-java), and [troubleshooting](/en/operations/self-managed/admin-procedures#troubleshooting). + +## Developing Components + +The development cycle consists of creating the component, deploying the application package to Vespa, writing tests, and iterating. These steps refer to files in [album-recommendation-java](https://github.com/vespa-engine/sample-apps/tree/master/album-recommendation-java): + + + + + + + + + + + + + + + + + + + + + + +
    **Build**All the Vespa sample applications use the bundle plugin to build the components.
    **Configure**A key Vespa feature is code and configuration consistency, deployed using an application package. This ensures that code and configuration is in sync, and loaded atomically when deployed. This is done by generating config classes from config definition files. In Vespa and application code, configuration is therefore accessed through generated config classes. The Maven target {`generate-sources`} (invoked by {`mvn install`}) uses metal-names.def to generate {`target/generated-sources/vespa-configgen-plugin/com/mydomain/example/MetalNamesConfig.java`}. After generating config classes, they will resolve in tools like IntelliJ IDEA.
    **Tests**Examples unit tests are found in MetalSearcherTest.java. {`testAddedOrTerm1`} and {`testAddedOrTerm2`} illustrates two ways of doing the same test: The first setting up the minimal search chain for YQL programmatically. The second uses {`com.yahoo.application.Application`}, which sets up the application package and simplifies testing. Read more in unit testing.
    + +## Debugging Components + + +**Important:** + +The debugging procedure only works for endpoints with an open debug port - most managed services don't do this for security reasons. + + +Vespa Cloud does not allow debugging over the *Java Debug Wire Protocol (JDWP)* due to the protocol's inherent lack of security measures. If you need interactive debugging, deploy your application to a self-hosted Vespa installation (below) and manually [add the *JDWP* agent to JVM options](#debugging-components). + +You may debug your Java code by requesting either a JVM heap dump or a Java Flight Recorder recording through the [Vespa Cloud Console](https://console.vespa-cloud.com/). Go to your application's cluster overview and select *export JVM artifact* on any *container* node. The process will take up to a few minutes. You'll find the steps to download the dump on the Console once it's completed. Extract the files from the downloaded Zstandard-compressed archive, and use the free [JDK Mission Control](https://www.oracle.com/java/technologies/jdk-mission-control) utility to inspect the dump/recording. + + +![](/assets/img/jvm-dump.png) + + +To debug a [Searcher](/en/applications/searchers) / [Document Processor](/en/applications/document-processors) / [Component](/en/applications/components) running in a self-hosted container, set up a remote debugging configuration in the IDEA - IntelliJ example: + + + + Run -> Edit Configurations... + + + + Click `+` to add a new configuration. + + + + Select the "Remote JVM Debug" option in the left-most pane. + + + + Set hostname to the host running the container, change the port if needed. + + + + Set the container's [jvm options](/en/reference/applications/services/container#jvm) to the value in "Command line arguments for remote JVM": + + ```xml + + + + + + ``` + + + + Re-deploy the application, then restart Vespa on the node that runs the container. Make sure the port is published if using a Docker/Podman container, e.g.: + + ```bash + $ docker run --detach --name vespa --hostname vespa-container \ + --publish 127.0.0.1:8080:8080 --publish 127.0.0.1:19071:19071 --publish 127.0.0.1:5005:5005 \ + vespaengine/vespa + ``` + + + Start debugging! Check *vespa.log* for errors. + + + + + + +Find *Debugging a Vespa Searcher* in the vespaengine [youtube channel](https://www.youtube.com/@vespaai)! + + + + +## Developing system and staging tests + +When using Vespa Cloud, system and tests are most easily developed using a test deployment in a `dev` zone to run the tests against. Refer to [general testing guide](/en/applications/testing) for a discussion of the different test types, and the [basic HTTP tests](/en/reference/applications/testing) or [Java JUnit tests](/en/reference/applications/testing-java) reference for how to write the relevant tests. + +If using the [Vespa CLI](/en/clients/vespa-cli) to deploy and run [basic HTTP tests](/en/reference/applications/testing), the same commands as in the test reference will just work, provided the CLI is configured to use the `cloud` target. + +### Running Java tests + +With Maven, and [Java Junit tests](/en/reference/applications/testing-java), some additional configuration is required, to infuse the test runtime on the local machine with API and data plane credentials: + +```bash +$ mvn test \ +-D test.categories=system \ +-D dataPlaneKeyFile=data-plane-private-key.pem -D dataPlaneCertificateFile=data-plane-public-cert.pem \ +-D apiKey="$API_KEY" +``` + +The `apiKey` is used to fetch the *dev* instance's endpoints. The data plane key and certificate pair is used by [ai.vespa.hosted.cd.Endpoint](https://github.com/vespa-engine/vespa/blob/master/cloud/tenant-cd-api/src/main/java/ai/vespa/hosted/cd/Endpoint.java) to access the application endpoint. Note that the `-D vespa.test.config` argument is gone; this configuration is automatically fetched from the Vespa Cloud API—hence the need for the API key. + +When running Vespa self-hosted like in the [sample application](/en/basics/deploy-an-application-local), no authentication is required by default, to either API or container, and specifying a data plane key and certificate will instead cause the test to fail, since the correct SSL context is the Java default in this case. + +Make sure the TestRuntime is able to start. As it will init an SSL context, make sure to remove config when running locally, in order to use a default context. Remove properties from *pom.xml* and IDE debug configuration. + +Developers can also set these parameters in the IDE run configuration to debug system tests: + +```txt +-D test.categories=system +-D tenant=my_tenant +-D application=my_app +-D instance=my_instance +-D apiKeyFile=/path/to/myname.mytenant.pem +-D dataPlaneCertificateFile=data-plane-public-cert.pem +-D dataPlaneKeyFile=data-plane-private-key.pem +``` + +## Tips and troubleshooting + +- Vespa Cloud upgrades daily, and applications in `dev` also have their Vespa platform upgraded. This usually happens at the opposite time of day of when deployments are made to each instance, and takes some minutes. Deployments without redundancy will be unavailable during the upgrade. +- Failure to deploy, due to authentication (HTTP code 401) or authorization (HTTP code 403), is most often due to wrong configuration of `tenant` and/or `application`, when using command line tools to deploy. Ensure the values set with Vespa CLI or in `pom.xml` match what is configured in the UI. +- In case of data plane failure, remember to copy the public certificate to `src/main/application/security/clients.pem` before building and deploying. This is handled by the Vespa CLI `vespa auth cert` command. +- To run Java [system and staging tests](/en/reference/applications/testing-java) in an IDE, ensure all API and data plane keys and certificates are configured in the IDE as well; not all IDEs pick up all settings from `pom.xml` correctly: + + ```txt + -Dtest.categories=system + -DapiKeyFile=/path-to/tname.pem + -DdataPlaneCertificateFile=/path-to/data-plane-public-cert.pem + -DdataPlaneKeyFile=/path-to/data-plane-private-key.pem + ``` diff --git a/en/applications/document-processors.html b/en/applications/document-processors.html deleted file mode 100644 index 2ce145f290..0000000000 --- a/en/applications/document-processors.html +++ /dev/null @@ -1,490 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Document processors" -redirect_from: -- /en/document-processing.html ---- - -

    - This document describes how to develop and deploy Document Processors, - often called docproc in this documentation. - Document processing is a framework to create chains - of configurable components, - that read and modify document operations. -

    - The input source splits the input data into logical units called documents. - A feeder application sends the documents into a document processing chain. - This chain is an ordered list of document processors. - Document processing examples range from language detection, - HTML removal and natural language processing to mail attachment processing, - character set transcoding and image thumbnailing. - At the end of the processing chain, extracted data will typically be set in some fields in the document. -

    - The motivation for document processing is that code and configuration is atomically deployed, - as like all Vespa components. - It is also easy to build components that access data in Vespa as part of processing. -

    - To get started, see the - sample application. - Read indexing to understand deployment and routing. - As document processors are chained components just like Searchers, - read Searcher Development. - For reference, see the Javadoc, - and services.xml. -

    -Document Processing component in Vespa overview - - - -

    Deploying a Document Processor

    -

    - Refer to - album-recommendation-docproc to get started, - - LyricsDocumentProcessor.java is a document processor example. - Add the document processor in services.xml, - and then add it to a chain. - The type of processing done by the processor dictates what chain it should be part of: -

      -
    • If it does general data-processing, such as populating some document fields from others, - looking up data in external services, etc., it should be added to a general docproc chain.
    • -
    • If, and only if, it does processing required for indexing - - or requires this to have already been run — - it should be added to a chain which inherits the indexing chain, - and which is used for indexing by a content cluster.
    • -
    -

    - An example that adds a general document processor to the "default" chain, - and an indexing related processor to the chain for a particular content cluster: -

    -
    -<services>
    -    <container version="1.0" id="default">
    -        <document-processing>
    -            <chain id="default">
    -                <documentprocessor id="ai.vespa.example.album.LyricsDocumentProcessor"
    -                                   bundle="albums-docproc"/>
    -            </chain>
    -            <chain id="my-indexing" inherits="indexing">
    -                <documentprocessor id="ai.vespa.example.album.PostIndexingDocumentProcessor"
    -                                   bundle="albums-docproc"/>
    -            </chain>
    -        </document-processing>
    -    </container>
    -
    -    <content version="1.0" id="content">
    -        <documents>
    -            ...
    -            <document-processing cluster="default" chain="my-indexing" />
    -        </documents>
    -    </content>
    -</services>
    -
    -

    - The "default" chain, if it exists, is run by default, before the chain used for indexing. - The default indexing chain is called "indexing", - and must be inherited by any chain that is to replace it. -

    -

    - To run through any chain, specify a route which includes the chain. - For example, the route default/chain.my-chain indexing would route feed operations - through the chain "my-chain" in the "default" container cluster, and then to the "indexing" hop, - which resolves to the specified indexing chain for each content cluster the document should be sent to. - More details can be found in indexing: -

    - - - -

    Document Processors

    -

    - A document processor is a component extending com.yahoo.docproc.DocumentProcessor. - All document processors must implement process(): -

    -
    -public Progress process(Processing processing);
    -
    -

    - When the container receives a document operation, - it will create a new Processing, and add the DocumentPuts, - DocumentUpdates or DocumentRemoves to the List - accessible through Processing.getDocumentOperations(). - The latter is useful also where a processing should be stopped - by doing Processing.getDocumentOperations().clear() before Progress.DONE, - say for blocklist use, to stop a DocumentPut/Update. -

    -

    - Furthermore, the call stack of the document processing chain in question will - be copied to Processing.callStack(), - so that document processors may freely modify the flow of control for this processing - without affecting all other processings going on. - After creation, the Processing is added to an internal queue. -

    - A worker thread will retrieve a Processing from the input queue, - and run its document operations through its call stack. - A minimal, no-op document processor implementation is thus: -

    -
    {% highlight java %}
    -import com.yahoo.docproc.*;
    -
    -public class SimpleDocumentProcessor extends DocumentProcessor {
    -    public Progress process(Processing processing) {
    -        return Progress.DONE;
    -    }
    -}
    -{% endhighlight %}
    -

    - The process() method should loop through all - document operations in Processing.getDocumentOperations(), do - whatever it sees fit to them, and return a Progress: -

    -
    {% highlight java %}
    -public Progress process(Processing processing) {
    -    for (DocumentOperation op : processing.getDocumentOperations()) {
    -        if (op instanceof DocumentPut) {
    -            DocumentPut put = (DocumentPut) op;
    -            // TODO do something to 'put here
    -        } else if (op instanceof DocumentUpdate) {
    -            DocumentUpdate update = (DocumentUpdate) op;
    -            // TODO do something to 'update' here
    -        } else if (op instanceof DocumentRemove) {
    -            DocumentRemove remove = (DocumentRemove) op;
    -            // TODO do something to 'remove' here
    -        }
    -    }
    -    return Progress.DONE;
    -}
    -{% endhighlight %}
    - - - - - - - - - - - - - - - - - - - - - - -
    Return codeDescription
    Progress.DONEReturned if a document processor has successfully processed a Processing.
    Progress.FAILED -

    - Processing failed and the input message should return a fatal - failure back to the feeding application, - meaning that this application will not try to re-feed this document operation. - Return an error message/reason by calling withReason(): -

    -

    - This result is represented as a 500 Internal Server Error response in Document v1. -

    -
    {% highlight java %}
    -if (op instanceof DocumentPut) {
    -    return Progress.FAILED.withReason("PUT is not supported");
    -}
    -{% endhighlight %}
    -
    Progress.INVALID_INPUT -

    - Available since 8.584. -

    -

    - Processing failed due to invalid input, like a malformed document operation. -

    -

    - This result is represented as a 400 Bad Request response in Document v1. -

    -
    Progress.LATER -

    - See execution model. - The document processor wants to release the calling thread and be called again later. - This is useful if e.g. calling an external service with high latency. - The document processor may then save its state in the Processing - and resume when called again later. - There are no guarantees as to when the processor is called again - with this Processing; it is simply appended to the back of the input queue. -

    - By the use of Progress.LATER, this is an asynchronous model, - where the processing of a document operation - does not need to consume one thread for its entire lifespan. - Note, however, that the document processors themselves - are shared between all processing operations in a chain, - and must thus be implemented thread-safe. -

    -
    - - - - - - - - - - - - - - - - -
    ExceptionDescription
    com.yahoo.docproc.TransientFailureException - Processing failed and the input message should return a transient failure - back to the feeding application, - meaning that this application may try to re-feed this document operation. -
    RuntimeException - Throwing any other RuntimeException - means same behavior as for Progress.FAILED. -
    - - - -

    Chains

    -

    - The call stack mentioned above is another name for a document processor chain. - Document processor chains are a special case of the general - component chains - - to avoid confusion some concepts are explained here as well. - A document processor chain is nothing more than a list of document processor instances, - having an id, and represented as a stack. - The document processor chains are typically not created for every processing, - but are part of the configuration. - Multiple ones may exist at the same time, - the chain to execute will be specified by the message bus destination of the incoming message. - The same document processor instance may exist in multiple document processor chains, - which is why the CallStack of the Processing - is responsible for knowing the next document processor to invoke in a particular message. -

    -

    - The execution order of the document processors in a chain are not ordered explicitly, - but by ordering constraints - declared in the document processors or their configuration. -

    - - - -

    Execution model

    -

    - The Document Processing Framework works like this: -

    -
      -
    1. - A thread from the message bus layer appends an incoming message to an internal priority queue, - shared between all document processing chains configured on a node. - The priority is set based on the message bus priority of the message. - Messages of the same priority are ordered FIFO. -
    2. - One worker thread from the docproc thread pool picks one message - from the head of the queue, deserializes it, copies the call stack - (chain) in question, and runs it through the document processors. -
    3. - Processing finishes if (a) the document(s) has - passed successfully through the whole chain, - or (b) a document processor in the chain has - returned Progress.FAILED or thrown an exception. -
    4. - The same thread passes the message on to the message bus layer for - further transport on to its destination. -
    5. -
    -

    - There is a single instance of each document processor chain. - In every chain, there is a single instance of each document processor - - unless a chain is configured with multiple, identical document processors - this is a rare case. -

    - As is evident from the model above, - multiple worker threads execute the document processors in a chain concurrently. - Thus, many threads of execution can be going through process() in a - document processor, at the same time. -

    - This model places an important constraint on document processor classes: - instance variables are not safe. - They must be eliminated, or made thread-safe somehow. -

    - Also see Resource management, - use deconstruct() in order to not leak resources. -

    - - -

    Asynchronous execution

    -

    - The execution model outlined above also shows one important restriction: - If a document processor performs any high-latency operation in its process() method, - a docproc worker thread will be occupied. - With all n worker threads blocking on an external resource, - throughput will be limited. - This can be fixed by saving the state in the Processing object, - and returning Progress.LATER. - A document processor doing a high-latency operation should use a pattern like this: -

    -
      -
    1. Check a self-defined context variable in Processing for status. - Basically, have we seen this Processing before?
    2. -
    3. If no: -
        -
      1. We have been given a Processing object fresh off the network, - we have not seen this before. - Process it up until the high-latency operation.
      2. -
      3. Start the high-latency operation (possibly in a separate thread).
      4. -
      5. Save the state of the operation in a self-defined context variable in the Processing.
      6. -
      7. Return Progress.LATER. - This Processing is the appended to the back of the input queue, - and we will be called again later.
      8. -
    4. -
    5. If yes: -
        -
      1. Retrieve the reference that we set in our self-defined context variable in Processing.
      2. -
      3. Is the high-latency operation done? If so, return Progress.DONE.
      4. -
      5. Is it not yet done? Return Progress.LATER again.
      6. -
    6. -
    -

    - As is evident, this will let the finite set of document processing threads to do more work at the same time. -

    - - - -

    State

    -

    - Any state in the document processor for the particular Processing - should be kept as local variables in the process method, - while state which should be shared by all Processings should be kept as member variables. - As the latter kind will be accessed by multiple threads at any one time, - the state of such member variables must be thread-safe. - This critical restriction is similar to those of e.g. the Servlet API. - Options for implementing a multithread-safe document processor with instance variables: -

    -
      -
    1. Use immutable (and preferably final) objects: - they never change after they are constructed; - no modifications to their state occurs after the DocumentProcessor constructor returns.
    2. -
    3. Use a single instance of a thread-safe class.
    4. -
    5. Create a single instance and synchronize access to it across all threads - (but this will severely limit scalability).
    6. -
    7. Arrange for each thread to have its own instance, e.g. with a ThreadLocal.
    8. -
    - - -

    Processing Context Variables

    -

    - Processing has a map String -> Object - that can be used to pass information between document processors. - It is also useful when using Progress.LATER to save the state of a processing - see - - Processing.java for get/setVariable and more. -

    -

    - The - sample application uses such context variables, too. -

    - - - -

    Operation ordering

    - -

    Feed ordering

    -

    - Ordering of feed operations is not guaranteed. - Operations on different documents will be done concurrently and are therefore not ordered. - However, Vespa guarantees that operations on the same document are processed in the order they were fed - if they enter vespa at the same feed endpoint. -

    - -

    Document processing ordering

    -

    - Document operations that are produced inside a document processor obey the same rules as at feed time. - If you either split the input into other documents or into multiple operations to the same document, - Vespa will ensure that operations to the same document ID are sequenced and are delivered in the order they enter. -

    - - - -

    (Re)configuring Document Processing

    -

    -Consider the following configuration: -

    -
    -<?xml version="1.0" encoding="utf-8" ?>
    -<services version="1.0">
    -    <container version="1.0" id="default">
    -        <document-processing>
    -            <chain id="default">
    -                <documentprocessor id="SomeDocumentProcessor">
    -                    <config name="foo.something">
    -                        <variable>value</variable>
    -                    </config>
    -                </documentprocessor>
    -            </chain>
    -        </document-processing>
    -    </container>
    -</services>
    -
    -

    - Changing chain ids, components in a chain, component configuration and schema mapping all takes effect after - deployment - no restart required. - Changing a cluster name (i.e. the container id) - requires a restart of docproc services after vespa activate. -

    - Note when adding or modifying a processing chain in a running cluster; - if at the same time deploying a new document processor - (i.e. a document processor that was unknown to Vespa at the time the cluster was started), - the container must be restarted: -

    -
    -$ vespa-sentinel-cmd restart container
    -
    - - - -

    Class diagram

    -Document processing core class diagram -

    - The framework core supports asynchronous processing, - processing one or multiple documents or document updates at the same time, - document processors that makes dynamic decisions about the processing flow and - passing of information between processors outside the document or document update: -

    -
      -
    • - One or more named Docproc Services may be created. - One of the services is the default. -
    • - A service accepts subclasses of DocumentOperation for processing, - meaning DocumentPuts, DocumentUpdates - and DocumentRemoves. - It has a Call Stack which lists the calls to make to - various DocumentProcessors to process each DocumentOperation handed to the service. -
    • - Call Stacks consist of Calls, - which refer to the Document Processor instance to call. -
    • - Document puts and document updates are processed asynchronously, - the state is kept in a Processing for its duration - (instead of in a thread or process). - A Document Processor may make some asynchronous calls (typically to remote services) - and return to the framework that it should be called again later - for the same Processing to handle the outcome of the calls. -
    • - A processing contains its own copy of the Call Stack of the Docproc Service - to keep track of what to call next. - Document Processors may modify this Call Stack - to dynamically decide the processing steps required to process a DocumentOperation. -
    • - A Processing may contain one or more DocumentOperations to be processed as a unit. -
    • - A Processing has a context, which is a Map of named values - which can be used to pass arguments between processors. -
    • - Processings are prepared to be stored to disk, - to allow a high number of ongoing long-term processings per node. -
    • -
    diff --git a/en/applications/document-processors.mdx b/en/applications/document-processors.mdx new file mode 100644 index 0000000000..1b89211216 --- /dev/null +++ b/en/applications/document-processors.mdx @@ -0,0 +1,278 @@ +--- +title: "Document processors" +description: "This document describes how to develop and deploy *Document Processors*, often called *docproc* in this documentation. Document processing is a framework to create [chains](/en/applications/chaining) of configurable [components](/en/applications/components), that read and modify document operations." +--- + +The input source splits the input data into logical units called [documents](/en/schemas/documents). A [feeder application](/en/writing/reads-and-writes) sends the documents into a document processing chain. This chain is an ordered list of document processors. Document processing examples range from language detection, HTML removal and natural language processing to mail attachment processing, character set transcoding and image thumbnailing. At the end of the processing chain, extracted data will typically be set in some fields in the document. + +The motivation for document processing is that code and configuration is atomically deployed, as like all Vespa components. It is also easy to build components that access data in Vespa as part of processing. + +To get started, see the [sample application](https://github.com/vespa-engine/sample-apps/tree/master/examples/document-processing). Read [indexing](/en/writing/indexing) to understand deployment and routing. As document processors are chained components just like Searchers, read [Searcher Development](/en/applications/searchers). For reference, see the [Javadoc](https://javadoc.io/doc/com.yahoo.vespa/docproc), and [services.xml](/en/reference/applications/services/docproc). + + +![](/assets/img/vespa-overview-docproc.svg) + + +## Deploying a Document Processor + +Refer to [album-recommendation-docproc](https://github.com/vespa-engine/sample-apps/tree/master/examples/document-processing) to get started, [LyricsDocumentProcessor.java](https://github.com/vespa-engine/sample-apps/blob/master/examples/document-processing/src/main/java/ai/vespa/example/album/LyricsDocumentProcessor.java) is a document processor example. Add the document processor in [services.xml](/en/reference/applications/services/docproc), and then add it to a [chain](#chains). The type of processing done by the processor dictates what chain it should be part of: + +- If it does general data-processing, such as populating some document fields from others, looking up data in external services, etc., it should be added to a general docproc chain. +- If, and only if, it does processing required for *indexing* +- or requires this to have already been run — it should be added to a chain which inherits the *indexing* chain, and which is used for indexing by a content cluster. + +An example that adds a general document processor to the "default" chain, and an indexing related processor to the chain for a particular content cluster: + +```xml highlight={4, 8, 18} + + + + + + + + + + + + + + + ... + + + + +``` + +The "default" chain, if it exists, is run by default, before the chain used for indexing. The default indexing chain is called "indexing", and *must* be inherited by any chain that is to replace it. + +To run through any chain, specify a [route](/en/writing/document-routing) which includes the chain. For example, the route `default/chain.my-chain indexing` would route feed operations through the chain "my-chain" in the "default" container cluster, and then to the "indexing" hop, which resolves to the specified indexing chain for each content cluster the document should be sent to. More details can be found in [indexing](/en/writing/document-routing#document-processing): + +## Document Processors + +A document processor is a component extending `com.yahoo.docproc.DocumentProcessor`. All document processors must implement `process()`: + +```txt +public Progress process(Processing processing); +``` + +When the container receives a document operation, it will create a new `Processing`, and add the `DocumentPut`s, `DocumentUpdate`s or `DocumentRemove`s to the `List` accessible through `Processing.getDocumentOperations()`. The latter is useful also where a processing should be stopped by doing `Processing.getDocumentOperations().clear()` before `Progress.DONE`, say for blocklist use, to stop a `DocumentPut/Update`. + +Furthermore, the call stack of the document processing chain in question will be *copied* to `Processing.callStack()`, so that document processors may freely modify the flow of control for this processing without affecting all other processings going on. After creation, the `Processing` is added to an internal queue. + +A worker thread will retrieve a `Processing` from the input queue, and run its document operations through its call stack. A minimal, no-op document processor implementation is thus: + +```java +import com.yahoo.docproc.*; +public class SimpleDocumentProcessor extends DocumentProcessor { + public Progress process(Processing processing) { + return Progress.DONE; + } +} +``` + +The `process()` method should loop through all document operations in `Processing.getDocumentOperations()`, do whatever it sees fit to them, and return a `Progress`: + +```java +public Progress process(Processing processing) { + for (DocumentOperation op : processing.getDocumentOperations()) { + if (op instanceof DocumentPut) { + DocumentPut put = (DocumentPut) op; + // TODO do something to 'put here + } else if (op instanceof DocumentUpdate) { + DocumentUpdate update = (DocumentUpdate) op; + // TODO do something to 'update' here + } else if (op instanceof DocumentRemove) { + DocumentRemove remove = (DocumentRemove) op; + // TODO do something to 'remove' here + } +} +return Progress.DONE; +} +``` + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Return codeDescription
    {`Progress.DONE`}Returned if a document processor has successfully processed a {`Processing`}.
    {`Progress.FAILED`}Processing failed and the input message should return a *fatal* failure back to the feeding application, meaning that this application will not try to re-feed this document operation. Return an error message/reason by calling {`withReason()`}. This result is represented as a {`500 Internal Server Error`} response in Document v1. Example: {`if (op instanceof DocumentPut) { return Progress.FAILED.withReason("PUT is not supported"); }`}
    {`Progress.INVALID_INPUT`}Available since 8.584. Processing failed due to invalid input, like a malformed document operation. This result is represented as a {`400 Bad Request`} response in Document v1.
    {`Progress.LATER`}See execution model. The document processor wants to release the calling thread and be called again later. This is useful if e.g. calling an external service with high latency. The document processor may then save its state in the {`Processing`} and resume when called again later. There are no guarantees as to *when* the processor is called again with this {`Processing`}; it is simply appended to the back of the input queue. By the use of {`Progress.LATER`}, this is an asynchronous model, where the processing of a document operation does not need to consume one thread for its entire lifespan. Note, however, that the document processors themselves are shared between all processing operations in a chain, and must thus be implemented thread-safe.
    + + + + + + + + + + + + + + + + + + +
    ExceptionDescription
    {`com.yahoo.docproc.TransientFailureException`}Processing failed and the input message should return a *transient* failure back to the feeding application, meaning that this application *may* try to re-feed this document operation.
    {`RuntimeException`}Throwing any other {`RuntimeException`} means same behavior as for {`Progress.FAILED`}.
    + +## Chains + +The call stack mentioned above is another name for a *document processor chain*. Document processor chains are a special case of the general [component chains](/en/applications/chaining) - to avoid confusion some concepts are explained here as well. A document processor chain is nothing more than a list of document processor instances, having an id, and represented as a stack. The document processor chains are typically not created for every processing, but are part of the configuration. Multiple ones may exist at the same time, the chain to execute will be specified by the message bus destination of the incoming message. The same document processor instance may exist in multiple document processor chains, which is why the `CallStack` of the `Processing` is responsible for knowing the next document processor to invoke in a particular message. + +The execution order of the document processors in a chain are not ordered explicitly, but by [ordering constraints](/en/applications/chaining#ordering-components) declared in the document processors or their configuration. + +## Execution model + +The Document Processing Framework works like this: + + + + A thread from the message bus layer appends an incoming message to an internal priority queue, shared between all document processing chains configured on a node. The priority is set based on the message bus priority of the message. Messages of the same priority are ordered FIFO. + + + + One worker thread from the docproc thread pool picks one message from the head of the queue, deserializes it, copies the call stack (chain) in question, and runs it through the document processors. + + + + Processing finishes if **(a)** the document(s) has passed successfully through the whole chain, or **(b)** a document processor in the chain has returned `Progress.FAILED` or thrown an exception. + + + + The same thread passes the message on to the message bus layer for further transport on to its destination. + + + + +There is a single instance of each document processor chain. In every chain, there is a single instance of each document processor - unless a chain is configured with multiple, identical document processors - this is a rare case. + +As is evident from the model above, multiple worker threads execute the document processors in a chain concurrently. Thus, many threads of execution can be going through `process()` in a document processor, at the same time. + +This model places an important constraint on document processor classes: *instance variables are not safe.* They must be eliminated, or made thread-safe somehow. + +Also see [Resource management](/en/applications/components#resource-management), use `deconstruct()` in order to not leak resources. + +### Asynchronous execution + +The execution model outlined above also shows one important restriction: If a document processor performs any high-latency operation in its process() method, a docproc worker thread will be occupied. With all *n* worker threads blocking on an external resource, throughput will be limited. This can be fixed by saving the state in the Processing object, and returning `Progress.LATER`. A document processor doing a high-latency operation should use a pattern like this: + + + + Check a self-defined context variable in Processing for status. Basically, *have we seen this Processing before?* + + + + If no: + + 1. We have been given a Processing object fresh off the network, we have not seen this before. Process it up until the high-latency operation. + 2. Start the high-latency operation (possibly in a separate thread). + 3. Save the state of the operation in a self-defined context variable in the Processing. + 4. Return `Progress.LATER`. This Processing is the appended to the back of the input queue, and we will be called again later. + + + + If yes: + + 1. Retrieve the reference that we set in our self-defined context variable in Processing. + 2. Is the high-latency operation done? If so, return `Progress.DONE`. + 3. Is it not yet done? Return `Progress.LATER` again. + + + +As is evident, this will let the finite set of document processing threads to do more work at the same time. + +## State + +Any state in the document processor for the particular Processing should be kept as local variables in the process method, while state which should be shared by all Processings should be kept as member variables. As the latter kind will be accessed by multiple threads at any one time, the state of such member variables must be *thread-safe*. This critical restriction is similar to those of e.g. the Servlet API. Options for implementing a multithread-safe document processor with instance variables: + +1. Use immutable (and preferably final) objects: they never change after they are constructed; no modifications to their state occurs after the DocumentProcessor constructor returns. +2. Use a single instance of a thread-safe class. +3. Create a single instance and synchronize access to it across all threads (but this will severely limit scalability). +4. Arrange for each thread to have its own instance, e.g. with a `ThreadLocal`. + +### Processing Context Variables + +`Processing` has a map `String -> Object` that can be used to pass information between document processors. It is also useful when using `Progress.LATER` to save the state of a processing - see [Processing.java](https://github.com/vespa-engine/vespa/blob/master/docproc/src/main/java/com/yahoo/docproc/Processing.java) for `get/setVariable` and more. + +The [sample application](https://github.com/vespa-engine/sample-apps/tree/master/examples/document-processing) uses such context variables, too. + +## Operation ordering + +### Feed ordering + +Ordering of feed operations is not guaranteed. Operations on different documents will be done concurrently and are therefore not ordered. However, Vespa guarantees that operations on the same document are processed in the order they were fed if they enter vespa at the *same* feed endpoint. + +### Document processing ordering + +Document operations that are produced inside a document processor obey the same rules as at feed time. If you either split the input into other documents or into multiple operations to the same document, Vespa will ensure that operations to the same document ID are sequenced and are delivered in the order they enter. + +## (Re)configuring Document Processing + +Consider the following configuration: + +```xml highlight={7-9} + + + + + + + + value + + + + + + +``` + +Changing chain ids, components in a chain, component configuration and schema mapping all takes effect after deployment - no restart required. Changing a *cluster name* (i.e. the container id) requires a restart of docproc services after *vespa activate*. + +Note when adding or modifying a processing chain in a running cluster; if at the same time deploying a *new* document processor (i.e. a document processor that was unknown to Vespa at the time the cluster was started), the container must be restarted: + +```txt +$ vespa-sentinel-cmd restart container +``` + +## Class diagram + + +![](/assets/img/document-processing-class-diagram.svg) + + +The framework core supports asynchronous processing, processing one or multiple documents or document updates at the same time, document processors that makes dynamic decisions about the processing flow and passing of information between processors outside the document or document update: + +- One or more named `Docproc Services` may be created. One of the services is the *default*. +- A service accepts subclasses of `DocumentOperation` for processing, meaning `DocumentPuts`, `DocumentUpdates` and `DocumentRemoves`. It has a `Call Stack` which lists the calls to make to various `DocumentProcessors` to process each DocumentOperation handed to the service. +- Call Stacks consist of `Calls`, which refer to the Document Processor instance to call. +- Document puts and document updates are processed asynchronously, the state is kept in a `Processing` for its duration (instead of in a thread or process). A Document Processor may make some asynchronous calls (typically to remote services) and return to the framework that it should be called again later for the same Processing to handle the outcome of the calls. +- A processing contains its own copy of the Call Stack of the Docproc Service to keep track of what to call next. Document Processors may modify this Call Stack to dynamically decide the processing steps required to process a DocumentOperation. +- A Processing may contain one or more DocumentOperations to be processed as a unit. +- A Processing has a `context`, which is a Map of named values which can be used to pass arguments between processors. +- Processings are prepared to be stored to disk, to allow a high number of ongoing long-term processings per node. diff --git a/en/applications/http-servers-and-filters.html b/en/applications/http-servers-and-filters.html deleted file mode 100644 index 81533bc18a..0000000000 --- a/en/applications/http-servers-and-filters.html +++ /dev/null @@ -1,264 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Http servers and filters" -redirect_from: -- /en/jdisc/http-server-and-filters.html ---- - -

    - This document explains how to set up http servers and filters in the Container. - Before proceeding, familiarize with the Developer Guide. -

    - - - -

    Set up Http servers

    -

    - To accept http requests on e.g. port 8090, add an http section with a server to services.xml: -

    -
    {% highlight xml%}
    -
    -
    -    
    -      
    -    
    -
    -{% endhighlight %}
    -

    - To verify that the new server is running, check the default handler on the root path, - which will return a list of all http servers: -

    -
    -$ curl http://localhost:8090/
    -
    -

    - Adding an http section to services.xml - disables the default http server at port 8080. -

    -

    - Binding to privileged ports (< 1024) is supported. - Note that this only works when running as a standalone container, - and not when running as a Vespa cluster. -

    - - -

    Configure the HTTP Server

    -

    - Configuration settings for the server can be modified by setting values for the - jdisc.http.connector config inside the server element: -

    -
    {% highlight xml%}
    -
    -
    -    
    -        
    -            
    -                false
    -            
    -        
    -    
    -
    -{% endhighlight %}
    -

    - Note that it is not allowed to set the listenPort in the http-server config, - as it conflicts with the port that is set in the port attribute in the server element. - For a complete list of configuration fields that can be set, refer to the config definition schema in - - jdisc.http.connector.def. -

    - - -

    TLS

    -

    - TLS can be configured using either the ssl - or the ssl-provider element. -

    -
    {% highlight xml %}
    -
    -    
    -        
    -          
    -              /path/to/private-key.pem
    -              /path/to/certificate.pem
    -              /path/to/ca-certificates.pem
    -              want
    -              
    -                  TLS_AES_128_GCM_SHA256,
    -                  TLS_AES_256_GCM_SHA384,
    -                  TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
    -                  TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
    -              
    -              TLSv1.2,TLSv1.3
    -            
    -        
    -        
    -            
    -        
    -    
    -
    -{% endhighlight %}
    -

    - Refer to the - multinode-HA sample application for an example. -

    - - - -

    Set up Filter Chains

    -

    - There are two main types of filters: -

    -
      -
    • request filters
    • -
    • response filters
    • -
    -

    - Request filters run before the handler that processes the request, and response filters run after. - They are used for tasks such as authentication, error checking and modifying headers. -

    - - -

    Using Filter Chains

    -

    - Filter chains are set up by using the request-chain and - response-chain elements inside the - filtering element. - Example setting up two request filter chains, and one response filter chain: -

    -
    {% highlight xml %}
    -
    -    
    -        
    -
    -        
    -            
    -            
    -        
    -
    -        
    -            
    -        
    -
    -        
    -            
    -        
    -    
    -    
    -
    -{% endhighlight %}
    -

    - Filters that should be used in more than one chain, - must be defined directly in the filtering element, - as shown with request-filter1 in the example above. -

    -

    - To actually use a filter chain, add one or more URI bindings: -

    -
    {% highlight xml %}
    -
    -    
    -        
    -            
    -            http://*/*
    -        
    -
    -        
    -            
    -            http://*/*
    -        
    -    
    -    
    -
    -{% endhighlight %}
    -

    - These bindings say that both the request chain and the response chain - should be used when the request URI matches http://*/*. - So both a request filter chain and a response filter chain can be used on a single request. - However, only one request chain will be used if there are multiple request chains - that have a binding that matches a request. - And vice versa for response chains. - Refer to the - javadoc for information about which chain that will be used in such cases. -

    -

    - In order to bind a filter chain to a specific server, add the server port to the binding: -

    - -
    {% highlight xml %}
    -
    -    
    -    http://*:8080/*
    -    http://*:9000/*
    -
    -{% endhighlight %}
    - -

    - A request must match a filter chain if any filter is configured. A 403 response is returned for non-matching request. - This semantic can be disabled - see strict-mode. -

    - -

    Excluding Filters from an Inherited Chain

    -

    - Say you have a request filter chain that you are binding to most of your URIs. - Now, you want to run almost the same chain on another URI, but you need to exclude one of the filters. - This is done by adding excludes, which takes a space separated list of filter ids, - to the chain element. - Example where a security filter is excluded from an inherited chain for status.html: -

    -
    {% highlight xml %}
    -
    -    http://*/status.html
    -
    -{% endhighlight %}
    - - -

    Creating a custom Filter

    -

    - Create an application package - with artifactId filter-bundle. - Create a new file filter-bundle/components/src/main/java/com/yahoo/demo/TestRequestFilter.java: -

    -
    {% highlight java %}
    -package com.yahoo.demo;
    -
    -import com.yahoo.jdisc.*;
    -import com.yahoo.jdisc.handler.*;
    -import com.yahoo.jdisc.http.*;
    -import com.yahoo.jdisc.http.filter.RequestFilter;
    -
    -import java.net.*;
    -import java.nio.ByteBuffer;
    -
    -public class TestRequestFilter extends AbstractResource implements RequestFilter  {
    -    @Override
    -    public void filter(HttpRequest httpRequest, ResponseHandler responseHandler) {
    -        if (isLocalAddress(httpRequest.getRemoteAddress())) {
    -            rejectRequest(httpRequest, responseHandler);
    -        } else {
    -            httpRequest.context().put("X-NOT-LOCALHOST", "true");
    -        }
    -    }
    -
    -    private boolean isLocalAddress(SocketAddress socketAddress) {
    -        if (socketAddress instanceof InetSocketAddress) {
    -            InetAddress address = ((InetSocketAddress)socketAddress).getAddress();
    -            return address.isAnyLocalAddress() || address.isLoopbackAddress();
    -        } else {
    -            return false;
    -        }
    -    }
    -
    -    private void rejectRequest(HttpRequest request, ResponseHandler responseHandler) {
    -        HttpResponse response = HttpResponse.newInstance(request, Response.Status.FORBIDDEN);
    -        ContentChannel channel = responseHandler.handleResponse(response);
    -        channel.write(ByteBuffer.wrap("Not accessible by localhost.".getBytes()), null);
    -        channel.close(null);
    -    }
    -}
    -{% endhighlight %}
    -

    - Build a bundle, and place it in the - application package's components directory. -

    diff --git a/en/applications/http-servers-and-filters.mdx b/en/applications/http-servers-and-filters.mdx new file mode 100644 index 0000000000..9960cca6e9 --- /dev/null +++ b/en/applications/http-servers-and-filters.mdx @@ -0,0 +1,199 @@ +--- +title: "Http servers and filters" +description: "This document explains how to set up http servers and filters in the Container. Before proceeding, familiarize with the [Developer Guide](/en/applications/developer-guide)." +--- + +## Set up Http servers + +To accept http requests on e.g. port 8090, add an `http` section with a server to *services.xml*: + +```xml + + + + + + +``` + +To verify that the new server is running, check the default handler on the root path, which will return a list of all http servers: + +```txt +$ curl http://localhost:8090/ +``` + +Adding an `http` section to *services.xml* **disables the default http server** at port 8080. + +Binding to privileged ports (< 1024) is supported. Note that this **only** works when running as a standalone container, and **not** when running as a Vespa cluster. + +### Configure the HTTP Server + +Configuration settings for the server can be modified by setting values for the `jdisc.http.connector` config inside the `server` element: + +```xml + + + + + + false + + + + +``` + +Note that it is not allowed to set the `listenPort` in the http-server config, as it conflicts with the port that is set in the *port* attribute in the *server* element. For a complete list of configuration fields that can be set, refer to the config definition schema in [jdisc.http.connector.def](https://github.com/vespa-engine/vespa/blob/master/container-disc/src/main/resources/configdefinitions/jdisc.http.jdisc.http.connector.def). + +### TLS + +TLS can be configured using either the [ssl](/en/reference/applications/services/http#ssl) or the [ssl-provider](/en/reference/applications/services/http#ssl-provider) element. + +```xml + + + + + /path/to/private-key.pem + /path/to/certificate.pem + /path/to/ca-certificates.pem + want + + TLS_AES_128_GCM_SHA256, + TLS_AES_256_GCM_SHA384, + TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, + TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 + + TLSv1.2,TLSv1.3 + + + + + + + +``` + +Refer to the [multinode-HA](https://github.com/vespa-engine/sample-apps/tree/master/examples/operations/multinode-HA) sample application for an example. + +## Set up Filter Chains + +There are two main types of filters: + +- request filters +- response filters + +Request filters run before the handler that processes the request, and response filters run after. They are used for tasks such as authentication, error checking and modifying headers. + +### Using Filter Chains + +Filter chains are set up by using the `request-chain` and `response-chain` elements inside the [filtering](/en/reference/applications/services/http#filtering) element. Example setting up two request filter chains, and one response filter chain: + +```xml + + + + + + + + + + + + + + + + + + + +``` + +Filters that should be used in more than one chain, must be defined directly in the `filtering` element, as shown with `request-filter1` in the example above. + +To actually use a filter chain, add one or more URI [bindings](/en/reference/applications/services/http#binding): + +```xml + + + + + http://*/* + + + + + http://*/* + + + + +``` + +These bindings say that both the request chain and the response chain should be used when the request URI matches `http://*/*`. So both a request filter chain and a response filter chain can be used on a single request. However, only one request chain will be used if there are multiple request chains that have a binding that matches a request. And vice versa for response chains. Refer to the [javadoc](https://javadoc.io/doc/com.yahoo.vespa/jdisc_core/latest/com/yahoo/jdisc/application/UriPattern.html) for information about which chain that will be used in such cases. + +In order to bind a filter chain to a specific *server*, add the server port to the binding: + +```xml + + + http://*:8080/* + http://*:9000/* + +``` + +A request must match a filter chain if any filter is configured. A 403 response is returned for non-matching request. This semantic can be disabled - see [strict-mode](/en/reference/applications/services/http#filtering). + +#### Excluding Filters from an Inherited Chain + +Say you have a request filter chain that you are binding to most of your URIs. Now, you want to run almost the same chain on another URI, but you need to exclude one of the filters. This is done by adding `excludes`, which takes a space separated list of filter ids, to the [chain element](/en/reference/applications/services/http#chain). Example where a security filter is excluded from an inherited chain for *status.html*: + +```xml + + http://*/status.html + +``` + +### Creating a custom Filter + +Create an [application package](/en/applications/developer-guide) with artifactId `filter-bundle`. Create a new file `filter-bundle/components/src/main/java/com/yahoo/demo/TestRequestFilter.java`: + +```java expandable +package com.yahoo.demo; +import com.yahoo.jdisc.*; +import com.yahoo.jdisc.handler.*; +import com.yahoo.jdisc.http.*; +import com.yahoo.jdisc.http.filter.RequestFilter; +import java.net.*; +import java.nio.ByteBuffer; +public class TestRequestFilter extends AbstractResource implements RequestFilter { + @Override + public void filter(HttpRequest httpRequest, ResponseHandler responseHandler) { + if (isLocalAddress(httpRequest.getRemoteAddress())) { + rejectRequest(httpRequest, responseHandler); + } else { + httpRequest.context().put("X-NOT-LOCALHOST", "true"); + } +} +private boolean isLocalAddress(SocketAddress socketAddress) { + if (socketAddress instanceof InetSocketAddress) { + InetAddress address = ((InetSocketAddress)socketAddress).getAddress(); + return address.isAnyLocalAddress() || address.isLoopbackAddress(); + } else { + return false; +} +} +private void rejectRequest(HttpRequest request, ResponseHandler responseHandler) { + HttpResponse response = HttpResponse.newInstance(request, Response.Status.FORBIDDEN); + ContentChannel channel = responseHandler.handleResponse(response); + channel.write(ByteBuffer.wrap("Not accessible by localhost.".getBytes()), null); + channel.close(null); +} +} +``` + +Build a bundle, and place it in the [application package](/en/basics/applications)'s *components* directory. diff --git a/en/applications/ide-support.html b/en/applications/ide-support.html deleted file mode 100644 index 2bdc47168f..0000000000 --- a/en/applications/ide-support.html +++ /dev/null @@ -1,22 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "IDE support" -redirect_from: -- /en/ide-support.html ---- - -

    Vespa provides plugins for working with schemas and rank profiles in IDE's: - -

    - -

    If you are working with non-trivial Vespa applications, installing a plugin is highly recommended!

    - -IDE demo - - - diff --git a/en/applications/ide-support.mdx b/en/applications/ide-support.mdx new file mode 100644 index 0000000000..28399ef86d --- /dev/null +++ b/en/applications/ide-support.mdx @@ -0,0 +1,15 @@ +--- +title: "IDE support" +description: "Vespa provides plugins for working with schemas and rank profiles in IDE's:" +--- + +- VSCode: [VS Code extension](https://marketplace.visualstudio.com/items?itemName=vespaai.vespa-language-support) +- Cursor, code-server and other VS Code compatible IDEs: [VS Code extension in Open VSX registry](https://open-vsx.org/extension/vespaai/vespa-language-support) +- IntelliJ, PyCharm or WebStorm: [Jetbrains plugin](https://plugins.jetbrains.com/plugin/18074-vespa-schema-language-support) +- Vim: [neovim](https://blog.vespa.ai/interns-languageserver/#neovim-plugin) + +If you are working with non-trivial Vespa applications, installing a plugin is highly recommended! + + +![](/assets/img/ide.gif) + diff --git a/en/applications/inspecting-structured-data.html b/en/applications/inspecting-structured-data.mdx similarity index 59% rename from en/applications/inspecting-structured-data.html rename to en/applications/inspecting-structured-data.mdx index a73f6b68ce..4fbcc40ea7 100644 --- a/en/applications/inspecting-structured-data.html +++ b/en/applications/inspecting-structured-data.mdx @@ -1,31 +1,20 @@ --- -# Copyright Vespa.ai. All rights reserved. title: "Inspecting structured data in a Searcher" -redirect_from: -- /en/reference/inspecting-structured-data.html +description: "The [Data Access API](https://javadoc.io/doc/com.yahoo.vespa/vespajlib/latest/com/yahoo/data/access/package-summary) is used to access structured data such as arrays and weighted sets." --- -

    - The - Data Access API is used to access structured data such as arrays and weighted sets. -

    +## Use Case: accessing array attributes +The following illustrates accessing some field that is of array type: - -

    Use Case: accessing array attributes

    -

    - The following illustrates accessing some field that is of array type: -

    -
    {% highlight java %}
    +```java expandable
     import com.yahoo.search.*;
     import com.yahoo.search.result.*;
     import com.yahoo.search.searchchain.*;
     import com.yahoo.data.access.*;
    -
     @After(PhaseNames.TRANSFORMED_QUERY)
     @Before(PhaseNames.BLENDED_RESULT)
     public class SimpleTestSearcher extends Searcher {
    -
         public Result search(Query query, Execution execution) {
             Result r = execution.search(query);
             execution.fill(r);
    @@ -48,49 +37,43 @@ 

    Use Case: accessing array attribute return r; } } -{% endhighlight %}

    -

    Here we assume there is a field in our schema like this:

    -
    -field titles type array<string> {
    -    indexing: attribute | summary
    +```
    +
    +Here we assume there is a field in our schema like this:
    +
    +```text
    +field titles type array {
    +indexing: attribute | summary
     }
    -
    -

    - Again we process each hit, this time traversing the array and building a string which contains all the titles, - transforming a field looking like this: -

    -
    {% highlight json %}
    - "titles": [
    -            "Bond",
    -            "James Bond"
    +```
    +
    +Again we process each hit, this time traversing the array and building a string which contains all the titles, transforming a field looking like this:
    +
    +```json
    +"titles": [
    +"Bond",
    +"James Bond"
     ]
    -{% endhighlight %}
    -

    into this output:

    -
    {% highlight json %}
    +```
    +
    +into this output:
    +
    +```json
     "titles": "Bond, James Bond"
    -{% endhighlight %}
    +``` +## Use Case: accessing weighted set attributes +The following example illustrates accessing data held in a weighted set. Note that the Data Access API doesn't have a "set" or "weighted set" concept; the weighted set is represented as an unordered array of objects where each object has an "item" and a "weight" field. The weight is a long integer value, while the item type will vary according to the field type as declared in the schema. -

    Use Case: accessing weighted set attributes

    -

    - The following example illustrates accessing data held in a weighted set. - Note that the Data Access API doesn't have a "set" or "weighted set" concept; - the weighted set is represented as an unordered array of objects - where each object has an "item" and a "weight" field. - The weight is a long integer value, - while the item type will vary according to the field type as declared in the schema. -

    -
    {% highlight java %}
    +```java expandable
     import com.yahoo.search.*;
     import com.yahoo.search.result.*;
     import com.yahoo.search.searchchain.*;
     import com.yahoo.data.access.*;
    -
     @After(PhaseNames.TRANSFORMED_QUERY)
     @Before(PhaseNames.BLENDED_RESULT)
     public class SimpleTestSearcher extends Searcher {
    -
         public Result search(Query query, Execution execution) {
             Result r = execution.search(query);
             execution.fill(r);
    @@ -99,7 +82,6 @@ 

    Use Case: accessing weighted } return r; } - void processHit(Hit hit) { if (hit.isMeta()) return; Object o = hit.getField("titles"); @@ -122,56 +104,53 @@

    Use Case: accessing weighted hit.setField("alternates", pasteBuf.toString()); } } - } -{% endhighlight %}

    -

    Here we assume there is a field in the schema like:

    -
    -field titles type weightedset<string> {
    -    indexing: attribute | summary
    +```
    +
    +Here we assume there is a field in the schema like:
    +
    +```txt
    +field titles type weightedset {
    +indexing: attribute | summary
     }
    -
    -

    Again we process each hit, and format each element of the weighted set, transforming this input:

    -
    {% highlight json %}
    +```
    +
    +Again we process each hit, and format each element of the weighted set, transforming this input:
    +
    +```json
     "titles": {
    -            "Bond": 15,
    -            "James Bond": 89
    +"Bond": 15,
    +"James Bond": 89
     }
    -{% endhighlight %}
    -

    into this output:

    -
    {% highlight json %}
    +```
    +
    +into this output:
    +
    +```json
     "alternates": "title: Bond[15], title: James Bond[89]"
    -{% endhighlight %}
    +``` +## Unit testing with structured data +For unit testing it is useful to be able to create structured data fields programmatically. This case be done using `Slime`: -

    Unit testing with structured data

    -

    -For unit testing it is useful to be able to create structured data fields programmatically. -This case be done using Slime: -

    -
    {% highlight java %}
    +```java
     import com.yahoo.slime.*;
     import com.yahoo.data.access.slime.SlimeAdapter;
    -
    -
     // Struct example:
     Slime slime = new Slime();
     Cursor struct = slime.setObject();
     struct.setString("foo", "bar");
     struct.setDouble("number", 1.0);
     myHit.setField("mystruct", new SlimeAdapter(struct));
    -
     // Array example:
     Slime slime = new Slime();
     Cursor array = slime.setArray();
     array.addString("foo");
     array.addString("bar");
     myHit.setField("myarray", new SlimeAdapter(array));
    -
     // Arrays and objects can be arbitrarily nested
    -
     // Alternatively, create the slime structure from a JSON string:
     Slime slime = SlimeUtils.jsonToSlime(myJsonString.getBytes(StandardCharsets.UTF_8));
     myHit.setField("myfield", new SlimeAdapter(slime.get()));
    -{% endhighlight %}
    +``` diff --git a/en/applications/pluggable-frameworks.html b/en/applications/pluggable-frameworks.html deleted file mode 100644 index 3318e943fd..0000000000 --- a/en/applications/pluggable-frameworks.html +++ /dev/null @@ -1,79 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Using pluggable frameworks" -redirect_from: -- /en/jdisc/pluggable-frameworks.html ---- - -

    - Many libraries provide pluggable architectures via Service Provider Interfaces (SPI). - Using such libraries usually requires some manual setup in the application package. -

    - - - -

    Troubleshooting and Configuring the Application

    -

    - Libraries for pluggable frameworks rely on loading classes dynamically at runtime, - usually via Class.forName("…"). - If the package of the class that is loaded is not imported by our user bundle, - this will result in the following error: -

    -
    -java.lang.ClassNotFoundException: com.sun.imageio.plugins.jpeg.JPEGImageReaderSpi not found by my-bundle [29]
    -    at
    -org.apache.felix.framework.BundleWiringImpl.findClassOrResourceByDelegation(BundleWiringImpl.java:1532)
    -
    -

    - The example above is from using the - Image I/O framework. - In this case, notice that the missing class is from a com.sun package, - which is available in the SDK. -

    - - -

    Importing the Missing Package

    -

    - The ClassNotFoundException means that the bundle is not importing the package. - The bundle-plugin will usually not have added an import - since the class is only referred to from a string in a Class.forName("…") statement. - Hence, add an explicit importPackage in the bundle's pom.xml: -

    -
    -<build>
    -    <plugins>
    -        <plugin>
    -          <groupId>com.yahoo.vespa</groupId>
    -          <artifactId>bundle-plugin</artifactId>
    -          ...
    -          <configuration>
    -              <importPackage>com.sun.imageio.plugins.jpeg</importPackage>
    -              ...
    -          </configuration>
    -        </plugin>
    -    </plugins>
    -</build>
    -
    -

    -The importPackage configuration option takes a comma-separated list of packages. -Adding multiple importPackage elements in pom.xml means that only one of them will take effect. -

    - - -

    Exporting the Missing Package from the Container

    -

    -As mentioned, the missing package in this example is part of the SDK. -In these cases, we must tell the Container to export the missing package. -When running in cluster mode, -this is done in services.xml: -

    -
    -<container version="1.0">
    -    <config name="search.config.qr-start">
    -        <jdisc>
    -            <export_packages>com.sun.imageio.plugins.jpeg</export_packages>
    -        </jdisc>
    -    </config>
    -    ...
    -</container>
    -
    diff --git a/en/applications/pluggable-frameworks.mdx b/en/applications/pluggable-frameworks.mdx new file mode 100644 index 0000000000..47221b61ee --- /dev/null +++ b/en/applications/pluggable-frameworks.mdx @@ -0,0 +1,53 @@ +--- +title: "Using pluggable frameworks" +description: "Many libraries provide pluggable architectures via Service Provider Interfaces (SPI)." +--- + +## Troubleshooting and Configuring the Application + +Libraries for pluggable frameworks rely on loading classes dynamically at runtime, usually via `Class.forName("…")`. If the package of the class that is loaded is not imported by our user bundle, this will result in the following error: + +```java +java.lang.ClassNotFoundException: com.sun.imageio.plugins.jpeg.JPEGImageReaderSpi not found by my-bundle [29] +at +org.apache.felix.framework.BundleWiringImpl.findClassOrResourceByDelegation(BundleWiringImpl.java:1532) +``` + +The example above is from using the [Image I/O framework](https://docs.oracle.com/javase/6/docs/technotes/guides/imageio/). In this case, notice that the missing class is from a `com.sun` package, which is available in the SDK. + +### Importing the Missing Package + +The `ClassNotFoundException` means that the bundle is not importing the package. The [bundle-plugin](/en/applications/bundles#maven-bundle-plugin) will usually not have added an import since the class is only referred to from a string in a `Class.forName("…")` statement. Hence, add an explicit `importPackage` in the bundle's pom.xml: + +```xml highlight={8} + + + + com.yahoo.vespa + bundle-plugin + ... + + com.sun.imageio.plugins.jpeg + ... + + + + +``` + +The `importPackage` configuration option takes a comma-separated list of packages. Adding multiple `importPackage` elements in pom.xml means that only one of them will take effect. + +### Exporting the Missing Package from the Container + +As mentioned, the missing package in this example is part of the SDK. In these cases, we must tell the Container to export the missing package. When running in [cluster mode](/en/operations/self-managed/multinode-systems#aws-ecs), this is done in `services.xml`: + +```xml + + + + com.sun.imageio.plugins.jpeg + + + ... + +``` diff --git a/en/applications/processing.html b/en/applications/processing.html deleted file mode 100644 index b016be20f0..0000000000 --- a/en/applications/processing.html +++ /dev/null @@ -1,434 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Request-Response Processing" -redirect_from: -- /en/jdisc/processing.html ---- - -

    -Processing makes it easy to create low-latency -request/response processing applications. It is the recommended way -of creating such applications on top of JDisc, but can also be used independently of JDisc. -Processing lets you define application behavior by combining Processors performing simple tasks. -Processors use a synchronous call model, but the underlying IO may be asynchronous. -

    -Javadoc:
    -com.yahoo.processing.Processor
    -com.yahoo.processing.rendering.Renderer -

    - - - -

    Using processing

    -

    -To use processing, add this dependency to pom.xml: -

    -<dependency>
    -  <groupId>com.yahoo.vespa</groupId>
    -  <artifactId>container</artifactId>
    -  <version>{{site.variables.vespa_version}}</version> <!-- Find latest version at search.maven.org/search?q=g:com.yahoo.vespa%20a:container -->
    -  <scope>provided</scope>
    -</dependency>
    -
    -

    -Or read how to start a deployable project from scratch. -

    - - - -

    Processors

    -

    -A processor subclasses Processor and implements a single method: -

    -
    -package com.mydomain.example;
    -
    -import com.yahoo.processing.*;
    -import com.yahoo.processing.execution.Execution;
    -import com.yahoo.processing.test.ProcessorLibrary.StringData;
    -
    -public class ExampleProcessor extends Processor {
    -
    -    @Override
    -    public Response process(Request request, Execution execution) {
    -        // Process the Request:
    -        request.properties().set("foo","bar");
    -
    -        // Pass on to the next processor in the chain
    -        Response response=execution.process(request);
    -
    -        // process the response
    -        response.data().add(new StringData(request,"Hello, world!"));
    -
    -        return response;
    -    }
    -
    -}
    -
    -

    -Processors may work on both the request and response, pass on the -request one or more times to further processors or create the result -data internally or by contacting a remote service. The result data may -be a nested composite structure where content is contributed by -multiple processors. -

    - - - -

    Chaining Processors

    -

    -Processors should carry out a single task and are combined into complete -applications. This is achieved using Chains: -

    -
    -Chain<Processor> myChain=new Chain<Processor>(new ExampleProcessor(),
    -                                              new FooProcessor(),
    -                                              new BarProcessor());
    -Response response=new Execution(myChain).process(request); // execute this chain
    -
    -

    -This executes the three processors in order. The Execution keeps track -of the execution state so the same processor instances may be used in -many chains at the same time. When the execution reaches the end of -the chain, the execution returns an empty Response to the processor -calling it. An AsyncExecution class is provided as a convenience to -perform an execution in a separate thread instead. -

    -In most cases it is more convenient to configure chains and processor -instances using external configuration. Chains of processors may be -specified in -a processing -element in -the services.xml -file in the application package. The compiled processors are added to -the application package as -OSGi components. Chain -configuration allows chains to be defined as sets of -processors with ordering constraints, such that the global ordering of -processors can be figured out by the framework, and set operations con -chains can be used to define extensions and variants of chains. -

    - - - -

    Asynchronous Results

    -

    -In some cases it is useful to return a Response before all the data in -it is available. This allows returning a partial response to clients -with low latency even though the complete response contains some data -arriving more slowly. The slow data can be added to the Response as a -placeholder where actual data will arrive later. The processing -framework allows waiting or listening for such completion events as -Guava ListenableFutures. -

    -If all data is added to the Response as future placeholders -the processing framework becomes completely non-blocking. -

    - - - -

    Dependency Injection

    -

    -Processors in real applications will typically depend on some -configuration and/or other components to run. Such dependencies -should be declared as straightforward constructor arguments to allow -them to be injected at construction time. -

    -The container runtime used to host the processing framework uses a -dependency injection framework based in Guice, see -container components. -

    -As a processor may participate in many processing executions at one -time, field values in a processing class should usually be immutable -after construction is completed. -

    - - - -

    Response Rendering

    -

    -A Renderer is used to serialize the Response for return to a -client. Renderers are subclasses of -com.yahoo.processing.rendering.Renderer. A convenience -superclass which handles waiting for future data in the asynchronous -case is provided as -com.yahoo.processing.rendering.AsynchronousSectionedRenderer. -The default renderer, which renders in a simple JSON format is -com.yahoo.processing.rendering.ProcessingRenderer -and can be subclassed to customize rendering of each kind of Data item. -

    -Processors are -regular components which are -added to the application package in -the renderer -section of the services.xml file. A renderer is selected -in the request by setting the format parameter in the request -to the renderer id. -

    - - - -

    Subclassing of Processing

    -

    -The Processing framework is meant to be generic and minimal. In some -domains it is useful to employ a richer model of Processors, Requests, -Responses and Executions targeted to that domain. An example is the -Search -domain, where Searchers, Queries and Results subclass -Processors, Requests and Responses. -The Processing framework is designed to allow such subclassing to build richer frameworks on top. -

    - - - -

    Testing Processors with an Application

    -

    -A processor can be tested running inside a container. -We create a JDisc from services.xml: -

    -
    -import com.yahoo.application.container.JDisc;
    -import com.yahoo.application.Networking;
    -
    -import com.yahoo.processing.Request;
    -import com.yahoo.processing.Response;
    -
    -import com.yahoo.component.ComponentSpecification;
    -
    -import org.junit.Test;
    -
    -import static org.junit.Assert.assertThat;
    -import static org.junit.matchers.JUnitMatchers.containsString;
    -
    -public class ContainerTest {
    -    @Test
    -    public void testSearch() {
    -        String servicesXml =
    -                "<container version=\"1.0\">" +
    -                "  <processing>" +
    -                "    <chain id=\"default\">" +
    -                "      <processor id=\"com.mydomain.example.ExampleProcessor\" />" +
    -                "    </chain>" +
    -                "  </processing>" +
    -                "</container>";
    -        try (JDisc container = JDisc.fromServicesXml(servicesXml, Networking.disable)) {
    -            Response response = container.processing().process(ComponentSpecification.fromString("default"), new Request());
    -            assertThat(response.data().get(0).toString(), containsString("Hello, world!"));
    -        }
    -
    -    }
    -}
    -
    -

    -We can also examine which processors are in a chain and their ordering: -

    -
    -ChainRegistry<Processor> chains = container.processing().getChains();
    -Chain<Processor> defaultChain = chains.getComponent("default");
    -
    -boolean foundExampleProcessor = false;
    -for (Processor processor: defaultChain.components()) {
    -    if ("ExampleProcessor".equals(processor.getClassName()))
    -        foundExampleProcessor = true;
    -}
    -
    -assertTrue("No instance of ExampleProcessor found in the default chain", foundExampleProcessor)
    -
    - - - -

    Selecting a Non-default Processor Chain

    -

    -A complete application will usually be composed of several processor chains, -which may or may not invoke each other. To select a chain configured with -another id than "default", add the chain ID as a GET -parameter named chain. -

    -In other words, given a chain named "testbed", as in: -

    -
    -<container version="1.0">
    -  <processing>
    -    <chain id="testbed">
    -      <processor id="com.yahoo.example.ExampleProcessor" />
    -    </chain>
    -  </processing>
    -</container>
    -
    -

    The chain testbed could be tested from the command line by doing:

    -
    -$ curl http://hostname:port/processing/?chain=testbed
    -
    - - - -

    References

    - - - - -

    Common tasks with processing

    -

    -This section contains a collection of "how do I" explanations with processing. -Most of these pertains to the jDisc binding of Processing, but note that Processing is independent of -jDisc and may be invoked programmatically in any environment. -

    - - -

    Accessing the HTTP request from Processors

    -

    -Processors which interface with the network layer may need to access the network level -request to access headers or request data, or to make outgoing calls through jDisc. -The jDisc request is available through request properties: -

    -
    -httpRequest = (com.yahoo.container.jdisc.HttpRequest)processingRequest.properties().get("jdisc.request");
    -
    - - -

    Setting response headers from Processors

    -

    -Response headers may be added to any Response by adding instances of -com.yahoo.processing.handler.ResponseHeaders to the Response -(ResponseHeaders is a kind of response Data). -Multiple instances of this may be added to the Response, and the complete set of headers returned -is the superset of all such objects. Example Processor: -

    -
    -import com.yahoo.processing.Processor;
    -import com.yahoo.processing.Request;
    -import com.yahoo.processing.Response;
    -import com.yahoo.processing.handler.ResponseHeaders;
    -import com.yahoo.processing.execution.Execution;
    -
    -import java.util.Collections;
    -import java.util.Map;
    -import java.util.List;
    -
    -public class ResponseHeaderSetter extends Processor {
    -
    -   private final Map<String,List<String>> responseHeaders;
    -
    -   public ResponseHeaderSetter(Map<String,List<String>> responseHeaders) {
    -       this.responseHeaders = Collections.unmodifiableMap(responseHeaders);
    -   }
    -
    -   @Override
    -   public Response process(Request request, Execution execution) {
    -       Response response = execution.process(request);
    -       response.data().add(new ResponseHeaders(responseHeaders, request));
    -       return response;
    -   }
    -
    -}
    -
    - - - -

    Example Processors

    -

    -This section lists a few example processors which shows some use cases -for the asynchronous aspects of the API. -

    -
    -import com.yahoo.component.chain.Chain;
    -import com.yahoo.processing.Processor;
    -import com.yahoo.processing.Request;
    -import com.yahoo.processing.Response;
    -import com.yahoo.processing.execution.AsyncExecution;
    -import com.yahoo.processing.execution.Execution;
    -import com.yahoo.processing.response.FutureResponse;
    -
    -import java.util.*;
    -
    -/**
    - * Call a number of chains in parallel
    - */
    -public class Federator extends Processor {
    -
    -    private final List<Chain<? extends Processor>> chains;
    -
    -    public Federator(Chain<? extends Processor> … chains) {
    -        this.chains= Arrays.asList(chains);
    -    }
    -
    -    @Override
    -    public Response process(Request request, Execution execution) {
    -        List<FutureResponse> futureResponses=new ArrayList<FutureResponse>(chains.size());
    -        for (Chain<? extends Processor> chain : chains) {
    -            futureResponses.add(new AsyncExecution(chain,execution).process(request));
    -        }
    -        Response response=execution.process(request);
    -        AsyncExecution.waitForAll(futureResponses,1000);
    -        for (FutureResponse futureResponse : futureResponses) {
    -            Response federatedResponse=futureResponse.get();
    -            response.data().add(federatedResponse.data());
    -            response.mergeWith(federatedResponse);
    -        }
    -        return response;
    -    }
    -}
    -
    -
    -import com.yahoo.processing.*;
    -import com.yahoo.processing.execution.Execution;
    -import com.yahoo.processing.response.*;
    -import com.yahoo.processing.test.ProcessorLibrary.StringData;
    -
    -/**
    - * A data producer which producer data which will receive asynchronously.
    - * This is not a realistic, thread safe implementation as only the incoming data
    - * from the last created incoming data can be completed.
    - */
    -public class AsyncDataProducer extends Processor {
    -
    -    private IncomingData incomingData;
    -
    -    @Override
    -    public Response process(Request request, Execution execution) {
    -        DataList dataList = ArrayDataList.createAsync(request); // Default implementation
    -        incomingData=dataList.incoming();
    -        return new Response(dataList);
    -    }
    -
    -    /** Called by some other data producing thread, later */
    -    public void completeLateData() {
    -        incomingData.addLast(new StringData(incomingData.getOwner().request(),
    -                                            "A late hello, world!"));
    -    }
    -
    -}
    -
    -
    -import com.google.common.util.concurrent.MoreExecutors;
    -import com.yahoo.component.chain.Chain;
    -import com.yahoo.processing.*;
    -import com.yahoo.processing.execution.*;
    -
    -/**
    - * A processor which registers a listener on the future completion of
    - * asynchronously arriving data to perform another chain at that point.
    - */
    -public class AsyncDataProcessingInitiator extends Processor {
    -
    -    private final Chain<Processor> asyncChain;
    -
    -    public AsyncDataProcessingInitiator(Chain<Processor> asyncChain) {
    -        this.asyncChain=asyncChain;
    -    }
    -
    -    @Override
    -    public Response process(Request request, Execution execution) {
    -        Response response=execution.process(request);
    -        response.data().complete().addListener(new RunnableExecution(request,
    -                new ExecutionWithResponse(asyncChain, response, execution)),
    -                MoreExecutors.sameThreadExecutor());
    -        return response;
    -    }
    -
    -}
    -
    diff --git a/en/applications/processing.mdx b/en/applications/processing.mdx new file mode 100644 index 0000000000..a298618554 --- /dev/null +++ b/en/applications/processing.mdx @@ -0,0 +1,325 @@ +--- +title: "Request-Response Processing" +description: "*Processing* makes it easy to create low-latency request/response processing applications. It is the recommended way of creating such applications on top of JDisc, but can also be used independently of JDisc. Processing lets you define application behavior by combining Processors performing simple tasks. Processors use a synchronous call model, but the underlying IO may be asynchronous." +--- + + +Javadoc: +[com.yahoo.processing.Processor](https://javadoc.io/doc/com.yahoo.vespa/processing/latest/com/yahoo/processing/Processor.html) +[com.yahoo.processing.rendering.Renderer](https://javadoc.io/doc/com.yahoo.vespa/processing/latest/com/yahoo/processing/rendering/Renderer.html) + +## Using processing + +To use processing, add this dependency to *pom.xml*: + +```xml + + com.yahoo.vespa + container + 8.689.26 {/* Find latest version at [search.maven.org/search?q=g:com.yahoo.vespa%20a:container](https://search.maven.org/search?q=g:com.yahoo.vespa%20a:container) */} + provided + +``` + +Or read [how to start a deployable project from scratch](/en/applications/developer-guide). + +## Processors + +A *processor* subclasses Processor and implements a single method: + +```java +package com.mydomain.example; + +import com.yahoo.processing.\*; +import com.yahoo.processing.execution.Execution; +import com.yahoo.processing.test.ProcessorLibrary.StringData; + +public class ExampleProcessor extends Processor { + + @Override + public Response process(Request request, Execution execution) { + // Process the Request: + request.properties().set("foo","bar"); + + // Pass on to the next processor in the chain + Response response=execution.process(request); + + // process the response + response.data().add(new StringData(request,"Hello, world!")); + + return response; + } + +} +``` + +Processors may work on both the request and response, pass on the request one or more times to further processors or create the result data internally or by contacting a remote service. The result data may be a nested composite structure where content is contributed by multiple processors. + +## Chaining Processors + +Processors should carry out a single task and are combined into complete applications. This is achieved using Chains: + +```java +Chain myChain=new Chain(new ExampleProcessor(), + new FooProcessor(), + new BarProcessor()); +Response response=new Execution(myChain).process(request); // execute this chain +``` + +This executes the three processors in order. The Execution keeps track of the execution state so the same processor instances may be used in many chains at the same time. When the execution reaches the end of the chain, the execution returns an empty Response to the processor calling it. An AsyncExecution class is provided as a convenience to perform an execution in a separate thread instead. + +In most cases it is more convenient to configure chains and processor instances using external configuration. Chains of processors may be specified in a [processing](/en/reference/applications/services/processing) element in the *[services.xml](/en/reference/applications/services/services)* file in the application package. The compiled processors are added to the application package as [OSGi components](/en/applications/components). Chain configuration allows chains to be defined as *sets* of processors with ordering constraints, such that the global ordering of processors can be figured out by the framework, and set operations con chains can be used to define extensions and variants of chains. + +## Asynchronous Results + +In some cases it is useful to return a Response before all the data in it is available. This allows returning a partial response to clients with low latency even though the complete response contains some data arriving more slowly. The slow data can be added to the Response as a placeholder where actual data will arrive later. The processing framework allows waiting or listening for such completion events as [Guava ListenableFutures.](https://guava.dev/releases/snapshot/api/docs/com/google/common/util/concurrent/ListenableFuture.html) + +If *all* data is added to the Response as future placeholders the processing framework becomes completely non-blocking. + +## Dependency Injection + +Processors in real applications will typically depend on some configuration and/or other components to run. Such dependencies should be declared as straightforward constructor arguments to allow them to be injected at construction time. + +The container runtime used to host the processing framework uses a dependency injection framework based in Guice, see [container components](/en/applications/components). + +As a processor may participate in many processing executions at one time, field values in a processing class should usually be immutable after construction is completed. + +## Response Rendering + +A *Renderer* is used to serialize the Response for return to a client. Renderers are subclasses of `com.yahoo.processing.rendering.Renderer`. A convenience superclass which handles waiting for future data in the asynchronous case is provided as `com.yahoo.processing.rendering.AsynchronousSectionedRenderer`. The default renderer, which renders in a simple JSON format is [com.yahoo.processing.rendering.ProcessingRenderer](https://github.com/vespa-engine/vespa/blob/master/container-disc/src/main/java/com/yahoo/processing/rendering/ProcessingRenderer.java) and can be subclassed to customize rendering of each kind of Data item. + +Processors are regular [components](/en/applications/components) which are added to the application package in the [renderer section](/en/reference/applications/services/processing#renderer) of the *services.xml* file. A renderer is selected in the request by setting the `format` parameter in the request to the renderer id. + +## Subclassing of Processing + +The Processing framework is meant to be generic and minimal. In some domains it is useful to employ a richer model of Processors, Requests, Responses and Executions targeted to that domain. An example is the [Search domain](/en/applications/searchers), where Searchers, Queries and Results subclass Processors, Requests and Responses. The Processing framework is designed to allow such subclassing to build richer frameworks on top. + +## Testing Processors with an Application + +A processor can be tested running inside a container. We create a JDisc from *services.xml*: + +```java expandable +import com.yahoo.application.container.JDisc; +import com.yahoo.application.Networking; + +import com.yahoo.processing.Request; +import com.yahoo.processing.Response; + +import com.yahoo.component.ComponentSpecification; + +import org.junit.Test; + +import static org.junit.Assert.assertThat; +import static org.junit.matchers.JUnitMatchers.containsString; + +public class ContainerTest { + @Test + public void testSearch() { + String servicesXml = + "" + + " " + + " " + + " " + + " " + + " " + + ""; + try (JDisc container = JDisc.fromServicesXml(servicesXml, Networking.disable)) { + Response response = container.processing().process(ComponentSpecification.fromString("default"), new Request()); + assertThat(response.data().get(0).toString(), containsString("Hello, world!")); + } + + } +} +``` + +We can also examine which processors are in a chain and their ordering: + +```java +ChainRegistry chains = container.processing().getChains(); +Chain defaultChain = chains.getComponent("default"); +boolean foundExampleProcessor = false; +for (Processor processor: defaultChain.components()) { + if ("ExampleProcessor".equals(processor.getClassName())) + foundExampleProcessor = true; +} + + +assertTrue("No instance of ExampleProcessor found in the default chain", foundExampleProcessor) +``` + +## Selecting a Non-default Processor Chain + +A complete application will usually be composed of several processor chains, which may or may not invoke each other. To select a chain configured with another `id` than "default", add the chain ID as a GET parameter named `chain`. + +In other words, given a chain named "testbed", as in: + +```xml + + + + + + + +``` + + +The chain testbed could be tested from the command line by doing: + +```bash +$ curl http://*hostname*:*port*/processing/?chain=testbed +``` + +## References + +- [Developing web services](/en/applications/web-services). +- [com.yahoo.processing](https://javadoc.io/doc/com.yahoo.vespa/processing/latest/com/yahoo/processing/package-summary.html) javadoc +- [Guava Javadoc](https://guava.dev/releases/snapshot/api/docs/). + +## Common tasks with processing + +This section contains a collection of "how do I" explanations with processing. Most of these pertains to the jDisc binding of Processing, but note that Processing is independent of jDisc and may be invoked programmatically in any environment. + +### Accessing the HTTP request from Processors + +Processors which interface with the network layer may need to access the network level request to access headers or request data, or to make outgoing calls through jDisc. The jDisc request is available through request properties: + +httpRequest = (com.yahoo.container.jdisc.HttpRequest)processingRequest.properties().get("jdisc.request"); + +### Setting response headers from Processors + +Response headers may be added to any Response by adding instances of `com.yahoo.processing.handler.ResponseHeaders` to the Response (ResponseHeaders is a kind of response Data). Multiple instances of this may be added to the Response, and the complete set of headers returned is the superset of all such objects. Example Processor: + +```java expandable +import com.yahoo.processing.Processor; +import com.yahoo.processing.Request; +import com.yahoo.processing.Response; +import com.yahoo.processing.handler.ResponseHeaders; +import com.yahoo.processing.execution.Execution; + +import java.util.Collections; +import java.util.Map; +import java.util.List; + +public class ResponseHeaderSetter extends Processor { + + private final Map> responseHeaders; + + public ResponseHeaderSetter(Map> responseHeaders) { + this.responseHeaders = Collections.unmodifiableMap(responseHeaders); + } + + @Override + public Response process(Request request, Execution execution) { + Response response = execution.process(request); + response.data().add(new ResponseHeaders(responseHeaders, request)); + return response; + } + +} +``` + +## Example Processors + +This section lists a few example processors which shows some use cases for the asynchronous aspects of the API. + +```java expandable +import com.yahoo.component.chain.Chain; +import com.yahoo.processing.Processor; +import com.yahoo.processing.Request; +import com.yahoo.processing.Response; +import com.yahoo.processing.execution.AsyncExecution; +import com.yahoo.processing.execution.Execution; +import com.yahoo.processing.response.FutureResponse; + +import java.util.\*; + +/\*\* + \* Call a number of chains in parallel + \*/ +public class Federator extends Processor { + + private final List> chains; + + public Federator(Chain … chains) { + this.chains= Arrays.asList(chains); + } + + @Override + public Response process(Request request, Execution execution) { + List futureResponses=new ArrayList(chains.size()); + for (Chain chain : chains) { + futureResponses.add(new AsyncExecution(chain,execution).process(request)); + } + Response response=execution.process(request); + AsyncExecution.waitForAll(futureResponses,1000); + for (FutureResponse futureResponse : futureResponses) { + Response federatedResponse=futureResponse.get(); + response.data().add(federatedResponse.data()); + response.mergeWith(federatedResponse); + } + return response; + } +} +``` + +```java expandable +import com.yahoo.processing.\*; +import com.yahoo.processing.execution.Execution; +import com.yahoo.processing.response.\*; +import com.yahoo.processing.test.ProcessorLibrary.StringData; + +/\*\* + \* A data producer which producer data which will receive asynchronously. + \* This is not a realistic, thread safe implementation as only the incoming data + \* from the last created incoming data can be completed. + \*/ +public class AsyncDataProducer extends Processor { + + private IncomingData incomingData; + + @Override + public Response process(Request request, Execution execution) { + DataList dataList = ArrayDataList.createAsync(request); // Default implementation + incomingData=dataList.incoming(); + return new Response(dataList); + } + + /\*\* Called by some other data producing thread, later \*/ + public void completeLateData() { + incomingData.addLast(new StringData(incomingData.getOwner().request(), + "A late hello, world!")); + } + +} +``` + +```java expandable +import com.google.common.util.concurrent.MoreExecutors; +import com.yahoo.component.chain.Chain; +import com.yahoo.processing.\*; +import com.yahoo.processing.execution.\*; + +/\*\* + \* A processor which registers a listener on the future completion of + \* asynchronously arriving data to perform another chain at that point. + \*/ +public class AsyncDataProcessingInitiator extends Processor { + + private final Chain asyncChain; + + public AsyncDataProcessingInitiator(Chain asyncChain) { + this.asyncChain=asyncChain; + } + + @Override + public Response process(Request request, Execution execution) { + Response response=execution.process(request); + response.data().complete().addListener(new RunnableExecution(request, + new ExecutionWithResponse(asyncChain, response, execution)), + MoreExecutors.sameThreadExecutor()); + return response; + } + +} +``` \ No newline at end of file diff --git a/en/applications/request-handlers.html b/en/applications/request-handlers.html deleted file mode 100644 index 18cf0a1ab9..0000000000 --- a/en/applications/request-handlers.html +++ /dev/null @@ -1,85 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Request handlers" -redirect_from: -- /en/jdisc/developing-request-handlers.html ---- - -

    - This document explains how to implement and deploy a custom request handler. -

    -

    - In most cases, implementing your own request handlers is unnecessary, as both searchers - and processors can access the request data directly. However, there are a few cases - where custom request handlers are useful: -

    -
      -
    1. You need to implement a custom REST API.
    2. -
    3. Your application needs to control which parameters - are used to route requests to a particular search or processing chain.
    4. -
    - - -

    Implementing a request handler

    -

    - Upon receiving a request, the request handler must consume its content, process it, - and then return a response. - The most convenient way to implement a request handler is by subclassing the - - ThreadedHttpRequestHandler. -

    -

    - This utility base class uses a synchronous API and a multithreaded execution model. - It also implements a lot of functionality that is needed by most request handlers: -

    -
      -
    • queries are automatically written to the access log
    • -
    • an HTTP date header is added to the response - (if your own code adds a date header, it will not be overwritten, though)
    • -
    • logging of exceptions and queries that time out
    • -
    • automatic shutdown when an Error is thrown
    • -
    - - -

    Example request handler implementations

    -

    - The Vespa sample apps on GitHub contains - a few example request handler implementations: -

    - - - - - - - - - - - - - -
    HandlerDescription
    - DemoHandlerA handler that modifies a request before dispatching it to the ProcessingHandler. - This handler is also used in the HTTP API tutorial. - Note that since this depends on ProcessingHandler you must add <processing/> to your <container> tag to use it. - If you want to issue Queries instead, have com.yahoo.search.searchchain.ExecutionFactory injected instead and use it - to create executions and call search/fill on them.
    - - - -

    Deploying a request handler

    -

    - To deploy a request handler in an application, - use the handler element in services.xml: -

    -
    -<container id="default" version="1.0">
    -    <handler id="my.package.MyRequestHandler" bundle="the name in <artifactId> in your pom.xml">
    -        <binding>http://*/*</binding>
    -    </handler>
    -
    -

    - A request handler may be bound to zero or more URI patterns by adding a - binding element for each pattern. -

    diff --git a/en/applications/request-handlers.mdx b/en/applications/request-handlers.mdx new file mode 100644 index 0000000000..a04e24ef3b --- /dev/null +++ b/en/applications/request-handlers.mdx @@ -0,0 +1,53 @@ +--- +title: "Request handlers" +description: "This document explains how to implement and deploy a custom request handler." +--- + +In most cases, implementing your own request handlers is unnecessary, as both searchers and processors can access the request data directly. However, there are a few cases where custom request handlers are useful: + + +1. You need to implement a custom REST API. +2. Your application needs to control which parameters are used to route requests to a particular search or processing chain. + +## Implementing a request handler + +Upon receiving a request, the request handler must consume its content, process it, and then return a response. The most convenient way to implement a request handler is by subclassing the [ThreadedHttpRequestHandler](https://javadoc.io/doc/com.yahoo.vespa/container-disc/latest/com/yahoo/container/jdisc/ThreadedHttpRequestHandler). + +This utility base class uses a synchronous API and a multithreaded execution model. It also implements a lot of functionality that is needed by most request handlers: + +- queries are automatically written to the access log +- an HTTP date header is added to the response (if your own code adds a date header, it will not be overwritten, though) +- logging of exceptions and queries that time out +- automatic shutdown when an Error is thrown + +### Example request handler implementations + +The [Vespa sample apps](https://github.com/vespa-engine/sample-apps) on GitHub contains a few example request handler implementations: + + + + + + + + + + + + + + +
    HandlerDescription
    DemoHandlerA handler that modifies a request before dispatching it to the {`ProcessingHandler`}. This handler is also used in the HTTP API tutorial. Note that since this depends on ProcessingHandler you must add {`processing`} to your {`container`} tag to use it. If you want to issue Queries instead, have com.yahoo.search.searchchain.ExecutionFactory injected instead and use it to create executions and call search/fill on them.
    + +## Deploying a request handler + +To deploy a request handler in an application, use the [handler](/en/reference/applications/services/container#handler) element in *services.xml*: + +```xml highlight={2-4} + + + http://*/* + +``` + +A request handler may be bound to zero or more URI patterns by adding a [binding](/en/reference/applications/services/container#binding) element for each pattern. diff --git a/en/applications/result-renderers.html b/en/applications/result-renderers.html deleted file mode 100644 index 9b8becd984..0000000000 --- a/en/applications/result-renderers.html +++ /dev/null @@ -1,340 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Result renderers" -redirect_from: -- /en/result-rendering.html -- /en/querying/result-rendering.html ---- - -

    - Vespa provides a default JSON format for query results. - Renderers can be configured to implement custom formats, - like binary and text format. - Renderers should not be used to implement business logic - - that should go in Searchers, - Handlers or - Processors. - This guide assumes familiarity with the Developer Guide. -

    - Renderers are implemented by subclassing one of: -

    - -

    - SectionedRenderer differs from Renderer by providing each part to be rendered in separate steps. - It is therefore easier to implement a SectionedRenderer than a regular Renderer. - AsynchronousSectionedRenderer has a similar API to SectionedRenderer, - but supports asynchronously fetched hit contents, - so if supporting slow clients or backends is a priority, this offers some advantages. - AsynchronousSectionedRenderer also exposes an OutputStream instead of a Writer, - so if the backend data contains data encoded the same way as the output from the container - (often UTF-8), performance gains are possible. -

    - All renderers are components. - They are built and deployed like all other container components, - and supports custom config. -

    - Renderers do not need to be thread safe - - they can safely use and store state during rendering in member variables. - The container supports this by cloning the renderers just before rendering the search result. - To support cloning correctly, the renderers are required to obey the following contract: -

    -
      -
    1. At construction time, only final members shall be initialized, - and these must refer to immutable data only.
    2. -
    3. State mutated during rendering shall be initialized in the init method.
    4. -
    -

    To enable a renderer, add to services.xml:

    -
    -<?xml version="1.0" encoding="utf-8" ?>
    -<services version="1.0">
    -    …
    -    <container version="1.0">
    -        <search>
    -            <renderer id="MyRenderer"
    -                      class="com.yahoo.mysearcher.MyRenderer"
    -                      bundle="the name in <artifactId> in pom.xml" />
    -        </search>
    -        …
    -    </container>
    -    …
    -</services>
    -
    -

    - To use the renderer, add &presentation.format=[id] - to queries - in this case &presentation.format=MyRenderer. -

    - - - -

    Renderer

    -

    - The simplest form of a renderer is extending Renderer. - The render method does all the work - - the derived class is expected to extract all the entities of interest itself and render them. - Simple example: -

    -
    {% highlight java%}
    -public class SimpleRenderer extends Renderer {
    -    @Override
    -    public void render(Writer writer, Result result) throws IOException {
    -        writer.write("The result contains " + result.getHitCount() + " hits.");
    -    }
    -
    -    @Override
    -    public String getEncoding() {
    -        return "utf-8";
    -    }
    -
    -    @Override
    -    public String getMimeType() {
    -        return "text/plain";
    -    }
    -}
    -{% endhighlight %}
    -

    More complex example:

    -
    {% highlight java%}
    -/**
    - * Render result sets as plain text. First line is whether an error occurred,
    - * second rendering initialization time stamp, then each line is the ID of each
    - * document returned, and the last line is time stamp for when the renderer was finished.
    - */
    -public class DemoRenderer extends Renderer {
    -    private String heading;
    -
    -    /**
    -     * No global, shared state to set.
    -     */
    -    public DemoRenderer() {
    -    }
    -
    -    @Override
    -    protected void render(Writer writer, Result result) throws IOException {
    -        if (result.hits().getErrorHit() == null) {
    -            writer.write("OK\n");
    -        } else {
    -            writer.write("Oops!\n");
    -        }
    -        writer.write(heading + "\n");
    -        renderHits(writer, result.hits());
    -        writer.write("Rendering finished work: " + System.currentTimeMillis() + "\n");
    -    }
    -
    -    private void renderHits(Writer writer, HitGroup hits) throws IOException {
    -        for (Iterator i = hits.deepIterator(); i.hasNext();) {
    -            Hit h = i.next();
    -            if (h.types().contains("summary")) {
    -                String id = h.getDisplayId();
    -                if (id != null) {
    -                    writer.write(id + "\n");
    -                }
    -            }
    -        }
    -    }
    -
    -    @Override
    -    public String getEncoding() {
    -        return "utf-8";
    -    }
    -
    -    @Override
    -    public String getMimeType() {
    -        return "text/plain";
    -    }
    -
    -    /**
    -     * Initialize mutable, per-result set state here.
    -     */
    -    @Override
    -    public void init() {
    -        long time = System.currentTimeMillis();
    -        heading = "Renderer initialized: " + time;
    -    }
    -
    -}
    -{% endhighlight %}
    - - - -

    SectionedRenderer

    -

    - To create a SectionedRenderer, subclass it and implement all its abstract methods. - For each non-compound entity such as regular hits and query contexts, - there are an associated method with the same name: -

    -
    {% highlight java %}
    -public class DemoRenderer extends SectionedRenderer {
    -
    -    @Override
    -    public void hit(Writer writer, Hit hit) throws IOException {
    -        writer.write("Hit: " + hit.getField("documentid") + "\n");
    -    }
    -}
    -{% endhighlight %}
    -

    - For each compound entity, such as hit groups and the result itself, - there are pairs of methods, named begin<name> and end<name>: -

    -
    {% highlight java %}
    -public class DemoRenderer extends SectionedRenderer {
    -
    -    private int indentation;
    -
    -    @Override
    -    public void beginHitGroup(PrintWriter writer, HitGroup hitGroup) throws IOException {
    -        writer.write("Begin hit group:" + hitGroup.getId() + "\n");
    -        ++indentation;
    -    }
    -
    -    @Override
    -    public void endHitGroup(PrintWriter writer, HitGroup hitGroup) throws IOException {
    -        --indentation;
    -        writer.write("End hit group:" + hitGroup.getId() + "\n");
    -    }
    -}
    -{% endhighlight %}
    -

    - For a compound entity, a method will be called for each of its members after its begin-method - and before its end-method has been called: -

    -
    -                          Call sequence
    -                          -------------------
    -Result {                  1. beginResult()
    -    HitGroup {            2. beginHitGroup()
    -        Hit               3. hit()
    -        Hit               4. hit()
    -        Hit               5. hit()
    -    }                     6. endHitGroup()
    -}                         7. endResult()
    -
    -

    - For grouping results, there is a dedicated set of callbacks available: -

    -
      -
    • beginGroup() / endGroup() -
    • beginGroupList() / endGroupList() -
    • beginHitList() / endHitList() -
    -

    - All of Group, GroupList and HitList are subclasses of HitGroup, - and the default implementation of the above methods is provided that calls - beginHitGroup() and endHitGroup(), respectively. - Furthermore, since all the attributes of those classes are regular fields - as defined by the root Hit class, - output is made by simply implementing beginHitGroup(), - endHitGroup(), and hit(). -

    - - -

    JSON example

    -

    - Read the default JSON result format - before implementing custom JSON renderers. - Example: Render a set of fields containing JSON data as a JSON array. - In other words, dump a variable length array containing all available data, - ignore everything else and silently ignore error states (i.e. good for prototyping): -

    -
    {% highlight java %}
    -package com.yahoo.mysearcher;
    -
    -import com.yahoo.search.Result;
    -import com.yahoo.search.query.context.QueryContext;
    -import com.yahoo.search.rendering.SectionedRenderer;
    -import com.yahoo.search.result.ErrorMessage;
    -import com.yahoo.search.result.Hit;
    -import com.yahoo.search.result.HitGroup;
    -
    -import java.io.IOException;
    -import java.io.Writer;
    -import java.util.Collection;
    -
    -public class MyRenderer extends SectionedRenderer {
    -    /**
    -     * A marker variable for the hit rendering to know whether
    -     * the hit being rendered is the first one that is rendered.
    -     */
    -    boolean firstHit;
    -
    -    public void init() {
    -        firstHit = true;
    -    }
    -
    -    @Override
    -    public String getEncoding() {
    -        return "utf-8";
    -    }
    -
    -    @Override
    -    public String getMimeType() {
    -        return "application/json";
    -    }
    -
    -    @Override
    -    public void beginResult(Writer writer, Result result) throws IOException {
    -        writer.write("[");
    -    }
    -
    -    @Override
    -    public void endResult(Writer writer, Result result) throws IOException {
    -        writer.write("]");
    -    }
    -
    -    @Override
    -    public void error(Writer writer, Collection errorMessages) throws IOException {
    -        // swallows errors silently
    -    }
    -
    -    @Override
    -    public void emptyResult(Writer writer, Result result) throws IOException {
    -        //write nothing.
    -    }
    -
    -    @Override
    -    public void queryContext(Writer writer, QueryContext queryContext) throws IOException {
    -        //write nothing.
    -    }
    -
    -    @Override
    -    public void beginHitGroup(Writer writer, HitGroup hitGroup) throws IOException {
    -        //write nothing.
    -    }
    -
    -    @Override
    -    public void endHitGroup(Writer writer, HitGroup hitGroup) throws IOException {
    -        //write nothing.
    -    }
    -
    -    @Override
    -    public void hit(Writer writer, Hit hit) throws IOException {
    -        if (!firstHit) {
    -            writer.write(",\n");
    -        }
    -        writer.write(hit.toString());
    -        firstHit = false;
    -    }
    -}
    -{% endhighlight %}
    - - - -

    AsynchronousSectionedRenderer<Result>

    -

    - This is the same as for the processing framework. - It is conceptually similar to SectionedRenderer, - but has no special cases for search results as such. - The utility method getResponse() has a parametrized return type, though, - so templating the renderer on Result takes away some of the hassle. -

    - Find an example in - DemoRenderer.java. -

    diff --git a/en/applications/result-renderers.mdx b/en/applications/result-renderers.mdx new file mode 100644 index 0000000000..1e8c68d288 --- /dev/null +++ b/en/applications/result-renderers.mdx @@ -0,0 +1,273 @@ +--- +title: "Result renderers" +description: "Vespa provides a default JSON format for query results. *Renderers* can be configured to implement custom formats, like binary and text format. Renderers should not be used to implement business logic - that should go in [Searchers](/en/applications/searchers), [Handlers](/en/applications/request-handlers) or [Processors](/en/applications/processing). This guide assumes familiarity with the [Developer Guide](/en/applications/developer-guide)." +--- + +Renderers are implemented by subclassing one of: + +- [com.yahoo.search.rendering.Renderer](https://javadoc.io/doc/com.yahoo.vespa/container-search/latest/com/yahoo/search/rendering/Renderer) +- [com.yahoo.search.rendering.SectionedRenderer](https://javadoc.io/doc/com.yahoo.vespa/container-search/latest/com/yahoo/search/rendering/SectionedRenderer) +- [com.yahoo.processing.rendering.AsynchronousSectionedRenderer<Result>](https://javadoc.io/doc/com.yahoo.vespa/container-disc/latest/com/yahoo/processing/rendering/AsynchronousSectionedRenderer) + +SectionedRenderer differs from Renderer by providing each part to be rendered in separate steps. It is therefore easier to implement a SectionedRenderer than a regular Renderer. AsynchronousSectionedRenderer has a similar API to SectionedRenderer, but supports asynchronously fetched hit contents, so if supporting slow clients or backends is a priority, this offers some advantages. AsynchronousSectionedRenderer also exposes an OutputStream instead of a Writer, so if the backend data contains data encoded the same way as the output from the container (often UTF-8), performance gains are possible. + +All renderers are [components](/en/applications/components). They are built and deployed like all other container components, and supports [custom config](/en/applications/configuring-components). + +Renderers do *not* need to be thread safe - they can safely use and store state during rendering in member variables. The container supports this by cloning the renderers just before rendering the search result. To support cloning correctly, the renderers are required to obey the following contract: + +1. At construction time, only final members shall be initialized, and these must refer to immutable data only. +2. State mutated during rendering shall be initialized in the init method. + +To enable a renderer, add to [services.xml](/en/reference/applications/services/container): + +```xml highlight={6-8} + + + … + + + + + … + + … + +``` + +To use the renderer, add [&presentation.format=\[id\]](/en/reference/api/query#presentation.format) to queries - in this case `&presentation.format=MyRenderer`. + +## Renderer + +The simplest form of a renderer is extending `Renderer`. The `render` method does all the work - the derived class is expected to extract all the entities of interest itself and render them. Simple example: + +```java +public class SimpleRenderer extends Renderer { + @Override + public void render(Writer writer, Result result) throws IOException { + writer.write("The result contains " + result.getHitCount() + " hits."); + } + + @Override + public String getEncoding() { + return "utf-8"; + } + + @Override + public String getMimeType() { + return "text/plain"; + } +} +``` + +More complex example: + +```java expandable +/** + * Render result sets as plain text. First line is whether an error occurred, + * second rendering initialization time stamp, then each line is the ID of each + * document returned, and the last line is time stamp for when the renderer was finished. + */ +public class DemoRenderer extends Renderer { + private String heading; + + /** + * No global, shared state to set. + */ + public DemoRenderer() { + } + + @Override + protected void render(Writer writer, Result result) throws IOException { + if (result.hits().getErrorHit() == null) { + writer.write("OK\n"); + } else { + writer.write("Oops!\n"); + } + writer.write(heading + "\n"); + renderHits(writer, result.hits()); + writer.write("Rendering finished work: " + System.currentTimeMillis() + "\n"); + } + + private void renderHits(Writer writer, HitGroup hits) throws IOException { + for (Iterator i = hits.deepIterator(); i.hasNext();) { + Hit h = i.next(); + if (h.types().contains("summary")) { + String id = h.getDisplayId(); + if (id != null) { + writer.write(id + "\n"); + } + } + } + } + + @Override + public String getEncoding() { + return "utf-8"; + } + + @Override + public String getMimeType() { + return "text/plain"; + } + + /** + * Initialize mutable, per-result set state here. + */ + @Override + public void init() { + long time = System.currentTimeMillis(); + heading = "Renderer initialized: " + time; + } + +} +``` + +## SectionedRenderer + +To create a SectionedRenderer, subclass it and implement all its abstract methods. For each non-compound entity such as regular hits and query contexts, there are an associated method with the same name: + +```java +public class DemoRenderer extends SectionedRenderer { + + @Override + public void hit(Writer writer, Hit hit) throws IOException { + writer.write("Hit: " + hit.getField("documentid") + "\n"); + } +} +``` + +For each compound entity, such as hit groups and the result itself, there are pairs of methods, named `begin` and `end`: + +```java +public class DemoRenderer extends SectionedRenderer { + + private int indentation; + + @Override + public void beginHitGroup(PrintWriter writer, HitGroup hitGroup) throws IOException { + writer.write("Begin hit group:" + hitGroup.getId() + "\n"); + ++indentation; + } + + @Override + public void endHitGroup(PrintWriter writer, HitGroup hitGroup) throws IOException { + --indentation; + writer.write("End hit group:" + hitGroup.getId() + "\n"); + } +} +``` + +```text +For a compound entity, a method will be called for each of its members after its `begin`\-method and before its `end`\-method has been called: + + Call sequence + ------------------- +Result { 1. beginResult() + HitGroup { 2. beginHitGroup() + Hit 3. hit() + Hit 4. hit() + Hit 5. hit() + } 6. endHitGroup() +} 7. endResult() +``` + +For [grouping results](/en/querying/grouping), there is a dedicated set of callbacks available: + +- `beginGroup()` / `endGroup()` +- `beginGroupList()` / `endGroupList()` +- `beginHitList()` / `endHitList()` + +All of `Group`, `GroupList` and `HitList` are subclasses of `HitGroup`, and the default implementation of the above methods is provided that calls `beginHitGroup()` and `endHitGroup()`, respectively. Furthermore, since all the attributes of those classes are regular fields as defined by the root `Hit` class, output is made by simply implementing `beginHitGroup()`, `endHitGroup()`, and `hit()`. + +### JSON example + +Read the [default JSON result format](/en/reference/querying/default-result-format) before implementing custom JSON renderers. Example: Render a set of fields containing JSON data as a JSON array. In other words, dump a variable length array containing all available data, ignore everything else and silently ignore error states (i.e. good for prototyping): + +```java expandable +package com.yahoo.mysearcher; + +import com.yahoo.search.Result; +import com.yahoo.search.query.context.QueryContext; +import com.yahoo.search.rendering.SectionedRenderer; +import com.yahoo.search.result.ErrorMessage; +import com.yahoo.search.result.Hit; +import com.yahoo.search.result.HitGroup; + +import java.io.IOException; +import java.io.Writer; +import java.util.Collection; + +public class MyRenderer extends SectionedRenderer { + /** + * A marker variable for the hit rendering to know whether + * the hit being rendered is the first one that is rendered. + */ + boolean firstHit; + + public void init() { + firstHit = true; + } + + @Override + public String getEncoding() { + return "utf-8"; + } + + @Override + public String getMimeType() { + return "application/json"; + } + + @Override + public void beginResult(Writer writer, Result result) throws IOException { + writer.write("["); + } + + @Override + public void endResult(Writer writer, Result result) throws IOException { + writer.write("]"); + } + + @Override + public void error(Writer writer, Collection errorMessages) throws IOException { + // swallows errors silently + } + + @Override + public void emptyResult(Writer writer, Result result) throws IOException { + //write nothing. + } + + @Override + public void queryContext(Writer writer, QueryContext queryContext) throws IOException { + //write nothing. + } + + @Override + public void beginHitGroup(Writer writer, HitGroup hitGroup) throws IOException { + //write nothing. + } + + @Override + public void endHitGroup(Writer writer, HitGroup hitGroup) throws IOException { + //write nothing. + } + + @Override + public void hit(Writer writer, Hit hit) throws IOException { + if (!firstHit) { + writer.write(",\n"); + } + writer.write(hit.toString()); + firstHit = false; + } +} +``` + +## AsynchronousSectionedRenderer<Result> + +This is the same as for the [processing framework](/en/applications/processing#response-rendering). It is conceptually similar to SectionedRenderer, but has no special cases for search results as such. The utility method getResponse() has a parametrized return type, though, so templating the renderer on `Result` takes away some of the hassle. + +Find an example in [DemoRenderer.java](https://github.com/vespa-engine/sample-apps/blob/master/examples/http-api-using-request-handlers-and-processors/src/main/java/ai/vespa/examples/DemoRenderer.java). diff --git a/en/applications/searchers.html b/en/applications/searchers.html deleted file mode 100644 index 060b78065a..0000000000 --- a/en/applications/searchers.html +++ /dev/null @@ -1,679 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Searchers" -redirect_from: -- /en/searchers.html -- /en/searcher-development.html ---- - -

    - The Container is the home for all global processing of - user actions (represented as queries) and their results. - It provides a development and hosting environment for processing - components, - and a model for composing such components developed by multiple development teams into a functional whole. -

    - This document describes how to develop and deploy Searcher components. - To get started with development, - see the Developer Guide. - For reference, see the - Container javadoc, - and the services.xml reference. -

    -

    - Best practise for queries is submitting the user-generated query as-is to Vespa, - then use Searcher components to implement additional logic. - Refer to the Query HTTP API. -

    -Searcher component in Vespa overview - - - -

    Searchers

    -

    - The components of the search container are called Searchers. - A searcher is a component - usually deployed as part of an OSGi bundle - - which extends the class com.yahoo.search.Searcher. - All Searchers must implement a single method: -

    -
    {% highlight java %}
    -public Result search(Query query, Execution execution);
    -{% endhighlight %}
    -

    - When the container receives a request, it will create a Query representing it - and execute a configured list of such Searcher components. - This is done by calling the search() method on the first searcher in the list. - That searcher is responsible for passing the call to the next searcher in the list (or not, as it sees fit). - This is done by calling search() on the Executor given, - which keeps track of where we are in the list of Searchers. - Hence, this is a noop searcher implementation: -

    -
    {% highlight java %}
    -public Result search(Query query, Execution execution) {
    -    return execution.search(query);
    -}
    -{% endhighlight %}
    -

    - Eventually the search call will reach the end of the list of searchers. - The last searcher in the list may create a Result (somehow), - which is now passed back up the call chain until it reaches the top. - The container will then translate that Result back to a response to the incoming request. -

    - As is evident from this description, this is a synchronous model, - where each request is processed in a dedicated worker thread until the result is returned. - This synchronous model is implemented with - multi-threading of individual searchers. -

    - The single searcher method is sufficient to express all kinds of functionality, e.g.: -

    -
      -
    • A query processor will modify the query, then pass it on - to the next searcher.
    • -
    • A result processor will pass the query on to get the - result, then modify the result before returning it.
    • -
    • A result producer which produces a result by some - internal lookup or (more typically) by sending a network request - to a backend will translate the query to the desired execution and - instantiate and return a Result holding the outcome.
    • -
    • A workflow might pass the query on multiple times in a - loop and gradually build up a Result for return from the Results - received from each Query execution, or choose to pass a particular - Query in an if-else loop etc.
    • -
    - - - -

    Queries and Results

    -

    -The Query in the search container is the container of all the -information needed to create a result to the request, including: -

    -
      -
    • The parameters received in the request, including the user's query - string, or chosen action.
    • -
    • The parameters in the chosen query - profile, if any.
    • -
    • The desired execution, including the boolean query tree. This - information is gradually created from the request and query - profile by Searcher components.
    • -
    • Any objects of any type containing information created by - Searchers along the way.
    • -
    -

    - The Result encapsulates all the data generated from a Query. - The Result contains a composite tree of Hit objects organized in lists called HitGroups - (the Result points to the topmost group). - Each Hit contains some particular data item which is deemed relevant to the Query. - The Hit objects has a general key-value storage, - but are also polymorphic to support representing more structured information. - See the inspecting - structured data documentation for details about handling structured information in a Searcher. -

    - As Hits may be hierarchically organized into hit lists, - the Result object is capable of representing any organization of the results. - For example, in a federated system the hits are initially organized in one hit group per source. - Upstream searchers may reorganize this into something that fits the user's need better, - e.g a single blended group, or one group per likely interpretation of the query etc. -

    - - - -

    Search Chains

    -

    - The lists of searchers mentioned above are called search chains. - Search chains are a special case of the general component chains. - A search chain is nothing more than a list of searcher instances having an id. - The search chains are typically not created for every query but are part of the configuration. - Multiple ones may exist at the same time, the chain to execute may be specified in the request. - If nothing is specified, a default one is used. - The same Searcher instance may exist in multiple search chains, - which is why the Execution object is responsible for knowing the next Searcher to invoke in a particular request. -

    - Search chains may also be executed programmatically (typically from a Searcher), - synchronously or asynchronously: -

    -
    {% highlight java %}
    -// Get a chain by id
    -SearchChain myChain = execution.searchChainRegistry().getComponent("myChain");
    -
    -// Execute it in the same thread
    -Result result = new Execution(myChain, execution.context()).search(query);
    -
    -// ... or in another thread
    -Execution settings = new Execution(myChain, execution.context());
    -FutureResult futureResult = new AsyncExecution(settings).search(query);
    -FutureResult otherFutureResult = new AsyncExecution(settings).search(otherQuery);
    -{% endhighlight %}
    -

    - Asynchronous execution is useful in cases like federation, - where a searcher forks a Query to multiple search chains in parallel, - each getting results from a particular source. - Also, as in the example, it is allowed to use the same Execution instance to construct - multiple AsyncExecution instances, as the state is only copied from the constructor argument. -

    - The execution order of the searchers in a chain are not ordered explicitly, - but by ordering constraints - declared in the searchers or their configuration. - Also read the search reference. -

    - - -

    Writing a Searcher

    -

    - Example of a complete searcher: -

    -
    {% highlight java %}
    -package com.yahoo.search.example;
    -
    -import com.yahoo.search.*;
    -import com.yahoo.search.result.Hit;
    -import com.yahoo.search.searchchain.Execution;
    -
    -/**
    - * A searcher adding a new hit.
    - */
    -public class SimpleSearcher extends Searcher {
    -
    -    public Result search(Query query, Execution execution) {
    -        Result result = execution.search(query); // Pass on to the next searcher to get results
    -        Hit hit = new Hit("test");
    -        hit.setField("message", "Hello world");
    -        result.hits().add(hit);
    -        return result;
    -    }
    -
    -}
    -{% endhighlight %}
    -

    - The container will create one or more instances of this class - and place it in the desired search chain(s) to serve queries, as specified in - the configuration. - The first line in this searcher forwards the query to whatever is the next searcher in the chain this is a part of. - This will eventually produce a Result, which is modified and then passed back to the previous searcher in this chain. - The container will create a new instance of this searcher only when it is reconfigured, - so any data needed by the searcher can be read and prepared from a constructor in the searcher. - Constructors may also accept configuration, - as any other pluggable component. -

    - Find the full API available to searchers in the - - Search Container Javadoc. -

    - - -

    Testing a Searcher

    -

    - Before there is any point in testing a searcher in a real system, - it should pass a set of unit tests which test it in isolation - or together with the few searchers it interacts with. - To do this, we can write unit tests which programmatically - sets up a search chain containing the searcher to be tested, the searchers it interacts with (if any) - and a searcher which produces mock results appropriate for the tests. - Here is a simple example testing the Searcher above: -

    -
    {% highlight java %}
    -package com.yahoo.search.example.test;
    -
    -import com.yahoo.search.*;
    -import com.yahoo.search.searchchain.*;
    -import com.yahoo.search.example.SimpleSearcher;
    -
    -public class SimpleSearcherTestCase extends junit.framework.TestCase {
    -
    -    public void testBasics() {
    -        // Create chain
    -        Chain searchChain = new Chain(new SimpleSearcher());
    -
    -        // Create an empty context, in a running container this would be
    -        // populated with settings used by different searcher. Tests must
    -        // set this according to their own requirements.
    -        Execution.Context context = Execution.Context.createContextStub(null);
    -        Execution execution = new Execution(searchChain, context);
    -
    -        // Execute it
    -        Result result = execution.search(new Query("search/?query=some test query"));
    -
    -        // Assert the result has the expected hit by scanning for the ID
    -        assertNotNull(result.hits().get("test"));
    -    }
    -
    -}
    -{% endhighlight %}
    -

    - In this case, no searcher producing mock results is needed - because the searcher we are testing does not care what the Result contains. - If the search chain ends with a searcher which produces no result, - the framework will simply return an empty result, which is what happens in this case. - A test adding a mock searcher producing results are shown in - federation. -

    - To write unit tests of the whole application package, - see the Developer Guide. -

    - - -

    Deploying a Searcher

    -

    - Once the searcher passes unit tests, it can be deployed to the Vespa system hosting it. - The procedure is the same as described in - deploying a component. - First build the component jar. - To include the searcher in services.xml, define a search chain and add the searcher to it - example: -

    -
    {% highlight xml %}
    -
    -
    -    
    -        
    -    
    -
    -    
    -        
    -            
    -                
    -            
    -        
    -        
    -            
    -        
    -    
    -
    -{% endhighlight %}
    -

    - This defines the search chain default, - which will be used in queries when no other chain is explicitly specified. - The searcher id above is resolved to the component bundle jar we added by the symbolic name in the manifest, - and to the right class within the bundle by the class name. - By keeping all these three the same, we keep things simple, - but more advanced use where this is possible is also supported, see later sections. -

    -

    - See the search chains reference. -

    -

    - Example hosts.xml: -

    -
    {% highlight xml %}
    -
    -
    -    
    -        node1
    -    
    -
    -{% endhighlight %}
    -

    - By creating a directory containing services.xml, - hosts.xml and components/Simplesearcher.jar, - that directory becomes a complete application package containing a bundle, - which can now be deployed to a Vespa instance. -

    - After deployment, query the application: - http://localhost:8080/search?query=best. -

    - - -

    Testing a Searcher with an Application

    -

    - A searcher can also be tested running inside a container. - Create an instance from the container part of the services.xml file above: -

    -
    {% highlight java %}
    -import com.yahoo.component.ComponentSpecification;
    -import com.yahoo.application.container.JDisc;
    -import com.yahoo.application.Networking;
    -
    -import com.yahoo.search.Query;
    -import com.yahoo.search.Result;
    -
    -import org.junit.jupiter.api.Test;
    -
    -import static org.junit.jupiter.api.Assertions.assertNotNull;
    -
    -public class ContainerTest {
    -    @Test
    -    public void testSearch() {
    -        String servicesXml =
    -                "" +
    -                "  " +
    -                "    " +
    -                "      " +
    -                "    " +
    -                "  " +
    -                "";
    -        try (JDisc container = JDisc.fromServicesXml(servicesXml, Networking.disabled)) {
    -            Result result = container.search().process(ComponentSpecification.fromString("default"),
    -                                                       new Query("search/?query=test+query"));
    -            assertNotNull(result.hits().get("test"));
    -        }
    -
    -    }
    -}
    -{% endhighlight %}
    -

    Examine which searchers are in a chain and their ordering:

    -
    {% highlight java %}
    -ChainRegistry chains = container.searching().getChains();
    -Chain defaultChain = chains.getComponent("default");
    -
    -boolean foundSimpleSearcher = false;
    -for (Searcher searcher: defaultChain.components()) {
    -    if ("com.yahoo.search.example.SimpleSearcher".equals(searcher.getClassName()))
    -        foundSimpleSearcher = true;
    -}
    -
    -assertTrue("No instance of SimpleSearcher found in the default chain", foundSimpleSearcher);
    -{% endhighlight %}
    - - - -

    Passing information between Searchers

    -

    - The query object is used to pass information between searchers. - A part of the query is a general property store which may hold any object. - Any values set in the request or in the query profile is available through these properties, - but in addition searchers may add any objects they create. - This is useful when some searcher component is producing information later consumed by some other. Example: -

    -
    {% highlight java %}
    -import com.yahoo.search.*;
    -import com.yahoo.search.searchchain.*;
    -
    -@Provides(SomeObject.NAME)
    -public class ProducerSearcher extends Searcher {
    -
    -    public Result search(Query query, Execution execution) {
    -        SomeObject.setTo(query.properties(), new SomeObject(query));
    -        return execution.search(query); // Pass to next in chain
    -    }
    -}
    -{% endhighlight %}
    -
    {% highlight java %}
    -import com.yahoo.search.*;
    -import com.yahoo.search.searchchain.*;
    -
    -@After(SomeObject.NAME)
    -public class ConsumerSearcher extends Searcher {
    -
    -    public Result search(Query query, Execution execution) {
    -        SomeObject someObject = SomeObject.getFrom(query.properties());
    -        ...
    -        return execution.search(query); // Pass to next in chain
    -    }
    -}
    -{% endhighlight %}
    -
    {% highlight java %}
    -import com.yahoo.search.query.Properties;
    -
    -public final class SomeObject {
    -    public static final String NAME = "SomeObject";
    -
    -    public static void setTo(Properties properties, SomeObject value) {
    -        properties.set(NAME, value);
    -    }
    -
    -    @SuppressWarnings("unchecked")
    -    public static SomeObject getFrom(Properties properties) {
    -        return (SomeObject) properties.get(NAME);
    -    }
    -}
    -{% endhighlight %}
    -

    This code illustrates two idioms such searchers should follow when exchanging data:

    -
      -
    • The key to an object should be exactly the same as the short name - of the object stored.
    • -
    • The searcher should declare that it provides exactly the - same name (and of course the consumers must declare that they need - to be after the object is provided).
    • -
    -

    - When it does not cause unwanted dependencies, - it is recommended to wrap the property get and put in a (static) - getFrom and setTo method in the stored object, - to allow storage and lookup without having to mention the key unnecessarily outside the object. -

    - Note that the objects are passed as regular in-memory references, - so there is no noticeable overhead in this. - However, in some situations (like when federating to multiple sources) the query will need to be cloned. - The query will then attempt to clone the added properties. - Those that implement Cloneable will have clone called, the rest will be copied by reference. -

    -{% include important.html content="It is important that objects added to the query which contains mutable state -are deep cloned to avoid bugs."%} -

    - On the other hand, cloning objects which should not change is wasteful, - they should be copied by reference. - Hence, the guidelines are: -

    -
      -
    • Objects which should not be modified downstream should enforce - immutability when added to the Query, either by not offering any - mutator methods, or by being frozen (in a state where any - mutator call causes an exception). Objects which enforces - mutability should either not implement Cloneable or should implement a shallow clone.
    • -
    • Objects which should support downstream modifications must - implement Cloneable and offer a clone method which performs deep copying.
    • -
    - - -

    Query Context

    -

    - In some cases there is a need for passing information between searchers beyond those who see the same Query object. - For this purpose, the Query provides a QueryContext object which provides a - shared data view to all Searchers working on the same request. - The context provides (among other things) a facility for setting properties (named objects). - The context can be accessed safely from all the threads working on a request - without incurring synchronization overhead (with some caveats), - but provides linear, not constant lookup time. - To set and retrieve such properties, use: -

    -
    {% highlight java %}
    -{query|result}.getContext(true).setProperty(name, value)
    -{query|result}.getContext(true).getProperty(name)
    -{% endhighlight %}
    - - - -

    Parametrizing Searchers

    -

    - It is easy to pass arguments to searchers - - any key-value looked up in the query properties in the searcher can be passed as is in the request, - or in a query profile. Example: -

    -
    {% highlight java %}
    -String myParameter = query.properties().getString("my.parameter", "defaultValue");
    -{% endhighlight %}
    -

    This value can be set by adding &my.parameter=myValue to the request. Guidelines:

    -
      -
    • Names should use camelCase with the first letter in small caps
    • -
    • Dots are used for nesting and have a special meaning - in query profiles. - They exist to aid organization of the space of parameters, which easily grows - quite large. Usually, the right thing to do is to create a - separate name space for each searcher - i.e. use the same - dotted prefix for all parameters, - as in myfeature.a, myfeature.b etc. - In addition to helping keep the search API clean, this allows various - query profiles containing settings for all values in - myfeature to be defined and selected at run time, - which is often useful.
    • -
    • To make such parameter APIs easier to use, one should also consider creating a - query profile - type defining the valid parameters. This will be in the form - of an XML file accompanying the bundle. This allows checking and - optionally enforcement of validity of request parameter and query - profile settings of the parameters.
    • -
    -

    - Parameters should be used for all query state which it is reasonable - and just as cheap to assume may change with every query. - Good candidates are e.g. numerical values to algorithms and switches to business logic. -

    - - - -

    Execution model

    -

    - In broad strokes, the Container works like this: -

    -
      -
    1. The main thread picks up one of the requests waiting in the queue of the input socket
    2. -
    3. This thread selects the search chain to be used to answer this request, - and hands off the actual execution of the chain to a worker thread (there are many such worker threads)
    4. -
    5. The worker thread calls all searchers in the search chain in turn, starting with the first one
    6. -
    7. Each searcher returns results
    8. -
    9. Results are eventually rendered (maybe using a template) into the buffer of the output socket
    10. -
    -

    - There is a single instance of each search chain. - In every chain, there is a single instance of each searcher. - (Unless a chain is configured with multiple, identical searchers - this is a rare case.) -

    - When simultaneous requests arrive for the same search chain, - multiple worker threads execute the searchers in that chain. - A searcher can therefore be executed concurrently by multiple threads; - many threads of execution can be going through the search() method, concurrently. -

    - This model places an important constraint on searcher classes: - instance variables are not safe. They must be eliminated, or made thread-safe somehow. -

    - - - -

    Keeping state in Searchers

    -

    - As the passing of queries and results happen on the call stack, - the container will allocate many worker threads to execute queries, - using one thread per query until the result is returned. -

    - This means that any state we wish to keep along in the searcher for this particular query - until the result is returned should be kept as local variables in the search method, - while state which should be shared by all queries should be kept as member variables. - As the latter kind will be accessed by multiple threads at any one time, - the state of such member variables must be multithread safe. -

    - This critical restriction is similar to those of e.g. the Servlet API. - A quick example should drive the point home: -

    -
    {% highlight java %}
    -public class SafeSearcher extends Searcher {
    -
    -    public Result search(Query query, Execution execution) {
    -        long count = (Long) query.properties().get("Count");
    -        count++;
    -        return execution.search(query);
    -    }
    -}
    -
    -public class UnsafeSearcher extends Searcher {
    -    private long count;
    -
    -    public Result search(Query query, Execution execution) {
    -        count = (Long) query.properties().get("Count");
    -        count++; // unsafe
    -        return execution.search(query);
    -    }
    -}
    -{% endhighlight %}
    -

    - The second example uses an instance variable, - which will be accessed concurrently by multiple threads. - Without proper concurrency controls (such as synchronization), - such access is inherently unsafe and may yield inconsistent results, and/or data corruption. -

    - Options for implementing a multithread-safe searcher with instance variables: -

    -
      -
    1. Use immutable objects: they never change after they are constructed; - no modifications to their state occurs after the Searcher constructor returns.
    2. -
    3. Use a single instance of a thread-safe class.
    4. -
    5. Create a single instance and synchronize access to it across all threads - (but this will severely limit your scalability).
    6. -
    7. Arrange for each thread to have its own instance, e.g. with a ThreadLocal.
    8. -
    - - - -

    Multiphase searching

    -

    - The model of a single pass fetching results from a Query described in - this document is sometimes too simplistic to produce good performance. - The search container supports multiphase searching to address such cases. - With multiphase searching, the hits of the result is first filled with some minimal information. - This minimally filled result is sent up the search chain where some of the hits are hopefully removed. - When more information is needed, a second fill request is sent down the search chain - to fetch more data for just those hits remaining in the result. - This can happen in repeated stages, working on progressively smaller sets of hits - containing progressively more expensive information. -

    - The container supports this by offering fill methods on execution, - which may be called to request more information added to the hits of the result from a searcher. - In addition, the backends and backend providers must support multiphase searching - (this is currently only the case for internal Vespa clusters). -

    - Any searchers should assume they are operating in a multiphase setup, meaning: -

    -
      -
    • Searchers which changes the query or contain workflows do not need to do anything
    • -
    • Searchers which accesses field information (not just id and - relevance) from hits should always call - either fill() to get the default set of fields for - each hit type or fill(summaryClassName) to get a - particular collection of fields known to exist in the backend(s) in question. - Calling fill on a result which contains already-filled hits is cheap.
    • -
    • Federating searchers should implement both the - regular search method and the fill method. - The fill method must request filling down the source - branches which has remaining hits in the result.
    • -
    • Backend searchers, which wish to support multiphase searching, - should initially deliver unfilled hits - and implement a fill method which fills the hits in the given - result belonging to that backend with information from the backend.
    • -
    -{% include note.html content=' -vespa-match-features -is a good article on multiphase searching, result fill and match-features.' %} - - -

    Error handling

    -

    - If your searcher encounters a problem and wants to signal an error, - set an error hit in the result object by calling result.hits().addError(errorMsg). -

    -

    - See the FAQ for timeouts. -

    - - -

    Timeouts

    -

    - How to gracefully handle a timeout inside a Searcher? - Result result = execution.search(query) can result in a timeout - when printed: -

    -
    -Container.com...vespa.Searcher result: Result: Source 'top-chain': 12: Timed out: Error in execution of chain 'top-chain': Chain timed out.
    -
    -

    - When having a tree of chains (see federation), - where the main chain calls one chain per source, - and in this case, one of the source chains times out (e.g. does not return a Result within its deadline), - this can happen. -

    -

    - It is not generally possible to prevent this from ever happening, - but searchers can check query.getTimeLeft before doing time-consuming stuff, - and pass query.getTimeLeft() - a_little as timeout - to processes they initiate (such as network calls) that are able to take a deadline themselves. -

    - - - -

    WordItem

    -

    - In a Searcher, one often will use - WordItem - to modify the current query, or create a new query based on input query terms, or results from the current query. - To keep linguistic settings (e.g. stemming) from the parent query, set isFromQuery to true - - - example. -

    diff --git a/en/applications/searchers.mdx b/en/applications/searchers.mdx new file mode 100644 index 0000000000..c2d3bd4951 --- /dev/null +++ b/en/applications/searchers.mdx @@ -0,0 +1,427 @@ +--- +title: "Searchers" +description: "The *Container* is the home for all global processing of user actions (represented as queries) and their results. It provides a development and hosting environment for processing [components](/en/applications/components), and a model for composing such components developed by multiple development teams into a functional whole." +--- + +This document describes how to develop and deploy Searcher components. To get started with development, see the [Developer Guide](/en/applications/developer-guide). For reference, see the [Container javadoc](https://javadoc.io/doc/com.yahoo.vespa/container-search/latest/com/yahoo/search/package-summary), and the [services.xml reference](/en/reference/applications/services/processing#chain). + +Best practise for queries is submitting the user-generated query as-is to Vespa, then use Searcher components to implement additional logic. Refer to the [Query HTTP API](/en/querying/query-api#http). + + +![](/assets/img/vespa-overview-searcher.svg) + + +## Searchers + +The components of the search container are called *Searchers*. A searcher is a component - usually deployed as part of an OSGi bundle - which extends the class `com.yahoo.search.Searcher`. All Searchers must implement a single method: + +```java +public Result search(Query query, Execution execution); +``` + +When the container receives a request, it will create a Query representing it and execute a configured list of such Searcher components. This is done by calling the `search()` method on the first searcher in the list. That searcher is responsible for passing the call to the next searcher in the list (or not, as it sees fit). This is done by calling `search()` on the Executor given, which keeps track of where we are in the list of Searchers. Hence, this is a noop searcher implementation: + +```java +public Result search(Query query, Execution execution) { + return execution.search(query); +} +``` + +Eventually the search call will reach the end of the list of searchers. The last searcher in the list may create a Result (somehow), which is now passed back up the call chain until it reaches the top. The container will then translate that Result back to a response to the incoming request. + +As is evident from this description, this is a synchronous model, where each request is processed in a dedicated worker thread until the result is returned. This synchronous model is implemented with [multi-threading of individual searchers](#keeping-state-in-searchers). + +The single searcher method is sufficient to express all kinds of functionality, e.g.: + +- A *query processor* will modify the query, then pass it on to the next searcher. +- A *result processor* will pass the query on to get the result, then modify the result before returning it. +- A *result producer* which produces a result by some internal lookup or (more typically) by sending a network request to a backend will translate the query to the desired execution and instantiate and return a Result holding the outcome. +- A *workflow* might pass the query on multiple times in a loop and gradually build up a Result for return from the Results received from each Query execution, or choose to pass a particular Query in an if-else loop etc. + +## Queries and Results + +The **Query** in the search container is the container of all the information needed to create a result to the request, including: + +- The parameters received in the request, including the user's query string, or chosen action. +- The parameters in the chosen [query profile](/en/querying/query-profiles), if any. +- The desired execution, including the boolean query tree. This information is gradually created from the request and query profile by Searcher components. +- Any objects of any type containing information created by Searchers along the way. + +The **Result** encapsulates all the data generated from a Query. The Result contains a composite tree of Hit objects organized in lists called HitGroups (the Result points to the topmost group). Each Hit contains some particular data item which is deemed relevant to the Query. The Hit objects has a general key-value storage, but are also polymorphic to support representing more structured information. See the [inspecting structured data](/en/applications/inspecting-structured-data) documentation for details about handling structured information in a Searcher. + +As Hits may be hierarchically organized into hit lists, the Result object is capable of representing any organization of the results. For example, in a federated system the hits are initially organized in one hit group per source. Upstream searchers may reorganize this into something that fits the user's need better, e.g a single blended group, or one group per likely interpretation of the query etc. + +## Search Chains + +The lists of searchers mentioned above are called *search chains*. Search chains are a special case of the [general component chains](/en/applications/chaining). A search chain is nothing more than a list of searcher instances having an id. The search chains are typically not created for every query but are part of the configuration. Multiple ones may exist at the same time, the chain to execute may be specified in the request. If nothing is specified, a default one is used. The same Searcher instance may exist in multiple search chains, which is why the Execution object is responsible for knowing the next Searcher to invoke in a particular request. + +Search chains may also be executed programmatically (typically from a Searcher), synchronously or asynchronously: + +```java +// Get a chain by id +SearchChain myChain = execution.searchChainRegistry().getComponent("myChain"); +// Execute it in the same thread +Result result = new Execution(myChain, execution.context()).search(query); +// ... or in another thread +Execution settings = new Execution(myChain, execution.context()); +FutureResult futureResult = new AsyncExecution(settings).search(query); +FutureResult otherFutureResult = new AsyncExecution(settings).search(otherQuery); +``` + +Asynchronous execution is useful in cases like [federation](/en/querying/federation), where a searcher forks a Query to multiple search chains in parallel, each getting results from a particular source. Also, as in the example, it is allowed to use the same Execution instance to construct multiple AsyncExecution instances, as the state is only copied from the constructor argument. + +The execution order of the searchers in a chain are not ordered explicitly, but by [ordering constraints](/en/applications/chaining) declared in the searchers or their configuration. Also read the [search reference](/en/reference/applications/services/search). + +### Writing a Searcher + +Example of a complete searcher: + +```java +package com.yahoo.search.example; +import com.yahoo.search.*; +import com.yahoo.search.result.Hit; +import com.yahoo.search.searchchain.Execution; +/** +* A searcher adding a new hit. +*/ +public class SimpleSearcher extends Searcher { + public Result search(Query query, Execution execution) { + Result result = execution.search(query); // Pass on to the next searcher to get results + Hit hit = new Hit("test"); + hit.setField("message", "Hello world"); + result.hits().add(hit); + return result; + } +} +``` + +The container will create one or more instances of this class and place it in the desired search chain(s) to serve queries, as specified in [the configuration](/en/applications/components#adding-component-to-application-package). The first line in this searcher forwards the query to whatever is the next searcher in the chain this is a part of. This will eventually produce a Result, which is modified and then passed back to the previous searcher in this chain. The container will create a new instance of this searcher only when it is reconfigured, so any data needed by the searcher can be read and prepared from a constructor in the searcher. Constructors may also accept [configuration](/en/applications/components#dependency-injection), as any other pluggable component. + +Find the full API available to searchers in the [Search Container Javadoc](https://javadoc.io/doc/com.yahoo.vespa/container-search/latest/com/yahoo/search/package-summary). + +### Testing a Searcher + +Before there is any point in testing a searcher in a real system, it should pass a set of unit tests which test it in isolation or together with the few searchers it interacts with. To do this, we can write unit tests which programmatically sets up a search chain containing the searcher to be tested, the searchers it interacts with (if any) and a searcher which produces mock results appropriate for the tests. Here is a simple example testing the Searcher above: + +```java expandable +package com.yahoo.search.example.test; + +import com.yahoo.search.*; +import com.yahoo.search.searchchain.*; +import com.yahoo.search.example.SimpleSearcher; + +public class SimpleSearcherTestCase extends junit.framework.TestCase { + +public void testBasics() { + // Create chain + Chain searchChain = new Chain(new SimpleSearcher()); + + // Create an empty context, in a running container this would be + // populated with settings used by different searcher. Tests must + // set this according to their own requirements. + Execution.Context context = Execution.Context.createContextStub(null); + Execution execution = new Execution(searchChain, context); + + // Execute it + Result result = execution.search(new Query("search/?query=some test query")); + + // Assert the result has the expected hit by scanning for the ID + assertNotNull(result.hits().get("test")); + } +} +``` + +In this case, no searcher producing mock results is needed because the searcher we are testing does not care what the Result contains. If the search chain ends with a searcher which produces no result, the framework will simply return an empty result, which is what happens in this case. A test adding a mock searcher producing results are shown in [federation](/en/querying/federation#unit-testing-the-result-processor). + +To write unit tests of the whole application package, see the [Developer Guide](/en/applications/developer-guide). + +### Deploying a Searcher + +Once the searcher passes unit tests, it can be deployed to the Vespa system hosting it. The procedure is the same as described in [deploying a component](/en/applications/components#deploying-a-component). First [build the component jar](/en/applications/components#building-the-plugin-jar). To include the searcher in *services.xml*, define a search chain and add the searcher to it - example: + +```xml + + + + + + + + + + + + + + + + + +``` + +This defines the search chain `default`, which will be used in queries when no other chain is explicitly specified. The searcher id above is resolved to the component bundle jar we added by the symbolic name in the manifest, and to the right class within the bundle by the class name. By keeping all these three the same, we keep things simple, but more advanced use where this is possible is also supported, see later sections. + +See the [search chains reference](/en/reference/applications/services/search#chain). + +Example *hosts.xml*: + +```xml + + + + node1 + + +``` + +By creating a directory containing *services.xml*, *hosts.xml* and *components/Simplesearcher.jar*, that directory becomes a complete application package containing a bundle, which can now be deployed to a Vespa instance. + +After deployment, query the application: [http://localhost:8080/search?query=best](http://localhost:8080/search/?query=best). + +### Testing a Searcher with an Application + +A searcher can also be tested running inside a container. Create an instance from the *container* part of the *services.xml* file above: + +```java expandable +import com.yahoo.component.ComponentSpecification; +import com.yahoo.application.container.JDisc; +import com.yahoo.application.Networking; + +import com.yahoo.search.Query; +import com.yahoo.search.Result; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertNotNull; + +public class ContainerTest { + @Test + public void testSearch() { + String servicesXml = + "" + + " " + + " " + + " " + + " " + + " " + + ""; + try (JDisc container = JDisc.fromServicesXml(servicesXml, Networking.disabled)) { + Result result = container.search().process(ComponentSpecification.fromString("default"), + new Query("search/?query=test+query")); + assertNotNull(result.hits().get("test")); + } + } +} +``` + +Examine which searchers are in a chain and their ordering: + +```java +ChainRegistry chains = container.searching().getChains(); +Chain defaultChain = chains.getComponent("default"); + +boolean foundSimpleSearcher = false; +for (Searcher searcher: defaultChain.components()) { + if ("com.yahoo.search.example.SimpleSearcher".equals(searcher.getClassName())) + foundSimpleSearcher = true; +} + +assertTrue("No instance of SimpleSearcher found in the default chain", foundSimpleSearcher); +``` + +## Passing information between Searchers + +The query object is used to pass information between searchers. A part of the query is a general property store which may hold any object. Any values set in the request or in the query profile is available through these properties, but in addition searchers may add any objects they create. This is useful when some searcher component is producing information later consumed by some other. Example: + +```java +import com.yahoo.search.*; +import com.yahoo.search.searchchain.*; +@Provides(SomeObject.NAME) +public class ProducerSearcher extends Searcher { + public Result search(Query query, Execution execution) { + SomeObject.setTo(query.properties(), new SomeObject(query)); + return execution.search(query); // Pass to next in chain + } +} +``` + +```java +import com.yahoo.search.*; +import com.yahoo.search.searchchain.*; +@After(SomeObject.NAME) +public class ConsumerSearcher extends Searcher { + public Result search(Query query, Execution execution) { + SomeObject someObject = SomeObject.getFrom(query.properties()); + ... + return execution.search(query); // Pass to next in chain + } +} +``` + +```java +import com.yahoo.search.query.Properties; +public final class SomeObject { + public static final String NAME = "SomeObject"; + public static void setTo(Properties properties, SomeObject value) { + properties.set(NAME, value); + } + @SuppressWarnings("unchecked") + public static SomeObject getFrom(Properties properties) { + return (SomeObject) properties.get(NAME); + } +} +``` + +This code illustrates two idioms such searchers should follow when exchanging data: + +- The key to an object should be exactly the same as the short name of the object stored. +- The searcher should declare that it *provides* exactly the same name (and of course the consumers must declare that they need to be *after* the object is provided). + +When it does not cause unwanted dependencies, it is recommended to wrap the property get and put in a (static) `getFrom` and `setTo` method in the stored object, to allow storage and lookup without having to mention the key unnecessarily outside the object. + +Note that the objects are passed as regular in-memory references, so there is no noticeable overhead in this. However, in some situations (like when federating to multiple sources) the query will need to be cloned. The query will then attempt to clone the added properties. Those that implement Cloneable will have clone called, the rest will be copied by reference. + + +**Important:** + +It is important that objects added to the query which contains mutable state are **deep cloned** to avoid bugs. + + +On the other hand, cloning objects which should not change is wasteful, they should be copied by reference. Hence, the guidelines are: + +- Objects which should not be modified downstream should enforce immutability when added to the Query, either by not offering any mutator methods, or by being *frozen* (in a state where any mutator call causes an exception). Objects which *enforces* mutability should either not implement Cloneable or should implement a shallow clone. +- Objects which should support downstream modifications **must** implement Cloneable and offer a clone method which performs deep copying. + +### Query Context + +In some cases there is a need for passing information between searchers beyond those who see the same Query object. For this purpose, the Query provides a QueryContext object which provides a shared data view to all Searchers working on the same request. The context provides (among other things) a facility for setting properties (named objects). The context can be accessed safely from all the threads working on a request without incurring synchronization overhead (with some caveats), but provides linear, not constant lookup time. To set and retrieve such properties, use: + +```java +{query|result}.getContext(true).setProperty(name, value) +{query|result}.getContext(true).getProperty(name) +``` + +## Parametrizing Searchers + +It is easy to pass arguments to searchers - any key-value looked up in the query properties in the searcher can be passed as is in the request, or in a query profile. Example: + +```java +String myParameter = query.properties().getString("my.parameter", "defaultValue"); +``` + +This value can be set by adding `&my.parameter=myValue` to the request. Guidelines: + +- Names should use camelCase with the first letter in small caps +- Dots are used for nesting and have a special meaning in [query profiles](/en/querying/query-profiles). They exist to aid organization of the space of parameters, which easily grows quite large. Usually, the right thing to do is to create a separate name space for each searcher - i.e. use the same dotted prefix for all parameters, as in `myfeature.a`, `myfeature.b` etc. In addition to helping keep the search API clean, this allows various query profiles containing settings for all values in `myfeature` to be defined and selected at run time, which is often useful. +- To make such parameter APIs easier to use, one should also consider creating a [query profile type](/en/querying/query-profiles#query-profile-types) defining the valid parameters. This will be in the form of an XML file accompanying the bundle. This allows checking and optionally enforcement of validity of request parameter and query profile settings of the parameters. + +Parameters should be used for all query state which it is reasonable and just as cheap to assume may change with every query. Good candidates are e.g. numerical values to algorithms and switches to business logic. + +## Execution model + +In broad strokes, the Container works like this: + + + + The main thread picks up one of the requests waiting in the queue of the input socket + + + + This thread selects the search chain to be used to answer this request, and hands off the actual execution of the chain to a worker thread (there are many such worker threads) + + + + The worker thread calls all searchers in the search chain in turn, starting with the first one + + + + Each searcher returns results + + + + Results are eventually rendered (maybe using a template) into the buffer of the output socket + + + +There is a single instance of each search chain. In every chain, there is a single instance of each searcher. (Unless a chain is configured with multiple, identical searchers - this is a rare case.) + +When simultaneous requests arrive for the same search chain, multiple worker threads execute the searchers in that chain. A searcher can therefore be executed concurrently by multiple threads; many threads of execution can be going through the `search()` method, concurrently. + +This model places an important constraint on searcher classes: *instance variables are not safe.* They must be eliminated, or made thread-safe somehow. + +## Keeping state in Searchers + +As the passing of queries and results happen on the call stack, the container will allocate many worker threads to execute queries, using one thread per query until the result is returned. + +This means that any state we wish to keep along in the searcher for this particular query until the result is returned should be kept as local variables in the search method, while state which should be shared by all queries should be kept as member variables. As the latter kind will be accessed by multiple threads at any one time, the state of such member variables must be *multithread safe*. + +This critical restriction is similar to those of e.g. the Servlet API. A quick example should drive the point home: + +```java +public class SafeSearcher extends Searcher { + public Result search(Query query, Execution execution) { + long count = (Long) query.properties().get("Count"); + count++; + return execution.search(query); + } +} +public class UnsafeSearcher extends Searcher { + private long count; + public Result search(Query query, Execution execution) { + count = (Long) query.properties().get("Count"); + count++; // unsafe + return execution.search(query); + } +} +``` + +The second example uses an instance variable, which will be accessed concurrently by multiple threads. Without proper concurrency controls (such as synchronization), such access is inherently unsafe and may yield inconsistent results, and/or data corruption. + +Options for implementing a multithread-safe searcher with instance variables: + +1. Use immutable objects: they never change after they are constructed; no modifications to their state occurs after the Searcher constructor returns. +2. Use a single instance of a thread-safe class. +3. Create a single instance and synchronize access to it across all threads (but this will severely limit your scalability). +4. Arrange for each thread to have its own instance, e.g. with a `ThreadLocal`. + +## Multiphase searching + +The model of a single pass fetching results from a Query described in this document is sometimes too simplistic to produce good performance. The search container supports *multiphase searching* to address such cases. With multiphase searching, the hits of the result is first filled with some minimal information. This minimally filled result is sent up the search chain where some of the hits are hopefully removed. When more information is needed, a second fill request is sent down the search chain to fetch more data for just those hits remaining in the result. This can happen in repeated stages, working on progressively smaller sets of hits containing progressively more expensive information. + +The container supports this by offering `fill` methods on execution, which may be called to request more information added to the hits of the result from a searcher. In addition, the backends and backend providers must support multiphase searching (this is currently only the case for internal Vespa clusters). + +Any searchers should assume they are operating in a multiphase setup, meaning: + +- Searchers which changes the query or contain workflows do not need to do anything +- Searchers which accesses field information (not just id and relevance) from hits should **always** call either `fill()` to get the default set of fields for each hit type or `fill(summaryClassName)` to get a particular collection of fields known to exist in the backend(s) in question. Calling fill on a result which contains already-filled hits is cheap. +- Federating searchers should implement both the regular `search` method and the `fill` method. The fill method must request filling down the source branches which has remaining hits in the result. +- Backend searchers, which wish to support multiphase searching, should initially deliver unfilled hits and implement a `fill` method which fills the hits in the given result belonging to that backend with information from the backend. + + +**Note:** + +[vespa-match-features](https://vinted.engineering/2025/11/06/vespa-match-features/) is a good article on multiphase searching, result fill and match-features. + + +## Error handling + +If your searcher encounters a problem and wants to signal an error, set an error hit in the result object by calling `result.hits().addError(errorMsg)`. + +See the FAQ for [timeouts](/en/learn/faq#how-is-the-query-timeout-computed). + +## Timeouts + +How to gracefully handle a timeout inside a Searcher? `Result result = execution.search(query)` can result in a timeout - when printed: + +```txt +Container.com...vespa.Searcher result: Result: Source 'top-chain': 12: Timed out: Error in execution of chain 'top-chain': Chain timed out. +``` + +When having a tree of chains (see [federation](/en/querying/federation#timeout-behavior)), where the main chain calls one chain per source, and in this case, one of the source chains times out (e.g. does not return a Result within its deadline), this can happen. + +It is not generally possible to prevent this from ever happening, but searchers can check `query.getTimeLeft` before doing time-consuming stuff, and pass `query.getTimeLeft() - a_little` as timeout to processes they initiate (such as network calls) that are able to take a deadline themselves. + +## WordItem + +In a Searcher, one often will use [WordItem](https://javadoc.io/doc/com.yahoo.vespa/container-search/latest/com/yahoo/prelude/query/WordItem) to modify the current query, or create a new query based on input query terms, or results from the current query. To keep linguistic settings (e.g. stemming) from the parent query, set `isFromQuery` to true - [example](https://github.com/vespa-cloud/vespa-documentation-search/blob/main/src/main/java/ai/vespa/cloud/docsearch/DocumentationSearcher.java). diff --git a/en/applications/testing.html b/en/applications/testing.html deleted file mode 100644 index 184bf7b524..0000000000 --- a/en/applications/testing.html +++ /dev/null @@ -1,241 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: System testing -redirect_from: -- /en/testing.html ---- - -

    - A system tests suite is an invaluable tool both when developing and maintaining a complex Vespa application. - These are functional tests which are run against a deployment of the application package to verify, - and use its HTTP APIs to execute feed and query operations which are compared to expected outcomes. - Vespa provides two formalizations of this: -

    -
      -
    • - Basic HTTP tests, - expressing requests and expected responses as JSON, - and run with the Vespa CLI. -
    • -
    • - Java JUnit tests, for more advanced tests, - run as regular Java tests, with some extra configuration. -
    • -
    -

    - These two frameworks also includes an upgrade—or staging—test construct for scenarios where the application - is upgraded, and state in the backend depends on the old application configuration; - as well as a production verification test—basically a health check for production deployments. - For system and staging tests, the frameworks provide an easy way to perform HTTP request against a - designated test deployment, separating the tests from the deployment and configuration of the test clusters. -

    -

    - This document describes how each of these test categories can be run as part of an imagined - CI/CD system for safely deploying changes to a Vespa application in a continuous manner. -

    -

    - Finally, find a section on A/B-testing / bucket tests - using feature switches. -

    - - - -

    System tests

    -

    - System tests are just functional tests that verify a deployed Vespa application behaves as expected - when fed and queried. Running a system test is as simple as making a separate deployment with the - application package to test, and then running the system test suite, or one or a few or those tests. -

    -{% include note.html content="Each system test should be self-contained, -i.e., it should be able to run each test in isolation; or all tests, in any order. -To achieve this, system tests should generally start by clearing all documents from the cluster to test. -This is the case with our sample system tests, so take care to not run them against a production cluster."%} -

    - For the most part, system tests must be updated due to changes in the application package. - Rarely, an upgrade of the Vespa version may also lead to changed functionality, but within - major versions, this should only be new features and bug fixes. In any case, it is a good idea - to always run system tests against a dedicated test deployment—both before upgrading the Vespa - platform, and the application package—before deploying the change to production. -

    - - -

    Running system tests

    - -

    The Vespa CLI makes it easy to set up a test deployment, and run system and staging tests. -To run a system test, first set up a test deployment:

    - -
    - -
    {% highlight sh %}
    -$ vespa deploy --wait 600
    -{% endhighlight %}
    -
    -

    - Run the basic HTTP tests (prefer using this test suite for regular tests) - also see the - example application: -

    -
    - -
    {% highlight sh %}
    -$ vespa test tests/system-test/feed-and-search-test.json
    -{% endhighlight %}
    -
    -

    - Example Java API tests (use for complex test cases) - also see the - example application: -

    -
    - -
    {% highlight sh %}
    -$ mvn test -D test.categories=system -D vespa.test.config=/path-to/test-config.json
    -{% endhighlight %}
    -
    -

    - The test config file used by the test runner in the maven-plugin defines the endpoints for each of the clusters in - services.xml as fields under a localEndpoints JSON object: -

    -
    - -
    {% highlight json %}
    -{
    -    "localEndpoints": {
    -        "query-service": "http://localhost:8080/",
    -        "feed-service" : "http://localhost:8081/"
    -    }
    -}
    -{% endhighlight %}
    -
    -

    - feed-service is the endpoint of the container cluster with <document-api> - in services.xml. - query-service is the endpoint of the container cluster with <search> - in services.xml. -

    - - - -

    Staging tests

    -

    - The goal of staging (upgrade) tests is not to ensure the new deployment - satisfies its functional specifications, as that should be covered by - system tests; rather, it is to ensure the upgrade of the application - package and/or Vespa platform does not break the application, and is - compatible with the behavior expected by existing clients. -

    -

    - As an example, consider a change in how documents are indexed, e.g., adding new document processor. - A system test would test verify this new behavior by feeding a document, and then verifying the - document processor modified the document, or perhaps did something else. - A staging test, on the other hand, would feed the document before the document processor was added, - and querying for the document after the upgrade could give different results - from what the system test would expect. -

    -

    Many such changes, which require additional action post-deployment, are also guarded by - validation overrides, - but the staging test is then a great way of figuring out what the exact consequences of the change are, - and how to deal with it. -

    -

    - As opposed to system tests, staging tests are not self-contained, as the state change during upgrade is - precisely what is tested. Instead, execution order of any staging tests that modify state, particularly - after upgrade, must be controlled. Indeed, some changes will require re-feeding data, and this should then be - part of the staging test code. Finally, it is also good to verify the expected state prior to upgrade. -

    -

    - The clients of a Vespa application should be compatible with both the system and staging test expectations, - and this dictates the workflow when deploying a breaking change - steps: -

    -
      -
    1. - The application code and system and staging tests are updated, so tests pass; - and clients are updated to reflect the updated test code. -
    2. -
    3. The application is upgraded.
    4. -
    5. The staging setup code is updated to match the new application code.
    6. -
    -

    - Again, it is a good idea to always run staging tests before deployment of every change—be it a - change in the application package, or an upgrade of the Vespa platform. -

    - - -

    Running staging tests

    -

    - See system tests above for links to example applications. - Steps: -

    -
      -
    1. A dedicated deployment is made with the current setup (package and Vespa version).
    2. -
    3. - staging setup code is run to put the test cluster in a particular state—typically - one that mimics the state in production clusters. -
    4. -
    5. The deployment is then upgraded to the new setup (package and/or Vespa version).
    6. -
    7. staging test code is run to verify the cluster behaves as expected post-upgrade.
    8. -
    -

    - Example using JSON-tests: -

    -
    {% highlight sh %}
    -# load old application code, deploy it, run setup
    -$ vespa deploy --wait 600
    -$ vespa test tests/staging-setup
    -
    -# make changes to the application, deploy it, run tests
    -$ vespa deploy --wait 120
    -$ vespa test tests/staging-test
    -{% endhighlight %}
    -

    - Example using Java tests (see system tests for test-config.json): -

    -
    {% highlight sh %}
    -# load old application code, deploy it, run setup
    -$ vespa deploy --wait 600
    -$ mvn test -D test.categories=staging-setup -D vespa.test.config=/path-to/test-config.json
    -
    -# make changes to the application, deploy it, run tests
    -$ vespa deploy --wait 120
    -$ mvn test -D test.categories=staging -D vespa.test.config=/path-to/test-config.json
    -{% endhighlight %}
    - - - -

    Feature switches and bucket tests

    -

    - With continuous deployment, it is not practical to hold off releasing a feature until it is done, - test it manually until convinced it works and then release it to production. - What to do instead? - The answer is feature switches: release new features to production as they are developed, - but include logic which keeps them deactivated until they are ready, - or until they have been verified in production with a subset of users. -

    -

    - Bucket tests is the practice of systematically testing new features or behavior for a controlled subset of users. - This is common practice when releasing new science models, - as they are difficult to verify in test, but can also be used for other features. -

    -

    - To test new behavior in Vespa, use a combination of - search chains and - rank profiles, - controlled by query profiles, - where one query profile corresponds to one bucket. - These features support inheritance to make it easy to express variation without repetition. -

    -

    - Sometimes a new feature requires - incompatible changes to a data field. - To be able to CD such changes, it is necessary to create a new field containing the new version of the data. - This costs extra resources but less than the alternative: standing up a new system copy with the new data. - New fields can be added and populated while the system is live. -

    -

    - One way to reduce the need for incompatible changes can be decreased - by making the semantics of the fields more precise. - E.g., if a field is defined as the "quality" of a document, where a higher number means higher quality, - a new algorithm which produces a different range and distribution will typically be an incompatible change. - However, if the field is defined more precisely as the average time spent on the document once it is clicked, - then a new algorithm which produces better estimates of this value will not be an incompatible change. - Using precise semantics also have the advantage of making it easier to understand - if the use of the data and its statistical properties are reasonable. -

    diff --git a/en/applications/testing.mdx b/en/applications/testing.mdx new file mode 100644 index 0000000000..dd6274e91b --- /dev/null +++ b/en/applications/testing.mdx @@ -0,0 +1,154 @@ +--- +title: "System testing" +description: "A system tests suite is an invaluable tool both when developing and maintaining a complex Vespa application. These are functional tests which are run against a deployment of the application package to verify, and use its HTTP APIs to execute feed and query operations which are compared to expected outcomes. Vespa provides two formalizations of this:" +--- + +- [Basic HTTP tests](/en/reference/applications/testing), expressing requests and expected responses as JSON, and run with the [Vespa CLI](/en/clients/vespa-cli). +- [Java JUnit tests](/en/reference/applications/testing-java), for more advanced tests, run as regular Java tests, with some extra configuration. + +These two frameworks also includes an upgrade—or staging—test construct for scenarios where the application is upgraded, and state in the backend depends on the old application configuration; as well as a production verification test—basically a health check for production deployments. For system and staging tests, the frameworks provide an easy way to perform HTTP request against a designated test deployment, separating the tests from the deployment and configuration of the test clusters. + +This document describes how each of these test categories can be run as part of an imagined CI/CD system for safely deploying changes to a Vespa application in a continuous manner. + +Finally, find a section on [A/B-testing / bucket tests](/en/applications/testing#feature-switches-and-bucket-tests) using feature switches. + +## System tests + +System tests are just functional tests that verify a deployed Vespa application behaves as expected when fed and queried. Running a system test is as simple as making a separate deployment with the application package to test, and then running the system test suite, or one or a few or those tests. + + +**Note:** + +Each system test should be self-contained, i.e., it should be able to run each test in isolation; or all tests, in any order. To achieve this, **system tests should generally start by clearing all documents from the cluster to test.** This is the case with our sample system tests, so take care to not run them against a production cluster. + + +For the most part, system tests must be updated due to changes in the application package. Rarely, an upgrade of the Vespa version may also lead to changed functionality, but within major versions, this should only be new features and bug fixes. In any case, it is a good idea to always run system tests against a dedicated test deployment—both before upgrading the Vespa platform, and the application package—before deploying the change to production. + +### Running system tests + +The [Vespa CLI](/en/clients/vespa-cli) makes it easy to set up a test deployment, and run system and staging tests. To run a system test, first set up a test deployment: + + + +```sh +$ vespa deploy --wait 600 +``` + +Run the basic HTTP tests (prefer using this test suite for regular tests) - also see the [example](https://github.com/vespa-engine/sample-apps/tree/master/examples/operations/CI-CD/production-deployment-with-tests) application: + + + +```sh +$ vespa test tests/system-test/feed-and-search-test.json +``` + +Example Java API tests (use for complex test cases) - also see the [example](https://github.com/vespa-engine/sample-apps/tree/master/examples/operations/CI-CD/production-deployment-with-tests-java) application: + + + +```sh +$ mvn test -D test.categories=system -D vespa.test.config=/path-to/test-config.json +``` + +The test config file used by the test runner in the maven-plugin defines the endpoints for each of the clusters in [services.xml](/en/reference/applications/services/services) as fields under a `localEndpoints` JSON object: + + + +```json +{ + "localEndpoints": { + "query-service": "http://localhost:8080/", + "feed-service" : "http://localhost:8081/" + } +} +``` + +`feed-service` is the endpoint of the container cluster with `` in [services.xml](/en/reference/applications/services/services). `query-service` is the endpoint of the container cluster with `` in [services.xml](/en/reference/applications/services/services). + +## Staging tests + +The goal of staging (upgrade) tests is *not* to ensure the new deployment satisfies its functional specifications, as that should be covered by system tests; rather, it is to ensure the upgrade of the application package and/or Vespa platform does not break the application, and is compatible with the behavior expected by existing clients. + +As an example, consider a change in how documents are indexed, e.g., adding new document processor. A system test would test verify this new behavior by feeding a document, and then verifying the document processor modified the document, or perhaps did something else. A staging test, on the other hand, would feed the document *before* the document processor was added, and querying for the document after the upgrade could give different results from what the system test would expect. + +Many such changes, which require additional action post-deployment, are also guarded by [validation overrides](/en/reference/applications/validation-overrides), but the staging test is then a great way of figuring out what the exact consequences of the change are, and how to deal with it. + +As opposed to system tests, staging tests are not self-contained, as the state change during upgrade is precisely what is tested. Instead, execution order of any staging tests that modify state, particularly after upgrade, must be controlled. Indeed, some changes will require re-feeding data, and this should then be part of the *staging test* code. Finally, it is also good to verify the expected state prior to upgrade. + +The clients of a Vespa application should be compatible with both the system and staging test expectations, and this dictates the workflow when deploying a breaking change - steps: + + + + The application code and system and staging tests are updated, so tests pass; and clients are updated to reflect the updated test code. + + + + The application is upgraded. + + + + The *staging setup* code is updated to match the new application code. + + Again, it is a good idea to always run staging tests before deployment of every change—be it a change in the application package, or an upgrade of the Vespa platform. + + + + +### Running staging tests + +See [system tests](#system-tests) above for links to example applications. Steps: + + + + A dedicated deployment is made with the *current* setup (package and Vespa version). + + + + *staging setup* code is run to put the test cluster in a particular state—typically one that mimics the state in production clusters. + + + + The deployment is then upgraded to the *new* setup (package and/or Vespa version). + + + + *staging test* code is run to verify the cluster behaves as expected post-upgrade. + + Example using JSON-tests: + + ```sh + # load old application code, deploy it, run setup + $ vespa deploy --wait 600 + $ vespa test tests/staging-setup + + # make changes to the application, deploy it, run tests + $ vespa deploy --wait 120 + $ vespa test tests/staging-test + ``` + + Example using Java tests (see [system tests](#running-system-tests) for *test-config.json*): + + ```sh + # load old application code, deploy it, run setup + $ vespa deploy --wait 600 + $ mvn test -D test.categories=staging-setup -D vespa.test.config=/path-to/test-config.json + + # make changes to the application, deploy it, run tests + $ vespa deploy --wait 120 + $ mvn test -D test.categories=staging -D vespa.test.config=/path-to/test-config.json + ``` + + + + +## Feature switches and bucket tests + +With continuous deployment, it is not practical to hold off releasing a feature until it is done, test it manually until convinced it works and then release it to production. What to do instead? The answer is *feature switches*: release new features to production as they are developed, but include logic which keeps them deactivated until they are ready, or until they have been verified in production with a subset of users. + +*Bucket tests* is the practice of systematically testing new features or behavior for a controlled subset of users. This is common practice when releasing new science models, as they are difficult to verify in test, but can also be used for other features. + +To test new behavior in Vespa, use a combination of [search chains](/en/applications/chaining) and [rank profiles](/en/reference/schemas/schemas#rank-profile), controlled by [query profiles](/en/querying/query-profiles), where one query profile corresponds to one bucket. These features support inheritance to make it easy to express variation without repetition. + +Sometimes a new feature requires [incompatible changes to a data field](/en/reference/schemas/schemas#modifying-schemas). To be able to CD such changes, it is necessary to create a new field containing the new version of the data. This costs extra resources but less than the alternative: standing up a new system copy with the new data. New fields can be added and populated while the system is live. + +One way to reduce the need for incompatible changes can be decreased by making the semantics of the fields more precise. E.g., if a field is defined as the "quality" of a document, where a higher number means higher quality, a new algorithm which produces a different range and distribution will typically be an incompatible change. However, if the field is defined more precisely as the average time spent on the document once it is clicked, then a new algorithm which produces better estimates of this value will not be an incompatible change. Using precise semantics also have the advantage of making it easier to understand if the use of the data and its statistical properties are reasonable. diff --git a/en/applications/unit-testing.html b/en/applications/unit-testing.html deleted file mode 100644 index da11f30cad..0000000000 --- a/en/applications/unit-testing.html +++ /dev/null @@ -1,200 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Unit testing" -redirect_from: -- /en/unit-testing.html ---- - -

    This document describes how to test application functionality in a local Java vm. -See automated deployments -for how to create system, staging and production verification tests.

    - - -

    Unit testing using Application

    -

    -The -Application class is useful when writing unit tests. -Application uses the application package configuration and set up a container instance for testing. -The -JDisc class that is accessed by -the test through app.getJDisc(clusterName) - -this class has methods for using all common component types. -

    -Refer to -MetalSearcherTest.java for example use. -Notice how the test disables the network layer in order to run tests in parallel. -

    -{% include note.html content="Application does not set up content nodes, only container. -It is hence fully stateless, and intended for unit testing the functionality of application components. -The ClusterSearcher will not find any content nodes and log errors if invoked. -Write a System Test to test end-to-end features like search."%} -

    -For prototyping, enable the network interface, -instantiate the Container and run requests using a browser: -

    -
    {% highlight java%}
    -public class ApplicationMain {
    -  @Test
    -  public static void main(String[] args) throws Exception {
    -    try (com.yahoo.application.Application app = com.yahoo.application.Application.fromApplicationPackage(
    -        FileSystems.getDefault().getPath("src/main/application"),
    -        Networking.enable)) {
    -      app.getClass();
    -      Thread.sleep(Long.MAX_VALUE);
    -    }
    -  }
    -}
    -{% endhighlight %}
    - - - -

    Unit Testing Configurable Components

    -

    -How to programmatically build configuration instances for unit testing. -Read the Developer Guide first. -

    -To be able to write self-contained unit tests using configuration classes -generated from a schema, it is necessary to instantiate the configuration -without the use of for instance an external services file. Configuration classes -contain their own builders which are useful for solving exactly this problem. By -using builders, the configuration will be created as an immutable, type-safe -object, exactly the same as used during deployment. -

    - - -

    Configuration schema

    -

    -Assume the config definition file demo.def with the following schema: -

    -
    -package=com.mydomain.demo
    -
    -toplevel[].term string
    -toplevel[].number int
    -toplevel[].largenumber long
    -toplevel[].secondlevel[].name string
    -toplevel[].secondlevel[].magnitude double
    -
    -simplename string
    -simplenumber int
    -simplevaluearray[] string
    -
    -coordinate.x double
    -coordinate.y double
    -coordinate.name string
    -
    -

    -In other words, the configuration class will be com.mydomain.demo.DemoConfig, -and it will contain an array of structures, -a couple of top-level primitives (simplename and simplenumber), -an array of primitive values (simplevaluearray) and a structure (coordinate). -

    - - -

    Using configuration builders

    -

    -All structured objects in the cloud configuration system have their own -Builder as a nested class. So, in the above example, one would get -DemoConfig.Builder for the complete configuration class, -DemoConfig.Toplevel.Builder for the top-level array, -DemoConfig.Toplevel.Secondlevel.Builder for the inner array, and -DemoConfig.Coordinate.Builder for the structure. -

    -A configuration object, or substructure, is easiest instantiated using a -constructor accepting the corresponding Builder class, an array of structures -should use the constructor accepting an array of Builder instances, and an -array of primitive values simply accepts a java.util.Collection of the -corresponding primitive value class: -

    -
    -package com.mydomain.demo;
    -
    -import static org.junit.Assert.*;
    -
    -import java.util.ArrayList;
    -import java.util.Arrays;
    -import java.util.List;
    -
    -import org.junit.Test;
    -
    -import static com.mydomain.demo.DemoConfig.Toplevel;
    -import static com.mydomain.demo.DemoConfig.Toplevel.Secondlevel;
    -import static com.mydomain.demo.DemoConfig.Coordinate;
    -
    -public class DemoTest {
    -
    -    /**
    -     * An example showing how to build a relatively complex, mixed type
    -     * configuration including arrays of primitive elements, nested arrays and
    -     * arrays of structures and so on.
    -     */
    -    @Test
    -    public final void test() {
    -        // We need use builders to safely create the graph of immutable
    -        // configuration objects. Each generated configuration class contains
    -        // the builder for creating an instance of itself. This pattern is
    -        // repeated for structures. So, in our case, we have four structured
    -        // levels. The complete configuration class, DemoConfig, the top-level,
    -        // nested array, Toplevel, the contained array, Secondlevel and the
    -        // structure Coordinate. This leaves us with using four distinct
    -        // builder classes: DemoConfig.Builder, Toplevel.Builder,
    -        // Secondlevel.Builder and Coordinate.Builder.
    -
    -        // Chained setters are the most used pattern for the builders:
    -        DemoConfig forTesting = new DemoConfig(new DemoConfig.Builder()
    -                .simplename("basic chained setter for the string simplename")
    -                .simplenumber(42)
    -                .toplevel(buildTopLevelArray())
    -                .simplevaluearray(
    -                        Arrays.asList(new String[] { "primitive", "arrays",
    -                                "are", "easier", "to", "build", "than",
    -                                "arrays", "of", "structures" }))
    -                .coordinate(
    -                        new Coordinate.Builder()
    -                                .name("have no idea what to call this one")
    -                                .x(1e300d).y(1e-300d)));
    -        assertTrue(forTesting != null); // ;)
    -    }
    -
    -    /**
    -     * It is often the more readable solution to use helper methods to build
    -     * configuration arrays.
    -     *
    -     * @return a list of Toplevel.Builder instances
    -     */
    -    private List<Toplevel.Builder> buildTopLevelArray() {
    -        // Note how the Builder classes tend to work on Collection classes and
    -        // mutable objects, while the config ready for use is bolted down and
    -        // immutable:
    -        List<Toplevel.Builder> configArray = new ArrayList<Toplevel.Builder>(3);
    -        String[] configStrings = new String[] { "a", "b", "c" };
    -        int[] configNumbers = new int[] { 1, 2, 3 };
    -        long[] configLargeNumbers = new long[] { 1L + (long) Integer.MAX_VALUE,
    -                2L + (long) Integer.MAX_VALUE, 3L + (long) Integer.MAX_VALUE };
    -        for (int i = 0; i < configStrings.length; ++i) {
    -            configArray.add(new Toplevel.Builder().number(configNumbers[i])
    -                    .largenumber(configLargeNumbers[i]).term(configStrings[i])
    -                    .secondlevel(buildSecondLevelArray(2)));
    -        }
    -        return configArray;
    -    }
    -
    -    /**
    -     * Once again, the building of an array is delegated to a helper method
    -     *
    -     * @param subelements
    -     *            the length of the returned list
    -     * @return a list of SecondLevel.Builder
    -     */
    -    private List<Secondlevel.Builder> buildSecondLevelArray(int subelements) {
    -        List<Secondlevel.Builder> builders = new ArrayList<Secondlevel.Builder>(
    -                subelements);
    -        for (int i = 0; i < subelements; ++i) {
    -            builders.add(new Secondlevel.Builder().name(String.valueOf(i))
    -                    .magnitude((double) i));
    -        }
    -        return builders;
    -    }
    -
    -}
    -
    diff --git a/en/applications/unit-testing.mdx b/en/applications/unit-testing.mdx new file mode 100644 index 0000000000..c01abb6752 --- /dev/null +++ b/en/applications/unit-testing.mdx @@ -0,0 +1,151 @@ +--- +title: "Unit testing" +description: "This document describes how to test application functionality in a local Java vm. See [automated deployments](/en/operations/automated-deployments) for how to create system, staging and production verification tests." +--- + +## Unit testing using Application + +The [Application](https://javadoc.io/doc/com.yahoo.vespa/application/latest/com/yahoo/application/Application.html) class is useful when writing unit tests. Application uses the application package configuration and set up a container instance for testing. The [JDisc](https://javadoc.io/doc/com.yahoo.vespa/application/latest/com/yahoo/application/container/JDisc.html) class that is accessed by the test through [app.getJDisc(clusterName)](https://javadoc.io/page/com.yahoo.vespa/application/latest/com/yahoo/application/Application#getJDisc-java.lang.String-) - this class has methods for using all common [component types](/en/reference/applications/components). + +Refer to [MetalSearcherTest.java](https://github.com/vespa-engine/sample-apps/blob/master/album-recommendation-java/app/src/test/java/ai/vespa/example/album/MetalSearcherTest.java) for example use. Notice how the test disables the network layer in order to run tests in parallel. + + +**Note:** + +`Application` does not set up *content* nodes, only *container*. It is hence fully stateless, and intended for unit testing the functionality of application components. The *ClusterSearcher* will not find any content nodes and log errors if invoked. Write a *System Test* to test end-to-end features like search. + + +For prototyping, enable the network interface, instantiate the Container and run requests using a browser: + +```java +public class ApplicationMain { + @Test + public static void main(String[] args) throws Exception { + try (com.yahoo.application.Application app = com.yahoo.application.Application.fromApplicationPackage( + FileSystems.getDefault().getPath("src/main/application"), + Networking.enable)) { + app.getClass(); + Thread.sleep(Long.MAX_VALUE); + } + } +} +``` + +## Unit Testing Configurable Components + +How to programmatically build configuration instances for unit testing. Read the [Developer Guide](/en/applications/developer-guide) first. + +To be able to write self-contained unit tests using configuration classes generated from a schema, it is necessary to instantiate the configuration without the use of for instance an external services file. Configuration classes contain their own builders which are useful for solving exactly this problem. By using builders, the configuration will be created as an immutable, type-safe object, exactly the same as used during deployment. + +### Configuration schema + +Assume the config definition file `demo.def` with the following schema: + +```java +package=com.mydomain.demo + +toplevel[].term string +toplevel[].number int +toplevel[].largenumber long +toplevel[].secondlevel[].name string +toplevel[].secondlevel[].magnitude double + +simplename string +simplenumber int +simplevaluearray[] string + +coordinate.x double +coordinate.y double +coordinate.name string +``` + +In other words, the configuration class will be `com.mydomain.demo.DemoConfig`, and it will contain an array of structures, a couple of top-level primitives (*simplename* and *simplenumber*), an array of primitive values (*simplevaluearray*) and a structure (*coordinate*). + +### Using configuration builders + +All structured objects in the cloud configuration system have their own Builder as a nested class. So, in the above example, one would get `DemoConfig.Builder` for the complete configuration class, `DemoConfig.Toplevel.Builder` for the top-level array, `DemoConfig.Toplevel.Secondlevel.Builder` for the inner array, and `DemoConfig.Coordinate.Builder` for the structure. + +A configuration object, or substructure, is easiest instantiated using a constructor accepting the corresponding *Builder* class, an array of structures should use the constructor accepting an array of *Builder* instances, and an array of primitive values simply accepts a java.util.Collection of the corresponding primitive value class: + +```java expandable +package com.mydomain.demo; +import static org.junit.Assert.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import org.junit.Test; +import static com.mydomain.demo.DemoConfig.Toplevel; +import static com.mydomain.demo.DemoConfig.Toplevel.Secondlevel; +import static com.mydomain.demo.DemoConfig.Coordinate; +public class DemoTest { + /** + * An example showing how to build a relatively complex, mixed type + * configuration including arrays of primitive elements, nested arrays and + * arrays of structures and so on. + */ + @Test + public final void test() { + // We need use builders to safely create the graph of immutable + // configuration objects. Each generated configuration class contains + // the builder for creating an instance of itself. This pattern is + // repeated for structures. So, in our case, we have four structured + // levels. The complete configuration class, DemoConfig, the top-level, + // nested array, Toplevel, the contained array, Secondlevel and the + // structure Coordinate. This leaves us with using four distinct + // builder classes: DemoConfig.Builder, Toplevel.Builder, + // Secondlevel.Builder and Coordinate.Builder. + // Chained setters are the most used pattern for the builders: + DemoConfig forTesting = new DemoConfig(new DemoConfig.Builder() + .simplename("basic chained setter for the string simplename") + .simplenumber(42) + .toplevel(buildTopLevelArray()) + .simplevaluearray( + Arrays.asList(new String[] { "primitive", "arrays", + "are", "easier", "to", "build", "than", + "arrays", "of", "structures" })) + .coordinate( + new Coordinate.Builder() + .name("have no idea what to call this one") + .x(1e300d).y(1e-300d))); + assertTrue(forTesting != null); // ;) + } + /** + * It is often the more readable solution to use helper methods to build + * configuration arrays. + * + * @return a list of Toplevel.Builder instances + */ + private List buildTopLevelArray() { + // Note how the Builder classes tend to work on Collection classes and + // mutable objects, while the config ready for use is bolted down and + // immutable: + List configArray = new ArrayList(3); + String[] configStrings = new String[] { "a", "b", "c" }; + int[] configNumbers = new int[] { 1, 2, 3 }; + long[] configLargeNumbers = new long[] { 1L + (long) Integer.MAX_VALUE, + 2L + (long) Integer.MAX_VALUE, 3L + (long) Integer.MAX_VALUE }; + for (int i = 0; i < configStrings.length; ++i) { + configArray.add(new Toplevel.Builder().number(configNumbers[i]) + .largenumber(configLargeNumbers[i]).term(configStrings[i]) + .secondlevel(buildSecondLevelArray(2))); + } + return configArray; + } + /** + * Once again, the building of an array is delegated to a helper method + * + * @param subelements + * the length of the returned list + * @return a list of SecondLevel.Builder + */ + private List buildSecondLevelArray(int subelements) { + List builders = new ArrayList( + subelements); + for (int i = 0; i < subelements; ++i) { + builders.add(new Secondlevel.Builder().name(String.valueOf(i)) + .magnitude((double) i)); + } + return builders; + } +} +``` diff --git a/en/applications/using-zookeeper.md b/en/applications/using-zookeeper.md deleted file mode 100644 index 62aaf5aa2d..0000000000 --- a/en/applications/using-zookeeper.md +++ /dev/null @@ -1,68 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Using ZooKeeper" -redirect_from: - - /en/using-zookeeper ---- - -The Vespa container supports [ZooKeeper](https://zookeeper.apache.org/), which -allows distributed synchronization across nodes in a container cluster. - -Once enabled all nodes in a container cluster will automatically form a -ZooKeeper ensemble, and participate as servers. Vespa takes care of -reconfiguring ZooKeeper members when nodes are added or removed from the -container cluster. - -Note that Vespa enforces an optimal node limit for clusters with ZooKeeper. -Application packages that violate this node count will be rejected. -The valid number of nodes is 3, 5 or 7. -See [#15762](https://github.com/vespa-engine/vespa/issues/15762) for other node counts. - - -## Configuration - -1. ZooKeeper must be explicitly enabled in the [container cluster -configuration](../reference/applications/services/container.html#zookeeper). - -2. The application must specify a dependency on `zkfacade`. Example for `pom.xml`: - - ```xml - - com.yahoo.vespa - zkfacade - [vespa-version] - provided - - ``` - - -## Code example - -ZooKeeper features are exposed through -[VespaCurator](https://github.com/vespa-engine/vespa/blob/master/zkfacade/src/main/java/com/yahoo/vespa/curator/api/VespaCurator.java). -[Inject](dependency-injection.html) `VespaCurator` to use it. [Handler](request-handlers.html) example: - -```java -public class MyRequestHandler extends ThreadedHttpRequestHandler { - - private final VespaCurator curator; - - @Inject - public CuratorHandler(Executor executor, VespaCurator curator) { - super(executor); - this.curator = curator; - } - - @Override - public HttpResponse handle(HttpRequest httpRequest) { - Path lockPath = Path.fromString("/locks/mylock"); - Duration timeout = Duration.ofSeconds(1); - try (var lock = curator.lock(lockPath, timeout)) { - // Do something while holding lock - } catch (Exception e) { - throw new RuntimeException("Failed to acquire lock " + lockPath, e); - } - } - -} -``` diff --git a/en/applications/using-zookeeper.mdx b/en/applications/using-zookeeper.mdx new file mode 100644 index 0000000000..e5f4b910ac --- /dev/null +++ b/en/applications/using-zookeeper.mdx @@ -0,0 +1,59 @@ +--- +title: "Using ZooKeeper" +description: "The Vespa container supports [ZooKeeper](https://zookeeper.apache.org/), which allows distributed synchronization across nodes in a container cluster." +--- + +Once enabled all nodes in a container cluster will automatically form a ZooKeeper ensemble, and participate as servers. Vespa takes care of reconfiguring ZooKeeper members when nodes are added or removed from the container cluster. + + +**Note:** + +Vespa enforces an optimal node limit for clusters with ZooKeeper. Application packages that violate this node count will be rejected. The valid number of nodes is 3, 5 or 7. See [#15762](https://github.com/vespa-engine/vespa/issues/15762) for other node counts. + + +## Configuration + + + + ZooKeeper must be explicitly enabled in the [container cluster configuration](/en/reference/applications/services/container#zookeeper). + + + + The application must specify a dependency on `zkfacade`. Example for `pom.xml`: + + ```xml + + com.yahoo.vespa + zkfacade + [vespa-version] + provided + + ``` + + + + +## Code example + +ZooKeeper features are exposed through [VespaCurator](https://github.com/vespa-engine/vespa/blob/master/zkfacade/src/main/java/com/yahoo/vespa/curator/api/VespaCurator.java). [Inject](/en/applications/dependency-injection) `VespaCurator` to use it. [Handler](/en/applications/request-handlers) example: + +```java +public class MyRequestHandler extends ThreadedHttpRequestHandler { + private final VespaCurator curator; + @Inject + public CuratorHandler(Executor executor, VespaCurator curator) { + super(executor); + this.curator = curator; + } + @Override + public HttpResponse handle(HttpRequest httpRequest) { + Path lockPath = Path.fromString("/locks/mylock"); + Duration timeout = Duration.ofSeconds(1); + try (var lock = curator.lock(lockPath, timeout)) { + // Do something while holding lock + } catch (Exception e) { + throw new RuntimeException("Failed to acquire lock " + lockPath, e); + } +} +} +``` diff --git a/en/applications/vespaignore.html b/en/applications/vespaignore.html deleted file mode 100644 index b8c5e5f931..0000000000 --- a/en/applications/vespaignore.html +++ /dev/null @@ -1,67 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: ".vespaignore" ---- - -

    - When deploying an application - package with Vespa CLI, - a .vespaignore file (similar to .gitignore) can be - added to the package to prevent specific files or path patterns from being - included in the deployed package. -

    -

    - Ignoring files is useful when the Vespa application directory contains files - that are only used for development purposes, and are not directly referenced - by the application. -

    - -

    Location

    - -

    - The .vespaignore file must be placed at the same level - as services.xml. Having - multiple .vespaignore at different path levels is not supported. -

    - -

    Example

    -

    - This is an example of a .vespaignore file that excludes files - and directories rarely needed in an application package. -

    -
    -# exclude hidden files and readme
    -.DS_Store
    -.gitignore
    -README.md
    -
    -# exclude feed input
    -ext/
    -
    -# exclude auxiliary scripts
    -*.py
    -*.sh
    -
    - - - -

    Format

    -

    - The .vespaignore format is a subset of - the .gitignore format, where: -

    -
      -
    • Lines starting with # are ignored and can be used for comments
    • -
    • Each non-empty line specifies a path pattern to ignore
    • -
    • Patterns are relative to services.xml
    • -
    • - A pattern can be either a literal string, - or a pattern string as consumed by filepath.Match -
    • -
    • - Lines ending with / always denote a directory, e.g. the pattern foo/ - will match the directory foo (and any files below), - but not the file foo -
    • -
    -

    Complex rules, such as negated patterns and recursive globbing (**) are not supported.

    diff --git a/en/applications/vespaignore.mdx b/en/applications/vespaignore.mdx new file mode 100644 index 0000000000..20108f8dc7 --- /dev/null +++ b/en/applications/vespaignore.mdx @@ -0,0 +1,42 @@ +--- +title: ".vespaignore" +sidebarTitle: ".vespaignore file" +description: "When deploying an [application package](/en/reference/applications/application-packages) with [Vespa CLI](/en/clients/vespa-cli), a `.vespaignore` file (similar to `.gitignore`) can be added to the package to prevent specific files or path patterns from being included in the deployed package." +--- + + +Ignoring files is useful when the Vespa application directory contains files that are only used for development purposes, and are not directly referenced by the application. + +## Location + +The `.vespaignore` file must be placed at the same level as [services.xml](/en/reference/applications/services/services). Having multiple `.vespaignore` at different path levels is not supported. + +## Example + +This is an example of a `.vespaignore` file that excludes files and directories rarely needed in an application package. + +```txt +# exclude hidden files and readme +.DS_Store +.gitignore +README.md + +# exclude feed input +ext/ + +# exclude auxiliary scripts +*.py +*.sh +``` + +## Format + +The `.vespaignore` format is a subset of the `.gitignore` format, where: + +- Lines starting with `#` are ignored and can be used for comments +- Each non-empty line specifies a path pattern to ignore +- Patterns are relative to `services.xml` +- A pattern can be either a literal string, or a pattern string as consumed by [filepath.Match](https://pkg.go.dev/path/filepath#Match) +- Lines ending with `/` always denote a directory, e.g. the pattern `foo/` will match the directory `foo` (and any files below), but not the file `foo` + +Complex rules, such as negated patterns and recursive globbing (`**`) are not supported. diff --git a/en/applications/web-services.html b/en/applications/web-services.html deleted file mode 100644 index 67e379e140..0000000000 --- a/en/applications/web-services.html +++ /dev/null @@ -1,217 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Developing Web Service Applications" -redirect_from: -- /en/developing-web-services.html ---- - -

    -This document explains how to develop (REST) web service type applications on the container - -design options, accessing the request path, returning a status code etc. -There are two types of web service APIs: -

    -
      -
    • Fine-grained APIs with closed semantics – for example return the - number of stars of an article
    • -
    • Coarse-grained APIs with open semantics – for example return a page - containing the most relevant mixture of stuff for this user and action
    • -
    -

    -With coarse-grained APIs, the container can help handle the complexity typically -involved in the implementation of such APIs -by providing a way to compose and federate components contributing to processing the request -and provide and modify the returned data, -and a way to allow such requests to start returning -before they are finished to reduce latency with large responses. -This is the processing framework -(or, in the case of search-like application, -the searcher specialization). -

    -In addition, the container -features a generic mechanism allowing a - request handler -to be bound to a URI pattern -and invoked to handle all requests matching that pattern. -This is useful where there is no need to handle complexity and/or federation -of various kinds of data in the response. Both the approaches above are -actually implemented as built-in request handlers. -

    -A custom request handler may be written to parse the url path/method -and dispatch to an appropriate chain of processing components. -A "main" processing chain may be written -to do the same by dispatching to other chains. -The simplest way to invoke a specific chain of processors -is to forward a query to the ProcessingHandler -with the request property chain set to the name of the chain to invoke: -

    -
    -import com.yahoo.component.annotation.Inject;
    -
    -public class DemoHandler extends com.yahoo.container.jdisc.ThreadedHttpRequestHandler {
    -
    -...
    -
    -    @Inject
    -    public DemoHandler(Executor executor, ProcessingHandler processingHandler) {
    -        super(executor);
    -        this.processingHandler = processingHandler;
    -    }
    -
    -...
    -
    -    @Override
    -    public HttpResponse handle(HttpRequest request) {
    -        HttpRequest processingRequest = new HttpRequest.Builder(request)
    -                .put(com.yahoo.processing.Request.CHAIN, "theProcessingChainIWant")
    -                .createDirectRequest();
    -        HttpResponse r = processingHandler.handle(processingRequest);
    -        return r;
    -    }
    -
    -...
    -
    -}
    -
    - - -

    Accessing the HTTP request

    -

    -Custom request handlers, -are given a - - com.yahoo.container.jdisc.HttpRequest, -with direct access to associated properties and request data. -

    -

    -In Processing, -the Processors are given a - - com.yahoo.processing.Request -containing the HTTP URL parameters: -

    -
    -// url parameters are added to properties
    -String urlParameter = request.properties().get("urlParameterName");
    -
    -// jdisc request context is added with prefix context
    -Object contextValue = request.properties().get("context.contextKey");
    -
    - -

    -If needed, a Processor can retrieve the entire HTTP request via a utility function: -

    -import com.yahoo.container.jdisc.HttpRequest;
    -...
    -
    -// Retrieve the underlying HTTP request:
    -Optional<HttpRequest> httpRequest = HttpRequest.getHttpRequest(request);
    -
    -if (httpRequest.isPresent()) {
    -    // The POST data input stream:
    -    InputStream in = httpRequest.get().getData();
    -    // The HTTP method:
    -    Method method = httpRequest.get().getMethod();
    -}
    -
    - - -

    Setting the HTTP status and HTTP headers

    -

    -In Processing, the return status can be set by adding a special Data item to the Response: -

    -
    -response.data().add(new com.yahoo.processing.handler.ResponseStatus(404, request));
    -
    -

    -If no such data element is present, the status will be determined by the container. -If it contains data able to render, it will be 200, -otherwise it will be determined by any ErrorMessage present in the response. -

    - - -

    Setting response headers from Processors

    -

    -Response headers may be added to any Response by adding instances of -com.yahoo.processing.handler.ResponseHeaders to the Response -(ResponseHeaders is a kind of response Data). -Multiple instances of this may be added to the Response, -and the complete set of headers returned is the superset of all such objects. -Example Processor: -

    -
    -processingResponse.data().add(new com.yahoo.processing.handler.ResponseHeaders(myHeaders, request));
    -
    -

    -Request handlers may in general set their return status, -and manipulate headers directly on the HttpRequest. -

    - - - -

    Queries

    -

    -Sometimes all that is needed is letting the standard query framework -reply for more paths than standard. -This is possible by adding extra bindings -inside the <search> element in services.xml. -Writing a custom request handler -is recommended if the application is a standalone HTTP API, -and especially if there are properties used with the same name as those in the -Query API. -A request handler may query the search components running in the same container -without any appreciable overhead:

    - - -

    Invoking Vespa queries from a component

    -

    - To invoke Vespa queries from a component, have an instance of - - ExecutionFactory injected in the constructor and use its API to construct and issue the query. - The container this runs in must include the <search> tag for the ExecutionFactory to be available. - Example: -

    -
    -import com.yahoo.component.annotation.Inject;
    -import com.yahoo.component.ComponentId;
    -import com.yahoo.search.Query;
    -import com.yahoo.search.Result;
    -import com.yahoo.component.Chain;
    -import com.yahoo.search.searchchain.Execution;
    -import com.yahoo.search.searchchain.ExecutionFactory;
    -
    -public class MyComponent {
    -
    -    private final ExecutionFactory executionFactory;
    -
    -    @Inject
    -    public MyComponent(ExecutionFactory executionFactory) {
    -        this.executionFactory = executionFactory;
    -    }
    -
    -    Result executeQuery(Query query, String chainId) {
    -        Chain<Search> searchChain = executionFactory.searchChainRegistry().getChain(new ComponentId(chainId));
    -        Execution execution = executionFactory.newExecution(searchChain);
    -        query.getModel().setExecution(execution);
    -        return execution.search(query);
    -    }
    -
    -}
    -
    -

    - ExecutionFactory depends on the search chains, - so it cannot be injected into any component which is part of the search chains. - But from within a Searcher it is not needed as the Execution passed gives what is needed: -

    -
      -
    • Access the search chains: execution.context().searchChainRegistry().
    • -
    • Create a new Execution: new Execution(mySearchChain, execution.context())
    • -
    -

    - This is the right way since it ties that execution to the one you're in. -

    -

    - One hence cannot execute a search chain from the search chain component constructor to e.g. refresh a cache. - It is impossible since the search chains can't be constructed until this constructor returns. - An alternative is to extract the refreshing into a separate component - which has both the client and execution factory injected into it. -

    diff --git a/en/applications/web-services.mdx b/en/applications/web-services.mdx new file mode 100644 index 0000000000..174dcf6fa9 --- /dev/null +++ b/en/applications/web-services.mdx @@ -0,0 +1,130 @@ +--- +title: "Developing Web Service Applications" +sidebarTitle: "Developing Web Services" +description: "This document explains how to develop (REST) web service type applications on the container - design options, accessing the request path, returning a status code etc. There are two types of web service APIs:" +--- + +- Fine-grained APIs with closed semantics – for example *return the number of stars of an article* +- Coarse-grained APIs with open semantics – for example *return a page containing the most relevant mixture of stuff for this user and action* + +With coarse-grained APIs, the container can help handle the complexity typically involved in the implementation of such APIs by providing a way to compose and federate components contributing to processing the request and provide and modify the returned data, and a way to allow such requests to start returning before they are finished to reduce latency with large responses. This is the [processing](/en/applications/processing) framework (or, in the case of search-like application, the [searcher](/en/applications/searchers) specialization). + +In addition, the [container](/en/reference/applications/components#component-types) features a generic mechanism allowing a [request handler](/en/applications/request-handlers) to be [bound](/en/reference/applications/components#binding) to a URI pattern and invoked to handle all requests matching that pattern. This is useful where there is no need to handle complexity and/or federation of various kinds of data in the response. Both the approaches above are actually implemented as built-in request handlers. + +A custom request handler may be written to parse the url path/method and dispatch to an appropriate chain of processing components. A "main" processing chain may be written to do the same by dispatching to other chains. The simplest way to invoke a specific chain of processors is to forward a query to the `ProcessingHandler` with the request property `chain` set to the name of the chain to invoke: + +```java +import com.yahoo.component.annotation.Inject; +public class DemoHandler extends com.yahoo.container.jdisc.ThreadedHttpRequestHandler { + ... + @Inject + public DemoHandler(Executor executor, ProcessingHandler processingHandler) { + super(executor); + this.processingHandler = processingHandler; + } + ... + @Override + public HttpResponse handle(HttpRequest request) { + HttpRequest processingRequest = new HttpRequest.Builder(request) + .put(com.yahoo.processing.Request.CHAIN, "theProcessingChainIWant") + .createDirectRequest(); + HttpResponse r = processingHandler.handle(processingRequest); + return r; + } + ... +} +``` + +## Accessing the HTTP request + +Custom [request handlers](/en/applications/request-handlers), are given a [com.yahoo.container.jdisc.HttpRequest](https://javadoc.io/doc/com.yahoo.vespa/container-disc/latest/com/yahoo/container/jdisc/HttpRequest), with direct access to associated properties and request data. + +In [Processing](/en/applications/processing), the Processors are given a [com.yahoo.processing.Request](https://javadoc.io/doc/com.yahoo.vespa/container-disc/latest/com/yahoo/processing/Request) containing the HTTP URL parameters: + +```txt +// url parameters are added to properties +String urlParameter = request.properties().get("urlParameterName"); + +// jdisc request context is added with prefix context +Object contextValue = request.properties().get("context.contextKey"); +``` + +If needed, a Processor can retrieve the entire HTTP request via a utility function: + +```java +import com.yahoo.container.jdisc.HttpRequest; ... + +// Retrieve the underlying HTTP request: Optional httpRequest = HttpRequest.getHttpRequest(request); + +if (httpRequest.isPresent()) { + // The POST data input stream: + InputStream in = httpRequest.get().getData(); + // The HTTP method: + Method method = httpRequest.get().getMethod(); +} +``` + +### Setting the HTTP status and HTTP headers + +In Processing, the return status can be set by adding a special Data item to the Response: + +```bash +response.data().add(new com.yahoo.processing.handler.ResponseStatus(404, request)); +``` + +If no such data element is present, the status will be determined by the container. If it contains data able to render, it will be 200, otherwise it will be determined by any ErrorMessage present in the response. + +### Setting response headers from Processors + +Response headers may be added to any Response by adding instances of `com.yahoo.processing.handler.ResponseHeaders` to the Response (ResponseHeaders is a kind of response Data). Multiple instances of this may be added to the Response, and the complete set of headers returned is the superset of all such objects. Example Processor: + +```bash +processingResponse.data().add(new com.yahoo.processing.handler.ResponseHeaders(myHeaders, request)); +``` + +Request handlers may in general set their return status, and manipulate headers directly on the HttpRequest. + +## Queries + +Sometimes all that is needed is letting the standard query framework reply for more paths than standard. This is possible by adding extra [binding](/en/reference/applications/services/search#binding)s inside the `` element in `services.xml`. Writing a custom [request handler](/en/applications/request-handlers) is recommended if the application is a standalone HTTP API, and especially if there are properties used with the same name as those in the [Query API](/en/reference/api/query). A request handler may query the search components running in the same container without any appreciable overhead: + +### Invoking Vespa queries from a component + +To invoke Vespa queries from a component, have an instance of [ExecutionFactory](https://github.com/vespa-engine/vespa/blob/master/container-search/src/main/java/com/yahoo/search/searchchain/ExecutionFactory.java) injected in the constructor and use its API to construct and issue the query. The container this runs in must include the `` tag for the ExecutionFactory to be available. Example: + +```java expandable +import com.yahoo.component.annotation.Inject; +import com.yahoo.component.ComponentId; +import com.yahoo.search.Query; +import com.yahoo.search.Result; +import com.yahoo.component.Chain; +import com.yahoo.search.searchchain.Execution; +import com.yahoo.search.searchchain.ExecutionFactory; + +public class MyComponent { + + private final ExecutionFactory executionFactory; + + @Inject + public MyComponent(ExecutionFactory executionFactory) { + this.executionFactory = executionFactory; + } + + Result executeQuery(Query query, String chainId) { + Chain searchChain = executionFactory.searchChainRegistry().getChain(new ComponentId(chainId)); + Execution execution = executionFactory.newExecution(searchChain); + query.getModel().setExecution(execution); + return execution.search(query); + } + +} +``` + +ExecutionFactory depends on the search chains, so it cannot be injected into any component which is part of the search chains. But from within a Searcher it is not needed as the Execution passed gives what is needed: + +- Access the search chains: execution.context().searchChainRegistry(). +- Create a new Execution: new Execution(mySearchChain, execution.context()) + +This is the right way since it ties that execution to the one you're in. + +One hence cannot execute a search chain from the search chain component constructor to e.g. refresh a cache. It is impossible since the search chains can't be constructed until this constructor returns. An alternative is to extract the refreshing into a separate component which has both the client and execution factory injected into it. \ No newline at end of file diff --git a/en/basics/applications.html b/en/basics/applications.html deleted file mode 100644 index 9505f60c61..0000000000 --- a/en/basics/applications.html +++ /dev/null @@ -1,143 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Vespa applications" -redirect_from: -- /en/cloudconfig/application-packages.html -- /en/cloudconfig/config-introduction.html -- /en/config-introduction.html -- /en/application-packages.html -- /en/applications.html ---- - -

    You use Vespa by deploying an application to it. Why applications? -Because Vespa handles both data and the -computations you do over them - together an application.

    - -

    An application is specified by an application package - a directory with some files. -The application package contains everything that is needed to run your application: -Config, schemas, components, ML models, and so on.

    - -

    The only way to change an application is to make the change in the application package -and then deploy it again. Vespa will then safely change the running system to match the new -application package revision, without impacting queries, writes, or data.

    - - -

    A minimal application package

    - -

    You can create a complete application package with just a single file: services.xml. This file -specifies the clusters that your application should run. It could just be a single stateless cluster -- what's called container - like this:

    - -
    {% highlight xml %}
    -
    -    
    -        
    -            
    -        
    -    
    -
    -{% endhighlight %}
    - -

    Put this in a file called services.xml, and you have created the world's smallest application package. -However, this won't do much, usually you want to have a content cluster which can store data, -maintain indexes, and run the distributed part of queries. You'll also want your container cluster to load -the necessary middleware for this. With that we get a services file like this:

    - -
    {% highlight xml %}
    -
    -    
    -        
    -        
    -        
    -            
    -        
    -    
    -    
    -        2
    -        
    -            
    -        
    -        
    -            
    -        
    -    
    -
    -{% endhighlight %}
    - -

    This specifies a pretty normal simple Vespa application, but now we need another file: -The schema of the document type we'll use. This goes into the directory schemas/, -so our application package now looks like this:

    - -
    -services.xml
    -schemas/myschema.sd
    -
    - -

    The schema file describes a kind of data and the computations (such as ranking/scoring) you want to do -over it. At minimum it just lists the fields of that data type and if and each field should be indexed:

    - -
    -schema myschema {
    -
    -    document myschema {
    -
    -        field text type string {
    -            indexing: summary | index
    -        }
    -
    -        field embedding type tensor<bfloat16>(x[384]) {
    -            indexing: attribute | index
    -        }
    -
    -        field popularity type double {
    -            indexing: summary | attribute
    -        }
    -
    -    }
    -
    -}
    -
    - -

    With these two files we have specified a fully functional application that can do text, -vector and hybrid search with filtering.

    - -

    Rather than creating applications from scratch like this, you can also clone one of our sample -applications as a starting point like we did in getting started.

    - -

    To read more on schemas, see the schemas guide. -To see everything an application package can contain, see the -application package reference.

    - - -

    Deploying applications

    - -

    To create running instances of an application, or make the changes to one take effect, you deploy it. -Deployments to the dev zone and to self-managed clusters sets up a single instance, while deployments to production -can set up multiple instances in one or more regions.

    - -

    To deploy an application package you use the deploy command in Vespa CLI:

    - -
    {% highlight shell %}
    -$ vespa deploy .
    -{% endhighlight %}
    - -

    This will deploy the application package at the current dir to the current target and the default dev zone -(use vespa deploy -h to see other options).

    - -

    Deployment to production zones use a separate command:

    - -
    {% highlight shell %}
    -$ vespa prod deploy .
    -{% endhighlight %}
    - -

    Production deployments also require an additional file in the application package to specify where -it should be deployed: deployment.xml. See production deployment. -The recommended way to deploy to production is by setting up a continuous deployment job, see -automated deployments.

    - -

    Deploying a change to an application package is generally safe to do at any time. -It does not disrupt queries and writes, and invalid or destructive changes are rejected before taking effect. -You can also add tests that verifies the application before deployment to production zones.

    - -
    -

    Next: Schemas

    diff --git a/en/basics/applications.mdx b/en/basics/applications.mdx new file mode 100644 index 0000000000..0044a5bcdf --- /dev/null +++ b/en/basics/applications.mdx @@ -0,0 +1,106 @@ +--- +title: Vespa applications +--- + +You use Vespa by deploying an *application* to it. Why applications? Because Vespa handles both data and the computations you do over them — together an application. + +An application is specified by an *application package* — a directory with some files. The application package contains *everything* that is needed to run your application: config, schemas, components, ML models, and so on. + +The *only* way to change an application is to make the change in the application package and then deploy it again. Vespa will then safely change the running system to match the new application package revision, without impacting queries, writes, or data. + +## A minimal application package + +You can create a complete application package with just a single file: `services.xml`. This file specifies the clusters that your application should run. It could just be a single stateless cluster — what's called *container* — like this: + +```xml + + + + + + + +``` + +Put this in a file called `services.xml`, and you have created the world's smallest application package. However, this won't do much; usually you want to have a `content` cluster which can store data, maintain indexes, and run the distributed part of queries. You'll also want your container cluster to load the necessary middleware for this. With that we get a services file like this: + +```xml + + + + + + + + + + 2 + + + + + + + + +``` + +This specifies a pretty normal simple Vespa application, but now we need another file: the schema of the document type we'll use. This goes into the directory `schemas/`, so our application package now looks like this: + +```txt +services.xml +schemas/myschema.sd +``` + +The schema file describes a kind of data and the computations (such as ranking/scoring) you want to do over it. At minimum it just lists the fields of that data type and whether each field should be indexed: + +```txt +schema myschema { + + document myschema { + + field text type string { + indexing: summary | index + } + + field embedding type tensor(x[384]) { + indexing: attribute | index + } + + field popularity type double { + indexing: summary | attribute + } + + } + +} +``` + +With these two files we have specified a fully functional application that can do text, vector and hybrid search with filtering. + +Rather than creating applications from scratch like this, you can also clone one of our sample applications as a starting point like we did in [getting started](/en/basics/deploy-an-application). + +To read more on schemas, see the [schemas](/en/basics/schemas) guide. To see everything an application package can contain, see the [application package reference](/en/reference/applications/application-packages). + +## Deploying applications + +To create running instances of an application, or make changes to one take effect, you *deploy* it. Deployments to the dev zone and to self-managed clusters set up a single instance, while deployments to production can set up multiple instances in one or more regions. + +To deploy an application package you use the [deploy command](/en/clients/vespa-cli#deployment) in Vespa CLI: + +```bash +vespa deploy . +``` + +This will deploy the application package at the current directory to the current target and the default dev zone (use `vespa deploy -h` to see other options). + +Deployment to production zones use a separate command: + +```bash +vespa prod deploy . +``` + +Production deployments also require an additional file in the application package to specify where it should be deployed: `deployment.xml`. See [production deployment](/en/operations/production-deployment). The recommended way to deploy to production is by setting up a continuous deployment job — see [automated deployments](/en/operations/automated-deployments). + +Deploying a change to an application package is generally safe to do at any time. It does not disrupt queries and writes, and invalid or destructive changes are rejected before taking effect. You can also add tests that verify the application before deployment to production zones. + diff --git a/en/basics/deploy-an-application-java.html b/en/basics/deploy-an-application-java.html deleted file mode 100644 index 1d3ea0313b..0000000000 --- a/en/basics/deploy-an-application-java.html +++ /dev/null @@ -1,108 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: Deploy an application having Java components -applies_to: cloud -redirect_from: -- /en/cloud/getting-started-java.html -- /en/deploy-an-application-java.html ---- - - - -

    Follow these steps to deploy a Vespa application which includes Java components -to the dev zone on Vespa Cloud (for free).

    - -

    Alternative versions of this guide:

    - - -
    - - - -{% include setup.html appname='album-recommendation-java' %} - -

    Steps:

    - -
      -
    1. -

      Build the application:

      -
      - -
      -$ mvn -U -f app package
      -
      -
      -
    2. - -
    3. -

      Deploy the application:

      -
      - -
      -$ vespa deploy --wait 600 ./app
      -
      -
      -

      The first deployment may take a few minutes while nodes are provisioned.

      -
    4. - -
    5. -

      Feed documents:

      -
      - -
      -$ vespa feed app/src/test/resources/*.json
      -
      -
      -
    6. - -
    7. -

      Run queries:

      -
      - -
      -$ vespa query "select * from music where album contains 'head'"
      -
      -
      -
      - -
      {% raw %}
      -$ vespa query \
      -    "select * from music where true" \
      -    "ranking=rank_albums" \
      -    "ranking.features.query(user_profile)={{cat:pop}:0.8,{cat:rock}:0.2,{cat:jazz}:0.1}"
      -{% endraw %}
      -
      -
    8. -
    - -
    - -

    Congratulations, you have deployed your first Vespa application! -Application instances in the dev zone -will by default keep running for 14 days after the last deployment. -You can control this in the -console.

    - -

    You can inspect the source code for this app at -sample-apps.

    - -
    -

    Next: Vespa applications

    - -
    -$ vespa destroy --force
    -
    - diff --git a/en/basics/deploy-an-application-java.mdx b/en/basics/deploy-an-application-java.mdx new file mode 100644 index 0000000000..e5f3e6c492 --- /dev/null +++ b/en/basics/deploy-an-application-java.mdx @@ -0,0 +1,110 @@ +--- +title: Deploy an application having Java components +--- + +Follow these steps to deploy a Vespa application which includes Java components to the [dev zone](/en/operations/environments#dev) on Vespa Cloud (for free). + +Alternative versions of this guide: + +- [Deploy an application using pyvespa](https://vespa-engine.github.io/pyvespa/getting-started-pyvespa-cloud.html) - for Python developers +- [Deploy an application without Java components](/en/basics/deploy-an-application) +- [Deploy an application without Vespa CLI](/en/basics/deploy-an-application-shell) +- [Deploy an application locally](/en/basics/deploy-an-application-local) +- [Deploy an application having Java components locally](/en/basics/deploy-an-application-local-java) + + + +**Prerequisites:** + +- [Java 17](https://openjdk.org/projects/jdk/17/). +- [Apache Maven](https://maven.apache.org/install.html) to build the application. + + +Setup: + + + +**Create a [tenant](/en/learn/tenant-apps-instances) on Vespa Cloud:** + + +**Install the [Vespa CLI](/en/clients/vespa-cli)** using [Homebrew](https://brew.sh/): + +```bash +$ brew install vespa-cli +``` + + +**Configure the Vespa client:** + +```bash +$ vespa config set target cloud +$ vespa config set application vespa-team.autotest +``` + + +**Get Vespa Cloud control plane access:** + +```bash +$ vespa auth login +``` + + + +**Clone a sample [application](/en/basics/applications):** + +```bash +$ vespa clone album-recommendation myapp && cd myapp +``` + + +**Add a certificate for [data plane access](/en/security/guide#data-plane) to the application:** + +```bash +$ vespa auth cert app +``` + + + + +Steps: + + + +**Build the application:** + +```bash +$ mvn install -f app +``` + + +**[Deploy](/en/basics/applications#deploying-applications) the application:** + +```bash +$ vespa deploy --wait 600 ./app +``` + + +**[Feed](/en/writing/reads-and-writes) [documents](/en/schemas/documents):** + +```bash +$ vespa feed app/src/test/resources/*.json +``` + + +**Run [queries](/en/querying/query-api):** + +```bash +vespa query "select * from music where album contains 'head'" +``` +```bash +vespa query \ + "select * from music where true" \ + "ranking=rank_albums" \ + "ranking.features.query(user_profile)={{cat:pop}:0.8,{cat:rock}:0.2,{cat:jazz}:0.1}" +``` + + + +Congratulations, you have deployed your first Vespa application! Application instances in the [dev zone](/en/operations/environments#dev) will by default keep running for 14 days after the last deployment. You can control this in the [console](https://console.vespa-cloud.com/). + +You can inspect the source code for this app at [sample-apps](https://github.com/vespa-engine/sample-apps/tree/master/album-recommendation-java). \ No newline at end of file diff --git a/en/basics/deploy-an-application-local-java.html b/en/basics/deploy-an-application-local-java.html deleted file mode 100644 index 4142e432e9..0000000000 --- a/en/basics/deploy-an-application-local-java.html +++ /dev/null @@ -1,157 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: Deploy an application having Java components locally -redirect_from: -- /en/vespa-quick-start-java.html -- /en/deploy-an-application-local-java.html ---- - - -

    Follow these steps to deploy a Vespa application having Java components on your own machine.

    - -

    Alternative versions of this guide:

    - - -

    This is tested with vespaengine/vespa:{{site.variables.vespa_version}} container image.

    - -
    - -{% include pre-req.html memory="4 GB" extra-reqs=' -
  • Java 17.
  • -
  • Apache Maven is used to build the application.
  • ' %} - -

    Steps:

    - -
      -
    1. -

      Validate the environment:

      -
      - -
      -$ docker info | grep "Total Memory"
      -or
      -$ podman info | grep "memTotal"
      -
      -
      -

      Make sure you see at minimum 4 GB.

      -
    2. - -
    3. -

      Install the Vespa CLI - using Homebrew:

      -
      - -
      -$ brew install vespa-cli
      -
      -
      -

      Windows/No Homebrew? See the Vespa CLI page to download directly.

      -
    4. - -
    5. -

      Set local target:

      -
      - -
      -$ vespa config set target local
      -
      -
      -
    6. - -
    7. -

      Start a Vespa Docker container:

      -
      - -
      -$ docker run --detach --name vespa --hostname vespa-container \
      -  --publish 8080:8080 --publish 19071:19071 \
      -  vespaengine/vespa
      -
      -
      -

      - The port 8080 is published to make the query and feed endpoints - accessible from outside the docker container, - 19071 is the config server endpoint. - Only one docker container named vespa can run at a time, change the name as needed. - See Docker containers for more insights. -

      -
    8. - -
    9. -

      Clone a sample application:

      -
      - -
      -$ vespa clone album-recommendation-java myapp && cd myapp
      -
      -
      -
    10. - -
    11. -

      Build it:

      -
      - -
      -$ mvn install -f app
      -
      -
      -
    12. - -
    13. -

      Deploy the application:

      -
      - -
      -$ vespa deploy --wait 300 ./app
      -
      -
      -
    14. - -
    15. -

      Feed documents:

      -
      - -
      -$ vespa feed app/src/test/resources/*.json
      -
      -
      -
    16. - -
    17. -

      Run queries:

      -
      - -
      -$ vespa query "select * from music where album contains 'head'"
      -
      -
      -
      - -
      {% raw %}
      -$ vespa query \
      -  "select * from music where true" \
      -  "ranking=rank_albums" \
      -  "ranking.features.query(user_profile)={{cat:pop}:0.8,{cat:rock}:0.2,{cat:jazz}:0.1}"
      -{% endraw %}
      -
      -
    18. -
    - -
    - -

    Congratulations, you have deployed your first Vespa application!

    - -
    -

    Next: Vespa applications

    - - - diff --git a/en/basics/deploy-an-application-local-java.mdx b/en/basics/deploy-an-application-local-java.mdx new file mode 100644 index 0000000000..93bc2dbe8e --- /dev/null +++ b/en/basics/deploy-an-application-local-java.mdx @@ -0,0 +1,106 @@ +--- +title: Deploy an application having Java components locally +--- + +Follow these steps to deploy a Vespa application which includes Java components to the [dev zone](/en/operations/environments#dev) on Vespa Cloud (for free). + +Alternative versions of this guide: + +- [Deploy an application using pyvespa](https://vespa-engine.github.io/pyvespa/getting-started-pyvespa-cloud.html) - for Python developers +- [Deploy an application without Java components](/en/basics/deploy-an-application) +- [Deploy an application without Vespa CLI](/en/basics/deploy-an-application-shell) +- [Deploy an application locally](/en/basics/deploy-an-application-local) +- [Deploy an application having Java components locally](/en/basics/deploy-an-application-local-java) + + + +**Prerequisites:** + +- [Java 17](https://openjdk.org/projects/jdk/17/). +- [Apache Maven](https://maven.apache.org/install.html) to build the application. + +Setup: + + + +**Create a [tenant](/en/learn/tenant-apps-instances) on Vespa Cloud:** + + +**Install the [Vespa CLI](/en/clients/vespa-cli)** using [Homebrew](https://brew.sh/): + +```bash +$ brew install vespa-cli +``` + + +**Configure the Vespa client:** + +```bash +$ vespa config set target cloud +$ vespa config set application vespa-team.autotest +``` + + +**Get Vespa Cloud control plane access:** + +```bash +$ vespa auth login +``` + + +**Clone a sample [application](/en/basics/applications):** + +```bash +$ vespa clone album-recommendation-java myapp && cd myapp +``` + + +**Add a certificate for [data plane access](/en/security/guide#data-plane) to the application:** + +```bash +$ vespa auth cert app +``` + + +Steps: + + + +**Build the application:** + +```bash +$ mvn -U -f app package +``` + + +**[Deploy](/en/basics/applications#deploying-applications) the application:** + +```bash +$ vespa deploy --wait 600 ./app +``` +The first deployment may take a few minutes while nodes are provisioned. + + +**[Feed](/en/writing/reads-and-writes) [documents](/en/schemas/documents):** + +```bash +$ vespa feed app/src/test/resources/*.json +``` + + +**Run [queries](/en/querying/query-api):** +```bash +vespa query "select * from music where album contains 'head'" +``` +```bash +vespa query \ + "select * from music where true" \ + "ranking=rank_albums" \ + "ranking.features.query(user_profile)={{cat:pop}:0.8,{cat:rock}:0.2,{cat:jazz}:0.1}" +``` + + + +Congratulations, you have deployed your first Vespa application! Application instances in the [dev zone](/en/operations/environments#dev) will by default keep running for 14 days after the last deployment. You can control this in the [console](https://console.vespa-cloud.com/). + +You can inspect the source code for this app at [sample-apps](https://github.com/vespa-engine/sample-apps/tree/master/album-recommendation-java). diff --git a/en/basics/deploy-an-application-local.html b/en/basics/deploy-an-application-local.html deleted file mode 100644 index eed4bfadad..0000000000 --- a/en/basics/deploy-an-application-local.html +++ /dev/null @@ -1,165 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: Deploy an application locally -redirect_from: -- /en/vespa-quick-start.html -- /en/deploy-an-application-local.html ---- - -

    Follow these steps to deploy a Vespa application on your own machine.

    - -

    Alternative versions of this guide:

    - - -

    This is tested with vespaengine/vespa:{{site.variables.vespa_version}} container image.

    - -
    - -{% include pre-req.html memory="4 GB" %} - -

    Steps:

    - -
      - -
    1. -

      Validate the environment:

      -
      - -
      -$ docker info | grep "Total Memory"
      -or
      -$ podman info | grep "memTotal"
      -
      -
      -

      Make sure you see at minimum 4 GB.

      -
    2. - -
    3. -

      Install the Vespa CLI - using Homebrew:

      -
      - -
      -$ brew install vespa-cli
      -
      -
      -

      Windows/No Homebrew? See the Vespa CLI page to download directly.

      -
    4. - -
    5. -

      Set local target:

      -
      - -
      -$ vespa config set target local
      -
      -
      -
    6. - -
    7. -

      Start a Vespa Docker container:

      -
      - -
      -$ docker run --detach --name vespa --hostname vespa-container \
      -  --publish 8080:8080 --publish 19071:19071 \
      -  vespaengine/vespa
      -
      -
      -

      Alternatively, use podman in the command above.

      -

      - The port 8080 is published to make the search and feed interfaces accessible from outside the container, - 19071 is the deploy-endpoint. - Only one container named vespa can run at a time, so change the name if needed. - See Docker containers for more insights. -

      -
    8. - -
    9. -

      Clone a sample application:

      -
      - -
      -$ vespa clone album-recommendation myapp && cd myapp
      -
      -
      -
    10. - -
    11. -

      Deploy the application:

      -
      - -
      -$ vespa deploy --wait 300 ./app
      -
      -
      -
    12. - -
    13. -

      Feed documents:

      -
      - -
      -$ vespa feed dataset/documents.jsonl
      -
      -
      -
    14. - -
    15. -

      Run queries:

      -
      - -
      -$ vespa query "select * from music where album contains 'head'"
      -
      -
      -
      - -
      {% raw %}
      -$ vespa query \
      -  "select * from music where true" \
      -  "ranking=rank_albums" \
      -  "ranking.features.query(user_profile)={{cat:pop}:0.8,{cat:rock}:0.2,{cat:jazz}:0.1}"
      -{% endraw %}
      -
      -
    16. - -
    17. -

      Get documents:

      -
      - -
      -$ vespa document get id:mynamespace:music::a-head-full-of-dreams
      -
      -
      -
      - -
      -$ vespa visit
      -
      -
      -

      - Get a document by ID, or export all documents - - see /document/v1 - and vespa visit.

      -
    18. -
    - -
    - -

    Congratulations, you have deployed your first Vespa application!

    - -
    -

    Next: Vespa applications

    - - - diff --git a/en/basics/deploy-an-application-local.mdx b/en/basics/deploy-an-application-local.mdx new file mode 100644 index 0000000000..d6ec166d5c --- /dev/null +++ b/en/basics/deploy-an-application-local.mdx @@ -0,0 +1,119 @@ +--- +title: Deploy an application locally +--- + +Follow these steps to deploy a Vespa application on your own machine. + +Alternative versions of this guide: + +- [Deploy an application using pyvespa](https://vespa-engine.github.io/pyvespa/getting-started-pyvespa-cloud.html) - for Python developers +- [Deploy an application](/en/basics/deploy-an-application) +- [Deploy an application having Java components](/en/basics/deploy-an-application-java) +- [Deploy an application without Vespa CLI](/en/basics/deploy-an-application-shell) +- [Deploy an application having Java components locally](/en/basics/deploy-an-application-local-java) + +This is tested with _vespaengine/vespa:8.692.16_ container image. + + + +**Prerequisites:** + +- Linux, macOS or Windows 10 Pro on x86\_64 or arm64, with [Podman Desktop](https://podman.io/) or [Docker Desktop](https://www.docker.com/products/docker-desktop/) installed, with an engine running. + - Alternatively, start the Podman daemon: + ```bash + $ podman machine init --memory 6000 + $ podman machine start + ``` + - See [Docker Containers](/en/operations/self-managed/docker-containers) for system limits and other settings. + +- For CPUs older than Haswell (2013), see [CPU Support](/en/operations/self-managed/cpu-support). +- Memory: Minimum 4 GB RAM dedicated to Docker/Podman. [Memory recommendations](/en/operations/self-managed/node-setup#memory-settings). +- Disk: Avoid `NO_SPACE` - the vespaengine/vespa container image + headroom for data requires disk space. [Read more](/en/writing/feed-block). +- [Homebrew](https://brew.sh/) to install the [Vespa CLI](/en/clients/vespa-cli), or download the Vespa CLI from [Github releases](https://github.com/vespa-engine/vespa/releases). + + + + + +**Validate the environment:** + +```bash +$ docker info | grep "Total Memory" + +or + +$ podman info | grep "memTotal" +``` + + +**Install the [Vespa CLI](/en/clients/vespa-cli)** using [Homebrew](https://brew.sh/): + +```bash +$ brew install vespa-cli +``` +Windows/No Homebrew? See the [Vespa CLI page](/en/clients/vespa-cli) to download directly. + + +**Set local target:** +```bash +$ vespa config set target local +``` + + +**Start a Vespa Docker container:** +```bash +docker run --detach --name vespa --hostname vespa-container \ + --publish 8080:8080 --publish 19071:19071 \ + vespaengine/vespa +``` +Alternatively, use `podman` in the command above. + +The port `8080` is published to make the search and feed interfaces accessible from outside the container, `19071` is the deploy-endpoint. Only one container named `vespa` can run at a time, so change the name if needed. See [Docker containers](/en/operations/self-managed/docker-containers) for more insights. + + + +**Clone a sample [application](/en/basics/applications):** +```bash +$ vespa clone album-recommendation myapp && cd myapp +``` + + + +**[Deploy](/en/basics/applications#deploying-applications) the application:** +```bash +$ vespa deploy --wait 300 ./app +``` + + + +**[Feed](/en/writing/reads-and-writes) [documents](/en/schemas/documents):** +```bash +$ vespa feed dataset/documents.jsonl +``` + + +**Run [queries](/en/querying/query-api):** +```bash +vespa query "select * from music where album contains 'head'" +``` +```bash +vespa query \ + "select * from music where true" \ + "ranking=rank_albums" \ + "ranking.features.query(user_profile)={{cat:pop}:0.8,{cat:rock}:0.2,{cat:jazz}:0.1}" +``` + + +**Get documents:** +```bash +vespa document get id:mynamespace:music::a-head-full-of-dreams +``` +```bash +vespa visit +``` + +Get a document by ID, or export all documents - see [/document/v1](/en/writing/document-v1-api-guide) and [vespa visit](/en/writing/visiting). + + + +Congratulations, you have deployed your first Vespa application! \ No newline at end of file diff --git a/en/basics/deploy-an-application-shell.html b/en/basics/deploy-an-application-shell.html deleted file mode 100644 index e3e4c0ded1..0000000000 --- a/en/basics/deploy-an-application-shell.html +++ /dev/null @@ -1,209 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: Deploy an application without Vespa CLI -applies_to: cloud -redirect_from: -- /en/cloud/getting-started-shell.html -- /en/deploy-an-application-shell.html ---- - - - -

    This lets you deploy an application to the dev zone -on Vespa Cloud (for free).

    - -

    Alternative versions of this guide:

    - - -
    - - - -

    Steps:

    - -
      - -
    1. -

      Create a tenant on Vespa Cloud:

      -

      Go to console.vespa-cloud.com - and create your tenant (unless you already have one).

      -
    2. - -
    3. -

      Clone a sample application:

      -
      - -
      -$ git clone --depth 1 https://github.com/vespa-engine/sample-apps.git && \
      -  cd sample-apps/album-recommendation
      -
      -
      -

      See sample-apps for other sample apps you can clone.

      -
    4. - -
    5. -

      Add a certificate for data plane access - to the application:

      -

      On Unix or Mac, use openssl:

      -
      - -
      -$ openssl req -x509 -nodes -days 14 -newkey rsa:4096 \
      -  -subj "/CN=cloud.vespa.example" \
      -  -keyout data-plane-private-key.pem -out data-plane-public-cert.pem
      -
      -
      -

      On Windows, the certificate has to be created with New-SelfSignedCertificate in PowerShell, and - then exported to PEM format using certutil.

      -

      Once the certificate has been created, add it to the application package.

      -
      - -
      -$ mkdir -p app/security && \
      -  cp data-plane-public-cert.pem app/security/clients.pem
      -
      -
      -
    6. - -
    7. -

      Create a deployable application package zip:

      -
      - -
      -$ ( cd app && zip -r ../application.zip . )
      -
      -
      -
    8. - -
    9. -

      Deploy the application:

      -

      - In the console, click Deploy Application. - Use "myapp" as the application name, leave the defaults. - Make sure DEV is selected, and upload the application.zip. - Click Create and deploy. -

      -

      The first deployment may take a few minutes while nodes are provisioned.

      - - - - -
      -$ export VESPA_CLI_HOME=$PWD/.vespa TMPDIR=$PWD/.tmp
      -$ mkdir -p $TMPDIR
      -$ mkdir -p $VESPA_CLI_HOME/vespa-team.album-rec-java.default
      -$ vespa config set target cloud
      -$ vespa config set application vespa-team.album-rec-java
      -$ export VESPA_CLI_API_KEY="$(echo "$VESPA_TEAM_API_KEY" | openssl base64 -A -a -d)"
      -$ cp data-plane-public-cert.pem $VESPA_CLI_HOME/vespa-team.album-rec-java.default
      -$ cp data-plane-private-key.pem $VESPA_CLI_HOME/vespa-team.album-rec-java.default
      -$ vespa deploy --wait 600
      -$ export ENDPOINT=https://album-rec-java.vespa-team.aws-us-east-1c.dev.z.vespa-app.cloud/
      -
      -
    10. - -
    11. -

      Verify the application endpoint:

      -
      - -
      -$ ENDPOINT=https://name.myapp.tenant-name.aws-us-east-1c.dev.z.vespa-app.cloud/
      -
      -
      -
      - -
      -$ curl --cert data-plane-public-cert.pem --key data-plane-private-key.pem $ENDPOINT
      -
      -
      -

      You can find the endpoint in the console deployment output, set it for later use and test it. - You can also do this in a browser.

      -
    12. - -
    13. -

      Feed documents:

      -
      - -
      -$ curl --cert data-plane-public-cert.pem --key data-plane-private-key.pem \
      -  -H "Content-Type:application/json" \
      -  --data-binary @ext/A-Head-Full-of-Dreams.json \
      -  $ENDPOINT/document/v1/mynamespace/music/docid/a-head-full-of-dreams
      -
      -
      -
      - -
      -$ curl --cert data-plane-public-cert.pem --key data-plane-private-key.pem \
      -  -H "Content-Type:application/json" \
      -  --data-binary @ext/Love-Is-Here-To-Stay.json \
      -  $ENDPOINT/document/v1/mynamespace/music/docid/love-is-here-to-stay
      -
      -
      -
      - -
      -$ curl --cert data-plane-public-cert.pem --key data-plane-private-key.pem \
      -  -H "Content-Type:application/json" \
      -  --data-binary @ext/Hardwired...To-Self-Destruct.json \
      -  $ENDPOINT/document/v1/mynamespace/music/docid/hardwired-to-self-destruct
      -
      -
      -
    14. - -
    15. -

      Run queries:

      -
      - -
      {% raw %}
      -curl --cert data-plane-public-cert.pem --key data-plane-private-key.pem \
      -  -X POST -H "Content-Type: application/json" --data '
      -  {
      -      "yql": "select * from music where true",
      -      "ranking": {
      -          "profile": "rank_albums",
      -          "features": {
      -              "query(user_profile)": "{{cat:pop}:0.8,{cat:rock}:0.2,{cat:jazz}:0.1}"
      -          }
      -      }
      -  }' \
      -  $ENDPOINT/search/
      -{% endraw %}
      -
      - -
      -$ curl --cert data-plane-public-cert.pem --key data-plane-private-key.pem \
      -"$ENDPOINT/search/?ranking=rank_albums&yql=select%20%2A%20from%20music%20where%20true&ranking.features.query(user_profile)=%7B%7Bcat%3Apop%7D%3A0.8%2C%7Bcat%3Arock%7D%3A0.2%2C%7Bcat%3Ajazz%7D%3A0.1%7D"
      -
      -
    16. -
    - -
    - -

    Congratulations, you have deployed your first Vespa application! -Application instances in the dev zone -will by default keep running for 14 days after the last deployment. -You can control this in the -console.

    - - -
    -

    Next: Vespa applications

    diff --git a/en/basics/deploy-an-application-shell.mdx b/en/basics/deploy-an-application-shell.mdx new file mode 100644 index 0000000000..e020606335 --- /dev/null +++ b/en/basics/deploy-an-application-shell.mdx @@ -0,0 +1,129 @@ +--- +title: Deploy an application without Vespa CLI +--- + +This lets you deploy an application to the [dev zone](/en/operations/environments#dev) on Vespa Cloud (for free). + +Alternative versions of this guide: + +- [Deploy an application using pyvespa](https://vespa-engine.github.io/pyvespa/getting-started-pyvespa-cloud.html) - for Python developers +- [Deploy an application](/en/basics/deploy-an-application) +- [Deploy an application having Java components](/en/basics/deploy-an-application-java) +- [Deploy an application locally](/en/basics/deploy-an-application-local) +- [Deploy an application with Java components locally](/en/basics/deploy-an-application-local-java) + + + + +**Prerequisites:** + +- git - or download the files from [album-recommendation](https://github.com/vespa-engine/sample-apps/tree/master/album-recommendation) +- zip - or other tool to create a .zip file +- curl - or other tool to send HTTP requests with security credentials +- OpenSSL + + +Steps: + + + +**Create a [tenant](/en/learn/tenant-apps-instances) on Vespa Cloud:** + +Go to [console.vespa-cloud.com](https://console.vespa-cloud.com/) and create your tenant (unless you already have one). + + +**Clone a sample [application](/en/basics/applications):** +```bash +git clone --depth 1 https://github.com/vespa-engine/sample-apps.git && \ + cd sample-apps/album-recommendation +``` +See [sample-apps](https://github.com/vespa-engine/sample-apps) for other sample apps you can clone + + +**Add a certificate for [data plane access](/en/security/guide#data-plane) to the application:** + +On Unix or Mac, use `openssl`: + +```bash +openssl req -x509 -nodes -days 14 -newkey rsa:4096 \ + -subj "/CN=cloud.vespa.example" \ + -keyout data-plane-private-key.pem -out data-plane-public-cert.pem +``` +On Windows, the certificate has to be created with [New-SelfSignedCertificate](https://learn.microsoft.com/en-us/powershell/module/pki/new-selfsignedcertificate) in PowerShell, and then exported to PEM format using [certutil](https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/certutil). + +Once the certificate has been created, add it to the application package. + +```bash +mkdir -p app/security && \ + cp data-plane-public-cert.pem app/security/clients.pem +``` + + + +**Create a deployable application package zip:** +```bash +( cd app && zip -r ../application.zip . ) +``` + + + +**Deploy the application:** + +In the [console](https://console.vespa-cloud.com/), click *Deploy Application*. Use "myapp" as the application name, leave the defaults. Make sure *DEV* is selected, and upload the `application.zip`. Click *Create and deploy*. + +The first deployment may take a few minutes while nodes are provisioned. + + + +**Verify the application endpoint:** + +```bash +ENDPOINT=https://name.myapp.tenant-name.aws-us-east-1c.dev.z.vespa-app.cloud/ +``` +```bash +curl --cert data-plane-public-cert.pem --key data-plane-private-key.pem $ENDPOINT +``` +You can find the endpoint in the console deployment output, set it for later use and test it. You can also [do this in a browser](/en/security/guide#using-a-browser). + + +**[Feed](/en/writing/reads-and-writes) [documents](/en/schemas/documents):** +```bash +curl --cert data-plane-public-cert.pem --key data-plane-private-key.pem \ + -H "Content-Type:application/json" \ + --data-binary @ext/A-Head-Full-of-Dreams.json \ + $ENDPOINT/document/v1/mynamespace/music/docid/a-head-full-of-dreams +``` +```bash +curl --cert data-plane-public-cert.pem --key data-plane-private-key.pem \ + -H "Content-Type:application/json" \ + --data-binary @ext/Love-Is-Here-To-Stay.json \ + $ENDPOINT/document/v1/mynamespace/music/docid/love-is-here-to-stay +``` +```bash +curl --cert data-plane-public-cert.pem --key data-plane-private-key.pem \ + -H "Content-Type:application/json" \ + --data-binary @ext/Hardwired...To-Self-Destruct.json \ + $ENDPOINT/document/v1/mynamespace/music/docid/hardwired-to-self-destruct +``` + + +**Run [queries](/en/querying/query-api):** +```bash +curl --cert data-plane-public-cert.pem --key data-plane-private-key.pem \ + -X POST -H "Content-Type: application/json" --data ' + { + "yql": "select * from music where true", + "ranking": { + "profile": "rank_albums", + "features": { + "query(user_profile)": "{{cat:pop}:0.8,{cat:rock}:0.2,{cat:jazz}:0.1}" + } + } + }' \ + $ENDPOINT/search/ +``` + + + + +Congratulations, you have deployed your first Vespa application! Application instances in the [dev zone](/en/operations/environments#dev) will by default keep running for 14 days after the last deployment. You can control this in the [console](https://console.vespa-cloud.com/). diff --git a/en/basics/deploy-an-application.html b/en/basics/deploy-an-application.html deleted file mode 100644 index 753d983ceb..0000000000 --- a/en/basics/deploy-an-application.html +++ /dev/null @@ -1,84 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: Deploy an application -applies_to: cloud -redirect_from: -- /en/cloud/getting-started.html -- /en/deploy-an-application.html ---- - - - -

    Follow these steps to deploy a Vespa application to the dev zone -on Vespa Cloud (for free).

    - -

    Alternative versions of this guide:

    - - - -{% include setup.html appname='album-recommendation' %} - -

    Steps:

    -
      -
    1. -

      Deploy the application:

      -
      - -
      -$ vespa deploy --wait 600 ./app
      -
      -
      -

      The first deployment may take a few minutes while nodes are provisioned.

      -
    2. - -
    3. -

      Feed documents:

      -
      - -
      -$ vespa feed dataset/documents.jsonl
      -
      -
      -
    4. - -
    5. -

      Run queries:

      -
      - -
      -$ vespa query "select * from music where album contains 'head'"
      -
      -
      -
      - -
      {% raw %}
      -$ vespa query \
      -  "select * from music where true" \
      -  "ranking=rank_albums" \
      -  "ranking.features.query(user_profile)={{cat:pop}:0.8,{cat:rock}:0.2,{cat:jazz}:0.1}"
      -{% endraw %}
      -
      -
    6. -
    - -
    - -

    Congratulations, you have deployed your first Vespa application! -Application instances in the dev zone -will by default keep running for 14 days after the last deployment. -You can control this in the -console.

    - -
    -

    Next: Vespa applications

    - -
    -$ vespa destroy --force
    -
    diff --git a/en/basics/deploy-an-application.mdx b/en/basics/deploy-an-application.mdx new file mode 100644 index 0000000000..3891c93a62 --- /dev/null +++ b/en/basics/deploy-an-application.mdx @@ -0,0 +1,111 @@ +--- +title: Deploy an application +description: "Follow these steps to deploy a Vespa application to the [dev zone](/en/operations/environments#dev) on Vespa Cloud (for free)." +--- + +{/* If you change this also make the same change in deploy-an-application-{shell,java} */} + +**Alternative versions of this guide:** + +- [Deploy an application using pyvespa](https://vespa-engine.github.io/pyvespa/getting-started-pyvespa-cloud.html) — for Python developers +- [Deploy an application having Java components](/en/basics/deploy-an-application-java) +- [Deploy an application without Vespa CLI](/en/basics/deploy-an-application-shell) +- [Deploy an application locally](/en/basics/deploy-an-application-local) +- [Deploy an application having Java components locally](/en/basics/deploy-an-application-local-java) + +## Setup + + + + Create a [tenant](/en/learn/tenant-apps-instances) on Vespa Cloud: + + Go to [console.vespa-cloud.com](https://console.vespa-cloud.com/) and create your tenant (unless you already have one). + + + + Install the [Vespa CLI](/en/clients/vespa-cli) using [Homebrew](https://brew.sh/): + + ```bash + brew install vespa-cli + ``` + + Windows or no Homebrew? See the [Vespa CLI](/en/clients/vespa-cli) page to download directly. + + + + ```bash + export VESPA_CLI_HOME=$PWD/.vespa + vespa config set target cloud + vespa config set application vespa-team.autotest + ``` + + Use the tenant name from step 1 instead of `vespa-team`, and replace it in other steps in this guide too. + + + + ```bash + vespa auth login + ``` + + Follow the instructions from the command to authenticate. + + + + **Clone a sample [application](/en/basics/applications):** + + ```bash + vespa clone album-recommendation myapp && cd myapp + ``` + + See [sample-apps](https://github.com/vespa-engine/sample-apps) for other sample apps you can clone. + + + + **Add a certificate for [data plane access](/en/security/guide#data-plane) to the application:** + + ```bash + vespa auth cert app + ``` + + It is a good idea to take note of the path to the `.pem` files written here. + + + +Steps: + + + + [Deploy](/en/basics/applications#deploying-applications) the application: + + ```bash + vespa deploy --wait 600 ./app + ``` + + The first deployment may take a few minutes while nodes are provisioned. + + + + [Feed](/en/writing/reads-and-writes) [documents](/en/schemas/documents): + + ```bash + vespa feed dataset/documents.jsonl + ``` + + + + Run [queries](/en/querying/query-api): + + ```bash + vespa query "select * from music where album contains 'head'" + ``` + + ```bash + vespa query \ + "select * from music where true" \ + "ranking=rank_albums" \ + "ranking.features.query(user_profile)={{cat:pop}:0.8,{cat:rock}:0.2,{cat:jazz}:0.1}" + ``` + + + +Congratulations, you have deployed your first Vespa application! Application instances in the [dev zone](/en/operations/environments#dev) will by default keep running for 14 days after the last deployment. You can control this in the [console](https://console.vespa-cloud.com/). \ No newline at end of file diff --git a/en/basics/operations.html b/en/basics/operations.html deleted file mode 100644 index a9bba15c7b..0000000000 --- a/en/basics/operations.html +++ /dev/null @@ -1,83 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Operations" -redirect_from: -- /en/operations.html ---- - -

    A deployed Vespa application is a self-contained highly available, distributed stateful system. -Operating these at scale is difficult, so Vespa automates this to the extent possible in the -deployment environment it is running.

    - -
    - - - - - - - - - - - - -
    Deployment environmentAutomated operationsSuitable for
    Vespa self-managed/open source - Application deployment (single application, single instance), application change (except rolling restarts), - data redistribution, failover - Development -
    Vespa Kubernetes Operator - Application deployment (single application, single instance), application change, - data redistribution, failover, node provisioning, failed node replacement, node type change, - autoscaling, - endpoint routing, - encryption - Production in environments outside hyperscalers -
    Vespa Cloud - Application deployment (multiple applications, instances, regions, clouds), - application change, - data redistribution, failover, node provisioning, failed node replacement, node type change, - autoscaling, - endpoint routing, - encryption, - Vespa platform and OS upgrades, continuous deployment pipeline with verification, metrics and management console - Development, production on hyperscalers (including in customer accounts and VPCs). -
    - -

    Vespa is designed to enable applications to evolve in production. -This includes these aspects:

    - -
      -
    • Application package changes are managed by Vespa's built-in control plane to be carried out - without impacting queries or writes. If a change can not be made without impacting queries or writes, - it is rejected on deployment (and will require a validation - override to be allowed). -
    • The operations supported by Vespa are those that can be scaled to hundreds of nodes, billions of - documents and hundreds of thousands of queries per second. If you can run it on a single machine, - you can scale it. -
    • The hardware resources available in a cluster can be changed both up and down. Redistribution - will happen automatically in the background, without limited resource usage to avoid impacting - queries and writes. -
    • When possible (on Vespa Cloud), new revisions of applications are deployed in test - zones where they can be verified by application-supplied functional tests before being allowed to progress - to production. -
    - -

    Performance and scaling

    - -

    Content clusters in Vespa can be scaled to any amount of content by adding more nodes (horizontal scaling). -Data will redistribute automatically, and there's no need for manual tuning of the process. -To scale to large amounts of queries, content clusters can also be scaled by adding multiple groups -of nodes (vertical scaling). Each group contains a single copy of the corpus and container clusters -will automatically load balance over groups.

    - -

    A Vespa application can consist of any number of stateless and stateful clusters. -On larger applications it can be beneficial to split different functions into separate clusters -that can be optimized separately. For example, having one stateless container cluster for -feeding and another for querying, or using different content clusters for different data schemas.

    - -

    Read more in elasticity and the performance guide.

    - - -
    -

    Next: What's more

    diff --git a/en/basics/operations.mdx b/en/basics/operations.mdx new file mode 100644 index 0000000000..ab78c68317 --- /dev/null +++ b/en/basics/operations.mdx @@ -0,0 +1,46 @@ +--- +title: Operations +description: "A deployed Vespa application is a self-contained highly available, distributed stateful system. Operating these at scale is difficult, so Vespa automates this to the extent possible in the deployment environment it is running." +--- + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Deployment environmentAutomated operationsSuitable for
    Vespa self-managed/open sourceApplication deployment (single application, single instance), application change (except rolling restarts), data redistribution, failoverDevelopment
    Vespa Kubernetes OperatorApplication deployment (single application, single instance), application change, data redistribution, failover, node provisioning, failed node replacement, node type change, autoscaling, endpoint routing, encryptionProduction in environments outside hyperscalers
    Vespa CloudApplication deployment (multiple applications, instances, regions, clouds), application change, data redistribution, failover, node provisioning, failed node replacement, node type change, autoscaling, endpoint routing, encryption, Vespa platform and OS upgrades, continuous deployment pipeline with verification, metrics and management consoleDevelopment, production on hyperscalers (including in customer accounts and VPCs)
    + +Vespa is designed to enable applications to evolve in production. This includes these aspects: + +- Application package changes are managed by Vespa's built-in control plane to be carried out without impacting queries or writes. If a change cannot be made without impacting queries or writes, it is rejected on deployment (and will require a [validation](/en/reference/applications/validation-overrides) override to be allowed). +- The operations supported by Vespa are those that can be scaled to hundreds of nodes, billions of documents and hundreds of thousands of queries per second. If you can run it on a single machine, you can scale it. +- The hardware resources available in a cluster can be changed both up and down. Redistribution will happen automatically in the background, without limiting resource usage to avoid impacting queries and writes. +- When possible (on Vespa Cloud), new revisions of applications are deployed in test zones where they can be verified by application-supplied functional tests before being allowed to progress to production. + +## Performance and scaling + +Content clusters in Vespa can be scaled to any amount of content by adding more nodes (horizontal scaling). Data will redistribute automatically, and there's no need for manual tuning of the process. To scale to large amounts of queries, content clusters can also be scaled by adding multiple *groups* of nodes (vertical scaling). Each group contains a single copy of the corpus and container clusters will automatically load balance over groups. + +A Vespa application can consist of any number of stateless and stateful clusters. On larger applications it can be beneficial to split different functions into separate clusters that can be optimized separately. For example, having one stateless container cluster for feeding and another for querying, or using different content clusters for different data schemas. + +Read more in [elasticity](/en/content/elasticity) and the [performance guide](/en/performance/). diff --git a/en/basics/querying.html b/en/basics/querying.html deleted file mode 100644 index d2cdb978f2..0000000000 --- a/en/basics/querying.html +++ /dev/null @@ -1,118 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Querying" -redirect_from: -- /en/querying.html ---- - -

    An introduction to querying with Vespa.

    - - -

    Queries

    - -

    Queries in Vespa are expressed as a YQL string: A query language identical to SQL for structured data, -with additions for vector and full-text search, for example:

    - -
    -select * from mySchema where myTextField contains 'someWord' and myNumber > 10.0
    -
    - -

    You can also search multiple fields with one query item (like "contains"), by defining -fieldset in the schema.

    - -

    Any nested combination of and/or and so on is supported, see the full syntax in the -query language reference.

    - - -

    Query requests

    - -

    Queries are sent as HTTP requests, to the endpoint of a container cluster having <search> in services.xml. -The YQL query is sent as the yql parameter (HTTP Encoded): - -

    -endpoint-url/search/?yql=select+%2A+from+sources+%2A+where+true
    -
    - -

    The Vespa CLI can do this for you:

    - -
    -vespa query "select * from sources * where true"
    -
    - -

    You can add the -v option to see the HTTP request that this becomes.

    - -

    On Vespa Cloud your application will by default get an mTLS certificate that you use to make requests. -If you want to use an access token, you can add one in the console.

    - - -

    Query request parameters

    - -

    In addition to the YQL parameter, you can send other query request parameters -to supply data such as user/llm query text, vectors, and parameters controlling the query execution. -These are added to HTTP requests in the obvious way, and passed to Vespa CLI by adding multiple arguments: - -

    -vespa query -v "select * from sources * where true" "timeout=100ms"
    -
    - -

    You can also send the query parameters -as a JSON payload instead of as request parameters:

    - -
    -$ curl -H "Content-Type: application/json" \
    ---data '{"yql" : "select * from sources * where true"}' \
    -endpoint-url/search/
    -
    - -

    To see all the parameters accepted, see the query API reference.

    - -

    You may end up wanting to set many query parameters in your queries. Instead of passing them in the request, -you can create a query profile in the application package containing all the parameters and just specify -the profile in the request, see query profiles.

    - - -

    Querying with text

    - -

    You can use the text YQL operator to retrieve or rank using raw text. -This will process the text and (by default) search it with the -WeakAnd text search operator.

    - -

    You can pass the text directly, or refer to a separate request parameter (using @parameter):

    - -
    {% raw %}
    -$ vespa query "select * from sources * where title contains text(@query)" \
    -  "query=Any text, from a human/llm"
    -{% endraw %}
    - -

    You can set options controlling how the text is to be parsed and matched, see -the text() reference documentation. - - -

    Querying with vectors

    - -

    Querying by vectors is done using the nearestNeighbor YQL operator, which takes a document and query vector:

    - -
    {% raw %}
    -$ vespa query 'select * from sources *  where {targetHits: 100}nearestNeighbor(my_vector_field, my_query_vector)' \
    -  ranking=my_rank_profile \
    -  'input.query(my_query_vector)'='[1,2,3]'
    -{% endraw %}
    - -

    Read more in nearest neighbor search.

    - -

    You can combine multiple nearestNeighbor, text and other operators in any way:

    - -
    {% raw %}
    -$ vespa query "select * from sources * where (({targetHits: 300}nearestNeighbor(my_title_embedding, my_query_vector)) \
    -                                              or ({targetHits: 150}nearestNeighbor(my_body_embeddings, my_query_vector)) \
    -                                              or title contains text(@query) or body contains text(@query)) \
    -                                             and range(title, 0.0, 500.0) and category in ('c1', 'c2') \
    -                                             and !(blacklisted=true)" \
    -  "input.query(my_query_vector)=embed(@query)" \
    -  "query=Hello, world! "
    -
    -{% endraw %}
    - - -
    -

    Next: Ranking

    diff --git a/en/basics/querying.mdx b/en/basics/querying.mdx new file mode 100644 index 0000000000..edac9a9a53 --- /dev/null +++ b/en/basics/querying.mdx @@ -0,0 +1,91 @@ +--- +title: Querying +description: "An introduction to querying with Vespa." +--- + +## Queries + +Queries in Vespa are expressed as a YQL string: a query language identical to SQL for structured data, with additions for vector and full-text search, for example: + +```sql +select * from mySchema where myTextField contains 'someWord' and myNumber > 10.0 +``` + +You can also search multiple fields with one query item (like "contains"), by defining [fieldset](/en/reference/schemas/schemas#fieldset) in the schema. + +Any nested combination of and/or and so on is supported; see the full syntax in the [query language reference](/en/reference/querying/yql). + +## Query requests + +Queries are sent as HTTP requests, to the endpoint of a container cluster having `` in `services.xml`. The YQL query is sent as the `yql` parameter (HTTP encoded): + +```text +endpoint-url/search/?yql=select+%2A+from+sources+%2A+where+true +``` + +The Vespa CLI can do this for you: + +```bash +vespa query "select * from sources * where true" +``` + +You can add the `-v` option to see the HTTP request that this becomes. + +On Vespa Cloud your application will by default get an mTLS certificate that you use to make requests. If you want to use an access token, you can [add one in the console](/en/security/guide#configuring-tokens). + +## Query request parameters + +In addition to the YQL parameter, you can send other query request parameters to supply data such as user/llm query text, vectors, and parameters controlling the query execution. These are added to HTTP requests in the obvious way, and passed to Vespa CLI by adding multiple arguments: + +```bash +vespa query -v "select * from sources * where true" "timeout=100ms" +``` + +You can also send the query parameters [as a JSON payload](/en/querying/query-api#http) instead of as request parameters: + +```bash +curl -H "Content-Type: application/json" \ + --data '{"yql" : "select * from sources * where true"}' \ + endpoint-url/search/ +``` + +To see all the parameters accepted, see the [query API reference](/en/reference/api/query). + +You may end up wanting to set many query parameters in your queries. Instead of passing them in the request, you can create a query profile in the application package containing all the parameters and just specify the profile in the request — see [query profiles](/en/querying/query-profiles). + +## Querying with text + +You can use the `text` YQL operator to retrieve or rank using raw text. This will process the text and (by default) search it with the [WeakAnd](/en/ranking/wand#weakand) text search operator. + +You can pass the text directly, or refer to a separate request parameter (using `@parameter`): + +```bash +vespa query "select * from sources * where title contains text(@query)" \ + "query=Any text, from a human/llm" +``` + +You can set options controlling how the text is to be parsed and matched; see the [text() reference documentation](/en/reference/querying/yql#text). + +## Querying with vectors + +Querying by vectors is done using the `nearestNeighbor` YQL operator, which takes a document and query vector: + +```bash +vespa query 'select * from sources * where {targetHits: 100}nearestNeighbor(my_vector_field, my_query_vector)' \ + ranking=my_rank_profile \ + 'input.query(my_query_vector)'='[1,2,3]' +``` + +Read more in [nearest neighbor search](/en/querying/nearest-neighbor-search). + +You can combine multiple `nearestNeighbor`, `text` and other operators in any way: + +```bash +vespa query "select * from sources * where (({targetHits: 300}nearestNeighbor(my_title_embedding, my_query_vector)) \ + or ({targetHits: 150}nearestNeighbor(my_body_embeddings, my_query_vector)) \ + or title contains text(@query) or body contains text(@query)) \ + and range(title, 0.0, 500.0) and category in ('c1', 'c2') \ + and !(blacklisted=true)" \ + "input.query(my_query_vector)=embed(@query)" \ + "query=Hello, world! " +``` diff --git a/en/basics/ranking.html b/en/basics/ranking.html deleted file mode 100644 index 41f5a56fb7..0000000000 --- a/en/basics/ranking.html +++ /dev/null @@ -1,180 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Ranking" -redirect_from: -- /en/ranking.html ---- - -

    Ranking in Vespa is the computation that is done on matching documents during query execution. -These are specified as ranking functions in -rank profiles in the schema.

    - -

    The special function named first-phase will determine the initial rank of the matches, -such that the top k can be selected as response to a query:

    - -
    -  rank-profile my-rank-profile {
    -    first-phase {
    -      expression: 0.7 * bm25(text) + 0.3 * attribute(popularity)
    -    }
    -  }
    -
    - - -

    Ranking functions and features

    - -

    The ranking functions can be any mathematical function combining rank features, -including tensor math and -machine-learned models.

    - -

    The rank features these functions can use are of three categories:

    - -
      -
    • Document features, using attribute(fieldName): Any document field which has attribute in the indexing statement.
    • -
    • Query features, aka inputs, using query(name): Any value sent with the query as an input. When these are tensors - (not scalars) they must be declared as an input in the rank profile. -
    • Match features: A built-in feature which says something about how well a query and document matches, e.g. bm25 or closeness.
    • -
    - -

    Refer to the full list of rank features.

    - -

    Query features (inputs) that are tensors must be declared in the rank profile:

    - -
    -  rank-profile my-rank-profile {
    -    inputs {
    -      query(user_context) tensor<float>(x[3])
    -    }
    -    first-phase {
    -      expression: bm25(text) + sum(query(user_context) * attribute(document_context))
    -    }
    -  }
    -
    - -

    This is also how the type of query vectors in vector search are declared.

    - - -

    Rank profiles

    - -

    A schema can have any number of rank profiles specifying computations and ranking -for different use cases, experiments, and so on. Queries select one using the -ranking.profile parameter -in requests or a query profile. -If no profile is specified in the request, the one called default is used, and -if that isn't specified in the schema, a default one ranking by the nativeRank -feature is used. Another built-in rank profile unranked is also always available. -Specifying this boosts serving performance in queries which do not need ranking because ordering is not important or -explicit field sorting is used.

    - -

    To avoid very long schema files, rank profiles can also be specified in their own files in the -application package, named -schemas/[schema-name]/[profile-name].profile. -See the schema reference for documentation -of all the content of rank profiles. - -

    Rank profiles can inherit other profiles to avoid duplication, as in -rank-profile myProfile inherits default, another.

    - - -

    Phased ranking

    - -

    In addition to first-phase which specify the initial ranking that will be applied -on all matching documents during matching, rank profiles can also specify functions -that will be applied to rerank the top k documents before returning the final result. -This is useful to direct more computation towards the most promising candidate documents: - -

    -schema myapp {
    -
    -    rank-profile my-rank-profile {
    -
    -        first-phase {
    -            expression {
    -                attribute(quality) * freshness(timestamp) + bm25(title)
    -            }
    -        }
    -
    -        second-phase {
    -            expression: xgboost(my_xgboost_reranker)
    -            total-rerank-count: 1000 # Over all nodes
    -        }
    -
    -        global-phase {
    -          expression: sum(onnx(my_large_onnx_model))
    -          rerank-count: 20
    -        }
    -
    -    }
    -
    -}
    -
    - -

    The second-phase expression is executed locally on the content node, using local data. -This is efficient on thousands of candidates. The global-phase expression -is executed on the global result set after merging, in the container node and is best used for -any very expensive and high quality final reranking. -See phased ranking for details.

    - - -

    Ranking functions

    - -

    A rank profile can define any number of functions which can be used in other ranking -expressions or (when taking no arguments) be returned with results.

    - -
    -schema myapp {
    -
    -    rank-profile my-rank-profile {
    -
    -        function clickProbability() {
    -            expression: xgboost('myClickModel')
    -        }
    -
    -        function textRanking(field) {
    -            expression: 0.7 * bm25(field) + 0.3 * nativeProximity(field)
    -        }
    -
    -        first-phase {
    -            expression {
    -                0.1 * clickProbability()
    -                0.2 * closeness(embeddingsField) +
    -                0.3 * textRanking(titleField) +
    -                0.4 * textRanking(bodyField)
    -            }
    -        }
    -
    -        summary-features {
    -            clickProbability() # Returned with every mathed document
    -        }
    -
    -    }
    -
    -}
    -
    - -

    Read more in ranking expressions and functions.

    - - -

    Layered ranking

    - -

    In addition to ranking documents, a rank profile can also rank and select array elements within documents. -This is most commonly used to select individual chunks within documents in RAG applications, see -working with chunks.

    - - - -

    Machine-Learned model inference

    - -

    The best quality is achieved by learning relevance functions using machine learning from a training set. -Vespa lets you use machine-learned models in these formats in distributed ranking (first-and second phase):

    - -
      -
    • ONNX, allowing importing models from ML frameworks like Tensorflow, PyTorch and scikit-learn.
    • -
    • XGBoost
    • -
    • LightGBM
    • -
    - -

    As these are exposed as rank features, they can be used in ranking expressions exactly like any other rank feature.

    - -
    -

    Next: Operations

    diff --git a/en/basics/ranking.mdx b/en/basics/ranking.mdx new file mode 100644 index 0000000000..eb73078ab5 --- /dev/null +++ b/en/basics/ranking.mdx @@ -0,0 +1,134 @@ +--- +title: Ranking +--- + +*Ranking* in Vespa is the computation that is done on matching documents during query execution. These are specified as [ranking functions](/en/ranking/ranking-expressions-features) in *rank profiles* in the schema. + +The special function named `first-phase` will determine the initial *rank* of the matches, such that the top k can be selected as response to a query: + +```js +rank-profile my-rank-profile { + first-phase { + expression: 0.7 * bm25(text) + 0.3 * attribute(popularity) + } +} +``` + +## Ranking functions and features + +The ranking functions can be any mathematical function combining rank features, including [tensor math](/en/ranking/tensor-user-guide#ranking-with-tensors) and [machine-learned models](#machine-learned-model-inference). + +The rank features these functions can use are of three categories: + +- **Document features**, using `attribute(fieldName)`: any document field which has `attribute` in the indexing statement. +- **Query features**, aka inputs, using `query(name)`: any value sent with the query as an input. When these are tensors (not scalars) they must be declared as an input in the rank profile. +- **Match features**: a built-in feature which says something about how well a query and document matches, e.g. bm25 or closeness. + +Refer to the [full list of rank features](/en/reference/ranking/rank-features). + +Query features (inputs) that are tensors must be declared in the rank profile: + +```js +rank-profile my-rank-profile { + inputs { + query(user_context) tensor(x[3]) + } + first-phase { + expression: bm25(text) + sum(query(user_context) * attribute(document_context)) + } +} +``` + +This is also how the type of query vectors in vector search are declared. + +## Rank profiles + +A schema can have any number of rank profiles specifying computations and ranking for different use cases, experiments, and so on. Queries select one using the [ranking.profile](/en/reference/api/query#ranking.profile) parameter in requests or a [query profile](/en/querying/query-profiles). If no profile is specified in the request, the one called `default` is used, and if that isn't specified in the schema, a default one ranking by the [nativeRank](/en/ranking/nativerank) feature is used. Another built-in rank profile `unranked` is also always available. Specifying this boosts serving performance in queries which do not need ranking because ordering is not important or [explicit field sorting](/en/reference/querying/sorting-language) is used. + +To avoid very long schema files, rank profiles can also be specified in their own files in the application package, named `schemas/[schema-name]/[profile-name].profile`. See the [schema reference](/en/reference/schemas/schemas#rank-profile) for documentation of all the content of rank profiles. + +Rank profiles can inherit other profiles to avoid duplication, as in `rank-profile myProfile inherits default, another`. + +## Phased ranking + +In addition to first-phase which specifies the initial ranking that will be applied on all matching documents during matching, rank profiles can also specify functions that will be applied to *rerank* the top k documents before returning the final result. This is useful to direct more computation towards the most promising candidate documents: + +```js +schema myapp { + + rank-profile my-rank-profile { + + first-phase { + expression { + attribute(quality) * freshness(timestamp) + bm25(title) + } + } + + second-phase { + expression: xgboost(my_xgboost_reranker) + total-rerank-count: 1000 # Over all nodes + } + + global-phase { + expression: sum(onnx(my_large_onnx_model)) + rerank-count: 20 + } + + } + +} +``` + +The `second-phase` expression is executed locally on the content node, using local data. This is efficient on thousands of candidates. The `global-phase` expression is executed on the global result set after merging, in the container node and is best used for any very expensive and high quality final reranking. See [phased ranking](/en/ranking/phased-ranking) for details. + +## Ranking functions + +A rank profile can define any number of functions which can be used in other ranking expressions or (when taking no arguments) be returned with results. + +```js expandable +schema myapp { + + rank-profile my-rank-profile { + + function clickProbability() { + expression: xgboost('myClickModel') + } + + function textRanking(field) { + expression: 0.7 * bm25(field) + 0.3 * nativeProximity(field) + } + + first-phase { + expression { + 0.1 * clickProbability() + 0.2 * closeness(embeddingsField) + + 0.3 * textRanking(titleField) + + 0.4 * textRanking(bodyField) + } + } + + summary-features { + clickProbability() # Returned with every matched document + } + + } + +} +``` + +Read more in [ranking expressions and functions](/en/ranking/ranking-expressions-features). + +## Layered ranking + +In addition to ranking *documents*, a rank profile can also rank and select array elements within documents. This is most commonly used to select individual chunks within documents in RAG applications — see [working with chunks](/en/rag/working-with-chunks#layered-ranking-selecting-chunks-to-return). + +## Machine-Learned model inference + +The best quality is achieved by learning relevance functions using machine learning from a training set. Vespa lets you use machine-learned models in these formats in distributed ranking (first- and second phase): + +- [ONNX](/en/ranking/onnx), allowing importing models from ML frameworks like Tensorflow, PyTorch and scikit-learn. +- [XGBoost](/en/ranking/xgboost) +- [LightGBM](/en/ranking/lightgbm) + +As these are exposed as rank features, they can be used in ranking expressions exactly like any other rank feature. + diff --git a/en/basics/schemas.html b/en/basics/schemas.html deleted file mode 100644 index d44ceeb8da..0000000000 --- a/en/basics/schemas.html +++ /dev/null @@ -1,147 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Schemas" -redirect_from: -- /en/schema-inheritance.html -- /en/schemas.html ---- - -

    This is an introduction to schemas in Vespa. You can find all the details in the -schema reference.

    - -

    A schema defines a type of data and what we want to compute over it. -An application package can contain multiple schemas for different kinds of data. -Each content cluster specified in services.xml refers to the schemas that should be stored an indexed -in that cluster. Schemas can inherit other schemas to avoid repeating common content.

    - -

    Schemas are placed in files named the same as the schema, with the ending ".sd" (for schema definition), -in the schemas/ directory of the application package.

    - - -

    Document fields

    - -

    A schema contains a document type, which is a named collection of fields:

    - -
    -schema mySchema {
    -
    -    document mySchema {
    -
    -        field myField type string {
    -            indexing: summary | index
    -        }
    -
    -        ... more fields
    -
    -    }
    -
    -}
    -
    - -

    Each field has a type, a way it should be processed and indexed, and optionally other settings. -The main decision you make is how the field should be used in queries, determined by the indexing -statement:

    - -
      -
    • indexing: summary: The field should be available in query responses - (document summaries). -
    • indexing: index: If a string: Create a full-text on-disk index. - If a tensor: Create an HNSW vector index (requires attribute in addition). -
    • indexing: attribute: For any field type: Make the field value available for structured search - (exact, range, regexp etc.), ranking, sorting, grouping, and aggregation in the - in-memory column store. - Suitable for structured data. -
    • indexing: attribute and attribute: fast-search: As above, but in addition, create an index - over this data to make it an efficient filter. Suitable for structured fields that are used as strong filters in queries. -
    - -

    The indexing statement can contain multiple expressions separated by a pipe character, and these can also preprocess the -value, so the pipe should be read as passing to the next expression, as on Unix. See the -reference for all the types and content of fields.

    - -

    When a schema is defined and added to a content cluster, you can write data -according to it, and query using the attributes and indexed fields in it. -Indexing always happens automatically in real time.

    - - -

    Synthetic fields

    - -

    The document type in the schema defines the fields that you can put and get (read and write) for that document type. -However, sometimes you want to take an input field and process it in some way before it is stored/indexed. To do that, -you can create additional synthetic fields outside the document in the schema:

    - -
    -schema mySchema {
    -
    -    document mySchema {
    -
    -        field myField type string {
    -            indexing: summary | index
    -        }
    -
    -        ...
    -
    -    }
    -
    -    field mySyntheticField type tensor(x[386]) {
    -        indexing: input myField | embed | attribute | index
    -    }
    -
    -}
    -
    - - -

    Rank profiles

    - -

    A rank profile specifies what should be computed over the data described by the schema, -and how the documents of it should be ranked to select the ones to return in a query response:

    - -
    -schema mySchema {
    -
    -    ...
    -
    -    rank-profile hybrid {
    -
    -        first-phase {
    -            expression {
    -                0.3 * bm25(myText) +
    -                0.5 * closeness(myEmbedding) +
    -                0.2 * attribute(popularity)
    -            }
    -        }
    -
    -    }
    -
    -}
    -
    - -

    A schema can have any number of rank profiles for different use cases, experiments and so on, -and each can have multiple functions that compute some value to be returned or used in ranking. -In addition to simple math functions like the above these can also be machine-learned models. -See ranking for more.

    - - -

    Working with schemas

    - -

    Schemas may become thousands of lines, with inheritance, multiple rank functions calling each other and so on. -The most efficient way of working with them is to use an IDE and install the Vespa plugin to get syntax highlighting, -completions and navigation - see IDE support. - -

    What happens if you change the schema of a running application? - - -

      -
    • Adding new fields: No problem, the new field will be added and have no value until data is written to it. -
    • Changing how a field is indexed: This will automatically cause a background reindexing on Vespa Cloud, - but in the meantime there may be inconsistency in how the field is used in queries and writes, so in production it - is sometimes preferable to create a new field instead. -
    • Removing a field: Data and indexes are removed for the field. -
    • Changing the type of a field: Existing data and indexes are removed for the field. For this reason, it is - often preferable to add a new field instead, populate it, switch usages to the new field, then remove the old. -
    - -

    You can find the details in modifying schemas.

    - -
    -

    Next: Writing

    diff --git a/en/basics/schemas.mdx b/en/basics/schemas.mdx new file mode 100644 index 0000000000..1577338dff --- /dev/null +++ b/en/basics/schemas.mdx @@ -0,0 +1,103 @@ +--- +title: Schemas +description: "This is an introduction to schemas in Vespa. You can find all the details in the [schema reference](/en/reference/schemas/schemas)." +--- + +A schema defines a type of data and what we want to compute over it. An application package can contain multiple schemas for different kinds of data. Each content cluster specified in `services.xml` refers to the schemas that should be stored and indexed in that cluster. Schemas can inherit other schemas to avoid repeating common content. + +Schemas are placed in files named the same as the schema, with the ending `.sd` (for schema definition), in the `schemas/` directory of the application package. + +## Document fields + +A schema contains a document type, which is a named collection of fields: + +```js +schema mySchema { + + document mySchema { + + field myField type string { + indexing: summary | index + } + + ... more fields + + } + +} +``` + +Each field has a type, a way it should be processed and indexed, and optionally other settings. The main decision you make is how the field should be used in queries, determined by the `indexing` statement: + +- `indexing: summary`: The field should be available in query responses ([document summaries](/en/querying/document-summaries)). +- `indexing: index`: If a string: create a full-text on-disk index. If a tensor: create an HNSW vector index (requires `attribute` in addition). +- `indexing: attribute`: For any field type: make the field value available for structured search (exact, range, regexp etc.), ranking, sorting, grouping, and aggregation in the [in-memory column store](/en/content/attributes). Suitable for structured data. +- `indexing: attribute` and `attribute: fast-search`: As above, but in addition, create an index over this data to make it an efficient filter. Suitable for structured fields that are used as strong filters in queries. + +The indexing statement can contain multiple expressions separated by a pipe character, and these can also preprocess the value, so the pipe should be read as passing to the next expression, as on Unix. See the [reference](/en/reference/schemas/schemas#field) for all the types and content of fields. + +When a schema is defined and added to a content cluster, you can [write data](/en/basics/writing) according to it, and [query](/en/basics/querying) using the attributes and indexed fields in it. Indexing always happens automatically in real time. + +## Synthetic fields + +The document type in the schema defines the fields that you can put and get (read and write) for that document type. However, sometimes you want to take an input field and process it in some way before it is stored/indexed. To do that, you can create additional synthetic fields outside the document in the schema (for example using the [embed](/en/rag/embedding) function): + +```js +schema mySchema { + + document mySchema { + + field myField type string { + indexing: summary | index + } + + ... + + } + + field mySyntheticField type tensor(x[386]) { + indexing: input myField | embed | attribute | index + } + +} +``` + +## Rank profiles + +A *rank profile* specifies what should be computed over the data described by the schema, and how the documents of it should be ranked to select the ones to return in a query response: + +```js +schema mySchema { + + ... + + rank-profile hybrid { + + first-phase { + expression { + 0.3 * bm25(myText) + + 0.5 * closeness(myEmbedding) + + 0.2 * attribute(popularity) + } + } + + } + +} +``` + +A schema can have any number of rank profiles for different use cases, experiments and so on, and each can have multiple functions that compute some value to be returned or used in ranking. In addition to simple math functions like the above these can also be machine-learned models. See [ranking](/en/basics/ranking) for more. + +## Working with schemas + +Schemas may become thousands of lines, with inheritance, multiple rank functions calling each other and so on. The most efficient way of working with them is to use an IDE and install the Vespa plugin to get syntax highlighting, completions and navigation — see [IDE support](/en/applications/ide-support). + +What happens if you change the schema of a running application? + +- **Adding new fields**: No problem; the new field will be added and have no value until data is written to it. +- **Changing how a field is indexed**: This will automatically cause a background reindexing on Vespa Cloud, but in the meantime there may be inconsistency in how the field is used in queries and writes, so in production it is sometimes preferable to create a new field instead. +- **Removing a field**: Data and indexes are removed for the field. +- **Changing the type of a field**: Existing data and indexes are removed for the field. For this reason, it is often preferable to add a new field instead, populate it, switch usages to the new field, then remove the old. + +You can find the details in [modifying schemas](/en/reference/schemas/schemas#modifying-schemas). + diff --git a/en/basics/whats-more.html b/en/basics/whats-more.html deleted file mode 100644 index 5e3446a468..0000000000 --- a/en/basics/whats-more.html +++ /dev/null @@ -1,48 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "What's more" -redirect_from: -- /en/whats-more.html ---- - - - -

    The Vespa basics articles introduces the central concepts in Vespa, but can't cover everything -needed to build complete applications. Some additional important features are:

    - -
      -
    • Grouping and aggregation (faceting): Grouping in the query language lets you - specify hierarchical - groupings and aggregations that will be performed over all the matches to a query distributed over all - participating nodes. -
    • Streaming search: In applications where queries search fixed small subsets of all data - (such as a user or tenant) it is not cost-effective to build indexes. For these use case Vespa - supports a streaming mode which delivers low-latency search - without the cost of maintaining indexes or even keeping data in memory. -
    • Application components: Applications can include Java components that implement application logic, - such as intercepting queries and results and implement custom workflows - (Searchers), - modify write operations (document processors), and - implementing custom API's (handlers). -
    • Parent-child relations: Joins are not supported in Vespa because they wouldn't scale, but - the special case where one side of the join is much smaller than the other is supported. This is called - a parent-child relation. -
    • Federation: Most applications federate over multiple types of content. - Vespa will federate over all schemas and clusters by default, and includes - a federation framework which lets you define application specific - schemes to formulate queries to - each content type, include content from other services, combine content in application-specific ways - and so on. -
    • Predicate fields: Sometimes it is useful to allow documents to specify when they should be matched, - as conditions on properties sent with the query, for example to let content target specific kinds of users. - This can be done using predicate fields. -
    • Geo search: By using geo fields, you can find documents within - a given area, use distance to the query in ranking, or even retrieve by distance to a path for route planning. -
    • Mutable attributes: By defining mutable attributes - on the documents, applications can collect statistics in real time on each document to track how - often they are matched, ranked, and returned in results. -
    - -

    Read more in the full features list.

    diff --git a/en/basics/whats-more.mdx b/en/basics/whats-more.mdx new file mode 100644 index 0000000000..089a4ce5ca --- /dev/null +++ b/en/basics/whats-more.mdx @@ -0,0 +1,17 @@ +--- +title: What's more +description: "The Vespa basics articles introduce the central concepts in Vespa, but can't cover everything needed to build complete applications." +--- + +Some additional important features are: + +- **[Grouping and aggregation](/en/querying/grouping)** (faceting): Grouping in the query language lets you specify hierarchical groupings and aggregations that will be performed over all the matches to a query distributed over all participating nodes. +- **Streaming search**: In applications where queries search fixed small subsets of all data (such as a user or tenant) it is not cost-effective to build indexes. For these use cases Vespa supports a [streaming mode](/en/performance/streaming-search) which delivers low-latency search without the cost of maintaining indexes or even keeping data in memory. +- **Application components**: Applications can include Java components that implement application logic, such as intercepting queries and results and implement custom workflows ([Searchers](/en/applications/searchers)), modify write operations ([document processors](/en/applications/document-processors)), and implementing custom APIs ([handlers](/en/applications/request-handlers)). +- **Parent-child relations**: Joins are not supported in Vespa because they wouldn't scale, but the special case where one side of the join is much smaller than the other is supported. This is called a [parent-child relation](/en/schemas/parent-child). +- **Federation**: Most applications federate over multiple types of content. Vespa will federate over all schemas and clusters by default, and includes a [federation framework](/en/querying/federation) which lets you define application-specific schemes to formulate queries to each content type, include content from other services, combine content in application-specific ways and so on. +- **Predicate fields**: Sometimes it is useful to allow documents to specify when they should be matched, as conditions on properties sent with the query, for example to let content target specific kinds of users. This can be done using [predicate fields](/en/schemas/predicate-fields). +- **Geo search**: By using [geo fields](/en/querying/geo-search), you can find documents within a given area, use distance to the query in ranking, or even retrieve by distance to a path for route planning. +- **Mutable attributes**: By defining [mutable attributes](/en/reference/schemas/schemas#mutate) on the documents, applications can collect statistics in real time on each document to track how often they are matched, ranked, and returned in results. + +Read more in the full [features](/en/learn/features) list. diff --git a/en/basics/writing.html b/en/basics/writing.html deleted file mode 100644 index c1305d3b95..0000000000 --- a/en/basics/writing.html +++ /dev/null @@ -1,110 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Writing" -redirect_from: -- /en/writing.html ---- - -

    This is an introduction to writing data into Vespa.

    - - -

    Documents

    - -

    Once you have added one or more schemas to an application, and have added <document-api> -in services.xml to the container cluster you want to handle writes, you can send writes following those schemas. -A document is written as a JSON map containing a value for each field:

    - -
    {% highlight json %}
    -{
    -    "put": "id:my-namespace:my-documenttype::my-id-string",
    -    "fields": {
    -        "myTextField": "Hello world!",
    -        "myNumericAttribute": 13.8,
    -        "myEmbedding": [0.3, 1.45, 1.03]
    -    }
    -}
    -{% endhighlight %}
    - -

    Each document has an id, which has two parts which can be decided freely: - -

      -
    • The namespace, which is just a string used to avoid name collisions if you have - multiple kinds of clients deciding ids and not used for any other purpose -
    • The id string, which can be any string you want, for example a product id or a url -
    - -

    Fields can remain empty; you do not need to set a value for every field defined in the document type.

    - -

    You can find complete information on the document format in the -document JSON format reference.

    - - -

    Writing documents

    - -

    Documents are written to your application instances write endpoint, -using the document/v1 HTTP API. -You can use the API directly, or use one of the clients provided by Vespa:

    - - - -

    Documents can also be removed, retrieved, and updated using the same API and clients.

    - - -

    Updating documents

    - -

    Documents can be fully replaced by a new version by writing them again, -but you can also update any individual fields of existing documents. This is especially useful -for updating attribute fields such as e.g. behavior signals or prices at high throughput, -without impacting other fields and indexes.

    - -

    Updates are sent in the same ways as document puts, it's just the format that's different:

    - -
    {% highlight json %}
    -{
    -    "update": "id:my-namespace:my-documenttype::my-id-string",
    -    "fields": {
    -        "myTextField": {
    -            "assign": "Some new value"
    -        }
    -    }
    -}
    -{% endhighlight %}
    - -

    Updates can also increment numerical values, add to arrays and tensor etc., read more in the -partial update guide.

    - - -

    Writes are streamed and realtime

    - -

    Write operations to Vespa are streamed (using HTTP/2), and processed asynchronously. -There is no need for a separate batch API to feed with the maximal throughput a system can handle, -servers will push back by responding more slowly when they are close to saturation, -and clients use this signal to back off, allowing them to dynamically converge at the maximal throughput -a system can handle.

    - -

    The write operations to Vespa are always applied in real time: When a write operation is -asynchronously acknowledged, the write operation is persisted, fully processed and the result is visible -in all subsequent queries. Vespa achieves this by a unique index design, combining in-memory -mutable structures with and (for full-text) disk-backed posting lists.

    - -

    Read more in the feed sizing doc.

    - - -

    The document API can also return documents

    - -

    In addition to supporting writes, the document/v1 HTTP API can also return single documents by id -(get), and stream any selection of a document corpus (visit). Visiting is used for background and one-time -jobs such as backup and scraping content for offline machine learning. It is designed to have minimal -impact on the running system rather than returning with low latency. Read more in -the document/v1 guide.

    - -
    -

    Next: Querying

    diff --git a/en/basics/writing.mdx b/en/basics/writing.mdx new file mode 100644 index 0000000000..45cce4a74c --- /dev/null +++ b/en/basics/writing.mdx @@ -0,0 +1,70 @@ +--- +title: Writing +description: "This is an introduction to writing data into Vespa." +--- + +## Documents + +Once you have added one or more schemas to an application, and have added `` in `services.xml` to the container cluster you want to handle writes, you can send writes following those schemas. A document is written as a JSON map containing a value for each field: + +```json +{ + "put": "id:my-namespace:my-documenttype::my-id-string", + "fields": { + "myTextField": "Hello world!", + "myNumericAttribute": 13.8, + "myEmbedding": [0.3, 1.45, 1.03] + } +} +``` + +Each document has an id, which has two parts which can be decided freely: + + - The **namespace**, which is just a string used to avoid name collisions if you have multiple kinds of clients deciding ids and not used for any other purpose + - The **id string**, which can be any string you want, for example a product id or a url + +Fields can remain empty; you do not need to set a value for every field defined in the document type. + +You can find complete information on the document format in the [document JSON format reference](/en/reference/schemas/document-json-format). + +## Writing documents + +Documents are written to your application instance's *write endpoint*, using the [document/v1](/en/writing/document-v1-api-guide) HTTP API. You can use the API directly, or use one of the clients provided by Vespa: + +- **Command line, with [Vespa CLI](/en/clients/vespa-cli)**: [`vespa feed`](/en/clients/vespa-cli#documents) to feed one or many documents to Vespa. +- **Python, with [PyVespa](https://vespa-engine.github.io/pyvespa/)**: [`application.feed_iterable(...)`](https://vespa-engine.github.io/pyvespa/reads-writes.html#feeding-operations-from-a-file) +- **Java, with the [Java Feed Client](/en/clients/vespa-feed-client)**: [`myFeedClient.put(id, json, params)`](/en/clients/vespa-feed-client#example-java) + +Documents can also be removed, retrieved, and updated using the same API and clients. + +## Updating documents + +Documents can be fully replaced by a new version by writing them again, but you can also update any individual fields of existing documents. This is especially useful for updating attribute fields such as e.g. behavior signals or prices at high throughput, without impacting other fields and indexes. + +Updates are sent in the same ways as document puts; it's just the format that's different: + +```json +{ + "update": "id:my-namespace:my-documenttype::my-id-string", + "fields": { + "myTextField": { + "assign": "Some new value" + } + } +} +``` + +Updates can also increment numerical values, add to arrays and tensor etc. Read more in the [partial update guide](/en/writing/partial-updates). + +## Writes are streamed and realtime + +Write operations to Vespa are streamed (using HTTP/2), and processed asynchronously. There is no need for a separate batch API to feed with the maximal throughput a system can handle; servers will push back by responding more slowly when they are close to saturation, and clients use this signal to back off, allowing them to dynamically converge at the maximal throughput a system can handle. + +The write operations to Vespa are always applied in real time: when a write operation is asynchronously acknowledged, the write operation is persisted, fully processed and the result is visible in all subsequent queries. Vespa achieves this by a unique index design, combining in-memory mutable structures with (for full-text) disk-backed posting lists. + +Read more in the [feed sizing doc](/en/performance/sizing-feeding). + +## The document API can also return documents + +In addition to supporting writes, the document/v1 HTTP API can also return single documents by id (get), and stream any selection of a document corpus (visit). Visiting is used for background and one-time jobs such as backup and scraping content for offline machine learning. It is designed to have minimal impact on the running system rather than returning with low latency. Read more in [the document/v1 guide](/en/writing/document-v1-api-guide#data-dump). + diff --git a/en/clients/http-best-practices.md b/en/clients/http-best-practices.md deleted file mode 100644 index 624302edd3..0000000000 --- a/en/clients/http-best-practices.md +++ /dev/null @@ -1,69 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: HTTP Best Practices -applies_to: cloud -redirect_from: - - /en/cloud/http-best-practices ---- - -## Always re-use connections -As connections to a JDisc container cluster are terminated at the individual container nodes, -the cost of connection overhead will impact their serving capability. -This is especially important for HTTPS/TLS as full TLS handshakes are expensive in terms of CPU cycles. -A handshake also entails multiple network round-trips that certainly degrades request latency for new connections. -A client instance should therefore re-use HTTPS connections if possible for subsequent requests. - -Note that some client implementation may not re-use connections by default. -For instance *Apache HttpClient (Java)* -[will by default not re-use connections when configured with a client X.509 certificate](https://stackoverflow.com/a/13049131/1615280). -Most programmatic clients require the response content to be fully consumed/read for a connection to be reused. - -## Use multiple connections -Clients performing feed/query must use sufficient number of connections to spread the load evenly among all containers in a cluster. -This is due to container clusters being served through a layer 4 load balancer (*Network Load Balancer*). -Too few connections overall may result in an unbalanced workload, and some containers may not receive any traffic at all. -This aspect is particular relevant for applications with large container clusters and/or few client instances. - -## Be aware of server-initiated connection termination -Vespa Cloud will terminate idle connections after a timeout and active connections after a max age threshold is exceeded. -The latter is performed gracefully through mechanisms in the HTTP protocol. -* *HTTP/1.1*: A `Connection: close` header is added to the response for the subsequent request received after timeout. -* *HTTP/2*: A `GOAWAY` frame with error code `NO_ERROR (0x0)` is returned for the subsequent request received after timeout. - Be aware that some client implementation may not handle this scenario gracefully. - -Both the idle timeout and max age threshold are aggressive to regularly rebalanced traffic. -This ensures that new container nodes quickly receives traffic from existing client instances, -for example when new resources are introduced by the [autoscaler](../operations/autoscaling.html). - -To avoid connection termination issues, clients should configure client-side idle timeouts to **less than 30 seconds** and connection TTL (max age) to **less than 45 seconds**. -Proactively closing connections before the server does helps prevent errors caused by server-initiated terminations. -Connections should still be reused for subsequent requests — these timeouts control when idle or long-lived connections are recycled, -not disabled. Disabling connection reuse entirely would incur the cost of a new TCP connection with TLS handshake for every request. - -## Prefer HTTP/2 -We recommend *HTTP/2* over *HTTP/1.1*. *HTTP/2* multiplexes multiple concurrent requests over a single connection, -and its binary protocol is more compact and efficient. -See Vespa's documentation on [HTTP/2](../performance/http2.html) for more details. - -## Be deliberate with timeouts and retries -Make sure to configure your clients with sensible timeouts and retry policies. -Too low timeouts combined with aggressive retries may cause havoc on your Vespa application if latency increases due to overload. - -Handle *transient failures* and *partial failures* through a retry strategy with backoff, for instance *capped exponential backoff* with a random *jitter*. -Consider implementing a [*circuit-breaker*](https://martinfowler.com/bliki/CircuitBreaker.html) for failures persisting over a longer time-span. - -Only retry requests on *server errors* - not on *client errors*. -A client should typically not retry requests after receiving a `400 Bad Request` response, -or retry a TLS connection after handshake fails with client's X.509 certificate being expired. - -Be careful when handling 5xx responses, especially `503 Service Unavailable` and `504 Gateway Timeout`. -These responses typically indicate an overloaded system, and blindly retrying without backoff will only worsen the situation. -Clients should reduce overall throughput when receiving such responses. - -The same principle applies to `429 Too Many Requests` responses from the [Document v1 API](../writing/document-v1-api-guide.html), -which indicates that the client is exceeding the system's feed capacity. Clients should implement strategies -such as reducing the request rate by a specific percentage, introducing exponential backoff, or pausing requests -for a short duration before retrying. These adjustments help prevent further overload and allow the system to recover. - -For more general advise on retries and timeouts see *Amazon Builder's Library*'s -[excellent article](https://aws.amazon.com/builders-library/timeouts-retries-and-backoff-with-jitter/) on the subject. diff --git a/en/clients/http-best-practices.mdx b/en/clients/http-best-practices.mdx new file mode 100644 index 0000000000..c0d7d7c236 --- /dev/null +++ b/en/clients/http-best-practices.mdx @@ -0,0 +1,43 @@ +--- +title: HTTP Best Practices +sidebarTitle: "HTTP best practices" +--- + +## Always re-use connections +As connections to a JDisc container cluster are terminated at the individual container nodes, the cost of connection overhead will impact their serving capability. This is especially important for HTTPS/TLS as full TLS handshakes are expensive in terms of CPU cycles. A handshake also entails multiple network round-trips that certainly degrades request latency for new connections. A client instance should therefore re-use HTTPS connections if possible for subsequent requests. + +Note that some client implementation may not re-use connections by default. For instance *Apache HttpClient (Java)* [will by default not re-use connections when configured with a client X.509 certificate](https://stackoverflow.com/a/13049131/1615280). Most programmatic clients require the response content to be fully consumed/read for a connection to be reused. + +## Use multiple connections +Clients performing feed/query must use sufficient number of connections to spread the load evenly among all containers in a cluster. This is due to container clusters being served through a layer 4 load balancer (*Network Load Balancer*). +Too few connections overall may result in an unbalanced workload, and some containers may not receive any traffic at all. This aspect is particular relevant for applications with large container clusters and/or few client instances. + +## Be aware of server-initiated connection termination +Vespa Cloud will terminate idle connections after a timeout and active connections after a max age threshold is exceeded. +The latter is performed gracefully through mechanisms in the HTTP protocol. +* *HTTP/1.1*: A `Connection: close` header is added to the response for the subsequent request received after timeout. +* *HTTP/2*: A `GOAWAY` frame with error code `NO_ERROR (0x0)` is returned for the subsequent request received after timeout. Be aware that some client implementation may not handle this scenario gracefully. + +Both the idle timeout and max age threshold are aggressive to regularly rebalanced traffic. This ensures that new container nodes quickly receives traffic from existing client instances, for example when new resources are introduced by the [autoscaler](/en/operations/autoscaling). + +To avoid connection termination issues, clients should configure client-side idle timeouts to **less than 30 seconds** and connection TTL (max age) to **less than 45 seconds**. Proactively closing connections before the server does helps prevent errors caused by server-initiated terminations. Connections should still be reused for subsequent requests — these timeouts control when idle or long-lived connections are recycled, not disabled. Disabling connection reuse entirely would incur the cost of a new TCP connection with TLS handshake for every request. + +## Prefer HTTP/2 +We recommend *HTTP/2* over *HTTP/1.1*. *HTTP/2* multiplexes multiple concurrent requests over a single connection, +and its binary protocol is more compact and efficient. +See Vespa's documentation on [HTTP/2](/en/performance/http2) for more details. + +## Be deliberate with timeouts and retries +Make sure to configure your clients with sensible timeouts and retry policies. +Too low timeouts combined with aggressive retries may cause havoc on your Vespa application if latency increases due to overload. + +Handle *transient failures* and *partial failures* through a retry strategy with backoff, for instance *capped exponential backoff* with a random *jitter*. +Consider implementing a [*circuit-breaker*](https://martinfowler.com/bliki/CircuitBreaker.html) for failures persisting over a longer time-span. + +Only retry requests on *server errors* - not on *client errors*. A client should typically not retry requests after receiving a `400 Bad Request` response, or retry a TLS connection after handshake fails with client's X.509 certificate being expired. + +Be careful when handling 5xx responses, especially `503 Service Unavailable` and `504 Gateway Timeout`. These responses typically indicate an overloaded system, and blindly retrying without backoff will only worsen the situation. Clients should reduce overall throughput when receiving such responses. + +The same principle applies to `429 Too Many Requests` responses from the [Document v1 API](/en/writing/document-v1-api-guide), which indicates that the client is exceeding the system's feed capacity. Clients should implement strategies such as reducing the request rate by a specific percentage, introducing exponential backoff, or pausing requests for a short duration before retrying. These adjustments help prevent further overload and allow the system to recover. + +For more general advise on retries and timeouts see *Amazon Builder's Library*'s [excellent article](https://aws.amazon.com/builders-library/timeouts-retries-and-backoff-with-jitter/) on the subject. diff --git a/en/clients/python-client.mdx b/en/clients/python-client.mdx new file mode 100644 index 0000000000..66d0f12153 --- /dev/null +++ b/en/clients/python-client.mdx @@ -0,0 +1,4 @@ +--- +title: "Python Client(py Vespa)" +url: "https://vespa-engine.github.io/pyvespa/" +--- \ No newline at end of file diff --git a/en/clients/vespa-cli.html b/en/clients/vespa-cli.mdx similarity index 57% rename from en/clients/vespa-cli.html rename to en/clients/vespa-cli.mdx index 94b1289c69..8848d85a27 100644 --- a/en/clients/vespa-cli.html +++ b/en/clients/vespa-cli.mdx @@ -1,59 +1,44 @@ --- -# Copyright Vespa.ai. All rights reserved. title: "Vespa CLI" -redirect_from: -- /en/vespa-cli-feed.html -- /en/vespa-cli.html --- -

    - Vespa CLI is the command-line client for Vespa. - It is a single binary without any runtime dependencies and is available for Linux, macOS and Windows. - With Vespa CLI you can: -

    -
      -
    • Clone the sample applications repository
    • -
    • Deploy your application to a Vespa installation running locally or remote
    • -
    • Deploy your application on Vespa Cloud -
    • Feed and query documents
    • -
    • Send custom requests with automatic authentication
    • -
    • Automate deployment operations with vespa auth api-key
    • -
    -

    Install Vespa CLI:

    - -

    - To learn the basics on how to use Vespa CLI, see - the quick start guide or the cheat sheet - below. -

    -

    - See the reference documentation - for documentation of individual Vespa CLI commands and their options. This - documentation is also bundled with CLI and accessible through vespa help - <command> or man vespa-<command>. -

    -

    MTLS keypair location:

    -
    +
    +Vespa CLI is the command-line client for Vespa. It is a single binary without any runtime dependencies and is available for Linux, macOS and Windows. With Vespa CLI you can:
    +
    +- Clone the [sample applications](https://github.com/vespa-engine/sample-apps/) repository
    +- Deploy your application to a Vespa installation running locally or remote
    +- Deploy your application on [Vespa Cloud](/)
    +- Feed and [query](/en/querying/query-language#query-examples) documents
    +- Send custom requests with automatic authentication
    +- Automate deployment operations with [vespa auth api-key](/en/reference/clients/vespa-cli/vespa_auth_api-key)
    +
    +Install Vespa CLI:
    +
    +- Homebrew: `brew install vespa-cli`
    +- Mise: `mise use vespa-cli`
    +- [Download from GitHub](https://github.com/vespa-engine/vespa/releases)
    +
    +To learn the basics on how to use Vespa CLI, see the [quick start guide](/en/basics/deploy-an-application-local) or the [cheat sheet below](#cheat-sheet).
    +
    +See the [reference documentation](/en/reference/clients/vespa-cli/vespa) for documentation of individual Vespa CLI commands and their options. This documentation is also bundled with CLI and accessible through `vespa help
    +  ` or `man vespa-`.
    +
    +MTLS keypair location:
    +
    +```bash
     $ ls -l .vespa/mytenant.myapp.default/
     total 16
    --rw-r--r--  1 name  staff  3273 Nov  7 08:02 data-plane-private-key.pem
    --rw-r--r--  1 name  staff  1697 Nov  7 08:02 data-plane-public-cert.pem
    -
    -

    - The .vespa directory should be in the home directory or cwd. - Remember to run vespa config set target cloud for Vespa Cloud endpoints. -

    +-rw-r--r-- 1 name staff 3273 Nov 7 08:02 data-plane-private-key.pem +-rw-r--r-- 1 name staff 1697 Nov 7 08:02 data-plane-public-cert.pem +``` +The `.vespa` directory should be in the home directory or cwd. Remember to run `vespa config set target cloud` for Vespa Cloud endpoints. -

    Cheat sheet

    +## Cheat sheet +### Install, configure and run -

    Install, configure and run

    -
    {% highlight shell %}
    +```bash
     # Install - make sure to upgrade frequently for new features
     $ brew install vespa-cli
     $ brew upgrade vespa-cli
    @@ -66,11 +51,11 @@ 

    Install, configure and run

    # Get help $ vespa document put --help -{% endhighlight %}
    +``` +### Login and init -

    Login and init

    -
    {% highlight shell %}
    +```bash
     # Use endpoints on localhost
     $ vespa config set target local
     
    @@ -85,11 +70,11 @@ 

    Login and init

    # Configure application instance, override global configuration (write to local .vespa) $ vespa config set --local application vespa-team.vespacloud-docsearch.other -{% endhighlight %}
    +``` +### Deployment -

    Deployment

    -
    {% highlight shell %}
    +```bash expandable
     # Deploy an application package from cwd
     $ vespa deploy
     
    @@ -110,11 +95,11 @@ 

    Deployment

    # Remove a deployment from Vespa Cloud $ vespa destroy -a vespa-team.vespacloud-docsearch.other -{% endhighlight %}
    +``` +### Documents -

    Documents

    -
    {% highlight shell %}
    +```bash expandable
     # Put a document from file
     $ vespa document put file-with-one-doc.json
     
    @@ -127,7 +112,6 @@ 

    Documents

    } }' - # Put a document, ID in JSON $ vespa document put --data ' { @@ -191,26 +175,19 @@

    Documents

    # Copy documents from one cluster to another: $ vespa visit --target http://localhost:8080 | vespa feed --target http://localhost:9090 - -{% endhighlight %}
    -

    Notes:

    -
      -
    • The input files for vespa feed contains either a JSON array of feed operations, - or one JSON operation per line (JSONL).
    • -
    • The <document-api> - must be enabled in the container before documents can be fed or accessed - - see - example.
    • -
    • - For automation, see example usage in a - GitHub Action. - This action uses security credentials in VESPA_CLI_DATA_PLANE_CERT and VESPA_CLI_DATA_PLANE_KEY - for easy security management in GitHub. -
    • -
    - - -

    Queries

    -
    {% highlight shell %}
    +```
    +
    +
    +Notes:
    +
    +- The input files for `vespa feed` contains either a JSON array of feed operations, or one JSON operation per line ([JSONL](https://jsonlines.org/)).
    +- The [\](/en/reference/applications/services/container#document-api) must be enabled in the container before documents can be fed or accessed - see [example](https://github.com/vespa-engine/sample-apps/blob/master/album-recommendation/app/services.xml).
    +- For automation, see example usage in a [GitHub Action](https://github.com/vespa-engine/documentation/blob/master/.github/workflows/feed.yml). This action uses security credentials in `VESPA_CLI_DATA_PLANE_CERT` and `VESPA_CLI_DATA_PLANE_KEY` for easy security management in GitHub.
    + 
    +
    +### Queries
    +
    +```bash expandable
     # Query for all documents in all schemas / sources
     $ vespa query 'yql=select * from sources * where true'
     
    @@ -229,12 +206,13 @@ 

    Queries

    # Use a query file - useful for large queries, e.g., when using query vectors $ vespa query --file queries-vector.json -{% endhighlight %}
    -

    Example query file:

    -
    {% highlight json %}
    +```
    +Example query file:
    +
    +```json
     {
         "yql": "select product_id, title from products where {totalTargetHits: 200}nearestNeighbor(dense_embedding, q_vector)",
         "input.query(q_vector)": [-0.050548091530799866, ... ,0.028366032987833023],
         "ranking": "vector_distance"
     }
    -{% endhighlight %}
    +``` \ No newline at end of file diff --git a/en/clients/vespa-feed-client.html b/en/clients/vespa-feed-client.html deleted file mode 100644 index 330fe60a95..0000000000 --- a/en/clients/vespa-feed-client.html +++ /dev/null @@ -1,198 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "vespa-feed-client" -redirect_from: -- /en/vespa-feed-client.html ---- -
      -
    • Java library and command line client for feeding document operations using - Document v1 over HTTP/2.
    • -
    • Asynchronous, high-performance Java implementation, with retries and dynamic throttling.
    • -
    • Supports a JSON array of feed operations, as well as JSONL: - one operation JSON per line.
    • -
    - - - -

    Installing

    - - -

    Java library

    - -

    The Java library is available as a -Maven JAR artifact -at Maven Central. It requires minimum JDK17.

    -

    - Find an example application using this client at - client-java. -

    - - -

    Command line client

    - -

    Two alternatives:

    - -

    Download example:

    -
    -$ F_REPO="https://repo1.maven.org/maven2/com/yahoo/vespa/vespa-feed-client-cli" && \
    -  F_VER=$(curl -Ss "${F_REPO}/maven-metadata.xml" | sed -n 's/.*<release>\(.*\)<.*>/\1/p') && \
    -  curl -SsLo vespa-feed-client-cli.zip ${F_REPO}/${F_VER}/vespa-feed-client-cli-${F_VER}-zip.zip && \
    -  unzip -o vespa-feed-client-cli.zip
    -
    - - -

    Enable feed endpoint in Vespa

    - -

    Requirements:

    - -

    - HTTP/2 over TLS is optional but recommended from a security perspective. -

    -

    Example services.xml with TLS:

    -
    -<?xml version="1.0" encoding="utf-8" ?>
    -<services version="1.0">
    -    <container version="1.0" id="default">
    -        <http>
    -            <server id="default" port="443">
    -                <ssl>
    -                    <private-key-file>/path/to/private-key.pem</private-key-file>
    -                    <certificate-file>/path/to/certificate.pem</certificate-file>
    -                    <ca-certificates-file>/path/ca-certificates.pem</ca-certificates-file>
    -                </ssl>
    -            </server>
    -        </http>
    -        <document-api/>
    -    </container>
    -</services>
    -
    -

    Example services.xml without TLS:

    -
    -<?xml version="1.0" encoding="utf-8" ?>
    -<services version="1.0">
    -    <container version="1.0" id="default">
    -        <document-api/>
    -    </container>
    -</services>
    -
    - - -

    Using the client

    - -

    - The Javadoc for the programmatic API is available at - javadoc.io. - See output of $ vespa-feed-client --help for usage. -

    -

    - Use --speed-test for bandwidth testing. -

    - -

    Example Java

    -

    Add vespa-feed-client as dependency to your Maven (or other build system using Maven for dependency management):

    -
    -<dependency>
    -    <groupId>com.yahoo.vespa</groupId>
    -    <artifactId>vespa-feed-client</artifactId>
    -    <version>{{site.variables.vespa_version}}</version>
    -</dependency>
    -
    -

    - Code examples are listed in the - - vespa-feed-client source code on GitHub. -

    - - - -

    Example command line

    -

    HTTP/2 over TLS:

    -
    -$ vespa-feed-client \
    -  --connections 4 \
    -  --certificate cert.pem --private-key key.pem --ca-certificates ca.pem \
    -  --file /path/to/json/file \
    -  --endpoint https://container-endpoint:443/
    -
    -

    - The input must be either a proper JSON array, or a series, - of JSON feed operations (JSONL), - in the format described for the Vespa feed client - here. -

    -

    HTTP/2 without TLS:

    -
    -$ vespa-feed-client \
    -  --connections 4 \
    -  --file /path/to/json/file \
    -  --endpoint http://container-endpoint:8080/
    -
    - - -

    Tuning for multi-worker pipelines

    -

    - A common pattern is feeding from an Apache Beam topology - (e.g., Google Cloud Dataflow). - It is important to balance the number of workers and the connection settings. -

    -

    - As each of the workers initializes its own FeedClient instance, - the default settings can create too many connections. - In this example we assume 128 workers and 10 Vespa Container nodes. - With defaults (8 connections per endpoint, 128 max streams per connection), - 128 workers opens 1,024 connections — each requiring a TLS handshake to the endpoint — - which is a major source of container CPU overhead. -

    -

    - Recommended configuration per worker - (Javadoc): -

    -
    {% highlight java %}
    -FeedClient client = FeedClientBuilder.create(endpoint)
    -    .setConnectionsPerEndpoint(1)
    -    .setMaxStreamPerConnection(maxStreams)
    -    .setInitialInflightFactor(factor)
    -    .build();
    -{% endhighlight %}
    -
      -
    • - setConnectionsPerEndpoint(1): - One connection per worker gives 128 total, which is more than sufficient for 10 container nodes. -
    • -
    • - setMaxStreamPerConnection(maxStreams): - Calculate based on the target feed rate and total number of workers. - For example, if the target is 50k docs/sec across 128 workers, each worker needs ~390 docs/sec. - With typical per-document latency of 5–10 ms, each worker needs ~2–4 concurrent streams. -
    • -
    • - setInitialInflightFactor(factor): - The dynamic throttler starts at a low inflight count and slowly ramps up via random walk. - If you observe slow ramp-up at the start of a feed job, - set this to a higher value (e.g., 4–8) to start closer to the optimal inflight level. - The factor multiplies the minimum inflight (2 × connectionsPerEndpoint × endpoints), - so with 1 connection and factor 8, you'd start at 16 inflight instead of 2. -
    • -
    - - {% include important.html content=" - Each worker should create a single FeedClient instance and reuse it for the lifetime of the worker. - Creating new instances per batch or per document group defeats connection reuse and prevents the throttler from converging." %} -

    - Also, use vespa-feed-client 8.657 or later, - for the latest improvements to connection handling and stability. -

    diff --git a/en/clients/vespa-feed-client.mdx b/en/clients/vespa-feed-client.mdx new file mode 100644 index 0000000000..47b8f2a2ff --- /dev/null +++ b/en/clients/vespa-feed-client.mdx @@ -0,0 +1,145 @@ +--- +title: "vespa-feed-client" +sidebarTitle: "Java feed client" +--- + +- Java library and command line client for feeding document operations using [Document v1](/en/writing/document-v1-api-guide) over [HTTP/2](/en/performance/http2). +- Asynchronous, high-performance Java implementation, with retries and dynamic throttling. +- Supports a JSON array of feed operations, as well as [JSONL](https://jsonlines.org): one operation JSON per line. + +## Installing + +### Java library + +The Java library is available as a [Maven JAR artifact](https://search.maven.org/search?q=g:com.yahoo.vespa%20a:vespa-feed-client) at Maven Central. It requires minimum JDK17. + +Find an example application using this client at [client-java](https://github.com/vespa-engine/sample-apps/blob/master/examples/clients/client-java/README.md). + +### Command line client + +Two alternatives: + +- Install [_vespa-clients_/_vespa_](/en/operations/self-managed/build-install) RPM package. +- Download [vespa-feed-client **zip** artifact](https://search.maven.org/artifact/com.yahoo.vespa/vespa-feed-client-cli) from Maven Central. + +Download example: + +```bash +$ F_REPO="https://repo1.maven.org/maven2/com/yahoo/vespa/vespa-feed-client-cli" && \ + F_VER=$(curl -Ss "${F_REPO}/maven-metadata.xml" | sed -n 's/.*\(.*\)<.*>/\1/p') && \ + curl -SsLo vespa-feed-client-cli.zip ${F_REPO}/${F_VER}/vespa-feed-client-cli-${F_VER}-zip.zip && \ + unzip -o vespa-feed-client-cli.zip +``` + +## Enable feed endpoint in Vespa + +Requirements: + +- [Document API must be enabled on container](/en/reference/applications/services/container#document-api). + +HTTP/2 over [TLS](/en/reference/applications/services/http#ssl) is optional but recommended from a security perspective. + +Example _services.xml_ with TLS: + +```xml highlight= {4-13} + + + + + + + /path/to/private-key.pem + /path/to/certificate.pem + /path/ca-certificates.pem + + + + + + + +``` + +Example _services.xml_ without TLS: + +```xml highlight= {4} + + + + + + +``` + +## Using the client + +The Javadoc for the programmatic API is available at [javadoc.io](https://javadoc.io/doc/com.yahoo.vespa/vespa-feed-client-api). See output of `$ vespa-feed-client --help` for usage. + +Use `--speed-test` for bandwidth testing. + +### Example Java + +Add _vespa-feed-client_ as dependency to your Maven (or other build system using Maven for dependency management): + +``` + + com.yahoo.vespa + vespa-feed-client + 8.689.26 + +``` + +Code examples are listed in the [vespa-feed-client source code](https://github.com/vespa-engine/vespa/tree/master/vespa-feed-client-api/src/test/java/ai/vespa/feed/client/examples) on GitHub. + +- [JsonFileFeederExample.java](https://github.com/vespa-engine/vespa/blob/master/vespa-feed-client-api/src/test/java/ai/vespa/feed/client/examples/JsonFileFeederExample.java) +- [JsonStreamFeederExample.java](https://github.com/vespa-engine/vespa/blob/master/vespa-feed-client-api/src/test/java/ai/vespa/feed/client/examples/JsonStreamFeederExample.java) +- [SimpleExample.java](https://github.com/vespa-engine/vespa/blob/master/vespa-feed-client-api/src/test/java/ai/vespa/feed/client/examples/SimpleExample.java) + +### Example command line + +HTTP/2 over TLS: + +```bash +$ vespa-feed-client \ + --connections 4 \ + --certificate cert.pem --private-key key.pem --ca-certificates ca.pem \ + --file /path/to/json/file \ + --endpoint https://container-endpoint:443/ +``` + +The input must be either a proper JSON array, or a series, of JSON feed operations ([JSONL](https://jsonlines.org)), in the format described for the Vespa feed client [here](../reference/schemas/document-json-format#document-operations). + +HTTP/2 without TLS: + +```bash +$ vespa-feed-client \ + --connections 4 \ + --file /path/to/json/file \ + --endpoint http://container-endpoint:8080/ +``` + +## Tuning for multi-worker pipelines + +A common pattern is feeding from an [Apache Beam](https://beam.apache.org/) topology (e.g., [Google Cloud Dataflow](https://docs.cloud.google.com/dataflow/docs/overview)). It is important to balance the number of workers and the connection settings. + +As each of the workers initializes its own `FeedClient` instance, the default settings can create too many connections. In this example we assume 128 workers and 10 Vespa Container nodes. With defaults (8 connections per endpoint, 128 max streams per connection), 128 workers opens 1,024 connections - each requiring a TLS handshake to the endpoint - which is a major source of container CPU overhead. + +Recommended configuration per worker ([Javadoc](https://javadoc.io/doc/com.yahoo.vespa/vespa-feed-client-api/latest/ai/vespa/feed/client/package-summary.html)): + +```java +FeedClient client = FeedClientBuilder.create(endpoint) + .setConnectionsPerEndpoint(1) + .setMaxStreamPerConnection(maxStreams) + .setInitialInflightFactor(factor) + .build(); +``` + +- `setConnectionsPerEndpoint(1)`: One connection per worker gives 128 total, which is more than sufficient for 10 container nodes. +- `setMaxStreamPerConnection(maxStreams)`: Calculate based on the target feed rate and total number of workers. For example, if the target is 50k docs/sec across 128 workers, each worker needs ~390 docs/sec. With typical per-document latency of 5-10 ms, each worker needs ~2-4 concurrent streams. +- `setInitialInflightFactor(factor)`: The dynamic throttler starts at a low inflight count and slowly ramps up via random walk. If you observe slow ramp-up at the start of a feed job, set this to a higher value (e.g., 4-8) to start closer to the optimal inflight level. The factor multiplies the minimum inflight (2 x connectionsPerEndpoint x endpoints), so with 1 connection and factor 8, you'd start at 16 inflight instead of 2. + + + **Important:** Each worker should create a single `FeedClient` instance and reuse it for the lifetime of the worker. Creating new instances per batch or per document group defeats connection reuse and prevents the throttler from converging. + + +Also, use vespa-feed-client 8.657 or later, for the latest improvements to connection handling and stability. \ No newline at end of file diff --git a/en/cloud/image.png b/en/cloud/image.png new file mode 100644 index 0000000000000000000000000000000000000000..497d81f65026937fb134fe3e4ddc8b321837649d GIT binary patch literal 140872 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

    +3mi!uIw5+Q6Sq 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 H8v6Vnw3KaH literal 0 HcmV?d00001 diff --git a/en/cloud/quota.html b/en/cloud/quota.html deleted file mode 100644 index 3e0dd30835..0000000000 --- a/en/cloud/quota.html +++ /dev/null @@ -1,37 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: Quota ---- - -

    - Tenants in Vespa Cloud have a quota that limits the amount of resources a tenant can use. - The quota is expressed as $/hour, - and is based on the maximum possible cost for a Vespa application. -

    -

    - That means, if you are using autoscaling, - the quota it will use is based on the maximum configured size of the application. -

    -

    - You can see how much quota your applications are using in the Vespa Cloud console. - The quota a tenant has depends on the plan the tenant is on: -

    - - - - - - - - - - - - - - - - - -
    PlanQuota
    Trial$2/hour
    All other plans$10/hour
    -

    Contact Support to change the quota.

    diff --git a/en/cloud/quota.mdx b/en/cloud/quota.mdx new file mode 100644 index 0000000000..07c9b7385a --- /dev/null +++ b/en/cloud/quota.mdx @@ -0,0 +1,29 @@ +--- +title: Quota +description: "Tenants in Vespa Cloud have a quota that limits the amount of resources a tenant can use. The quota is expressed as *$/hour*, and is based on the maximum possible cost for a Vespa application." +--- + +That means, if you are using [autoscaling](/en/operations/autoscaling), the quota it will use is based on the maximum configured size of the application. + +You can see how much quota your applications are using in the Vespa Cloud console. The quota a tenant has depends on the [plan](https://vespa.ai/pricing/) the tenant is on: + + + + + + + + + + + + + + + + + + +
    PlanQuota
    Trial\$2/hour
    All other plans\$10/hour
    + +Contact [Support](https://vespa.ai/support) to change the quota. \ No newline at end of file diff --git a/en/cloud/support.md b/en/cloud/support.md deleted file mode 100644 index 75e393337f..0000000000 --- a/en/cloud/support.md +++ /dev/null @@ -1,67 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: Cloud and Enterprise support ---- - -Support options and other resources like status tracking -are found at [Vespa Support](https://vespa.ai/support/). - - - -## Create a support case -Open a support case using the support portal at [support.vespa.ai](https://support.vespa.ai/). - -Use this for: - -* Production support (reads and writes) -* Deployment support (making changes to applications) -* Technical support (general, including user access) -* Feature requests - -Use the support portal to track your ongoing cases. - -In case of any problems with the support portal itself, mail [support@vespa.ai](mailto:support@vespa.ai). - -You must be a [registered user](https://console.vespa-cloud.com/link/tenant/account/users) in your -organization's [tenant](https://docs.vespa.ai/en/learn/tenant-apps-instances.html) in the Vespa Console -to create a support case. - -## Escalate a support case -Support response times are defined for the different [support levels](https://cloud.vespa.ai/price-calculator). - -To escalate a support case for response within defined SLA, first create the case, -then use "Escalate to oncall" to page the Vespa Team: - -Example support case - -An escalation will be acknowledged in the support case ticket within support SLA. -Note that non-escalated cases will be handled during regular business hours. - -Depending on support level, your organization might have a shared Slack channel with the Vespa Team. -Such a channel does not have an SLA, and does not replace the need to create a support ticket. -The Slack channel is used on a best-effort basis and can be a useful tool in the support case process. - - -## Incident management -Depending on the severity of a support case, the Vespa Team might create an incident. - -A customer can request the incident process to be initiated in a support case. - -### Incident process -An incident creation triggers the incident process. When the incident is resolved, a root cause analysis (RCA) is performed. - -During an incident, the support case is updated with relevant status at regular intervals: -* The teams can mutually agree to use a shared Slack-channel for status and coordinated work - - in this case, the support case will have a link to the Slack channel -* The teams can mutually agree on next steps and timing, in the support case ticket or Slack channel. - - -### Post-mortem -The incident process includes a post-mortem event. -Post-mortems are held weekly, on cases that are closed at least two days before the post-mortem, -and all relevant information for the post-mortem is made available. - -Post-mortems are internal to Vespa.ai. -A customer can request a joint post-mortem meeting after the Vespa.ai-internal post-mortem is completed. - -A Post-mortem report is shared with the customer within 7 days of the post-mortem event. diff --git a/en/cloud/support.mdx b/en/cloud/support.mdx new file mode 100644 index 0000000000..112e476f55 --- /dev/null +++ b/en/cloud/support.mdx @@ -0,0 +1,60 @@ +--- +title: "Cloud and Enterprise support" +sidebarTitle: "Support" +--- + +Support options and other resources like status tracking are found at [Vespa Support](https://vespa.ai/support/). + +## Create a support case + +Open a support case using the support portal at [support.vespa.ai](https://support.vespa.ai/). + +Use this for: + +- Production support (reads and writes) +- Deployment support (making changes to applications) +- Technical support (general, including user access) +- Feature requests + +Use the support portal to track your ongoing cases. + +In case of any problems with the support portal itself, mail [support@vespa.ai](mailto:support@vespa.ai). + +You must be a [registered user](https://console.vespa-cloud.com/link/tenant/account/users) in your organization's [tenant](/en/learn/tenant-apps-instances.html) in the Vespa Console to create a support case. + +## Escalate a support case + +Support response times are defined for the different [support levels](https://cloud.vespa.ai/price-calculator?_gl=1*yoo5sb*_gcl_au*ODE0ODM4MTI2LjE3Nzk3MjQ3OTY.). + +To escalate a support case for response within defined SLA, first create the case, then use "Escalate to oncall" to page the Vespa Team: + + +![Example support case](/en/cloud/image.png) + + +An escalation will be acknowledged in the support case ticket within support SLA. Note that non-escalated cases will be handled during regular business hours. + +Depending on support level, your organization might have a shared Slack channel with the Vespa Team. Such a channel does not have an SLA, and does not replace the need to create a support ticket. The Slack channel is used on a best-effort basis and can be a useful tool in the support case process. + +## Incident management + +Depending on the severity of a support case, the Vespa Team might create an incident. + +A customer can request the incident process to be initiated in a support case. + +### Incident process + +An incident creation triggers the incident process. When the incident is resolved, a root cause analysis (RCA) is performed. + +During an incident, the support case is updated with relevant status at regular intervals: + +- The teams can mutually agree to use a shared Slack-channel for status and coordinated work - in this case, the support case will have a link to the Slack channel +- The teams can mutually agree on next steps and timing, in the support case ticket or Slack channel. + +### Post-mortem + +The incident process includes a post-mortem event. Post-mortems are held weekly, on cases that are closed at least two days before the post-mortem, and all relevant information for the post-mortem is made available. + +Post-mortems are internal to Vespa.ai. A customer can request a joint post-mortem meeting after the Vespa.ai-internal post-mortem is completed. + +A Post-mortem report is shared with the customer within 7 days of the post-mortem event. \ No newline at end of file diff --git a/en/content/attributes.html b/en/content/attributes.html deleted file mode 100644 index 1e43bf5009..0000000000 --- a/en/content/attributes.html +++ /dev/null @@ -1,727 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Attributes" -redirect_from: -- /en/attributes.html ---- - -

    - An attribute is a schema keyword, - specifying the indexing for a field: -

    -
    -field price type int {
    -    indexing: attribute
    -}
    -
    -

    - Attribute properties and use cases: -

    -
      -
    • Flexible match modes - including exact match, prefix match, and case-sensitive matching, but not text matching (tokenization and linguistic processing).
    • -
    • High sustained update rates (avoiding read-apply-write patterns). Any mutating operation against an attribute field - is written to Vespa's transaction log and persisted, but appending to the log is sequential access, not random. - Read more in partial updates.
    • -
    • Instant query updates - values are immediately searchable.
    • -
    • Document Summaries are memory-only operations if all fields are attributes.
    • -
    • Numerical range queries. -
      where price > 100
    • -
    • Grouping - aggregate results into groups - - it is also great for generating diversity in results. -
      -all(group(customer) each(max(3) each(output(summary()))))
      -
    • -
    • Ranking - use attribute values directly in rank functions. -
      -rank-profile rank_fresh {
      -    first-phase {
      -        expression { freshness(timestamp) }
      -    }
      -}
      -
    • -
    • Sorting - order results by attribute value. -
      -order by price asc, release_date desc
      -
    • -
    • Parent/child - import attribute values from global parent documents. -
      -import field advertiser_ref.name as advertiser_name {}
      -
    • - -
    - -

    - The other field option is index - - use index for fields used for - text search, - with stemming and - normalization. -

    -

    - An attribute is an in-memory data structure. - Attributes speed up query execution and document updates, trading off memory. - As data structures are regularly optimized, consider both static and temporary resource usage - - see attribute memory usage below. - Use attributes in document summaries to limit access to storage to generate result sets. -

    -Attribute is an in-memory data structure -

    - Configuration overview: -

    - - - - - - - - - - - - -
    fast-search - -
    -field titles type array<string> {
    -    indexing : summary | attribute
    -    attribute: fast-search
    -}
    -
    -
    fast-access -

    - For high-throughput updates, all nodes with a replica should have the attribute loaded in memory. - Depending on replication factor and other configuration, this is not always the case. - Use fast-access - to increase feed rates by having replicas on all nodes in memory - - see the reference - and sizing feeding. -

    -
    -field titles type array<string> {
    -    indexing : summary | attribute
    -    attribute: fast-access
    -}
    -
    -
    distance-metric -

    - Features like nearest neighbor search - require a distance-metric, - and can also have an hsnw index to speed up queries. - Read more in approximate nearest neighbor. - Pay attention to the field's index setting to enable the index: -

    -
    -field image_sift_encoding type tensor<float>(x[128]) {
    -    indexing: summary | attribute | index
    -    attribute {
    -        distance-metric: euclidean
    -    }
    -    index {
    -        hnsw {
    -            max-links-per-node: 16
    -            neighbors-to-explore-at-insert: 500
    -        }
    -    }
    -}
    -
    -
    - - - -

    Data structures

    -

    - The attribute field's data type decides which data structures are used by the attribute - to store values for that field across all documents on a content node. - For some data types, a combination of data structures is used: -

    -
      -
    • Attribute Multivalue Mapping stores arrays of values for array and weighed set types.
    • -
    • Attribute Enum Store stores unique strings for all string attributes and unique values for - attributes with fast-search.
    • -
    • Attribute Tensor Store stores tensor values for all tensor attributes.
    • -
    -

    - In the following illustration, a row represents a document, while a named column represents an attribute. -

    -Attribute in-memory stores -

    - Attributes can be: -

    - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    TypeSizeDescription
    Single-valuedFixed - Like the "A" attribute, example int. - The element size is the size of the type, like 4 bytes for an integer. - A memory buffer (indexed by Local ID) holds all values directly. -
    Multi-valuedFixed - Like the "B" attribute, example array<int>. - A memory buffer (indexed by Local ID) is holding references (32 bit) to where in the Multivalue Mapping the arrays are stored. - The Multivalue Mapping consists of multiple memory buffers, where arrays of the same size are co-located in the same buffer. -
    Multi-valuedVariable - Like the "B" attribute, example array<string>. - A memory buffer (indexed by Local ID) is holding references (32 bit) to where in the Multivalue Mapping the arrays are stored. - The unique strings are stored in the Enum Store, and the arrays in the Multivalue Mapping - stores the references (32 bit) to the strings in the Enum Store. - The Enum Store consists of multiple memory buffers. -
    Single-valuedVariable - Like the "C" attribute, example string. - A memory buffer (indexed by Local ID) is holding references (32 bit) to where in the Enum Store the strings are stored. -
    TensorFixed / Variable - Like the "D" attribute, example tensor<float>(x{},y[64]). - A memory buffer (indexed by Local ID) is holding references (32 bit) to where in the Tensor Store the tensor values are stored. - The memory layout in the Tensor Store depends on the tensor type. -
    -

    - The "A", "B", "C" and "D" attribute memory buffers have attribute values or references in Local ID (LID) order - - see document meta store. -

    -

    - When updating an attribute, the full value is written. - This also applies to multivalue fields - - example adding an item to an array: -

    -
      -
    1. Space for the new array is reserved in a memory buffer
    2. -
    3. The current value is copied
    4. -
    5. The new element is written
    6. -
    -

    - This means that larger fields will copy more data at updates. - It also implies that updates to weighted sets - are faster when using numeric keys (less memory and easier comparisons). -

    -

    - Data stored in the Multivalue Mapping, Enum Store and Tensor Store - is referenced using 32 bit references. - This address space can go full, and then feeding is blocked - learn more. - For array or weighted set attributes, - the max limit on the number of documents that can have the same number of values is approx 2 billion per node. - For string attributes or attributes with - fast-search, - the max limit on the number of unique values is approx 2 billion per node. -

    - - - -

    Index structures

    -

    - Without fast-search, attribute access is a memory lookup, - being one value or all values, depending on query execution. - An attribute is a linear array-like data structure - - matching documents potentially means scanning all attribute values. -

    -

    - Setting fast-search - creates an index structure for quicker lookup and search. - This consists of a dictionary pointing to posting lists. - This uses more memory, and also more CPU when updating documents. - It increases steady state memory usage for all attribute types - and also add initialization overhead for numeric types. -

    -

    - The default dictionary is a b-tree of attribute values, - pointing to an occurrence b-tree (posting list) of local doc IDs for each value, exemplified in the A-attribute below. - Using dictionary: hash on the attribute generates a hash table of attributes values pointing to the posting lists, - as in the C-attribute (short posting lists are represented as arrays instead of b-trees): -

    -Attribute index structures -

    -Notes: -

    -
      -
    • If a value occurs in many documents, the occurrence b-tree grows large. - For such values, a boolean-occurrence list (i.e. bitvector) is generated in addition to the b-tree.
    • -
    • Setting fast-search is not observable in the files on disk, other than size.
    • -
    • fast-search causes a memory increase even for empty fields, - due to the extra index structures created. - E.g. single value fields will have the "undefined value" when empty, - and there is a posting list for this value.
    • -
    • The value b-tree enables fast range-searches in numerical attributes. - This is also available for hash-based dictionaries, but slower as a full scan is needed.
    • -
    -

    - Using fast-search has many implications, read more in - when to use fast-search. -

    - - - -

    Attribute memory usage

    -

    - Attribute structures are regularly optimized, and this causes temporary resource usage - - read more in maintenance jobs. - The memory footprint of an attribute depends on a few factors, - data type being the most important: -

    -
      -
    • Numeric (int, long, byte, and double) and Boolean (bit) types - - fixed length and fix cost per document
    • -
    • String type - the footprint depends on the length of the - strings and how many unique strings that needs to be - stored.
    • -
    -

    - Collection types like array and weighted sets increases the memory - usage some, but the main factor is the average number of values per document. - String attributes are typically the largest attributes, - and requires most memory during initialization - use boolean/numeric types where possible. - Example, refer to formulas below: -

    -
    -schema foo {
    -    document bar {
    -        field titles type array<string> {
    -            indexing: summary | attribute
    -        }
    -    }
    -}
    -
    -
      -
    • Assume average 10 values per document, average string length 15, 100k unique strings and 20M documents.
    • -
    • Steady state memory usage is approx 1 GB (20M*4*(6/5) + 20M*10*4*(6/5) + 100k*(15+1+4+4)*(6/5)).
    • -
    • During initialization (loading attribute from disk) an additional 2.4 GB is allocated (20M*10*(4+4+4), - for each value: -
        -
      • local document ID
      • -
      • enum value
      • -
      • weight
      • -
      -
    • -
    • Increasing the average number of values per document to 20 (double) will also double the memory - footprint during initialization (4.8 GB).
    • -
    -

    - When doing the capacity planning, keep in mind the maximum footprint, which occurs during initialization. - For the steady state footprint, the number of unique values is important for string attributes. -

    - Check the Example attribute sizing spreadsheet, - with various data types and collection types. - It also contains estimates for how many documents a 48 GB RAM node can hold, taking initialization into account. -

    -

    - Multivalue attributes use an - adaptive approach in how data is stored in memory, - and up to 2 billion documents per node is supported. -

    -

    - Pro-tip: The proton /state/v1/ interface can be explored for attribute memory usage. - This is an undocumented debug-interface, subject to change at any moment - example: - http://localhost:19110/state/v1/custom/component/documentdb/music/subdb/ready/attribute/artist -

    - - - -

    Attribute file usage

    -

    Attribute data is stored in two locations on disk:

    -
      -
    • -

      - The attribute store in memory, which is regularly flushed to disk. - At startup, the flushed files are used to quickly populate the memory structures, - resulting in a much quicker startup compared to generating the attribute store from the source in the document store. -

      -

      - The attribute store will temporarily double its disk usage when generating a new flush file, - see attribute flush. -

      -
    • -
    • -

      - The document store on disk. - Documents here are used to (re)generate index structures, - as well as being the source for replica generation across nodes. -

      -

      - Note that the attribute data is stored in the document store regardless of - the summary configuration. -

      -
    • -
    -

    - The different field types use various data types for storage, see below, - a conservative rule of thumb for steady-state disk usage is hence twice the data size. -

    - - - -

    Sizing

    -

    - Attribute sizing is not an exact science but rather an approximation. - The reason is that they vary in size. - Both the number of documents, number of values, and uniqueness of the values are variable. - The components of the attributes that occupy memory are: -

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    AbbreviationConceptComment
    DNumber of documentsNumber of documents on the node, or rather the maximum number of local document IDs allocated
    VAverage number of values per documentOnly applicable for arrays and weighted sets
    UNumber of unique valuesOnly applies for strings or if - fast-search is set
    FWFixed data widthsizeof(T) for numerics, 1 byte for strings, 1 bit for boolean
    WWWeight widthWidth of the weight in a weighted set, 4 bytes. 0 bytes for arrays.
    EIWEnum index widthWidth of the index into the enum store, 4 bytes. Used by all strings and other attributes if - fast-search is set
    VWVariable data widthstrlen(s) for strings, 0 bytes for the rest
    PWPosting entry widthWidth of a posting list entry, 4 bytes for singlevalue, 8 bytes for array and weighted sets. - Only applies if fast-search is set.
    PIWPosting index widthWidth of the index into the store of posting lists; 4 bytes
    MIWMultivalue index widthWidth of the index into the multivalue mapping; 4 bytes
    ROFResize overhead factorDefault is 6/5. This is the average overhead in any dynamic vector due to - resizing strategy. Resize strategy is 50% indicating that - structure is 5/6 full on average.
    - - -

    Components

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    ComponentFormulaApprox FactorApplies to
    Document vectorD * ((FW or EIW) or MIW)ROFFW for singlevalue numeric attributes and MIW for - multivalue attributes. EIW for singlevalue string or if the - attribute is singlevalue fast-search
    Multivalue mappingD * V * ((FW or EIW) + WW)ROFApplicable only for array or weighted sets. EIW if string or fast-search
    Enum storeU * ((FW + VW) + 4 + ((EIW + PIW) or EIW))ROFApplicable for strings or if fast-search is set. - (EIW + PIW) if fast-search is set, EIW otherwise.
    Posting listD * V * PWROFApplicable if fast-search is set
    - - -

    Variants

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    TypeComponentsFormula
    Numeric singlevalue plainDocument vectorD * FW * ROF
    Numeric multivalue value plainDocument vector, Multivalue mappingD * MIW * ROF + D * V * (FW+WW) * ROF
    Numeric singlevalue fast-searchDocument vector, Enum store, Posting ListD * EIW * ROF + U * (FW+4+EIW+PIW) * ROF + D * PW * ROF
    Numeric multivalue value fast-searchDocument vector, Multivalue mapping, Enum store, Posting ListD * MIW * ROF + D * V * (EIW+WW) * ROF + U * (FW+4+EIW+PIW) * ROF + D * V * PW * ROF
    Singlevalue string plainDocument vector, Enum storeD * EIW * ROF + U * (FW+VW+4+EIW) * ROF
    Singlevalue string fast-searchDocument vector, Enum store, Posting ListD * EIW * ROF + U * (FW+VW+4+EIW+PIW) * ROF + D * PW * ROF
    Multivalue string plainDocument vector, Multivalue mapping, Enum storeD * MIW * ROF + D * V * (EIW+WW) * ROF + U * (FW+VW+4+EIW) * ROF
    Multivalue string fast-searchDocument vector, Multivalue mapping, Enum store, Posting listD * MIW * ROF + D * V * (EIW+WW) * ROF + U * (FW+VW+4+EIW+PIW) * ROF + D * V * PW * ROF
    Boolean singlevalueDocument vectorD * FW * ROF
    - - - -

    Paged attributes

    -

    - Regular attribute fields are guaranteed to be in-memory, while the - paged - attribute setting allows paging the attribute data out of memory to disk. - The paged setting is not supported for the following types: -

    - -

    - For attribute fields using - fast-search, - the memory needed for dictionary and index structures are never paged out to disk. -

    -

    - Using the paged setting for attributes - is an alternative when there are memory resource constraints - and the attribute data is only accessed by a limited number of hits per query during ranking. - E.g. a dense tensor attribute which is only used during a re-ranking phase, - where the number of attribute accesses are limited by the re-ranking phase count. -

    -

    - For example using a second phase total-rerank-count - of 100 will limit the maximum number of page-ins/disk access per query to 100. - Running at 100 QPS would need up to 10K disk accesses per second. - This is the worst case if none of the accessed attribute data were paged into memory already. - This depends on access locality and memory pressure (size of the attribute data versus available memory). -

    -

    - In this example, we have a dense tensor with - 1024 int8 values. - The tensor attribute is only accessed during re-ranking (second-phase ranking expression): -

    -
    -schema foo {
    -    document foo {
    -        field tensordata type tensor<int8>(x[1024]) {
    -            indexing: attribute
    -            attribute: paged
    -        }
    -    }
    -    rank-profile foo {
    -        first-phase {}
    -        second-phase {
    -            total-rerank-count: 100
    -            expression: sum(attribute(tensordata))
    -        }
    -    }
    -}
    -
    -

    - For some use cases where serving latency SLA is not strict and query throughput is low, - the paged attribute setting might be a tuning alternative, - as it allows storing more data per node. -

    - - -

    Paged attributes disadvantages

    -

    - The disadvantages of using paged attributes are many: -

    -
      -
    • -Unpredictable query latency as attribute access might touch disk. Limited queries per second throughput -per node (depends on the locality of document re-ranking requests). -
    • -
    • -Paged attributes are implemented by file-backed memory mappings. The performance depends on -the Linux virtual memory management ability to page -data in and out. Using many threads per search/high query throughput might cause high system (kernel) CPU and -system unresponsiveness. -
    • -
    • -The content node's total memory utilization will be close to 100% when using paged attributes. -It's up to the Linux kernel to determine what part of the attribute data is paged into memory based on access patterns. -A good understanding of how the Linux virtual memory management system works is recommended before enabling paged attributes. -
    • -
    • -The -memory usage metrics -from content nodes are not reflecting the reality when using paged attributes. -They can indicate a usage that is much higher than the available memory on the node. -This is because attribute memory usage is reported as the amount of data contained in the attribute, -and whether this data is paged out to disk is controlled by the Linux kernel. -
    • -
    • -Using paged attributes doubles the disk usage of attribute data. -For example if the original attribute size is 92 GB (100M documents of the above 1024 int8 per document schema), -using the paged setting will double the attribute disk usage to close to 200 GB. -
    • -
    • -Changing the paged setting (e.g. removing the option) on a running system might cause hard out-of-memory situations as -without paged, the content nodes will attempt loading the attribute into memory without the option for page outs. -
    • -
    • -Using a paged attribute in first-phase ranking -can result in extremely high query latency if a large amount of the corpus is retrieved by the query. -The number of disk accesses will, in the worst case, be equal to the number of hits the query produces. -A similar problem can occur if running a query that searches a paged attribute. -
    • -
    • -Using paged in combination with HNSW indexing is strongly discouraged. -HNSW indexing -also searches and reads tensors during indexing, causing random access during feeding. Once the system memory usage reaches 100%, -the Linux kernel will start paging pages in and out of memory. This can cause a high system (kernel) CPU and slows down HNSW indexing -throughput significantly. -
    • -
    - - - -

    Mutable attributes

    -

    - Mutable attributes is document metadata - for matching and ranking performance per document. -

    -

    - The attribute values are mutated as part of query execution, as defined in rank profiles - - see rank phase statistics for details. -

    - - - -

    Document meta store

    -

    - The document meta store is an in-memory data structure for all documents on a node. - It is an implicit attribute, and is - compacted and - flushed. - Memory usage for applications with small documents / no other attributes can be dominated by this attribute. -

    - The document meta store scales linearly with number of documents - - using approximately 30 bytes per document. - The metric content.proton.documentdb.ready.attribute.memory_usage.allocated_bytes for - "field": "[documentmetastore]" is the size of the document meta store in memory - use the - metric API to find the size - - in this example, the node has 9M ready documents with 52 bytes in memory per document: -

    -
    -{
    -    "name": "content.proton.documentdb.ready.attribute.memory_usage.allocated_bytes",
    -    "description": "The number of allocated bytes",
    -    "values": {
    -        "average": 4.69736008E8,
    -        "count": 12,
    -        "rate": 0.2,
    -        "min": 469736008,
    -        "max": 469736008,
    -        "last": 469736008
    -    },
    -    "dimensions": {
    -        "documenttype": "doctype",
    -        "field": "[documentmetastore]"
    -    }
    -},
    -
    -

    - The above is for the ready documents, - also check removed and notready - - refer to sub-databases. -

    diff --git a/en/content/attributes.mdx b/en/content/attributes.mdx new file mode 100644 index 0000000000..6935dc467e --- /dev/null +++ b/en/content/attributes.mdx @@ -0,0 +1,466 @@ +--- +title: "Document attributes" +description: "An *attribute* is a [schema](/en/reference/schemas/schemas#attribute) keyword, specifying the indexing for a field:" +--- + +```txt +field price type int { +indexing: attribute +} +``` + +Attribute properties and use cases: + +- Flexible [match modes](/en/reference/schemas/schemas#match) including exact match, prefix match, and case-sensitive matching, but not text matching (tokenization and linguistic processing). +- High sustained update rates (avoiding read-apply-write patterns). Any mutating operation against an attribute field is written to Vespa's [transaction log](/en/content/proton#transaction-log) and persisted, but appending to the log is sequential access, not random. Read more in [partial updates](/en/writing/partial-updates). +- Instant query updates - values are immediately searchable. +- [Document Summaries](/en/querying/document-summaries) are memory-only operations if all fields are attributes. +- [Numerical range queries](/en/reference/querying/yql#numeric). + + ```txt + where price > 100 + ``` + +- [Grouping](/en/querying/grouping) - aggregate results into groups - it is also great for generating diversity in results. + + ```txt + all(group(customer) each(max(3) each(output(summary())))) + ``` + +- [Ranking](/en/basics/ranking) - use attribute values directly in rank functions. + + ```txt + rank-profile rank_fresh { + first-phase { + expression { freshness(timestamp) } + } + } + ``` + +- [Sorting](/en/reference/querying/sorting-language) - order results by attribute value. + + ```txt + order by price asc, release_date desc + ``` + +- [Parent/child](/en/schemas/parent-child) - import attribute values from global parent documents. + + ```txt + import field advertiser_ref.name as advertiser_name {} + ``` + +The other field option is *index* - use [index](/en/content/proton#index) for fields used for [text search](/en/querying/text-matching), with [stemming](/en/linguistics/linguistics-opennlp#stemming) and [normalization](/en/linguistics/linguistics-opennlp#normalization). + +An attribute is an in-memory data structure. Attributes speed up query execution and [document updates](/en/writing/partial-updates), trading off memory. As data structures are regularly optimized, consider both static and temporary resource usage - see [attribute memory usage](#attribute-memory-usage) below. Use attributes in document summaries to limit access to storage to generate result sets. + + +![](/assets/img/attributes-update.svg) + + +Configuration overview: + + + + + + + + + + + + + + + + + + + + + + +
    **fast-search**Also see the reference. Add an index structure to improve query performance:
    {`field titles type array {     indexing : summary | attribute     attribute: fast-search }`}
    **fast-access**For high-throughput updates, all nodes with a replica should have the attribute loaded in memory. Depending on replication factor and other configuration, this is not always the case. Use fast-access to increase feed rates by having replicas on all nodes in memory - see the reference and sizing feeding.
    {`field titles type array {     indexing : summary | attribute     attribute: fast-access }`}
    **distance-metric**Features like nearest neighbor search require a distance-metric, and can also have an {`hsnw index`} to speed up queries. Read more in approximate nearest neighbor. Pay attention to the field's {`index`} setting to enable the index:
    {`field image_sift_encoding type tensor(x[128]) {     indexing: summary | attribute | index     attribute {         distance-metric: euclidean     }     index {         hnsw {             max-links-per-node: 16             neighbors-to-explore-at-insert: 500         }     } }`}
    + +The attribute field's data type decides which data structures are used by the attribute to store values for that field across all documents on a content node. For some data types, a combination of data structures is used: + +- *Attribute Multivalue Mapping* stores arrays of values for array and weighed set types. +- *Attribute Enum Store* stores unique strings for all string attributes and unique values for attributes with [fast-search](/en/content/attributes#fast-search). +- *Attribute Tensor Store* stores tensor values for all tensor attributes. + +In the following illustration, a row represents a document, while a named column represents an attribute. + + +![](/assets/img/attributes.svg) + + +Attributes can be: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    TypeSizeDescription
    Single-valuedFixedLike the "A" attribute, example {`int`}. The element size is the size of the type, like 4 bytes for an integer. A memory buffer (indexed by Local ID) holds all values directly.
    Multi-valuedFixedLike the "B" attribute, example {`array`}. A memory buffer (indexed by Local ID) is holding references (32 bit) to where in the *Multivalue Mapping* the arrays are stored. The *Multivalue Mapping* consists of multiple memory buffers, where arrays of the same size are co-located in the same buffer.
    Multi-valuedVariableLike the "B" attribute, example {`array`}. A memory buffer (indexed by Local ID) is holding references (32 bit) to where in the *Multivalue Mapping* the arrays are stored. The unique strings are stored in the *Enum Store*, and the arrays in the *Multivalue Mapping* stores the references (32 bit) to the strings in the *Enum Store*. The *Enum Store* consists of multiple memory buffers.
    Single-valuedVariableLike the "C" attribute, example {`string`}. A memory buffer (indexed by Local ID) is holding references (32 bit) to where in the *Enum Store* the strings are stored.
    TensorFixed / VariableLike the "D" attribute, example {`tensor(x{},y[64])`}. A memory buffer (indexed by Local ID) is holding references (32 bit) to where in the *Tensor Store* the tensor values are stored. The memory layout in the *Tensor Store* depends on the tensor type.
    + +The "A", "B", "C" and "D" attribute memory buffers have attribute values or references in Local ID (LID) order - see [document meta store](#document-meta-store). + +When updating an attribute, the full value is written. This also applies to [multivalue](/en/basics/schemas#document-fields) fields - example adding an item to an array: + +1. Space for the new array is reserved in a memory buffer +2. The current value is copied +3. The new element is written + +This means that larger fields will copy more data at updates. It also implies that updates to [weighted sets](/en/reference/schemas/schemas#weightedset) are faster when using numeric keys (less memory and easier comparisons). + +Data stored in the *Multivalue Mapping*, *Enum Store* and *Tensor Store* is referenced using 32 bit references. This address space can go full, and then feeding is blocked - [learn more](/en/writing/feed-block). For array or weighted set attributes, the max limit on the number of documents that can have the same number of values is approx 2 billion per node. For string attributes or attributes with [fast-search](/en/content/attributes#fast-search), the max limit on the number of unique values is approx 2 billion per node. + +## Index structures + +Without `fast-search`, attribute access is a memory lookup, being one value or all values, depending on query execution. An attribute is a linear array-like data structure - matching documents potentially means scanning *all* attribute values. + +Setting [fast-search](/en/reference/schemas/schemas#attribute) creates an index structure for quicker lookup and search. This consists of a [dictionary](/en/reference/schemas/schemas#dictionary) pointing to posting lists. This uses more memory, and also more CPU when updating documents. It increases steady state memory usage for all attribute types and also add initialization overhead for numeric types. + +The default dictionary is a b-tree of attribute *values*, pointing to an *occurrence* b-tree (posting list) of local doc IDs for each value, exemplified in the A-attribute below. Using `dictionary: hash` on the attribute generates a hash table of attributes values pointing to the posting lists, as in the C-attribute (short posting lists are represented as arrays instead of b-trees): + + +![](/assets/img/attributes-indexes.svg) + + +Notes: + +- If a value occurs in many documents, the *occurrence* b-tree grows large. For such values, a boolean-occurrence list (i.e. bitvector) is generated in addition to the b-tree. +- Setting `fast-search` is not observable in the files on disk, other than size. +- `fast-search` causes a memory increase even for empty fields, due to the extra index structures created. E.g. single value fields will have the "undefined value" when empty, and there is a posting list for this value. +- The *value* b-tree enables fast range-searches in numerical attributes. This is also available for `hash`-based dictionaries, but slower as a full scan is needed. + +Using `fast-search` has many implications, read more in [when to use fast-search](/en/performance/feature-tuning#when-to-use-fast-search-for-attribute-fields). + +## Attribute memory usage + +Attribute structures are regularly optimized, and this causes temporary resource usage - read more in [maintenance jobs](/en/content/proton#proton-maintenance-jobs). The memory footprint of an attribute depends on a few factors, data type being the most important: + +- Numeric (int, long, byte, and double) and Boolean (bit) types - fixed length and fix cost per document +- String type - the footprint depends on the length of the strings and how many unique strings that needs to be stored. + +Collection types like array and weighted sets increases the memory usage some, but the main factor is the average number of values per document. String attributes are typically the largest attributes, and requires most memory during initialization - use boolean/numeric types where possible. Example, refer to formulas below: + +```js +schema foo { +document bar { + field titles type array { + indexing: summary | attribute + } + } +} +``` + +- Assume average 10 values per document, average string length 15, 100k unique strings and 20M documents. +- Steady state memory usage is approx 1 GB \(20M\*4\*(6/5) + 20M\*10\*4\*(6/5) + 100k\*(15+1+4+4)\*(6/5)\). +- During initialization (loading attribute from disk) an additional 2.4 GB is allocated \(20M\*10\*(4+4+4)\), for each value: + +- local document ID +- enum value +- weight +- Increasing the average number of values per document to 20 (double) will also double the memory footprint during initialization (4.8 GB). + +When doing the capacity planning, keep in mind the maximum footprint, which occurs during initialization. For the steady state footprint, the number of unique values is important for string attributes. + +Check the [Example attribute sizing spreadsheet](/assets/attribute-memory-Vespa.xls), with various data types and collection types. It also contains estimates for how many documents a 48 GB RAM node can hold, taking initialization into account. + +[Multivalue](/en/basics/schemas#document-fields) attributes use an adaptive approach in how data is stored in memory, and up to 2 billion documents per node is supported. + + +**Pro-tip:** + +The proton */state/v1/* interface can be explored for attribute memory usage. This is an undocumented debug-interface, subject to change at any moment - example: *http://localhost:19110/state/v1/custom/component/documentdb/music/subdb/ready/attribute/artist* + + +## Attribute file usage + +Attribute data is stored in two locations on disk: + +- The attribute store in memory, which is regularly flushed to disk. At startup, the flushed files are used to quickly populate the memory structures, resulting in a much quicker startup compared to generating the attribute store from the source in the document store. The attribute store will temporarily double its disk usage when generating a new flush file, see [attribute flush](/en/content/proton#attribute-flush). +- The document store on disk. Documents here are used to (re)generate index structures, as well as being the source for replica generation across nodes. Note that the attribute data is stored in the document store regardless of the [summary](/en/querying/document-summaries) configuration. + +The different field types use various data types for storage, see below, a conservative rule of thumb for steady-state disk usage is hence twice the data size. + +## Sizing + +Attribute sizing is not an exact science but rather an approximation. The reason is that they vary in size. Both the number of documents, number of values, and uniqueness of the values are variable. The components of the attributes that occupy memory are: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    AbbreviationConceptComment
    DNumber of documentsNumber of documents on the node, or rather the maximum number of local document IDs allocated
    VAverage number of values per documentOnly applicable for arrays and weighted sets
    UNumber of unique valuesOnly applies for strings or if fast-search is set
    FWFixed data widthsizeof(T) for numerics, 1 byte for strings, 1 bit for boolean
    WWWeight widthWidth of the weight in a weighted set, 4 bytes. 0 bytes for arrays.
    EIWEnum index widthWidth of the index into the enum store, 4 bytes. Used by all strings and other attributes if fast-search is set
    VWVariable data widthstrlen(s) for strings, 0 bytes for the rest
    PWPosting entry widthWidth of a posting list entry, 4 bytes for singlevalue, 8 bytes for array and weighted sets. Only applies if fast-search is set.
    PIWPosting index widthWidth of the index into the store of posting lists; 4 bytes
    MIWMultivalue index widthWidth of the index into the multivalue mapping; 4 bytes
    ROFResize overhead factorDefault is 6/5. This is the average overhead in any dynamic vector due to resizing strategy. Resize strategy is 50% indicating that structure is 5/6 full on average.
    + +### Components + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    ComponentFormulaApprox FactorApplies to
    Document vectorD * ((FW or EIW) or MIW)ROFFW for singlevalue numeric attributes and MIW for multivalue attributes. EIW for singlevalue string or if the attribute is singlevalue fast-search
    Multivalue mappingD * V * ((FW or EIW) + WW)ROFApplicable only for array or weighted sets. EIW if string or fast-search
    Enum storeU * ((FW + VW) + 4 + ((EIW + PIW) or EIW))ROFApplicable for strings or if fast-search is set. (EIW + PIW) if fast-search is set, EIW otherwise.
    Posting listD * V * PWROFApplicable if fast-search is set
    + +### Variants + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    TypeComponentsFormula
    Numeric singlevalue plainDocument vectorD * FW * ROF
    Numeric multivalue value plainDocument vector, Multivalue mappingD * MIW * ROF + D * V * (FW+WW) * ROF
    Numeric singlevalue fast-searchDocument vector, Enum store, Posting ListD * EIW * ROF + U * (FW+4+EIW+PIW) * ROF + D * PW * ROF
    Numeric multivalue value fast-searchDocument vector, Multivalue mapping, Enum store, Posting ListD * MIW * ROF + D * V * (EIW+WW) * ROF + U * (FW+4+EIW+PIW) * ROF + D * V * PW * ROF
    Singlevalue string plainDocument vector, Enum storeD * EIW * ROF + U * (FW+VW+4+EIW) * ROF
    Singlevalue string fast-searchDocument vector, Enum store, Posting ListD * EIW * ROF + U * (FW+VW+4+EIW+PIW) * ROF + D * PW * ROF
    Multivalue string plainDocument vector, Multivalue mapping, Enum storeD * MIW * ROF + D * V * (EIW+WW) * ROF + U * (FW+VW+4+EIW) * ROF
    Multivalue string fast-searchDocument vector, Multivalue mapping, Enum store, Posting listD * MIW * ROF + D * V * (EIW+WW) * ROF + U * (FW+VW+4+EIW+PIW) * ROF + D * V * PW * ROF
    Boolean singlevalueDocument vectorD * FW * ROF
    + +## Paged attributes + +Regular attribute fields are guaranteed to be in-memory, while the [paged](/en/reference/schemas/schemas#attribute) attribute setting allows paging the attribute data out of memory to disk. The `paged` setting is *not* supported for the following types: + +- [tensor](/en/reference/schemas/schemas#tensor) with [fast-rank](/en/reference/schemas/schemas#attribute). +- [predicate](/en/reference/schemas/schemas#predicate). + +For attribute fields using [fast-search](/en/reference/schemas/schemas#attribute), the memory needed for dictionary and index structures are never paged out to disk. + +Using the `paged` setting for attributes is an alternative when there are memory resource constraints and the attribute data is only accessed by a limited number of hits per query during ranking. E.g. a dense tensor attribute which is only used during a [re-ranking phase](/en/ranking/phased-ranking), where the number of attribute accesses are limited by the re-ranking phase count. + +For example using a second phase [total-rerank-count](/en/reference/schemas/schemas#secondphase-total-rerank-count) of 100 will limit the maximum number of page-ins/disk access per query to 100. Running at 100 QPS would need up to 10K disk accesses per second. This is the worst case if none of the accessed attribute data were paged into memory already. This depends on access locality and memory pressure (size of the attribute data versus available memory). + +In this example, we have a dense tensor with 1024 [int8](/en/reference/ranking/tensor#tensor-type-spec) values. The tensor attribute is only accessed during re-ranking (second-phase ranking expression): + +```txt +schema foo { + document foo { + field tensordata type tensor(x[1024]) { + indexing: attribute + attribute: paged + } + } + rank-profile foo { + first-phase {} + second-phase { + total-rerank-count: 100 + expression: sum(attribute(tensordata)) + } + } +} +``` + +For some use cases where serving latency SLA is not strict and query throughput is low, the `paged` attribute setting might be a tuning alternative, as it allows storing more data per node. + +### Paged attributes disadvantages + +The disadvantages of using *paged* attributes are many: + +- Unpredictable query latency as attribute access might touch disk. Limited queries per second throughput per node (depends on the locality of document re-ranking requests). +- Paged attributes are implemented by file-backed memory mappings. The performance depends on the [Linux virtual memory management](https://tldp.org/LDP/tlk/mm/memory.html) ability to page data in and out. Using many threads per search/high query throughput might cause high system (kernel) CPU and system unresponsiveness. +- The content node's total memory utilization will be close to 100% when using paged attributes. It's up to the Linux kernel to determine what part of the attribute data is paged into memory based on access patterns. A good understanding of how the Linux virtual memory management system works is recommended before enabling paged attributes. +- The [memory usage metrics](/en/performance/sizing-search#metrics-for-vespa-sizing) from content nodes are not reflecting the reality when using paged attributes. They can indicate a usage that is much higher than the available memory on the node. This is because attribute memory usage is reported as the amount of data contained in the attribute, and whether this data is paged out to disk is controlled by the Linux kernel. +- Using paged attributes doubles the disk usage of attribute data. For example if the original attribute size is 92 GB (100M documents of the above 1024 int8 per document schema), using the `paged` setting will double the attribute disk usage to close to 200 GB. +- Changing the `paged` setting (e.g. removing the option) on a running system might cause hard out-of-memory situations as without `paged`, the content nodes will attempt loading the attribute into memory without the option for page outs. +- Using a paged attribute in [first-phase](/en/ranking/phased-ranking) ranking can result in extremely high query latency if a large amount of the corpus is retrieved by the query. The number of disk accesses will, in the worst case, be equal to the number of hits the query produces. A similar problem can occur if running a query that searches a paged attribute. +- Using `paged` in combination with [HNSW indexing](/en/querying/approximate-nn-hnsw) is *strongly* discouraged. *HNSW* indexing also searches and reads tensors during indexing, causing random access during feeding. Once the system memory usage reaches 100%, the Linux kernel will start paging pages in and out of memory. This can cause a high system (kernel) CPU and slows down HNSW indexing throughput significantly. + +## Mutable attributes + +[Mutable attributes](/en/reference/schemas/schemas#mutate) is document metadata for matching and ranking performance per document. + +The attribute values are mutated as part of query execution, as defined in rank profiles - see [rank phase statistics](/en/ranking/phased-ranking#rank-phase-statistics) for details. + +## Document meta store + +The document meta store is an in-memory data structure for all documents on a node. It is an *implicit attribute*, and is [compacted](/en/content/proton#lid-space-compaction) and [flushed](/en/content/proton#attribute-flush). Memory usage for applications with small documents / no other attributes can be dominated by this attribute. + +The document meta store scales linearly with number of documents - using approximately 30 bytes per document. The metric *content.proton.documentdb.ready.attribute.memory_usage.allocated_bytes* for `"field": "[documentmetastore]"` is the size of the document meta store in memory - use the [metric API](/en/reference/api/state-v1#state-v1-metrics) to find the size - in this example, the node has 9M ready documents with 52 bytes in memory per document: + +```json highlight= {10,14} +{ + "name": "content.proton.documentdb.ready.attribute.memory_usage.allocated_bytes", + "description": "The number of allocated bytes", + "values": { + "average": 4.69736008E8, + "count": 12, + "rate": 0.2, + "min": 469736008, + "max": 469736008, + "last": 469736008 + }, + "dimensions": { + "documenttype": "doctype", + "field": "[documentmetastore]" + } +}, +``` + +The above is for the *ready* documents, also check *removed* and *notready* - refer to [sub-databases](/en/content/proton#sub-databases). diff --git a/en/content/buckets.html b/en/content/buckets.html deleted file mode 100644 index ac08111231..0000000000 --- a/en/content/buckets.html +++ /dev/null @@ -1,248 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Buckets" ---- - -

    -The content layer splits the document space into chunks called buckets, -and algorithmically maps documents to buckets by their id. -The cluster automatically splits and joins buckets -to maintain a uniform distribution across all nodes -and to keep bucket sizes within configurable limits. -

    -Documents have string identifiers that maps to a 58 bit numeric location. -A bucket is defined as all the documents that shares a given amount -of the least significant bits within the location. -The amount of bits used controls how many buckets will exist. -For instance, if a bucket contains all documents whose 8 LSB bits is 0x01, -the bucket can be split in two by using the 9th bit in the location to split them. -Similarly, buckets can be joined by requiring one less bit in common. -

    - - - -

    Distribution

    -

    -Distribution happens in several layers. -

    -
      -
    • Documents map to 58 bit numeric locations.
    • -
    • Locations map to buckets
    • -
    • Buckets map to distributors responsible for handling requests related to those buckets.
    • -
    • Buckets map to content nodes responsible for storing replicas of buckets.
    • -
    - - -

    Document to location distribution

    -

    -Document identifiers use document identifier schemes -to map documents to locations. -This way it is possible to co-locate data within buckets -by enforcing some documents to have common LSB bits. -Specifying a group or numeric value with the n and g options -overrides the 32 LSB bits of the location. -Only use when required, e.g. when using streaming search for personal search. -

    - - -

    Location to bucket distribution

    -

    -The cluster state contains a distribution bit count, -which is the amount of location bits to use to generate buckets which can be mapped to distributors. -

    -The cluster state may change the number of distribution bits to adjust the -number of buckets distributed at this level. -When adding more nodes to the cluster, -the number of buckets increases in order for the distribution to remain uniform. -

    -Altering the distribution bit count causes a redistribution of all buckets. -

    -If locations have been overridden to co-localize documents into few units, -the distribution of documents into these buckets may be skewed. -

    - - -

    Bucket to distributor distribution

    -

    -Buckets are mapped to distributors using the ideal state algorithm. -

    - - -

    Bucket to content node distribution

    -

    -Buckets are mapped to content nodes using the ideal state algorithm. -As the content nodes persist data, -changing bucket ownership takes more time/resources than on the distributors. -

    -There is usually a replica of a bucket on the same content node -as the distributor owning the bucket, as the same algorithm is used. -

    -The distributors may split the buckets further than the distribution bit count indicates, -allowing more units to be distributed among the content nodes to create a more even distribution, -while not affecting routing from client to distributors. -

    - - - -

    Maintenance operations

    -

    -The content layer defines a set of maintenance operations to keep the cluster balanced. -Distributors schedule maintenance operations and issue them to content nodes. -Maintenance operations are typically not high priority requests. -Scheduling a maintenance operation does not block any external operations. -

    - - - - - - - - - - - - - - - - - -
    Split bucket - Split a bucket in two, by enforcing the documents within the new buckets - to have more location bits in common. - Buckets are split either because they have grown too big, - or because the cluster wants to use more distribution bits.
    Join bucket - Join two buckets into one. - If a bucket has been previously split due to being large, - but documents have now been deleted, the bucket can be joined again.
    Merge bucket - If there are multiple replicas of a bucket, but they do not store the same - set of versioned documents, merge is used to synchronize the replicas. - A special case of a merge is a one-way merge, which may be done if - some of the replicas are to be deleted right after the merge. Merging is used - not only to fix inconsistent bucket replicas, - but also to move buckets between nodes. - To move a bucket, an empty replica is created on the target node, - a merge is executed, and the source bucket is deleted. -
    Create bucket - This operation exist merely for the distributor to notify a content node - that it is now to store documents for this bucket too. This allows content - nodes to refuse operations towards buckets it does not own. The ability to - refuse traffic is a safeguard to avoid inconsistencies. If a client talks - to a distributor that is no longer working correctly, we rather want its - requests to fail than to alter the content cluster in strange ways.
    Delete bucket - Drop stored state for a bucket and reject further requests for it
    (De)activate bucket - Activate bucket for search results - - refer to bucket management
    Garbage collections - If configured, documents are periodically garbage collected through - background maintenance operations.
    - - -

    Bucket split size

    -

    -The distributors may split existing buckets further to keep bucket sizes at manageable levels, -or to ensure more units to split among the backends and their partitions. -

    -Using small buckets, the distribution will be more uniform and bucket operations will be smaller. -Using large buckets, less memory is needed for metadata operations -and bucket splitting and joining is less frequent. -

    -The size limits may be altered by configuring bucket splitting. -

    - - - -

    Document to bucket distribution

    -

    -Each document has a document identifier following a document identifier -uri scheme. -From this scheme a 58 bit numeric location is generated. -Typically, all the bits are created from an MD5 checksum of the whole identifier. -

    -Schemes specifying a groupname, will have the LSB bits of -the location set to a hash of the groupname. -Thus, all documents belonging to that group will have locations with similar least significant bits, -which will put them in the same bucket. -If buckets end up split far enough to use more bits than the hash bits overridden by the group, -the data will be split into many buckets, but each will typically only contain data for that group. -

    -MD5 checksums maps document identifiers to random locations. -This creates a uniform bucket distribution, and is default. -For some use cases, it is better to co-locate documents, -optimizing grouped access - an example is personal documents. -By enforcing some documents to map to similar locations, -these documents are likely to end up in the same actual buckets. -There are several use cases for where this may be useful: -

    -
      -
    • When migrating documents for some entity between clusters, this may be - implemented more efficient if the entity is contained in just a few - buckets rather than having documents scattered around all the existing - buckets.
    • -
    • If operations to the cluster is clustered somehow, clustering - the documents equally in the backend may make better use of caches. For - instance, if a service stores data for users, and traffic is typically - created for users at short intervals while the users are actively using - the service, clustering user data may allow a lot of the user traffic - to be easily cached by generic bucket caches.
    • -
    -

    -If the n= option is specified, the 32 LSB bits of the given -number overrides the 32 LSB bits of the location. -If the g= option is specified, a hash is created of the group name, -the hash value is then used as if it were specified with n=. -When the location is calculated, it is mapped to a bucket. -Clients map locations to buckets using -distribution bits. -

    -Distributors map locations to buckets by searching their bucket database, -which is sorted in inverse location order. -The common case is that there is one. -If there are several, there is currently inconsistent bucket splitting. -If there are none, the distributor will create a new bucket for -the request if it is a request that may create new data. -Typically, new buckets are generated split according to the distribution bit count. -

    -Content nodes should rarely need to map documents to buckets, as distributors -specify bucket targets for all requests. However, as external operations are -not queued during bucket splits and joins, the content nodes remap operations -to avoid having to fail them due to a bucket having recently been split or joined. -

    - - -

    Limitations

    -

    -One basic limitation to the document to location mapping is that it may never change. -If it changes, then documents will suddenly be in the wrong buckets in the cluster. -This would violate a core invariant in the system, and is not supported. -

    -To allow new functionality, -document identifier schemes may be extended or created that -maps to location in new ways, but the already existing ones must map -the same way as they have always done. -

    -Current document identifier schemes typically allow the 32 least significant -bits to be overridden for co-localization, while the remaining 26 bits are -reserved for bits created from the MD5 checksum. -

    - - -

    Splitting

    -

    -When there are enough documents co-localized to the same bucket, -causing the bucket to be split, it will typically need to split past the 32 LSB. -At this split-level and beyond, there is no longer a 1-1 relationship between the -node owning the bucket and the nodes its replica data will be stored on. -

    -The effect of this is that documents sharing a location will be spread across -nodes in the entire cluster once they reach a certain size. This enables efficient -parallel processing. -

    - - - -

    Bucket space

    -

    -Buckets exist in the default or global bucket space. - -

    diff --git a/en/content/buckets.mdx b/en/content/buckets.mdx new file mode 100644 index 0000000000..ee539d02ec --- /dev/null +++ b/en/content/buckets.mdx @@ -0,0 +1,127 @@ +--- +title: "Buckets" +description: "The content layer splits the document space into chunks called *buckets*, and algorithmically maps documents to buckets by their id. The cluster automatically splits and joins buckets to maintain a uniform distribution across all nodes and to keep bucket sizes within configurable limits." +--- + +Documents have string identifiers that maps to a 58 bit numeric location. A bucket is defined as all the documents that shares a given amount of the least significant bits within the location. The amount of bits used controls how many buckets will exist. For instance, if a bucket contains all documents whose 8 LSB bits is 0x01, the bucket can be split in two by using the 9th bit in the location to split them. Similarly, buckets can be joined by requiring one less bit in common. + +## Distribution + +Distribution happens in several layers. + + - Documents map to 58 bit numeric locations. + - Locations map to buckets + - Buckets map to distributors responsible for handling requests related to those buckets. + - Buckets map to content nodes responsible for storing replicas of buckets. + +### Document to location distribution + +Document identifiers use [document identifier schemes](/en/schemas/documents) to map documents to locations. This way it is possible to co-locate data within buckets by enforcing some documents to have common LSB bits. Specifying a group or numeric value with the n and g options overrides the 32 LSB bits of the location. Only use when required, e.g. when using streaming search for personal search. + +### Location to bucket distribution + +The cluster state contains a distribution bit count, which is the amount of location bits to use to generate buckets which can be mapped to distributors. + +The cluster state may change the number of distribution bits to adjust the number of buckets distributed at this level. When adding more nodes to the cluster, the number of buckets increases in order for the distribution to remain uniform. + +Altering the distribution bit count causes a redistribution of all buckets. + +If locations have been overridden to co-localize documents into few units, the distribution of documents into these buckets may be skewed. + +### Bucket to distributor distribution + +Buckets are mapped to distributors using the ideal state algorithm. + +### Bucket to content node distribution + +Buckets are mapped to content nodes using the ideal state algorithm. As the content nodes persist data, changing bucket ownership takes more time/resources than on the distributors. + +There is usually a replica of a bucket on the same content node as the distributor owning the bucket, as the same algorithm is used. + +The distributors may split the buckets further than the distribution bit count indicates, allowing more units to be distributed among the content nodes to create a more even distribution, while not affecting routing from client to distributors. + +## Maintenance operations + +The content layer defines a set of maintenance operations to keep the cluster balanced. Distributors schedule maintenance operations and issue them to content nodes. Maintenance operations are typically not high priority requests. Scheduling a maintenance operation does not block any external operations. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    **Split bucket**Split a bucket in two, by enforcing the documents within the new buckets to have more location bits in common. Buckets are split either because they have grown too big, or because the cluster wants to use more distribution bits.
    **Join bucket**Join two buckets into one. If a bucket has been previously split due to being large, but documents have now been deleted, the bucket can be joined again.
    **Merge bucket**If there are multiple replicas of a bucket, but they do not store the same set of versioned documents, _merge_ is used to synchronize the replicas. A special case of a merge is a one-way merge, which may be done if some of the replicas are to be deleted right after the merge. Merging is used not only to fix inconsistent bucket replicas, but also to move buckets between nodes. To move a bucket, an empty replica is created on the target node, a merge is executed, and the source bucket is deleted.
    **Create bucket**This operation exist merely for the distributor to notify a content node that it is now to store documents for this bucket too. This allows content nodes to refuse operations towards buckets it does not own. The ability to refuse traffic is a safeguard to avoid inconsistencies. If a client talks to a distributor that is no longer working correctly, we rather want its requests to fail than to alter the content cluster in strange ways.
    **Delete bucket**Drop stored state for a bucket and reject further requests for it
    **(De)activate bucket**Activate bucket for search results - refer to bucket management
    **Garbage collections**If configured, documents are periodically garbage collected through background maintenance operations.
    + +### Bucket split size + +The distributors may split existing buckets further to keep bucket sizes at manageable levels, or to ensure more units to split among the backends and their partitions. + +Using small buckets, the distribution will be more uniform and bucket operations will be smaller. Using large buckets, less memory is needed for metadata operations and bucket splitting and joining is less frequent. + +The size limits may be altered by configuring [bucket splitting](/en/reference/applications/services/content#bucket-splitting). + +## Document to bucket distribution + +Each document has a document identifier following a document identifier [uri scheme](/en/schemas/documents). From this scheme a 58 bit numeric _location_ is generated. Typically, all the bits are created from an MD5 checksum of the whole identifier. + +Schemes specifying a _groupname_, will have the LSB bits of the location set to a hash of the _groupname_. Thus, all documents belonging to that group will have locations with similar least significant bits, which will put them in the same bucket. If buckets end up split far enough to use more bits than the hash bits overridden by the group, the data will be split into many buckets, but each will typically only contain data for that group. + +MD5 checksums maps document identifiers to random locations. This creates a uniform bucket distribution, and is default. For some use cases, it is better to co-locate documents, optimizing grouped access - an example is personal documents. By enforcing some documents to map to similar locations, these documents are likely to end up in the same actual buckets. There are several use cases for where this may be useful: + +- When migrating documents for some entity between clusters, this may be implemented more efficient if the entity is contained in just a few buckets rather than having documents scattered around all the existing buckets. +- If operations to the cluster is clustered somehow, clustering the documents equally in the backend may make better use of caches. For instance, if a service stores data for users, and traffic is typically created for users at short intervals while the users are actively using the service, clustering user data may allow a lot of the user traffic to be easily cached by generic bucket caches. + +If the `n=` option is specified, the 32 LSB bits of the given number overrides the 32 LSB bits of the location. If the `g=` option is specified, a hash is created of the group name, the hash value is then used as if it were specified with `n=`. When the location is calculated, it is mapped to a bucket. Clients map locations to buckets using [distribution bits](#location-to-bucket-distribution). + +Distributors map locations to buckets by searching their bucket database, which is sorted in inverse location order. The common case is that there is one. If there are several, there is currently inconsistent bucket splitting. If there are none, the distributor will create a new bucket for the request if it is a request that may create new data. Typically, new buckets are generated split according to the distribution bit count. + +Content nodes should rarely need to map documents to buckets, as distributors specify bucket targets for all requests. However, as external operations are not queued during bucket splits and joins, the content nodes remap operations to avoid having to fail them due to a bucket having recently been split or joined. + +### Limitations + +One basic limitation to the document to location mapping is that it may never change. If it changes, then documents will suddenly be in the wrong buckets in the cluster. This would violate a core invariant in the system, and is not supported. + +To allow new functionality, document identifier schemes may be extended or created that maps to location in new ways, but the already existing ones must map the same way as they have always done. + +Current document identifier schemes typically allow the 32 least significant bits to be overridden for co-localization, while the remaining 26 bits are reserved for bits created from the MD5 checksum. + +### Splitting + +When there are enough documents co-localized to the same bucket, causing the bucket to be split, it will typically need to split past the 32 LSB. At this split-level and beyond, there is no longer a 1-1 relationship between the node owning the bucket and the nodes its replica data will be stored on. + +The effect of this is that documents sharing a location will be spread across nodes in the entire cluster once they reach a certain size. This enables efficient parallel processing. + +## Bucket space + +Buckets exist in the _default_ or _global_ bucket space. \ No newline at end of file diff --git a/en/content/consistency.md b/en/content/consistency.md deleted file mode 100644 index 3c8030d90d..0000000000 --- a/en/content/consistency.md +++ /dev/null @@ -1,210 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Vespa Consistency Model" ---- - -Vespa offers configurable data redundancy with eventual consistency across replicas. -It's designed for high efficiency under workloads where eventual consistency is an -acceptable tradeoff. This document aims to go into some detail on what these tradeoffs -are, and what you, as a user, can expect. - - - -### Vespa and CAP - -Vespa may be considered a limited subset of AP under the [CAP theorem](https://en.wikipedia.org/wiki/CAP_theorem). - -Under CAP, there is a fundamental limitation of whether any distributed system can offer -guarantees on consistency (C) or availability (A) in scenarios where nodes are partitioned (P) -from each other. Since there is no escaping that partitions can and will happen, we talk -either of systems that are _either_ CP or AP. - -Consistency (C) in CAP implies that reads and writes are strongly consistent, i.e. the system offers -_linearizability_. -Weaker forms such as causal consistency or "read your writes" consistency is _not_ sufficient. -As mentioned initially, Vespa is an eventually consistent data store and therefore does not offer -this property. In practice, Consistency requires the use of a majority consensus algorithm, which -Vespa does not currently use. - -Availability (A) in CAP implies that _all requests_ receive a non-error response regardless of how -the network may be partitioned. Vespa is dependent on a centralized (but fault tolerant) node health -checker and coordinator. A network partition may take place between the coordinator and a subset of nodes. -Operations to nodes in this subset aren't guaranteed to succeed until the partition heals. -As a consequence, Vespa is not _guaranteed_ to be strongly available, -so we treat this as a "limited subset" of AP (though this is not technically part of the CAP definition). - -In _practice_, the best-effort semantics of Vespa have proven to be both robust and -highly available in common datacenter networks. - -### Write durability and consistency - -When a client receives a successful [write](../writing/reads-and-writes.html) response, -the operation has been written and synced to disk. The replication level is configurable. -Operations are by default written on _all_ available replica nodes before sending a response. -"Available" here means being Up in the [cluster state](content-nodes.html#cluster-state), -which is determined by the fault-tolerant, centralized Cluster Controller service. -If a cluster has a total of 3 nodes, 2 of these are available and the replication factor -is 3, writes will be ACKed to the client if both the available nodes ACK the operation. - -On each replica node, operations are persisted to a write-ahead log before -being applied. The system will automatically recover after a crash by replaying -logged operations. Writes are guaranteed to be synced to durable storage prior -to sending a successful response to the client, so acknowledged writes are retained even -in the face of sudden power loss. - -If a client receives a failure response for a write operation, the operation may or may not have taken -place on a subset of the replicas. If not all replicas could be written to, -they are considered divergent (out of sync). The system detects and reconciles -divergent replicas. This happens without any required user intervention. - -Each document write assigns a new wall-clock timestamp to the resulting document -version. As a consequence, configure servers with NTP to keep clock drift as small -as possible. Large clock drifts may result in timestamp collisions or unexpected -operation orderings. - -Vespa has support for conditional writes for individual documents through -test-and-set operations. Multi-document transactions are not supported. - -After a successful response, changes to the search indexes are immediately -visible by default. - -### Read consistency - -Reads are consistent on a best-effort basis and are not guaranteed to be linearizable. - -When using a [Get](../reference/api/document-v1.html#get) or [Visit](../writing/visiting.html) operation, -the client will never observe a partially updated document. -For these read operations, writes behave as if they are atomic. - -Searches may observe partial updates, as updates are not atomic across index -structures. This can only happen _after_ a write has started, but _before_ it's -complete. Once a write is complete, all index updates are visible. - -Searches may observe transient loss of coverage when nodes go down. Vespa will -restore coverage automatically when this happens. How fast this happens depends -on the configured [searchable-copies](../reference/applications/services/content.html#searchable-copies) value. - -If replicas diverge during a Get, Vespa performs a read-repair. This fetches the -requested document from all divergent replicas. The client then receives the -version with the newest timestamp. - -If replicas diverge during a Visit, the behavior is slightly different between -the Document V1 API and [vespa-visit](/en/reference/operations/self-managed/tools.html#vespa-visit): - - * Document V1 will prefer immediately visiting the replica that contains the - most documents. This means it's possible for a subset of documents in a bucket - to not be returned. - * `vespa-visit` will by default retry visiting the bucket until it is in sync. - This may take a long time if large parts of the system are out of sync. - -The rationale for this difference in behavior is that Document V1 is usually -called in a real-time request context, whereas `vespa-visit` is usually called -in a background/batch processing context. - -Visitor operations iterate over the document corpus in an implementation-specific -order. Any given document is returned in the state it was in at the time the visitor -iterated over the data bucket containing the document. This means there is no -snapshot isolation—a document mutation happening concurrently with a visitor -may or may not be reflected in the returned document set, depending on whether -the mutation happened before or after iteration of the bucket containing the document. - -### Replica reconciliation - -Reconciliation is the act of bringing divergent replicas back into sync. This -usually happens after a node restarts or fails. It will also happen after -network partitions. - -Unlike several other eventually consistent databases, Vespa doesn't use -distributed replica operation logs. Instead, reconciling replicas involves -exchanging sets of timestamped documents. Reconciliation is complete once -the union set of documents is present on all replicas. Metadata is checksummed -to determine whether replicas are in sync with each other. - -When reconciling replicas, the newest available version of a document will -"win" and become visible. This version may be a remove (tombstone). Tombstones -are replicated in the same way as regular documents. - -Reconciliation happens the document level, not at the field level. I.e. there -is no merging of individual fields across different versions. - -If a test-and-set operation updates at least one replica, it will eventually -become visible on the other replicas. - -The reconciliation operation is referred to as a "merge" in the rest of the Vespa -documentation. - -Tombstone entries have a configurable time-to-live before they are compacted away. -Nodes that have been partitioned away from the network for a longer period of time -than this TTL should ideally have their indexes removed before being allowed back into the cluster. -If not, there is a risk of resurrecting previously removed documents. -Vespa does not currently detect or handle this scenario automatically. - -See the documentation on [data-retention-vs-size](/en/operations/self-managed/admin-procedures.html#data-retention-vs-size). - -### Q/A - -#### How does Vespa perform read-repair for Get-operations, and how many replicas are consulted? - -When the distributor process that is responsible for a particular data bucket receives -a Get operation, it checks its locally cached replica metadata state for inconsistencies. - -If all replicas have consistent metadata, the operation is routed to a single replica—preferably -located on the same host as the distributor, if present. This is the normal case when -the bucket replicas are in sync. - -If there is at least one replica metadata mismatch, the distributor automatically initiates -a read-repair process: - - 1. The distributor splits the bucket replicas into subsets based on their metadata, - where all replicas in each subset have the same metadata. It then sends a lightweight - metadata-only Get to one replica in each subset. The core assumption is that all - these replicas have the same set of document versions, and that it suffices to - consult one replica in the set. If a metadata read fails, the distributor will - automatically fail over to another replica in the subset. - 2. It then sends one full Get to a node in the replica set that returned the _highest_ - timestamp. - -This means that if you have 100 replicas and 1 has different metadata from the remaining -99, only 2 nodes in total will be initially queried, and only 1 will receive the actual -(full) Get read. - -Similar algorithms are used by other operations that may trigger read/write-repair. - -#### Since Vespa performs read-repair when inconsistencies are detected, does this mean replies are strongly consistent? - -Unfortunately not. Vespa does not offer any cross-document transactions, so in -this case strong consistency implies single-object _linearizability_ (as opposed to -_strict serializability_ across multiple objects). Linearizability requires the ability -to reach a majority consensus amongst a particular known and stable configuration of -replicas (side note: replica sets can be reconfigured in strongly consistent algorithms -like Raft and Paxos, but such a reconfiguration must also be threaded through the -consensus machinery). - -The active replica set for a given data bucket (and thus the documents it logically -contains) is ephemeral and dynamic based on the nodes that are currently available in -the cluster (as seen from the cluster controller). This precludes having a stable set -of replicas that can be used for reaching majority decisions. - -See also [Vespa and CAP](#vespa-and-cap). - -#### In what kind of scenario might Vespa return a stale version of a document? - -Stale document versions may be returned when all replicas containing the most recent -document version have become unavailable. - -Example scenario (for simplicity—but without loss of generality—assuming redundancy 1) in -a cluster with two nodes {A, B}: - - 1. Document X is stored in a replica on node A with timestamp 100. - 2. Node A goes down; node B takes over ownership. - 3. A write request is received for document X; it is stored on node B with timestamp - 200 and ACKed to the client. - 4. Node B goes down. - 5. Node A comes back up. - 6. A read request arrives for document X. The only visible replica is on node A, which - ends up serving the request. - 7. The document version at timestamp 100 is returned to the client. - -Since the write at `t=200` _happens-after_ the write at `t=100`, returning the version at -`t=100` violates linearizability. - diff --git a/en/content/consistency.mdx b/en/content/consistency.mdx new file mode 100644 index 0000000000..b6af387670 --- /dev/null +++ b/en/content/consistency.mdx @@ -0,0 +1,113 @@ +--- +title: "Vesa Consistency Model" +sidebarTitle: "Consistency Model" +description: "Vespa offers configurable data redundancy with eventual consistency across replicas. It's designed for high efficiency under workloads where eventual consistency is an acceptable tradeoff. This document aims to go into some detail on what these tradeoffs are, and what you, as a user, can expect." +--- + +## Vespa and CAP + +Vespa may be considered a limited subset of AP under the [CAP theorem](https://en.wikipedia.org/wiki/CAP_theorem). + +Under CAP, there is a fundamental limitation of whether any distributed system can offer guarantees on consistency (C) or availability (A) in scenarios where nodes are partitioned (P) from each other. Since there is no escaping that partitions can and will happen, we talk either of systems that are _either_ CP or AP. + +Consistency (C) in CAP implies that reads and writes are strongly consistent, i.e. the system offers _linearizability_. Weaker forms such as causal consistency or "read your writes" consistency is _not_ sufficient. As mentioned initially, Vespa is an eventually consistent data store and therefore does not offer this property. In practice, Consistency requires the use of a majority consensus algorithm, which Vespa does not currently use. + +Availability (A) in CAP implies that _all requests_ receive a non-error response regardless of how the network may be partitioned. Vespa is dependent on a centralized (but fault tolerant) node health checker and coordinator. A network partition may take place between the coordinator and a subset of nodes. Operations to nodes in this subset aren't guaranteed to succeed until the partition heals. As a consequence, Vespa is not _guaranteed_ to be strongly available, so we treat this as a "limited subset" of AP (though this is not technically part of the CAP definition). + +In _practice_, the best-effort semantics of Vespa have proven to be both robust and highly available in common datacenter networks. + +## Write durability and consistency + +When a client receives a successful [write](/en/writing/reads-and-writes) response, the operation has been written and synced to disk. The replication level is configurable. Operations are by default written on _all_ available replica nodes before sending a response. "Available" here means being Up in the [cluster state](/en/content/content-nodes#cluster-state), which is determined by the fault-tolerant, centralized Cluster Controller service. If a cluster has a total of 3 nodes, 2 of these are available and the replication factor is 3, writes will be ACKed to the client if both the available nodes ACK the operation. + +On each replica node, operations are persisted to a write-ahead log before being applied. The system will automatically recover after a crash by replaying logged operations. Writes are guaranteed to be synced to durable storage prior to sending a successful response to the client, so acknowledged writes are retained even in the face of sudden power loss. + +If a client receives a failure response for a write operation, the operation may or may not have taken place on a subset of the replicas. If not all replicas could be written to, they are considered divergent (out of sync). The system detects and reconciles divergent replicas. This happens without any required user intervention. + +Each document write assigns a new wall-clock timestamp to the resulting document version. As a consequence, configure servers with NTP to keep clock drift as small as possible. Large clock drifts may result in timestamp collisions or unexpected operation orderings. + +Vespa has support for conditional writes for individual documents through test-and-set operations. Multi-document transactions are not supported. + +After a successful response, changes to the search indexes are immediately visible by default. + +## Read consistency + +Reads are consistent on a best-effort basis and are not guaranteed to be linearizable. + +When using a [Get](/en/reference/api/document-v1#get) or [Visit](/en/writing/visiting) operation, the client will never observe a partially updated document. For these read operations, writes behave as if they are atomic. + +Searches may observe partial updates, as updates are not atomic across index structures. This can only happen _after_ a write has started, but _before_ it's complete. Once a write is complete, all index updates are visible. + +Searches may observe transient loss of coverage when nodes go down. Vespa will restore coverage automatically when this happens. How fast this happens depends on the configured [searchable-copies](/en/reference/applications/services/content#searchable-copies) value. + +If replicas diverge during a Get, Vespa performs a read-repair. This fetches the requested document from all divergent replicas. The client then receives the version with the newest timestamp. + +If replicas diverge during a Visit, the behavior is slightly different between the Document V1 API and [vespa-visit](/en/reference/operations/self-managed/tools#vespa-visit): + +- Document V1 will prefer immediately visiting the replica that contains the most documents. This means it's possible for a subset of documents in a bucket to not be returned. +- `vespa-visit` will by default retry visiting the bucket until it is in sync. This may take a long time if large parts of the system are out of sync. + +The rationale for this difference in behavior is that Document V1 is usually called in a real-time request context, whereas `vespa-visit` is usually called in a background/batch processing context. + +Visitor operations iterate over the document corpus in an implementation-specific order. Any given document is returned in the state it was in at the time the visitor iterated over the data bucket containing the document. This means there is no snapshot isolation—a document mutation happening concurrently with a visitor may or may not be reflected in the returned document set, depending on whether the mutation happened before or after iteration of the bucket containing the document. + +## Replica reconciliation + +Reconciliation is the act of bringing divergent replicas back into sync. This usually happens after a node restarts or fails. It will also happen after network partitions. + +Unlike several other eventually consistent databases, Vespa doesn't use distributed replica operation logs. Instead, reconciling replicas involves exchanging sets of timestamped documents. Reconciliation is complete once the union set of documents is present on all replicas. Metadata is checksummed to determine whether replicas are in sync with each other. + +When reconciling replicas, the newest available version of a document will "win" and become visible. This version may be a remove (tombstone). Tombstones are replicated in the same way as regular documents. + +Reconciliation happens the document level, not at the field level. I.e. there is no merging of individual fields across different versions. + +If a test-and-set operation updates at least one replica, it will eventually become visible on the other replicas. + +The reconciliation operation is referred to as a "merge" in the rest of the Vespa documentation. + +Tombstone entries have a configurable time-to-live before they are compacted away. Nodes that have been partitioned away from the network for a longer period of time than this TTL should ideally have their indexes removed before being allowed back into the cluster. If not, there is a risk of resurrecting previously removed documents. Vespa does not currently detect or handle this scenario automatically. + +See the documentation on [data-retention-vs-size](/en/operations/self-managed/admin-procedures#data-retention-vs-size). + +## Q/A + + + +When the distributor process that is responsible for a particular data bucket receives a Get operation, it checks its locally cached replica metadata state for inconsistencies. + +If all replicas have consistent metadata, the operation is routed to a single replica—preferably located on the same host as the distributor, if present. This is the normal case when the bucket replicas are in sync. + +If there is at least one replica metadata mismatch, the distributor automatically initiates a read-repair process: + +1. The distributor splits the bucket replicas into subsets based on their metadata, where all replicas in each subset have the same metadata. It then sends a lightweight metadata-only Get to one replica in each subset. The core assumption is that all these replicas have the same set of document versions, and that it suffices to consult one replica in the set. If a metadata read fails, the distributor will automatically fail over to another replica in the subset. +2. It then sends one full Get to a node in the replica set that returned the _highest_ timestamp. + +This means that if you have 100 replicas and 1 has different metadata from the remaining 99, only 2 nodes in total will be initially queried, and only 1 will receive the actual (full) Get read. + +Similar algorithms are used by other operations that may trigger read/write-repair. + + + +Unfortunately not. Vespa does not offer any cross-document transactions, so in this case strong consistency implies single-object _linearizability_ (as opposed to _strict serializability_ across multiple objects). Linearizability requires the ability to reach a majority consensus amongst a particular known and stable configuration of replicas (side note: replica sets can be reconfigured in strongly consistent algorithms like Raft and Paxos, but such a reconfiguration must also be threaded through the consensus machinery). + +The active replica set for a given data bucket (and thus the documents it logically contains) is ephemeral and dynamic based on the nodes that are currently available in the cluster (as seen from the cluster controller). This precludes having a stable set of replicas that can be used for reaching majority decisions. + +See also [Vespa and CAP](#vespa-and-cap). + + + +Stale document versions may be returned when all replicas containing the most recent document version have become unavailable. + +Example scenario (for simplicity—but without loss of generality—assuming redundancy 1) in a cluster with two nodes `{A, B}`: + +1. Document X is stored in a replica on node A with timestamp 100. +2. Node A goes down; node B takes over ownership. +3. A write request is received for document X; it is stored on node B with timestamp 200 and ACKed to the client. +4. Node B goes down. +5. Node A comes back up. +6. A read request arrives for document X. The only visible replica is on node A, which ends up serving the request. +7. The document version at timestamp 100 is returned to the client. + +Since the write at `t=200` _happens-after_ the write at `t=100`, returning the version at `t=100` violates linearizability. + + \ No newline at end of file diff --git a/en/content/content-nodes.html b/en/content/content-nodes.html deleted file mode 100644 index c371b9251e..0000000000 --- a/en/content/content-nodes.html +++ /dev/null @@ -1,586 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Content nodes, states and metrics" ---- - -Content cluster overview -

    - Content cluster processes are distributor, proton and cluster controller. -

    -

    - The distributor calculates the correct content node using - the distribution algorithm and the cluster state. - With no known cluster state, the client library will send requests to a random node, - which replies with the updated cluster state if the node was incorrect. - Cluster states are versioned, such that clients hitting outdated distributors do not override - updated states with old states. -

    -

    - The distributor keeps track of - which content nodes that stores replicas of each bucket (maximum one replica each), - based on redundancy - and information from the cluster controller. - A bucket maps to one distributor only. - A distributor keeps a bucket database with bucket metadata. - The metadata holds which content nodes store replicas of the buckets, - the checksum of the bucket content and the number of documents and meta entries within the bucket. - Each document is algorithmically mapped to a bucket and forwarded to the correct content nodes. - The distributors detect whether there are enough bucket replicas on the - content nodes and add/remove as needed. - Write operations wait for replies from every replica - and fail if less than redundancy are persisted within timeout. -

    -

    - The cluster controller - manages the state of the distributor and content nodes. - This cluster state is used by the document processing chains - to know which distributor to send documents to, - as well as by the distributor to know which content nodes should have which bucket. -

    - - - -

    Cluster state

    -

    -There are three kinds of state: unit state, - user state and - generated state (a.k.a. cluster state). -

    -

    - For new cluster states, the cluster state version is incremented, - and the new cluster state is broadcast to all nodes. - There is a minimum time between each cluster state change. - -

    -

    - It is possible to set a minimum capacity for the cluster state to be up. - - If a cluster has so many nodes unavailable that it is considered down, - the state of each node is irrelevant, - and thus new cluster states will not be created and broadcast - before enough nodes are back for the cluster to come back up. - A cluster state indicating the entire cluster is down, - may thus have outdated data on the node level. -

    - - - - -

    Cluster controller

    -

    - The main task of the cluster controller is to maintain the cluster state. - This is done by polling nodes for state, - generating a cluster state, - which is then broadcast to all the content nodes in the cluster. - Note that clients do not interface with the cluster controller - - they get the cluster state from the distributors - details. -

    - - - - - - - - - - - - - - - - - - - -
    TaskDescription
    Node state polling -

    - The cluster controller polls nodes, sending the current cluster state. - If the cluster state is no longer correct, the node returns correct information immediately. - If the state is correct, the request lingers on the node, - such that the node can reply to it immediately if its state changes. - After a while, the cluster controller will send a new state request to the node, - even with one pending. - This triggers a reply to the lingering request and makes the new one linger instead. - Hence, nodes have a pending state request. -

    -

    - During a controlled node shutdown, it starts the shutdown process - by responding to the pending state request that it is now stopping. - {% include note.html content='As controlled restarts or shutdowns are implemented as TERM signals - from the config-sentinel, - the cluster controller is not able to differ between controlled and other shutdowns.' %} -

    -
    Cluster state generation -

    - The cluster controller translates unit and user states - into the generated cluster state -

    -
    Cluster state broadcast -

    - When node unit states are received, a cluster controller internal cluster state is updated. - New cluster states are distributed with a minimum interval between. - A grace period per unit state too - - e.g., distributors and content nodes that are on the same node often stop at the same time. -

    -

    - The version number is incremented, and the new cluster state is broadcast. -

    -

    - If cluster state version is reset, - distributors and content node processes may have to be restarted in order for the system - to converge to the new state. Nodes will reject lower cluster state versions to - prevent race conditions caused by overlapping cluster controller leadership periods. -

    -
    -

    - See - cluster controller configuration. -

    - - -

    Master election

    -

    -Vespa can be configured with one cluster controller. -Reads and writes will work well in case of cluster controller down, -but other changes to the cluster (like a content node going down) will not be handled. -It is hence recommended to configure a set of cluster controllers. -

    -The cluster controller nodes elect a master, -which does the node polling and cluster state broadcast. -The other cluster controller nodes only exist to do master election -and potentially take over if the master dies. -

    -All cluster controllers will vote for the cluster controller with the lowest index that says it is ready. -If a cluster controller has more than half of the votes, it will be elected master. -As a majority vote is required, -the number of cluster controllers should be an odd number of 3 or greater. -A fresh master will not broadcast states before a transition time is passed, -allowing an old master to have some time to realize it is no longer the master. -

    - - - -

    Distributor

    -

    -Buckets are mapped to distributors using the ideal state algorithm. -As the cluster state changes, buckets are re-mapped immediately. -The mapping does not overlap - -a bucket is owned by one distributor. -

    -Distributors do not persist the bucket database, -the bucket-to-content-node mapping is kept in memory in the distributor. -Document count, persisted size and a metadata checksum per bucket is stored as well. -At distributor (re)start, content nodes are polled for bucket information, -and return which buckets are owned by this distributor (using the ideal state algorithm). -There is no centralized bucket directory node. -Likewise, at any distributor cluster state change, -content nodes are polled for bucket handover - -a distributor will then handle a new set of buckets. -

    -Document operations are mapped to content nodes based on bucket locations - -each put/update/get/remove is mapped to a bucket -and sent to the right content nodes. -To manage the document set as it grows and nodes change, buckets move between content nodes. -

    -Document API clients (i.e. container nodes with -<document-api>) -do not communicate directly with the cluster controller, and do not know the cluster state at startup. -Clients therefore start out by sending requests to a random distributor. -If the document operation hits the wrong distributor, -WRONG_DISTRIBUTION is returned, with the current cluster state in the response. -WRONG_DISTRIBUTION is hence expected and normal at cold start / state change events. -

    - - -

    Timestamps

    -

    -Write operations -have a last modified time timestamp assigned when passing through the distributor. -The timestamp is guaranteed to be unique within the -bucket where it is stored. -The timestamp is used by the content layer to decide which operation is newest. -These timestamps can be used when visiting, -to process/retrieve documents within a given time range. -To guarantee unique timestamps, they are in microseconds - -the microsecond part is generated to avoid conflicts with other documents. -

    -If documents are migrated between clusters, -the target cluster will have new timestamps for their entries. -Also, when reprocessing documents within a cluster, -documents will have new timestamps, even if not modified. -

    - - -

    Ordering

    -

    -The Document API uses the document ID to order operations. -A Document API client ensures that only one operation is pending at the same time. -This ensures that if a client sends multiple operations for the same document, -they will be processed in a defined order. This is done by queueing pending -operations locally at the client. -

    -{% include note.html content='If sending two write operations to the same document, -and the first operation fails, the enqueued operation is sent. In other words, the -client does not assume there exists any kind of dependency between separate -operations to the same document. If you need to enforce this, use -test-and-set conditions -for writes.' %} -

    -If different clients have pending operations on the same document, -the order is unspecified. -

    - - -

    Maintenance operations

    -

    -Distributors track which content nodes have which buckets in their bucket database. -Distributors then use the ideal state algorithm -to generate bucket maintenance operations. -A stable system has all buckets located per the ideal state: -

    -
      -
    • If buckets have too few replicas, new are generated on other content nodes.
    • -
    • If the replicas differ, a bucket merge is issued to get replicas consistent.
    • -
    • If a buckets has too many replicas, superfluous are deleted. - Buckets are merged, if inconsistent, before deletion.
    • -
    • If two buckets exist, such that both may contain the same document, - the buckets are split or joined to remove such overlapping buckets. - Read more on inconsistent buckets.
    • -
    • If buckets are too small/large, they will be joined or split.
    • -
    -

    -The maintenance operations have different priorities. -If no maintenance operations are needed, the cluster is said to be in the ideal state. -The distributors synchronize maintenance load with user load, -e.g. to remap requests to other buckets after bucket splitting and joining. -

    - - -

    Restart

    -

    - When a distributor stops, it will try to respond to any pending cluster state request first. - New incoming requests after shutdown is commenced will fail immediately, - as the socket is no longer accepting requests. - Cluster controllers will thus detect processes stopping almost immediately. -

    -

    - The cluster state will be updated with the new state internally in the cluster controller. - Then the cluster controller will wait for maximum - - min_time_between_new_systemstates - before publishing the new cluster state - this to reduce short-term state fluctuations. -

    -

    - The cluster controller has the option of setting states to make other - distributors take over ownership of buckets, or mask the change, making the - buckets owned by the distributor restarting unavailable for the time being. -

    -

    - If the distributor transitions from up to down, - other distributors will request metadata from the content nodes to take - over ownership of buckets previously owned by the restarting distributor. - Until the distributors have gathered this new metadata from all the content - nodes, requests for these buckets can not be served, and will fail back to client. - When the restarting node comes back up and is marked up in the cluster state again, - the additional nodes will discard knowledge of the extra buckets they previously acquired. -

    -

    - For requests with timeouts of several seconds, - the transition should be invisible due to automatic client resending. - Requests with a lower timeout might fail, - and it is up to the application whether to resend or handle failed requests. -

    -

    - Requests to buckets not owned by the restarting distributor will not be affected. -

    - - - -

    Content node

    -

    - The content node runs proton, which is the query backend. -

    - - - -

    Restart

    -

    - When a content node does a controlled restart, - it marks itself in the stopping state and rejects new requests. - It will process its pending request queue before shutting down. - Consequently, client requests are typically unaffected by content node restarts. - The currently pending requests will typically be completed. - New copies of buckets will be created on other nodes, to store new requests in appropriate redundancy. - This happens whether node transitions through down or maintenance state. - The difference being that if transitioning through maintenance, - the distributor will not start any effort of synchronizing new copies with existing copies. - They will just store the new requests until the maintenance node comes back up. -

    -

    - When starting, content nodes will start with gathering information - on what buckets it has data stored for. - While this is happening, the service layer will expose that it is down. -

    - - - -

    Metrics

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    MetricDescription
    .idealstate.idealstate_diff - This metric tries to create a single value indicating distance to the ideal state. - A value of zero indicates that the cluster is in the ideal state. - Graphed values of this metric gives a good indication for how - fast the cluster gets back to the ideal state after changes. - Note that some issues may hide other issues, so sometimes the graph - may appear to stand still or even go a bit up again, as resolving one - issue may have detected one or several others.
    .idealstate.buckets_toofewcopies - Specifically lists how many buckets have too few copies. - Compare to the buckets metric to see how big a portion of the cluster this is.
    .idealstate.buckets_toomanycopies - Specifically lists how many buckets have too many copies. - Compare to the buckets metric to see how big a portion of the cluster this is.
    .idealstate.buckets - The total number of buckets managed. Used by other metrics reporting - bucket counts to know how big a part of the cluster they relate to.
    .idealstate.buckets_notrusted - Lists how many buckets have no trusted copies. - Without trusted buckets operations against the bucket may have poor performance, - having to send requests to many copies to try and create consistent replies.
    .idealstate.delete_bucket.pending - Lists how many buckets that needs to be deleted.
    .idealstate.merge_bucket.pending - Lists how many buckets there are, - where we suspect not all copies store identical document sets.
    .idealstate.split_bucket.pending - Lists how many buckets are currently being split.
    .idealstate.join_bucket.pending - Lists how many buckets are currently being joined.
    .idealstate.set_bucket_state.pending - Lists how many buckets are currently altered for active state. - These are high priority requests which should finish fast, - so these requests should seldom be seen as pending.
    -

    - Example, using the quickstart - - find the distributor port (look for HTTP): -

    -
    -$ docker exec vespa vespa-model-inspect service distributor
    -
    -distributor @ vespa-container : content
    -music/distributor/0
    -    tcp/vespa-container:19112 (MESSAGING)
    -    tcp/vespa-container:19113 (STATUS RPC)
    -    tcp/vespa-container:19114 (STATE STATUS HTTP)
    -
    -

    - Get the metric value: -

    -
    -$ docker exec vespa curl -s http://localhost:19114/state/v1/metrics | jq . | \
    -  grep -A 10 idealstate.merge_bucket.pending
    -
    -        "name": "vds.idealstate.merge_bucket.pending",
    -        "description": "The number of operations pending",
    -        "values": {
    -          "average": 0,
    -          "sum": 0,
    -          "count": 1,
    -          "rate": 0.016666,
    -          "min": 0,
    -          "max": 0,
    -          "last": 0
    -        },
    -
    - - - -

    /cluster/v2 API examples

    -

    - Examples of state manipulation using the /cluster/v2 API. -

    -

    - List content clusters: -

    -
    $ curl http://localhost:19050/cluster/v2/
    -
    {% highlight json %}
    -{
    -    "cluster": {
    -        "music": {
    -            "link": "/cluster/v2/music"
    -        },
    -        "books": {
    -            "link": "/cluster/v2/books"
    -        }
    -    }
    -}
    -{% endhighlight %}
    -

    - Get cluster state and list service types within cluster: -

    -
    $ curl http://localhost:19050/cluster/v2/music
    -
    {% highlight json %}
    -{
    -    "state": {
    -        "generated": {
    -            "state": "state-generated",
    -            "reason": "description"
    -        }
    -    }
    -    "service": {
    -        "distributor": {
    -            "link": "/cluster/v2/music/distributor"
    -        },
    -        "storage": {
    -            "link": "/cluster/v2/music/storage"
    -        }
    -    }
    - }
    -{% endhighlight %}
    -

    - List nodes per service type for cluster: -

    -
    $ curl http://localhost:19050/cluster/v2/music/storage
    -
    {% highlight json %}
    -{
    -    "node": {
    -        "0": {
    -            "link": "/cluster/v2/music/storage/0"
    -        },
    -        "1": {
    -            "link": "/cluster/v2/music/storage/1"
    -        }
    -    }
    -}
    -{% endhighlight %}
    -

    - Get node state: -

    -
    -$ curl http://localhost:19050/cluster/v2/music/storage/0
    -
    -
    {% highlight json %}
    -{
    -    "attributes": {
    -        "hierarchical-group": "group0"
    -    },
    -    "state": {
    -        "generated": {
    -            "state": "up",
    -            "reason": ""
    -        },
    -        "unit": {
    -            "state": "up",
    -            "reason": ""
    -        },
    -        "user": {
    -            "state": "up",
    -            "reason": ""
    -        }
    -    },
    -    "metrics": {
    -        "bucket-count": 0,
    -        "unique-document-count": 0,
    -        "unique-document-total-size": 0
    -    }
    -}
    -{% endhighlight %}
    -

    - Get all nodes, including topology information (see hierarchical-group): -

    -
    -$ curl http://localhost:19050/cluster/v2/music/?recursive=true
    -
    -
    {% highlight json %}
    -{
    -    "state": {
    -        "generated": {
    -            "state": "up",
    -            "reason": ""
    -        }
    -    },
    -    "service": {
    -        "storage": {
    -            "node": {
    -                "0": {
    -                    "attributes": {
    -                        "hierarchical-group": "group0"
    -                    },
    -                    "state": {
    -                        "generated": {
    -                            "state": "up",
    -                            "reason": ""
    -                        },
    -                        "unit": {
    -                            "state": "up",
    -                            "reason": ""
    -                        },
    -                        "user": {
    -                            "state": "up",
    -                            "reason": ""
    -                        }
    -                    },
    -                    "metrics": {
    -                        "bucket-count": 0,
    -                        "unique-document-count": 0,
    -                        "unique-document-total-size": 0
    -                    }
    -{% endhighlight %}
    - -

    - Set node user state: -

    -
    -curl -X PUT -H "Content-Type: application/json" --data '
    -  {
    -      "state": {
    -          "user": {
    -              "state": "retired",
    -              "reason": "This node will be removed soon"
    -          }
    -      }
    -  }' \
    -  http://localhost:19050/cluster/v2/music/storage/0
    -
    -
    {% highlight json %}
    -{
    -    "wasModified": true,
    -    "reason": "ok"
    -}
    -{% endhighlight %}
    - - - -

    Further reading

    - diff --git a/en/content/content-nodes.mdx b/en/content/content-nodes.mdx new file mode 100644 index 0000000000..1e63e9a630 --- /dev/null +++ b/en/content/content-nodes.mdx @@ -0,0 +1,385 @@ +--- +title: "Content nodes and states" +--- + + +![](/assets/img/elastic-feed.svg) + + +Content cluster processes are *distributor*, *proton* and *cluster controller*. + +The distributor calculates the correct content node using the distribution algorithm and the [cluster state](#cluster-state). With no known cluster state, the client library will send requests to a random node, which replies with the updated cluster state if the node was incorrect. Cluster states are versioned, such that clients hitting outdated distributors do not override updated states with old states. + +The [distributor](#distributor) keeps track of which content nodes that stores replicas of each bucket (maximum one replica each), based on [redundancy](/en/reference/applications/services/content#redundancy) and information from the *cluster controller*. A bucket maps to one distributor only. A distributor keeps a bucket database with bucket metadata. The metadata holds which content nodes store replicas of the buckets, the checksum of the bucket content and the number of documents and meta entries within the bucket. Each document is algorithmically mapped to a bucket and forwarded to the correct content nodes. The distributors detect whether there are enough bucket replicas on the content nodes and add/remove as needed. Write operations wait for replies from every replica and fail if less than redundancy are persisted within timeout. + +The [cluster controller](#cluster-controller) manages the state of the distributor and content nodes. This *cluster state* is used by the document processing chains to know which distributor to send documents to, as well as by the distributor to know which content nodes should have which bucket. + +## Cluster state + +There are three kinds of state: [unit state](/en/reference/api/cluster-v2#state-unit), [user state](/en/reference/api/cluster-v2#state-user) and [generated state](/en/reference/api/cluster-v2#state-generated) (a.k.a. *cluster state*). + +For new cluster states, the cluster state version is incremented, and the new cluster state is broadcast to all nodes. There is a minimum time between each cluster state change. + +It is possible to set a minimum capacity for the cluster state to be `up`. + +If a cluster has so many nodes unavailable that it is considered down, the state of each node is irrelevant, and thus new cluster states will not be created and broadcast before enough nodes are back for the cluster to come back up. A cluster state indicating the entire cluster is down, may thus have outdated data on the node level. + +## Cluster controller + +The main task of the cluster controller is to maintain the [cluster state](#cluster-state). This is done by *polling* nodes for state, *generating* a cluster state, which is then *broadcast* to all the content nodes in the cluster. Note that clients do not interface with the cluster controller - they get the cluster state from the distributors - [details](#distributor). + + + + + + + + + + + + + + + + + + + + + + + +
    TaskDescription
    Node state pollingThe cluster controller polls nodes, sending the current cluster state. If the cluster state is no longer correct, the node returns correct information immediately. If the state is correct, the request lingers on the node, such that the node can reply to it immediately if its state changes. After a while, the cluster controller will send a new state request to the node, even with one pending. This triggers a reply to the lingering request and makes the new one linger instead. Hence, nodes have a pending state request.
    During a controlled node shutdown, it starts the shutdown process by responding to the pending state request that it is now stopping.
    **Note:** As controlled restarts or shutdowns are implemented as TERM signals from the config-sentinel, the cluster controller is not able to differ between controlled and other shutdowns.
    Cluster state generationThe cluster controller translates unit and user states into the generated cluster state
    Cluster state broadcastWhen node unit states are received, a cluster controller internal cluster state is updated. New cluster states are distributed with a minimum interval between. A grace period per unit state too - e.g., distributors and content nodes that are on the same node often stop at the same time.
    The version number is incremented, and the new cluster state is broadcast.
    If cluster state version is reset, distributors and content node processes may have to be restarted in order for the system to converge to the new state. Nodes will reject lower cluster state versions to prevent race conditions caused by overlapping cluster controller leadership periods.
    + +See [cluster controller configuration](/en/operations/self-managed/admin-procedures#cluster-controller-configuration). + +### Master election + +Vespa can be configured with one cluster controller. Reads and writes will work well in case of cluster controller down, but other changes to the cluster (like a content node going down) will not be handled. It is hence recommended to configure a set of cluster controllers. + +The cluster controller nodes elect a master, which does the node polling and cluster state broadcast. The other cluster controller nodes only exist to do master election and potentially take over if the master dies. + +All cluster controllers will vote for the cluster controller with the lowest index that says it is ready. If a cluster controller has more than half of the votes, it will be elected master. As a majority vote is required, the number of cluster controllers should be an odd number of 3 or greater. A fresh master will not broadcast states before a transition time is passed, allowing an old master to have some time to realize it is no longer the master. + +## Distributor + +Buckets are mapped to distributors using the [ideal state algorithm](/en/content/idealstate). As the cluster state changes, buckets are re-mapped immediately. The mapping does not overlap - a bucket is owned by one distributor. + +Distributors do not persist the bucket database, the bucket-to-content-node mapping is kept in memory in the distributor. Document count, persisted size and a metadata checksum per bucket is stored as well. At distributor (re)start, content nodes are polled for bucket information, and return which buckets are owned by this distributor (using the ideal state algorithm). There is no centralized bucket directory node. Likewise, at any distributor cluster state change, content nodes are polled for bucket handover - a distributor will then handle a new set of buckets. + +Document operations are mapped to content nodes based on bucket locations - each put/update/get/remove is mapped to a [bucket](/en/content/buckets) and sent to the right content nodes. To manage the document set as it grows and nodes change, buckets move between content nodes. + +Document API clients (i.e. container nodes with [document-api](/en/reference/applications/services/container#document-api)) do not communicate directly with the cluster controller, and do not know the cluster state at startup. Clients therefore start out by sending requests to a random distributor. If the document operation hits the wrong distributor, `WRONG_DISTRIBUTION` is returned, with the current cluster state in the response. `WRONG_DISTRIBUTION` is hence expected and normal at cold start / state change events. + +### Timestamps + +[Write operations](/en/writing/reads-and-writes) have a *last modified time* timestamp assigned when passing through the distributor. The timestamp is guaranteed to be unique within the [bucket](/en/content/buckets) where it is stored. The timestamp is used by the content layer to decide which operation is newest. These timestamps can be used when [visiting](/en/writing/visiting), to process/retrieve documents within a given time range. To guarantee unique timestamps, they are in microseconds - the microsecond part is generated to avoid conflicts with other documents. + +If documents are migrated *between* clusters, the target cluster will have new timestamps for their entries. Also, when [reprocessing documents](/en/applications/document-processors) *within* a cluster, documents will have new timestamps, even if not modified. + +### Ordering + +The Document API uses the [document ID](/en/schemas/documents#document-ids) to order operations. A Document API client ensures that only one operation is pending at the same time. This ensures that if a client sends multiple operations for the same document, they will be processed in a defined order. This is done by queueing pending operations *locally* at the client. + + +**Note:** + +If sending two write operations to the same document, and the first operation fails, the enqueued operation is sent. In other words, the client does not assume there exists any kind of dependency between separate operations to the same document. If you need to enforce this, use [test-and-set conditions](/en/writing/document-v1-api-guide#conditional-writes) for writes. + + +If *different* clients have pending operations on the same document, the order is unspecified. + +### Maintenance operations + +Distributors track which content nodes have which buckets in their bucket database. Distributors then use the [ideal state algorithm](/en/content/idealstate) to generate bucket *maintenance operations*. A stable system has all buckets located per the ideal state: + +- If buckets have too few replicas, new are generated on other content nodes. +- If the replicas differ, a bucket merge is issued to get replicas consistent. +- If a buckets has too many replicas, superfluous are deleted. Buckets are merged, if inconsistent, before deletion. +- If two buckets exist, such that both may contain the same document, the buckets are split or joined to remove such overlapping buckets. Read more on [inconsistent buckets](/en/content/buckets). +- If buckets are too small/large, they will be joined or split. + +The maintenance operations have different priorities. If no maintenance operations are needed, the cluster is said to be in the *ideal state*. The distributors synchronize maintenance load with user load, e.g. to remap requests to other buckets after bucket splitting and joining. + +### Restart + +When a distributor stops, it will try to respond to any pending cluster state request first. New incoming requests after shutdown is commenced will fail immediately, as the socket is no longer accepting requests. Cluster controllers will thus detect processes stopping almost immediately. + +The cluster state will be updated with the new state internally in the cluster controller. Then the cluster controller will wait for maximum [min_time_between_new_systemstates](https://github.com/vespa-engine/vespa/blob/master/configdefinitions/src/vespa/fleetcontroller.def) before publishing the new cluster state - this to reduce short-term state fluctuations. + +The cluster controller has the option of setting states to make other distributors take over ownership of buckets, or mask the change, making the buckets owned by the distributor restarting unavailable for the time being. + +If the distributor transitions from `up` to `down`, other distributors will request metadata from the content nodes to take over ownership of buckets previously owned by the restarting distributor. Until the distributors have gathered this new metadata from all the content nodes, requests for these buckets can not be served, and will fail back to client. When the restarting node comes back up and is marked `up` in the cluster state again, the additional nodes will discard knowledge of the extra buckets they previously acquired. + +For requests with timeouts of several seconds, the transition should be invisible due to automatic client resending. Requests with a lower timeout might fail, and it is up to the application whether to resend or handle failed requests. + +Requests to buckets not owned by the restarting distributor will not be affected. + +## Content node + +The content node runs *proton*, which is the query backend. + +### Restart + +When a content node does a controlled restart, it marks itself in the `stopping` state and rejects new requests. It will process its pending request queue before shutting down. Consequently, client requests are typically unaffected by content node restarts. The currently pending requests will typically be completed. New copies of buckets will be created on other nodes, to store new requests in appropriate redundancy. This happens whether node transitions through `down` or `maintenance` state. The difference being that if transitioning through `maintenance`, the distributor will not start any effort of synchronizing new copies with existing copies. They will just store the new requests until the maintenance node comes back up. + +When starting, content nodes will start with gathering information on what buckets it has data stored for. While this is happening, the service layer will expose that it is `down`. + +## Metrics + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    MetricDescription
    .idealstate.idealstate_diffThis metric tries to create a single value indicating distance to the ideal state. A value of zero indicates that the cluster is in the ideal state. Graphed values of this metric gives a good indication for how fast the cluster gets back to the ideal state after changes. Note that some issues may hide other issues, so sometimes the graph may appear to stand still or even go a bit up again, as resolving one issue may have detected one or several others.
    .idealstate.buckets_toofewcopiesSpecifically lists how many buckets have too few copies. Compare to the *buckets* metric to see how big a portion of the cluster this is.
    .idealstate.buckets_toomanycopiesSpecifically lists how many buckets have too many copies. Compare to the *buckets* metric to see how big a portion of the cluster this is.
    .idealstate.bucketsThe total number of buckets managed. Used by other metrics reporting bucket counts to know how big a part of the cluster they relate to.
    .idealstate.buckets_notrustedLists how many buckets have no trusted copies. Without trusted buckets operations against the bucket may have poor performance, having to send requests to many copies to try and create consistent replies.
    .idealstate.delete_bucket.pendingLists how many buckets that needs to be deleted.
    .idealstate.merge_bucket.pendingLists how many buckets there are, where we suspect not all copies store identical document sets.
    .idealstate.split_bucket.pendingLists how many buckets are currently being split.
    .idealstate.join_bucket.pendingLists how many buckets are currently being joined.
    .idealstate.set_bucket_state.pendingLists how many buckets are currently altered for active state. These are high priority requests which should finish fast, so these requests should seldom be seen as pending.
    + +Example, using the [quickstart](/en/basics/deploy-an-application-local) - find the distributor port (look for HTTP): + +```txt +$ docker exec vespa vespa-model-inspect service distributor + +distributor @ vespa-container : content +music/distributor/0 + tcp/vespa-container:19112 (MESSAGING) + tcp/vespa-container:19113 (STATUS RPC) + tcp/vespa-container:19114 (STATE STATUS HTTP) +``` + +Get the metric value: + +```txt +$ docker exec vespa curl -s http://localhost:19114/state/v1/metrics | jq . | \ + grep -A 10 idealstate.merge_bucket.pending + + "name": "vds.idealstate.merge_bucket.pending", + "description": "The number of operations pending", + "values": { + "average": 0, + "sum": 0, + "count": 1, + "rate": 0.016666, + "min": 0, + "max": 0, + "last": 0 + }, +``` + +## /cluster/v2 API examples + +Examples of state manipulation using the [/cluster/v2 API](/en/reference/api/cluster-v2). + +List content clusters: + +```txt +$ curl http://localhost:19050/cluster/v2/ +``` + +```json +{ + "cluster": { + "music": { + "link": "/cluster/v2/music" + }, + "books": { + "link": "/cluster/v2/books" + } + } +} +``` + +Get cluster state and list service types within cluster: + +```txt +$ curl http://localhost:19050/cluster/v2/music +``` + +```json +{ + "state": { + "generated": { + "state": "state-generated", + "reason": "description" + } + }, + "service": { + "distributor": { + "link": "/cluster/v2/music/distributor" + }, + "storage": { + "link": "/cluster/v2/music/storage" + } + } +} +``` + +List nodes per service type for cluster: + +```txt +$ curl http://localhost:19050/cluster/v2/music/storage +``` + +```json +{ + "node": { + "0": { + "link": "/cluster/v2/music/storage/0" + }, + "1": { + "link": "/cluster/v2/music/storage/1" + } + } +} +``` + +Get node state: + +```txt +$ curl http://localhost:19050/cluster/v2/music/storage/0 +``` + +```json +{ + "attributes": { + "hierarchical-group": "group0" + }, + "state": { + "generated": { + "state": "up", + "reason": "" + }, + "unit": { + "state": "up", + "reason": "" + }, + "user": { + "state": "up", + "reason": "" + } + }, + "metrics": { + "bucket-count": 0, + "unique-document-count": 0, + "unique-document-total-size": 0 + } +} +``` + +Get all nodes, including topology information (see `hierarchical-group`): + +```txt +$ curl http://localhost:19050/cluster/v2/music/?recursive=true +``` + +```json expandable +{ + "state": { + "generated": { + "state": "up", + "reason": "" + } + }, + "service": { + "storage": { + "node": { + "0": { + "attributes": { + "hierarchical-group": "group0" + }, + "state": { + "generated": { + "state": "up", + "reason": "" + }, + "unit": { + "state": "up", + "reason": "" + }, + "user": { + "state": "up", + "reason": "" + } + }, + "metrics": { + "bucket-count": 0, + "unique-document-count": 0, + "unique-document-total-size": 0 + } + } + } + } + } +} +``` + +Set node user state: + +```txt +curl -X PUT -H "Content-Type: application/json" --data ' + { + "state": { + "user": { + "state": "retired", + "reason": "This node will be removed soon" + } + } + }' \ + http://localhost:19050/cluster/v2/music/storage/0 +``` + +```json +{ + "wasModified": true, + "reason": "ok" +} +``` + +## Further reading + +- Refer to [administrative procedures](/en/operations/self-managed/admin-procedures) for configuration and state monitoring / management. +- Try the [Multinode testing and observability](https://github.com/vespa-engine/sample-apps/tree/master/examples/operations/multinode) sample app to get familiar with interfaces and behavior. diff --git a/en/content/elasticity.html b/en/content/elasticity.html deleted file mode 100644 index 75c219947a..0000000000 --- a/en/content/elasticity.html +++ /dev/null @@ -1,305 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Content cluster elasticity" -redirect_from: -- /en/elastic-vespa.html -- /en/elasticity.html ---- - -

    - Vespa clusters can be grown and shrunk while serving queries and writes. - Documents in content clusters are automatically redistributed on changes to maintain an even distribution - with minimal data movement. - To resize, just change the nodes - and redeploy the application - no restarts needed. -

    -A cluster growing in two dimensions - -

    - Documents are managed by Vespa in chunks called buckets. - The size and number of buckets are completely managed by Vespa and there is never any - need to manually control sharding. -

    - -

    - The elasticity mechanism is also used to recover from a node loss: - New replicas of documents are created automatically on other nodes to maintain - the configured redundancy. - Failed nodes is therefore not a problem that requires immediate attention - - clusters will self-heal from node failures as long as there are sufficient resources. - -

    -A cluster with a node failure - -

    - When you want to remove nodes from a content cluster, you can have the system migrate data - off them in an orderly fashion prior to removal. This is done by marking nodes as retired. - This is useful to remove nodes that should be retired, but also to migrate a cluster to entirely new nodes - while online: Add the new nodes, mark the old nodes retired, wait for the data to be redistributed - and remove the old nodes. -

    -

    - The auto-elasticity is configured for a normal fail-safe operation, - but there are tradeoffs like recovery speed and resource usage. - Learn more in procedures. -

    - - -

    Adding nodes

    - -

    - To add or remove nodes from a content cluster, just nodes - tag of the content cluster in - services.xml - and redeploy. - Read more in procedures. -

    - -

    - When adding a new node, a new ideal state is calculated for all buckets. - The buckets mapped to the new node are moved, the superfluous are removed. - See redistribution example - add a new node to the system, with redundancy n=2: -

    -Bucket migration as a node is added to the cluster -

    - The distribution algorithm generates a random node sequence for each bucket. - In this example with n=2, replicas map to the two nodes sorted first. - The illustration shows how placement onto two nodes changes as a third node is added. - The new node takes over as primary for the buckets where it got sorted first, - and as secondary for the buckets where it got sorted second. - This ensures minimal data movement when nodes come and go, and allows capacity to be changed easily. -

    - No buckets are moved between the existing nodes when a new node is added. - Based on the pseudo-random sequences, some buckets change from primary to secondary, or are removed. - Multiple nodes can be added in the same deployment. -

    - - - -

    Removing nodes

    - -

    - Whether a node fails or is retired, the same redistribution happens. - If the node is retired, replicas are generated on the other nodes - and the node stays up, but with no active replicas. - Example of redistribution after node failure, n=2: -

    -Bucket migration as a node is removed from the cluster -

    - Here, node 2 fails. This node held the active replicas of bucket 2 and 6. - Once the node fails the secondary replicas are set active. - If they were already in a ready state, they start serving queries immediately, - otherwise they will index replicas, - see searchable-copies. - All buckets that no longer have secondary replicas - are merged to the remaining nodes according to the ideal state. -

    - - - -

    Grouped distribution

    - -

    - Nodes in content clusters can be placed in groups. - A group of nodes in a content cluster will have one or more complete replicas of the entire document corpus. -

    -A cluster changes from using one to many groups - -

    This is useful in the cases listed below:

    - - - - - - - - - - - - - - -
    Cluster upgrade - With multiple groups it becomes safe to take out a full group for upgrade instead of just one node at a time. - Read more. -
    Query throughput - Applications with high query rates and/or high static query cost can use groups to scale to higher query rates - since Vespa will automatically send a query to just a single group. - Read more. -
    Topology - By using groups you can control replica placement over availability zones, network switches, or racks to - ensure there is redundancy at the switch and rack level. -
    -

    - Tuning group sizes and node resources enables applications to easily find the latency/cost sweet spot, - the elasticity operations are automatic and queries and writes work as usual with no downtime. -

    - -

    Pinning groups

    - -

    While each group contains the same data, they may not return exactly the same search results -since each node in each group sees a unique subset of the groups data written in a unique order. -This can lead to some inconsistency when users are paging over multiple result pages. To avoid this, -Vespa supports pinning to a particular search group: Results contains a searchGroups -field in the top level in results (root.fields.searchGroup) containing the integer index of the group -that produced the result. This can be passed back in the -query for the next page as the model.searchGroup query parameter.

    - -

    Note that:

    -
      -
    • The searchGroup field is only present in results where it is informative and unique: - It will not be present when searching content clusters without multiple groups, or when composed of - hits from multiple grouped queries.
    • -
    • The model.searchGroup parameter is a soft preference and always safe to pass: - If the group is unavailable or non-existent another group will be used.
    • -
    - - -

    Changing topology

    -

    - A Vespa elasticity feature is the ability to change topology (i.e. grouped distribution) without service disruption. - This is a live change, and will auto-redistribute documents to the new topology. -

    -

    - Also read topology change - if running Vespa self-hosted - the below steps are general for all hosting options. -

    - - -

    Replicas

    -

    - When changing topology, pay attention to the min-redundancy setting - - this setting configures a minimum number of replicas in a cluster, - the actual number is topology dependent - example: -

    -

    - A flat cluster with min-redundancy n=2 and 15 nodes is changed into a grouped cluster with 3 groups with 5 nodes each - (total node count and n is kept unchanged). - In this case, the actual redundancy will be 3 after the change, - as each of the 3 groups will have at least 1 replica for full query coverage. - The practical consequence is that disk and memory requirements per node increases due to the change to topology. - It is therefore important to calculate the actual replica count before reconfiguring topology. -

    - - -

    Query coverage

    -

    - Changing topology might cause query coverage loss in the transition, unless steps taken in the right order. - If full coverage is not important, just make the change and wait for document redistribution to complete. -

    -

    - To keep full query coverage, make sure not to change both group size and number of groups at the same time: -

    -
      -
    1. - To add nodes for more data, or to have less data per node, increase group size. - E.g., in a 2-group cluster with 8 nodes per group, add 4 nodes for a 25% capacity increase with 10 nodes per group. -
    2. -
    3. - If the goal is to add query capacity, add one or more groups, with the same node count as existing group(s). - A flat cluster is the same as one group - if the flat cluster has 8 nodes, - change to a grouped cluster with 2 groups of 8 nodes per group. - This will add an empty group, which is put in query serving once populated. -
    4. -
    -

    - In short, if the end-state means both changing number of groups and node count per group, - do this as separate steps, as a combination of the above. - Between each step, wait for document redistribution to complete using the merge_bucket.pending metric - - see example. -

    - - - -

    Buckets

    - -

    - To manage documents, Vespa groups them in buckets, - using hashing or hints in the document ID. -

    - -

    - A document Put or Update is sent to all replicas of the bucket with the document. - If bucket replicas are out of sync, a bucket merge operation is run to re-sync the bucket. - A bucket contains tombstones - of recently removed documents. -

    - -

    - Buckets are split when they grow too large, and joined when they shrink. - This is a key feature for high performance in small to large instances, - and eliminates need for downtime or manual operations when scaling. - Buckets are purely a content management concept, and data is not stored or - indexed in separate buckets, nor does queries relate to buckets in any way. - Read more in buckets. -

    - - - -

    Ideal state distribution algorithm

    -

    - The ideal state distribution algorithm - uses a variant of the CRUSH algorithm - to decide bucket placement. - It makes a minimal number of documents move when nodes are added or removed. - Central to the algorithm is the assignment of a node sequence to each bucket: -

    -Assignment of a node sequence to each bucket -

    - Steps to assign a bucket to a set of nodes: -

    -
      -
    1. Seed a random generator with the bucket ID to generate a pseudo-random sequence of numbers. - Using the bucket ID as seed will then always generate the same sequence for the bucket.
    2. -
    3. Nodes are ordered by distribution-key, - assign the random number in that order. - E.g. a node with distribution-key 0 will get the first random number, node 1 the second.
    4. -
    5. Sort the node list by the random number.
    6. -
    7. Select nodes in descending random number order - - above, node 1, 3 and 0 will store bucket 0x3c000000000000a0 with n=3 (redundancy). - For n=2, node 1 and 3 will store the bucket. - This specification of where to place a bucket is called the bucket's ideal state.
    8. -
    -

    - Repeat this for all buckets in the system. -

    - - - -

    Consistency

    - -

    - Consistency is maintained at bucket level. - Content nodes calculate local checksums based on the bucket contents, - and the distributors compare checksums across the bucket replicas. - A bucket merge is issued to resolve inconsistency, when detected. - While there are inconsistent bucket replicas, operations are routed to the "best" replica. -

    - -

    - As buckets are split and joined, it is possible for replicas of a bucket to be split at different levels. - A node may have been down while its buckets have been split or joined. - This is called inconsistent bucket splitting. - Bucket checksums can not be compared across buckets with different split levels. - Consequently, content nodes do not know whether all documents exist in enough replicas in this state. - Due to this, inconsistent splitting is one of the highest maintenance priorities. - After all buckets are split or joined back to the same level, - the content nodes can verify that all the replicas are consistent and fix any detected issues with a merge. - Read more. -

    - - - -

    Further reading

    - - diff --git a/en/content/elasticity.mdx b/en/content/elasticity.mdx new file mode 100644 index 0000000000..ae4ba33140 --- /dev/null +++ b/en/content/elasticity.mdx @@ -0,0 +1,158 @@ +--- +title: "Elasticity" +description: "Vespa clusters can be grown and shrunk while serving queries and writes. Documents in content clusters are automatically redistributed on changes to maintain an even distribution with minimal data movement. To resize, just change the [nodes](/en/reference/applications/services/services#nodes) and redeploy the application - no restarts needed." +--- + + +![](/en/content/images/elastic-grow.png) + + +Documents are managed by Vespa in chunks called [buckets](#buckets). The size and number of buckets are completely managed by Vespa and there is never any need to manually control sharding. + +The elasticity mechanism is also used to recover from a node loss: New replicas of documents are created automatically on other nodes to maintain the configured redundancy. Failed nodes is therefore not a problem that requires immediate attention - clusters will self-heal from node failures as long as there are sufficient resources. + + +![](/en/content/images/elastic-fail.png) + + +When you want to remove nodes from a content cluster, you can have the system migrate data off them in an orderly fashion prior to removal. This is done by marking nodes as *retired*. This is useful to remove nodes that should be retired, but also to migrate a cluster to entirely new nodes while online: Add the new nodes, mark the old nodes retired, wait for the data to be redistributed and remove the old nodes. + +The auto-elasticity is configured for a normal fail-safe operation, but there are tradeoffs like recovery speed and resource usage. Learn more in [procedures](/en/operations/self-managed/admin-procedures#content-cluster-configuration). + +## Adding nodes + +To add or remove nodes from a content cluster, just `nodes` tag of the [content](/en/reference/applications/services/content) cluster in [services.xml](/en/reference/applications/services/services) and [redeploy](/en/basics/applications#deploying-applications). Read more in [procedures](/en/operations/self-managed/admin-procedures). + +When adding a new node, a new *ideal state* is calculated for all buckets. The buckets mapped to the new node are moved, the superfluous are removed. See redistribution example - add a new node to the system, with redundancy n=2: + + +![](/assets/img/add-node-move-buckets.svg) + + +The distribution algorithm generates a random node sequence for each bucket. In this example with n=2, replicas map to the two nodes sorted first. The illustration shows how placement onto two nodes changes as a third node is added. The new node takes over as primary for the buckets where it got sorted first, and as secondary for the buckets where it got sorted second. This ensures minimal data movement when nodes come and go, and allows capacity to be changed easily. + +No buckets are moved between the existing nodes when a new node is added. Based on the pseudo-random sequences, some buckets change from primary to secondary, or are removed. Multiple nodes can be added in the same deployment. + +## Removing nodes + +Whether a node fails or is *retired*, the same redistribution happens. If the node is retired, replicas are generated on the other nodes and the node stays up, but with no active replicas. Example of redistribution after node failure, n=2: + + +![](/assets/img/lose-node-move-buckets.svg) + + +Here, node 2 fails. This node held the active replicas of bucket 2 and 6. Once the node fails the secondary replicas are set active. If they were already in a *ready* state, they start serving queries immediately, otherwise they will index replicas, see [searchable-copies](/en/reference/applications/services/content#searchable-copies). All buckets that no longer have secondary replicas are merged to the remaining nodes according to the ideal state. + +## Grouped distribution + +Nodes in content clusters can be placed in [groups](/en/reference/applications/services/content#group). A group of nodes in a content cluster will have one or more complete replicas of the entire document corpus. + + +![](/en/content/images/query-groups.png) + + +This is useful in the cases listed below: + + + + + + + + + + + + + + + + + + + + + + +
    **Cluster upgrade**With multiple groups it becomes safe to take out a full group for upgrade instead of just one node at a time. Read more.
    **Query throughput**Applications with high query rates and/or high static query cost can use groups to scale to higher query rates since Vespa will automatically send a query to just a single group. Read more
    **Topology**By using groups you can control replica placement over network switches or racks to ensure there is redundancy at the switch and rack level.
    + +Tuning group sizes and node resources enables applications to easily find the latency/cost sweet spot, the elasticity operations are automatic and queries and writes work as usual with no downtime. + + +### Pinning groups + +While each group contains the same data, they may not return exactly the same search results since each node in each group sees a unique subset of the groups data written in a unique order. This can lead to some inconsistency when users are paging over multiple result pages. To avoid this, Vespa supports *pinning* to a particular search group: Results contains a `searchGroups` field in the top level in results (root.fields.searchGroup) containing the integer index of the group that produced the result. This can be passed back in the query for the next page as the `model.searchGroup` query parameter. + +Note that: + +- The searchGroup field is only present in results where it is informative and unique: It will not be present when searching content clusters without multiple groups, or when composed of hits from multiple grouped queries. +- The model.searchGroup parameter is a soft preference and always safe to pass: If the group is unavailable or non-existent another group will be used. + +## Changing topology + +A Vespa elasticity feature is the ability to change topology (i.e. grouped distribution) without service disruption. This is a live change, and will auto-redistribute documents to the new topology. + +Also read [topology change](/en/operations/self-managed/admin-procedures#topology-change) if running Vespa self-hosted - the below steps are general for all hosting options. + +### Replicas + +When changing topology, pay attention to the [min-redundancy](/en/reference/applications/services/content#min-redundancy) setting - this setting configures a *minimum* number of replicas in a cluster, the *actual* number is topology dependent - example: + +A flat cluster with min-redundancy n=2 and 15 nodes is changed into a grouped cluster with 3 groups with 5 nodes each (total node count and n is kept unchanged). In this case, the actual redundancy will be 3 after the change, as each of the 3 groups will have at least 1 replica for full query coverage. The practical consequence is that disk and memory requirements per node *increases* due to the change to topology. It is therefore important to calculate the actual replica count before reconfiguring topology. + +### Query coverage + +Changing topology might cause query coverage loss in the transition, unless steps taken in the right order. If full coverage is not important, just make the change and wait for document redistribution to complete. + +To keep full query coverage, make sure not to change both group size and number of groups at the same time: + +1. To add nodes for more data, or to have less data per node, increase group size. E.g., in a 2-group cluster with 8 nodes per group, add 4 nodes for a 25% capacity increase with 10 nodes per group. +2. If the goal is to add query capacity, add one or more groups, with the same node count as existing group(s). A flat cluster is the same as one group - if the flat cluster has 8 nodes, change to a grouped cluster with 2 groups of 8 nodes per group. This will add an empty group, which is put in query serving once populated. + +In short, if the end-state means both changing number of groups and node count per group, do this as separate steps, as a combination of the above. Between each step, wait for document redistribution to complete using the `merge_bucket.pending` metric - see [example](/en/writing/initial-batch-feed). + +## Buckets + +To manage documents, Vespa groups them in *buckets*, using hashing or hints in the [document ID](/en/schemas/documents). + +A document Put or Update is sent to all replicas of the bucket with the document. If bucket replicas are out of sync, a bucket merge operation is run to re-sync the bucket. A bucket contains [tombstones](/en/operations/self-managed/admin-procedures#data-retention-vs-size) of recently removed documents. + +Buckets are split when they grow too large, and joined when they shrink. This is a key feature for high performance in small to large instances, and eliminates need for downtime or manual operations when scaling. Buckets are purely a content management concept, and data is not stored or indexed in separate buckets, nor does queries relate to buckets in any way. Read more in [buckets](/en/content/buckets). + +## Ideal state distribution algorithm + +The [ideal state distribution algorithm](/en/content/idealstate) uses a variant of the [CRUSH algorithm](https://ceph.io/assets/pdfs/weil-crush-sc06.pdf) to decide bucket placement. It makes a minimal number of documents move when nodes are added or removed. Central to the algorithm is the assignment of a node sequence to each bucket: + + +![](/assets/img/bucket-node-sequence.svg) + + +Steps to assign a bucket to a set of nodes: + + + +Seed a random generator with the bucket ID to generate a pseudo-random sequence of numbers. Using the bucket ID as seed will then always generate the same sequence for the bucket. + + +Nodes are ordered by [distribution-key](/en/reference/applications/services/content#node), assign the random number in that order. E.g. a node with distribution-key 0 will get the first random number, node 1 the second. + + +Sort the node list by the random number. + + +Select nodes in descending random number order - above, node 1, 3 and 0 will store bucket 0x3c000000000000a0 with n=3 (redundancy). For n=2, node 1 and 3 will store the bucket. This specification of where to place a bucket is called the bucket's *ideal state*. + + + +Repeat this for all buckets in the system. + +## Consistency + +Consistency is maintained at bucket level. Content nodes calculate local checksums based on the bucket contents, and the distributors compare checksums across the bucket replicas. A *bucket merge* is issued to resolve inconsistency, when detected. While there are inconsistent bucket replicas, operations are routed to the "best" replica. + +As buckets are split and joined, it is possible for replicas of a bucket to be split at different levels. A node may have been down while its buckets have been split or joined. This is called *inconsistent bucket splitting*. Bucket checksums can not be compared across buckets with different split levels. Consequently, content nodes do not know whether all documents exist in enough replicas in this state. Due to this, inconsistent splitting is one of the highest maintenance priorities. After all buckets are split or joined back to the same level, the content nodes can verify that all the replicas are consistent and fix any detected issues with a merge. [Read more](/en/content/consistency). + +## Further reading + +- [content nodes](/en/content/content-nodes) +- [proton](/en/content/proton) - see *ready* state diff --git a/en/content/idealstate.html b/en/content/idealstate.html deleted file mode 100644 index 7f386dd637..0000000000 --- a/en/content/idealstate.html +++ /dev/null @@ -1,1606 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Distribution Algorithm" ---- - -

    - The distribution algorithm decides what nodes should be responsible for a given bucket. - This is used directly in the clients to calculate distributor to talk to. - Content nodes need time to move buckets when the distribution is changing, - so routing to content nodes is done using tracked current state. - The distribution algorithm decides which content nodes is wanted to store the bucket copies though, - and due to this, the algorithm is also referred to as the ideal state algorithm. -

    - The input to the distribution algorithm is a bucket identifier, - together with knowledge about what nodes are available, and what their capacities are. -

    - The output of the distribution algorithm is a sorted list of the available nodes. - The first node in the order is the node most preferred to handle a given bucket. - Currently, the highest order distributor node will be the owning distributor, - and the redundancy factor decides how many of the highest order content nodes - are preferred to store copies for a bucket. -

    - To enable minimal transfer of buckets when the list of available nodes changes, - the removal or addition of nodes should not alter the sort order of the remaining nodes. -

    - Desired qualities for the ideal state algorithm: -

    - - - - - - - - - - -
    Minimal reassignment on cluster state change -
      -
    • If a node goes down, only buckets that resided on that node should be reassigned.
    • -
    • If a node comes up, only buckets that are moved to the new node should relocate.
    • -
    • Increasing the capacity of a single node should only move buckets to that node.
    • -
    • Reducing the capacity of a single node should only move buckets away from that node.
    • -
    -
    No skew in distribution -
    • Nodes should get an amount of data relative to their capacity.
    -
    Lightweight -
    • A simple algorithm that is easy to understand is a plus. - Being lightweight to calculate is also a plus, giving more options of how to use it, - without needing to cache results.
    -
    - - - -

    Computational cost

    -

    - When considering how efficient the algorithm have to be, - it is important to consider how often we need to calculate the ideal locations. - Calculations are needed for the following tasks: -

    -
      -
    • - A client needs to map buckets to the distributors. - If there are few buckets existing, all the results can be cached in clients, - but for larger clusters, a lot of buckets may need to exist to create an even distribution, - and caching becomes more memory intensive. - Preferably the computational cost is cheap enough, such that no caching is needed. - Currently, no caching is done by clients, but there is typically less than a million buckets, - so caching all results would still have been viable. -
    • - Distributors need to calculate ideal state for a single bucket - to verify that incoming operations are mapped to the correct distributor - (clients have cluster state matching the distributor). - This could be eliminated for buckets pre-existing in the bucket database, - which would be true in most all cases. - Currently, calculation is done for all requests. -
    • - Distributors need to calculate correct content nodes to create bucket - copies on when operations to currently non-existing buckets come in. - This is typically only something happening at the start of the cluster lifetime though. - Normally buckets are created through splitting or joining existing buckets. -
    • - Distributors need to calculate ideal state to check if any maintenance operations need to be done for a bucket. -
    • - Content nodes need to calculate ideal state for a single bucket - to verify that the correct distributor sent the request. - This could be cached or served through bucket database but currently there is no need. -
    • -
    -

    - As long as the algorithm is cheap, we can avoid needing to cache the result. - The cache will then not limit scalability, - and we have less dependencies and complexity within the content layer. - The current algorithm has shown itself cheap enough, such that little caching has been needed. -

    - - - -

    A simple example: Modulo

    -

    - A simple approach would be to use a modulo operation to find the most preferred node, - and then just order the nodes in configured order from there, - skipping nodes that are currently not available: -

    -$$\text{most preferred node} = \text{bucket % nodecount}$$ -

    -Properties: -

    -
      -
    • Computational lightweight and easy to understand
    • -
    • Perfect distribution among nodes.
    • -
    • Total redistribution on state change.
    • -
    -

    - By just skipping currently unavailable nodes, nodes can go down and up with minimal movement. - However, if the number of configured nodes change, - practically all buckets will be redistributed. - As the content layer is intended to be scalable, - this breaks with one of the intentions and this algorithm has thus not been considered. -

    - - - -

    Weighted random election

    -

    - This is the algorithm that is currently used for distribution in the content layer, - as it fits our use case well. -

    - To avoid a total redistribution on state change, - the mapping can not be heavily dependent on the number of nodes in the cluster. - By using random numbers, we can distribute the buckets randomly between the nodes, - in such a fashion that altering the cluster state has a small impact. - As we need the result to be reproducible, - we obviously need to use a pseudo-random number generator and not real random numbers. -

    - The idea is as follows. To find the location of a given bucket, - seed a random number generator with the bucket identifier, - when draw one number for each node. - The drawn numbers will then decide upon the preferred node order for that specific bucket. -

    - For this to be reproducible, all nodes need to draw the same numbers each time. - Each node is assigned a distribution key in the configuration. - This key decides what random number the node will be assigned. - For instance, a node with distribution key 13, will be assigned the 14th random number generated. - (As the first will go to the node with key 0). - The existence of this node then also requires us to always generate at least 14 random numbers - to do the calculation. -

    - Thus, one may end up calculating random numbers for nodes that are currently not available, - either because they are temporarily down, - or because the configuration have left holes in the distribution key space. - It is recommended to not leave too large holes in the distribution key space to not waste too much. -

    - Using this approach, if you add another node to the cluster, it will roll for each bucket. - It should thus steal ownership of some of the buckets. - As all the numbers are random, it will steal buckets from all the other nodes, thus, - given that the bucket count is large compared to the number of nodes, - it will steal on average 1/n of the buckets from each pre-existing node, - where n is the number of nodes in the current cluster. - Likewise, if a node is removed from the cluster, - the remaining nodes will divide the extra load between them. -

    - - -

    Weighting nodes

    -

    - By enforcing all the numbers drawn to be floating point numbers between 0 and 1, - we can introduce node weights using the following formula: -

    - $${r}^{\frac{1}{c}}$$ -

    - Where r is the floating point number between 0 and 1 that was drawn for a given node, - and c is the node capacity, which is the weight of the node. - Proof not included here, but this will end up giving each node on average an - amount of data that is relative to its capacity. - That is, among any nodes there are two nodes X and Y, - where the number of buckets given to X should be equal to the number of buckets - given to Y multiplied by capacity(X)/capacity(Y). (Given perfect random distribution) -

    - Altering the weight in a running system will also create a minimal redistribution of data. - If we reduce the capacity, all the nodes number will be reduced, - and some of its buckets will be taken over by the other nodes, - and vice versa if the capacity is increased. Properties: -

    -
      -
    • Minimum data movement on state changes.
    • -
    • Some skew, depending on how good the random number generator is, the - amount of nodes we have to divide buckets between, and the number of - buckets we have to divide between them.
    • -
    • Fairly cheap to compute given a reasonable amount of nodes, and an - inexpensive pseudo-random number generator.
    • -
    - - - -

    Distribution skew

    -

    - The algorithm does generate a bit of skew in the distribution, - as it is essentially random. The following attributes decrease the skew: -

    -
      -
    • Having more buckets to distribute.
    • -
    • Having less targets (nodes and partitions) to distribute buckets to.
    • -
    • Having a more uniform pseudo-random function.
    • -
    -

    - The more buckets exist, the more metadata needs to be tracked in the distributors though, - and operations that wants to scan all the buckets will take longer. - Additionally, the backend may want buckets above a given size to improve performance, - storage efficiency or similar. - Consequently, we typically want to enforce enough buckets for a decent distribution, but not more. -

    - Then the number of nodes increase, more buckets need to exist to keep the distribution even. - If the number of nodes is doubled, - the number of buckets must typically more than double to keep the distribution equally even. - Thus, this scales worse than linear. It does not scale much worse though, - and this has not proved to be a practical problem for the cluster sizes we have used up until now. - (A cluster size of a thousand nodes does not seem to be any issue here) -

    - Having a good and uniform pseudo-random function makes the distribution more even. - However, this may require more computationally heavy generators. - Currently, we are using a simple and fast algorithm, - and it has proved more than sufficient for our needs. -

    - The distribution to distributors are done to create an even distribution between the nodes. - The distributors are free to split the buckets further if the backend wants buckets to contain less data. - They can not use fewer buckets than are needed for distribution though. - By using a minimum amount of buckets for distribution, - the distributors have more freedom to control sizes of buckets. -

    - - -

    Distribution waste

    -

    - To measure how many buckets are needed to create a decent distribution a metric is needed. - We have defined a waste metric for this purpose as follows: -

    - Distribute the buckets to all the units. - Assume the size of all units are identical. - Assume the unit with the most units assigned to it is at 100% capacity. - The wasted space is the percentage of unused capacity compared to the used capacity. -

    - This definition seems useful as a cluster is considered at full capacity once - one of its partitions is at full capacity. - Having one node with more buckets than the rest is thus damaging, - while having one node with fewer buckets than the rest is just fine. -

    - Example: There are 4 nodes distributing 18 units. The node with the most units has 6. - Distribution waste is 100% * (4 * 6 - 18) / (4 * 6) = 25%. -

    - Below we have calculated waste based on number of nodes and the amount of - buckets to distribute between them. - Bits refer to distribution bits used. - A distribution bit count of 16 indicates that there will be 216 buckets. -

    - The calculations assume all buckets have the same size. This is normally close - to true as documents are randomly assigned to buckets. - There will be lots of buckets per node too, so a little variance typically evens out fairly well. -

    - The tables below assume only one partition exist on each node. - If you have 4 partitions on 16 nodes, you should rather use the values for - 4 * 16 = 64 nodes. -

    - A higher redundancy factor indicates more buckets to distribute between the same amount of nodes, - resulting in a more even distribution. - Doubling the redundancy has the same effect as adding one to the distribution bit count. - To get values for redundancy 4, the redundancy 2 values can be used, - and then the waste will be equal to the value with one less distribution bit used. -

    - - -

    Calculated waste from various cluster sizes

    -

    - A value of 1 indicates 100% waste. A value of 0.1 indicates 10% waste. - A waste below 1 % is shown green, below 10% as yellow and below 30% as orange. - Red indicates more than 30% waste. -

    - - - -

    Distribution with redundancy 1:

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    Bits \ Nodes123456789101112131415
    10.00000.00000.33330.50000.60000.66670.71430.75000.77780.80000.81820.83330.84620.85710.8667
    20.00000.33330.33330.50000.20000.33330.42860.50000.55560.60000.63640.66670.69230.71430.7333
    30.00000.20000.11110.33330.20000.33330.61900.66670.82220.84000.85450.83330.69230.71430.7333
    40.00000.11110.11110.33330.36000.33330.42860.50000.77780.80000.81820.80950.69230.71430.6444
    5-0.05880.11110.27270.28890.40740.23810.33330.81290.83160.84690.85190.83590.83670.8359
    6-0.00000.07250.15790.14670.11110.16880.38460.70370.72170.74700.74600.72650.69520.6718
    7-0.07250.05190.08570.08570.11110.20500.20000.45300.46670.51520.51520.45300.39050.3436
    8-0.00000.00780.07250.08570.09220.12930.13510.16340.17420.16880.23810.24260.29670.3173
    9-0.00390.01920.14670.16070.12030.10800.11110.13800.13220.12180.17950.19620.23810.2580
    10-0.00190.02750.09220.08980.06230.07410.09220.11110.10180.12180.12030.14380.16880.1675
    11-0.00190.02340.04300.03850.02480.02480.04830.06360.06480.07370.07250.08940.08000.0958
    12--0.01210.02850.02820.01210.01490.05710.05770.05620.05490.04120.05100.04390.0616
    13--0.00740.00190.00700.01770.03040.03030.03370.01890.02520.03580.04090.05010.0385
    14--0.00410.00240.00370.00270.01450.00730.01010.01300.02200.02340.02900.02480.0195
    15--0.00190.00210.00360.00830.00590.00560.01010.00970.01230.01630.01500.01860.0173
    16--0.00100.00070.00100.00300.00490.00390.00850.00720.00970.01080.01350.01410.0115
    17-----0.00300.00330.00240.00360.00300.00550.00910.01350.01560.0143
    18------0.0019-0.00290.00270.00430.00400.00660.00610.0060
    19--------0.0019-0.00210.00300.00230.00310.0042
    20-----------0.00290.00250.00370.0044
    21------------0.00260.00350.0040
    - -

    Distribution with redundancy 2:

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    Bits \ Nodes123456789101112131415
    10.00000.00000.33330.50000.60000.66670.42860.50000.55560.60000.63640.66670.69230.71430.7333
    20.00000.00000.33330.33330.20000.33330.42860.50000.55560.60000.63640.66670.69230.42860.4667
    30.00000.00000.11110.20000.20000.33330.42860.50000.70370.73330.75760.77780.79490.77140.7333
    40.00000.00000.11110.20000.20000.33330.34690.20000.74600.77140.77620.77780.79490.77140.7630
    5--0.07250.15790.24710.23810.29670.27270.72650.75380.76730.77780.79490.79220.7968
    6--0.05190.11110.17420.14670.20500.23810.69080.70230.70160.71170.72650.72290.7247
    7--0.03030.01540.03400.03030.08570.11110.49210.48800.48280.47970.50770.46220.4667
    8--0.00780.03030.02480.06230.08570.07250.09700.13220.10490.12930.16200.18730.2242
    9--0.00190.02660.05190.04660.06820.07910.08240.05190.06910.05190.06230.07410.0898
    10--0.00630.01730.01540.02750.01160.03400.05580.02940.04520.04660.05670.05010.0584
    11--0.00780.00490.01540.01770.01490.02100.02750.01770.02520.03030.03050.03440.0317
    12---0.00730.01120.01920.02310.03120.02960.01770.02780.03580.02450.03120.0385
    13---0.00610.00490.00960.01120.02010.02180.00880.00770.01990.01380.03040.0317
    14---0.00590.00580.00580.00570.00920.01280.00820.01390.00810.00960.01990.0213
    15----0.00140.00390.00520.00340.00510.00850.00440.00720.01070.01010.0082
    16----0.00160.00300.00260.00360.00650.00510.00610.00840.00650.00830.0100
    17------0.00100.00200.0028-0.00400.00490.00670.00710.0062
    18--------0.0032-0.0024-0.00340.00560.0041
    19------------0.00250.0018-
    - -

    Distribution with redundancy 2:

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    Bits \ Nodes162032486410012816020025635050080010005000
    80.20000.30810.27270.51520.52940.57330.63640.70910.76730.80000.85370.88620.89330.89760.9659
    90.07250.22420.17950.17950.30430.31730.38460.50770.53450.63640.73400.79520.84000.87200.9317
    100.07250.13220.12330.20990.15790.24150.33330.57330.46110.57890.65580.72690.82930.84250.8976
    110.03400.08570.09220.11110.12330.19690.25580.59370.56430.58970.59650.60990.65870.75910.8830
    120.04480.03850.06230.10650.09860.12850.37250.38310.40640.40740.47990.48800.51240.83280.8976
    130.03400.03280.05540.06990.06230.09480.10490.21830.23440.31910.34980.45390.57330.66560.8870
    140.01400.01890.03760.04520.04660.07170.09860.10570.10470.22420.28530.27980.40640.49590.8830
    150.00940.01180.03850.02680.03310.06380.07080.07750.08980.13220.21330.21040.35500.44460.8752
    160.00970.00810.03800.03030.03620.05770.05010.06270.07170.10330.17330.16780.25860.31010.8511
    170.00750.00660.03460.02930.01540.02580.04660.05460.07040.10410.14690.19830.27020.29720.7740
    180.00530.00570.00980.00980.01220.01490.02380.03000.03940.03530.04340.05530.06110.17820.6334
    19-0.00220.00500.01620.00980.01330.01490.02200.02420.02520.03330.03980.04950.09990.5145
    20--0.00300.01070.00880.00980.01440.01400.01480.02030.01950.02550.03480.11330.4481
    21--0.00430.00630.00510.00740.00790.00850.00860.01130.01470.01700.02370.10680.4422
    22---0.00260.00350.00370.00820.00610.00770.00870.01010.01340.01930.11400.4635
    23---0.0019-0.00260.00800.00550.00560.00570.00630.00960.01550.12940.4982
    24---0.0013--0.00740.00600.00580.00530.00490.00680.01120.04710.3219
    25---------0.00430.00430.00580.00670.05120.2543
    26----------0.00400.00420.00430.00510.0210
    27------------0.00280.01570.0814
    - - -

    Default number of distribution bits used

    -

    - Note that changing the amount of distribution bits used will change what buckets exist, - which will change the distribution considerably. - We thus do not want to alter the distribution bit count too often. -

    - Ideally, the users would be allowed to configure minimal and maximal acceptable waste, - and the current amount of distribution bits could then just be calculated on the fly. - But as computing the waste values above are computational heavy, - especially with many nodes and many distribution bits, - currently only a couple of profiles are available for you to configure. -

    -

    - Vespa Cloud note: Vespa Cloud locks distribution bit count to 16. - This is because Vespa Cloud offers auto-scaling of nodes, and such a scaling decision - should not implicitly lead to a full redistribution of data by crossing a distribution bit - node count boundary. 16 bits strikes a good balance of low skew and high performance for - most production deployments. -

    - -

    Loose mode (default)

    -

    - The loose mode allows for more waste, allowing the amount of nodes to - change considerably without altering the distribution bit counts. -

    - - - - - - - - - - - - - - - - - - - - - - - - - -
    Node count1-45-199200->
    Distribution bit count81624
    Max calculated waste *)3.03 %7.17 %?
    Minimum buckets/node **)256 - 6413108 - 32983886 -
    - -

    Strict mode (not default)

    -

    - The strict mode attempts to keep the waste below 1.0 %. - When it needs to increase the bit count it increases the bit count significantly - to allow considerable more growth before having to adjust the count again. -

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    Node count1-45-1415-199200-799800-14991500-49995000->
    Distribution bit count8162125283032
    Max calculated waste *)3 %0.83 %0.86 %0.67 %???
    Minimum buckets/node **)256 - 6413107 - 4681139810 - 10538167772 - 41995335544 - 179076715827 - 214791858993 -
    -

    - *) Max calculated waste, given redundancy 2 and the max node count in the given range, - as shown in the table above. - (Note that this assumes equal sized buckets, and that every possible bucket exist. - In a real system there will be random variation). -

    -

    - **) Given a node count and distribution bits, there is a minimum number of buckets enforced to exist. - However, splitting due to bucket size may increase this count beyond this number. - This value shows the maximum value of the minimum. - (That is the number of buckets per node enforced for the lowest node count in the range) - Ideally one wants to have few buckets enforced by distribution - and rather let bucket size split buckets, as that leaves more freedom to users. -

    - -

    Q/A

    -

    - Q: I have a cluster with multiple groups, with the same number of nodes (more than one) in - each group. Why does the first node in the first group store a slightly different number of documents - than the first node in the second group (and so on)? -

    -

    - A: This is both expected and intentional—to see why we must look at how the ideal state algorithm works. -

    -

    - As previously outlined, the ideal state algorithm requires 3 distinct inputs: -

    -
      -
    1. The ID of the bucket to be replicated across content nodes.
    2. -
    3. The set of all nodes (i.e. unique distribution keys) in the cluster across all groups, - and their current availability state (Down, Up, Maintenance etc.).
    4. -
    5. The cluster topology and replication configuration. The topology includes knowledge of all groups.
    6. -
    -

    - From this the algorithm returns a deterministic, ordered sequence of nodes (i.e. distribution keys) - across all configured groups. The ordering of nodes is given by their individual pseudo-random node - score, where higher scoring nodes are considered more ideal for storing replicas - for a given bucket. The set of nodes in this sequence respects the constraints given by the configured - group topology and replication level. -

    -

    - When computing node scores within a group, the absolute distribution keys are used rather - than a node's relative ordering within the group. This means the individual node scores—and - consequently the distribution of bucket replicas—within one group is different (with a very high - probability) from all other groups. -

    -

    - What the ideal state algorithm ensures is that there exists a deterministic, configurable number of replicas - per bucket within each group and that they are evenly distributed across each group's nodes—the exact mapping - can be considered an unspecified "implementation detail". -

    -

    - The rationale for using absolute distribution keys rather than relative ordering is closely related to - the earlier discussion about why modulo distribution is a poor - choice. Let \(N_g \gt 1\) be the number of nodes in a given group: -

    -
      -
    • - A relative ordering means that removing—or just reordering—a single node from the configuration can - potentially lead to a full redistribution of all data within that group, not just \( \frac{1}{N_g} \) - of the data. Imagine for instance moving a node from being first in the group to being the last. -
    • -
    • - If we require nodes with the same relative index in each group to store the same data set (i.e. - a row-column strategy), this immediately suffers in failure scenarios even when just a single node - becomes unavailable. - Data coverage in the group remains reduced until the node is replaced, as no other nodes can take over - responsibility for the data. This is because removing the node leads to the problem in the previous point, - where a disproportionally large amount of data must be moved due to the relative ordering changing. - With the ideal state algorithm, the remaining nodes in the group will transparently assume ownership - of the data, with each node receiving an expected \( \frac{1}{N_g - 1} \) of the unavailable node's buckets. -
    • -
    - diff --git a/en/content/idealstate.mdx b/en/content/idealstate.mdx new file mode 100644 index 0000000000..110e5462cd --- /dev/null +++ b/en/content/idealstate.mdx @@ -0,0 +1,1415 @@ +--- +title: "Distribution algorithm" +--- + +The distribution algorithm decides what nodes should be responsible for a given bucket. This is used directly in the clients to calculate distributor to talk to. Content nodes need time to move buckets when the distribution is changing, so routing to content nodes is done using tracked current state. The distribution algorithm decides which content nodes is wanted to store the bucket copies though, and due to this, the algorithm is also referred to as the ideal state algorithm. + +The input to the distribution algorithm is a bucket identifier, together with knowledge about what nodes are available, and what their capacities are. + +The output of the distribution algorithm is a sorted list of the available nodes. The first node in the order is the node most preferred to handle a given bucket. Currently, the highest order distributor node will be the owning distributor, and the redundancy factor decides how many of the highest order content nodes are preferred to store copies for a bucket. + +To enable minimal transfer of buckets when the list of available nodes changes, the removal or addition of nodes should not alter the sort order of the remaining nodes. + +Desired qualities for the ideal state algorithm: + + + + + + + + + + + + + + + + + + + + + + +
    **Minimal reassignment on cluster state change**- If a node goes down, only buckets that resided on that node should be reassigned.
    - If a node comes up, only buckets that are moved to the new node should relocate.
    - Increasing the capacity of a single node should only move buckets to that node.
    - Reducing the capacity of a single node should only move buckets away from that node.
    **No skew in distribution**- Nodes should get an amount of data relative to their capacity.
    **Lightweight**- A simple algorithm that is easy to understand is a plus. Being lightweight to calculate is also a plus, giving more options of how to use it, without needing to cache results.
    + +## Computational cost + +When considering how efficient the algorithm have to be, it is important to consider how often we need to calculate the ideal locations. Calculations are needed for the following tasks: + +- A client needs to map buckets to the distributors. If there are few buckets existing, all the results can be cached in clients, but for larger clusters, a lot of buckets may need to exist to create an even distribution, and caching becomes more memory intensive. Preferably the computational cost is cheap enough, such that no caching is needed. Currently, no caching is done by clients, but there is typically less than a million buckets, so caching all results would still have been viable. +- Distributors need to calculate ideal state for a single bucket to verify that incoming operations are mapped to the correct distributor (clients have cluster state matching the distributor). This could be eliminated for buckets pre-existing in the bucket database, which would be true in most all cases. Currently, calculation is done for all requests. +- Distributors need to calculate correct content nodes to create bucket copies on when operations to currently non-existing buckets come in. This is typically only something happening at the start of the cluster lifetime though. Normally buckets are created through splitting or joining existing buckets. +- Distributors need to calculate ideal state to check if any maintenance operations need to be done for a bucket. +- Content nodes need to calculate ideal state for a single bucket to verify that the correct distributor sent the request. This could be cached or served through bucket database but currently there is no need. + +As long as the algorithm is cheap, we can avoid needing to cache the result. The cache will then not limit scalability, and we have less dependencies and complexity within the content layer. The current algorithm has shown itself cheap enough, such that little caching has been needed. + +## A simple example: Modulo + +A simple approach would be to use a modulo operation to find the most preferred node, and then just order the nodes in configured order from there, skipping nodes that are currently not available: + + +$$ +most\ preferred\ node = bucket\ \%\ nodecount +$$ + +Properties: + +- Computational lightweight and easy to understand +- Perfect distribution among nodes. +- Total redistribution on state change. + +By just skipping currently unavailable nodes, nodes can go down and up with minimal movement. However, if the number of configured nodes change, practically all buckets will be redistributed. As the content layer is intended to be scalable, this breaks with one of the intentions and this algorithm has thus not been considered. + +## Weighted random election + +This is the algorithm that is currently used for distribution in the content layer, as it fits our use case well. + +To avoid a total redistribution on state change, the mapping can not be heavily dependent on the number of nodes in the cluster. By using random numbers, we can distribute the buckets randomly between the nodes, in such a fashion that altering the cluster state has a small impact. As we need the result to be reproducible, we obviously need to use a pseudo-random number generator and not real random numbers. + +The idea is as follows. To find the location of a given bucket, seed a random number generator with the bucket identifier, when draw one number for each node. The drawn numbers will then decide upon the preferred node order for that specific bucket. + +For this to be reproducible, all nodes need to draw the same numbers each time. Each node is assigned a distribution key in the configuration. This key decides what random number the node will be assigned. For instance, a node with distribution key 13, will be assigned the 14th random number generated. (As the first will go to the node with key 0). The existence of this node then also requires us to always generate at least 14 random numbers to do the calculation. + +Thus, one may end up calculating random numbers for nodes that are currently not available, either because they are temporarily down, or because the configuration have left holes in the distribution key space. It is recommended to not leave too large holes in the distribution key space to not waste too much. + +Using this approach, if you add another node to the cluster, it will roll for each bucket. It should thus steal ownership of some of the buckets. As all the numbers are random, it will steal buckets from all the other nodes, thus, given that the bucket count is large compared to the number of nodes, it will steal on average 1/n of the buckets from each pre-existing node, where n is the number of nodes in the current cluster. Likewise, if a node is removed from the cluster, the remaining nodes will divide the extra load between them. + +### Weighting nodes + +By enforcing all the numbers drawn to be floating point numbers between 0 and 1, we can introduce node weights using the following formula: + +$$ +r^{1/c} +$$ + +Where r is the floating point number between 0 and 1 that was drawn for a given node, and c is the node capacity, which is the weight of the node. Proof not included here, but this will end up giving each node on average an amount of data that is relative to its capacity. That is, among any nodes there are two nodes X and Y, where the number of buckets given to X should be equal to the number of buckets given to Y multiplied by capacity(X)/capacity(Y). (Given perfect random distribution) + +Altering the weight in a running system will also create a minimal redistribution of data. If we reduce the capacity, all the nodes number will be reduced, and some of its buckets will be taken over by the other nodes, and vice versa if the capacity is increased. Properties: + +- Minimum data movement on state changes. +- Some skew, depending on how good the random number generator is, the amount of nodes we have to divide buckets between, and the number of buckets we have to divide between them. +- Fairly cheap to compute given a reasonable amount of nodes, and an inexpensive pseudo-random number generator. + +### Distribution skew + +The algorithm does generate a bit of skew in the distribution, as it is essentially random. The following attributes decrease the skew: + +- Having more buckets to distribute. +- Having less targets (nodes and partitions) to distribute buckets to. +- Having a more uniform pseudo-random function. + +The more buckets exist, the more metadata needs to be tracked in the distributors though, and operations that wants to scan all the buckets will take longer. Additionally, the backend may want buckets above a given size to improve performance, storage efficiency or similar. Consequently, we typically want to enforce enough buckets for a decent distribution, but not more. + +Then the number of nodes increase, more buckets need to exist to keep the distribution even. If the number of nodes is doubled, the number of buckets must typically more than double to keep the distribution equally even. Thus, this scales worse than linear. It does not scale much worse though, and this has not proved to be a practical problem for the cluster sizes we have used up until now. (A cluster size of a thousand nodes does not seem to be any issue here) + +Having a good and uniform pseudo-random function makes the distribution more even. However, this may require more computationally heavy generators. Currently, we are using a simple and fast algorithm, and it has proved more than sufficient for our needs. + +The distribution to distributors are done to create an even distribution between the nodes. The distributors are free to split the buckets further if the backend wants buckets to contain less data. They can not use fewer buckets than are needed for distribution though. By using a minimum amount of buckets for distribution, the distributors have more freedom to control sizes of buckets. + +### Distribution waste + +To measure how many buckets are needed to create a decent distribution a metric is needed. We have defined a waste metric for this purpose as follows: + +Distribute the buckets to all the units. Assume the size of all units are identical. Assume the unit with the most units assigned to it is at 100% capacity. The wasted space is the percentage of unused capacity compared to the used capacity. + +This definition seems useful as a cluster is considered at full capacity once one of its partitions is at full capacity. Having one node with more buckets than the rest is thus damaging, while having one node with fewer buckets than the rest is just fine. + +Example: There are 4 nodes distributing 18 units. The node with the most units has 6. Distribution waste is `100% * (4 * 6 - 18) / (4 * 6) = 25%`. + +Below we have calculated waste based on number of nodes and the amount of buckets to distribute between them. Bits refer to distribution bits used. A distribution bit count of 16 indicates that there will be 216 buckets. + +The calculations assume all buckets have the same size. This is normally close to true as documents are randomly assigned to buckets. There will be lots of buckets per node too, so a little variance typically evens out fairly well. + +The tables below assume only one partition exist on each node. If you have 4 partitions on 16 nodes, you should rather use the values for `4 * 16 = 64` nodes. + +A higher redundancy factor indicates more buckets to distribute between the same amount of nodes, resulting in a more even distribution. Doubling the redundancy has the same effect as adding one to the distribution bit count. To get values for redundancy 4, the redundancy 2 values can be used, and then the waste will be equal to the value with one less distribution bit used. + +### Calculated waste from various cluster sizes + +A value of 1 indicates 100% waste. A value of 0.1 indicates 10% waste. A waste below 1 % is shown green, below 10% as yellow and below 30% as orange. Red indicates more than 30% waste. + +#### Distribution with redundancy 1: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Bits \ Nodes123456789101112131415
    10.00000.00000.33330.50000.60000.66670.71430.75000.77780.80000.81820.83330.84620.85710.8667
    20.00000.33330.33330.50000.20000.33330.42860.50000.55560.60000.63640.66670.69230.71430.7333
    30.00000.20000.11110.33330.20000.33330.61900.66670.82220.84000.85450.83330.69230.71430.7333
    40.00000.11110.11110.33330.36000.33330.42860.50000.77780.80000.81820.80950.69230.71430.6444
    5-0.05880.11110.27270.28890.40740.23810.33330.81290.83160.84690.85190.83590.83670.8359
    6-0.00000.07250.15790.14670.11110.16880.38460.70370.72170.74700.74600.72650.69520.6718
    7-0.07250.05190.08570.08570.11110.20500.20000.45300.46670.51520.51520.45300.39050.3436
    8-0.00000.00780.07250.08570.09220.12930.13510.16340.17420.16880.23810.24260.29670.3173
    9-0.00390.01920.14670.16070.12030.10800.11110.13800.13220.12180.17950.19620.23810.2580
    10-0.00190.02750.09220.08980.06230.07410.09220.11110.10180.12180.12030.14380.16880.1675
    11-0.00190.02340.04300.03850.02480.02480.04830.06360.06480.07370.07250.08940.08000.0958
    12--0.01210.02850.02820.01210.01490.05710.05770.05620.05490.04120.05100.04390.0616
    13--0.00740.00190.00700.01770.03040.03030.03370.01890.02520.03580.04090.05010.0385
    14--0.00410.00240.00370.00270.01450.00730.01010.01300.02200.02340.02900.02480.0195
    15--0.00190.00210.00360.00830.00590.00560.01010.00970.01230.01630.01500.01860.0173
    16--0.00100.00070.00100.00300.00490.00390.00850.00720.00970.01080.01350.01410.0115
    17-----0.00300.00330.00240.00360.00300.00550.00910.01350.01560.0143
    18------0.0019-0.00290.00270.00430.00400.00660.00610.0060
    19--------0.0019-0.00210.00300.00230.00310.0042
    20-----------0.00290.00250.00370.0044
    21------------0.00260.00350.0040
    + +#### Distribution with redundancy 2: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Bits \ Nodes123456789101112131415
    10.00000.00000.33330.50000.60000.66670.42860.50000.55560.60000.63640.66670.69230.71430.7333
    20.00000.00000.33330.33330.20000.33330.42860.50000.55560.60000.63640.66670.69230.42860.4667
    30.00000.00000.11110.20000.20000.33330.42860.50000.70370.73330.75760.77780.79490.77140.7333
    40.00000.00000.11110.20000.20000.33330.34690.20000.74600.77140.77620.77780.79490.77140.7630
    5--0.07250.15790.24710.23810.29670.27270.72650.75380.76730.77780.79490.79220.7968
    6--0.05190.11110.17420.14670.20500.23810.69080.70230.70160.71170.72650.72290.7247
    7--0.03030.01540.03400.03030.08570.11110.49210.48800.48280.47970.50770.46220.4667
    8--0.00780.03030.02480.06230.08570.07250.09700.13220.10490.12930.16200.18730.2242
    9--0.00190.02660.05190.04660.06820.07910.08240.05190.06910.05190.06230.07410.0898
    10--0.00630.01730.01540.02750.01160.03400.05580.02940.04520.04660.05670.05010.0584
    11--0.00780.00490.01540.01770.01490.02100.02750.01770.02520.03030.03050.03440.0317
    12---0.00730.01120.01920.02310.03120.02960.01770.02780.03580.02450.03120.0385
    13---0.00610.00490.00960.01120.02010.02180.00880.00770.01990.01380.03040.0317
    14---0.00590.00580.00580.00570.00920.01280.00820.01390.00810.00960.01990.0213
    15----0.00140.00390.00520.00340.00510.00850.00440.00720.01070.01010.0082
    16----0.00160.00300.00260.00360.00650.00510.00610.00840.00650.00830.0100
    17------0.00100.00200.0028-0.00400.00490.00670.00710.0062
    18--------0.0032-0.0024-0.00340.00560.0041
    19------------0.00250.0018-
    + +#### Distribution with redundancy 2: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Bits \ Nodes162032486410012816020025635050080010005000
    80.20000.30810.27270.51520.52940.57330.63640.70910.76730.80000.85370.88620.89330.89760.9659
    90.07250.22420.17950.17950.30430.31730.38460.50770.53450.63640.73400.79520.84000.87200.9317
    100.07250.13220.12330.20990.15790.24150.33330.57330.46110.57890.65580.72690.82930.84250.8976
    110.03400.08570.09220.11110.12330.19690.25580.59370.56430.58970.59650.60990.65870.75910.8830
    120.04480.03850.06230.10650.09860.12850.37250.38310.40640.40740.47990.48800.51240.83280.8976
    130.03400.03280.05540.06990.06230.09480.10490.21830.23440.31910.34980.45390.57330.66560.8870
    140.01400.01890.03760.04520.04660.07170.09860.10570.10470.22420.28530.27980.40640.49590.8830
    150.00940.01180.03850.02680.03310.06380.07080.07750.08980.13220.21330.21040.35500.44460.8752
    160.00970.00810.03800.03030.03620.05770.05010.06270.07170.10330.17330.16780.25860.31010.8511
    170.00750.00660.03460.02930.01540.02580.04660.05460.07040.10410.14690.19830.27020.29720.7740
    180.00530.00570.00980.00980.01220.01490.02380.03000.03940.03530.04340.05530.06110.17820.6334
    19-0.00220.00500.01620.00980.01330.01490.02200.02420.02520.03330.03980.04950.09990.5145
    20--0.00300.01070.00880.00980.01440.01400.01480.02030.01950.02550.03480.11330.4481
    21--0.00430.00630.00510.00740.00790.00850.00860.01130.01470.01700.02370.10680.4422
    22---0.00260.00350.00370.00820.00610.00770.00870.01010.01340.01930.11400.4635
    23---0.0019-0.00260.00800.00550.00560.00570.00630.00960.01550.12940.4982
    24---0.0013--0.00740.00600.00580.00530.00490.00680.01120.04710.3219
    25---------0.00430.00430.00580.00670.05120.2543
    26----------0.00400.00420.00430.00510.0210
    27------------0.00280.01570.0814
    + +### Default number of distribution bits used + +Note that changing the amount of distribution bits used will change what buckets exist, which will change the distribution considerably. We thus do not want to alter the distribution bit count too often. + +Ideally, the users would be allowed to configure minimal and maximal acceptable waste, and the current amount of distribution bits could then just be calculated on the fly. But as computing the waste values above are computational heavy, especially with many nodes and many distribution bits, currently only a couple of profiles are available for you to configure. + +**Vespa Cloud note:** Vespa Cloud locks distribution bit count to 16. This is because Vespa Cloud offers auto-scaling of nodes, and such a scaling decision should not implicitly lead to a full redistribution of data by crossing a distribution bit node count boundary. 16 bits strikes a good balance of low skew and high performance for most production deployments. + +#### Loose mode (default) + +The loose mode allows for more waste, allowing the amount of nodes to change considerably without altering the distribution bit counts. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Node count1-45-199200->
    Distribution bit count81624
    Max calculated waste *)3.03 %7.17 %?
    Minimum buckets/node **)256 - 6413108 - 32983886 -
    + +#### Strict mode (not default) + +The strict mode attempts to keep the waste below 1.0 %. When it needs to increase the bit count it increases the bit count significantly to allow considerable more growth before having to adjust the count again. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Node count1-45-1415-199200-799800-14991500-49995000->
    Distribution bit count8162125283032
    Max calculated waste *)3 %0.83 %0.86 %0.67 %???
    Minimum buckets/node **)256 - 6413107 - 4681139810 - 10538167772 - 41995335544 - 179076715827 - 214791858993 -
    + +*) Max calculated waste, given redundancy 2 and the max node count in the given range, as shown in the table above. (Note that this assumes equal sized buckets, and that every possible bucket exist. In a real system there will be random variation). + +**) Given a node count and distribution bits, there is a minimum number of buckets enforced to exist. However, splitting due to bucket size may increase this count beyond this number. This value shows the maximum value of the minimum. (That is the number of buckets per node enforced for the lowest node count in the range) Ideally one wants to have few buckets enforced by distribution and rather let bucket size split buckets, as that leaves more freedom to users. + +## Q/A + + +A: This is both expected and intentional—to see why we must look at how the ideal state algorithm works. + +As previously outlined, the ideal state algorithm requires 3 distinct inputs: + +1. The ID of the bucket to be replicated across content nodes. +2. The set of all nodes (i.e. unique distribution keys) in the cluster *across* all groups, and their current availability state (Down, Up, Maintenance etc.). +3. The cluster topology and replication configuration. The topology includes knowledge of all groups. + +From this the algorithm returns a deterministic, ordered sequence of nodes (i.e. distribution keys) across all configured groups. The ordering of nodes is given by their individual pseudo-random node *score*, where higher scoring nodes are considered more *ideal* for storing replicas for a given bucket. The set of nodes in this sequence respects the constraints given by the configured group topology and replication level. + +When computing node scores within a group, the *absolute* distribution keys are used rather than a node's *relative* ordering within the group. This means the individual node scores—and consequently the distribution of bucket replicas—within one group is different (with a very high probability) from all other groups. + +What the ideal state algorithm ensures is that there exists a deterministic, configurable number of replicas per bucket within each group and that they are evenly distributed across each group's nodes—the exact mapping can be considered an unspecified "implementation detail". + +The rationale for using absolute distribution keys rather than relative ordering is closely related to the earlier discussion about why [modulo distribution](/en/content/idealstate#a-simple-example-modulo) is a poor choice. Let $N_g \gt 1$ be the number of nodes in a given group: + +- A relative ordering means that removing—or just reordering—a single node from the configuration can potentially lead to a full redistribution of all data within that group, not just $\frac{1}{N_g}$ of the data. Imagine for instance moving a node from being first in the group to being the last. +- If we require nodes with the same relative index in each group to store the same data set (i.e. a row-column strategy), this immediately suffers in failure scenarios even when just a single node becomes unavailable. Data coverage in the group remains reduced until the node is replaced, as no other nodes can take over responsibility for the data. This is because removing the node leads to the problem in the previous point, where a disproportionally large amount of data must be moved due to the relative ordering changing. With the ideal state algorithm, the remaining nodes in the group will transparently assume ownership of the data, with each node receiving an expected $( \frac{1}{N_g - 1} )$ of the unavailable node's buckets. + \ No newline at end of file diff --git a/en/content/images/elastic-fail.png b/en/content/images/elastic-fail.png new file mode 100644 index 0000000000000000000000000000000000000000..d87fa40cdf3c1ff16e67d3a7b3aaf6eef68143f3 GIT binary patch literal 10403 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 literal 0 HcmV?d00001 diff --git a/en/content/images/query-groups.png b/en/content/images/query-groups.png new file mode 100644 index 0000000000000000000000000000000000000000..b0f5332f3a1b5154a5688054761034233c822d35 GIT binary patch literal 9813 zcmeHNc~p~E7XOfzv|8cVcB@xBYI zz7ANk-LOKPlaS9?u{KVd#yocH<~#LU>P=dfzQI>lY6`xdVy!$7QWI#t2y{VFpE+Q_ zC*i1*2l#LrU4b1C^bAGf06tNLzqQ3au1gRjEH#H`>^j5{R;LMT5A)Zw(W`w-w{Jyi zdLqM-p>zRUx!YP&r1v)LK@$0vFnl*r_Jyq@0x7Z~IQ6i%qAYcYra`FUsKyFeuSb^_ zfthsmdtD+X`7-Y%_+UvGxzD-bc@RAZMXW#UYW7t&A{{>8+_d^pf}C z*#3)ys+=E*Q4nbREp(LXl-fi^q>Zw+GBg=}C^q1HT z8CGvZxJP>tCe6DuYk~nWtGbl8(x*Sl3n(~g=~s8q@w~{Pp+HS;-Vg2UB+3U3 z&Cvam{4_5{mMVuw%Whbx@-qj%=nD3-S@Bc58zc*$>?8tte7yQW&}O{!mU23au++KO z%wfPiB+<3546;Y}p@@X}t50Z`6+Rq$bhX5ldiDOJtq%#>Go#~)AjRa8xg?oiO>`<8 zVZO5Q%iWcyqe}Mk#l7|n_XzpD+i>R$EdhC_SO1p ze6r~VJu&=&F1y?rS866Bj^w3uWEBgX(>Tj&?KRi5Kn8%|KpE3MJ2b&i51376B5xWGTQ^~${j?UmC>3_oh zXSTl@{q@CGjwAS74*C@q`_{^vMdJcJvFZN)24x@dN+bm3uA)L5_eP+!Zz~v@)Z6@k z8G#>yNvZZVV3=g*0WdqIJPoX&J_V+!|0w!f9J79Y?o0XDpiwZ$CU4E8UwrBauWud1 z*@n8E`xffiBk~|K|K(C=+9}99LWrdB+4YlmZy%_0n5ppgZ&p#wrTWg%eV=FxrhI62^=PfHPa-Pn zcf(}}&~t~oUy`u_1*`}FmbiZompc#Rk`9bJmuv75iA?`&@DS!3u;_WqpoA6Z!Y3e; zD@Knp2>BGgbAWd-!3~5f9F0pEh}NCU_d4jlokL(p+tT?RymZy);%;8^(80;foWOCg z@MrX>B5)LiY?9G^uVMB}f_)**`>5~f)x6=*&sU~xjKeCi`uqZyh}I@|Nb(XWg3Wlq zEVNR6!RWrqHV`c{u*wfT>VjWCp-fQIbo^+#zNf&26oKw|9o-SWU(zcSkeZLbR*V*& zD-Q$&jmrAud01A?!*X^%z*q)SGo)NF-t2Frm<9#{efS_O3BjnEvUyZ0Y+qf0kJW%n z-$67i^U1=-r(FhN=>Q29^%073@)JSWtbA^hzs4lBo}oMnU0w^SygH6~6$-x%s|*q|4s97oBz zsW94*+@c8bFlUnqrA5-0S#0V{N^HJu-F2e8+k`pyC0-eiDL?hBAb)-wb1qj( zhqjyb^q+c6@Z}SsGC(gM)zd0tu+{N-fp65RiKlwRuGWfJDh$bbb1;EX-6J$%n)n)pUl zP9RQ}5neTvRQS!(g3G(?&tN6&YM?b_QC#`!XF$IthN*ctH+EaB*}l)v~L}HRNp)B zVHlXBn>EL|)^^A_$T|gHHU0`f0{QveF^MVgxJ0Mo*?JEFRL%laK6ES>f9e>4*v8rd zI=wm7poUI)P_0rAs%4xHSAS}QDgjMyzs(s+T{LYGHBbh>%UtVF-s+vmU=U7hT2LQg z!9KNBR%KKadlcs7^CA#;aF={)w(=5yGmsu&k{YG)S7z@~F>kJ#6zsJ8D%OaD!M)KN%Ewl@8bvCH% za6mK%e(vThZ!}}hc>>n>%36XqDa^W{en8ocvSO~P+88@1CX~QF7K&&B)HLXA+sE(y zZ7Q9yk8I4z+7zoCy$EB#VjWlnQ?dG!dLom+j-l@Y!^AaQLwQVr6&Fh1o4*4g15P~* z*yCN$D_|M%YK$tGSe~p{hrA+9n-Zi4aLdQy3p&uNu*d>>L>uT4E}*59r0XN<=|_dN zZg@OjqwRe%tiTotC~TbxlduCc%3t^~ezA=$wz0)Fw%Gh7N#(rmQ4BYPipZ~Z*Fjp@ebc#GEZv+QHJ6?_0TK-DHyZ;7&g%TV9 literal 0 HcmV?d00001 diff --git a/en/content/proton.html b/en/content/proton.html deleted file mode 100644 index bfeef8f838..0000000000 --- a/en/content/proton.html +++ /dev/null @@ -1,1011 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Proton" -redirect_from: -- /en/reference/search-custom-state-api.html -- /en/proton.html ---- - -

    - Proton is Vespa's search core and runs on each content node as the vespa-proton-bin process. - Proton maintains disk and memory structures for documents (organized per document type), - handles read and write operations, - and execution of queries. - As the document data is dynamic, disk and memory structures are periodically optimized by - maintenance jobs. -

    -

    - The content node has a bucket management system - which sends requests to a set of document databases, - which each consists of three sub-databases - ready, not ready and removed: -

    -Proton feed overview - - - -

    Bucket management

    -

    -When the node starts up it first needs to get an overview of what documents and buckets it has. -Once metadata for all buckets is known, the content nodes transition from down to up state. -As the distributors want quick access to bucket metadata, it maintains an in-memory bucket -database to efficiently serve these requests. -The state of the bucket database can always be reconstructed from the durably persisted -search node state, but this is expensive and therefore only happens at process startup time. -

    -

    -This database is considered the source of truth for the state of the node's bucket metadata -for the duration of the process's lifetime. As incoming operations mutate the state of the -documents on the node, it is critical that the database is always kept in sync with these changes. -

    - -

    Persistence threads and operation dispatching

    -

    -A content node has a pool of persistence threads that is created at startup -and remains fixed in size for the lifetime of the process. It is the responsibility of the -persistence threads to schedule incoming write and read operations received by the content -node, dispatch these to the search core, and ensure the bucket database remains in sync -with changes caused by write operations. -

    -

    -Unless explicitly configured, the size of the thread pool is automatically set based on the -number of CPU cores available. -

    -

    -Persistence threads are backed by a persistence queue. Read/write-operations received -by the RPC subsystem are pushed onto this queue. The queue is operation deadline-aware; if -an operation has exceeded its deadline while enqueued, it is immediately failed back to -the sender without being executed. This avoids a particular failure scenario where a heavily -loaded node spends increasingly more and more time processing already doomed operations, -due to not being able to drain its queue quickly enough. -

    -

    -All operations bound for a particular data bucket (such as Puts, Gets, etc.) execute in the -context of a bucket lock. Locks are shared for reads and exclusive for writes. -This means that multiple read operations can execute in parallel for the same bucket, but -only one write operation can execute for a bucket at any given time (and no reads can be -started concurrently with existing writes for a bucket, and vice versa). Note that some of -these locking restrictions can be relaxed when it's safe to do so—see -performance optimizations for details. -

    -

    -If a persistence thread tries to pop an operation from the queue and sees that the bucket it's -bound for is already locked, it will leave the operation in place in the queue and try the next -operation(s) instead. This means that although the queue acts as a FIFO for client operations -towards a single bucket, this is not the case across multiple buckets. -

    -

    Write operations

    -

    -Write operations are dispatched as asynchronous—i.e., non-blocking—tasks to the search core. -This increases parallelism by freeing up persistence threads to handle other operations, and a deeper -pipeline enables the search core to optimize transaction log synchronization and batching of data -structure updates. -

    -

    -Since a deeper pipeline comes at the potential cost of increased latency when many operations are in -flight, the maximum number of concurrent asynchronous operations is bounded by an adaptive persistence -throttling mechanism. The throttler will dynamically scale the window of concurrency until it reaches a -saturation point where further increasing the window size also results in increased operation latencies. -When the number of in-flight operations hits the current maximum, persistence threads will not -dispatch any more writes until the number goes down. Reads can still be processed during this time. -

    -

    -An asynchronous write-task holds on to the exclusive bucket lock for the duration of its lifetime. -Once the search core completes the write operation, the bucket database is updated with the new metadata -state of the bucket (which reflects the side effects of the write) prior to releasing the lock. -An operation reply is then generated and sent back via the RPC subsystem. -

    -

    Read operations

    -

    -Read operations are always evaluated synchronously—i.e. blocking—by persistence threads. -To avoid having potentially expensive maintenance read operations (such as those used for -replica reconciliation) block client -operations for prolonged amounts of time, a subset of the persistence threads are not allowed -to process such maintenance operations. -

    -

    -Note that the condition evaluation step of a test-and-set write is considered a read sub-operation -and is therefore done synchronously. Since it's part of a write operation, it happens atomically in -the context of the exclusive lock of the higher-level operation. -

    -

    Performance optimizations

    -

    -To reduce thread context switches, some write operations may bypass the persistence thread queues -and be directly asynchronously dispatched to the search core from the RPC thread the operation -was received at. -Such operations must still successfully acquire the appropriate exclusive bucket lock—if the -lock cannot be immediately acquired the operation is pushed onto the persistence queue instead. -

    -

    -To reduce lock contention and thread wakeups, smaller numbers of persistence threads are grouped -together in stripes that share a dedicated per-stripe persistence queue. -Operations are routed deterministically to a particular stripe based on their bucket ID, meaning -that stripes operate on non-overlapping parts of the bucket space. Together, the many stripes and -queues form one higher-level logical queue that covers the entire bucket space. -

    -

    -If the queue contains multiple non-conflicting write operations to the same bucket, these -may be dispatched in parallel in the context of the same write lock. This avoids having -to wait for an entire lock-execute-unlock roundtrip prior to dispatching the next write for the -same bucket. An example of conflicting writes is multiple Puts to the same document ID. -The maximum number of operations dispatched in parallel is implementation-defined. -

    - -

    Document database

    -

    -Each document database is responsible for a single document type. -It has a component called FeedHandler which takes care of incoming documents, updates, and remove requests. -All requests are first written to a transaction log, -then handed to the appropriate sub-database, based on the request type. -

    - - -

    Sub-databases

    -

    -There are three types of sub-databases, each with its own -document meta store and document store. -The document meta store holds a map from the document ID to a local ID. -This local id is used to address the document in the document store. -The document meta store also maintains information on the -state of the buckets that are present in the sub-database. -

    -The sub-databases are maintained by the Maintenance Controller. -The document distribution changes as the system is resized. -When the number of nodes in the system changes, -the Maintenance Controller will move documents between the Ready and -Not Ready sub-databases to reflect the new distribution. -When an entry in the Removed sub-database gets old, it is purged. -The sub-databases are: -

    - - - - - - - - - - - - - -
    Not Ready - Holds the redundant documents that are not searchable, - i.e. the not ready documents. - Documents that are not ready are only stored, not indexed. - It takes some processing to move from this state to the ready state. -
    Ready - Maintains attributes and indexes of all ready documents and keeps them searchable. - One of the ready copies is active while the rest are not active: - - - - - - - - - -
    ActiveThere should always be exactly one active copy of each - document in the system, though intermittently there may be more. - These documents produce results when queries are evaluated.
    Not ActiveThe ready copies that are not active are indexed but will not produce results. - By being indexed, they are ready to take over immediately - if the node holding the active copy becomes unavailable. - Read more in searchable-copies.
    -
    RemovedKeeps track of documents that have been removed. - The id and timestamp for each document are kept. - This information is used when buckets from two nodes are merged. - If the removed document exists on another node but with a different timestamp, - the most recent entry prevails. -
    - - -

    Transaction log

    -

    -Content nodes have a transaction log to persist mutating operations. -The transaction log persists operations by file append. -Having a transaction log simplifies proton's in-memory index structures -and enables steady-state high performance, read more below. -

    -All operations are written and synced to the transaction log. -This is sequential (not random) IO, but might impact overall feed performance if running on NAS attached storage -where the sync operation has a much higher cost than on local attached storage (e.g., SSD). -See sync-transactionlog. -

    -

    -By default, proton will -flush components -like attribute vectors and memory index on shutdown, for quicker startup after scheduled restarts. -

    - - - -

    Document store

    -

    - Documents are stored as compressed serialized blobs in the document store. - Put, update and remove operations are persisted in the transaction log - before updating the document in the document store. - The operation is acked to the client and the result of the operation is immediately seen in search results. -

    -

    - Files in the document store are written sequentially, and occur in pairs - example: -

    --rw-r--r-- 1 owner users 4133380096 Aug 10 13:36 1467957947689211000.dat
    --rw-r--r-- 1 owner users   71192112 Aug 10 13:36 1467957947689211000.idx
    -
    -

    - The maximum size: - (in bytes) per .dat file on disk can be set using the following: -

    -
    -<content id="mycluster" version="1.0">
    -  <engine>
    -    <proton>
    -      <tuning>
    -        <searchnode>
    -          <summary>
    -            <store>
    -              <logstore>
    -                <maxfilesize>8000000000</maxfilesize>
    -
    -Notes: -
      -
    • The files are written in sequence. proton starts with one pair - and grows it until maxfilesize. Once full, a new pair is started.
    • -
    • This means the pair is immutable, except for the last pair, which is written to.
    • -
    • Documents exist in multiple versions in multiple files. - Older versions are compacted away when a pair is scheduled to be the new active pair - - obsolete versions are removed, leaving only the active document version left in a new file pair - - which is the new active pair.
    • -
    • Read more on implications of setting maxfilesize in - proton maintenance jobs.
    • -
    • Files are written in chunks, - using compression settings. -
    - - -

    Defragmentation

    -

    - Document store compaction, - defragments and sort document store files. - It removes stale versions of documents (i.e. old versions of updated documents). - It is triggered when the disk bloat of the document store is larger than the total disk usage of the document store - times - diskbloatfactor. - Refer to summary tuning for details. -

    -

    - Defragmentation status is best observed by tracking the - max_bucket_spread - metric over time. - A sawtooth pattern is normal for corpora that change over time. - The document_store_compact - metric tracks when proton is running the document store compaction job. - Compaction settings can be set too tight, in that case, the metric is always, or close to, 1. -

    -

    - When benchmarking, it is important to set the correct compaction settings, - and also make sure that proton has compacted files since (can take hours), - and is not actively compacting (document_store_compact should be 0 most of the time). - -

    -{% include note.html content="There is no bucket-compaction across files - documents will not move between files."%} - - -

    Optimized reads using chunks

    -

    - As documents are clustered within the .dat file, - proton optimizes reads by reading larger chunks when accessing documents. - When visiting, documents are read in bucket order. - This is the same order as the defragmentation jobs use. -

    -

    - The first document read in a visit operation for a bucket will read a chunk from the .dat file into memory. - Subsequent document accesses are served by a memory lookup only. - The chunk size is configured by - maxsize: -

    -
    -<engine>
    -  <proton>
    -    <tuning>
    -      <searchnode>
    -        <summary>
    -          <store>
    -            <logstore>
    -              <chunk>
    -                <maxsize>16384</maxsize>
    -              </chunk>
    -            </logstore>
    -
    -

    - There can be 2^22=4M chunks. This sets a minimum chunk size based on maxfilesize - - e.g. an 8G file can have minimum 2k chunk size. - Finally, the bucket size is configured by setting - bucket-splitting: -

    -
    -<content id="imagepersonal" version="1.0">
    -  <tuning>
    -    <bucket-splitting max-documents="1024"/>
    -
    -

    The following are the relevant sizing units:

    -
      -
    • .dat file size - maxfilesize. - Larger files give fewer files and so better locality, - but compaction requires more memory and more time to complete.
    • -
    • chunk size - maxsize. - Smaller chunks give less wasted IO bytes but more IO operations.
    • -
    • bucket size - bucket-splitting. - Larger buckets give fewer buckets and better locality to nodes and files, but - incur more overhead during content layer bucket maintenance operations. - Overhead can be treated as linear in both CPU, memory and network usage - with the bucket size.
    • -
    - - -

    Memory usage

    -

    - The document store has a mapping in memory from local ID (LID) to position in a document store file (.dat). - Part of this mapping is persisted in the .idx-file paired to the .dat file. - The memory used by the document store is linear with the number of documents and updates to these. -

    - The metric content.proton.documentdb.ready.document_store.memory_usage.allocated_bytes - gives the size in memory - - use the metric API to find it. - A rule of thumb is 12 bytes per document. -

    - - - -

    Attributes

    -

    -Attribute fields are in-memory fields used for -matching, ranking, sorting and grouping. -Each attribute is a separate component that consists of a set of -data structures -to store values for that field across all documents in a sub-database. -Attributes are managed by the Ready sub-database. -Some attributes can also be managed by the Not Ready sub-database, -see high-throughput updates for details. -

    - - - -

    Index

    -

    - Index fields are string fields, used for text search. - Other field types are attributes - and summary fields. -

    -

    -The Index in the Ready sub-database consists of a memory index and one or more disk indexes. -Mutating document operations are applied to the memory index, -which is flushed regularly. -Flushed memory indexes are merged with the primary disk index. -

    -Proton stores position information in text indices by default, for proximity relevance - - posocc (below). -All the occurrences of a term are stored in the posting list, with its position. -This provides superior ranking features, -but is somewhat more expensive than just storing a single occurrence per document. -For most applications, it is the correct tradeoff, -since most of the cost is usually elsewhere and relevance is valuable. -

    -Applications that only need occurrence information for filtering -can use rank: filter -to optimize query performance, using only boolocc-files (below). -

    -The memory index has a dictionary per index field. -This contains all unique words in that field, with mapping to posting lists with position information. -The position information is used during ranking, see -nativeRank for details on how a text match score is calculated. -

    -The disk index stores the content of each index field in separate folders. -Each folder contains: -

    -
      -
    • Dictionary. Files: dictionary.pdat, - dictionary.spdat, dictionary.ssdat.
    • -
    • Compressed posting lists with position information. File: posocc.dat.compressed.
    • -
    • Posting lists with only occurrence information (bitvector). These are generated for common words. - Files: boolocc.bdat, boolocc.idx.
    • -
    -

    -Example: -

    -
    {% highlight sh%}
    -$ pwd
    -/opt/vespa/var/db/vespa/search/cluster.mycluster/n1/documents/myschema/0.ready/index/index.flush.1/myfield
    -$ ls -la
    -total 7632
    -drwxr-xr-x  2 org users     145 Oct 29 06:09 .
    -drwxr-xr-x 74 org users    4096 Oct 29 06:11 ..
    --rw-r--r--  1 org users    4096 Oct 29 06:11 boolocc.bdat
    --rw-r--r--  1 org users    4096 Oct 29 06:11 boolocc.idx
    --rw-r--r--  1 org users    8192 Oct 29 06:11 dictionary.pdat
    --rw-r--r--  1 org users    8192 Oct 29 06:11 dictionary.spdat
    --rw-r--r--  1 org users    4120 Oct 29 06:11 dictionary.ssdat
    --rw-r--r--  1 org users 7778304 Oct 29 06:11 posocc.dat.compressed
    -{% endhighlight %}
    -

    - Note that boolocc-files are empty if the number of occurrences is small, like in the example above. -

    - - - -

    Proton maintenance jobs

    -

    -The memory and disk data structures used in Proton are -periodically optimized by a set of maintenance jobs. -These jobs are automatically executed, and some can be tuned in -flush strategy tuning. -All jobs are described in the table below. -

    -There is only one instance of each job at a time - e.g., attributes are flushed in sequence. -When a job is running, its metric is set to 1 - otherwise 0. -Use this to correlate observed performance or resource usage with job runs - see Run metric below. -

    -The temporary resources used when jobs are executed are described in CPU, Memory and Disk. -The memory and disk usage metrics of components that are optimized by the jobs -are described in Metrics (with Metric prefix). -For a list of all available Proton metrics, refer to the searchnode metrics in the -Vespa Metric Set. -Metrics are available at the Metrics API. -

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    JobDescription
    attribute flush -

    - Flush an attribute vector from memory to disk, - based on the configuration in the - flush strategy. - This ensures that Proton starts quicker - see - flush on shutdown. - An attribute flush also releases memory after a LID-space compaction. -

    -
    CPULittle - one thread flushes to disk
    MemoryLittle - some temporary use
    DiskA new file is written too, so 2x the size of an attribute on disk until the old flush file is deleted.
    Run metriccontent.proton.documentdb.job.attribute_flush
    Metric prefixcontent.proton.documentdb.[ready|notready].attribute.memory_usage.
    Metrics - allocated_bytes.average
    - used_bytes.average
    - dead_bytes.average
    - onhold_bytes.average
    -
    memory index flush -

    - Flush a memory index to disk, then trigger disk index fusion. - The goal is to shrink memory usage by adding to the disk-backed indices. - Note: A high feed rate can cause multiple smaller flushed indices, like - $VESPA_HOME/var/db/vespa/search/cluster.name/n1/documents/doc/0.ready/index/index.flush.102 - - see the high index number. - Multiple smaller indices are a symptom of too small memory indices compared to the feed rate - - to fix, increase - flushstrategy > native > component > maxmemorygain. -

    -
    CPULittle - one thread flushes to disk
    MemoryLittle
    DiskCreates a new disk index, size of the memory index.
    Run metriccontent.proton.documentdb.job.memory_index_flush
    Metric prefixcontent.proton.documentdb.index.memory_usage.
    Metricsallocated_bytes.average
    - used_bytes.average
    - dead_bytes.average
    - onhold_bytes.average
    disk index fusion -

    - Merge the primary disk index with smaller indices generated by - memory index flush - - triggered by the memory index flush. -

    -
    CPUMultiple threads merge indices, configured as a function of - feeding concurrency - - refer to this for details
    MemoryLittle
    DiskCreates a new index while serving from the current: 2x temporary disk usage for the given index.
    Run metriccontent.proton.documentdb.job.disk_index_fusion
    document store flush -

    - Flushes the document store. -

    -
    CPULittle
    MemoryLittle
    DiskLittle
    Run metriccontent.proton.documentdb.job.document_store_flush
    document store compaction -

    - Defragment and sort document store files as documents are updated and deleted, - in order to reduce disk usage. - The file is sorted in bucket order on output. Triggered by - - diskbloatfactor. -

    -
    CPULittle - one thread reads one file, sorts and writes a new file
    Memory - Holds a document store file in memory plus memory for sorting the file. - Note: This is important on hosts with little memory! - Reduce - maxfilesize to increase the number of files and use less temporary memory for compaction. -
    DiskA new file is written while the current is serving, max temporary usage is 2x.
    Run metriccontent.proton.documentdb.job.document_store_compact
    Metric prefixcontent.proton.documentdb.[ready|notready|removed].document_store.
    Metricsdisk_usage.average
    - disk_bloat.average
    - max_bucket_spread.average
    - memory_usage.allocated_bytes.average
    - memory_usage.used_bytes.average
    - memory_usage.dead_bytes.average
    - memory_usage.onhold_bytes.average
    bucket move -

    - Triggered by nodes going up/down, refer to - content cluster elasticity and - searchable-copies. - It causes documents to be indexed or de-indexed, similar to feeding. - This moves documents between the ready and not ready sub-databases. -

    -
    CPUCPU similar to feeding. - Consumes capacity from the write threads, so has feeding impact
    MemoryAs feeding - e.g., the attribute memory usage and memory index in the ready sub-database will grow
    DiskAs feeding
    Run metriccontent.proton.documentdb.job.bucket_move
    lid-space compaction -

    - Each sub-database has a - document meta store - that manages a local document ID space (LID-space). - When a cluster grows with more nodes, - documents are redistributed to new nodes, and each node ends up with fewer documents. - The result is holes in the LID-space, and a compaction is triggered when the bloat is above 1%. - This in-place defragments the - document meta store - and attribute vectors - by moving documents from high to low LIDs inside the sub-database. - Resources are freed on a subsequent attribute flush. -

    -
    CPULike feeding - add and remove documents
    MemoryLittle
    Disk0
    Run metriccontent.proton.documentdb.job.lid_space_compact
    Metric prefixcontent.proton.documentdb.[ready|notready|removed].lid_space.
    Metricslid_limit.last
    - lid_bloat_factor.average
    - lid_fragmentation_factor.average
    removed documents pruning -

    - Prunes the removed sub-database, which keeps IDs for deleted documents. - See removed-db - for details. -

    -
    CPULittle
    MemoryLittle
    DiskLittle
    Run metriccontent.proton.documentdb.job.removed_documents_prune
    - - - -

    Retrieving documents

    -

    - Retrieving documents is done by specifying an id to get, - or use a selection expression - to visit a range of documents - refer to the Document API. - Overview: -

    -Retrieving documents - - - - - - - - -
    Get -

    - When the content node receives a get request, - it scans through all the document databases, - and for each one, it checks all three sub-databases. - Once the document is found, the scan is stopped and the document returned. - If the document is found in a Ready sub-database, - the document retriever will apply any changes that are stored in the - attributes before returning the document. -

    -
    Visit -

    - A visit request creates an iterator over each candidate bucket. - This iterator will retrieve matching documents from all sub-databases of all document databases. - As for get, attribute values are applied to document fields in the Ready sub-database. -

    -
    - - - -

    Queries

    -

    - Queries have a separate pathway through the system. - They do not use the distributor, nor do they go through the content node persistence threads. - They are orthogonal to the elasticity set up by the storage and retrieval described above. - How queries move through the system: -

    -Queries -

    - A query enters the system through the QR-server (query rewrite server) - in the Vespa Container. - The QR-server issues one query per document type to the search nodes: -

    - - - - - - - - -
    Container -

    - The Container knows all the document types and rewrites queries as a - collection of queries, one for each type. - Queries may have a restrict parameter, - in which case the container will send the query only to the specified document types. -

    -

    - It sends the query to content nodes and collects partial results. - It pings all content nodes every second to know whether they are alive, - and keeps open TCP connections to each one. - If a node goes down, the elastic system will make the documents available on other nodes. -

    -
    Content node matching -

    - The match engine - receives queries and routes them to the right document database based on the document type. - The query is passed to the Ready sub-database, where the searchable documents are. - Based on information stored in the document meta store, - the query is augmented with a blocklist that ensures only active documents are matched. -

    -
    - - -

    /state/v1 API

    -

    - Besides the common endpoints documented in the - /state/v1 API reference, Proton has additional endpoints - as part of the /state/v1 API that expose information about the internal state of a search node. - This API is available at http://host:stateport/state/v1/. -

    -

    - Run vespa-model-inspect - to find the JSON HTTP stateport: -

    -
    -vespa-model-inspect service searchnode
    -
    - -

    Initialization Progress API

    -

    - The initialization progress can be found by HTTP GET at http://host:stateport/state/v1/initialization. - This endpoint becomes available early during initialization of Proton when other endpoints are not yet available. - It gives a human-readable overview of the document databases and their attributes being loaded. - Note that this is not a stable API, and it will expand and change between releases. -

    -

    - Example state/v1/initialization: -

    -
    {% highlight json %}
    -{
    -  "state": "initializing",
    -  "current_time": "1758873251.933488",
    -  "start_time": "1758873249.715624",
    -  "load": 1,
    -  "replay_transaction_log": 0,
    -  "online": 0,
    -  "dbs": [
    -    {
    -      "state": "load",
    -      "start_time": "1758873249.936939",
    -      "name": "dbname",
    -      "ready_subdb": {
    -        "loaded_attributes": [
    -          {
    -            "state": "loaded",
    -            "start_time": "1758873249.941415",
    -            "name": "int_field",
    -            "end_time": "1758873249.942051"
    -          },
    -          {
    -            "state": "loaded",
    -            "start_time": "1758873249.941498",
    -            "name": "string_field",
    -            "end_time": "1758873249.944647"
    -          }
    -        ],
    -        "loading_attributes": [
    -          {
    -            "state": "reprocessing",
    -            "start_time": "1758873249.941555",
    -            "name": "tensor_field",
    -            "reprocess_progress": "6.061879",
    -            "reprocess_start_time": "1758873249.993847"
    -          }
    -        ],
    -        "queued_attributes": [
    -
    -        ]
    -      }
    -    }
    -  ]
    -}
    -{% endhighlight %}
    - -

    Custom Component State API

    -

    - The custom component status can be found by HTTP GET at http://host:stateport/state/v1/custom/component. - It gives an overview of the relevant search node components and their internal state. - Note that this is not a stable API, and it will expand and change between releases. -

    -

    - Example state/v1/custom/component: -

    -
    {% highlight json %}
    -{
    -    "documentdb": {
    -        "mydoctype": {
    -            "documentType": "mydoctype",
    -            "status": {
    -                "state": "ONLINE",
    -                "configState": "OK"
    -            },
    -            "documents": {
    -                "active": 10,
    -                "ready": 10,
    -                "total": 10,
    -                "removed": 0
    -            },
    -            "url": "http://host:stateport/state/v1/custom/component/documentdb/mydoctype"
    -        }
    -    },
    -    "threadpools": {
    -        "url": "http://host:stateport/state/v1/custom/component/threadpools"
    -    },
    -    "matchengine": {
    -        "status": {
    -            "state": "ONLINE"
    -        },
    -        "url": "http://host:stateport/state/v1/custom/component/matchengine"
    -    },
    -    "flushengine": {
    -        "url": "http://host:stateport/state/v1/custom/component/flushengine"
    -    },
    -    "tls": {
    -        "url": "http://host:stateport/state/v1/custom/component/tls"
    -    },
    -    "hwinfo": {
    -        "url": "http://host:stateport/state/v1/custom/component/hwinfo"
    -    },
    -    "resourceusage": {
    -        "url": "http://host:stateport/state/v1/custom/component/resourceusage",
    -        "disk": 0.25,
    -        "memory": 0.35,
    -        "attribute_address_space": 0
    -    },
    -    "session": {
    -        "search": {
    -            "url": "http://host:stateport/state/v1/custom/component/session/search",
    -            "numSessions": 0
    -        }
    -    }
    -}
    -{% endhighlight %}
    -

    - Example state/v1/custom/component/documentdb/mydoctype: -

    -
    {% highlight json %}
    -{
    -    "documentType": "mydoctype",
    -    "status": {
    -        "state": "ONLINE",
    -        "configState": "OK"
    -    },
    -    "documents": {
    -        "active": 10,
    -        "ready": 10,
    -        "total": 10,
    -        "removed": 0
    -    },
    -    "subdb": {
    -        "removed": {
    -            "url": "http://host:stateport/state/v1/custom/component/documentdb/mydoctype/subdb/removed"
    -        },
    -        "ready": {
    -            "url": "http://host:stateport/state/v1/custom/component/documentdb/mydoctype/subdb/ready"
    -        },
    -        "notready": {
    -            "url": "http://host:stateport/state/v1/custom/component/documentdb/mydoctype/subdb/notready"
    -        }
    -    },
    -    "threadingservice": {
    -        "url": "http://host:stateport/state/v1/custom/component/documentdb/mydoctype/threadingservice"
    -    },
    -    "bucketdb": {
    -        "url": "http://host:stateport/state/v1/custom/component/documentdb/mydoctype/bucketdb",
    -        "numBuckets": 1
    -    },
    -    "maintenancecontroller": {
    -        "url": "http://host:stateport/state/v1/custom/component/documentdb/mydoctype/maintenancecontroller"
    -    }
    -}
    -{% endhighlight %}
    diff --git a/en/content/proton.mdx b/en/content/proton.mdx new file mode 100644 index 0000000000..fc20374db3 --- /dev/null +++ b/en/content/proton.mdx @@ -0,0 +1,617 @@ +--- +title: "Proton" +sidebarTitle: "Content clusters" +description: "Proton is Vespa's search core and runs on each content node as the *vespa-proton-bin* process. Proton maintains disk and memory structures for documents (organized per document type), handles [read and write operations](/en/writing/reads-and-writes#operations), and execution of [queries](#queries). As the document data is dynamic, disk and memory structures are periodically optimized by [maintenance jobs](#proton-maintenance-jobs)." +--- + +The content node has a *bucket management system* which sends requests to a set of *document databases*, which each consists of three *sub-databases* `ready`, `not ready` and `removed`: + + +![](/assets/img/proton-feed.svg) + + + +### Bucket management + +When the node starts up it first needs to get an overview of what documents and buckets it has. Once metadata for all buckets is known, the content nodes transition from down to up state. As the distributors want quick access to bucket metadata, it maintains an in-memory bucket database to efficiently serve these requests. The state of the bucket database can always be reconstructed from the durably persisted search node state, but this is expensive and therefore only happens at process startup time. + +This database is considered the source of truth for the state of the node's bucket metadata for the duration of the process's lifetime. As incoming operations mutate the state of the documents on the node, it is critical that the database is always kept in sync with these changes. + +### Persistence threads and operation dispatching + +A content node has a pool of *persistence threads* that is created at startup and remains fixed in size for the lifetime of the process. It is the responsibility of the persistence threads to schedule incoming write and read operations received by the content node, dispatch these to the search core, and ensure the bucket database remains in sync with changes caused by write operations. + +Unless explicitly configured, the size of the thread pool is automatically set based on the number of CPU cores available. + +Persistence threads are backed by a *persistence queue*. Read/write-operations received by the RPC subsystem are pushed onto this queue. The queue is operation deadline-aware; if an operation has exceeded its deadline while enqueued, it is immediately failed back to the sender without being executed. This avoids a particular failure scenario where a heavily loaded node spends increasingly more and more time processing already doomed operations, due to not being able to drain its queue quickly enough. + +All operations bound for a particular data bucket (such as Puts, Gets, etc.) execute in the context of a *bucket lock*. Locks are *shared* for reads and *exclusive* for writes. This means that multiple read operations can execute in parallel for the same bucket, but only one write operation can execute for a bucket at any given time (and no reads can be started concurrently with existing writes for a bucket, and vice versa). Note that some of these locking restrictions can be relaxed when it's safe to do so—see [performance optimizations](#performance-optimizations) for details. + +If a persistence thread tries to pop an operation from the queue and sees that the bucket it's bound for is already locked, it will leave the operation in place in the queue and try the next operation(s) instead. This means that although the queue acts as a FIFO for client operations towards a *single* bucket, this is not the case across *multiple* buckets. + +#### Write operations + +Write operations are dispatched as *asynchronous*—i.e., non-blocking—tasks to the search core. This increases parallelism by freeing up persistence threads to handle other operations, and a deeper pipeline enables the search core to optimize transaction log synchronization and batching of data structure updates. + +Since a deeper pipeline comes at the potential cost of increased latency when many operations are in flight, the maximum number of concurrent asynchronous operations is bounded by an adaptive persistence throttling mechanism. The throttler will dynamically scale the window of concurrency until it reaches a saturation point where further increasing the window size also results in increased operation latencies. When the number of in-flight operations hits the current maximum, persistence threads will not dispatch any more writes until the number goes down. Reads can still be processed during this time. + +An asynchronous write-task holds on to the exclusive bucket lock for the duration of its lifetime. Once the search core completes the write operation, the bucket database is updated with the new metadata state of the bucket (which reflects the side effects of the write) prior to releasing the lock. An operation reply is then generated and sent back via the RPC subsystem. + +#### Read operations + +Read operations are always evaluated *synchronously*—i.e. blocking—by persistence threads. To avoid having potentially expensive maintenance read operations (such as those used for [replica reconciliation](/en/content/consistency#replica-reconciliation)) block client operations for prolonged amounts of time, a subset of the persistence threads are *not* allowed to process such maintenance operations. + +Note that the condition evaluation step of a test-and-set write is considered a *read* sub-operation and is therefore done synchronously. Since it's part of a write operation, it happens atomically in the context of the exclusive lock of the higher-level operation. + +#### Performance optimizations + +To reduce thread context switches, some write operations may bypass the persistence thread queues and be directly asynchronously dispatched to the search core from the RPC thread the operation was received at. Such operations must still successfully acquire the appropriate exclusive bucket lock—if the lock cannot be immediately acquired the operation is pushed onto the persistence queue instead. + +To reduce lock contention and thread wakeups, smaller numbers of persistence threads are grouped together in *stripes* that share a dedicated per-stripe persistence queue. Operations are routed deterministically to a particular stripe based on their bucket ID, meaning that stripes operate on non-overlapping parts of the bucket space. Together, the many stripes and queues form one higher-level *logical* queue that covers the entire bucket space. + +If the queue contains multiple *non-conflicting* write operations to the same bucket, these may be dispatched in parallel in the context of the *same* write lock. This avoids having to wait for an entire lock-execute-unlock roundtrip prior to dispatching the next write for the same bucket. An example of conflicting writes is multiple Puts to the same document ID. The maximum number of operations dispatched in parallel is implementation-defined. + +### Document database + +Each document database is responsible for a single document type. It has a component called FeedHandler which takes care of incoming documents, updates, and remove requests. All requests are first written to a [transaction log](#transaction-log), then handed to the appropriate sub-database, based on the request type. + +### Sub-databases + +There are three types of sub-databases, each with its own [document meta store](/en/content/attributes#document-meta-store) and [document store](#document-store). The document meta store holds a map from the document ID to a local ID. This local id is used to address the document in the document store. The document meta store also maintains information on the state of the buckets that are present in the sub-database. + +The sub-databases are maintained by the *Maintenance Controller*. The document distribution changes as the system is resized. When the number of nodes in the system changes, the Maintenance Controller will move documents between the Ready and Not Ready sub-databases to reflect the new distribution. When an entry in the Removed sub-database gets old, it is purged. The sub-databases are: + + + + + + + + + + + + + + + + + + + + + + + +
    **Not Ready**Holds the redundant documents that are not searchable, i.e. the not ready documents. Documents that are not ready are only stored, not indexed. It takes some processing to move from this state to the ready state.
    **Ready**Maintains attributes and indexes of all ready documents and keeps them searchable. One of the ready copies is active while the rest are not active:

    **Active**
    There should always be exactly one active copy of each document in the system, though intermittently there may be more. These documents produce results when queries are evaluated.

    >**Not Active**
    The ready copies that are not active are indexed but will not produce results. By being indexed, they are ready to take over immediately if the node holding the active copy becomes unavailable. Read more in searchable-copies.
    **Removed**Keeps track of documents that have been removed. The id and timestamp for each document are kept. This information is used when buckets from two nodes are merged. If the removed document exists on another node but with a different timestamp, the most recent entry prevails.
    + +## Transaction log + +Content nodes have a transaction log to persist mutating operations. The transaction log persists operations by file append. Having a transaction log simplifies proton's in-memory index structures and enables steady-state high performance, read more below. + +All operations are written and synced to the [transaction log](/en/content/proton#transaction-log). This is sequential (not random) IO, but might impact overall feed performance if running on NAS attached storage where the sync operation has a much higher cost than on local attached storage (e.g., SSD). See [sync-transactionlog](/en/reference/applications/services/content#sync-transactionlog). + +By default, proton will [flush components](/en/reference/applications/services/content#flush-on-shutdown) like attribute vectors and memory index on shutdown, for quicker startup after scheduled restarts. + +## Document store + +Documents are stored as compressed serialized blobs in the *document store*. Put, update and remove operations are persisted in the [transaction log](#transaction-log) before updating the document in the document store. The operation is acked to the client and the result of the operation is immediately seen in search results. + +Files in the document store are written sequentially, and occur in pairs - example: + +```bash +-rw-r--r-- 1 owner users 4133380096 Aug 10 13:36 1467957947689211000.dat +-rw-r--r-- 1 owner users 71192112 Aug 10 13:36 1467957947689211000.idx +``` + +The [maximum size](/en/reference/applications/services/content#summary-store-logstore-maxfilesize): (in bytes) per .dat file on disk can be set using the following: + +```xml highlight= {9} + + + + + + + + + 8000000000 +``` + +Notes: + +- The files are written in sequence. *proton* starts with one pair and grows it until *maxfilesize*. Once full, a new pair is started. +- This means the pair is immutable, except for the last pair, which is written to. + +- Documents exist in multiple versions in multiple files. Older versions are compacted away when a pair is scheduled to be the new active pair - obsolete versions are removed, leaving only the active document version left in a new file pair - which is the new active pair. +- Read more on implications of setting *maxfilesize* in [proton maintenance jobs](/en/content/proton#document-store-compaction). +- Files are written in [chunks](/en/reference/applications/services/content#summary-store-logstore-chunk), using compression settings. + +## Defragmentation + +[Document store compaction](#document-store-compaction), defragments and sort document store files. It removes stale versions of documents (i.e. old versions of updated documents). It is triggered when the disk bloat of the document store is larger than the total disk usage of the document store times [diskbloatfactor](/en/reference/applications/services/content#flushstrategy-native-total-diskbloatfactor). Refer to [summary tuning](/en/reference/applications/services/content#summary) for details. + +Defragmentation status is best observed by tracking the [max_bucket_spread](/en/reference/operations/metrics/searchnode#content_proton_documentdb_ready_document_store_max_bucket_spread) metric over time. A sawtooth pattern is normal for corpora that change over time. The [document_store_compact](/en/reference/operations/metrics/searchnode#content_proton_documentdb_job_document_store_compact) metric tracks when proton is running the document store compaction job. Compaction settings can be set too tight, in that case, the metric is always, or close to, 1. + +When benchmarking, it is important to set the correct compaction settings, and also make sure that proton has compacted files since (can take hours), and is not actively compacting (*document_store_compact* should be 0 most of the time). + + + +**Note:** + +There is no bucket-compaction across files - documents will not move between files. + + +Optimized reads using chunks + +As documents are clustered within the .dat file, proton optimizes reads by reading larger chunks when accessing documents. When visiting, documents are read in *bucket* order. This is the same order as the defragmentation jobs use. + +The first document read in a visit operation for a bucket will read a chunk from the .dat file into memory. Subsequent document accesses are served by a memory lookup only. The chunk size is configured by [maxsize](/en/reference/applications/services/content#summary-store-logstore-chunk-maxsize): + +```xml highlight= {9} + + + + + + + + + 16384 + + +``` + +There can be 2^22=4M chunks. This sets a minimum chunk size based on *maxfilesize* - e.g. an 8G file can have minimum 2k chunk size. Finally, the bucket size is configured by setting [bucket-splitting](/en/reference/applications/services/content#bucket-splitting): + +```xml highlight= {3} + + + +``` + +The following are the relevant sizing units: + +.dat file size - *maxfilesize*. Larger files give fewer files and so better locality, but compaction requires more memory and more time to complete. chunk size - *maxsize*. Smaller chunks give less wasted IO bytes but more IO operations. bucket size - *bucket-splitting*. Larger buckets give fewer buckets and better locality to nodes and files, but incur more overhead during content layer bucket maintenance operations. Overhead can be treated as linear in both CPU, memory and network usage with the bucket size. + +### Memory usage + +The document store has a mapping in memory from local ID (LID) to position in a document store file (.dat). Part of this mapping is persisted in the .idx-file paired to the .dat file. The memory used by the document store is linear with the number of documents and updates to these. + +The metric [content.proton.documentdb.ready.document_store.memory_usage.allocated_bytes](/en/reference/operations/metrics/searchnode#content_proton_documentdb_ready_document_store_memory_usage_allocated_bytes) gives the size in memory - use the [metric API](/en/reference/api/state-v1#state-v1-metrics) to find it. A rule of thumb is 12 bytes per document. + +## Attributes + +[Attribute](/en/content/attributes) fields are in-memory fields used for matching, ranking, sorting and grouping. Each attribute is a separate component that consists of a set of [data structures](/en/content/attributes#data-structures) to store values for that field across all documents in a sub-database. Attributes are managed by the Ready sub-database. Some attributes can also be managed by the Not Ready sub-database, see [high-throughput updates](/en/content/attributes#fast-access) for details. + +## Index + +Index fields are string fields, used for text search. Other field types are [attributes](/en/content/attributes) and [summary fields](/en/querying/document-summaries). + +The Index in the Ready sub-database consists of a memory index and one or more disk indexes. Mutating document operations are applied to the memory index, which is [flushed](#memory-index-flush) regularly. Flushed memory indexes are [merged](#disk-index-fusion) with the primary disk index. + +Proton stores position information in text indices by default, for proximity relevance - `posocc` (below). All the occurrences of a term are stored in the posting list, with its position. This provides superior ranking features, but is somewhat more expensive than just storing a single occurrence per document. For most applications, it is the correct tradeoff, since most of the cost is usually elsewhere and relevance is valuable. + +Applications that only need occurrence information for filtering can use [rank: filter](/en/reference/schemas/schemas#rank) to optimize query performance, using only `boolocc`\-files (below). + +The memory index has a dictionary per index field. This contains all unique words in that field, with mapping to posting lists with position information. The position information is used during ranking, see [nativeRank](/en/ranking/nativerank) for details on how a text match score is calculated. + +The disk index stores the content of each index field in separate folders. Each folder contains: + +- Dictionary. Files: `dictionary.pdat`, `dictionary.spdat`, `dictionary.ssdat`. +- Compressed posting lists with position information. File: `posocc.dat.compressed`. +- Posting lists with only occurrence information (bitvector). These are generated for common words. Files: `boolocc.bdat`, `boolocc.idx`. + +Example: + +```bash +$ pwd +/opt/vespa/var/db/vespa/search/cluster.mycluster/n1/documents/myschema/0.ready/index/index.flush.1/myfield +$ ls -la +total 7632 +drwxr-xr-x 2 org users 145 Oct 29 06:09 . +drwxr-xr-x 74 org users 4096 Oct 29 06:11 .. +-rw-r--r-- 1 org users 4096 Oct 29 06:11 boolocc.bdat +-rw-r--r-- 1 org users 4096 Oct 29 06:11 boolocc.idx +-rw-r--r-- 1 org users 8192 Oct 29 06:11 dictionary.pdat +-rw-r--r-- 1 org users 8192 Oct 29 06:11 dictionary.spdat +-rw-r--r-- 1 org users 4120 Oct 29 06:11 dictionary.ssdat +-rw-r--r-- 1 org users 7778304 Oct 29 06:11 posocc.dat.compressed +``` + +Note that `boolocc`\-files are empty if the number of occurrences is small, like in the example above. + +## Proton maintenance jobs + +The memory and disk data structures used in Proton are periodically optimized by a set of maintenance jobs. These jobs are automatically executed, and some can be tuned in [flush strategy tuning](/en/reference/applications/services/content#flushstrategy). All jobs are described in the table below. + +There is only one instance of each job at a time - e.g., attributes are flushed in sequence. When a job is running, its metric is set to 1 - otherwise 0. Use this to correlate observed performance or resource usage with job runs - see *Run metric* below. + +The *temporary* resources used when jobs are executed are described in *CPU*, *Memory* and *Disk*. The memory and disk usage metrics of components that are optimized by the jobs are described in *Metrics* (with *Metric prefix*). For a list of all available Proton metrics, refer to the searchnode metrics in the [Vespa Metric Set](/en/reference/operations/metrics/vespa-metric-set#searchnode-metrics). Metrics are available at the [Metrics API](/en/operations/metrics). + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    JobDescription
    CPULittle - one thread flushes to disk
    MemoryLittle - some temporary use
    DiskA new file is written too, so 2x the size of an attribute on disk until the old flush file is deleted.
    Run metriccontent.proton.documentdb.job.attribute_flush
    content.proton.documentdb.[ready|notready].attribute.memory_usage.
    Metricsallocated_bytes.average
    used_bytes.average
    dead_bytes.average
    onhold_bytes.average
    CPULittle - one thread flushes to disk
    MemoryLittle
    DiskCreates a new disk index, size of the memory index.
    Run metriccontent.proton.documentdb.job.memory_index_flush
    Metric prefixcontent.proton.documentdb.index.memory_usage.
    Metricsallocated_bytes.average
    used_bytes.average
    dead_bytes.average
    onhold_bytes.average
    CPUMultiple threads merge indices, configured as a function of
    feeding concurrency -
    refer to this for details
    MemoryLittle
    DiskCreates a new index while serving from the current: 2x temporary disk usage for the given index.
    Run metriccontent.proton.documentdb.job.disk_index_fusion
    CPULittle
    MemoryLittle
    DiskLittle
    Run metriccontent.proton.documentdb.job.document_store_flush
    CPULittle - one thread reads one file, sorts and writes a new file
    MemoryHolds a document store file in memory plus memory for sorting the file.
    Note: This is important on hosts with little memory!
    Reduce maxfilesize to increase the number of files and use less temporary memory for compaction.
    DiskA new file is written while the current is serving, max temporary usage is 2x.
    Run metriccontent.proton.documentdb.job.document_store_compact
    Metric prefixcontent.proton.documentdb.[ready|notready|removed].document_store.
    Metricsdisk_usage.average
    disk_bloat.average
    max_bucket_spread.average
    memory_usage.allocated_bytes.average
    memory_usage.used_bytes.average
    memory_usage.dead_bytes.average
    memory_usage.onhold_bytes.average
    CPUCPU similar to feeding.
    Consumes capacity from the write threads, so has feeding impact
    MemoryAs feeding - e.g., the attribute memory usage and memory index in the ready sub-database will grow
    DiskAs feeding
    Run metriccontent.proton.documentdb.job.bucket_move
    CPULike feeding - add and remove documents
    MemoryLittle
    Disk0
    Run metriccontent.proton.documentdb.job.lid_space_compact
    Metric prefixcontent.proton.documentdb.[ready|notready|removed].lid_space.
    Metricslid_limit.last
    lid_bloat_factor.average
    lid_fragmentation_factor.average
    CPULittle
    MemoryLittle
    DiskLittle
    Run metriccontent.proton.documentdb.job.removed_documents_prune
    + + +## Retrieving documents + +Retrieving documents is done by specifying an id to *get*, or use a [selection expression](/en/reference/writing/document-selector-language) to *visit* a range of documents - refer to the [Document API](/en/reference/api/api). Overview: + + +![Retrieving documents](/assets/img/elastic-visit-get.svg) + + + + + + + + + + + + + + + + + + + +
    **Get**When the content node receives a get request, it scans through all the document databases, and for each one, it checks all three sub-databases. Once the document is found, the scan is stopped and the document returned. If the document is found in a Ready sub-database, the document retriever will apply any changes that are stored in the attributes before returning the document.
    **Visit**A visit request creates an iterator over each candidate bucket. This iterator will retrieve matching documents from all sub-databases of all document databases. As for get, attribute values are applied to document fields in the Ready sub-database.
    + +## Queries + +Queries have a separate pathway through the system. They do not use the distributor, nor do they go through the content node persistence threads. They are orthogonal to the elasticity set up by the storage and retrieval described above. How queries move through the system: + + +![Queries](/assets/img/proton-query.svg) + + +A query enters the system through the *QR-server (query rewrite server)* in the [Vespa Container](/en/applications/containers). The QR-server issues one query per document type to the search nodes: + + + + + + + + + + + + + + + + + + + +
    **Container**The Container knows all the document types and rewrites queries as a collection of queries, one for each type. Queries may have a restrict parameter, in which case the container will send the query only to the specified document types. It sends the query to content nodes and collects partial results. It pings all content nodes every second to know whether they are alive, and keeps open TCP connections to each one. If a node goes down, the elastic system will make the documents available on other nodes.
    **Content node matching**The *match engine* receives queries and routes them to the right document database based on the document type. The query is passed to the *Ready* sub-database, where the searchable documents are. Based on information stored in the document meta store, the query is augmented with a blocklist that ensures only *active* documents are matched.
    + +## /state/v1 API + +Besides the common endpoints documented in the [/state/v1 API reference](/en/reference/api/state-v1), Proton has additional endpoints as part of the /state/v1 API that expose information about the internal state of a search node. This API is available at `http://host:stateport/state/v1/`. + +Run [vespa-model-inspect](/en/reference/operations/self-managed/tools#vespa-model-inspect) to find the JSON HTTP stateport: + +``` +vespa-model-inspect service searchnode +``` + +### Initialization Progress API + +The initialization progress can be found by HTTP GET at `http://host:stateport/state/v1/initialization`. This endpoint becomes available early during initialization of Proton when other endpoints are not yet available. It gives a human-readable overview of the document databases and their attributes being loaded. Note that this is **not** a stable API, and it will expand and change between releases. + +Example `state/v1/initialization`: + +```json expandable +{ + "state": "initializing", + "current_time": "1758873251.933488", + "start_time": "1758873249.715624", + "load": 1, + "replay_transaction_log": 0, + "online": 0, + "dbs": [ + { + "state": "load", + "start_time": "1758873249.936939", + "name": "dbname", + "ready_subdb": { + "loaded_attributes": [ + { + "state": "loaded", + "start_time": "1758873249.941415", + "name": "int_field", + "end_time": "1758873249.942051" + }, + { + "state": "loaded", + "start_time": "1758873249.941498", + "name": "string_field", + "end_time": "1758873249.944647" + } + ], + "loading_attributes": [ + { + "state": "reprocessing", + "start_time": "1758873249.941555", + "name": "tensor_field", + "reprocess_progress": "6.061879", + "reprocess_start_time": "1758873249.993847" + } + ], + "queued_attributes": [ + + ] + } + } + ] +} +``` + +### Custom Component State API + +The custom component status can be found by HTTP GET at `http://host:stateport/state/v1/custom/component`. It gives an overview of the relevant search node components and their internal state. Note that this is **not** a stable API, and it will expand and change between releases. + +Example `state/v1/custom/component`: + +```json expandable +{ + "documentdb": { + "mydoctype": { + "documentType": "mydoctype", + "status": { + "state": "ONLINE", + "configState": "OK" + }, + "documents": { + "active": 10, + "ready": 10, + "total": 10, + "removed": 0 + }, + "url": "http://host:stateport/state/v1/custom/component/documentdb/mydoctype" + } + }, + "threadpools": { + "url": "http://host:stateport/state/v1/custom/component/threadpools" + }, + "matchengine": { + "status": { + "state": "ONLINE" + }, + "url": "http://host:stateport/state/v1/custom/component/matchengine" + }, + "flushengine": { + "url": "http://host:stateport/state/v1/custom/component/flushengine" + }, + "tls": { + "url": "http://host:stateport/state/v1/custom/component/tls" + }, + "hwinfo": { + "url": "http://host:stateport/state/v1/custom/component/hwinfo" + }, + "resourceusage": { + "url": "http://host:stateport/state/v1/custom/component/resourceusage", + "disk": 0.25, + "memory": 0.35, + "attribute_address_space": 0 + }, + "session": { + "search": { + "url": "http://host:stateport/state/v1/custom/component/session/search", + "numSessions": 0 + } + } +} +``` + +Example `state/v1/custom/component/documentdb/mydoctype`: + +```json expandable +{ + "documentType": "mydoctype", + "status": { + "state": "ONLINE", + "configState": "OK" + }, + "documents": { + "active": 10, + "ready": 10, + "total": 10, + "removed": 0 + }, + "subdb": { + "removed": { + "url": "http://host:stateport/state/v1/custom/component/documentdb/mydoctype/subdb/removed" + }, + "ready": { + "url": "http://host:stateport/state/v1/custom/component/documentdb/mydoctype/subdb/ready" + }, + "notready": { + "url": "http://host:stateport/state/v1/custom/component/documentdb/mydoctype/subdb/notready" + } + }, + "threadingservice": { + "url": "http://host:stateport/state/v1/custom/component/documentdb/mydoctype/threadingservice" + }, + "bucketdb": { + "url": "http://host:stateport/state/v1/custom/component/documentdb/mydoctype/bucketdb", + "numBuckets": 1 + }, + "maintenancecontroller": { + "url": "http://host:stateport/state/v1/custom/component/documentdb/mydoctype/maintenancecontroller" + } +} +``` diff --git a/en/examples/billion-scale-image-search.md b/en/examples/billion-scale-image-search.mdx similarity index 83% rename from en/examples/billion-scale-image-search.md rename to en/examples/billion-scale-image-search.mdx index 29e74ff4f7..d025cbf648 100644 --- a/en/examples/billion-scale-image-search.md +++ b/en/examples/billion-scale-image-search.mdx @@ -1,5 +1,4 @@ --- -# Copyright Vespa.ai. All rights reserved. title: "Billion Scale Image Search" --- @@ -21,14 +20,17 @@ This sample app use the [LAION-5B](https://laion.ai/blog/laion-5b/) dataset, The LAION-5B dataset was used to train the popular text-to-image generative StableDiffusion model. + Note the following about the LAION 5B dataset: > Be aware that this large-scale dataset is un-curated. > Keep in mind that the un-curated nature of the dataset means that collected > links may lead to strongly discomforting and disturbing content for a human viewer. + + The released dataset does not contain image data itself, -but CLIP encoded vector representations of the images, +but [CLIP](https://openai.com/research/clip) encoded vector representations of the images, and metadata like `url` and `caption`. ## Use cases @@ -40,7 +42,7 @@ The app can be used to implement several use cases over the LAION dataset, or ad - Given an image vector representation, search for similar images in the dataset. This can for example be used to take the output image of StableDiffusion to find similar images in the training dataset. -All this combined using [Vespa's query language](../querying/query-language.html), +All this combined using [Vespa's query language](/en/querying/query-language), and also in combination with filters. ## Vespa Primitives Demonstrated @@ -49,9 +51,9 @@ The sample application demonstrates many Vespa primitives: - Importing an [ONNX](https://onnx.ai/)-exported version of [CLIP ViT-L/14](https://github.com/openai/CLIP) for [accelerated inference](https://blog.vespa.ai/stateful-model-serving-how-we-accelerate-inference-using-onnx-runtime/) -in [Vespa stateless](../learn/overview.html) containers. +in [Vespa stateless](/en/learn/overview) containers. The exported CLIP model encodes a free-text prompt to a joint image-text embedding space with 768 dimensions. -- [HNSW](../querying/approximate-nn-hnsw.html) indexing of vector centroids drawn +- [HNSW](/en/querying/approximate-nn-hnsw) indexing of vector centroids drawn from the dataset, and combination with classic Inverted File as described in [Billion-scale vector search using hybrid HNSW-IF](https://blog.vespa.ai/vespa-hybrid-billion-scale-vector-search/). - Decoupling of vector storage and vector similarity computations. The stateless layer performs vector @@ -67,7 +69,7 @@ high memory instances, this allows Vespa to scale cost efficiently to trillion-s evaluated in Vespa using accelerated inference, both at indexing time and at query time. The PCA weights are represented also using ONNX. - Phased ranking. The image embedding vectors are also projected to 128 dimensions, stored using -memory mapped [paged attribute tensors](../content/attributes.html#paged-attributes). +memory mapped [paged attribute tensors](/en/content/attributes#paged-attributes). Full precision vectors are on stored on disk in Vespa summary store. The first-phase coarse search ranks vectors in the reduced vector space, per node, and results are merged from all nodes before the final ranking phase in the stateless layer. @@ -80,12 +82,12 @@ can be on url, caption, image height, width, safety probability, NSFW label, and - Caching, both reduced vectors (128) cached by the OS buffer cache, and full version 768 dims are cached using Vespa summary cache. - Query-time vector de-duping and diversification of the search engine result page using document to document similarity instead of query to document similarity. Also accelerated by stateless model inference. -- Scale, from a single node deployment to multi-node deployment using managed [Vespa Cloud](https://cloud.vespa.ai/), +- Scale, from a single node deployment to multi-node deployment using managed [Vespa Cloud](/), or self-hosted on-premise. ## Stateless Components -The app contains several [container components](../applications/components.html): +The app contains several [container components](/en/applications/components): - [RankingSearcher](https://github.com/vespa-engine/sample-apps/tree/master/billion-scale-image-search/app/src/main/java/ai/vespa/examples/searcher/RankingSearcher.java) implements the last stage ranking using full-precision vectors using an ONNX model for accelerated inference. @@ -102,98 +104,161 @@ for playing around with the app on a laptop. **Requirements:** -* [Docker](https://www.docker.com/) Desktop installed and running. 6GB available memory for Docker is recommended. - Refer to [Docker memory](https://docs.vespa.ai/en/operations-selfhosted/docker-containers.html#memory) +- [Docker](https://www.docker.com/) Desktop installed and running. 6GB available memory for Docker is recommended. + Refer to [Docker memory](/en/operations/self-managed/docker-containers#memory) for details and troubleshooting -* Alternatively, deploy using [Vespa Cloud](#deployment-note) -* Operating system: Linux, macOS or Windows 10 Pro (Docker requirement) -* Architecture: x86_64 or arm64 -* [Homebrew](https://brew.sh/) to install [Vespa CLI](../clients/vespa-cli.html), or download +- Alternatively, deploy using [Vespa Cloud](#deployment-note) +- Operating system: Linux, macOS or Windows 10 Pro (Docker requirement) +- Architecture: x86_64 or arm64 +- [Homebrew](https://brew.sh/) to install [Vespa CLI](/en/clients/vespa-cli), or download a vespa cli release from [GitHub releases](https://github.com/vespa-engine/vespa/releases). -* Java 17 installed. -* Python3 and numpy to process the vector dataset -* [Apache Maven](https://maven.apache.org/install.html) - this sample app uses custom Java components and Maven is used - to build the application. +- Java 17 installed. +- Python3 and numpy to process the vector dataset +- [Apache Maven](https://maven.apache.org/install.html) - this sample app uses custom Java components and Maven is used to build the application. Verify Docker Memory Limits: -
    +
    +```bash
     $ docker info | grep "Total Memory"
     or
     $ podman info | grep "memTotal"
    -
    +``` + + +Install [Vespa CLI](/en/clients/vespa-cli): -Install [Vespa CLI](../clients/vespa-cli.html): -
    +```bash
     $ brew install vespa-cli
    -
    +``` + For local deployment using docker image: -
    +
    +```bash
     $ vespa config set target local
    -
    +``` + Use the [multi-node high availability](https://github.com/vespa-engine/sample-apps/tree/master/examples/operations/multinode-HA) template for inspiration for multi-node, on-premise deployments. Pull and start the vespa docker container image: -
    +
    +```bash
     $ docker pull vespaengine/vespa
     $ docker run --detach --name vespa --hostname vespa-container \
       --publish 127.0.0.1:8080:8080 --publish 127.0.0.1:19071:19071 \
       vespaengine/vespa
    -
    +``` + Verify that the configuration service (deploy api) is ready: -
    +
    +```bash
     $ vespa status deploy --wait 300 ./app
    -
    +``` + Download this sample application: -
    +
    +```bash
     $ vespa clone billion-scale-image-search myapp && cd myapp
    -
    +``` +Setup: -{% include setup.html appname='billion-scale-image-search' %} + + +**Create a [tenant](/en/learn/tenant-apps-instances) on Vespa Cloud:** + + Go to [console.vespa-cloud.com](https://console.vespa-cloud.com/) and create your tenant (unless you already have one). + + +**Install the [Vespa CLI](/en/clients/vespa-cli)** using [Homebrew](https://brew.sh/): +```bash +$ brew install vespa-cli +``` + Windows/No Homebrew? See the [Vespa CLI page](/en/clients/vespa-cli) to download directly. + + +**Configure the Vespa client:** +```bash +$ vespa config set target cloud +$ vespa config set application vespa-team.autotest +``` + Use the tenant name from step 1 instead of "vespa-team", and replace in other steps in this example guide, too. + + +**Get Vespa Cloud control plane access:** +```bash +$ vespa auth login +``` + Follow the instructions from the command to authenticate. + + +**Clone a sample [application](/en/basics/applications):** +```bash +$ vespa clone billion-scale-image-search myapp && cd myapp +``` + See [sample-apps](https://github.com/vespa-engine/sample-apps) for other sample apps you can clone. + + +**Add a certificate for [data plane access](/en/security/guide#data-plane) to the application:** +```bash +$ vespa auth cert app +``` + It is a good idea to take note of the path to the `.pem` files written here. + + ## Download Vector + Metadata These instructions use the first split file (0000) of a total of 2314 files in the LAION2B-en split. Download the vector data file: -
    +
    +```bash
     $ curl --http1.1 -L -o img_emb_0000.npy \
       https://the-eye.eu/public/AI/cah/laion5b/embeddings/laion2B-en/img_emb/img_emb_0000.npy
    -
    +``` + Download the metadata file: -
    +
    +```bash
     $ curl -L -o metadata_0000.parquet \
       https://the-eye.eu/public/AI/cah/laion5b/embeddings/laion2B-en/laion2B-en-metadata/metadata_0000.parquet
    -
    +``` + Install python dependencies to process the files: -
    +
    +```bash
     $ python3 -m pip install pandas numpy requests mmh3 pyarrow
    -
    +``` + Generate centroids, this process randomly selects vectors from the dataset to represent centroids. Performing an incremental clustering can improve vector search recall and allow indexing fewer centroids. For simplicity, this tutorial uses random sampling. -
    +
    +```bash
     $ python3 app/src/main/python/create-centroid-feed.py img_emb_0000.npy > centroids.jsonl
    -
    +``` + Generate the image feed, this merges the embedding data with the metadata and creates a Vespa jsonl feed file, with one json operation per line. -
    +
    +```bash
     $ python3 app/src/main/python/create-joined-feed.py metadata_0000.parquet img_emb_0000.npy > feed.jsonl
    -
    +``` + To process the entire dataset, we recommend starting several processes, each operating on separate split files as the processing implementation is single-threaded. @@ -218,60 +283,74 @@ ability to export the model to [ONNX](https://onnx.ai/) format: Build the sample app (make sure you have JDK 17, verify with `mvn -v`): - This step also downloads a pre-exported ONNX model for mapping the prompt text to the CLIP vector embedding space. -
    +
    +```bash
     $ mvn clean package -U -f app
    -
    +``` + Deploy the application. This step deploys the application package built in the previous step: -
    +
    +```bash
     $ vespa deploy --wait 300 ./app
    -
    +``` + #### Deployment note It is possible to deploy this app to -[Vespa Cloud](../basics/deploy-an-application-java.html). -For Vespa cloud deployments to the [dev env](../operations/zones.html) +[Vespa Cloud](/en/basics/deploy-an-application-java). +For Vespa cloud deployments to the [dev env](/en/operations/zones) replace the [src/main/application/services.xml](https://github.com/vespa-engine/sample-apps/tree/master/billion-scale-image-search/app/src/main/application/services.xml) with [src/main/application/services-cloud.xml](https://github.com/vespa-engine/sample-apps/tree/master/billion-scale-image-search/app/src/main/application/services-cloud.xml) - the cloud deployment uses dedicated clusters for `feed` and `query`. Wait for the application endpoint to become available: -
    +
    +```bash
     $ vespa status --wait 300
    -
    +``` + -Run [Vespa System Tests](../reference/applications/testing.html), +Run [Vespa System Tests](/en/reference/applications/testing), which runs a set of basic tests to verify that the application is working as expected: -
    +
    +```bash
     $ vespa test app/src/test/application/tests/system-test/feed-and-search-test.json
    -
    +``` + The _centroid_ vectors **must** be indexed first: -
    +
    +```bash
     $ vespa feed centroids.jsonl
     $ vespa feed feed.jsonl
    -
    +``` + Track number of documents while feeding: -
    +
    +```bash
     $ vespa query 'yql=select * from image where true' \
       hits=0 \
       ranking=unranked
    -
    +``` + ## Fetching data -Fetch a single document using [document api](../reference/api/document-v1.html): +Fetch a single document using [document api](/en/reference/api/document-v1): -
    +
    +```bash
     $ vespa document get \
      id:laion:image::5775990047751962856
    -
    +``` + The response contains all fields, including the full vector representation and the reduced vector, plus all the metadata. Everything represented in the same @@ -284,12 +363,14 @@ The following provides a few query examples, [CLIPEmbeddingSearcher](https://github.com/vespa-engine/sample-apps/tree/master/billion-scale-image-search/app/src/main/java/ai/vespa/examples/searcher/CLIPEmbeddingSearcher.java) which will encode the prompt text into a CLIP vector representation using the embedded CLIP model: -
    +
    +```bash
     $ vespa query \
      'yql=select documentid, caption, url, height, width from image where nsfw contains "unlikely"'\
      'hits=10' \
      'prompt=two dogs running on a sandy beach'
    -
    +``` + Results are filtered by a constraint on the `nsfw` field. Note that even if the image is classified as `unlikely` the image content might still be explicit as the NSFW classifier is not 100% accurate. @@ -299,35 +380,41 @@ The returned images are ranked by CLIP similarity (The score is found in each hi The following query adds another filter, restricting the search so that only images crawled from urls with `shutterstock.com` is retrieved. -
    +
    +```bash
     $ vespa query \
      'yql=select documentid, caption, url, height, width from image where nsfw contains "unlikely" and url contains "shutterstock.com"'\
      'hits=10' \
      'prompt=two dogs running on a sandy beach'
    -
    +``` + Another restricting the search further, adding a phrase constraint `caption contains phrase("sandy", "beach")`: -
    +
    +```bash
     $ vespa query \
      'yql=select documentid, caption, url, height, width from image where nsfw contains "unlikely" and url contains "shutterstock.com" and caption contains phrase("sandy", "beach")'\
      'hits=10' \
      'prompt=two dogs running on a sandy beach'
    -
    +``` + Regular query, matching over the `default` fieldset, searching the `caption` and the `url` field, ranked by the `text` ranking profile: -
    +
    +```bash
     $ vespa query \
      'yql=select documentid, caption, url from image where nsfw contains "unlikely" and userQuery()'\
      'hits=10' \
      'query=two dogs running on a sandy beach' \
      'ranking=text'
    -
    +``` -The `text` [rank](../basics/ranking.html) profile uses -[nativeRank](../ranking/nativerank.html), one of Vespa's many + +The `text` [rank](/en/basics/ranking) profile uses +[nativeRank](/en/ranking/nativerank), one of Vespa's many text matching rank features. ## Non-native hyperparameters @@ -355,7 +442,9 @@ embedding would save resources. ## Shutdown and remove the container: -
    +
    +```bash
     $ vespa destroy --force
    -
    +``` + diff --git a/en/examples/billion-scale-vector-search.md b/en/examples/billion-scale-vector-search.mdx similarity index 55% rename from en/examples/billion-scale-vector-search.md rename to en/examples/billion-scale-vector-search.mdx index 422f4a5ca6..7439018cbc 100644 --- a/en/examples/billion-scale-vector-search.md +++ b/en/examples/billion-scale-vector-search.mdx @@ -1,5 +1,4 @@ --- -# Copyright Vespa.ai. All rights reserved. title: "SPANN Billion Scale Vector Search" --- @@ -16,38 +15,80 @@ Excerpt: > Then, it reads the k associated posting lists for the retrieved centroids > and computes the distance between the query vector and the vector data read from the posting list: + ![](https://blog.vespa.ai/assets/2022-06-07-vespa-spann-billion-scale-vector-search/spann-posting-lists.excalidraw.png) + This sample application demonstrates how to represent SPANN using Vespa. -{% include setup.html appname='billion-scale-vector-search' %} +Setup: + + +**Create a [tenant](/en/learn/tenant-apps-instances) on Vespa Cloud:** + + Go to [console.vespa-cloud.com](https://console.vespa-cloud.com/) and create your tenant (unless you already have one). + + +**Install the [Vespa CLI](/en/clients/vespa-cli)** using [Homebrew](https://brew.sh/): +```bash +$ brew install vespa-cli +``` + Windows/No Homebrew? See the [Vespa CLI page](/en/clients/vespa-cli) to download directly. + + +**Configure the Vespa client:** +```bash +$ vespa config set target cloud +$ vespa config set application vespa-team.autotest +``` + Use the tenant name from step 1 instead of "vespa-team", and replace in other steps in this example guide, too. + + +**Get Vespa Cloud control plane access:** +```bash +$ vespa auth login +``` + Follow the instructions from the command to authenticate. + + +**Clone a sample [application](/en/basics/applications):** +```bash +$ vespa clone billion-scale-vector-search myapp && cd myapp +``` + See [sample-apps](https://github.com/vespa-engine/sample-apps) for other sample apps you can clone. + + +**Add a certificate for [data plane access](/en/security/guide#data-plane) to the application:** +```bash +$ vespa auth cert app +``` + It is a good idea to take note of the path to the `.pem` files written here. + + ## Download Vector Data This sample app uses the Microsoft SPACEV vector dataset from [big-ann-benchmarks.com](https://big-ann-benchmarks.com/). It uses the first 10M vectors of the 100M slice sample. This sample file is about 1GB (10M vectors): -
    - -
    +
    +```bash
     $ curl -L -o spacev10m_base.i8bin \
       https://data.vespa-cloud.com/sample-apps-data/spacev10m_base.i8bin
    -
    -
    +``` + Install dependencies and create the feed files for the first 10M vectors from the 100M sample: -
    - -
    +
    +```bash
     $ pip3 install numpy requests tqdm
    -
    -
    -
    - -
    +```
    +
    +
    +```bash
     $ python3 app/src/main/python/create-vespa-feed.py spacev10m_base.i8bin
    -
    -
    +``` + Output: * `graph-vectors.jsonl` * `if-vectors.jsonl` @@ -55,52 +96,46 @@ Output: ## Build and deploy Vespa app Build the application: -
    - -
    +
    +```bash
     $ mvn clean package -U -f app
    -
    -
    +``` + Deploy the application: -
    - -
    +
    +```bash
     $ vespa deploy --wait 900 ./app
    -
    -
    +``` + Wait for the application endpoint to become available: -
    - -
    +
    +```bash
     $ vespa status --wait 300
    -
    -
    +``` + Test [basic functionality](https://github.com/vespa-engine/sample-apps/blob/master/billion-scale-vector-search/app/src/test/application/tests/system-test/feed-and-search-test.json): -
    - -
    +
    +```bash
     $ vespa test app/src/test/application/tests/system-test/feed-and-search-test.json
    -
    -
    -See [CD tests](https://docs.vespa.ai/en/operations/automated-deployments.html#cd-tests) for details. +``` + +See [CD tests](/en/operations/automated-deployments#cd-tests) for details. ## Feed data The _graph_ vectors must be feed before the _if_ vectors: -
    - -
    +
    +```bash
     $ vespa feed graph-vectors.jsonl
    -
    -
    -
    - -
    +```
    +
    +
    +```bash
     $ vespa feed if-vectors.jsonl
    -
    -
    +``` + Now is a good time to open the Vespa Cloud Dashboard @@ -111,49 +146,45 @@ configuration to manage the feeding speed - more CPU is better, e.g.: ``` ``` -Use the [instance type reference](https://cloud.vespa.ai/en/reference/aws-flavors.html) to find good combinations. +Use the [instance type reference](/en/performance/instance-types/aws-instance-types) to find good combinations. Run time for a 2 VCPU deployment vs. 8 VCPU: -![duration 2vcpu](assets/billion-vector-2vcpu.png) -![duration 8vcpu](assets/billion-vector-8vcpu.png) + +![duration 2vcpu](/en/examples/assets/billion-vector-2vcpu.png) + +
    + +![duration 8vcpu](/en/examples/assets/billion-vector-8vcpu.png) + Observe the feed and query phases (below) of this guide: -![feed and queries](assets/billion-vector-feed-queries.png) - + +![feed and queries](/en/examples/assets/billion-vector-feed-queries.png) + ## Recall Evaluation Download the query vectors and the ground truth for the 10M first vectors: -
    - -
    +
    +```bash
     $ curl -L -o query.i8bin \
       https://github.com/microsoft/SPTAG/raw/main/datasets/SPACEV1B/query.bin
     $ curl -L -o spacev10m_gt100.i8bin \
       https://data.vespa-cloud.com/sample-apps-data/spacev10m_gt100.i8bin
    -
    -
    - - +``` Find the path to the credentials from the `vespa auth cert` step above, like -
    +
    +```txt
     /Users/username/.vespa/tenant_name.autotest.default/data-plane-public-cert.pem
    -
    +``` + Replace the two filenames in the command below. (This is not needed when running a [local test](#local-test-with-oci-image)) Run first 1K queries and evaluate recall@10. A higher number of clusters gives higher recall: -
    - -
    +
    +```bash
     $ ENDPOINT=$(vespa status --format=plain)
     $ python3 app/src/main/python/recall.py \
       --endpoint ${ENDPOINT}/search/ \
    @@ -161,91 +192,99 @@ $ python3 app/src/main/python/recall.py \
       --query_gt_file spacev10m_gt100.i8bin \
       --certificate $PWD/../.vespa/vespa-team.autotest.default/data-plane-public-cert.pem \
       --key         $PWD/../.vespa/vespa-team.autotest.default/data-plane-private-key.pem
    -
    -
    +``` + See the [blog post](https://blog.vespa.ai/vespa-hybrid-billion-scale-vector-search/#hnsw-if-accuracy) for details about this script. -
    +
    +```bash
     $ vespa destroy --force
    -
    +``` + ## Local test with OCI image -{% include pre-req.html memory="4 GB" extra-reqs=' -
  • Java 17.
  • -
  • Apache Maven is used to build the application.
  • ' %} + + +**Prerequisites:** + +- Linux, macOS or Windows 10 Pro on x86\_64 or arm64, with [Podman Desktop](https://podman.io/) or [Docker Desktop](https://www.docker.com/products/docker-desktop/) installed, with an engine running. + - Alternatively, start the Podman daemon: + + ```bash + $ podman machine init --memory 6000 + $ podman machine start + ``` + - See [Docker Containers](/en/operations/self-managed/docker-containers.html) for system limits and other settings. +- For CPUs older than Haswell (2013), see [CPU Support](/en/operations/self-managed/cpu-support). +- Memory: Minimum 4 GB RAM dedicated to Docker/Podman. [Memory recommendations](/en/operations/self-managed/node-setup#memory-settings). +- Disk: Avoid `NO_SPACE` - the vespaengine/vespa container image + headroom for data requires disk space. [Read more](/en/writing/feed-block). +- [Homebrew](https://brew.sh/) to install the [Vespa CLI](/en/clients/vespa-cli), or download the Vespa CLI from [Github releases](https://github.com/vespa-engine/vespa/releases). +- [Java 17](https://openjdk.org/projects/jdk/17/). +- [Apache Maven](https://maven.apache.org/install.html) is used to build the application. + + Verify memory Limits: -
    - -
    +
    +```bash
     $ docker info | grep "Total Memory"
    -
    +``` or -
    -
    - -
    + 
    +```bash
     $ podman info | grep "memTotal"
    -
    -
    +``` + Install [Vespa CLI](../clients/vespa-cli.html): -
    - -
    +
    +```bash
     $ brew install vespa-cli
    -
    -
    +``` + For local deployment: -
    - -
    +
    +```bash
     $ vespa config set target local
    -
    -
    +``` + Download this sample application: -
    - -
    +
    +```bash
     $ vespa clone billion-scale-vector-search myapp && cd myapp
    -
    -
    +``` + Pull and start the Vespa image: -
    - -
    +
    +```bash
     $ docker pull vespaengine/vespa
     $ docker run --detach --name vespa --hostname vespa-container \
       --publish 127.0.0.1:8080:8080 --publish 127.0.0.1:19071:19071 \
       vespaengine/vespa
    -
    -
    +``` + Verify that the configuration service (deploy api) is ready: -
    - -
    +
    +```bash
     $ vespa status deploy --wait 300
    -
    -
    +``` -At this point, you can continue the guide from [download vector data](#download-vector-data). ----- +At this point, you can continue the guide from [download vector data](#download-vector-data). ### Cleanup When done, remove the container: -
    - -
    +
    +```bash
     $ docker rm -f vespa
    -
    -
    +``` + diff --git a/en/examples/rag-blueprint.md b/en/examples/rag-blueprint.mdx similarity index 54% rename from en/examples/rag-blueprint.md rename to en/examples/rag-blueprint.mdx index 3bbaa981e5..5a9ae41949 100644 --- a/en/examples/rag-blueprint.md +++ b/en/examples/rag-blueprint.mdx @@ -1,5 +1,4 @@ --- -# Copyright Vespa.ai. All rights reserved. title: "The RAG Blueprint" --- @@ -13,48 +12,89 @@ This open source sample applications contains all the elements you need to creat * scales to any amount of data, query load, and complexity. This README provides the steps to create and run your own application based on the blueprint. -Refer to the [RAG Blueprint tutorial](https://docs.vespa.ai/en/learn/tutorials/rag-blueprint.html) for more in-depth explanations, +Refer to the [RAG Blueprint tutorial](/en/learn/tutorials/rag-blueprint.html) for more in-depth explanations, or try out the [Python notebook](https://vespa-engine.github.io/pyvespa/examples/rag-blueprint-vespa-cloud.html). - - -{% include setup.html appname='rag-blueprint' %} - +Setup: + + + +**Create a [tenant](/en/learn/tenant-apps-instances) on Vespa Cloud:** + Go to [console.vespa-cloud.com](https://console.vespa-cloud.com/) and create your tenant (unless you already have one). + + +**Install the [Vespa CLI](/en/clients/vespa-cli)** using [Homebrew](https://brew.sh/): +```bash +$ brew install vespa-cli +``` + Windows/No Homebrew? See the [Vespa CLI page](/en/clients/vespa-cli) to download directly. + + +**Configure the Vespa client:** +```bash +$ vespa config set target cloud +$ vespa config set application vespa-team.autotest +``` + Use the tenant name from step 1 instead of "vespa-team", and replace in other steps in this example guide, too. + + +**Get Vespa Cloud control plane access:** +```bash +$ vespa auth login +``` + Follow the instructions from the command to authenticate. + + +**Clone a sample [application](/en/basics/applications):** +```bash +$ vespa clone rag-blueprint myapp && cd myapp +``` +See [sample-apps](https://github.com/vespa-engine/sample-apps) for other sample apps you can clone. + + +**Add a certificate for [data plane access](/en/security/guide#data-plane) to the application:** +```bash +$ vespa auth cert app +``` +It is a good idea to take note of the path to the `.pem` files written here. + + ## Test the application -
    - -
    +
    +```bash
     $ vespa deploy --wait 900 ./app
    -
    -
    +``` + Feed some documents, this will also chunk and embed so it takes about 3 minutes: -
    - -
    +
    +```bash
     $ vespa feed dataset/docs.jsonl
    -
    -
    +``` + Now you can issue queries: -
    - -
    +
    +```bash
     $ vespa query 'query=yc b2b sales'
    -
    -
    +``` + -
    +
    +```bash
     $ vespa destroy --force
    -
    +``` + + +**TIP:** -> [!TIP] -> Add "-v" to see the HTTP request this becomes. +Add "-v" to see the HTTP request this becomes. + Congratulations! You have now created a RAG application that can scale to billions of documents and thousands of queries per second, while delivering state-of-the-art quality. diff --git a/en/learn/about-documentation.html b/en/learn/about-documentation.html deleted file mode 100644 index 4b24c3d4b2..0000000000 --- a/en/learn/about-documentation.html +++ /dev/null @@ -1,74 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "About this documentation" -redirect_from: -- /en/introduction-to-documentation.html ---- - -

    The Vespa documentation (https://docs.vespa.ai/) provides all the information required -to use all Vespa features and deploy them in any supported environment.

    - -

    It is split into guides and tutorials, which explains features and how to use them to solve problems, -and reference documentation which list complete information about all features and APIs.

    - - -

    Applicability

    - -

    The Vespa platform is open source, and can be deployed in self-managed systems and on the Vespa Cloud -service. Some add-ons (but no core functionality) are only available under a commercial license.

    - -

    Documents that describe functionality with such limited applicability are clearly marked by one -or more of the following chips:

    - - - - - - -
    {% include chip-cloud.html %}Only applicable to Vespa Cloud deployments.
    {% include chip-self-managed.html %}Only applicable to self-managed deployments.
    {% include chip-enterprise.html %}Not open source: Available commercially only (both self-managed and on cloud unless also marked by one of the other chips above).
    - -

    - -

    For clarity, any document not marked with any of these chips describes functionality that is -open source and available both on Vespa Cloud and self-managed deployments.

    - - -

    Contributing

    - -

    If you find errors or want to improve the documentation, -create an issue or -contribute a fix. See the README -before contributing. - - -

    Notation

    - -

    - Italic is used for: -

    -
      -
    • Pathnames, filenames, program names, hostnames, and URLs
    • -
    • New terms where they are defined
    • -
    -

    - Constant Width is used for: -

    -
      -
    • Programming language elements, code examples, keywords, functions, - classes, interfaces, methods, etc.
    • -
    • Commands and command-line output
    • -
    -

    -Commands meant to be run on the command line are shown like this, -prepended by a $ for the prompt: -

    -
    -$ export PATH=$VESPA_HOME/bin:$PATH   # how to highlight text in <pre>
    -
    -

    - Notes and other Important pieces of information are shown like: -

    -{% include note.html content="Some info here"%} -{% include important.html content="Important info here"%} -{% include warning.html content="Warning here"%} -{% include deprecated.html content="Deprecation warning here"%} diff --git a/en/learn/about-documentation.mdx b/en/learn/about-documentation.mdx new file mode 100644 index 0000000000..4d1fd4ebc1 --- /dev/null +++ b/en/learn/about-documentation.mdx @@ -0,0 +1,85 @@ +--- +title: About this documentation +description: "The Vespa documentation [https://docs.vespa.ai](https://docs.vespa.ai) provides all the information required to use all Vespa features and deploy them in any supported environment." +--- + +It is split into guides and tutorials, which explains features and how to use them to solve problems, and reference documentation which list complete information about all features and APIs. + +## Applicability + +The Vespa platform is open source, and can be deployed in self-managed systems and on the Vespa Cloud service. Some add-ons (but no core functionality) are only available under a commercial license. + +Documents that describe functionality with such limited applicability are clearly marked by one or more of the following chips: + + + + + + + + + + + + + + + + + + + + + + +
    **Vespa Cloud**Only applicable to Vespa Cloud deployments.
    **Self-managed**Only applicable to self-managed deployments.
    **Enterprise**Not open source: Available commercially only (both self-managed and on cloud unless also marked by one of the other chips above).
    + +For clarity, any document *not* marked with any of these chips describes functionality that is open source and available both on Vespa Cloud and self-managed deployments. + +## Contributing + +If you find errors or want to improve the documentation, [create an issue](https://github.com/vespa-engine/vespa/issues) or [contribute a fix](/en/learn/contributing). See the [README](https://docs.vespa.ai/README.md) before contributing. + +## Notation + +*Italic* is used for: + +- Pathnames, filenames, program names, hostnames, and URLs +- New terms where they are defined + +`Constant Width` is used for: + +- Programming language elements, code examples, keywords, functions, classes, interfaces, methods, etc. +- Commands and command-line output + +Commands meant to be run on the command line are shown like this, prepended by a $ for the prompt: + +```bash +$ export PATH=$VESPA_HOME/bin:$PATH # how to highlight text in pre +``` + +Notes and other Important pieces of information are shown like: + + +**Note:** + +Some info here + + + +**Important:** + +Important info here + + + +**Warning:** + +Warning here + + + +**Deprecation:** + +Deprecation warning here + diff --git a/en/learn/contributing.md b/en/learn/contributing.md deleted file mode 100644 index 08e7849340..0000000000 --- a/en/learn/contributing.md +++ /dev/null @@ -1,47 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Contributing to Vespa" -redirect_from: - - /en/contributing ---- - -Contributions to [Vespa](http://github.com/vespa-engine/vespa) -and the [Vespa documentation](http://github.com/vespa-engine/documentation) -are welcome. -This document tells you what you need to know to contribute. - -## Open development -All work on Vespa happens directly on GitHub, -using the [GitHub flow model](https://docs.github.com/en/get-started/quickstart/github-flow). -We release the master branch a few times a week, and you should expect it to almost always work. -In addition to the [builds seen on factory.vespa.ai](https://factory.vespa.ai) -we have a large acceptance and performance test suite which -is also run continuously. - -### Pull requests -All pull requests are reviewed by a member of the Vespa Committers team. -You can find a suitable reviewer in the OWNERS file upward in the source tree from -where you are making the change (the OWNERS have a special responsibility for -ensuring the long-term integrity of a portion of the code). -If you want to become a committer/OWNER making some quality contributions is the way to start. - -We require all pull request checks to pass. - -## Versioning -Vespa uses semantic versioning - see [vespa versions](releases.html). -Notice in particular that any Java API in a package having a @PublicAPI -annotation in the package-info file cannot be changed in an incompatible way -between major versions: Existing types and method signatures must be preserved -(but can be marked deprecated). - -## Issues -We track issues in [GitHub issues](https://github.com/vespa-engine/vespa/issues). -It is fine to submit issues also for feature requests and ideas, whether you intend to work on them or not. - -There is also a [ToDo list](https://github.com/vespa-engine/vespa/blob/master/TODO.md) for larger things -which no one are working on yet. - -## Community -If you have questions, want to share your experience or help others, -please join our community on the [Vespa Slack](https://slack.vespa.ai), -or see Vespa on [Stack Overflow](http://stackoverflow.com/questions/tagged/vespa). diff --git a/en/learn/contributing.mdx b/en/learn/contributing.mdx new file mode 100644 index 0000000000..476b071522 --- /dev/null +++ b/en/learn/contributing.mdx @@ -0,0 +1,28 @@ +--- +title: Contributing to Vespa +description: "Contributions to [Vespa](https://github.com/vespa-engine/vespa) and the [Vespa documentation](https://github.com/vespa-engine/documentation) are welcome. This document tells you what you need to know to contribute." +--- + +## Open development + +All work on Vespa happens directly on GitHub, using the [GitHub flow model](https://docs.github.com/en/get-started/quickstart/github-flow). We release the master branch a few times a week, and you should expect it to almost always work. In addition to the [builds seen on factory.vespa.ai](https://factory.vespa.ai) we have a large acceptance and performance test suite which is also run continuously. + +### Pull requests + +All pull requests are reviewed by a member of the Vespa Committers team. You can find a suitable reviewer in the OWNERS file upward in the source tree from where you are making the change (the OWNERS have a special responsibility for ensuring the long-term integrity of a portion of the code). If you want to become a committer/OWNER making some quality contributions is the way to start. + +We require all pull request checks to pass. + +## Versioning + +Vespa uses semantic versioning - see [vespa versions](/en/learn/releases). Notice in particular that any Java API in a package having a @PublicAPI annotation in the package-info file cannot be changed in an incompatible way between major versions: Existing types and method signatures must be preserved (but can be marked deprecated). + +## Issues + +We track issues in [GitHub issues](https://github.com/vespa-engine/vespa/issues). It is fine to submit issues also for feature requests and ideas, whether you intend to work on them or not. + +There is also a [ToDo list](https://github.com/vespa-engine/vespa/blob/master/TODO.md) for larger things which no one are working on yet. + +## Community + +If you have questions, want to share your experience or help others, please join our community on the [Vespa Slack](https://slack.vespa.ai), or see Vespa on [Stack Overflow](http://stackoverflow.com/questions/tagged/vespa). diff --git a/en/learn/faq.md b/en/learn/faq.md deleted file mode 100644 index 87c2fc8d1e..0000000000 --- a/en/learn/faq.md +++ /dev/null @@ -1,985 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: FAQ - frequently asked questions -redirect_from: - - /en/faq ---- -Refer to [Vespa Support](https://vespa.ai/support/) for more support options. - - - - - -{:.faq-section} -## Ranking - -### Does Vespa support a flexible ranking score? -[Ranking](../basics/ranking.html) is maybe the primary Vespa feature - -we like to think of it as scalable, online computation. -A rank profile is where the application's logic is implemented, -supporting simple types like `double` and complex types like `tensor`. -Supply ranking data in queries in query features (e.g. different weights per customer), -or look up in a [Searcher](../applications/searchers.html). -Typically, a document (e.g. product) "feature vector"/"weights" will be compared to a user-specific vector (tensor). - -### Where would customer specific weightings be stored? -Vespa doesn't have specific support for storing customer data as such. -You can store this data as a separate document type in Vespa and look it up before passing the query, -or store this customer meta-data as part of the other meta-data for the customer -(i.e. login information) and pass it along the query when you send it to the backend. -Find an example on how to look up data in -[album-recommendation-docproc](https://github.com/vespa-engine/sample-apps/tree/master/examples/document-processing). - -### How to create a tensor on the fly in the ranking expression? -Create a tensor in the ranking function from arrays or weighted sets using `tensorFrom...` functions - -see [document features](../reference/ranking/rank-features.html#document-features). - -### How to set a dynamic (query time) ranking drop threshold? -Pass a ranking feature like `query(threshold)` and use an `if` statement in the ranking expression - -see [retrieval and ranking](../ranking/ranking-intro#retrieval-and-ranking). Example: -
    -rank-profile drop-low-score {
    -   function my_score() {
    -     expression: ..... #custom first phase score
    -   }
    -   rank-score-drop-limit:0.0
    -   first-phase {
    -     if(my_score() < query(threshold), -1, my_score())
    -   }
    -}
    -
    - -### Are ranking expressions or functions evaluated lazily? -

    Rank expressions are not evaluated lazily. No, this would require lambda arguments. - Only doubles and tensors are passed between functions. - - Example:

    -
    -function inline foo(tensor, defaultVal) {
    -    expression: if (count(tensor) == 0, defaultValue, sum(tensor))
    -}
    -
    -function bar() {
    -    expression: foo(tensor, sum(tensor1 * tensor2))
    -}
    -
    - -### Does Vespa support early termination of matching and ranking? -Yes, this can be accomplished by configuring [match-phase](../reference/schemas/schemas.html#match-phase) -in the rank profile, or by adding a range query item using *hitLimit* to the query tree, -see [capped numeric range search](../reference/querying/yql.html#numeric). -Both methods require an *attribute* field with *fast-search*. The capped range query is faster, but beware that if -there are other restrictive filters in the query, one might end up with 0 hits. -The additional filters are applied as a post filtering -step over the hits from the capped range query. *match-phase* on the other hand, is safe to use with filters or other query terms, -and also supports diversification which the capped range query term does not support. - -### What could cause the relevance field to be -Infinity? -If a ranking profile produces NaNs or Infinities - -which are impossible to represent as a number in JSON - -the strings "Infinity" or "-Infinity", (NaN becomes "-Infinity") are returned in result sets, -and client libraries might handle that by default (e.g., Golang). - -The returned [relevance](../reference/querying/default-result-format.html#relevance) -for a hit can become "-Infinity" instead of a double: - -- The [ranking](../basics/ranking.html) expression used a feature which became `NaN` (Not a Number). - For example, `log(0)` would produce `-Infinity`. - Use [isNan](../reference/ranking/ranking-expressions.html#isnan-x) to guard against this. -- Surfacing low scoring hits using [grouping](../querying/grouping.html), that is, - rendering low ranking hits with `each(output(summary()))` that are outside what Vespa computed and caches on a heap. - This is controlled by the [total-keep-rank-count](../reference/schemas/schemas.html#total-keep-rank-count) parameter. -- Using unset fields in the ranking function - -Resolve this by one or more of: - -- Extend the client code to specifically handle these strings -- Make sure the field is set to some value for all documents -- Add a default value for the field when accessing it in your rank profile: - `if (isNan(attribute(last_update)), 0, attribute(last_update))` -- Add a final guard in the ranking expressions coercing to some small number, - making non-finite scores sink to the bottom while remaining a valid number: - ``` -function finite_or_sentinel(x) { - expression: if (isNan(x - x), -1e9, x) -} - ``` -- Use CBOR instead of JSON - using a binary format can represent NaNs and Infinities without issues, - and it can also be faster/more efficient - - -### How to pin query results? -To hard-code documents to positions in the result set, -see the [pin results example](../ranking/multivalue-query-operators.html#pin-results-example). - - -{:.faq-section} -## Documents - -### What limits apply to document size? -There is a [maximum document size](../reference/applications/services/container.html#document-api) of 100 MiB, -which is configurable per content cluster in services.xml. - -### Is there any size limitation for multivalued fields? -No enforced limit, except resource usage (memory). - -### Can a document have lists (key value pairs)? -E.g. a product is offered in a list of stores with a quantity per store. -Use [multivalue fields](../searching-multi-valued-fields.html) (array of struct) or [parent child](../schemas/parent-child.html). -Which one to chose depends on use case, see discussion in the latter link. - -### Does a whole document need to be updated and re-indexed? -E.g. price and quantity available per store may often change vs the actual product attributes. -Vespa supports [partial updates](../writing/reads-and-writes.html) of documents. -Also, the parent/child feature is implemented to support use-cases where child elements are updated frequently, -while a more limited set of parent elements are updated less frequently. - -### What ACID guarantees if any does Vespa provide for single writes / updates / deletes vs batch operations etc? -See the [Vespa Consistency Model](../content/consistency). -Vespa is not transactional in the traditional sense, it doesn't have strict ACID guarantees. -Vespa is designed for high performance use-cases with eventual consistency -as an acceptable (and to some extent configurable) trade-off. - -### Does vespa support wildcard fields? -Wildcard fields are not supported in vespa. -Workaround would be to use maps to store the wildcard fields. -Map needs to be defined with indexing: attribute and hence will be stored in memory. -Refer to [map](../reference/schemas/schemas.html#map). - -### Can we set a limit for the number of elements that can be stored in an array? -Implement a [document processor](../applications/document-processors.html) for this. - -### How to auto-expire documents / set up garbage collection? -Set a selection criterion on the `document` element in `services.xml`. -The criterion selects documents to keep. -I.e. to purge documents "older than two weeks", the expression should be "newer than two weeks". -Read more about [document expiry](../schemas/documents.html#document-expiry). - -### How to increase redundancy and track data migration progress? -Changing redundancy is a live and safe change -(assuming there is headroom on disk / memory - e.g. from 2 to 3 is 50% more). -The time to migrate will be quite similar to what it took to feed initially - -a bit hard to say generally, and depends on IO and index settings, like if building an HNSW index. -To monitor progress, take a look at the -[multinode](https://github.com/vespa-engine/sample-apps/tree/master/examples/operations/multinode) -sample application for the _clustercontroller_ status page - this shows buckets pending, live. -Finally, use the `.idealstate.merge_bucket.pending` metric to track progress - -when 0, there are no more data syncing operations - see -[monitor distance to ideal state](../operations/self-managed/admin-procedures.html#monitor-distance-to-ideal-state). -Nodes will work as normal during data sync, and query coverage will be the same. - -### How does namespace relate to schema? -It does not, -_namespace_ is a mechanism to split the document space into parts that can be used for document selection - -see [documentation](../schemas/documents.html#namespace). The namespace is not indexed and cannot -be searched using the query api, but can be used by [visiting](../writing/visiting.html). - -### Visiting does not dump all documents, and/or hangs. -There are multiple things that can cause this, see [visiting troubleshooting](../writing/visiting.html#troubleshooting). - -### How to find number of documents in the index? -Run a query like `vespa query "select * from sources * where true"` and see the `totalCount` field. -Alternatively, use metrics or `vespa visit` - see [examples](../writing/batch-delete.html#example). - -### Can I define a default value for a field? - -Not in the field definition, but it's possible to do this with the [choice](../writing/indexing.html#choice-example) -expression in an indexing statement. - - -{:.faq-section} -## Query - -### Are hierarchical facets supported? -Facets is called grouping in Vespa. -Groups can be multi-level. - -### Are filters supported? -Add filters to the query using [YQL](../querying/query-language.html) -using boolean, numeric and [text matching](../querying/text-matching.html). Query terms can be annotated -as filters, which means that they are not highlighted when bolding results. - -### How to query for similar items? -One way is to describe items using tensors and query for the -[nearest neighbor](../reference/querying/yql.html#nearestneighbor) - -using full precision or approximate (ANN) - the latter is used when the set is too large for an exact calculation. -Apply filters to the query to limit the neighbor candidate set. -Using [dot products](../ranking/multivalue-query-operators.html) or [weak and](../ranking/wand.html) are alternatives. - -### Does Vespa support stop-word removal? -Vespa does not have a stop-word concept inherently. -See the [sample app](https://github.com/vespa-engine/sample-apps/pull/335/files) -for how to use [filter terms](../reference/querying/yql.html#annotations). -[Tripling the query performance of lexical search](https://blog.vespa.ai/tripling-the-query-performance-of-lexical-search/) -it s good blog post on this subject. - -### How to extract more than 400 hits / query and get ALL documents? -Trying to request more than 400 hits in a query, getting this error: -`{'code': 3, 'summary': 'Illegal query', 'message': '401 hits requested, configured limit: 400.'}`. - -* To increase max result set size (i.e. allow a higher [hits](../reference/api/query.html#hits)), - configure `maxHits` in a [query profile](../reference/api/query.html#queryprofile), - e.g. `500` in `search/query-profiles/default.xml` (create as needed). - The [query timeout](../reference/api/query.html#timeout) can be increased, - but it will still be costly and likely impact other queries - - large limit more so than a large offset. - It can be made cheaper by using a smaller [document summary](../querying/document-summaries.html), - and avoiding fields on disk if possible. -* Using _visit_ in the [document/v1/ API](../writing/document-v1-api-guide.html) - is usually a better option for dumping all the data. - -### How to make a sub-query to get data to enrich the query, like get a user profile? -See the [UserProfileSearcher](https://github.com/vespa-engine/sample-apps/blob/master/news/app-6-recommendation-with-searchers/src/main/java/ai/vespa/example/UserProfileSearcher.java) -for how to create a new query to fetch data - -this creates a new Query, sets a new root and parameters - then `fill`s the Hits. - -### How to create a cache that refreshes itself regularly - -See the sub-query question above, in addition add something like: -
    {% highlight java%}
    -
    -public class ConfigCacheRefresher extends AbstractComponent {
    -
    -    private final ScheduledExecutorService configFetchService = Executors.newSingleThreadScheduledExecutor();
    -    private Chain searcherChain;
    -
    -    void initialize() {
    -        Runnable task = () -> refreshCache();
    -        configFetchService.scheduleWithFixedDelay(task, 1, 1, TimeUnit.MINUTES);
    -        searcherChain = executionFactory.searchChainRegistry().getChain(new ComponentId("configDefaultProvider"));
    -    }
    -
    -    public void refreshCache() {
    -        Execution execution = executionFactory.newExecution(searcherChain);
    -        Query query = createQuery(execution);
    -
    -    public void deconstruct() {
    -        super.deconstruct();
    -        try {
    -            configFetchService.shutdown();
    -            configFetchService.awaitTermination(1, TimeUnit.MINUTES);
    -        }catch(Exception e) {..}
    -    }
    -}
    -{% endhighlight %}
    - -### Is it possible to query Vespa using a list of document IDs? -Yes, using the [in query operator](../reference/querying/yql.html#in). Example: -``` -select * from data where user_id in (10, 20, 30) -``` -The best article on the subject is -[multi-lookup set filtering](../performance/feature-tuning.html#multi-lookup-set-filtering). -Refer to the [in operator example](../ranking/multivalue-query-operators.html#in-example) -on how to use it programmatically in a [Java Searcher](../applications/searchers.html). - -### How to query documents where one field matches any values in a list? Similar to using SQL IN operator -Use the [in query operator](../reference/querying/yql.html#in). Example: -``` -select * from data where category in ('cat1', 'cat2', 'cat3') -``` -See [multi-lookup set filtering](#is-it-possible-to-query-vespa-using-a-list-of-document-ids) -above for more details. - - -### How to count hits / all documents without returning results? -Count all documents using a query like [select * from doc where true](../querying/query-language.html) - -this counts all documents from the "doc" source. -Using `select * from doc where true limit 0` will return the count and no hits, -alternatively add [hits=0](../reference/api/query.html#hits). -Pass [ranking.profile=unranked](../reference/api/query.html#ranking.profile) -to make the query less expensive to run. -If an _estimate_ is good enough, use [hitcountestimate=true](../reference/api/query.html#hitcountestimate). - -### Must all fields in a fieldset have compatible type and matching settings? -Yes - a deployment warning with _This may lead to recall and ranking issues_ is emitted -when fields with conflicting tokenization are put in the same -[fieldset](../reference/schemas/schemas.html#fieldset). -This is because a given query item searching one fieldset is tokenized just once, -so there's no right choice of tokenization in this case. -If you have text that you want to apply to multiple fields with different tokenization, -include the text multiple times in the query: -``` -select * from sources * where fieldsetOrField1 contains text(@query) or fieldsetOrField2 contains text(@query) -``` -More details on [stack overflow](https://stackoverflow.com/questions/72784136/why-vepsa-easily-warning-me-this-may-lead-to-recall-and-ranking-issues). - -### Why can common words like "the" hurt recall and collapse significance across a fieldset? -Symptoms — can appear when a term's DF differs substantially between member fields: -- Poor recall for queries mixing a common term with a rare one (e.g. `"the cure"`, `"the X"`). - [weakAnd](../ranking/wand.html) may drop the common term, so good matches never surface. -- `term(n).significance` and `fieldMatch(field).significance` read identical across member fields - in rank-feature dumps — even in fields where the term is actually rare. - -Cause: when a term matches a [fieldset](../reference/schemas/schemas.html#fieldset) -(including the implicit `default` used by [userQuery()](../reference/querying/yql.html#userquery)), -Vespa aggregates the document frequency across all member fields. - -If the DF differs substantially between members, the high-DF field dominates -and pulls the term's significance down for the whole fieldset. - -**Example:** - -With `fieldset default { fields: title, artist }`, `"the"` is common in `title` -(countless _"The Watcher"_, _"The Best Of the ..."_) but rare in `artist`. - -Its aggregated significance is pulled down toward the `title` DF, -so searching for the artist `"The Cure"` loses the signal from `"the"`. - -The same aggregated DF drives every DF/IDF feature: [bm25](../ranking/bm25.html), -[nativeRank](../ranking/nativerank.html), `term(n).significance`, `fieldMatch.significance`. - -Matches that survive retrieval are scored using the aggregated DF rather than per-field statistics. - -Fix: rewrite as OR'd [userInput](../reference/querying/yql.html#userinput) clauses -with a [defaultIndex](../reference/querying/yql.html#defaultindex) annotation per field. -Each field then uses its own DF: - -*Combined-fieldset DF:* - -```bash -vespa query 'select * from sources * where userQuery()' \ - query='the cure' -``` - -*Per-field DF:* - -```bash -vespa query 'select * from sources * where ({defaultIndex:"title"}userInput(@q)) or ({defaultIndex:"artist"}userInput(@q))' \ - q='the cure' -``` - -{% include important.html content="BM25 and significance feature values shift scale when switching to per-field DF. -Retrain any learned ranker on features collected with the new query formulation."%} - -### How is the query timeout computed? -Find query timeout details in the [Query API Guide](../querying/query-api.html#timeout) -and the [Query API Reference](../reference/api/query.html#timeout). - -### How does backslash escapes work? -Backslash is used to escape special characters in YQL. -For example, to query with a literal backslash, which is useful in regexpes, -you need to escape it with another backslash: \\. -Unescaped backslashes in YQL will lead to "token recognition error at: '\'". - -In addition, Vespa CLI unescapes double backslashes to single -(while single backslashes are left alone), -so if you query with Vespa CLI you need to escape with another backslash: \\\\. -The same applies to strings in Java. - -Also note that both log messages and JSON results escape backslashes, so any \ becomes \\. - -### Is it possible to have multiple SELECT statements in a single call (subqueries)? -E.g. two select queries with slightly different filtering condition and have a limit operator for each of the subquery. -This makes it impossible to do via OR conditions to select both collection of documents - something equivalent to: - - SELECT 1 AS x - UNION ALL - SELECT 2 AS y; - -This isn’t possible, need to run 2 queries. -Alternatively, split a single incoming query into two running in parallel in a [Searcher](../applications/searchers.html) - example: -```java -FutureResult futureResult = new AsyncExecution(settings).search(query); -FutureResult otherFutureResult = new AsyncExecution(settings).search(otherQuery); -``` -### Is it possible to query for the number of elements in an array -There is no index or attribute data structure that allows efficient _searching_ for documents where -an array field has a certain number of elements or items. -The _grouping language_ has a [size()](../reference/querying/grouping-language.html#list-expressions) operator that can be used in queries. - -### Is it possible to query for fields with NaN/no value set/null/none -The [visiting](../writing/visiting.html#analyzing-field-values) API using document selections supports it, with a linear scan over all documents. -If the field is an _attribute_ one can query using grouping to identify Nan Values, -see count and list [fields with NaN](../querying/grouping.html#count-fields-with-nan). - -### How to retrieve random documents using YQL? Functionality similar to MySQL "ORDER BY rand()" -See the [random.match](../reference/ranking/rank-features.html#random.match) rank feature - example: -``` -rank-profile random { - first-phase { - expression: random.match - } -} -``` -Run queries, seeding the random generator: -``` -$ vespa query 'select * from music where true' \ - ranking=random \ - rankproperty.random.match.seed=2 -``` - -### Some of the query results have too many hits from the same source, how to create a diverse result set? -See [result diversity](../querying/result-diversity) for strategies on how to create result sets from different sources. - -### How to find most distant neighbor in a embedding field called clip_query_embedding? -If you want to search for the most dissimilar items, -you can with angular distance multiply your `clip_query_embedding` by the scalar -1. -Then you are searching for the points that are closest to the point -which is the farthest away from your `clip_query_embedding`. - -Also see a [pyvespa example](https://vespa-engine.github.io/pyvespa/examples/pyvespa-examples.html#Neighbors). - - -{:.faq-section} -## Feeding - -### How to debug a feeding 400 response? -The best option is to use `--verbose` option, like `vespa feed --verbose myfile.jsonl` - -see [documentation](../clients/vespa-cli.html#documents). -A common problem is a mismatch in schema names and [document IDs](../schemas/documents.html#document-ids) - a schema like: -``` -schema article { - document article { - ... - } -} -``` -will have a document feed like: -``` -{"put": "id:mynamespace:article::1234", "fields": { ... }} -``` -Note that the [namespace](glossary.html#namespace) is not mentioned in the schema, -and the schema name is the same as the document name. - -### How to debug document processing chain configuration? -This configuration is a combination of content and container cluster configuration, -see [indexing](../writing/indexing.html) and [feed troubleshooting](../operations/self-managed/admin-procedures.html#troubleshooting). - -### I feed documents with no error, but they are not in the index -This is often a problem if using [document expiry](../schemas/documents.html#document-expiry), -as documents already expired will not be persisted, they are silently dropped and ignored. -Feeding stale test data with old timestamps in combination with document-expiry can cause this -behavior. - -### How to feed many files, avoiding 429 error? -Using too many HTTP clients can generate a 429 response code. -The Vespa sample apps use [vespa feed](../clients/vespa-cli.html#documents) which uses HTTP/2 for high throughput - -it is better to stream the feed files through this client. - -### Can I use Kafka to feed to Vespa? -Vespa does not have a Kafka connector. -Refer to third-party connectors like [kafka-connect-vespa](https://github.com/vinted/kafka-connect-vespa). - - - -{:.faq-section} -## Text Search - -### Does Vespa support addition of flexible NLP processing for documents and search queries? -E.g. integrating NER, word sense disambiguation, specific intent detection. -Vespa supports these things well: -- [Query (and result) processing](../applications/searchers.html) -- [Document processing](../applications/document-processors.html) - and document processors working on semantic annotations of text - -### Does Vespa support customization of the inverted index? -E.g. instead of using terms or n-grams as the unit, we might use terms with specific word senses - -e.g. bark (dog bark) vs. bark (tree bark), or BCG (company) vs. BCG (vaccine name). -Creating a new index format means changing the core. -However, for the examples above, one just need control over the tokens which are indexed (and queried). -That is easily done in some Java code. -The simplest way to do this is to plug in a [custom tokenizer](../linguistics/linguistics.html). -That gets called from the query parser and bundled linguistics processing [Searchers](../applications/searchers.html) -as well as the [Document Processor](../applications/document-processors.html) -creating the annotations that are consumed by the indexing operation. -Since all that is Searchers and Docprocs which you can replace and/or add custom components before and after, -you can also take full control over these things without modifying the platform itself. - -### Does vespa provide any support for named entity extraction? -It provides the building blocks but not an out-of-the-box solution. -We can write a [Searcher](../applications/searchers.html) to detect query-side entities and rewrite the query, -and a [DocProc](../applications/document-processors.html) if we want to handle them in some special way on the indexing side. - -### Does vespa provide support for text extraction? -You can write a document processor for text extraction, Vespa does not provide it out of the box. - -### How to do Text Search in an imported field? -[Imported fields](../schemas/parent-child.html) from parent documents are defined as [attributes](../content/attributes.html), -and have limited text match modes (i.e. `indexing: index` cannot be used). -[Details](https://stackoverflow.com/questions/71936330/parent-child-mode-cannot-be-searched-by-parent-column). - - - -{:.faq-section} -## Semantic search - -### Why is closeness 1 for all my vectors? - -If you have added vectors to your documents and queries, and see that the rank feature -closeness(field, yourEmbeddingField) produces 1.0 for all documents, you are likely using -[distance-metric](../reference/schemas/schemas.html#distance-metric): innerproduct/prenormalized-angular, -but your vectors are not normalized, and the solution is normally to switch to -[distance-metric: angular](../reference/schemas/schemas.html#angular) -or use -[distance-metric: dotproduct](../reference/schemas/schemas.html#dotproduct) -(available from {% include version.html version="8.170.18" %}). - -With non-normalized vectors, you often get negative distances, and those are capped to 0, -leading to closeness 1.0. -Some embedding models, such as models from sbert.net, claim to output normalized vectors but might not. - - - -{:.faq-section} -## Programming Vespa - -### Is Python plugins supported / is there a scripting language? -Plugins have to run in the JVM - [jython](https://www.jython.org/) might be an alternative, -however Vespa Team has no experience with it. -Vespa does not have a language like -[painless](https://www.elastic.co/guide/en/elasticsearch/reference/current/modules-scripting-painless.html) - -it is more flexible to write application logic in a JVM-supported language, using -[Searchers](../applications/searchers.html) and [Document Processors](../applications/document-processors.html). - -### How can I batch-get documents by ids in a Searcher -A [Searcher](../applications/searchers.html) intercepts a query and/or result. -To get a number of documents by id in a Searcher or other component like a [Document processor](../applications/document-processors.html), -you can have an instance of [com.yahoo.documentapi.DocumentAccess](../reference/applications/components.html#injectable-components) -injected and use that to get documents by id instead of the HTTP API. - - - - - - -### Does Vespa work with Java 20? -Vespa uses Java 17 - it will support 20 some time in the future. - -### How to write debug output from a custom component? -Use `System.out.println` to write text to the [vespa.log](../reference/operations/log-files.html). - - - -{:.faq-section} -## Performance - -### What is the latency of documents being ingested vs indexed and available for search? -Vespa has a near real-time indexing core with typically sub-second latencies from document ingestion to being indexed. -This depends on the use-case, available resources and how the system is tuned. -Some more examples and thoughts can be found in the [scaling guide](../performance/sizing-search.html). - -### Is there a batch ingestion mode, what limits apply? -Vespa does not have a concept of "batch ingestion" -as it contradicts many of the core features that are the strengths of Vespa, -including [serving elasticity](../content/elasticity.html) and sub-second indexing latency. -That said, we have numerous use-cases in production -that do high throughput updates to large parts of the (sometimes entire) document set. -In cases where feed throughput is more important than indexing latency, you can tune this to meet your requirements. -Some of this is detailed in the [feed sizing guide](../performance/sizing-feeding.html). - -### Can the index support up to 512 GB index size in memory? -Yes. The [content node](../content/proton.html) is implemented in C++ -and not memory constrained other than what the operating system does. - -### Get request for a document when document is not in sync in all the replica nodes? -If the replicas are in sync the request is only sent to the primary content node. -Otherwise, it's sent to several nodes, depending on replica metadata. -Example: if a bucket has 3 replicas A, B, C and A & B both have metadata state X and C has metadata state Y, -a request will be sent to A and C -(but not B since it has the same state as A and would therefore not return a potentially different document). - -### How to keep indexes in memory? -[Attribute](../content/attributes.html) (with or without `fast-search`) is always in memory, -but does not support tokenized matching. -It is for structured data. -[Index](../basics/schemas.html#document-fields) (where there’s no such thing as fast-search since it is always fast) -is in memory to the extent there is available memory and supports tokenized matching. -It is for unstructured text. - -It is possible to guarantee that fields that are defined with `index` -have both the dictionary and the postings in memory by changing from `mmap` to `populate`, -see [index > io > search](../reference/applications/services/content.html#index-io-search). -Make sure that the content nodes run on nodes with plenty of memory available, -during index switch the memory footprint will 2x. -Familiarity with Linux tools like `pmap` can help diagnose what is mapped and if it’s resident or not. - -Fields that are defined with `attribute` are in-memory, -fields that have both `index` and `attribute` have separate data structures, -queries will use the default mapped on disk data structures that supports `text` matching, -while grouping, summary and ranking can access the field from the `attribute` store. - -A Vespa query is executed in two phases as described in [sizing search](../performance/sizing-search.html), -and summary requests can touch disk (and also uses `mmap` by default). -Due to their potential size there is no populate option here, -but one can define [dedicated document summary](../querying/document-summaries.html#performance) -containing only fields that are defined with `attribute`. - -The [practical performance guide](../performance/practical-search-performance-guide) -can be a good starting point as well to understand Vespa query execution, -difference between `index` and `attribute` and summary fetching performance. - -### Is memory freed when deleting documents? -Deleting documents, by using the [document API](../writing/reads-and-writes.html) -or [garbage collection](../schemas/documents.html#document-expiry) will increase the capacity on the content nodes. -However, this is not necessarily observable in system metrics - -this depends on many factors, like what kind of memory that is released, -when [flush](../content/proton.html#proton-maintenance-jobs) jobs are run and document [schema](../basics/schemas.html). - -In short, Vespa is not designed to release memory once used. -It is designed for sustained high throughput, low latency, -keeping maximum memory used under control -using features like [feed block](../writing/feed-block.html). - -When deleting documents, one can observe a slight increase in memory. -A deleted document is represented using a [tombstone](../operations/self-managed/admin-procedures.html#content-cluster-configuration), -that will later be removed, see [removed-db-prune-age](../reference/applications/services/content.html#removed-db-prune-age). -When running garbage collection, -the summary store is scanned using mmap and both VIRT and page cache memory usage increases. - -Read up on [attributes](../content/attributes.html) to understand more of how such fields are stored and managed. -[Paged attributes](../content/attributes.html#paged-attributes) trades off memory usage vs. query latency -for a lower max memory usage. - -### Do empty fields consume memory? -A field is of type _index_ or _attribute_ - [details](../querying/text-matching.html#index-and-attribute). - -Fields with _index_ use no incremental memory at deployment, if the field has no value. - -Fields with _attribute_ use memory, even if the field value is not set, - -Attributes are optimized for random access: To be able to jump to the value of any document in O(1) time. -That requires allocating a constant amount of memory (the value, or a pointer) per document, -regardless of whether there is a value. -In short, knowing that a value is unset is a value in itself for attributes, -so deploying new fields or new schemas with attributes will cause an incremental increase in memory. -Applications with many unused schemas and fields can factor this in when sizing for memory. -Refer to [attributes](../content/attributes.html#attribute-memory-usage) for details. - -### What is the best practice for scaling Vespa for day vs night? -[Autoscaling](../operations/autoscaling.html) is the best guide to understand how to size and autoscale the system. -Container clusters are stateless and can be autoscaled more quickly than content clusters. - -### We can spike 8x in 5 minutes in terms of throughput requirements. -It is not possible to autoscale content clusters for 8x load increase in 5 minutes, -as this requires both provisioning and data migration. -Such use cases are best discussed with the Vespa Team to understand the resource bottlenecks, -tradeoffs and mitigations. -Also read [Graceful Degradation](../performance/graceful-degradation.html). - -### How much lower-level configuration do we need to do? For example, do we need to alter the number of threads per container? -It depends. Vespa aims to adapt to resources (like auto thread config based on virtual node thread count) -and actual use (when to run maintenance jobs like compaction), -but there are tradeoffs that applications owners can/should make. -Start off by reading the [Vespa Serving Scaling Guide](../performance/sizing-search.html), -then run [benchmarks](../performance/benchmarking-cloud.html) and use the [dashboards](../operations/monitoring.html). - - -{:.faq-section} -## Administration - -### Self-managed: Can one do a partial deploy to the config server / update the schema without deploying all the node configs? -Yes, deployment is using this web service API, -which allows you to create an edit session from the currently deployed package, -make modifications, and deploy (prepare+activate) it: [deploy-rest-api-v2.html](../reference/api/deploy-v2.html). -However, this is only useful in cases where you want to avoid transferring data to the config server unnecessarily. -When you resend everything, the config server will notice that you did not actually change e.g. the node configs -and avoid unnecessary noop changes. - -### How fast can nodes be added and removed from a running cluster? -[Elasticity](../content/elasticity.html) is a core Vespa strength - -easily add and remove nodes with minimal (if any) serving impact. -The exact time needed depends on how much data will need to be migrated in the background -for the system to converge to [ideal data distribution](../content/idealstate.html). - -### Should Vespa API search calls be load balanced or does Vespa do this automatically? -You will need to load balance incoming requests between the nodes running the -[stateless Java container cluster(s)](overview.html). -This can typically be done using a simple network load balancer available in most cloud services. -This is included when using [Vespa Cloud](https://cloud.vespa.ai/), -with an HTTPS endpoint that is already load balanced - both locally within the region and globally across regions. - -### Supporting index partitions -[Search sizing](../performance/sizing-search.html) is the intro to this. -Topology matters, and this is much used in the high-volume Vespa applications to optimise latency vs. cost. - -### Can a running cluster be upgraded with zero downtime? -With [Vespa Cloud](https://cloud.vespa.ai/), -we do automated background upgrades daily without noticeable serving impact. -If you host Vespa yourself, you can do this, but need to implement the orchestration logic necessary to handle this. -The high level procedure is found in [live-upgrade](../operations/self-managed/live-upgrade.html). - -### Can Vespa be deployed multi-region? -[Vespa Cloud](https://cloud.vespa.ai/en/reference/zones) has integrated support - query a global endpoint. -Writes will have to go to each zone. There is no auto-sync between zones. - -### Can Vespa serve an Offline index? -Building indexes offline requires the partition layout to be known in the offline system, -which is in conflict with elasticity and auto-recovery -(where nodes can come and go without service impact). -It is also at odds with realtime writes. -For these reasons, it is not recommended, and not supported. - -### Does vespa give us any tool to browse the index and attribute data? -Use [visiting](../writing/visiting.html) to dump all or a subset of the documents. -See [data-management-and-backup](https://cloud.vespa.ai/en/data-management-and-backup) for more information. - -### What is the response when data is written only on some nodes and not on all replica nodes (Based on the redundancy count of the content cluster)? -Failure response will be given in case the document is not written on some replica nodes. - -### When the doc is not written to some nodes, will the document become available due to replica reconciliation? -Yes, it will be available, eventually. -Also try [Multinode testing and observability](https://github.com/vespa-engine/sample-apps/tree/master/examples/operations/multinode). - -### Does vespa provide soft delete functionality? -Yes just add a `deleted` attribute, add [fast-search](../content/attributes.html#fast-search) on it -and create a searcher which adds an `andnot deleted` item to queries. - -### Can we configure a grace period for bucket distribution so that buckets are not redistributed as soon as a node goes down? -You can set a [transition-time](../reference/applications/services/content.html#transition-time) in services.xml -to configure the cluster controller how long a node is to be kept in maintenance mode -before being automatically marked down. - -### What is the recommended redundant/searchable-copies config when using grouping distribution? -Grouped distribution is used to reduce search latency. -Content is distributed to a configured set of groups, -such that the entire document collection is contained in each group. -Setting the redundancy and searchable-copies equal to the number of groups -ensures that data can be queried from all groups. - -### How to set up for disaster recovery / backup? -Refer to [#17898](https://github.com/vespa-engine/vespa/issues/17898) for a discussion of options. - -### Self-managed: How to check Vespa version for a running instance? -Use [/state/v1/version](../reference/api/state-v1.html#state-v1-version) to find Vespa version. - -### Deploy rollback -See [rollback](../applications/deployment.html#rollback) for options. - - -{:.faq-section} -## Troubleshooting - -### Deployment fails with response code 413 -If deployment fails with error message "Deployment failed, code: 413 ("Payload Too Large.")" -you might need to increase the config server's JVM heap size. The config server has a default -JVM heap size of 2 Gb. When deploying an app with e.g. large models this might not be enough, -try increasing the heap to e.g. 4 Gb when executing 'docker run ...' by adding an environment -variable to the command line: - -``` -docker run --env VESPA_CONFIGSERVER_JVMARGS=-Xmx4g -``` - -### The endpoint does not come up after deployment -When deploying an application package, with some kind of error, the endpoints might fail, like: -``` -$ vespa deploy --wait 300 - -Uploading application package ... done - -Success: Deployed target/application.zip - -Waiting up to 5m0s for query service to become available ... -Error: service 'query' is unavailable: services have not converged -``` -Another example: -``` -[INFO] [03:33:48] Failed to get 100 consecutive OKs from endpoint ... -``` -There are many ways this can fail, the first step is to check the Vespa Container: -``` -$ docker exec vespa vespa-logfmt -l error - -[2022-10-21 10:55:09.744] ERROR container -Container.com.yahoo.container.jdisc.ConfiguredApplication -Reconfiguration failed, your application package must be fixed, unless this is a JNI reload issue: -Could not create a component with id 'ai.vespa.example.album.MetalSearcher'. -Tried to load class directly, since no bundle was found for spec: album-recommendation-java. -If a bundle with the same name is installed, -there is a either a version mismatch or the installed bundle's version contains a qualifier string. -... -``` -[Bundle plugin troubleshooting](../applications/bundles.html#bundle-plugin-troubleshooting) is a good resource -to analyze Vespa container startup / bundle load problems. - - - -### Starting Vespa using Docker on M1 fails -Using an M1 MacBook Pro / AArch64 makes the Docker run fail: -``` -WARNING: The requested image’s platform (linux/amd64) does not match the detected host platform (linux/arm64/v8) -and no specific platform was requested -``` -Make sure you are running a recent version of the Docker image, do `docker pull vespaengine/vespa`. - - -### Deployment fails / nothing is listening on 19071 -Make sure all [Config servers](../operations/self-managed/configuration-server.html#troubleshooting) are started, -and are able to establish ZooKeeper quorum (if more than one) - -see the [multinode](https://github.com/vespa-engine/sample-apps/tree/master/examples/operations/multinode) sample application. -Validate that the container has [enough memory](../operations/self-managed/docker-containers.html). - -### Startup problems in multinode Kubernetes cluster - readinessProbe using 19071 fails -The Config Server cluster with 3 nodes fails to start. -The ZooKeeper cluster the Config Servers use waits for hosts on the network, -the hosts wait for ZooKeeper in a catch 22 - -see [sampleapp troubleshooting](https://github.com/vespa-engine/sample-apps/tree/master/examples/operations#troubleshooting). - -### How to display vespa.log? -Use [vespa-logfmt](../reference/operations/self-managed/tools.html#vespa-logfmt) to dump logs. -If Vespa is running in a local container (named "vespa"), run `docker exec vespa vespa-logfmt`. - -### How to fix encoding problems in document text? -See [encoding troubleshooting](../linguistics/troubleshooting-encoding.html) -for how to handle and remove control characters from the document feed. - - - -## Login, Tenants and Plans - -### How to get started? -[Deploy an application](../basics/deploy-an-application.html) to create a tenant and start your -[free trial](https://vespa.ai/free-trial/). -This tenant can be your personal tenant, or shared with others. -It can not be renamed. - -### How to create a company tenant? -If the tenant is already created, add more users to it. -In the Vespa Cloud Console, open [**Account > Users**](https://console.vespa-cloud.com/link/tenant/account/users). -From this view you can manage users in the tenant, and their roles - -from here, you can add/set tenant admins. - -### How to accept Terms of Service? -When starting the free trial, you are asked to accept Terms of Service. -For paid plans, this is covered by the contract. - -### How do I switch from free trial to a paid plan? -In the console, open [**Account > Billing**](https://console.vespa-cloud.com/link/tenant/account/billing) to enter information required for billing. -Use [Vespa Support](https://vespa.ai/support/) if you need to provide this information without console login. - -### Does Vespa Cloud support Single Sign-On (SSO)? -Yes, SSO is available for customers on the Enterprise plan. -See the [Single Sign-On](../operations/sso.html) documentation for setup instructions. - - - -## Vespa Cloud Operations - -### How can I change the cost of my Vespa Cloud usage? -See [node resources](../performance/node-resources) to assess current and auto-suggested resources -and [autoscaling](../operations/autoscaling.html) for how to automate. - -### How can I manually modify resources used? -Managing resources is easy, as most changes are automated. -Adding / removing / changing nodes starts automated data migration, -see [elasticity](../content/elasticity.html). - -### How to modify a schema? -Schema changes might require data reindexing, which is automated, but takes some time. -Other schema changes require data refeed - [details](../reference/schemas/schemas.html#modifying-schemas) - - -### How to evaluate how much memory a field is using? -Use the [Memory Visualizer](../performance/memory-visualizer.html) to evaluate how memory is allocated to the fields. -Fields can be `index`, `attribute` and `summary`, and combinations of these, -with settings like `fast-search` that affects memory usage. -[Attributes](../content/attributes.html) is a great read for understanding Vespa memory usage. - -### Archive access failed with Permission 'serviceusage.services.use' denied -Listing archived objects can fail, -e.g. `gsutil -u my_project ls gs://vespa-cloud-data-prod-gcp-us-central1-f-12345f/my_tenant` can fail with -`AccessDeniedException: 403 me@mymail.com does not have serviceusage.services.use access to the Google Cloud project. -Permission \'serviceusage.services.use\' denied on resource (or it may not exist).` -This can be due to missing rights on your Google project (my_project in the example above) -\- from the Google documentation: -_"The user account accessing the Cloud Storage Bucket must be granted the Service Usage Consumer role -(see [https://cloud.google.com/service-usage/docs/access-control](https://cloud.google.com/service-usage/docs/access-control)) -in order to charge the specified user project for the bucket usage cost"_ - -### How do I integrate with my current monitoring infrastructure? -Vespa Cloud applications have a Prometheus endpoint. -Find guides for how to integrate with Grafana and AWS Cloudwatch at [monitoring](../operations/monitoring.html). - -### What is the best way to monitor instantaneously what is happening in Vespa? CPU usage? Memory usage? htop? Cloudwatch metrics? -Vespa Cloud has detailed dashboards linked from the _monitoring_ tab in the Console, -one for each zone the instance is deployed to. - -### How are Vespa versions upgrades handled - only for new deploys? -Vespa is normally upgraded daily. There are exceptions, like holidays and weekends. -During upgrades, nodes are stopped one-by-one per cluster. -As all clusters have one redundant node, serving and write traffic is not impacted by upgrades. -Before the upgrade, the application's [system and staging tests](../operations/automated-deployments.html) are run, -halting the upgrade if they fail. -Documents are re-migrated to the upgraded node before doing the next node, -see [Elastic Vespa](../content/elasticity.html) for details. - -### How do we get alerted to issues like Feed Block? Searchable copy going offline? -Issues like Feed Blocked, Deployment and Deprecation warnings show up in the console. -There are no warnings on redundancy level / searchable copies, -as redundant document buckets are activated for queries automatically, -and auto data-migration kicks in for node failures / replacements. - -### What actions are needed when deploying schema changes? -* Schema changes that [require service restart](../reference/schemas/schemas.html#changes-that-require-restart-but-not-re-feed) - are handled automatically by Vespa Cloud. A deployment job involves waiting for these to complete. -* Schema changes that [require reindexing](../reference/schemas/schemas.html#changes-that-require-reindexing) - of data require a validation override, and will trigger automatic reindexing. Status can be tracked in the console application view. - Vespa Cloud also periodically re-indexes all data, with minimal resource usage, to account for changes in linguistics libraries. -* Schema changes that [require refeeding](../reference/schemas/schemas.html#changes-that-require-re-feed) - data require a validation override, and the user must refeed the data after deployment. - -### What are the Vespa Cloud data retention policies? -The management of data stored in an application running on Vespa Cloud is the responsibility of the -application owner and, as such, Vespa Cloud does not have any retention policy for this data as -long as it is stored by the application. - -The following data retention policies applies to Vespa Cloud: -* After a node previously allocated to an application has been deallocated (e.g. due to - application being deleted by application owner), all application data will be deleted within - _four hours_. -* All application log data will be deleted from Vespa servers after no more than _30 days_ (most - often sooner) dependent on log volume, allocated disk resources, etc. *PLEASE NOTE:* This is - the theoretical maximum retention time - see [archive guide](../cloud/archive-guide.html) for how to ensure - access to your application logs. - - - -### Is Vespa Cloud certified for ISO 27001 or SOC 2? -Yes, Vespa.ai has a SOC 2 attestation: [Trust Center](https://trust.vespa.ai). - -### Is Vespa Cloud GDPR compliant? -Read more in [GDPR](https://cloud.vespa.ai/en/gdpr). - -### Does Vespa store information from the information sources with which it is integrated? -Vespa is most often used for queries in data written from the information sources, -although it can also be used without data, e.g. for model serving. -It is the application owner that writes the integration with Vespa Cloud to write data. - -### What is the encryption algorithm used at rest? -Vespa Cloud uses the following Cloud providers: -* AWS EC2 instances, with local or remote storage -* GCP Compute instances, with local or remote storage -* Azure Compute instances, with local or remote storage - -The storage devices are encrypted per Cloud provider, at rest. - -### Does the Vespa console have audit trails/logs module and can it be accessed by an Admin user? -See the [security guide](../security/guide.html) for roles and permissions. -The Vespa Cloud Console has a log view tool, -and logs / access logs can be exported to the customer's AWS account easily. -Deployment operations are tracked in the deployment view, with a history. -Vespa Cloud Operators do not have node access, unless specifically granted by the customer, audit logged. - -### Once the service purchased with Vespa is terminated, is there a secure deletion procedure for the information collected from the customer? -At termination, all application instances are removed, with data, before the tenant can be deactivated. - -### Why is the CPU usage for my application above 100%? -In `dev` zones we use shared resources hence have more than one node on each host/instance. -In order to provide a best possible overall responsiveness we do not restrict CPU resources for the individual application nodes. diff --git a/en/learn/faq.mdx b/en/learn/faq.mdx new file mode 100644 index 0000000000..6a53f10e4f --- /dev/null +++ b/en/learn/faq.mdx @@ -0,0 +1,749 @@ +--- +title: "FAQ - frequently asked questions" +sidebarTitle: "Frequently asked questions" +description: "Refer to Vespa Support for more support options." +--- + +## Ranking + + + +[Ranking](/en/basics/ranking) is maybe the primary Vespa feature - we like to think of it as scalable, online computation. A rank profile is where the application's logic is implemented, supporting simple types like `double` and complex types like `tensor`. Supply ranking data in queries in query features (e.g. different weights per customer), or look up in a [Searcher](/en/applications/searchers). Typically, a document (e.g. product) "feature vector"/"weights" will be compared to a user-specific vector (tensor). + + + +Vespa doesn't have specific support for storing customer data as such. You can store this data as a separate document type in Vespa and look it up before passing the query, or store this customer meta-data as part of the other meta-data for the customer (i.e. login information) and pass it along the query when you send it to the backend. Find an example on how to look up data in [album-recommendation-docproc](https://github.com/vespa-engine/sample-apps/tree/master/examples/document-processing). + + + +Create a tensor in the ranking function from arrays or weighted sets using `tensorFrom...` functions - see [document features](/en/reference/ranking/rank-features#document-features). + + + +Pass a ranking feature like `query(threshold)` and use an `if` statement in the ranking expression - see [retrieval and ranking](/en/ranking/ranking-intro#retrieval-and-ranking). Example: + +```txt +rank-profile drop-low-score { + function my_score() { + expression: ..... #custom first phase score + } + rank-score-drop-limit:0.0 + first-phase { + if(my_score() < query(threshold), -1, my_score()) + } +} +``` + + + +Rank expressions are not evaluated lazily. No, this would require lambda arguments. Only doubles and tensors are passed between functions. + +Example: + +```txt +function inline foo(tensor, defaultVal) { + expression: if (count(tensor) == 0, defaultValue, sum(tensor)) +} + +function bar() { + expression: foo(tensor, sum(tensor1 * tensor2)) +} +``` + + + +Yes, this can be accomplished by configuring [match-phase](/en/reference/schemas/schemas#match-phase) in the rank profile, or by adding a range query item using *hitLimit* to the query tree, see [capped numeric range search](/en/reference/querying/yql#numeric). Both methods require an *attribute* field with *fast-search*. The capped range query is faster, but beware that if there are other restrictive filters in the query, one might end up with 0 hits. The additional filters are applied as a post filtering step over the hits from the capped range query. *match-phase* on the other hand, is safe to use with filters or other query terms, and also supports diversification which the capped range query term does not support. + + + +If a ranking profile produces NaNs or Infinities - which are impossible to represent as a number in JSON - the strings "Infinity" or "-Infinity", (NaN becomes "-Infinity") are returned in result sets, and client libraries might handle that by default (e.g., Golang). + +The returned [relevance](/en/reference/querying/default-result-format#relevance) for a hit can become "-Infinity" instead of a double: + +- The [ranking](/en/basics/ranking) expression used a feature which became `NaN` (Not a Number). For example, `log(0)` would produce `-Infinity`. Use [isNan](/en/reference/ranking/ranking-expressions#isnan-x) to guard against this. +- Surfacing low scoring hits using [grouping](/en/querying/grouping), that is, rendering low ranking hits with `each(output(summary()))` that are outside what Vespa computed and caches on a heap. This is controlled by the [total-keep-rank-count](/en/reference/schemas/schemas#total-keep-rank-count) parameter. +- Using unset fields in the ranking function + +Resolve this by one or more of: + +- Extend the client code to specifically handle these strings +- Make sure the field is set to some value for all documents +- Add a default value for the field when accessing it in your rank profile: `if (isNan(attribute(last_update)), 0, attribute(last_update))` +- Add a final guard in the ranking expressions coercing to some small number, making non-finite scores sink to the bottom while remaining a valid number: + +```txt +function finite_or_sentinel(x) { + expression: if (isNan(x - x), -1e9, x) +} +``` + +- Use CBOR instead of JSON - using a binary format can represent NaNs and Infinities without issues, and it can also be faster/more efficient + + + +To hard-code documents to positions in the result set, see the [pin results example](/en/ranking/multivalue-query-operators#pin-results-example). + + + +## Documents + + + +There is a [maximum document size](/en/reference/applications/services/container#document-api) of 100 MiB, which is configurable per content cluster in services.xml. + + + +No enforced limit, except resource usage (memory). + + + +E.g. a product is offered in a list of stores with a quantity per store. Use [multivalue fields](/en/querying/searching-multivalue-fields) (array of struct) or [parent child](/en/schemas/parent-child). Which one to chose depends on use case, see discussion in the latter link. + + + +E.g. price and quantity available per store may often change vs the actual product attributes. Vespa supports [partial updates](/en/writing/reads-and-writes) of documents. Also, the parent/child feature is implemented to support use-cases where child elements are updated frequently, while a more limited set of parent elements are updated less frequently. + + + +See the [Vespa Consistency Model](/en/content/consistency). Vespa is not transactional in the traditional sense, it doesn't have strict ACID guarantees. Vespa is designed for high performance use-cases with eventual consistency as an acceptable (and to some extent configurable) trade-off. + + + +Wildcard fields are not supported in vespa. Workaround would be to use maps to store the wildcard fields. Map needs to be defined with `indexing: attribute` and hence will be stored in memory. Refer to [map](/en/reference/schemas/schemas#map). + + + +Implement a [document processor](/en/applications/document-processors) for this. + + + +Set a selection criterion on the `document` element in `services.xml`. The criterion selects documents to keep. I.e. to purge documents "older than two weeks", the expression should be "newer than two weeks". Read more about [document expiry](/en/schemas/documents#document-expiry). + + + +Changing redundancy is a live and safe change (assuming there is headroom on disk / memory - e.g. from 2 to 3 is 50% more). The time to migrate will be quite similar to what it took to feed initially - a bit hard to say generally, and depends on IO and index settings, like if building an HNSW index. To monitor progress, take a look at the [multinode](https://github.com/vespa-engine/sample-apps/tree/master/examples/operations/multinode) sample application for the _clustercontroller_ status page - this shows buckets pending, live. Finally, use the `.idealstate.merge_bucket.pending` metric to track progress - when 0, there are no more data syncing operations - see [monitor distance to ideal state](/en/operations/self-managed/admin-procedures#monitor-distance-to-ideal-state). Nodes will work as normal during data sync, and query coverage will be the same. + + + +It does not, _namespace_ is a mechanism to split the document space into parts that can be used for document selection - see [documentation](/en/schemas/documents#namespace). The namespace is not indexed and cannot be searched using the query api, but can be used by [visiting](/en/writing/visiting). + + + +There are multiple things that can cause this, see [visiting troubleshooting](/en/writing/visiting#troubleshooting). + + + +Run a query like `vespa query "select * from sources * where true"` and see the `totalCount` field. Alternatively, use metrics or `vespa visit` - see [examples](/en/writing/batch-delete#example). + + + +Not in the field definition, but it's possible to do this with the [choice](/en/writing/indexing#choice-example) expression in an indexing statement. + + + +## Query + + + +Facets is called grouping in Vespa. Groups can be multi-level. + + + +Add filters to the query using [YQL](/en/querying/query-language) using boolean, numeric and [text matching](/en/querying/text-matching). Query terms can be annotated as filters, which means that they are not highlighted when bolding results. + + + +One way is to describe items using tensors and query for the [nearest neighbor](/en/reference/querying/yql#nearestneighbor) - using full precision or approximate (ANN) - the latter is used when the set is too large for an exact calculation. Apply filters to the query to limit the neighbor candidate set. Using [dot products](/en/ranking/multivalue-query-operators) or [weak and](/en/ranking/wand) are alternatives. + + + +Vespa does not have a stop-word concept inherently. See the [sample app](https://github.com/vespa-engine/sample-apps/pull/335/files) for how to use [filter terms](/en/reference/querying/yql#annotations). [Tripling the query performance of lexical search](https://blog.vespa.ai/tripling-the-query-performance-of-lexical-search/) it s good blog post on this subject. + + + +Trying to request more than 400 hits in a query, getting this error: `{'code': 3, 'summary': 'Illegal query', 'message': '401 hits requested, configured limit: 400.'}`. + +- To increase max result set size (i.e. allow a higher [hits](/en/reference/api/query#hits)), configure `maxHits` in a [query profile](/en/reference/api/query#queryprofile), e.g. `500` in `search/query-profiles/default.xml` (create as needed). The [query timeout](/en/reference/api/query#timeout) can be increased, but it will still be costly and likely impact other queries - large limit more so than a large offset. It can be made cheaper by using a smaller [document summary](/en/querying/document-summaries), and avoiding fields on disk if possible. +- Using _visit_ in the [document/v1/ API](/en/writing/document-v1-api-guide) is usually a better option for dumping all the data. + + + +See the [UserProfileSearcher](https://github.com/vespa-engine/sample-apps/blob/master/news/app-6-recommendation-with-searchers/src/main/java/ai/vespa/example/UserProfileSearcher.java) for how to create a new query to fetch data - this creates a new Query, sets a new root and parameters - then `fill`s the Hits. + + + +See the sub-query question above, in addition add something like: +```java expandable +public class ConfigCacheRefresher extends AbstractComponent { + + private final ScheduledExecutorService configFetchService = Executors.newSingleThreadScheduledExecutor(); + private Chain searcherChain; + + void initialize() { + Runnable task = () -> refreshCache(); + configFetchService.scheduleWithFixedDelay(task, 1, 1, TimeUnit.MINUTES); + searcherChain = executionFactory.searchChainRegistry().getChain(new ComponentId("configDefaultProvider")); + } + + public void refreshCache() { + Execution execution = executionFactory.newExecution(searcherChain); + Query query = createQuery(execution); + + public void deconstruct() { + super.deconstruct(); + try { + configFetchService.shutdown(); + configFetchService.awaitTermination(1, TimeUnit.MINUTES); + }catch(Exception e) {..} + } +} +``` + + + +Yes, using the [in query operator](/en/reference/querying/yql#in). Example: +```sql +select * from data where user_id in (10, 20, 30) +``` +The best article on the subject is [multi-lookup set filtering](/en/performance/feature-tuning#multi-lookup-set-filtering). Refer to the [in operator example](/en/ranking/multivalue-query-operators#in-example) on how to use it programmatically in a [Java Searcher](/en/applications/searchers). + + + +Use the [in query operator](/en/reference/querying/yql#in). Example: +```sql +select * from data where category in ('cat1', 'cat2', 'cat3') +``` +See [multi-lookup set filtering](#is-it-possible-to-query-vespa-using-a-list-of-document-ids) above for more details. + + + +Count all documents using a query like [select * from doc where true](/en/querying/query-language) - this counts all documents from the "doc" source. Using `select * from doc where true limit 0` will return the count and no hits, alternatively add [hits=0](/en/reference/api/query#hits). Pass [ranking.profile=unranked](/en/reference/api/query#ranking.profile) to make the query less expensive to run. If an _estimate_ is good enough, use [hitcountestimate=true](/en/reference/api/query#hitcountestimate). + + + +Yes - a deployment warning with _This may lead to recall and ranking issues_ is emitted when fields with conflicting tokenization are put in the same [fieldset](/en/reference/schemas/schemas#fieldset). This is because a given query item searching one fieldset is tokenized just once, so there's no right choice of tokenization in this case. If you have text that you want to apply to multiple fields with different tokenization, include the text multiple times in the query: +```sql +select * from sources * where fieldsetOrField1 contains text(@query) or fieldsetOrField2 contains text(@query) +``` +More details on [stack overflow](https://stackoverflow.com/questions/72784136/why-vepsa-easily-warning-me-this-may-lead-to-recall-and-ranking-issues). + + + +Symptoms — can appear when a term's DF differs substantially between member fields: +- Poor recall for queries mixing a common term with a rare one (e.g. `"the cure"`, `"the X"`). [weakAnd](/en/ranking/wand) may drop the common term, so good matches never surface. +- `term(n).significance` and `fieldMatch(field).significance` read identical across member fields in rank-feature dumps — even in fields where the term is actually rare. + +Cause: when a term matches a [fieldset](/en/reference/schemas/schemas#fieldset) (including the implicit `default` used by [userQuery()](/en/reference/querying/yql#userquery)), Vespa aggregates the document frequency across all member fields. + +If the DF differs substantially between members, the high-DF field dominates and pulls the term's significance down for the whole fieldset. + +**Example:** + +With `fieldset default { fields: title, artist }`, `"the"` is common in `title` (countless _"The Watcher"_, _"The Best Of the ..."_) but rare in `artist`. + +Its aggregated significance is pulled down toward the `title` DF, so searching for the artist `"The Cure"` loses the signal from `"the"`. + +The same aggregated DF drives every DF/IDF feature: [bm25](/en/ranking/bm25), [nativeRank](/en/ranking/nativerank), `term(n).significance`, `fieldMatch.significance`. + +Matches that survive retrieval are scored using the aggregated DF rather than per-field statistics. + +Fix: rewrite as OR'd [userInput](/en/reference/querying/yql#userinput) clauses with a [defaultIndex](/en/reference/querying/yql#defaultindex) annotation per field. Each field then uses its own DF: + +*Combined-fieldset DF:* + +```php +vespa query 'select * from sources * where userQuery()' \ + query='the cure' +``` + +*Per-field DF:* + +```bash +vespa query 'select * from sources * where ({defaultIndex:"title"}userInput(@q)) or ({defaultIndex:"artist"}userInput(@q))' \ + q='the cure' +``` + + +**Important** + +BM25 and significance feature values shift scale when switching to per-field DF. Retrain any learned ranker on features collected with the new query formulation. + + + + +Find query timeout details in the [Query API Guide](/en/querying/query-api#timeout) and the [Query API Reference](/en/reference/api/query#timeout). + + + +Backslash is used to escape special characters in YQL. For example, to query with a literal backslash, which is useful in regexpes, you need to escape it with another backslash: \\. Unescaped backslashes in YQL will lead to "token recognition error at: '\'". + +In addition, Vespa CLI unescapes double backslashes to single (while single backslashes are left alone), so if you query with Vespa CLI you need to escape with another backslash: \\\\. The same applies to strings in Java. + +Also note that both log messages and JSON results escape backslashes, so any \ becomes \\. + + + +E.g. two select queries with slightly different filtering condition and have a limit operator for each of the subquery. This makes it impossible to do via OR conditions to select both collection of documents - something equivalent to: + +SELECT 1 AS x UNION ALL SELECT 2 AS y; + +This isn’t possible, need to run 2 queries. Alternatively, split a single incoming query into two running in parallel in a [Searcher](/en/applications/searchers) - example: + +```java +FutureResult futureResult = new AsyncExecution(settings).search(query); +FutureResult otherFutureResult = new AsyncExecution(settings).search(otherQuery); +``` + + + +There is no index or attribute data structure that allows efficient _searching_ for documents where an array field has a certain number of elements or items. The _grouping language_ has a [size()](/en/reference/querying/grouping-language#list-expressions) operator that can be used in queries. + + + +The [visiting](/en/writing/visiting#analyzing-field-values) API using document selections supports it, with a linear scan over all documents. If the field is an _attribute_ one can query using grouping to identify Nan Values, see count and list [fields with NaN](/en/querying/grouping#count-fields-with-nan). + + + +See the [random.match](/en/reference/ranking/rank-features#random.match) rank feature - example: + +```txt +rank-profile random { + first-phase { + expression: random.match + } +} +``` + +Run queries, seeding the random generator: + +```bash +$ vespa query 'select * from music where true' \ + ranking=random \ + rankproperty.random.match.seed=2 +``` + + + +See [result diversity](/en/querying/result-diversity) for strategies on how to create result sets from different sources. + + + +If you want to search for the most dissimilar items, you can with angular distance multiply your `clip_query_embedding` by the scalar -1. Then you are searching for the points that are closest to the point which is the farthest away from your `clip_query_embedding`. + +Also see a [pyvespa example](https://vespa-engine.github.io/pyvespa/examples/pyvespa-examples#Neighbors). + + + +## Feeding + + + +The best option is to use `--verbose` option, like `vespa feed --verbose myfile.jsonl` - see [documentation](/en/clients/vespa-cli#documents). A common problem is a mismatch in schema names and [document IDs](/en/schemas/documents#document-ids) - a schema like: +```yaml +schema article { + document article { + ... + } +} +``` + +will have a document feed like: + +```json +{"put": "id:mynamespace:article::1234", "fields": { ... }} +``` + +Note that the [namespace](/en/learn/glossary#namespace) is not mentioned in the schema, and the schema name is the same as the document name. + + + +This configuration is a combination of content and container cluster configuration, see [indexing](/en/writing/indexing) and [feed troubleshooting](/en/operations/self-managed/admin-procedures#troubleshooting). + + + +This is often a problem if using [document expiry](/en/schemas/documents#document-expiry), as documents already expired will not be persisted, they are silently dropped and ignored. Feeding stale test data with old timestamps in combination with document-expiry can cause this behavior. + + + +Using too many HTTP clients can generate a 429 response code. The Vespa sample apps use [vespa feed](/en/clients/vespa-cli#documents) which uses HTTP/2 for high throughput - it is better to stream the feed files through this client. + + + +Vespa does not have a Kafka connector. Refer to third-party connectors like [kafka-connect-vespa](https://github.com/vinted/kafka-connect-vespa). + + + +## Text Search + + + +E.g. integrating NER, word sense disambiguation, specific intent detection. Vespa supports these things well: +- [Query (and result) processing](/en/applications/searchers) +- [Document processing](/en/applications/document-processors) and document processors working on semantic annotations of text + + + +E.g. instead of using terms or n-grams as the unit, we might use terms with specific word senses - e.g. bark (dog bark) vs. bark (tree bark), or BCG (company) vs. BCG (vaccine name). Creating a new index *format* means changing the core. However, for the examples above, one just need control over the tokens which are indexed (and queried). That is easily done in some Java code. The simplest way to do this is to plug in a [custom tokenizer](/en/linguistics/linguistics). That gets called from the query parser and bundled linguistics processing [Searchers](/en/applications/searchers) as well as the [Document Processor](/en/applications/document-processors) creating the annotations that are consumed by the indexing operation. Since all that is Searchers and Docprocs which you can replace and/or add custom components before and after, you can also take full control over these things without modifying the platform itself. + + + +It provides the building blocks but not an out-of-the-box solution. We can write a [Searcher](/en/applications/searchers) to detect query-side entities and rewrite the query, and a [DocProc](/en/applications/document-processors) if we want to handle them in some special way on the indexing side. + + + +You can write a document processor for text extraction, Vespa does not provide it out of the box. + + + +[Imported fields](/en/schemas/parent-child) from parent documents are defined as [attributes](/en/content/attributes), and have limited text match modes (i.e. `indexing: index` cannot be used). [Details](https://stackoverflow.com/questions/71936330/parent-child-mode-cannot-be-searched-by-parent-column). + + + +## Semantic search + + + +If you have added vectors to your documents and queries, and see that the rank feature closeness(field, yourEmbeddingField) produces 1.0 for all documents, you are likely using [distance-metric](/en/reference/schemas/schemas#distance-metric): innerproduct/prenormalized-angular, but your vectors are not normalized, and the solution is normally to switch to [distance-metric: angular](/en/reference/schemas/schemas#angular) or use [distance-metric: dotproduct](/en/reference/schemas/schemas#dotproduct) (available from Vespa 8.170.18). + +With non-normalized vectors, you often get negative distances, and those are capped to 0, leading to closeness 1.0. Some embedding models, such as models from sbert.net, claim to output normalized vectors but might not. + + + +## Programming Vespa + + + +Plugins have to run in the JVM - [jython](https://www.jython.org/) might be an alternative, however Vespa Team has no experience with it. Vespa does not have a language like [painless](https://www.elastic.co/guide/en/elasticsearch/reference/current/modules-scripting-painless) - it is more flexible to write application logic in a JVM-supported language, using [Searchers](/en/applications/searchers) and [Document Processors](/en/applications/document-processors). + + + +A [Searcher](/en/applications/searchers) intercepts a query and/or result. To get a number of documents by id in a Searcher or other component like a [Document processor](/en/applications/document-processors), you can have an instance of [com.yahoo.documentapi.DocumentAccess](/en/reference/applications/components#injectable-components) injected and use that to get documents by id instead of the HTTP API. + + + +Vespa uses Java 17 - it will support 20 some time in the future. + + + +Use `System.out.println` to write text to the [vespa.log](/en/reference/operations/log-files). + + + +## Performance + + + +Vespa has a near real-time indexing core with typically sub-second latencies from document ingestion to being indexed. This depends on the use-case, available resources and how the system is tuned. Some more examples and thoughts can be found in the [scaling guide](/en/performance/sizing-search). + + + +Vespa does not have a concept of "batch ingestion" as it contradicts many of the core features that are the strengths of Vespa, including [serving elasticity](/en/content/elasticity) and sub-second indexing latency. That said, we have numerous use-cases in production that do high throughput updates to large parts of the (sometimes entire) document set. In cases where feed throughput is more important than indexing latency, you can tune this to meet your requirements. Some of this is detailed in the [feed sizing guide](/en/performance/sizing-feeding). + + + +Yes. The [content node](/en/content/proton) is implemented in C++ and not memory constrained other than what the operating system does. + + + +If the replicas are in sync the request is only sent to the primary content node. Otherwise, it's sent to several nodes, depending on replica metadata. Example: if a bucket has 3 replicas A, B, C and A & B both have metadata state X and C has metadata state Y, a request will be sent to A and C (but not B since it has the same state as A and would therefore not return a potentially different document). + + + +[Attribute](/en/content/attributes) (with or without `fast-search`) is always in memory, but does not support tokenized matching. It is for structured data. [Index](/en/basics/schemas#document-fields) (where there’s no such thing as fast-search since it is always fast) is in memory to the extent there is available memory and supports tokenized matching. It is for unstructured text. + +It is possible to guarantee that fields that are defined with `index` have both the dictionary and the postings in memory by changing from `mmap` to `populate`, see [index > io > search](/en/reference/applications/services/content#index-io-search). Make sure that the content nodes run on nodes with plenty of memory available, during index switch the memory footprint will 2x. Familiarity with Linux tools like `pmap` can help diagnose what is mapped and if it’s resident or not. + +Fields that are defined with `attribute` are in-memory, fields that have both `index` and `attribute` have separate data structures, queries will use the default mapped on disk data structures that supports `text` matching, while grouping, summary and ranking can access the field from the `attribute` store. + +A Vespa query is executed in two phases as described in [sizing search](/en/performance/sizing-search), and summary requests can touch disk (and also uses `mmap` by default). Due to their potential size there is no populate option here, but one can define [dedicated document summary](/en/querying/document-summaries#performance) containing only fields that are defined with `attribute`. + +The [practical performance guide](/en/performance/practical-search-performance-guide) can be a good starting point as well to understand Vespa query execution, difference between `index` and `attribute` and summary fetching performance. + + + +Deleting documents, by using the [document API](/en/writing/reads-and-writes) or [garbage collection](/en/schemas/documents#document-expiry) will increase the capacity on the content nodes. However, this is not necessarily observable in system metrics - this depends on many factors, like what kind of memory that is released, when [flush](/en/content/proton#proton-maintenance-jobs) jobs are run and document [schema](/en/basics/schemas). + +In short, Vespa is not designed to release memory once used. It is designed for sustained high throughput, low latency, keeping maximum memory used under control using features like [feed block](/en/writing/feed-block). + +When deleting documents, one can observe a slight increase in memory. A deleted document is represented using a [tombstone](/en/operations/self-managed/admin-procedures#content-cluster-configuration), that will later be removed, see [removed-db-prune-age](/en/reference/applications/services/content#removed-db-prune-age). When running garbage collection, the summary store is scanned using mmap and both VIRT and page cache memory usage increases. + +Read up on [attributes](/en/content/attributes) to understand more of how such fields are stored and managed. [Paged attributes](/en/content/attributes#paged-attributes) trades off memory usage vs. query latency for a lower max memory usage. + + + +A field is of type _index_ or _attribute_ - [details](/en/querying/text-matching#index-and-attribute). + +Fields with _index_ use no incremental memory at deployment, if the field has no value. + +Fields with _attribute_ use memory, even if the field value is not set, + +Attributes are optimized for random access: To be able to jump to the value of any document in O(1) time. That requires allocating a constant amount of memory (the value, or a pointer) per document, regardless of whether there is a value. In short, knowing that a value is unset is a value in itself for attributes, so deploying new fields or new schemas with attributes will cause an incremental increase in memory. Applications with many unused schemas and fields can factor this in when sizing for memory. Refer to [attributes](/en/content/attributes#attribute-memory-usage) for details. + + + +[Autoscaling](/en/operations/autoscaling) is the best guide to understand how to size and autoscale the system. Container clusters are stateless and can be autoscaled more quickly than content clusters. + + + +It is not possible to autoscale content clusters for 8x load increase in 5 minutes, as this requires both provisioning and data migration. Such use cases are best discussed with the Vespa Team to understand the resource bottlenecks, tradeoffs and mitigations. Also read [Graceful Degradation](/en/performance/graceful-degradation). + + + +It depends. Vespa aims to adapt to resources (like auto thread config based on virtual node thread count) and actual use (when to run maintenance jobs like compaction), but there are tradeoffs that applications owners can/should make. Start off by reading the [Vespa Serving Scaling Guide](/en/performance/sizing-search), then run [benchmarks](/en/performance/benchmarking-cloud) and use the [dashboards](/en/operations/monitoring). + + + +## Administration + + + +Yes, deployment is using this web service API, which allows you to create an edit session from the currently deployed package, make modifications, and deploy (prepare+activate) it: [deploy-rest-api-v2](/en/reference/api/deploy-v2). However, this is only useful in cases where you want to avoid transferring data to the config server unnecessarily. When you resend everything, the config server will notice that you did not actually change e.g. the node configs and avoid unnecessary noop changes. + + + +[Elasticity](/en/content/elasticity) is a core Vespa strength - easily add and remove nodes with minimal (if any) serving impact. The exact time needed depends on how much data will need to be migrated in the background for the system to converge to [ideal data distribution](/en/content/idealstate). + + + +You will need to load balance incoming requests between the nodes running the [stateless Java container cluster(s)](/en/learn/overview). This can typically be done using a simple network load balancer available in most cloud services. This is included when using [Vespa Cloud](/), with an HTTPS endpoint that is already load balanced - both locally within the region and globally across regions. + + + +[Search sizing](/en/performance/sizing-search) is the intro to this. Topology matters, and this is much used in the high-volume Vespa applications to optimise latency vs. cost. + + + +With [Vespa Cloud](/), we do automated background upgrades daily without noticeable serving impact. If you host Vespa yourself, you can do this, but need to implement the orchestration logic necessary to handle this. The high level procedure is found in [live-upgrade](/en/operations/self-managed/live-upgrade). + + + +[Vespa Cloud](/en/operations/zones) has integrated support - query a global endpoint. Writes will have to go to each zone. There is no auto-sync between zones. + + + +Building indexes offline requires the partition layout to be known in the offline system, which is in conflict with elasticity and auto-recovery (where nodes can come and go without service impact). It is also at odds with realtime writes. For these reasons, it is not recommended, and not supported. + + + +Use [visiting](/en/writing/visiting) to dump all or a subset of the documents. See [data-management-and-backup](/en/operations/data-management) for more information. + + + +Failure response will be given in case the document is not written on some replica nodes. + + + +Yes, it will be available, eventually. Also try [Multinode testing and observability](https://github.com/vespa-engine/sample-apps/tree/master/examples/operations/multinode). + + + +Yes just add a `deleted` attribute, add [fast-search](/en/content/attributes#fast-search) on it and create a searcher which adds an `andnot deleted` item to queries. + + + +You can set a [transition-time](/en/reference/applications/services/content#transition-time) in services.xml to configure the cluster controller how long a node is to be kept in maintenance mode before being automatically marked down. + + + +Grouped distribution is used to reduce search latency. Content is distributed to a configured set of groups, such that the entire document collection is contained in each group. Setting the redundancy and searchable-copies equal to the number of groups ensures that data can be queried from all groups. + + + +Refer to [#17898](https://github.com/vespa-engine/vespa/issues/17898) for a discussion of options. + + + +Use [/state/v1/version](/en/reference/api/state-v1#state-v1-version) to find Vespa version. + + + +See [rollback](/en/applications/deployment#rollback) for options. + + + +## Troubleshooting + + + +If deployment fails with error message "Deployment failed, code: 413 ("Payload Too Large.")" you might need to increase the config server's JVM heap size. The config server has a default JVM heap size of 2 Gb. When deploying an app with e.g. large models this might not be enough, try increasing the heap to e.g. 4 Gb when executing 'docker run ...' by adding an environment variable to the command line: + +```bash +docker run --env VESPA_CONFIGSERVER_JVMARGS=-Xmx4g +``` + + + +When deploying an application package, with some kind of error, the endpoints might fail, like: +```bash +$ vespa deploy --wait 300 + +Uploading application package ... done + +Success: Deployed target/application.zip + +Waiting up to 5m0s for query service to become available ... +Error: service 'query' is unavailable: services have not converged +``` +Another example: + +```text +[INFO] [03:33:48] Failed to get 100 consecutive OKs from endpoint ... +``` + +There are many ways this can fail, the first step is to check the Vespa Container: + +```bash +$ docker exec vespa vespa-logfmt -l error + +[2022-10-21 10:55:09.744] ERROR container +Container.com.yahoo.container.jdisc.ConfiguredApplication +Reconfiguration failed, your application package must be fixed, unless this is a JNI reload issue: +Could not create a component with id 'ai.vespa.example.album.MetalSearcher'. +Tried to load class directly, since no bundle was found for spec: album-recommendation-java. +If a bundle with the same name is installed, +there is a either a version mismatch or the installed bundle's version contains a qualifier string. +... +``` + +[Bundle plugin troubleshooting](/en/applications/bundles#bundle-plugin-troubleshooting) is a good resource to analyze Vespa container startup / bundle load problems. + + + +Using an M1 MacBook Pro / AArch64 makes the Docker run fail: + +```txt +WARNING: The requested image’s platform (linux/amd64) does not match the detected host platform (linux/arm64/v8) +and no specific platform was requested +``` + +Make sure you are running a recent version of the Docker image, do `docker pull vespaengine/vespa`. + + + +Make sure all [Config servers](/en/operations/self-managed/configuration-server#troubleshooting) are started, and are able to establish ZooKeeper quorum (if more than one) - see the [multinode](https://github.com/vespa-engine/sample-apps/tree/master/examples/operations/multinode) sample application. Validate that the container has [enough memory](/en/operations/self-managed/docker-containers). + + + +The Config Server cluster with 3 nodes fails to start. The ZooKeeper cluster the Config Servers use waits for hosts on the network, the hosts wait for ZooKeeper in a catch 22 - see [sampleapp troubleshooting](https://github.com/vespa-engine/sample-apps/tree/master/examples/operations#troubleshooting). + + + +Use [vespa-logfmt](/en/reference/operations/self-managed/tools#vespa-logfmt) to dump logs. If Vespa is running in a local container (named "vespa"), run `docker exec vespa vespa-logfmt`. + + + +See [encoding troubleshooting](/en/linguistics/troubleshooting-encoding) for how to handle and remove control characters from the document feed. + + + +## Login, Tenants and Plans + + + +[Deploy an application](/en/basics/deploy-an-application) to create a tenant and start your [free trial](https://vespa.ai/free-trial/). This tenant can be your personal tenant, or shared with others. It can not be renamed. + + + +If the tenant is already created, add more users to it. In the Vespa Cloud Console, open [**Account > Users**](https://console.vespa-cloud.com/link/tenant/account/users). From this view you can manage users in the tenant, and their roles - from here, you can add/set tenant admins. + + + +When starting the free trial, you are asked to accept Terms of Service. For paid plans, this is covered by the contract. + + + +In the console, open [**Account > Billing**](https://console.vespa-cloud.com/link/tenant/account/billing) to enter information required for billing. Use [Vespa Support](https://vespa.ai/support/) if you need to provide this information without console login. + + + +Yes, contact [Vespa Support](https://vespa.ai/support/) to set it up. + + + +## Vespa Cloud Operations + + + +See [node resources](/en/performance/node-resources) to assess current and auto-suggested resources and [autoscaling](/en/operations/autoscaling) for how to automate. + + + +Managing resources is easy, as most changes are automated. Adding / removing / changing nodes starts automated data migration, see [elasticity](/en/content/elasticity). + + + +Schema changes might require data reindexing, which is automated, but takes some time. Other schema changes require data refeed - [details](/en/reference/schemas/schemas#modifying-schemas) + + + +Use the [Memory Visualizer](/en/performance/memory-visualizer) to evaluate how memory is allocated to the fields. Fields can be `index`, `attribute` and `summary`, and combinations of these, with settings like `fast-search` that affects memory usage. [Attributes](/en/content/attributes) is a great read for understanding Vespa memory usage. + + + +Listing archived objects can fail, e.g. `gsutil -u my_project ls gs://vespa-cloud-data-prod-gcp-us-central1-f-12345f/my_tenant` can fail with `AccessDeniedException: 403 me@mymail.com does not have serviceusage.services.use access to the Google Cloud project. Permission \'serviceusage.services.use\' denied on resource (or it may not exist).` This can be due to missing rights on your Google project (my_project in the example above) \- from the Google documentation: _"The user account accessing the Cloud Storage Bucket must be granted the Service Usage Consumer role (see [https://cloud.google.com/service-usage/docs/access-control](https://cloud.google.com/service-usage/docs/access-control)) in order to charge the specified user project for the bucket usage cost"_ + + + +Vespa Cloud applications have a Prometheus endpoint. Find guides for how to integrate with Grafana and AWS Cloudwatch at [monitoring](/en/operations/monitoring). + + + +Vespa Cloud has detailed dashboards linked from the _monitoring_ tab in the Console, one for each zone the instance is deployed to. + + + +Vespa is normally upgraded daily. There are exceptions, like holidays and weekends. During upgrades, nodes are stopped one-by-one per cluster. As all clusters have one redundant node, serving and write traffic is not impacted by upgrades. Before the upgrade, the application's [system and staging tests](/en/operations/automated-deployments) are run, halting the upgrade if they fail. Documents are re-migrated to the upgraded node before doing the next node, see [Elastic Vespa](/en/content/elasticity) for details. + + + +Issues like Feed Blocked, Deployment and Deprecation warnings show up in the console. There are no warnings on redundancy level / searchable copies, as redundant document buckets are activated for queries automatically, and auto data-migration kicks in for node failures / replacements. + + + +- Schema changes that [require service restart](/en/reference/schemas/schemas#changes-that-require-restart-but-not-re-feed) are handled automatically by Vespa Cloud. A deployment job involves waiting for these to complete. +- Schema changes that [require reindexing](/en/reference/schemas/schemas#changes-that-require-reindexing) of data require a validation override, and will trigger automatic reindexing. Status can be tracked in the console application view. Vespa Cloud also periodically re-indexes all data, with minimal resource usage, to account for changes in linguistics libraries. +- Schema changes that [require refeeding](/en/reference/schemas/schemas#changes-that-require-re-feed) data require a validation override, and the user must refeed the data after deployment. + + + +The management of data stored in an application running on Vespa Cloud is the responsibility of the application owner and, as such, Vespa Cloud does not have any retention policy for this data as long as it is stored by the application. + +The following data retention policies applies to Vespa Cloud: +- After a node previously allocated to an application has been deallocated (e.g. due to application being deleted by application owner), all application data will be deleted within _four hours_. +- All application log data will be deleted from Vespa servers after no more than _30 days_ (most often sooner) dependent on log volume, allocated disk resources, etc. *PLEASE NOTE:* This is the theoretical maximum retention time - see [archive guide](/en/operations/archive/archive-guide) for how to ensure access to your application logs. + + + +Yes, Vespa.ai has a SOC 2 attestation: [Trust Center](https://trust.vespa.ai). + + + +Read more in [GDPR](https://cloud.vespa.ai/en/gdpr?_gl=1*1uexiwi*_gcl_au*ODE0ODM4MTI2LjE3Nzk3MjQ3OTY.). + + + +Vespa is most often used for queries in data written from the information sources, although it can also be used without data, e.g. for model serving. It is the application owner that writes the integration with Vespa Cloud to write data. + + + +Vespa Cloud uses the following Cloud providers: +- AWS EC2 instances, with local or remote storage +- GCP Compute instances, with local or remote storage +- Azure Compute instances, with local or remote storage + +The storage devices are encrypted per Cloud provider, at rest. + + + +See the [security guide](/en/security/guide) for roles and permissions. The Vespa Cloud Console has a log view tool, and logs / access logs can be exported to the customer's AWS account easily. Deployment operations are tracked in the deployment view, with a history. Vespa Cloud Operators do not have node access, unless specifically granted by the customer, audit logged. + + + +At termination, all application instances are removed, with data, before the tenant can be deactivated. + + + +In `dev` zones we use shared resources hence have more than one node on each host/instance. In order to provide a best possible overall responsiveness we do not restrict CPU resources for the individual application nodes. + + + diff --git a/en/learn/features.html b/en/learn/features.html deleted file mode 100644 index e841396976..0000000000 --- a/en/learn/features.html +++ /dev/null @@ -1,135 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Features" -redirect_from: -- /en/features.html ---- - - -

    What is Vespa?

    - -

    Vespa is a platform for applications which need low-latency computation over large data sets. -It allows you to write and persist any amount of data, and execute high volumes of queries over -the data which typically complete in tens of milliseconds.

    - -

    Queries can use both structured filters conditions, text and nearest neighbor vector search to select data. -All the matching data is then ranked according to ranking functions - typically machine learned - -to implement such use cases as search relevance, recommendation, targeting and personalization.

    - -

    All the matching data can also be grouped into groups and subgroups where data is aggregated -for each group to implement features like graphs, tag clouds, navigational tools, result -diversity and so on.

    - -

    Application specific behavior can be included by adding Java components for processing -queries, results and writes to the application package.

    - -

    Vespa is real time. It is architected to maintain constant response times with any data volume by -executing queries in parallel over many data shards and cores, and with added query volume -by executing queries in parallel over many copies of the same data (groups). It is optimized -to return responses in tens of milliseconds. Writes to data becomes visible in a few milliseconds -and can be handled at a rate of thousands to tens of thousands per node per second.

    - -

    A lot of work has gone into making Vespa easy to set up and operate. -Any Vespa application - from single node systems to systems running on hundreds of nodes in data centers - -are fully configured by a single artifact called an application package. Low level configuration of -nodes, processes and components is done by the system itself based on the desired traits specified in the -application package.

    - -

    Vespa is scalable. System sizes up to hundreds of nodes handling tens of billions of documents, -and tens of thousands of queries per second -are not uncommon, and no harder to set up and modify than single node systems. Since -all system components, as well as stored data is redundant and self-correcting, hardware -failures are not operational emergencies and can be handled by re-adding capacity when convenient.

    - -

    Vespa is self-repairing and dynamic. When machines are lost or new ones added, data is automatically -redistributed over the machines, while continuing serving and accepting writes to the data. -Changes to configuration and Java components can be made while serving by deploying a changed -application package - no downtime or restarts required.

    - - - -

    Features

    - -This section provides an overview of the main features of Vespa. -The remainder of the documentation goes into full detail. - -

    Data and writes

    - -
      -
    • Documents in Vespa may be added, replaced, modified (single fields or any subset) and removed. -
    • Writes are acknowledged back to the client issuing them when they are durable and visible in queries, - in a few milliseconds. -
    • Writes can be issued at a sustained volume of thousands to tens of thousands per node per second while serving queries. -
    • Data is replicated with a configurable redundancy. -
    • An even data distribution, with the desired redundancy is automatically maintained when nodes are added, - removed or lost unexpectedly. -
    • Data corruption is automatically repaired from an uncorrupted replica of the data. -
    • Data is written over a simple HTTP/2 API, or (for high volume) using a small, standalone client. -
    • Document data schemas allow fields of any of the usual primitive types as well as collections, structs and tensors. -
    • Any number of data schemas can be used at the same time. -
    • Documents may reference each other and field from referenced documents may be used in queries without performance penalty. -
    • Write operations can be processed by adding custom Java components. -
    • Data can be streamed out of the system for batch reprocessing. -
    - -

    Queries

    - -
      -
    • Queries may contain any combination of structured filters, free text and vector search operators. -
    • Queries may contain large tensors and vectors (to represent e.g a user). -
    • Queries choose how results should be ranked and specify how they should be organized (see sections below). -
    • Queries and results may be processed by adding custom Java components - or any HTTP request may be - turned into a query by custom request handlers. -
    • Query response times are typically in tens of milliseconds and can be maintained given any load - and data size by adding more hardware. -
    • A streaming search mode is available where search/selection is only supported on predefined - groups of documents (e.g a user's document). In this mode each node can store and serve billions of - documents while maintaining low response times. -
    - -

    Ranking and inference

    - -
      -
    • All results are ranked using a configured ranking function, selected in the query. -
    • A ranking function may be any mathematical function over scalars or tensors (multidimensional arrays). -
    • Scalar functions include an "if" function to express business logic and decision trees. -
    • Tensor functions include a powerful set of primitives and composite functions which - allows expression of advanced machine-learned ranking functions such as e.g. deep neural nets. -
    • Functions can also refer to ONNX models invoked locally on the content nodes.
    • -
    • Multiple ranking phases are supported to allocate more CPU to ranking promising candidates. -
    • A powerful set of text ranking features using positional information from the documents is provided out of the box. -
    • Other ranking features include 2D distance and freshness. -
    - -

    Organizing data and presenting results

    - -
      -
    • Matches to a query can be grouped and aggregated according to a specification in the query. -
    • All the matches are included, even though they reside on multiple machines executing in parallel. -
    • Matches can be grouped by a unique value or by a numerical bucket. -
    • Any level of groups and subgroups are supported, and multiple parallel groupings can be specified in one query. -
    • Data can be aggregated (counted, averaged etc.) and selected within each group and subgroup. -
    • Any selection of data from documents can be included with the final result returned to the client. -
    • Search engine style keyword highlighting in matching fields is supported. -
    - -

    Configuration and operations

    - -
      -
    • Vespa can be installed using rpm files or a Docker image - on personal laptops, owned datacenters or in AWS. -
    • An application of Vespa is fully specified as a separate buildable artifact: An application package - - individual machines or processes need never be configured individually. -
    • Systems may contain multiple clusters of each type (stateless and stateful), each containing any number of nodes. -
    • Systems of any size may be specified by two short configuration files in the application package. -
    • Document schemas, Java components and ranking functions/models are also configured in the application package. -
    • An application package is deployed as a single unit to Vespa to realizes the system desired by the application. -
    • Most application changes (including Java component changes) can be performed by deploying - a changed application package - the system will manage its own change process while serving and handling writes. -
    • Most document schema changes (excluding field type changes) can be made while the system is live. -
    • Application package changes are validated on deployment to prevent destructive changes to live systems. -
    • Vespa has no single-point-of-failures and automatically routes around failing nodes. -
    • System logs are collected to a central server in real time. -
    • Selected metrics may be emitted to a third-party metrics/alerting system from all the nodes. -
    - - diff --git a/en/learn/features.mdx b/en/learn/features.mdx new file mode 100644 index 0000000000..11bd68f111 --- /dev/null +++ b/en/learn/features.mdx @@ -0,0 +1,85 @@ +--- +title: Features +--- + +## What is Vespa? + +Vespa is a platform for applications which need low-latency computation over large data sets. It allows you to write and persist any amount of data, and execute high volumes of queries over the data which typically complete in tens of milliseconds. + +Queries can use both structured filters conditions, text and nearest neighbor vector search to select data. All the matching data is then ranked according to ranking functions - typically machine learned - to implement such use cases as search relevance, recommendation, targeting and personalization. + +All the matching data can also be grouped into groups and subgroups where data is aggregated for each group to implement features like graphs, tag clouds, navigational tools, result diversity and so on. + +Application specific behavior can be included by adding Java components for processing queries, results and writes to the application package. + +Vespa is real time. It is architected to maintain constant response times with any data volume by executing queries in parallel over many data shards and cores, and with added query volume by executing queries in parallel over many copies of the same data (groups). It is optimized to return responses in tens of milliseconds. Writes to data becomes visible in a few milliseconds and can be handled at a rate of thousands to tens of thousands per node per second. + +A lot of work has gone into making Vespa easy to set up and operate. Any Vespa application - from single node systems to systems running on hundreds of nodes in data centers - are fully configured by a single artifact called an *application package*. Low level configuration of nodes, processes and components is done by the system itself based on the desired traits specified in the application package. + +Vespa is scalable. System sizes up to hundreds of nodes handling tens of billions of documents, and tens of thousands of queries per second are not uncommon, and no harder to set up and modify than single node systems. Since all system components, as well as stored data is redundant and self-correcting, hardware failures are not operational emergencies and can be handled by re-adding capacity when convenient. + +Vespa is self-repairing and dynamic. When machines are lost or new ones added, data is automatically redistributed over the machines, while continuing serving and accepting writes to the data. Changes to configuration and Java components can be made while serving by deploying a changed application package - no downtime or restarts required. + +## Features + +This section provides an overview of the main features of Vespa. The remainder of the documentation goes into full detail. + +### Data and writes + +- Documents in Vespa may be added, replaced, modified (single fields or any subset) and removed. +- Writes are acknowledged back to the client issuing them when they are durable and visible in queries, in a few milliseconds. +- Writes can be issued at a sustained volume of thousands to tens of thousands per node per second while serving queries. +- Data is replicated with a configurable redundancy. +- An even data distribution, with the desired redundancy is automatically maintained when nodes are added, removed or lost unexpectedly. +- Data corruption is automatically repaired from an uncorrupted replica of the data. +- Data is written over a simple HTTP/2 API, or (for high volume) using a small, standalone client. +- Document data schemas allow fields of any of the usual primitive types as well as collections, structs and tensors. +- Any number of data schemas can be used at the same time. +- Documents may reference each other and field from referenced documents may be used in queries without performance penalty. +- Write operations can be processed by adding custom Java components. +- Data can be streamed out of the system for batch reprocessing. + +### Queries + +- Queries may contain any combination of structured filters, free text and vector search operators. +- Queries may contain large tensors and vectors (to represent e.g a user). +- Queries choose how results should be ranked and specify how they should be organized (see sections below). +- Queries and results may be processed by adding custom Java components - or any HTTP request may be turned into a query by custom request handlers. +- Query response times are typically in tens of milliseconds and can be maintained given any load and data size by adding more hardware. +- A *streaming search* mode is available where search/selection is only supported on predefined groups of documents (e.g a user's document). In this mode each node can store and serve billions of documents while maintaining low response times. + +### Ranking and inference + +- All results are ranked using a configured ranking function, selected in the query. +- A ranking function may be any mathematical function over scalars or tensors (multidimensional arrays). +- Scalar functions include an "if" function to express business logic and decision trees. +- Tensor functions include a powerful set of primitives and composite functions which allows expression of advanced machine-learned ranking functions such as e.g. deep neural nets. +- Functions can also refer to ONNX models invoked locally on the content nodes. +- Multiple ranking phases are supported to allocate more CPU to ranking promising candidates. +- A powerful set of text ranking features using positional information from the documents is provided out of the box. +- Other ranking features include 2D distance and freshness. + +### Organizing data and presenting results + +- Matches to a query can be grouped and aggregated according to a specification in the query. +- All the matches are included, even though they reside on multiple machines executing in parallel. +- Matches can be grouped by a unique value or by a numerical bucket. +- Any level of groups and subgroups are supported, and multiple parallel groupings can be specified in one query. +- Data can be aggregated (counted, averaged etc.) and selected within each group and subgroup. +- Any selection of data from documents can be included with the final result returned to the client. +- Search engine style keyword highlighting in matching fields is supported. + +## Configuration and operations + +- Vespa can be installed using rpm files or a Docker image - on personal laptops, owned datacenters or in AWS. +- An application of Vespa is fully specified as a separate buildable artifact: An *application package* - individual machines or processes need never be configured individually. +- Systems may contain multiple clusters of each type (stateless and stateful), each containing any number of nodes. +- Systems of any size may be specified by two short configuration files in the application package. +- Document schemas, Java components and ranking functions/models are also configured in the application package. +- An application package is deployed as a single unit to Vespa to realizes the system desired by the application. +- Most application changes (including Java component changes) can be performed by deploying a changed application package - the system will manage its own change process while serving and handling writes. +- Most document schema changes (excluding field type changes) can be made while the system is live. +- Application package changes are validated on deployment to prevent destructive changes to live systems. +- Vespa has no single-point-of-failures and automatically routes around failing nodes. +- System logs are collected to a central server in real time. +- Selected metrics may be emitted to a third-party metrics/alerting system from all the nodes. diff --git a/en/learn/glossary.html b/en/learn/glossary.html deleted file mode 100644 index 2a18252ad8..0000000000 --- a/en/learn/glossary.html +++ /dev/null @@ -1,502 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Glossary" -redirect_from: -- /en/glossary.html ---- - -

    This is a glossary of both Vespa-specific terminology, and general terms useful in this context.

    -
    - -
      -
    • -

      Application

      -

      - The unit of deployment and management. - It can contain any number of clusters and schemas etc., but all deployed together. - The files defining the application is called Application Package. -

      -
    • -
    • -

      Attribute

      -

      - An attribute is a field with properties other than an indexed field. - Attribute fields have flexible match modes, including exact match, prefix match, and case-sensitive matching. - Attributes enable high sustained update rates by writing directly to memory without disk access. - Features like Grouping, Sorting, and Parent/Child use attributes. -

      -
    • -
    • - -

      - Use Predicate fields - to match queries to a set of boolean constraints in documents. - The typical use case is to have a set of boolean constraints - representing advertisements, specifying their target groups. - Example: hobby in [Music, Hiking] and age in [20..30]. -

      -
    • -
    • -

      Cluster

      -

      - A set of homogenous nodes which all perform the same task. - Vespa has two types: Container clusters are stateless, and content clusters store and process the data. -

      -
    • -
    • -

      Component

      -

      - Components extend a base class from the Container code module; - some are Chained for execution. - The component types are: -

      - -
    • -
    • -

      Configuration Server

      -

      - The configuration server hosts most of the control plane of Vespa, - where application packages are deployed to - often shortened to "config server". - Config servers are deployed as one or in a cluster - see overview. - The config server serves configuration for all Vespa processes, - and is normally the first cluster started. -

      -
    • -
    • -

      Container

      -

      - Vespa's Java container, hosting all application components as well as the stateless logic of Vespa itself. - Read more in Container. - Not to be confused with Docker Containers. -

      -
    • -
    • -

      Content Node

      -

      - Content nodes are stateful and holds the document and index data - - see content nodes. - These nodes implement Vespa's elasticity - for seamless data migration and scaling. -

      -
    • -
    • -

      Control Plane

      -

      - The deploy-commands are Vespa's control plane. - The control plane is often secured with other credentials than the data plane. - Often low throughput and used by automation like GitHub Actions to deploy new versions - of application packages. -

      -
    • -
    • -

      Data Plane

      -

      - Document and Query APIs make the Vespa Data plane. - Also see control plane. - Often high throughout / low latency, as this is user-serving. -

      -
    • -
    • -

      Deploy

      -

      - deploy is a control-plane command to upload and activate a new version of an - application package. -

      -
    • -
    • -

      Deployment

      -

      - A deployment is a running Vespa application, - created by using deploy. -

      -
    • -
    • -

      Diversity

      -

      - Result diversity means having diverse results in the result set. - As an example, not return the n highest ranking results, but eliminate similar hits, e.g. from the same domain. - Refer to diversity and - grouping for features to eliminate similar hits or group them together. -

      -
    • -
    • -

      Docker

      -

      - Vespa is available as a container image from - hub.docker.com. - Products to run this image include Docker, Podman and runC, - and it enables users to run Vespa in a well-defined environment on multiple platforms. - Read more in Docker Containers. -

      -
    • -
    • -

      Document

      -

      - Vespa models data as documents. - A document has a string identifier, set by the application, unique across all documents. - A document is a set of key-value pairs. - A document has a Schema. - Read more in Documents. -

      -
    • -
    • -

      Document frequency (normalized)

      -

      - The document frequency of a term captures how often the term occurs in the document corpus - relative to the total number of documents. - For ranking purposes this value is always normalized so that it is in the range [0, 1]. - For example, if a term occurs in 600 out of 1000 documents, its normalized document - frequency will be \(600/1000 = 0.6\). -

      -

      - From an information retrieval perspective, the normalized document frequency gives a measure - of how common (or rare) a term is. Query terms that occur rarely (thus having a low document - frequency) are usually expected to be more relevant to the query, since they are - more specific. On the other end, very common terms (with high document frequency) are often - considered to be "stopwords" (such as "the", "an" etc.), and are expected to have a low - contribution to query relevance. This is directly related to - inverse document frequency, - which is used by classic text ranking algorithms such as tf-idf - and BM25. -

      -
    • -
    • -

      Document summary

      -

      - A document summary - is the information that is shown for each document in a query result. - What information to include is determined by a document summary class: - A named set of fields with config on which information they should contain. - When Vespa stores a document, it is written to the document store - and used to generate summaries. - The document store is scanned when using streaming search. -

      -
    • -
    • -

      Document Processor

      -

      - Document processing is a framework to create chains of configurable Components - that read and modify document operations. - A Document Processor uses getFieldValue() and setFieldValue() to process fields, - alternatively using generated code from Concrete Documents. -

      -
    • -
    • -

      Document Type

      -

      The data type part of a Schema - a collection of fields.

      -
    • -
    • -

      Elasticity

      -

      - Vespa's clusters are elastic - a user can add or remove nodes on running applications without service disruption. - For the stateful content nodes, this causes data sync between nodes for uniform distribution, - with minimal data re-distribution. Read more in Elasticity. -

      -
    • -
    • -

      Enclave

      -

      - Vespa Cloud Enclave is a feature to run your Vespa application in Vespa Cloud in your own AWS or GCP account, - see the Enclave documentation. -

      -
    • -
    • -

      Embedding

      -

      - A common technique in modern big data serving applications is to map the subject data - say, text or images - - to points in an abstract vector space and then do computation in that vector space. - For example, retrieve similar data by finding nearby points in the vector space, - or using the vectors as input to a neural net. - This mapping is usually referred to as embedding, - and Vespa provides built-in support for this. -

      -
    • -
    • -

      Estimated hit ratio

      -

      - When Vespa plans how a query should be evaluated in the most efficient way - possible, one of the most important pieces of information is how many hits - different parts of the query will produce. The estimated hit ratio is a normalized - number in the range [0, 1] that states the proportion of documents that is expected - to match a given part of the query. -

      -

      - For example, a query with an AND operator over multiple terms will benefit - by having the query planner place the term with the lowest estimated hit - ratio first in the AND's evaluation order. This is because that term will be - the cheapest to evaluate (least number of candidate documents to iterate over), and all - other terms can be excluded as a possible match if it doesn't match. -

      -
    • -
    • -

      Federation

      -

      - The Container allows multiple sources of data - to be federated to a common search service. - The sources of data may be both search clusters, or external services, - backed by Vespa or any other kind of service. - The container may be used as a pure federation platform - by setting up a system consisting solely of container nodes federating to external services. -

      -
    • -
    • -

      Field

      -

      - Documents have Fields. - A field has a type, and a field contained in a document can be written to, read from and queried. - A field can also be generated (i.e. a synthetic field) - - in this case, the field definition is outside the document - example. - A field can be singlevalue, like a string, or multivalue, like an array of strings. -

      -
    • -
    • -

      Fieldset

      -

      - The term fieldset has two meanings in Vespa: -

      -
        -
      • - A collection of fields that are queried together - - configured in the schema: -
        -fieldset myset {
        -    fields: artist, title, album
        -}
        -
        -
      • -
      • - A collection of fields to return for a GET or VISIT operation, see the guide: -
        -$ vespa visit --field-set restaurant:name,rating
        -
        -
      • -
      -
    • -
    • -

      Garbage Collection

      -

      - Use a Document Selection - to auto-expire documents by time or any other criterion. -

      -
    • -
    • -

      Grouping

      -

      - Vespa Grouping is a list processing language which describes how the query hits should be grouped, - aggregated and presented in result sets. - A grouping statement takes the list of all matches to a query as input and groups/aggregates it, - possibly in multiple nested and parallel ways to produce the output. - Read more. -

      -
    • -
    • -

      Handler

      -

      - Also called Request Handler. - A handler is a Component used to build API endpoints on the Container. - Find documentation at developing request handlers, - and example use. -

      -
    • -
    • -

      Indexing

      -

      - The process of creating index structures. - This includes routing document writes to indexing processors, - processing (indexing) documents and writing the documents to content clusters. - Settings like streaming search do not create indices to optimize resource usage. -

      -
    • -
    • -

      Instance

      -

      - Instance is always "default" in Vespa.ai - (i.e. there is only one) - - managed services like Vespa Cloud support multiple, - read more. - An instance is a deployment of an application for a given purpose, like production serving - - multiple instances of an application can be used to support more use cases like integration testing. -

      -
    • -
    • -

      Namespace

      -

      - A segment of document IDs - which helps you generate unique ids also if you have multiple sources of unique values. - Namespace can be used to Visit a subspace of the corpus. - -

      -
    • -
    • - -

      - Nearest neighbor search, - or vector search, - is a technique used to find the closest data points to a given query point in a high-dimensional vector space - - see distance metric. - It can be exact or approximate. - - This is supported in Vespa using the - nearestNeighbor query operator. -

      -
    • -
    • -

      Node

      -

      - A Node is a host / container instance running one or more Services. - The mapping from logical to actual name is configured in hosts.xml. -

      -
    • -
    • -

      Parent / Child

      -

      - Using document references, documents can have parent/child relationships. - Use this to join data by importing fields from parent documents. - Parent documents are replicated to all nodes in the cluster. -

      -
    • -
    • -

      Partial Update

      -

      - A partial update is an update to one or more fields in a document. - It also includes updating all index structures, - so the effect of the partial update is immediately observable in queries. - Partial updates do not require the full document, - and enables a high write throughput with memory-only operations. - Read more. -

      -
    • -
    • -

      Posting List

      -

      - A posting list is a fundamental data structure in information retrieval and search engines. - It is used in inverted indexes to store the occurrences of a term in a collection of documents. - Read more. -

      -
    • -
    • -

      Quantization

      -

      - Quantization is the process of constraining an input - from a continuous or otherwise large set of values (such as the real numbers) - to a discrete set (such as the integers). - It is a way to reduce memory and CPU usage for tensor operations - in nearest neighbor search, - to improve throughput or latencies. - -

      -
    • -
    • -

      Query

      -

      - Use the Query API to query the corpus. - Queries are written in YQL, - or can be created programmatically in a Searcher. - Configure query execution in a Query Profile. -

      -
    • -
    • -

      Ranking

      -

      - Ranking is where Vespa does computing, or inference over documents. - The computations to be done are expressed in functions called - Ranking Expressions, - bundled into Rank Profiles defined in a Schema. - These can range from simple math expressions combining some rank features, - to tensor expressions or large machine-learned models. - Ranking can be single- or multiphased. -

      -
    • -
    • -

      Schema

      -

      - A description of a particular type of data and how to process/rank it. - See the Schema guide. -

      -
    • -
    • -

      Searcher

      -

      - A searcher is a Component - usually deployed as part of an OSGi bundle. - All Searchers must implement a single method search(query). - Developers implement application query logic in Searchers - - read more. -

      -
    • -
    • - -

      - Semantic search denotes search with meaning, - as distinguished from lexical search where the search engine looks for literal matches of the query words. - Read - Revolutionizing Semantic Search with Multi-Vector HNSW Indexing - for more details on semantic search, pointers to resources, and how to implement it. -

      -
    • -
    • -

      Service

      -

      - A Service runs in a Cluster of container or content nodes, - configured in services.xml. -

      -
    • -
    • - -

      - Streaming search is querying fields that do not have an index structure. - The indexing cost is minimal as no index is generated. - A query is hence a scan over all data, and normally slower than using index structures. - Streaming search is used for applications like personal search, where the searched data volume is small. - It can be a powerful option to drastically limit memory use in nearest-neighbor applications - where the possible neighbor set it orders of magnitude smaller than the total. -

      -
    • -
    • -

      Tenant

      -

      - An organizational unit that owns applications. - In Vespa.ai APIs, tenant and application are always "default", - and a Vespa system has exactly one tenant - and one application. - On Vespa Cloud, - multiple tenants and applications is supported - - read more. -

      -
    • -
    • -

      Tensor

      -

      - A Tensor is a data structure which generalizes scalars, vectors and matrices - to any number of dimensions: - A scalar is a tensor of rank 0, a vector is a tensor of rank 1, a matrix is a tensor of rank 2. - Tensors consist of a set of scalar valued cells, with each cell having a unique address. - A cell's address is specified by its index or label in all the dimensions of that tensor. - The number of dimensions in a tensor is the rank of the tensor, - each dimension can be either mapped or indexed. -

      -
    • -
    • -

      Visit

      -

      - Visit is a feature to efficiently get or process a set of / all documents, - identified by a Document Selection Expression. - Visit iterates over all, or a set of, buckets and sends documents to a (set of) targets. -

      -
    • -
    - - diff --git a/en/learn/glossary.mdx b/en/learn/glossary.mdx new file mode 100644 index 0000000000..23767a5b48 --- /dev/null +++ b/en/learn/glossary.mdx @@ -0,0 +1,222 @@ +--- +title: Glossary +description: "This is a glossary of both Vespa-specific terminology, and general terms useful in this context." +--- + +- **Application** + + The unit of deployment and management. It can contain any number of clusters and schemas etc., but all deployed together. The files defining the application is called [Application Package](/en/basics/applications). + +- **Attribute** + + An attribute is a field with properties other than an indexed field. Attribute fields have flexible match modes, including exact match, prefix match, and case-sensitive matching. Attributes enable high sustained update rates by writing directly to memory without disk access. Features like Grouping, Sorting, and [Parent/Child](/en/learn/glossary#parent-child) use attributes. + +- **Boolean Search** + + Use [Predicate fields](/en/schemas/predicate-fields) to match queries to a set of boolean constraints in documents. The typical use case is to have a set of boolean constraints representing advertisements, specifying their target groups. Example: `hobby in [Music, Hiking] and age in [20..30]`. + +- **Cluster** + + A set of homogenous nodes which all perform the same task. Vespa has two types: Container clusters are stateless, and content clusters store and process the data. + +- **Component** + + Components extend a base class from the Container code module; some are [Chained](/en/applications/chaining) for execution. The component types are: + + - [Processors](/en/applications/processing#processors) + - [Searchers](/en/learn/glossary#searcher) + - [Document Processors](/en/learn/glossary#document-processor) + - [Search Result Renderers](/en/applications/result-renderers) + - [Provider Components](/en/applications/dependency-injection#special-components) + +- **Configuration Server** + + The configuration server hosts most of the control plane of Vespa, where application packages are deployed to - often shortened to "config server". Config servers are deployed as one or in a cluster - see [overview](/en/learn/overview). The config server serves configuration for all Vespa processes, and is normally the first cluster started. + +- **Container** + + Vespa's Java container, hosting all application components as well as the stateless logic of Vespa itself. Read more in [Container](/en/applications/containers). Not to be confused with [Docker Containers](/en/learn/glossary#docker). + +- **Content Node** + + Content nodes are stateful and holds the document and index data - see [content nodes](/en/content/content-nodes). These nodes implement Vespa's [elasticity](/en/content/elasticity) for seamless data migration and scaling. + +- **Control Plane** + + The deploy-commands are Vespa's control plane. The control plane is often secured with other credentials than the [data plane](/en/learn/glossary#data-plane). Often low throughput and used by automation like GitHub Actions to deploy new versions of application packages. + +- **Data Plane** + + Document and Query APIs make the Vespa Data plane. Also see [control plane](/en/learn/glossary#control-plane). Often high throughout / low latency, as this is user-serving. + +- **Deploy** + + `deploy` is a control-plane command to upload and activate a new version of an [application package](/en/learn/glossary#application). + +- **Deployment** + + A deployment is a running Vespa application, created by using [deploy](/en/learn/glossary#deploy). + +- **Diversity** + + Result diversity means having diverse results in the result set. As an example, not return the n highest ranking results, but eliminate similar hits, e.g. from the same domain. Refer to [diversity](/en/reference/schemas/schemas#diversity) and [grouping](/en/querying/grouping) for features to eliminate similar hits or group them together. + +- **Docker** + + Vespa is available as a container image from [hub.docker.com](https://hub.docker.com/r/vespaengine/vespa). Products to run this image include Docker, Podman and runC, and it enables users to run Vespa in a well-defined environment on multiple platforms. Read more in [Docker Containers](/en/operations/self-managed/docker-containers). + +- **Document** + + Vespa models data as documents. A document has a string identifier, set by the application, unique across all documents. A document is a set of key-value pairs. A document has a [Schema](/en/learn/glossary#schema). Read more in [Documents](/en/schemas/documents). + +- **Document frequency (normalized)** + + The *document frequency* of a term captures how often the term occurs in the document corpus relative to the total number of documents. For ranking purposes this value is always normalized so that it is in the range [0, 1]. For example, if a term occurs in 600 out of 1000 documents, its normalized document frequency will be \(600/1000 = 0.6\). + + From an information retrieval perspective, the normalized document frequency gives a measure of how common (or rare) a term is. Query terms that occur rarely (thus having a low document frequency) are usually expected to be more *relevant* to the query, since they are more specific. On the other end, very common terms (with high document frequency) are often considered to be "stopwords" (such as "the", "an" etc.), and are expected to have a low contribution to query relevance. This is directly related to [inverse document frequency](https://en.wikipedia.org/wiki/Tf%E2%80%93idf#Inverse_document_frequency), which is used by classic text ranking algorithms such as [tf-idf](https://en.wikipedia.org/wiki/Tf%E2%80%93idf) and [BM25](/en/ranking/bm25). + +- **Document summary** + + A [document summary](/en/querying/document-summaries) is the information that is shown for each document in a query result. What information to include is determined by a document summary class: A named set of fields with config on which information they should contain. When Vespa stores a document, it is written to the [document store](/en/content/proton#document-store) and used to generate summaries. The document store is scanned when using [streaming search](/en/performance/streaming-search). + +- **Document Processor** + + Document processing is a framework to create chains of configurable [Components](/en/learn/glossary#component) that read and modify document operations. A Document Processor uses `getFieldValue()` and `setFieldValue()` to process fields, alternatively using generated code from [Concrete Documents](/en/schemas/concrete-documents). + +- **Document Type** + + The data type part of a [Schema](/en/learn/glossary#schema) - a collection of fields. + +- **Elasticity** + + Vespa's clusters are elastic - a user can add or remove nodes on running applications without service disruption. For the stateful content nodes, this causes data sync between nodes for uniform distribution, with minimal data re-distribution. Read more in [Elasticity](/en/content/elasticity). + +- **Enclave** + + Vespa Cloud Enclave is a feature to run your Vespa application in Vespa Cloud in your own AWS or GCP account, see the [Enclave documentation](/en/operations/enclave/enclave). + +- **Embedding** + + A common technique in modern big data serving applications is to map the subject data - say, text or images - to points in an abstract vector space and then do computation in that vector space. For example, retrieve similar data by finding nearby points in the vector space, or using the vectors as input to a neural net. This mapping is usually referred to as *embedding*, and Vespa provides [built-in support](/en/rag/embedding) for this. + +- **Estimated hit ratio** + + When Vespa plans how a query should be evaluated in the most efficient way possible, one of the most important pieces of information is how many *hits* different parts of the query will produce. The estimated hit ratio is a normalized number in the range [0, 1] that states the proportion of documents that is expected to match a given part of the query. + + For example, a query with an `AND` operator over multiple terms will benefit by having the query planner place the term with the *lowest* estimated hit ratio *first* in the AND's evaluation order. This is because that term will be the cheapest to evaluate (least number of candidate documents to iterate over), and all other terms can be excluded as a possible match if it doesn't match. + +- **Federation** + + The [Container](/en/learn/glossary#container) allows multiple sources of data to be [federated](/en/querying/federation) to a common search service. The sources of data may be both search clusters, or external services, backed by Vespa or any other kind of service. The container may be used as a pure federation platform by setting up a system consisting solely of container nodes federating to external services. + +- **Field** + + Documents have [Fields](/en/basics/schemas#document-fields). A field has a type, and a field contained in a document can be written to, read from and queried. A field can also be generated (i.e. a synthetic field) - in this case, the field definition is outside the document - [example](/en/writing/indexing#date-indexing). A field can be singlevalue, like a string, or multivalue, like an array of strings. + +- **Fieldset** + + The term *fieldset* has two meanings in Vespa: + +- **A collection of fields that are queried together - configured in the [schema](/en/reference/schemas/schemas#fieldset):** + + ```sql + fieldset myset { + fields: artist, title, album + } + ``` + +- **A collection of fields to return for a GET or VISIT operation, see the [guide](/en/schemas/documents#fieldsets):** + + ```text + $ vespa visit --field-set restaurant:name,rating + ``` + +- **Garbage Collection** + + Use a [Document Selection](/en/reference/applications/services/content#document) to [auto-expire](/en/schemas/documents#document-expiry) documents by time or any other criterion. + +- **Grouping** + + Vespa Grouping is a list processing language which describes how the query hits should be grouped, aggregated and presented in result sets. A grouping statement takes the list of all matches to a query as input and groups/aggregates it, possibly in multiple nested and parallel ways to produce the output. [Read more](/en/querying/grouping). + +- **Handler** + + Also called *Request Handler*. A handler is a [Component](/en/learn/glossary#component) used to build API endpoints on the [Container](/en/learn/glossary#container). Find documentation at [developing request handlers](/en/applications/request-handlers), and [example use](https://github.com/vespa-engine/sample-apps/tree/master/model-inference/src/main/java/ai/vespa/example). + +- **Indexing** + + The process of creating index structures. This includes routing document writes to indexing processors, processing (/en/writing/indexing ) documents and writing the documents to content clusters. Settings like [streaming search](/en/learn/glossary#streaming-search) do not create indices to optimize resource usage. + +- **Instance** + + *Instance* is always "default" in Vespa.ai (i.e. there is only one) - managed services like [Vespa Cloud](/) support multiple, [read more](/en/learn/tenant-apps-instances). An instance is a deployment of an application for a given purpose, like production serving - multiple instances of an application can be used to support more use cases like integration testing. + +- **Namespace** + + A segment of [document IDs](/en/learn/glossary#document) which helps you generate unique ids also if you have multiple sources of unique values. Namespace can be used to [Visit](/en/learn/glossary#visit) a subspace of the corpus. + +- **Nearest neighbor search** + + [Nearest neighbor search](/en/querying/nearest-neighbor-search), or [vector search](/en/querying/vector-search-intro), is a technique used to find the closest data points to a given query point in a high-dimensional vector space - see [distance metric](/en/querying/nearest-neighbor-search#distance-metrics-for-nearest-neighbor-search). It can be exact or approximate. + + This is supported in Vespa using the [nearestNeighbor](/en/reference/querying/yql) query operator. + +- **Node** + + A Node is a host / container instance running one or more [Services](/en/learn/glossary#service). The mapping from logical to actual name is configured in [hosts.xml](/en/reference/applications/hosts). + +- **Parent / Child** + + Using document references, documents can have [parent/child](/en/schemas/parent-child) relationships. Use this to join data by importing fields from parent documents. Parent documents are replicated to all nodes in the cluster. + +- **Partial Update** + + A partial update is an update to one or more fields in a document. It also includes updating all index structures, so the effect of the partial update is immediately observable in queries. Partial updates do not require the full document, and enables a high write throughput with memory-only operations. [Read more](/en/writing/partial-updates). + +- **Posting List** + + A posting list is a fundamental data structure in information retrieval and search engines. It is used in inverted indexes to store the occurrences of a term in a collection of documents. [Read more](/en/performance/feature-tuning#posting-lists). + +- **Quantization** + + Quantization is the process of constraining an input from a continuous or otherwise large set of values (such as the real numbers) to a discrete set (such as the integers). It is a way to reduce memory and CPU usage for [tensor operations](/en/learn/glossary#tensor) in [nearest neighbor search](/en/learn/glossary#nearest-neighbor-search), to improve throughput or latencies. + +- **Query** + + Use the [Query API](/en/querying/query-api) to query the corpus. Queries are written in [YQL](/en/reference/querying/yql), or can be created programmatically in a [Searcher](/en/learn/glossary#searcher). Configure query execution in a [Query Profile](/en/querying/query-profiles). + +- **Ranking** + + Ranking is where Vespa does computing, or inference over documents. The computations to be done are expressed in functions called [Ranking Expressions](/en/ranking/ranking-expressions-features#ranking-expressions), bundled into [Rank Profiles](/en/basics/ranking#rank-profiles) defined in a [Schema](/en/learn/glossary#schema). These can range from simple math expressions combining some rank features, to tensor expressions or large machine-learned models. Ranking can be single- or [multiphased](/en/ranking/phased-ranking). + +- **Schema** + + A description of a particular type of data and how to process/rank it. See the [Schema guide](/en/basics/schemas). + +- **Searcher** + + A searcher is a [Component](/en/learn/glossary#component) - usually deployed as part of an OSGi bundle. All Searchers must implement a single method `search(query)`. Developers implement application query logic in Searchers - [read more](/en/applications/searchers). + +- **Semantic search** + + Semantic search denotes search with meaning, as distinguished from lexical search where the search engine looks for literal matches of the query words. Read [ Revolutionizing Semantic Search with Multi-Vector HNSW Indexing](https://blog.vespa.ai/semantic-search-with-multi-vector-indexing/) for more details on semantic search, pointers to resources, and how to implement it. + +- **Service** + + A Service runs in a [Cluster](/en/learn/glossary#cluster) of container or content nodes, configured in [services.xml](/en/reference/applications/services/services). + +- **Streaming search** + + [Streaming search](/en/performance/streaming-search) is querying fields that do not have an index structure. The indexing cost is minimal as no index is generated. A query is hence a scan over all data, and normally slower than using index structures. Streaming search is used for applications like personal search, where the searched data volume is small. It can be a powerful option to drastically limit memory use in nearest-neighbor applications where the possible neighbor set it orders of magnitude smaller than the total. + +- **Tenant** + + An organizational unit that owns [applications](/en/learn/glossary#application). In Vespa.ai APIs, *tenant* and *application* are always "default", and a Vespa system has exactly one tenant and one application. On [Vespa Cloud](/), multiple tenants and applications is supported - [read more](/en/learn/tenant-apps-instances). + +- **Tensor** + + A [Tensor](/en/ranking/tensor-user-guide) is a data structure which generalizes scalars, vectors and matrices to any number of dimensions: A scalar is a tensor of rank 0, a vector is a tensor of rank 1, a matrix is a tensor of rank 2. Tensors consist of a set of scalar valued cells, with each cell having a unique address. A cell's address is specified by its index or label in all the dimensions of that tensor. The number of dimensions in a tensor is the rank of the tensor, each dimension can be either mapped or indexed. + +- **Visit** + + [Visit](/en/writing/visiting) is a feature to efficiently get or process a set of / all documents, identified by a [Document Selection Expression](/en/reference/writing/document-selector-language). Visit iterates over all, or a set of, buckets and sends documents to a (set of) targets. diff --git a/en/learn/llm-help.md b/en/learn/llm-help.mdx similarity index 50% rename from en/learn/llm-help.md rename to en/learn/llm-help.mdx index 456cb84481..0b239f2d21 100644 --- a/en/learn/llm-help.md +++ b/en/learn/llm-help.mdx @@ -1,57 +1,52 @@ --- -# Copyright Vespa.ai. All rights reserved. -title: "Getting help from LLMs" -redirect_from: -- /en/llm-help +title: Getting help from LLMs +description: "This page describes some of the ways that you can get help from large language models (LLMs) when developing a Vespa application." --- -This page describes some of the ways that you can get help from large language models (LLMs) when developing a Vespa application. - From our experience, providing the right context to the LLM is essential to get good results when asking questions about Vespa. ## Markdown version of documentation pages -Every page of the documentation is available in Markdown format, by changing the URL from `.html` to `.html.md`. -There is also a link to the markdown version in the top right corner of each page. +Every page of the documentation is available in Markdown format, by changing the URL from `.html` to `.html.md`. There is also a link to the markdown version in the top right corner of each page. This can for example be used to copy/paste relevant markdown documentation page(s) into your AI tool of choice when working with LLMs on particular topics. ## llms.txt -We provide an [llms.txt](../../llms.txt) file, that can serve as a top level entrypoint for an LLM, which includes both top-level overview, architecture, -as well as title of and link to markdown-version of all documentation pages. +We provide an [llms.txt](https://docs.vespa.ai/llms.txt) file, that can serve as a top level entrypoint for an LLM, which includes both top-level overview, architecture, as well as title of and link to markdown-version of all documentation pages. See [llmstxt.org](https://llmstxt.org/) for more information about the format. ### Example usage -The [llms.txt](../../llms.txt) file can be downloaded with: +The [llms.txt](https://docs.vespa.ai/llms.txt) file can be downloaded with: ```bash -curl -O https://docs.vespa.ai/llms.txt +curl -O /llms.txt ``` -This file can then be used as an entrypoint when working with LLMs, either through an IDE, CLI or a chat interface. -If the LLM has a tool available that allows it to fetch the referenced URLs, it can fetch the content of the desired pages as needed. +This file can then be used as an entrypoint when working with LLMs, either through an IDE, CLI or a chat interface. If the LLM has a tool available that allows it to fetch the referenced URLs, it can fetch the content of the desired pages as needed. -We also provide [llms-full.txt](../../llms-full.txt) which contains the _full_ content of all documentation pages in markdown format. -This file is relatively large (almost 0.5M words as of Oct 2025), so use accordingly. +We also provide [llms-full.txt](https://docs.vespa.ai/llms-full.txt) which contains the _full_ content of all documentation pages in markdown format. This file is relatively large (almost 0.5M words as of Oct 2025), so use accordingly. ## MCP Server + ### Public Vespa MCP server + We don't provide any official [MCP](https://modelcontextprotocol.io/) server at this time, but will update this page as soon as we do. ### Personal MCP server -Users can enable MCP server capablities in their own Vespa apps. -This can be done by adding `McpRequestHandler` to `services.xml` with one or more `McpSpecProvider` components. -A pre-built `McpSearchSpecProvider` already exists, and a usage example can be found in [this sample app](https://github.com/vespa-engine/sample-apps/tree/master/examples/mcp-server-app). -This exposes Vespa search to LLMs via the `/mcp/` endpoint. +Users can enable MCP server capablities in their own Vespa apps. This can be done by adding `McpRequestHandler` to `services.xml` with one or more `McpSpecProvider` components. + +A pre-built `McpSearchSpecProvider` already exists, and a usage example can be found in [this sample app](https://github.com/vespa-engine/sample-apps/tree/master/examples/mcp-server-app). This exposes Vespa search to LLMs via the `/mcp/` endpoint. Users can add more tools by implementing `McpSpecProvider` and adding the components in `services.xml`. #### Example MCP config + Add this to `services.xml` + ```xml diff --git a/en/learn/migrating-from-elastic-search.html b/en/learn/migrating-from-elastic-search.html deleted file mode 100644 index 1d4d09197a..0000000000 --- a/en/learn/migrating-from-elastic-search.html +++ /dev/null @@ -1,333 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: Migrating from Elasticsearch -applies_to: cloud -redirect_from: -- /en/cloud/migrating-from-elastic-search-to-vespa.html ---- - - -

    - This is a guide for how to move data from Elasticsearch to Vespa. - By the end of this guide you will have exported documents from Elasticsearch, - generated a deployable Vespa application package and tested this with documents and queries. -

    -{% include pre-req.html extra-reqs=' -
  • - Linux, macOS or Windows 10 Pro on x86_64 or arm64, - with Podman or Docker installed. - See Docker Containers - for system limits and other settings. -
  • ' %} -

    - To get started, sign up to get an endpoint to deploy to. - Set the tenant name from the signup: -

    -
    - -
    -$ export TENANT_NAME=vespa-team    # Replace with your tenant name
    -
    -
    -

    Alternatively, test with local deployment.

    - - - -

    Feed a sample Elasticsearch index

    -

    - This section sets up an index with 1000 sample documents using - getting-started-index. - Skip this part if you already have an index. - Wait for Elasticsearch to start: -

    -
    - -
    -$ docker network create --driver bridge esnet
    -
    -$ docker run -d --rm --name esnode --network esnet -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" \
    -  docker.elastic.co/elasticsearch/elasticsearch:7.10.2
    -
    -$ while [[ "$(curl -s -o /dev/null -w ''%{http_code}'' localhost:9200)" != "200" ]]; do sleep 1; echo 'waiting ...'; done
    -
    -
    -

    Download test data, and feed it to the Elasticsearch instance:

    -
    - -
    -$ curl 'https://raw.githubusercontent.com/elastic/elasticsearch/7.10/docs/src/test/resources/accounts.json' \
    -  > accounts.json
    -
    -$ curl -H "Content-Type:application/json" --data-binary @accounts.json 'localhost:9200/bank/_bulk?pretty&refresh'
    -
    -
    -

    Verify that the index has 1000 documents:

    -
    - -
    -$ curl 'localhost:9200/_cat/indices?v'
    -
    -
    - - - -

    Export documents from Elasticsearch

    -

    - This guide uses ElasticDump - to export the index contents and the index mapping. - Export the documents and mappings, then delete the Docker network and the Elasticsearch container: -

    -
    - -
    -$ docker run --rm --name esdump --network esnet -v "$PWD":/dump -w /dump elasticdump/elasticsearch-dump \
    -  --input=http://esnode:9200/bank --output=bank_data.json --type=data
    -
    -$ docker run --rm --name esdump --network esnet -v "$PWD":/dump -w /dump elasticdump/elasticsearch-dump \
    -  --input=http://esnode:9200/bank --output=bank_mapping.json --type=mapping
    -
    -$ docker rm -f esnode && docker network remove esnet
    -
    -
    - - - -

    Generate Vespa documents and Application Package

    -

    - ES_Vespa_parser.py - is provided for conversion of Elasticsearch data and index mappings to Vespa data and configuration. - It is a basic script with minimal error checking - - it is designed for a simple export, modify this as needed for your application's needs. - Generate Vespa documents and configuration: -

    -
    - -
    -$ curl 'https://raw.githubusercontent.com/vespa-engine/vespa/master/config-model/src/main/python/ES_Vespa_parser.py' \
    -  > ES_Vespa_parser.py
    -
    -$ python3 ./ES_Vespa_parser.py --application_name bank bank_data.json bank_mapping.json
    -
    -
    -

    - This generates documents in documents.json - (see JSON format) - where each document has IDs like this id:bank:_doc::1. - It also generates a bank folder with an application package: -

    -
    -/bank
    -      │
    -      ├── documents.json
    -      ├── hosts.xml
    -      ├── services.xml
    -      └── /schemas
    -            └── _doc.sd
    -
    -

    Enter the application package directory:

    -
    - -
    -$ cd bank
    -
    -
    - - - - -

    Deploy

    -

    - Install Vespa CLI. - In this example we use Homebrew, - you can also download from GitHub: -

    -
    - -
    -$ brew install vespa-cli
    -
    -
    -

    Configure for Vespa Cloud deployment, log in and add credentials:

    -
    - -
    -$ vespa config set target cloud
    -$ vespa config set application $TENANT_NAME.myapp.default
    -
    -
    -
    - -
    -$ vespa auth login
    -
    -
    - - -
    -$ export VESPA_CLI_HOME=$PWD/.vespa TMPDIR=$PWD/.tmp
    -$ mkdir -p $TMPDIR
    -$ vespa config set target cloud
    -$ vespa config set application vespa-team.migration
    -$ export VESPA_CLI_API_KEY="$(echo "$VESPA_TEAM_API_KEY" | openssl base64 -A -a -d)"
    -
    -
    - -
    -$ vespa auth cert
    -
    -
    -

    - Also see getting started guide. - Deploy the application package: -

    -
    - -
    -$ vespa deploy --wait 300
    -
    -
    -

    Index the documents exported from Elasticsearch:

    -
    - -
    -$ vespa feed documents.json
    -
    -
    - - - -

    Interfacing with Vespa

    -

    Export all documents:

    -
    - -
    -$ vespa visit
    -
    -
    -

    Get a document:

    -
    - -
    -$ vespa document get id:bank:_doc::1
    -
    -
    -

    Count documents, find "totalCount":1000 in the output:

    -
    - -
    -$ vespa query 'select * from _doc where true'
    -
    -
    -

    Run a simple query against the firstname field:

    -
    - -
    -$ vespa query 'select firstname,lastname from _doc where firstname contains "amber"'
    -
    -
    - - - -

    Next steps

    -

    Review the differences in document records, Vespa to the right:

    - - - - - -
    -
    -{
    -    "_index": "bank",
    -    "_type": "_doc",
    -    "_id": "1",
    -    "_score": 1,
    -    "_source": {
    -      "account_number": 1,
    -      "balance": 39225,
    -      "firstname": "Amber",
    -      "lastname": "Duke",
    -      "age": 32,
    -      "gender": "M",
    -      "address": "880 Holmes Lane",
    -      "employer": "Pyrami",
    -      "email": "amberduke@pyrami.com",
    -      "city": "Brogan",
    -      "state": "IL"
    -    }
    -}
    -
    -
    -
    -{
    -
    -    "put": "id:bank:_doc::1",
    -
    -
    -    "fields": {
    -      "account_number": 1,
    -      "balance": 39225,
    -      "firstname": "Amber",
    -      "lastname": "Duke",
    -      "age": 32,
    -      "gender": "M",
    -      "address": "880 Holmes Lane",
    -      "employer": "Pyrami",
    -      "email": "amberduke@pyrami.com",
    -      "city": "Brogan",
    -      "state": "IL"
    -    }
    -}
    -
    -
    -

    - The id field - id:bank:_doc::1 is composed of: -

    -
      -
    • namespace: bank
    • -
    • schema: _doc
    • -
    • id: 1
    • -
    - -

    - Read more in Documents and - Schemas. - The schema is the key Vespa configuration file where field types - and ranking are configured. - The schema (found in schemas/_doc.sd) also has - indexing settings, example: -

    -
    -search _doc {
    -    document _doc {
    -        field account_number type long {
    -            indexing: summary | attribute
    -        }
    -        field address type string {
    -            indexing: summary | index
    -        }
    -        ...
    -    }
    -}
    -
    -

    - These settings impact both performance and how fields are matched. - For example, the account_number above is using the attribute keyword, - which makes the field available for sorting, - ranking, grouping, - but which by default does not have data structures for fast search. - Read more in attributes and - - practical search performance guide. -

    - - - -

    Test with local deployment

    -

    - To run the steps above, using a local deployment, - follow the steps in the quickstart - to start a local container running Vespa. - Then, deploy the application package from the bank folder. -

    diff --git a/en/learn/migrating-from-elastic-search.mdx b/en/learn/migrating-from-elastic-search.mdx new file mode 100644 index 0000000000..a68cb9ca1f --- /dev/null +++ b/en/learn/migrating-from-elastic-search.mdx @@ -0,0 +1,239 @@ +--- +title: Migrating from Elasticsearch +description: "This is a guide for how to move data from Elasticsearch to Vespa. By the end of this guide you will have exported documents from Elasticsearch, generated a deployable Vespa application package and tested this with documents and queries." +--- + + +**Prerequisites:** + +- Linux, macOS or Windows 10 Pro on x86\_64 or arm64, with [Podman Desktop](https://podman.io/) or [Docker Desktop](https://www.docker.com/products/docker-desktop/) installed, with an engine running. + - Alternatively, start the Podman daemon: + + ```bash + $ podman machine init --memory 6000 + $ podman machine start + ``` + - See [Docker Containers](/en/operations/self-managed/docker-containers) for system limits and other settings. +- For CPUs older than Haswell (2013), see [CPU Support](/en/operations/self-managed/cpu-support). +- Memory: Minimum RAM dedicated to Docker/Podman. [Memory recommendations](/en/operations/self-managed/node-setup#memory-settings). +- Disk: Avoid `NO_SPACE` - the vespaengine/vespa container image + headroom for data requires disk space. [Read more](/en/writing/feed-block). +- [Homebrew](https://brew.sh/) to install the [Vespa CLI](/en/clients/vespa-cli), or download the Vespa CLI from [Github releases](https://github.com/vespa-engine/vespa/releases). +- Linux, macOS or Windows 10 Pro on x86\_64 or arm64, with Podman or [Docker](https://docs.docker.com/engine/install/) installed. See [Docker Containers](../operations/self-managed/docker-containers) for system limits and other settings. + + +To get started, [sign up](https://vespa.ai/free-trial/) to get an endpoint to deploy to. Set the *tenant name* from the signup: + +```bash +$ export TENANT_NAME=vespa-team # Replace with your tenant name +``` + +Alternatively, [test with local deployment](#test-with-local-deployment). + +## Feed a sample Elasticsearch index + +This section sets up an index with 1000 sample documents using [getting-started-index](https://www.elastic.co/guide/en/elasticsearch/reference/7.9/getting-started-index). Skip this part if you already have an index. Wait for Elasticsearch to start: + +```bash +$ docker network create --driver bridge esnet + +$ docker run -d --rm --name esnode --network esnet -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" \ + docker.elastic.co/elasticsearch/elasticsearch:7.10.2 + +$ while [[ "$(curl -s -o /dev/null -w ''%{http_code}'' localhost:9200)" != "200" ]]; do sleep 1; echo 'waiting ...'; done +``` + +Download test data, and feed it to the Elasticsearch instance: + +```bash +$ curl 'https://raw.githubusercontent.com/elastic/elasticsearch/7.10/docs/src/test/resources/accounts.json' \ + > accounts.json + +$ curl -H "Content-Type:application/json" --data-binary @accounts.json 'localhost:9200/bank/_bulk?pretty&refresh' +``` + +Verify that the index has 1000 documents: + +```bash +$ curl 'localhost:9200/_cat/indices?v' +``` + +## Export documents from Elasticsearch + +This guide uses [ElasticDump](https://github.com/elasticsearch-dump/elasticsearch-dump) to export the index contents and the index mapping. Export the documents and mappings, then delete the Docker network and the Elasticsearch container: + +```bash +$ docker run --rm --name esdump --network esnet -v "$PWD":/dump -w /dump elasticdump/elasticsearch-dump \ + --input=http://esnode:9200/bank --output=bank_data.json --type=data + +$ docker run --rm --name esdump --network esnet -v "$PWD":/dump -w /dump elasticdump/elasticsearch-dump \ + --input=http://esnode:9200/bank --output=bank_mapping.json --type=mapping + +$ docker rm -f esnode && docker network remove esnet +``` + +## Generate Vespa documents and Application Package + +[ES_Vespa_parser.py](https://github.com/vespa-engine/vespa/tree/master/config-model/src/main/python/ES_Vespa_parser.py) is provided for conversion of Elasticsearch data and index mappings to Vespa data and configuration. It is a basic script with minimal error checking - it is designed for a simple export, modify this as needed for your application's needs. Generate Vespa documents and configuration: + +```bash +$ curl 'https://raw.githubusercontent.com/vespa-engine/vespa/master/config-model/src/main/python/ES_Vespa_parser.py' \ + > ES_Vespa_parser.py + +$ python3 ./ES_Vespa_parser.py --application_name bank bank_data.json bank_mapping.json +``` + +This generates documents in *documents.json* (see [JSON format](/en/reference/schemas/document-json-format)) where each document has IDs like this `id:bank:_doc::1`. It also generates a *bank* folder with an [application package](/en/basics/applications): + +```txt +/bank + │ + ├── documents.json + ├── hosts.xml + ├── services.xml + └── /schemas + └── _doc.sd +``` + +Enter the application package directory: + +```bash +$ cd bank +``` + +## Deploy + +Install [Vespa CLI](/en/clients/vespa-cli). In this example we use [Homebrew](https://brew.sh/), you can also download from [GitHub](https://github.com/vespa-engine/vespa/releases): + +```bash +$ brew install vespa-cli +``` + +Configure for Vespa Cloud deployment, log in and add credentials: + +```bash +$ vespa config set target cloud +$ vespa config set application $TENANT_NAME.myapp.default +``` + +```bash +$ vespa auth login +``` + +```bash +$ vespa auth cert +``` + +Also see [getting started](/en/basics/deploy-an-application) guide. Deploy the application package: + +```bash +$ vespa deploy --wait 300 +``` + +Index the documents exported from Elasticsearch: + +```bash +$ vespa feed documents.json +``` + +## Interfacing with Vespa + +Export all documents: + +```bash +$ vespa visit +``` + +Get a document: + +```bash +$ vespa document get id:bank:_doc::1 +``` + +Count documents, find `"totalCount":1000` in the output: + +```bash +$ vespa query 'select * from _doc where true' +``` + +Run a simple query against the *firstname* field: + +```bash +$ vespa query 'select firstname,lastname from _doc where firstname contains "amber"' +``` + +## Next steps + +Review the differences in document records, Vespa to the right: + +Elasticsearch: + +```json +{ + "_index": "bank", + "_type": "_doc", + "_id": "1", + "_score": 1, + "_source": { + "account_number": 1, + "balance": 39225, + "firstname": "Amber", + "lastname": "Duke", + "age": 32, + "gender": "M", + "address": "880 Holmes Lane", + "employer": "Pyrami", + "email": "amberduke@pyrami.com", + "city": "Brogan", + "state": "IL" + } +} +``` + +Vespa: + +```json +{ + "put": "id:bank:_doc::1", + "fields": { + "account_number": 1, + "balance": 39225, + "firstname": "Amber", + "lastname": "Duke", + "age": 32, + "gender": "M", + "address": "880 Holmes Lane", + "employer": "Pyrami", + "email": "amberduke@pyrami.com", + "city": "Brogan", + "state": "IL" + } +} +``` + +The [id](/en/schemas/documents#document-ids) field `id:bank:_doc::1` is composed of: + +- namespace: `bank` +- schema: `_doc` +- id: `1` + +Read more in [Documents](/en/schemas/documents) and [Schemas](/en/basics/schemas). The schema is the key Vespa configuration file where field types and [ranking](/en/basics/ranking) are configured. The schema (found in `schemas/_doc.sd`) also has [indexing](/en/basics/schemas#document-fields) settings, example: + +```txt +search _doc { + document _doc { + field account_number type long { + indexing: summary | attribute + } + field address type string { + indexing: summary | index + } + ... + } +} +``` + +These settings impact both performance and how fields are matched. For example, the *account_number* above is using the *attribute* keyword, which makes the field available for [sorting](/en/reference/querying/sorting-language), [ranking](/en/basics/ranking), [grouping](/en/querying/grouping), but which by default does not have data structures for fast search. Read more in [attributes](/en/content/attributes) and [practical search performance guide](/en/performance/practical-search-performance-guide). + +## Test with local deployment + +To run the steps above, using a local deployment, follow the steps in the [quickstart](/en/basics/deploy-an-application-local) to start a local container running Vespa. Then, deploy the application package from the *bank* folder. diff --git a/en/learn/migrating-to-cloud.md b/en/learn/migrating-to-cloud.md deleted file mode 100644 index 19cae0644d..0000000000 --- a/en/learn/migrating-to-cloud.md +++ /dev/null @@ -1,235 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Migrating to Vespa Cloud" -applies_to: cloud -redirect_from: - - /en/cloud/migrating-to-cloud.html ---- - -Migrating a Vespa application to Vespa Cloud is straightforward, -as applications on Vespa Cloud supports all the same features as your self-hosted Vespa instances, -you're just gaining some new capabilities and avoid the operational work. - -The high-level process is as follows: -1. Functional validation using the [dev](../operations/environments.html#dev) environment (this guide). -2. Deployment to a [prod](../operations/environments.html#prod) zone. - -The rest of this guide assumes you have a [tenant](tenant-apps-instances.html) ready for deployment: - -``` -$ export VESPA_TENANT_NAME=mytenant -``` - -{% include pre-req.html %} -{% include note.html content='[Vespa Cloud Enclave](../operations/enclave/enclave.html) users: Run the enclave setup steps first.'%} - -### 1. Verify source application package -An [application package](../basics/applications.html) from a self-hosted system -can be deployed with minor modifications to the Vespa Cloud `dev` environment. - -The root of an application package might look at this: - -``` -├── schemas -│ └── doc.sd -└── services.xml -``` - -There are often more files, the above is a minimum. -This is the root of the application package - make this the current working directory: - -``` -$ cd /location/of/app/package -``` - - -### 2. Validate the Vespa CLI -Make sure the Vespa CLI is installed, see pre-requisites above: - -``` -$ vespa -Usage: - vespa [flags] - vespa [command] -``` - - -### 3. Vespa Cloud login -Configure the local environment and log in to Vespa Cloud: - -``` -$ vespa config set target cloud && \ - vespa config set application $VESPA_TENANT_NAME.myapp && \ - vespa auth login -``` - - -### 4. Add Credentials to the Application package -Create and get security credentials: - -``` -$ vespa auth cert -``` - -This will add the `security` directory to the application package, -and add a public certificate to it: - -``` -├── schemas -│ └── doc.sd -├── security -│ └── clients.pem -└── services.xml -``` - -The command also installs a key/certificate pair in the Vespa CLI home directory, see -[vespa auth cert](../reference/clients/vespa-cli/vespa_auth_cert.html). -This pair is used in subsequent accesses to the data plane for document and query operations. - - -### 5. Vespa Cloud Enclave Only: Add Account -{% include note.html content='Skip this step unless you are using [Vespa Cloud Enclave](../operations/enclave/enclave.html).'%} - -Add [deployment.xml](../reference/applications/deployment.html#deployment) with your cloud provider account - -This ensures the deployment uses resources from the correct account - examples: -```xml - - - -``` -```xml - - - -``` -The application package should look like: -``` -├── deployment.xml -├── schemas -│ └── doc.sd -├── security -│ └── clients.pem -└── services.xml -``` - - -### 6. Remove hosts.xml -`hosts.xml` is not used in Vespa Cloud, remove it. - - -### 7. Modify services.xml -Edit the `` configuration in `services.xml` - from: -```xml - - - - - - - - - -``` -to: -```xml - - - - - - - - -``` - -In short, this is the Vespa Cloud syntax for resource specifications. - -Example, migrating from a cluster using `c7i.2xlarge` instance type, -with a 200G disk - from the AWS specifications: -``` -c7i.2xlarge 8 16 EBS-Only -``` -Equivalent Vespa Cloud configuration: -```xml - -``` -Repeat this for all clusters in `services.xml`. Notes: - -1. As you are now migrating to the `dev` environment, what is _actually_ deployed is a minimized version. - The configuration changes above are easily tested in this environment. -2. Using `count=2` is best practise at this point. -3. Resources must match a node instance type at the cloud providers(s) deploying to, see - [AWS flavors](https://cloud.vespa.ai/en/reference/aws-flavors.html), - [GCP flavors](https://cloud.vespa.ai/en/reference/gcp-flavors.html), and [Azure flavors](https://cloud.vespa.ai/en/reference/azure-flavors.html). - - -### 8. Deploy to Vespa Cloud Dev Environment -At this point, the local environment and the application package is ready for deployment: - -``` -$ vespa deploy --wait 600 -``` - -Please note that a first-time deployment normally takes a few minutes, -as resources are provisioned. - -At this point, we recommend opening the console to observe the deployed application. -The link will be `https://console.vespa-cloud.com/tenant/mytenant/application/myapp/dev/instance/default` -(replace with your own names) - this is also easily found in the console main page: - -![dev view](/assets/img/free-trial.png) - -Refer to [vespa8 release notes](../reference/release-notes/vespa8.html) for troubleshooting -in case the deployments fails, based on a Vespa 7 (or earlier) version. - - - - - -### 9. Use the Endpoints -The endpoints are shown in the console, one can also list them like: -``` -$ vespa status query -Container default at https://aa1c1234.b225678e.z.vespa-app.cloud/ is ready -``` - -Test the query endpoint, expect `totalCount: 0`: -``` -$ vespa query 'select * from sources * where true' -``` -```json -{ - "root": { - "id": "toplevel", - "relevance": 1.0, - "fields": { - "totalCount": 0 - }, -``` - -In the `services.xml` examples at the start of this guide, -both `` and `` and configured in the same cluster, named `default`. -In case of multiple container clusters, select the one configured with ``: -``` -vespa query 'select * from sources * where true' --cluster myquerycluster -``` - -Finally, feed a document to the cluster (this is the cluster configured with ``) -``` -vespa feed mydoc.jsonl --cluster myfeedcluster -``` - -Redo the query and observe nonzero `totalCount`. - - -## Next steps -This is the final step in the functional validation. Please note: -{% include note.html content='Deployments to `dev` expire after 7 days of inactivity, -i.e., 7 days after the last deployment. -**This applies to all plans**. -Use the Vespa Console to extend the expiry period, or redeploy the application to add 7 more days.' %} - -* Read more about the [dev](../operations/environments.html#dev) environment -* Feed (a subset) of the data and validate that queries and other API accesses work as expected. -* At the end of the validation process, - continue to [production deployment](../operations/production-deployment.html) to set up in production zones. diff --git a/en/learn/migrating-to-cloud.mdx b/en/learn/migrating-to-cloud.mdx new file mode 100644 index 0000000000..8ec6fd9b77 --- /dev/null +++ b/en/learn/migrating-to-cloud.mdx @@ -0,0 +1,261 @@ +--- +title: Migrating to Vespa Cloud +description: "Migrating a Vespa application to Vespa Cloud is straightforward, as applications on Vespa Cloud supports all the same features as your self-hosted Vespa instances, you're just gaining some new capabilities and avoid the operational work." +--- + +The high-level process is as follows: + + + + Functional validation using the [dev](/en/operations/environments#dev) environment (this guide). + + + Deployment to a [prod](/en/operations/environments#prod) zone. + + + +The rest of this guide assumes you have a [tenant](/en/learn/tenant-apps-instances) ready for deployment: + +```bash +$ export VESPA_TENANT_NAME=mytenant +``` + + +**Prerequisites:** + +- Linux, macOS or Windows 10 Pro on x86\_64 or arm64, with [Podman Desktop](https://podman.io/) or [Docker Desktop](https://www.docker.com/products/docker-desktop/) installed, with an engine running. +- Alternatively, start the Podman daemon: + ```bash + $ podman machine init --memory 6000 + $ podman machine start + ``` +- See [Docker Containers](/en/operations/self-managed/docker-containers) for system limits and other settings. +- For CPUs older than Haswell (2013), see [CPU Support](/en/operations/self-managed/cpu-support). +- Memory: Minimum RAM dedicated to Docker/Podman. [Memory recommendations](/en/operations/self-managed/node-setup#memory-settings). +- Disk: Avoid `NO_SPACE` - the vespaengine/vespa container image + headroom for data requires disk space. [Read more](/en/writing/feed-block). +- [Homebrew](https://brew.sh/) to install the [Vespa CLI](/en/clients/vespa-cli), or download the Vespa CLI from [Github releases](https://github.com/vespa-engine/vespa/releases). + + + +**Note:** + +[Vespa Cloud Enclave](/en/operations/enclave/enclave) users: Run the enclave setup steps first. + + + + + An [application package](/en/basics/applications) from a self-hosted system can be deployed with minor modifications to the Vespa Cloud `dev` environment. + + The root of an application package might look at this: + + ```txt + ├── schemas + │ └── doc.sd + └── services.xml + ``` + + There are often more files, the above is a minimum. This is the root of the application package - make this the current working directory: + + ```bash + $ cd /location/of/app/package + ``` + + + + Make sure the [Vespa CLI](/en/clients/vespa-cli) is installed: + + ```bash + $ vespa + Usage: + vespa [flags] + vespa [command] + ``` + + + + Configure the local environment and log in to Vespa Cloud: + + ```bash + $ vespa config set target cloud && \ + vespa config set application $VESPA_TENANT_NAME.myapp && \ + vespa auth login + ``` + + + + Create and get security credentials: + + ```bash + $ vespa auth cert + ``` + + This will add the `security` directory to the application package, and add a public certificate to it: + + ```txt + ├── schemas + │ └── doc.sd + ├── security + │ └── clients.pem + └── services.xml + ``` + + The command also installs a key/certificate pair in the Vespa CLI home directory, see [vespa auth cert](/en/reference/clients/vespa-cli/vespa_auth_cert). This pair is used in subsequent accesses to the data plane for document and query operations. + + + + + **Note:** + + Skip this step unless you are using [Vespa Cloud Enclave](/en/operations/enclave/enclave). + + + Add [deployment.xml](/en/reference/applications/deployment#deployment) with your cloud provider account - This ensures the deployment uses resources from the correct account - examples: + + ```xml + + + + ``` + + ```xml + + + + ``` + + The application package should look like: + + ```txt + ├── deployment.xml + ├── schemas + │ └── doc.sd + ├── security + │ └── clients.pem + └── services.xml + ``` + + + + `hosts.xml` is not used in Vespa Cloud, remove it. + + + + Edit the `` configuration in `services.xml` - from: + + ```xml + + + + + + + + + + ``` + + to: + + ```xml + + + + + + + + + ``` + + In short, this is the Vespa Cloud syntax for resource specifications. + + Example, migrating from a cluster using `c7i.2xlarge` instance type, with a 200G disk - from the AWS specifications: + + ```txt + c7i.2xlarge 8 16 EBS-Only + ``` + + Equivalent Vespa Cloud configuration: + + ```xml + + ``` + + Repeat this for all clusters in `services.xml`. Notes: + + 1. As you are now migrating to the `dev` environment, what is _actually_ deployed is a minimized version. The configuration changes above are easily tested in this environment. + 2. Using `count=2` is best practise at this point. + 3. Resources must match a node instance type at the cloud providers(s) deploying to, see [AWS flavors](/en/performance/instance-types/aws-instance-types), [GCP flavors](/en/performance/instance-types/gcp-instance-types), and [Azure flavors](/en/performance/instance-types/azure-instance-types). + + + + At this point, the local environment and the application package is ready for deployment: + + ```bash + $ vespa deploy --wait 600 + ``` + + Please note that a first-time deployment normally takes a few minutes, as resources are provisioned. + + At this point, we recommend opening the console to observe the deployed application. The link will be `https://console.vespa-cloud.com/tenant/mytenant/application/myapp/dev/instance/default` (replace with your own names) - this is also easily found in the console main page: + + + ![dev view](/assets/img/free-trial.png) + + + Refer to [vespa8 release notes](/en/reference/release-notes/vespa8) for troubleshooting in case the deployments fails, based on a Vespa 7 (or earlier) version. + + + + The endpoints are shown in the console, one can also list them like: + + ```bash + $ vespa status query + Container default at https://aa1c1234.b225678e.z.vespa-app.cloud/ is ready + ``` + + Test the query endpoint, expect `totalCount: 0`: + + ```bash + $ vespa query 'select * from sources * where true' + ``` + + ```json + { + "root": { + "id": "toplevel", + "relevance": 1.0, + "fields": { + "totalCount": 0 + }, + ``` + + In the `services.xml` examples at the start of this guide, both `` and `` and configured in the same cluster, named `default`. In case of multiple container clusters, select the one configured with ``: + + ```bash + vespa query 'select * from sources * where true' --cluster myquerycluster + ``` + + Finally, feed a document to the cluster (this is the cluster configured with ``) + + ```bash + vespa feed mydoc.jsonl --cluster myfeedcluster + ``` + + Redo the query and observe nonzero `totalCount`. + + + +## Next steps + +This is the final step in the functional validation. Please note: + + +**Note:** + +Deployments to `dev` expire after 7 days of inactivity, i.e., 7 days after the last deployment. **This applies to all plans**. Use the Vespa Console to extend the expiry period, or redeploy the application to add 7 more days. + + +- Read more about the [dev](/en/operations/environments#dev) environment +- Feed (a subset) of the data and validate that queries and other API accesses work as expected. +- At the end of the validation process, continue to [production deployment](/en/operations/production-deployment) to set up in production zones. diff --git a/en/learn/overview.html b/en/learn/overview.html deleted file mode 100644 index 7a6be2dd95..0000000000 --- a/en/learn/overview.html +++ /dev/null @@ -1,201 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Vespa Overview" -redirect_from: -- /en/overview.html ---- - -

    Vespa is a platform for applications which need low-latency computation over large data sets. -It stores and indexes your structured, text and vector data so that queries, selection and processing -and machine-learned model inference over the data can be performed quickly at serving time at any scale. -Functionality can be customized and extended with application components hosted within Vespa. -This document is an overview of the features and main components of Vespa.

    - - -

    Introduction

    - -

    Vespa allows application developers to create applications that scale -to large amounts of data and high loads without sacrificing latency or reliability. -A Vespa application consists of a number of stateless Java container clusters -and zero or more content clusters storing data.

    - -Vespa Overview - -

    The stateless container clusters host components -which process incoming data and/or queries and their responses. -These components provide functionality belonging to the platform like indexing -transformations and the global stages of query execution, but can also include the middleware logic of the application. -Application developers can configure their Vespa system with a single stateless cluster which -performs all such functions, or create different clusters for each kind of task. -The container clusters then pass queries and data operations on to the appropriate nodes in the content clusters. -If the application uses data it does not own, you can add components to access data from external services as well.

    - -

    Content clusters in Vespa are responsible for storing data -and execute queries and inferences over the data. -Queries can range from simple data lookups for content serving to complex conditions for selecting the relevant -data, ranking it using machine-learned models, and grouping and aggregating the data across all nodes -participating in the query. -All the operations provided by Vespa scales to more content, more expensive inference, and higher query volume -simply by adding more nodes to the content clusters.

    - -

    When changing the nodes of a content cluster for scaling or on node failure, -content clusters automatically re-balance data in the background -to maintain a balanced distribution at the configured redundancy level. -Faulty nodes are also automatically removed from the serving path to avoid any impact to queries and -writes (failover).

    - -

    After intermediate processing in a container cluster, data is written to content clusters. -Writes are persistent and visible in all queries after receiving an ack on the write message, -after a few milliseconds. Each write is guaranteed to either succeed or provide failure information -response within a given time limit, and writes and scale linearly with the available resources, indefinitely. -In addition to rewriting and removing entire documents, writes may selectively modify only individual document fields. -Writes can be sent directly over HTTP/2, or by using a Java client — -refer to the API documentation.

    - -

    Each document instance stored in Vespa are of a type defined in a configured schema, -which defines the document fields and how to store and index them, as well as the ranking and inference -profiles that belongs to the document type. Applications can contain any number of schemas for different -data types, and configure them to be stored either in the same or multiple content clusters.

    - -

    Container and content clusters handle all the end user traffic of a Vespa application, -but there's also a third type of cluster, the admin and config clusters. -These set up and manage the other clusters in the application according to configuration, and -manages the process of changing the clusters safely without disruption to traffic -when the configuration changed.

    - -

    A Vespa application is completely specified by an -application package, -which is a directory structure containing a declaration of the clusters to run as part of the application, -the content schemas, any machine-learned models and Java components, -and other configuration or data files needed by various features. -Application developers create a running application from their application package by -deploying it to any node in the config cluster. -Changes to a running application is made in the same way: By changing the application package and deploying again. -Once Vespa is installed and started on a node, -it is managed by the config system such that the entire system can be treated as a single unit, -and application owners do not need to perform any administration tasks locally on the nodes running the application. -It is also possible to configure nodes as log servers on Vespa. These will collect logs in real time -from all the nodes of the application. By default, the first node in the config server cluster performs this role.

    - -

    The rest of this document provides some more detail on the functions Vespa performs.

    - - -

    Vespa operations

    - -

    Vespa accepts the following operations:

    - -
      -
    • Writes: Put (add and replace) and remove documents, and update fields in these.
    • -
    • Lookup of a document (or some subset of it) by id.
    • -
    • Queries: - Select documents - whose fields match conditions, which search - free-text fields, structured data or vector spaces (ANN). - Any number of such conditions can be combined freely in boolean trees to - define the full query to be executed. - Vespa will compute a query plan over the conditions which executes them - efficiently with any number of conditions such as e.g. filters combined with ANN conditions. - Matches to a query can be passed through an inference step which can compute - any business logic or machine-learned model expressed as a - ranking expression or ONNX model. - Optionally, the highest scoring matches can also run through a second stage of this, - to spend more computational resources on promising candidates. - The final documents are ordered according to their score from these inferences (ranking), - or by explicit sorting. - Matches to queries can be grouped - hierarchically by field values, - where each group can contain aggregated values over the data in the group - This can be used to calculate values for, e.g., navigation aids, tag clouds, - graphs or for clustering in a distributed fashion - without having to transfer the distributed to a single container node.
    • -
    • Data dumps: Content matching some criterion can be streamed out for background reprocessing, - backup, etc., by using the visit operation.
    • -
    • Any other custom network request - which can be handled by application components deployed on a container cluster.
    • -
    - - -

    The stateless container

    - -

    Container clusters host the application components -which employ the operations listed above and process their return data. -Vespa provides a set of components out of the box, together with component infrastructure: -dependency injection, -with added support for injection of config from the admin server or the application package; -a component model based on OSGi; -a shared mechanism to chain components into handler chains for modularity -as well as metrics and logging. -The container also provides the network layer for handling and issuing remote requests - -HTTP is provided out of the box, -and other protocols/transports can be transparently plugged in as components.

    - -

    Developers can make changes to components (and of course their configuration) -simply by redeploying their application package - -the system takes care of copying the components to the nodes of the cluster -and loading/unloading components impacting request serving or restarting nodes.

    - - - -

    Content clusters

    - -

    Content clusters store data -and maintain distributed indices of data for searches and selects. -Data is replicated over multiple nodes, with a number of copies specified by the application, -such that the cluster can automatically repair itself on loss of a node or a disk. -Using the same mechanism, clusters can also be grown or shrunk while online, -simply by changing the set of available nodes declared in the application package.

    - -

    Lookup of an individual document is routed directly to a node storing that document, -while queries are spread over a subset of nodes which contain the queried documents. -Complex queries are performed as distributed algorithms with multiple steps back and -forth between the container and the content nodes; this is to achieve the low latency -which is one of the main design goals of Vespa.

    - - - -

    Administration clusters and developer support

    - -

    The single configuration cluster -controls all the other clusters of the system. - -A config server derives the low level configuration of each individual cluster, node and process, -such that the application developer can specify the desired system on a higher level -without worrying about its detailed realization. -Whenever the application package is redeployed, -the system will compute the necessary changes in configuration and manage the process -of moving safely from the current to the new configuration without disrupting queries or writes.

    - -Other admin clusters in Vespa are the cluster controller cluster (controls one or more content clusters), -logserver cluster (logserver holds log archive for logs from all nodes in the application) -and service location brokers (slobroks, which are a name service used by some services in Vespa). - -

    Application packages

    -

    Application packages may be changed, redeployed -and inspected over an HTTP REST API, -or through a command line interface. -The administration cluster runs over ZooKeeper -to make changes to configuration singular and consistent, and to avoid having a single point of failure.

    - -

    An application package looks the same, and is deployed the same way, -whether it specifies a large system with hundreds of nodes or a single node running all services. -The only change needed is to the lists of nodes making up the cluster. -The container clusters may also be started within a single Java VM by "deploying" the -application package from a method call. -This is useful for testing applications in an IDE and in unit tests. -Application packages with components can be developed -in an IDE using Maven starting from sample applications.

    - - - -

    Summary

    - -

    Vespa allows functionally rich and highly available applications to be developed to scale and perform to -high standards without burdening developers with the considerable low level complexity this requires. -It allows developers to evolve and grow their applications over time without taking the system offline, -and lets them avoid complex data and page precomputing schemes -which lead to stale data that cannot be personalized, -since this often requires complex queries to complete in real user time -over data which is constantly changing at the same time.

    - -

    For more details, read Vespa Features, or try to -deploy an application.

    diff --git a/en/learn/overview.mdx b/en/learn/overview.mdx new file mode 100644 index 0000000000..164b96bada --- /dev/null +++ b/en/learn/overview.mdx @@ -0,0 +1,70 @@ +--- +title: Vespa Overview +description: "Vespa is a platform for applications which need low-latency computation over large data sets. It stores and indexes your structured, text and vector data so that queries, selection and processing and machine-learned model inference over the data can be performed quickly at serving time at any scale. Functionality can be customized and extended with application components hosted within Vespa. This document is an overview of the features and main components of Vespa." +--- + +## Introduction + +Vespa allows application developers to create applications that scale to large amounts of data and high loads without sacrificing latency or reliability. A Vespa application consists of a number of *stateless Java container clusters* and zero or more *content* clusters storing data. + + +![Vespa Overview](/assets/img/vespa-overview.svg) + + +The [stateless **container** clusters](/en/applications/containers) host components which process incoming data and/or queries and their responses. These components provide functionality belonging to the platform like indexing transformations and the global stages of query execution, but can also include the middleware logic of the application. Application developers can configure their Vespa system with a single stateless cluster which performs all such functions, or create different clusters for each kind of task. The container clusters then pass queries and data operations on to the appropriate nodes in the content clusters. If the application uses data it does not own, you can add components to access data from external services as well. + +[**Content** clusters](/en/content/elasticity) in Vespa are responsible for storing data and execute queries and inferences over the data. Queries can range from simple data lookups for content serving to complex conditions for selecting the relevant data, ranking it using machine-learned models, and grouping and aggregating the data across all nodes participating in the query. All the operations provided by Vespa scales to more content, more expensive inference, and higher query volume simply by adding more nodes to the content clusters. + +When changing the nodes of a content cluster for scaling or on node failure, content clusters automatically re-balance data in the background to maintain a balanced distribution at the configured redundancy level. Faulty nodes are also automatically removed from the serving path to avoid any impact to queries and writes (failover). + +After intermediate processing in a container cluster, data is written to content clusters. Writes are persistent and visible in all queries after receiving an ack on the write message, after a few milliseconds. Each write is guaranteed to either succeed or provide failure information response within a given time limit, and writes and scale linearly with the available resources, indefinitely. In addition to rewriting and removing entire documents, writes may selectively modify only individual document fields. Writes can be sent directly over HTTP/2, or by using a Java client — refer to the [API documentation](/en/reference/api/api). + +Each document instance stored in Vespa are of a type defined in a configured [schema](/en/basics/schemas), which defines the document fields and how to store and index them, as well as the ranking and inference profiles that belongs to the document type. Applications can contain any number of schemas for different data types, and configure them to be stored either in the same or multiple content clusters. + +Container and content clusters handle all the end user traffic of a Vespa application, but there's also a third type of cluster, the *admin and config clusters*. These set up and manage the other clusters in the application according to configuration, and manages the process of changing the clusters safely without disruption to traffic when the configuration changed. + +A Vespa application is completely specified by an [*application package*](/en/basics/applications), which is a directory structure containing a declaration of the clusters to run as part of the application, the content schemas, any machine-learned models and Java components, and other configuration or data files needed by various features. Application developers create a running application from their application package by *deploying* it to any node in the config cluster. Changes to a running application is made in the same way: By changing the application package and deploying again. Once Vespa is installed and started on a node, it is managed by the config system such that the entire system can be treated as a single unit, and application owners do not need to perform any administration tasks locally on the nodes running the application. It is also possible to configure nodes as *log servers* on Vespa. These will collect logs in real time from all the nodes of the application. By default, the first node in the config server cluster performs this role. + +The rest of this document provides some more detail on the functions Vespa performs. + +## Vespa operations + +Vespa accepts the following operations: + +- Writes: Put (add and replace) and remove documents, and update fields in these. +- Lookup of a document (or some subset of it) by id. +- [Queries](/en/querying/query-api): [*Select*](/en/querying/query-language) documents whose fields match conditions, which search free-text fields, structured data or [vector spaces (ANN)](/en/querying/nearest-neighbor-search). Any number of such conditions can be combined freely in boolean trees to define the full query to be executed. Vespa will compute a query plan over the conditions which executes them efficiently with any number of conditions such as e.g. filters combined with ANN conditions. Matches to a query can be passed through an inference step which can compute any business logic or machine-learned model expressed as a [ranking expression](/en/reference/ranking/ranking-expressions) or [ONNX model](/en/ranking/onnx). Optionally, the highest scoring matches can also run through a second stage of this, to spend more computational resources on promising candidates. The final documents are ordered according to their score from these inferences ([*ranking*](/en/basics/ranking)), or by explicit [*sorting*](/en/reference/querying/sorting-language). Matches to queries can be [*grouped*](/en/querying/grouping) hierarchically by field values, where each group can contain aggregated values over the data in the group This can be used to calculate values for, e.g., navigation aids, tag clouds, graphs or for clustering in a distributed fashion without having to transfer the distributed to a single container node. +- Data dumps: Content matching some criterion can be streamed out for background reprocessing, backup, etc., by using the [*visit*](/en/writing/visiting) operation. +- [Any other custom network request](/en/reference/applications/components) which can be handled by application components deployed on a container cluster. + +## The stateless container + +[Container clusters](/en/applications/containers) host the application components which employ the operations listed above and process their return data. Vespa provides a set of components out of the box, together with component infrastructure: dependency injection, with added support for injection of config from the admin server or the application package; a component model based on OSGi; a shared mechanism to chain components into handler chains for modularity as well as metrics and logging. The container also provides the network layer for handling and issuing remote requests - HTTP is provided out of the box, and other protocols/transports can be transparently plugged in as components. + +Developers can make changes to components (and of course their configuration) simply by redeploying their application package - the system takes care of copying the components to the nodes of the cluster and loading/unloading components impacting request serving or restarting nodes. + +## Content clusters + +[Content clusters](/en/content/elasticity) store data and maintain distributed indices of data for searches and selects. Data is replicated over multiple nodes, with a number of copies specified by the application, such that the cluster can automatically repair itself on loss of a node or a disk. Using the same mechanism, clusters can also be grown or shrunk while online, simply by changing the set of available nodes declared in the application package. + +Lookup of an individual document is routed directly to a node storing that document, while queries are spread over a subset of nodes which contain the queried documents. Complex queries are performed as distributed algorithms with multiple steps back and forth between the container and the content nodes; this is to achieve the low latency which is one of the main design goals of Vespa. + +## Administration clusters and developer support + +The [single configuration cluster](/en/basics/applications) controls all the other clusters of the system. + +A config server derives the low level configuration of each individual cluster, node and process, such that the application developer can specify the desired system on a higher level without worrying about its detailed realization. Whenever the application package is redeployed, the system will compute the necessary changes in configuration and manage the process of moving safely from the current to the new configuration without disrupting queries or writes. + +Other admin clusters in Vespa are the cluster controller cluster (controls one or more content clusters), logserver cluster (logserver holds log archive for logs from all nodes in the application) and service location brokers (slobroks, which are a name service used by some services in Vespa). + +### Application packages + +Application packages may be [changed, redeployed](/en/reference/api/deploy-v2) and [inspected](/en/reference/api/config-v2) over an HTTP REST API, or through a [command line interface](/en/clients/vespa-cli#deployment). The administration cluster runs over [ZooKeeper](https://zookeeper.apache.org) to make changes to configuration singular and consistent, and to avoid having a single point of failure. + +An application package looks the same, and is deployed the same way, whether it specifies a large system with hundreds of nodes or a single node running all services. The only change needed is to the lists of nodes making up the cluster. The container clusters may also be started within a single Java VM by "deploying" the application package from a method call. This is useful for testing applications in an IDE and in unit tests. Application packages with components can be [developed](/en/applications/developer-guide) in an IDE using Maven starting from sample applications. + +## Summary + +Vespa allows functionally rich and highly available applications to be developed to scale and perform to high standards without burdening developers with the considerable low level complexity this requires. It allows developers to evolve and grow their applications over time without taking the system offline, and lets them avoid complex data and page precomputing schemes which lead to stale data that cannot be personalized, since this often requires complex queries to complete in real user time over data which is constantly changing at the same time. + +For more details, read [Vespa Features](/en/learn/features), or try to [deploy an application](/en/basics/deploy-an-application). diff --git a/en/learn/releases.html b/en/learn/releases.html deleted file mode 100644 index 11332de1a5..0000000000 --- a/en/learn/releases.html +++ /dev/null @@ -1,78 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Releases" -redirect_from: -- /en/releases.html ---- - -

    - Vespa is released every Monday through Thursday. Each public release has passed all - functional and performance tests, and all cloud applications are automatically upgraded to it. -

    -
    -

    - For each Vespa release, the following artifacts are provided: -

    - -
    -

    - Releases: -

    - -
    -

    - Use the Vespa Factory - to inspect the commits in each release: -

    -Screenshot of commit list diff per release - - -

    Versions

    -

    - Vespa uses semantic versioning. - Each release is backwards compatible and supports live migration on running systems, provided they - are running a version which is less than 2 months old. - It is therefore a minor version number change. All new features are released on such minor versions. - Every second year or so we make a major version change which removes previously deprecated - functionality. -

    -

    - Java APIs, web service APIs and all application package constructs are supported through a major release - and only removed on a new release if they are already marked deprecated. -

    -

    - Use of deprecated Java APIs will cause a warning on compilation, - and use of deprecated application package constructs will cause a deprecation warning on deployment. - Note that Java APIs come in two categories: -

    -
      -
    • Public APIs carry the compatibility guarantee and are visible from your code - as well as in the javadoc
    • -
    • Exported APIs are also visible from your code, - but is not in the public Javadoc and carry no compatibility guarantee
    • -
    -

    - Check the Javadoc list to verify that you are using public packages. -

    -

    - In addition, some public Java classes and methods are marked with the com.yahoo.api.annotations.Beta tag. - These are under development and may still change before they stabilize. -

    - - - -

    Stored Data

    - -

    - Data written to Vespa is compatible between adjacent releases. - For self-hosted systems, it may be necessary to upgrade through each - minor release rather than in larger leaps to ensure Vespa can read existing data. - This is a good practice in any case. -

    diff --git a/en/learn/releases.mdx b/en/learn/releases.mdx new file mode 100644 index 0000000000..08cb079a2d --- /dev/null +++ b/en/learn/releases.mdx @@ -0,0 +1,40 @@ +--- +title: Releases +description: "Vespa is released every Monday through Thursday. Each public release has passed all functional and performance tests, and all cloud applications are automatically upgraded to it." +--- + +For each Vespa release, the following artifacts are provided: + +- [Java artifacts for building Vespa applications on Maven Central](https://search.maven.org/artifact/com.yahoo.vespa/parent) +- [Vespa RPMs on Fedora Copr](https://copr.fedorainfracloud.org/coprs/g/vespa/vespa/) +- [Container images on Docker Hub](https://hub.docker.com/repository/docker/vespaengine/vespa) + +Releases: + +- [Vespa 7](/en/reference/release-notes/vespa7) +- [Vespa 8](/en/reference/release-notes/vespa8) + +Use the [Vespa Factory](https://factory.vespa.ai/releases) to inspect the commits in each release: + + +![Screenshot of commit list diff per release](/assets/commits-release.png) + + +## Versions + +Vespa uses [semantic versioning](https://semver.org/). Each release is backwards compatible and supports live migration on running systems, provided they are running a version which is less than 2 months old. It is therefore a minor version number change. All new features are released on such minor versions. Every second year or so we make a major version change which removes previously deprecated functionality. + +Java APIs, web service APIs and all application package constructs are supported through a major release and only removed on a new release if they are already marked deprecated. + +Use of deprecated Java APIs will cause a warning on compilation, and use of deprecated application package constructs will cause a deprecation warning on deployment. Note that Java APIs come in two categories: + +- *Public APIs* carry the compatibility guarantee and are visible from your code as well as in the javadoc +- *Exported APIs* are also visible from your code, but is not in the public Javadoc and carry no compatibility guarantee + +Check the Javadoc list to verify that you are using public packages. + +In addition, some public Java classes and methods are marked with the com.yahoo.api.annotations.Beta tag. These are under development and may still change before they stabilize. + +## Stored Data + +Data written to Vespa is compatible between adjacent releases. For self-hosted systems, it may be necessary to upgrade through each minor release rather than in larger leaps to ensure Vespa can read existing data. This is a good practice in any case. diff --git a/en/learn/tenant-apps-instances.html b/en/learn/tenant-apps-instances.html deleted file mode 100644 index a6c940c14e..0000000000 --- a/en/learn/tenant-apps-instances.html +++ /dev/null @@ -1,58 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: Tenants, Applications and Instances -applies_to: cloud -redirect_from: -- /en/cloud/tenant-apps-instances.html ---- - -

    - When registering for Vespa Cloud, a tenant is created. - Tenant is the billable unit, and most often represents an organization or a project. - A tenant has one or more applications with one or more instances: -

    -A tenant can have multiple applications with multiple instances each -

    - Instances are used for different use cases, - and are deployed to a set of zones - example: -

    -An application can be deployed to multiple zones -

    - The Application has a "default" instance serving queries from two production zones. - It has an "integration" instance with another dataset, used for other applications to interface a - production-like, stable interface. - Finally, a developer has deployed the "bob" instance to a dev zone to further develop plugin code. -

    -

    - Deployments to production zones are specified in deployment.xml. - Deployments to the manual dev zones are normally done directly from a developer computer for rapid code and config development. - Read more in Automated deployments. -

    -

    - The service configuration is specified in services.xml - and is composed of individually sized clusters. - A cluster is deployed to a set of nodes with resources specified. -

    -

    - One or more users may be a member of the tenant. A user is given roles in the tenant based on their access level. - Administrator for tenant level managment like adding new members and updating billing data, - Developer for managing applications, and one for read-only access. -

    - - - -

    Lifecycle

    -

    - The tenant name cannot be changed - create a new tenant, or contact Vespa Support. -

    -

    - Tenants in trial are auto-expired once trial is completed. - Move to a paid plan to keep applications and data. -

    -

    - It is not possible to auto-migrate applications and data between tenants. - To move an application to a new tenant, re-deploy the application with the new tenant name, - see cloning applications and data. -

    diff --git a/en/learn/tenant-apps-instances.mdx b/en/learn/tenant-apps-instances.mdx new file mode 100644 index 0000000000..c458b86474 --- /dev/null +++ b/en/learn/tenant-apps-instances.mdx @@ -0,0 +1,30 @@ +--- +title: Tenants, Applications and Instances +description: "When registering for Vespa Cloud, a tenant is created. Tenant is the billable unit, and most often represents an organization or a project. A tenant has one or more applications with one or more instances." +--- + + +![A tenant can have multiple applications with multiple instances each](/assets/img/tenants-apps-instances.svg) + + +Instances are used for different use cases, and are deployed to a set of [zones](/en/operations/zones) - example: + + +![An application can be deployed to multiple zones](/assets/img/instances-zones.svg) + + +The *Application* has a "default" instance serving queries from two *production* zones. It has an "integration" instance with another dataset, used for other applications to interface a production-like, stable interface. Finally, a developer has deployed the "bob" instance to a *dev* zone to further develop plugin code. + +Deployments to production zones are specified in [deployment.xml](/en/reference/applications/deployment). Deployments to the manual *dev* zones are normally done directly from a developer computer for rapid code and config development. Read more in [Automated deployments](/en/operations/automated-deployments). + +The service configuration is specified in [services.xml](/en/reference/applications/services/services) and is composed of individually sized *clusters*. A cluster is deployed to a set of *nodes* with *resources* specified. + +One or more users may be a member of the tenant. A user is given roles in the tenant based on their access level. *Administrator* for tenant level managment like adding new members and updating billing data, *Developer* for managing applications, and one for *read-only* access. + +## Lifecycle + +The tenant name cannot be changed - create a new tenant, or contact Vespa Support. + +Tenants in trial are auto-expired once trial is completed. Move to a paid plan to keep applications and data. + +It is not possible to auto-migrate applications and data between tenants. To move an application to a new tenant, re-deploy the application with the new tenant name, see [cloning applications and data](/en/operations/cloning). diff --git a/en/learn/tutorials.mdx b/en/learn/tutorials.mdx new file mode 100644 index 0000000000..2a7188ff76 --- /dev/null +++ b/en/learn/tutorials.mdx @@ -0,0 +1,64 @@ +--- +title: Tutorials and use cases +--- + +### Text search + +- [Tutorial: Text Search](/en/learn/tutorials/text-search) A text search tutorial and introduction to text ranking with Vespa using traditional information retrieval techniques like BM25. +- [Tutorial: Improving Text Search with Machine Learning](/en/learn/tutorials/text-search-ml) This tutorial builds on the text search tutorial but introduces Learning to Rank to improve relevance. + + +### Vector Search + +Learn how to use Vespa Vector Search in the [practical nearest neighbor search guide](/en/querying/nearest-neighbor-search-guide). It uses Vespa's support for [nearest neighbor search](/en/querying/nearest-neighbor-search), there is also support for fast [approximate nearest neighbor search](/en/querying/approximate-nn-hnsw) in Vespa. The guide covers combining vector search with filters and how to perform hybrid search, combining retrieval over inverted index structures with vector search. + +### Hybrid Search + +[Tutorial: Hybrid Text Search](/en/learn/tutorials/hybrid-search) A search tutorial and introduction to hybrid text ranking with Vespa, combining BM25 with text embedding models. + +### RAG (Retrieval-Augmented Generation) + +- [Tutorial: The RAG Blueprint](/en/learn/tutorials/rag-blueprint) A tutorial that provides a blueprint for building high-quality RAG applications with Vespa. Includes evaluation and learning-to-rank (LTR). +- [Retrieval-augmented generation (RAG) in Vespa](/en/rag/rag) + +### Combining search and recommendation: The News tutorial + +Follow this series to learn how to build a complete application supporting both content recommendation/personalization, navigation, and search. + +- [News 1: Getting Started](/en/learn/tutorials/news-1-deploy-an-application) +- [News 2: Application Packages, Feeding, Query](/en/learn/tutorials/news-2-basic-feeding-and-query) +- [News 3: Sorting, Grouping and Ranking](/en/learn/tutorials/news-3-searching) +- [News 4: Embeddings](/en/learn/tutorials/news-4-embeddings) +- [News 5: Partial Updates, ANNs, Filtering](/en/learn/tutorials/news-5-recommendation) +- [News 6: Custom Searchers, Document Processors](/en/learn/tutorials/news-6-recommendation-with-searchers) +- [News 7: Parent-Child, Tensor Ranking](/en/learn/tutorials/news-7-recommendation-with-parent-child) + +### ML Model Serving + +Learn how to use Vespa for ML model serving in [Stateless Model Evaluation](/en/ranking/stateless-model-evaluation.html). Vespa supports running inference with models from many popular ML frameworks, which can be used for ranking, query classification, question answering, multi-modal retrieval, and more. + +- [Ranking with ONNX models](/en/ranking/onnx) Export models from popular deep learning frameworks such as PyTorch to ONNX format for serving in Vespa. Vespa integrates with ONNX-Runtime for accelerated inference. +- [Ranking with LightGBM models](/en/ranking/lightgbm) +- [Ranking with XGBoost models](/en/ranking/xgboost) +- [Ranking with TensorFlow models](/en/ranking/tensorflow) + +### Embedding Model Inference + +Vespa supports integrating [embedding](/en/rag/embedding) models, which avoids transferring large amounts of embedding vector data over the network and allows for efficient serving of embedding models. + +- [Huggingface Embedder](/en/rag/embedding) Use single-vector embedding models from Hugging Face. +- [ColBERT Embedder](/en/rag/embedding) Use multi-vector embedding models. +- [Splade Embedder](/en/rag/embedding) Use sparse learned single vector embedding models. + + +### E-Commerce + +The [e-commerce shopping sample application](/en/learn/tutorials/e-commerce) demonstrates Vespa grouping, true in-place partial updates, custom ranking, and more. + +### Building a custom HTTP API + +The [HTTP API tutorial](/en/learn/tutorials/http-api) shows how to build a custom HTTP API in an application. + +### More examples and sample applications + +There are many examples and starting applications on [GitHub](https://github.com/vespa-engine/sample-apps/) and [Pyvespa examples](https://vespa-engine.github.io/pyvespa/index.html). diff --git a/en/learn/tutorials/e-commerce.md b/en/learn/tutorials/e-commerce.md deleted file mode 100644 index 98b82e4e84..0000000000 --- a/en/learn/tutorials/e-commerce.md +++ /dev/null @@ -1,149 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Use Case - shopping" -redirect_from: - - /en/use-case-shopping ---- - -The [e-commerce, or shopping, use case](https://github.com/vespa-engine/sample-apps/tree/master/use-case-shopping) -is an example of an e-commerce site complete with sample data and a web front -end to browse product data and reviews. To quick start the application, follow -the instructions in the -[README](https://github.com/vespa-engine/sample-apps/blob/master/use-case-shopping/README.md) -in the sample app. - -Shopping sample app screenshot - -To browse the application, navigate to -localhost:8080/site. -This site is implemented through a custom [request handler](../../applications/request-handlers.html) -and is meant to be a simple example of creating a front end / middleware that -sits in front of the Vespa back end. As such it is fairly independent of Vespa -features, and the code is designed to be fairly easy to follow and as -non-magical as possible. All the queries against Vespa are sent as HTTP -requests, and the JSON results from Vespa are parsed and rendered. - -This sample application is built around the Amazon product data set found at -https://cseweb.ucsd.edu/~jmcauley/datasets.html. -A small sample of this data is included in the sample application, and full -data sets are available from the above site. This sample application contains -scripts to convert from the data set format to Vespa format: -[convert_meta.py](https://github.com/vespa-engine/sample-apps/blob/master/use-case-shopping/convert_meta.py) and -[convert_reviews.py](https://github.com/vespa-engine/sample-apps/blob/master/use-case-shopping/convert_reviews.py). -See [README](https://github.com/vespa-engine/sample-apps/tree/master/use-case-shopping#readme) for example use. - -When feeding reviews, there is a custom [document processor](../../applications/document-processors.html) -that intercepts document writes and updates the parent item with the review rating, -so the aggregated review rating is kept stored with the item - -see [ReviewProcessor](https://github.com/vespa-engine/sample-apps/blob/master/use-case-shopping/src/main/java/ai/vespa/example/shopping/ReviewProcessor.java). -This is more an example of a custom document processor than a recommended way to do this, -as feeding the reviews more than once will result in inflated values. -To do this correctly, one should probably calculate this offline so a re-feed does not cause unexpected results. - - - -### Highlighted features - -* [Multiple document types](../../basics/schemas.html) - - Vespa models data as documents, which are configured in schemas - that defines how documents should be stored, indexed, ranked, and searched. - In Vespa, you can have multiple documents types, which can be defined in - `services.xml` how these should be distributed around the content clusters. - This application uses three document types that are stored in the same - content cluster: item, review and query. Search is done on items, but reviews - refer to a single parent item and are rendered on the item page. The query - document type is used to power auto-suggest functionality. - -* [Custom document processor](../../applications/document-processors.html) - - In Vespa, you can set up custom document processors to perform any type of - extra processing during document feeding. One example is to enrich the - document with extra information, and another is to precalculate values of - fields to avoid unnecessary computation during ranking. This application - uses a document processor to intercept reviews and update the parent item's - review rating. - -* [Custom searcher processor](../../applications/searchers.html) - - In Vespa, you can set up custom searchers to perform any type of - extra processing during querying. - In the sample app there is a single custom searcher which builds the query for auto-suggestions, - using a combination of [fuzzy matching](../../reference/querying/yql.html#fuzzy) - and [prefix search](../../querying/text-matching.html#prefix-match). - -* [Custom handlers](../../applications/request-handlers.html) - - With Vespa, you can set up general request handlers to handle any type of request. - This example site is implemented with a single such request handler, - [SiteHandler](https://github.com/vespa-engine/sample-apps/blob/master/use-case-shopping/src/main/java/ai/vespa/example/shopping/site/SiteHandler.java) - which is set up in - [services.xml](https://github.com/vespa-engine/sample-apps/blob/master/use-case-shopping/src/main/application/services.xml) - to be bound to `/site`. - Note that this handler is for example purposes and is designed to be independent of Vespa. - Most applications would serve this through a dedicated setup. - -* [Custom configuration](../../applications/configuring-components.html) - - When creating custom components in Vespa, for instance document processors, - searchers or handlers, one can use custom configuration to inject config - parameters into the components. This involves defining a config definition - (a `.def` file), which creates a config class. You can instantiate this - class with data in `services.xml` and the resulting object is dependency - injected to the component during construction. This application uses custom - config to set up the Vespa host details for the handler. - -* [Partial update](../../reference/schemas/document-json-format.html#update) - - With Vespa, you can make changes to an existing document without submitting - the full document. Examples are setting the value of a single field, adding - elements to an array, or incrementing the value of a field without knowing - the field value beforehand. This application contains an example of a - partial update, in the voting of whether a review is helpful or not. The - `SiteHandler` receives the request and the `ReviewVote` class sends a - partial update to increment the `up`- or `downvotes` field. - -* [Search using YQL](../../querying/query-language.html) - - In Vespa, you search for documents using YQL. In this application, the - classes responsible for retrieving data from Vespa (in the `data` package - beneath the `SiteHandler`) set up the YQL queries which are used to query - Vespa over HTTP. - -* [Grouping](../../querying/grouping.html) - - Grouping is used to group various fields of query results together. For - this application, many of the queries to Vespa include grouping requests. - The home page uses grouping to dynamically extract the first 3 levels of - categories from the stored items. The search page groups results matching - the query into categories, brands, item rating and price ranges. The order - which the groups are rendered are determined by both counting and the - relevance of the hits. This enables query-contextualized navigation. - -* [Rank profiles](../../basics/ranking.html) - - Rank profiles are profiles containing instructions on how to score - documents for a given query. The most important part of rank profiles are - the ranking expressions. The schemas for the item and review - document types contain different rank profiles to sort or score the - data. The item ranking is using a hybrid combination of keyword and vector matching. - -* [Native embedders](../../rag/embedding.html) - - Native embedders are used to map the textual query and document representations - into dense high dimensional vectors which are used for semantic search. The application - uses an open-source embedding model and inference is performed using - [stateless model evaluation](../../ranking/stateless-model-evaluation.html), both during - document and query processing. - -* [Vector search](../../querying/nearest-neighbor-search) - - The default retrieval uses approximate nearest neighbor search in combination with traditional - lexical matching. Both the keyword and vector matching is constrained by the filters such as brand, price or - category. - -* [Ranking functions](../../reference/schemas/schemas.html#function-rank) - - Ranking functions are contained in rank profiles and can be referenced - as part of any ranking expression from either first-phase, second-phase, global-phase or - other functions. diff --git a/en/learn/tutorials/e-commerce.mdx b/en/learn/tutorials/e-commerce.mdx new file mode 100644 index 0000000000..7bc18f1219 --- /dev/null +++ b/en/learn/tutorials/e-commerce.mdx @@ -0,0 +1,69 @@ +--- +title: "Use Case - shopping" +--- + +The [e-commerce, or shopping, use case](https://github.com/vespa-engine/sample-apps/tree/master/use-case-shopping) is an example of an e-commerce site complete with sample data and a web front end to browse product data and reviews. To quick start the application, follow the instructions in the [README](https://github.com/vespa-engine/sample-apps/blob/master/use-case-shopping/README.md) in the sample app. + + + +![Shopping sample app screenshot](/assets/img/shopping-1.png) + + + +To browse the application, navigate to [localhost:8080/site](http://localhost:8080/site). This site is implemented through a custom [request handler](/en/applications/request-handlers) and is meant to be a simple example of creating a front end / middleware that sits in front of the Vespa back end. As such it is fairly independent of Vespa features, and the code is designed to be fairly easy to follow and as non-magical as possible. All the queries against Vespa are sent as HTTP requests, and the JSON results from Vespa are parsed and rendered. + +This sample application is built around the Amazon product data set found at [https://cseweb.ucsd.edu/~jmcauley/datasets.html](https://cseweb.ucsd.edu/~jmcauley/datasets.html). A small sample of this data is included in the sample application, and full data sets are available from the above site. This sample application contains scripts to convert from the data set format to Vespa format: [convert_meta.py](https://github.com/vespa-engine/sample-apps/blob/master/use-case-shopping/convert_meta.py) and [convert_reviews.py](https://github.com/vespa-engine/sample-apps/blob/master/use-case-shopping/convert_reviews.py). See [README](https://github.com/vespa-engine/sample-apps/tree/master/use-case-shopping#readme) for example use. + +When feeding reviews, there is a custom [document processor](/en/applications/document-processors) that intercepts document writes and updates the parent item with the review rating, so the aggregated review rating is kept stored with the item - see [ReviewProcessor](https://github.com/vespa-engine/sample-apps/blob/master/use-case-shopping/src/main/java/ai/vespa/example/shopping/ReviewProcessor.java). This is more an example of a custom document processor than a recommended way to do this, as feeding the reviews more than once will result in inflated values. To do this correctly, one should probably calculate this offline so a re-feed does not cause unexpected results. + + + +### Highlighted features + +* [Multiple document types](/en/basics/schemas) + +Vespa models data as documents, which are configured in schemas that defines how documents should be stored, indexed, ranked, and searched. In Vespa, you can have multiple documents types, which can be defined in `services.xml` how these should be distributed around the content clusters. This application uses three document types that are stored in the same content cluster: item, review and query. Search is done on items, but reviews refer to a single parent item and are rendered on the item page. The query document type is used to power auto-suggest functionality. + +* [Custom document processor](/en/applications/document-processors) + +In Vespa, you can set up custom document processors to perform any type of extra processing during document feeding. One example is to enrich the document with extra information, and another is to precalculate values of fields to avoid unnecessary computation during ranking. This application uses a document processor to intercept reviews and update the parent item's review rating. + +* [Custom searcher processor](/en/applications/searchers) + +In Vespa, you can set up custom searchers to perform any type of extra processing during querying. In the sample app there is a single custom searcher which builds the query for auto-suggestions, using a combination of [fuzzy matching](/en/reference/querying/yql#fuzzy) and [prefix search](/en/querying/text-matching#prefix-match). + +* [Custom handlers](/en/applications/request-handlers) + +With Vespa, you can set up general request handlers to handle any type of request. This example site is implemented with a single such request handler, [SiteHandler](https://github.com/vespa-engine/sample-apps/blob/master/use-case-shopping/src/main/java/ai/vespa/example/shopping/site/SiteHandler.java) which is set up in [services.xml](https://github.com/vespa-engine/sample-apps/blob/master/use-case-shopping/src/main/application/services.xml) to be bound to `/site`. Note that this handler is for example purposes and is designed to be independent of Vespa. Most applications would serve this through a dedicated setup. + +* [Custom configuration](/en/applications/configuring-components) + +When creating custom components in Vespa, for instance document processors, searchers or handlers, one can use custom configuration to inject config parameters into the components. This involves defining a config definition (a `.def` file), which creates a config class. You can instantiate this class with data in `services.xml` and the resulting object is dependency injected to the component during construction. This application uses custom config to set up the Vespa host details for the handler. + +* [Partial update](/en/reference/schemas/document-json-format#update) + +With Vespa, you can make changes to an existing document without submitting the full document. Examples are setting the value of a single field, adding elements to an array, or incrementing the value of a field without knowing the field value beforehand. This application contains an example of a partial update, in the voting of whether a review is helpful or not. The `SiteHandler` receives the request and the `ReviewVote` class sends a partial update to increment the `up`- or `downvotes` field. + +* [Search using YQL](/en/querying/query-language) + +In Vespa, you search for documents using YQL. In this application, the classes responsible for retrieving data from Vespa (in the `data` package beneath the `SiteHandler`) set up the YQL queries which are used to query Vespa over HTTP. + +* [Grouping](/en/querying/grouping) + +Grouping is used to group various fields of query results together. For this application, many of the queries to Vespa include grouping requests. The home page uses grouping to dynamically extract the first 3 levels of categories from the stored items. The search page groups results matching the query into categories, brands, item rating and price ranges. The order which the groups are rendered are determined by both counting and the relevance of the hits. This enables query-contextualized navigation. + +* [Rank profiles](/en/basics/ranking) + +Rank profiles are profiles containing instructions on how to score documents for a given query. The most important part of rank profiles are the ranking expressions. The schemas for the item and review document types contain different rank profiles to sort or score the data. The item ranking is using a hybrid combination of keyword and vector matching. + +* [Native embedders](/en/rag/embedding) + +Native embedders are used to map the textual query and document representations into dense high dimensional vectors which are used for semantic search. The application uses an open-source embedding model and inference is performed using [stateless model evaluation](/en/ranking/stateless-model-evaluation), both during document and query processing. + +* [Vector search](/en/querying/nearest-neighbor-search) + +The default retrieval uses approximate nearest neighbor search in combination with traditional lexical matching. Both the keyword and vector matching is constrained by the filters such as brand, price or category. + +* [Ranking functions](/en/reference/schemas/schemas#function-rank) + +Ranking functions are contained in rank profiles and can be referenced as part of any ranking expression from either first-phase, second-phase, global-phase or other functions. diff --git a/en/learn/tutorials/http-api.html b/en/learn/tutorials/http-api.html deleted file mode 100644 index 3705c65649..0000000000 --- a/en/learn/tutorials/http-api.html +++ /dev/null @@ -1,159 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Building an HTTP API using request handlers and processors" -redirect_from: -- /en/jdisc/http-api-tutorial.html ---- - -

    - This tutorial builds a simple application consisting of these pieces: -

    -
      -
    • A custom REST API - implemented in a request handler. -
    • Two pieces of request/response processing logic - implemented as two chained processors. -
    • A component shared by the above processors. -
    • A custom output format - a renderer. -
    -

    The end result is to process incoming request of the form:

    -
    -http://hostname:port/demo?terms=something%20completely%20different
    -
    -

    - into a nested structure response produced by the processors and serialized by the renderer. - Use the sample application found at - - http-api-using-request-handlers-and-processors. -

    - - - -

    Request handler

    -

    - The custom request handler is required to implement a custom API. - In many cases it is not necessary to add a custom handler - as the Processors can access the request data directly. - However, it is needed if e.g. your application wants more control over exactly which parameters - are used to route to a particular processing chain. -

    - In this case, the request handler will simply add the request URI as a property - and then forward to the built-in processing handler for processing. -

    - Review the code in - - DemoHandler.java -

    - - - -

    Processors

    -

    - This application contains two processors, - one for annotating the incoming request (using default values from config) and checking the result, - and one for creating the result using the shared component. -

    - - -

    AnnotatingProcessor

    -

    - Review the code in - - AnnotatingProcessor.java -

    - - -

    DataProcessor

    -

    - The other processor creates some structured Response Data from data handled to it in the request. - This is done in cases where the web service is a processing service. - In cases where the service is implementing some middleware on top of other services, - similar processors will instead make outgoing requests - to downstream web services to produce Response Data. -

    - Review the code in - - DataProcessor.java -

    - Notice how the task of the server is decomposed into separate Processing steps which can be composed - by chaining at configuration time and which communicates through the Request and Response only. - This structure enhances sharing, reuse and modularity - and makes it easy to create variations where some logic encapsulated in a Processor is added, removed or modified. -

    - The order of the processors is decided by the @Before and @After annotations - - refer to chained components. -

    - - -

    Custom configuration

    -

    - The default terms used by the AnnotatingProcessor are placed in user configuration, where the definition is in - - demo.def: -

    -
    -package=com.mydomain.demo
    -
    -demo[].term string
    -
    -

    - In other words, a configuration class containing a single array named demo, - containing a class Demo which only contains single string named term. -

    - - - -

    Renderer

    -

    - The responsibility of the renderer is to serialize the structured result into bytes for transport back to the client. -

    - Rendering works by first creating a single instance of the renderer, - invoking the constructor, then cloning a new renderer for each result set to be rendered. - init() will be invoked once on each new clone before render() is invoked. -

    - Review the code in - - DemoRenderer.java -

    - - - -

    Shared component

    -

    - The responsibility of this custom component is to decouple some parts of the application from the Searcher. - This makes it possible to reconfigure the Searcher without rebuilding the potentially costly custom component. -

    - In this case, what the component does is more than a little silly. - More typical use would be an FSA - or complex, shared helper functionality. -

    - Review the code in - - DemoComponent.java -

    - - - -

    Application

    -

    - Review the application's configuration in - - services.xml -

    - - - -

    Try it!

    -

    - Build the project, then run a test, - querying - http://localhost:8080/demo?terms=1%202%203%204 gives: -

    -
    -OK
    -Renderer initialized: 1369733374898
    -http://localhost:8080/demo?terms=1%202%203%204
    -1
    -    2
    -        3
    -            4
    -Rendering finished work: 1369733374902
    -
    diff --git a/en/learn/tutorials/http-api.mdx b/en/learn/tutorials/http-api.mdx new file mode 100644 index 0000000000..0cfe5890ed --- /dev/null +++ b/en/learn/tutorials/http-api.mdx @@ -0,0 +1,91 @@ +--- +title: "Building an HTTP API using request handlers and" +--- + +This tutorial builds a simple application consisting of these pieces: + +- A custom REST API - implemented in a _request handler_. +- Two pieces of request/response processing logic - implemented as two chained _processors_. +- A _component_ shared by the above processors. +- A custom output format - a _renderer_. + +The end result is to process incoming request of the form: + +```bash +http://hostname:port/demo?terms=something%20completely%20different +``` + +into a nested structure response produced by the processors and serialized by the renderer. Use the sample application found at [http-api-using-request-handlers-and-processors](https://github.com/vespa-engine/sample-apps/tree/master/examples/http-api-using-request-handlers-and-processors). + +## Request handler + +The custom request handler is required to implement a custom API. In many cases it is not necessary to add a custom handler as the Processors can access the request data directly. However, it is needed if e.g. your application wants more control over exactly which parameters are used to route to a particular processing chain. + +In this case, the request handler will simply add the request URI as a property and then forward to the built-in processing handler for processing. + +Review the code in [DemoHandler.java](https://github.com/vespa-engine/sample-apps/blob/master/examples/http-api-using-request-handlers-and-processors/src/main/java/ai/vespa/examples/DemoHandler.java) + +## Processors + +This application contains two processors, one for annotating the incoming request (using default values from config) and checking the result, and one for creating the result using the shared component. + +### AnnotatingProcessor + +Review the code in [AnnotatingProcessor.java](https://github.com/vespa-engine/sample-apps/blob/master/examples/http-api-using-request-handlers-and-processors/src/main/java/ai/vespa/examples/AnnotatingProcessor.java) + +### DataProcessor + +The other processor creates some structured Response Data from data handled to it in the request. This is done in cases where the web service is a processing service. In cases where the service is implementing some middleware on top of other services, similar processors will instead make outgoing requests to downstream web services to produce Response Data. + +Review the code in [DataProcessor.java](https://github.com/vespa-engine/sample-apps/blob/master/examples/http-api-using-request-handlers-and-processors/src/main/java/ai/vespa/examples/DataProcessor.java) + +Notice how the task of the server is decomposed into separate Processing steps which can be composed by chaining at configuration time and which communicates through the Request and Response only. This structure enhances sharing, reuse and modularity and makes it easy to create variations where some logic encapsulated in a Processor is added, removed or modified. + +The order of the processors is decided by the @Before and @After annotations - refer to [chained components](../../applications/chaining.html). + +### Custom configuration + +The default terms used by the AnnotatingProcessor are placed in user configuration, where the definition is in [demo.def](https://github.com/vespa-engine/sample-apps/blob/master/examples/http-api-using-request-handlers-and-processors/src/main/resources/configdefinitions/demo.def): + +```bash +package=com.mydomain.demo + +demo[].term string +``` + +In other words, a configuration class containing a single array named _demo_, containing a class Demo which only contains single string named _term_. + +## Renderer + +The responsibility of the renderer is to serialize the structured result into bytes for transport back to the client. + +Rendering works by first creating a single instance of the renderer, invoking the constructor, then cloning a new renderer for each result set to be rendered. `init()` will be invoked once on each new clone before `render()` is invoked. + +Review the code in [DemoRenderer.java](https://github.com/vespa-engine/sample-apps/blob/master/examples/http-api-using-request-handlers-and-processors/src/main/java/ai/vespa/examples/DemoRenderer.java) + +## Shared component + +The responsibility of this custom component is to decouple some parts of the application from the Searcher. This makes it possible to reconfigure the Searcher without rebuilding the potentially costly custom component. + +In this case, what the component does is more than a little silly. More typical use would be an [FSA](/en/reference/operations/tools#vespa-makefsa) or complex, shared helper functionality. + +Review the code in [DemoComponent.java](https://github.com/vespa-engine/sample-apps/blob/master/examples/http-api-using-request-handlers-and-processors/src/main/java/ai/vespa/examples/DemoComponent.java) + +## Application + +Review the application's configuration in [services.xml](https://github.com/vespa-engine/sample-apps/blob/master/examples/http-api-using-request-handlers-and-processors/src/main/application/services.xml) + +## Try it! + +Build the project, then [run a test](../../applications/developer-guide.html), querying [http://localhost:8080/demo?terms=1%202%203%204](http://localhost:8080/demo?terms=1%202%203%204) gives: + +```bash +OK +Renderer initialized: 1369733374898 +http://localhost:8080/demo?terms=1%202%203%204 +1 + 2 + 3 + 4 +Rendering finished work: 1369733374902 +``` \ No newline at end of file diff --git a/en/learn/tutorials/hybrid-search.md b/en/learn/tutorials/hybrid-search.mdx similarity index 59% rename from en/learn/tutorials/hybrid-search.md rename to en/learn/tutorials/hybrid-search.mdx index 7b7bac67d6..53f5a18899 100644 --- a/en/learn/tutorials/hybrid-search.md +++ b/en/learn/tutorials/hybrid-search.mdx @@ -1,66 +1,71 @@ --- -# Copyright Vespa.ai. All rights reserved. title: "Hybrid Text Search Tutorial" -redirect_from: - - /en/tutorials/hybrid-search --- Hybrid search combines different retrieval methods to improve search quality. This tutorial distinguishes between two core components of search: -* **Retrieval**: Identifying a subset of potentially relevant documents from a large corpus. Traditional lexical methods like [BM25](../../ranking/bm25.html) excel at this, as do modern, embedding-based [vector search](../../querying/vector-search-intro.html) approaches. -* **Ranking**: Ordering retrieved documents by relevance to refine the results. Vespa's flexible [ranking framework](../../basics/ranking.html) enables complex scoring mechanisms. +- **Retrieval**: Identifying a subset of potentially relevant documents from a large corpus. Traditional lexical methods like [BM25](/en/ranking/bm25) excel at this, as do modern, embedding-based [vector search](/en/querying/vector-search-intro) approaches. +- **Ranking**: Ordering retrieved documents by relevance to refine the results. Vespa's flexible [ranking framework](/en/basics/ranking) enables complex scoring mechanisms. -This tutorial demonstrates building a hybrid search application with Vespa that leverages the strengths of both lexical and embedding-based approaches. - We'll use the [NFCorpus](https://ir-datasets.com/nfcorpus.html) dataset from the [BEIR](https://github.com/beir-cellar/beir) benchmark and explore various hybrid search techniques using Vespa's query language and ranking features. +This tutorial demonstrates building a hybrid search application with Vespa that leverages the strengths of both lexical and embedding-based approaches. We'll use the [NFCorpus](https://ir-datasets.com/nfcorpus.html) dataset from the [BEIR](https://github.com/beir-cellar/beir) benchmark and explore various hybrid search techniques using Vespa's query language and ranking features. -The main goal is to set up a text search app that combines simple text scoring features -such as [BM25](../../ranking/bm25.html) [^1] with vector search in combination with text-embedding models. -We demonstrate how to obtain text embeddings within Vespa using Vespa's [embedder](../../rag/embedding.html#huggingface-embedder) -functionality. In this guide, we use [snowflake-arctic-embed-xs](https://huggingface.co/Snowflake/snowflake-arctic-embed-xs) as the -text embedding model. It is a small model that is fast to run and has a small memory footprint. +The main goal is to set up a text search app that combines simple text scoring features such as [BM25](/en/ranking/bm25) [^1] with vector search in combination with text-embedding models. We demonstrate how to obtain text embeddings within Vespa using Vespa's [embedder](/en/rag/embedding#huggingface-embedder) functionality. In this guide, we use [snowflake-arctic-embed-xs](https://huggingface.co/Snowflake/snowflake-arctic-embed-xs) as the text embedding model. It is a small model that is fast to run and has a small memory footprint. + + + +**Prerequisites:** + +- Linux, macOS or Windows 10 Pro on x86\_64 or arm64, with [Podman Desktop](https://podman.io/) or [Docker Desktop](https://www.docker.com/products/docker-desktop/) installed, with an engine running. + - Alternatively, start the Podman daemon: + + ```bash + $ podman machine init --memory 6000 + $ podman machine start + ``` + - See [Docker Containers](/en/operations/self-managed/docker-containers) for system limits and other settings. +- For CPUs older than Haswell (2013), see [CPU Support](/en/operations/self-managed/cpu-support). +- Memory: Minimum 4 GB RAM dedicated to Docker/Podman. [Memory recommendations](/en/operations/self-managed/node-setup#memory-settings). +- Disk: Avoid `NO_SPACE` - the vespaengine/vespa container image + headroom for data requires disk space. [Read more](/en/writing/feed-block). +- [Homebrew](https://brew.sh/) to install the [Vespa CLI](/en/clients/vespa-cli), or download the Vespa CLI from [Github releases](https://github.com/vespa-engine/vespa/releases). +- Python3 +- `curl` + -{% include pre-req.html memory="4 GB" extra-reqs=' -
  • Python3
  • -
  • curl
  • ' %} ## Installing vespa-cli and ir_datasets -This tutorial uses [Vespa-CLI](../../clients/vespa-cli.html) to deploy, feed, and query Vespa. We also use -[ir-datasets](https://ir-datasets.com/) to obtain the NFCorpus relevance dataset. -
    - -
    +This tutorial uses [Vespa-CLI](/en/clients/vespa-cli) to deploy, feed, and query Vespa. We also use [ir-datasets](https://ir-datasets.com/) to obtain the NFCorpus relevance dataset. 
    +```bash
     $ pip3 install --ignore-installed vespacli ir_datasets ir_measures requests
    -
    -
    +``` + We can quickly look at a document from [nfcorpus](https://ir-datasets.com/beir.html#beir/nfcorpus): -
    - -
    +
    +```bash
     $ ir_datasets export beir/nfcorpus docs --format jsonl | head -1
    -
    -
    +``` + Which outputs: -
    {% highlight json%}
    +
    +```json expandable
     {"doc_id": "MED-10", "text": "Recent studies have suggested that statins, an established drug group in the prevention of cardiovascular mortality, could delay or prevent breast cancer recurrence but the effect on disease-specific mortality remains unclear. We evaluated risk of breast cancer death among statin users in a population-based cohort of breast cancer patients. The study cohort included all newly diagnosed breast cancer patients in Finland during 1995\u20132003 (31,236 cases), identified from the Finnish Cancer Registry. Information on statin use before and after the diagnosis was obtained from a national prescription database. We used the Cox proportional hazards regression method to estimate mortality among statin users with statin use as time-dependent variable. A total of 4,151 participants had used statins. During the median follow-up of 3.25 years after the diagnosis (range 0.08\u20139.0 years) 6,011 participants died, of which 3,619 (60.2%) was due to breast cancer. After adjustment for age, tumor characteristics, and treatment selection, both post-diagnostic and pre-diagnostic statin use were associated with lowered risk of breast cancer death (HR 0.46, 95% CI 0.38\u20130.55 and HR 0.54, 95% CI 0.44\u20130.67, respectively). The risk decrease by post-diagnostic statin use was likely affected by healthy adherer bias; that is, the greater likelihood of dying cancer patients to discontinue statin use as the association was not clearly dose-dependent and observed already at low-dose/short-term use. The dose- and time-dependence of the survival benefit among pre-diagnostic statin users suggests a possible causal effect that should be evaluated further in a clinical trial testing statins\u2019 effect on survival in breast cancer patients.", "title": "Statin Use and Breast Cancer Survival: A Nationwide Cohort Study from Finland", "url": "http://www.ncbi.nlm.nih.gov/pubmed/25329299"}
    -{% endhighlight %}
    +``` + The NFCorpus documents have four fields: -- The `doc_id` and `url` -- The `text` and the `title` +- The `doc_id` and `url` +- The `text` and the `title` + +We are interested in the title and the text, and we want to be able to search across these two fields. We also need to store the `doc_id` to evaluate [ranking](/en/basics/ranking) accuracy. We will create a small script that converts the above output to [Vespa JSON document](/en/reference/schemas/document-json-format) format. Create a `convert.py` file: -We are interested in the title and the text, and we want to be able to search across these two fields. We also need to store the `doc_id` to evaluate [ranking](../../basics/ranking.html) -accuracy. We will create a small script that converts the above output to [Vespa JSON document](../../reference/schemas/document-json-format.html) format. Create a `convert.py` file: -
    - -
    {% highlight python %}
    +```python
     import sys
     import json
     
    @@ -73,43 +78,41 @@ for line in sys.stdin:
           **doc
         }
       }
    -  print(json.dumps(vespa_doc)){% endhighlight %}
    -
    + print(json.dumps(vespa_doc)) +``` + +```bash +Paste the above into file convert.py +``` +With this script, we convert the document dump to Vespa JSON format. Use the following command to convert the entire dataset to Vespa JSON format: -With this script, we convert the document dump to Vespa JSON format. Use -the following command to convert the entire dataset to Vespa JSON format: -
    - -
    +```bash
     $ ir_datasets export beir/nfcorpus docs --format jsonl | python3 convert.py > vespa-docs.jsonl
    -
    -
    +``` + Now, we will create the Vespa application package and schema to index the documents. ## Create a Vespa Application Package -A [Vespa application package](../../application-packages.html) is a set of configuration files and optional Java components that together define the behavior of a Vespa system. Let us define the minimum set of required files to create our hybrid text search application: `doc.sd` and `services.xml`. +A [Vespa application package](/en/basics/applications) is a set of configuration files and optional Java components that together define the behavior of a Vespa system. Let us define the minimum set of required files to create our hybrid text search application: `doc.sd` and `services.xml`. -
    - -
    +
    +```bash
     $ mkdir -p app/schemas
    -
    -
    +``` + ### Schema -A [schema](../../basics/schemas.html) is a document-type configuration; a single Vespa application can have multiple schemas with document types. -For this application, we define a schema `doc`, which must be saved in a file named `schemas/doc.sd` in the application package directory. +A [schema](/en/basics/schemas) is a document-type configuration; a single Vespa application can have multiple schemas with document types. For this application, we define a schema `doc`, which must be saved in a file named `schemas/doc.sd` in the application package directory. Write the following to `app/schemas/doc.sd`: -
    - -
    +
    +```js expandable
     schema doc {
         document doc {
             field language type string {
    @@ -134,7 +137,7 @@ schema doc {
             fields: title, text
         }
         
    -    field embedding type tensor<bfloat16>(v[384]) {
    +    field embedding type tensor(v[384]) {
             indexing: input title." ".input text | embed | attribute
             attribute {
                 distance-metric: angular
    @@ -149,43 +152,36 @@ schema doc {
     
         rank-profile semantic {
             inputs {
    -            query(e) tensor<bfloat16>(v[384])
    +            query(e) tensor(v[384])
             }
             first-phase {
                 expression: closeness(field, embedding)
             }
         }
     }
    -
    -
    -A lot is happening here; let us go through it in detail. +``` + +```bash +Paste the above into file app/schemas/doc.sd +``` + + A lot is happening here; let us go through it in detail. #### Document type and fields -The `document` section contains the fields of the document, their types, -and how Vespa should index and [match](../../reference/schemas/schemas.html#match) them. +The `document` section contains the fields of the document, their types, and how Vespa should index and [match](/en/reference/schemas/schemas#match) them. -The field property `indexing` configures the _indexing pipeline_ for a field. -For more information, see [schemas - indexing](../../basics/schemas.html#document-fields). -The [string](../../reference/schemas/schemas.html#string) data type represents both unstructured and structured texts, -and there are significant differences between [index and attribute](../../querying/text-matching.html#index-and-attribute). The above -schema includes default `match` modes for `attribute` and `index` property for visibility. +The field property `indexing` configures the _indexing pipeline_ for a field. For more information, see [schemas - indexing](/en/basics/schemas#document-fields). The [string](/en/reference/schemas/schemas#string) data type represents both unstructured and structured texts, and there are significant differences between [index and attribute](/en/querying/text-matching#index-and-attribute). The above schema includes default `match` modes for `attribute` and `index` property for visibility. -Note that we are enabling [BM25](../../ranking/bm25.html) for `title` and `text` -by including `index: enable-bm25`. The language field is the only field that is not the NFCorpus dataset. -We hardcode its value to "en" since the dataset is English. Using `set_language` avoids automatic language detection and uses the value when processing the other -text fields. Read more in [linguistics](../../linguistics/linguistics.html). +Note that we are enabling [BM25](/en/ranking/bm25) for `title` and `text` by including `index: enable-bm25`. The language field is the only field that is not the NFCorpus dataset. We hardcode its value to "en" since the dataset is English. Using `set_language` avoids automatic language detection and uses the value when processing the other text fields. Read more in [linguistics](/en/linguistics/linguistics). #### Fieldset for matching across multiple fields -[Fieldset](../../reference/schemas/schemas.html#fieldset) allows searching across multiple fields. Defining `fieldset` does not -add indexing/storage overhead. String fields grouped using fieldsets must share the same -[match](../../reference/schemas/schemas.html#match) and [linguistic processing](../../linguistics/linguistics.html) settings because -the query processing that searches a field or fieldset uses *one* type of transformation. +[Fieldset](/en/reference/schemas/schemas#fieldset) allows searching across multiple fields. Defining `fieldset` does not add indexing/storage overhead. String fields grouped using fieldsets must share the same [match](/en/reference/schemas/schemas#match) and [linguistic processing](/en/linguistics/linguistics) settings because the query processing that searches a field or fieldset uses *one* type of transformation. #### Embedding inference -Our `embedding` vector field is of [tensor](../../ranking/tensor-user-guide.html) type with a single named dimension (`v`) of 384 values. +Our `embedding` vector field is of [tensor](/en/ranking/tensor-user-guide) type with a single named dimension (`v`) of 384 values. -``` +```js field embedding type tensor(v[384]) { indexing: input title." ".input text | embed arctic | attribute attribute { @@ -193,31 +189,23 @@ field embedding type tensor(v[384]) { } } ``` -The `indexing` expression creates the input to the `embed` inference call (in our example the concatenation of the title and the text field). Since -the dataset is small, we do not specify `index` which would build [HNSW](../../querying/approximate-nn-hnsw) data structures for faster (but approximate) vector search. This guide uses [snowflake-arctic-embed-xs](https://huggingface.co/Snowflake/snowflake-arctic-embed-xs) as the text embedding model. The model is -trained with cosine similarity, which maps to Vespa's `angular` [distance-metric](../../reference/schemas/schemas.html#distance-metric) for -nearestNeighbor search. +The `indexing` expression creates the input to the `embed` inference call (in our example the concatenation of the title and the text field). Since the dataset is small, we do not specify `index` which would build [HNSW](/en/querying/approximate-nn-hnsw) data structures for faster (but approximate) vector search. This guide uses [snowflake-arctic-embed-xs](https://huggingface.co/Snowflake/snowflake-arctic-embed-xs) as the text embedding model. The model is trained with cosine similarity, which maps to Vespa's `angular` [distance-metric](/en/reference/schemas/schemas#distance-metric) for nearestNeighbor search. #### Ranking to determine matched documents ordering -You can define many [rank profiles](../../basics/ranking.html), -named collections of score calculations, and ranking phases. +You can define many [rank profiles](/en/basics/ranking), named collections of score calculations, and ranking phases. In this starting point, we have two simple rank-profile's: -- a `bm25` rank-profile that uses [BM25](../../ranking/bm25.html). We sum the two field-level BM25 scores -using a Vespa [ranking expression](../../ranking/ranking-expressions-features.html). +- a `bm25` rank-profile that uses [BM25](/en/ranking/bm25). We sum the two field-level BM25 scores using a Vespa [ranking expression](/en/ranking/ranking-expressions-features). - a `semantic` rank-profile which is used in combination Vespa's nearestNeighbor query operator (vector search). -Both profiles specify a single [ranking phase](../../ranking/phased-ranking.html). +Both profiles specify a single [ranking phase](/en/ranking/phased-ranking). ### Services Specification -The [services.xml](../../reference/applications/services/services.html) defines the services that make up -the Vespa application — which services to run and how many nodes per service. -Write the following to `app/services.xml`: +The [services.xml](/en/reference/applications/services/services.html) defines the services that make up the Vespa application — which services to run and how many nodes per service. Write the following to `app/services.xml`: + -
    - -
    {% highlight xml%}
    +```xml expandable
     
     
         
    @@ -241,17 +229,17 @@ Write the following to `app/services.xml`:
             
         
     
    -{% endhighlight %}
    -
    -
    +``` +```xml +Paste the above into file app/services.xml +``` Some notes about the elements above: -- `` defines the [container cluster](../../applications/containers.html) for document, query and result processing. -- `` sets up the [query endpoint](../../querying/query-api.html). The default port is 8080. -- `` sets up the [document endpoint](../../reference/api/document-v1.html) for feeding. -- `` with type `hugging-face-embedder` configures the embedder in the application package. This includes where to fetch the model files from, the prepend -instructions, and the pooling strategy. See [huggingface-embedder](../../rag/embedding.html#huggingface-embedder) for details and other embedders supported. +- `` defines the [container cluster](/en/applications/containers) for document, query and result processing. +- `` sets up the [query endpoint](/en/querying/query-api). The default port is 8080. +- `` sets up the [document endpoint](/en/reference/api/document-v1) for feeding. +- `` with type `hugging-face-embedder` configures the embedder in the application package. This includes where to fetch the model files from, the prepend instructions, and the pooling strategy. See [huggingface-embedder](/en/rag/embedding#huggingface-embedder) for details and other embedders supported. - `` defines how documents are stored and searched. - `` denotes how many copies to keep of each document. - `` assigns the document types in the _schema_ to content clusters. @@ -259,66 +247,55 @@ instructions, and the pooling strategy. See [huggingface-embedder](../../rag/emb ## Deploy the application package -Once we have finished writing our application package, we can deploy it. -We use settings similar to those in the [Vespa quick start guide](../../basics/deploy-an-application-local.html). +Once we have finished writing our application package, we can deploy it. We use settings similar to those in the [Vespa quick start guide](/en/basics/deploy-an-application-local). Start the Vespa container: -
    - -
    -$ docker run --detach --name vespa-hybrid --hostname vespa-container \
    -  --publish 8080:8080 --publish 19071:19071 \
    -  vespaengine/vespa
    -
    -
    - -Notice that we publish two ports: 8080 is the data-plane where we write and query documents, and 19071 is -the control-plane where we can deploy the application. Note that the data-plane port is inactive before deploying the application. - -Configure the Vespa CLI to use the local container: -
    - -
    +
    +```bash
    +$ docker run --detach --name vespa-hybrid --hostname vespa-container \ --publish 8080:8080 --publish 19071:19071 \ vespaengine/vespa
    +```
    +
    +
    +Notice that we publish two ports: 8080 is the data-plane where we write and query documents, and 19071 is the control-plane where we can deploy the application. Note that the data-plane port is inactive before deploying the application.
    +
    +Configure the Vespa CLI to use the local container: 
    +```bash
     $ vespa config set target local
    -
    -
    +``` + + +Starting the container can take a short while. Make sure that the configuration service is running by using `vespa status`. -Starting the container can take a short while. Make sure -that the configuration service is running by using `vespa status`. -
    - -
    -$ vespa status deploy --wait 300 
    -
    -
    +```bash +$ vespa status deploy --wait 300 +``` + Now, deploy the Vespa application from the `app` directory: -
    - -
    +
    +```bash
     $ vespa deploy --wait 300 app
    -
    -
    +``` + ## Feed the data -The data fed to Vespa must match the document type in the schema. This step performs embed inference inside Vespa -using the snowflake arctic embedding model. Remember the `component` definition in `services.xml` and the `embed` call in the schema. +The data fed to Vespa must match the document type in the schema. This step performs embed inference inside Vespa using the snowflake arctic embedding model. Remember the `component` definition in `services.xml` and the `embed` call in the schema. -
    - -
    +
    +```bash
     $ vespa feed -t http://localhost:8080 vespa-docs.jsonl
    -
    -
    +``` + The output should look like this (rates may vary depending on your machine HW): -
    {% highlight json%}
    +
    +```json expandable
     {
       "feeder.operation.count": 3633,
       "feeder.seconds": 148.515,
    @@ -340,28 +317,28 @@ The output should look like this (rates may vary depending on your machine HW):
       "http.response.code.counts": {
         "200": 3633
       }
    -}{% endhighlight %}
    +} +``` + Notice: -- `feeder.ok.rate` which is the throughput (Note that this step includes embedding inference). See [embedder-performance](../../rag/embedding.html#embedder-performance) for details on embedding inference performance. In this case, embedding inference is the bottleneck for overall indexing throughput. -- `http.response.code.counts` matches with `feeder.ok.count`. The dataset has 3633 documents. Note that if you observe any `429` responses, these are -harmless. Vespa asks the client to slow down the feed speed because of resource contention. +- `feeder.ok.rate` which is the throughput (Note that this step includes embedding inference). See [embedder-performance](/en/rag/embedding#embedder-performance) for details on embedding inference performance. In this case, embedding inference is the bottleneck for overall indexing throughput. +- `http.response.code.counts` matches with `feeder.ok.count`. The dataset has 3633 documents. Note that if you observe any `429` responses, these are harmless. Vespa asks the client to slow down the feed speed because of resource contention. ## Sample queries -We can now run a few sample queries to demonstrate various ways to perform searches over this data using the [Vespa query language](../../querying/query-language.html). +We can now run a few sample queries to demonstrate various ways to perform searches over this data using the [Vespa query language](/en/querying/query-language). -
    - -
    +
    +```bash
     $ ir_datasets export beir/nfcorpus/test queries --fields query_id text | head -1
    -
    -
    +``` -
    +```bash
     PLAIN-2	Do Cholesterol Statin Drugs Cause Breast Cancer?
    -
    +``` + If you see a pipe related error from the above command, you can safely ignore it. @@ -369,26 +346,23 @@ Here, `PLAIN-2` is the query id of the first test query. We'll use this test que ### Lexical search with BM25 scoring -The following query uses [weakAnd](../../ranking/wand.html) and where `totalTargetHits` is a hint -of how many documents we want to expose to configurable [ranking phases](../../ranking/phased-ranking.html). Refer -to [text search tutorial](text-search.html#querying-the-data) for more on querying with `text`. +The following query uses [weakAnd](/en/ranking/wand) and where `totalTargetHits` is a hint of how many documents we want to expose to configurable [ranking phases](/en/ranking/phased-ranking). Refer to [text search tutorial](/en/learn/tutorials/text-search#querying-the-data) for more on querying with `text`. + -
    - -
    -$ vespa query \
    +```bash
    +vespa query \
       'yql=select * from doc where default contains ({targetHits:10}text(@user-query))' \
       'user-query=Do Cholesterol Statin Drugs Cause Breast Cancer?' \
       'hits=1' \
       'language=en' \
       'ranking=bm25'
    -
    -
    +``` + + +Notice that we choose `ranking` to specify which rank profile to rank the documents retrieved by the query. This query returns the following [JSON result response](/en/reference/querying/default-result-format): -Notice that we choose `ranking` to specify which rank profile to rank the documents retrieved by the query. -This query returns the following [JSON result response](../../reference/querying/default-result-format.html): -
    {% highlight json %}
    +```json expandable
     {
         "root": {
             "id": "toplevel",
    @@ -420,49 +394,48 @@ This query returns the following [JSON result response](../../reference/querying
             ]
         }
     }
    +```
    +
     
    -{% endhighlight %}
    +The query retrieves and ranks `MED-10` as the most relevant document—notice the `totalCount` which is the number of documents that were retrieved for ranking phases. In this case, we exposed about 50 documents to first-phase ranking, it is higher than our target, but also fewer than the total number of documents that match any query terms. -The query retrieves and ranks `MED-10` as the most relevant document—notice the `totalCount` which is the number of documents that were retrieved for ranking -phases. In this case, we exposed about 50 documents to first-phase ranking, it is higher than our target, but also fewer than the total number of documents that match any query terms. +In the example below, we change the grammar from the default `weakAnd` to `any`, and the query matches 1780, or almost 50% of the indexed documents. -In the example below, we change the grammar from the default `weakAnd` to `any`, and the query matches 1780, or almost 50% of the indexed documents. -
    - -
    -$ vespa query \
    +```bash
    +vespa query \
       'yql=select * from doc where default contains ({targetHits:100, grammar:"any"}text(@user-query))' \
       'user-query=Do Cholesterol Statin Drugs Cause Breast Cancer?' \
       'hits=1' \
       'language=en' \
       'ranking=bm25'
    -
    -
    +``` + + +The bm25 rank profile calculates the relevance score (\~25.521), which is configured in the schema as: -The bm25 rank profile calculates the relevance score (~25.521), which is configured in the schema as: -
    +```txt
     rank-profile bm25 {
         first-phase {
             expression: bm25(title) + bm25(text)
         }
     }
    -
    +``` + So, in this case, `relevance` is the sum of the two BM25 scores. The retrieved document looks relevant; we can look at the graded judgment for this query `PLAIN-2`. The following exports the query relevance judgments (we grep for the query id that we are interested in): -
    - -
    +
    +```bash
     $ ir_datasets export beir/nfcorpus/test qrels | grep "PLAIN-2 "
    -
    -
    +``` + -The following is the output from the above command. Notice line two, the `MED-10` document retrieved above, is judged as very relevant with the grade 2 (perfect) for the query_id PLAIN-2. -This dataset has graded relevance judgments where a grade of 1 is less relevant than 2. Documents retrieved by the system without a relevance judgment are assumed to be irrelevant (grade 0). +The following is the output from the above command. Notice line two, the `MED-10` document retrieved above, is judged as very relevant with the grade 2 (perfect) for the query_id PLAIN-2. This dataset has graded relevance judgments where a grade of 1 is less relevant than 2. Documents retrieved by the system without a relevance judgment are assumed to be irrelevant (grade 0). -
    +
    +```bash expandable
     PLAIN-2 0 MED-2427 2
     PLAIN-2 0 MED-10 2
     PLAIN-2 0 MED-2429 2
    @@ -487,29 +460,28 @@ PLAIN-2 0 MED-4560 1
     PLAIN-2 0 MED-4828 1
     PLAIN-2 0 MED-4829 1
     PLAIN-2 0 MED-4830 1
    -
    +``` + ### Dense search using text embedding -Now, we turn to embedding-based retrieval, where we embed the query text using the configured text-embedding model and perform -an exact `nearestNeighbor` search. We use [embed query](../../rag/embedding.html#embedding-a-query-text) to produce the -input tensor `query(e)`, defined in the `semantic` rank-profile in the schema. +Now, we turn to embedding-based retrieval, where we embed the query text using the configured text-embedding model and perform an exact `nearestNeighbor` search. We use [embed query](/en/rag/embedding#embedding-a-query-text) to produce the input tensor `query(e)`, defined in the `semantic` rank-profile in the schema. + -
    - -
    -$ vespa query \
    +```bash
    +vespa query \
       'yql=select * from doc where {targetHits:10}nearestNeighbor(embedding,e)' \
       'user-query=Do Cholesterol Statin Drugs Cause Breast Cancer?' \
       'input.query(e)=embed(@user-query)' \
       'hits=1' \
       'ranking=semantic'
    -
    -
    +``` + + +This query returns the following [JSON result response](/en/reference/querying/default-result-format): -This query returns the following [JSON result response](../../reference/querying/default-result-format.html): -
    {% highlight json %}
    +```json expandable
     {
         "root": {
             "id": "toplevel",
    @@ -540,12 +512,13 @@ This query returns the following [JSON result response](../../reference/querying
                 }
             ]
         }
    -}{% endhighlight %}
    +} +``` -The result of this vector-based search differed from the previous sparse keyword search, with a different relevant document at position 1. In this case, -the relevance score is 0.606 and calculated by the `closeness` function in the `semantic` rank-profile. Note that more documents were retrieved than the `targetHits`. -``` +The result of this vector-based search differed from the previous sparse keyword search, with a different relevant document at position 1. In this case, the relevance score is 0.606 and calculated by the `closeness` function in the `semantic` rank-profile. Note that more documents were retrieved than the `targetHits`. + +```bash rank-profile semantic { inputs { query(e) tensor(v[384]) @@ -556,24 +529,20 @@ rank-profile semantic { } ``` -Where [closeness(field, embedding)](../../reference/ranking/rank-features.html#attribute-match-features-normalized) is a ranking feature that calculates the cosine similarity between the query and the document embedding. This returns the inverted of the distance between the two vectors. Small distance = higher closeness. This because Vespa sorts results in descending order of relevance. -Descending order means the largest will appear at the top of the ranked list. +Where [closeness(field, embedding)](/en/reference/ranking/rank-features#attribute-match-features-normalized) is a ranking feature that calculates the cosine similarity between the query and the document embedding. This returns the inverted of the distance between the two vectors. Small distance = higher closeness. This because Vespa sorts results in descending order of relevance. Descending order means the largest will appear at the top of the ranked list. -Note that similarity scores of embedding vectors are often optimized via contrastive or ranking losses, which make them difficult to interpret. +Note that similarity scores of embedding vectors are often optimized via contrastive or ranking losses, which make them difficult to interpret. ## Evaluate ranking accuracy -The previous section demonstrated how to combine the Vespa query language with rank profiles -to implement two different retrieval and ranking strategies. +The previous section demonstrated how to combine the Vespa query language with rank profiles to implement two different retrieval and ranking strategies. -In the following section we evaluate all 323 test queries with both models to compare their overall effectiveness, measured using [nDCG@10](https://en.wikipedia.org/wiki/Discounted_cumulative_gain). `nDCG@10` is the official evaluation metric of the BEIR benchmark and is an appropriate metric for test sets with graded relevance judgments. +In the following section we evaluate all 323 test queries with both models to compare their overall effectiveness, measured using [nDCG@10](https://en.wikipedia.org/wiki/Discounted_cumulative_gain). `nDCG@10` is the official evaluation metric of the BEIR benchmark and is an appropriate metric for test sets with graded relevance judgments. -For this evaluation task, we need to write a small script. The following script iterates over the queries in the test set, executes the query against the Vespa instance, and reads -the response from Vespa. It then evaluates and prints the metric. The overall effectiveness is measured using the average of each query `nDCG@10` metric. +For this evaluation task, we need to write a small script. The following script iterates over the queries in the test set, executes the query against the Vespa instance, and reads the response from Vespa. It then evaluates and prints the metric. The overall effectiveness is measured using the average of each query `nDCG@10` metric. -
    - -
    {% highlight python %}
    +
    +```python expandable
     import requests
     import ir_datasets
     from ir_measures import calc_aggregate, nDCG, ScoredDoc
    @@ -643,90 +612,88 @@ def main():
       print("Ranking metric NDCG@10 for rank profile {}: {:.4f}".format(args.ranking, metrics[nDCG@10]))
     
     if __name__ == "__main__":
    -    main(){% endhighlight %}
    -
    + main() +``` + +```bash +Paste the above into file evaluate_ranking.py +``` + -Then execute the script: -
    - -
    +Then execute the script: 
    +```bash
     $ python3 evaluate_ranking.py --ranking bm25 --mode sparse
    -
    -
    +``` + The script will produce the following output: -
    -Ranking metric NDCG@10 for rank profile bm25: 0.3210 
    -
    + +```txt +Ranking metric NDCG@10 for rank profile bm25: 0.3210 +``` + Now, we can evaluate the dense model using the same script: -
    - -
    +
    +```bash
     $ python3 evaluate_ranking.py --ranking semantic --mode dense
    -
    -
    +``` -
    +```txt
     Ranking metric NDCG@10 for rank profile semantic: 0.3077
    -
    -Note that the _average_ `nDCG@10` score is computed across all the 327 test queries. -You can also experiment beyond a single metric and modify the script to calculate -more [measures](https://ir-measur.es/en/latest/measures.html), for example, including precision with a relevance label cutoff of 2: +``` + + +Note that the _average_ `nDCG@10` score is computed across all the 327 test queries. You can also experiment beyond a single metric and modify the script to calculate more [measures](https://ir-measur.es/en/latest/measures.html), for example, including precision with a relevance label cutoff of 2: -
    +
    +```txt
     metrics = [nDCG@10, P(rel=2)@10]
    -
    +``` + -Also note that the exact nDCG@10 values may vary slightly between runs. +Also note that the exact nDCG@10 values may vary slightly between runs. ## Hybrid Search & Ranking -We demonstrated and evaluated two independent retrieval and ranking strategies in the previous sections. -Now, we want to explore hybrid search techniques where we combine: +We demonstrated and evaluated two independent retrieval and ranking strategies in the previous sections. Now, we want to explore hybrid search techniques where we combine: -- traditional lexical keyword matching with a text scoring method (BM25) -- embedding-based search using a text embedding model +- traditional lexical keyword matching with a text scoring method (BM25) +- embedding-based search using a text embedding model -With Vespa, there is a distinction between retrieval (matching) and configurable [ranking](../../basics/ranking.html). +With Vespa, there is a distinction between retrieval (matching) and configurable [ranking](/en/basics/ranking). -In the Vespa ranking phases, we can express arbitrary scoring complexity with the full power of the Vespa [ranking](../../basics/ranking.html) framework. -Meanwhile, top-k retrieval relies on simple built-in functions associated with Vespa's top-k query operators. -These top-k operators aim to avoid scoring all documents in the collection for a query by using a simplistic scoring function to identify the top-k documents. +In the Vespa ranking phases, we can express arbitrary scoring complexity with the full power of the Vespa [ranking](/en/basics/ranking) framework. Meanwhile, top-k retrieval relies on simple built-in functions associated with Vespa's top-k query operators. These top-k operators aim to avoid scoring all documents in the collection for a query by using a simplistic scoring function to identify the top-k documents. -These top-k query operators use `index` structures to accelerate the query evaluation, avoiding scoring all documents using heuristics. In the context of hybrid text -search, the following Vespa top-k query operators are relevant: +These top-k query operators use `index` structures to accelerate the query evaluation, avoiding scoring all documents using heuristics. In the context of hybrid text search, the following Vespa top-k query operators are relevant: -- YQL `{targetHits:k}nearestNeighbor()` for dense representations (text embeddings) using -a configured [distance-metric](../../reference/schemas/schemas.html#distance-metric) as the scoring function. -- YQL `myField contains ({targetHits:k}text(@user-query))` which by default uses [weakAnd](../../ranking/wand.html) for sparse representations. +- YQL `{targetHits:k}nearestNeighbor()` for dense representations (text embeddings) using a configured [distance-metric](/en/reference/schemas/schemas#distance-metric) as the scoring function. +- YQL `myField contains ({targetHits:k}text(@user-query))` which by default uses [weakAnd](/en/ranking/wand) for sparse representations. -We can combine these operators using boolean query operators like AND/OR/RANK to express a hybrid search query. Then, there is a wild number of -ways that we can combine various signals in [ranking](../../basics/ranking.html). +We can combine these operators using boolean query operators like AND/OR/RANK to express a hybrid search query. Then, there is a wild number of ways that we can combine various signals in [ranking](/en/basics/ranking). ### Define our first simple hybrid rank profile -First, we can add our first simple hybrid rank profile that combines the dense and sparse components using multiplication to -combine them into a single score. +First, we can add our first simple hybrid rank profile that combines the dense and sparse components using multiplication to combine them into a single score. -
    +
    +```txt
     closeness(field, embedding) * (1 + bm25(title) + bm25(text))
    -
    +``` -- the [closeness(field, embedding)](../../reference/ranking/rank-features.html#attribute-match-features-normalized) rank-feature returns a normalized score in the range 0 to 1 inclusive -- Any of the per-field BM25 scores are in the range of 0 to infinity -We add a bias constant (1) to avoid the overall score becoming 0 if the document does not match any query terms, -as the BM25 scores would be 0. We also add `match-features` to be able to debug each of the scores. +- the [closeness(field, embedding)](/en/reference/ranking/rank-features#attribute-match-features-normalized) rank-feature returns a normalized score in the range 0 to 1 inclusive +- Any of the per-field BM25 scores are in the range of 0 to infinity +We add a bias constant (1) to avoid the overall score becoming 0 if the document does not match any query terms, as the BM25 scores would be 0. We also add `match-features` to be able to debug each of the scores. -
    - -
    +
    +
    +```js expandable
     schema doc {
         document doc {
             field language type string {
    @@ -751,7 +718,7 @@ schema doc {
             fields: title, text
         }
         
    -    field embedding type tensor<bfloat16>(v[384]) {
    +    field embedding type tensor(v[384]) {
           indexing: input title." ".input text | embed | attribute
           attribute {
             distance-metric: angular
    @@ -760,7 +727,7 @@ schema doc {
       
         rank-profile hybrid {
             inputs {
    -          query(e) tensor<bfloat16>(v[384])
    +          query(e) tensor(v[384])
             }
             first-phase {
                 expression: closeness(field, embedding) * (1 + (bm25(title) + bm25(text)))
    @@ -768,46 +735,42 @@ schema doc {
             match-features: bm25(title) bm25(text) closeness(field, embedding)
         }
     }
    -
    -
    +``` + Now, re-deploy the Vespa application from the `app` directory: -
    - -
    +
    +```bash
     $ vespa deploy --wait 300 app
    -
    -
    +``` + -After that, we can start experimenting with how to express hybrid queries using the Vespa query language. +After that, we can start experimenting with how to express hybrid queries using the Vespa query language. ### Hybrid query examples -The following demonstrates combining the two top-k query operators using the Vespa query language. In a later section, we will show -how to combine the two retrieval strategies using the Vespa ranking framework. This section focuses on the top-k retrieval part -that exposes matched documents to the Vespa [ranking](../../basics/ranking.html) phase(s). + +The following demonstrates combining the two top-k query operators using the Vespa query language. In a later section, we will show how to combine the two retrieval strategies using the Vespa ranking framework. This section focuses on the top-k retrieval part that exposes matched documents to the Vespa [ranking](/en/basics/ranking) phase(s). #### Hybrid query using the OR operator -The following query exposes documents to ranking that match the query using *either (OR)* the sparse or dense representation. +The following query exposes documents to ranking that match the query using *either (OR)* the sparse or dense representation. + -
    - -
    -$ vespa query \
    +```bash
    +vespa query \
       'yql=select * from doc where default contains ({targetHits:10}text(@user-query)) or ({targetHits:10}nearestNeighbor(embedding,e))' \
       'user-query=Do Cholesterol Statin Drugs Cause Breast Cancer?' \
       'input.query(e)=embed(@user-query)' \
       'hits=1' \
       'language=en' \
       'ranking=hybrid'
    -
    -
    -The documents retrieved into ranking is scored by the `hybrid` rank-profile. Note that both top-k query operators might expose more than -the the `targetHits` setting. +``` + The documents retrieved into ranking is scored by the `hybrid` rank-profile. Note that both top-k query operators might expose more than the the `targetHits` setting. + +The above query returns the following [JSON result response](/en/reference/querying/default-result-format): -The above query returns the following [JSON result response](../../reference/querying/default-result-format.html): -
    {% highlight json %}
    +```json expandable
     {
         "root": {
             "id": "toplevel",
    @@ -843,100 +806,84 @@ The above query returns the following [JSON result response](../../reference/que
                 }
             ]
         }
    -}{% endhighlight %}
    +} +``` + -What is going on here is that we are combining the two top-k query operators using a boolean OR (disjunction). -The `totalCount` is the number of documents retrieved into ranking (About 90, which is higher than 10 + 10). -The `relevance` is the score assigned by `hybrid` rank-profile. Notice that the `matchfeatures` field shows all the feature scores. This is -useful for debugging and understanding the ranking behavior, also for feature logging. +What is going on here is that we are combining the two top-k query operators using a boolean OR (disjunction). The `totalCount` is the number of documents retrieved into ranking (About 90, which is higher than 10 + 10). The `relevance` is the score assigned by `hybrid` rank-profile. Notice that the `matchfeatures` field shows all the feature scores. This is useful for debugging and understanding the ranking behavior, also for feature logging. #### Hybrid query with AND operator The following combines the two top-k operators using AND, meaning that the retrieved documents must match both the sparse and dense top-k operators: -
    - -
    -$ vespa query \
    +
    +```bash
    +vespa query \
       'yql=select * from doc where default contains ({targetHits:10}text(@user-query)) and ({targetHits:10}nearestNeighbor(embedding,e))' \
       'user-query=Do Cholesterol Statin Drugs Cause Breast Cancer?' \
       'input.query(e)=embed(@user-query)' \
       'hits=1' \
       'language=en' \
       'ranking=hybrid'
    -
    -
    -For the sparse keyword query matching, the `weakAnd` operator is used by default -and it requires that at least one term in the query matches the document (fieldset searched). +``` + For the sparse keyword query matching, the `weakAnd` operator is used by default and it requires that at least one term in the query matches the document (fieldset searched). #### Hybrid query with rank query operator -The following combines the two top-k operators using the [rank](../../reference/querying/yql.html#rank) query operator, which allows us to retrieve -using only the first operand of the rank operator, but where the remaining operands allow computing (match) features -that can be used in ranking phases. +The following combines the two top-k operators using the [rank](/en/reference/querying/yql#rank) query operator, which allows us to retrieve using only the first operand of the rank operator, but where the remaining operands allow computing (match) features that can be used in ranking phases. + +This query is meaningful because we can use the computed features in the ranking expressions but retrieve only by the dense representation. This is usually the most resource-effective way to combine the two representations. -This query is meaningful because we can use the computed features in the ranking expressions but retrieve only by the dense representation. This -is usually the most resource-effective way to combine the two representations. -
    - -
    -$ vespa query \
    +```bash
    +vespa query \
       'yql=select * from doc where rank(({targetHits:10}nearestNeighbor(embedding,e)), default contains ({targetHits:10}text(@user-query)))' \
       'user-query=Do Cholesterol Statin Drugs Cause Breast Cancer?' \
       'input.query(e)=embed(@user-query)' \
       'hits=1' \
       'language=en' \
       'ranking=hybrid'
    -
    -
    -We can also invert the order of the operands to the `rank` query operator that retrieves by the sparse representation -but uses the dense representation to compute features for ranking. This is very useful in cases where we do not want -to build HNSW indexes (adds memory and slows down indexing), but still be able to use semantic signals in ranking phases. - -
    - -
    -$ vespa query \
    +```
    + We can also invert the order of the operands to the `rank` query operator that retrieves by the sparse representation but uses the dense representation to compute features for ranking. This is very useful in cases where we do not want to build HNSW indexes (adds memory and slows down indexing), but still be able to use semantic signals in ranking phases.
    +
    +
    +```bash
    +vespa query \
       'yql=select * from doc where rank(default contains ({targetHits:10}text(@user-query)), ({targetHits:10}nearestNeighbor(embedding,e)))' \
       'user-query=Do Cholesterol Statin Drugs Cause Breast Cancer?' \
       'input.query(e)=embed(@user-query)' \
       'hits=1' \
       'language=en' \
       'ranking=hybrid'
    -
    -
    +``` + -This way of performing hybrid retrieval allows retrieving only by the sparse representation and uses the dense vector -representation to compute features for ranking. +This way of performing hybrid retrieval allows retrieving only by the sparse representation and uses the dense vector representation to compute features for ranking. ## Hybrid ranking -In the previous section, we demonstrated combining the two top-k query operators using boolean query operators. +In the previous section, we demonstrated combining the two top-k query operators using boolean query operators. -This section will show combining the two retrieval strategies using the Vespa ranking framework. We can first start evaluating -the effectiveness of the hybrid rank profile that combines the two retrieval strategies. +This section will show combining the two retrieval strategies using the Vespa ranking framework. We can first start evaluating the effectiveness of the hybrid rank profile that combines the two retrieval strategies. -
    - -
    +
    +```bash
     $ python3 evaluate_ranking.py --ranking hybrid --mode hybrid
    -
    -
    +``` Which outputs -
    +
    +```txt
     Ranking metric NDCG@10 for rank profile hybrid: 0.3330
    -
    +``` + -The `nDCG@10` score is slightly higher than the profiles that only use one of the ranking strategies. +The `nDCG@10` score is slightly higher than the profiles that only use one of the ranking strategies. -Now, we can experiment with more complex ranking expressions that combine the two retrieval strategies. -We add a few more rank profiles to the schema that combine the two retrieval strategies in different ways. +Now, we can experiment with more complex ranking expressions that combine the two retrieval strategies. We add a few more rank profiles to the schema that combine the two retrieval strategies in different ways. -
    - -
    +
    +```js expandable
     schema doc {
         document doc {
             field language type string {
    @@ -961,7 +908,7 @@ schema doc {
             fields: title, text
         }
         
    -    field embedding type tensor<bfloat16>(v[384]) {
    +    field embedding type tensor(v[384]) {
           indexing: input title." ".input text | embed | attribute
           attribute {
             distance-metric: angular
    @@ -970,7 +917,7 @@ schema doc {
       
         rank-profile hybrid {
             inputs {
    -          query(e) tensor<bfloat16>(v[384])
    +          query(e) tensor(v[384])
             }
             first-phase {
                 expression: closeness(field, embedding) * (1 + (bm25(title) + bm25(text)))
    @@ -1029,86 +976,78 @@ schema doc {
             }
             match-features: bm25(title) bm25(text) bm25_score cosine
         }
    -}  
    -
    -
    +} +``` + +```bash +Paste the above into file app/schemas/doc.sd +``` Now, re-deploy the Vespa application from the `app` directory: -
    - -
    -$ vespa deploy --wait 300 app
    -
    -
    + +```bash +vespa deploy --wait 300 app +``` Let us break down the new rank profiles: -- `hybrid-sum` combines the two retrieval strategies using addition. This is a simple way to combine the two strategies. But since the BM25 scores are not normalized (unbound) -and the closeness score is normalized (0-1), the BM25 scores will dominate the closeness score. +- `hybrid-sum` combines the two retrieval strategies using addition. This is a simple way to combine the two strategies. But since the BM25 scores are not normalized (unbound) and the closeness score is normalized (0-1), the BM25 scores will dominate the closeness score. - `hybrid-normalize-bm25-with-atan` combines the two strategies using a normalized BM25 score and the cosine similarity. The BM25 scores are normalized using the `atan` function. - `hybrid-rrf` combines the two strategies using the reciprocal rank feature. This is a way to combine the two strategies using a reciprocal rank feature. - `hybrid-linear-normalize` combines the two strategies using a linear normalization function. This is a way to combine the two strategies using a linear normalization function. -The two last profiles are using `global-phase` to rerank the top 100 documents using the reciprocal rank and linear normalization functions. This can only be done in the global phase -as it requires access to all the documents that are retrieved into ranking and in a multi-node setup, this requires communication between the nodes and knowledge of -the score distribution across all the nodes. In addition, each ranking phase can only order the documents by a single score. +The two last profiles are using `global-phase` to rerank the top 100 documents using the reciprocal rank and linear normalization functions. This can only be done in the global phase as it requires access to all the documents that are retrieved into ranking and in a multi-node setup, this requires communication between the nodes and knowledge of the score distribution across all the nodes. In addition, each ranking phase can only order the documents by a single score. ### Evaluate the new rank profiles Adding new rank-profiles is a hot change. Once we have deployed the application, we can evaluate the new hybrid profiles using the script: -
    - -
    +
    +```bash
     $ python3 evaluate_ranking.py --ranking hybrid-sum --mode hybrid
    -
    -
    +``` -
    +```txt
     Ranking metric NDCG@10 for rank profile hybrid-sum: 0.3244
    -
    - +``` -
    - -
    +```bash
     $ python3 evaluate_ranking.py --ranking hybrid-normalize-bm25-with-atan --mode hybrid
    -
    -
    +``` -
    +```txt
     Ranking metric NDCG@10 for rank profile hybrid-normalize-bm25-with-atan: 0.3410
    -
    +``` + + -
    - -
    +```bash
     $ python3 evaluate_ranking.py --ranking hybrid-rrf --mode hybrid
    -
    -
    +``` + + -
    +```txt
     Ranking metric NDCG@10 for rank profile hybrid-rrf: 0.3233
    -
    +``` + -
    - -
    +
    +```bash
     $ python3 evaluate_ranking.py --ranking hybrid-linear-normalize --mode hybrid
    -
    -
    +``` + -
    +
    +```txt
     Ranking metric NDCG@10 for rank profile hybrid-linear-normalize: 0.3423
    -
    +``` -On this particular dataset, the `hybrid-normalize-bm25-with-atan` rank profile performs the best, but the difference is small. This also demonstrates that hybrid search -and ranking is a complex problem and that the effectiveness of the hybrid model depends on the dataset and the retrieval strategies. -These results (which is the best) might not -transfer to your specific retrieval use case and dataset, so it is important to evaluate the effectiveness of a hybrid model on -your specific dataset. +On this particular dataset, the `hybrid-normalize-bm25-with-atan` rank profile performs the best, but the difference is small. This also demonstrates that hybrid search and ranking is a complex problem and that the effectiveness of the hybrid model depends on the dataset and the retrieval strategies. + +These results (which is the best) might not transfer to your specific retrieval use case and dataset, so it is important to evaluate the effectiveness of a hybrid model on your specific dataset. See [Improving retrieval with LLM-as-a-judge](https://blog.vespa.ai/improving-retrieval-with-llm-as-a-judge/) for more information on how to collect relevance judgments for your dataset. @@ -1118,14 +1057,10 @@ We showed how to express hybrid queries using the Vespa query language and how t ## Cleanup -
    - -
    -$ docker rm -f vespa-hybrid
    -
    -
    -[^1]: Robertson, Stephen and Zaragoza, Hugo and others, 2009. The probabilistic relevance framework: BM25 and beyond. Foundations and Trends in Information Retrieval. +```bash +$ docker rm -f vespa-hybrid +``` - +1. Robertson, Stephen and Zaragoza, Hugo and others, 2009. The probabilistic relevance framework: BM25 and beyond. Foundations and Trends in Information Retrieval. diff --git a/en/learn/tutorials/index.html b/en/learn/tutorials/index.html deleted file mode 100644 index 8879e35ed3..0000000000 --- a/en/learn/tutorials/index.html +++ /dev/null @@ -1,112 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Tutorials and use cases" -redirect_from: -- /en/vespa-quick-start-vagrant.html -- /en/getting-started.html -- /en/tutorials/index.html ---- - -

    Text search

    - - - - -

    Vector Search

    - -

    Learn how to use Vespa Vector Search in the practical nearest neighbor search guide. -It uses Vespa's support for nearest neighbor search, there is -also support for fast approximate nearest neighbor search in Vespa. -The guide covers combining vector search with filters and how to perform hybrid search, -combining retrieval over inverted index structures with vector search.

    - - -

    Hybrid Search

    - -

    Tutorial: Hybrid Text Search. -A search tutorial and introduction to hybrid text ranking with Vespa, -combining BM25 with text embedding models.

    - - -

    RAG (Retrieval-Augmented Generation)

    - - - - -

    Combining search and recommendation: The News tutorial

    - -

    Follow this series to learn how to build a complete application supporting both -content recommendation/personalization, navigation, and search.

    - - - - -

    ML Model Serving

    - -

    Learn how to use Vespa for ML model serving in Stateless Model Evaluation. -Vespa supports running inference with models from many popular ML frameworks, which can be used -for ranking, query classification, question answering, multi-modal retrieval, and more.

    - - - - -

    Embedding Model Inference

    - -

    Vespa supports integrating embedding models, which avoids transferring large amounts of embedding vector data -over the network and allows for efficient serving of embedding models.

    - - - -

    E-Commerce

    - -

    The e-commerce shopping sample application demonstrates Vespa grouping, -true in-place partial updates, custom ranking, and more.

    - - -

    Building a custom HTTP API

    - -

    The HTTP API tutorial shows how to build a custom HTTP API in an application.

    - - -

    More examples and sample applications

    - -

    There are many examples and starting applications on -GitHub -and Pyvespa examples.

    diff --git a/en/learn/tutorials/news-1-deploy-an-application.md b/en/learn/tutorials/news-1-deploy-an-application.md deleted file mode 100644 index ca51b8d1f6..0000000000 --- a/en/learn/tutorials/news-1-deploy-an-application.md +++ /dev/null @@ -1,299 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "News search and recommendation tutorial - getting started on Docker" -redirect_from: -- /en/tutorials/news-1-getting-started.html -- /en/tutorials/news-1-deploy-an-application ---- - - -Our goal with this series is to set up a Vespa application for personalized -news recommendations. We will do this in stages, starting with a simple news -search system and gradually adding functionality as we go through the -tutorial parts. - -The parts are: - -1. [Getting started](news-1-deploy-an-application.html) - this part -2. [A basic news search application](news-2-basic-feeding-and-query.html) - application packages, feeding, query -3. [News search](news-3-searching.html) - sorting, grouping, and ranking -4. [Generating embeddings for users and news articles](news-4-embeddings.html) -5. [News recommendation](news-5-recommendation.html) - partial updates (news embeddings), ANNs, filtering -6. [News recommendation with searchers](news-6-recommendation-with-searchers.html) - custom searchers, doc processors -7. [News recommendation with parent-child](news-7-recommendation-with-parent-child.html) - parent-child, tensor ranking - -There are different entry points to this tutorial. This one is describing how to get -started using Docker on your local machine. You can also deploy the application we are creating on -[Vespa Cloud](https://cloud.vespa.ai). - -In this part, we will start with a minimal Vespa application to -get used to some basic operations for running the application on Docker. -In the next part of the tutorial, we'll start developing our application. - -{% include pre-req.html memory="4 GB" extra-reqs=' -
  • Python3 for converting the dataset to Vespa JSON.
  • -
  • curl to download the dataset and run the Vespa health-checks.
  • -
  • Java 17 in part 6.
  • -
  • Apache Maven in part 6.
  • ' %} - -{% include note.html content='4 GB Docker memory is sufficient for the demo dataset in part 2. -The full MIND dataset requires more, use 10 GB.' %} - -In upcoming parts of this series, we will have some additional Python dependencies - -we use [PyTorch](https://pytorch.org/) to train vector representations for news and users -and train machine learning models for use in ranking. - - -## Installing vespa-cli - -This tutorial uses [Vespa-CLI](../../clients/vespa-cli.html), -Vespa CLI is the official command-line client for Vespa.ai. -It is a single binary without any runtime dependencies and is available for Linux, macOS, and Windows. - -
    - -
    -$ brew install vespa-cli 
    -
    -
    - -For the rest of this tutorial, you will be using localhost, so you need to configure your Vespa CLI to connect to localhost. -Run the following to use endpoints on localhost: - -
    - -
    -$ vespa config set target local
    -
    -
    - - - -## A minimal Vespa application - -This tutorial has a [companion sample application](https://github.com/vespa-engine/sample-apps/tree/master/news). -Throughout the tutorial, we will be using support code from this application. -Also, the final state of each tutorial can be found in the various `app-...` subdirectories. - -Let's start by cloning the sample application: - -
    - -
    -$ vespa clone -f news news && cd news
    -
    -
    - -The above downloads the `news` directory from the Vespa -[sample apps repository](https://github.com/vespa-engine/sample-apps/) and -places the contents in a folder called `news`. Use `--help` to see documentation -for the vespa-cli utility: - -
    - -
    -$ vespa clone --help
    -
    -
    -In the `news` directory, several pre-configured application packages are available. -The `app-1-getting-started` directory contains a minimal Vespa application. -There are two files there: - -- `services.xml` - defines the services that the application consists of -- `schemas/news.sd` - defines the schema for searchable content. - -We will revisit these files in the next part of the tutorial. - - -## Starting Vespa - -This application doesn't contain much at the moment, -let's start up the application anyway by starting a Docker container to run it: - -
    - -
    -$ docker pull vespaengine/vespa
    -$ docker run --detach --name vespa --hostname vespa-tutorial \
    -  --publish 8080:8080 --publish 19071:19071 --publish 19092:19092 \
    -  vespaengine/vespa
    -
    -
    - -First, we pull the latest [vespa-image](https://hub.docker.com/r/vespaengine/vespa/) -from the Docker hub, then we -start it with the name `vespa`. This starts the Docker container and the -initial Vespa services to be able to deploy an application. - -Starting the container can take a short while. Before continuing, make sure -that the configuration service is running by using `vespa status`. - -
    - -
    -$ vespa status deploy --wait 300 
    -
    -
    - -With the config server up and running, deploy the application using vespa-cli: - -
    - -
    -$ vespa deploy --wait 300 app-1-getting-started 
    -
    -
    - -The command uploads the application and verifies the content. -If anything is wrong with the application, this step will fail with a failure description; -Otherwise, this switches the application to a live status. - -Whenever you have a new version of your application, -run the same command to deploy the application. -In most cases, there is no need to restart services. -Vespa takes care of reconfiguring the system. -If a restart of services is required in some rare case, however, the output will notify -which services need restart to make the change effective. - -In the upcoming parts of the tutorials, we'll frequently deploy the -application changes in this manner. - - -## Feeding to Vespa - -We must index data before we can search for it. -This is called "feeding", and we'll get back to that in more detail in the next part of the tutorial. -For now, to test that everything is up and running, we'll feed in a single test document: -
    - -
    -$ vespa feed -t http://localhost:8080 doc.json
    -
    -
    - -The `-v` option will make vespa-cli print the http request: -
    - -
    -$ vespa document -v doc.json
    -
    -
    - -We can also feed using [Vespa document api](../../writing/document-v1-api-guide.html) directly. - -Once the feed operation is acknowledged by Vespa, the operation is visible in search. - - -## Querying Vespa - -We can query the endpoint using the vespa-cli's support for performing queries. -It uses the [Vespa query api](../../querying/query-api.html) to query vespa, -including `-v` in the command, we can see the exact endpoint and url request parameters used. - -
    - -
    -$ vespa query -v 'yql=select * from news where true'
    -
    -
    - -This example uses [YQL (Vespa Query Language)](../../querying/query-language.html) to -search for all documents of type `news`. This query request will return `1` result, which is the document we fed above. - -
    - -
    -$ vespa query \
    -  'yql=select * from news where userQuery()' \
    -  'query=hello world' \
    -  'default-index=title'
    -
    -
    - -Another query language example that searches for hello or world in the title. - -
    - -
    -$ vespa query \
    -  'yql=select * from news where title contains phrase("hello","world")'
    -
    -
    - -Another query language example that searches for the phrase "hello world" in the title. -In the [next part of the tutorial](news-2-basic-feeding-and-query.html) we'll demonstrate more query examples, -and also ranking and grouping of results. - - -## Remove documents -Run the following to remove the document from the index: -
    - -
    -$ vespa document -v remove id:news:news::1
    -
    -
    - -Well done! - - -## Stopping and starting Vespa - -Keep Vespa running to continue with the next steps in this tutorial set (skip the below). - -To stop Vespa, we can run the following commands: - -
    - -
    -$ docker exec vespa vespa-stop-services
    -$ docker exec vespa vespa-stop-configserver
    -
    -
    - -Likewise, to start the Vespa services: - -
    - -
    -$ docker exec vespa vespa-start-configserver
    -$ docker exec vespa vespa-start-services
    -
    -
    - -If a [restart is required](../../reference/schemas/schemas.html#changes-that-require-restart-but-not-re-feed) -due to changes in the application package, -these two steps are what you need to do. - -To wipe the index and restart: - -
    - -
    -$ docker exec vespa sh -c ' \
    -  vespa-stop-services && \
    -  vespa-remove-index -force && \
    -  vespa-start-services'
    -
    -
    - -You can stop and kill the Vespa container application like this: - -
    - -
    -$ docker stop vespa; docker rm -f vespa
    -
    -
    - -This will delete the Vespa application, including all data and configuration. See -[container tuning for production](../../operations/self-managed/docker-containers.html). - - -## Conclusion - -Our simple application should now be up and running. In the [next part -of the tutorial](news-2-basic-feeding-and-query.html), we'll start building -from this foundation. - - diff --git a/en/learn/tutorials/news-1-deploy-an-application.mdx b/en/learn/tutorials/news-1-deploy-an-application.mdx new file mode 100644 index 0000000000..d114168a53 --- /dev/null +++ b/en/learn/tutorials/news-1-deploy-an-application.mdx @@ -0,0 +1,222 @@ +--- +title: "News search and recommendation tutorial - getting started on Docker" +--- + +Our goal with this series is to set up a Vespa application for personalized news recommendations. We will do this in stages, starting with a simple news search system and gradually adding functionality as we go through the tutorial parts. + +The parts are: + +1. [Getting started](/en/learn/tutorials/news-1-deploy-an-application) - this part +2. [A basic news search application](/en/learn/tutorials/news-2-basic-feeding-and-query) - application packages, feeding, query +3. [News search](/en/learn/tutorials/news-3-searching) - sorting, grouping, and ranking +4. [Generating embeddings for users and news articles](/en/learn/tutorials/news-4-embeddings) +5. [News recommendation](/en/learn/tutorials/news-5-recommendation) - partial updates (news embeddings), ANNs, filtering +6. [News recommendation with searchers](/en/learn/tutorials/news-6-recommendation-with-searchers) - custom searchers, doc processors +7. [News recommendation with parent-child](/en/learn/tutorials/news-7-recommendation-with-parent-child) - parent-child, tensor ranking + +There are different entry points to this tutorial. This one is describing how to get started using Docker on your local machine. You can also deploy the application we are creating on [Vespa Cloud](https://cloud.vespa.ai). + +In this part, we will start with a minimal Vespa application to get used to some basic operations for running the application on Docker. In the next part of the tutorial, we'll start developing our application. + + + +**Prerequisites:** + +- Linux, macOS or Windows 10 Pro on x86\_64 or arm64, with [Podman Desktop](https://podman.io/) or [Docker Desktop](https://www.docker.com/products/docker-desktop/) installed, with an engine running. + - Alternatively, start the Podman daemon: + + ```bash + $ podman machine init --memory 6000 + $ podman machine start + ``` + - See [Docker Containers](/en/operations/self-managed/docker-containers) for system limits and other settings. +- For CPUs older than Haswell (2013), see [CPU Support](/en/operations/self-managed/cpu-support). +- Memory: Minimum 4 GB RAM dedicated to Docker/Podman. [Memory recommendations](/en/operations/self-managed/node-setup#memory-settings). +- Disk: Avoid `NO_SPACE` - the vespaengine/vespa container image + headroom for data requires disk space. [Read more](/en/writing/feed-block). +- [Homebrew](https://brew.sh/) to install the [Vespa CLI](/en/clients/vespa-cli), or download the Vespa CLI from [Github releases](https://github.com/vespa-engine/vespa/releases). +- Python3 for converting the dataset to Vespa JSON. +- `curl` to download the dataset and run the Vespa health-checks. +- [Java 17](https://openjdk.org/projects/jdk/17/) in part 6. +- [Apache Maven](https://maven.apache.org/install.html) in part 6. + + + + +**Note:** + +4 GB Docker memory is sufficient for the demo dataset in part 2. The full MIND dataset requires more, use 10 GB. + + + +In upcoming parts of this series, we will have some additional Python dependencies - we use [PyTorch](https://pytorch.org/) to train vector representations for news and users and train machine learning models for use in ranking. + + +## Installing vespa-cli + +This tutorial uses [Vespa-CLI](/en/clients/vespa-cli), Vespa CLI is the official command-line client for Vespa.ai. It is a single binary without any runtime dependencies and is available for Linux, macOS, and Windows. + +```bash +$ brew install vespa-cli +``` + +For the rest of this tutorial, you will be using localhost, so you need to configure your Vespa CLI to connect to localhost. Run the following to use endpoints on localhost: + +```bash +$ vespa config set target local +``` + + +## A minimal Vespa application + +This tutorial has a [companion sample application](https://github.com/vespa-engine/sample-apps/tree/master/news). Throughout the tutorial, we will be using support code from this application. Also, the final state of each tutorial can be found in the various `app-...` subdirectories. + +Let's start by cloning the sample application: + +```bash +$ vespa clone -f news news && cd news +``` + +The above downloads the `news` directory from the Vespa [sample apps repository](https://github.com/vespa-engine/sample-apps/) and places the contents in a folder called `news`. Use `--help` to see documentation for the vespa-cli utility: + +```bash +$ vespa clone --help +``` + +In the `news` directory, several pre-configured application packages are available. The `app-1-getting-started` directory contains a minimal Vespa application. There are two files there: + +- `services.xml` - defines the services that the application consists of +- `schemas/news.sd` - defines the schema for searchable content. + +We will revisit these files in the next part of the tutorial. + + +## Starting Vespa + +This application doesn't contain much at the moment, let's start up the application anyway by starting a Docker container to run it: + +```bash +$ docker pull vespaengine/vespa +$ docker run --detach --name vespa --hostname vespa-tutorial \ + --publish 8080:8080 --publish 19071:19071 --publish 19092:19092 \ + vespaengine/vespa +``` + +First, we pull the latest [vespa-image](https://hub.docker.com/r/vespaengine/vespa/) from the Docker hub, then we start it with the name `vespa`. This starts the Docker container and the initial Vespa services to be able to deploy an application. + +Starting the container can take a short while. Before continuing, make sure that the configuration service is running by using `vespa status`. + +```bash +$ vespa status deploy --wait 300 +``` + +With the config server up and running, deploy the application using vespa-cli: + +```bash +$ vespa deploy --wait 300 app-1-getting-started +``` + +The command uploads the application and verifies the content. If anything is wrong with the application, this step will fail with a failure description; Otherwise, this switches the application to a live status. + +Whenever you have a new version of your application, run the same command to deploy the application. In most cases, there is no need to restart services. Vespa takes care of reconfiguring the system. If a restart of services is required in some rare case, however, the output will notify which services need restart to make the change effective. + +In the upcoming parts of the tutorials, we'll frequently deploy the application changes in this manner. + + +## Feeding to Vespa + +We must index data before we can search for it. This is called "feeding", and we'll get back to that in more detail in the next part of the tutorial. For now, to test that everything is up and running, we'll feed in a single test document: + +```bash +$ vespa feed -t http://localhost:8080 doc.json +``` + +The `-v` option will make vespa-cli print the http request: + +```bash +$ vespa document -v doc.json +``` + +We can also feed using [Vespa document api](/en/writing/document-v1-api-guide) directly. + +Once the feed operation is acknowledged by Vespa, the operation is visible in search. + + +## Querying Vespa + +We can query the endpoint using the vespa-cli's support for performing queries. It uses the [Vespa query api](/en/querying/query-api) to query vespa, including `-v` in the command, we can see the exact endpoint and url request parameters used. + +```bash +$ vespa query -v 'yql=select * from news where true' +``` + +This example uses [YQL (Vespa Query Language)](/en/querying/query-language) to search for all documents of type `news`. This query request will return `1` result, which is the document we fed above. + +```bash +$ vespa query \ + 'yql=select * from news where userQuery()' \ + 'query=hello world' \ + 'default-index=title' +``` + +Another query language example that searches for hello or world in the title. + +```bash +$ vespa query \ + 'yql=select * from news where title contains phrase("hello","world")' +``` + +Another query language example that searches for the phrase "hello world" in the title. In the [next part of the tutorial](/en/learn/tutorials/news-2-basic-feeding-and-query) we'll demonstrate more query examples, and also ranking and grouping of results. + + +## Remove documents + +Run the following to remove the document from the index: + +```bash +$ vespa document -v remove id:news:news::1 +``` + +Well done! + + +## Stopping and starting Vespa + +Keep Vespa running to continue with the next steps in this tutorial set (skip the below). + +To stop Vespa, we can run the following commands: + +```bash +$ docker exec vespa vespa-stop-services +$ docker exec vespa vespa-stop-configserver +``` + +Likewise, to start the Vespa services: + +```bash +$ docker exec vespa vespa-start-configserver +$ docker exec vespa vespa-start-services +``` + +If a [restart is required](/en/reference/schemas/schemas#changes-that-require-restart-but-not-re-feed) due to changes in the application package, these two steps are what you need to do. + +To wipe the index and restart: + +```bash +$ docker exec vespa sh -c ' \ + vespa-stop-services && \ + vespa-remove-index -force && \ + vespa-start-services' +``` + +You can stop and kill the Vespa container application like this: + +```bash +$ docker stop vespa; docker rm -f vespa +``` + +This will delete the Vespa application, including all data and configuration. See [container tuning for production](/en/operations/self-managed/docker-containers). + + +## Conclusion + +Our simple application should now be up and running. In the [next part of the tutorial](/en/learn/tutorials/news-2-basic-feeding-and-query), we'll start building from this foundation. diff --git a/en/learn/tutorials/news-2-basic-feeding-and-query.md b/en/learn/tutorials/news-2-basic-feeding-and-query.md deleted file mode 100644 index 7f765496ff..0000000000 --- a/en/learn/tutorials/news-2-basic-feeding-and-query.md +++ /dev/null @@ -1,495 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "News search and recommendation tutorial - applications, feeding and querying" -redirect_from: - - /en/tutorials/news-2-basic-feeding-and-query ---- - - -This is the second part of the tutorial series for setting up a Vespa -application for personalized news recommendations. The parts are: - -1. [Getting started](news-1-deploy-an-application.html) -2. [A basic news search application](news-2-basic-feeding-and-query.html) - application packages, feeding, query -3. [News search](news-3-searching.html) - sorting, grouping, and ranking -4. [Generating embeddings for users and news articles](news-4-embeddings.html) -5. [News recommendation](news-5-recommendation.html) - partial updates (news embeddings), ANNs, filtering -6. [News recommendation with searchers](news-6-recommendation-with-searchers.html) - custom searchers, doc processors -7. [News recommendation with parent-child](news-7-recommendation-with-parent-child.html) - parent-child, tensor ranking -8. Advanced news recommendation - intermission - training a ranking model -9. Advanced news recommendation - ML models - -In this part, we will build upon the minimal Vespa application in the -previous part. First, we'll take a look at the [Microsoft News -Dataset](https://msnews.github.io/) (MIND), which we'll be using throughout -the tutorial. We'll use this to set up the search schema, deploy the -application and feed some data. We'll round off with some basic querying -before moving on to the next part of the tutorial: searching for content. - -For reference, the final state of this tutorial can be found in the -[app-2-feed-and-query](https://github.com/vespa-engine/sample-apps/tree/master/news/app-2-feed-and-query) -subdirectory of the `news` sample application. - - -## The Microsoft News Dataset - -During these tutorials, we will use the [Microsoft News -Dataset](https://msnews.github.io/) (MIND). This is a large-scale dataset for -news recommendation research. It contains over 160.000 articles, 15 million -impressions logs, and 1 million users. We will not use the full dataset in this -tutorial. To make the tutorial easier to follow along, we will use the much -smaller DEMO part containing only 5000 users. However, readers are free to -use the entire dataset at their own discretion. - -The [MIND dataset -description](https://github.com/msnews/msnews.github.io/blob/master/assets/doc/introduction.md) -contains an introduction to the contents of this dataset. For this tutorial, -there are particularly two pieces of data that we will use: - -- News article content which contains data such as title, abstract, news - category, and entities extracted from the title and abstract. -- Impressions which contain a list of news articles that were shown to a user, - labeled with whether the user clicked on them or not. - -We'll start with developing a search application, so we'll focus on the -news content at first. We'll use the impression data as we begin building -the recommendation system later in this series. - -Let's start by downloading the data. The `news` sample app directory will -be our starting point. We've included a script to download the data for us: - -
    - -
    -$ ./bin/download-mind.sh small
    -
    -
    - -The argument defines which dataset to download. Here, we download the `small` -dataset, but `small` and `large` are valid options. Both the training and -validation parts are downloaded to a directory called `mind`. -Both `train` and `dev` datasets will be downloaded. - -Taking a look at the data, in `mind/train/news.tsv`, we see tab-separated -lines like the following: - -``` -N16680 travel traveltripideas The Most Beautiful Natural Wonder in Every State While humans have built some impressive, gravity-defying, and awe-inspiring marvels here are the most photographed structures in the world the natural world may have us beat. https://www.msn.com/en-us/travel/traveltripideas/the-most-beautiful-natural-wonder-in-every-state/ss-AAF8Brj?ocid=chopendata [] [] -``` - -Here we see the news article id, a category, a subcategory, the title, an -abstract, and a URL to the article's content. The last two fields contain the -identified entities in the title and abstract. This particular news item has no -such entities. - -Note that the body content of the news article is retrievable by the URL. The -dataset repository contains tools to download this. For the purposes of -this tutorial, we won't be using this data, but feel free to download -yourself. - -Let's start building a Vespa application to make this data searchable. We'll -create the directory `my-app` under the `news` sample app directory to -contain your Vespa application: - -
    - -
    -$ mkdir -p my-app/schemas
    -
    -
    - - -## Application Packages - -Vespa Overview - -A Vespa [application package](../../basics/applications.html) is the -set of configuration files and Java plugins that together define the behavior -of a Vespa system: what functionality to use, the available document types, how -ranking will be done and how data will be processed during feeding and indexing. -The schema, e.g., `news.sd`, is a required part of an -application package — the other file needed is `services.xml`. - -For self-hosted multi-node deployments, a `hosts.xml` file is also needed. -For multi-node self-hosted deployments using `hosts.xml`, see -the [multinode high -availability](https://github.com/vespa-engine/sample-apps/tree/master/examples/operations/multinode-HA) -sample application. - -We mentioned these files in the previous part but didn't really explain them at the time. -We'll go through them here, starting with the specification of services. - - -### Services Specification - -The [services.xml](../../reference/applications/services/services.html) file defines the services that -make up the Vespa application — which services to run and how many nodes per -service. Write the following to `news/my-app/services.xml`: - -
    - -
    -<?xml version="1.0" encoding="UTF-8"?>
    -<services version="1.0">
    -
    -    <container id="default" version="1.0">
    -        <search />
    -        <document-api />
    -        <nodes>
    -            <node hostalias="node1" />
    -        </nodes>
    -    </container>
    -
    -    <content id="mind" version="1.0">
    -        <redundancy>1</redundancy>
    -        <documents>
    -            <document type="news" mode="index" />
    -        </documents>
    -        <nodes>
    -            <node hostalias="node1" distribution-key="0" />
    -        </nodes>
    -    </content>
    -
    -</services>
    -
    -
    - -Quite a lot is set up here: - -- `` defines the stateless [container cluster](../../applications/containers.html) for - document, query and result processing -- `` sets up the [query endpoint](../../querying/query-api.html). The default port - is 8080. See also [Securing Vespa with mutually authenticated TLS (mTLS)](https://blog.vespa.ai/securing-vespa-with-mutually-authenticated-tls/) - for how to use mTLS with Vespa. -- `` sets up the [document - endpoint](../../reference/api/document-v1.html) for feeding and visiting. -- `` defines the nodes required per service. (See the - [reference](../../reference/applications/services/container.html) for more on container - cluster setup). -- `` The stateful content cluster -- `` denotes how many copies to store of each document. -- `` assigns the document types in the _schema_ — the content - cluster capacity can be increased by adding node elements — see [elasticity](../../content/elasticity.html). (See also the - [reference](../../reference/applications/services/content.html) for more on content cluster - setup.) - - -### Schema - -In terms of data, Vespa operates with the notion of [documents](../../schemas/documents.html). -A document represents a single, searchable item in your system, e.g., a news article, a photo, or a user. -Each document type must be defined in the Vespa configuration through a [schema](../../basics/schemas.html). -Think of the document type in a schema as similar to a table definition in a relational database - -it consists of a set of fields, each with a given name, a specific type, and some optional properties. -The data fed into Vespa must match the structure of the schema, -and the results returned when searching will be in this format as well. -There is no dynamic field creation support in Vespa, -one can say Vespa document schemas are strongly typed. - -The `news` document type mentioned in the `services.xml` file above is defined in a schema. -Schemas are found under the `schemas` directory in the application package, -and **must** have the same name as the document type mentioned in `services.xml`. - -Given the MIND dataset described above, we'll set up the schema as follows. -Write the following to `news/my-app/schemas/news.sd`: - -
    - -
    -schema news {
    -    document news {
    -        field news_id type string {
    -            indexing: summary | attribute
    -            attribute: fast-search
    -        }
    -        field category type string {
    -            indexing: summary | attribute
    -        }
    -        field subcategory type string {
    -            indexing: summary | attribute
    -        }
    -        field title type string {
    -            indexing: index | summary
    -            index: enable-bm25
    -        }
    -        field abstract type string {
    -            indexing: index | summary
    -            index: enable-bm25
    -        }
    -        field body type string {
    -            indexing: index | summary
    -            index: enable-bm25
    -        }
    -        field url type string {
    -            indexing: index | summary
    -        }
    -        field date type int {
    -            indexing: summary | attribute
    -            attribute: fast-search
    -        }
    -        field clicks type int {
    -            indexing: summary | attribute
    -        }
    -        field impressions type int {
    -            indexing: summary | attribute
    -        }
    -    }
    -
    -    fieldset default {
    -        fields: title, abstract, body
    -    }
    -
    -}
    -
    -
    - -The `document` is wrapped inside another element called `schema`. -The name following these elements, here `news`, must be exactly the same for both. - -This document contains several fields. Each field has a -[type](../../reference/schemas/schemas.html#field), such as `string`, `int`, or -`tensor`. Fields also have properties. For instance, property `indexing` -configures the _indexing pipeline_ for a field, which defines how Vespa will -treat input during indexing — see [indexing -language](../../reference/writing/indexing-language.html). Each part of the -indexing pipeline is separated by the pipe character '|': - -- `index:` Create a search index for this field. -- `attribute:` Store this field in memory as an [attribute](../../content/attributes.html) - — for [sorting](../../reference/querying/sorting-language.html), [querying](../../querying/query-api.html), [ranking](../../basics/ranking.html) and - [grouping](../../querying/grouping.html). -- `summary:` Lets this field be part of the [document - summary](../../querying/document-summaries.html) in the result set. - -Here, we also use the [index](../../reference/schemas/schemas.html#index) property, -which sets up parameters for how Vespa should index the field. -For the `title`, `abstract`, and `body` fields, we configure Vespa to set up an -index compatible with [bm25 ranking](../../reference/ranking/rank-features.html#bm25) for text search. - - -## Deploy the Application Package - -With the two necessary files above, we are ready to deploy the application package. -Make sure it looks like this (use `ls` if `tree` is not installed): -
    -my-app/
    -├── schemas
    -│   └── news.sd
    -└── services.xml
    -
    - -
    - -
    -$ vespa deploy --wait 300 my-app
    -
    -
    - - -## Feeding data - -The data fed to Vespa must match the schema for the document type. -The downloaded MIND data must be converted to a valid Vespa JSON [document -format](../../reference/schemas/document-json-format.html) before it can be fed to Vespa: - -
    - -
    -$ python3 src/python/convert_to_vespa_format.py mind
    -
    -
    - -The argument is where to find the downloaded data above, which was in the -`mind` directory. This script creates a new file in that directory called -`vespa.json`. This contains all 28603 news articles in the data set. This -file can now be fed to Vespa. Use the method described in the previous part: - -
    - -
    -$ vespa feed mind/vespa.json --target http://localhost:8080
    -
    -
    - -`vespa feed` reads a JSON array of document operations, -or JSONL with one Vespa document JSON formatted operation per line. -Once the feed job finishes, all our 65 238 documents are searchable, let us do a quick query to verify: - -
    - -
    -$ vespa query -v 'yql=select * from news where true' 'hits=0'
    -
    -
    - -You can verify that specific documents are indexed by fetching documents by -document ID using the [Document V1 API](../../writing/document-v1-api-guide.html): - -
    - -
    -$ vespa document -v get id:news:news::N10864
    -
    -
    - - -## The first query - -Searching with Vespa is done using HTTP(S) GET or HTTP(S) POST requests, like: - - /search?yql=select..&hits=1... - -or with a JSON POST: - -```json -{ - "yql" : "select ..", - "hits" : 2 -} -``` - -The only mandatory parameter is the query, using either `yql=` or `query=`. -More details in the [Query API](../../querying/query-api.html). - -Consider the query: `select * from news where default contains "music"` - -Given the above schema, where the fields `title`, `abstract` and `body` are part of the `fieldset default`, -any document containing the word "music" in one or more of these fields matches that query. -Let's try that with either a GET query: - -
    - -
    -$ vespa query -v 'yql=select * from news where default contains "music"'  
    -
    -
    - -or a POST JSON query (Notice the *Content-Type* header specification): - -
    - -
    -$ curl -s -H "Content-Type: application/json" \
    -  --data '{"yql" : "select * from sources * where default contains \"music\""}' \
    -  http://localhost:8080/search/ | python3 -m json.tool
    -
    -
    - -{% include note.html content="Try the -Query Builder -application!" %} - -Looking at the output, please note: - -- The field `documentid` in the output and how it matches the value - we assigned to each put operation when feeding data to Vespa. -- Each hit has a property named relevance, which indicates how well the given - document matches our query, using a pre-defined default ranking function. - You have full control over ranking — more about ranking and ordering later. - The hits are sorted by this value (descending). -- When multiple hits have the same relevance score, their internal ordering is undefined. - However, their internal ordering will not change unless the documents are re-indexed. -- You can add `&trace.level=3` to dump query parsing details and execution plan, - see [query tracing](../../querying/query-api.html#query-tracing). -- The `totalCount` field at the top level contains the number of documents - that *matched* the query. -- Also note the `coverage` element, this tells us how many documents and nodes we searched over. -Coverage might be degraded, see [graceful degradation](../../performance/graceful-degradation.html). - -Prefer HTTP POST over GET in production due to limitations on URI length (64 Kb). - - -### Query examples - -
    - -
    -$ vespa query -v 'yql=select title from news where title contains "music"'  
    -
    -
    - -Again, this is a search for the single term "music", but this time explicitly in the `title` field. -This means that we only want to match documents that contain the word "music" in the field `title`. -As expected, you will see fewer hits for this query than for the previous one searching the `fieldset default`. -Also note that we scope the select to only return the title. - -
    - -
    -$ vespa query -v 'yql=select title from news where default contains "music" and default contains "festival"'  
    -
    -
    - -This is a query for the two terms "music" and "festival", combined with an `AND` operation; -it finds documents that match both terms, -not just one of them. - -
    - -
    -$ vespa query -v \
    -  'yql=select title from news where userQuery()' \
    -  'query=music festival' \
    -  'type=all'
    -
    -
    - -This combines YQL [userQuery()](../../reference/querying/yql.html#userquery) -with Vespa's [simple query language](../../reference/querying/simple-query-language.html). -In this case, documents needs to match both "music" and "festival". - -
    - -
    -$ vespa query -v \
    -  'yql=select title from news where userQuery()' \
    -  'query=music festival -beer' \
    -  'type=any'
    -
    -
    - -Above changes the query type from all to any, -so that all documents that match either (or both) of the terms are returned, -excluding documents with the term "beer". -Note that number of hits which are matched and ranked increases the computational complexity of the query execution. -See [using WAND with Vespa](../../ranking/wand.html) -for a way to speed up evaluation of type any/or-like queries. - -
    - -
    -$ vespa query -v \
    -  'yql=select title from news where userQuery()' \
    -  'query=music festival' \
    -  'type=phrase' \
    -  'default-index=title'
    -
    -
    - -Above searches using `type=phrase` which requires the exact phrase "music festival" to match in the title. - -
    - -
    -$ vespa query -v \
    -  'yql=select title from news where rank(userQuery(), title contains "festival")' \
    -  'query=music'
    -
    -
    - -Search for "music" in the default fieldset, boost documents with festival in the title. -The [rank()](../../reference/querying/yql.html#rank) query operator allows -us to retrieve on the first operand, -and have match ranking features calculated for the second operand argument. -The second and further operands does not impact recall (which documents match the query), -but can be used to tune precision (ordering of the results). -More on ranking in the next part of the tutorial. - - -## Conclusion - -We now have a Vespa application running with searchable data. In -the [next part of the tutorial](news-3-searching.html), we'll explore searching with -sorting, grouping, and ranking results. - - diff --git a/en/learn/tutorials/news-2-basic-feeding-and-query.mdx b/en/learn/tutorials/news-2-basic-feeding-and-query.mdx new file mode 100644 index 0000000000..32a2b70b19 --- /dev/null +++ b/en/learn/tutorials/news-2-basic-feeding-and-query.mdx @@ -0,0 +1,322 @@ +--- +title: "News search and recommendation tutorial - applications, feeding and querying" +--- + +This is the second part of the tutorial series for setting up a Vespa application for personalized news recommendations. The parts are: + +1. [Getting started](/en/learn/tutorials/news-1-deploy-an-application) +2. [A basic news search application](/en/learn/tutorials/news-2-basic-feeding-and-query) - application packages, feeding, query +3. [News search](/en/learn/tutorials/news-3-searching) - sorting, grouping, and ranking +4. [Generating embeddings for users and news articles](/en/learn/tutorials/news-4-embeddings) +5. [News recommendation](/en/learn/tutorials/news-5-recommendation) - partial updates (news embeddings), ANNs, filtering +6. [News recommendation with searchers](/en/learn/tutorials/news-6-recommendation-with-searchers) - custom searchers, doc processors +7. [News recommendation with parent-child](/en/learn/tutorials/news-7-recommendation-with-parent-child) - parent-child, tensor ranking +8. Advanced news recommendation - intermission - training a ranking model +9. Advanced news recommendation - ML models + +In this part, we will build upon the minimal Vespa application in the previous part. First, we'll take a look at the [Microsoft News Dataset](https://msnews.github.io/) (MIND), which we'll be using throughout the tutorial. We'll use this to set up the search schema, deploy the application and feed some data. We'll round off with some basic querying before moving on to the next part of the tutorial: searching for content. + +For reference, the final state of this tutorial can be found in the [app-2-feed-and-query](https://github.com/vespa-engine/sample-apps/tree/master/news/app-2-feed-and-query) subdirectory of the `news` sample application. + + +## The Microsoft News Dataset + +During these tutorials, we will use the [Microsoft News Dataset](https://msnews.github.io/) (MIND). This is a large-scale dataset for news recommendation research. It contains over 160.000 articles, 15 million impressions logs, and 1 million users. We will not use the full dataset in this tutorial. To make the tutorial easier to follow along, we will use the much smaller DEMO part containing only 5000 users. However, readers are free to use the entire dataset at their own discretion. + +The [MIND dataset description](https://github.com/msnews/msnews.github.io/blob/master/assets/doc/introduction.md) contains an introduction to the contents of this dataset. For this tutorial, there are particularly two pieces of data that we will use: + +- News article content which contains data such as title, abstract, news category, and entities extracted from the title and abstract. +- Impressions which contain a list of news articles that were shown to a user, labeled with whether the user clicked on them or not. + +We'll start with developing a search application, so we'll focus on the news content at first. We'll use the impression data as we begin building the recommendation system later in this series. + +Let's start by downloading the data. The `news` sample app directory will be our starting point. We've included a script to download the data for us: + +```bash +$ ./bin/download-mind.sh small +``` + +The argument defines which dataset to download. Here, we download the `small` dataset, but `small` and `large` are valid options. Both the training and validation parts are downloaded to a directory called `mind`. Both `train` and `dev` datasets will be downloaded. + +Taking a look at the data, in `mind/train/news.tsv`, we see tab-separated lines like the following: + +``` +N16680 travel traveltripideas The Most Beautiful Natural Wonder in Every State While humans have built some impressive, gravity-defying, and awe-inspiring marvels here are the most photographed structures in the world the natural world may have us beat. https://www.msn.com/en-us/travel/traveltripideas/the-most-beautiful-natural-wonder-in-every-state/ss-AAF8Brj?ocid=chopendata [] [] +``` + +Here we see the news article id, a category, a subcategory, the title, an abstract, and a URL to the article's content. The last two fields contain the identified entities in the title and abstract. This particular news item has no such entities. + +Note that the body content of the news article is retrievable by the URL. The dataset repository contains tools to download this. For the purposes of this tutorial, we won't be using this data, but feel free to download yourself. + +Let's start building a Vespa application to make this data searchable. We'll create the directory `my-app` under the `news` sample app directory to contain your Vespa application: + +```bash +$ mkdir -p my-app/schemas +``` + + +## Application Packages + +![Vespa Overview](/assets/img/vespa-overview.svg) + +A Vespa [application package](/en/basics/applications) is the set of configuration files and Java plugins that together define the behavior of a Vespa system: what functionality to use, the available document types, how ranking will be done and how data will be processed during feeding and indexing. The schema, e.g., `news.sd`, is a required part of an application package — the other file needed is `services.xml`. + +For self-hosted multi-node deployments, a `hosts.xml` file is also needed. For multi-node self-hosted deployments using `hosts.xml`, see the [multinode high availability](https://github.com/vespa-engine/sample-apps/tree/master/examples/operations/multinode-HA) sample application. + +We mentioned these files in the previous part but didn't really explain them at the time. We'll go through them here, starting with the specification of services. + + +### Services Specification + +The [services.xml](/en/reference/applications/services/services) file defines the services that make up the Vespa application — which services to run and how many nodes per service. Write the following to `news/my-app/services.xml`: + +```xml expandable + + + + + + + + + + + + + 1 + + + + + + + + + +``` + +Quite a lot is set up here: + +- `` defines the stateless [container cluster](/en/applications/containers) for document, query and result processing +- `` sets up the [query endpoint](/en/querying/query-api). The default port is 8080. +- `` sets up the [document endpoint](/en/reference/api/document-v1) for feeding and visiting. +- `` defines the nodes required per service. (See the [reference](/en/reference/applications/services/container) for more on container cluster setup). +- `` The stateful content cluster +- `` denotes how many copies to store of each document. +- `` assigns the document types in the *schema* — the content cluster capacity can be increased by adding node elements — see [elasticity](/en/content/elasticity). (See also the [reference](/en/reference/applications/services/content) for more on content cluster setup.) + + +### Schema + +In terms of data, Vespa operates with the notion of [documents](/en/schemas/documents). A document represents a single, searchable item in your system, e.g., a news article, a photo, or a user. Each document type must be defined in the Vespa configuration through a [schema](/en/basics/schemas). Think of the document type in a schema as similar to a table definition in a relational database - it consists of a set of fields, each with a given name, a specific type, and some optional properties. The data fed into Vespa must match the structure of the schema, and the results returned when searching will be in this format as well. There is no dynamic field creation support in Vespa, one can say Vespa document schemas are strongly typed. + +The `news` document type mentioned in the `services.xml` file above is defined in a schema. Schemas are found under the `schemas` directory in the application package, and **must** have the same name as the document type mentioned in `services.xml`. + +Given the MIND dataset described above, we'll set up the schema as follows. Write the following to `news/my-app/schemas/news.sd`: + +```sd expandable +schema news { + document news { + field news_id type string { + indexing: summary | attribute + attribute: fast-search + } + field category type string { + indexing: summary | attribute + } + field subcategory type string { + indexing: summary | attribute + } + field title type string { + indexing: index | summary + index: enable-bm25 + } + field abstract type string { + indexing: index | summary + index: enable-bm25 + } + field body type string { + indexing: index | summary + index: enable-bm25 + } + field url type string { + indexing: index | summary + } + field date type int { + indexing: summary | attribute + attribute: fast-search + } + field clicks type int { + indexing: summary | attribute + } + field impressions type int { + indexing: summary | attribute + } + } + + fieldset default { + fields: title, abstract, body + } + +} +``` + +The `document` is wrapped inside another element called `schema`. The name following these elements, here `news`, must be exactly the same for both. + +This document contains several fields. Each field has a [type](/en/reference/schemas/schemas#field), such as `string`, `int`, or `tensor`. Fields also have properties. For instance, property `indexing` configures the *indexing pipeline* for a field, which defines how Vespa will treat input during indexing — see [indexing language](/en/reference/writing/indexing-language). Each part of the indexing pipeline is separated by the pipe character '|': + +- `index:` Create a search index for this field. +- `attribute:` Store this field in memory as an [attribute](/en/content/attributes) — for [sorting](/en/reference/querying/sorting-language), [querying](/en/querying/query-api), [ranking](/en/basics/ranking) and [grouping](/en/querying/grouping). +- `summary:` Lets this field be part of the [document summary](/en/querying/document-summaries) in the result set. + +Here, we also use the [index](/en/reference/schemas/schemas#index) property, which sets up parameters for how Vespa should index the field. For the `title`, `abstract`, and `body` fields, we configure Vespa to set up an index compatible with [bm25 ranking](/en/reference/ranking/rank-features#bm25) for text search. + + +## Deploy the Application Package + +With the two necessary files above, we are ready to deploy the application package. Make sure it looks like this (use `ls` if `tree` is not installed): + +``` +my-app/ +├── schemas +│ └── news.sd +└── services.xml +``` + +```bash +$ vespa deploy --wait 300 my-app +``` + + +## Feeding data + +The data fed to Vespa must match the schema for the document type. The downloaded MIND data must be converted to a valid Vespa JSON [document format](/en/reference/schemas/document-json-format) before it can be fed to Vespa: + +```bash +$ python3 src/python/convert_to_vespa_format.py mind +``` + +The argument is where to find the downloaded data above, which was in the `mind` directory. This script creates a new file in that directory called `vespa.json`. This contains all 28603 news articles in the data set. This file can now be fed to Vespa. Use the method described in the previous part: + +```bash +$ vespa feed mind/vespa.json --target http://localhost:8080 +``` + +`vespa feed` reads a JSON array of document operations, or JSONL with one Vespa document JSON formatted operation per line. Once the feed job finishes, all our 65 238 documents are searchable, let us do a quick query to verify: + +```bash +$ vespa query -v 'yql=select * from news where true' 'hits=0' +``` + +You can verify that specific documents are indexed by fetching documents by document ID using the [Document V1 API](/en/writing/document-v1-api-guide): + +```bash +$ vespa document -v get id:news:news::N10864 +``` + + +## The first query + +Searching with Vespa is done using HTTP(S) GET or HTTP(S) POST requests, like: + +``` +/search?yql=select..&hits=1... +``` + +or with a JSON POST: + +```json +{ + "yql" : "select ..", + "hits" : 2 +} +``` + +The only mandatory parameter is the query, using either `yql=` or `query=`. More details in the [Query API](/en/querying/query-api). + +Consider the query: `select * from news where default contains "music"` + +Given the above schema, where the fields `title`, `abstract` and `body` are part of the `fieldset default`, any document containing the word "music" in one or more of these fields matches that query. Let's try that with either a GET query: + +```bash +$ vespa query -v 'yql=select * from news where default contains "music"' +``` + +or a POST JSON query (Notice the *Content-Type* header specification): + +```bash +$ curl -s -H "Content-Type: application/json" \ + --data '{"yql" : "select * from sources * where default contains \"music\""}' \ + http://localhost:8080/search/ | python3 -m json.tool +``` + + +Try the [Query Builder](https://github.com/vespa-engine/vespa/tree/master/client/js/app#query-builder) application! + + +Looking at the output, please note: + +- The field `documentid` in the output and how it matches the value we assigned to each put operation when feeding data to Vespa. +- Each hit has a property named relevance, which indicates how well the given document matches our query, using a pre-defined default ranking function. You have full control over ranking — more about ranking and ordering later. The hits are sorted by this value (descending). +- When multiple hits have the same relevance score, their internal ordering is undefined. However, their internal ordering will not change unless the documents are re-indexed. +- You can add `&trace.level=3` to dump query parsing details and execution plan, see [query tracing](/en/querying/query-api#query-tracing). +- The `totalCount` field at the top level contains the number of documents that *matched* the query. +- Also note the `coverage` element, this tells us how many documents and nodes we searched over. Coverage might be degraded, see [graceful degradation](/en/performance/graceful-degradation). + +Prefer HTTP POST over GET in production due to limitations on URI length (64 Kb). + + +### Query examples + +```bash +$ vespa query -v 'yql=select title from news where title contains "music"' +``` + +Again, this is a search for the single term "music", but this time explicitly in the `title` field. This means that we only want to match documents that contain the word "music" in the field `title`. As expected, you will see fewer hits for this query than for the previous one searching the `fieldset default`. Also note that we scope the select to only return the title. + +```bash +$ vespa query -v 'yql=select title from news where default contains "music" and default contains "festival"' +``` + +This is a query for the two terms "music" and "festival", combined with an `AND` operation; it finds documents that match both terms, not just one of them. + +```bash +$ vespa query -v \ + 'yql=select title from news where userQuery()' \ + 'query=music festival' \ + 'type=all' +``` + +This combines YQL [userQuery()](/en/reference/querying/yql#userquery) with Vespa's [simple query language](/en/reference/querying/simple-query-language). In this case, documents needs to match both "music" and "festival". + +```bash +$ vespa query -v \ + 'yql=select title from news where userQuery()' \ + 'query=music festival -beer' \ + 'type=any' +``` + +Above changes the query type from all to any, so that all documents that match either (or both) of the terms are returned, excluding documents with the term "beer". Note that number of hits which are matched and ranked increases the computational complexity of the query execution. See [using WAND with Vespa](/en/ranking/wand) for a way to speed up evaluation of type any/or-like queries. + +```bash +$ vespa query -v \ + 'yql=select title from news where userQuery()' \ + 'query=music festival' \ + 'type=phrase' \ + 'default-index=title' +``` + +Above searches using `type=phrase` which requires the exact phrase "music festival" to match in the title. + +```bash +$ vespa query -v \ + 'yql=select title from news where rank(userQuery(), title contains "festival")' \ + 'query=music' +``` + +Search for "music" in the default fieldset, boost documents with festival in the title. The [rank()](/en/reference/querying/yql#rank) query operator allows us to retrieve on the first operand, and have match ranking features calculated for the second operand argument. The second and further operands does not impact recall (which documents match the query), but can be used to tune precision (ordering of the results). More on ranking in the next part of the tutorial. + + +## Conclusion + +We now have a Vespa application running with searchable data. In the [next part of the tutorial](/en/learn/tutorials/news-3-searching), we'll explore searching with sorting, grouping, and ranking results. diff --git a/en/learn/tutorials/news-3-searching.md b/en/learn/tutorials/news-3-searching.md deleted file mode 100644 index 0513ed5f61..0000000000 --- a/en/learn/tutorials/news-3-searching.md +++ /dev/null @@ -1,561 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "News search and recommendation tutorial - searching" -redirect_from: - - /en/tutorials/news-3-searching ---- - - -This is the third part of the tutorial series for setting up a Vespa -application for personalized news recommendations. The parts are: - -1. [Getting started](news-1-deploy-an-application.html) -2. [A basic news search application](news-2-basic-feeding-and-query.html) - application packages, feeding, query -3. [News search](news-3-searching.html) - sorting, grouping, and ranking -4. [Generating embeddings for users and news articles](news-4-embeddings.html) -5. [News recommendation](news-5-recommendation.html) - partial updates (news embeddings), ANNs, filtering -6. [News recommendation with searchers](news-6-recommendation-with-searchers.html) - custom searchers, doc processors -7. [News recommendation with parent-child](news-7-recommendation-with-parent-child.html) - parent-child, tensor ranking -8. Advanced news recommendation - intermission - training a ranking model -9. Advanced news recommendation - ML models - -In the previous part, we converted the [Microsoft News Dataset](https://msnews.github.io/) (MIND) to Vespa, -and fed it to our application. -In this part, we'll issue searches in this content and look at sorting, grouping, and ranking the results. - -For reference, the final state of this tutorial can be found in the -[app-3-searching](https://github.com/vespa-engine/sample-apps/tree/master/news/app-3-searching) -sub-directory of the `news` sample application. - -Conceptually, Vespa has two stages when determining the exact result to return. -This first is "matching", where all the documents that match the query are found. -This is a binary decision; either the document matches or it doesn't. -For instance, when searching for a word, all documents that contain it are selected as candidates in this stage. - -The next stage determines the ordering of the results. -We can think of the results being ordered either by: - -- a fixed value, or attribute, in the document -- a function calculating a score - -Ordering by an attribute is called [sorting](../../reference/querying/sorting-language.html). -For instance, we can sort by decreasing `date`. -[Grouping](../../reference/querying/grouping-language.html) also works on attributes. -An example is to group the results by a `category` attribute. - -Calculating a score to order by is generally called "ranking". -As these scores are usually dependent upon both query and document, -they can also be called *relevance*. -Such expressions can be arbitrarily complex, but in general, -require some form of computation to find this score. -Ranking can be divided into [multiple rank phases](../../ranking/phased-ranking.html) as well. - -We'll start by looking at attribute-based sorting and grouping before moving on to ranking. - - -## What is an attribute? - -We saw multiple examples of attributes in the `news.sd` schema, for instance: - - field date type int { - indexing: summary | attribute - attribute: fast-search - } - -Note that this `date` field has been defined as an `int` here, and when -feeding document, we convert the date to the format `YYYYMMDD`. - -An [attribute](../../content/attributes.html) is an in-memory field - -this is different from _index_ fields, which may be moved to a disk-based index -as more documents are added and the index grows. -Since attributes are kept in memory, -they are excellent for fields that require fast access for many documents, e.g. -fields used for sorting, ranking or grouping query results. -The downside is higher memory usage. - -In the above field definition we have included an additional property `attribute: fast-search` -which will inform Vespa that we want to build inverted index structures (dictionary and posting lists) -for *fast* *matching* in the field. -See more about -[when to use fast-search](../../performance/feature-tuning.html#when-to-use-fast-search-for-attribute-fields) -in the performance feature tuning section. - - -### Example queries using attribute field -
    - -
    -$ vespa query -v 'yql=select * from news where default contains "20191110"'
    -
    -
    - -This is a single-term query for the term `20191110` in the `default` fieldset. -In the schema, the field `date` is not included in the `default` fieldset, so no results are found. -Instead, we search using `=` which can be used for numeric and bool fields: - -
    - -
    -$ vespa query -v 'yql=select * from news where date=20191110'
    -
    -
    - -To get documents that were created 10 November 2019, and whose `date` field is -`20191110`, replace `default` with `date` in the YQL query string. - -
    - -
    -$ vespa query -v 'yql=select * from news where date=20191110 and default contains "weather"'
    -
    -
    - -This is a query with two terms; a search in the `default` field set for the term -"weather" combined with a search in the `date` field for the value `20191110`. - - -### Range searches - -The examples above searched over `date` just as any other field, -and requested documents where the value was exactly `20191110`. -Since the field is of type _int_, however, we can use it for _range searches_ as well, -using the "less than" and "greater than" operators (`<` and `>`). The query: - -
    - -
    -$ vespa query -v 'yql=select * from news where date < 20191110'
    -
    -
    - -finds all documents where the value of `date` is less than `20191110`, -i.e. all documents from before 10 November 2019, while - -
    - -
    -$ vespa query -v 'yql=select * from news where date <= 20191110 AND date >= 20191108'
    -
    -
    - -finds all news articles from 8 November 2019 to 10 November 2019, inclusive. - - -### Sorting on attribute fields - -The first feature we will look at is how an attribute can be used to change the hit order. -By now, you have probably noticed that hits are returned in order of descending relevance, -i.e. how well the document matches the query — -if not, take a moment to verify this. -You might ask how Vespa does this since we haven't even touched upon ranking yet. -The answer is that Vespa uses its [nativeRank](../../ranking/nativerank.html) score -unless anything else is defined in the schema. -We'll get back to defining custom ranking later on. - -Now send the following query to Vespa, and look at the order of the hits: - -
    - -
    -$ vespa query -v 'yql=select date from news where default contains phrase("music","festival") order by date' 
    -
    -
    - -By default, sorting is done in ascending order. -This can also be specified by appending `asc` after the sort attribute name. -Use `desc` to sort the results in descending order: - -
    - -
    -$ vespa query -v 'yql=select date from news where default contains phrase("music","festival") order by date desc' 
    -
    -
    - -Attempting to sort on a field which is not defined as attribute in the schema will create an error. - - -### Query time result grouping - -[Grouping](../../querying/grouping.html) is the concept of looking through all matching documents at query-time -and then performing operations with specified fields across all the documents — -some common use cases include: - -- Find all the unique values for a given field, make **one group per unique - value**, and return the count of documents per group. -- **Group documents by time and date** in fixed-width or custom-width buckets. - An example of fixed-width buckets could be to group all documents by year, - while an example of custom buckets could be to sort bug tickets by date of - creation into the buckets _Today_, _Past Week_, _Past Month_, _Past Year_, and _Everything else_. -- Calculate the **minimum/maximum/average value** for a given field. -- [Result diversification](https://blog.vespa.ai/result-diversification-with-vespa/), e.g. - to only display 3 best ranking results per category for up to 5 categories. - -Displaying such groups and their sizes (in terms of matching documents per group) on a search result page, -with a link to each such group, -is a common way to let users refine searches. -For now, we will only do a simple grouping query to get a list of unique values for `category`, -ordered by the number of documents they occur in and top 3 is shown: - -
    - -
    -$ vespa query -v 'yql=select * from news where true limit 0 | all(group(category) max(3) order(-count())each(output(count())))'
    -
    -
    - -Note that expression after the pipe (`|`): -this is the grouping expression that determines how grouping will be performed. -You can read more about the grouping syntax in the -[grouping reference documentation](../../reference/querying/grouping-language.html). -`limit 0` is an alternative syntax for the native `hits` parameter, -in this case we are only interested in the group counts, so we set limit to 0. - -For this query, you will get something like the following: -```json -{ - "root": { - "children": [ - { - "children": [ - { - "children": [ - { - "fields": { - "count()": 9115 - }, - "id": "group:string:news", - "relevance": 1.0, - "value": "news" - }, - { - "fields": { - "count()": 6765 - }, - "id": "group:string:sports", - "relevance": 0.6666666666666666, - "value": "sports" - }, - { - "fields": { - "count()": 1886 - }, - "id": "group:string:finance", - "relevance": 0.3333333333333333, - "value": "finance" - } - ], - "continuation": { - "next": "BGAAABEBGBC" - }, - "id": "grouplist:category", - "label": "category", - "relevance": 1.0 - } - ], - "continuation": { - "this": "" - }, - "id": "group:root:0", - "relevance": 1.0 - } - ], - "coverage": { - "coverage": 100, - "documents": 28603, - "full": true, - "nodes": 1, - "results": 1, - "resultsFull": 1 - }, - "fields": { - "totalCount": 28603 - }, - "id": "toplevel", - "relevance": 1.0 - } -} -``` - -So, the three most common unique values of `category` among the indexed documents -(for the demo data set) are: - -- `news` with 9115 articles -- `sports` with 6765 articles -- `finance` with 1886 articles - -Try to change the filter part of the YQL+ expression — the `where` clause — -to a text match of "weather", or restrict `date` to be less than 20191110, -and see how the list of unique values changes as the set of matching documents for your query changes. -If you try to search for a single term that is *not* present in the document set, -you will see that the list of groups is empty as no documents have been matched. -Vespa grouping is only applied over the documents which matched the query. - -In the following example we use the [select](../../reference/api/query.html#select) -parameter to pass the grouping specification: - -
    - -
    -$ vespa query -v 'yql=select * from news where userQuery() limit 0' \
    -  'select=all(group(category) max(2) each(max(2)each(output(summary()))))' \
    -  'query=drinks'
    -
    -
    - -This request searches for drinks, groups by category and for each unique category output the -2 top ranking hits (according to the rank profile used). -Groups are sorted by default by maximum relevance in the group. -Notice that we also set an upper limit on the number of unique groups my the outermost max. -This is important in cases with many unique values. -See also [Result diversification using Vespa result grouping](https://blog.vespa.ai/result-diversification-with-vespa/). - -Please refer to the [grouping guide](../../querying/grouping.html) for more information and examples using Vespa grouping. -Similar to with sorting, -attempting to group on a field which is not defined as attribute in the schema will create an error. - - -### Matching - index versus attribute - -Before we move on to ranking, -it's important to know some of the differences between `index` and `attribute`. - -#### Matching - -Consider the `title` field from our schema, -and the document for the article with title "A little snow causes a big mess, more than 100 crashes on Minnesota roads". -In the original input, the value for `title` is a string built of up the 14 words, -with a single white space character between them. -How should we be able to search this field? - -For string fields with `index` which defaults to `match:text`, Vespa performs linguistic processing of the string. -This includes [tokenization](../../linguistics/linguistics-opennlp.html#tokenization), [normalization](../../linguistics/linguistics-opennlp.html#normalization) -and language dependent [stemming](../../linguistics/linguistics-opennlp.html#stemming) of the string. - -In our example, this means that the string above is split into the 14 tokens, -enabling Vespa to match this document for: - -- the single-term queries such as "Michigan", "snow" and "roads", -- the exact phrase query "A little snow causes a big mess, more than 100 crashes on Minnesota roads", -- a query with two or more tokens in either order (e.g. "minnesota crashes"). - -This is how we all have come to expect normal free text search to work. - -However, string fields with `indexing:attributes` do not support `match:text`, -only *exact matching* or *prefix matching*. -Exact matching is the default, and, as the name implies, -it requires you to search for the exact contents of the field in order to get a match. -See supported [match](../../reference/schemas/schemas.html#match) modes -and the differences in support between `attribute` and `index`. - -#### Memory usage - -Attributes are stored in memory, as opposed to fields with `index`, -where the data is mostly kept on disk but paged in on-demand and cached by the OS buffer cache. -Even with large flavor types, -one will notice that it is not practical to define all the document type fields as attributes, -as it will heavily restrict the number of documents per search node. -Some Vespa applications have more than 1 billion documents per node — -having megabytes of text per document in memory per document might not be cost-effective. - -#### When to use attributes - -There are both advantages and drawbacks of using attributes — -it enables sorting, ranking and grouping, -but requires more memory and does not support `match:text` capabilities. -Attribute fields do support at least one order higher update throughput then regular `index` fields, -see [partial updates with Vespa](../../writing/partial-updates.html). - -When to use attributes depends on the application; -in general, use attributes for: - -- fields used for sorting, e.g. a last-update timestamp, -- fields used for grouping, e.g. category, and -- fields accessed in ranking expressions - -Finally, all numeric and [tensors](../../ranking/tensor-user-guide.html) fields used in ranking must be defined with attribute. - -#### Combining index and attribute - - field category type string { - indexing: summary | attribute | index - } - -Combining both index and attribute for the same field is supported. -In this case, we can sort and group on the category, -while search or matching will be using index matching with `match:text`, -which will tokenize and stem the contents of the field. - - -## Relevance and Ranking - -[Ranking](../../basics/ranking.html) and relevance were briefly mentioned above; -what is really the relevance of a hit? -How can one change the relevance calculations? -It is time to introduce _rank profiles_ and _ranking expressions_ — -simple, yet powerful methods for tuning the relevance. - -Relevance is a measure of how well a given document matches a query. -The default relevance is calculated by a formula that takes several *matching* factors into consideration. -It computes, in essence, how well the document matches the terms in the query. -The default Vespa ranking function and its limitations is described in [ranking with nativeRank](../../ranking/nativerank.html). - -Ranking signals that might be useful, like freshness (the age of the document compared to the time of the query) -or any other document or query features, -are not a part of the nativeRank calculation. -These need to be added to the ranking function depending on application specifics. - -Some use cases for tweaking the relevance calculations: - -- Personalize search results based on some property; age, nationality, - language, friends and friends of friends. -- Rank fresh (age) documents higher, while still considering other relevance measures. -- Rank documents by geographical location, searching for relevant resources nearby. -- Rank documents by machine learned ranking functions - Learning to Rank (LTR). -- Rank documents by business constraints - For example by product availability. - -Vespa allows creating any number of _rank profiles_: -named collections of ranking and relevance calculations that one can choose from at query time. -A number of built-in functions and expressions are available to create highly -specialized ranking expressions and users can define their own functions in the schema. - - -### News article popularity signal - -During the conversion of the news dataset, the conversion script counted both the -number of times a news article was shown (impressions) and how many clicks it received. -A high number of clicks relative to impressions indicates that the news article was generally popular. -We can use this signal in our ranking. -Since both clicks and impressions are attribute fields, -these fields can be [updated](../../writing/partial-updates.html) at scale with very high throughput. - -We can use this signal in our ranking, -by including a `popularity` rank profile, -as defined below at the bottom of `schemas/news.sd`. -Note that rank profiles are defined outside the `document` block: - -
    - -
    -schema news {
    -    document news {
    -        field news_id type string {
    -            indexing: summary | attribute
    -            attribute: fast-search
    -        }
    -        field category type string {
    -            indexing: summary | attribute
    -        }
    -        field subcategory type string {
    -            indexing: summary | attribute
    -        }
    -        field title type string {
    -            indexing: index | summary
    -            index: enable-bm25
    -        }
    -        field abstract type string {
    -            indexing: index | summary
    -            index: enable-bm25
    -        }
    -        field body type string {
    -            indexing: index | summary
    -            index: enable-bm25
    -        }
    -        field url type string {
    -            indexing: index | summary
    -        }
    -        field date type int {
    -            indexing: summary | attribute
    -            attribute: fast-search
    -        }
    -        field clicks type int {
    -            indexing: summary | attribute
    -        }
    -        field impressions type int {
    -            indexing: summary | attribute
    -        }
    -    }
    -
    -    fieldset default {
    -        fields: title, abstract, body
    -    }
    -
    -    rank-profile popularity inherits default {
    -        function popularity() {
    -            expression: if (attribute(impressions) > 0, attribute(clicks) / attribute(impressions), 0)
    -        }
    -        first-phase {
    -            expression: nativeRank(title, abstract) + 10 * popularity
    -        }
    -    }
    -}
    -
    -
    - -- `rank-profile popularity inherits default` - - This configures Vespa to create a new rank profile named `popularity`, - which inherits all the properties of the default rank-profile; - only properties that are explicitly defined, or overridden, - will differ from those of the default rank-profile. - -- `first-phase` - - Relevance calculations in Vespa are two-phased. The calculations done in the - first phase are performed on every single document matching your query, - while the second phase calculations are only done on the top `n` documents - as determined by the calculations done in the first phase. - See [phased ranking](../../ranking/phased-ranking.html). - -- `function popularity()` - - This sets up a function that can be called from other expressions. - This function calculates the number of clicks divided by impressions for indicating popularity. - However, this isn't really the best way of calculating this as an - article with a low number of impressions can score high on such a value, - even though uncertainty is high. - -- `expression: nativeRank + 10 * popularity` - - This expression is used to rank documents. - Here, the default ranking expression — the `nativeRank` of the `default` fieldset — - is included to make the query relevant, - while the second term calls the `popularity` function. - The weighted sum of these two terms is the final relevance for each document. - Note that the weight here, `10`, is set by observation. - A better approach would be to learn such values using machine learning. - -More information can be found in the [schema reference](../../reference/schemas/schemas.html#rank-profile). - -Deploy the _popularity_ rank profile: - -
    - -
    -$ vespa deploy --wait 300 my-app
    -
    -
    - -Run a query: - -
    - -
    -$ vespa query -v \
    -  'yql=select * from news where default contains "music"' \
    -  'ranking=popularity' 
    -
    -
    - -and find documents with high `popularity` values at the top. -Note that we must specify the rank profile to use with the run time `ranking` parameter. - - -## Conclusion - -After completing this part of the tutorial, you should now have a basic -understanding of how you can load data into Vespa and effectively search for content. -In the [next part of the tutorial](news-4-embeddings.html), -we'll start with the basics for transforming this search app into a recommendation system. - - diff --git a/en/learn/tutorials/news-3-searching.mdx b/en/learn/tutorials/news-3-searching.mdx new file mode 100644 index 0000000000..b87b204802 --- /dev/null +++ b/en/learn/tutorials/news-3-searching.mdx @@ -0,0 +1,340 @@ +--- +# Copyright Vespa.ai. All rights reserved. +title: "News search and recommendation tutorial - searching" +--- + + +This is the third part of the tutorial series for setting up a Vespa application for personalized news recommendations. The parts are: + +1. [Getting started](/en/learn/tutorials/news-1-deploy-an-application) +2. [A basic news search application](/en/learn/tutorials/news-2-basic-feeding-and-query) - application packages, feeding, query +3. [News search](/en/learn/tutorials/news-3-searching) - sorting, grouping, and ranking +4. [Generating embeddings for users and news articles](/en/learn/tutorials/news-4-embeddings) +5. [News recommendation](/en/learn/tutorials/news-5-recommendation) - partial updates (news embeddings), ANNs, filtering +6. [News recommendation with searchers](/en/learn/tutorials/news-6-recommendation-with-searchers) - custom searchers, doc processors +7. [News recommendation with parent-child](/en/learn/tutorials/news-7-recommendation-with-parent-child) - parent-child, tensor ranking +8. Advanced news recommendation - intermission - training a ranking model +9. Advanced news recommendation - ML models + +In the previous part, we converted the [Microsoft News Dataset](https://msnews.github.io/) (MIND) to Vespa, and fed it to our application. In this part, we'll issue searches in this content and look at sorting, grouping, and ranking the results. + +For reference, the final state of this tutorial can be found in the [app-3-searching](https://github.com/vespa-engine/sample-apps/tree/master/news/app-3-searching) sub-directory of the `news` sample application. + +Conceptually, Vespa has two stages when determining the exact result to return. This first is "matching", where all the documents that match the query are found. This is a binary decision; either the document matches or it doesn't. For instance, when searching for a word, all documents that contain it are selected as candidates in this stage. + +The next stage determines the ordering of the results. We can think of the results being ordered either by: + +- a fixed value, or attribute, in the document +- a function calculating a score + +Ordering by an attribute is called [sorting](/en/reference/querying/sorting-language). For instance, we can sort by decreasing `date`. [Grouping](/en/reference/querying/grouping-language) also works on attributes. An example is to group the results by a `category` attribute. + +Calculating a score to order by is generally called "ranking". As these scores are usually dependent upon both query and document, they can also be called *relevance*. Such expressions can be arbitrarily complex, but in general, require some form of computation to find this score. Ranking can be divided into [multiple rank phases](/en/ranking/phased-ranking) as well. + +We'll start by looking at attribute-based sorting and grouping before moving on to ranking. + + +## What is an attribute? + +We saw multiple examples of attributes in the `news.sd` schema, for instance: + +`field date type int { indexing: summary | attribute attribute: fast-search }` + +Note that this `date` field has been defined as an `int` here, and when feeding document, we convert the date to the format `YYYYMMDD`. + +An [attribute](/en/content/attributes) is an in-memory field - this is different from _index_ fields, which may be moved to a disk-based index as more documents are added and the index grows. Since attributes are kept in memory, they are excellent for fields that require fast access for many documents, e.g. fields used for sorting, ranking or grouping query results. The downside is higher memory usage. + +In the above field definition we have included an additional property `attribute: fast-search` which will inform Vespa that we want to build inverted index structures (dictionary and posting lists) for *fast* *matching* in the field. See more about [when to use fast-search](/en/performance/feature-tuning#when-to-use-fast-search-for-attribute-fields) in the performance feature tuning section. + + +### Example queries using attribute field + +```bash +$ vespa query -v 'yql=select * from news where default contains "20191110"' +``` + + +This is a single-term query for the term `20191110` in the `default` fieldset. In the schema, the field `date` is not included in the `default` fieldset, so no results are found. Instead, we search using `=` which can be used for numeric and bool fields: + + +```bash +$ vespa query -v 'yql=select * from news where date=20191110' +``` + + +To get documents that were created 10 November 2019, and whose `date` field is `20191110`, replace `default` with `date` in the YQL query string. + + +```bash +$ vespa query -v 'yql=select * from news where date=20191110 and default contains "weather"' +``` + + +This is a query with two terms; a search in the `default` field set for the term "weather" combined with a search in the `date` field for the value `20191110`. + + +### Range searches + +The examples above searched over `date` just as any other field, and requested documents where the value was exactly `20191110`. Since the field is of type _int_, however, we can use it for _range searches_ as well, using the "less than" and "greater than" operators (`<` and `>`). The query: + + +```bash +$ vespa query -v 'yql=select * from news where date < 20191110' +``` + + +finds all documents where the value of `date` is less than `20191110`, i.e. all documents from before 10 November 2019, while + + +```bash +$ vespa query -v 'yql=select * from news where date = 20191108' +``` + + +finds all news articles from 8 November 2019 to 10 November 2019, inclusive. + + +### Sorting on attribute fields + +The first feature we will look at is how an attribute can be used to change the hit order. By now, you have probably noticed that hits are returned in order of descending relevance, i.e. how well the document matches the query — if not, take a moment to verify this. You might ask how Vespa does this since we haven't even touched upon ranking yet. The answer is that Vespa uses its [nativeRank](/en/ranking/nativerank) score unless anything else is defined in the schema. We'll get back to defining custom ranking later on. + +Now send the following query to Vespa, and look at the order of the hits: + + +```bash +$ vespa query -v 'yql=select date from news where default contains phrase("music","festival") order by date' +``` + + +By default, sorting is done in ascending order. This can also be specified by appending `asc` after the sort attribute name. Use `desc` to sort the results in descending order: + + +```bash +$ vespa query -v 'yql=select date from news where default contains phrase("music","festival") order by date desc' +``` + + +Attempting to sort on a field which is not defined as attribute in the schema will create an error. + + +### Query time result grouping + +[Grouping](/en/querying/grouping) is the concept of looking through all matching documents at query-time and then performing operations with specified fields across all the documents — some common use cases include: + +- Find all the unique values for a given field, make **one group per unique value**, and return the count of documents per group. +- **Group documents by time and date** in fixed-width or custom-width buckets. An example of fixed-width buckets could be to group all documents by year, while an example of custom buckets could be to sort bug tickets by date of creation into the buckets _Today_, _Past Week_, _Past Month_, _Past Year_, and _Everything else_. +- Calculate the **minimum/maximum/average value** for a given field. +- [Result diversification](https://blog.vespa.ai/result-diversification-with-vespa/), e.g. to only display 3 best ranking results per category for up to 5 categories. + +Displaying such groups and their sizes (in terms of matching documents per group) on a search result page, with a link to each such group, is a common way to let users refine searches. For now, we will only do a simple grouping query to get a list of unique values for `category`, ordered by the number of documents they occur in and top 3 is shown: + + +```bash +$ vespa query -v 'yql=select * from news where true limit 0 | all(group(category) max(3) order(-count())each(output(count())))' +``` + + +Note that expression after the pipe (`|`): this is the grouping expression that determines how grouping will be performed. You can read more about the grouping syntax in the [grouping reference documentation](/en/reference/querying/grouping-language). `limit 0` is an alternative syntax for the native `hits` parameter, in this case we are only interested in the group counts, so we set limit to 0. + +For this query, you will get something like the following: +```json expandable +{ + "root": { + "children": [ + { + "children": [ + { + "children": [ + { + "fields": { + "count()": 9115 + }, + "id": "group:string:news", + "relevance": 1.0, + "value": "news" + }, + { + "fields": { + "count()": 6765 + }, + "id": "group:string:sports", + "relevance": 0.6666666666666666, + "value": "sports" + }, + { + "fields": { + "count()": 1886 + }, + "id": "group:string:finance", + "relevance": 0.3333333333333333, + "value": "finance" + } + ], + "continuation": { + "next": "BGAAABEBGBC" + }, + "id": "grouplist:category", + "label": "category", + "relevance": 1.0 + } + ], + "continuation": { + "this": "" + }, + "id": "group:root:0", + "relevance": 1.0 + } + ], + "coverage": { + "coverage": 100, + "documents": 28603, + "full": true, + "nodes": 1, + "results": 1, + "resultsFull": 1 + }, + "fields": { + "totalCount": 28603 + }, + "id": "toplevel", + "relevance": 1.0 + } +} +``` + +So, the three most common unique values of `category` among the indexed documents (for the demo data set) are: + +- `news` with 9115 articles +- `sports` with 6765 articles +- `finance` with 1886 articles + +Try to change the filter part of the YQL+ expression — the `where` clause — to a text match of "weather", or restrict `date` to be less than 20191110, and see how the list of unique values changes as the set of matching documents for your query changes. If you try to search for a single term that is *not* present in the document set, you will see that the list of groups is empty as no documents have been matched. Vespa grouping is only applied over the documents which matched the query. + +In the following example we use the [select](/en/reference/api/query#select) parameter to pass the grouping specification: + + +```bash +$ vespa query -v 'yql=select * from news where userQuery() limit 0' \ 'select=all(group(category) max(2) each(max(2)each(output(summary()))))' \ 'query=drinks' +``` + + +This request searches for drinks, groups by category and for each unique category output the 2 top ranking hits (according to the rank profile used). Groups are sorted by default by maximum relevance in the group. Notice that we also set an upper limit on the number of unique groups my the outermost max. This is important in cases with many unique values. See also [Result diversification using Vespa result grouping](https://blog.vespa.ai/result-diversification-with-vespa/). + +Please refer to the [grouping guide](/en/querying/grouping) for more information and examples using Vespa grouping. Similar to with sorting, attempting to group on a field which is not defined as attribute in the schema will create an error. + + +### Matching - index versus attribute + +Before we move on to ranking, it's important to know some of the differences between `index` and `attribute`. + +#### Matching + +Consider the `title` field from our schema, and the document for the article with title "A little snow causes a big mess, more than 100 crashes on Minnesota roads". In the original input, the value for `title` is a string built of up the 14 words, with a single white space character between them. How should we be able to search this field? + +For string fields with `index` which defaults to `match:text`, Vespa performs linguistic processing of the string. This includes [tokenization](/en/linguistics/linguistics-opennlp#tokenization), [normalization](/en/linguistics/linguistics-opennlp#normalization) and language dependent [stemming](/en/linguistics/linguistics-opennlp#stemming) of the string. + +In our example, this means that the string above is split into the 14 tokens, enabling Vespa to match this document for: + +- the single-term queries such as "Michigan", "snow" and "roads", +- the exact phrase query "A little snow causes a big mess, more than 100 crashes on Minnesota roads", +- a query with two or more tokens in either order (e.g. "minnesota crashes"). + +This is how we all have come to expect normal free text search to work. + +However, string fields with `indexing:attributes` do not support `match:text`, only *exact matching* or *prefix matching*. Exact matching is the default, and, as the name implies, it requires you to search for the exact contents of the field in order to get a match. See supported [match](/en/reference/schemas/schemas#match) modes and the differences in support between `attribute` and `index`. + +#### Memory usage + +Attributes are stored in memory, as opposed to fields with `index`, where the data is mostly kept on disk but paged in on-demand and cached by the OS buffer cache. Even with large flavor types, one will notice that it is not practical to define all the document type fields as attributes, as it will heavily restrict the number of documents per search node. Some Vespa applications have more than 1 billion documents per node — having megabytes of text per document in memory per document might not be cost-effective. + +#### When to use attributes + +There are both advantages and drawbacks of using attributes — it enables sorting, ranking and grouping, but requires more memory and does not support `match:text` capabilities. Attribute fields do support at least one order higher update throughput then regular `index` fields, see [partial updates with Vespa](/en/writing/partial-updates). + +When to use attributes depends on the application; in general, use attributes for: + +- fields used for sorting, e.g. a last-update timestamp, +- fields used for grouping, e.g. category, and +- fields accessed in ranking expressions + +Finally, all numeric and [tensors](/en/ranking/tensor-user-guide) fields used in ranking must be defined with attribute. + +#### Combining index and attribute + +`field category type string { indexing: summary | attribute | index }` + +Combining both index and attribute for the same field is supported. In this case, we can sort and group on the category, while search or matching will be using index matching with `match:text`, which will tokenize and stem the contents of the field. + + +## Relevance and Ranking + +[Ranking](/en/basics/ranking) and relevance were briefly mentioned above; what is really the relevance of a hit? How can one change the relevance calculations? It is time to introduce _rank profiles_ and _ranking expressions_ — simple, yet powerful methods for tuning the relevance. + +Relevance is a measure of how well a given document matches a query. The default relevance is calculated by a formula that takes several *matching* factors into consideration. It computes, in essence, how well the document matches the terms in the query. The default Vespa ranking function and its limitations is described in [ranking with nativeRank](/en/ranking/nativerank). + +Ranking signals that might be useful, like freshness (the age of the document compared to the time of the query) or any other document or query features, are not a part of the nativeRank calculation. These need to be added to the ranking function depending on application specifics. + +Some use cases for tweaking the relevance calculations: + +- Personalize search results based on some property; age, nationality, language, friends and friends of friends. +- Rank fresh (age) documents higher, while still considering other relevance measures. +- Rank documents by geographical location, searching for relevant resources nearby. +- Rank documents by machine learned ranking functions - Learning to Rank (LTR). +- Rank documents by business constraints - For example by product availability. + +Vespa allows creating any number of _rank profiles_: named collections of ranking and relevance calculations that one can choose from at query time. A number of built-in functions and expressions are available to create highly specialized ranking expressions and users can define their own functions in the schema. + + +### News article popularity signal + +During the conversion of the news dataset, the conversion script counted both the number of times a news article was shown (impressions) and how many clicks it received. A high number of clicks relative to impressions indicates that the news article was generally popular. We can use this signal in our ranking. Since both clicks and impressions are attribute fields, these fields can be [updated](/en/writing/partial-updates) at scale with very high throughput. + +We can use this signal in our ranking, by including a `popularity` rank profile, as defined below at the bottom of `schemas/news.sd`. Note that rank profiles are defined outside the `document` block: + + +```txt +schema news { document news { field news_id type string { indexing: summary | attribute attribute: fast-search } field category type string { indexing: summary | attribute } field subcategory type string { indexing: summary | attribute } field title type string { indexing: index | summary index: enable-bm25 } field abstract type string { indexing: index | summary index: enable-bm25 } field body type string { indexing: index | summary index: enable-bm25 } field url type string { indexing: index | summary } field date type int { indexing: summary | attribute attribute: fast-search } field clicks type int { indexing: summary | attribute } field impressions type int { indexing: summary | attribute } } + +fieldset default { fields: title, abstract, body } + +rank-profile popularity inherits default { function popularity() { expression: if (attribute(impressions) > 0, attribute(clicks) / attribute(impressions), 0) } first-phase { expression: nativeRank(title, abstract) + 10 * popularity } } } +``` + + +- `rank-profile popularity inherits default` + +This configures Vespa to create a new rank profile named `popularity`, which inherits all the properties of the default rank-profile; only properties that are explicitly defined, or overridden, will differ from those of the default rank-profile. + +- `first-phase` + +Relevance calculations in Vespa are two-phased. The calculations done in the first phase are performed on every single document matching your query, while the second phase calculations are only done on the top `n` documents as determined by the calculations done in the first phase. See [phased ranking](/en/ranking/phased-ranking). + +- `function popularity()` + +This sets up a function that can be called from other expressions. This function calculates the number of clicks divided by impressions for indicating popularity. However, this isn't really the best way of calculating this as an article with a low number of impressions can score high on such a value, even though uncertainty is high. + +- `expression: nativeRank + 10 * popularity` + +This expression is used to rank documents. Here, the default ranking expression — the `nativeRank` of the `default` fieldset — is included to make the query relevant, while the second term calls the `popularity` function. The weighted sum of these two terms is the final relevance for each document. Note that the weight here, `10`, is set by observation. A better approach would be to learn such values using machine learning. + +More information can be found in the [schema reference](/en/reference/schemas/schemas#rank-profile). + +Deploy the _popularity_ rank profile: + + +```bash +$ vespa deploy --wait 300 my-app +``` + +Run a query: + +```bash +$ vespa query -v \ 'yql=select * from news where default contains "music"' \ 'ranking=popularity' +``` + +and find documents with high `popularity` values at the top. Note that we must specify the rank profile to use with the run time `ranking` parameter. + +## Conclusion + +After completing this part of the tutorial, you should now have a basic understanding of how you can load data into Vespa and effectively search for content. In the [next part of the tutorial](/en/learn/tutorials/news-4-embeddings), we'll start with the basics for transforming this search app into a recommendation system. + diff --git a/en/learn/tutorials/news-4-embeddings.md b/en/learn/tutorials/news-4-embeddings.md deleted file mode 100644 index 5d9e584c18..0000000000 --- a/en/learn/tutorials/news-4-embeddings.md +++ /dev/null @@ -1,484 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "News search and recommendation tutorial - embeddings" -redirect_from: - - /en/tutorials/news-4-embeddings ---- - - -This is the fourth part of the tutorial series for setting up a Vespa -application for personalized news recommendations. The parts are: - -1. [Getting started](news-1-deploy-an-application.html) -2. [A basic news search application](news-2-basic-feeding-and-query.html) - application packages, feeding, query -3. [News search](news-3-searching.html) - sorting, grouping, and ranking -4. [Generating embeddings for users and news articles](news-4-embeddings.html) -5. [News recommendation](news-5-recommendation.html) - partial updates (news embeddings), ANNs, filtering -6. [News recommendation with searchers](news-6-recommendation-with-searchers.html) - custom searchers, doc processors -7. [News recommendation with parent-child](news-7-recommendation-with-parent-child.html) - parent-child, tensor ranking -8. Advanced news recommendation - intermission - training a ranking model -9. Advanced news recommendation - ML models - -In this part, we'll start transforming our application from news search to -recommendation. We won't be using Vespa at all in this part. -Our focus is to generate news and user embeddings. -We'll start using these embeddings in the next part - you can skip this part if you wish. - -The primary function of a recommendation system is to provide items of interest to any given user. -The more we know about the user, the better recommendations we can provide. -We can view recommendation as search where the query is the user profile. -So, in this tutorial we will build upon the previous news search tutorial by creating user profiles -and use them to search for relevant news articles. - -We start by generating embeddings using a collaborative filtering method. -We'll then improve upon that using a content-based approach, which generates embedding based on BERT models. -Since we'll use this in a nearest neighbors algorithm, -we'll touch upon how the maximum inner product search is transformed -to a distance search form. - -Let's start with taking a look again at what data the MIND dataset provides for us. - - -### Requirements - -We start using some machine learning tools in this tutorial. -Specifically, we need Numpy, Scikit-learn, PyTorch, and the HuggingFace Transformers library. -Make sure you have all the necessary dependencies by running the following in the sample application directory: - -
    - -
    -$ python3 -m pip install --ignore-installed -r requirements.txt
    -
    -
    - - -## The MIND dataset - -The MIND dataset, for our purposes in this series of tutorials, -consists of two main files: `news.tsv` and `behaviors.tsv`. -We used the former in the previous tutorial, as that contains all news article content. - -The `behaviors.tsv` file contains a set of impressions. -An impression is an ordered list of news articles that was generated for a user. -It includes which of those articles the user clicked, and conversely, which ones were not. -We designate articles not clicked as "skips". -Also included in the impression is a list of articles the user has previously clicked. -An example is: - -``` -3 U11552 11/11/2019 1:03:52 PM N2139 N18390-0 N10537-0 N23967-1 -``` - -Here, user `U11552` was shown three articles: `N18390`, `N10537`, and `N23967`, -of which the user skipped two and clicked the last one. -At that time, the user had previously clicked on article `N2139`. -We can cross-reference with the `news.tsv` and extract the content of these articles. - -We interpret a click as a positive signal for interest and a skip as possibly a negative signal for interest. -This is called implicit feedback, as the users haven't explicitly expressed their interests. -However, using clicks and skips, we can still start to infer the users' interests. - - -## Collaborative filtering in recommendation systems - -A simple approach to provide recommendations to the above would be to extract the categories, subcategories, -and/or entities the users have implicitly interacted with, and store these for each user. -We can call this a sparse user profile because we store the exact terms of entities or categories. -We could then use traditional information retrieval techniques to search for more articles with similar content. - -However, by doing this we miss out on a lot of information. -For instance, some categories or entities are similar, which could be of interest to the user. -Also, users with similar interests tend to click on similar articles. -If some type of content was interesting to one user, -it would likely be interesting to similar users. - -Exploiting this information is called collaborative filtering -and the classical approach to this is matrix factorization. -In this approach, we create a large matrix with users along one axis and news articles along the other. -We'll call this the interaction matrix. -Then we factorize this matrix into two smaller matrices, -where the product of these two smaller approximates the original. - -![Matrix factorization](/assets/img/tutorials/mf.png) - - -In the image above, you can see a user matrix with as many rows as there are users -and a news matrix with as many columns as there are news articles. -Each user row, or news column, has the same length, signified by the `k` dimension. -The intuition is that the dot product of the `k` length vector for a user and news pair -approximates the user's interest in the news article. -Since the information is compressed into the `k` length vector, -this works across users as well. -Thus, the "collaborative" filtering. - -These `k` length vectors can be extracted from the matrices and associated with the user or news article. -So, when we want to recommend news articles to a user, -we simply find the user's vector and find the articles with the highest dot products. -In the following, we will use this approach to generate such embeddings for users and news articles. - -Please note, however, this approach would not work well in practice for news recommendation. -The reason is that a large part of news recommendation is to recommend **new** news articles, -which might not have received any implicit feedback yet. -This is called the "cold start" problem. -For such problems, we need to use additional content (often called "side information") -of news articles to provide recommendations. -We'll tackle this "cold start" a bit later. - - -## Generating embeddings - -A standard method for factorizing the interaction matrix is to use Alternating Least Squares. -The idea is to randomly fill the user and news matrices -and freeze one of the matrices' parameters while solving for the other. -By alternating between which matrix is fixed, this can be solved with a traditional least-squares problem. -We can iterate the process until convergence. - -This tutorial aims to generate embeddings so that the dot product -between a user and news vector signifies the probability of a click. -Using this signal we can rank news articles by click probability. -To train the embedding vectors, we will use a stochastic gradient descent approach to modify the embeddings -so that their dot product followed by the logistic function predicts a user click. -We use a binary cross-entropy as loss function. - -We'll use PyTorch for this. The main PyTorch model class is as follows: - -```python -class MF(torch.nn.Module): - def __init__(self, num_users, num_items, embedding_size): - super(MF, self).__init__() - self.user_embeddings = torch.nn.Embedding(num_embeddings=num_users, - embedding_dim=embedding_size) - self.news_embeddings = torch.nn.Embedding(num_embeddings=num_items, - embedding_dim=embedding_size) - - def forward(self, users, items): - user_embeddings = self.user_embeddings(users) - news_embeddings = self.news_embeddings(items) - dot_prod = torch.sum(torch.mul(user_embeddings, news_embeddings), 1) - return torch.sigmoid(dot_prod) -``` - -We use the PyTorch's `Embedding` class to hold the user and news embeddings. -The forward function is the forward pass of the gradient descent. -First, the users and items selected for a mini-batch update are extracted from their embedding tables. -Then we take the dot-product with a logistic function and return the value. -This prediction for user and news pairs is then evaluated against the click or skip labels: - -```python - # forward + backward + optimize - user_ids, news_ids, labels = batch - prediction = model(user_ids, news_ids) - loss = loss_function(prediction.view(-1), labels) - loss.backward() - optimizer.step() -``` - -This is done across several of epochs. -The `batch` here contains a batch of`user_id`s, `news_id`s, and `label`s used for training a mini-batch. -For instance, from the example impression above, a training example would be `U11552, N23967, 1`. -The code responsible for generating the training data -samples 4 negative examples (skips) for each positive example (click). - -The full code can be seen in the sample application, -in [train_mf.py](https://github.com/vespa-engine/sample-apps/blob/master/news/src/python/train_mf.py). -Let's go ahead and generate the embeddings: - -
    - -
    -$ ./src/python/train_mf.py mind 10
    -
    -
    - -This runs the training code for 10 epochs, and deposits the resulting -user and news vectors in the `mind` directory, where the rest of the data is: - -``` -Total loss after epoch 1: 573.5299682617188 (3.49713397026062 avg) -Total loss after epoch 2: 551.6585083007812 (3.363771438598633 avg) -... -{'auc': 0.5776, 'mrr': 0.248, 'ndcg@5': 0.2573, 'ndcg@10': 0.317} -{'auc': 0.4988, 'mrr': 0.2154, 'ndcg@5': 0.2182, 'ndcg@10': 0.2824} -``` - -We can see the loss reduces over the number of epochs. -The two final lines here are ranking metrics run on the training set and validation set. -Here, the `AUC` metric - Area Under the (ROC) Curve - -is at `0.5776` for the training set and `0.4988` for the validation set. -If you run for a greater number of epochs, you would see the `AUC` for the training set become much larger -than the validation set, around `0.974` and `0.51` respectively if run for 100 epochs. - -In this case, the `AUC` metric measures the probability of ranking relevant news higher than non-relevant news. -A score of around `0.5` means that it is totally random. -Thus, we haven't learned anything of use for the validation set. - -This is not overfitting but rather an instance of the problem mentioned earlier. -The validation set contains news articles shown to users a time period after the data in the training set. -Thus, most news articles are new, and their embedding vectors are effectively random. - -We'll address this next. - - -## Addressing the cold start problem - -The approach above based itself on news articles that users interacted with in the training set period. -Only the user ids and news article ids were used. -To overcome the problem that new articles haven't been seen in the training set, -we need to use the article's content features. -So, the predictions will be based on the similarity of content a user has previously interacted, -rather than the actual news article id. - -This is, naturally enough, called content-based recommendation. - -The general approach we'll take here is to still rely on a dot product between a user embedding and news embedding, -however the news embedding will be constructed from various content features. - -The MIND dataset has a few such features we can use. -Each news article has a `category`, a `subcategory` and zero or more `entities` extracted from the text. -These features are categorical, meaning that they have a finite set of values they can take. -To handle these, we'll generate an embedding for each possible value, -similar to how we generated embeddings for the user id's and news id's above. -These ids are also categorical, after all. - - -### Creating BERT embeddings - -However, there are other content features as well such as the `title` and `abstract`. -To create embeddings from these, we'll employ a -[BERT-based sentence classifier](https://huggingface.co/docs/transformers/model_doc/bert#transformers.BertForSequenceClassification) -from the [HuggingFace transformers](https://huggingface.co/docs/transformers/index) library: - -```python -from transformers import BertTokenizer, BertModel -tokenizer = BertTokenizer.from_pretrained('bert-base-uncased') -model = BertModel.from_pretrained('google/bert_uncased_L-8_H-512_A-8') -tokens = tokenizer(title, abstract, return_tensors="pt") -outputs = model(**tokens) -embedding = outputs[0][0][0] -``` - -Here, we use a medium-sized BERT model with 8 layers and a hidden dimension size of 512. -This means that the embedding will be a vector of size 512. -We use the vector from the first `CLS` token to represent the combined title and abstract. - -To generate these embeddings for all news content, run one of the following: - -
      -
    1. - Generate embeddings. - This might take a while, around an hour for all news articles in the train and dev demo dataset. - -
      - -
      -$ python3 src/python/create_bert_embeddings.py mind
      -
      -
      - - -
    2. -
    3. - Download pre-processed embeddings: - -
      - -
      -$ curl -L -o mind/train/news_embeddings.tsv \
      -  https://data.vespa-cloud.com/sample-apps-data/mind_news_embedding.tsv
      -$ curl -L -o mind/dev/news_embeddings.tsv \
      -  https://data.vespa-cloud.com/sample-apps-data/mind_news_embedding_dev.tsv
      -
      -
      -
    4. -
    - -This creates a `news_embeddings.tsv` file under the `mind/train` and `mind/dev` subdirectories. - - -## Training the model - -Now that we have content-based embeddings for each news article, -we can train the model to use them. The following figure illustrates -the model we are training: - -Model training example - - -So, we'll pass the 512-dimensional embeddings from the BERT model -through a typical neural network layer to reduce dimensions to 50. -We then concatenate this representation with the 50 dimensional -embeddings for `category`, `subcategory` and `entity`. -We only use one entity for now. -This representation is then sent through another neural network layer -to form the final representation for a news article. -Finally, the dot product is taken with the user embedding. - -In PyTorch code, this looks like: - -```python -class ContentBasedModel(torch.nn.Module): - def __init__(self, - num_users, - num_news, - num_categories, - num_subcategories, - num_entities, - embedding_size, - bert_embeddings): - super(ContentBasedModel, self).__init__() - - # Embedding tables for category variables - self.user_embeddings = torch.nn.Embedding(num_embeddings=num_users, embedding_dim=embedding_size) - self.news_embeddings = torch.nn.Embedding(num_embeddings=num_news, embedding_dim=embedding_size) - self.cat_embeddings = torch.nn.Embedding(num_embeddings=num_categories, embedding_dim=embedding_size) - self.sub_cat_embeddings = torch.nn.Embedding(num_embeddings=num_subcategories, embedding_dim=embedding_size) - self.entity_embeddings = torch.nn.Embedding(num_embeddings=num_entities, embedding_dim=embedding_size) - - # Pretrained BERT embeddings - self.news_bert_embeddings = torch.nn.Embedding.from_pretrained(bert_embeddings, freeze=True) - - # Linear transformation from BERT size to embedding size (512 -> 50) - self.news_bert_transform = torch.nn.Linear(bert_embeddings.shape[1], embedding_size) - - # Linear transformation from concatenation of category, subcategory, entity and BERT embedding - self.news_content_transform = torch.nn.Linear(in_features=embedding_size*5, out_features=embedding_size) - - def get_user_embeddings(self, users): - return self.user_embeddings(users) - - def get_news_embeddings(self, items, categories, subcategories, entities): - # Transform BERT representation to a shorter embedding - bert_embeddings = self.news_bert_embeddings(items) - bert_embeddings = self.news_bert_transform(bert_embeddings) - bert_embeddings = torch.sigmoid(bert_embeddings) - - # Create news content representation by concatenating BERT, category, subcategory and entities - cat_embeddings = self.cat_embeddings(categories) - news_embeddings = self.news_embeddings(items) - sub_cat_embeddings = self.sub_cat_embeddings(subcategories) - entity_embeddings_1 = self.entity_embeddings(entities[:,0]) - news_embedding = torch.cat((news_embeddings, bert_embeddings, cat_embeddings, - sub_cat_embeddings, entity_embeddings_1), 1) - news_embedding = self.news_content_transform(news_embedding) - news_embedding = torch.sigmoid(news_embedding) - - return news_embedding - - def forward(self, users, items, categories, subcategories, entities): - user_embeddings = self.get_user_embeddings(users) - news_embeddings = self.get_news_embeddings(items, categories, subcategories, entities) - dot_prod = torch.sum(torch.mul(user_embeddings, news_embeddings), 1) - return torch.sigmoid(dot_prod) - -``` - -The forward pass function is pretty much the same as before. -You can see the entire training script in -[train_cold_start.py](https://github.com/vespa-engine/sample-apps/blob/master/news/src/python/train_cold_start.py). -Running this results in: - -
    - -
    -$ python3 src/python/train_cold_start.py mind 5
    -
    -
    - -``` -Total loss after epoch 1: 920.5855102539062 (0.703811526298523 avg) -{'auc': 0.5391, 'mrr': 0.2367, 'ndcg@5': 0.2464, 'ndcg@10': 0.3059} -{'auc': 0.5131, 'mrr': 0.2239, 'ndcg@5': 0.2296, 'ndcg@10': 0.2933} -Total loss after epoch 2: 761.7719116210938 (0.5823944211006165 avg) -{'auc': 0.647, 'mrr': 0.2992, 'ndcg@5': 0.3246, 'ndcg@10': 0.3829} -{'auc': 0.5656, 'mrr': 0.2447, 'ndcg@5': 0.2604, 'ndcg@10': 0.3255} -Total loss after epoch 3: 660.2255859375 (0.5047596096992493 avg) -{'auc': 0.7032, 'mrr': 0.3323, 'ndcg@5': 0.3662, 'ndcg@10': 0.426} -{'auc': 0.5926, 'mrr': 0.2623, 'ndcg@5': 0.285, 'ndcg@10': 0.3474} -Total loss after epoch 4: 625.5714111328125 (0.4782656133174896 avg) -{'auc': 0.7329, 'mrr': 0.3519, 'ndcg@5': 0.3901, 'ndcg@10': 0.4514} -{'auc': 0.5998, 'mrr': 0.2634, 'ndcg@5': 0.2872, 'ndcg@10': 0.3492} -Total loss after epoch 5: 605.7533569335938 (0.4631142020225525 avg) -{'auc': 0.7602, 'mrr': 0.3758, 'ndcg@5': 0.4191, 'ndcg@10': 0.4797} -{'auc': 0.6095, 'mrr': 0.271, 'ndcg@5': 0.2962, 'ndcg@10': 0.358} -Total loss after epoch 6: 588.0604248046875 (0.44958749413490295 avg) -{'auc': 0.7881, 'mrr': 0.3992, 'ndcg@5': 0.4492, 'ndcg@10': 0.5094} -{'auc': 0.6156, 'mrr': 0.2742, 'ndcg@5': 0.3009, 'ndcg@10': 0.3634} -Total loss after epoch 7: 570.162353515625 (0.435903936624527 avg) -{'auc': 0.8141, 'mrr': 0.4268, 'ndcg@5': 0.4837, 'ndcg@10': 0.5408} -{'auc': 0.6201, 'mrr': 0.2777, 'ndcg@5': 0.3044, 'ndcg@10': 0.3676} -Total loss after epoch 8: 551.164306640625 (0.4213794469833374 avg) -{'auc': 0.8382, 'mrr': 0.4546, 'ndcg@5': 0.5184, 'ndcg@10': 0.5734} -{'auc': 0.6223, 'mrr': 0.28, 'ndcg@5': 0.3064, 'ndcg@10': 0.3702} -Total loss after epoch 9: 534.6984252929688 (0.40879085659980774 avg) -{'auc': 0.8577, 'mrr': 0.4785, 'ndcg@5': 0.5479, 'ndcg@10': 0.6009} -{'auc': 0.6266, 'mrr': 0.2847, 'ndcg@5': 0.3115, 'ndcg@10': 0.3747} -Total loss after epoch 10: 517.16748046875 (0.3953879773616791 avg) -{'auc': 0.8758, 'mrr': 0.5074, 'ndcg@5': 0.5818, 'ndcg@10': 0.6316} -{'auc': 0.6249, 'mrr': 0.2842, 'ndcg@5': 0.3114, 'ndcg@10': 0.3733} -``` - -This is much better. The `AUC` score at epoch 9 is a respectable `0.6266`. -Note that as we train further, the `AUC` for the dev set starts dropping. -This is a sign of overfitting, so we should stop training. - -For reference, the baseline model for the MIND competition, -[Neural News Recommendation with Multi-Head Self-Attention](https://aclanthology.org/D19-1671/), -results in `0.6362`. -This model additionally uses the user history in each impression to create a better model for the user embedding. -For the moment, however, we are satisfied with these, and we'll use them going forward. -Feel free to experiment and see if you can achieve better results! - -{% include note.html content="These numbers are for the demo dataset, which is much smaller than the full dataset. -For reference, in [the MIND paper](https://msnews.github.io/assets/doc/ACL2020_MIND.pdf) -the baseline here achieves `0.6776` on the full dataset." %} - -The training script writes these embeddings to the files -`mind/user_embeddings.tsv` and `mind/news_embeddings.tsv`. - - -## Mapping from inner-product search to Euclidean search - -These vectors have been trained to maximize the inner product. -Finding the best news articles given a user vector is called Maximum Inner Product Search - or MIPS. -This form isn't really suitable for efficient retrieval as-is, -but it can be mapped to a nearest neighbor search problem, so -we can use an efficient approximate nearest neighbors index. - -When specifying `distance-metric: dotproduct`, Vespa uses the technique discussed in -[Speeding Up the Xbox Recommender System Using a Euclidean Transformation for Inner-Product -Spaces](https://www.microsoft.com/en-us/research/wp-content/uploads/2016/02/XboxInnerProduct.pdf) -to solve the MIPS case. See [blog post announcing MIPS support in Vespa](https://blog.vespa.ai/announcing-maximum-inner-product-search/). - -
    -field embedding type tensor<float>(d0[50]) {
    -            indexing: attribute | index
    -            attribute {
    -                distance-metric: dotproduct
    -            }
    -}
    - -See [Nearest Neighbor Search](../../querying/nearest-neighbor-search) for more -information on nearest neighbor search and supported distance metrics in Vespa. - -We've included a script to create a feed suitable for Vespa: - -
    - -
    -$ python3 src/python/convert_embeddings_to_vespa_format.py mind
    -
    -
    - -We are now ready to feed these embedding vectors to Vespa. - -## Conclusion - -Now that we've generated user and document embeddings, -we can start using these to recommend news items to users. -We'll start feeding these in the [next part of the tutorial](news-5-recommendation.html). - - diff --git a/en/learn/tutorials/news-4-embeddings.mdx b/en/learn/tutorials/news-4-embeddings.mdx new file mode 100644 index 0000000000..bb3d1e9309 --- /dev/null +++ b/en/learn/tutorials/news-4-embeddings.mdx @@ -0,0 +1,291 @@ +--- +title: "News search and recommendation tutorial - embeddings" +--- + +This is the fourth part of the tutorial series for setting up a Vespa application for personalized news recommendations. The parts are: + +1. [Getting started](/en/learn/tutorials/news-1-deploy-an-application) +2. [A basic news search application](/en/learn/tutorials/news-2-basic-feeding-and-query) - application packages, feeding, query +3. [News search](/en/learn/tutorials/news-3-searching) - sorting, grouping, and ranking +4. [Generating embeddings for users and news articles](/en/learn/tutorials/news-4-embeddings) +5. [News recommendation](/en/learn/tutorials/news-5-recommendation) - partial updates (news embeddings), ANNs, filtering +6. [News recommendation with searchers](/en/learn/tutorials/news-6-recommendation-with-searchers) - custom searchers, doc processors +7. [News recommendation with parent-child](/en/learn/tutorials/news-7-recommendation-with-parent-child) - parent-child, tensor ranking +8. Advanced news recommendation - intermission - training a ranking model +9. Advanced news recommendation - ML models + +In this part, we'll start transforming our application from news search to recommendation. We won't be using Vespa at all in this part. Our focus is to generate news and user embeddings. We'll start using these embeddings in the next part - you can skip this part if you wish. + +The primary function of a recommendation system is to provide items of interest to any given user. The more we know about the user, the better recommendations we can provide. We can view recommendation as search where the query is the user profile. So, in this tutorial we will build upon the previous news search tutorial by creating user profiles and use them to search for relevant news articles. + +We start by generating embeddings using a collaborative filtering method. We'll then improve upon that using a content-based approach, which generates embedding based on BERT models. Since we'll use this in a nearest neighbors algorithm, we'll touch upon how the maximum inner product search is transformed to a distance search form. + +Let's start with taking a look again at what data the MIND dataset provides for us. + + +### Requirements + +We start using some machine learning tools in this tutorial. Specifically, we need Numpy, Scikit-learn, PyTorch, and the HuggingFace Transformers library. Make sure you have all the necessary dependencies by running the following in the sample application directory: + +```bash +$ python3 -m pip install --ignore-installed -r requirements.txt +``` + + +## The MIND dataset + +The MIND dataset, for our purposes in this series of tutorials, consists of two main files: `news.tsv` and `behaviors.tsv`. We used the former in the previous tutorial, as that contains all news article content. + +The `behaviors.tsv` file contains a set of impressions. An impression is an ordered list of news articles that was generated for a user. It includes which of those articles the user clicked, and conversely, which ones were not. We designate articles not clicked as "skips". Also included in the impression is a list of articles the user has previously clicked. An example is: + +``` +3 U11552 11/11/2019 1:03:52 PM N2139 N18390-0 N10537-0 N23967-1 +``` + +Here, user `U11552` was shown three articles: `N18390`, `N10537`, and `N23967`, of which the user skipped two and clicked the last one. At that time, the user had previously clicked on article `N2139`. We can cross-reference with the `news.tsv` and extract the content of these articles. + +We interpret a click as a positive signal for interest and a skip as possibly a negative signal for interest. This is called implicit feedback, as the users haven't explicitly expressed their interests. However, using clicks and skips, we can still start to infer the users' interests. + + +## Collaborative filtering in recommendation systems + +A simple approach to provide recommendations to the above would be to extract the categories, subcategories, and/or entities the users have implicitly interacted with, and store these for each user. We can call this a sparse user profile because we store the exact terms of entities or categories. We could then use traditional information retrieval techniques to search for more articles with similar content. + +However, by doing this we miss out on a lot of information. For instance, some categories or entities are similar, which could be of interest to the user. Also, users with similar interests tend to click on similar articles. If some type of content was interesting to one user, it would likely be interesting to similar users. + +Exploiting this information is called collaborative filtering and the classical approach to this is matrix factorization. In this approach, we create a large matrix with users along one axis and news articles along the other. We'll call this the interaction matrix. Then we factorize this matrix into two smaller matrices, where the product of these two smaller approximates the original. + + +![Matrix factorization](/assets/img/tutorials/mf.png) + + +In the image above, you can see a user matrix with as many rows as there are users and a news matrix with as many columns as there are news articles. Each user row, or news column, has the same length, signified by the `k` dimension. The intuition is that the dot product of the `k` length vector for a user and news pair approximates the user's interest in the news article. Since the information is compressed into the `k` length vector, this works across users as well. Thus, the "collaborative" filtering. + +These `k` length vectors can be extracted from the matrices and associated with the user or news article. So, when we want to recommend news articles to a user, we simply find the user's vector and find the articles with the highest dot products. In the following, we will use this approach to generate such embeddings for users and news articles. + +Please note, however, this approach would not work well in practice for news recommendation. The reason is that a large part of news recommendation is to recommend **new** news articles, which might not have received any implicit feedback yet. This is called the "cold start" problem. For such problems, we need to use additional content (often called "side information") of news articles to provide recommendations. We'll tackle this "cold start" a bit later. + + +## Generating embeddings + +A standard method for factorizing the interaction matrix is to use Alternating Least Squares. The idea is to randomly fill the user and news matrices and freeze one of the matrices' parameters while solving for the other. By alternating between which matrix is fixed, this can be solved with a traditional least-squares problem. We can iterate the process until convergence. + +This tutorial aims to generate embeddings so that the dot product between a user and news vector signifies the probability of a click. Using this signal we can rank news articles by click probability. To train the embedding vectors, we will use a stochastic gradient descent approach to modify the embeddings so that their dot product followed by the logistic function predicts a user click. We use a binary cross-entropy as loss function. + +We'll use PyTorch for this. The main PyTorch model class is as follows: + +```python +class MF(torch.nn.Module): + def __init__(self, num_users, num_items, embedding_size): + super(MF, self).__init__() + self.user_embeddings = torch.nn.Embedding(num_embeddings=num_users, + embedding_dim=embedding_size) + self.news_embeddings = torch.nn.Embedding(num_embeddings=num_items, + embedding_dim=embedding_size) + + def forward(self, users, items): + user_embeddings = self.user_embeddings(users) + news_embeddings = self.news_embeddings(items) + dot_prod = torch.sum(torch.mul(user_embeddings, news_embeddings), 1) + return torch.sigmoid(dot_prod) +``` + +We use the PyTorch's `Embedding` class to hold the user and news embeddings. The forward function is the forward pass of the gradient descent. First, the users and items selected for a mini-batch update are extracted from their embedding tables. Then we take the dot-product with a logistic function and return the value. This prediction for user and news pairs is then evaluated against the click or skip labels: + +```python +# forward + backward + optimize + user_ids, news_ids, labels = batch + prediction = model(user_ids, news_ids) + loss = loss_function(prediction.view(-1), labels) + loss.backward() + optimizer.step() +``` + +This is done across several of epochs. The `batch` here contains a batch of `user_id`s, `news_id`s, and `label`s used for training a mini-batch. For instance, from the example impression above, a training example would be `U11552, N23967, 1`. The code responsible for generating the training data samples 4 negative examples (skips) for each positive example (click). + +The full code can be seen in the sample application, in [train_mf.py](https://github.com/vespa-engine/sample-apps/blob/master/news/src/python/train_mf.py). Let's go ahead and generate the embeddings: + +```bash +$ ./src/python/train_mf.py mind 10 +``` + +This runs the training code for 10 epochs, and deposits the resulting user and news vectors in the `mind` directory, where the rest of the data is: + +``` +Total loss after epoch 1: 573.5299682617188 (3.49713397026062 avg) +Total loss after epoch 2: 551.6585083007812 (3.363771438598633 avg) +... +{'auc': 0.5776, 'mrr': 0.248, 'ndcg@5': 0.2573, 'ndcg@10': 0.317} +{'auc': 0.4988, 'mrr': 0.2154, 'ndcg@5': 0.2182, 'ndcg@10': 0.2824} +``` + +We can see the loss reduces over the number of epochs. The two final lines here are ranking metrics run on the training set and validation set. Here, the `AUC` metric - Area Under the (ROC) Curve - is at `0.5776` for the training set and `0.4988` for the validation set. If you run for a greater number of epochs, you would see the `AUC` for the training set become much larger than the validation set, around `0.974` and `0.51` respectively if run for 100 epochs. + +In this case, the `AUC` metric measures the probability of ranking relevant news higher than non-relevant news. A score of around `0.5` means that it is totally random. Thus, we haven't learned anything of use for the validation set. + +This is not overfitting but rather an instance of the problem mentioned earlier. The validation set contains news articles shown to users a time period after the data in the training set. Thus, most news articles are new, and their embedding vectors are effectively random. + +We'll address this next. + + +## Addressing the cold start problem + +The approach above based itself on news articles that users interacted with in the training set period. Only the user ids and news article ids were used. To overcome the problem that new articles haven't been seen in the training set, we need to use the article's content features. So, the predictions will be based on the similarity of content a user has previously interacted, rather than the actual news article id. + +This is, naturally enough, called content-based recommendation. + +The general approach we'll take here is to still rely on a dot product between a user embedding and news embedding, however the news embedding will be constructed from various content features. + +The MIND dataset has a few such features we can use. Each news article has a `category`, a `subcategory` and zero or more `entities` extracted from the text. These features are categorical, meaning that they have a finite set of values they can take. To handle these, we'll generate an embedding for each possible value, similar to how we generated embeddings for the user id's and news id's above. These ids are also categorical, after all. + + +### Creating BERT embeddings + +However, there are other content features as well such as the `title` and `abstract`. To create embeddings from these, we'll employ a [BERT-based sentence classifier](https://huggingface.co/docs/transformers/model_doc/bert#transformers.BertForSequenceClassification) from the [HuggingFace transformers](https://huggingface.co/docs/transformers/index) library: + +```python +from transformers import BertTokenizer, BertModel +tokenizer = BertTokenizer.from_pretrained('bert-base-uncased') +model = BertModel.from_pretrained('google/bert_uncased_L-8_H-512_A-8') +tokens = tokenizer(title, abstract, return_tensors="pt") +outputs = model(**tokens) +embedding = outputs[0][0][0] +``` + +Here, we use a medium-sized BERT model with 8 layers and a hidden dimension size of 512. This means that the embedding will be a vector of size 512. We use the vector from the first `CLS` token to represent the combined title and abstract. + +To generate these embeddings for all news content, run one of the following: + +1. Generate embeddings. This might take a while, around an hour for all news articles in the `train` and `dev` demo dataset. + +```bash +$ python3 src/python/create_bert_embeddings.py mind +``` + +2. Download pre-processed embeddings: + +```bash +$ curl -L -o mind/train/news_embeddings.tsv \ + https://data.vespa-cloud.com/sample-apps-data/mind_news_embedding.tsv +$ curl -L -o mind/dev/news_embeddings.tsv \ + https://data.vespa-cloud.com/sample-apps-data/mind_news_embedding_dev.tsv +``` + +This creates a `news_embeddings.tsv` file under the `mind/train` and `mind/dev` subdirectories. + + +## Training the model + +Now that we have content-based embeddings for each news article, we can train the model to use them. The following figure illustrates the model we are training: + + +![Model training example](/assets/img/tutorials/embeddings.png) + + +So, we'll pass the 512-dimensional embeddings from the BERT model through a typical neural network layer to reduce dimensions to 50. We then concatenate this representation with the 50 dimensional embeddings for `category`, `subcategory` and `entity`. We only use one entity for now. This representation is then sent through another neural network layer to form the final representation for a news article. Finally, the dot product is taken with the user embedding. + +In PyTorch code, this looks like: + +```python expandable +class ContentBasedModel(torch.nn.Module): + def __init__(self, + num_users, + num_news, + num_categories, + num_subcategories, + num_entities, + embedding_size, + bert_embeddings): + super(ContentBasedModel, self).__init__() + + self.user_embeddings = torch.nn.Embedding(num_embeddings=num_users, embedding_dim=embedding_size) + self.news_embeddings = torch.nn.Embedding(num_embeddings=num_news, embedding_dim=embedding_size) + self.cat_embeddings = torch.nn.Embedding(num_embeddings=num_categories, embedding_dim=embedding_size) + self.sub_cat_embeddings = torch.nn.Embedding(num_embeddings=num_subcategories, embedding_dim=embedding_size) + self.entity_embeddings = torch.nn.Embedding(num_embeddings=num_entities, embedding_dim=embedding_size) + + self.news_bert_embeddings = torch.nn.Embedding.from_pretrained(bert_embeddings, freeze=True) + self.news_bert_transform = torch.nn.Linear(bert_embeddings.shape[1], embedding_size) + self.news_content_transform = torch.nn.Linear(in_features=embedding_size*5, out_features=embedding_size) + + def get_user_embeddings(self, users): + return self.user_embeddings(users) + + def get_news_embeddings(self, items, categories, subcategories, entities): + bert_embeddings = self.news_bert_embeddings(items) + bert_embeddings = self.news_bert_transform(bert_embeddings) + bert_embeddings = torch.sigmoid(bert_embeddings) + + cat_embeddings = self.cat_embeddings(categories) + news_embeddings = self.news_embeddings(items) + sub_cat_embeddings = self.sub_cat_embeddings(subcategories) + entity_embeddings_1 = self.entity_embeddings(entities[:,0]) + news_embedding = torch.cat((news_embeddings, bert_embeddings, cat_embeddings, + sub_cat_embeddings, entity_embeddings_1), 1) + news_embedding = self.news_content_transform(news_embedding) + news_embedding = torch.sigmoid(news_embedding) + + return news_embedding + + def forward(self, users, items, categories, subcategories, entities): + user_embeddings = self.get_user_embeddings(users) + news_embeddings = self.get_news_embeddings(items, categories, subcategories, entities) + dot_prod = torch.sum(torch.mul(user_embeddings, news_embeddings), 1) + return torch.sigmoid(dot_prod) +``` + +The forward pass function is pretty much the same as before. You can see the entire training script in [train_cold_start.py](https://github.com/vespa-engine/sample-apps/blob/master/news/src/python/train_cold_start.py). Running this results in: + +```bash +$ python3 src/python/train_cold_start.py mind 5 +``` + +``` +Total loss after epoch 1: 920.5855102539062 (0.703811526298523 avg) +{'auc': 0.5391, 'mrr': 0.2367, 'ndcg@5': 0.2464, 'ndcg@10': 0.3059} +... +Total loss after epoch 10: 517.16748046875 (0.3953879773616791 avg) +{'auc': 0.8758, 'mrr': 0.5074, 'ndcg@5': 0.5818, 'ndcg@10': 0.6316} +{'auc': 0.6249, 'mrr': 0.2842, 'ndcg@5': 0.3114, 'ndcg@10': 0.3733} +``` + +This is much better. The `AUC` score at epoch 9 is a respectable `0.6266`. Note that as we train further, the `AUC` for the dev set starts dropping. This is a sign of overfitting, so we should stop training. + +For reference, the baseline model for the MIND competition, [Neural News Recommendation with Multi-Head Self-Attention](https://aclanthology.org/D19-1671/), results in `0.6362`. This model additionally uses the user history in each impression to create a better model for the user embedding. For the moment, however, we are satisfied with these, and we'll use them going forward. Feel free to experiment and see if you can achieve better results! + + +These numbers are for the demo dataset, which is much smaller than the full dataset. For reference, in [the MIND paper](https://msnews.github.io/assets/doc/ACL2020_MIND.pdf) the baseline here achieves `0.6776` on the full dataset. + + +The training script writes these embeddings to the files `mind/user_embeddings.tsv` and `mind/news_embeddings.tsv`. + + +## Mapping from inner-product search to Euclidean search + +These vectors have been trained to maximize the inner product. Finding the best news articles given a user vector is called Maximum Inner Product Search - or MIPS. This form isn't really suitable for efficient retrieval as-is, but it can be mapped to a nearest neighbor search problem, so we can use an efficient approximate nearest neighbors index. + +When specifying `distance-metric: dotproduct`, Vespa uses the technique discussed in [Speeding Up the Xbox Recommender System Using a Euclidean Transformation for Inner-Product Spaces](https://www.microsoft.com/en-us/research/wp-content/uploads/2016/02/XboxInnerProduct.pdf) to solve the MIPS case. See [blog post announcing MIPS support in Vespa](https://blog.vespa.ai/announcing-maximum-inner-product-search/). + +```sd +field embedding type tensor(d0[50]) { + indexing: attribute | index + attribute { + distance-metric: dotproduct + } +} +``` + +See [Nearest Neighbor Search](/en/querying/nearest-neighbor-search) for more information on nearest neighbor search and supported distance metrics in Vespa. + +We've included a script to create a feed suitable for Vespa: + +```bash +$ python3 src/python/convert_embeddings_to_vespa_format.py mind +``` + +We are now ready to feed these embedding vectors to Vespa. + + +## Conclusion + +Now that we've generated user and document embeddings, we can start using these to recommend news items to users. We'll start feeding these in the [next part of the tutorial](/en/learn/tutorials/news-5-recommendation). diff --git a/en/learn/tutorials/news-5-recommendation.md b/en/learn/tutorials/news-5-recommendation.md deleted file mode 100644 index df6f55781c..0000000000 --- a/en/learn/tutorials/news-5-recommendation.md +++ /dev/null @@ -1,668 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "News search and recommendation tutorial - recommendations" -redirect_from: - - /en/tutorials/news-5-recommendation ---- - -This is the fifth part of the tutorial series for setting up a Vespa -application for personalized news recommendations. The parts are: - -1. [Getting started](news-1-deploy-an-application.html) -2. [A basic news search application](news-2-basic-feeding-and-query.html) - application packages, feeding, query -3. [News search](news-3-searching.html) - sorting, grouping, and ranking -4. [Generating embeddings for users and news articles](news-4-embeddings.html) -5. [News recommendation](news-5-recommendation.html) - partial updates (news embeddings), ANNs, filtering -6. [News recommendation with searchers](news-6-recommendation-with-searchers.html) - custom searchers, doc processors -7. [News recommendation with parent-child](news-7-recommendation-with-parent-child.html) - parent-child, tensor ranking -8. Advanced news recommendation - intermission - training a ranking model -9. Advanced news recommendation - ML models - -In this part, we'll start transforming our application from news search to -recommendation using the embeddings we created in the previous part. -So, we'll start by modifying our application, -so we can feed the embeddings and start using them for searching. - -For reference, the final state of this tutorial can be found in the -[app-5-recommendation](https://github.com/vespa-engine/sample-apps/tree/master/news/app-5-recommendation) -sub-directory of the `news` sample application. - - -## Indexing embeddings - -First, we need to modify the `news.sd` search definition to include a field -to hold the embedding and a recommendation rank profile: - -
    - -
    -schema news {
    -    document news {
    -        field news_id type string {
    -            indexing: summary | attribute
    -            attribute: fast-search
    -        }
    -        field category type string {
    -            indexing: summary | attribute
    -        }
    -        field subcategory type string {
    -            indexing: summary | attribute
    -        }
    -        field title type string {
    -            indexing: index | summary
    -            index: enable-bm25
    -        }
    -        field abstract type string {
    -            indexing: index | summary
    -            index: enable-bm25
    -        }
    -        field body type string {
    -            indexing: index | summary
    -            index: enable-bm25
    -        }
    -        field url type string {
    -            indexing: index | summary
    -        }
    -        field date type int {
    -            indexing: summary | attribute
    -        }
    -        field clicks type int {
    -            indexing: summary | attribute
    -        }
    -        field impressions type int {
    -            indexing: summary | attribute
    -        }
    -        field embedding type tensor<float>(d0[50]) {
    -            indexing: attribute 
    -            attribute {
    -                distance-metric: dotproduct
    -            }
    -        }
    -    }
    -
    -    fieldset default {
    -        fields: title, abstract, body
    -    }
    -
    -    rank-profile popularity inherits default {
    -        function popularity() {
    -            expression: if (attribute(impressions) > 0, attribute(clicks) / attribute(impressions), 0)
    -        }
    -        first-phase {
    -            expression: nativeRank(title, abstract) + 10 * popularity
    -        }
    -    }
    -
    -    rank-profile recommendation inherits default {
    -        first-phase {
    -          expression: closeness(field, embedding)
    -        }
    -    }
    -}
    -
    -
    - -The `embedding` field is a tensor field. -Tensors in Vespa are flexible multi-dimensional data structures, and, as first-class citizens, -can be used in queries, document fields, and constants in ranking. -Tensors can be either dense or sparse or both, and can contain any number of dimensions. -See [the tensor user guide](../../ranking/tensor-user-guide.html) for more information. - -Here we have defined a dense tensor with a single dimension (`d0` - dimension 0), which represents a vector. -The distance metric is "dotproduct" as we would like to use this field for nearest-neighbor search -where we search for the maximal dotproduct. - -This is seen in the `recommendation` rank profile. -Here, we've added a ranking expression using the -[closeness](../../reference/ranking/rank-features.html#closeness(dimension,name)) ranking feature, -which calculates the dot product and uses that to rank the news articles. -This depends on using the `nearestNeighbor` search operator, -which we'll get back to below when searching. -But for now, this expects a tensor in the query to be used as the initial search point. - -If you take a look at the file generated for the news embeddings, -`mind/vespa_news_embeddings.json`, you'll see several lines with something like this: - -```json -{ - "update": "id:news:news::N13390", - "fields": { - "embedding": { - "assign": { - "values": [9.871717,-0.403103,...] - } - } - } -} -``` - -This is a [partial update](../../writing/partial-updates.html). -So, assuming you already have a system up and running from the previous search tutorial, -you don't need to feed the entire corpus. -With a partial update, you only need to update the necessary fields. -So, after training another set of embeddings you can partially feed them again. -Please refer to [Vespa reads and writes](../../writing/reads-and-writes.html) for more information on feeding formats. - -We need to add another document type to represent a user. -Add this schema in `schemas/user.sd`: - -
    - -
    -schema user {
    -    document user {
    -        field user_id type string {
    -            indexing: summary | attribute
    -            attribute: fast-search
    -        }
    -        field embedding type tensor<float>(d0[50]) {
    -            indexing: summary | attribute
    -        }
    -    }
    -}
    -
    -
    - -This schema is set up so that we can search for a `user_id` and retrieve the user's embedding vector. - -We also need to let Vespa know we want to use this document type, -so we modify `services.xml` and add it under `documents` in the `content` section: - -
    - -
    -<?xml version="1.0" encoding="UTF-8"?>
    -<services version="1.0">
    -
    -    <container id="default" version="1.0">
    -        <search />
    -        <document-api />
    -        <nodes>
    -            <node hostalias="node1" />
    -        </nodes>
    -    </container>
    -
    -    <content id="mind" version="1.0">
    -        <redundancy>1</redundancy>
    -        <documents>
    -            <document type="news" mode="index" />
    -            <document type="user" mode="index" />
    -        </documents>
    -        <nodes>
    -            <node hostalias="node1" distribution-key="0" />
    -        </nodes>
    -    </content>
    -
    -</services>
    -
    -
    - -
    - -
    -$ vespa deploy --wait 300 my-app 
    -
    -
    - - -
    -$ sleep 20
    -
    - -After redeploying with the updated schemas and `services.xml`, -feed `mind/vespa_user_embeddings.json` and `mind/vespa_news_embeddings.json`: - -
    - -
    -$ vespa feed mind/vespa_user_embeddings.json --target http://localhost:8080
    -$ vespa feed mind/vespa_news_embeddings.json --target http://localhost:8080
    -
    -
    - -Once the feeding jobs finishes, the index is ready to be used, -we can verify that we have 65238 news documents and 94057 user documents: - - -
    -$ sleep 20
    -
    - -
    - -
    -$ vespa query -v \
    -  'yql=select * from news where true' \
    -  'hits=0'
    -
    -
    - -
    - -
    -$ vespa query -v \
    -  'yql=select * from user where true' \
    -  'hits=0'
    -
    -
    - - -## Query profiles and query profile types - -Before we can test the application, we need to add a query profile type. -The `recommendation` rank profile above requires a tensor to be sent along with the query. -For Vespa to bind the correct types, it needs to know the expected type of this query parameter. -That is called a query profile type. - -[Query profiles](../../querying/query-profiles.html) are named sets of search request parameters -that can be set as default, so you don't have to pass them along with the query. -We don't use this in this sample application. -Still we need to set up a default query profile to set up the types of query parameters we expect to pass. - -So, write the following to `news/my-app/search/query-profiles/default.xml`: - -
    - -
    -<query-profile id="default" type="root" />
    -
    -
    - -To set up the query profile types, write them to the file -`search/query-profiles/types/root.xml`: - -
    - -
    -<query-profile-type id="root" inherits="native">
    -    <field name="ranking.features.query(user_embedding)" type="tensor&lt;float&gt;(d0[50])" />
    -</query-profile-type>
    -
    -
    - -This configures Vespa to expect a float tensor with dimension `d0[50]` when the -query parameter `ranking.features.query(user_embedding)` is passed. -We'll see how this works together with the `nearestNeighbor` search operator below. - -{% include important.html content="Setting up this query profile type is required when sending a tensor as a query parameter. -A common pitfall is to forget the default query profile, -but that is required to successfully set up the query profile type." %} - -Deploy the updates to query profiles: -
    - -
    -$ vespa deploy --wait 300 my-app
    -
    -
    - - -## Testing the application - -We can now query Vespa using embeddings. -First, let's find the user `U33527`: - -
    - -
    -$ vespa query -v \
    -  'yql=select user_id, embedding from user where user_id contains "U33527"' \
    -  'hits=1'
    -
    -
    - -This returns the document containing the user's embedding: - -```json -{ - "root": { - ... - "children": [ - { - "id": "index:mind/0/ce7cc40b398f32626fcff97a", - "relevance": 0.0017429193899782135, - "source": "mind", - "fields": { - "user_id": "U33527", - "embedding": { - "type": "tensor(d0[50])", - "values": [ - 0.0, - 0.06090399995446205, - 0.15839800238609314, - ... - ] - } - } - } - ] - } -} -``` - -Now we can use this vector to query the news articles. -You can either write this query by hand, -but we've added a convenience script -[user_search.py](https://github.com/vespa-engine/sample-apps/blob/master/news/src/python/user_search.py) -which queries Vespa: - -
    - -
    -$ ./src/python/user_search.py U33527 10
    -
    -
    - -This script first retrieves the user embedding using an HTTP `GET` query to -Vespa. It then parses the tensor containing the embedding vector. Finally, it -issues a `nearestNeighbor` search using a `POST` (however a `GET` would work just as well). -Please see the [nearest-neighbor operator](../../reference/querying/yql.html#nearestneighbor) -for more on the syntax for nearest-neighbor searches. -The `nearestNeighbor` search looks like: - -```json -{ - "hits": 10, - "yql": "select * from sources news where (nearestNeighbor(embedding, user_embedding))", - "ranking.features.query(user_embedding)": "{ ... }" , - "ranking.profile": "recommendation" -} -``` - -Here, you can see the `nearestNeighbor` search operator being set up -so that the query parameter `user_embedding` will be searched against the `embedding` document field. -The tensor for the `user_embedding` is in the `ranking.features.query(user_embedding)` parameter. -Recall from above that we set a query profile type for this exact query parameter, -so Vespa knows what to expect here. - -When Vespa receives this query, it scans linearly through all documents, -and scores them using the `recommendation` rank profile we set up above. -Recall that we ask Vespa to convert the problem from maximum inner product to -a nearest distance problem by using the `dotproduct` distance metric; -in this case `distance` ranking feature just outputs the negative dotproduct. - -With a distance search, we want to find the smallest distances. -However, Vespa sorts the final results by decreasing rank score. -To get the expected rank order, Vespa provides the `closeness` feature which -in this case is just the dotproduct directly. - -Let's test that this works as intended, using -[evaluate.py](https://github.com/vespa-engine/sample-apps/blob/master/news/src/python/evaluate.py): - -
    - -
    -$ ./src/python/evaluate.py mind 1000
    -
    -
    - -This reads both the training and validation set impressions, -queries Vespa for 1000 randomly drawn impressions, -and calculates the same metrics we saw during training. -The result is something like: - -``` -Train: {'auc': 0.8774, 'mrr': 0.5115, 'ndcg@5': 0.5842, 'ndcg@10': 0.6345} -Valid: {'auc': 0.6308, 'mrr': 0.2935, 'ndcg@5': 0.3203, 'ndcg@10': 0.3789} -``` - -This is in line with the results from the training. -So, the conversion from inner product space to euclidean space works as intended. -The resulting rank scores are different, but the transformation evidently retains the same ordering. - - -## Approximate Nearest Neighbor Search - -So far, we've been using exact nearest-neighbor search. -This is a linear scan through all documents. -For the MIND demo dataset we've been using, -this isn't a problem as it only contains roughly 28000 documents, -and Vespa only uses a few milliseconds to scan through these. -However, as the index grows, the time (and computational cost) becomes significant. - -There are no exact methods for finding the nearest-neighbors efficiently. -So we trade accuracy for efficiency in what is called approximate nearest-neighbors (ANN). -Vespa provides a unique implementation of ANNs that uses the HNSW (hierarchical navigable small world) algorithm, -while still being compatible with other facets of the search such as filtering. -We'll get back to this in the next section. - -If you recall, Vespa returned something like the following when searching -for single users above (with `targetHits` equals to 10): - -```json - "root": { - "id": "toplevel", - "relevance": 1.0, - "fields": { - "totalCount": 95 - }, - "coverage": { - "coverage": 100, - "documents": 65238, - "full": true, - "nodes": 1, - "results": 1, - "resultsFull": 1 - } -``` - -Here, `coverage` shows that Vespa did scan through all 65238 documents. -The interesting piece here is the `totalCount`. -This number is the number of times a document has been put in the top 10 results during this linear scan. - -Let's switch to using approximate nearest-neighbors by adding `index` to the embedding field in `news.sd`: - -
    - -
    -schema news {
    -    document news {
    -        field news_id type string {
    -            indexing: summary | attribute
    -            attribute: fast-search
    -        }
    -        field category type string {
    -            indexing: summary | attribute
    -        }
    -        field subcategory type string {
    -            indexing: summary | attribute
    -        }
    -        field title type string {
    -            indexing: index | summary
    -            index: enable-bm25
    -        }
    -        field abstract type string {
    -            indexing: index | summary
    -            index: enable-bm25
    -        }
    -        field body type string {
    -            indexing: index | summary
    -            index: enable-bm25
    -        }
    -        field url type string {
    -            indexing: index | summary
    -        }
    -        field date type int {
    -            indexing: summary | attribute
    -            attribute: fast-search
    -        }
    -        field clicks type int {
    -            indexing: summary | attribute
    -        }
    -        field impressions type int {
    -            indexing: summary | attribute
    -        }
    -        field embedding type tensor<float>(d0[50]) {
    -            indexing: attribute | index
    -            attribute {
    -                distance-metric: dotproduct
    -            }
    -        }
    -    }
    -
    -    fieldset default {
    -        fields: title, abstract, body
    -    }
    -
    -    rank-profile popularity inherits default {
    -        function popularity() {
    -            expression: if (attribute(impressions) > 0, attribute(clicks) / attribute(impressions), 0)
    -        }
    -        first-phase {
    -            expression: nativeRank(title, abstract) + 10 * popularity
    -        }
    -    }
    -
    -    rank-profile recommendation inherits default {
    -        first-phase {
    -          expression: closeness(field, embedding)
    -        }
    -    }
    -}
    -
    -
    - -If you make this change and deploy it, you will get prompted by Vespa that a -restart is required so that the index can be built: - -
    - -
    -$ vespa deploy --wait 300 my-app
    -
    -
    - -Introducing the HNSW `index` requires a content node restart, in this case we restart all services: - -
    - -
    -$ docker exec vespa /usr/bin/sh -c \
    -  '/opt/vespa/bin/vespa-stop-services && /opt/vespa/bin/vespa-start-services'
    -
    -
    - - -
    -$ vespa status --wait 300 
    -
    - - -After doing this and waiting a bit for Vespa to start, we can query Vespa again: - -
    - -
    -$ ./src/python/user_search.py U33527 10
    -
    -
    - -```json -{ - "root": { - "id": "toplevel", - "relevance": 1.0, - "fields": { - "totalCount": 10 - }, - "coverage": { - "coverage": 100, - "documents": 65238, - "full": true, - "nodes": 1, - "results": 1, - "resultsFull": 1 - } -``` - -Here, `coverage` is still 100%, but the `totalCount` has been reduced to 10 - -the same number of hits we requested. -By adding the index to this field, Vespa built a HNSW graph structure for the values in this field. -When used in an approximate nearest-neighbor search, -this graph is queried and only the closest points as determined by this graph is added to the list. - -The particularly observant might have noticed that the result set has changed. -Indeed, the third result when using exact nearest neighbor search was news article `N438`. -This was omitted from the approximate search. -As mentioned, we trade accuracy for efficiency when using approximate nearest-neighbor search. - -It should also be mentioned that searching through this graph comes with a cost. -In our case, since we only have a relatively small amount of documents, -there isn't that much gain in efficiency. -However, as the number of documents grows, this starts to pay off. -See [Approximate nearest neighbor search in Vespa](https://blog.vespa.ai/approximate-nearest-neighbor-search-in-vespa-part-1/) -for more of a discussion around this. -See also [Billion-scale vector search with Vespa - part one](https://blog.vespa.ai/billion-scale-knn/) -and [Billion-scale vector search with Vespa - part two](https://blog.vespa.ai/billion-scale-knn-part-two/) -which cover the many trade-offs related to approximate nearest neighbor search. - -The implementation of ANN using HNSW in Vespa has some nice features. -Notice that we did not have to re-feed the corpus to enable ANN. -Many other approaches for ANNs require building an index offline in a batch job. -HNSW allows for incrementally building this index, which is fully exploited in Vespa. - -A unique feature of Vespa is that the implementation allows for filtering during graph traversal, -which we'll look at next. - - -## Filtering - -A common case when using approximate nearest-neighbors is to combine with some additional query filters. -For instance, for retail search, one can imagine finding relevant products for a user. -In this case, we should not recommend products that are out of stock. -So an additional query filter would be to ensure that `in_stock` is true. - -Now, most implementations of ANNs come in the form of a library, -so they are not integrated with the search at large. -The natural approach is to first perform the ANN, the *post-filter* the results. -Unfortunately, this often leads to sub-optimal results as relevant documents might not have been recalled. -See [Using approximate nearest-neighbor search in real world applications](https://blog.vespa.ai/using-approximate-nearest-neighbor-search-in-real-world-applications/) -for more of a discussion around this, -and [Query Time Constrained Approximate Nearest Neighbor Search](https://blog.vespa.ai/constrained-approximate-nearest-neighbor-search/) -for a better understanding of pre- and post-filtering tradeoffs. - -In our case, let's assume we want to retrieve 10 `sports` articles for a user. -It turns out we need to retrieve at least 278 news articles from the search to get to 10 `sports` articles for this user: - - -
    - -
    -$ ./src/python/user_search.py U63195  10 | grep "category\": \"sports\"" | wc -l
    -$ ./src/python/user_search.py U63195 278 | grep "category\": \"sports\"" | wc -l
    -
    -
    - -On the other hand, if we add a filter specifically: - -
    - -
    -$ ./src/python/user_search.py U63195 10 "AND category contains 'sports'" | \
    -  grep "category\": \"sports" | wc -l
    -
    -
    - -Here, we only specify 10 hits and exactly 10 hits of `sports` category are returned. -Vespa still searches through the graph starting from the query point, -however the search does not stop when we have 10 hits. -In effect, the graph search widens until 10 results fulfilling the filters are found. - -As a note, strict filters that filter away a large part of the corpus -would entail that many candidates in the graph are skipped while searching for the results that fulfill the filters. -This can take an exponential amount of time. -For this case, Vespa falls back to a linear, brute-force search over the few documents -which matches the filter for efficiency. - - -## Conclusion - -We now have a basic recommendation system up and running. -We can query for a user, retrieve the embedding vector and use that for querying the news articles. -Right now, this means two calls to Vespa. -In the [next part of the tutorial](news-6-recommendation-with-searchers.html), -we will introduce `searchers`, -which allows for custom logic during query processing inside the Vespa cluster, -requiring only one pass from the client to Vespa. - - diff --git a/en/learn/tutorials/news-5-recommendation.mdx b/en/learn/tutorials/news-5-recommendation.mdx new file mode 100644 index 0000000000..3d790db95e --- /dev/null +++ b/en/learn/tutorials/news-5-recommendation.mdx @@ -0,0 +1,467 @@ +--- +title: "News search and recommendation tutorial - recommendation" +--- + +This is the fifth part of the tutorial series for setting up a Vespa application for personalized news recommendations. The parts are: + +1. [Getting started](/en/learn/tutorials/news-1-deploy-an-application) +2. [A basic news search application](/en/learn/tutorials/news-2-basic-feeding-and-query) - application packages, feeding, query +3. [News search](/en/learn/tutorials/news-3-searching) - sorting, grouping, and ranking +4. [Generating embeddings for users and news articles](/en/learn/tutorials/news-4-embeddings) +5. [News recommendation](/en/learn/tutorials/news-5-recommendation) - partial updates (news embeddings), ANNs, filtering +6. [News recommendation with searchers](/en/learn/tutorials/news-6-recommendation-with-searchers) - custom searchers, doc processors +7. [News recommendation with parent-child](/en/learn/tutorials/news-7-recommendation-with-parent-child) - parent-child, tensor ranking +8. Advanced news recommendation - intermission - training a ranking model +9. Advanced news recommendation - ML models + +In this part, we'll start transforming our application from news search to recommendation using the embeddings we created in the previous part. So, we'll start by modifying our application, so we can feed the embeddings and start using them for searching. + +For reference, the final state of this tutorial can be found in the [app-5-recommendation](https://github.com/vespa-engine/sample-apps/tree/master/news/app-5-recommendation) sub-directory of the `news` sample application. + + +## Indexing embeddings + +First, we need to modify the `news.sd` search definition to include a field to hold the embedding and a recommendation rank profile: + +```sd expandable +schema news { + document news { + field news_id type string { + indexing: summary | attribute + attribute: fast-search + } + field category type string { + indexing: summary | attribute + } + field subcategory type string { + indexing: summary | attribute + } + field title type string { + indexing: index | summary + index: enable-bm25 + } + field abstract type string { + indexing: index | summary + index: enable-bm25 + } + field body type string { + indexing: index | summary + index: enable-bm25 + } + field url type string { + indexing: index | summary + } + field date type int { + indexing: summary | attribute + } + field clicks type int { + indexing: summary | attribute + } + field impressions type int { + indexing: summary | attribute + } + field embedding type tensor(d0[50]) { + indexing: attribute + attribute { + distance-metric: dotproduct + } + } + } + + fieldset default { + fields: title, abstract, body + } + + rank-profile popularity inherits default { + function popularity() { + expression: if (attribute(impressions) > 0, attribute(clicks) / attribute(impressions), 0) + } + first-phase { + expression: nativeRank(title, abstract) + 10 * popularity + } + } + + rank-profile recommendation inherits default { + first-phase { + expression: closeness(field, embedding) + } + } +} +``` + +The `embedding` field is a tensor field. Tensors in Vespa are flexible multi-dimensional data structures, and, as first-class citizens, can be used in queries, document fields, and constants in ranking. Tensors can be either dense or sparse or both, and can contain any number of dimensions. See [the tensor user guide](/en/ranking/tensor-user-guide) for more information. + +Here we have defined a dense tensor with a single dimension (`d0` - dimension 0), which represents a vector. The distance metric is "dotproduct" as we would like to use this field for nearest-neighbor search where we search for the maximal dotproduct. + +This is seen in the `recommendation` rank profile. Here, we've added a ranking expression using the [closeness](/en/reference/ranking/rank-features#closeness(dimension,name)) ranking feature, which calculates the dot product and uses that to rank the news articles. This depends on using the `nearestNeighbor` search operator, which we'll get back to below when searching. But for now, this expects a tensor in the query to be used as the initial search point. + +If you take a look at the file generated for the news embeddings, `mind/vespa_news_embeddings.json`, you'll see several lines with something like this: + +```json +{ + "update": "id:news:news::N13390", + "fields": { + "embedding": { + "assign": { + "values": [9.871717,-0.403103,...] + } + } + } +} +``` + +This is a [partial update](/en/writing/partial-updates). So, assuming you already have a system up and running from the previous search tutorial, you don't need to feed the entire corpus. With a partial update, you only need to update the necessary fields. So, after training another set of embeddings you can partially feed them again. Please refer to [Vespa reads and writes](/en/writing/reads-and-writes) for more information on feeding formats. + +We need to add another document type to represent a user. Add this schema in `schemas/user.sd`: + +```sd +schema user { + document user { + field user_id type string { + indexing: summary | attribute + attribute: fast-search + } + field embedding type tensor(d0[50]) { + indexing: summary | attribute + } + } +} +``` + +This schema is set up so that we can search for a `user_id` and retrieve the user's embedding vector. + +We also need to let Vespa know we want to use this document type, so we modify `services.xml` and add it under `documents` in the `content` section: + +```xml + + + + +``` + +```bash +$ vespa deploy --wait 300 my-app +``` + +```bash +$ sleep 20 +``` + +After redeploying with the updated schemas and `services.xml`, feed `mind/vespa_user_embeddings.json` and `mind/vespa_news_embeddings.json`: + +```bash +$ vespa feed mind/vespa_user_embeddings.json --target http://localhost:8080 +$ vespa feed mind/vespa_news_embeddings.json --target http://localhost:8080 +``` + +Once the feeding jobs finishes, the index is ready to be used, we can verify that we have 65238 news documents and 94057 user documents: + +```bash +$ sleep 20 +``` + +```bash +$ vespa query -v \ + 'yql=select * from news where true' \ + 'hits=0' +``` + +```bash +$ vespa query -v \ + 'yql=select * from user where true' \ + 'hits=0' +``` + + +## Query profiles and query profile types + +Before we can test the application, we need to add a query profile type. The `recommendation` rank profile above requires a tensor to be sent along with the query. For Vespa to bind the correct types, it needs to know the expected type of this query parameter. That is called a query profile type. + +[Query profiles](/en/reference/querying/query-profiles) are named sets of search request parameters that can be set as default, so you don't have to pass them along with the query. We don't use this in this sample application. Still we need to set up a default query profile to set up the types of query parameters we expect to pass. + +So, write the following to `news/my-app/search/query-profiles/default.xml`: + +```xml + +``` + +To set up the query profile types, write them to the file `search/query-profiles/types/root.xml`: + +```xml + + + +``` + +This configures Vespa to expect a float tensor with dimension `d0[50]` when the query parameter `ranking.features.query(user_embedding)` is passed. We'll see how this works together with the `nearestNeighbor` search operator below. + + +Setting up this query profile type is required when sending a tensor as a query parameter. A common pitfall is to forget the default query profile, but that is required to successfully set up the query profile type. + + +Deploy the updates to query profiles: + +```bash +$ vespa deploy --wait 300 my-app +``` + + +## Testing the application + +We can now query Vespa using embeddings. First, let's find the user `U33527`: + +```bash +$ vespa query -v \ + 'yql=select user_id, embedding from user where user_id contains "U33527"' \ + 'hits=1' +``` + +This returns the document containing the user's embedding: + +```json expandable +{ + "root": { + ... + "children": [ + { + "id": "index:mind/0/ce7cc40b398f32626fcff97a", + "relevance": 0.0017429193899782135, + "source": "mind", + "fields": { + "user_id": "U33527", + "embedding": { + "type": "tensor(d0[50])", + "values": [ + 0.0, + 0.06090399995446205, + 0.15839800238609314, + ... + ] + } + } + } + ] + } +} +``` + +Now we can use this vector to query the news articles. You can either write this query by hand, but we've added a convenience script [user_search.py](https://github.com/vespa-engine/sample-apps/blob/master/news/src/python/user_search.py) which queries Vespa: + +```bash +$ ./src/python/user_search.py U33527 10 +``` + +This script first retrieves the user embedding using an HTTP `GET` query to Vespa. It then parses the tensor containing the embedding vector. Finally, it issues a `nearestNeighbor` search using a `POST` (however a `GET` would work just as well). Please see the [nearest-neighbor operator](/en/reference/querying/yql#nearestneighbor) for more on the syntax for nearest-neighbor searches. The `nearestNeighbor` search looks like: + +```json +{ + "hits": 10, + "yql": "select * from sources news where (nearestNeighbor(embedding, user_embedding))", + "ranking.features.query(user_embedding)": "{ ... }", + "ranking.profile": "recommendation" +} +``` + +Here, you can see the `nearestNeighbor` search operator being set up so that the query parameter `user_embedding` will be searched against the `embedding` document field. The tensor for the `user_embedding` is in the `ranking.features.query(user_embedding)` parameter. Recall from above that we set a query profile type for this exact query parameter, so Vespa knows what to expect here. + +When Vespa receives this query, it scans linearly through all documents, and scores them using the `recommendation` rank profile we set up above. Recall that we ask Vespa to convert the problem from maximum inner product to a nearest distance problem by using the `dotproduct` distance metric; in this case `distance` ranking feature just outputs the negative dotproduct. + +With a distance search, we want to find the smallest distances. However, Vespa sorts the final results by decreasing rank score. To get the expected rank order, Vespa provides the `closeness` feature which in this case is just the dotproduct directly. + +Let's test that this works as intended, using [evaluate.py](https://github.com/vespa-engine/sample-apps/blob/master/news/src/python/evaluate.py): + +```bash +$ ./src/python/evaluate.py mind 1000 +``` + +This reads both the training and validation set impressions, queries Vespa for 1000 randomly drawn impressions, and calculates the same metrics we saw during training. The result is something like: + +``` +Train: {'auc': 0.8774, 'mrr': 0.5115, 'ndcg@5': 0.5842, 'ndcg@10': 0.6345} +Valid: {'auc': 0.6308, 'mrr': 0.2935, 'ndcg@5': 0.3203, 'ndcg@10': 0.3789} +``` + +This is in line with the results from the training. So, the conversion from inner product space to euclidean space works as intended. The resulting rank scores are different, but the transformation evidently retains the same ordering. + + +## Approximate Nearest Neighbor Search + +So far, we've been using exact nearest-neighbor search. This is a linear scan through all documents. For the MIND demo dataset we've been using, this isn't a problem as it only contains roughly 28000 documents, and Vespa only uses a few milliseconds to scan through these. However, as the index grows, the time (and computational cost) becomes significant. + +There are no exact methods for finding the nearest-neighbors efficiently. So we trade accuracy for efficiency in what is called approximate nearest-neighbors (ANN). Vespa provides a unique implementation of ANNs that uses the HNSW (hierarchical navigable small world) algorithm, while still being compatible with other facets of the search such as filtering. We'll get back to this in the next section. + +If you recall, Vespa returned something like the following when searching for single users above (with `targetHits` equals to 10): + +```json expandable +{ + "root": { + "id": "toplevel", + "relevance": 1.0, + "fields": { + "totalCount": 95 + }, + "coverage": { + "coverage": 100, + "documents": 65238, + "full": true, + "nodes": 1, + "results": 1, + "resultsFull": 1 + } + } +} +``` + +Here, `coverage` shows that Vespa did scan through all 65238 documents. The interesting piece here is the `totalCount`. This number is the number of times a document has been put in the top 10 results during this linear scan. + +Let's switch to using approximate nearest-neighbors by adding `index` to the embedding field in `news.sd`: + +```sd expandable +schema news { + document news { + field news_id type string { + indexing: summary | attribute + attribute: fast-search + } + field category type string { + indexing: summary | attribute + } + field subcategory type string { + indexing: summary | attribute + } + field title type string { + indexing: index | summary + index: enable-bm25 + } + field abstract type string { + indexing: index | summary + index: enable-bm25 + } + field body type string { + indexing: index | summary + index: enable-bm25 + } + field url type string { + indexing: index | summary + } + field date type int { + indexing: summary | attribute + attribute: fast-search + } + field clicks type int { + indexing: summary | attribute + } + field impressions type int { + indexing: summary | attribute + } + field embedding type tensor(d0[50]) { + indexing: attribute | index + attribute { + distance-metric: dotproduct + } + } + } + + fieldset default { + fields: title, abstract, body + } + + rank-profile popularity inherits default { + function popularity() { + expression: if (attribute(impressions) > 0, attribute(clicks) / attribute(impressions), 0) + } + first-phase { + expression: nativeRank(title, abstract) + 10 * popularity + } + } + + rank-profile recommendation inherits default { + first-phase { + expression: closeness(field, embedding) + } + } +} +``` + +If you make this change and deploy it, you will get prompted by Vespa that a restart is required so that the index can be built: + +```bash +$ vespa deploy --wait 300 my-app +``` + +Introducing the HNSW `index` requires a content node restart, in this case we restart all services: + +```bash +$ docker exec vespa /usr/bin/sh -c \ + '/opt/vespa/bin/vespa-stop-services && /opt/vespa/bin/vespa-start-services' +``` + +```bash +$ vespa status --wait 300 +``` + +After doing this and waiting a bit for Vespa to start, we can query Vespa again: + +```bash +$ ./src/python/user_search.py U33527 10 +``` + +```json expandable +{ + "root": { + "id": "toplevel", + "relevance": 1.0, + "fields": { + "totalCount": 10 + }, + "coverage": { + "coverage": 100, + "documents": 65238, + "full": true, + "nodes": 1, + "results": 1, + "resultsFull": 1 + } + } +} +``` + +Here, `coverage` is still 100%, but the `totalCount` has been reduced to 10 - the same number of hits we requested. By adding the index to this field, Vespa built a HNSW graph structure for the values in this field. When used in an approximate nearest-neighbor search, this graph is queried and only the closest points as determined by this graph is added to the list. + +The particularly observant might have noticed that the result set has changed. Indeed, the third result when using exact nearest neighbor search was news article `N438`. This was omitted from the approximate search. As mentioned, we trade accuracy for efficiency when using approximate nearest-neighbor search. + +It should also be mentioned that searching through this graph comes with a cost. In our case, since we only have a relatively small amount of documents, there isn't that much gain in efficiency. However, as the number of documents grows, this starts to pay off. See [Approximate nearest neighbor search in Vespa](https://blog.vespa.ai/approximate-nearest-neighbor-search-in-vespa-part-1/) for more of a discussion around this. See also [Billion-scale vector search with Vespa - part one](https://blog.vespa.ai/billion-scale-knn/) and [Billion-scale vector search with Vespa - part two](https://blog.vespa.ai/billion-scale-knn-part-two/) which cover the many trade-offs related to approximate nearest neighbor search. + +The implementation of ANN using HNSW in Vespa has some nice features. Notice that we did not have to re-feed the corpus to enable ANN. Many other approaches for ANNs require building an index offline in a batch job. HNSW allows for incrementally building this index, which is fully exploited in Vespa. + +A unique feature of Vespa is that the implementation allows for filtering during graph traversal, which we'll look at next. + + +## Filtering + +A common case when using approximate nearest-neighbors is to combine with some additional query filters. For instance, for retail search, one can imagine finding relevant products for a user. In this case, we should not recommend products that are out of stock. So an additional query filter would be to ensure that `in_stock` is true. + +Now, most implementations of ANNs come in the form of a library, so they are not integrated with the search at large. The natural approach is to first perform the ANN, the *post-filter* the results. Unfortunately, this often leads to sub-optimal results as relevant documents might not have been recalled. See [Using approximate nearest-neighbor search in real world applications](https://blog.vespa.ai/using-approximate-nearest-neighbor-search-in-real-world-applications/) for more of a discussion around this, and [Query Time Constrained Approximate Nearest Neighbor Search](https://blog.vespa.ai/constrained-approximate-nearest-neighbor-search/) for a better understanding of pre- and post-filtering tradeoffs. + +In our case, let's assume we want to retrieve 10 `sports` articles for a user. It turns out we need to retrieve at least 278 news articles from the search to get to 10 `sports` articles for this user: + +```bash +$ ./src/python/user_search.py U63195 10 | grep "category\": \"sports\"" | wc -l +$ ./src/python/user_search.py U63195 278 | grep "category\": \"sports\"" | wc -l +``` + +On the other hand, if we add a filter specifically: + +```bash +$ ./src/python/user_search.py U63195 10 "AND category contains 'sports'" | \ + grep "category\": \"sports" | wc -l +``` + +Here, we only specify 10 hits and exactly 10 hits of `sports` category are returned. Vespa still searches through the graph starting from the query point, however the search does not stop when we have 10 hits. In effect, the graph search widens until 10 results fulfilling the filters are found. + +As a note, strict filters that filter away a large part of the corpus would entail that many candidates in the graph are skipped while searching for the results that fulfill the filters. This can take an exponential amount of time. For this case, Vespa falls back to a linear, brute-force search over the few documents which matches the filter for efficiency. + + +## Conclusion + +We now have a basic recommendation system up and running. We can query for a user, retrieve the embedding vector and use that for querying the news articles. Right now, this means two calls to Vespa. In the [next part of the tutorial](/en/learn/tutorials/news-6-recommendation-with-searchers), we will introduce `searchers`, which allows for custom logic during query processing inside the Vespa cluster, requiring only one pass from the client to Vespa. diff --git a/en/learn/tutorials/news-6-recommendation-with-searchers.md b/en/learn/tutorials/news-6-recommendation-with-searchers.md deleted file mode 100644 index 8eb524680c..0000000000 --- a/en/learn/tutorials/news-6-recommendation-with-searchers.md +++ /dev/null @@ -1,409 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "News search and recommendation tutorial - Searchers" -redirect_from: - - /en/tutorials/news-6-recommendation-with-searchers ---- - -This is the sixth part of the tutorial series for setting up a Vespa -application for personalized news recommendations. The parts are: - -1. [Getting started](news-1-deploy-an-application.html) -2. [A basic news search application](news-2-basic-feeding-and-query.html) - application packages, feeding, query -3. [News search](news-3-searching.html) - sorting, grouping, and ranking -4. [Generating embeddings for users and news articles](news-4-embeddings.html) -5. [News recommendation](news-5-recommendation.html) - partial updates (news embeddings), ANNs, filtering -6. [News recommendation with searchers](news-6-recommendation-with-searchers.html) - custom searchers, doc processors -7. [News recommendation with parent-child](news-7-recommendation-with-parent-child.html) - parent-child, tensor ranking -8. Advanced news recommendation - intermission - training a ranking model -9. Advanced news recommendation - ML models - -In the previous part of this series, we set up a recommendation system that, -given a user id, needed two requests to generate a recommendation. -The first to retrieve the user embedding, and a second for finding the nearest neighbor news articles. -In this part, we'll introduce `Searchers`, -which are processors that can modify queries before passing them along to search. -These allow us to pull the logic from the Python scripts into Vespa. - -For reference, the final state of this tutorial can be found in the -[app-6-recommendation-with-searchers](https://github.com/vespa-engine/sample-apps/tree/master/news/app-6-recommendation-with-searchers) -directory of the `news` sample application. - - -## Searchers and document processors - -First, let's revisit Vespa's overall architecture: - -Vespa Overview - -Recall that the application package contains everything necessary to run the application. -When this is deployed, the config cluster takes care of distributing the services to the various nodes. -In particular, the two main types of nodes are the stateless `container` nodes and the stateful `content` nodes. - -All requests pass through the `container` cluster before passing along to -`content` cluster where the actual retrieval and ranking occurs. -The queries actually pass through a chain of Searchers; -each one possibly doing a small amount of processing. -This can be seen by adding a `&trace.level=5` to a query: - -```json -{ "message": "Invoke searcher 'com.yahoo.search.querytransform.WeakAndReplacementSearcher in vespa'" }, -{ "message": "Invoke searcher 'com.yahoo.prelude.statistics.StatisticsSearcher in native'" }, -{ "message": "Invoke searcher 'com.yahoo.prelude.querytransform.PhrasingSearcher in vespa'" }, -{ "message": "Invoke searcher 'com.yahoo.prelude.searcher.FieldCollapsingSearcher in vespa'" }, -{ "message": "Invoke searcher 'com.yahoo.search.yql.MinimalQueryInserter in vespa'" }, - -{ "message": "Federating to [mind]" }, - -{ "message": "Got 10 hits from source:mind" }, -{ "message": "Return searcher 'federation in native'" }, - -{ "message": "Return searcher 'com.yahoo.search.yql.MinimalQueryInserter in vespa'" }, -{ "message": "Return searcher 'com.yahoo.prelude.searcher.FieldCollapsingSearcher in vespa'" }, -{ "message": "Return searcher 'com.yahoo.prelude.querytransform.PhrasingSearcher in vespa'" }, -{ "message": "Return searcher 'com.yahoo.prelude.statistics.StatisticsSearcher in native'" }, -{ "message": "Return searcher 'com.yahoo.search.querytransform.WeakAndReplacementSearcher in vespa'" }, -``` - -This shows a small sample of the additional output when using `trace.level`. -Note the invocations of the Searchers. -Each Searcher gets invoked along a chain, -and the last Searcher in the chain sends the post-processed query to the search backend. -When the results come back, the processing passes back up the chain. -The Searchers can then process the results before passing them to the previous Searcher, -and ultimately back as a response to the query. - -{% include note.html content="Adding a [trace.level](../../reference/api/query.html#trace.level) -is generally helpful when debugging vespa queries." %} - -So, [Searchers](../../applications/searchers.html) are Java components that do some kind of processing along the query chain; -either modifying the query before the actual search, -modifying the results after the search, -or some combination of both. - -Developers can provide their own Searchers and inject them into the query chain. -We'll capitalize on this and create a Searcher that performs essentially the same task that -[user_search.py](https://github.com/vespa-engine/sample-apps/blob/master/news/src/python/user_search.py) does: -retrieve a user embedding and do a news article search based on that. -In the process, we'll only pass a `user_id` to Vespa instead of a full YQL query: -`/search/?user_id=U33527&searchchain=user` - -The search will take care of creating the actual query for us - let's get started. - - -## Adding a user profile Searcher - -While the `content` layer in Vespa is written in C++ for maximum performance, -the `container` layer is in Java for flexibility. -So, all Searchers and thus custom Searchers are written in Java. -Refer to [the guide on Searcher development](../../applications/searchers.html) for more information. - -We want to create a Searcher that takes a `user_id`, -issues a query to find the corresponding embedding, -then issues a second query to retrieve the news articles. - -To do this, we create a `UserProfileSearcher` that extends the base Searcher class `com.yahoo.search.Searcher`. -This Searcher must implement a single method: `search`, -and has the responsibility of passing the query to the next Searcher on the list. A minimal example: - -```java -public class UserProfileSearcher extends Searcher { - public Result search(Query query, Execution execution) { - // ... process query - Result results = execution.search(query) - // ... process results - return results; - } -} -``` - -So, what we do before we pass the query along (in `execution.search(query)`) and -before we return the results is completely up to us. -So, we implement our `UserProfileSearcher` like this: - -
    - -
    -public class UserProfileSearcher extends Searcher {
    -
    -    public Result search(Query query, Execution execution) {
    -
    -        // Get tensor and read items from user profile
    -        Object userIdProperty = query.properties().get("user_id");
    -        if (userIdProperty != null) {
    -
    -            // Retrieve user embedding by doing a search for the user_id and extract the tensor
    -            Tensor userEmbedding = retrieveUserEmbedding(userIdProperty.toString(), execution);
    -
    -            // Create a new search using the user's embedding tensor
    -            NearestNeighborItem nn = new NearestNeighborItem("embedding", "user_embedding");
    -            nn.setTargetNumHits(query.getHits());
    -            nn.setAllowApproximate(true);
    -
    -            query.getModel().getQueryTree().setRoot(nn);
    -            query.getRanking().getFeatures().put("query(user_embedding)", userEmbedding);
    -            query.getModel().setRestrict("news");
    -
    -            // Override default rank profile
    -            if (query.getRanking().getProfile().equals("default")) {
    -                query.getRanking().setProfile("recommendation");
    -            }
    -        }
    -
    -        return execution.search(query);
    -    }
    -
    -    private Tensor retrieveUserEmbedding(String userId, Execution execution) {
    -        Query query = new Query();
    -        query.getModel().setRestrict("user");
    -        query.getModel().getQueryTree().setRoot(new WordItem(userId, "user_id"));
    -        query.setHits(1);
    -
    -        Result result = execution.search(query);
    -        execution.fill(result); // This is needed to get the actual summary data
    -
    -        if (result.getTotalHitCount() == 0)
    -            throw new RuntimeException("User id " + userId + " not found...");
    -        return (Tensor) result.hits().get(0).getField("embedding");
    -    }
    -
    -}
    -
    -
    - -First, we retrieve the `user_id` from the query. -If this is given in the query, we first call the `retrieveUserEmbedding` method, -which creates a new `Query` to find the user's embedding. -This is a straight-forward search which is restricted to the `user` document type. -Since the `user_id` is unique, we only expect a single hit. -We then extract the `embedding` tensor from the user document. - -{% include note.html content="We explicitly call a *fill* on the results before returning. -A query is usually passed to the search backend at least twice: one to retrieve the top ranked results, -another to retrieve the summary data of the final result set. -This is to avoid sending excess data between services. -For instance, if searching for the top 10 results with two search backends, -each backend will retrieve the top 10 results from the local content on that node. -A Searcher will determine the global top ten ranked results (potentially including diversification) -and only issue a *fill* to retrieve the summary features for those top 10." %} - -Now that we've retrieved the user embedding, -we programmatically set up a nearest-neighbor search, -and add the user embedding to the query as the ranking feature `query(user_embedding)`. -The search is then passed along to the next Searcher in the chain. -We do not need to explicitly fill the result here, -as that is guaranteed to happen before ultimately rendering the results. - -Again, note that all this is pretty much the same as what we did in -[user_search.py](https://github.com/vespa-engine/sample-apps/blob/master/news/src/python/user_search.py) - -just in Java. - - -## Adding a search chain - -To add this Searcher to Vespa, we need to modify `services.xml`: -
    - -
    -<container id="default" version="1.0">
    -    <search>
    -        <chain id="user" inherits="vespa">
    -            <searcher bundle="news-recommendation-searcher"
    -                      id="ai.vespa.example.UserProfileSearcher" />
    -        </chain>
    -    </search>
    -    ...
    -</container>
    -
    -
    - -Here, we instruct Vespa to add a new search chain called `user` -(which inherits the default `vespa` search chain), -and includes our `UserProfileSearcher`. -Note that Vespa expects this Searcher to be in a bundle called `news-recommendation`, -so we need to compile and package this code. -In Vespa, we use [Apache Maven](https://maven.apache.org/) for this, -which requires a project object model, or `pom.xml`, -to specify how to build this artifact. - -We won't go through that here; please refer to -[app-6-recommendation-with-searchers](https://github.com/vespa-engine/sample-apps/tree/master/news/app-6-recommendation-with-searchers) -in the `news` sample application for details. -Note that this application's directory structure has -changed compared to the previous parts in the tutorial. -The structure is now: - -``` -. -├── pom.xml -└── src - └── main - ├── application - │ ├── schemas - │ │ ├── news.sd - │ │ └── user.sd - │ ├── search - │ │ └── query-profiles - │ │ ├── default.xml - │ │ └── types - │ │ └── root.xml - │ └── services.xml - └── java - └── ai - └── vespa - └── example - └── UserProfileSearcher.java -``` - -The Vespa application now lies under `src/main/application`, -and all custom Java components are under `src/main/java` as is standard in a Java project. -We can now compile and package this application: - -
    - -
    -$ (cd app-6-recommendation-with-searchers && mvn package)
    -
    -
    - -[pom.xml](https://github.com/vespa-engine/sample-apps/blob/master/news/app-6-recommendation-with-searchers/pom.xml) -is set up to create an artifact called `news-recommendation-searcher`, -which is referred to in `services.xml`. -When the command finishes, we can see this artifact in `target/application.zip`. -This contains the full Vespa application, with Java components - deploy it: - -
    - -
    -$ vespa deploy --wait 300 app-6-recommendation-with-searchers
    -
    -
    - -After the application has been deployed, we are ready to test. -Refer to [the Searcher development guide](../../applications/searchers.html) -for much more on custom Searchers and the Java API. - - -## Testing - -Now we can search for a user's recommended news articles directly from the `user_id`: - -
    - -
    -$ vespa query -v \
    -  'user_id=U33527' \
    -  'searchChain=user'
    -
    -
    - -This should now return the top 10 recommended news articles for this user. -Indeed, if we now add a with a `trace.level=5`, we see the Searcher being invoked: - -
    - -
    -$ vespa query -v \
    -  'user_id=U33527' \
    -  'searchChain=user' \
    -  'trace.level=5'
    -
    -
    - -```json - { "message": "Invoke searcher 'ai.vespa.example.UserProfileSearcher in user'" }, - { "message": "Invoke searcher 'com.yahoo.search.querytransform.WeakAndReplacementSearcher in vespa'" }, - { "message": "Invoke searcher 'com.yahoo.prelude.statistics.StatisticsSearcher in native'" }, - - { "message": "Return searcher 'com.yahoo.prelude.statistics.StatisticsSearcher in native'" }, - { "message": "Return searcher 'com.yahoo.search.querytransform.WeakAndReplacementSearcher in vespa'" }, - { "message": "Return searcher 'ai.vespa.example.UserProfileSearcher in user'" }, -``` - -Note that the `searchChain` query parameter can be set as default, -so this does not have to be passed with the query request. -This is done by adding it to the default query profile in -[src/main/application/search/query-profiles/default.xml](https://github.com/vespa-engine/sample-apps/blob/master/news/app-6-recommendation-with-searchers/src/main/application/search/query-profiles/default.xml): - -
    - -
    -<query-profile id="default" type="root"<
    -    <field name="searchChain">user</field<
    -</query-profile>
    -
    -
    - -{% include note.html content="[src/python/evaluate.py](https://github.com/vespa-engine/sample-apps/blob/master/news/src/python/evaluate.py) -can now be modified to use this Searcher. -However, to properly calculate the metrics, -the Searcher needs to be modified to accept a list of news article id's and only recall those. -We'll leave this as an exercise to the reader." %} - - -## Document processors - -As can be seen in the architecture overview above, there are other component types as well. -One is Document Processors, which are conceptually similar to Searchers. -When a document is fed to Vespa, it goes through a chain of Document Processors -before being passed to the content node for storage and indexing. - -Vespa also supports custom Document Processors, refer to -[the guide for document processing](../../applications/document-processors.html) for more information. - - -## Improving recommendation diversity - -If we take a closer look at the query above, and search for the top 100 hits: - -
    - -
    -$ vespa query \
    -  'user_id=U33527' \
    -  'searchChain=user' \
    -  'hits=100' | \
    -  grep "category\": \"sports" | wc -l
    -
    -
    - - - -We see that all the hits are of category `sports` for this user. -Actually, they are all from the `football_nfl` sub-category. -Indeed, from inspection of the impressions file, this user has only clicked on `sports` articles. -So, while this can seem a success, -we generally would like to give users some form of diversity to keep them interested. -This is also to combat the negative effects of filter bubbles. - -One way to do this is to create Searchers that perform multiple queries -to the backend with various rank profiles. -In the above, we were only retrieving results from the `recommendation` rank profile. -Still, we can have any number of rank profiles. -By searching in multiple rank profiles, we can blend the results from these sources before returning to the user, -and thus introduce diversity. - -This is often called federation. -Vespa supports federation both from internal and external sources, -see [the guide on federation](../../querying/federation.html) for more information. - -{% include important.html content="If the same document can be returned from multiple sources, -it's important to perform some form of de-duplication before returning the final results!" %} - -A common way of performing blending from multiple sources is to implement a specialized blending Searcher. -This Searcher can, for instance, -use an approach such as [reciprocal rank fusion](https://research.google/pubs/pub36196/), -which gives decent results. -However, when it comes to diversity, there are usually some goals or restrictions that needs to be controlled. -In this case the business rules can be hand-written in the blending Searcher. -Searchers are flexible enough to perform any type of processing. - - -## Conclusion - -We now have a Vespa application up and running that takes a single `user_id` -and returns recommendations for that user. -In the [next part of the tutorial](news-7-recommendation-with-parent-child.html), -we'll address what to do when new users without any history visit our recommendation system. - - diff --git a/en/learn/tutorials/news-6-recommendation-with-searchers.mdx b/en/learn/tutorials/news-6-recommendation-with-searchers.mdx new file mode 100644 index 0000000000..92d87c8fd9 --- /dev/null +++ b/en/learn/tutorials/news-6-recommendation-with-searchers.mdx @@ -0,0 +1,278 @@ +--- +title: "News search and recommendation tutorial - Searchers" +--- + +This is the sixth part of the tutorial series for setting up a Vespa application for personalized news recommendations. The parts are: + +1. [Getting started](/en/learn/tutorials/news-1-deploy-an-application) +2. [A basic news search application](/en/learn/tutorials/news-2-basic-feeding-and-query) - application packages, feeding, query +3. [News search](/en/learn/tutorials/news-3-searching) - sorting, grouping, and ranking +4. [Generating embeddings for users and news articles](/en/learn/tutorials/news-4-embeddings) +5. [News recommendation](/en/learn/tutorials/news-5-recommendation) - partial updates (news embeddings), ANNs, filtering +6. [News recommendation with searchers](/en/learn/tutorials/news-6-recommendation-with-searchers) - custom searchers, doc processors +7. [News recommendation with parent-child](/en/learn/tutorials/news-7-recommendation-with-parent-child) - parent-child, tensor ranking +8. Advanced news recommendation - intermission - training a ranking model +9. Advanced news recommendation - ML models + +In the previous part of this series, we set up a recommendation system that, given a user id, needed two requests to generate a recommendation. The first to retrieve the user embedding, and a second for finding the nearest neighbor news articles. In this part, we'll introduce `Searchers`, which are processors that can modify queries before passing them along to search. These allow us to pull the logic from the Python scripts into Vespa. + +For reference, the final state of this tutorial can be found in the [app-6-recommendation-with-searchers](https://github.com/vespa-engine/sample-apps/tree/master/news/app-6-recommendation-with-searchers) directory of the `news` sample application. + + +## Searchers and document processors + +First, let's revisit Vespa's overall architecture: + + +![Vespa Overview](/assets/img/vespa-overview.svg) + + +Recall that the application package contains everything necessary to run the application. When this is deployed, the config cluster takes care of distributing the services to the various nodes. In particular, the two main types of nodes are the stateless `container` nodes and the stateful `content` nodes. + +All requests pass through the `container` cluster before passing along to `content` cluster where the actual retrieval and ranking occurs. The queries actually pass through a chain of Searchers; each one possibly doing a small amount of processing. This can be seen by adding a `&trace.level=5` to a query: + +```json expandable +{ "message": "Invoke searcher 'com.yahoo.search.querytransform.WeakAndReplacementSearcher in vespa'" }, +{ "message": "Invoke searcher 'com.yahoo.prelude.statistics.StatisticsSearcher in native'" }, +{ "message": "Invoke searcher 'com.yahoo.prelude.querytransform.PhrasingSearcher in vespa'" }, +{ "message": "Invoke searcher 'com.yahoo.prelude.searcher.FieldCollapsingSearcher in vespa'" }, +{ "message": "Invoke searcher 'com.yahoo.search.yql.MinimalQueryInserter in vespa'" }, + +{ "message": "Federating to [mind]" }, + +{ "message": "Got 10 hits from source:mind" }, +{ "message": "Return searcher 'federation in native'" }, + +{ "message": "Return searcher 'com.yahoo.search.yql.MinimalQueryInserter in vespa'" }, +{ "message": "Return searcher 'com.yahoo.prelude.searcher.FieldCollapsingSearcher in vespa'" }, +{ "message": "Return searcher 'com.yahoo.prelude.querytransform.PhrasingSearcher in vespa'" }, +{ "message": "Return searcher 'com.yahoo.prelude.statistics.StatisticsSearcher in native'" }, +{ "message": "Return searcher 'com.yahoo.search.querytransform.WeakAndReplacementSearcher in vespa'" }, +``` + +This shows a small sample of the additional output when using `trace.level`. Note the invocations of the Searchers. Each Searcher gets invoked along a chain, and the last Searcher in the chain sends the post-processed query to the search backend. When the results come back, the processing passes back up the chain. The Searchers can then process the results before passing them to the previous Searcher, and ultimately back as a response to the query. + + +Adding a [trace.level](/en/reference/api/query#trace.level) is generally helpful when debugging vespa queries. + + +So, [Searchers](/en/applications/searchers) are Java components that do some kind of processing along the query chain; either modifying the query before the actual search, modifying the results after the search, or some combination of both. + +Developers can provide their own Searchers and inject them into the query chain. We'll capitalize on this and create a Searcher that performs essentially the same task that [user_search.py](https://github.com/vespa-engine/sample-apps/blob/master/news/src/python/user_search.py) does: retrieve a user embedding and do a news article search based on that. In the process, we'll only pass a `user_id` to Vespa instead of a full YQL query: `/search/?user_id=U33527&searchchain=user` + +The search will take care of creating the actual query for us - let's get started. + + +## Adding a user profile Searcher + +While the `content` layer in Vespa is written in C++ for maximum performance, the `container` layer is in Java for flexibility. So, all Searchers and thus custom Searchers are written in Java. Refer to [the guide on Searcher development](/en/applications/searchers) for more information. + +We want to create a Searcher that takes a `user_id`, issues a query to find the corresponding embedding, then issues a second query to retrieve the news articles. + +To do this, we create a `UserProfileSearcher` that extends the base Searcher class `com.yahoo.search.Searcher`. This Searcher must implement a single method: `search`, and has the responsibility of passing the query to the next Searcher on the list. A minimal example: + +```java +public class UserProfileSearcher extends Searcher { + public Result search(Query query, Execution execution) { + // ... process query + Result results = execution.search(query) + // ... process results + return results; + } +} +``` + +So, what we do before we pass the query along (in `execution.search(query)`) and before we return the results is completely up to us. So, we implement our `UserProfileSearcher` like this: + +```java expandable +public class UserProfileSearcher extends Searcher { + + public Result search(Query query, Execution execution) { + + // Get tensor and read items from user profile + Object userIdProperty = query.properties().get("user_id"); + if (userIdProperty != null) { + + // Retrieve user embedding by doing a search for the user_id and extract the tensor + Tensor userEmbedding = retrieveUserEmbedding(userIdProperty.toString(), execution); + + // Create a new search using the user's embedding tensor + NearestNeighborItem nn = new NearestNeighborItem("embedding", "user_embedding"); + nn.setTargetNumHits(query.getHits()); + nn.setAllowApproximate(true); + + query.getModel().getQueryTree().setRoot(nn); + query.getRanking().getFeatures().put("query(user_embedding)", userEmbedding); + query.getModel().setRestrict("news"); + + // Override default rank profile + if (query.getRanking().getProfile().equals("default")) { + query.getRanking().setProfile("recommendation"); + } + } + + return execution.search(query); + } + + private Tensor retrieveUserEmbedding(String userId, Execution execution) { + Query query = new Query(); + query.getModel().setRestrict("user"); + query.getModel().getQueryTree().setRoot(new WordItem(userId, "user_id")); + query.setHits(1); + + Result result = execution.search(query); + execution.fill(result); // This is needed to get the actual summary data + + if (result.getTotalHitCount() == 0) + throw new RuntimeException("User id " + userId + " not found..."); + return (Tensor) result.hits().get(0).getField("embedding"); + } + +} +``` + +First, we retrieve the `user_id` from the query. If this is given in the query, we first call the `retrieveUserEmbedding` method, which creates a new `Query` to find the user's embedding. This is a straight-forward search which is restricted to the `user` document type. Since the `user_id` is unique, we only expect a single hit. We then extract the `embedding` tensor from the user document. + + +We explicitly call a *fill* on the results before returning. A query is usually passed to the search backend at least twice: one to retrieve the top ranked results, another to retrieve the summary data of the final result set. This is to avoid sending excess data between services. For instance, if searching for the top 10 results with two search backends, each backend will retrieve the top 10 results from the local content on that node. A Searcher will determine the global top ten ranked results (potentially including diversification) and only issue a *fill* to retrieve the summary features for those top 10. + + +Now that we've retrieved the user embedding, we programmatically set up a nearest-neighbor search, and add the user embedding to the query as the ranking feature `query(user_embedding)`. The search is then passed along to the next Searcher in the chain. We do not need to explicitly fill the result here, as that is guaranteed to happen before ultimately rendering the results. + +Again, note that all this is pretty much the same as what we did in [user_search.py](https://github.com/vespa-engine/sample-apps/blob/master/news/src/python/user_search.py) - just in Java. + + +## Adding a search chain + +To add this Searcher to Vespa, we need to modify `services.xml`: + +```xml + + + + + + + ... + +``` + +Here, we instruct Vespa to add a new search chain called `user` (which inherits the default `vespa` search chain), and includes our `UserProfileSearcher`. Note that Vespa expects this Searcher to be in a bundle called `news-recommendation`, so we need to compile and package this code. In Vespa, we use [Apache Maven](https://maven.apache.org/) for this, which requires a project object model, or `pom.xml`, to specify how to build this artifact. + +We won't go through that here; please refer to [app-6-recommendation-with-searchers](https://github.com/vespa-engine/sample-apps/tree/master/news/app-6-recommendation-with-searchers) in the `news` sample application for details. Note that this application's directory structure has changed compared to the previous parts in the tutorial. The structure is now: + +``` +. +├── pom.xml +└── src + └── main + ├── application + │ ├── schemas + │ │ ├── news.sd + │ │ └── user.sd + │ ├── search + │ │ └── query-profiles + │ │ ├── default.xml + │ │ └── types + │ │ └── root.xml + │ └── services.xml + └── java + └── ai + └── vespa + └── example + └── UserProfileSearcher.java +``` + +The Vespa application now lies under `src/main/application`, and all custom Java components are under `src/main/java` as is standard in a Java project. We can now compile and package this application: + +```bash +$ (cd app-6-recommendation-with-searchers && mvn package) +``` + +[pom.xml](https://github.com/vespa-engine/sample-apps/blob/master/news/app-6-recommendation-with-searchers/pom.xml) is set up to create an artifact called `news-recommendation-searcher`, which is referred to in `services.xml`. When the command finishes, we can see this artifact in `target/application.zip`. This contains the full Vespa application, with Java components - deploy it: + +```bash +$ vespa deploy --wait 300 app-6-recommendation-with-searchers +``` + +After the application has been deployed, we are ready to test. Refer to [the Searcher development guide](/en/applications/searchers) for much more on custom Searchers and the Java API. + + +## Testing + +Now we can search for a user's recommended news articles directly from the `user_id`: + +```bash +$ vespa query -v \ + 'user_id=U33527' \ + 'searchChain=user' +``` + +This should now return the top 10 recommended news articles for this user. Indeed, if we now add a with a `trace.level=5`, we see the Searcher being invoked: + +```bash +$ vespa query -v \ + 'user_id=U33527' \ + 'searchChain=user' \ + 'trace.level=5' +``` + +```json +{ "message": "Invoke searcher 'ai.vespa.example.UserProfileSearcher in user'" }, +{ "message": "Invoke searcher 'com.yahoo.search.querytransform.WeakAndReplacementSearcher in vespa'" }, +{ "message": "Invoke searcher 'com.yahoo.prelude.statistics.StatisticsSearcher in native'" }, + +{ "message": "Return searcher 'com.yahoo.prelude.statistics.StatisticsSearcher in native'" }, +{ "message": "Return searcher 'com.yahoo.search.querytransform.WeakAndReplacementSearcher in vespa'" }, +{ "message": "Return searcher 'ai.vespa.example.UserProfileSearcher in user'" }, +``` + +Note that the `searchChain` query parameter can be set as default, so this does not have to be passed with the query request. This is done by adding it to the default query profile in [src/main/application/search/query-profiles/default.xml](https://github.com/vespa-engine/sample-apps/blob/master/news/app-6-recommendation-with-searchers/src/main/application/search/query-profiles/default.xml): + +```xml + + user + +``` + + +[src/python/evaluate.py](https://github.com/vespa-engine/sample-apps/blob/master/news/src/python/evaluate.py) can now be modified to use this Searcher. However, to properly calculate the metrics, the Searcher needs to be modified to accept a list of news article id's and only recall those. We'll leave this as an exercise to the reader. + + + +## Document processors + +As can be seen in the architecture overview above, there are other component types as well. One is Document Processors, which are conceptually similar to Searchers. When a document is fed to Vespa, it goes through a chain of Document Processors before being passed to the content node for storage and indexing. + +Vespa also supports custom Document Processors, refer to [the guide for document processing](/en/applications/document-processors) for more information. + + +## Improving recommendation diversity + +If we take a closer look at the query above, and search for the top 100 hits: + +```bash +$ vespa query \ + 'user_id=U33527' \ + 'searchChain=user' \ + 'hits=100' | \ + grep "category\": \"sports" | wc -l +``` + +We see that all the hits are of category `sports` for this user. Actually, they are all from the `football_nfl` sub-category. Indeed, from inspection of the impressions file, this user has only clicked on `sports` articles. So, while this can seem a success, we generally would like to give users some form of diversity to keep them interested. This is also to combat the negative effects of filter bubbles. + +One way to do this is to create Searchers that perform multiple queries to the backend with various rank profiles. In the above, we were only retrieving results from the `recommendation` rank profile. Still, we can have any number of rank profiles. By searching in multiple rank profiles, we can blend the results from these sources before returning to the user, and thus introduce diversity. + +This is often called federation. Vespa supports federation both from internal and external sources, see [the guide on federation](/en/querying/federation) for more information. + + +If the same document can be returned from multiple sources, it's important to perform some form of de-duplication before returning the final results! + + +A common way of performing blending from multiple sources is to implement a specialized blending Searcher. This Searcher can, for instance, use an approach such as [reciprocal rank fusion](https://research.google/pubs/pub36196/), which gives decent results. However, when it comes to diversity, there are usually some goals or restrictions that needs to be controlled. In this case the business rules can be hand-written in the blending Searcher. Searchers are flexible enough to perform any type of processing. + + +## Conclusion + +We now have a Vespa application up and running that takes a single `user_id` and returns recommendations for that user. In the [next part of the tutorial](/en/learn/tutorials/news-7-recommendation-with-parent-child), we'll address what to do when new users without any history visit our recommendation system. diff --git a/en/learn/tutorials/news-7-recommendation-with-parent-child.md b/en/learn/tutorials/news-7-recommendation-with-parent-child.md deleted file mode 100644 index 8f26c61fef..0000000000 --- a/en/learn/tutorials/news-7-recommendation-with-parent-child.md +++ /dev/null @@ -1,399 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "News Recommendation Tutorial - parent child and tensor ranking" -redirect_from: - - /en/tutorials/news-7-recommendation-with-parent-child ---- - - - -This is the seventh part of the tutorial series for setting up a Vespa -application for personalized news recommendations. The parts are: - -1. [Getting started](news-1-deploy-an-application.html) -2. [A basic news search application](news-2-basic-feeding-and-query.html) - application packages, feeding, query -3. [News search](news-3-searching.html) - sorting, grouping, and ranking -4. [Generating embeddings for users and news articles](news-4-embeddings.html) -5. [News recommendation](news-5-recommendation.html) - partial updates (news embeddings), ANNs, filtering -6. [News recommendation with searchers](news-6-recommendation-with-searchers.html) - custom searchers, doc processors -7. [News recommendation with parent-child](news-7-recommendation-with-parent-child.html) - parent-child, tensor ranking -8. Advanced news recommendation - intermission - training a ranking model -9. Advanced news recommendation - ML models - -In this part of the series, we'll introduce a new ranking signal: -category click-through rate (CTR). -The idea is that we can recommend popular content for users that don't have a click history yet. -Rather than just recommending based on articles, we recommend based on categories. -However, these global CTR values can often change continuously, -so we need an efficient way to update this value for all documents. -We'll do that by introducing parent-child relationships between documents in Vespa. -We will also use sparse tensors directly in ranking. - -For reference, the final state of this tutorial can be found in the -[app-7-parent-child](https://github.com/vespa-engine/sample-apps/tree/master/news/app-7-parent-child) -directory of the `news` sample application. - - -## Parent-child relationships in Vespa - -Recall that most features come from either attributes in the document or -parameters passed with the query when ranking a document. -Parent-child relationships introduce the option of using attributes found in other documents. -Parent-child relationships work as a form of scalable document joins. - -For instance, assume we have a global CTR value for the sports category of `0.2`. -If we want to use this value during ranking, -we could have a field in each news article holding this value. -However, when we need to update this value, we need to issue a partial update to all documents, which seems wasteful. - -Another way would be to take inspiration from the `UserProfileSearcher`, -where we retrieved the tensor embedding for a user in a search -before passing that with the news article query. -We could have a single document holding all global values and retrieve that with each query. -However, that isn't particularly efficient. - -For these cases, Vespa introduced the [parent-child relationship](../../schemas/parent-child.html). -Parents are global documents, which are automatically distributed to all content nodes. -Other documents can reference these parents and "import" values for use in ranking. -The benefit is that the global category CTR values only need to be written to one place: the global document. - -Please see the [guide on parent-child relationships](../../schemas/parent-child.html) for more information and examples. - - -## Setting up a global category CTR document - -So, let's set this up for our application. -First we need to add a new document type to hold the CTR values. -We introduce the `category_ctr` document type, -which we add in `schemas/category_ctr.sd`: - -
    - -
    -schema category_ctr {
    -    document category_ctr {
    -        field ctrs type tensor<float>(category{}) {
    -            indexing: attribute
    -            attribute: fast-search
    -        }
    -    }
    -}
    -
    -
    - -This document holds a single field: a [tensor](../../ranking/tensor-user-guide.html) of type `tensor(category{})`. -This is a tensor with a single sparse dimension, -which is slightly different from the tensors we have seen so far. -Sparse tensors have strings as dimension addresses rather than a numeric index. -More concretely, an example of such a tensor is -(using the [tensor literal form](../../reference/ranking/tensor.html#tensor-literal-form)): - -``` -{ - {category: entertainment}: 0.2 }, - {category: news}: 0.3 }, - {category: sports}: 0.5 }, - {category: travel}: 0.4 }, - {category: finance}: 0.1 }, - ... -} -``` - -This tensor holds all the CTR scores for all the categories. -When updating this tensor, we can update individual cells if we don't need to update the whole tensor. -This is called [tensor modify](../../reference/schemas/document-json-format.html#tensor-modify) -and can be helpful when you have large tensors. - -To use this document, add it to `services.xml`: - -
    - -
    -<content id="mind" version="1.0">
    -    <documents>
    -        <document type="news" mode="index" />
    -        <document type="user" mode="index" />
    -        <document type="category_ctr" mode="index" global="true" />
    -    </documents>
    -
    -</content>
    -
    -
    - - -Notice that we've set `global="true"`, -configuring Vespa to keep a copy of these documents on all content nodes. -This is required for using it in a parent-child relationship. -This also put limits on how many parent documents a system can have, -as all nodes needs to index all parent documents. - - -## Importing parent values in child documents - -To use the `category_ctr` tensor when ranking `news` documents, -we need to "import" the tensor into the child document type. -There are two things to set up: - -1. The reference to the parent document -2. Which fields to import. - -Modify `schemas/news.sd`: - -
    - -
    -schema news {
    -    document news {
    -        ...
    -        field category_ctr_ref type reference<category_ctr> {
    -            indexing: attribute
    -        }
    -        ...
    -    }
    -    import field category_ctr_ref.ctrs as global_category_ctrs {}
    -}
    -
    -
    - -The field `category_ctr_ref` is a field of type `reference` of a `category_ctr` document type. -When feeding this field, Vespa expects the fully qualified document ID. -For instance, if our global CTR document has the id `id:category_ctr:category_ctr::global`, -that is what this field must be set to. -Usually, there are many parent documents that children can reference, -but our application will only hold one. - -You can think of the reference field as holding a foreign key to the parent document, -and the import as performing a real-time join between the child and parent document using this foreign key. -The imported values are usable as if they were stored with the child. - -The `import` statement defines that we should import the `ctrs` field from -the document referenced in the `category_ctr_ref` field. -We name this as `global_category_ctrs`, -and we can reference this as `attribute(global_category_ctrs)` during ranking. - - -## Tensor expressions in ranking - -Up until this point, we've only used tensors as storage. -We used tensors to hold news and user embeddings, -and Vespa used these tensors to calculate the dot product in nearest-neighbor searches. - -However, Vespa has a [rich language](../../ranking/tensor-user-guide.html#ranking-with-tensors) -to perform calculations with tensors. -We'll exploit that by looking up the `news` article's category in the global CTR tensor -and using that as a feature in ranking. - -Our `news` document has a field currently that holds the `category` as a string. -Unfortunately, tensor expressions only work on tensors, so we need to add a new field to hold the category tensor: - -
    - -
    -    field category_tensor type tensor<float>(category{}) {
    -        indexing: attribute
    -    }
    -
    -
    - -Using a tensor in this way also enables a document to have multiple categories, -but our dataset only has a single category per article. -For instance, we can represent the `finance` category of a `news` article like: - -
    - -
    -    {category: finance}: 1.0 }
    -
    -
    - -Since this is a sparse tensor, we don't need to mention the other categories. -Now, we can use this tensor to calculate the global CTR score for an article's category: - -
    - -
    -    attribute(category_tensor) * attribute(global_category_ctrs)
    -
    -
    - -Given the global category CTR example above, this would result in the value `0.1`. -How did we arrive at this? -Recall that the value for the cell `finance` in the `category` dimension of the example above -had a value of `0.1`. -The multiplication of these two tensors is conceptually an "inner join", -so you can take the matching cells and multiply them together. -Due to the sparseness of the tensor, only the `finance` cell matches, -and that value is multiplied by the `1.0` in this document. -So in this case, this would effectively work as a lookup. - -{% include note.html content="Much more complex operations are available, -refer to the [tensor user guide](../../ranking/tensor-user-guide.html#ranking-with-tensors) for more information." %} - -Let's add a new rank profile to do this calculation: - -
    - -
    -rank-profile recommendation_with_global_category_ctr inherits recommendation {
    -    function category_ctr() {
    -        expression: sum(attribute(category_tensor) * attribute(global_category_ctrs))
    -    }
    -    function nearest_neighbor() {
    -        expression: closeness(field, embedding)
    -    }
    -    first-phase {
    -        expression: nearest_neighbor * category_ctr
    -    }
    -    summary-features {
    -        attribute(category_tensor)
    -        attribute(global_category_ctrs)
    -        category_ctr
    -        nearest_neighbor
    -    }
    -}
    -
    -
    - -Here, we've added a first phase ranking expression -that multiplies the nearest-neighbor score with the category CTR score, -implemented with the functions `nearest_neighbor` and `category_ctr`, respectively. - -We've added a `sum` function around the `category_ctr` expression - -this is simply to unbox the single-value tensor to a double value suitable for use in the first phase expression. - -Note that, as a first attempt, we just multiply the nearest-neighbor with the category CTR score. -This is not necessarily the correct way to combine these values, -but we'll get back to that in a bit. - -We've added a section for [summary features](../../reference/schemas/schemas.html#summary-features). -This is simply a list of features that will be returned with the hit when using this rank profile. -Recall that we can specify which features should be returned in the summary -with the `indexing: summary` statement with each field. -The `summary-features` can also include the result of functions as well. -This is a helpful debugging tool, and we'll see how this looks after feeding some data. - - -## Feeding parent and child updates - -Deploy the application: - -
    - -
    -$ (cd app-7-parent-child && mvn package)
    -
    -
    - -
    - -
    -$ vespa deploy --wait 300 app-7-parent-child
    -
    -
    - -After deploying the application, we are ready to feed a global CTR document. -For convenience, we've created -[create_category_ctrs.py](https://github.com/vespa-engine/sample-apps/blob/master/news/src/python/create_category_ctrs.py) -that reads the MIND content and impression data to calculate CTR scores for each category. -This produces two files in the `mind` directory: - -1. `mind/global_category_ctr.json` - a feed file for the global CTR document containing CTR - score for each category. -2. `mind/news_category_ctr_update.json` - a feed file for partially updating the `news` - articles with the reference to the global CTR document as well as the category tensor. - -These files can now be fed to Vespa, but note that the -`mind/global_category_ctr.json` need to be fed first because the global -document needs to exist before the child documents can reference it. - -Create feed files: - -
    - -
    -$ ./src/python/create_category_ctrs.py mind
    -
    -
    - -Feed the created feed files: - -
    - -
    -$ vespa feed mind/global_category_ctr.json --target http://localhost:8080
    -$ vespa feed mind/news_category_ctr_update.json --target http://localhost:8080
    -
    -
    - - -## Testing the application - -After feeding the above files, we can now test the application with a query: - -
    - -
    -$ vespa query \
    -  'user_id=U33527' \
    -  'ranking.profile=recommendation_with_global_category_ctr' \
    -  'hits=10'
    -
    -
    - -Note that we specify the rank profile to use. -The first result of this query is something like the following: - -```json -"fields": { - "title": "Matthew Stafford's status vs. Bears uncertain, Sam Martin will play", - "abstract": "abstract": "Stafford's start streak could be in jeopardy, according to Ian Rapoport.", - "category": "sports", - ... - "summary-features": { - "attribute(category_tensor)": {"cells": [{"address": { "category": "sports"}, "value":1.0 }]}, - "attribute(global_category_ctrs)": {"cells": [ - ... - { "address": { "category": "sports" }, "value": 0.05611187964677811 }, - ... - ]}, - "rankingExpression(category_ctr)": 0.05611187964677811, - "rankingExpression(nearest_neighbor)": 0.14914761220236453, - } - ... - "relevance": 0.008368952865503413, -} -``` - -This is clearly a sports article. -The global CTR document is also listed here, and the CTR score for the `sports` category is `0.0561`. -Thus, the result of the `category_ctr` function is `0.0561` as intended. -The `nearest_neighbor` score is `0.149`, and the resulting relevance score is `0.00836`. -So, this worked as expected. - -If we were to feed another value to the global CTR document, this updated value is immediately available. -As such, the system responds quickly to changes in the global parameters. - -Now, a simple multiplication between these features might not give us what we want. -For instance, these features have different average values and different standard deviations. -Particularly, if we add multiple additional features, -just multiplying them together will probably not give a great user experience. -Instead of a hand-tuned final relevancy calculation as demonstrated above, -we could use a machine learned function with these as feature inputs. - -Ultimately, these features are computed in real-time for every news article during ranking. -These features can then be added to any machine-learned ranking model. -Vespa supports gradient-boosted trees from [XGBoost](../../ranking/xgboost) and [LightGBM](../../ranking/lightgbm), -and also neural networks in [ONNX](../../ranking/onnx) format, -exported from popular ML frameworks like -[PyTorch](https://pytorch.org/) and [Tensorflow](https://www.tensorflow.org/). - - -## Conclusion - -This tutorial introduced parent-child relationships -and demonstrated it through a global CTR feature we used in ranking. -As this feature was based on tensors, we also introduced ranking with tensor expressions. -For a real-world use-case using parent-child tensors, -see this [blog post](https://blog.vespa.ai/parent-child-joins-tensors-content-recommendation/). - - diff --git a/en/learn/tutorials/news-7-recommendation-with-parent-child.mdx b/en/learn/tutorials/news-7-recommendation-with-parent-child.mdx new file mode 100644 index 0000000000..ef188a0c59 --- /dev/null +++ b/en/learn/tutorials/news-7-recommendation-with-parent-child.mdx @@ -0,0 +1,250 @@ +--- +title: "News Recommendation Tutorial - parent child and tensor ranking" +--- + +This is the seventh part of the tutorial series for setting up a Vespa application for personalized news recommendations. The parts are: + +1. [Getting started](/en/learn/tutorials/news-1-deploy-an-application) +2. [A basic news search application](/en/learn/tutorials/news-2-basic-feeding-and-query) - application packages, feeding, query +3. [News search](/en/learn/tutorials/news-3-searching) - sorting, grouping, and ranking +4. [Generating embeddings for users and news articles](/en/learn/tutorials/news-4-embeddings) +5. [News recommendation](/en/learn/tutorials/news-5-recommendation) - partial updates (news embeddings), ANNs, filtering +6. [News recommendation with searchers](/en/learn/tutorials/news-6-recommendation-with-searchers) - custom searchers, doc processors +7. [News recommendation with parent-child](/en/learn/tutorials/news-7-recommendation-with-parent-child) - parent-child, tensor ranking +8. Advanced news recommendation - intermission - training a ranking model +9. Advanced news recommendation - ML models + +In this part of the series, we'll introduce a new ranking signal: category click-through rate (CTR). The idea is that we can recommend popular content for users that don't have a click history yet. Rather than just recommending based on articles, we recommend based on categories. However, these global CTR values can often change continuously, so we need an efficient way to update this value for all documents. We'll do that by introducing parent-child relationships between documents in Vespa. We will also use sparse tensors directly in ranking. + +For reference, the final state of this tutorial can be found in the [app-7-parent-child](https://github.com/vespa-engine/sample-apps/tree/master/news/app-7-parent-child) directory of the `news` sample application. + + +## Parent-child relationships in Vespa + +Recall that most features come from either attributes in the document or parameters passed with the query when ranking a document. Parent-child relationships introduce the option of using attributes found in other documents. Parent-child relationships work as a form of scalable document joins. + +For instance, assume we have a global CTR value for the sports category of `0.2`. If we want to use this value during ranking, we could have a field in each news article holding this value. However, when we need to update this value, we need to issue a partial update to all documents, which seems wasteful. + +Another way would be to take inspiration from the `UserProfileSearcher`, where we retrieved the tensor embedding for a user in a search before passing that with the news article query. We could have a single document holding all global values and retrieve that with each query. However, that isn't particularly efficient. + +For these cases, Vespa introduced the [parent-child relationship](/en/schemas/parent-child). Parents are global documents, which are automatically distributed to all content nodes. Other documents can reference these parents and "import" values for use in ranking. The benefit is that the global category CTR values only need to be written to one place: the global document. + +Please see the [guide on parent-child relationships](/en/schemas/parent-child) for more information and examples. + + +## Setting up a global category CTR document + +So, let's set this up for our application. First we need to add a new document type to hold the CTR values. We introduce the `category_ctr` document type, which we add in `schemas/category_ctr.sd`: + +```sd +schema category_ctr { + document category_ctr { + field ctrs type tensor(category{}) { + indexing: attribute + attribute: fast-search + } + } +} +``` + +This document holds a single field: a [tensor](/en/ranking/tensor-user-guide) of type `tensor(category{})`. This is a tensor with a single sparse dimension, which is slightly different from the tensors we have seen so far. Sparse tensors have strings as dimension addresses rather than a numeric index. More concretely, an example of such a tensor is (using the [tensor literal form](/en/reference/ranking/tensor#tensor-literal-form)): + +``` +{ + {category: entertainment}: 0.2, + {category: news}: 0.3, + {category: sports}: 0.5, + {category: travel}: 0.4, + {category: finance}: 0.1, + ... +} +``` + +This tensor holds all the CTR scores for all the categories. When updating this tensor, we can update individual cells if we don't need to update the whole tensor. This is called [tensor modify](/en/reference/schemas/document-json-format#tensor-modify) and can be helpful when you have large tensors. + +To use this document, add it to `services.xml`: + +```xml + + + + + + + +``` + +Notice that we've set `global="true"`, configuring Vespa to keep a copy of these documents on all content nodes. This is required for using it in a parent-child relationship. This also put limits on how many parent documents a system can have, as all nodes needs to index all parent documents. + + +## Importing parent values in child documents + +To use the `category_ctr` tensor when ranking `news` documents, we need to "import" the tensor into the child document type. There are two things to set up: + +1. The reference to the parent document +2. Which fields to import. + +Modify `schemas/news.sd`: + +```sd +schema news { + document news { + ... + field category_ctr_ref type reference { + indexing: attribute + } + ... + } + import field category_ctr_ref.ctrs as global_category_ctrs {} +} +``` + +The field `category_ctr_ref` is a field of type `reference` of a `category_ctr` document type. When feeding this field, Vespa expects the fully qualified document ID. For instance, if our global CTR document has the id `id:category_ctr:category_ctr::global`, that is what this field must be set to. Usually, there are many parent documents that children can reference, but our application will only hold one. + +You can think of the reference field as holding a foreign key to the parent document, and the import as performing a real-time join between the child and parent document using this foreign key. The imported values are usable as if they were stored with the child. + +The `import` statement defines that we should import the `ctrs` field from the document referenced in the `category_ctr_ref` field. We name this as `global_category_ctrs`, and we can reference this as `attribute(global_category_ctrs)` during ranking. + + +## Tensor expressions in ranking + +Up until this point, we've only used tensors as storage. We used tensors to hold news and user embeddings, and Vespa used these tensors to calculate the dot product in nearest-neighbor searches. + +However, Vespa has a [rich language](/en/ranking/tensor-user-guide#ranking-with-tensors) to perform calculations with tensors. We'll exploit that by looking up the `news` article's category in the global CTR tensor and using that as a feature in ranking. + +Our `news` document has a field currently that holds the `category` as a string. Unfortunately, tensor expressions only work on tensors, so we need to add a new field to hold the category tensor: + +```sd +field category_tensor type tensor(category{}) { + indexing: attribute +} +``` + +Using a tensor in this way also enables a document to have multiple categories, but our dataset only has a single category per article. For instance, we can represent the `finance` category of a `news` article like: + +``` +{category: finance}: 1.0 +``` + +Since this is a sparse tensor, we don't need to mention the other categories. Now, we can use this tensor to calculate the global CTR score for an article's category: + +``` +attribute(category_tensor) * attribute(global_category_ctrs) +``` + +Given the global category CTR example above, this would result in the value `0.1`. How did we arrive at this? Recall that the value for the cell `finance` in the `category` dimension of the example above had a value of `0.1`. The multiplication of these two tensors is conceptually an "inner join", so you can take the matching cells and multiply them together. Due to the sparseness of the tensor, only the `finance` cell matches, and that value is multiplied by the `1.0` in this document. So in this case, this would effectively work as a lookup. + + +Much more complex operations are available, refer to the [tensor user guide](/en/ranking/tensor-user-guide#ranking-with-tensors) for more information. + + +Let's add a new rank profile to do this calculation: + +```sd expandable +rank-profile recommendation_with_global_category_ctr inherits recommendation { + function category_ctr() { + expression: sum(attribute(category_tensor) * attribute(global_category_ctrs)) + } + function nearest_neighbor() { + expression: closeness(field, embedding) + } + first-phase { + expression: nearest_neighbor * category_ctr + } + summary-features { + attribute(category_tensor) + attribute(global_category_ctrs) + category_ctr + nearest_neighbor + } +} +``` + +Here, we've added a first phase ranking expression that multiplies the nearest-neighbor score with the category CTR score, implemented with the functions `nearest_neighbor` and `category_ctr`, respectively. + +We've added a `sum` function around the `category_ctr` expression - this is simply to unbox the single-value tensor to a double value suitable for use in the first phase expression. + +Note that, as a first attempt, we just multiply the nearest-neighbor with the category CTR score. This is not necessarily the correct way to combine these values, but we'll get back to that in a bit. + +We've added a section for [summary features](/en/reference/schemas/schemas#summary-features). This is simply a list of features that will be returned with the hit when using this rank profile. Recall that we can specify which features should be returned in the summary with the `indexing: summary` statement with each field. The `summary-features` can also include the result of functions as well. This is a helpful debugging tool, and we'll see how this looks after feeding some data. + + +## Feeding parent and child updates + +Deploy the application: + +```bash +$ (cd app-7-parent-child && mvn package) +``` + +```bash +$ vespa deploy --wait 300 app-7-parent-child +``` + +After deploying the application, we are ready to feed a global CTR document. For convenience, we've created [create_category_ctrs.py](https://github.com/vespa-engine/sample-apps/blob/master/news/src/python/create_category_ctrs.py) that reads the MIND content and impression data to calculate CTR scores for each category. This produces two files in the `mind` directory: + +1. `mind/global_category_ctr.json` - a feed file for the global CTR document containing CTR score for each category. +2. `mind/news_category_ctr_update.json` - a feed file for partially updating the `news` articles with the reference to the global CTR document as well as the category tensor. + +These files can now be fed to Vespa, but note that the `mind/global_category_ctr.json` need to be fed first because the global document needs to exist before the child documents can reference it. + +Create feed files: + +```bash +$ ./src/python/create_category_ctrs.py mind +``` + +Feed the created feed files: + +```bash +$ vespa feed mind/global_category_ctr.json --target http://localhost:8080 +$ vespa feed mind/news_category_ctr_update.json --target http://localhost:8080 +``` + + +## Testing the application + +After feeding the above files, we can now test the application with a query: + +```bash +$ vespa query \ + 'user_id=U33527' \ + 'ranking.profile=recommendation_with_global_category_ctr' \ + 'hits=10' +``` + +Note that we specify the rank profile to use. The first result of this query is something like the following: + +```json expandable +"fields": { + "title": "Matthew Stafford's status vs. Bears uncertain, Sam Martin will play", + "abstract": "Stafford's start streak could be in jeopardy, according to Ian Rapoport.", + "category": "sports", + ... + "summary-features": { + "attribute(category_tensor)": {"cells": [{"address": { "category": "sports"}, "value":1.0 }]}, + "attribute(global_category_ctrs)": {"cells": [ + ... + { "address": { "category": "sports" }, "value": 0.05611187964677811 }, + ... + ]}, + "rankingExpression(category_ctr)": 0.05611187964677811, + "rankingExpression(nearest_neighbor)": 0.14914761220236453, + } + ... + "relevance": 0.008368952865503413, +} +``` + +This is clearly a sports article. The global CTR document is also listed here, and the CTR score for the `sports` category is `0.0561`. Thus, the result of the `category_ctr` function is `0.0561` as intended. The `nearest_neighbor` score is `0.149`, and the resulting relevance score is `0.00836`. So, this worked as expected. + +If we were to feed another value to the global CTR document, this updated value is immediately available. As such, the system responds quickly to changes in the global parameters. + +Now, a simple multiplication between these features might not give us what we want. For instance, these features have different average values and different standard deviations. Particularly, if we add multiple additional features, just multiplying them together will probably not give a great user experience. Instead of a hand-tuned final relevancy calculation as demonstrated above, we could use a machine learned function with these as feature inputs. + +Ultimately, these features are computed in real-time for every news article during ranking. These features can then be added to any machine-learned ranking model. Vespa supports gradient-boosted trees from [XGBoost](/en/ranking/xgboost) and [LightGBM](/en/ranking/lightgbm), and also neural networks in [ONNX](/en/ranking/onnx) format, exported from popular ML frameworks like [PyTorch](https://pytorch.org/) and [Tensorflow](https://www.tensorflow.org/). + + +## Conclusion + +This tutorial introduced parent-child relationships and demonstrated it through a global CTR feature we used in ranking. As this feature was based on tensors, we also introduced ranking with tensor expressions. For a real-world use-case using parent-child tensors, see this [blog post](https://blog.vespa.ai/parent-child-joins-tensors-content-recommendation/?_gl=1*1cqlj1i*_gcl_au*ODE0ODM4MTI2LjE3Nzk3MjQ3OTY.). diff --git a/en/learn/tutorials/rag-blueprint.md b/en/learn/tutorials/rag-blueprint.mdx similarity index 58% rename from en/learn/tutorials/rag-blueprint.md rename to en/learn/tutorials/rag-blueprint.mdx index c946bc4192..76f311c09a 100644 --- a/en/learn/tutorials/rag-blueprint.md +++ b/en/learn/tutorials/rag-blueprint.mdx @@ -1,19 +1,15 @@ --- -# Copyright Vespa.ai. All rights reserved. title: "RAG Blueprint" -redirect_from: - - /en/tutorials/rag-blueprint +description: "Many of our users use Vespa to power large scale RAG Applications." --- -Many of our users use Vespa to power large scale RAG Applications. - This blueprint aims to exemplify many of the best practices we have learned while supporting these users. While many RAG tutorials exist, this blueprint provides a customizable template that: -* Can [(auto)scale](../../operations/autoscaling.html) with your data size and/or query load. -* Is fast and [production grade](../../operations/production-deployment.html). -* Enables you to build RAG applications with state-of-the-art quality. +- Can [(auto)scale](/en/operations/autoscaling) with your data size and/or query load. +- Is fast and [production grade](/en/operations/production-deployment). +- Enables you to build RAG applications with state-of-the-art quality. This tutorial will show how we can develop a _high-quality_ RAG application with an evaluation-driven mindset, while being a resource you can revisit for making informed choices for your own use case. @@ -27,36 +23,53 @@ We will guide you through the following steps: 6. [Second-phase ranking](#second-phase-ranking) 7. [(Optional) Global-phase ranking](#optional-global-phase-ranking) -All the accompanying code can be found in our [sample app](https://github.com/vespa-engine/sample-apps/tree/master/rag-blueprint) repo. +All the accompanying code can be found in our [sample app](https://github.com/vespa-engine/sample-apps/tree/master/rag-blueprint) repo. Each step will contain reasoning behind the choices and design of the blueprint, as well as pointers for customizing your own application. Below, you can see a diagram of the indexing (document side), retrieval and ranking of the sample application, which will be explained in more detail in the following sections. -{% include note.html content="The elements in the diagram are clickable, and will lead you to the relevant sections, either of this tutorial or in the Vespa documentation." %} + +**Note:** + +The elements in the diagram are clickable, and will lead you to the relevant sections, either of this tutorial or in the Vespa documentation. + + + +![RAG Blueprint Overview](/assets/img/tutorials/rag-blueprint-overview.svg) + - - -

    Your browser can’t display SVG. Try upgrading your browser or using a different one.

    -
    +[Click to open diagram in full size](/assets/img/tutorials/rag-blueprint-overview.svg) - [Click to open diagram in full size](/assets/img/tutorials/rag-blueprint-overview.svg) + +**Note:** -{% include note.html content="This is not a **'Deploy RAG in 5 minutes'** tutorial (although you _can_ technically do that by following the README in our [sample app](https://github.com/vespa-engine/sample-apps/tree/master/rag-blueprint)). This focus is more about providing you with the insights and tools for you to apply it to your own use case. Therefore we suggest taking your time to look at the code in the sample app, and run the described steps." %} +This is not a **'Deploy RAG in 5 minutes'** tutorial (although you _can_ technically do that by following the README in our [sample app](https://github.com/vespa-engine/sample-apps/tree/master/rag-blueprint)). This focus is more about providing you with the insights and tools for you to apply it to your own use case. Therefore we suggest taking your time to look at the code in the sample app, and run the described steps. + + + +**Prerequisites:** + +- Linux, macOS or Windows 10 Pro on x86\_64 or arm64, with [Podman Desktop](https://podman.io/) or [Docker Desktop](https://www.docker.com/products/docker-desktop/) installed, with an engine running. + - Alternatively, start the Podman daemon: + ```bash + $ podman machine init --memory 6000 + $ podman machine start + ``` + - See [Docker Containers](/en/operations/self-managed/docker-containers) for system limits and other settings. +- For CPUs older than Haswell (2013), see [CPU Support](/en/operations/self-managed/cpu-support). +- Memory: Minimum 4 GB RAM dedicated to Docker/Podman. [Memory recommendations](/en/operations/self-managed/node-setup#memory-settings). +- Disk: Avoid `NO_SPACE` - the vespaengine/vespa container image + headroom for data requires disk space. [Read more](/en/writing/feed-block). +- [Homebrew](https://brew.sh/) to install the [Vespa CLI](/en/clients/vespa-cli), or download the Vespa CLI from [Github releases](https://github.com/vespa-engine/vespa/releases). +- [uv](https://docs.astral.sh/uv/) For Python dependency handling + -{% include pre-req.html memory="4 GB" extra-reqs= -'
  • uv For Python dependency handling
  • ' %} ## Our use case -The sample use case is a document search application, for a user who wants to get answers and insights quickly from a document collection containing company documents, notes, learning material, training logs. -To make the blueprint more realistic, we required a dataset with more structured fields than are commonly found in public datasets. Therefore, we used a Large Language Model (LLM) to generate a custom one. +The sample use case is a document search application, for a user who wants to get answers and insights quickly from a document collection containing company documents, notes, learning material, training logs. To make the blueprint more realistic, we required a dataset with more structured fields than are commonly found in public datasets. Therefore, we used a Large Language Model (LLM) to generate a custom one. -It is a toy example, with only 100 documents, but we think it will illustrate the necessary concepts. -You can also feel confident that the blueprint will provide a starting point that can scale as you want, with minimal changes. +It is a toy example, with only 100 documents, but we think it will illustrate the necessary concepts. You can also feel confident that the blueprint will provide a starting point that can scale as you want, with minimal changes. Below you can see a sample document from the dataset: @@ -76,19 +89,18 @@ Below you can see a sample document from the dataset: } ``` -In order to evaluate the quality of the RAG application, we also need a set of representative queries, with annotated relevant documents. -Crucially, you need a set of representative queries that thoroughly cover your expected use case. More is better, but _some_ eval is always better than none. +In order to evaluate the quality of the RAG application, we also need a set of representative queries, with annotated relevant documents. Crucially, you need a set of representative queries that thoroughly cover your expected use case. More is better, but _some_ eval is always better than none. We used `gemini-2.5-pro` to create our queries and relevant document labels. Please check out our [blog post](https://blog.vespa.ai/improving-retrieval-with-llm-as-a-judge/) to learn more about using LLM-as-a-judge. -We decided to generate some queries that need several documents to provide a good answer, and some that only need one document. -If these queries are representative of the use case, we will show that they can be a great starting point for creating an (initial) ranking expression that can be used for retrieving and ranking candidate documents. But, it can (and should) also be improved, for example by collecting user interaction data, human labeling and/ or using an LLM to generate relevance feedback following the initial ranking expression. +We decided to generate some queries that need several documents to provide a good answer, and some that only need one document. If these queries are representative of the use case, we will show that they can be a great starting point for creating an (initial) ranking expression that can be used for retrieving and ranking candidate documents. But, it can (and should) also be improved, for example by collecting user interaction data, human labeling and/ or using an LLM to generate relevance feedback following the initial ranking expression. + ## Data modeling Here is the schema that we will use for our sample application. -```txt +```txt expandable # Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. schema doc { @@ -172,32 +184,33 @@ schema doc { Keep reading for an explanation and reasoning behind the choices in the schema. + ### Picking your searchable unit When building a RAG application, your first key decision is choosing the "searchable unit." This is the basic block of information your system will search through and return as context to the LLM. For instance, if you have millions of documents, some hundreds of pages long, what should be your searchable unit? Consider these points when selecting your searchable unit: -* **Too fine-grained (e.g., individual sentences or very small paragraphs):** - * Leads to duplication of context and metadata across many small units. - * May result in units lacking sufficient context for the LLM to make good selections or generate relevant responses. - * Increases overhead for managing many small document units. -* **Too coarse-grained (e.g., very long chapters or entire large documents):** - * Can cause performance issues due to the size of the units being processed. - * May lead to some large documents appearing relevant to too many queries, reducing precision. - * If you embed the whole document, a too large context will lead to reduced retrieval quality. +- **Too fine-grained (e.g., individual sentences or very small paragraphs):** +- Leads to duplication of context and metadata across many small units. +- May result in units lacking sufficient context for the LLM to make good selections or generate relevant responses. +- Increases overhead for managing many small document units. +- **Too coarse-grained (e.g., very long chapters or entire large documents):** +- Can cause performance issues due to the size of the units being processed. +- May lead to some large documents appearing relevant to too many queries, reducing precision. +- If you embed the whole document, a too large context will lead to reduced retrieval quality. We recommend erring on the side of using slightly larger units. -* LLMs are increasingly capable of handling larger contexts. -* In Vespa, you can index larger units, while avoiding data duplication and performance issues, by returning only the most relevant parts. +- LLMs are increasingly capable of handling larger contexts. +- In Vespa, you can index larger units, while avoiding data duplication and performance issues, by returning only the most relevant parts. With Vespa, it is now possible to return only the top k most relevant chunks of a document, and include and combine both document-level and chunk-level features in ranking. + ### Chunk selection -Assume you have chosen a document as your searchable unit. -Your documents may then contain text index fields of highly variable lengths. Consider for example a corpus of web pages. Some might be very long, while the average is well within the recommended size. See [scaling retrieval size](../../performance/sizing-search.html#scaling-retrieval-size) for more details. +Assume you have chosen a document as your searchable unit. Your documents may then contain text index fields of highly variable lengths. Consider for example a corpus of web pages. Some might be very long, while the average is well within the recommended size. See [scaling retrieval size](/en/performance/sizing-search#scaling-retrieval-size) for more details. While we recommend implementing guards against too long documents in your feeding pipeline, you still probably do not want to return every chunk of the top k documents to an LLM for RAG. @@ -205,24 +218,21 @@ In Vespa, we now have a solution for this problem. Check out our [blog post on l Below, we show how you can score both documents as well as individual chunks, and use that score to select the best chunks to be returned in a summary, instead of returning all chunks belonging to the top k ranked documents. -Compute closeness per chunk in a ranking function; use `elementwise(bm25(chunks), i, double)` for a per-chunk text signal. See [rank feature reference](../../reference/ranking/rank-features.html#elementwise-bm25) -Now available: elementwise rank functions and filtering on the content nodes. +Compute closeness per chunk in a ranking function; use `elementwise(bm25(chunks), i, double)` for a per-chunk text signal. See [rank feature reference](/en/reference/ranking/rank-features#elementwise-bm25) Now available: elementwise rank functions and filtering on the content nodes. This allows you to pick a large document as the searchable unit, while still addressing the potential drawbacks many encounter as follows: -* Pick your (larger) document as your searchable unit. -* Chunk the text-fields automatically on indexing. -* Embed each chunk (enabled through Vespa's multivector support) -* Calculate chunk-level features (e.g. bm25 and embedding similarity) and document-level features. Combine as you want. -* Limit the actual chunks that are returned to the ones that are actually relevant context for the LLM. +- Pick your (larger) document as your searchable unit. +- Chunk the text-fields automatically on indexing. +- Embed each chunk (enabled through Vespa's multivector support) +- Calculate chunk-level features (e.g. bm25 and embedding similarity) and document-level features. Combine as you want. +- Limit the actual chunks that are returned to the ones that are actually relevant context for the LLM. This allows you to index larger units, while avoiding data duplication and performance issues, by returning only the most relevant parts. -Vespa also supports automatic [chunking](../../reference/writing/indexing-language.html#converters) -in the [indexing language](../../writing/indexing.html). +Vespa also supports automatic [chunking](/en/reference/writing/indexing-language#converters) in the [indexing language](/en/writing/indexing). -Here are the parts of the schema, which defines the searchable unit as a document with a text field, -and automatically chunks it into smaller parts of 1024 characters, which each are embedded and indexed separately: +Here are the parts of the schema, which defines the searchable unit as a document with a text field, and automatically chunks it into smaller parts of 1024 characters, which each are embedded and indexed separately: ```txt field chunks type array { @@ -238,15 +248,11 @@ field chunk_embeddings type tensor(chunk{}, x[96]) { } ``` -In Vespa, we can specify which chunks to be returned with a summary feature, see -[docs](../../reference/schemas/schemas.html#select-elements-by) for details. For this blueprint, -we will return the top 3 chunks based on the similarity score of the chunk embeddings, -which is calculated in the ranking phase. Note that this feature could be any chunk-level -summary feature defined in your rank-profile. +In Vespa, we can specify which chunks to be returned with a summary feature, see [docs](/en/reference/schemas/schemas#select-elements-by) for details. For this blueprint, we will return the top 3 chunks based on the similarity score of the chunk embeddings, which is calculated in the ranking phase. Note that this feature could be any chunk-level summary feature defined in your rank-profile. Here is how the summary feature is calculated in the rank-profile: -```txt +```txt expandable # This function unpacks the bits of each dimension of the mapped chunk_embeddings attribute tensor function chunk_emb_vecs() { expression: unpack_bits(attribute(chunk_embeddings)) @@ -280,21 +286,17 @@ summary-features { } ``` -{% include note.html content="The ranking expression may seem a bit complex, as we chose to embed each chunk independently, -store the embeddings in a binarized format, and then unpack them to calculate similarity based on their float representations. -For single dimension dense vector similarity between same-precision embeddings, this can be simplified significantly -using the [closeness](../../reference/ranking/rank-features.html#closeness(name)) convenience function." %} + +**Note:** + +The ranking expression may seem a bit complex, as we chose to embed each chunk independently, store the embeddings in a binarized format, and then unpack them to calculate similarity based on their float representations. For single dimension dense vector similarity between same-precision embeddings, this can be simplified significantly using the [closeness](/en/reference/ranking/rank-features#closeness(name)) convenience function. + -Note that we want to use the float-representation of the query-embedding, and thus also need to convert the binary -embedding of the chunks to float. After that, we can calculate the similarity score between the query -embedding and the chunk embeddings using cosine similarity (the dot product, and then normalize it by the norms of the embeddings). +Note that we want to use the float-representation of the query-embedding, and thus also need to convert the binary embedding of the chunks to float. After that, we can calculate the similarity score between the query embedding and the chunk embeddings using cosine similarity (the dot product, and then normalize it by the norms of the embeddings). -See [ranking expressions](../../reference/ranking/ranking-expressions.html#non-primitive-functions) for more details on the -`top`-function, and other functions available for ranking expressions. +See [ranking expressions](/en/reference/ranking/ranking-expressions#non-primitive-functions) for more details on the `top`-function, and other functions available for ranking expressions. -Now, we can use this summary feature in our document summary to return the top 3 chunks of the document, -which will be used as context for the LLM. Note that we can also define a document summary that returns all chunks, -which might be useful for another use case, such as deep research. +Now, we can use this summary feature in our document summary to return the top 3 chunks of the document, which will be used as context for the LLM. Note that we can also define a document summary that returns all chunks, which might be useful for another use case, such as deep research. ```txt document-summary top_3_chunks { @@ -306,10 +308,10 @@ document-summary top_3_chunks { } ``` + ### Use multiple text fields, consider multiple embeddings -We recommend indexing different textual content as separate indexes. -These can be searched together, using [field-sets](../../reference/schemas/schemas.html#fieldset) +We recommend indexing different textual content as separate indexes. These can be searched together, using [field-sets](/en/reference/schemas/schemas#fieldset) In our schema, this is exemplified by the sections below, which define the `title` and `chunks` fields as separate indexed text fields. @@ -325,11 +327,9 @@ field chunks type array { } ``` -Whether you should have separate embedding fields, depends on whether the added memory usage is justified by the -quality improvement you could get from the additional embedding field. +Whether you should have separate embedding fields, depends on whether the added memory usage is justified by the quality improvement you could get from the additional embedding field. -We choose to index both a `title_embedding` and a `chunk_embeddings` field for this blueprint, as we aim to minimize -cost by embedding the binary vectors. +We choose to index both a `title_embedding` and a `chunk_embeddings` field for this blueprint, as we aim to minimize cost by embedding the binary vectors. ```txt field title_embedding type tensor(title{}, x[96]) { @@ -346,56 +346,51 @@ field chunk_embeddings type tensor(chunk{}, x[96]) { } ``` -Indexing several embedding fields may not be worth the cost for you. Evaluate whether the cost-quality trade-off -is worth it for your application. +Indexing several embedding fields may not be worth the cost for you. Evaluate whether the cost-quality trade-off is worth it for your application. + +If you have different vector space representations of your document (e.g images), indexing them separately is likely worth it, as they are likely to provide signals that are complementary to the text-based embeddings. -If you have different vector space representations of your document (e.g images), indexing them separately -is likely worth it, as they are likely to provide signals that are complementary to the text-based embeddings. ### Model Metadata and Signals Using Structured Fields -We recommend modeling metadata and signals as structured fields in your schema. -Below are some general recommendations, as well as the implementation in our blueprint schema. +We recommend modeling metadata and signals as structured fields in your schema. Below are some general recommendations, as well as the implementation in our blueprint schema. **Metadata** — knowledge about your data: -* Authors, publish time, source, links, category, price, … -* Usage: filters, ranking, grouping/aggregation -* Index only metadata that are strong filters +- Authors, publish time, source, links, category, price, … +- Usage: filters, ranking, grouping/aggregation +- Index only metadata that are strong filters In our blueprint schema, we include these metadata fields to demonstrate these concepts: -* `id` - document identifier -* `title` - document name/filename for display and text matching -* `created_timestamp`, `modified_timestamp` - temporal metadata for filtering and ranking by recency +- `id` - document identifier +- `title` - document name/filename for display and text matching +- `created_timestamp`, `modified_timestamp` - temporal metadata for filtering and ranking by recency **Signals** — observations about your data: -* Popularity, quality, spam probability, click_probability, … -* Usage: ranking -* Often updated separately via partial updates -* Multiple teams can add their own signals independently +- Popularity, quality, spam probability, click_probability, … +- Usage: ranking +- Often updated separately via partial updates +- Multiple teams can add their own signals independently In our blueprint schema, we include several of these signals: -* `last_opened_timestamp` - user engagement signal for personalization -* `open_count` - popularity signal indicating document importance -* `favorite` - explicit user preference signal, can be used for boosting relevant content +- `last_opened_timestamp` - user engagement signal for personalization +- `open_count` - popularity signal indicating document importance +- `favorite` - explicit user preference signal, can be used for boosting relevant content These fields are configured as `attribute | summary` to enable efficient filtering, sorting, and grouping operations while being returned in search results. The timestamp fields allow for temporal filtering (e.g., "recent documents") and recency-based ranking, while usage signals like `open_count` and `favorite` can boost frequently accessed or explicitly marked important documents. -Consider [parent-child](../../schemas/parent-child.html) relationships for low-cardinality metadata. -Most large scale RAG application schemas contain at least a hundred structured fields. +Consider [parent-child](/en/schemas/parent-child) relationships for low-cardinality metadata. Most large scale RAG application schemas contain at least a hundred structured fields. ## LLM-generation with OpenAI-client -Vespa supports both Local LLMs, and any OpenAI-compatible API for LLM generation. For details, see [LLMs in Vespa](../../llms-in-vespa.html) +Vespa supports both Local LLMs, and any OpenAI-compatible API for LLM generation. For details, see [LLMs in Vespa](/en/rag/llms-in-vespa) -The recommended way to provide an API key is by using the [secret store](../../security/secret-store.html) in Vespa Cloud. +The recommended way to provide an API key is by using the [secret store](/en/security/secret-store) in Vespa Cloud. -To enable this, you need to create a vault (if you don't have one already) and a secret through the -[Vespa Cloud console](https://cloud.vespa.ai/). If your vault is named `sample-apps` and contains a secret -with the name `openai-api-key`, you would use the following configuration in your `services.xml` to set up the OpenAI client to use that secret: +To enable this, you need to create a vault (if you don't have one already) and a secret through the [Vespa Cloud console](/). If your vault is named `sample-apps` and contains a secret with the name `openai-api-key`, you would use the following configuration in your `services.xml` to set up the OpenAI client to use that secret: ```xml @@ -411,11 +406,10 @@ with the name `openai-api-key`, you would use the following configuration in you Alternatively, for local deployments, you can set the `X-LLM-API-KEY` header in your query to use the OpenAI client for generation. -To test generation using the OpenAI client, post a query that runs the `openai` search chain, with `format=sse`. -(Use `format=json` for a streaming json response including both the search hits and the LLM-generated tokens.) +To test generation using the OpenAI client, post a query that runs the `openai` search chain, with `format=sse`. (Use `format=json` for a streaming json response including both the search hits and the LLM-generated tokens.) ```bash -vespa query \ +$ vespa query \ --timeout 60 \ --header="X-LLM-API-KEY:" \ yql='select * @@ -429,49 +423,45 @@ vespa query \ hits=5 ``` + ## Structuring your vespa application -This section provides recommendations for structuring your Vespa application package. See also the -[application package docs](../../application-packages.html) for more details on the application package structure. -Note that this is not mandatory, and it might be simpler to start without query profiles and rank profiles, -but as you scale out your application, it will be beneficial to have a well-structured application package. +This section provides recommendations for structuring your Vespa application package. See also the [application package docs](/en/basics/applications) for more details on the application package structure. Note that this is not mandatory, and it might be simpler to start without query profiles and rank profiles, but as you scale out your application, it will be beneficial to have a well-structured application package. Consider the following structure for our application package: -```txt +```txt expandable app ├── models -│   └── lightgbm_model.json +│ └── lightgbm_model.json ├── schemas -│   └── doc +│ └── doc │ │ ├-- base-features.profile -│   │   ├── collect-second-phase.profile -│   │   ├── collect-training-data.profile -│   │   ├── learned-linear.profile -│   │   ├── match-only.profile -│   │   └── second-with-gbdt.profile -│   └── doc.sd +│ │ ├── collect-second-phase.profile +│ │ ├── collect-training-data.profile +│ │ ├── learned-linear.profile +│ │ ├── match-only.profile +│ │ └── second-with-gbdt.profile +│ └── doc.sd ├── search -│   └── query-profiles -│   ├── deepresearch-with-gbdt.xml -│   ├── deepresearch.xml -│   ├── hybrid-with-gbdt.xml -│   ├── hybrid.xml -│   ├── rag-with-gbdt.xml -│   └── rag.xml +│ └── query-profiles +│ ├── deepresearch-with-gbdt.xml +│ ├── deepresearch.xml +│ ├── hybrid-with-gbdt.xml +│ ├── hybrid.xml +│ ├── rag-with-gbdt.xml +│ └── rag.xml ├── security -│   └── clients.pem +│ └── clients.pem └── services.xml ``` -You can see that we have separated the [query profiles](../../querying/query-profiles.html), -and [rank profiles](../../basics/ranking.html#rank-profiles) into their own directories. +You can see that we have separated the [query profiles](/en/reference/querying/query-profiles), and [rank profiles](/en/basics/ranking#rank-profiles) into their own directories. + ### Manage queries in query profiles -Query profiles let you maintain collections of query parameters in one file. -Clients choose a query profile → the profile sets everything else. -This lets us change behavior for a use case without involving clients. +Query profiles let you maintain collections of query parameters in one file. Clients choose a query profile → the profile sets everything else. This lets us change behavior for a use case without involving clients. Let us take a closer look at 3 of the query profiles in our sample application. @@ -479,10 +469,10 @@ Let us take a closer look at 3 of the query profiles in our sample application. 2. `rag` 3. `deepresearch` + ### **_hybrid_** query profile -This query profile will be the one used by clients for traditional search, where the user is presented a limited number of hits. -Our other query profiles will inherit this one (but may override some fields). +This query profile will be the one used by clients for traditional search, where the user is presented a limited number of hits. Our other query profiles will inherit this one (but may override some fields). ```xml @@ -502,10 +492,10 @@ Our other query profiles will inherit this one (but may override some fields). ``` + ### **_rag_** query profile -This will be the query profile where the `openai` searchChain will be added, to generate a response based on the retrieved context. -Here, we set some configuration that are specific to this use case. +This will be the query profile where the `openai` searchChain will be added, to generate a response based on the retrieved context. Here, we set some configuration that are specific to this use case. ```xml @@ -515,11 +505,10 @@ Here, we set some configuration that are specific to this use case. ``` + ### **_deepresearch_** query profile -Again, we will inherit from the `hybrid` query profile, but override with a `targetHits` value of 10 000 (original was 100) -that prioritizes recall over latency. -We will also increase number of hits to be returned, and increase the timeout to 5 seconds. +Again, we will inherit from the `hybrid` query profile, but override with a `targetHits` value of 10 000 (original was 100) that prioritizes recall over latency. We will also increase number of hits to be returned, and increase the timeout to 5 seconds. ```xml @@ -535,13 +524,9 @@ We will also increase number of hits to be returned, and increase the timeout to ``` -We will leave out the LLM-generation for this one, and let an LLM agent on the client side be responsible for using this -API call as a tool, and to determine whether enough relevant context to answer has been retrieved. -Note that the `targetHits` parameter set here does not really make sense until your dataset reach a certain scale. +We will leave out the LLM-generation for this one, and let an LLM agent on the client side be responsible for using this API call as a tool, and to determine whether enough relevant context to answer has been retrieved. Note that the `targetHits` parameter set here does not really make sense until your dataset reach a certain scale. -As we add more rank-profiles, we can also inherit the existing query profiles, only to override the `ranking.profile` -field to use a different rank profile. This is what we have done for the `rag-with-gbdt` and `deepresearch-with-gbdt` -query profiles, which will use the `second-with-gbdt` rank profile instead of the `learned-linear` rank profile. +As we add more rank-profiles, we can also inherit the existing query profiles, only to override the `ranking.profile` field to use a different rank profile. This is what we have done for the `rag-with-gbdt` and `deepresearch-with-gbdt` query profiles, which will use the `second-with-gbdt` rank profile instead of the `learned-linear` rank profile. ```xml @@ -551,44 +536,42 @@ query profiles, which will use the `second-with-gbdt` rank profile instead of th ``` + ### Separating out rank profiles -To build a great RAG application, assume you’ll need many ranking models. This will allow you to bucket-test alternatives -continuously and to serve different use cases, including data collection for different phases, and the rank profiles -to be used in production. +To build a great RAG application, assume you'll need many ranking models. This will allow you to bucket-test alternatives continuously and to serve different use cases, including data collection for different phases, and the rank profiles to be used in production. Separate common functions/setup into parent rank profiles and use `.profile` files. + ## Phased ranking in Vespa -Before we move on, it might be useful to recap Vespa´s [phased ranking](../../ranking/phased-ranking.html) approach. +Before we move on, it might be useful to recap Vespa´s [phased ranking](/en/ranking/phased-ranking) approach. -Below is a schematic overview of how to think about retrieval and ranking for this RAG blueprint. Since we are -developing this as a tutorial using a small toy dataset, the application can be deployed in a single machine, -using a single docker container, where only one container node and one container node will run. This is obviously -not the case for most real-world RAG applications, so this is cruical to have in mind as you want to scale your application. +Below is a schematic overview of how to think about retrieval and ranking for this RAG blueprint. Since we are developing this as a tutorial using a small toy dataset, the application can be deployed in a single machine, using a single docker container, where only one container node and one container node will run. This is obviously not the case for most real-world RAG applications, so this is cruical to have in mind as you want to scale your application. + ![phased ranking overview](/assets/img/phased-ranking-rag.png) + + +The stateless container nodes can be [scaled independently](/en/performance/sizing-search) to handle increased query load. -The stateless container nodes can -be [scaled independently](../../performance/sizing-search.html) to handle increased query load. ## Configuring match-phase (retrieval) This section will contain important considerations for the retrieval-phase of a RAG application in Vespa. -The goal of the retrieval phase is to retrieve candidate documents efficiently, and maximize recall, -without exposing too many documents to ranking. +The goal of the retrieval phase is to retrieve candidate documents efficiently, and maximize recall, without exposing too many documents to ranking. + ### Choosing a Retrieval Strategy: Vector, Text, or Hybrid? -As you could see from the schema, we create and index both a text representation and a vector representation for -each chunk of the document. This will allow us to use both text-based features and semantic features for both recall and ranking. +As you could see from the schema, we create and index both a text representation and a vector representation for each chunk of the document. This will allow us to use both text-based features and semantic features for both recall and ranking. The text and vector representation complement each other well: -* **Text-only** → misses recall of semantically similar content -* **Vector-only** → misses recall of specific content not well understood by the embedding models +- **Text-only** → misses recall of semantically similar content +- **Vector-only** → misses recall of specific content not well understood by the embedding models Our recommendation is to default to hybrid retrieval: @@ -608,15 +591,9 @@ select * where rank({targetHits:10000}nearestNeighbor(embeddings_field, query_embedding), default contains text(@query)) ``` -Notice that only the first argument of the [rank](../../reference/querying/yql.html#rank)-operator -will be used to determine if a document is a match, while all arguments are used for calculating rank features. -This mean we can do vector only for matching, but still use text-based features such as `bm25` and `nativeRank` for ranking. -Note that if you do this, it makes sense to increase the number of `targetHits` for the `nearestNeighbor`-operator. +Notice that only the first argument of the [rank](/en/reference/querying/yql#rank)-operator will be used to determine if a document is a match, while all arguments are used for calculating rank features. This mean we can do vector only for matching, but still use text-based features such as `bm25` and `nativeRank` for ranking. Note that if you do this, it makes sense to increase the number of `targetHits` for the `nearestNeighbor`-operator. -For our sample application, we add three different retrieval operators (that are combined with `OR`), -one with `weakAnd` for text matching, and two `nearestNeighbor` operators for vector matching, one for the title -and one for the chunks. This will allow us to retrieve both relevant documents based on text and vector similarity, -while also allowing us to return the most relevant chunks of the documents. +For our sample application, we add three different retrieval operators (that are combined with `OR`), one with `weakAnd` for text matching, and two `nearestNeighbor` operators for vector matching, one for the title and one for the chunks. This will allow us to retrieve both relevant documents based on text and vector similarity, while also allowing us to return the most relevant chunks of the documents. ```sql select * @@ -626,38 +603,29 @@ select * ({targetHits:100}nearestNeighbor(chunk_embeddings, embedding)) ``` + ### Choosing your embedding model (and strategy) -Choice of embedding model will be a trade-off between inference time (both indexing and query time), memory usage -(embedding dimensions) and quality. There are many good open-source models available, and we recommend checking -out the [MTEB leaderboard](https://huggingface.co/spaces/mteb/leaderboard), and look at the `Retrieval`-column to gauge performance, -while also considering the memory usage, vector dimensions, and context length of the model. +Choice of embedding model will be a trade-off between inference time (both indexing and query time), memory usage (embedding dimensions) and quality. There are many good open-source models available, and we recommend checking out the [MTEB leaderboard](https://huggingface.co/spaces/mteb/leaderboard), and look at the `Retrieval`-column to gauge performance, while also considering the memory usage, vector dimensions, and context length of the model. -See [model hub](../../rag/model-hub.html) for a list of provided models ready to use with Vespa. -See also [Huggingface Embedder](../../rag/embedding.html#huggingface-embedder) for details on -using other models (exported as ONNX) with Vespa. +See [model hub](/en/rag/model-hub) for a list of provided models ready to use with Vespa. See also [Huggingface Embedder](/en/rag/embedding#huggingface-embedder) for details on using other models (exported as ONNX) with Vespa. -In addition to dense vector representation, Vespa supports sparse embeddings (token weights) and multi-vector (ColBERT-style) embeddings. -See our [example notebook](https://vespa-engine.github.io/pyvespa//examples/mother-of-all-embedding-models-cloud.html#bge-m3-the-mother-of-all-embedding-models) of using the bge-m3 model, which supports both, with Vespa. +In addition to dense vector representation, Vespa supports sparse embeddings (token weights) and multi-vector (ColBERT-style) embeddings. See our [example notebook](https://vespa-engine.github.io/pyvespa//examples/mother-of-all-embedding-models-cloud#bge-m3-the-mother-of-all-embedding-models) of using the bge-m3 model, which supports both, with Vespa. -Vespa also supports [Matryoshka embeddings](https://blog.vespa.ai/combining-matryoshka-with-binary-quantization-using-embedder/), -which can be a great way of reducing inference cost for retrieval phases, by using a subset of the embedding dimensions, -while using more dimensions for increased precision in the later ranking phases. +Vespa also supports [Matryoshka embeddings](https://blog.vespa.ai/combining-matryoshka-with-binary-quantization-using-embedder/), which can be a great way of reducing inference cost for retrieval phases, by using a subset of the embedding dimensions, while using more dimensions for increased precision in the later ranking phases. For domain-specific applications or less popular languages, you may want to consider finetuning a model on your own data. + ### Consider binary vectors for recall -Another decision to make is which precision you will use for your embeddings. -See [binarization docs](../../rag/binarizing-vectors) for an introduction to binarization in Vespa. +Another decision to make is which precision you will use for your embeddings. See [binarization docs](/en/rag/binarizing-vectors) for an introduction to binarization in Vespa. -For most cases, binary vectors (in Vespa, packed into `int8`-representation) will provide an attractive tradeoff, -especially for recall during match-phase. -Consider these factors to determine whether this holds true for your application: +For most cases, binary vectors (in Vespa, packed into `int8`-representation) will provide an attractive tradeoff, especially for recall during match-phase. Consider these factors to determine whether this holds true for your application: -* Reduces memory-vector cost by 5 – 30 × -* Reduces query and indexing cost by 30 × -* Often reduces quality by only a few percentage points +- Reduces memory-vector cost by 5 – 30 × +- Reduces query and indexing cost by 30 × +- Often reduces quality by only a few percentage points ```txt field binary_chunk_embeddings type tensor(chunk{}, x) { @@ -666,9 +634,7 @@ field binary_chunk_embeddings type tensor(chunk{}, x) { } ``` -If you need higher precision vector similarity, you should use bfloat16 precision, and consider paging these vectors to -disk to avoid large memory cost. Note that this means that when accessing this field in ranking, they will also need -to be read from disk, so you need to restrict the number of hits that accesses this field to avoid performance issues. +If you need higher precision vector similarity, you should use bfloat16 precision, and consider paging these vectors to disk to avoid large memory cost. Note that this means that when accessing this field in ranking, they will also need to be read from disk, so you need to restrict the number of hits that accesses this field to avoid performance issues. ```txt field chunk_embeddings type tensor(chunk{}, x) { @@ -677,25 +643,18 @@ field chunk_embeddings type tensor(chunk{}, x) { } ``` -For example, if you want to calculate `closeness` for a paged embedding vector in first-phase, consider configuring -your retrieval operators (typically `weakAnd` and/or `nearestNeighbor`, optionally combined with filters) so that not -too many hits are matched. Another option is to enable match-phase limiting, see -[match-phase docs](../../reference/schemas/schemas.html#match-phase). -In essence, you restrict the number of matches by specifying an attribute field. +For example, if you want to calculate `closeness` for a paged embedding vector in first-phase, consider configuring your retrieval operators (typically `weakAnd` and/or `nearestNeighbor`, optionally combined with filters) so that not too many hits are matched. Another option is to enable match-phase limiting, see [match-phase docs](/en/reference/schemas/schemas#match-phase). In essence, you restrict the number of matches by specifying an attribute field. + ### Consider float-binary for ranking -In our blueprint, we choose to index binary vectors of the documents. This does not prevent us from using the -float-representation of the query embedding though. +In our blueprint, we choose to index binary vectors of the documents. This does not prevent us from using the float-representation of the query embedding though. -By unpacking the binary document chunk embeddings to their float representations -(using [`unpack_bits`](../../reference/ranking/ranking-expressions.html#unpack-bits)), -we can calculate the similarity between query and document with slightly higher precision using a `float-binary` dot product, -instead of hamming distance (`binary-binary`) +By unpacking the binary document chunk embeddings to their float representations (using [`unpack_bits`](/en/reference/ranking/ranking-expressions#unpack-bits)), we can calculate the similarity between query and document with slightly higher precision using a `float-binary` dot product, instead of hamming distance (`binary-binary`) Below, you can see how we can do this: -```txt +```txt expandable rank-profile base-features { inputs { @@ -728,20 +687,18 @@ rank-profile base-features { } ``` + ### Use complex linguistics/recall only for precision -Vespa gives you extensive control over [linguistics](../../linguistics/linguistics.html). -You can decide [match mode](../../reference/schemas/schemas.html#match), stemming, normalization, or control derived tokens. +Vespa gives you extensive control over [linguistics](/en/linguistics/linguistics). You can decide [match mode](/en/reference/schemas/schemas#match), stemming, normalization, or control derived tokens. -It is also possible to use more specific operators than [weakAnd](../../reference/querying/yql.html#weakand) -to match only close occurrences ([near](../../reference/querying/yql.html#near)/ -[onear](../../reference/querying/yql.html#near)), multiple alternatives ([equiv](../../linguistics/query-rewriting.html#equiv)), -weight items, set connectivity, and apply [query-rewrite](../../linguistics/query-rewriting.html) rules. +It is also possible to use more specific operators than [weakAnd](/en/reference/querying/yql#weakand) to match only close occurrences ([near](/en/reference/querying/yql#near)/ [onear](/en/reference/querying/yql#near)), multiple alternatives ([equiv](/en/linguistics/query-rewriting#equiv)), weight items, set connectivity, and apply [query-rewrite](/en/linguistics/query-rewriting) rules. -**Don’t use this to increase recall — improve your embedding model instead.** +**Don't use this to increase recall — improve your embedding model instead.** Consider using it to improve precision when needed. + ### Evaluating recall of the retrieval phase To know whether your retrieval phase is working well, you need to measure recall, number of total matches and the reported time spent. @@ -754,23 +711,25 @@ For this sample application, we set up an evaluation script that compares three 2. **WeakAnd-only**: Uses only text-based matching with `userQuery()`. 3. **Hybrid**: Combines both approaches with OR logic. -{% include note.html content="Note that this is only generic suggestion for and that you are of course free to include both -[filter clauses](../../reference/querying/yql.html#where), [grouping](../../grouping), -[predicates](../../schemas/predicate-fields.html), [geosearch](../../geo-search) etc. to support your specific use cases." %} + +**Note:** + +Note that this is only generic suggestion for and that you are of course free to include both [filter clauses](/en/reference/querying/yql#where), [grouping](/en/querying/grouping), [predicates](/en/schemas/predicate-fields), [geosearch](/en/querying/geo-search) etc. to support your specific use cases. + It is recommended to use a ranking profile that does not use any first-phase ranking, to run the match-phase evaluation faster. The evaluation will output metrics like: -* Recall (percentage of relevant documents matched) -* Total number of matches per query -* Query latency statistics -* Per-query detailed results (when `write_verbose=True`) to identify "offending" queries in regard to recall or performance. +- Recall (percentage of relevant documents matched) +- Total number of matches per query +- Query latency statistics +- Per-query detailed results (when `write_verbose=True`) to identify "offending" queries in regard to recall or performance. This will be valuable input for tuning each of them. -Run the complete evaluation script from the `eval/` directory to see detailed comparisons between all three retrieval -strategies on your dataset. +Run the complete evaluation script from the `eval/` directory to see detailed comparisons between all three retrieval strategies on your dataset. + #### Semantic Query Evaluation @@ -780,15 +739,7 @@ select * from doc where ({targetHits:100}nearestNeighbor(chunk_embeddings, embedding)) ``` - - +
    @@ -839,17 +790,16 @@ th { width: 120px; }
    Metric
    + #### WeakAnd Query Evaluation -The `userQuery` is just a convenience wrapper for `weakAnd`, see -[reference/query-language-reference.html](../../reference/querying/yql.html). -The default `targetHits` for `weakAnd` is 100, but it is [overridable](../../reference/querying/yql.html#targethits). +The `userQuery` is just a convenience wrapper for `weakAnd`, see [reference/query-language-reference.html](/en/reference/querying/yql). The default `targetHits` for `weakAnd` is 100, but it is [overridable](/en/reference/querying/yql#targethits). ```sql select * from doc where userQuery() ``` - +
    @@ -900,6 +850,7 @@ select * from doc where userQuery()
    Metric
    + #### Hybrid Query Evaluation ```sql @@ -909,7 +860,7 @@ select * from doc where userQuery() ``` - +
    @@ -960,17 +911,16 @@ userQuery()
    Metric
    + ### Tuning the retrieval phase -We can see that all queries match all relevant documents, which is expected, since we use `targetHits:100` -in the `nearestNeighbor` operator, and this is also the default for `weakAnd`(and `userQuery`). By setting `targetHits` -lower, we can see that recall will drop. +We can see that all queries match all relevant documents, which is expected, since we use `targetHits:100` in the `nearestNeighbor` operator, and this is also the default for `weakAnd`(and `userQuery`). By setting `targetHits` lower, we can see that recall will drop. In general, you have these options if you want to increase recall: 1. Increase `targetHits` in your retrieval operators (e.g., `nearestNeighbor`, `weakAnd`). 2. Improve your embedding model (use a better model or finetune it on your data). -3. You can also consider tuning HNSW parameters, see [docs on HNSW](../../querying/approximate-nn-hnsw#using-vespas-approximate-nearest-neighbor-search). +3. You can also consider tuning HNSW parameters, see [docs on HNSW](/en/querying/approximate-nn-hnsw#using-vespas-approximate-nearest-neighbor-search). Conversely, if you want to reduce the latency of one of your retrieval 'arms' at the cost of a small trade-off in recall, you can: @@ -988,34 +938,31 @@ rank-profile optimized inherits baseline { } ``` -See the [reference](../../reference/schemas/schemas.html#weakand) for more details on the `weakAnd` parameters. -These can also be set as query parameters. +See the [reference](/en/reference/schemas/schemas#weakand) for more details on the `weakAnd` parameters. These can also be set as query parameters. 1. As already [mentioned](#consider-binary-vectors-for-recall), consider binary vectors for your embeddings. 2. Consider using an embedding model with less dimensions, or using only a subset of the dimensions (e.g., using [Matryoshka embeddings](https://blog.vespa.ai/combining-matryoshka-with-binary-quantization-using-embedder/)). + ## First-phase ranking For the first-phase ranking, we must use a computationally cheap function, as it is applied to all documents matched in the retrieval phase. For many applications, this can amount to millions of candidate documents. -Common options include (learned) linear combination of features including text similarity features, vector closeness, and metadata. -It could also be a heuristic handwritten function. +Common options include (learned) linear combination of features including text similarity features, vector closeness, and metadata. It could also be a heuristic handwritten function. -Text features should include [nativeRank](../../reference/ranking/nativerank.html#nativeRank) -or [bm25](../../ranking/bm25.html#ranking-function) — not [fieldMatch](../../reference/ranking/rank-features.html#field-match-features-normalized) (it is too expensive). +Text features should include [nativeRank](/en/reference/ranking/nativerank#nativeRank) or [bm25](/en/ranking/bm25#ranking-function) — not [fieldMatch](/en/reference/ranking/rank-features#field-match-features-normalized) (it is too expensive). Considerations for deciding whether to choose `bm25` or `nativeRank`: -* **bm25**: cheapest, strong significance, no proximity, not normalized. -* **nativeRank**: 2 – 3 × costlier, truncated significance, includes proximity, normalized. +- **bm25**: cheapest, strong significance, no proximity, not normalized. +- **nativeRank**: 2 – 3 × costlier, truncated significance, includes proximity, normalized. + +For this blueprint, we opted for using `bm25` for first phase, but you could evaluate and compare to see whether the additional cost of using `nativeRank` is justified by increased quality. -For this blueprint, we opted for using `bm25` for first phase, but you could evaluate and compare to see whether the additional -cost of using `nativeRank` is justified by increased quality. ### Collecting training data for first-phase ranking -The features we will use for first-phase ranking are not normalized (i.e. they have values in different ranges). -This means we can't just weight them equally and expect that to be a good proxy for relevance. +The features we will use for first-phase ranking are not normalized (i.e. they have values in different ranges). This means we can't just weight them equally and expect that to be a good proxy for relevance. Below we will show how we can find (learn) optimal weights (coefficients) for each feature, so that we can combine them into a ranking-expression on the format: @@ -1023,13 +970,11 @@ Below we will show how we can find (learn) optimal weights (coefficients) for ea a * bm25(title) + b * bm25(chunks) + c * max_chunk_sim_scores() + d * max_chunk_text_scores() + e * avg_top_3_chunk_sim_scores() + f * avg_top_3_chunk_text_scores() ``` -The first thing we need to is to collect training data. -We do this using the [VespaFeatureCollector](https://vespa-engine.github.io/pyvespa/api/vespa/evaluation.html#vespa.evaluation.VespaFeatureCollector) from the pyvespa library. +The first thing we need to is to collect training data. We do this using the [VespaFeatureCollector](https://vespa-engine.github.io/pyvespa/api/vespa/evaluation.html#vespa.evaluation.VespaFeatureCollector) from the pyvespa library. -These are the features we will include: -(Below, ) +These are the features we will include: (Below, ) -```txt +```txt expandable rank-profile base-features { inputs { query(embedding) tensor(x[96]) @@ -1110,46 +1055,21 @@ rank-profile collect-training-data inherits base-features { } ``` -As you can see, we have defined a `collect-training-data` rank profile that inherits from the `base-features` rank profile. -This rank profile will collect the match features we defined in the `match-features` section. +As you can see, we have defined a `collect-training-data` rank profile that inherits from the `base-features` rank profile. This rank profile will collect the match features we defined in the `match-features` section. -The `random` expression in the `second-phase` allows us to collect random hits, and is necessary for our data collection script. -See the [docstring of pyvespa class VespaFeatureCollector](https://vespa-engine.github.io/pyvespa/api/vespa/evaluation.html#vespa.evaluation.VespaFeatureCollector) that is used in the script for details. +The `random` expression in the `second-phase` allows us to collect random hits, and is necessary for our data collection script. See the [docstring of pyvespa class VespaFeatureCollector](https://vespa-engine.github.io/pyvespa/api/vespa/evaluation.html#vespa.evaluation.VespaFeatureCollector) that is used in the script for details. -As you can see, we rely on the `bm25` and different vector similarity features (both document-level and chunk-level) -for the first-phase ranking. -These are relatively cheap to calculate, and will likely provide good enough ranking signals for the first-phase ranking. +As you can see, we rely on the `bm25` and different vector similarity features (both document-level and chunk-level) for the first-phase ranking. These are relatively cheap to calculate, and will likely provide good enough ranking signals for the first-phase ranking. -Running the command below will save a .csv-file with the collected features, which can be used to train a ranking model -for the first-phase ranking. +Running the command below will save a .csv-file with the collected features, which can be used to train a ranking model for the first-phase ranking. -
    -python eval/collect_pyvespa.py --collect_matchfeatures
    -
    +```bash +$ python eval/collect_pyvespa.py --collect_matchfeatures +``` Our output file looks like this: - - +
    @@ -1228,34 +1148,32 @@ Our output file looks like this:
    query_id
    -Note that the `relevance_score` in this table is just the random expression we used in the `second-phase` -of the `collect-training-data` rank profile, and will be dropped before training the model. +Note that the `relevance_score` in this table is just the random expression we used in the `second-phase` of the `collect-training-data` rank profile, and will be dropped before training the model. + ### Training a first-phase ranking model -As you recall, a first-phase ranking expression must be cheap to evaluate. -This most often means a heuristic handwritten combination of match features, or a linear model trained on match features. +As you recall, a first-phase ranking expression must be cheap to evaluate. This most often means a heuristic handwritten combination of match features, or a linear model trained on match features. -We will demonstrate how to train a simple Logistic Regression model to predict relevance based on the collected match features. -The full training script can be found in the [sample-apps repository](https://github.com/vespa-engine/sample-apps/blob/master/rag-blueprint/eval/train_logistic_regression.py). +We will demonstrate how to train a simple Logistic Regression model to predict relevance based on the collected match features. The full training script can be found in the [sample-apps repository](https://github.com/vespa-engine/sample-apps/blob/master/rag-blueprint/eval/train_logistic_regression.py). Some "gotchas" to be aware of: -* We sample an equal number of relevant and random documents for each query, to avoid class imbalance. -* We make sure that we drop `query_id` and `doc_id` columns before training. -* We apply standard scaling to the features before training the model. We apply the inverse transform to the model coefficients after training, so that we can use them in Vespa. -* We do 5-fold stratified cross-validation to evaluate the model performance, ensuring that each fold has a balanced number of relevant and random documents. -* We also make sure to have an unseen set of test queries to evaluate the model on, to avoid overfitting. +- We sample an equal number of relevant and random documents for each query, to avoid class imbalance. +- We make sure that we drop `query_id` and `doc_id` columns before training. +- We apply standard scaling to the features before training the model. We apply the inverse transform to the model coefficients after training, so that we can use them in Vespa. +- We do 5-fold stratified cross-validation to evaluate the model performance, ensuring that each fold has a balanced number of relevant and random documents. +- We also make sure to have an unseen set of test queries to evaluate the model on, to avoid overfitting. Run the training [script](https://github.com/vespa-engine/sample-apps/blob/master/rag-blueprint/eval/train_logistic_regression.py) ```bash -python eval/train_logistic_regression.py +$ python eval/train_logistic_regression.py ``` Expect output like this: -```txt +```txt expandable ------------------------------------------------------------ Cross-Validation Results (5-Fold, Standardized) ------------------------------------------------------------ @@ -1285,7 +1203,7 @@ Which seems quite good. With such a small dataset however, it is easy to overfit First, we need to add the learned coefficients as inputs to a new rank profile in our schema, so that we can use them in Vespa. -```txt +```txt expandable rank-profile learned-linear inherits collect-training-data { match-features: inputs { @@ -1317,10 +1235,9 @@ rank-profile learned-linear inherits collect-training-data { } ``` -To allow for changing the parameters without redeploying the application, we will also add the values of the -coefficients as query parameters to a new query profile. +To allow for changing the parameters without redeploying the application, we will also add the values of the coefficients as query parameters to a new query profile. -```xml +```xml expandable doc embed(@query) @@ -1345,20 +1262,20 @@ coefficients as query parameters to a new query profile. ``` + ### Evaluating first-phase ranking -Now we are ready to evaluate our first-phase ranking function. -We can use the [VespaEvaluator](https://vespa-engine.github.io/pyvespa/evaluating-vespa-application-cloud.html#vespaevaluator) from the [pyvespa](https://vespa-engine.github.io/pyvespa/) library to evaluate the first-phase ranking function. +Now we are ready to evaluate our first-phase ranking function. We can use the [VespaEvaluator](https://vespa-engine.github.io/pyvespa/evaluating-vespa-application-cloud.html#vespaevaluator) from the [pyvespa](https://vespa-engine.github.io/pyvespa/) library to evaluate the first-phase ranking function. Run the following command to run the [evaluation script](https://github.com/vespa-engine/sample-apps/blob/master/rag-blueprint/eval/evaluate_ranking.py) -``` -python eval/evaluate_ranking.py +```bash +$ python eval/evaluate_ranking.py ``` We run the evaluation script on a set of unseen test queries, and get the following output: -```json +```json expandable { "accuracy@1": 1.0, "accuracy@3": 1.0, @@ -1378,58 +1295,51 @@ We run the evaluation script on a set of unseen test queries, and get the follow } ``` -For the first phase ranking, we care most about recall, as we just want to make sure that the candidate documents are -ranked high enough to be included in the second-phase ranking. The number of documents to be reranked in second-phase -in total over all content nodes is controlled by the `total-rerank-count` parameter. +For the first phase ranking, we care most about recall, as we just want to make sure that the candidate documents are ranked high enough to be included in the second-phase ranking. The number of documents to be reranked in second-phase in total over all content nodes is controlled by the `total-rerank-count` parameter. -We can see that our results are already very good. This is of course due to the fact that we have a small,synthetic dataset. -In reality, you should align the metric expectations with your dataset and test queries. +We can see that our results are already very good. This is of course due to the fact that we have a small,synthetic dataset. In reality, you should align the metric expectations with your dataset and test queries. + +We can also see that our search time is quite fast, with an average of 22ms. You should consider whether this is well within your latency budget, as you want some headroom for second-phase ranking. -We can also see that our search time is quite fast, with an average of 22ms. You should consider whether this is well -within your latency budget, as you want some headroom for second-phase ranking. ## Second-phase ranking -For the second-phase ranking, we can afford to use a more expensive ranking expression, since we will only run it -on the top-k documents from the first-phase ranking (decided by the `total-rerank-count` parameter). +For the second-phase ranking, we can afford to use a more expensive ranking expression, since we will only run it on the top-k documents from the first-phase ranking (decided by the `total-rerank-count` parameter). + +This is where we can significantly improve ranking quality by using more sophisticated models and features that would be too expensive to compute for all matched documents. -This is where we can significantly improve ranking quality by using more sophisticated models and features that would -be too expensive to compute for all matched documents. ### Collecting features for second-phase ranking -For second-phase ranking, we request Vespa's default set of rank features, which includes a comprehensive set of text -features. See the [rank features documentation](../../reference/ranking/rank-features.html) for complete details. +For second-phase ranking, we request Vespa's default set of rank features, which includes a comprehensive set of text features. See the [rank features documentation](/en/reference/ranking/rank-features) for complete details. -We can collect both match features and rank features by running the same -[script](https://github.com/vespa-engine/sample-apps/blob/master/rag-blueprint/eval/collect_pyvespa.py) -as we did for first-phase ranking, with some additional parameters to collect rank features as well: +We can collect both match features and rank features by running the same [script](https://github.com/vespa-engine/sample-apps/blob/master/rag-blueprint/eval/collect_pyvespa.py) as we did for first-phase ranking, with some additional parameters to collect rank features as well: ```bash -python eval/collect_pyvespa.py --collect_rankfeatures --collect_matchfeatures --collector_name rankfeatures-secondphase +$ python eval/collect_pyvespa.py --collect_rankfeatures --collect_matchfeatures --collector_name rankfeatures-secondphase ``` This collects approximately 194 features, providing a rich feature set for training more sophisticated ranking models. + ### Training a GBDT model for second-phase ranking -With the expanded feature set, we can train a Gradient Boosted Decision Tree (GBDT) model to predict document relevance. -We use [LightGBM](../../ranking/lightgbm) for this purpose. +With the expanded feature set, we can train a Gradient Boosted Decision Tree (GBDT) model to predict document relevance. We use [LightGBM](/en/ranking/lightgbm) for this purpose. -Vespa also supports [XGBoost](../../ranking/xgboost) and [ONNX](../../ranking/onnx) models. +Vespa also supports [XGBoost](/en/ranking/xgboost) and [ONNX](/en/ranking/onnx) models. To train the model, run the following command ([link to training script](https://github.com/vespa-engine/sample-apps/blob/master/rag-blueprint/eval/train_lightgbm.py)): ```bash -python eval/train_lightgbm.py --input_file eval/output/Vespa-training-data_match_rank_second_phase_20250623_135819.csv +$ python eval/train_lightgbm.py --input_file eval/output/Vespa-training-data_match_rank_second_phase_20250623_135819.csv ``` The training process includes several important considerations: -* **Cross-validation**: We use 5-fold stratified cross-validation to evaluate model performance and prevent overfitting -* **Hyperparameter tuning**: We set conservative hyperparameters to prevent growing overly large and deep trees, especially important for smaller datasets -* **Feature selection**: Features with zero importance during cross-validation are excluded from the final model -* **Early stopping**: Training stops when validation scores don't improve for 50 rounds +- **Cross-validation**: We use 5-fold stratified cross-validation to evaluate model performance and prevent overfitting +- **Hyperparameter tuning**: We set conservative hyperparameters to prevent growing overly large and deep trees, especially important for smaller datasets +- **Feature selection**: Features with zero importance during cross-validation are excluded from the final model +- **Early stopping**: Training stops when validation scores don't improve for 50 rounds Example training output: @@ -1446,27 +1356,61 @@ Overall CV AUC: 0.9249 • ACC: 0.9216 ------------------------------------------------------------ ``` + ### Feature importance analysis The trained model reveals which features are most important for ranking quality. For our sample application, the top features include: -| Feature | Importance | -| --------------------------- | ---------- | -| nativeProximity | 168.8498 | -| firstPhase | 151.7382 | -| max_chunk_sim_scores | 69.4377 | -| avg_top_3_chunk_text_scores | 56.5079 | -| avg_top_3_chunk_sim_scores | 31.8700 | -| nativeRank | 20.0716 | -| nativeFieldMatch | 15.9914 | -| elementSimilarity(chunks) | 9.7003 | + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    FeatureImportance
    nativeProximity168.8498
    firstPhase151.7382
    max_chunk_sim_scores69.4377
    avg_top_3_chunk_text_scores56.5079
    avg_top_3_chunk_sim_scores31.8700
    nativeRank20.0716
    nativeFieldMatch15.9914
    elementSimilarity(chunks)9.7003
    Key observations: -* **Text proximity features** ([nativeProximity](../../reference/ranking/nativerank.html#nativeProximity)) are highly valuable for understanding query-document relevance -* **First-phase score** (`firstPhase`) being important validates that our first-phase ranking provides a good foundation -* **Chunk-level features** (both text and semantic) contribute significantly to ranking quality -* **Traditional text features** like [nativeRank](../../reference/ranking/nativerank.html#nativeRank) and [bm25](../../ranking/bm25.html#ranking-function) remain important +- **Text proximity features** ([nativeProximity](/en/reference/ranking/nativerank#nativeProximity)) are highly valuable for understanding query-document relevance +- **First-phase score** (`firstPhase`) being important validates that our first-phase ranking provides a good foundation +- **Chunk-level features** (both text and semantic) contribute significantly to ranking quality +- **Traditional text features** like [nativeRank](/en/reference/ranking/nativerank#nativeRank) and [bm25](/en/ranking/bm25#ranking-function) remain important + ### Integrating the GBDT model into Vespa @@ -1495,20 +1439,21 @@ rank-profile second-with-gbdt inherits collect-training-data { And redeploy your application: ```bash -vespa deploy +$ vespa deploy ``` + ### Evaluating second-phase ranking performance Run the [evaluate_ranking.py](https://github.com/vespa-engine/sample-apps/blob/master/rag-blueprint/eval/evaluate_ranking.py) script to evaluate the GBDT-powered second-phase ranking on unseen test queries: ```bash -python evaluate_ranking.py --second_phase +$ python evaluate_ranking.py --second_phase ``` Expected results should show something like this: -```json +```json expandable { "accuracy@1": 0.9, "accuracy@3": 1.0, @@ -1528,11 +1473,11 @@ Expected results should show something like this: } ``` -For a larger dataset, we would expect to see significant improvement over first-phase ranking. -Since our first-phase ranking is already quite good, we can not see this here, but we will leave the comparison code for you to run on a real-world dataset. +For a larger dataset, we would expect to see significant improvement over first-phase ranking. Since our first-phase ranking is already quite good, we can not see this here, but we will leave the comparison code for you to run on a real-world dataset. We also observe a slight increase in search time (from 22ms to 35ms average), which is expected due to the additional complexity of the GBDT model. + ### Query profiles with GBDT ranking Create new query profiles that leverage the improved ranking: @@ -1553,111 +1498,101 @@ Create new query profiles that leverage the improved ranking: Test the improved ranking: ```bash -vespa query query="what are key points learned for finetuning llms?" queryProfile=hybrid-with-gbdt +$ vespa query query="what are key points learned for finetuning llms?" queryProfile=hybrid-with-gbdt ``` For RAG applications with LLM generation: ```bash -vespa query \ +$ vespa query \ --timeout 60 \ query="what are key points learned for finetuning llms?" \ queryProfile=rag-with-gbdt ``` + ### Best practices for second-phase ranking **Model complexity considerations:** -* Use more sophisticated models (GBDT, neural networks) that would be too expensive for first-phase -* Take advantage of the reduced candidate set (typically 100-10,000 documents) -* Include expensive text features like `nativeProximity` and `fieldMatch` +- Use more sophisticated models (GBDT, neural networks) that would be too expensive for first-phase +- Take advantage of the reduced candidate set (typically 100-10,000 documents) +- Include expensive text features like `nativeProximity` and `fieldMatch` **Feature engineering:** -* Combine first-phase scores with additional text and semantic features -* Use chunk-level aggregations (max, average, top-k) to capture document structure -* Include metadata signals +- Combine first-phase scores with additional text and semantic features +- Use chunk-level aggregations (max, average, top-k) to capture document structure +- Include metadata signals **Training data quality:** -* Use the first-phase ranking to generate better training data -* Consider having LLMs generate relevance judgments for top-k results -* Iteratively improve with user interaction data when available +- Use the first-phase ranking to generate better training data +- Consider having LLMs generate relevance judgments for top-k results +- Iteratively improve with user interaction data when available **Performance monitoring:** -* Monitor latency impact of second-phase ranking -* Adjust `total-rerank-count` based on quality vs. performance trade-offs -* Consider using different models for different query types or use cases +- Monitor latency impact of second-phase ranking +- Adjust `total-rerank-count` based on quality vs. performance trade-offs +- Consider using different models for different query types or use cases + +The second-phase ranking represents a crucial step in building high-quality RAG applications, providing the precision needed for effective LLM context while maintaining reasonable query latencies. -The second-phase ranking represents a crucial step in building high-quality RAG applications, -providing the precision needed for effective LLM context while maintaining reasonable query latencies. ## (Optional) Global-phase ranking -We also have the option of configuring [global-phase](../../reference/schemas/schemas.html#globalphase-rank) ranking, which can rerank the top k -(as set by `total-rerank-count` parameter) documents from the second-phase ranking. +We also have the option of configuring [global-phase](/en/reference/schemas/schemas#globalphase-rank) ranking, which can rerank the top k (as set by `total-rerank-count` parameter) documents from the second-phase ranking. + +Common options for global-phase are [cross-encoders](/en/ranking/cross-encoders) or another GBDT model, trained for better separating top ranked documents on objectives such as [LambdaMart](https://xgboost.readthedocs.io/en/latest/tutorials/learning_to_rank.html). For RAG applications, we consider this less important than for search applications where the results are mainly consumed by an human, as LLMs don't care that much about the ordering of the results. -Common options for global-phase are [cross-encoders](../../ranking/cross-encoders.html) or another GBDT model, trained for -better separating top ranked documents on objectives such as [LambdaMart](https://xgboost.readthedocs.io/en/latest/tutorials/learning_to_rank.html). For RAG applications, -we consider this less important than for search applications where the results are mainly consumed by an human, -as LLMs don't care that much about the ordering of the results. ## Further improvements -Finally, we will sketch out some opportunities for further improvements. -As you have seen, we started out with only binary relevance labels for a few queries, and trained a model -based on the relevant docs and a set of random documents. +Finally, we will sketch out some opportunities for further improvements. As you have seen, we started out with only binary relevance labels for a few queries, and trained a model based on the relevant docs and a set of random documents. -As you may have noted, we have not discussed what most people think about when discussing RAG evals, evaluating the -"Generation"-step. There are several tools available to do this, for example [ragas](https://docs.ragas.io/en/stable/) and [ARES](https://github.com/stanford-futuredata/ARES). -We refer to other sources for details on this, as this tutorial is probably enough to digest as it is. +As you may have noted, we have not discussed what most people think about when discussing RAG evals, evaluating the "Generation"-step. There are several tools available to do this, for example [ragas](https://docs.ragas.io/en/stable/) and [ARES](https://github.com/stanford-futuredata/ARES). We refer to other sources for details on this, as this tutorial is probably enough to digest as it is. + +This was useful initially, as we had no better way to retrieve the candidate documents. Now, that we have a reasonably good second-phase ranking, we could potentially generate a new set of relevance labels for queries that we did not have labels for by having an LLM do relevance judgments of the top k returned hits. This training dataset would likely be even better in separating the top documents. -This was useful initially, as we had no better way to retrieve the candidate documents. -Now, that we have a reasonably good second-phase ranking, we could potentially generate a new set of relevance labels -for queries that we did not have labels for by having an LLM do relevance judgments of the top k returned hits. -This training dataset would likely be even better in separating the top documents. ## Summary -In this tutorial, we have built a complete RAG application using Vespa, providing our recommendations for how to -approach both retrieval phase with binary vectors and text matching, first-phase ranking with a linear combination -of relatively cheap features to a more sophisticated second-phase ranking system with more expensive features and a GBDT model. +In this tutorial, we have built a complete RAG application using Vespa, providing our recommendations for how to approach both retrieval phase with binary vectors and text matching, first-phase ranking with a linear combination of relatively cheap features to a more sophisticated second-phase ranking system with more expensive features and a GBDT model. + +We hope that this tutorial, along with the provided code in our [sample-apps repository](https://github.com/vespa-engine/sample-apps/tree/master/rag-blueprint), will serve as a useful reference for building your own RAG applications, with an evaluation-driven approach. -We hope that this tutorial, along with the provided code in our [sample-apps repository](https://github.com/vespa-engine/sample-apps/tree/master/rag-blueprint), -will serve as a useful reference for building your own RAG applications, with an evaluation-driven approach. +By using the principles demonstrated in this tutorial, you are empowered to build high-quality RAG applications that can scale to any dataset size, and any query load. -By using the principles demonstrated in this tutorial, you are empowered to build high-quality RAG applications -that can scale to any dataset size, and any query load. ## FAQ -* **Q: Why don't you use ColBERT for ranking?** - - A: We love ColBERT, and it has shown great performance. We do support ColBERT-style models in Vespa. The challenge is the added cost in memory storage, especially for large-scale applications. If you use it, we recommend consider binarizing the vectors to reduce memory usage 32x compared to float. If you want to improve the ranking quality and accept the additional cost, we encourage you to evaluate and try. - Here are some resources if you want to learn more about using ColBERT with Vespa: - - * [Announcing ColBERT embedder](https://blog.vespa.ai/announcing-colbert-embedder-in-vespa/#what-is-colbert?) - * [Long context ColBERT](https://blog.vespa.ai/announcing-long-context-colbert-in-vespa/) - * [Long context ColBERT sample app](https://github.com/vespa-engine/sample-apps/tree/master/colbert-long/#vespa-sample-applications---long-context-colbert) - * [ColBERT sample app](https://github.com/vespa-engine/sample-apps/tree/master/colbert) - * [ColBERT embedder reference](../../rag/embedding.html#colbert-embedder) - * [ColBERT standalone python example notebook](https://vespa-engine.github.io/pyvespa/examples/colbert_standalone_Vespa-cloud.html) - * [ColBERT standalone long context example notebook](https://vespa-engine.github.io/pyvespa/examples/colbert_standalone_long_context_Vespa-cloud.html) - -* **Q: Which embedding models can I use with Vespa?** - - A: Vespa supports a variety of embedding models. For a list of vespa provided models on Vespa Cloud, see [Model hub](../../rag/model-hub.html). See also [embedding reference](../../rag/embedding.html#provided-embedders) for how to use embedders. You can also use private models (gated by authentication with Bearer token from Vespa Cloud secret store). - -* **Q: Do I need to use an LLM with Vespa?** - - A: No, you are free to use Vespa as a search engine. We provide the option of calling out to LLMs from within a Vespa application for reduced latency compared to sending large search results sets several times over network as well as the option to deploy Local LLMs, optionally in your own infrastructure if you prefer. See [Vespa Cloud Enclave](../../operations/enclave/enclave.html) - -* **Q: Why do we use binary vectors for the document embeddings?** - - A: Binary vectors takes up a lot less memory and are faster to compute distances on, with only a slight reduction in quality. See blog [post](https://blog.vespa.ai/combining-matryoshka-with-binary-quantization-using-embedder/) for details. - -* **Q: How can you say that Vespa can scale to any data and query load?** - - A: Vespa can scale both the stateless container nodes and content nodes of your application. See [overview](../overview.html) and [elasticity](../../content/elasticity.html) for details. + + +We love ColBERT, and it has shown great performance. We do support ColBERT-style models in Vespa. The challenge is the added cost in memory storage, especially for large-scale applications. If you use it, we recommend consider binarizing the vectors to reduce memory usage 32x compared to float. If you want to improve the ranking quality and accept the additional cost, we encourage you to evaluate and try. Here are some resources if you want to learn more about using ColBERT with Vespa: + +- [Announcing ColBERT embedder](https://blog.vespa.ai/announcing-colbert-embedder-in-vespa/#what-is-colbert?) +- [Long context ColBERT](https://blog.vespa.ai/announcing-long-context-colbert-in-vespa/) +- [Long context ColBERT sample app](https://github.com/vespa-engine/sample-apps/tree/master/colbert-long/#vespa-sample-applications---long-context-colbert) +- [ColBERT sample app](https://github.com/vespa-engine/sample-apps/tree/master/colbert) +- [ColBERT embedder reference](/en/rag/embedding#colbert-embedder) +- [ColBERT standalone python example notebook](https://vespa-engine.github.io/pyvespa/examples/colbert_standalone_Vespa-cloud.html) +- [ColBERT standalone long context example notebook](https://vespa-engine.github.io/pyvespa/examples/colbert_standalone_long_context_Vespa-cloud.html) + + + +Vespa supports a variety of embedding models. For a list of vespa provided models on Vespa Cloud, see [Model hub](/en/rag/model-hub). See also [embedding reference](/en/rag/embedding#provided-embedders) for how to use embedders. You can also use private models (gated by authentication with Bearer token from Vespa Cloud secret store). + + + +No, you are free to use Vespa as a search engine. We provide the option of calling out to LLMs from within a Vespa application for reduced latency compared to sending large search results sets several times over network as well as the option to deploy Local LLMs, optionally in your own infrastructure if you prefer. See [Vespa Cloud Enclave](/en/operations/enclave/enclave) + + + +Binary vectors takes up a lot less memory and are faster to compute distances on, with only a slight reduction in quality. See blog [post](https://blog.vespa.ai/combining-matryoshka-with-binary-quantization-using-embedder/) for details. + + + +Vespa can scale both the stateless container nodes and content nodes of your application. See [overview](../overview) and [elasticity](/en/content/elasticity) for details. + + diff --git a/en/learn/tutorials/text-search-ml.md b/en/learn/tutorials/text-search-ml.md deleted file mode 100644 index 158d16f8f9..0000000000 --- a/en/learn/tutorials/text-search-ml.md +++ /dev/null @@ -1,490 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Improving Text Search through ML" -redirect_from: - - /en/tutorials/text-search-ml ---- - - -At this point, we assume you have read our [Text Search Tutorial](text-search.html) and accomplished the following steps. -* Created and deployed a basic text search app in Vespa. -* Fed the app with the MS MARCO full document dataset. -* Compared and evaluated two different ranking functions. - -We are now going to show you how to create a dataset that joins relevance information from the MS MARCO dataset -with ranking features from Vespa to enable you to train ML models to improve your application. -More specifically, you will accomplish the following steps in this tutorial. - -* Learn how to collect rank feature data from Vespa associated with a specific query. -* Create a dataset that can be used to improve your app's ranking function. -* Propose sanity-checks to help you detect bugs in your data collection logic - and ensure you have a properly built dataset at the end of the process. -* Illustrate the importance of going beyond pointwise loss functions when dealing with Learning To Rank (LTR) tasks. - -[Vespa Product Ranking](https://github.com/vespa-engine/sample-apps/tree/master/commerce-product-ranking) -is a good resource for Learning To Rank using XGBoost and LightGBM, with linked blog posts. - - -## Collect rank feature data from Vespa - -Vespa's [rank feature set](../../reference/ranking/rank-features.html) contains a large set of low and high level features. -Those features are useful to understand the behavior of your app and to improve your ranking function. - - -### Default rank features - -To access the default set of ranking features, -set the query parameter [`ranking.listFeatures`](../../reference/api/query.html#ranking.listfeatures) to `true`. -For example, below is the body of a post request that in a [query](../../querying/query-language.html), -selects the `bm25` rank-profile developed in the previous tutorial -and returns the rank features associated with each of the results returned. - -
    - -
    -$ vespa query \
    -  'yql=select id,rankfeatures from msmarco where userQuery()' \
    -  'query=what is dad bod' \
    -  'ranking=bm25' \
    -  'type=weakAnd' \
    -  'ranking.listFeatures=true'
    -
    -
    - -The list of rank features that are returned by default can change in the future - the current list can be checked in the -[system test](https://github.com/vespa-engine/system-test/blob/master/tests/search/rankfeatures/dump.txt). -For the request specified by the body above we get the following (edited) json back. -Each result will contain a field called `rankfeatures` containing the set of default ranking features: - -
    {% highlight json%}
    -{
    -    "root": {
    -        "children": [
    -            ...
    -            {
    -                "fields": {
    -                    "rankfeatures": {
    -                        ...
    -                        "attributeMatch(id).totalWeight": 0.0,
    -                        "attributeMatch(id).weight": 0.0,
    -                        "elementCompleteness(body).completeness": 0.5051413881748072,
    -                        "elementCompleteness(body).elementWeight": 1.0,
    -                        "elementCompleteness(body).fieldCompleteness": 0.010282776349614395,
    -                        "elementCompleteness(body).queryCompleteness": 1.0,
    -                        "elementCompleteness(title).completeness": 0.75,
    -                        "elementCompleteness(title).elementWeight": 1.0,
    -                        "elementCompleteness(title).fieldCompleteness": 1.0,
    -                        "elementCompleteness(title).queryCompleteness": 0.5,
    -                        "elementCompleteness(url).completeness": 0.0,
    -                        "elementCompleteness(url).elementWeight": 0.0,
    -                        "elementCompleteness(url).fieldCompleteness": 0.0,
    -                        "elementCompleteness(url).queryCompleteness": 0.0,
    -                        "fieldMatch(body)": 0.7529285549778888,
    -                        "fieldMatch(body).absoluteOccurrence": 0.065,
    -                        ...
    -                    }
    -                },
    -                "id": "index:msmarco/0/811ccbaf9796f92bfa343045",
    -                "relevance": 37.7705101001455,
    -                "source": "msmarco"
    -            },
    -        ],
    -    ...
    -}
    -{% endhighlight %}
    - -### Chose and process specific rank features - -If instead of returning the complete set of rank features you want to select [specific ones](../../reference/ranking/rank-features.html), -you can add a new rank-profile (let's call it `collect_rank_features`) to our _msmarco.sd_ schema definition -and disable the default ranking features by adding `ignore-default-rank-features` to the new rank-profile. -In addition, we can specify the desired features within the `rank-features` element. -In the example below we explicitly configured Vespa to only return -`bm25(title)`, `bm25(body)`, `nativeRank(title)` and `nativeRank(body)`. - -Note that using _all_ available rank features comes with computational cost, -as Vespa needs to calculate all these features. -Using many features is usually only advisable using second phase ranking, -see [phased ranking with Vespa](../../ranking/phased-ranking.html). - -
    -schema msmarco {
    -    document msmarco {
    -        field id type string {
    -            indexing: attribute | summary
    -        }
    -        field title type string {
    -            indexing: index | summary
    -            index: enable-bm25
    -        }
    -        field url type string {
    -            indexing: index | summary
    -        }
    -        field body type string {
    -            indexing: index
    -            index: enable-bm25
    -        }
    -    }
    -
    -    document-summary minimal {
    -        summary id {  }
    -    }
    -
    -    fieldset default {
    -        fields: title, body, url
    -    }
    -
    -    rank-profile default {
    -        first-phase {
    -            expression: nativeRank(title, body, url)
    -        }
    -    }
    -
    -    rank-profile bm25 inherits default {
    -        first-phase {
    -            expression: bm25(title) + bm25(body) + bm25(url)
    -        }
    -    }
    -
    -    rank-profile collect_rank_features inherits default {
    -        first-phase {
    -            expression: bm25(title) + bm25(body) + bm25(url)
    -        }
    -        second-phase {
    -            expression: random
    -        }
    -
    -        match-features {
    -            bm25(title)
    -            bm25(body)
    -            bm25(url)
    -            nativeRank(title)
    -            nativeRank(body)
    -            nativeRank(url)
    -        }
    -    }
    -}
    -
    - -The [random](../../reference/ranking/rank-features.html#random) global feature -will be useful in the next section when we describe our data collection process. - -After adding the `collect_rank_features` rank-profile to _msmarco.sd_, redeploy the app: - -
    - -
    -$ vespa deploy --wait 300 app
    -
    -
    - - -## Create a training dataset - -The [MS MARCO](https://microsoft.github.io/msmarco/) dataset described in [the previous tutorial](text-search.html) -provides us with more than 300 000 training queries, -each of which is associated with a specific document ID that is relevant to the query. -In this section we want to combine the information contained in the pairs `(query, relevant_id)` -with the information available in the Vespa ranking features -to create a dataset that can be used to train ML models to improve the ranking function of our msmarco text app. - -Before we move on to describe the collection process in detail, -we want to point out that the whole process can be replicated by the following call -to the data collection script `collect_training_data.py` -available in [this tutorial repository](https://github.com/vespa-engine/sample-apps/tree/master/text-search): - -The following routine requires that you have downloaded the full dataset. -
    - -
    -$ ./src/python/collect_training_data.py msmarco collect_rank_features 99
    -
    -
    - -The command above use data contained in the query (msmarco-doctrain-queries.tsv.gz) -and in the relevance (msmarco-doctrain-qrels.tsv.gz) files that are part of the MSMARCO dataset, -and send queries to Vespa using the `collect_rank_features` rank-profile -defined in the previous section in order to request `99` randomly selected documents for each query -in addition to the relevant document associated with the query. -All the data from the request are then parsed and stored in the output folder, -which is chosen to be `data` in this case. - - -### Data collection logic - -Since we want to improve the first-phase ranking function of our application, -our goal here is to create a dataset that will be used to train models that will generalize well -when used in the first-phase ranking of an actual Vespa instance running against possibly unseen queries and documents. -This might be obvious at first but turns out to be easy to neglect when making some data collection decisions. - -The logic behind the `collect_training_data.py` can be summarized by the pseudo-code below: - -
    {% highlight python %}
    -hits = get_relevant_hit(query, rank_profile, relevant_id)
    -if relevant_hit:
    -    hits.extend(get_random_hits(query, rank_profile, number_random_sample))
    -    data = annotate_data(hits, query_id, relevant_id)
    -    append_data(file, data) 
    -{% endhighlight %}
    - -For each query, we first send a request to Vespa to get the relevant document associated with the query. -If the relevant document is matched by the query, Vespa will return it, -and we will expand the number of documents associated with the query by sending a second request to Vespa. -The second request asks Vespa to return a number of random documents -sampled from the set of documents that were matched by the query. -We then parse the hits returned by Vespa and organize the data into a tabular form -containing the rank features and the binary variable indicating if the query-document pair is relevant or not. - -We are only interested in collecting documents that are matched by the query -because those are the documents that would be presented to the first-phase model in a production environment. -This means that we will likely leave some queries that contain information about relevant documents -out of the collected dataset, but it will create a dataset that are closer to our stated goal. -In other words, the dataset we collect is conditional on our match criteria. - - -### Get relevant hit - -The first Vespa request is contained in the function call `get_relevant_hit(query, rank_profile, relevant_id)` -where the `query` parameter contains the desired query string, -`rank_profile` is set to the `collect_rank_features` defined earlier -and `relevant_id` is the document ID that is said to be relevant to that specific query. - -The body of the request is given by: -
    {% highlight python %}
    -body = {
    -    "yql": "select id, rankfeatures from sources * where userQuery()",
    -    "query": query,
    -    "hits": 1,
    -    "recall": "+id:" + str(relevant_id),
    -    "ranking": {"profile": rank_profile, "listFeatures": "true"},
    -}
    -{% endhighlight %}
    - -where the `yql` and `userQuery` parameters instruct Vespa to return the _id_ of the documents -along with the selected rank-features defined in the `collect_rank_features` rank-profile. -The `hits` parameter is set to 1 because we know there are only one relevant id for each query, -so we set Vespa to return only one document in the result set. -The `recall` parameter allow us to specify the exact document _id_ we want to retrieve. - -Note that the parameter `recall` only works if the document is matched by the query, -which is exactly the behavior we want in this case. - -The `recall` syntax to retrieve one document with id equal to 1 is given by `"recall": "+id:1"` -and the syntax to retrieve more than one document, -say documents with ids 1 and 2 is given by `"recall": "+(id:1 id:2)"`. - -If we wanted to retrieve the document even if it did not match the query specification we could -alter the query to use the following query specification: - -
    {% highlight python%}
    -body = {
    -    "yql": "select id, rankfeatures from sources * where true or userQuery()",
    -    "query": query,
    -    "hits": 1,
    -    "recall": "+id:" + str(relevant_id),
    -    "ranking": {"profile": rank_profile, "listFeatures": "true"},
    -}
    -{% endhighlight %}
    - -### Get random hits - -The second Vespa request happens when we want to extend the dataset -by adding randomly selected documents from the matched set. -The request is contained in the function call `get_random_hits(query, rank_profile, number_random_sample)` -where the only new parameter is `number_random_sample`, -which specify how many documents we should sample from the matched set. - -The body of the request is given by -
    {% highlight python %}
    -body = {
    -    "yql": "select id, rankfeatures from sources * where default contains text(@userQuery)",
    -    "userQuery": query,
    -    "hits": number_random_sample,
    -    "ranking": {"profile": collect_features, "listFeatures": "true"},
    -}
    -{% endhighlight %}
    - -where the only changes with respect to the `get_relevant_hit` is that we no longer need to use the `recall` parameter -and that we set the number of hits returned by Vespa to be equal to `number_random_sample`. - -Remember we had configured the second phase to use random scoring: -
    -second-phase {
    -    expression: random
    -}
    -
    - -Using `random` as our second-phase ranking function -ensures that the top documents returned by Vespa are randomly selected -from the set of documents that were matched by the query. - - -### Annotated data - -Once we have both the relevant and the random documents associated with a given query, -we parse the Vespa result and store it in a file with the following format: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    bm25(body)bm25(title)nativeRank(body)nativeRank(title)docidqidrelevant
    25.79207612.1173090.3225670.084239D31295931
    22.1912280.0438990.2471450.017715D316229930
    13.8806250.0980520.2194130.036826D282382730
    - -where the values in the `relevant` column are equal to 1 if document `docid` is relevant to the query `qid` -and zero otherwise. - - -## Data collection sanity check - -In the process of writing this tutorial and creating the data collection logic described above, -we found it useful to develop a data collection sanity-check to help us catch bugs in our process. -There is no unique right answer here, -but our proposal is to use the dataset to train a model using the same features and functional form -used by the baseline you want to improve upon. -If the dataset is well-built and contains useful information about the task you are interested in, -you should be able to get results at least as good as the one obtained by your baseline on a separate test set. - -In our case, the baseline is the ranking function used in [our previous tutorial](text-search.html): -
    -rank-profile bm25 inherits default {
    -    first-phase {
    -        expression: bm25(title) + bm25(body)
    -    }
    -}
    -
    -Therefore, our sanity-check model will be a linear model containing only the two features above, -i.e. `a + b * bm25(title) + c * bm25(body)`, where `a`, `b`and `c` should be learned by using our collected dataset. - -We split our dataset into training and validation sets, -train the linear model and evaluate it on the validation dataset. -We then expect the difference observed in the collected validation set between the model and the baseline -to be similar to the difference observed on a running instance of Vespa when applied to an independent test set. -In addition, we expect that the trained model to do at least as good as the baseline on a test set, -given that the baseline model is contained in the set of possible trained models -and is recovered when `a=0`, `b=1` and `c=1`. - -This is a simple procedure, but it did catch some bugs while we were writing this tutorial. -For example, at one point we forgot to include -
    -first-phase {
    -    expression: random
    -}
    -
    -in the `collect_rank_features` rank-profile leading to a biased dataset -where the negative examples were actually quite relevant to the query. -The trained model did well on the validation set, -but failed miserably on the test set when deployed to Vespa. -This showed us that our dataset probably had a different distribution than what was observed on a running Vespa instance -and led us to investigate and catch the bug. - - -## Beyond pointwise loss functions - -The most straightforward way to train the linear model mentioned in the previous section -would be to use a vanilla logistic regression, -since our target variable `relevant` is binary. -The most commonly used loss function in this case (binary cross-entropy) -is referred to as a pointwise loss function in the LTR literature, -as it does not take the relative order of documents into account. -However, as we described in [the previous tutorial](text-search.html), -the metric that we want to optimize in this case is the Mean Reciprocal Rank (MRR). -The MRR is affected by the relative order of the relevance we assign to the list of documents generated by a query -and not by their absolute magnitudes. -This disconnect between the characteristics of the loss function and the metric of interest -might lead to suboptimal results. - -For ranking search results, it is preferable to use a listwise loss function when training our linear model, -which takes the entire ranked list into consideration when updating the model parameters. -To illustrate this, we trained linear models using the [TF-Ranking framework](https://github.com/tensorflow/ranking). -The framework is built on top of TensorFlow and allow us to specify pointwise, pairwise and listwise loss functions, -among other things. - -The two _rank-profile_'s below are obtained by training the linear model with a pointwise (sigmoid cross-entropy) -and listwise (softmax cross-entropy) loss functions, respectively: -
    -rank-profile pointwise_linear_bm25 inherits default {
    -    first-phase {
    -        expression: 0.22499913 * bm25(title) + 0.07596389 * bm25(body) 
    -    }
    -}
    -
    -rank-profile listwise_linear_bm25 inherits default {
    -    first-phase {
    -        expression: 0.13446581 * bm25(title) + 0.5716889 * bm25(body)
    -    }
    -}
    -
    -It is interesting to see that a pointwise loss function set more weight into the title in relation to the body -while the opposite happens when using the listwise loss function. - -The figure below shows how frequently (over more than 5.000 test queries) -those two ranking functions allocate the relevant document between the 1st and 10th position -of the list of documents returned by Vespa. -Although there is not a huge difference between those models on average, -we can clearly see in the figure below that a model based on a listwise loss function -allocate more documents in the first two positions of the ranked list when compared to the pointwise model: - -
    -Plot of pointwise and listwise BM25 -
    - -Overall, on average, there is not much difference between those models (with respect to MRR), -which was expected given the simplicity of the models described here. -The point was simply to point out the importance of choosing better loss functions when dealing with LTR tasks -and to give a quick start for those who want to give it a shot in their own applications. -We expect the difference in MRR between pointwise and listwise loss functions to increase -as we move on to more complex models. - - -## Next steps -In this tutorial we have looked at using a simple *linear* ranking function. -Vespa integrates with several popular machine learning libraries which can be used for Machine Learned Ranking: - - - [Ranking with XGBoost Models](../../ranking/xgboost) - - [Ranking with LightGBM Models](../../ranking/lightgbm) - - [Ranking with ONNX Models](../../ranking/onnx) - - diff --git a/en/learn/tutorials/text-search-ml.mdx b/en/learn/tutorials/text-search-ml.mdx new file mode 100644 index 0000000000..a7a3592a8c --- /dev/null +++ b/en/learn/tutorials/text-search-ml.mdx @@ -0,0 +1,370 @@ +--- +title: "Improving Text Search through ML" +--- + +At this point, we assume you have read our [Text Search Tutorial](/en/learn/tutorials/text-search) and accomplished the following steps. + +- Created and deployed a basic text search app in Vespa. +- Fed the app with the MS MARCO full document dataset. +- Compared and evaluated two different ranking functions. + +We are now going to show you how to create a dataset that joins relevance information from the MS MARCO dataset with ranking features from Vespa to enable you to train ML models to improve your application. More specifically, you will accomplish the following steps in this tutorial. + +- Learn how to collect rank feature data from Vespa associated with a specific query. +- Create a dataset that can be used to improve your app's ranking function. +- Propose sanity-checks to help you detect bugs in your data collection logic and ensure you have a properly built dataset at the end of the process. +- Illustrate the importance of going beyond pointwise loss functions when dealing with Learning To Rank (LTR) tasks. + +[Vespa Product Ranking](https://github.com/vespa-engine/sample-apps/tree/master/commerce-product-ranking) is a good resource for Learning To Rank using XGBoost and LightGBM, with linked blog posts. + + +## Collect rank feature data from Vespa + +Vespa's [rank feature set](/en/reference/ranking/rank-features) contains a large set of low and high level features. Those features are useful to understand the behavior of your app and to improve your ranking function. + + +### Default rank features + +To access the default set of ranking features, set the query parameter [`ranking.listFeatures`](/en/reference/api/query#ranking.listfeatures) to `true`. For example, below is the body of a post request that in a [query](/en/querying/query-language), selects the `bm25` rank-profile developed in the previous tutorial and returns the rank features associated with each of the results returned. + +```bash +$ vespa query \ + 'yql=select id,rankfeatures from msmarco where userQuery()' \ + 'query=what is dad bod' \ + 'ranking=bm25' \ + 'type=weakAnd' \ + 'ranking.listFeatures=true' +``` + +The list of rank features that are returned by default can change in the future - the current list can be checked in the [system test](https://github.com/vespa-engine/system-test/blob/master/tests/search/rankfeatures/dump.txt). For the request specified by the body above we get the following (edited) json back. Each result will contain a field called `rankfeatures` containing the set of default ranking features: + +```json expandable +{ + "root": { + "children": [ + ... + { + "fields": { + "rankfeatures": { + ... + "attributeMatch(id).totalWeight": 0.0, + "attributeMatch(id).weight": 0.0, + "elementCompleteness(body).completeness": 0.5051413881748072, + "elementCompleteness(body).elementWeight": 1.0, + "elementCompleteness(body).fieldCompleteness": 0.010282776349614395, + "elementCompleteness(body).queryCompleteness": 1.0, + "elementCompleteness(title).completeness": 0.75, + "elementCompleteness(title).elementWeight": 1.0, + "elementCompleteness(title).fieldCompleteness": 1.0, + "elementCompleteness(title).queryCompleteness": 0.5, + "fieldMatch(body)": 0.7529285549778888, + "fieldMatch(body).absoluteOccurrence": 0.065, + ... + } + }, + "id": "index:msmarco/0/811ccbaf9796f92bfa343045", + "relevance": 37.7705101001455, + "source": "msmarco" + }, + ], + ... + } +} +``` + + +### Chose and process specific rank features + +If instead of returning the complete set of rank features you want to select [specific ones](/en/reference/ranking/rank-features), you can add a new rank-profile (let's call it `collect_rank_features`) to our *msmarco.sd* schema definition and disable the default ranking features by adding `ignore-default-rank-features` to the new rank-profile. In addition, we can specify the desired features within the `rank-features` element. In the example below we explicitly configured Vespa to only return `bm25(title)`, `bm25(body)`, `nativeRank(title)` and `nativeRank(body)`. + +Note that using *all* available rank features comes with computational cost, as Vespa needs to calculate all these features. Using many features is usually only advisable using second phase ranking, see [phased ranking with Vespa](/en/ranking/phased-ranking). + +```sd expandable +schema msmarco { + document msmarco { + field id type string { + indexing: attribute | summary + } + field title type string { + indexing: index | summary + index: enable-bm25 + } + field url type string { + indexing: index | summary + } + field body type string { + indexing: index + index: enable-bm25 + } + } + + document-summary minimal { + summary id { } + } + + fieldset default { + fields: title, body, url + } + + rank-profile default { + first-phase { + expression: nativeRank(title, body, url) + } + } + + rank-profile bm25 inherits default { + first-phase { + expression: bm25(title) + bm25(body) + bm25(url) + } + } + + rank-profile collect_rank_features inherits default { + first-phase { + expression: bm25(title) + bm25(body) + bm25(url) + } + second-phase { + expression: random + } + + match-features { + bm25(title) + bm25(body) + bm25(url) + nativeRank(title) + nativeRank(body) + nativeRank(url) + } + } +} +``` + + +``` +Paste the above into file text-search/app/schemas/msmarco.sd +``` + +The [random](/en/reference/ranking/rank-features#random) global feature will be useful in the next section when we describe our data collection process. + +After adding the `collect_rank_features` rank-profile to *msmarco.sd*, redeploy the app: + +```bash +$ vespa deploy --wait 300 app +``` + + +## Create a training dataset + +The [MS MARCO](https://microsoft.github.io/msmarco/) dataset described in [the previous tutorial](/en/learn/tutorials/text-search) provides us with more than 300 000 training queries, each of which is associated with a specific document ID that is relevant to the query. In this section we want to combine the information contained in the pairs `(query, relevant_id)` with the information available in the Vespa ranking features to create a dataset that can be used to train ML models to improve the ranking function of our msmarco text app. + +Before we move on to describe the collection process in detail, we want to point out that the whole process can be replicated by the following call to the data collection script `collect_training_data.py` available in [this tutorial repository](https://github.com/vespa-engine/sample-apps/tree/master/text-search): + +The following routine requires that you have downloaded the full dataset. + +```bash +$ ./src/python/collect_training_data.py msmarco collect_rank_features 99 +``` + +The command above use data contained in the query (msmarco-doctrain-queries.tsv.gz) and in the relevance (msmarco-doctrain-qrels.tsv.gz) files that are part of the MSMARCO dataset, and send queries to Vespa using the `collect_rank_features` rank-profile defined in the previous section in order to request `99` randomly selected documents for each query in addition to the relevant document associated with the query. All the data from the request are then parsed and stored in the output folder, which is chosen to be `data` in this case. + + +### Data collection logic + +Since we want to improve the first-phase ranking function of our application, our goal here is to create a dataset that will be used to train models that will generalize well when used in the first-phase ranking of an actual Vespa instance running against possibly unseen queries and documents. This might be obvious at first but turns out to be easy to neglect when making some data collection decisions. + +The logic behind the `collect_training_data.py` can be summarized by the pseudo-code below: + +```python +hits = get_relevant_hit(query, rank_profile, relevant_id) +if relevant_hit: + hits.extend(get_random_hits(query, rank_profile, number_random_sample)) + data = annotate_data(hits, query_id, relevant_id) + append_data(file, data) +``` + +For each query, we first send a request to Vespa to get the relevant document associated with the query. If the relevant document is matched by the query, Vespa will return it, and we will expand the number of documents associated with the query by sending a second request to Vespa. The second request asks Vespa to return a number of random documents sampled from the set of documents that were matched by the query. We then parse the hits returned by Vespa and organize the data into a tabular form containing the rank features and the binary variable indicating if the query-document pair is relevant or not. + +We are only interested in collecting documents that are matched by the query because those are the documents that would be presented to the first-phase model in a production environment. This means that we will likely leave some queries that contain information about relevant documents out of the collected dataset, but it will create a dataset that are closer to our stated goal. In other words, the dataset we collect is conditional on our match criteria. + + +### Get relevant hit + +The first Vespa request is contained in the function call `get_relevant_hit(query, rank_profile, relevant_id)` where the `query` parameter contains the desired query string, `rank_profile` is set to the `collect_rank_features` defined earlier and `relevant_id` is the document ID that is said to be relevant to that specific query. + +The body of the request is given by: + +```python +body = { + "yql": "select id, rankfeatures from sources * where userQuery()", + "query": query, + "hits": 1, + "recall": "+id:" + str(relevant_id), + "ranking": {"profile": rank_profile, "listFeatures": "true"}, +} +``` + +where the `yql` and `userQuery` parameters instruct Vespa to return the *id* of the documents along with the selected rank-features defined in the `collect_rank_features` rank-profile. The `hits` parameter is set to 1 because we know there are only one relevant id for each query, so we set Vespa to return only one document in the result set. The `recall` parameter allow us to specify the exact document *id* we want to retrieve. + +Note that the parameter `recall` only works if the document is matched by the query, which is exactly the behavior we want in this case. + +The `recall` syntax to retrieve one document with id equal to 1 is given by `"recall": "+id:1"` and the syntax to retrieve more than one document, say documents with ids 1 and 2 is given by `"recall": "+(id:1 id:2)"`. + +If we wanted to retrieve the document even if it did not match the query specification we could alter the query to use the following query specification: + +```python +body = { + "yql": "select id, rankfeatures from sources * where true or userQuery()", + "query": query, + "hits": 1, + "recall": "+id:" + str(relevant_id), + "ranking": {"profile": rank_profile, "listFeatures": "true"}, +} +``` + + +### Get random hits + +The second Vespa request happens when we want to extend the dataset by adding randomly selected documents from the matched set. The request is contained in the function call `get_random_hits(query, rank_profile, number_random_sample)` where the only new parameter is `number_random_sample`, which specify how many documents we should sample from the matched set. + +The body of the request is given by: + +```python +body = { + "yql": "select id, rankfeatures from sources * where default contains text(@userQuery)", + "userQuery": query, + "hits": number_random_sample, + "ranking": {"profile": collect_features, "listFeatures": "true"}, +} +``` + +where the only changes with respect to the `get_relevant_hit` is that we no longer need to use the `recall` parameter and that we set the number of hits returned by Vespa to be equal to `number_random_sample`. + +Remember we had configured the second phase to use random scoring: + +```sd +second-phase { + expression: random +} +``` + +Using `random` as our second-phase ranking function ensures that the top documents returned by Vespa are randomly selected from the set of documents that were matched by the query. + + +### Annotated data + +Once we have both the relevant and the random documents associated with a given query, we parse the Vespa result and store it in a file with the following format: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    bm25(body)bm25(title)nativeRank(body)nativeRank(title)docidqidrelevant
    25.79207612.1173090.3225670.084239D31295931
    22.1912280.0438990.2471450.017715D316229930
    13.8806250.0980520.2194130.036826D282382730
    + +where the values in the `relevant` column are equal to 1 if document `docid` is relevant to the query `qid` and zero otherwise. + + +## Data collection sanity check + +In the process of writing this tutorial and creating the data collection logic described above, we found it useful to develop a data collection sanity-check to help us catch bugs in our process. There is no unique right answer here, but our proposal is to use the dataset to train a model using the same features and functional form used by the baseline you want to improve upon. If the dataset is well-built and contains useful information about the task you are interested in, you should be able to get results at least as good as the one obtained by your baseline on a separate test set. + +In our case, the baseline is the ranking function used in [our previous tutorial](/en/learn/tutorials/text-search): + +```sd +rank-profile bm25 inherits default { + first-phase { + expression: bm25(title) + bm25(body) + } +} +``` + +Therefore, our sanity-check model will be a linear model containing only the two features above, i.e. `a + b * bm25(title) + c * bm25(body)`, where `a`, `b` and `c` should be learned by using our collected dataset. + +We split our dataset into training and validation sets, train the linear model and evaluate it on the validation dataset. We then expect the difference observed in the collected validation set between the model and the baseline to be similar to the difference observed on a running instance of Vespa when applied to an independent test set. In addition, we expect that the trained model to do at least as good as the baseline on a test set, given that the baseline model is contained in the set of possible trained models and is recovered when `a=0`, `b=1` and `c=1`. + +This is a simple procedure, but it did catch some bugs while we were writing this tutorial. For example, at one point we forgot to include + +```sd +first-phase { + expression: random +} +``` + +in the `collect_rank_features` rank-profile leading to a biased dataset where the negative examples were actually quite relevant to the query. The trained model did well on the validation set, but failed miserably on the test set when deployed to Vespa. This showed us that our dataset probably had a different distribution than what was observed on a running Vespa instance and led us to investigate and catch the bug. + + +## Beyond pointwise loss functions + +The most straightforward way to train the linear model mentioned in the previous section would be to use a vanilla logistic regression, since our target variable `relevant` is binary. The most commonly used loss function in this case (binary cross-entropy) is referred to as a pointwise loss function in the LTR literature, as it does not take the relative order of documents into account. However, as we described in [the previous tutorial](/en/learn/tutorials/text-search), the metric that we want to optimize in this case is the Mean Reciprocal Rank (MRR). The MRR is affected by the relative order of the relevance we assign to the list of documents generated by a query and not by their absolute magnitudes. This disconnect between the characteristics of the loss function and the metric of interest might lead to suboptimal results. + +For ranking search results, it is preferable to use a listwise loss function when training our linear model, which takes the entire ranked list into consideration when updating the model parameters. To illustrate this, we trained linear models using the [TF-Ranking framework](https://github.com/tensorflow/ranking). The framework is built on top of TensorFlow and allow us to specify pointwise, pairwise and listwise loss functions, among other things. + +The two *rank-profile*'s below are obtained by training the linear model with a pointwise (sigmoid cross-entropy) and listwise (softmax cross-entropy) loss functions, respectively: + +```sd +rank-profile pointwise_linear_bm25 inherits default { + first-phase { + expression: 0.22499913 * bm25(title) + 0.07596389 * bm25(body) + } +} + +rank-profile listwise_linear_bm25 inherits default { + first-phase { + expression: 0.13446581 * bm25(title) + 0.5716889 * bm25(body) + } +} +``` + +It is interesting to see that a pointwise loss function set more weight into the title in relation to the body while the opposite happens when using the listwise loss function. + +The figure below shows how frequently (over more than 5.000 test queries) those two ranking functions allocate the relevant document between the 1st and 10th position of the list of documents returned by Vespa. Although there is not a huge difference between those models on average, we can clearly see in the figure below that a model based on a listwise loss function allocate more documents in the first two positions of the ranked list when compared to the pointwise model: + + +![Plot of pointwise and listwise BM25](/assets/img/tutorials/text_search_baseline_pointwise_listwise_rr.png) + + +Overall, on average, there is not much difference between those models (with respect to MRR), which was expected given the simplicity of the models described here. The point was simply to point out the importance of choosing better loss functions when dealing with LTR tasks and to give a quick start for those who want to give it a shot in their own applications. We expect the difference in MRR between pointwise and listwise loss functions to increase as we move on to more complex models. + + +## Next steps + +In this tutorial we have looked at using a simple *linear* ranking function. Vespa integrates with several popular machine learning libraries which can be used for Machine Learned Ranking: + +- [Ranking with XGBoost Models](/en/ranking/xgboost) +- [Ranking with LightGBM Models](/en/ranking/lightgbm) +- [Ranking with ONNX Models](/en/ranking/onnx) diff --git a/en/learn/tutorials/text-search.md b/en/learn/tutorials/text-search.md deleted file mode 100644 index e00caeddf5..0000000000 --- a/en/learn/tutorials/text-search.md +++ /dev/null @@ -1,814 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Text Search Tutorial" -redirect_from: - - /en/tutorials/text-search ---- - -This tutorial will guide you through setting up a simple text search application. -At the end, you can index text documents in Vespa and search them via text queries. -The application built here will be the foundation for other tutorials, -such as creating ranking functions based on Machine Learning (ML) models. - -The main goal is to set up a text search app based on simple text scoring features -such as [BM25](../../ranking/bm25.html) [^1] and [nativeRank](../../reference/ranking/nativerank.html). - - -{% include pre-req.html memory="4 GB" extra-reqs=' -
  • Python3
  • -
  • curl
  • ' %} - -## Installing vespa-cli - -This tutorial uses [Vespa-CLI](../../clients/vespa-cli.html) to deploy, feed and query Vespa. Below, we use [HomeBrew](https://brew.sh/) to -download and install `vespa-cli`, you can also -download a binary from [GitHub](https://github.com/vespa-engine/vespa/releases) for your OS/CPU architecture. - -
    - -
    -$ brew install vespa-cli 
    -
    -
    - -We acquire the scripts to follow this tutorial from the -[sample-apps repository](https://github.com/vespa-engine/sample-apps/tree/master/text-search) via -`vespa clone`. - -
    - -
    -$ vespa clone text-search text-search && cd text-search
    -
    -
    - -The repository contains a fully-fledged Vespa application, but below, we will build -it all from scratch for educational purposes. - -## Dataset - -We use a dataset called [MS MARCO](https://microsoft.github.io/msmarco/) throughout this tutorial. -MS MARCO is a collection of large-scale datasets released by Microsoft -with the intent of helping the advance of deep learning research related to search. -Many tasks are associated with MS MARCO datasets, -but we want to build an end-to-end search application that returns relevant documents to a text query. -We have included a small dataset sample for this tutorial under the `ext/sample` directory, which contains around 1000 documents. - -The sample data must be converted to Vespa [JSON feed format](../../reference/schemas/document-json-format.html). -The following step includes extracting documents, queries and relevance judgments from the sample files: - -
    - -
    -$ ./scripts/convert-msmarco.sh
    -
    -
    - - -After running the script, we end up with a file `dataset/documents.jsonl` containing lines such as the one below: - -
    {% highlight json%}
    -{
    -    "put": "id:msmarco:msmarco::D1555982",
    -    "fields": {
    -        "id": "D1555982",
    -        "url": "https://answers.yahoo.com/question/index?qid=20071007114826AAwCFvR",
    -        "title": "The hot glowing surfaces of stars emit energy in the form of electromagnetic radiation",
    -        "body": "Science   Mathematics Physics The hot glowing surfaces of stars emit energy in the form of electromagnetic radiation ... "
    -    }
    -}{% endhighlight %}
    - -In addition to `vespa.json` we also have a `test-queries.tsv` file containing a list of the sampled queries -along with the document ID relevant to each particular query. - - -## Create a Vespa Application Package - -A [Vespa application package](../../application-packages) is a set of configuration files and optional Java components -that together define the behavior of a Vespa system. Let us define the minimum set of required files to create our -basic text search application, -: `msmarco.sd` and `services.xml`. - -For this tutorial, we will create a new Vespa application rather than using the one in the repository, -so we will create a directory for this application: - -
    - -
    -$ mkdir -p app/schemas
    -
    -
    - - -### Schema - -A [schema](../../basics/schemas.html) is a document-type configuration; a single vespa application can have multiple schemas with document types. -For this application, we define a schema `msmarco` which must be saved in a file named `schemas/msmarco.sd`. -Write the following to `text-search/app/schemas/msmarco.sd`: - -
    -schema msmarco {
    -    document msmarco {
    -        field language type string {
    -            indexing: "en" | set_language 
    -        }
    -        field id type string {
    -            indexing: attribute | summary
    -            match: word
    -        }
    -        field title type string {
    -            indexing: index | summary
    -            match: text
    -            index: enable-bm25
    -        }
    -        field body type string {
    -            indexing: index | summary
    -            match: text
    -            index: enable-bm25
    -        }
    -        field url type string {
    -            indexing: index | summary
    -            index: enable-bm25
    -        }
    -    }
    -    fieldset default {
    -        fields: title, body, url
    -    }
    -    document-summary minimal {
    -        summary id {  }
    -    }
    -    document-summary debug-tokens {
    -        summary url {}
    -        summary url-tokens {
    -            source: url
    -            tokens
    -        }
    -        from-disk
    -    }
    -    rank-profile default {
    -        first-phase {
    -            expression: nativeRank(title, body, url)
    -        }
    -    }
    -    rank-profile bm25 inherits default {
    -        first-phase {
    -            expression: bm25(title) + bm25(body) + bm25(url)
    -        }
    -    }
    -}
    -
    -A lot is going on here; let us go through it in detail. - -#### Document type and fields - -The `document` section contains the fields of the document, their types, -and how Vespa should index and [match](../../reference/schemas/schemas.html#match) them. - -The field property `indexing` configures the _indexing pipeline_ for a field. -For more information, see [schemas - indexing](../../basics/schemas.html#document-fields). -The [string](../../reference/schemas/schemas.html#string) data type is used to represent both unstructured and structured texts, -and there are significant differences between [index and attribute](../../querying/text-matching.html#index-and-attribute). The above -schema includes default `match` modes for `attribute` and `index` property for visibility. - -Note that we are enabling the usage of [BM25](../../ranking/bm25.html) for `title`, `body` and `url`. -by including `index: enable-bm25`. The language field is the only field not in the msmarco dataset. We hardcode its value -to "en" since the dataset is English. Using `set_language` avoids automatic language detection and uses the value when processing the other -text fields. Read more in [linguistics](../../linguistics/linguistics.html). - -#### Fieldset for matching across multiple fields - -[Fieldset](../../reference/schemas/schemas.html#fieldset) allows searching across multiple fields. Defining `fieldset` does not -add indexing/storage overhead. String fields grouped using fieldsets must share the same -[match](../../reference/schemas/schemas.html#match) and [linguistic processing](../../linguistics/linguistics.html) settings because -the query processing that searches a field or fieldset uses *one* type of transformation. - -#### Document summaries to control search response contents - -Next, we define two [document summaries](../../querying/document-summaries.html). -Document summaries control what fields are available in the [response](../../reference/querying/default-result-format.html); -we include the `debug-tokens` document-summary to -demonstrate later how we can get visibility into how text is converted into searchable tokens. - -#### Ranking to determine matched documents ordering - -You can define many [rank profiles](../../basics/ranking.html), -named collections of score calculations, and ranking phases. - -In this tutorial, we define our `default` to be using [nativeRank](../../reference/ranking/nativerank.html). -In addition, we have a `bm25` rank-profile that uses [bm25](../../ranking/bm25.html). Both are examples of -text-scoring [rank-features](../../reference/ranking/rank-features.html) in Vespa. - -### Services Specification - -The [services.xml](../../reference/applications/services/services.html) defines the services that make up -the Vespa application — which services to run and how many nodes per service. -Write the following to `text-search/app/services.xml`: - -
    -<?xml version="1.0" encoding="UTF-8"?>
    -<services version="1.0">
    -
    -    <container id="text_search" version="1.0">
    -        <search />
    -        <document-processing />
    -        <document-api />
    -    </container>
    -
    -    <content id="msmarco" version="1.0">
    -        <min-redundancy>1</min-redundancy>
    -        <documents>
    -            <document type="msmarco" mode="index" />
    -            <document-processing cluster="text_search" />
    -        </documents>
    -        <nodes>
    -            <node distribution-key="0" hostalias="node1" />
    -        </nodes>
    -    </content>
    -</services>
    -
    - -Some notes about the elements above: - -- `` defines the [container cluster](../../applications/containers.html) for document, query and result processing -- `` sets up the [query endpoint](../../querying/query-api.html). The default port is 8080. -- `` sets up the [document endpoint](../../reference/api/document-v1.html) for feeding. -- `` defines how documents are stored and searched -- `` denotes how many copies to keep of each document. -- `` assigns the document types in the _schema_ to content clusters — - the content cluster capacity can be increased by adding node elements — - see [elasticity](../../content/elasticity.html). - (See also the [reference](../../reference/applications/services/content.html) for more on content cluster setup.) -- `` defines the hosts for the content cluster. - -## Deploy the application package - -Once we have finished writing our application package, we can deploy it. -We use settings similar to those in the [Vespa quick start guide](../../basics/deploy-an-application-local.html). - -Start the Vespa container: - -
    - -
    -$ docker run --detach --name vespa-msmarco --hostname vespa-msmarco \
    -  --publish 8080:8080 --publish 19071:19071 \
    -  vespaengine/vespa
    -
    -
    -Notice that we publish two ports (:8080) is the data-plane port where we write and query documents, and 19071 is -the control-plane where we can deploy the application. - -Configure the Vespa CLI to use the local container: -
    - -
    -$ vespa config set target local
    -
    -
    - -Starting the container can take a short while. Make sure -that the configuration service is running by using `vespa status`. - -
    - -
    -$ vespa status deploy --wait 300 
    -
    -
    - -Now, deploy the Vespa application from the `app` directory: - -
    - -
    -$ vespa deploy --wait 300 app
    -
    -
    - - -## Feed the data - -The data fed to Vespa must match the document type in the schema. -The file `vespa.json` generated by the `convert-msmarco.sh` script described in the [dataset section](#dataset) -already has data in the appropriate format expected by Vespa: - -
    - -
    -$ vespa feed -t http://localhost:8080 dataset/documents.jsonl
    -
    -
    - - -## Querying the data - -This section demonstrates various ways to search the data using the [Vespa query language](../../querying/query-language.html). All -the examples use the `vespa-cli` client, the tool uses the HTTP api and if you pass `-v`, you will see the `curl` equivalent -API request. - -
    - -
    -$ vespa query \
    -  'yql=select * from msmarco where default contains text(@user-query)' \
    -  'user-query=what is dad bod' \
    -  'hits=3' \
    -  'language=en'
    -
    -
    - -This query combines YQL [text()](../../reference/querying/yql.html#text), a robust -way to combine free text from users or models with application logic. Similar to `set_language` in indexing, we specify -the language of the query using the [language](../../linguistics/linguistics.html#querying-with-language) API parameter. This ensures -symmetric linguistic processing of both the query and the document text. Automatic language detection is inaccurate -for short query strings and might lead to asymmetric processing of queries and document texts. - -Following is a partial output of the query above when using the small dataset sample: -
    {% highlight json%}
    -{
    -    "root": {
    -        "id": "toplevel",
    -        "relevance": 1,
    -        "fields": {
    -            "totalCount": 562
    -        },
    -        "children":[
    -            {
    -                "id": "id:msmarco:msmarco::D2977840",
    -                "relevance": 0.20676669550322158,
    -                "source": "msmarco",
    -                "fields": {
    -                    "sddocname": "msmarco",
    -                    "body": "After The Cut released a piece explaining what the  dad bod  is last week  the internet pretty much exploded into debate over the trend  ",
    -                    "documentid": "id:msmarco:msmarco::D2977840",
    -                    "id": "D2977840",
    -                    "title": "What Is A  Dad Bod   An Insight Into The Latest Male Body Craze To Sweep The Internet",
    -                    "url": "http://www.huffingtonpost.co.uk/2015/05/05/what-is-a-dadbod-male-body_n_7212072.html"
    -                }
    -            }
    -        ]
    -
    -    }
    -}
    -{% endhighlight %}
    -As shown, 562 documents matched the query out of 996 in the corpus. The `first-phase` ranking expression scores all the matching documents. - -A few important observations: - -- We did not specify which fields to search in the query. Vespa will, by default, use a field set or field named `default` when the query terms do not specify a field. In our case: - -
    -fieldset default {
    -  fields: title, body, url
    -}
    -
    -- Our query for `what is dad bod` searches across all those three fields. -- If we did not specify a `default` fieldset in the schema, the above query would return zero hits as the query did not specify a field. -- The hit `relevance` holds the score computed by the rank profile. Vespa uses `default` by default. -In our case: - -
    -rank-profile default {
    -    first-phase {
    -        expression: nativeRank(title, body, url)
    -    }
    -}
    -
    - -We can use query operator annotations for the [text](../../reference/querying/yql.html#text) operator to control various -matching aspects, for example to set the number of hits to produce in the text operator: - -
    - -
    -$ vespa query \
    -  'yql=select * from msmarco where title contains ({targetHits:100}text(@user-query))' \
    -  'user-query=what is dad bod' \
    -  'hits=3' \
    -  'language=en'
    -
    -
    -Notice how the query above matches fewer documents `totalCount:116` because we limited the free text query to the title field. We can -change the [grammar](../../reference/querying/yql.html#grammar) to specify how the user query text is parsed into a query execution plan. -In the following example, we use `grammar:"all"` to specify that we only want to retrieve documents where *all* the query terms match the title field. - -
    - -
    -$ vespa query \
    -  'yql=select * from msmarco where title contains ({grammar:"all"}text(@user-query))' \
    -  'user-query=what is dad bod' \
    -  'hits=3' \
    -  'language=en'
    -
    -
    -This query, using `all`, matches only one document. Notice how the relevance of the hit is the same as in the above example. The difference -between the two types of queries is in the matching specification. - -We can use `text` to build a query that searches multiple fields (or fieldsets): - -
    - -
    -$ vespa query \
    -  'yql=select * from msmarco where title contains ({grammar:"all"}text(@user-query)) or url contains ({grammar:"all"}text(@user-query))' \
    -  'user-query=what is dad bod' \
    -  'hits=3' \
    -  'language=en'
    -
    -
    - -### Boosting by query terms -Sometimes, we want to add a query time boost if some field matches a query term; the following uses the [rank](../../reference/querying/yql.html#rank) query operator. -The rank query operator allows us to retrieve using the first operand, and the remaining operands can only impact ranking. - -It is important to note that the following approach for query time term boosting is in the context of using the `nativeRank` text scoring feature. - -
    - -
    -$ vespa query \
    -  'yql=select * from msmarco where rank(default contains text(@user-query), url contains ({weight:1000, significance:1.0}"www.answers.com"))' \
    -  'user-query=what is dad bod' \
    -  'hits=3' \
    -  'language=en'
    -
    -
    -The above will match the user query against the default fieldset and produce match features for the second operand. It does not -change the *retrieval* or *matching* as the number of documents exposed to ranking is the same as before. The -`rank` operator can be used to implement a variety of use case around boosting. - -#### Combine free text with filters -We can combine the `text` operator with application logic. We add an application-specific query filter on the `url` field -to demonstrate how to combine `text` with other query time constraints. -We add `ranked:false` to tell Vespa that this -specific term should not contribute to the relevance calculation and `filter`:true` to ensure that the term is not -used for [bolding/highlighting or dynamic snippeting](../../querying/document-summaries.html#dynamic-snippets). - -
    - -
    -$ vespa query \
    -  'yql=select * from msmarco where default contains text(@user-query) and url contains ({filter:true,ranked:false}"huffingtonpost.co.uk")' \
    -  'user-query=what is dad bod' \
    -  'hits=3' \
    -  'language=en'
    -
    -
    - -Notice that the `relevance` stays the same since we used `ranked:false` for the filter. -Let us see what is going on by adding [query tracing](../../querying/query-api.html#query-tracing): - -
    - -
    -$ vespa query \
    -  'yql=select * from msmarco where default contains text(@user-query) and url contains ({filter:true,ranked:false}"huffingtonpost.co.uk")' \
    -  'user-query=what is dad bod' \
    -  'trace.level=3' \
    -  'language=en'
    -
    -
    - -We can notice the following in the trace output: -
    -query=[AND (WEAKAND(100) default:what default:is default:dad default:bod) |url:'huffingtonpost co uk']
    -
    - -Notice that the `text` part is converted to a [weakAnd](../../ranking/wand.html) query operator and that this operator is -AND'ed with a phrase search ('huffingtonpost co uk') in the `url` field. Notice also the field scoping where the query terms are -prefixed with `default`. Notice also that punctuation characters (.) are removed as part of the tokenization. -Suppose this is a common pattern where we want to filter on specific strings. -In that case, we should create a separate field to avoid phrase matching, phrase matching is more expensive than a single token search. - - -### Supporting end user query syntax -In some applications, you'd like end users or models to be able to search specific fields, match phrases etc. -To do that, you can use the `userInput` YQL operator instead of `text`: - -
    - -
    -$ vespa query \
    -  'yql=select * from msmarco where userInput(@user-query)' \
    -  'user-query=title:"dad bod"' \
    -  'hits=3' \
    -  'language=en'
    -
    -
    - -Notice that since the string given to userInput may specify the fields to search, there is no "field contains" part -that specifies the field on the YQL side. See the [userInput() documentation](../../reference/querying/yql.html#userinput) -on the various end user query languages supported and other parameters that can be set. - - - -### Debugging token string matching -Query tracing, combined with a summary using [tokens](../../reference/schemas/schemas.html#tokens) can help debug matching. -
    - -
    -$ vespa query \
    -  'yql=select * from msmarco where url contains ({filter:true,ranked:false}"huffingtonpost.co.uk")' \
    -  'trace.level=0' \
    -  'language=en' \
    -  'summary=debug-tokens'
    -
    -
    - -
    {% highlight json%}
    -{
    -    "root": {
    -        "id": "toplevel",
    -        "relevance": 1,
    -        "fields": {
    -            "totalCount": 562
    -        },
    -        "children": [
    -            {
    -                "id": "index:msmarco/0/59444ddd06537a24953b73e6",
    -                "relevance": 0.0,
    -                "source": "msmarco",
    -                "fields": {
    -                    "sddocname": "msmarco",
    -                    "url": "http://www.huffingtonpost.co.uk/2015/05/05/what-is-a-dadbod-male-body_n_7212072.html",
    -                    "url-tokens": [
    -                        "http",
    -                        "www",
    -                        "huffingtonpost",
    -                        "co",
    -                        "uk",
    -                        "2015",
    -                        "05",
    -                        "05",
    -                        "what",
    -                        "is",
    -                        "a",
    -                        "dadbod",
    -                        "male",
    -                        "body",
    -                        "n",
    -                        "7212072",
    -                        "html"
    -                    ]
    -                }
    -            }
    -        ]
    -
    -    }
    -}
    -{% endhighlight %}
    -This gives us insight into how the input `url` field was tokenized and indexed. Those are the tokens that the query can match. -Notice how punctuation characters like `:`, `,`, `.`, `/`, `_` and `-` are removed as part of the text tokenization. - -Observations: - -- Relevance is 0.0, because the term uses `ranked:false`. -- We cannot match "://" because those are not searchable characters with `match:text` -- `dadbod` is a token in the url, this cannot match `dad` or `bod` as it is represented as a single token `dadbod`. - -Let us do a similar example to demonstrate the impact of linguistic stemming - -
    - -
    -$ vespa query \
    -  'yql=select * from msmarco where url contains ({filter:true,ranked:false}"http")' \
    -  'summary=debug-tokens' \
    -  'language=en'
    -
    -
    - - -
    - "url": "http://www.ourbabynamer.com/meaning-of-Anika.html",
    -  "url-tokens": [
    -    "http",
    -    "www",
    -    "ourbabynamer",
    -    "com",
    -    "meaning",
    -    "of",
    -    "anika",
    -    "html"
    -  ]
    -
    -Notice that a query for `https` matches `http`, because 'https' on the query is stemmed to `http`. -If we `turn off` stemming on` the query side, searching for `https` directly, we -end up with 0 results. - -
    - -
    -$ vespa query \
    -  'yql=select * from msmarco where url contains ({filter:true,ranked:false,stem:false}"https")' \
    -  'summary=debug-tokens' \
    -  'language=en'
    -
    -
    - -Similarly, if we pass a different language tag, which will not stem https to http, we also get 0 results: -
    - -
    -$ vespa query \
    -  'yql=select * from msmarco where url contains ({filter:true,ranked:false}"https")' \
    -  'summary=debug-tokens' \
    -  'language=de'
    -
    -
    - -## Ranking - The previous section covered free-text search matching, linguistics, and how to combine business logic with - free-text user queries. All the examples used a `default` rank-profile using Vespa's [nativeRank](../../ranking/nativerank.html) text scoring feature. - - With free-text search, we can use other text scoring functions, like [BM25](../../ranking/bm25.html). All the matching - capabilities (or limitations) still apply, we can use fieldsets or fields; the difference is in the text scoring function where BM25 - is different from nativeRank. - -
    - -
    -$ vespa query \
    -  'yql=select * from msmarco where default contains text(@user-query)' \
    -  'user-query=what is dad bod' \
    -  'hits=3' \
    -  'language=en' \
    -  'ranking=bm25'
    -
    -
    - -While the `nativeRank` text score is normalized to the range 0 to 1, BM25 is unbounded, as demonstrated above. When -querying (matching), we can ask Vespa to compute both features in the same query. - -Modify the schema and add a new rank-profile `combined`: - -
    -schema msmarco {
    -    document msmarco {
    -        field language type string {
    -            indexing: "en" | set_language 
    -        }
    -        field id type string {
    -            indexing: attribute | summary
    -            match: word
    -        }
    -        field title type string {
    -            indexing: index | summary
    -            match: text
    -            index: enable-bm25
    -        }
    -        field body type string {
    -            indexing: index | summary
    -            match: text
    -            index: enable-bm25
    -        }
    -        field url type string {
    -            indexing: index | summary
    -            index: enable-bm25
    -        }
    -    }
    -    fieldset default {
    -        fields: title, body, url
    -    }
    -    document-summary minimal {
    -        summary id {  }
    -    }
    -    document-summary debug-tokens {
    -        summary url {}
    -        summary url-tokens {
    -            source: url
    -            tokens
    -        }
    -        from-disk
    -    }
    -    rank-profile default {
    -        first-phase {
    -            expression: nativeRank(title, body, url)
    -        }
    -    }
    -    rank-profile bm25 inherits default {
    -        first-phase {
    -            expression: bm25(title) + bm25(body) + bm25(url)
    -        }
    -    }
    -
    -    rank-profile combined inherits default {
    -        first-phase {
    -            expression: bm25(title) + bm25(body) + bm25(url) + nativeRank(title) + nativeRank(body) + nativeRank(url)
    -        }
    -        match-features { 
    -          bm25(title)
    -          bm25(body)
    -          bm25(url)
    -          nativeRank(title)
    -          nativeRank(body)
    -          nativeRank(url)
    -        }
    -    }
    -}
    -
    - -Then, re-deploy the Vespa application from the `app` directory: - -
    - -
    -$ vespa deploy --wait 300 app
    -
    -
    - -Adding or removing rank profiles is a live-change as it only impacts how we score documents, not how we index or match -them. - -Run a query with the new rank-profile: - -
    - -
    -$ vespa query \
    -  'yql=select * from msmarco where default contains text(@user-query)' \
    -  'user-query=what is dad bod' \
    -  'hits=3' \
    -  'language=en' \
    -  'ranking=combined'
    -
    -
    - -Which will produce a result like this: - -
    {% highlight json%}
    -{
    -    "root": {
    -        "id": "toplevel",
    -        "relevance": 1,
    -        "fields": {
    -            "totalCount": 562
    -        },
    -        "children": [
    -          {
    -                "id": "id:msmarco:msmarco::D2977840",
    -                "relevance": 25.482783473796484,
    -                "source": "msmarco",
    -                "fields": {
    -                    "matchfeatures": {
    -                        "bm25(body)": 19.51565699523739,
    -                        "bm25(title)": 4.978933753876959,
    -                        "bm25(url)": 0.3678926381724701,
    -                        "nativeRank(body)": 0.3010929113058281,
    -                        "nativeRank(title)": 0.24814575272673867,
    -                        "nativeRank(url)": 0.07106142247709807
    -                    },
    -                    "sddocname": "msmarco",
    -                    "documentid": "id:msmarco:msmarco::D2977840",
    -                    "id": "D2977840",
    -                    "title": "What Is A  Dad Bod   An Insight Into The Latest Male Body Craze To Sweep The Internet",
    -                    "url": "http://www.huffingtonpost.co.uk/2015/05/05/what-is-a-dadbod-male-body_n_7212072.html"
    -                }
    -            }
    -        ]
    -
    -    }
    -}
    -{% endhighlight %}
    -Notice that `matchfeatures` field that is added to the hit when using `match-features` in the rank-profile. -Here, we have all the computed features from the matched document, and the final `relevance` score is the sum -of these features (In this case). -This query and ranking example demonstrates that for a single query searching a set of fields via fieldset, -we can compute different types of text scoring features and use combinations. - -Now consider the following where we limit matching to the title field: - -
    - -
    -$ vespa query \
    -  'yql=select * from msmarco where title contains text(@user-query)' \
    -  'user-query=what is dad bod' \
    -  'hits=3' \
    -  'language=en' \
    -  'ranking=combined'
    -
    -
    - -Now, we do not get features for `body` or `url`, because they were not matched by the query. - -## Next steps -Check out the [Improving Text Search through ML](text-search-ml.html). - -## Cleanup -If you do not want to proceed with the [Improving Text Search through ML](text-search-ml.html) guide, you can -stop and remove the container (and data): - -
    - -
    -$ docker rm -f vespa-msmarco
    -
    -
    - - -[^1]: Robertson, Stephen and Zaragoza, Hugo and others, 2009. The probabilistic relevance framework: BM25 and beyond. -Foundations and Trends in Information Retrieval. - - diff --git a/en/learn/tutorials/text-search.mdx b/en/learn/tutorials/text-search.mdx new file mode 100644 index 0000000000..7d71b2a5b6 --- /dev/null +++ b/en/learn/tutorials/text-search.mdx @@ -0,0 +1,692 @@ +--- +title: "Text Search Tutorial" +--- + +This tutorial will guide you through setting up a simple text search application. At the end, you can index text documents in Vespa and search them via text queries. The application built here will be the foundation for other tutorials, such as creating ranking functions based on Machine Learning (ML) models. + +The main goal is to set up a text search app based on simple text scoring features such as [BM25](/en/ranking/bm25) [^1] and [nativeRank](/en/reference/ranking/nativerank). + + +**Prerequisites:** + +- Linux, macOS or Windows 10 Pro on x86_64 or arm64, with [Podman Desktop](https://podman.io/) or [Docker Desktop](https://www.docker.com/products/docker-desktop/) installed, with an engine running. + - Alternatively, start the Podman daemon: + + ```bash + $ podman machine init --memory 6000 + $ podman machine start + ``` + +- See [Docker Containers](/en/operations/self-managed/docker-containers) for system limits and other settings. +- For CPUs older than Haswell (2013), see [CPU Support](/en/operations/self-managed/cpu-support). +- Memory: Minimum 4 GB RAM dedicated to Docker/Podman. [Memory recommendations](/en/operations/self-managed/node-setup#memory-settings). +- Disk: Avoid `NO_SPACE` - the vespaengine/vespa container image + headroom for data requires disk space. [Read more](/en/writing/feed-block). +- [Homebrew](https://brew.sh/) to install the [Vespa CLI](/en/clients/vespa-cli), or download the Vespa CLI from [Github releases](https://github.com/vespa-engine/vespa/releases). +- Python3 +- `curl` + + + +## Installing vespa-cli + +This tutorial uses [Vespa-CLI](/en/clients/vespa-cli) to deploy, feed and query Vespa. Below, we use [HomeBrew](https://brew.sh/) to download and install `vespa-cli`, you can also download a binary from [GitHub](https://github.com/vespa-engine/vespa/releases) for your OS/CPU architecture. + +```bash +$ brew install vespa-cli +``` + +We acquire the scripts to follow this tutorial from the [sample-apps repository](https://github.com/vespa-engine/sample-apps/tree/master/text-search) via `vespa clone`. + +```bash +$ vespa clone text-search text-search && cd text-search +``` + +The repository contains a fully-fledged Vespa application, but below, we will build it all from scratch for educational purposes. + + +## Dataset + +We use a dataset called [MS MARCO](https://microsoft.github.io/msmarco/) throughout this tutorial. MS MARCO is a collection of large-scale datasets released by Microsoft with the intent of helping the advance of deep learning research related to search. Many tasks are associated with MS MARCO datasets, but we want to build an end-to-end search application that returns relevant documents to a text query. We have included a small dataset sample for this tutorial under the `ext/sample` directory, which contains around 1000 documents. + +The sample data must be converted to Vespa [JSON feed format](/en/reference/schemas/document-json-format). The following step includes extracting documents, queries and relevance judgments from the sample files: + +```bash +$ ./scripts/convert-msmarco.sh +``` + +After running the script, we end up with a file `dataset/documents.jsonl` containing lines such as the one below: + +```json +{ + "put": "id:msmarco:msmarco::D1555982", + "fields": { + "id": "D1555982", + "url": "https://answers.yahoo.com/question/index?qid=20071007114826AAwCFvR", + "title": "The hot glowing surfaces of stars emit energy in the form of electromagnetic radiation", + "body": "Science Mathematics Physics The hot glowing surfaces of stars emit energy in the form of electromagnetic radiation ... " + } +} +``` + +In addition to `vespa.json` we also have a `test-queries.tsv` file containing a list of the sampled queries along with the document ID relevant to each particular query. + + +## Create a Vespa Application Package + +A [Vespa application package](/en/basics/applications) is a set of configuration files and optional Java components that together define the behavior of a Vespa system. Let us define the minimum set of required files to create our basic text search application: `msmarco.sd` and `services.xml`. + +For this tutorial, we will create a new Vespa application rather than using the one in the repository, so we will create a directory for this application: + +```bash +$ mkdir -p app/schemas +``` + + +### Schema + +A [schema](/en/basics/schemas) is a document-type configuration; a single vespa application can have multiple schemas with document types. For this application, we define a schema `msmarco` which must be saved in a file named `schemas/msmarco.sd`. Write the following to `text-search/app/schemas/msmarco.sd`: + +```sd expandable +schema msmarco { + document msmarco { + field language type string { + indexing: "en" | set_language + } + field id type string { + indexing: attribute | summary + match: word + } + field title type string { + indexing: index | summary + match: text + index: enable-bm25 + } + field body type string { + indexing: index | summary + match: text + index: enable-bm25 + } + field url type string { + indexing: index | summary + index: enable-bm25 + } + } + fieldset default { + fields: title, body, url + } + document-summary minimal { + summary id { } + } + document-summary debug-tokens { + summary url {} + summary url-tokens { + source: url + tokens + } + from-disk + } + rank-profile default { + first-phase { + expression: nativeRank(title, body, url) + } + } + rank-profile bm25 inherits default { + first-phase { + expression: bm25(title) + bm25(body) + bm25(url) + } + } +} +``` + +A lot is going on here; let us go through it in detail. + + +#### Document type and fields + +The `document` section contains the fields of the document, their types, and how Vespa should index and [match](/en/reference/schemas/schemas#match) them. + +The field property `indexing` configures the *indexing pipeline* for a field. For more information, see [schemas - indexing](/en/basics/schemas#document-fields). The [string](/en/reference/schemas/schemas#string) data type is used to represent both unstructured and structured texts, and there are significant differences between [index and attribute](/en/querying/text-matching#index-and-attribute). The above schema includes default `match` modes for `attribute` and `index` property for visibility. + +Note that we are enabling the usage of [BM25](/en/ranking/bm25) for `title`, `body` and `url`. by including `index: enable-bm25`. The language field is the only field not in the msmarco dataset. We hardcode its value to "en" since the dataset is English. Using `set_language` avoids automatic language detection and uses the value when processing the other text fields. Read more in [linguistics](/en/linguistics/linguistics). + + +#### Fieldset for matching across multiple fields + +[Fieldset](/en/reference/schemas/schemas#fieldset) allows searching across multiple fields. Defining `fieldset` does not add indexing/storage overhead. String fields grouped using fieldsets must share the same [match](/en/reference/schemas/schemas#match) and [linguistic processing](/en/linguistics/linguistics) settings because the query processing that searches a field or fieldset uses *one* type of transformation. + + +#### Document summaries to control search response contents + +Next, we define two [document summaries](/en/querying/document-summaries). Document summaries control what fields are available in the [response](/en/reference/querying/default-result-format); we include the `debug-tokens` document-summary to demonstrate later how we can get visibility into how text is converted into searchable tokens. + + +#### Ranking to determine matched documents ordering + +You can define many [rank profiles](/en/basics/ranking), named collections of score calculations, and ranking phases. + +In this tutorial, we define our `default` to be using [nativeRank](/en/reference/ranking/nativerank). In addition, we have a `bm25` rank-profile that uses [bm25](/en/ranking/bm25). Both are examples of text-scoring [rank-features](/en/reference/ranking/rank-features) in Vespa. + + +### Services Specification + +The [services.xml](/en/reference/applications/services/services) defines the services that make up the Vespa application — which services to run and how many nodes per service. Write the following to `text-search/app/services.xml`: + +```xml expandable + + + + + + + + + + + 1 + + + + + + + + + +``` + +``` +Paste the above into file text-search/app/services.xml +``` + +Some notes about the elements above: + +- `` defines the [container cluster](/en/applications/containers) for document, query and result processing +- `` sets up the [query endpoint](/en/querying/query-api). The default port is 8080. +- `` sets up the [document endpoint](/en/reference/api/document-v1) for feeding. +- `` defines how documents are stored and searched +- `` denotes how many copies to keep of each document. +- `` assigns the document types in the *schema* to content clusters — the content cluster capacity can be increased by adding node elements — see [elasticity](/en/content/elasticity). (See also the [reference](/en/reference/applications/services/content) for more on content cluster setup.) +- `` defines the hosts for the content cluster. + + +## Deploy the application package + +Once we have finished writing our application package, we can deploy it. We use settings similar to those in the [Vespa quick start guide](/en/basics/deploy-an-application-local). + +Start the Vespa container: + +```bash +$ docker run --detach --name vespa-msmarco --hostname vespa-msmarco \ + --publish 8080:8080 --publish 19071:19071 \ + vespaengine/vespa +``` + +Notice that we publish two ports (:8080) is the data-plane port where we write and query documents, and 19071 is the control-plane where we can deploy the application. + +Configure the Vespa CLI to use the local container: + +```bash +$ vespa config set target local +``` + +Starting the container can take a short while. Make sure that the configuration service is running by using `vespa status`. + +```bash +$ vespa status deploy --wait 300 +``` + +Now, deploy the Vespa application from the `app` directory: + +```bash +$ vespa deploy --wait 300 app +``` + + +## Feed the data + +The data fed to Vespa must match the document type in the schema. The file `vespa.json` generated by the `convert-msmarco.sh` script described in the [dataset section](#dataset) already has data in the appropriate format expected by Vespa: + +```bash +$ vespa feed -t http://localhost:8080 dataset/documents.jsonl +``` + + +## Querying the data + +This section demonstrates various ways to search the data using the [Vespa query language](/en/querying/query-language). All the examples use the `vespa-cli` client, the tool uses the HTTP api and if you pass `-v`, you will see the `curl` equivalent API request. + +```bash +$ vespa query \ + 'yql=select * from msmarco where default contains text(@user-query)' \ + 'user-query=what is dad bod' \ + 'hits=3' \ + 'language=en' +``` + +This query combines YQL [text()](/en/reference/querying/yql#text), a robust way to combine free text from users or models with application logic. Similar to `set_language` in indexing, we specify the language of the query using the [language](/en/linguistics/linguistics#querying-with-language) API parameter. This ensures symmetric linguistic processing of both the query and the document text. Automatic language detection is inaccurate for short query strings and might lead to asymmetric processing of queries and document texts. + +Following is a partial output of the query above when using the small dataset sample: + +```json expandable +{ + "root": { + "id": "toplevel", + "relevance": 1, + "fields": { + "totalCount": 562 + }, + "children": [ + { + "id": "id:msmarco:msmarco::D2977840", + "relevance": 0.20676669550322158, + "source": "msmarco", + "fields": { + "sddocname": "msmarco", + "body": "After The Cut released a piece explaining what the dad bod is last week the internet pretty much exploded into debate over the trend ", + "documentid": "id:msmarco:msmarco::D2977840", + "id": "D2977840", + "title": "What Is A Dad Bod An Insight Into The Latest Male Body Craze To Sweep The Internet", + "url": "http://www.huffingtonpost.co.uk/2015/05/05/what-is-a-dadbod-male-body_n_7212072.html" + } + } + ] + } +} +``` + +As shown, 562 documents matched the query out of 996 in the corpus. The `first-phase` ranking expression scores all the matching documents. + +A few important observations: + +- We did not specify which fields to search in the query. Vespa will, by default, use a field set or field named `default` when the query terms do not specify a field. In our case: + +```sd +fieldset default { + fields: title, body, url +} +``` + +- Our query for `what is dad bod` searches across all those three fields. +- If we did not specify a `default` fieldset in the schema, the above query would return zero hits as the query did not specify a field. +- The hit `relevance` holds the score computed by the rank profile. Vespa uses `default` by default. In our case: + +```sd +rank-profile default { + first-phase { + expression: nativeRank(title, body, url) + } +} +``` + +We can use query operator annotations for the [text](/en/reference/querying/yql#text) operator to control various matching aspects, for example to set the number of hits to produce in the text operator: + +```bash +$ vespa query \ + 'yql=select * from msmarco where title contains ({targetHits:100}text(@user-query))' \ + 'user-query=what is dad bod' \ + 'hits=3' \ + 'language=en' +``` + +Notice how the query above matches fewer documents `totalCount:116` because we limited the free text query to the title field. We can change the [grammar](/en/reference/querying/yql#grammar) to specify how the user query text is parsed into a query execution plan. In the following example, we use `grammar:"all"` to specify that we only want to retrieve documents where *all* the query terms match the title field. + +```bash +$ vespa query \ + 'yql=select * from msmarco where title contains ({grammar:"all"}text(@user-query))' \ + 'user-query=what is dad bod' \ + 'hits=3' \ + 'language=en' +``` + +This query, using `all`, matches only one document. Notice how the relevance of the hit is the same as in the above example. The difference between the two types of queries is in the matching specification. + +We can use `text` to build a query that searches multiple fields (or fieldsets): + +```bash +$ vespa query \ + 'yql=select * from msmarco where title contains ({grammar:"all"}text(@user-query)) or url contains ({grammar:"all"}text(@user-query))' \ + 'user-query=what is dad bod' \ + 'hits=3' \ + 'language=en' +``` + + +### Boosting by query terms + +Sometimes, we want to add a query time boost if some field matches a query term; the following uses the [rank](/en/reference/querying/yql#rank) query operator. The rank query operator allows us to retrieve using the first operand, and the remaining operands can only impact ranking. + +It is important to note that the following approach for query time term boosting is in the context of using the `nativeRank` text scoring feature. + +```bash +$ vespa query \ + 'yql=select * from msmarco where rank(default contains text(@user-query), url contains ({weight:1000, significance:1.0}"www.answers.com"))' \ + 'user-query=what is dad bod' \ + 'hits=3' \ + 'language=en' +``` + +The above will match the user query against the default fieldset and produce match features for the second operand. It does not change the *retrieval* or *matching* as the number of documents exposed to ranking is the same as before. The `rank` operator can be used to implement a variety of use case around boosting. + + +#### Combine free text with filters + +We can combine the `text` operator with application logic. We add an application-specific query filter on the `url` field to demonstrate how to combine `text` with other query time constraints. We add `ranked:false` to tell Vespa that this specific term should not contribute to the relevance calculation and `filter:true` to ensure that the term is not used for [bolding/highlighting or dynamic snippeting](/en/querying/document-summaries#dynamic-snippets). + +```bash +$ vespa query \ + 'yql=select * from msmarco where default contains text(@user-query) and url contains ({filter:true,ranked:false}"huffingtonpost.co.uk")' \ + 'user-query=what is dad bod' \ + 'hits=3' \ + 'language=en' +``` + +Notice that the `relevance` stays the same since we used `ranked:false` for the filter. Let us see what is going on by adding [query tracing](/en/querying/query-api#query-tracing): + +```bash +$ vespa query \ + 'yql=select * from msmarco where default contains text(@user-query) and url contains ({filter:true,ranked:false}"huffingtonpost.co.uk")' \ + 'user-query=what is dad bod' \ + 'trace.level=3' \ + 'language=en' +``` + +We can notice the following in the trace output: + +``` +query=[AND (WEAKAND(100) default:what default:is default:dad default:bod) |url:'huffingtonpost co uk'] +``` + +Notice that the `text` part is converted to a [weakAnd](/en/ranking/wand) query operator and that this operator is AND'ed with a phrase search ('huffingtonpost co uk') in the `url` field. Notice also the field scoping where the query terms are prefixed with `default`. Notice also that punctuation characters (.) are removed as part of the tokenization. Suppose this is a common pattern where we want to filter on specific strings. In that case, we should create a separate field to avoid phrase matching, phrase matching is more expensive than a single token search. + + +### Supporting end user query syntax + +In some applications, you'd like end users or models to be able to search specific fields, match phrases etc. To do that, you can use the `userInput` YQL operator instead of `text`: + +```bash +$ vespa query \ + 'yql=select * from msmarco where userInput(@user-query)' \ + 'user-query=title:"dad bod"' \ + 'hits=3' \ + 'language=en' +``` + +Notice that since the string given to userInput may specify the fields to search, there is no "field contains" part that specifies the field on the YQL side. See the [userInput() documentation](/en/reference/querying/yql#userinput) on the various end user query languages supported and other parameters that can be set. + + +### Debugging token string matching + +Query tracing, combined with a summary using [tokens](/en/reference/schemas/schemas#tokens) can help debug matching. + +```bash +$ vespa query \ + 'yql=select * from msmarco where url contains ({filter:true,ranked:false}"huffingtonpost.co.uk")' \ + 'trace.level=0' \ + 'language=en' \ + 'summary=debug-tokens' +``` + +```json expandable +{ + "root": { + "id": "toplevel", + "relevance": 1, + "fields": { + "totalCount": 562 + }, + "children": [ + { + "id": "index:msmarco/0/59444ddd06537a24953b73e6", + "relevance": 0.0, + "source": "msmarco", + "fields": { + "sddocname": "msmarco", + "url": "http://www.huffingtonpost.co.uk/2015/05/05/what-is-a-dadbod-male-body_n_7212072.html", + "url-tokens": [ + "http", + "www", + "huffingtonpost", + "co", + "uk", + "2015", + "05", + "05", + "what", + "is", + "a", + "dadbod", + "male", + "body", + "n", + "7212072", + "html" + ] + } + } + ] + } +} +``` + +This gives us insight into how the input `url` field was tokenized and indexed. Those are the tokens that the query can match. Notice how punctuation characters like `:`, `,`, `.`, `/`, `_` and `-` are removed as part of the text tokenization. + +Observations: + +- Relevance is 0.0, because the term uses `ranked:false`. +- We cannot match "://" because those are not searchable characters with `match:text` +- `dadbod` is a token in the url, this cannot match `dad` or `bod` as it is represented as a single token `dadbod`. + +Let us do a similar example to demonstrate the impact of linguistic stemming: + +```bash +$ vespa query \ + 'yql=select * from msmarco where url contains ({filter:true,ranked:false}"http")' \ + 'summary=debug-tokens' \ + 'language=en' +``` + +```json +{ + "url": "http://www.ourbabynamer.com/meaning-of-Anika.html", + "url-tokens": [ + "http", + "www", + "ourbabynamer", + "com", + "meaning", + "of", + "anika", + "html" + ] +} +``` + +Notice that a query for `https` matches `http`, because 'https' on the query is stemmed to `http`. If we turn off stemming on the query side, searching for `https` directly, we end up with 0 results. + +```bash +$ vespa query \ + 'yql=select * from msmarco where url contains ({filter:true,ranked:false,stem:false}"https")' \ + 'summary=debug-tokens' \ + 'language=en' +``` + +Similarly, if we pass a different language tag, which will not stem https to http, we also get 0 results: + +```bash +$ vespa query \ + 'yql=select * from msmarco where url contains ({filter:true,ranked:false}"https")' \ + 'summary=debug-tokens' \ + 'language=de' +``` + + +## Ranking + +The previous section covered free-text search matching, linguistics, and how to combine business logic with free-text user queries. All the examples used a `default` rank-profile using Vespa's [nativeRank](/en/ranking/nativerank) text scoring feature. + +With free-text search, we can use other text scoring functions, like [BM25](/en/ranking/bm25). All the matching capabilities (or limitations) still apply, we can use fieldsets or fields; the difference is in the text scoring function where BM25 is different from nativeRank. + +```bash +$ vespa query \ + 'yql=select * from msmarco where default contains text(@user-query)' \ + 'user-query=what is dad bod' \ + 'hits=3' \ + 'language=en' \ + 'ranking=bm25' +``` + +While the `nativeRank` text score is normalized to the range 0 to 1, BM25 is unbounded, as demonstrated above. When querying (matching), we can ask Vespa to compute both features in the same query. + +Modify the schema and add a new rank-profile `combined`: + +```sd expandable +schema msmarco { + document msmarco { + field language type string { + indexing: "en" | set_language + } + field id type string { + indexing: attribute | summary + match: word + } + field title type string { + indexing: index | summary + match: text + index: enable-bm25 + } + field body type string { + indexing: index | summary + match: text + index: enable-bm25 + } + field url type string { + indexing: index | summary + index: enable-bm25 + } + } + fieldset default { + fields: title, body, url + } + document-summary minimal { + summary id { } + } + document-summary debug-tokens { + summary url {} + summary url-tokens { + source: url + tokens + } + from-disk + } + rank-profile default { + first-phase { + expression: nativeRank(title, body, url) + } + } + rank-profile bm25 inherits default { + first-phase { + expression: bm25(title) + bm25(body) + bm25(url) + } + } + + rank-profile combined inherits default { + first-phase { + expression: bm25(title) + bm25(body) + bm25(url) + nativeRank(title) + nativeRank(body) + nativeRank(url) + } + match-features { + bm25(title) + bm25(body) + bm25(url) + nativeRank(title) + nativeRank(body) + nativeRank(url) + } + } +} +``` + +Then, re-deploy the Vespa application from the `app` directory: + +```bash +$ vespa deploy --wait 300 app +``` + +Adding or removing rank profiles is a live-change as it only impacts how we score documents, not how we index or match them. + +Run a query with the new rank-profile: + +```bash +$ vespa query \ + 'yql=select * from msmarco where default contains text(@user-query)' \ + 'user-query=what is dad bod' \ + 'hits=3' \ + 'language=en' \ + 'ranking=combined' +``` + +Which will produce a result like this: + +```json expandable +{ + "root": { + "id": "toplevel", + "relevance": 1, + "fields": { + "totalCount": 562 + }, + "children": [ + { + "id": "id:msmarco:msmarco::D2977840", + "relevance": 25.482783473796484, + "source": "msmarco", + "fields": { + "matchfeatures": { + "bm25(body)": 19.51565699523739, + "bm25(title)": 4.978933753876959, + "bm25(url)": 0.3678926381724701, + "nativeRank(body)": 0.3010929113058281, + "nativeRank(title)": 0.24814575272673867, + "nativeRank(url)": 0.07106142247709807 + }, + "sddocname": "msmarco", + "documentid": "id:msmarco:msmarco::D2977840", + "id": "D2977840", + "title": "What Is A Dad Bod An Insight Into The Latest Male Body Craze To Sweep The Internet", + "url": "http://www.huffingtonpost.co.uk/2015/05/05/what-is-a-dadbod-male-body_n_7212072.html" + } + } + ] + } +} +``` + +Notice that `matchfeatures` field that is added to the hit when using `match-features` in the rank-profile. Here, we have all the computed features from the matched document, and the final `relevance` score is the sum of these features (In this case). This query and ranking example demonstrates that for a single query searching a set of fields via fieldset, we can compute different types of text scoring features and use combinations. + +Now consider the following where we limit matching to the title field: + +```bash +$ vespa query \ + 'yql=select * from msmarco where title contains text(@user-query)' \ + 'user-query=what is dad bod' \ + 'hits=3' \ + 'language=en' \ + 'ranking=combined' +``` + +Now, we do not get features for `body` or `url`, because they were not matched by the query. + + +## Next steps + +Check out the [Improving Text Search through ML](/en/learn/tutorials/text-search-ml). + + +## Cleanup + +If you do not want to proceed with the [Improving Text Search through ML](/en/learn/tutorials/text-search-ml) guide, you can stop and remove the container (and data): + +```bash +$ docker rm -f vespa-msmarco +``` + +[^1]: Robertson, Stephen and Zaragoza, Hugo and others, 2009. The probabilistic relevance framework: BM25 and beyond. Foundations and Trends in Information Retrieval. diff --git a/en/linguistics/linguistics-custom.html b/en/linguistics/linguistics-custom.html deleted file mode 100644 index 53ef3a425e..0000000000 --- a/en/linguistics/linguistics-custom.html +++ /dev/null @@ -1,54 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Custom Linguistics" -redirect_from: -- /en/linguistics-custom.html ---- - -

    -A linguistics component is an implementation of - - com.yahoo.language.Linguistics. Refer to the - - com.yahoo.language.simple.SimpleLinguistics implementation (which can be subclassed for convenience). -

    -SimpleLinguistics provides support for english stemming only. -Try loading the com.yahoo.language.simple.SimpleLinguistics module, -or providing another linguistics module. -

    -The linguistics implementation must be configured as a component -in container clusters doing linguistics processing, -see injecting components. -

    -As document processing for indexing is by default done by an autogenerated container cluster -which cannot be configured, specify a container cluster for indexing explicitly. -

    -This example shows how to configure SimpleLinguistics for linguistics using the same cluster for both query and indexing processing -(if using different clusters, add the same linguistics component to all of them): -

    -
    -<services>
    -
    -    <container version="1.0" id="mycontainer">
    -        <component id="com.yahoo.language.simple.SimpleLinguistics"/>
    -        <document-processing/>
    -        <search/>
    -        <nodes ...>
    -    </container>
    -
    -    <content version="1.0">
    -        <redundancy>1</redundancy>
    -        <documents>
    -            <document type="mydocument" mode="index"/>
    -            <document-processing cluster="mycontainer"/>
    -        </documents>
    -        <nodes ...>
    -    </content>
    -
    -</services>
    -
    -

    -If changing the linguistics component of a live system, -recall can be reduced until all documents are re-written. -This because documents will still be stored with tokens generated by the previous linguistics module. -

    diff --git a/en/linguistics/linguistics-custom.mdx b/en/linguistics/linguistics-custom.mdx new file mode 100644 index 0000000000..e8354751c9 --- /dev/null +++ b/en/linguistics/linguistics-custom.mdx @@ -0,0 +1,37 @@ +--- +title: "Custom Linguistics" +--- + +A linguistics component is an implementation of [com.yahoo.language.Linguistics](https://github.com/vespa-engine/vespa/blob/master/linguistics/src/main/java/com/yahoo/language/Linguistics.java). Refer to the [com.yahoo.language.simple.SimpleLinguistics](https://github.com/vespa-engine/vespa/blob/master/linguistics/src/main/java/com/yahoo/language/simple/SimpleLinguistics.java) implementation (which can be subclassed for convenience). + +SimpleLinguistics provides support for english stemming only. Try loading the `com.yahoo.language.simple.SimpleLinguistics` module, or providing another linguistics module. + +The linguistics implementation must be configured as a component in container clusters doing linguistics processing, see [injecting components](/en/applications/dependency-injection). + +As document processing for indexing is by default done by an autogenerated container cluster which cannot be configured, specify a container cluster for indexing explicitly. + +This example shows how to configure SimpleLinguistics for linguistics using the same cluster for both query and indexing processing (if using different clusters, add the same linguistics component to all of them): + +```xml highlight= {4,14} + + + + + + + + + + + 1 + + + + + + + + +``` + +If changing the linguistics component of a live system, recall can be reduced until all documents are re-written. This because documents will still be stored with tokens generated by the previous linguistics module. \ No newline at end of file diff --git a/en/linguistics/linguistics-opennlp.html b/en/linguistics/linguistics-opennlp.html deleted file mode 100644 index 50b45de321..0000000000 --- a/en/linguistics/linguistics-opennlp.html +++ /dev/null @@ -1,207 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "OpenNLP Linguistics" -redirect_from: -- /en/linguistics-opennlp.html ---- - -

    - The default Vespa linguistics implementation uses OpenNLP. The Apache - OpenNLP language detection is also used, by default, even if you're using a different implementation. See - Language handling for more information. OpenNLP has support - for 103 languages. -

    - -

    OpenNLP language detection

    -

    - The OpenNLP language detector gives a prediction with a - confidence; with confidence typically increasing with more - input. The threshold for using the prediction can be configured - with a number typically from 1.0 (wild guess) to 6.0 (confident - guess), with 2.0 as the default: -

    -  <container id="..." version="1.0">
    -    ...
    -    <config name="ai.vespa.opennlp.open-nlp">
    -      <detectConfidenceThreshold>4.2</detectConfidenceThreshold>
    -    </config>
    -
    -

    - -

    Default languages

    -

    - OpenNLP tokenization and stemming supports these languages: -

      -
    • Arabic (ar)
    • -
    • Catalan (ca)
    • -
    • Danish (da)
    • -
    • Dutch (nl)
    • -
    • English (en)
    • -
    • Finnish (fi)
    • -
    • French (fr)
    • -
    • German (de)
    • -
    • Greek (el)
    • -
    • Hungarian (hu)
    • -
    • Indonesian (id)
    • -
    • Irish (ga)
    • -
    • Italian (it)
    • -
    • Norwegian (no)
    • -
    • Portuguese (pt)
    • -
    • Romanian (ro)
    • -
    • Russian (ru)
    • -
    • Spanish (es)
    • -
    • Swedish (sv)
    • -
    • Turkish (tr)
    • -
    - -

    Other languages will use a fallback to English en.

    - -

    English uses a simpler stemmer (kStem) by default, which produces fewer stems and therefore lower recall. - To use OpenNlp stemming (Snowball) also for English add this config to your <container> element(s): -

    -  <container id="..." version="1.0">
    -    ...
    -    <config name="ai.vespa.opennlp.open-nlp">
    -      <snowballStemmingForEnglish>true</snowballStemmingForEnglish>
    -    </config>
    -
    - -

    See Tokens OpenNLP models and text matching - for examples and how to experiment with linguistics.

    - -

    If you need support for more languages, you can consider replacing the default OpenNLP based linguistic integration - with the Lucene Linguistics implementation which supports more languages. -

    - -

    Chinese

    - -

    The default linguistics implementation does not segment Chinese into tokens, but this can be turned on by config:

    - -
    -  <container id="..." version="1.0">
    -    ...
    -    <config name="ai.vespa.opennlp.open-nlp">
    -      <cjk>true</cjk>
    -      <createCjkGrams>true</createCjkGrams>
    -    </config>
    -
    - -

    The createCjkGrams adds substrings of segments longer than 2 characters, which may increase recall.

    - - -

    Tokenization

    -

    - Tokenization removes any non-word characters, - and splits the string into tokens on each word boundary. - In addition, CJK tokens are split using a segmentation algorithm. - The resulting tokens are then searchable in the index. -

    -

    Also see N-gram matching.

    - - - -

    Normalization

    -

    -An example normalization is à ⇒ a. -Normalizing will cause accents and similar decorations which are often misspelled -to be normalized the same way both in documents and queries. -

    -Vespa uses java.text.Normalizer -to normalize text, see - -SimpleTransformer.java. -Normalization preserves case. -

    -

    - Refer to the nfkc query term annotation. - Also see the YQL accentDrop annotation. -

    - - - -

    Stemming

    -

    -Stemming means translate a word to its base form -(singular forms for nouns, infinitive for verbs), -using a stemmer. -Use of stemming increases search recall, -because the searcher is usually interested in documents containing query words -regardless of the word form used. -Stemming in Vespa is symmetric, -i.e. words are converted to stems both when indexing and searching. -

    -Examples of this is when text is indexed, -the stemmer will convert the noun reports (plural) to report, -and the latter will be stored in the index. -Likewise, before searching, reports will be stemmed to report. -Another example is that am, are and was -will be stemmed to be both in queries and indexes. -

    -When bolding is enabled, -all forms of the query term will be bolded. -I.e. when searching for reports, -both report, reported and reports will be bolded. -

    -

    - See the stem query term annotation. -

    - - -

    Theory

    -

    -From a matching point of view, -stemming takes all possible token strings and maps them into equivalence classes. -So in the example above, the set of tokens -{ report, reports, reported } are in an equivalence class. -To represent the class, -the linguistics library should pick the best element in the class. -At query time, the text typed by a user will be tokenized, -and then each token should be mapped to the most likely equivalence class, -again represented by the shortest element that belongs to the class. -

    -While the theory sounds pretty simple, -in practice it is not always possible to figure out which equivalence class a token should belong to. -A typical example is the string number. -In most cases we would guess this to mean a numerical entity of some kind, -and the equivalence class would be { number, numbers } - but it could also be a verb, -with a different equivalence class { number, numbered, numbering }. -These are of course closely related, and in practice they will be merged, -so we'll have a slightly larger equivalence class -{ number, numbers, numbered, numbering } -and be happy with that. -However, in a sentence such as my legs keep getting number every day, -the number token clearly does not have the semantics of a numerical entity, -but should be in the equivalence class -{ numb, number, numbest, numbness } instead. -But blindly assigning number to the equivalence class numb is clearly not right, -since the more numb meaning is much less likely than the numerical entity meaning. -

    -The approach currently taken by the low-level linguistics library -will often lead to problems in the number-like cases as described above. -To give better recall, Vespa has implemented a multiple stemming option. -

    - - -

    Configuration

    -

    -By default, all words are stemmed to their best form. -Refer to the -stemming reference for other stemming types. To change type, add: -

    -
    -stemming: [stemming-type]
    -
    -

    - Stemming can be set either for a field, a fieldset or as a default for all fields. - Example: Disable stemming for the field title: -

    -
    -field title type string {
    -    indexing: summary | index
    -    stemming: none
    -}
    -
    -

    - See andSegmenting - for how to control re-segmenting when stemming. -

    \ No newline at end of file diff --git a/en/linguistics/linguistics-opennlp.mdx b/en/linguistics/linguistics-opennlp.mdx new file mode 100644 index 0000000000..f6eff7aa19 --- /dev/null +++ b/en/linguistics/linguistics-opennlp.mdx @@ -0,0 +1,123 @@ +--- +title: "OpenNLP Linguistics" +sidebarTitle: "Default (OpenNLP) linguistics" +--- + +The default Vespa linguistics implementation uses [OpenNLP](https://opennlp.apache.org/). The Apache OpenNLP language detection is also used, by default, even if you're using a different implementation. See [Language handling](/en/linguistics/linguistics#language-handling) for more information. OpenNLP has support for 103 languages. + +## OpenNLP language detection + +The OpenNLP language detector gives a prediction with a confidence; with confidence typically increasing with more input. The threshold for using the prediction can be configured with a number typically from 1.0 (wild guess) to 6.0 (confident guess), with 2.0 as the default: + +```xml + + ... + + 4.2 + +``` + +## Default languages + +OpenNLP tokenization and stemming supports these languages: + +- Arabic (ar) +- Catalan (ca) +- Danish (da) +- Dutch (nl) +- English (en) +- Finnish (fi) +- French (fr) +- German (de) +- Greek (el) +- Hungarian (hu) +- Indonesian (id) +- Irish (ga) +- Italian (it) +- Norwegian (no) +- Portuguese (pt) +- Romanian (ro) +- Russian (ru) +- Spanish (es) +- Swedish (sv) +- Turkish (tr) + +Other languages will use a fallback to English _en_. + +English uses a simpler stemmer (kStem) by default, which produces fewer stems and therefore lower recall. To use OpenNlp stemming (Snowball) also for English add this config to your \ element(s): + +```xml + + ... + + true + +``` + +See _Tokens_ [OpenNLP models](https://opennlp.apache.org/models.html) and [text matching](/en/querying/text-matching) for examples and how to experiment with linguistics. + +If you need support for more languages, you can consider replacing the default OpenNLP based linguistic integration with the [Lucene Linguistics](/en/linguistics/lucene-linguistics) implementation which supports more languages. + +### Chinese + +The default linguistics implementation does not segment Chinese into tokens, but this can be turned on by config: + +```xml + + ... + + true + true + +``` + +The createCjkGrams adds substrings of segments longer than 2 characters, which may increase recall. + +## Tokenization + +Tokenization removes any non-word characters, and splits the string into _tokens_ on each word boundary. In addition, CJK tokens are split using a _segmentation_ algorithm. The resulting tokens are then searchable in the index. + +Also see [N-gram matching](/en/reference/schemas/schemas#gram). + +## Normalization + +An example normalization is à ⇒ a. Normalizing will cause accents and similar decorations which are often misspelled to be normalized the same way both in documents and queries. + +Vespa uses [java.text.Normalizer](https://docs.oracle.com/javase/7/docs/api/java/text/Normalizer.html) to normalize text, see [SimpleTransformer.java](https://github.com/vespa-engine/vespa/blob/master/linguistics/src/main/java/com/yahoo/language/simple/SimpleTransformer.java). Normalization preserves case. + +Refer to the [nfkc](/en/reference/querying/yql#nfkc) query term annotation. Also see the YQL [accentDrop](/en/reference/querying/yql#accentdrop) annotation. + +## Stemming + +Stemming means _translate a word to its base form_ (singular forms for nouns, infinitive for verbs), using a [stemmer](https://en.wikipedia.org/wiki/Stemming). Use of stemming increases search recall, because the searcher is usually interested in documents containing query words regardless of the word form used. Stemming in Vespa is symmetric, i.e. words are converted to stems both when indexing and searching. + +Examples of this is when text is indexed, the stemmer will convert the noun _reports_ (plural) to _report_, and the latter will be stored in the index. Likewise, before searching, _reports_ will be stemmed to _report_. Another example is that _am_, _are_ and _was_ will be stemmed to _be_ both in queries and indexes. + +When [bolding](/en/reference/schemas/schemas#bolding) is enabled, all forms of the query term will be bolded. I.e. when searching for _reports_, both _report_, _reported_ and _reports_ will be bolded. + +See the [stem](/en/reference/querying/yql#stem) query term annotation. + +### Theory + +From a matching point of view, stemming takes all possible token strings and maps them into equivalence classes. So in the example above, the set of tokens \{ _report_, _reports_, _reported_ } are in an equivalence class. To represent the class, the linguistics library should pick the best element in the class. At query time, the text typed by a user will be tokenized, and then each token should be mapped to the most likely equivalence class, again represented by the shortest element that belongs to the class. + +While the theory sounds pretty simple, in practice it is not always possible to figure out which equivalence class a token should belong to. A typical example is the string _number_. In most cases we would guess this to mean a numerical entity of some kind, and the equivalence class would be \{ _number_, _numbers_ } - but it could also be a verb, with a different equivalence class \{ _number_, _numbered_, _numbering_ \}. These are of course closely related, and in practice they will be merged, so we'll have a slightly larger equivalence class \{ _number_, _numbers_, _numbered_, _numbering_ \} and be happy with that. However, in a sentence such as _my legs keep getting number every day_, the _number_ token clearly does not have the semantics of a numerical entity, but should be in the equivalence class \{ _numb_, _number_, _numbest_, _numbness_ \} instead. But blindly assigning _number_ to the equivalence class _numb_ is clearly not right, since the _more numb_ meaning is much less likely than the _numerical entity_ meaning. + +The approach currently taken by the low-level linguistics library will often lead to problems in the _number_-like cases as described above. To give better recall, Vespa has implemented a _multiple_ stemming option. + +### Configuration + +By default, all words are stemmed to their _best_ form. Refer to the [stemming reference](/en/reference/schemas/schemas#stemming) for other stemming types. To change type, add: + +```yaml +stemming: [stemming-type] +``` + +Stemming can be set either for a field, a fieldset or as a default for all fields. Example: Disable stemming for the field _title_: + +```yaml +field title type string { + indexing: summary | index + stemming: none +} +``` \ No newline at end of file diff --git a/en/linguistics/linguistics.html b/en/linguistics/linguistics.html deleted file mode 100644 index 64ac9bc72f..0000000000 --- a/en/linguistics/linguistics.html +++ /dev/null @@ -1,448 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Linguistics in Vespa" -redirect_from: -- /en/linguistics.html ---- - -

    - Vespa uses a linguistics module to process text in queries and documents during indexing and searching. - The goal of linguistic processing is to increase recall (how many documents are matched) - without hurting precision (the relevance of the documents matched) too much. - It consists of such operations as: -

    -
      -
    • tokenizing text into chunks of known types such as words and punctuation.
    • -
    • normalizing accents.
    • -
    • finding the base form of words (stemming or lemmatization).
    • -
    -

    - Linguistic processing is run when writing documents, and when querying: -

    -Overview: linguistic processing in Vespa -

    - The processing is run on string fields - with index indexing mode. Overview: -

    -
      -
    1. - When writing documents, string fields with indexing: index are by default processed. - A field's language will configure this processing. - A document/fields can have the language set explicitly, - if not, it is detected. - -
    2. -
    3. - The field's content is processed (e.g., tokenized, normalized, stemmed, etc.), - and the resulting terms are added to the index. - {% include note.html content='The language for the field is not persisted on the content node, - just the processed terms themselves' %} -
    4. -
    5. - A query is also processed in a similar fashion. Typically through the same - linguistics profile as the field content, - producing the same terms from the same text. - The language of query strings is detected unless specified using - model.locale - or annotations like language. - {% include note.html content='This is a very common query problem - - it is hard to detect language precisely from short strings.' %} -
    6. -
    7. - The processed query is evaluated on the content nodes, - and will only work as expected if both documents and queries produce the same terms. -
    8. -
    -

    - These operations can be turned on or off per field in the schema. - See implicitTransforms - for how to enable/disable transforms per query term. -

    - -

    Linguistics implementations

    -

    - Vespa comes with two linguistics variants out of the box: - OpenNLP and Lucene. - Check out the respective pages for more information on how to configure them. -

    -

    - You can also implement a custom Linguistics component. -

    -

    - The default linguistics variant is OpenNLP, but for the - rest of this page we'll go through common options, such as language handling, inherited by - all implementations. -

    - -{% include note.html content='Linguistics implementations only control how text is tokenized, - including positional information. These tokens are stored in the same way in the underlying - index. For example, if you use Lucene linguistics, Vespa does not store information such as - positions in Lucene segment files. Storage is the same as with OpenNLP, - only resulting tokens might differ.' %} - -

    Language handling

    -

    -Vespa does not know the language of a document - this applies: -

    -
      -
    1. The indexing processor is instructed on a per-field level what language to -use when calling the underlying linguistics library
    2. -
    3. The query processor is instructed on a per-query level what language to use
    4. -
    -

    -If no language is explicitly set in a document or a query, -Vespa will run its configured language detector (by default, OpenNLP language detection) -on the available text (the full content of a document field, or the full query= parameter value). -

    -A document that contains the exact same word as a query might not be recall-able -if the language of the document field is detected differently from the query. -Unless the query has explicitly declared a language, -this can occur. -

    - - -

    Indexing with language

    - -

    The indexing process run by Vespa is a sequential execution -of the indexing scripts of each field in the schema, in the declared order. -At any point, the script may set the language that will be used for indexing statements for subsequent fields, -using set_language. - -Example: -

    -
    -schema doc {
    -    document doc {
    -        field language type string {
    -            indexing: set_language
    -        }
    -        field title type string {
    -            indexing: index
    -        }
    -    }
    -}
    -
    - -

    If a language has not been set when tokenization of a field is run, the language is determined by -language detection.

    - -

    If all documents have the same language, the language can be hardcoded it the schema in this way:

    -
    -schema doc {
    -
    -    field language type string {
    -        indexing: "en" | set_language
    -    }
    -
    -    document doc {
    -    ...
    -
    -

    If the same document contains fields in multiple languages, set_language can be invoked multiple times, e.g.:

    -
    -schema doc {
    -    document doc {
    -        field language_title1 type string {
    -            indexing: set_language
    -        }
    -        field title1 type string {
    -            indexing: index
    -        }
    -        field language_title2 type string {
    -            indexing: set_language
    -        }
    -        field title2 type string {
    -            indexing: index
    -        }
    -    }
    -}
    -
    - -

    Or, if fixed per field, use multiple indexing statements in each field:

    - -
    -schema doc {
    -    document doc {
    -        field my_english_field type string {
    -            indexing {
    -                "en" | set_language;
    -                index;
    -            }
    -        }
    -        field my_spanish_field type string {
    -            indexing {
    -                "es" | set_language;
    -                index;
    -            }
    -        }
    -    }
    -}
    -
    - - -

    Field language detection

    -

    - When indexing a document, if a field has unknown language (i.e. not set using set_language), - language detection is run on the field's content. - This means, language detection is per field, not per document. -

    -

    - See query language detection for detection confidence, - fields with little text will default to English. -

    - - -

    Querying with language

    -

    - The content of an indexed string field is language-agnostic. - One must therefore apply a compatible tokenization on the query terms (e.g., stemming for the same language) - in order to match the content of that field. -

    -

    - The query parser subscribes to configuration that tells it what fields are indexed strings, - and every query term that targets such a field are run through appropriate tokenization. - The language query parameter - controls the language state of these calls. -

    -

    - Because an index may simultaneously contain terms in any number of languages, - one can have stemmed variants of one language match the stemmed variants of another. - To work around this, store the language of a document in a separate attribute, - and apply a filter against that attribute at query-time. -

    -

    - By default, there is no knowledge anywhere that captures what - languages are used to generate the content of an index. - The language parameter only affects the transformation of query terms that hit tokenized indexes. -

    - - -

    Query language detection

    -

    - If no language parameter is used, - or the query terms are annotated, - the language detector is called to process the query string. -

    -

    - Queries are normally short, as a consequence, the detection confidence is low. Example: -

    -
    -$ vespa query "select * from music where default contains text(@text)" \
    -  tracelevel=3 text='Eine kleine Nachtmusik' | grep 'Stemming with language'
    -    "message": "Stemming with language=ENGLISH"
    -
    -$ vespa query "select * from music where default contains text(@text)" \
    -  tracelevel=3 text='Eine kleine Nachtmusik schnell' | grep 'Stemming with language'
    -    "message": "Stemming with language=GERMAN"
    -
    -

    - See #24265 for details - - in short, with the current 0.02 confidence cutoff, queries with 3 terms or fewer will default to English. -

    - - -

    Multiple languages

    -

    Vespa supports having documents in multiple languages in the same schema, but does not out-of-the-box - support cross-lingual retrieval (e.g., search using English and retrieve relevant documents written in German). - - This is because the language of a query is determined - by the language of the query string and only one transformation can take place. -

    -

    -Approaches to overcome this limitation include: -

    -
      -
    1. - Use semantic retrieval using a multilingual text embedding model (see blog post) - which has been trained on multilingual corpus and can be used to retrieve documents in multiple languages. -
    2. - Stem and tokenize the query using the relevant languages, - build a query tree using weakAnd / - or - and using equiv per stem variant. - This is easiest done in a custom Searcher as mentioned in - #12154. -
    3. -
    -

    -Example: -

    -language=fr: machine learning => machin learn -

    -language=en: machine learning => machine learn -

    -Using weakAnd here as example as that technique is already mentioned in #12154: -

    -
    -select * from sources * where rank( 
    -    default contains "machine", 
    -    default contains "learning",
    -    weakAnd(
    -      default contains equiv("machin", "machine"), 
    -      default contains "learn"
    -    )
    -)
    -
    -

    -We now retrieve using all possible stems/base forms with weakAnd, -and use the rank operator -to pass in the original query form, so that ranking can rank literal matches (original) higher. -Benefit of equiv is that it allows multiple term variants to share the same position, -so that proximity ranking does not become broken by this approach. -

    - -

    Linguistics profiles

    - -

    - Linguistics profiles are used - to configure linguistics processing for a field in the schema. They are typically used with - the Lucene linguistics implementation, but can be used - in e.g., custom linguistics implementations as well. -

    - -

    Symmetrical processing

    - -

    - For example, a definition like this: -

    -
    -field title type string {
    -  indexing: summary | index
    -  linguistics {
    -      profile: whitespaceLowercase
    -  }
    -}
    -
    - -

    - Will look for a profile named whitespaceLowercase, which could be defined like this - in services.xml: -

    -
    {% highlight xml %}
    -  
    -    
    -      whitespace
    -    
    -    
    -      
    -        lowercase
    -      
    -    
    -  
    -{% endhighlight %}
    - -

    - Note language=en there. It is optional: if it's not set, - the profile will be used for all languages. But you can have different - definitions for different languages on the same profile (e.g., different stemming). -

    - -

    Different processing for query strings

    - -

    - For some use cases, you may want to process the query string differently than the document content. - Synonyms are a good example. If you expand dog to dog,puppy at query time, - it will match either term in the document anyway - no need to expand it at write-time. -

    - -

    - To do this, you'd define a different profile for the query string. Like: -

    -
    {% highlight xml %}
    -  
    -    
    -      whitespace
    -    
    -    
    -      
    -        lowercase
    -      
    -      
    -        synonymGraph
    -        
    -          
    -          en/synonyms.txt
    -        
    -      
    -    
    -  
    -{% endhighlight %}
    - -

    - Then, in the schema, expand profile to profile.index and profile.search: -

    - -
    -  field title type string {
    -    indexing: summary | index
    -    linguistics {
    -        profile {
    -            index: whitespaceLowercase
    -            search: whitespaceLowercaseSynonyms
    -        }
    -    }
    -}
    -
    - -

    - At this point, where synonyms_test contains 'dog' will match a document containing puppy. -

    - -

    Overriding profile for query strings

    - -

    - At query time, you can tell Vespa to use a specific profile to process the query string via grammar.profile. - This works with the userInput() and text() operators. - For example, to use the whitespaceLowercase profile for the query string: -

    - -
    -where title contains ({grammar.profile: 'whitespaceLowercase'}text('dog'))
    -
    - -Equivalent expression via userInput(): -
    -where {defaultIndex:'title', grammar.profile: 'whitespaceLowercase', grammar: 'linguistics'}userInput('dog')
    -
    - -{% include note.html content='You should use grammar=linguistics (like in the example above) with grammar.profile to ensure that there is no -additional processing (e.g., tokenization) besides what is already defined in the profile.' %} - - -

    Troubleshooting linguistics processing

    - -

    - If your documents don't match as expected, there are two ways to get more information. - - First, you can get the tokenized text for a field by using tokens - in the document summary. - For example, to get the original text and tokens for the title field: -

    - -
    -  document-summary debug-text-tokens {
    -    summary title {}
    -    summary title_tokens {
    -        source: title
    -        tokens
    -    }
    -    from-disk
    -}
    -
    - -

    - Then, at query time, you can also get the tokens of the query string - by increasing the trace level: -

    - -
    {% highlight json %}
    -{
    -  "yql": "select * from sources * where title contains \"dog\"",
    -  "presentation.summary": "debug-text-tokens",
    -  "model.locale": "en",
    -  "trace.level": 2
    -}
    -{% endhighlight %}
    \ No newline at end of file diff --git a/en/linguistics/linguistics.mdx b/en/linguistics/linguistics.mdx new file mode 100644 index 0000000000..1765aa1c8b --- /dev/null +++ b/en/linguistics/linguistics.mdx @@ -0,0 +1,313 @@ +--- +title: "Linguistics in Vespa" +sidebarTitle: "Linguistics Overview" +--- + +Vespa uses a _linguistics_ module to process text in queries and documents during indexing and searching. The goal of linguistic processing is to increase _recall_ (how many documents are matched) without hurting _precision_ (the relevance of the documents matched) too much. It consists of such operations as: + +- tokenizing text into chunks of known types such as words and punctuation. +- normalizing accents. +- finding the base form of words (stemming or lemmatization). + +Linguistic processing is run when writing documents, and when querying: + + + ![Overview: linguistic processing in Vespa](/assets/img/vespa-overview-linguistics.svg) + + +The processing is run on [string](/en/reference/schemas/schemas#string) fields with `index` indexing mode. Overview: + +1. When writing documents, string fields with `indexing: index` are by default processed. A field's language will configure this processing. A document/fields can have the language set explicitly, if not, it is [detected](/en/linguistics/linguistics#field-language-detection). +2. The field's content is processed (e.g., tokenized, normalized, stemmed, etc.), and the resulting terms are added to the index. + + **Note:** The language for the field is not persisted on the content node, just the processed terms themselves + +3. A query is also processed in a similar fashion. Typically through the same [linguistics profile](/en/reference/schemas/schemas#linguistics) as the field content, producing the same terms from the same text. The language of query strings is [detected](/en/linguistics/linguistics#query-language-detection) unless specified using [model.locale](/en/reference/api/query#model.locale) or [annotations](/en/reference/querying/yql#annotations) like `language`. + + **Note:** This is a very common query problem - it is hard to detect language precisely from short strings. + +4. The processed query is evaluated on the content nodes, and will only work as expected if both documents and queries produce the same terms. + +These operations can be turned on or off per field in the [schema](/en/basics/schemas). See [implicitTransforms](/en/reference/querying/yql#implicittransforms) for how to enable/disable transforms per query term. + +## Linguistics implementations + +Vespa comes with two linguistics variants out of the box: [OpenNLP](/en/linguistics/linguistics-opennlp) and [Lucene](/en/linguistics/lucene-linguistics). Check out the respective pages for more information on how to configure them. + +You can also implement a custom [Linguistics](/en/linguistics/linguistics-custom) component. + +The default linguistics variant is [OpenNLP](/en/linguistics/linguistics-opennlp), but for the rest of this page we'll go through common options, such as language handling, inherited by all implementations. + + + **Note:** Linguistics implementations only control how text is tokenized, including positional information. These tokens are stored in the same way in the underlying index. For example, if you use Lucene linguistics, Vespa does not store information such as positions in Lucene segment files. Storage is the same as with OpenNLP, only resulting tokens might differ. + + +## Language handling + +Vespa does _not_ know the language of a document - this applies: + +1. The indexing processor is instructed on a per-field level what language to use when calling the underlying linguistics library +2. The query processor is instructed on a per-query level what language to use + +If no language is explicitly set in a document or a query, Vespa will run its configured language detector (by default, [OpenNLP language detection](/en/linguistics/linguistics-opennlp#language-detection)) on the available text (the full content of a document field, or the full `query=` parameter value). + +A document that contains the exact same word as a query might not be recall-able if the language of the document field is detected differently from the query. Unless the query has explicitly declared a [language](/en/reference/api/query#model.language), this can occur. + +### Indexing with language + +The indexing process run by Vespa is a sequential execution of the indexing scripts of each field in the schema, in the declared order. At any point, the script may set the language that will be used for indexing statements for subsequent fields, using [set\_language](/en/reference/writing/indexing-language#set_language). Example: + +```yaml +schema doc { + document doc { + field language type string { + indexing: set_language + } + field title type string { + indexing: index + } + } +} +``` + +If a language has not been set when tokenization of a field is run, the language is determined by [language detection](/en/linguistics/linguistics#field-language-detection). + +If all documents have the same language, the language can be hardcoded it the schema in this way: + +```yaml +schema doc { + + field language type string { + indexing: "en" | set_language + } + + document doc { + ... +``` + +If the same document contains fields in multiple languages, set\_language can be invoked multiple times, e.g.: + +```yaml +schema doc { + document doc { + field language_title1 type string { + indexing: set_language + } + field title1 type string { + indexing: index + } + field language_title2 type string { + indexing: set_language + } + field title2 type string { + indexing: index + } + } +} +``` + +Or, if fixed per field, use multiple indexing statements in each field: + +```yaml +schema doc { + document doc { + field my_english_field type string { + indexing { + "en" | set_language; + index; + } + } + field my_spanish_field type string { + indexing { + "es" | set_language; + index; + } + } + } +} +``` + +### Field language detection + +When indexing a document, if a field has unknown language (i.e. not set using `set_language`), language detection is run on the field's content. This means, language detection is per field, not per document. + +See [query language detection](/en/linguistics/linguistics#query-language-detection) for detection confidence, fields with little text will default to English. + +### Querying with language + +The content of an indexed string field is language-agnostic. One must therefore apply a compatible tokenization on the query terms (e.g., stemming for the same language) in order to match the content of that field. + +The query parser subscribes to configuration that tells it what fields are indexed strings, and every query term that targets such a field are run through appropriate tokenization. The [language](/en/reference/api/query#model.language) query parameter controls the language state of these calls. + +Because an index may simultaneously contain terms in any number of languages, one can have stemmed variants of one language match the stemmed variants of another. To work around this, store the language of a document in a separate attribute, and apply a filter against that attribute at query-time. + +By default, there is no knowledge anywhere that captures what languages are used to generate the content of an index. The language parameter only affects the transformation of query terms that hit tokenized indexes. + +### Query language detection + +If no [language](/en/reference/api/query#model.language) parameter is used, or the query terms are [annotated](/en/reference/querying/yql#annotations), the language detector is called to process the query string. + +Queries are normally short, as a consequence, the detection confidence is low. Example: + +```bash +$ vespa query "select * from music where default contains text(@text)" \ + tracelevel=3 text='Eine kleine Nachtmusik' | grep 'Stemming with language' + "message": "Stemming with language=ENGLISH" + +$ vespa query "select * from music where default contains text(@text)" \ + tracelevel=3 text='Eine kleine Nachtmusik schnell' | grep 'Stemming with language' + "message": "Stemming with language=GERMAN" +``` + +See [#24265](https://github.com/vespa-engine/vespa/issues/24265) for details - in short, with the current 0.02 confidence cutoff, queries with 3 terms or fewer will default to English. + +### Multiple languages + +Vespa supports having documents in multiple languages in the same schema, but does not out-of-the-box support cross-lingual retrieval (e.g., search using English and retrieve relevant documents written in German). This is because the language of a query is determined by the language of the query string and only one transformation can take place. + +Approaches to overcome this limitation include: + +1. Use semantic retrieval using a multilingual text embedding model (see [blog post](https://blog.vespa.ai/simplify-search-with-multilingual-embeddings/)) which has been trained on multilingual corpus and can be used to retrieve documents in multiple languages. +2. Stem and tokenize the query using the relevant languages, build a query tree using [weakAnd](/en/reference/querying/yql#weakand) / [or](/en/reference/querying/yql#or) and using [equiv](/en/reference/querying/yql#equiv) per stem variant. This is easiest done in a custom [Searcher](/en/applications/searchers) as mentioned in [#12154](https://github.com/vespa-engine/vespa/issues/12154). + +Example: + +**language=fr:** machine learning =\> machin learn + +**language=en:** machine learning =\> machine learn + +Using _weakAnd_ here as example as that technique is already mentioned in #12154: + +```sql +select * from sources * where rank( + default contains "machine", + default contains "learning", + weakAnd( + default contains equiv("machin", "machine"), + default contains "learn" + ) +) +``` + +We now retrieve using all possible stems/base forms with _weakAnd_, and use the [rank](/en/reference/querying/yql#rank) operator to pass in the original query form, so that ranking can rank literal matches (original) higher. Benefit of _equiv_ is that it allows multiple term variants to share the same position, so that proximity ranking does not become broken by this approach. + +## Linguistics profiles + +Linguistics profiles are used to configure linguistics processing for a field in the schema. They are typically used with the [Lucene linguistics implementation](/en/linguistics/lucene-linguistics), but can be used in e.g., [custom linguistics implementations](/en/linguistics/linguistics-custom) as well. + +### Symmetrical processing + +For example, a definition like this: + +```yaml +field title type string { + indexing: summary | index + linguistics { + profile: whitespaceLowercase + } +} +``` + +Will look for a profile named `whitespaceLowercase`, which could be defined like this in `services.xml`: + +```xml + + + whitespace + + + + lowercase + + + +``` + +Note `language=en` there. It is optional: if it's not set, the profile will be used for all languages. But you can have different definitions for different languages on the same profile (e.g., different stemming). + +### Different processing for query strings + +For some use cases, you may want to process the query string differently than the document content. Synonyms are a good example. If you expand `dog` to `dog,puppy` at query time, it will match either term in the document anyway - no need to expand it at write-time. + +To do this, you'd define a different profile for the query string. Like: + +```xml + + + whitespace + + + + lowercase + + + synonymGraph + + + en/synonyms.txt + + + + +``` + +Then, in the schema, expand `profile` to `profile.index` and `profile.search`: + +```yaml +field title type string { + indexing: summary | index + linguistics { + profile { + index: whitespaceLowercase + search: whitespaceLowercaseSynonyms + } + } +} +``` + +At this point, `where synonyms_test contains 'dog'` will match a document containing `puppy`. + +### Overriding profile for query strings + +At query time, you can tell Vespa to use a specific profile to process the query string via [grammar.profile](/en/reference/querying/yql#grammar). This works with the [userInput()](/en/reference/querying/yql#userinput) and [text()](/en/reference/querying/yql#text) operators. For example, to use the `whitespaceLowercase` profile for the query string: + +```sql +where title contains ({grammar.profile: 'whitespaceLowercase'}text('dog')) +``` +Equivalent expression via `userInput()`: +```sql +where {defaultIndex:'title', grammar.profile: 'whitespaceLowercase', grammar: 'linguistics'}userInput('dog') +``` + + + **Note:** You should use grammar=linguistics (like in the example above) with grammar.profile to ensure that there is no additional processing (e.g., tokenization) besides what is already defined in the profile. + + +## Troubleshooting linguistics processing + +If your documents don't match as expected, there are two ways to get more information. First, you can get the tokenized text for a field by using [tokens](/en/reference/schemas/schemas#tokens) in the [document summary](/en/querying/document-summaries). For example, to get the original text and tokens for the `title` field: + +```yaml +document-summary debug-text-tokens { + summary title {} + summary title_tokens { + source: title + tokens + } + from-disk +} +``` + +Then, at query time, you can also get the tokens of the query string by increasing the [trace level](/en/reference/api/query#trace.level): + +```json +{ + "yql": "select * from sources * where title contains \"dog\"", + "presentation.summary": "debug-text-tokens", + "model.locale": "en", + "trace.level": 2 +} +``` diff --git a/en/linguistics/lucene-linguistics.html b/en/linguistics/lucene-linguistics.html deleted file mode 100644 index cbfa21c441..0000000000 --- a/en/linguistics/lucene-linguistics.html +++ /dev/null @@ -1,535 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Lucene Linguistics" -redirect_from: -- /en/lucene-linguistics.html ---- - -

    - Lucene Linguistics is a custom linguistics implementation of the - Apache Lucene library. - It provides a Lucene analyzer to handle text processing for a language - with an optional variation per stemming mode. -

    - -

    - Check sample apps to - get started. -

    - -

    Crash course to Lucene text analysis

    - -

    - Lucene text - analysis - is a process of converting text into searchable tokens. - This text analysis consists of a series of components applied to the text in order: -

    -
      -
    • - CharFilters: - transform the text before it is tokenized, while providing corrected character offsets to account for these - modifications. -
    • -
    • Tokenizers: - responsible for breaking up incoming text into tokens. -
    • -
    • - TokenFilters: - responsible for modifying tokens that have been created by the Tokenizer. -
    • -
    - -

    - A specific configuration of the above components is wrapped into an - Analyzer object. -

    - -The text analysis works as follows: -
      -
    1. All char filters are applied in the specified order on the entire text string
    2. -
    3. Token filters in the specified order are applied on each token.
    4. -
    - -

    Defaults language analysis

    - -

    - Lucene Linguistics out-of-the-box exposes the analysis components provided - by the lucene-core - and the - lucene-analysis-common - libraries. - Other libraries with Lucene text analysis components - (e.g. analysis-kuromoji) - can be added to the application package as a Maven dependency. -

    - -

    - Lucene Linguistics out-of-the-box provides analyzers for 40 languages: -

    -
      -
    • Arabic
    • -
    • Armenian
    • -
    • Basque
    • -
    • Bengali
    • -
    • Bulgarian
    • -
    • Catalan
    • -
    • Chinese
    • -
    • Czech
    • -
    • Danish
    • -
    • Dutch
    • -
    • English
    • -
    • Estonian
    • -
    • Finnish
    • -
    • French
    • -
    • Galician
    • -
    • German
    • -
    • Greek
    • -
    • Hindi
    • -
    • Hungarian
    • -
    • Indonesian
    • -
    • Irish
    • -
    • Italian
    • -
    • Japanese
    • -
    • Korean
    • -
    • Kurdish
    • -
    • Latvian
    • -
    • Lithuanian
    • -
    • Nepali
    • -
    • Norwegian
    • -
    • Persian
    • -
    • Portuguese
    • -
    • Romanian
    • -
    • Russian
    • -
    • Serbian
    • -
    • Spanish
    • -
    • Swedish
    • -
    • Tamil
    • -
    • Telugu
    • -
    • Thai
    • -
    • Turkish
    • -
    - -

    - The Lucene - StandardAnalyzer - is used for the languages that doesn't have a custom nor a default analyzer. -

    - -

    Linguistics key

    - -

    - Linguistics keys identify a configuration of text analysis. It can be made of two parts, - separated by a semicolon, though you can omit one or the other. The two parts are: -

    -

    -

    - The language key, in turn, has 2 parts: a mandatory - language code and an optional stemming mode. - The format is LANGUAGE_CODE[/STEM_MODE]. - There are 5 stemming modes: NONE, DEFAULT, ALL, SHORTEST, BEST (they can be specified in the field schema). -

    - -

    Examples of linguistics key:

    -
      -
    • - profile=whitespaceLowercase: a profile that applies to all languages. You can bind it to - different fields by specifying their linguistics profiles in the schema. -
    • -
    • - profile=whitespaceLowercase;language=en: a profile that applies to the English language. - You'd still bind it to fields via their linguistics profiles in the schema, - but it will only be applied to the English texts (either at indexing or query time). -
    • -
    • - en: English language: applies to all English texts where no profile is specified - (in the schema or in the query). -
    • -
    • - en/BEST: English language with the BEST stemming mode. Like the previous example, - but only applies when stemming is set to BEST. -
    • -
    - -{% include note.html content='You can use different profiles for document fields and query strings. -See Different processing for query strings and the -multiple-profiles sample app for more information.' %} - -

    Customizing text analysis

    - -

    - Lucene linguistics provides multiple ways to customize text analysis per language: -

    -
      -
    • - LuceneLinguistics component configuration in the services.xml -
    • -
    • - ComponentsRegistry -
    • -
    - -

    LuceneLinguistics component configuration

    - -

    - In services.xml it is possible to construct an analyzer by providing - configuration for the - LuceneLinguistics component (from all text analysis components that are available on the classpath). - Example for the English language: -

    - -
    {% highlight xml %}
    -  
    -    
    -      lucene-linguistics
    -      
    -        
    -          
    -            standard
    -          
    -          
    -            
    -              stop
    -              
    -                en/stopwords.txt
    -                true
    -              
    -            
    -            
    -              englishMinimalStem
    -            
    -          
    -        
    -      
    -    
    -  
    -{% endhighlight %}
    - -

    Notes:

    -
      -
    • - item key="profile=standardStopStem;language=en" value is a linguistics key. -
    • -
    • - name values are the SPI names - of the text analysis components. You'll typically find them in the - Lucene analysis JavaDocs. - For example, the name stop along with other options can be found in the - StopFilterFactory JavaDoc. -
    • -
    • - The en/stopwords.txt file must be placed in your application package under - the lucene-linguistics directory, which is referenced by the configDir option. -
    • -
    • - If configDir is not provided the files must be on the classpath. -
    • -
    - -

    Components registry

    - -

    - The ComponentsRegistry - mechanism can be used to set a Lucene Analyzer for a language. -

    - -

    -

    - -
    {% highlight xml %}
    -
    -{% endhighlight %}
    - -

    - Where: -

    -
      -
    • - id must be a linguistics key; -
    • -
    • - class is the implementation class that extends the `Analyzer` class; -
    • -
    • - bundle is a name of the application package as specified in the pom.xml - (or can be any bundle added to your components dir that contains the class). -
    • -
    - -

    - For this to work, the class must provide only a constructor without arguments. -

    - -

    - In case your analyzer class needs some initialization you must wrap the analyzer into a class - that implements the Provider<Analyzer> class. -

    - -

    Custom text analysis components

    - -

    - The text analysis components are loaded via Java Service provider interface (SPI). -

    - -

    - To use an external library that is properly prepared it is enough to add the - library to the application package as a Maven dependency. -

    - -

    - In case you need to create a custom component the steps are: -

    -
      -
    1. Implement a component in a Java class
    2. -
    3. Register the component class in the (e.g. a custom token filter) META-INF/services/org.apache.lucene.analysis.TokenFilterFactory - file that is on the classpath. -
    4. -
    - -

    Language Detection

    - -

    - Lucene Linguistics doesn't provide language detection. - This means that for both feeding and searching you should provide a - language parameter. -

    - -

    Indexing all stems

    - -

    - Some analyzers expand the input text into multiple tokens on the same position. - For example, those based on the NGramTokenFilter. - Here's a sample analyzer configuration: -

    - -
    {% highlight xml %}
    -
    -  
    -    whitespace
    -  
    -  
    -    
    -      nGram
    -      
    -        2
    -        2
    -      
    -    
    -  
    -
    -{% endhighlight %}
    - -

    - This will take a text like dog and produce do and og as tokens, plus (by default) the original dog. - However, Vespa only takes the first token (do) and writes it to the index, ignoring the other "stems". As a result, - a search for og will not match documents that contain dog, which is the whole point of using letter n-grams. -

    - -

    - To index all stems, you can use the stemming parameter in the schema definition of your field: -

    - -
    -field title_grams type string {
    -  indexing: summary | index
    -  linguistics {
    -      profile: ngram
    -  }
    -  stemming: multiple
    -}
    -
    - -

    - Now, Vespa will index all stems, and a search for og will match documents that contain dog. -

    - -{% include note.html content='Queries look for all stems by default (regardless of the schema configuration). For example, a search for -dog would expand to do and og as well, looking for all three terms.' %} - - -

    Recipe: autocomplete with edge n-grams

    - -

    - Edge n-grams turn a term into its growing prefixes — coffee becomes - c, co, cof, coff, coffe, coffee — - which is a common way to implement prefix matching and search-as-you-type. Reach for Lucene Linguistics edge n-grams - when you also need text normalization that the built-in - gram match does not provide, for example - mapping & to and so that "Barnes & Noble" also matches a search for - and. If you do not need normalization, prefer the built-in - gram matching (see the - search-as-you-type - sample app) — it grams the query and the document consistently and avoids the pitfalls below. -

    - -

    - Getting edge n-grams right with Lucene Linguistics requires three pieces to line up. The two warnings below - are the mistakes that are easy to make and hard to spot. -

    -
      -
    1. Generate the grams with edgeNGram as a token filter, not a tokenizer.
    2. -
    3. Add stemming: multiple so that - all grams are indexed (the grams share a token position — see - Indexing all stems above).
    4. -
    5. Use a separate, non-gramming profile for the query string (bound in the schema with - profile.index / profile.search).
    6. -
    - -

    Indexing: generate the grams

    - -{% include warning.html content='Do not combine a length-changing -CharFilter -(for example a patternReplace that rewrites & to and) with an n-gram -tokenizer such as edgeNGram. The CharFilter rewrites character offsets, which makes the -n-gram tokenizer collapse several grams onto the same token position; only the first of those is then indexed, so grams -go silently missing. Generate n-grams with a token filter (after a regular tokenizer) instead, as shown -below.' %} - -

    - Put the n-gram generation last in the tokenFilters chain, after a normal tokenizer and the - normalization filters. The standard tokenizer splits on whitespace and punctuation so each word is - grammed on its own; use the keyword tokenizer instead if you want prefixes of the whole field value. -

    - -
    {% highlight xml %}
    -
    -  
    -    standard
    -  
    -  
    -    
    -      patternReplace
    -      
    -        &
    -        and
    -      
    -    
    -  
    -  
    -    asciiFolding
    -    lowercase
    -    
    -      edgeNGram
    -      
    -        2
    -        20
    -      
    -    
    -  
    -
    -{% endhighlight %}
    - -

    - The matching field must set stemming: multiple, otherwise only the first (shortest) gram of each word - is written to the index: -

    - -
    -field name type string {
    -    indexing: index | summary
    -    linguistics {
    -        profile: autocomplete
    -    }
    -    stemming: multiple
    -}
    -
    - -{% include note.html content='Verify what gets indexed with a -tokens document summary. In that output a -nested array represents several tokens that share one position (the grams of one word) — that is expected, -not a bug.' %} - -

    - Keep minGramSize at 2 or higher unless you really need single-character prefixes: a - minGramSize of 1 makes every term emit a one-character gram, so those posting lists grow to - cover a large fraction of the corpus. -

    - -

    Querying: do not gram the query string

    - -{% include warning.html content='Do not apply the n-gram analyzer to the query string. The query term is itself -expanded into its grams — coffee becomes WORD_ALTERNATIVES [co, cof, coff, coffe, coffee], -which is an OR — so the query matches any document containing any of those grams. A -search for coffee would then also match "cottage" through the shared gram co, and the -smaller the minGramSize the worse this gets. Gram at indexing time only, and analyze the query with a -non-gramming profile.' %} - -

    - Define a second profile that is identical to the indexing one but without the edgeNGram token - filter, so a query term stays a single token: -

    - -
    {% highlight xml %}
    -
    -  
    -    standard
    -  
    -  
    -    
    -      patternReplace
    -      
    -        &
    -        and
    -      
    -    
    -  
    -  
    -    asciiFolding
    -    lowercase
    -  
    -
    -{% endhighlight %}
    - -

    - Bind it as the query-side profile on the field by expanding profile into index and - search (see - Different processing for query strings). This - keeps the asymmetry in the schema, so ordinary queries do the right thing without any per-query annotation: -

    - -
    -field name type string {
    -    indexing: index | summary
    -    linguistics {
    -        profile {
    -            index: autocomplete
    -            search: autocomplete_query
    -        }
    -    }
    -    stemming: multiple
    -}
    -
    - -

    - A search for coffee now stays the single token coffee and matches only terms that start with - coffee, while the document side still holds every gram: -

    - -
    -where name contains @query
    -
    - -

    - You can also override the profile per query with - grammar.profile (with grammar: 'linguistics' so no - extra tokenization is layered on top; works with text() and - userInput()) — handy when you do not want to change the - schema. See Overriding profile for query strings: -

    - -
    -where name contains ({grammar: 'linguistics', grammar.profile: 'autocomplete_query'}text(@query))
    -
    \ No newline at end of file diff --git a/en/linguistics/lucene-linguistics.mdx b/en/linguistics/lucene-linguistics.mdx new file mode 100644 index 0000000000..22969ccab1 --- /dev/null +++ b/en/linguistics/lucene-linguistics.mdx @@ -0,0 +1,214 @@ +--- +title: "Lucene Linguistics" +--- + +Lucene Linguistics is a custom [linguistics](/en/linguistics/linguistics) implementation of the [Apache Lucene](https://lucene.apache.org) library. It provides a Lucene analyzer to handle text processing for a language with an optional variation per [stemming mode](https://github.com/vespa-engine/vespa/blob/master/linguistics/src/main/java/com/yahoo/language/process/StemMode.java). + +Check [sample apps](https://github.com/vespa-engine/sample-apps/tree/master/examples/lucene-linguistics) to get started. + +## Crash course to Lucene text analysis + +Lucene [text analysis](https://lucene.apache.org/core/9_8_0/core/org/apache/lucene/analysis/package-summary.html) is a process of converting text into searchable tokens. This text analysis consists of a series of components applied to the text in order: + +- [CharFilters](https://lucene.apache.org/core/9_8_0/core/org/apache/lucene/analysis/CharFilter.html): transform the text before it is tokenized, while providing corrected character offsets to account for these modifications. +- [Tokenizers](https://lucene.apache.org/core/9_8_0/core/org/apache/lucene/analysis/Tokenizer.html): responsible for breaking up incoming text into tokens. +- [TokenFilters](https://lucene.apache.org/core/9_8_0/core/org/apache/lucene/analysis/TokenFilter.html): responsible for modifying tokens that have been created by the Tokenizer. + +A specific configuration of the above components is wrapped into an [Analyzer](https://lucene.apache.org/core/9_8_0/core/org/apache/lucene/analysis/Analyzer.html) object. + +The text analysis works as follows: +1. All char filters are applied in the specified order on the entire text string +2. Token filters in the specified order are applied on each token. + +## Defaults language analysis + +Lucene Linguistics out-of-the-box exposes the analysis components provided by the [lucene-core](https://lucene.apache.org/core/9_8_0/core/index.html) and the [lucene-analysis-common](https://lucene.apache.org/core/9_8_0/analysis/common/index.html) libraries. Other libraries with Lucene text analysis components (e.g. [analysis-kuromoji](https://lucene.apache.org/core/9_8_0/analysis/kuromoji/index.html)) can be added to the application package as a Maven dependency. + +Lucene Linguistics out-of-the-box provides analyzers for 40 languages: + +- Arabic +- Armenian +- Basque +- Bengali +- Bulgarian +- Catalan +- Chinese +- Czech +- Danish +- Dutch +- English +- Estonian +- Finnish +- French +- Galician +- German +- Greek +- Hindi +- Hungarian +- Indonesian +- Irish +- Italian +- Japanese +- Korean +- Kurdish +- Latvian +- Lithuanian +- Nepali +- Norwegian +- Persian +- Portuguese +- Romanian +- Russian +- Serbian +- Spanish +- Swedish +- Tamil +- Telugu +- Thai +- Turkish + +The Lucene [StandardAnalyzer](https://lucene.apache.org/core/9_8_0/core/org/apache/lucene/analysis/standard/StandardAnalyzer.html) is used for the languages that doesn't have a custom nor a default analyzer. + +## Linguistics key + +Linguistics keys identify a configuration of text analysis. It can be made of two parts, separated by a semicolon, though you can omit one or the other. The two parts are: + +- A [linguistics profile](/en/linguistics/linguistics#linguistics-profiles). +- A language key. + +The language key, in turn, has 2 parts: a mandatory [language code](https://github.com/vespa-engine/vespa/blob/master/linguistics/src/main/java/com/yahoo/language/Language.java) and an optional stemming mode. The format is `LANGUAGE_CODE[/STEM_MODE]`. There are 5 stemming modes: `NONE, DEFAULT, ALL, SHORTEST, BEST` (they can be specified in the [field schema](/en/reference/schemas/schemas#stemming)). + +Examples of linguistics key: + +- `profile=whitespaceLowercase`: a profile that applies to all languages. You can bind it to different fields by specifying their [linguistics profiles](/en/linguistics/linguistics#linguistics-profiles) in the schema. +- `profile=whitespaceLowercase;language=en`: a profile that applies to the English language. You'd still bind it to fields via their [linguistics profiles](/en/linguistics/linguistics#linguistics-profiles) in the schema, but it will only be applied to the English texts (either at indexing or query time). +- `en`: English language: applies to all English texts where no profile is specified (in the schema or [in the query](/en/linguistics/linguistics#overriding-profile-for-query-strings)). +- `en/BEST`: English language with the `BEST` stemming mode. Like the previous example, but only applies when [stemming](/en/reference/schemas/schemas#stemming) is set to `BEST`. + + + **Note:** You can use different profiles for document fields and query strings. See [Different processing for query strings](/en/linguistics/linguistics#different-processing-for-query-strings) and the [multiple-profiles sample app](https://github.com/vespa-engine/sample-apps/tree/master/examples/lucene-linguistics/multiple-profiles) for more information. + + +## Customizing text analysis + +Lucene linguistics provides multiple ways to customize text analysis per language: + +- `LuceneLinguistics` component configuration in the `services.xml` +- `ComponentsRegistry` + +### LuceneLinguistics component configuration + +In `services.xml` it is possible to construct an analyzer by providing [configuration for the](https://github.com/vespa-engine/vespa/blob/master/lucene-linguistics/src/main/resources/configdefinitions/lucene-analysis.def)`LuceneLinguistics` component (from all text analysis components that are available on the classpath). Example for the English language: + +```xml + + + lucene-linguistics + + + + standard + + + + stop + + en/stopwords.txt + true + + + + englishMinimalStem + + + + + + +``` + +Notes: + +- `item key="profile=standardStopStem;language=en"` value is a [linguistics key](#linguistics-key). +- `name` values are the [SPI names](https://docs.oracle.com/en/java/javase/17/docs/api/java.naming/javax/naming/spi/package-summary.html) of the text analysis components. You'll typically find them in the [Lucene analysis JavaDocs](https://lucene.apache.org/core/9_11_1/analysis/common/allclasses-index.html). For example, the name `stop` along with other options can be found in the [StopFilterFactory JavaDoc](https://lucene.apache.org/core/9_11_1/analysis/common/org/apache/lucene/analysis/core/StopFilterFactory.html). +- The `en/stopwords.txt` file must be placed in your application package under the `lucene-linguistics` directory, which is referenced by the `configDir` option. +- If `configDir` is not provided the files must be on the classpath. + +### Components registry + +The [ComponentsRegistry](/en/applications/dependency-injection#depending-on-all-components-of-a-specific-type) mechanism can be used to set a Lucene Analyzer for a language. + +```xml + +``` + +Where: + +- `id` must be a [linguistics key](#linguistics-key); +- `class` is the implementation class that extends the `Analyzer` class; +- `bundle` is a name of the application package as specified in the `pom.xml` (or can be any bundle added to your `components` dir that contains the class). + +For this to work, the class must provide **only** a constructor without arguments. + +In case your analyzer class needs some initialization you must wrap the analyzer into a class that implements the `Provider` class. + +### Custom text analysis components + +The text analysis components are loaded via Java Service provider interface ([SPI](https://www.baeldung.com/java-spi)). + +To use an external library that is properly prepared it is enough to add the library to the application package as a Maven dependency. + +In case you need to create a custom component the steps are: + +1. Implement a component in a Java class +2. Register the component class in the (e.g. a custom token filter) `META-INF/services/org.apache.lucene.analysis.TokenFilterFactory` file that is on the classpath. + +## Language Detection + +Lucene Linguistics doesn't provide language detection. This means that for both feeding and searching you should provide a [language parameter](/en/reference/api/query#model.language). + +## Indexing all stems + +Some analyzers expand the input text into multiple tokens on the same position. For example, those based on the [NGramTokenFilter](https://lucene.apache.org/core/9_11_1/analysis/common/org/apache/lucene/analysis/ngram/NGramTokenFilter.html). Here's a sample analyzer configuration: + +```xml + + + whitespace + + + + nGram + + 2 + 2 + + + + +``` + +This will take a text like `dog` and produce `do` and `og` as tokens, plus (by default) the original `dog`. However, Vespa only takes the first token (`do`) and writes it to the index, ignoring the other "stems". As a result, a search for `og` will not match documents that contain `dog`, which is the whole point of using letter n-grams. + +To index all stems, you can use the [stemming](/en/reference/schemas/schemas#stemming) parameter in the schema definition of your field: + +```yaml +field title_grams type string { + indexing: summary | index + linguistics { + profile: ngram + } + stemming: multiple +} +``` + +Now, Vespa will index all stems, and a search for `og` will match documents that contain `dog`. + + + **Note:** Queries look for all stems by default (regardless of the schema configuration). For example, a search for `dog` would expand to `do` and `og` as well, looking for all three terms. + \ No newline at end of file diff --git a/en/linguistics/query-rewriting.html b/en/linguistics/query-rewriting.html deleted file mode 100644 index cc6e7d7905..0000000000 --- a/en/linguistics/query-rewriting.html +++ /dev/null @@ -1,331 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Query Rewriting" -redirect_from: -- /en/query-rewriting.html ---- - -

    -A search application can improve the quality by interpreting the intended meaning of the user queries. -Once the meaning is guessed, -the query can be rewritten to one that will satisfy the user better than the raw query. -Vespa includes a query rewriting language which makes it easy to use query -rewriting to understand and act upon the query semantics. -

    -These query rewriting techniques can be combined to improve the search experience: -

    -
      -
    • Query focusing: Decide a field to search for a term
    • -
    • Query enhancing: Add additional terms which improves the query
    • -
    • Stopwords: Remove terms which hurts recall or precision - - - example
    • -
    • Synonyms: Replace terms or phrases by others
    • -
    -

    -Query rewriting done by semantic rules or searchers. -Semantic rules is a simple production rule language that operates on queries. -For more complex query rewriting logic which could not be handled by simple rules, -one could create a rewriting searcher making use of the query rewriting framework. -

    - - - -

    EQUIV

    -

    -EQUIV is a query operator that can be used to add synonyms -for words where the various synonyms should be equivalent - example: -

    -
      -
    • The user query is (used AND automobile)
    • -
    • automobile is a synonym for car (from a dictionary)
    • -
    • Rewrite the query to (used AND (automobile EQUIV car))
    • -
    • automobile or car are here equivalent - - the query shall behave as if all occurrences of car in the document corpus - had been replaced by automobile
    • -
    -

    -See the reference -for differences between OR and EQUIV. -In many cases it might be better to use OR instead of EQUIV. -Example Snoop Dogg: -

    -
    -"Snoop" EQUIV "Snoop Doggy Dogg" EQUIV "Snoop Lion" EQUIV "Calvin Broadus" EQUIV "Calvin Cordozar Broadus Junior" 
    -
    -

    -However, Snoop is used by other people - -so matching that alone is not a sure hit for the correct entity, -and finding more than one of the synonyms in the same text gives better confidence. -This is exactly what OR does: -

    -
    -"Snoop"!20 OR "Snoop Doggy Dogg"!90 OR "Snoop Lion"!75 OR "Calvin Broadus"!60 OR "Calvin Cordozar Broadus Junior"!100
    -
    -

    -Use lower weights on the alternatives with less confidence. -If it looks like the many words and phrases inside the OR -overwhelms other words in the query, giving even lower weights may be useful, -for example making the sum of weights 100 - the default weight for just one alternative. -

    -The decision to use EQUIV must be taken by application-specific dictionary or linguistics use. -This can be done using YQL -or from a container plugin (example - -EquivSearcher.java) where the query object is manipulated as follows: -

    -
      -
    1. Find a word item in the query
    2. -
    3. Check that an EQUIV can be used in that place - (see limitations)
    4. -
    5. Find the synonyms in the dictionary
    6. -
    7. Make an EquivItem with the synonyms (and the original word) as children
    8. -
    9. Replace the original WordItem with the new EquivItem
    10. -
    - - - -

    Rules

    -

    -A simple semantic rule looks like: -

    -
    -lotr -> lord of the rings;
    -
    -

    -This means that whenever the term lotr is encountered in a query, -replace it by the terms lord of the rings. -Rules can also refer to conditions, and the produced terms can be a -modified version of whatever is matched instead of a concrete term: -

    -
    -[brand] -> company:[brand];
    -[brand] :- sony, dell, ibm, hp;
    -
    -

    -This rule says that, whenever the condition named brand is matched, -replace the matched term(s) by the same term(s) searching the company field. -In addition, the brand condition is defined to match any of a list of brands. -Note how -> means a replacing production rule, -:- means a condition and , separates alternatives. -

    -It is also possible to do grouping using parentheses, -list multiple terms which must be matched in sequence, -and to write adding production rules using +> instead of ->. -Terms are by default added using the query default (as if they were written in the search box), -but it is also possible to force them to be AND, OR, NOT or RANK using respectively -+, ?, - and $. -Here is a more complex rule illustrating this: -

    -
    -[destination] (in, by, at, on) [place] +> $name:[destination]
    -
    -

    -This rule boosts matches which has a destination which matches the -name field followed by a preposition and a place -(the definition of the destination and place conditions are not shown). -This is achieved by adding a RANK term - -a term which do not impact whether a document is matched or not, -but which adds a relevancy boost if it is. -

    -The complete syntax of this language is found in the -semantic rules reference. -

    - - - -

    Rule bases

    -

    -A collection of rules used together are collected in a rule base - -a text file containing rules and conditions, with file suffix .sr (for semantic rules). -Example: -

    -
    -# Replacements
    -lotr -> lord of the rings;
    -colour -> color;
    -audi -> skoda;
    -
    -# Stopwords
    -[stopword] -> ;  # (Replace them by nothing)
    -[stopword] :- and, or, the, be;
    -
    -# Focus brands to the brand field. If we think the brand
    -# field has high quality data, we can replace.  We use the same name
    -# for the condition and the field, but this is not necessary.
    -[brand] :- brand:[brand];
    -[brand] :- sony, dell, ibm, hp;
    -
    -# Boost recognized categories
    -[category] +> $category:[category];
    -[category] :- laptop, digital camera, camera;
    -
    -

    -The rules in a rule base is evaluated in order from the top down. -A rule will be matched as many times as is possible before evaluation moves on to the next query. -So the query colour colour will be rewritten to color color -before moving on to the next rule. -

    - - - -

    Configuration

    -

    -A rule base file is placed in the rules/ directory under -the application package, -and will be named as the file, excluding the .sr suffix. -E.g. if the rules above are saved to [my-application]/rules/example.sr, -the rules base available is named example. -

    -To make a rule base be used by default in queries, -add @default on a separate line to the rule base. -To deactivate the default rules, -add rules.off to the query. -

    -The rules can safely be updated at any time by running vespa prepare again. -If there are errors in the rule bases, they will not be updated, -and the errors will be reported on the command line. -

    -To trace what the rules are doing, -add tracelevel.rules=[number] to the query. -

    - - - -

    Using multiple rule bases

    -

    -It is possible to place multiple rule bases in the [my-application]/rules/ -and choose between them in the query. -Rules may also include each other. -This is useful to organize larger sets of rules, -to experiment with variants of the rule set in new bases which includes the standard base, -or to use different sets of rules for different use cases. -

    -To include one rule base in another, -add @include(rulebasename) on a separate line, -where rulebasename is the file name (with or without the .sr). -The result will be the same as if the included rule base were copied in -to the location of the include line. -If a condition is defined in both bases, the one from the including base will be used. -It is also possible to refer to the same-named condition in an included rule base -using the @super directive as a condition. -For example, this rule base adds some more categories to the category definition -in the example.sr above: -

    -
    -@include(example)
    -
    -# Category becomes laptop, digital camera, camera, palmtop, phone
    -[category] :- @super, palmtop, phone;
    -
    -

    - Multiple rule bases can be included, and included rule bases can themselves have included rule bases. - All the rule bases included in the application package will be available when making queries. - One of the rule bases can be made the default by adding @default on a separate line in the rule base. - To use another rule base, - add rules.rulebase=[rulebasename] to the query. -

    - - - -

    Using a finite state automaton

    -

    -Finite state automata (FSA) are efficient in storing and making lookups in large string lists. -A rule base can be compiled into an FSA to increase performance. -An automaton is created from a text file which lists the condition terms to match -and the condition names separated by a tab (by default). -The name of the condition can be followed by a semicolon and additional data which will be ignored. -

    -This automaton source file defines the same as the -stopword and brand conditions in the example rule base: -

    -
    -and   stopword
    -or    stopword
    -be    stopword
    -the   stopword
    -sony  brand
    -dell  brand
    -ibm   brand; This text is ignored
    -hp    brand
    -
    -

    Use vespa-makefsa to compile the automaton file:

    -
    -$ vespa-makefsa -t sourcefile.txt targetfile.fsa
    -
    -

    -The target file is used from a rule base by adding @automata(automatonfile) -on a separate line in the rule base file (the file path is relative to $VESPA_HOME). -Automata-files must be stored on all container nodes. -

    -Note that automata are not included in others, -so a rule base including another which uses an automaton -must also declare to use the same automaton -(or an automaton containing any changes from the automaton of the included base). -

    - - - -

    Query phrasing

    -

    -Users search for phrases like New York, Rolling Stones, -The Who, or daily horoscopes. -Considering the latter, most of the time the query string will look like this: -

    -
    -/search/?query=daily horoscopes&…
    -
    -

    -This is actually a search for documents where both daily and horoscopes match, -but not necessarily documents with the exact phrase "daily horoscopes". -PhrasingSearcher is a Searcher that compares queries with a list of common phrases, -and replaces two search terms with a phrase. -If "daily horoscopes" is a common phrase, the above query becomes: -

    -
    -/search/?query="daily horoscopes"&…
    -
    -

    -The PhrasingSearcher must be configured with a list of common phrases, -compiled into a finite state automation (FSA). The phrase list must be: -

    -
      -
    • all lowercase
    • -
    • sorted alphabetically
    • -
    -

    Example:

    -
    -$ perl -ne 'print lc' listofphrasestextfile.unsorted.mixedcase | sort > listofphrasestextfile
    -
    -

    -Note that the Perl command to convert the text file to lowercase does -not handle non-ASCII characters well (this is just an example). -If the list of phrases is e.g. UTF-8 encoded and/or contains non-English characters, -double-check that the resulting file is correct. -

    -Use vespa-makefsa -to compile the list into an FSA file: -

    -
    -$ vespa-makefsa listofphrasestextfile phrasefsa
    -
    -

    -Put the file on all container nodes, configure the location -and deploy: -

    -
    -<container id="default" version="1.0">
    -    <config name="container.qr-searchers">
    -        <com>
    -            <yahoo>
    -                <prelude>
    -                    <querytransform>
    -                        <PhrasingSearcher>
    -                            <automatonfile>/path/phrasefsa</automatonfile>
    -                        </PhrasingSearcher>
    -                    </querytransform>
    -                </prelude>
    -            </yahoo>
    -        </com>
    -    </config>
    -
    diff --git a/en/linguistics/query-rewriting.mdx b/en/linguistics/query-rewriting.mdx new file mode 100644 index 0000000000..0b76fb900d --- /dev/null +++ b/en/linguistics/query-rewriting.mdx @@ -0,0 +1,204 @@ +--- +title: "Query Rewriting" +--- + + +A search application can improve the quality by interpreting the intended meaning of the user queries. Once the meaning is guessed, the query can be rewritten to one that will satisfy the user better than the raw query. Vespa includes a query rewriting language which makes it easy to use query rewriting to understand and act upon the query semantics. + +These query rewriting techniques can be combined to improve the search experience: + +- Query focusing: Decide a field to search for a term +- Query enhancing: Add additional terms which improves the query +- Stopwords: Remove terms which hurts recall or precision - [example](https://github.com/vespa-cloud/cord-19-search/blob/main/src/main/java/ai/vespa/example/cord19/searcher/BoldingSearcher.java) +- Synonyms: Replace terms or phrases by others + +Query rewriting done by _semantic rules_ or _searchers_. Semantic rules is a simple production rule language that operates on queries. For more complex query rewriting logic which could not be handled by simple rules, one could create a rewriting searcher making use of the query rewriting framework. + +## EQUIV + +EQUIV is a query operator that can be used to add synonyms for words where the various synonyms should be equivalent - example: + +- The user query is `(used AND automobile)` +- _automobile_ is a synonym for _car_ (from a dictionary) +- Rewrite the query to `(used AND (automobile EQUIV car))` +- _automobile_ or _car_ are here equivalent - the query shall behave as if all occurrences of _car_ in the document corpus had been replaced by _automobile_ + +See the [reference](/en/reference/querying/yql#equiv) for differences between OR and EQUIV. In many cases it might be better to use OR instead of EQUIV. Example _Snoop_ Dogg: + +```sql +"Snoop" EQUIV "Snoop Doggy Dogg" EQUIV "Snoop Lion" EQUIV "Calvin Broadus" EQUIV "Calvin Cordozar Broadus Junior" +``` + +However, _Snoop_ is used by other people - so matching that alone is not a sure hit for the correct entity, and finding more than one of the synonyms in the same text gives better confidence. This is exactly what OR does: + +```sql +"Snoop"!20 OR "Snoop Doggy Dogg"!90 OR "Snoop Lion"!75 OR "Calvin Broadus"!60 OR "Calvin Cordozar Broadus Junior"!100 +``` + +Use lower weights on the alternatives with less confidence. If it looks like the many words and phrases inside the OR overwhelms other words in the query, giving even lower weights may be useful, for example making the sum of weights 100 - the default weight for just one alternative. + +The decision to use EQUIV must be taken by application-specific dictionary or linguistics use. This can be done using [YQL](/en/reference/querying/yql#equiv) or from a container plugin (example [EquivSearcher.java](https://github.com/vespa-engine/sample-apps/blob/master/album-recommendation-java/app/src/main/java/ai/vespa/example/album/EquivSearcher.java)) where the query object is manipulated as follows: + +1. Find a word item in the query +2. Check that an EQUIV can be used in that place (see [limitations](/en/reference/querying/yql#equiv)) +3. Find the synonyms in the dictionary +4. Make an `EquivItem` with the synonyms (and the original word) as children +5. Replace the original `WordItem` with the new `EquivItem` + +## Rules + +A simple semantic rule looks like: + +```sql +lotr -> lord of the rings; +``` + +This means that whenever the term _lotr_ is encountered in a query, replace it by the terms _lord of the rings_. Rules can also refer to conditions, and the produced terms can be a modified version of whatever is matched instead of a concrete term: + +```sql +[brand] -> company:[brand]; +[brand] :- sony, dell, ibm, hp; +``` + +This rule says that, whenever the condition named _brand_ is matched, replace the matched term(s) by _the same term(s)_ searching the _company_ field. In addition, the _brand_ condition is defined to match any of a list of brands. Note how `->` means a replacing production rule, `:-` means a condition and `,` separates alternatives. + +It is also possible to do grouping using parentheses, list multiple terms which must be matched in sequence, and to write _adding_ production rules using `+>` instead of `->`. Terms are by default added using the query default (as if they were written in the search box), but it is also possible to force them to be AND, OR, NOT or RANK using respectively `+`, `?`, `-` and `$`. Here is a more complex rule illustrating this: + +```sql +[destination] (in, by, at, on) [place] +> $name:[destination] +``` + +This rule boosts matches which has a destination which matches the _name_ field followed by a preposition and a place (the definition of the _destination_ and _place_ conditions are not shown). This is achieved by adding a RANK term - a term which do not impact whether a document is matched or not, but which adds a relevancy boost if it is. + +The complete syntax of this language is found in the [semantic rules reference](/en/reference/querying/semantic-rules). + +## Rule bases + +A collection of rules used together are collected in a _rule base_ - a text file containing rules and conditions, with file suffix `.sr` (for semantic rules). Example: + +```sql +# Replacements +lotr -> lord of the rings; +colour -> color; +audi -> skoda; + +# Stopwords +[stopword] -> ; # (Replace them by nothing) +[stopword] :- and, or, the, be; + +# Focus brands to the brand field. If we think the_brand_# field has high quality data, we can replace. We use the same name +# for the condition and the field, but this is not necessary. +[brand] :- brand:[brand]; +[brand] :- sony, dell, ibm, hp; + +# Boost recognized categories +[category] +> $category:[category]; +[category] :- laptop, digital camera, camera; +``` + +The rules in a rule base is evaluated in order from the top down. A rule will be matched as many times as is possible before evaluation moves on to the next query. So the query _colour colour_ will be rewritten to _color color_ before moving on to the next rule. + +## Configuration + +A rule base file is placed in the `rules/` directory under the [application package](/en/reference/applications/application-packages), and will be named as the file, excluding the `.sr` suffix. E.g. if the rules above are saved to `[my-application]/rules/example.sr`, the rules base available is named `example`. + +To make a rule base be used by default in queries, add `@default` on a separate line to the rule base. To deactivate the default rules, add [rules.off](/en/reference/api/query#rules.off) to the query. + +The rules can safely be updated at any time by running `vespa prepare` again. If there are errors in the rule bases, they will not be updated, and the errors will be reported on the command line. + +To trace what the rules are doing, add [tracelevel.rules=[number]](/en/reference/api/query#tracelevel.rules) to the query. + +## Using multiple rule bases + +It is possible to place multiple rule bases in the `[my-application]/rules/` and choose between them in the query. Rules may also include each other. This is useful to organize larger sets of rules, to experiment with variants of the rule set in new bases which includes the standard base, or to use different sets of rules for different use cases. + +To include one rule base in another, add `@include(rulebasename)` on a separate line, where _rulebasename_ is the file name (with or without the _.sr_). The result will be the same as if the included rule base were copied in to the location of the `include` line. If a condition is defined in both bases, the one from the _including_ base will be used. It is also possible to refer to the same-named condition in an included rule base using the `@super` directive as a condition. For example, this rule base adds some more categories to the _category_ definition in the `example.sr` above: + +```sql +@include(example) + +# Category becomes laptop, digital camera, camera, palmtop, phone +[category] :- @super, palmtop, phone; +``` + +Multiple rule bases can be included, and included rule bases can themselves have included rule bases. All the rule bases included in the application package will be available when making queries. One of the rule bases can be made the default by adding `@default` on a separate line in the rule base. To use another rule base, add [rules.rulebase=[rulebasename]](/en/reference/api/query#rules.rulebase) to the query. + +## Using a finite state automaton + +_Finite state automata_ (FSA) are efficient in storing and making lookups in large string lists. A rule base can be compiled into an FSA to increase performance. An automaton is created from a text file which lists the condition terms to match and the condition names separated by a tab (by default). The name of the condition can be followed by a semicolon and additional data which will be ignored. + +This automaton source file defines the same as the _stopword_ and _brand_ conditions in the example rule base: + +```txt +and stopword +or stopword +be stopword +the stopword +sony brand +dell brand +ibm brand; This text is ignored +hp brand +``` + +Use [vespa-makefsa](/en/reference/operations/tools#vespa-makefsa) to compile the automaton file: + +```bash +$ vespa-makefsa -t sourcefile.txt targetfile.fsa +``` + +The target file is used from a rule base by adding _@automata(automatonfile)_ on a separate line in the rule base file (the file path is relative to _$VESPA\_HOME_). Automata-files must be stored on all container nodes. + +Note that automata are not included in others, so a rule base including another which uses an automaton must also declare to use the same automaton (or an automaton containing any changes from the automaton of the included base). + +## Query phrasing + +Users search for phrases like _New York_, _Rolling Stones_, _The Who_, or _daily horoscopes_. Considering the latter, most of the time the query string will look like this: + +```bash +/search/?query=daily horoscopes&… +``` + +This is actually a search for documents where both _daily_ and _horoscopes_ match, but not necessarily documents with the exact phrase _"daily horoscopes"_. PhrasingSearcher is a Searcher that compares queries with a list of common phrases, and replaces two search terms with a phrase. If _"daily horoscopes"_ is a common phrase, the above query becomes: + +```bash +/search/?query="daily horoscopes"&… +``` + +The PhrasingSearcher must be configured with a list of common phrases, compiled into a _finite state automation_ (FSA). The phrase list must be: + +- all lowercase +- sorted alphabetically + +Example: + +```bash +$ perl -ne 'print lc' listofphrasestextfile.unsorted.mixedcase | sort > listofphrasestextfile +``` + +Note that the Perl command to convert the text file to lowercase does not handle non-ASCII characters well (this is just an example). If the list of phrases is e.g. UTF-8 encoded and/or contains non-English characters, double-check that the resulting file is correct. + +Use [vespa-makefsa](/en/reference/operations/tools#vespa-makefsa) to compile the list into an FSA file: + +```bash +$ vespa-makefsa listofphrasestextfile phrasefsa +``` + +Put the file on all container nodes, configure the location and [deploy](/en/basics/applications): + +```xml + + + + + + + + /path/phrasefsa + + + + + + +``` + diff --git a/en/linguistics/troubleshooting-encoding.html b/en/linguistics/troubleshooting-encoding.html deleted file mode 100644 index 995e891251..0000000000 --- a/en/linguistics/troubleshooting-encoding.html +++ /dev/null @@ -1,122 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Troubleshooting character encoding" -redirect_from: -- /en/troubleshooting-encoding.html ---- - -

    - This document helps recognize the most common problems related to Unicode and I18N. -

    - UTF-8 is a Unicode specific encoding where each letter (code point) - is encoded as one to four 8 bit bytes. - The UTF-8 schema can technically use more bytes, - but Unicode is defined as having approximately 1 million code points - (partly on cause of limitations of UTF-16), - and more than four bytes are then never necessary. -

    - A string in Java is stored as UTF-16, a series of 16 bits char(acter)s. - All code points in Unicode base plane, the first 64k code points, - is represented as a single char, while higher code points is represented using surrogate pairs. - A surrogate pair is a pair of char from a reserved range. -

    - Accessing a code point in a Java string is done using e.g. String.codePointAt(), - which then returns a 32-bit integer representing the code point (basically UCS-4). - When traversing a string in Java, use codePointAt + offsetByCodePoints - or String.codePoints() or similar methods. - If your applications conceptually handles letters, - using String.charAt() will most of the time be wrong. - To calculate buffer sizes for UTF-8 buffers with UTF-16 inputs without doing speculative encoding, - Vespa has a toolbox, - - com.yahoo.text.Utf8, with static helper methods. -

    -

    If you are using python, use the following to remove control characters:

    -
    -def remove_control_characters(s):
    -    return "".join(ch for ch in s if unicodedata.category(ch)[0]!="C")
    -
    - - - -

    Visual pattern matching of encoding bugs

    - - - - - - - - - - - - - - - - - - - - - - - - -
    TransformationResult
    Inputhôtel
    Correctly URL quoted (Vespa always uses UTF-8 there)h%C3%B4tel
    Encoded as ISO-8859-1 (ISO Latin-1), then URL quotedh%F4tel
    Encoded as UTF-16 (as in Java strings), then URL quoted%00h%00%F4%00t%00e%00l
    For completeness, little endian UTF-16, including byte order marker%FF%FEh%00%F4%00t%00e%00l%00
    -

    - What we are looking for is single bytes outside ASCII, i.e. ordinal above 127. - Given UTF-8, there should always be sequences of two or more of these - when a code point is outside ASCII. - The first byte for each code point will have the two most significant bits set, - in other words hex C to hex F. - The rest of the bytes for that code point will have the most significant bit set, - and the second most unset, in other words hex 8 to hex B. -

    - From here, we move on to the two most common de-/encoding errors: -

    - - - - - - - - - - - - - - - - -
    ErrorHex dump of code pointsRendered
    UTF-8 input decoded as if it were ISO-8859-1h\xc3\xb4telhôtel
    UTF-8 input re-encoded as UTF-8, then decoded as UTF-8 againh\xc3\xb4telhôtel
    -

    - Note how these two bugs create exactly the same byte sequences. - This is because the first 256 code points of Unicode are identical to ISO-8859-1. - What we are looking for is line noise in-between normal ASCII, - as both ISO-8859-1 and Unicode are ASCII compatible. -

    - Trying to decode valid ISO-8859-1 input with a UTF-8 decoder will usually make - the decoder report the input as invalid if there are code points outside ASCII. - Valid ISO-8859-1 rarely end up conforming to the required bit patterns of valid UTF-8, - though it sometimes happens. -

    - Never try to debug encoding problems with a web browser. - Always use a hexdump tool. - xxd is a nice utility which is included with vim, - which avoids several of the endianness headaches associated with some UNIX alternatives. -

    - Also, remember Windows 1252 is not the same as ISO-8859-1. -

    - - - -

    JSON

    -

    - Use proper JSON - a common error is not stripping ASCII control characters from feed data. - See - stripInvalidCharacters for a utility function. -

    diff --git a/en/linguistics/troubleshooting-encoding.mdx b/en/linguistics/troubleshooting-encoding.mdx new file mode 100644 index 0000000000..a59bde60fb --- /dev/null +++ b/en/linguistics/troubleshooting-encoding.mdx @@ -0,0 +1,89 @@ +--- +title: "Troubleshooting character encoding" +--- + +This document helps recognize the most common problems related to Unicode and I18N. + +UTF-8 is a Unicode specific encoding where each letter (code point) is encoded as one to four 8 bit bytes. The UTF-8 schema can technically use more bytes, but Unicode is defined as having approximately 1 million code points (partly on cause of limitations of UTF-16), and more than four bytes are then never necessary. + +A string in Java is stored as UTF-16, a series of 16 bits char(acter)s. All code points in Unicode base plane, the first 64k code points, is represented as a single char, while higher code points is represented using surrogate pairs. A surrogate pair is a pair of char from a reserved range. + +Accessing a code point in a Java string is done using e.g. String.codePointAt(), which then returns a 32-bit integer representing the code point (basically UCS-4). When traversing a string in Java, use codePointAt + offsetByCodePoints or String.codePoints() or similar methods. If your applications conceptually handles letters, using String.charAt() will most of the time be wrong. To calculate buffer sizes for UTF-8 buffers with UTF-16 inputs without doing speculative encoding, Vespa has a toolbox, [com.yahoo.text.Utf8](https://github.com/vespa-engine/vespa/blob/master/vespajlib/src/main/java/com/yahoo/text/Utf8.java), with static helper methods. + +If you are using python, use the following to remove control characters: + +```python +def remove_control_characters(s): + return "".join(ch for ch in s if unicodedata.category(ch)[0]!="C") +``` + +## Visual pattern matching of encoding bugs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    TransformationResult
    Inputhôtel
    Correctly URL quoted (Vespa always uses UTF-8 there)h%C3%B4tel
    Encoded as ISO-8859-1 (ISO Latin-1), then URL quotedh%F4tel
    Encoded as UTF-16 (as in Java strings), then URL quoted%00h%00%F4%00t%00e%00l
    For completeness, little endian UTF-16, including byte order marker%FF%FEh%00%F4%00t%00e%00l%00
    + +What we are looking for is single bytes outside ASCII, i.e. ordinal above 127. Given UTF-8, there should always be sequences of two or more of these when a code point is outside ASCII. The first byte for each code point will have the two most significant bits set, in other words hex C to hex F. The rest of the bytes for that code point will have the most significant bit set, and the second most unset, in other words hex 8 to hex B. + +From here, we move on to the two most common de-/encoding errors: + + + + + + + + + + + + + + + + + + + + + +
    ErrorHex dump of code pointsRendered
    UTF-8 input decoded as if it were ISO-8859-1h\xc3\xb4telhôtel
    UTF-8 input re-encoded as UTF-8, then decoded as UTF-8 againh\xc3\xb4telhôtel
    + +Note how these two bugs create exactly the same byte sequences. This is because the first 256 code points of Unicode are identical to ISO-8859-1. What we are looking for is line noise in-between normal ASCII, as both ISO-8859-1 and Unicode are ASCII compatible. + +Trying to decode valid ISO-8859-1 input with a UTF-8 decoder will usually make the decoder report the input as invalid if there are code points outside ASCII. Valid ISO-8859-1 rarely end up conforming to the required bit patterns of valid UTF-8, though it sometimes happens. + +_Never_ try to debug encoding problems with a web browser. Always use a hexdump tool. `xxd` is a nice utility which is included with vim, which avoids several of the endianness headaches associated with some UNIX alternatives. + +Also, remember Windows 1252 is _not_ the same as ISO-8859-1. + +## JSON + +Use proper JSON - a common error is not stripping ASCII control characters from feed data. See [stripInvalidCharacters](https://github.com/vespa-engine/vespa/blob/master/vespajlib/src/main/java/com/yahoo/text/Text.java) for a utility function. diff --git a/en/modules/e-commerce/multi-currency-filtering.html b/en/modules/e-commerce/multi-currency-filtering.html deleted file mode 100644 index e8481dc8ae..0000000000 --- a/en/modules/e-commerce/multi-currency-filtering.html +++ /dev/null @@ -1,753 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: Multi-Currency Pricing -applies_to: enterprise -redirect_from: -- /en/cloud/multi-currency-filtering.html ---- - -

    - Vespa for e-commerce includes multi-currency pricing support - for e-commerce applications with global product catalogs - where products are priced in different currencies and sold across multiple markets. - Multi-currency pricing refers to presenting and working with prices in multiple currencies, - enabling applications to query, filter, and rank products using prices expressed in the buyer’s - preferred currency. This enables filtering by price range in any currency and using - converted prices in ranking, with automatic currency conversion when market-specific pricing is not available. -

    - - -

    Overview

    -

    - The multi-currency pricing feature supports: -

    -
      -
    • Per-market pricing - Define different prices for different markets on each product.
    • -
    • Keeping track of exchange rates - An N×N tensor mapping of currency-to-currency exchange rates is stored in a "forex" document, and can be updated at any time.
    • -
    • Automatic currency conversion - Fallback to a default market when no other market-specific price exists for the buyer's market.
    • -
    • Query-time filtering - Filter products by price range in any currency.
    • -
    • Ranking integration - Optional exposure of currency rates for use in ranking expressions (ranking on the computed price).
    • -
    - -

    - The implementation consists of two key components: -

    -
      -
    • MultiCurrencyFilterSearcher - A custom searcher that intercepts queries and dynamically filters products based on effective prices.
    • -
    • CachedForexRateService - A background service that stores exchange rates from the forex document in-memory for faster look-ups.
    • -
    - - - -

    Quick Start

    - -This quick start walks through an end-to-end example of enabling multi-currency pricing in a Vespa application. - -

    Define Schemas

    -

    - Create two schemas: one to store the forex rates, and one for products. If you already have an existing product schema, - you can reuse it as long as it contains the required fields described below. -

    - -

    Forex Schema

    -

    - The forex schema stores currency exchange rates as a tensor. Add a forex.sd schema to your application defined as: -

    - -
    {% highlight xml %}
    -schema forex {
    -    document forex {
    -        field timestamp type long {
    -            indexing: attribute | summary
    -        }
    -
    -        field rates type tensor(from{}, to{}) {
    -            indexing: attribute | summary
    -        }
    -    }
    -}
    -{% endhighlight %}
    - -

    Product Schema

    -

    - The product schema stores products with their seller currency and per-market prices. - The per_market_price array contains price overrides for specific markets, - with a DEFAULT market used as fallback. Every product must include a - DEFAULT entry, and all per_market_price.price values are expressed in the - document's seller_currency. -

    - -{% include note.html content='Every per-market override is stored in the seller's native currency, so the searcher can convert buyer price windows instead of rewriting stored prices.' %} - -{% include important.html content='fast-search on the struct-fields is recommended. -Without it, price filtering becomes significantly slower as the number of supported currencies grows. -rank: filter can be added for further optimization, but this depends on the specific ranking setup — -see rank: filter for details.' %} - -
    {% highlight xml %}
    -schema product {
    -    document product {
    -
    -        # Your existing fields above
    -
    -        field seller_currency type string {
    -            indexing: summary | attribute
    -        }
    -
    -        struct market_price {
    -            field market type string {}
    -            field price type double {}
    -        }
    -
    -        field per_market_price type array {
    -            indexing: summary
    -            summary: matched-elements-only
    -            struct-field market {
    -                indexing: attribute
    -                attribute: fast-search
    -            }
    -            struct-field price {
    -                indexing: attribute
    -                attribute: fast-search
    -            }
    -        }
    -
    -        # Your existing fields below
    -
    -    }
    -}
    -{% endhighlight %}
    - - -

    Configure Services

    -

    - Vespa only applies multi-currency filtering when the searcher and forex cache are wired into the container cluster. - Queries must pass through a chain that includes MultiCurrencyFilterSearcher, and the - ForexRateRetriever must read the global forex document via its own search chain. Add both chains and the two - components to your container definition in services.xml: -

    - -

    - Inside your existing <container> block, add the multi-currency chains and components: -

    - -
    {% highlight xml %}
    -
    -    
    -        
    -    
    -    
    -
    -
    -
    -
    -
    -{% endhighlight %}
    - -

    - In the <content> cluster ensure both document types are declared: -

    - -
    {% highlight xml %}
    -
    -    
    -    
    -
    -{% endhighlight %}
    - -

    - The retriever issues background queries through the forex-cache chain. If that chain is missing or restricts the wrong - document type, the cache never reaches READY and queries fail with “ensure exactly one forex document exists”. -

    - -

    - Putting it all together, a minimal services.xml might look like this: -

    - -
    {% highlight xml %}
    -
    -  
    -    
    -    
    -      
    -        
    -      
    -      
    -    
    -    
    -    
    -  
    -
    -  
    -    
    -      
    -      
    -    
    -    
    -  
    -
    -{% endhighlight %}
    - - -

    Feed Data

    - -

    Feed Forex Rates

    -

    - Feed a single forex document with ID id:forex:forex::forex containing all currency-to-currency exchange rates. - Include identity rates (e.g., USD→USD = 1.0) to avoid missing-cell lookups. - The timestamp field is required and must be updated with each rate change to ensure the cache picks up new rates. -

    - -{% include warning.html content='Exactly one global forex document must exist. If multiple documents are present, the retriever reports INVALID_FOREX_DOCUMENTS and the searcher returns error hits instructing you to keep a single forex document.' %} - -
    {% highlight json %}
    -{
    -  "put": "id:forex:forex::forex",
    -  "fields": {
    -    "timestamp": 1757385600,
    -    "rates": {
    -      "cells": [
    -        {"address": {"from": "USD", "to": "USD"}, "value": 1.0},
    -        {"address": {"from": "USD", "to": "EUR"}, "value": 0.92},
    -        {"address": {"from": "USD", "to": "GBP"}, "value": 0.78},
    -        {"address": {"from": "USD", "to": "NOK"}, "value": 10.50},
    -        {"address": {"from": "EUR", "to": "USD"}, "value": 1.09},
    -        {"address": {"from": "EUR", "to": "EUR"}, "value": 1.0},
    -        {"address": {"from": "EUR", "to": "GBP"}, "value": 0.85}
    -      ]
    -    }
    -  }
    -}
    -{% endhighlight %}
    - -

    Feed Products

    -

    - Feed products with their seller currency and per-market prices. Always include a DEFAULT market - entry as fallback. -

    - -
    {% highlight json %}
    -{
    -  "put": "id:product:product::sku-100",
    -  "fields": {
    -    "seller_currency": "USD",
    -    "per_market_price": [
    -      {"market": "DEFAULT", "price": 199.0},
    -      {"market": "EU", "price": 189.0},
    -      {"market": "UK", "price": 209.0},
    -      {"market": "NO", "price": 300.0}
    -    ]
    -  }
    -}
    -{% endhighlight %}
    - -{% include note.html content='If your product schema already includes identifiers or descriptive fields (such as product_id or product_name), include them in the feed as usual. The example keeps only the required currency fields so it works with the minimal schema shown above.' %} - - -

    Query with Price Filtering

    -

    - Use the following query parameters to filter products by price range in a specific market and currency: -

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    ParameterDescriptionExample
    ecommerce.multicurrency.marketTarget market codeNO, US, EU, NO-49, 27
    ecommerce.multicurrency.currencyTarget currency codeNOK, USD, EUR
    ecommerce.multicurrency.price-minMinimum price in target currency1000
    ecommerce.multicurrency.price-maxMaximum price in target currency1500
    ecommerce.multicurrency.enrichOptional: expose forex rates as query tensor for ranking. Defaults to falsetrue or false
    - -

    Example Query

    -
    {% highlight sh %}
    -$ vespa query \
    -    'yql=select * from product where true' \
    -    'searchChain=multi-currency-filter' \
    -    'ecommerce.multicurrency.market=NO' \
    -    'ecommerce.multicurrency.currency=NOK' \
    -    'ecommerce.multicurrency.price-min=1000' \
    -    'ecommerce.multicurrency.price-max=1500'
    -{% endhighlight %}
    - -

    - This query returns all products whose effective price in NOK (Norwegian Krone) for the Norwegian market - is between 1000 and 1500 NOK. The searcher will: -

    -
      -
    1. Check if the product has a market-specific price for NO
    2. -
    3. If yes, use that price directly
    4. -
    5. If no, convert the product's DEFAULT market price from the seller currency to NOK using forex rates
    6. -
    7. Keep only products within the specified price range
    8. -
    - - -

    Validation Rules

    -

    - The multi-currency searcher validates query parameters and returns an error if validation fails: -

    -
      -
    • Currency codes must be exactly 3 letters (ISO-4217 format, e.g., USD, EUR, NOK)
    • -
    • Market codes must be alphanumeric (e.g., US, NO, EU, NO-47, 13)
    • -
    • Price values must be valid numbers and non-negative
    • -
    • Price range: price-max must be greater than or equal to price-min
    • -
    • Currency availability: The requested currency must exist in the forex document
    • -
    - -

    - If any parameter is missing or invalid, the searcher will either skip filtering (for format issues) - or return an error result (for logical issues like invalid price ranges or unknown currencies). -

    - -{% include note.html content='When filtering is skipped due to malformed inputs, the searcher acts as a no-op and the trace log records the reason (for example, “currency failed ISO-4217 validation; skipping filter”). Use query tracing to confirm whether the multi-currency filter actually ran.' %} - - -

    Updating Forex Rates

    -

    - Forex rates can be updated at any time by feeding a new version of the forex document - with an updated timestamp field. The cache will automatically pick up the new rates - on its next refresh cycle (typically within seconds). -

    - -
    {% highlight sh %}
    -$ vespa feed <(echo '{
    -    "update": "id:forex:forex::forex",
    -    "fields": {
    -        "timestamp": {"assign": 1757472000},
    -        "rates": {
    -            "assign": {
    -                "cells": [...]
    -            }
    -        }
    -    }
    -}')
    -{% endhighlight %}
    - - - -

    How It Works

    - -

    Price Resolution Logic

    -

    - For each product, the effective price in the target currency is determined as follows: -

    -
      -
    1. Market-specific price: If the product has a price entry for the requested market, use that price directly
    2. -
    3. Currency conversion: Otherwise, use the DEFAULT market price and convert it from the seller currency to the target currency using forex rates
    4. -
    5. Price range filter: Keep only products whose effective price falls within the specified min/max range
    6. -
    - -

    Forex Cache

    -

    - The CachedForexRateService component maintains an in-memory cache of exchange rates - and refreshes them periodically from the forex document (id:forex:forex::forex). - This ensures low-latency access to forex rates during query processing. -

    - -

    Automatic Refresh

    -

    - The ForexRateRetriever component automatically refreshes forex rates every 10 seconds using a fixed schedule. - This cadence (10s interval, 5s retry window, 1s per attempt) is hard-coded in the provided component and cannot be tuned at deployment time. - Each refresh cycle: -

    -
      -
    • Queries the forex document using the forex-cache search chain
    • -
    • Validates the document has both rates (tensor) and timestamp (long) fields
    • -
    • Only applies updates if the timestamp is newer than the cached version
    • -
    • Retries within a 5-second budget if the first attempt fails
    • -
    - -

    Health States

    -

    - The forex service tracks its operational status with the following health states: -

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    StateDescriptionQuery Behavior
    READYForex rates loaded and service is operationalQueries with multi-currency filtering work normally
    UNINITIALIZEDNo forex document has been loaded yetQueries return error: "forex rate service not initialized"
    OUTAGERefresh failed but stale data exists (cache stays ready for re-use once the retriever succeeds again)Queries return error: "forex rate service temporarily unavailable (last refresh failed)"
    INVALID_FOREX_DOCUMENTSMultiple forex documents detected (expected exactly one)Queries return error: "ensure exactly one forex document exists"
    - -

    Error Handling

    -

    - When the service is not in READY state, queries with multi-currency filtering will: -

    -
      -
    • Return an empty result with an appropriate error message
    • -
    • Log detailed diagnostic information at appropriate trace levels
    • -
    • Continue retrying background refresh attempts until successful
    • -
    - -

    Performance

    -

    - Multi-currency price filtering is implemented as efficient query-time filter construction, - not result-time evaluation. This means Vespa can use its indexes to find matching products - without iterating through all documents. -

    - -

    How Filtering Works

    -

    - When a query with multi-currency parameters is received, the searcher: -

    -
      -
    1. Pre-computes price ranges: Converts the buyer's price range (e.g., 1000-1500 NOK) - into equivalent ranges for every seller currency using cached forex rates. - For example, if the forex cache has USD, EUR, and GBP, it computes what 1000-1500 NOK equals in each currency.
    2. -
    3. Builds structured query filters: Creates a query tree using Vespa's efficient query primitives: -
        -
      • SameElementItem - Matches documents where market and price appear in the same array element
      • -
      • RangeItem - Efficiently filters on numeric price ranges using indexes
      • -
      • WordItem - Matches exact seller currency and market values
      • -
      -
    4. -
    5. Injects filter into query tree: Combines the price filter with the user's query, - allowing Vespa's query execution engine to evaluate it efficiently using indexes.
    6. -
    - -

    - This approach has several performance benefits: -

    -
      -
    • No document iteration: Vespa uses attribute indexes to quickly identify matching documents - without fetching and evaluating all products
    • -
    • One-time conversion: Currency conversion happens once during query construction, - not for every product in the result set
    • -
    • Index-backed filtering: Price range and market matching leverage Vespa's fast attribute lookups
    • -
    • Query optimization: Vespa's query optimizer can reorder and optimize the combined query tree - for efficient execution
    • -
    - -

    Advanced Usage

    - -

    Custom Field Configuration

    -

    - By default, the multi-currency components expect specific field names in your product schema. - You can customize these field names using the ecommerce-schema-wiring configuration. - See Configuration Reference for all available parameters and their defaults. -

    - -
    {% highlight xml %}
    -
    -    
    -        
    -            
    -                
    -                    
    -                        seller_currency
    -                        per_market_price
    -                        market
    -                        price
    -                    
    -                    
    -                        DEFAULT
    -                    
    -                    
    -                        forexRates
    -                    
    -                
    -            
    -        
    -    
    -
    -{% endhighlight %}
    - - -

    Using Forex Rates in Ranking

    -

    - When ecommerce.multicurrency.enrich=true is set, the searcher exposes the forex rates - as a query tensor query(forexRates) that can be used in ranking expressions. - The ranking profile should implement the same fallback logic as the searcher: check for market-specific prices first, - then fall back to the DEFAULT market price, and convert to the buyer's currency. -

    - -
    {% highlight xml %}
    -rank-profile price_ranking {
    -    inputs {
    -        query(forexRates) tensor(from{}, to{})
    -        query(buyer_currency) tensor(to{})
    -        query(buyer_market) tensor(market{})
    -    }
    -
    -    function from_selector() {
    -        expression: tensorFromLabels(attribute(seller_currency), from)
    -    }
    -
    -    function buyer_rate() {
    -        expression: sum(query(forexRates) * from_selector() * query(buyer_currency), from, to)
    -    }
    -
    -    function price_tensor() {
    -        expression: tensorFromStructs(attribute(per_market_price), market, price, double)
    -    }
    -
    -    function market_specific_price() {
    -        expression: sum(price_tensor() * query(buyer_market), market)
    -    }
    -
    -    function default_price() {
    -        expression: price_tensor(){market:'DEFAULT'}
    -    }
    -
    -    function effective_price_in_seller_currency() {
    -        expression: if(market_specific_price() > 0, market_specific_price(), default_price())
    -    }
    -
    -    function effective_price_in_buyer_currency() {
    -        expression: effective_price_in_seller_currency() * buyer_rate()
    -    }
    -
    -    first-phase {
    -        expression: -effective_price_in_buyer_currency()
    -    }
    -}
    -{% endhighlight %}
    - -

    Filter Parameters vs Ranking Inputs

    -

    - There are two distinct types of query parameters used together, and they must not be confused: -

    - - - - - - - - - - - - - - - - - - - - - - - -
    TypePrefixFormatPurpose
    Filter parametersecommerce.multicurrency.*Plain string or numberTells the searcher which market, currency, and price range to filter on. Consumed server-side — never reach the rank profile.
    Ranking inputsranking.features.query(...)One-hot tensorPassed directly to the rank profile to drive scoring expressions. The searcher does not read or modify these.
    - -{% include note.html content='The only exception is enrich=true, which causes the searcher to inject query(forexRates) from its in-memory cache — because the client cannot know the current rates. buyer_currency and buyer_market are already known to the client so they are passed directly as ranking inputs, not via the searcher.' %} - -

    One-Hot Tensor Format

    -

    - Ranking inputs use one-hot encoded tensors. The format is {% raw %}{{dimension:value}:1}{% endraw %} - where dimension is the named dimension defined in the rank profile inputs block, - and value is the label to select: -

    -
      -
    • query(buyer_currency) has dimension to — pass {% raw %}{{to:NOK}:1}{% endraw %}
    • -
    • query(buyer_market) has dimension market — pass {% raw %}{{market:NO}:1}{% endraw %}
    • -
    -

    - Both tensors are required by the example rank profile above. Omitting either causes its ranking expressions to produce zero for all documents. -

    - -

    Complete Query with Filtering and Ranking

    -

    - The following example uses the price_ranking profile defined above with price filtering and both ranking input tensors: -

    -
    {% highlight sh %}
    -$ vespa query \
    -    'yql=select * from product where true' \
    -    'searchChain=multi-currency-filter' \
    -    'ecommerce.multicurrency.market=NO' \
    -    'ecommerce.multicurrency.currency=NOK' \
    -    'ecommerce.multicurrency.price-min=1000' \
    -    'ecommerce.multicurrency.price-max=1500' \
    -    'ecommerce.multicurrency.enrich=true' \
    -    'ranking.profile=price_ranking' \
    -    'ranking.features.query(buyer_currency)={% raw %}{{to:NOK}:1}{% endraw %}' \
    -    'ranking.features.query(buyer_market)={% raw %}{{market:NO}:1}{% endraw %}'
    -{% endhighlight %}
    - -

    - Key functions: -

    -
      -
    • tensorFromStructs - Converts the per_market_price array to a tensor at ranking time
    • -
    • market_specific_price() - Extracts price for the requested market if it exists
    • -
    • default_price() - Gets the DEFAULT market price as fallback
    • -
    • effective_price_in_seller_currency() - Selects market-specific price or falls back to DEFAULT
    • -
    • effective_price_in_buyer_currency() - Converts the effective price using forex rates
    • -
    - - -

    Configuration Reference

    - -

    - This section describes the configuration parameters - used by the multi-currency components. - All parameters are part of the ecommerce-schema-wiring config - (ai.vespa.ecommerce.common.ecommerce-schema-wiring). -

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    ParameterDescriptionTypeDefault
    productFields.sellerCurrencyField name for the product's seller currency.stringseller_currency
    productFields.perMarketPriceArrayStructArray field name containing per-market prices.stringper_market_price
    productFields.marketStructFieldStruct field name for market code.stringmarket
    productFields.priceStructFieldStruct field name for price value.stringprice
    defaults.marketDefault market identifier used as fallback when no market-specific price exists.stringDEFAULT
    rankProfileInputs.forexRatesQuery tensor name for forex rates in ranking. Used when enrich=true to inject the forex tensor into the query.stringforexRates
    - - -

    Requirements

    -
      -
    • - Single global forex document: Maintain one document with ID id:forex:forex::forex - and mark it global="true". Additional documents trigger the INVALID_FOREX_DOCUMENTS health state and queries fail. -
    • -
    • - Forex payload completeness: Every feed/update must include the rates tensor for all buyer/seller pairs you filter on, - identity rates (USD→USD, etc.), and a monotonically increasing timestamp (epoch seconds). -
    • -
    • - Product schema layout: Products expose seller_currency, encode all per_market_price.price values - in that seller currency, and include a DEFAULT market entry. -
    • -
    • - Container wiring: Deploy the multi-currency-filter search chain and the forex-cache chain in your container cluster, - along with the ForexRateService and ForexRateRetriever components. -
    • -
    • - Query parameters: Multi-currency filtering only runs when the query supplies market, currency, - price-min, and price-max. Missing or malformed parameters cause the searcher to skip filtering. -
    • -
    - - -
      -
    • - Structure product ids/names as needed: Keep your existing product fields (IDs, names, facets) and add the required currency fields alongside them. -
    • -
    • - Model asymmetric rates: Store both A→B and B→A conversions explicitly so buyer→seller lookups stay accurate even when FX rates are not perfect inverses. -
    • -
    • - Plan update cadence: Choose how often you feed forex data based on market volatility. The retriever polls every 10 seconds, - so frequent feeds are reflected quickly. -
    • -
    • - Default chain selection: Either set searchChain=multi-currency-filter on relevant queries or make it the default chain - so multi-currency filtering is always applied when parameters are present. -
    • -
    - - -

    See Also

    - diff --git a/en/modules/e-commerce/multi-currency-filtering.mdx b/en/modules/e-commerce/multi-currency-filtering.mdx new file mode 100644 index 0000000000..a0b3227069 --- /dev/null +++ b/en/modules/e-commerce/multi-currency-filtering.mdx @@ -0,0 +1,614 @@ +--- +title: Multi-Currency Pricing +--- + +Vespa for e-commerce includes multi-currency pricing support for e-commerce applications with global product catalogs where products are priced in different currencies and sold across multiple markets. Multi-currency pricing refers to presenting and working with prices in multiple currencies, enabling applications to query, filter, and rank products using prices expressed in the buyer's preferred currency. This enables filtering by price range in any currency and using converted prices in ranking, with automatic currency conversion when market-specific pricing is not available. + +## Overview + +The multi-currency pricing feature supports: + +- **Per-market pricing** - Define different prices for different markets on each product. +- **Keeping track of exchange rates** - An N×N tensor mapping of currency-to-currency exchange rates is stored in a "forex" document, and can be updated at any time. +- **Automatic currency conversion** - Fallback to a default market when no other market-specific price exists for the buyer's market. +- **Query-time filtering** - Filter products by price range in any currency. +- **Ranking integration** - Optional exposure of currency rates for use in ranking expressions (ranking on the computed price). + +The implementation consists of two key components: + +- **MultiCurrencyFilterSearcher** - A custom searcher that intercepts queries and dynamically filters products based on effective prices. +- **CachedForexRateService** - A background service that stores exchange rates from the forex document in-memory for faster look-ups. + +## Quick Start +This quick start walks through an end-to-end example of enabling multi-currency pricing in a Vespa application. +### Define Schemas + +Create two schemas: one to store the forex rates, and one for products. If you already have an existing product schema, you can reuse it as long as it contains the required fields described below. + +#### Forex Schema + +The forex schema stores currency exchange rates as a tensor. Add a `forex.sd` schema to your application defined as: + +```json +schema forex { + document forex { + field timestamp type long { + indexing: attribute | summary + } + + field rates type tensor(from{}, to{}) { + indexing: attribute | summary + } + } +} +``` + +#### Product Schema + +The product schema stores products with their seller currency and per-market prices. The `per_market_price` array contains price overrides for specific markets, with a `DEFAULT` market used as fallback. Every product must include a `DEFAULT` entry, and all `per_market_price.price` values are expressed in the document's `seller_currency`. + + + **Note:** Every per-market override is stored in the seller's native currency, so the searcher can convert buyer price windows instead of rewriting stored prices. + + + + **Important:**`fast-search` on the struct-fields is recommended. Without it, price filtering becomes significantly slower as the number of supported currencies grows. `rank: filter` can be added for further optimization, but this depends on the specific ranking setup ” see [rank: filter](/en/reference/schemas/schemas#filter) for details. + + +```js expandable +schema product { + document product { + + # Your existing fields above + + field seller_currency type string { + indexing: summary | attribute + } + + struct market_price { + field market type string {} + field price type double {} + } + + field per_market_price type array { + indexing: summary + summary: matched-elements-only + struct-field market { + indexing: attribute + attribute: fast-search + } + struct-field price { + indexing: attribute + attribute: fast-search + } + } + + # Your existing fields below + + } +} +``` + +### Configure Services + +Vespa only applies multi-currency filtering when the searcher and forex cache are wired into the container cluster. Queries must pass through a chain that includes `MultiCurrencyFilterSearcher`, and the `ForexRateRetriever` must read the global forex document via its own search chain. Add both chains and the two components to your container definition in `services.xml`: + +Inside your existing `` block, add the multi-currency chains and components: + +```xml + + + + + + + + + + +``` + +In the `` cluster ensure both document types are declared: + +``` + + + + +``` + +The retriever issues background queries through the `forex-cache` chain. If that chain is missing or restricts the wrong document type, the cache never reaches `READY` and queries fail with "ensure exactly one forex document exists". + +Putting it all together, a minimal `services.xml` might look like this: + +```xml + + + + + + + + + + + + + + + + + + + + + +``` + +### Feed Data + +#### Feed Forex Rates + +Feed a single forex document with ID `id:forex:forex::forex` containing all currency-to-currency exchange rates. Include identity rates (e.g., USD→USD = 1.0) to avoid missing-cell lookups. The `timestamp` field is required and must be updated with each rate change to ensure the cache picks up new rates. + + **Warning:** Exactly one global forex document must exist. If multiple documents are present, the retriever reports `INVALID_FOREX_DOCUMENTS` and the searcher returns error hits instructing you to keep a single forex document. + +```json +{ + "put": "id:forex:forex::forex", + "fields": { + "timestamp": 1757385600, + "rates": { + "cells": [ + {"address": {"from": "USD", "to": "USD"}, "value": 1.0}, + {"address": {"from": "USD", "to": "EUR"}, "value": 0.92}, + {"address": {"from": "USD", "to": "GBP"}, "value": 0.78}, + {"address": {"from": "USD", "to": "NOK"}, "value": 10.50}, + {"address": {"from": "EUR", "to": "USD"}, "value": 1.09}, + {"address": {"from": "EUR", "to": "EUR"}, "value": 1.0}, + {"address": {"from": "EUR", "to": "GBP"}, "value": 0.85} + ] + } + } +} +``` + +#### Feed Products + +Feed products with their seller currency and per-market prices. Always include a `DEFAULT` market entry as fallback. + +```json +{ + "put": "id:product:product::sku-100", + "fields": { + "seller_currency": "USD", + "per_market_price": [ + {"market": "DEFAULT", "price": 199.0}, + {"market": "EU", "price": 189.0}, + {"market": "UK", "price": 209.0}, + {"market": "NO", "price": 300.0} + ] + } +} +``` + + + **Note:** If your product schema already includes identifiers or descriptive fields (such as `product_id` or `product_name`), include them in the feed as usual. The example keeps only the required currency fields so it works with the minimal schema shown above. + + +### Query with Price Filtering + +Use the following query parameters to filter products by price range in a specific market and currency: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    ParameterDescriptionExample
    {`ecommerce.multicurrency.market`}Target market code{`NO`}, {`US`}, {`EU`}, {`NO-49`}, {`27`}
    {`ecommerce.multicurrency.currency`}Target currency code{`NOK`}, {`USD`}, {`EUR`}
    {`ecommerce.multicurrency.price-min`}Minimum price in target currency{`1000`}
    {`ecommerce.multicurrency.price-max`}Maximum price in target currency{`1500`}
    {`ecommerce.multicurrency.enrich`}Optional: expose forex rates as query tensor for ranking. Defaults to false{`true`} or {`false`}
    + +#### Example Query + +```sql +$ vespa query \ + 'yql=select * from product where true' \ + 'searchChain=multi-currency-filter' \ + 'ecommerce.multicurrency.market=NO' \ + 'ecommerce.multicurrency.currency=NOK' \ + 'ecommerce.multicurrency.price-min=1000' \ + 'ecommerce.multicurrency.price-max=1500' +``` + +This query returns all products whose effective price in NOK (Norwegian Krone) for the Norwegian market is between 1000 and 1500 NOK. The searcher will: + +1. Check if the product has a market-specific price for `NO` +2. If yes, use that price directly +3. If no, convert the product's `DEFAULT` market price from the seller currency to NOK using forex rates +4. Keep only products within the specified price range + +### Validation Rules + +The multi-currency searcher validates query parameters and returns an error if validation fails: + +- **Currency codes** must be exactly 3 letters (ISO-4217 format, e.g., `USD`, `EUR`, `NOK`) +- **Market codes** must be alphanumeric (e.g., `US`, `NO`, `EU`, `NO-47`, `13`) +- **Price values** must be valid numbers and non-negative +- **Price range**: `price-max` must be greater than or equal to `price-min` +- **Currency availability**: The requested currency must exist in the forex document + +If any parameter is missing or invalid, the searcher will either skip filtering (for format issues) or return an error result (for logical issues like invalid price ranges or unknown currencies). + + + **Note:** When filtering is skipped due to malformed inputs, the searcher acts as a no-op and the trace log records the reason (for example, "currency failed ISO-4217 validation; skipping filter"). Use [query tracing](/en/reference/api/query#tracing) to confirm whether the multi-currency filter actually ran. + + +### Updating Forex Rates + +Forex rates can be updated at any time by feeding a new version of the forex document with an updated `timestamp` field. The cache will automatically pick up the new rates on its next refresh cycle (typically within seconds). + +```bash +$ vespa feed <(echo '{ + "update": "id:forex:forex::forex", + "fields": { + "timestamp": {"assign": 1757472000}, + "rates": { + "assign": { + "cells": [...] + } + } + } +}') +``` + +## How It Works + +### Price Resolution Logic + +For each product, the effective price in the target currency is determined as follows: + +1. **Market-specific price:** If the product has a price entry for the requested market, use that price directly +2. **Currency conversion:** Otherwise, use the `DEFAULT` market price and convert it from the seller currency to the target currency using forex rates +3. **Price range filter:** Keep only products whose effective price falls within the specified min/max range + +### Forex Cache + +The `CachedForexRateService` component maintains an in-memory cache of exchange rates and refreshes them periodically from the forex document (`id:forex:forex::forex`). This ensures low-latency access to forex rates during query processing. + +#### Automatic Refresh + +The `ForexRateRetriever` component automatically refreshes forex rates every 10 seconds using a fixed schedule. This cadence (10s interval, 5s retry window, 1s per attempt) is hard-coded in the provided component and cannot be tuned at deployment time. Each refresh cycle: + +- Queries the forex document using the `forex-cache` search chain +- Validates the document has both `rates` (tensor) and `timestamp` (long) fields +- Only applies updates if the timestamp is newer than the cached version +- Retries within a 5-second budget if the first attempt fails + +#### Health States + +The forex service tracks its operational status with the following health states: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    StateDescriptionQuery Behavior
    {`READY`}Forex rates loaded and service is operationalQueries with multi-currency filtering work normally
    {`UNINITIALIZED`}No forex document has been loaded yetQueries return error: "forex rate service not initialized"
    {`OUTAGE`}Refresh failed but stale data exists (cache stays ready for re-use once the retriever succeeds again)Queries return error: "forex rate service temporarily unavailable (last refresh failed)"
    {`INVALID_FOREX_DOCUMENTS`}Multiple forex documents detected (expected exactly one)Queries return error: "ensure exactly one forex document exists"
    + +#### Error Handling + +When the service is not in `READY` state, queries with multi-currency filtering will: + +- Return an empty result with an appropriate error message +- Log detailed diagnostic information at appropriate trace levels +- Continue retrying background refresh attempts until successful + +### Performance + +Multi-currency price filtering is implemented as efficient query-time filter construction, not result-time evaluation. This means Vespa can use its indexes to find matching products without iterating through all documents. + +#### How Filtering Works + +When a query with multi-currency parameters is received, the searcher: + +1. **Pre-computes price ranges:** Converts the buyer's price range (e.g., 1000-1500 NOK) into equivalent ranges for every seller currency using cached forex rates. For example, if the forex cache has USD, EUR, and GBP, it computes what 1000-1500 NOK equals in each currency. +2. **Builds structured query filters:** Creates a query tree using Vespa's efficient query primitives: + - `SameElementItem` - Matches documents where market and price appear in the same array element + - `RangeItem` - Efficiently filters on numeric price ranges using indexes + - `WordItem` - Matches exact seller currency and market values + +3. **Injects filter into query tree:** Combines the price filter with the user's query, allowing Vespa's query execution engine to evaluate it efficiently using indexes. + +This approach has several performance benefits: + +- **No document iteration:** Vespa uses attribute indexes to quickly identify matching documents without fetching and evaluating all products +- **One-time conversion:** Currency conversion happens once during query construction, not for every product in the result set +- **Index-backed filtering:** Price range and market matching leverage Vespa's fast attribute lookups +- **Query optimization:** Vespa's query optimizer can reorder and optimize the combined query tree for efficient execution + +## Advanced Usage + +### Custom Field Configuration + +By default, the multi-currency components expect specific field names in your product schema. You can customize these field names using the `ecommerce-schema-wiring` configuration. See [Configuration Reference](#configuration) for all available parameters and their defaults. + +```html + + + + + + + seller_currency + per_market_price + market + price + + + DEFAULT + + + forexRates + + + + + + +``` + +### Using Forex Rates in Ranking + +When `ecommerce.multicurrency.enrich=true` is set, the searcher exposes the forex rates as a query tensor `query(forexRates)` that can be used in ranking expressions. The ranking profile should implement the same fallback logic as the searcher: check for market-specific prices first, then fall back to the `DEFAULT` market price, and convert to the buyer's currency. + +```js expandable +rank-profile price_ranking { + inputs { + query(forexRates) tensor(from{}, to{}) + query(buyer_currency) tensor(to{}) + query(buyer_market) tensor(market{}) + } + + function from_selector() { + expression: tensorFromLabels(attribute(seller_currency), from) + } + + function buyer_rate() { + expression: sum(query(forexRates) * from_selector() * query(buyer_currency), from, to) + } + + function price_tensor() { + expression: tensorFromStructs(attribute(per_market_price), market, price, double) + } + + function market_specific_price() { + expression: sum(price_tensor() * query(buyer_market), market) + } + + function default_price() { + expression: price_tensor(){market:'DEFAULT'} + } + + function effective_price_in_seller_currency() { + expression: if(market_specific_price() > 0, market_specific_price(), default_price()) + } + + function effective_price_in_buyer_currency() { + expression: effective_price_in_seller_currency() * buyer_rate() + } + + first-phase { + expression: -effective_price_in_buyer_currency() + } +} +``` + +#### Filter Parameters vs Ranking Inputs + +There are two distinct types of query parameters used together, and they must not be confused: + + + + + + + + + + + + + + + + + + + + + + + + +
    TypePrefixFormatPurpose
    Filter parameters{`ecommerce.multicurrency.*`}Plain string or numberTells the searcher which market, currency, and price range to filter on. Consumed server-side ” never reach the rank profile.
    Ranking inputs{`ranking.features.query(...)`}One-hot tensorPassed directly to the rank profile to drive scoring expressions. The searcher does not read or modify these.
    + + + **Note:** The only exception is `enrich=true`, which causes the searcher to inject `query(forexRates)` from its in-memory cache ” because the client cannot know the current rates. `buyer_currency` and `buyer_market` are already known to the client so they are passed directly as ranking inputs, not via the searcher. + + +#### One-Hot Tensor Format + +Ranking inputs use one-hot encoded tensors. The format is `{{dimension:value}:1}` where `dimension` is the named dimension defined in the rank profile `inputs` block, and `value` is the label to select: + +- `query(buyer_currency)` has dimension `to` ” pass `{{to:NOK}:1}` +- `query(buyer_market)` has dimension `market` ” pass `{{market:NO}:1}` + +Both tensors are required by the example rank profile above. Omitting either causes its ranking expressions to produce zero for all documents. + +#### Complete Query with Filtering and Ranking + +The following example uses the `price_ranking` profile defined above with price filtering and both ranking input tensors: + +```bash +$ vespa query \ + 'yql=select * from product where true' \ + 'searchChain=multi-currency-filter' \ + 'ecommerce.multicurrency.market=NO' \ + 'ecommerce.multicurrency.currency=NOK' \ + 'ecommerce.multicurrency.price-min=1000' \ + 'ecommerce.multicurrency.price-max=1500' \ + 'ecommerce.multicurrency.enrich=true' \ + 'ranking.profile=price_ranking' \ + 'ranking.features.query(buyer_currency)={{to:NOK}:1}' \ + 'ranking.features.query(buyer_market)={{market:NO}:1}' +``` + +Key functions: + +- `tensorFromStructs` - Converts the `per_market_price` array to a tensor at ranking time +- `market_specific_price()` - Extracts price for the requested market if it exists +- `default_price()` - Gets the `DEFAULT` market price as fallback +- `effective_price_in_seller_currency()` - Selects market-specific price or falls back to `DEFAULT` +- `effective_price_in_buyer_currency()` - Converts the effective price using forex rates + +## Configuration Reference + +This section describes the [configuration parameters](/en/applications/configuring-components) used by the multi-currency components. All parameters are part of the `ecommerce-schema-wiring` config (`ai.vespa.ecommerce.common.ecommerce-schema-wiring`). + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    ParameterDescriptionTypeDefault
    {`productFields.sellerCurrency`}Field name for the product's seller currency.{`string`}{`seller_currency`}
    {`productFields.perMarketPriceArrayStruct`}Array field name containing per-market prices.{`string`}{`per_market_price`}
    {`productFields.marketStructField`}Struct field name for market code.{`string`}{`market`}
    {`productFields.priceStructField`}Struct field name for price value.{`string`}{`price`}
    {`defaults.market`}Default market identifier used as fallback when no market-specific price exists.{`string`}{`DEFAULT`}
    {`rankProfileInputs.forexRates`}Query tensor name for forex rates in ranking. Used when {`enrich=true`} to inject the forex tensor into the query.{`string`}{`forexRates`}
    + +## Requirements + +- **Single global forex document:** Maintain one document with ID `id:forex:forex::forex` and mark it `global="true"`. Additional documents trigger the `INVALID_FOREX_DOCUMENTS` health state and queries fail. +- **Forex payload completeness:** Every feed/update must include the `rates` tensor for all buyer/seller pairs you filter on, identity rates (USD→USD, etc.), and a monotonically increasing `timestamp` (epoch seconds). +- **Product schema layout:** Products expose `seller_currency`, encode all `per_market_price.price` values in that seller currency, and include a `DEFAULT` market entry. +- **Container wiring:** Deploy the `multi-currency-filter` search chain and the `forex-cache` chain in your container cluster, along with the `ForexRateService` and `ForexRateRetriever` components. +- **Query parameters:** Multi-currency filtering only runs when the query supplies `market`, `currency`, `price-min`, and `price-max`. Missing or malformed parameters cause the searcher to skip filtering. + +## Recommended Practices + +- **Structure product ids/names as needed:** Keep your existing product fields (IDs, names, facets) and add the required currency fields alongside them. +- **Model asymmetric rates:** Store both A→B and B→A conversions explicitly so buyer→seller lookups stay accurate even when FX rates are not perfect inverses. +- **Plan update cadence:** Choose how often you feed forex data based on market volatility. The retriever polls every 10 seconds, so frequent feeds are reflected quickly. +- **Default chain selection:** Either set `searchChain=multi-currency-filter` on relevant queries or make it the default chain so multi-currency filtering is always applied when parameters are present. + +## See Also + +- [Saved Search Notifications](/en/modules/e-commerce/saved-search) +- [Using Features Together](/en/modules/e-commerce/using-features-together) +- [E-commerce tutorial](/en/learn/tutorials/e-commerce) +- [Searcher Development](/en/applications/searchers) +- [Tensor Guide](/en/ranking/tensor-user-guide) +- [tensorFromStructs - Convert struct arrays to tensors](/en/reference/ranking/rank-features#tensorFromStructs(attribute,key,value,type)) +- [Struct Fields in Schemas](/en/reference/schemas/schemas#struct-field) +- [Search Chains](/en/applications/searchers#search-chains) \ No newline at end of file diff --git a/en/modules/e-commerce/saved-search.html b/en/modules/e-commerce/saved-search.html deleted file mode 100644 index b7bc39283d..0000000000 --- a/en/modules/e-commerce/saved-search.html +++ /dev/null @@ -1,543 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: Saved Search Notifications -applies_to: enterprise ---- - -{% include important.html content=" -Experimental feature under active development. -Saved Search Notifications is in early access and is being actively developed. -Schemas, configuration, the webhook payload format, and other public APIs may change in -backwards-incompatible ways without notice. Do not rely on this feature for -production-critical workloads, and expect to migrate as the feature evolves. -Feedback is welcome — please reach out to Vespa Support." %} - -

    - Vespa for e-commerce includes a module for storing queries in Vespa ("searches") and issuing notifications - when a new or updated document matches any saved searches. - A typical use case in e-commerce is letting users store queries on products using filters on - keywords, price, location etc. and sending them a notification when a new product matches their query. -

    - -

    Overview

    - -

    - The saved search notifications feature supports: -

    -
      -
    • Storing predicate queries - Saved searches contain arbitrary boolean expressions over a set of string attributes and numerical ranges. See Predicate Fields.
    • -
    • Schema wiring configuration - Wirings between the saved search attributes and the document fields can be configured by the application.
    • -
    • Webhook notifications - A match between a new or updated document and a set of saved searches can be sent to a HTTP endpoint in a JSON format.
    • -
    • Separate document processing - Separate routing to the saved search component allows processing of saved searches without disrupting normal feed operations.
    • -
    - -

    Quick Start

    - -

    - A minimal setup for demonstrating saved search notification capabilities is given in this section. - We will develop a small shopping use-case example with a few saved search attributes. -

    - -

    Define Schemas

    - -

    - Create two schemas: one for products and one for storing the saved searches. -

    - -

    Product Schema

    - -

    - We create a minimal document type representing a product for sale. - Each of the three fields will correspond to a searchable attribute in the saved searches. -

    - -
    {% highlight xml %}
    -schema product {
    -    document product {
    -        
    -        # Other fields
    -
    -        field price type int {
    -            indexing: attribute
    -        }
    -
    -        field category type string {
    -            indexing: attribute
    -        }
    -
    -        field condition type string {
    -            indexing: attribute
    -        }
    -    }
    -
    -    # rank-profiles etc.
    -}
    -{% endhighlight %}
    - -

    Saved Search Schema

    - -

    - The predicate field will contain the entire search expression used to match products. -

    - -
    {% highlight xml %}
    -schema saved_search {
    -    document saved_search {
    -        field filters type predicate {
    -            indexing: attribute
    -            index {
    -                arity: 2 # Mandatory
    -                # Range of values the expressions are expected to operate on. 
    -                # Better performance if these are smaller
    -                lower-bound: 3
    -                upper-bound: 500
    -
    -                dense-posting-list-threshold: 0.25
    -            }
    -        }
    -    }
    -}
    -{% endhighlight %}
    - -

    Configure Services

    - -

    - A minimal services.xml configuring the saved search component can look like this: -

    - -
    {% highlight xml %}
    -
    -    
    -        
    -        
    -
    -        
    -            
    -                
    -                
    -                    
    -                        WEBHOOK
    -                        
    -                            http://localhost:8000/notification
    -                        
    -                    
    -
    -                    product
    -                    saved_search
    -                    filters
    -                    100
    -
    -                    
    -                        
    -                            category
    -                            category
    -                            true
    -                        
    -                        
    -                            condition
    -                            condition
    -                            false
    -                        
    -                    
    -                    
    -                        
    -                            price
    -                            price
    -                            true
    -                        
    -                    
    -                
    -            
    -        
    -    
    -
    -    
    -        2
    -        
    -            
    -            
    -
    -            
    -        
    -    
    -
    -    
    -        
    -            
    -            
    -                
    -                
    -            
    -        
    -    
    -
    -{% endhighlight %}
    - -

    Feed Data

    - - - -

    - To test the functionality, feed two saved search documents: -

    - -
    {% highlight json %}
    -[
    -  {"put": "id:saved_search:saved_search::search1", "fields": {
    -    "filters": "price in [20..100] and category in [Sports, Books]"
    -  }},
    -  {"put": "id:saved_search:saved_search::search2", "fields": {
    -    "filters": "price in [200..487] and category in [Electronics]"
    -  }}
    -]
    -{% endhighlight %}
    - -

    Feed a product to the notification route

    - -

    - Assume a new product is available, with the following schema: -

    - -
    {% highlight json %}
    -{"put": "id:saved_search:product::product1", "fields":{"category": "Sports", "price": 50}}
    -{% endhighlight %}
    - -

    - To enable notifications when feeding this product, feed it to the notification-route: -

    - -
    {% highlight shell %}
    -$ vespa feed --route notification-route product.jsonl
    -{% endhighlight %}
    - -

    - Assuming everything is set up correctly, it should match id:saved_search:saved_search::search1 but not search2. - - If a server is receiving requests at the endpoint specified in the URL parameter of the webhook configuration, - you should see a request with a JSON body representing the matched pair. -

    - -{% include warning.html content='The SavedSearchDocumentProcessor acts as a sink for incoming documents. That is, Put and Update operations sent to that document processor will not propagate down to the content nodes, effectively discarding the operations. This is why a routingtable is specified in the example - documents going to the route notification-route will "fork", with one path going to the content cluster and one path to the saved search component.' %} - -

    Notification kinds

    - -

    Webhook

    - -

    - The webhook notification kind will send a request to a specified URL for each document that matches a set of saved searches. - - It requires an external application to provide the handling of such requests. - -

    -

    - If the configuration parameter notification.kind == WEBHOOK, all configuration parameters prefixed with notification.webhook will take effect. - - The requests from the saved search application will be POST-requests with a JSON body: -

    - -
    {% highlight json %}
    -{
    -  "id": "",
    -  "timestamp": "",
    -  "matched_documents": [
    -    "",
    -    "",
    -    ...
    -  ]
    -}
    -{% endhighlight %}
    - -

    Security

    - -

    - In most cases, the Webhook endpoint handling the notifications is (and should be) protected in some way. - - The currently supported way to send authorized requests to the webhook endpoint is by combining the Vespa secret store with the notification.webhook.headers[].secret config parameter. - - Assume we want to send notifications to https://my.webhook.com/notification, and that the api requires the following header: -

    - -
    {% highlight yaml %}
    -Authorization: Bearer TOKEN
    -{% endhighlight %}
    - -

    - to be present in all requests. To enable our application to use this, first create the secret in Vespa Cloud and let it contain the full - value of the header: Bearer TOKEN, replacing TOKEN with the actual token. -

    - -

    - Next, add the secret to the application in services.xml: -

    - -
    {% highlight xml %}
    -
    -    ...
    -    
    -        
    -    
    -{% endhighlight %}
    - -

    - Finally, configure the SavedSearchDocumentProcessor to add a header with this secret value to all notification requests: -

    - -
    {% highlight xml %}
    -    ...
    -    
    -    
    -        
    -            WEBHOOK
    -            
    -            
    -                
    -                    Authorization
    -                    
    -                    myApiToken
    -                
    -            
    -            
    -        
    -    
    -
    -{% endhighlight %}
    - -

    Webhook notifications are sent once without automatic retry. -Delivery failures are recorded in the Vespa log.

    - -

    Vespa Schema

    - -

    - For a simpler way to test saved search notification, a method for storing the notifications within the Vespa application is provided. - - This method represents each notification between a pair of a product and a saved search using a dedicated Vespa document type. - - It is recommended for testing purposes only. -

    -

    - If the configuration parameter notification.kind == VESPA_SCHEMA, all configuration parameters prefixed with notification.vespaSchema will take effect. - - A minimal working example of this notification kind is given below. -

    - -

    Notification example

    - -

    - Define a document type for storing the notifications, for example notification.sd: -

    - -
    {% highlight xml %}
    -schema notification {
    -    document notification {
    -        field product_id type string {
    -            indexing: attribute | summary
    -        }
    -
    -        field saved_search_id type string {
    -            indexing: attribute | summary
    -        }
    -
    -        field timestamp type long {
    -            indexing: attribute | summary
    -        }
    -    }
    -}
    -{% endhighlight %}
    - -

    - Add the document type to the application: -

    - -
    {% highlight xml %}
    -
    -    
    -        ...
    -        
    -    
    -
    -{% endhighlight %}
    - -

    - Configure the schema wirings of the SavedSearchDocumentProcessor: -

    - -
    {% highlight xml %}
    -
    -    
    -    
    -        
    -            VESPA_SCHEMA
    -            
    -                notification
    -                saved_search
    -                product_id
    -                saved_search_id
    -                timestamp
    -            
    -        
    -    
    -
    -{% endhighlight %}
    - -Now notifications can be inspected by using vespa visit or vespa query with the appropriate parameters. - -

    Configuration reference

    - -

    -This section describes the possible configuration parameters used by the document processor. -

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    ParameterDescriptionTypeDefault value
    notification.kindMethod to use for sending notifications.enum {WEBHOOK, DUMMY, VESPA_SCHEMA}DUMMY
    notification.webhook.URLURL to send notification requests.string
    notification.webhook.connectionPoolSizeNumber of HTTP client threads to use in the container cluster.int20
    notification.webhook.headers[].keyKey of a header to add to all webhook requests.string
    notification.webhook.headers[].valueValue of a header to add to all webhook requests.string
    notification.webhook.headers[].secretUse a secret from Vespa secret store instead of the value provided in .value. The value provided here should match the name of a secret specified with a secrets tag in services.xml.string
    notification.vespaSchema.documentTypeName of the Vespa document type to use for storing notifications. This document type has to be defined in the application.stringsaved_search_notification
    notification.vespaSchema.namespaceNamespace to use for creating document IDs for the notification documents.stringsaved_search
    notification.vespaSchema.fieldPathProductIdFieldpath for storing the product id in the notification documents.stringproduct_id
    notification.vespaSchema.fieldPathSavedSearchIdFieldpath for storing saved search id in the notification documents.stringsaved_search_id
    notification.vespaSchema.fieldPathTimestampFieldpath for storing timestamps in the notification documents.stringtimestamp
    productDocumentTypeThe name of the document type that can trigger notifications, e.g. product.stringproduct
    savedSearchDocumentTypeThe name of the document type storing saved searches, e.g. saved_search.stringsaved_search
    predicateFieldNameThe name of the field in savedSearchDocumentType storing the predicate query.stringfilters
    savedSearchNumHitsMaximum number of saved searches that can match per product update. - Matches beyond this limit are silently dropped. Higher values increase work per update. - int100
    regularAttributes[].predicateNameThe name of a regular (string) attribute to be used in the saved search predicate field.string
    regularAttributes[].fieldPathThe field in the productDocumentType to be matched with this attribute. This field should be of type string.string
    regularAttributes[].requiredWhether documents are required to specify this attribute.boolfalse
    rangeAttributes[].predicateNameThe name of a numerical range attribute to be used in the saved search predicate field.string
    rangeAttributes[].fieldPathThe field in the productDocumentType to be matched with this attribute. This field should be of a numeric type, e.g. int.string
    rangeAttributes[].requiredWhether documents are required to specify this attribute.boolfalse
    - - -

    See Also

    - diff --git a/en/modules/e-commerce/saved-search.mdx b/en/modules/e-commerce/saved-search.mdx new file mode 100644 index 0000000000..2e14b81270 --- /dev/null +++ b/en/modules/e-commerce/saved-search.mdx @@ -0,0 +1,465 @@ +--- +title: Saved Search Notifications +--- + + + +**Important:** **Experimental feature under active development.** Saved Search Notifications is in early access and is being actively developed. Schemas, configuration, the webhook payload format, and other public APIs may change in backwards-incompatible ways without notice. Do not rely on this feature for production-critical workloads, and expect to migrate as the feature evolves. Feedback is welcome - please reach out to [Vespa Support](https://vespa.ai/support/). + + +Vespa for e-commerce includes a module for storing queries in Vespa ("searches") and issuing notifications when a new or updated document matches any saved searches. A typical use case in e-commerce is letting users store queries on products using filters on keywords, price, location etc. and sending them a notification when a new product matches their query. + +## Overview + +The saved search notifications feature supports: + +- **Storing predicate queries** - Saved searches contain arbitrary boolean expressions over a set of string attributes and numerical ranges. See [Predicate Fields](/en/schemas/predicate-fields). +- **Schema wiring configuration** - Wirings between the saved search attributes and the document fields can be configured by the application. +- **Webhook notifications** - A match between a new or updated document and a set of saved searches can be sent to a HTTP endpoint in a JSON format. +- **Separate document processing** - Separate routing to the saved search component allows processing of saved searches without disrupting normal feed operations. + +## Quick Start + +A minimal setup for demonstrating saved search notification capabilities is given in this section. We will develop a small shopping use-case example with a few saved search attributes. + +### Define Schemas + +Create two schemas: one for products and one for storing the saved searches. + +#### Product Schema + +We create a minimal document type representing a product for sale. Each of the three fields will correspond to a searchable attribute in the saved searches. + +```js expandable +schema product { + document product { + + # Other fields + + field price type int { + indexing: attribute + } + + field category type string { + indexing: attribute + } + + field condition type string { + indexing: attribute + } + } + + # rank-profiles etc. +} +``` + +#### Saved Search Schema + +The predicate field will contain the entire search expression used to match products. + +```js expandable +schema saved_search { + document saved_search { + field filters type predicate { + indexing: attribute + index { + arity: 2 # Mandatory + # Range of values the expressions are expected to operate on. + # Better performance if these are smaller + lower-bound: 3 + upper-bound: 500 + + dense-posting-list-threshold: 0.25 + } + } + } +} +``` + +### Configure Services + +A minimal `services.xml` configuring the saved search component can look like this: + +```xml expandable + + + + + + + + + + + WEBHOOK + + http://localhost:8000/notification + + + + product + saved_search + filters + 100 + + + + category + category + true + + + condition + condition + false + + + + + price + price + true + + + + + + + + + 2 + + + + + + + + + + + + + + + + + + +``` + +### Feed Data + +#### Feed saved searches + +To test the functionality, feed two saved search documents: + +```json +[ + {"put": "id:saved_search:saved_search::search1", "fields": { + "filters": "price in [20..100] and category in [Sports, Books]" + }}, + {"put": "id:saved_search:saved_search::search2", "fields": { + "filters": "price in [200..487] and category in [Electronics]" + }} +] +``` + +#### Feed a product to the notification route + +Assume a new product is available, with the following schema: + +```json +{"put": "id:saved_search:product::product1", "fields":{"category": "Sports", "price": 50}} +``` + +To enable notifications when feeding this product, feed it to the `notification-route`: + +```bash +$ vespa feed --route notification-route product.jsonl +``` + +Assuming everything is set up correctly, it should match `id:saved_search:saved_search::search1` but not `search2`. If a server is receiving requests at the endpoint specified in the `URL` parameter of the `webhook` configuration, you should see a request with a JSON body representing the matched pair. + + + **Warning:** The `SavedSearchDocumentProcessor` acts as a **sink** for incoming documents. That is, `Put` and `Update` operations sent to that document processor will not propagate down to the content nodes, effectively discarding the operations. This is why a `routingtable` is specified in the example - documents going to the route `notification-route` will "fork", with one path going to the content cluster and one path to the saved search component. + + +## Notification kinds + +### Webhook + +The webhook notification kind will send a request to a specified URL for each document that matches a set of saved searches. It requires an external application to provide the handling of such requests. + +If the configuration parameter `notification.kind == WEBHOOK`, all configuration parameters prefixed with `notification.webhook` will take effect. The requests from the saved search application will be `POST`-requests with a JSON body: + +```json highlight= {7} +{ + "id": "", + "timestamp": "", + "matched_documents": [ + "", + "", + ... + ] +} +``` + +#### Security + +In most cases, the Webhook endpoint handling the notifications is (and should be) protected in some way. The currently supported way to send authorized requests to the webhook endpoint is by combining the [Vespa secret store](/en/security/secret-store) with the `notification.webhook.headers[].secret` config parameter. Assume we want to send notifications to `https://my.webhook.com/notification`, and that the api requires the following header: + +```text +Authorization: Bearer TOKEN +``` + +to be present in all requests. To enable our application to use this, first create the secret in Vespa Cloud and let it contain the **full** value of the header: `Bearer TOKEN`, replacing `TOKEN` with the actual token. + +Next, add the secret to the application in `services.xml`: + +```xml expandable + + ... + + + +``` + +Finally, configure the `SavedSearchDocumentProcessor` to add a header with this secret value to all notification requests: + +```xml expandable +... + + + + WEBHOOK + + + + Authorization + + myApiToken + + + + + + +``` + +Webhook notifications are sent once without automatic retry. Delivery failures are recorded in the Vespa log. + +### Vespa Schema + +For a simpler way to test saved search notification, a method for storing the notifications within the Vespa application is provided. This method represents each notification between a pair of a product and a saved search using a dedicated Vespa document type. It is recommended for testing purposes only. + +If the configuration parameter `notification.kind == VESPA_SCHEMA`, all configuration parameters prefixed with `notification.vespaSchema` will take effect. A minimal working example of this notification kind is given below. + +#### Notification example + +Define a document type for storing the notifications, for example `notification.sd`: + +```js +schema notification { + document notification { + field product_id type string { + indexing: attribute | summary + } + + field saved_search_id type string { + indexing: attribute | summary + } + + field timestamp type long { + indexing: attribute | summary + } + } +} +``` + +Add the document type to the application: + +```xml + + + ... + + + +``` + +Configure the schema wirings of the `SavedSearchDocumentProcessor`: + +```xml expandable + + + + + + VESPA_SCHEMA + + notification + saved_search + product_id + saved_search_id + timestamp + + + + +``` +Now notifications can be inspected by using `vespa visit` or `vespa query` with the appropriate parameters. +## Configuration reference + +This section describes the possible [configuration parameters](/en/applications/configuring-components) used by the document processor. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    ParameterDescriptionTypeDefault value
    {`notification.kind`}Method to use for sending notifications.{`enum {WEBHOOK, DUMMY, VESPA_SCHEMA}`}{`DUMMY`}
    {`notification.webhook.URL`}URL to send notification requests.{`string`}
    {`notification.webhook.connectionPoolSize`}Number of HTTP client threads to use in the container cluster.{`int`}{`20`}
    {`notification.webhook.headers[].key`}Key of a header to add to all webhook requests.{`string`}
    {`notification.webhook.headers[].value`}Value of a header to add to all webhook requests.{`string`}
    {`notification.webhook.headers[].secret`}Use a secret from Vespa secret store instead of the value provided in {`.value`}. The value provided here should match the name of a secret specified with a {`secrets`} tag in {`services.xml`}.{`string`}
    {`notification.vespaSchema.documentType`}Name of the Vespa document type to use for storing notifications. This document type has to be defined in the application.{`string`}saved_search_notification
    {`notification.vespaSchema.namespace`}Namespace to use for creating document IDs for the notification documents.{`string`}saved_search
    {`notification.vespaSchema.fieldPathProductId`}Fieldpath for storing the product id in the notification documents.{`string`}product_id
    {`notification.vespaSchema.fieldPathSavedSearchId`}Fieldpath for storing saved search id in the notification documents.{`string`}saved_search_id
    {`notification.vespaSchema.fieldPathTimestamp`}Fieldpath for storing timestamps in the notification documents.{`string`}timestamp
    {`productDocumentType`}The name of the document type that can trigger notifications, e.g. {`product`}.{`string`}product
    {`savedSearchDocumentType`}The name of the document type storing saved searches, e.g. {`saved_search`}.{`string`}saved_search
    {`predicateFieldName`}The name of the field in {`savedSearchDocumentType`} storing the predicate query.{`string`}filters
    {`savedSearchNumHits`}Maximum number of saved searches that can match per product update. Matches beyond this limit are silently dropped. Higher values increase work per update.{`int`}100
    {`regularAttributes[].predicateName`}The name of a regular (string) attribute to be used in the saved search predicate field.{`string`}
    {`regularAttributes[].fieldPath`}The field in the {`productDocumentType`} to be matched with this attribute. This field should be of type {`string`}.{`string`}
    {`regularAttributes[].required`}Whether documents are required to specify this attribute.{`bool`}{`false`}
    {`rangeAttributes[].predicateName`}The name of a numerical range attribute to be used in the saved search predicate field.{`string`}
    {`rangeAttributes[].fieldPath`}The field in the {`productDocumentType`} to be matched with this attribute. This field should be of a numeric type, e.g. {`int`}.{`string`}
    {`rangeAttributes[].required`}Whether documents are required to specify this attribute.{`bool`}{`false`}
    + +## See Also + +- [Predicate Fields](/en/schemas/predicate-fields) +- [Document Processors](/en/applications/document-processors) +- [Configuring Components](/en/applications/configuring-components) +- [Secret Store](/en/security/secret-store) +- [Multi-Currency Pricing](/en/modules/e-commerce/multi-currency-filtering) +- [Using Features Together](/en/modules/e-commerce/using-features-together) \ No newline at end of file diff --git a/en/modules/e-commerce/using-features-together.html b/en/modules/e-commerce/using-features-together.html deleted file mode 100644 index dd64685086..0000000000 --- a/en/modules/e-commerce/using-features-together.html +++ /dev/null @@ -1,322 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: Using Features Together -applies_to: enterprise ---- - -{% include important.html content=" -Some features described on this page (notably Saved Search Notifications) -are experimental and under active development. APIs and configuration may change in -backwards-incompatible ways — see the individual feature pages for details." %} - -

    - The e-commerce features are designed as standalone components that can be composed together. - This page covers the configuration needed - when features interact at feed time or query time. - Read the individual feature pages before this one: -

    - - -

    - The schemas, services configuration, and other examples on this page are illustrative. - Adapt field names, document types, and configuration values to match your application. -

    - - -

    Saved Search with Multi-Currency

    - -

    - When saved search notifications and - multi-currency pricing are used together, - the saved search document processor generates per-currency price features at feed time. - This enables saved searches with price filters to match products - regardless of the seller's currency. -

    - -

    How It Works

    - -

    - Without multi-currency, a saved search like price in [100..200] matches against the product's - single price field. With multi-currency enabled, the document processor: -

    -
      -
    1. Reads the product's per_market_price entries and seller_currency
    2. -
    3. Converts each price to every known currency using the forex rates
    4. -
    5. Scales the converted prices to integers by multiplying with priceScaleFactor - (e.g., a factor of 100 preserves two decimal places). - Predicate ranges require integer values, so this step is needed to retain precision.
    6. -
    7. Generates predicate range features named {featurePrefix}_{currency}_{market} - (e.g., price_NOK_DEFAULT, price_EUR_NO)
    8. -
    9. Feeds these features alongside the regular attributes into the predicate query
    10. -
    - -

    - A saved search filtering on NOK prices can then use price_NOK_DEFAULT in [1000..1500] - as its predicate expression, and it will match products originally priced in any currency. -

    - -

    Predicate Upper Bound

    - -

    - The predicate field's upper-bound in the saved search schema must be large enough - to cover the highest possible scaled converted price. - Because prices are both currency-converted and scaled by priceScaleFactor, - the resulting values can be significantly larger than the original prices. -

    - -{% include warning.html content='If any single range feature in a predicate query exceeds the -upper-bound, Vespa rejects the entire predicate query for that document, -not just the out-of-range feature. This means the product will not match any saved searches at all.' %} - -

    - For example, with a scale factor of 100 and a EUR-to-SEK rate of ~11.76: - a product priced at 200 EUR produces price_SEK_DEFAULT = 200 × 11.76 × 100 = 235,294. - The upper-bound must be at least 235,294 for this to work. -

    - -

    - Choose an upper bound that covers your highest-priced products converted to - the weakest target currency, multiplied by the scale factor. - A generous margin is recommended to accommodate price and exchange rate fluctuations. -

    - - -

    Schema Setup

    - -

    - The following are minimal examples showing the fields required from each feature. - Your schemas will likely have additional fields and configuration. -

    - -

    Product Schema

    -

    - The product schema must include fields from both features: - the saved search attributes (price, category, etc.) - and the multi-currency fields (seller_currency, per_market_price). -

    - -
    {% highlight xml %}
    -schema product {
    -    document product {
    -
    -        # Saved search attributes
    -        field price type int {
    -            indexing: attribute
    -        }
    -
    -        field category type string {
    -            indexing: attribute
    -        }
    -
    -        # Multi-currency fields
    -        field seller_currency type string {
    -            indexing: summary | attribute
    -        }
    -
    -        struct market_price {
    -            field market type string {}
    -            field price type double {}
    -        }
    -
    -        field per_market_price type array {
    -            indexing: summary
    -            summary: matched-elements-only
    -            struct-field market {
    -                indexing: attribute
    -            }
    -            struct-field price {
    -                indexing: attribute
    -            }
    -        }
    -    }
    -}
    -{% endhighlight %}
    - -

    Saved Search Schema

    -

    - Set the upper-bound high enough to cover scaled converted prices. - See Predicate Upper Bound above. -

    - -
    {% highlight xml %}
    -schema saved_search {
    -    document saved_search {
    -        field filters type predicate {
    -            indexing: attribute
    -            index {
    -                arity: 2
    -                lower-bound: 3
    -                upper-bound: 250000
    -
    -                dense-posting-list-threshold: 0.25
    -            }
    -        }
    -    }
    -}
    -{% endhighlight %}
    - - -

    Services Configuration

    - -

    - Both features are configured in the same container cluster. - The example below shows a combined setup — the key addition is the multicurrency block inside the - ecommerce-schema-wiring config of the document processor: -

    - -
    {% highlight xml %}
    -
    -    
    -        
    -            
    -                
    -            
    -            
    -        
    -        
    -
    -        
    -        
    -        
    -
    -        
    -        
    -            
    -                
    -                
    -                    
    -                        WEBHOOK
    -                        
    -                            http://my-webhook-endpoint:8000/notification
    -                        
    -                    
    -
    -                    
    -                        seller_currency
    -                        per_market_price
    -                        market
    -                        price
    -                    
    -
    -                    product
    -                    saved_search
    -                    filters
    -
    -                    
    -                        
    -                            category
    -                            category
    -                            false
    -                        
    -                    
    -                    
    -                        
    -                            price
    -                            price
    -                            false
    -                        
    -                    
    -
    -                    
    -                    
    -                        true
    -                        price
    -                        100
    -                    
    -                
    -            
    -        
    -    
    -
    -    
    -        
    -            
    -            
    -            
    -        
    -    
    -
    -    
    -        
    -            
    -            
    -                
    -                
    -            
    -        
    -    
    -
    -{% endhighlight %}
    - - -

    Multi-currency Configuration Reference

    - -

    - These parameters are part of the ecommerce-schema-wiring config - and only apply when the saved search document processor is used together with multi-currency: -

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    ParameterDescriptionTypeDefault
    multicurrency.enabledEnable generation of per-currency price features at feed time.boolfalse
    multicurrency.featurePrefixPrefix for the generated predicate range features. A feature is named {prefix}_{currency}_{market}.stringprice
    multicurrency.priceScaleFactorInteger multiplier applied to converted prices before feeding as predicate range features. Predicate ranges require integer values, so this preserves decimal precision. A factor of 100 preserves two decimal places.int100
    - -{% include note.html content='The productFields config block is shared between both features. When combining, use the same field names in the searcher and document processor configurations.' %} - - -

    Feeding Workflow

    - -

    - When both features are active, the feeding workflow is: -

    -
      -
    1. Feed the forex document (id:forex:forex::forex) and wait for the CachedForexRateService to reach READY state
    2. -
    3. Feed saved search documents to the content cluster
    4. -
    5. Feed products to the notification-route — the document processor will generate multi-currency predicate features and match against saved searches
    6. -
    - -{% include warning.html content='The forex rates must be loaded before feeding products to the notification route. -If the ForexRateService is not ready, the document processor cannot generate currency-converted price features.' %} - - -

    See Also

    - diff --git a/en/modules/e-commerce/using-features-together.mdx b/en/modules/e-commerce/using-features-together.mdx new file mode 100644 index 0000000000..93eb716ce1 --- /dev/null +++ b/en/modules/e-commerce/using-features-together.mdx @@ -0,0 +1,259 @@ +--- +title: Using Features Together +--- + + +**Important:** Some features described on this page (notably [Saved Search Notifications](/en/modules/e-commerce/saved-search)) are experimental and under active development. APIs and configuration may change in backwards-incompatible ways - see the individual feature pages for details. + + +The e-commerce features are designed as standalone components that can be composed together. This page covers the configuration needed when features interact at feed time or query time. Read the individual feature pages before this one: + +- [Multi-Currency Pricing](/en/modules/e-commerce/multi-currency-filtering) +- [Saved Search Notifications](/en/modules/e-commerce/saved-search) + +The schemas, services configuration, and other examples on this page are illustrative. Adapt field names, document types, and configuration values to match your application. + +## Saved Search with Multi-Currency + +When [saved search notifications](/en/modules/e-commerce/saved-search) and [multi-currency pricing](/en/modules/e-commerce/multi-currency-filtering) are used together, the saved search document processor generates per-currency price features at feed time. This enables saved searches with price filters to match products regardless of the seller's currency. + +### How It Works + +Without multi-currency, a saved search like `price in [100..200]` matches against the product's single `price` field. With multi-currency enabled, the document processor: + +1. Reads the product's `per_market_price` entries and `seller_currency` +2. Converts each price to every known currency using the forex rates +3. Scales the converted prices to integers by multiplying with `priceScaleFactor` (e.g., a factor of 100 preserves two decimal places). Predicate ranges require integer values, so this step is needed to retain precision. +4. Generates predicate range features named `{featurePrefix}_{currency}_{market}` (e.g., `price_NOK_DEFAULT`, `price_EUR_NO`) +5. Feeds these features alongside the regular attributes into the predicate query + +A saved search filtering on NOK prices can then use `price_NOK_DEFAULT in [1000..1500]` as its predicate expression, and it will match products originally priced in any currency. + +### Predicate Upper Bound + +The predicate field's `upper-bound` in the saved search schema must be large enough to cover the highest possible scaled converted price. Because prices are both currency-converted and scaled by `priceScaleFactor`, the resulting values can be significantly larger than the original prices. + + + **Warning:** If any single range feature in a predicate query exceeds the `upper-bound`, Vespa rejects the **entire** predicate query for that document, not just the out-of-range feature. This means the product will not match any saved searches at all. + + +For example, with a scale factor of 100 and a EUR-to-SEK rate of \~11.76: a product priced at 200 EUR produces `price_SEK_DEFAULT = 200 × 11.76 × 100 = 235,294`. The `upper-bound` must be at least 235,294 for this to work. + +Choose an upper bound that covers your highest-priced products converted to the weakest target currency, multiplied by the scale factor. A generous margin is recommended to accommodate price and exchange rate fluctuations. + +### Schema Setup + +The following are minimal examples showing the fields required from each feature. Your schemas will likely have additional fields and configuration. + +#### Product Schema + +The product schema must include fields from both features: the saved search attributes (`price`, `category`, etc.) and the multi-currency fields (`seller_currency`, `per_market_price`). + +```js expandable +schema product { + document product { + + # Saved search attributes + field price type int { + indexing: attribute + } + + field category type string { + indexing: attribute + } + + # Multi-currency fields + field seller_currency type string { + indexing: summary | attribute + } + + struct market_price { + field market type string {} + field price type double {} + } + + field per_market_price type array { + indexing: summary + summary: matched-elements-only + struct-field market { + indexing: attribute + } + struct-field price { + indexing: attribute + } + } + } +} +``` + +#### Saved Search Schema + +Set the `upper-bound` high enough to cover scaled converted prices. See [Predicate Upper Bound](#predicate-upper-bound) above. + +```js expandable +schema saved_search { + document saved_search { + field filters type predicate { + indexing: attribute + index { + arity: 2 + lower-bound: 3 + upper-bound: 250000 + + dense-posting-list-threshold: 0.25 + } + } + } +} +``` + +### Services Configuration + +Both features are configured in the same container cluster. The example below shows a combined setup - the key addition is the `multicurrency` block inside the `ecommerce-schema-wiring` config of the document processor: + +```xml expandable + + + + + + + + + + + + + + + + + + + + + WEBHOOK + + http://my-webhook-endpoint:8000/notification + + + + + seller_currency + per_market_price + market + price + + + product + saved_search + filters + + + + category + category + false + + + + + price + price + false + + + + + + true + price + 100 + + + + + + + + + + + + + + + + + + + + + + + + +``` + +### Multi-currency Configuration Reference + +These parameters are part of the `ecommerce-schema-wiring` config and only apply when the saved search document processor is used together with multi-currency: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    ParameterDescriptionTypeDefault
    {`multicurrency.enabled`}Enable generation of per-currency price features at feed time.{`bool`}{`false`}
    {`multicurrency.featurePrefix`}Prefix for the generated predicate range features. A feature is named {`{prefix}_{currency}_{market}`}.{`string`}{`price`}
    {`multicurrency.priceScaleFactor`}Integer multiplier applied to converted prices before feeding as predicate range features. Predicate ranges require integer values, so this preserves decimal precision. A factor of 100 preserves two decimal places.{`int`}{`100`}
    + + + **Note:** The `productFields` config block is shared between both features. When combining, use the same field names in the searcher and document processor configurations. + + +### Feeding Workflow + +When both features are active, the feeding workflow is: + +1. Feed the forex document (`id:forex:forex::forex`) and wait for the `CachedForexRateService` to reach `READY` state +2. Feed saved search documents to the content cluster +3. Feed products to the `notification-route` - the document processor will generate multi-currency predicate features and match against saved searches + + + **Warning:** The forex rates must be loaded before feeding products to the notification route. If the `ForexRateService` is not ready, the document processor cannot generate currency-converted price features. + + +## See Also + +- [Saved Search Notifications](/en/modules/e-commerce/saved-search) +- [Multi-Currency Pricing](/en/modules/e-commerce/multi-currency-filtering) +- [Predicate Fields](/en/schemas/predicate-fields) +- [Configuring Components](/en/applications/configuring-components) diff --git a/en/operations/access-logging.html b/en/operations/access-logging.html deleted file mode 100644 index 4e95cf876a..0000000000 --- a/en/operations/access-logging.html +++ /dev/null @@ -1,356 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: Access Logging -redirect_from: -- /en/access-logging.html ---- - -

    - The Vespa access log format allows the logs to be processed by a number of available tools - handling JSON based (log) files. - With the ability to add custom key/value pairs to the log from any Searcher, - you can easily track the decisions done by container components for given requests. -

    - - - -

    Vespa Access Log Format

    -

    - In the Vespa access log, each log event is logged as a JSON object on a single line. - The log format defines a list of fields that can be logged with every request. - In addition to these fields, - custom key/value pairs can be - logged via Searcher code. Pre-defined fields: -

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    NameTypeDescriptionAlways present
    ipstringThe IP address request came fromyes
    timenumberUNIX timestamp with millisecond decimal precision (e.g. 1477828938.123) when request is receivedyes
    durationnumberThe duration of the request in seconds with millisecond decimal precision (e.g. 0.123)yes
    responsesizenumberThe size of the response in bytesyes
    codenumberThe HTTP status code returnedyes
    methodstringThe HTTP method used (e.g. 'GET')yes
    uristringThe request URI from path and beyond (e.g. '/search?query=test')yes
    versionstringThe HTTP version (e.g. 'HTTP/1.1')yes
    agentstringThe user agent specified in the requestyes
    hoststringThe host header provided in the requestyes
    schemestringThe scheme of the requestyes
    portnumberThe IP port number of the interface on which the request was receivedyes
    remoteaddrstringThe IP address of the remote client if specified in HTTP headerno
    remoteportstringThe port used from the remote client if specified in HTTP header no
    peeraddrstringAddress of immediate client making request if different from remoteaddrno
    peerportstringPort used by immediate client making request if different from remoteportno
    user-principalstringThe name of the authenticated user (java.security.Principal.getName()) if principal is setno
    ssl-principalstringThe name of the x500 principal if client is authenticated through SSL/TLSno
    searchobjectObject holding search specific fieldsno
    search.totalhitsnumberThe total number of hits for the queryno
    search.hitsnumberThe hits returned in this specific responseno
    search.coverageobjectObject holding query coverage information similar to that returned in result set.no
    connectionstringReference to the connection log entry. See Connection logno
    attributesobjectObject holding custom key/value pairs logged in searcher.no
    -{% include note.html content="IP addresses can be both IPv4 addresses in standard dotted format (e.g. 127.0.0.1) -or IPv6 addresses in standard form with leading zeros omitted (e.g. 2222:1111:123:1234:0:0:0:4321)."%} -

    An example log line will look like this (here, pretty-printed):

    -
    -{
    -  "ip": "152.200.54.243",
    -  "time": 920880005.023,
    -  "duration": 0.122,
    -  "responsesize": 9875,
    -  "code": 200,
    -  "method": "GET",
    -  "uri": "/search?query=test&param=value",
    -  "version": "HTTP/1.1",
    -  "agent": "Mozilla/4.05 [en] (Win95; I)",
    -  "host": "localhost",
    -  "search": {
    -    "totalhits": 1234,
    -    "hits": 0,
    -    "coverage": {
    -      "coverage": 98,
    -      "documents": 100,
    -      "degraded": {
    -        "non-ideal-state": true
    -      }
    -    }
    -  }
    -}
    -
    -{% include note.html content="The log format is extendable by design such that the order of the fields can be changed -and new fields can be added between minor versions. -Make sure any programmatic log handling is using a proper JSON processor."%} -

    Example: Decompress, pretty-print, with human-readable timestamps:

    -
    -$ jq '. + {iso8601date:(.time | todateiso8601)}' \
    -  <(unzstd -c /opt/vespa/logs/vespa/access/JsonAccessLog.default.20210601010000.zst)
    -
    - - -

    Logging Remote Address/Port

    -

    - In some cases when a request passes through an intermediate service, - this service may add HTTP headers indicating the IP address and port of the real origin client. - These values are logged as remoteaddr and remoteport respectively. - Vespa will log the contents in any of the following HTTP request headers as - remoteaddr: X-Forwarded-For, Y-RA, YahooRemoteIP or Client-IP. - If more than one of these headers are present, the precedence is in the order listed here, - i.e. X-Forwarded-For takes precedence over Y-RA. - The contents of the Y-RP HTTP request header will be logged as remoteport. -

    - If the remote address or -port differs from those initiating the HTTP request, - the address and port for the immediate client making the request are logged as - peeraddress and peerport respectively. -

    - - - -

    Configuring Logging

    -{% include note.html content='The accesslog element can only be overridden on self-hosted Vespa, not on Vespa Cloud.' %} -

    -For details on the access logging configuration see -accesslog in the container element in services.xml. -

    -

    -Key configuration options include: -

    -
      -
    • fileNamePattern: Pattern for log file names with time variable support
    • -
    • rotationInterval: Time-based rotation schedule (minutes since midnight)
    • -
    • rotationSize: Size-based rotation threshold in bytes (0 = disabled)
    • -
    • rotationScheme: Either 'sequence' or 'date'
    • -
    • compressionFormat: GZIP or ZSTD compression for rotated files
    • -
    - -

    Logging Request Content

    -

    - Vespa supports logging of request content for specific URI paths. This is useful for inspecting query content of search POST requests - or document operations of Document v1 POST/PUT requests. The request content is logged as a base64-encoded string in the JSON access log. -

    -

    - To configure request content logging, use the request-content element - in the accesslog configuration in services.xml. -

    -

    - Here is an example of how the request content appears in the JSON access log: -

    -
    -{
    -  ...
    -  "method": "POST",
    -  "uri": "/search",
    -  ...,
    -  "request-content": {
    -    "type": "application/json; charset=utf-8",
    -    "length": 12345,
    -    "body": "<raw bytes encoded with base64>"
    -  }
    -}
    -
    - -

    File name pattern

    -

    -The file name pattern is expanded using the time when the file is created. -The following parts in the file name are expanded: -

    - - - - - - - - - - - - - - - - - - -
    FieldFormatMeaningExample
    %YYYYYYear2003
    %mMMMonth, numeric08
    %xMMMMonth, textualAug
    %dddDate25
    %HHHHour14
    %MmmMinute30
    %SssSeconds35
    %sSSSMilliseconds123
    %ZZTime zone-0400
    %TLongSystem.currentTimeMillis1349333576093
    %%%Escape percentage%
    - - - -

    Log rotation

    -

    - Apache httpd style log rotation can be configured by setting the rotationScheme. - There's two alternatives for the rotationScheme, sequence and date. - Rotation can be triggered by time intervals using rotationInterval and/or by file size using rotationSize. -

    - - -

    Sequence rotation scheme

    -

    - The fileNamePattern is used for the active log file name - (which in this case will often be a constant string). - At rotation, this file is given the name fileNamePattern.N where N is 1 + the largest integer found by - extracting the integers from all files ending by .<Integer> in the same directory -

    -
    -<accesslog type='json'
    -           fileNamePattern='logs/vespa/access/JsonAccessLog.<container id>.%Y%m%d%H%M%S'
    -           rotationScheme='sequence' />
    -
    - - -

    Date rotation scheme

    -

    - The fileNamePattern is used for the active log file name here too, - but the log files are not renamed at rotation. - Instead, you must specify a time-dependent fileNamePattern so that each time a new log file is created, - the name is unique. - In addition, a symlink is created pointing to the active log file. - The name of the symlink is specified using symlinkName. -

    -
    -<accesslog type='json'
    -           fileNamePattern='logs/vespa/access/JsonAccessLog.<container id>.%Y%m%d%H%M%S'
    -           rotationScheme='date'
    -           symlinkName='JsonAccessLog' />
    -
    - - -

    Rotation interval

    -

    - The time of rotation is controlled by setting rotationInterval: -

    -
    -<accesslog type='json'
    -           fileNamePattern='logs/vespa/access/JsonAccessLog.<container id>.%Y%m%d%H%M%S'
    -           rotationInterval='0 60 ...'
    -           rotationScheme='date'
    -           symlinkName='JsonAccessLog.<container id>' />
    -
    -

    - The rotationInterval is a list of numbers specifying when to do rotation. - Each element represents the number of minutes since midnight. - Ending the list with '...' means continuing the - arithmetic progression - defined by the two last numbers for the rest of the day. - E.g. "0 100 240 480 ..." is expanded to "0 100 240 480 720 960 1200" -

    - -

    Log retention

    -

    - Access logs are rotated, but not deleted by Vespa processes. - It is up to the application owner to take care of archiving of access logs. -

    - -

    Logging Key/Value pairs to the JSON Access Log from Searchers

    -

    - To add a key/value pair to the access log from a searcher, use -

    -
    -query/result.getContext(true).logValue(key,value)
    -
    -

    - Such key/value pairs may be added from any thread participating in handling the - query without incurring synchronization overhead. -

    - If the same key is logged multiple times, the values written will - be included in the log as an array of strings rather than a single string value. -

    - The key/value pairs are added to the attributes object in the log. -

    - An example log line will then look something like this: -

    -
    -{"ip":"152.200.54.243","time":920880005.023,"duration":0.122,"responsesize":9875,"code":200,"method":"GET","uri":"/search?query=test&param=value","version":"HTTP/1.1","agent":"Mozilla/4.05 [en] (Win95; I)","host":"localhost","search":{"totalhits":1234,"hits":0},"attributes":{"singlevalue":"value1","multivalue":["value2","value3"]}}
    -
    -

    A pretty print version of the same example:

    -
    -{
    -  "ip": "152.200.54.243",
    -  "time": 920880005.023,
    -  "duration": 0.122,
    -  "responsesize": 9875,
    -  "code": 200,
    -  "method": "GET",
    -  "uri": "/search?query=test&param=value",
    -  "version": "HTTP/1.1",
    -  "agent": "Mozilla/4.05 [en] (Win95; I)",
    -  "host": "localhost",
    -  "search": {
    -    "totalhits": 1234,
    -    "hits": 0
    -  },
    -  "attributes": {
    -    "singlevalue": "value1",
    -    "multivalue": [
    -      "value2",
    -      "value3"
    -    ]
    -  }
    -}
    -
    - - - -

    Connection log

    -

    - In addition to the access log, one entry per connection is written to the connection log. - This entry is written on connection close. Available fields: -

    - - - - - - - - - - - - - - - - - - - - -
    NameTypeDescriptionAlways present
    idstringUnique ID of the connection, referenced from access log.yes
    timestampnumberTimestamp (ISO8601 format) when the connection was openedyes
    durationnumberThe duration of the request in seconds with millisecond decimal precision (e.g. 0.123)yes
    peerAddressstringIP address used by immediate client making requestyes
    peerPortnumberPort used by immediate client making requestyes
    localAddressstringThe local IP address the request was received onyes
    localPortnumberThe local port the request was received onyes
    remoteAddressstringOriginal client ip, if proxy protocol enabledno
    remotePortnumberOriginal client port, if proxy protocol enabledno
    httpBytesReceivednumberNumber of HTTP bytes sent over the connectionno
    httpBytesSentnumberNumber of HTTP bytes received over the connectionno
    requestsnumberNumber of requests sent by the clientno
    responsesnumberNumber of responses sent to the clientno
    sslobjectDetailed information on ssl connectionno
    - - - -

    SSL information

    - - - - - - - - - - - - - -
    NameTypeDescriptionAlways present
    clientSubjectstringClient certificate subjectno
    clientNotBeforestringClient certificate valid fromno
    clientNotAfterstringClient certificate valid tono
    sessionIdstringSSL session idno
    protocolstringSSL protocolno
    cipherSuitestringName of session cipher suiteno
    sniServerNamestringSNI server nameno
    diff --git a/en/operations/access-logging.mdx b/en/operations/access-logging.mdx new file mode 100644 index 0000000000..9ef02a0c37 --- /dev/null +++ b/en/operations/access-logging.mdx @@ -0,0 +1,585 @@ +--- +title: "Access Logging" +--- + +The Vespa access log format allows the logs to be processed by a number of available tools handling JSON based (log) files. With the ability to add custom key/value pairs to the log from any Searcher, you can easily track the decisions done by container components for given requests. + +## Vespa Access Log Format + +In the Vespa access log, each log event is logged as a JSON object on a single line. The log format defines a list of fields that can be logged with every request. In addition to these fields, [custom key/value pairs](#logging-key-value-pairs-to-the-json-access-log-from-searchers) can be logged via Searcher code. Pre-defined fields: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescriptionAlways present
    ipstringThe IP address request came fromyes
    timenumberUNIX timestamp with millisecond decimal precision (e.g. 1477828938.123) when request is receivedyes
    durationnumberThe duration of the request in seconds with millisecond decimal precision (e.g. 0.123)yes
    responsesizenumberThe size of the response in bytesyes
    codenumberThe HTTP status code returnedyes
    methodstringThe HTTP method used (e.g. 'GET')yes
    uristringThe request URI from path and beyond (e.g. '/search?query=test')yes
    versionstringThe HTTP version (e.g. 'HTTP/1.1')yes
    agentstringThe user agent specified in the requestyes
    hoststringThe host header provided in the requestyes
    schemestringThe scheme of the requestyes
    portnumberThe IP port number of the interface on which the request was receivedyes
    remoteaddrstringThe IP address of the remote client if specified in HTTP headerno
    remoteportstringThe port used from the remote client if specified in HTTP headerno
    peeraddrstringAddress of immediate client making request if different from *remoteaddr*no
    peerportstringPort used by immediate client making request if different from *remoteport*no
    user-principalstringThe name of the authenticated user (java.security.Principal.getName()) if principal is setno
    ssl-principalstringThe name of the x500 principal if client is authenticated through SSL/TLSno
    searchobjectObject holding search specific fieldsno
    search.totalhitsnumberThe total number of hits for the queryno
    search.hitsnumberThe hits returned in this specific responseno
    search.coverageobjectObject holding query coverage information similar to that returned in result set.no
    connectionstringReference to the connection log entry. See Connection logno
    attributesobjectObject holding custom key/value pairs logged in searcher.no
    + + +**Note:** + +IP addresses can be both IPv4 addresses in standard dotted format (e.g. 127.0.0.1) or IPv6 addresses in standard form with leading zeros omitted (e.g. 2222:1111:123:1234:0:0:0:4321). + + +An example log line will look like this (here, pretty-printed): + +```json +{ + "ip": "152.200.54.243", + "time": 920880005.023, + "duration": 0.122, + "responsesize": 9875, + "code": 200, + "method": "GET", + "uri": "/search?query=test¶m=value", + "version": "HTTP/1.1", + "agent": "Mozilla/4.05 [en] (Win95; I)", + "host": "localhost", + "search": { + "totalhits": 1234, + "hits": 0, + "coverage": { + "coverage": 98, + "documents": 100, + "degraded": { + "non-ideal-state": true + } + } + } +} +``` + + +**Note:** + +The log format is extendable by design such that the order of the fields can be changed and new fields can be added between minor versions. Make sure any programmatic log handling is using a proper JSON processor. + + +Example: Decompress, pretty-print, with human-readable timestamps: + +```bash +$ jq '. + {iso8601date:(.time | todateiso8601)}' \ + <(unzstd -c /opt/vespa/logs/vespa/access/JsonAccessLog.default.20210601010000.zst) +``` + +### Logging Remote Address/Port + +In some cases when a request passes through an intermediate service, this service may add HTTP headers indicating the IP address and port of the real origin client. These values are logged as *remoteaddr* and *remoteport* respectively. Vespa will log the contents in any of the following HTTP request headers as *remoteaddr*: *X-Forwarded-For*, *Y-RA*, *YahooRemoteIP* or *Client-IP*. If more than one of these headers are present, the precedence is in the order listed here, i.e. *X-Forwarded-For* takes precedence over *Y-RA*. The contents of the *Y-RP* HTTP request header will be logged as *remoteport*. + +If the remote address or -port differs from those initiating the HTTP request, the address and port for the immediate client making the request are logged as *peeraddress* and *peerport* respectively. + +## Configuring Logging + +For details on the access logging configuration see [accesslog in the container](/en/reference/applications/services/container#accesslog) element in *services.xml*. + +Key configuration options include: + +- **fileNamePattern**: Pattern for log file names with time variable support +- **rotationInterval**: Time-based rotation schedule (minutes since midnight) +- **rotationSize**: Size-based rotation threshold in bytes (0 = disabled) +- **rotationScheme**: Either 'sequence' or 'date' +- **compressionFormat**: GZIP or ZSTD compression for rotated files + +### Logging Request Content + +Vespa supports logging of request content for specific URI paths. This is useful for inspecting query content of search POST requests or document operations of Document v1 POST/PUT requests. The request content is logged as a base64-encoded string in the JSON access log. + +To configure request content logging, use the [request-content](/en/reference/applications/services/container#request-content) element in the accesslog configuration in *services.xml*. + +Here is an example of how the request content appears in the JSON access log: + +```json +{ + ... + "method": "POST", + "uri": "/search", + ..., + "request-content": { + "type": "application/json; charset=utf-8", + "length": 12345, + "body": "" + } +} +``` + +### File name pattern + +The file name pattern is expanded using the time when the file is created. The following parts in the file name are expanded: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    FieldFormatMeaningExample
    {`%`}Y`YYYYYear2003
    {`%`}m`MMMonth, numeric08
    {`%`}x`MMMMonth, textualAug
    {`%`}d`ddDate25
    {`%`}H`HHHour14
    {`%`}M`mmMinute30
    {`%`}S`ssSeconds35
    {`%`}s`SSSMilliseconds123
    {`%`}Z`ZTime zone-0400
    {`%`}T`LongSystem.currentTimeMillis1349333576093
    {`%`}%{` `}{`%`}`Escape percentage%
    + +## Log rotation + +Apache httpd style log *rotation* can be configured by setting the *rotationScheme*. There's two alternatives for the rotationScheme, sequence and date. Rotation can be triggered by time intervals using *rotationInterval* and/or by file size using *rotationSize*. + +### Sequence rotation scheme + +The *fileNamePattern* is used for the active log file name (which in this case will often be a constant string). At rotation, this file is given the name fileNamePattern.N where N is 1 + the largest integer found by extracting the integers from all files ending by .`` in the same directory + +```xml + +``` + +### Date rotation scheme + +The *fileNamePattern* is used for the active log file name here too, but the log files are not renamed at rotation. Instead, you must specify a time-dependent fileNamePattern so that each time a new log file is created, the name is unique. In addition, a symlink is created pointing to the active log file. The name of the symlink is specified using *symlinkName*. + +```xml + +``` + +### Rotation interval + +The time of rotation is controlled by setting *rotationInterval*: + +```xml + +``` + +The rotationInterval is a list of numbers specifying when to do rotation. Each element represents the number of minutes since midnight. Ending the list with '...' means continuing the [arithmetic progression](https://en.wikipedia.org/wiki/Arithmetic_progression) defined by the two last numbers for the rest of the day. E.g. "0 100 240 480 ..." is expanded to "0 100 240 480 720 960 1200" + +### Log retention + +Access logs are rotated, but not deleted by Vespa processes. It is up to the application owner to take care of archiving of access logs. + +## Logging Key/Value pairs to the JSON Access Log from Searchers + +To add a key/value pair to the access log from a searcher, use + +```bash +query/result.getContext(true).logValue(key,value) +``` + +Such key/value pairs may be added from any thread participating in handling the query without incurring synchronization overhead. + +If the same key is logged multiple times, the values written will be included in the log as an array of strings rather than a single string value. + +The key/value pairs are added to the *attributes* object in the log. + +An example log line will then look something like this: + +```json +{"ip":"152.200.54.243","time":920880005.023,"duration":0.122,"responsesize":9875,"code":200,"method":"GET","uri":"/search?query=test¶m=value","version":"HTTP/1.1","agent":"Mozilla/4.05 [en] (Win95; I)","host":"localhost","search":{"totalhits":1234,"hits":0},"attributes":{"singlevalue":"value1","multivalue":["value2","value3"]}} +``` + +A pretty print version of the same example: + +```json +{ + "ip": "152.200.54.243", + "time": 920880005.023, + "duration": 0.122, + "responsesize": 9875, + "code": 200, + "method": "GET", + "uri": "/search?query=test¶m=value", + "version": "HTTP/1.1", + "agent": "Mozilla/4.05 [en] (Win95; I)", + "host": "localhost", + "search": { + "totalhits": 1234, + "hits": 0 + }, + "attributes": { + "singlevalue": "value1", + "multivalue": [ + "value2", + "value3" + ] + } +} +``` + +## Connection log + +In addition to the access log, one entry per connection is written to the connection log. This entry is written on connection close. Available fields: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescriptionAlways present
    idstringUnique ID of the connection, referenced from access log.yes
    timestampnumberTimestamp (ISO8601 format) when the connection was openedyes
    durationnumberThe duration of the request in seconds with millisecond decimal precision (e.g. 0.123)yes
    peerAddressstringIP address used by immediate client making requestyes
    peerPortnumberPort used by immediate client making requestyes
    localAddressstringThe local IP address the request was received onyes
    localPortnumberThe local port the request was received onyes
    remoteAddressstringOriginal client ip, if proxy protocol enabledno
    remotePortnumberOriginal client port, if proxy protocol enabledno
    httpBytesReceivednumberNumber of HTTP bytes sent over the connectionno
    httpBytesSentnumberNumber of HTTP bytes received over the connectionno
    requestsnumberNumber of requests sent by the clientno
    responsesnumberNumber of responses sent to the clientno
    sslobjectDetailed information on ssl connectionno
    + +## SSL information + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescriptionAlways present
    clientSubjectstringClient certificate subjectno
    clientNotBeforestringClient certificate valid fromno
    clientNotAfterstringClient certificate valid tono
    sessionIdstringSSL session idno
    protocolstringSSL protocolno
    cipherSuitestringName of session cipher suiteno
    sniServerNamestringSNI server nameno
    \ No newline at end of file diff --git a/en/operations/archive/archive-guide-aws.md b/en/operations/archive/archive-guide-aws.md deleted file mode 100644 index d86369d563..0000000000 --- a/en/operations/archive/archive-guide-aws.md +++ /dev/null @@ -1,124 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: AWS Archive guide -applies_to: cloud -redirect_from: -- /en/cloud/archive-guide-aws ---- - -{% include note.html content="This guide is for tenants using Vespa Cloud. -If your tenant uses **Enclave**, the archive buckets are in your own cloud account -and you can access them directly — see the -[Enclave archive guide](/en/operations/enclave/archive.html) instead." %} - -Vespa Cloud exports log data, heap dumps, and Java Flight Recorder sessions to buckets in AWS S3. -This guide explains how to access this data. Access to the data must happen through an AWS account -controlled by the tenant. Data traffic to access this data is charged to this AWS account. - -These resources are needed to get started: -* An AWS account -* An IAM Role in that AWS account -* The [AWS command line client](https://aws.amazon.com/cli/) - -Access is configured through the Vespa Cloud Console in the tenant account screen. -Choose the "archive" tab, then expand the **AWS** section. - -## Register IAM Role -![AWS archive accordion](/assets/img/archive-aws-expanded-dropdown.png) - -Click **Configure access to your cloud archive** to open the configuration dialog. - -## Configure access -![AWS configure access](/assets/img/archive-aws-configure-access.png) - -In **Step 1**, enter the ARN of the IAM Role that should have access to the S3 buckets -(e.g. `arn:aws:iam::123456789012:role/my-iam-role`) and click **Save**. -Vespa Cloud will then grant access to that role on the S3 buckets. - -In **Step 2**, a policy is generated that must be attached to your IAM Role. -Copy the policy and attach it to the IAM Role in your AWS account. -AWS requires permissions to be registered in both Vespa Cloud's AWS account (step 1) -and the tenant's AWS account (step 2). -Make your own equivalent policy should you have other requirements. -For more information, see the [AWS documentation](https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies_manage-attach-detach.html). - -## Access files using AWS CLI -![AWS download logs](/assets/img/archive-aws-access-logs.png) - -Once permissions have been granted, the IAM Role can access the contents of the archive -buckets. Any AWS S3 client will work, but the AWS command line client is an easy tool -to use. The archive page will list all buckets where data is stored, typically one -bucket per zone the tenant has applications. - -The `--request-payer=requester` parameter is mandatory to make sure network traffic -is charged to the correct AWS account. - -Refer to [access-log-lambda](https://github.com/vespa-cloud/vespa-documentation-search/blob/main/access-log-lambda/README.md) -for how to install and use `aws cli`, which can be used to download logs as in the illustration, -or e.g. list objects: - -``` -$ aws s3 ls --profile=archive --request-payer=requester \ - s3://vespa-cloud-data-prod.aws-us-east-1c-9eb633/vespa-team/ - - PRE album-rec-searcher/ - PRE cord-19/ - PRE vespacloud-docsearch/ -``` - -In the example above, the S3 bucket name is _vespa-cloud-data-prod.aws-us-east-1c-9eb633_ -and the tenant name is _vespa-team_ (for that particular prod zone). -Archiving is per tenant, and a log file is normally stored with a key like: - - /vespa-team/vespacloud-docsearch/default/h2946a/logs/access/JsonAccessLog.default.20210629100001.zst - -The URI to this object is hence: - - s3://vespa-cloud-data-prod.aws-us-east-1c-9eb633/vespa-team/vespacloud-docsearch/default/h2946a/logs/access/JsonAccessLog.default.20210629100001.zst - -Objects are exported once generated - access log files are compressed and exported at least once per hour. - -If you are having problems accessing the files, please run - - aws sts get-caller-identity - -to verify that you are correctly assuming the role which has been granted access. - -## Lambda processing - -When processing logs using a lambda function, -write a minimal function to list objects, -to sort out access / keys / roles: - -``` -const aws = require("aws-sdk"); -const s3 = new aws.S3({ apiVersion: "2006-03-01" }); - -const findRelevantKeys = ({ Bucket, Prefix }) => { - console.log(`Finding relevant keys in bucket ${Bucket}`); - return s3 - .listObjectsV2({ Bucket: Bucket, Prefix: Prefix, RequestPayer: "requester" }) - .promise() - .then((res) => - res.Contents.map((content) => ({ Bucket, Key: content.Key })) - ) - .catch((err) => Error(err)); -}; - -exports.handler = async (event, context) => { - const options = { Bucket: "vespa-cloud-data-prod.aws-us-east-1c-9eb633", Prefix: "MY-TENANT-NAME/" }; - return findRelevantKeys(options) - .then((res) => { - console.log("response: ", res); - return { statusCode: 200 }; - }) - .catch((err) => ({ statusCode: 500, message: err })); -}; -``` - -Note: Always set `RequestPayer: "requester"` to access the objects - -transfer cost is assigned to the requester. - -Once the above lists the log files from S3, -review [access-log-lambda](https://github.com/vespa-cloud/vespa-documentation-search/blob/main/access-log-lambda/README.md) -for how to write a function to decompress and handle the log data. diff --git a/en/operations/archive/archive-guide-aws.mdx b/en/operations/archive/archive-guide-aws.mdx new file mode 100644 index 0000000000..11ab754b4f --- /dev/null +++ b/en/operations/archive/archive-guide-aws.mdx @@ -0,0 +1,118 @@ +--- +title: "AWS Archive guide" +--- + + +**Note:** + +This guide is for tenants using Vespa Cloud. If your tenant uses **Enclave**, the archive buckets are in your own cloud account and you can access them directly — see the [Enclave archive guide](/en/operations/enclave/archive) instead. + + +Vespa Cloud exports log data, heap dumps, and Java Flight Recorder sessions to buckets in AWS S3. This guide explains how to access this data. Access to the data must happen through an AWS account controlled by the tenant. Data traffic to access this data is charged to this AWS account. + +These resources are needed to get started: + +- An AWS account +- An IAM Role in that AWS account +- The [AWS command line client](https://aws.amazon.com/cli/) + +Access is configured through the Vespa Cloud Console in the tenant account screen. Choose the "archive" tab, then expand the **AWS** section. + +## Register IAM Role + + +![AWS archive accordion](/assets/img/archive-aws-expanded-dropdown.png) + + +Click **Configure access to your cloud archive** to open the configuration dialog. + +## Configure access + + +![AWS configure access](/assets/img/archive-aws-configure-access.png) + + +In **Step 1**, enter the ARN of the IAM Role that should have access to the S3 buckets (e.g. `arn:aws:iam::123456789012:role/my-iam-role`) and click **Save**. Vespa Cloud will then grant access to that role on the S3 buckets. + +In **Step 2**, a policy is generated that must be attached to your IAM Role. Copy the policy and attach it to the IAM Role in your AWS account. AWS requires permissions to be registered in both Vespa Cloud's AWS account (step 1) and the tenant's AWS account (step 2). Make your own equivalent policy should you have other requirements. For more information, see the [AWS documentation](https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies_manage-attach-detach.html). + +## Access files using AWS CLI + + +![AWS download logs](/assets/img/archive-aws-access-logs.png) + + +Once permissions have been granted, the IAM Role can access the contents of the archive buckets. Any AWS S3 client will work, but the AWS command line client is an easy tool to use. The archive page will list all buckets where data is stored, typically one bucket per zone the tenant has applications. + +The `--request-payer=requester` parameter is mandatory to make sure network traffic is charged to the correct AWS account. + +Refer to [access-log-lambda](https://github.com/vespa-cloud/vespa-documentation-search/blob/main/access-log-lambda/README.md) for how to install and use `aws cli`, which can be used to download logs as in the illustration, or e.g. list objects: + +```bash +$ aws s3 ls --profile=archive --request-payer=requester \ + s3://vespa-cloud-data-prod.aws-us-east-1c-9eb633/vespa-team/ + + PRE album-rec-searcher/ + PRE cord-19/ + PRE vespacloud-docsearch/ +``` + +In the example above, the S3 bucket name is *vespa-cloud-data-prod.aws-us-east-1c-9eb633* and the tenant name is *vespa-team* (for that particular prod zone). Archiving is per tenant, and a log file is normally stored with a key like: + +```bash +/vespa-team/vespacloud-docsearch/default/h2946a/logs/access/JsonAccessLog.default.20210629100001.zst +``` + +The URI to this object is hence: + +```bash +s3://vespa-cloud-data-prod.aws-us-east-1c-9eb633/vespa-team/vespacloud-docsearch/default/h2946a/logs/access/JsonAccessLog.default.20210629100001.zst +``` + +Objects are exported once generated - access log files are compressed and exported at least once per hour. + +If you are having problems accessing the files, please run + +```bash +aws sts get-caller-identity +``` + +to verify that you are correctly assuming the role which has been granted access. + +## Lambda processing + +When processing logs using a lambda function, write a minimal function to list objects, to sort out access / keys / roles: + +```js expandable +const aws = require("aws-sdk"); +const s3 = new aws.S3({ apiVersion: "2006-03-01" }); + +const findRelevantKeys = ({ Bucket, Prefix }) => { + console.log(`Finding relevant keys in bucket ${Bucket}`); + return s3 + .listObjectsV2({ Bucket: Bucket, Prefix: Prefix, RequestPayer: "requester" }) + .promise() + .then((res) => + res.Contents.map((content) => ({ Bucket, Key: content.Key })) + ) + .catch((err) => Error(err)); +}; + +exports.handler = async (event, context) => { + const options = { Bucket: "vespa-cloud-data-prod.aws-us-east-1c-9eb633", Prefix: "MY-TENANT-NAME/" }; + return findRelevantKeys(options) + .then((res) => { + console.log("response: ", res); + return { statusCode: 200 }; + }) + .catch((err) => ({ statusCode: 500, message: err })); +}; +``` + + +**Note:** + +Always set `RequestPayer: "requester"` to access the objects - transfer cost is assigned to the requester. + + +Once the above lists the log files from S3, review [access-log-lambda](https://github.com/vespa-cloud/vespa-documentation-search/blob/main/access-log-lambda/README.md) for how to write a function to decompress and handle the log data. \ No newline at end of file diff --git a/en/operations/archive/archive-guide-azure.md b/en/operations/archive/archive-guide-azure.md deleted file mode 100644 index c6c186b640..0000000000 --- a/en/operations/archive/archive-guide-azure.md +++ /dev/null @@ -1,59 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: Azure Archive guide -applies_to: cloud -index: false # TODO: Remove index: false and meta_robots_noindex when Azure non-Enclave archive is publicly available -meta_robots_noindex: true ---- - -{% include note.html content="This guide is for tenants using Vespa Cloud. -If your tenant uses **Enclave**, the archive buckets are in your own cloud account -and you can access them directly — see the -[Enclave archive guide](/en/operations/enclave/archive.html) instead." %} - -Vespa Cloud exports log data, heap dumps, and Java Flight Recorder sessions to -Azure Blob Storage containers. This guide explains how to access this data. -Access to the data is through an Azure principal controlled by the tenant. - -These resources are needed to get started: -* An Azure account -* An email address or principal with access to that account -* [AzCopy](https://learn.microsoft.com/en-us/azure/storage/common/storage-use-azcopy-v10) - -Access is configured through the Vespa Cloud Console in the tenant account screen. -Choose the "archive" tab, then expand the **Azure** section. - -## Register Azure principal -![Azure archive accordion](/assets/img/archive-azure-expanded-dropdown.png) - -Click **Configure access to your cloud archive** to open the configuration dialog. - -## Configure access -![Azure configure access](/assets/img/archive-azure-configure-access.png) - -In **Step 1**, enter the email address or principal that should have access to the -blob storage containers (e.g. `email@example.com`) and click **Save**. -Vespa Cloud will then grant access to that principal on the storage containers. - -In **Step 2**, you will receive an email from Microsoft inviting you to join the Vespa.ai tenant. -Open that email and follow the instructions to accept the invitation. -This is required in order to have the necessary permissions to access and download the logs. - -## Access files using AzCopy -![Azure download logs](/assets/img/archive-azure-access-logs.png) - -Once permissions have been granted, the Azure principal can access the contents of the archive -containers. Any Azure Blob Storage client will work, but -[AzCopy](https://learn.microsoft.com/en-us/azure/storage/common/storage-use-azcopy-v10) -is an easy tool to use. The archive page will list all containers where data is stored, -typically one container per zone the tenant has applications. - -``` -$ azcopy list CONTAINER_URI -``` - -Archiving is per tenant, and a log file is normally stored with a key like: - - mytenant/myapp/default/h2946a/logs/access/JsonAccessLog.default.20210629100001.zst - -Objects are exported once generated - access log files are compressed and exported at least once per hour. diff --git a/en/operations/archive/archive-guide-gcp.md b/en/operations/archive/archive-guide-gcp.md deleted file mode 100644 index 416287a190..0000000000 --- a/en/operations/archive/archive-guide-gcp.md +++ /dev/null @@ -1,76 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: GCP Archive guide -applies_to: cloud -redirect_from: -- /en/cloud/archive-guide-gcp ---- - -{% include note.html content="This guide is for tenants using Vespa Cloud. -If your tenant uses **Enclave**, the archive buckets are in your own cloud account -and you can access them directly — see the -[Enclave archive guide](/en/operations/enclave/archive.html) instead." %} - -Vespa Cloud exports log data, heap dumps, and Java Flight Recorder sessions to -buckets in Google Cloud Storage. This guide explains how to access this data. -Access to the data is through a GCP project controlled by the tenant. -Data traffic to access this data is charged to this GCP project. - -These resources are needed to get started: -* A GCP project -* A Google user account -* The [gcloud command line interface](https://cloud.google.com/sdk/docs/install) - -Access is configured through the Vespa Cloud Console in the tenant account screen. -Choose the "archive" tab, then expand the **GCP** section. - -## Register IAM principal -![GCP archive accordion](/assets/img/archive-gcp-expanded-dropdown.png) - -Click **Configure access to your cloud archive** to open the configuration dialog. - -## Grant access to Vespa Cloud resources -![GCP configure access](/assets/img/archive-gcp-configure-access.png) - -Enter a [principal](https://cloud.google.com/iam/docs/overview) with a supported prefix -and click **Save**. Vespa Cloud will then grant access to that principal on the Cloud Storage buckets. - -Supported principal prefixes: -* `user:` — Google Account, e.g. `user:email@example.com` -* `serviceAccount:` — Service account, e.g. `serviceAccount:my-app@project.iam.gserviceaccount.com` -* `group:` — Google group, e.g. `group:admins@example.com` -* `domain:` — Google Workspace or Cloud Identity domain, e.g. `domain:example.com` - -## Access files using Gcloud CLI -![GCP download logs](/assets/img/archive-gcp-access-logs.png) - -Once permissions have been granted, the GCP member can access the contents of the archive -buckets. Any Cloud Storage client will work, but the `gsutil` command line -client is an easy tool to use. The archive page will list all buckets where -data is stored, typically one bucket per zone the tenant has applications. - -The `-u user-project` parameter is mandatory to make sure network traffic is -charged to the correct GCP project. - -``` -$ gsutil -u my-project ls \ - gs://vespa-cloud-data-prod.gcp-us-central1-f-73770f/vespa-team/ - gs://vespa-cloud-data-prod.gcp-us-central1-f-73770f/vespa-team/album-rec-searcher/ - gs://vespa-cloud-data-prod.gcp-us-central1-f-73770f/vespa-team/cord-19/ - gs://vespa-cloud-data-prod.gcp-us-central1-f-73770f/vespa-team/vespacloud-docsearch/ -``` - -In the example above, the bucket name is _vespa-cloud-data-prod.gcp-us-central1-f-73770f_ -and the tenant name is _vespa-team_ (for that particular prod zone). -Archiving is per tenant, and a log file is normally stored with a key like: - - /vespa-team/vespacloud-docsearch/default/h7644a/logs/access/JsonAccessLog.20221011080000.zst - -The URI to this object is hence: - - gs://vespa-cloud-data-prod.gcp-us-central1-f-73770f/vespa-team/vespacloud-docsearch/default/h2946a/logs/access/JsonAccessLog.default.20210629100001.zst - -Objects are exported once generated - access log files are compressed and exported at least once per hour. - -Note: Always set a user project to access the objects - transfer cost is -assigned to the requester. diff --git a/en/operations/archive/archive-guide-gcp.mdx b/en/operations/archive/archive-guide-gcp.mdx new file mode 100644 index 0000000000..253966a5d1 --- /dev/null +++ b/en/operations/archive/archive-guide-gcp.mdx @@ -0,0 +1,81 @@ +--- +title: "GCP Archive guide" +sidebarTitle: "Archive Guide GCP" +--- + + +**Note:** + +This guide is for tenants using Vespa Cloud. If your tenant uses **Enclave**, the archive buckets are in your own cloud account and you can access them directly — see the [Enclave archive guide](/en/operations/enclave/archive) instead. + + +Vespa Cloud exports log data, heap dumps, and Java Flight Recorder sessions to buckets in Google Cloud Storage. This guide explains how to access this data. Access to the data is through a GCP project controlled by the tenant. Data traffic to access this data is charged to this GCP project. + +These resources are needed to get started: + +- A GCP project +- A Google user account +- The [gcloud command line interface](https://cloud.google.com/sdk/docs/install) + +Access is configured through the Vespa Cloud Console in the tenant account screen. Choose the "archive" tab, then expand the **GCP** section. + +## Register IAM principal + + +![GCP archive accordion](/assets/img/archive-gcp-expanded-dropdown.png) + + +Click **Configure access to your cloud archive** to open the configuration dialog. + +## Grant access to Vespa Cloud resources + + +![GCP configure access](/assets/img/archive-gcp-configure-access.png) + + +Enter a [principal](https://cloud.google.com/iam/docs/overview) with a supported prefix and click **Save**. Vespa Cloud will then grant access to that principal on the Cloud Storage buckets. + +Supported principal prefixes: + +- `user:` — Google Account, e.g. `user:email@example.com` +- `serviceAccount:` — Service account, e.g. `serviceAccount:my-app@project.iam.gserviceaccount.com` +- `group:` — Google group, e.g. `group:admins@example.com` +- `domain:` — Google Workspace or Cloud Identity domain, e.g. `domain:example.com` + +## Access files using Gcloud CLI + + +![GCP download logs](/assets/img/archive-gcp-access-logs.png) + + +Once permissions have been granted, the GCP member can access the contents of the archive buckets. Any Cloud Storage client will work, but the `gsutil` command line client is an easy tool to use. The archive page will list all buckets where data is stored, typically one bucket per zone the tenant has applications. + +The `-u user-project` parameter is mandatory to make sure network traffic is charged to the correct GCP project. + +```bash +$ gsutil -u my-project ls \ + gs://vespa-cloud-data-prod.gcp-us-central1-f-73770f/vespa-team/ + gs://vespa-cloud-data-prod.gcp-us-central1-f-73770f/vespa-team/album-rec-searcher/ + gs://vespa-cloud-data-prod.gcp-us-central1-f-73770f/vespa-team/cord-19/ + gs://vespa-cloud-data-prod.gcp-us-central1-f-73770f/vespa-team/vespacloud-docsearch/ +``` + +In the example above, the bucket name is *vespa-cloud-data-prod.gcp-us-central1-f-73770f* and the tenant name is *vespa-team* (for that particular prod zone). Archiving is per tenant, and a log file is normally stored with a key like: + +```bash +/vespa-team/vespacloud-docsearch/default/h7644a/logs/access/JsonAccessLog.20221011080000.zst +``` + +The URI to this object is hence: + +```bash +gs://vespa-cloud-data-prod.gcp-us-central1-f-73770f/vespa-team/vespacloud-docsearch/default/h2946a/logs/access/JsonAccessLog.default.20210629100001.zst +``` + +Objects are exported once generated - access log files are compressed and exported at least once per hour. + + +**Note:** + +Always set a user project to access the objects - transfer cost is assigned to the requester. + \ No newline at end of file diff --git a/en/operations/archive/archive-guide.md b/en/operations/archive/archive-guide.mdx similarity index 67% rename from en/operations/archive/archive-guide.md rename to en/operations/archive/archive-guide.mdx index d422cb178f..bd28724008 100644 --- a/en/operations/archive/archive-guide.md +++ b/en/operations/archive/archive-guide.mdx @@ -1,30 +1,23 @@ --- -# Copyright Vespa.ai. All rights reserved. -title: Archive guide -applies_to: cloud -redirect_from: -- /en/cloud/archive-guide +title: "Archive guide" --- -Vespa Cloud exports log data, heap dumps, and Java Flight Recorder sessions to -storage buckets. The bucket system used will depend on which cloud provider is -backing the zone your application is running in. AWS S3 will be used in the AWS -zones, and Cloud Storage will be used in the GCP zones. +Vespa Cloud exports log data, heap dumps, and Java Flight Recorder sessions to storage buckets. The bucket system used will depend on which cloud provider is backing the zone your application is running in. AWS S3 will be used in the AWS zones, and Cloud Storage will be used in the GCP zones. How to access and use the storage buckets is found in the documentation for the respective cloud providers: - * [AWS S3](archive-guide-aws) - * [Google Cloud Storage](archive-guide-gcp) - + + + + ## Examples -These examples use GCP as source, replace with AWS commands as needed. -Here, _resonant-triode-123456_ is the Google project ID that owns the target bucket _my_access_logs_ for data copy -(and will get the data download cost, if any). -Use the CLUSTERS view in the Vespa Cloud Console to find hostname(s) for the nodes to export logs from - -then list contents: -``` +These examples use GCP as source, replace with AWS commands as needed. Here, *resonant-triode-123456* is the Google project ID that owns the target bucket *my\_access\_logs* for data copy (and will get the data download cost, if any). + +Use the CLUSTERS view in the Vespa Cloud Console to find hostname(s) for the nodes to export logs from - then list contents: + +```bash $ gsutil -u resonant-triode-123456 ls \ gs://vespa-cloud-data-prod-gcp-us-central1-f-73770f/mytenant/myapp/ @@ -34,16 +27,20 @@ $ gsutil -u resonant-triode-123456 ls \ $ gsutil -u resonant-triode-123456 ls \ gs://vespa-cloud-data-prod-gcp-us-central1-f-73770f/mytenant/myapp/myinstance/h404a/logs/access ``` -Copy files for a host to the _my_access_logs_ bucket: -``` + +Copy files for a host to the *my\_access\_logs* bucket: + +```bash $ gsutil -u resonant-triode-123456 \ -m -o "GSUtil:parallel_process_count=1" \ cp -r \ gs://vespa-cloud-data-prod-gcp-us-central1-f-73770f/vespa-team/vespacloud-docsearch/default/h404a \ gs://my_access_logs/vespa-files ``` + `rsync` can be used to reduce number of files copied, using `-x` to exclude paths: -``` + +```bash $ gsutil -u resonant-triode-123456 \ -m -o "GSUtil:parallel_process_count=1" \ rsync -r \ @@ -51,34 +48,40 @@ $ gsutil -u resonant-triode-123456 \ gs://vespa-cloud-data-prod-gcp-us-central1-f-73770f/vespa-team/vespacloud-docsearch/default/h404a \ gs://my_access_logs/vespa-files ``` -Refer to [cloud-functions](https://github.com/vespa-engine/sample-apps/tree/master/examples/google-cloud/cloud-functions) -and [lambda](https://github.com/vespa-engine/sample-apps/tree/master/examples/aws/lambda) -for how to write and deploy simple functions to process files in Google Cloud and AWS. + +Refer to [cloud-functions](https://github.com/vespa-engine/sample-apps/tree/master/examples/google-cloud/cloud-functions) and [lambda](https://github.com/vespa-engine/sample-apps/tree/master/examples/aws/lambda) for how to write and deploy simple functions to process files in Google Cloud and AWS. For local processing, copy files for a host to local file system (or use `rsync`): -``` + +```bash $ gsutil -u resonant-triode-123456 \ -m -o "GSUtil:parallel_process_count=1" \ cp -r \ gs://vespa-cloud-data-prod-gcp-us-central1-f-73770f/vespa-team/vespacloud-docsearch/default/h404a \ . ``` + Use [zstd](https://facebook.github.io/zstd/) to decompress files: -``` + +```bash $ zstd -d * ``` + Example: Filter out healthchecks using [jq](https://stedolan.github.io/jq/): -``` + +```bash $ cat JsonAccessLog.20230117* | jq '. | select (.uri != "/status.html") | select (.uri != "/state/v1/metrics") | select (.uri != "/state/v1/health")' ``` + Add a human-readable date field per access log entry: -``` + +```bash $ cat JsonAccessLog.20230117* | jq '. | select (.uri != "/status.html") | select (.uri != "/state/v1/metrics") | select (.uri != "/state/v1/health") | . +{iso8601date:(.time|todateiso8601)}' -``` +``` \ No newline at end of file diff --git a/en/operations/automated-deployments.html b/en/operations/automated-deployments.html deleted file mode 100644 index 796f8b2d91..0000000000 --- a/en/operations/automated-deployments.html +++ /dev/null @@ -1,710 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: Automated Deployments -applies_to: cloud -redirect_from: -- /en/cloud/automated-deployments.html ---- - -Picture of an automated deployment -

    - See pipeline graph for details on the visual elements. -

    - -

    Vespa Cloud provides:

    - -

    - This guide goes through details of an orchestrated deployment. - Read / try production deployment first to have a baseline understanding. - The developer guide is useful for writing tests. - Use example GitHub Actions for automation. -

    - - - -

    CD tests

    -

    - Before deployment in production zones, system tests and - staging tests are run. - Tests are run in a dedicated and downsized environment. - These tests are optional, see details in the sections below. - Status and logs of ongoing tests can be found in the Deployment view in the - Vespa Cloud Console: -

    -Minimal deployment pipeline -

    These tests are also run during Vespa Cloud upgrades.

    -

    Find deployable example applications in CI-CD.

    - - -

    System tests

    -

    - When a system test is run, the application is deployed in - the test environment. - The system test suite is then run against the endpoints of the test deployment. - The test deployment is empty when the test execution begins. - The application package and Vespa platform version is the same as that to be deployed to production. -

    -

    - A test suite includes at least one - system test. - An application can be deployed to a production zone without system tests - - this step will then only test that the application starts successfully. - See production deployment for an example without tests. -

    -

    - If the production zones span multiple cloud providers (e.g., both AWS and GCP), - system tests are run separately for each cloud provider, - using test nodes from that provider. - This ensures the application starts and works correctly on each provider's infrastructure - before production deployment. -

    -

    Read more about system tests.

    - - -

    Staging tests

    -

    - A staging test verifies the transition of a deployment of a new application package - - i.e., from application package Appold to Appnew. - A test suite includes at least one - staging setup, and - staging test. -

    -
      -
    1. - All production zone deployments are polled for the current versions. - As there can be multiple versions already being deployed - (i.e. multiple Appold), - there can be a series of staging test runs. -
    2. -
    3. - The application at revision Appold is deployed in the - staging environment. -
    4. -
    5. - The staging setup test code is run, typically making the cluster reasonably - similar to a production cluster. -
    6. -
    7. The test deployment is then upgraded to application revision Appnew.
    8. -
    9. - Finally, the staging test code is run, - to verify the deployment works as expected after the upgrade. -
    10. -
    -

    - An application can be deployed to a production zone without staging tests - - this step will then only test that the application starts successfully before and after the change. - See production deployment for an example without tests. -

    -

    - Like system tests, staging tests are run separately for each cloud provider - when the production zones span multiple providers. -

    -

    - Read more about staging tests. -

    - - -

    Disabling tests

    -

    - To deploy without testing, remove the test files from the application package. - Tests are always run, regardless of deployment.xml. -

    -

    - To temporarily deploy without testing, run deploy and hit the "Abort" button - (see illustration above, hover over the test step in the Console) - - this skips the test step and makes the orchestration progress to the next step. -

    - - -

    Running tests only

    -

    - To run a system test, without deploying to any nodes after, - add a new test instance. - In deployment.xml, add the instance without dev orprod elements, like: -

    -
    {% highlight xml %}
    -
    -    
    -        
    -    
    -    ...
    -
    -{% endhighlight %}
    -

    - Note that this will leave an empty instance in the console, - as the deployment is for testing only, - so no resources deployed to after test. -

    -

    - Make sure to run vespa prod deploy to invoke the pipeline for testing, - and use a separate application for this test. -

    - - - -

    Deployment orchestration

    -

    - The deployment orchestration is flexible. - One can configure dependencies between deployments to production zones, - production verification tests, and configured delays; - by ordering these in parallel and serial blocks of steps: -

    -Picture of a complex automated deployment - -

    Pipeline graph

    -

    - The deployment pipeline is visualized as a graph in the - Vespa Cloud Console. - Each node represents a step in the pipeline, - and edges show dependencies between steps. - Hover over any node to see details and available actions. -

    - -

    Node shapes

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    ShapeStep typeDescription
    - - Instance - The application instance. Hover to see target versions, cancel/deploy/pin controls, and block windows. -
    - - Test - System test, staging test, or production test. - Hover to see run status, versions, and abort/restart actions. -
    - - Production deployment - A deployment to a production zone. - Hover to see run status, versions, and abort/restart/defer actions. -
    - - Delay - A configured delay between steps. -
    - -

    Visual indicators

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    IndicatorMeaningDescription
    - - CompletedThe step has completed successfully on the current version. The color corresponds to the deployed version.
    - - RunningA deployment or test is currently in progress. Shown as an animated gradient between the source and target version colors.
    - - FailedThe last run of this step failed.
    - - Unknown / initialNo version has been deployed to this step yet.
    - - Pending changeA newer version is queued and waiting to be deployed to this step.
    - - Paused / deferredDeployments to this step are temporarily postponed.
    - - Application blockedApplication changes are blocked by a block window. Shown as vertical bars.
    - - Upgrade or maintenance blockedA Vespa platform upgrade, or maintenance (such as OS upgrades and node backup or restore), is blocked by a block window. Shown as horizontal bars.
    -

    - Each version deployed through the pipeline is assigned a distinct color. - This makes it easy to see at a glance which zones are on the same version - and where a rollout is in progress. - A thumbtack icon on a node indicates that the version is - pinned. -

    - -

    - On a higher level, instances can also depend on each other in the same way. - This makes it easy to configure a deployment process - which gradually rolls out changes to increasingly larger subsets of production nodes, - as confidence grows with successful production verification tests. - Refer to deployment.xml for details. -

    -

    - Deployments run sequentially by default, but can be configured to - run in parallel. Inside each zone, Vespa Cloud - orchestrates the deployment, such that the change is applied without - disruption to read or write traffic against the application. A production - deployment in a zone is complete when the new configuration is active on all nodes. -

    -

    - Most changes are instant, making this a quick process. - If node restarts are needed, e.g., during platform upgrades, - these will happen automatically and safely as part of the deployment. - When this is necessary, deployments will take longer to complete. -

    -

    - System and staging tests, if present, - must always be successfully run before the application package is deployed to production zones. -

    - - -

    Version progression

    -

    - The deployment pipeline deploys one revision at a time through the production zones. - When a revision is being deployed, it must complete deployment to all declared production zones - before the next revision begins its production rollout. - System and staging tests for newer revisions may run in parallel, - but production deployment is serialized. -

    -

    - For example, if build 90 is being deployed to the second of two production zones, - build 91 will not start deploying to the first zone until build 90 has completed in all zones — - even if build 91 has already passed system and staging tests. -

    - -

    Superseding a version

    -

    - To override the currently deploying revision and force a newer build through the pipeline, - hover over the instance node in the pipeline graph and use the TARGET VERSIONS controls. - Select the desired build number from the revision dropdown and click deploy. - This updates the instance's deployment target. - Any running production job for the old revision will be aborted, - and the pipeline will start deploying the new revision from the first production zone. -

    -Picture of instance hover card with build selector and deploy button -

    - To cancel the currently deploying revision without selecting a new one, - click cancel. - This lets the pipeline pick the next revision automatically. -

    - -

    Pinning versions

    -

    - Pinning locks the pipeline to a specific platform version or application revision, - preventing automatic upgrades. - This is useful for forcing a downgrade, holding a known-good revision during an incident, - or preventing the system from picking up a new platform version. -

    -

    - To pin a version, hover over the instance node in the pipeline graph. - Under TARGET VERSIONS, select the desired version from the dropdown - and click pin. - A reason is required — enter a description and click submit pin. - Platform and revision can be pinned independently. -

    -Picture of instance hover card showing pin dialog -

    - While pinned, no newer platform versions or revisions will be deployed for the pinned dimension. - The dropdown and deploy button are disabled to prevent accidental changes. - To unpin, hover over the instance node and click unpin, - which allows newer versions to move through the pipeline again. -

    -

    - For example, to roll back to a previous revision: -

    -
      -
    1. Select the older build number from the revision dropdown.
    2. -
    3. Click pin and provide a reason (e.g., "rollback due to regression in build 91").
    4. -
    5. The pipeline will deploy the pinned build to all production zones.
    6. -
    7. Once the issue is resolved, click unpin to resume normal deployments.
    8. -
    - -

    Cooldown after failures

    -

    - When a production deployment fails repeatedly, an exponential cooldown is applied before - the job is automatically retried. The cooldown period grows with the time between the first - failure and the last completed run. This prevents the system from continuously retrying - a failing deployment. -

    -

    - The cooldown applies only when the target versions match those of the failing runs. - If the target changes (e.g., a new revision is set as the deployment target), - the cooldown resets and the new revision can be deployed immediately. -

    -

    - To manually re-trigger a failed deployment and bypass the cooldown, - hover over the failed zone node in the pipeline graph and click restart. -

    -Picture of zone hover card showing failed status with restart button - -

    Pausing deployments to a zone

    -

    - To temporarily hold off deployments to a specific production zone, - hover over the zone node in the pipeline graph and click defer. - This postpones deployments for 72 hours. - Click enable to resume scheduling before the deferral period expires. -

    - -

    Source code repository integration

    -

    - Each new submission is assigned an increasing build number, - which can be used to track the roll-out of the new package to the instances and their zones. - With the submission, add a source code repository reference for easy integration - - this makes it easy to track changes: -

    -Build numbers and source code repository reference -

    - Add the source diff link to the pull request - - see example - GitHub Action: -

    -
    -$ vespa prod deploy \
    -  --source-url "$(git config --get remote.origin.url | sed 's+git@\(.*\):\(.*\)\.git+https://\1/\2+')/commit/$(git rev-parse HEAD)"
    -
    - - -

    Block-windows

    -

    - Use block-windows to block deployments during certain windows throughout the week, - e.g., avoid rolling out changes during peak hours / during vacations. - Hover over the instance (here "default") to find block status - - see block-change: -

    -Application block window - - -

    Validation overrides

    -

    - Some configuration changes are potentially destructive / change the application behavior - - examples are removing fields and changing linguistic processing. - These changes are disallowed by default, the deploy-command will fail. - To override and force a deploy, - use a validation override: -

    -
    - -
    -<validation-overrides>
    -    <allow until="YYYY-MM-DD"
    -           comment="Use fewer dimensions">tensor-type-change</allow>
    -</validation-overrides>
    -
    -
    - - -

    Production tests

    -

    - Production tests are optional and configured in deployment.xml. - A production test is placed after a deployment zone in the pipeline and acts as a gate: - if it fails, the rollout stops and subsequent zones will not receive the new version. - This is useful in multi-zone deployments where the first zone serves as a canary. - Production tests run against the endpoints of the preceding production region in the pipeline. -

    -Picture of production test hover card with version or build tested - - -

    Deploying Components

    -

    - Vespa is backwards compatible within major versions, - and major versions rarely change. - This means that Components - compiled against an older version of Vespa APIs - can always be run on the same major version. - However, if the application package is compiled against a newer API version, - and then deployed to an older runtime version in production, - it might fail. - See vespa:compileVersion - for how to solve this. -

    - - - -

    Automating with GitHub Actions

    -

    Auto-deploy production applications using GitHub Actions - examples:

    - -

    The automation scripts use an API-KEY to deploy:

    -
    - -
    -$ vespa auth api-key
    -
    -
    -

    This creates a key, or outputs:

    -
    -Error: refusing to overwrite /Users/me/.vespa/mytenant.api-key.pem
    -Hint: Use -f to overwrite it
    -
    -This is your public key:
    ------BEGIN PUBLIC KEY-----
    -ABCDEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEB2UFsh8ZjoWNtkrDhyuMyaZQe1ze
    -qLB9qquTKUDQTuM2LOr2dawUs02nfSc3UTfC08Lgr/dvnTnHpc0/fY+3Aw==
    ------END PUBLIC KEY-----
    -
    -Its fingerprint is:
    -12:34:56:78:65:30:77:90:30:ab:83:ee:a9:67:68:2c
    -
    -To use this key in Vespa Cloud click 'Add custom key' at
    -https://console.vespa-cloud.com/tenant/mytenant/account/keys
    -and paste the entire public key including the BEGIN and END lines.
    -
    -

    - This means, if there is a key, it is not overwritten, it is safe to run. - Make sure to add the deploy-key to the tenant using the Vespa Cloud Console. -

    -

    After the deploy-key is added, everything is ready for deployment.

    -

    - You can upload or create new Application keys in the console, and store - them as a secret in the repository like the GitHub actions example above. -

    -

    - Some services like Travis CI do not accept - multi-line values for Environment Variables in Settings. - A workaround is to use the output of -

    -
    -$ openssl base64 -A -a < mykey.pem && echo
    -
    -

    - in a variable, say VESPA_MYAPP_API_KEY, in Travis Settings. - VESPA_MYAPP_API_KEY is exported in the Travis environment, example output: -

    -
    -Setting environment variables from repository settings
    -$ export VESPA_MYAPP_API_KEY=[secure]
    -
    -

    - Then, before deploying to Vespa Cloud, regenerate the key value: -

    -
    -$ MY_API_KEY=`echo $VESPA_MYAPP_API_KEY | openssl base64 -A -a -d`
    -
    -

    - and use ${MY_API_KEY} in the deploy command. -

    - - - -

    Vespa Cloud upgrades

    -

    - Vespa upgrades follows the same pattern as for new application revisions in CD tests, - and can be tracked via its version number in the Vespa Cloud Console. -

    -

    - System tests are run the same way as for deploying a new application package. -

    -

    - A staging test verifies the upgrade from application package - Appold to Appnew, - and from Vespa platform version Vold to Vnew. - The staging test then consists of the following steps: -

    -
      -
    1. - All production zone deployments are polled for the current - Vold / Appold versions. - As there can be multiple versions already being deployed - (i.e. multiple Vold / Appold), - there can be a series of staging test runs. -
    2. -
    3. - The application at revision Appold is deployed on platform version Vold, - to a zone in the staging environment. -
    4. -
    5. - The staging setup test code is run, - typically making the cluster reasonably - similar to a production cluster. -
    6. -
    7. - The test deployment is then upgraded to application revision Appnew - and platform version Vnew. -
    8. -
    9. - Finally, the staging test test code is run, - to verify the deployment works as expected after the upgrade. -
    10. -
    -

    - Note that one or both of the application revision and platform may be upgraded during the staging test, - depending on what upgrade scenario the test is run to verify. -

    - -

    Concurrent platform and revision changes

    -

    - When both a platform upgrade and a revision change are pending, - the rollout setting in - deployment.xml - controls how they interact in production zones: -

    -
      -
    • - simultaneous (default): Revision changes deploy independently of platform upgrades. - A revision can catch up to and pass an ongoing platform upgrade. -
    • -
    • - leading: When a revision catches up to a platform upgrade, - the two changes fuse and roll out together. -
    • -
    • - separate: The revision waits for the platform upgrade to complete, - unless the upgrade is failing. -
    • -
    -

    - With the default simultaneous strategy, - a new revision will not be held back by an ongoing platform upgrade. -

    - - - -

    Next steps

    -
      -
    • - Read more about - feature switches and bucket tests. -
    • -
    • - A challenge with continuous deployment can be integration testing across multiple services: - Another service depends on this Vespa application for its own integration testing. - Use a separate application instance for such integration testing. -
    • -
    • - Set up a deployment badge - available from the console's deployment view - example: - vespa-team.vespacloud-docsearch.default overview -
    • -
    • Set up a global query endpoint.
    • -
    diff --git a/en/operations/automated-deployments.mdx b/en/operations/automated-deployments.mdx new file mode 100644 index 0000000000..d6bbe34cfc --- /dev/null +++ b/en/operations/automated-deployments.mdx @@ -0,0 +1,412 @@ +--- +title: "Automated Deployments" +--- + + +![Picture of an automated deployment](/assets/img/automated-deployments-overview.png) + + +See [pipeline graph](#pipeline-graph) for details on the visual elements. + +Vespa Cloud provides: + +- A [CD test framework](#cd-tests) for safe deployments to production zones. +- [Multi-zone deployments](#deployment-orchestration) with orchestration and test steps. + +This guide goes through details of an orchestrated deployment. Read / try [production deployment](/en/reference/applications/deployment) first to have a baseline understanding. The [developer guide](/en/applications/developer-guide) is useful for writing tests. Use [example GitHub Actions](#automating-with-github-actions) for automation. + +## CD tests + +Before deployment in production zones, [system tests](#system-tests) and [staging tests](#staging-tests) are run. Tests are run in a dedicated and [downsized](/en/operations/environments) environment. These tests are optional, see details in the sections below. Status and logs of ongoing tests can be found in the *Deployment* view in the [Vespa Cloud Console](https://console.vespa-cloud.com/): + + +![Minimal deployment pipeline](/assets/img/deployment-with-system-test.png) + + +These tests are also run during [Vespa Cloud upgrades](#vespa-cloud-upgrades). + +Find deployable example applications in [CI-CD](https://github.com/vespa-cloud/examples/tree/main/CI-CD). + +### System tests + +When a system test is run, the application is deployed in the [test environment](/en/operations/environments#test). The system test suite is then run against the endpoints of the test deployment. The test deployment is empty when the test execution begins. The application package and Vespa platform version is the same as that to be deployed to production. + +A test suite includes at least one [system test](/en/applications/testing#system-tests). An application can be deployed to a production zone without system tests - this step will then only test that the application starts successfully. See [production deployment](/en/reference/applications/deployment) for an example without tests. + +If the production zones span multiple cloud providers (e.g., both AWS and GCP), system tests are run separately for each cloud provider, using test nodes from that provider. This ensures the application starts and works correctly on each provider's infrastructure before production deployment. + +Read more about [system tests](/en/applications/testing#system-tests). + +### Staging tests + +A staging test verifies the transition of a deployment of a new application package - i.e., from application package `Appold` to `Appnew`. A test suite includes at least one [staging setup](/en/applications/testing#staging-tests), and [staging test](/en/applications/testing#staging-tests). + + + +All production zone deployments are polled for the current versions. As there can be multiple versions already being deployed (i.e. multiple `Appold`), there can be a series of staging test runs. + + +The application at revision `Appold` is deployed in the [staging environment](/en/operations/environments#staging). + + +The staging setup test code is run, typically making the cluster reasonably similar to a production cluster. + + +The test deployment is then upgraded to application revision `Appnew`. + + +Finally, the staging test code is run, to verify the deployment works as expected after the upgrade. + + + +An application can be deployed to a production zone without staging tests - this step will then only test that the application starts successfully before and after the change. See [production deployment](/en/reference/applications/deployment) for an example without tests. + +Like system tests, staging tests are run separately for each cloud provider when the production zones span multiple providers. + +Read more about [staging tests](/en/applications/testing#staging-tests). + +### Disabling tests + +To deploy without testing, remove the test files from the application package. Tests are always run, regardless of *deployment.xml*. + +To temporarily deploy without testing, run `deploy` and hit the "Abort" button (see illustration above, hover over the test step in the Console) - this skips the test step and makes the orchestration progress to the next step. + +### Running tests only + +To run a system test, without deploying to any nodes after, add a new test instance. In *deployment.xml*, add the instance without `dev` or`prod` elements, like: + +```xml + + + + + ... + +``` + +Note that this will leave an empty instance in the console, as the deployment is for testing only, so no resources deployed to after test. + +Make sure to run `vespa prod deploy` to invoke the pipeline for testing, and use a separate application for this test. + +## Deployment orchestration + +The *deployment orchestration* is flexible. One can configure dependencies between deployments to production zones, production verification tests, and configured delays; by ordering these in parallel and serial blocks of steps: + + +![Picture of a complex automated deployment](/assets/img/automated-deployments-complex.png) + + +### Pipeline graph + +The deployment pipeline is visualized as a graph in the [Vespa Cloud Console](https://console.vespa-cloud.com/). Each node represents a step in the pipeline, and edges show dependencies between steps. Hover over any node to see details and available actions. + +#### Node shapes + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    ShapeStep typeDescription
    InstanceThe application instance. Hover to see target versions, cancel/deploy/pin controls, and block windows.
    TestSystem test, staging test, or production test. Hover to see run status, versions, and abort/restart actions.
    Production deploymentA deployment to a production zone. Hover to see run status, versions, and abort/restart/defer actions.
    DelayA configured delay between steps.
    + +#### Visual indicators + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    IndicatorMeaningDescription
    CompletedThe step has completed successfully on the current version. The color corresponds to the deployed version.
    RunningA deployment or test is currently in progress. Shown as an animated gradient between the source and target version colors.
    FailedThe last run of this step failed.
    Unknown / initialNo version has been deployed to this step yet.
    Pending changeA newer version is queued and waiting to be deployed to this step.
    Paused / deferredDeployments to this step are temporarily postponed.

    Application blockedApplication changes are blocked by a block window. Shown as vertical bars.


    Platform blockedPlatform upgrades are blocked by a block window. Shown as horizontal bars.
    + +Each version deployed through the pipeline is assigned a distinct color. This makes it easy to see at a glance which zones are on the same version and where a rollout is in progress. A thumbtack icon on a node indicates that the version is [pinned](#pinning-versions). + +On a higher level, instances can also depend on each other in the same way. This makes it easy to configure a deployment process which gradually rolls out changes to increasingly larger subsets of production nodes, as confidence grows with successful production verification tests. Refer to [deployment.xml](/en/reference/applications/deployment) for details. + +Deployments run sequentially by default, but can be configured to [run in parallel](/en/reference/applications/deployment). Inside each zone, Vespa Cloud orchestrates the deployment, such that the change is applied without disruption to read or write traffic against the application. A production deployment in a zone is complete when the new configuration is active on all nodes. + +Most changes are instant, making this a quick process. If node restarts are needed, e.g., during platform upgrades, these will happen automatically and safely as part of the deployment. When this is necessary, deployments will take longer to complete. + +System and staging tests, if present, must always be successfully run before the application package is deployed to production zones. + +### Version progression + +The deployment pipeline deploys one revision at a time through the production zones. When a revision is being deployed, it must complete deployment to *all* declared production zones before the next revision begins its production rollout. System and staging tests for newer revisions may run in parallel, but production deployment is serialized. + +For example, if build 90 is being deployed to the second of two production zones, build 91 will not start deploying to the first zone until build 90 has completed in all zones — even if build 91 has already passed system and staging tests. + +#### Superseding a version + +To override the currently deploying revision and force a newer build through the pipeline, hover over the instance node in the pipeline graph and use the *TARGET VERSIONS* controls. Select the desired build number from the revision dropdown and click **deploy**. This updates the instance's deployment target. Any running production job for the old revision will be aborted, and the pipeline will start deploying the new revision from the first production zone. + + +![Picture of instance hover card with build selector and deploy button](/assets/img/automated-deployment-supersede.png) + + +To cancel the currently deploying revision without selecting a new one, click **cancel**. This lets the pipeline pick the next revision automatically. + +#### Pinning versions + +Pinning locks the pipeline to a specific platform version or application revision, preventing automatic upgrades. This is useful for forcing a downgrade, holding a known-good revision during an incident, or preventing the system from picking up a new platform version. + +To pin a version, hover over the instance node in the pipeline graph. Under *TARGET VERSIONS*, select the desired version from the dropdown and click **pin**. A reason is required — enter a description and click **submit pin**. Platform and revision can be pinned independently. + + +![Picture of instance hover card showing pin dialog](/assets/img/automated-deployment-pin.png) + + +While pinned, no newer platform versions or revisions will be deployed for the pinned dimension. The dropdown and deploy button are disabled to prevent accidental changes. To unpin, hover over the instance node and click **unpin**, which allows newer versions to move through the pipeline again. + +For example, to roll back to a previous revision: + + + +Select the older build number from the revision dropdown. + + +Click **pin** and provide a reason (e.g., "rollback due to regression in build 91"). + + +The pipeline will deploy the pinned build to all production zones. + + +Once the issue is resolved, click **unpin** to resume normal deployments. + + + +#### Cooldown after failures + +When a production deployment fails repeatedly, an exponential cooldown is applied before the job is automatically retried. The cooldown period grows with the time between the first failure and the last completed run. This prevents the system from continuously retrying a failing deployment. + +The cooldown applies only when the target versions match those of the failing runs. If the target changes (e.g., a new revision is set as the deployment target), the cooldown resets and the new revision can be deployed immediately. + +To manually re-trigger a failed deployment and bypass the cooldown, hover over the failed zone node in the pipeline graph and click **restart**. + + +![Picture of zone hover card showing failed status with restart button](/assets/img/automated-deployment-restart.png) + + +#### Pausing deployments to a zone + +To temporarily hold off deployments to a specific production zone, hover over the zone node in the pipeline graph and click **defer**. This postpones deployments for 72 hours. Click **enable** to resume scheduling before the deferral period expires. + +### Source code repository integration + +Each new *submission* is assigned an increasing build number, which can be used to track the roll-out of the new package to the instances and their zones. With the submission, add a source code repository reference for easy integration - this makes it easy to track changes: + + +![Build numbers and source code repository reference](/assets/img/CI-integration.png) + + +Add the source diff link to the pull request - see example [GitHub Action](https://github.com/vespa-cloud/vespa-documentation-search/blob/main/.github/workflows/deploy-vespa-documentation-search.yaml): + +```bash +$ vespa prod deploy \ + --source-url "$(git config --get remote.origin.url | sed 's+git@\(.*\):\(.*\)\.git+https://\1/\2+')/commit/$(git rev-parse HEAD)" +``` + +### Block-windows + +Use block-windows to block deployments during certain windows throughout the week, e.g., avoid rolling out changes during peak hours / during vacations. Hover over the instance (here "default") to find block status - see [block-change](/en/reference/applications/deployment#block-change): + + +![Application block window](/assets/img/block-window.png) + + +### Validation overrides + +Some configuration changes are potentially destructive / change the application behavior - examples are removing fields and changing linguistic processing. These changes are disallowed by default, the deploy-command will fail. To override and force a deploy, use a [validation override](/en/reference/applications/validation-overrides): + +```xml + + tensor-type-change + +``` + +### Production tests + +Production tests are optional and configured in [deployment.xml](/en/reference/applications/deployment). A production test is placed after a deployment zone in the pipeline and acts as a gate: if it fails, the rollout stops and subsequent zones will not receive the new version. This is useful in multi-zone deployments where the first zone serves as a canary. Production tests run against the endpoints of the preceding production region in the pipeline. + + +![Picture of production test hover card with version or build tested](/assets/img/automated-deployment-production-test.png) + + +### Deploying Components + +Vespa is [backwards compatible](/en/learn/releases#versions) within major versions, and major versions rarely change. This means that [Components](/en/applications/components) compiled against an older version of Vespa APIs can always be run on the same major version. However, if the application package is compiled against a newer API version, and then deployed to an older runtime version in production, it might fail. See [vespa:compileVersion](/en/reference/applications/deployment#production-deployment-with-components) for how to solve this. + +## Automating with GitHub Actions + +Auto-deploy production applications using GitHub Actions - examples: + +- [deploy-vector-search.yaml](https://github.com/vespa-cloud/vector-search/blob/main/.github/workflows/deploy-vector-search.yaml) deploys an application to a production environment - a good example to start from! +- [deploy.yaml](https://github.com/vespa-cloud/examples/blob/main/.github/workflows/deploy.yaml) deploys an application with basic HTTP tests. +- [deploy-vespa-documentation-search.yaml](https://github.com/vespa-cloud/vespa-documentation-search/blob/main/.github/workflows/deploy-vespa-documentation-search.yaml) deploys an application with Java-tests. + +The automation scripts use an API-KEY to deploy: + +```bash +$ vespa auth api-key +``` + +This creates a key, or outputs: + +```bash +Error: refusing to overwrite /Users/me/.vespa/mytenant.api-key.pem +Hint: Use -f to overwrite it + +This is your public key: +-----BEGIN PUBLIC KEY----- +ABCDEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEB2UFsh8ZjoWNtkrDhyuMyaZQe1ze +qLB9qquTKUDQTuM2LOr2dawUs02nfSc3UTfC08Lgr/dvnTnHpc0/fY+3Aw== +-----END PUBLIC KEY----- + +Its fingerprint is: +12:34:56:78:65:30:77:90:30:ab:83:ee:a9:67:68:2c + +To use this key in Vespa Cloud click 'Add custom key' at +https://console.vespa-cloud.com/tenant/mytenant/account/keys +and paste the entire public key including the BEGIN and END lines. +``` + +This means, if there is a key, it is not overwritten, it is safe to run. Make sure to add the deploy-key to the tenant using the Vespa Cloud Console. + +After the deploy-key is added, everything is ready for deployment. + +You can upload or create new Application keys in the console, and store them as a secret in the repository like the GitHub actions example above. + +Some services like [Travis CI](https://travis-ci.com) do not accept multi-line values for Environment Variables in Settings. A workaround is to use the output of + +```bash +$ openssl base64 -A -a < mykey.pem && echo +``` + +in a variable, say `VESPA_MYAPP_API_KEY`, in Travis Settings. `VESPA_MYAPP_API_KEY` is exported in the Travis environment, example output: + +```bash +Setting environment variables from repository settings +$ export VESPA_MYAPP_API_KEY=[secure] +``` + +Then, before deploying to Vespa Cloud, regenerate the key value: + +```bash +$ MY_API_KEY=`echo $VESPA_MYAPP_API_KEY | openssl base64 -A -a -d` +``` + +and use `${MY_API_KEY}` in the deploy command. + +## Vespa Cloud upgrades + +Vespa upgrades follows the same pattern as for new application revisions in [CD tests](#cd-tests), and can be tracked via its version number in the Vespa Cloud Console. + +System tests are run the same way as for deploying a new application package. + +A staging test verifies the upgrade from application package `Appold` to `Appnew`, and from Vespa platform version `Vold` to `Vnew`. The staging test then consists of the following steps: + + + +All production zone deployments are polled for the current `Vold` / `Appold` versions. As there can be multiple versions already being deployed (i.e. multiple `Vold` / `Appold`), there can be a series of staging test runs. + + +The application at revision `Appold` is deployed on platform version `Vold`, to a zone in the [staging environment](/en/operations/environments#staging). + + +The *staging setup* test code is run, typically making the cluster reasonably similar to a production cluster. + + +The test deployment is then upgraded to application revision `Appnew` and platform version `Vnew`. + + +Finally, the *staging test* test code is run, to verify the deployment works as expected after the upgrade. + + + +Note that one or both of the application revision and platform may be upgraded during the staging test, depending on what upgrade scenario the test is run to verify. + +### Concurrent platform and revision changes + +When both a platform upgrade and a revision change are pending, the `rollout` setting in [deployment.xml](/en/reference/applications/deployment) controls how they interact in production zones: + +- `simultaneous` (default): Revision changes deploy independently of platform upgrades. A revision can catch up to and pass an ongoing platform upgrade. +- `leading`: When a revision catches up to a platform upgrade, the two changes fuse and roll out together. +- `separate`: The revision waits for the platform upgrade to complete, unless the upgrade is failing. + +With the default `simultaneous` strategy, a new revision will not be held back by an ongoing platform upgrade. + +## Next steps + +- Read more about [feature switches and bucket tests](/en/applications/testing#feature-switches-and-bucket-tests). +- A challenge with continuous deployment can be integration testing across multiple services: Another service depends on this Vespa application for its own integration testing. Use a separate [application instance](/en/reference/applications/deployment#instance) for such integration testing. +- Set up a deployment badge - available from the console's deployment view - example: ![vespa-team.vespacloud-docsearch.default overview](https://api-ctl.vespa-cloud.com/badge/v1/vespa-team/vespacloud-docsearch/default) +- Set up a [global query endpoint](/en/reference/applications/deployment#endpoints-global). \ No newline at end of file diff --git a/en/operations/autoscaling.html b/en/operations/autoscaling.html deleted file mode 100644 index 11eff1be37..0000000000 --- a/en/operations/autoscaling.html +++ /dev/null @@ -1,206 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: Autoscaling -applies_to: cloud -redirect_from: -- /en/cloud/autoscaling.html ---- - -

    - Autoscaling lets you adjust the hardware resources allocated to application clusters automatically - depending on actual usage. - It will attempt to keep utilization of all allocated resources close to ideal, - and will automatically reconfigure to the cheapest option allowed by the ranges when necessary. -

    -

    - You can turn it on by specifying ranges in square brackets for the - nodes and/or - node resource values in services.xml. - Vespa Cloud will monitor the resource utilization of your clusters - and automatically choose the cheapest resource allocation within ranges that produces close to optimal utilization. -

    -

    - You can see the status and recent actions of the autoscaler in the Resources view - under a deployment in the console. -

    -

    -Autoscaling is not considering latency differences achieved by different configurations. -If your application has certain configurations that produce good throughput but too high latency, -you should not include these configurations in your autoscaling ranges. -

    -

    -Adjusting the allocation of a cluster may happen quickly for stateless container clusters, -and much more slowly for content clusters with a lot of data. Autoscaling will adjust each cluster -on the timescale it typically takes to rescale it (including any data redistribution). -

    -

    - The ideal utilization takes into account that a node may be down or failing, - that another region may be down causing doubling of traffic, - and that we need headroom for maintenance operations and handling requests with low latency. - It acts on what it has observed on your system in the recent past. - If you need much more capacity in the near future than you do currently, - you may want to set the lower limit to take this into account. - Upper limits should be set to the maximum size that makes business sense. -

    - - - -

    When to use autoscaling

    -

    Autoscaling is useful in a number of scenarios. Some typical ones are:

    -
      -
    • - You have a new application which you can't benchmark with realistic data and usage, - making you unsure what resources to allocate: - Set wide ranges for all resource parameters and let the system choose a configuration. Once you gain experience - you can consider tightening the configuration space. -
    • -
    • - You have load that varies quickly during the day, or that may suddenly increase quickly due to some event, - and want container cluster resources to quickly adjust to the load: - Set a range for the number of nodes and/or vcpu on containers. -
    • -
    • - You expect your data volume to grow over time, but you don't want to allocate resources prematurely, - nor constantly worry about whether it is time to increase: Configure ranges for content nodes and/or node - resources such that the size of the system grows with the data. -
    • -
    - - - -

    Resource tradeoffs

    -

    Some other considerations when deciding resources:

    -
      -
    • - Making changes to resources/nodes is easy and safe, and one of Vespa Cloud's strengths. - We advise you make controlled changes and observe effect on latencies, data migration and cost. - Everything is automated, just deploy a new application package. - This is useful learning when later needed during load peaks and capacity requirement changes. -
    • -
    • - Node resources cannot be chosen freely in all zones, CPU/Memory often comes in increments of x 2. - Try to make sure that the resource configuration is a good fit. -
    • -
    • - CPU is the most expensive component, optimize for this for most applications. -
    • -
    • - Having few nodes means more overcapacity as Vespa requires that the system will handle - one node being down (or one group, in content clusters having multiple groups). - 4-5 nodes minimum is a good rule of thumb. - Whether 4-5 or 9-10 nodes of half the size is better depends on - quicker upgrade cycles vs. smoother resource auto-scale curves. - Latencies can be better or worse, depending on static vs dynamic query cost. -
    • -
    • - Changing a node resource may mean allocating a new node, so it may be faster to scale - container nodes by changing the number of nodes. -
    • -
    • - As a consequence, during resource shortage (say almost full disk), - add nodes and keep the rest unchanged. -
    • -
    • - It is easiest to reason over capacity when changing one thing at a time. -
    • -
    -

    -It is often safe to follow the suggested resources advice when shown in the console -and feel free to contact us if you have questions. -

    - - - - -

    Mixed load

    -

    -A Vespa application must handle a combination of reads and writes, from multiple sources. -User load often resembles a sine-like curve. -Machine-generated load, like a batch job, can be spiky and abrupt. -

    -

    -In the default Vespa configuration, all kinds of load uses one default container cluster. -Example: An application where daily batch jobs update the corpus at high rate: -

    -nodes and resources -

    -Autoscaling scales up much quicker than down, as the probability of a new spike -is higher after one has been observed. -In this example, see the rapid cluster growth for the daily load spike - -followed by a slow decay. -

    -

    -The best solution for this case is to slow down the batch job, as it is of short duration. -It is not always doable to slow down jobs - in these cases, setting up multiple -container clusters -can be a smart thing - optimize each cluster for its load characteristics. -This could be a combination of clusters using autoscale and clusters with a fixed size. -Autoscaling often works best for the user-generated load, -whereas the machine-generated load could either be tuned -or routed to a different cluster in the same Vespa application. -

    - -

    Examples

    -

    - Below is an example of node resources with autoscaling that would work - well for a container cluster: -

    -
    {% highlight xml %}
    -  
    -    
    -  
    -{% endhighlight xml %}
    - -

    - The above would in general not be recommended for a content - cluster. Changing cpu, memory or disk usually leads to - allocating new nodes to fulfil the new node resources spec. When - that happens there will be redistribution of documents between the - old and new nodes and this might impact service quality to some - degree. For a content cluster it would usually be better to try to - stick to the same node resources and add or remove nodes, e.g - something like: -

    -
    {% highlight xml %}
    -  
    -    
    -  
    -{% endhighlight xml %}
    -

    - If a content cluster is configured to autoscale based on node resources - (not just number of nodes or groups) this will work fine, but note - that using paged attributes or HNSW indexes will make it more expensive - and time-consuming to redistribute documents when scaling up or down. - When doing the initial feeding of a cluster it will be best to avoid - auto-scaling, as changing the topology will require redistribution of - documents, possibly several times. -

    -

    - When using groups in a content cluster it's possible to scale the - number of groups instead of the number of nodes, e.g. with a fixed - group size and a range for the number of groups: -

    -
    {% highlight xml %}
    -  
    -    
    -  
    -{% endhighlight xml %}
    -

    - Note that at the moment it is not possible to autoscale GPU - resources per node, but you can scale the number of nodes - with GPUs: -

    -
    {% highlight xml %}
    -  
    -    
    -      
    -    
    -  
    -{% endhighlight xml %}
    - - -

    Related reading

    - diff --git a/en/operations/autoscaling.mdx b/en/operations/autoscaling.mdx new file mode 100644 index 0000000000..e06533e2ac --- /dev/null +++ b/en/operations/autoscaling.mdx @@ -0,0 +1,96 @@ +--- +title: "Autoscaling" +--- + +Autoscaling lets you adjust the hardware resources allocated to application clusters automatically depending on actual usage. It will attempt to keep utilization of all allocated resources close to ideal, and will automatically reconfigure to the cheapest option allowed by the ranges when necessary. + +You can turn it on by specifying *ranges* in square brackets for the [nodes](/en/reference/applications/services/services#nodes) and/or [node resource](/en/reference/applications/services/services#resources) values in *services.xml*. Vespa Cloud will monitor the resource utilization of your clusters and automatically choose the cheapest resource allocation within ranges that produces close to optimal utilization. + +You can see the status and recent actions of the autoscaler in the *Resources* view under a deployment in the console. + +Autoscaling is not considering latency differences achieved by different configurations. If your application has certain configurations that produce good throughput but too high latency, you should not include these configurations in your autoscaling ranges. + +Adjusting the allocation of a cluster may happen quickly for stateless container clusters, and much more slowly for content clusters with a lot of data. Autoscaling will adjust each cluster on the timescale it typically takes to rescale it (including any data redistribution). + +The ideal utilization takes into account that a node may be down or failing, that another region may be down causing doubling of traffic, and that we need headroom for maintenance operations and handling requests with low latency. It acts on what it has observed on your system in the recent past. If you need much more capacity in the near future than you do currently, you may want to set the lower limit to take this into account. Upper limits should be set to the maximum size that makes business sense. + +## When to use autoscaling + +Autoscaling is useful in a number of scenarios. Some typical ones are: + +- You have a new application which you can't benchmark with realistic data and usage, making you unsure what resources to allocate: Set wide ranges for all resource parameters and let the system choose a configuration. Once you gain experience you can consider tightening the configuration space. +- You have load that varies quickly during the day, or that may suddenly increase quickly due to some event, and want container cluster resources to quickly adjust to the load: Set a range for the number of nodes and/or vcpu on containers. +- You expect your data volume to grow over time, but you don't want to allocate resources prematurely, nor constantly worry about whether it is time to increase: Configure ranges for content nodes and/or node resources such that the size of the system grows with the data. + +## Resource tradeoffs + +Some other considerations when deciding resources: + +- Making changes to resources/nodes is easy and safe, and one of Vespa Cloud's strengths. We advise you make controlled changes and observe effect on latencies, data migration and cost. Everything is automated, just deploy a new application package. This is useful learning when later needed during load peaks and capacity requirement changes. +- Node resources cannot be chosen freely in all zones, CPU/Memory often comes in increments of x 2. Try to make sure that the resource configuration is a good fit. +- CPU is the most expensive component, optimize for this for most applications. +- Having few nodes means more overcapacity as Vespa requires that the system will handle one node being down (or one group, in content clusters having multiple groups). 4-5 nodes minimum is a good rule of thumb. Whether 4-5 or 9-10 nodes of half the size is better depends on quicker upgrade cycles vs. smoother resource auto-scale curves. Latencies can be better or worse, depending on static vs dynamic query cost. +- Changing a node resource may mean allocating a new node, so it may be faster to scale container nodes by changing the number of nodes. +- As a consequence, during resource shortage (say almost full disk), add nodes and keep the rest unchanged. +- It is easiest to reason over capacity when changing one thing at a time. + +It is often safe to follow the *suggested resources* advice when shown in the console and feel free to contact us if you have questions. + +## Mixed load + +A Vespa application must handle a combination of reads and writes, from multiple sources. User load often resembles a sine-like curve. Machine-generated load, like a batch job, can be spiky and abrupt. + +In the default Vespa configuration, all kinds of load uses *one* default container cluster. Example: An application where daily batch jobs update the corpus at high rate: + + +![nodes and resources](/assets/img/load.png) + + +Autoscaling scales *up* much quicker than *down*, as the probability of a new spike is higher after one has been observed. In this example, see the rapid cluster growth for the daily load spike - followed by a slow decay. + +The best solution for this case is to slow down the batch job, as it is of short duration. It is not always doable to slow down jobs - in these cases, setting up multiple [container clusters](/en/applications/containers) can be a smart thing - optimize each cluster for its load characteristics. This could be a combination of clusters using autoscale and clusters with a fixed size. Autoscaling often works best for the user-generated load, whereas the machine-generated load could either be tuned or routed to a different cluster in the same Vespa application. + +## Examples + +Below is an example of node resources with autoscaling that would work well for a container cluster: + +```xml + + + +``` + +The above would in general **not be recommended for a content cluster.** Changing cpu, memory or disk usually leads to allocating new nodes to fulfil the new node resources spec. When that happens there will be redistribution of documents between the old and new nodes and this might impact service quality to some degree. For a content cluster it would usually be better to try to stick to the same node resources and add or remove nodes, e.g something like: + +```xml + + + +``` + +If a content cluster is configured to autoscale based on node resources (not just number of nodes or groups) this will work fine, but note that using paged attributes or HNSW indexes will make it more expensive and time-consuming to redistribute documents when scaling up or down. When doing the initial feeding of a cluster it will be best to avoid auto-scaling, as changing the topology will require redistribution of documents, possibly several times. + +When using groups in a content cluster it's possible to scale the number of groups instead of the number of nodes, e.g. with a fixed group size and a range for the number of groups: + +```xml + + + +``` + +Note that at the moment it is not possible to autoscale GPU resources per node, but you can scale the number of nodes with GPUs: + +```xml + + + + + +``` + +## Related reading + + + + + \ No newline at end of file diff --git a/en/operations/az.html b/en/operations/az.html deleted file mode 100644 index a653d6f74a..0000000000 --- a/en/operations/az.html +++ /dev/null @@ -1,76 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Availability zones" -applies_to: cloud -redirect_from: -- /en/cloud/az.html ---- - -

    - A traditional zone in Vespa Cloud is tied to a single availability zone (AZ). Deploying to such a single-AZ - zone results in the application running in that AZ. -

    -

    - Vespa Cloud also has zones like prod.aws-us-east-1 that support multiple availability zones, - aka multi-AZ zones. - To deploy to a multi-AZ zone, you must specify a list of availability zones, using the - <availability-zone> elements in the <region> - element, see deployment.xml. -

    - - - - - -
    -

    Single-AZ zone

    - Single-AZ zone with container and content cluster -
    -

    Multi-AZ zone

    - Multi-AZ zone with clusters spread across two availability zones -
    -

    - It is an error to deploy to a multi-AZ zone and not specifying at least one AZ. - If you specify exactly one AZ, it is identical to deploying to a traditional zone tied to that AZ. -

    -

    - Specifying more than one AZ means your application instance will spread evenly across those AZ. - This means the number of nodes in each cluster must be a multiple of the number of AZs. - For content clusters, the number of groups must also be a multiple of the number of AZs. -

    -

    - For example, let's say an application instance has been configured in deployment.xml with 2 availability zones - use1-az1 and use1-az2, and a content cluster in services.xml specifies - 4 groups and a total of 12 nodes. - Each group contains 3 nodes. This means 2 groups will be placed in use1-az1, - and 2 groups will be placed in use1-az2. -

    - - - - - -
    -

    Single-AZ zone

    - Content cluster with 4 groups in a single AZ -
    -

    Multi-AZ zone

    - Content cluster with 4 groups spread across two AZs -
    - -

    Benefits to multi-AZ applications

    - -

    - Deploying a single application instance to multiple availability zones, instead of - deploying multiple application instances to each of those availability zones, has several benefits: -

    -
      -
    1. A single load balancer spanning backends across AZs is more reliable than a DNS-based global endpoint wrapping the zonal endpoints.
    2. -
    3. Document embedding is cheaper, as it needs to be done once instead of once per AZ.
    4. -
    5. Feeding to a single endpoint is simpler and more robust than feeding to one endpoint for each AZ. - For instance, if an entire AZ goes down, then feeding to a multi-AZ application instance still works. - And once the AZ comes back online, the nodes in that AZ will automatically have their documents - updated from the nodes in the other AZs.
    6. -
    7. Deploying two instances of the same application to two AZs in the same region, with redundancy=1, - is prone to data loss. Instead, one application instance can be deployed to those two AZs.
    8. -
    diff --git a/en/operations/cloning.md b/en/operations/cloning.md deleted file mode 100644 index 2324ff1530..0000000000 --- a/en/operations/cloning.md +++ /dev/null @@ -1,321 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: Cloning applications and data -applies_to: cloud -redirect_from: -- /en/cloud/cloning-applications-and-data.html ---- - -This is a guide on how to replicate a Vespa application in different environments, with or without data. -Use cases for cloning include: -* Get a copy of the application and (some) data on a laptop to work offline, or attach a debugger. -* Deploy local experiments to the `dev` environment to easily cooperate and share. -* Set up a copy of the application and (some) data to test a new major version of Vespa. -* Replicate a bug report in a non-production environment. -* Set up a copy of the application and (some) data in a `prod` environment to experiment with a CI/CD pipeline, - without touching the current production serving. -* Onboard a new team member by setting up a copy of the application and test data in a `dev` environment. -* Clone to a `dev` environment for load testing. - -This guide uses _applications_. -One can also use _instances_, but that will not work across Vespa major versions on Vespa Cloud - -refer to [tenant, applications, instances](../learn/tenant-apps-instances) for details. - -Vespa Cloud has different environments `dev` and `prod`, with different characteristics - -[details](environments.html). -Clone to `dev` for short-lived experiments/development/benchmarking, -use `prod` for serving applications with a [CI/CD pipeline](automated-deployments.html). - -As some steps are similar, it is a good idea to read through all, as details are added only the first time for brevity. -Examples are based on the -[album-recommendation](https://github.com/vespa-engine/sample-apps/tree/master/album-recommendation) sample application. - -{% include note.html content='When done, it is easy to tear down resources in Vespa Cloud. -E.g., _https://console.vespa-cloud.com/tenant/mytenant/application/myapp/prod/deploy_ or -_https://console.vespa-cloud.com/tenant/mytenant/application/myapp/dev/instance/default_ to find a delete-link. -Instances in `dev` environments are auto-expired ([details](environments.html)), -so application cloning is a safe way to work with Vespa. -Find more information in [deleting applications](deleting-applications). -'%} - - - -## Cloning - self-hosted to Vespa Cloud - -**Source setup:** -``` -$ docker run --detach --name vespa1 --hostname vespa-container1 \ - --publish 8080:8080 --publish 19071:19071 \ - vespaengine/vespa - -$ vespa deploy -t http://localhost:19071 -``` - - -**Target setup:** - -[Create a tenant](../basics/deploy-an-application.html) in the Vespa Cloud console, in this guide using "mytenant". - - -**Export source application package:** - -This gets the application package and copies it out of the container to local file system: -``` -$ vespa fetch -t http://localhost:19071 && \ - unzip application.zip -x application.zip -``` - - -**Deploy target application package** - -The procedure differs a little whether deploying to dev or prod [environment](environments.html). -The `mvn -U clean package` step is only needed for applications with custom code. -Configure application name and create data plane credentials: -
    -$ vespa config set target cloud && \
    -  vespa config set application mytenant.myapp
    -
    -$ vespa auth login
    -
    -$ vespa auth cert -f
    -
    -$ mvn -U clean package
    -
    -{% include note.html content='When deploying to a new app, -one will often want to generate a new data plane cert/key pair. -To do this, use `vespa auth cert -f`. -If reusing a cert/key pair, drop `-f` and make sure to put the pair in _.vespa_, to avoid errors like -`Error: open /Users/me/.vespa/mytenant.myapp.default/data-plane-public-cert.pem: no such file or directory` -in the subsequent deploy step.'%} -Then deploy the application. -Depending on the use case, deploy to dev or prod: -
      -
    • - dev: -
      -$ vespa deploy
      -
      - Expect something like: -
      -Uploading application package ... done
      -
      -Success: Triggered deployment of . with run ID 1
      -
      -Use vespa status for deployment status, or follow this deployment at
      -https://console.vespa-cloud.com/tenant/mytenant/application/myapp/dev/instance/default/job/dev-aws-us-east-1c/run/1
      -
      -
    • -
    • - Deployments to the prod environment requires deployment.xml - - select which zone to deploy to: -
      -$ cat <<EOF > deployment.xml
      -<deployment version="1.0">
      -    <prod>
      -        <region>aws-us-east-1c</region>
      -    </prod>
      -</deployment>
      -EOF
      -
      - prod deployments also require resources specifications - in services.xml - - use - vespa-documentation-search as an example and add/replace nodes elements - for container and content clusters. - If in doubt, just add a small config to start with, and change later: -
      -<nodes count="2">
      -    <resources vcpu="2" memory="8Gb" disk="10Gb" />
      -</nodes>
      -
      - Deploy the application package: -
      -$ vespa prod deploy
      -
      - Expect something like: -
      -Hint: See production deployment
      -Success: Deployed .
      -See https://console.vespa-cloud.com/tenant/mytenant/application/myapp/prod/deployment for deployment progress
      -
      - A proper deployment to a prod zone should have automated tests, - read more in automated deployments -
    • -
    - -**Data copy** - -Export documents from the local instance and feed to the Vespa Cloud instance: -``` -$ vespa visit -t http://localhost:8080 | vespa feed - -``` -Add more parameters as needed to `vespa feed` for other endpoints. - - -**Get access log from source:** -``` -$ docker exec vespa1 cat /opt/vespa/logs/vespa/access/JsonAccessLog.default -``` - - - -## Cloning - Vespa Cloud to self-hosted -**Download application from Vespa Cloud** - -Validate the endpoint, and fetch the application package: -``` -$ vespa config get application -application = mytenant.myapp.default - -$ vespa fetch -Downloading application package... done -Success: Application package written to application.zip -``` - -The application package can also be downloaded from the Vespa Cloud Console: -
      -
    • -

      - dev: Navigate to https://console.vespa-cloud.com/tenant/mytenant/application/myapp/dev/instance/default, - click Application to download: -

      - Application package download from dev environment -
    • -
    • -

      - prod: Navigate to https://console.vespa-cloud.com/tenant/mytenant1/application/myapp/prod/deployment?tab=builds - and select the version of the application to download: -

      - Application package download from prod environment -
    • -
    - -**Target setup:** - -Note the name of the application package .zip-file just downloaded. -If changes are needed, unzip it and use `vespa deploy -t http://localhost:19071 ` -to deploy from current directory: -``` -$ docker run --detach --name vespa1 --hostname vespa-container1 \ - --publish 8080:8080 --publish 19071:19071 \ - vespaengine/vespa - -$ vespa config set target local - -$ vespa deploy -t http://localhost:19071 mytenant.myapp.default.dev.aws-us-east-1c.zip -``` - -**Data copy** - -Set config target cloud for `vespa visit` and pipe the jsonl output into `vespa feed` to the local instance: -``` -$ vespa config set target cloud - -$ vespa visit | vespa feed - -t http://localhost:8080 -``` - -**data copy - minimal** - -For use cases requiring a few documents, visit just a few documents: -``` -$ vespa visit --chunk-count 10 -``` - -**Get access log from source:** - -Use the Vespa Cloud Console to get access logs - - - -## Cloning - Vespa Cloud to Vespa Cloud -This is a combination of the procedures above. -Download the application package from dev or prod, -make note of the source name, like mytenant.myapp.default. -Then use `vespa deploy` or `vespa prod deploy` as above to deploy to dev or prod. - -If cloning from `dev` to `prod`, pay attention to changes in _deployment.xml_ and _services.xml_ -as in [cloning to Vespa Cloud](#cloning---self-hosted-to-vespa-cloud). - -**Data copy** - -Set the feed endpoint name / paths, e.g. mytenant.myapp-new.default: -``` -$ vespa config set target cloud - -$ vespa visit | vespa feed - -t https://default.myapp-new.mytenant.aws-us-east-1c.dev.z.vespa-app.cloud -``` - -**Data copy 5%** -Set the --selection argument to `vespa visit` to select a subset of the documents. - - - - -## Cloning - self-hosted to self-hosted -Creating a copy from one self-hosted application to another. -Self-hosted means running [Vespa](https://vespa.ai/) on a laptop -or a [multinode system](self-managed/multinode-systems.html). - -This example sets up a source app and deploys the [application package](../basics/applications.html) - -use [album-recommendation](https://github.com/vespa-engine/sample-apps/tree/master/album-recommendation) -as an example. -The application package is then exported from the source and deployed to a new target app. -Steps: - -**Source setup:** -``` -$ vespa config set target local - -$ docker run --detach --name vespa1 --hostname vespa-container1 \ - --publish 8080:8080 --publish 19071:19071 \ - vespaengine/vespa - -$ vespa deploy -t http://localhost:19071 -``` - -**Target setup:** -``` -$ docker run --detach --name vespa2 --hostname vespa-container2 \ - --publish 8081:8080 --publish 19072:19071 \ - vespaengine/vespa -``` - -**Export source application package** - -Export files: -``` -$ vespa fetch -t http://localhost:19071 -``` - -**Deploy application package to target** - -Before deploying, one can make changes to the application package files as needed. Deploy to target: -``` -$ vespa deploy -t http://localhost:19072 application.zip -``` - -**Data copy from source to target** - -This pipes the source data directly into `vespa feed` - -another option is to save the data to files temporarily and feed these individually: -``` -$ vespa visit -t http://localhost:8080 | vespa feed - -t http://localhost:8081 -``` - -**Data copy 5%** - -This is an example on how to use a [selection](../reference/writing/document-selector-language.html) -to specify a subset of the documents - here a "random" 5% selection: -``` -$ vespa visit -t http://localhost:8080 --selection 'id.hash().abs() % 20 = 0' | \ - vespa feed - -t http://localhost:8081 -``` - - - -**Get access log from source** - -Get the current query access log from the source application (there might be more files there): -``` -$ docker exec vespa1 cat /opt/vespa/logs/vespa/access/JsonAccessLog.default -``` diff --git a/en/operations/cloning.mdx b/en/operations/cloning.mdx new file mode 100644 index 0000000000..ab0cfd9a9d --- /dev/null +++ b/en/operations/cloning.mdx @@ -0,0 +1,280 @@ +--- +title: "Cloning applications and data" +--- + +This is a guide on how to replicate a Vespa application in different environments, with or without data. Use cases for cloning include: + +- Get a copy of the application and (some) data on a laptop to work offline, or attach a debugger. +- Deploy local experiments to the `dev` environment to easily cooperate and share. +- Set up a copy of the application and (some) data to test a new major version of Vespa. +- Replicate a bug report in a non-production environment. +- Set up a copy of the application and (some) data in a `prod` environment to experiment with a CI/CD pipeline, without touching the current production serving. +- Onboard a new team member by setting up a copy of the application and test data in a `dev` environment. +- Clone to a `dev` environment for load testing. + +This guide uses *applications*. One can also use *instances*, but that will not work across Vespa major versions on Vespa Cloud - refer to [tenant, applications, instances](/en/learn/tenant-apps-instances) for details. + +Vespa Cloud has different environments `dev` and `prod`, with different characteristics - [details](/en/operations/environments). Clone to `dev` for short-lived experiments/development/benchmarking, use `prod` for serving applications with a [CI/CD pipeline](/en/operations/automated-deployments). + +As some steps are similar, it is a good idea to read through all, as details are added only the first time for brevity. Examples are based on the [album-recommendation](https://github.com/vespa-engine/sample-apps/tree/master/album-recommendation) sample application. + + +**Note:** + +When done, it is easy to tear down resources in Vespa Cloud. E.g., *https://console.vespa-cloud.com/tenant/mytenant/application/myapp/prod/deploy* or *https://console.vespa-cloud.com/tenant/mytenant/application/myapp/dev/instance/default* to find a delete-link. Instances in `dev` environments are auto-expired ([details](/en/operations/environments)), so application cloning is a safe way to work with Vespa. Find more information in [deleting applications](/en/operations/deleting-applications). + + +## Cloning - self-hosted to Vespa Cloud + +**Source setup:** + +```bash +$ docker run --detach --name vespa1 --hostname vespa-container1 \ + --publish 8080:8080 --publish 19071:19071 \ + vespaengine/vespa + +$ vespa deploy -t http://localhost:19071 +``` + +**Target setup:** + +[Create a tenant](/en/basics/deploy-an-application) in the Vespa Cloud console, in this guide using "mytenant". + +**Export source application package:** + +This gets the application package and copies it out of the container to local file system: + +```bash +$ vespa fetch -t http://localhost:19071 && \ + unzip application.zip -x application.zip +``` + +**Deploy target application package** + +The procedure differs a little whether deploying to dev or prod [environment](/en/operations/environments). The `mvn -U clean package` step is only needed for applications with custom code. Configure application name and create data plane credentials: + +```bash +$ vespa config set target cloud && \ + vespa config set application mytenant.myapp + +$ vespa auth login + +$ vespa auth cert -f + +$ mvn -U clean package +``` + + +**Note:** + +When deploying to a new app, one will often want to generate a new data plane cert/key pair. To do this, use `vespa auth cert -f`. If reusing a cert/key pair, drop `-f` and make sure to put the pair in *.vespa*, to avoid errors like `Error: open /Users/me/.vespa/mytenant.myapp.default/data-plane-public-cert.pem: no such file or directory` in the subsequent deploy step. + + +Then deploy the application. Depending on the use case, deploy to `dev` or `prod`: + +- `dev`: + + ```bash + $ vespa deploy + ``` + Expect something like: + + ```bash + Uploading application package ... done + + Success: Triggered deployment of . with run ID 1 + + Use vespa status for deployment status, or follow this deployment at + https://console.vespa-cloud.com/tenant/mytenant/application/myapp/dev/instance/default/job/dev-aws-us-east-1c/run/1 + ``` +- Deployments to the `prod` environment requires [deployment.xml](/en/reference/applications/deployment) - select which [zone](/en/operations/zones) to deploy to: + + ```bash + $ cat < deployment.xml + + + aws-us-east-1c + + + EOF + ``` + + `prod` deployments also require `resources` specifications in [services.xml](/en/reference/applications/services/services) - use [vespa-documentation-search](https://github.com/vespa-cloud/vespa-documentation-search/blob/main/src/main/application/services.xml) as an example and add/replace `nodes` elements for `container` and `content` clusters. If in doubt, just add a small config to start with, and change later: + + ```bash + + + + ``` + Deploy the application package: + + ```bash + $ vespa prod deploy + ``` + + Expect something like: + + `Hint: See` [`production deployment`](/en/reference/applications/deployment)
    + `Success: Deployed` .
    + `See https://console.vespa-cloud.com/tenant/mytenant/application/myapp/prod/deployment for deployment progress` + + A proper deployment to a `prod` zone should have automated tests, read more in [automated deployments](/en/operations/automated-deployments) + +**Data copy** + +Export documents from the local instance and feed to the Vespa Cloud instance: + +```bash +$ vespa visit -t http://localhost:8080 | vespa feed - +``` + +Add more parameters as needed to `vespa feed` for other endpoints. + +**Get access log from source:** + +```bash +$ docker exec vespa1 cat /opt/vespa/logs/vespa/access/JsonAccessLog.default +``` + +## Cloning - Vespa Cloud to self-hosted + +**Download application from Vespa Cloud** + +Validate the endpoint, and fetch the application package: + +```bash +$ vespa config get application +application = mytenant.myapp.default + +$ vespa fetch +Downloading application package... done +Success: Application package written to application.zip +``` + +The application package can also be downloaded from the Vespa Cloud Console: + +- dev: Navigate to *https://console.vespa-cloud.com/tenant/mytenant/application/myapp/dev/instance/default*, click *Application* to download: + ![Application package download from dev environment](/assets/img/app-download-dev.png) +- prod: Navigate to *https://console.vespa-cloud.com/tenant/mytenant1/application/myapp/prod/deployment?tab=builds* and select the version of the application to download: + ![Application package download from prod environment](/assets/img/app-download-prod.png) + +**Target setup:** + +Note the name of the application package .zip-file just downloaded. If changes are needed, unzip it and use `vespa deploy -t http://localhost:19071 ` to deploy from current directory: + +```bash +$ docker run --detach --name vespa1 --hostname vespa-container1 \ + --publish 8080:8080 --publish 19071:19071 \ + vespaengine/vespa + +$ vespa config set target local + +$ vespa deploy -t http://localhost:19071 mytenant.myapp.default.dev.aws-us-east-1c.zip +``` + +**Data copy** + +Set config target cloud for `vespa visit` and pipe the jsonl output into `vespa feed` to the local instance: + +```bash +$ vespa config set target cloud + +$ vespa visit | vespa feed - -t http://localhost:8080 +``` + +**data copy - minimal** + +For use cases requiring a few documents, visit just a few documents: + +```bash +$ vespa visit --chunk-count 10 +``` + +**Get access log from source:** + +Use the Vespa Cloud Console to get access logs + +## Cloning - Vespa Cloud to Vespa Cloud + +This is a combination of the procedures above. Download the application package from dev or prod, make note of the source name, like mytenant.myapp.default. Then use `vespa deploy` or `vespa prod deploy` as above to deploy to dev or prod. + +If cloning from `dev` to `prod`, pay attention to changes in *deployment.xml* and *services.xml* as in [cloning to Vespa Cloud](#cloning---self-hosted-to-vespa-cloud). + +**Data copy** + +Set the feed endpoint name / paths, e.g. mytenant.myapp-new.default: + +```bash +$ vespa config set target cloud + +$ vespa visit | vespa feed - -t https://default.myapp-new.mytenant.aws-us-east-1c.dev.z.vespa-app.cloud +``` + +**Data copy 5%** Set the –selection argument to `vespa visit` to select a subset of the documents. + +## Cloning - self-hosted to self-hosted + +Creating a copy from one self-hosted application to another. Self-hosted means running [Vespa](https://vespa.ai/) on a laptop or a [multinode system](/en/operations/self-managed/multinode-systems). + +This example sets up a source app and deploys the [application package](/en/basics/applications) - use [album-recommendation](https://github.com/vespa-engine/sample-apps/tree/master/album-recommendation) as an example. The application package is then exported from the source and deployed to a new target app. Steps: + +**Source setup:** + +```bash +$ vespa config set target local + +$ docker run --detach --name vespa1 --hostname vespa-container1 \ + --publish 8080:8080 --publish 19071:19071 \ + vespaengine/vespa + +$ vespa deploy -t http://localhost:19071 +``` + +**Target setup:** + +```bash +$ docker run --detach --name vespa2 --hostname vespa-container2 \ + --publish 8081:8080 --publish 19072:19071 \ + vespaengine/vespa +``` + +**Export source application package** + +Export files: + +```bash +$ vespa fetch -t http://localhost:19071 +``` + +**Deploy application package to target** + +Before deploying, one can make changes to the application package files as needed. Deploy to target: + +```bash +$ vespa deploy -t http://localhost:19072 application.zip +``` + +**Data copy from source to target** + +This pipes the source data directly into `vespa feed` - another option is to save the data to files temporarily and feed these individually: + +```bash +$ vespa visit -t http://localhost:8080 | vespa feed - -t http://localhost:8081 +``` + +**Data copy 5%** + +This is an example on how to use a [selection](/en/reference/writing/document-selector-language) to specify a subset of the documents - here a "random" 5% selection: + +```bash +$ vespa visit -t http://localhost:8080 --selection 'id.hash().abs() % 20 = 0' | \ + vespa feed - -t http://localhost:8081 +``` + +**Get access log from source** + +Get the current query access log from the source application (there might be more files there): + +```bash +$ docker exec vespa1 cat /opt/vespa/logs/vespa/access/JsonAccessLog.default +``` \ No newline at end of file diff --git a/en/operations/data-management.html b/en/operations/data-management.html deleted file mode 100644 index 910d7c750d..0000000000 --- a/en/operations/data-management.html +++ /dev/null @@ -1,278 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: Data management and backup -applies_to: cloud -redirect_from: -- /en/cloud/data-management-and-backup ---- - -

    - This guide covers data management operations for Vespa Cloud applications, - including automated backups, document export, feed, and bulk updates and removals. -

    - -

    Automated Backups

    -

    - Depending on plan, content clusters are automatically backed up when a - <backup> element is specified in deployment.xml. - Vespa Cloud manages the backup schedule, storage, and lifecycle with no external tooling required. Backups will run at the configured frequency - while also respecting any maintenance block windows - (<block-change maintenance="true">) defined for the instance. -

    -
    {% highlight xml %}
    -
    -    
    -    
    -        aws-us-east-1c
    -    
    -
    -{% endhighlight %}
    -

    - Backups are retained for three backup intervals (e.g. 21 days for a 7-day frequency). - The most recent fully completed backup is always retained regardless of age. - See Restore from Backup for how to request a restore. -

    -

    - If you prefer to manage backups yourself, documents can be exported manually using - vespa visit as shown in the - Google Cloud Function example. -

    - - -

    Restore from Backup

    -

    - Restoring from a backup is handled by Vespa Cloud. To initiate a restore, contact - Vespa Support. Response time and priority handling are governed by your - support plan. -

    -

    - Restore requires a deployed target cluster with: -

    -
      -
    • The same number of content nodes as the backup.
    • -
    • At least equivalent disk capacity per node as at the time of the backup.
    • -
    -

    - Note that content redistribution is usually required after restoration. - See backup reference for details. -

    - -

    Export documents

    -{% include note.html content='The examples below use the Vespa CLI. Ensure you have the latest version installed.' %} -

    - To export documents, configure the application to export from, - then select zone, container cluster and schema - example: -

    -
    -$ vespa config set application vespa-team.vespacloud-docsearch.default
    -
    -$ vespa visit --zone prod.aws-us-east-1c --cluster default --selection doc | head
    -
    -

    - Some of the parameters above are redundant if unambiguous. - Here, the application is set up using a template found in - multinode-HA - with multiple container clusters. - This example visit - documents from the doc schema. -

    -

    Use a fieldset to export document IDs only:

    -
    -$ vespa visit --zone prod.aws-us-east-1c --cluster default --selection doc --field-set '[id]' | head
    -
    -{% include note.html content='Configuring the documentid field to be an attribute in the schema avoids that this requires disk access and, hence, speeds up the exporting process.' %} -

    - As the name implies, fieldsets are useful to select a subset of fields to export. - Note that, if disk access is required to fetch a field from the fieldset, selecting fewer - fields does not speed up the exporting process as the same amount of data is read from the index. - The data transfer out of the Vespa application is smaller with fewer fields. -

    -

    -For copying documents between applications, see cloning applications and data. -

    - -

    Feed

    -

    - If a document feed is generated with vespa visit (above), - it is already in JSON Lines feed-ready format by default: -

    -
    -$ vespa visit | vespa feed - -t $ENDPOINT
    -
    -

    - Find more examples in cloning applications and data. -

    -

    - A document export generated using /document/v1 - is slightly different from the .jsonl output from vespa visit - (e.g., fields like a continuation token are added). - Extract the document objects before feeding: -

    -
    -$ gunzip -c docs.gz | jq '.documents[]' | \
    -  vespa feed - -t $ENDPOINT
    -
    - - - -

    Delete

    -

    - To remove all documents in a Vespa deployment—or a selection of them—run a deletion visit. - Use the DELETE HTTP method, and fetch only the continuation token from the response: -

    -
    -#!/bin/bash
    -
    -set -x
    -
    -# The ENDPOINT must be a regional endpoint, do not use '*.g.vespa-app.cloud/'
    -ENDPOINT="https://vespacloud-docsearch.vespa-team.aws-us-east-1c.z.vespa-app.cloud"
    -NAMESPACE=open
    -DOCTYPE=doc
    -CLUSTER=documentation
    -
    -# doc.path =~ "^/old/" -- all documents under the /old/ directory:
    -SELECTION='doc.path%3D~%22%5E%2Fold%2F%22'
    -
    -continuation=""
    -
    -while
    -  token=$( curl -X DELETE -s \
    -           --cert data-plane-public-cert.pem \
    -           --key data-plane-private-key.pem \
    -           "${ENDPOINT}/document/v1/${NAMESPACE}/${DOCTYPE}/docid?selection=${SELECTION}&cluster=${CLUSTER}&${continuation}" \
    -           | tee >( jq . > /dev/tty ) | jq -re .continuation )
    -do
    -  continuation="continuation=${token}"
    -done
    -
    -

    - Each request will return a response after roughly one minute—change this by specifying timeChunk (default 60). -

    -

    - To purge all documents in a document export (above), - generate a feed with remove-entries for each document ID, like: -

    -
    -$ gunzip -c docs.gz | jq '[ .documents[] | {remove: .id} ]' | head
    -
    -[
    -  {
    -    "remove": "id:open:doc::open/documentation/schemas.html"
    -  },
    -  {
    -    "remove": "id:open:doc::open/documentation/securing-your-vespa-installation.html"
    -  },
    -
    -

    Complete example for a single chunk:

    -
    -$ gunzip -c docs.gz | jq '[ .documents[] | {remove: .id} ]' | \
    -  vespa feed - -t $ENDPOINT
    -
    - - - -

    Update

    -

    - To update all documents in a Vespa deployment—or a selection of them—run an update visit. - Use the PUT HTTP method, and specify a partial update in the request body: -

    -
    -#!/bin/bash
    -
    -set -x
    -
    -# The ENDPOINT must be a regional endpoint, do not use '*.g.vespa-app.cloud/'
    -ENDPOINT="https://vespacloud-docsearch.vespa-team.aws-us-east-1c.z.vespa-app.cloud"
    -NAMESPACE=open
    -DOCTYPE=doc
    -CLUSTER=documentation
    -
    -# doc.inlinks == "some-url" -- the weightedset<string> inlinks has the key "some-url"
    -SELECTION='doc.inlinks%3D%3D%22some-url%22'
    -
    -continuation=""
    -
    -while
    -  token=$( curl -X PUT -s \
    -           --cert data-plane-public-cert.pem \
    -           --key data-plane-private-key.pem \
    -           --data '{ "fields": { "inlinks": { "remove": { "some-url": 0 } } } }' \
    -           "${ENDPOINT}/document/v1/${NAMESPACE}/${DOCTYPE}/docid?selection=${SELECTION}&cluster=${CLUSTER}&${continuation}" \
    -           | tee >( jq . > /dev/tty ) | jq -re .continuation )
    -do
    -  continuation="continuation=${token}"
    -done
    -
    -

    - Each request will return a response after roughly one minute—change this by specifying timeChunk (default 60). -

    - - - -

    Using /document/v1/ api

    -

    - To get started with a document export, find the namespace and document type by listing a few IDs. - Hit the /document/v1/ ENDPOINT. - Restrict to one CLUSTER, see content clusters: -

    -
    -$ curl \
    -  --cert data-plane-public-cert.pem \
    -  --key data-plane-private-key.pem \
    -  "$ENDPOINT/document/v1/?cluster=$CLUSTER"
    -
    - -

    For ID export only, use a fieldset:

    -
    -$ curl \
    -  --cert data-plane-public-cert.pem \
    -  --key data-plane-private-key.pem \
    -  "$ENDPOINT/document/v1/?cluster=$CLUSTER&fieldSet=%5Bid%5D"
    -
    - -

    From an ID, like id:open:doc::open/documentation/schemas.html, extract

    -
      -
    • NAMESPACE: open
    • -
    • DOCTYPE: doc
    • -
    - -

    Example script:

    -
    -#!/bin/bash
    -
    -set -x
    -
    -# The ENDPOINT must be a regional endpoint, do not use '*.g.vespa-app.cloud/'
    -ENDPOINT="https://vespacloud-docsearch.vespa-team.aws-us-east-1c.z.vespa-app.cloud"
    -NAMESPACE=open
    -DOCTYPE=doc
    -CLUSTER=documentation
    -
    -continuation=""
    -idx=0
    -
    -while
    -  ((idx+=1))
    -  echo "$continuation"
    -  printf -v out "%05g" $idx
    -  filename=${NAMESPACE}-${DOCTYPE}-${out}.data.gz
    -  echo "Fetching data..."
    -  token=$( curl -s \
    -           --cert data-plane-public-cert.pem \
    -           --key data-plane-private-key.pem \
    -           "${ENDPOINT}/document/v1/${NAMESPACE}/${DOCTYPE}/docid?wantedDocumentCount=1000&concurrency=4&cluster=${CLUSTER}&${continuation}" \
    -           | tee >( gzip > ${filename} ) | jq -re .continuation )
    -do
    -  continuation="continuation=${token}"
    -done
    -
    -

    - If only a few documents are returned per response, wantedDocumentCount (default 1, max 1024) can be - specified for a lower bound on the number of documents per response, if that many documents still remain. -

    -

    - Specifying concurrency (default 1, max 100) increases throughput, at the cost of resource usage. - This also increases the number of documents per response, and could lead to excessive memory usage - in the HTTP container when many large documents are buffered to be returned in the same response. -

    diff --git a/en/operations/data-management.mdx b/en/operations/data-management.mdx new file mode 100644 index 0000000000..5fd23b3bdf --- /dev/null +++ b/en/operations/data-management.mdx @@ -0,0 +1,233 @@ +--- +title: "Data management and backup" +--- + +This guide covers data management operations for Vespa Cloud applications, including automated backups, document export, feed, and bulk updates and removals. + +## Automated Backups + +Depending on [plan](https://vespa.ai/pricing/), content clusters are automatically backed up when a [``](/en/reference/applications/deployment#backup) element is specified in *deployment.xml*. Vespa Cloud manages the backup schedule, storage, and lifecycle with no external tooling required. Backups will run at the configured frequency while also respecting any [block windows](/en/reference/applications/deployment#block-change) defined for the instance. + +```xml + + + + aws-us-east-1c + + +``` + +Backups are retained for three backup intervals (e.g. 21 days for a 7-day frequency). The most recent fully completed backup is always retained regardless of age. See [Restore from Backup](#restore) for how to request a restore. + +If you prefer to manage backups yourself, documents can be exported manually using `vespa visit` as shown in the [Google Cloud Function example](https://github.com/vespa-engine/sample-apps/tree/master/examples/google-cloud/cloud-functions#backup---experimental). + +## Restore from Backup + +Restoring from a backup is handled by Vespa Cloud. To initiate a restore, contact [Vespa Support](https://vespa.ai/support/). Response time and priority handling are governed by your [support plan](https://vespa.ai/pricing/). + +Restore requires a deployed target cluster with: + +- The same number of content nodes as the backup. +- At least equivalent disk capacity per node as at the time of the backup. + +Note that content redistribution is usually required after restoration. See [backup reference](/en/reference/applications/deployment#backup) for details. + +## Export documents + + +**Note:** + +The examples below use the [Vespa CLI](/en/clients/vespa-cli). Ensure you have the latest version installed. + + +To export documents, configure the application to export from, then select zone, container cluster and schema - example: + +```bash +$ vespa config set application vespa-team.vespacloud-docsearch.default + +$ vespa visit --zone prod.aws-us-east-1c --cluster default --selection doc | head +``` + +Some of the parameters above are redundant if unambiguous. Here, the application is set up using a template found in [multinode-HA](https://github.com/vespa-engine/sample-apps/tree/master/examples/operations/multinode-HA) with multiple container clusters. This example [visit](/en/writing/visiting) documents from the `doc` schema. + +Use a [fieldset](/en/schemas/documents#fieldsets) to export document IDs only: + +```bash +$ vespa visit --zone prod.aws-us-east-1c --cluster default --selection doc --field-set '[id]' | head +``` + + +**Note:** + +Configuring the [`documentid`](/en/reference/schemas/schemas#documentid) field to be an attribute in the schema avoids that this requires disk access and, hence, speeds up the exporting process. + + +As the name implies, fieldsets are useful to select a subset of fields to export. Note that, if disk access is required to fetch a field from the fieldset, selecting fewer fields does not speed up the exporting process as the same amount of data is read from the index. The data transfer out of the Vespa application is smaller with fewer fields. + +For copying documents between applications, see [cloning applications and data](/en/operations/cloning). + +## Feed + +If a document feed is generated with `vespa visit` (above), it is already in [JSON Lines](https://jsonlines.org/) feed-ready format by default: + +```bash +$ vespa visit | vespa feed - -t $ENDPOINT +``` + +Find more examples in [cloning applications and data](/en/operations/cloning). + +A document export generated using [/document/v1](/en/writing/document-v1-api-guide) is slightly different from the .jsonl output from `vespa visit` (e.g., fields like a continuation token are added). Extract the `document` objects before feeding: + +```bash +$ gunzip -c docs.gz | jq '.documents[]' | \ + vespa feed - -t $ENDPOINT +``` + +## Delete + +To remove all documents in a Vespa deployment—or a selection of them—run a *deletion visit*. Use the `DELETE` HTTP method, and fetch only the continuation token from the response: + +```bash expandable +#!/bin/bash + +set -x + +# The ENDPOINT must be a regional endpoint, do not use '*.g.vespa-app.cloud/' +ENDPOINT="https://vespacloud-docsearch.vespa-team.aws-us-east-1c.z.vespa-app.cloud" +NAMESPACE=open +DOCTYPE=doc +CLUSTER=documentation + +# doc.path =~ "^/old/" -- all documents under the /old/ directory: +SELECTION='doc.path%3D~%22%5E%2Fold%2F%22' + +continuation="" + +while + token=$( curl -X DELETE -s \ + --cert data-plane-public-cert.pem \ + --key data-plane-private-key.pem \ + "${ENDPOINT}/document/v1/${NAMESPACE}/${DOCTYPE}/docid?selection=${SELECTION}&cluster=${CLUSTER}&${continuation}" \ + | tee >( jq . > /dev/tty ) | jq -re .continuation ) +do + continuation="continuation=${token}" +done +``` + +Each request will return a response after roughly one minute—change this by specifying *timeChunk* (default 60). + +To purge all documents in a document export (above), generate a feed with `remove`\-entries for each document ID, like: + +```bash +$ gunzip -c docs.gz | jq '[ .documents[] | {remove: .id} ]' | head + +[ + { + "remove": "id:open:doc::open/documentation/schemas.html" + }, + { + "remove": "id:open:doc::open/documentation/securing-your-vespa-installation.html" + }, +``` + +Complete example for a single chunk: + +```bash +$ gunzip -c docs.gz | jq '[ .documents[] | {remove: .id} ]' | \ + vespa feed - -t $ENDPOINT +``` + +## Update + +To update all documents in a Vespa deployment—or a selection of them—run an *update visit*. Use the `PUT` HTTP method, and specify a partial update in the request body: + +```bash expandable +#!/bin/bash + +set -x + +# The ENDPOINT must be a regional endpoint, do not use '*.g.vespa-app.cloud/' +ENDPOINT="https://vespacloud-docsearch.vespa-team.aws-us-east-1c.z.vespa-app.cloud" +NAMESPACE=open +DOCTYPE=doc +CLUSTER=documentation + +# doc.inlinks == "some-url" -- the weightedset inlinks has the key "some-url" +SELECTION='doc.inlinks%3D%3D%22some-url%22' + +continuation="" + +while + token=$( curl -X PUT -s \ + --cert data-plane-public-cert.pem \ + --key data-plane-private-key.pem \ + --data '{ "fields": { "inlinks": { "remove": { "some-url": 0 } } } }' \ + "${ENDPOINT}/document/v1/${NAMESPACE}/${DOCTYPE}/docid?selection=${SELECTION}&cluster=${CLUSTER}&${continuation}" \ + | tee >( jq . > /dev/tty ) | jq -re .continuation ) +do + continuation="continuation=${token}" +done +``` + +Each request will return a response after roughly one minute—change this by specifying *timeChunk* (default 60). + +## Using /document/v1/ api + +To get started with a document export, find the *namespace* and *document type* by listing a few IDs. Hit the [/document/v1/](/en/reference/api/document-v1) ENDPOINT. Restrict to one CLUSTER, see [content clusters](/en/reference/applications/services/content): + +```bash +$ curl \ + --cert data-plane-public-cert.pem \ + --key data-plane-private-key.pem \ + "$ENDPOINT/document/v1/?cluster=$CLUSTER" +``` + +For ID export only, use a [fieldset](/en/schemas/documents#fieldsets): + +```bash +$ curl \ + --cert data-plane-public-cert.pem \ + --key data-plane-private-key.pem \ + "$ENDPOINT/document/v1/?cluster=$CLUSTER&fieldSet=%5Bid%5D" +``` + +From an ID, like *id:open:doc::open/documentation/schemas.html*, extract + +- NAMESPACE: open +- DOCTYPE: doc + +Example script: + +```bash expandable +#!/bin/bash + +set -x + +# The ENDPOINT must be a regional endpoint, do not use '*.g.vespa-app.cloud/' +ENDPOINT="https://vespacloud-docsearch.vespa-team.aws-us-east-1c.z.vespa-app.cloud" +NAMESPACE=open +DOCTYPE=doc +CLUSTER=documentation + +continuation="" +idx=0 + +while + ((idx+=1)) + echo "$continuation" + printf -v out "%05g" $idx + filename=${NAMESPACE}-${DOCTYPE}-${out}.data.gz + echo "Fetching data..." + token=$( curl -s \ + --cert data-plane-public-cert.pem \ + --key data-plane-private-key.pem \ + "${ENDPOINT}/document/v1/${NAMESPACE}/${DOCTYPE}/docid?wantedDocumentCount=1000&concurrency=4&cluster=${CLUSTER}&${continuation}" \ + | tee >( gzip > ${filename} ) | jq -re .continuation ) +do + continuation="continuation=${token}" +done +``` + +If only a few documents are returned per response, *wantedDocumentCount* (default 1, max 1024) can be specified for a lower bound on the number of documents per response, if that many documents still remain. + +Specifying *concurrency* (default 1, max 100) increases throughput, at the cost of resource usage. This also increases the number of documents per response, and *could* lead to excessive memory usage in the HTTP container when many large documents are buffered to be returned in the same response. \ No newline at end of file diff --git a/en/operations/deleting-applications.md b/en/operations/deleting-applications.md deleted file mode 100644 index 9f88ca40b2..0000000000 --- a/en/operations/deleting-applications.md +++ /dev/null @@ -1,57 +0,0 @@ ---- -# Copyright Verizon Media. All rights reserved. -title: Deleting Applications -applies_to: cloud -redirect_from: -- /en/cloud/deleting-applications ---- - -{% include warning.html content='Following these steps will remove production instances or regions and all data within them. -Data will be unrecoverable.' %} - - - -## Deleting an application -To delete an application, use the console: -* navigate to the *application* view at -https://console.vespa-cloud.com/tenant/tenant-name/application where you can find the trash -can icon to the far right, as an `ACTION`. -* navigate to the *deploy* view at -*https://console.vespa-cloud.com/tenant/tenant-name/application/app-name/prod/deploy*. - -![delete production deployment](/assets/img/console/delete-production-deployment.png) - -When the application deployments are deleted, -delete the application in the [console](https://console.vespa-cloud.com). -Remove the CI job that builds and deploys application packages, if any. - - - -## Deleting an instance / region -To remove an instance or a deployment to a region from an application: - -1. Remove the `region` from `prod`, or the `instance` from `deployment` - in [deployment.xml](../reference/applications/deployment.html#instance): - - ```xml - - - aws-us-east-1c - - - - - ``` - -2. Add or modify [validation-overrides.xml](../reference/applications/validation-overrides.html), - allowing Vespa Cloud to remove production instances: - - ```xml - - deployment-removal - - global-endpoint-change - - ``` - -3. Build and deploy the application package. diff --git a/en/operations/deleting-applications.mdx b/en/operations/deleting-applications.mdx new file mode 100644 index 0000000000..eaa5c13397 --- /dev/null +++ b/en/operations/deleting-applications.mdx @@ -0,0 +1,57 @@ +--- +title: "Deleting Applications" +sidebarTitle: "Deleting Applications" +--- + + +**Warning:** + +Following these steps will remove production instances or regions and all data within them. Data will be unrecoverable. + + + +## Deleting an application + +To delete an application, use the console: + +- navigate to the *application* view at https://console.vespa-cloud.com/tenant/tenant-name/application where you can find the trash can icon to the far right, as an `ACTION`. +- navigate to the *deploy* view at *https://console.vespa-cloud.com/tenant/tenant-name/application/app-name/prod/deploy*. + + +![delete production deployment](/assets/img/console/delete-production-deployment.png) + + +When the application deployments are deleted, delete the application in the [console](https://console.vespa-cloud.com). Remove the CI job that builds and deploys application packages, if any. + +## Deleting an instance / region + +To remove an instance or a deployment to a region from an application: + + + +Remove the `region` from `prod`, or the `instance` from `deployment` in [deployment.xml](/en/reference/applications/deployment#instance): +```xml + + + aws-us-east-1c + + + + +``` + + +Add or modify [validation-overrides.xml](/en/reference/applications/validation-overrides), allowing Vespa Cloud to remove production instances: + +```xml + + deployment-removal + + global-endpoint-change + +``` + + +Build and deploy the application package. + + \ No newline at end of file diff --git a/en/operations/deployment-patterns.md b/en/operations/deployment-patterns.mdx similarity index 68% rename from en/operations/deployment-patterns.md rename to en/operations/deployment-patterns.mdx index acc0dc4668..08a6aa4595 100644 --- a/en/operations/deployment-patterns.md +++ b/en/operations/deployment-patterns.mdx @@ -1,27 +1,17 @@ --- -# Copyright Vespa.ai. All rights reserved. -title: Deployment patterns -applies_to: cloud -redirect_from: -- /en/cloud/deployment-patterns +title: "Deployment patterns" --- - - -Vespa Cloud's [automated deployments](automated-deployments.html) -lets you design CD pipelines for staged rollouts and multi-zone deployments. -This guide documents some of these patterns. - - +Vespa Cloud's [automated deployments](/en/operations/automated-deployments) lets you design CD pipelines for staged rollouts and multi-zone deployments. This guide documents some of these patterns. ## Two regions, two AZs each, sequenced deployment + This is the simplest pattern, deploy to a set of zones/regions, in a sequence: + ![Two regions, two AZs each, sequenced deployment](/assets/img/pipeline-1.png) + + ```xml @@ -33,12 +23,14 @@ This is the simplest pattern, deploy to a set of zones/regions, in a sequence: ``` - - ## Two regions, two AZs each, parallel deployment + Same as above, but deploying all zones in parallel: + ![Two regions, two AZs each, parallel deployment](/assets/img/pipeline-2.png) + + ```xml @@ -52,12 +44,14 @@ Same as above, but deploying all zones in parallel: ``` - - ## Two regions, two AZs each, parallel deployment inside region + Deploy to the use1 region first, both AZs in parallel, then the use2 region, both AZs in parallel: + ![Two regions, two AZs each, parallel deployment inside region](/assets/img/pipeline-3.png) + + ```xml @@ -73,11 +67,14 @@ Deploy to the use1 region first, both AZs in parallel, then the use2 region, bot ``` - ## Deploy to a test instance first + Deploy to a (downscaled) instance first, and add a delay before propagating to later instances and zones. + ![With a canary instance](/assets/img/canary-instance-one-app.png) + + ```xml @@ -94,9 +91,10 @@ Deploy to a (downscaled) instance first, and add a delay before propagating to l ``` -### Deployment variants -[Deployment variants](deployment-variants.html) are useful to set up a downscaled instance. -In [services.xml](../reference/applications/services/services.html), override settings per instance: +### Deployment variants + +[Deployment variants](/en/operations/deployment-variants) are useful to set up a downscaled instance. In [services.xml](/en/reference/applications/services/services), override settings per instance: + ```xml @@ -106,27 +104,30 @@ In [services.xml](../reference/applications/services/services.html), override se ``` - - ## Test and prod instances as separate applications -In the section before, we modeled the test and prod app as one pipeline. -This lets users halt the pipeline (using the delay) before prod propagation. + +In the section before, we modeled the test and prod app as one pipeline. This lets users halt the pipeline (using the delay) before prod propagation. In some cases, this is better modeled as different applications: -* The CI pipeline is multistep, with approvals and use of different branches -The below uses different _applications_ to model the flow, these are completely separate application instances. -The application owner will model the flow in own tool, and orchestrate deployments to Vespa Cloud as fit: +- The CI pipeline is multistep, with approvals and use of different branches + +The below uses different *applications* to model the flow, these are completely separate application instances. The application owner will model the flow in own tool, and orchestrate deployments to Vespa Cloud as fit: -![canary app](/assets/img/canaryapp.png) + +![canary app](/assets/img/canaryapp.png) + + ![prod app](/assets/img/prodapp.png) + -The important point is, these are two _separate_ deploy commands to Vespa Cloud: +The important point is, these are two *separate* deploy commands to Vespa Cloud: -```shell +```bash $ vespa config set application tenant1.canaryapp $ vespa prod deploy app ``` + ```xml @@ -137,10 +138,11 @@ $ vespa prod deploy app ``` -```shell +```bash $ vespa config set application tenant1.prodapp $ vespa prod deploy app ``` + ```xml @@ -151,10 +153,9 @@ $ vespa prod deploy app ``` - - ## services.xml structure -It is possible to split _services.xml_ to more file using includes: + +It is possible to split *services.xml* to more file using includes: ```xml @@ -163,11 +164,16 @@ It is possible to split _services.xml_ to more file using includes: ``` -Note: The include-feature can not be used in combination with [deployment variants](#deployment-variants). - + +**Note:** +The include-feature can not be used in combination with [deployment variants](#deployment-variants). + ## Next reads -* [Environments](environments.html) -* [Zones](zones.html) -* [Routing](endpoint-routing.html) + + + + + + \ No newline at end of file diff --git a/en/operations/deployment-variants.html b/en/operations/deployment-variants.html deleted file mode 100644 index a3fe0b24ad..0000000000 --- a/en/operations/deployment-variants.html +++ /dev/null @@ -1,182 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Application, instance, region, cloud and environment variants" -applies_to: cloud -redirect_from: -- en/reference/deployment-variants.html ---- - -

    - Sometimes it is useful to create configuration that varies depending on properties of the deployment, - for example to set region specific endpoints of services used by Searchers, - use smaller clusters for a "beta" instance, or vary configuration when the same application package is shared across - multiple applications. -

    -

    - This is supported both for services.xml and - query profiles. -

    - - - -

    services.xml variants

    -

    - services.xml files support different configuration settings - for different tags, applications, instances, environments, clouds and regions. - To use this, import the deploy namespace: -

    -
    {% highlight xml %}
    -
    -{% endhighlight %}
    -

    - Deploy directives are used to specify with which tags, and in which application, instance, environment, - cloud and/or region an XML element should be included: -

    -
    {% highlight xml %}
    -
    -    2
    -    
    -        
    -    
    -    
    -    
    -        
    -    
    -    
    -    
    -        
    -    
    -    
    -        
    -    
    -    
    -        
    -    
    -    
    -
    -{% endhighlight %}
    -

    - The example above configures different node counts/configurations depending on the deployment target. - Deploying the application in the dev environment gives: -

    -
    {% highlight xml %}
    -
    -    2
    -    
    -        
    -    
    -    
    -
    -{% endhighlight %}
    -

    Whereas in aws-us-west-2a it is:

    -
    {% highlight xml %}
    -
    -    2
    -    
    -        
    -    
    -    
    -        
    -    
    -
    -{% endhighlight %}
    -

    This can be used to modify any config by deployment target.

    -

    The deploy directives have a set of override rules:

    -
      -
    • A directive specifying more conditions will override one specifying fewer.
    • -
    • Directives are inherited in child elements.
    • -
    • - When multiple XML elements with the same name is specified - (e.g. when specifying search or docproc chains), - the id attribute or the idref attribute of the element - is used together with the element name when applying directives. -
    • -
    -

    - Some overrides are applied by default in some environments, - see environments. - Any override made explicitly for an environment will override the defaults for it. -

    - - -

    Specifying multiple targets

    -

    More than one tag, application, instance, region or environment can be specified in the attribute, separated by space.

    -

    - Note that tags by default only apply in production instances, - and are matched whenever the tags of the element and the tags of the instance intersect. - To match tags in other environments, - an explicit deploy:environment directive for that environment must also match. - Use tags if you have a complex instance structure which you want config to vary by. -

    -

    The namespace can be applied to any element. Example:

    -
    {% highlight xml %}
    -
    -    
    -        
    -            
    -                
    -                    Hello from application config
    -                    Hello from east colo!
    -                
    -            
    -        
    -    
    -
    -{% endhighlight %}
    -

    - Above, the container element is configured for the 3 environments only (it will not apply to dev) - - and in region aws-us-east-1c, the config is different. -

    - - - -

    Query profile variants

    -

    - Query profiles support different configuration settings - for different applications, instances, environments and regions through - query profile variants. - This allows you to set different query parameters for a query type depending on these deployment attributes. -

    -

    - To use this feature, create a regular query profile variant with any of - application, instance, environment and region as dimension names and - let your query profile vary by that. For example: -

    -
    {% highlight xml %}
    -
    -
    -    application, instance, environment, region
    -
    -    My default value
    -
    -    
    -    
    -        My beta value
    -    
    -
    -    
    -    
    -        My dev value
    -    
    -
    -    
    -    
    -        My main instance prod value
    -    
    -
    -
    -{% endhighlight %}
    -

    You can pick and combine these dimensions in any way you want with other dimensions sent as query parameters, e.g:

    -
    {% highlight xml %}
    -    device, application, instance, usecase
    -{% endhighlight %}
    diff --git a/en/operations/deployment-variants.mdx b/en/operations/deployment-variants.mdx new file mode 100644 index 0000000000..23f3c55bf8 --- /dev/null +++ b/en/operations/deployment-variants.mdx @@ -0,0 +1,150 @@ +--- +title: "Application, instance, region, cloud and environment variants" +sidebarTitle: "Deployment variants" +--- + +Sometimes it is useful to create configuration that varies depending on properties of the deployment, for example to set region specific endpoints of services used by [Searchers](/en/applications/searchers), use smaller clusters for a "beta" instance, or vary configuration when the same application package is shared across multiple applications. + +This is supported both for [services.xml](#services.xml-variants) and [query profiles](#query-profile-variants). + +## services.xml variants + +[services.xml](/en/reference/applications/services/services) files support different configuration settings for different *tags*, *applications*, *instances*, *environments*, *clouds* and *regions*. To use this, import the *deploy* namespace: + +```xml + +``` + +Deploy directives are used to specify with which tags, and in which application, instance, environment, cloud and/or [region](/en/operations/zones) an XML element should be included: + +```xml expandable + + 2 + + + + + + + + + + + + + + + + + + + +``` + +The example above configures different node counts/configurations depending on the deployment target. Deploying the application in the *dev* environment gives: + +```xml + + 2 + + + + + +``` + +Whereas in `aws-us-west-2a` it is: + +```xml + + 2 + + + + + + + +``` + +This can be used to modify any config by deployment target. + +The `deploy` directives have a set of override rules: + +- A directive specifying more conditions will override one specifying fewer. +- Directives are inherited in child elements. +- When multiple XML elements with the same name is specified (e.g. when specifying search or docproc chains), the *id* attribute or the *idref* attribute of the element is used together with the element name when applying directives. + +Some overrides are applied by default in some environments, see [environments](/en/operations/environments). Any override made explicitly for an environment will override the defaults for it. + +### Specifying multiple targets + +More than one tag, application, instance, region or environment can be specified in the attribute, separated by space. + +Note that `tags` by default only apply in production instances, and are matched whenever the tags of the element and the tags of the instance intersect. To match tags in other environments, an explicit `deploy:environment` directive for that environment must also match. Use tags if you have a complex instance structure which you want config to vary by. + +The namespace can be applied to any element. Example: + +```xml + + + + + + Hello from application config + Hello from east colo! + + + + + +``` + +Above, the `container` element is configured for the 3 environments only (it will not apply to `dev`) - and in region `aws-us-east-1c`, the config is different. + +## Query profile variants + +[Query profiles](/en/querying/query-profiles) support different configuration settings for different *applications*, *instances*, *environments* and *regions* through [query profile variants](/en/querying/query-profiles#query-profile-variants). This allows you to set different query parameters for a query type depending on these deployment attributes. + +To use this feature, create a regular query profile variant with any of `application`, `instance`, `environment` and `region` as dimension names and let your query profile vary by that. For example: + +```xml expandable + + + application, instance, environment, region + + My default value + + + + My beta value + + + + + My dev value + + + + + My main instance prod value + + + +``` + +You can pick and combine these dimensions in any way you want with other dimensions sent as query parameters, e.g: + +```xml +device, application, instance, usecase +``` \ No newline at end of file diff --git a/en/operations/enclave/archive.md b/en/operations/enclave/archive.md deleted file mode 100644 index e42b53177e..0000000000 --- a/en/operations/enclave/archive.md +++ /dev/null @@ -1,47 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: Log archive in Vespa Cloud Enclave -applies_to: cloud -redirect_from: -- /en/cloud/enclave/archive ---- - -{% include warning.html content="The structure of log archive buckets may change without notice" %} - -After Vespa Cloud Enclave is established in your cloud provider account using Terraform, -the module will have created a storage bucket per Vespa Cloud zone you configured in your enclave. -These storage buckets are used to archive logs from the machines that run Vespa inside your account. - -Since the buckets are in your own cloud account, you do not need to register an IAM role -or configure access through the Vespa Cloud Console — you can access the archive buckets -directly using your existing cloud credentials. - -![Enclave archive view](/assets/img/archive-aws-enclave.png) - -There will be one storage bucket per Vespa Cloud Zone that is configured in the enclave. -The name of the bucket will depend on the cloud provider you are setting up the enclave in. - -Files are synchronized to the archive bucket when the file is rotated by the logging system, -or when a virtual machine is deprovisioned from the application. The consequence of this is -that frequency of uploads will depend on the activity of the Vespa application. - -## Directory structure -The directory structure in the bucket is as follows: - -``` -////logs// -``` - -* `tenant` is the tenant ID. -* `application` is the application ID that generated the log. -* `instance` is the instance ID of the generated log, e.g. `default`. -* `host` is the name prefix of the host that generated the log, e.g. `e103a`. -* `logtype` is the type of log in the directory (see below). -* `logfile` is the specific file of the log. - -## Log types -There are three log types that are synced to this bucket. - -* `vespa`: [Vespa logs](../../reference/operations/log-files.html) -* `access`: [Access logs](../access-logging.html) -* `connection`: [Connection logs](../access-logging.html#connection-log) diff --git a/en/operations/enclave/archive.mdx b/en/operations/enclave/archive.mdx new file mode 100644 index 0000000000..70e23c008c --- /dev/null +++ b/en/operations/enclave/archive.mdx @@ -0,0 +1,45 @@ +--- +title: "Log archive in Vespa Cloud Enclave" +sidebarTitle: "Log archive" +--- + + +**Warning:** + +The structure of log archive buckets may change without notice + + +After Vespa Cloud Enclave is established in your cloud provider account using Terraform, the module will have created a storage bucket per Vespa Cloud zone you configured in your enclave. These storage buckets are used to archive logs from the machines that run Vespa inside your account. + +Since the buckets are in your own cloud account, you do not need to register an IAM role or configure access through the Vespa Cloud Console — you can access the archive buckets directly using your existing cloud credentials. + + +![Enclave archive view](/assets/img/archive-aws-enclave.png) + + +There will be one storage bucket per Vespa Cloud Zone that is configured in the enclave. The name of the bucket will depend on the cloud provider you are setting up the enclave in. + +Files are synchronized to the archive bucket when the file is rotated by the logging system, or when a virtual machine is deprovisioned from the application. The consequence of this is that frequency of uploads will depend on the activity of the Vespa application. + +## Directory structure + +The directory structure in the bucket is as follows: + +```bash +////logs// +``` + +- `tenant` is the tenant ID. +- `application` is the application ID that generated the log. +- `instance` is the instance ID of the generated log, e.g. `default`. +- `host` is the name prefix of the host that generated the log, e.g. `e103a`. +- `logtype` is the type of log in the directory (see below). +- `logfile` is the specific file of the log. + +## Log types + +There are three log types that are synced to this bucket. + +- `vespa`: [Vespa logs](/en/reference/operations/log-files) +- `access`: [Access logs](/en/operations/access-logging) +- `connection`: [Connection logs](/en/operations/access-logging#connection-log) \ No newline at end of file diff --git a/en/operations/enclave/aws-architecture.md b/en/operations/enclave/aws-architecture.md deleted file mode 100644 index 042fdd77af..0000000000 --- a/en/operations/enclave/aws-architecture.md +++ /dev/null @@ -1,77 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: Vespa Cloud Enclave AWS Architecture -applies_to: cloud -redirect_from: -- /en/cloud/enclave/aws-architecture ---- - -Each Vespa Cloud Enclave in the tenant AWS account corresponds to a Vespa Cloud -[zone](../zones.html). Inside the tenant AWS account one enclave is -contained within one single -[VPC](https://docs.aws.amazon.com/vpc/latest/userguide/what-is-amazon-vpc.html). - -![Enclave architecture](/assets/img/vespa-cloud-enclave-aws.png) - -#### EC2 Instances, Load Balancers, and S3 buckets - -Configuration Servers inside the Vespa Cloud zone makes the decision to create -or destroy EC2 instances ("Vespa Hosts" in diagram) based on the Vespa -applications that are deployed. The Configuration Servers also set up the -Network Load Balancers needed to communicate with the deployed Vespa -application. - -Each Vespa Host will periodically sync its logs to a S3 bucket ("Log Archive"). -This bucket is "local" to the enclave and provisioned by the Terraform module -inside the tenant's AWS account. - -#### Networking - -The enclave VPC is very network restricted. Vespa Hosts do not have public IPv4 -addresses and there is no -[NAT gateway](https://docs.aws.amazon.com/vpc/latest/userguide/vpc-nat-gateway.html) -available in the VPC. Vespa Hosts have public IPv6 addresses and are able to -make outbound connections. Inbound connections are not allowed. Outbound IPv6 -connections are used to bootstrap communication with the Configuration Servers, -and to report operational metrics back to Vespa Cloud. - -When a Vespa Host is booted it will set up an encrypted tunnel back to the -Configuration Servers. All communication between Configuration Servers and the -Vespa Hosts will be run over this tunnel after it is set up. - -### Security - -The Vespa Cloud operations team does _not_ have any direct access to the -resources that is part of the customer account. The only possible access is -through the management APIs needed to run Vespa itself. In case it is needed -for, e.g. incident debugging, direct access can only be granted to the Vespa -team by the tenant itself. For further details, see the documentation for the -[`ssh`-submodule](https://registry.terraform.io/modules/vespa-cloud/enclave/aws/latest/submodules/ssh). - -All communication between the enclave and the Vespa Cloud configuration servers -is encrypted, authenticated and authorized using -[mTLS](https://en.wikipedia.org/wiki/Mutual_authentication#mTLS) with identities -embedded in the certificate. mTLS communication is facilitated with the -[Athenz](https://www.athenz.io/) service. - -All data stored is encrypted at rest using -[KMS](https://docs.aws.amazon.com/kms/latest/developerguide/overview.html). All -keys are managed by the tenant in the tenant's AWS account. - -The resources provisioned in the tenant AWS account are either provisioned by the -Terraform module executed by the tenant, or by the orchestration services inside -a Vespa Cloud Zone. - -Resources are provisioned by the Vespa Cloud configuration servers, using the -[`provision_policy`](https://github.com/vespa-cloud/terraform-aws-enclave/blob/main/modules/provision/main.tf) -AWS IAM policy document defined in the Terraform module. - -The tenant that registered the AWS account is the only tenant that can deploy -applications targeting the enclave. - -The [terraform-aws-enclave](https://github.com/vespa-cloud/terraform-aws-enclave) -module is scanned with [Checkov](https://www.checkov.io/) to detect security -misconfigurations before release. - -For more general information about security in Vespa Cloud, see the -[whitepaper](../../security/whitepaper). diff --git a/en/operations/enclave/aws-architecture.mdx b/en/operations/enclave/aws-architecture.mdx new file mode 100644 index 0000000000..c373006425 --- /dev/null +++ b/en/operations/enclave/aws-architecture.mdx @@ -0,0 +1,38 @@ +--- +title: "Vespa Cloud Enclave AWS Architecture" +sidebarTitle: "AWS architecture" +--- + +Each Vespa Cloud Enclave in the tenant AWS account corresponds to a Vespa Cloud [zone](../zones.html). Inside the tenant AWS account one enclave is contained within one single [VPC](https://docs.aws.amazon.com/vpc/latest/userguide/what-is-amazon-vpc.html). + + +![Enclave architecture](/assets/img/vespa-cloud-enclave-aws.png) + + +#### EC2 Instances, Load Balancers, and S3 buckets + +Configuration Servers inside the Vespa Cloud zone makes the decision to create or destroy EC2 instances ("Vespa Hosts" in diagram) based on the Vespa applications that are deployed. The Configuration Servers also set up the Network Load Balancers needed to communicate with the deployed Vespa application. + +Each Vespa Host will periodically sync its logs to a S3 bucket ("Log Archive"). This bucket is "local" to the enclave and provisioned by the Terraform module inside the tenant's AWS account. + +#### Networking + +The enclave VPC is very network restricted. Vespa Hosts do not have public IPv4 addresses and there is no [NAT gateway](https://docs.aws.amazon.com/vpc/latest/userguide/vpc-nat-gateway.html) available in the VPC. Vespa Hosts have public IPv6 addresses and are able to make outbound connections. Inbound connections are not allowed. Outbound IPv6 connections are used to bootstrap communication with the Configuration Servers, and to report operational metrics back to Vespa Cloud. + +When a Vespa Host is booted it will set up an encrypted tunnel back to the Configuration Servers. All communication between Configuration Servers and the Vespa Hosts will be run over this tunnel after it is set up. + +### Security + +The Vespa Cloud operations team does *not* have any direct access to the resources that is part of the customer account. The only possible access is through the management APIs needed to run Vespa itself. In case it is needed for, e.g. incident debugging, direct access can only be granted to the Vespa team by the tenant itself. For further details, see the documentation for the [`ssh`\-submodule](https://registry.terraform.io/modules/vespa-cloud/enclave/aws/latest/submodules/ssh). + +All communication between the enclave and the Vespa Cloud configuration servers is encrypted, authenticated and authorized using [mTLS](https://en.wikipedia.org/wiki/Mutual_authentication#mTLS) with identities embedded in the certificate. mTLS communication is facilitated with the [Athenz](https://www.athenz.io/) service. + +All data stored is encrypted at rest using [KMS](https://docs.aws.amazon.com/kms/latest/developerguide/overview.html). All keys are managed by the tenant in the tenant's AWS account. + +The resources provisioned in the tenant AWS account are either provisioned by the Terraform module executed by the tenant, or by the orchestration services inside a Vespa Cloud Zone. + +Resources are provisioned by the Vespa Cloud configuration servers, using the [`provision_policy`](https://github.com/vespa-cloud/terraform-aws-enclave/blob/main/modules/provision/main.tf) AWS IAM policy document defined in the Terraform module. + +The tenant that registered the AWS account is the only tenant that can deploy applications targeting the enclave. + +For more general information about security in Vespa Cloud, see the [whitepaper](/en/security/whitepaper). \ No newline at end of file diff --git a/en/operations/enclave/aws-getting-started.md b/en/operations/enclave/aws-getting-started.md deleted file mode 100644 index 3d19f816df..0000000000 --- a/en/operations/enclave/aws-getting-started.md +++ /dev/null @@ -1,97 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: Getting started with Vespa Cloud Enclave in AWS -applies_to: cloud -redirect_from: -- /en/cloud/enclave/aws-getting-started -- /en/cloud/enclave/aws-deploy-an-application ---- - -Setting up Vespa Cloud Enclave requires: - -1. Registration at [Vespa Cloud](https://console.vespa-cloud.com), or use a pre-existing tenant. -2. Running a [Terraform](https://www.terraform.io/) configuration to provision AWS resources in the account. - Go through the [AWS tutorial](https://developer.hashicorp.com/terraform/tutorials/aws-get-started) as needed. -3. Registration of the AWS account ID in Vespa Cloud. -4. Deployment of a Vespa application. - -### 1. Vespa Cloud Tenant setup - -Register at [Vespa Cloud](https://console.vespa-cloud.com) or use an existing tenant. -Note that the tenant must be on a [paid plan](https://vespa.ai/pricing/). - -### 2. Configure AWS Account - -{% include note.html content='We recommend using a _dedicated_ account for your Vespa Cloud Enclave. -Vespa Cloud will manage resources in the Enclave VPCs created in the AWS resource provisioning step. Primarily EC2 instances, load balancers and service endpoints.' %} - -One account can host all your Vespa applications, there is no need for multiple tenants or accounts. - -The AWS account you intend to use for Vespa Cloud Enclave must be prepared for deploying Vespa applications using either *Terraform* or *Cloudformation*. - -#### Terraform -Use [Terraform](https://www.terraform.io/) to set up the necessary resources using the -[modules](https://registry.terraform.io/modules/vespa-cloud/enclave/aws/latest) published by the Vespa team. - -Modify the -[multi-region Terraform files](https://github.com/vespa-cloud/terraform-aws-enclave/blob/main/examples/multi-region/main.tf) -for your deployment. - -If you are unfamiliar with Terraform: It is a tool to manage resources and their -configuration in various cloud providers, like AWS and GCP. -Terraform has published an -[AWS](https://developer.hashicorp.com/terraform/tutorials/aws-get-started) -tutorial, and we strongly encourage enclave users to read and follow the -Terraform recommendations for -[CI/CD](https://developer.hashicorp.com/terraform/tutorials/automation/automate-terraform). - -The Terraform module we provide is regularly updated to add new required -resources or extra permissions for Vespa Cloud to automate the operations of -your applications. In order for your enclave applications to use the new -features you must re-apply your terraform templates with the latest release. -The [notification system](../notifications) -will let you know when a new release is available. - -### 3. Onboarding - -Once the AWS account is configured, contact [support@vespa.ai](mailto:support@vespa.ai) stating which tenant should be on-boarded to use Vespa Cloud Enclave. -Also include the [AWS account ID](https://docs.aws.amazon.com/accounts/latest/reference/manage-acct-identifiers.html#FindAccountId) -to associate with the tenant. - -{% include note.html content='Wait for confirmation from the Vespa team that onboarding is complete before deploying an application in the next step.' %} - -### 4. Deploy a Vespa application - -By default, all applications are deployed on resources in Vespa Cloud accounts. -To deploy in your enclave account, -update [deployment.xml](../../reference/applications/deployment.html) to reference the AWS account you onboarded: - -```xml - - - -``` - -Useful resources are [getting started](../../basics/deploy-an-application-java.html) -and [migrating to Vespa Cloud](../../learn/migrating-to-cloud) - -put _deployment.xml_ next to _services.xml_. - -## Next steps - -After a successful deployment to the [dev](../environments.html#dev) environment, -iterate on the configuration to implement your application on Vespa. -The _dev_ environment is ideal for this, with rapid deployment cycles. - -For production serving, deploy to the [prod](../environments.html#prod) environment - -follow the steps in [production deployment](../production-deployment.html). - -## Enclave teardown - -To tear down a Vespa Cloud Enclave system, do the steps above in reverse order: - - -1. [Undeploy the application(s)](../deleting-applications.html) -2. [Undeploy the Terraform changes](https://developer.hashicorp.com/terraform/cli/commands/destroy) - -It is important to undeploy the Vespa application(s) first. There will be a delay between when the application is deleted and all its supporting cloud resources are removed. -After running the Terraform, Vespa Cloud cannot manage the resources allocated, so you must clean up these yourself. diff --git a/en/operations/enclave/aws-getting-started.mdx b/en/operations/enclave/aws-getting-started.mdx new file mode 100644 index 0000000000..fa0a55778a --- /dev/null +++ b/en/operations/enclave/aws-getting-started.mdx @@ -0,0 +1,85 @@ +--- +title: "Getting started with Vespa Cloud Enclave in AWS" +sidebarTitle: "AWS getting started" +--- + +Setting up Vespa Cloud Enclave requires: + + + +Registration at [Vespa Cloud](https://console.vespa-cloud.com), or use a pre-existing tenant. + + +Running a [Terraform](https://www.terraform.io/) configuration to provision AWS resources in the account. Go through the [AWS tutorial](https://developer.hashicorp.com/terraform/tutorials/aws-get-started) as needed. + + +Registration of the AWS account ID in Vespa Cloud + + +Deployment of a Vespa application. + + + +### 1. Vespa Cloud Tenant setup + +Register at [Vespa Cloud](https://console.vespa-cloud.com) or use an existing tenant. Note that the tenant must be on a [paid plan](https://vespa.ai/pricing/). + +### 2. Configure AWS Account + + +**Note:** + +We recommend using a *dedicated* account for your Vespa Cloud Enclave. Vespa Cloud will manage resources in the Enclave VPCs created in the AWS resource provisioning step. Primarily EC2 instances, load balancers and service endpoints. + + +One account can host all your Vespa applications, there is no need for multiple tenants or accounts. + +The AWS account you intend to use for Vespa Cloud Enclave must be prepared for deploying Vespa applications using either *Terraform* or *Cloudformation*. + +#### Terraform + +Use [Terraform](https://www.terraform.io/) to set up the necessary resources using the [modules](https://registry.terraform.io/modules/vespa-cloud/enclave/aws/latest) published by the Vespa team. + +Modify the [multi-region Terraform files](https://github.com/vespa-cloud/terraform-aws-enclave/blob/main/examples/multi-region/main.tf) for your deployment. + +If you are unfamiliar with Terraform: It is a tool to manage resources and their configuration in various cloud providers, like AWS and GCP. Terraform has published an [AWS](https://developer.hashicorp.com/terraform/tutorials/aws-get-started) tutorial, and we strongly encourage enclave users to read and follow the Terraform recommendations for [CI/CD](https://developer.hashicorp.com/terraform/tutorials/automation/automate-terraform). + +The Terraform module we provide is regularly updated to add new required resources or extra permissions for Vespa Cloud to automate the operations of your applications. In order for your enclave applications to use the new features you must re-apply your terraform templates with the latest release. The [notification system](/en/operations/notifications) will let you know when a new release is available. + +### 3. Onboarding + +Once the AWS account is configured, contact [support@vespa.ai](mailto:support@vespa.ai) stating which tenant should be on-boarded to use Vespa Cloud Enclave. Also include the [AWS account ID](https://docs.aws.amazon.com/accounts/latest/reference/manage-acct-identifiers.html#FindAccountId) to associate with the tenant. + + +**Note:** + +Wait for confirmation from the Vespa team that onboarding is complete before deploying an application in the next step. + + + +### 4. Deploy a Vespa application + +By default, all applications are deployed on resources in Vespa Cloud accounts. To deploy in your enclave account, update [deployment.xml](/en/reference/applications/deployment) to reference the AWS account you onboarded: + +```xml + + + +``` + +Useful resources are [getting started](/en/basics/deploy-an-application-java) and [migrating to Vespa Cloud](/en/learn/migrating-to-cloud) - put *deployment.xml* next to *services.xml*. + +## Next steps + +After a successful deployment to the [dev](/en/operations/environments#dev) environment, iterate on the configuration to implement your application on Vespa. The *dev* environment is ideal for this, with rapid deployment cycles. + +For production serving, deploy to the [prod](/en/operations/environments#prod) environment - follow the steps in [production deployment](/en/reference/applications/deployment). + +## Enclave teardown + +To tear down a Vespa Cloud Enclave system, do the steps above in reverse order: + +1. [Undeploy the application(s)](/en/operations/deleting-applications) +2. Undeploy the Terraform changes + +It is important to undeploy the Vespa application(s) first. After running the Terraform, Vespa Cloud cannot manage the resources allocated, so you must clean up these yourself. \ No newline at end of file diff --git a/en/operations/enclave/azure-architecture.md b/en/operations/enclave/azure-architecture.md deleted file mode 100644 index 8325673019..0000000000 --- a/en/operations/enclave/azure-architecture.md +++ /dev/null @@ -1,82 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: Architecture for Vespa Cloud Enclave in Azure -applies_to: cloud -redirect_from: -- /en/cloud/enclave/azure-architecture ---- - -### Architecture - -With Vespa Cloud Enclave, all Azure resources associated with your Vespa Cloud applications -are in your enclave Azure subscription, as opposed to a shared Vespa Cloud subscription. - -Each Vespa Cloud [zone](../zones.html) has an associated zone resource group (RG) -in the enclave subscription, that contains all the resources for that zone. For instance, -it has one Virtual Network (VNet aka [VPC](https://cloud.google.com/vpc/)). - -![Enclave architecture](/assets/img/vespa-cloud-enclave-azure.png) - -#### Virtual Machines, Load Balancers, and Blob Storage - -Configuration Servers inside the Vespa Cloud subscription make the decision to create -or destroy virtual machines ("Vespa Hosts" in diagram) based on the Vespa -applications that are deployed. The Configuration Servers also set up the -Container Load Balancers needed to communicate with the deployed Vespa -application. - -Each Vespa Host will periodically sync its logs to a Blob Storage container ("Log -Archive") in a Storage Account in the zone RG. This storage account is "local" -to the enclave and provisioned by the Terraform module inside your Azure subscription. - -#### Networking - -The Zone Virtual Network (VNet aka VPC) is very network restricted. The Vespa Hosts do not -have a public IPv4 address. But your application can connect to external IPv4 services using a -[NAT gateway](https://learn.microsoft.com/en-us/azure/nat-gateway/nat-overview). -Vespa Hosts have public IPv6 addresses and are able to make outbound -connections. Inbound connections are not allowed. Outbound IPv6 connections are -used to bootstrap communication with the Configuration Servers, and to report -operational metrics back to Vespa Cloud. - -When a Vespa Host is booted, it will set up an encrypted tunnel back to the -Configuration Servers. All communication between Configuration Servers and the -Vespa Hosts will be run over this tunnel after it is set up. - -### Security - -The Vespa Cloud operations team does _not_ have any direct access to the -resources in your subscription. The only possible access is -through the management APIs needed to run Vespa itself. In case it is needed -for, e.g. incident debugging, direct access can only be granted to the Vespa -team by you. Enable direct access by setting the -`enable_ssh` input to true in the enclave module. For further details, see the -documentation for the -[enclave module inputs](https://registry.terraform.io/modules/vespa-cloud/enclave/azure/latest/?tab=inputs). - -All communication between the enclave and the Vespa Cloud Configuration servers -is encrypted, authenticated, and authorized using -[mTLS](https://en.wikipedia.org/wiki/Mutual_authentication#mTLS) with identities -embedded in the certificate. mTLS communication is facilitated with the -[Athenz](https://www.athenz.io/) service. - -All data stored is encrypted at rest using -[Encryption At Host](https://learn.microsoft.com/en-us/azure/virtual-machines/disk-encryption-overview). All -keys are managed automatically by the Azure platform. - -The resources provisioned in your Azure subscription are either provisioned by the -Vespa Cloud Enclave Terraform module you apply, or by the orchestration services inside -a Vespa Cloud zone. - -Resources are provisioned by the Vespa Cloud Configuration servers, using the -[`id-provisioner`](https://github.com/vespa-cloud/terraform-azure-enclave/blob/main/provisioner.tf) -user-assigned managed identity defined in the Terraform module. - -Only your Vespa tenant (that registered this Azure subscription) can deploy applications targeting your enclave. - -The [terraform-azure-enclave](https://github.com/vespa-cloud/terraform-azure-enclave) -module is scanned with [Checkov](https://www.checkov.io/) to detect security -misconfigurations before release. - -For more general information about security in Vespa Cloud, see the -[whitepaper](../../security/whitepaper). diff --git a/en/operations/enclave/azure-architecture.mdx b/en/operations/enclave/azure-architecture.mdx new file mode 100644 index 0000000000..9e24f07649 --- /dev/null +++ b/en/operations/enclave/azure-architecture.mdx @@ -0,0 +1,42 @@ +--- +title: "Architecture for Vespa Cloud Enclave in Azure" +sidebarTitle: "Azure architecture" +--- + +### Architecture + +With Vespa Cloud Enclave, all Azure resources associated with your Vespa Cloud applications are in your enclave Azure subscription, as opposed to a shared Vespa Cloud subscription. + +Each Vespa Cloud [zone](/en/operations/zones) has an associated zone resource group (RG) in the enclave subscription, that contains all the resources for that zone. For instance, it has one Virtual Network (VNet aka [VPC](https://cloud.google.com/vpc/)). + + +![Enclave architecture](/assets/img/vespa-cloud-enclave-azure.png) + + +#### Virtual Machines, Load Balancers, and Blob Storage + +Configuration Servers inside the Vespa Cloud subscription make the decision to create or destroy virtual machines ("Vespa Hosts" in diagram) based on the Vespa applications that are deployed. The Configuration Servers also set up the Container Load Balancers needed to communicate with the deployed Vespa application. + +Each Vespa Host will periodically sync its logs to a Blob Storage container ("Log Archive") in a Storage Account in the zone RG. This storage account is "local" to the enclave and provisioned by the Terraform module inside your Azure subscription. + +#### Networking + +The Zone Virtual Network (VNet aka VPC) is very network restricted. The Vespa Hosts do not have a public IPv4 address. But your application can connect to external IPv4 services using a [NAT gateway](https://learn.microsoft.com/en-us/azure/nat-gateway/nat-overview). Vespa Hosts have public IPv6 addresses and are able to make outbound connections. Inbound connections are not allowed. Outbound IPv6 connections are used to bootstrap communication with the Configuration Servers, and to report operational metrics back to Vespa Cloud. + +When a Vespa Host is booted, it will set up an encrypted tunnel back to the Configuration Servers. All communication between Configuration Servers and the Vespa Hosts will be run over this tunnel after it is set up. + +### Security + +The Vespa Cloud operations team does *not* have any direct access to the resources in your subscription. The only possible access is through the management APIs needed to run Vespa itself. In case it is needed for, e.g. incident debugging, direct access can only be granted to the Vespa team by you. Enable direct access by setting the `enable_ssh` input to true in the enclave module. For further details, see the documentation for the [enclave module inputs](https://registry.terraform.io/modules/vespa-cloud/enclave/azure/latest/?tab=inputs). + +All communication between the enclave and the Vespa Cloud Configuration servers is encrypted, authenticated, and authorized using [mTLS](https://en.wikipedia.org/wiki/Mutual_authentication#mTLS) with identities embedded in the certificate. mTLS communication is facilitated with the [Athenz](https://www.athenz.io/) service. + +All data stored is encrypted at rest using [Encryption At Host](https://learn.microsoft.com/en-us/azure/virtual-machines/disk-encryption-overview). All keys are managed automatically by the Azure platform. + +The resources provisioned in your Azure subscription are either provisioned by the Vespa Cloud Enclave Terraform module you apply, or by the orchestration services inside a Vespa Cloud zone. + +Resources are provisioned by the Vespa Cloud Configuration servers, using the [`id-provisioner`](https://github.com/vespa-cloud/terraform-azure-enclave/blob/main/provisioner.tf) user-assigned managed identity defined in the Terraform module. + +Only your Vespa tenant (that registered this Azure subscription) can deploy applications targeting your enclave. + +For more general information about security in Vespa Cloud, see the [whitepaper](/en/security/whitepaper). \ No newline at end of file diff --git a/en/operations/enclave/azure-getting-started.md b/en/operations/enclave/azure-getting-started.md deleted file mode 100644 index ead1d3bc61..0000000000 --- a/en/operations/enclave/azure-getting-started.md +++ /dev/null @@ -1,101 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: Getting started with Vespa Cloud Enclave in Azure -category: cloud -redirect_from: -- /en/cloud/enclave/azure-getting-started -- /en/cloud/enclave/azure-deploy-an-application ---- - -Setting up Vespa Cloud Enclave requires: - -1. Registration at [Vespa Cloud](https://console.vespa-cloud.com), or use a pre-existing Vespa tenant. -2. Running a [Terraform](https://www.terraform.io/) configuration to provision necessary Azure resources in the subscription. -3. Registration of the Azure subscription in Vespa Cloud. -4. Deployment of a Vespa application. - - -### 1. Vespa Cloud Tenant setup - -Register at [Vespa Cloud](https://console.vespa-cloud.com) or use an existing Vespa tenant. -Note that the tenant must be on a [paid plan](https://vespa.ai/pricing/). - - -### 2. Configure Azure subscription - -Choose an Azure subscription to use for Vespa Cloud Enclave. - -{% include note.html content='We recommend using a _dedicated_ subscription for your Vespa Cloud Enclave. -Resources in this subscription will be fully managed by Vespa Cloud.' %} - -One subscription can host all your Vespa applications, there is no need for multiple Vespa tenants or Azure subscriptions. - -The subscription must be prepared for deploying Vespa applications. -Use [Terraform](https://www.terraform.io/) to set up the necessary resources using the -[modules](https://registry.terraform.io/modules/vespa-cloud/enclave/azure/latest) -published by the Vespa team. - -Feel free to use the -[example](https://github.com/vespa-cloud/terraform-azure-enclave/blob/main/examples/basic/main.tf) - -to get started. - -If you are unfamiliar with Terraform: It is a tool to manage resources and their -configuration in various cloud providers, like AWS, Azure, and GCP. -Terraform has published a -[Get Started - Azure](https://developer.hashicorp.com/terraform/tutorials/azure-get-started) -tutorial, and we strongly encourage enclave users to read and follow the -Terraform recommendations for -[CI/CD](https://developer.hashicorp.com/terraform/tutorials/automation/automate-terraform). - -The Terraform module we provide is regularly updated to add new required -resources or extra permissions for Vespa Cloud to automate the operations of -your applications. In order for your enclave applications to use the new -features you must re-apply your terraform templates with the latest release. -The [notification system](../notifications.html) -will let you know when a new release is available. - -### 3. Onboarding - -Contact [support@vespa.ai](mailto:support@vespa.ai) and provide the `enclave_config` output after -applying the Terraform, see -[Outputs](https://github.com/vespa-cloud/terraform-azure-enclave?tab=readme-ov-file#outputs). -The `enclave_config` includes which Vespa tenant should be on-boarded to use Vespa Cloud Enclave. -And the Azure tenant ID, the subscription ID, and a client ID of an Athenz identity the Terraform created. - -### 4. Deploy a Vespa application - -By default, all applications are deployed on resources in Vespa Cloud accounts. -To deploy in your Azure enclave subscription instead, -update [deployment.xml](../../reference/applications/deployment.html) to reference the subscription ID from step 2: - -```xml - - - -``` - -Useful resources are [getting started](../../basics/deploy-an-application.html) -and [migrating to Vespa Cloud](../../learn/migrating-to-cloud) - -put _deployment.xml_ next to _services.xml_. - - -## Next steps - -After a successful deployment to the [dev](../environments.html#dev) environment, -iterate on the configuration to implement your application on Vespa. -The _dev_ environment is ideal for this, with rapid deployment cycles. - -For production serving, deploy to the [prod](../environments.html#prod) environment - -follow the steps in [production deployment](../production-deployment.html). - - -## Enclave teardown - -To tear down a Vespa Cloud Enclave system, do the steps above in reverse order: - -1. [Undeploy the application(s)](../deleting-applications.html) -2. [Undeploy the Terraform changes](https://developer.hashicorp.com/terraform/cli/commands/destroy) - -It is important to undeploy the Vespa application(s) first. There will be a delay between when the application is deleted and all its supporting cloud resources are removed. -After running the Terraform, Vespa Cloud cannot manage the resources allocated, so you must clean up these yourself. diff --git a/en/operations/enclave/azure-getting-started.mdx b/en/operations/enclave/azure-getting-started.mdx new file mode 100644 index 0000000000..751856b38b --- /dev/null +++ b/en/operations/enclave/azure-getting-started.mdx @@ -0,0 +1,76 @@ +--- +title: "Getting started with Vespa Cloud Enclave in Azure" +sidebarTitle: "Azure getting started" +--- + +Setting up Vespa Cloud Enclave requires: + + + +Registration at [Vespa Cloud](https://console.vespa-cloud.com), or use a pre-existing Vespa tenant. + + +Running a [Terraform](https://www.terraform.io/) configuration to provision necessary Azure resources in the subscription. + + +Registration of the Azure subscription in Vespa Cloud. + + +Deployment of a Vespa application. + + + +### 1. Vespa Cloud Tenant setup + +Register at [Vespa Cloud](https://console.vespa-cloud.com) or use an existing Vespa tenant. Note that the tenant must be on a [paid plan](https://vespa.ai/pricing/). + +### 2. Configure Azure subscription + +Choose an Azure subscription to use for Vespa Cloud Enclave. + + +**Note:** + +We recommend using a *dedicated* subscription for your Vespa Cloud Enclave. Resources in this subscription will be fully managed by Vespa Cloud. + + +One subscription can host all your Vespa applications, there is no need for multiple Vespa tenants or Azure subscriptions. + +The subscription must be prepared for deploying Vespa applications. Use [Terraform](https://www.terraform.io/) to set up the necessary resources using the [modules](https://registry.terraform.io/modules/vespa-cloud/enclave/azure/latest) published by the Vespa team. + +Feel free to use the [example](https://github.com/vespa-cloud/terraform-azure-enclave/blob/main/examples/basic/main.tf) to get started. + +If you are unfamiliar with Terraform: It is a tool to manage resources and their configuration in various cloud providers, like AWS, Azure, and GCP. Terraform has published a [Get Started - Azure](https://developer.hashicorp.com/terraform/tutorials/azure-get-started) tutorial, and we strongly encourage enclave users to read and follow the Terraform recommendations for [CI/CD](https://developer.hashicorp.com/terraform/tutorials/automation/automate-terraform). + +The Terraform module we provide is regularly updated to add new required resources or extra permissions for Vespa Cloud to automate the operations of your applications. In order for your enclave applications to use the new features you must re-apply your terraform templates with the latest release. The [notification system](/en/operations/notifications) will let you know when a new release is available. + +### 3. Onboarding + +Contact [support@vespa.ai](mailto:support@vespa.ai) and provide the `enclave_config` output after applying the Terraform, see [Outputs](https://github.com/vespa-cloud/terraform-azure-enclave?tab=readme-ov-file#outputs). The `enclave_config` includes which Vespa tenant should be on-boarded to use Vespa Cloud Enclave. And the Azure tenant ID, the subscription ID, and a client ID of an Athenz identity the Terraform created. + +### 4. Deploy a Vespa application + +By default, all applications are deployed on resources in Vespa Cloud accounts. To deploy in your Azure enclave subscription instead, update [deployment.xml](/en/reference/applications/deployment) to reference the subscription ID from step 2: + +```bash + + + +``` + +Useful resources are [getting started](/en/basics/deploy-an-application) and [migrating to Vespa Cloud](/en/learn/migrating-to-cloud) - put *deployment.xml* next to *services.xml*. + +## Next steps + +After a successful deployment to the [dev](/en/operations/environments#dev) environment, iterate on the configuration to implement your application on Vespa. The *dev* environment is ideal for this, with rapid deployment cycles. + +For production serving, deploy to the [prod](/en/operations/environments#prod) environment - follow the steps in [production deployment](/en/reference/applications/deployment). + +## Enclave teardown + +To tear down a Vespa Cloud Enclave system, do the steps above in reverse order: + +1. [Undeploy the application(s)](/en/operations/deleting-applications) +2. Undeploy the Terraform changes + +It is important to undeploy the Vespa application(s) first. After running the Terraform, Vespa Cloud cannot manage the resources allocated, so you must clean up these yourself. \ No newline at end of file diff --git a/en/operations/enclave/enclave.md b/en/operations/enclave/enclave.md deleted file mode 100644 index 3b03fbd80b..0000000000 --- a/en/operations/enclave/enclave.md +++ /dev/null @@ -1,84 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: Vespa Cloud Enclave -applies_to: cloud -redirect_from: -- /en/cloud/enclave/enclave ---- - -![enclave architecture](/assets/img/enclave-architecture.png) - -Vespa Cloud Enclave allows Vespa Cloud applications to run inside the tenant's -own cloud accounts while everything is still fully managed by Vespa Cloud's -automation, giving the tenant full access to Vespa Cloud features inside their -own cloud account. This allows tenant data to always remain within the bounds of -services controlled by the tenant, and also to build closer integrations with -Vespa applications inside the cloud services. - -Vespa Cloud Enclave is available in AWS, Azure, and GCP. - -**Note:** As the Vespa Cloud Enclave resources run in _your_ account, this incurs -resource costs from your cloud provider in _addition_ to the Vespa Cloud costs. - -## AWS - -* [Getting started](aws-getting-started.html) -* [Architecture and security](aws-architecture) - -## Azure - -* [Getting started](azure-getting-started.html) -* [Architecture and security](azure-architecture) - -## GCP - -* [Getting started](gcp-getting-started.html) -* [Architecture and security](gcp-architecture) - -## Guides - -* [Log archive](archive) -* [Operations and Support](operations) - -## FAQ - -**Which kind of permission is needed for the Vespa control plane to access my AWS accounts / Azure subscriptions / GCP projects?** - -The permissions required are coded into the Terraform modules found at: - -* [terraform-aws](https://github.com/vespa-cloud/terraform-aws-enclave/tree/main) -* [terraform-azure](https://github.com/vespa-cloud/terraform-azure-enclave/tree/main) -* [terraform-google](https://github.com/vespa-cloud/terraform-google-enclave/tree/main) - -Navigate to the _modules_ directory for details. - - -**How can I configure agents/daemons on Vespa hosts securely?** - -Use terraform to grant Vespa hosts access to necessary secrets, and create an RPM -that retrieves them and configures your application. See [enclave-examples](https://github.com/vespa-cloud/enclave-examples/tree/main/systemd-secrets) -for a complete example. - - -**Deployment failure: Could not provision ...** - -This happens if you deploy to new zones _before_ running the Terraform/CloudFormation templates: - -``` -Deployment failed: Invalid application: In container cluster 'mycluster': Could not provision load balancer mytenant:myapp:myinstance:mycluster: Expected to find exactly 1 resource, but got 0 for subnet with service 'tenantelb' -``` - - -**Do we need to take any actions when AWS sends us Amazon EC2 Instance Retirement, Amazon EC2 Instance Availability Issue, or Amazon EC2 Maintenance notifications,?** - -Vespa Cloud will take proactive actions on maintenance operations and replace instances that are scheduled for maintenance tasks ahead of time to reduce any impact the maintenance may incur. - -All EC2 instance failures are detected by our control plane, and the problematic instances are automatically replaced. The system will, as part of the replacement process, also ensure that the document distribution is kept in line with your application configuration. - - -**Does Vespa Cloud Enclave support VPC peering?** - -VPC peering is not supported; [AWS PrivateLink](../private-endpoints.html#aws-private-link) and -[Google Private Service Connect](../private-endpoints.html#gcp-private-service-connect) are good alternatives, -so you can access the endpoints without going over public internet. -[Read more](../production-deployment.html#accessing-a-public-cloud-application-from-another-vpc-on-another-account). diff --git a/en/operations/enclave/enclave.mdx b/en/operations/enclave/enclave.mdx new file mode 100644 index 0000000000..063e0e65fe --- /dev/null +++ b/en/operations/enclave/enclave.mdx @@ -0,0 +1,84 @@ +--- +title: "Vespa Cloud Enclave" +sidebarTitle: "Enclave" +--- + + +![enclave architecture](/assets/img/enclave-architecture.png) + + +Vespa Cloud Enclave allows Vespa Cloud applications to run inside the tenant's own cloud accounts while everything is still fully managed by Vespa Cloud's automation, giving the tenant full access to Vespa Cloud features inside their own cloud account. This allows tenant data to always remain within the bounds of services controlled by the tenant, and also to build closer integrations with Vespa applications inside the cloud services. + +Vespa Cloud Enclave is available in AWS, Azure, and GCP. + + +**Note:** + +As the Vespa Cloud Enclave resources run in *your* account, this incurs resource costs from your cloud provider in *addition* to the Vespa Cloud costs. + + + +## AWS + + + + + + +## Azure + + + + + + +## GCP + + + + + + +## Guides + + + + + + +## FAQ + + + + +The permissions required are coded into the Terraform modules found at: + + + + + + +Navigate to the *modules* directory for details. + + + +Use terraform to grant Vespa hosts access to necessary secrets, and create an RPM that retrieves them and configures your application. See [enclave-examples](https://github.com/vespa-cloud/enclave-examples/tree/main/systemd-secrets) for a complete example. + + + +This happens if you deploy to new zones *before* running the Terraform/CloudFormation templates: + +```bash +Deployment failed: Invalid application: In container cluster 'mycluster': Could not provision load balancer mytenant:myapp:myinstance:mycluster: Expected to find exactly 1 resource, but got 0 for subnet with service 'tenantelb' +``` + + + +Vespa Cloud will take proactive actions on maintenance operations and replace instances that are scheduled for maintenance tasks ahead of time to reduce any impact the maintenance may incur. + +All EC2 instance failures are detected by our control plane, and the problematic instances are automatically replaced. The system will, as part of the replacement process, also ensure that the document distribution is kept in line with your application configuration. + + +VPC peering is not supported; [AWS PrivateLink](/en/operations/private-endpoints#aws-private-link) and [Google Private Service Connect](/en/operations/private-endpoints#gcp-private-service-connect) are good alternatives, so you can access the endpoints without going over public internet. [Read more](/en/reference/applications/deployment#accessing-a-public-cloud-application-from-another-vpc-on-another-account). + + \ No newline at end of file diff --git a/en/operations/enclave/gcp-architecture.md b/en/operations/enclave/gcp-architecture.md deleted file mode 100644 index 8851273c64..0000000000 --- a/en/operations/enclave/gcp-architecture.md +++ /dev/null @@ -1,80 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: Architecture for Vespa Cloud Enclave in GCP -applies_to: cloud -redirect_from: -- /en/cloud/enclave/gcp-architecture ---- - -### Architecture - -Each Vespa Cloud Enclave in the tenant GCP project corresponds to a Vespa Cloud -[zone](../zones.html). Inside the tenant GCP project one enclave is -contained within one single [VPC](https://cloud.google.com/vpc/). - -![Enclave architecture](/assets/img/vespa-cloud-enclave-gcp.png) - -#### Compute Instances, Load Balancers, and Cloud Storage buckets - -Configuration Servers inside the Vespa Cloud zone makes the decision to create -or destroy compute instances ("Vespa Hosts" in diagram) based on the Vespa -applications that are deployed. The Configuration Servers also set up the -Network Load Balancers needed to communicate with the deployed Vespa -application. - -Each Vespa Host will periodically sync its logs to a Cloud Storage bucket ("Log -Archive"). This bucket is "local" to the enclave and provisioned by the -Terraform module inside the tenant's GCP project. - -#### Networking - -The enclave VPC is very network restricted. Vespa Hosts do not have public IPv4 -addresses and there is no -[NAT gateway](https://cloud.google.com/nat/docs/overview) available in the VPC. -Vespa Hosts have public IPv6 addresses and are able to make outbound -connections. Inbound connections are not allowed. Outbound IPv6 connections are -used to bootstrap communication with the Configuration Servers, and to report -operational metrics back to Vespa Cloud. - -When a Vespa Host is booted it will set up an encrypted tunnel back to the -Configuration Servers. All communication between Configuration Servers and the -Vespa Hosts will be run over this tunnel after it is set up. - -### Security - -The Vespa Cloud operations team does _not_ have any direct access to the -resources that is part of the customer account. The only possible access is -through the management APIs needed to run Vespa itself. In case it is needed -for, e.g. incident debugging, direct access can only be granted to the Vespa -team by the tenant itself. Enabling direct access is done by setting the -`enable_ssh` input to true in the enclave module. For further details, see the -documentation for the -[enclave module inputs](https://registry.terraform.io/modules/vespa-cloud/enclave/google/latest/?tab=inputs). - -All communication between the enclave and the Vespa Cloud configuration servers -is encrypted, authenticated and authorized using -[mTLS](https://en.wikipedia.org/wiki/Mutual_authentication#mTLS) with identities -embedded in the certificate. mTLS communication is facilitated with the -[Athenz](https://www.athenz.io/) service. - -All data stored is encrypted at rest using -[Cloud Key Management](https://cloud.google.com/security-key-management). All -keys are managed by the tenant in the tenant's GCP project. - -The resources provisioned in the tenant GCP project are either provisioned by the -Terraform module executed by the tenant, or by the orchestration services inside -a Vespa Cloud zone. - -Resources are provisioned by the Vespa Cloud configuration servers, using the -[`vespa_cloud_provisioner_role`](https://github.com/vespa-cloud/terraform-google-enclave/blob/main/main.tf) -IAM role defined in the Terraform module. - -The tenant that registered the GCP project is the only tenant that can deploy -applications targeting the enclave. - -The [terraform-google-enclave](https://github.com/vespa-cloud/terraform-google-enclave) -module is scanned with [Checkov](https://www.checkov.io/) to detect security -misconfigurations before release. - -For more general information about security in Vespa Cloud, see the -[whitepaper](../../security/whitepaper). diff --git a/en/operations/enclave/gcp-architecture.mdx b/en/operations/enclave/gcp-architecture.mdx new file mode 100644 index 0000000000..b059028a19 --- /dev/null +++ b/en/operations/enclave/gcp-architecture.mdx @@ -0,0 +1,40 @@ +--- +title: "Architecture for Vespa Cloud Enclave in GCP" +sidebarTitle: "GCP architecture" +--- + +### Architecture + +Each Vespa Cloud Enclave in the tenant GCP project corresponds to a Vespa Cloud [zone](/en/operations/zones). Inside the tenant GCP project one enclave is contained within one single [VPC](https://cloud.google.com/vpc/). + + +![Enclave architecture](/assets/img/vespa-cloud-enclave-gcp.png) + + +#### Compute Instances, Load Balancers, and Cloud Storage buckets + +Configuration Servers inside the Vespa Cloud zone makes the decision to create or destroy compute instances ("Vespa Hosts" in diagram) based on the Vespa applications that are deployed. The Configuration Servers also set up the Network Load Balancers needed to communicate with the deployed Vespa application. + +Each Vespa Host will periodically sync its logs to a Cloud Storage bucket ("Log Archive"). This bucket is "local" to the enclave and provisioned by the Terraform module inside the tenant's GCP project. + +#### Networking + +The enclave VPC is very network restricted. Vespa Hosts do not have public IPv4 addresses and there is no [NAT gateway](https://cloud.google.com/nat/docs/overview) available in the VPC. Vespa Hosts have public IPv6 addresses and are able to make outbound connections. Inbound connections are not allowed. Outbound IPv6 connections are used to bootstrap communication with the Configuration Servers, and to report operational metrics back to Vespa Cloud. + +When a Vespa Host is booted it will set up an encrypted tunnel back to the Configuration Servers. All communication between Configuration Servers and the Vespa Hosts will be run over this tunnel after it is set up. + +### Security + +The Vespa Cloud operations team does *not* have any direct access to the resources that is part of the customer account. The only possible access is through the management APIs needed to run Vespa itself. In case it is needed for, e.g. incident debugging, direct access can only be granted to the Vespa team by the tenant itself. Enabling direct access is done by setting the `enable_ssh` input to true in the enclave module. For further details, see the documentation for the [enclave module inputs](https://registry.terraform.io/modules/vespa-cloud/enclave/google/latest/?tab=inputs). + +All communication between the enclave and the Vespa Cloud configuration servers is encrypted, authenticated and authorized using [mTLS](https://en.wikipedia.org/wiki/Mutual_authentication#mTLS) with identities embedded in the certificate. mTLS communication is facilitated with the [Athenz](https://www.athenz.io/) service. + +All data stored is encrypted at rest using [Cloud Key Management](https://cloud.google.com/security-key-management). All keys are managed by the tenant in the tenant's GCP project. + +The resources provisioned in the tenant GCP project are either provisioned by the Terraform module executed by the tenant, or by the orchestration services inside a Vespa Cloud zone. + +Resources are provisioned by the Vespa Cloud configuration servers, using the [`vespa_cloud_provisioner_role`](https://github.com/vespa-cloud/terraform-google-enclave/blob/main/main.tf) IAM role defined in the Terraform module. + +The tenant that registered the GCP project is the only tenant that can deploy applications targeting the enclave. + +For more general information about security in Vespa Cloud, see the [whitepaper](/en/security/whitepaper). \ No newline at end of file diff --git a/en/operations/enclave/gcp-getting-started.md b/en/operations/enclave/gcp-getting-started.md deleted file mode 100644 index 03d0148960..0000000000 --- a/en/operations/enclave/gcp-getting-started.md +++ /dev/null @@ -1,108 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: Getting started with Vespa Cloud Enclave in GCP -applies_to: cloud -redirect_from: -- /en/cloud/enclave/gcp-getting-started -- /en/cloud/enclave/gcp-deploy-an-application ---- - -Setting up Vespa Cloud Enclave requires: - -1. Registration at [Vespa Cloud](https://console.vespa-cloud.com), or use a pre-existing tenant. -2. Running a [Terraform](https://www.terraform.io/) configuration to provision necessary GCP resources in the project. -3. Registration of the GCP project in Vespa Cloud. -4. Deployment of a Vespa application. - - -### 1. Vespa Cloud Tenant setup - -Register at [Vespa Cloud](https://console.vespa-cloud.com) or use an existing tenant. -Note that the tenant must be on a [paid plan](https://vespa.ai/pricing/). - - -### 2. Configure GCP Project - -{% include note.html content='We recommend using a _dedicated_ project for your Vespa Cloud Enclave. -Resources in this project will be fully managed by Vespa Cloud.' %} - -One project can host all your Vespa applications, there is no need for multiple tenants or projects. - -The project you intend to use for Vespa Cloud Enclave must be prepared for deploying Vespa applications. -Use [Terraform](https://www.terraform.io/) to set up the necessary resources using the -[modules](https://registry.terraform.io/modules/vespa-cloud/enclave/google/latest) -published by the Vespa team. - -Modify the -[multi-region example](https://github.com/vespa-cloud/terraform-google-enclave/blob/main/examples/multi-region/main.tf) -for your deployment. - -If you are unfamiliar with Terraform: It is a tool to manage resources and their -configuration in various cloud providers, like AWS and GCP. -Terraform has published a -[GCP](https://developer.hashicorp.com/terraform/tutorials/gcp-get-started) -tutorial, and we strongly encourage enclave users to read and follow the -Terraform recommendations for -[CI/CD](https://developer.hashicorp.com/terraform/tutorials/automation/automate-terraform). - -The Terraform module we provide is regularly updated to add new required -resources or extra permissions for Vespa Cloud to automate the operations of -your applications. In order for your enclave applications to use the new -features you must re-apply your terraform templates with the latest release. -The [notification system](../notifications.html) -will let you know when a new release is available. - - -### 3. Onboarding - -Once the GCP project is configured, contact [support@vespa.ai](mailto:support@vespa.ai) stating which tenant should be on-boarded to use Vespa Cloud Enclave. -Also include the [GCP Project ID](https://cloud.google.com/resource-manager/docs/creating-managing-projects#identifying_projects) -to associate with the tenant. - -{% include note.html content='Wait for confirmation from the Vespa team that onboarding is complete before deploying an application in the next step.' %} - - -### 4. Deploy a Vespa application - -By default, all applications are deployed on resources in Vespa Cloud accounts. -To deploy in your enclave account, -update [deployment.xml](../../reference/applications/deployment.html) to reference the GCP project you onboarded: - -```xml - - - -``` - -Useful resources are [getting started](../../basics/deploy-an-application.html) -and [migrating to Vespa Cloud](../../learn/migrating-to-cloud) - -put _deployment.xml_ next to _services.xml_. - - -## Next steps - -After a successful deployment to the [dev](../environments.html#dev) environment, -iterate on the configuration to implement your application on Vespa. -The _dev_ environment is ideal for this, with rapid deployment cycles. - -For production serving, deploy to the [prod](../environments.html#prod) environment - -follow the steps in [production deployment](../production-deployment.html). - - -## Enclave teardown - -To tear down a Vespa Cloud Enclave system, do the steps above in reverse order: - -1. [Undeploy the application(s)](../deleting-applications.html) -2. [Undeploy the Terraform changes](https://developer.hashicorp.com/terraform/cli/commands/destroy) - -It is important to undeploy the Vespa application(s) first. There will be a delay between when the application is deleted and all its supporting cloud resources are removed. -After running the Terraform, Vespa Cloud cannot manage the resources allocated, so you must clean up these yourself. - -## Troubleshooting - -**Identities restricted by domain**: If your GCP organization is using -[domain restriction for identities](https://cloud.google.com/resource-manager/docs/organization-policy/restricting-domains) -you will need to permit Vespa.ai GCP identities to be added to your project. -For Vespa Cloud the organization ID to allow identities from is: _1056130768533_, -and the Google Customer ID is _C00u32w3e_. diff --git a/en/operations/enclave/gcp-getting-started.mdx b/en/operations/enclave/gcp-getting-started.mdx new file mode 100644 index 0000000000..5de7521cb5 --- /dev/null +++ b/en/operations/enclave/gcp-getting-started.mdx @@ -0,0 +1,84 @@ +--- +title: "Getting started with Vespa Cloud Enclave in GCP" +sidebarTitle: "GCP getting started" +--- + +Setting up Vespa Cloud Enclave requires: + + + +Registration at [Vespa Cloud](https://console.vespa-cloud.com), or use a pre-existing tenant. + + +Running a [Terraform](https://www.terraform.io/) configuration to provision necessary GCP resources in the project. + + +Registration of the GCP project in Vespa Cloud. + + +Deployment of a Vespa application. + + + +### 1. Vespa Cloud Tenant setup + +Register at [Vespa Cloud](https://console.vespa-cloud.com) or use an existing tenant. Note that the tenant must be on a [paid plan](https://vespa.ai/pricing/). + +### 2. Configure GCP Project + + +**Note:** + +We recommend using a *dedicated* project for your Vespa Cloud Enclave. Resources in this project will be fully managed by Vespa Cloud. + + +One project can host all your Vespa applications, there is no need for multiple tenants or projects. + +The project you intend to use for Vespa Cloud Enclave must be prepared for deploying Vespa applications. Use [Terraform](https://www.terraform.io/) to set up the necessary resources using the [modules](https://registry.terraform.io/modules/vespa-cloud/enclave/google/latest) published by the Vespa team. + +Modify the [multi-region example](https://github.com/vespa-cloud/terraform-google-enclave/blob/main/examples/multi-region/main.tf) for your deployment. + +If you are unfamiliar with Terraform: It is a tool to manage resources and their configuration in various cloud providers, like AWS and GCP. Terraform has published a [GCP](https://developer.hashicorp.com/terraform/tutorials/gcp-get-started) tutorial, and we strongly encourage enclave users to read and follow the Terraform recommendations for [CI/CD](https://developer.hashicorp.com/terraform/tutorials/automation/automate-terraform). + +The Terraform module we provide is regularly updated to add new required resources or extra permissions for Vespa Cloud to automate the operations of your applications. In order for your enclave applications to use the new features you must re-apply your terraform templates with the latest release. The [notification system](/en/operations/notifications) will let you know when a new release is available. + +### 3. Onboarding + +Once the GCP project is configured, contact [support@vespa.ai](mailto:support@vespa.ai) stating which tenant should be on-boarded to use Vespa Cloud Enclave. Also include the [GCP Project ID](https://cloud.google.com/resource-manager/docs/creating-managing-projects#identifying_projects) to associate with the tenant. + + +**Note:** + +Wait for confirmation from the Vespa team that onboarding is complete before deploying an application in the next step. + + +### 4. Deploy a Vespa application + +By default, all applications are deployed on resources in Vespa Cloud accounts. To deploy in your enclave account, update [deployment.xml](/en/reference/applications/deployment) to reference the GCP project you onboarded: + +```xml + + + +``` + +Useful resources are [getting started](/en/basics/deploy-an-application) and [migrating to Vespa Cloud](/en/learn/migrating-to-cloud) - put *deployment.xml* next to *services.xml*. + +## Next steps + +After a successful deployment to the [dev](/en/operations/environments#dev) environment, iterate on the configuration to implement your application on Vespa. The *dev* environment is ideal for this, with rapid deployment cycles. + +For production serving, deploy to the [prod](/en/operations/environments#prod) environment - follow the steps in [production deployment](/en/reference/applications/deployment). + +## Enclave teardown + +To tear down a Vespa Cloud Enclave system, do the steps above in reverse order: + +1. [Undeploy the application(s)](/en/operations/deleting-applications) +2. Undeploy the Terraform changes + +It is important to undeploy the Vespa application(s) first. After running the Terraform, Vespa Cloud cannot manage the resources allocated, so you must clean up these yourself. + +## Troubleshooting + +**Identities restricted by domain**: If your GCP organization is using [domain restriction for identities](https://cloud.google.com/resource-manager/docs/organization-policy/restricting-domains) you will need to permit Vespa.ai GCP identities to be added to your project. For Vespa Cloud the organization ID to allow identities from is: *1056130768533*, and the Google Customer ID is *C00u32w3e*. \ No newline at end of file diff --git a/en/operations/enclave/operations.md b/en/operations/enclave/operations.md deleted file mode 100644 index 8e6d60f20f..0000000000 --- a/en/operations/enclave/operations.md +++ /dev/null @@ -1,42 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: Operations and Support for Vespa Cloud Enclave -applies_to: cloud -redirect_from: -- /en/cloud/enclave/operations ---- - -Vespa Cloud Enclave requires that resources provisioned within the VPC are wholly -managed by the Vespa Cloud orchestration services, and must not be manually -managed by tenant operations. Changing or removing the resources created by the -Configuration Servers will negatively impact your Vespa application and may -prevent Vespa Cloud from properly managing the applications as well as Vespa -engineers from support it. - -The Terraform modules might see occasional backwards compatible updates. It is -recommended that the tenant applies updates to their system on a regular basis. -For more information, see the Terraform documentation on -[using Terraform in automation](https://developer.hashicorp.com/terraform/tutorials/automation/automate-terraform). - -The network access granted to Vespa Hosts must be in place for the Vespa -application to operate properly. If network access is restricted the Vespa -application might stop working. - - -## Custom resource tags - -Custom tags can be applied to the cloud resources (virtual machines and disks) that Vespa Cloud -provisions inside the tenant's cloud account. Tags are declared in _deployment.xml_ via the -[``](../../reference/applications/deployment.html#resource-tags) element and are -commonly used for cost tracking and resource management. This is supported on AWS, Azure, and GCP. - - -## Quota -Make sure your organization's AWS or GCP quotas are set high enough to support common Vespa Cloud use cases. -A common use case is migrating to new instance types, -and this causes temporary doubled (or more) resource usage in the data migration transition period. -Other use cases with temporary increased resource usage are node replacements. - -Best practise is to ensure the quota is 3x of current resource usage, to also cover for capacity expansion. - -This is not to be confused with the [Vespa Cloud quota](https://cloud.vespa.ai/en/reference/quota). diff --git a/en/operations/enclave/operations.mdx b/en/operations/enclave/operations.mdx new file mode 100644 index 0000000000..2c7202b03c --- /dev/null +++ b/en/operations/enclave/operations.mdx @@ -0,0 +1,21 @@ +--- +title: "Operations and Support for Vespa Cloud Enclave" +sidebarTitle: "Operations" +--- +Vespa Cloud Enclave requires that resources provisioned within the VPC are wholly managed by the Vespa Cloud orchestration services, and must not be manually managed by tenant operations. Changing or removing the resources created by the Configuration Servers will negatively impact your Vespa application and may prevent Vespa Cloud from properly managing the applications as well as Vespa engineers from support it. + +The Terraform modules might see occasional backwards compatible updates. It is recommended that the tenant applies updates to their system on a regular basis. For more information, see the Terraform documentation on [using Terraform in automation](https://developer.hashicorp.com/terraform/tutorials/automation/automate-terraform). + +The network access granted to Vespa Hosts must be in place for the Vespa application to operate properly. If network access is restricted the Vespa application might stop working. + +## Custom resource tags + +Custom tags can be applied to the cloud resources (virtual machines and disks) that Vespa Cloud provisions inside the tenant's cloud account. Tags are declared in *deployment.xml* via the [``](/en/reference/applications/deployment#resource-tags) element and are commonly used for cost tracking and resource management. This is supported on AWS, Azure, and GCP. + +## Quota + +Make sure your organization's AWS or GCP quotas are set high enough to support common Vespa Cloud use cases. A common use case is migrating to new instance types, and this causes temporary doubled (or more) resource usage in the data migration transition period. Other use cases with temporary increased resource usage are node replacements. + +Best practise is to ensure the quota is 3x of current resource usage, to also cover for capacity expansion. + +This is not to be confused with the [Vespa Cloud quota](/en/cloud/quota). \ No newline at end of file diff --git a/en/operations/endpoint-routing.html b/en/operations/endpoint-routing.html deleted file mode 100644 index 3fd8461e1c..0000000000 --- a/en/operations/endpoint-routing.html +++ /dev/null @@ -1,121 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Routing and endpoints" -applies_to: cloud -redirect_from: -- /en/cloud/routing.html ---- - -

    - Vespa Cloud supports multiple methods of routing requests to an application. - This guide describes how these routing methods work, failover, and how to configure them. -

    -

    - By default, each deployment of a Vespa Cloud application will have a zone endpoint. - In addition to the default zone endpoint, one can configure global endpoints. -

    -

    - All endpoints for an application are available under the - endpoints tab of each deployment in the console. -

    - - - -

    Endpoint format

    -

    Vespa Cloud endpoints are on the format: {random}.{random}.{scope}.vespa-app.cloud.

    - - - -

    Endpoint scopes

    - - -

    Zone endpoint

    -

    - This is the default endpoint for a deployment. - Requests through a zone endpoint are sent directly to the zone. -

    -

    - Zone endpoints are created implicitly, one per container cluster declared - in services.xml. - Zone endpoints are not configurable. -

    -

    Zone endpoints have the suffix z.vespa-app.cloud

    - - -

    Global endpoint

    -

    - A global endpoint is an endpoint that can route requests to multiple zones. - It can be configured in deployment.xml. - Similar to how a CDN works, - requests through this endpoint will be routed to the nearest zone based on geo proximity, - i.e. the zone that is nearest to the client. -

    -

    Global endpoints have the suffix g.vespa-app.cloud

    -{% include important.html content='Global endpoints do not support feeding. -Feeding must be done through zone endpoints.' %} - - - -

    Routing control

    -

    Vespa Cloud has two mechanisms for manually controlling routing of requests to a zone:

    -
      -
    • - Removing the <region> element from - the relevant <endpoint> elements in - deployment.xml and deploying a new version of your application. -
    • -
    • Changing the status through the console.
    • -
    -

    - This section describes the latter mechanism. - Navigate to the relevant deployment of your application in the console. - Hovering over the GLOBAL ROUTING badge will display the current status and when it was last changed. -

    - - -

    Change status

    -

    -In case of a production emergency, -a zone can be manually set out to prevent it from receiving requests: -

    -
      -
    1. - Hover over the GLOBAL ROUTING badge for the problematic deployment and click Deactivate. -
    2. -
    3. - Inspection of the status will now show the status set to OUT. - To set the zone back in and have it continue receiving requests: - Hover over the GLOBAL ROUTING badge again and click Activate. -
    4. -
    - - - -

    Behaviour

    -

    - Changing the routing status is independent of the endpoint scope used. - You're technically overriding the routing status the deployment reports to the Vespa - Cloud routing infrastructure. This means that a change to routing status - affects both zonal endpoints and global endpoints. -

    -

    - Deactivating a deployment disables routing of requests to that deployment - through global endpoints until the deployment is activated again. - As routing through these endpoints is DNS-based, - it may take up between 5 and 15 minutes for all traffic to shift to other deployments. -

    -

    - If all deployments of an endpoint are deactivated, requests are distributed as if all deployments were active. - This is because attempting to route traffic according to the original configuration is preferable - to discarding all requests. -

    - - - -

    AWS clients

    -

    - While Vespa Cloud is hosted in AWS, clients that talk to Vespa Cloud - from AWS nodes will be treated as any other client from the Internet. - This means clients in AWS will generate regular Internet egress traffic - even though they are talking to a service in AWS in the same zone. -

    diff --git a/en/operations/endpoint-routing.mdx b/en/operations/endpoint-routing.mdx new file mode 100644 index 0000000000..b3e3fbea2a --- /dev/null +++ b/en/operations/endpoint-routing.mdx @@ -0,0 +1,64 @@ +--- +title: "Routing and endpoints" +sidebarTitle: "Endpoint routing" +--- +Vespa Cloud supports multiple methods of routing requests to an application. This guide describes how these routing methods work, failover, and how to configure them. + +By default, each deployment of a Vespa Cloud application will have a zone endpoint. In addition to the default zone endpoint, one can configure global endpoints. + +All endpoints for an application are available under the *endpoints* tab of each deployment in the console. + +## Endpoint format + +Vespa Cloud endpoints are on the format: `{random}.{random}.{scope}.vespa-app.cloud`. + +## Endpoint scopes + +### Zone endpoint + +This is the default endpoint for a deployment. Requests through a zone endpoint are sent directly to the zone. + +Zone endpoints are created implicitly, one per container cluster declared in [services.xml](/en/reference/applications/services/container). Zone endpoints are not configurable. + +Zone endpoints have the suffix `z.vespa-app.cloud` + +### Global endpoint + +A global endpoint is an endpoint that can route requests to multiple zones. It can be configured in [deployment.xml](/en/reference/applications/deployment#endpoints-global). Similar to how a [CDN](https://en.wikipedia.org/wiki/Content_delivery_network) works, requests through this endpoint will be routed to the nearest zone based on geo proximity, i.e. the zone that is nearest to the client. + +Global endpoints have the suffix `g.vespa-app.cloud` + + +**Important:** + +Global endpoints do not support feeding. Feeding must be done through zone endpoints. + + + +## Routing control + +Vespa Cloud has two mechanisms for manually controlling routing of requests to a zone: + +- Removing the `` element from the relevant `` elements in [deployment.xml](/en/reference/applications/deployment) and deploying a new version of your application. +- Changing the status through the console. + +This section describes the latter mechanism. Navigate to the relevant deployment of your application in the console. Hovering over the *GLOBAL ROUTING* badge will display the current status and when it was last changed. + +### Change status + +In case of a production emergency, a zone can be manually set out to prevent it from receiving requests: + +1. Hover over the *GLOBAL ROUTING* badge for the problematic deployment and click *Deactivate*. +2. Inspection of the status will now show the status set to *OUT*. To set the zone back in and have it continue receiving requests: Hover over the *GLOBAL ROUTING* badge again and click *Activate*. + +### Behaviour + +Changing the routing status is independent of the endpoint scope used. You're technically overriding the routing status the deployment reports to the Vespa Cloud routing infrastructure. This means that a change to routing status affects both *zonal endpoints* and *global endpoints*. + +Deactivating a deployment disables routing of requests to that deployment through global endpoints until the deployment is activated again. As routing through these endpoints is DNS-based, it may take up between 5 and 15 minutes for all traffic to shift to other deployments. + +If all deployments of an endpoint are deactivated, requests are distributed as if all deployments were active. This is because attempting to route traffic according to the original configuration is preferable to discarding all requests. + +## AWS clients + +While Vespa Cloud is hosted in AWS, clients that talk to Vespa Cloud from AWS nodes will be treated as any other client from the Internet. This means clients in AWS will generate regular Internet egress traffic even though they are talking to a service in AWS in the same zone. \ No newline at end of file diff --git a/en/operations/environments.html b/en/operations/environments.html deleted file mode 100644 index 6f434c8161..0000000000 --- a/en/operations/environments.html +++ /dev/null @@ -1,156 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: Environments -applies_to: cloud -redirect_from: -- /en/cloud/environments ---- - -

    Vespa Cloud has two kinds of environments:

    -
      -
    • Manual environment for rapid development and test: dev
    • -
    • Automated environment with integrated CD pipeline: prod
    • -
    -

    - An application is deployed to one or more zones (see zone list), - which is a combination of an environment and a region, like - vespa deploy -z dev.aws-us-east-1c. -

    - - -

    Dev

    -

    - The dev environment is built for rapid developments cycles, - with auto-downscaling and auto-expiry for ease of use and cost control. - The dev environment is the default, to deploy to this, use vespa deploy. -

    - - -

    Auto downscaling

    -

    - One use case for the dev environment is to take an application package from a prod environment - and deploy to the dev environment to debug. - To minimize cost and make this speedy, - Vespa Cloud will by default ignore nodes and - resources settings. -

    -

    With this, you can safely download an application package from prod (that are normally large) and deploy to dev, with no changes.

    -

    - To override this behavior and control the resources, - specify them explicitly for the dev environment - as described in deployment variants. - Example: -

    -
    {% highlight xml %}
    -
    -    
    -    
    -        
    -    
    ->
    -{% endhighlight %}
    -{% include important.html content='The dev environment has redundancy 1 by default, -and there are no availability or data persistence guarantees. -Do not use applications deployed to these zones for production serving use cases.' %} - - -

    Auto expiry

    -

    - Deployments to dev expire after 14 days of inactivity, - that is, 14 days after the last deployment. - This applies to all plans. - To add 7 more days to the expiry period, redeploy the application or use the Vespa Cloud Console. -

    - - -

    Vespa version

    -

    - The latest active Vespa version is used when deploying to the dev environment. - The deployment is upgraded at a time which is most likely at night for the developer in order to minimize downtime - (based on the time when last deployments were made). - An upgrade will be skipped if metrics indicate ongoing feed or query load, - but will still be done if current version is more than a week old. -

    - - - -

    Prod

    -

    - Applications are deployed to the prod environment for production serving. - Deployments are passed through an integrated CD pipeline for system tests and staging tests. - Read more in automated deployments. -

    - - - -

    Test

    -

    - The test environment is used by the integrated CD pipeline for prod deployments, - to run system tests. - The test capacity is ephemeral and only used during test. - Nodes in test and staging environments do not have access to data in prod environments. -

    - Note that one cannot deploy directly to test and staging environments. - For long-lived test applications (e.g., a QA system that is integrated with other services) use the prod environment. -

    -

    - System tests are always invoked, even if there are no tests defined. - In this case, an instance is just started and then stopped. - This has value in itself, as it ensures that the application is able to start. -

    -

    - Test runs can be aborted. -

    - - - -

    Staging

    -

    - See system tests above, this applies to the staging, too. - Staging tests - use a fraction of the configured prod capacity, this can be overridden to using 1 node regardless of prod cluster size: -

    -
    {% highlight xml %}
    -
    -    
    -        
    -    
    -
    -{% endhighlight %}
    - - - -

    Reference

    -

    Environment settings:

    - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    NameDescriptionExpiryCluster sizes
    devUsed for manual development testing.14 days1
    testUsed for automated system tests.-1
    stagingUsed for automated staging tests.-min(max(2, 0.05 * spec), spec)
    prodHosts all production deployments.No expirymax(2, spec)
    - diff --git a/en/operations/environments.mdx b/en/operations/environments.mdx new file mode 100644 index 0000000000..25b758ad70 --- /dev/null +++ b/en/operations/environments.mdx @@ -0,0 +1,115 @@ +--- +title: "Environments" +--- + +Vespa Cloud has two kinds of environments: + +- Manual environment for rapid development and test: `dev` +- Automated environment with integrated CD pipeline: `prod` + +An application is deployed to one or more *zones* (see [zone list](/en/operations/zones)), which is a combination of an *environment* and a *region*, like `vespa deploy -z dev.aws-us-east-1c`. + +## Dev + +The dev environment is built for rapid developments cycles, with auto-downscaling and auto-expiry for ease of use and cost control. The dev environment is the default, to deploy to this, use `vespa deploy`. + +### Auto downscaling + +One use case for the dev environment is to take an application package from a prod environment and deploy to the dev environment to debug. To minimize cost and make this speedy, Vespa Cloud will by default ignore [nodes](/en/reference/applications/services/services#nodes) and [resources](/en/reference/applications/services/services#resources) settings. + +With this, you can safely download an application package from prod (that are normally large) and deploy to dev, with no changes. + +To override this behavior and control the resources, specify them explicitly for the dev environment as described in [deployment variants](/en/operations/deployment-variants#services.xml-variants). Example: + +```xml + + + + + +> +``` + + +**Important:** + +The `dev` environment has redundancy 1 by default, and there are no availability or data persistence guarantees. Do not use applications deployed to these zones for production serving use cases. + + +### Auto expiry + +Deployments to `dev` expire after 14 days of inactivity, that is, 14 days after the last [deployment](/en/basics/applications#deploying-applications). **This applies to all plans**. To add 7 more days to the expiry period, redeploy the application or use the Vespa Cloud Console. + +### Vespa version + +The latest active Vespa version is used when deploying to the dev environment. The deployment is upgraded at a time which is most likely at night for the developer in order to minimize downtime (based on the time when last deployments were made). An upgrade will be skipped if metrics indicate ongoing feed or query load, but will still be done if current version is more than a week old. + +## Prod + +Applications are deployed to the `prod` environment for production serving. Deployments are passed through an integrated CD pipeline for system tests and staging tests. Read more in [automated deployments](/en/operations/automated-deployments). + +## Test + +The `test` environment is used by the integrated CD pipeline for prod deployments, to run [system tests](/en/operations/automated-deployments#system-tests). The test capacity is ephemeral and only used during test. Nodes in test and staging environments do not have access to data in prod environments. + +Note that one cannot deploy directly to test and staging environments. For long-lived test applications (e.g., a QA system that is integrated with other services) use the prod environment. + +System tests are always invoked, even if there are no tests defined. In this case, an instance is just started and then stopped. This has value in itself, as it ensures that the application is able to start. + +Test runs can be [aborted](/en/operations/automated-deployments#disabling-tests). + +## Staging + +See system tests above, this applies to the staging, too. [Staging tests](/en/operations/automated-deployments#staging-tests) use a fraction of the configured prod capacity, this can be overridden to using 1 node regardless of prod cluster size: + +```xml + + + + + +``` + +## Reference + +Environment settings: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameDescriptionExpiryCluster sizes
    {`dev`}Used for manual development testing.14 days{`1`}
    {`test`}Used for automated system tests.-{`1`}
    {`staging`}Used for automated staging tests.-{`min(max(2, 0.05 * spec), spec)`}
    {`prod`}Hosts all production deployments.No expiry{`max(2, spec)`}
    \ No newline at end of file diff --git a/en/operations/kubernetes/architecture.html b/en/operations/kubernetes/architecture.html deleted file mode 100644 index d53f6f65db..0000000000 --- a/en/operations/kubernetes/architecture.html +++ /dev/null @@ -1,19 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: Architecture -applies_to: enterprise ---- - -Vespa Operator Architecture - -

    - The Vespa Operator is an implementation of the Operator Pattern that extends Kubernetes with custom - orchestration capabilities for Vespa. It relies on a Custom Resource Definition called a VespaSet, which represents - a quorum of ConfigServers in a Kubernetes namespace. The Vespa Operator is responsible for the deployment and lifecycle of the VespaSet resource and its ConfigServers, - which collectively entails the infrastructure for Vespa on Kubernetes. -

    - -

    - Application Packages are deployed to the ConfigServers to create Vespa applications. The ConfigServers will dynamically instantiate the services as individual Pods based on the settings - provided in the Application Package. After an Application Package is deployed, the ConfigServers will remain responsible for the management and lifecycle of the Vespa application. -

    diff --git a/en/operations/kubernetes/architecture.mdx b/en/operations/kubernetes/architecture.mdx new file mode 100644 index 0000000000..dedd147d08 --- /dev/null +++ b/en/operations/kubernetes/architecture.mdx @@ -0,0 +1,11 @@ +--- +title: "Architecture" +--- + + +![Vespa Operator Architecture](/assets/img/vespa-operator-architecture.png) + + +The Vespa Operator is an implementation of the [Operator Pattern](https://kubernetes.io/docs/concepts/extend-kubernetes/operator/) that extends Kubernetes with custom orchestration capabilities for Vespa. It relies on a [Custom Resource Definition](https://kubernetes.io/docs/concepts/extend-kubernetes/api-extension/custom-resources/) called a `VespaSet`, which represents a quorum of [ConfigServers](/en/operations/self-managed/configuration-server) in a Kubernetes namespace. The Vespa Operator is responsible for the deployment and lifecycle of the `VespaSet` resource and its ConfigServers, which collectively entails the infrastructure for Vespa on Kubernetes. + +[Application Packages](/en/basics/applications) are deployed to the [ConfigServers](/en/operations/self-managed/configuration-server) to create Vespa applications. The ConfigServers will dynamically instantiate the services as individual Pods based on the settings provided in the Application Package. After an Application Package is deployed, the ConfigServers will remain responsible for the management and lifecycle of the Vespa application. \ No newline at end of file diff --git a/en/operations/kubernetes/configuration/configure-local-storage-type.html b/en/operations/kubernetes/configuration/configure-local-storage-type.html deleted file mode 100644 index 4bd0808f47..0000000000 --- a/en/operations/kubernetes/configuration/configure-local-storage-type.html +++ /dev/null @@ -1,250 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: Configure Local Storage Type -applies_to: enterprise ---- - -

    - We recommend configuring node-local storage for the content cluster (i.e. the search core) to maximize - performance by avoiding network I/O on the data path. In a standard Vespa deployment, this is controlled through - the storage-type attribute under the resources tag in the application package. - However, that attribute has no effect when running Vespa on Kubernetes. Instead, local storage should be configured through the spec.application.storageClass field in the - VespaSet. Vespa on Kubernetes abstracts away the concept of storage and will - consume whatever is provided by the referenced storage class. -

    - -

    - For ConfigServer pods, storage performance is less critical; therefore, selecting a more cost-efficient network-attached storage class, such as gp3 EBS volumes on Amazon EKS, is generally an appropriate tradeoff. -

    - -

    - To provision node-local storage, we recommend using Kubernetes Local Persistent Volumes. These volumes expose - NodeAffinity constraints to the Kubernetes scheduler, ensuring that Pods consuming them are scheduled - onto nodes where the underlying storage is available. This avoids the need to manually manage NodeAffinity rules on per Pod. -

    - -

    - In addition, the Kubernetes Special Interest Groups (SIGs) provide an external - Local Persistent Volume - static provisioner. This provisioner automatically discovers local disks mounted on each node and creates corresponding - PersistentVolumes, while managing their lifecycle, including cleanup and reuse as Pods are deleted. We recommend using this - component in production deployments. -

    - -

    - This guide walks through setting up local NVMe instance storage on EKS nodes using the Kubernetes Local Volume Static Provisioner. - This exposes the physical NVMe disks available on instances as a local-nvme StorageClass that Application Pods can claim. - While this guide specifically targets an Amazon EKS setup, the concept is similar across different environments - refer to the project for several other examples. -

    - -

    - Setup Local Storage on Amazon EKS -

    - -

    - This guide assumes that your EKS cluster has a Node Group configured with an instance type that supports local NVMe instance storage, - such as m7gd.xlarge. These instance types typically contain the d suffix to designate themselves as specialized for workloads that require local instance storage. - Refer to the AWS EKS Node Groups documentation for further information on configuring Node Groups. -

    - -

    - This guide specifically targets Bottlerocket-based EKS Nodes. These Nodes do not execute the standard EKS bootstrap - script responsible for preparing NVMe instance storage. - Disk formatting and mounting is therefore handled by an init container, after which the static provisioner scans for - available volumes and registers them as PersistentVolumes. -

    - -

    - Add the Helm repository for the Local Volume Static Provisioner. -

    - -
    -$ helm repo add sig-storage-local-static-provisioner https://kubernetes-sigs.github.io/sig-storage-local-static-provisioner
    -$ helm repo update
    -
    - -

    - Create an EKS NVMe instance storage configuration. The example below will run an initContainer - that will scan for NVMe instance store disks and format them as ext4 under /mnt/disks, which the static provisioner will detect. -

    - -
    -cat <<'EOF' > local-nvme-values.yaml
    -# EKS Bottlerocket NVMe instance storage configuration.
    -classes:
    -  - name: local-nvme
    -    hostDir: /mnt/disks
    -    mountDir: /mnt/disks
    -    volumeMode: Filesystem
    -    fsType: ext4
    -    accessMode: ReadWriteOnce
    -    storageClass:
    -      reclaimPolicy: Delete
    -      isDefaultClass: false
    -
    -nodeSelector:
    -  eks.amazonaws.com/nodegroup: test-node-group
    -
    -priorityClassName: system-node-critical
    -mountDevVolume: true
    -
    -initContainers:
    -  - name: nvme-disk-setup
    -    image: registry.k8s.io/sig-storage/local-volume-provisioner:v2.8.0
    -    securityContext:
    -      privileged: true
    -    command:
    -      - sh
    -      - -c
    -      - |
    -        set -eu
    -
    -        DISKS_PATH=/mnt/disks
    -
    -        disks=$(ls /dev/nvme*n1 2>/dev/null | grep -v '/dev/nvme0n1' || true)
    -
    -        if [ -z "${disks}" ]; then
    -          echo "No NVMe instance-store disks found, nothing to do"
    -          exit 0
    -        fi
    -
    -        for disk in ${disks}; do
    -          echo "Processing ${disk}..."
    -
    -          model=$(cat /sys/block/$(basename ${disk})/device/model 2>/dev/null || true)
    -          if ! echo "${model}" | grep -q "Amazon EC2 NVMe Instance Storage"; then
    -            echo "${disk} is not an instance store disk (model: ${model}), skipping"
    -            continue
    -          fi
    -
    -          if grep -q "^${disk} " /proc/mounts; then
    -            echo "${disk} is already mounted, skipping"
    -            continue
    -          fi
    -
    -          if ! blkid "${disk}" >/dev/null 2>&1; then
    -            echo "No filesystem on ${disk}, formatting as ext4..."
    -            mkfs.ext4 -F "${disk}"
    -          fi
    -
    -          uuid=$(blkid -s UUID -o value "${disk}")
    -          if [ -z "${uuid}" ]; then
    -            echo "Could not determine UUID for ${disk}, skipping"
    -            continue
    -          fi
    -
    -          mount_point="${DISKS_PATH}/${uuid}"
    -          mkdir -p "${mount_point}"
    -          echo "Mounting ${disk} (UUID=${uuid}) at ${mount_point}"
    -          mount "${disk}" "${mount_point}"
    -        done
    -
    -        echo "Setup complete. Disks mounted under ${DISKS_PATH}:"
    -        grep "${DISKS_PATH}" /proc/mounts || echo "  (none found)"
    -    volumeMounts:
    -      - name: provisioner-dev
    -        mountPath: /dev
    -      - name: local-nvme
    -        mountPath: /mnt/disks
    -        mountPropagation: Bidirectional
    -
    -resources:
    -  requests:
    -    cpu: 10m
    -    memory: 32Mi
    -  limits:
    -    cpu: 100m
    -    memory: 128Mi
    -EOF
    -
    -$ helm install local-volume-provisioner \
    -  sig-storage-local-static-provisioner/local-static-provisioner \
    -  --namespace kube-system \
    -  --values local-nvme-values.yaml
    -
    - -

    - mountPropagation: Bidirectional will ensure that the volume mount is propagated back to the host, and priorityClassName: system-node-critical - ensures the provisioner Pod will not be evicted in the case of Node pressure. -

    - -

    - After installing the static provisioner, a StorageClass type of local-nvme will be created. This - should be used in the spec.application.storageClass attribute of the VespaSet. -

    - -
    -$ kubectl get storageclasses
    -NAME         PROVISIONER                    RECLAIMPOLICY   VOLUMEBINDINGMODE      ALLOWVOLUMEEXPANSION   AGE
    -local-nvme   kubernetes.io/no-provisioner   Delete          WaitForFirstConsumer   false                  12h
    -
    - -

    - Ensure that the VolumeBindingMode is WaitForFirstConsumer to delay - PersistentVolume binding until a Pod is scheduled, allowing the scheduler to place the Pod on a Node where the - storage physically resides. -

    - -

    - After the initContainer has completed, the static provisioner will provision PersistentVolumes. -

    - -
    -$ kubectl get persistentvolumes
    -NAME                                       CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM   STORAGECLASS   VOLUMEATTRIBUTESCLASS   REASON   AGE
    -local-pv-201c66f3                          216Gi      RWO            Delete           Available           local-nvme     <unset>                          12h
    -local-pv-2942e993                          216Gi      RWO            Delete           Available           local-nvme     <unset>                          12h
    -local-pv-2fea7934                          216Gi      RWO            Delete           Available           local-nvme     <unset>                          12h
    -local-pv-335a2831                          216Gi      RWO            Delete           Available           local-nvme     <unset>                          12h
    -local-pv-3499cebf                          216Gi      RWO            Delete           Available           local-nvme     <unset>                          12h
    -local-pv-36dc72b5                          216Gi      RWO            Delete           Available           local-nvme     <unset>                          12h
    -local-pv-37928b3d                          216Gi      RWO            Delete           Available           local-nvme     <unset>                          12h
    -local-pv-5e09d438                          216Gi      RWO            Delete           Available           local-nvme     <unset>                          12h
    -local-pv-6e9849a9                          216Gi      RWO            Delete           Available           local-nvme     <unset>                          12h
    -
    - -

    - Configure the VespaSet to use the newly created StorageClass. For example: -

    - -
    -# vespaset sample for EKS with local storage configured
    -$ cat > vespaset.yaml <<EOF
    -apiVersion: k8s.ai.vespa/v1
    -kind: VespaSet
    -metadata:
    -  name: vespaset-sample
    -  namespace: ${NAMESPACE}
    -spec:
    -  version: "${VESPA_VERSION}"
    -
    -  configServer:
    -    image: "${VESPA_OPERATOR_IMAGE}"
    -    storageClass: "gp3"
    -    generateRbac: false
    -
    -  application:
    -    image: "${VESPA_IMAGE}"
    -    storageClass: "local-nvme"
    -
    -  ingress:
    -    endpointType: "LOAD_BALANCER"
    -EOF
    -
    -$ kubectl apply -f vespaset.yaml
    -
    - -

    - Other Provisioners -

    - -

    - Several other local storage provisioners, such as OpenEBS Dynamic LocalPV Provisioner and TopoLVM - may be used as alternatives. These provisioners offer dynamic volume provisioning, creating PersistentVolumes on demand rather than pre-provisioning them, - which may be preferable in environments where disk availability changes frequently. - - However, some provisioners may require manual configuration of NodeAffinity rules to ensure pods are scheduled on nodes where the storage physically resides. - In these cases, refer to the PodTemplates section on configuring custom NodeAffinity rules for ConfigServer and Application Pods. -

    - - diff --git a/en/operations/kubernetes/configuration/configure-local-storage-type.mdx b/en/operations/kubernetes/configuration/configure-local-storage-type.mdx new file mode 100644 index 0000000000..9370780ba9 --- /dev/null +++ b/en/operations/kubernetes/configuration/configure-local-storage-type.mdx @@ -0,0 +1,183 @@ +--- +title: "Configure Local Storage Type" +--- +We recommend configuring node-local storage for the [content cluster](/en/content/proton) (i.e. the search core) to maximize performance by avoiding network I/O on the data path. In a standard Vespa deployment, this is controlled through the `storage-type` attribute under the [resources](/en/reference/applications/services/services#resources) tag in the [application package](/en/basics/applications). However, that attribute has no effect when running Vespa on Kubernetes. Instead, local storage should be configured through the `spec.application.storageClass` field in the `VespaSet`. Vespa on Kubernetes abstracts away the concept of storage and will consume whatever is provided by the referenced storage class. + +For ConfigServer pods, storage performance is less critical; therefore, selecting a more cost-efficient network-attached storage class, such as `gp3` EBS volumes on Amazon EKS, is generally an appropriate tradeoff. + +To provision node-local storage, we recommend using Kubernetes [Local Persistent Volumes](https://kubernetes.io/blog/2019/04/04/kubernetes-1.14-local-persistent-volumes-ga/). These volumes expose `NodeAffinity` constraints to the Kubernetes scheduler, ensuring that Pods consuming them are scheduled onto nodes where the underlying storage is available. This avoids the need to manually manage NodeAffinity rules on per Pod. + +In addition, the Kubernetes Special Interest Groups (SIGs) provide an external [Local Persistent Volume](https://kubernetes.io/blog/2019/04/04/kubernetes-1.14-local-persistent-volumes-ga/) static provisioner. This provisioner automatically discovers local disks mounted on each node and creates corresponding `PersistentVolumes`, while managing their lifecycle, including cleanup and reuse as Pods are deleted. We recommend using this component in production deployments. + +This guide walks through setting up local NVMe instance storage on EKS nodes using the [Kubernetes Local Volume Static Provisioner](https://github.com/kubernetes-sigs/sig-storage-local-static-provisioner). This exposes the physical NVMe disks available on instances as a `local-nvme` StorageClass that Application Pods can claim. While this guide specifically targets an Amazon EKS setup, the concept is similar across different environments - refer to the [project](https://github.com/kubernetes-sigs/sig-storage-local-static-provisioner/tree/master/helm/examples) for several other examples. + +## Setup Local Storage on Amazon EKS + +This guide assumes that your EKS cluster has a Node Group configured with an instance type that supports local NVMe instance storage, such as `m7gd.xlarge`. These instance types typically contain the `d` suffix to designate themselves as specialized for workloads that require local instance storage. Refer to the [AWS EKS Node Groups](https://docs.aws.amazon.com/eks/latest/userguide/managed-node-groups.html) documentation for further information on configuring Node Groups. + +This guide specifically targets Bottlerocket-based EKS Nodes. These Nodes do not execute the standard EKS bootstrap script responsible for preparing NVMe instance storage. Disk formatting and mounting is therefore handled by an init container, after which the static provisioner scans for available volumes and registers them as `PersistentVolumes`. + +Add the Helm repository for the Local Volume Static Provisioner. + +```text +$ helm repo add sig-storage-local-static-provisioner https://kubernetes-sigs.github.io/sig-storage-local-static-provisioner +$ helm repo update +``` + +Create an EKS NVMe instance storage configuration. The example below will run an [initContainer](https://kubernetes.io/docs/concepts/workloads/pods/init-containers/) that will scan for NVMe instance store disks and format them as `ext4` under `/mnt/disks`, which the static provisioner will detect. + +```bash expandable +cat <<'EOF' > local-nvme-values.yaml +# EKS Bottlerocket NVMe instance storage configuration. +classes: + - name: local-nvme + hostDir: /mnt/disks + mountDir: /mnt/disks + volumeMode: Filesystem + fsType: ext4 + accessMode: ReadWriteOnce + storageClass: + reclaimPolicy: Delete + isDefaultClass: false + +nodeSelector: + eks.amazonaws.com/nodegroup: test-node-group + +priorityClassName: system-node-critical +mountDevVolume: true + +initContainers: + - name: nvme-disk-setup + image: registry.k8s.io/sig-storage/local-volume-provisioner:v2.8.0 + securityContext: + privileged: true + command: + - sh + - -c + - | + set -eu + + DISKS_PATH=/mnt/disks + + disks=$(ls /dev/nvme*n1 2>/dev/null | grep -v '/dev/nvme0n1' || true) + + if [ -z "${disks}" ]; then + echo "No NVMe instance-store disks found, nothing to do" + exit 0 + fi + + for disk in ${disks}; do + echo "Processing ${disk}..." + + model=$(cat /sys/block/$(basename ${disk})/device/model 2>/dev/null || true) + if ! echo "${model}" | grep -q "Amazon EC2 NVMe Instance Storage"; then + echo "${disk} is not an instance store disk (model: ${model}), skipping" + continue + fi + + if grep -q "^${disk} " /proc/mounts; then + echo "${disk} is already mounted, skipping" + continue + fi + + if ! blkid "${disk}" >/dev/null 2>&1; then + echo "No filesystem on ${disk}, formatting as ext4..." + mkfs.ext4 -F "${disk}" + fi + + uuid=$(blkid -s UUID -o value "${disk}") + if [ -z "${uuid}" ]; then + echo "Could not determine UUID for ${disk}, skipping" + continue + fi + + mount_point="${DISKS_PATH}/${uuid}" + mkdir -p "${mount_point}" + echo "Mounting ${disk} (UUID=${uuid}) at ${mount_point}" + mount "${disk}" "${mount_point}" + done + + echo "Setup complete. Disks mounted under ${DISKS_PATH}:" + grep "${DISKS_PATH}" /proc/mounts || echo " (none found)" + volumeMounts: + - name: provisioner-dev + mountPath: /dev + - name: local-nvme + mountPath: /mnt/disks + mountPropagation: Bidirectional + +resources: + requests: + cpu: 10m + memory: 32Mi + limits: + cpu: 100m + memory: 128Mi +EOF + +$ helm install local-volume-provisioner \ + sig-storage-local-static-provisioner/local-static-provisioner \ + --namespace kube-system \ + --values local-nvme-values.yaml +``` + +`mountPropagation: Bidirectional` will ensure that the volume mount is propagated back to the host, and `priorityClassName: system-node-critical` ensures the provisioner Pod will not be evicted in the case of Node pressure. + +After installing the static provisioner, a `StorageClass` type of `local-nvme` will be created. This should be used in the `spec.application.storageClass` attribute of the `VespaSet`. + +```text +$ kubectl get storageclasses +NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION AGE +local-nvme kubernetes.io/no-provisioner Delete WaitForFirstConsumer false 12h +``` + +Ensure that the `VolumeBindingMode` is `WaitForFirstConsumer` to delay `PersistentVolume` binding until a Pod is scheduled, allowing the scheduler to place the Pod on a Node where the storage physically resides. + +After the `initContainer` has completed, the static provisioner will provision `PersistentVolumes`. + +```text expandable +$ kubectl get persistentvolumes +NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS VOLUMEATTRIBUTESCLASS REASON AGE +local-pv-201c66f3 216Gi RWO Delete Available local-nvme 12h +local-pv-2942e993 216Gi RWO Delete Available local-nvme 12h +local-pv-2fea7934 216Gi RWO Delete Available local-nvme 12h +local-pv-335a2831 216Gi RWO Delete Available local-nvme 12h +local-pv-3499cebf 216Gi RWO Delete Available local-nvme 12h +local-pv-36dc72b5 216Gi RWO Delete Available local-nvme 12h +local-pv-37928b3d 216Gi RWO Delete Available local-nvme 12h +local-pv-5e09d438 216Gi RWO Delete Available local-nvme 12h +local-pv-6e9849a9 216Gi RWO Delete Available local-nvme 12h +``` + +Configure the `VespaSet` to use the newly created `StorageClass`. For example: + +```bash expandable +# vespaset sample for EKS with local storage configured +$ cat > vespaset.yaml < -While services.xml defines the Vespa application specification, it abstracts away the underlying Kubernetes infrastructure. -Advanced users often need to configure Kubernetes-specific settings for the Vespa application Pods to integrate Vespa within their broader platform ecosystem. -

    - -

    -The Pod Template mechanism allows you to inject custom configurations into the Vespa application pods created by the ConfigServer. -

    - -

    -Common use cases for overriding the default pod configuration include: - -

      -
    • Sidecar Injection: Running auxiliary containers alongside Vespa for logging (e.g., Fluent Bit), monitoring (e.g., Datadog, Prometheus exporters), or service mesh proxies (e.g., Envoy, Istio).
    • -
    • Scheduling Constraints: Using nodeSelector, affinity, or tolerations to pin Vespa pods to specific hardware (e.g., high-memory nodes, specific availability zones) or isolate them from other workloads.
    • -
    • Metadata Management: Adding custom Labels or Annotations for cost allocation, team ownership, or integration with external inventory tools.
    • -
    • Security & Config: Mounting Kubernetes Secrets or ConfigMaps that contain credentials or environment configurations required by custom sidecars.
    • -
    - -

    Configure Custom Overrides

    - -

    -Overrides are defined in the VespaSet Custom Resource under spec.application.podTemplate and spec.configServer.podTemplate. This field accepts a standard Kubernetes PodTemplateSpec. -

    - -

    -The Operator and ConfigServer treat this template as an overlay. When creating a ConfigServer or Application Pod, the base template of the main vespa container is merged with your custom overlay. -

    - -

    -Vespa on Kubernetes enforces a Add-Only merge strategy. One cannot remove or downgrade core vespa container settings, but only augment them. -

    - - - - - - - - - - - - - - - - - - - - - - - - - - -
    CategoryAllowed ActionsRestricted Actions
    Containers -
      -
    • Add new sidecar containers.
    • -
    • Add env vars/mounts to main container.
    • -
    -
    -
      -
    • Cannot change main container image, command, or args.
    • -
    • Cannot override main container CPU/Memory resources (these are locked to services.xml).
    • -
    -
    Volumes -
      -
    • Add new Volumes (ConfigMap, Secret, EmptyDir).
    • -
    -
    -
      -
    • Cannot modify operator-reserved volumes (e.g., /data).
    • -
    -
    Metadata -
      -
    • Add new Labels and Annotations.
    • -
    -
    -
      -
    • Cannot overwrite operator-created labels and annotations
    • -
    -
    -

    Examples

    - -

    Example 1: Injecting a Logging Sidecar

    - -

    -This example adds a Fluent Bit sidecar to ship logs to a central system. It defines the sidecar container and mounts a shared volume that the Vespa container also writes to. -

    - -
    -apiVersion: k8s.ai.vespa/v1
    -kind: VespaSet
    -metadata:
    -  name: my-vespa-cluster
    -spec:
    -  application:
    -    image: vespaengine/vespa:8.200.15
    -    # Define the Custom Overlay
    -    podTemplate:
    -      spec:
    -        containers:
    -          # 1. Define the Sidecar
    -          - name: fluent-bit
    -            image: fluent/fluent-bit:1.9
    -            volumeMounts:
    -              - name: vespa-logs
    -                mountPath: /opt/vespa/logs/vespa
    -        # 2. Define the Shared Volume
    -        volumes:
    -          - name: vespa-logs
    -            emptyDir: {}
    -
    - -

    Example 2: Pinning Pods to Specific Nodes

    - -

    -This example uses a nodeSelector to ensure Vespa pods only run on nodes labeled with workload=high-performance. -

    - -
    -apiVersion: k8s.ai.vespa/v1
    -kind: VespaSet
    -metadata:
    -  name: prod-vespa
    -spec:
    -  application:
    -    podTemplate:
    -      spec:
    -        # Schedule only on nodes with label 'workload: high-performance'
    -        nodeSelector:
    -          workload: high-performance
    -        # Tolerate the 'dedicated' taint if those nodes are tainted
    -        tolerations:
    -          - key: "dedicated"
    -            operator: "Equal"
    -            value: "search-team"
    -            effect: "NoSchedule"
    -
    - -

    Example 3: Adding Cost Allocation Labels

    - -

    -This example adds custom labels that will appear on every tenant pod, enabling cost tracking by team. -

    - -
    -apiVersion: k8s.ai.vespa/v1
    -kind: VespaSet
    -metadata:
    -  name: shared-vespa
    -spec:
    -  application:
    -    podTemplate:
    -      metadata:
    -        labels:
    -          cost-center: "engineering-search"
    -          owner: "team-alpha"
    -        annotations:
    -          # Example annotation for an external monitoring system
    -          monitoring.datadoghq.com/enabled: "true"
    -
    \ No newline at end of file diff --git a/en/operations/kubernetes/configuration/ingress.html b/en/operations/kubernetes/configuration/ingress.html deleted file mode 100644 index 648187e45d..0000000000 --- a/en/operations/kubernetes/configuration/ingress.html +++ /dev/null @@ -1,121 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: Configure External Access Layer -applies_to: enterprise ---- - -

    - The Vespa Operator automatically provisions Kubernetes Service resources to enable external access for feeding and querying data. - This behavior is controlled by the VespaSet Custom Resource configuration. -

    - -

    - Load balancers are provisioned exclusively for Container clusters. Content clusters communicate internally - and do not require external load balancing services. The type of service provisioned is determined by the - spec.ingress.endpointType field in the VespaSet. -

    - -

    Supported Endpoint Types

    - -

    - The operator supports four endpoint types to cover different infrastructure requirements. -

    - - - - - - - - - - - - - - - - - - - - - - - - - - -
    Endpoint TypeKubernetes Service TypeUse Case
    LOAD_BALANCERLoadBalancerProvision the cloud-native (AWS, GCP, Azure) load-balancer.
    CLUSTER_IPClusterIPEach Container Pod will expose an internal IP address. Should not be used for production use-cases.
    NONEN/AAn external access layer will not be provisioned. Custom networking setups (Istio, Ingress Controllers) where no automatic service is desired.
    - -

    LOAD_BALANCER

    - -

    - This is the recommended configuration for production deployments on cloud providers (EKS, GKE, AKS). - The operator creates a standard Kubernetes LoadBalancer service, triggering the cloud provider to provision - a managed load balancer (e.g., AWS NLB). -

    - -

    - Configuration: -

    -
    -ingress:
    -  endpointType: LOAD_BALANCER
    -
    - -

    - On AWS, the ConfigServer automatically applies the annotation service.beta.kubernetes.io/aws-load-balancer-internal: "true" to all Container pods. - This provisions an internal Network Load Balancer (NLB) accessible only within the VPC where the EKS cluster nodes reside. -

    - -

    CLUSTER_IP

    - -

    - This type restricts access to within the Kubernetes cluster. It provides a stable internal IP and DNS name - (e.g., lb-default.vespa.svc.cluster.local) but assigns no external IP. -

    - -

    - Configuration: -

    -
    -ingress:
    -  endpointType: CLUSTER_IP
    -
    - -

    - The CLUSTER_IP service is ideal for architectures where the clients (e.g., front-end applications or ingestion services) run inside the same Kubernetes cluster as Vespa. -

    - -

    NONE

    - -

    - This option disables automatic Service provisioning. Use this if you intend to manually define Ingress resources, - use a Service Mesh (like Istio or Linkerd), or have complex networking requirements not covered by the standard types. -

    - -

    - Configuration: -

    -
    -ingress:
    -  endpointType: NONE
    -
    - -

    Traffic Routing & Labeling

    - -

    - To ensure zero-downtime deployments, the ConfigServer manages traffic routing dynamically using Kubernetes labels. - The created Services use the selector vespa.ai/tenant-lb: backend. When the Pod is provisioned, these labels - are automatically attached. -

    - -

    - During a rolling upgrade, the label is removed from the terminating Pod(s) before they are shut down. This provides a window - for the remaining traffic to drain before the Pod is upgraded. -

    - -

    - Note: The Service exposes port 80 (plaintext) and 443 (TLS) externally, mapping them to the container's port 4443. -

    \ No newline at end of file diff --git a/en/operations/kubernetes/custom-overrides-podtemplate.mdx b/en/operations/kubernetes/custom-overrides-podtemplate.mdx new file mode 100644 index 0000000000..33447429be --- /dev/null +++ b/en/operations/kubernetes/custom-overrides-podtemplate.mdx @@ -0,0 +1,123 @@ +--- +title: "Provide Custom Overrides" +description: "While services.xml defines the Vespa application specification, it abstracts away the underlying Kubernetes infrastructure. Advanced users often need to configure Kubernetes-specific settings for the Vespa application Pods to integrate Vespa within their broader platform ecosystem." +--- + +The Pod Template mechanism allows you to inject custom configurations into the Vespa application pods created by the ConfigServer. + +Common use cases for overriding the default pod configuration include: + +- **Sidecar Injection**: Running auxiliary containers alongside Vespa for logging (e.g., Fluent Bit), monitoring (e.g., Datadog, Prometheus exporters), or service mesh proxies (e.g., Envoy, Istio). +- **Scheduling Constraints**: Using nodeSelector, affinity, or tolerations to pin Vespa pods to specific hardware (e.g., high-memory nodes, specific availability zones) or isolate them from other workloads. +- **Metadata Management**: Adding custom Labels or Annotations for cost allocation, team ownership, or integration with external inventory tools. +- **Security & Config**: Mounting Kubernetes Secrets or ConfigMaps that contain credentials or environment configurations required by custom sidecars. + +## Configure Custom Overrides + +Overrides are defined in the `VespaSet` Custom Resource under `spec.application.podTemplate` and `spec.configServer.podTemplate`. This field accepts a standard Kubernetes PodTemplateSpec. + +The Operator and ConfigServer treat this template as an overlay. When creating a ConfigServer or Application Pod, the base template of the main `vespa` container is merged with your custom overlay. + +Vespa on Kubernetes enforces a `Add-Only` merge strategy. One cannot remove or downgrade core `vespa` container settings, but only augment them. + + + + + + + + + + + + + + + + + + + + + + + + + + +
    CategoryAllowed ActionsRestricted Actions
    **Containers**• Add new sidecar containers.
    • Add env vars/mounts to main container.
    • Cannot change main container image, command, or args.
    • Cannot override main container CPU/Memory resources (these are locked to {`services.xml`}).
    **Volumes**• Add new Volumes (ConfigMap, Secret, EmptyDir).• Cannot modify operator-reserved volumes (e.g., {`/data`}).
    **Metadata**• Add new Labels and Annotations.• Cannot overwrite operator-created labels and annotations
    + +## Examples + +### Example 1: Injecting a Logging Sidecar + +This example adds a Fluent Bit sidecar to ship logs to a central system. It defines the sidecar container and mounts a shared volume that the Vespa container also writes to. + +```bash +apiVersion: k8s.ai.vespa/v1 +kind: VespaSet +metadata: + name: my-vespa-cluster +spec: + application: + image: vespaengine/vespa:8.200.15 + # Define the Custom Overlay + podTemplate: + spec: + containers: + # 1. Define the Sidecar + - name: fluent-bit + image: fluent/fluent-bit:1.9 + volumeMounts: + - name: vespa-logs + mountPath: /opt/vespa/logs/vespa + # 2. Define the Shared Volume + volumes: + - name: vespa-logs + emptyDir: {} +``` + +### Example 2: Pinning Pods to Specific Nodes + +This example uses a nodeSelector to ensure Vespa pods only run on nodes labeled with workload=high-performance. + +```bash +apiVersion: k8s.ai.vespa/v1 +kind: VespaSet +metadata: + name: prod-vespa +spec: + application: + podTemplate: + spec: + # Schedule only on nodes with label 'workload: high-performance' + nodeSelector: + workload: high-performance + # Tolerate the 'dedicated' taint if those nodes are tainted + tolerations: + - key: "dedicated" + operator: "Equal" + value: "search-team" + effect: "NoSchedule" +``` + +### Example 3: Adding Cost Allocation Labels + +This example adds custom labels that will appear on every tenant pod, enabling cost tracking by team. + +```bash +apiVersion: k8s.ai.vespa/v1 +kind: VespaSet +metadata: + name: shared-vespa +spec: + application: + podTemplate: + metadata: + labels: + cost-center: "engineering-search" + owner: "team-alpha" + annotations: + # Example annotation for an external monitoring system + monitoring.datadoghq.com/enabled: "true" +``` \ No newline at end of file diff --git a/en/operations/kubernetes/deployment/dev-mode.html b/en/operations/kubernetes/deployment/dev-mode.html deleted file mode 100644 index 876b7453e7..0000000000 --- a/en/operations/kubernetes/deployment/dev-mode.html +++ /dev/null @@ -1,115 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: Setup Dev Environment -applies_to: enterprise ---- - -

    - The steps to enable the dev environment for Vespa on Kubernetes are described in this guide. - This is a one-time irreversible operation. Once a VespaSet has been deployed in the dev environment - configuration, it cannot be reserved. -

    - -{% include important.html content='The dev environment is intended for local development, integration testing, and experimentation — not -for production serving.' %} - -

    Dev Environment

    - -

    - Contrary to Vespa Cloud, the dev environment must additionally be configured at the VespaSet resource level. - Once this is enabled, any Vespa Cluster that is reconciled through this VespaSet will have a min-availability in their - contenet cluster and node count of 1 for all cluster types. -

    - -

    - As such, HA (high-availability) of Vespa Pods is not guaranteed, and availability will be reduced - during upgrades. The only exception is the ConfigServer Pods, which must always maintain a replica count of 3 to ensure a quorum. - -

    - -

    - For more information on Environments, refer to the Vespa Cloud documentation. -

    - -

    Enable Dev Environment

    - -

    - The dev environment is activated by adding the following annotation to the VespaSet resource: -

    - - - - - - - - - - - - - - - - -
    AnnotationValueEffect
    internal.vespa.ai/environmentdev - Signals to the ConfigServer that this is a dev environment. -
    - -
    -$ cat > vespaset-dev.yaml <<EOF
    -apiVersion: k8s.ai.vespa/v1
    -kind: VespaSet
    -metadata:
    -  name: vespaset-dev
    -  namespace: ${NAMESPACE}
    -  annotations:
    -    internal.vespa.ai/environment: "dev"
    -spec:
    -  version: "${VESPA_VERSION}"
    -
    -  configServer:
    -    image: "${VESPA_OPERATOR_IMAGE}"
    -    storageClass: "local-storage"
    -    generateRbac: false
    -
    -  application:
    -    image: "${VESPA_IMAGE}"
    -    storageClass: "local-storage"
    -
    -  ingress:
    -    endpointType: "NONE"
    -EOF
    -
    -$ kubectl apply -f vespaset-dev.yaml
    -
    - -

    - It is then possible to deploy a Vespa Application with minimal resources. An example is shown below. -

    - -
    -<?xml version="1.0" encoding="utf-8" ?>
    -<services version="1.0" xmlns:deploy="vespa" xmlns:preprocess="properties">
    -
    -    <container id="default" version="1.0">
    -        <document-api/>
    -        <search/>
    -
    -        <nodes count="1">
    -            <resources vcpu="2" memory="2Gb" disk="20Gb" />
    -        </nodes>
    -    </container>
    -
    -    <content id="music" version="1.0">
    -        <min-redundancy>1</min-redundancy>
    -        <documents>
    -            <document type="music" mode="index" />
    -        </documents>
    -        <nodes count="1">
    -            <resources vcpu="2" memory="2Gb" disk="20Gb" />
    -        </nodes>
    -    </content>
    -
    -</services>
    -
    diff --git a/en/operations/kubernetes/deployment/dev-mode.mdx b/en/operations/kubernetes/deployment/dev-mode.mdx new file mode 100644 index 0000000000..535fa94864 --- /dev/null +++ b/en/operations/kubernetes/deployment/dev-mode.mdx @@ -0,0 +1,97 @@ +--- +title: "Setup Dev Environment" +--- + +The steps to enable the `dev` environment for Vespa on Kubernetes are described in this guide. This is a one-time irreversible operation. Once a `VespaSet` has been deployed in the `dev` environment configuration, it cannot be reserved. + + +**Important:** + +The `dev` environment is intended for local development, integration testing, and experimentation — not for production serving. + + +## Dev Environment + +Contrary to Vespa Cloud, the `dev` environment must additionally be configured at the `VespaSet` resource level. Once this is enabled, any Vespa Cluster that is reconciled through this `VespaSet` will have a `min-availability` in their `contenet` cluster and `node` count of 1 for all cluster types. + +As such, HA (high-availability) of Vespa Pods is not guaranteed, and availability will be reduced during upgrades. The only exception is the ConfigServer Pods, which must always maintain a replica count of 3 to ensure a quorum. + +For more information on Environments, refer to the [Vespa Cloud](/en/operations/environments#dev) documentation. + +## Enable Dev Environment + +The `dev` environment is activated by adding the following annotation to the `VespaSet` resource: + + + + + + + + + + + + + + + + +
    AnnotationValueEffect
    {`internal.vespa.ai/environment`}{`dev`}Signals to the ConfigServer that this is a {`dev`} environment.
    + +```bash +$ cat > vespaset-dev.yaml < + + + + + + + + + + + + + 1 + + + + + + + + + +``` \ No newline at end of file diff --git a/en/operations/kubernetes/deployment/ecr-pull-through-cache.html b/en/operations/kubernetes/deployment/ecr-pull-through-cache.html deleted file mode 100644 index 5aef181c72..0000000000 --- a/en/operations/kubernetes/deployment/ecr-pull-through-cache.html +++ /dev/null @@ -1,95 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: Setup Amazon ECR Pull-Through Cache -applies_to: enterprise ---- - -

    - For production, we recommend mirroring the upstream artifacts into your own registry. This section shows how to create an - Amazon ECR pull-through cache - for the images referenced in the Installation guide. -

    - -

    AWS Console Steps

    -
      -
    1. Open AWS Console -> Amazon ECR -> Private registry -> Pull through cache rules.
    2. -
    3. Choose Create rule.
    4. -
    5. Set ECR repository prefix to vespa-cache.
    6. -
    7. Set Upstream registry URL to images.ves.pa.
    8. -
    9. Create or select a Secrets Manager credential with your support-provided upstream username/token.
    10. -
    11. Create the rule, then optionally pull one tag of each artifact to warm the cache.
    12. -
    - -

    AWS CLI Steps

    - -

    - Set the AWS account, region, and ECR registry variables, along with the upstream credentials provided by Vespa support. -

    - -
    -export AWS_ACCOUNT_ID=123456789012
    -export AWS_REGION=us-east-1
    -export ECR_REGISTRY=${AWS_ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com
    -export ECR_CACHE_PREFIX=vespa-cache
    -
    -export VESPAAI_REGISTRY_USER=<support-provided-user>
    -export VESPAAI_REGISTRY_TOKEN=<support-provided-token>
    -
    - -

    - Create a Secrets Manager secret to store the upstream registry credentials. -

    - -
    -aws secretsmanager create-secret \
    -  --name vespa-registry-creds \
    -  --secret-string "{\"username\":\"${VESPAAI_REGISTRY_USER}\",\"password\":\"${VESPAAI_REGISTRY_TOKEN}\"}" \
    -  --region ${AWS_REGION} || \
    -aws secretsmanager put-secret-value \
    -  --secret-id vespa-registry-creds \
    -  --secret-string "{\"username\":\"${VESPAAI_REGISTRY_USER}\",\"password\":\"${VESPAAI_REGISTRY_TOKEN}\"}" \
    -  --region ${AWS_REGION}
    -
    - -

    - Create the pull-through cache rule. A single rule covers all repositories under the images.ves.pa host. -

    - -
    -aws ecr create-pull-through-cache-rule \
    -  --ecr-repository-prefix ${ECR_CACHE_PREFIX} \
    -  --upstream-registry-url images.ves.pa \
    -  --credential-arn arn:aws:secretsmanager:${AWS_REGION}:${AWS_ACCOUNT_ID}:secret:vespa-registry-creds \
    -  --region ${AWS_REGION}
    -
    - -

    - Authenticate your local tooling to the ECR registry. -

    - -
    -aws ecr get-login-password --region ${AWS_REGION} | \
    -  docker login --username AWS --password-stdin ${ECR_REGISTRY}
    -aws ecr get-login-password --region ${AWS_REGION} | \
    -  helm registry login --username AWS --password-stdin ${ECR_REGISTRY}
    -
    - -

    - Warm the cache by pulling the Vespa images and the Helm chart artifact. -

    - -
    -podman pull ${ECR_REGISTRY}/${ECR_CACHE_PREFIX}/kubernetes/vespa:${VESPA_VERSION}
    -podman pull ${ECR_REGISTRY}/${ECR_CACHE_PREFIX}/kubernetes/operator:${VESPA_VERSION}
    -helm pull oci://${ECR_REGISTRY}/${ECR_CACHE_PREFIX}/helm/vespa-operator --version ${VESPA_VERSION}
    -
    - -

    - Point the installation variables to ECR. -

    - -
    -export VESPA_IMAGE=${ECR_REGISTRY}/${ECR_CACHE_PREFIX}/kubernetes/vespa
    -export VESPA_OPERATOR_IMAGE=${ECR_REGISTRY}/${ECR_CACHE_PREFIX}/kubernetes/operator
    -export HELM_CHART_REF=oci://${ECR_REGISTRY}/${ECR_CACHE_PREFIX}/helm/vespa-operator
    -
    diff --git a/en/operations/kubernetes/deployment/ecr-pull-through-cache.mdx b/en/operations/kubernetes/deployment/ecr-pull-through-cache.mdx new file mode 100644 index 0000000000..3078ce8253 --- /dev/null +++ b/en/operations/kubernetes/deployment/ecr-pull-through-cache.mdx @@ -0,0 +1,91 @@ +--- +title: "Setup Amazon ECR Pull-Through Cache" +sidebarTitle: "Setup ECR Pull-through Cache" +--- + +For production, we recommend mirroring the upstream artifacts into your own registry. This section shows how to create an [Amazon ECR pull-through cache](https://docs.aws.amazon.com/AmazonECR/latest/userguide/pull-through-cache.html) for the images referenced in the [Installation](/en/operations/kubernetes/deployment/installation) guide. + +## AWS Console Steps + + + +Open AWS Console -> **Amazon ECR** -> **Private registry** -> **Pull through cache rules**. + + +Choose **Create rule**. + + +Set **ECR repository prefix** to `vespa-cache`. + + +Set **Upstream registry URL** to `images.ves.pa`. + + +Create or select a Secrets Manager credential with your support-provided upstream username/token. + + +Create the rule, then optionally pull one tag of each artifact to warm the cache. + + + +## AWS CLI Steps + +Set the AWS account, region, and ECR registry variables, along with the upstream credentials provided by Vespa support. + +```js +export AWS_ACCOUNT_ID=123456789012 +export AWS_REGION=us-east-1 +export ECR_REGISTRY=${AWS_ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com +export ECR_CACHE_PREFIX=vespa-cache + +export VESPAAI_REGISTRY_USER= +export VESPAAI_REGISTRY_TOKEN= +``` + +Create a Secrets Manager secret to store the upstream registry credentials. + +```bash +aws secretsmanager create-secret \ + --name vespa-registry-creds \ + --secret-string "{\"username\":\"${VESPAAI_REGISTRY_USER}\",\"password\":\"${VESPAAI_REGISTRY_TOKEN}\"}" \ + --region ${AWS_REGION} || \ +aws secretsmanager put-secret-value \ + --secret-id vespa-registry-creds \ + --secret-string "{\"username\":\"${VESPAAI_REGISTRY_USER}\",\"password\":\"${VESPAAI_REGISTRY_TOKEN}\"}" \ + --region ${AWS_REGION} +``` + +Create the pull-through cache rule. A single rule covers all repositories under the `images.ves.pa` host. + +```bash +aws ecr create-pull-through-cache-rule \ + --ecr-repository-prefix ${ECR_CACHE_PREFIX} \ + --upstream-registry-url images.ves.pa \ + --credential-arn arn:aws:secretsmanager:${AWS_REGION}:${AWS_ACCOUNT_ID}:secret:vespa-registry-creds \ + --region ${AWS_REGION} +``` + +Authenticate your local tooling to the ECR registry. + +```bash +aws ecr get-login-password --region ${AWS_REGION} | \ + docker login --username AWS --password-stdin ${ECR_REGISTRY} +aws ecr get-login-password --region ${AWS_REGION} | \ + helm registry login --username AWS --password-stdin ${ECR_REGISTRY} +``` + +Warm the cache by pulling the Vespa images and the Helm chart artifact. + +```bash +podman pull ${ECR_REGISTRY}/${ECR_CACHE_PREFIX}/kubernetes/vespa:${VESPA_VERSION} +podman pull ${ECR_REGISTRY}/${ECR_CACHE_PREFIX}/kubernetes/operator:${VESPA_VERSION} +helm pull oci://${ECR_REGISTRY}/${ECR_CACHE_PREFIX}/helm/vespa-operator --version ${VESPA_VERSION} +``` + +Point the installation variables to ECR. + +```bash +export VESPA_IMAGE=${ECR_REGISTRY}/${ECR_CACHE_PREFIX}/kubernetes/vespa +export VESPA_OPERATOR_IMAGE=${ECR_REGISTRY}/${ECR_CACHE_PREFIX}/kubernetes/operator +export HELM_CHART_REF=oci://${ECR_REGISTRY}/${ECR_CACHE_PREFIX}/helm/vespa-operator +``` \ No newline at end of file diff --git a/en/operations/kubernetes/deployment/installation.html b/en/operations/kubernetes/deployment/installation.html deleted file mode 100644 index 855d5fd96f..0000000000 --- a/en/operations/kubernetes/deployment/installation.html +++ /dev/null @@ -1,396 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: Install Vespa on Kubernetes -applies_to: enterprise ---- - -

    - These steps walk through deploying Vespa using the official Helm chart. -

    - - -

    Requirements

    - -

    - The following tools are required for a smooth deployment. -

    - - - -

    - These instructions assume that your kubeconfig is pointing to an active Kubernetes cluster. Refer to the Getting Started guide to create a Kubernetes cluster. For instructions on deploying Vespa - locally on MiniKube, refer to the Deploy Vespa Locally guide. -

    - -

    - Vespa on Kubernetes uses a Custom Resource Definition (CRD) called a VespaSet. Users intending to manage - the CRD definition by themselves should apply it to the cluster before installation. -

    - -

    - The permissions that are needed to run Vespa are listed on the Permissions page. The Helm Chart - will automatically apply a default set of RBAC API resources onto the cluster. -

    - -

    Setup Registry Access

    - -

    - Note: Vespa on Kubernetes is an enterprise feature. You will need access to the images below. - Contact us through our support portal to receive an authentication ID and token. For production use, we recommend mirroring - these images into your own registry or a well-known internal repository appropriate to your infrastructure. -

    - -
      -
    • VESPA_IMAGE=images.ves.pa/kubernetes/vespa
    • -
    • VESPA_OPERATOR_IMAGE=images.ves.pa/kubernetes/operator
    • -
    • HELM_CHART_REF=oci://images.ves.pa/helm/vespa-operator
    • -
    - -

    - We will use this naming convention throughout this guide. The tags for all three images conform to the Vespa Version release semantics. - We recommend using the latest Vespa release as the default. We will refer to it as VESPA_VERSION. -

    - -

    - The Vespa Operator and all Vespa components are local to a namespace. We will refer to the namespace as NAMESPACE in this guide. -

    - -

    Deploy the Vespa Operator

    -

    - Authenticate to the Helm Chart OCI registry. The credentials will be provided by our support team. -

    - -
    -$ helm registry login images.ves.pa -u $USER -p $TOKEN
    -
    - -

    - Install the Helm Chart onto the namespace. This will deploy the Vespa Operator and apply - the VespaSet resource definition. Set image.repository to VESPA_OPERATOR_IMAGE as provided by our support team. - The image.tag refers to the VESPA_VERSION. -

    - -
    -$ helm install vespa-operator $HELM_CHART_REF --namespace $NAMESPACE --create-namespace --set image.repository=$VESPA_OPERATOR_IMAGE --set image.tag=$VESPA_VERSION
    -
    - -

    - The lifecycle of the CRD definition can be managed separately. However, the CRD specification must - be manually applied to the Kubernetes cluster before installing the Helm Chart. Our support team can provide this specification if necessary. - To do this, use the --skip-crds option in Helm. -

    - -
    -$ kubectl apply vespasets.k8s.ai.vespa-v1.yaml
    -$ helm install vespa-operator $HELM_CHART_REF --namespace $NAMESPACE --create-namespace --skip-crds --set image.repository $VESPA_OPERATOR_IMAGE --set image.tag $VESPA_VERSION
    -
    - -

    - Ensure that the Deployment resource was successfully created, and that the Vespa Operator Pod is running. -

    - -

    - Deploy a VespaSet -

    - -

    - To set up a dev environment in Vespa on Kubernetes, refer to the example on the - Setup Dev Environment page. -

    - -

    - A VespaSet is a quorum of ConfigServer Pods that manage the lifecycle of Vespa applications. Several examples of - VespaSet resources are provided in the Helm Chart samples directory. - An example of a VespaSet for an archetypical Amazon Elastic Kubernetes Service (EKS) setup is shown below. -

    - -
    -# vespaset sample for EKS
    -$ cat > vespaset.yaml <<EOF
    -apiVersion: k8s.ai.vespa/v1
    -kind: VespaSet
    -metadata:
    -  name: vespaset-sample
    -  namespace: "${NAMESPACE}"
    -spec:
    -  version: "${VESPA_VERSION}"
    -
    -  configServer:
    -    image: "${VESPA_OPERATOR_IMAGE}"
    -    storageClass: "gp3"
    -    generateRbac: false
    -
    -  application:
    -    image: "${VESPA_IMAGE}"
    -    storageClass: "gp3"
    -
    -  ingress:
    -    endpointType: "LOAD_BALANCER"
    -EOF
    -
    -$ kubectl apply -f vespaset.yaml
    -
    - -

    - An example for an archetypical local deployment on MiniKube is shown below. -

    - -
    -# vespaset sample for MiniKube
    -$ cat > vespaset.yaml <<EOF
    -apiVersion: k8s.ai.vespa/v1
    -kind: VespaSet
    -metadata:
    -  name: vespaset-sample
    -  namespace: ${NAMESPACE}
    -spec:
    -  version: "${VESPA_VERSION}"
    -
    -  configServer:
    -    image: "${VESPA_OPERATOR_IMAGE}"
    -    storageClass: "local-storage"
    -
    -  application:
    -    image: "${VESPA_IMAGE}"
    -    storageClass: "local-storage"
    -
    -  ingress:
    -    endpointType: "NONE"
    -EOF
    -
    -$ kubectl apply -f vespaset.yaml
    -
    - -

    - Once a VespaSet is applied, the operator will automatically detect the newly created resource and create a quorum of - ConfigServers. -

    - -

    - Once applied, the ConfigServers will bootstrap themselves until meeting a quorum. This process takes roughly a minute. The bootstrap process is complete once - the VespaSet resource shows the status as RUNNING for all ConfigServer Pods. For example: -

    - -
    -$ kubectl describe vespaset vespaset-sample -n $NAMESPACE
    -Name:         vespaset-sample
    -Namespace:    ${NAMESPACE}
    -API Version:  k8s.ai.vespa/v1
    -Kind:         VespaSet
    -Metadata:
    -  Finalizers:
    -    vespasets.k8s.ai.vespa/finalizer
    -  Generation:        2
    -  Resource Version:  389668
    -  UID:               b8849ca6-4d1a-4730-a36c-2a93d09ff6db
    -Spec:
    -  Config Server:
    -    Image:            ${VESPA_OPERATOR_IMAGE}
    -    Service Account:  vespa-operator-sa
    -    Storage Class:    local-storage
    -  Application:
    -    Image:          ${VESPA_IMAGE}
    -    Storage Class:  local-storage
    -  Ingress:
    -    Endpoint Type:  NONE
    -  Version:                  ${VESPA_VERSION}
    -Status:
    -  Initialized:  true
    -  Pods:
    -    cfg-1:
    -      Converged Version:     ${VESPA_VERSION}
    -      Last Transition Time:  2026-05-22T15:01:10.029966613Z
    -      Message:               Running
    -      Phase:                 RUNNING
    -    cfg-2:
    -      Converged Version:     ${VESPA_VERSION}
    -      Last Transition Time:  2026-05-22T15:01:14.554896223Z
    -      Message:               Running
    -      Phase:                 RUNNING
    -    cfg-3:
    -      Converged Version:     ${VESPA_VERSION}
    -      Last Transition Time:  2026-05-22T15:01:10.729564482Z
    -      Message:               Running
    -      Phase:                 RUNNING
    -Events:                      ...
    -
    - -

    Deploy a Vespa Application

    - -

    - A Vespa application can be deployed through the ConfigServers' ingress endpoint once a quorum has been met. Refer to the Vespa Sample Applications - to get started. In the following example, we will use the Album Recommendation sample application. -

    - -

    - Set up the Vespa CLI to download the Album Recommendation sample to a directory. -

    - -
    -$ vespa clone album-recommendation myapp && cd myapp
    -
    - -

    - The Node resources must be specified for any application package is deployed on Vespa on Kubernetes. - These will directly translate to Kubernetes container resource requests and limits. - In a default deployment without any PodTemplate overrides, the requests will equal the limits for a container. -

    - -

    - Modify the container and content cluster specifications in the application package, as shown below: -

    - -
    -<?xml version="1.0" encoding="utf-8" ?>
    -<services version="1.0" xmlns:deploy="vespa" xmlns:preprocess="properties">
    -
    -    <container id="default" version="1.0">
    -        <document-api/>
    -        <search/>
    -
    -        <nodes count="2">
    -            <resources vcpu="2" memory="2Gb" disk="20Gb" />
    -        </nodes>
    -    </container>
    -
    -    <content id="music" version="1.0">
    -        <min-redundancy>2</min-redundancy>
    -        <documents>
    -            <document type="music" mode="index" />
    -        </documents>
    -        <nodes count="2">
    -            <resources vcpu="2" memory="2Gb" disk="20Gb" />
    -        </nodes>
    -    </content>
    -
    -</services>
    -
    - -

    - Enable port-forwarding from the ConfigServer's ingress port 19071 to your local port 19071. Any ConfigServer - Pod can be used. -

    - -
    -$ vespa config set target local
    -$ kubectl -n $NAMESPACE port-forward pod/cfg-1 19071:19071
    -
    - -

    - Deploy and activate the application. -

    - -
    -$ vespa prepare --target local
    -$ while ! vespa --target local activate; do sleep 1; done
    -
    - -

    - The ConfigServers will create the Container, Content, and Cluster-Controller Pods as specified in the application package. The deployment - is considered complete once all Pods show the phase RUNNING in the VespaSet status. -

    - -
    -$ kubectl describe vespaset vespaset-sample -n $NAMESPACE
    -Name:         vespaset-sample
    -Namespace:    $NAMESPACE
    -API Version:  k8s.ai.vespa/v1
    -Kind:         VespaSet
    -Metadata:
    -  Finalizers:
    -    vespasets.k8s.ai.vespa/finalizer
    -  Generation:        2
    -  Resource Version:  389668
    -  UID:               b8849ca6-4d1a-4730-a36c-2a93d09ff6db
    -Spec:
    -  Config Server:
    -    Image:            ${VESPA_OPERATOR_IMAGE}
    -    Service Account:  vespa-operator-sa
    -    Storage Class:    local-storage
    -  Application:
    -    Image:          ${VESPA_IMAGE}
    -    Storage Class:  local-storage
    -  Ingress:
    -    Endpoint Type:  NONE
    -  Version:                  ${VESPA_VERSION}
    -Status:
    -  Initialized:  true
    -  Pods:
    -    cfg-1:
    -      Converged Version:     ${VESPA_VERSION}
    -      Last Transition Time:  2026-05-22T15:01:10.029966613Z
    -      Message:               Running
    -      Phase:                 RUNNING
    -    cfg-2:
    -      Converged Version:     ${VESPA_VERSION}
    -      Last Transition Time:  2026-05-22T15:01:14.554896223Z
    -      Message:               Running
    -      Phase:                 RUNNING
    -    cfg-3:
    -      Converged Version:     ${VESPA_VERSION}
    -      Last Transition Time:  2026-05-22T15:01:10.729564482Z
    -      Message:               Running
    -      Phase:                 RUNNING
    -    cluster-controllers-104:
    -      Converged Version:     ${VESPA_VERSION}
    -      Last Transition Time:  2026-05-22T15:03:05.835674401Z
    -      Message:               Running
    -      Phase:                 RUNNING
    -    cluster-controllers-105:
    -      Converged Version:     ${VESPA_VERSION}
    -      Last Transition Time:  2026-05-22T15:03:05.670193721Z
    -      Message:               Running
    -      Phase:                 RUNNING
    -    cluster-controllers-106:
    -      Converged Version:     ${VESPA_VERSION}
    -      Last Transition Time:  2026-05-22T15:03:15.476004765Z
    -      Message:               Running
    -      Phase:                 RUNNING
    -    default-100:
    -      Converged Version:     ${VESPA_VERSION}
    -      Last Transition Time:  2026-05-22T15:01:56.947772851Z
    -      Message:               Running
    -      Phase:                 RUNNING
    -    default-101:
    -      Converged Version:     ${VESPA_VERSION}
    -      Last Transition Time:  2026-05-22T15:01:51.146161725Z
    -      Message:               Running
    -      Phase:                 RUNNING
    -    music-102:
    -      Converged Version:     ${VESPA_VERSION}
    -      Last Transition Time:  2026-05-22T15:01:36.107108177Z
    -      Message:               Running
    -      Phase:                 RUNNING
    -    music-103:
    -      Converged Version:     ${VESPA_VERSION}
    -      Last Transition Time:  2026-05-22T15:01:35.389697089Z
    -      Message:               Running
    -      Phase:                 RUNNING
    -Events:                      ...
    -
    - -

    - Port-forwarding provides a simple way to ingress to the ConfigServer locally. - For other ingress options, refer to the Configuring the External Access Layer page. -

    - -

    Feed and Query Documents

    - -

    - Feed and query documents by port-forwarding the ConfigServer ingress port and the Dataplane ingress port, then using the Vespa CLI. -

    - -
    -$ kubectl -n $NAMESPACE port-forward pod/cfg-1 19071:19071
    -$ kubectl -n $NAMESPACE port-forward pod/default-100 8080:8080
    -$ vespa feed dataset/A-Head-Full-of-Dreams.json
    -$ vespa query 'yql=select * from music where true limit 1'
    -
    - -

    - Refer to the Vespa CLI documentation for the full list of available commands. -

    \ No newline at end of file diff --git a/en/operations/kubernetes/deployment/installation.mdx b/en/operations/kubernetes/deployment/installation.mdx new file mode 100644 index 0000000000..7551a4d5c3 --- /dev/null +++ b/en/operations/kubernetes/deployment/installation.mdx @@ -0,0 +1,238 @@ +--- +title: "Install Vespa on Kubernetes" +sidebarTitle: "Installation" +description: "These steps walk through deploying Vespa using the official Helm chart." +--- + +## Requirements + +The following tools are required for a smooth deployment. + + + + + + + +These instructions assume that your `kubeconfig` is pointing to an active Kubernetes cluster. Refer to the [Getting Started](https://kubernetes.io/docs/setup/) guide to create a Kubernetes cluster. For instructions on deploying Vespa locally on MiniKube, refer to the [Deploy Vespa Locally](/en/reference/applications/deployment) guide. + +Vespa on Kubernetes uses a [Custom Resource Definition](https://kubernetes.io/docs/concepts/extend-kubernetes/api-extension/custom-resources/) (CRD) called a `VespaSet`. Users intending to manage the CRD definition by themselves should apply it to the cluster before installation. + +The permissions that are needed to run Vespa are listed on the [Permissions](/en/operations/kubernetes/deployment/permissions) page. The Helm Chart will automatically apply a default set of RBAC API resources onto the cluster. + +## Setup Registry Access + + +**Note**: + +Vespa on Kubernetes is an enterprise feature. You will need access to the images below. Contact us through our support portal to receive an authentication ID and token. For production use, we recommend mirroring these images into your own registry or a well-known internal repository appropriate to your infrastructure. + + +- `VESPA_IMAGE=images.ves.pa/kubernetes/vespa` +- `VESPA_OPERATOR_IMAGE=images.ves.pa/kubernetes/operator` +- `HELM_CHART_REF=oci://images.ves.pa/helm/vespa-operator` + +We will use this naming convention throughout this guide. The tags for all three images conform to the [Vespa Version](/en/learn/releases) release semantics. We recommend using the latest Vespa release as the default. We will refer to it as `VESPA_VERSION`. + +The Vespa Operator and all Vespa components are local to a namespace. We will refer to the namespace as `NAMESPACE` in this guide. + +## Deploy the Vespa Operator + +Authenticate to the Helm Chart OCI registry. The credentials will be provided by our support team. + +```bash +$ helm registry login images.ves.pa -u $USER -p $TOKEN +``` + +Install the Helm Chart onto the namespace. This will deploy the Vespa Operator and apply the `VespaSet` resource definition. Set `image.repository` to `VESPA_OPERATOR_IMAGE` as provided by our support team. The `image.tag` refers to the `VESPA_VERSION`. + +```bash +$ helm install vespa-operator $HELM_CHART_REF --namespace $NAMESPACE --create-namespace --set image.repository=$VESPA_OPERATOR_IMAGE --set image.tag=$VESPA_VERSION +``` + +The lifecycle of the CRD definition can be managed separately. However, the CRD specification must be manually applied to the Kubernetes cluster before installing the Helm Chart. Our support team can provide this specification if necessary. To do this, use the `--skip-crds` option in Helm. + +```bash +$ kubectl apply vespasets.k8s.ai.vespa-v1.yaml +$ helm install vespa-operator $HELM_CHART_REF --namespace $NAMESPACE --create-namespace --skip-crds --set image.repository $VESPA_OPERATOR_IMAGE --set image.tag $VESPA_VERSION +``` + +Ensure that the `Deployment` resource was successfully created, and that the `Vespa Operator` Pod is running. + +## Deploy a VespaSet + +To set up a `dev` environment in Vespa on Kubernetes, refer to the example on the [Setup Dev Environment](/en/operations/kubernetes/deployment/dev-mode) page. + +A `VespaSet` is a quorum of [ConfigServer](/en/operations/self-managed/configuration-server) Pods that manage the lifecycle of Vespa applications. Several examples of `VespaSet` resources are provided in the Helm Chart `samples` directory. An example of a `VespaSet` for an archetypical [Amazon Elastic Kubernetes Service](https://aws.amazon.com/eks/) (EKS) setup is shown below. + +```bash +# vespaset sample for EKS +$ cat > vespaset.yaml < vespaset.yaml < +``` + +## Deploy a Vespa Application + +A Vespa application can be deployed through the ConfigServers' ingress endpoint once a quorum has been met. Refer to the [Vespa Sample Applications](https://github.com/vespa-engine/sample-apps) to get started. In the following example, we will use the [Album Recommendation](https://github.com/vespa-engine/sample-apps/tree/master/album-recommendation) sample application. + +Set up the Vespa CLI to download the Album Recommendation sample to a directory. + +```bash +$ vespa clone album-recommendation myapp && cd myapp +``` + +The `Node` resources must be specified for any application package is deployed on Vespa on Kubernetes. These will directly translate to Kubernetes container [resource requests and limits](https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/). In a default deployment without any [PodTemplate](/en/operations/kubernetes/custom-overrides-podtemplate) overrides, the requests will equal the limits for a container. + +Modify the container and content cluster specifications in the application package, as shown below: + +```xml + + + + + + + + + + + + + + 2 + + + + + + + + + +``` + +Enable port-forwarding from the ConfigServer's ingress port `19071` to your local port `19071`. Any ConfigServer Pod can be used. + +```bash +$ vespa config set target local +$ kubectl -n $NAMESPACE port-forward pod/cfg-1 19071:19071 +``` + +Deploy and activate the application. + +```bash +$ vespa prepare --target local +$ while ! vespa --target local activate; do sleep 1; done +``` + +The ConfigServers will create the Container, Content, and Cluster-Controller Pods as specified in the application package. The deployment is considered complete once all Pods show the phase `RUNNING` in the `VespaSet` status. + +Port-forwarding provides a simple way to ingress to the ConfigServer locally. For other ingress options, refer to the [Configuring the External Access Layer](/en/operations/kubernetes/ingress) page. + +## Feed and Query Documents + +Feed and query documents by port-forwarding the ConfigServer ingress port and the Dataplane ingress port, then using the Vespa CLI. + +```bash +$ kubectl -n $NAMESPACE port-forward pod/cfg-1 19071:19071 +$ kubectl -n $NAMESPACE port-forward pod/default-100 8080:8080 +$ vespa feed dataset/A-Head-Full-of-Dreams.json +$ vespa query 'yql=select * from music where true limit 1' +``` + +Refer to the [Vespa CLI documentation](/en/reference/clients/vespa-cli/vespa) for the full list of available commands. \ No newline at end of file diff --git a/en/operations/kubernetes/deployment/local-deployment.html b/en/operations/kubernetes/deployment/local-deployment.html deleted file mode 100644 index df230229b7..0000000000 --- a/en/operations/kubernetes/deployment/local-deployment.html +++ /dev/null @@ -1,101 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: Deploy Vespa Locally -applies_to: enterprise ---- - -

    - Vespa on Kubernetes can be deployed locally using MiniKube for development and experimental use-cases. -

    - -

    - Note: This setup is not recommended for production. -

    - -

    - Initialize a Minikube cluster with 8 nodes, each with 4GiB of memory and 2 CPUs. Enable Minikube's image registry - add-on to allow the Minikube nodes to access the Vespa images. In this example, we use podman as the driver. -

    - -
    -$ minikube start --nodes 8 --cpus 2 --memory 4GiB --driver=podman --insecure-registry="192.168.49.0/24"
    -$ minikube addons enable registry
    -
    - -

    - Cache the images provided by our support team into the MiniKube registry. -

    - -
    -$ echo $VESPAAI_REGISTRY_TOKEN | podman login images.ves.pa \
    -  -u "$VESPAAI_REGISTRY_USER" \
    -  --password-stdin
    -
    -$ podman pull images.ves.pa/kubernetes/vespa:$VESPA_VERSION
    -$ podman pull images.ves.pa/kubernetes/operator:$VESPA_VERSION
    -
    - -

    - Then, push the images to the MiniKube registry. The images will then be accessible from - $(minikube ip):5000. -

    - -
    -$ export MINIKUBE_REGISTRY=$(minikube ip)
    -
    -$ podman tag  kubernetes/vespa:$VESPA_VERSION $MINIKUBE_REGISTRY:5000/localhost/kubernetes/vespa:$VESPA_VERSION
    -$ podman push --tls-verify=false $MINIKUBE_REGISTRY:5000/localhost/kubernetes/vespa:$VESPA_VERSION
    -
    -$ podman tag  kubernetes/operator:$VESPA_VERSION $MINIKUBE_REGISTRY:5000/localhost/kubernetes/operator:$VESPA_VERSION
    -$ podman push --tls-verify=false $MINIKUBE_REGISTRY:5000/localhost/kubernetes/operator:$VESPA_VERSION
    -
    - -

    - We will now use the following environment variables for the rest of the guide to refer to the images. -

    - -
    -$ export VESPA_IMAGE=$MINIKUBE_REGISTRY:5000/localhost/kubernetes/vespa
    -$ export VESPA_OPERATOR_IMAGE=$MINIKUBE_REGISTRY:5000/localhost/kubernetes/operator
    -
    - -

    - Then, install the Local Persistent Volume Helm Chart. - This will allow provisioning Persistent Volumes locally, which is required to run Vespa on Kubernetes. Helm will automatically create a StorageClass called local-storage, which should be - used as the StorageClass for subsequent steps. -

    - -
    -$ git clone git@github.com:kubernetes-sigs/sig-storage-local-static-provisioner.git
    -
    -# Install the Helm Chart onto the cluster globally
    -$ cd sig-storage-local-static-provisioner
    -$ helm install -f helm/examples/baremetal-default-storage.yaml local-volume-provisioner --namespace kube-system ./helm/provisioner
    -
    - -

    - Create several usable volumes on each MiniKube Node. We recommend at least 4 per node for a smooth deployment. -

    - -
    -# Create several volumes on each Minikube node.
    -$ for n in minikube minikube-m02 minikube-m03 minikube-m04 minikube-m05 minikube-m06 minikube-m07 minikube-m08; do
    -  echo "==> $n"
    -  minikube ssh -n "$n" -- '
    -    set -e
    -    for i in 1 2 3 4; do
    -      sudo mkdir -p /mnt/disks/vol$i
    -      if ! mountpoint -q /mnt/disks/vol$i; then
    -        sudo mount --bind /mnt/disks/vol$i /mnt/disks/vol$i
    -      fi
    -    done
    -    echo "Mounted:"
    -    mount | grep -E "/mnt/disks/vol[1-4]" || true
    -  '
    -done
    -
    - -

    - Once the images are available in the MiniKube registry, proceed to the Installation guide, - using local-storage as the storageClass and NONE as the endpointType. -

    diff --git a/en/operations/kubernetes/deployment/local-deployment.mdx b/en/operations/kubernetes/deployment/local-deployment.mdx new file mode 100644 index 0000000000..74b0af925d --- /dev/null +++ b/en/operations/kubernetes/deployment/local-deployment.mdx @@ -0,0 +1,80 @@ +--- +title: "Deploy Vespa Locally" +description: "Vespa on Kubernetes can be deployed locally using MiniKube for development and experimental use-cases." +sidebarTitle: "Minikube Setup" +--- + + +**Note:** + +This setup is not recommended for production. + + +Initialize a Minikube cluster with 8 nodes, each with 4GiB of memory and 2 CPUs. Enable Minikube's image registry add-on to allow the Minikube nodes to access the Vespa images. In this example, we use `podman` as the driver. + +```bash +minikube start --nodes 8 --cpus 2 --memory 4GiB --driver=podman --insecure-registry="192.168.49.0/24" +minikube addons enable registry +``` + +Cache the images provided by our support team into the MiniKube registry. + +```bash +echo $VESPAAI_REGISTRY_TOKEN | podman login images.ves.pa \ + -u "$VESPAAI_REGISTRY_USER" \ + --password-stdin + +podman pull images.ves.pa/kubernetes/vespa:$VESPA_VERSION +podman pull images.ves.pa/kubernetes/operator:$VESPA_VERSION +``` + +Then, push the images to the MiniKube registry. The images will then be accessible from `$(minikube ip):5000`. + +```bash +export MINIKUBE_REGISTRY=$(minikube ip) + +podman tag kubernetes/vespa:$VESPA_VERSION $MINIKUBE_REGISTRY:5000/localhost/kubernetes/vespa:$VESPA_VERSION +podman push --tls-verify=false $MINIKUBE_REGISTRY:5000/localhost/kubernetes/vespa:$VESPA_VERSION + +podman tag kubernetes/operator:$VESPA_VERSION $MINIKUBE_REGISTRY:5000/localhost/kubernetes/operator:$VESPA_VERSION +podman push --tls-verify=false $MINIKUBE_REGISTRY:5000/localhost/kubernetes/operator:$VESPA_VERSION +``` + +We will now use the following environment variables for the rest of the guide to refer to the images. + +```bash +export VESPA_IMAGE=$MINIKUBE_REGISTRY:5000/localhost/kubernetes/vespa +export VESPA_OPERATOR_IMAGE=$MINIKUBE_REGISTRY:5000/localhost/kubernetes/operator +``` + +Then, install the [Local Persistent Volume](https://github.com/kubernetes-sigs/sig-storage-local-static-provisioner) Helm Chart. This will allow provisioning Persistent Volumes locally, which is required to run Vespa on Kubernetes. Helm will automatically create a StorageClass called `local-storage`, which should be used as the `StorageClass` for subsequent steps. + +```bash +$ git clone git@github.com:kubernetes-sigs/sig-storage-local-static-provisioner.git + +# Install the Helm Chart onto the cluster globally +$ cd sig-storage-local-static-provisioner +$ helm install -f helm/examples/baremetal-default-storage.yaml local-volume-provisioner --namespace kube-system ./helm/provisioner +``` + +Create several usable volumes on each MiniKube Node. We recommend at least 4 per node for a smooth deployment. + +```bash +# Create several volumes on each Minikube node. +$ for n in minikube minikube-m02 minikube-m03 minikube-m04 minikube-m05 minikube-m06 minikube-m07 minikube-m08; do + echo "==> $n" + minikube ssh -n "$n" -- ' + set -e + for i in 1 2 3 4; do + sudo mkdir -p /mnt/disks/vol$i + if ! mountpoint -q /mnt/disks/vol$i; then + sudo mount --bind /mnt/disks/vol$i /mnt/disks/vol$i + fi + done + echo "Mounted:" + mount | grep -E "/mnt/disks/vol[1-4]" || true + ' +done +``` + +Once the images are available in the MiniKube registry, proceed to the [Installation](/en/operations/kubernetes/deployment/installation) guide, using `local-storage` as the `storageClass` and `NONE` as the `endpointType`. \ No newline at end of file diff --git a/en/operations/kubernetes/deployment/permissions.html b/en/operations/kubernetes/deployment/permissions.html deleted file mode 100644 index bb01eaf4c7..0000000000 --- a/en/operations/kubernetes/deployment/permissions.html +++ /dev/null @@ -1,72 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: Permissions -applies_to: enterprise ---- - -

    - The Vespa Operator requires the following permissions within the namespace. - These permissions are listed by Kubernetes API verbs per resource. -

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    Kubernetes ResourceRequired Permissions
    CustomResourceDefinitionscreate, get, list, watch
    VespaSetget, list, watch, create, update, patch, delete
    VespaSet Subresources - vespasets/status: update, patch
    - vespasets/finalizers: update -
    ConfigMapsget, list, watch, create, update, patch, delete
    Servicesget, list, watch, create, update, patch, delete
    Podsget, list, watch, create, update, patch, delete
    Pod Executionget, create
    Eventscreate, patch
    PersistentVolumeClaimsget, list, watch, create, update, patch, delete
    ServiceAccountsget, list, watch, create, update, patch, delete
    Rolesget, list, watch, create, update, patch, delete
    RoleBindingsget, list, watch, create, update, patch, delete
    diff --git a/en/operations/kubernetes/deployment/permissions.mdx b/en/operations/kubernetes/deployment/permissions.mdx new file mode 100644 index 0000000000..bff9bcfcb5 --- /dev/null +++ b/en/operations/kubernetes/deployment/permissions.mdx @@ -0,0 +1,64 @@ +--- +title: "Permissions" +--- + +The Vespa Operator requires the following permissions within the namespace. These permissions are listed by Kubernetes [API verbs](https://kubernetes.io/docs/reference/access-authn-authz/rbac/) per resource. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Kubernetes ResourceRequired Permissions
    CustomResourceDefinitionscreate, get, list, watch
    VespaSetget, list, watch, create, update, patch, delete
    VespaSet Subresources{`vespasets/status`}: update, patch {`vespasets/finalizers`}: update
    ConfigMapsget, list, watch, create, update, patch, delete
    Servicesget, list, watch, create, update, patch, delete
    Podsget, list, watch, create, update, patch, delete
    Pod Executionget, create
    Eventscreate, patch
    PersistentVolumeClaimsget, list, watch, create, update, patch, delete
    ServiceAccountsget, list, watch, create, update, patch, delete
    Rolesget, list, watch, create, update, patch, delete
    RoleBindingsget, list, watch, create, update, patch, delete
    \ No newline at end of file diff --git a/en/operations/kubernetes/ingress.mdx b/en/operations/kubernetes/ingress.mdx new file mode 100644 index 0000000000..cd5fabfdb4 --- /dev/null +++ b/en/operations/kubernetes/ingress.mdx @@ -0,0 +1,161 @@ +--- +title: "Configure External Access Layer" +--- + +The Vespa Operator automatically provisions Kubernetes `Service` resources to enable external access for feeding and querying data. This behavior is controlled by the `VespaSet` Custom Resource configuration. + +Load balancers are provisioned exclusively for Container clusters. Content clusters communicate internally and do not require external load balancing services. The type of service provisioned is determined by the `spec.ingress.endpointType` field in the `VespaSet`. + +## Supported Endpoint Types + +The operator supports four endpoint types to cover different infrastructure requirements. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Endpoint TypeKubernetes Service TypeUse Case
    {`LOAD_BALANCER`}{`LoadBalancer`}Provision the cloud-native (AWS, GCP, Azure) load-balancer.
    {`NODE_PORT`}{`NodePort`}Expose a static port across every worker node, allowing external traffic to access the cluster from any node's IP.
    {`CLUSTER_IP`}{`ClusterIP`}Each Container Pod will expose an internal IP address. Should not be used for production use-cases.
    {`NONE`}N/AAn external access layer will not be provisioned. Custom networking setups (Istio, Ingress Controllers) where no automatic service is desired.
    + +## LOAD_BALANCER + +This is the recommended configuration for production deployments on cloud providers (EKS, GKE, AKS). The operator creates a standard Kubernetes `LoadBalancer` service, triggering the cloud provider to provision a managed load balancer (e.g., AWS NLB). + +**Configuration:** + +```bash +ingress: + endpointType: LOAD_BALANCER +``` + +On AWS, the ConfigServer automatically applies the annotation `service.beta.kubernetes.io/aws-load-balancer-internal: "true"` to all Container pods. This provisions an **internal** Network Load Balancer (NLB) accessible only within the VPC where the EKS cluster nodes reside. + +## NODE_PORT + +The `NODE_PORT` type exposes the Vespa container cluster on a specific port (range 30000-32767) across all Kubernetes worker nodes. + +**Configuration:** + +```bash +ingress: + endpointType: NODE_PORT +``` + +When this option is set, Kubernetes opens a static port on every worker node. External traffic can reach the application via `:`. Note that unlike `LOAD_BALANCER`, this does not provide health checks at the entry point level. If a worker node with a connection crashes, the connection will simply time out or fail. This additionally requires all worker nodes to expose an External IP. + +To use the `NODE_PORT` service, find the assigned port. + +```bash +$ kubectl get service lb-default -n $NAMESPACE + +NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE +lb-default NodePort 10.100.150.25 80:31942/TCP 5m +``` + +Get the list of nodes and look for their External IP addresses. + +```bash +$ kubectl get nodes -o wide + +NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION +ip-192-168-3-50.us-east-2.compute.internal Ready 10d v1.27.3-eks-a5565ad 192.168.3.50 18.221.100.45 Amazon Linux 2 5.10.184-175.731.amzn2.x86_64 +ip-192-168-3-51.us-east-2.compute.internal Ready 10d v1.27.3-eks-a5565ad 192.168.3.51 3.142.200.10 Amazon Linux 2 5.10.184-175.731.amzn2.x86_64 +``` + +Choose any External IP and combine the IP and port to access the service. + +```js expandable +$ curl http://18.221.100.45:31942/state/v1/health + +{ + "time" : 1769981985754, + "status" : { + "code" : "up" + }, + "metrics" : { + "snapshot" : { + "from" : 1.769981924895E9, + "to" : 1.769981984895E9 + }, + "values" : [ { + "name" : "requestsPerSecond", + "values" : { + "count" : 19, + "rate" : 0.31666666666666665 + } + }, { + "name" : "latencySeconds", + "values" : { + "average" : 0.009578947368421053, + "sum" : 0.182, + "count" : 19, + "last" : 0.003, + "max" : 0.057, + "min" : 0.0, + "rate" : 0.31666666666666665 + } + } ] + } +} +``` + +## CLUSTER\_IP + +This type restricts access to within the Kubernetes cluster. It provides a stable internal IP and DNS name (e.g., `lb-default.vespa.svc.cluster.local`) but assigns no external IP. + +**Configuration:** + +```bash +ingress: + endpointType: CLUSTER_IP +``` + +The `CLUSTER_IP` service is ideal for architectures where the clients (e.g., front-end applications or ingestion services) run inside the same Kubernetes cluster as Vespa. + +## NONE + +This option disables automatic Service provisioning. Use this if you intend to manually define `Ingress` resources, use a Service Mesh (like Istio or Linkerd), or have complex networking requirements not covered by the standard types. + +**Configuration:** + +```bash +ingress: + endpointType: NONE +``` + +## Traffic Routing & Labeling + +To ensure zero-downtime deployments, the ConfigServer manages traffic routing dynamically using Kubernetes labels. The created Services use the selector `vespa.ai/tenant-lb: backend`. When the Pod is provisioned, these labels are automatically attached. + +During a rolling upgrade, the label is removed from the terminating Pod(s) before they are shut down. This provides a window for the remaining traffic to drain before the Pod is upgraded. + + +**Note**: + +The Service exposes port **80** (plaintext) and **443** (TLS) externally, mapping them to the container's port 4443. + \ No newline at end of file diff --git a/en/operations/kubernetes/configuration/logging.html b/en/operations/kubernetes/logging.mdx similarity index 60% rename from en/operations/kubernetes/configuration/logging.html rename to en/operations/kubernetes/logging.mdx index 956bc8c90c..ebcdf5b5d2 100644 --- a/en/operations/kubernetes/configuration/logging.html +++ b/en/operations/kubernetes/logging.mdx @@ -1,57 +1,40 @@ --- -# Copyright Vespa.ai. All rights reserved. -title: Configure Log Collection -applies_to: enterprise +title: "Configure Log Collection" +description: "Use the Fluent Bit Operator to collect logs and forward them to Grafana Cloud Loki." --- -

    - Use the Fluent Bit Operator to collect logs and forward them to Grafana Cloud Loki. -

    +## 1. Install Fluent Bit Operator -

    1. Install Fluent Bit Operator

    +Install the Fluent Bit Operator in a dedicated `logging` namespace. -

    - Install the Fluent Bit Operator in a dedicated logging namespace. -

    - -
    +```bash
     $ helm repo add fluent https://fluent.github.io/helm-charts
     $ helm repo update
     $ kubectl create namespace logging
     $ helm install fluent-operator fluent/fluent-operator --namespace logging --set operator.logLevel=debug
    -
    +``` -

    - Verify the installation by ensuring the operator Pod is running: -

    +Verify the installation by ensuring the operator Pod is running: -
    +```bash
     $ kubectl get pods -n logging
    -
    +``` -

    2. Configure Loki Credentials

    +## 2. Configure Loki Credentials -

    - To forward logs to Grafana Cloud Loki, you must create a Kubernetes Secret containing your credentials. - Obtain your User ID and API Token from the Grafana Cloud Portal under Connections → Loki. -

    +To forward logs to Grafana Cloud Loki, you must create a Kubernetes Secret containing your credentials. Obtain your User ID and API Token from the Grafana Cloud Portal under *Connections → Loki*. -
    +```bash
     $ kubectl create secret generic grafana-cloud-loki -n logging --from-literal=username=$USER_ID --from-literal=password=$API_TOKEN
    -
    +``` -

    3. Deploy Fluent Bit Configuration

    +## 3. Deploy Fluent Bit Configuration -

    - The Fluent Operator uses Custom Resources to define the logging pipeline. - The following configuration sets up a ClusterInput to tail container logs, a ClusterFilter to add Kubernetes metadata, and a ClusterOutput to ship logs to Loki. -

    +The Fluent Operator uses Custom Resources to define the logging pipeline. The following configuration sets up a **ClusterInput** to tail container logs, a **ClusterFilter** to add Kubernetes metadata, and a **ClusterOutput** to ship logs to Loki. -

    - Save the following as fluentbit-logging.yaml. Note: Replace logs-prod-006.grafana.net with your specific Loki endpoint. -

    +Save the following as `fluentbit-logging.yaml`. **Note:** Replace `logs-prod-006.grafana.net` with your specific Loki endpoint. -
    +```yaml expandable
         apiVersion: fluentbit.fluent.io/v1alpha2
     kind: ClusterFluentBitConfig
     metadata:
    @@ -137,35 +120,27 @@ 

    3. Deploy Fluent Bit Configuration

    image: ghcr.io/fluent/fluent-operator/fluent-bit:3.1.4 tolerations: - operator: Exists -
    +``` -

    - Apply the configuration to your cluster: -

    +Apply the configuration to your cluster: -
    +```bash
     $ kubectl apply -f fluentbit-logging.yaml
    -
    +``` -

    4. Query Logs

    +## 4. Query Logs -

    - Once deployed, Fluent Bit will run as a DaemonSet on every node. You can query the logs using LogQL in Grafana Explore. -

    +Once deployed, Fluent Bit will run as a `DaemonSet` on every node. You can query the logs using LogQL in Grafana Explore. -

    - Check the Fluent Bit logs to ensure there are no authentication errors (HTTP 401): -

    +Check the Fluent Bit logs to ensure there are no authentication errors (HTTP 401): -
    +```bash
     $ kubectl logs -n logging -l app.kubernetes.io/name=fluent-bit --tail=50
    -
    +``` -

    - Use these LogQL queries to inspect Vespa components specifically: -

    +Use these LogQL queries to inspect Vespa components specifically: -
    +```bash
     # Filter logs for the Config Server
     {cluster="minikube", namespace_name="default", pod_name=~"cfg-.*"}
     
    @@ -174,4 +149,4 @@ 

    4. Query Logs

    # Search for errors across all Vespa pods {cluster="minikube"} |= "error" -
    \ No newline at end of file +``` \ No newline at end of file diff --git a/en/operations/kubernetes/monitoring.mdx b/en/operations/kubernetes/monitoring.mdx new file mode 100644 index 0000000000..26c1f7b079 --- /dev/null +++ b/en/operations/kubernetes/monitoring.mdx @@ -0,0 +1,194 @@ +--- +title: "Monitor a Vespa on Kubernetes Deployment" +--- + +Use the Prometheus Operator to collect metrics from a Vespa on Kubernetes deployment. This guide covers the installation of the monitoring stack, configuration of `PodMonitor` resources for Vespa components, and forwarding metrics to Grafana Cloud. + +## Prerequisites + +- A Kubernetes cluster (EKS, GKE, AKS, or Minikube). +- [Helm CLI](https://helm.sh/docs/intro/install/) +- Kubernetes Command Line Tool ([kubectl](https://kubernetes.io/docs/reference/kubectl/)) +- A Grafana Cloud account + +## 1. Install Prometheus Operator + +The recommended way to install Prometheus on Kubernetes is via the [kube-prometheus-stack](https://github.com/prometheus-community/helm-charts/tree/main/charts/kube-prometheus-stack) Helm chart. Add the repository and create a monitoring namespace. + +```bash +$ helm repo add prometheus-community https://prometheus-community.github.io/helm-charts +$ helm repo update +$ kubectl create namespace monitoring +``` + +### Configure Grafana Cloud Credentials + +If you intend to forward metrics to Grafana Cloud, create a Kubernetes Secret with your credentials. Retrieve your **Instance ID** (User) and **API Token** (Password) from the Grafana Cloud Portal under *Configure Prometheus*. + +```bash +$ kubectl create secret generic grafana-cloud-prometheus -n monitoring --from-literal=username=$INSTANCE_ID --from-literal=password=$API_TOKEN +``` + +### Configure Helm Values + +Create a `prometheus-values.yaml` file. This configuration enables remote writing to Grafana Cloud, configures the Prometheus Operator to select all `PodMonitors`, and disables the local Grafana instance. + +```yaml expandable +prometheus: + prometheusSpec: + # Allow Prometheus to discover PodMonitors in other namespaces + podMonitorSelectorNilUsesHelmValues: false + serviceMonitorSelectorNilUsesHelmValues: false + + # Remote write configuration for Grafana Cloud + remoteWrite: + - url: [https://prometheus-prod-XX-prod-XX.grafana.net/api/prom/push](https://prometheus-prod-XX-prod-XX.grafana.net/api/prom/push) + basicAuth: + username: + name: grafana-cloud-prometheus + key: username + password: + name: grafana-cloud-prometheus + key: password + writeRelabelConfigs: + - sourceLabels: [__address__] + targetLabel: cluster + replacement: my-cluster-name + +# Disable local Grafana +grafana: + enabled: false + +# Enable Alertmanager and Kube State Metrics +alertmanager: + enabled: true + kube-state-metrics: + enabled: true +``` + +Install the stack using Helm: + +```bash +$ helm install prometheus prometheus-community/kube-prometheus-stack --namespace monitoring --values prometheus-values.yaml +``` + +## 2. Configure PodMonitors + +Vespa exposes metrics on specific ports that differ from standard web traffic ports. We use the `PodMonitor` Custom Resource to define how Prometheus should scrape these endpoints. + +### Monitor ConfigServer Pods + +ConfigServers expose metrics on port **19071** at the path `/configserver-metrics`. Apply the following configuration to scrape these metrics. + +```yaml expandable +apiVersion: monitoring.coreos.com/v1 +kind: PodMonitor +metadata: + name: vespa-configserver + namespace: $NAMESPACE + labels: + release: prometheus # Required to be picked up by the operator +spec: + selector: + matchLabels: + app: vespa-configserver + podMetricsEndpoints: + - targetPort: 19071 + path: /configserver-metrics + interval: 30s + scheme: http + params: + format: ['prometheus'] + relabelings: + # Map Kubernetes pod name to the 'pod' label + - sourceLabels: [__meta_kubernetes_pod_name] + targetLabel: pod + - targetLabel: vespa_role + replacement: configserver +``` + +### Monitor Application Pods + +Container and Content Pods expose metrics on the state API port **19092** at `/prometheus/v1/values`. The following example defines a PodMonitor for Vespa application pods. + +```yaml expandable +apiVersion: monitoring.coreos.com/v1 +kind: PodMonitor +metadata: + name: vespa-application + namespace: default + labels: + release: prometheus +spec: + selector: + matchExpressions: + # Selects pods that are part of a Vespa application (feed, query, content) + - key: vespa.ai/cluster-name + operator: Exists + podMetricsEndpoints: + - targetPort: 19092 + path: /prometheus/v1/values + interval: 30s + scheme: http + relabelings: + - sourceLabels: [__meta_kubernetes_pod_name] + targetLabel: pod + - sourceLabels: [__meta_kubernetes_namespace] + targetLabel: namespace + # Extract the role from the pod name or labels if needed + - targetLabel: vespa_role + replacement: node +``` + +## 3. Verify Metrics + +Once the `PodMonitors` are applied, verify that Prometheus is successfully scraping the targets. + +### Check Targets Locally + +Port-forward the Prometheus UI to your local machine: + +```bash +$ kubectl port-forward -n monitoring svc/prometheus-kube-prometheus-prometheus 9090:9090 +``` + +Navigate to [http://localhost:9090/targets](http://localhost:9090/targets). You should see targets named `default/vespa-configserver` and `default/vespa-application` in the **UP** state. + +### Query Metrics + +You can verify the data using PromQL queries in the Prometheus UI or Grafana Explore: + +```js +# Check availability of Config Servers +up{vespa_role="configserver"} + +# Retrieve average maintenance duration +vespa_maintenance_duration_average + +# List all metrics coming from Vespa +{job=~"default/vespa-.*"} +``` + +## Troubleshooting + +**Targets show `No active targets`**: + +This indicates the `PodMonitor` selector does not match any Pods. Verify the labels on your Vespa pods: + +```bash +$ kubectl get pods -n $NAMESPACE --show-labels +``` + +Ensure the `selector.matchLabels` in your `PodMonitor` YAML matches the labels shown in the output above. + +**Targets are in `DOWN` state**: + +This usually means Prometheus cannot reach the metric endpoint. Verify that the metrics are exposed on the expected port by running a curl command from within the cluster: + +```bash +$ kubectl run curl-test -n $NAMESPACE --image=curlimages/curl -it --rm -- curl http://cfg-0.$NAMESPACE.svc.cluster.local:19071/configserver-metrics?format=prometheus +``` + +**Network Policies**: + +If you use `NetworkPolicy` to restrict traffic, ensure you have a policy allowing ingress traffic from the `monitoring` namespace to the `$NAMESPACE` namespace on ports 19071 and 19092. \ No newline at end of file diff --git a/en/operations/kubernetes/operations/delete-vespaset.html b/en/operations/kubernetes/operations/delete-vespaset.html deleted file mode 100644 index 2c4f16c719..0000000000 --- a/en/operations/kubernetes/operations/delete-vespaset.html +++ /dev/null @@ -1,119 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: Delete a VespaSet -applies_to: enterprise ---- - -

    - This page provides instructions for deleting a VespaSet. -

    - -

    - The ConfigServer and Application Pods use Kubernetes PreStop Hooks - to prevent their immediate removal when evicted voluntarily or deleted involuntarily. - In production cases, these finalizers are paramount for ensuring proper data redistribution between the Content Pods. However, - they also have the adverse side effect of making Vespa difficult to fully uninstall. -

    - -

    - Follow the steps below to fully uninstall your setup. This example assumes the Pods were created for the - Album Recommendation sample application, - the Pods are scheduled in the $NAMESPACE namespace, and a VespaSet called vespaset-test was deployed. -

    - -{% include important.html content='These instructions should not be run on production serving environments.' %} - -

    Steps

    - -

    Delete Pods

    - -

    - Run vespa-stop-configserver on all ConfigServer Pods. This will ensure that any finalizers will exit immediately, since all - finalizers ultimately route to a ConfigServer. -

    - -
    -$ kubectl exec -n $NAMESPACE cfg-1 -- vespa-stop-configserver
    -$ kubectl exec -n $NAMESPACE cfg-2 -- vespa-stop-configserver
    -$ kubectl exec -n $NAMESPACE cfg-3 -- vespa-stop-configserver
    -
    - -

    - Run vespa-stop-services on all Application Pods. -

    - -
    -$ kubectl exec -n $NAMESPACE default-100 -- vespa-stop-services
    -$ kubectl exec -n $NAMESPACE default-101 -- vespa-stop-services
    -$ kubectl exec -n $NAMESPACE music-102 -- vespa-stop-services
    -$ kubectl exec -n $NAMESPACE music-103 -- vespa-stop-services
    -$ kubectl exec -n $NAMESPACE cluster-controllers-104 -- vespa-stop-services
    -$ kubectl exec -n $NAMESPACE cluster-controllers-105 -- vespa-stop-services
    -$ kubectl exec -n $NAMESPACE cluster-controllers-106 -- vespa-stop-services
    -
    - -

    - Delete all the ConfigServer and Application Pods. The finalizers will exit immediately. -

    - -
    -$ kubectl delete pod -n $NAMESPACE cfg-1 --grace-period=0 --force --ignore-not-found
    -$ kubectl delete pod -n $NAMESPACE cfg-2 --grace-period=0 --force --ignore-not-found
    -$ kubectl delete pod -n $NAMESPACE cfg-3 --grace-period=0 --force --ignore-not-found
    -$ kubectl delete pod -n $NAMESPACE default-100 --grace-period=0 --force --ignore-not-found
    -$ kubectl delete pod -n $NAMESPACE default-101 --grace-period=0 --force --ignore-not-found
    -$ kubectl delete pod -n $NAMESPACE music-102 --grace-period=0 --force --ignore-not-found
    -$ kubectl delete pod -n $NAMESPACE music-103 --grace-period=0 --force --ignore-not-found
    -$ kubectl delete pod -n $NAMESPACE cluster-controllers-104 --grace-period=0 --force --ignore-not-found
    -$ kubectl delete pod -n $NAMESPACE cluster-controllers-105 --grace-period=0 --force --ignore-not-found
    -$ kubectl delete pod -n $NAMESPACE cluster-controllers-106 --grace-period=0 --force --ignore-not-found
    -
    - -

    Delete Persistent Volume Claims

    - -

    - Delete all PersistentVolumeClaims (PVCs) from the namespace. This should be performed after all Pods have been - deleted, to ensure PVC deletion does not hang on a Pod binding. -

    - -
    -$ kubectl delete pvc -n $NAMESPACE cfg-1-data --ignore-not-found
    -$ kubectl delete pvc -n $NAMESPACE cfg-2-data --ignore-not-found
    -$ kubectl delete pvc -n $NAMESPACE cfg-3-data --ignore-not-found
    -$ kubectl delete pvc -n $NAMESPACE default-100-data --ignore-not-found
    -$ kubectl delete pvc -n $NAMESPACE default-101-data --ignore-not-found
    -$ kubectl delete pvc -n $NAMESPACE music-102-data --ignore-not-found
    -$ kubectl delete pvc -n $NAMESPACE music-103-data --ignore-not-found
    -
    - -

    Delete ConfigMaps

    - -

    - Delete all remaining ConfigMaps in the namespace. -

    - -
    -$ kubectl delete configmap -n $NAMESPACE vespa-config --ignore-not-found
    -
    - -

    Delete Services

    - -

    - Delete any Services and other networking components that may have been setup by the operator. -

    - -
    -$ kubectl delete svc -n $NAMESPACE x --ignore-not-found
    -$ kubectl delete svc -n $NAMESPACE cfg-internal --ignore-not-found
    -
    - -

    Delete VespaSet

    - -

    - Delete the VespaSet resource. With all Pods and services already removed, the operator's finalizer - will exit immediately. -

    - -
    -$ kubectl delete vespaset -n $NAMESPACE vespaset-test --ignore-not-found
    -
    diff --git a/en/operations/kubernetes/operations/delete-vespaset.mdx b/en/operations/kubernetes/operations/delete-vespaset.mdx new file mode 100644 index 0000000000..29ff4d17fb --- /dev/null +++ b/en/operations/kubernetes/operations/delete-vespaset.mdx @@ -0,0 +1,92 @@ +--- +title: "Delete a VespaSet" +sidebarTitle: "Delete a VespaSet" +--- + +This page provides instructions for deleting a VespaSet. + +The ConfigServer and Application Pods use [Kubernetes PreStop Hooks](https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/) to prevent their immediate removal when evicted voluntarily or deleted involuntarily. In production cases, these finalizers are paramount for ensuring proper data redistribution between the Content Pods. However, they also have the adverse side effect of making Vespa difficult to fully uninstall. + +Follow the steps below to fully uninstall your setup. This example assumes the Pods were created for the [Album Recommendation](https://github.com/vespa-engine/sample-apps/tree/master/album-recommendation) sample application, the Pods are scheduled in the `$NAMESPACE` namespace, and a `VespaSet` called `vespaset-test` was deployed. + + + **Important:** These instructions should not be run on production serving environments. + + +## Steps + +### Delete Pods + +Run `vespa-stop-configserver` on all ConfigServer Pods. This will ensure that any finalizers will exit immediately, since all finalizers ultimately route to a ConfigServer. + +```bash +$ kubectl exec -n $NAMESPACE cfg-1 -- vespa-stop-configserver +$ kubectl exec -n $NAMESPACE cfg-2 -- vespa-stop-configserver +$ kubectl exec -n $NAMESPACE cfg-3 -- vespa-stop-configserver +``` + +Run `vespa-stop-services` on all Application Pods. + +```bash +$ kubectl exec -n $NAMESPACE default-100 -- vespa-stop-services +$ kubectl exec -n $NAMESPACE default-101 -- vespa-stop-services +$ kubectl exec -n $NAMESPACE music-102 -- vespa-stop-services +$ kubectl exec -n $NAMESPACE music-103 -- vespa-stop-services +$ kubectl exec -n $NAMESPACE cluster-controllers-104 -- vespa-stop-services +$ kubectl exec -n $NAMESPACE cluster-controllers-105 -- vespa-stop-services +$ kubectl exec -n $NAMESPACE cluster-controllers-106 -- vespa-stop-services +``` + +Delete all the ConfigServer and Application Pods. The finalizers will exit immediately. + +```bash +$ kubectl delete pod -n $NAMESPACE cfg-1 --grace-period=0 --force --ignore-not-found +$ kubectl delete pod -n $NAMESPACE cfg-2 --grace-period=0 --force --ignore-not-found +$ kubectl delete pod -n $NAMESPACE cfg-3 --grace-period=0 --force --ignore-not-found +$ kubectl delete pod -n $NAMESPACE default-100 --grace-period=0 --force --ignore-not-found +$ kubectl delete pod -n $NAMESPACE default-101 --grace-period=0 --force --ignore-not-found +$ kubectl delete pod -n $NAMESPACE music-102 --grace-period=0 --force --ignore-not-found +$ kubectl delete pod -n $NAMESPACE music-103 --grace-period=0 --force --ignore-not-found +$ kubectl delete pod -n $NAMESPACE cluster-controllers-104 --grace-period=0 --force --ignore-not-found +$ kubectl delete pod -n $NAMESPACE cluster-controllers-105 --grace-period=0 --force --ignore-not-found +$ kubectl delete pod -n $NAMESPACE cluster-controllers-106 --grace-period=0 --force --ignore-not-found +``` + +### Delete Persistent Volume Claims + +Delete all PersistentVolumeClaims (PVCs) from the namespace. This should be performed after all Pods have been deleted, to ensure PVC deletion does not hang on a Pod binding. + +```bash +$ kubectl delete pvc -n $NAMESPACE cfg-1-data --ignore-not-found +$ kubectl delete pvc -n $NAMESPACE cfg-2-data --ignore-not-found +$ kubectl delete pvc -n $NAMESPACE cfg-3-data --ignore-not-found +$ kubectl delete pvc -n $NAMESPACE default-100-data --ignore-not-found +$ kubectl delete pvc -n $NAMESPACE default-101-data --ignore-not-found +$ kubectl delete pvc -n $NAMESPACE music-102-data --ignore-not-found +$ kubectl delete pvc -n $NAMESPACE music-103-data --ignore-not-found +``` + +### Delete ConfigMaps + +Delete all remaining ConfigMaps in the namespace. + +```bash +$ kubectl delete configmap -n $NAMESPACE vespa-config --ignore-not-found +``` + +### Delete Services + +Delete any Services and other networking components that may have been setup by the operator. + +```bash +$ kubectl delete svc -n $NAMESPACE x --ignore-not-found +$ kubectl delete svc -n $NAMESPACE cfg-internal --ignore-not-found +``` + +### Delete VespaSet + +Delete the `VespaSet` resource. With all Pods and services already removed, the operator's finalizer will exit immediately. + +```bash +$ kubectl delete vespaset -n $NAMESPACE vespaset-test --ignore-not-found +``` \ No newline at end of file diff --git a/en/operations/kubernetes/operations/monitoring.html b/en/operations/kubernetes/operations/monitoring.html deleted file mode 100644 index 058242e139..0000000000 --- a/en/operations/kubernetes/operations/monitoring.html +++ /dev/null @@ -1,235 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: Monitor a Vespa on Kubernetes Deployment -applies_to: enterprise ---- - -

    - Use the Prometheus Operator to collect metrics from a Vespa on Kubernetes deployment. - This guide covers the installation of the monitoring stack, configuration of PodMonitor resources for Vespa components, and forwarding metrics to Grafana Cloud. -

    - -

    Prerequisites

    - -
      -
    • A Kubernetes cluster (EKS, GKE, AKS, or Minikube).
    • -
    • Helm CLI
    • -
    • Kubernetes Command Line Tool (kubectl)
    • -
    • A Grafana Cloud account
    • -
    - -

    1. Install Prometheus Operator

    - -

    - The recommended way to install Prometheus on Kubernetes is via the kube-prometheus-stack Helm chart. - Add the repository and create a monitoring namespace. -

    - -
    -$ helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
    -$ helm repo update
    -$ kubectl create namespace monitoring
    -
    - -

    Configure Grafana Cloud Credentials

    -

    - If you intend to forward metrics to Grafana Cloud, create a Kubernetes Secret with your credentials. - Retrieve your Instance ID (User) and API Token (Password) from the Grafana Cloud Portal under Configure Prometheus. -

    - -
    -$ kubectl create secret generic grafana-cloud-prometheus -n monitoring --from-literal=username=$INSTANCE_ID --from-literal=password=$API_TOKEN
    -
    - -

    Configure Helm Values

    -

    - Create a prometheus-values.yaml file. This configuration enables remote writing to Grafana Cloud, - configures the Prometheus Operator to select all PodMonitors, and disables the local Grafana instance. -

    - -
    -prometheus:
    -  prometheusSpec:
    -  # Allow Prometheus to discover PodMonitors in other namespaces
    -  podMonitorSelectorNilUsesHelmValues: false
    -  serviceMonitorSelectorNilUsesHelmValues: false
    -
    -  # Remote write configuration for Grafana Cloud
    -  remoteWrite:
    -    - url: [https://prometheus-prod-XX-prod-XX.grafana.net/api/prom/push](https://prometheus-prod-XX-prod-XX.grafana.net/api/prom/push)
    -      basicAuth:
    -        username:
    -          name: grafana-cloud-prometheus
    -          key: username
    -        password:
    -          name: grafana-cloud-prometheus
    -          key: password
    -      writeRelabelConfigs:
    -        - sourceLabels: [__address__]
    -          targetLabel: cluster
    -          replacement: my-cluster-name
    -
    -# Disable local Grafana
    -grafana:
    -  enabled: false
    -
    -# Enable Alertmanager and Kube State Metrics
    -alertmanager:
    -  enabled: true
    -  kube-state-metrics:
    -  enabled: true
    -
    - -

    - Install the stack using Helm: -

    - -
    -$ helm install prometheus prometheus-community/kube-prometheus-stack --namespace monitoring --values prometheus-values.yaml
    -
    - -

    2. Configure PodMonitors

    - -

    - Vespa exposes metrics on specific ports that differ from standard web traffic ports. - We use the PodMonitor Custom Resource to define how Prometheus should scrape these endpoints. -

    - -

    Monitor ConfigServer Pods

    -

    - ConfigServers expose metrics on port 19071 at the path /configserver-metrics. - Apply the following configuration to scrape these metrics. -

    - -
    -apiVersion: monitoring.coreos.com/v1
    -kind: PodMonitor
    -metadata:
    -  name: vespa-configserver
    -  namespace: $NAMESPACE
    -  labels:
    -    release: prometheus # Required to be picked up by the operator
    -spec:
    -  selector:
    -    matchLabels:
    -      app: vespa-configserver
    -  podMetricsEndpoints:
    -    - targetPort: 19071
    -      path: /configserver-metrics
    -      interval: 30s
    -      scheme: http
    -      params:
    -        format: ['prometheus']
    -      relabelings:
    -        # Map Kubernetes pod name to the 'pod' label
    -        - sourceLabels: [__meta_kubernetes_pod_name]
    -          targetLabel: pod
    -        - targetLabel: vespa_role
    -          replacement: configserver
    -
    - -

    Monitor Application Pods

    -

    - Container and Content Pods expose metrics on the state API port 19092 at /prometheus/v1/values. - The following example defines a PodMonitor for Vespa application pods. -

    - -
    -apiVersion: monitoring.coreos.com/v1
    -kind: PodMonitor
    -metadata:
    -  name: vespa-application
    -  namespace: default
    -  labels:
    -    release: prometheus
    -spec:
    -  selector:
    -    matchExpressions:
    -      # Selects pods that are part of a Vespa application (feed, query, content)
    -      - key: vespa.ai/cluster-name
    -        operator: Exists
    -  podMetricsEndpoints:
    -    - targetPort: 19092
    -      path: /prometheus/v1/values
    -      interval: 30s
    -      scheme: http
    -      relabelings:
    -        - sourceLabels: [__meta_kubernetes_pod_name]
    -          targetLabel: pod
    -        - sourceLabels: [__meta_kubernetes_namespace]
    -          targetLabel: namespace
    -        # Extract the role from the pod name or labels if needed
    -        - targetLabel: vespa_role
    -          replacement: node
    -
    - -

    3. Verify Metrics

    - -

    - Once the PodMonitors are applied, verify that Prometheus is successfully scraping the targets. -

    - -

    Check Targets Locally

    -

    - Port-forward the Prometheus UI to your local machine: -

    - -
    -$ kubectl port-forward -n monitoring svc/prometheus-kube-prometheus-prometheus 9090:9090
    -
    - -

    - Navigate to http://localhost:9090/targets. You should see targets named default/vespa-configserver and default/vespa-application in the UP state. -

    - -

    Query Metrics

    -

    - You can verify the data using PromQL queries in the Prometheus UI or Grafana Explore: -

    - -
    -# Check availability of Config Servers
    -up{vespa_role="configserver"}
    -
    -# Retrieve average maintenance duration
    -vespa_maintenance_duration_average
    -
    -# List all metrics coming from Vespa
    -{job=~"default/vespa-.*"}
    -
    - -

    Troubleshooting

    - -

    -Targets show No active targets: -

    -

    - This indicates the PodMonitor selector does not match any Pods. - Verify the labels on your Vespa pods: -

    -
    -$ kubectl get pods -n $NAMESPACE --show-labels
    -
    -

    - Ensure the selector.matchLabels in your PodMonitor YAML matches the labels shown in the output above. -

    - -

    -Targets are in DOWN state: -

    -

    - This usually means Prometheus cannot reach the metric endpoint. - Verify that the metrics are exposed on the expected port by running a curl command from within the cluster: -

    -
    -$ kubectl run curl-test -n $NAMESPACE --image=curlimages/curl -it --rm -- curl http://cfg-0.$NAMESPACE.svc.cluster.local:19071/configserver-metrics?format=prometheus
    -
    - -

    -Network Policies: -

    -

    - If you use NetworkPolicy to restrict traffic, ensure you have a policy allowing ingress traffic - from the monitoring namespace to the $NAMESPACE namespace on ports 19071 and 19092. -

    - diff --git a/en/operations/kubernetes/operations/monitoring.mdx b/en/operations/kubernetes/operations/monitoring.mdx new file mode 100644 index 0000000000..6a9df01a7b --- /dev/null +++ b/en/operations/kubernetes/operations/monitoring.mdx @@ -0,0 +1,195 @@ +--- +title: "Monitor a Vespa on Kubernetes Deployment" +sidebarTitle: "Monitor a Vespa on Kubernetes Deployment" +--- + +Use the Prometheus Operator to collect metrics from a Vespa on Kubernetes deployment. This guide covers the installation of the monitoring stack, configuration of `PodMonitor` resources for Vespa components, and forwarding metrics to Grafana Cloud. + +## Prerequisites + +- A Kubernetes cluster (EKS, GKE, AKS, or Minikube). +- [Helm CLI](https://helm.sh/docs/intro/install/) +- Kubernetes Command Line Tool ([kubectl](https://kubernetes.io/docs/reference/kubectl/)) +- A Grafana Cloud account + +## 1. Install Prometheus Operator + +The recommended way to install Prometheus on Kubernetes is via the [kube-prometheus-stack](https://github.com/prometheus-community/helm-charts/tree/main/charts/kube-prometheus-stack) Helm chart. Add the repository and create a monitoring namespace. + +```bash +$ helm repo add prometheus-community https://prometheus-community.github.io/helm-charts +$ helm repo update +$ kubectl create namespace monitoring +``` + +### Configure Grafana Cloud Credentials + +If you intend to forward metrics to Grafana Cloud, create a Kubernetes Secret with your credentials. Retrieve your **Instance ID** (User) and **API Token** (Password) from the Grafana Cloud Portal under _Configure Prometheus_. + +```bash +$ kubectl create secret generic grafana-cloud-prometheus -n monitoring --from-literal=username=$INSTANCE_ID --from-literal=password=$API_TOKEN +``` + +### Configure Helm Values + +Create a `prometheus-values.yaml` file. This configuration enables remote writing to Grafana Cloud, configures the Prometheus Operator to select all `PodMonitors`, and disables the local Grafana instance. + +```yaml expandable +prometheus: + prometheusSpec: + # Allow Prometheus to discover PodMonitors in other namespaces + podMonitorSelectorNilUsesHelmValues: false + serviceMonitorSelectorNilUsesHelmValues: false + + # Remote write configuration for Grafana Cloud + remoteWrite: + - url: [https://prometheus-prod-XX-prod-XX.grafana.net/api/prom/push](https://prometheus-prod-XX-prod-XX.grafana.net/api/prom/push) + basicAuth: + username: + name: grafana-cloud-prometheus + key: username + password: + name: grafana-cloud-prometheus + key: password + writeRelabelConfigs: + - sourceLabels: [__address__] + targetLabel: cluster + replacement: my-cluster-name + +# Disable local Grafana +grafana: + enabled: false + +# Enable Alertmanager and Kube State Metrics +alertmanager: + enabled: true + kube-state-metrics: + enabled: true +``` + +Install the stack using Helm: + +```bash +$ helm install prometheus prometheus-community/kube-prometheus-stack --namespace monitoring --values prometheus-values.yaml +``` + +## 2. Configure PodMonitors + +Vespa exposes metrics on specific ports that differ from standard web traffic ports. We use the `PodMonitor` Custom Resource to define how Prometheus should scrape these endpoints. + +### Monitor ConfigServer Pods + +ConfigServers expose metrics on port **19071** at the path `/configserver-metrics`. Apply the following configuration to scrape these metrics. + +```yaml expandable +apiVersion: monitoring.coreos.com/v1 +kind: PodMonitor +metadata: + name: vespa-configserver + namespace: $NAMESPACE + labels: + release: prometheus # Required to be picked up by the operator +spec: + selector: + matchLabels: + app: vespa-configserver + podMetricsEndpoints: + - targetPort: 19071 + path: /configserver-metrics + interval: 30s + scheme: http + params: + format: ['prometheus'] + relabelings: + # Map Kubernetes pod name to the 'pod' label + - sourceLabels: [__meta_kubernetes_pod_name] + targetLabel: pod + - targetLabel: vespa_role + replacement: configserver +``` + +### Monitor Application Pods + +Container and Content Pods expose metrics on the state API port **19092** at `/prometheus/v1/values`. The following example defines a PodMonitor for Vespa application pods. + +```yaml expandable +apiVersion: monitoring.coreos.com/v1 +kind: PodMonitor +metadata: + name: vespa-application + namespace: default + labels: + release: prometheus +spec: + selector: + matchExpressions: + # Selects pods that are part of a Vespa application (feed, query, content) + - key: vespa.ai/cluster-name + operator: Exists + podMetricsEndpoints: + - targetPort: 19092 + path: /prometheus/v1/values + interval: 30s + scheme: http + relabelings: + - sourceLabels: [__meta_kubernetes_pod_name] + targetLabel: pod + - sourceLabels: [__meta_kubernetes_namespace] + targetLabel: namespace + # Extract the role from the pod name or labels if needed + - targetLabel: vespa_role + replacement: node +``` + +## 3. Verify Metrics + +Once the `PodMonitors` are applied, verify that Prometheus is successfully scraping the targets. + +### Check Targets Locally + +Port-forward the Prometheus UI to your local machine: + +```bash +$ kubectl port-forward -n monitoring svc/prometheus-kube-prometheus-prometheus 9090:9090 +``` + +Navigate to [http://localhost:9090/targets](http://localhost:9090/targets). You should see targets named `default/vespa-configserver` and `default/vespa-application` in the **UP** state. + +### Query Metrics + +You can verify the data using PromQL queries in the Prometheus UI or Grafana Explore: + +```js +# Check availability of Config Servers +up{vespa_role="configserver"} + +# Retrieve average maintenance duration +vespa_maintenance_duration_average + +# List all metrics coming from Vespa +{job=~"default/vespa-.*"} +``` + +## Troubleshooting + +**Targets show `No active targets`**: + +This indicates the `PodMonitor` selector does not match any Pods. Verify the labels on your Vespa pods: + +```bash +$ kubectl get pods -n $NAMESPACE --show-labels +``` + +Ensure the `selector.matchLabels` in your `PodMonitor` YAML matches the labels shown in the output above. + +**Targets are in `DOWN` state**: + +This usually means Prometheus cannot reach the metric endpoint. Verify that the metrics are exposed on the expected port by running a curl command from within the cluster: + +```bash +$ kubectl run curl-test -n $NAMESPACE --image=curlimages/curl -it --rm -- curl http://cfg-0.$NAMESPACE.svc.cluster.local:19071/configserver-metrics?format=prometheus +``` + +**Network Policies**: + +If you use `NetworkPolicy` to restrict traffic, ensure you have a policy allowing ingress traffic from the `monitoring` namespace to the `$NAMESPACE` namespace on ports 19071 and 19092. \ No newline at end of file diff --git a/en/operations/kubernetes/operations/operations.html b/en/operations/kubernetes/operations/operations.html deleted file mode 100644 index e5c134b5ab..0000000000 --- a/en/operations/kubernetes/operations/operations.html +++ /dev/null @@ -1,60 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: Lifecycle Operations for Vespa on Kubernetes -applies_to: enterprise ---- - -

    - The ConfigServer and Vespa Application Pods have built-in resilience and recovery capabilities; they are automatically recovered during failures and gracefully shut down during maintenance or scaling operations to preserve data integrity. -

    - -

    Automatic Recovery

    -

    - Vespa relies on standard Kubernetes controllers to detect and restart crashed Pods. If a container exits unexpectedly (e.g., OOMKilled or application crash), the kubelet will automatically restart it. -

    -

    - However, the ConfigServers track the health history of every Pod. To prevent a "crash loop" from causing cascading failures or constantly churning resources, the system implements a strict throttling mechanism. - The ConfigServers allow a maximum of 2 involuntary Pod disruptions per 24-hour period for a given Vespa Application. If this limit is exceeded, the ConfigServer stops automatically failing these Pods and will - require human intervention to investigate the root cause. -

    - -

    Graceful Shutdown

    -

    - To prevent query failures or data loss during termination, a PreStop Hook is placed on every ConfigServer and Vespa Application Pod. - During a voluntary disruption, this hook ensures that existing traffic is drained and that data is flushed before killing the Pod. -

    - -

    - Two types of disruptions exist in Kubernetes: -

    - - - - - - - - - - - - - - - - - - - - - -
    TypeScenarioBehavior
    Voluntary DisruptionScaling down, rolling upgrades, or node maintenance. - The preStop hook detects a voluntary disruption, stops the Vespa Container cluster from accepting new traffic, flushes in-memory data to disk for Content clusters, and ensures a clean exit before the Pod is deleted. -
    Involuntary DisruptionNode hardware failure, kernel panic, or eviction. - Kubernetes initiates the termination. The preStop hook attempts to run to flush data and close connections. However, if the Pod is lost abruptly. the hook cannot run, and recovery relies on Vespa's data replication. -
    - -

    Pod Disruption Budget

    -

    - Defining a PodDisruptionBudget (PBD) is not supported for Vespa on Kubernetes. The ConfigServers will override any PBD with its own orchestration policy. -

    \ No newline at end of file diff --git a/en/operations/kubernetes/operations/operations.mdx b/en/operations/kubernetes/operations/operations.mdx new file mode 100644 index 0000000000..c2e7bb80ab --- /dev/null +++ b/en/operations/kubernetes/operations/operations.mdx @@ -0,0 +1,79 @@ +--- +title: "Lifecycle Operations for Vespa on Kubernetes" +sidebarTitle: "Operations" +--- +The ConfigServer and Vespa Application Pods have built-in resilience and recovery capabilities; they are automatically recovered during failures and gracefully shut down during maintenance or scaling operations to preserve data integrity. + +### Automatic Recovery + +Vespa relies on standard Kubernetes controllers to detect and restart crashed Pods. If a container exits unexpectedly (e.g., OOMKilled or application crash), the kubelet will automatically restart it. + +However, the ConfigServers track the health history of every Pod. To prevent a "crash loop" from causing cascading failures or constantly churning resources, the system implements a strict throttling mechanism. The ConfigServers allow a maximum of 2 involuntary Pod disruptions per 24-hour period for a given Vespa Application. If this limit is exceeded, the ConfigServer stops automatically failing these Pods and will require human intervention to investigate the root cause. + +### Graceful Shutdown + +To prevent query failures or data loss during termination, a [PreStop Hook](https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/) is placed on every ConfigServer and Vespa Application Pod. During a voluntary disruption, this hook ensures that existing traffic is drained and that data is flushed before killing the Pod. + +Two types of disruptions exist in Kubernetes: + + + + + + + + + + + + + + + + + + + + + +
    TypeScenarioBehavior
    **Voluntary Disruption**Scaling down, rolling upgrades, or node maintenance.The preStop hook detects a voluntary disruption, stops the Vespa Container cluster from accepting new traffic, flushes in-memory data to disk for Content clusters, and ensures a clean exit before the Pod is deleted.
    **Involuntary Disruption**Node hardware failure, kernel panic, or eviction.Kubernetes initiates the termination. The preStop hook attempts to run to flush data and close connections. However, if the Pod is lost abruptly. the hook cannot run, and recovery relies on Vespa's data replication.
    + +### Pod Disruption Budget + +Defining a `PodDisruptionBudget` (PBD) is not supported for Vespa on Kubernetes. The ConfigServers will override any PBD with its own orchestration policy. + +### Application Pod Resources + +For Vespa Application Pods, the resources for each Pod, the number of Pods in a Vespa cluster, and the group configuration can be updated through the `` element in the application package. Refer to the [specification](/en/reference/applications/services/services) for more details. + +### ConfigServer Pod Resources + +ConfigServer Pod resources can be configured by overriding the `vespa` container's resource specification via the PodTemplate in the `VespaSet`. The Config Server deduces its heap size from the Pod cgroup limits, which are derived from the `requests` and `limits` set on the Pod. Setting requests and limits to the same value is recommended to ensure the heap size is deduced correctly. + +Horizontally scaling the replica count for ConfigServer Pods is not supported. + +```bash +apiVersion: k8s.ai.vespa/v1 +kind: VespaSet +metadata: + name: sample-vespaset +spec: + configServer: + image: "$VESPA_IMAGE" + storageClass: "gp3" + podTemplate: + spec: + containers: + - name: vespa + resources: + requests: + cpu: "4" + memory: "8Gi" + limits: + cpu: "4" + memory: "8Gi" +``` + +### Autoscaling + +Vespa on Kubernetes provides autoscaling through ranges specified in the `resource` elements in the application package. Refer to the [Autoscaling](/en/operations/autoscaling) guide for more details. \ No newline at end of file diff --git a/en/operations/kubernetes/operations/resource-scaling.html b/en/operations/kubernetes/operations/resource-scaling.html deleted file mode 100644 index 2618ea059e..0000000000 --- a/en/operations/kubernetes/operations/resource-scaling.html +++ /dev/null @@ -1,114 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: Resource Management and Scaling -applies_to: enterprise ---- - -

    - Resources, namely CPU, memory (RAM), and disk (Persistent Volume) are specified through a combination of the - Application Package and VespaSet CRD. We've maintained the familiar notion of resources - from the Application Package, while allowing Kubernetes-specific overrides when necessary. -

    - -

    Vespa Application

    - -

    - Resources for the Vespa Application Pods, including the Container, Content, and Cluster Controller Pods, are configured through the - Application Package. The requests from the Application - Package will be directly translated to [Kubernetes request and limit](https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/#requests-and-limits) constraints for - the Application Pods. -

    - -

    - For CPU, a whole number will translate to a virtual core, and a float will translate to a fractional CPU request. Memory values will translate - to a Gi suffix, and disk values will translate to a G suffix. The disk value will be rounded to the nearest whole integer if a float is provided. -

    - -

    - For example: -

    - -
    -<?xml version="1.0" encoding="UTF-8"?>
    -<services version="1.0" xmlns:deploy="vespa">
    -
    -    <container id="default" version="1.0">
    -        <document-api/>
    -        <nodes count="2" exclusive="true">
    -            <resources vcpu="1" memory="2Gb" disk="10Gb" />
    -        </nodes>
    -        <search/>
    -    </container>
    -
    -    <content id="documentation" version="1.0">
    -        <min-redundancy>2</min-redundancy>
    -        <documents>
    -            <document type="music" mode="index" />
    -        </documents>
    -        <nodes count="2">
    -            <resources vcpu="1" memory="2Gb" disk="10Gb"   />
    -        </nodes>
    -    </content>
    -</services>
    -
    - -

    This application package will translate to the following Kubernetes requests and limits for the Container and Content Pod. The Operator will set the request and limit resources equally, which is often safer for latency-sensitive JVM services.

    - -
    -$ kubectl get pod default-100 -n test -o jsonpath='{range .spec.containers[?(@.name=="vespa")]}requests: {.resources.requests}{"\n"}limits: {.resources.limits}{"\n"}{end}'
    -
    -requests: {"cpu":"1","memory":"2Gi"}
    -limits: {"cpu":"1","memory":"2Gi"}
    -
    - -

    - Additionally, it will create a PersistentVolumeClaim (PVC) requesting 10G of storage. -

    - -

    - For all practical purposes, the Operator will impose a hard floor of CPU=0.5, memory=1Gi, and disk=10Gb. -

    - -

    ConfigServer Resources

    - -

    - ConfigServer Pod resources can be configured by overriding the vespa container's resource specification via PodTemplate Overrides. The ConfigServer deduces its heap size from the Pod cgroup limits, which are derived from the requests and limits set on the Pod. - Setting requests and limits to the same value is recommended. Horizontally scaling the replica count for ConfigServer Pods is not currently supported. -

    - -

    - For example, the following specification in VespaSet.spec.configServer.podTemplate will tune the ConfigServer's request and limit resources to - CPU=4 and memory=8Gi. -

    - -
    -apiVersion: k8s.ai.vespa/v1
    -kind: VespaSet
    -metadata:
    -  name: sample-vespaset
    -spec:
    -  configServer:
    -    podTemplate:
    -      spec:
    -        containers:
    -          - name: vespa
    -            resources:
    -              requests:
    -                cpu: "4"
    -                memory: "8Gi"
    -              limits:
    -                cpu: "4"
    -                memory: "8Gi"
    -
    - -

    Autoscaling

    - -

    - Vespa on Kubernetes supports autoscaling through ranges specified in the resource elements in the application package. - The Vespa Autoscaler acts on metrics that are exposed via the /metrics/v2/ API. This does not require the Kubernetes Metrics - Server to be enabled. -

    - -

    - Refer to the Autoscaling guide for more details. -

    diff --git a/en/operations/kubernetes/operations/upgrades.html b/en/operations/kubernetes/operations/upgrades.html deleted file mode 100644 index 1cc8978e23..0000000000 --- a/en/operations/kubernetes/operations/upgrades.html +++ /dev/null @@ -1,270 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: Upgrade Vespa on Kubernetes -applies_to: enterprise ---- - -

    - Vespa on Kubernetes supports zero-downtime rolling upgrades. An upgrade involves upgrading the - vespa-operator via the Helm chart and the ConfigServer and Application (Container and Content) Pods through - the VespaSet resource. -

    - -

    - We do not support version drift between the vespa-operator and the VespaSet. Accordingly, - upgrades should be planned so that all components are updated together. To ensure availability, they should be performed - in the order as shown in this guide. -

    - -

    Update the CRD

    - -

    - Some upgrades may introduce changes to the VespaSet CRD definition. These changes should be - applied to the cluster before performing the upgrade. As a rule of thumb, we recommend executing this before - every upgrade procedure. -

    - -

    - Helm does not manage the lifecycle of the CRD after it is installed (see the official documentation). - As a result, CRD updates must be handled manually. Given the official Helm Chart for Vespa on Kubernetes, this can be performed by extracting the - CRD definition from the OCI package and applying it directly using kubectl. -

    - -
    -$ helm show crds $HELM_CHART_REF --version $VESPA_VERSION > vespaset-crd.yaml
    -$ kubectl apply -f vespaset-crd.yaml
    -
    - -

    Upgrade the Vespa Operator

    - -

    - The operator can be upgraded through helm by running helm upgrade with the new VESPA_VERSION. - Replace $NAMESPACE with the namespace where Vespa is installed. Refer to Factory for the latest VESPA_VERSION. - Note that upgrading the operator does not affect the ConfigServer and Application Pods. Their upgrade will be performed in a subsequent step. -

    - -
    -$ helm upgrade vespa-operator vespa/vespa-operator \
    -  --version $OPERATOR_VERSION \
    -  --namespace $NAMESPACE \
    -  --reuse-values
    -
    - -

    - Wait for the operator to finish rolling out before proceeding. -

    - -
    -$ kubectl rollout status deployment/vespa-operator -n $NAMESPACE
    -
    - -

    Upgrade the VespaSet

    - -

    - To upgrade the ConfigServer and application Pods, patch the spec.version field in the - VespaSet resource. Ensure that the target image is available and accessible on the Kubernetes Node at - VESPA_OPERATOR_IMAGE:VESPA_VERSION and VESPA_IMAGE:VESPA_VERSION before proceeding. - For example: -

    - -
    -$ cat > vespaset.yaml <<EOF
    -apiVersion: k8s.ai.vespa/v1
    -kind: VespaSet
    -metadata:
    -  name: vespaset-sample
    -  namespace: ${NAMESPACE}
    -spec:
    -  version: 8.566.7 # Specify the version to upgrade to.
    -
    -  configServer:
    -    image: "${VESPA_OPERATOR_IMAGE}"
    -    storageClass: "gp3"
    -    generateRbac: false
    -
    -  application:
    -    image: "${VESPA_IMAGE}"
    -    storageClass: "gp3"
    -
    -  ingress:
    -    endpointType: "NONE"
    -EOF
    -
    -$ kubectl apply -f vespaset.yaml
    -
    - -

    - The ConfigServer Pods will detect a change to the VespaSet resource and orchestrate the upgrade procedure to - themselves and the Application Pods. -

    - -

    Upgrade Sequence

    - -

    - The upgrade always proceeds in two phases: ConfigServer Pods are upgraded first, followed by Application Pods. - This ordering is required because the Config Servers must be running the new version before they can safely - orchestrate Application Pods onto it. -

    - -

    - Additionally, the base template for creating a Vespa Pod, whether it be a ConfigServer - or Application Pod, could have been changed during the upgrade. As such, the ConfigServer Pods should ensure that they are - basing off the latest template, rather than a stale one, to prevent needlessly recreating the Pods with the latest - template post-upgrade. -

    - -

    - During the upgrade procedure, each Pod is upgraded one at a time. This process is sequential. For each Pod, the operator: -

    - -
      -
    1. Drains the Pod of traffic and flushes any in-memory state to disk.
    2. -
    3. Deletes the Pod, and recreates it with the new image
    4. -
    5. Waits for the Pod to become healthy and report its Converged Version as a status on the VespaSet
    6. -
    7. Proceeds to the next Pod.
    8. -
    - -

    - The cluster remains operational throughout this procedure. The remaining ConfigServer Pods continue serving - configuration to Application Pods while each node is upgraded in turn, and the Dataplane layer will continue to serve - traffic as normal. For Content Pods, the operator waits for data redistribution to complete before moving to the next Pod, ensuring no data loss during the rollout. - -

    - -

    - To ensure zero downtime for any applications, ingress should be properly configured so that traffic is - correctly load balanced across the Dataplane layer, allowing requests to be seamlessly routed away from - Pods undergoing upgrades. Refer to the Ingress page for more details. -

    - -

    Monitoring the Upgrade

    - -

    - Throughout the upgrade, each Pod's status is reflected in the VespaSet status. A Pod that - is actively being upgraded reports its phase as UPGRADING. A Pod that has successfully completed - the upgrade reports its Converged Version as the new version. -

    - -

    - In the example below, the Config Server Pods have all converged to 8.577, while the Application Pod - default-100 is currently upgrading and has not yet converged from 8.576. -

    - -
    -$ kubectl describe vespaset vespaset-sample -n $NAMESPACE
    -Name:         vespaset-sample
    -Namespace:    $NAMESPACE
    -Labels:       <none>
    -Annotations:  <none>
    -API Version:  k8s.ai.vespa/v1
    -Kind:         VespaSet
    -Metadata:
    -  Creation Timestamp:  2026-01-29T21:32:27Z
    -  Finalizers:
    -    vespasets.k8s.ai.vespa/finalizer
    -  Generation:        1
    -  Resource Version:  121822902
    -  UID:               a70f56e9-6625-4011-acd7-9f7cad29dbc2
    -Spec:
    -  Application:
    -    Image:          $VESPA_IMAGE
    -    Storage Class:  gp3
    -  Config Server:
    -    Generate Rbac:    false
    -    Image:            $VESPA_IMAGE
    -    Storage Class:    gp3
    -  Ingress:
    -    Endpoint Type:  LOAD_BALANCER
    -  Version:          8.577
    -Status:
    -  Bootstrap Status:
    -    Pods:
    -      cfg-1:
    -        Last Updated:  2026-01-29T21:38:45Z
    -        Message:       Pod is running
    -        Phase:         RUNNING
    -        Converged Version: 8.577
    -      cfg-2:
    -        Last Updated:  2026-01-29T21:38:09Z
    -        Message:       Pod is running
    -        Phase:         RUNNING
    -        Converged Version: 8.577
    -      cfg-3:
    -        Last Updated:  2026-01-29T21:36:32Z
    -        Message:       Pod is running
    -        Phase:         RUNNING
    -        Converged Version: 8.577
    -      default-100:
    -        Last Updated:  2026-01-29T21:38:45Z
    -        Message:       Pod is upgrading
    -        Phase:         UPGRADING
    -        Converged Version: 8.576
    -      default-101:
    -        Last Updated:  2026-01-29T21:38:09Z
    -        Message:       Pod is running
    -        Phase:         RUNNING
    -        Converged Version: 8.576
    -      documentation-102:
    -        Last Updated:  2026-01-29T21:36:32Z
    -        Message:       Pod is running
    -        Phase:         RUNNING
    -        Converged Version: 8.576
    -      documentation-103:
    -        Last Updated:  2026-01-29T21:36:32Z
    -        Message:       Pod is running
    -        Phase:         RUNNING
    -        Converged Version: 8.576
    -      cluster-controller-104:
    -        Last Updated:  2026-01-29T21:36:32Z
    -        Message:       Pod is running
    -        Phase:         RUNNING
    -        Converged Version: 8.576
    -      cluster-controller-105:
    -        Last Updated:  2026-01-29T21:36:32Z
    -        Message:       Pod is running
    -        Phase:         RUNNING
    -        Converged Version: 8.576
    -      cluster-controller-106:
    -        Last Updated:  2026-01-29T21:36:32Z
    -        Message:       Pod is running
    -        Phase:         RUNNING
    -        Converged Version: 8.576
    -  Last Transition Time:  2026-01-29T21:33:55Z
    -  Message:               All configservers running
    -  Phase:                 RUNNING
    -Events:                  <none>
    -
    - -

    - The upgrade is complete when every Pod's Converged Version matches the new version and all - phases report RUNNING. -

    - -

    Debugging Upgrade Failures

    - -

    - If a Pod fails to converge to the target version — for example, due to an image pull failure, a crash loop, or - a failed health check, the ConfigServer will continuously retry the upgrade for that Pod until it either succeeds or an administrator intervenes. -

    - -

    - In this scenario, the administrator can diagnose the issue by inspecting the ConfigServer logs or the events of the failing Pod in the current upgrade phase. - Once the issue is resolved, the ConfigServer will automatically retry the upgrade for that Pod and proceed with the remaining nodes. -

    - -

    - For example, suppose the Pod search-106 is failing to upgrade. -

    - -
    -$ kubectl get logs cfg-1 -n $NAMESPACE
    -$ kubectl get logs cfg-2 -n $NAMESPACE
    -$ kubectl get logs cfg-3 -n $NAMESPACE
    -$ kubectl describe pod search-106 -n $NAMESPACE
    -
    - -

    - This design prevents a bad upgrade from cascading to the rest of the Pods. Since the ConfigServer refuses to - advance past a Pod that has not converged, the remaining Pods stay on the previous known-good version while the - administrator investigates. -

    \ No newline at end of file diff --git a/en/operations/kubernetes/operations/upgrades.mdx b/en/operations/kubernetes/operations/upgrades.mdx new file mode 100644 index 0000000000..c417904ea8 --- /dev/null +++ b/en/operations/kubernetes/operations/upgrades.mdx @@ -0,0 +1,196 @@ +--- +title: "Upgrades" +sidebarTitle: "Upgrade Vespa on Kubernetes" +--- + +Vespa on Kubernetes supports zero-downtime rolling upgrades. An upgrade involves upgrading the `vespa-operator` via the Helm chart and the ConfigServer and Application (Container and Content) Pods through the `VespaSet` resource. + +We do not support version drift between the `vespa-operator` and the `VespaSet`. Accordingly, upgrades should be planned so that all components are updated together. To ensure availability, they should be performed in the order as shown in this guide. + +## Update the CRD + +Some upgrades may introduce changes to the `VespaSet` CRD definition. These changes should be applied to the cluster before performing the upgrade. As a rule of thumb, we recommend executing this before every upgrade procedure. + +Helm does not manage the lifecycle of the CRD after it is installed (see [the official documentation](https://helm.sh/docs/chart_best_practices/custom_resource_definitions/?utm_source=chatgpt.com)). As a result, CRD updates must be handled manually. Given the official Helm Chart for Vespa on Kubernetes, this can be performed by extracting the CRD definition from the OCI package and applying it directly using `kubectl`. + +```text +$ helm show crds $HELM_CHART_REF --version $VESPA_VERSION > vespaset-crd.yaml +$ kubectl apply -f vespaset-crd.yaml +``` + +## Upgrade the Vespa Operator + +The operator can be upgraded through helm by running `helm upgrade` with the new `VESPA_VERSION`. Replace `$NAMESPACE` with the namespace where Vespa is installed. Refer to [Factory](https://factory.vespa.ai/) for the latest `VESPA_VERSION`. Note that upgrading the operator does not affect the ConfigServer and Application Pods. Their upgrade will be performed in a subsequent step. + +```bash +$ helm upgrade vespa-operator vespa/vespa-operator \ + --version $OPERATOR_VERSION \ + --namespace $NAMESPACE \ + --reuse-values +``` + +Wait for the operator to finish rolling out before proceeding. + +```js +$ kubectl rollout status deployment/vespa-operator -n $NAMESPACE +``` + +## Upgrade the VespaSet + +To upgrade the ConfigServer and application Pods, patch the `spec.version` field in the `VespaSet` resource. Ensure that the target image is available and accessible on the Kubernetes Node at `VESPA_OPERATOR_IMAGE:VESPA_VERSION` and `VESPA_IMAGE:VESPA_VERSION` before proceeding. For example: + +```bash +$ cat > vespaset.yaml < +Annotations: +API Version: k8s.ai.vespa/v1 +Kind: VespaSet +Metadata: + Creation Timestamp: 2026-01-29T21:32:27Z + Finalizers: + vespasets.k8s.ai.vespa/finalizer + Generation: 1 + Resource Version: 121822902 + UID: a70f56e9-6625-4011-acd7-9f7cad29dbc2 +Spec: + Application: + Image: $VESPA_IMAGE + Storage Class: gp3 + Config Server: + Generate Rbac: false + Image: $VESPA_IMAGE + Storage Class: gp3 + Ingress: + Endpoint Type: LOAD_BALANCER + Version: 8.577 +Status: + Bootstrap Status: + Pods: + cfg-1: + Last Updated: 2026-01-29T21:38:45Z + Message: Pod is running + Phase: RUNNING + Converged Version: 8.577 + cfg-2: + Last Updated: 2026-01-29T21:38:09Z + Message: Pod is running + Phase: RUNNING + Converged Version: 8.577 + cfg-3: + Last Updated: 2026-01-29T21:36:32Z + Message: Pod is running + Phase: RUNNING + Converged Version: 8.577 + default-100: + Last Updated: 2026-01-29T21:38:45Z + Message: Pod is upgrading + Phase: UPGRADING + Converged Version: 8.576 + default-101: + Last Updated: 2026-01-29T21:38:09Z + Message: Pod is running + Phase: RUNNING + Converged Version: 8.576 + documentation-102: + Last Updated: 2026-01-29T21:36:32Z + Message: Pod is running + Phase: RUNNING + Converged Version: 8.576 + documentation-103: + Last Updated: 2026-01-29T21:36:32Z + Message: Pod is running + Phase: RUNNING + Converged Version: 8.576 + cluster-controller-104: + Last Updated: 2026-01-29T21:36:32Z + Message: Pod is running + Phase: RUNNING + Converged Version: 8.576 + cluster-controller-105: + Last Updated: 2026-01-29T21:36:32Z + Message: Pod is running + Phase: RUNNING + Converged Version: 8.576 + cluster-controller-106: + Last Updated: 2026-01-29T21:36:32Z + Message: Pod is running + Phase: RUNNING + Converged Version: 8.576 + Last Transition Time: 2026-01-29T21:33:55Z + Message: All configservers running + Phase: RUNNING +Events: +``` + +The upgrade is complete when every Pod's `Converged Version` matches the new version and all phases report `RUNNING`. + +## Debugging Upgrade Failures + +If a Pod fails to converge to the target version — for example, due to an image pull failure, a crash loop, or a failed health check, the ConfigServer will continuously retry the upgrade for that Pod until it either succeeds or an administrator intervenes. + +In this scenario, the administrator can diagnose the issue by inspecting the ConfigServer logs or the events of the failing Pod in the current upgrade phase. Once the issue is resolved, the ConfigServer will automatically retry the upgrade for that Pod and proceed with the remaining nodes. + +For example, suppose the Pod `search-106` is failing to upgrade. + +```bash +$ kubectl get logs cfg-1 -n $NAMESPACE +$ kubectl get logs cfg-2 -n $NAMESPACE +$ kubectl get logs cfg-3 -n $NAMESPACE +$ kubectl describe pod search-106 -n $NAMESPACE +``` + +This design prevents a bad upgrade from cascading to the rest of the Pods. Since the ConfigServer refuses to advance past a Pod that has not converged, the remaining Pods stay on the previous known-good version while the administrator investigates. \ No newline at end of file diff --git a/en/operations/kubernetes/configuration/tls.html b/en/operations/kubernetes/tls.mdx similarity index 57% rename from en/operations/kubernetes/configuration/tls.html rename to en/operations/kubernetes/tls.mdx index 79c2e20637..0549691e43 100644 --- a/en/operations/kubernetes/configuration/tls.html +++ b/en/operations/kubernetes/tls.mdx @@ -1,56 +1,32 @@ --- -# Copyright Vespa.ai. All rights reserved. -title: Enable TLS Encryption for Vespa on Kubernetes -applies_to: enterprise +title: "Enable TLS Encryption for Vespa on Kubernetes" +sidebarTitle: "Enable TLS Encryption" --- -

    - TLS encryption for Vespa on Kubernetes can be configured for internal pod-to-pod communication using mutual TLS (mTLS) - and for external ingress traffic. This guide demonstrates using cert-manager, a - Kubernetes-native certificate management solution that automates certificate issuance and renewal, to set up TLS for the - Vespa on Kubernetes deployment. -

    - -

    - cert-manager integrates with multiple certificate authorities including self-signed CAs, Let's Encrypt, HashiCorp Vault, - and commercial providers. It handles the certificate lifecycle by automatically issuing certificates and renewing them before expiration. - The cert-manager CSI driver provides secure certificate delivery to pods - through runtime injection via a DaemonSet, ensuring certificates are available before containers start. -

    - -

    Prerequisites

    - - - -

    Enable mTLS for Internal Communication

    - -

    - Mutual TLS (mTLS) secures communication between Vespa services within the Kubernetes cluster. Each pod authenticates using - client certificates issued by a namespace-local root Certificate Authority. It is also possible to configure the Certificate Authority - to be cluster-global. -

    - -

    - This method is ideal for those who prefer TLS to terminate at the service, or those who have already integrated with - mTLS from Vespa Cloud. For more details on Vespa's mTLS implementation, - see the Vespa mTLS documentation. -

    - -

    Step 1: Create Certificate Authority

    - -

    - Create a self-signed issuer to bootstrap the certificate chain, then use it to create a namespace-local root CA certificate that acts as the - trust anchor for all internal mTLS certificates. -

    - -
    -$ cat <<EOF | kubectl apply -f -
    +TLS encryption for Vespa on Kubernetes can be configured for internal pod-to-pod communication using mutual TLS (mTLS) and for external ingress traffic. This guide demonstrates using [cert-manager](https://cert-manager.io/), a Kubernetes-native certificate management solution that automates certificate issuance and renewal, to set up TLS for the Vespa on Kubernetes deployment.
    +
    +`cert-manager` integrates with multiple certificate authorities including self-signed CAs, Let's Encrypt, HashiCorp Vault, and commercial providers. It handles the certificate lifecycle by automatically issuing certificates and renewing them before expiration. The [cert-manager CSI driver](https://cert-manager.io/docs/usage/csi-driver/) provides secure certificate delivery to pods through runtime injection via a DaemonSet, ensuring certificates are available before containers start.
    +
    +## Prerequisites
    +
    +- Kubernetes cluster with Vespa Operator installed (see [Installation](/en/operations/kubernetes/deployment/installation))
    +- [cert-manager](https://cert-manager.io/docs/installation/) v1.13 or later
    +- [cert-manager CSI driver](https://cert-manager.io/docs/usage/csi-driver/) installed
    +- Kubernetes Command Line Tool ([kubectl](https://kubernetes.io/docs/reference/kubectl/))
    +- OpenSSL (for CA generation)
    +
    +## Enable mTLS for Internal Communication
    +
    +Mutual TLS (mTLS) secures communication between Vespa services within the Kubernetes cluster. Each pod authenticates using client certificates issued by a namespace-local root Certificate Authority. It is also possible to configure the Certificate Authority to be cluster-global.
    +
    +This method is ideal for those who prefer TLS to terminate at the service, or those who have already integrated with mTLS from Vespa Cloud. For more details on Vespa's mTLS implementation, see the [Vespa mTLS documentation](/en/security/mtls).
    +
    +### Step 1: Create Certificate Authority
    +
    +Create a self-signed issuer to bootstrap the certificate chain, then use it to create a namespace-local root CA certificate that acts as the trust anchor for all internal mTLS certificates.
    +
    +```yaml expandable
    +$ cat <Step 1: Create Certificate Authority
  • +``` -

    Step 2: Create CA Issuer

    +### Step 2: Create CA Issuer -

    - The CA issuer uses the root CA to issue certificates to individual pods. -

    +The CA issuer uses the root CA to issue certificates to individual pods. -
    -$ cat <<EOF | kubectl apply -f -
    +```yaml
    +$ cat <Step 2: Create CA Issuer
       ca:
         secretName: root-ca-secret
     EOF
    -
    +``` -

    Step 3: Create TLS Configuration

    +### Step 3: Create TLS Configuration -

    - Create a ConfigMap that defines the location where Vespa loads TLS certificates and private keys. This configuration file is loaded - through the VESPA_TLS_CONFIG_FILE environment variable in the Pod specification of the VespaSet. -

    +Create a `ConfigMap` that defines the location where Vespa loads TLS certificates and private keys. This configuration file is loaded through the `VESPA_TLS_CONFIG_FILE` environment variable in the Pod specification of the `VespaSet`. -
    -$ cat <<EOF | kubectl apply -f -
    +```yaml
    +$ cat <Step 3: Create TLS Configuration
           }
         }
     EOF
    -
    +``` -

    Step 4: Configure VespaSet with mTLS

    +### Step 4: Configure VespaSet with mTLS -

    - Update the VespaSet specification to mount the TLS configuration and use the cert-manager CSI driver for certificate injection via a - PodTempalte. For more information on the PodTemplate specification, refer to the Custom Overrides section. -

    +Update the VespaSet specification to mount the TLS configuration and use the cert-manager CSI driver for certificate injection via a `PodTempalte`. For more information on the `PodTemplate` specification, refer to the [Custom Overrides](/en/operations/kubernetes/custom-overrides-podtemplate) section. -

    - Set the VESPA_TLS_CONFIG_FILE environment variable to point to the TLS configuration file from the ConfigMap. - The main container name will always be vespa. -

    +Set the `VESPA_TLS_CONFIG_FILE` environment variable to point to the TLS configuration file from the `ConfigMap`. The main container name will always be `vespa`. -
    -$ cat <<EOF | kubectl apply -f -
    +```yaml expandable
    +$ cat <Step 4: Configure VespaSet with mTLS
       ingress:
         endpointType: "NODE_PORT"
     EOF
    -
    +``` -

    - The main vespa container in the VespaSet includes a new specification to mount the cert-manager - CSI-managed certificates to /etc/tls/. -

    +The main `vespa` container in the `VespaSet` includes a new specification to mount the cert-manager CSI-managed certificates to `/etc/tls/`. -

    - The cert-manager CSI driver automatically injects certificates signed by the CA issuer through the volume override, - with a 30-day validity period and automatic renewal 3 days before expiration in this example. -

    +The cert-manager CSI driver automatically injects certificates signed by the CA issuer through the `volume` override, with a 30-day validity period and automatic renewal 3 days before expiration in this example. -

    Enabling TLS for Ingress Traffic

    +## Enabling TLS for Ingress Traffic -

    - Ingress TLS secures external traffic to Vespa's Data Plane HTTP port. This configuration allows using a separate - certificate chain from mTLS, providing security boundary separation between internal and external communication. - This is useful when you want to terminate TLS at the service but use different trust anchors for internal and external traffic. - These settings apply only to application pods. -

    +Ingress TLS secures external traffic to Vespa's Data Plane HTTP port. This configuration allows using a separate certificate chain from mTLS, providing security boundary separation between internal and external communication. This is useful when you want to terminate TLS at the service but use different trust anchors for internal and external traffic. These settings apply only to application pods. -

    Step 1: Create External CA for Ingress

    +### Step 1: Create External CA for Ingress -

    - In this example, we will generate a dummy Certificate Authority (CA) that will act as a commercial authoritative CA. -

    +In this example, we will generate a dummy Certificate Authority (CA) that will act as a commercial authoritative CA. -

    - Generate a separate Certificate Authority for signing ingress certificates. -

    +Generate a separate Certificate Authority for signing ingress certificates. -
    +```bash
     $ openssl genrsa -out ingress-ca.key 2048
     $ openssl req -x509 -new -nodes -key ingress-ca.key -sha256 -days 365 \
       -out ingress-ca.crt -subj "/CN=Ingress CA" \
       -addext "basicConstraints=CA:TRUE"
    -
    +``` -

    Step 2: Import CA as Kubernetes Secret

    +### Step 2: Import CA as Kubernetes Secret -

    - Manually import the CA as a Kubernetes Secret resource. -

    +Manually import the CA as a Kubernetes `Secret` resource. -
    +```bash
     $ kubectl create secret tls ingress-ca \
       --cert=ingress-ca.crt \
       --key=ingress-ca.key \
       -n vespa
    -
    +``` -

    Step 3: Create Ingress CA Issuer

    +### Step 3: Create Ingress CA Issuer -

    - The ingress issuer references the externally-generated CA and uses it to sign certificates for ingress traffic, - separate from the internal mTLS certificate chain. -

    +The ingress issuer references the externally-generated CA and uses it to sign certificates for ingress traffic, separate from the internal mTLS certificate chain. -
    -$ cat <<EOF | kubectl apply -f -
    +```yaml
    +$ cat <Step 3: Create Ingress CA Issuer
       ca:
         secretName: ingress-ca
     EOF
    -
    +``` -

    Step 4: Create Ingress TLS Configuration

    -

    - Create a separate ConfigMap for ingress TLS configuration. This configuration file is loaded - through the VESPA_TLS_CONFIG_FILE_INGRESS environment variable in the Pod specification of the VespaSet. -

    +### Step 4: Create Ingress TLS Configuration -
    -$ cat <<EOF | kubectl apply -f -
    +Create a separate ConfigMap for ingress TLS configuration. This configuration file is loaded through the `VESPA_TLS_CONFIG_FILE_INGRESS` environment variable in the Pod specification of the `VespaSet`.
    +
    +```yaml
    +$ cat <Step 4: Create Ingress TLS Configuration
           }
         }
     EOF
    -
    +``` -

    Step 5: Configure Ingress TLS in VespaSet

    +### Step 5: Configure Ingress TLS in VespaSet -

    - Update the VespaSet application pod template to include ingress TLS configuration. Set the VESPA_TLS_CONFIG_FILE_INGRESS - environment variable to point to the ingress TLS configuration file. -

    +Update the VespaSet application pod template to include ingress TLS configuration. Set the `VESPA_TLS_CONFIG_FILE_INGRESS` environment variable to point to the ingress TLS configuration file. -
    -$ cat <<EOF | kubectl apply -f -
    +```yaml expandable
    +$ cat <Step 5: Configure Ingress TLS in VespaSet
       ingress:
         endpointType: "NODE_PORT"
     EOF
    -
    +``` -

    - This configuration adds a second TLS certificate chain for ingress traffic by setting the - VESPA_TLS_CONFIG_FILE_INGRESS environment variable and mounting certificates from the - ingress issuer. Both mTLS and ingress TLS configurations coexist, providing separate trust boundaries - for internal and external communication. -

    +This configuration adds a second TLS certificate chain for ingress traffic by setting the `VESPA_TLS_CONFIG_FILE_INGRESS` environment variable and mounting certificates from the ingress issuer. Both mTLS and ingress TLS configurations coexist, providing separate trust boundaries for internal and external communication. -

    Step 6: Testing with Client Certificates

    +### Step 6: Testing with Client Certificates -

    - To test ingress TLS, generate a client certificate signed by the ingress CA. -

    +To test ingress TLS, generate a client certificate signed by the ingress CA. -
    +```bash
     $ openssl genrsa -out ingress-client.key 2048
     $ openssl req -new -key ingress-client.key -out ingress-client.csr \
       -subj "/CN=client.local"
     $ openssl x509 -req -in ingress-client.csr \
       -CA ingress-ca.crt -CAkey ingress-ca.key -CAcreateserial \
       -out ingress-client.crt -days 365 -sha256
    -
    +``` + -

    - Query Vespa using the client certificate by starting a port-forward to the DataPlane 4443 port. -

    +Query Vespa using the client certificate by starting a port-forward to the DataPlane 4443 port. -
    +```bash
     $ kubectl -n mramdenbourg-upgradetest port-forward pod/$CONTAINER_POD_NAME 4443:4443
    -
    +``` -

    - Use the client certificate to query the DataPlane cluster. -

    +Use the client certificate to query the DataPlane cluster. -
    +```curl
     $ curl --cacert ingress-ca.crt \
       --key ingress-client.key \
       --cert ingress-client.crt \
    @@ -469,4 +399,4 @@ 

    Step 6: Testing with Client Certificates

    "year": 1969 } }' -
    \ No newline at end of file +``` \ No newline at end of file diff --git a/en/operations/kubernetes/vespa-on-kubernetes.html b/en/operations/kubernetes/vespa-on-kubernetes.html deleted file mode 100644 index 234d7d1422..0000000000 --- a/en/operations/kubernetes/vespa-on-kubernetes.html +++ /dev/null @@ -1,38 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: Vespa on Kubernetes -applies_to: enterprise ---- - -

    - Vespa applications can be deployed and managed on Kubernetes through the Vespa Operator. The Vespa Operator provides a native - Kubernetes experience to the management of Vespa infrastructure and applications. -

    - -

    Overview

    -

    - The Vespa Operator simplifies running Vespa on Kubernetes by providing: -

    -
      -
    • Declarative configuration with a Custom Resource Definition (CRD)
    • -
    • Automated Vespa infrastructure deployment and management
    • -
    • Automated deployment, updates, and upgrades to Vespa applications
    • -
    • Critical lifecycle operations such as failure handling
    • -
    • Secure clusters with TLS
    • -
    • Kubernetes-native monitoring of Vespa applications
    • -
    • Integration with Kubernetes-native tooling
    • -
    - -

    -Refer to the following sections to install, configure, and manage Vespa on Kubernetes. -For more details on the design of Vespa on Kubernetes, refer to the Architecture section. -

    - diff --git a/en/operations/kubernetes/vespa-on-kubernetes.mdx b/en/operations/kubernetes/vespa-on-kubernetes.mdx new file mode 100644 index 0000000000..769a251e45 --- /dev/null +++ b/en/operations/kubernetes/vespa-on-kubernetes.mdx @@ -0,0 +1,30 @@ +--- +title: "Vespa on Kubernetes" +sidebarTitle: "Introduction" +--- + +Vespa applications can be deployed and managed on Kubernetes through the Vespa Operator. The Vespa Operator provides a native Kubernetes experience to the management of Vespa infrastructure and applications. + +## Overview + +The Vespa Operator simplifies running Vespa on Kubernetes by providing: + +- Declarative configuration with a [Custom Resource Definition (CRD)](https://kubernetes.io/docs/concepts/extend-kubernetes/api-extension/custom-resources/) +- Automated Vespa infrastructure deployment and management +- Automated deployment, updates, and upgrades to Vespa applications +- Critical lifecycle operations such as failure handling +- Secure clusters with TLS +- Kubernetes-native monitoring of Vespa applications +- Integration with Kubernetes-native tooling + +Refer to the following sections to install, configure, and manage Vespa on Kubernetes. For more details on the design of Vespa on Kubernetes, refer to the [Architecture](/en/operations/kubernetes/architecture) section. + + + + + + + + + + \ No newline at end of file diff --git a/en/operations/login.md b/en/operations/login.md deleted file mode 100644 index 641e72102d..0000000000 --- a/en/operations/login.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: Login -applies_to: cloud ---- - -## MFA reset - -If you have lost your MFA information you can request it reset from Vespa.ai Support. - -To reset MFA for a user, contact support@vespa.ai with the user's email address. Support will send a verification code to that email address as part of the reset process. Once received, the code must be returned to support — along with explicit confirmation that the MFA reset is authorized — before the reset can be completed. diff --git a/en/operations/login.mdx b/en/operations/login.mdx new file mode 100644 index 0000000000..64b4bf5d1f --- /dev/null +++ b/en/operations/login.mdx @@ -0,0 +1,9 @@ +--- +title: "Login" +--- + +## MFA reset + +If you have lost your MFA information you can request it reset from Vespa.ai Support. + +To reset MFA for a user, contact [support@vespa.ai](mailto:support@vespa.ai) with the user's email address. Support will send a verification code to that email address as part of the reset process. Once received, the code must be returned to support — along with explicit confirmation that the MFA reset is authorized — before the reset can be completed. diff --git a/en/operations/metrics.html b/en/operations/metrics.html deleted file mode 100644 index a32132bdc1..0000000000 --- a/en/operations/metrics.html +++ /dev/null @@ -1,384 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Metrics" -redirect_from: -- /en/reference/metrics.html ---- - -

    - Metrics for all nodes is aggregated using - /metrics/v2/values or - /prometheus/v1/values. - Values from these endpoints reflect the 1 minute activity immediately before the request. -

    -

    Example getting a metric value from using the prometheus endpoint:

    - -
    -$ curl -s http://ENDPOINT/prometheus/v1/values/?consumer=vespa | \
    -  grep "vds.idealstate.merge_bucket.pending.average" | egrep -v 'HELP|TYPE'
    -
    -{% include important.html content='Make sure to use consumer=vespa -to list all metrics.' %} -

    Example getting a metric value using /metrics/v2/values:

    -
    -$ curl ENDPOINT/metrics/v2/values | \
    -  jq -r -c '
    -    .nodes[] |
    -    .hostname as $h |
    -    .services[].metrics[] |
    -    select(.values."content.proton.documentdb.documents.total.last") |
    -    [$h, .dimensions.documenttype, .values."content.proton.documentdb.documents.total.last"] |
    -    @tsv'
    -
    -node9.vespanet	music	0
    -node8.vespanet	music	0
    -
    - - - -

    Aggregating metrics

    -

    - Metrics in Vespa are generated from services running on the individual nodes, and in many cases - have many recordings per metric, from within each node, with unique tag / dimension combinations. - These recordings need to be put together to contribute to the overall picture of how the system - is behaving. If this is done the right way you will be able to “zoom out” to get the bigger - picture, or to “zoom in” to see how things behave in more detail. This is very useful when - looking into possible production issues. Unfortunately it is easy to combine metrics the wrong - way, resulting in potentially significantly distorted graphs. -

    -

    - For each of the values (suffixes) available for the different metrics here is how we recommend - that you aggregate them to get the best use of them. The guidelines should be used both for - aggregations over time (multiple snapshot intervals) and over tag combinations. -

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    Suffix NameAggregation
    max

    - Use the highest value available MAX(max). -

    min

    - Use the lowest value available MIN(min). -

    sum

    - Use the sum of all values SUM(sum). -

    count

    - Use the sum of all values SUM(count). -

    average

    - To generate an average value you want to do SUM(sum) / SUM(count) where you generate the graph. - Don’t use the average suffix itself if you have the sum and count - suffixes available. Using this will easily lead to computing averages of averages, which will easily become - very distorted and noisy. -

    last

    - Avoid this except for metrics you expect to be stable, such as amount of memory available on a node, etc. - This value is the last from a metrics snapshot period, hence basically a single value picked from all values - during the snapshot period. Typically, very noisy for volatile metrics. It does not make sense to aggregate - on this value at all, but if you must then choose a value with the same combination of tags over time. -

    95percentile

    - This value cannot be aggregated in a way that gives a mathematically correct value. But where you have to - either compute the average value for the most realistic value, AVERAGE(95percentile), or max if the goal is to better identify - outliers, MAX(95percentile). Regardless, this value is best used when considered at the most granular level, with all tag - values specified. -

    99percentile

    - Same as for the 95percentile suffix. -

    - - - - -

    Metric-sets

    -

    - Node metrics in /metrics/v1/values are listed per service, - with a set of system metrics - example: -

    -
    {% highlight json %}
    -{
    -    "services": [
    -        {
    -            "name": "vespa.container",
    -            "timestamp": 1662120754,
    -            "status": {
    -                "code": "up",
    -                "description": "Data collected successfully"
    -            },
    -            "metrics": [
    -                {
    -                    "values": {
    -                        "memory_virt": 3683172352,
    -                        "memory_rss": 1425416192,
    -                        "cpu": 2.0234722784298,
    -                        "cpu_util": 0.202347227843
    -                    },
    -                    "dimensions": {
    -                        "metrictype": "system",
    -                        "instance": "container",
    -                        "clustername": "default",
    -                        "vespaVersion": "8.46.19"
    -                    }
    -                },
    -                {
    -                    "values": {},
    -                    "dimensions": {
    -                        "clustername": "default",
    -                        "instance": "container",
    -                        "vespaVersion": "8.46.19"
    -                    }
    -                }
    -            ]
    -        },
    -{% endhighlight %}
    -

    - The default metric-set is added to the system metric-set, - unless a consumer request parameter - specifies a different built-in or custom metric set - - see - metric list. -

    -

    - The Vespa metric-set has a richer set of metrics, see - - metric list. -

    -

    - The consumer request parameter can also be used in - /metrics/v2/values and - /prometheus/v1/values. -

    -

    - Example minimal metric-set; system metric-set + a specific metric: -

    -
    {% highlight xml%}
    -
    -    
    -    
    -        
    -            
    -        
    -    
    -
    -{% endhighlight %}
    -

    - Example default metric-set and more; system metric-set + default metric-set + a built-in metric: -

    -
    {% highlight xml%}
    -
    -    
    -    
    -        
    -            
    -            
    -        
    -    
    -
    -{% endhighlight %}
    - - - -

    Metrics names

    -

    - The names of metrics emitted by Vespa typically follow this naming scheme: - <prefix>.<service>.<component>.<suffix>. The separator (. here) may differ for - different metrics integrations. Similarly, the <prefix> string may differ depending on your configuration. - Further some metrics have several levels of component names. Each metric will have a number of values associated - with them, one for each suffix provided by the metric. Typical suffixes include sum, count and - max. -

    - - - -

    Container Metrics

    -

    - Metrics from the container with description and unit can be found in the - container metrics reference. - The most commonly used metrics are mentioned below. -

    - - -

    Generic Container Metrics

    -

    - These metrics are output for the server as a whole, e.g. related to resources. - Some metrics indicate memory usage, such as mem.heap.*, mem.native.*, mem.direct.*. - Other metrics are related to the JVM garbage collection, jdisc.gc.count and jdisc.gc.ms. -

    - - -

    Thread Pool Metrics

    -

    - Metrics for the container thread pools. - The jdisc.thread_pool.* metrics have a dimension threadpool with thread pool name, - e.g. default-pool for the container's default thread pool. - See Container Tuning for details. -

    - - -

    HTTP Specific Metrics

    -

    - These are metrics specific for HTTP. - Those metrics that are specific to a connector will have a dimension containing the TCP listen port. -

    -

    - Refer to Container Metrics - for metrics on HTTP status response codes, - http.status.* or more detailed requests related to the handling of requests, jdisc.http.*. - Other relevant metrics include serverNumConnections, - serverNumOpenConnections, - serverBytesReceived and - serverBytesSent. -

    - - -

    Query Specific Metrics

    -

    - For metrics related to queries please start with the queries and query_latency, - the handled.requests and handled.latency - or the httpapi_* metrics for more insights. -

    - - -

    Feed Specific Metrics

    -

    - For metrics related to feeding into Vespa, - we recommend using the feed.operations and feed.latency metrics. -

    - - - -

    Tracking Vespa version per node

    -

    - Every Vespa node emits a constant 1 heartbeat metric named - host_life.alive. Its presence and freshness indicate that the - node is up; the value itself carries no significance. -

    -

    - host_life.alive is the only metric that carries the - vespaVersion dimension, identifying the Vespa version running - on each node. The dimension is not added to every metric because doing so - would significantly increase time series cardinality. Use this metric to - build dashboards that track version upgrade progress across a cluster, or - to correlate Vespa upgrades with changes in latency, resource usage and - other operational metrics. -

    - - - -

    Available metrics

    -

    - Each of the services running in a Vespa installation maintains and reports a number of metrics. -

    -

    - Metrics from the container services are the most commonly used, and are listed in - Container Metrics. - You will find the metrics available there, with description and unit. -

    - - - -

    Metrics from custom components

    -

    - Add custom metrics from components like Searchers and - Document processors: -

    -
      -
    1. - Add a - MetricReceiver instance to the constructor of the component - - it is injected by the Container. -
    2. -
    3. - Declare - Gauge - and - Counter metrics using the declare-methods on the MetricReceiver. - Optionally set arbitrary metric dimensions to default values at declaration time - refer to the javadoc for details. -
    4. -
    5. - Each time there is some data to measure, invoke the - - sample method on gauges or the - - add method on counters. - The gauges and counters declared are inherently thread-safe. - When sampling data, any dimensions can optionally be set. -
    6. -
    7. - Add a consumer in services.xml - for the metrics to be emitted in the metric APIs, like in the previous section. -
    8. -
    -

    - Find a full example in the - - album-recommendation-java sample application. -

    -{% include note.html content="Metrics with no value do now show in the metric APIs - -in the example above, make at least one query to set the metric value." %} - - -

    Example / QA

    -

    - I have two different libraries that are running as components with their own threads within the vespa container. - We are injecting MetricReceiver to each library. - After injecting the receiver we store the reference to this receiver in a container-wide object - so that they can be used inside these libraries - (the libraries each have several classes and such, so it is not possible to inject the receiver every time, - and we need to use the stored reference). Questions: -

    -
      -
    1. -

      - Q: Is the MetricReceiver object unique within the container? - That is, if I am injecting the receiver to two different components, is always the same object getting injected? -

      -

      - A: Yes, you get the same object. -

      -
    2. -
    3. -

      - Q: How long does an object remain valid? - Does the same object remain valid for the life of the container - (meaning from container booting up to the point of restart/shutdown) or can the object change? - I ask this because we store the reference to the receiver at a common place - so that it can be used to emit metrics elsewhere in the library where we can’t inject it, - so I am wondering how frequently we need to update this reference. -

      -

      - A: It remains valid for the lifetime of the component to which it got injected. - Therefore, if you share component references through some other mean than direct or indirect injection - you may end up with invalid references. - A "container-wide object" sounds like trouble. - You should have it injected into all the components that needs it instead. - Or, if you feel that will be too fine-grained, - create one large object which gets these things injected, - and then have that injected into all components that need the common stuff. -

      -
    4. -
    diff --git a/en/operations/metrics.mdx b/en/operations/metrics.mdx new file mode 100644 index 0000000000..2ec11140ed --- /dev/null +++ b/en/operations/metrics.mdx @@ -0,0 +1,232 @@ +--- +title: "Metrics" +--- + +Metrics for all nodes is aggregated using *[/metrics/v2/values](/en/reference/api/metrics-v2#metrics-v2-values)* or *[/prometheus/v1/values](/en/reference/api/prometheus-v1#prometheus-v1-values)*. Values from these endpoints reflect the 1 minute activity immediately before the request. + +Example getting a metric value from using the prometheus endpoint: + +```bash +$ curl -s http://ENDPOINT/prometheus/v1/values/?consumer=vespa | \ + grep "vds.idealstate.merge_bucket.pending.average" | egrep -v 'HELP|TYPE' +``` + + +**Important:** + +Make sure to use [consumer=vespa](/en/reference/api/metrics-v1#consumer) to list all metrics. + + +Example getting a metric value using */metrics/v2/values*: + +```bash +$ curl ENDPOINT/metrics/v2/values | \ + jq -r -c ' + .nodes[] | + .hostname as $h | + .services[].metrics[] | + select(.values."content.proton.documentdb.documents.total.last") | + [$h, .dimensions.documenttype, .values."content.proton.documentdb.documents.total.last"] | + @tsv' + +node9.vespanet music 0 +node8.vespanet music 0 +``` + +## Aggregating metrics + +Metrics in Vespa are generated from services running on the individual nodes, and in many cases have many recordings per metric, from within each node, with unique tag / dimension combinations. These recordings need to be put together to contribute to the overall picture of how the system is behaving. If this is done the right way you will be able to “zoom out” to get the bigger picture, or to “zoom in” to see how things behave in more detail. This is very useful when looking into possible production issues. Unfortunately it is easy to combine metrics the wrong way, resulting in potentially significantly distorted graphs. + +For each of the values (suffixes) available for the different metrics here is how we recommend that you aggregate them to get the best use of them. The guidelines should be used both for aggregations over time (multiple snapshot intervals) and over tag combinations. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Suffix NameAggregation
    {`max`}Use the highest value available {`MAX(max)`}.
    {`min`}Use the lowest value available {`MIN(min)`}.
    {`sum`}Use the sum of all values {`SUM(sum)`}.
    {`count`}Use the sum of all values {`SUM(count)`}.
    {`average`}To generate an average value you want to do {`SUM(sum) / SUM(count)`} where you generate the graph. Don’t use the {`average`} suffix itself if you have the {`sum`} and {`count`} suffixes available. Using this will easily lead to computing averages of averages, which will easily become very distorted and noisy.
    {`last`}Avoid this except for metrics you expect to be stable, such as amount of memory available on a node, etc. This value is the last from a metrics snapshot period, hence basically a single value picked from all values during the snapshot period. Typically, very noisy for volatile metrics. It does not make sense to aggregate on this value at all, but if you must then choose a value with the same combination of tags over time.
    {`95percentile`}This value cannot be aggregated in a way that gives a mathematically correct value. But where you have to either compute the average value for the most realistic value, {`AVERAGE(95percentile)`}, or max if the goal is to better identify outliers, {`MAX(95percentile)`}. Regardless, this value is best used when considered at the most granular level, with all tag values specified.
    {`99percentile`}Same as for the {`95percentile`} suffix.
    + +## Metric-sets + +Node metrics in */metrics/v1/values* are listed per service, with a set of system metrics - example: + +```json expandable +{ + "services": [ + { + "name": "vespa.container", + "timestamp": 1662120754, + "status": { + "code": "up", + "description": "Data collected successfully" + }, + "metrics": [ + { + "values": { + "memory_virt": 3683172352, + "memory_rss": 1425416192, + "cpu": 2.0234722784298, + "cpu_util": 0.202347227843 + }, + "dimensions": { + "metrictype": "system", + "instance": "container", + "clustername": "default", + "vespaVersion": "8.46.19" + } + }, + { + "values": {}, + "dimensions": { + "clustername": "default", + "instance": "container", + "vespaVersion": "8.46.19" + } + } + ] + }, +``` + +The `default` metric-set is added to the system metric-set, unless a [consumer](/en/reference/api/metrics-v1#consumer) request parameter specifies a different built-in or custom metric set - see [metric list](/en/reference/operations/metrics/default-metric-set#metric-sets). + +The `Vespa` metric-set has a richer set of metrics, see [metric list](/en/reference/operations/metrics/vespa-metric-set#metric-sets). + +The *consumer* request parameter can also be used in [/metrics/v2/values](/en/reference/api/metrics-v2#metrics-v2-values) and [/prometheus/v1/values](/en/reference/api/prometheus-v1#prometheus-v1-values). + +Example minimal metric-set; system metric-set + a specific metric: + +```xml + + + + + + + + +``` + +Example default metric-set and more; system metric-set + default metric-set + a built-in metric: + +```xml + + + + + + + + + +``` + +## Metrics names + +The names of metrics emitted by Vespa typically follow this naming scheme: `...`. The separator (`.` here) may differ for different metrics integrations. Similarly, the `` string may differ depending on your configuration. Further some metrics have several levels of `component` names. Each metric will have a number of values associated with them, one for each `suffix` provided by the metric. Typical suffixes include `sum`, `count` and `max`. + +## Container Metrics + +Metrics from the container with description and unit can be found in the [container metrics reference](/en/reference/operations/metrics/container#container-metrics). The most commonly used metrics are mentioned below. + +### Generic Container Metrics + +These metrics are output for the server as a whole, e.g. related to resources. Some metrics indicate memory usage, such as `mem.heap.*`, `mem.native.*`, `mem.direct.*`. Other metrics are related to the JVM garbage collection, `jdisc.gc.count` and `jdisc.gc.ms`. + +### Thread Pool Metrics + +Metrics for the container thread pools. The `jdisc.thread_pool.*` metrics have a dimension `threadpool` with thread pool name, e.g. *default-pool* for the container's default thread pool. See [Container Tuning](/en/performance/container-tuning#container-tuning) for details. + +### HTTP Specific Metrics + +These are metrics specific for HTTP. Those metrics that are specific to a connector will have a dimension containing the TCP listen port. + +Refer to [Container Metrics](/en/reference/operations/metrics/container) for metrics on HTTP status response codes, `http.status.*` or more detailed requests related to the handling of requests, `jdisc.http.*`. Other relevant metrics include `serverNumConnections`, `serverNumOpenConnections`, `serverBytesReceived` and `serverBytesSent`. + +### Query Specific Metrics + +For metrics related to queries please start with the `queries` and `query_latency`, the `handled.requests` and `handled.latency` or the `httpapi_*` metrics for more insights. + +### Feed Specific Metrics + +For metrics related to feeding into Vespa, we recommend using the `feed.operations` and `feed.latency` metrics. + +## Available metrics + +Each of the services running in a Vespa installation maintains and reports a number of metrics. + +Metrics from the container services are the most commonly used, and are listed in [Container Metrics](/en/reference/operations/metrics/container). You will find the metrics available there, with description and unit. + +## Metrics from custom components + +Add custom metrics from components like [Searchers](/en/applications/searchers) and [Document processors](/en/applications/document-processors): + + + +Add a [MetricReceiver](https://javadoc.io/doc/com.yahoo.vespa/container-disc/latest/com/yahoo/metrics/simple/MetricReceiver.html) instance to the constructor of the component - it is [injected](/en/applications/dependency-injection) by the Container. + + +Declare [Gauge](https://javadoc.io/doc/com.yahoo.vespa/container-disc/latest/com/yahoo/metrics/simple/Gauge.html) and [Counter](https://javadoc.io/doc/com.yahoo.vespa/container-disc/latest/com/yahoo/metrics/simple/Counter.html) metrics using the *declare*\-methods on the *MetricReceiver*. Optionally set arbitrary metric dimensions to default values at declaration time - refer to the javadoc for details. + + +Each time there is some data to measure, invoke the [sample](https://javadoc.io/doc/com.yahoo.vespa/container-disc/latest/com/yahoo/metrics/simple/Gauge.html#sample\(double\)) method on gauges or the [add](https://javadoc.io/doc/com.yahoo.vespa/container-disc/latest/com/yahoo/metrics/simple/Counter.html#add\(\)) method on counters. The gauges and counters declared are inherently thread-safe. When sampling data, any dimensions can optionally be set. + + +Add a [consumer](/en/reference/applications/services/admin#consumer) in *services.xml* for the metrics to be emitted in the metric APIs, like in the previous section. + + +Find a full example in the [album-recommendation-java](https://github.com/vespa-engine/sample-apps/tree/master/album-recommendation-java) sample application. + + +**Note:** + +Metrics with no value do now show in the metric APIs - in the example above, make at least one query to set the metric value. + + +### Example / QA + +I have two different libraries that are running as components with their own threads within the vespa container. We are injecting MetricReceiver to each library. After injecting the receiver we store the reference to this receiver in a container-wide object so that they can be used inside these libraries (the libraries each have several classes and such, so it is not possible to inject the receiver every time, and we need to use the stored reference). Questions: + + + +Yes, you get the same object. + + +It remains valid for the lifetime of the component to which it got injected. Therefore, if you share component references through some other mean than direct or indirect injection you may end up with invalid references. A "container-wide object" sounds like trouble. You should have it injected into all the components that needs it instead. Or, if you feel that will be too fine-grained, create one large object which gets these things injected, and then have that injected into all components that need the common stuff. + + \ No newline at end of file diff --git a/en/operations/monitoring.html b/en/operations/monitoring.html deleted file mode 100644 index c14267ff2f..0000000000 --- a/en/operations/monitoring.html +++ /dev/null @@ -1,799 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: Monitoring -applies_to: cloud -redirect_from: -- /en/cloud/monitoring.html ---- - -Sample Vespa Console dashboard -

    - The Vespa Cloud Console has dashboards for insight into performance metrics, - use the METRICS tab in the application zone view. -

    -

    - These metrics can also be pulled into external monitoring tools using the Prometheus metrics API. -

    - - - -

    The Vespa Cloud metrics dashboard

    -

    - The Vespa Cloud metrics dashboard (the METRICS tab in the application zone view) - is organized around a symptom → layer → resource workflow, - so an investigation that starts from "latency is up" can land on "this specific - layer is the bottleneck" without scanning every chart. -

    - - -

    Tabs and filters

    -Dashboard tab bar -

    The dashboard is organized into seven tabs:

    - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    TabWhat it showsWhen to use it
    OverviewHealth indicators, request rates, QoS, latency summary, HTTP status codes, resource utilizationDaily health check, first stop during incidents
    QueryContainer- and content-node query latency, per-rank-profile breakdown, match/docsum executorsInvestigating read latency, query quality issues
    FeedFeed operation rates and latency at each layer, feed blockingInvestigating write latency or throughput issues
    Nearest Neighbor SearchNNS distance computations, visit efficiencyTuning HNSW parameters (hidden when not in use)
    Content NodeDocument counts, Proton resource usage, executor utilization, maintenance jobsDeep investigation of search engine internals
    ResourcesCPU, memory, disk, GPU, JVM, thread poolsSizing and scaling decisions
    HealthCluster state, data consistency, restarts, reindexing, resource limitsStability monitoring, post-incident review
    -

    Filters at the top apply across all tabs:

    -
      -
    • Cluster — limit metrics to specific clusters
    • -
    • Per host metrics — toggle between aggregated cluster view and per-node breakdown
    • -
    • Rank Profile — filter per-rank-profile panels on the Query tab (defaults to "All")
    • -
    -

    - Query, Feed, Content Node, Resources, and Health tabs group metrics per cluster — - you see all metrics for one cluster before scrolling to the next. - Container metrics are grouped per container cluster, content metrics per content cluster. -

    - - -

    Annotations

    -Latency chart with Service restart and Core dump annotations -

    - Annotations are vertical lines drawn on every chart that mark operational events. - When a latency or throughput anomaly lines up with an annotation, you get - the context for the change without having to infer it from the graph alone. -

    - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    AnnotationTriggered byWhy it matters
    Feed blocked in clusterA content node crosses its disk/memory feed-block limitWrites are paused cluster-wide until remediated
    Vespa upgradeA new Vespa version is rolled outBrief rolling-restart latency spikes are expected around this marker
    Data migrationBucket merges pending exceed a thresholdExplains elevated CPU/IO and latency during redistribution
    Document re-indexingA reindexing job is runningExplains elevated CPU and search-side load
    Auto-scalingThe autoscaler changed the cluster shapeBrief capacity drop during reshuffle
    Service restartdelta(sentinel_totalRestarts[10m]) > 0 — a Vespa service process restarted on one or more nodesUnexpected restarts usually indicate a crash, OOM, or forced stop; outside of planned upgrades these are always worth investigating
    Core dumpdelta(coredumps_processed[1h]) > 0 — a process core-dumpedSignals a crash; cross-reference with Service restart. Should be extremely rare
    - - -

    Overview tab

    -

    - The Overview tab is the fastest place to answer "is anything obviously broken?" - and provides everything needed for daily monitoring at a glance. -

    - -

    Health Indicators

    -Overview tab Health Indicators row -

    - The Overview tab opens with a dedicated Health Indicators row — - five stat panels designed to surface stability issues in a single glance. - A row of green zeros is the signal to stop; a non-zero value tells you which tab to visit next. -

    - - - - - - - - - - - - - - - - - - - - - -
    IndicatorWhat it countsHealthy value
    Core Dumps (1h)Core dumps processed across all clusters in the last hour0 — any non-zero value is a crash to investigate
    Restarts (1h)Vespa service restarts across all clusters in the last hour0 during steady state; brief spikes are normal during upgrades
    Feed BlockedNodes currently above a feed-block resource limit0 — non-zero means writes are being rejected cluster-wide
    Content: Groups/Nodes DownContent groups with at least one node down0 during steady state. 1 group down is normal during rolling restarts or maintenance; 2 or more should be investigated
    Container: Services DownActive container nodes where some service isn't running0 during steady state; brief spikes during deployments are expected
    - -

    QoS and latency overview

    -

    - QoS (Quality of Service) shows the percentage of successful requests. - Read and write QoS are shown separately; a healthy application should be above 99.9%. - If QoS drops, consult the HTTP Response Code Reference row (collapsed by default) - for a table explaining every observed status code and its meaning in Vespa context. - 4xx responses are client errors; 5xx responses are server errors and should be - investigated immediately. -

    -

    - Latency summary separates query and feed latency into read and write rows. - Compare averages with p99 — a large gap indicates tail latency that won't show up - in averages. As a rule of thumb, if p99 is more than 5× the average, investigate the tail. -

    - -

    Resource utilization

    -

    - The bottom row gives a quick view of CPU, memory, and disk across all clusters. - Any resource consistently above 80% warrants attention. -

    - - -

    Query tab

    -

    - When query latency increases, the Query tab helps find the cause layer-by-layer. - Metrics are grouped per container cluster (for container-level metrics) and - per content cluster (for content-node metrics). -

    -

    A query flows through multiple layers, each with its own latency metric:

    -
    -Client
    -  → HTTP Read Latency             (end-to-end including network I/O)
    -    → Query Container Latency     (time in the container itself)
    -      → Query Latency             (container-observed total, excluding HTTP overhead)
    -        → Search Protocol Latency (time on each content node)
    -          → Rank Profile Latency  (per rank-profile breakdown)
    -
    - -

    Container-level metrics

    -

    Start with the Query Rate & Latency row:

    -
      -
    • Did QPS increase? More queries means more load.
    • -
    • Which latency metric increased? -
        -
      • Query Latency — container level, includes dispatch to content nodes
      • -
      • HTTP Read Latency — includes HTTP I/O overhead
      • -
      • Search Protocol Latency — content node execution only
      • -
      -
    • -
    -

    - If HTTP latency is much higher than query latency, the bottleneck is network or payload size. - If search protocol latency dominates, the bottleneck is on the content nodes. -

    -

    The Query Quality row shows:

    -
      -
    • Failed queries — actual errors. Should be near zero.
    • -
    • Degraded queries — queries that were - soft-doomed (ran out of time during matching). - These return partial results.
    • -
    • Empty results — queries returning zero hits. - A sudden increase may indicate an indexing problem or a query change.
    • -
    - -

    Rank profile metrics

    -Query tab rank profile sub-rows -

    - The Query tab groups per-rank-profile metrics into four sub-rows, all filterable by - the Rank Profile dropdown: -

    -
      -
    • Rank Profile — Latency & Volume — - query latency (avg and max), QPS per profile, and raw docs matched per profile
    • -
    • Rank Profile — Time Breakdown — - setup time, rerank time, and grouping time, each shown as avg plus peak - so you can tell whether a profile has steady-state cost or occasional cost spikes
    • -
    • Rank Profile — Quality — - docs matched per query, soft-doom factor, and soft-doomed queries. - These tell you when a profile is - overrunning its time budget.
    • -
    • Rank Profile — Query Distribution — - QPS split by content group, which helps spot uneven routing
    • -
    -

    Things to look for:

    -
      -
    • Which rank profile has the highest latency?
    • -
    • Are soft-doomed queries concentrated on a specific rank profile?
    • -
    • Is the peak for rerank or grouping time much higher than the average? - That often points to a specific second-phase or grouping expression that's expensive - only on some queries.
    • -
    • Did docs matched per query grow? More documents matched means more ranking work.
    • -
    -

    - See Latency tracking below for a worked example, - and the - rank profiles documentation for background. -

    - -

    Match and Docsum executor panels

    -

    - The Query tab also includes Match Executor and Docsum Executor sub-rows - (queue size + accepted rate) so you can see whether the content-node thread pools - feeding the query and summary paths are saturated. These are not attributable to a - rank profile, but often explain tail-latency spikes that aren't visible in rank-profile metrics. -

    - - -

    Feed tab

    -

    When feed latency increases or throughput drops, the Feed tab shows where in the write path - the slowdown occurs. A write operation flows through:

    -
    -Client
    -  → HTTP Write Latency            (end-to-end)
    -    → Container Feed Latency      (document processing chains, embedders)
    -      → Distributor Latency       (routing based on bucket distribution)
    -        → Content: Storage Latency(persistence, per document replica)
    -          → Commit Latency        (transaction log)
    -
    -

    Start from the top and find where latency increases. - If container feed latency is normal but HTTP write latency is high, the bottleneck is network/payload. - If distributor latency is high, check for node state issues in the Health tab. - If storage latency is high, check disk I/O in the Resources tab.

    - -

    Typical healthy values

    -
      -
    • Feed latency: 1–50 ms for puts/updates is typical; spikes during maintenance are normal
    • -
    • Distributor failures: zero — non-zero indicates node state issues
    • -
    • HTTP API failures: near zero
    • -
    • Feed blocked: always zero
    • -
    - -

    Feed blocked

    -

    - Feed Blocked is the most critical feed metric. When a content node exceeds its - disk or memory resource limit, feeding is paused for - the entire cluster. HTTP clients receive 507 Insufficient Storage. -

    -

    If feed is being blocked:

    -
      -
    1. Check Health > Feed Resource Limits for which resource is near the limit.
    2. -
    3. Check the Resources tab for the specific nodes causing pressure.
    4. -
    5. Add nodes to the content cluster (always add, don't resize — - data auto-redistributes).
    6. -
    -

    - The Health tab includes a Resource Limits Reference panel explaining the default limits, - the blocking mechanism, and how to remediate. -

    - - -

    Nearest Neighbor Search tab

    -

    - This tab only appears when the application uses - approximate nearest neighbor search — - it is automatically hidden when no NNS distance computations are detected. -

    -

    Vespa supports two NNS modes:

    -
      -
    • Approximate NNS — uses an HNSW graph index to find neighbors efficiently - without scanning every document. Fast, but may miss some true nearest neighbors.
    • -
    • Exact NNS — brute-force scan computing distance to every document. - Accurate but expensive. Vespa falls back to this when the filter hit ratio is below the - approximate-threshold (default 0.02).
    • -
    -

    Key metrics:

    -
      -
    • Exact NNS Ratio — fraction of queries using brute-force search. - Should be below 0.05 (5%). High values mean many queries fall back to exact search, - significantly increasing cost.
    • -
    • Approx NNS Visit Efficiency — ratio of graph nodes visited to - distances computed. Values of 1.0–3.0 are typical; much higher suggests the HNSW index - could be tuned.
    • -
    • Distances Computed / Nodes Visited — rate metrics showing the raw - NNS workload.
    • -
    -

    - Tuning parameters (set per - rank profile): - approximate-threshold, filter-first-threshold, - target-hits-max-adjustment-factor, exploration-slack. - If the exact NNS ratio is high, consider increasing approximate-threshold - or restructuring filters to be less restrictive. -

    - - -

    Content Node tab

    -

    The Content Node tab shows internals of the - Proton search engine running on each content node. - All metrics are grouped per content cluster.

    - -

    Documents

    -
      -
    • Total — all documents in the database (including removed)
    • -
    • Ready — documents available for search
    • -
    • Active — primary copies that should be searchable on this node
    • -
    • Removed — tombstones pending garbage collection
    • -
    - -

    Proton resource usage

    -

    - Disk and memory usage from Proton's internal accounting. This is distinct from node-level - metrics in the Resources tab — these are the values Vespa uses for - feed-blocking decisions. -

    - -

    Executor utilization

    -

    Proton uses several thread pools (executors):

    -
      -
    • Match — executes queries. Directly impacts query latency.
    • -
    • Shared — handles background tasks like flush and compaction.
    • -
    • Proton — internal coordination tasks.
    • -
    • Field writer — writes attribute and index data during feeding. - Saturation directly impacts feed throughput.
    • -
    -

    Typical healthy values:

    -
      -
    • Utilization below 0.8 (80%) — sustained values above this are a bottleneck
    • -
    • Field writer saturation well below 1.0
    • -
    • Queue sizes near zero during steady state
    • -
    -

    - The dashboard renders avg as a solid green line and max as a dashed yellow line, - making it easy to spot whether the maximum tracks the average or has concerning spikes. -

    - -

    Maintenance jobs

    -

    - Proton runs background - maintenance jobs - that manage data structures. The dashboard includes a reference panel (collapsed) - explaining each job and its resource impact: -

    - - - - - - - - - - -
    JobResource impact
    Attribute FlushLow
    Memory Index FlushModerate
    Disk Index FusionHigh — temporary 2× disk usage
    Document Store CompactionHigh — holds file in memory
    Bucket MoveHigh — competes with feeding
    LID-Space CompactionModerate
    -

    - Latency spikes that correlate with active maintenance are expected but may indicate - the cluster needs more headroom. -

    - - -

    Resources tab

    -

    - The Resources tab is the primary tool for sizing decisions. Node-level resources - (CPU, memory, disk) are grouped per cluster. Container-specific metrics - (JVM, thread pools, GPU, network) are grouped per container cluster. -

    - -

    Typical healthy values

    - - - - - - - - - - - - -
    ResourceHealthyConcerningAction needed
    CPU< 70%70–85%> 85% sustained
    CPU IOWait< 5%5–10%> 10% (I/O bottleneck)
    Memory< 70%70–80%Approaching feed-block limit
    Disk< 70%70–80%Approaching feed-block limit
    JVM GC Overhead< 5%5–15%> 15% (severe latency impact)
    Threadpool utilization< 70%70–90%Rejected tasks = requests dropped
    -

    - Content nodes need extra headroom because - maintenance jobs - (especially disk index fusion) temporarily increase resource usage. -

    - -

    Container thread pools

    -Container thread pools row with per-pool avg/max panels -

    Which thread pools exist on a container depends on which elements are configured - in services.xml:

    - - - - - - - -
    Thread poolPresent when
    default-handler-commonAlways (handler executor used by anything without its own pool)
    search-handler<search> element is present
    feedapi-handler<document-api> element is present
    -

    - To keep the dashboard free of empty panels, the Resources tab contains three - threadpool rows — one per container configuration case — and each row - repeats per container cluster that falls into that case: -

    -
      -
    • Container Thread Pools (search + document-api) — clusters with both pools
    • -
    • Container Thread Pools (search only) — clusters with <search> but no feed API
    • -
    • Container Thread Pools (document-api only) — feed-only clusters
    • -
    -

    - Classification is automatic: hidden variables derive the cluster list per case, - so only relevant rows render for a given deployment. - Each pool gets three panels — Utilization, - Work Queue Size, Work Queue Utilization — - with avg as a solid green line and max as a dashed yellow line. -

    -
      -
    • Utilization — active threads as percentage of pool size
    • -
    • Work queue size — tasks waiting for a thread. - The default pool uses a synchronous queue (capacity 0), so there is no buffering — - if no thread is available, the task is rejected.
    • -
    • Queue utilization — percentage of configured queue capacity used - (only meaningful for thread pools with bounded queues)
    • -
    - -

    JVM memory breakdown

    -JVM memory breakdown: heap, direct, native, GC -

    The Resources tab's JVM row separates the three layers of container memory:

    -
      -
    • JVM Heap Usage — Java objects (searchers, document processors, caches)
    • -
    • JVM Direct Memory — NIO buffers, Netty pools
    • -
    • JVM Native Memory — JNI allocations, including ONNX embedder working - memory and — if configured — a local LLM's KV cache and compute buffers
    • -
    -

    - When overall node memory is high but heap and direct look normal, the native layer - is usually the answer. This is common on container nodes running embedder or local-LLM - components: model weights are memory-mapped and only partially resident, but KV cache - and compute buffers are allocated upfront as native memory. -

    - - -

    Health tab

    -

    The Health tab tracks cluster stability and data consistency, grouped per content cluster.

    - -

    Cluster state

    -

    - Nodes are distributed across states: up (serving), - down (unreachable), initializing (starting up), - maintenance (temporarily out), retired (being removed). - During normal operation: all up, zero down. - See content node states. -

    - -

    Data consistency

    -
      -
    • Buckets Out of Sync — percentage of - data buckets not yet replicated/consistent. - Should be 0% during steady state; non-zero during scaling, restarts, or failures.
    • -
    • Merge Pending — bucket merge operations queued. - High during data redistribution.
    • -
    -

    - After scaling events, expect buckets out of sync and pending merges. - These should converge back to zero. If they don't, investigate. -

    - -

    Stability

    -
      -
    • Service Restarts — cumulative restarts per cluster. - An increase indicates a process crash.
    • -
    • Core Dumps — should always be zero.
    • -
    -

    - Both signals surface in three complementary ways: as per-cluster time series on this tab - (for historical context), as at-a-glance counters in the - Health Indicators row on the Overview tab, - and as Service restart/Core dump - annotations drawn as vertical lines on every chart. -

    - -

    Feed Resource Limits

    -

    - Shows memory and disk utilization vs. configured limits. When utilization exceeds the limit, - feeding is blocked. - The dashboard includes a Resource Limits Reference panel (collapsed) explaining the - default limits (disk 80%, memory 80%), the blocking mechanism, and what to do about it. -

    - - -

    Common workflows

    - -

    "Our query latency increased"

    -
      -
    1. Overview: confirm the latency increase, check if QPS also changed.
    2. -
    3. Query: which percentile increased most? (avg vs p95 vs p99)
    4. -
    5. Query → Rank Profile: is it one query type or all?
    6. -
    7. Resources: CPU, JVM GC overhead.
    8. -
    9. Content Node: match executor utilization, queue sizes growing?
    10. -
    - -

    "Feed is slow / feed is blocked"

    -
      -
    1. Overview: check feed latency and feed operation rate.
    2. -
    3. Feed: which layer shows increased latency?
    4. -
    5. Health: is feed blocked? Check resource limits.
    6. -
    7. Resources: disk and memory utilization, CPU IOWait.
    8. -
    9. Content Node: field writer saturation, filestor queue, active maintenance jobs.
    10. -
    - -

    "Should we scale up?"

    -
      -
    1. Resources: identify the bottleneck resource.
    2. -
    3. Content Node: check executor utilization (are we compute-bound?).
    4. -
    5. Per host view: is load evenly distributed?
    6. -
    7. Enable autoscaling or adjust resources in - services.xml.
    8. -
    9. See the benchmarking guide - for systematic capacity testing.
    10. -
    - - - -

    Latency tracking

    -

    - When monitoring latency in clusters with mixed loads, - it is useful to use rank profiles to separate them. - As an example, an application might have user queries mixed with agentic, batch-oriented queries. - Tracking the Container-level query latencies might look like: -

    -total query latency chart -

    - Using Content node level metrics, separated by ranking profile, we see: -

    -query latency per rank profile chart -

    - From this, we see that query latency varies with the rank profile used. - Relevant metrics to export to your monitoring system include: -

    - -

    In short, when debugging latency, look for changes, per rank profile:

    -
      -
    • Did the query rate increase?
    • -
    • - Did number of matched - or ranked documents change? -
    • -
    -

    - The above metrics is a subset or the available metrics. - It is a good idea to set a query profile per class of query, - and in each query profile, select a distinct rank profile. - With this, you can change the rank profile for a given query class by configuration only (no need to change the clients) - - a good example is having a lightweight rank profile to use in overload situations. - This makes it easier to track the individual query classes, per rank profile. -

    - - - -

    Exporting telemetry to your own backend

    -

    - In addition to pulling metrics with the Prometheus metrics API, - Vespa Cloud can push your application's metrics and logs to an external observability backend - (OTLP, Google Cloud, …) — self-service telemetry export. Vespa Cloud runs a managed collector - on your hosts that exports the data to the backend you configure. - See Telemetry export. -

    -

    - This feature is available for Vespa Cloud Enclave only. -

    - - -

    Prometheus metrics API

    -

    - Prometheus metrics are found at $ENDPOINT/prometheus/v1/values: -

    -
    -$ curl -s --cert data-plane-public-cert.pem --key data-plane-private-key.pem \
    -  'https://b6718765.b68a1234.z.vespa-app.cloud/prometheus/v1/values'
    -
    -

    - The metrics can be fed into e.g. your Grafana Cloud or self-hosted Grafana instance. - See the Vespa metrics documentation for more information. -

    - - -

    Using Grafana

    -

    - This section explains how to set up Grafana to consume Vespa metrics using the Prometheus API. -

    - - -

    1. Prometheus configuration

    -

    - Prometheus is configured using prometheus.yml, find sample config in - prometheus. - See prometheus-cloud.yml, - which is designed to be easy to set up with any Vespa Cloud instance. - Replace <Endpoint> and <SERVICE_NAME> with the endpoint - for the application and the service name, respectively. - In addition, the path to the private key and public cert - that is used for the data plane to the endpoint need to be provided - - refer to security. - Then, configure the Prometheus instance to use this configuration file. - The Prometheus instance will now start retrieving the metrics from Vespa Cloud. - If the Prometheus instance is used for multiple services, - append the target configuration for Vespa to scrape_configs. -

    - - -

    2. Grafana configuration

    -

    - Use the - provisioning folder - as a baseline for further configuration. -

    -

    - In the provisioning folder there are a few different files that all help for configuring Grafana locally. - These work as good examples of default configurations, - but the most important is the file named Vespa-Engine-Advanced-Metrics-External.json. - This is a default dashboard, based upon the metrics the Vespa team use to monitor performance. -

    -

    - Click the + button on the side and go to import. - Upload the file to the Grafana instance. - This should automatically load in the dashboard for usage. - For now, it will not display any data as no data sources are configured yet. -

    - - -

    3. Grafana Data Source

    -

    - The Prometheus data source has to be added to the Grafana instance for the visualisation. - Click the cog on the left and then "Data Sources". - Click "Add data source" and choose Prometheus from the list. - Add the URL for the Prometheus instance with appropriate bindings for connecting. - The configuration for the bindings will depend on how the Prometheus instance is hosted. - Once the configuration details have been entered, - click Save & Test at the bottom and ensure that Grafana says "Data source is working". -

    -

    - To verify the data flow, - navigate back to the Vespa Metrics dashboard by clicking the dashboard symbol on the left (4 blocks) - and clicking manage and then click Vespa Metrics. - Data should now appear in the Grafana dashboard. - If no data shows up, edit one of the data sets and ensure that it has the right data source selected. - The name of the data source the dashboard is expecting might be different from what your data source is named. - If there is still no data appearing, - it either means that the Vespa instance is not being used - or that some part of the configuration is wrong. -

    - - -

    Using AWS Cloudwatch

    -

    - To pull metrics from your Vespa application into AWS Cloudwatch, refer to the - metrics-emitter documentation - for how to set up an AWS Lambda. -

    - - -

    Alerting

    -

    - The Vespa Grafana Terraform template - provides a set of dashboards and alerts. - If you are using a different monitoring service and want to set up an equivalent alert set, you can follow this table: -

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    Metric nameThresholdDimension aggregation
    content_proton_resource_usage_disk_average> 0.9max by(applicationId, clusterId, zone)
    content_proton_resource_usage_memory_average> 0.8max by(applicationId, zone, clusterId)
    cpu_util> 90max by(applicationId, zone, clusterId)
    content_proton_resource_usage_feeding_blocked_last>= 1N/A
    -

    - All metrics are from the default metric set. - Metrics are using the naming scheme from the Prometheus metrics API. - Dimension aggregation is optional, but reduces alerting noise - e.g. in the case where an entire cluster goes bad. - It is recommended to filter all alerts on zones in the prod environment. -

    - - -

    Prometheus Metrics Sample

    -

    - Below is a sample request with sample response for prometheus metrics for a minimal application on Vespa Cloud: -

    -
    -$ curl -s --cert data-plane-public-cert.pem --key data-plane-private-key.pem \
    -  'https://b6718765.b68a1234.z.vespa-app.cloud/prometheus/v1/values'
    -
    -...
    -jdisc_thread_pool_work_queue_size_min{threadpool="default-pool",zone="dev.aws-us-east-1c",applicationId="mytenant.myapp.default",serviceId="logserver-container",clusterId="admin/logserver",hostname="h97490a.dev.us-east-1c.aws.vespa-cloud.net",vespa_service="vespa_logserver_container",} 0.0 1733139324000
    -jdisc_thread_pool_work_queue_size_min{threadpool="default-handler-common",zone="dev.aws-us-east-1c",applicationId="mytenant.myapp.default",serviceId="logserver-container",clusterId="admin/logserver",hostname="h97490a.dev.us-east-1c.aws.vespa-cloud.net",vespa_service="vespa_logserver_container",} 0.0 1733139324000
    -# HELP content_proton_documentdb_matching_rank_profile_rerank_time_average 
    -# TYPE content_proton_documentdb_matching_rank_profile_rerank_time_average untyped
    -content_proton_documentdb_matching_rank_profile_rerank_time_average{rankProfile="rank_albums",documenttype="music",zone="dev.aws-us-east-1c",applicationId="mytenant.myapp.default",serviceId="searchnode",clusterId="content/music",hostname="h104562a.dev.us-east-1c.aws.vespa-cloud.net",vespa_service="vespa_searchnode",} 0.0 1733139324000
    -content_proton_documentdb_matching_rank_profile_rerank_time_average{rankProfile="unranked",documenttype="music",zone="dev.aws-us-east-1c",applicationId="mytenant.myapp.default",serviceId="searchnode",clusterId="content/music",hostname="h104562a.dev.us-east-1c.aws.vespa-cloud.net",vespa_service="vespa_searchnode",} 0.0 1733139324000
    -content_proton_documentdb_matching_rank_profile_rerank_time_average{rankProfile="default",documenttype="music",zone="dev.aws-us-east-1c",applicationId="mytenant.myapp.default",serviceId="searchnode",clusterId="content/music",hostname="h104562a.dev.us-east-1c.aws.vespa-cloud.net",vespa_service="vespa_searchnode",} 0.0 1733139324000
    -...
    -
    -

    Relevant labels include:

    -
      -
    • - chain This is the name on the search chain in the container that is used for a set of query requests. - This is typically used to get separate metrics, such as latency and the number of queries for each chain over time. -
    • -
    • - documenttype This is the name of the document type for which a set of queries are run in the content clusters. - This is typically used to get separate content layer metrics, such as latency and the number of queries for each chain over time. -
    • -
    • - groupId This is the id of the cluster group for which the metric measurement is done. - This is typically used to get separate metrics aggregates per group in a content cluster. - The label is most relevant for metrics from the content clusters running multiple content groups, - see Content Cluster Elasticity. - The value is in the format group 0, group 1, group 2, etc. -
    • -
    • - rankProfile This label is present for a subset of metrics from the content clusters, - with names starting with content_proton_documentdb_matching_rank_profile_. - The label is typically used in cases where you use multiple rank profiles - and want to analyse performance differences between the different rank profiles, - or to better understand certain types of performance issues and need to narrow down the candidate set. -
    • -
    • - source This is a label applied on container metrics for classifying query failures by the content cluster - where the failure was triggered. -
    • -
    -

    - How you will use labels to separate different kinds of queries depends on the observability backend you use, - but you will typically compute weighted averages for query latency and query volume, - and split graphs by the relevant labels to better understand system performance and bottlenecks. -

    -

    - For the container level metrics you use the chain label to differentiate between different query streams, - while you use the rankProfile label to do the same in the content level. -

    diff --git a/en/operations/monitoring.mdx b/en/operations/monitoring.mdx new file mode 100644 index 0000000000..44308555ea --- /dev/null +++ b/en/operations/monitoring.mdx @@ -0,0 +1,720 @@ +--- +title: "Monitoring" +--- + + +![Sample Vespa Console dashboard](/assets/img/grafana-metrics.png) + + +The Vespa Cloud Console has dashboards for insight into performance metrics, use the METRICS tab in the application zone view. + +These metrics can also be pulled into external monitoring tools using the Prometheus metrics API. + +## The Vespa Cloud metrics dashboard + +The Vespa Cloud metrics dashboard (the METRICS tab in the application zone view) is organized around a *symptom → layer → resource* workflow, so an investigation that starts from "latency is up" can land on "this specific layer is the bottleneck" without scanning every chart. + +### Tabs and filters + + +![Dashboard tab bar](/assets/img/monitoring-dashboard-tabs.png) + + +The dashboard is organized into seven tabs: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    TabWhat it showsWhen to use it
    **Overview**Health indicators, request rates, QoS, latency summary, HTTP status codes, resource utilizationDaily health check, first stop during incidents
    **Query**Container- and content-node query latency, per-rank-profile breakdown, match/docsum executorsInvestigating read latency, query quality issues
    **Feed**Feed operation rates and latency at each layer, feed blockingInvestigating write latency or throughput issues
    **Nearest Neighbor Search**NNS distance computations, visit efficiencyTuning HNSW parameters (hidden when not in use)
    **Content Node**Document counts, Proton resource usage, executor utilization, maintenance jobsDeep investigation of search engine internals
    **Resources**CPU, memory, disk, GPU, JVM, thread poolsSizing and scaling decisions
    **Health**Cluster state, data consistency, restarts, reindexing, resource limitsStability monitoring, post-incident review
    + +Filters at the top apply across all tabs: + +- **Cluster** — limit metrics to specific clusters +- **Per host metrics** — toggle between aggregated cluster view and per-node breakdown +- **Rank Profile** — filter per-rank-profile panels on the Query tab (defaults to "All") + +Query, Feed, Content Node, Resources, and Health tabs group metrics per cluster — you see all metrics for one cluster before scrolling to the next. Container metrics are grouped per container cluster, content metrics per content cluster. + +### Annotations + + +![Latency chart with Service restart and Core dump annotations](/assets/img/monitoring-annotations-example.png) + + +Annotations are vertical lines drawn on every chart that mark operational events. When a latency or throughput anomaly lines up with an annotation, you get the context for the change without having to infer it from the graph alone. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    AnnotationTriggered byWhy it matters
    **Feed blocked in cluster**A content node crosses its disk/memory feed-block limitWrites are paused cluster-wide until remediated
    **Vespa upgrade**A new Vespa version is rolled outBrief rolling-restart latency spikes are expected around this marker
    **Data migration**Bucket merges pending exceed a thresholdExplains elevated CPU/IO and latency during redistribution
    **Document re-indexing**A reindexing job is runningExplains elevated CPU and search-side load
    **Auto-scaling**The autoscaler changed the cluster shapeBrief capacity drop during reshuffle
    **Service restart**{`delta(sentinel_totalRestarts[10m]) > 0`} — a Vespa service process restarted on one or more nodesUnexpected restarts usually indicate a crash, OOM, or forced stop; outside of planned upgrades these are always worth investigating
    **Core dump**{`delta(coredumps_processed[1h]) > 0`} — a process core-dumpedSignals a crash; cross-reference with Service restart. Should be extremely rare
    + +### Overview tab + +The Overview tab is the fastest place to answer "is anything obviously broken?" and provides everything needed for daily monitoring at a glance. + +#### Health Indicators + + +![Overview tab Health Indicators row](/assets/img/monitoring-health-indicators.png) + + +The Overview tab opens with a dedicated **Health Indicators** row — five stat panels designed to surface stability issues in a single glance. A row of green zeros is the signal to stop; a non-zero value tells you which tab to visit next. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    IndicatorWhat it countsHealthy value
    **Core Dumps (1h)**Core dumps processed across all clusters in the last hour0 — any non-zero value is a crash to investigate
    **Restarts (1h)**Vespa service restarts across all clusters in the last hour0 during steady state; brief spikes are normal during upgrades
    **Feed Blocked**Nodes currently above a feed-block resource limit0 — non-zero means writes are being rejected cluster-wide
    **Content: Groups/Nodes Down**Content groups with at least one node down0 during steady state. 1 group down is normal during rolling restarts or maintenance; 2 or more should be investigated
    **Container: Services Down**Active container nodes where some service isn't running0 during steady state; brief spikes during deployments are expected
    + +#### QoS and latency overview + +**QoS (Quality of Service)** shows the percentage of successful requests. Read and write QoS are shown separately; a healthy application should be above 99.9%. If QoS drops, consult the HTTP Response Code Reference row (collapsed by default) for a table explaining every observed status code and its meaning in Vespa context. 4xx responses are client errors; 5xx responses are server errors and should be investigated immediately. + +**Latency summary** separates query and feed latency into read and write rows. Compare averages with p99 — a large gap indicates tail latency that won't show up in averages. As a rule of thumb, if p99 is more than 5× the average, investigate the tail. + +#### Resource utilization + +The bottom row gives a quick view of CPU, memory, and disk across all clusters. Any resource consistently above 80% warrants attention. + +### Query tab + +When query latency increases, the Query tab helps find the cause layer-by-layer. Metrics are grouped per container cluster (for container-level metrics) and per content cluster (for content-node metrics). + +A query flows through multiple layers, each with its own latency metric: + +```bash +Client + → HTTP Read Latency (end-to-end including network I/O) + → Query Container Latency (time in the container itself) + → Query Latency (container-observed total, excluding HTTP overhead) + → Search Protocol Latency (time on each content node) + → Rank Profile Latency (per rank-profile breakdown) +``` + +#### Container-level metrics + +Start with the *Query Rate & Latency* row: + +- Did QPS increase? More queries means more load. +- Which latency metric increased? + - **Query Latency** — container level, includes dispatch to content nodes + - **HTTP Read Latency** — includes HTTP I/O overhead + - **Search Protocol Latency** — content node execution only + +If HTTP latency is much higher than query latency, the bottleneck is network or payload size. If search protocol latency dominates, the bottleneck is on the content nodes. + +The *Query Quality* row shows: + +- **Failed queries** — actual errors. Should be near zero. +- **Degraded queries** — queries that were [soft-doomed](/en/performance/graceful-degradation) (ran out of time during matching). These return partial results. +- **Empty results** — queries returning zero hits. A sudden increase may indicate an indexing problem or a query change. + +#### Rank profile metrics + + +![Query tab rank profile sub-rows](/assets/img/monitoring-rank-profile-rows.png) + + +The Query tab groups per-rank-profile metrics into four sub-rows, all filterable by the Rank Profile dropdown: + +- **Rank Profile — Latency & Volume** — query latency (avg and max), QPS per profile, and raw docs matched per profile +- **Rank Profile — Time Breakdown** — setup time, rerank time, and grouping time, each shown as avg plus peak so you can tell whether a profile has steady-state cost or occasional cost spikes +- **Rank Profile — Quality** — docs matched per query, soft-doom factor, and soft-doomed queries. These tell you when a profile is [overrunning its time budget](/en/performance/graceful-degradation). +- **Rank Profile — Query Distribution** — QPS split by content group, which helps spot uneven routing + +Things to look for: + +- Which rank profile has the highest latency? +- Are soft-doomed queries concentrated on a specific rank profile? +- Is the peak for rerank or grouping time much higher than the average? That often points to a specific second-phase or grouping expression that's expensive only on some queries. +- Did docs matched per query grow? More documents matched means more ranking work. + +See [Latency tracking](#latency-tracking) below for a worked example, and the [rank profiles](/en/basics/ranking#rank-profiles) documentation for background. + +#### Match and Docsum executor panels + +The Query tab also includes *Match Executor* and *Docsum Executor* sub-rows (queue size + accepted rate) so you can see whether the content-node thread pools feeding the query and summary paths are saturated. These are not attributable to a rank profile, but often explain tail-latency spikes that aren't visible in rank-profile metrics. + +### Feed tab + +When feed latency increases or throughput drops, the Feed tab shows where in the write path the slowdown occurs. A write operation flows through: + +```bash +Client + → HTTP Write Latency (end-to-end) + → Container Feed Latency (document processing chains, embedders) + → Distributor Latency (routing based on bucket distribution) + → Content: Storage Latency(persistence, per document replica) + → Commit Latency (transaction log) +``` + +Start from the top and find where latency increases. If container feed latency is normal but HTTP write latency is high, the bottleneck is network/payload. If distributor latency is high, check for node state issues in the Health tab. If storage latency is high, check disk I/O in the Resources tab. + +#### Typical healthy values + +- Feed latency: 1–50 ms for puts/updates is typical; spikes during maintenance are normal +- Distributor failures: zero — non-zero indicates node state issues +- HTTP API failures: near zero +- Feed blocked: always zero + +#### Feed blocked + +**Feed Blocked** is the most critical feed metric. When a content node exceeds its disk or memory [resource limit](/en/writing/feed-block), feeding is paused for the entire cluster. HTTP clients receive `507 Insufficient Storage`. + +If feed is being blocked: + + + +Check *Health > Feed Resource Limits* for which resource is near the limit. + + +Check the Resources tab for the specific nodes causing pressure. + + +Add nodes to the content cluster (always add, don't resize — data [auto-redistributes](/en/content/elasticity)). + + + +The Health tab includes a Resource Limits Reference panel explaining the default limits, the blocking mechanism, and how to remediate. + +### Nearest Neighbor Search tab + +This tab only appears when the application uses [approximate nearest neighbor search](/en/querying/approximate-nn-hnsw) — it is automatically hidden when no NNS distance computations are detected. + +Vespa supports two NNS modes: + +- **Approximate NNS** — uses an HNSW graph index to find neighbors efficiently without scanning every document. Fast, but may miss some true nearest neighbors. +- **Exact NNS** — brute-force scan computing distance to every document. Accurate but expensive. Vespa falls back to this when the filter hit ratio is below the `approximate-threshold` (default 0.02). + +Key metrics: + +- **Exact NNS Ratio** — fraction of queries using brute-force search. Should be below 0.05 (5%). High values mean many queries fall back to exact search, significantly increasing cost. +- **Approx NNS Visit Efficiency** — ratio of graph nodes visited to distances computed. Values of 1.0–3.0 are typical; much higher suggests the HNSW index could be tuned. +- **Distances Computed / Nodes Visited** — rate metrics showing the raw NNS workload. + +Tuning parameters (set per [rank profile](/en/basics/ranking#rank-profiles)): `approximate-threshold`, `filter-first-threshold`, `target-hits-max-adjustment-factor`, `exploration-slack`. If the exact NNS ratio is high, consider increasing `approximate-threshold` or restructuring filters to be less restrictive. + +### Content Node tab + +The Content Node tab shows internals of the [Proton](/en/content/proton) search engine running on each content node. All metrics are grouped per content cluster. + +#### Documents + +- **Total** — all documents in the database (including removed) +- **Ready** — documents available for search +- **Active** — primary copies that should be searchable on this node +- **Removed** — tombstones pending garbage collection + +#### Proton resource usage + +Disk and memory usage from Proton's internal accounting. This is distinct from node-level metrics in the Resources tab — these are the values Vespa uses for [feed-blocking](/en/writing/feed-block) decisions. + +#### Executor utilization + +Proton uses several thread pools (executors): + +- **Match** — executes queries. Directly impacts query latency. +- **Shared** — handles background tasks like flush and compaction. +- **Proton** — internal coordination tasks. +- **Field writer** — writes attribute and index data during feeding. Saturation directly impacts feed throughput. + +Typical healthy values: + +- Utilization below 0.8 (80%) — sustained values above this are a bottleneck +- Field writer saturation well below 1.0 +- Queue sizes near zero during steady state + +The dashboard renders avg as a solid green line and max as a dashed yellow line, making it easy to spot whether the maximum tracks the average or has concerning spikes. + +#### Maintenance jobs + +Proton runs background [maintenance jobs](/en/content/proton#proton-maintenance-jobs) that manage data structures. The dashboard includes a reference panel (collapsed) explaining each job and its resource impact: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    JobResource impact
    Attribute FlushLow
    Memory Index FlushModerate
    Disk Index FusionHigh — temporary 2× disk usage
    Document Store CompactionHigh — holds file in memory
    Bucket MoveHigh — competes with feeding
    LID-Space CompactionModerate
    + +Latency spikes that correlate with active maintenance are expected but may indicate the cluster needs more headroom. + +### Resources tab + +The Resources tab is the primary tool for sizing decisions. Node-level resources (CPU, memory, disk) are grouped per cluster. Container-specific metrics (JVM, thread pools, GPU, network) are grouped per container cluster. + +#### Typical healthy values + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    ResourceHealthyConcerningAction needed
    **CPU**{`< 70%`}{`70-85%`}{`> 85%`} sustained
    **CPU IOWait**{`< 5%`}{`5-10%`}{`> 10%`} (I/O bottleneck)
    **Memory**{`< 70%`}{`70-80%`}Approaching feed-block limit
    **Disk**{`< 70%`}{`70-80% `}Approaching feed-block limit
    **JVM GC Overhead**{`< 5%`}{`5-15%`}{`> 15%`} (severe latency impact)
    **Threadpool utilization**{`< 70%`}{`70-90%`}Rejected tasks = requests dropped
    + +Content nodes need extra headroom because [maintenance jobs](/en/content/proton#proton-maintenance-jobs) (especially disk index fusion) temporarily increase resource usage. + +#### Container thread pools + + +![Container thread pools row with per-pool avg/max panels](/assets/img/monitoring-container-thread-pools.png) + + +Which thread pools exist on a container depends on which elements are configured in `services.xml`: + + + + + + + + + + + + + + + + + + + + + + +
    Thread poolPresent when
    {`default-handler-common`}Always (handler executor used by anything without its own pool)
    {`search-handler`}{``} element is present
    {`feedapi-handler`}{``} element is present
    + +To keep the dashboard free of empty panels, the Resources tab contains three threadpool rows — one per container configuration case — and each row repeats per container cluster that falls into that case: + +- **Container Thread Pools (search + document-api)** — clusters with both pools +- **Container Thread Pools (search only)** — clusters with `` but no feed API +- **Container Thread Pools (document-api only)** — feed-only clusters + +Classification is automatic: hidden variables derive the cluster list per case, so only relevant rows render for a given deployment. Each pool gets three panels — **Utilization**, **Work Queue Size**, **Work Queue Utilization** — with avg as a solid green line and max as a dashed yellow line. + +- **Utilization** — active threads as percentage of pool size +- **Work queue size** — tasks waiting for a thread. The default pool uses a synchronous queue (capacity 0), so there is no buffering — if no thread is available, the task is rejected. +- **Queue utilization** — percentage of configured queue capacity used (only meaningful for thread pools with bounded queues) + +#### JVM memory breakdown + + +![JVM memory breakdown: heap, direct, native, GC](/assets/img/monitoring-jvm-memory.png) + + +The Resources tab's JVM row separates the three layers of container memory: + +- **JVM Heap Usage** — Java objects (searchers, document processors, caches) +- **JVM Direct Memory** — NIO buffers, Netty pools +- **JVM Native Memory** — JNI allocations, including ONNX embedder working memory and — if configured — a local LLM's KV cache and compute buffers + +When overall node memory is high but heap and direct look normal, the native layer is usually the answer. This is common on container nodes running embedder or local-LLM components: model weights are memory-mapped and only partially resident, but KV cache and compute buffers are allocated upfront as native memory. + +### Health tab + +The Health tab tracks cluster stability and data consistency, grouped per content cluster. + +#### Cluster state + +Nodes are distributed across states: **up** (serving), **down** (unreachable), **initializing** (starting up), **maintenance** (temporarily out), **retired** (being removed). During normal operation: all up, zero down. See [content node states](/en/content/content-nodes). + +#### Data consistency + +- **Buckets Out of Sync** — percentage of [data buckets](/en/content/buckets) not yet replicated/consistent. Should be 0% during steady state; non-zero during scaling, restarts, or failures. +- **Merge Pending** — bucket merge operations queued. High during data redistribution. + +After scaling events, expect buckets out of sync and pending merges. These should converge back to zero. If they don't, investigate. + +#### Stability + +- **Service Restarts** — cumulative restarts per cluster. An increase indicates a process crash. +- **Core Dumps** — should always be zero. + +Both signals surface in three complementary ways: as per-cluster time series on this tab (for historical context), as at-a-glance counters in the [Health Indicators row](#health-indicators) on the Overview tab, and as *Service restart*/*Core dump* [annotations](#dashboard-annotations) drawn as vertical lines on every chart. + +#### Feed Resource Limits + +Shows memory and disk utilization vs. configured limits. When utilization exceeds the limit, [feeding is blocked](/en/writing/feed-block). The dashboard includes a Resource Limits Reference panel (collapsed) explaining the default limits (disk 80%, memory 80%), the blocking mechanism, and what to do about it. + +### Common workflows + +#### "Our query latency increased" + + + +Overview: confirm the latency increase, check if QPS also changed. + + +Query: which percentile increased most? (avg vs p95 vs p99) + + +Query → Rank Profile: is it one query type or all? + + +Resources: CPU, JVM GC overhead. + + +Content Node: match executor utilization, queue sizes growing? + + + +#### "Feed is slow / feed is blocked" + + + +Overview: check feed latency and feed operation rate. + + +Feed: which layer shows increased latency? + + +Health: is feed blocked? Check resource limits. + + +Resources: disk and memory utilization, CPU IOWait. + + +Content Node: field writer saturation, filestor queue, active maintenance jobs. + + + +#### "Should we scale up?" + + + +Resources: identify the bottleneck resource. + + +Content Node: check executor utilization (are we compute-bound?). + + +Per host view: is load evenly distributed? + + +Enable [autoscaling](/en/operations/autoscaling) or adjust resources in `services.xml`. + + +See the [benchmarking guide](/en/performance/benchmarking) for systematic capacity testing. + + + +## Latency tracking + +When monitoring latency in clusters with mixed loads, it is useful to use [rank profiles](/en/basics/ranking#rank-profiles) to separate them. As an example, an application might have user queries mixed with agentic, batch-oriented queries. Tracking the Container-level query latencies might look like: + + +![total query latency chart](/assets/img/latency-total.png) + + +Using Content node level metrics, separated by ranking profile, we see: + + +![query latency per rank profile chart](/assets/img/latency-rank-profile.png) + + +From this, we see that query latency varies with the rank profile used. Relevant metrics to export to your monitoring system include: + +- [content.proton.documentdb.matching.rank\_profile.queries](/en/reference/operations/metrics/searchnode#content_proton_documentdb_matching_rank_profile_queries) +- [content.proton.documentdb.matching.rank\_profile.docs\_matched](/en/reference/operations/metrics/searchnode#content_proton_documentdb_matching_rank_profile_docs_matched) +- [content.proton.documentdb.matching.rank\_profile.query\_latency](/en/reference/operations/metrics/searchnode#content_proton_documentdb_matching_rank_profile_query_latency) +- [content.proton.documentdb.matching.rank\_profile.rerank\_time](/en/reference/operations/metrics/searchnode#content_proton_documentdb_matching_rank_profile_rerank_time) + +In short, when debugging latency, look for changes, per rank profile: + +- Did the query rate increase? +- Did number of matched or ranked documents change? + +The above metrics is a subset or the available metrics. It is a good idea to set a [query profile](/en/reference/querying/query-profiles) per class of query, and in each query profile, select a distinct rank profile. With this, you can change the rank profile for a given query class by configuration only (no need to change the clients) - a good example is having a lightweight rank profile to use in overload situations. This makes it easier to track the individual query classes, per rank profile. + +## Prometheus metrics API + +Prometheus metrics are found at `$ENDPOINT/prometheus/v1/values`: + +```bash +$ curl -s --cert data-plane-public-cert.pem --key data-plane-private-key.pem \ + 'https://b6718765.b68a1234.z.vespa-app.cloud/prometheus/v1/values' +``` + +The metrics can be fed into e.g. your Grafana Cloud or self-hosted Grafana instance. See the [Vespa metrics documentation](/en/reference/operations/metrics/vespa-metric-set) for more information. + +## Using Grafana + +This section explains how to set up Grafana to consume Vespa metrics using the Prometheus API. + +### 1. Prometheus configuration + +Prometheus is configured using `prometheus.yml`, find sample config in [prometheus](https://github.com/vespa-engine/sample-apps/tree/master/examples/operations/monitoring/album-recommendation-monitoring/prometheus). See `prometheus-cloud.yml`, which is designed to be easy to set up with any Vespa Cloud instance. Replace `` and `` with the endpoint for the application and the service name, respectively. In addition, the path to the private key and public cert that is used for the data plane to the endpoint need to be provided - refer to [security](/en/security/guide). Then, configure the Prometheus instance to use this configuration file. The Prometheus instance will now start retrieving the metrics from Vespa Cloud. If the Prometheus instance is used for multiple services, append the target configuration for Vespa to scrape\_configs. + +### 2. Grafana configuration + +Use the [provisioning folder](https://github.com/vespa-engine/sample-apps/tree/master/examples/operations/monitoring/album-recommendation-monitoring/grafana/provisioning) as a baseline for further configuration. + +In the provisioning folder there are a few different files that all help for configuring Grafana locally. These work as good examples of default configurations, but the most important is the file named `Vespa-Engine-Advanced-Metrics-External.json`. This is a default dashboard, based upon the metrics the Vespa team use to monitor performance. + +Click the + button on the side and go to import. Upload the file to the Grafana instance. This should automatically load in the dashboard for usage. For now, it will not display any data as no data sources are configured yet. + +### 3. Grafana Data Source + +The Prometheus data source has to be added to the Grafana instance for the visualisation. Click the cog on the left and then "Data Sources". Click "Add data source" and choose Prometheus from the list. Add the URL for the Prometheus instance with appropriate bindings for connecting. The configuration for the bindings will depend on how the Prometheus instance is hosted. Once the configuration details have been entered, click Save & Test at the bottom and ensure that Grafana says "Data source is working". + +To verify the data flow, navigate back to the Vespa Metrics dashboard by clicking the dashboard symbol on the left (4 blocks) and clicking manage and then click Vespa Metrics. Data should now appear in the Grafana dashboard. If no data shows up, edit one of the data sets and ensure that it has the right data source selected. The name of the data source the dashboard is expecting might be different from what your data source is named. If there is still no data appearing, it either means that the Vespa instance is not being used or that some part of the configuration is wrong. + +## Using AWS Cloudwatch + +To pull metrics from your Vespa application into AWS Cloudwatch, refer to the [metrics-emitter](https://github.com/vespa-engine/metrics-emitter/tree/master/cloudwatch) documentation for how to set up an AWS Lambda. + +## Alerting + +The [Vespa Grafana Terraform template](https://github.com/vespa-engine/sample-apps/tree/master/examples/operations/monitoring/vespa-grafana-terraform) provides a set of dashboards and alerts. If you are using a different monitoring service and want to set up an equivalent alert set, you can follow this table: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Metric nameThresholdDimension aggregation
    content_proton_resource_usage_disk_average{`>`} 0.9max by(applicationId, clusterId, zone)
    content_proton_resource_usage_memory_average{`>`} 0.8max by(applicationId, zone, clusterId)
    cpu_util{`>`} 90max by(applicationId, zone, clusterId)
    content_proton_resource_usage_feeding_blocked_last{`>=`} 1N/A
    + +All metrics are from the [default metric set](/en/reference/operations/metrics/default-metric-set#metric-sets). Metrics are using the naming scheme from the [Prometheus metrics](/en/reference/api/prometheus-v1#prometheus-v1-values) API. Dimension aggregation is optional, but reduces alerting noise - e.g. in the case where an entire cluster goes bad. It is recommended to filter all alerts on zones in the [prod environment](/en/operations/environments#prod). + +## Prometheus Metrics Sample + +Below is a sample request with sample response for prometheus metrics for a minimal application on Vespa Cloud: + + +```bash +$ curl -s --cert data-plane-public-cert.pem --key data-plane-private-key.pem \ + 'https://b6718765.b68a1234.z.vespa-app.cloud/prometheus/v1/values' + +... +jdisc_thread_pool_work_queue_size_min{threadpool="default-pool",zone="dev.aws-us-east-1c",applicationId="mytenant.myapp.default",serviceId="logserver-container",clusterId="admin/logserver",hostname="h97490a.dev.us-east-1c.aws.vespa-cloud.net",vespa_service="vespa_logserver_container",} 0.0 1733139324000 +jdisc_thread_pool_work_queue_size_min{threadpool="default-handler-common",zone="dev.aws-us-east-1c",applicationId="mytenant.myapp.default",serviceId="logserver-container",clusterId="admin/logserver",hostname="h97490a.dev.us-east-1c.aws.vespa-cloud.net",vespa_service="vespa_logserver_container",} 0.0 1733139324000 +# HELP content_proton_documentdb_matching_rank_profile_rerank_time_average +# TYPE content_proton_documentdb_matching_rank_profile_rerank_time_average untyped +content_proton_documentdb_matching_rank_profile_rerank_time_average{rankProfile="rank_albums",documenttype="music",zone="dev.aws-us-east-1c",applicationId="mytenant.myapp.default",serviceId="searchnode",clusterId="content/music",hostname="h104562a.dev.us-east-1c.aws.vespa-cloud.net",vespa_service="vespa_searchnode",} 0.0 1733139324000 +content_proton_documentdb_matching_rank_profile_rerank_time_average{rankProfile="unranked",documenttype="music",zone="dev.aws-us-east-1c",applicationId="mytenant.myapp.default",serviceId="searchnode",clusterId="content/music",hostname="h104562a.dev.us-east-1c.aws.vespa-cloud.net",vespa_service="vespa_searchnode",} 0.0 1733139324000 +content_proton_documentdb_matching_rank_profile_rerank_time_average{rankProfile="default",documenttype="music",zone="dev.aws-us-east-1c",applicationId="mytenant.myapp.default",serviceId="searchnode",clusterId="content/music",hostname="h104562a.dev.us-east-1c.aws.vespa-cloud.net",vespa_service="vespa_searchnode",} 0.0 1733139324000 +... +``` + +Relevant labels include: + +- `chain` This is the name on the search chain in the container that is used for a set of query requests. This is typically used to get separate metrics, such as latency and the number of queries for each chain over time. +- `documenttype` This is the name of the document type for which a set of queries are run in the content clusters. This is typically used to get separate content layer metrics, such as latency and the number of queries for each chain over time. +- `groupId` This is the id of the cluster group for which the metric measurement is done. This is typically used to get separate metrics aggregates per group in a content cluster. The label is most relevant for metrics from the content clusters running multiple content groups, see [Content Cluster Elasticity](/en/content/elasticity). The value is in the format group 0, group 1, group 2, etc. +- `rankProfile` This label is present for a subset of metrics from the content clusters, with names starting with `content_proton_documentdb_matching_rank_profile_`. The label is typically used in cases where you use multiple rank profiles and want to analyse performance differences between the different rank profiles, or to better understand certain types of performance issues and need to narrow down the candidate set. +- `source` This is a label applied on container metrics for classifying query failures by the content cluster where the failure was triggered. + +How you will use labels to separate different kinds of queries depends on the observability backend you use, but you will typically compute weighted averages for query latency and query volume, and split graphs by the relevant labels to better understand system performance and bottlenecks. + +For the container level metrics you use the `chain` label to differentiate between different query streams, while you use the `rankProfile` label to do the same in the content level. \ No newline at end of file diff --git a/en/operations/notifications.md b/en/operations/notifications.md deleted file mode 100644 index e1efd5d05f..0000000000 --- a/en/operations/notifications.md +++ /dev/null @@ -1,35 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: Notifications -applies_to: cloud -redirect_from: -- /en/cloud/notifications ---- - -Vespa Cloud supports two different categories of notifications. Notifications can be sent -by email if this has been configured in the Console. - - * **Tenant notifications** are administrative notifications about the tenant. Information - about users, plan, etc. are sent to all contacts configured to get tenant notifications. - - * **Application notifications** are notifications about your running Vespa applications. - If there are resource constraint issues, deployment errors, configuration errors or other - issues with a Vespa application, they will be sent to all contacts configured to get - application notifications. - -## Configuring Notifications -Notifications are configured in the Console under [**Account \> Notifications**](https://console.vespa-cloud.com/link/tenant/account/notifications). You can add -contacts here that will start receiving emails for the categories enabled for that contact. - - -Console Notifications - -To add a new address to get notifications: -1. Click **+Add new contact**. -2. Enter the email address to receive notifications to. -3. Choose the types of notifications to receive. -4. Click **Save** -5. Go to your email inbox and click the verification link you have received there. diff --git a/en/operations/notifications.mdx b/en/operations/notifications.mdx new file mode 100644 index 0000000000..6c71a9000d --- /dev/null +++ b/en/operations/notifications.mdx @@ -0,0 +1,36 @@ +--- +title: "Notifications" +--- + +Vespa Cloud supports two different categories of notifications. Notifications can be sent by email if this has been configured in the Console. + +- **Tenant notifications** are administrative notifications about the tenant. Information about users, plan, etc. are sent to all contacts configured to get tenant notifications. +- **Application notifications** are notifications about your running Vespa applications. If there are resource constraint issues, deployment errors, configuration errors or other issues with a Vespa application, they will be sent to all contacts configured to get application notifications. + +## Configuring Notifications + +Notifications are configured in the Console under [**Account > Notifications**](https://console.vespa-cloud.com/link/tenant/account/notifications). You can add contacts here that will start receiving emails for the categories enabled for that contact. + + +![Console Notifications](/assets/img/console-notifications.png) + + +To add a new address to get notifications: + + + +Click **+Add new contact**. + + +Enter the email address to receive notifications to. + + +Choose the types of notifications to receive. + + +Click **Save** + + +Go to your email inbox and click the verification link you have received there. + + \ No newline at end of file diff --git a/en/operations/private-endpoints.html b/en/operations/private-endpoints.html deleted file mode 100644 index f1c0337cc2..0000000000 --- a/en/operations/private-endpoints.html +++ /dev/null @@ -1,410 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: Private endpoints -applies_to: cloud -redirect_from: -- /en/cloud/private-endpoints.html ---- - -

    - Vespa Cloud lets you set up private endpoint services on your application clusters, - for exclusive access from your own, co-located VPCs with the same cloud provider. - This is supported for AWS deployments through AWS's - - PrivateLink, and for GCP deployments through GCP's - - Private Service Connect. - This guide takes you through the necessary configuration steps for either - AWS PrivateLink - or for GCP Private Service Connect. -

    - -{% include note.html content=' - Private endpoints use mTLS authentication by default, and token-based authentication must be explicitly enabled. - See configuring private endpoint authentication method. -'%} - - -

    Required information:

    - - - - - - - - - - - - - - - - - - - - - -
    ItemDescription
    Your IAM account numberThe numeric identifier for your AWS account.
    VPC IDThe identifier of your AWS VPC where you wish to connect to the service endpoints from.
    AWS region nameThe name of the AWS region to connect from. Note that you can only connect to a service in the same region, - or, if public endpoints are disabled, in the same AWS availability zone.
    -

    Procedure:

    -
      -
    1. -

      Configure a private endpoint service on your Vespa Cloud Container cluster

      -

      - Add <endpoint type="private" /> - to deployment.xml, - allowing access to the container cluster using the designated ARN from your account. - The example allows all roles and users under the 123123123123 account - to connect to the endpoint service on the my-container cluster, - in each region listed under the <prod> tag. -

      -

      - See endpoint service configuration - for details on valid ARNs, and more fine-grained access control. -

      -

      - The example also shows how to disable the public zone endpoint by adding the - "zone" type endpoint - declaration—this is an optional step, and not required to set up the private service: -

      -
      {% highlight xml %}
      -
      -    
      -        region-1
      -        region-2
      -    
      -    
      -        
      -        
      -            
      -        
      -    
      -
      -{% endhighlight %}
      -

      - Build and deploy the application package, and wait for it to deploy to the indicated regions. -

      - {% include important.html content=' - In the above example, the public endpoint is set to disabled, and the private endpoint is added. - Make this change in the same deployment. - If you disable the public endpoint after the private endpoint is live, - the private endpoint will be disabled and recreated as part of recreating the load balancer. - If this happens, run through the procedure again for correct load balancer creation. '%} -
    2. -
    3. -

      Find the service ID of your endpoint services

      -

      - Navigate to the endpoints tab for your application in the Console, - and find the service ID for the deployment to which you wish to connect. - While there, verify that access to connect to the endpoint was granted to the correct ARNs. -

      - Service ID for VPC endpoint -
    4. -
    5. -

      Create the VPC interface endpoint

      -

      - Create a VPC endpoint - in your VPC. This is your entry point, which forwards connections to your Vespa application through the private network of AWS. - For this example, assume your VPC has id - vpc-123 and resides in the AWS region us-east-1, and that the service ID of your - endpoint service, found in the Console, is com.amazonaws.vpce.us-east-1.vpce-svc-321: -

      -
      -$ aws ec2 create-vpc-endpoint \
      -  --region us-east-1 \
      -  --vpc-id vpc-123 \
      -  --service-name com.amazonaws.vpce.us-east-1.vpce-svc-321 \
      -  --vpc-endpoint-type Interface \
      -  --private-dns-enabled | jq .
      -
      -

      - Note the value of the VpcEndpointId field, for verification in the below item. - This is also where you specify optional security group and subnet IDs; these are omitted here for brevity. - If creating the VPC endpoint through the AWS console instead, be sure to check "Enable DNS names"! -

      -
    6. -
    7. -

      Verify the VPC endpoint is connected to the Vespa cluster

      -

      - Navigate back to the endpoints tab in the Console, and refresh the page. - You should now see a new entry representing the connection between your newly created interface endpoint - and the endpoint service on your container cluster. - This is the "CONNECTED ENDPOINTS" in the image above. - Verify the ID matches the value of the VpcEndpointId field above. - The connection is ready when the state is open. -

      -
    8. -
    9. -

      Verify your Vespa cluster is reachable from within your VPC

      -

      - The zone endpoint of the designated container cluster should now resolve through private DNS, for any AWS resource - that is allowed to connect to your VPC endpoint. - The easiest way to verify this is to run the following Python 3.9 lambda, - using your own zone endpoint, from within your VPC: -

      -
      -from socket import gethostbyname
      -from urllib.request import urlopen
      -
      -def lambda_handler(event, context):
      -    return {
      -        'statusCode': 200,
      -        'body': urlopen('https://badc0ffee.deadbeef.z.vespa-app.cloud/status.html').read(),
      -        'ip': gethostbyname('badc0ffee.deadbeef.z.vespa-app.cloud')
      -    }
      -
      -

      Alternatively, run a couple of commands from a host inside the VPC:

      -
      -$ host my-container.my-app.my-tenant.region-1.z.vespa-app.cloud
      -$ curl https://my-container.my-app.my-tenant.region-1.z.vespa-app.cloud/status.html
      -
      -

      In both cases, the IP should be in one of the private IP ranges, and the HTTP response from the - Vespa container endpoint should be OK.

      -
    10. -
    - -{% include note.html content=' -Enclave users may set up high-availability PrivateLink endpoints connected across multiple AZs. Contact -Vespa support for guidance. -'%} - - -

    GCP Private Service Connect

    -

    Prerequisites:

    - - - - - - - - - - - - - - - - - - - - - -
    ItemDescription
    Enabled GCP APIsThe Compute Engine, Service Directory and Cloud DNS - APIs must all be enabled in your GCP account: -
    -$ gcloud services enable compute.googleapis.com
    -$ gcloud services enable dns.googleapis.com
    -$ gcloud services enable servicedirectory.googleapis.com
    -
    -
    Your GCP project nameThe string identifier for your GCP account, like resonant-diode-123456
    VPC network and subnetwork namesThe name of the network and subnetwork to create your consumer endpoint in.
    -

    Procedure:

    -
      -
    1. -

      Configure a private endpoint service on your Vespa Cloud Container cluster

      -

      - Add <endpoint type="private"/> - to deployment.xml, - allowing access to the container cluster from the GCP account with the designated project ID. - The example below allows consumer endpoints created under the private-test account - to connect to the endpoint service on the my-container cluster, - in each region listed under the <prod> tag. -

      -

      - The example also shows how to disable the public zone endpoint by adding the - "zone" type endpoint - declaration—this is an optional step, and not required to set up the private service -

      -
      {% highlight xml %}
      -
      -    
      -        region-1
      -        region-2
      -    
      -    
      -        
      -        
      -            
      -        
      -    
      -
      -{% endhighlight %}
      -

      - Build and deploy the application package, and wait for it to deploy to the indicated regions. -

      -
    2. -
    3. -

      Find the service ID of your endpoint services

      -

      - Navigate to the endpoints tab for your application in the Console, - and find the service ID for the deployment to which you wish to connect. - While there, verify that access to connect to the endpoint was granted to the correct projects. -

      - Service ID for VPC endpoint -
    4. -
    5. -

      Create the service consumer endpoint

      -

      - Create a service consumer endpoint - in your VPC. This is your entry point, which forwards connections to your Vespa application through the private GCP network. - In this example, the project is named - test-project, has a VPC network named - test-network that resides in the GCP region us-central1, with a subnet test-subnet - to hold the endpoint, behind an address to be named test-address, and the service ID of the - endpoint service (found in the Console) is - projects/vespa-external/regions/us-central1/serviceAttachments/scsa-xxxxxx. - Finally, the endpoint is named badc0ffee, and the service directory namespace is my-tenant-my-app. - See the discussion on generated endpoint names in the last item in this guide. -

      -

      Create network (if it does not already exist):

      -
      -$ gcloud compute networks create test-network
      -
      -

      Create subnet (if it does not already exist):

      -
      -$ gcloud compute networks subnets create test-subnet \
      -    --region=us-central1 \
      -    --network=test-network \
      -    --range=10.10.0.0/24
      -
      -

      Create the IP address which will be used for the endpoint, for clients inside your VPC:

      -
      -$ gcloud compute addresses create test-address \
      -    --region=us-central1 \
      -    --subnet=test-subnet
      -
      -

      Create a forwarding rule for traffic to the above IP, to the service endpoint in Vespa Cloud:

      -
      -$ gcloud compute forwarding-rules create badc0ffee \
      -    --region=us-central1 \
      -    --network=test-network \
      -    --address=test-address \
      -    --target-service-attachment=projects/vespa-external/regions/us-central1/serviceAttachments/scsa-xxxxxx \
      -    --service-directory-registration=projects/test-project/locations/us-central1/namespaces/my-tenant-my-app
      -
      -

      Note the ID of the created resource, for the verification step below.

      -
    6. -
    7. -

      Verify the VPC endpoint is connected to the Vespa cluster

      -

      - Navigate back to the endpoints tab in the Console, and refresh the page. - You should now see a new entry representing the connection between your newly created interface endpoint, - and the endpoint service on your container cluster. - This is the "CONNECTED ENDPOINTS" in the image above. - Verify the ID matches the resource ID of the forwarding rule created above. - The connection is ready when the state is open. -

      -
    8. -
    9. -

      Verify your Vespa cluster is reachable from within your VPC

      -

      - The generated endpoint name (see last items) of the designated container cluster should now resolve - through private DNS inside your VPC. - The easiest way to verify this is to launch an instance in your VPC, inside the designated subnet, - and run a couple of commands from it: -

      -
      -$ host badc0ffee.deadbeef.z.vespa-app.cloud
      -$ curl https://badc0ffee.deadbeef.z.vespa-app.cloud/status.html
      -
      -

      - The resolved IP address should be that of the address created earlier, and the curl command - should simply output OK. -

      -

      - If the endpoint fails to resolve, refer to - - GCP's troubleshooting documentation. -

      -
    10. -
    11. -

      Generated endpoints with Private Service Connect

      -

      - When a consumer endpoint is created with a Service Directory namespace, GCP automatically creates - a private DNS record for that endpoint, which must be used instead of the IP address (created above) of the endpoint, - as Vespa application containers have web certificates matching specific domain names. - Unfortunately, we are unable to set the final endpoint names for the consumer endpoint. - For a private endpoint service, we can only set a domain name suffix, and GCP then generates private - DNS records matching your endpoint resource name prepended to this suffix. - The Service Directory namespace of these endpoints must also be one-to-one with their domain name suffixes, - lest the automatic setup fail. -

      -

      - The domain name suffix used by Vespa Cloud is - [<tenant-and-app-anonymous-id>.].z.vespa-app.cloud. We therefore encourage using the - <tenant>-<application> pair as the service directory namespace, - as this ensures a one-to-one mapping between suffixes and namespaces, as required by GCP (see above). -

      -

      - The Vespa Cloud web certificates (see above) match any direct descendant of the domain suffix we set for your services. - Thus, any endpoint resource name yields a private DNS record that matches the web certificate, with Service Directory. - Moreover, the zone endpoints generated by Vespa Cloud consist of a random, unique cluster-instance-region ID. - Using this same ID as the GCP endpoint resource name (as in the example) results in identical domain names for - the private DNS set up by GCP, and the endpoint names generated by Vespa Cloud, visible in our console. -

      -
    12. -
    - -

    Configuring Private Endpoint Authentication

    - -

    -You can configure private endpoints to use either mTLS or token-based authentication -with the optional auth-method attribute. If the attribute is not set, mTLS will be used by default. -The attribute is only allowed with private type endpoints and must -be either mtls or token. -

    - -{% include note.html content=' -Only one authentication method can be enabled at the same time. -Enabling token authentication will disable mTLS authentication for the private endpoint, and vice versa. -'%} - -

    Example with token-based authentication

    - -
    {% highlight xml %}
    -
    -    
    -        region-1
    -        region-2
    -    
    -    
    -        
    -            
    -        
    -    
    -
    -{% endhighlight %}
    -
    - -

    Changing authentication method for an existing deployment

    - -

    -If you have an existing deployment with a private endpoint, you must remove any connections and redeploy with a -validation override -to modify the authentication method: -

    - -
      -
    1. - Remove the VPC interface endpoint (AWS) or service consumer endpoint (GCP) configured above -
    2. -
    3. - Change the authentication method for the endpoint in deployment.xml -
    4. -
    5. - Deploy with the zone-endpoint-change validation override: -
    6. -
    - -
    {% highlight xml %}
    -
    -    
    -        zone-endpoint-change
    -    
    -
    -{% endhighlight %}
    diff --git a/en/operations/private-endpoints.mdx b/en/operations/private-endpoints.mdx new file mode 100644 index 0000000000..b34446a619 --- /dev/null +++ b/en/operations/private-endpoints.mdx @@ -0,0 +1,297 @@ +--- +title: "Private endpoints" +--- + +Vespa Cloud lets you set up private endpoint services on your application clusters, for exclusive access from your own, co-located VPCs with the same cloud provider. This is supported for AWS deployments through AWS's [PrivateLink](https://docs.aws.amazon.com/vpc/latest/privatelink/what-is-privatelink.html), and for GCP deployments through GCP's [Private Service Connect](https://cloud.google.com/vpc/docs/private-service-connect). This guide takes you through the necessary configuration steps for either [AWS PrivateLink](#aws-private-link) or for [GCP Private Service Connect](#gcp-private-service-connect). + +Private endpoints are only supported in zones in the [prod environment](/en/operations/environments#prod). + + +**Note:** + +Private endpoints use mTLS authentication by default, and token-based authentication must be explicitly enabled. See [configuring private endpoint authentication method](#authentication-methods). + + +## AWS PrivateLinkRequired information: + + + + + + + + + + + + + + + + + + + + + + +
    ItemDescription
    **Your IAM account number**The numeric identifier for your AWS account.
    **VPC ID**The identifier of your AWS VPC where you wish to connect to the service endpoints from.
    **AWS region name**The name of the AWS region to connect from. Note that you can only connect to a service in the same region, or, if public endpoints are disabled, in the same AWS availability zone.
    + +Procedure: + + + +Add `` to [deployment.xml](/en/reference/applications/deployment#endpoint-private), allowing access to the container cluster using the designated ARN from your account. The example allows all roles and users under the `123123123123` account to connect to the endpoint service on the `my-container` cluster, in each region listed under the `` tag. + +See [endpoint service configuration](https://docs.aws.amazon.com/vpc/latest/privatelink/configure-endpoint-service.html) for details on valid ARNs, and more fine-grained access control. + +The example also shows how to disable the public zone endpoint by adding the [`"zone"` type endpoint](/en/reference/applications/deployment#endpoint-zone) declaration—this is an optional step, and not required to set up the private service: + ```xml + + + region-1 + region-2 + + + + + + + + + ``` + +Build and deploy the application package, and wait for it to deploy to the indicated regions. + +**Important:** + +In the above example, the public endpoint is set to disabled, and the private endpoint is added. Make this change in the *same* deployment. If you disable the public endpoint *after* the private endpoint is live, the private endpoint will be disabled and recreated as part of recreating the load balancer. If this happens, run through the procedure again for correct load balancer creation. + + + +Navigate to the endpoints tab for your application in the Console, and find the service ID for the deployment to which you wish to connect. While there, verify that access to connect to the endpoint was granted to the correct ARNs. + + +![Service ID for VPC endpoint](/assets/img/vpc-1.png) + + + +[Create a VPC endpoint](https://docs.aws.amazon.com/cli/latest/reference/ec2/create-vpc-endpoint.html) in your VPC. This is your entry point, which forwards connections to your Vespa application through the private network of AWS. For this example, assume your VPC has id `vpc-123` and resides in the AWS region `us-east-1`, and that the service ID of your endpoint service, found in the Console, is `com.amazonaws.vpce.us-east-1.vpce-svc-321`: + +```bash +$ aws ec2 create-vpc-endpoint \ + --region us-east-1 \ + --vpc-id vpc-123 \ + --service-name com.amazonaws.vpce.us-east-1.vpce-svc-321 \ + --vpc-endpoint-type Interface \ + --private-dns-enabled | jq . +``` + +Note the value of the `VpcEndpointId` field, for verification in the below item. This is also where you specify optional security group and subnet IDs; these are omitted here for brevity. If creating the VPC endpoint through the AWS console instead, be sure to check "Enable DNS names"! + + +Navigate back to the endpoints tab in the Console, and refresh the page. You should now see a new entry representing the connection between your newly created interface endpoint and the endpoint service on your container cluster. This is the "CONNECTED ENDPOINTS" in the image above. Verify the ID matches the value of the `VpcEndpointId` field above. The connection is ready when the state is `open`. + + +The zone endpoint of the designated container cluster should now resolve through private DNS, for any AWS resource that is allowed to connect to your VPC endpoint. The easiest way to verify this is to run the following Python 3.9 lambda, using your own zone endpoint, from within your VPC: + +```bash +from socket import gethostbyname +from urllib.request import urlopen + +def lambda_handler(event, context): + return { + 'statusCode': 200, + 'body': urlopen('https://badc0ffee.deadbeef.z.vespa-app.cloud/status.html').read(), + 'ip': gethostbyname('badc0ffee.deadbeef.z.vespa-app.cloud') + } +``` + +Alternatively, run a couple of commands from a host inside the VPC: + +```bash +$ host my-container.my-app.my-tenant.region-1.z.vespa-app.cloud +$ curl https://my-container.my-app.my-tenant.region-1.z.vespa-app.cloud/status.html +``` + +In both cases, the IP should be in one of the private IP ranges, and the HTTP response from the Vespa container endpoint should be `OK`. + + + + +**Note:** + +Enclave users may set up high-availability PrivateLink endpoints connected across multiple AZs. Contact [Vespa support](https://vespa.ai/support/) for guidance. + + +## GCP Private Service ConnectPrerequisites: + + + + + + + + + + + + + + + + + + + + + + +
    ItemDescription
    **Enabled GCP APIs**The *Compute Engine*, *Service Directory* and *Cloud DNS* APIs must all be enabled in your GCP account:

    {`$ gcloud services enable compute.googleapis.com`}
    {`$ gcloud services enable dns.googleapis.com`}
    {`$ gcloud services enable servicedirectory.googleapis.com`}
    **Your GCP project name**The string identifier for your GCP account, like *resonant-diode-123456*
    **VPC network and subnetwork names**The name of the network and subnetwork to create your consumer endpoint in.
    + +Procedure: + + + + +Add `` to [deployment.xml](/en/reference/applications/deployment#endpoint-private), allowing access to the container cluster from the GCP account with the designated project ID. The example below allows consumer endpoints created under the `private-test` account to connect to the endpoint service on the `my-container` cluster, in each region listed under the `` tag. + +The example also shows how to disable the public zone endpoint by adding the [`"zone"` type endpoint](/en/reference/applications/deployment#endpoint-zone) declaration—this is an optional step, and not required to set up the private service + ```xml + + + region-1 + region-2 + + + + + + + + + ``` + Build and deploy the application package, and wait for it to deploy to the indicated regions. + + +Navigate to the endpoints tab for your application in the Console, and find the service ID for the deployment to which you wish to connect. While there, verify that access to connect to the endpoint was granted to the correct projects. + + +![Service ID for VPC endpoint](/assets/img/vpc-2.png) + + + + + +[Create a service consumer endpoint](https://cloud.google.com/vpc/docs/configure-private-service-connect-services) in your VPC. This is your entry point, which forwards connections to your Vespa application through the private GCP network. In this example, the project is named `test-project`, has a VPC network named `test-network` that resides in the GCP region `us-central1`, with a subnet `test-subnet` to hold the endpoint, behind an address to be named `test-address`, and the service ID of the endpoint service (found in the Console) is `projects/vespa-external/regions/us-central1/serviceAttachments/scsa-xxxxxx`. Finally, the endpoint is named `badc0ffee`, and the service directory namespace is `my-tenant-my-app`. See the discussion on generated endpoint names in the last item in this guide. + +Create network (if it does not already exist): + +```bash +$ gcloud compute networks create test-network +``` + +Create subnet (if it does not already exist): + +```bash +$ gcloud compute networks subnets create test-subnet \ + --region=us-central1 \ + --network=test-network \ + --range=10.10.0.0/24 +``` + +Create the IP address which will be used for the endpoint, for clients inside your VPC: + +```bash +$ gcloud compute addresses create test-address \ + --region=us-central1 \ + --subnet=test-subnet +``` + +Create a forwarding rule for traffic to the above IP, to the service endpoint in Vespa Cloud: + +```bash +$ gcloud compute forwarding-rules create badc0ffee \ + --region=us-central1 \ + --network=test-network \ + --address=test-address \ + --target-service-attachment=projects/vespa-external/regions/us-central1/serviceAttachments/scsa-xxxxxx \ + --service-directory-registration=projects/test-project/locations/us-central1/namespaces/my-tenant-my-app +``` + +Note the ID of the created resource, for the verification step below. + + + +Navigate back to the endpoints tab in the Console, and refresh the page. You should now see a new entry representing the connection between your newly created interface endpoint, and the endpoint service on your container cluster. This is the "CONNECTED ENDPOINTS" in the image above. Verify the ID matches the resource ID of the forwarding rule created above. The connection is ready when the state is `open`. + + + +The generated endpoint name (see last items) of the designated container cluster should now resolve through private DNS inside your VPC. The easiest way to verify this is to launch an instance in your VPC, inside the designated subnet, and run a couple of commands from it: + +```bash +$ host badc0ffee.deadbeef.z.vespa-app.cloud +$ curl https://badc0ffee.deadbeef.z.vespa-app.cloud/status.html +``` + +The resolved IP address should be that of the address created earlier, and the `curl` command should simply output `OK`. + +If the endpoint fails to resolve, refer to [GCP's troubleshooting documentation](https://cloud.google.com/vpc/docs/configure-private-service-connect-services#troubleshooting). + + +When a consumer endpoint is created with a *Service Directory* namespace, GCP automatically creates a private DNS record for that endpoint, which must be used instead of the IP address (created above) of the endpoint, as Vespa application containers have web certificates matching specific domain names. Unfortunately, we are unable to set the final endpoint names for the consumer endpoint. For a private endpoint service, we can only set a domain name *suffix*, and GCP then generates private DNS records matching *your endpoint resource name prepended to this suffix*. The Service Directory namespace of these endpoints *must also be one-to-one* with their domain name suffixes, lest the automatic setup fail. + +The domain name suffix used by Vespa Cloud is `[.].z.vespa-app.cloud`. We therefore encourage using the `-` pair as the service directory namespace, as this ensures a one-to-one mapping between suffixes and namespaces, as required by GCP (see above). + +The Vespa Cloud web certificates (see above) match any direct descendant of the domain suffix we set for your services. Thus, any endpoint resource name yields a private DNS record that matches the web certificate, with Service Directory. Moreover, the zone endpoints generated by Vespa Cloud consist of a random, unique cluster-instance-region ID. Using this same ID as the GCP endpoint resource name (as in the example) results in identical domain names for the private DNS set up by GCP, and the endpoint names generated by Vespa Cloud, visible in our console. + + + +## Configuring Private Endpoint Authentication + +You can configure private endpoints to use either mTLS or token-based authentication with the optional `auth-method` attribute. If the attribute is not set, mTLS will be used by default. The attribute is only allowed with `private` type endpoints and must be either `mtls` or `token`. + + +**Note:** + +Only one authentication method can be enabled at the same time. Enabling token authentication will disable mTLS authentication for the private endpoint, and vice versa. + + +#### Example with token-based authentication + +```xml + + + region-1 + region-2 + + + + + + + +``` + +#### Changing authentication method for an existing deployment + +If you have an existing deployment with a private endpoint, you must remove any connections and redeploy with a [validation override](/en/reference/applications/validation-overrides) to modify the authentication method: + + + +Remove the VPC interface endpoint (AWS) or service consumer endpoint (GCP) configured above + + +Change the authentication method for the endpoint in `deployment.xml` + + +Deploy with the `zone-endpoint-change` validation override: + ```xml + + + zone-endpoint-change + + + ``` + + \ No newline at end of file diff --git a/en/operations/production-deployment.html b/en/operations/production-deployment.html deleted file mode 100644 index 24ee82c075..0000000000 --- a/en/operations/production-deployment.html +++ /dev/null @@ -1,378 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: Production Deployment -applies_to: cloud -redirect_from: -- /en/cloud/production-deployment.html ---- - - -

    - Production zones enable serving from various locations, - with a CI/CD pipeline for safe deployments. - This guide goes through the minimal steps for a production deployment - in short:

    -
      -
    • Configure a production zone in deployment.xml.
    • -
    • Configure resources for clusters in services.xml.
    • -
    • Name the tenant, application, log in.
    • -
    • Create or have access to the data-plane cert/key pair.
    • -
    • Deploy the application to Vespa Cloud.
    • -
    -

    - The sample application used in getting started is a good basis for these steps, - see source files. -

    -

    - Read migrating to Vespa Cloud first, - as a primer on deployment and endpoint usage. -

    -

    There are alternative ways of deploying at the end of this guide, too.

    - - - -

    deployment.xml

    -

    Add a <prod> element to deployment.xml:

    -
    - -
    {% highlight xml %}
    -
    -    
    -        aws-us-east-1c
    -    
    -
    -{% endhighlight %}
    -
    -

    - If deployment.xml does not exist, add it to the application package root (next to services.xml). -

    -{% include note.html content=' -If the application uses private endpoints, -add this configuration here, too, and run the setup steps in the guide.'%} - - -

    services.xml

    -

    - Modify services.xml - minimal example: -

    -
    - -
    {% highlight xml %}
    -
    -
    -    
    -        
    -        
    -        
    -            
    -        
    -    
    -
    -    
    -        2
    -        
    -            
    -        
    -        
    -            
    -        
    -    
    -
    -
    -{% endhighlight %}
    -
    -

    - For production deployments, at least 2 nodes are required for each - cluster to ensure availability during maintenance tasks and upgrades. - In some cases one might still want to use just 1 node per cluster, even - though redundancy will be lost. This can be done by adding - - a validation override minimum-node-count (and additional - validation override redundancy-one in case of a content cluster). - The nodes section is also where you specify your required resources: -

    -
    - -
    {% highlight xml %}
    -        
    -            
    -        
    -{% endhighlight %}
    -
    -

    Also note the minimum redundancy requirement of 2:

    -
    - -
    {% highlight xml %}
    -        2
    -{% endhighlight %}
    -
    - -

    Minimum resources

    -

    - To help ensure a reliable service, there is a minimum resource requirement for nodes in the production environment. - The minimum is currently 0.5 VCPU, 8Gb of memory, and for disk, 2 x memory for stateless nodes, or 3 x memory for content nodes. - As the disk resource is normally the least expensive, - we recommend it should be allocated generously to ensure it does not limit the use of more expensive cpu and memory resources. -

    - -

    Application name

    -

    Give the deployment a name and log in:

    -
    - -
    -$ vespa config set target cloud
    -$ vespa config set application mytenant.myapp
    -$ vespa auth login
    -
    -
    -

    - The tenant name is found in the console, the application is something unique within your organization - - see tenants, applications and instances. -

    - - - -

    Add public certificate

    -

    - Just as in the getting started guide, - the application package needs the public key in the security directory. - You might already have a pair, if not generate it: -

    -
    -$ vespa auth cert -f
    -Success: Certificate written to security/clients.pem
    -Success: Certificate written to /Users/me/.vespa/mytenant.myapp.default/data-plane-public-cert.pem
    -Success: Private key written to /Users/me/.vespa/mytenant.myapp.default/data-plane-private-key.pem
    -
    -

    - Observe that the files are put in $HOME/.vespa. - The content from data-plane-public-cert.pem is copied to security/clients.pem. - More details on data-plane access control permissions. -

    - - - -

    Deploy the application

    -

    Package the application and deploy it to a production zone:

    -
    - -
    -$ vespa prod deploy
    -
    -
    -

    Find alternative deployment procedures in the next sections.

    -{% include note.html content=' -The vespa prod deploy command to prod zones, -which uses deployment.xml -differs from the vespa deploy command used for dev zones - see -environments.'%} - - - -

    Endpoints

    -

    - Find the 'zone' endpoint to use under Endpoints in the console. - There is an mTLS endpoint for each zone by default. - See configuring mTLS - for how to use mTLS certificates. -

    -

    - You can also add access tokens - in the console as an alternative to mTLS, - and specify global - and private endpoints - in deployment.xml. -

    -

    - Write data efficiently using the - document/v1 API using HTTP/2, - or with the Vespa CLI. - There is also a Java library. -

    -

    - To feed data from a self-hosted Vespa into a new cloud instances, see the - appendix or - cloning applications and data. -

    - -

    Also see the http best practices documentation.

    - - -

    Automate deployments

    -

    - Use - deploy-vector-search.yaml as a starting point, - and see Automating with GitHub Actions - for more information. -

    - - - -

    Production deployment using console

    -

    - Instead of using the Vespa CLI, - one can build an application package for production deployment using zip only: -

    -
      -
    • - Create deployment.xml - and modify services.xml as above. -
    • -
    • Skip the Application name step.
    • -
    • - Add a public certificate to security/clients.pem. - See creating a self-signed certificate - for how to create the key/cert pair, then copy the cert file to security/clients.pem. - At this point, the files are ready for deployment. -
    • -
    • - Create a deployable zip-file: -
      - -
      -$ zip -r application.zip . \
      -  -x application.zip "ext/*" README.md .gitignore ".idea/*"
      -
      -
      -
    • -
    • - Click Create Application in the console. - Select the PROD tab. - Enter a name for the application and drop the application.zip file in the upload section. -
    • -
    • Click Create and deploy to deploy the application to the production environment.
    • -
    - - - -

    Production deployment with components

    -

    - Deploying an application with Components - is a little different from above: -

    -
      -
    • The application package root is at src/main/application.
    • -
    • Find the Vespa API version to compile the component.
    • -
    • The application package is built into a zip artifact, before deploying it.
    • -
    -

    See Getting started java for prerequisites. Procedure:

    -
      -
    1. - Use the - album-recommendation-java sample application as a starting point. -
    2. -
    3. - Make the same changes to src/main/application/deployment.xml and - src/main/application/services.xml. -
    4. -
    5. - Run the same steps for Application name and - Add public certificate. -
    6. -
    7. - Find the lowest Vespa version of the current deployments (if any) - - details: -
      - -
      -$ mvn vespa:compileVersion \
      -  -Dtenant=mytenant \
      -  -Dapplication=myapp
      -
      -
      -
    8. -
    9. - Build target/application.zip: -
      - -
      -$ mvn -U package -Dvespa.compile.version="$(cat target/vespa.compile.version)"
      -
      -
      -
    10. -
    11. - Run the Deploy the application step. - Here, the Vespa CLI command will deploy target/application.zip built in the step above. -
    12. -
    - - - -

    Next steps

    -
      -
    • - Vespa Cloud takes responsibility for rolling out application changes - to all production zones as well as testing the changes first. - You will usually want to set up a job which automatically builds your application package - when changes to it are checked in, to get continuous deployment of your application. - Read automated deployments - for automation, adding CD tests and multi-zone deployments. -
    • -
    • Once you have experience with load patterns, consider autoscaling.
    • -
    • - Set up monitoring. -
    • - -
    - - -

    Feeding data from an existing Vespa instance

    -

    To dump data from an existing Vespa instance, you can use this command with Vespa CLI:

    -
    -slices=10
    -for slice in $(seq 0 $((slices-1))); do
    -    vespa visit \
    -        --slices $slices --slice-id $slice \
    -        --target [existing Vespa instance endpoint] \
    -        | gzip > dump.$slice.gz &
    -done
    -
    -

    This dumps all the content to files, but you can also pipe the content directly into 'vespa feed'.

    -

    To feed the data:

    -
    -slices=10
    -for slice in $(seq 0 $((slices-1))); do
    -    zcat dump.$slice.gz | \
    -    vespa feed \
    -        --application <tenant>.<application>.<instance> \
    -        --target [zone endpoint from the Vespa Console] -
    -done
    -
    -

    Note that the different slices in these commands can be done in parallel on different machines.

    - - - -

    Accessing a public cloud application from another VPC on another account

    -

    - A common challenge when deploying on the public cloud, is network connectivity between workloads - running in different accounts and VPCs. Within in a team, this is often resolved by setting up - VPC peering between VPCs, but this has its challenges when coordinating between many different - teams and dynamic workloads. Vespa does not support direct VPC peering. -

    -

    There are three recommended options:

    -
      -
    1. - Use your public endpoints, but IPv6 if you can: The default. - There are many advantages to a Zero-Trust approach and accessing your application through the public endpoint. - If you use IPv6, you will also avoid some of the network costs associated with IPv4 NATs, etc. - For some applications, this option could be cost prohibitive, - but one should not assume this is the case for all applications - with a moderate amount of data being transferred over the endpoint. -
    2. -
    3. - Use private endpoints via AWS PrivateLink or GCP Private Service Connect: - Vespa allows you to set up private endpoints for exclusive access from your own, co-located VPCs. - This requires less administrative overhead than general VPC peering and is also more secure. - Refer to private endpoints. -
    4. -
    5. - Run Vespa workloads in your own account/project (Enclave): - The Vespa Cloud Enclave feature allows you to have all your Vespa workloads run in your own account. - In this case, you can set up any required peering to open the connection into your application. - While generally available, using Vespa Cloud Enclave requires significantly more effort - from the application team in terms of operating the service, - and is only recommended for larger applications that can justify the additional work - from e.g., a security or interoperability perspective. - Refer to Vespa Cloud Enclave. -
    6. -
    diff --git a/en/operations/production-deployment.mdx b/en/operations/production-deployment.mdx new file mode 100644 index 0000000000..c8eefc12f3 --- /dev/null +++ b/en/operations/production-deployment.mdx @@ -0,0 +1,243 @@ +--- +title: "Production Deployment" +--- + +Production zones enable serving from various locations, with a [CI/CD pipeline](/en/operations/automated-deployments) for safe deployments. This guide goes through the minimal steps for a production deployment - in short: + +- Configure a production zone in [deployment.xml](/en/reference/applications/deployment). +- Configure resources for clusters in [services.xml](/en/reference/applications/services/services). +- Name the tenant, application, log in. +- Create or have access to the data-plane cert/key pair. +- Deploy the application to Vespa Cloud. + +The sample application used in [getting started](/en/basics/deploy-an-application) is a good basis for these steps, see [source files](https://github.com/vespa-engine/sample-apps/tree/master/album-recommendation). + +Read [migrating to Vespa Cloud](/en/learn/migrating-to-cloud) first, as a primer on deployment and endpoint usage. + +There are alternative ways of deploying at the end of this guide, too. + +## deployment.xml + +Add a `` element to *deployment.xml*: + +```xml + + + aws-us-east-1c + + +``` + +If *deployment.xml* does not exist, add it to the application package root (next to *services.xml*). + + +**Note:** + +If the application uses [private endpoints](/en/operations/private-endpoints), add this configuration here, too, and run the setup steps in the guide. + + +## services.xml + +Modify *services.xml* - minimal example: + +```xml expandable + + + + + + + + + + + + 2 + + + + + + + + + +``` + +For production deployments, at least 2 nodes are required for each cluster to ensure availability during maintenance tasks and upgrades. In some cases one might still want to use just 1 node per cluster, even though redundancy will be lost. This can be done by adding [a validation override](/en/reference/applications/validation-overrides) `minimum-node-count` (and additional validation override `redundancy-one` in case of a content cluster). The `nodes` section is also where you specify your required [resources](/en/reference/applications/services/services#resources): + +```xml + + + +``` + +Also note the minimum redundancy requirement of 2: + +```xml +2 +``` + +## Minimum resources + +To help ensure a reliable service, there is a minimum resource requirement for nodes in the production environment. The minimum is currently 0.5 VCPU, 8Gb of memory, and for disk, 2 x memory for stateless nodes, or 3 x memory for content nodes. As the disk resource is normally the least expensive, we recommend it should be allocated generously to ensure it does not limit the use of more expensive cpu and memory resources. + +## Application name + +Give the deployment a name and log in: + +```bash +vespa config set target cloud +vespa config set application mytenant.myapp +vespa auth login +``` + +The tenant name is found in the console, the application is something unique within your organization - see [tenants, applications and instances](/en/learn/tenant-apps-instances). + +## Add public certificate + +Just as in the [getting started](/en/basics/deploy-an-application) guide, the application package needs the public key in the *security* directory. You might already have a pair, if not generate it: + +```bash +$ vespa auth cert -f +Success: Certificate written to security/clients.pem +Success: Certificate written to /Users/me/.vespa/mytenant.myapp.default/data-plane-public-cert.pem +Success: Private key written to /Users/me/.vespa/mytenant.myapp.default/data-plane-private-key.pem +``` + +Observe that the files are put in *$HOME/.vespa*. The content from *data-plane-public-cert.pem* is copied to *security/clients.pem*. More details on [data-plane access control permissions](/en/security/guide#permissions). + +## Deploy the application + +Package the application and deploy it to a production zone: + +```bash +vespa prod deploy +``` + +Find alternative deployment procedures in the next sections. + + +**Note:** + +The `vespa prod deploy` command to prod zones, which uses [deployment.xml](/en/reference/applications/deployment) differs from the `vespa deploy` command used for dev zones - see [environments](/en/operations/environments). + + +## Endpoints + +Find the 'zone' endpoint to use under Endpoints in the [console](https://console.vespa-cloud.com/). There is an mTLS endpoint for each zone by default. See [configuring mTLS](/en/security/guide#configuring-mtls) for how to use mTLS certificates. + +You can also add [access tokens](/en/security/guide#configuring-tokens) in the console as an alternative to mTLS, and specify [global](/en/reference/applications/deployment#endpoints-global) and [private](/en/reference/applications/deployment#endpoint-private) endpoints in *deployment.xml*. + +Write data efficiently using the [document/v1 API](/en/reference/api/document-v1) using HTTP/2, or with the [Vespa CLI](/en/clients/vespa-cli). There is also a [Java library](/en/clients/vespa-feed-client#java-library). + +To feed data from a self-hosted Vespa into a new cloud instances, see the [appendix](#feeding-data-from-an-existing-vespa-instance) or [cloning applications and data](/en/operations/cloning). + +Also see the [http best practices documentation](/en/clients/http-best-practices). + +## Automate deployments + +Use [deploy-vector-search.yaml](https://github.com/vespa-cloud/vector-search/blob/main/.github/workflows/deploy-vector-search.yaml) as a starting point, and see [Automating with GitHub Actions](/en/operations/automated-deployments#automating-with-github-actions) for more information. + +## Production deployment using console + +Instead of using the [Vespa CLI](/en/clients/vespa-cli), one can build an application package for production deployment using zip only: + +- Create [deployment.xml](#deployment-xml) and modify [services.xml](#services-xml) as above. +- Skip the [Application name](#application-name) step. +- Add a public certificate to *security/clients.pem*. See [creating a self-signed certificate](/en/basics/deploy-an-application-shell#create-a-self-signed-certificate) for how to create the key/cert pair, then copy the cert file to *security/clients.pem*. At this point, the files are ready for deployment. +- Create a deployable zip-file: + + ```bash + zip -r application.zip . \ + -x application.zip "ext/*" README.md .gitignore ".idea/*" + ``` +- Click *Create Application* in the [console](https://console.vespa-cloud.com/). Select the *PROD* tab. Enter a name for the application and drop the *application.zip* file in the upload section. +- Click *Create and deploy* to deploy the application to the production environment. + +## Production deployment with components + +Deploying an application with [Components](/en/applications/components) is a little different from above: + +- The application package root is at *src/main/application*. +- Find the Vespa API version to compile the component. +- The application package is built into a zip artifact, before deploying it. + +See [Getting started java](/en/basics/deploy-an-application-java) for prerequisites. Procedure: + + + +Use the [album-recommendation-java](https://github.com/vespa-engine/sample-apps/tree/master/album-recommendation-java) sample application as a starting point. + + +Make the same changes to *src/main/application/deployment.xml* and *src/main/application/services.xml*. + + +Run the same steps for [Application name](#application-name) and [Add public certificate](#add-public-certificate). + + +Find the lowest Vespa version of the current deployments (if any) - [details](/en/operations/automated-deployments#deploying-components): + +```bash +mvn vespa:compileVersion \ + -Dtenant=mytenant \ + -Dapplication=myapp +``` + + +Build *target/application.zip*: + +```bash +mvn -U package -Dvespa.compile.version="$(cat target/vespa.compile.version)" +``` + + +Run the [Deploy the application](#deploy-the-application) step. Here, the Vespa CLI command will deploy *target/application.zip* built in the step above. + + + +## Next steps + +- Vespa Cloud takes responsibility for rolling out application changes to all production zones as well as testing the changes first. You will usually want to set up a job which automatically builds your application package when changes to it are checked in, to get continuous deployment of your application. Read [automated deployments](/en/operations/automated-deployments) for automation, adding CD tests and multi-zone deployments. +- Once you have experience with load patterns, consider [autoscaling](/en/operations/autoscaling). +- Set up [monitoring](/en/operations/monitoring). + +## Feeding data from an existing Vespa instance + +To dump data from an existing Vespa instance, you can use this command with Vespa CLI: + +```bash +slices=10 +for slice in $(seq 0 $((slices-1))); do + vespa visit \ + --slices $slices --slice-id $slice \ + --target [existing Vespa instance endpoint] \ + | gzip > dump.$slice.gz & +done +``` + +This dumps all the content to files, but you can also pipe the content directly into 'vespa feed'. + +To feed the data: + +```bash +slices=10 +for slice in $(seq 0 $((slices-1))); do + zcat dump.$slice.gz | \ + vespa feed \ + --application .. \ + --target [zone endpoint from the Vespa Console] - +done +``` + +Note that the different slices in these commands can be done in parallel on different machines. + +## Accessing a public cloud application from another VPC on another account + +A common challenge when deploying on the public cloud, is network connectivity between workloads running in different accounts and VPCs. Within in a team, this is often resolved by setting up VPC peering between VPCs, but this has its challenges when coordinating between many different teams and dynamic workloads. Vespa does not support direct VPC peering. + +There are three recommended options: + +1. **Use your public endpoints, but IPv6 if you can:** The default. There are many advantages to a Zero-Trust approach and accessing your application through the public endpoint. If you use IPv6, you will also avoid some of the network costs associated with IPv4 NATs, etc. For some applications, this option could be cost prohibitive, but one should not assume this is the case for all applications with a moderate amount of data being transferred over the endpoint. +2. **Use private endpoints via AWS PrivateLink or GCP Private Service Connect:** Vespa allows you to set up private endpoints for exclusive access from your own, co-located VPCs. This requires less administrative overhead than general VPC peering and is also more secure. Refer to [private endpoints](/en/operations/private-endpoints). +3. **Run Vespa workloads in your own account/project (Enclave):** The Vespa Cloud Enclave feature allows you to have all your Vespa workloads run in your own account. In this case, you can set up any required peering to open the connection into your application. While generally available, using Vespa Cloud Enclave requires significantly more effort from the application team in terms of operating the service, and is only recommended for larger applications that can justify the additional work from e.g., a security or interoperability perspective. Refer to [Vespa Cloud Enclave](/en/operations/enclave/enclave). \ No newline at end of file diff --git a/en/operations/reindexing-cloud.html b/en/operations/reindexing-cloud.html deleted file mode 100644 index d6d319cf72..0000000000 --- a/en/operations/reindexing-cloud.html +++ /dev/null @@ -1,102 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Reindexing on Vespa Cloud" -applies_to: cloud ---- - -

    - When the indexing pipeline of an application changes, Vespa may need to - reindex stored data so the index reflects the new specification. - On Vespa Cloud this is handled for you: reindexing is enabled and started automatically - as part of your deployment, and you can follow, pause, resume, re-trigger, and tune it from the - Vespa Cloud console. - There is no need to call the reindex endpoint - and redeploy manually as you would in a - self-managed deployment. -

    -

    - This page describes the Vespa Cloud workflow. - See Reindexing for the underlying concepts, reindexing states, - and schema-change use cases that apply to all deployments. -

    - - - -

    How reindexing is triggered

    -

    - When you deploy a change that requires reindexing (see - changes that require reindexing - for the full list), Vespa Cloud enables and starts reindexing automatically once the deployment - has converged on the new configuration. - This replaces the manual procedure in a self-managed deployment, where reindexing is enabled through the - reindex endpoint and started with another redeployment. -

    -

    - Only the stored document fields are re-fed; - all derived fields are recomputed. - Reindexing runs in the background, but how much it affects the cluster depends on the available - resources and the current load. - You control this trade-off with the reindexing speed: - a lower speed uses fewer resources and has less impact on concurrent feed and queries, - while a higher speed completes sooner at the cost of higher load. - Until reindexing completes, affected fields may be empty or carry annotations that do not match the - new configuration. -

    - - - -

    Controlling reindexing from the console

    -

    - Reindexing is managed per content cluster from the Clusters view of a deployment - in the Vespa Cloud console. - Each content cluster lists its document types with the current reindexing - state, speed, progress, and start and end times. -

    -Reindexing table and controls for a content cluster in the Vespa Cloud console -

    - Select one or more document types to: -

    -
      -
    • Reindex: trigger a fresh reindexing, for example after deploying a - document processor that transforms data, - or to re-run an earlier reindexing.
    • -
    • Pause and Resume: temporarily halt an ongoing reindexing, - and resume it later from where it stopped.
    • -
    • Disable reindexing: stop reindexing for the selected document types.
    • -
    -

    - When you trigger reindexing, a dialog lets you choose the reindexing speed: - Conservative, Balanced (the recommended default), or Aggressive. - The speed balances time to completion against resource usage: - a more aggressive speed completes reindexing sooner but consumes more of the cluster's resources, - while a more conservative speed reduces the impact on concurrent feed and queries. -

    -Dialog for confirming reindexing and selecting the reindexing speed - - - -

    Monitoring progress

    -

    - The Clusters view shows live progress for each document type: the state - (pending, running, successful, or failed), - a completion percentage, and the start and end times. -

    -A completed reindexing showing state successful and 100% progress -

    - In addition, a running reindexing is shown as a Reindexing annotation on the metric charts. - See Monitoring for details. - This makes it easy to correlate reindexing with any change in feed or query latency while it runs. -

    - - - - - diff --git a/en/operations/reindexing.html b/en/operations/reindexing.html deleted file mode 100644 index fecbb24465..0000000000 --- a/en/operations/reindexing.html +++ /dev/null @@ -1,201 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Reindexing" -redirect_from: -- /en/reindexing.html ---- - -

    - When the indexing pipeline of a Vespa application changes, - Vespa may automatically refeed stored data such that the index is updated according to the new specification. - Changes in the indexing pipeline may be due to changes in external libraries, - e.g. for linguistics, or due to changes in the configuration done by the user, - such as the indexing script in a document's schema, - or the indexing mode - of a document type in a content cluster. - Reindexing can be done for an application's full corpus, for only certain content clusters, - or for only certain document types in certain clusters, - using the reindex endpoint, - and inspected at the reindexing endpoint, details - are described below. -

    - -{% include note.html content='Using Vespa Cloud? -Reindexing is triggered automatically as part of your deployment, and is controlled -(start, pause, speed) from the console, with no need to call the reindex endpoint manually. -See Reindexing on Vespa Cloud. -The concepts, states, and use cases below apply to all deployments; the -manual procedure below is for self-managed Vespa.' %} - - - -

    Start reindexing

    -

    - When a change in the indexing pipeline of an application is deployed, this is discovered by the config server - (see the prepare endpoint for details). - If the change is to be deployed, a validation override might have to be - added to the application package (e.g. if changing match settings for a field). - Deployment output will then list the reindex actions required to make the index reflect the new indexing pipeline. - Use the reindex endpoint - to mark reindexing as ready for affected document types, - but only after the new indexing pipeline is successfully deployed, - i.e. when the application has converged on the config generation that introduced the change. - Reindexing then commences with the next deployment of the application. - Summary of steps needed to enable and start reindexing:

    -
      -
    1. Change indexing pipeline in application package, adding validation overrides if needed
    2. -
    3. Wait until config has converged on new config generation
    4. -
    5. Mark reindexing change as ready by POSTing to reindex endpoint
    6. -
    7. Start reindexing job by deploying application package one more time
    8. -
    - - - - -

    Reindexing progress

    -

    - Reindexing is done by a component in each content cluster that - visits all documents of the indicated types, - and re-feeds these through the indexing chain of the cluster. - (Note that only the document fields are re-fed — - all derived fields, produced by the indexing pipeline, are recomputed.) - The reindexing process avoids write races with concurrent feed by locking - small subsets of the corpus when reindexing them; - this may cause elevated write latencies for a fraction of concurrent write operations, - but does not impact general throughput. - Moreover, since reindexing can be both lengthy and resource consuming, depending on the corpus, - the process is tuned to yield resources to other tasks, - such as external feed and serving, - and is generally safe to run in the background. -

    -

    - Reindexing is done for one document type at a time, in parallel across content clusters. - Detailed progress can be found at the - reindexing endpoint. - If state is failed, reindexing attempts to resume from the position where it failed after a grace period of some minutes. - State pending indicates reindexing will start, or resume, when the cluster is ready, - while running means it's currently progressing. - Finally, successful means all documents of that type were successfully reindexed. - Additionally, if the speed of a reindexing is 0.0—set by users—that reindexing is - halted until the speed is either set to a positive value again, or it is replaced by a new reindexing of that document type. -

    - - - -

    Procedure

    -

    - Refer to schema changes - for a procedure / way to test the reindexing feature, and tools to validate the data. -

    - - - -

    Use cases

    -

    - Below are sample changes to the schema for different use cases, - or examples of operational steps for data manipulation. -

    - - - - - - - - - - - - - - - - - - - -
    Use caseDescription
    clear field -

    - To clear a field, do a partial update of all documents with the value, say an empty string. -

    - It is also possible to use reindexing, but there is a twist - intuitively, this would work: -

    -
    -field artist type string {
    -    indexing: "" | summary | index
    -}
    -
    -

    - However, the reset only works for synthetic fields. -

    - A solution is to deploy a document processor that empties the field, - to the default indexing chain - then trigger a reprocessing. -

    -
    change indexing settings -

    - As reindexing takes time, a field's data can be in one state or another, - while the queries to it have the most current state. - This is OK for many changes and applications. -

    - If not, it is possible to reindex to a new field for a more atomic change. - Add a synthetic field outside the document definition - and pipe the content of the current field to it: -

    -
    -search mydocs {
    -
    -    field title_non_stemmed type string {
    -        indexing: input title | index | summary
    -        stemming: none
    -    }
    -
    -    document mydocs {
    -        field title type string {
    -            indexing: index | summary
    -        }
    -
    -

    - Once reindexing is completed, switch queries to use the new field. - This solution naturally increases memory and disk requirements in the transition. -

    -

    - Going back to using the original field with the new settings can be done by changing - the index settings for the original field, wait for reindexing to be finished and - start using the original field again in queries, then remove the temporary synthetic - field. -

    -
    -

    - Relevant pointers: -

    - - - diff --git a/en/operations/reindexing.mdx b/en/operations/reindexing.mdx new file mode 100644 index 0000000000..3c4ae31884 --- /dev/null +++ b/en/operations/reindexing.mdx @@ -0,0 +1,64 @@ +--- +title: "Reindexing" +--- + +When the indexing pipeline of a Vespa application changes, Vespa may automatically refeed stored data such that the index is updated according to the new specification. Changes in the indexing pipeline may be due to changes in external libraries, e.g. for linguistics, or due to changes in the configuration done by the user, such as the [indexing script](/en/reference/writing/indexing-language) in a document's schema, or the [indexing mode](/en/reference/applications/services/content#document.mode) of a document type in a content cluster. Reindexing can be done for an application's full corpus, for only certain content clusters, or for only certain document types in certain clusters, using the [reindex endpoint](/en/reference/api/deploy-v2#reindex), and inspected at the [reindexing endpoint](/en/reference/api/deploy-v2#reindexing), details are described below. + +## Start reindexing + +When a change in the indexing pipeline of an application is deployed, this is discovered by the config server (see the [prepare endpoint](/en/reference/api/deploy-v2#prepare-session) for details). If the change is to be deployed, a [validation override](/en/reference/applications/validation-overrides) might have to be added to the application package (e.g. if changing match settings for a field). Deployment output will then list the *reindex actions* required to make the index reflect the new indexing pipeline. Use the [reindex endpoint](/en/reference/api/deploy-v2#reindex) to mark reindexing as ready for affected document types, **but only after the new indexing pipeline is successfully deployed**, i.e. when the application has converged on the config generation that introduced the change. Reindexing then commences with the next deployment of the application. Summary of steps needed to enable and start reindexing: + + + +Change indexing pipeline in application package, adding validation overrides if needed + + +Wait until config has converged on new config generation + + +Mark reindexing change as ready by POSTing to reindex endpoint + + +Start reindexing job by deploying application package one more time + + + +## Reindexing progress + +Reindexing is done by a component in each content cluster that [visits](/en/writing/visiting) all documents of the indicated types, and re-feeds these through the [indexing chain](/en/writing/indexing) of the cluster. (Note that only the [document fields](/en/reference/schemas/schemas#document) are re-fed — all derived fields, produced by the indexing pipeline, are recomputed.) The reindexing process avoids write races with concurrent feed by locking [small subsets](/en/content/buckets) of the corpus when reindexing them; this may cause elevated write latencies for a fraction of concurrent write operations, but does not impact general throughput. Moreover, since reindexing can be both lengthy and resource consuming, depending on the corpus, the process is tuned to yield resources to other tasks, such as external feed and serving, and is generally safe to run in the background. + +Reindexing is done for one document type at a time, in parallel across content clusters. Detailed progress can be found at the [reindexing endpoint](/en/reference/api/deploy-v2#reindexing). If state is *failed*, reindexing attempts to resume from the position where it failed after a grace period of some minutes. State *pending* indicates reindexing will start, or resume, when the cluster is ready, while *running* means it's currently progressing. Finally, *successful* means all documents of that type were successfully reindexed. Additionally, if the *speed* of a reindexing is `0.0`—set by users—that reindexing is halted until the speed is either set to a positive value again, or it is replaced by a new reindexing of that document type. + +## Procedure + +Refer to [schema changes](/en/reference/schemas/schemas#modifying-schemas) for a procedure / way to test the reindexing feature, and tools to validate the data. + +## Use cases + +Below are sample changes to the schema for different use cases, or examples of operational steps for data manipulation. + + + + + + + + + + + + + + + + + + +
    Use caseDescription
    **clear field**To clear a field, do a partial update of all documents with the value, say an empty string.

    It is also possible to use reindexing, but there is a twist - intuitively, this would work:

    {`field artist type string {`}
    {`indexing: "" | summary | index`}
    {`}`}

    However, the reset only works for synthetic fields.

    A solution is to deploy a document processor that empties the field, to the default indexing chain - then trigger a reprocessing.
    **change indexing settings**As reindexing takes time, a field's data can be in one state or another, while the queries to it have the most current state. This is OK for many changes and applications.

    If not, it is possible to reindex to a new field for a more atomic change. Add a *synthetic field* outside the *document definition* and pipe the content of the current field to it:

    {`search mydocs {`}
    {`field title_non_stemmed type string {`}
    {`indexing: input title | index | summary`}
    {`stemming: none`}
    {`}`}
    {`document mydocs {`}
    {`field title type string`}
    {`{`}
    {`indexing: index | summary`}
    {`}`}

    Once reindexing is completed, switch queries to use the new field. This solution naturally increases memory and disk requirements in the transition.

    Going back to using the original field with the new settings can be done by changing the index settings for the original field, wait for reindexing to be finished and start using the original field again in queries, then remove the temporary synthetic field.
    + +Relevant pointers: + + + + + \ No newline at end of file diff --git a/en/operations/self-managed/admin-procedures.html b/en/operations/self-managed/admin-procedures.html deleted file mode 100644 index 2288a832e2..0000000000 --- a/en/operations/self-managed/admin-procedures.html +++ /dev/null @@ -1,563 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Administrative Procedures" -applies_to: self-managed -redirect_from: -- /en/operations/admin-procedures.html -- /en/operations-selfhosted/admin-procedures.html ---- - - - -

    Install

    -

    - Refer to the - multinode-HA sample application for a primer on how to set up a cluster - - use this as a starting point. - Try the - Multinode testing and observability sample app to get familiar with interfaces and behavior. -

    - - - -

    Vespa start / stop / restart

    -

    - Start and stop all services on a node: -

    -
    -$ $VESPA_HOME/bin/vespa-start-services
    -$ $VESPA_HOME/bin/vespa-stop-services
    -
    -

    Likewise, for the config server:

    -
    -$ $VESPA_HOME/bin/vespa-start-configserver
    -$ $VESPA_HOME/bin/vespa-stop-configserver
    -
    -

    - There is no restart command, do a stop then start for a restart. - Learn more about which processes / services are started at Vespa startup, - read the start sequence - and find training videos in the vespaengine YouTube channel. -

    -

    - Use vespa-sentinel-cmd - to stop/start individual services. -

    -{% include important.html content="Running vespa-stop-services on a content node will call -prepareRestart to optimize restart time, -and is the recommended way to stop Vespa on a node." %} -

    - See multinode for systemd /systemctl examples. - Docker containers has relevant start/stop information, too. -

    -

    Content node maintenance mode

    -

    - When stopping a content node temporarily (e.g. for a software upgrade), consider manually setting the node into - maintenance mode before stopping the node to prevent - automatic redistribution of data while the node is down. Maintenance mode must be manually removed once the node has - come back online. See also: cluster state. -

    -

    - Example of setting a node with distribution key 42 into - maintenance mode using vespa-set-node-state, - additionally supplying a reason that will be recorded by the cluster controller: -

    -
    -  $ vespa-set-node-state --type storage --index 42 maintenance "rebooting for software upgrade"
    -
    -

    - After the node has come back online, clear maintenance mode by marking the node as up: -

    -
    -  $ vespa-set-node-state --type storage --index 42 up
    -
    -

    - Note that if the above commands are executed locally on the host running the services for node 42, - --index 42 can be omitted; vespa-set-node-state will use the distribution key of - the local node if no --index has been explicitly specified. -

    - -

    System status

    -
      -
    • Use vespa-config-status - on a node in hosts.xml to verify all services run with updated config
    • -
    • Make sure - VESPA_CONFIGSERVERS is set and identical on all nodes in hosts.xml
    • -
    • Use the cluster controller status page (below) - to track the status of search/storage nodes.
    • -
    • Check logs
    • -
    • Use performance graphs, System Activity Report (sar) - or status pages to track load
    • -
    • Use query tracing
    • -
    • Disk and/or memory might be exhausted and block feeding - recover from - feed block
    • -
    - - -

    Status pages

    -

    - All Vespa services have status pages, for showing health, Vespa version, config, and metrics. - Status pages are subject to change at any time - take care when automating. Procedure: -

    -
      -
    1. - Find the port: - The status pages runs on ports assigned by Vespa. To find status page ports, - use vespa-model-inspect - to list the services run in the application. -
      -$ vespa-model-inspect services
      -
      - To find the status page port for a specific node for a specific service, - pick the correct service and run: -
      -$ vespa-model-inspect service [Options] <service-name>
      -
      -
    2. - Get the status and metrics: - distributor, storagenode, searchnode and - container-clustercontroller are content services with status pages. - These ports are tagged HTTP. The cluster controller have multiple ports tagged HTTP, - where the port tagged STATE is the one with the status page. - Try connecting to the root at the port, or /state/v1/metrics. - The distributor and storagenode status pages are available at /: -
      -$ vespa-model-inspect service searchnode
      -
      -  searchnode @ myhost.mydomain.com : search
      -  search/search/cluster.search/0
      -  tcp/myhost.mydomain.com:19110 (STATUS ADMIN RTC RPC)
      -  tcp/myhost.mydomain.com:19111 (FS4)
      -  tcp/myhost.mydomain.com:19112 (TEST HACK SRMP)
      -  tcp/myhost.mydomain.com:19113 (ENGINES-PROVIDER RPC)
      -  tcp/myhost.mydomain.com:19114 (HEALTH JSON HTTP)
      -  $ curl http://myhost.mydomain.com:19114/state/v1/metrics
      -  ...
      -  $ vespa-model-inspect service distributor
      -  distributor @ myhost.mydomain.com : content
      -  search/distributor/0
      -  tcp/myhost.mydomain.com:19116 (MESSAGING)
      -  tcp/myhost.mydomain.com:19117 (STATUS RPC)
      -  tcp/myhost.mydomain.com:19118 (STATE STATUS HTTP)
      -  $ curl http://myhost.mydomain.com:19118/state/v1/metrics
      -  ...
      -  $ curl http://myhost.mydomain.com:19118/
      -  ...
      -
      -
    3. - Use the cluster controller status page: - A status page for the cluster controller is available at the status port at - http://hostname:port/clustercontroller-status/v1/<clustername>. - If clustername is not specified, the available clusters will be listed. - The cluster controller leader status page will show if any nodes are operating with differing cluster state versions. - It will also show how many data buckets are pending merging (document set reconciliation) - due to either missing or being out of sync. -
      -$ vespa-model-inspect service container-clustercontroller | grep HTTP
      -
      -

      - With multiple cluster controllers, look at the one with a "/0" suffix in its config ID; - it is the preferred leader. -

      - The cluster state version is listed under the SSV table column. - Divergence here usually points to host or networking issues. -

      -
    4. -
    - - - -

    Cluster state

    -

    - Cluster and node state information is available through the - /cluster/v2 API. - This API can also be used to set a user state for a node - alternatively use: -

    - -

    - Also see the cluster controller status page. -

    - State is persisted in a ZooKeeper cluster, restarting/changing a cluster controller preserves: -

    -
      -
    • Last cluster state version number, for new cluster controller handover at restarts
    • -
    • User states, set by operators - i.e. nodes manually set to down / maintenance
    • -
    -

    - In case of state data lost, the cluster state is reset - see - cluster controller for implications. -

    - - - -

    Cluster controller configuration

    -

    - It is recommended to run cluster controllers on the same hosts as - config servers, - as they share a zookeeper cluster for state and deploying three nodes is best practise for both. - See the multinode-HA - sample app for a working example. -

    -

    - To configure the cluster controller, use - services.xml and/or add - - configuration under the services element - example: -

    -
    -<services version="1.0">
    -    <config name="vespa.config.content.fleetcontroller">
    -        <min_time_between_new_systemstates>5000</min_time_between_new_systemstates>
    -    </config>
    -
    -

    - A broken content node may end up with processes constantly restarting. - It may die during initialization due to accessing corrupt files, - or it may die when it starts receiving requests of a given type triggering a node local bug. - This is bad for distributor nodes, - as these restarts create constant ownership transfer between distributors, - causing windows where buckets are unavailable. -

    - The cluster controller has functionality for detecting such nodes. - If a node restarts in a way that is not detected as a controlled shutdown, more than - - max_premature_crashes, - the cluster controller will set the wanted state of this node to be down. -

    - Detecting a controlled restart is currently a bit tricky. - A controlled restart is typically initiated by sending a TERM signal to the process. - Not having any other sign, - the content layer has to assume that all TERM signals are the cause of controlled shutdowns. - Thus, if the process keep being killed by kernel due to using too much memory, - this will look like controlled shutdowns to the content layer. -

    - - - -

    Monitor distance to ideal state

    -

    - Refer to the distribution algorithm. - Use distributor status pages to inspect state metrics, - see metrics. - idealstate.merge_bucket.pending is the best metric to track, - it is 0 when the cluster is balanced - a non-zero value indicates buckets out of sync. -

    - - - -

    Cluster configuration

    -
      -
    • Running vespa prepare will not change served - configuration until vespa activate is run. - vespa prepare will warn about all config changes that require restart. -
    • - Refer to schemas for how to add/change/remove these. -
    • - Refer to elasticity - for how to add/remove capacity from a Vespa cluster, procedure below. -
    • - See chained components - for how to add or remove searchers and document processors. -
    • - Refer to the sizing examples - for changing from a flat to grouped content cluster. -
    • -
    - - - -

    Add or remove a content node

    -
      -
    1. - Node setup: - Prepare the node by installing software, set up the file systems/directories - and set - VESPA_CONFIGSERVERS. - Start the node. -
    2. - Modify configuration: - Add/remove a node-element in services.xml - and hosts.xml. - Refer to multinode install. - Make sure the distribution-key is unique. -
    3. - Deploy: - Observe metrics to track progress as the cluster redistributes documents. - Use the cluster controller - to monitor the state of the cluster. -
    4. - Tune performance (optional): - Use - maxpendingidealstateoperations to tune concurrency of bucket merge operations from distributor nodes. - Likewise, tune merges - - concurrent merge operations per content node. The tradeoff is speed of bucket replication vs - use of resources, which impacts the applications' regular load. -
    5. - Finish: - The cluster is done redistributing when idealstate.merge_bucket.pending - is zero on all distributors. -
    6. -
    -

    - Do not remove more than redundancy-1 nodes at a time, to avoid data loss. - Observe idealstate.merge_bucket.pending to know bucket replica status, - when zero on all distributor nodes, it is safe to remove more nodes. - If grouped distribution - is used to control bucket replicas, remove all nodes in a group - if the redundancy settings ensure replicas in each group. -

    - To increase bucket redundancy level before taking nodes out, - retire nodes. - Again, track idealstate.merge_bucket.pending to know when done. - Use the /cluster/v2 API or - vespa-set-node-state - to set a node to the retired state. You can set any number of nodes retired at the same time. - The cluster controller's - status page lists node states. -

    - An alternative to increasing cluster size is building a new cluster, then migrate documents to it. - This is supported using visiting. -

    - To merge two content clusters, add nodes to the cluster like above, considering: -

    - - - - -

    Topology change

    -

    - Read changing topology first, - and plan the sequence of steps. -

    -

    - Make sure to not change the distribution-key - for nodes in services.xml. -

    -

    - It is not required to restart nodes as part of this process -

    - - - -

    Add or remove services on a node

    -

    - It is possible to run multiple Vespa services on the same host. - If changing the services on a given host, - stop Vespa on the given host before running vespa activate. - This is because the services are dynamically allocated port numbers, - depending on what is running on the host. - Consider if some of the services changed are used by services on other hosts. - In that case, restart services on those hosts too. Procedure: -

    -
      -
    1. Edit services.xml and hosts.xml
    2. -
    3. Stop Vespa on the nodes that have changes
    4. -
    5. Run vespa prepare and vespa activate
    6. -
    7. Start Vespa on the nodes that have changes
    8. -
    - - - -

    Troubleshooting

    -

    - Also see the FAQ. -

    - - - - - - - - - - - - - - - - - - - - - -
    No endpoint -

    - Most problems with the quick start guides are due to Docker out of memory. - Make sure at least 6G memory is allocated to Docker: -

    -
    -$ docker info | grep "Total Memory"
    -or
    -$ podman info | grep "memTotal"
    -
    - OOM symptoms include -
    -INFO: Problem with Handshake localhost:8080 ssl=false: localhost:8080 failed to respond
    -
    - The container is named vespa in the guides, for a shell do: -
    -$ docker exec -it vespa bash
    -
    -
    Log viewing -

    - Use vespa-logfmt to view the vespa log - example: -

    -
    -$ /opt/vespa/bin/vespa-logfmt -l warning,error
    -
    -
    Json -

    For json pretty-print, append

    -
    -| python -m json.tool
    -
    - to commands that output json - or use jq. -
    Routing -

    - Vespa lets application set up custom document processing / indexing, - with different feed endpoints. - Refer to indexing for how to configure this in services.xml. -

    -

    - #13193 - has a summary of problems and solutions. -

    -
    Tracing -

    - Use tracelevel - to dump the routes and hops for a write operation - example: -

    -
    -$ curl -H Content-Type:application/json --data-binary @docs.json \
    -  $ENDPOINT/document/v1/mynamespace/doc/docid/1?tracelevel=4 | jq .
    -
    -{
    -    "pathId": "/document/v1/mynamespace/doc/docid/1",
    -    "id": "id:mynamespace:doc::1",
    -    "trace": [
    -        { "message": "[1623413878.905] Sending message (version 7.418.23) from client to ..." },
    -        { "message": "[1623413878.906] Message (type 100004) received at 'default/container.0' ..." },
    -        { "message": "[1623413878.907] Sending message (version 7.418.23) from 'default/container.0' ..." },
    -        { "message": "[1623413878.907] Message (type 100004) received at 'default/container.0' ..." },
    -        { "message": "[1623413878.909] Selecting route" },
    -        { "message": "[1623413878.909] No cluster state cached. Sending to random distributor." }
    -
    -
    - - - -

    Clean start mode

    -

    - There has been rare occasions were Vespa stored data that was internally inconsistent. - For those circumstances it is possible to start the node in a - - validate_and_sanitize_docstore mode. - This will do its best to clean up inconsistent data. - However, detecting that this is required is not easy, consult the Vespa Team first. - In order for this approach to work, all nodes must be stopped before enabling this feature - - this to make sure the data is not redistributed. -

    - - - -

    Content cluster configuration

    - - - - - - - - - - - - - - - - - - -
    Availability vs resources -

    - Keeping index structures costs resources. Not all replicas of buckets are - necessarily searchable, unless configured using - searchable-copies. - As Vespa indexes buckets on-demand, the most cost-efficient setting is 1, - if one can tolerate temporary coverage loss during node failures. -

    -
    Data retention vs size -

    - When a document is removed, the document data is not immediately purged. - Instead, remove-entries (tombstones of removed documents) are kept - for a configurable amount of time. The default is two weeks, refer to - removed-db prune age. - This ensures that removed documents stay removed in a distributed system where nodes change state. - Entries are removed periodically after expiry. - Hence, if a node comes back up after being down for more than two weeks, - removed documents are available again, unless the data on the node is wiped first. - A larger prune age will grow the storage size as this keeps - document and tombstones longer. -

    - Note: The backend does not store remove-entries for nonexistent documents. - This to prevent clients sending wrong document identifiers from - filling a cluster with invalid remove-entries. - A side effect is that if a problem has caused all replicas of a bucket to be unavailable, - documents in this bucket cannot be marked removed until at least one replica is available again. - Documents are written in new bucket replicas while the others are down - - if these are removed, then older versions of these will not re-emerge, - as the most recent change wins. -

    Transition time -

    - See transition-time - for tradeoffs for how quickly nodes are set down vs. system stability. -

    -
    Removing unstable nodes -

    - One can configure how many times a node is allowed to crash before it will automatically be removed. - The crash count is reset if the node has been up or down continuously for more than the - stable state period. - If the crash count exceeds - max premature crashes, the node will be disabled. - Refer to troubleshooting. -

    -
    Minimal amount of nodes required to be available -

    - A cluster is typically sized to handle a given load. - A given percentage of the cluster resources are required for normal operations, - and the remainder is the available resources that can be used if some of the nodes are no longer usable. - If the cluster loses enough nodes, it will be overloaded: -

    -
      -
    • Remaining nodes may create disk full situation. - This will likely fail a lot of write operations, and if disk is shared with OS, - it may also stop the node from functioning.
    • -
    • Partition queues will grow to maximum size. - As queues are processed in FIFO order, operations are likely to get long latencies.
    • -
    • Many operations may time out while being processed, - causing the operation to be resent, adding more load to the cluster.
    • -
    • When new nodes are added, they cannot serve requests - before data is moved to the new nodes from the already overloaded nodes. - Moving data puts even more load on the existing nodes, - and as moving data is typically not high priority this may never actually happen.
    • -
    - To configure what the minimal cluster size is, use - min-distributor-up-ratio and - min-storage-up-ratio. -
    diff --git a/en/operations/self-managed/admin-procedures.mdx b/en/operations/self-managed/admin-procedures.mdx new file mode 100644 index 0000000000..27bce04f0d --- /dev/null +++ b/en/operations/self-managed/admin-procedures.mdx @@ -0,0 +1,295 @@ +--- +title: "Administrative Procedures" +sidebarTitle: "Admin procedures" +--- + +## Install + +Refer to the [multinode-HA](https://github.com/vespa-engine/sample-apps/tree/master/examples/operations/multinode-HA) sample application for a primer on how to set up a cluster - use this as a starting point. Try the [Multinode testing and observability](https://github.com/vespa-engine/sample-apps/tree/master/examples/operations/multinode) sample app to get familiar with interfaces and behavior. + +## Vespa start / stop / restart + +Start and stop all services on a node: + +`$ $VESPA_HOME/bin/`[`vespa-start-services`](/en/reference/operations/self-managed/tools#vespa-start-services) +`$ $VESPA_HOME/bin/`[`vespa-stop-services`](/en/reference/operations/self-managed/tools#vespa-stop-services) + +Likewise, for the config server: + +`$ $VESPA_HOME/bin/`[`vespa-start-configserver`](/en/reference/operations/self-managed/tools#vespa-start-configserver) +`$ $VESPA_HOME/bin/`[`vespa-stop-configserver`](/en/reference/operations/self-managed/tools#vespa-stop-configserver) + +There is no *restart* command, do a *stop* then *start* for a restart. Learn more about which processes / services are started at [Vespa startup](/en/operations/self-managed/config-sentinel#start-sequence), read the [start sequence](/en/operations/self-managed/configuration-server#start-sequence) and find training videos in the vespaengine [YouTube channel](https://www.youtube.com/@vespaai). + +Use [vespa-sentinel-cmd](/en/reference/operations/self-managed/tools#vespa-sentinel-cmd) to stop/start individual services. + + +**Important:** + +Running *vespa-stop-services* on a content node will call [prepareRestart](/en/reference/operations/self-managed/tools#vespa-proton-cmd) to optimize restart time, and is the recommended way to stop Vespa on a node. + + +See [multinode](/en/operations/self-managed/multinode-systems#aws-ec2) for *systemd* /*systemctl* examples. [Docker containers](/en/operations/self-managed/docker-containers) has relevant start/stop information, too. + +### Content node maintenance mode + +When stopping a content node *temporarily* (e.g. for a software upgrade), consider manually setting the node into [maintenance mode](/en/reference/api/cluster-v2#maintenance) *before* stopping the node to prevent automatic redistribution of data while the node is down. Maintenance mode must be manually removed once the node has come back online. See also: [cluster state](#cluster-state). + +Example of setting a node with [distribution key](/en/reference/applications/services/content#node) 42 into `maintenance` mode using [vespa-set-node-state](/en/reference/operations/self-managed/tools#vespa-set-node-state), additionally supplying a reason that will be recorded by the cluster controller: + +```bash + $ vespa-set-node-state --type storage --index 42 maintenance "rebooting for software upgrade" +``` + +After the node has come back online, clear maintenance mode by marking the node as `up`: + +```bash + $ vespa-set-node-state --type storage --index 42 up +``` + +Note that if the above commands are executed *locally* on the host running the services for node 42, `--index 42` can be omitted; `vespa-set-node-state` will use the distribution key of the local node if no `--index` has been explicitly specified. + +## System status + +- Use [vespa-config-status](/en/reference/operations/self-managed/tools#vespa-config-status) on a node in [hosts.xml](/en/reference/applications/hosts) to verify all services run with updated config +- Make sure [VESPA\_CONFIGSERVERS](/en/operations/self-managed/files-processes-and-ports#environment-variables) is set and identical on all nodes in hosts.xml +- Use the *cluster controller* status page (below) to track the status of search/storage nodes. +- Check [logs](/en/reference/operations/log-files) +- Use performance graphs, System Activity Report (*sar*) or [status pages](#status-pages) to track load +- Use [query tracing](/en/reference/api/query#trace.level) +- Disk and/or memory might be exhausted and block feeding - recover from [feed block](/en/writing/feed-block) + +## Status pages + +All Vespa services have status pages, for showing health, Vespa version, config, and metrics. Status pages are subject to change at any time - take care when automating. Procedure: + + + +**Find the port:** The status pages runs on ports assigned by Vespa. To find status page ports, use [vespa-model-inspect](/en/reference/operations/self-managed/tools#vespa-model-inspect) to list the services run in the application. + +```bash +$ vespa-model-inspect services +``` + +To find the status page port for a specific node for a specific service, pick the correct service and run: + +```bash +$ vespa-model-inspect service [Options] +``` + + +**Get the status and metrics:** *distributor*, *storagenode*, *searchnode* and *container-clustercontroller* are content services with status pages. These ports are tagged HTTP. The cluster controller have multiple ports tagged HTTP, where the port tagged STATE is the one with the status page. Try connecting to the root at the port, or /state/v1/metrics. The *distributor* and *storagenode* status pages are available at `/`: + +```bash +$ vespa-model-inspect service searchnode + + searchnode @ myhost.mydomain.com : search + search/search/cluster.search/0 + tcp/myhost.mydomain.com:19110 (STATUS ADMIN RTC RPC) + tcp/myhost.mydomain.com:19111 (FS4) + tcp/myhost.mydomain.com:19112 (TEST HACK SRMP) + tcp/myhost.mydomain.com:19113 (ENGINES-PROVIDER RPC) + tcp/myhost.mydomain.com:19114 (HEALTH JSON HTTP) + $ curl http://myhost.mydomain.com:19114/state/v1/metrics + ... + $ vespa-model-inspect service distributor + distributor @ myhost.mydomain.com : content + search/distributor/0 + tcp/myhost.mydomain.com:19116 (MESSAGING) + tcp/myhost.mydomain.com:19117 (STATUS RPC) + tcp/myhost.mydomain.com:19118 (STATE STATUS HTTP) + $ curl http://myhost.mydomain.com:19118/state/v1/metrics + ... + $ curl http://myhost.mydomain.com:19118/ + ... +``` + + +**Use the cluster controller status page**: A status page for the cluster controller is available at the status port at `http://hostname:port/clustercontroller-status/v1/**`. If *clustername* is not specified, the available clusters will be listed. The cluster controller leader status page will show if any nodes are operating with differing cluster state versions. It will also show how many data buckets are pending merging (document set reconciliation) due to either missing or being out of sync. + +`$` [`vespa-model-inspect`](/en/reference/operations/self-managed/tools#vespa-model-inspect) `service container-clustercontroller | grep HTTP` + +With multiple cluster controllers, look at the one with a "/0" suffix in its config ID; it is the preferred leader. + +The cluster state version is listed under the *SSV* table column. Divergence here usually points to host or networking issues. + + + +## Cluster state + +Cluster and node state information is available through the [/cluster/v2 API](/en/reference/api/cluster-v2). This API can also be used to set a *user state* for a node - alternatively use: + + + + + + + +Also see the cluster controller [status page](#status-pages). + +State is persisted in a ZooKeeper cluster, restarting/changing a cluster controller preserves: + +- Last cluster state version number, for new cluster controller handover at restarts +- User states, set by operators - i.e. nodes manually set to down / maintenance + +In case of state data lost, the cluster state is reset - see [cluster controller](/en/content/content-nodes#cluster-controller) for implications. + +## Cluster controller configuration + +It is recommended to run cluster controllers on the same hosts as [config servers](/en/operations/self-managed/configuration-server), as they share a zookeeper cluster for state and deploying three nodes is best practise for both. See the [multinode-HA](https://github.com/vespa-engine/sample-apps/tree/master/examples/operations/multinode-HA) sample app for a working example. + +To configure the cluster controller, use [services.xml](/en/reference/applications/services/content#cluster-controller) and/or add [configuration](https://github.com/vespa-engine/vespa/blob/master/configdefinitions/src/vespa/fleetcontroller.def) under the *services* element - example: + +```bash + + + 5000 + +``` + +A broken content node may end up with processes constantly restarting. It may die during initialization due to accessing corrupt files, or it may die when it starts receiving requests of a given type triggering a node local bug. This is bad for distributor nodes, as these restarts create constant ownership transfer between distributors, causing windows where buckets are unavailable. + +The cluster controller has functionality for detecting such nodes. If a node restarts in a way that is not detected as a controlled shutdown, more than [max\_premature\_crashes](https://github.com/vespa-engine/vespa/blob/master/configdefinitions/src/vespa/fleetcontroller.def), the cluster controller will set the wanted state of this node to be down. + +Detecting a controlled restart is currently a bit tricky. A controlled restart is typically initiated by sending a TERM signal to the process. Not having any other sign, the content layer has to assume that all TERM signals are the cause of controlled shutdowns. Thus, if the process keep being killed by kernel due to using too much memory, this will look like controlled shutdowns to the content layer. + +## Monitor distance to ideal state + +Refer to the [distribution algorithm](/en/content/idealstate). Use distributor [status pages](#status-pages) to inspect state metrics, see [metrics](/en/content/content-nodes#metrics). `idealstate.merge_bucket.pending` is the best metric to track, it is 0 when the cluster is balanced - a non-zero value indicates buckets out of sync. + +## Cluster configuration + +- Running `vespa prepare` will not change served configuration until `vespa activate` is run. `vespa prepare` will warn about all config changes that require restart. +- Refer to [schemas](/en/basics/schemas) for how to add/change/remove these. +- Refer to [elasticity](/en/content/elasticity) for how to add/remove capacity from a Vespa cluster, procedure below. +- See [chained components](/en/applications/chaining) for how to add or remove searchers and document processors. +- Refer to the [sizing examples](/en/operations/self-managed/sizing-examples) for changing from a *flat* to *grouped* content cluster. + +## Add or remove a content node + + + +**Node setup:** Prepare the node by installing software, set up the file systems/directories and set [VESPA\_CONFIGSERVERS](/en/operations/self-managed/files-processes-and-ports#environment-variables). [Start](#vespa-start-stop-restart) the node. + + +**Modify configuration:** Add/remove a [node](/en/reference/applications/services/content#node)\-element in *services.xml* and [hosts.xml](/en/reference/applications/hosts). Refer to [multinode install](/en/operations/self-managed/multinode-systems). Make sure the *distribution-key* is unique. + + +**Deploy**: [Observe metrics](#monitor-distance-to-ideal-state) to track progress as the cluster redistributes documents. Use the [cluster controller](/en/content/content-nodes#cluster-controller) to monitor the state of the cluster. + + +**Tune performance (optional):** Use [maxpendingidealstateoperations](https://github.com/vespa-engine/vespa/blob/master/storage/src/vespa/storage/config/stor-distributormanager.def) to tune concurrency of bucket merge operations from distributor nodes. Likewise, tune [merges](/en/reference/applications/services/content#merges) - concurrent merge operations per content node. The tradeoff is speed of bucket replication vs use of resources, which impacts the applications' regular load. + + +**Finish:** The cluster is done redistributing when `idealstate.merge_bucket.pending` is zero on all distributors. + + + +Do not remove more than *redundancy*\-1 nodes at a time, to avoid data loss. Observe `idealstate.merge_bucket.pending` to know bucket replica status, when zero on all distributor nodes, it is safe to remove more nodes. If [grouped distribution](/en/content/elasticity#grouped-distribution) is used to control bucket replicas, remove all nodes in a group if the redundancy settings ensure replicas in each group. + +To increase bucket redundancy level before taking nodes out, [retire](/en/content/content-nodes) nodes. Again, track `idealstate.merge_bucket.pending` to know when done. Use the [/cluster/v2 API](/en/reference/api/cluster-v2) or [vespa-set-node-state](/en/reference/operations/self-managed/tools#vespa-set-node-state) to set a node to the *retired* state. You can set any number of nodes retired at the same time. The [cluster controller's](/en/content/content-nodes#cluster-controller) status page lists node states. + +An alternative to increasing cluster size is building a new cluster, then migrate documents to it. This is supported using [visiting](/en/writing/visiting). + +To *merge* two content clusters, add nodes to the cluster like above, considering: +- [distribution-keys](/en/reference/applications/services/content#node) must be unique. Modify paths like *$VESPA\_HOME/var/db/vespa/search/mycluster/n3* before adding the node. +- Set [VESPA\_CONFIGSERVERS](/en/operations/self-managed/files-processes-and-ports#environment-variables), then start the node. + +## Topology change + +Read [changing topology first](/en/content/elasticity#changing-topology), and plan the sequence of steps. + +Make sure to not change the `distribution-key` for nodes in *services.xml*. + +It is not required to restart nodes as part of this process + +## Add or remove services on a node + +It is possible to run multiple Vespa services on the same host. If changing the services on a given host, stop Vespa on the given host before running `vespa activate`. This is because the services are dynamically allocated port numbers, depending on what is running on the host. Consider if some of the services changed are used by services on other hosts. In that case, restart services on those hosts too. Procedure: + + + +Edit *services.xml* and *hosts.xml* + + +Stop Vespa on the nodes that have changes + + +Run `vespa prepare` and `vespa activate` + + +Start Vespa on the nodes that have changes + + + +## Troubleshooting + +Also see the [FAQ](/en/learn/faq). + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    **No endpoint**Most problems with the quick start guides are due to Docker out of memory. Make sure at least 6G memory is allocated to Docker:

    {`$ docker info | grep "Total Memory"`}
    {`or`}
    {`$ podman info | grep "memTotal"`}

    OOM symptoms includeINFO:

    {`Problem with Handshake localhost:8080 ssl=false: localhost:8080 failed to respond `}

    The container is named *vespa* in the guides, for a shell do:

    {`$ docker exec -it vespa bash`}
    **Log viewing**Use vespa-logfmt to view the vespa log - example:

    {`$ /opt/vespa/bin/vespa-logfmt -l warning,error`}
    **Json**For json pretty-print, append

    {`| python -m json.tool`}

    to commands that output json - or use jq.
    **Routing**Vespa lets application set up custom document processing / indexing, with different feed endpoints. Refer to indexing for how to configure this in *services.xml*.

    #13193 has a summary of problems and solutions.
    **Tracing**Use tracelevel to dump the routes and hops for a write operation - example:

    {`$ curl -H Content-Type:application/json --data-binary @docs.json \\`}
    {`$ENDPOINT/document/v1/mynamespace/doc/docid/1?tracelevel=4 \\ jq .`}
    {`{`}
    {`"pathId": "/document/v1/mynamespace/doc/docid/1",`}
    {`"id": "id:mynamespace:doc::1",`}
    {`"trace": [`}
    {`{ "message": "[1623413878.905] Sending message (version 7.418.23) from client to ..." },`}
    {`{ "message": "[1623413878.906] Message (type 100004) received at 'default/container.0' ..." },`}
    {`{ "message": "[1623413878.907] Sending message (version 7.418.23) from 'default/container.0' ..." },`}
    {`{ "message": "[1623413878.907] Message (type 100004) received at 'default/container.0' ..." },`}
    {`{ "message": "[1623413878.909] Selecting route" },`}
    {`{ "message": "[1623413878.909] No cluster state cached. Sending to random distributor." }`}
    + +## Clean start mode + +There has been rare occasions were Vespa stored data that was internally inconsistent. For those circumstances it is possible to start the node in a [validate\_and\_sanitize\_docstore](https://github.com/vespa-engine/vespa/blob/master/configdefinitions/src/vespa/proton.def) mode. This will do its best to clean up inconsistent data. However, detecting that this is required is not easy, consult the Vespa Team first. In order for this approach to work, all nodes must be stopped before enabling this feature - this to make sure the data is not redistributed. + +## Content cluster configuration + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    **Availability vs resources**Keeping index structures costs resources. Not all replicas of buckets are necessarily searchable, unless configured using searchable-copies. As Vespa indexes buckets on-demand, the most cost-efficient setting is 1, if one can tolerate temporary coverage loss during node failures.
    **Data retention vs size**When a document is removed, the document data is not immediately purged. Instead, *remove-entries* (tombstones of removed documents) are kept for a configurable amount of time. The default is two weeks, refer to removed-db prune age. This ensures that removed documents stay removed in a distributed system where nodes change state. Entries are removed periodically after expiry. Hence, if a node comes back up after being down for more than two weeks, removed documents are available again, unless the data on the node is wiped first. A larger *prune age* will grow the storage size as this keeps document and tombstones longer.

    **Note:**

    The backend does not store remove-entries for nonexistent documents. This to prevent clients sending wrong document identifiers from filling a cluster with invalid remove-entries. A side effect is that if a problem has caused all replicas of a bucket to be unavailable, documents in this bucket cannot be marked removed until at least one replica is available again. Documents are written in new bucket replicas while the others are down - if these are removed, then older versions of these will not re-emerge, as the most recent change wins.
    **Transition time**See transition-time for tradeoffs for how quickly nodes are set down vs. system stability.
    **Removing unstable nodes**One can configure how many times a node is allowed to crash before it will automatically be removed. The crash count is reset if the node has been up or down continuously for more than the stable state period. If the crash count exceeds max premature crashes, the node will be disabled. Refer to troubleshooting.
    **Minimal amount of nodes required to be available**A cluster is typically sized to handle a given load. A given percentage of the cluster resources are required for normal operations, and the remainder is the available resources that can be used if some of the nodes are no longer usable. If the cluster loses enough nodes, it will be overloaded:

    • Remaining nodes may create disk full situation. This will likely fail a lot of write operations, and if disk is shared with OS, it may also stop the node from functioning.
    • Partition queues will grow to maximum size. As queues are processed in FIFO order, operations are likely to get long latencies.
    • Many operations may time out while being processed, causing the operation to be resent, adding more load to the cluster.
    • When new nodes are added, they cannot serve requests before data is moved to the new nodes from the already overloaded nodes. Moving data puts even more load on the existing nodes, and as moving data is typically not high priority this may never actually happen.

    To configure what the minimal cluster size is, use min-distributor-up-ratio and min-storage-up-ratio.
    \ No newline at end of file diff --git a/en/operations/self-managed/build-install.html b/en/operations/self-managed/build-install.html deleted file mode 100644 index cbc5ebb836..0000000000 --- a/en/operations/self-managed/build-install.html +++ /dev/null @@ -1,144 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Build / install Vespa" -redirect_from: -- /en/build-install-vespa.html ---- - -

    - To develop with Vespa, follow the - guide - to set up a development environment on AlmaLinux 8 using Docker. -

    - Build Vespa Java artifacts with Java >= 17 and Maven >= 3.6.3. - Once built, Vespa Java artifacts are ready to be used and one can build a Vespa application - using the bundle plugin. -

    -
    -$ export MAVEN_OPTS="-Xms128m -Xmx1024m"
    -$ ./bootstrap.sh java && mvn install
    -
    -

    - See vespa.ai releases. -

    - - - -

    Container images

    - - - - - - - - - - - - - - - - - - - -
    ImageDescription
    - docker.io/vespaengine/vespa
    - ghcr.io/vespa-engine/vespa -
    Container image for running Vespa.
    - docker.io/vespaengine/vespa-build-almalinux-8Container image for building Vespa on AlmaLinux 8.
    - docker.io/vespaengine/vespa-dev-almalinux-8Container image for development of Vespa on AlmaLinux 8. Used for incremental building and system testing.
    - - - -

    RPMs

    -

    - Dependency graph: -

    -RPM overview -

    - Installing Vespa on AlmaLinux 8: -

    -
    -$ dnf config-manager \
    -  --add-repo https://raw.githubusercontent.com/vespa-engine/vespa/master/dist/vespa-engine.repo
    -$ dnf config-manager --enable powertools
    -$ dnf install -y epel-release
    -$ dnf install -y vespa
    -
    - -

    -Package repository hosting is graciously provided by Cloudsmith which is a fully hosted, cloud-native -and universal package management solution: -OSS hosting by Cloudsmith -

    - -{% include important.html content="Please note that the retention of released RPMs in the repository is limited to the latest 50 releases. -Use the Docker images (above) for installations of specific versions older than this. -Any problems with released rpm packages will be fixed in subsequent releases, -please report any issues - -troubleshoot using the install example." %} - -

    - Refer to vespa.spec. - Build RPMs for a given Vespa version X.Y.Z: -

    -
    -$ git clone https://github.com/vespa-engine/vespa
    -$ cd vespa
    -$ git checkout vX.Y.Z
    -$ docker run --rm -ti -v $(pwd):/wd:Z -w /wd  \
    -         docker.io/vespaengine/vespa-build-almalinux-8:latest \
    -         make -f .copr/Makefile rpms outdir=/wd
    -$ ls *.rpm | grep -v debug
    -vespa-{{site.variables.vespa_version}}-1.el8.src.rpm
    -vespa-{{site.variables.vespa_version}}-1.el8.x86_64.rpm
    -vespa-ann-benchmark-{{site.variables.vespa_version}}-1.el8.x86_64.rpm
    -vespa-base-{{site.variables.vespa_version}}-1.el8.x86_64.rpm
    -vespa-base-libs-{{site.variables.vespa_version}}-1.el8.x86_64.rpm
    -vespa-clients-{{site.variables.vespa_version}}-1.el8.x86_64.rpm
    -vespa-config-model-fat-{{site.variables.vespa_version}}-1.el8.x86_64.rpm
    -vespa-jars-{{site.variables.vespa_version}}-1.el8.x86_64.rpm
    -vespa-libs-{{site.variables.vespa_version}}.el8.x86_64.rpm
    -vespa-malloc-{{site.variables.vespa_version}}-1.el8.x86_64.rpm
    -vespa-node-admin-{{site.variables.vespa_version}}-1.el8.x86_64.rpm
    -vespa-tools-{{site.variables.vespa_version}}-1.el8.x86_64.rpm
    -
    -

    - Find most utilities in the vespa-x.y.z*.rpm - other RPMs: -

    - - - - - - - - - - - - - - - - - - - -
    RPMDescription
    vespa-toolsTools accessing Vespa endpoints for query or document operations: - -
    vespa-mallocVespa has its own memory allocator, vespa-malloc - - refer to /opt/vespa/etc/vespamalloc.conf
    vespa-clientsvespa-feed-client.jar - see vespa-feed-client
    diff --git a/en/operations/self-managed/build-install.mdx b/en/operations/self-managed/build-install.mdx new file mode 100644 index 0000000000..e3b31f0eb7 --- /dev/null +++ b/en/operations/self-managed/build-install.mdx @@ -0,0 +1,116 @@ +--- +title: "Build / install Vespa" +sidebarTitle: "Build and install" +--- + +To develop with Vespa, follow the [guide](https://github.com/vespa-engine/vespa#building) to set up a development environment on AlmaLinux 8 using Docker. + +Build Vespa Java artifacts with Java >= 17 and Maven >= 3.6.3. Once built, Vespa Java artifacts are ready to be used and one can build a Vespa application using the [bundle plugin](/en/applications/bundles#maven-bundle-plugin). + +```bash +$ export MAVEN_OPTS="-Xms128m -Xmx1024m" +$ ./bootstrap.sh java && mvn install +``` + +See [vespa.ai releases](/en/learn/releases). + +## Container images + + + + + + + + + + + + + + + + + + + + + + +
    ImageDescription
    docker.io/vespaengine/vespa ghcr.io/vespa-engine/vespaContainer image for running Vespa.
    docker.io/vespaengine/vespa-build-almalinux-8Container image for building Vespa on AlmaLinux 8.
    docker.io/vespaengine/vespa-dev-almalinux-8Container image for development of Vespa on AlmaLinux 8. Used for incremental building and system testing.
    + +## RPMs + +Dependency graph: + + +![RPM overview](/assets/img/rpms.svg) + + +Installing Vespa on AlmaLinux 8: + +```bash +$ dnf config-manager \ + --add-repo https://raw.githubusercontent.com/vespa-engine/vespa/master/dist/vespa-engine.repo +$ dnf config-manager --enable powertools +$ dnf install -y epel-release +$ dnf install -y vespa +``` + +Package repository hosting is graciously provided by [Cloudsmith](https://cloudsmith.com) which is a fully hosted, cloud-native and universal package management solution: +[![OSS hosting by Cloudsmith](https://img.shields.io/badge/OSS%20hosting%20by-cloudsmith-blue?logo=cloudsmith&style=flat-square)](https://cloudsmith.com) + + +**Important:** + +Please note that the retention of released RPMs in the repository is limited to the latest 50 releases. Use the Docker images (above) for installations of specific versions older than this. Any problems with released rpm packages will be fixed in subsequent releases, please [report any issues](https://vespa.ai/support/) - troubleshoot using the [install example](/en/operations/self-managed/multinode-systems#aws-ec2-singlenode). + + +Refer to [vespa.spec](https://github.com/vespa-engine/vespa/blob/master/dist/vespa.spec). Build RPMs for a given Vespa version X.Y.Z: + +```bash +$ git clone https://github.com/vespa-engine/vespa +$ cd vespa +$ git checkout vX.Y.Z +$ docker run --rm -ti -v $(pwd):/wd:Z -w /wd \ + docker.io/vespaengine/vespa-build-almalinux-8:latest \ + make -f .copr/Makefile rpms outdir=/wd +$ ls *.rpm | grep -v debug +vespa-8.691.19-1.el8.src.rpm +vespa-8.691.19-1.el8.x86_64.rpm +vespa-ann-benchmark-8.691.19-1.el8.x86_64.rpm +vespa-base-8.691.19-1.el8.x86_64.rpm +vespa-base-libs-8.691.19-1.el8.x86_64.rpm +vespa-clients-8.691.19-1.el8.x86_64.rpm +vespa-config-model-fat-8.691.19-1.el8.x86_64.rpm +vespa-jars-8.691.19-1.el8.x86_64.rpm +vespa-libs-8.691.19.el8.x86_64.rpm +vespa-malloc-8.691.19-1.el8.x86_64.rpm +vespa-node-admin-8.691.19-1.el8.x86_64.rpm +vespa-tools-8.691.19-1.el8.x86_64.rpm +``` + +Find most utilities in the vespa-x.y.z\*.rpm - other RPMs: + + + + + + + + + + + + + + + + + + + + + + +
    RPMDescription
    **vespa-tools**Tools accessing Vespa endpoints for query or document operations:

    vespa-destination
    vespa-fbench
    vespa-feeder
    vespa-get
    vespa-query-profile-dump-tool
    vespa-stat
    vespa-summary-benchmark
    vespa-visit
    vespa-visit-target
    **vespa-malloc**Vespa has its own memory allocator, *vespa-malloc* - refer to */opt/vespa/etc/vespamalloc.conf*
    **vespa-clients***vespa-feed-client.jar* - see vespa-feed-client
    \ No newline at end of file diff --git a/en/operations/self-managed/config-proxy.html b/en/operations/self-managed/config-proxy.html deleted file mode 100644 index 67f252064b..0000000000 --- a/en/operations/self-managed/config-proxy.html +++ /dev/null @@ -1,133 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Configuration proxy" -applies_to: self-managed -redirect_from: -- /en/reference/config-proxy.html -- /en/operations/config-proxy.html -- /en/operations-selfhosted/config-proxy.html ---- - -

    - Read application packages - for an overview of the cloud config system. - The config proxy runs on every Vespa node. - It has a set of config sources, defined in - VESPA_CONFIGSERVERS. -

    - The config proxy will act as a proxy for config clients on the same machine, - so that all clients can ask for config on localhost:19090. - The config source that the config proxy uses is set in - VESPA_CONFIGSERVERS - and consists of one or more config sources - (the addresses of config servers). -

    - The proxy has a memory cache that is used to serve configs if it is possible. - In default mode, the proxy will have an outstanding request to the config server - that will return when the config has changed (a new generation of config). - This means that every time config changes on the config server, - the proxy will get a response, update its cache and respond to all its clients with the changed config. -

    - The config proxy has two modes: -

    - - - - - - - - - - - - - - - -
    ModeDescription
    default - Gets config from server and stores in memory cache. - The config proxy will always be started in default mode. - Serves from cache if possible. Always uses a config source. - If restarted, it will lose all configs that were cached in memory. -
    memorycacheServes config from memory cache only. - Never uses a config source. A restart will lose all cached configs. - Setting the mode to memorycache will make all applications on - the node work as before (given that they have previously been running - and requested config), since the config proxy will serve config - from cache and work without connection to any config server. - Applications on this node will not work if the config proxy stops, - is restarted or crashes. -
    -

    -Use vespa-configproxy-cmd -to inspect cached configs, mode, config sources etc., there are also some commands to change some of the settings. -Run the command as: -

    -
    -$ vespa-configproxy-cmd -m
    -
    -

    to see all possible commands.

    - - - -

    Detaching from config servers

    -
    -$ vespa-configproxy-cmd -m setmode memorycache
    -
    - - - -

    Inspecting config

    -

    - To inspect the configuration for a service, in this example a searchnode (proton) instance, do: -

    -
      -
    1. - Find the active config generation used by the service, - using /state/v1/config - - example for http://localhost:19110/state/v1/config, here the generation is 2: -
      {% highlight json %}
      -{
      -    "config": {
      -        "generation": 2,
      -        "proton": {
      -            "generation": 2
      -        },
      -        "proton.documentdb.music": {
      -            "generation": 2
      -        }
      -    }
      -}
      -{% endhighlight %}
      -
    2. -
    3. - Find the relevant config definition name, config id and config generation using - vespa-configproxy-cmd - e.g.: -
      -$ vespa-configproxy-cmd | grep proton
      -
      -vespa.config.search.core.proton,music/search/cluster.music/0,2,MD5:40087d6195cedb1840721b55eb333735,XXHASH64:43829e79cea8e714
      -
      - vespa.config.search.core.proton is the config definition name for this particular config, - music/search/cluster.music/0 is the config id - used by the proton service instance on this node and 2 is the active config generation. - This means, the service is using the correct config generation as it is matching the /state/v1/config response - (a restart can be required for some config changes). -
    4. -
    5. - Get the generated config using - vespa-get-config - e.g.: -
      -$ vespa-get-config -n vespa.config.search.core.proton -i music/search/cluster.music/0
      -
      -basedir "/opt/vespa/var/db/vespa/search/cluster.music/n0"
      -rpcport 19106
      -httpport 19110
      -...
      -
      - {% include important.html content='Omitting -i will return the - default configuration, - meaning not generated for the active service instance.' %} -
    6. -
    diff --git a/en/operations/self-managed/config-proxy.mdx b/en/operations/self-managed/config-proxy.mdx new file mode 100644 index 0000000000..7ffa85d090 --- /dev/null +++ b/en/operations/self-managed/config-proxy.mdx @@ -0,0 +1,99 @@ +--- +title: "Configuration proxy" +sidebarTitle: "Config Proxy" +--- + +Read [application packages](/en/basics/applications) for an overview of the cloud config system. The *config proxy* runs on every Vespa node. It has a set of config sources, defined in [VESPA\_CONFIGSERVERS](/en/operations/self-managed/files-processes-and-ports#environment-variables). + +The config proxy will act as a proxy for config clients on the same machine, so that all clients can ask for config on *localhost:19090*. The *config source* that the config proxy uses is set in [VESPA\_CONFIGSERVERS](/en/operations/self-managed/files-processes-and-ports#environment-variables) and consists of one or more config sources (the addresses of [config servers](/en/operations/self-managed/configuration-server)). + +The proxy has a memory cache that is used to serve configs if it is possible. In default mode, the proxy will have an outstanding request to the config server that will return when the config has changed (a new generation of config). This means that every time config changes on the config server, the proxy will get a response, update its cache and respond to all its clients with the changed config. + +The config proxy has two modes: + + + + + + + + + + + + + + + + + + +
    ModeDescription
    defaultGets config from server and stores in memory cache. The config proxy will always be started in *default* mode. Serves from cache if possible. Always uses a config source. If restarted, it will lose all configs that were cached in memory.
    memorycacheServes config from memory cache only. Never uses a config source. A restart will lose all cached configs. Setting the mode to *memorycache* will make all applications on the node work as before (given that they have previously been running and requested config), since the config proxy will serve config from cache and work without connection to any config server. Applications on this node will not work if the config proxy stops, is restarted or crashes.
    + +Use [vespa-configproxy-cmd](/en/reference/operations/self-managed/tools#vespa-configproxy-cmd) to inspect cached configs, mode, config sources etc., there are also some commands to change some of the settings. Run the command as: + +```bash +$ vespa-configproxy-cmd -m +``` + +to see all possible commands. + +## Detaching from config servers + +```bash +$ vespa-configproxy-cmd -m setmode memorycache +``` + +## Inspecting config + +To inspect the configuration for a service, in this example a searchnode (proton) instance, do: + + + +Find the active config generation used by the service, using [/state/v1/config](/en/reference/api/state-v1#state-v1-config) - example for *http://localhost:19110/state/v1/config*, here the generation is 2: + +```json +{ + "config": { + "generation": 2, + "proton": { + "generation": 2 + }, + "proton.documentdb.music": { + "generation": 2 + } + } +} +``` + + +Find the relevant *config definition name*, *config id* and *config generation* using [vespa-configproxy-cmd](/en/reference/operations/self-managed/tools#vespa-configproxy-cmd) - e.g.:$ vespa-configproxy-cmd | grep proton + +```bash +$ vespa-configproxy-cmd | grep proton + +vespa.config.search.core.proton,music/search/cluster.music/0,2,MD5:40087d6195cedb1840721b55eb333735,XXHASH64:43829e79cea8e714 +``` + +`vespa.config.search.core.proton` is the *config definition name* for this particular config, `music/search/cluster.music/0` is the *config id* used by the proton service instance on this node and `2` is the active config generation. This means, the service is using the correct config generation as it is matching the /state/v1/config response (a restart can be required for some config changes). + + +Get the generated config using [vespa-get-config](/en/reference/operations/self-managed/tools#vespa-get-config) - e.g.:$ vespa-get-config -n vespa.config.search.core.proton -i music/search/cluster.music/0 + +```bash +$ vespa-get-config -n vespa.config.search.core.proton -i music/search/cluster.music/0 + +basedir "/opt/vespa/var/db/vespa/search/cluster.music/n0" +rpcport 19106 +httpport 19110 +... +``` + + +**Important:** + +Omitting `-i` will return the default configuration, meaning not generated for the active service instance. + + + + \ No newline at end of file diff --git a/en/operations/self-managed/config-sentinel.html b/en/operations/self-managed/config-sentinel.mdx similarity index 52% rename from en/operations/self-managed/config-sentinel.html rename to en/operations/self-managed/config-sentinel.mdx index a8593c972f..5174932209 100644 --- a/en/operations/self-managed/config-sentinel.html +++ b/en/operations/self-managed/config-sentinel.mdx @@ -1,120 +1,75 @@ --- -# Copyright Vespa.ai. All rights reserved. title: "Config sentinel" -applies_to: self-managed -redirect_from: -- /en/config-sentinel.html -- /en/operations/config-sentinel.html -- /en/operations-selfhosted/config-sentinel.html --- -

    - The config sentinel starts and stops services - - and restart failed services unless they are manually stopped. - All nodes in a Vespa system have at least these running processes: -

    - +The config sentinel starts and stops services - and restart failed services unless they are manually stopped. All nodes in a Vespa system have at least these running processes: + +
    - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + +
    ProcessDescription
    config-proxy - Proxies config requests between Vespa applications and the configserver node. - All configuration is cached locally so that this node can maintain its current configuration, - even if the configserver shuts down. -
    config-sentinel - Registers itself with the config-proxy - and subscribes to and enforces node configuration, - meaning the configuration of what services should be run locally, - and with what parameters. -
    vespa-logd - Monitors $VESPA_HOME/logs/vespa/vespa.log, which is used by all other services, - and relays everything to the log-server. -
    metrics-proxy - Provides APIs for metrics access to all nodes and services. - ProcessDescription
    config-proxyProxies config requests between Vespa applications and the configserver node. All configuration is cached locally so that this node can maintain its current configuration, even if the configserver shuts down.
    **config-sentinel**Registers itself with the *config-proxy* and subscribes to and enforces node configuration, meaning the configuration of what services should be run locally, and with what parameters.
    vespa-logdMonitors *$VESPA_HOME/logs/vespa/vespa.log*, which is used by all other services, and relays everything to the log-server.
    metrics-proxyProvides APIs for metrics access to all nodes and services.
    -Vespa node configuration, startup and logs -

    Start sequence:

    -
      -
    1. - config server(s) are started and application config is deployed to them - - see config server operations. -
    2. -
    3. - config-proxy is started. - The environment variables VESPA_CONFIGSERVERS - and VESPA_CONFIGSERVER_RPC_PORT - are used to connect to the config-server(s). - It will retry all config servers in case some are down. -
    4. - config-sentinel is started, - and subscribes to node configuration (i.e. a service list) from config-proxy - using its hostname as the config id. - See Node and network setup for - details about how the hostname is detected and how to override it. - The config for the config-sentinel (the service list) - lists the processes to be started, along with the config id to assign to each, - typically the logical name of that service instance. -
    5. - config-proxy subscribes to node configuration from config-server, - caches it, and returns the result to config-sentinel -
    6. - config-sentinel starts the services given in the node configuration, - with the config id as argument. - See example output below, like id="search/qrservers/qrserver.0". - logd and metrics-proxy are always started, regardless of configuration. - Each service: -
        -
      1. - Subscribes to configuration from config-proxy. -
      2. - config-proxy subscribes to configuration from config-server, - caches it and returns result to the service. -
      3. - The service runs according to its configuration, - logging to $VESPA_HOME/logs/vespa/vespa.log. - The processes instantiate internal components, - each assigned the same or another config id, and instantiating further components. -
      4. -
      - Also see cluster startup for a minimum nodes-up start setting. -
    7. -
    -

    - When new config is deployed to config-servers - they propagate the changed configuration to nodes subscribing to it. - In turn, these nodes reconfigure themselves accordingly. -

    - - - -

    User interface

    -

    - The config sentinel runs an RPC service which can be used to list, - start and stop the services supposed to run on that node. - This can be useful for testing and debugging. - Use vespa-sentinel-cmd - to trigger these actions. - Example output from vespa-sentinel-cmd list: -

    -
    +
    +
    +![Vespa node configuration, startup and logs](/assets/img/config-sentinel.svg)
    +
    +
    +Start sequence:
    +
    +
    +
    +*config server(s)* are started and application config is deployed to them - see [config server operations](/en/operations/self-managed/configuration-server).
    +
    +
    +*config-proxy* is started. The environment variables [VESPA\_CONFIGSERVERS](/en/operations/self-managed/files-processes-and-ports#environment-variables) and [VESPA\_CONFIGSERVER\_RPC\_PORT](/en/operations/self-managed/files-processes-and-ports#environment-variables) are used to connect to the [config-server(s)](/en/operations/self-managed/configuration-server). It will retry all config servers in case some are down.
    +
    +
    +*config-sentinel* is started, and subscribes to node configuration (i.e. a service list) from *config-proxy* using its hostname as the [config id](/en/applications/configapi-dev#config-id). See [Node and network setup](/en/operations/self-managed/node-setup) for details about how the hostname is detected and how to override it. The config for the config-sentinel (the service list) lists the processes to be started, along with the *config id* to assign to each, typically the logical name of that service instance.
    +
    +
    +*config-proxy* subscribes to node configuration from *config-server*, caches it, and returns the result to *config-sentinel*
    +
    +
    +*config-sentinel* starts the services given in the node configuration, with the config id as argument. See example output below, like *id="search/qrservers/qrserver.0"*. *logd* and *metrics-proxy* are always started, regardless of configuration. Each service:
    +	
    +    a. Subscribes to configuration from *config-proxy*.
    +	
    +    b. *config-proxy* subscribes to configuration from *config-server*, caches it and returns result to the service.
    +	
    +    c. The service runs according to its configuration, logging to *`$VESPA_HOME/logs/vespa/vespa.log`*. The processes instantiate internal components, each assigned the same or another config id, and instantiating further components.
    +
    +Also see [cluster startup](#cluster-startup) for a minimum nodes-up start setting.
    +
    +
    +
    +When new config is deployed to *config-servers* they propagate the changed configuration to nodes subscribing to it. In turn, these nodes reconfigure themselves accordingly.
    +
    +## User interface
    +
    +The config sentinel runs an RPC service which can be used to list, start and stop the services supposed to run on that node. This can be useful for testing and debugging. Use [vespa-sentinel-cmd](/en/reference/operations/self-managed/tools#vespa-sentinel-cmd) to trigger these actions. Example output from `vespa-sentinel-cmd list`:
    +
    +```bash
     vespa-sentinel-cmd 'sentinel.ls' OK.
     container state=RUNNING mode=AUTO pid=27993 exitstatus=0 id="default/container.0"
     container-clustercontroller state=RUNNING mode=AUTO pid=27997 exitstatus=0 id="admin/cluster-controllers/0"
    @@ -123,26 +78,15 @@ 

    User interface

    logserver state=RUNNING mode=AUTO pid=27994 exitstatus=0 id="admin/logserver" searchnode state=RUNNING mode=AUTO pid=27995 exitstatus=0 id="search/search/cluster.search/0" slobrok state=RUNNING mode=AUTO pid=28000 exitstatus=0 id="admin/slobrok.0" -
    -

    - To learn more about the processes and services, - see files and processes. - Use - vespa-model-inspect host hostname to list services running on a node. -

    - - - -

    Cluster startup

    -

    - The config sentinel will not start services on a node unless it has connectivity to a minimum of other nodes, - default 50%. - Find an example of this feature in the - - multinode-HA example application. - Example configuration: -

    -
    {% highlight xml %}
    +```
    +
    +To learn more about the processes and services, see [files and processes](/en/operations/self-managed/files-processes-and-ports). Use [vespa-model-inspect host *hostname*](/en/reference/operations/self-managed/tools#vespa-model-inspect) to list services running on a node.
    +
    +## Cluster startup
    +
    +The config sentinel will not start services on a node unless it has connectivity to a minimum of other nodes, default 50%. Find an example of this feature in the [multinode-HA](https://github.com/vespa-engine/sample-apps/tree/master/examples/operations/multinode-HA#start-the-admin-server) example application. Example configuration:
    +
    +```xml
     
         
             
    @@ -150,27 +94,15 @@ 

    Cluster startup

    1
    -{% endhighlight %}
    -

    - Example: minOkPercent 10 means that services will be started only if - more than or equal to 10% of nodes are up. - If there are 11 nodes in the application, - the first node started will not start its services - - when the second node is started, services will be started on both. -

    -

    - maxBadCount is for connectivity checks where the other node is up, - but we still do not have proper two-way connectivity. - Normally, one-way connectivity means network configuration is broken and needs looking into, - so this may be set low (1 or even 0 are the recommended values). If there are some - temporary problems (in the example below non-responding DNS which leads to various - issues at startup) the config sentinel will loop and retry, so the service startup will just - be slightly delayed. -

    -

    - Example log: -

    -
    +```
    +
    +Example: `minOkPercent 10` means that services will be started only if more than or equal to 10% of nodes are up. If there are 11 nodes in the application, the first node started will not start its services - when the second node is started, services will be started on both.
    +
    +`maxBadCount` is for connectivity checks where the other node is up, but we still do not have proper two-way connectivity. Normally, one-way connectivity means network configuration is broken and needs looking into, so this may be set low (1 or even 0 are the recommended values). If there are some temporary problems (in the example below non-responding DNS which leads to various issues at startup) the config sentinel will loop and retry, so the service startup will just be slightly delayed.
    +
    +Example log:
    +
    +```bash
     [2021-06-15 14:33:25] EVENT   : starting/1 name="sbin/vespa-config-sentinel -c hosts/le40808.ostk (pid 867)"
     [2021-06-15 14:33:25] EVENT   : started/1 name="config-sentinel"
     [2021-06-15 14:33:25] CONFIG  : Sentinel got 4 service elements [tenant(footest), application(bartest), instance(default)] for config generation 1001
    @@ -230,4 +162,4 @@ 

    Cluster startup

    [2021-06-15 14:33:31] INFO : Enough connectivity checks OK, proceeding with service startup [2021-06-15 14:33:31] EVENT : starting/1 name="searchnode" ... -
    +``` \ No newline at end of file diff --git a/en/operations/self-managed/configuration-server.html b/en/operations/self-managed/configuration-server.html deleted file mode 100644 index 74b4254ec6..0000000000 --- a/en/operations/self-managed/configuration-server.html +++ /dev/null @@ -1,527 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Configuration Servers" -applies_to: self-managed -redirect_from: -- /en/cloudconfig/configuration-server.html -- /en/operations/configuration-server.html -- /en/operations-selfhosted/configuration-server.html ---- - -

    - Vespa Configuration Servers host the endpoint where application packages are deployed - - and serves generated configuration to all services - - see the overview - and application packages for details. - I.e., one cannot configure Vespa without config servers, and services cannot run without it. -

    -

    - It is useful to understand the Vespa start sequence. - Refer to the sample applications - multinode and - multinode-HA - for practical examples of multi-configserver configuration. -

    -

    - Vespa configuration is set up using one or more configuration servers (config servers). - A config server uses Apache ZooKeeper - as a distributed data storage for the configuration system. - In addition, each node runs a config proxy to cache configuration data - - find an overview at services start. -

    - - - -

    Status and config generation

    -

    - Check the health of a running config server using (replace localhost with hostname): -

    -
    -$ curl http://localhost:19071/state/v1/health
    -
    -

    - Note that the config server is a service is itself, and runs with file-based configuration. - The application packages deployed will not change the config server - - the config server serves this configuration to all other Vespa nodes. - This will hence always be config generation 0: -

    -
    -$ curl http://localhost:19071/state/v1/config
    -
    -

    - Details in - start-configserver. -

    - - - -

    Redundancy

    -

    - The config servers are defined in - VESPA_CONFIGSERVERS, - services.xml and hosts.xml: -

    -
    -$ VESPA_CONFIGSERVERS=myserver0.mydomain.com,myserver1.mydomain.com,myserver2.mydomain.com
    -
    -
    {% highlight xml %}
    -
    -    
    -        
    -            
    -            
    -            
    -        
    -    
    -
    -{% endhighlight %}
    -
    {% highlight xml %}
    -
    -    
    -        admin0
    -    
    -    
    -        admin1
    -    
    -    
    -        admin2
    -    
    -
    -{% endhighlight %}
    -

    - VESPA_CONFIGSERVERS - must be set on all nodes. - This is a comma- or whitespace-separated list with the hostname of all config servers, - like myhost1.mydomain.com,myhost2.mydomain.com,myhost3.mydomain.com. -

    -

    - When there are multiple config servers, the config proxy - will pick a config server randomly (to achieve load balancing between config servers). - The config proxy is fault-tolerant and will switch to another config server (if there is more than one) - if the one it is using becomes unavailable or there is an error in the configuration it receives. -

    -

    - For the system to tolerate n failures, - ZooKeeper by design requires using (2*n)+1 nodes. - Consequently, only an odd numbers of nodes is useful, - so you need minimum 3 nodes to have a fault-tolerant config system. -

    -

    - Even when using just one config server, the application will work if the server goes down - (but deploying application changes will not work). - Since the config proxy runs on every node and caches configs, - it will continue to serve config to the services on that node. - However, restarting a node when config servers are unavailable - means that services on the node will be unable to start since the cache will be destroyed when restarting the config proxy. -

    -

    - Refer to the admin model reference - for more details on services.xml. -

    - - - -

    Start sequence

    -

    - To bootstrap a Vespa application instance, the high-level steps are: -

    -
      -
    • Start config servers
    • -
    • Deploy config
    • -
    • Start Vespa nodes
    • -
    -

    - multinode-HA - is a great guide on how to start a multinode Vespa application instance - try this first. - Detailed steps for config server startup: -

    -
      -
    1. - Set VESPA_CONFIGSERVERS - on all nodes, using fully qualified hostnames and the same value on all nodes, including the config servers. -
    2. -
    3. - Start the config server on the nodes configured in services/hosts.xml. - Make sure the startup is successful by inspecting - /state/v1/health, default on port 19071: -
      -$ curl http://localhost:19071/state/v1/health
      -
      -
      {% highlight json %}
      -{
      -    "time" : 1651147368066,
      -    "status" : {
      -        "code" : "up"
      -    },
      -    "metrics" : {
      -        "snapshot" : {
      -            "from" : 1.651147308063E9,
      -            "to" : 1.651147367996E9
      -        }
      -    }
      -}
      -{% endhighlight %}
      - If there is no response on the health API, two things can have happened: -
        -
      • The config server process did not start - inspect logs using vespa-logfmt, - or check $VESPA_HOME/logs/vespa/vespa.log, normally /opt/vespa/logs/vespa/vespa.log.
      • -
      • The config server process started, and is waiting for Zookeeper quorum:
      • -
      -
      -$ vespa-logfmt -S configserver
      -
      -
      -configserver     Container.com.yahoo.vespa.zookeeper.ZooKeeperRunner	Starting ZooKeeper server with /opt/vespa/var/zookeeper/conf/zookeeper.cfg. Trying to establish ZooKeeper quorum (members: [node0.vespanet, node1.vespanet, node2.vespanet], attempt 1)
      -configserver     Container.com.yahoo.container.handler.threadpool.ContainerThreadpoolImpl	Threadpool 'default-pool': min=12, max=600, queue=0
      -configserver     Container.com.yahoo.vespa.config.server.tenant.TenantRepository	Adding tenant 'default', created 2022-04-28T13:02:24.182Z. Bootstrapping in PT0.175576S
      -configserver     Container.com.yahoo.vespa.config.server.rpc.RpcServer	Rpc server will listen on port 19070
      -configserver     Container.com.yahoo.container.jdisc.state.StateMonitor	Changing health status code from 'initializing' to 'up'
      -configserver     Container.com.yahoo.jdisc.http.server.jetty.Janitor	Creating janitor executor with 2 threads
      -configserver     Container.com.yahoo.jdisc.http.server.jetty.JettyHttpServer	Threadpool size: min=22, max=22
      -configserver     Container.org.eclipse.jetty.server.Server	jetty-9.4.46.v20220331; built: 2022-03-31T16:38:08.030Z; git: bc17a0369a11ecf40bb92c839b9ef0a8ac50ea18; jvm 11.0.14.1+1-
      -configserver     Container.org.eclipse.jetty.server.handler.ContextHandler	Started o.e.j.s.ServletContextHandler@341c0dfc{19071,/,null,AVAILABLE}
      -configserver     Container.org.eclipse.jetty.server.AbstractConnector	Started configserver@3cd6d147{HTTP/1.1, (http/1.1, h2c)}{0.0.0.0:19071}
      -configserver     Container.org.eclipse.jetty.server.Server	Started @21955ms
      -configserver     Container.com.yahoo.container.jdisc.ConfiguredApplication	Switching to the latest deployed set of configurations and components. Application config generation: 0
      -
      - It will hang until quorum is reached, and the second highlighted log line is emitted. - Root causes for missing quorum can be: -
        -
      • - No connectivity between the config servers. - Zookeeper logs the members like (members: [node0.vespanet, node1.vespanet, node2.vespanet], attempt 1). - Verify that the nodes running config server can reach each other on port 2181. -
      • -
      • - No connectivity can be wrong network config. - multinode-HA - uses a docker network, make sure there are no underscores in the hostnames. -
      • -
      -
    4. -
    5. - Once all config servers return up on state/v1/health, - an application package can be deployed. - This means, if deploy fails, it is always a good idea to verify the config server health first - - if config servers are up, and deploy fails, it is most likely an issue with the application package - - if so, refer to application packages. -
    6. -
    7. - A successful deployment logs the following, for the prepare and activate steps: -
      -Container.com.yahoo.vespa.config.server.ApplicationRepository	Session 2 prepared successfully.
      -Container.com.yahoo.vespa.config.server.deploy.Deployment	Session 2 activated successfully using no host provisioner. Config generation 2. File references: [file '9cfc8dc57f415c72']
      -Container.com.yahoo.vespa.config.server.session.SessionRepository	Session activated: 2
      -
      -
    8. -
    9. - Start the Vespa nodes. - Technically, they can be started at any time. - When troubleshooting, it is easier to make sure the config servers are started successfully, - and deployment was successful - before starting any other nodes. - Refer to the Vespa start sequence - and Vespa start / stop / restart. -
    10. -
    -

    - Make sure to look for logs on all config servers when debugging. -

    - - - -

    Scaling up

    -

    - Add a config server node for increased fault tolerance or when replacing a node. - Read up on ZooKeeper configuration before continuing. - Although it is possible to add more than one config server at a time, - doing it one by one is recommended, to keep the ZooKeeper quorum intact. -

    -

    - Due to the ZooKeeper majority vote, use one or three config servers. -

    -
      -
    1. - Install vespa on new config server node. -
    2. - Append the config server node's hostname to VESPA_CONFIGSERVERS on all nodes, then (re)start - all config servers in sequence to update the ZooKeeper config. By appending, the current - config server nodes keep their current ZooKeeper index. Restart the existing config server(s) - first. Config server will log which servers are configured when starting up to vespa log. -
    3. - Update services.xml and hosts.xml with the new set of config servers, - then vespa prepare and vespa activate. -
    4. -
    5. - Restart other nodes one by one to start using the new config servers. - This will let the vespa nodes use the updated set of config servers. -
    6. -
    -

    - The config servers will automatically redistribute the application data to new nodes. -

    - - - -

    Scaling down

    -

    - This is the inverse of scaling up, and the procedure is the same. - Remove config servers from the end of VESPA_CONFIGSERVERS, - and here one can remove two nodes in one go, if going from three to one. -

    - -

    Replacing nodes

    -

    -

  • Make sure to replace only one node at a time.
  • -
  • If you have only one config server you need to first scale up with a new node, then scale down by removing the old node.
  • -
  • If you have 3 or more you can replace one of the old nodes in VESPA_CONFIGSERVERS with the new one instead of adding one, otherwise same procedure as - in Scaling up. Repeat for each node you want to replace.
  • - - - -

    Tools

    -

    - Tools to access config: -

    - - - - -

    ZooKeeper

    -

    - ZooKeeper - handles data consistency across multiple config servers. - The config server Java application runs a ZooKeeper server, - embedded with an RPC frontend that the other nodes use. - ZooKeeper stores data internally in nodes that can have sub-nodes, - similar to a file system. -

    -

    - At vespa prepare, the application's files, - along with global configurations, are stored in ZooKeeper. - The application data is stored under /config/v2/tenants/default/sessions/[sessionid]/userapp. - At vespa activate, - the newest application is activated live - by writing the session id into /config/v2/tenants/default/applications/default:default:default. - It is at that point the other nodes get configured. -

    -

    - Use vespa-zkcli to inspect state, replace with actual session id: -

    -
    -$ vespa-zkcli ls  /config/v2/tenants/default/sessions/sessionid/userapp
    -$ vespa-zkcli get /config/v2/tenants/default/sessions/sessionid/userapp/services.xml
    -
    -

    - The ZooKeeper server logs to $VESPA_HOME/logs/vespa/zookeeper.configserver.0.log (files are rotated with sequence number) -

    - - -

    ZooKeeper configuration

    -

    - The members of the ZooKeeper cluster is generated based on the contents of - VESPA_CONFIGSERVERS. - $VESPA_HOME/var/zookeeper/conf/zookeeper.cfg is written when (re)starting the config server. - Hence, config server(s) must all be restarted when VESPA_CONFIGSERVERS changes. -

    -

    - The order of the nodes is used to create indexes in zookeeper.cfg, do not change node order. -

    - - -

    ZooKeeper recovery

    -

    - If the config server(s) should experience data corruption, - for instance a hardware failure, use the following recovery procedure. - One example of such a scenario is if $VESPA_HOME/logs/vespa/zookeeper.configserver.0.log - says java.io.IOException: Negative seek offset at java.io.RandomAccessFile.seek(Native Method), - which indicates ZooKeeper has not been able to recover after a full disk. - There is no need to restart Vespa on other nodes during the procedure: -

    -
      -
    1. vespa-stop-configserver
    2. -
    3. vespa-configserver-remove-state
    4. -
    5. vespa-start-configserver
    6. -
    7. vespa prepare <application path>
    8. -
    9. vespa activate
    10. -
    -

    - This procedure completely cleans out ZooKeeper's internal data snapshots and deploys from scratch. -

    -

    - Note that by default the cluster controller - that maintains the state of the content cluster will use the shared same ZooKeeper instance, - so the content cluster state is also reset when removing state. - Manually set state will be lost (e.g. a node with user state down). - It is possible to run cluster-controllers in standalone zookeeper mode - - see standalone-zookeeper. -

    - - -

    ZooKeeper barrier timeout

    -

    - If the config servers are heavily loaded, or the applications being deployed are big, - the internals of the server may time out when synchronizing with the other servers during deploy. - To work around, increase the timeout by setting: - - VESPA_CONFIGSERVER_ZOOKEEPER_BARRIER_TIMEOUT - to 600 (seconds) or higher, and restart the config servers. -

    - - -

    Configuration

    -

    - To access config from a node not running the config system - (e.g. doing feeding via the Document API), - use the environment variable - VESPA_CONFIG_SOURCES: -

    -
    -$ export VESPA_CONFIG_SOURCES="myadmin0.mydomain.com:19071,myadmin1.mydomain.com:19071"
    -
    -

    - Alternatively, for Java programs, use the system property configsources - and set it programmatically or on the command line with the -D option to Java. - The syntax for the value is the same as for VESPA_CONFIG_SOURCES. -

    - - -

    System requirements

    -

    - The minimum heap size for the JVM it runs under is 128 Mb and max heap size is 2 GB - (which can be changed with a setting). - It writes a transaction log that is regularly purged of old items, so little disk space is required. - Note that running on a server that has a lot of disk I/O - will adversely affect performance and is not recommended. -

    - - -

    Ports

    -

    - The config server RPC port can be changed by setting - VESPA_CONFIGSERVER_RPC_PORT - on all nodes in the system. -

    -

    - Changing HTTP port requires changing the port in - $VESPA_HOME/conf/configserver-app/services.xml: -

    -
    {% highlight xml %}
    -
    -    
    -
    -{% endhighlight %}
    -

    - When deploying, use the -p option, if port is changed from the default. -

    - - -

    Troubleshooting

    - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    ProblemDescription
    Health checks -

    - Verify that a config server is up and running using - /state/v1/health, - see start sequence. - Status code is up if the server is up and has finished bootstrapping. -

    -

    - Alternatively, use - http://localhost:19071/status.html - which will return response code 200 if server is up and has finished bootstrapping. -

    -

    - Metrics are found at - /state/v1/metrics. - Use vespa-model-inspect - to find host and port number, port is 19071 by default. -

    -
    Consistency -

    - When having more than one config server, consistency between the servers is crucial. - http://localhost:19071/status - can be used to check that settings for config servers are the same for all servers. -

    -

    - vespa-config-status - can be used to check config on nodes. -

    -

    - - http://localhost:19071/application/v2/tenant/default/application/default - displays active config generation and should be the same on all servers, - and the same as in response from running - vespa deploy -

    -
    Bad Node -

    - If running with more than one config server and one of these goes down or has hardware failure, - the cluster will still work and serve config as usual - (clients will switch to use one of the good servers). - It is not necessary to remove a bad server from the configuration. -

    -

    - Deploying applications will take longer, - as vespa deploy - will not be able to complete a deployment on all servers when one of them is down. - If this is troublesome, lower the barrier timeout - - (default value is 120 seconds). -

    -

    - Note also that if you have not configured - cluster controllers explicitly, - these will run on the config server nodes and the operation of these might be affected. - This is another reason for not trying to manually remove a bad node from the config server setup. -

    -
    Stuck filedistribution -

    - The config system distributes binary files (such as jar bundle files) - using file-distribution - - use - vespa-status-filedistribution to see detailed status if it gets stuck. -

    -
    Memory -

    - Insufficient memory on the host / in the container running the config server will cause - startup or deploy / configuration problems - - see Docker containers. -

    -
    ZooKeeper -

    - The following can be caused by a full disk on the config server, or clocks out of sync: -

    -
    -at com.yahoo.vespa.zookeeper.ZooKeeperRunner.startServer(ZooKeeperRunner.java:92)
    -Caused by: java.io.IOException: The accepted epoch, 10 is less than the current epoch, 48
    -
    -

    Users have reported that "Copying the currentEpoch to acceptedEpoch fixed the problem".

    -
    diff --git a/en/operations/self-managed/configuration-server.mdx b/en/operations/self-managed/configuration-server.mdx new file mode 100644 index 0000000000..47a30b3572 --- /dev/null +++ b/en/operations/self-managed/configuration-server.mdx @@ -0,0 +1,298 @@ +--- +title: "Configuration Servers" +--- + +Vespa Configuration Servers host the endpoint where application packages are deployed - and serves generated configuration to all services - see the [overview](/en/learn/overview) and [application packages](/en/basics/applications) for details. I.e., one cannot configure Vespa without config servers, and services cannot run without it. + +It is useful to understand the [Vespa start sequence](/en/operations/self-managed/config-sentinel). Refer to the sample applications [multinode](https://github.com/vespa-engine/sample-apps/tree/master/examples/operations/multinode) and [multinode-HA](https://github.com/vespa-engine/sample-apps/tree/master/examples/operations/multinode-HA) for practical examples of multi-configserver configuration. + +Vespa configuration is set up using one or more configuration servers (config servers). A config server uses [Apache ZooKeeper](https://zookeeper.apache.org/) as a distributed data storage for the configuration system. In addition, each node runs a config proxy to cache configuration data - find an overview at [services start](/en/operations/self-managed/config-sentinel). + +## Status and config generation + +Check the health of a running config server using (replace localhost with hostname): + +```bash +$ curl http://localhost:19071/state/v1/health +``` + +Note that the config server is a service is itself, and runs with file-based configuration. The application packages deployed will not change the config server - the config server serves this configuration to all other Vespa nodes. This will hence always be config generation 0: + +```bash +$ curl http://localhost:19071/state/v1/config +``` + +Details in [start-configserver](https://github.com/vespa-engine/vespa/blob/master/configserver/src/main/sh/start-configserver). + +## Redundancy + +The config servers are defined in [VESPA\_CONFIGSERVERS](/en/operations/self-managed/files-processes-and-ports#environment-variables), [services.xml](/en/reference/applications/services/services) and [hosts.xml](/en/reference/applications/hosts): + + +```bash +$ VESPA_CONFIGSERVERS=myserver0.mydomain.com,myserver1.mydomain.com,myserver2.mydomain.com +``` + +```xml + + + + + + + + + +``` +```xml + + + admin0 + + + admin1 + + + admin2 + + +``` + +[VESPA\_CONFIGSERVERS](/en/operations/self-managed/files-processes-and-ports#environment-variables) must be set on all nodes. This is a comma- or whitespace-separated list with the hostname of all config servers, like *myhost1.mydomain.com,myhost2.mydomain.com,myhost3.mydomain.com*. + +When there are multiple config servers, the [config proxy](/en/operations/self-managed/config-proxy) will pick a config server randomly (to achieve load balancing between config servers). The config proxy is fault-tolerant and will switch to another config server (if there is more than one) if the one it is using becomes unavailable or there is an error in the configuration it receives. + +For the system to tolerate *n* failures, [ZooKeeper](#zookeeper) by design requires using *(2\*n)+1* nodes. Consequently, only an odd numbers of nodes is useful, so you need minimum 3 nodes to have a fault-tolerant config system. + +Even when using just one config server, the application will work if the server goes down (but deploying application changes will not work). Since the *config proxy* runs on every node and caches configs, it will continue to serve config to the services on that node. However, restarting a node when config servers are unavailable means that services on the node will be unable to start since the cache will be destroyed when restarting the config proxy. + +Refer to the [admin model reference](/en/reference/applications/services/admin#configservers) for more details on *services.xml*. + +## Start sequence + +To bootstrap a Vespa application instance, the high-level steps are: + +- Start config servers +- Deploy config +- Start Vespa nodes + +[multinode-HA](https://github.com/vespa-engine/sample-apps/tree/master/examples/operations/multinode-HA) is a great guide on how to start a multinode Vespa application instance - try this first. Detailed steps for config server startup: + + + +Set [VESPA\_CONFIGSERVERS](/en/operations/self-managed/files-processes-and-ports#environment-variables) on all nodes, using fully qualified hostnames and the same value on all nodes, including the config servers. + + +Start the config server on the nodes configured in *services/hosts.xml*. Make sure the startup is successful by inspecting [/state/v1/health](/en/reference/api/state-v1#state-v1-health), default on port 19071: + +```bash +$ curl http://localhost:19071/state/v1/health +``` + +```json +{ + "time" : 1651147368066, + "status" : { + "code" : "up" + }, + "metrics" : { + "snapshot" : { + "from" : 1.651147308063E9, + "to" : 1.651147367996E9 + } + } +} +``` + +If there is no response on the health API, two things can have happened: + - The config server process did not start - inspect logs using `vespa-logfmt`, or check *`$VESPA_HOME/logs/vespa/vespa.log`*, normally */opt/vespa/logs/vespa/vespa.log*. + - The config server process started, and is waiting for [Zookeeper quorum](#zookeeper): + + ```bash + $ vespa-logfmt -S configserver + ``` + + ```bash + configserver Container.com.yahoo.vespa.zookeeper.ZooKeeperRunner Starting ZooKeeper server with /opt/vespa/var/zookeeper/conf/zookeeper.cfg. Trying to establish ZooKeeper quorum (members: [node0.vespanet, node1.vespanet, node2.vespanet], attempt 1) + configserver Container.com.yahoo.container.handler.threadpool.ContainerThreadpoolImpl Threadpool 'default-pool': min=12, max=600, queue=0 + configserver Container.com.yahoo.vespa.config.server.tenant.TenantRepository Adding tenant 'default', created 2022-04-28T13:02:24.182Z. Bootstrapping in PT0.175576S + configserver Container.com.yahoo.vespa.config.server.rpc.RpcServer Rpc server will listen on port 19070 + configserver Container.com.yahoo.container.jdisc.state.StateMonitor Changing health status code from 'initializing' to 'up' + configserver Container.com.yahoo.jdisc.http.server.jetty.Janitor Creating janitor executor with 2 threads + configserver Container.com.yahoo.jdisc.http.server.jetty.JettyHttpServer Threadpool size: min=22, max=22 + configserver Container.org.eclipse.jetty.server.Server jetty-9.4.46.v20220331; built: 2022-03-31T16:38:08.030Z; git: bc17a0369a11ecf40bb92c839b9ef0a8ac50ea18; jvm 11.0.14.1+1- + configserver Container.org.eclipse.jetty.server.handler.ContextHandler Started o.e.j.s.ServletContextHandler@341c0dfc{19071,/,null,AVAILABLE} + configserver Container.org.eclipse.jetty.server.AbstractConnector Started configserver@3cd6d147{HTTP/1.1, (http/1.1, h2c)}{0.0.0.0:19071} + configserver Container.org.eclipse.jetty.server.Server Started @21955ms + configserver Container.com.yahoo.container.jdisc.ConfiguredApplication Switching to the latest deployed set of configurations and components. Application config generation: 0 + ``` + +It will hang until quorum is reached, and the second highlighted log line is emitted. Root causes for missing quorum can be: + - No connectivity between the config servers. Zookeeper logs the members like `(members: [node0.vespanet, node1.vespanet, node2.vespanet], attempt 1)`. Verify that the nodes running config server can reach each other on port 2181. + - No connectivity can be wrong network config. [multinode-HA](https://github.com/vespa-engine/sample-apps/tree/master/examples/operations/multinode-HA) uses a docker network, make sure there are no underscores in the hostnames. + + +Once all config servers return `up` on *state/v1/health*, an application package can be deployed. This means, if deploy fails, it is always a good idea to verify the config server health first - if config servers are up, and deploy fails, it is most likely an issue with the application package - if so, refer to [application packages](/en/basics/applications). + + +A successful deployment logs the following, for the *prepare* and *activate* steps: + +```bash +Container.com.yahoo.vespa.config.server.ApplicationRepository Session 2 prepared successfully. +Container.com.yahoo.vespa.config.server.deploy.Deployment Session 2 activated successfully using no host provisioner. Config generation 2. File references: [file '9cfc8dc57f415c72'] +Container.com.yahoo.vespa.config.server.session.SessionRepository Session activated: 2 +``` + + +Start the Vespa nodes. Technically, they can be started at any time. When troubleshooting, it is easier to make sure the config servers are started successfully, and deployment was successful - before starting any other nodes. Refer to the [Vespa start sequence](/en/operations/self-managed/config-sentinel) and [Vespa start / stop / restart](/en/operations/self-managed/admin-procedures#vespa-start-stop-restart). + + + +Make sure to look for logs on all config servers when debugging. + +## Scaling up + +Add a config server node for increased fault tolerance or when replacing a node. Read up on [ZooKeeper configuration](#zookeeper-configuration) before continuing. Although it is *possible* to add more than one config server at a time, doing it one by one is recommended, to keep the ZooKeeper quorum intact. + +Due to the ZooKeeper majority vote, use one or three config servers. + + + +Install *vespa* on new config server node. + + +Append the config server node's hostname to VESPA\_CONFIGSERVERS on all nodes, then (re)start all config servers in sequence to update the ZooKeeper config. By appending, the current config server nodes keep their current ZooKeeper index. Restart the existing config server(s) first. Config server will log which servers are configured when starting up to vespa log. + + +Update *services.xml* and *hosts.xml* with the new set of config servers, then *vespa prepare* and *vespa activate*. + + +Restart other nodes one by one to start using the new config servers. This will let the vespa nodes use the updated set of config servers. + + + +The config servers will automatically redistribute the application data to new nodes. + +## Scaling down + +This is the inverse of scaling up, and the procedure is the same. Remove config servers from the end of *VESPA\_CONFIGSERVERS*, and here one can remove two nodes in one go, if going from three to one. + +## Replacing nodes + +- Make sure to replace only one node at a time. +- If you have only one config server you need to first scale up with a new node, then scale down by removing the old node. +- If you have 3 or more you can replace one of the old nodes in VESPA\_CONFIGSERVERS with the new one instead of adding one, otherwise same procedure as in [Scaling up](#scaling-up). Repeat for each node you want to replace. + +## Tools + +Tools to access config: + + + + + + + +## ZooKeeper + +[ZooKeeper](https://zookeeper.apache.org/) handles data consistency across multiple config servers. The config server Java application runs a ZooKeeper server, embedded with an RPC frontend that the other nodes use. ZooKeeper stores data internally in *nodes* that can have *sub-nodes*, similar to a file system. + +At [vespa prepare](/en/reference/clients/vespa-cli#vespa-prepare), the application's files, along with global configurations, are stored in ZooKeeper. The application data is stored under */config/v2/tenants/default/sessions/\[sessionid\]/userapp*. At [vespa activate](/en/reference/clients/vespa-cli#vespa-activate), the newest application is activated *live* by writing the session id into */config/v2/tenants/default/applications/default:default:default*. It is at that point the other nodes get configured. + +Use *vespa-zkcli* to inspect state, replace with actual session id: + +```bash +$ vespa-zkcli ls /config/v2/tenants/default/sessions/sessionid/userapp +$ vespa-zkcli get /config/v2/tenants/default/sessions/sessionid/userapp/services.xml +``` + +The ZooKeeper server logs to *`$VESPA_HOME/logs/vespa/zookeeper.configserver.0.log` (files are rotated with sequence number)* + +### ZooKeeper configuration + +The members of the ZooKeeper cluster is generated based on the contents of [VESPA\_CONFIGSERVERS](/en/operations/self-managed/files-processes-and-ports#environment-variables). *`$VESPA_HOME/var/zookeeper/conf/zookeeper.cfg`* is written when (re)starting the config server. Hence, config server(s) must all be restarted when `VESPA_CONFIGSERVERS` changes. + +The order of the nodes is used to create indexes in *zookeeper.cfg*, do not change node order. + +### ZooKeeper recovery + +If the config server(s) should experience data corruption, for instance a hardware failure, use the following recovery procedure. One example of such a scenario is if *`$VESPA_HOME/logs/vespa/zookeeper.configserver.0.log`* says *java.io.IOException: Negative seek offset at java.io.RandomAccessFile.seek(Native Method)*, which indicates ZooKeeper has not been able to recover after a full disk. There is no need to restart Vespa on other nodes during the procedure: + +1. [vespa-stop-configserver](/en/reference/operations/self-managed/tools#vespa-stop-configserver) +2. [vespa-configserver-remove-state](/en/reference/operations/self-managed/tools#vespa-configserver-remove-state) +3. [vespa-start-configserver](/en/reference/operations/self-managed/tools#vespa-start-configserver) +4. [vespa](/en/clients/vespa-cli#deployment) prepare `` +5. [vespa](/en/clients/vespa-cli#deployment) activate + +This procedure completely cleans out ZooKeeper's internal data snapshots and deploys from scratch. + +Note that by default the [cluster controller](/en/content/content-nodes#cluster-controller) that maintains the state of the content cluster will use the shared same ZooKeeper instance, so the content cluster state is also reset when removing state. Manually set state will be lost (e.g. a node with user state *down*). It is possible to run cluster-controllers in standalone zookeeper mode - see [standalone-zookeeper](/en/reference/applications/services/admin#cluster-controllers). + +### ZooKeeper barrier timeout + +If the config servers are heavily loaded, or the applications being deployed are big, the internals of the server may time out when synchronizing with the other servers during deploy. To work around, increase the timeout by setting: [VESPA\_CONFIGSERVER\_ZOOKEEPER\_BARRIER\_TIMEOUT](/en/operations/self-managed/files-processes-and-ports#environment-variables) to 600 (seconds) or higher, and restart the config servers. + +## Configuration + +To access config from a node not running the config system (e.g. doing feeding via the Document API), use the environment variable [VESPA\_CONFIG\_SOURCES](/en/operations/self-managed/files-processes-and-ports#environment-variables): + +```bash +$ export VESPA_CONFIG_SOURCES="myadmin0.mydomain.com:19071,myadmin1.mydomain.com:19071" +``` + +Alternatively, for Java programs, use the system property *configsources* and set it programmatically or on the command line with the *\-D* option to Java. The syntax for the value is the same as for *VESPA\_CONFIG\_SOURCES*. + +### System requirements + +The minimum heap size for the JVM it runs under is 128 Mb and max heap size is 2 GB (which can be changed with a [setting](/en/performance/container-tuning#config-server-and-config-proxy)). It writes a transaction log that is regularly purged of old items, so little disk space is required. Note that running on a server that has a lot of disk I/O will adversely affect performance and is not recommended. + +### Ports + +The config server RPC port can be changed by setting [VESPA\_CONFIGSERVER\_RPC\_PORT](/en/operations/self-managed/files-processes-and-ports#environment-variables) on all nodes in the system. + +Changing HTTP port requires changing the port in *`$VESPA_HOME/conf/configserver-app/services.xml`*: + +```xml + + + +``` + +When deploying, use the *\-p* option, if port is changed from the default. + +## Troubleshooting + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    ProblemDescription
    **Health checks**Verify that a config server is up and running using /state/v1/health, see start sequence. Status code is {`up`} if the server is up and has finished bootstrapping.

    Alternatively, use http://localhost:19071/status.html which will return response code 200 if server is up and has finished bootstrapping.

    Metrics are found at /state/v1/metrics. Use vespa-model-inspect to find host and port number, port is 19071 by default.
    **Consistency**When having more than one config server, consistency between the servers is crucial. http://localhost:19071/status can be used to check that settings for config servers are the same for all servers.

    vespa-config-status can be used to check config on nodes.

    http://localhost:19071/application/v2/tenant/default/application/default displays active config generation and should be the same on all servers, and the same as in response from running vespa deploy
    **Bad Node**If running with more than one config server and one of these goes down or has hardware failure, the cluster will still work and serve config as usual (clients will switch to use one of the good servers). It is not necessary to remove a bad server from the configuration.

    Deploying applications will take longer, as vespa deploy will not be able to complete a deployment on all servers when one of them is down. If this is troublesome, lower the barrier timeout - (default value is 120 seconds).

    Note also that if you have not configured cluster controllers explicitly, these will run on the config server nodes and the operation of these might be affected. This is another reason for not trying to manually remove a bad node from the config server setup.
    **Stuck filedistribution**The config system distributes binary files (such as jar bundle files) using file-distribution - use vespa-status-filedistribution to see detailed status if it gets stuck.
    **Memory**Insufficient memory on the host / in the container running the config server will cause startup or deploy / configuration problems - see Docker containers.
    **ZooKeeper**The following can be caused by a full disk on the config server, or clocks out of sync:

    {`at com.yahoo.vespa.zookeeper.ZooKeeperRunner.startServer(ZooKeeperRunner.java:92)`}
    {`Caused by: java.io.IOException: The accepted epoch, 10 is less than the current epoch, 48`}

    Users have reported that "Copying the currentEpoch to acceptedEpoch fixed the problem".
    \ No newline at end of file diff --git a/en/operations/self-managed/container.html b/en/operations/self-managed/container.html deleted file mode 100644 index 4039d7d93b..0000000000 --- a/en/operations/self-managed/container.html +++ /dev/null @@ -1,146 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Container" -applies_to: self-managed -redirect_from: -- /en/inspecting-java-services.html -- /en/operations/container.html -- /en/operations-selfhosted/container.html ---- - -

    - This is the Container service operational guide. -

    -Vespa Overview -

    - Note that "container" is an overloaded concept in Vespa - - in this guide it refers to service instance nodes in blue. -

    -

    Refer to container metrics.

    - - - - -

    Endpoints

    -

    - Container service(s) hosts the query and feed endpoints - examples: -

    -
      -
    • - - album-recommendation configures _both_ query and feed in the same container cluster (i.e. service): -
      {% highlight xml %}
      -
      -    
      -    
      -    
      -        
      -    
      -
      -{% endhighlight %}
      -
    • -
    • - - multinode-HA configures query and feed in separate container clusters (i.e. services): -
      {% highlight xml %}
      -
      -    
      -    
      -    
      -        
      -        
      -    
      -
      -
      -
      -    
      -    
      -        
      -        
      -    
      -
      -{% endhighlight %}
      -
    • -
    -

    - Observe that <document-api> and <search> - are located in separate clusters in the second example, and endpoints are therefore different. -

    -{% include important.html content='The first thing to validate when troubleshooting query errors -is to make sure that the endpoint is correct, i.e. that query requests hit the correct nodes. -A query will be written to the access log -on one of the nodes in the container cluster' %} - - - -

    Inspecting Vespa Java Services using JConsole

    -

    - Determine the state of each running Java Vespa service using JConsole. - JConsole is distributed along with the Java developer kit. - Start JConsole: -

    -
    -$ jconsole <host>:<port>
    -
    -

    - where the host and port determine which service to attach to. - For security purposes the JConsole tool can not directly attach to Vespa services from external machines. -

    - - - -

    Connecting to a Vespa instance

    -

    - To attach a JConsole to a Vespa service running on another host, - create a tunnel from the JConsole host to the Vespa service host. - This can for example be done by setting up two SSH tunnels as follows: -

    -
    -$ ssh -N -L<port1>:localhost:<port1> <service-host> &
    -$ ssh -N -L<port2>:localhost:<port2> <service-host> &
    -
    -

    - where port1 and port2 are determined by the type of service (see below). - A JConsole can then be attached to the service as follows: -

    -
    -$ jconsole localhost:<port1>
    -
    -

    Port numbers:

    - - - - - - - - - - - - - - - - - - - - -
    ServicePort 1Port 2
    QRS1901519016
    Docproc1912319124
    -

    Updated port information can be found by running:

    -
    -$ vespa-model-inspect service <servicename>
    -
    -

    - where the resulting RMIREGISTRY and JMX lines determine port1 and port2, respectively. -

    - - -

    Examining thread states

    -

    - The state of each container is available in JConsole by pressing the Threads tab - and selecting the thread of interest in the threads list. - Threads of interest includes search, connector, closer, transport and - acceptor (the latter four are used for backend communications). -

    diff --git a/en/operations/self-managed/container.mdx b/en/operations/self-managed/container.mdx new file mode 100644 index 0000000000..58a8edaf13 --- /dev/null +++ b/en/operations/self-managed/container.mdx @@ -0,0 +1,112 @@ +--- +title: "Container" +description: "This is the Container service operational guide." +--- + + +![Vespa Overview](/assets/img/vespa-overview.svg) + + +Note that "container" is an overloaded concept in Vespa - in this guide it refers to service instance nodes in blue. + +Refer to [container metrics](/en/operations/metrics#container-metrics). + +## Endpoints + +Container service(s) hosts the query and feed endpoints - examples: + +- [album-recommendation](https://github.com/vespa-engine/sample-apps/blob/master/album-recommendation/app/services.xml) configures \_both\_ query and feed in the same container cluster (i.e. service): + ```xml + + + + + + + + ``` +- [multinode-HA](https://github.com/vespa-engine/sample-apps/blob/master/examples/operations/multinode-HA/services.xml) configures query and feed in separate container clusters (i.e. services): + ```xml + + + + + + + + + + + + + + + + ``` + +Observe that `` and `` are located in separate clusters in the second example, and endpoints are therefore different. + + +**Important:** + +The first thing to validate when troubleshooting query errors is to make sure that the endpoint is correct, i.e. that query requests hit the correct nodes. A query will be written to the [access log](/en/operations/access-logging) on one of the nodes in the container cluster + + +## Inspecting Vespa Java Services using JConsole + +Determine the state of each running Java Vespa service using JConsole. JConsole is distributed along with the Java developer kit. Start JConsole: + +```bash +$ jconsole : +``` + +where the host and port determine which service to attach to. For security purposes the JConsole tool can not directly attach to Vespa services from external machines. + +### Connecting to a Vespa instance + +To attach a JConsole to a Vespa service running on another host, create a tunnel from the JConsole host to the Vespa service host. This can for example be done by setting up two SSH tunnels as follows: + +```bash +$ ssh -N -L:localhost: & +$ ssh -N -L:localhost: & +``` + +where port1 and port2 are determined by the type of service (see below). A JConsole can then be attached to the service as follows: + +```bash +$ jconsole localhost: +``` + +Port numbers: + + + + + + + + + + + + + + + + + + + + + +
    ServicePort 1Port 2
    QRS1901519016
    Docproc1912319124
    + +Updated port information can be found by running: + +`$` [`vespa-model-inspect`](/en/reference/operations/self-managed/tools#vespa-model-inspect) `service ` + +where the resulting RMIREGISTRY and JMX lines determine port1 and port2, respectively. + +### Examining thread states + +The state of each container is available in JConsole by pressing the Threads tab and selecting the thread of interest in the threads list. Threads of interest includes *search*, *connector*, *closer*, *transport* and *acceptor* (the latter four are used for backend communications). \ No newline at end of file diff --git a/en/operations/self-managed/content-node-recovery.html b/en/operations/self-managed/content-node-recovery.html deleted file mode 100644 index 127d46cd3d..0000000000 --- a/en/operations/self-managed/content-node-recovery.html +++ /dev/null @@ -1,73 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Content node recovery" -applies_to: self-managed -redirect_from: -- /en/operations/content-node-recovery.html -- /en/operations-selfhosted/content-node-recovery.html ---- - -

    - In exceptional cases, one or more content nodes may end up with corrupted data causing it to fail to restart. - Possible reasons are -

    -
      -
    • - the application configuring a higher memory or disk limit - such that the node is allowed to accept more data than it can manage, -
    • -
    • hardware failure, or
    • -
    • a bug in Vespa.
    • -
    -

    - Normally a corrupted node can just be wiped of all data or removed from the cluster, but - when this happens simultaneously to multiple nodes, or redundancy 1 is used, it may be necessary to recover - the node(s) to avoid data loss. This documents explains the procedure. -

    - -

    Recovery steps

    - -

    On each of the nodes needing recovery:

    - -
      -
    1. Stop services on the node if running.
    2. -
    3. Repair the node: -
        -
      • - If the node cannot start due to needing more memory than available: - Increase the memory available to the node, or if not possible stop all non-essential processes on the node - using vespa-sentinel-cmd list - and vespa-sentinel-cmd stop [name], - and (if necessary) start only the content node process using vespa-sentinel-cmd start searchnode. - When the node is successfully started, issue delete operations or increase the cluster size to reduce the - amount of data on the node if necessary. -
      • -
      • - If the node cannot start due to needing more disk than available: - Increase the disk available to the node, or if not possible delete non-essential data such as logs and cached packages. - When the node is successfully started, issue delete operations or increase the cluster size to reduce the - amount of data on the node if necessary. -
      • -
      • - If the node cannot start for any other reason, repair the data manually as needed. - This procedure will depend on the specific nature of the data corruption. -
      • -
      -
    4. - Start services on the node. -
    5. -
    6. - Verify that the node is fully up before doing the next node - - metrics/interfaces to be used to evaluate if the next node can be stopped: -
        -
      • - Check if a node is up using - /state/v1/health. -
      • -
      • - Check the vds.idealstate.merge_bucket.pending.average metric on content nodes. - When 0, all buckets are in sync - see example. -
      • -
      -
    7. -
    diff --git a/en/operations/self-managed/content-node-recovery.mdx b/en/operations/self-managed/content-node-recovery.mdx new file mode 100644 index 0000000000..cad08f9f1d --- /dev/null +++ b/en/operations/self-managed/content-node-recovery.mdx @@ -0,0 +1,35 @@ +--- +title: "Content node recovery" +--- + +In exceptional cases, one or more content nodes may end up with corrupted data causing it to fail to restart. Possible reasons are + +- the application configuring a higher memory or disk limit such that the node is allowed to accept more data than it can manage, +- hardware failure, or +- a bug in Vespa. + +Normally a corrupted node can just be wiped of all data or removed from the cluster, but when this happens simultaneously to multiple nodes, or redundancy 1 is used, it may be necessary to recover the node(s) to avoid data loss. This documents explains the procedure. + +## Recovery steps + +On each of the nodes needing recovery: + + + +[Stop services](/en/operations/self-managed/admin-procedures#vespa-start-%2F-stop-%2F-restart) on the node if running. + + +Repair the node: + - If the node cannot start due to needing more memory than available: Increase the memory available to the node, or if not possible stop all non-essential processes on the node using [`vespa-sentinel-cmd`](/en/reference/operations/self-managed/tools#vespa-sentinel-cmd) list and `vespa-sentinel-cmd stop [name]`, and (if necessary) start only the content node process using `vespa-sentinel-cmd start searchnode`. When the node is successfully started, issue delete operations or increase the cluster size to reduce the amount of data on the node if necessary. + - If the node cannot start due to needing more disk than available: Increase the disk available to the node, or if not possible delete non-essential data such as logs and cached packages. When the node is successfully started, issue delete operations or increase the cluster size to reduce the amount of data on the node if necessary. + - If the node cannot start for any other reason, repair the data manually as needed. This procedure will depend on the specific nature of the data corruption. + + +[Start services](/en/operations/self-managed/admin-procedures#vespa-start-%2F-stop-%2F-restart) on the node. + + +Verify that the node is fully up before doing the next node - metrics/interfaces to be used to evaluate if the next node can be stopped: + - Check if a node is up using [/state/v1/health](/en/reference/api/state-v1#state-v1-health). + - Check the `vds.idealstate.merge_bucket.pending.average` metric on content nodes. When 0, all buckets are in sync - see [example](/en/operations/metrics). + + \ No newline at end of file diff --git a/en/operations/self-managed/cpu-support.html b/en/operations/self-managed/cpu-support.html deleted file mode 100644 index 8e2489d5ea..0000000000 --- a/en/operations/self-managed/cpu-support.html +++ /dev/null @@ -1,38 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "CPU Support" -applies_to: self-managed -redirect_from: -- /en/cpu-support.html -- /en/operations-selfhosted/cpu-support.html ---- - -

    -For maximum performance, the current version of Vespa for x86_64 is compiled only for Haswell (2013) or later CPUs. -If trying to run on an older CPU, you will likely see error messages like the following: -

    - -
    -Problem running program /opt/vespa/bin/vespa-runserver => died with signal: illegal instruction (you probably have an older CPU than required)
    -
    - -

    or in older versions of Vespa, something like

    - -
    -/usr/local/bin/start-container.sh: line 67: 10 Illegal instruction /opt/vespa/bin/vespa-start-configserver
    -
    - -

    -If you would like to run Vespa on an older CPU, we provide a generic x86 container image. -This image is slower, receives less testing than the regular image, and is less frequently updated. -

    - -

    To start a Vespa Docker container using this image:

    -
    - -
    -$ docker run --detach --name vespa --hostname vespa-container \
    -  --publish 8080:8080 --publish 19071:19071 \
    -  vespaengine/vespa-generic-intel-x86_64
    -
    -
    diff --git a/en/operations/self-managed/cpu-support.mdx b/en/operations/self-managed/cpu-support.mdx new file mode 100644 index 0000000000..cf99ef3d9c --- /dev/null +++ b/en/operations/self-managed/cpu-support.mdx @@ -0,0 +1,25 @@ +--- +title: "CPU Support" +--- + +For maximum performance, the current version of Vespa for x86\_64 is compiled only for [Haswell (2013)](https://en.wikipedia.org/wiki/Haswell_\(microarchitecture\)) or later CPUs. If trying to run on an older CPU, you will likely see error messages like the following: + +```bash +Problem running program /opt/vespa/bin/vespa-runserver => died with signal: illegal instruction (you probably have an older CPU than required) +``` + +or in older versions of Vespa, something like + +```bash +/usr/local/bin/start-container.sh: line 67: 10 Illegal instruction /opt/vespa/bin/vespa-start-configserver +``` + +If you would like to run Vespa on an older CPU, we provide a [generic x86 container image](https://hub.docker.com/r/vespaengine/vespa-generic-intel-x86_64/). This image is slower, receives less testing than the regular image, and is less frequently updated. + +**To start a Vespa Docker container using this image:** + +```bash +$ docker run --detach --name vespa --hostname vespa-container \ + --publish 8080:8080 --publish 19071:19071 \ + vespaengine/vespa-generic-intel-x86_64 +``` \ No newline at end of file diff --git a/en/operations/self-managed/docker-containers.html b/en/operations/self-managed/docker-containers.html deleted file mode 100644 index 95a27bbb87..0000000000 --- a/en/operations/self-managed/docker-containers.html +++ /dev/null @@ -1,365 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Docker containers" -applies_to: self-managed -redirect_from: -- /en/docker-containers-in-production.html -- /en/operations/docker-containers.html -- /en/operations-selfhosted/docker-containers.html ---- - -

    - This document describes tuning and adaptions for running Vespa Docker containers, - for developer use on laptop, and in production. -

    - - - - -

    Mounting persistent volumes

    -

    - The quick start - and AWS ECS multinode guides - show how to run Vespa in Docker containers. - In these examples, all the data is stored inside the container - the data is lost if the container is deleted. - When running Vespa inside Docker containers in production, - volume mappings to the parent host should be added to persist data and logs. -

    -
      -
    • /opt/vespa/var
    • -
    • /opt/vespa/logs
    • -
    -
    -$ mkdir -p /tmp/vespa/var;  export VESPA_VAR_STORAGE=/tmp/vespa/var
    -$ mkdir -p /tmp/vespa/logs; export VESPA_LOG_STORAGE=/tmp/vespa/logs
    -$ docker run --detach --name vespa --hostname vespa-container \
    -  --volume $VESPA_VAR_STORAGE:/opt/vespa/var \
    -  --volume $VESPA_LOG_STORAGE:/opt/vespa/logs \
    -  --publish 8080:8080 \
    -  vespaengine/vespa
    -
    - - - -

    Start Vespa container with Vespa user

    -

    - You can start the container directly as the vespa user. - The vespa user and group within the container - are configured with user id 1000 and group id 1000. - The vespa user and group must be the owner of the /opt/vespa/var - and /opt/vespa/logs volumes that are mounted in the container for Vespa to start. - This is required for Vespa to create the required directories and files within those directories. -

    -

    - The start script will check that the correct owner uid and gid are set - and fail if the wrong user or group is set as the owner. -

    -

    - When using an isolated user namespace for the Vespa container, you must set the uid and gid of the - directories on the host to the subordinate uid and gid, depending on your mapping. - See the Docker documentation for more details. -

    -
    -$ mkdir -p /tmp/vespa/var;  export VESPA_VAR_STORAGE=/tmp/vespa/var
    -$ mkdir -p /tmp/vespa/logs; export VESPA_LOG_STORAGE=/tmp/vespa/logs
    -$ sudo chown -R 1000:1000 $VESPA_VAR_STORAGE $VESPA_LOG_STORAGE
    -$ docker run --detach --name vespa --user vespa:vespa --hostname vespa-container \
    -  --volume $VESPA_VAR_STORAGE:/opt/vespa/var \
    -  --volume $VESPA_LOG_STORAGE:/opt/vespa/logs \
    -  --publish 8080:8080 \
    -  vespaengine/vespa
    -
    - - - -

    System limits

    -

    - When Vespa starts inside Docker containers, the startup scripts will set - system limits. - Make sure that the environment starting the Docker engine is set up in such a way - that these limits can be set inside the containers. -

    -

    - For a CentOS/RHEL base host, Docker is usually started by - systemd. - In this case, LimitNOFILE, LimitNPROC and LimitCORE - should be set to meet the minimum requirements in - system limits. -

    -

    - In general, when using Docker or Podman to run Vespa, the --ulimit option should be used - to set limits according to system limits. - The --pids-limit should be set to unlimited (-1 for Docker and 0 for Podman). -

    - - - -

    Transparent Huge Pages

    -

    - Vespa performance improves significantly by enabling - Transparent Huge Pages (THP), - especially for memory-intensive applications with large dense tensors with concurrent query and write workloads. -

    -

    - One application improved query p99 latency from 950 ms to 150 ms during concurrent query and write by enabling THP. - Using THP is even more important when running in virtualized environments like AWS and GCP due to nested page tables. -

    -

    - When running Vespa using the container image, THP settings must be set on the base host OS (Linux). - The recommended settings are: -

    -
    -$ echo 1 >      /sys/kernel/mm/transparent_hugepage/khugepaged/defrag
    -$ echo always > /sys/kernel/mm/transparent_hugepage/enabled
    -$ echo never >  /sys/kernel/mm/transparent_hugepage/defrag
    -
    -

    - To verify that the setting is active, one should see that AnonHugePages is non-zero, - In this case, 75 GB has been allocated using AnonHugePages. -

    -
    -$ cat /proc/meminfo |grep AnonHuge
    -
    -  AnonHugePages:  75986944 kB
    -
    -

    - Note that the Vespa container needs to be restarted after modifying the base host OS settings - to make the changes effective. - Vespa uses MADV_HUGEPAGE for memory allocations done by the - content node process (proton). -

    - - - -

    Controlling which services to start

    -

    - The Docker image vespaengine/vespa's - start script - takes a parameter that controls which services are started inside the container. -

    -

    Starting a configserver container:

    -
    -$ docker run <other arguments> \
    -  --env VESPA_CONFIGSERVERS=<comma separated list of config servers> \
    -  vespaengine/vespa configserver
    -
    -

    Starting a services container (configserver will not be started):

    -
    -$ docker run <other arguments> \
    -  --env VESPA_CONFIGSERVERS=<comma separated list of config servers> \
    -  vespaengine/vespa services
    -
    -

    Starting a container with both configserver and services:

    -
    -$ docker run <other arguments> \
    -  --env VESPA_CONFIGSERVERS=<comma separated list of config servers> \
    -  vespaengine/vespa configserver,services
    -
    -

    - This is required in the case where the configserver container should run other services - like an adminserver or logserver (see services.html) -

    -

    - If the VESPA_CONFIGSERVERS - environment variable is not specified, it will be set to the container hostname, - also see node setup. -

    -

    - Use the multinode-HA - sample application as a blueprint for how to set up config servers and services. -

    - - - -

    Graceful stop

    -

    - Stopping a running vespaengine/vespa container triggers a graceful shutdown, - which saves time when starting the container again - (i.e., data structures are flushed). - If the container is shut down forcefully, - the content nodes might need to restore the state from the transaction log, which might be time-consuming. - There is no chance of data loss or data corruption as the data is always written and synced to persistent storage. -

    -

    - The default timeout for the Docker daemon to wait for the shutdown - might be too low for larger number of documents per node. - Below stop will wait at least 120 seconds before terminating the running container forcefully, - if the stop is successfully performed before the timeout has passed, the command takes less than the timeout: -

    -
    -$ docker stop name -t 120
    -
    -

    - It is also possible to configure the default Docker daemon timeout, - see --shutdown-timeout. -

    -

    A clean content node shutdown looks like:

    -
    -[2025-05-02 10:07:52.052] EVENT   searchnode       proton.node.server   stopping/1 name="storagenode" why="Stopped"
    -[2025-05-02 10:07:52.056] EVENT   searchnode       proton       stopping/1 name="servicelayer" why="clean shutdown"
    -[2025-05-02 10:07:52.056] INFO    searchnode       proton.proton.server.rtchooks        shutting down monitoring interface
    -[2025-05-02 10:07:52.058] INFO    searchnode       proton.searchlib.docstore.logdatastore      Flushing. Disk bloat is now at 0 of 8832 at 0.00 percent
    -[2025-05-02 10:07:52.059] INFO    searchnode       proton.searchlib.docstore.logdatastore      Flushing. Disk bloat is now at 0 of 8832 at 0.00 percent
    -[2025-05-02 10:07:52.060] INFO    searchnode       proton.searchlib.docstore.logdatastore      Flushing. Disk bloat is now at 0 of 8840 at 0.00 percent
    -[2025-05-02 10:07:52.066] INFO    searchnode       proton.transactionlog.server Stopping TLS
    -[2025-05-02 10:07:52.066] INFO    searchnode       proton.transactionlog.server TLS Stopped
    -[2025-05-02 10:07:52.071] EVENT   searchnode       proton       stopping/1 name="proton" why="clean shutdown"
    -[2025-05-02 10:07:52.078] EVENT   config-sentinel  sentinel.sentinel.service    stopped/1 name="searchnode" pid=354 exitcode=0
    -
    - - - -

    Memory

    -

    - The sample applications - and local application deployment guide - indicates the minimum memory requirements for the Docker containers. -

    -{% include note.html content="Too little memory is a very common problem when testing Vespa in Docker containers. -Use the below to troubleshoot before making a support request, and also see the FAQ."%} -

    - As a rule of thumb, a single-node Vespa application requires a minimum of 4 GB for the Docker container. - Using docker stats can be useful to track memory usage: -

    -
    -$ docker stats
    -
    -CONTAINER ID   NAME      CPU %     MEM USAGE / LIMIT    MEM %     NET I/O           BLOCK I/O        PIDS
    -589bf5801b22   node0     213.25%   697.3MiB / 3.84GiB   17.73%    14.2kB / 11.5kB   617MB / 976MB    253
    -e108dde84679   node1     213.52%   492.7MiB / 3.84GiB   12.53%    15.7kB / 12.7kB   74.3MB / 924MB   252
    -be43aacd0bbb   node2     191.22%   497.8MiB / 3.84GiB   12.66%    19.6kB / 21.6kB   64MB / 949MB     261
    -
    -

    - It is not necessarily easy to verify that Vespa has started all services successfully. - Symptoms of errors due to insufficient memory vary, depending on where it fails. - Example: Inspect restart logs in a container named vespa, - running the quickstart with only 2G: -

    -
    -$ docker exec -it vespa sh -c "/opt/vespa/bin/vespa-logfmt -S config-sentinel -c sentinel.sentinel.service"
    -
    -INFO    : config-sentinel  sentinel.sentinel.service	container: incremented restart penalty to 2.000 seconds
    -INFO    : config-sentinel  sentinel.sentinel.service	container: incremented restart penalty to 6.000 seconds
    -INFO    : config-sentinel  sentinel.sentinel.service	container: incremented restart penalty to 14.000 seconds
    -INFO    : config-sentinel  sentinel.sentinel.service	container: incremented restart penalty to 30.000 seconds
    -INFO    : config-sentinel  sentinel.sentinel.service	container: will delay start by 25.173 seconds
    -INFO    : config-sentinel  sentinel.sentinel.service	container: incremented restart penalty to 62.000 seconds
    -INFO    : config-sentinel  sentinel.sentinel.service	container: incremented restart penalty to 126.000 seconds
    -INFO    : config-sentinel  sentinel.sentinel.service	container: will delay start by 119.515 seconds
    -INFO    : config-sentinel  sentinel.sentinel.service	container: incremented restart penalty to 254.000 seconds
    -INFO    : config-sentinel  sentinel.sentinel.service	container: incremented restart penalty to 510.000 seconds
    -INFO    : config-sentinel  sentinel.sentinel.service	container: will delay start by 501.026 seconds
    -INFO    : config-sentinel  sentinel.sentinel.service	container: incremented restart penalty to 1022.000 seconds
    -INFO    : config-sentinel  sentinel.sentinel.service	container: incremented restart penalty to 1800.000 seconds
    -INFO    : config-sentinel  sentinel.sentinel.service	container: will delay start by 1793.142 seconds
    -
    -

    - Observe that the container service restarts in a loop, with increasing pause. -

    -

    - A common problem is config servers - not starting or running properly due to a lack of memory. - This manifests itself as nothing listening on 19071, or deployment failures. -

    -

    - Some guides/sample applications have specific configurations to minimize resource usage. - Example from multinode-HA: -

    -
    -$ docker run --detach --name node0 --hostname node0.vespanet \
    -    -e VESPA_CONFIGSERVERS=node0.vespanet,node1.vespanet,node2.vespanet \
    -    -e VESPA_CONFIGSERVER_JVMARGS="-Xms32M -Xmx128M"  \
    -    -e VESPA_CONFIGPROXY_JVMARGS="-Xms32M -Xmx32M" \
    -    --network vespanet \
    -    --publish 19071:19071 --publish 19100:19100 --publish 19050:19050 --publish 20092:19092 \
    -    vespaengine/vespa
    -
    -

    - Here - VESPA_CONFIGSERVER_JVMARGS - and - VESPA_CONFIGPROXY_JVMARGS - are tweaked to the minimum for a functional test only. -

    -{% include important.html content="For production use, -do not reduce memory settings in VESPA_CONFIGSERVER_JVMARGS and VESPA_CONFIGPROXY_JVMARGS -unless you know what you are doing - -the Vespa defaults are set for regular production use, and rarely need changing."%} -

    - Container memory setting are done in services.xml, example from - - multinode-HA: -

    -
    -<container id="query" version="1.0">
    -    <nodes>
    -        <jvm options="-Xms32M -Xmx128M"/>
    -        <node hostalias="node6" />
    -        <node hostalias="node7" />
    -
    -

    Make sure that the settings match the Docker container Vespa is running in.

    -

    Also see node memory settings for more settings.

    - - - -

    Network

    -

    - Vespa processes communicate over both fixed and ephemeral ports - - in general, all ports must be accessible. - See example ephemeral use. -

    -

    - Find an example application using a Docker network in - multinode-HA. -

    - - - -

    Resource usage

    -

    - Note that CPU usage will not be zero even if there are zero documents and zero queries. - Starting the vespaengine/vespa container image means starting the - configuration server - and the configuration sentinel. - When deploying an application, the sentinel starts the configured service processes, - and they all listen to work to do, changes in the config, and so forth. -

    -

    Therefore, an "idle" container instance consumes CPU and memory.

    - - - -

    Troubleshooting

    -

    - The Vespa documentation examples use docker. - The Vespa Team has good experience with using podman, too, - in the examples just change from docker to podman. - We recommend using Podman v5, - see the release notes. - emulating-docker-cli-with-podman - is a useful resource. -

    -

    - Many startup failures are caused by a failed Vespa Container start due to configuration or download errors. - Use docker logs vespa to show the log (this example assumes a Docker container named vespa, - use docker ps to list containers). -

    - - -

    Docker image

    -

    - Make sure to use a recent Vespa release (check releases) - and validate the downloaded image: -

    -
    -$ docker images
    -REPOSITORY                   TAG         IMAGE ID      CREATED       SIZE
    -docker.io/vespaengine/vespa  latest      8cfb0da22c01  35 hours ago  1.2 GB
    -
    - - -

    Model download failures

    -

    - If the application package depends on downloaded models, - look for RuntimeException: Not able to create config builder for payload - - details. -

    diff --git a/en/operations/self-managed/docker-containers.mdx b/en/operations/self-managed/docker-containers.mdx new file mode 100644 index 0000000000..0cb54ba470 --- /dev/null +++ b/en/operations/self-managed/docker-containers.mdx @@ -0,0 +1,246 @@ +--- +title: "Docker containers" +--- + +This document describes tuning and adaptions for running Vespa Docker containers, for developer use on laptop, and in production. + +## Mounting persistent volumes + +The [quick start](/en/basics/deploy-an-application-local) and [AWS ECS multinode](/en/operations/self-managed/multinode-systems#aws-ecs) guides show how to run Vespa in Docker containers. In these examples, all the data is stored inside the container - the data is lost if the container is deleted. When running Vespa inside Docker containers in production, volume mappings to the parent host should be added to persist data and logs. + +- /opt/vespa/var +- /opt/vespa/logs + +```bash +$ mkdir -p /tmp/vespa/var; export VESPA_VAR_STORAGE=/tmp/vespa/var +$ mkdir -p /tmp/vespa/logs; export VESPA_LOG_STORAGE=/tmp/vespa/logs +$ docker run --detach --name vespa --hostname vespa-container \ + --volume $VESPA_VAR_STORAGE:/opt/vespa/var \ + --volume $VESPA_LOG_STORAGE:/opt/vespa/logs \ + --publish 8080:8080 \ + vespaengine/vespa +``` + +## Start Vespa container with Vespa user + +You can start the container directly as the *vespa* user. The *vespa* user and group within the container are configured with user id *1000* and group id *1000*. The vespa user and group must be the owner of the */opt/vespa/var* and */opt/vespa/logs* volumes that are mounted in the container for Vespa to start. This is required for Vespa to create the required directories and files within those directories. + +The start script will check that the correct owner uid and gid are set and fail if the wrong user or group is set as the owner. + +When using an isolated user namespace for the Vespa container, you must set the uid and gid of the directories on the host to the subordinate uid and gid, depending on your mapping. See the [Docker documentation](https://docs.docker.com/engine/security/userns-remap/) for more details. + +```bash +$ mkdir -p /tmp/vespa/var; export VESPA_VAR_STORAGE=/tmp/vespa/var +$ mkdir -p /tmp/vespa/logs; export VESPA_LOG_STORAGE=/tmp/vespa/logs +$ sudo chown -R 1000:1000 $VESPA_VAR_STORAGE $VESPA_LOG_STORAGE +$ docker run --detach --name vespa --user vespa:vespa --hostname vespa-container \ + --volume $VESPA_VAR_STORAGE:/opt/vespa/var \ + --volume $VESPA_LOG_STORAGE:/opt/vespa/logs \ + --publish 8080:8080 \ + vespaengine/vespa +``` + +## System limits + +When Vespa starts inside Docker containers, the startup scripts will set [system limits](/en/operations/self-managed/files-processes-and-ports#vespa-system-limits). Make sure that the environment starting the Docker engine is set up in such a way that these limits can be set inside the containers. + +For a CentOS/RHEL base host, Docker is usually started by [systemd](https://www.freedesktop.org/software/systemd/man/systemd.exec.html). In this case, `LimitNOFILE`, `LimitNPROC` and `LimitCORE` should be set to meet the minimum requirements in [system limits](/en/operations/self-managed/files-processes-and-ports#vespa-system-limits). + +In general, when using Docker or Podman to run Vespa, the `--ulimit` option should be used to set limits according to [system limits](/en/operations/self-managed/files-processes-and-ports#vespa-system-limits). The `--pids-limit` should be set to unlimited (`-1` for Docker and `0` for Podman). + +## Transparent Huge Pages + +Vespa performance improves significantly by enabling [Transparent Huge Pages (THP)](https://www.kernel.org/doc/html/latest/admin-guide/mm/transhuge.html), especially for memory-intensive applications with large dense tensors with concurrent query and write workloads. + +One application improved query p99 latency from 950 ms to 150 ms during concurrent query and write by enabling THP. Using THP is even more important when running in virtualized environments like AWS and GCP due to nested page tables. + +When running Vespa using the container image, *THP* settings must be set on the base host OS (Linux). The recommended settings are: + +```bash +$ echo 1 > /sys/kernel/mm/transparent_hugepage/khugepaged/defrag +$ echo always > /sys/kernel/mm/transparent_hugepage/enabled +$ echo never > /sys/kernel/mm/transparent_hugepage/defrag +``` + +To verify that the setting is active, one should see that *AnonHugePages* is non-zero, In this case, 75 GB has been allocated using AnonHugePages. + +```bash +$ cat /proc/meminfo |grep AnonHuge + + AnonHugePages: 75986944 kB +``` + +Note that the Vespa container needs to be restarted after modifying the base host OS settings to make the changes effective. Vespa uses `MADV_HUGEPAGE` for memory allocations done by the [content node process (proton)](/en/content/proton). + +## Controlling which services to start + +The Docker image *vespaengine/vespa*'s [start script](https://github.com/vespa-engine/docker-image/blob/master/include/start-container.sh) takes a parameter that controls which services are started inside the container. + +Starting a *configserver* container: + +```bash +$ docker run \ + --env VESPA_CONFIGSERVERS= \ + vespaengine/vespa configserver +``` + +Starting a *services* container (configserver will not be started): + +```bash +$ docker run \ + --env VESPA_CONFIGSERVERS= \ + vespaengine/vespa services +``` + +Starting a container with *both configserver and services*: + +```bash +$ docker run \ + --env VESPA_CONFIGSERVERS= \ + vespaengine/vespa configserver,services +``` + +This is required in the case where the configserver container should run other services like an adminserver or logserver (see [services.html](/en/reference/applications/services/services)) + +If the [VESPA\_CONFIGSERVERS](/en/operations/self-managed/files-processes-and-ports#environment-variables) environment variable is not specified, it will be set to the container hostname, also see [node setup](/en/operations/self-managed/node-setup#hostname). + +Use the [multinode-HA](https://github.com/vespa-engine/sample-apps/tree/master/examples/operations/multinode-HA) sample application as a blueprint for how to set up config servers and services. + +## Graceful stop + +Stopping a running *vespaengine/vespa* container triggers a graceful shutdown, which saves time when starting the container again (i.e., data structures are flushed). If the container is shut down forcefully, the content nodes might need to restore the state from the transaction log, which might be time-consuming. There is no chance of data loss or data corruption as the data is always written and synced to persistent storage. + +The default timeout for the Docker daemon to wait for the shutdown might be too low for larger number of documents per node. Below stop will wait at least 120 seconds before terminating the running container forcefully, if the stop is successfully performed before the timeout has passed, the command takes less than the timeout: + +```bash +$ docker stop name -t 120 +``` + +It is also possible to configure the default Docker daemon timeout, see [\--shutdown-timeout](https://docs.docker.com/reference/cli/dockerd/). + +A clean content node shutdown looks like: + +```bash +[2025-05-02 10:07:52.052] EVENT searchnode proton.node.server stopping/1 name="storagenode" why="Stopped" +[2025-05-02 10:07:52.056] EVENT searchnode proton stopping/1 name="servicelayer" why="clean shutdown" +[2025-05-02 10:07:52.056] INFO searchnode proton.proton.server.rtchooks shutting down monitoring interface +[2025-05-02 10:07:52.058] INFO searchnode proton.searchlib.docstore.logdatastore Flushing. Disk bloat is now at 0 of 8832 at 0.00 percent +[2025-05-02 10:07:52.059] INFO searchnode proton.searchlib.docstore.logdatastore Flushing. Disk bloat is now at 0 of 8832 at 0.00 percent +[2025-05-02 10:07:52.060] INFO searchnode proton.searchlib.docstore.logdatastore Flushing. Disk bloat is now at 0 of 8840 at 0.00 percent +[2025-05-02 10:07:52.066] INFO searchnode proton.transactionlog.server Stopping TLS +[2025-05-02 10:07:52.066] INFO searchnode proton.transactionlog.server TLS Stopped +[2025-05-02 10:07:52.071] EVENT searchnode proton stopping/1 name="proton" why="clean shutdown" +[2025-05-02 10:07:52.078] EVENT config-sentinel sentinel.sentinel.service stopped/1 name="searchnode" pid=354 exitcode=0 +``` + +## Memory + +The [sample applications](https://github.com/vespa-engine/sample-apps) and [local application deployment guide](/en/basics/deploy-an-application-local) indicates the minimum memory requirements for the Docker containers. + + +**Note:** + +Too little memory is a very common problem when testing Vespa in Docker containers. Use the below to troubleshoot before making a support request, and also see the [FAQ](/en/learn/faq). + + +As a rule of thumb, a single-node Vespa application requires a minimum of 4 GB for the Docker container. Using `docker stats` can be useful to track memory usage: + +```bash +$ docker stats + +CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS +589bf5801b22 node0 213.25% 697.3MiB / 3.84GiB 17.73% 14.2kB / 11.5kB 617MB / 976MB 253 +e108dde84679 node1 213.52% 492.7MiB / 3.84GiB 12.53% 15.7kB / 12.7kB 74.3MB / 924MB 252 +be43aacd0bbb node2 191.22% 497.8MiB / 3.84GiB 12.66% 19.6kB / 21.6kB 64MB / 949MB 261 +``` + +It is not necessarily easy to verify that Vespa has started all services successfully. Symptoms of errors due to insufficient memory vary, depending on where it fails. Example: Inspect restart logs in a container named *vespa*, running the [quickstart](/en/basics/deploy-an-application-local) with only 2G: + +```bash +$ docker exec -it vespa sh -c "/opt/vespa/bin/vespa-logfmt -S config-sentinel -c sentinel.sentinel.service" + +INFO : config-sentinel sentinel.sentinel.service container: incremented restart penalty to 2.000 seconds +INFO : config-sentinel sentinel.sentinel.service container: incremented restart penalty to 6.000 seconds +INFO : config-sentinel sentinel.sentinel.service container: incremented restart penalty to 14.000 seconds +INFO : config-sentinel sentinel.sentinel.service container: incremented restart penalty to 30.000 seconds +INFO : config-sentinel sentinel.sentinel.service container: will delay start by 25.173 seconds +INFO : config-sentinel sentinel.sentinel.service container: incremented restart penalty to 62.000 seconds +INFO : config-sentinel sentinel.sentinel.service container: incremented restart penalty to 126.000 seconds +INFO : config-sentinel sentinel.sentinel.service container: will delay start by 119.515 seconds +INFO : config-sentinel sentinel.sentinel.service container: incremented restart penalty to 254.000 seconds +INFO : config-sentinel sentinel.sentinel.service container: incremented restart penalty to 510.000 seconds +INFO : config-sentinel sentinel.sentinel.service container: will delay start by 501.026 seconds +INFO : config-sentinel sentinel.sentinel.service container: incremented restart penalty to 1022.000 seconds +INFO : config-sentinel sentinel.sentinel.service container: incremented restart penalty to 1800.000 seconds +INFO : config-sentinel sentinel.sentinel.service container: will delay start by 1793.142 seconds +``` + +Observe that the *container* service restarts in a loop, with increasing pause. + +A common problem is [config servers](/en/operations/self-managed/configuration-server) not starting or running properly due to a lack of memory. This manifests itself as nothing listening on 19071, or deployment failures. + +Some guides/sample applications have specific configurations to minimize resource usage. Example from [multinode-HA](https://github.com/vespa-engine/sample-apps/tree/master/examples/operations/multinode-HA): + +```bash +$ docker run --detach --name node0 --hostname node0.vespanet \ + -e VESPA_CONFIGSERVERS=node0.vespanet,node1.vespanet,node2.vespanet \ + -e VESPA_CONFIGSERVER_JVMARGS="-Xms32M -Xmx128M" \ + -e VESPA_CONFIGPROXY_JVMARGS="-Xms32M -Xmx32M" \ + --network vespanet \ + --publish 19071:19071 --publish 19100:19100 --publish 19050:19050 --publish 20092:19092 \ + vespaengine/vespa +``` + +Here [VESPA\_CONFIGSERVER\_JVMARGS](/en/operations/self-managed/files-processes-and-ports#environment-variables) and [VESPA\_CONFIGPROXY\_JVMARGS](/en/operations/self-managed/files-processes-and-ports#environment-variables) are tweaked to the minimum for a functional test only. + + +**Important:** + +For production use, do not reduce memory settings in `VESPA_CONFIGSERVER_JVMARGS` and `VESPA_CONFIGPROXY_JVMARGS` unless you know what you are doing - the Vespa defaults are set for regular production use, and rarely need changing. + + +Container memory setting are done in *services.xml*, example from [multinode-HA](https://github.com/vespa-engine/sample-apps/blob/master/examples/operations/multinode-HA/services.xml): + +```xml + + + + + +``` + +Make sure that the settings match the Docker container Vespa is running in. + +Also see [node memory settings](/en/operations/self-managed/node-setup#memory-settings) for more settings. + +## Network + +Vespa processes communicate over both fixed and ephemeral ports - in general, all ports must be accessible. See [example ephemeral use](/en/writing/visiting#handshake-failed). + +Find an example application using a Docker network in [multinode-HA](https://github.com/vespa-engine/sample-apps/tree/master/examples/operations/multinode-HA). + +## Resource usage + +Note that CPU usage will not be zero even if there are zero documents and zero queries. Starting the *vespaengine/vespa* container image means starting the [configuration server](/en/operations/self-managed/configuration-server) and the [configuration sentinel](/en/operations/self-managed/config-sentinel). When deploying an application, the sentinel starts the configured service processes, and they all listen to work to do, changes in the config, and so forth. + +Therefore, an "idle" container instance consumes CPU and memory. + +## Troubleshooting + +The Vespa documentation examples use `docker`. The Vespa Team has good experience with using `podman`, too, in the examples just change from `docker` to `podman`. We recommend using Podman v5, see the [release notes](https://github.com/containers/podman/blob/main/RELEASE_NOTES.md). [emulating-docker-cli-with-podman](https://podman-desktop.io/docs/migrating-from-docker/emulating-docker-cli-with-podman) is a useful resource. + +Many startup failures are caused by a failed Vespa Container start due to configuration or download errors. Use `docker logs vespa` to show the log (this example assumes a Docker container named `vespa`, use `docker ps` to list containers). + +### Docker image + +Make sure to use a recent Vespa release (check [releases](https://factory.vespa.ai/releases)) and validate the downloaded image: + +```bash +$ docker images +REPOSITORY TAG IMAGE ID CREATED SIZE +docker.io/vespaengine/vespa latest 8cfb0da22c01 35 hours ago 1.2 GB +``` + +### Model download failures + +If the application package depends on downloaded models, look for `RuntimeException: Not able to create config builder for payload` - [details](/en/applications/components#component-load). \ No newline at end of file diff --git a/en/operations/self-managed/files-processes-and-ports.html b/en/operations/self-managed/files-processes-and-ports.html deleted file mode 100644 index da3e2cebdc..0000000000 --- a/en/operations/self-managed/files-processes-and-ports.html +++ /dev/null @@ -1,337 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Files, Processes, Ports, Environment" -applies_to: self-managed -redirect_from: -- /en/setting-vespa-variables.html -- /en/reference/files-processes-and-ports.html -- /en/operations-selfhosted/files-processes-and-ports.html ---- - -

    - This is a reference of directories used in a Vespa installation, - processes that run on the Vespa nodes and ports / environment variables used. - Also see log files. -

    - - - -

    Directories

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    DirectoryDescription
    $VESPA_HOME/bin/Command line utilities and scripts
    $VESPA_HOME/libexec/vespa/Command line utilities and scripts
    $VESPA_HOME/sbin/Server programs, daemons, etc
    $VESPA_HOME/lib64/Dynamically linked libraries, typically third-party - libraries
    $VESPA_HOME/lib/jars/Java archives
    $VESPA_HOME/logs/vespa/Log files
    $VESPA_HOME/var/db/vespa/config_server/serverdb/Config server database and user applications
    $VESPA_HOME/share/vespa/A directory with config definitions and XML schemas for - application package validation
    $VESPA_HOME/conf/vespaVarious config files used by Vespa or libraries Vespa depend on
    - - - -

    Processes and ports

    -

    -The following is an overview of which ports and port ranges -are used by the different services in a Vespa system. -Note that for services capable of running multiple instances on the same node, -all instances will run within the listed port range. -

    -

    - Processes are run as user vespa. -

    -

    -Many services are allocated ports dynamically. -So even though the allocation is deterministic, -i.e. the same system will get the same ports on subsequent startups, -a particular service instance may get different ports -when the overall system setup is changed through services.xml. -Use vespa-model-inspect to see port allocations. -

    -
      -
    • - The number of ports used in a range depends on number of instances that are running -
    • - Not all ports within a range are used, - but they are assigned each service to support future extensions -
    • - The range from 19100 is used for internal communication ports, - i.e. ports that are not necessary to use from an external API -
    • - See - Configuring Http Servers and Filters for how to configure Container ports and - services.xml for how to configure other ports -
    • -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    ProcessHostPort/rangepsFunction
    Config serverConfig server nodes19070-19071java (...) -jar $VESPA_HOME/lib/jars/standalone-container-jar-with-dependencies.jarVespa Configuration server
    2181-2183Embedded Zookeeper cluster ports, see - - zookeeper-server.def
    Config sentinelAll nodes19098$VESPA_HOME/sbin/vespa-config-sentinelSentinel that starts and stops vespa services and makes sure they - are running unless they are manually stopped
    Config proxyAll nodes19090java (…) com.yahoo.vespa.config.proxy.ProxyServerCommunication liaison between Vespa processes and config - server. Caches config in memory
    SlobrokAdmin nodes19099 for RPC port, HTTP port dynamically allocated in the 19100-19899 range$VESPA_HOME/sbin/vespa-slobrokService location object broker
    logdAll nodes19089$VESPA_HOME/sbin/vespa-logdReads local log files and sends them to log server
    Log serverLog server node19080java (...) -jar lib/jars/logserver-jar-with-dependencies.jarVespa Log server
    Metrics proxyAll nodes19092-19095java (...) -jar $VESPA_HOME/lib/jars/container-disc-with-dependencies.jarProvides a single access point for metrics from all services on a Vespa node
    DistributorContent clusterdynamically allocated in the 19100-19899 range$VESPA_HOME/sbin/vespa-distributord-binContent layer distributor processes
    Cluster controllerContent cluster19050, plus ports dynamically allocated in the 19100-19899 rangejava (...) -jar - $VESPA_HOME/lib/jars/container-disc-jar-with-dependencies.jarCluster controller processes, manages state for content nodes
    protonContent clusterdynamically allocated in the 19100-19899 range$VESPA_HOME/sbin/vespa-proton-binSearchnode process, receives queries from the container - and returns results from the indexes. - Also receives feed and indexes documents
    containerContainer cluster8080java (...) -jar $VESPA_HOME/lib/jars/container-disc-with-dependencies.jarContainer running servers, handlers and processing components
    - - - -

    System limits

    -

    - The startup scripts - checks that system limits are set, failing startup if not. - Refer to - vespa-configserver.service and - vespa.service - for minimum values. -

    - - - -

    Core dumps

    -

    - Example settings: -

    -
    -$ mkdir -p /tmp/cores && chmod a+rwx /tmp/cores
    -$ echo "/tmp/cores/core.%e.%p.%h.%t" > /proc/sys/kernel/core_pattern
    -
    -

    - This will write files like /tmp/cores/core.vespa-proton-bi.1721.localhost.1580387387. -

    - - - -

    Environment variables

    -

    - Vespa configuration is set in - application packages. - Some configuration is used to bootstrap nodes - this is set in environment variables. - Environment variables are only read at startup. -

    -

    - $VESPA_HOME/conf/vespa/default-env.txt is read in Vespa start scripts - - use this to modify variables (example). - Each line has the format action variablename value where the items are: -

    - - - - - - - - - - - - -
    ItemDescription
    actionOne of fallback, override, or unset. - fallback sets the variable if it is unset (or empty). - override set the value regardless. - unset unsets the variable.
    variablenameThe name of the variable, e.g. VESPA_CONFIGSERVERS
    valueThe rest of the line is the variable's value.
    -

    - Refer to the - template for format. -

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    Environment variableDescription
    VESPA_CONFIGSERVERS - A comma-separated list of hosts to run configservers, use fully qualified hostnames. - Should always be set to the same value on all hosts in a multi-host setup. - If not set, localhost is assumed. - Refer to configuration server operations. -
    VESPA_HOSTNAME -

    - Vespa uses hostname for node identity. - But sometimes this doesn't work properly, - either because that name can't be used to find an IP address - which works for connecting to services running on the node, - or it's just that the name doesn't agree with what the config server thinks the node's host name is. - In this case, override by setting the VESPA_HOSTNAME, - to be used instead of running the hostname command. -

    -

    - Note that VESPA_HOSTNAME will be used - both when a node identifies itself to the config server - and when a service on that node registers a network connection point - that other services can connect to. -

    -

    - An error message with "hostname detection failed" is emitted if the - VESPA_HOSTNAME isn't set and the hostname isn't usable. - If VESPA_HOSTNAME is set to something that cannot work, - an error with "hostname validation failed" is emitted instead. -

    -
    VESPA_CONFIG_SOURCES - Used by libraries like the Document API - to set config server endpoints. - Refer to configuration server operations - for example use. -
    VESPA_WEB_SERVICE_PORT - The port number where REST apis will run, default 8080. - This isn't strictly needed, as the port number can be set - for each HTTP server in services.xml, - but with a big application it can be easier to set the default port number just once. - Also note that this needs to be set when starting the configserver, - since the REST api implementation gets its port number from there. -
    VESPA_TLS_CONFIG_FILE - Absolute path to TLS configuration file. -
    VESPA_CONFIGSERVER_JVMARGSJVM arguments for the config server - - see tuning.
    VESPA_CONFIGPROXY_JVMARGSJVM arguments for the config proxy - - see tuning.
    VESPA_LOG_LEVEL - Tuning of log output from tools, see controlling log levels. -
    diff --git a/en/operations/self-managed/files-processes-and-ports.mdx b/en/operations/self-managed/files-processes-and-ports.mdx new file mode 100644 index 0000000000..585989a1c1 --- /dev/null +++ b/en/operations/self-managed/files-processes-and-ports.mdx @@ -0,0 +1,254 @@ +--- +title: "Files, Processes, Ports, Environment" +--- + +This is a reference of directories used in a Vespa installation, processes that run on the Vespa nodes and ports / environment variables used. Also see [log files](/en/reference/operations/log-files). + +## Directories + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    DirectoryDescription
    {`$VESPA_HOME/bin/`}Command line utilities and scripts
    {`$VESPA_HOME/libexec/vespa/`}Command line utilities and scripts
    {`$VESPA_HOME/sbin/`}Server programs, daemons, etc
    {`$VESPA_HOME/lib64/`}Dynamically linked libraries, typically third-party libraries
    {`$VESPA_HOME/lib/jars/`}Java archives
    {`$VESPA_HOME/logs/vespa/`}Log files
    {`$VESPA_HOME/var/db/vespa/config_server/serverdb/`}Config server database and user applications
    {`$VESPA_HOME/share/vespa/`}A directory with config definitions and XML schemas for application package validation
    {`$VESPA_HOME/conf/vespa`}Various config files used by Vespa or libraries Vespa depend on
    + +## Processes and ports + +The following is an overview of which ports and port ranges are used by the different services in a Vespa system. Note that for services capable of running multiple instances on the same node, all instances will run within the listed port range. + +Processes are run as user `vespa`. + +Many services are allocated ports dynamically. So even though the allocation is deterministic, i.e. the same system will get the same ports on subsequent startups, a particular service instance may get different ports when the overall system setup is changed through [services.xml](/en/reference/applications/services/services). Use [vespa-model-inspect](/en/reference/operations/self-managed/tools#vespa-model-inspect) to see port allocations. + +- The number of ports used in a range depends on number of instances that are running +- Not all ports within a range are used, but they are assigned each service to support future extensions +- The range from 19100 is used for internal communication ports, i.e. ports that are not necessary to use from an external API +- See [Configuring Http Servers and Filters](/en/applications/http-servers-and-filters) for how to configure Container ports and [services.xml](/en/reference/applications/services/services) for how to configure other ports + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    ProcessHostPort/rangepsFunction
    Config serverConfig server nodes19070-19071{`java (...) -jar $VESPA_HOME/lib/jars/standalone-container-jar-with-dependencies.jar`}Vespa Configuration server
    2181-2183Embedded Zookeeper cluster ports, see zookeeper-server.def
    Config sentinelAll nodes19098{`$VESPA_HOME/sbin/vespa-config-sentinel`}Sentinel that starts and stops vespa services and makes sure they are running unless they are manually stopped
    Config proxyAll nodes19090{`java (…) com.yahoo.vespa.config.proxy.ProxyServer`}Communication liaison between Vespa processes and config server. Caches config in memory
    SlobrokAdmin nodes19099 for RPC port, HTTP port dynamically allocated in the 19100-19899 range{`$VESPA_HOME/sbin/vespa-slobrok`}Service location object broker
    logdAll nodes19089{`$VESPA_HOME/sbin/vespa-logd`}Reads local log files and sends them to log server
    Log serverLog server node19080{`java (...) -jar lib/jars/logserver-jar-with-dependencies.jar`}Vespa Log server
    Metrics proxyAll nodes19092-19095{`java (...) -jar $VESPA_HOME/lib/jars/container-disc-with-dependencies.jar`}Provides a single access point for metrics from all services on a Vespa node
    DistributorContent clusterdynamically allocated in the 19100-19899 range{`$VESPA_HOME/sbin/vespa-distributord-bin`}Content layer distributor processes
    Cluster controllerContent cluster19050, plus ports dynamically allocated in the 19100-19899 range{`java (...) -jar $VESPA_HOME/lib/jars/container-disc-jar-with-dependencies.jar`}Cluster controller processes, manages state for content nodes
    protonContent clusterdynamically allocated in the 19100-19899 range{`$VESPA_HOME/sbin/vespa-proton-bin`}Searchnode process, receives queries from the container and returns results from the indexes. Also receives feed and indexes documents
    containerContainer cluster8080{`java (...) -jar $VESPA_HOME/lib/jars/container-disc-with-dependencies.jar`}Container running servers, handlers and processing components
    + +## System limits + +The [startup scripts](/en/operations/self-managed/admin-procedures#vespa-start-stop-restart) checks that system limits are set, failing startup if not. Refer to [vespa-configserver.service](https://github.com/vespa-engine/vespa/blob/master/vespabase/src/vespa-configserver.service.in) and [vespa.service](https://github.com/vespa-engine/vespa/blob/master/vespabase/src/vespa.service.in) for minimum values. + +## Core dumps + +Example settings: + +```bash +$ mkdir -p /tmp/cores && chmod a+rwx /tmp/cores +$ echo "/tmp/cores/core.%e.%p.%h.%t" > /proc/sys/kernel/core_pattern +``` + +This will write files like */tmp/cores/core.vespa-proton-bi.1721.localhost.1580387387*. + +## Environment variables + +Vespa configuration is set in [application packages](/en/basics/applications). Some configuration is used to bootstrap nodes - this is set in environment variables. Environment variables are only read at startup. + +*`$VESPA_HOME/conf/vespa/default-env.txt`* is read in Vespa start scripts - use this to modify variables ([example](/en/operations/self-managed/multinode-systems#aws-ec2)). Each line has the format `action variablename value` where the items are: + + + + + + + + + + + + + + + + + + + + + + +
    ItemDescription
    actionOne of {`fallback`}, {`override`}, or {`unset`}. {`fallback`} sets the variable if it is unset (or empty). {`override`} set the value regardless. {`unset`} unsets the variable.
    variablenameThe name of the variable, e.g. {`VESPA_CONFIGSERVERS`}
    valueThe rest of the line is the variable's value.
    + +Refer to the [template](https://github.com/vespa-engine/vespa/blob/master/vespabase/conf/default-env.txt.in) for format. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Environment variableDescription
    VESPA_CONFIGSERVERSA comma-separated list of hosts to run configservers, use fully qualified hostnames. Should always be set to the same value on all hosts in a multi-host setup. If not set, {`localhost`} is assumed. Refer to configuration server operations.
    VESPA_HOSTNAMEVespa uses {`hostname`} for node identity. But sometimes this doesn't work properly, either because that name can't be used to find an IP address which works for connecting to services running on the node, or it's just that the name doesn't agree with what the config server thinks the node's host name is. In this case, override by setting the {`VESPA_HOSTNAME`}, to be used instead of running the {`hostname`} command.

    Note that {`VESPA_HOSTNAME`} will be used *both* when a node identifies itself to the config server *and* when a service on that node registers a network connection point that other services can connect to.

    An error message with "hostname detection failed" is emitted if the {`VESPA_HOSTNAME`} isn't set and the hostname isn't usable. If {`VESPA_HOSTNAME`} is set to something that cannot work, an error with "hostname validation failed" is emitted instead.
    VESPA_CONFIG_SOURCESUsed by libraries like the Document API to set config server endpoints. Refer to configuration server operations for example use.
    VESPA_WEB_SERVICE_PORTThe port number where REST apis will run, default {`8080`}. This isn't strictly needed, as the port number can be set for each HTTP server in {`services.xml`}, but with a big application it can be easier to set the default port number just once. Also note that this needs to be set when starting the *configserver*, since the REST api implementation gets its port number from there.
    VESPA_TLS_CONFIG_FILEAbsolute path to TLS configuration file.
    VESPA_CONFIGSERVER_JVMARGSJVM arguments for the config server - see tuning.
    VESPA_CONFIGPROXY_JVMARGSJVM arguments for the config proxy - see tuning.
    VESPA_LOG_LEVELTuning of log output from tools, see controlling log levels
    \ No newline at end of file diff --git a/en/operations/self-managed/live-upgrade.html b/en/operations/self-managed/live-upgrade.html deleted file mode 100644 index e660b9f251..0000000000 --- a/en/operations/self-managed/live-upgrade.html +++ /dev/null @@ -1,92 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Live-upgrading Vespa" -applies_to: self-managed -redirect_from: -- /en/operations/live-upgrade.html -- /en/operations-selfhosted/live-upgrade.html ---- - -

    -This document describes how to live upgrade a Vespa instance. -Use this procedure to upgrade without disruption to read or write traffic. -

    -
      -
    1. Before upgrading -
        -
      • - If upgrading to a new major version: - Upgrade to the latest version on the current major first, then read the release notes - for the new major before progressing. -
      • -
      • - If upgrading to a new minor version - you should upgrade to a version that is less than 2 months older than the one you are running - When upgrading a config server this is verified by checking that the minor - version number bump is less than 30, otherwise the config server refuses to start. - This behavior can be overridden at your own risk by setting environment variable - VESPA_SKIP_UPGRADE_CHECK=true on config servers before upgrading. -
      • -
      • - Redundancy: For availability, - there must be sufficient capacity to take one node per cluster out of service at the time. - If the clusters have redundancy=1, or searchable-copies=1, - some data will not be available during the upgrade (reduced coverage). -
      • -
      • - To reduce node downtime, download the new Vespa version to all hosts in advance. -
      • -
      -
    2. - -
    3. Detach the application nodes -Not necessary in Vespa 8, for upgrading between Vespa 7 versions see Vespa 8 release notes. -
    4. - -
    5. Upgrade config servers -
      • - Install the new Vespa version on the config servers and - restart them one by one. - Wait until it is up again, look in vespa log for - "Changing health status code from 'initializing' to 'up'" - or use health checks. -
      • - Redeploy and activate the application: -
        $ vespa prepare <app> && vespa activate
        -
      • - The other nodes in the system will not receive config until they are upgraded to the new version - (there will be warnings in vespa log containing "Request callback failed: UNKNOWN_VESPA_VERSION" - until the node is upgraded). This is to make sure that no new, possibly incompatible, config - is served. -
      • -
      -
    6. - -
    7. Upgrade all other nodes one by one - - for each of the other nodes in the system: -
        -
      • Stop services - on the node. -
      • -
      • Install the new Vespa version.
      • -
      • Start services - on the node.
      • -
      • Wait until the node is fully up before doing the next node - - metrics/interfaces to be used to evaluate if the next node can be stopped: -
          -
        • - Check if a node is up using - /state/v1/health. -
        • -
        • - Check the vds.idealstate.merge_bucket.pending.average metric on content nodes. - When 0, all buckets are in sync - see example. -
        • -
        -
      • -
      -
    - -

    Troubleshooting

    -See config server troubleshooting. - diff --git a/en/operations/self-managed/live-upgrade.mdx b/en/operations/self-managed/live-upgrade.mdx new file mode 100644 index 0000000000..ddcd3f2de3 --- /dev/null +++ b/en/operations/self-managed/live-upgrade.mdx @@ -0,0 +1,30 @@ +--- +title: "Live-upgrading Vespa" +sidebarTitle: "Live Vespa upgrade procedure" +--- + +This document describes how to live upgrade a Vespa instance. Use this procedure to upgrade without disruption to read or write traffic. + +1. **Before upgrading** + - If upgrading to a **new major version**: Upgrade to the latest version on the current major first, then read the release notes for the new major before progressing. + - If upgrading to a new minor version [you should upgrade to a version that is less than 2 months older than the one you are running](/en/learn/releases#versions) When upgrading a config server this is verified by checking that the minor version number bump is less than 30, otherwise the config server refuses to start. This behavior can be overridden at your own risk by setting environment variable *VESPA\_SKIP\_UPGRADE\_CHECK=true* on config servers before upgrading. + - Redundancy: For availability, there must be sufficient capacity to take one node per cluster out of service at the time. If the clusters have redundancy=1, or searchable-copies=1, some data will not be available during the upgrade (reduced coverage). + - To reduce node downtime, download the new Vespa version to all hosts in advance. +2. **Detach the application nodes** Not necessary in Vespa 8, for upgrading between Vespa 7 versions see [Vespa 8 release notes](/en/reference/release-notes/vespa8#upgrade-procedure). +3. **Upgrade config servers** + - Install the new Vespa version on the config servers and [restart](/en/operations/self-managed/admin-procedures#vespa-start-stop-restart) them one by one. Wait until it is up again, look in vespa log for "Changing health status code from 'initializing' to 'up'" or use [health checks](/en/operations/self-managed/configuration-server#troubleshooting). + - Redeploy and activate the application: + + `$` [`vespa`](/en/clients/vespa-cli#deployment) `prepare && vespa activate` + - The other nodes in the system will not receive config until they are upgraded to the new version (there will be warnings in vespa log containing "Request callback failed: UNKNOWN\_VESPA\_VERSION" until the node is upgraded). This is to make sure that no new, possibly incompatible, config is served. +4. **Upgrade all other nodes one by one** - for each of the other nodes in the system: + - [Stop services](/en/operations/self-managed/admin-procedures#vespa-start-stop-restart) on the node. + - Install the new Vespa version. + - [Start services](/en/operations/self-managed/admin-procedures#vespa-start-stop-restart) on the node. + - Wait until the node is fully up before doing the next node - metrics/interfaces to be used to evaluate if the next node can be stopped: + - Check if a node is up using [/state/v1/health](/en/reference/api/state-v1#state-v1-health). + - Check the `vds.idealstate.merge_bucket.pending.average` metric on content nodes. When 0, all buckets are in sync - see [example](/en/operations/metrics). + +### Troubleshooting + +See [config server troubleshooting](/en/operations/self-managed/configuration-server#troubleshooting). \ No newline at end of file diff --git a/en/operations/self-managed/monitoring.html b/en/operations/self-managed/monitoring.html deleted file mode 100644 index 536b74f566..0000000000 --- a/en/operations/self-managed/monitoring.html +++ /dev/null @@ -1,579 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Monitoring" -applies_to: self-managed -redirect_from: -- /en/monitoring.html -- /en/monitoring-with-grafana-quick-start.html -- /en/operations/metrics-proxy.html -- /en/operations-selfhosted/metrics-proxy.html -- /en/operations-selfhosted/monitoring.html ---- - -

    -Vespa provides metrics integration with CloudWatch, Datadog and Prometheus / Grafana, -as well as a JSON HTTP API. -

    -

    -There are two main approaches to transfer metrics to an external system: -

      -
    • Have the external system pull metrics from Vespa
    • -
    • Make Vespa push metrics to the external system
    • -
    - -

    - Use the example overview of two nodes running Vespa for where the APIs are set up and how they interact: -

    -Metrics interfaces - -{% include note.html content=" -refer to the multinode -and multinode-HA -sample applications for a practical example of using the APIs. -These apps also include examples for how to find ports used by using -vespa-model-inspect."%} -

    - See the metrics guide for how to get a metric using /metrics/v1/values - and /prometheus/v1/values. - This guide also documents use of custom metrics and histograms. -

    - - - -

    Metrics proxy

    -

    - Each Vespa node has a metrics-proxy process running for this API, default port 19092. - It aggregates metrics from all processes on the node, and across nodes: -

    -

    - The metrics-proxy normally listens on port 19092 - - use vespa-model-inspect to validate. -

    -

    See the metrics guide for the metrics interfaces hosted by the metrics proxy.

    -

    - Metric-proxies intercommunicate to build a metric cache served on the internal applicationmetrics/v1/ API. - This is replicated on the container on /metrics/v2/values for easy access to all metrics for an application. -

    -

    - The metrics-proxy is started by the config-sentinel and is not configurable. - The metrics-proxy process looks like: -

    -
    -$ ps ax | grep admin/metrics/vespa-container
    -
    -  703 ?        Sl     0:10 /usr/bin/java
    -  -Dconfig.id=admin/metrics/vespa-container
    -  ...
    -  -cp /opt/vespa/lib/jars/jdisc_core-jar-with-dependencies.jar
    -  com.yahoo.jdisc.core.StandaloneMain
    -  file:/opt/vespa/lib/jars/container-disc-jar-with-dependencies.jar
    -
    - - - -

    /state/v1/health

    -

    - Per-process health status is found at http://host:port/state/v1/health -

    -Health API -

    - /state/v1/health is most commonly used for heartbeating, - see the reference for details. Example: -

    -
    {% highlight json %}
    -{
    -    "status": {
    -        "code": "up",
    -        "message": "Everything ok here"
    -    }
    -}
    -{% endhighlight %}
    - - - - -

    /state/v1/metrics

    -

    Per-process metrics are found at http://host:port/state/v1/metrics

    -

    - Internally, Vespa aggregates metrics in the APIs above from the per-process metrics and health APIs. - While most users would use the aggregated APIs, - the per-process metric APIs could be used for specific cases. -

    -

    - Metrics are reported in snapshots, where the snapshot specifies the - time window the metrics are gathered from. - Typically, the service will aggregate metrics as they are reported, and after each snapshot period, - a snapshot is taken of the current values, and they are reset. - Using this approach, min and max values are tracked, - and enables values like 95% percentile for each complete snapshot period. -

    -

    Refer to the reference for details.

    -

    Vespa supports custom metrics.

    -

    Example:

    -
    {% highlight json %}
    -{
    -    "status" : {
    -        "code" : "up",
    -        "message" : "Everything ok here"
    -    },
    -    "metrics" : {
    -        "snapshot" : {
    -            "from" : 1334134640.089,
    -            "to" : 1334134700.088,
    -        },
    -        "values" : [
    -            {
    -                "name" : "queries",
    -                "description" : "Number of queries executed during snapshot interval",
    -                "values" : {
    -                    "count" : 28,
    -                    "rate" : 0.4667
    -                },
    -                "dimensions" : {
    -                    "chain" : "vespa"
    -                }
    -            },
    -            {
    -                "name" : "hits_per_query",
    -                "description" : "Number of hits returned for queries during snapshot interval",
    -                "values" : {
    -                    "count" : 28,
    -                    "rate" : 0.4667,
    -                    "average" : 128.3,
    -                    "min" : 0,
    -                    "max" : 1000,
    -                    "sum" : 3584,
    -                    "last" : 72,
    -                    "95percentile" : 849.1,
    -                    "99percentile": 672.0,
    -                },
    -                "dimensions" : {
    -                    "chain" : "vespa"
    -                }
    -            }
    -        ]
    -    }
    -}
    -{% endhighlight %}
    -

    - A flat list of metrics is returned. - Each metric value reported by a component should be a separate metric. - For related metrics, prefix metric names with common parts and dot separate the names - - e.g. memory.free and memory.virtual. -

    - - - -

    /metrics/v1/values

    -

    - This API can be used for monitoring, using products like - Prometheus and DataDog. - The response contains a selected set of metrics from each service running on the node, - see the reference for details. - Example: -

    -
    $ curl http://localhost:19092/metrics/v1/values
    -
    {% highlight json %}
    -{
    -    "services": [
    -        {
    -            "name": "vespa.container",
    -            "timestamp": 1661945852,
    -            "status": {
    -                "code": "up",
    -                "description": "Data collected successfully"
    -            },
    -            "metrics": [
    -                {
    -                    "values": {
    -                        "memory_virt": 3693178880,
    -                        "memory_rss": 1331331072,
    -                        "cpu": 2.3794255627932,
    -                        "cpu_util": 0.2379425562793
    -                    },
    -                    "dimensions": {
    -                        "metrictype": "system",
    -                        "instance": "container",
    -                        "clustername": "default",
    -                        "vespaVersion": "8.43.64"
    -                    }
    -                }
    -            ]
    -        }
    -    ]
    -}
    -
    -{% endhighlight %}
    - - -

    /metrics/v2/values

    -
    $ curl http://localhost:19092/metrics/v2/values
    -

    - A container service on the same node as the metrics proxy might forward /metrics/v2/values - on its own port, normally 8080. -

    -

    - /metrics/v2/values exposes a selected set of metrics for every service on all nodes for the application. - For example, it can be used to - - pull Vespa metrics to Cloudwatch using an AWS lambda function. -

    -

    - The metrics API exposes a - selected - set of metrics for the whole application, or for a single node, - to allow integration with graphing and alerting services. -

    -

    - The response is a nodes list with metrics (see example output below), - see the reference for details. -

    -
    {% highlight json %}
    -{
    -    "nodes": [
    -        {
    -            "hostname": "vespa-container",
    -            "role": "hosts/vespa-container",
    -            "services": [
    -                {
    -                    "name": "vespa.container",
    -                    "timestamp": 1634127924,
    -                    "status": {
    -                        "code": "up",
    -                        "description": "Data collected successfully"
    -                    },
    -                    "metrics": [
    -                        {
    -                            "values": {
    -                                "memory_virt": 3685253120,
    -                                "memory_rss": 1441259520,
    -                                "cpu": 29.1900152827305
    -                            },
    -                            "dimensions": {
    -                                "serviceId": "container"
    -                            }
    -                        },
    -                        {
    -                            "values": {
    -                                "jdisc.gc.ms.average": 0
    -                            },
    -                            "dimensions": {
    -                                "gcName": "G1OldGeneration",
    -                                "serviceId": "container"
    -                            }
    -                        },
    -{% endhighlight %}
    - - -

    /prometheus/v1/values

    -

    - Vespa provides a node metrics API on each node at http://host:port/prometheus/v1/values -

    -

    - Port and content is the same as /metrics/v1/values. -

    -

    - The prometheus API on each node exposes metrics in a text based - format that can be - scraped by Prometheus. - See below for a Prometheus / Grafana example. -

    - - - -

    Pulling metrics from Vespa

    -

    -All pull-based solutions use Vespa's metrics API, -which provides metrics in JSON format, either for the full system or for a single node. -The polling frequency should be limited to max once every 30 seconds as more frequent polling would -not give increased granularity but only lead to unnecessary load on your systems. -

    - - - - - - - - - - - - - - - - - -
    ServiceDescription
    CloudWatch

    - Metrics can be pulled into CloudWatch from both Vespa Cloud and self-hosted Vespa. - The recommended solution is to use an AWS lambda function, as described in - - Pulling Vespa metrics to Cloudwatch. -

    Datadog

    - The Vespa team has created a Datadog Agent integration - to allow real-time monitoring of Vespa in Datadog. - The Datadog Vespa integration - is not packaged with the agent, but is included in Datadog's - integrations-extras repository. - Clone it and follow the steps in the - README. -

    - {% include note.html content='The Datadog Agent integration currently works for self-hosted Vespa only.' %} -
    Prometheus

    - Vespa exposes metrics in a text based - format that can be - scraped by Prometheus. - For Vespa Cloud, append /prometheus/v1/values - to your endpoint URL. For self-hosted Vespa the URL is: - http://<container-host>:<port>/prometheus/v1/values, where - the port is the same as for searching, e.g. 8080. Metrics for each individual host - can also be retrieved at http://host:19092/prometheus/v1/values. -

    - See the below for a Prometheus / Grafana example. -

    - - - -

    Pushing metrics to CloudWatch

    -

    -Note: This method currently works for self-hosted Vespa only. -

    -This is presumably the most convenient way to monitor Vespa in CloudWatch. -Steps / requirements: -

    -
      -
    1. An IAM user or IAM role that only has the putMetricData permission.
    2. -
    3. Store the credentials for the above user or role in a - - shared credentials file on each Vespa node. - If a role is used, provide a mechanism to keep the credentials file updated when keys are rotated.
    4. -
    5. Configure Vespa to push metrics to CloudWatch - - example configuration for the admin section in services.xml: -
      -<metrics>
      -    <consumer id="my-cloudwatch">
      -        <metric-set id="default" />
      -        <cloudwatch region="us-east-1" namespace="my-vespa-metrics">
      -            <shared-credentials file="/path/to/credentials-file" />
      -        </cloudwatch>
      -    </consumer>
      -</metrics>
      -
      - This configuration sends the default set of Vespa metrics to the CloudWatch namespace - my-vespa-metrics in the us-east-1 region. - Refer to the - metric list - for default metric set. -
    6. -
    - - -

    Monitoring with Grafana

    -

    - Follow these steps to set up monitoring with Grafana for a Vespa instance. - This guide builds on the quick start - by adding three more Docker containers and connecting these in the Docker monitoring network: -

    -Docker containers in a Docker network -
      -
    1. -

      Run the Quick Start:

      -

      - Complete steps 1-7 (or 1-10), but skip the removal step. - Clone repository: -

      -
      - -
      -$ git clone --depth 1 https://github.com/vespa-engine/sample-apps.git && \
      -  cd sample-apps/examples/operations/monitoring/album-recommendation-monitoring
      -
      -
      -
    2. -
    3. -

      Create a network and add the vespa container to it:

      -
      - -
      -$ docker network create --driver bridge monitoring && \
      -  docker network connect monitoring vespa
      -
      -
      -

      - This creates the monitoring network and attaches the vespa container to it. - Find details in - docker-compose.yml. -

      -
    4. -
    5. -

      Launch Prometheus:

      -
      - -
      -$ docker run --detach --name sample-apps-prometheus --hostname prometheus \
      -  --network monitoring \
      -  --publish 9090:9090 \
      -  --volume `pwd`/prometheus/prometheus-selfhosted.yml:/etc/prometheus/prometheus.yml \
      -  prom/prometheus
      -
      -
      -

      - Prometheus is a time-series database, - which holds a series of values associated with a timestamp. - Open Prometheus at - http://localhost:9090/. - One can easily find what data Prometheus has, the input box auto-completes, - e.g. enter feed_operations_rate and click Execute. - Also explore the Status dropdown. -

      -
    6. -
    7. -

      Launch Grafana:

      -
      - -
      -$ docker run --detach --name sample-apps-grafana \
      -  --network monitoring \
      -  --publish 3000:3000 \
      -  --volume `pwd`/grafana/provisioning:/etc/grafana/provisioning \
      -  grafana/grafana
      -
      -
      -

      - This launches Grafana. - Grafana is a visualisation tool that can be used to easily make representations of important metrics surrounding Vespa. - Open - http://localhost:3000/ and find the Grafana login screen - log in with admin/admin (skip changing password). - From the list on the left, click Browse under Dashboards (the symbol with 4 blocks), - then click the Vespa Detailed Monitoring Dashboard. - The dashboard displays detailed Vespa metrics - empty for now. -

      -
    8. -
    9. -

      Build the Random Data Feeder:

      -
      - -
      -$ docker build album-recommendation-random-data --tag random-data-feeder
      -
      -
      - -
      -$ docker build album-recommendation-random-data --no-cache --tag random-data-feeder -o out
      -
      -
      -$ ls out/usr/local/lib/app.jar
      -
      -

      - This builds the - - Random Data Feeder - - it generates random sets of data and puts them into the Vespa instance. - Also, it repeatedly runs queries, for Grafana visualisation. - Compiling the Random Data Feeder takes a few minutes. -

      -
    10. -
    11. -

      Run the Random Data Feeder:

      -
      - - -
      -$ docker run --detach --name sample-apps-random-data-feeder \
      -  --network monitoring \
      -  random-data-feeder
      -
      -
      -
    12. -
    13. -

      Check the updated Grafana metrics:

      -

      - Graphs will now show up in Grafana and Prometheus - it might take a minute or two. - The Grafana dashboard is fully customisable. - Change the default modes of Grafana and Prometheus by editing the configuration files in - - album-recommendation-monitoring. -

      -
    14. -
    15. -

      Remove containers and network:

      -
      - - -
      -$ docker rm -f vespa \
      -  sample-apps-grafana \
      -  sample-apps-prometheus \
      -  sample-apps-random-data-feeder
      -
      -
      -
      - -
      -$ docker network rm monitoring || true
      -
      -
      -
    16. -
    - - - -

    Histograms

    -

    - Metric histograms is supported for - - Gauge metrics. - Create the metric like in - - album-recommendation-java, adding the histogram: -

    -
    -public HitCountSearcher(MetricReceiver receiver) {
    -    this.hitCountMetric = receiver.declareGauge(EXAMPLE_METRIC_NAME, Optional.empty(),
    -        new MetricSettings.Builder().histogram(true).build());
    -}
    -
    -

    - The histograms for the last five minutes of logged data are available as CSV per - dimension at /state/v1/metrics/histograms. - Example output: -

    -
    -# start of metric hits_per_query, dimensions: { "chain": "metalchain" }
    -"Value","Percentile","TotalCount","1/(1-Percentile)"
    -1.00,0.000000000000,1,1.00
    -1.00,1.000000000000,1,Infinity
    -# end of metric hits_per_query, dimensions: { "chain": "metalchain" }
    -# start of metric example_hitcounts, dimensions: { "query_language": "en" }
    -"Value","Percentile","TotalCount","1/(1-Percentile)"
    -1.00,0.000000000000,1,1.00
    -1.00,1.000000000000,1,Infinity
    -# end of metric example_hitcounts, dimensions: { "query_language": "en" }
    -# start of metric query_latency, dimensions: { "chain": "metalchain" }
    -"Value","Percentile","TotalCount","1/(1-Percentile)"
    -5.69,0.000000000000,1,1.00
    -5.69,1.000000000000,1,Infinity
    -# end of metric query_latency, dimensions: { "chain": "metalchain" }
    -# start of metric totalhits_per_query, dimensions: { "chain": "metalchain" }
    -"Value","Percentile","TotalCount","1/(1-Percentile)"
    -1.00,0.000000000000,1,1.00
    -1.00,1.000000000000,1,Infinity
    -# end of metric totalhits_per_query, dimensions: { "chain": "metalchain" }
    -
    diff --git a/en/operations/self-managed/monitoring.mdx b/en/operations/self-managed/monitoring.mdx new file mode 100644 index 0000000000..712d3a2eef --- /dev/null +++ b/en/operations/self-managed/monitoring.mdx @@ -0,0 +1,409 @@ +--- +title: "Monitoring" +description: "Vespa provides metrics integration with CloudWatch, Datadog and Prometheus / Grafana, as well as a JSON HTTP API." +--- + +There are two main approaches to transfer metrics to an external system: + +- Have the external system *pull* metrics from Vespa +- Make Vespa *push* metrics to the external system + +Use the example overview of two nodes running Vespa for where the APIs are set up and how they interact: + + +![Metrics interfaces](/assets/img/metrics-api.svg) + + +- [/metrics/v1/values](#metrics-v1-values) is the node metrics api, and aggregates metrics for processes running on the node. +- [/state/v1/metrics](#state-v1-metrics) is the process metrics api, and exposes all metrics from an individual service - here each node runs a container and a content node. +- [/metrics/v2/values](#metrics-v2-values) is an aggregation of [/metrics/v1/values](#metrics-v1-values), for all nodes. Served on the metrics-proxy port. +- [/prometheus/v1/values](/en/reference/api/prometheus-v1#prometheus-v1-values) is the same as [/metrics/v1/values](#metrics-v1-values), in prometheus format. Served on the metrics-proxy port. +- [/prometheus/v1/values](/en/reference/api/prometheus-v1#prometheus-v1-values) and [/metrics/v2/values](#metrics-v2-values) are also replicated on the container port, default 8080. + + +**Note:** + +refer to the [multinode](https://github.com/vespa-engine/sample-apps/tree/master/examples/operations/multinode) and [multinode-HA](https://github.com/vespa-engine/sample-apps/tree/master/examples/operations/multinode-HA) sample applications for a practical example of using the APIs. These apps also include examples for how to find ports used by using [vespa-model-inspect](/en/reference/operations/self-managed/tools#vespa-model-inspect). + + +See the [metrics guide](/en/operations/metrics) for how to get a metric using `/metrics/v1/values` and `/prometheus/v1/values`. This guide also documents use of custom metrics and histograms. + +## Metrics proxy + +Each Vespa node has a *metrics-proxy* process running for this API, default port 19092. It aggregates metrics from all processes on the node, and across nodes: + +The metrics-proxy normally listens on port 19092 - use [vespa-model-inspect](/en/reference/operations/self-managed/tools#vespa-model-inspect) to validate. + +See the [metrics guide](/en/operations/metrics) for the metrics interfaces hosted by the metrics proxy. + +Metric-proxies intercommunicate to build a metric cache served on the internal *applicationmetrics/v1/* API. This is replicated on the container on */metrics/v2/values* for easy access to all metrics for an application. + +The metrics-proxy is started by the [config-sentinel](/en/operations/self-managed/config-sentinel) and is not configurable. The metrics-proxy process looks like: + +```bash +$ ps ax | grep admin/metrics/vespa-container + + 703 ? Sl 0:10 /usr/bin/java + -Dconfig.id=admin/metrics/vespa-container + ... + -cp /opt/vespa/lib/jars/jdisc_core-jar-with-dependencies.jar + com.yahoo.jdisc.core.StandaloneMain + file:/opt/vespa/lib/jars/container-disc-jar-with-dependencies.jar +``` + +## /state/v1/health + +*Per-process* health status is found at `http://host:port/state/v1/health` + + +![Health API](/assets/img/health-api.svg) + + +`/state/v1/health` is most commonly used for heartbeating, see the [reference](/en/reference/api/state-v1#state-v1-health) for details. Example: + +```json +{ + "status": { + "code": "up", + "message": "Everything ok here" + } +} +``` + +## /state/v1/metrics + +*Per-process* metrics are found at `http://host:port/state/v1/metrics` + +Internally, Vespa aggregates metrics in the APIs above from the *per-process* metrics and health APIs. While most users would use the aggregated APIs, the per-process metric APIs could be used for specific cases. + +Metrics are reported in snapshots, where the snapshot specifies the time window the metrics are gathered from. Typically, the service will aggregate metrics as they are reported, and after each snapshot period, a snapshot is taken of the current values, and they are reset. Using this approach, min and max values are tracked, and enables values like 95% percentile for each complete snapshot period. + +Refer to the [reference](/en/reference/api/state-v1#state-v1-metrics) for details. + +Vespa supports [custom metrics](/en/operations/metrics#metrics-from-custom-components). + +Example: + +```json expandable +{ + "status" : { + "code" : "up", + "message" : "Everything ok here" + }, + "metrics" : { + "snapshot" : { + "from" : 1334134640.089, + "to" : 1334134700.088, + }, + "values" : [ + { + "name" : "queries", + "description" : "Number of queries executed during snapshot interval", + "values" : { + "count" : 28, + "rate" : 0.4667 + }, + "dimensions" : { + "chain" : "vespa" + } + }, + { + "name" : "hits_per_query", + "description" : "Number of hits returned for queries during snapshot interval", + "values" : { + "count" : 28, + "rate" : 0.4667, + "average" : 128.3, + "min" : 0, + "max" : 1000, + "sum" : 3584, + "last" : 72, + "95percentile" : 849.1, + "99percentile": 672.0, + }, + "dimensions" : { + "chain" : "vespa" + } + } + ] + } +} +``` + +A flat list of metrics is returned. Each metric value reported by a component should be a separate metric. For related metrics, prefix metric names with common parts and dot separate the names - e.g. `memory.free` and `memory.virtual`. + +### /metrics/v1/values + +This API can be used for monitoring, using products like [Prometheus](#pulling-into-prometheus) and [DataDog](#pulling-into-datadog). The response contains a selected set of metrics from each service running on the node, see the [reference](/en/reference/api/metrics-v1#metrics-v1-values) for details. Example: + +```bash +$ curl http://localhost:19092/metrics/v1/values +``` + +```json expandable +{ + "services": [ + { + "name": "vespa.container", + "timestamp": 1661945852, + "status": { + "code": "up", + "description": "Data collected successfully" + }, + "metrics": [ + { + "values": { + "memory_virt": 3693178880, + "memory_rss": 1331331072, + "cpu": 2.3794255627932, + "cpu_util": 0.2379425562793 + }, + "dimensions": { + "metrictype": "system", + "instance": "container", + "clustername": "default", + "vespaVersion": "8.43.64" + } + } + ] + } + ] +} +``` + +### /metrics/v2/values + +```bash +$ curl http://localhost:19092/metrics/v2/values +``` + +A container service on the same node as the metrics proxy might forward `/metrics/v2/values` on its own port, normally 8080. + +`/metrics/v2/values` exposes a selected set of metrics for every service on all nodes for the application. For example, it can be used to [pull Vespa metrics to Cloudwatch](https://github.com/vespa-engine/metrics-emitter/tree/master/cloudwatch) using an AWS lambda function. + +The [metrics API](#metrics-v2-values) exposes a [selected set of metrics](https://github.com/DataDog/integrations-extras/blob/master/vespa/metadata.csv) for the whole application, or for a single node, to allow integration with graphing and alerting services. + +The response is a `nodes` list with metrics (see example output below), see the [reference](/en/reference/api/metrics-v2#metrics-v2-values) for details. + +```json expandable +{ + "nodes": [ + { + "hostname": "vespa-container", + "role": "hosts/vespa-container", + "services": [ + { + "name": "vespa.container", + "timestamp": 1634127924, + "status": { + "code": "up", + "description": "Data collected successfully" + }, + "metrics": [ + { + "values": { + "memory_virt": 3685253120, + "memory_rss": 1441259520, + "cpu": 29.1900152827305 + }, + "dimensions": { + "serviceId": "container" + } + }, + { + "values": { + "jdisc.gc.ms.average": 0 + }, + "dimensions": { + "gcName": "G1OldGeneration", + "serviceId": "container" + } + }, +``` + +### /prometheus/v1/values + +Vespa provides a *node metrics API* on each *node* at `http://host:port/prometheus/v1/values` + +Port and content is the same as */metrics/v1/values*. + +The prometheus API on each node exposes metrics in a text based [format](https://prometheus.io/docs/instrumenting/exposition_formats/) that can be scraped by [Prometheus](https://prometheus.io/docs/introduction/overview/). See below for a Prometheus / Grafana example. + +## Pulling metrics from Vespa + +All pull-based solutions use Vespa's [metrics API](#metrics-v2-values), which provides metrics in JSON format, either for the full system or for a single node. The polling frequency should be limited to max once every 30 seconds as more frequent polling would not give increased granularity but only lead to unnecessary load on your systems. + + + + + + + + + + + + + + + + + + + + + + +
    ServiceDescription
    CloudWatchMetrics can be pulled into CloudWatch from both Vespa Cloud and self-hosted Vespa. The recommended solution is to use an AWS lambda function, as described in Pulling Vespa metrics to Cloudwatch.
    DatadogThe Vespa team has created a Datadog Agent integration to allow real-time monitoring of Vespa in Datadog. The Datadog Vespa integration is not packaged with the agent, but is included in Datadog's integrations-extras repository. Clone it and follow the steps in the README.

    **Note:**

    The Datadog Agent integration currently works for self-hosted Vespa only.
    PrometheusVespa exposes metrics in a text based format that can be scraped by Prometheus. For Vespa Cloud, append */prometheus/v1/values* to your endpoint URL. For self-hosted Vespa the URL is: *http://{`:`}/prometheus/v1/values*, where the *port* is the same as for searching, e.g. 8080. Metrics for each individual host can also be retrieved at {`http://host:19092/prometheus/v1/values`}.

    See the below for a Prometheus / Grafana example.
    + +## Pushing metrics to CloudWatch + + +**Note:** + +This method currently works for self-hosted Vespa only. + + +This is presumably the most convenient way to monitor Vespa in CloudWatch. Steps / requirements: + + + +An IAM user or IAM role that only has the *putMetricData* permission. + + +Store the credentials for the above user or role in a [shared credentials file](https://docs.aws.amazon.com/ses/latest/dg/create-shared-credentials-file.html) on each Vespa node. If a role is used, provide a mechanism to keep the credentials file updated when keys are rotated. + + +Configure Vespa to push metrics to CloudWatch - example configuration for the [admin](/en/reference/applications/services/admin) section in *services.xml*: + +```xml + + + + + + + + +``` + +This configuration sends the default set of Vespa metrics to the CloudWatch namespace `my-vespa-metrics` in the `us-east-1` region. Refer to the [metric list](https://github.com/DataDog/integrations-extras/blob/master/vespa/metadata.csv) for `default` metric set. + + + +## Monitoring with Grafana + +Follow these steps to set up monitoring with Grafana for a Vespa instance. This guide builds on the [quick start](/en/basics/deploy-an-application-local) by adding three more Docker containers and connecting these in the Docker *monitoring* network: + + +![Docker containers in a Docker network](/assets/img/monitoring-getting-started.svg) + + + + +Complete steps [1-7](/en/basics/deploy-an-application-local) (or 1-10), but skip the removal step. Clone repository: + +```bash +$ git clone --depth 1 https://github.com/vespa-engine/sample-apps.git && \ + cd sample-apps/examples/operations/monitoring/album-recommendation-monitoring +``` + + + +```bash +$ docker network create --driver bridge monitoring && \ + docker network connect monitoring vespa +``` + +This creates the *monitoring* network and attaches the vespa container to it. Find details in [docker-compose.yml](https://github.com/vespa-engine/sample-apps/blob/master/examples/operations/monitoring/album-recommendation-monitoring/docker-compose.yml). + + +```bash +$ docker run --detach --name sample-apps-prometheus --hostname prometheus \ + --network monitoring \ + --publish 9090:9090 \ + --volume `pwd`/prometheus/prometheus-selfhosted.yml:/etc/prometheus/prometheus.yml \ + prom/prometheus +``` + +[Prometheus](https://prometheus.io/) is a time-series database, which holds a series of values associated with a timestamp. Open Prometheus at [http://localhost:9090/](http://localhost:9090/). One can easily find what data Prometheus has, the input box auto-completes, e.g. enter *feed\_operations\_rate* and click *Execute*. Also explore the *Status* dropdown. + + +```bash +$ docker run --detach --name sample-apps-grafana \ + --network monitoring \ + --publish 3000:3000 \ + --volume `pwd`/grafana/provisioning:/etc/grafana/provisioning \ + grafana/grafana +``` + +This launches [Grafana](https://grafana.com/oss/grafana/). Grafana is a visualisation tool that can be used to easily make representations of important metrics surrounding Vespa. Open [http://localhost:3000/](http://localhost:3000/) and find the Grafana login screen - log in with admin/admin (skip changing password). From the list on the left, click *Browse* under *Dashboards* (the symbol with 4 blocks), then click the *Vespa Detailed Monitoring Dashboard*. The dashboard displays detailed Vespa metrics - empty for now. + + + +```bash +$ docker build album-recommendation-random-data --tag random-data-feeder +``` + +This builds the [Random Data Feeder](https://github.com/vespa-engine/sample-apps/tree/master/examples/operations/monitoring/album-recommendation-monitoring/album-recommendation-random-data) - it generates random sets of data and puts them into the Vespa instance. Also, it repeatedly runs queries, for Grafana visualisation. Compiling the Random Data Feeder takes a few minutes. + + +```bash +$ docker run --detach --name sample-apps-random-data-feeder \ + --network monitoring \ + random-data-feeder +``` + + +Graphs will now show up in Grafana and Prometheus - it might take a minute or two. The Grafana dashboard is fully customisable. Change the default modes of Grafana and Prometheus by editing the configuration files in [album-recommendation-monitoring](https://github.com/vespa-engine/sample-apps/tree/master/examples/operations/monitoring/album-recommendation-monitoring). + + + +```bash +$ docker rm -f vespa \ + sample-apps-grafana \ + sample-apps-prometheus \ + sample-apps-random-data-feeder +``` + +```bash +$ docker network rm monitoring || true +``` + + + +## Histograms + +Metric histograms is supported for [Gauge](https://javadoc.io/doc/com.yahoo.vespa/container-disc/latest/com/yahoo/metrics/simple/Gauge.html) metrics. Create the metric like in [album-recommendation-java](https://github.com/vespa-engine/sample-apps/tree/master/album-recommendation-java), adding the histogram: + +```js +public HitCountSearcher(MetricReceiver receiver) { + this.hitCountMetric = receiver.declareGauge(EXAMPLE_METRIC_NAME, Optional.empty(), + new MetricSettings.Builder().histogram(true).build()); +} +``` + +The histograms for the last five minutes of logged data are available as CSV per dimension at [/state/v1/metrics/histograms](/en/reference/api/state-v1#state-v1-metrics-histograms). Example output: + +```bash +# start of metric hits_per_query, dimensions: { "chain": "metalchain" } +"Value","Percentile","TotalCount","1/(1-Percentile)" +1.00,0.000000000000,1,1.00 +1.00,1.000000000000,1,Infinity +# end of metric hits_per_query, dimensions: { "chain": "metalchain" } +# start of metric example_hitcounts, dimensions: { "query_language": "en" } +"Value","Percentile","TotalCount","1/(1-Percentile)" +1.00,0.000000000000,1,1.00 +1.00,1.000000000000,1,Infinity +# end of metric example_hitcounts, dimensions: { "query_language": "en" } +# start of metric query_latency, dimensions: { "chain": "metalchain" } +"Value","Percentile","TotalCount","1/(1-Percentile)" +5.69,0.000000000000,1,1.00 +5.69,1.000000000000,1,Infinity +# end of metric query_latency, dimensions: { "chain": "metalchain" } +# start of metric totalhits_per_query, dimensions: { "chain": "metalchain" } +"Value","Percentile","TotalCount","1/(1-Percentile)" +1.00,0.000000000000,1,1.00 +1.00,1.000000000000,1,Infinity +# end of metric totalhits_per_query, dimensions: { "chain": "metalchain" } +``` \ No newline at end of file diff --git a/en/operations/self-managed/multinode-systems.html b/en/operations/self-managed/multinode-systems.html deleted file mode 100644 index 3d0f9d7359..0000000000 --- a/en/operations/self-managed/multinode-systems.html +++ /dev/null @@ -1,778 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Multinode systems" -applies_to: self-managed -redirect_from: -- /en/multinode-systems.html -- /en/vespa-quick-start-multinode-aws.html -- /en/vespa-quick-start-multinode-aws-ecs.html -- /en/operations/multinode-systems.html -- /en/operations-selfhosted/multinode-systems.html ---- - -

    - A Vespa system consists of one or more stateless and stateful clusters configured by an application package. - A Vespa system is configured and managed through an admin cluster as shown below. -

    -Vespa Overview -

    - All nodes of a Vespa system have the same software installed. - Which processes are started on each node and how they are configured - is determined by the admin cluster from the specification given in - services.xml in the application package. -

    - - - -

    - Creating a multinode system from a sample application

    -

    - To create a fully functional production ready multinode system from a single-node sample application, - follow these steps (also see next steps): -

    -
      -
    1. Add an admin cluster in services.xml: -
      {% highlight xml %}
      -
      -    
      -        
      -            
      -            
      -            
      -        
      -        
      -            
      -            
      -            
      -        
      -        
      -            
      -            
      -            
      -        
      -        
      -    
      -{% endhighlight %}
      -
    2. -
    3. Install the Vespa packages or the vespaengine/vespa Docker image on all the nodes.
    4. -
    5. Run -
      -$ echo "override VESPA_CONFIGSERVERS [configserver-hostnames]" >> $VESPA_HOME/conf/vespa/default-env.txt
      -
      - where [configserver-hostnames] is replaced by the full hostname of the config server - (or a comma-separated list if multiple). -
    6. -
    7. - Add these nodes to the container and content clusters - by adding more node tags in services.xml. -
    8. -
    9. Add the same nodes to hosts.xml.
    10. -
    11. Start Vespa on the nodes
    12. -
    -

    - See below for AWS examples. - Refer to configuration server operations for troubleshooting. -

    - - - -

    AWS EC2

    -

    - The following is a procedure to set up a multinode application on AWS EC2 instances. - Please run the procedure in - multinode-HA - first, to get familiar with the different Vespa concepts before running the AWS procedure below. - This procedure will use the name number of hosts, 10, and set up the same application. -

    -{% include important.html content='Note the use of sudo. -The Vespa start scripts will modify the environment (directories, system limits), requiring root access - -refer to vespa-start-configserver -and vespa-start-services. -After the environment setup, Vespa is run as the vespa user.' %} - -{% include note.html content="The procedure below is a bare minimum, for educational purposes. -Make sure to use AWS instance types suitable for the application load, -and implement security mechanisms of choice." %} -

    - Can AWS Auto Scaling be used? - Read the autoscaling Q/A. -

    - - -

    Node setup

    -
      -
    • - Provision nodes: -
        -
      • Find AMI at CentOS AWS AMI Cloud Images - - this procedure is tested with CentOS Stream 8 us-east-1 x86_64 ami-0ee70e88eed976a1b - and vespa-8.30.50.
      • -
      • Use minimum t2.medium instances.
      • -
      • Let AWS create a security group for the nodes, or use an existing one.
      • -
      • Make sure to check for SSH traffic, for host login.
      • -
      • - Launch 10 instances - the 3 first will be Vespa config server nodes, the 7 last Vespa nodes. - Write down private / public hostnames. - The private names are used in Vespa configuration, the public names for login to check status. - To find a hostname, click the instance - and copy hostname from Private IP DNS name (IPv4 only) and Public IPv4 DNS. - Create a table like: - - - - - - - - - - - - - - - - - - -
        typePrivate IP DNS name (IPv4 only)Public IPv4 DNS
        configserverip-10-0-1-234.ec2.internalec2-3-231-33-190.compute-1.amazonaws.com
        configserverip-10-0-1-154.ec2.internalec2-3-216-28-201.compute-1.amazonaws.com
        configserverip-10-0-0-88.ec2.internalec2-34-230-33-42.compute-1.amazonaws.com
        servicesip-10-0-1-95.ec2.internalec2-44-192-98-165.compute-1.amazonaws.com
        servicesip-10-0-0-219.ec2.internalec2-3-88-143-47.compute-1.amazonaws.com
        servicesip-10-0-0-28.ec2.internalec2-107-23-52-245.compute-1.amazonaws.com
        servicesip-10-0-0-67.ec2.internalec2-54-198-251-100.compute-1.amazonaws.com
        servicesip-10-0-1-84.ec2.internalec2-44-193-84-85.compute-1.amazonaws.com
        servicesip-10-0-0-167.ec2.internalec2-54-224-15-163.compute-1.amazonaws.com
        servicesip-10-0-1-41.ec2.internalec2-44-200-227-127.compute-1.amazonaws.com
        -
      • -
      -
    • -
    • - Security group setup: -
        -
      • Click the Security Group for the nodes just provisioned (under the security tab), - then Edit inbound rules. - Add All TCP for port range 0-65535, specifying the name of the current Security Group as the Source. - This lets the hosts communicate with each other. -
      • -
      -
    • -
    • - Host login example, without ssh-agent: -
      -$ SSH_AUTH_SOCK=/dev/null ssh -i mykeypair.pem centos@ec2-3-231-33-190.compute-1.amazonaws.com
      -
      -
    • -
    • - On each of the 10 hosts, - install Vespa using the installation procedure: -
      -$ sudo dnf config-manager \
      -  --add-repo https://raw.githubusercontent.com/vespa-engine/vespa/master/dist/vespa-engine.repo
      -$ sudo dnf config-manager --enable powertools
      -$ sudo dnf install -y epel-release
      -$ sudo dnf install -y vespa
      -$ export VESPA_HOME=/opt/vespa
      -
      -
    • -
    • - On all the 10 hosts, set up the environment using the config server host list: -
      -$ echo "override VESPA_CONFIGSERVERS" \
      -  "ip-10-0-1-234.ec2.internal,ip-10-0-1-154.ec2.internal,ip-10-0-0-88.ec2.internal" \
      -  | sudo tee -a $VESPA_HOME/conf/vespa/default-env.txt
      -
      - It is required that all nodes, both config server and Vespa nodes, - have the same setting for VESPA_CONFIGSERVERS. -
    • -
    - - -

    Config server cluster setup

    -
      -
    • - Start the 3-node config server cluster: -
      -$ sudo systemctl start vespa-configserver
      -
      -
    • -
    • - Verify the config cluster is running - on one of the config server nodes: -
      -$ for configserver in \
      -  ip-10-0-1-234.ec2.internal \
      -  ip-10-0-1-154.ec2.internal \
      -  ip-10-0-0-88.ec2.internal; \
      -  do curl -s http://$configserver:19071/state/v1/health | head -5; done
      -
      -{
      -  "time" : 1660034756595,
      -  "status" : {
      -    "code" : "up"
      -  },
      -{
      -  "time" : 1660034756607,
      -  "status" : {
      -    "code" : "up"
      -  },
      -{
      -  "time" : 1660034756786,
      -  "status" : {
      -    "code" : "up"
      -  },
      -
      - A successful config server start will log an entry like: -
      -$ $VESPA_HOME/bin/vespa-logfmt | grep "Application config generation"
      -
      -  [2022-08-09 08:29:38.684] INFO    : configserver
      -  Container.com.yahoo.container.jdisc.ConfiguredApplication
      -  Switching to the latest deployed set of configurations and components.
      -  Application config generation: 0
      -
      - Do not continue setup before the config server cluster is successfully started. - See the video: - Troubleshooting startup - multinode and read - config server start sequence. -
    • -
    • - Start Vespa services on the 3 config server nodes - this starts basic Vespa services like log forwarding: -
      -$ sudo systemctl start vespa
      -
      - $VESPA_HOME/logs/vespa/vespa.log will now contain messages for - APPLICATION_NOT_LOADED, - this is normal until an application is deployed (next section). -
    • -
    - - -

    Configure application

    -
      -
    • Configure the sample application - on one of the config server nodes: -
      -$ sudo dnf install -y git zip
      -$ git clone https://github.com/vespa-engine/sample-apps.git && \
      -  cd sample-apps/examples/operations/multinode-HA
      -
      -
    • -
    • Edit hosts.xml - replace the nodeX.vespanet names. - Let the 3 first hosts be the config server hosts above, the 7 rest the Vespa hosts - example: -
      {% highlight xml %}
      -  
      -  
      -    
      -    
      -        node0
      -    
      -    
      -        node1
      -    
      -    
      -        node2
      -    
      -
      -
      -    
      -    
      -        node3
      -    
      -
      -    
      -        node4
      -    
      -    
      -        node5
      -    
      -
      -    
      -        node6
      -    
      -    
      -        node7
      -    
      -
      -    
      -        node8
      -    
      -    
      -        node9
      -    
      -
      -{% endhighlight %}
      -
    • -
    • Deploy the application: -
      -$ zip -r - . -x "img/*" "scripts/*" "pki/*" "tls/*" README.md .gitignore | \
      -  curl --header Content-Type:application/zip --data-binary @- \
      -  http://localhost:19071/application/v2/tenant/default/prepareandactivate
      -
      - Expected output: -
      {% highlight json %}
      -{
      -    "log": [],
      -    "tenant": "default",
      -    "url": "http://localhost:19071/application/v2/tenant/default/application/default/environment/prod/region/default/instance/default",
      -    "message": "Session 2 for tenant 'default' prepared and activated.",
      -    "configChangeActions": {
      -        "restart": [],
      -        "refeed": [],
      -        "reindex": []
      -    }
      -}
      -{% endhighlight %}
      -
    • -
    - - -

    Vespa nodes setup

    -
      -
    • - Start Vespa on the 7 hosts: -
      -$ sudo systemctl start vespa
      -
      -
    • -
    • - Validate the installation. - Use the - multinode-HA steps to check the health interfaces on all 10 nodes. - Note that in this guide, the ports are not mapped through a Docker container, - so the native Vespa ports should be used - e.g. for nodes 4 to 7 (see illustration below): -
      -$ curl http://localhost:8080/state/v1/health
      -
      -{
      -  "time" : 1660038306465,
      -  "status" : {
      -    "code" : "up"
      -  },
      -
      - Refer to the sample application ports: - Sample application ports -
    • -
    - - -

    Terminate instances

    -

    Remember to terminate the instances in the AWS console after use.

    - - -

    AWS EC2 singlenode

    -

    - This is a variant of the multinode install, using only one host, - running both a config server and the other Vespa services on the same node. -

    -
      -
    • - Provision a node, minimum a t2.large. - Get its hostname for use in VESPA_CONFIGSERVERS: -
      -$ hostname
      -
      -
    • -
    • - Install Vespa: -
      -$ sudo dnf config-manager \
      -  --add-repo https://raw.githubusercontent.com/vespa-engine/vespa/master/dist/vespa-engine.repo
      -$ sudo dnf config-manager --enable powertools
      -$ sudo dnf install -y epel-release
      -$ sudo dnf install -y vespa
      -$ export VESPA_HOME=/opt/vespa
      -$ echo "override VESPA_CONFIGSERVERS ip-172-31-95-248.ec2.internal" | \
      -  sudo tee -a $VESPA_HOME/conf/vespa/default-env.txt
      -
      -
    • -
    • - Get a sample application: -
      -$ sudo dnf install -y git zip
      -$ git clone https://github.com/vespa-engine/sample-apps.git && cd sample-apps/album-recommendation
      -
      -
    • -
    • - Start the config server, check health port after a few seconds: -
      -$ sudo systemctl start vespa-configserver
      -$ curl http://localhost:19071/state/v1/health | head -5
      -
      -
    • -
    • - Deploy the sample application: -
      -$ zip -r - . -x "img/*" "scripts/*" "pki/*" "tls/*" README.md .gitignore | \
      -  curl --header Content-Type:application/zip --data-binary @- \
      -  http://localhost:19071/application/v2/tenant/default/prepareandactivate
      -
      -
    • -
    • - Start Vespa, check container node health after some seconds: -
      -$ sudo systemctl start vespa
      -$ curl http://localhost:8080/state/v1/health | head -5
      -
      -
    • -
    • Remember to terminate the instances in the AWS console after use.
    • -
    - - - - -

    AWS ECS

    -

    - The following is a procedure to set up a multinode application on - AWS ECS instances. - Please run the procedure in - multinode-HA - first, to get familiar with the different Vespa concepts before running the AWS procedure below. - This procedure will use the name number of host, 10, and set up the same application. - Running the EC2 procedure above can also be helpful, - this procedure has a similar structure. -

    - - -

    Create a 10-node ECS cluster

    -
      -
    • - Log in to AWS and the EC2 Container Service. - Click Clusters > Create Cluster > EC2 Linux + Networking > Next step, using the defaults and: - - - - - - - - - -
      Cluster namevespa
      EC2 instance typet2.medium
      Number of instances10
      Key pairSelect or create your keypair
      Security group inbound rules - port range0 - 65535
      -
    • -
    • - Click Create, wait for the tasks to succeed, then View Cluster - - it should say Registered container instances: 10 in ACTIVE state. -
    • -
    - - -

    Configure ECS instances

    -
      -
    • Click the ECS Instances tab - this should list 10 container instances.
    • -
    • Select the 3 first Container Instance checkboxes, - then Actions > View/Edit attributes.
    • -
    • Click Add attribute. - Set Name=type and Value=configserver, - click the green checkbox on the right, then Close.
    • -
    • Select the next 7 Container instance checkboxes, - then Actions > View/Edit attributes.
    • -
    • Click Add attribute. - Set Name=type and Value=services, - click the green checkbox on the right, then Close.
    • -
    • - Write down private / public hostnames and create a table like in the EC2 procedure - The private names are used in Vespa configuration, the public names for login to check status. - To find a hostname, click ECS Instance > Instance ID - and copy hostname from Private IP DNS name (IPv4 only) and Public IPv4 DNS. -
    • -
    - - -

    Start the config server task

    -
      -
    • Click Task Definitions > Create new Task Definition > EC2 > Next step.
    • -
    • Click Configure via JSON and replace the content with - (note the comma-separated hostnames of the config servers addresses): -
      -{
      -    "networkMode": "host",
      -    "containerDefinitions": [
      -        {
      -            "name": "configserver",
      -            "environment": [
      -                {
      -                    "name": "VESPA_CONFIGSERVERS",
      -                    "value": "ip-10-0-1-234.ec2.internal,ip-10-0-1-154.ec2.internal,ip-10-0-0-88.ec2.internal"
      -                }
      -            ],
      -            "image": "vespaengine/vespa",
      -            "privileged": true,
      -            "memoryReservation": 1024
      -        }
      -    ],
      -    "placementConstraints": [
      -        {
      -            "expression": "attribute:type == configserver",
      -            "type": "memberOf"
      -        }
      -    ],
      -    "family": "configserver"
      -}
      -
      -
    • -
    • Click Save > Create.
    • -
    • Choose Actions -> Run task and configure: - - - - - - - -
      Launch typeEC2
      Clustervespa
      Number of tasks3
      Placement templatesOne Task Per Host
      -
    • -
    • Click Run Task.
    • -
    • Validate that the config servers started successfully - - use the same procedure as for EC2 instances, - checking /state/v1/health. - Do not continue before successfully validating this: -
      -$ ssh -i mykeypair.pem ec2-user@ec2-3-231-33-190.compute-1.amazonaws.com \
      -  curl -s http://localhost:19071/state/v1/health | head -5
      -
      -{
      -    "time" : 1660635645783,
      -    "status" : {
      -      "code" : "up"
      -    },
      -
      -
    • -
    - - -

    Configure application - ECS

    -
      -
    • Log into a config server: -
      -$ ssh -i mykeypair.pem ec2-user@ec2-3-231-33-190.compute-1.amazonaws.com
      -
      -
    • -
    • Download the multinode-HA sample application: -
      -$ sudo yum -y install git zip
      -$ git clone https://github.com/vespa-engine/sample-apps.git && \
      -  cd sample-apps/examples/operations/multinode-HA
      -
      -
    • -
    • - Modify hosts.xml using the internal DNS hostnames - - this step is the same as for EC2 instances
    • -
    • Deploy the application: -
      -$ zip -r - . -x "img/*" "scripts/*" "pki/*" "tls/*" README.md .gitignore | \
      -  curl --header Content-Type:application/zip --data-binary @- \
      -  http://localhost:19071/application/v2/tenant/default/prepareandactivate
      -
      -
    • -
    - - -

    Start the services tasks

    -
      -
    • Click Task Definitions > Create new Task Definition > EC2 > Next step.
    • -
    • - Click Configure via JSON and replace the content with - (using the same 3 config server internal DNS names): -
      -{
      -    "networkMode": "host",
      -    "containerDefinitions": [
      -        {
      -            "name": "services",
      -            "environment": [
      -                {
      -                    "name": "VESPA_CONFIGSERVERS",
      -                    "value": "ip-10-0-1-234.ec2.internal,ip-10-0-1-154.ec2.internal,ip-10-0-0-88.ec2.internal"
      -                }
      -            ],
      -            "image": "vespaengine/vespa",
      -            "command": [
      -                "services"
      -            ],
      -            "privileged": true,
      -            "memoryReservation": 1024
      -        }
      -    ],
      -    "placementConstraints": [
      -        {
      -            "expression": "attribute:type == services",
      -            "type": "memberOf"
      -        }
      -    ],
      -    "family": "services"
      -}
      -
      -
    • -
    • - Click Save > Create. - Note the "command": [ "services" ]. - See controlling which services to start - for details, this starts services only - - the start script starts both the configserver and services if given no arguments - - this is used for the config server above. - For these 7 nodes, services is given as an argument to the start script to only start Vespa services. -
    • -
    • Choose Actions > Run task and configure: - - - - - - - -
      Launch typeEC2
      Clustervespa
      Number of tasks7
      Placement templatesOne Task Per Host
      -
    • -
    • Click Run Task.
    • -
    • - Validate startup. - This step is the same as for EC2 instances, - e.g. for nodes running a Vespa container the port is 8080: -
      -$ ssh -i mykeypair.pem ec2-user@ec2-3-88-143-47.compute-1.amazonaws.com \
      -  curl -s http://localhost:8080/state/v1/health | head -5
      -
      -{
      -    "time" : 1660652648442,
      -    "status" : {
      -      "code" : "up"
      -    },
      -
      -
    • -
    - - -

    Terminate cluster

    -
      -
    • Remember to delete the cluster in the AWS console after use.
    • -
    - -

    Log collection

    -

    - Logs are automatically collected from all nodes in real time to the admin node listed as adminserver. - To view log messages from the system, - run vespa-logfmt on this node. -

    - - - -

    Making changes to live systems

    -

    - To change the system, deploy the changed application to the admin cluster. - The admin cluster will automatically change the participating nodes as necessary. - It is safe to do this while serving live query and write traffic. - In some cases the admin cluster will report that some processes must be restarted to make the change effective. - To avoid query or write traffic disruption, - such restarts must be done on one node at the time, - waiting until the node is fully up before restarting the next one. -

    - - - -

    Multiple proton processes

    -

    - A content cluster can have multiple schemas. - There is another way to distribute load over hosts, - by mapping multiple content clusters to the same hosts: -

    -
    {% highlight xml %}
    -
    -    
    -        
    -    
    -    
    -        
    -    
    -
    -
    -
    -    
    -        
    -    
    -    
    -        
    -    
    -
    -{% endhighlight %}
    -

    - Observe that both clusters use node1. - This is a non-recommended configuration, as it runs multiple proton processes per node. - To reduce interference between the processes in this case, virtualize the host into more nodes. - One can use containers or VMs to do this: -

    -Multiple proton processes per node -{% include important.html content='Vespa\'s features for overload handling, -like feed-block, -requires that only one proton process is running on the node.' %} - - - -

    Autoscaling

    -

    - A common question is, "Can AWS Auto Scaling be used?" - That is a difficult question to answer, here is a transcript from the Vespa Slack: -

    -
    - I have a question about deployment. - I set up cluster on two AWS auto-scaling groups (config & services) based on - multinode-systems.html#aws-ec2. - But if one of instances was replaced by auto-scaling group, - I need manually update hosts.xml file, zip it and deploy new version of the app. - I'm thinking about automation of this process by Cloudwatch & Lambda... - I wonder if there is some node-discovery mechanism which can e.g. - check instances tags and update hosts config based on it? -
    -

    - First, you see in aws-ec2 that there are two types of hosts, - configserver and services. - configserver setup / operations is documented at - configuration server operations. - This must be set up first. - This is backed by an Apache ZooKeeper cluster, - so should be 1 or 3 nodes large. - In our own clusters in Yahoo, we do not autoscale configserver clusters, there is no need - we use 3. - If that is too many, use 1. So this question is easy - do not autoscale configservers. -

    -

    - For the services nodes, observe that there are two kinds of nodes - - stateless containers and stateful content nodes - - see the overview. - In any way, you will want to manage these differently - - the stateless nodes are more easily replaced / increased / shrunk, - by changing services.xml and hosts.xml. - It is doable to build an autoscaling service for the stateless nodes, - but you need to make sure to use the right metrics for your autoscaling code, - and integrate the deploy-automation with the other deployments (say schema modifications). -

    -

    - A much harder problem is autoscaling the stateful nodes - - these are the nodes with the indexes and data. See elasticity - - adding a node + data redistribution can take hours, - and the node's load will increase during redistribution. - Building autoscaling here is very difficult to do safely and efficient. -

    -

    - Nothing of this is impossible, - and it is actually implemented at cloud.vespa.ai/autoscaling - - but it is a difficult feature to get right. -

    -

    - So, my recommendation is starting with a static set of hosts, like in - multinode-HA - - and in parallel try out cloud.vespa.ai/en/free-trial - with autoscaling experiments using your data and use cases. -

    -

    - Autoscaling can save money, but before going there, - it is wise to read about performance - and optimize resources using a static node set - (or use the sizing suggestions from the Vespa Cloud Console). - I.e., get the node resources right first, - then consider if autoscaling node count for your load patterns makes sense. -

    - - - -

    Next steps

    -
      -
    • - Multinode-HA - is a high-availability multi-node template - use this as a basis for the final configuration. -
    • -
    • - The multinode - sample application is a useful for experimenting with node state transitions. -
    • -
    diff --git a/en/operations/self-managed/multinode-systems.mdx b/en/operations/self-managed/multinode-systems.mdx new file mode 100644 index 0000000000..7dd6593d76 --- /dev/null +++ b/en/operations/self-managed/multinode-systems.mdx @@ -0,0 +1,638 @@ +--- +title: "Multinode systems" +--- + +A Vespa *system* consists of one or more stateless and stateful clusters configured by an application package. A Vespa system is configured and managed through an admin cluster as shown below. + + +![Vespa Overview](/assets/img/vespa-overview.svg) + + +All nodes of a Vespa system have the same software installed. Which processes are started on each node and how they are configured is determined by the admin cluster from the specification given in [services.xml](/en/reference/applications/services/services) in the application package. + +## Creating a multinode system from a sample application + +To create a fully functional production ready multinode system from a single-node sample application, follow these steps (also see [next steps](#next-steps)): + + + +Add an [admin cluster](/en/reference/applications/services/admin) in services.xml: + ```xml + + + + + + + + + + + + + + + + + + + + ``` + + +Install the Vespa packages or the *vespaengine/vespa* Docker image on all the nodes. + + +Run + +```bash +$ echo "override VESPA_CONFIGSERVERS [configserver-hostnames]" >> $VESPA_HOME/conf/vespa/default-env.txt +``` +where [`configserver-hostnames`] is replaced by the full hostname of the config server (or a comma-separated list if multiple). + + +Add these nodes to the container and content clusters by adding more `node` tags in *services.xml*. + + +Add the same nodes to *hosts.xml*. + + +Start Vespa on the nodes + + + +See below for AWS examples. Refer to [configuration server operations](/en/operations/self-managed/configuration-server) for troubleshooting. + +## AWS EC2 + +The following is a procedure to set up a multinode application on *AWS EC2* instances. Please run the procedure in [multinode-HA](https://github.com/vespa-engine/sample-apps/tree/master/examples/operations/multinode-HA) first, to get familiar with the different Vespa concepts before running the AWS procedure below. This procedure will use the name number of hosts, 10, and set up the same application. + + +**Important:** + +Note the use of `sudo`. The Vespa start scripts will modify the environment (directories, system limits), requiring root access - refer to [vespa-start-configserver](/en/reference/operations/self-managed/tools#vespa-start-configserver) and [vespa-start-services](/en/reference/operations/self-managed/tools#vespa-start-services). After the environment setup, Vespa is run as the `vespa` user. + + + +**Note:** + +The procedure below is a bare minimum, for educational purposes. Make sure to use AWS instance types suitable for the application load, and implement security mechanisms of choice. + + +Can [AWS Auto Scaling](https://aws.amazon.com/autoscaling/) be used? Read the [autoscaling Q/A](#autoscaling). + +### Node setup + +- Provision nodes: + - Find AMI at [CentOS AWS AMI Cloud Images](https://centos.org/download/aws-images/) - this procedure is tested with *CentOS Stream 8 us-east-1 x86\_64 ami-0ee70e88eed976a1b* and vespa-8.30.50. + - Use minimum *t2.medium* instances. + - Let AWS create a security group for the nodes, or use an existing one. + - Make sure to check for SSH traffic, for host login. + - Launch 10 instances - the 3 first will be Vespa config server nodes, the 7 last Vespa nodes. Write down private / public hostnames. The private names are used in Vespa configuration, the public names for login to check status. To find a hostname, click the instance and copy hostname from *Private IP DNS name (IPv4 only)* and *Public IPv4 DNS*. Create a table like: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    typePrivate IP DNS name (IPv4 only)Public IPv4 DNS
    configserverip-10-0-1-234.ec2.internalec2-3-231-33-190.compute-1.amazonaws.com
    configserverip-10-0-1-154.ec2.internalec2-3-216-28-201.compute-1.amazonaws.com
    configserverip-10-0-0-88.ec2.internalec2-34-230-33-42.compute-1.amazonaws.com
    servicesip-10-0-1-95.ec2.internalec2-44-192-98-165.compute-1.amazonaws.com
    servicesip-10-0-0-219.ec2.internalec2-3-88-143-47.compute-1.amazonaws.com
    servicesip-10-0-0-28.ec2.internalec2-107-23-52-245.compute-1.amazonaws.com
    servicesip-10-0-0-67.ec2.internalec2-54-198-251-100.compute-1.amazonaws.com
    servicesip-10-0-1-84.ec2.internalec2-44-193-84-85.compute-1.amazonaws.com
    servicesip-10-0-0-167.ec2.internalec2-54-224-15-163.compute-1.amazonaws.com
    servicesip-10-0-1-41.ec2.internalec2-44-200-227-127.compute-1.amazonaws.com
    +- Security group setup: + - Click the Security Group for the nodes just provisioned (under the security tab), then *Edit inbound rules*. Add *All TCP* for port range 0-65535, specifying the name of the current Security Group as the Source. This lets the hosts communicate with each other. +- Host login example, without ssh-agent: + ```bash + $ SSH_AUTH_SOCK=/dev/null ssh -i mykeypair.pem centos@ec2-3-231-33-190.compute-1.amazonaws.com + ``` +- On each of the 10 hosts, install Vespa using the [installation procedure](/en/operations/self-managed/build-install#rpms):$ sudo dnf config-manager \\ + ```bash + $ sudo dnf config-manager \ + --add-repo https://raw.githubusercontent.com/vespa-engine/vespa/master/dist/vespa-engine.repo + $ sudo dnf config-manager --enable powertools + $ sudo dnf install -y epel-release + $ sudo dnf install -y vespa + $ export VESPA_HOME=/opt/vespa + ``` +- On all the 10 hosts, set up the environment using the config server host list: + ```bash + $ echo "override VESPA_CONFIGSERVERS" \ + "ip-10-0-1-234.ec2.internal,ip-10-0-1-154.ec2.internal,ip-10-0-0-88.ec2.internal" \ + | sudo tee -a $VESPA_HOME/conf/vespa/default-env.txt + ``` + It is required that all nodes, both config server and Vespa nodes, have the same setting for `VESPA_CONFIGSERVERS`. + +### Config server cluster setup + +- Start the 3-node config server cluster: + ```bash + $ sudo systemctl start vespa-configserver + ``` +- Verify the config cluster is running - on one of the config server nodes +```bash +$ for configserver in \ + ip-10-0-1-234.ec2.internal \ + ip-10-0-1-154.ec2.internal \ + ip-10-0-0-88.ec2.internal; \ + do curl -s http://$configserver:19071/state/v1/health | head -5; done + +{ + "time" : 1660034756595, + "status" : { + "code" : "up" + }, +{ + "time" : 1660034756607, + "status" : { + "code" : "up" + }, +{ + "time" : 1660034756786, + "status" : { + "code" : "up" + }, +``` + A successful config server start will log an entry like: + + ```bash + $ $VESPA_HOME/bin/vespa-logfmt | grep "Application config generation" + + [2022-08-09 08:29:38.684] INFO : configserver + Container.com.yahoo.container.jdisc.ConfiguredApplication + Switching to the latest deployed set of configurations and components. + Application config generation: 0 + ``` + Do not continue setup before the config server cluster is successfully started. See the video: [Troubleshooting startup - multinode](https://www.youtube.com/embed/BG7XZmXpIzo) and read [config server start sequence](/en/operations/self-managed/configuration-server#start-sequence). +- Start Vespa services on the 3 config server nodes - this starts basic Vespa services like log forwarding: + + ```bash + $ sudo systemctl start vespa + ``` + *`$VESPA_HOME/logs/vespa/vespa.log`* will now contain messages for `APPLICATION_NOT_LOADED`, this is normal until an application is deployed (next section). + +### Configure application + +- Configure the sample application - on one of the config server nodes: + ```bash + $ sudo dnf install -y git zip + $ git clone https://github.com/vespa-engine/sample-apps.git && \ + cd sample-apps/examples/operations/multinode-HA + ``` +- Edit *hosts.xml* - replace the *nodeX.vespanet* names. Let the 3 first hosts be the config server hosts above, the 7 rest the Vespa hosts - example: + ```xml expandable + + + + + node0 + + + node1 + + + node2 + + + + node3 + + + node4 + + + node5 + + + node6 + + + node7 + + + node8 + + + node9 + + + ``` +- Deploy the application: + + ```bash + $ zip -r - . -x "img/*" "scripts/*" "pki/*" "tls/*" README.md .gitignore | \ + curl --header Content-Type:application/zip --data-binary @- \ + http://localhost:19071/application/v2/tenant/default/prepareandactivate + ``` + Expected output: + + ```json + { + "log": [], + "tenant": "default", + "url": "http://localhost:19071/application/v2/tenant/default/application/default/environment/prod/region/default/instance/default", + "message": "Session 2 for tenant 'default' prepared and activated.", + "configChangeActions": { + "restart": [], + "refeed": [], + "reindex": [] + } + } + ``` + +### Vespa nodes setup + +- Start Vespa on the 7 hosts: + ```bash + $ sudo systemctl start vespa + ``` +- Validate the installation. Use the [multinode-HA](https://github.com/vespa-engine/sample-apps/tree/master/examples/operations/multinode-HA) steps to check the health interfaces on all 10 nodes. Note that in this guide, the ports are not mapped through a Docker container, so the native Vespa ports should be used - e.g. for nodes 4 to 7 (see illustration below):$ curl http://localhost:8080/state/v1/health + + ```js + $ curl http://localhost:8080/state/v1/health + + { + "time" : 1660038306465, + "status" : { + "code" : "up" + }, + ``` + Refer to the sample application ports: + + ![Sample application ports](https://raw.githubusercontent.com/vespa-engine/sample-apps/master/examples/operations/multinode-HA/img/multinode-HA.svg) + + +### Terminate instances + +Remember to terminate the instances in the AWS console after use. + +### AWS EC2 singlenode + +This is a variant of the multinode install, using only one host, running both a config server and the other Vespa services on the same node. + +- Provision a node, minimum a *t2.large*. Get its hostname for use in `VESPA_CONFIGSERVERS`: + ```bash + $ hostname + ``` +- Install Vespa: + ```bash + $ sudo dnf config-manager \ + --add-repo https://raw.githubusercontent.com/vespa-engine/vespa/master/dist/vespa-engine.repo + $ sudo dnf config-manager --enable powertools + $ sudo dnf install -y epel-release + $ sudo dnf install -y vespa + $ export VESPA_HOME=/opt/vespa + $ echo "override VESPA_CONFIGSERVERS ip-172-31-95-248.ec2.internal" | \ + sudo tee -a $VESPA_HOME/conf/vespa/default-env.txt + ``` +- Get a sample application: + ```bash + $ sudo dnf install -y git zip + $ git clone https://github.com/vespa-engine/sample-apps.git && cd sample-apps/album-recommendation + ``` +- Start the config server, check health port after a few seconds: + ```bash + $ sudo systemctl start vespa-configserver + $ curl http://localhost:19071/state/v1/health | head -5 + ``` +- Deploy the sample application + ```bash + $ zip -r - . -x "img/*" "scripts/*" "pki/*" "tls/*" README.md .gitignore | \ + curl --header Content-Type:application/zip --data-binary @- \ + http://localhost:19071/application/v2/tenant/default/prepareandactivate + ``` +- Start Vespa, check container node health after some seconds: + ```bash + $ sudo systemctl start vespa + $ curl http://localhost:8080/state/v1/health | head -5 + ``` +- Remember to terminate the instances in the AWS console after use. + +## AWS ECS + +The following is a procedure to set up a multinode application on [AWS ECS](https://us-east-1.console.aws.amazon.com/ecs) instances. Please run the procedure in [multinode-HA](https://github.com/vespa-engine/sample-apps/tree/master/examples/operations/multinode-HA) first, to get familiar with the different Vespa concepts before running the AWS procedure below. This procedure will use the name number of host, 10, and set up the same application. Running the [EC2 procedure](#aws-ec2) above can also be helpful, this procedure has a similar structure. + +### Create a 10-node ECS cluster + +- Log in to AWS and the EC2 Container Service. Click *Clusters > Create Cluster > EC2 Linux + Networking > Next step*, using the defaults and: + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Cluster namevespa
    EC2 instance typet2.medium
    Number of instances10
    Key pair*Select or create your keypair*
    Security group inbound rules - port range0 - 65535
    + +- Click *Create*, wait for the tasks to succeed, then *View Cluster* - it should say *Registered container instances: 10* in ACTIVE state. + +### Configure ECS instances + +- Click the *ECS Instances tab* - this should list 10 container instances. +- Select the 3 first Container Instance checkboxes, then *Actions `>` View/Edit attributes*. +- Click *Add attribute*. Set `Name=type` and `Value=configserver`, click the green checkbox on the right, then *Close*. +- Select the next 7 Container instance checkboxes, then *Actions `>` View/Edit attributes*. +- Click *Add attribute*. Set `Name=type` and `Value=services`, click the green checkbox on the right, then *Close*. +- Write down private / public hostnames and create a table like in the [EC2 procedure](#node-setup) The private names are used in Vespa configuration, the public names for login to check status. To find a hostname, click *ECS Instance > Instance ID* and copy hostname from *Private IP DNS name (IPv4 only)* and *Public IPv4 DNS*. + +### Start the config server task + +- Click *Task Definitions `>` Create new Task Definition `>` EC2 `>` Next step*. +- Click *Configure via JSON* and replace the content with (note the comma-separated hostnames of the config servers addresses): +```json expandable +{ + "networkMode": "host", + "containerDefinitions": [ + { + "name": "configserver", + "environment": [ + { + "name": "VESPA_CONFIGSERVERS", + "value": "ip-10-0-1-234.ec2.internal,ip-10-0-1-154.ec2.internal,ip-10-0-0-88.ec2.internal" + } + ], + "image": "vespaengine/vespa", + "privileged": true, + "memoryReservation": 1024 + } + ], + "placementConstraints": [ + { + "expression": "attribute:type == configserver", + "type": "memberOf" + } + ], + "family": "configserver" +} +``` +- Click *Save `>` Create*. +- Choose *Actions `->` Run task* and configure: + + + + + + + + + + + + + + + + + + + + + + +
    Launch typeEC2
    Clustervespa
    Number of tasks3
    Placement templatesOne Task Per Host
    + +- Click *Run Task*. +- Validate that the config servers started successfully - use the same procedure as for [EC2 instances](#config-server-cluster-setup), checking */state/v1/health*. Do not continue before successfully validating this: + ```bash + $ ssh -i mykeypair.pem ec2-user@ec2-3-231-33-190.compute-1.amazonaws.com \ + curl -s http://localhost:19071/state/v1/health | head -5 + + { + "time" : 1660635645783, + "status" : { + "code" : "up" + }, + ``` + +### Configure application - ECS + +- Log into a config server: + ```bash + $ ssh -i mykeypair.pem ec2-user@ec2-3-231-33-190.compute-1.amazonaws.com + ``` +- Download the multinode-HA sample application: + ```bash + $ sudo yum -y install git zip + $ git clone https://github.com/vespa-engine/sample-apps.git && \ + cd sample-apps/examples/operations/multinode-HA + ``` +- Modify *hosts.xml* using the internal DNS hostnames - this step is the same as for [EC2 instances](#configure-application) +- Deploy the application: + ```bash + $ zip -r - . -x "img/*" "scripts/*" "pki/*" "tls/*" README.md .gitignore | \ + curl --header Content-Type:application/zip --data-binary @- \ + http://localhost:19071/application/v2/tenant/default/prepareandactivate + ``` + +### Start the services tasks + +- Click *Task Definitions `>` Create new Task Definition `>` EC2 `>` Next step*. +- Click *Configure via JSON* and replace the content with (using the same 3 config server internal DNS names): +```json expandable +{ + "networkMode": "host", + "containerDefinitions": [ + { + "name": "services", + "environment": [ + { + "name": "VESPA_CONFIGSERVERS", + "value": "ip-10-0-1-234.ec2.internal,ip-10-0-1-154.ec2.internal,ip-10-0-0-88.ec2.internal" + } + ], + "image": "vespaengine/vespa", + "command": [ + "services" + ], + "privileged": true, + "memoryReservation": 1024 + } + ], + "placementConstraints": [ + { + "expression": "attribute:type == services", + "type": "memberOf" + } + ], + "family": "services" +} +``` +- Click *Save `>` Create*. Note the `"command": [ "services" ]`. See [controlling which services to start](/en/operations/self-managed/docker-containers#controlling-which-services-to-start) for details, this starts *services* only - the start script starts both the *configserver* and *services* if given no arguments - this is used for the config server above. For these 7 nodes, `services` is given as an argument to the start script to only start Vespa services. +- Choose *Actions `>` Run task* and configure: + + + + + + + + + + + + + + + + + + + + + + +
    Launch typeEC2
    Clustervespa
    Number of tasks7
    Placement templatesOne Task Per Host
    + +- Click *Run Task*. +- Validate startup. This step is the same as for [EC2 instances](#vespa-nodes-setup), e.g. for nodes running a Vespa container the port is 8080: + ```bash + $ ssh -i mykeypair.pem ec2-user@ec2-3-88-143-47.compute-1.amazonaws.com \ + curl -s http://localhost:8080/state/v1/health | head -5 + + { + "time" : 1660652648442, + "status" : { + "code" : "up" + }, + ``` + +### Terminate cluster + +- Remember to delete the cluster in the AWS console after use. + +## Log collection + +Logs are automatically collected from all nodes in real time to the admin node listed as `adminserver`. To view log messages from the system, run [vespa-logfmt](/en/reference/operations/self-managed/tools#vespa-logfmt) on this node. + +## Making changes to live systems + +To change the system, deploy the changed application to the admin cluster. The admin cluster will automatically change the participating nodes as necessary. It is safe to do this while serving live query and write traffic. In some cases the admin cluster will report that some processes must be restarted to make the change effective. To avoid query or write traffic disruption, such restarts must be done on one node at the time, waiting until the node is fully up before restarting the next one. + +## Multiple proton processes + +A content cluster can have multiple schemas. There is another way to distribute load over hosts, by mapping multiple content clusters to the same hosts: + +```xml + + + + + + + + + + + + + + + + + +``` + +Observe that both clusters use `node1`. This is a non-recommended configuration, as it runs multiple [proton](/en/content/proton) processes per node. To reduce interference between the processes in this case, virtualize the host into more nodes. One can use [containers or VMs](/en/operations/self-managed/docker-containers) to do this: + + +![Multiple proton processes per node](/assets/img/schemas-and-content-clusters-multiple-proton.svg) + + + +**Important:** + +Vespa's features for overload handling, like [feed-block](/en/writing/feed-block), requires that only one proton process is running on the node. + + +## Autoscaling + +A common question is, *"Can [AWS Auto Scaling](https://aws.amazon.com/autoscaling/) be used?"* That is a difficult question to answer, here is a transcript from the [Vespa Slack](https://slack.vespa.ai): + +> *I have a question about deployment. I set up cluster on two AWS auto-scaling groups (config & services) based on [multinode-systems.html#aws-ec2](#aws-ec2). But if one of instances was replaced by auto-scaling group, I need manually update hosts.xml file, zip it and deploy new version of the app. I'm thinking about automation of this process by Cloudwatch & Lambda... I wonder if there is some node-discovery mechanism which can e.g. check instances tags and update hosts config based on it?* + +First, you see in [aws-ec2](#aws-ec2) that there are two types of hosts, `configserver` and `services`. configserver setup / operations is documented at [configuration server operations](/en/operations/self-managed/configuration-server). This must be set up first. This is backed by an [Apache ZooKeeper](https://zookeeper.apache.org/) cluster, so should be 1 or 3 nodes large. In our own clusters in Yahoo, we do not autoscale configserver clusters, there is no need - we use 3. If that is too many, use 1. So this question is easy - do not autoscale configservers. + +For the services nodes, observe that there are two kinds of nodes - stateless containers and stateful content nodes - see the [overview](/en/learn/overview). In any way, you will want to manage these differently - the stateless nodes are more easily replaced / increased / shrunk, by changing *services.xml* and *hosts.xml*. It is doable to build an autoscaling service for the stateless nodes, but you need to make sure to use the right metrics for your autoscaling code, and integrate the deploy-automation with the other deployments (say schema modifications). + +A much harder problem is autoscaling the stateful nodes - these are the nodes with the indexes and data. See [elasticity](/en/content/elasticity) - adding a node + data redistribution can take hours, and the node's load will increase during redistribution. Building autoscaling here is very difficult to do safely and efficient. + +Nothing of this is impossible, and it is actually implemented at [cloud.vespa.ai/autoscaling](/#autoscaling) - but it is a difficult feature to get right. + +So, my recommendation is starting with a static set of hosts, like in [multinode-HA](https://github.com/vespa-engine/sample-apps/tree/master/examples/operations/multinode-HA) - and in parallel try out [cloud.vespa.ai/en/free-trial](https://vespa.ai/free-trial/) with autoscaling experiments using your data and use cases. + +Autoscaling can save money, but before going there, it is wise to read about [performance](/en/performance/) and optimize resources using a static node set (or use the sizing suggestions from the Vespa Cloud Console). I.e., get the node resources right first, then consider if autoscaling node count for your load patterns makes sense. + +## Next steps + +- [Multinode-HA](https://github.com/vespa-engine/sample-apps/tree/master/examples/operations/multinode-HA) is a high-availability multi-node template - use this as a basis for the final configuration. +- The [multinode](https://github.com/vespa-engine/sample-apps/tree/master/examples/operations/multinode) sample application is a useful for experimenting with node state transitions. \ No newline at end of file diff --git a/en/operations/self-managed/node-setup.html b/en/operations/self-managed/node-setup.html deleted file mode 100644 index b8612b1b61..0000000000 --- a/en/operations/self-managed/node-setup.html +++ /dev/null @@ -1,134 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Node and network setup" -applies_to: self-managed -redirect_from: -- /en/node-setup.html -- /en/operations/node-setup.html -- /en/operations-selfhosted/node-setup.html ---- - -

    - Vespa is composed of services that communicate and interact with each other. - These services can be partitioned onto any amount of actual hardware for scaling, - or they can all coexist on a single environment for development. - To achieve this flexibility, - some requirements must be met for the environment where the services will run. -

    - - - -

    Node

    -

    - A node in this context is the environment where some Vespa services are running. - This can be an actual machine like a server in a datacenter, - or a laptop for development and testing of Vespa configuration. - It can also be a Virtual Machine or a Docker container, - so one can run multiple nodes on a single piece of hardware. -

    -

    - The different Vespa services that run on nodes will mostly communicate with each other via the network. - This means that all nodes must have an IP address and have network connectivity to all other nodes. - Both IPv4 and IPv6 protocols are supported. - Note that the same framework is used even when running the entire Vespa stack on a single node. -

    - - - -

    Memory settings

    -

    - In the getting started guides - and sample applications, - memory settings are always the minimum to run the guides. - This to make it easy to set up and explore Vespa features. - The - multinode-HA application has examples for even tighter settings, - just to be able to test a larger application on a small host. -

    -

    - It is important to note that these are - not the recommended Vespa memory settings. - Finding the optimal node configuration is covered in the performance guides. - There are many ways to configure, feed and use Vespa, it is not possible to have a general, recommended setting. -

    -{% include important.html content='Vespa is a multiprocess application, -and can be configured to run multiple services per node - see config sentinel. -Out-of-memory can cause a range of problems hard to diagnose - -the Vespa team recommends testing with larger nodes in these cases.' %} -

    - As a rule of thumb, start with an 8 GB node, - just to make sure the application is functionally correct - then optimize later. -

    -

    - Review system defaults. - A common issue is too low default for vm.max_map_count - which makes apps run into memory mapping assertions, - especially if the paged option - has been enabled for a lot of attribute fields - - symptoms of this can be like: -

    -
    -vespalib::alloc::MemoryAllocator::PtrAndSize vespalib::alloc::MmapFileAllocator::alloc(size_t) const: Assertion buf!=MAP_FAILED
    -
    -
    -'terminate called after throwing an instance of 'std::runtime_error' 'what(): mmap of file '/opt/vespa/var/db/vespa/search/cluster.abc/n12/documents/xyz/0.ready/index/index.fusion.43/field123/boolocc.bdat' with flags '1' failed with error: 'Cannot allocate memory'
    -
    - - - -

    Hostname

    -

    - When Vespa services are started on a node, the node must identify - itself to the configuration system to get configuration (including which services to run). - This requires a unique identifier for the node in the config server. - Since it is already a requirement that the node has a hostname that the config server knows, - Vespa uses the same hostname when a node identifies itself to get its configuration. - See config sentinel for details. -

    -

    - In order to find the IP address of a node and connect to it, - the node must have a hostname that identifies it and which maps to its IP address. - Actual machines on a network will usually have a Fully Qualified Domain Name (FQDN) in DNS, - which should be used as the host name for this purpose. -

    -

    - Note that it is a requirement that the host name, - configured in hosts.xml, - can be used to look up the IP address of the node (see workaround using VESPA_HOSTNAME below). - The configuration server use this host name to create URLs - to be used to open network connections to Vespa services running on that node. - If the nodes use IP addresses which don't have DNS names, one must have all - those IP addresses with corresponding host names in - /etc/hosts on all nodes in the Vespa installation. - We recommend using names that can be used as FQDNs also in this case, - in case of moving to using a DNS server instead of publishing /etc/hosts. -

    -

    - This means that the node must know its own hostname (FQDN), - and be in agreement with the config server about what exactly the host name is. - Usually this is achieved by just running the hostname command. - If hostname is set to the FQDN of the node, then everything should Just Work. -

    -

    - As an alternative to modifying /etc/hosts, - set VESPA_HOSTNAME - on the hosts. - vespa-ip-vs-hostname - is a great post on how to do this. -

    - - - -

    Simple single-node development environment

    -

    - When testing a Vespa configuration on a single-node setup, - one can usually avoid the setup hassle by overriding the hostname with the value "localhost". - Try this command for that purpose: -

    -
    -$ echo "override VESPA_HOSTNAME localhost" >> $VESPA_HOME/conf/vespa/default-env.txt
    -
    -

    - Running Java unit tests won't pick up settings in default-env.txt - and will default to "localhost" if VESPA_HOSTNAME isn't set in the environment. -

    diff --git a/en/operations/self-managed/node-setup.mdx b/en/operations/self-managed/node-setup.mdx new file mode 100644 index 0000000000..c2dce5ba20 --- /dev/null +++ b/en/operations/self-managed/node-setup.mdx @@ -0,0 +1,58 @@ +--- +title: "Node and network setup" +sidebarTitle: "Node Setup" +--- + +Vespa is composed of services that communicate and interact with each other. These services can be partitioned onto any amount of actual hardware for scaling, or they can all coexist on a single environment for development. To achieve this flexibility, some requirements must be met for the environment where the services will run. + +## Node + +A *node* in this context is the environment where some Vespa services are running. This can be an actual machine like a server in a datacenter, or a laptop for development and testing of Vespa configuration. It can also be a Virtual Machine or a Docker container, so one can run multiple nodes on a single piece of hardware. + +The different Vespa services that run on nodes will mostly communicate with each other via the network. This means that all nodes must have an IP address and have network connectivity to all other nodes. Both IPv4 and IPv6 protocols are supported. Note that the same framework is used even when running the entire Vespa stack on a single node. + +## Memory settings + +In the [getting started guides](/en/basics/deploy-an-application-local) and [sample applications](https://github.com/vespa-engine/sample-apps), memory settings are always the minimum to run the guides. This to make it easy to set up and explore Vespa features. The [multinode-HA](https://github.com/vespa-engine/sample-apps/tree/master/examples/operations/multinode-HA) application has examples for even tighter settings, just to be able to test a larger application on a small host. + +It is important to note that these are not the recommended Vespa memory settings. Finding the optimal node configuration is covered in the [performance guides](/en/performance/index). There are many ways to configure, feed and use Vespa, it is not possible to have a general, recommended setting. + + +**Important:** + +Vespa is a multiprocess application, and can be configured to run multiple services per node - see [config sentinel](/en/operations/self-managed/config-sentinel). Out-of-memory can cause a range of problems hard to diagnose - the Vespa team recommends testing with larger nodes in these cases. + + +As a rule of thumb, start with an 8 GB node, just to make sure the application is functionally correct - then optimize later. + +Review system defaults. A common issue is too low default for `vm.max_map_count` which makes apps run into memory mapping assertions, especially if the [paged](/en/content/attributes#paged-attributes) option has been enabled for a lot of attribute fields - symptoms of this can be like: + +```bash +vespalib::alloc::MemoryAllocator::PtrAndSize vespalib::alloc::MmapFileAllocator::alloc(size_t) const: Assertion buf!=MAP_FAILED +``` + +```bash +'terminate called after throwing an instance of 'std::runtime_error' 'what(): mmap of file '/opt/vespa/var/db/vespa/search/cluster.abc/n12/documents/xyz/0.ready/index/index.fusion.43/field123/boolocc.bdat' with flags '1' failed with error: 'Cannot allocate memory' +``` + +## Hostname + +When Vespa services are started on a node, the node must identify itself to the configuration system to get configuration (including which services to run). This requires a unique identifier for the node in the config server. Since it is already a requirement that the node has a *hostname* that the config server knows, Vespa uses the same *hostname* when a node identifies itself to get its configuration. See [config sentinel](/en/operations/self-managed/config-sentinel) for details. + +In order to find the IP address of a node and connect to it, the node must have a *hostname* that identifies it and which maps to its IP address. Actual machines on a network will usually have a *Fully Qualified Domain Name* (FQDN) in DNS, which should be used as the host name for this purpose. + +Note that it is a *requirement* that the host name, configured in [hosts.xml](/en/reference/applications/hosts), can be used to look up the IP address of the node (see workaround using `VESPA_HOSTNAME` below). The configuration server use this host name to create URLs to be used to open network connections to Vespa services running on that node. If the nodes use IP addresses which don't have DNS names, one *must* have *all* those IP addresses with corresponding host names in `/etc/hosts` on *all* nodes in the Vespa installation. We recommend using names that can be used as FQDNs also in this case, in case of moving to using a DNS server instead of publishing `/etc/hosts`. + +This means that the node *must* know its own hostname (FQDN), and be in agreement with the config server about what exactly the host name is. Usually this is achieved by just running the `hostname` command. If `hostname` is set to the FQDN of the node, then everything should Just Work. + +As an alternative to modifying `/etc/hosts`, set [VESPA\_HOSTNAME](/en/operations/self-managed/files-processes-and-ports#environment-variables) on the hosts. [vespa-ip-vs-hostname](https://www.jocas.lt/journal/articles/vespa-ip-vs-hostname/) is a great post on how to do this. + +## Simple single-node development environment + +When testing a Vespa configuration on a single-node setup, one can usually avoid the setup hassle by overriding the hostname with the value "localhost". Try this command for that purpose: + +```bash +$ echo "override VESPA_HOSTNAME localhost" >> $VESPA_HOME/conf/vespa/default-env.txt +``` + +Running Java unit tests won't pick up settings in `default-env.txt` and will default to "localhost" if `VESPA_HOSTNAME` isn't set in the environment. \ No newline at end of file diff --git a/en/operations/self-managed/procedure-change-attribute-index.html b/en/operations/self-managed/procedure-change-attribute-index.mdx similarity index 54% rename from en/operations/self-managed/procedure-change-attribute-index.html rename to en/operations/self-managed/procedure-change-attribute-index.mdx index 6ae930d287..66c16a92f3 100644 --- a/en/operations/self-managed/procedure-change-attribute-index.html +++ b/en/operations/self-managed/procedure-change-attribute-index.mdx @@ -1,37 +1,21 @@ --- -# Copyright Vespa.ai. All rights reserved. title: "Procedure: Change from attribute to index" -applies_to: self-managed -redirect_from: -- /en/operations/procedure-change-attribute-index.html -- /en/operations-selfhosted/procedure-change-attribute-index.html +sidebarTitle: "Change from attribute to index procedure" --- - - -

    - Changing between index and attribute is a common field change operation - to optimize performance. - Use the reindexing feature to safely migrate data to/from index structures. -

    -

    - Changing from attribute to index can be seen as "drop attribute" and "add index". - When the attribute aspect of a field is removed, the field's data is not queryable after deployment. - The reindexing process will populate the field's index structure, - but this takes time, depending on corpus size. -

    -

    - Another approach is to run with both attribute and index in the transition, keeping data available for queries. - The gist of this procedure is to add index, run a reindex - - then remove attribute aspect: -

    -
    +Changing between `index` and `attribute` is a common field change operation to optimize performance. Use the [reindexing](/en/operations/reindexing) feature to safely migrate data to/from index structures.
    +
    +Changing from attribute to index can be seen as "drop attribute" and "add index". When the attribute aspect of a field is removed, the field's data is not queryable after deployment. The reindexing process will populate the field's index structure, but this takes time, depending on corpus size.
    +
    +Another approach is to run with both attribute and index in the transition, keeping data available for queries. The gist of this procedure is to add `index`, run a reindex - then remove `attribute` aspect:
    +
    +```js
     # field configuration at start
     field artist type string {
         indexing: summary | attribute
     }
     
    -->
    +->
     
     # intermediate step to populate index structure, keeping the data in the attribute
     field artist type string {
    @@ -40,7 +24,7 @@
         stemming: none
     }
     
    -->
    +->
     
     # final configuration, migrated to index
     field artist type string {
    @@ -48,50 +32,43 @@
         match: word
         stemming: none
     }
    -
    -{% include note.html content='If the field is used as a filter only (i.e. no ranking), -consider adding rank: filter, see example in -feature-tuning.' %} - - - - -

    Procedure

    -
      -
    1. -

      - Test this using the quick-start, - changing the artist field to an attribute before running. - Also add a validation override - in src/main/application/validation-overrides.xml: -

      -
      -<validation-overrides>
      -    <allow until="2021-08-30">indexing-change</allow>
      -</validation-overrides>
      -
      -
    2. -
    3. -

      - Run the quick start, stop after feeding documents. - Run a query to validate data can be queried: -

      -
      +```
      +
      +
      +**Note:** 
      +
      +If the field is used as a filter only (i.e. no ranking), consider adding `rank: filter`, see example in [feature-tuning](/en/performance/feature-tuning).
      +
      +
      +## Procedure
      +
      +
      +
      +Test this using the [quick-start](/en/basics/deploy-an-application-local), changing the `artist` field to an attribute before running. Also add a [validation override](/en/reference/applications/validation-overrides) in `src/main/application/validation-overrides.xml`:
      +
      +```xml
      +
      +    indexing-change
      +
      +```
      +
      +
      +Run the quick start, stop after feeding documents. Run a query to validate data can be queried:
      +
      +```bash
       $ curl "http://localhost:8080/search/?ranking=rank_albums&yql=select%20%2A%20from%20sources%20%2A%20where%20artist%20contains%20%22Coldplay%22"
      -
      -

      - One can also dump current index structures, see artist as an attribute. -

      -
    4. -
    5. -

      - Add index aspect and match/stemming settings to the field, deploy and observe output -

      -
      +```
      +
      +One can also [dump current index structures](#appendix), see `artist` as an attribute.
      +
      +
      +Add index aspect and match/stemming settings to the field, deploy and observe output
      +
      +```js expandable
       field artist type string {
      -    indexing: summary | attribute | index
      +    indexing: summary | attribute | index
               match    : word
      -        stemming : none
      +        stemming : none
       }
       
       $ (cd src/main/application && zip -r - .) |   curl --header Content-Type:application/zip --data-binary @- \
      @@ -139,16 +116,12 @@ 

      Procedure

      ] } } -
      -
    6. -
    7. -

      - Wait for the new configuration generation to be activated on the config server(s) - - this is normally quite immediate. - After that, allow up to 3 minutes for the config servers to set reindexing ready, - track this using the reindexing endpoint: -

      -
      +```
      +
      +
      +Wait for the new configuration generation to be activated on the config server(s) - this is normally quite immediate. After that, allow up to 3 minutes for the config servers to set reindexing ready, track this using the `reindexing` endpoint:
      +
      +```json expandable
       $ while true; do
           curl http://localhost:19071/application/v2/tenant/default/application/default/environment/prod/region/default/instance/default/reindexing | jq .
           sleep 10
      @@ -183,14 +156,12 @@ 

      Procedure

      } } } -
      -
    8. -
    9. -

      - When ready, deploy again to start reindexing, - wait for it to complete (use the loop in previous step): -

      -
      +```
      +
      +
      +When ready, deploy again to start reindexing, wait for it to complete (use the loop in previous step):
      +
      +```json expandable
       $ (cd src/main/application && zip -r - .) |   curl --header Content-Type:application/zip --data-binary @- \
         localhost:19071/application/v2/tenant/default/prepareandactivate
       
      @@ -228,28 +199,24 @@ 

      Procedure

      } } } -
      -
    10. -
    11. -

      - Dumping the index structures now shows artist both in index and attribute, and there is an entry in vespa.log. - Verify the query still works: -

      -
      +```
      +
      +
      +Dumping the index structures now shows artist both in index and attribute, and there is an entry in vespa.log. Verify the query still works:
      +
      +```bash
       $ docker exec vespa /usr/bin/sh -c 'vespa-logfmt | grep Reindexer'
       [2021-08-11 07:59:00.535] INFO    : container-clustercontroller Container.ai.vespa.reindexing.Reindexer
                                           Completed reindexing of datatype music (code: 1412693671) after PT0.558683S
       
       $ curl "http://localhost:8080/search/?ranking=rank_albums&yql=select%20%2A%20from%20sources%20%2A%20where%20artist%20contains%20%22Coldplay%22"
      -
      -
    12. -
    13. -

      - As data is now reindexed into the index data structures, deploy without attribute. - (Observe changes to index files, "artist" is now in index only). - Test query after restart: -

      -
      +```
      +
      +
      +
      +As data is now reindexed into the index data structures, deploy without attribute. (Observe changes to index files, "artist" is now in index only). Test query after restart:
      +
      +```js
       field artist type string {
           indexing: summary | index
               match    : word
      @@ -260,31 +227,27 @@ 

      Procedure

      localhost:19071/application/v2/tenant/default/prepareandactivate $ curl "http://localhost:8080/search/?ranking=rank_albums&yql=select%20%2A%20from%20sources%20%2A%20where%20artist%20contains%20%22Coldplay%22" -
      -
    14. -
    15. -

      - Optional: restart Vespa - a restart will reclaim memory from the attribute: -

      -
      +```
      +
      +
      +
      +Optional: restart Vespa - a restart will reclaim memory from the attribute:
      +
      +```bash
       $ docker exec vespa sh -c 'vespa-stop-services && vespa-start-services'
      -
      -
    16. -
    -

    Notes:

    -
      -
    • The match/stemming settings above are set to the same at default attribute settings
    • -
    - - - -

    Appendix

    -

    - To inspect attribute and index data (can be useful when troubleshooting), - use vespa-proton-cmd, - then list files: -

    -
    +```
    +
    +
    +
    +Notes:
    +
    +- The match/stemming settings above are set to the same at default attribute settings
    +
    +## Appendix
    +
    +To inspect attribute and index data (can be useful when troubleshooting), use [vespa-proton-cmd](/en/reference/operations/self-managed/tools#vespa-proton-cmd), then list files:
    +
    +```bash
     $ docker exec vespa vespa-proton-cmd --local triggerFlush
     $ docker exec vespa find /opt/vespa/var/db/vespa/search/cluster.music/n0/documents/music/0.ready
    -
    +``` \ No newline at end of file diff --git a/en/operations/self-managed/sizing-examples.html b/en/operations/self-managed/sizing-examples.mdx similarity index 50% rename from en/operations/self-managed/sizing-examples.html rename to en/operations/self-managed/sizing-examples.mdx index 399325d421..d46448af68 100644 --- a/en/operations/self-managed/sizing-examples.html +++ b/en/operations/self-managed/sizing-examples.mdx @@ -1,31 +1,16 @@ --- -# Copyright Vespa.ai. All rights reserved. title: "Vespa Scaling Configuration Examples" -applies_to: self-managed -redirect_from: -- /en/performance/sizing-examples.html +sidebarTitle: "Sizing examples" --- +This guide provides some example [services.xml](/en/reference/applications/services/content) files for content clusters using flat or grouped data distribution in self-managed clusters. -

    - This guide provides some example services.xml files - for content clusters using flat or grouped data distribution in self-managed clusters. -

    -

    - Refer to the - multinode-HA sample application for how to get started from a deployable multinode starting point. - The examples below are trimmed down for readability in the admin and container sections. - See the appendix for hosts.xml to use when testing deployments. -

    +Refer to the [multinode-HA](https://github.com/vespa-engine/sample-apps/tree/master/examples/operations/multinode-HA) sample application for how to get started from a deployable multinode starting point. The examples below are trimmed down for readability in the `admin` and `container` sections. See the [appendix](#appendix-hosts-xml) for *hosts.xml* to use when testing deployments. -

    Flat Distribution

    -

    - Flat (single group) distribution with min-redundancy=3. - Data is distributed and partitioned over 9 nodes and there are 3 replicas of each document, stored on 3 different nodes. - Queries are dispatched in parallel to all nodes. - In case of a node failure, the remaining nodes will index (make ready) - and activate the not ready (stored) copies to restore full search coverage. -

    -
    {% highlight xml%}
    +## Flat Distribution
    +
    +Flat (single group) distribution with [min-redundancy](/en/reference/applications/services/content#min-redundancy)=3. Data is distributed and partitioned over 9 nodes and there are 3 replicas of each document, stored on 3 different nodes. Queries are dispatched in parallel to all nodes. In case of a node failure, the remaining nodes will index (make ready) and activate the *not ready* (stored) copies to restore full search coverage.
    +
    +```xml expandable
     
         
             
    @@ -66,48 +51,25 @@ 

    Flat Distribution

    -{% endhighlight %}
    +``` +## Grouped Distribution +See [sizing search](/en/performance/sizing-search) more on when to use grouped distribution. When using grouped distribution in an indexed content cluster, the following restrictions apply: -

    Grouped Distribution

    +- There can only be a single level of leaf groups under the top group +- The number of leaf groups must be a factor of the *redundancy* +- The [distribution partitions](/en/reference/applications/services/content#distribution) must be specified such that the redundancy per group is equal -

    See sizing search more on when to use grouped distribution. +With a low number of nodes per group, it's important to remember that a node failure will cause the data to be re-distributed to the remaining nodes and their memory footprint and disk usage will grow when those nodes start activating the documents originally activated on the failed node. E.g. with 2 nodes per group, the remaining healthy node will start activating all the content, which will cause a 2x memory and disk footprint compared with the ideal state. -When using grouped distribution in an indexed content cluster, the following restrictions apply: -

    -
      -
    • There can only be a single level of leaf groups under the top group
    • -
    • The number of leaf groups must be a factor of the redundancy
    • -
    • The distribution partitions - must be specified such that the redundancy per group is equal
    • -
    -

    - With a low number of nodes per group, it's important to remember that a node failure - will cause the data to be re-distributed to the remaining nodes - and their memory footprint and disk usage will grow - when those nodes start activating the documents originally activated on the failed node. - E.g. with 2 nodes per group, the remaining healthy node will start activating all the content, - which will cause a 2x memory and disk footprint compared with the ideal state. -

    -

    - The min-node-ratio-per-group - controls the data distribution behavior inside a group in cases of node failures. - This sets a lower bound on the ratio of nodes within groups - that must be online and accepting feed and query traffic, - before the entire group is automatically taken out of service from both feed and search/serving. - Once number of nodes in the group have been restored, and ideal state has been achieved, - the group will be automatically set in service. -

    +The [min-node-ratio-per-group](/en/reference/applications/services/content#min-node-ratio-per-group) controls the data distribution behavior inside a group in cases of node failures. This sets a lower bound on the ratio of nodes within groups that must be online and accepting feed and query traffic, before the entire group is automatically taken out of service from both feed and search/serving. Once number of nodes in the group have been restored, and ideal state has been achieved, the group will be automatically set in service. +## 9 nodes, 3 groups with 3 nodes per group -

    9 nodes, 3 groups with 3 nodes per group

    -

    - This example has 3 groups and each group index all the documents over the 3 nodes in the group. - With 3 groups there are 3 replicas in total of each document, and each replica is indexed and active. - Losing a node does not reduce search coverage. -

    -
    {% highlight xml%}
    +This example has 3 groups and each group index all the documents over the 3 nodes in the group. With 3 groups there are 3 replicas in total of each document, and each replica is indexed and active. Losing a node does not reduce search coverage.
    +
    +```xml expandable
     
         
             
    @@ -155,19 +117,13 @@ 

    9 nodes, 3 groups with 3 nodes -{% endhighlight %}

    +``` +## 9 nodes, 9 groups with 1 node per group +This example has 9 groups and each group index all the documents on a single node. With 9 groups there are 9 replicas in total of each document, and each replica is indexed and active. Losing a node does not reduce search coverage. With a single node, indexing throughput is limited by the single node performance, as all data needs to go all nodes. -

    9 nodes, 9 groups with 1 node per group

    -

    - This example has 9 groups and each group index all the documents on a single node. - With 9 groups there are 9 replicas in total of each document, and each replica is indexed and active. - Losing a node does not reduce search coverage. - With a single node, indexing throughput is limited by the single node performance, - as all data needs to go all nodes. -

    -
    {% highlight xml%}
    +```xml expandable
     
         
             
    @@ -227,50 +183,23 @@ 

    9 nodes, 9 groups with 1 node pe -{% endhighlight %}

    - +``` +## Serving Availability Tuning -

    Serving Availability Tuning

    -

    - When using flat distribution, - soft failing nodes is a challenge for serving with high availability and low latency. - Soft failing nodes are nodes which answers health checks from - cluster controllers - and search container dispatch health checks, - but still experiences issues which impacts serving latency - (e.g. cpu frequency throttling due to thermal heating, memory corruptions and so forth). - In a cluster with a flat distribution, - the slowest node determines the latency, - as the query request is dispatched to all content nodes in parallel. - The probability of a soft failing node increases with the number of nodes used to distribute the data over. -

    -

    - Use adaptive coverage timeout - to prevent slow soft failing nodes to impact availability. - This allows the dispatcher to stop waiting for the slowest node(s). - See also graceful search degradation. -

    -

    - Grouped distributions are less impacted by soft failing nodes in general, - as queries are dispatched to one group at a time using a - dispatch policy. - The adaptive policy takes group latency into account - when deciding which group the query request should be routed to. -

    +When using flat distribution, *soft failing nodes* is a challenge for serving with high availability and low latency. Soft failing nodes are nodes which answers health checks from [cluster controllers](/en/content/content-nodes) and search container dispatch health checks, but still experiences issues which impacts serving latency (e.g. cpu frequency throttling due to thermal heating, memory corruptions and so forth). In a cluster with a flat distribution, the slowest node determines the latency, as the query request is dispatched to all content nodes in parallel. The probability of a soft failing node increases with the number of nodes used to distribute the data over. +Use [adaptive coverage timeout](/en/reference/applications/services/content#coverage) to prevent slow soft failing nodes to impact availability. This allows the dispatcher to stop waiting for the slowest node(s). See also [graceful search degradation](/en/performance/graceful-degradation). +Grouped distributions are less impacted by soft failing nodes in general, as queries are dispatched to one group at a time using a [dispatch policy](/en/reference/applications/services/content#dispatch-policy). The *adaptive* policy takes group latency into account when deciding which group the query request should be routed to. -

    Changing Group Configuration

    -

    - It is easy to change the group topology without service disruption, - with a few caveats - read more in elasticity. -

    +## Changing Group Configuration +It is easy to change the group topology without service disruption, with a few caveats - read more in [elasticity](/en/content/elasticity#changing-topology). +## Appendix: hosts.xml -

    Appendix: hosts.xml

    -
    {% highlight xml%}
    +```xml expandable
     
         
             node0
    @@ -300,4 +229,4 @@ 

    Appendix: hosts.xml

    node8
    -{% endhighlight %}
    +``` \ No newline at end of file diff --git a/en/operations/self-managed/slobrok.html b/en/operations/self-managed/slobrok.html deleted file mode 100644 index ab22efdac5..0000000000 --- a/en/operations/self-managed/slobrok.html +++ /dev/null @@ -1,43 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Service location broker - slobrok" -applies_to: self-managed -redirect_from: -- /en/slobrok.html -- /en/operations-selfhosted/slobrok.html ---- - -

    - Slobrok is an acronym for Service Location Broker, - and it is a name service used in Vespa. - The service listens on a specific port - - use vespa-model-inspect - to find the Slobrok service's port number. -

    -

    - Slobrok is running by default on the administration node as well as one or two other random nodes for redundancy. - Best practise for a multi-node, high-availability application is found in the - multinode-HA - sample application. - In this application, slobrok instances are hosted on nodes running - config servers. - The motivation is, like the config servers, Vespa requires slobrok to be up for services to function. - Operating slobrok is the same as config servers, too - three is enough for most applications. - As slobrok requires minimal system resources, - it does not impact other services running on the same node -> - using config server nodes is ideal. -

    -

    - Clients, like the Document API, - will do lookups on any of the service location broker nodes. - Slobrok is not used in the query pipeline. - The cluster-controller - uses slobrok to evaluate service availability. -

    -

    - The Slobrok process looks like: -

    -
    {% highlight sh %}
    -$ ps ax | grep vespa-slobrok
    -93906  ??  SJ     2:31.52 $VESPA_HOME/sbin/vespa-slobrok -p 19100 -c slobrok.0
    -{% endhighlight %}
    diff --git a/en/operations/self-managed/slobrok.mdx b/en/operations/self-managed/slobrok.mdx new file mode 100644 index 0000000000..2c01b6d11f --- /dev/null +++ b/en/operations/self-managed/slobrok.mdx @@ -0,0 +1,17 @@ +--- +title: "Service location broker - slobrok" +sidebarTitle: "Service Location Broker" +--- + +Slobrok is an acronym for *Service Location Broker*, and it is a name service used in Vespa. The service listens on a specific port - use [vespa-model-inspect](/en/reference/operations/self-managed/tools#vespa-model-inspect) to find the `Slobrok` service's port number. + +Slobrok is running by default on the administration node as well as one or two other random nodes for redundancy. Best practise for a multi-node, high-availability application is found in the [multinode-HA](https://github.com/vespa-engine/sample-apps/tree/master/examples/operations/multinode-HA) sample application. In this application, slobrok instances are hosted on nodes running [config servers](/en/operations/self-managed/configuration-server). The motivation is, like the config servers, Vespa requires slobrok to be up for services to function. Operating slobrok is the same as config servers, too - three is enough for most applications. As slobrok requires minimal system resources, it does not impact other services running on the same node -> using config server nodes is ideal. + +Clients, like the [Document API](/en/reference/api/api), will do lookups on any of the service location broker nodes. Slobrok is not used in the query pipeline. The [cluster-controller](/en/content/content-nodes#cluster-controller) uses slobrok to evaluate service availability. + +The Slobrok process looks like: + +```sh +$ ps ax | grep vespa-slobrok +93906 ?? SJ 2:31.52 $VESPA_HOME/sbin/vespa-slobrok -p 19100 -c slobrok.0 +``` \ No newline at end of file diff --git a/en/operations/self-managed/using-kubernetes-with-vespa.html b/en/operations/self-managed/using-kubernetes-with-vespa.html deleted file mode 100644 index a083edcf61..0000000000 --- a/en/operations/self-managed/using-kubernetes-with-vespa.html +++ /dev/null @@ -1,357 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Using Kubernetes with Vespa" -applies_to: self-managed -redirect_from: -- /en/vespa-quick-start-kubernetes.html -- /en/operations/using-kubernetes-with-vespa.html -- /en/operations-selfhosted/using-kubernetes-with-vespa.html ---- - -{% include note.html content=' -In this article, find a recipe for how to start self-managed Vespa in a Kubernetes cluster. -For production serving, Vespa on Kubernetes is a good read; -The Vespa Operator provides a more Kubernetes-native integration with a high degree of automation and value-adds.' %} - -

    - This article outlines how to run Vespa using Kubernetes. - Find a quickstart for running Vespa in a single pod in - singlenode quickstart with minikube. -

    -

    - Setting up a multi-pod Vespa cluster is a bit more complicated, - and requires knowledge about how Vespa configures its services. - Use the - multinode-HA sample application as a basis for configuration. -

    -Vespa overview illustration -
      -
    • - A Vespa cluster is made of one or more config servers in a config server cluster. - This cluster keeps configuration for the services running in the service pods. - The config server cluster pods should hence be started first. -
    • -
    • - Config servers use Apache Zookeeper for shared state. - The config servers will not set their /state/v1/health to UP before Zookeeper quorum is reached. - This means that all config server pods must be running before quorum is reached, - and one cannot use a readinessProbe probe for the config servers for a staggered start. -
    • -
    • - See a practical example at - config server cluster startup - once completed it should look like: -
      -$ kubectl get pods
      -NAME                   READY   STATUS    RESTARTS   AGE
      -vespa-configserver-0   1/1     Running   0          2m45s
      -vespa-configserver-1   1/1     Running   0          107s
      -vespa-configserver-2   1/1     Running   0          62s
      -
      -
    • -
    • - Once the config server cluster is started successfully, - the application package can be deployed, - and the pods for the services nodes started. - The application package maps services to pods (nodes), - so this must be deployed successfully before the services in the pods can start. - It does not matter whether one deploys the application package before or after starting the service pods, - as the pods will idle, waiting for configuration. -
    • -
    • - - multinode-HA starts the pods first, - see - Vespa startup. - As the application package is not yet deployed, the service inside the pods is not started (as it is not configured). - The Vespa infrastructure is started, however, see - config sentinel - so the pod is started with the config-proxy waiting for services config at this point. -
    • -
    • - The cluster startup feature is good to know. - This is a setting to not start a service before enough services can run - - see the Connectivity check log messages. -
    • -
    • - Deploy the application package. - At this point, the pods will know which service to run, and start a container or content node service. - Shortly after, the /state/v1/health endpoint is enabled on the pods. -
    • -
    • - Note that ports are allocated dynamically, - but the defaults will get you started - - see the illustration with - services and ports for /state/v1/health: -
        -
      • Config server: 19071
      • -
      • Container node: 8080
      • -
      • Content node: 19107
      • -
      -
    • -
    -

    - The list above is an overview of the config server -> application package -> service /state/v1/health - dependency chain. - This sequence of steps must be considered when building the Kubernetes cluster configuration. -

    -

    - A good next step is running the - multinode-HA for Kubernetes - there you will also find useful - - troubleshooting tools. -

    - - -

    Singlenode quickstart with minikube

    -

    - This section describes how to install and run Vespa on a single machine using Kubernetes (K8s). - Also see - Vespa example on GKE. -

    - -{% include pre-req.html memory="5 GB" extra-reqs=' -
  • Git.
  • -
  • Minikube.
  • ' %} - -
      - -
    1. - Validate environment: -

      - Refer to Docker memory - for details and troubleshooting: -

      -
      - -
      -$ docker info | grep "Total Memory"
      -or
      -$ podman info | grep "memTotal"
      -
      -
      -
    2. - -
    3. - Start Kubernetes cluster with minikube: -
      - -
      -$ minikube start --driver docker --memory 4096
      -
      -
      -
    4. - -
    5. - Clone the Vespa sample apps: -
      - -
      -$ git clone --depth 1 https://github.com/vespa-engine/sample-apps.git
      -$ export VESPA_SAMPLE_APPS=`pwd`/sample-apps
      -
      -
      -
    6. - -
    7. - Create Kubernetes configuration files: -
      - -
      -$ cat << EOF > service.yml
      -apiVersion: v1
      -kind: Service
      -metadata:
      -  name: vespa
      -  labels:
      -    app: vespa
      -spec:
      -  selector:
      -    app: vespa
      -  type: NodePort
      -  ports:
      -  - name: container
      -    port: 8080
      -    targetPort: 8080
      -    protocol: TCP
      -  - name: config
      -    port: 19071
      -    targetPort: 19071
      -    protocol: TCP
      -EOF
      -
      -
      -
      - -
      -$ cat << EOF > statefulset.yml
      -apiVersion: apps/v1
      -kind: StatefulSet
      -metadata:
      -  name: vespa
      -  labels:
      -    app: vespa
      -spec:
      -  replicas: 1
      -  serviceName: vespa
      -  selector:
      -    matchLabels:
      -      app: vespa
      -  template:
      -    metadata:
      -      labels:
      -        app: vespa
      -    spec:
      -      containers:
      -      - name: vespa
      -        image: vespaengine/vespa
      -        imagePullPolicy: Always
      -        env:
      -        - name: VESPA_CONFIGSERVERS
      -          value: vespa-0.vespa.default.svc.cluster.local
      -        securityContext:
      -          runAsUser: 1000
      -        ports:
      -        - containerPort: 8080
      -          protocol: TCP
      -        readinessProbe:
      -          httpGet:
      -            path: /state/v1/health
      -            port: 19071
      -            scheme: HTTP
      -EOF
      -
      -
      -
    8. - -
    9. - Start the service: -
      - -
      -$ kubectl apply -f service.yml -f statefulset.yml
      -
      -
      -
    10. - -
    11. - Wait for the service to enter a running state: -
      - -
      -$ kubectl get pods --watch
      -
      -
      - Wait for STATUS Running: -
      -    NAME      READY   STATUS              RESTARTS   AGE
      -    vespa-0   0/1     ContainerCreating   0          8s
      -    vespa-0   0/1     Running             0          2m4s
      -
      -
    12. - -
    13. - Start port forwarding to pods: -
      - -
      -$ kubectl port-forward vespa-0 19071 8080 &
      -
      -
      -
    14. - -
    15. - Wait for configserver start - wait for 200 OK: -
      - -
      -$ curl -s --head http://localhost:19071/state/v1/health
      -
      -
      -
    16. - -
    17. - Deploy and activate the application package: -
      - -
      -$ vespa deploy ${VESPA_SAMPLE_APPS}/album-recommendation
      -
      -
      -
    18. - -
    19. - Ensure the application is active - wait for 200 OK: -

      This normally takes a minute or so:

      -
      - -
      -$ curl -s --head http://localhost:8080/state/v1/health
      -
      -
      -
    20. - -
    21. - Feed documents: -
      - -
      -$ vespa feed sample-apps/album-recommendation/ext/documents.jsonl
      -
      -
      -
    22. - -
    23. -

      Make a query:

      -
      - -
      -$ vespa query 'select * from music where true'
      -
      -
      -
    24. - -
    25. - Run a document get request: -
      - -
      -$ vespa document get id:mynamespace:music::love-is-here-to-stay
      -
      -
      -
    26. - -
    27. -

      Clean up:

      -

      Stop the running container:

      -
      - -
      -$ kubectl delete service,statefulsets vespa
      -
      -
      -

      Stop port forwarding:

      -
      - -
      -$ killall kubectl
      -
      -
      -

      Stop minikube:

      -
      - -
      -$ minikube stop
      -
      -
      -
    28. - -
    - - -

    At any point during the procedure, dump logs for troubleshooting:

    -
    - -
    -$ kubectl logs vespa-0
    -
    -
    diff --git a/en/operations/self-managed/using-kubernetes-with-vespa.mdx b/en/operations/self-managed/using-kubernetes-with-vespa.mdx new file mode 100644 index 0000000000..11314c47ee --- /dev/null +++ b/en/operations/self-managed/using-kubernetes-with-vespa.mdx @@ -0,0 +1,234 @@ +--- +title: "Using Kubernetes with Vespa" +sidebarTitle: "Using Kubernetes" +--- + + +**Note:** + +In this article, find a recipe for how to start self-managed Vespa in a Kubernetes cluster. For production serving, [Vespa on Kubernetes](/en/operations/kubernetes/vespa-on-kubernetes) is a good read; The Vespa Operator provides a more Kubernetes-native integration with a high degree of automation and value-adds. + + +This article outlines how to run Vespa using Kubernetes. Find a quickstart for running Vespa in a single pod in [singlenode quickstart with minikube](#singlenode-quickstart-with-minikube). + +Setting up a multi-pod Vespa cluster is a bit more complicated, and requires knowledge about how Vespa configures its services. Use the [multinode-HA](https://github.com/vespa-engine/sample-apps/tree/master/examples/operations/multinode-HA/gke) sample application as a basis for configuration. + + +![Vespa overview illustration](/assets/img/vespa-overview.svg) + + +- A Vespa cluster is made of one or more config servers in a config server cluster. This cluster keeps configuration for the services running in the service pods. The config server cluster pods should hence be started first. +- Config servers use Apache Zookeeper for shared state. The config servers will not set their */state/v1/health* to UP before Zookeeper quorum is reached. This means that all config server pods must be running before quorum is reached, and one cannot use a *readinessProbe* probe for the config servers for a staggered start. +- See a practical example at [config server cluster startup](https://github.com/vespa-engine/sample-apps/tree/master/examples/operations/multinode-HA/gke#config-server-cluster-startup) - once completed it should look like:$ kubectl get pods + + ```bash + $ kubectl get pods + NAME READY STATUS RESTARTS AGE + vespa-configserver-0 1/1 Running 0 2m45s + vespa-configserver-1 1/1 Running 0 107s + vespa-configserver-2 1/1 Running 0 62s + ``` +- Once the config server cluster is started successfully, the [application package](/en/basics/applications) can be deployed, and the pods for the services nodes started. The application package maps services to pods (nodes), so this must be deployed successfully before the services in the pods can start. It does not matter whether one deploys the application package before or after starting the service pods, as the pods will idle, waiting for configuration. +- [multinode-HA](https://github.com/vespa-engine/sample-apps/tree/master/examples/operations/multinode-HA/gke) starts the pods first, see [Vespa startup](https://github.com/vespa-engine/sample-apps/tree/master/examples/operations/multinode-HA/gke#vespa-startup). As the application package is not yet deployed, the service inside the pods is not started (as it is not configured). The Vespa infrastructure is started, however, see [config sentinel](/en/operations/self-managed/config-sentinel) - so the pod is started with the config-proxy waiting for services config at this point. +- The [cluster startup](/en/operations/self-managed/config-sentinel#cluster-startup) feature is good to know. This is a setting to not start a service before enough services can run - see the *Connectivity check* log messages. +- Deploy the application package. At this point, the pods will know which service to run, and start a container or content node service. Shortly after, the */state/v1/health* endpoint is enabled on the pods. +- Note that ports are allocated dynamically, but the defaults will get you started - see the illustration with [services and ports](https://github.com/vespa-engine/sample-apps/tree/master/examples/operations/multinode-HA#get-started) for */state/v1/health*: + - Config server: 19071 + - Container node: 8080 + - Content node: 19107 + +The list above is an overview of the config server -> application package -> service */state/v1/health* dependency chain. This sequence of steps must be considered when building the Kubernetes cluster configuration. + +A good next step is running the [multinode-HA](https://github.com/vespa-engine/sample-apps/tree/master/examples/operations/multinode-HA/gke) for Kubernetes - there you will also find useful [troubleshooting](https://github.com/vespa-engine/sample-apps/tree/master/examples/operations/multinode-HA/gke#misc--troubleshooting) tools. + +## Singlenode quickstart with minikube + +This section describes how to install and run Vespa on a single machine using Kubernetes (K8s). Also see [Vespa example on GKE](https://github.com/vespa-engine/sample-apps/tree/master/examples/operations/basic-search-on-gke). + + +**Prerequisites:** + +- Linux, macOS or Windows 10 Pro on x86\_64 or arm64, with [Podman Desktop](https://podman.io/) or [Docker Desktop](https://www.docker.com/products/docker-desktop/) installed, with an engine running. + - Alternatively, start the Podman daemon: + + ```bash + $ podman machine init --memory 6000 + $ podman machine start + ``` + - See [Docker Containers](/en/operations/self-managed/docker-containers) for system limits and other settings. +- For CPUs older than Haswell (2013), see [CPU Support](/en/operations/self-managed/cpu-support). +- Memory: Minimum 5 GB RAM dedicated to Docker/Podman. [Memory recommendations](/en/operations/self-managed/node-setup#memory-settings). +- Disk: Avoid `NO_SPACE` - the vespaengine/vespa container image + headroom for data requires disk space. [Read more](/en/writing/feed-block). +- [Homebrew](https://brew.sh/) to install the [Vespa CLI](/en/clients/vespa-cli), or download the Vespa CLI from [Github releases](https://github.com/vespa-engine/vespa/releases). +- [Git](https://git-scm.com/downloads). +- [Minikube](https://kubernetes.io/docs/tasks/tools/). + + + + +Refer to [Docker memory](/en/operations/self-managed/docker-containers#memory) for details and troubleshooting: + +```bash +docker info | grep "Total Memory" +or +podman info | grep "memTotal" +``` + + +```bash +minikube start --driver docker --memory 4096 +``` + + +**Clone the [Vespa sample apps](https://github.com/vespa-engine/sample-apps):** + +```bash +git clone --depth 1 https://github.com/vespa-engine/sample-apps.git +export VESPA_SAMPLE_APPS=`pwd`/sample-apps +``` + + +```yaml expandable +cat << EOF > service.yml +apiVersion: v1 +kind: Service +metadata: + name: vespa + labels: + app: vespa +spec: + selector: + app: vespa + type: NodePort + ports: + - name: container + port: 8080 + targetPort: 8080 + protocol: TCP + - name: config + port: 19071 + targetPort: 19071 + protocol: TCP +EOF +``` + +```yaml expandable +cat << EOF > statefulset.yml +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: vespa + labels: + app: vespa +spec: + replicas: 1 + serviceName: vespa + selector: + matchLabels: + app: vespa + template: + metadata: + labels: + app: vespa + spec: + containers: + - name: vespa + image: vespaengine/vespa + imagePullPolicy: Always + env: + - name: VESPA_CONFIGSERVERS + value: vespa-0.vespa.default.svc.cluster.local + securityContext: + runAsUser: 1000 + ports: + - containerPort: 8080 + protocol: TCP + readinessProbe: + httpGet: + path: /state/v1/health + port: 19071 + scheme: HTTP +EOF +``` + + +```bash +kubectl apply -f service.yml -f statefulset.yml +``` + + +```bash +kubectl get pods --watch +``` + +Wait for STATUS Running: + +```bash +NAME READY STATUS RESTARTS AGE +vespa-0 0/1 ContainerCreating 0 8s +vespa-0 0/1 Running 0 2m4s +``` + + +```bash +kubectl port-forward vespa-0 19071 8080 & +``` + + +```bash +curl -s --head http://localhost:19071/state/v1/health +``` + + +```bash +vespa deploy ${VESPA_SAMPLE_APPS}/album-recommendation +``` + + +This normally takes a minute or so: + +```bash +$ curl -s --head http://localhost:8080/state/v1/health +``` + + +```bash +$ vespa feed sample-apps/album-recommendation/ext/documents.jsonl +``` + + +```bash +$ vespa query 'select * from music where true' +``` + + +```bash +$ vespa document get id:mynamespace:music::love-is-here-to-stay +``` + + +Stop the running container: + +```bash +$ kubectl delete service,statefulsets vespa +``` + +Stop port forwarding: + +```bash +$ killall kubectl +``` + +Stop minikube: + +```bash +$ minikube stop +``` + + + +At any point during the procedure, dump logs for troubleshooting: + +```bash +$ kubectl logs vespa-0 +``` \ No newline at end of file diff --git a/en/operations/self-managed/vespa-gpu-container.html b/en/operations/self-managed/vespa-gpu-container.html deleted file mode 100644 index 5889638a00..0000000000 --- a/en/operations/self-managed/vespa-gpu-container.html +++ /dev/null @@ -1,149 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Container GPU setup" -applies_to: self-managed -redirect_from: -- /en/vespa-gpu-container.html -- /en/operations-selfhosted/vespa-gpu-container.html ---- - -

    -Vespa supports using GPUs to evaluate ONNX models, as part of -its stateless model evaluation -feature. When running Vespa inside a container engine such as Docker or -Podman, special configuration is required to make GPU(s) available inside the -container. -

    -

    -The following guide explains how to do this for Nvidia GPUs, using Podman on -RHEL8. This should also work on plain Rocky Linux 8.8 and AlmaLinux 8.8 on x86_64. -For other platforms and container engines, see -the Nvidia -container toolkit installation guide. -Commands below need to run as root (use sudo bash first). -

    - - - -

    Run a script

    -

    Fetch and run our script for RHEL8 / x86_64 and run it as follows:

    -
    -sudo dnf -y install wget
    -wget https://raw.githubusercontent.com/vespa-engine/docker-image/master/experimental/gpu-setup-rhel8-x86.sh
    -sh gpu-setup-rhel8-x86.sh
    -
    -

    -This will follow the steps below and check if a sample application is able to utilise the GPU. -For more details see the steps below. -

    - - -

    Configuration steps

    -
      -
    1. -

      - Check that SELinux is disabled with getenforce; edit /etc/selinux/config and reboot if necessary. - To temporarily avoid SELinux interfering, it's possible to run setenforce Permissive instead. -

      -
    2. -
    3. -

      - Ensure that Nvidia drivers are installed on your host where you want to run the vespaengine/vespa - container image. On RHEL 8 this can be done as follows: -

      -
      -dnf config-manager \
      -  --add-repo=https://developer.download.nvidia.com/compute/cuda/repos/rhel8/x86_64/cuda-rhel8.repo
      -dnf module install -y --enablerepo cuda-rhel8-x86_64 nvidia-driver:530
      -nvidia-modprobe
      -ls -ld /dev/nvidia*
      -
      -

      You should have (at least) these devices listed after running the above commands:

      -
      -crw-rw-rw-. 1 root root 195,   0 Aug 16 11:24 /dev/nvidia0
      -crw-rw-rw-. 1 root root 195, 255 Aug 16 11:24 /dev/nvidiactl
      -crw-rw-rw-. 1 root root 238,   0 Aug 16 11:24 /dev/nvidia-uvm
      -crw-rw-rw-. 1 root root 238,   1 Aug 16 11:24 /dev/nvidia-uvm-tools
      -
      -

      - See - Device Node Verification in the CUDA installation guide for more details. -

      -
    4. -
    5. -

      - Install nvidia-container-toolkit. This grants the container engine access - to your GPU device(s). On RHEL 8 this can be done as follows: -

      -
      -dnf config-manager \
      -  --add-repo=https://nvidia.github.io/libnvidia-container/rhel8.6/libnvidia-container.repo
      -dnf install -y --enablerepo libnvidia-container nvidia-container-toolkit
      -
      -
    6. -
    7. -

      Generate a "Container Device Interface" config:

      -
      -nvidia-ctk cdi generate --device-name-strategy=type-index --format=json --output /etc/cdi/nvidia.json
      -
      -
    8. -
    9. -

      Verify that the GPU device is exposed to the container:

      -
      -podman run --rm -it --device nvidia.com/gpu=all docker.io/nvidia/cuda:11.6.2-base-ubuntu20.04 nvidia-smi
      -
      -

      This should print details about your GPU(s) if everything is configured correctly.

      -
    10. -
    11. -

      Start the Vespa container with the --device option:

      -
      -podman run --detach --name vespa --hostname vespa-container \
      -  --publish 8080:8080 --publish 19071:19071 \
      -  --device nvidia.com/gpu=all \
      -  vespaengine/vespa
      -
      -
    12. -
    13. - The vespaengine/vespa image does not currently include the - necessary CUDA libraries by default, due to their large size. These - libraries must be installed inside the container manually: -
      -podman exec -u 0 -it vespa /bin/bash
      -dnf -y install dnf-plugins-core
      -dnf config-manager \
      -  --add-repo https://developer.download.nvidia.com/compute/cuda/repos/rhel8/x86_64/cuda-rhel8.repo
      -dnf -y install vespa-onnxruntime-cuda
      -
      -

      - Instead of the above installation of vespa-onnxruntime-cuda inside the running container, - you might want to build your own container image using the following Dockerfile as - it avoids having to run the container image with install privileges. -

      -
      -FROM vespaengine/vespa
      -
      -USER root
      -
      -RUN dnf -y install 'dnf-command(config-manager)'
      -RUN dnf config-manager --add-repo https://developer.download.nvidia.com/compute/cuda/repos/rhel8/x86_64/cuda-rhel8.repo
      -RUN dnf -y install $(rpm -q --queryformat '%{NAME}-cuda-%{VERSION}' vespa-onnxruntime)
      -
      -USER vespa
      -
      -

      Then instead run with your container image name:

      -
      -podman run --detach --name vespa --hostname vespa-container \
      -  --publish 8080:8080 --publish 19071:19071 \
      -  --device nvidia.com/gpu=all \
      -  your-container-image-name
      -
      -
    14. -
    15. - All Nvidia GPUs on the host should now be available inside the container, - with devices exposed at /dev/nvidiaN. - See stateless - model evaluation for how to configure the ONNX runtime to use a GPU for - computation. Similar for embedding models using GPU, see - embedder onnx reference. -
    16. -
    diff --git a/en/operations/self-managed/vespa-gpu-container.mdx b/en/operations/self-managed/vespa-gpu-container.mdx new file mode 100644 index 0000000000..1a1f43e473 --- /dev/null +++ b/en/operations/self-managed/vespa-gpu-container.mdx @@ -0,0 +1,122 @@ +--- +title: "Container GPU setup" +sidebarTitle: "Docker Containers GPU setup" +--- + +Vespa supports using GPUs to evaluate ONNX models, as part of its [stateless model evaluation feature](/en/ranking/stateless-model-evaluation). When running Vespa inside a container engine such as Docker or Podman, special configuration is required to make GPU(s) available inside the container. + +The following guide explains how to do this for Nvidia GPUs, using Podman on RHEL8. This should also work on plain Rocky Linux 8.8 and AlmaLinux 8.8 on x86\_64. For other platforms and container engines, see the [Nvidia container toolkit installation guide](https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/install-guide.html). Commands below need to run as root (use `sudo bash` first). + +## Run a script + +Fetch and run our script for RHEL8 / x86\_64 and run it as follows: + +```bash +sudo dnf -y install wget +wget https://raw.githubusercontent.com/vespa-engine/docker-image/master/experimental/gpu-setup-rhel8-x86.sh +sh gpu-setup-rhel8-x86.sh +``` + +This will follow the steps below and check if a sample application is able to utilise the GPU. For more details see the steps below. + +## Configuration steps + + + +Check that SELinux is disabled with `getenforce`; edit `/etc/selinux/config` and reboot if necessary. To temporarily avoid SELinux interfering, it's possible to run `setenforce Permissive` instead. + + +Ensure that Nvidia drivers are installed on your **host** where you want to run the `vespaengine/vespa` container image. On RHEL 8 this can be done as follows: + +```bash +dnf config-manager \ + --add-repo=https://developer.download.nvidia.com/compute/cuda/repos/rhel8/x86_64/cuda-rhel8.repo +dnf module install -y --enablerepo cuda-rhel8-x86_64 nvidia-driver:530 +nvidia-modprobe +ls -ld /dev/nvidia* +``` + +You should have (at least) these devices listed after running the above commands: + +```bash +crw-rw-rw-. 1 root root 195, 0 Aug 16 11:24 /dev/nvidia0 +crw-rw-rw-. 1 root root 195, 255 Aug 16 11:24 /dev/nvidiactl +crw-rw-rw-. 1 root root 238, 0 Aug 16 11:24 /dev/nvidia-uvm +crw-rw-rw-. 1 root root 238, 1 Aug 16 11:24 /dev/nvidia-uvm-tools +``` + +See [Device Node Verification](https://docs.nvidia.com/cuda/cuda-installation-guide-linux/index.html#device-node-verification) in the CUDA installation guide for more details. + + +Install `nvidia-container-toolkit`. This grants the container engine access to your GPU device(s). On RHEL 8 this can be done as follows: + +```bash +dnf config-manager \ + --add-repo=https://nvidia.github.io/libnvidia-container/rhel8.6/libnvidia-container.repo +dnf install -y --enablerepo libnvidia-container nvidia-container-toolkit +``` + + +Generate a "Container Device Interface" config: + +```bash +nvidia-ctk cdi generate --device-name-strategy=type-index --format=json --output /etc/cdi/nvidia.json +``` + + +Verify that the GPU device is exposed to the container: + +```bash +podman run --rm -it --device nvidia.com/gpu=all docker.io/nvidia/cuda:11.6.2-base-ubuntu20.04 nvidia-smi +``` + +This should print details about your GPU(s) if everything is configured correctly. + + +Start the Vespa container with the `--device` option: + +```bash +podman run --detach --name vespa --hostname vespa-container \ + --publish 8080:8080 --publish 19071:19071 \ + --device nvidia.com/gpu=all \ + vespaengine/vespa +``` + + +The `vespaengine/vespa` image does not currently include the necessary CUDA libraries by default, due to their large size. These libraries must be installed inside the container manually:podman exec -u 0 -it vespa /bin/bash + +```bash +podman exec -u 0 -it vespa /bin/bash +dnf -y install dnf-plugins-core +dnf config-manager \ + --add-repo https://developer.download.nvidia.com/compute/cuda/repos/rhel8/x86_64/cuda-rhel8.repo +dnf -y install vespa-onnxruntime-cuda +``` + +Instead of the above installation of `vespa-onnxruntime-cuda` inside the running container, you might want to build your own container image using the following `Dockerfile` as it avoids having to run the container image with install privileges. + +```bash +FROM vespaengine/vespa + +USER root + +RUN dnf -y install 'dnf-command(config-manager)' +RUN dnf config-manager --add-repo https://developer.download.nvidia.com/compute/cuda/repos/rhel8/x86_64/cuda-rhel8.repo +RUN dnf -y install $(rpm -q --queryformat '%{NAME}-cuda-%{VERSION}' vespa-onnxruntime) + +USER vespa +``` + +Then instead run with your container image name: + +```bash +podman run --detach --name vespa --hostname vespa-container \ + --publish 8080:8080 --publish 19071:19071 \ + --device nvidia.com/gpu=all \ + your-container-image-name +``` + + +All Nvidia GPUs on the host should now be available inside the container, with devices exposed at `/dev/nvidiaN`. See [stateless model evaluation](/en/ranking/stateless-model-evaluation#onnx-inference-options) for how to configure the ONNX runtime to use a GPU for computation. Similar for embedding models using GPU, see [embedder onnx reference](/en/reference/rag/embedding#embedder-onnx-reference-config). + + diff --git a/en/operations/self-managed/vespa-support.html b/en/operations/self-managed/vespa-support.html deleted file mode 100644 index bf51bebe0b..0000000000 --- a/en/operations/self-managed/vespa-support.html +++ /dev/null @@ -1,170 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Vespa Support CLI" -applies_to: enterprise ---- - -

    - The Vespa Enterprise image includes a built-in command-line tool called vespa-support. - It is designed to simplify and expedite the process of collecting diagnostic information from your system, - making it easier to request and receive support from the Vespa team. -

    -

    You should run this tool whenever you are:

    -
      -
    • Experiencing unexpected behaviour or degraded performance in your Vespa deployment
    • -
    • Preparing to open or respond to a Vespa support ticket
    • -
    • Asked by Vespa support to provide diagnostic information
    • -
    - - - -

    Accessing the Tool

    -

    - vespa-support is available in the shell of any running Vespa Enterprise container. - To access it, open a shell session into the container using your platform's standard method, - then verify the tool is available: -

    -
    -$ vespa support --help
    -
    -

    To confirm the tool version and the container version:

    -
    -$ vespa support version
    -
    - - - -

    Commands

    - -

    vespa support version

    -

    - Prints the version of the vespa-support tool and the Vespa Enterprise container version. -

    - -

    vespa support diagnostics sysinfo

    -

    - Collects host-level system information from the node where the command is run. - --dest-dir is required. The command will fail if it is not provided. -

    -
    -$ vespa support diagnostics sysinfo --dest-dir <output-directory>
    -
    - -

    vespa support diagnostics application

    -

    - Collects the currently deployed Vespa application state. - --dest-dir is required. The command will fail if it is not provided. -

    -
    -$ vespa support diagnostics application --dest-dir <output-directory>
    -
    - - - -

    Options

    -

    The following options apply to all diagnostics subcommands.

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    OptionRequiredDefaultDescription
    --dest-dirYesDirectory where the diagnostic output files will be written.
    --config-server-hostNoAuto-detectedHost address of the Vespa config server. If not set, the tool resolves it automatically from the environment.
    --config-server-portNo19071Port of the Vespa config server.
    --timeout-secsNo60Timeout in seconds for operations that contact the config server.
    - - - -

    What Is Collected

    - -

    sysinfo

    -

    - The output is written as a ZIP file named diag-sysinfo.zip in the specified destination directory. - It includes the following information about the host: -

    -
      -
    • Vespa version
    • -
    • Hostname, operating system, kernel version, and CPU architecture
    • -
    • CPU model and core count
    • -
    • Memory usage (total, free, and available)
    • -
    • Disk usage per mounted filesystem (size, free space, usage percentage)
    • -
    • Network interfaces, routing table, DNS servers, and firewall rules as seen from within the container
    • -
    • Host level diagnostics metrics
    • -
    - -

    application

    -

    - The output is written as a ZIP file named diag-application.zip in the specified destination directory. - It includes the following information about the application:

    -
      -
    • The full running Vespa application state
    • -
    • Application level diagnostics metrics
    • -
    - - - -

    Creating a Support Bundle

    -

    - The following steps walk you through collecting a complete set of diagnostic files - to share with the Vespa support team. -

    -

    1. Open a shell session into the Vespa Enterprise container.

    -

    2. Create a directory to store the output:

    -
    -$ mkdir /tmp/vespa-diagnostics
    -
    -

    3. Collect system information:

    -
    -$ vespa support diagnostics sysinfo --dest-dir /tmp/vespa-diagnostics
    -
    -

    4. Collect application diagnostics:

    -
    -$ vespa support diagnostics application --dest-dir /tmp/vespa-diagnostics
    -
    -

    5. Verify the output:

    -
    -$ ls /tmp/vespa-diagnostics
    -diag-sysinfo.zip
    -diag-application.zip
    -
    -

    - Once complete, copy the ZIP files from the container and attach them to your support ticket. -

    - - - -

    Sharing with Vespa Support

    -

    - Attach the ZIP files generated in the previous step to your Vespa support ticket. - If you have not yet opened a ticket, include the files when submitting your request. -

    -

    - If the files are too large to attach directly, the Vespa support team will provide an alternative upload method. -

    diff --git a/en/operations/self-managed/vespa-support.mdx b/en/operations/self-managed/vespa-support.mdx new file mode 100644 index 0000000000..edf5192e07 --- /dev/null +++ b/en/operations/self-managed/vespa-support.mdx @@ -0,0 +1,148 @@ +--- +title: "Vespa Support CLI" +--- + +The Vespa Enterprise image includes a built-in command-line tool called `vespa-support`. It is designed to simplify and expedite the process of collecting diagnostic information from your system, making it easier to request and receive support from the Vespa team. + +You should run this tool whenever you are: + +- Experiencing unexpected behaviour or degraded performance in your Vespa deployment +- Preparing to open or respond to a Vespa support ticket +- Asked by Vespa support to provide diagnostic information + +## Accessing the Tool + +`vespa-support` is available in the shell of any running Vespa Enterprise container. To access it, open a shell session into the container using your platform's standard method, then verify the tool is available: + +```bash +$ vespa support --help +``` + +To confirm the tool version and the container version: + +```bash +$ vespa support version +``` + +## Commands + +### vespa support version + +Prints the version of the `vespa-support` tool and the Vespa Enterprise container version. + +### vespa support diagnostics sysinfo + +Collects host-level system information from the node where the command is run. `--dest-dir` is required. The command will fail if it is not provided. + +```bash +$ vespa support diagnostics sysinfo --dest-dir +``` + +### vespa support diagnostics application + +Collects the currently deployed Vespa application state. `--dest-dir` is required. The command will fail if it is not provided. + +```bash +$ vespa support diagnostics application --dest-dir +``` + +## Options + +The following options apply to all `diagnostics` subcommands. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    OptionRequiredDefaultDescription
    {`--dest-dir`}YesDirectory where the diagnostic output files will be written.
    {`--config-server-host`}NoAuto-detectedHost address of the Vespa config server. If not set, the tool resolves it automatically from the environment.
    {`--config-server-port`}No19071Port of the Vespa config server.
    {`--timeout-secs`}No60Timeout in seconds for operations that contact the config server.
    + +## What Is Collected + +### sysinfo + +The output is written as a ZIP file named `diag-sysinfo.zip` in the specified destination directory. It includes the following information about the host: + +- Vespa version +- Hostname, operating system, kernel version, and CPU architecture +- CPU model and core count +- Memory usage (total, free, and available) +- Disk usage per mounted filesystem (size, free space, usage percentage) +- Network interfaces, routing table, DNS servers, and firewall rules as seen from within the container +- Host level diagnostics metrics + +### application + +The output is written as a ZIP file named `diag-application.zip` in the specified destination directory. It includes the following information about the application: + +- The full running Vespa application state +- Application level diagnostics metrics + +## Creating a Support Bundle + +The following steps walk you through collecting a complete set of diagnostic files to share with the Vespa support team. + + + + +```bash +$ mkdir /tmp/vespa-diagnostics +``` + + +```bash +$ vespa support diagnostics sysinfo --dest-dir /tmp/vespa-diagnostics +``` + + +```bash +$ vespa support diagnostics application --dest-dir /tmp/vespa-diagnostics +``` + + + +```bash +$ ls /tmp/vespa-diagnostics +diag-sysinfo.zip +diag-application.zip +``` + +Once complete, copy the ZIP files from the container and attach them to your support ticket. + + + +## Sharing with Vespa Support + +Attach the ZIP files generated in the previous step to your Vespa support ticket. If you have not yet opened a ticket, include the files when submitting your request. + +If the files are too large to attach directly, the Vespa support team will provide an alternative upload method. \ No newline at end of file diff --git a/en/operations/sso.md b/en/operations/sso.md deleted file mode 100644 index 16c3013876..0000000000 --- a/en/operations/sso.md +++ /dev/null @@ -1,47 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: Single Sign-On -applies_to: cloud ---- - -Single Sign-On (SSO) is available for Vespa Cloud customers on the [Enterprise plan](https://vespa.ai/pricing/). -It is powered by Auth0's Self-Service Enterprise Configuration and supports the following identity provider connectors: - -- **Generic SAML** for any SAML 2.0 compliant identity provider -- **Okta Workforce Identity OIDC** for organizations using Okta as their identity provider - -Once SSO is active, users signing in with an email address on the configured domain are automatically redirected to your identity provider. - -## Setup process - -SSO setup requires involvement from both the customer and the Vespa Support team. -The overall flow is: - -1. **Initiate:** Contact [Vespa Support](https://vespa.ai/support/) or your account manager to request SSO setup. - Include your Vespa Cloud tenant name in the request. - -2. **Receive self-service URL:** Vespa Support will provide you with a self-service configuration URL for your tenant. - -3. **Validate your domain:** In the self-service portal, you will be given a DNS TXT record to add to your domain. - This proves ownership of the email domain that will be used for SSO. - Propagation may take up to 48 hours depending on your DNS provider. - -4. **Configure your identity provider:** The self-service portal guides you through the connector-specific setup. - Follow the on-screen instructions to configure either Generic SAML or Okta OIDC in your identity provider. - -5. **Confirm completion:** Once you have completed the configuration, notify Vespa Support. - -6. **Activation:** Vespa Support will verify the setup and activate the SSO connection for your tenant. - -## After activation - -Users authenticating with an email address belonging to your validated domain will be automatically redirected -to your identity provider when signing in to Vespa Cloud. - -Users who previously authenticated with username/password or other methods will be required to -authenticate through SSO going forward. - -## Getting help - -For questions or issues during setup, reach out to [Vespa Support](https://vespa.ai/support/) -or contact your account manager. diff --git a/en/operations/telemetry-export.html b/en/operations/telemetry-export.html deleted file mode 100644 index ce354222b9..0000000000 --- a/en/operations/telemetry-export.html +++ /dev/null @@ -1,376 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: Telemetry export -applies_to: cloud ---- - -

    - Telemetry export lets you ship your application's metrics and logs from Vespa Cloud directly to your own - observability backend — a fully self-service, push-based alternative to the pull-based - Prometheus metrics API. - You declare one or more exporters in - services.xml; Vespa Cloud then runs a - collector on your hosts that scrapes the selected metrics, tails the selected logs, and pushes them to the - backend(s) you configure, authenticated with credentials from your vault. Configure your vault and secrets - using the Vespa secret store, grant infrastructure access, add - the telemetry exporter configuration, deploy, and telemetry starts flowing in minutes. -

    - -{% include important.html content='Telemetry export is available for -Vespa Cloud Enclave only.' %} - - -

    Exporter

    -

    - An exporter is a single export target: it defines one backend destination, how to authenticate - to it, and which signals (metrics and/or logs) to send there. It is the - exporter concept from the - OpenTelemetry Collector — the component that delivers telemetry to a specific destination. Configure - up to three exporters to fan your telemetry out to multiple backends — each with its - own destination, authentication, metric set, and log selection. -

    - - -

    How it works

    -

    - On deploy, Vespa Cloud provisions and runs a dedicated - OpenTelemetry collector on each of your Enclave hosts - — as of now, Grafana Alloy — running - isolated from Vespa's own observability so a failure in your export pipeline cannot affect your nodes. - For each exporter, the system generates a collector configuration that scrapes the configured metric sets - from the internal Vespa metrics endpoint, tails the selected log files, and enriches each metric point and - log line with labels such as hostname, parent hostname, and zone. The collector then batches the data and - pushes it to your backend over HTTPS. -

    -

    - A metric set is a named selection of Vespa metrics, - and is the unit the collector exports: the OpenTelemetry collector consumes the metric set(s) referenced by - each exporter. To limit or customize which metrics are exported — rather than exporting a predefined - set such as default — define your own metric set and configure the exporter to collect - it. -

    -

    - Authentication tokens for your telemetry backend stay in your - Vespa secret store vault and are referenced from - services.xml by name only — never embedded in the application package. They are resolved - securely and used solely to authenticate the collector to your backend. To enable this, grant - infrastructure access to the vault once for your Enclave cloud account: the collector reads your - secret once to set up the export, so the Vespa infrastructure needs read access to it. This access is given - to the platform's identity, not to people — having access to a host does not let anyone read your - secret. It stays within your own cloud account, is read-only, and is used only to set up the collector, so - your secret is never exposed to others, including Vespa operators. -

    -Telemetry export architecture: a collector on the Vespa host scrapes metrics
-          and tails logs from the tenant container node and pushes them to one or more external backends. - -

    Labels

    -

    - Exported metrics and logs are labeled with metadata identifying where they came from, so you can filter and - group them by host, application, zone, cluster, and cloud in your backend. -

    -

    - Metrics carry Vespa's standard metric labels (such as applicationId, - clusterid, clustertype, vespa_service), and the pipeline ensures - these are present: -

    -
      -
    • host — the node's hostname.
    • -
    • parentHostname — the host the node runs on.
    • -
    • zone — the zone, as <environment>.<region>.
    • -
    • system — the Vespa system (for example publiccd).
    • -
    -

    - Logs — each log line is labeled with these resource attributes, following - OpenTelemetry semantic conventions: -

    -
      -
    • ai.vespa.system — the Vespa system.
    • -
    • ai.vespa.zone — the zone.
    • -
    • ai.vespa.instance — the application instance - (<tenant>.<application>.<instance>).
    • -
    • ai.vespa.node — the Vespa node name.
    • -
    • host.name — the host the node runs on.
    • -
    • ai.vespa.cluster — the cluster name.
    • -
    • ai.vespa.cluster_type — the cluster type (container, - content, …).
    • -
    • ai.vespa.group — the content group.
    • -
    • log.file.name — the source log file.
    • -
    -

    Log lines are additionally parsed into structured fields such as timestamp, level, and component.

    -

    - Cloud attributes — for both metrics and logs, the collector detects the host's cloud - environment and adds standard cloud.* and host.* resource attributes, such as - cloud.provider, cloud.account.id, cloud.region, - cloud.availability_zone, host.id, and host.type. -

    - - -

    Before you begin

    -

    Complete the following before configuring telemetry export:

    -
      -
    1. Confirm your application runs on - Vespa Cloud Enclave.
    2. -
    3. In the Vespa secret store, create a vault and add the - authentication secret(s) your backend requires.
    4. -
    5. Grant infrastructure access to the vault for your Enclave cloud account, so the Vespa hosts - can read the secret.
    6. -
    -{% include note.html content='A googlecloud exporter authenticates with the host's workload -identity instead of a vault secret. In your Google Cloud project, enable the Cloud Monitoring and Cloud -Logging APIs, and grant the Enclave host service account the roles/monitoring.metricWriter -(metrics) and roles/logging.logWriter (logs) roles. Without these, the exporter runs but no data -is recorded.' %} - - -

    Configuration

    -

    - Telemetry export is configured under <admin version="4.0"><telemetry> in - services.xml. Each <exporter> - maps to a Grafana Alloy - exporter component and is - configured with the attributes and child elements below. At most three exporters may be configured per - application. -

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    Attribute / elementRequiredDescription
    idYesA unique identifier for the exporter.
    typeYesThe exporter type: otlp, otlphttp, or googlecloud. Determines - the Alloy exporter component the exporter maps to.
    endpointFor otlp / otlphttpThe telemetry backend URL.
    projectFor googlecloudThe Google Cloud project ID. Not used by otlp / otlphttp.
    <auth>If the backend requires authenticationAuthenticates the exporter to your backend.
    <metric-set>To export metricsSelects a set of metrics to export.
    <logs>To export logsSelects the log files to export.
    - -

    Authentication — <auth>

    -

    - For otlp / otlphttp exporters whose backend requires authentication, add an - <auth> element with exactly one of the methods below. Each references a - vault and the secret name(s) holding the credential; the secret - values themselves stay in the vault. A googlecloud exporter does not use <auth>. -

    - - - - - - - - - - - - - - - - - - - - - -
    MethodAttributesSends
    <bearer-token>vault, secret-nameAuthorization: Bearer <token>
    <api-key>vault, secret-name, headerThe secret value in the header named by header.
    <basic-auth>vault, username-secret-name, password-secret-nameAn HTTP Basic Authorization header.
    - -

    Metrics — <metric-set>

    -

    - Add a <metric-set> for each metric set to export, referenced by id. Use a - predefined set such as default, or a custom set defined as a - metrics consumer. An exporter may reference several - metric sets. -

    - -

    Logs — <logs>

    -

    - Add a <logs> element containing one or more <type> entries, with - id set to container-logs (the application's Vespa log) and/or - access-logs (the container access log). -

    - - -

    Examples

    - -

    Define a custom metric set and export it by id:

    -
    {% highlight xml %}
    -
    -  
    -    
    -      
    -      
    -    
    -  
    -  
    -    
    -      
    -        
    -      
    -      
    -    
    -  
    -
    -{% endhighlight %}
    - -

    OTLP over HTTP with a bearer token, exporting both metrics and logs:

    -
    {% highlight xml %}
    -
    -  
    -    
    -      
    -        
    -      
    -      
    -      
    -        
    -        
    -      
    -    
    -  
    -
    -{% endhighlight %}
    - -

    API key in a custom header:

    -
    {% highlight xml %}
    -
    -  
    -    
    -  
    -  
    -
    -{% endhighlight %}
    - -

    Basic authentication:

    -
    {% highlight xml %}
    -
    -  
    -    
    -  
    -  
    -
    -{% endhighlight %}
    - -

    Google Cloud — metrics to Cloud Monitoring, logs to Cloud Logging - (no <auth>; see Before you begin for the required project setup):

    -
    {% highlight xml %}
    -
    -  
    -  
    -    
    -  
    -
    -{% endhighlight %}
    - -

    Different backends per environment, using - deployment variants:

    -
    {% highlight xml %}
    -
    -  
    -    
    -      
    -        
    -          
    -        
    -        
    -      
    -      
    -        
    -          
    -        
    -        
    -      
    -    
    -  
    -
    -{% endhighlight %}
    - - -

    Operations and troubleshooting

    -

    - By default, each exporter also sends the collector's own self-metrics to your backend, alongside your - application metrics and logs. Use these to observe the export pipeline itself: include whichever are - relevant in your dashboards, and define alerts both on their values and on missing data — - absent self-metrics can indicate the collector is no longer running. -

    -

    Recommended metrics to consume:

    -
      -
    • Metric points sent — otelcol_exporter_sent_metric_points_total
    • -
    • Metric points that failed to send — otelcol_exporter_send_failed_metric_points_total
    • -
    • Log records sent — otelcol_exporter_sent_log_records_total
    • -
    • Log records that failed to send — otelcol_exporter_send_failed_log_records_total
    • -
    • Collector CPU and memory usage — alloy_resources_process_cpu_seconds_total, - alloy_resources_process_resident_memory_bytes
    • -
    • Configuration load success — alloy_config_last_load_successful
    • -
    • Running components — alloy_component_controller_running_components
    • -
    • Collector start time, to detect restarts — - alloy_resources_process_start_time_seconds
    • -
    -

    - Vespa Cloud also monitors the collector's health and export success on its side and raises alarms on - failures, such as the collector failing to start or persistent export errors. -

    -

    - If a deployment fails, make sure the vault exists and that - infrastructure access to it has been granted for your Enclave cloud account. -

    -

    - Operating the telemetry backend is your responsibility. If the endpoint rejects incoming data or becomes - unavailable, the collector retries and buffers the data for a limited period, so brief interruptions are - tolerated. A prolonged outage, however, exhausts the queue, and the affected telemetry — including the - collector's self-metrics, which are delivered over the same path — is then permanently lost. We - recommend monitoring the endpoint's availability independently and ensuring it remains in a healthy, - operational state to prevent telemetry loss. -

    -

    - Independently of telemetry export, application metrics remain available at all times in the - Vespa Cloud Console, which you can rely on as a fallback should - the export pipeline be unavailable. -

    - - -

    Supported signals and limitations

    -
      -
    • Metrics and logs are supported.
    • -
    • Traces are not supported yet.
    • -
    • Export is customizable by metric set and by log file. Additional customizations and transformations in - the telemetry pipeline are not supported at this time. If you need additional capabilities, let us know - through support.
    • -
    diff --git a/en/operations/zones.html b/en/operations/zones.html deleted file mode 100644 index 007b7ae723..0000000000 --- a/en/operations/zones.html +++ /dev/null @@ -1,96 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: Zones -applies_to: cloud -redirect_from: -- /en/cloud/zones.html ---- - -

    - An application instance can be deployed to one or more zones. A zone in this documentation - refers to a Vespa zone, the basic target location for the deployment of an application instance. A zone is - identified by an environment and a region identifier, for example - prod.aws-us-east-1c. This zone can be found in the below list of zones in Vespa Cloud. -

    -

    - A zone supports one or more availability zones. - The prod.aws-us-east-1c zone is in the AWS availability zone with AZ ID use1-az6. - Zones in Azure and GCP support the availability zone given by the region identifier, for example - prod.gcp-europe-west3-b is in the GCP availability zone europe-west3-b. - You can read more about zones supporting more than one availability zones in regional zones. -

    -

    - If an application requires zone-specific configuration (e.g., different capacity requirements per zone), - use - environment and region variants. - Also see deployment.xml. -

    -

    dev zones for development and performance testing:

    - - - - - - - - - - - -
    EnvironmentDefaultRegionAvailability Zones
    devYesaws-us-east-1cuse1-az6
    devNoaws-euw1-az1euw1-az1
    devNoazure-eastus-az1
    devNogcp-us-central1-a
    devNogcp-us-central1-f
    - -

    - prod zones for production serving, - with a CD pipeline: -

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    EnvironmentRegionAvailability Zones
    prodaws-us-east-1use1-az1 to use1-az6, except use1-az3
    prodaws-us-east-1cuse1-az6
    prodaws-use1-az4use1-az4
    prodaws-use2-az1use2-az1
    prodaws-use2-az3use2-az3
    prodaws-us-west-2ausw2-az1
    prodaws-usw2-az3usw2-az3
    prodaws-eu-west-1euw1-az1 to euw1-az3
    prodaws-eu-west-1aeuw1-az2
    prodaws-euw1-az1euw1-az1
    prodaws-euc1-az1euc1-az1
    prodaws-euc1-az3euc1-az3
    prodaws-eun1-az1eun1-az1
    prodaws-cac1-az1cac1-az1
    prodaws-cac1-az2cac1-az2
    prodaws-aps1-az1aps1-az1
    prodaws-ap-northeast-1aapne1-az4
    prodaws-apne1-az1apne1-az1
    prodgcp-europe-west3-b
    prodgcp-us-central1-a
    prodgcp-us-central1-b
    prodgcp-us-central1-c
    prodgcp-us-central1-f
    prodgcp-us-west1-c
    -

    - The prod zones use ephemeral instances for system tests and staging tests, - running in test and - staging environments. - These are internal zones, and never directly deployed to, included here for reference: -

    - - - - - - - - - - -
    EnvironmentRegionAvailability Zones
    testaws-us-east-1cuse1-az6
    testgcp-us-central1-f
    stagingaws-us-east-1cuse1-az6
    staginggcp-us-central1-f
    - -

    Contact Support to request more zones.

    diff --git a/en/operations/zones.mdx b/en/operations/zones.mdx new file mode 100644 index 0000000000..85001645fe --- /dev/null +++ b/en/operations/zones.mdx @@ -0,0 +1,196 @@ +--- +title: "Zones" +--- + +An application is deployed to a *zone*, which is a combination of an [environment](/en/operations/environments) and a *region*, like `vespa deploy -z dev.aws-us-east-1c`. + +If an application requires zone-specific configuration (e.g., different capacity requirements per zone), use [environment and region variants](/en/operations/deployment-variants#services.xml-variants). Also see [deployment.xml](/en/reference/applications/deployment). + +`dev` zones for development and performance testing: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    EnvironmentDefaultRegionAWS Zone ID
    devYesaws-us-east-1cuse1-az6
    devNoaws-euw1-az1euw1-az1
    devNoazure-eastus-az1
    devNogcp-us-central1-f
    + +`prod` zones for production serving, with a [CD pipeline](/en/operations/automated-deployments): + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    EnvironmentRegionAWS Zone ID
    prodaws-us-east-1cuse1-az6
    prodaws-use1-az4use1-az4
    prodaws-use2-az1use2-az1
    prodaws-use2-az3use2-az3
    prodaws-us-west-2ausw2-az1
    prodaws-usw2-az3usw2-az3
    prodaws-eu-west-1aeuw1-az2
    prodaws-euw1-az1euw1-az1
    prodaws-euc1-az1euc1-az1
    prodaws-euc1-az3euc1-az3
    prodaws-cac1-az1cac1-az1
    prodaws-cac1-az2cac1-az2
    prodaws-aps1-az1aps1-az1
    prodaws-ap-northeast-1aapne1-az4
    prodaws-apne1-az1apne1-az1
    prodgcp-europe-west3-b
    prodgcp-us-central1-a
    prodgcp-us-central1-b
    prodgcp-us-central1-c
    prodgcp-us-central1-f
    + +The `prod` zones use ephemeral instances for system tests and staging tests, running in [test](/en/operations/environments#test) and [staging](/en/operations/environments#staging) environments. These are internal zones, and never directly deployed to, included here for reference: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    EnvironmentRegionAWS Zone ID
    testaws-us-east-1cuse1-az6
    testgcp-us-central1-f
    stagingaws-us-east-1cuse1-az6
    staginggcp-us-central1-f
    + +Contact [Support](https://vespa.ai/support/) to request more zones. \ No newline at end of file diff --git a/en/performance.mdx b/en/performance.mdx new file mode 100644 index 0000000000..69b50ae604 --- /dev/null +++ b/en/performance.mdx @@ -0,0 +1,36 @@ +--- +title: "Performance" +sidebarTitle: "Performance overview" +--- +## Practical search performance guide + +See [practical search performance guide](/en/performance/practical-search-performance-guide). The guide walks through a music search use case and gives a practical introduction to Vespa search performance. + +## Sizing and capacity planning + +Sizing and capacity planning involves figuring out how many nodes are needed and what kind of hardware flavor best fits the use case: + +- [Sizing Vespa search](/en/performance/sizing-search): How to size a Vespa search cluster +- [Caching in Vespa](/en/performance/caches-in-vespa): How to enable caches in Vespa +- [Attributes and memory usage](/en/content/attributes): How attributes impact the memory footprint, find attribute memory usage +- [Proton maintenance jobs](/en/content/proton#proton-maintenance-jobs): Impact on resource usage +- [Coverage degradation](/en/performance/graceful-degradation): Timeout handling and Degraded Coverage + +## Benchmarking and tuning + +Benchmarking is important both during sizing and for testing new features. What tools to use for benchmarking and how to tune system aspects of Vespa: + +- [Benchmarking Vespa](/en/performance/benchmarking): Test Vespa performance +- [Search features and performance](/en/performance/feature-tuning): Test Vespa performance +- [Feed performance](/en/performance/sizing-feeding): Test Vespa performance +- [Container Http performance testing using Gatling](/en/performance/container-http): Test Vespa performance +- [Container tuning](/en/performance/container-tuning): JVM, container, docproc +- [vespa-fbench](/en/reference/operations/tools#vespa-fbench): Reference documentation +- [HTTP/2](/en/performance/http2): improve HTTP performance using HTTP/2 + +## Profiling + +Do a deep performance analysis - how to profile the application as well as Vespa: + +- [Profiling](/en/performance/profiling): Generic profiling tips. +- [Valgrind](/en/performance/valgrind): Run Vespa with Valgrind diff --git a/en/performance/benchmarking-cloud.html b/en/performance/benchmarking-cloud.html deleted file mode 100644 index 09b81f5ff1..0000000000 --- a/en/performance/benchmarking-cloud.html +++ /dev/null @@ -1,322 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: Benchmarking -applies_to: cloud -redirect_from: -- /en/cloud/benchmarking.html ---- - -

    - This is a step-by-step guide to get started with benchmarking on Vespa Cloud, - based on the Vespa benchmarking guide, - using the sample app. - Overview: -

    -Vespa Cloud Benchmarking - - - -

    Set up a performance test instance

    -

    - Use an instance in a dev zone for benchmarks. - To deploy an instance there, use the getting started guide, - and make sure to specify the resources using a deploy:environment="dev" attribute: -

    -
    {% highlight xml %}
    -
    -    
    -
    -{% endhighlight %}
    -
    -$ vespa deploy --wait 600
    -
    -

    Feed documents:

    -
    -$ vespa feed ext/documents.jsonl
    -
    -

    Query documents to validate the feed:

    -
    -$ vespa query "select * from music where true"
    -
    -

    Query documents using curl:

    -
    -$ curl \
    -  --cert ~/.vespa/mytenant.myapp.default/data-plane-public-cert.pem \
    -  --key  ~/.vespa/mytenant.myapp.default/data-plane-private-key.pem \
    -  -H "Content-Type: application/json" \
    -  --data '{"yql" : "select * from music where true"}' \
    -  https://baaae1db.b68ddc0d.z.vespa-app.cloud/search/
    -
    -

    At this point, the instance is ready, with data, and can be queried using data-plane credentials.

    - - -

    Test using vespa-fbench with a token endpoint

    - -

    To test using an API token endpoint instead of mTLS, use these options:

    - -
    -$ vespa-fbench -q query.txt -s 5 -H "Authorization: Bearer [your token]" -T /etc/ssl/certs/ca-bundle.crt -o out.txt "[token-endpoint]" 443
    -
    - -

    ca-bundle.crt is just the root certificates, nothing Vespa specific.

    - - -

    Test using vespa-fbench

    - -

    The rest of the guide assumes we're using an mTLS endpoint and have data-plane credentials are in working directory:

    -
    -$ ls -1 *.pem
    -  data-plane-private-key.pem
    -  data-plane-public-cert.pem
    -
    -

    Prepare a query file:

    -
    -$ echo "/search/?yql=select+*+from+music+where+true" > query001.txt
    -
    -

    - Test using - vespa-fbench - running in a docker container: -

    -
    -$ docker run -v $(pwd):/files -w /files \
    -    --entrypoint /opt/vespa/bin/vespa-fbench \
    -    vespaengine/vespa \
    -    -C data-plane-public-cert.pem \
    -    -K data-plane-private-key.pem \
    -    -T /etc/ssl/certs/ca-bundle.crt \
    -    -n 1 -q query001.txt -s 1 -c 0 \
    -    -o output.txt \
    -    baaae1db.b68ddc0d.z.vespa-app.cloud 443
    -
    -

    - -o output.txt is useful when validating the test - remove this option when load testing. - Make sure there are no SSL_do_handshake errors in the output. - Expect HTTP status code 200: -

    -
    -  Starting clients...
    -  Stopping clients
    -  Clients stopped.
    -  .
    -  Clients Joined.
    -  *** HTTP keep-alive statistics ***
    -  connection reuse count -- 4
    -  ***************** Benchmark Summary *****************
    -  clients:                       1
    -  ran for:                       1 seconds
    -  cycle time:                    0 ms
    -  lower response limit:          0 bytes
    -  skipped requests:              0
    -  failed requests:               0
    -  successful requests:           5
    -  cycles not held:               5
    -  minimum response time:    128.17 ms
    -  maximum response time:    515.35 ms
    -  average response time:    206.38 ms
    -  25 percentile:            128.70 ms
    -  50 percentile:            129.60 ms
    -  75 percentile:            130.20 ms
    -  90 percentile:            361.32 ms
    -  95 percentile:            438.36 ms
    -  99 percentile:            499.99 ms
    -  actual query rate:          4.80 Q/s
    -  utilization:               99.03 %
    -  zero hit queries:              5
    -  http request status breakdown:
    -    200 :        5
    -
    -

    At this point, running queries using vespa-fbench works well from local laptop.

    - - - -

    Run queries inside data center

    -

    - Next step is to run this from the same location (data center) as the dev zone. - In this example, an AWS zone. - Deduce the AWS zone from Vespa Cloud zone name. - Below is an example using a host with Amazon Linux 2023 AMI (HVM) image: -

    -
      -
    1. -

      - Create the host - here assume key pair is named key.pem. - No need to do anything other than default. -

      -
    2. -
    3. -

      Log in, update, install docker:

      -
      -$ ssh -i key.pem ec2-user@ec2-xx-xxx-xxx-xxx.compute-1.amazonaws.com
      -[ec2-user]$ sudo yum update -y
      -[ec2-user]$ sudo yum install -y docker
      -[ec2-user]$ sudo service docker start
      -[ec2-user]$ sudo usermod -a -G docker ec2-user
      -[ec2-user]$ exit
      -
      -
    4. -
    5. -

      - Copy credentials for endpoint access, log in and validate docker setup: -

      -
      -$ scp -i key.pem data-plane-private-key.pem ec2-user@ec2-xx-xxx-xxx-xxx.compute-1.amazonaws.com:
      -$ scp -i key.pem data-plane-public-cert.pem ec2-user@ec2-xx-xxx-xxx-xxx.compute-1.amazonaws.com:
      -
      -$ ssh -i key.pem ec2-user@ec2-xx-xxx-xxx-xxx.compute-1.amazonaws.com
      -[ec2-user]$ docker info
      -
      -
    6. -
    7. -

      Make a dummy query:

      -
      -[ec2-user]$ echo "/search/?yql=select+*+from+music+where+true" > query001.txt
      -
      -
    8. -
    9. -

      Run vespa-fbench and verify 200 response:

      -
      -[ec2-user]$ docker run -v $(pwd):/files -w /files \
      -              --entrypoint /opt/vespa/bin/vespa-fbench \
      -              vespaengine/vespa \
      -              -C data-plane-public-cert.pem \
      -              -K data-plane-private-key.pem \
      -              -T /etc/ssl/certs/ca-bundle.crt \
      -              -n 1 -q query001.txt -s 1 -c 0 \
      -              baaae1db.b68ddc0d.z.vespa-app.cloud 443
      -
      -
    10. -
    -

    - At this point, you are able to benchmark using vespa-fbench in the same zone as the Vespa Cloud dev instance. -

    - - - -

    Run benchmark

    -

    - Use the Vespa Benchmarking Guide - to plan and run benchmarks. - Also see sizing below. - Make sure the client running the benchmark tool has sufficient resources. -

    -

    Export metrics:

    -
    -$ curl \
    -    --cert data-plane-public-cert.pem \
    -    --key data-plane-private-key.pem \
    -    https://baaae1db.b68ddc0d.z.vespa-app.cloud/prometheus/v1/values
    -
    -

    Notes:

    -
      -
    • Periodically dump all metrics using consumer=Vespa.
    • -
    • - Make sure you will not exhaust your serving threads on your container nodes while in production. This can be verified by making - sure this expression stays well below 100% (typically below 50%) for the traffic you expect: - 100 * (jdisc.thread_pool.active_threads.sum / jdisc.thread_pool.active_threads.count) / jdisc.thread_pool.size.max - for each threadpool value. You can increase the number of threads in the pools by using larger container nodes, - more container nodes or by tuning the number of threads as described in - services-search. - In the case you do exhaust a threadpool and its queue you will experience HTTP 503 responses for requests that are rejected by - the container. -
    • -
    - - - -

    Making changes

    -

    - Whenever deploying changes to configuration, track progress in the Deployment dashboard. - Some changes, like changing - requestthreads - will restart content nodes, and this is done in sequence and takes time. - Wait for successful completion in Wait for services and endpoints to come online. -

    -

    - When changing node type/count, wait for auto data redistribution to complete, - watching the vds.idealstate.merge_bucket.pending.average metric: -

    -
    -$ while true; do curl -s \
    -    --cert data-plane-public-cert.pem \
    -    --key data-plane-private-key.pem \
    -    https://baaae1db.b68ddc0d.z.vespa-app.cloud/prometheus/v1/values?consumer=Vespa | \
    -    grep idealstate.merge_bucket.pending.average; \
    -    sleep 10; done
    -
    -

    Notes:

    -
      -
    • Dump all metrics using consumer=Vespa.
    • -
    • - After changing the number of content nodes, this metric will jump, then decrease (not necessarily linearly) - - speed depending on data volume. -
    • -
    - - - -

    Sizing

    -

    -Using Vespa Cloud enables the Vespa Team to assist you to optimise the application to reduce resource spend. -Based on 150 applications running on Vespa Cloud today, savings are typically 50%. -Cost optimization is hard to do without domain knowledge - -but few teams are experts in both their application and its serving platform. -Sizing means finding both the right node size and the right cluster topology: -

    -

    -Resize to fewer and smaller nodes -

    -

    -Applications use Vespa for their primary business use cases. -Availability and performance vs. cost are business decisions. -The best sized application can handle all expected load situations, -and is configured to degrade quality gracefully for the unexpected. -

    -

    -Even though Vespa is cost-efficient out of the box, -Vespa experts can usually spot over/under-allocations in CPU, memory and disk space/IO, -and discuss trade-offs with the application team. -

    -

    -Using automated deployments applications go live with little risk. -After launch, right-size the application based on true load after using Vespa’s elasticity features -with automated data migration. -

    -

    -Use the Vespa sizing guide -to size the application and find metrics used there. Pro-tips: -

    -
      -
    • 60% is a good max memory allocation
    • -
    • 50% is a good max CPU allocation, although application dependent.
    • -
    • 70% is a good max disk allocation
    • -
    -

    Rules of thumb:

    -
      -
    • - Memory and disk scales approximately linearly for indexed fields' data - - attributes have a fixed cost for empty fields. -
    • -
    • Data variance will impact memory usage.
    • -
    • Undersized instances will - block writes.
    • -
    • - If is often a good idea to use the dev zone to test memory impact of adding large fields, - e.g. adding an embedding. -
    • -
    - - -

    Notes

    -
      -
    • - The user running benchmarks must have read access to the endpoint - - if you already have, you can skip this section. - Refer to the Vespa security guide. -
    • -
    • Monitoring is useful to track metrics when benchmarking.
    • -
    diff --git a/en/performance/benchmarking-cloud.mdx b/en/performance/benchmarking-cloud.mdx new file mode 100644 index 0000000000..aaf53dd7d1 --- /dev/null +++ b/en/performance/benchmarking-cloud.mdx @@ -0,0 +1,252 @@ +--- +title: "Benchmarking" +sidebarTitle: "Benchmarking using Vespa Cloud" +--- + +This is a step-by-step guide to get started with benchmarking on Vespa Cloud, based on the [Vespa benchmarking guide](/en/performance/benchmarking), using the [sample app](https://github.com/vespa-engine/sample-apps/tree/master/album-recommendation). Overview: + +![Vespa Cloud Benchmarking](/assets/img/cloud-benchmarks.svg) + +## Set up a performance test instance + +Use an instance in a [dev zone](/en/operations/environments#dev) for benchmarks. To deploy an instance there, use the [getting started](/en/basics/deploy-an-application) guide, and make sure to specify the resources using a `deploy:environment="dev"` attribute: + +```xml + + + +``` + +```bash +$ vespa deploy --wait 600 +``` + +Feed documents: + +```bash +$ vespa feed ext/documents.jsonl +``` + +Query documents to validate the feed: + +```bash +$ vespa query "select * from music where true" +``` + +Query documents using curl: + +```bash +$ curl \ + --cert ~/.vespa/mytenant.myapp.default/data-plane-public-cert.pem \ + --key ~/.vespa/mytenant.myapp.default/data-plane-private-key.pem \ + -H "Content-Type: application/json" \ + --data '{"yql" : "select * from music where true"}' \ + https://baaae1db.b68ddc0d.z.vespa-app.cloud/search/ +``` + +At this point, the instance is ready, with data, and can be queried using data-plane credentials. + +## Test using vespa-fbench with a token endpoint + +To test using an API token endpoint instead of mTLS, use these options: + +```bash +$ vespa-fbench -q query.txt -s 5 -H "Authorization: Bearer [your token]" -T /etc/ssl/certs/ca-bundle.crt -o out.txt "[token-endpoint]" 443 +``` + +ca-bundle.crt is just the root certificates, nothing Vespa specific. + +## Test using vespa-fbench + +The rest of the guide assumes we're using an mTLS endpoint and have data-plane credentials are in working directory: + +```bash +$ ls -1 \*.pem + data-plane-private-key.pem + data-plane-public-cert.pem +``` + +Prepare a query file: + +```bash +$ echo "/search/?yql=select+*+from+music+where+true" > query001.txt +``` + +Test using [vespa-fbench](/en/reference/operations/tools#vespa-fbench) running in a docker container: + +```bash +$ docker run -v $(pwd):/files -w /files \ + --entrypoint /opt/vespa/bin/vespa-fbench \ + vespaengine/vespa \ + -C data-plane-public-cert.pem \ + -K data-plane-private-key.pem \ + -T /etc/ssl/certs/ca-bundle.crt \ + -n 1 -q query001.txt -s 1 -c 0 \ + -o output.txt \ + baaae1db.b68ddc0d.z.vespa-app.cloud 443 +``` + +`-o output.txt` is useful when validating the test - remove this option when load testing. Make sure there are no `SSL_do_handshake` errors in the output. Expect HTTP status code 200: + +```bash expandable + Starting clients... + Stopping clients + Clients stopped. + . + Clients Joined. + *** HTTP keep-alive statistics *** + connection reuse count -- 4 + ***************** Benchmark Summary ***************** + clients: 1 + ran for: 1 seconds + cycle time: 0 ms + lower response limit: 0 bytes + skipped requests: 0 + failed requests: 0 + successful requests: 5 + cycles not held: 5 + minimum response time: 128.17 ms + maximum response time: 515.35 ms + average response time: 206.38 ms + 25 percentile: 128.70 ms + 50 percentile: 129.60 ms + 75 percentile: 130.20 ms + 90 percentile: 361.32 ms + 95 percentile: 438.36 ms + 99 percentile: 499.99 ms + actual query rate: 4.80 Q/s + utilization: 99.03 % + zero hit queries: 5 + http request status breakdown: + 200 : 5 +``` + +At this point, running queries using *vespa-fbench* works well from local laptop. + +## Run queries inside data center + +Next step is to run this from the same location (data center) as the dev zone. In this example, an AWS [zone](/en/operations/zones). Deduce the AWS zone from Vespa Cloud zone name. Below is an example using a host with Amazon Linux 2023 AMI (HVM) image: + + + +Create the host - here assume key pair is named *key.pem*. No need to do anything other than default. + + +Log in, update, install docker: + +```bash +$ ssh -i key.pem ec2-user@ec2-xx-xxx-xxx-xxx.compute-1.amazonaws.com +[ec2-user]$ sudo yum update -y +[ec2-user]$ sudo yum install -y docker +[ec2-user]$ sudo service docker start +[ec2-user]$ sudo usermod -a -G docker ec2-user +[ec2-user]$ exit +``` + + +Copy credentials for endpoint access, log in and validate docker setup: + +```bash +$ scp -i key.pem data-plane-private-key.pem ec2-user@ec2-xx-xxx-xxx-xxx.compute-1.amazonaws.com: +$ scp -i key.pem data-plane-public-cert.pem ec2-user@ec2-xx-xxx-xxx-xxx.compute-1.amazonaws.com: + +$ ssh -i key.pem ec2-user@ec2-xx-xxx-xxx-xxx.compute-1.amazonaws.com +[ec2-user]$ docker info +``` + + +Make a dummy query: + +```bash +[ec2-user]$ echo "/search/?yql=select+*+from+music+where+true" > query001.txt +``` + + +Run vespa-fbench and verify 200 response: + +```bash +[ec2-user]$ docker run -v $(pwd):/files -w /files \ + --entrypoint /opt/vespa/bin/vespa-fbench \ + vespaengine/vespa \ + -C data-plane-public-cert.pem \ + -K data-plane-private-key.pem \ + -T /etc/ssl/certs/ca-bundle.crt \ + -n 1 -q query001.txt -s 1 -c 0 \ + baaae1db.b68ddc0d.z.vespa-app.cloud 443 +``` + + + +At this point, you are able to benchmark using *vespa-fbench* in the same zone as the Vespa Cloud dev instance. + +## Run benchmark + +Use the [Vespa Benchmarking Guide](/en/performance/benchmarking) to plan and run benchmarks. Also see [sizing](#sizing) below. Make sure the client running the benchmark tool has sufficient resources. + +Export [metrics](../operations/metrics.html): + +```bash +$ curl \ + --cert data-plane-public-cert.pem \ + --key data-plane-private-key.pem \ + https://baaae1db.b68ddc0d.z.vespa-app.cloud/prometheus/v1/values +``` + +Notes: + +- Periodically dump all metrics using `consumer=Vespa`. +- Make sure you will not exhaust your serving threads on your container nodes while in production. This can be verified by making sure this expression stays well below 100% (typically below 50%) for the traffic you expect: `100 * (jdisc.thread_pool.active_threads.sum / jdisc.thread_pool.active_threads.count) / jdisc.thread_pool.size.max` for each `threadpool` value. You can increase the number of threads in the pools by using larger container nodes, more container nodes or by tuning the number of threads as described in [services-search](/en/reference/applications/services/search#threadpool). In the case you do exhaust a threadpool and its queue you will experience HTTP 503 responses for requests that are rejected by the container. + +## Making changes + +Whenever deploying changes to configuration, track progress in the Deployment dashboard. Some changes, like changing [requestthreads](/en/reference/applications/services/content#requestthreads) will restart content nodes, and this is done in sequence and takes time. Wait for successful completion in *Wait for services and endpoints to come online*. + +When changing node type/count, wait for auto data redistribution to complete, watching the `vds.idealstate.merge_bucket.pending.average` metric: + +```bash +$ while true; do curl -s \ + --cert data-plane-public-cert.pem \ + --key data-plane-private-key.pem \ + https://baaae1db.b68ddc0d.z.vespa-app.cloud/prometheus/v1/values?consumer=Vespa | \ + grep idealstate.merge_bucket.pending.average; \ + sleep 10; done +``` + +Notes: + +- Dump all metrics using `consumer=Vespa`. +- After changing the number of content nodes, this metric will jump, then decrease (not necessarily linearly) - speed depending on data volume. + +## Sizing + +Using Vespa Cloud enables the Vespa Team to assist you to optimise the application to reduce resource spend. Based on 150 applications running on Vespa Cloud today, savings are typically 50%. Cost optimization is hard to do without domain knowledge - but few teams are experts in both their application and its serving platform. Sizing means finding both the right node size and the right cluster topology: + +![Resize to fewer and smaller nodes](/assets/img/nodes.svg) + +Applications use Vespa for their primary business use cases. Availability and performance vs. cost are business decisions. The best sized application can handle all expected load situations, and is configured to degrade quality gracefully for the unexpected. + +Even though Vespa is cost-efficient out of the box, Vespa experts can usually spot over/under-allocations in CPU, memory and disk space/IO, and discuss trade-offs with the application team. + +Using [automated deployments](/en/operations/automated-deployments) applications go live with little risk. After launch, right-size the application based on true load after using Vespa's elasticity features with automated data migration. + +Use the [Vespa sizing guide](/en/performance/sizing-search) to size the application and find metrics used there. Pro-tips: + +- 60% is a good max memory allocation +- 50% is a good max CPU allocation, although application dependent. +- 70% is a good max disk allocation + +Rules of thumb: + +- Memory and disk scales approximately linearly for indexed fields' data - attributes have a fixed cost for empty fields. +- Data variance will impact memory usage. +- Undersized instances will [block writes](/en/writing/feed-block). +- If is often a good idea to use the `dev` zone to test memory impact of adding large fields, e.g. adding an embedding. + +## Notes + +- The user running benchmarks must have read access to the endpoint - if you already have, you can skip this section. Refer to the [Vespa security guide](/en/security/guide). +- [Monitoring](/en/operations/monitoring) is useful to track metrics when benchmarking. diff --git a/en/performance/benchmarking.html b/en/performance/benchmarking.html deleted file mode 100644 index f1caaef3f1..0000000000 --- a/en/performance/benchmarking.html +++ /dev/null @@ -1,238 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Vespa Benchmarking" -redirect_from: -- /en/performance/vespa-benchmarking.html ---- - -

    -Benchmarking a Vespa application is essential to get an idea of -how well the test configuration performs. -Thus, benchmarking is an essential part of sizing a search cluster itself. -Benchmarking a cluster can answer the following questions:

    -
      -
    • What throughput and latency to expect from a search node?
    • -
    • Which resource is the bottleneck in the system?
    • -
    -

    -These in turn indirectly answers other questions such as how many nodes are needed, -and if it will help to upgrade disk or CPU. -Thus, benchmarking will help in finding the optimal Vespa configuration, -using all resources optimally, which in turn lowers costs. -

    -A good rule is to benchmark whenever the workload changes. -Benchmarking should also be done when adding new features to queries. -

    -Having an understanding of the query mix and SLA will help to set the test parameters. -Before benchmarking, consider: -

      -
    • - What is the expected query mix? - Having a representative query mix to test with is essential in order to get valid results. - Splitting up in different types of queries - is also a useful way to get an idea of which query classes are resource intensive. -
    • - What is the expected SLA, both in terms of latency and query throughput? -
    • - How important is real-time behavior? What is the rate of incoming documents, if any? -
    • - Timeout, in a benchmarking scenario, is it ok for requests to time out? Default - timeout is 500 ms, and - softtimeout is enabled. - If the full cost of all queries are to be considered: -
        -
      • Disable soft timeout with execution parameter - -
      • -
      • Set timeout to e.g. 5 seconds -
          -
        • Note that timeout in YQL takes precedence
        • -
        • Replace timeout in YQL or use the execution parameter - timeout as above. -
        -
      • -
      -
    • -
    - -

    -If benchmarking using Vespa Cloud, see -Vespa Cloud Benchmarking. -

    - - -

    vespa-fbench

    -

    -Vespa provides a query load generator tool, -vespa-fbench, -to run queries and generate statistics - much like a traditional web server load generator. -It allows running any number of clients -(i.e. the more clients, the higher load), for any length of time, -and adjust the client response time before issuing the next query. -It outputs the throughput, max, min, and average latency, -as well as the 25, 50, 75, 90, 95, 99 and 99.9 latency percentiles. -This provides quite accurate information of how well the system manages the workload. -

    -Disclaimer: vespa-fbench is a tool to drive load for benchmarking and tuning. -It is not a tool for finding the maximum load -or latencies in a production setting. -This is due to the way it is implemented: It is run with -n number of clients per run. -It is good for testing, as proton can be observed at different levels of concurrency. -In the real world, the number of clients and query arrival will follow a different distribution, -and impact 95p / 99p latency percentiles. -

    - - -

    Prepare queries

    -

    -vespa-fbench uses query files for GET and POST queries - -see the reference - examples: - -HTTP GET requests: -

    -/search/?yql=select%20%2A%20from%20sources%20%2A%20where%20true
    -
    -HTTP POST requests format: -
    -/search/
    -{"yql" : "select * from sources * where true"}
    -
    - -

    Run queries

    -

    -A typical vespa-fbench command looks like: -

    -$ vespa-fbench -n 8 -q queries.txt -s 300 -c 0 myhost.mydomain.com 8080
    -
    -

    -This starts 8 clients, using requests read from queries.txt. - -The -s parameter indicates that the benchmark will run for 300 seconds. -The -c parameter, states that each client thread should wait for 0 milliseconds between each query. -The last two parameters are container hostname and port. -Multiple hosts and ports can be provided, -and the clients will be uniformly distributed to query the containers round-robin. -

    -A more complex example, using docker, hitting a Vespa Cloud endpoint: -

    -
    -$ docker run -v /Users/myself/tmp:/testfiles \
    -      -w /testfiles --entrypoint '' vespaengine/vespa \
    -      /opt/vespa/bin/vespa-fbench \
    -          -C data-plane-public-cert.pem -K data-plane-private-key.pem -T /etc/ssl/certs/ca-bundle.crt \
    -          -n 10 -q queries.txt -o result.txt -s 300 -c 0 \
    -          myapp.mytenant.aws-us-east-1c.z.vespa-app.cloud 443
    -
    -When using a query file with HTTP POST requests (-P option) one also need -to pass the Content-Type header using the -H header option. - -
    -  $ docker run -v /Users/myself/tmp:/testfiles \
    -        -w /testfiles --entrypoint '' vespaengine/vespa \
    -        /opt/vespa/bin/vespa-fbench \
    -            -C data-plane-public-cert.pem -K data-plane-private-key.pem -T /etc/ssl/certs/ca-bundle.crt \
    -            -n 10 -P -H "Content-Type: application/json" -q queries_post.txt -o output.txt -s 300 -c 0 \
    -            myapp.mytenant.aws-us-east-1c.z.vespa-app.cloud 443
    -
    -          
    - - -

    Post Processing

    -

    -After each run, a summary is written to stdout (and possibly an output file from each client) - example: -

    -***************** Benchmark Summary *****************
    -clients:                      30
    -ran for:                    1800 seconds
    -cycle time:                    0 ms
    -lower response limit:          0 bytes
    -skipped requests:              0
    -failed requests:               0
    -successful requests:    12169514
    -cycles not held:        12169514
    -minimum response time:      0.82 ms
    -maximum response time:   3010.53 ms
    -average response time:      4.44 ms
    -25 percentile:              3.00 ms
    -50 percentile:              4.00 ms
    -75 percentile:              6.00 ms
    -90 percentile:              7.00 ms
    -95 percentile:              8.00 ms
    -99 percentile:             11.00 ms
    -actual query rate:       6753.90 Q/s
    -utilization:               99.93 %
    -
    -

    -Take note of the number of failed requests, -as a high number here can indicate that the system is overloaded, -or that the queries are invalid. -

    -
      -
    • - In some modes of operation, vespa-fbench waits before sending the next query. - "utilization" represents the time that vespa-fbench is sending queries and waiting for responses. - For example, a 'system utilization' of 50% - means that vespa-fbench is stress testing the system 50% of the time, - and is doing nothing the remaining 50% of the time -
    • - vespa-fbench latency results include network latency between the client and the Vespa instance. - Measure and subtract network latency to obtain the true vespa query latency. -
    • -
    - -

    Benchmark

    -

    -Strategy: find optimal requestthreads number, -then find capacity by increasing number of parallel test clients: -

      -
    1. -

      - Test with single client (n=1), single thread to find a latency baseline. - For each test run, increase threads: -

      -<content id="search" version="1.0">
      -  <engine>
      -    <proton>
      -      <tuning>
      -        <searchnode>
      -            <requestthreads>
      -                <persearch>1</persearch>
      -            </requestthreads>
      -
      - use 1, 2, 4, 8, ... threads and measure query latency (vespa-fbench output) - and CPU utilization (metric - below). - Note: after deploying the thread config change, - proton must be restarted for new thread setting to take effect - (look for ONLINE): -
      -$ vespa-stop-services && vespa-start-services && sleep 60 && vespa-proton-cmd --local getProtonStatus
      -   ...
      -  "matchengine","OK","state=ONLINE",""
      -  "documentdb:search","OK","state=ONLINE configstate=OK",""
      -
      -
    2. -

      - use #threads sweet spot, then increase number of clients, observe latency and CPU. -

      -
    3. -
    - -

    Metrics

    -

    -The container nodes expose the -/metrics/v2/values interface - -use this to dump metrics during benchmarks. -Example - output all metrics from content node: -

    -$ curl http://localhost:8080/metrics/v2/values | \
    -  jq '.nodes[] | select(.role=="content/mysearchcluster/0/0") | .node.metrics[].values'
    -
    -Output CPU util: -
    -$ curl http://localhost:8080/metrics/v2/values | \
    -  jq '.nodes[] | select(.role=="content/mysearchcluster/0/0") | .node.metrics[].values."cpu.util"'
    -
    diff --git a/en/performance/benchmarking.mdx b/en/performance/benchmarking.mdx new file mode 100644 index 0000000000..b79f2a82f5 --- /dev/null +++ b/en/performance/benchmarking.mdx @@ -0,0 +1,161 @@ +--- +title: "Vespa Benchmarking" +sidebarTitle: "Benchmarking" +--- +Benchmarking a Vespa application is essential to get an idea of how well the test configuration performs. Thus, benchmarking is an essential part of sizing a search cluster itself. Benchmarking a cluster can answer the following questions: + +- What throughput and latency to expect from a search node? +- Which resource is the bottleneck in the system? + +These in turn indirectly answers other questions such as how many nodes are needed, and if it will help to upgrade disk or CPU. Thus, benchmarking will help in finding the optimal Vespa configuration, using all resources optimally, which in turn lowers costs. + +A good rule is to benchmark whenever the workload changes. Benchmarking should also be done when adding new features to queries. + +Having an understanding of the query mix and SLA will help to set the test parameters. Before benchmarking, consider: + +- What is the expected query mix? Having a representative query mix to test with is essential in order to get valid results. Splitting up in different types of queries is also a useful way to get an idea of which query classes are resource intensive. +- What is the expected SLA, both in terms of latency and query throughput? +- How important is real-time behavior? What is the rate of incoming documents, if any? +- Timeout, in a benchmarking scenario, is it ok for requests to time out? Default [timeout](/en/reference/querying/yql#timeout) is 500 ms, and [softtimeout](/en/reference/api/query#ranking.softtimeout.enable) is enabled. If the full cost of all queries are to be considered: + - Disable soft timeout with execution parameter + - by a [query profile](/en/reference/querying/query-profiles) + - by appending: `&ranking.softtimeout.enable=false` to with the [vespa-fbench](#vespa-fbench) `-a` option + - Set timeout to e.g. 5 seconds + - Note that `timeout` in YQL takes precedence + - Replace timeout in YQL or use the execution parameter [timeout](/en/reference/api/query#timeout) as above. + +If benchmarking using Vespa Cloud, see [Vespa Cloud Benchmarking](/en/performance/benchmarking-cloud). + +## vespa-fbench + +Vespa provides a query load generator tool, [vespa-fbench](/en/reference/operations/tools#vespa-fbench), to run queries and generate statistics - much like a traditional web server load generator. It allows running any number of *clients* (i.e. the more clients, the higher load), for any length of time, and adjust the client response time before issuing the next query. It outputs the throughput, max, min, and average latency, as well as the 25, 50, 75, 90, 95, 99 and 99.9 latency percentiles. This provides quite accurate information of how well the system manages the workload. + +**Disclaimer:** *vespa-fbench* is a tool to drive load for benchmarking and tuning. It is not a tool for finding the maximum load or latencies in a production setting. This is due to the way it is implemented: It is run with `-n` number of clients per run. It is good for testing, as proton can be observed at different levels of concurrency. In the real world, the number of clients and query arrival will follow a different distribution, and impact 95p / 99p latency percentiles. + +### Prepare queries + +vespa-fbench uses *query files* for GET and POST queries - see the [reference](/en/reference/operations/tools#vespa-fbench) - examples: *HTTP GET* requests: + +```bash +/search/?yql=select%20%2A%20from%20sources%20%2A%20where%20true +``` + +*HTTP POST* requests format: + +```text +/search/ +{"yql" : "select * from sources * where true"} +``` + +### Run queries + +A typical vespa-fbench command looks like: + +```bash +$ vespa-fbench -n 8 -q queries.txt -s 300 -c 0 myhost.mydomain.com 8080 +``` + +This starts 8 clients, using requests read from `queries.txt`. The `-s` parameter indicates that the benchmark will run for 300 seconds. The `-c` parameter, states that each client thread should wait for 0 milliseconds between each query. The last two parameters are container hostname and port. Multiple hosts and ports can be provided, and the clients will be uniformly distributed to query the containers round-robin. + +A more complex example, using docker, hitting a Vespa Cloud endpoint: + +```bash +$ docker run -v /Users/myself/tmp:/testfiles \ + -w /testfiles --entrypoint '' vespaengine/vespa \ + /opt/vespa/bin/vespa-fbench \ + -C data-plane-public-cert.pem -K data-plane-private-key.pem -T /etc/ssl/certs/ca-bundle.crt \ + -n 10 -q queries.txt -o result.txt -s 300 -c 0 \ + myapp.mytenant.aws-us-east-1c.z.vespa-app.cloud 443 +``` + +When using a query file with HTTP POST requests (`-P` option) one also need to pass the *Content-Type* header using the `-H` header option. + +```bash + $ docker run -v /Users/myself/tmp:/testfiles \ + -w /testfiles --entrypoint '' vespaengine/vespa \ + /opt/vespa/bin/vespa-fbench \ + -C data-plane-public-cert.pem -K data-plane-private-key.pem -T /etc/ssl/certs/ca-bundle.crt \ + -n 10 -P -H "Content-Type: application/json" -q queries_post.txt -o output.txt -s 300 -c 0 \ + myapp.mytenant.aws-us-east-1c.z.vespa-app.cloud 443 +``` + +### Post Processing + +After each run, a summary is written to stdout (and possibly an output file from each client) - example: + +```text +***************** Benchmark Summary ***************** +clients: 30 +ran for: 1800 seconds +cycle time: 0 ms +lower response limit: 0 bytes +skipped requests: 0 +failed requests: 0 +successful requests: 12169514 +cycles not held: 12169514 +minimum response time: 0.82 ms +maximum response time: 3010.53 ms +average response time: 4.44 ms +25 percentile: 3.00 ms +50 percentile: 4.00 ms +75 percentile: 6.00 ms +90 percentile: 7.00 ms +95 percentile: 8.00 ms +99 percentile: 11.00 ms +actual query rate: 6753.90 Q/s +utilization: 99.93 % +``` + +Take note of the number of *failed requests*, as a high number here can indicate that the system is overloaded, or that the queries are invalid. + +- In some modes of operation, vespa-fbench waits before sending the next query. "utilization" represents the time that vespa-fbench is sending queries and waiting for responses. For example, a 'system utilization' of 50% means that vespa-fbench is stress testing the system 50% of the time, and is doing nothing the remaining 50% of the time +- vespa-fbench latency results include network latency between the client and the Vespa instance. Measure and subtract network latency to obtain the true vespa query latency. + +## Benchmark + +Strategy: find optimal *requestthreads* number, then find capacity by increasing number of parallel test clients: + + + +Test with single client (n=1), single thread to find a *latency baseline*. For each test run, increase [threads](/en/reference/applications/services/content#requestthreads): + +```xml + + + + + + + 1 + +``` + +use 1, 2, 4, 8, ... threads and measure query latency (vespa-fbench output) and CPU utilization ([metric](#metrics) - below). Note: after deploying the thread config change, [proton](/en/content/proton) must be restarted for new thread setting to take effect (look for ONLINE): + +```bash +$ vespa-stop-services && vespa-start-services && sleep 60 && vespa-proton-cmd --local getProtonStatus + ... + "matchengine","OK","state=ONLINE","" + "documentdb:search","OK","state=ONLINE configstate=OK","" +``` + + +use #threads sweet spot, then increase number of clients, observe latency and CPU. + + + +### Metrics + +The *container* nodes expose the [/metrics/v2/values](/en/operations/metrics) interface - use this to dump metrics during benchmarks. Example - output all metrics from content node: + +```bash +$ curl http://localhost:8080/metrics/v2/values | \ + jq '.nodes[] | select(.role=="content/mysearchcluster/0/0") | .node.metrics[].values' +``` + +Output CPU util: + +```bash +$ curl http://localhost:8080/metrics/v2/values | \ + jq '.nodes[] | select(.role=="content/mysearchcluster/0/0") | .node.metrics[].values."cpu.util"' +``` diff --git a/en/performance/caches-in-vespa.html b/en/performance/caches-in-vespa.html deleted file mode 100644 index a2894e43b2..0000000000 --- a/en/performance/caches-in-vespa.html +++ /dev/null @@ -1,54 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Caches" ---- - - -

    Content node summary cache

    -

    - The summary cache caches summary requests and is enabled by - proton tuning configuration. - When enabling a proton summary cache, - one should also change the way proton reads summary data from mmap to directio as done below. - The summary cache saves IO and cpu spent on decompressing of chunked blocks (default 64 KB) of summary data. -

    -

    - Note that the summary cache is shared across multiple document types. -

    -

    - By default, the cache is enabled, using up to 5% of available memory - configuration example: -

    -
    {% highlight xml %}
    -
    -  
    -    
    -      
    -        
    -          
    -            
    -              directio
    -            
    -            
    -              
    -                5
    -              
    -            
    -          
    -        
    -      
    -    
    -  
    -
    -
    -{% endhighlight %}
    -{% include note.html content="If the requested document-summary only contains fields that are -attributes, the summary store (and cache) is not used." %} - -

    Protocol phases caches

    -

    -ranking.queryCache and groupingSessionCache -described in the Query API reference -are only caching data in between phases for a given a query, -so other queries do not get any benefits, -but these caches saves container - content node(s) round-trips for a given query. -

    diff --git a/en/performance/caches-in-vespa.mdx b/en/performance/caches-in-vespa.mdx new file mode 100644 index 0000000000..83eb8b2044 --- /dev/null +++ b/en/performance/caches-in-vespa.mdx @@ -0,0 +1,44 @@ +--- +title: "Caches" +--- +## Content node summary cache + +The summary cache caches summary requests and is enabled by [proton tuning configuration](/en/reference/applications/services/content#summary). When enabling a proton summary cache, one should also change the way proton reads summary data from mmap to directio as done below. The summary cache saves IO and cpu spent on decompressing of chunked blocks (default 64 KB) of summary data. + +Note that the summary cache is shared across multiple document types. + +By default, the cache is enabled, using up to 5% of available memory - configuration example: + +```xml + + + + + + + + directio + + + + 5 + + + + + + + + + +``` + + +**Note:** + +If the requested document-summary only contains fields that are [attributes](/en/content/attributes), the summary store (and cache) is not used. + + +## Protocol phases caches + +*ranking.queryCache* and *groupingSessionCache* described in the [Query API reference](/en/reference/api/query) are only caching data in between phases for a given a query, so other queries do not get any benefits, but these caches saves container - content node(s) round-trips for a *given* query. \ No newline at end of file diff --git a/en/performance/container-http.html b/en/performance/container-http.mdx similarity index 56% rename from en/performance/container-http.html rename to en/performance/container-http.mdx index 152a8e3125..00a57216b7 100644 --- a/en/performance/container-http.html +++ b/en/performance/container-http.mdx @@ -1,44 +1,21 @@ --- -# Copyright Vespa.ai. All rights reserved. title: "HTTP Performance Testing of the Container using Gatling" +sidebarTitle: "HTTP performance testing" --- -

    For container testing, more flexibility and more detailed -checking than straightforward saturating an interface with HTTP requests is -often required. The stress test tool Gatling provides such capabilities in a -flexible manner with the possibility of writing arbitrary plug-ins and a DSL for -the most common cases. This document shows how to get started using Gatling -with Vespa. Experienced Gatling users should find there is nothing special with -testing Vespa versus other HTTP services.

    - - - -

    Install Gatling

    -

    -Refer to Gatling's documentation for getting started, -or simply get the newest version from the -Gatling front page, -unpack the tar ball and jump straight into it. -The tool runs happily from the directory created when unpacking it. -This tutorial is written with Gatling 2 in mind. -

    - - - -

    Configure the First Test with a Query Log

    -

    -Refer to the Gatling documentation on how to set up the recorder. -This tool acts as a browser proxy, recording what you do in the browser, -allowing you to replay that as a test scenario. -

    -After running bin/recorder.sh and setting package to com.vespa.example -and class name to VespaTutorial, -running a simple query against your node mynode (running e.g. - -album-recommendation-java), should create a basic simulation looking something like the following in -user-files/simulations/com/vespa/example/VespaTutorial.scala: -

    +For container testing, more flexibility and more detailed checking than straightforward saturating an interface with HTTP requests is often required. The stress test tool [Gatling](https://gatling.io/) provides such capabilities in a flexible manner with the possibility of writing arbitrary plug-ins and a DSL for the most common cases. This document shows how to get started using Gatling with Vespa. Experienced Gatling users should find there is nothing special with testing Vespa versus other HTTP services.
    +
    +## Install Gatling
    +
    +Refer to Gatling's [documentation for getting started](https://gatling.io/docs/gatling/reference/current/), or simply get the newest version from the [Gatling front page](https://gatling.io/), unpack the tar ball and jump straight into it. The tool runs happily from the directory created when unpacking it. This tutorial is written with Gatling 2 in mind.
    +
    +## Configure the First Test with a Query Log
    +
    +Refer to the Gatling documentation on how to set up the recorder. This tool acts as a browser proxy, recording what you do in the browser, allowing you to replay that as a test scenario.
    +
    +After running *bin/recorder.sh* and setting package to *com.vespa.example* and class name to *VespaTutorial*, running a simple query against your node *mynode* (running e.g. [album-recommendation-java](https://github.com/vespa-engine/sample-apps/tree/master/album-recommendation-java)), should create a basic simulation looking something like the following in *user-files/simulations/com/vespa/example/VespaTutorial.scala*:
    +
    +```scala expandable
     package com.vespa.example
     
     import io.gatling.core.Predef._
    @@ -60,7 +37,7 @@ 

    Configure the First Test with .connection("keep-alive") .userAgentHeader("Mozilla/5.0 (X11; Linux x86_64; rv:27.0) Gecko/20100101 Firefox/27.0") - val headers_1 = Map("""Cache-Control""" -> """max-age=0""") + val headers_1 = Map("""Cache-Control""" -> """max-age=0""") val scn = scenario("Scenario Name") .exec(http("request_1") @@ -69,25 +46,24 @@

    Configure the First Test with setUp(scn.inject(atOnce(1 user))).protocols(httpProtocol) } -

    -Running a single query over and over again is not useful, so we have a -tiny query log in a CSV file we want to run in our test, -user-files/data/userinput.csv: -
    +```
    +
    +Running a single query over and over again is not useful, so we have a tiny query log in a CSV file we want to run in our test, *user-files/data/userinput.csv*:
    +
    +```text
     userinput
     bad religion
     bad
     lucky oops
     radiohead
     bad jackson
    -
    -

    -As usual for CSV files, the first line names the parameters. -A literal comma may be escaped with backslash as "\,". -Gatling takes hand of URL quoting, there is no need to e.g. encode space as "%20". -

    +``` + +As usual for CSV files, the first line names the parameters. A literal comma may be escaped with backslash as "\\,". Gatling takes hand of URL quoting, there is no need to e.g. encode space as "%20". + Add a feeder: -

    +
    +```scala expandable
     package com.vespa.example
     
     import io.gatling.core.Predef._
    @@ -109,7 +85,7 @@ 

    Configure the First Test with .connection("keep-alive") .userAgentHeader("Mozilla/5.0 (X11; Linux x86_64; rv:27.0) Gecko/20100101 Firefox/27.0") - val headers_1 = Map("""Cache-Control""" -> """max-age=0""") + val headers_1 = Map("""Cache-Control""" -> """max-age=0""") val scn = scenario("Scenario Name") .feed(csv("userinput.csv").random) @@ -121,24 +97,15 @@

    Configure the First Test with setUp(scn.inject(constantRate(100 usersPerSec) during (10 seconds))) .protocols(httpProtocol) } -

    -

    -Now, we have done a couple of changes to the original scenario. -First, we have added the feeder. -Since we do not have enough queries available for running long enough to get a scenario for some traffic, -we chose the "random" strategy. -This means a random user input string will be chosen for each invocation, and it might be reused. -Also, we have changed how the test is run, from just a single query, into a constant rate of 100 users for 10 seconds. -We should expect something as close as possible to 100 QPS in our test report. -

    - - - -

    Running a Benchmark

    -

    -We now have something we can run both on a headless node and on a personal laptop, -sample run output: -

    +```
    +
    +Now, we have done a couple of changes to the original scenario. First, we have added the feeder. Since we do not have enough queries available for running long enough to get a scenario for some traffic, we chose the "random" strategy. This means a random user input string will be chosen for each invocation, and it might be reused. Also, we have changed how the test is run, from just a single query, into a constant rate of 100 users for 10 seconds. We should expect something as close as possible to 100 QPS in our test report.
    +
    +## Running a Benchmark
    +
    +We now have something we can run both on a headless node and on a personal laptop, sample run output:
    +
    +```bash expandable
     $ ./bin/gatling.sh
     GATLING_HOME is set to ~/tmp/gatling-charts-highcharts-2.0.0-M3a
     Choose a simulation number:
    @@ -158,7 +125,7 @@ 

    Running a Benchmark

    [- ] 0% waiting: 998 / running: 2 / done:0 ---- Requests ------------------------------------------------------------------ -> Global (OK=0 KO=0 ) +> Global (OK=0 KO=0 ) ================================================================================ @@ -169,8 +136,8 @@

    Running a Benchmark

    [#################################### ] 49% waiting: 505 / running: 0 / done:495 ---- Requests ------------------------------------------------------------------ -> Global (OK=495 KO=0 ) -> request_1 (OK=495 KO=0 ) +> Global (OK=495 KO=0 ) +> request_1 (OK=495 KO=0 ) ================================================================================ @@ -180,8 +147,8 @@

    Running a Benchmark

    [######################################################################### ] 99% waiting: 8 / running: 0 / done:992 ---- Requests ------------------------------------------------------------------ -> Global (OK=992 KO=0 ) -> request_1 (OK=992 KO=0 ) +> Global (OK=992 KO=0 ) +> request_1 (OK=992 KO=0 ) ================================================================================ @@ -191,8 +158,8 @@

    Running a Benchmark

    [##########################################################################]100% waiting: 0 / running: 0 / done:1000 ---- Requests ------------------------------------------------------------------ -> Global (OK=1000 KO=0 ) -> request_1 (OK=1000 KO=0 ) +> Global (OK=1000 KO=0 ) +> request_1 (OK=1000 KO=0 ) ================================================================================ Simulation finished. @@ -202,24 +169,23 @@

    Running a Benchmark

    ================================================================================ ---- Global Information -------------------------------------------------------- -> numberOfRequests 1000 (OK=1000 KO=0 ) -> minResponseTime 10 (OK=10 KO=- ) -> maxResponseTime 30 (OK=30 KO=- ) -> meanResponseTime 10 (OK=10 KO=- ) -> stdDeviation 2 (OK=2 KO=- ) -> percentiles1 10 (OK=10 KO=- ) -> percentiles2 10 (OK=10 KO=- ) -> meanNumberOfRequestsPerSecond 99 (OK=99 KO=- ) +> numberOfRequests 1000 (OK=1000 KO=0 ) +> minResponseTime 10 (OK=10 KO=- ) +> maxResponseTime 30 (OK=30 KO=- ) +> meanResponseTime 10 (OK=10 KO=- ) +> stdDeviation 2 (OK=2 KO=- ) +> percentiles1 10 (OK=10 KO=- ) +> percentiles2 10 (OK=10 KO=- ) +> meanNumberOfRequestsPerSecond 99 (OK=99 KO=- ) ---- Response Time Distribution ------------------------------------------------ -> t < 800 ms 1000 (100%) -> 800 ms < t < 1200 ms 0 ( 0%) -> t > 1200 ms 0 ( 0%) -> failed 0 ( 0%) +> t < 800 ms 1000 (100%) +> 800 ms < t < 1200 ms 0 ( 0%) +> t > 1200 ms 0 ( 0%) +> failed 0 ( 0%) ================================================================================ Reports generated in 0s. Please open the following file : ~/tmp/gatling-charts-highcharts-2.0.0-M3a/results/vespatutorial-20140409115432/index.html -
    -

    +``` + The report gives graphs showing how the test progressed and summaries for failures and time spent. -

    diff --git a/en/performance/container-tuning.html b/en/performance/container-tuning.html deleted file mode 100644 index 9615ca13b0..0000000000 --- a/en/performance/container-tuning.html +++ /dev/null @@ -1,260 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Container Tuning" ---- - -

    - A collection of configuration parameters to tune the Container as used in Vespa. - Some configuration parameters have native - services.xml support - while others are configured through - - generic config overrides. -

    - - - -

    Container worker threads

    -

    - The container uses multiple thread pools for its operations. - Most components including request handlers use the container's - default thread pool, - which is controlled by a shared executor instance. - Any component can utilize the default pool by injecting an java.util.concurrent.Executor instance. - Some built-in components have dedicated thread pools - such as the Jetty server, the - search handler and - document-processing chains. - These thread pools are injected through special wiring in the config model and - are not easily accessible from other components. -

    -

    - The thread pools are by default scaled on the system resources as reported by the JVM - (Runtime.getRuntime().availableProcessors()). - It's paramount that the -XX:ActiveProcessorCount/jvm_availableProcessors - configuration is correct for the container to work optimally. - The default thread pool configuration can be - overridden through services.xml. - We recommend you keep the default configuration as it's tuned to work across a variety of workloads. - Note that the default configuration and pool usage may change between minor versions. -

    -

    - The container will pre-start the minimum number of worker threads, - so even an idle container may report running several hundred threads. - The search handler and - document processing handler - thread pools each pre-start the number of workers set in their configurations. - Note that tuning the capacity upwards increases the risk of high GC pressure - as concurrency becomes higher with more in-flight requests. - The GC pressure is a function of number of in-flight requests, the time it takes to complete the request - and the amount of garbage produced per request. - Increasing the queue size will allow the application to handle shorter traffic bursts without rejecting requests, - although increasing the average latency for those requests that are queued up. - Large queues will also increase heap consumption in overload situations. - For some thread pools, extra threads will be created once the queue is full (when - max is specified), and are destroyed - after an idle timeout. If all threads are occupied, requests are rejected with a 503 response. -

    -

    - The effective thread pool configuration and utilization statistics can be observed through the - Container Metrics. - See Thread Pool Metrics for a list of metrics exported. -

    -{% include note.html content=' If the queue size is set to 0 the metric measuring the queue size - -jdisc.thread_pool.work_queue.size - will instead switch to measure how many threads are active.'%} - -

    Recommendation

    -

    - A fixed size pool is preferable for stable latency during peak load, - at a cost of a higher static memory load and increased context-switching overhead if excessive number of threads are configured. - Variable size pool is mostly beneficial to minimize memory consumption during low-traffic periods, and in general if the size of peak load is somewhat unknown. - The downside is that once all core threads are active, - latency will increase as additional tasks are queued and launching extra threads is relatively expensive as it involves system calls to the OS. -

    - -

    Example

    -

    - Consider a container host with 8 vCPU. Setting <threads>4</threads> on the - search handler threadpool - yields 4 * 8 = 32 worker threads, and adding <queue>25</queue> gives the pool a total - queue capacity of 32 * 25 = 800 requests. The same thread calculation applies to the - document processing handler threadpool, which does not support - queue configuration. - The example below shows a consistent configuration where the default thread pool, the - search handler threadpool, and the document processing handler threadpool are all kept fixed. -

    -
    {% highlight xml %}
    -
    -
    -    
    -    
    -        
    -
    -        
    -        5
    -
    -        
    -        25
    -    
    -
    -    
    -        
    -        
    -            
    -
    -            
    -            4
    -
    -            
    -            25
    -        
    -    
    -
    -    
    -        
    -        
    -            
    -            2
    -        
    -    
    -
    -
    -{% endhighlight %}
    - -

    Container memory usage

    -
    Help, my container nodes are using more than 70% memory!
    -

    - It's common to observe the container process utilizing its maximum configured heap size. - This, by itself, is not necessarily an indication of a problem. The Java Virtual Machine (JVM) - manages memory within the allocated heap, and it's designed to use as much of it as possible - to reduce the frequency of garbage collection. -

    - To understand whether enough memory is allocated, look at the garbage collection activity. - If GC is running frequently and using significant CPU or causing long pauses, it might - indicate that the heap size is too small for the workload. In such cases, consider increasing - the maximum heap size. However, if the garbage collector is running infrequently and efficiently, - it's perfectly normal for the container to utilize most or all of its allocated heap, and even more - (as some memory will also be allocated outside the heap; e.g. direct buffers for efficient data transfer). -

    - Vespa exports several metrics to allow you to monitor JVM GC performance, such as jvm.gc.overhead - - if this exceeds 8-10% you should consider increasing heap memory and/or tuning GC settings. - -

    JVM heap size

    -

    - Change the default JVM heap size settings used by Vespa to better suit - the specific hardware settings or application requirements. -

    -

    - By setting the relative size of the total JVM heap in - percentage of available memory, - one does not know exactly what the heap size will be, - but the configuration will be adaptable - and ensure that the container can start - even in environments with less available memory. - The example below allocates 50% of available memory on the machine to the JVM heap: -

    -
    {% highlight xml %}
    -
    -    
    -        
    -        
    -    
    -
    -{% endhighlight %}
    - - - -

    JVM Tuning

    -

    - Use gc-options for controlling GC related parameters - and options for tuning other parameters. - See reference documentation. - Example: Running with 4 GB heap using G1 garbage collector and using NewRatio = 1 - (equal size of old and new generation) and enabling verbose GC logging (logged to stdout to vespa.log file). -

    -
    {% highlight xml %}
    -
    -    
    -        
    -        
    -    
    -
    -{% endhighlight %}
    -

    - The default heap size with docker image is 1.5g which can for high throughput applications be on the low side, - causing frequent garbage collection. - By default, the G1GC collector is used. -

    - - -

    Config Server and Config Proxy

    -

    - The config server and proxy are not executed based on the model in services.xml. - On the contrary, they are used to bootstrap the services in that model. - Consequently, one must use configuration variables - to set the JVM parameters for the config server and config proxy. - They also need to be restarted (services in the config proxy's case) after a change, - but one does not need to vespa prepare - or vespa activate first. Example: -

    -
    -VESPA_CONFIGSERVER_JVMARGS      -Xlog:gc
    -VESPA_CONFIGPROXY_JVMARGS       -Xlog:gc -Xmx256m
    -
    -

    - Refer to Setting Vespa variables. -

    - - - -

    Container warmup

    -

    - Some applications observe that the first queries made to a freshly started container - take a long time to complete. - This is typically due to some components performing lazy setup of data structures or connections. - Lazy initialization should be avoided in favor of eager initialization in component constructor, - but this is not always possible. -

    -

    - A way to avoid problems with the first queries in such cases - is to perform warmup queries at startup. - This is done by issuing queries from the constructor of the Handler of regular queries. - If using the default handler, - - com.yahoo.search.handler.SearchHandler, - subclass this and configure your subclass as the handler of query requests in services.xml. -

    -

    - Add a call to a warmupQueries() method as the last line of your handler constructor. - The method can look something like this: -

    -
    {% highlight java %}
    -private void warmupQueries() {
    -    String[] requestUris = new String[] {"warmupRequestUri1", "warmupRequestUri2"};
    -    int warmupIterations = 50;
    -
    -    for (int i = 0; i < warmupIterations; i++) {
    -        for (String requestUri : requestUris) {
    -            handle(HttpRequest.createTestRequest(requestUri, com.yahoo.jdisc.http.HttpRequest.Method.GET));
    -        }
    -    }
    -}
    -{% endhighlight %}
    -

    - Since these queries will be executed before the container starts accepting external queries, - they will cause the first external queries to observe a warmed up container instance. -

    -

    - Use metrics.ignore - in the warmup queries to eliminate them from being reported in metrics. -

    - -

    Disabling warmups

    -

    - Warmups can be disabled by adding the following container http config to the container section in services.xml: -

    -
    {% highlight xml %}
    -
    -  false
    -
    -{% endhighlight %}
    diff --git a/en/performance/container-tuning.mdx b/en/performance/container-tuning.mdx new file mode 100644 index 0000000000..2a2cba3070 --- /dev/null +++ b/en/performance/container-tuning.mdx @@ -0,0 +1,154 @@ +--- +title: "Container Tuning" +--- + +A collection of configuration parameters to tune the Container as used in Vespa. Some configuration parameters have native [services.xml](/en/basics/applications) support while others are configured through [generic config overrides](/en/reference/applications/config-files#generic-configuration-in-services-xml). + +## Container worker threads + +The container uses multiple thread pools for its operations. Most components including request handlers use the container's [default thread pool](/en/reference/applications/services/container#threadpool), which is controlled by a shared executor instance. Any component can utilize the default pool by injecting an `java.util.concurrent.Executor` instance. Some built-in components have dedicated thread pools - such as the Jetty server, the [search handler](/en/reference/applications/services/search#threadpool) and [document-processing](/en/reference/applications/services/docproc#threadpool) chains. These thread pools are injected through special wiring in the config model and are not easily accessible from other components. + +The thread pools are by default scaled on the system resources as reported by the JVM (`Runtime.getRuntime().availableProcessors()`). It's paramount that the `-XX:ActiveProcessorCount`/`jvm_availableProcessors` configuration is correct for the container to work optimally. The [default thread pool](/en/reference/applications/services/container#threadpool) configuration can be overridden through services.xml. We recommend you keep the default configuration as it's tuned to work across a variety of workloads. Note that the default configuration and pool usage may change between minor versions. + +The container will pre-start the minimum number of worker threads, so even an idle container may report running several hundred threads. The [search handler](/en/reference/applications/services/search#threadpool) and [document processing handler](/en/reference/applications/services/docproc#threadpool) thread pools each pre-start the number of workers set in their configurations. Note that tuning the capacity upwards increases the risk of high GC pressure as concurrency becomes higher with more in-flight requests. The GC pressure is a function of number of in-flight requests, the time it takes to complete the request and the amount of garbage produced per request. Increasing the queue size will allow the application to handle shorter traffic bursts without rejecting requests, although increasing the average latency for those requests that are queued up. Large queues will also increase heap consumption in overload situations. For some thread pools, extra threads will be created once the queue is full (when [`max`](/en/reference/applications/services/search#threads.max) is specified), and are destroyed after an idle timeout. If all threads are occupied, requests are rejected with a 503 response. + +The effective thread pool configuration and utilization statistics can be observed through the [Container Metrics](/en/operations/metrics#container-metrics). See [Thread Pool Metrics](/en/operations/metrics#thread-pool-metrics) for a list of metrics exported. + + +**Note:** + +If the queue size is set to 0 the metric measuring the queue size - `jdisc.thread_pool.work_queue.size` - will instead switch to measure how many threads are active. + + +### Recommendation + +A fixed size pool is preferable for stable latency during peak load, at a cost of a higher static memory load and increased context-switching overhead if excessive number of threads are configured. Variable size pool is mostly beneficial to minimize memory consumption during low-traffic periods, and in general if the size of peak load is somewhat unknown. The downside is that once all core threads are active, latency will increase as additional tasks are queued and launching extra threads is relatively expensive as it involves system calls to the OS. + +### Example + +Consider a container host with 8 vCPU. Setting `4` on the [search handler threadpool](/en/reference/applications/services/search#threadpool) yields `4 * 8 = 32` worker threads, and adding `25` gives the pool a total queue capacity of `32 * 25 = 800` requests. The same thread calculation applies to the [document processing handler threadpool](/en/reference/applications/services/docproc#threadpool), which does not support queue configuration. The example below shows a consistent configuration where the default thread pool, the search handler threadpool, and the document processing handler threadpool are all kept fixed. + +```xml expandable + + + + + + + + 5 + + + 25 + + + + + + + + + 4 + + + 25 + + + + + + + + 2 + + + + +``` + +## Container memory usage + +> *Help, my container nodes are using more than 70% memory!* + +It's common to observe the container process utilizing its maximum configured heap size. This, by itself, is not necessarily an indication of a problem. The Java Virtual Machine (JVM) manages memory within the allocated heap, and it's designed to use as much of it as possible to reduce the frequency of garbage collection. + +To understand whether enough memory is allocated, look at the garbage collection activity. If GC is running frequently and using significant CPU or causing long pauses, it might indicate that the heap size is too small for the workload. In such cases, consider increasing the maximum heap size. However, if the garbage collector is running infrequently and efficiently, it's perfectly normal for the container to utilize most or all of its allocated heap, and even more (as some memory will also be allocated outside the heap; e.g. direct buffers for efficient data transfer). + +Vespa exports several metrics to allow you to monitor JVM GC performance, such as [jvm.gc.overhead](/en/reference/operations/metrics/container#jvm_gc_overhead) - if this exceeds 8-10% you should consider increasing heap memory and/or tuning GC settings. + +## JVM heap size + +Change the default JVM heap size settings used by Vespa to better suit the specific hardware settings or application requirements. + +By setting the relative size of the total JVM heap in [percentage of available memory](/en/reference/applications/services/container#nodes), one does not know exactly what the heap size will be, but the configuration will be adaptable and ensure that the container can start even in environments with less available memory. The example below allocates 50% of available memory on the machine to the JVM heap: + +```xml + + + + + + +``` + +## JVM Tuning + +Use *gc-options* for controlling GC related parameters and *options* for tuning other parameters. See [reference documentation](/en/reference/applications/services/container#nodes). Example: Running with 4 GB heap using G1 garbage collector and using NewRatio = 1 (equal size of old and new generation) and enabling verbose GC logging (logged to stdout to vespa.log file). + +```xml + + + + + + +``` + +The default heap size with docker image is 1.5g which can for high throughput applications be on the low side, causing frequent garbage collection. By default, the G1GC collector is used. + +### Config Server and Config Proxy + +The config server and proxy are not executed based on the model in *services.xml*. On the contrary, they are used to bootstrap the services in that model. Consequently, one must use configuration variables to set the JVM parameters for the config server and config proxy. They also need to be restarted (*services* in the config proxy's case) after a change, but one does *not* need to *vespa prepare* or *vespa activate* first. Example: + +```bash +VESPA_CONFIGSERVER_JVMARGS -Xlog:gc +VESPA_CONFIGPROXY_JVMARGS -Xlog:gc -Xmx256m +``` + +Refer to [Setting Vespa variables](/en/operations/self-managed/files-processes-and-ports#environment-variables). + +## Container warmup + +Some applications observe that the first queries made to a freshly started container take a long time to complete. This is typically due to some components performing lazy setup of data structures or connections. Lazy initialization should be avoided in favor of eager initialization in component constructor, but this is not always possible. + +A way to avoid problems with the first queries in such cases is to perform warmup queries at startup. This is done by issuing queries from the constructor of the Handler of regular queries. If using the default handler, [com.yahoo.search.handler.SearchHandler](https://github.com/vespa-engine/vespa/blob/master/container-search/src/main/java/com/yahoo/search/handler/SearchHandler.java), subclass this and configure your subclass as the handler of query requests in *services.xml*. + +Add a call to a warmupQueries() method as the last line of your handler constructor. The method can look something like this: + +```java +private void warmupQueries() { + String[] requestUris = new String[] {"warmupRequestUri1", "warmupRequestUri2"}; + int warmupIterations = 50; + + for (int i = 0; i < warmupIterations; i++) { + for (String requestUri : requestUris) { + handle(HttpRequest.createTestRequest(requestUri, com.yahoo.jdisc.http.HttpRequest.Method.GET)); + } + } +} +``` + +Since these queries will be executed before the container starts accepting external queries, they will cause the first external queries to observe a warmed up container instance. + +Use [metrics.ignore](/en/reference/api/query#metrics.ignore) in the warmup queries to eliminate them from being reported in metrics. + +### Disabling warmups + +Warmups can be disabled by adding the following container http config to the container section in services.xml: + +```xml + + false + +``` \ No newline at end of file diff --git a/en/performance/feature-tuning.html b/en/performance/feature-tuning.html deleted file mode 100644 index d3cb21ebff..0000000000 --- a/en/performance/feature-tuning.html +++ /dev/null @@ -1,898 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Vespa Serving Tuning" ---- - -

    - This document describes how to tune certain features of an application for high query serving performance, - where the main focus is on content cluster search features; - see Container tuning for tuning of container clusters. - The search sizing guide is about scaling an application deployment. -

    - -

    Attribute vs index

    -

    - The attribute documentation summarizes when to use - attribute - in the indexing statement. - Also see the procedure - for changing from attribute to index and vice-versa. -

    -
    -field timestamp type long {
    -    indexing: summary | attribute
    -}
    -
    -

    - If both index and attribute are configured for string-type fields, - Vespa will search and match against the index with default match text. - All numeric type fields and tensor fields are attribute (in-memory) fields in Vespa. -

    - - -

    When to use fast-search for attribute fields

    -

    - By default, Vespa does not build any posting list index structures over attribute fields. - Adding fast-search to the attribute definition as shown below - will add an in-memory B-tree posting list structure - which enables faster search for some cases (but not all, see next paragraph): -

    -
    -field timestamp type long {
    -    indexing:  summary | attribute
    -    attribute: fast-search
    -    rank:      filter
    -}
    -
    -

    - When Vespa runs a query with multiple query items, it builds a query execution plan. - It tries to optimize the plan so that the temporary result set is as small as possible. - To do this, restrictive query tree items (matching few documents) are evaluated early. - The query execution plan looks at hit count estimates for each part of the query tree - using the index and B-tree dictionaries, which track the number of documents in which a given term occurs. -

    -

    - However, for attribute fields without fast-search - there is no hit count estimate, - so the estimate becomes the total number of documents (matches all) - and the query tree item is moved to the end of the query evaluation. - A query with only one query term searching an attribute field without fast-search - would be a linear scan over all documents and thus expensive: -

    -
    -select * from sources * where range(timestamp, 0, 100)
    -
    -

    - But if this query term is and-ed with another term that matches fewer documents, - that term will determine the cost instead, and fast-search won't be necessary, e.g.: -

    -
    -select * from sources * where range(timestamp, 0, 100) and uuid contains "123e4567-e89b-12d3-a456-426655440000"
    -
    -

    The general rules of thumb for when to use fast-search for an attribute field are:

    -
      -
    • Use fast-search if the attribute field is searched without any other query terms
    • -
    • Use fast-search if the attribute field could limit the total number of hits efficiently
    • -
    -

    - Changing fast-search aspect of the attribute is a - live change - which does not require any re-feeding, so testing the performance with and without is low effort. - Adding or removing fast-search requires restart. -

    - -

    Note that attribute fields with fast-search that are not used in term based - ranking should use rank: filter -for optimal performance. See reference rank: filter. -

    -

    - See optimization for sorting on a single-value numeric attribute with fast-search using - sorting.degrading. -

    - - - -

    - Lexical search (or keyword-based search) is a method that matches query terms as they appear in indexed documents. - It relies on the lexical representation of words rather than their meaning, and is one of the two retrieval methods used in - hybrid search. - Lexical search in Vespa is done by querying string (text) index fields, - typically using the weakAnd query operator with BM25 ranking. -

    -

    - The following schema represents a simple article document with title and content fields, - that can represent Wikipedia articles as an example. - A default fieldset is specified such that user queries are matched against both the title and content fields. - BM25 ranking combines the scores of both fields in the default rank profile. - In addition, the optimized rank profile specifies tuning parameters to improve query performance: -

    -
    -schema article {
    -    document article {
    -        field title type string {
    -            indexing: index | summary
    -            index: enable-bm25
    -        }
    -        field content type string {
    -            indexing: index | summary
    -            index: enable-bm25
    -        }
    -    }
    -
    -    fieldset default {
    -        fields: title, content
    -    }
    -
    -    rank-profile default {
    -        first-phase {
    -            expression: bm25(title) + bm25(content)
    -        }
    -    }
    -
    -    rank-profile optimized inherits default {
    -        filter-threshold: 0.05
    -        weakand {
    -            stopword-limit: 0.6
    -            adjust-target: 0.01
    -        }
    -    }
    -}
    -
    -

    - The following shows an example question-answer query against a collection of articles, - using the weakAnd query operator and the optimized rank profile. - Question-answer queries are often written in full sentences, and as a consequence, - they tend to contain many stopwords that are present in many documents and of less relevance when it comes to ranking. - E.g., terms as "the", "in", and "are" are typically present in more the 60% of the documents: -

    -
    {% highlight json %}
    -{
    -  "yql": "select * from article where userQuery()",
    -  "ranking.profile": "optimized",
    -  "query": "what are the three highest mountains in the world"
    -}
    -{% endhighlight %}
    -

    - The cost of evaluating such a query is primarily linear with the number of matched documents. - The AND operator is most effective, but often ends up being too restrictive by not returning enough matches. - The OR operator is less restrictive, but has the problem of returning too many matches, which is very costly. - The weakAnd operator is somewhere in between the two in cost. -

    - -

    Posting Lists

    -

    - To find matching documents, the query operator uses the posting lists associated with each query term. - A posting list is part of the inverted index and contains all occurrences of a term within a collection of documents. - It consists of document IDs for documents that contain the term, - and additional information such as the positions of the term within those documents (used for ranking purposes). - For common terms (e.g., stopwords), the posting lists are very large and can be expensive to use during evaluation and ranking. - CPU work is required to iterate them, and I/O work is required to load portions of them from disk to memory with MMAP. - The last part is especially problematic when all posting lists of a disk index cannot fit into physical memory, - and the system must constantly swap parts of them in and out of memory, leading to high I/O wait times. -

    -

    - To improve query performance, the following tuning parameters are available, - as seen used in the optimized rank profile. - These are used to make tradeoffs between performance and quality. -

    -
      -
    • - Use more compact posting lists for common terms: - Setting filter-threshold to 0.05 - ensures that all terms that are estimated to occur in more than 5% of the documents are - handled with compact posting lists (bitvectors) instead of the full posting lists. - This makes matching faster at the cost of producing less information for BM25 ranking (only a boolean signal is available). -
    • -
    • - Avoid using large posting lists all together: - Setting stopword-limit to 0.6, - ensures that all terms that are estimated to occur in more than 60% of the documents are considered stopwords - and dropped entirely from the query and also from ranking. -
    • -
    • - Reduce the number of hits produced by weakAnd: - Setting adjust-target - ensures that documents that only match terms that occur very frequently in the documents are not considered hits. - This also removes the need to calculate first-phase ranking for these documents, - which is beneficial if first-phase ranking is more complex and expensive. -
    • -
    - - -

    Performance

    -

    - The tuning parameters used in the optimized rank profile have been shown to provide a good tradeoff between performance and quality in testing. - A Wikipedia dataset with SQuAD - (Stanford Question Answering Dataset) queries was used to analyze performance, - and trec-covid, - MS MARCO and - nfcorpus from the BEIR dataset to analyze quality implications. -

    -

    - For instance, the query performance was tripled without any measurable drop in quality with the Wikipedia dataset, - using the tuning parameters in the optimized rank profile. - See the blog post Tripling the query performance of lexical search - for more details. - Note that testing should be conducted on your particular dataset to find the right tradeoff between performance and quality. -

    - - -

    Hybrid TAAT and DAAT query evaluation

    -

    - Vespa supports hybrid query evaluation over inverted indexes, - combining TAAT and DAAT evaluation to combine the best of both query evaluation techniques. - Hybrid is not enabled per default and is triggered by a run-time query parameter. -

    -
      -
    • TAAT: Term At A Time scores documents one query term at a time. - The entire posting iterator can be read per query term, and the score of a document is accumulated. - It is CPU cache friendly as posting data is read sequentially without randomly seeking the posting list iterator. - The downside is that TAAT limits the term-based ranking function to be a linear sum of term scores. - This downside is one reason why most search engines use DAAT.
    • -
    • DAAT: Document At A Time scores documents completely one at a time. - This requires multiple seeks in the term posting lists, - which is CPU cache unfriendly but allows non-linear ranking functions.
    • -
    -

    - Generally, Vespa does DAAT (document-at-a-time) query evaluation - and not TAAT (term-at-a time) for the reason listed above. -

    -

    - Ranking (score calculation) and matching (does the document match the query logic) - is not fully two separate disjunct phases, - where one first finds matches and calculates the ranking score in a later phase. - Matching and first-phase score calculation is interleaved when using DAAT. -

    -

    - The first-phase ranking score is assigned to the hit when it satisfies the query constraints. - At that point, the term iterators are positioned at the document ID - and one can unpack additional data from the term posting lists - - e.g., for term proximity scoring used by the nativeRank ranking feature, - which also requires unpacking of positions of the term within the document. -

    -

    - The way hybrid query evaluation is done is that TAAT is used for sub-branches - of the overall query tree, which is not used for term-based ranking. -

    -

    - Using TAAT can speed up query matching significantly (up to 30-50%) - in cases where the query tree is large and complex, - and where only parts of the query tree are used for term-based ranking. - Examples of query tree branches that would require DAAT - is using text ranking features like bm25 or nativeRank. - - The list of ranking features which can handle TAAT is long, - but using attribute or tensor features only - can have the entire tree evaluated using TAAT. -

    -

    - For example, for a query where there is a user text query from an end user, - one can use userQuery() YQL syntax and combine it with application-level constraints. - The application level filter constraints in the query could benefit from using TAAT. - Given the following document schema: -

    -
    -search news {
    -    document news {
    -        field title type string {}
    -        field body type string{}
    -        field popularity type float {}
    -        field market type string {
    -            rank:filter
    -            indexing: attribute
    -            attribute: fast-search
    -        }
    -        field language type string {
    -            rank:filter
    -            indexing: attribute
    -            attribute: fast-search
    -        }
    -    }
    -    fieldset default {
    -        fields: title,body
    -    }
    -    rank-profile text-and-popularity {
    -        first-phase {
    -            expression: attribute(popularity) + log10(bm25(title)) + log10(bm25(body))
    -        }
    -    }
    -}
    -
    -

    - In this case, the rank profile only uses two ranking features, - the popularity attribute and the bm25 score of the userQuery(). - These are used in the default fieldset containing the title and body. - Notice how neither market nor language is used in the ranking expression. -

    -

    - In this query example, there is a language constraint and a market constraint, - where both language and market are queried with a long list of valid values using OR, - meaning that the document should match any of the market constraints and any of the language constraints: -

    -
    {% highlight json %}
    -{
    -    "hits": 10,
    -    "ranking.profile": "text-and-popularity",
    -    "yql": "select * from sources * where userQuery() and
    -      (language contains \"en\" or language contains \"br\") and
    -      (market contains \"us\" or market contains \"eu\" or market contains \"apac\" or market contains \"..\" )",
    -    "query": "cat video",
    -    "ranking.matching.termwiselimit": 0.1
    -}
    -{% endhighlight %}
    -

    - The language and the market constraints in the query tree are not used in the - ranking score, and that part of the query tree could be evaluated using TAAT. - See also multi lookup set filter - for how to most efficiently search with large set filters. - - The subtree result is then passed as a bit vector into the DAAT query evaluation, - which could significantly speed up the overall evaluation. -

    -

    - Enabling hybrid TAAT is done by passing - ranking.matching.termwiselimit=0.1 as a request parameter. It's possible to - evaluate the performance impact by changing this limit. Setting the limit to 0 will force - termwise evaluation, which might hurt performance. -

    -

    - One can evaluate if using the hybrid evaluation improves search performance by adding the above parameter. - The limit is compared to the hit fraction estimate of the entire query tree. - If the hit fraction estimate is higher than the limit, - the termwise evaluation is used to evaluate the sub-branch of the query. -

    - - -

    Indexing uuids

    -

    - When configuring string - type fields with index, - the default match mode is text. - This means Vespa will tokenize the content and index the tokens. -

    -

    - The string representation of an - Universally unique identifier (UUID) is 32 hexadecimal (base 16) digits, - in five groups, separated by hyphens, in the form 8-4-4-4-12, - for a total of 36 characters (32 alphanumeric characters and four hyphens). -

    -

    - Example: Indexing 123e4567-e89b-12d3-a456-426655440000 with the above document definition, - Vespa will tokenize this into 5 tokens: [123e4567,e89b,12d3,a456,426655440000], - each of which could be matched independently, leading to possible incorrect matches. -

    -

    - To avoid this, change the mode to match: word to - treat the entire uuid as one token/word: -

    -
    -field uuid type string {
    -    indexing: summary | index
    -    match:    word
    -    rank:     filter
    -}
    -
    -

    - In addition, configure the uuid as a - rank: filter field - - the field will then be represented as efficiently as possible during search and ranking. - The rank:filter behavior can also be triggered at query time on a per-query item basis - by the com.yahoo.prelude.query.Item.setRanked() - in a custom searcher. -

    - - - -

    Parent child and search performance

    -

    - When searching imported attribute fields (with fast-search) from parent document types, - there is an additional indirection that can be reduced significantly - if the imported field is defined with rank:filter - and visibility-delay is configured to > 0. - The rank:filter setting impacts posting list granularity - and visibility-delay enables a cache for the indirection between the child and parent document. -

    - - - -

    Ranking and ML Model inferences

    -

    - Vespa scales with the number of hits the query retrieves per node/search thread, - and which needs to be evaluated by the first-phase ranking function. - Read more on phased ranking. - Phased ranking enables using more resources during the second phase ranking step than in the first phase. - The first phase should focus on getting decent recall (retrieving relevant documents in the top k), - while the second phase should tune precision. -

    -

    - For text search applications, - consider using the WAND query operator - - WAND can efficiently (sublinear) find the top-k documents using an inner scoring function. -

    - - -

    Multi Lookup - Set filtering

    -

    - Several real-world search use cases are built around limiting or filtering based on a set filter. - If the contents of a field in the document match any of the values in the query set, it should be retrieved. - E.g., searching data for a set of users: -

    -
    -select * from sources * where user_id = 1 or user_id = 2 or user_id = 3 or user_id = 3 or user_id = 4 or user_id 5 ...
    -
    -

    - For OR filters over the same field, it is strongly recommended to use the - in query operator instead. - It has considerably better performance than plain OR for set filtering: -

    -
    -select * from sources * where user_id in (1, 2, 3, 4, 5)
    -
    -{% include note.html content=' -Large sets can slow down YQL-parsing of the query - -see parameter substitution -for how to send the set in a compact, performance-effective way.'%} -

    - Attribute fields used like the above without other stronger query terms, - should have fast-search and rank: filter. - If there is a large number of unique values in the field, - it is also faster to use hash dictionary instead of btree, - which is the default data structure for dictionaries for attribute fields with fast-search: -

    -
    -field user_id type long {
    -    indexing:   summary | attribute
    -    attribute:  fast-search
    -    dictionary: hash
    -    rank:       filter
    -}
    -
    -

    - For string fields, we also need to include match - settings if using the hash dictionary:

    -
    -field user_id_str type string {
    -    indexing:   summary | attribute
    -    attribute:  fast-search
    -    match:      cased	
    -    rank:       filter
    -    dictionary { 
    -        hash
    -        cased
    -    }
    -}
    -
    -

    -If having 10M unique user_ids in the dictionary and searching for 1000 users per query, -the btree dictionary would be 1000 lookup times log(10M), -while hash based would be 1000 lookups times O(1). Still, the btree dictionary -offers more flexibility in terms of match settings. -

    -

    - The in query set filtering approach can be used in combination with hybrid TAAT - evaluation to further improve performance. See the hybrid TAAT/DAAT section. -

    -

    - Also see the dictionary schema reference. -

    - -{% include note.html content=' -For most use cases, the time spent on dictionary traversal is negligible compared to the time spent on -query evaluation (matching and ranking). If the query is very selective, for example, using -vespa as a key-value lookup store with ranking support, the dictionary traversal time can be significant.'%} - -

    Document summaries - hits

    -

    - If queries request many (thousands) of hits from a content cluster with few content nodes, - increasing the summary cache might reduce latency and cost. -

    - Using explicit document summaries, - Vespa can support memory-only summary fetching if all fields referenced in the document summary are - all defined with attribute. Dedicated in-memory summaries avoid (potential) - disk read and summary chunk decompression. - Vespa document summaries are stored using compressed - chunks. - See also the practical - search performance guide on hits fetching. -

    - - -

    Boolean, numeric, text attribute

    -

    -When using the attribute field type, considering performance, this is a rule of thumb: -

    -
      -
    1. Use boolean if a field is a boolean (max two values)
    2. -
    3. Use a string attribute if there is a set of values - only unique strings are stored
    4. -
    5. Use a numeric attribute for range searches
    6. -
    7. Use a numeric attribute if the data really is numeric; don't replace numeric with string numeric
    8. -
    -

    -Refer to attributes for details. -

    - - - -

    Tensor ranking

    -

    - The ranking workload can be significant for large tensors - - it is important to understand both the potential memory and computational cost for each query. -

    - - -

    Memory

    -

    - Assume the dot product of two tensors with 1000 values of 8 bytes each, as in - tensor<double>(x[1000]). - With one query tensor and one document tensor, - the dot product is sum(query(tensor1) * attribute(tensor2)). - Given a Haswell CPU architecture, where the theoretical upper memory bandwidth is 68 GB/sec, - this gives 68 GB/sec / 8 KB = 9M ranking evaluations/sec. - In other words, for a 1 M index, 9 queries per second before being memory bound. -

    -

    - See below for using smaller cell value types, and read more about - - quantization. -

    -

    Compute

    -

    - When using tensor types with at least one mapped dimension (sparse or mixed tensor), - attribute: fast-rank - can be used to optimize the tensor attribute for ranking expression evaluation at the cost of using more memory. - This is a good tradeoff if benchmarking indicates significant latency improvements with fast-rank. -

    -

    - When optimizing ranking functions with tensors, try to avoid temporary objects. - Use the Tensor Playground to evaluate what the expressions map to, - using the execution details - - - - - - - - - - to list the detailed steps - find examples below. -

    - - -

    Multiphase ranking

    -

    - To save both memory and compute resources, use multiphase ranking. - In short, use less expensive ranking evaluations to find the most promising candidates, - then a high-precision evaluation for the top-k candidates. -

    -

    - The blog post series on - Building Billion-Scale Vector Search is a good read. -

    - - -

    Cell value types

    - - - - - - - - - - - - - - - - - - - - - - - - - -
    TypeDescription
    double -

    - The default tensor cell type is the 64-bit floating-point double format. - It gives the best precision at the cost of high memory usage and somewhat slower calculations. - Using a smaller value type increases performance, trading off precision, - so consider changing to one of the cell types below before scaling the application. -

    -
    float -

    - The 32-bit floating-point format float should usually be used for all tensors - when scaling for production. - Note that some frameworks like TensorFlow prefer 32-bit floats. - A vector with 1000 dimensions, - tensor<float>(x[1000]) uses approximately 4K memory per tensor value. -

    -
    bfloat16 -

    - This type has the range as a normal 32-bit float but only 8 bits of precision - and can be thought of as a "float with lossy compression" - - see Wikipedia. - If memory (or memory bandwidth) is a concern, - change the most space-consuming tensors to use the bfloat16 cell type. - Some careful analysis of the data is required before using this type. -

    -

    - When doing calculations, bfloat16 will act as if it was a 32-bit float, - but the smaller size comes with a potential computational overhead. - In most cases, the bfloat16 needs conversion to a 32-bit float - before the actual calculation can occur, adding an extra conversion step. -

    -

    - In some cases, having tensors with bfloat16 cells might bypass some built-in optimizations - (like matrix multiplication) that will be hardware-accelerated only if the cells are of the same type. - To avoid this, - use the cell_cast tensor operation - to make sure the cells are of the right type before doing the more expensive operations. -

    -
    int8 -

    - If using machine learning to generate a model with data quantization, - one can target the int8 cell value type, - which is a signed integer with a range from -128 to +127 only. - This is also treated like a "float with limited range and lossy compression" by the Vespa tensor framework, - and gives results as if it were a 32-bit float when any calculation is done. - This type is also suitable when representing boolean values (0 or 1). -

    - {% include note.html content='If the input for an int8 cell is not directly representable, - the resulting cell value is undefined, - so take care to only input numbers in the [-128,127] range.' %} -

    - It's also possible to use int8 representing binary data for - hamming distance Nearest-Neighbor search. - Refer to billion-scale-knn for example use. -

    -
    - - -

    Inner/outer products

    -

    The following is a primer into inner/outer products and execution details:

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    tensor atensor bproductsumcomment
    tensor(x[3]):[1.0, 2.0, 3.0]tensor(x[3]):[4.0, 5.0, 6.0]tensor(x[3]):[4.0, 10.0, 18.0]32 -

    - - Playground example. - The dimension name and size are the same in both tensors - this is an inner product with a scalar result. -

    -
    tensor(x[3]):[1.0, 2.0, 3.0]tensor(y[3]):[4.0, 5.0, 6.0]tensor(x[3],y[3]):[
    [4.0, 5.0, 6.0],
    [8.0, 10.0, 12.0],
    [12.0, 15.0, 18.0] ]
    90 -

    - - Playground example. - The dimension size is the same in both tensors, but dimensions have different names -> - this is an outer product; the result is a two-dimensional tensor. -

    -
    tensor(x[3]):[1.0, 2.0, 3.0]tensor(x[2]):[4.0, 5.0]undefined -

    - - Playground example. - Two tensors in the same dimension but with different lengths -> undefined. -

    -
    tensor(x[3]):[1.0, 2.0, 3.0]tensor(y[2]):[4.0, 5.0]tensor(x[3],y[2]):[
    [4.0, 5.0],
    [8.0, 10.0],
    [12.0, 15.0] ]
    54 -

    - - Playground example. - Two tensors with different names and dimensions -> - this is an outer product; the result is a two-dimensional tensor. -

    -
    -

    - Inner product - observe optimized into DenseDotProductFunction - with no temporary objects: -

    -
    {% highlight json %}
    -[ {
    -    "class": "vespalib::eval::tensor_function::Inject",
    -    "symbol": ""
    -  },
    -  {
    -    "class": "vespalib::eval::tensor_function::Inject",
    -    "symbol": ""
    -  },
    -  {
    -    "class": "vespalib::eval::DenseDotProductFunction",
    -    "symbol": "vespalib::eval::(anonymous namespace)::my_cblas_double_dot_product_op(vespalib::eval::InterpretedFunction::State&, unsigned long)"
    -  } ]
    -{% endhighlight %}
    -

    - Outer product, parsed into a tensor multiplication (DenseSimpleExpandFunction), - followed by a Reduce operation: -

    -
    {% highlight json %}
    -[ {
    -    "class": "vespalib::eval::tensor_function::Inject",
    -    "symbol": ""
    -  },
    -  {
    -    "class": "vespalib::eval::tensor_function::Inject",
    -    "symbol": ""
    -  },
    -  {
    -    "class": "vespalib::eval::DenseSimpleExpandFunction",
    -    "symbol": "void vespalib::eval::(anonymous namespace)::my_simple_expand_op, true>(vespalib::eval::InterpretedFunction::State&, unsigned long)"
    -  },
    -  {
    -    "class": "vespalib::eval::tensor_function::Reduce",
    -    "symbol": "void vespalib::eval::instruction::(anonymous namespace)::my_full_reduce_op >(vespalib::eval::InterpretedFunction::State&, unsigned long)"
    -  } ]
    -{% endhighlight %}
    -

    - Note that an inner product can also be run on mapped tensors - (Playground example): -

    -
    {% highlight json %}
    -[ {
    -    "class": "vespalib::eval::tensor_function::Inject",
    -    "symbol": ""
    -  },
    -  {
    -    "class": "vespalib::eval::tensor_function::Inject",
    -    "symbol": ""
    -  },
    -  {
    -    "class": "vespalib::eval::SparseFullOverlapJoinFunction",
    -    "symbol": "void vespalib::eval::(anonymous namespace)::my_sparse_full_overlap_join_op, true>(vespalib::eval::InterpretedFunction::State&, unsigned long)"
    -  } ]
    -{% endhighlight %}
    - - -

    Mapped lookups

    -

    - sum(model_id * models, m_id) -

    - - - - - - - - -
    tensor nametensor type
    model_idtensor(m_id{})
    modelstensor(m_id{}, x[3])
    -

    - Using a mapped dimension to select an indexed tensor can be considered a - mapped lookup. - This is similar to creating a slice but optimized into a single MappedLookup - - see - Tensor Playground example. -

    -
    {% highlight json %}
    -[ {
    -    "class": "vespalib::eval::tensor_function::Inject",
    -    "symbol": ""
    -  },
    -  {
    -    "class": "vespalib::eval::tensor_function::Inject",
    -    "symbol": ""
    -  },
    -  {
    -    "class": "vespalib::eval::MappedLookup",
    -    "symbol": "void vespalib::eval::(anonymous namespace)::my_mapped_lookup_op(vespalib::eval::InterpretedFunction::State&, unsigned long)"
    -  } ]
    -{% endhighlight %}
    - - -

    Three-way dot product - mapped

    -

    - sum(query(model_id) * model_weights * model_features) -

    - - - - - - - - - -
    tensor nametensor type
    query(model_id)tensor(model{})
    model_weightstensor(model{}, feature{})
    model_featurestensor(feature{})
    -

    - Three-way mapped (sparse) dot product: - - Tensor Playground -

    -
    {% highlight json %}
    -[ {
    -    "class": "vespalib::eval::tensor_function::Inject",
    -    "symbol": ""
    -  },
    -  {
    -    "class": "vespalib::eval::tensor_function::Inject",
    -    "symbol": ""
    -  },
    -  {
    -    "class": "vespalib::eval::tensor_function::Inject",
    -    "symbol": ""
    -  },
    -  {
    -    "class": "vespalib::eval::Sparse112DotProduct",
    -    "symbol": "void vespalib::eval::(anonymous namespace)::my_sparse_112_dot_product_op(vespalib::eval::InterpretedFunction::State&, unsigned long)"
    -  } ]
    -{% endhighlight %}
    - - -

    Three-way dot product - mixed

    -

    - sum(query(model_id) * model_weights * model_features) -

    - - - - - - - - - -
    tensor nametensor type
    query(model_id)tensor(model{})
    model_weightstensor(model{}, feature[2])
    model_featurestensor(feature[2])
    -

    - Three-way mapped (mixed) dot product: - - Tensor Playground -

    -
    {% highlight json %}
    -[ {
    -    "class": "vespalib::eval::tensor_function::Inject",
    -    "symbol": ""
    -  },
    -  {
    -    "class": "vespalib::eval::tensor_function::Inject",
    -    "symbol": ""
    -  },
    -  {
    -    "class": "vespalib::eval::tensor_function::Inject",
    -    "symbol": ""
    -  },
    -  {
    -    "class": "vespalib::eval::Mixed112DotProduct",
    -    "symbol": "void vespalib::eval::(anonymous namespace)::my_mixed_112_dot_product_op(vespalib::eval::InterpretedFunction::State&, unsigned long)"
    -  } ]
    -{% endhighlight %}
    diff --git a/en/performance/feature-tuning.mdx b/en/performance/feature-tuning.mdx new file mode 100644 index 0000000000..da80ffe730 --- /dev/null +++ b/en/performance/feature-tuning.mdx @@ -0,0 +1,585 @@ +--- +title: "Vespa Serving Tuning" +sidebarTitle: "Feature tuning" +--- + +This document describes how to tune certain features of an application for high query serving performance, where the main focus is on content cluster search features; see [Container tuning](/en/performance/container-tuning) for tuning of container clusters. The [search sizing guide](/en/performance/sizing-search) is about *scaling* an application deployment. + +## Attribute vs index + +The [attribute](/en/content/attributes) documentation summarizes when to use [attribute](/en/reference/schemas/schemas#attribute) in the [indexing](/en/reference/schemas/schemas#indexing) statement. Also see the [procedure](/en/reference/schemas/schemas#modifying-schemas) for changing from attribute to index and vice-versa. + +```js +field timestamp type long { + indexing: summary | attribute +} +``` + +If both index and attribute are configured for string-type fields, Vespa will search and match against the index with default match `text`. All numeric type fields and tensor fields are attribute (in-memory) fields in Vespa. + +## When to use fast-search for attribute fields + +By default, Vespa does not build any posting list index structures over *attribute* fields. Adding *fast-search* to the attribute definition as shown below will add an in-memory B-tree posting list structure which enables faster search for some cases (but not all, see next paragraph): + +```js +field timestamp type long { + indexing: summary | attribute + attribute: fast-search + rank: filter +} +``` + +When Vespa runs a query with multiple query items, it builds a query execution plan. It tries to optimize the plan so that the temporary result set is as small as possible. To do this, restrictive query tree items (matching few documents) are evaluated early. The query execution plan looks at hit count estimates for each part of the query tree using the index and B-tree dictionaries, which track the number of documents in which a given term occurs. + +However, for attribute fields without [fast-search](/en/content/attributes#fast-search) there is no hit count estimate, so the estimate becomes the total number of documents (matches all) and the query tree item is moved to the end of the query evaluation. A query with only one query term searching an attribute field without `fast-search` would be a linear scan over all documents and thus expensive: + +```text +select * from sources * where range(timestamp, 0, 100) +``` + +But if this query term is *and*\-ed with another term that matches fewer documents, that term will determine the cost instead, and fast-search won't be necessary, e.g.: + +```text +select * from sources * where range(timestamp, 0, 100) and uuid contains "123e4567-e89b-12d3-a456-426655440000" +``` + +The general rules of thumb for when to use fast-search for an attribute field are: + +- Use *fast-search* if the attribute field is searched without any other query terms +- Use *fast-search* if the attribute field could limit the total number of hits efficiently + +Changing fast-search aspect of the attribute is a [live change](/en/reference/schemas/schemas#modifying-schemas) which does not require any re-feeding, so testing the performance with and without is low effort. Adding or removing *fast-search* requires restart. + +Note that *attribute* fields with *fast-search* that are not used in term based [ranking](/en/basics/ranking) should use *rank: filter* for optimal performance. See reference [rank: filter](/en/reference/schemas/schemas#rank). + +See optimization for sorting on a *single-value numeric attribute with fast-search* using [sorting.degrading](/en/reference/api/query#sorting.degrading). + +## Tuning query performance for lexical search + +Lexical search (or keyword-based search) is a method that matches query terms as they appear in indexed documents. It relies on the lexical representation of words rather than their meaning, and is one of the two retrieval methods used in [hybrid search](/en/learn/tutorials/hybrid-search). Lexical search in Vespa is done by querying string (text) [index](/en/basics/schemas#document-fields) fields, typically using the [weakAnd](/en/ranking/wand#weakand) query operator with [BM25](/en/ranking/bm25) ranking. + +The following schema represents a simple article document with *title* and *content* fields, that can represent Wikipedia articles as an example. A *default* fieldset is specified such that user queries are matched against both the *title* and *content* fields. BM25 ranking combines the scores of both fields in the *default* rank profile. In addition, the *optimized* rank profile specifies tuning parameters to improve query performance: + +```js expandable +schema article { + document article { + field title type string { + indexing: index | summary + index: enable-bm25 + } + field content type string { + indexing: index | summary + index: enable-bm25 + } + } + + fieldset default { + fields: title, content + } + + rank-profile default { + first-phase { + expression: bm25(title) + bm25(content) + } + } + + rank-profile optimized inherits default { + filter-threshold: 0.05 + weakand { + stopword-limit: 0.6 + adjust-target: 0.01 + } + } +} +``` + +The following shows an example question-answer query against a collection of articles, using the *weakAnd* query operator and the *optimized* rank profile. Question-answer queries are often written in full sentences, and as a consequence, they tend to contain many stopwords that are present in many documents and of less relevance when it comes to ranking. E.g., terms as "the", "in", and "are" are typically present in more the 60% of the documents: + +```json +{ + "yql": "select * from article where userQuery()", + "ranking.profile": "optimized", + "query": "what are the three highest mountains in the world" +} +``` + +The cost of evaluating such a query is primarily linear with the number of matched documents. The *AND* operator is most effective, but often ends up being too restrictive by not returning enough matches. The *OR* operator is less restrictive, but has the problem of returning too many matches, which is very costly. The *weakAnd* operator is somewhere in between the two in cost. + +### Posting Lists + +To find matching documents, the query operator uses the *posting lists* associated with each query term. A posting list is part of the inverted index and contains all occurrences of a term within a collection of documents. It consists of document IDs for documents that contain the term, and additional information such as the positions of the term within those documents (used for ranking purposes). For common terms (e.g., stopwords), the posting lists are very large and can be expensive to use during evaluation and ranking. CPU work is required to iterate them, and I/O work is required to load portions of them from disk to memory with MMAP. The last part is especially problematic when all posting lists of a disk index cannot fit into physical memory, and the system must constantly swap parts of them in and out of memory, leading to high I/O wait times. + +To improve query performance, the following tuning parameters are available, as seen used in the *optimized* rank profile. These are used to make tradeoffs between performance and quality. + +- **Use more compact posting lists for common terms**: Setting [filter-threshold](/en/reference/schemas/schemas#filter-threshold) to 0.05 ensures that all terms that are estimated to occur in more than 5% of the documents are handled with [compact posting lists (bitvectors)](/en/content/proton#index) instead of the full posting lists. This makes matching faster at the cost of producing less information for BM25 ranking (only a boolean signal is available). +- **Avoid using large posting lists all together**: Setting [stopword-limit](/en/reference/schemas/schemas#weakand-stopword-limit) to 0.6, ensures that all terms that are estimated to occur in more than 60% of the documents are considered stopwords and dropped entirely from the query and also from ranking. +- **Reduce the number of hits produced by *weakAnd***: Setting [adjust-target](/en/reference/schemas/schemas#weakand-adjust-target) ensures that documents that only match terms that occur very frequently in the documents are not considered hits. This also removes the need to calculate *first-phase* ranking for these documents, which is beneficial if *first-phase* ranking is more complex and expensive. + +### Performance + + +The tuning parameters used in the *optimized* rank profile have been shown to provide a good tradeoff between performance and quality in testing. A Wikipedia dataset with [SQuAD](https://nlp.stanford.edu/pubs/rajpurkar2016squad.pdf) (Stanford Question Answering Dataset) queries was used to analyze performance, and [trec-covid](https://ir.nist.gov/trec-covid/), [MS MARCO](https://microsoft.github.io/msmarco/) and [nfcorpus](https://huggingface.co/datasets/BeIR/nfcorpus) from the BEIR dataset to analyze quality implications. + +For instance, the query performance was tripled without any measurable drop in quality with the Wikipedia dataset, using the tuning parameters in the *optimized* rank profile. See the blog post [Tripling the query performance of lexical search](https://blog.vespa.ai/tripling-the-query-performance-of-lexical-search/) for more details. Note that testing should be conducted on your particular dataset to find the right tradeoff between performance and quality. + +## Hybrid TAAT and DAAT query evaluation + +Vespa supports **hybrid** query evaluation over inverted indexes, combining *TAAT* and *DAAT* evaluation to combine the best of both query evaluation techniques. Hybrid is not enabled per default and is triggered by a run-time query parameter. + +- **TAAT:** *Term At A Time* scores documents one query term at a time. The entire posting iterator can be read per query term, and the score of a document is accumulated. It is CPU cache friendly as posting data is read sequentially without randomly seeking the posting list iterator. The downside is that *TAAT* limits the term-based ranking function to be a linear sum of term scores. This downside is one reason why most search engines use *DAAT*. +- **DAAT:** *Document At A Time* scores documents completely one at a time. This requires multiple seeks in the term posting lists, which is CPU cache unfriendly but allows non-linear ranking functions. + +Generally, Vespa does *DAAT* (document-at-a-time) query evaluation and not *TAAT* (term-at-a time) for the reason listed above. + +Ranking (score calculation) and matching (does the document match the query logic) is not fully two separate disjunct phases, where one first finds matches and calculates the ranking score in a later phase. Matching and *first-phase* score calculation is interleaved when using *DAAT*. + +The *first-phase* ranking score is assigned to the hit when it satisfies the query constraints. At that point, the term iterators are positioned at the document ID and one can unpack additional data from the term posting lists - e.g., for term proximity scoring used by the [nativeRank](/en/ranking/nativerank) ranking feature, which also requires unpacking of positions of the term within the document. + +The way hybrid query evaluation is done is that *TAAT* is used for sub-branches of the overall query tree, which is not used for term-based ranking. + +Using *TAAT* can speed up query matching significantly (up to 30-50%) in cases where the query tree is large and complex, and where only parts of the query tree are used for term-based ranking. Examples of query tree branches that would require *DAAT* is using text ranking features like [bm25 or nativeRank](/en/reference/ranking/rank-features). The list of ranking features which can handle *TAAT* is long, but using [attribute or tensor](/en/ranking/tensor-user-guide) features only can have the entire tree evaluated using *TAAT*. + +For example, for a query where there is a user text query from an end user, one can use *userQuery()* YQL syntax and combine it with application-level constraints. The application level filter constraints in the query could benefit from using *TAAT*. Given the following document schema: + +```js expandable +search news { + document news { + field title type string {} + field body type string{} + field popularity type float {} + field market type string { + rank:filter + indexing: attribute + attribute: fast-search + } + field language type string { + rank:filter + indexing: attribute + attribute: fast-search + } + } + fieldset default { + fields: title,body + } + rank-profile text-and-popularity { + first-phase { + expression: attribute(popularity) + log10(bm25(title)) + log10(bm25(body)) + } + } +} +``` + +In this case, the rank profile only uses two ranking features, the popularity attribute and the [bm25](/en/ranking/bm25) score of the userQuery(). These are used in the default fieldset containing the title and body. Notice how neither *market* nor *language* is used in the ranking expression. + +In this query example, there is a language constraint and a market constraint, where both language and market are queried with a long list of valid values using OR, meaning that the document should match any of the market constraints and any of the language constraints: + +```json +{ + "hits": 10, + "ranking.profile": "text-and-popularity", + "yql": "select * from sources * where userQuery() and + (language contains \"en\" or language contains \"br\") and + (market contains \"us\" or market contains \"eu\" or market contains \"apac\" or market contains \"..\" )", + "query": "cat video", + "ranking.matching.termwiselimit": 0.1 +} +``` + +The language and the market constraints in the query tree are not used in the ranking score, and that part of the query tree could be evaluated using *TAAT*. See also [multi lookup set filter](#multi-lookup-set-filtering) for how to most efficiently search with large set filters. The subtree result is then passed as a bit vector into the *DAAT* query evaluation, which could significantly speed up the overall evaluation. + +Enabling hybrid *TAAT* is done by passing `ranking.matching.termwiselimit=0.1` as a request parameter. It's possible to evaluate the performance impact by changing this limit. Setting the limit to 0 will force termwise evaluation, which might hurt performance. + +One can evaluate if using the hybrid evaluation improves search performance by adding the above parameter. The limit is compared to the hit fraction estimate of the entire query tree. If the hit fraction estimate is higher than the limit, the termwise evaluation is used to evaluate the sub-branch of the query. + +## Indexing uuids + +When configuring [string](/en/reference/schemas/schemas#string) type fields with `index`, the default [match](/en/reference/schemas/schemas#match) mode is `text`. This means Vespa will [tokenize](/en/linguistics/linguistics-opennlp#tokenization) the content and index the tokens. + +The string representation of an [Universally unique identifier](https://en.wikipedia.org/wiki/Universally_unique_identifier) (UUID) is 32 hexadecimal (base 16) digits, in five groups, separated by hyphens, in the form 8-4-4-4-12, for a total of 36 characters (32 alphanumeric characters and four hyphens). + +Example: Indexing `123e4567-e89b-12d3-a456-426655440000` with the above document definition, Vespa will tokenize this into 5 tokens: `[123e4567,e89b,12d3,a456,426655440000]`, each of which could be matched independently, leading to possible incorrect matches. + +To avoid this, change the mode to [match: word](/en/reference/schemas/schemas#word) to treat the entire uuid as *one* token/word: + +```js +field uuid type string { + indexing: summary | index + match: word + rank: filter +} +``` + +In addition, configure the `uuid` as a [rank: filter](/en/reference/schemas/schemas#rank) field - the field will then be represented as efficiently as possible during search and ranking. The `rank:filter` behavior can also be triggered at query time on a per-query item basis by the `com.yahoo.prelude.query.Item.setRanked()` in a [custom searcher](/en/applications/searchers). + +## Parent child and search performance + +When searching imported attribute fields (with `fast-search`) from parent document types, there is an additional indirection that can be reduced significantly if the imported field is defined with `rank:filter` and [visibility-delay](/en/reference/applications/services/content#visibility-delay) is configured to > 0. The [rank:filter](/en/reference/schemas/schemas#rank) setting impacts posting list granularity and `visibility-delay` enables a cache for the indirection between the child and parent document. + +## Ranking and ML Model inferences + +Vespa [scales](/en/performance/sizing-search) with the number of hits the query retrieves per node/search thread, and which needs to be evaluated by the first-phase ranking function. Read more on [phased ranking](/en/ranking/phased-ranking). Phased ranking enables using more resources during the second phase ranking step than in the first phase. The first phase should focus on getting decent recall (retrieving relevant documents in the top k), while the second phase should tune precision. + +For [text search](/en/ranking/nativerank) applications, consider using the [WAND](/en/ranking/wand) query operator - WAND can efficiently (sublinear) find the top-k documents using an inner scoring function. + +## Multi Lookup - Set filtering + +Several real-world search use cases are built around limiting or filtering based on a set filter. If the contents of a field in the document match any of the values in the query set, it should be retrieved. E.g., searching data for a set of users: + +```sql +select * from sources * where user_id = 1 or user_id = 2 or user_id = 3 or user_id = 3 or user_id = 4 or user_id 5 ... +``` + +For OR filters over the same field, it is strongly recommended to use the [in query operator](/en/reference/querying/yql#in) instead. It has considerably better performance than plain OR for set filtering: + +```sql +select * from sources * where user_id in (1, 2, 3, 4, 5) +``` + + +**Note:** + +Large sets can slow down YQL-parsing of the query - see [parameter substitution](/en/reference/querying/yql#parameter-substitution) for how to send the set in a compact, performance-effective way. + + +Attribute fields used like the above without other stronger query terms, should have `fast-search` and `rank: filter`. If there is a large number of unique values in the field, it is also faster to use `hash` dictionary instead of `btree`, which is the default data structure for dictionaries for attribute fields with `fast-search`: + +```sql +field user_id type long { + indexing: summary | attribute + attribute: fast-search + dictionary: hash + rank: filter +} +``` + +For `string` fields, we also need to include [match](/en/reference/schemas/schemas#match) settings if using the `hash` dictionary: + +```sql +field user_id_str type string { + indexing: summary | attribute + attribute: fast-search + match: cased + rank: filter + dictionary { + hash + cased + } +} +``` + +If having 10M unique user\_ids in the dictionary and searching for 1000 users per query, the *btree* dictionary would be 1000 lookup times log(10M), while *hash* based would be 1000 lookups times O(1). Still, the *btree* dictionary offers more flexibility in terms of [match](/en/reference/schemas/schemas#match) settings. + +The `in` query set filtering approach can be used in combination with hybrid *TAAT* evaluation to further improve performance. See the [hybrid TAAT/DAAT](#hybrid-taat-daat) section. + +Also see the [dictionary schema reference](/en/reference/schemas/schemas#dictionary). + + +**Note:** + +For most use cases, the time spent on dictionary traversal is negligible compared to the time spent on query evaluation (matching and ranking). If the query is very selective, for example, using vespa as a key-value lookup store with ranking support, the dictionary traversal time can be significant. + + +## Document summaries - hits + +If queries request many (thousands) of hits from a content cluster with few content nodes, increasing the [summary cache](/en/performance/caches-in-vespa) might reduce latency and cost. + +Using [explicit document summaries](/en/querying/document-summaries), Vespa can support memory-only summary fetching if all fields referenced in the document summary are **all** defined with `attribute`. Dedicated in-memory summaries avoid (potential) disk read and summary chunk decompression. Vespa document summaries are stored using compressed [chunks](/en/reference/applications/services/content#summary-store-logstore-chunk). See also the [practical search performance guide on hits fetching](/en/performance/practical-search-performance-guide#hits-and-summaries). + +## Boolean, numeric, text attribute + +When using the attribute field type, considering performance, this is a rule of thumb: +1. Use boolean if a field is a boolean (max two values) +2. Use a string attribute if there is a set of values - only unique strings are stored +3. Use a numeric attribute for range searches +4. Use a numeric attribute if the data really is numeric; don't replace numeric with string numeric + + +Refer to [attributes](/en/content/attributes) for details. + +## Tensor ranking + +The ranking workload can be significant for large tensors - it is important to understand both the potential memory and computational cost for each query. + +### Memory + +Assume the dot product of two tensors with 1000 values of 8 bytes each, as in `tensor(x[1000])`. With one query tensor and one document tensor, the dot product is `sum(query(tensor1) * attribute(tensor2))`. Given a Haswell CPU architecture, where the theoretical upper memory bandwidth is 68 GB/sec, this gives 68 GB/sec / 8 KB = 9M ranking evaluations/sec. In other words, for a 1 M index, 9 queries per second before being memory bound. + +See below for using smaller [cell value types](#cell-value-types), and read more about [quantization](https://blog.vespa.ai/from-research-to-production-scaling-a-state-of-the-art-machine-learning-system/#model-quantization). + +### Compute + +When using tensor types with at least one mapped dimension (sparse or mixed tensor), [attribute: fast-rank](/en/reference/schemas/schemas#attribute) can be used to optimize the tensor attribute for ranking expression evaluation at the cost of using more memory. This is a good tradeoff if benchmarking indicates significant latency improvements with `fast-rank`. + +When optimizing ranking functions with tensors, try to avoid temporary objects. Use the [Tensor Playground](https://docs.vespa.ai/playground/) to evaluate what the expressions map to, using the execution details to list the detailed steps - find examples below. + +### Multiphase ranking + +To save both memory and compute resources, use [multiphase ranking](/en/ranking/phased-ranking). In short, use less expensive ranking evaluations to find the most promising candidates, then a high-precision evaluation for the top-k candidates. + +The blog post series on [Building Billion-Scale Vector Search](https://blog.vespa.ai/building-billion-scale-vector-search/) is a good read. + +### Cell value types + + + + + + + + + + + + + + + + + + + + + + + + + + +
    TypeDescription
    doubleThe default tensor cell type is the 64-bit floating-point {`double`} format. It gives the best precision at the cost of high memory usage and somewhat slower calculations. Using a smaller value type increases performance, trading off precision, so consider changing to one of the cell types below before scaling the application.
    floatThe 32-bit floating-point format {`float`} should usually be used for all tensors when scaling for production. Note that some frameworks like TensorFlow prefer 32-bit floats. A vector with 1000 dimensions, {`tensor(x[1000])`} uses approximately 4K memory per tensor value.
    bfloat16This type has the range as a normal 32-bit float but only 8 bits of precision and can be thought of as a "float with lossy compression" - see Wikipedia. If memory (or memory bandwidth) is a concern, change the most space-consuming tensors to use the {`bfloat16`} cell type. Some careful analysis of the data is required before using this type.

    When doing calculations, {`bfloat16`} will act as if it was a 32-bit float, but the smaller size comes with a potential computational overhead. In most cases, the {`bfloat16`} needs conversion to a 32-bit float before the actual calculation can occur, adding an extra conversion step.

    In some cases, having tensors with {`bfloat16`} cells might bypass some built-in optimizations (like matrix multiplication) that will be hardware-accelerated only if the cells are of the same type. To avoid this, use the cell_cast tensor operation to make sure the cells are of the right type before doing the more expensive operations.
    int8If using machine learning to generate a model with data quantization, one can target the {`int8`} cell value type, which is a signed integer with a range from -128 to +127 only. This is also treated like a "float with limited range and lossy compression" by the Vespa tensor framework, and gives results as if it were a 32-bit float when any calculation is done. This type is also suitable when representing boolean values (0 or 1).

    **Note:**

    If the input for an {`int8`} cell is not directly representable, the resulting cell value is undefined, so take care to only input numbers in the {`[-128,127]`} range.


    It's also possible to use {`int8`} representing binary data for hamming distance Nearest-Neighbor search. Refer to billion-scale-knn for example use.>
    + +### Inner/outer products + +The following is a primer into inner/outer products and execution details: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    tensor atensor bproductsumcomment
    tensor(x[3]):[1.0, 2.0, 3.0]tensor(x[3]):[4.0, 5.0, 6.0]tensor(x[3]):[4.0, 10.0, 18.0]32Playground example. The dimension name and size are the same in both tensors - this is an inner product with a scalar result.
    tensor(x[3]):[1.0, 2.0, 3.0]tensor(y[3]):[4.0, 5.0, 6.0]tensor(x[3],y[3]):[ [4.0, 5.0, 6.0], [8.0, 10.0, 12.0], [12.0, 15.0, 18.0] ]90Playground example. The dimension size is the same in both tensors, but dimensions have different names -> this is an outer product; the result is a two-dimensional tensor.
    tensor(x[3]):[1.0, 2.0, 3.0]tensor(x[2]):[4.0, 5.0]undefinedPlayground example. Two tensors in the same dimension but with different lengths -> undefined.
    tensor(x[3]):[1.0, 2.0, 3.0]tensor(y[2]):[4.0, 5.0]tensor(x[3],y[2]):[ [4.0, 5.0], [8.0, 10.0], [12.0, 15.0] ]54Playground example. Two tensors with different names and dimensions -> this is an outer product; the result is a two-dimensional tensor.
    + +Inner product - observe optimized into `DenseDotProductFunction` with no temporary objects: + +```json +[ { + "class": "vespalib::eval::tensor_function::Inject", + "symbol": "" + }, + { + "class": "vespalib::eval::tensor_function::Inject", + "symbol": "" + }, + { + "class": "vespalib::eval::DenseDotProductFunction", + "symbol": "vespalib::eval::(anonymous namespace)::my_cblas_double_dot_product_op(vespalib::eval::InterpretedFunction::State&, unsigned long)" + } ] +``` + +Outer product, parsed into a tensor multiplication (`DenseSimpleExpandFunction`), followed by a `Reduce` operation: + +```json +[ { + "class": "vespalib::eval::tensor_function::Inject", + "symbol": "" + }, + { + "class": "vespalib::eval::tensor_function::Inject", + "symbol": "" + }, + { + "class": "vespalib::eval::DenseSimpleExpandFunction", + "symbol": "void vespalib::eval::(anonymous namespace)::my_simple_expand_op, true>(vespalib::eval::InterpretedFunction::State&, unsigned long)" + }, + { + "class": "vespalib::eval::tensor_function::Reduce", + "symbol": "void vespalib::eval::instruction::(anonymous namespace)::my_full_reduce_op >(vespalib::eval::InterpretedFunction::State&, unsigned long)" + } ] +``` + +Note that an inner product can also be run on mapped tensors ([Playground example](https://docs.vespa.ai/playground/#N4KABGBEBmkFxgNrgmUrWQPYAd5QFNIAaFDSPBdDTAO30gEMSybIiFIAXA2gZywAnABQAPYAF8AlHGCiAjHHnEwogExw1K0QGY4OiZFYQJrCaQzVyuBkQttKaY3QYAjFjUwcoPfkLGSMnKKACwAdAAM2hoArJHaegBskYbOphjmrFaYNpx2zhT42eT0nACWtLQEgjiCWAAmAK4AxlwenoQMjABU7mlmKAC6IBJAA)): + +```json +[ { + "class": "vespalib::eval::tensor_function::Inject", + "symbol": "" + }, + { + "class": "vespalib::eval::tensor_function::Inject", + "symbol": "" + }, + { + "class": "vespalib::eval::SparseFullOverlapJoinFunction", + "symbol": "void vespalib::eval::(anonymous namespace)::my_sparse_full_overlap_join_op, true>(vespalib::eval::InterpretedFunction::State&, unsigned long)" + } ] +``` + +### Mapped lookups + +`sum(model_id * models, m_id)` + + + + + + + + + + + + + + + + + + +
    tensor nametensor type
    model_id{`tensor(m_id{})`}
    models{`tensor(m_id{}, x[3])`}
    + +Using a mapped dimension to select an indexed tensor can be considered a [mapped lookup](/en/ranking/tensor-examples#using-a-tensor-as-a-lookup-structure). This is similar to creating a slice but optimized into a single `MappedLookup` - see [Tensor Playground](https://docs.vespa.ai/playground/#N4KABGBEBmkFxgNrgmUrWQPYAd5QFNIAaFDSPBdDTAO30gFssATAgGwH0BLFksmpCIJIAFwK0AzlgBOACkY8WwAL4BKOMEYAmOAEYAdAAYVkARBUCVpDNXK4GRG4MppzdBszbtJ-GpmEocSlZBSVVYgAPRABmAF0NLT04RAAWY2IwAFYMsAA2YzjMnRSAdlyADlyATkLTd0sMawE7TAcRJ3cKfFbyehFJAFdGBVYOJTAAKjAvDklipTU-f0IGIZHZrl4pmbGfBd4lhqtnCF6odtXTzFdziEh+qEl2bgBjTpXVkVHvSUnNxZaJRwHT1fyNVDNWxdS5CZbkW7ue6PJgAQxwOAILE47CwWAA1oMcJwZFjBu94YJApBSSxyQQfnN-nslMR1sRFIczOCrCg4iAVEA) example. + +```json +[ { + "class": "vespalib::eval::tensor_function::Inject", + "symbol": "" + }, + { + "class": "vespalib::eval::tensor_function::Inject", + "symbol": "" + }, + { + "class": "vespalib::eval::MappedLookup", + "symbol": "void vespalib::eval::(anonymous namespace)::my_mapped_lookup_op(vespalib::eval::InterpretedFunction::State&, unsigned long)" + } ] +``` + +### Three-way dot product - mapped + +`sum(query(model_id) * model_weights * model_features)` + + + + + + + + + + + + + + + + + + + + + + +
    tensor nametensor type
    query(model_id){`tensor(model{})`}
    model_weights{`tensor(model{}, feature{})`}
    model_features{`tensor(feature{})`}
    + +Three-way mapped (sparse) dot product: [Tensor Playground](https://docs.vespa.ai/playground/#N4KABGBEBmkFxgNrgmUrWQPYAd5QFNIAaFDSPBdDTAO30gEcBXAgJwE8AKAWywBMCAGwD6AS34BKEmRqQiCSABcCtAM5Y2AHmhCsAQyUA+XgOHAAvpLjAeABjgBGC5FkQLsi6QzVyuBkTecpRobnQMfIKiAO4EYgDmABZKajI0mApQKuqaOnqGJpHmXmDQBIbMbASW1sBgADq0tmZCcPbEpeVKlQRw0HYWTsSNzVFtdh1lFVV9znAATMNNRa08jpNdPX0DcADMS6PCbeud073QcwAsYC5hHhhesr6Y-oqBYRT4z+T0iisiU26VVSQXS8gY2Q02l0BmMXEBPRqNn6cAArJNHHAAGy3dL3VCPHwfV6ENLBL5hCCQX5QFjsbj-CSSMAAKjA-1iCWSalZ7JaAM2wLJYMyTFYnFMUXEUl5HLiSRSsv5CKFd08oNCYJJ4I1VJC33CijUzB4XDpEsZMrZcq5iutysFBDU0l1GQYxtN5oZ-KZSqlnIVPPtUpVTukaoeKAAuiALEA) + +```json +[ { + "class": "vespalib::eval::tensor_function::Inject", + "symbol": "" + }, + { + "class": "vespalib::eval::tensor_function::Inject", + "symbol": "" + }, + { + "class": "vespalib::eval::tensor_function::Inject", + "symbol": "" + }, + { + "class": "vespalib::eval::Sparse112DotProduct", + "symbol": "void vespalib::eval::(anonymous namespace)::my_sparse_112_dot_product_op(vespalib::eval::InterpretedFunction::State&, unsigned long)" + } ] +``` + +### Three-way dot product - mixed + +`sum(query(model_id) * model_weights * model_features)` + + + + + + + + + + + + + + + + + + + + + + +
    tensor nametensor type
    query(model_id){`tensor(model{})`}
    model_weights{`tensor(model{}, feature[2])`}
    model_features{`tensor(feature[2])`}
    + +Three-way mapped (mixed) dot product: [Tensor Playground](https://docs.vespa.ai/playground/#N4KABGBEBmkFxgNrgmUrWQPYAd5QFNIAaFDSPBdDTAO30gEcBXAgJwE8AKAWywBMCAGwD6AS34BKEmRqQiCSABcCtAM5Y2AHmhCsAQyUA+XgOHAAvpLjAeABjgBGC5FkQLsi6QzVyuBkTecpRobnQMfIKiAO4EYgDmABZKajI0mApQKuqaOnqGJpHmXmDQBIbMbASIAEwAutbAYAA6tLZmQnD2xKXlSpUEcHYWTsSt7VFddj1lFVVOIzVjbUWdPI4zfQNDIwDMyxPCXRu9c4POcAAsYC5hHhhesr6Y-oqBYRT4z+T0iqsis36VVSQXS8gY2Q02l0BmMXEBA1qDTgiAArMQAGx1Vzpe6oR4+D6vQhpYJfMIQSC-KAsdjcf4SSRgABUYH+sQSyTULLZHQBW2BpLBmSYrE4pii4ikPPZcSSKRlfIRgrunlBoTBxPB6spIW+4UUamYPC4tPFDOlrNlnIVVqVAoIamkOoyDCNJrN9L5jMVko58u5dslysd0lVDxQdRAFiAA) + +```json +[ { + "class": "vespalib::eval::tensor_function::Inject", + "symbol": "" + }, + { + "class": "vespalib::eval::tensor_function::Inject", + "symbol": "" + }, + { + "class": "vespalib::eval::tensor_function::Inject", + "symbol": "" + }, + { + "class": "vespalib::eval::Mixed112DotProduct", + "symbol": "void vespalib::eval::(anonymous namespace)::my_mixed_112_dot_product_op(vespalib::eval::InterpretedFunction::State&, unsigned long)" + } ] +``` \ No newline at end of file diff --git a/en/performance/graceful-degradation.html b/en/performance/graceful-degradation.html deleted file mode 100644 index f9f4f69b1d..0000000000 --- a/en/performance/graceful-degradation.html +++ /dev/null @@ -1,328 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Graceful Query Coverage Degradation" -redirect_from: -- /en/graceful-degradation.html ---- - -

    - Ideally you want to query all data indexed in a Vespa cluster within the specified timeout, - but that might not always be possible: -

    -
      -
    • The system might be overloaded due to capacity constraints, - and queries do not complete within the timeout, - as they are sitting in a queue waiting for a resource. -
    • -
    • - A complex query might take longer time to execute than the specified timeout, - or the timeout is too low given the complexity of the query and available resource capacity. -
    • -
    -

    - This document describes how Vespa could gracefully degrade the result set - if the query cannot be completed within the timeout specified. -

    -

    Definitions:

    -
      -
    • - Coverage: The percentage of documents indexed which were evaluated by the query. - The ideal coverage is 100%. -
    • -
    • - Timeout: The total time a query is allowed to run for, - see timeout (default 500 ms). - Vespa is a distributed system where multiple components are involved in the query execution. -
    • -
    • - Soft Timeout: Soft timeout allows coverage to be less than 100%, - but larger than 0% if the query is approaching timeout. - Soft timeout might also be considered as an early termination technique, and is enabled by default. - Refer to ranking.softtimeout.enable. -
    • -
    • - ANN Timeout: The ANN timeout allows approximate nearest neighbor searches - to terminate early in order to leave enough time for the actual matching process. - See Early termination of approximate nearest neighbor search - for more details. -
    • -
    - - - -

    Detection

    -

    -The default JSON renderer template will always render a coverage element below the root element, -which has a degraded element if the query execution was degraded in some way. -Example request with a query timeout of 200 ms and ranking.softtimeout.enable=true: -

    -
    -/search/?searchChain=vespa&yql=select * from sources * where foo contains bar&presentation.format=json&timeout=200ms&ranking.softtimeout.enable=true
    -
    -
    {% highlight json %}
    -{
    -    "root": {
    -        "coverage": {
    -            "coverage": 99,
    -            "degraded": {
    -                "adaptive-timeout": false,
    -                "match-phase": false,
    -                "non-ideal-state": false,
    -                "timeout": true,
    -                "anntimeout": false
    -            },
    -            "documents": 167006201,
    -            "full": false,
    -            "nodes": 11,
    -            "results": 1,
    -            "resultsFull": 0
    -        },
    -        "fields": {
    -            "totalCount": 16469732
    -        }
    -    }
    -}
    -{% endhighlight %}
    -

    -The result was delivered in 200 ms but the query was degraded as coverage is less than 100. -In this case, 167,006,201 out of x documents where queried, and 16,469,732 documents where matched and ranked, -using the first-phase ranking expression in the default rank profile. -

    -The degraded field contains the following fields which explains why the result had coverage less than 100: -

    -
      -
    • adaptive-timeout is true if adaptive node timeout - has been enabled, and one or more nodes fail to produce a result at all within the timeout. - This could be caused by nodes with degraded hardware making them slower than peers in the cluster.
    • -
    • match-phase is true if the rank profile has defined - match phase ranking degradation. - Match-phase can be used to control which documents are ranked within the timeout. -
    • -
    • non-ideal-state is true in cases where the system is not in - ideal state. This case is extremely rare.
    • -
    • timeout is true if the soft timeout was enabled, and not all documents could be matched and ranked - within the query timeout.
    • -
    • anntimeout is true if the ANN timeout was enabled and an ANN search terminated early - due to reaching the timeout, cf. Early termination of approximate nearest neighbor search
    • -
    -

    -Note that the degraded reasons are not mutually exclusive. -In the example, the soft timeout was triggered -and only 99% of the documents where queried before the time budget ran out. - -One could imagine scenarios where 10 out of 11 nodes involved in the query execution were healthy -and triggered soft timeout and delivered a result, -while the last node was in a bad state (e.g. hw issues) and could not produce a result at all, -and that would cause both timeout and adaptive-timeout to be true. -

    -When working on Results in a Searcher, -get the coverage information programmatically: -

    -
    {% highlight java %}
    -    @Override
    -    public Result search(Query query, Execution execution) {
    -        Result result = execution.search(query);
    -        Coverage  coverage = result.getCoverage(false);
    -        if (coverage != null && coverage.isDegraded()) {
    -            logger.warning("Got a degraded result for query " + query + " : " +
    -                            coverage.getResultPercentage() + "% was searched");
    -        }
    -        return result;
    -    }
    -{% endhighlight %}
    - - - -

    Adaptive node timeout

    -

    - For a content cluster with flat - data distribution, query performance is no better than the slowest node. The worst case scenario happens - when a node in the cluster is experiencing underlying HW issues. In such a state, - a node might answer health checks and pings, but still not be able to serve queries within the timeout. -

    -

    - Using adaptive coverage allows ignoring slow node(s). - The following example demonstrates how to use adaptive timeout. - The example uses a flat content cluster with 10 nodes:

    -
    {% highlight xml %}
    -
    -    
    -        
    -            0.9
    -            0.2
    -            0.3
    -        
    -    
    -
    -{% endhighlight %}
    -
      -
    • - Assuming using the default vespa timeout of 500ms, the stateless container dispatches the query - to all 10 nodes in parallel and waits until 9 out of 10 have replied (minimum coverage 0.9). -
    • -
    • - Assuming 9 could respond in 100ms, there is 400ms left. The dispatcher then waits minimum 80 ms (0.2*400ms) for the last - node to respond, and at maximum 120 (0.3*400ms) before giving up waiting for the slowest node and return the result. -
    • -
    • - The min wait setting is used to allow some per node response time variance. - Using min wait 0 will cause the query to return immediately when min coverage has been reached - (9 out of 10 nodes replied). - A higher than 0 value for min allows a node to be slightly slower than the - peers and overall still reach 100% coverage. -
    • -
    - - - - -

    - Approximate nearest neighbor search - in Vespa using HNSW - happens before the actual matching process. - This means that a soft timeout - during an ANN search leaves no time for the actual matching process - and results in no hits being returned at all. - However, the way HNSW works makes it possible to terminate the search early: - It maintains a queue of the best results found so far, - which it returns once it stops finding better results. - Instead of waiting for the algorithm to terminate, - the search can also be terminated early by returning the queue - in its current state. -

    -

    - Early termination of HNSW in Vespa can be triggered by - an explicitly specified time budget or by the ANN timeout. - These two concepts work independently, and the former is intended to be used for tweaking purposes, - while the latter is intended as a rescue mechanism in case a query is in danger of timing out. -

      -
    • - An ANN time budget can be configured with the - ranking.matching.anntimebudget - parameter. - It is intended to be used for performance tweaking: For example, instead of fine-tuning - ANN parameters like - targetHits - or - ranking.matching.filterFirstExploration - to obtain the optimal balance of latency and recall, - you increase these parameters and then specify an ANN time budget - to limit the time of ANN searches to the time budget you have in mind. - This way, you ideally would get the best recall possible within your specified time budget. -
    • -
    • - The ANN timeout can be configured with the - ranking.matching.anntimeout.enable - and - ranking.matching.anntimeout.factor - parameters. - Enabling the ANN timeout makes ANN searches terminate early if they - reach a configured point in time before the soft timeout. - This avoids the situation described above, where a soft timeout during an ANN search - results in no hits at all being returned: - it makes it possible to still have enough time left for the actual matching process, - possibly even without hitting the soft timeout there. - To this end, the factor might need tweaking for the specific use case: - A large factor is well suited for - approximate nearest neighbor search with filtering, - where only the hits found by ANN have to be considered in matching, which usually - only takes very little time. - For - hybrid search, - a lower value might be required to leave enough time - for the text search. -
    • -
    - Note that only early termination by hitting the ANN timeout is considered a failure and, thus, - reported via the - anntimeout - element in the query response. -

    - -

    Match phase degradation

    -

    - Refer to the match-phase reference. - Concrete examples of using match phase is found in the - - practical performance guide. -

    -

    -Match-phase works by specifying an attribute that measures document -quality in some way (popularity, click-through rate, pagerank, ad bid value, price, text quality). -In addition, a total.max-hits value is specified -that specifies how many hits in total over the content nodes are "more than enough" for the application. -Then an estimate is made after collecting a reasonable amount of hits for the query, -and if the estimate is higher than the node's share of the total-max-hits value, -an extra limitation is added to the query, -ensuring that only the highest quality documents can become hits. -

    -In effect, this limits the documents actually queried to the highest quality documents, -a subset of the full corpus, -where the size of subset is calculated in such a way -that the query is estimated to give the node's share of total-max-hits hits. -Since some (low-quality) hits will already have been collected to do the estimation, -the actual number of hits returned will usually be higher than total-max-hits. -But since the distribution of documents isn't perfectly smooth, -you risk sometimes getting less than the configured total-max-hits hits back. -

    -Note that limiting hits in the match-phase also affects aggregation/grouping, -and total-hit-count since it actually limits, so the query gets fewer hits. -Also note that it doesn't really make sense to use this -feature together with a WAND operator that also limit hits, -since they both operate in the same manner, - -The graph shows possible hits versus actual hits in a corpus with 100 000 documents, -where total-max-hits is configured to 10 000 per node. -The corpus is a synthetic (slightly randomized) data set, -in practice the graph will be less smooth: -

    -Plot of possible vs. actual hits -

    -There is a content node metric per rank-profile named -content.proton.documentdb.matching.rank_profile.limited_queries -which can be used to see how many of the queries are actually affected by these settings; -compare with the corresponding content.proton.documentdb.matching.rank_profile.queries metric -to measure the percentage. -

    - - -

    Match Phase Tradeoffs

    -

    -There are some important things to consider before using match-phase. -In a normal query scenario, latency is directly proportional to the number of hits the query matches: -a query that matches few documents will have low latency -and a query that matches many documents will have high latency. -Match-phase has the opposite effect. -This means that if you have queries that match few documents, -match-phase might make these queries significantly slower. -It might actually be faster to run the query without the filter. -

    -Example: Lets say you have a corpus with a document attribute named created_time. -For all queries you want the newest content surfaced, so you enable match-phase on created_time. -So far, so good - you get a great latency and always get your top-k hits. -The problem might come if you introduce a filter. -If you have a filter saying you only want documents from the last day, -then match-phase can become suboptimal and in some cases much worse than running without match-phase. -

    -

    -By design, Vespa will evaluate potential matches for a query by the order of their internal documentid. -This means it will start evaluating documents in the order they were indexed on the node, -and for most use-cases that means the oldest documents first. -Without a filter, every document is a potential match, -and match-phase will quickly figure out how it can optimize. -With the filter, on the other hand, the algorithm need to evaluate almost the full corpus -before it reaches potential matches (1 day old corpus), -and because of the way the algorithm is implemented, -end up with doing a lot of unnecessary work and can have orders of magnitude higher latencies -than running the query without the filter. -

    -Another important thing to mention is that the reported total-hits will be different -when doing queries with match-phase enabled. -This is because match-phase works on an estimated "virtual" corpus, -which might have much fewer hits than is actually in the full corpus. -

    -If used correctly match-phase can be a life-saver, however, -it is not a straight forward fix-it-all silver bullet. -Please test and measure your use of match-phase, -and contact the Vespa team if your results are not what you expect. -

    diff --git a/en/performance/graceful-degradation.mdx b/en/performance/graceful-degradation.mdx new file mode 100644 index 0000000000..71a3fa732a --- /dev/null +++ b/en/performance/graceful-degradation.mdx @@ -0,0 +1,122 @@ +--- +title: "Graceful Query Coverage Degradation" +sidebarTitle: "Graceful degradation" +--- + +Ideally you want to query all data indexed in a Vespa cluster within the specified timeout, but that might not always be possible: + +- The system might be overloaded due to capacity constraints, and queries do not complete within the timeout, as they are sitting in a queue waiting for a resource. +- A complex query might take longer time to execute than the specified timeout, or the timeout is too low given the complexity of the query and available resource capacity. + +This document describes how Vespa could gracefully degrade the result set if the query cannot be completed within the timeout specified. + +Definitions: + +- **Coverage**: The percentage of documents indexed which were evaluated by the query. The ideal coverage is 100%. +- **Timeout**: The total time a query is allowed to run for, see [timeout](/en/reference/api/query#timeout) (default 500 ms). Vespa is a distributed system where multiple components are involved in the query execution. +- **Soft Timeout**: Soft timeout allows coverage to be less than 100%, but larger than 0% if the query is approaching timeout. Soft timeout might also be considered as an *early termination* technique, and is enabled by default. Refer to [ranking.softtimeout.enable](/en/reference/api/query#ranking.softtimeout.enable). + +## Detection + +The default JSON renderer template will always render a *coverage* element below the root element, which has a *degraded* element if the query execution was degraded in some way and the *coverage* field will be less than 100. Example request with a query timeout of 200 ms and *ranking.softtimeout.enable=true*: + +```bash +/search/?searchChain=vespa&yql=select * from sources * where foo contains bar&presentation.format=json&timeout=200ms&ranking.softtimeout.enable=true +``` + +```json +{ + "root": { + "coverage": { + "coverage": 99, + "degraded": { + "adaptive-timeout": false, + "match-phase": false, + "non-ideal-state": false, + "timeout": true + }, + "documents": 167006201, + "full": false, + "nodes": 11, + "results": 1, + "resultsFull": 0 + }, + "fields": { + "totalCount": 16469732 + } + } +} +``` + +The result was delivered in 200 ms but the query was degraded as coverage is less than 100. In this case, 167,006,201 out of x documents where queried, and 16,469,732 documents where matched and ranked, using the first-phase ranking expression in the default rank profile. + +The *degraded* field contains the following fields which explains why the result had coverage less than 100: + +- *adaptive-timeout* is true if [adaptive node timeout](#adaptive-node-timeout) has been enabled, and one or more nodes fail to produce a result at all within the timeout. This could be caused by nodes with degraded hardware making them slower than peers in the cluster. +- *match-phase* is true if the rank profile has defined [match phase ranking degradation](/en/reference/schemas/schemas#match-phase). Match-phase can be used to control which documents are ranked within the timeout. +- *non-ideal-state* is true in cases where the system is not in [ideal state](/en/content/idealstate). This case is extremely rare. +- *timeout* is true if softtimeout was enabled, and not all documents could be matched and ranked within the query timeout. + +Note that the degraded reasons are not mutually exclusive. In the example, the softtimeout was triggered and only 99% of the documents where queried before the time budget ran out. One could imagine scenarios where 10 out of 11 nodes involved in the query execution were healthy and triggered soft timeout and delivered a result, while the last node was in a bad state (e.g. hw issues) and could not produce a result at all, and that would cause both *timeout* and *adaptive-timeout* to be true. + +When working on Results in a [Searcher](/en/applications/searchers), get the coverage information programmatically: + +```java +@Override + public Result search(Query query, Execution execution) { + Result result = execution.search(query); + Coverage coverage = result.getCoverage(false); + if (coverage != null && coverage.isDegraded()) { + logger.warning("Got a degraded result for query " + query + " : " + + coverage.getResultPercentage() + "% was searched"); + } + return result; + } +``` + +## Adaptive node timeout + +For a content cluster with [flat](/en/performance/sizing-search#data-distribution) data distribution, query performance is no better than the slowest node. The worst case scenario happens when a node in the cluster is experiencing underlying HW issues. In such a state, a node might answer health checks and pings, but still not be able to serve queries within the timeout. + +Using [adaptive coverage](/en/reference/applications/services/content#coverage) allows ignoring slow node(s). The following example demonstrates how to use adaptive timeout. The example uses a flat content cluster with 10 nodes: + +```xml + + + + 0.9 + 0.2 + 0.3 + + + +``` +- Assuming using the default vespa timeout of 500ms, the stateless container dispatches the query to all 10 nodes in parallel and waits until 9 out of 10 have replied (minimum coverage 0.9). +- Assuming 9 could respond in 100ms, there is 400ms left. The dispatcher then waits minimum 80 ms (0.2\*400ms) for the last node to respond, and at maximum 120 (0.3\*400ms) before giving up waiting for the slowest node and return the result. +- The min wait setting is used to allow some per node response time variance. Using min wait 0 will cause the query to return immediately when min coverage has been reached (9 out of 10 nodes replied). A higher than 0 value for min allows a node to be slightly slower than the peers and overall still reach 100% coverage. + +## Match phase degradation + +Refer to the [match-phase reference](/en/reference/schemas/schemas#match-phase). Concrete examples of using match phase is found in the [practical performance guide](/en/performance/practical-search-performance-guide#match-phase-limit---early-termination). + +Match-phase works by specifying an `attribute` that measures document quality in some way (popularity, click-through rate, pagerank, ad bid value, price, text quality). In addition, a `total.max-hits` value is specified that specifies how many hits in total over the content nodes are "more than enough" for the application. Then an estimate is made after collecting a reasonable amount of hits for the query, and if the estimate is higher than the node's share of the `total-max-hits` value, an extra limitation is added to the query, ensuring that only the highest quality documents can become hits. + +In effect, this limits the documents actually queried to the highest quality documents, a subset of the full corpus, where the size of subset is calculated in such a way that the query is estimated to give the node's share of `total-max-hits` hits. Since some (low-quality) hits will already have been collected to do the estimation, the actual number of hits returned will usually be higher than total-max-hits. But since the distribution of documents isn't perfectly smooth, you risk sometimes getting less than the configured `total-max-hits` hits back. + +Note that limiting hits in the match-phase also affects [aggregation/grouping](/en/querying/grouping), and total-hit-count since it actually limits, so the query gets fewer hits. Also note that it doesn't really make sense to use this feature together with a [WAND operator](/en/ranking/wand) that also limit hits, since they both operate in the same manner, and you would get interference between them that could cause unpredictable results. The graph shows possible hits versus actual hits in a corpus with 100 000 documents, where `total-max-hits` is configured to 10 000 per node. The corpus is a synthetic (slightly randomized) data set, in practice the graph will be less smooth: + +![Plot of possible vs. actual hits](/assets/img/relevance/match-phase-max-hits.png) + +There is a content node metric per rank-profile named *content.proton.documentdb.matching.rank\_profile.limited\_queries* which can be used to see how many of the queries are actually affected by these settings; compare with the corresponding *content.proton.documentdb.matching.rank\_profile.queries* metric to measure the percentage. + +### Match Phase Tradeoffs + +There are some important things to consider before using *match-phase*. In a normal query scenario, latency is directly proportional to the number of hits the query matches: a query that matches few documents will have low latency and a query that matches many documents will have high latency. Match-phase has the **opposite** effect. This means that if you have queries that match few documents, match-phase might make these queries significantly slower. It might actually be faster to run the query without the filter. + +Example: Lets say you have a corpus with a document attribute named *created\_time*. For all queries you want the newest content surfaced, so you enable match-phase on *created\_time*. So far, so good - you get a great latency and always get your top-k hits. The problem might come if you introduce a filter. If you have a filter saying you only want documents from the last day, then match-phase can become suboptimal and in some cases much worse than running without match-phase. + +By design, Vespa will evaluate potential matches for a query by the order of their internal documentid. This means it will start evaluating documents in the order they were indexed on the node, and for most use-cases that means the oldest documents first. Without a filter, every document is a potential match, and match-phase will quickly figure out how it can optimize. With the filter, on the other hand, the algorithm need to evaluate almost the full corpus before it reaches potential matches (1 day old corpus), and because of the way the algorithm is implemented, end up with doing a lot of unnecessary work and can have orders of magnitude higher latencies than running the query without the filter. + +Another important thing to mention is that the reported total-hits will be different when doing queries with match-phase enabled. This is because match-phase works on an estimated "virtual" corpus, which might have much fewer hits than is actually in the full corpus. + +If used correctly match-phase can be a life-saver, however, it is not a straight forward fix-it-all silver bullet. Please test and measure your use of match-phase, and contact the Vespa team if your results are not what you expect. \ No newline at end of file diff --git a/en/performance/http2.html b/en/performance/http2.html deleted file mode 100644 index 057be601fb..0000000000 --- a/en/performance/http2.html +++ /dev/null @@ -1,133 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "HTTP/2" ---- - -

    - This document contains HTTP/2 performance considerations on the container—see - Container tuning for general tuning of container clusters. -

    - - - -

    Enabling HTTP/2 on container

    -

    - HTTP/2 is enabled by default on a container for all connectors. - We recommend HTTP/2 with TLS, both for added security, but also for a more robust connection upgrade mechanism. - Web browsers will typically only allow HTTP/2 over TLS. -

    - - -

    HTTP/2 with TLS

    -

    - Both HTTP/1.1 and HTTP/2 will be served over the same connector using the - TLS ALPN Extension. - The Application-Layer Protocol Negotiation (ALPN) extension allows the client to send a list of supported protocols - during TLS handshake. - The container selects a supported protocol from that list. -

    -

    - The HTTP/2 specification - dictates multiple requirements for the TLS connection. - Vespa may enforce some or all of these restrictions. - See the HTTP/2 specification for the full list. The most significant are listed below: -

    -
      -
    • Client must use at least TLSv1.2.
    • -
    • Client must provide target domain with the TLS Server Name Indication (SNI) Extension.
    • -
    • Client must not use any of the banned - TLSv1.2 ciphers.
    • -
    - - -

    HTTP/2 without TLS

    -

    The jdisc container supports both mechanism for HTTP/2 without TLS - see testing:

    -
      -
    1. Upgrading to HTTP/2 from HTTP/1
    2. -
    3. HTTP/2 with prior knowledge
    4. -
    - - - -

    Feeding over HTTP/2

    -

    - One of the major improvements with HTTP/2 is multiplexing of multiple concurrent requests over a single TCP connection. - This allows for high-throughput feeding through the - /document/v1/ HTTP API, - with a simple one-operation–one-request model, - but without the overhead of hundreds of parallel connections that HTTP/1.1 would require for sufficient concurrency. -

    -

    - vespa feed in the Vespa CLI - and vespa-feed-client use /document/v1/ over HTTP/2. -

    - - - -

    Performance tuning

    - - -

    Client

    -

    - The number of multiple concurrent requests per connection is typically adjustable in HTTP/2 clients/libraries. - Document v1 API is designed for high concurrency and can easily handle thousands of concurrent requests. - Its implementation is asynchronous and max concurrency is not restricted by a thread pool size, - so configure your client to allow enough concurrent requests/streams to saturate the feed container. - Other APIs such as the Query API is backed by a synchronous implementation, - and max concurrency is restricted by the - underlying thread pool size. - Too many concurrent streams may result in the container rejecting requests with 503 responses. -

    -

    - There are also still some reasons to use multiple TCP connections—even with HTTP/2: -

    -
      -
    • Utilize multiple containers. - A single container may not saturate the content layer. - A client may have to use more connections than container nodes if the containers are behind a load balancer.
    • -
    • Higher throughput. - Many clients allow only for a single thread to operate each connection. - Multiple connections may be required for utilizing several CPU cores. -
    • -
    - - - -

    Client recommendations

    -

    - Use vespa-feed-client for feeding through Document v1 API (JDK8+). -

    -

    - We recommend the h2load benchmarking tool for load testing. - vespa-fbench does not support HTTP/2 at the moment. -

    -

    - For Java there are 4 good alternatives: -

    -
      -
    1. Jetty Client -
    2. OkHttp
    3. -
    4. Apache HttpClient 5.x
    5. -
    6. java.net.http.HttpClient (JDK11+)
    7. -
    - - - -

    Testing

    -

    - The server does not perform a protocol upgrade if a request contains content (POST, PUT, PATCH with payload). - This might be a limitation in Jetty, the HTTP server used in Vespa. - Any client should assume HTTP/2 supported - example using curl --http2-prior-knowledge: -

    -
    -$ curl -i --http2-prior-knowledge \
    -  -X POST -H 'Content-Type: application/json' \
    -  --data-binary @ext/A-Head-Full-of-Dreams.json \
    -  http://127.0.0.1:8080/document/v1/mynamespace/music/docid/a-head-full-of-dreams
    -
    -HTTP/2 200
    -date: Tue, 06 Dec 2022 11:04:13 GMT
    -content-type: application/json;charset=utf-8
    -vary: Accept-Encoding
    -content-length: 122
    -
    diff --git a/en/performance/http2.mdx b/en/performance/http2.mdx new file mode 100644 index 0000000000..5eebbdee96 --- /dev/null +++ b/en/performance/http2.mdx @@ -0,0 +1,73 @@ +--- +title: "HTTP/2" +--- + +This document contains HTTP/2 performance considerations on the container—see [Container tuning](/en/performance/container-tuning) for general tuning of container clusters. + +## Enabling HTTP/2 on container + +HTTP/2 is enabled by default on a container for all connectors. We recommend HTTP/2 with TLS, both for added security, but also for a more robust connection upgrade mechanism. Web browsers will typically only allow HTTP/2 over TLS. + +### HTTP/2 with TLS + +Both HTTP/1.1 and HTTP/2 will be served over the same connector using the [TLS ALPN Extension](https://datatracker.ietf.org/doc/html/rfc7301). The Application-Layer Protocol Negotiation (ALPN) extension allows the client to send a list of supported protocols during TLS handshake. The container selects a supported protocol from that list. + +The [HTTP/2 specification](https://datatracker.ietf.org/doc/html/rfc7540) dictates multiple requirements for the TLS connection. Vespa may enforce some or all of these restrictions. See the HTTP/2 specification for the full list. The most significant are listed below: + +- Client must use at least TLSv1.2. +- Client must provide target domain with the TLS Server Name Indication (SNI) Extension. +- Client must not use any of the banned [TLSv1.2 ciphers](https://datatracker.ietf.org/doc/html/rfc7540#appendix-A). + +### HTTP/2 without TLS + +The jdisc container supports both mechanism for HTTP/2 without TLS - see [testing](#testing): + +1. Upgrading to HTTP/2 from HTTP/1 +2. HTTP/2 with prior knowledge + +## Feeding over HTTP/2 + +One of the major improvements with HTTP/2 is multiplexing of multiple concurrent requests over a single TCP connection. This allows for high-throughput feeding through the [/document/v1/](/en/reference/api/document-v1) HTTP API, with a simple one-operation–one-request model, but without the overhead of hundreds of parallel connections that HTTP/1.1 would require for sufficient concurrency. + +`vespa feed` in the [Vespa CLI](/en/clients/vespa-cli#documents) and [vespa-feed-client](/en/clients/vespa-feed-client) use /document/v1/ over HTTP/2. + +## Performance tuning + +### Client + +The number of multiple concurrent requests per connection is typically adjustable in HTTP/2 clients/libraries. Document v1 API is designed for high concurrency and can easily handle thousands of concurrent requests. Its implementation is asynchronous and max concurrency is not restricted by a thread pool size, so configure your client to allow enough concurrent requests/streams to saturate the feed container. Other APIs such as the [Query API](/en/querying/query-api) is backed by a synchronous implementation, and max concurrency is restricted by the [underlying thread pool size](/en/performance/container-tuning#container-worker-threads). Too many concurrent streams may result in the container rejecting requests with 503 responses. + +There are also still some reasons to use multiple TCP connections—even with HTTP/2: + +- **Utilize multiple containers**. A single container may not saturate the content layer. A client may have to use more connections than container nodes if the containers are behind a load balancer. +- **Higher throughput**. Many clients allow only for a single thread to operate each connection. Multiple connections may be required for utilizing several CPU cores. + +## Client recommendations + +Use [vespa-feed-client](/en/clients/vespa-feed-client) for feeding through Document v1 API (JDK8+). + +We recommend the [h2load benchmarking tool](https://nghttp2.org/documentation/h2load-howto.html) for load testing. [vespa-fbench](/en/reference/operations/tools#vespa-fbench) does not support HTTP/2 at the moment. + +For Java there are 4 good alternatives: + +1. [Jetty Client](https://javadoc.jetty.org/jetty-11/org/eclipse/jetty/client/HttpClient.html) +2. [OkHttp](https://square.github.io/okhttp/) +3. [Apache HttpClient 5.x](https://hc.apache.org/httpcomponents-client-5.1.x/) +4. [java.net.http.HttpClient (JDK11+)](https://docs.oracle.com/en/java/javase/11/docs/api/java.net.http/java/net/http/HttpClient.html) + +## Testing + +The server does not perform a protocol upgrade if a request contains content (POST, PUT, PATCH with payload). This might be a limitation in Jetty, the HTTP server used in Vespa. Any client should assume HTTP/2 supported - example using `curl --http2-prior-knowledge`: + +```bash highlight={6} +$ curl -i --http2-prior-knowledge \ + -X POST -H 'Content-Type: application/json' \ + --data-binary @ext/A-Head-Full-of-Dreams.json \ + http://127.0.0.1:8080/document/v1/mynamespace/music/docid/a-head-full-of-dreams + +HTTP/2 200 +date: Tue, 06 Dec 2022 11:04:13 GMT +content-type: application/json;charset=utf-8 +vary: Accept-Encoding +content-length: 122 +``` \ No newline at end of file diff --git a/en/performance/index.html b/en/performance/index.html deleted file mode 100644 index f4efc4fb3d..0000000000 --- a/en/performance/index.html +++ /dev/null @@ -1,52 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Performance" ---- - -

    Practical search performance guide

    -

    -See practical search performance guide. -The guide walks through a music search use case and gives a practical introduction to Vespa search performance. -

    - -

    Sizing and capacity planning

    -

    -Sizing and capacity planning involves figuring out how many nodes are needed -and what kind of hardware flavor best fits the use case: -

    - - - -

    Benchmarking and tuning

    -

    -Benchmarking is important both during sizing and for testing new features. -What tools to use for benchmarking and how to tune system aspects of Vespa: -

    - - - - -

    Profiling

    -

    -Do a deep performance analysis - how to profile the application as well as Vespa: -

    - diff --git a/en/performance/index.mdx b/en/performance/index.mdx new file mode 100644 index 0000000000..e69de29bb2 diff --git a/en/performance/instance-types/aws-instance-types.html b/en/performance/instance-types/aws-instance-types.html deleted file mode 100644 index 8df737c620..0000000000 --- a/en/performance/instance-types/aws-instance-types.html +++ /dev/null @@ -1,51 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "AWS Instance Type Reference" ---- - - - -

    - All instance types without Local SSD use Amazon Elastic Block Store for storage.
    - These volumes can be any size from a minimum of 3 x memory, up to 16TB.

    -

    - - - - - - - - - - - {% for row in site.data.aws-instance-types %} - - - - - - - - - {% endfor %} -
    ArchitectureCPU coresMemory (GB)Local SSD (GB)GPU Memory (GB)
    {{ row["arch"] }}{{ row["cpu"] }}{{ row["mem"] }}{{ row["localdisk"] }}{{ row["gpumem"] }}
    diff --git a/en/performance/instance-types/aws-instance-types.mdx b/en/performance/instance-types/aws-instance-types.mdx new file mode 100644 index 0000000000..a26d7aceff --- /dev/null +++ b/en/performance/instance-types/aws-instance-types.mdx @@ -0,0 +1,1335 @@ +--- +title: "AWS Instance Type Reference" +sidebarTitle: "AWS instance types" +--- + +All instance types without Local SSD use [Amazon Elastic Block Store](https://aws.amazon.com/ebs/) for storage. + +These volumes can be any size from a minimum of 3 x memory, up to 16TB. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    ArchitectureCPU coresMemory (GB)Local SSD (GB)GPU Memory (GB)
    arm641.08--
    arm641.0859-
    arm641.016--
    arm641.01659-
    arm642.08--
    arm642.08118-
    arm642.016--
    arm642.016118-
    arm642.016468-
    arm642.032--
    arm642.032118-
    arm644.08--
    arm644.08237-
    arm644.016--
    arm644.016237-
    arm644.032--
    arm644.032237-
    arm644.032937-
    arm644.064--
    arm644.064237-
    arm648.016--
    arm648.016474-
    arm648.032--
    arm648.032474-
    arm648.064--
    arm648.064474-
    arm648.0641875-
    arm648.0128--
    arm648.0128475-
    arm6416.032--
    arm6416.032950-
    arm6416.064--
    arm6416.064950-
    arm6416.0128--
    arm6416.0128950-
    arm6416.01283750-
    arm6416.0256--
    arm6416.0256950-
    arm6432.064--
    arm6432.0641900-
    arm6432.0128--
    arm6432.01281900-
    arm6432.0256--
    arm6432.02561900-
    arm6432.02567500-
    arm6432.0512--
    arm6432.05121900-
    arm6448.096--
    arm6448.0962850-
    arm6448.0192--
    arm6448.01922850-
    arm6448.0384--
    arm6448.03842850-
    arm6448.038411250-
    arm6448.0768--
    arm6448.07682850-
    arm6464.0128--
    arm6464.01283800-
    arm6464.0256--
    arm6464.02563800-
    arm6464.0512--
    arm6464.05123800-
    arm6464.051215000-
    arm6464.01024--
    arm6464.010243800-
    arm6496.0192--
    arm6496.01925700-
    arm6496.0384--
    arm6496.03845700-
    arm6496.0768--
    arm6496.07685700-
    arm6496.076822500-
    arm6496.01536--
    arm64192.0384--
    arm64192.038411400-
    arm64192.0768--
    arm64192.076811400-
    arm64192.01536--
    arm64192.0153611400-
    arm64192.0153645000-
    arm64192.03072--
    x86_642.08--
    x86_642.0875-
    x86_642.08118-
    x86_642.016--
    x86_642.01675-
    x86_642.016468-
    x86_642.0161250-
    x86_644.08--
    x86_644.08100-
    x86_644.08237-
    x86_644.016--
    x86_644.01612516.0
    x86_644.016150-
    x86_644.016237-
    x86_644.032--
    x86_644.032150-
    x86_644.032237-
    x86_644.032937-
    x86_644.0322500-
    x86_648.016--
    x86_648.016200-
    x86_648.016474-
    x86_648.032--
    x86_648.03222516.0
    x86_648.032300-
    x86_648.032474-
    x86_648.064--
    x86_648.064300-
    x86_648.0641875-
    x86_648.0645000-
    x86_6412.0967500-
    x86_6416.032--
    x86_6416.032400-
    x86_6416.032950-
    x86_6416.064--
    x86_6416.064600-
    x86_6416.064950-
    x86_6416.0128--
    x86_6416.0128600-
    x86_6416.01283750-
    x86_6424.0192900-
    x86_6424.019215000-
    x86_6432.064--
    x86_6432.0641200-
    x86_6432.0641900-
    x86_6432.0128--
    x86_6432.01281200-
    x86_6432.01281900-
    x86_6432.0256--
    x86_6432.02561200-
    x86_6432.02567500-
    x86_6436.072--
    x86_6436.072900-
    x86_6448.096--
    x86_6448.0961800-
    x86_6448.0962850-
    x86_6448.0192--
    x86_6448.01921800-
    x86_6448.01922850-
    x86_6448.0384--
    x86_6448.03841800-
    x86_6448.038430000-
    x86_6464.0128--
    x86_6464.01283800-
    x86_6464.0256--
    x86_6464.02562400-
    x86_6464.02563800-
    x86_6464.0512--
    x86_6464.05122400-
    x86_6464.051215000-
    x86_6472.0144--
    x86_6472.01441800-
    x86_6472.057645000-
    x86_6496.0192--
    x86_6496.01923600-
    x86_6496.01925700-
    x86_6496.0384--
    x86_6496.03843600-
    x86_6496.0768--
    x86_6496.07683600-
    x86_6496.076860000-
    x86_64128.0102430000-
    x86_64192.01536120000-
    \ No newline at end of file diff --git a/en/performance/instance-types/azure-instance-types.html b/en/performance/instance-types/azure-instance-types.html deleted file mode 100644 index 9397e9220e..0000000000 --- a/en/performance/instance-types/azure-instance-types.html +++ /dev/null @@ -1,48 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Azure Instance Type Reference" ---- - - - -

    - All instance types without Local SSD use Azure Managed Disk for storage.

    -

    - - - - - - - - - - - {% for row in site.data.azure-instance-types %} - - - - - - - - - {% endfor %} -
    ArchitectureCPU coresMemory (GB)Local SSD (GB)GPU Memory (GB)
    {{ row["arch"] }}{{ row["cpu"] }}{{ row["mem"] }}{{ row["localdisk"] }}{{ row["gpumem"] }}
    diff --git a/en/performance/instance-types/azure-instance-types.mdx b/en/performance/instance-types/azure-instance-types.mdx new file mode 100644 index 0000000000..f94a1f8221 --- /dev/null +++ b/en/performance/instance-types/azure-instance-types.mdx @@ -0,0 +1,542 @@ +--- +title: "Azure Instance Type Reference" +sidebarTitle: "Azure instance types" +--- + +All instance types without Local SSD use [Azure Managed Disk](https://learn.microsoft.com/en-us/azure/virtual-machines/managed-disks-overview) for storage. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    ArchitectureCPU coresMemory (GB)Local SSD (GB)GPU Memory (GB)
    x86_642.08--
    x86_642.0875-
    x86_642.08118-
    x86_642.016--
    x86_642.01675-
    x86_642.016118-
    x86_644.08--
    x86_644.08150-
    x86_644.016--
    x86_644.016150-
    x86_644.016236-
    x86_644.032--
    x86_644.032150-
    x86_644.032236-
    x86_648.016--
    x86_648.016300-
    x86_648.032--
    x86_648.032300-
    x86_648.032472-
    x86_648.064--
    x86_648.064300-
    x86_648.064472-
    x86_6416.032--
    x86_6416.032600-
    x86_6416.064--
    x86_6416.064600-
    x86_6416.064944-
    x86_6416.0128--
    x86_6416.0128600-
    x86_6416.0128944-
    x86_6420.0160--
    x86_6420.0160750-
    x86_6420.01601181-
    x86_6432.064--
    x86_6432.0641200-
    x86_6432.0128--
    x86_6432.01281200-
    x86_6432.01281889-
    x86_6432.0256--
    x86_6432.02561200-
    x86_6432.02561889-
    x86_6448.096--
    x86_6448.0961800-
    x86_6448.0192--
    x86_6448.01921800-
    x86_6448.01922834-
    x86_6448.0384--
    x86_6448.03841800-
    x86_6448.03842834-
    x86_6464.0128--
    x86_6464.01282400-
    x86_6464.0256--
    x86_6464.02562400-
    x86_6464.02563779-
    x86_6464.0512--
    x86_6464.05122400-
    x86_6464.05123779-
    x86_6496.0192--
    x86_6496.01923600-
    x86_6496.0384--
    x86_6496.03843600-
    x86_6496.03845669-
    x86_6496.0672--
    x86_6496.06723600-
    x86_6496.06725669-
    \ No newline at end of file diff --git a/en/performance/instance-types/gcp-instance-types.html b/en/performance/instance-types/gcp-instance-types.html deleted file mode 100644 index 1061ff0da1..0000000000 --- a/en/performance/instance-types/gcp-instance-types.html +++ /dev/null @@ -1,49 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "GCP Instance Type Reference" ---- - - - -

    - All instance types without Local SSD use GCP Persistent Disk for storage.
    - These volumes can be any size from a minimum of 3 x memory, up to 64TB.

    -

    - - - - - - - - - - - {% for row in site.data.gcp-instance-types %} - - - - - - - - - {% endfor %} -
    ArchitectureCPU coresMemory (GB)Local SSD (GB)GPU Memory (GB)
    {{ row["arch"] }}{{ row["cpu"] }}{{ row["mem"] }}{{ row["localdisk"] }}{{ row["gpumem"] }}
    diff --git a/en/performance/instance-types/gcp-instance-types.mdx b/en/performance/instance-types/gcp-instance-types.mdx new file mode 100644 index 0000000000..63af633b86 --- /dev/null +++ b/en/performance/instance-types/gcp-instance-types.mdx @@ -0,0 +1,1688 @@ +--- +title: "GCP Instance Type Reference" +sidebarTitle: "GCP instance types" +--- + +All instance types without Local SSD use [GCP Persistent Disk](https://cloud.google.com/persistent-disk) for storage. + +These volumes can be any size from a minimum of 3 x memory, up to 64TB. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    ArchitectureCPU coresMemory (GB)Local SSD (GB)GPU Memory (GB)
    arm642.08--
    arm642.016--
    arm644.08--
    arm644.016--
    arm644.016375-
    arm644.032--
    arm644.032375-
    arm648.016--
    arm648.032--
    arm648.032750-
    arm648.064--
    arm648.064750-
    arm6416.032--
    arm6416.064--
    arm6416.0641500-
    arm6416.0128--
    arm6416.01281500-
    arm6432.064--
    arm6432.0128--
    arm6432.01282250-
    arm6432.0256--
    arm6432.02562250-
    arm6448.096--
    arm6448.0192--
    arm6448.01923750-
    arm6448.0384--
    arm6448.03843750-
    arm6464.0128--
    arm6464.0256--
    arm6464.02565250-
    arm6464.0512--
    arm6464.05125250-
    arm6472.0144--
    arm6472.0288--
    arm6472.02886000-
    arm6472.0576--
    arm6472.05766000-
    x86_642.08--
    x86_642.08375-
    x86_642.08750-
    x86_642.081500-
    x86_642.083000-
    x86_642.086000-
    x86_642.089000-
    x86_642.016--
    x86_642.016375-
    x86_642.016750-
    x86_642.0161500-
    x86_642.0163000-
    x86_642.0166000-
    x86_642.0169000-
    x86_644.08--
    x86_644.016--
    x86_644.016375-
    x86_644.016750-
    x86_644.0161500-
    x86_644.0163000-
    x86_644.0166000-
    x86_644.0169000-
    x86_644.032--
    x86_644.032375-
    x86_644.032750-
    x86_644.0321500-
    x86_644.0323000-
    x86_644.0326000-
    x86_644.0329000-
    x86_648.08--
    x86_648.08375-
    x86_648.08750-
    x86_648.081500-
    x86_648.083000-
    x86_648.086000-
    x86_648.089000-
    x86_648.016--
    x86_648.032--
    x86_648.032375-
    x86_648.032750-
    x86_648.0321500-
    x86_648.0323000-
    x86_648.0326000-
    x86_648.0329000-
    x86_648.064--
    x86_648.064375-
    x86_648.064750-
    x86_648.0641500-
    x86_648.0643000-
    x86_648.0646000-
    x86_648.0649000-
    x86_6416.016--
    x86_6416.016375-
    x86_6416.016750-
    x86_6416.0161500-
    x86_6416.0163000-
    x86_6416.0166000-
    x86_6416.0169000-
    x86_6416.032--
    x86_6416.064--
    x86_6416.064375-
    x86_6416.064750-
    x86_6416.0641500-
    x86_6416.0643000-
    x86_6416.0646000-
    x86_6416.0649000-
    x86_6416.0128--
    x86_6416.0128375-
    x86_6416.0128750-
    x86_6416.01281500-
    x86_6416.01283000-
    x86_6416.01286000-
    x86_6416.01289000-
    x86_6432.032--
    x86_6432.032750-
    x86_6432.0321500-
    x86_6432.0323000-
    x86_6432.0326000-
    x86_6432.0329000-
    x86_6432.064--
    x86_6432.0128--
    x86_6432.0128750-
    x86_6432.01281500-
    x86_6432.01283000-
    x86_6432.01286000-
    x86_6432.01289000-
    x86_6432.0256--
    x86_6432.0256750-
    x86_6432.02561500-
    x86_6432.02563000-
    x86_6432.02566000-
    x86_6432.02569000-
    x86_6448.048--
    x86_6448.048750-
    x86_6448.0481500-
    x86_6448.0483000-
    x86_6448.0486000-
    x86_6448.0489000-
    x86_6448.096--
    x86_6448.0192--
    x86_6448.0192750-
    x86_6448.01921500-
    x86_6448.01923000-
    x86_6448.01926000-
    x86_6448.01929000-
    x86_6448.0384--
    x86_6448.0384750-
    x86_6448.03841500-
    x86_6448.03843000-
    x86_6448.03846000-
    x86_6448.03849000-
    x86_6464.064--
    x86_6464.0641500-
    x86_6464.0643000-
    x86_6464.0646000-
    x86_6464.0649000-
    x86_6464.0128--
    x86_6464.0256--
    x86_6464.02561500-
    x86_6464.02563000-
    x86_6464.02566000-
    x86_6464.02569000-
    x86_6464.0512--
    x86_6464.05121500-
    x86_6464.05123000-
    x86_6464.05126000-
    x86_6464.05129000-
    x86_6480.080--
    x86_6480.0801500-
    x86_6480.0803000-
    x86_6480.0806000-
    x86_6480.0809000-
    x86_6480.0160--
    x86_6480.0320--
    x86_6480.03201500-
    x86_6480.03203000-
    x86_6480.03206000-
    x86_6480.03209000-
    x86_6480.0640--
    x86_6480.06401500-
    x86_6480.06403000-
    x86_6480.06406000-
    x86_6480.06409000-
    x86_6496.096--
    x86_6496.0963000-
    x86_6496.0966000-
    x86_6496.0969000-
    x86_6496.0384--
    x86_6496.03843000-
    x86_6496.03846000-
    x86_6496.03849000-
    x86_6496.0768--
    x86_6496.07683000-
    x86_6496.07686000-
    x86_6496.07689000-
    x86_64128.0128--
    x86_64128.01283000-
    x86_64128.01286000-
    x86_64128.01289000-
    x86_64128.0512--
    x86_64128.05123000-
    x86_64128.05126000-
    x86_64128.05129000-
    x86_64224.0224--
    x86_64224.02243000-
    x86_64224.02246000-
    x86_64224.02249000-
    x86_64224.0896--
    x86_64224.08963000-
    x86_64224.08966000-
    x86_64224.08969000-
    \ No newline at end of file diff --git a/en/performance/memory-visualizer.md b/en/performance/memory-visualizer.md deleted file mode 100644 index cef24f35f2..0000000000 --- a/en/performance/memory-visualizer.md +++ /dev/null @@ -1,84 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: Memory Visualizer -applies_to: cloud -redirect_from: - - /en/cloud/memory-visualizer ---- - -The [schema](../basics/schemas.html) defines fields, types of fields and settings per field, e.g. - -``` -schema product { - - document product { - - field productId type long { - indexing: summary | attribute - attribute: fast-search - rank: filter - } - - field description type string { - indexing: summary | index - } - - ... - } -} -``` - -The field types are often given by the application's data, but the _usage_ of the fields is also important - examples: - -{:.list} -* high-speed updates to documents can be achieved by using attributes for memory-writes only, - even though the field could be a summary or indexed field, use case permitting -* string fields can be faster than numeric, if the access is equality (not range like "price < 100") - -In short, there are functional, performance and cost tradeoffs. -There are guides to help estimate resource use, see [attributes](../content/attributes.html), -but often one does not know factors like number of unique values in the data. -It might as well be easier to feed the data to Vespa Cloud and do schema changes online and observe the effect. -Vespa Cloud has two features that accelerates this process - the Memory Visualizer and Automated Reindexing: - -![Memory Visualizer](/assets/img/memory-visualizer-1.png) - -The Memory Visualizer lets you browse the attribute fields and observe absolute and relative size. -This can help find the cost drivers for memory-bound applications, and identify bottlenecks for optimizations. - -The Memory Visualizer is found in the "services" view in the console for an application. -Click a node of type "documentation (type: search)" and use the _Memory visualizer_ link. - - - -## Adding or changing fields -Use the Memory Visualizer to track memory when adding a field. -Attribute, index and summary fields have different behavior when it comes to empty fields and memory use, -depending on data type - here, the tool indicates headroom for more data to assist in the evaluation. - -Use the [field change procedure](../operations/procedure-change-attribute-index.html) -to plan the schema changes for data availability in the transition. -The Console will display reindexing progress: - -![Reindex progress](/assets/img/reindex-progress.png) - -This makes it easy to estimate when the reindexing is complete. -Note that attribute memory usage might require a node restart for all data structures to drain, -take note of this when using the Memory Visualizer again. - - - - - -## Using the visualizer -Some fields have a different color code. -To understand the types of fields, -read more about the [content node data structures](../content/proton.html) - in short: - -{:.list} -* `Ready` are indexed documents that might or might not be included in queries -* `Not Ready` are document replicas stored on the nodes that might be indexed later -* `Removed` are deleted documents, either by the application, - or the document replica has been moved to another node -* `Documentmetastore` is the document ID mapping - - see [attributes](../content/attributes.html#document-meta-store) diff --git a/en/performance/memory-visualizer.mdx b/en/performance/memory-visualizer.mdx new file mode 100644 index 0000000000..38f76755c7 --- /dev/null +++ b/en/performance/memory-visualizer.mdx @@ -0,0 +1,57 @@ +--- +title: "Memory Visualizer" +--- + +The [schema](/en/basics/schemas) defines fields, types of fields and settings per field, e.g. + +```js expandable +schema product { + + document product { + + field productId type long { + indexing: summary | attribute + attribute: fast-search + rank: filter + } + + field description type string { + indexing: summary | index + } + + ... + } +} +``` + +The field types are often given by the application's data, but the *usage* of the fields is also important - examples: + +- high-speed updates to documents can be achieved by using attributes for memory-writes only, even though the field could be a summary or indexed field, use case permitting +- string fields can be faster than numeric, if the access is equality (not range like "price < 100") + +In short, there are functional, performance and cost tradeoffs. There are guides to help estimate resource use, see [attributes](/en/content/attributes), but often one does not know factors like number of unique values in the data. It might as well be easier to feed the data to Vespa Cloud and do schema changes online and observe the effect. Vespa Cloud has two features that accelerates this process - the Memory Visualizer and Automated Reindexing: + +![Memory Visualizer](/assets/img/memory-visualizer-1.png) + +The Memory Visualizer lets you browse the attribute fields and observe absolute and relative size. This can help find the cost drivers for memory-bound applications, and identify bottlenecks for optimizations. + +The Memory Visualizer is found in the "services" view in the console for an application. Click a node of type "documentation (type: search)" and use the *Memory visualizer* link. + +## Adding or changing fields + +Use the Memory Visualizer to track memory when adding a field. Attribute, index and summary fields have different behavior when it comes to empty fields and memory use, depending on data type - here, the tool indicates headroom for more data to assist in the evaluation. + +Use the [field change procedure](/en/operations/self-managed/procedure-change-attribute-index) to plan the schema changes for data availability in the transition. The Console will display reindexing progress: + +![Reindex progress](/assets/img/reindex-progress.png) + +This makes it easy to estimate when the reindexing is complete. Note that attribute memory usage might require a node restart for all data structures to drain, take note of this when using the Memory Visualizer again. + +## Using the visualizer + +Some fields have a different color code. To understand the types of fields, read more about the [content node data structures](/en/content/proton) - in short: + +- `Ready` are indexed documents that might or might not be included in queries +- `Not Ready` are document replicas stored on the nodes that might be indexed later +- `Removed` are deleted documents, either by the application, or the document replica has been moved to another node +- `Documentmetastore` is the document ID mapping - see [attributes](/en/content/attributes#document-meta-store) \ No newline at end of file diff --git a/en/performance/node-resources.md b/en/performance/node-resources.md deleted file mode 100644 index fa6eba43b4..0000000000 --- a/en/performance/node-resources.md +++ /dev/null @@ -1,142 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: Node Resources -applies_to: cloud -redirect_from: - - /cloud/node-resources - - /en/cloud/node-resources.html ---- - -This guide goes through the following aspects of node resource configuration: - -1. Independent configuration of resource dimensions -2. Using automated resource suggestions -3. Deployment automation for rapid optimization cycles -4. Automated instance type migration for optimal performance over time - - -## Independent resource dimensions -In Vespa Cloud, a node's `resources` is configured like: - -```xml - - - -``` - -With this, you specify the dimensions independently. -E.g., one can double the CPU, keeping all other dimensions constant. - -This is important when tuning for the optimal price/performance point, -as the pieces of an application has different sweet spots. -For example, the product search cluster of an application can be more CPU bound than product recommendations; -the latter might need relatively more memory. - -{% include note.html content='the above is a simplified example, -there are more resource dimensions like GPU use, or CPU architecture, available.' %} - -Optimizing for cost/performance is therefore easy. -Simplified, applications can be CPU, disk, or memory bound. -A general rule of thumb is to be bound by the most expensive component, often CPU. -Refer to the node resource [reference](../reference/applications/services/services.html#resources) for all dimensions. - - -## Resource suggestions -Applications change over time: - -* Data size growth -* Query rate growth -* Write rate growth -* Schema changes, like new fields or binarized embeddings - -Finding the optimal node configuration is an iterative process. -It is simplified by using the Resource Suggestions view in the Vespa Console: - -![Resource Suggestions](/assets/img/resource-suggestions-1.png) - -Vespa Cloud tracks usage over time and suggests node configuration -and [topology](topology-and-resizing.html) changes based on last week's load. -In the example above, observe a suggestion that doubles the memory relative to CPU. - -This simplifies _what_ to configure, and one can roll out isolated changes while -observing latency and other business metrics like relevance quality. - - - -## Automated resource configuration deployment -Resource configuration is part of the [application package](../basics/applications.html). -To change a cluster's resources, deploy the new version of the application package to Vespa Cloud -and wait for the changes to apply: - -1. Changes to stateless Vespa Container clusters are almost instant, - dependent on the cloud provider's provisioning latency. -2. Changes to stateful Vespa Content clusters (where the document indices are stored) take more time, - as data is redistributed for uniform load: - 1. Changing the node `count` will modify the existing cluster. - 2. Changing the `resources` configuration will set up a parallel cluster and migrate data to it. - This is generally slower than changing the node count, as more data moves. - -{% include important.html content='Vespa Cloud is designed for online changes. -All of the above changes can be deployed to a running system, with zero to minimal disruption. -See [content cluster elasticity](../content/elasticity.html) for details.' %} - -Making changes to the resource specifications is hence fully automated. -The quickest way to the sweet spot is to initially deploy with enough capacity -and do daily re-tuning to cut cost. - -Vespa Cloud provides performance dashboards with the relevant metrics in this phase: - -![performance dashboard](/assets/img/dashboard.png) - -Eventually, the application has its optimal price/performance characteristics, -without lengthy benchmarking activities. - - -## Automated instance type migration -Resource configurations map to the cloud provider's real resources, like AWS EC2 compute instances. -The instance inventory develops over time, like: - -* _r7g_4xlarge (Graviton3)_ -* _r8g_4xlarge (Graviton4)_ - -Both have 16 vCPU and 128G RAM, but _r8g_4xlarge_ is of a newer generation, -and has presumably higher performance: -_"R8g instances deliver around 30% higher performance over R7g instances, ..."_ - -{% include important.html content='As the `resources` configuration is general and independent of instance types, -Vespa Cloud will automatically migrate load to more cost-effective compute instances over time.' %} - -This means, Vespa Cloud applications will migrate to more recent instance types of the same configuration, -with **zero manual interventions**. -This keeps the total cost in check, and performance tracking advances in hardware. - -Find a list of supported instance types at -[AWS](instance-types/aws-instance-types.html), -[GCP](instance-types/gcp-instance-types.html), and -[Azure](instance-types/azure-instance-types.html) instance types. - - -### Instance type equivalence -Some instance types are so similar that it does not matter which one you get - -example from the [GCP Instance Type Reference](instance-types/gcp-instance-types.html): - - arm64 32.0 256 - x86_64 32.0 252 - x86_64 32.0 256 - -Observe variations in both architecture and memory capacity. -CPU architectures have different load profiles, -but not setting the architecture increases likelihood of successfully provisioning the node during shortages. - -Nodes must match resources to within a 2% difference to be eligible, -e.g., this configuration matches all of the above: - - - - - -## Next reads -* [The practical search performance guide](../performance/practical-search-performance-guide.html) -* [Autoscaling](../operations/autoscaling.html) - - diff --git a/en/performance/node-resources.mdx b/en/performance/node-resources.mdx new file mode 100644 index 0000000000..517502f025 --- /dev/null +++ b/en/performance/node-resources.mdx @@ -0,0 +1,104 @@ +--- +title: "Node Resources" +--- + +This guide goes through the following aspects of node resource configuration: + +1. Independent configuration of resource dimensions +2. Using automated resource suggestions +3. Deployment automation for rapid optimization cycles +4. Automated instance type migration for optimal performance over time + + +## Independent resource dimensions +In Vespa Cloud, a node's `resources` is configured like: + +```xml + + + +``` + +With this, you specify the dimensions independently. E.g., one can double the CPU, keeping all other dimensions constant. + +This is important when tuning for the optimal price/performance point, as the pieces of an application has different sweet spots. For example, the product search cluster of an application can be more CPU bound than product recommendations; the latter might need relatively more memory. + + +**Note:** + +the above is a simplified example, there are more resource dimensions like GPU use, or CPU architecture, available. + + +Optimizing for cost/performance is therefore easy. Simplified, applications can be CPU, disk, or memory bound. A general rule of thumb is to be bound by the most expensive component, often CPU. Refer to the node resource [reference](/en/reference/applications/services/services#resources) for all dimensions. + +## Resource suggestions + +Applications change over time: + +- Data size growth +- Query rate growth +- Write rate growth +- Schema changes, like new fields or binarized embeddings + +Finding the optimal node configuration is an iterative process. It is simplified by using the Resource Suggestions view in the Vespa Console: + +![Resource Suggestions](/assets/img/resource-suggestions-1.png) + +Vespa Cloud tracks usage over time and suggests node configuration and [topology](/en/performance/topology-and-resizing) changes based on last week's load. In the example above, observe a suggestion that doubles the memory relative to CPU. + +This simplifies *what* to configure, and one can roll out isolated changes while observing latency and other business metrics like relevance quality. + +## Automated resource configuration deployment + +Resource configuration is part of the [application package](/en/basics/applications). To change a cluster's resources, deploy the new version of the application package to Vespa Cloud and wait for the changes to apply: + + + +Changes to stateless Vespa Container clusters are almost instant, dependent on the cloud provider's provisioning latency. + + +Changes to stateful Vespa Content clusters (where the document indices are stored) take more time, as data is redistributed for uniform load: + 1. Changing the node `count` will modify the existing cluster. + 2. Changing the `resources` configuration will set up a parallel cluster and migrate data to it. This is generally slower than changing the node count, as more data moves. + + + + +**Important:** + +Vespa Cloud is designed for online changes. All of the above changes can be deployed to a running system, with zero to minimal disruption. See [content cluster elasticity](/en/content/elasticity) for details. + + +Making changes to the resource specifications is hence fully automated. The quickest way to the sweet spot is to initially deploy with enough capacity and do daily re-tuning to cut cost. + +Vespa Cloud provides performance dashboards with the relevant metrics in this phase: + +![performance dashboard](/assets/img/dashboard.png) + +Eventually, the application has its optimal price/performance characteristics, without lengthy benchmarking activities. + +## Automated instance type migration + +Resource configurations map to the cloud provider's real resources, like AWS EC2 compute instances. The instance inventory develops over time, like: + +- *r7g\_4xlarge (Graviton3)* +- *r8g\_4xlarge (Graviton4)* + +Both have 16 vCPU and 128G RAM, but *r8g\_4xlarge* is of a newer generation, and has presumably higher performance: *"R8g instances deliver around 30% higher performance over R7g instances, …"* + + +**Important:** + +As the `resources` configuration is general and independent of instance types, Vespa Cloud will automatically migrate load to more cost-effective compute instances over time. + + +This means, Vespa Cloud applications will migrate to more recent instance types of the same configuration, with **zero manual interventions**. This keeps the total cost in check, and performance tracking advances in hardware. + +Find a list of supported instance types at [AWS](/en/performance/instance-types/aws-instance-types), [GCP](/en/performance/instance-types/gcp-instance-types), and [Azure](/en/performance/instance-types/azure-instance-types) instance types. + +## Next reads + + + + + diff --git a/en/performance/practical-search-performance-guide.md b/en/performance/practical-search-performance-guide.md deleted file mode 100644 index c95ca052ff..0000000000 --- a/en/performance/practical-search-performance-guide.md +++ /dev/null @@ -1,2395 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Vespa query performance - a practical guide" ---- - -This is a practical Vespa query performance guide. It uses the -[Last.fm](http://millionsongdataset.com/lastfm/) tracks dataset to illustrate Vespa query performance. -Latency numbers mentioned in the guide are obtained from running this guide on a MacBook Pro x86. - -This guide covers the following query serving performance aspects: -- [Basic text search query performance](#basic-text-search-query-performance) -- [Hits and document summaries](#hits-and-summaries) -- [Multivalued query operators (dotProduct, weakAnd, wand, in)](#multi-valued-query-operators) -- [Searching attribute fields](#searching-attribute-fields) -- [Searching attribute fields with fast-search](#searching-attribute-fields-using-fast-search) -- [Ranking with tensor computations](#tensor-computations) -- [Multithreaded search and ranking](#multithreaded-search-and-ranking) -- [Range-search with hit limits for early termination](#advanced-range-search-with-hitlimit) -- [Early termination using match phase limits](#match-phase-limit---early-termination) -- [Advanced query tracing](#advanced-query-tracing) - -The guide includes step-by-step instructions on how to reproduce the experiments. -This guide is best read after having read the [Vespa Overview](../learn/overview.html) documentation first. - -{% include pre-req.html memory="4 GB" extra-reqs=' -
  • Python3 for converting the dataset to Vespa JSON.
  • -
  • curl to download the dataset and run the Vespa health-checks.
  • ' %} - - -## Installing vespa-cli - -This tutorial uses [Vespa-CLI](../clients/vespa-cli.html), -Vespa CLI is the official command-line client for Vespa.ai. -It is a single binary without any runtime dependencies and is available for Linux, macOS and Windows. - -
    - -
    -$ brew install vespa-cli 
    -
    -
    - -## Dataset -This guide uses the [Last.fm](http://millionsongdataset.com/lastfm/) tracks dataset. -Note that the dataset is released under the following terms: - ->Research only, strictly non-commercial. For details, or if you are unsure, please contact Last.fm. ->Also, Last.fm has the right to advertise and refer to any work derived from the dataset. - -To download the dataset directly (120 MB zip file), run: -
    - -
    -$ curl -L -o lastfm_test.zip \
    -    http://millionsongdataset.com/sites/default/files/lastfm/lastfm_test.zip 
    -$ unzip lastfm_test.zip
    -
    -
    - -The downloaded data needs to be converted to -[the JSON format expected by Vespa](../reference/schemas/document-json-format.html). - -This [python](https://www.python.org/) script is used to traverse the dataset -files and create a JSONL formatted feed file with Vespa feed operations. -The schema for this feed is introduced in the next sections. - -
    -import os
    -import sys
    -import json
    -import unicodedata
    -
    -directory = sys.argv[1]
    -seen_tracks = set() 
    -
    -def remove_control_characters(s):
    -    return "".join(ch for ch in s if unicodedata.category(ch)[0]!="C")
    -
    -def process_file(filename):
    -    global seen_tracks
    -    with open(filename) as fp:
    -        doc = json.load(fp)
    -        title = doc['title']
    -        artist = doc['artist']
    -        hash = title + artist
    -        if hash in seen_tracks:
    -            return
    -        else:
    -            seen_tracks.add(hash) 
    -
    -        track_id = doc['track_id']
    -        tags = doc['tags']
    -        tags_dict = dict()
    -        for t in tags:
    -            k,v = t[0],int(t[1])
    -            tags_dict[k] = v
    -        similars = doc['similars']
    -        tensor_cells = []
    -        keys_seen = dict()
    -        for s in similars:
    -            k,v = s[0],float(s[1])
    -            if k in keys_seen:
    -                continue
    -            else:
    -                keys_seen[k] = 1
    -            cell = {
    -                "address": {
    -                    "trackid": k
    -                },
    -                "value": v
    -            }
    -            tensor_cells.append(cell)
    -
    -        vespa_doc = {
    -            "put": "id:music:track::%s" % track_id,
    -                "fields": {
    -                    "title": remove_control_characters(title),
    -                    "track_id": track_id,
    -                    "artist": remove_control_characters(artist),
    -                    "tags": tags_dict,
    -                    "similar": {
    -                        "cells": tensor_cells
    -                    }
    -            }
    -        }
    -        print(json.dumps(vespa_doc))
    -
    -sorted_files = []
    -for root, dirs, files in os.walk(directory):
    -    for filename in files:
    -        filename = os.path.join(root, filename)
    -        sorted_files.append(filename)
    -sorted_files.sort()
    -for filename in sorted_files:
    -    process_file(filename)
    -
    - -
    {% highlight python%}
    -import os
    -import sys
    -import json
    -import unicodedata
    -
    -directory = sys.argv[1]
    -seen_tracks = set() 
    -
    -def remove_control_characters(s):
    -    return "".join(ch for ch in s if unicodedata.category(ch)[0]!="C")
    -
    -def process_file(filename):
    -    global seen_tracks
    -    with open(filename) as fp:
    -        doc = json.load(fp)
    -        title = doc['title']
    -        artist = doc['artist']
    -        hash = title + artist
    -        if hash in seen_tracks:
    -            return
    -        else:
    -            seen_tracks.add(hash) 
    -
    -        track_id = doc['track_id']
    -        tags = doc['tags']
    -        tags_dict = dict()
    -        for t in tags:
    -            k,v = t[0],int(t[1])
    -            tags_dict[k] = v
    -        similars = doc['similars']
    -        tensor_cells = []
    -        keys_seen = dict()
    -        for s in similars:
    -            k,v = s[0],float(s[1])
    -            if k in keys_seen:
    -                continue
    -            else:
    -                keys_seen[k] = 1
    -            cell = {
    -                "address": {
    -                    "trackid": k
    -                },
    -                "value": v
    -            }
    -            tensor_cells.append(cell)
    -
    -        vespa_doc = {
    -            "put": "id:music:track::%s" % track_id,
    -                "fields": {
    -                    "title": remove_control_characters(title),
    -                    "track_id": track_id,
    -                    "artist": remove_control_characters(artist),
    -                    "tags": tags_dict,
    -                    "similar": {
    -                        "cells": tensor_cells
    -                    }
    -            }
    -        }
    -        print(json.dumps(vespa_doc))
    -
    -sorted_files = []
    -for root, dirs, files in os.walk(directory):
    -    for filename in files:
    -        filename = os.path.join(root, filename)
    -        sorted_files.append(filename)
    -sorted_files.sort()
    -for filename in sorted_files:
    -    process_file(filename)
    -{% endhighlight %}
    - -Run the script and create the `feed.jsonl` file: - -
    - -
    -$ python3 create-vespa-feed.py lastfm_test > feed.jsonl
    -
    -
    - -## Create a Vespa Application Package - -A [Vespa application package](../basics/applications.html) is the set -of configuration files and Java plugins that together define the behavior of a Vespa system: -what functionality to use, the available document types, how ranking will be done, -and how data will be processed during feeding and indexing. - -The minimum required files to create the basic search application are `track.sd` and `services.xml`. -Create directories for the configuration files: - -
    - -
    -$ mkdir -p app/schemas; mkdir -p app/search/query-profiles/
    -
    -
    - -### Schema - -A Vespa [schema](../basics/schemas.html) is a configuration of a document type and ranking and -compute specifications. This app use a `track` schema defined as: - -
    -schema track {
    -
    -    document track {
    -
    -        field track_id type string {
    -            indexing: summary | attribute
    -            rank: filter
    -            match: word
    -        }
    -
    -        field title type string {
    -            indexing: summary | index
    -            index: enable-bm25
    -        }
    -
    -        field artist type string {
    -            indexing: summary | index
    -        }
    -
    -        field tags type weightedset<string> {
    -            indexing: summary | attribute
    -        }
    -
    -        field similar type tensor<float>(trackid{}) {
    -            indexing: summary | attribute
    -        }
    -    }
    -
    -    fieldset default {
    -        fields: title, artist
    -    }
    -}
    -
    - -Notice that the `track_id` field has : - -- [rank: filter](../reference/schemas/schemas.html#rank). -This setting can save resources when matching against the field. -- [match: word](../reference/schemas/schemas.html#match). -This is a database-style matching mode, preserving punctuation characters. - -### Services Specification - -The [services.xml](../reference/applications/services/services.html) defines the services that make up -the Vespa application — which services to run and how many nodes per service. - -
    -<?xml version="1.0" encoding="UTF-8"?>
    -<services version="1.0">
    -
    -    <container id="default" version="1.0">
    -        <search/>
    -        <document-api/>
    -    </container>
    -
    -    <content id="tracks" version="1.0">
    -        <redundancy>1</redundancy>
    -        <documents>
    -            <document type="track" mode="index"></document>
    -        </documents>
    -        <nodes>
    -            <node distribution-key="0" hostalias="node1"></node>
    -        </nodes>
    -    </content>
    -</services>
    -
    - -The default [query profile](../querying/query-profiles.html) can be used to override -default query api settings for all queries. - -The following enables [presentation.timing](../reference/api/query.html#presentation.timing) and -renders `weightedset` fields as JSON maps. - -
    -<query-profile id="default">
    -    <field name="presentation.timing">true</field>
    -    <field name="renderer.json.jsonWsets">true</field>
    -</query-profile>
    -
    - -## Deploy the application package - -The application package can now be deployed to a running Vespa instance. -See also the [Vespa quick start guide](../basics/deploy-an-application-local.html). - -Start the Vespa container image using Docker: - -
    - -
    -$ docker run --detach --name vespa --hostname vespa-container \
    -  --publish 8080:8080 --publish 19071:19071 --publish 19110:19110 \
    -  vespaengine/vespa
    -
    -
    - -Starting the container can take a short while. Before continuing, make sure -that the configuration service is running by using `vespa status deploy`. - -
    - -
    -$ vespa config set target local
    -$ vespa status deploy --wait 300 
    -
    -
    - -Once ready, the Vespa application can be deployed using the Vespa CLI: - -
    - -
    -$ vespa deploy --wait 300 app
    -
    -
    - -## Index the dataset -Feed the feed file generated in the previous section: - -
    - -
    -$ vespa feed -t http://localhost:8080 feed.jsonl
    -
    -
    - -## Basic text search query performance -The following sections use the Vespa [query api](../reference/api/query.html) and -formulate queries using Vespa [query language](../querying/query-language.html). -For readability, all query examples are expressed using the -[vespa-cli](../clients/vespa-cli.html) command which supports running queries against a Vespa instance. -The CLI uses the Vespa http search api internally. -Use `vespa query -v` to see the actual http request sent: - -
    -$ vespa query -v 'yql=select ..'
    -
    - -The first query uses `where true` to match all `track` documents. -It also uses [hits](../reference/api/query.html#hits) to specify how many -documents to return in the response: - -
    - -
    -$ vespa query \
    -    'yql=select artist, title, track_id, tags from track where true' \
    -    'hits=1'
    -
    -
    - -The [result json output](../reference/querying/default-result-format.html) for this query will -look something like this: - -
    {% highlight json%}
    -{
    -    "timing": {
    -        "querytime": 0.009000000000000001,
    -        "summaryfetchtime": 0.001,
    -        "searchtime": 0.011
    -    },
    -    "root": {
    -        "id": "toplevel",
    -        "relevance": 1.0,
    -        "fields": {
    -            "totalCount": 95666
    -        },
    -        "coverage": {
    -            "coverage": 100,
    -            "documents": 95666,
    -            "full": true,
    -            "nodes": 1,
    -            "results": 1,
    -            "resultsFull": 1
    -        },
    -        "children": [
    -            {
    -                "id": "index:tracks/0/632facf01973795ba294b7d5",
    -                "relevance": 0.0,
    -                "source": "tracks",
    -                "fields": {
    -                    "track_id": "TRRRGWV128F92FC7E0",
    -                    "title": "Zombies",
    -                    "artist": "True Blood"
    -                }
    -            }
    -        ]
    -    }
    -}
    -{% endhighlight %}
    - -Observations: - -- The query searched one node (`coverage.nodes`) and the -coverage (`coverage.coverage`) was 100%, -see [graceful-degradation](graceful-degradation.html) for more information about -the `coverage` element, and Vespa timeout behavior. Vespa's default timeout is 0.5 seconds. -- The query matched a total of 95666 documents (`totalCount`) out of -95666 documents available (`coverage.documents`). - -The response `timing` has three fields. A Vespa query is executed in two protocol phases: - -- Query matching phase which fans the query out from the stateless container -to a content group, each node in the group finds the nodes top-k documents and returns k. -The stateless container then merges the nodes' k hits each to obtain a globally ordered top-k documents. -- Summary phase which asks the content nodes that produced the global top-k hits for summary data. - -See also [Life of a query in Vespa](sizing-search.html#life-of-a-query-in-vespa). The `timing` -in the response measures the time it takes to execute these two phases: - -- `querytime` - Time to execute the first protocol phase/matching phase. -- `summaryfetchtime` - Time to execute the summary fill protocol phase for the globally ordered top-k hits. -- `searchtime` Is roughly the sum of the above and is close to what a client will observe (except network latency). - -All three metrics are second resolution. Moving on, the following query performs a free text query: - -
    - -
    -$ vespa query \
    -    'yql=select artist, title, track_id from track where userQuery()' \
    -    'query=total eclipse of the heart' \
    -    'hits=1'
    -
    -
    - -This query request combines YQL [userQuery()](../reference/querying/yql.html#userquery) -with Vespa's [simple query language](../reference/querying/simple-query-language.html), the -default [query type](../reference/api/query.html#model.type) is -using `all`, requiring that all the terms match. - -The above example searches for *total AND eclipse AND of AND the AND heart* in the fieldset `default`, -which in the schema includes the `title` and `artist` fields. -Since the request did not specify any [ranking](../basics/ranking.html) parameters, -the matched documents were ranked by Vespa's default -text rank feature: [nativeRank](../ranking/nativerank.html). - -The result output for the above query: - -
    {% highlight json%}
    -{
    -    "timing": {
    -        "querytime": 0.012,
    -        "summaryfetchtime": 0.001,
    -        "searchtime": 0.014
    -    },
    -    "root": {
    -        "id": "toplevel",
    -        "relevance": 1.0,
    -        "fields": {
    -            "totalCount": 1
    -        },
    -        "coverage": {
    -            "coverage": 100,
    -            "documents": 95666,
    -            "full": true,
    -            "nodes": 1,
    -            "results": 1,
    -            "resultsFull": 1
    -        },
    -        "children": [
    -            {
    -                "id": "index:tracks/0/3f18869c19c25e3ae237702f",
    -                "relevance": 0.13274821039905835,
    -                "source": "tracks",
    -                "fields": {
    -                    "track_id": "TRUKHZD128F92DF70A",
    -                    "title": "Total Eclipse Of The Heart",
    -                    "artist": "Bonnie Tyler"
    -                }
    -            }
    -        ]
    -    }
    -}
    -{% endhighlight %}
    - -This query only matched one document because the query terms were ANDed. -Matching can be relaxed to `type=any` instead using -[query model type](../reference/api/query.html#model.type). - -
    - -
    -$ vespa query \
    -    'yql=select artist, title, track_id from track where userQuery()' \
    -    'query=total eclipse of the heart' \
    -    'hits=1' \
    -    'type=any'
    -
    -
    - -Now, the query matches 24,053 documents and is considerably slower than the previous `all` query. -Comparing `querytime` of these two query examples, the one which matches the most documents -have the highest `querytime`. In worst case, the search query matches all documents, and -without any techniques for early termination or skipping, all matches are exposed to ranking. -Query performance is greatly impacted by the number of documents -that matches the query specification. Generally, type `any` queries -requires more query compute resources than type `all`. - -There is an algorithmic optimization available for `type=any` queries, using -the `weakAnd` query operator which implements the WAND algorithm. -See the [using wand with Vespa](../ranking/wand.html) for an -introduction to the algorithm. - -Run the same query, but instead of `type=any` use `type=weakAnd`: - -
    - -
    -$ vespa query \
    -    'yql=select artist, title, track_id from track where userQuery()' \
    -    'query=total eclipse of the heart' \
    -    'hits=1' \
    -    'type=weakAnd'
    -
    -
    - -Compared to the type `any` query which fully ranked 24,053 documents, -`weakAnd` only fully ranks 3,679 documents. -Also notice that the faster search returns the same document at the first position. -Conceptually a search query is about finding the documents that match the query, -then score the documents using a ranking model. -In the worst case a search query can match all documents, which will expose -all of them to the ranking. - -## Hits and summaries -The previous examples used `hits=1` query parameter, and in the previous -query examples the `summaryfetchtime` has been close to constant. - -The following query requests considerably more hits, note that the result is piped to `head` -to increase readability: - -
    - -
    -$ vespa query \
    -    'yql=select artist, title, track_id from track where userQuery()' \
    -    'query=total eclipse of the heart' \
    -    'hits=200' \
    -    'type=weakAnd' |head -40 
    -
    -
    - -Increasing number of hits increases `summaryfetchtime` significantly from -the previous query examples, while `querytime` is relatively unchanged. -Repeating the query a second time will reduce the `summaryfetchtime` -due to the content node summary cache, -see [caches in Vespa](../performance/caches-in-vespa.html) for details. - -There are largely four factors which determines the `summaryfetchtime`: - -- The number of hits requested and number of content nodes that produced the -query result which makes up the `querytime`. - With many content nodes in the group the query was dispatched to, - we expect that top-ranking hits would be distributed across the nodes, so that each node - does less work. -- The network package size of each hit. -Returning hits with larger fields costs more resources and -higher `summaryfetchtime` than smaller docs. -- The summary used with the query, and which fields go into the summary. -For example, a [document-summary](../querying/document-summaries.html) which only contain -fields that are defined as `attribute` will be read from memory. For the `default` summary, or others -containing at least one non-attribute field, a fill will potentially access data -from summary storage on disk. Read more about in-memory [attribute](../content/attributes.html) fields. -- [summary-features](../reference/schemas/schemas.html#summary-features) used to return computed - [rank features](../reference/ranking/rank-features.html) from the content nodes. - -Creating a dedicated [document-summary](../querying/document-summaries.html) which -only contain the `track_id` field can improve performance, since `track_id` is defined in the schema with -`attribute`, any summary fetches using this document summary will be reading in-memory data. -In addition, since the summary only contain one field, it saves network time as less data is -transferred during the summary fill phase. - -
    -document-summary track_id {
    -    summary track_id { }
    -}
    -
    - -The new schema then becomes: - -
    -schema track {
    -
    -    document track {
    -
    -        field track_id type string {
    -            indexing: summary | attribute
    -            rank: filter
    -            match: word
    -        }
    -
    -        field title type string {
    -            indexing: summary | index
    -            index: enable-bm25
    -        }
    -
    -        field artist type string {
    -            indexing: summary | index
    -        }
    -
    -        field tags type weightedset<string> {
    -            indexing: summary | attribute
    -        }
    -
    -        field similar type tensor<float>(trackid{}) {
    -            indexing: summary | attribute
    -        }
    -    }
    -
    -    fieldset default {
    -        fields: title, artist
    -    }
    -
    -    document-summary track_id {
    -        summary track_id { }
    -    }
    -}
    -
    - -Re-deploy the application: - -
    - -
    -$ vespa deploy --wait 300 app
    -
    -
    - -Re-executing the query using the `track_id` `document-summary` is done by -setting the [summary](../reference/api/query.html#presentation.summary) -query request parameter: - -
    - -
    -$ vespa query \
    -    'yql=select artist, title, track_id from track where userQuery()' \
    -    'query=total eclipse of the heart' \
    -    'hits=200' \
    -    'type=weakAnd' \
    -    'summary=track_id' |head -40 
    -
    -
    - -In this particular case the `summaryfetchtime` difference is not that large, but for larger number of hits and -larger documents the difference is significant. Especially in single content node deployments. - -A note on select field scoping with YQL, e.g. `select title, track_id from ..`. -When using the default summary by not using a summary parameter, all fields are delivered from the -content nodes to the stateless search container in the summary fill phase, -regardless of field scoping. The search container removes the set of fields not -selected and renders the result. Hence, select scoping only reduces the amount of data -transferred back to the client, and does not impact or optimize the performance -of the internal communication and potential summary cache miss. -For optimal performance for use cases asking for large number of hits to the client it is -recommended to use dedicated document summaries. -Note also that Vespa per default limits the max hits to 400 per default, -the behavior can be overridden in the -[default queryProfile](../reference/api/query.html#queryprofile). - -When requesting large amounts of data, consider how to reduce response size. -Vespa supports gzip compression if the HTTP client uses -the [Accept-Encoding](https://www.rfc-editor.org/rfc/rfc9110.html#name-accept-encoding) HTTP request header: -
    -Accept-Encoding: gzip
    -
    - -Compression reduces data transfer but adds CPU overhead. -The lowest latency is achieved without compression if network bandwidth is sufficient. -[CBOR format](../reference/api/query.html#presentation.format) (`format=cbor` or `Accept: application/cbor`) -is both more compact and faster to generate than JSON, especially for numeric data -such as tensors and embeddings. CBOR can also be combined with gzip compression. -CBOR is a drop-in replacement for JSON - when deserialized, the result is identical. - -## Searching attribute fields - -The previous section covered free text searching in a `fieldset` containing fields with -`indexing:index`. See [indexing reference](../reference/schemas/schemas.html#indexing). -Fields of [type string](../reference/schemas/schemas.html#field) are -treated differently depending on having `index` or `attribute`: - -- `index` integrates with [linguistic](../linguistics/linguistics.html) processing and is matched using -[match:text](../reference/schemas/schemas.html#match). - -- `attribute` does not integrate with linguistic processing and is matched using -[match:word](../reference/schemas/schemas.html#match). - -With `index` Vespa builds inverted index data structures which roughly consist of: - -- A dictionary of the unique text tokens (after linguistic processing) -- Posting lists for each unique text token in the collection. Posting lists comes in different -formats, and using `rank: filter` can help guide the decision on what format to use. Bitvector -representation is the most compacting post list representation. - -With `attribute` Vespa will per default not build any inverted index-like data structures for -potential faster query evaluation. See [Wikipedia:Inverted Index](https://en.wikipedia.org/wiki/Inverted_index) -and [Vespa internals](../content/proton.html#index). -The reason for this default setting is that Vespa `attribute` fields can be used -for many different aspects: [ranking](../basics/ranking.html), [result grouping](../querying/grouping.html), - [result sorting](../reference/querying/sorting-language.html), and finally searching/matching. - -The following section focuses on the `tags` field which we defined with `attribute`, -matching in this field will be performed using `match:word` which is the -default match mode for string fields with `indexing: attribute`. -The `tags` field is of type [weightedset](../reference/schemas/schemas.html#weightedset). - -
    - field tags type weightedset<string> {
    -      indexing: summary | attribute
    - }
    -
    - -`weightedset` is a field type that allows representing a tag with an integer weight, which can be used for ranking. -In this case, there is no inverted index structure, -and matching against the `tags` field is performed as a linear scan. -The following scans for documents where `tags` match *rock*: - -
    - -
    -$ vespa query \
    -    'yql=select track_id, tags from track where tags contains "rock"' \
    -    'hits=1' 
    -
    -
    - -The query matches 8,160 documents, notice that for `match: word`, matching can also include whitespace, -or generally punctuation characters which are removed and not searchable -when using `match:text` with string fields that have `index`: - -
    - -
    -$ vespa query \
    -    'yql=select track_id, tags from track where tags contains "classic rock"' \
    -    'hits=1' 
    -
    -
    -The above query matches exactly tags with "classic rock", not "rock" and also not "classic rock music". - -Another query searching for *rock* or *pop*: - -
    - -
    -$ vespa query \
    -    'yql=select track_id, tags from track where tags contains "rock" or tags contains "pop"' \
    -    'hits=1' 
    -
    -
    - -In all these examples searching the `tags` field, the matching is done by a linear scan through all -`track` documents. The `tags` search can be combined with regular -free text query terms searching fields that do have inverted index structures: - -
    - -
    -$ vespa query \
    -    'yql=select track_id, tags from track where tags contains "rock" and userQuery()' \
    -    'hits=1' \
    -    'query=total eclipse of the heart'
    -
    -
    - -In this case - the query terms searching the default fieldset will restrict the number of -documents that needs to be scanned for the _tags_ constraint. This query is automatically optimized -by the Vespa query planner. - -### Searching attribute fields using fast-search -This section adds `fast-search` to the `tags` field to speed up searches where there are no -other query filters which restricts the search. The schema with `fast-search`: - -
    -schema track {
    -
    -    document track {
    -
    -        field track_id type string {
    -            indexing: summary | attribute
    -            rank: filter
    -            match: word
    -        }
    -
    -        field title type string {
    -            indexing: summary | index
    -            index: enable-bm25
    -        }
    -
    -        field artist type string {
    -            indexing: summary | index
    -        }
    -
    -        field tags type weightedset<string> {
    -            indexing: summary | attribute
    -            attribute: fast-search
    -        }
    -
    -        field similar type tensor<float>(trackid{}) {
    -            indexing: summary | attribute
    -        }
    -    }
    -
    -    fieldset default {
    -        fields: title, artist
    -    }
    -
    -    document-summary track_id {
    -        summary track_id { }
    -    }
    -}
    -
    - -Re-deploy the application: - -
    - -
    -$ vespa deploy --wait 300 app
    -
    -
    - -The above will print a WARNING: - -
    -vespa deploy --wait 300 app/ 
    -Uploading application package ... done
    -
    -Success: Deployed app/
    -WARNING Change(s) between active and new application that require restart:
    -In cluster 'tracks' of type 'search':
    -    Restart services of type 'searchnode' because:
    -        1) Document type 'track': Field 'tags' changed: add attribute 'fast-search'
    -
    -Waiting up to 300 seconds for query service to become available ...
    -
    - -To enable `fast-search`, content node(s) needs to be restarted to re-build the fast-search data structures -for the attribute. - -The following uses [vespa-sentinel-cmd command tool](/en/reference/operations/self-managed/tools.html#vespa-sentinel-cmd) -to restart the searchnode process: - -
    - -
    -$ docker exec vespa vespa-sentinel-cmd restart searchnode
    -
    -
    - -This step requires waiting for the searchnode, use the [health state api](../operations/metrics.html): -
    - -
    -$ curl -s http://localhost:19110/state/v1/health
    -
    -
    - -Wait for status code to flip to `up` before querying again: - -
    {% highlight json%}
    -{
    -    "status": {
    -        "code": "up"
    -    }
    -}
    -{% endhighlight %}
    - -
    -$ sleep 60
    -
    - -Once up, execute the `tags` query again: - -
    - -
    -$ vespa query \
    -    'yql=select track_id, tags from track where tags contains "rock" or tags contains "pop"' \
    -    'hits=1' 
    -
    -
    - -Now the `querytime` will be a few milliseconds, since Vespa has built index structures to support -`fast-search` in the attribute. The downside of enabling `fast-search` is -increased memory usage and slightly reduced indexing throughput. See also -[when to use fast-search for attributes](feature-tuning.html#when-to-use-fast-search-for-attribute-fields). - -For use cases requiring `match:text` when searching multivalued string field types -like [weightedset](../reference/schemas/schemas.html#weightedset), see -[searching multi-value fields](../searching-multi-valued-fields.html). - -For fields that don't need any match ranking features, it's strongly recommended -to use [rank: filter](../reference/schemas/schemas.html#rank). - -
    -field availability type int {
    -    indexing: summary | attribute
    -    rank: filter
    -    attribute {
    -        fast-search
    -    }
    -}
    -
    - -With the settings above, bit vector posting list representations are used. This is especially efficient -when used in combination with [TAAT (term at a time)](feature-tuning.html#hybrid-taat-daat) -query evaluation. For some cases with many query terms, enabling `rank: filter` can reduce match latency -by 75%. - -## Multi-valued query operators - -This section covers [multi-value query operators](../ranking/multivalue-query-operators.html) -and their query performance characteristics. Many real-world search and recommendation use cases -involve structured multivalued queries. - -Assuming a process has learned a sparse user profile representation, which, for a given user, based -on past interactions with a service, could produce a user profile with -*hard rock*, *rock*, *metal* and *finnish metal*. Sparse features from a fixed vocabulary/feature space. - -Retrieving and ranking using sparse representations can be done using -the dot product between the sparse user profile representation and document representation. In the -track example, the `tags` field could be the document side sparse representation. Each document -is tagged with multiple `tags` using a weight, and similar the sparse user profile -representation could use weights. - -In the following examples, the [dotProduct()](../reference/querying/yql.html#dotproduct) and -[wand()](../reference/querying/yql.html#wand) query operators are used. - -To configure [ranking](../basics/ranking.html), add a `rank-profile` to the schema: - -
    -schema track {
    -
    -    document track {
    -
    -        field track_id type string {
    -            indexing: summary | attribute
    -            rank: filter
    -            match: word
    -        }
    -
    -        field title type string {
    -            indexing: summary | index
    -            index: enable-bm25
    -        }
    -
    -        field artist type string {
    -            indexing: summary | index
    -        }
    -
    -        field tags type weightedset<string> {
    -            indexing: summary | attribute
    -            attribute: fast-search
    -        }
    -
    -        field similar type tensor<float>(trackid{}) {
    -            indexing: summary | attribute
    -        }
    -    }
    -
    -    fieldset default {
    -        fields: title, artist
    -    }
    -
    -    document-summary track_id {
    -        summary track_id { }
    -    }
    -
    -    rank-profile personalized {
    -        first-phase {
    -            expression: rawScore(tags)
    -        }
    -    }
    -}
    -
    - -The `dotProduct`and `wand` query operators produce a `rank feature` called -[rawScore(name)](../reference/ranking/rank-features.html#rawScore(field)). This feature calculates -the sparse dot product between the query and document weights. - -Deploy the application again: - -
    - -
    -$ vespa deploy --wait 300 app
    -
    -
    - -The [dotProduct](../reference/querying/yql.html#dotproduct) -query operator accepts a field to match over and supports -[parameter substitution](../reference/querying/yql.html#parameter-substitution). -Using substitution is recommended for large inputs as it saves compute resources when parsing the YQL input. - -The following example assumes a learned sparse representation, with equal weight: -
    -userProfile={"hard rock":1, "rock":1,"metal":1, "finnish metal":1}
    -
    - -This userProfile is referenced as a parameter - -
    -where dotProduct(tags, @userProfile)
    -
    - -
    - -
    -$ vespa query \
    -    'yql=select track_id, title, artist, tags from track where dotProduct(tags, @userProfile)' \
    -    'userProfile={"hard rock":1, "rock":1,"metal":1, "finnish metal":1}' \
    -    'hits=1' \
    -    'ranking=personalized'
    -
    -
    -The query also specifies the `rank-profile` `personalized`, if not specified, ranking would be -using `nativeRank`. The above query returns the following response: - -
    {% highlight json%}
    -{
    -    "timing": {
    -        "querytime": 0.004,
    -        "summaryfetchtime": 0.001,
    -        "searchtime": 0.006
    -    },
    -    "root": {
    -        "id": "toplevel",
    -        "relevance": 1.0,
    -        "fields": {
    -            "totalCount": 10323
    -        },
    -        "coverage": {
    -            "coverage": 100,
    -            "documents": 95666,
    -            "full": true,
    -            "nodes": 1,
    -            "results": 1,
    -            "resultsFull": 1
    -        },
    -        "children": [
    -            {
    -                "id": "index:tracks/0/74d3f4df2989650b2cc095be",
    -                "relevance": 400.0,
    -                "source": "tracks",
    -                "fields": {
    -                    "track_id": "TRJTBAO128F932191C",
    -                    "title": "Vastarannan valssi",
    -                    "artist": "Viikate",
    -                    "tags": {
    -                        "Suomi": 100,
    -                        "rautalanka": 100,
    -                        "suomi rock": 100,
    -                        "hard rock": 100,
    -                        "melodic metal": 100,
    -                        "finnish": 100,
    -                        "finnish metal": 100,
    -                        "metal": 100,
    -                        "rock": 100
    -                    }
    -                }
    -            }
    -        ]
    -    }
    -}
    -{% endhighlight %}
    - -Notice that the query above will brute-force rank all tracks where the `tags` field matches *any* of the multivalued -userProfile features. Due to this, the query ranks 10,323 tracks as seen by `totalCount`. -Including for example *pop* in the userProfile list increases the number of hits to 13,638. - -For a large user profile with many learned features/tags, one would easily match and rank the entire document collection. -Also notice the `relevance` score which is 400, since the document matches all the query input tags (4x100 = 400). - -To optimize the evaluation the [wand query operator](../reference/querying/yql.html#wand) -can be used. The `wand` query operator supports setting a target number of top ranking hits that gets -exposed to the `first-phase` ranking function. - -Repeating the query from above, replacing `dotProduct` with `wand`: -
    - -
    -$ vespa query \
    -    'yql=select track_id, title, artist, tags from track where {totalTargetHits:10}wand(tags, @userProfile)' \
    -    'userProfile={"hard rock":1, "rock":1,"metal":1, "finnish metal":1}' \
    -    'hits=1' \
    -    'ranking=personalized'
    -
    -
    - -The `wand` query operator retrieves the exact same hit at rank 1, which is the expected behavior. -The `wand` query operator is safe, meaning it returns the same top-k results as the `dotProduct` query operator. - -For larger document collections, the *wand* query operator can significantly -improve query performance compared to `dotProduct`. - -*wand* is a query operator which performs matching and ranking interleaved and skips documents -which cannot make it into the top-k results. [Using wand with Vespa](../ranking/wand.html) -guide has more details on the WAND algorithm. - -Finally, these multi-value query operators work on both single-valued fields and array fields, -but optimal performance is achieved using the [weightedset](../reference/schemas/schemas.html#weightedset) -field type. The `weightedset` field type only supports integer weights. The next section -covers tensors that support more floating point number types. - -## Tensor computations -The previous sections covered matching and ranking where query matching query operators -also produced rank features which could be used to influence the order of the hits returned. -In this section we look at ranking with [tensor computations](../ranking/tensor-examples.html) -using [tensor expressions](../ranking/tensor-user-guide.html). - -Tensor computations can be used to calculate dense dot products, sparse -dot products, matrix multiplication, neural networks and more. Tensor computations can be performed -on documents that are retrieved by the query matching operators. The only exception to this is -dense single order tensors (vectors) where Vespa also supports "matching" using [(approximate) nearest -neighbor search](../querying/approximate-nn-hnsw). - - -The `track` schema was defined with a `similar` tensor field with one named *mapped* dimension. -*Mapped* tensors can be used to represent sparse feature representations, similar -to the `weightedset` field, but in a more generic way, and here using `float` to represent -the tensor cell value. - -
    -field similar type tensor<float>(trackid{}) {
    -      indexing: summary | attribute
    -}
    -
    - -Inspecting one document, using the vespa-cli (Wraps [Vespa document/v1 api](../writing/document-v1-api-guide.html)): - -
    - -
    -$ vespa document get id:music:track::TRQIQMT128E0791D9C
    -
    -
    - -Returns: - -
    {% highlight json%}
    -{
    -    "pathId": "/document/v1/music/track/docid/TRUAXHV128F42694E8",
    -    "id": "id:music:track::TRUAXHV128F42694E8",
    -    "fields": {
    -        "artist": "Bryan Adams",
    -        "title": "Summer Of '69",
    -        "similar": {
    -            "cells": [
    -                {
    -                    "address": {
    -                        "trackid": "TRWJIPT128E0791D99"
    -                    },
    -                    "value": 1.0
    -                },
    -                {
    -                    "address": {
    -                        "trackid": "TRKPGHH128F1453DD0"
    -                    },
    -                    "value": 0.9129049777984619
    -                },
    -                 {
    -                    "address": {
    -                        "trackid": "TRGVORX128F4291DF1"
    -                    },
    -                    "value": 0.3269079923629761
    -                }
    -            ]
    -        },
    -        "tags": {
    -            "All time favourites": 1,
    -            "male vocalists": 7,
    -            "singer-songwriter": 6,
    -            "happy": 2,
    -            "Driving": 3,
    -            "classic rock": 59,
    -            "loved": 1,
    -            "Energetic": 2,
    -            "male vocalist": 1,
    -            "dance": 1,
    -            "soft rock": 2,
    -            "1980s": 1
    -        }
    -    }
    -}
    -{% endhighlight %}
    - -In the lastfm collection, each track lists similar tracks with a similarity score using float resolution, according to this -similarity algorithm the most similar track to this sample document is `TRWJIPT128E0791D99`, with a similarity score of 1.0. - -Searching for that doc using the query api: - -
    - -
    -$ vespa query \
    -    'yql=select title, artist from track where track_id contains "TRWJIPT128E0791D99"' \
    -    'hits=1'  
    -
    -
    - -Note that `track_id` was not defined with `fast-search`, so searching it without any other query terms makes this -query a linear scan over all tracks. - -The query returns: - -
    {% highlight json %}
    -{
    -    "timing": {
    -        "querytime": 0.01,
    -        "summaryfetchtime": 0.002,
    -        "searchtime": 0.013000000000000001
    -    },
    -    "root": {
    -        "id": "toplevel",
    -        "relevance": 1.0,
    -        "fields": {
    -            "totalCount": 1
    -        },
    -        "coverage": {
    -            "coverage": 100,
    -            "documents": 95666,
    -            "full": true,
    -            "nodes": 1,
    -            "results": 1,
    -            "resultsFull": 1
    -        },
    -        "children": [
    -            {
    -                "id": "index:tracks/0/63eadd770a5dfde1f111aed7",
    -                "relevance": 0.0017429193899782135,
    -                "source": "tracks",
    -                "fields": {
    -                    "title": "Run To You",
    -                    "artist": "Bryan Adams"
    -                }
    -            }
    -        ]
    -    }
    -}
    -{% endhighlight %}
    - -Given a single track, one could just retrieve the document and display the offline computed similar tracks, but, -if a user has listened to multiple tracks in a real time session, one could use a sparse dot product -between the user recent activity and the track similarity fields. For example, listening -to the following tracks: - -- `TRQIQMT128E0791D9C` Summer Of '69 by Bryan Adams -- `TRWJIPT128E0791D99` Run To You by Bryan Adams -- `TRGVORX128F4291DF1` Broken Wings by Mr. Mister - -could be represented as a query tensor `query(user_liked)` and passed with the query request like this: - -
    {% raw %}
    -input.query(user_liked)={{trackid:TRUAXHV128F42694E8 }:1.0,{trackid:TRQIQMT128E0791D9C}:1.0,{trackid:TRGVORX128F4291DF1}:1.0}
    -{% endraw %}
    - -Both the document tensor and the query tensor are defined with `trackid{}` as the *named* *mapped* dimension. The -sparse tensor dot product can then be expression in a `rank-profile`: - -
    -rank-profile similar {
    -    inputs {
    -        query(user_liked) tensor<float>(trackid{})
    -    }
    -    first-phase {
    -        expression: sum(attribute(similar) * query(user_liked))
    -    }
    -}
    -
    - -See [tensor user guide](../ranking/tensor-user-guide.html) for more on tensor fields and tensor computations -with Vespa. Adding this `rank-profile` to the document schema: - -
    -schema track {
    -
    -    document track {
    -
    -        field track_id type string {
    -            indexing: summary | attribute
    -            rank: filter
    -            match: word
    -        }
    -
    -        field title type string {
    -            indexing: summary | index
    -            index: enable-bm25
    -        }
    -
    -        field artist type string {
    -            indexing: summary | index
    -        }
    -
    -        field tags type weightedset<string> {
    -            indexing: summary | attribute
    -            attribute: fast-search
    -        }
    -
    -        field similar type tensor<float>(trackid{}) {
    -            indexing: summary | attribute
    -        }
    -    }
    -
    -    fieldset default {
    -        fields: title, artist
    -    }
    -
    -    document-summary track_id {
    -        summary track_id { }
    -    }
    -
    -    rank-profile personalized {
    -        first-phase {
    -            expression: rawScore(tags)
    -        }
    -    }
    -
    -    rank-profile similar {
    -        inputs {
    -            query(user_liked) tensor<float>(trackid{})
    -        }
    -        first-phase {
    -            expression: sum(attribute(similar) * query(user_liked))
    -        }
    -    }
    -}
    -
    - -Deploy the application again : - -
    - -
    -$ vespa deploy --wait 300 app
    -
    -
    - -The track list of recently played tracks (or liked): - -- `TRQIQMT128E0791D9C` Summer Of '69 by Bryan Adams -- `TRWJIPT128E0791D99` Run To You by Bryan Adams -- `TRGVORX128F4291DF1` Broken Wings by Mr. Mister - -Is represented as the `query(user_liked)` query tensor -
    {% raw %}
    -input.query(user_liked)={{trackid:TRUAXHV128F42694E8 }:1.0,{trackid:TRQIQMT128E0791D9C}:1.0,{trackid:TRGVORX128F4291DF1}:1.0}
    -{% endraw %}
    - -The first query example runs the tensor computation over all tracks using `where true`, notice also -`ranking=similar`, without it, ranking with `nativeRank` would not take into account the query tensor: - -
    - -
    {% raw %}
    -$ vespa query \
    -    'yql=select title, artist, track_id from track where true' \
    -    'input.query(user_liked)={{trackid:TRUAXHV128F42694E8}:1.0,{trackid:TRQIQMT128E0791D9C}:1.0,{trackid:TRGVORX128F4291DF1}:1.0}' \
    -    'ranking=similar' \
    -    'hits=5'
    -{% endraw %}
    -
    - -This query also retrieved some of the previous *liked* tracks. These can be removed -from the result set using the `not` query operator, in YQL represented as `!`. - -
    -where !(track_id in (@userLiked))
    -
    - -The [in query operator](../reference/querying/yql.html#in) -is the most efficient multi-value *filtering* query operator, either -using a positive filter (match if any of the keys matches) or negative filter using `not` -(remove from result if any of the keys matches). - -See more examples in [feature-tuning set filtering](feature-tuning.html#multi-lookup-set-filtering). - -Run query with the `not` filter: - -
    - -
    {% raw %}
    -$ vespa query \
    -    'yql=select title, artist, track_id from track where !(track_id in (@userLiked))' \
    -    'input.query(user_liked)={{trackid:TRQIQMT128E0791D9C}:1.0,{trackid:TRWJIPT128E0791D99}:1.0,{trackid:TRGVORX128F4291DF1}:1.0}' \
    -    'ranking=similar' \
    -    'hits=5' \
    -    'userLiked=TRQIQMT128E0791D9C,TRWJIPT128E0791D99,TRGVORX128F4291DF1'
    -{% endraw %}
    -
    - -Note that the tensor query input format is slightly different from the variable substitution supported for -the multivalued query operators `wand`, `in` and `dotProduct`. -The above query produces the following result: - -
    {% highlight json %}
    -{
    -    "timing": {
    -        "querytime": 0.121,
    -        "summaryfetchtime": 0.004,
    -        "searchtime": 0.125
    -    },
    -    "root": {
    -        "id": "toplevel",
    -        "relevance": 1.0,
    -        "fields": {
    -            "totalCount": 95663
    -        },
    -        "coverage": {
    -            "coverage": 100,
    -            "documents": 95666,
    -            "full": true,
    -            "nodes": 1,
    -            "results": 1,
    -            "resultsFull": 1
    -        },
    -        "children": [
    -            {
    -                "id": "index:tracks/0/83b83fed0f2353b738591b15",
    -                "relevance": 1.1211640238761902,
    -                "source": "tracks",
    -                "fields": {
    -                    "track_id": "TRGJNAN128F42AEEF6",
    -                    "title": "Holding Out For A Hero",
    -                    "artist": "Bonnie Tyler"
    -                }
    -            },
    -            {
    -                "id": "index:tracks/0/be76cb3bc209be6f818c91a7",
    -                "relevance": 1.0151770114898682,
    -                "source": "tracks",
    -                "fields": {
    -                    "track_id": "TRAONMM128F92DF7B0",
    -                    "title": "Africa",
    -                    "artist": "Toto"
    -                }
    -            },
    -            {
    -                "id": "index:tracks/0/074b6b937d0ff7b59710c279",
    -                "relevance": 1.0,
    -                "source": "tracks",
    -                "fields": {
    -                    "track_id": "TRFQRYC12903CD0BB9",
    -                    "title": "Kyrie",
    -                    "artist": "Mr. Mister"
    -                }
    -            },
    -            {
    -                "id": "index:tracks/0/f13697952a0d5eaeb2c43ffc",
    -                "relevance": 0.7835690081119537,
    -                "source": "tracks",
    -                "fields": {
    -                    "track_id": "TRKLIXH128F42766B6",
    -                    "title": "Total Eclipse Of The Heart",
    -                    "artist": "Bonnie Tyler"
    -                }
    -            },
    -            {
    -                "id": "index:tracks/0/1c3ab39c8ffa4fd2ba388b4e",
    -                "relevance": 0.7503079921007156,
    -                "source": "tracks",
    -                "fields": {
    -                    "track_id": "TRAFGCY128F92E5F6C",
    -                    "title": "Hold The Line",
    -                    "artist": "Toto"
    -                }
    -            }
    -        ]
    -    }
    -}
    -{% endhighlight %}
    - -This query retrieves 95,663 documents, and the three tracks previously *liked* were removed from the result. - -The following example filters by a tags query, `tags:popular`, reducing the complexity of the -query as fewer documents gets ranked by the tensor ranking expression: - -
    - -
    {% raw %}
    -$ vespa query \
    -    'yql=select title,artist, track_id from track where tags contains "popular" and !(track_id in (@userLiked))' \
    -    'input.query(user_liked)={{trackid:TRQIQMT128E0791D9C}:1.0,{trackid:TRWJIPT128E0791D99}:1.0,{trackid:TRGVORX128F4291DF1}:1.0}' \
    -    'ranking=similar' \
    -    'hits=5' \
    -    'userLiked=TRQIQMT128E0791D9C,TRWJIPT128E0791D99,TRGVORX128F4291DF1'
    -{% endraw %}
    -
    - -With fewer matches to score using the tensor expression the latency decreases. In this query case -latency is strictly linear with number of matches. One could also use a combination of `wand` for -efficient retrieval and tensor computations for ranking. Notice that `querytime` of the unconstrained search -was around 120 ms, which is on the high side for real-time serving. - -The sparse tensor product can be optimized by adding `attribute: fast-search` to the mapped field tensor. -`attribute: fast-search` is supported for `tensor` fields using mapped dimensions, or mixed tensors using -both mapped and dense dimensions. The cost of doing this is increased memory usage. The schema -with `attribute: fast-search` added to the `similar` tensor field: - -
    -schema track {
    -    document track {
    -
    -        field track_id type string {
    -            indexing: summary | attribute
    -            rank: filter
    -            match: word
    -        }
    -
    -        field title type string {
    -            indexing: summary | index
    -            index: enable-bm25
    -        }
    -
    -        field artist type string {
    -            indexing: summary | index
    -        }
    -
    -        field tags type weightedset<string> {
    -            indexing: summary | attribute
    -            attribute: fast-search
    -        }
    -
    -        field similar type tensor<float>(trackid{}) {
    -            indexing: summary | attribute
    -            attribute: fast-search 
    -        }
    -    }
    -
    -    fieldset default {
    -        fields: title, artist
    -    }
    -
    -    document-summary track_id {
    -        summary track_id { }
    -    }
    -
    -    rank-profile personalized {
    -        first-phase {
    -            expression: rawScore(tags)
    -        }
    -    }
    -
    -    rank-profile similar {
    -        inputs {
    -            query(user_liked) tensor<float>(trackid{})
    -        }
    -        first-phase {
    -            expression: sum(attribute(similar) * query(user_liked))
    -        }
    -    }
    -}
    -
    - -Deploy the application again : - -
    - -
    -$ vespa deploy --wait 300 app
    -
    -
    - -And again, adding `fast-search`, requires a re-start of the searchnode process: - -
    - -
    -$ docker exec vespa vespa-sentinel-cmd restart searchnode
    -
    -
    - -Wait for the searchnode to start by waiting for `status:code:up`: - -
    - -
    -$ curl -s http://localhost:19110/state/v1/health
    -
    -
    - -
    -$ sleep 60
    -
    - -Re-run the tensor ranking query: - -
    - -
    {% raw %}
    -$ vespa query \
    -    'yql=select title,artist, track_id from track where !(track_id in (@userLiked))' \
    -    'input.query(user_liked)={{trackid:TRQIQMT128E0791D9C}:1.0,{trackid:TRWJIPT128E0791D99}:1.0,{trackid:TRGVORX128F4291DF1}:1.0}' \
    -    'ranking=similar' \
    -    'hits=5' \
    -    'userLiked=TRQIQMT128E0791D9C,TRWJIPT128E0791D99,TRGVORX128F4291DF1'
    -{% endraw %}
    -
    - -The `querytime` dropped to 40 ms instead of 120 ms without the `fast-search` option. -See also [performance considerations](feature-tuning.html#tensor-ranking) when using tensor expression. -Vespa supports `int8`, `bfloat16`, `float` and `double` precision cell types. -A tradeoff between speed, accuracy and memory usage. - - -## Multithreaded search and ranking -So far in this guide all search queries and ranking computations have been performed using -single threaded execution. -To enable multithreaded execution, a setting needs to be added to `services.xml`. -Multithreaded search and ranking can improve query latency significantly and make better -use of multi-cpu core architectures. - -The following adds a `tuning` element to `services.xml` overriding -[requestthreads:persearch](../reference/applications/services/content.html#requestthreads-persearch). -The default number of threads used `persearch` is one. - -
    -<?xml version="1.0" encoding="UTF-8"?>
    -<services version="1.0">
    -
    -    <container id="default" version="1.0">
    -        <search/>
    -        <document-api/>
    -    </container>
    -
    -    <content id="tracks" version="1.0">
    -        <engine>
    -            <proton>
    -                <tuning>
    -                    <searchnode>
    -                        <requestthreads>
    -                            <persearch>4</persearch>
    -                        </requestthreads>
    -                    </searchnode>
    -                </tuning>
    -            </proton>
    -        </engine>
    -        <redundancy>1</redundancy>
    -        <documents>
    -            <document type="track" mode="index"></document>
    -        </documents>
    -        <nodes>
    -            <node distribution-key="0" hostalias="node1"></node>
    -        </nodes>
    -    </content>
    -</services>
    -
    - -Deploy the application again : - -
    - -
    -$ vespa deploy --wait 300 app
    -
    -
    - -Changing the global threads per search requires a restart of the `searchnode` process: - -
    - -
    -$ docker exec vespa vespa-sentinel-cmd restart searchnode
    -
    -
    - -Wait for the `searchnode` to start: -
    - -
    -$ curl -s localhost:19110/state/v1/health
    -
    -
    - -
    -$ sleep 60
    -
    - -Then repeat the tensor ranking query: - -
    - -
    {% raw %}
    -$ vespa query \
    -    'yql=select title,artist, track_id from track where !(track_id in (@userLiked))' \
    -    'input.query(user_liked)={{trackid:TRQIQMT128E0791D9C}:1.0,{trackid:TRWJIPT128E0791D99}:1.0,{trackid:TRGVORX128F4291DF1}:1.0}' \
    -    'ranking=similar' \
    -    'hits=5' \
    -    'userLiked=TRQIQMT128E0791D9C,TRWJIPT128E0791D99,TRGVORX128F4291DF1'
    -{% endraw %}
    -
    - -Now, the content node(s) will parallelize the matching and ranking -using multiple search threads and `querytime` drops to about 15 ms. - -The setting in `services.xml` sets the global *persearch* value, -It is possible to tune down the number of threads used for a query with -`rank-profile` overrides using [num-threads-per-search](../reference/schemas/schemas.html#num-threads-per-search). -Note that the per rank-profile setting can only be used to tune the number of threads -to a lower number than the global default. - -Note that increasing `persearch` reduces the maximum number of queries that can execute concurrently, -since the match engine executor has `search / persearch` slots. -See [thread configuration](sizing-search.html#thread-configuration) for details on sizing -the thread pool to avoid this bottleneck. - -This adds a new `rank-profile` `similar-t2` using `num-threads-per-search: 2` instead -of the global 4 setting. It's also possible to set the number of threads in the query request -using [ranking.matching.numThreadsPerSearch](../reference/api/query.html#ranking.matching). - -
    -schema track {
    -
    -    document track {
    -
    -        field track_id type string {
    -            indexing: summary | attribute
    -            rank: filter
    -            match: word
    -        }
    -
    -        field title type string {
    -            indexing: summary | index
    -            index: enable-bm25
    -        }
    -
    -        field artist type string {
    -            indexing: summary | index
    -        }
    -
    -        field tags type weightedset<string> {
    -            indexing: summary | attribute
    -            attribute: fast-search
    -        }
    -
    -        field similar type tensor<float>(trackid{}) {
    -            indexing: summary | attribute
    -            attribute: fast-search 
    -        }
    -    }
    -
    -    fieldset default {
    -        fields: title, artist
    -    }
    -
    -    document-summary track_id {
    -        summary track_id { }
    -    }
    -
    -    rank-profile personalized {
    -        first-phase {
    -            expression: rawScore(tags)
    -        }
    -    }
    -
    -    rank-profile similar {
    -        inputs {
    -            query(user_liked) tensor<float>(trackid{})
    -        }
    -        first-phase {
    -            expression: sum(attribute(similar) * query(user_liked))
    -        }
    -    }
    -
    -    rank-profile similar-t2 inherits similar {
    -        num-threads-per-search: 2
    -    }
    -}
    -
    - -Deploy the application again : - -
    - -
    -$ vespa deploy --wait 300 app
    -
    -
    - -Adding a new rank-profile does not require any restart, repeat the query again, -now using the `similar-t2` profile: - -
    - -
    {% raw %}
    -$ vespa query \
    -    'yql=select title,artist, track_id from track where !(track_id in (@userLiked))' \
    -    'input.query(user_liked)={{trackid:TRQIQMT128E0791D9C}:1.0,{trackid:TRWJIPT128E0791D99}:1.0,{trackid:TRGVORX128F4291DF1}:1.0}' \
    -    'ranking=similar-t2' \
    -    'hits=5' \
    -    'userLiked=TRQIQMT128E0791D9C,TRWJIPT128E0791D99,TRGVORX128F4291DF1'
    -{% endraw %}
    -
    - -By using multiple rank profiles developers can find the sweet-spot -where latency does not improve much by using more threads. Using more threads per search -limits query concurrency as more threads will be occupied -per query. Read more in [Vespa sizing guide:reduce latency with -multithreaded search](sizing-search.html#reduce-latency-with-multi-threaded-per-search-execution). - -## Advanced range search with hitLimit - -Vespa has an advanced query operator that allows selecting the -documents with the k-largest or k-smallest values of a `fast-search` attribute field. - -To demonstrate this query operator, this guide introduces a `popularity` -field. Since the last.fm dataset does not have a real popularity metric, -the number of tags per track is used as a *proxy* of the true track popularity. - -The following script runs through the dataset and -counts the number of tags and creates a Vespa -[partial update](../writing/partial-updates.html) feed operation per track. - -
    -import os
    -import sys
    -import json
    -
    -directory = sys.argv[1]
    -seen_tracks = set() 
    -
    -def process_file(filename):
    -    global seen_tracks
    -    with open(filename) as fp:
    -        doc = json.load(fp)
    -        title = doc['title']
    -        artist = doc['artist']
    -        hash = title + artist
    -        if hash in seen_tracks:
    -            return
    -        else:
    -            seen_tracks.add(hash) 
    -
    -        track_id = doc['track_id']
    -        tags = doc['tags']
    -        tags_dict = dict()
    -        for t in tags:
    -            k,v = t[0],int(t[1])
    -            tags_dict[k] = v
    -        n = len(tags_dict)
    -
    -        vespa_doc = {
    -            "update": "id:music:track::%s" % track_id,
    -                "fields": {
    -                    "popularity": {
    -                        "assign": n
    -                    }
    -                }
    -        }
    -        print(json.dumps(vespa_doc))
    -
    -sorted_files = []
    -for root, dirs, files in os.walk(directory):
    -    for filename in files:
    -        filename = os.path.join(root, filename)
    -        sorted_files.append(filename)
    -sorted_files.sort()
    -for filename in sorted_files:
    -    process_file(filename)
    -
    - -```python -import os -import sys -import json - -directory = sys.argv[1] -seen_tracks = set() - -def process_file(filename): - global seen_tracks - with open(filename) as fp: - doc = json.load(fp) - title = doc['title'] - artist = doc['artist'] - hash = title + artist - if hash in seen_tracks: - return - else: - seen_tracks.add(hash) - - track_id = doc['track_id'] - tags = doc['tags'] - tags_dict = dict() - for t in tags: - k,v = t[0],int(t[1]) - tags_dict[k] = v - n = len(tags_dict) - - vespa_doc = { - "update": "id:music:track::%s" % track_id, - "fields": { - "popularity": { - "assign": n - } - } - } - print(json.dumps(vespa_doc)) - -sorted_files = [] -for root, dirs, files in os.walk(directory): - for filename in files: - filename = os.path.join(root, filename) - sorted_files.append(filename) -sorted_files.sort() -for filename in sorted_files: - process_file(filename) -``` - -With this script, run through the dataset and create the [partial update](../writing/partial-updates.html) feed : - -
    - -
    -$ python3 create-popularity-updates.py lastfm_test > updates.jsonl
    -
    -
    - -Add the `popularity` field defined with `fast-search` to the `track` schema. -Also, a `popularity` rank profile is added, which uses one thread per search: - -
    -schema track {
    -
    -    document track {
    -
    -        field track_id type string {
    -            indexing: summary | attribute
    -            rank: filter
    -            match: word
    -        }
    -
    -        field title type string {
    -            indexing: summary | index
    -            index: enable-bm25
    -        }
    -
    -        field artist type string {
    -            indexing: summary | index
    -        }
    -
    -        field tags type weightedset<string> {
    -            indexing: summary | attribute
    -            attribute: fast-search
    -        }
    -
    -        field similar type tensor<float>(trackid{}) {
    -            indexing: summary | attribute
    -            attribute: fast-search 
    -        }
    -
    -        field popularity type int {
    -            indexing: summary | attribute
    -            attribute: fast-search
    -        }
    -    }
    -
    -    fieldset default {
    -        fields: title, artist
    -    }
    -
    -    document-summary track_id {
    -        summary track_id { }
    -    }
    -
    -    rank-profile personalized {
    -        first-phase {
    -            expression: rawScore(tags)
    -        }
    -    }
    -
    -    rank-profile similar {
    -        inputs {
    -            query(user_liked) tensor<float>(trackid{})
    -        }
    -        first-phase {
    -            expression: sum(attribute(similar) * query(user_liked))
    -        }
    -    }
    -
    -    rank-profile similar-t2 inherits similar {
    -        num-threads-per-search: 2
    -    }
    -
    -    rank-profile popularity {
    -        num-threads-per-search: 1
    -        first-phase {
    -            expression: attribute(popularity)
    -        }
    -    }
    -}
    -
    - -Deploy the application again : - -
    - -
    -$ vespa deploy --wait 300 app
    -
    -
    - -Adding a new field does not require a restart, apply the partial updates by: - -
    - -
    -$ vespa feed -t http://localhost:8080 updates.jsonl
    -
    -
    - -With that feed job completed, it is possible to select the five tracks with the highest popularity by -using the [range()](../reference/querying/yql.html) query operator with -[hitLimit](../reference/querying/yql.html#hitlimit): - -
    - -
    -$ vespa query \
    -    'yql=select track_id, popularity from track where {hitLimit:5,descending:true}range(popularity,0,Infinity)' \
    -    'ranking=popularity'
    -
    -
    - -The search returned 1,352 documents, while we asked for just five. The reason -is that the `hitLimit` annotation for the `range` operator only specifies the lower bound. -Documents that are tied with the same `popularity` value within the 5 largest values are returned. - -The `range()` query operator with `hitLimit` can be used to efficiently implement -*top-k* selection for ranking a subset of the documents in the index. For example, use -the `range` search with `hitLimit` to only run the track -[recommendation tensor computation](#tensor-computations) over the most popular tracks: - -
    - -
    {% raw %}
    -$ vespa query \
    -    'yql=select title,artist, track_id, popularity from track where {hitLimit:5,descending:true}range(popularity,0,Infinity) and !(track_id in (@userLiked))' \
    -    'input.query(user_liked)={{trackid:TRQIQMT128E0791D9C}:1.0,{trackid:TRWJIPT128E0791D99}:1.0,{trackid:TRGVORX128F4291DF1}:1.0}' \
    -    'ranking=similar' \
    -    'hits=5' \
    -    'userLiked=TRQIQMT128E0791D9C,TRWJIPT128E0791D99,TRGVORX128F4291DF1'
    -{% endraw %}
    -
    - -Notice that this query returns 1,349 documents while the `range` search from the previous example returned -1,352 documents. This is due to the `not` filter. - -The range search with `hitLimit` can be used for cases where one wants to select efficiently *top-k* of a -single valued numeric `attribute` with `fast-search`. Some use cases which can be efficiently implemented -by using it: - -- Run ranking computations over the most recent documents -using a `long` to represent a timestamp (e.g., using Unix epoch). -- Compute personalization tensor expressions over pre-selected content, e.g. using popularity. -- Optimize [sorting](../reference/querying/sorting-language.html) queries, instead of sorting a large result, -find the smallest or largest values quickly by using range search with `hitLimit`. -- Autosuggest - see [#25333](https://github.com/vespa-engine/vespa/issues/25333). - -Do note that any other query or filter terms in the query are applied after having found the -top-k documents, so an aggressive filter removing many documents might end up recalling 0 documents. - -This behavior is illustrated with this query: - -
    - -
    -$ vespa query \
    -    'yql=select track_id, popularity from track where {hitLimit:5,descending:true}range(popularity,0,Infinity) and popularity=99'
    -
    -
    - -This query fails to retrieve any documents because the range search finds -1,352 documents where popularity is 100, *and'ing* that -top-k result with the popularity=99 filter constraint ends up with 0 results. - -Using range search query operator with `hitLimit` is practical for search use cases -like auto-complete or [search suggestions](https://github.com/vespa-engine/sample-apps/tree/master/incremental-search/search-suggestions) -where one typically use [match: prefix](../reference/schemas/schemas.html#match) or -n-gram matching using [match: gram](../reference/schemas/schemas.html#match). Limiting the short -few first character searches to include a `hitLimit` range on popularity -can greatly improve the query performance and at the same time match against popular suggestions. -As the user types more characters, the number of matches is greatly reduced, so ranking can focus on more factors -than just the single popularity attribute and increase the `hitLimit`. - -## Match phase limit - early termination -An alternative to `range` search with `hitLimit` is using -early termination with [match-phase](../reference/schemas/schemas.html#match-phase), -which enables early termination of search and `first-phase` ranking -using a document field to determine the search evaluation order. - -Match-phase early termination uses an attribute field during matching and ranking to impact the -order the search and ranking is performed in. -If a query is likely to generate more than `ranking.matchPhase.maxHits` per node the search -will be terminated early and matching and evaluaton of the query will be in the order dictated -by the `ranking.matchPhase.attribute` attribute field. - -Match phase early termination requires using a single-value numeric field with `attribute` -and `fast-search`. See [Match phase query parameters](../reference/api/query.html#ranking.matchPhase). -Match-phase limit cannot terminate early or stop any potential `second-phase` ranking expression, -only matching and `first-phase` ranking, hence the name: *match phase limit*. - -The following enables `matchPhase` early termination with `maxHits` target set to 100: - -
    - -
    -$ vespa query \
    -    'yql=select track_id, popularity from track where true' \
    -    'ranking=popularity' \
    -    'ranking.matchPhase.maxHits=100' \
    -    'ranking.matchPhase.attribute=popularity' \
    -    'hits=2'
    -
    -
    - -Which will produce the following result: - -
    {% highlight json%}
    -{
    -    "timing": {
    -        "querytime": 0.007,
    -        "summaryfetchtime": 0.002,
    -        "searchtime": 0.01
    -    },
    -    "root": {
    -        "id": "toplevel",
    -        "relevance": 1.0,
    -        "fields": {
    -            "totalCount": 1476
    -        },
    -        "coverage": {
    -            "coverage": 0,
    -            "documents": 252,
    -            "degraded": {
    -                "match-phase": true,
    -                "timeout": false,
    -                "adaptive-timeout": false,
    -                "non-ideal-state": false
    -            },
    -            "full": false,
    -            "nodes": 1,
    -            "results": 1,
    -            "resultsFull": 0
    -        },
    -        "children": [
    -            {
    -                "id": "index:tracks/0/63f963f1f9372275e12d9e9c",
    -                "relevance": 100.0,
    -                "source": "tracks",
    -                "fields": {
    -                    "track_id": "TRGCNGP12903CFA2BA",
    -                    "popularity": 100
    -                }
    -            },
    -            {
    -                "id": "index:tracks/0/7a74f1cd064acef348a1a701",
    -                "relevance": 100.0,
    -                "source": "tracks",
    -                "fields": {
    -                    "track_id": "TRFVTTT128F930D148",
    -                    "popularity": 100
    -                }
    -            }
    -        ]
    -    }
    -}
    -{% endhighlight %}
    -In this case, totalCount became 1,476, a few more than the `range` search with `hitLimit`. Notice -also the presence of `coverage:degraded` - this informs the client that this result was not fully evaluated -over all matched documents. Read more about [graceful result degradation](graceful-degradation.html). -Note that the example uses the `popularity` rank-profile which was configured with one -thread per search, for low settings of `maxHits`, this is the recommended setting. - -
    -rank-profile popularity {
    -    num-threads-per-search: 1
    -    first-phase {
    -        expression: attribute(popularity)
    -    }
    -}
    -
    - -The core difference from capped range search is that `match-phase` is safe, as filters work inline -with the search and are not applied after finding the top-k documents. - -This query does not trigger match-phase early termination because there -are few hits matching the query: - -
    - -
    -$ vespa query \
    -    'yql=select track_id, popularity from track where popularity=99' \
    -    'ranking=popularity' \
    -    'ranking.matchPhase.maxHits=100' \
    -    'ranking.matchPhase.attribute=popularity' \
    -    'hits=2'
    -
    -
    - -Generally, prefer `match-phase` early termination over `range` search with `hitLimit`. -Match phase limiting can also be used in combination with text search queries: - -
    - -
    -$ vespa query \
    -    'yql=select title, artist, popularity from track where userQuery()' \
    -    'query=love songs' \
    -    'type=any' \
    -    'ranking=popularity' \
    -    'ranking.matchPhase.maxHits=100' \
    -    'ranking.matchPhase.attribute=popularity' \
    -    'hits=2'
    -
    -
    - -Since this query uses `type=any` the above query retrieves a lot more documents than -the target `matchPhase.maxHits`, so early termination is triggered, which will then cause -the search core to match and rank tracks with the highest popularity. - -Early termination using match-phase limits is a powerful feature -that can keep latency and cost in check for many large scale serving use cases -where a document quality signal is available. -Match phase termination also supports specifying a result diversity constraint. -See [Result diversification blog post](https://blog.vespa.ai/result-diversification-with-vespa/). -Note that result diversity is normally obtained with Vespa [result grouping](../querying/grouping.html), -the match-phase diversity is used to ensure that diverse hits are also collected **if** -early termination kicks in. - -## Advanced query tracing - -This section introduces query tracing. Tracing helps understand where time (and cost) is spent, and how -to best optimize the query or schema settings. Query tracing can be enabled using the following parameters: - -- [trace.level](../reference/api/query.html#trace.level) -- [trace.explainLevel](../reference/api/query.html#trace.explainlevel) -- [trace.timestamps](../reference/api/query.html#trace.timestamps) - -A simple example query with tracing enabled: - -
    - -
    -$ vespa query 'yql=select track_id from track where tags contains "rock"' \
    -  'trace.level=3' 'trace.timestamps=true' 'trace.explainLevel=1' 'hits=1'
    -
    -
    - -The first part of the trace traces the query through the stateless container search -chain. For each searcher invoked in the chain a timestamp relative to the start of the query request -is emitted: -
    {% highlight json%}
    -{
    -    "trace": {
    -        "children": [
    -            {
    -                "message": "Using query profile 'default' of type 'root'"
    -            },
    -            {
    -                "message": "Invoking chain 'vespa' [com.yahoo.search.querytransform.WeakAndReplacementSearcher@vespa -> com.yahoo.prelude.statistics.StatisticsSearcher@native -> ... -> federation@native]"
    -            },
    -            {
    -                "children": [
    -                {
    -                    "timestamp": 0,
    -                    "message": "Invoke searcher 'com.yahoo.search.querytransform.WeakAndReplacementSearcher in vespa'"
    -                }]
    -            }
    -        ]
    -    }
    -}
    -{% endhighlight %}
    - -The trace runs all the way to the query is dispatched to the content node(s) and the merged response -is returned up to the client. - -
    {% highlight json%}
    -{
    -    "timestamp": 2,
    -    "message": "sc0.num0 search to dispatch: query=[tags:rock] timeout=9993ms offset=0 hits=1 restrict=[track]"
    -}
    -{% endhighlight %}
    - -In this case, with tracing it has taken 2ms of processing in the stateless container, -before the query is about to be put on the wire on its way -to the content nodes. - -The first protocol phase is the next trace message. -In this case the reply, is ready read from the wire at timestamp 6, -so approximately 4 ms was spent in the first protocol matching phase, -including network serialization and deserialization. -
    {% highlight json%}
    -{
    -    "timestamp": 6,
    -    "message": [
    -        {
    -            "start_time": "2022-03-27 15:03:20.769 UTC",
    -            "traces": [
    -
    -            ],
    -            "distribution-key": 0,
    -            "duration_ms": 1.9814
    -        }
    -    ]
    -}
    -{% endhighlight %}
    -Inside this message is the content node traces of the query, `timestamp_ms` is relative to the start of the query -on the content node. In this case, the content node uses 1.98 ms to evaluate the first protocol phase -of the query (`duration_ms`). - -More explanation of the content node `traces` is coming soon. It includes information like -- How much time was spent traversing the dictionary and setting up the query. -- How much time was spent on matching and first-phase ranking. -- How much time was spent on second-phase ranking (if enabled). - -These traces can help guide both feature tuning decisions and [scaling and sizing](sizing-search.html). - -Later in the trace one can also see the second query protocol phase, which is the summary fill: -
    {% highlight json%}
    -{
    -    "timestamp": 7,
    -    "message": "sc0.num0 fill to dispatch: query=[tags:rock] timeout=9997ms offset=0 hits=1 restrict=[track] summary=[null]"
    -}
    -{% endhighlight %}
    - -And finally an overall breakdown of the two phases: - -
    {% highlight json%}
    -{
    -    "timestamp": 9,
    -    "message": "Query time query 'tags:rock': 7 ms"
    -}
    -{
    -    "timestamp": 9,
    -    "message": "Summary fetch time query 'tags:rock': 2 ms"
    -}
    -{% endhighlight %}
    - -Also try the [Trace Visualizer](https://github.com/vespa-engine/vespa/tree/master/client/js/app#trace-visualizer) -for a flame-graph of the query trace. - - -## Tear down the container -This concludes this tutorial. The following removes the container and the data: -
    - -
    -$ docker rm -f vespa
    -
    -
    - - diff --git a/en/performance/practical-search-performance-guide.mdx b/en/performance/practical-search-performance-guide.mdx new file mode 100644 index 0000000000..eb072bb3e0 --- /dev/null +++ b/en/performance/practical-search-performance-guide.mdx @@ -0,0 +1,1907 @@ +--- +title: "Vespa query performance - a practical guide" +sidebarTitle: "Practical performance guide" +--- + +This is a practical Vespa query performance guide. It uses the [Last.fm](http://millionsongdataset.com/lastfm/) tracks dataset to illustrate Vespa query performance. Latency numbers mentioned in the guide are obtained from running this guide on a MacBook Pro x86. + +This guide covers the following query serving performance aspects: + + + + + + + + + + + + + + +The guide includes step-by-step instructions on how to reproduce the experiments. This guide is best read after having read the [Vespa Overview](/en/learn/overview) documentation first. + + +**Prerequisites:** + +- Linux, macOS or Windows 10 Pro on x86\_64 or arm64, with [Podman Desktop](https://podman.io/) or [Docker Desktop](https://www.docker.com/products/docker-desktop/) installed, with an engine running. + - Alternatively, start the Podman daemon: + + ```bash + $ podman machine init --memory 6000 + $ podman machine start + ``` + - See [Docker Containers](/en/operations/self-managed/docker-containers) for system limits and other settings. +- For CPUs older than Haswell (2013), see [CPU Support](/en/operations/self-managed/cpu-support). +- Memory: Minimum 4 GB RAM dedicated to Docker/Podman. [Memory recommendations](/en/operations/self-managed/node-setup#memory-settings). +- Disk: Avoid `NO_SPACE` - the vespaengine/vespa container image + headroom for data requires disk space. [Read more](/en/writing/feed-block). +- [Homebrew](https://brew.sh/) to install the [Vespa CLI](/en/clients/vespa-cli), or download the Vespa CLI from [Github releases](https://github.com/vespa-engine/vespa/releases). +- Python3 for converting the dataset to Vespa JSON. +- `curl` to download the dataset and run the Vespa health-checks. + + +## Installing vespa-cli + +This tutorial uses [Vespa-CLI](/en/clients/vespa-cli), Vespa CLI is the official command-line client for Vespa.ai. It is a single binary without any runtime dependencies and is available for Linux, macOS and Windows. + +```bash +$ brew install vespa-cli +``` + +## Dataset + +This guide uses the [Last.fm](http://millionsongdataset.com/lastfm/) tracks dataset. Note that the dataset is released under the following terms: + +> *Research only, strictly non-commercial. For details, or if you are unsure, please contact Last.fm. Also, Last.fm has the right to advertise and refer to any work derived from the dataset.* + +To download the dataset directly (120 MB zip file), run: + +```bash +$ curl -L -o lastfm_test.zip \ + http://millionsongdataset.com/sites/default/files/lastfm/lastfm_test.zip +$ unzip lastfm_test.zip +``` + +The downloaded data needs to be converted to [the JSON format expected by Vespa](/en/reference/schemas/document-json-format). + +This [python](https://www.python.org/) script is used to traverse the dataset files and create a JSONL formatted feed file with Vespa feed operations. The schema for this feed is introduced in the next sections. + + +Paste the above into file create-vespa-feed.py + + +```python expandable +import os +import sys +import json +import unicodedata + +directory = sys.argv[1] +seen_tracks = set() + +def remove_control_characters(s): + return "".join(ch for ch in s if unicodedata.category(ch)[0]!="C") + +def process_file(filename): + global seen_tracks + with open(filename) as fp: + doc = json.load(fp) + title = doc['title'] + artist = doc['artist'] + hash = title + artist + if hash in seen_tracks: + return + else: + seen_tracks.add(hash) + + track_id = doc['track_id'] + tags = doc['tags'] + tags_dict = dict() + for t in tags: + k,v = t[0],int(t[1]) + tags_dict[k] = v + similars = doc['similars'] + tensor_cells = [] + keys_seen = dict() + for s in similars: + k,v = s[0],float(s[1]) + if k in keys_seen: + continue + else: + keys_seen[k] = 1 + cell = { + "address": { + "trackid": k + }, + "value": v + } + tensor_cells.append(cell) + + vespa_doc = { + "put": "id:music:track::%s" % track_id, + "fields": { + "title": remove_control_characters(title), + "track_id": track_id, + "artist": remove_control_characters(artist), + "tags": tags_dict, + "similar": { + "cells": tensor_cells + } + } + } + print(json.dumps(vespa_doc)) + +sorted_files = [] +for root, dirs, files in os.walk(directory): + for filename in files: + filename = os.path.join(root, filename) + sorted_files.append(filename) +sorted_files.sort() +for filename in sorted_files: + process_file(filename) +``` + + +Run the script and create the `feed.jsonl` file: + +```bash +$ python3 create-vespa-feed.py lastfm\_test > feed.jsonl +``` + +## Create a Vespa Application Package + +A [Vespa application package](/en/basics/applications) is the set of configuration files and Java plugins that together define the behavior of a Vespa system: what functionality to use, the available document types, how ranking will be done, and how data will be processed during feeding and indexing. + +The minimum required files to create the basic search application are `track.sd` and `services.xml`. Create directories for the configuration files: + +```bash +$ mkdir -p app/schemas; mkdir -p app/search/query-profiles/ +``` + +### Schema + +A Vespa [schema](/en/basics/schemas) is a configuration of a document type and ranking and compute specifications. This app use a `track` schema defined as: + +```js expandable +schema track { + + document track { + + field track\_id type string { + indexing: summary | attribute + rank: filter + match: word + } + + field title type string { + indexing: summary | index + index: enable-bm25 + } + + field artist type string { + indexing: summary | index + } + + field tags type weightedset { + indexing: summary | attribute + } + + field similar type tensor(trackid{}) { + indexing: summary | attribute + } + } + + fieldset default { + fields: title, artist + } +} +``` + + +Paste the above into file app/schemas/track.sd + + +Notice that the `track_id` field has : + +- [rank: filter](/en/reference/schemas/schemas#rank). This setting can save resources when matching against the field. +- [match: word](/en/reference/schemas/schemas#match). This is a database-style matching mode, preserving punctuation characters. + +### Services Specification + +The [services.xml](/en/reference/applications/services/services) defines the services that make up the Vespa application — which services to run and how many nodes per service. + +```xml + + + + + + + + + + 1 + + + + + + + + +``` + + +Paste the above into file app/services.xml + + +The default [query profile](/en/reference/querying/query-profiles) can be used to override default query api settings for all queries. + +The following enables [presentation.timing](/en/reference/api/query#presentation.timing) and renders `weightedset` fields as JSON maps. + +```xml + + true + true + +``` + + +Paste the above into file app/search/query-profiles/default.xml + + +## Deploy the application package + +The application package can now be deployed to a running Vespa instance. See also the [Vespa quick start guide](/en/basics/deploy-an-application-local). + +Start the Vespa container image using Docker: + +```bash +$ docker run --detach --name vespa --hostname vespa-container \ + --publish 8080:8080 --publish 19071:19071 --publish 19110:19110 \ + vespaengine/vespa +``` + +Starting the container can take a short while. Before continuing, make sure that the configuration service is running by using `vespa status deploy`. + +```bash +$ vespa config set target local +$ vespa status deploy --wait 300 +``` + +Once ready, the Vespa application can be deployed using the Vespa CLI: + +```bash +$ vespa deploy --wait 300 app +``` + +## Index the dataset + +Feed the feed file generated in the previous section: + +```bash +$ vespa feed -t http://localhost:8080 feed.jsonl +``` + +## Basic text search query performance +The following sections use the Vespa [query api](/en/reference/api/query) and formulate queries using Vespa [query language](/en/querying/query-language). For readability, all query examples are expressed using the [vespa-cli](/en/clients/vespa-cli) command which supports running queries against a Vespa instance. The CLI uses the Vespa http search api internally. Use `vespa query -v` to see the actual http request sent: + +```bash +$ vespa query -v 'yql=select ..' +``` + +The first query uses `where true` to match all `track` documents. It also uses [hits](/en/reference/api/query#hits) to specify how many documents to return in the response: + +```bash +$ vespa query \ + 'yql=select artist, title, track_id, tags from track where true' \ + 'hits=1' +``` + +The [result json output](/en/reference/querying/default-result-format) for this query will look something like this: + +```json expandable +{ + "timing": { + "querytime": 0.009000000000000001, + "summaryfetchtime": 0.001, + "searchtime": 0.011 + }, + "root": { + "id": "toplevel", + "relevance": 1.0, + "fields": { + "totalCount": 95666 + }, + "coverage": { + "coverage": 100, + "documents": 95666, + "full": true, + "nodes": 1, + "results": 1, + "resultsFull": 1 + }, + "children": [ + { + "id": "index:tracks/0/632facf01973795ba294b7d5", + "relevance": 0.0, + "source": "tracks", + "fields": { + "track_id": "TRRRGWV128F92FC7E0", + "title": "Zombies", + "artist": "True Blood" + } + } + ] + } +} +``` + +Observations: + +- The query searched one node (`coverage.nodes`) and the coverage (`coverage.coverage`) was 100%, see [graceful-degradation](/en/performance/graceful-degradation) for more information about the `coverage` element, and Vespa timeout behavior. Vespa's default timeout is 0.5 seconds. +- The query matched a total of 95666 documents (`totalCount`) out of 95666 documents available (`coverage.documents`). + +The response `timing` has three fields. A Vespa query is executed in two protocol phases: + +- Query matching phase which fans the query out from the stateless container to a content group, each node in the group finds the nodes top-k documents and returns k. The stateless container then merges the nodes' k hits each to obtain a globally ordered top-k documents. +- Summary phase which asks the content nodes that produced the global top-k hits for summary data. + +See also [Life of a query in Vespa](/en/performance/sizing-search#life-of-a-query-in-vespa). The `timing` in the response measures the time it takes to execute these two phases: + +- `querytime` - Time to execute the first protocol phase/matching phase. +- `summaryfetchtime` - Time to execute the summary fill protocol phase for the globally ordered top-k hits. +- `searchtime` Is roughly the sum of the above and is close to what a client will observe (except network latency). + +All three metrics are second resolution. Moving on, the following query performs a free text query: + +```bash +$ vespa query \ + 'yql=select artist, title, track_id from track where userQuery()' \ + 'query=total eclipse of the heart' \ + 'hits=1' +``` + +This query request combines YQL [userQuery()](/en/reference/querying/yql#userquery) with Vespa's [simple query language](/en/reference/querying/simple-query-language), the default [query type](/en/reference/api/query#model.type) is using `all`, requiring that all the terms match. + +The above example searches for *total AND eclipse AND of AND the AND heart* in the fieldset `default`, which in the schema includes the `title` and `artist` fields. Since the request did not specify any [ranking](/en/basics/ranking) parameters, the matched documents were ranked by Vespa's default text rank feature: [nativeRank](/en/ranking/nativerank). + +The result output for the above query: + +```json expandable +{ + "timing": { + "querytime": 0.012, + "summaryfetchtime": 0.001, + "searchtime": 0.014 + }, + "root": { + "id": "toplevel", + "relevance": 1.0, + "fields": { + "totalCount": 1 + }, + "coverage": { + "coverage": 100, + "documents": 95666, + "full": true, + "nodes": 1, + "results": 1, + "resultsFull": 1 + }, + "children": [ + { + "id": "index:tracks/0/3f18869c19c25e3ae237702f", + "relevance": 0.13274821039905835, + "source": "tracks", + "fields": { + "track_id": "TRUKHZD128F92DF70A", + "title": "Total Eclipse Of The Heart", + "artist": "Bonnie Tyler" + } + } + ] + } +} +``` + +This query only matched one document because the query terms were ANDed. Matching can be relaxed to `type=any` instead using [query model type](/en/reference/api/query#model.type). + +```bash +$ vespa query \ + 'yql=select artist, title, track_id from track where userQuery()' \ + 'query=total eclipse of the heart' \ + 'hits=1' \ + 'type=any' +``` + +Now, the query matches 24,053 documents and is considerably slower than the previous `all` query. Comparing `querytime` of these two query examples, the one which matches the most documents have the highest `querytime`. In worst case, the search query matches all documents, and without any techniques for early termination or skipping, all matches are exposed to ranking. Query performance is greatly impacted by the number of documents that matches the query specification. Generally, type `any` queries requires more query compute resources than type `all`. + +There is an algorithmic optimization available for `type=any` queries, using the `weakAnd` query operator which implements the WAND algorithm. See the [using wand with Vespa](/en/ranking/wand) for an introduction to the algorithm. + +Run the same query, but instead of `type=any` use `type=weakAnd`: + +```bash +$ vespa query \ + 'yql=select artist, title, track_id from track where userQuery()' \ + 'query=total eclipse of the heart' \ + 'hits=1' \ + 'type=weakAnd' +``` + +Compared to the type `any` query which fully ranked 24,053 documents, `weakAnd` only fully ranks 3,679 documents. Also notice that the faster search returns the same document at the first position. Conceptually a search query is about finding the documents that match the query, then score the documents using a ranking model. In the worst case a search query can match all documents, which will expose all of them to the ranking. + +## Hits and summaries + +The previous examples used `hits=1` query parameter, and in the previous query examples the `summaryfetchtime` has been close to constant. + +The following query requests considerably more hits, note that the result is piped to `head` to increase readability: + +```bash +$ vespa query \ + 'yql=select artist, title, track_id from track where userQuery()' \ + 'query=total eclipse of the heart' \ + 'hits=200' \ + 'type=weakAnd' |head -40 +``` + +Increasing number of hits increases `summaryfetchtime` significantly from the previous query examples, while `querytime` is relatively unchanged. Repeating the query a second time will reduce the `summaryfetchtime` due to the content node summary cache, see [caches in Vespa](/en/performance/caches-in-vespa) for details. + +There are largely four factors which determines the `summaryfetchtime`: + +- The number of hits requested and number of content nodes that produced the query result which makes up the `querytime`. With many content nodes in the group the query was dispatched to, we expect that top-ranking hits would be distributed across the nodes, so that each node does less work. +- The network package size of each hit. Returning hits with larger fields costs more resources and higher `summaryfetchtime` than smaller docs. +- The summary used with the query, and which fields go into the summary. For example, a [document-summary](/en/querying/document-summaries) which only contain fields that are defined as `attribute` will be read from memory. For the `default` summary, or others containing at least one non-attribute field, a fill will potentially access data from summary storage on disk. Read more about in-memory [attribute](/en/content/attributes) fields. +- [summary-features](/en/reference/schemas/schemas#summary-features) used to return computed [rank features](/en/reference/ranking/rank-features) from the content nodes. + +Creating a dedicated [document-summary](/en/querying/document-summaries) which only contain the `track_id` field can improve performance, since `track_id` is defined in the schema with `attribute`, any summary fetches using this document summary will be reading in-memory data. In addition, since the summary only contain one field, it saves network time as less data is transferred during the summary fill phase. + +```js +document-summary track_id { + summary track_id { } +} +``` + +The new schema then becomes: + +```js expandable +schema track { + + document track { + + field track_id type string { + indexing: summary | attribute + rank: filter + match: word + } + + field title type string { + indexing: summary | index + index: enable-bm25 + } + + field artist type string { + indexing: summary | index + } + + field tags type weightedset { + indexing: summary | attribute + } + + field similar type tensor(trackid{}) { + indexing: summary | attribute + } + } + + fieldset default { + fields: title, artist + } + + document-summary track_id { + summary track_id { } + } +} +``` + + +Paste the above into file app/schemas/track.sd + + +Re-deploy the application: + +```bash +$ vespa deploy --wait 300 app +``` + +Re-executing the query using the `track_id` `document-summary` is done by setting the [summary](/en/reference/api/query#presentation.summary) query request parameter: + +```bash +$ vespa query \ + 'yql=select artist, title, track_id from track where userQuery()' \ + 'query=total eclipse of the heart' \ + 'hits=200' \ + 'type=weakAnd' \ + 'summary=track_id' |head -40 +``` + +In this particular case the `summaryfetchtime` difference is not that large, but for larger number of hits and larger documents the difference is significant. Especially in single content node deployments. + +A note on select field scoping with YQL, e.g. `select title, track_id from ..`. When using the default summary by not using a summary parameter, all fields are delivered from the content nodes to the stateless search container in the summary fill phase, regardless of field scoping. The search container removes the set of fields not selected and renders the result. Hence, select scoping only reduces the amount of data transferred back to the client, and does not impact or optimize the performance of the internal communication and potential summary cache miss. For optimal performance for use cases asking for large number of hits to the client it is recommended to use dedicated document summaries. Note also that Vespa per default limits the max hits to 400 per default, the behavior can be overridden in the [default queryProfile](/en/reference/api/query#queryprofile). + +When requesting large amounts of data, consider how to reduce response size. Vespa supports gzip compression if the HTTP client uses the [Accept-Encoding](https://www.rfc-editor.org/rfc/rfc9110.html#name-accept-encoding) HTTP request header: + +```text +Accept-Encoding: gzip +``` + +Compression reduces data transfer but adds CPU overhead. The lowest latency is achieved without compression if network bandwidth is sufficient. [CBOR format](/en/reference/api/query#presentation.format) (`format=cbor` or `Accept: application/cbor`) is both more compact and faster to generate than JSON, especially for numeric data such as tensors and embeddings. CBOR can also be combined with gzip compression. CBOR is a drop-in replacement for JSON - when deserialized, the result is identical. + +## Searching attribute fields + +The previous section covered free text searching in a `fieldset` containing fields with `indexing:index`. See [indexing reference](/en/reference/schemas/schemas#indexing). Fields of [type string](/en/reference/schemas/schemas#field) are treated differently depending on having `index` or `attribute`: + +- `index` integrates with [linguistic](/en/linguistics/linguistics) processing and is matched using [match:text](/en/reference/schemas/schemas#match). +- `attribute` does not integrate with linguistic processing and is matched using [match:word](/en/reference/schemas/schemas#match). + +With `index` Vespa builds inverted index data structures which roughly consist of: + +- A dictionary of the unique text tokens (after linguistic processing) +- Posting lists for each unique text token in the collection. Posting lists comes in different formats, and using `rank: filter` can help guide the decision on what format to use. Bitvector representation is the most compacting post list representation. + +With `attribute` Vespa will per default not build any inverted index-like data structures for potential faster query evaluation. See [Wikipedia:Inverted Index](https://en.wikipedia.org/wiki/Inverted_index) and [Vespa internals](/en/content/proton#index). The reason for this default setting is that Vespa `attribute` fields can be used for many different aspects: [ranking](/en/basics/ranking), [result grouping](/en/querying/grouping), [result sorting](/en/reference/querying/sorting-language), and finally searching/matching. + +The following section focuses on the `tags` field which we defined with `attribute`, matching in this field will be performed using `match:word` which is the default match mode for string fields with `indexing: attribute`. The `tags` field is of type [weightedset](/en/reference/schemas/schemas#weightedset). + +```js + field tags type weightedset { + indexing: summary | attribute + } +``` + +`weightedset` is a field type that allows representing a tag with an integer weight, which can be used for ranking. In this case, there is no inverted index structure, and matching against the `tags` field is performed as a linear scan. The following scans for documents where `tags` match *rock*: + +```bash +$ vespa query \ + 'yql=select track_id, tags from track where tags contains "rock"' \ + 'hits=1' +``` + +The query matches 8,160 documents, notice that for `match: word`, matching can also include whitespace, or generally punctuation characters which are removed and not searchable when using `match:text` with string fields that have `index`: + +```bash +$ vespa query \ + 'yql=select track_id, tags from track where tags contains "classic rock"' \ + 'hits=1' +``` + +The above query matches exactly tags with "classic rock", not "rock" and also not "classic rock music". + +Another query searching for *rock* or *pop*: + +```bash +$ vespa query \ + 'yql=select track_id, tags from track where tags contains "rock" or tags contains "pop"' \ + 'hits=1' +``` + +In all these examples searching the `tags` field, the matching is done by a linear scan through all `track` documents. The `tags` search can be combined with regular free text query terms searching fields that do have inverted index structures: + +```bash +$ vespa query \ + 'yql=select track_id, tags from track where tags contains "rock" and userQuery()' \ + 'hits=1' \ + 'query=total eclipse of the heart' +``` + +In this case - the query terms searching the default fieldset will restrict the number of documents that needs to be scanned for the *tags* constraint. This query is automatically optimized by the Vespa query planner. + +### Searching attribute fields using fast-search + +This section adds `fast-search` to the `tags` field to speed up searches where there are no other query filters which restricts the search. The schema with `fast-search`: + +```js expandable +schema track { + + document track { + + field track_id type string { + indexing: summary | attribute + rank: filter + match: word + } + + field title type string { + indexing: summary | index + index: enable-bm25 + } + + field artist type string { + indexing: summary | index + } + + field tags type weightedset { + indexing: summary | attribute + attribute: fast-search + } + + field similar type tensor(trackid{}) { + indexing: summary | attribute + } + } + + fieldset default { + fields: title, artist + } + + document-summary track_id { + summary track_id { } + } +} +``` + + +Paste the above into file app/schemas/track.sd + + +Re-deploy the application: + +```bash +$ vespa deploy --wait 300 app +``` + +The above will print a WARNING: + +```bash +vespa deploy --wait 300 app/ +Uploading application package ... done + +Success: Deployed app/ +WARNING Change(s) between active and new application that require restart: +In cluster 'tracks' of type 'search': + Restart services of type 'searchnode' because: + 1) Document type 'track': Field 'tags' changed: add attribute 'fast-search' + +Waiting up to 300 seconds for query service to become available ... +``` + +To enable `fast-search`, content node(s) needs to be restarted to re-build the fast-search data structures for the attribute. + +The following uses [vespa-sentinel-cmd command tool](/en/reference/operations/self-managed/tools#vespa-sentinel-cmd) to restart the searchnode process: + +```bash +$ docker exec vespa vespa-sentinel-cmd restart searchnode +``` + +This step requires waiting for the searchnode, use the [health state api](/en/reference/operations/metrics#health-state-api): +```bash +$ curl -s http://localhost:19110/state/v1/health +``` + +Wait for status code to flip to `up` before querying again: + +```json +{ + "status": { + "code": "up" + } +} +``` + +Once up, execute the `tags` query again: + +```bash +$ vespa query \ + 'yql=select track_id, tags from track where tags contains "rock" or tags contains "pop"' \ + 'hits=1' +``` + +Now the `querytime` will be a few milliseconds, since Vespa has built index structures to support `fast-search` in the attribute. The downside of enabling `fast-search` is increased memory usage and slightly reduced indexing throughput. See also [when to use fast-search for attributes](/en/performance/feature-tuning#when-to-use-fast-search-for-attribute-fields). + +For use cases requiring `match:text` when searching multivalued string field types like [weightedset](/en/reference/schemas/schemas#weightedset), see [searching multi-value fields](/en/querying/searching-multivalue-fields). + +For fields that don't need any match ranking features, it's strongly recommended to use [rank: filter](/en/reference/schemas/schemas#rank). + +```js +field availability type int { + indexing: summary | attribute + rank: filter + attribute { + fast-search + } +} +``` + +With the settings above, bit vector posting list representations are used. This is especially efficient when used in combination with [TAAT (term at a time)](/en/performance/feature-tuning#hybrid-taat-daat) query evaluation. For some cases with many query terms, enabling `rank: filter` can reduce match latency by 75%. + +## Multi-valued query operators + +This section covers [multi-value query operators](/en/ranking/multivalue-query-operators) and their query performance characteristics. Many real-world search and recommendation use cases involve structured multivalued queries. + +Assuming a process has learned a sparse user profile representation, which, for a given user, based on past interactions with a service, could produce a user profile with *hard rock*, *rock*, *metal* and *finnish metal*. Sparse features from a fixed vocabulary/feature space. + +Retrieving and ranking using sparse representations can be done using the dot product between the sparse user profile representation and document representation. In the track example, the `tags` field could be the document side sparse representation. Each document is tagged with multiple `tags` using a weight, and similar the sparse user profile representation could use weights. + +In the following examples, the [dotProduct()](/en/reference/querying/yql#dotproduct) and [wand()](/en/reference/querying/yql#wand) query operators are used. + +To configure [ranking](/en/basics/ranking), add a `rank-profile` to the schema: + +```js expandable +schema track { + + document track { + + field track_id type string { + indexing: summary | attribute + rank: filter + match: word + } + + field title type string { + indexing: summary | index + index: enable-bm25 + } + + field artist type string { + indexing: summary | index + } + + field tags type weightedset { + indexing: summary | attribute + attribute: fast-search + } + + field similar type tensor(trackid{}) { + indexing: summary | attribute + } + } + + fieldset default { + fields: title, artist + } + + document-summary track_id { + summary track_id { } + } + + rank-profile personalized { + first-phase { + expression: rawScore(tags) + } + } +} +``` + + +Paste the above into file app/schemas/track.sd + + +The `dotProduct`and `wand` query operators produce a `rank feature` called [rawScore(name)](/en/reference/ranking/rank-features#rawScore\(field\)). This feature calculates the sparse dot product between the query and document weights. + +Deploy the application again: + +```bash +$ vespa deploy --wait 300 app +``` + +The [dotProduct](/en/reference/querying/yql#dotproduct) query operator accepts a field to match over and supports [parameter substitution](/en/reference/querying/yql#parameter-substitution). Using substitution is recommended for large inputs as it saves compute resources when parsing the YQL input. + +The following example assumes a learned sparse representation, with equal weight: + +```js +userProfile={"hard rock":1, "rock":1,"metal":1, "finnish metal":1} +``` + +This userProfile is referenced as a parameter + +```text +where dotProduct(tags, @userProfile) +``` + +```bash +$ vespa query \ + 'yql=select track_id, title, artist, tags from track where dotProduct(tags, @userProfile)' \ + 'userProfile={"hard rock":1, "rock":1,"metal":1, "finnish metal":1}' \ + 'hits=1' \ + 'ranking=personalized' +``` + +The query also specifies the `rank-profile` `personalized`, if not specified, ranking would be using `nativeRank`. The above query returns the following response: + +```json expandable +{ + "timing": { + "querytime": 0.004, + "summaryfetchtime": 0.001, + "searchtime": 0.006 + }, + "root": { + "id": "toplevel", + "relevance": 1.0, + "fields": { + "totalCount": 10323 + }, + "coverage": { + "coverage": 100, + "documents": 95666, + "full": true, + "nodes": 1, + "results": 1, + "resultsFull": 1 + }, + "children": [ + { + "id": "index:tracks/0/74d3f4df2989650b2cc095be", + "relevance": 400.0, + "source": "tracks", + "fields": { + "track_id": "TRJTBAO128F932191C", + "title": "Vastarannan valssi", + "artist": "Viikate", + "tags": { + "Suomi": 100, + "rautalanka": 100, + "suomi rock": 100, + "hard rock": 100, + "melodic metal": 100, + "finnish": 100, + "finnish metal": 100, + "metal": 100, + "rock": 100 + } + } + } + ] + } +} +``` + +Notice that the query above will brute-force rank all tracks where the `tags` field matches *any* of the multivalued userProfile features. Due to this, the query ranks 10,323 tracks as seen by `totalCount`. Including for example *pop* in the userProfile list increases the number of hits to 13,638. + +For a large user profile with many learned features/tags, one would easily match and rank the entire document collection. Also notice the `relevance` score which is 400, since the document matches all the query input tags (4x100 = 400). + +To optimize the evaluation the [wand query operator](/en/reference/querying/yql#wand) can be used. The `wand` query operator supports setting a target number of top ranking hits that gets exposed to the `first-phase` ranking function. + +Repeating the query from above, replacing `dotProduct` with `wand`: + +```bash +$ vespa query \ + 'yql=select track_id, title, artist, tags from track where {totalTargetHits:10}wand(tags, @userProfile)' \ + 'userProfile={"hard rock":1, "rock":1,"metal":1, "finnish metal":1}' \ + 'hits=1' \ + 'ranking=personalized' +``` + +The `wand` query operator retrieves the exact same hit at rank 1, which is the expected behavior. The `wand` query operator is safe, meaning it returns the same top-k results as the `dotProduct` query operator. + +For larger document collections, the *wand* query operator can significantly improve query performance compared to `dotProduct`. + +*wand* is a query operator which performs matching and ranking interleaved and skips documents which cannot make it into the top-k results. [Using wand with Vespa](/en/ranking/wand) guide has more details on the WAND algorithm. + +Finally, these multi-value query operators work on both single-valued fields and array fields, but optimal performance is achieved using the [weightedset](/en/reference/schemas/schemas#weightedset) field type. The `weightedset` field type only supports integer weights. The next section covers tensors that support more floating point number types. + +## Tensor computations + +The previous sections covered matching and ranking where query matching query operators also produced rank features which could be used to influence the order of the hits returned. In this section we look at ranking with [tensor computations](/en/ranking/tensor-examples) using [tensor expressions](/en/ranking/tensor-user-guide). + +Tensor computations can be used to calculate dense dot products, sparse dot products, matrix multiplication, neural networks and more. Tensor computations can be performed on documents that are retrieved by the query matching operators. The only exception to this is dense single order tensors (vectors) where Vespa also supports "matching" using [(approximate) nearest neighbor search](/en/querying/approximate-nn-hnsw). + +The `track` schema was defined with a `similar` tensor field with one named *mapped* dimension. *Mapped* tensors can be used to represent sparse feature representations, similar to the `weightedset` field, but in a more generic way, and here using `float` to represent the tensor cell value. + +```js +field similar type tensor(trackid{}) { + indexing: summary | attribute +} +``` + +Inspecting one document, using the vespa-cli (Wraps [Vespa document/v1 api](/en/writing/document-v1-api-guide)): + +```bash +$ vespa document get id:music:track::TRQIQMT128E0791D9C +``` + +Returns: + +```json expandable +{ + "pathId": "/document/v1/music/track/docid/TRUAXHV128F42694E8", + "id": "id:music:track::TRUAXHV128F42694E8", + "fields": { + "artist": "Bryan Adams", + "title": "Summer Of '69", + "similar": { + "cells": [ + { + "address": { + "trackid": "TRWJIPT128E0791D99" + }, + "value": 1.0 + }, + { + "address": { + "trackid": "TRKPGHH128F1453DD0" + }, + "value": 0.9129049777984619 + }, + { + "address": { + "trackid": "TRGVORX128F4291DF1" + }, + "value": 0.3269079923629761 + } + ] + }, + "tags": { + "All time favourites": 1, + "male vocalists": 7, + "singer-songwriter": 6, + "happy": 2, + "Driving": 3, + "classic rock": 59, + "loved": 1, + "Energetic": 2, + "male vocalist": 1, + "dance": 1, + "soft rock": 2, + "1980s": 1 + } + } +} +``` + +In the lastfm collection, each track lists similar tracks with a similarity score using float resolution, according to this similarity algorithm the most similar track to this sample document is `TRWJIPT128E0791D99`, with a similarity score of 1.0. + +Searching for that doc using the query api: + +```bash +$ vespa query \ + 'yql=select title, artist from track where track_id contains "TRWJIPT128E0791D99"' \ + 'hits=1' +``` + +Note that `track_id` was not defined with `fast-search`, so searching it without any other query terms makes this query a linear scan over all tracks. + +The query returns: + +```json expandable +{ + "timing": { + "querytime": 0.01, + "summaryfetchtime": 0.002, + "searchtime": 0.013000000000000001 + }, + "root": { + "id": "toplevel", + "relevance": 1.0, + "fields": { + "totalCount": 1 + }, + "coverage": { + "coverage": 100, + "documents": 95666, + "full": true, + "nodes": 1, + "results": 1, + "resultsFull": 1 + }, + "children": [ + { + "id": "index:tracks/0/63eadd770a5dfde1f111aed7", + "relevance": 0.0017429193899782135, + "source": "tracks", + "fields": { + "title": "Run To You", + "artist": "Bryan Adams" + } + } + ] + } +} +``` + +Given a single track, one could just retrieve the document and display the offline computed similar tracks, but, if a user has listened to multiple tracks in a real time session, one could use a sparse dot product between the user recent activity and the track similarity fields. For example, listening to the following tracks: + +- `TRQIQMT128E0791D9C` Summer Of '69 by Bryan Adams +- `TRWJIPT128E0791D99` Run To You by Bryan Adams +- `TRGVORX128F4291DF1` Broken Wings by Mr. Mister + +could be represented as a query tensor `query(user_liked)` and passed with the query request like this: + +```bash +input.query(user_liked)={{trackid:TRUAXHV128F42694E8 }:1.0,{trackid:TRQIQMT128E0791D9C}:1.0,{trackid:TRGVORX128F4291DF1}:1.0} +``` + +Both the document tensor and the query tensor are defined with `trackid{}` as the *named* *mapped* dimension. The sparse tensor dot product can then be expression in a `rank-profile`: + +```js +rank-profile similar { + inputs { + query(user_liked) tensor(trackid{}) + } + first-phase { + expression: sum(attribute(similar) * query(user_liked)) + } +} +``` + +See [tensor user guide](/en/ranking/tensor-user-guide) for more on tensor fields and tensor computations with Vespa. Adding this `rank-profile` to the document schema: + +```js expandable +schema track { + + document track { + + field track_id type string { + indexing: summary | attribute + rank: filter + match: word + } + + field title type string { + indexing: summary | index + index: enable-bm25 + } + + field artist type string { + indexing: summary | index + } + + field tags type weightedset { + indexing: summary | attribute + attribute: fast-search + } + + field similar type tensor(trackid{}) { + indexing: summary | attribute + } + } + + fieldset default { + fields: title, artist + } + + document-summary track_id { + summary track_id { } + } + + rank-profile personalized { + first-phase { + expression: rawScore(tags) + } + } + + rank-profile similar { + inputs { + query(user_liked) tensor(trackid{}) + } + first-phase { + expression: sum(attribute(similar) * query(user_liked)) + } + } +} +``` + + +Paste the above into file app/schemas/track.sd + + +Deploy the application again : + +```bash +$ vespa deploy --wait 300 app +``` + +The track list of recently played tracks (or liked): + +- `TRQIQMT128E0791D9C` Summer Of '69 by Bryan Adams +- `TRWJIPT128E0791D99` Run To You by Bryan Adams +- `TRGVORX128F4291DF1` Broken Wings by Mr. Mister + +Is represented as the `query(user_liked)` query tensor + +```bash +input.query(user_liked)={{trackid:TRUAXHV128F42694E8 }:1.0,{trackid:TRQIQMT128E0791D9C}:1.0,{trackid:TRGVORX128F4291DF1}:1.0} +``` + +The first query example runs the tensor computation over all tracks using `where true`, notice also `ranking=similar`, without it, ranking with `nativeRank` would not take into account the query tensor: + +```bash +$ vespa query \ + 'yql=select title, artist, track_id from track where true' \ + 'input.query(user_liked)={{trackid:TRUAXHV128F42694E8}:1.0,{trackid:TRQIQMT128E0791D9C}:1.0,{trackid:TRGVORX128F4291DF1}:1.0}' \ + 'ranking=similar' \ + 'hits=5 +``` + +This query also retrieved some of the previous *liked* tracks. These can be removed from the result set using the `not` query operator, in YQL represented as `!`. + +```bash +where !(track_id in (@userLiked)) +``` + +The [in query operator](/en/reference/querying/yql#in) is the most efficient multi-value *filtering* query operator, either using a positive filter (match if any of the keys matches) or negative filter using `not` (remove from result if any of the keys matches). + +See more examples in [feature-tuning set filtering](/en/performance/feature-tuning#multi-lookup-set-filtering). + +Run query with the `not` filter: + +```bash +vespa query \ + 'yql=select title, artist, track_id from track where !(track_id in (@userLiked))' \ + 'input.query(user_liked)={{trackid:TRQIQMT128E0791D9C}:1.0,{trackid:TRWJIPT128E0791D99}:1.0,{trackid:TRGVORX128F4291DF1}:1.0}' \ + 'ranking=similar' \ + 'hits=5' \ + 'userLiked=TRQIQMT128E0791D9C,TRWJIPT128E0791D99,TRGVORX128F4291DF1'' +``` + +Note that the tensor query input format is slightly different from the variable substitution supported for the multivalued query operators `wand`, `in` and `dotProduct`. The above query produces the following result: + +```json expandable +{ + "timing": { + "querytime": 0.121, + "summaryfetchtime": 0.004, + "searchtime": 0.125 + }, + "root": { + "id": "toplevel", + "relevance": 1.0, + "fields": { + "totalCount": 95663 + }, + "coverage": { + "coverage": 100, + "documents": 95666, + "full": true, + "nodes": 1, + "results": 1, + "resultsFull": 1 + }, + "children": [ + { + "id": "index:tracks/0/83b83fed0f2353b738591b15", + "relevance": 1.1211640238761902, + "source": "tracks", + "fields": { + "track_id": "TRGJNAN128F42AEEF6", + "title": "Holding Out For A Hero", + "artist": "Bonnie Tyler" + } + }, + { + "id": "index:tracks/0/be76cb3bc209be6f818c91a7", + "relevance": 1.0151770114898682, + "source": "tracks", + "fields": { + "track_id": "TRAONMM128F92DF7B0", + "title": "Africa", + "artist": "Toto" + } + }, + { + "id": "index:tracks/0/074b6b937d0ff7b59710c279", + "relevance": 1.0, + "source": "tracks", + "fields": { + "track_id": "TRFQRYC12903CD0BB9", + "title": "Kyrie", + "artist": "Mr. Mister" + } + }, + { + "id": "index:tracks/0/f13697952a0d5eaeb2c43ffc", + "relevance": 0.7835690081119537, + "source": "tracks", + "fields": { + "track_id": "TRKLIXH128F42766B6", + "title": "Total Eclipse Of The Heart", + "artist": "Bonnie Tyler" + } + }, + { + "id": "index:tracks/0/1c3ab39c8ffa4fd2ba388b4e", + "relevance": 0.7503079921007156, + "source": "tracks", + "fields": { + "track_id": "TRAFGCY128F92E5F6C", + "title": "Hold The Line", + "artist": "Toto" + } + } + ] + } +} +``` + +This query retrieves 95,663 documents, and the three tracks previously *liked* were removed from the result. + +The following example filters by a tags query, `tags:popular`, reducing the complexity of the query as fewer documents gets ranked by the tensor ranking expression: + +```bash +vespa query \ + 'yql=select title,artist, track_id from track where tags contains "popular" and !(track_id in (@userLiked))' \ + 'input.query(user_liked)={{trackid:TRQIQMT128E0791D9C}:1.0,{trackid:TRWJIPT128E0791D99}:1.0,{trackid:TRGVORX128F4291DF1}:1.0}' \ + 'ranking=similar' \ + 'hits=5' \ + 'userLiked=TRQIQMT128E0791D9C,TRWJIPT128E0791D99,TRGVORX128F4291DF1' +``` + +With fewer matches to score using the tensor expression the latency decreases. In this query case latency is strictly linear with number of matches. One could also use a combination of `wand` for efficient retrieval and tensor computations for ranking. Notice that `querytime` of the unconstrained search was around 120 ms, which is on the high side for real-time serving. + +The sparse tensor product can be optimized by adding `attribute: fast-search` to the mapped field tensor. `attribute: fast-search` is supported for `tensor` fields using mapped dimensions, or mixed tensors using both mapped and dense dimensions. The cost of doing this is increased memory usage. The schema with `attribute: fast-search` added to the `similar` tensor field: + +```js expandable +schema track { + document track { + + field track_id type string { + indexing: summary | attribute + rank: filter + match: word + } + + field title type string { + indexing: summary | index + index: enable-bm25 + } + + field artist type string { + indexing: summary | index + } + + field tags type weightedset { + indexing: summary | attribute + attribute: fast-search + } + + field similar type tensor(trackid{}) { + indexing: summary | attribute + attribute: fast-search + } + } + + fieldset default { + fields: title, artist + } + + document-summary track_id { + summary track_id { } + } + + rank-profile personalized { + first-phase { + expression: rawScore(tags) + } + } + + rank-profile similar { + inputs { + query(user_liked) tensor(trackid{}) + } + first-phase { + expression: sum(attribute(similar) * query(user_liked)) + } + } +} +``` + + +Paste the above into file app/schemas/track.sd + + +Deploy the application again : + +```bash +$ vespa deploy --wait 300 app +``` + +And again, adding `fast-search`, requires a re-start of the searchnode process: + +```bash +$ docker exec vespa vespa-sentinel-cmd restart searchnode +``` + +Wait for the searchnode to start by waiting for `status:code:up`: + +```bash +$ curl -s http://localhost:19110/state/v1/health +``` + +Re-run the tensor ranking query: + +```bash +vespa query \ + 'yql=select title,artist, track_id from track where !(track_id in (@userLiked))' \ + 'input.query(user_liked)={{trackid:TRQIQMT128E0791D9C}:1.0,{trackid:TRWJIPT128E0791D99}:1.0,{trackid:TRGVORX128F4291DF1}:1.0}' \ + 'ranking=similar' \ + 'hits=5' \ + 'userLiked=TRQIQMT128E0791D9C,TRWJIPT128E0791D99,TRGVORX128F4291DF1' +``` + +The `querytime` dropped to 40 ms instead of 120 ms without the `fast-search` option. See also [performance considerations](/en/performance/feature-tuning#tensor-ranking) when using tensor expression. Vespa supports `int8`, `bfloat16`, `float` and `double` precision cell types. A tradeoff between speed, accuracy and memory usage. + +## Multithreaded search and ranking + +So far in this guide all search queries and ranking computations have been performed using single threaded execution. To enable multithreaded execution, a setting needs to be added to `services.xml`. Multithreaded search and ranking can improve query latency significantly and make better use of multi-cpu core architectures. + +The following adds a `tuning` element to `services.xml` overriding [requestthreads:persearch](/en/reference/applications/services/content#requestthreads-persearch). The default number of threads used `persearch` is one. + +```xml expandable + + + + + + + + + + + + + + + 4 + + + + + + 1 + + + + + + + + +``` + + +Paste the above into file app/services.xml + + +Deploy the application again : + +```bash +$ vespa deploy --wait 300 app +``` + +Changing the global threads per search requires a restart of the `searchnode` process: + +```bash +$ docker exec vespa vespa-sentinel-cmd restart searchnode +``` + +Wait for the `searchnode` to start: +```bash +$ curl -s localhost:19110/state/v1/health +``` + +Then repeat the tensor ranking query: + +```bash +vespa query \ + 'yql=select title,artist, track_id from track where !(track_id in (@userLiked))' \ + 'input.query(user_liked)={{trackid:TRQIQMT128E0791D9C}:1.0,{trackid:TRWJIPT128E0791D99}:1.0,{trackid:TRGVORX128F4291DF1}:1.0}' \ + 'ranking=similar' \ + 'hits=5' \ + 'userLiked=TRQIQMT128E0791D9C,TRWJIPT128E0791D99,TRGVORX128F4291DF1' +``` + +Now, the content node(s) will parallelize the matching and ranking using multiple search threads and `querytime` drops to about 15 ms. + +The setting in `services.xml` sets the global *persearch* value, It is possible to tune down the number of threads used for a query with `rank-profile` overrides using [num-threads-per-search](/en/reference/schemas/schemas#num-threads-per-search). Note that the per rank-profile setting can only be used to tune the number of threads to a lower number than the global default. + +Note that increasing `persearch` reduces the maximum number of queries that can execute concurrently, since the match engine executor has `search / persearch` slots. See [thread configuration](/en/performance/sizing-search#thread-configuration) for details on sizing the thread pool to avoid this bottleneck. + +This adds a new `rank-profile` `similar-t2` using `num-threads-per-search: 2` instead of the global 4 setting. It's also possible to set the number of threads in the query request using [ranking.matching.numThreadsPerSearch](/en/reference/api/query#ranking.matching). + +```js expandable +schema track { + + document track { + + field track_id type string { + indexing: summary | attribute + rank: filter + match: word + } + + field title type string { + indexing: summary | index + index: enable-bm25 + } + + field artist type string { + indexing: summary | index + } + + field tags type weightedset { + indexing: summary | attribute + attribute: fast-search + } + + field similar type tensor(trackid{}) { + indexing: summary | attribute + attribute: fast-search + } + } + + fieldset default { + fields: title, artist + } + + document-summary track_id { + summary track_id { } + } + + rank-profile personalized { + first-phase { + expression: rawScore(tags) + } + } + + rank-profile similar { + inputs { + query(user_liked) tensor(trackid{}) + } + first-phase { + expression: sum(attribute(similar) * query(user_liked)) + } + } + + rank-profile similar-t2 inherits similar { + num-threads-per-search: 2 + } +} +``` + + +Paste the above into file app/schemas/track.sd + + +Deploy the application again : + +```bash +$ vespa deploy --wait 300 app +``` + +Adding a new rank-profile does not require any restart, repeat the query again, now using the `similar-t2` profile: + +```bash +$ vespa query \ + 'yql=select title,artist, track_id from track where !(track_id in (@userLiked))' \ + 'input.query(user_liked)={{trackid:TRQIQMT128E0791D9C}:1.0,{trackid:TRWJIPT128E0791D99}:1.0,{trackid:TRGVORX128F4291DF1}:1.0}' \ + 'ranking=similar-t2' \ + 'hits=5' \ + 'userLiked=TRQIQMT128E0791D9C,TRWJIPT128E0791D99,TRGVORX128F4291DF1' +``` + +By using multiple rank profiles developers can find the sweet-spot where latency does not improve much by using more threads. Using more threads per search limits query concurrency as more threads will be occupied per query. Read more in [Vespa sizing guide:reduce latency with multithreaded search](/en/performance/sizing-search#reduce-latency-with-multi-threaded-per-search-execution). + +## Advanced range search with hitLimit + +Vespa has an advanced query operator that allows selecting the documents with the k-largest or k-smallest values of a `fast-search` attribute field. + +To demonstrate this query operator, this guide introduces a `popularity` field. Since the last.fm dataset does not have a real popularity metric, the number of tags per track is used as a *proxy* of the true track popularity. + +The following script runs through the dataset and counts the number of tags and creates a Vespa [partial update](/en/writing/partial-updates) feed operation per track. + + +Paste the above into file create-popularity-updates.py + + +```python expandable +import os +import sys +import json + +directory = sys.argv[1] +seen_tracks = set() + +def process_file(filename): + global seen_tracks + with open(filename) as fp: + doc = json.load(fp) + title = doc['title'] + artist = doc['artist'] + hash = title + artist + if hash in seen_tracks: + return + else: + seen_tracks.add(hash) + + track_id = doc['track_id'] + tags = doc['tags'] + tags_dict = dict() + for t in tags: + k,v = t[0],int(t[1]) + tags_dict[k] = v + n = len(tags_dict) + + vespa_doc = { + "update": "id:music:track::%s" % track_id, + "fields": { + "popularity": { + "assign": n + } + } + } + print(json.dumps(vespa_doc)) + +sorted_files = [] +for root, dirs, files in os.walk(directory): + for filename in files: + filename = os.path.join(root, filename) + sorted_files.append(filename) +sorted_files.sort() +for filename in sorted_files: + process_file(filename) +``` + +With this script, run through the dataset and create the [partial update](/en/writing/partial-updates) feed : + +```bash +$ python3 create-popularity-updates.py lastfm\_test > updates.jsonl +``` + +Add the `popularity` field defined with `fast-search` to the `track` schema. Also, a `popularity` rank profile is added, which uses one thread per search: + +```js expandable +schema track { + + document track { + + field track_id type string { + indexing: summary | attribute + rank: filter + match: word + } + + field title type string { + indexing: summary | index + index: enable-bm25 + } + + field artist type string { + indexing: summary | index + } + + field tags type weightedset { + indexing: summary | attribute + attribute: fast-search + } + + field similar type tensor(trackid{}) { + indexing: summary | attribute + attribute: fast-search + } + + field popularity type int { + indexing: summary | attribute + attribute: fast-search + } + } + + fieldset default { + fields: title, artist + } + + document-summary track_id { + summary track_id { } + } + + rank-profile personalized { + first-phase { + expression: rawScore(tags) + } + } + + rank-profile similar { + inputs { + query(user_liked) tensor(trackid{}) + } + first-phase { + expression: sum(attribute(similar) * query(user_liked)) + } + } + + rank-profile similar-t2 inherits similar { + num-threads-per-search: 2 + } + + rank-profile popularity { + num-threads-per-search: 1 + first-phase { + expression: attribute(popularity) + } + } +} +``` + + +Paste the above into file app/schemas/track.sd + + +Deploy the application again : + +```bash +$ vespa deploy --wait 300 app +``` + +Adding a new field does not require a restart, apply the partial updates by: + +```bash +$ vespa feed -t http://localhost:8080 updates.jsonl +``` + +With that feed job completed, it is possible to select the five tracks with the highest popularity by using the [range()](/en/reference/querying/yql#range) query operator with [hitLimit](/en/reference/querying/yql#hitlimit): + +```bash +vespa query \ + 'yql=select track_id, popularity from track where {hitLimit:5,descending:true}range(popularity,0,Infinity)' \ + 'ranking=popularity' +``` + +The search returned 1,352 documents, while we asked for just five. The reason is that the `hitLimit` annotation for the `range` operator only specifies the lower bound. Documents that are tied with the same `popularity` value within the 5 largest values are returned. + +The `range()` query operator with `hitLimit` can be used to efficiently implement *top-k* selection for ranking a subset of the documents in the index. For example, use the `range` search with `hitLimit` to only run the track [recommendation tensor computation](#tensor-computations) over the most popular tracks: + +```bash +vespa query \ + 'yql=select title,artist, track_id, popularity from track where {hitLimit:5,descending:true}range(popularity,0,Infinity) and !(track_id in (@userLiked))' \ + 'input.query(user_liked)={{trackid:TRQIQMT128E0791D9C}:1.0,{trackid:TRWJIPT128E0791D99}:1.0,{trackid:TRGVORX128F4291DF1}:1.0}' \ + 'ranking=similar' \ + 'hits=5' \ + 'userLiked=TRQIQMT128E0791D9C,TRWJIPT128E0791D99,TRGVORX128F4291DF1' +``` + +Notice that this query returns 1,349 documents while the `range` search from the previous example returned 1,352 documents. This is due to the `not` filter. + +The range search with `hitLimit` can be used for cases where one wants to select efficiently *top-k* of a single valued numeric `attribute` with `fast-search`. Some use cases which can be efficiently implemented by using it: + +- Run ranking computations over the most recent documents using a `long` to represent a timestamp (e.g., using Unix epoch). +- Compute personalization tensor expressions over pre-selected content, e.g. using popularity. +- Optimize [sorting](/en/reference/querying/sorting-language) queries, instead of sorting a large result, find the smallest or largest values quickly by using range search with `hitLimit`. +- Autosuggest - see [#25333](https://github.com/vespa-engine/vespa/issues/25333). + +Do note that any other query or filter terms in the query are applied after having found the top-k documents, so an aggressive filter removing many documents might end up recalling 0 documents. + +This behavior is illustrated with this query: + +```bash +vespa query \ + 'yql=select track_id, popularity from track where {hitLimit:5,descending:true}range(popularity,0,Infinity) and popularity=99' +``` + +This query fails to retrieve any documents because the range search finds 1,352 documents where popularity is 100, *and'ing* that top-k result with the popularity=99 filter constraint ends up with 0 results. + +Using range search query operator with `hitLimit` is practical for search use cases like auto-complete or [search suggestions](https://github.com/vespa-engine/sample-apps/tree/master/incremental-search/search-suggestions) where one typically use [match: prefix](/en/reference/schemas/schemas#match) or n-gram matching using [match: gram](/en/reference/schemas/schemas#match). Limiting the short few first character searches to include a `hitLimit` range on popularity can greatly improve the query performance and at the same time match against popular suggestions. As the user types more characters, the number of matches is greatly reduced, so ranking can focus on more factors than just the single popularity attribute and increase the `hitLimit`. + +## Match phase limit - early termination + +An alternative to `range` search with `hitLimit` is using early termination with [match-phase](/en/reference/schemas/schemas#match-phase), which enables early termination of search and `first-phase` ranking using a document field to determine the search evaluation order. + +Match-phase early termination uses an attribute field during matching and ranking to impact the order the search and ranking is performed in. If a query is likely to generate more than `ranking.matchPhase.maxHits` per node the search will be terminated early and matching and evaluaton of the query will be in the order dictated by the `ranking.matchPhase.attribute` attribute field. + +Match phase early termination requires using a single-value numeric field with `attribute` and `fast-search`. See [Match phase query parameters](/en/reference/api/query#ranking.matchPhase). Match-phase limit cannot terminate early or stop any potential `second-phase` ranking expression, only matching and `first-phase` ranking, hence the name: *match phase limit*. + +The following enables `matchPhase` early termination with `maxHits` target set to 100: + +```bash +$ vespa query \ + 'yql=select track\_id, popularity from track where true' \ + 'ranking=popularity' \ + 'ranking.matchPhase.maxHits=100' \ + 'ranking.matchPhase.attribute=popularity' \ + 'hits=2' +``` + +Which will produce the following result: + +```json expandable +{ + "timing": { + "querytime": 0.007, + "summaryfetchtime": 0.002, + "searchtime": 0.01 + }, + "root": { + "id": "toplevel", + "relevance": 1.0, + "fields": { + "totalCount": 1476 + }, + "coverage": { + "coverage": 0, + "documents": 252, + "degraded": { + "match-phase": true, + "timeout": false, + "adaptive-timeout": false, + "non-ideal-state": false + }, + "full": false, + "nodes": 1, + "results": 1, + "resultsFull": 0 + }, + "children": [ + { + "id": "index:tracks/0/63f963f1f9372275e12d9e9c", + "relevance": 100.0, + "source": "tracks", + "fields": { + "track_id": "TRGCNGP12903CFA2BA", + "popularity": 100 + } + }, + { + "id": "index:tracks/0/7a74f1cd064acef348a1a701", + "relevance": 100.0, + "source": "tracks", + "fields": { + "track_id": "TRFVTTT128F930D148", + "popularity": 100 + } + } + ] + } +} +``` + +In this case, totalCount became 1,476, a few more than the `range` search with `hitLimit`. Notice also the presence of `coverage:degraded` - this informs the client that this result was not fully evaluated over all matched documents. Read more about [graceful result degradation](/en/performance/graceful-degradation). Note that the example uses the `popularity` rank-profile which was configured with one thread per search, for low settings of `maxHits`, this is the recommended setting. + +```js +rank-profile popularity { + num-threads-per-search: 1 + first-phase { + expression: attribute(popularity) + } +} +``` + +The core difference from capped range search is that `match-phase` is safe, as filters work inline with the search and are not applied after finding the top-k documents. + +This query does not trigger match-phase early termination because there are few hits matching the query: + +```bash +vespa query \ + 'yql=select track_id, popularity from track where popularity=99' \ + 'ranking=popularity' \ + 'ranking.matchPhase.maxHits=100' \ + 'ranking.matchPhase.attribute=popularity' \ + 'hits=2' +``` + +Generally, prefer `match-phase` early termination over `range` search with `hitLimit`. Match phase limiting can also be used in combination with text search queries: + +```bash +vespa query \ + 'yql=select title, artist, popularity from track where userQuery()' \ + 'query=love songs' \ + 'type=any' \ + 'ranking=popularity' \ + 'ranking.matchPhase.maxHits=100' \ + 'ranking.matchPhase.attribute=popularity' \ + 'hits=2' +``` + +Since this query uses `type=any` the above query retrieves a lot more documents than the target `matchPhase.maxHits`, so early termination is triggered, which will then cause the search core to match and rank tracks with the highest popularity. + +Early termination using match-phase limits is a powerful feature that can keep latency and cost in check for many large scale serving use cases where a document quality signal is available. Match phase termination also supports specifying a result diversity constraint. See [Result diversification blog post](https://blog.vespa.ai/result-diversification-with-vespa/). Note that result diversity is normally obtained with Vespa [result grouping](/en/querying/grouping), the match-phase diversity is used to ensure that diverse hits are also collected **if** early termination kicks in. + +## Advanced query tracing + +This section introduces query tracing. Tracing helps understand where time (and cost) is spent, and how to best optimize the query or schema settings. Query tracing can be enabled using the following parameters: + + + + + + + +A simple example query with tracing enabled: + +```bash +vespa query 'yql=select track_id from track where tags contains "rock"' \ + 'trace.level=3' 'trace.timestamps=true' 'trace.explainLevel=1' 'hits=1' +``` + +The first part of the trace traces the query through the stateless container search chain. For each searcher invoked in the chain a timestamp relative to the start of the query request is emitted: + +```json +{ + "trace": { + "children": [ + { + "message": "Using query profile 'default' of type 'root'" + }, + { + "message": "Invoking chain 'vespa' [com.yahoo.search.querytransform.WeakAndReplacementSearcher@vespa -> com.yahoo.prelude.statistics.StatisticsSearcher@native -> ... -> federation@native]" + }, + { + "children": [ + { + "timestamp": 0, + "message": "Invoke searcher 'com.yahoo.search.querytransform.WeakAndReplacementSearcher in vespa'" + }] + } + ] + } +} +``` + +The trace runs all the way to the query is dispatched to the content node(s) and the merged response is returned up to the client. + +```json +{ + "timestamp": 2, + "message": "sc0.num0 search to dispatch: query=[tags:rock] timeout=9993ms offset=0 hits=1 restrict=[track]" +} +``` + +In this case, with tracing it has taken 2ms of processing in the stateless container, before the query is about to be put on the wire on its way to the content nodes. + +The first protocol phase is the next trace message. In this case the reply, is ready read from the wire at timestamp 6, so approximately 4 ms was spent in the first protocol matching phase, including network serialization and deserialization. + +```json +{ + "timestamp": 6, + "message": [ + { + "start_time": "2022-03-27 15:03:20.769 UTC", + "traces": [ + + ], + "distribution-key": 0, + "duration_ms": 1.9814 + } + ] +} +``` + +```text +Inside this message is the content node traces of the query, `timestamp_ms` is relative to the start of the query on the content node. In this case, the content node uses 1.98 ms to evaluate the first protocol phase of the query (`duration_ms`). +``` + +More explanation of the content node `traces` is coming soon. It includes information like + +- How much time was spent traversing the dictionary and setting up the query. +- How much time was spent on matching and first-phase ranking. +- How much time was spent on second-phase ranking (if enabled). + +These traces can help guide both feature tuning decisions and [scaling and sizing](/en/performance/sizing-search). + +Later in the trace one can also see the second query protocol phase, which is the summary fill: +```json +{ + "timestamp": 7, + "message": "sc0.num0 fill to dispatch: query=[tags:rock] timeout=9997ms offset=0 hits=1 restrict=[track] summary=[null]" +} +``` + +And finally an overall breakdown of the two phases: + +```json +{ + "timestamp": 9, + "message": "Query time query 'tags:rock': 7 ms" +} +{ + "timestamp": 9, + "message": "Summary fetch time query 'tags:rock': 2 ms" +} +``` + +Also try the [Trace Visualizer](https://github.com/vespa-engine/vespa/tree/master/client/js/app#trace-visualizer) for a flame-graph of the query trace. + +## Tear down the container + +This concludes this tutorial. The following removes the container and the data: + +```bash +$ docker rm -f vespa +``` diff --git a/en/performance/profiling.html b/en/performance/profiling.html deleted file mode 100644 index 3040439cff..0000000000 --- a/en/performance/profiling.html +++ /dev/null @@ -1,205 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Profiling" -redirect_from: -- /en/performance/profiling-search-container.html ---- - -

    -Guidelines when profiling: -

    -
      -
    • Define clearly what to profile.
    • -
    • Find a load that represents what to profile. - This is often the hardest part, as there is a lot of noise if stressing other components.
    • -
    • Make sure that there are no other bottlenecks that blocks stressing the profiled component. - It makes little sense to do CPU profiling if the network or the disk is the limitation factor.
    • -
    • If possible, write special unit-tests like benchmark programs - that stress exactly what to profile.
    • -
    • If the system is multithreaded: -
        -
      • Always profile single threaded first - - that gives a baseline for doing the scaling tests. - Verify utilizing as many cores as expected.
      • -
      • Increase scaling gradually to at least 2x numcores - or until throughput degrades.
      • -
      -
    • -
    -

    -Also see using valgrind with Vespa. -

    - -

    CPU profiling

    - - - - - - - - - - - - - - - -
    vmstat -

    vmstat can be used to figure out what kind of resources are used:

    -
      -
    • cpu usage split in user, system, idle, and io wait: - system should be low(<10)
    • -
    • swap in/out: should be zero.
    • -
    -

    Note: A maxed out system should have either maxed out disks or cpu (idle == 0). - If not, there might be lock contention or the system is bottlenecked by upstream services.

    -

    Example:

    -
    -$ vmstat 1
    -
    -procs -----------memory---------- ---swap-- -----io---- --system-- ----cpu----
    -r  b   swpd   free   buff  cache   si   so    bi    bo   in    cs us sy id wa
    -0  0   5628 3315460 304024 23008616    0    0    14    34    0     0  0  0 99  0
    -1  0   5628 3298884 304024 23008640    0    0     0   396   33  4615  9  1 90  0
    -0  0   5628 3316336 304028 23008644    0    0     0     0   15  4469  4  1 95  0
    -0  0   5628 3316592 304028 23008644    0    0     0     0   24  4364  0  0 100  0
    -0  0   5628 3316592 304028 23008644    0    0     0  2948   20  4305  0  0 100  0
    -0  0   5628 3316468 304028 23008644    0    0     0     0   22  4259  0  0 100  0
    -0  0   5628 3316468 304028 23008644    0    0     0   180   20  4279  0  0 100  0
    -0  0   5628 3316468 304028 23008644    0    0     0     0   26  4349  0  0 100  0
    -16  0   5628 3284236 304056 23008688    0    0    12   188   17  9196 38  2 60  0
    -19  0   5628 3267020 304056 23008732    0    0     8   128   44  6408 99  1  0  0
    -16  0   5628 3245472 304060 23008840    0    0    20     0    9  7191 99  1  0  0
    -17  0   5628 3227784 304060 23008872    0    0    20     0   27  6420 99  1  0  0
    -
    -
    top - Use top to get a real-time view of which processes consume CPU and memory. -
    iostat - Use iostat to monitor disk IO. Note that the % busy is useless - for SSD/NVMe storage disks, see Two traps in iostat: %util and svctm. -
    - - - -

    CPU Profiling using perf

    -

    -Sometimes, when debugging cpu usage in a remote cluster and debugging performance, -it might be beneficial to get a performance profile snapshot. -To use perf find the pid of the vespa-proton-bin process which can be obtained using -vespa-sentinel-cmd, or top/ps. Record: -

    -$ sudo perf record -g --pid=<pid-of-proton-process> sleep 60
    -
    -View a performance profile report: -
    -$ sudo perf report
    -
    -Sometimes it is useful to have kernel debug info installed to get symbol info for the Linux kernel: -
    -$ sudo yum install --enablerepo=base-debuginfo kernel-debuginfo-$(uname -r)
    -
    -

    It is important to get somewhat same version of kernel-debuginfo as the kernel package.

    - - -

    Container privileges

    -

    -When debugging an unprivileged docker container, -perf commands can be executed from inside a privileged container sharing pid space: -{% raw %} -

    -$ CONTAINER=host002-09
    -$ sudo docker run -ti --rm --privileged --pid container:$CONTAINER \
    -  --entrypoint bash $(sudo docker ps --filter name=$CONTAINER --format "{{.Image}}")
    -
    -{% endraw %} -

    -This starts a privileged container that shares the pid namespace, -using the same docker image as the container to debug. -Run perf record ... inside this privileged container. -

    - - - -

    Profiling the Query Container

    -

    - This section describes how you can configure the Container to - allow for profiling custom searchers in order to identify performance bottlenecks - - be it lock contention or CPU intensive algorithms. -

    - - -

    Install YourKit profiler on the Container

    -

    - Yourkit is a good and simple tool for finding hotspots in Java code. - It supports both sampling and tracing. Often it is necessary to use both modes. - Tracing is accurate as to how many times a method is invoked and from where. - That can be used to analyze if you are actually not computing the same thing from multiple places - and overall doing more than you need. - However, it will hide effects of cache miss and especially cost of atomic operations and synchronization costs. -

    -

    - Assume there is an installation in a data center that you would like to profile, - preferably with a nice UI running on your local desktop. - All this is just a few steps away: -

    -
      -
    • Install yourkit
    • -
    • Modify services.xml: -
      -<nodes>
      -  <jvm options="-agentlib:yjpagent" />
      -...
      -</nodes>
      -
      - Read more about jvm tuning. - Disabling the freezedetector stops the container from shutting down during profiling.
    • -
    • Re-deploy the application: -
      -$ vespa deploy appdir
      -
    • -
    • restart Vespa on the node that runs the Container
    • -
    -

    - Browse $VESPA_HOME/logs/vespa.log for errors. - You are now ready to perform profiling; you just need to install the UI on your desktop. -

    - - -

    Install YourKit UI on the Desktop

    -

    - The server is ready for profiling, - now install the YourKit profiler on the desktop. - Download the distribution that fits the OS you are running from - YourKit. - Follow the installation instructions, including setting the license server. -

    -

    - Note: - By default the YourKit agent runs on port 10001. - If Vespa is running on hosts not directly reachable from the desktop, - setting up an SSH tunnel can work around: -

    -
    -$ ssh -L 1080:$hostname:10001 $hostname
    -
    -

    - where $hostname is the node that is running the container with the YourKit agent profiler. - All traffic to localhost (the desktop) port 1080 will be forwarded to the remote application running on port 10001. -

    - - - -

    Using Yourkit

    -

    - You are now ready to profile your application. - (You will need to put some realistic load against the container instance, - see the Vespa benchmarking guide) - After having started the load simulation you can start the profiling session, - open the YourKit application installed locally and select - Monitor Remote Applications => Connect to remote application. - Enter localhost:1080 and press Connect. - You should now see the profiling screen with - Remote application "Server" (PID XXXXX) is being profiled at localhost:1080. -

    diff --git a/en/performance/profiling.mdx b/en/performance/profiling.mdx new file mode 100644 index 0000000000..99bcef69b0 --- /dev/null +++ b/en/performance/profiling.mdx @@ -0,0 +1,124 @@ +--- +title: "Profiling" +--- + +Guidelines when profiling: + +- Define clearly what to profile. +- Find a load that represents what to profile. This is often the hardest part, as there is a lot of noise if stressing other components. +- Make sure that there are no other bottlenecks that blocks stressing the profiled component. It makes little sense to do CPU profiling if the network or the disk is the limitation factor. +- If possible, write special unit-tests like benchmark programs that stress exactly what to profile. +- If the system is multithreaded: + - Always profile single threaded first - that gives a baseline for doing the scaling tests. Verify utilizing as many cores as expected. + - Increase scaling gradually to at least 2x numcores or until throughput degrades. + +Also see [using valgrind with Vespa](/en/performance/valgrind). + +## CPU profiling + + + + + + + + + + + + + + + + + + + + + + +
    **vmstat***vmstat* can be used to figure out what kind of resources are used:

    • cpu usage split in user, system, idle, and io wait: system should be low({`<10`})
    • swap in/out: should be zero.

    **Note:**
    A maxed out system should have either maxed out disks or cpu ({`idle == 0`}). If not, there might be lock contention or the system is bottlenecked by upstream services.


    Example:

    {`$ vmstat 1`}

    {`procs -----------memory---------- ---swap-- -----io---- --system-- ----cpu----`}
    {`r b swpd free buff cache si so bi bo in cs us sy id wa`}
    {`0 0 5628 3315460 304024 23008616 0 0 14 34 0 0 0 0 99 0`}
    {`1 0 5628 3298884 304024 23008640 0 0 0 396 33 4615 9 1 90 0`}
    {`0 0 5628 3316336 304028 23008644 0 0 0 0 15 4469 4 1 95 0`}
    {`0 0 5628 3316592 304028 23008644 0 0 0 0 24 4364 0 0 100 0`}
    {`0 0 5628 3316592 304028 23008644 0 0 0 2948 20 4305 0 0 100 0`}
    {`0 0 5628 3316468 304028 23008644 0 0 0 0 22 4259 0 0 100 0`}
    {`0 0 5628 3316468 304028 23008644 0 0 0 180 20 4279 0 0 100 0`}
    {`0 0 5628 3316468 304028 23008644 0 0 0 0 26 4349 0 0 100 0`}
    {`16 0 5628 3284236 304056 23008688 0 0 12 188 17 9196 38 2 60 0`}
    {`19 0 5628 3267020 304056 23008732 0 0 8 128 44 6408 99 1 0 0`}
    {`16 0 5628 3245472 304060 23008840 0 0 20 0 9 7191 99 1 0 0`}
    {`17 0 5628 3227784 304060 23008872 0 0 20 0 27 6420 99 1 0 0`}
    **top**Use top to get a real-time view of which processes consume CPU and memory.
    **iostat**Use iostat to monitor disk IO. Note that the % busy is useless for SSD/NVMe storage disks, see Two traps in iostat: %util and svctm.
    + + +## CPU Profiling using perf + +Sometimes, when debugging cpu usage in a remote cluster and debugging performance, it might be beneficial to get a performance profile snapshot. To use `perf` find the pid of the [vespa-proton-bin](/en/content/proton) process which can be obtained using [vespa-sentinel-cmd](/en/reference/operations/self-managed/tools#vespa-sentinel-cmd), or *top/ps*. Record: + +```bash +$ sudo perf record -g --pid= sleep 60 +``` + +View a performance profile report: + +```text +$ sudo perf report +``` + +Sometimes it is useful to have kernel debug info installed to get symbol info for the Linux kernel: + +```bash +$ sudo yum install --enablerepo=base-debuginfo kernel-debuginfo-$(uname -r) +``` + +It is important to get somewhat same version of *kernel-debuginfo* as the *kernel* package. + +### Container privileges + +When debugging an unprivileged docker container, `perf` commands can be executed from inside a privileged container sharing pid space: + +```bash +$ CONTAINER=host002-09 +$ sudo docker run -ti --rm --privileged --pid container:$CONTAINER \ + --entrypoint bash $(sudo docker ps --filter name=$CONTAINER --format "{{.Image}}") +``` + +This starts a privileged container that shares the pid namespace, using the same docker image as the container to debug. Run `perf record ...` inside this privileged container. + +## Profiling the Query Container + +This section describes how you can configure the Container to allow for profiling custom searchers in order to identify performance bottlenecks - be it lock contention or CPU intensive algorithms. + +### Install YourKit profiler on the Container + +Yourkit is a good and simple tool for finding hotspots in Java code. It supports both sampling and tracing. Often it is necessary to use both modes. Tracing is accurate as to how many times a method is invoked and from where. That can be used to analyze if you are actually not computing the same thing from multiple places and overall doing more than you need. However, it will hide effects of cache miss and especially cost of atomic operations and synchronization costs. + +Assume there is an installation in a data center that you would like to profile, preferably with a nice UI running on your local desktop. All this is just a few steps away: +- Install yourkit +- Modify *services.xml*: + + ```xml + + + ... + + ``` + Read more about [jvm tuning](/en/performance/container-tuning). Disabling the freezedetector stops the container from shutting down during profiling. +- Re-deploy the application: + + ```bash + $ vespa deploy appdir + ``` +- restart Vespa on the node that runs the Container + + +Browse *$VESPA\_HOME/logs/vespa.log* for errors. You are now ready to perform profiling; you just need to install the UI on your desktop. + +### Install YourKit UI on the Desktop + +The server is ready for profiling, now install the YourKit profiler on the desktop. Download the distribution that fits the OS you are running from [YourKit](https://www.yourkit.com/). Follow the installation instructions, including setting the *license server*. + + +**Note:** + +By default the YourKit agent runs on port 10001. If Vespa is running on hosts not directly reachable from the desktop, setting up an SSH tunnel can work around: + + +```bash +$ ssh -L 1080:$hostname:10001 +``` + +where *$hostname* is the node that is running the container with the YourKit agent profiler. All traffic to localhost (the desktop) port 1080 will be forwarded to the remote application running on port 10001. + +### Using Yourkit + +You are now ready to profile your application. (You will need to put some realistic load against the container instance, see the [Vespa benchmarking guide](/en/performance/benchmarking)) After having started the load simulation you can start the profiling session, open the YourKit application installed locally and select *Monitor Remote Applications => Connect to remote application*. Enter *localhost:1080* and press Connect. You should now see the profiling screen with *Remote application "Server" (PID XXXXX) is being profiled at localhost:1080*. \ No newline at end of file diff --git a/en/performance/rate-limiting-searcher.html b/en/performance/rate-limiting-searcher.html deleted file mode 100644 index e7f364cfa0..0000000000 --- a/en/performance/rate-limiting-searcher.html +++ /dev/null @@ -1,143 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Rate Limiting Search Requests" ---- - -

    - To avoid overloading a Vespa content cluster or to limit query load from e.g. - certain clients, the bundled - - Vespa Rate Limiting Searcher can be configured to reject incoming requests to a search chain with - HTTP return code 429 - if number of requests per second exceed a certain quota. - The counter will reset once the quota is refilled the next second. -

    - -

    Getting Started

    -

    - While the rate limiting searcher is bundled with Vespa, it needs to be - explicitly configured in services.xml before it is loaded. - This example shows how the searcher is configured for the default search chain: -

    -
    {% highlight xml %}
    -
    -    
    -        
    -            
    -        
    -    
    -
    -{% endhighlight %}
    -

    - When this configuration is live, the rate limiting searcher is loaded, but not active. - It is enabled on a per-request basis using either parameters directly in your HTTP search request - or by configuring query profiles. - Both approaches are shown below. -

    - -

    Activate With Query Parameters

    -

    - The searcher takes these query parameter arguments: -

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    ArgumentTypeDescription
    rate.idStringThe id of the client from rate limiting perspective.
    rate.costDoubleThe cost Double of this query. This is read after - executing the query and so can be set by downstream searchers - inspecting the result to allow differencing the cost of various queries. - Default is 1.
    rate.quotaDoubleThe cost per second a particular id is allowed to - consume in this system.
    rate.idDimensionStringThe name of the rate-id dimension used when - logging metrics. If this is not specified, the metric will be logged - without dimensions.
    rate.dryRunBooleanEmit metrics on rejected requests but don't - actually reject them.
    -

    - In a typical scenario, the application logic constructing the HTTP search - request will set &rate.id and &rate.quota - in the request depending on where the traffic originated - example: -

    -
    http://localhost:8080/search?query=foo&rate.id=clientA&rate.quota=300
    - -

    Activate With Query Profiles

    -

    - If you don't want to add the rate limiting parameters to every request - or don't control the application logic constructing the search requests, - you can enable the rate limiting using query profiles. - An example default query profile enabling rate limiting in the application package: -

    -
    {% highlight xml %}
    -
    -    100
    -    default
    -
    -{% endhighlight %}
    - - -

    Per Client Quotas

    -

    - In a shared service scenario, you may want to assign different quota based - on a query parameter passed with the request, e.g. &clientId. - The example below will assign different quotas based on the clientId parameter passed with the request: -

    -
    {% highlight xml %}
    -
    -    
    -    clientId
    -
    -    
    -    100
    -    default
    -    clientID
    -
    -    
    -    
    -        200
    -        clientA
    -        clientID
    -    
    -
    -    
    -    
    -        400
    -        clientB
    -        clientID
    -    
    -
    -{% endhighlight %}
    - -

    Metrics

    -

    - The searcher will emit the count metric - requestsOverQuota with the dimension [rate.idDimension=rate.id]. -

    diff --git a/en/performance/rate-limiting-searcher.mdx b/en/performance/rate-limiting-searcher.mdx new file mode 100644 index 0000000000..3349d1bd64 --- /dev/null +++ b/en/performance/rate-limiting-searcher.mdx @@ -0,0 +1,114 @@ +--- +title: "Rate Limiting Search Requests" +sidebarTitle: "Rate-limiting queries" +--- + +To avoid overloading a Vespa content cluster or to limit query load from e.g. certain clients, the bundled [Vespa Rate Limiting Searcher](https://github.com/vespa-engine/vespa/blob/master/container-search/src/main/java/com/yahoo/search/searchers/RateLimitingSearcher.java) can be configured to reject incoming requests to a search chain with [HTTP return code 429](https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#429) if number of requests per second exceed a certain quota. The counter will reset once the quota is refilled the next second. + +## Getting Started + +While the rate limiting searcher is bundled with Vespa, it needs to be explicitly configured in [services.xml](/en/reference/applications/services/services) before it is loaded. This example shows how the searcher is configured for the default search chain: + +```xml + + + + + + + +``` + +When this configuration is live, the rate limiting searcher is loaded, but not active. It is enabled on a per-request basis using either parameters directly in your HTTP search request or by configuring query profiles. Both approaches are shown below. + +## Activate With Query Parameters + +The searcher takes these query parameter arguments: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    ArgumentTypeDescription
    rate.idStringThe id of the client from rate limiting perspective.
    rate.costDoubleThe cost Double of this query. This is read after executing the query and so can be set by downstream searchers inspecting the result to allow differencing the cost of various queries. Default is 1.
    rate.quotaDoubleThe cost per second a particular id is allowed to consume in this system.
    rate.idDimensionStringThe name of the rate-id dimension used when logging metrics. If this is not specified, the metric will be logged without dimensions.
    rate.dryRunBooleanEmit metrics on rejected requests but don't actually reject them.
    + +In a typical scenario, the application logic constructing the HTTP search request will set `&rate.id` and `&rate.quota` in the request depending on where the traffic originated - example: + +```bash +http://localhost:8080/search?query=foo&rate.id=clientA&rate.quota=300 +``` + +## Activate With Query Profiles + +If you don't want to add the rate limiting parameters to every request or don't control the application logic constructing the search requests, you can enable the rate limiting using [query profiles](/en/reference/querying/query-profiles). An example default query profile enabling rate limiting in the application package: + +```xml + + 100 + default + +``` + +### Per Client Quotas + +In a shared service scenario, you may want to assign different quota based on a query parameter passed with the request, e.g. `&clientId`. The example below will assign different quotas based on the clientId parameter passed with the request: + +```xml + + + clientId + + + 100 + default + clientID + + + + 200 + clientA + clientID + + + + + 400 + clientB + clientID + + +``` + +## Metrics + +The searcher will emit the [count metric](/en/operations/metrics) `requestsOverQuota` with the dimension `[rate.idDimension=rate.id]`. \ No newline at end of file diff --git a/en/performance/sizing-feeding.html b/en/performance/sizing-feeding.html deleted file mode 100644 index 332db11bed..0000000000 --- a/en/performance/sizing-feeding.html +++ /dev/null @@ -1,630 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Vespa Feed Sizing Guide" ---- - -

    - Vespa is optimized to sustain a high feed load while serving - - also during planned and unplanned changes to the instance. - This guide provides an overview of how to optimize feed performance and also understand bottlenecks. -

    -

    - The reads and writes guide has an overview of the Vespa architecture and relevant - APIs for feeding and searching. One key takeaway is that Vespa is split into two main service types: -

    -
      -
    • Stateless container cluster(s)
    • -
    • Stateful content cluster(s)
    • -
    -

    - The stateless container cluster is responsible for processing all document operations to Vespa. - The stateful content cluster is responsible for writing and syncing all document operations (persisting state and managing data structures). -

    - Generally, Vespa cannot sustain a higher write rate than the underlying storage can handle (MB/s and IOPS). To understand - resource utilization it is critical that resource usage like CPU, memory, disk and network are monitored. Only this way can - a system be sized correctly and bottlenecks identified. -

    - - -

    Stateless container cluster

    -

    - The processing of all document operations to Vespa are routed through the stateless container cluster. - Processing includes both Vespa processing like linguistic processing - and custom document processing. The - stateless container cluster is also responsible for embedding inference. Embedding - inference can be compute-resource intensive, depending on number of embed calls and the size of the embedding model. - See embedding performance. -

    -

    - The stateless cluster is compute (CPU/GPU util) bound and processing rates should scale linearly with the number of nodes and the number of V-CPU's in the cluster as - long as the client can deliver enough operations over the network. -

    - See multiple container clusters - for how to separate search and write to different container clusters. Isolated container clusters - are useful for high load scenarios where the container cluster is compute bound and where there is concurrent search and write load - and where we want to avoid write operations to impact search queries due to compute-related resource contention. -

    - The stateless container cluster is implemented in Java (JVM), ensure enough memory allocated for heap to avoid excessive JVM garbage collection. - See stateless container tuning for tuning options. The default max heap size is 1.5GB in self-hosted deployments - unless overridden. -

    - - -

    Stateful content cluster

    -

    - All feed operations to Vespa are written and synced to the transaction log on - the content node(s). This include both writing new documents and updating existing documents. - The Vespa transaction log is a write-ahead log (WAL) that ensures durability of the data. A reply is only sent back to the client - when the operation is written successfully to the transaction log and applied (visible in search/get). -

    -

    - The write pattern is append and sequential (not random) IO. Note that Vespa cannot sustain a higher write rate than - the underlying storage can handle. Feeding might be impacted severely if the content nodes are using network attached storage - where the sync operation (for durability) has a much higher cost than on local attached storage (e.g. SSD). - See sync-transactionlog. -

    - -

    Document store

    -

    - Documents are written to the document store in all - indexing modes - - this is where the copy of the document is persisted. -

    -

    - Adding new documents to the document store is append-only with a sequential IO pattern. - Writing a new version of a document (PUT a document that already exists) is the same as for a - document ID that does not exist. The in-memory mapping from - document ID to summary data file position is updated to point to the latest version in both cases. The - summary files are defragmented to remove old versions of documents. -

    - -

    Attribute store

    -

    - Fields that are defined with the attribute property are in-memory fields that - supports in-place updates with higher partial update throughput than fields that are indexed - with `index` property (avoiding read-apply-write pattern). - The attribute store is a memory-only data structure that is regularly persisted to disk in the attribute store. -

    -
    -schema ticker {
    -    document ticker {
    -        field volume type int {
    -            indexing: summary | attribute
    -        }
    -    }
    -}
    -
    -

    - See partial updates for details. -

    - -

    Redundancy settings

    -

    -To achieve memory-only updates (plus transaction log writing), -make sure all attributes to update are ready, -meaning the content node has loaded the attribute field into memory: -

    -
      -
    • - One way to ensure this is to set - searchable copies equal to - redundancy - - i.e. all nodes that has a replica of the document has loaded it as searchable -
    • -
    • - Another way is by setting - fast-access on each attribute to update -
    • -
    - - - -

    Index

    -

    - Changes to index fields are written to the document store - and the index. - Note that an UPDATE operation requires a read-modify-write to the document store and limits throughput. - Refer to partial updates for more details. -

    -
    -schema music {
    -    document music {
    -        field artist type string {
    -            indexing: summary | index
    -        }
    -    }
    -}
    -
    - - -

    Content Node Thread pools

    -

    -Several thread pools are involved when handling write operations on a content node. -These are summarized in the following table. Not all mutating operations can be handled in parallel and tracking -these metrics can help identify bottlenecks. For example, if you notice that feed throughput is not increasing beyond -a certain CPU utilization, it might be that one of the thread pools is saturated. - -Metrics are available for each thread pool, see -searchnode metrics -for details. -

    -

    -To analyse performance and bottlenecks, the most relevant metrics are .utilization and .queuesize. -In addition, .saturation is relevant for the field writer thread pool. -See bottlenecks for details. -

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    Thread poolDescription
    master -

    - Updates the - document metastore, - prepares tasks to the index and summary threads, - and splits a write operation into a set of tasks to update individual - attributes, - executed by the threads in the field writer. -

    -
    Threads1
    InstancesOne instance per document database.
    Metric prefixcontent.proton.documentdb.threading_service.master.
    index -

    - Manages writing of index fields in the memory index. - It splits a write operation into a set of tasks to update individual index fields, - executed by the threads in the field writer. -

    -
    Threads1
    InstancesOne instance per document database.
    Metric prefixcontent.proton.documentdb.threading_service.index.
    summary -

    - Writes documents to the - document store. -

    -
    Threads1
    InstancesOne instance per document database.
    Metric prefixcontent.proton.documentdb.threading_service.summary.
    field writer -

    - The threads in this thread pool are used to invert index fields, - write changes to the memory index, and write changes to attributes. - Index fields and attribute fields across all document databases are randomly assigned - to one of the threads in this thread pool. - A field that is costly to write or update might become the bottleneck during feeding. -

    -
    ThreadsMany, controlled by feeding concurrency.
    InstancesOne instance shared between all document databases.
    Metric prefixcontent.proton.executor.field_writer.
    shared -

    - The threads in this thread pool are among other used to compress and - de-compress documents in the - document store, - merge files as part of - disk index fusion, - and prepare for inserting a vector into a - HNSW index. -

    -
    ThreadsMany, controlled by feeding concurrency.
    InstancesOne instance shared between all document databases.
    Metric prefixcontent.proton.executor.shared.
    - - -

    Multivalue attribute

    -

    - Multivalued attributes are - weightedset, array of struct/map, map of struct/map and tensor. - The attributes have different characteristics, which affects write performance. - Generally, updates to multivalue fields are more expensive as the field size grows: -

    - - - - - - - - - - - - - - - - - - - -
    AttributeDescription
    weightedset - Memory-only operation when updating: read full set, update, write back. - Make the update as inexpensive as possible using numeric types instead of strings, where possible - Example: a weighted set of string with many (1000+) elements. - Adding an element to the set means an enum store lookup/add and add/sort of the attribute multivalue map - - details in attributes. - Use a numeric type instead to speed this up - this has no string comparisons. -
    array/map of struct/map - Update to array of struct/map and map of struct/map requires a read from the - document store and will reduce update rate - - see #10892. -
    tensor - Updating tensor cell values is a memory-only operation: copy tensor, update, write back. - For large tensors, this implicates reading and writing a large chunk of memory for single cell updates. -
    - - - -

    Parent/child

    -

    - Parent documents are global, i.e. has a replica on all nodes. - Writing to fields in parent documents often simplify logic, - compared to the de-normalized case where all - (child) documents are updated. - Write performance depends on the average number of child documents vs number of nodes in the cluster - examples: -

    -
      -
    • 10-node cluster, avg number of children=100, redundancy=2: - A parent write means 10 writes, compared to 200 writes, or 20x better
    • -
    • 50-node cluster, avg number of children=10, redundancy=2: - A parent write means 50 writes, compared to 20 writes, or 2.5x worse
    • -
    -

    - Hence, the more children, the better performance effect for parent writes. -

    - - - -

    Conditional updates

    -

    - A conditional update looks like: -

    -
    -{
    -    "update" : "id:namespace:myDoc::1",
    -    "condition" : "myDoc.myField == \"abc\"",
    -    "fields" : { "myTimestamp" : { "assign" : 1570187817 } }
    -}
    -
    -

    - If the document store - is accessed when evaluating the condition, performance drops significantly because you get random access instead of - just appending to the persisted data structures. - Conditions should be evaluated using attribute values for high performance - - in the example above, myField should be an attribute. -

    -

    - Note: If the condition uses struct or map, values are read from the document store: -

    -
    -    "condition" : "myDoc.myMap{1} == 3"
    -
    -

    - This is true even though all struct fields are defined as attribute. - Improvements to this is tracked in - #10892. -

    - - - - -

    Client roundtrips

    -

    - Consider the difference when sending two fields assignments to the same document: -

    -
    -{
    -    "update" : "id:namespace:doctype::1",
    -    "fields" : {
    -        "myMap{1}" : { "assign" : { "timestamp" : 1570187817 } }
    -        "myMap{2}" : { "assign" : { "timestamp" : 1570187818 } }
    -    }
    -}
    -
    -

    vs.

    -
    -{
    -    "update" : "id:namespace:doctype::1",
    -    "fields" : {
    -        "myMap{1}" : { "assign" : { "timestamp" : 1570187817 } }
    -    }
    -}
    -{
    -    "update" : "id:namespace:doctype::1",
    -    "fields" : {
    -        "myMap{2}" : { "assign" : { "timestamp" : 1570187818 } }
    -    }
    -}
    -
    -

    - In the first case, one update operation is sent from - vespa feed - - in the latter, the client will send the second update operation after receiving an ack for the first. - When updating multiple fields, put the updates in as few operations as possible. - See ordering details. -

    - - - - -

    - A content node normally has a fixed set of resources (CPU, memory, disk). - Configure the CPU allocation for feeding vs. searching in - concurrency - - value from 0 to 1.0 - a higher value means more CPU resources for feeding. -

    -

    - In addition, you can also control priority of feed versus search, or rather how nice feeding shall be. - Since a process needs root privileges for increasing feed, we have opted to reduce priority (be nice) - of feeding. This is controlled by a niceness - number from 0 to 1.0 - higher value will favor search over feed. 0 is default. -

    - - - -

    Feed testing

    -

    - When testing for feeding capacity: -

    -
      -
    1. Use vespa feed.
    2. -
    3. Test using one content node to find its capacity and where the bottlenecks are (resource utilization metrics) and Vespa metrics
    4. -
    5. Test feeding performance by adding feeder instances. - Make sure network and CPU (content and container node) usage increases, until saturation.
    6. -
    7. See troubleshooting at end to make sure there are no errors.
    8. -
    -

    - Other scenarios: Feed testing for capacity for sustained load in a system in steady state, - during state changes, during query load. -

    - - - -

    Troubleshooting

    -{% include note.html content="Use the -monitoring sample app -to set up a sample system, with a document/query feed and dashboards, to familiarize with metrics." %} - - - - - - - - - - - - - - - - - - - - - - - -
    Metrics -

    - Use metrics - from content nodes and look at queues - - queue wait time and queue size (all metrics in milliseconds): -

    -
    -vds.filestor.averagequeuewait.sum
    -vds.filestor.queuesize
    -
    -

    Check content node metrics across all nodes to see if there are any outliers. - Also check latency metrics per operation type:

    -
    -vds.filestor.allthreads.put.latency
    -vds.filestor.allthreads.update.latency
    -vds.filestor.allthreads.remove.latency
    -
    -
    Bottlenecks -

    - One of the threads used to handle write operations - might become the bottleneck during feeding. Look at the .utilization - metrics for all thread pools: -

    -
    -content.proton.documentdb.threading_service.master.utilization
    -content.proton.documentdb.threading_service.index.utilization
    -content.proton.documentdb.threading_service.summary.utilization
    -content.proton.executor.field_writer.utilization
    -content.proton.executor.shared.utilization
    -
    -

    - If utilization is high for - field writer or - shared, adjust - feeding concurrency - to allow more CPU cores to be used for feeding. -

    -

    - For the field writer also look at the .saturation metric: -

    -
    -content.proton.executor.field_writer.saturation
    -
    -

    - If this is close to 1.0 and higher than .utilization it indicates that - one of its worker threads is a bottleneck. The reason can be that this particular thread - is handling a large index or attribute field that is naturally expensive to write and update. - Use the custom component state API - to find which index and attribute fields are assigned to which thread (identified by executor_id), - and look at the detailed statistics of the field writer to find which thread is the actual bottleneck: -

    -
    -state/v1/custom/component/documentdb/mydoctype/subdb/ready/index
    -state/v1/custom/component/documentdb/mydoctype/subdb/ready/attributewriter
    -state/v1/custom/component/threadpools/field_writer
    -
    -
    Failure rates -

    Inspect these metrics for failures during load testing:

    -
    -vds.distributor.updates.latency
    -vds.distributor.updates.ok
    -vds.distributor.updates.failures.total
    -vds.distributor.puts.latency
    -vds.distributor.puts.ok
    -vds.distributor.puts.failures.total
    -vds.distributor.removes.latency
    -vds.distributor.removes.ok
    -vds.distributor.removes.failures.total
    -
    -
    Blocked feeding -

    - This metric should be 0 - refer to feed block: -

    -
    -content.proton.resource_usage.feeding_blocked
    -
    -
    Concurrent mutations -

    Multiple clients updating the same document concurrently will stall writes:

    -
    -vds.distributor.updates.failures.concurrent_mutations
    -
    - Mutating client operations towards a given document ID are sequenced on the - distributors. - If an operation is already active towards a document, - a subsequently arriving one will be bounced back to the client with a transient failure code. - Usually this happens when users send feed from multiple clients concurrently without synchronisation. - Note that feed operations sent by a single client are sequenced client-side, - so this should not be observed with a single client only. - Bounced operations are never sent on to the backends and should not cause elevated latencies there, - although the client will observe higher latencies due to automatic retries with back-off. -
    Wrong distribution -

    -
    -vds.distributor.updates.failures.wrongdistributor
    -
    - Indicates that clients keep sending to the wrong distributor. - Normally this happens infrequently - (but is does happen on client startup or distributor state transitions), - as clients update and cache all state required to route directly to the correct distributor - (Vespa uses a deterministic CRUSH-based algorithmic distribution). - Some potential reasons for this: -
      -
    1. Clients are being constantly re-created with no cached state.
    2. -
    3. The system is in some kind of flux where the underlying state keeps changing constantly.
    4. -
    5. The client distribution policy has received so many errors - that it throws away its cached state to start with a clean slate to - e.g. avoid the case where it only has cached information for the bad side of a network partition.
    6. -
    7. The system has somehow failed to converge to a shared cluster state, - causing parts of the cluster to have a different idea of the correct state than others.
    8. -
    -
    Cluster out of sync -

    - update_puts/gets indicate "two-phase" updates: -

    -
    -vds.distributor.update_puts.latency
    -vds.distributor.update_puts.ok
    -vds.distributor.update_gets.latency
    -vds.distributor.update_gets.ok
    -vds.distributor.update_gets.failures.total
    -vds.distributor.update_gets.failures.notfound
    -
    -

    - If replicas are out of sync, - updates cannot be applied directly on the replica nodes - as they risk ending up with diverging state. - In this case, Vespa performs an explicit read-consolidate-write (write repair) operation on the distributors. - This is usually a lot slower than the regular update path because it doesn't happen in parallel. - It also happens in the write-path of other operations, - so risks blocking these if the updates are expensive in terms of CPU. -

    - Replicas being out of sync is by definition not the expected steady state of the system. - For example, replica divergence can happen if one or more replica nodes are unable to process or persist operations. - Track (pending) merges: -

    -
    -vds.idealstate.buckets
    -vds.idealstate.merge_bucket.pending
    -vds.idealstate.merge_bucket.done_ok
    -vds.idealstate.merge_bucket.done_failed
    -
    -
    diff --git a/en/performance/sizing-feeding.mdx b/en/performance/sizing-feeding.mdx new file mode 100644 index 0000000000..b7d94cea53 --- /dev/null +++ b/en/performance/sizing-feeding.mdx @@ -0,0 +1,361 @@ +--- +title: "Vespa Feed Sizing Guide" +sidebarTitle: "Feed sizing guide" +--- + +Vespa is optimized to sustain a high feed load while serving - also during planned and unplanned changes to the instance. This guide provides an overview of how to optimize feed performance and also understand bottlenecks. + +The [reads and writes](/en/writing/reads-and-writes) guide has an overview of the Vespa architecture and relevant APIs for feeding and searching. One key takeaway is that Vespa is split into two main service types: + +- Stateless container cluster(s) +- Stateful content cluster(s) + +The stateless container cluster is responsible for processing all document operations to Vespa. The stateful content cluster is responsible for writing and syncing all document operations (persisting state and managing data structures). + +Generally, Vespa cannot sustain a higher write rate than the underlying storage can handle (MB/s and IOPS). To understand resource utilization it is critical that resource usage like CPU, memory, disk and network are monitored. Only this way can a system be sized correctly and bottlenecks identified. + +## Stateless container cluster + +The processing of all document operations to Vespa are routed through the stateless *container* cluster. Processing includes both Vespa processing like [linguistic processing](/en/linguistics/linguistics) and custom [document processing](/en/applications/document-processors). The stateless container cluster is also responsible for [embedding](/en/rag/embedding) inference. Embedding inference can be compute-resource intensive, depending on number of *embed* calls and the size of the embedding model. See [embedding performance](/en/rag/embedding#embedder-performance). + +The stateless cluster is compute (CPU/GPU util) bound and processing rates should scale linearly with the number of nodes and the number of V-CPU's in the cluster as long as the client can deliver enough operations over the network. + +See [multiple container clusters](/en/writing/document-routing#multiple-container-clusters) for how to separate search and write to different container clusters. Isolated container clusters are useful for high load scenarios where the container cluster is compute bound and where there is concurrent search and write load and where we want to avoid write operations to impact search queries due to compute-related resource contention. + +The stateless container cluster is implemented in Java (JVM), ensure enough memory allocated for heap to avoid excessive JVM garbage collection. See [stateless container tuning](/en/performance/container-tuning) for tuning options. The default max heap size is 1.5GB in self-hosted deployments unless overridden. + +## Stateful content cluster + +**All feed operations** to Vespa are **written and synced** to the [transaction log](/en/content/proton#transaction-log) on the content node(s). This include both writing new documents and updating existing documents. The Vespa transaction log is a write-ahead log (WAL) that ensures durability of the data. A reply is only sent back to the client when the operation is written successfully to the transaction log and applied (visible in search/get). + +The write pattern is append and sequential (not random) IO. Note that Vespa cannot sustain a higher write rate than the underlying storage can handle. Feeding might be impacted severely if the content nodes are using network attached storage where the sync operation (for durability) has a much higher cost than on local attached storage (e.g. SSD). See [sync-transactionlog](/en/reference/applications/services/content#sync-transactionlog). + +### Document store + +Documents are written to the [document store](/en/content/proton#document-store) in all [indexing modes](/en/reference/applications/services/content#document) - this is where the copy of the document is persisted. + +Adding new documents to the document store is append-only with a sequential IO pattern. Writing a new version of a document (PUT a document that already exists) is the same as for a document ID that does not exist. The in-memory mapping from document ID to summary data file position is updated to point to the latest version in both cases. The summary files are [defragmented](/en/content/proton#defragmentation) to remove old versions of documents. + +### Attribute store + +Fields that are defined with the [attribute](/en/content/attributes) property are in-memory fields that supports in-place updates with higher [partial update](/en/writing/partial-updates) throughput than fields that are indexed with \`index\` property (avoiding read-apply-write pattern). The attribute store is a memory-only data structure that is regularly persisted to disk in the [attribute store](/en/content/proton#attributes). + +```js +schema ticker { + document ticker { + field volume type int { + indexing: summary | attribute + } + } +} +``` + +See [partial updates](/en/writing/partial-updates) for details. + +#### Redundancy settings + +To achieve memory-only updates (plus transaction log writing), make sure all attributes to update are [ready](/en/content/proton#sub-databases), meaning the content node has loaded the attribute field into memory: + +- One way to ensure this is to set [searchable copies](/en/reference/applications/services/content#searchable-copies) equal to [redundancy](/en/reference/applications/services/content#redundancy) - i.e. all nodes that has a replica of the document has loaded it as searchable +- Another way is by setting [fast-access](/en/reference/schemas/schemas#attribute) on each attribute to update + +### Index + +Changes to index fields are written to the [document store](#document-store) and the [index](/en/content/proton#index). Note that an UPDATE operation requires a read-modify-write to the document store and limits throughput. Refer to [partial updates](/en/writing/partial-updates) for more details. + +```js +schema music { + document music { + field artist type string { + indexing: summary | index + } + } +} +``` + +### Content Node Thread pools + +Several thread pools are involved when handling write operations on a content node. These are summarized in the following table. Not all mutating operations can be handled in parallel and tracking these metrics can help identify bottlenecks. For example, if you notice that feed throughput is not increasing beyond a certain CPU utilization, it might be that one of the thread pools is saturated. Metrics are available for each thread pool, see [searchnode metrics](/en/reference/operations/metrics/vespa-metric-set#searchnode-metrics) for details. + +To analyse performance and bottlenecks, the most relevant metrics are *.utilization* and *.queuesize*. In addition, *.saturation* is relevant for the [field writer](#field-writer-executor) thread pool. See [bottlenecks](#bottlenecks) for details. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Thread poolDescription
    **master**Updates the document metastore, prepares tasks to the index and summary threads, and splits a write operation into a set of tasks to update individual attributes, executed by the threads in the field writer.
    **Threads**1
    **Instances**One instance per document database.
    **Metric prefix***content.proton.documentdb.threading_service.master.*
    **index**Manages writing of index fields in the memory index. It splits a write operation into a set of tasks to update individual index fields, executed by the threads in the field writer.
    **Threads**1
    **Instances**One instance per document database.
    **Metric prefix***content.proton.documentdb.threading_service.index.*
    **summary**Writes documents to the document store.
    **Threads**1
    **Instances**One instance per document database.
    **Metric prefix***content.proton.documentdb.threading_service.summary.*
    **field writer**The threads in this thread pool are used to invert index fields, write changes to the memory index, and write changes to attributes. Index fields and attribute fields across all document databases are randomly assigned to one of the threads in this thread pool. A field that is costly to write or update might become the bottleneck during feeding.
    **Threads**Many, controlled by feeding concurrency.
    **Instances**One instance shared between all document databases.
    **Metric prefix***content.proton.executor.field_writer.*
    **shared**The threads in this thread pool are among other used to compress and de-compress documents in the document store, merge files as part of disk index fusion, and prepare for inserting a vector into a HNSW index.
    **Threads**Many, controlled by feeding concurrency.
    **Instances**One instance shared between all document databases.
    **Metric prefix***content.proton.executor.shared.*
    + +## Multivalue attribute + +[Multivalued attributes](/en/reference/schemas/schemas#field) are *weightedset*, *array of struct/map*, *map of struct/map* and *tensor*. The attributes have different characteristics, which affects write performance. Generally, updates to multivalue fields are more expensive as the field size grows: + + + + + + + + + + + + + + + + + + + + + + +
    AttributeDescription
    **weightedset**Memory-only operation when updating: read full set, update, write back. Make the update as inexpensive as possible using numeric types instead of strings, where possible Example: a weighted set of string with many (1000+) elements. Adding an element to the set means an enum store lookup/add and add/sort of the attribute multivalue map - details in attributes. Use a numeric type instead to speed this up - this has no string comparisons.
    **array/map of struct/map**Update to array of struct/map and map of struct/map requires a read from the document store and will reduce update rate - see #10892.
    **tensor**Updating tensor cell values is a memory-only operation: copy tensor, update, write back. For large tensors, this implicates reading and writing a large chunk of memory for single cell updates.
    + +## Parent/child + +[Parent documents](/en/schemas/parent-child) are global, i.e. has a replica on all nodes. Writing to fields in parent documents often simplify logic, compared to the de-normalized case where all (child) documents are updated. Write performance depends on the average number of child documents vs number of nodes in the cluster - examples: + +- 10-node cluster, avg number of children=100, redundancy=2: A parent write means 10 writes, compared to 200 writes, or 20x better +- 50-node cluster, avg number of children=10, redundancy=2: A parent write means 50 writes, compared to 20 writes, or 2.5x worse + +Hence, the more children, the better performance effect for parent writes. + +## Conditional updates + +A conditional update looks like: + +```json +{ + "update" : "id:namespace:myDoc::1", + "condition" : "myDoc.myField == \"abc\"", + "fields" : { "myTimestamp" : { "assign" : 1570187817 } } +} +``` + +If the [document store](/en/content/proton#document-store) is accessed when evaluating the condition, performance drops significantly because you get random access instead of just appending to the persisted data structures. Conditions should be evaluated using attribute values for high performance - in the example above, *myField* should be an attribute. + + +**Note:** + +If the condition uses struct or map, values are read from the document store: + + +```bash + "condition" : "myDoc.myMap{1} == 3" +``` + +This is true even though all struct fields are defined as attribute. Improvements to this is tracked in [#10892](https://github.com/vespa-engine/vespa/issues/10892). + +## Client roundtrips + +Consider the difference when sending two fields assignments to the same document: + +```json +{ + "update" : "id:namespace:doctype::1", + "fields" : { + "myMap{1}" : { "assign" : { "timestamp" : 1570187817 } } + "myMap{2}" : { "assign" : { "timestamp" : 1570187818 } } + } +} +``` + +vs. + +```json +{ + "update" : "id:namespace:doctype::1", + "fields" : { + "myMap{1}" : { "assign" : { "timestamp" : 1570187817 } } + } +} +{ + "update" : "id:namespace:doctype::1", + "fields" : { + "myMap{2}" : { "assign" : { "timestamp" : 1570187818 } } + } +} +``` + +In the first case, *one* update operation is sent from [vespa feed](/en/clients/vespa-cli) - in the latter, the client will send the second update operation *after* receiving an ack for the first. When updating multiple fields, put the updates in as few operations as possible. See [ordering details](/en/content/content-nodes#ordering). + +## Feed vs. search + +A content node normally has a fixed set of resources (CPU, memory, disk). Configure the CPU allocation for feeding vs. searching in [concurrency](/en/reference/applications/services/content#feeding) - value from 0 to 1.0 - a higher value means more CPU resources for feeding. + +In addition, you can also control priority of feed versus search, or rather how nice feeding shall be. Since a process needs root privileges for increasing feed, we have opted to reduce priority (be nice) of feeding. This is controlled by a [niceness](/en/reference/applications/services/content#feeding-niceness) number from 0 to 1.0 - higher value will favor search over feed. 0 is default. + +## Feed testing + +When testing for feeding capacity: + + + +Use [vespa feed](/en/clients/vespa-cli). + + +Test using one content node to find its capacity and where the bottlenecks are (resource utilization metrics) and Vespa metrics + + +Test feeding performance by adding feeder instances. Make sure network and CPU (content and container node) usage increases, until saturation. + + +See troubleshooting at end to make sure there are no errors. + + + +Other scenarios: Feed testing for capacity for sustained load in a system in steady state, during state changes, during query load. + +## Troubleshooting + + +**Note:** + +Use the [monitoring sample app](/en/operations/self-managed/monitoring#monitoring-with-grafana) to set up a sample system, with a document/query feed and dashboards, to familiarize with metrics. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    **Metrics**Use metrics from content nodes and look at queues - queue wait time and queue size (all metrics in milliseconds):

    {`vds.filestor.averagequeuewait.sum`}
    {`vds.filestor.queuesize`}

    Check content node metrics across all nodes to see if there are any outliers. Also check latency metrics per operation type:

    {`vds.filestor.allthreads.put.latency`}
    {`vds.filestor.allthreads.update.latency`}
    {`vds.filestor.allthreads.remove.latency`}
    **Bottlenecks**One of the threads used to handle write operations might become the bottleneck during feeding. Look at the *.utilization* metrics for all thread pools:

    {`content.proton.documentdb.threading_service.master.utilization`}
    {`content.proton.documentdb.threading_service.index.utilization`}
    {`content.proton.documentdb.threading_service.summary.utilization`}
    {`content.proton.executor.field_writer.utilization`}
    {`content.proton.executor.shared.utilization`}

    If utilization is high for field writer or shared, adjust feeding concurrency to allow more CPU cores to be used for feeding.

    For the field writer also look at the *.saturation* metric:

    {`content.proton.executor.field_writer.saturation`}

    If this is close to 1.0 and higher than *.utilization* it indicates that one of its worker threads is a bottleneck. The reason can be that this particular thread is handling a large index or attribute field that is naturally expensive to write and update. Use the custom component state API to find which index and attribute fields are assigned to which thread (identified by *executor_id*), and look at the detailed statistics of the field writer to find which thread is the actual bottleneck:

    {`state/v1/custom/component/documentdb/mydoctype/subdb/ready/index`}
    {`state/v1/custom/component/documentdb/mydoctype/subdb/ready/attributewriter`}
    `state/v1/custom/component/threadpools/field_writer
    **Failure rates**Inspect these metrics for failures during load testing:

    {`vds.distributor.updates.latency`}
    {`vds.distributor.updates.ok`}
    {`vds.distributor.updates.failures.total`}
    {`vds.distributor.puts.latency`}
    {`vds.distributor.puts.ok`}
    {`vds.distributor.puts.failures.total`}
    {`vds.distributor.removes.latency`}
    {`vds.distributor.removes.ok`}
    {`vds.distributor.removes.failures.total`}
    **Blocked feeding**This metric should be 0 - refer to feed block:

    {`content.proton.resource_usage.feeding_blocked`}
    **Concurrent mutations**Multiple clients updating the same document concurrently will stall writes:

    {`vds.distributor.updates.failures.concurrent_mutations`}

    Mutating client operations towards a given document ID are sequenced on the distributors. If an operation is already active towards a document, a subsequently arriving one will be bounced back to the client with a transient failure code. Usually this happens when users send feed from multiple clients concurrently without synchronisation. Note that feed operations sent by a single client are sequenced client-side, so this should not be observed with a single client only. Bounced operations are never sent on to the backends and should not cause elevated latencies there, although the client will observe higher latencies due to automatic retries with back-off.
    **Wrong distribution**{`vds.distributor.updates.failures.wrongdistributor`}

    Indicates that clients keep sending to the wrong distributor. Normally this happens infrequently (but is *does* happen on client startup or distributor state transitions), as clients update and cache all state required to route directly to the correct distributor (Vespa uses a deterministic CRUSH-based algorithmic distribution). Some potential reasons for this:

    1. Clients are being constantly re-created with no cached state.
    2. The system is in some kind of flux where the underlying state keeps changing constantly.
    3. The client distribution policy has received so many errors that it throws away its cached state to start with a clean slate to e.g. avoid the case where it only has cached information for the bad side of a network partition.
    4. The system has somehow failed to converge to a shared cluster state, causing parts of the cluster to have a different idea of the correct state than others.
    **Cluster out of sync***update_puts/gets* indicate "two-phase" updates:

    {`vds.distributor.update_puts.latency`}
    {`vds.distributor.update_puts.ok`}
    {`vds.distributor.update_gets.latency`}
    {`vds.distributor.update_gets.ok`}
    {`vds.distributor.update_gets.failures.total`}
    {`vds.distributor.update_gets.failures.notfound`}

    If replicas are out of sync, updates cannot be applied directly on the replica nodes as they risk ending up with diverging state. In this case, Vespa performs an explicit read-consolidate-write (write repair) operation on the distributors. This is usually a lot slower than the regular update path because it doesn't happen in parallel. It also happens in the write-path of other operations, so risks blocking these if the updates are expensive in terms of CPU. Replicas being out of sync is by definition not the expected steady state of the system. For example, replica divergence can happen if one or more replica nodes are unable to process or persist operations. Track (pending) merges:

    {`vds.idealstate.buckets`}
    {`vds.idealstate.merge_bucket.pending`}
    {`vds.idealstate.merge_bucket.done_ok`}
    {`vds.idealstate.merge_bucket.done_failed`}
    diff --git a/en/performance/sizing-search.html b/en/performance/sizing-search.html deleted file mode 100644 index ff1ae6cbea..0000000000 --- a/en/performance/sizing-search.html +++ /dev/null @@ -1,733 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Vespa Serving Scaling Guide" ---- - -

    Vespa can scale in multiple scaling dimensions:

    -
      -
    • Scale document volume and write volume
    • -
    • Scale query throughput
    • -
    • Scale serving latency to meet service level agreements (SLA)
    • -
    -

    The question one tries to answer during a sizing exercise is: -What the total cost would be to serve a use case using Vespa?.

    -

    -This document helps sizing an application correctly with as low cost as possible. -Vespa is used to implement many use cases, and this document is relevant for all of them: -

    - -

    -With Vespa, it is possible to do benchmarking on a few nodes -to infer the overall performance and cost of the chosen deployment architecture, -and as Vespa supports live resizing, -it is easy to scale from a prototype to a full production size deployment. -

    -This document covers sizing and capacity planning for serving, -see feed performance sizing for feed performance sizing -and Vespa serving feature tuning. -It also covers the following topics: -

    - - -

    Data distribution in Vespa - flat versus grouped

    -

    -The basic element in the Vespa search architecture is a content node, which is part of a content cluster. -A Vespa deployment can have several content clusters, which can be scaled independently. -

    -A content node holds a fraction of the entire data corpus. -Data is distributed to nodes using a distribution algorithm, -which goal is to uniformly distribute data over the set of nodes. -The goal is also to avoid distribution skew, -while at the same time supporting re-distribution of data, -with minimal data movement, -if the size of the content cluster changes. -Read content cluster elasticity to learn -how data is distributed across nodes, and how adding or removing nodes works. -See also Vespa's consistency model documentation. -

    - - -

    Flat content distribution

    -Flat content distribution -

    -With a flat distribution, the content is distributed to content nodes using the -ideal state distribution algorithm. -A query is dispatched in parallel from a container instance to -all content nodes in the content cluster. -Each content node searches the active part of the ready sub-database. -The above figure illustrates a deployment using 4 nodes with redundancy 2 and searchable-copies 2 - -see the availability section. -

    -When using flat data distribution, the only way to scale query throughput is to reduce the search latency. -Given a fixed occupancy (users, load clients), -this relationship between query throughput and latency is described by -Little's law - -more on this in content cluster scalability model section. -

    - - -

    Grouped content distribution

    -Grouped content distribution -

    -With a grouped distribution, content is distributed to a configured set of groups, -such that the entire document collection is contained in each group. -A group contains a set of content nodes where the content is -distributed using the distribution algorithm. -In the above illustration, there are 4 nodes in total, 2 groups with 2 nodes in each group. -redundancy is 2 and searchable-copies is also 2. -As can be seen from the figure with this grouped configuration, -the content nodes only have a populated ready sub-database. -A query is dispatched in parallel to all nodes in one group at a time -using a dispatch-policy. -The default policy is adaptive, which load balances over the set of groups, aiming at even latency. -

    - - -

    High Data Availability

    -

    -Ideally, the data is available and searchable at all times, even during node failures. -High availability costs resources due to data replication. -How many replicas of the data to configure -depends on what kind of availability guarantees the deployment should provide. -Configure availability vs cost: -

    - - - - - - - - - - -
    redundancy -

    - Defines the total number of copies of each piece of data the cluster will store and maintain to avoid data loss. - Example: with a redundancy of 2, the system tolerates 1 node failure - before any further node failures may cause data to become unavailable. -

    -
    - searchable-copies -

    - Configures how many of the copies (as configured with redundancy) - to be indexed (ready) at any time. - Configuring searchable-copies to be less than redundancy saves resources (memory, disk, cpu), - as not all copies are indexed (ready). - In case of node failure, the remaining nodes needs to index the - not ready documents which belonged to the failed node. - In this transition period, the search has reduced search coverage. -

    -
    - - -

    Content node database

    -Content node databases -

    -The above figure illustrates the three -sub-databases inside a Vespa content node. -

    -
      -
    • The documents in the Ready DB are indexed, - but only the documents in Active state are searchable. - In a flat distributed system there is only one active instance of the same document, - while with grouped distribution there is one active instance per group.
    • -
    • The documents in the Not Ready DB are stored but not indexed.
    • -
    • The documents in the Removed DB are stored but blocklisted, hidden from search. - The documents are permanently deleted from storage by - Proton maintenance jobs.
    • -
    -

    -If the availability guarantees tolerate temporary search coverage loss during node -failures (e.g. searchable-copies=1), this is by far the most optimal for serving performance - -the query work per node is less, as index structures like posting lists are smaller. -The index structures only contains documents in Active state, -not including Not Active documents. -

    -With searchable-copies=2 and redundancy=2, -each replica is fully indexed on separate content nodes. -Only the documents in Active state are searchable, -the posting lists for a given term are (up to) doubled as compared to searchable-copies=1. -

    - - - -

    Life of a query in Vespa

    -

    - Find an overview in query execution: -

    -Query execution - from query to response -

    -Vespa executes a query in two protocol phases -(or more if using result grouping features) -to optimize the network footprint of the parallel query execution. -The first protocol phase executes the query in parallel over content nodes in a group to find the global top hits, -the second protocol phase fetches the data of the global top hits. -

    -During the first phase, -content nodes match and rank documents using the selected rank-profile/model. -The hits are returned to the stateless container for merging -and potentially blending when multiple content clusters are involved. -

    -When the global top ranking documents are found, -the second protocol phase fetch the summary data for the global best hits -(e.g. summary snippets, the original field contents, and ranking features). -By doing the query in two protocol -phases one avoids transferring summary data for hits which will not make it into the global best hits. -

    -Components Involved in query execution: -

    -
      -
    • Container -
        -
      • Parses the API request - and the query and run time context features.
      • -
      • Modifies the query according to the schema specification (stemming, etc.) for a text search application - or creating run time query or user context tensors for an ML serving application.
      • -
      • Invokes chains of custom container components/plugins - which can work on the request and query input and also the results.
      • -
      • Dispatching of query to content nodes in the content cluster(s) for parallel execution. - With flat distribution queries are dispatched to all content nodes, - while with a grouped distribution the query is dispatched to all content nodes within a group - and the queries are load-balanced between the groups using a - dispatch-policy.
      • -
      • Blending of global top ranking results from cluster(s).
      • -
      • Fetching the top ranking results with document summaries from cluster(s).
      • -
      • Result processing and possible top-k re-ranking and finally rendering of results back to client.
      • -
      -
    • -
    • Content node (Proton) -
        -
      • Finding all documents matching the query specification. - For an ML serving use case, the selection might be a subset of the content pool - (e.g. limit the model to only be evaluated for content-type video documents), - while for a text ranking application it might be a - WAND text matching query.
      • -
      • Calculating the score (which might be a text ranking relevancy score or - the inferred score of a Machine Learned model) of each hit, - using the chosen rank-profile. See ranking with Vespa.
      • -
      • Aggregating information over all the generated hits using result grouping.
      • -
      • Sorting hits on relevancy score (text ranking) or inference score (e.g. ML model serving), - or on attribute(s). - See max-hits-per-partition and top-k-probability in - dispatch tuning - for how to tune how many hits to return.
      • -
      • Processing and returning the document summaries of the selected top hits - (during summary fetch phase after merging and blending has happened on levels above).
      • -
      -
    • -
    -

    -The detailed execution inside Proton during the matching and ranking first protocol phase is: -

    -
      -
    1. Build up the query tree from the serialized network representation.
    2. -
    3. Lookup the query terms in the index and B-tree dictionaries - and estimate the number of hits each term and parts of the query tree will produce. - Terms which search attribute fields without fast-search - will be given a hit count estimate to the total number of documents.
    4. -
    5. Optimize and re-arrange the query tree for most efficient performance, trying to move terms or - operators with the lowest hit ratio estimate first in the query tree.
    6. -
    7. Prepare for query execution, by fetching posting lists from the index and B-tree structures.
    8. -
    9. Multithreaded execution per search starts using the above information. - Each thread will do its own thread local setup.
    10. -
    11. Each search thread will evaluate the query over its document space.
    12. -
    13. The search threads complete first phase and agree which hits will continue to second phase ranking - (if enabled per the used rank-profile). - The threads operate over a shared heap with the global top ranking hits.
    14. -
    15. Each thread will the complete second phase and grouping/aggregation/sorting.
    16. -
    17. Merge all threads results and return up to the container.
    18. -
    -

    -Container clusters are stateless and easy to scale horizontally, -and don't require any data distribution during re-sizing. -The set of stateful content nodes can be scaled independently -and re-sized which requires re-distribution of data. -Re-distribution of data in Vespa is supported and designed to be done without significant serving impact. -Altering the number of nodes or groups in a Vespa content cluster does not require re-feeding of the corpus, -so it's easy to start out with a sample prototype and scale it to production scale workloads. -

    - - - -

    Content cluster scalability model

    -

    -Vespa is a parallel computing platform -where the work of matching and ranking is parallelized across a set of nodes and processors. -The speedup one can get by altering the number of nodes in a Vespa content group follows -Amdahl's law, -which is a formula used to find the maximum improvement possible by improving a particular part of a system. -In parallel computing, Amdahl's law is mainly used to predict the theoretical -maximum speedup for program processing using multiple processors. -In Vespa, as in any parallel computing system, -there is work which can be parallelized and work which cannot. -The relationship between these two work types determine how to best scale the system, -using a flat or grouped distribution. -

    - - - - - - - - - - - -
    static query work -

    - Portion of the query work on a content node that does not depend on the - number of documents indexed on the node. - This is an administrative overhead caused by system design and abstractions, - e.g. number of memory allocations per query term. - Typically, a large query tree means higher static work, - and this work cannot be parallelized over multiple processors, threads or nodes. - The static query work portion is described in step 1 to 4 and step 9 in the detailed life of a query explanation above. -

    -
    dynamic query work -

    - Portion of the query work on a content node that depends on the number - of documents indexed and active on the node. - This portion of the work scales mostly linearly with the number of matched documents. - The dynamic query work can be parallelized over multiple processors and nodes. - Referenced later as DQW. - The DQW also depends on the phase two protocol summary fill - where the actual contents of the global best documents is fetched from the content nodes - which produced the hit in the first protocol phase. -

    -
    Total query work -

    - The total query work is given as the dynamic query work (DQW) + static query work (SQW). -

    -
    -

    -Adding content nodes to a content cluster (keeping the total document volume fixed) with flat distribution -reduces the dynamic query work per node (DQW), -but does not reduce the static query work (SQW). -The overall system cost also increases as you need to rent another node. -

    -Since DQW depends and scales almost linearly with the number of documents on the content nodes, -one can try to distribute the work over more nodes. -Amdahl's law specifies that the maximum speedup one achieve by parallelizing the -dynamic work (DQW) is given by the formula: -

    - $$\text{max_speedup}_{\text{group}} = \frac{1}{1 - \frac{DQW}{SQW+DQW}}$$ -

    -For example, if one by inspecting metrics see that the DQW = 0.50, -the maximum speedup one can get by increasing parallelism by using more nodes and decreasing DQW is 2. -With fixed occupancy (number of users, clients or load), -Little's Law' -tells us that one could achieve two times the throughput if one is able to speed up the latency by a factor of two: -

    - $$\frac{1}{1 - \frac{0.5}{0.5+0.5}} = 2$$ -

    -When SQW is no longer significantly less than DQW, -adding more nodes in a flat distributed cluster just increases the overall system cost. -This without any serving performance gain, -except increasing overall supported feed throughput, -which increases almost linearly with number of nodes. -

    -Two different DQW/(DQW+SQW) factors are illustrated in the figures below. -The overall query work TQW is the same for both cases (10 ms), -but the DQW portion of the TQW is different. -The throughput (QPS) is a function of the latency (Little's Law) -and the number of cpu cores * nodes. -Using 1 node with 24 v-cpu cores and 10 ms service time (TQW), -one can expect reaching close to 2400 QPS at 100% utilization -(unless there are other bottlenecks like network or stateless container processing). -

    -
    - Scaling throughput/latency where DQW/(SQW+DQW)=0.5 - Scaling throughput/latency where DQW/(SQW+DQW)=0.9 -
    -

    -In the first figure the overall latency is 10 ms, -but the dynamic query work (latency) is only 50% -and given Amdahl's law it follows that the maximum speedup one can get is two. -This is true regardless of how many processors or nodes the dynamic query work is parallelized over. -No matter how many nodes one adds, one don't get above 4800 queries/s. -The only thing one achieve by adding more nodes -is increasing the cost without any performance benefits. -

    -In the second figure there is a system where the dynamic work portion is much higher (0.9), -and the theoretical maximum speedup becomes bound by 10x as given by Amdahl's law. -Note that both figures are with a single flat distributed content cluster with a fixed document volume. -

    -Given the theory, one can derive two rules of thumb for scaling throughput and latency:

    - - - - - - - - - - -
    Add nodes in a flat distributionWhen DQW/TQW is large (close to 1.0), - throughput QPS can be scaled by just adding more content nodes in a system using flat distribution. - This will reduce the number of documents per node, - and thus reduce the DQW per node.
    Add groups using grouped distributionWhen DQW/TQW is low, one can no longer just add more content nodes to scale throughput - and must instead use a grouped distribution to scale throughput.
    - - - -

    Scaling latency in a content group

    -

    -Irrespective of using single group (flat distribution) or multiple groups, -the serving latency depends on the factors already described; DQW and SQW. -For use cases where DQW dominates the total query work TQW, -one can effectively scale latency down by parallelizing the DQW over more nodes per group. -

    -It is important to decide on a latency service level agreement (SLA) -before sizing the Vespa deployment for the application and query features. -A latency SLA is often specified as a latency percentile at a certain throughput level - example:

    -
      -
    • SLA Example 1: 95.0 percentile < 100 ms @ 2000 QPS
    • -
    • SLA Example 2: 95.0 percentile < 40 ms @ 8000 QPS
    • -
    -

    -Different use cases might have different performance characteristics, -depending on how the dynamic work query portion is compared to the static query work portion. -This graph illustrates the relationship between overall latency versus number of documents indexed per node for two -different use cases. -

    -
    - Latency vs document count per node -
    -
      -
    • -

      - For the yellow use case the measured latency is almost independent of the total document volume. - This is called sublinear latency scaling, which calls for scaling up using better flavor - specification instead of scaling out. -

      -

      - The observed latency at 10M documents per node is almost the same as with 1M documents per node. - Such a use case would be most cost-effective by storing as many documents as possible - (within the memory/disk/feeding constrains set by the concurrency settings and node flavor) - and scale throughput by using a grouped distribution. - Efficient query operators which are sublinear has scaling properties - like the yellow case. Example of such query operators include - the wand operators, and - approximate nearest neighbor search operator -

      -
    • -
    • -

      - For the blue use case the measured latency shows a clear correlation with the document volume. - This is a case where the dynamic query work portion is high, - and adding nodes to the flat group will reduce the serving latency. - The sweet spot is found where targeted latency SLA is achieved. - This sweet spot depends on which model or ranking features are in use, - e.g. how expensive the model is per retrieved or ranked document. -

      -

      - For example, a GBDT xgboost model with 3000 trees - might breach the targeted latency SLA already at 200K documents, - while a 300 tree model might be below the SLA at 2M documents. - Using exact nearest neighbor search - has scaling properties like the blue case. - See also feature tuning. -

      -
    • -
    - - -

    - Reduce latency with multithreaded per-search execution

    -

    -It is possible to reduce latency of queries -where the dynamic query work portion is high. -Using multiple threads per search for a use case where the static query work is high -will be as wasteful as adding nodes to a flat distribution. -

    -
    - Content node search latency vs threads per search -
    -

    -Using more threads per search will reduce latency as long as the dynamic portion of the query cost is high -compared to the static query cost. The reduction in latency comes with the cost of higher cpu utilization. -

    -

    -A search request with four threads will occupy all four threads until the last thread has completed, -and the intra-node per thread document space partitioning must be balanced to give optimal results. -

    -

    -For rank profiles with second phase ranking, see phased ranking, -the hits from first-phase ranking are rebalanced so that each matching thread scores about -the same amount of hits using the second phase ranking expression. -

    -

    - From the above examples with the blue and yellow use case it follows that -

    -
      -
    • Linear exact nearest neighbor search latency can be reduced by using more threads per search
    • -
    • Sublinear approximate nearest neighbor search latency does not benefit from using more threads per search
    • -
    -

    -By default the number of threads per search is one, -as that gives the best resource usage measured as CPU resources used per query. -The optimal threads per search depends on the query use case, -and should be evaluated by benchmarking. -

    -The threads per search settings globally is tuned by -persearch. -This can be overridden to a lower value in -rank profiles -so that different query use cases can use different number of threads per search. -Using multiple threads per search allows better utilization of -multicore cpu architectures for low query volume applications. -The persearch number in services should always be equal to the highest -num-threads-per-search in your rank profiles. -Setting it higher reduces the maximum number of concurrent queries without any latency benefit. -

    - -

    Thread configuration

    -

    - The search and persearch settings together determine - the maximum number of queries that can execute concurrently on a content node: -

    -
    -max concurrent queries = search / persearch
    -
    -

    - For example, with search=64 (the default) and persearch=8, - only 8 queries can execute simultaneously. - Queries arriving when all slots are busy are queued in the match engine, - adding latency without any backpressure to the caller. - This can significantly reduce throughput even when CPU cores are available. -

    -

    - To get started, set search to be 2x number of CPU cores. - persearch should never be more than number of cores. - summary should be equal to number of cores. - Start with the default value of 1 for persearch and only increase it for lower query latency, - as it will reduce throughput and efficiency. -

    -

    - If you increase persearch, consider increasing search proportionally - to maintain enough concurrent query slots. - For example, if you need persearch=8 on a 16-core machine, - setting search=128 gives 16 concurrent query slots — - enough that queries using fewer threads (via per-rank-profile - num-threads-per-search - overrides) are not starved of executor slots. -

    -

    - Monitor content.proton.executor.match.utilization to detect when - the match engine is saturated — a sustained value at or near 1.0 indicates - all executor slots are busy and queries are queuing. - A high content.proton.executor.match.queuesize.max relative to - search / persearch confirms the bottleneck. -

    - -

    Scaling the size of the retrievable unit

    -

    -Retrieving units that are too small or too large can have a drastic impact on both the quality and -performance of your search. Consider an example where we want to search in PDF files: Creating one -document per PDF file seems like a logical solution, and with Vespa this is possible - up to a point. -But as system architects we must consider the potential edge cases: some files may be entire books, -or long reports with many hundreds or even thousands of pages. -

    The current max document size is limited by the max protobuf message size of 2 GB, but we advise -staying well below this limit, at least < 200MB and ideally < 1MB for even, predictable performance. -For reference to scale, the complete text of the bible is about 4MB. - -

    Split too-large documents into smaller units for better search quality and performance! -Natural subdivisions like chapters, parts or sections are good candidates for splitting into separate -Vespa documents. - -

    When documents are too large

    -If each document is a complete PDF file and some are very large, what problems could we run into? -

    -Usefulness of the result - knowing that there are relevant parts somewhere in -several hundred pages of text is not very helpful to the user. As of 2025, this is still -true also if the "user" is an LLM in a RAG or agentic workflow. -

    -We can improve the usefulness by providing dynamic snippets -or returning per-chunk similarity scores as feature values -to be able to identify the most relevant portions of the returned summary. -

    -Performance - Returning large document values in the query response over HTTP has a significant -cost, both in CPU time spent in rendering the response, compression, and network transfer time. This -can easily become the largest contribution to the total end-to-end latency. -

    -Large documents can also contribute to poor performance in indexing and query execution, or greatly -increase the amount of temporary memory required for complex ranking expressions like multi-dimensional ColBert maxsim. -As document are processed, indexed, stored and ranked as individual units, working on a few very large documents -at a time may not offer the system enough opportunity to parallelize and result in poor, uneven utilization -of resources, and even a small fraction of very large documents may impact your mean (and especially higher percentile) -latencies both for processing and query execution. - -

    When documents are too small

    -What problems can occur if documents are very small? Consider indexing small fragments of text, like a single sentence or even a word. -

    -Granularity - As the size of text fragments decrease, we are less likely to find good matches for queries as the relevant terms -or context is spread across multiple documents. The response may not contain enough information to resolve the user information need, -or to even judge if it is likely to resolve the need if the source is examined in full. This problem is described both in -traditional information retrieval literature -and has also been a popular topic in recent years as "chunking" for semantic search. -

    -Overhead - splitting a document into very small pieces means that more resources will be spent on per-document overhead. -Shared metadata like e.g. the abstract or access permissions of a document will be replicated many times, and updates/deletes to the -source document or its metadata must fan-out to all the sub-documents, increasing write load. Unlike too-large documents, having a -fraction of very small documents is fine, what matters for efficiency is that the average size is not too small. - -

    Scaling document volume per node

    -

    -One want to fit as many documents as possible into a node given the node constrains -(e.g. available cpu, memory, disk) while maintaining: -

      -
    • The targeted search latency SLA
    • -
    • The targeted feed and update rate, and feed latency
    • -
    -

    -With the latency SLA in mind, benchmark with increasing number of documents per node -and watch system level metrics and Vespa metrics. -If latency is within the stated latency SLA and the system meets the targeted sustained feed rate, -overall cost is reduced by fitting more documents into each node -(e.g. by increasing memory, cpu and disk constraints set by the node flavor). -

    -With larger fan-out using more nodes to partition the data also overcomes higher tail latency -as search waits for all results from all nodes. Therefore, the overall execution time depends on -the slowest node at the time of the query. In such cases with large fan-out, using -adaptive timeout is recommended -to keep tail latency in check. -

    -Vespa will block feed operations -if resource limits have been reached. -

    - - -

    Disk usage sizing

    -

    -Disk usage of a content node increases as the document volume increases: -The disk usage per document depends on various factors like the number of schemas, -the number of indexed fields and their settings, -and most important the size of the fields that are indexed and stored. -The simplest way to determine the disk usage is to simply index documents -and watch the disk usage along with the relevant metrics. -The redundancy (number of copies) impact the disk usage footprint, obviously. -

    -Note that content node maintenance jobs -temporarily increases disk usage. -E.g. index fusion is an example, where new index files are written, -causing an increase in used disk space while running. -Space used depends on configuration and data - headroom must include the temporary usage. -See metrics for capacity planning. -

    - - -

    Memory usage sizing

    -

    -The memory usage on a content node increases as the document volume increases. -The memory usage increases almost linearly with the number of documents. -The vespa-proton-bin process (content node) uses the full 64-bit virtual address space, -so the virtual memory usage reported might be high, -as both index and summary files are mapped into memory using mmap -and pages are paged into memory as needed. -

    -The memory usage per document depends on the number of fields, -the raw size of the documents and how many of the fields are defined as -attributes. -Also see metrics for capacity planning. -

    - - -

    Scaling Throughput

    -

    -As seen in the previous sections, -when the static query work (SQW) becomes large, -scale throughput using grouped distribution. -Regardless, if throughput is scaled by grouped distribution for use cases -with high static query work portion -or a flat distribution for high dynamic query work portion, -one should identify how much throughput the total system supports. -

    -Finding where the latency starts climbing exponentially versus throughput -is important in order to make sure that the deployed system is scaled well below this throughput threshold. -Also, that it has capacity to absorb load increases over time, -as well as having sufficient capacity to sustain node outages during peak traffic. -

    -At some throughput level some resource(s) in the system will be fully saturated, -and requests will be queued up causing latency to spike up, -as requests are spending more time waiting for the saturated resource. -

    -This behaviour is illustrated in the figure below: -

    -
    - Latency vs throughput -
    -

    -A small increase in serving latency is observed as throughput increases, -until saturated at approximately 2200 QPS. -Pushing more queries than this only increases queueing time, -and latency increases sharply. -

    - - - -

    Scaling for failures and headroom

    -

    -It is important to also measure behaviour under non-ideal circumstances, -to avoid getting too good results. -E.g., by simulating node failures or node replacements, -verifying feeding concurrency versus search and serving. -

    -Generally, the higher utilization a system has in production, -the more fragile it becomes when changing query patterns or ranking models. -

    -The target system utilization should be kept sufficiently low for the response times to be reasonable and within latency SLA, -even with some extra traffic occurring at peak hours. -See also graceful degradation. -Also see sizing write versus read. -

    - - - -

    Metrics for Vespa Sizing

    -

    -The relevant Vespa Metrics for measuring the cost factors, -in addition to system level metrics like cpu util, are: -

    -Metric capturing static query work (SQW) at content nodes -
    -content.proton.documentdb.matching.rank_profile.query_setup_time
    -
    -Metric capturing dynamic query work (DQW) at content nodes -
    -content.proton.documentdb.matching.rank_profile.query_latency
    -
    -

    -By sampling these metrics, -one can calculate the theoretical speedup we can achieve by increasing number of nodes using flat distribution, -by using Amdahl's law: -

    - $$\text{max_speedup}_{\text{}} = \frac{1}{1 - \frac{match\_time}{query\_setup\_time+match\_time}}$$ -

    -In addition, the following metrics are used to find number of matches per query, per node: -

    -content.proton.documentdb.matching.rank_profile.docs_matched
    -content.proton.documentdb.matching.rank_profile.queries
    -
    -Disk usage: -
      -
    • documentdb: vespa.content.proton.documentdb.disk_usage.last
    • -
    • transaction log: vespa.content.proton.transactionlog.disk_usage.last
    • -
    -Memory usage: -
      -
    • documentdb: vespa.content.proton.documentdb.memory_usage.allocated_bytes.last
    • -
    diff --git a/en/performance/sizing-search.mdx b/en/performance/sizing-search.mdx new file mode 100644 index 0000000000..38c4290c39 --- /dev/null +++ b/en/performance/sizing-search.mdx @@ -0,0 +1,388 @@ +--- +title: "Vespa Serving Scaling Guide" +sidebarTitle: "Serving sizing guide" +--- + +*Vespa can scale in multiple scaling dimensions:* + +- Scale document volume and write volume +- Scale query throughput +- Scale serving latency to meet service level agreements (SLA) + +The question one tries to answer during a sizing exercise is: *What the total cost would be to serve a use case using Vespa?*. + +This document helps sizing an application correctly with as low cost as possible. Vespa is used to implement many use cases, and this document is relevant for all of them: + +- Serving a [text ranking](/en/ranking/nativerank) use case or a [recommendation](/en/learn/tutorials/news-1-deploy-an-application) use case +- Serving a machine learned model, e.g., an [ONNX](/en/ranking/onnx), [XGBoost](/en/ranking/xgboost), or [LightGBM](/en/ranking/lightgbm) model + +With Vespa, it is possible to do benchmarking on a few nodes to infer the overall performance and cost of the chosen deployment architecture, and as Vespa supports [live resizing](/en/content/elasticity), it is easy to scale from a prototype to a full production size deployment. + +This document covers sizing and capacity planning for serving, see [feed performance sizing](/en/performance/sizing-feeding) for feed performance sizing and [Vespa serving feature tuning](/en/performance/feature-tuning). It also covers the following topics: +- [Data distribution](#data-distribution) in Vespa and how it impacts serving +- [Scaling Serving Latency and Throughput](#content-cluster-scalability-model) in Vespa +- [Scaling Data Volume](#scaling-document-volume-per-node) in Vespa + +## Data distribution in Vespa - flat versus grouped + +The basic element in the Vespa search architecture is a content node, which is part of a content cluster. A Vespa deployment can have several content clusters, which can be scaled independently. + +A content node holds a fraction of the entire data corpus. Data is distributed to nodes using a [distribution algorithm](/en/content/idealstate), which goal is to uniformly distribute data over the set of nodes. The goal is also to avoid distribution skew, while at the same time supporting re-distribution of data, with minimal data movement, if the size of the content cluster changes. Read [content cluster elasticity](/en/content/elasticity) to learn how data is distributed across nodes, and how adding or removing nodes works. See also [Vespa's consistency model](/en/content/consistency) documentation. + +### Flat content distribution + +![Flat content distribution](/assets/img/flat-content-distribution.svg) + +With a flat distribution, the content is distributed to content nodes using the [ideal state distribution algorithm](/en/content/idealstate). A query is dispatched in parallel from a container instance to **all** content nodes in the content cluster. Each content node searches the *active* part of the *ready* sub-database. The above figure illustrates a deployment using 4 nodes with *redundancy* 2 and *searchable-copies* 2 - see the [availability](#high-data-availability) section. + +When using flat data distribution, the only way to scale query throughput is to reduce the search latency. Given a fixed occupancy (users, load clients), this relationship between query throughput and latency is described by [Little's law](https://en.wikipedia.org/wiki/Little%27s_law) - more on this in [content cluster scalability model](#content-cluster-scalability-model) section. + +### Grouped content distribution + +![Grouped content distribution](/assets/img/grouped-content-distribution.svg) + +With a grouped distribution, content is distributed to a configured set of *groups*, such that the entire document collection is contained in each group. A *group* contains a set of content nodes where the content is distributed using the [distribution algorithm](/en/content/idealstate). In the above illustration, there are 4 nodes in total, 2 groups with 2 nodes in each group. *redundancy* is 2 and *searchable-copies* is also 2. As can be seen from the figure with this grouped configuration, the content nodes only have a populated ready sub-database. A query is dispatched in parallel to all nodes in **one group** at a time using a [dispatch-policy](/en/reference/applications/services/content#dispatch-policy). The default policy is *adaptive*, which load balances over the set of groups, aiming at even latency. + +### High Data Availability + +Ideally, the data is available and searchable at all times, even during node failures. High availability costs resources due to data replication. How many replicas of the data to configure depends on what kind of availability guarantees the deployment should provide. Configure availability vs cost: + + + + + + + + + + + + + + + + + + +
    redundancyDefines the total number of copies of each piece of data the cluster will store and maintain to avoid data loss. Example: with a redundancy of 2, the system tolerates 1 node failure before any further node failures may cause data to become unavailable.
    searchable-copiesConfigures how many of the copies (as configured with *redundancy*) to be indexed (*ready*) at any time. Configuring *searchable-copies* to be less than *redundancy* saves resources (memory, disk, cpu), as not all copies are indexed (*ready*). In case of node failure, the remaining nodes needs to index the *not ready* documents which belonged to the failed node. In this transition period, the search has reduced search coverage.
    + +### Content node database + +![Content node databases](/assets/img/proton-databases.svg) + +The above figure illustrates the three [sub-databases](/en/content/proton#sub-databases) inside a Vespa content node. + +- The documents in the **Ready** DB are indexed, but only the documents in **Active** state are searchable. In a flat distributed system there is only one active instance of the same document, while with grouped distribution there is one active instance per group. +- The documents in the **Not Ready** DB are stored but not indexed. +- The documents in the **Removed** DB are stored but blocklisted, hidden from search. The documents are permanently deleted from storage by [Proton maintenance jobs](/en/content/proton#proton-maintenance-jobs). + +If the availability guarantees tolerate temporary search coverage loss during node failures (e.g. *searchable-copies*\=1), this is by far the most optimal for serving performance - the query work per node is less, as index structures like posting lists are smaller. The index structures only contains documents in *Active* state, not including *Not Active* documents. + +With *searchable-copies*\=2 and *redundancy*\=2, each replica is fully indexed on separate content nodes. Only the documents in *Active* state are searchable, the posting lists for a given term are (up to) doubled as compared to *searchable-copies*\=1. + +## Life of a query in Vespa + +Find an overview in [query execution](/en/querying/query-api#query-execution): + +![Query execution - from query to response](/assets/img/query-to-response.svg) + +Vespa executes a query in two protocol phases (or more if using [result grouping features](/en/querying/grouping)) to optimize the network footprint of the parallel query execution. The first protocol phase executes the query in parallel over content nodes in a group to find the global top hits, the second protocol phase fetches the data of the global top hits. + +During the first phase, content nodes match and [rank](/en/basics/ranking) documents using the selected rank-profile/model. The hits are returned to the stateless container for merging and potentially blending when multiple content clusters are involved. + +When the global top ranking documents are found, the second protocol phase fetch the summary data for the global best hits (e.g. summary snippets, the original field contents, and ranking features). By doing the query in two protocol phases one avoids transferring summary data for hits which will not make it into the global best hits. + +Components Involved in query execution: + +- **Container** + - Parses the [API](/en/querying/query-api) request and the [query](/en/querying/query-language) and run time context features. + - Modifies the query according to the schema specification (stemming, etc.) for a text search application or creating run time query or user context tensors for an ML serving application. + - Invokes chains of custom [container components/plugins](/en/applications/components) which can work on the request and query input and also the results. + - Dispatching of query to content nodes in the content cluster(s) for parallel execution. With flat distribution queries are dispatched to all content nodes, while with a grouped distribution the query is dispatched to all content nodes within a group and the queries are load-balanced between the groups using a [dispatch-policy](/en/reference/applications/services/content#dispatch-policy). + - Blending of global top ranking results from cluster(s). + - Fetching the top ranking results with document summaries from cluster(s). + - Result processing and possible top-k re-ranking and finally rendering of results back to client. +- **Content node (Proton)** + - Finding all documents matching the [query specification](/en/querying/query-api). For an ML serving use case, the selection might be a subset of the content pool (e.g. limit the model to only be evaluated for content-type video documents), while for a text ranking application it might be a [WAND](/en/ranking/wand) text matching query. + - Calculating the score (which might be a text ranking relevancy score or the inferred score of a Machine Learned model) of each hit, using the chosen rank-profile. See [ranking with Vespa](/en/basics/ranking). + - Aggregating information over all the generated hits using [result grouping](/en/querying/grouping). + - Sorting hits on relevancy score (text ranking) or inference score (e.g. ML model serving), or on attribute(s). See *max-hits-per-partition* and *top-k-probability* in [dispatch tuning](/en/reference/applications/services/content#dispatch-tuning) for how to tune how many hits to return. + - Processing and returning the document summaries of the selected top hits (during summary fetch phase after merging and blending has happened on levels above). + +The detailed execution inside Proton during the matching and ranking first protocol phase is: + + + +Build up the query tree from the serialized network representation. + + +Lookup the query terms in the index and B-tree dictionaries and estimate the number of hits each term and parts of the query tree will produce. Terms which search attribute fields without [fast-search](/en/content/attributes#fast-search) will be given a hit count estimate to the total number of documents. + + +Optimize and re-arrange the query tree for most efficient performance, trying to move terms or operators with the lowest hit ratio estimate first in the query tree. + + +Prepare for query execution, by fetching posting lists from the index and B-tree structures. + + +Multithreaded execution per search starts using the above information. Each thread will do its own thread local setup. + + +Each search thread will evaluate the query over its document space. + + +The search threads complete first phase and agree which hits will continue to second phase ranking (if enabled per the used rank-profile). The threads operate over a shared heap with the global top ranking hits. + + +Each thread will the complete second phase and grouping/aggregation/sorting. + + +Merge all threads results and return up to the container. + + + +[Container](/en/applications/components) clusters are stateless and easy to scale horizontally, and don't require any data distribution during re-sizing. The set of stateful content nodes can be scaled independently and [re-sized](/en/content/elasticity) which requires re-distribution of data. Re-distribution of data in Vespa is supported and designed to be done without significant serving impact. Altering the number of nodes or groups in a Vespa content cluster does not require re-feeding of the corpus, so it's easy to start out with a sample prototype and scale it to production scale workloads. + +## Content cluster scalability model + +Vespa is a parallel computing platform where the work of matching and ranking is parallelized across a set of nodes and processors. The speedup one can get by altering the number of nodes in a Vespa content group follows [Amdahl's law](https://en.wikipedia.org/wiki/Amdahl%27s_law), which is a formula used to find the maximum improvement possible by improving a particular part of a system. In parallel computing, *Amdahl's law* is mainly used to predict the theoretical maximum speedup for program processing using multiple processors. In Vespa, as in any parallel computing system, there is work which can be parallelized and work which cannot. The relationship between these two work types determine how to best scale the system, using a flat or grouped distribution. + + + + + + + + + + + + + + + + + + + + + + +
    **static query work**Portion of the query work on a content node that does not depend on the number of documents indexed on the node. This is an administrative overhead caused by system design and abstractions, e.g. number of memory allocations per query term. Typically, a large query tree means higher static work, and this work cannot be parallelized over multiple processors, threads or nodes. The static query work portion is described in step 1 to 4 and step 9 in the detailed life of a query explanation above.
    **dynamic query work**Portion of the query work on a content node that depends on the number of documents indexed and active on the node. This portion of the work scales mostly linearly with the number of matched documents. The dynamic query work can be parallelized over multiple processors and nodes. Referenced later as *DQW*. The *DQW* also depends on the phase two protocol summary fill where the actual contents of the global best documents is fetched from the content nodes which produced the hit in the first protocol phase.
    **Total query work**The total query work is given as the dynamic query work (*DQW*) + static query work (*SQW*).
    + +Adding content nodes to a content cluster (keeping the total document volume fixed) with flat distribution reduces the dynamic query work per node (*DQW*), but does not reduce the static query work (*SQW*). The overall system cost also increases as you need to rent another node. + +Since *DQW* depends and scales almost linearly with the number of documents on the content nodes, one can try to distribute the work over more nodes. *Amdahl's law* specifies that the maximum speedup one achieve by parallelizing the dynamic work (*DQW*) is given by the formula: + + +$$ +\text{max\_speedup}_{\text{group}} = \frac{1}{1 - \frac{DQW}{SQW + DQW}} +$$ + +For example, if one by inspecting [metrics](#metrics-for-vespa-sizing) see that the *DQW* = 0.50, the maximum speedup one can get by increasing parallelism by using more nodes and decreasing *DQW* is 2. With fixed occupancy (number of users, clients or load), [Little's Law'](https://en.wikipedia.org/wiki/Little%27s_law) tells us that one could achieve two times the throughput if one is able to speed up the latency by a factor of two: + + +$$ +\frac{1}{1 - \frac{0.5}{0.5 + 0.5}} = 2 +$$ + + +When *SQW* is no longer significantly less than *DQW*, adding more nodes in a flat distributed cluster just increases the overall system cost. This without any serving performance gain, except increasing overall supported feed throughput, which increases almost linearly with number of nodes. + +Two different *DQW/(DQW+SQW)* factors are illustrated in the figures below. The overall query work *TQW* is the same for both cases (10 ms), but the *DQW* portion of the *TQW* is different. The throughput (QPS) is a function of the latency ([Little's Law](https://en.wikipedia.org/wiki/Little%27s_law)) and the number of cpu cores \* nodes. Using 1 node with 24 v-cpu cores and 10 ms service time (*TQW*), one can expect reaching close to 2400 QPS at 100% utilization (unless there are other bottlenecks like network or stateless container processing). + +![Scaling throughput/latency where DQW/(SQW+DQW)=0.5](/assets/img/ScalingLatencyFactor0.5.svg) +![Scaling throughput/latency where DQW/(SQW+DQW)=0.5](/assets/img/ScalingLatencyFactor0.005.svg) + +In the first figure the overall latency is 10 ms, but the dynamic query work (latency) is only 50% and given *Amdahl's law* it follows that the maximum speedup one can get is two. This is true regardless of how many processors or nodes the dynamic query work is parallelized over. No matter how many nodes one adds, one don't get above 4800 queries/s. The only thing one achieve by adding more nodes is increasing the cost without *any* performance benefits. + +In the second figure there is a system where the dynamic work portion is much higher (0.9), and the theoretical maximum speedup becomes bound by 10x as given by *Amdahl's law*. Note that both figures are with a single flat distributed content cluster with a fixed document volume. + +Given the theory, one can derive two rules of thumb for scaling throughput and latency: + + + + + + + + + + + + + + + + + + +
    **Add nodes in a flat distribution**When DQW/TQW is large (close to 1.0), throughput QPS can be scaled by just adding more content nodes in a system using flat distribution. This will reduce the number of documents per node, and thus reduce the *DQW* per node.
    **Add groups using grouped distribution**When DQW/TQW is low, one can no longer just add more content nodes to scale throughput and must instead use a grouped distribution to scale throughput.
    + +## Scaling latency in a content group + +Irrespective of using single group (flat distribution) or multiple groups, the serving latency depends on the factors already described; *DQW* and *SQW*. For use cases where *DQW* dominates the total query work *TQW*, one can effectively scale latency down by parallelizing the *DQW* over more nodes per group. + +It is important to decide on a latency service level agreement (SLA) before sizing the Vespa deployment for the application and query features. A latency SLA is often specified as a latency percentile at a certain throughput level - example: + +- SLA Example 1: 95.0 percentile < 100 ms @ 2000 QPS +- SLA Example 2: 95.0 percentile < 40 ms @ 8000 QPS + +Different use cases might have different performance characteristics, depending on how the dynamic work query portion is compared to the static query work portion. This graph illustrates the relationship between overall latency versus number of documents indexed per node for two different use cases. + +![Latency vs document count per node](/assets/img/latency-documents.svg) + +- For the yellow use case the measured latency is almost independent of the total document volume. This is called sublinear latency scaling, which calls for scaling up using better flavor specification instead of scaling out. + The observed latency at 10M documents per node is almost the same as with 1M documents per node. Such a use case would be most cost-effective by storing as many documents as possible (within the memory/disk/feeding constrains set by the concurrency settings and node flavor) and scale throughput by using a grouped distribution. Efficient query operators which are sublinear has scaling properties like the yellow case. Example of such query operators include the [wand operators](/en/ranking/wand), and [approximate nearest neighbor search operator](/en/querying/approximate-nn-hnsw) +- For the blue use case the measured latency shows a clear correlation with the document volume. This is a case where the dynamic query work portion is high, and adding nodes to the flat group will reduce the serving latency. The sweet spot is found where targeted latency SLA is achieved. This sweet spot depends on which model or ranking features are in use, e.g. how expensive the model is per retrieved or ranked document. + For example, a [GBDT xgboost model](/en/ranking/xgboost) with 3000 trees might breach the targeted latency SLA already at 200K documents, while a 300 tree model might be below the SLA at 2M documents. Using exact [nearest neighbor search](/en/querying/nearest-neighbor-search) has scaling properties like the blue case. See also [feature tuning](/en/performance/feature-tuning). + +### Reduce latency with multithreaded per-search execution + +It is possible to reduce latency of queries where the [dynamic query work](#dynamic-query-work) portion is high. Using multiple threads per search for a use case where the static query work is high will be as wasteful as adding nodes to a flat distribution. + +![Content node search latency vs threads per search](/assets/img/Threads-per-search.svg) + +Using more threads per search will reduce latency as long as the dynamic portion of the query cost is high compared to the static query cost. The reduction in latency comes with the cost of higher cpu utilization. + +A search request with four threads will occupy all four threads until the last thread has completed, and the intra-node per thread document space partitioning must be balanced to give optimal results. + +For rank profiles with second phase ranking, see [phased ranking](/en/ranking/phased-ranking), the hits from first-phase ranking are rebalanced so that each matching thread scores about the same amount of hits using the second phase ranking expression. + +From the above examples with the blue and yellow use case it follows that + +- Linear exact nearest neighbor search latency can be reduced by using more threads per search +- Sublinear approximate nearest neighbor search latency does not benefit from using more threads per search + +By default the number of threads per search is one, as that gives the best resource usage measured as CPU resources used per query. The optimal threads per search depends on the query use case, and should be evaluated by benchmarking. + +The threads per search settings globally is tuned by [persearch](/en/reference/applications/services/content#requestthreads-persearch). This can be overridden to a lower value in [rank profiles](/en/reference/schemas/schemas#num-threads-per-search) so that different query use cases can use different number of threads per search. Using multiple threads per search allows better utilization of multicore cpu architectures for low query volume applications. The `persearch` number in services should always be equal to the highest `num-threads-per-search` in your rank profiles. Setting it higher reduces the maximum number of concurrent queries without any latency benefit. + +#### Thread configuration + +The `search` and `persearch` settings together determine the maximum number of queries that can execute concurrently on a content node: + +```text +max concurrent queries = search / persearch +``` + +For example, with `search=64` (the default) and `persearch=8`, only 8 queries can execute simultaneously. Queries arriving when all slots are busy are queued in the match engine, adding latency without any backpressure to the caller. This can significantly reduce throughput even when CPU cores are available. + +To get started, set `search` to be 2x number of CPU cores. `persearch` should never be more than number of cores. `summary` should be equal to number of cores. Start with the default value of 1 for `persearch` and only increase it for lower query latency, as it will reduce throughput and efficiency. + +If you increase `persearch`, consider increasing `search` proportionally to maintain enough concurrent query slots. For example, if you need `persearch=8` on a 16-core machine, setting `search=128` gives 16 concurrent query slots — enough that queries using fewer threads (via per-rank-profile [num-threads-per-search](/en/reference/schemas/schemas#num-threads-per-search) overrides) are not starved of executor slots. + +Monitor `content.proton.executor.match.utilization` to detect when the match engine is saturated — a sustained value at or near 1.0 indicates all executor slots are busy and queries are queuing. A high `content.proton.executor.match.queuesize.max` relative to `search / persearch` confirms the bottleneck. + +## Scaling the size of the retrievable unit + +Retrieving units that are too small or too large can have a drastic impact on both the quality and performance of your search. Consider an example where we want to search in PDF files: Creating one document per PDF file seems like a logical solution, and with Vespa this is possible - up to a point. But as system architects we must consider the potential edge cases: some files may be entire books, or long reports with many hundreds or even thousands of pages. + +The current max document size is limited by the max protobuf message size of 2 GB, but we advise staying well below this limit, at least < 200MB and ideally < 1MB for even, predictable performance. For reference to scale, the complete text of the bible is about 4MB. + +*Split too-large documents into smaller units for better search quality and performance!* Natural subdivisions like chapters, parts or sections are good candidates for splitting into separate Vespa documents. + +### When documents are too large + +If each document is a complete PDF file and some are very large, what problems could we run into? + +**Usefulness of the result** - knowing that there are relevant parts *somewhere* in several hundred pages of text is not very helpful to the user. As of 2025, this is still true also if the "user" is an LLM in a RAG or agentic workflow. + +We can improve the usefulness by providing [dynamic snippets](/en/querying/document-summaries#dynamic-snippets) or returning per-chunk similarity scores as [feature values](/en/ranking/ranking-expressions-features#accessing-feature-function-values-in-results) to be able to identify the most relevant portions of the returned summary. + +**Performance** - Returning large document values in the query response over HTTP has a significant cost, both in CPU time spent in rendering the response, compression, and network transfer time. This can easily become the largest contribution to the total end-to-end latency. + +Large documents can also contribute to poor performance in indexing and query execution, or greatly increase the amount of temporary memory required for complex ranking expressions like multi-dimensional ColBert maxsim. As document are processed, indexed, stored and ranked as individual units, working on a few very large documents at a time may not offer the system enough opportunity to parallelize and result in poor, uneven utilization of resources, and even a small fraction of very large documents may impact your mean (and especially higher percentile) latencies both for processing and query execution. + +### When documents are too small + +What problems can occur if documents are very small? Consider indexing small fragments of text, like a single sentence or even a word. + +**Granularity** - As the size of text fragments decrease, we are less likely to find good matches for queries as the relevant terms or context is spread across multiple documents. The response may not contain enough information to resolve the user information need, or to even judge if it is likely to resolve the need if the source is examined in full. This problem is described both in [traditional information retrieval literature](https://nlp.stanford.edu/IR-book/html/htmledition/choosing-a-document-unit-1.html) and has also been a popular topic in recent years as "chunking" for semantic search. + +**Overhead** - splitting a document into very small pieces means that more resources will be spent on per-document overhead. Shared metadata like e.g. the abstract or access permissions of a document will be replicated many times, and updates/deletes to the source document or its metadata must fan-out to all the sub-documents, increasing write load. Unlike too-large documents, having a fraction of very small documents is fine, what matters for efficiency is that the average size is not too small. + +## Scaling document volume per node + +One want to fit as many documents as possible into a node given the node constrains (e.g. available cpu, memory, disk) while maintaining: + +- The targeted search latency SLA +- The targeted feed and update rate, and feed latency + +With the latency SLA in mind, benchmark with increasing number of documents per node and watch system level metrics and Vespa metrics. If latency is within the stated latency SLA and the system meets the targeted sustained feed rate, overall cost is reduced by fitting more documents into each node (e.g. by increasing memory, cpu and disk constraints set by the node flavor). + +With larger fan-out using more nodes to partition the data also overcomes higher tail latency as search waits for all results from all nodes. Therefore, the overall execution time depends on the slowest node at the time of the query. In such cases with large fan-out, using [adaptive timeout](/en/reference/applications/services/content#coverage) is recommended to keep tail latency in check. + +Vespa will block feed operations if [resource limits](/en/reference/applications/services/content#resource-limits) have been reached. + +### Disk usage sizing + +Disk usage of a content node increases as the document volume increases: The disk usage per document depends on various factors like the number of schemas, the number of indexed fields and their settings, and most important the size of the fields that are indexed and stored. The simplest way to determine the disk usage is to simply index documents and watch the disk usage along with the relevant metrics. The *redundancy* (number of copies) impact the disk usage footprint, obviously. + +Note that [content node maintenance jobs](/en/content/proton#proton-maintenance-jobs) temporarily increases disk usage. E.g. *index fusion* is an example, where new index files are written, causing an increase in used disk space while running. Space used depends on configuration and data - headroom must include the temporary usage. See [metrics for capacity planning](#metrics-for-vespa-sizing). + +### Memory usage sizing + +The memory usage on a content node increases as the document volume increases. The memory usage increases almost linearly with the number of documents. The vespa-proton-bin process (content node) uses the full 64-bit virtual address space, so the virtual memory usage reported might be high, as both index and summary files are mapped into memory using mmap and pages are paged into memory as needed. + +The memory usage per document depends on the number of fields, the raw size of the documents and how many of the fields are defined as [attributes](/en/content/attributes). Also see [metrics for capacity planning](#metrics-for-vespa-sizing). + +## Scaling Throughput + +As seen in the previous sections, when the static query work (*SQW*) becomes large, scale throughput using grouped distribution. Regardless, if throughput is scaled by grouped distribution for use cases with high static query work portion or a flat distribution for high dynamic query work portion, one should identify how much throughput the total system supports. + +Finding where the latency starts climbing exponentially versus throughput is important in order to make sure that the deployed system is scaled well below this throughput threshold. Also, that it has capacity to absorb load increases over time, as well as having sufficient capacity to sustain node outages during peak traffic. + +At some throughput level some resource(s) in the system will be fully saturated, and requests will be queued up causing latency to spike up, as requests are spending more time waiting for the saturated resource. + +This behaviour is illustrated in the figure below: + +![Latency vs throughput](/assets/img/QPS-scaling.svg) + +A small increase in serving latency is observed as throughput increases, until saturated at approximately 2200 QPS. Pushing more queries than this only increases queueing time, and latency increases sharply. + +## Scaling for failures and headroom + +It is important to also measure behaviour under non-ideal circumstances, to avoid getting too good results. E.g., by simulating node failures or node replacements, verifying feeding concurrency versus search and serving. + +Generally, the higher utilization a system has in production, the more fragile it becomes when changing query patterns or ranking models. + +The target system utilization should be kept sufficiently low for the response times to be reasonable and within latency SLA, even with some extra traffic occurring at peak hours. See also [graceful degradation](/en/performance/graceful-degradation). Also see [sizing write versus read](/en/performance/sizing-feeding#feed-vs-search). + +## Metrics for Vespa Sizing + +The relevant [Vespa Metrics](/en/reference/operations/metrics) for measuring the cost factors, in addition to system level metrics like cpu util, are: + +*Metric capturing static query work (SQW) at content nodes + +```text +content.proton.documentdb.matching.rank_profile.query_setup_time +``` + +*Metric capturing dynamic query work (DQW) at content nodes* + +```text +content.proton.documentdb.matching.rank_profile.query_latency +``` + +By sampling these metrics, one can calculate the theoretical speedup we can achieve by increasing number of nodes using flat distribution, by using *Amdahl's law*: + + +$$ +\text{max\_speedup} = \frac{1}{1 - \frac{\text{match\_time}}{\text{query\_setup\_time} + \text{match\_time}}} +$$ + + +In addition, the following metrics are used to find number of matches per query, per node: + +```text +content.proton.documentdb.matching.rank_profile.docs_matched +content.proton.documentdb.matching.rank_profile.queries +``` + +**Disk usage**: +- documentdb: *vespa.content.proton.documentdb.disk\_usage.last* +- transaction log: *vespa.content.proton.transactionlog.disk\_usage.last* + +**Memory usage**: +- documentdb: *vespa.content.proton.documentdb.memory\_usage.allocated\_bytes.last* diff --git a/en/performance/streaming-search.html b/en/performance/streaming-search.html deleted file mode 100644 index 80330684c1..0000000000 --- a/en/performance/streaming-search.html +++ /dev/null @@ -1,271 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Streaming Search" -redirect_from: -- /en/streaming-search.html ---- - -

    Search engines make queries fast by creating indexes over the stored data. -While the indexes cost extra resources to build and maintain, this is usually -a good tradeoff because they make queries so much cheaper. However, this -does not hold for use cases where the data is split into many small subsets -where each query just searches one (or a few) of these subsets, the canonical -example being personal indexes where a user only searches their own data.

    - -

    For such use cases, Vespa provides streaming search -- a mode where only the raw data of the documents is -stored and searches are implemented -by streaming - no indexes required. In addition, attributes -are also only stored on disk so that the only data needed in memory is -45 bytes per document, meaning that streaming mode lets you store billions of documents -on each node.

    - -

    This is especially important in personal data applications using vector embeddings, -which otherwise require a lot of memory and require ANN to perform well, -which is often unsuited for searching personal data as they don't surface -all the most relevant documents.

    - -

    Streaming mode is suitable when subsets are on average small compared -to the entire corpus. Vespa delivers low query latency also for the occasional large -subset (say, users with huge amounts of data) by automatically sharding such data groups -over multiple content nodes, searched in parallel.

    - -

    Note: Using both streaming and indexed mode in the same cluster is discouraged. -The resource usage and performance characteristics for the two modes are -very different, and it might be very hard to operate and get good performance for -such a system.

    - - -

    Streaming search uses the same implementation of most features in Vespa, including matching, -ranking, grouping and sorting, and mostly supports the same features. -A schema used in -indexed mode -can in most cases be used in streaming search without any changes. -The following differences however apply:

    - -
      -
    • Streaming search does not use the linguistics module while feeding documents. - Instead, the string fields of each streamed document are - tokenized and - normalized on the fly as part of performing a search. - Query terms are normalized in the same way. - Stemming is not supported for streaming search. -
    • -
    • Since there are no indexes, the content nodes do not collect term statistics and average field length statistics. - -
    • -
    • Even without any indexes, fields must be specified as - index or - attribute to make them available for - matching, ranking, grouping and sorting. - The associated default match setting for a field - is equivalent to indexed mode. -
    • -
    • Streaming search supports a wider range of matching options (such as substring and prefix), and - these can be specified either at query time or at configuration time. - See matching options for details. -
    • -
    • HNSW indexes are not supported in streaming search. - This means a nearest neighbor search - is always exact when used in streaming search. - The following parameters for adjusting approximate nearest neighbor search thus have no effect: - -
    • -
    • Parent/child relationships are not supported in streaming search. - Using such functionality will fail deployment. -
    • -
    • Predicate fields are not supported in streaming search. - They can exist as summary only fields in the document, but they are not searchable. -
    • -
    • URI-fields are not supported in streaming search. - They are handled as regular string fields, and do not support the uri search functionality. -
    • -
    • firstPhaseRank rank feature always returns the default value in streaming search. -
    • -
    - - - - -

    -These are the steps required to use streaming search: -

    -
      -
    1. - Set indexing mode to streaming: -
      -<content id="mycluster" version="1.0">
      -    <documents>
      -        <document type="myType" mode="streaming" />
      -
      -
    2. - -
    3. Use - document IDs which contains a group value specifying - the small subset the document belongs to (usually a userid). These have the form - id:myNamespace:myType:g=myUserid:myLocalid - and when represented as paths in document/v1 requests, - document/v1/myNamespace/myType/group/myUserId/myLocalId -
    4. - -
    5. - Specify the subset to search using the query parameter - streaming.groupname. -
    6. - -
    - -

    See the vector streaming search sample application -for a complete example.

    - - - - -{% include important.html content='Since Vespa 8.287, this section can be disregarded - -it is not necessary to add <document-processing/> tags. -The configuration is identical to using indexed mode.' %} -

    - Indexing statements are - as the name indicates - mostly used for indexing, - and so they are not executed by default with streaming search. -

    -

    However, sometimes it is convenient to run indexing statements also when using streaming, for example to -use the embed function to turn text into an embedding vector, as in

    - -
    indexing: input myTextField | embed | attribute
    - -

    Indexing statements are run by a document processor, so to enable them with streaming, -enable document processing enabled on a container cluster and point to it as the one to do -indexing processing from the content cluster:

    - -
    -<services version="1.0">
    -    <container id="myContainers" version="1.0">
    -        ...
    -        <document-processing/>
    -        ...
    -    </container>
    -
    -    <content id="mail" version="1.0">
    -        ...
    -        <documents>
    -            <document type="myType" mode="streaming" />
    -            <document-processing chain="indexing" cluster="myContainers" />
    -        </documents>
    -        ...
    -    </content>
    -</services>
    -
    - - - - -

    Streaming search offers more flexibility in matching text fields: Match settings -can be specified at query time on any text field, and fields marked with indexing: index -supports suffix and -substring matching.

    - -

    To specify match settings at query time in YQL:

    -
    -select * from sources * where artist contains ({prefix:true}"col")
    -select * from sources * where artist contains ({substring:true}"old")
    -select * from sources * where artist contains ({suffix:true}"play")
    -
    - -

    To specify a default match setting for a field in the schema:

    - -
    -field artist type string {
    -    indexing: summary | index
    -    match: substring
    -}
    -
    - - -

    Streaming search grouping extension

    - -

    Grouping works as normal with streaming search but offers two additional features, -explained here.

    - -

    Grouping over all documents

    - -

    Since streaming search "looks at" all documents matching the group name/selection - regardless of the query, it is possible to group over all those documents and not just the ones - matching the query. This is done by using where(true) in the grouping expression: - -

    all( where(true) all(group(myfield) each(output(count()))) )
    - -

    When doing this, relevancy is not calculated for groups, as only matched hits have relevance.

    - -

    The docidnsspecific function

    - -

    The docidnsspecific function returns the docid without namespace.

    -
    all( group(docidnsspecific()) each(output(count())) )
    - - - - - -

    Memory: Streaming search requires 45 bytes of memory per document regardless of the document content.

    - -

    Disk: Streaming search requires disk space to store the raw document data in compressed form. -The size is dependent on the actual data but can be extrapolated linearly with the number of documents.

    - - - - -

    Streaming search is a visit operation. -Parallelism is configured using persistence-threads: -

    - -
    -<persistence-threads count='8'/>
    -<visitors thread-count='8'/>
    -
    - -

    On Vespa Cloud, -this number is set automatically to match the number of VCPUs set in -resources. -If you cannot get lower latency by increasing VCPUs, it means your streaming searches have become -IO bound.

    - - -

    Tuning document store: Direct IO and cache

    - -

    For better control of memory usage, use direct IO for reads when -document store cache -is enabled - -this makes the OS buffer cache size smaller and more predictable performance. -The document store cache will cache recent entries and increase performance for users or groups doing repeated accesses. -This sets aside 1 GB for document store cache. -

    -
    -<engine>
    -  <proton>
    -    <tuning>
    -      <searchnode>
    -        <summary>
    -          <io>
    -            <write>directio</write>
    -            <read>directio</read>
    -          </io>
    -          <store>
    -            <cache>
    -              <maxsize>1073741824</maxsize>
    -            </cache>
    -
    diff --git a/en/performance/streaming-search.mdx b/en/performance/streaming-search.mdx new file mode 100644 index 0000000000..95e1b746d1 --- /dev/null +++ b/en/performance/streaming-search.mdx @@ -0,0 +1,180 @@ +--- +title: "Streaming Search" +--- + +Search engines make queries fast by creating indexes over the stored data. While the indexes cost extra resources to build and maintain, this is usually a good tradeoff because they make queries so much cheaper. However, this does not hold for use cases where the data is split into many small subsets where each query just searches one (or a few) of these subsets, the canonical example being *personal indexes* where a user only searches their own data. + +For such use cases, Vespa provides *streaming search* - a mode where only the raw data of the documents is [stored](/en/content/proton#document-store) and searches are implemented by streaming - no indexes required. In addition, attributes are also only stored on disk so that the only data needed in memory is 45 bytes per document, meaning that streaming mode lets you store billions of documents on each node. + +This is especially important in personal data applications using vector embeddings, which otherwise require a lot of memory and require ANN to perform well, which is often unsuited for searching personal data as they don't surface all the most relevant documents. + +Streaming mode is suitable when subsets are *on average* small compared to the entire corpus. Vespa delivers low query latency also for the occasional large subset (say, users with huge amounts of data) by automatically sharding such data groups over multiple content nodes, searched in parallel. + + +**Note:** + +Using both streaming and indexed mode in the same cluster is discouraged. The resource usage and performance characteristics for the two modes are very different, and it might be very hard to operate and get good performance for such a system. + + +## Differences in streaming search + +Streaming search uses the same implementation of most features in Vespa, including matching, ranking, grouping and sorting, and mostly supports the same features. A [schema](/en/basics/schemas) used in [indexed mode](/en/reference/applications/services/content#document) can in most cases be used in streaming search without any changes. The following differences however apply: + +- Streaming search does not use the [linguistics](/en/linguistics/linguistics) module while feeding documents. Instead, the string fields of each streamed document are [tokenized](/en/linguistics/linguistics-opennlp#tokenization) and [normalized](/en/linguistics/linguistics-opennlp#normalization) on the fly as part of performing a search. Query terms are [normalized](/en/linguistics/linguistics-opennlp#normalization) in the same way. [Stemming](/en/linguistics/linguistics-opennlp#stemming) is not supported for streaming search. +- Since there are no indexes, the content nodes do not collect term statistics and average field length statistics. + - Term significance should be provided by a [global significance model](/en/ranking/significance#global-significance-model), if [text matching features](/en/reference/ranking/rank-features) that benefit from it are used. This includes among others *[bm25](/en/ranking/bm25)*, *nativeRank*, *nativeFieldMatch*, *nativeProximity* and *fieldMatch*. + - If using *bm25*, adjust the [averageFieldLength](/en/reference/ranking/rank-feature-configuration#properties) configuration for a more precise *bm25* score. +- Even without any indexes, fields must be specified as [index](/en/reference/schemas/schemas#index) or [attribute](/en/reference/schemas/schemas#attribute) to make them available for matching, ranking, grouping and sorting. The associated default [match](/en/reference/schemas/schemas#match) setting for a field is equivalent to [indexed mode](/en/reference/applications/services/content#document). +- Streaming search supports a wider range of matching options (such as substring and prefix), and these can be specified either at query time or at configuration time. See [matching options](#matching-options-in-streaming-search) for details. +- [HNSW](/en/reference/schemas/schemas#index-hnsw) indexes are not supported in streaming search. This means a [nearest neighbor search](/en/querying/nearest-neighbor-search#using-nearest-neighbor-search) is always *exact* when used in streaming search. The following parameters for adjusting *approximate* nearest neighbor search thus have no effect: + - [post-filter-threshold](/en/reference/schemas/schemas#post-filter-threshold) + - [approximate-threshold](/en/reference/schemas/schemas#approximate-threshold) + - [filter-first-threshold](/en/reference/schemas/schemas#filter-first-threshold) + - [filter-first-exploration](/en/reference/schemas/schemas#filter-first-exploration) + - [exploration-slack](/en/reference/schemas/schemas#exploration-slack) + - [target-hits-max-adjustment-factor](/en/reference/schemas/schemas#target-hits-max-adjustment-factor) +- [Parent/child relationships](/en/schemas/parent-child) are not supported in streaming search. Using such functionality will fail [deployment](/en/learn/glossary#deployment). +- [Predicate fields](/en/schemas/predicate-fields) are not supported in streaming search. They can exist as summary only fields in the document, but they are not searchable. +- [URI-fields](/en/reference/schemas/schemas#uri) are not supported in streaming search. They are handled as regular string fields, and do not support the uri search functionality. +- [firstPhaseRank](/en/reference/ranking/rank-features#firstPhaseRank) rank feature always returns the default value in streaming search. + +## Using streaming search + +These are the steps required to use streaming search: + + + +Set indexing mode to [streaming](/en/reference/applications/services/content#document): + +```xml + + + +``` + + +Use [document IDs](/en/schemas/documents) which contains a *group* value specifying the small subset the document belongs to (usually a userid). These have the form `id:myNamespace:myType:**g=myUserid**:myLocalid` and when represented as paths in [document/v1](/en/writing/document-v1-api-guide) requests, `document/v1/myNamespace/myType/**group/myUserId**/myLocalId` + + +Specify the subset to search using the query parameter [streaming.groupname](/en/reference/api/query#streaming.groupname). + + + +See the [vector streaming search sample application](https://github.com/vespa-engine/sample-apps/tree/master/vector-streaming-search) for a complete example. + +## Enabling indexing statements in streaming search + + +**Important:** + +Since Vespa 8.287, this section can be disregarded - it is not necessary to add `` tags. The configuration is identical to using indexed mode. + + +Indexing statements are - as the name indicates - mostly used for indexing, and so they are not executed by default with streaming search. + +However, sometimes it is convenient to run indexing statements also when using streaming, for example to use the `embed` function to turn text into an embedding vector, as in + +```js +indexing: input myTextField | embed | attribute +``` + +Indexing statements are run by a document processor, so to enable them with streaming, enable document processing enabled on a container cluster and point to it as the one to do indexing processing from the content cluster: + +```xml + + + ... + + ... + + + + ... + + + + + ... + + +``` + +## Matching options in streaming search + +Streaming search offers more flexibility in matching text fields: Match settings can be specified at query time on any text field, and fields marked with `indexing: index` supports [suffix](/en/reference/schemas/schemas#suffix) and [substring](/en/reference/schemas/schemas#substring) matching. + +To specify match settings at query time in YQL: + +```text +select * from sources * where artist contains ({prefix:true}"col") +select * from sources * where artist contains ({substring:true}"old") +select * from sources * where artist contains ({suffix:true}"play") +``` + +To specify a default match setting for a field in the schema: + +```js +field artist type string { + indexing: summary | index + match: substring +} +``` + +## Streaming search grouping extension + +[Grouping](/en/querying/grouping) works as normal with streaming search but offers two additional features, explained here. + +### Grouping over all documents + +Since streaming search "looks at" all documents matching the group name/selection regardless of the query, it is possible to group over all those documents and not just the ones matching the query. This is done by using `where(true)` in the grouping expression: + +```text +all( where(true) all(group(myfield) each(output(count()))) ) +``` + +When doing this, relevancy is not calculated for groups, as only matched hits have relevance. + +### The docidnsspecific function + +The `docidnsspecific` function returns the docid without namespace. + +```text +all( group(docidnsspecific()) each(output(count())) ) +``` + +## Resource usage with streaming search + +**Memory**: Streaming search requires 45 bytes of memory per document regardless of the document content. + +**Disk**: Streaming search requires disk space to store the raw document data in compressed form. The size is dependent on the actual data but can be extrapolated linearly with the number of documents. + +## Query tuning in streaming search + +Streaming search is a [visit](/en/writing/visiting) operation. Parallelism is configured using [persistence-threads](/en/reference/applications/services/content#persistence-threads): + +```xml + + +``` + +On [Vespa Cloud](/), this number is set automatically to match the number of VCPUs set in [resources](/en/reference/applications/services/services). If you cannot get lower latency by increasing VCPUs, it means your streaming searches have become IO bound. + +### Tuning document store: Direct IO and cache + +For better control of memory usage, use direct IO for reads when [document store cache](/en/reference/applications/services/content#summary) is enabled - this makes the OS buffer cache size smaller and more predictable performance. The document store cache will cache recent entries and increase performance for users or groups doing repeated accesses. This sets aside 1 GB for document store cache. + +```xml highlight= {7-8, 11-13} + + + + + + + directio + directio + + + + 1073741824 + +``` diff --git a/en/performance/topology-and-resizing.md b/en/performance/topology-and-resizing.md deleted file mode 100644 index 377822795f..0000000000 --- a/en/performance/topology-and-resizing.md +++ /dev/null @@ -1,181 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: Topology and Resizing -applies_to: cloud -redirect_from: - - /cloud/topology-and-resizing - - /en/cloud/topology-and-resizing.html ---- - -Vespa has features to optimize cost, query latency, and throughput, -at the same time, making tradeoffs for availability. -This guide goes through various topologies by example, highlighting the most relevant tradeoffs -and discusses operational events like node stop and changing the topology. - -Use cases for using a grouped topology is found in the -[elasticity](../content/elasticity.html#grouped-distribution) guide. -E.g., query latency can dictate the maximum number of documents per node and hence how many nodes are needed in a group - -if query latency is at the maximum tolerated for 1M documents, -6 nodes are needed in a group for a 6M index. - -Content nodes are stateful, holding replicas of the documents to be queried. -Content nodes can be deployed in different topologies - example using 6 nodes: - -4 different topologies - -Vespa Cloud requires a redundancy of at least 2. -In this guide, it is assumed that redundancy, configured as -[min-redundancy](../reference/applications/services/content.html#min-redundancy), is set to n=3. -Redundancy is a function of data availability / criticality and cost, and varies from application to application. - -Redundancy is for storing a document replica on a node. -Not all replicas are searchable - -read [Proton](../content/proton.html) for a detailed understanding of sub-databases. - - - -## Out of the box: 1x6 -Most applications should be configured without a grouped topology, until optimizing for a use case - -see the elasticity guide linked above. -Therefore, start with a _flat_ configuration, like: -
    -
    - 1x6 -
    -
    -
    -<min-redundancy>3</min-redundancy>
    -<nodes count="6">
    -    <resources .../>
    -</nodes>
    -
    -
    -
    -This means the corpus is spread over 6 nodes, with 17% of documents active in queries each. -This topology is called 1x6 in this guide. - -This is important to remember when benchmarking for latency, normally done on a single node with n=1. -In the 6-node system with n=3, more memory and disk space are used for the redundant replicas - -more on that later. - -This topology is the default topology, and works great: - -* When a node is stopped (unplanned, or planned like a software upgrade), - there are 5 other nodes to serve queries, where each of the 5 will have 1/5 larger corpus to serve -* Adding capacity, say 17% is done by increasing the node count to 7 - -{% include note.html content="This topology is the default, and what most applications should start with."%} - - - -## 3-row topology: 3x2 -Some applications, particularly the ones with extreme low-latency serving, -will find that queries are dominated by the static part of query execution. -This means that reducing the number of documents queried does not lower latency. - -The flip side is, increasing document count does not increase the latency much, either - consider 3x2: -
    -
    - 3x2 -
    -
    -
    -<min-redundancy>3</min-redundancy>
    -<nodes count="6" groups="3">
    -    <resources .../>
    -</nodes>
    -
    -
    -
    - -Here we have configured 3 groups, with n=3. -This means the other node in the row does not have a replica - redundancy is between the rows. - -Each node now has 3x the number of documents per query (compared to 1x6), but query capacity is also tripled, -as each row has the full document corpus. -This can be a great way to scale query throughput! -Notes: -* At planned/unplanned node stop, the full row is eliminated from query serving - - there are four nodes total left, in two rows. - Query capacity is hence down to 67%. -* Feeding requirements are the same as in 1x6 - every document write is written to 3 replicas. -* Document reconciliation is independent of topology - - replicas from all nodes are used when rebuilding nodes after a node stop. - - - -## 6-row topology: 6x1 -Maximizing the number of documents per node is good for cases where the query latency is still within requirements, -and less total work is done, as fewer nodes in a row calculate candidates in ranking. -The extreme case is all documents on a single node replicated with 6 groups. -This is a quite common configuration due to high throughput and simplicity: - -
    -
    - 6x1 -
    -
    -
    -<min-redundancy>6</min-redundancy>
    -<nodes count="6" groups="6">
    -    <resources .../>
    -</nodes>
    -
    -
    -
    - -Notes: -* Feeding _total work_ is higher - with n=6, six replicas are written (compared to three above). - See [feeding latency](#feeding) notes below. - - - -## 2-row topology: 2x3 -In this case, the application has a redundancy of 2 - it must be the same as the number of rows: - -
    -
    - 2x3 -
    -
    -
    -<min-redundancy>2</min-redundancy>
    -<nodes count="6" groups="2">
    -    <resources .../>
    -</nodes>
    -
    -
    -
    - -By default, when a node stops (and it does daily for Vespa upgrades), -the full row stops serving, which is 50% of the capacity out. - -When using this topology, -we recommend setting [min-active-docs-coverage](../reference/applications/services/content.html#min-active-docs-coverage) -to a lower number than the default 97%. -E.g., if there are three nodes in a group, each node is 33.3%, and a node loss gives 66.7% coverage. -Setting min-active-docs-coverage to 65 means that _more than one node_ must stop for the group (row) to stop serving, -which is normally what one would want in this topology. - - - -## Topology migration -Migrating from one topology to another is easy, as Vespa Cloud will auto-migrate documents: - -* All rows must have same node count, meaning `count / groups` must be an integer. -* When changing topology, Vespa Cloud will provision new nodes as needed to ensure no coverage loss. - An increased node count is hence normal in the transition phase, - superfluous nodes are de-provisioned after data migration. -* Topology migration is therefore a safe operation and makes it easy to optimize for the best price/performance. - - - - - -## Feeding -Documents are fed to Vespa Cloud using the -[<document-api>](../reference/api/document-v1.html#configuration) endpoint. -This means one Vespa Container node forwards document writes to all the replicas in parallel. -As all groups have a replica, adding a group will not add feed _latency_ in theory due to the parallelism. -However, there will be an increase in practice as more nodes mean more latency variation, -and the slowest node sets the end latency. diff --git a/en/performance/topology-and-resizing.mdx b/en/performance/topology-and-resizing.mdx new file mode 100644 index 0000000000..483b263c20 --- /dev/null +++ b/en/performance/topology-and-resizing.mdx @@ -0,0 +1,127 @@ +--- +title: "Topology and Resizing" +sidebarTitle: "Topology and resizing" +--- + +Vespa has features to optimize cost, query latency, and throughput, at the same time, making tradeoffs for availability. This guide goes through various topologies by example, highlighting the most relevant tradeoffs and discusses operational events like node stop and changing the topology. + +Use cases for using a grouped topology is found in the [elasticity](/en/content/elasticity#grouped-distribution) guide. E.g., query latency can dictate the maximum number of documents per node and hence how many nodes are needed in a group - if query latency is at the maximum tolerated for 1M documents, 6 nodes are needed in a group for a 6M index. + + +**Note:** + +Vespa Cloud supports a one-level grouped topology - a group of groups is hence not supported. + + +Content nodes are stateful, holding replicas of the documents to be queried. Content nodes can be deployed in different topologies - example using 6 nodes: + +![4 different topologies](/assets/img/grouped-topology.svg) + +Vespa Cloud requires a redundancy of at least 2. In this guide, it is assumed that redundancy, configured as [min-redundancy](/en/reference/applications/services/content#min-redundancy), is set to n=3. Redundancy is a function of data availability / criticality and cost, and varies from application to application. + +Redundancy is for storing a document replica on a node. Not all replicas are searchable - read [Proton](/en/content/proton) for a detailed understanding of sub-databases. + +## Out of the box: 1x6 + +Most applications should be configured without a grouped topology, until optimizing for a use case - see the elasticity guide linked above. Therefore, start with a *flat* configuration, like: + + +![1x6](/assets/img/1x6.svg) + +```xml +3 + + + +``` + + +This means the corpus is spread over 6 nodes, with 17% of documents active in queries each. This topology is called 1x6 in this guide. + +This is important to remember when benchmarking for latency, normally done on a single node with n=1. In the 6-node system with n=3, more memory and disk space are used for the redundant replicas - more on that later. + +This topology is the default topology, and works great: + +- When a node is stopped (unplanned, or planned like a software upgrade), there are 5 other nodes to serve queries, where each of the 5 will have 1/5 larger corpus to serve +- Adding capacity, say 17% is done by increasing the node count to 7 + + +**Note:** + +This topology is the default, and what most applications should start with. + + +## 3-row topology: 3x2 + +Some applications, particularly the ones with extreme low-latency serving, will find that queries are dominated by the static part of query execution. This means that reducing the number of documents queried does not lower latency. + +The flip side is, increasing document count does not increase the latency much, either - consider 3x2: + + +![3x2](/assets/img/3x2.svg) + +```xml +3 + + + +``` + + +Here we have configured 3 groups, with n=3. This means the other node in the row does not have a replica - redundancy is between the rows. + +Each node now has 3x the number of documents per query (compared to 1x6), but query capacity is also tripled, as each row has the full document corpus. This can be a great way to scale query throughput! Notes: + +- At planned/unplanned node stop, the full row is eliminated from query serving - there are four nodes total left, in two rows. Query capacity is hence down to 67%. +- Feeding requirements are the same as in 1x6 - every document write is written to 3 replicas. +- Document reconciliation is independent of topology - replicas from all nodes are used when rebuilding nodes after a node stop. + +## 6-row topology: 6x1 + +Maximizing the number of documents per node is good for cases where the query latency is still within requirements, and less total work is done, as fewer nodes in a row calculate candidates in ranking. The extreme case is all documents on a single node replicated with 6 groups. This is a quite common configuration due to high throughput and simplicity: + + +![6x1](/assets/img/6x1.svg) + +```xml +6 + + + +``` + + +Notes: + +- Feeding *total work* is higher - with n=6, six replicas are written (compared to three above). See [feeding latency](#feeding) notes below. + +## 2-row topology: 2x3 + +In this case, the application has a redundancy of 2 - it must be the same as the number of rows: + + +![2x3](/assets/img/2x3.svg) + +```xml +2 + + + +``` + + +By default, when a node stops (and it does daily for Vespa upgrades), the full row stops serving, which is 50% of the capacity out. + +When using this topology, we recommend setting [min-active-docs-coverage](/en/reference/applications/services/content#min-active-docs-coverage) to a lower number than the default 97%. E.g., if there are three nodes in a group, each node is 33.3%, and a node loss gives 66.7% coverage. Setting min-active-docs-coverage to 65 means that *more than one node* must stop for the group (row) to stop serving, which is normally what one would want in this topology. + +## Topology migration + +Migrating from one topology to another is easy, as Vespa Cloud will auto-migrate documents: + +- All rows must have same node count, meaning `count / groups` must be an integer. +- When changing topology, Vespa Cloud will provision new nodes as needed to ensure no coverage loss. An increased node count is hence normal in the transition phase, superfluous nodes are de-provisioned after data migration. +- Topology migration is therefore a safe operation and makes it easy to optimize for the best price/performance. + +## Feeding + +Documents are fed to Vespa Cloud using the [``](/en/reference/api/document-v1#configuration) endpoint. This means one Vespa Container node forwards document writes to all the replicas in parallel. As all groups have a replica, adding a group will not add feed *latency* in theory due to the parallelism. However, there will be an increase in practice as more nodes mean more latency variation, and the slowest node sets the end latency. \ No newline at end of file diff --git a/en/performance/valgrind.html b/en/performance/valgrind.html deleted file mode 100644 index 998f1c4ecf..0000000000 --- a/en/performance/valgrind.html +++ /dev/null @@ -1,66 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Using Valgrind with Vespa" ---- - -

    - Valgrind is a useful tool to investigate bugs, - and to get a detailed performance profile of an application - after profiling to get the higher level picture. - This documents how to run Vespa processes with valgrind. -

    - - - -

    Valgrind with callgrind

    -

    - Install valgrind. - One might need to enable world writeable $VESPA_HOME: -

    -
    -$ sudo chmod 777 $VESPA_HOME
    -
    -

    - Keep in mind to reset that after profiling session is completed. - General use of valgrind - show memory errors: -

    -
    -$ valgrind 'application'
    -
    -

    Show call graph:

    -
    -$ valgrind --tool=callgrind 'application'
    -
    -

    Show a detailed profiling graph - use this to optimize the application:

    -
    -$ valgrind --tool=callgrind --simulate-hwpref=yes --simulate-cache=yes \
    -  --dump-instr=yes --collect-jumps=yes 'application'
    -
    -

    - After running valgrind, copy callgrind.out.* to a host that has kcachegrind installed. - Also copy the binary to the same path as it had while running. - It might also be nice to have access to the code - path to code can be specified in kcachegrind. -

    - - - -

    Start Vespa using valgrind

    -

    - Start Vespa with the following environment variables set: -

    -
    -$ VESPA_USE_VALGRIND="vespa-proton"
    -
    -

    Run Vespa under valgrind and check for memory errors (logs in $VESPA_HOME/tmp/):

    -
    -$ VESPA_USE_VALGRIND="vespa-proton" VESPA_VALGRIND_OPT="--tool=callgrind --simulate-hwpref=yes \
    -  --simulate-cache=yes --dump-instr=yes --collect-jumps=yes"
    -
    -

    Profile the application:

    -
    -$ VESPA_USE_VALGRIND="vespa-proton" VESPA_VALGRIND_OPT="--tool=callgrind --simulate-hwpref=yes \
    -  --simulate-wb=yes --dump-instr=yes --collect-jumps=yes --collect-bus=yes --branch-sim=yes"
    -
    -

    - Remember to stop Vespa - the callgrind.* files are not generated until the program stops. -

    diff --git a/en/performance/valgrind.mdx b/en/performance/valgrind.mdx new file mode 100644 index 0000000000..018f02b033 --- /dev/null +++ b/en/performance/valgrind.mdx @@ -0,0 +1,59 @@ +--- +title: "Using Valgrind with Vespa" +sidebarTitle: "Valgrind" +--- + +Valgrind is a useful tool to investigate bugs, and to get a detailed performance profile of an application after [profiling](/en/performance/profiling) to get the higher level picture. This documents how to run Vespa processes with valgrind. + +## Valgrind with callgrind + +Install valgrind. One might need to enable world writeable `$VESPA_HOME`: + +```bash +$ sudo chmod 777 $VESPA_HOME +``` + +Keep in mind to reset that after profiling session is completed. General use of valgrind - show memory errors: + +```bash +$ valgrind 'application' +``` + +Show call graph: + +```bash +$ valgrind --tool=callgrind 'application' +``` + +Show a detailed profiling graph - use this to optimize the application: + +```bash +$ valgrind --tool=callgrind --simulate-hwpref=yes --simulate-cache=yes \ + --dump-instr=yes --collect-jumps=yes 'application' +``` + +After running valgrind, copy *callgrind.out.\** to a host that has *kcachegrind* installed. Also copy the binary to the same path as it had while running. It might also be nice to have access to the code - path to code can be specified in kcachegrind. + +## Start Vespa using valgrind + +Start Vespa with the following environment variables set: + +```bash +$ VESPA_USE_VALGRIND="vespa-proton" +``` + +Run Vespa under valgrind and check for memory errors (logs in `$VESPA_HOME/tmp/`): + +```bash +$ VESPA_USE_VALGRIND="vespa-proton" VESPA_VALGRIND_OPT="--tool=callgrind --simulate-hwpref=yes \ + --simulate-cache=yes --dump-instr=yes --collect-jumps=yes" +``` + +Profile the application: + +```bash +$ VESPA_USE_VALGRIND="vespa-proton" VESPA_VALGRIND_OPT="--tool=callgrind --simulate-hwpref=yes \ + --simulate-wb=yes --dump-instr=yes --collect-jumps=yes --collect-bus=yes --branch-sim=yes" +``` + +Remember to stop Vespa - the callgrind.* files are not generated until the program stops. diff --git a/en/querying/approximate-nn-hnsw.md b/en/querying/approximate-nn-hnsw.md deleted file mode 100644 index edd162bd20..0000000000 --- a/en/querying/approximate-nn-hnsw.md +++ /dev/null @@ -1,260 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Approximate nearest neighbor search using HNSW index" -keywords: "ann, approximate nearest neighbor" -redirect_from: - - /en/approximate-nn-hnsw ---- - -This document describes how to speed up searches for nearest neighbors in vector spaces by adding -[HNSW index](../reference/schemas/schemas.html#index-hnsw) to tensor fields. -For an introduction to nearest neighbor search, see [nearest neighbor search](nearest-neighbor-search) documentation, -for practical usage of Vespa's nearest neighbor search, see [nearest neighbor search - a practical guide](nearest-neighbor-search-guide), -and to have Vespa create vectors for you, see [embedding](../rag/embedding.html). - -Vespa implements a modified version of the Hierarchical Navigable Small World (HNSW) graph algorithm [paper](https://arxiv.org/abs/1603.09320). -The implementation in Vespa supports: - -* **Filtering** - The search for nearest neighbors can be constrained by query filters. -The [nearestNeighbor](../reference/querying/yql.html#nearestneighbor) query operator can be combined with other filters or query terms using the [Vespa query language](query-language.html). -See the query examples in the [practical guide](nearest-neighbor-search-guide#combining-approximate-nearest-neighbor-search-with-query-filters). - -* **Multi-field vector Indexing** - A schema can include multiple indexed tensor fields and search any combination -of them in a query. This is useful to support multiple models, multiple text sources, and multi-modal search such -as indexing both a textual description and image for the same entity. - -* **Multi-vector Indexing** - A single document field can contain any number of vector values by -defining it as a mixed tensor (a "map of vectors"). -Documents will then be retrieved by the closest vector in each document compared to the query vector. -See the [Multi-vector indexing sample application](https://github.com/vespa-engine/sample-apps/tree/master/multi-vector-indexing) -for examples. -This is commonly used to [index documents with multiple chunks](../rag/working-with-chunks.html). -See also [this blog post](https://blog.vespa.ai/semantic-search-with-multi-vector-indexing/#implementation). - -* **Real Time Indexing** - CRUD (Create, Add, Update, Remove) vectors in the index in true real time. - -* **Mutable HNSW Graph** - No query or indexing overhead from searching multiple HNSW graphs. In Vespa, -there is one graph per tensor field per content node. No segmented or partitioned graph where a query against -a content node need to scan multiple HNSW graphs. - -* **Multithreaded Indexing** - The costly part when performing real time changes to the *HNSW* graph -is distance calculations while searching the graph layers to find which links to change. -These distance calculations are performed by multiple indexing threads. - -* **Multiple value types** - The cost driver of vector search is often storing the vectors in memory, -which is required to produce accurate results at low latency. An effective way to reduce cost is to reduce the -size of each vector value. Vespa supports double, float, bfloat16, int8 and [single-bit values](../rag/binarizing-vectors.html). -Changing from float to bfloat16 can halve cost with negligible impact on accuracy, while single-bit -values greatly reduce both memory and cpu costs, and can be effectively combined with larger vector values -stored on disk as a paged attribute to be used for ranking. - -* **Optimized HNSW lookups** - ANN searches in Vespa [support](https://blog.vespa.ai/tweaking-ann-parameters/) -both pre-and post-filtering, beam exploration, and filtering before distance calculation ("Acorn 1"). -Tuning parameters for these makes it possible to strike a good balance between performance and accuracy for any -data set. Vespa's [ANN tuning tool](https://vespa-engine.github.io/pyvespa/examples/ann-parameter-tuning-vespa-cloud.html) -can be used to automate the process. - -## Using Vespa's approximate nearest neighbor search -The query examples in [nearest neighbor search](nearest-neighbor-search) uses exact search, which has perfect accuracy. -However, this is computationally expensive for large document volumes -as distances are calculated for every document which matches the query filters. - -To enable fast approximate matching, the tensor field definition -needs an `index` directive. A Vespa [document schema](../basics/schemas.html) can declare multiple tensor fields with `HNSW` enabled. - -
    -field image_embeddings type tensor<float>(i{},x[512]) {
    -  indexing: summary | attribute | index
    -  attribute {
    -    distance-metric: angular
    -  }
    -  index {
    -    hnsw {
    -      max-links-per-node: 16
    -      neighbors-to-explore-at-insert: 100
    -    }
    -  }
    -}
    -
    -field text_embedding type tensor<float>(x[384]) {
    -  indexing: summary | attribute | index
    -  attribute {
    -    distance-metric: prenormalized-angular
    -  }
    -  index {
    -    hnsw {
    -      max-links-per-node: 24
    -      neighbors-to-explore-at-insert: 200
    -    }
    -  }
    -}
    -
    - -In the schema snippet above, fast approximate search is enabled by building an `HNSW` index for the -`image_embeddings` and the `text_embedding` tensor fields. -`image_embeddings` indexes multiple vectors per document, -while `text_embedding` indexes one vector per document. - -The two vector fields use different [distance-metric](../reference/schemas/schemas.html#distance-metric) -and `HNSW` index settings: - -* `max-links-per-node` - a higher value increases recall accuracy, but also memory usage, indexing and search cost. -* `neighbors-to-explore-at-insert` - a higher value increases recall accuracy, but also indexing cost. - -Choosing the value of these parameters affects both accuracy, search performance, memory usage and indexing performance. -See [Billion-scale vector search with Vespa - part two](https://blog.vespa.ai/billion-scale-knn-part-two/) -for a detailed description of these tradeoffs. See [HNSW index reference](../reference/schemas/schemas.html#index-hnsw) -for details on the index parameters. - -### Indexing throughput -![Real-time indexing throughput](https://blog.vespa.ai/assets/2022-01-27-billion-scale-knn-part-two/throughput.png) - -The `HNSW` settings impacts indexing throughput. Higher values of `max-links-per-node` and `neighbors-to-explore-at-insert` -reduces indexing throughput. Example from [Billion-scale vector search with Vespa - part two](https://blog.vespa.ai/billion-scale-knn-part-two/). - -### Memory usage -Higher value of `max-links-per-node` impacts memory usage, higher values means higher memory usage: - -![Memory footprint](https://blog.vespa.ai/assets/2022-01-27-billion-scale-knn-part-two/memory.png) - -### Accuracy - -![Accuracy](https://blog.vespa.ai/assets/2022-01-27-billion-scale-knn-part-two/ann.png) - -Higher `max-links-per-node` and `neighbors-to-explore-at-insert` improves the quality of the graph and -recall accuracy. As the search-time parameter [hnsw.exploreAdditionalHits](../reference/querying/yql.html#hnsw-exploreadditionalhits) -is increased, the lower combination reaches about 70% recall@10, while the higher combination reaches -about 92% recall@10. The improvement in accuracy needs to be weighted against the impact on indexing performance and -memory usage. - -## Using approximate nearest neighbor search - -With an *HNSW* index enabled on the tensor field one can choose between approximate -or exact (brute-force) search by using the [approximate query annotation](../reference/querying/yql.html#approximate) - -
    -{
    -  "yql": "select * from doc where {totalTargetHits: 10, approximate:false}nearestNeighbor(image_embeddings,query_image_embedding)",
    -  "hits": 10
    -  "input.query(query_image_embedding)": [0.21,0.12,....],
    -  "ranking.profile": "image_similarity" 
    -}
    -
    -By default, `approximate` is true when searching a tensor field with `HNSW` index enabled. -The `approximate` parameter allows quantifying the accuracy loss of using approximate search. -The loss can be calculated by performing an exact neighbor search using `approximate:false` and -compare the retrieved documents with `approximate:true` and calculate the overlap@k metric. - -Note that exact searches over a large vector volume require adjustment of the -[query timeout](../reference/api/query.html#timeout). -The default [query timeout](../reference/api/query.html#timeout) is 500ms, -which will be too low for an exact search over many vectors. - -In addition to [totalTargetHits](../reference/querying/yql.html#totaltargethits), -there is a [hnsw.exploreAdditionalHits](../reference/querying/yql.html#hnsw-exploreadditionalhits) parameter -which controls how many extra nodes in the graph (in addition to `totalTargetHits`) -that are explored during the graph search. This parameter is used to tune accuracy quality versus query performance. - -## Combining approximate nearest neighbor search with filters -The [nearestNeighbor](../reference/querying/yql.html#nearestneighbor) query operator can be combined with other -query filters using the [Vespa query language](../reference/querying/yql.html) and its query operators. -There are two high-level strategies for combining query filters with approximate nearest neighbor search: -* [pre-filtering](https://blog.vespa.ai/constrained-approximate-nearest-neighbor-search/#pre-filtering-strategy) (the default) -* [post-filtering](https://blog.vespa.ai/constrained-approximate-nearest-neighbor-search/#post-filtering-strategy) - -These strategies can be configured in a rank profile using -[approximate-threshold](../reference/schemas/schemas.html#approximate-threshold) and -[post-filter-threshold](../reference/schemas/schemas.html#post-filter-threshold). -See -[Controlling the filtering behavior with approximate nearest neighbor search](https://blog.vespa.ai/constrained-approximate-nearest-neighbor-search/#controlling-the-filtering-behavior-with-approximate-nearest-neighbor-search) -for more details. - -Note that when using `pre-filtering` the following query operators are not included when evaluating the filter part of the query: -* [geoLocation](../reference/querying/yql.html#geolocation) -* [predicate](../reference/querying/yql.html#predicate) - -These are instead evaluated after the approximate nearest neighbors are retrieved, more like a `post-filter`. -This might cause the search to expose fewer hits to ranking than the wanted `totalTargetHits`. - -Since {% include version.html version="8.78" %} the `pre-filter` can be evaluated using -[multiple threads per query](../performance/practical-search-performance-guide.html#multithreaded-search-and-ranking). -This can be used to reduce query latency for larger vector datasets where the cost of evaluating the `pre-filter` is significant. -Note that searching the `HNSW` index is always single-threaded per query. -Multithreaded evaluation when using `post-filtering` has always been supported, -but this is less relevant as the `HNSW` index search first reduces the document candidate set based on `totalTargetHits`. - -## Nearest Neighbor Search Considerations - -* **totalTargetHits**: -The [totalTargetHits](../reference/querying/yql.html#totaltargethits) parameter -specifies how many hits one wants to expose to [ranking](../basics/ranking.html) in total over the content nodes -participating in the query (you can also set this per node using [targetHits](../reference/querying/yql.html#targethits)). -Approximate search exposes exactly `totalTargetHits` hits to `first-phase` ranking over the content nodes -as long as `totalTargetHits` hits are actually found and not filtered out. -Nearest neighbor search is typically used as an efficient retriever in a [phased ranking](../ranking/phased-ranking.html) -pipeline. See [performance sizing](../performance/sizing-search.html). - -* **Pagination**: -Pagination uses the standard [hits](../reference/api/query.html#hits) -and [offset](../reference/api/query.html#offset) query api parameters. -There is no caching of results in between pagination requests, -so a query for a higher `offset` will cause the search to be performed over again. -This aspect is no different from [sparse search](../ranking/wand.html) not using nearest neighbor query operator. - -* **Total hit count is not accurate**: -Technically, all vectors in the searchable index are neighbors. There is no strict boundary between a match -and no match. Both exact (`approximate:false`) and approximate (`approximate:true`) usages -of the [nearestNeighbor](../reference/querying/yql.html#nearestneighbor) query operator -does not produce an accurate `totalCount`. -This is the same behavior as with sparse dynamic pruning search algorithms like -[weakAnd](../reference/querying/yql.html#weakand) and [wand](../reference/querying/yql.html#wand). - -* **Grouping** counts are not accurate: -Grouping counts from [grouping](grouping.html) are not accurate when using [nearestNeighbor](../reference/querying/yql.html#nearestneighbor) -search. This is the same behavior as with other dynamic pruning search algorithms like -[weakAnd](../reference/querying/yql.html#weakand) and -[wand](../reference/querying/yql.html#wand). -See the [Result diversification](https://blog.vespa.ai/result-diversification-with-vespa/) -blog post on how grouping can be combined with nearest neighbor search. - - -## Scaling Approximate Nearest Neighbor Search - -### Memory -Vespa tensor fields are [in-memory](../content/attributes.html) data structures and so is the `HNSW` graph data structure. -For large vector datasets the primary memory resource usage relates to the raw vector field memory usage. - -Using lower tensor cell type precision can reduce memory footprint significantly, for example using `bfloat16` -instead of `float` saves close to 50% memory usage without significant accuracy loss. - -Vespa [tensor cell value types](../performance/feature-tuning.html#cell-value-types) include: - -* `int8` - 1 byte per value. Also used to represent [packed binary values](../rag/binarizing-vectors.html). -* `bfloat16` - 2 bytes per value. See [bfloat16 floating-point format](https://en.wikipedia.org/wiki/Bfloat16_floating-point_format). -* `float` - 4 bytes per value. Standard float. -* `double` - 8 bytes per value. Standard double. - - -### Search latency and document volume - -The `HNSW` greedy search algorithm is sublinear (close to log(N) where N is the number of vectors in the graph). -This has interesting properties when attempting to add more -nodes horizontally using [flat data distribution](../performance/sizing-search.html#data-distribution). -Even if the document volume per node is reduced by a factor of 10, the search latency is only reduced by 50%. -Still, flat scaling helps scale document volume, and increasing indexing throughput as vectors are partitioned -randomly over a set of nodes. - -Pure vector search applications (without filtering, or re-ranking) should attempt to scale up document volume by using -larger instance type and maximize the number of vectors per node. To scale with query throughput, -use [grouped data distribution](../performance/sizing-search.html#data-distribution) to replicate content. - -Note that strongly sublinear search is not necessarily true if the application -uses nearest neighbor search for candidate retrieval in a multiphase ranking pipeline, -or combines nearest neighbor search with filters. - - -## HNSW Operations -Changing the [distance-metric](../reference/schemas/schemas.html#distance-metric) -for a tensor field with `hnsw` index requires [restarting](../reference/schemas/schemas.html#changes-that-require-restart-but-not-re-feed), -but not re-indexing (re-feed vectors). Similar, changing the `max-links-per-node` and -`neighbors-to-explore-at-insert` construction parameters requires re-starting. diff --git a/en/querying/approximate-nn-hnsw.mdx b/en/querying/approximate-nn-hnsw.mdx new file mode 100644 index 0000000000..ff0fef4ecd --- /dev/null +++ b/en/querying/approximate-nn-hnsw.mdx @@ -0,0 +1,158 @@ +--- +title: "Approximate nearest neighbor search using HNSW index" +--- + +This document describes how to speed up searches for nearest neighbors in vector spaces by adding [HNSW index](../reference/schemas/schemas#index-hnsw) to tensor fields. For an introduction to nearest neighbor search, see [nearest neighbor search](/en/querying/nearest-neighbor-search) documentation, for practical usage of Vespa's nearest neighbor search, see [nearest neighbor search - a practical guide](/en/querying/nearest-neighbor-search-guide), and to have Vespa create vectors for you, see [embedding](../rag/embedding). + +Vespa implements a modified version of the Hierarchical Navigable Small World (HNSW) graph algorithm [paper](https://arxiv.org/abs/1603.09320). The implementation in Vespa supports: + +* **Filtering** - The search for nearest neighbors can be constrained by query filters. The [nearestNeighbor](../reference/querying/yql#nearestneighbor) query operator can be combined with other filters or query terms using the [Vespa query language](/en/querying/query-language). See the query examples in the [practical guide](/en/querying/nearest-neighbor-search-guide#combining-approximate-nearest-neighbor-search-with-query-filters). + +* **Multi-field vector Indexing** - A schema can include multiple indexed tensor fields and search any combination of them in a query. This is useful to support multiple models, multiple text sources, and multi-modal search such as indexing both a textual description and image for the same entity. + +* **Multi-vector Indexing** - A single document field can contain any number of vector values by defining it as a mixed tensor (a "map of vectors"). Documents will then be retrieved by the closest vector in each document compared to the query vector. See the [Multi-vector indexing sample application](https://github.com/vespa-engine/sample-apps/tree/master/multi-vector-indexing) for examples. This is commonly used to [index documents with multiple chunks](../rag/working-with-chunks). See also [this blog post](https://blog.vespa.ai/semantic-search-with-multi-vector-indexing/#implementation). + +* **Real Time Indexing** - CRUD (Create, Add, Update, Remove) vectors in the index in true real time. + +* **Mutable HNSW Graph** - No query or indexing overhead from searching multiple HNSW graphs. In Vespa, there is one graph per tensor field per content node. No segmented or partitioned graph where a query against a content node need to scan multiple HNSW graphs. + +* **Multithreaded Indexing** - The costly part when performing real time changes to the *HNSW* graph is distance calculations while searching the graph layers to find which links to change. These distance calculations are performed by multiple indexing threads. + +* **Multiple value types** - The cost driver of vector search is often storing the vectors in memory, which is required to produce accurate results at low latency. An effective way to reduce cost is to reduce the size of each vector value. Vespa supports double, float, bfloat16, int8 and [single-bit values](../rag/binarizing-vectors). Changing from float to bfloat16 can halve cost with negligible impact on accuracy, while single-bit values greatly reduce both memory and cpu costs, and can be effectively combined with larger vector values stored on disk as a paged attribute to be used for ranking. + +* **Optimized HNSW lookups** - ANN searches in Vespa [support](https://blog.vespa.ai/tweaking-ann-parameters/) both pre-and post-filtering, beam exploration, and filtering before distance calculation ("Acorn 1"). Tuning parameters for these makes it possible to strike a good balance between performance and accuracy for any data set. Vespa's [ANN tuning tool](https://vespa-engine.github.io/pyvespa/examples/ann-parameter-tuning-vespa-cloud) can be used to automate the process. + +## Using Vespa's approximate nearest neighbor search + +The query examples in [nearest neighbor search](/en/querying/nearest-neighbor-search) uses exact search, which has perfect accuracy. However, this is computationally expensive for large document volumes as distances are calculated for every document which matches the query filters. + +To enable fast approximate matching, the tensor field definition needs an `index` directive. A Vespa [document schema](../basics/schemas) can declare multiple tensor fields with `HNSW` enabled. + +```js expandable +field image_embeddings type tensor(i{},x[512]) { + indexing: summary | attribute | index + attribute { + distance-metric: angular + } + index { + hnsw { + max-links-per-node: 16 + neighbors-to-explore-at-insert: 100 + } + } +} + +field text_embedding type tensor(x[384]) { + indexing: summary | attribute | index + attribute { + distance-metric: prenormalized-angular + } + index { + hnsw { + max-links-per-node: 24 + neighbors-to-explore-at-insert: 200 + } + } +} +``` + +In the schema snippet above, fast approximate search is enabled by building an `HNSW` index for the `image_embeddings` and the `text_embedding` tensor fields. `image_embeddings` indexes multiple vectors per document, while `text_embedding` indexes one vector per document. + +The two vector fields use different [distance-metric](../reference/schemas/schemas#distance-metric) and `HNSW` index settings: + +* `max-links-per-node` - a higher value increases recall accuracy, but also memory usage, indexing and search cost. +* `neighbors-to-explore-at-insert` - a higher value increases recall accuracy, but also indexing cost. + +Choosing the value of these parameters affects both accuracy, search performance, memory usage and indexing performance. See [Billion-scale vector search with Vespa - part two](https://blog.vespa.ai/billion-scale-knn-part-two/) for a detailed description of these tradeoffs. See [HNSW index reference](../reference/schemas/schemas#index-hnsw) for details on the index parameters. + +### Indexing throughput + + +![Real-time indexing throughput](https://blog.vespa.ai/assets/2022-01-27-billion-scale-knn-part-two/throughput.png) + +The `HNSW` settings impacts indexing throughput. Higher values of `max-links-per-node` and `neighbors-to-explore-at-insert` reduces indexing throughput. Example from [Billion-scale vector search with Vespa - part two](https://blog.vespa.ai/billion-scale-knn-part-two/). + +### Memory usage + +Higher value of `max-links-per-node` impacts memory usage, higher values means higher memory usage: + +![Memory footprint](https://blog.vespa.ai/assets/2022-01-27-billion-scale-knn-part-two/memory.png) + +### Accuracy + +![Accuracy](https://blog.vespa.ai/assets/2022-01-27-billion-scale-knn-part-two/ann.png) + +Higher `max-links-per-node` and `neighbors-to-explore-at-insert` improves the quality of the graph and recall accuracy. As the search-time parameter [hnsw.exploreAdditionalHits](../reference/querying/yql#hnsw-exploreadditionalhits) is increased, the lower combination reaches about 70% recall@10, while the higher combination reaches about 92% recall@10. The improvement in accuracy needs to be weighted against the impact on indexing performance and memory usage. + +## Using approximate nearest neighbor search + +With an *HNSW* index enabled on the tensor field one can choose between approximate or exact (brute-force) search by using the [approximate query annotation](../reference/querying/yql#approximate) + +```json +{ + "yql": "select * from doc where {totalTargetHits: 10, approximate:false}nearestNeighbor(image_embeddings,query_image_embedding)", + "hits": 10 + "input.query(query_image_embedding)": [0.21,0.12,....], + "ranking.profile": "image_similarity" +} +``` + +By default, `approximate` is true when searching a tensor field with `HNSW` index enabled. The `approximate` parameter allows quantifying the accuracy loss of using approximate search. The loss can be calculated by performing an exact neighbor search using `approximate:false` and compare the retrieved documents with `approximate:true` and calculate the overlap@k metric. + +Note that exact searches over a large vector volume require adjustment of the [query timeout](../reference/api/query#timeout). The default [query timeout](../reference/api/query#timeout) is 500ms, which will be too low for an exact search over many vectors. + +In addition to [totalTargetHits](../reference/querying/yql#totaltargethits), there is a [hnsw.exploreAdditionalHits](../reference/querying/yql#hnsw-exploreadditionalhits) parameter which controls how many extra nodes in the graph (in addition to `totalTargetHits`) that are explored during the graph search. This parameter is used to tune accuracy quality versus query performance. + +## Combining approximate nearest neighbor search with filters + +The [nearestNeighbor](../reference/querying/yql#nearestneighbor) query operator can be combined with other query filters using the [Vespa query language](../reference/querying/yql) and its query operators. There are two high-level strategies for combining query filters with approximate nearest neighbor search: +* [pre-filtering](https://blog.vespa.ai/constrained-approximate-nearest-neighbor-search/#pre-filtering-strategy) (the default) +* [post-filtering](https://blog.vespa.ai/constrained-approximate-nearest-neighbor-search/#post-filtering-strategy) + +These strategies can be configured in a rank profile using [approximate-threshold](../reference/schemas/schemas#approximate-threshold) and [post-filter-threshold](../reference/schemas/schemas#post-filter-threshold). See [Controlling the filtering behavior with approximate nearest neighbor search](https://blog.vespa.ai/constrained-approximate-nearest-neighbor-search/#controlling-the-filtering-behavior-with-approximate-nearest-neighbor-search) for more details. + +Note that when using `pre-filtering` the following query operators are not included when evaluating the filter part of the query: + +* [geoLocation](../reference/querying/yql#geolocation) +* [predicate](../reference/querying/yql#predicate) + +These are instead evaluated after the approximate nearest neighbors are retrieved, more like a `post-filter`. This might cause the search to expose fewer hits to ranking than the wanted `totalTargetHits`. + +Since Vespa 8.78 the `pre-filter` can be evaluated using [multiple threads per query](../performance/practical-search-performance-guide#multithreaded-search-and-ranking). This can be used to reduce query latency for larger vector datasets where the cost of evaluating the `pre-filter` is significant. Note that searching the `HNSW` index is always single-threaded per query. Multithreaded evaluation when using `post-filtering` has always been supported, but this is less relevant as the `HNSW` index search first reduces the document candidate set based on `totalTargetHits`. + +## Nearest Neighbor Search Considerations + +* **totalTargetHits**: The [totalTargetHits](../reference/querying/yql#totaltargethits) parameter specifies how many hits one wants to expose to [ranking](../basics/ranking) in total over the content nodes participating in the query (you can also set this per node using [targetHits](../reference/querying/yql#targethits)). Approximate search exposes exactly `totalTargetHits` hits to `first-phase` ranking over the content nodes as long as `totalTargetHits` hits are actually found and not filtered out. Nearest neighbor search is typically used as an efficient retriever in a [phased ranking](../ranking/phased-ranking) pipeline. See [performance sizing](../performance/sizing-search). + +* **Pagination**: Pagination uses the standard [hits](../reference/api/query#hits) and [offset](../reference/api/query#offset) query api parameters. There is no caching of results in between pagination requests, so a query for a higher `offset` will cause the search to be performed over again. This aspect is no different from [sparse search](../ranking/wand) not using nearest neighbor query operator. + +* **Total hit count is not accurate**: Technically, all vectors in the searchable index are neighbors. There is no strict boundary between a match and no match. Both exact (`approximate:false`) and approximate (`approximate:true`) usages of the [nearestNeighbor](../reference/querying/yql#nearestneighbor) query operator does not produce an accurate `totalCount`. This is the same behavior as with sparse dynamic pruning search algorithms like [weakAnd](../reference/querying/yql#weakand) and [wand](../reference/querying/yql#wand). + +* **Grouping** counts are not accurate: Grouping counts from [grouping](/en/querying/grouping) are not accurate when using [nearestNeighbor](../reference/querying/yql#nearestneighbor) search. This is the same behavior as with other dynamic pruning search algorithms like [weakAnd](../reference/querying/yql#weakand) and [wand](../reference/querying/yql#wand). See the [Result diversification](https://blog.vespa.ai/result-diversification-with-vespa/) blog post on how grouping can be combined with nearest neighbor search. + +## Scaling Approximate Nearest Neighbor Search + +### Memory + +Vespa tensor fields are [in-memory](../content/attributes) data structures and so is the `HNSW` graph data structure. For large vector datasets the primary memory resource usage relates to the raw vector field memory usage. + +Using lower tensor cell type precision can reduce memory footprint significantly, for example using `bfloat16` instead of `float` saves close to 50% memory usage without significant accuracy loss. + +Vespa [tensor cell value types](../performance/feature-tuning#cell-value-types) include: + +* `int8` - 1 byte per value. Also used to represent [packed binary values](../rag/binarizing-vectors). +* `bfloat16` - 2 bytes per value. See [bfloat16 floating-point format](https://en.wikipedia.org/wiki/Bfloat16_floating-point_format). +* `float` - 4 bytes per value. Standard float. +* `double` - 8 bytes per value. Standard double. + +### Search latency and document volume + +The `HNSW` greedy search algorithm is sublinear (close to log(N) where N is the number of vectors in the graph). This has interesting properties when attempting to add more nodes horizontally using [flat data distribution](../performance/sizing-search#data-distribution). Even if the document volume per node is reduced by a factor of 10, the search latency is only reduced by 50%. Still, flat scaling helps scale document volume, and increasing indexing throughput as vectors are partitioned randomly over a set of nodes. + +Pure vector search applications (without filtering, or re-ranking) should attempt to scale up document volume by using larger instance type and maximize the number of vectors per node. To scale with query throughput, use [grouped data distribution](../performance/sizing-search#data-distribution) to replicate content. + +Note that strongly sublinear search is not necessarily true if the application uses nearest neighbor search for candidate retrieval in a multiphase ranking pipeline, or combines nearest neighbor search with filters. + +## HNSW Operations + +Changing the [distance-metric](../reference/schemas/schemas#distance-metric) for a tensor field with `hnsw` index requires [restarting](../reference/schemas/schemas#changes-that-require-restart-but-not-re-feed), but not re-indexing (re-feed vectors). Similar, changing the `max-links-per-node` and `neighbors-to-explore-at-insert` construction parameters requires re-starting. diff --git a/en/querying/document-summaries.html b/en/querying/document-summaries.html deleted file mode 100644 index 3e01ce56d4..0000000000 --- a/en/querying/document-summaries.html +++ /dev/null @@ -1,261 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Document Summaries" -redirect_from: -- /en/document-summaries.html ---- - -

    A document summary is the information that is shown for each document in a query result. -What information to include is determined by a document summary class: -A named set of fields with config on which information they should contain.

    - -

    A special document summary named default is always present and used by default. -This contains:

    -
      -
    • all fields which specifies in their indexing statements that they may be included in summaries -
    • all fields specified in any document summary -
    • sddocname -
    • documentid. -
    - -

    Summary classes are defined in the schema:

    - -
    -schema music {
    -
    -    document music {
    -        field artist type string {
    -            indexing: summary | index
    -        }
    -        field album type string {
    -            indexing: summary | index
    -            index: enable-bm25
    -        }
    -        field year type int {
    -            indexing: summary | attribute
    -        }
    -        field category_scores type tensor<float>(cat{}) {
    -            indexing: summary | attribute
    -        }
    -    }
    -
    -    document-summary my-short-summary {
    -        summary artist {}
    -        summary album {}
    -    }
    -}
    -
    - -

    See the schema reference for details.

    - -

    The summary class to use for a query is determined by the parameter -presentation.summary;: -

    -
    -$ vespa query "select * from music where album contains 'head'" \
    -  "presentation.summary=my-short-summary"
    -
    - -

    A common reason to define a document summary class is performance: -By configuring a document summary that only contains attributes the result can be generated -without disk accesses. Note that this may be needed to ensure only memory is accessed even if all fields are -attributes because the Document IDs -are not an attribute unless explicitly configured otherwise.

    - -

    Document summaries may also contain dynamic snippets and highlighted terms.

    - -

    The document summary class to use can also be issued programmatically to the fill() -method from a searcher, and multiple fill operations interleaved with programmatic filtering can be used to -optimize data access and transfer when programmatic filtering in a Searcher is used.

    - - - -

    Selecting summary fields in YQL

    - -

    A YQL statement can also be used to filter which fields from a document summary -to include in results. Note that this is just a field filter in the container - -a summary containing all fields of a summary class is always -fetched from content nodes, so to optimize performance it is necessary to create custom summary classes. -

    - -
    -$ vespa query "select artist, album, documentid, sddocname from music where album contains 'head'"
    -
    -
    {% highlight json %}
    -{
    -    "root": { },
    -        "children": [
    -            {
    -                "id": "id:mynamespace:music::a-head-full-of-dreams",
    -                "relevance": 0.16343879032006284,
    -                "source": "mycontentcluster",
    -                "fields": {
    -                    "sddocname": "music",
    -                    "documentid": "id:mynamespace:music::a-head-full-of-dreams",
    -                    "artist": "Coldplay",
    -                    "album": "A Head Full of Dreams"
    -                }
    -            }
    -        ]
    -    }
    -}
    -{% endhighlight %}
    - -

    -Use * to select all the fields of the chosen document summary class used, -(which is default by default). - -

    -$ vespa query "select * from music where album contains 'head'"
    -
    -
    {% highlight json %}
    -{
    -    "root": { },
    -        "children": [
    -            {
    -                "id": "id:mynamespace:music::a-head-full-of-dreams",
    -                "relevance": 0.16343879032006284,
    -                "source": "mycontentcluster",
    -                "fields": {
    -                    "sddocname": "music",
    -                    "documentid": "id:mynamespace:music::a-head-full-of-dreams",
    -                    "artist": "Coldplay",
    -                    "album": "A Head Full of Dreams",
    -                    "year": 2015,
    -                    "category_scores": {
    -                        "type": "tensor(cat{})",
    -                        "cells": {
    -                            "pop": 1.0,
    -                            "rock": 0.20000000298023224,
    -                            "jazz": 0.0
    -                        }
    -                    }
    -                }
    -            }
    -        ]
    -    }
    -}
    -{% endhighlight %}
    - - -

    Summary field rename

    - -

    Summary classes may define fields by names not used in the document type: -

    -    document-summary rename-summary {
    -        summary artist_name {
    -            source: artist
    -        }
    -    }
    -
    - -

    Refer to the schema reference for -adding -attribute and - -non-attribute fields - some changes require re-indexing.

    - - - - - - -

    Dynamic snippets

    - -

    Use dynamic -to generate dynamic snippets from fields based on the query keywords. -Example from Vespa Documentation Search - see the - - schema: -

    -
    -document doc {
    -
    -    field content type string {
    -        indexing: summary | index
    -        summary : dynamic
    -    }
    -
    - -

    A query for document summary returns:

    - -
    - Use document summaries to configure which fields ... - indexing: summary | index } } document-summary - titleyear { summary title ... -
    - -

    The example above creates a dynamic summary with the matched terms highlighted. -The latter is called bolding -and can be enabled independently of dynamic summaries.

    - -

    Refer to the reference for the response format.

    - -

    Dynamic snippet configuration

    - -

    You can configure generation of dynamic snippets by adding an instance of the -vespa.config.search.summary.juniperrc config -in services.xml inside the <content> cluster tag for the content cluster in question. E.g:

    - -
    -<content ...>
    -    ...
    -    <config name="vespa.config.search.summary.juniperrc">
    -      <max_matches>2</max_matches>
    -      <length>1000</length>
    -      <surround_max>500</surround_max>
    -      <min_length>300</min_length>
    -    </config>
    -    ...
    -</content>
    -
    - -

    Numbers here are in bytes.

    - - - -

    Performance

    -

    - Attribute fields are held in memory. - This means summaries are memory-only operations if all fields requested are attributes, - and is the optimal way to get high query throughput. - The other document fields are stored as blobs in the - document store. - Requesting these fields may therefore require a disk access, increasing latency. -

    -{% include important.html content=' -The default summary class contains the -the documentid field, -which by default is stored in the document store only. Hence, using it -will cause accesses to the document store by default. -For maximum query throughput using memory-only access, -set documentid -to attribute in the schema -or use a custom summary class with attributes only. -' %} - -

    - When using additional summary classes to increase performance, - only the network data size is changed - the data read from storage is unchanged. - Having "debug" fields with summary enabled will hence also affect the - amount of information that needs to be read from disk. -

    -

    - See query execution - - breakdown of the summary (a.k.a. result processing, rendering) phase: -

    -
      -
    • The document summary latency on the content node, - tracked by content_proton_search_protocol_docsum_latency_average.
    • -
    • Getting data across from content nodes to containers.
    • -
    • Deserialization from internal binary formats (potentially) to Java objects - if touched in a Searcher, - and finally serialization to JSON (default rendering) + rendering and network.
    • -
    -

    - The work, and thus latency, increases with more hits. - Use query tracing to analyze performance. -

    -

    - Refer to content node summary cache. -

    diff --git a/en/querying/document-summaries.mdx b/en/querying/document-summaries.mdx new file mode 100644 index 0000000000..4ea0fd0b6e --- /dev/null +++ b/en/querying/document-summaries.mdx @@ -0,0 +1,194 @@ +--- +title: "Document Summaries" +--- + +A *document summary* is the information that is shown for each document in a query result. What information to include is determined by a *document summary class*: A named set of fields with config on which information they should contain. + +A special document summary named `default` is always present and used by default. This contains: + +- all fields which specifies in their indexing statements that they may be included in summaries +- all fields specified in any document summary +- [sddocname](../reference/querying/default-result-format#sddocname) +- [documentid](../reference/querying/default-result-format#documentid). + +Summary classes are defined in the schema: + +```js +schema music { + + document music { + field artist type string { + indexing: summary | index + } + field album type string { + indexing: summary | index + index: enable-bm25 + } + field year type int { + indexing: summary | attribute + } + field category_scores type tensor(cat{}) { + indexing: summary | attribute + } + } + + document-summary my-short-summary { + summary artist {} + summary album {} + } +} +``` + +See the [schema reference](../reference/schemas/schemas#summary) for details. + +The summary class to use for a query is determined by the parameter [presentation.summary](../reference/api/query#presentation.summary);: + +```bash +$ vespa query "select * from music where album contains 'head'" \ + "presentation.summary=my-short-summary" +``` + +A common reason to define a document summary class is [performance](#performance): By configuring a document summary which only contains attributes the result can be generated without disk accesses. Note that this is needed to ensure only memory is accessed even if all fields are attributes because the [document ID](../schemas/documents#document-ids) is not stored as an attribute. + +Document summaries may also contain [dynamic snippets and highlighted terms](#dynamic-snippets). + +The document summary class to use can also be issued programmatically to the `fill()` method from a searcher, and multiple fill operations interleaved with programmatic filtering can be used to optimize data access and transfer when programmatic filtering in a Searcher is used. + +## Selecting summary fields in YQL + +A [YQL](/en/querying/query-language) statement can also be used to filter which fields from a document summary to include in results. Note that this is just a field filter in the container - a summary containing all fields of a summary class is always fetched from content nodes, so to optimize performance it is necessary to create custom summary classes. + +```js +$ vespa query "select artist, album, documentid, sddocname from music where album contains 'head'" +``` + +```json +{ + "root": { }, + "children": [ + { + "id": "id:mynamespace:music::a-head-full-of-dreams", + "relevance": 0.16343879032006284, + "source": "mycontentcluster", + "fields": { + "sddocname": "music", + "documentid": "id:mynamespace:music::a-head-full-of-dreams", + "artist": "Coldplay", + "album": "A Head Full of Dreams" + } + } + ] + } +} +``` + +Use `*` to select all the fields of the chosen document summary class used, (which is `default` by default). + +```js +$ vespa query "select * from music where album contains 'head'" +``` + +```json +{ + "root": { }, + "children": [ + { + "id": "id:mynamespace:music::a-head-full-of-dreams", + "relevance": 0.16343879032006284, + "source": "mycontentcluster", + "fields": { + "sddocname": "music", + "documentid": "id:mynamespace:music::a-head-full-of-dreams", + "artist": "Coldplay", + "album": "A Head Full of Dreams", + "year": 2015, + "category_scores": { + "type": "tensor(cat{})", + "cells": { + "pop": 1.0, + "rock": 0.20000000298023224, + "jazz": 0.0 + } + } + } + } + ] + } +} +``` + +## Summary field rename + +Summary classes may define fields by names not used in the document type: + +```js +document-summary rename-summary { + summary artist_name { + source: artist + } + } +``` + +Refer to the [schema reference](../reference/schemas/schemas#source) for adding [attribute](../reference/schemas/schemas#add-or-remove-an-existing-document-field-from-document-summary) and [non-attribute](../reference/schemas/schemas#add-or-remove-a-new-non-attribute-document-field-from-document-summary) fields - some changes require re-indexing. + +## Dynamic snippets + +Use [dynamic](../reference/schemas/schemas#summary) to generate dynamic snippets from fields based on the query keywords. Example from Vespa Documentation Search - see the [schema](https://github.com/vespa-cloud/vespa-documentation-search/blob/main/src/main/application/schemas/doc.sd): + +```js +document doc { + + field content type string { + indexing: summary | index + summary : dynamic + } +``` + +A query for *document summary* returns: + +> *Use document summaries to configure which fields ... indexing: summary | index `}}` document-summary titleyear `{ summary title ...`* + +The example above creates a dynamic summary with the matched terms highlighted. The latter is called [bolding](../reference/schemas/schemas#bolding) and can be enabled independently of dynamic summaries. + +Refer to the [reference](../reference/schemas/schemas#summary) for the response format. + +### Dynamic snippet configuration + +You can configure generation of dynamic snippets by adding an instance of the [vespa.config.search.summary.juniperrc config](https://github.com/vespa-engine/vespa/blob/master/searchsummary/src/vespa/searchsummary/config/juniperrc.def) in services.xml inside the `` cluster tag for the content cluster in question. E.g: + +```xml + + ... + + 2 + 1000 + 500 + 300 + + ... + +``` + +Numbers here are in bytes. + +## Performance + +[Attribute](../content/attributes) fields are held in memory. This means summaries are memory-only operations if all fields requested are attributes, and is the optimal way to get high query throughput. The other document fields are stored as blobs in the [document store](../content/proton#document-store). Requesting these fields may therefore require a disk access, increasing latency. + + +**Important:** + +The default summary class will access the document store as it includes the [documentid](../reference/querying/default-result-format#documentid) field which is stored there. For maximum query throughput using memory-only access, use a dedicated summary class with attributes only. + + +When using additional summary classes to increase performance, only the network data size is changed - the data read from storage is unchanged. Having "debug" fields with summary enabled will hence also affect the amount of information that needs to be read from disk. + +See [query execution](/en/querying/query-api#query-execution) - breakdown of the summary (a.k.a. result processing, rendering) phase: + +- The document summary latency on the content node, tracked by [content_proton_search_protocol_docsum_latency_average](../operations/metrics). +- Getting data across from content nodes to containers. +- Deserialization from internal binary formats (potentially) to Java objects if touched in a [Searcher](../applications/searchers), and finally serialization to JSON (default rendering) + rendering and network. + +The work, and thus latency, increases with more [hits](../reference/api/query#hits). Use [query tracing](/en/querying/query-api#query-tracing) to analyze performance. + +Refer to [content node summary cache](../performance/caches-in-vespa#content-node-summary-cache). diff --git a/en/querying/federation.html b/en/querying/federation.html deleted file mode 100644 index d53e4fd066..0000000000 --- a/en/querying/federation.html +++ /dev/null @@ -1,798 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Federation" -redirect_from: -- /en/federation.html ---- - -Federation example -

    -The Vespa Container allows multiple sources of data to -be federated to a common search service. -The sources of data may be both search clusters belonging to the same application, -or external services, backed by Vespa or any other kind of service. -The container may be used as a pure federation platform by -setting up a system consisting solely of container nodes federating to external services. -

    -

    -This document gives a short intro to federation, -explains how to create an application package doing federation -and shows what support is available for choosing the sources given a query, -and the final result given the query and some source specific results. -

    -Federation allows users to access data from multiple -sources of various kinds through one interface. This is useful to: -

    -
      -
    • enrich the results returned from an application with auxiliary - data, like finding appropriate images to accompany news - articles.
    • -
    • provide more comprehensive results by finding data from - alternative sources in the cases where the application has none, - like back-filling web results.
    • -
    • create applications whose main purpose is not to provide access - to some data set but to provide users or frontend applications a - single starting point to access many kinds of data from various - sources. Examples are browse pages created dynamically for any - topic by pulling together data from external sources.
    • -
    -

    The main tasks in creating a federation solution are:

    -
      -
    1. creating connectors to the various sources
    2. -
    3. selecting the data sources which will receive a given query
    4. -
    5. rewriting the received request to an executable query returning the - desired data from each source
    6. -
    7. creating the final result by selecting from, organizing and - combining the returned data from each selected source
    8. -
    -

    -The container aids with these tasks by providing a way to -organize a federated execution as a set of search chains which can be -configured through the application package. -Read the Container intro and -Chained components before proceeding. -Refer to the com.yahoo.search.federation - - Javadoc. -

    - - - -

    Configuring Providers

    - -

    A provider is a search chain that produces - data (in the form of a Result) from a data source. - The provider must contain a Searcher which connects to - the data source and produces a Result from the returned data. - Configure a provider as follows: -

    -<search>
    -    <provider id="my-provider">
    -        <searcher id="MyDataFetchingSearcher"
    -                  bundle="the name in artifactId in pom.xml" />
    -    </provider>
    -</search>
    -
    - -

    You can add multiple searchers in the provider just like in other chains.

    - -

    Search chains that provide data from some content cluster in the same application -are also providers. To explicitly configure a provider talking to internal -content clusters, set the attribute type="local" on the provider. That will automatically - add the searchers necessary to talk to internal content clusters to the search chain. - Example: querying this provider will not lowercase / stem terms: -

    -
    -<provider id="myProvider"
    -          type="local"
    -          cluster="mydocs"
    -          excludes="com.yahoo.prelude.searcher.BlendingSearcher
    -              com.yahoo.prelude.querytransform.StemmingSearcher
    -              com.yahoo.search.querytransform.VespaLowercasingSearcher">
    -
    - - - -

    Configuring Sources

    -

    -A single provider may be used to produce multiple kinds of results. -To implement and present each kind of result, we can use sources. -A source is a search chain that provides a specific kind of result -by extending or modifying the behavior of one or more providers. -

    -Suppose that we want to retrieve two kinds of results from -my-provider: Web results and java API documentation: -

    -<search>
    -    <provider id="my-provider">
    -        <searcher id="MyDataFetchingSearcher"
    -                  bundle="the name in artifactId in pom.xml" />
    -        <source id="web" />
    -        <source id="java-api">
    -            <searcher id="com.yahoo.example.JavaApiSearcher"
    -                      bundle="the name in artifactId in pom.xml" />
    -        </source>
    -    </provider>
    -
    -

    -This results in two source search chains being created, -web@my-provider and java-api@my-provider. -Each of them constitutes a source, -namely web and java-api respectively. -As the example suggests, these search chains are named -after the source and the enclosing provider. -The @-sign in the name should be read as in, -so web@my-provider should for example be read as web in my-provider. -

    -The JavaApiSearcher is responsible for modifying the query so that we -only get hits from the java API documentation. We added this searcher -directly inside the source element; source search chains and providers -are both instances of search chains. All the options for configuring -regular search chains are therefore also available for them. -

    -How does the web@my-provider -and java-api@my-provider source search chains use the -my-provider provider to send queries to the external -service? Internally, the source search chains inherit from -the enclosing provider. Since the provider contains searchers that -know how to talk to the external service, the sources will also -contain the same searchers. As an example, consider the "web" search -chain; It will contain exactly the same searcher instances as the -my-provider search chain. By organizing chains for talking -to data providers, we can reuse the same connections and logic -for talking to remote services ("providers") for multiple purposes ("sources").

    - -

    The provider search chain my-provider is not modified by adding sources. -To verify this, try to send queries to the three search chains -my-provider, web@my-provider and java-api@my-provider. -

    - - -

    Multiple Providers per Source

    -

    -You can create a source that consists of source search chains from -several providers. Effectively, this lets you vary which provider -should be used to satisfy each request to the source: -

    -<search>
    -    <provider id="my-provider">
    -        <searcher id="MyDataFetchingSearcher"
    -                  bundle="the name in artifactId in pom.xml" />
    -        <source id="common-search" />
    -    </provider>
    -
    -    <provider id="news-search">
    -        <searcher id="MyNewsDataFetchingSearcher"
    -                  bundle="the name in artifactId in pom.xml" />
    -        <source idref="common-search" />
    -    </provider>
    -
    -

    -Here, the two source search chains common-search@news-search and -common-search@my-provider constitutes a single source common-search. -The source search chains using the idref attribute are -called participants, while the ones using the id attribute are called leaders. -Each source must consist of a single leader and zero or more participants. -

    -Per default, only the leader search chain is used when federating to a source. -To use one of the participants instead, -use sources and source: -

    -
    -http://[host]:[port]/?sources=common-search&source.common-search.provider=news-search
    -
    - - - - -

    Federation

    -

    -Now we can search both the web and the java API documentation at the -same time, and get a combined result set back. -We achieve this by setting up a federation searcher: -

    -<search>
    -    <provider id="my-provider">
    -        <searcher id="MyDataFetchingSearcher"
    -                  bundle="the name in artifactId in pom.xml" />
    -        <source id="web" />
    -        <source id="java-api">
    -            <searcher id="com.yahoo.example.JavaApiSearcher"
    -                      bundle="the name in artifactId in pom.xml" />
    -        </source>
    -    </provider>
    -    <chain id="combined">
    -        <federation id="combinator">
    -            <source idref="web" />
    -            <source idref="java-api" />
    -        </federation>
    -    </chain>
    -
    -

    -Inside the Federation element, we list the sources we want to use. -Do not let the name source fool you; -If it behaves like a source, then you can use it as a source -(i.e. all types of search chains including providers are accepted). -As an example, try replacing the web reference with my-provider. -

    -When searching, select a subset of the sources specified in the federation element -by specifying the sources query parameter. -

    - - - -

    Built-in Federation

    -

    -The built-in search chains native and -vespa contain a federation searcher named federation. -This searcher has been configured to federate to: -

      -
    • All sources
    • -
    • All providers that does not contain a source
    • -
    -

    -If configuring your own federation searcher, you are not limited to a subset of these sources - -you can use any provider, source or search chain. -

    - - - -

    Inheriting default Sources

    -

    -To get the same sources as the built-in federation searcher, inherit the default source set: -

    -
    -<search>
    -    <chain id="my-chain">
    -        <federation id="combinator">
    -            <source-set inherits="default" />
    -            ...
    -        </federation>
    -    </chain>
    -</search>
    -
    - - - -

    Changing content cluster chains

    -

    -With the information above, we can create a configuration where we modify -the search chain sending queries to and receiving queries form a single content cluster -(here, removing a searcher and adding another): -

    -
    -<search>
    -    <chain id="default" inherits="vespa">
    -        <federation id="federationSearcher">
    -            <source id="local"/>
    -        </federation>
    -    </chain>
    -    <provider cluster="my_content" id="local" type="local"
    -              excludes="com.yahoo.search.querytransform.NGramSearcher">
    -        <searcher id="ai.vespa.example.OrNGramSearcher"
    -                  bundle="the name in artifactId in pom.xml"/>
    -    </provider>
    -</search>
    -
    - - - -

    Timeout behavior

    -

    -What if we want to limit how much time a provider is allowed to use to answer a query? -

    -
    -<search>
    -    <provider id="my-provider">
    -        <federationoptions timeout="100 ms" />
    -        <searcher id="MyDataFetchingSearcher"
    -                  bundle="the name in artifactId in pom.xml" />
    -        <source id="web" />
    -        <source id="java-api">
    -            <searcher id="com.yahoo.example.JavaApiSearcher"
    -                      bundle="the name in artifactId in pom.xml" />
    -        </source>
    -    </provider>
    -
    -

    -The provider search chain will then be limited to use 100 ms to execute each query. -The Federation layer allows all providers to continue until the -non-optional provider with the longest timeout is finished or canceled. -

    -In some cases it is useful to be able to keep executing the request to a provider -longer than we are willing to wait for it in that particular query. -This allows us to populate caches inside sources -which can only meet the timeout after caches are populated. -To use this option, specify a request timeout -for the provider: -

    -
    -<search>
    -    <provider id="my-provider">
    -        <federationoptions timeout="100 ms" requestTimeout="10000ms" />
    -        ...
    -    </provider>
    -
    -

    Also see Searcher timeouts.

    - - - -

    Non-essential Providers

    -

    -Now let us add a provider that retrieves ads: -

    -
    -<search>
    -    <provider id="ads">
    -        <searcher id="MyAdFetcher"
    -                  bundle="the name in artifactId in pom.xml" />
    -    </provider>
    -
    -

    -Suppose that it is more important to return the result to the user as -fast as possible, than to retrieve ads. -To signal this, we mark the ads provider as optional: -

    -
    -<search>
    -    <provider id="ads">
    -        <federationoptions optional="true" />
    -        <searcher id="MyAdFetcher"
    -                  bundle="the name in artifactId in pom.xml" />
    -    </provider>
    -
    -

    -The Federation searcher will then only wait for ads as long as it waits for mandatory providers. -If the ads are available in time, they are used, otherwise they are dropped. -

    -If only optional providers are selected for Federation, they will all be treated as mandatory. -Otherwise, they would not get a chance to return any results. -

    - - - -

    Federation options inheritance

    -

    -The sources automatically use the same Federation options as the enclosing provider. -override one or more of the federation options in the sources: -

    -<search>
    -    <provider id="my-provider">
    -        <federationoptions timeout="100ms" />
    -        <searcher id="MyDataFetchingSearcher"
    -                  bundle="the name in artifactId in pom.xml" />
    -        <source id="web">
    -            <federationoptions timeout="200ms" />
    -        </source>
    -        <source id="java-api">
    -            <searcher id="com.yahoo.example.JavaApiSearcher"
    -                      bundle="the name in artifactId in pom.xml" />
    -        </source>
    -    </provider>
    -
    -

    -You can use a single source in different Federation searchers. -If you send queries with different cost to the same source from different federation searchers, -you might also want to override the federation options for when they are used: -

    -
    -<search>
    -    <provider id="my-provider">
    -        <federationoptions timeout="100ms" />
    -        <searcher id="MyDataFetchingSearcher"
    -                  bundle="the name in artifactId in pom.xml" />
    -        <source id="web">
    -            <federationoptions timeout="200ms" />
    -        </source>
    -        <source id="java-api">
    -            <searcher id="com.yahoo.example.JavaApiSearcher"
    -                      bundle="the name in artifactId in pom.xml" />
    -        </source>
    -    </provider>
    -    <chain id="combined">
    -        <federation id="combinator">
    -            <source idref="web">
    -                <federationoptions timeout="2.5s" />
    -            </source>
    -            <source idref="java-api" />
    -        </federation>
    -    </chain>
    -
    - - - -

    Selecting Search Chains programmatically

    -

    -If we have complicated rules for when a search chain should be used, -we can select search chains programmatically instead of setting up sources under federation in services.xml. -The selection code is implemented as a - -TargetSelector. This TargetSelector is used by registering it on a federation searcher. -

    {% highlight java %}
    -package com.yahoo.example;
    -
    -import com.google.common.base.Preconditions;
    -import com.yahoo.component.chain.Chain;
    -import com.yahoo.processing.execution.chain.ChainRegistry;
    -import com.yahoo.search.Query;
    -import com.yahoo.search.Result;
    -import com.yahoo.search.result.Hit;
    -import com.yahoo.search.Searcher;
    -import com.yahoo.search.federation.selection.FederationTarget;
    -import com.yahoo.search.federation.selection.TargetSelector;
    -import com.yahoo.search.searchchain.model.federation.FederationOptions;
    -
    -import java.util.Arrays;
    -import java.util.Collection;
    -
    -class MyTargetSelector implements TargetSelector {
    -
    -    @Override
    -    public Collection> getTargets(Query query,
    -                                                           ChainRegistry searcherChainRegistry) {
    -        Chain searchChain = searcherChainRegistry.getComponent("my-chain");
    -        Preconditions.checkNotNull(searchChain, "No search chain named 'my-chain' exists in services.xml");
    -
    -        return Arrays.asList(new FederationTarget<>(searchChain, new FederationOptions(), null));
    -    }
    -
    -    @Override
    -    public void modifyTargetQuery(FederationTarget target, Query query) {
    -        query.setHits(10);
    -    }
    -
    -    @Override
    -    public void modifyTargetResult(FederationTarget target, Result result) {
    -        for (Hit hit: result.hits()) {
    -            hit.setField("my-field", "hello-world");
    -        }
    -    }
    -
    -}
    -{% endhighlight %}
    -

    -The target selector chooses search chains for the federation searcher. -In this example, MyTargetSelector.getTargets returns a single chain named "my-chain" -that has been set up in services.xml. -

    -Before executing each search chain, the federation searcher allows the target selector -to modify the query by calling modifyTargetQuery. -In the example, the number of hits to retrieve is set to 10. -

    -After the search chain has been executed, the federation searcher allows the target selector -to modify the result by calling modifyTargetResult. -In the example, each hit gets a field called "my-field" with the value "hello-world". -

    -Configure a federation searcher to use a target selector in services.xml. -Only a single target selector is supported. -

    -<search>
    -    <chain id="my-chain">
    -      <federation id="my-federation">
    -          <target-selector id="com.yahoo.example.MyTargetSelector"
    -                           bundle="the name in artifactId in pom.xml" />
    -      </federation>
    -    </chain>
    -
    -

    -We can also set up both a target-selector and normal sources. -The federation searcher will then send queries both to programmatically selected sources -and those that would normally be selected without the target selector: -

    -
    -<search>
    -    <chain id="my-chain">
    -        <federation id="my-federation">
    -            <target-selector id="com.yahoo.example.MyTargetSelector"
    -                             bundle="the name in artifactId in pom.xml" />
    -            <source idref="java-api" />
    -            <source-set inherits="default" />
    -            ...
    -        </federation>
    -    </chain>
    -
    - - - -

    Example: Setting up a Federated Service

    -

    -A federation application is created by providing custom searcher -components performing the basic federation tasks and combining these -into chains in a federation setup in -services.xml. -For example, this is a complete configuration which sets up a cluster of -container nodes (having 1 node) which federates to the another Vespa -service (news) and to some web service: -

    -
    -<?xml version="1.0" encoding="utf-8" ?>
    -<services version="1.0">
    -    <admin version="2.0">
    -        <adminserver hostalias="node1"/>
    -    </admin>
    -
    -    <container id="test" version="1.0">
    -        <nodes>
    -            <node hostalias="node1"/>
    -        </nodes>
    -        <search>
    -            <provider id="news">
    -                <searcher id="com.yahoo.example.NewsCustomerIdSearcher"
    -                          bundle="the name in artifactId in pom.xml" />
    -            </provider>
    -            <provider id="webService">
    -                <searcher id="com.yourdomain.WebProviderSearcher"
    -                          bundle="the name in artifactId in pom.xml" />
    -            </provider>
    -        </search>
    -    </container>
    -</services>
    -
    -

    -This creates a configuration of search chains like: -

    -Federation example -

    -Each provider is a search chain ending in a Searcher -forwarding the query to a remote service. -In addition, there is a main chain (included by default) ending in a FederationSearcher, -which by default forwards the query to all the providers in parallel. -The provider chains returns their result upwards to the federation searcher -which merges them into a complete result which is returned up the main chain. -

    -This services file, an implementation of the example classes (see below), -and hosts.xml -listing the container nodes, is all that is needed to set up and -deploy -an application federating to multiple sources. -For a reference to these XML sections, -see the chains reference. -

    -The following sections outlines how this can be elaborated into a -solution producing more user-friendly federated results. -

    - - -

    Selecting Sources

    -

    -To do the best possible job of bringing relevant data to the user, -we should send every query to all sources, and decide what data to -include when all the results are available, and we have as much information as possible at hand. -In general this is not advisable because of the resource cost involved, -so we must select a subset based on information in the query. -This is best viewed as a probabilistic optimization problem: -The selected sources should be the ones having a high enough probability of being useful -to offset the cost of querying it. -

    -Any Searcher which is involved in selecting sources or processing the -entire result should be added to the main search chain, -which was created implicitly in the examples above. -To do this, the main chain should be created explicitly: -

    -
    -<?xml version="1.0" encoding="utf-8" ?>
    -<services version="1.0">
    -    <admin version="2.0">
    -        <adminserver hostalias="node1"/>
    -    </admin>
    -    <container id="test" version="1.0">
    -        <nodes>
    -            <node hostalias="node1"/>
    -        </nodes>
    -        <search>
    -            <chain id="default" inherits="native">
    -                <searcher id="com.yahoo.example.ResultBlender"
    -                          bundle="the name in artifactId in pom.xml" />
    -            </chain>
    -            <provider id="news">
    -                <searcher id="com.yahoo.example.NewsCustomerIdSearcher"
    -                          bundle="the name in artifactId in pom.xml" />
    -            </provider>
    -            <provider id="webService">
    -                <searcher id="com.yahoo.example.ExampleProviderSearcher"
    -                          bundle="the name in artifactId in pom.xml" />
    -            </provider>
    -        </search>
    -    </container>
    -</services>
    -
    -

    -This adds an explicit main chain to the configuration -which has two additional searchers in addition to those inherited -from the native chain, which includes the FederationSearcher. -Note that if the full Vespa functionality is needed, -the vespa chain should be inherited rather than native. -

    -The chain called default will be invoked if no -searchChain parameter is given in the query. -

    -To learn more about creating Searcher components, -see searcher development. -

    - - -

    Rewriting Queries to Individual Providers

    -

    -The provider searchers are responsible for accepting a Query object, -translating it to a suitable request to the backend in question -and deserializing the response into a Result object. -There is often a need to modify the query to match the particulars of a provider before passing it on: -

      -
    • To get results from the provider which matches the determined - interpretation and intent as well as possible, the query may need - to be rewritten using detailed information about the provider
    • -
    • Parameters beyond the basic ones supported by each provider - searcher may need to be translated to the provider
    • -
    • There may be a need for provider specific business rules
    • -
    -

    -These query changes may range in complexity from setting a query parameter, -applying some source specific information to the query -or transferring all the relevant query state into a new object -representation which is consumed by the provider searcher. -

    -This example shows a searcher adding a customer id to the news request: -

    -
    {% highlight java %}
    -package com.yahoo.example;
    -
    -import com.yahoo.search.searchchain.Execution;
    -import com.yahoo.search.*;
    -
    -public class NewsCustomerIdSearcher extends Searcher {
    -
    -    @Override
    -    public Result search(Query query, Execution execution) {
    -        String customerId="provider.news.custid";
    -        if (query.properties().get(customerId) == null)
    -            query.properties().set(customerId, "yahoo/test");
    -        if (query.getTraceLevel() >= 3)
    -            query.trace("News provider: Will use "
    -                + customerId + "=" + query.properties().get(customerId), false, 3);
    -        return execution.search(query);
    -    }
    -
    -}
    -{% endhighlight %}
    -

    -This searcher should be added to the news source chain as shown above. -

    -You may have noticed that we have referred to the search chains -talking to a service as a provider -while referring to selection of sources. -The reason for making this distinction is that it is sometimes useful to treat different kinds of -processing of queries and results to/from the same service as different sources. -Hence, it is possible to create source search chains -in addition to the provider chains in services.xml. -Each such source will refer to a provider (by inheriting the provider chain) -but include some searchers specific to that source. -Selection and routing of the query from the federation searchers is always to sources, not providers. -By default, if no source tags are added in the provider, -each provider implicitly creates a source by the same name. -

    - - -

    Processing Results

    -

    -When we have selected the sources, created queries fitting to get -results from each source and executed those queries, -we have produced a result which contains a HitGroup per source -containing the list of hits from that source. -These results may be returned in XML as is, -preserving the structure as XML, by requesting -the page result format: -

    -
    -http://[host]:[port]/search/?query=test&presentation.format=page
    -
    -

    -However, this is not suitable for presenting to the user in most cases. -What we want to do is select the subset of the hits having the highest probable utility to the user, -organized in a way that maximizes the user experience. -This is not an easy task, and we will not attempt to solve it here, -other than noting that any solution should make use of both the information in the intent model -and the information within the results from each source, -and that this is a highly connected optimization problem -because the utility of including some data in the result depends on what other data is included. -

    -Here we will just use a searcher which shows how this is done in principle, -this searcher flattens the news and web service hit groups into a single list of hits, -where only the highest ranked news ones are included: -

    {% highlight java %}
    -package com.yahoo.example;
    -
    -import com.yahoo.search.*;
    -import com.yahoo.search.result.*;
    -import com.yahoo.search.searchchain.Execution;
    -
    -public class ResultBlender extends Searcher {
    -
    -    @Override
    -    public Result search(Query query,Execution execution) {
    -        Result result = execution.search(query);
    -        HitGroup news = (HitGroup)result.hits().remove("source:news");
    -        HitGroup webService = (HitGroup)result.hits().remove("source:webService");
    -        if (webService == null) return result;
    -        result.hits().addAll(webService.asList());
    -        if (news == null) return result;
    -        for (Hit hit : news.asList())
    -            if (shouldIncludeNewsHit(hit))
    -                result.hits().add(hit);
    -        return result;
    -    }
    -
    -    private boolean shouldIncludeNewsHit(Hit hit) {
    -        if (hit.isMeta()) return true;
    -        if (hit.getRelevance().getScore() > 0.7) return true;
    -        return false;
    -    }
    -
    -}
    -{% endhighlight %}
    -

    -The optimal result to return to the user is not necessarily one flattened list. -In some cases it may be better to keep the source organization, or to pick some other organization. -The page result format -requested in the query above is able to represent any hierarchical organization as XML. -A more realistic version of this searcher will use that to choose between some -predefined layouts which the frontend in question knows how to handle, -and choose some way of grouping the available hits suitable for the selected layout. -

    -This searcher should be added to the main (default) search chain in -services.xml together with the SourceSelector (the order does not matter). -

    - - -

    Unit Testing the Result Processor

    -

    -Unit test example for the Searcher above: -

    -
    {% highlight java %}
    -package com.yahoo.search.example.test;
    -
    -import org.junit.Test;
    -import com.yahoo.search.searchchain.*;
    -import com.yahoo.search.example.ResultBlender;
    -import com.yahoo.search.*;
    -import com.yahoo.search.result.*;
    -
    -public class ResultBlenderTestCase {
    -
    -    @Test
    -    public void testBlending() {
    -        Chain chain = new Chain(new ResultBlender(), new MockBackend());
    -        Context context = Execution.Context.createContextStub(null);
    -        Result result = new Execution(chain, context).search(new Query("?query=test"));
    -        assertEquals(4, result.hits().size());
    -        assertEquals("webService:1", result.hits().get(0).getId().toString());
    -        assertEquals("news:1", result.hits().get(1).getId().toString());
    -        assertEquals("webService:2", result.hits().get(2).getId().toString());
    -        assertEquals("webService:3", result.hits().get(3).getId().toString());
    -    }
    -
    -    private static class MockBackend extends Searcher {
    -
    -        @Override
    -        public Result search(Query query,Execution execution) {
    -            Result result = new Result(query);
    -            HitGroup webService = new HitGroup("source:webService");
    -            webService.add(new Hit("webService:1",0.9));
    -            webService.add(new Hit("webService:2",0.7));
    -            webService.add(new Hit("webService:3",0.5));
    -            result.hits().add(webService);
    -            HitGroup news = new HitGroup("source:news");
    -            news.add(new Hit("news:1",0.8));
    -            news.add(new Hit("news:2",0.6));
    -            news.add(new Hit("news:3",0.4));
    -            result.hits().add(news);
    -            return result;
    -        }
    -    }
    -
    -}
    -{% endhighlight %}
    -

    -This shows how a search chain can be created programmatically, -with a mock backend producing results suitable for exercising -the functionality of the searcher being tested. -

    diff --git a/en/querying/federation.mdx b/en/querying/federation.mdx new file mode 100644 index 0000000000..891f3331fa --- /dev/null +++ b/en/querying/federation.mdx @@ -0,0 +1,589 @@ +--- +title: "Federation" +--- + + +![](/assets/img/federation-simple.svg) + + +The Vespa Container allows multiple sources of data to be *federated* to a common search service. The sources of data may be both search clusters belonging to the same application, or external services, backed by Vespa or any other kind of service. The container may be used as a pure *federation platform* by setting up a system consisting solely of container nodes federating to external services. + +This document gives a short intro to federation, explains how to create an application package doing federation and shows what support is available for choosing the sources given a query, and the final result given the query and some source specific results. + +*Federation* allows users to access data from multiple sources of various kinds through one interface. This is useful to: + +- enrich the results returned from an application with auxiliary data, like finding appropriate images to accompany news articles. +- provide more comprehensive results by finding data from alternative sources in the cases where the application has none, like back-filling web results. +- create applications whose main purpose is not to provide access to some data set but to provide users or frontend applications a single starting point to access many kinds of data from various sources. Examples are browse pages created dynamically for any topic by pulling together data from external sources. + +The main tasks in creating a federation solution are: + + + +creating connectors to the various sources + + +selecting the data sources which will receive a given query + + +rewriting the received request to an executable query returning the desired data from each source + + +creating the final result by selecting from, organizing and combining the returned data from each selected source + + + +The container aids with these tasks by providing a way to organize a federated execution as a set of search chains which can be configured through the application package. Read the [Container intro](../applications/containers) and [Chained components](../applications/chaining) before proceeding. Refer to the `com.yahoo.search.federation` [Javadoc](https://javadoc.io/doc/com.yahoo.vespa/container-search/latest/com/yahoo/search/federation/package-summary). + +## Configuring Providers + +A *provider* is a search chain that produces data (in the form of a Result) from a data source. The provider must contain a Searcher which connects to the data source and produces a Result from the returned data. Configure a provider as follows: + +```xml + + + + + +``` + +You can add multiple searchers in the provider just like in other chains. + +Search chains that provide data from some content cluster in the same application are also *providers*. To explicitly configure a provider talking to internal content clusters, set the attribute type="local" on the provider. That will automatically add the searchers necessary to talk to internal content clusters to the search chain. Example: querying this provider will not lowercase / stem terms: + +```xml + +``` + +## Configuring Sources + +A single provider may be used to produce multiple kinds of results. To implement and present each kind of result, we can use *sources*. A *source* is a search chain that provides a specific kind of result by extending or modifying the behavior of one or more providers. + +Suppose that we want to retrieve two kinds of results from my-provider: Web results and java API documentation: + +```xml + + + + + + + + +``` + +This results in two *source search chains* being created, `web@my-provider` and `java-api@my-provider`. Each of them constitutes a source, namely `web` and `java-api` respectively. As the example suggests, these search chains are named after the source and the enclosing provider. The @-sign in the name should be read as *in*, so `web@my-provider` should for example be read as *web in my-provider*. + +The JavaApiSearcher is responsible for modifying the query so that we only get hits from the java API documentation. We added this searcher directly inside the source element; source search chains and providers are both instances of search chains. All the options for configuring regular search chains are therefore also available for them. + +How does the `web@my-provider` and `java-api@my-provider` source search chains use the `my-provider` provider to send queries to the external service? Internally, the source search chains *inherit* from the enclosing provider. Since the provider contains searchers that know how to talk to the external service, the sources will also contain the same searchers. As an example, consider the "web" search chain; It will contain exactly the same searcher instances as the `my-provider` search chain. By organizing chains for talking to data providers, we can reuse the same connections and logic for talking to remote services ("providers") for multiple purposes ("sources"). + +The provider search chain `my-provider` is *not modified* by adding sources. To verify this, try to send queries to the three search chains `my-provider`, `web@my-provider` and `java-api@my-provider`. + +### Multiple Providers per Source + +You can create a source that consists of source search chains from several providers. Effectively, this lets you vary which provider should be used to satisfy each request to the source: + +```xml + + + + + + + + + + +``` + +Here, the two source search chains `common-search@news-search` and `common-search@my-provider` constitutes a single source `common-search`. The source search chains using the `idref` attribute are called participants, while the ones using the `id` attribute are called leaders. Each source must consist of a single leader and zero or more participants. + +Per default, only the leader search chain is used when *federating* to a source. To use one of the participants instead, use [sources](../reference/api/query#model.sources) and *source*: + +```text +http://[host]:[port]/?sources=common-search&source.common-search.provider=news-search +``` + +## Federation + +Now we can search both the web and the java API documentation at the same time, and get a combined result set back. We achieve this by setting up a *federation* searcher: + +```xml + + + + + + + + + + + + + + +``` + +Inside the Federation element, we list the sources we want to use. Do not let the name *source* fool you; If it behaves like a source, then you can use it as a source (i.e. all types of search chains including providers are accepted). As an example, try replacing the *web* reference with *my-provider*. + +When searching, select a subset of the sources specified in the federation element by specifying the [sources](../reference/api/query#model.sources) query parameter. + +## Built-in Federation + +The built-in search chains *native* and *vespa* contain a federation searcher named *federation.* This searcher has been configured to federate to: + +- All sources +- All providers that does not contain a source + +If configuring your own federation searcher, you are not limited to a subset of these sources - you can use any provider, source or search chain. + +## Inheriting default Sources + +To get the same sources as the built-in federation searcher, inherit the default source set: + +```xml + + + + + ... + + + +``` + +## Changing content cluster chains + +With the information above, we can create a configuration where we modify the search chain sending queries to and receiving queries form a single content cluster (here, removing a searcher and adding another): + +```xml + + + + + + + + + + +``` + +## Timeout behavior + +What if we want to limit how much time a provider is allowed to use to answer a query? + +```xml + + + + + + + + + +``` + +The provider search chain will then be limited to use 100 ms to execute each query. The Federation layer allows all providers to continue until the non-optional provider with the longest timeout is finished or canceled. + +In some cases it is useful to be able to keep executing the request to a provider longer than we are willing to wait for it in that particular query. This allows us to populate caches inside sources which can only meet the timeout after caches are populated. To use this option, specify a [request timeout](../reference/applications/services/search#federationoptions) for the provider: + +```xml + + + + ... + +``` + +Also see [Searcher timeouts](../applications/searchers#timeouts). + +## Non-essential Providers + +Now let us add a provider that retrieves ads: + +```xml + + + + +``` + +Suppose that it is more important to return the result to the user as fast as possible, than to retrieve ads. To signal this, we mark the ads provider as *optional*: + +```xml + + + + + +``` + +The Federation searcher will then only wait for ads as long as it waits for mandatory providers. If the ads are available in time, they are used, otherwise they are dropped. + +If only optional providers are selected for Federation, they will all be treated as mandatory. Otherwise, they would not get a chance to return any results. + +## Federation options inheritance + +The sources automatically use the same Federation options as the enclosing provider. *override* one or more of the federation options in the sources: + +```xml + + + + + + + + + + + +``` + +You can use a single source in different Federation searchers. If you send queries with different cost to the same source from different federation searchers, you might also want to *override* the federation options for when they are used: + +```xml + + + + + + + + + + + + + + + + + + + +``` + +## Selecting Search Chains programmatically + +If we have complicated rules for when a search chain should be used, we can select search chains programmatically instead of setting up sources under federation in services.xml. The selection code is implemented as a [TargetSelector](https://javadoc.io/doc/com.yahoo.vespa/container-search/latest/com/yahoo/search/federation/selection/TargetSelector). This TargetSelector is used by registering it on a federation searcher. + +```java expandable +package com.yahoo.example; + +import com.google.common.base.Preconditions; +import com.yahoo.component.chain.Chain; +import com.yahoo.processing.execution.chain.ChainRegistry; +import com.yahoo.search.Query; +import com.yahoo.search.Result; +import com.yahoo.search.result.Hit; +import com.yahoo.search.Searcher; +import com.yahoo.search.federation.selection.FederationTarget; +import com.yahoo.search.federation.selection.TargetSelector; +import com.yahoo.search.searchchain.model.federation.FederationOptions; + +import java.util.Arrays; +import java.util.Collection; + +class MyTargetSelector implements TargetSelector { + + @Override + public Collection> getTargets(Query query, + ChainRegistry searcherChainRegistry) { + Chain searchChain = searcherChainRegistry.getComponent("my-chain"); + Preconditions.checkNotNull(searchChain, "No search chain named 'my-chain' exists in services.xml"); + + return Arrays.asList(new FederationTarget<>(searchChain, new FederationOptions(), null)); + } + + @Override + public void modifyTargetQuery(FederationTarget target, Query query) { + query.setHits(10); + } + + @Override + public void modifyTargetResult(FederationTarget target, Result result) { + for (Hit hit: result.hits()) { + hit.setField("my-field", "hello-world"); + } + } + +} +``` + +The target selector chooses search chains for the federation searcher. In this example, MyTargetSelector.getTargets returns a single chain named "my-chain" that has been set up in `services.xml`. + +Before executing each search chain, the federation searcher allows the target selector to modify the query by calling modifyTargetQuery. In the example, the number of hits to retrieve is set to 10. + +After the search chain has been executed, the federation searcher allows the target selector to modify the result by calling modifyTargetResult. In the example, each hit gets a field called "my-field" with the value "hello-world". + +Configure a federation searcher to use a target selector in ``services.xml``. Only a single target selector is supported. + +```js + + + + + + +``` + +We can also set up both a target-selector and normal sources. The federation searcher will then send queries both to programmatically selected sources and those that would normally be selected without the target selector: + +```js + + + + + + + ... + + +``` + +## Example: Setting up a Federated Service + +A federation application is created by providing custom searcher components performing the basic federation tasks and combining these into chains in a federation setup in [services.xml](/en/reference/applications/services/services). For example, this is a complete configuration which sets up a cluster of container nodes (having 1 node) which federates to the another Vespa service (news) and to some web service: + +```js expandable + + + + + + + + + + + + + + + + + + + + +``` + +This creates a configuration of search chains like: + + +![](/assets/img/federation.svg) + + +Each provider is a search chain ending in a Searcher forwarding the query to a remote service. In addition, there is a main chain (included by default) ending in a FederationSearcher, which by default forwards the query to all the providers in parallel. The provider chains returns their result upwards to the federation searcher which merges them into a complete result which is returned up the main chain. + +This services file, an implementation of the example classes (see below), and [hosts.xml](/en/reference/applications/hosts) listing the container nodes, is all that is needed to set up and [deploy](/en/basics/applications#deploying-applications) an application federating to multiple sources. For a reference to these XML sections, see the [chains reference](/en/reference/applications/services/search#chain). + +The following sections outlines how this can be elaborated into a solution producing more user-friendly federated results. + +### Selecting Sources + +To do the best possible job of bringing relevant data to the user, we should send every query to all sources, and decide what data to include when all the results are available, and we have as much information as possible at hand. In general this is not advisable because of the resource cost involved, so we must select a subset based on information in the query. This is best viewed as a probabilistic optimization problem: The selected sources should be the ones having a high enough probability of being useful to offset the cost of querying it. + +Any Searcher which is involved in selecting sources or processing the entire result should be added to the main search chain, which was created implicitly in the examples above. To do this, the main chain should be created explicitly: + +```js expandable + + + + + + + + + + + + + + + + + + + + + + +``` + +This adds an explicit main chain to the configuration which has two additional searchers in addition to those inherited from the native chain, which includes the FederationSearcher. Note that if the full Vespa functionality is needed, the `vespa` chain should be inherited rather than `native`. + +The chain called default will be invoked if no searchChain parameter is given in the query. + +To learn more about creating Searcher components, see [searcher development](/en/applications/searchers). + +### Rewriting Queries to Individual Providers + +The provider searchers are responsible for accepting a Query object, translating it to a suitable request to the backend in question and deserializing the response into a Result object. There is often a need to modify the query to match the particulars of a provider before passing it on: + +- To get results from the provider which matches the determined interpretation and intent as well as possible, the query may need to be rewritten using detailed information about the provider +- Parameters beyond the basic ones supported by each provider searcher may need to be translated to the provider +- There may be a need for provider specific business rules + +These query changes may range in complexity from setting a query parameter, applying some source specific information to the query or transferring all the relevant query state into a new object representation which is consumed by the provider searcher. + +This example shows a searcher adding a customer id to the news request: + +```java expandable +package com.yahoo.example; + +import com.yahoo.search.searchchain.Execution; +import com.yahoo.search.*; + +public class NewsCustomerIdSearcher extends Searcher { + + @Override + public Result search(Query query, Execution execution) { + String customerId="provider.news.custid"; + if (query.properties().get(customerId) == null) + query.properties().set(customerId, "yahoo/test"); + if (query.getTraceLevel() >= 3) + query.trace("News provider: Will use " + + customerId + "=" + query.properties().get(customerId), false, 3); + return execution.search(query); + } + +} +``` + +This searcher should be added to the news source chain as shown above. + +You may have noticed that we have referred to the search chains talking to a service as a **provider** while referring to selection of **sources**. The reason for making this distinction is that it is sometimes useful to treat different kinds of processing of queries and results to/from the same service as different sources. Hence, it is possible to create source search chains in addition to the provider chains in services.xml. Each such source will refer to a provider (by inheriting the provider chain) but include some searchers specific to that source. Selection and routing of the query from the federation searchers is always to sources, not providers. By default, if no source tags are added in the provider, each provider implicitly creates a source by the same name. + +## Processing Results + +When we have selected the sources, created queries fitting to get results from each source and executed those queries, we have produced a result which contains a HitGroup per source containing the list of hits from that source. These results may be returned in XML as is, preserving the structure as XML, by requesting the [page](/en/reference/querying/page-result-format) result format: + +```bash +http://[host]:[port]/search/?query=test&presentation.format=page +``` + +However, this is not suitable for presenting to the user in most cases. What we want to do is select the subset of the hits having the highest probable utility to the user, organized in a way that maximizes the user experience. This is not an easy task, and we will not attempt to solve it here, other than noting that any solution should make use of both the information in the intent model and the information within the results from each source, and that this is a highly connected optimization problem because the utility of including some data in the result depends on what other data is included. + +Here we will just use a searcher which shows how this is done in principle, this searcher flattens the news and web service hit groups into a single list of hits, where only the highest ranked news ones are included: + +```java expandable +package com.yahoo.example; + +import com.yahoo.search.*; +import com.yahoo.search.result.*; +import com.yahoo.search.searchchain.Execution; + +public class ResultBlender extends Searcher { + + @Override + public Result search(Query query,Execution execution) { + Result result = execution.search(query); + HitGroup news = (HitGroup)result.hits().remove("source:news"); + HitGroup webService = (HitGroup)result.hits().remove("source:webService"); + if (webService == null) return result; + result.hits().addAll(webService.asList()); + if (news == null) return result; + for (Hit hit : news.asList()) + if (shouldIncludeNewsHit(hit)) + result.hits().add(hit); + return result; + } + + private boolean shouldIncludeNewsHit(Hit hit) { + if (hit.isMeta()) return true; + if (hit.getRelevance().getScore() > 0.7) return true; + return false; + } + +} +``` + +The optimal result to return to the user is not necessarily one flattened list. In some cases it may be better to keep the source organization, or to pick some other organization. The [page result format](/en/reference/querying/page-result-format) requested in the query above is able to represent any hierarchical organization as XML. A more realistic version of this searcher will use that to choose between some predefined layouts which the frontend in question knows how to handle, and choose some way of grouping the available hits suitable for the selected layout. + +This searcher should be added to the main (`default`) search chain in services.xml together with the SourceSelector (the order does not matter). + +## Unit Testing the Result Processor + +Unit test example for the Searcher above: + +```java expandable +package com.yahoo.search.example.test; + +import org.junit.Test; +import com.yahoo.search.searchchain.*; +import com.yahoo.search.example.ResultBlender; +import com.yahoo.search.*; +import com.yahoo.search.result.*; + +public class ResultBlenderTestCase { + + @Test + public void testBlending() { + Chain chain = new Chain(new ResultBlender(), new MockBackend()); + Context context = Execution.Context.createContextStub(null); + Result result = new Execution(chain, context).search(new Query("?query=test")); + assertEquals(4, result.hits().size()); + assertEquals("webService:1", result.hits().get(0).getId().toString()); + assertEquals("news:1", result.hits().get(1).getId().toString()); + assertEquals("webService:2", result.hits().get(2).getId().toString()); + assertEquals("webService:3", result.hits().get(3).getId().toString()); + } + + private static class MockBackend extends Searcher { + + @Override + public Result search(Query query,Execution execution) { + Result result = new Result(query); + HitGroup webService = new HitGroup("source:webService"); + webService.add(new Hit("webService:1",0.9)); + webService.add(new Hit("webService:2",0.7)); + webService.add(new Hit("webService:3",0.5)); + result.hits().add(webService); + HitGroup news = new HitGroup("source:news"); + news.add(new Hit("news:1",0.8)); + news.add(new Hit("news:2",0.6)); + news.add(new Hit("news:3",0.4)); + result.hits().add(news); + return result; + } + } + +} +``` + +This shows how a search chain can be created programmatically, with a mock backend producing results suitable for exercising the functionality of the searcher being tested. diff --git a/en/querying/geo-search.html b/en/querying/geo-search.html deleted file mode 100644 index 9a88919852..0000000000 --- a/en/querying/geo-search.html +++ /dev/null @@ -1,390 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Geo Search" -redirect_from: -- /en/geo-search.html ---- - -

    - To model a geographical position in documents, use a field where the type is - position for - a single, required position. To allow any number of positions - (including none at all) use array<position> instead. - This can be used to limit hits (only those documents with a position - inside a circular area will be hits), the distance from a point can - be used as input to ranking functions, or both. -

    -

    - A geographical point in Vespa is specified using the geographical - latitude - and - longitude. - As an example, a location in - - Sunnyvale, California - could be latitude 37.4181488 degrees North, longitude 122.0256157 degrees West. - This would be represented as - { "lat": 37.4181488, "lng": -122.0256157 } - in JSON. -

    -

    - As seen above, positive numbers are used for north (latitudes) and - east (longitudes); negative numbers are used for south and west. - This is the usual convention. -

    -{% include note.html content="Old formats for position (those used in Vespa 5, 6, and 7) -are still accepted as feed input; enabling legacy output is -temporarily possible also. See -legacy flag v7-geo-positions." %} - - -

    Sample schema and document

    -

    - A sample schema could be a business directory, where every - business has a position (for its main office or contact point): -

    -
    -schema biz {
    -    document biz {
    -        field title type string {
    -            indexing: index
    -        }
    -        field mainloc type position {
    -            indexing: attribute | summary
    -        }
    -    }
    -    fieldset default {
    -        fields: title
    -    }
    -}
    -
    -

    - Using this schema is one possible business entry with its location: -

    -
    {% highlight json %}
    -{
    -    "put": "id:mynamespace:biz::business-1",
    -    "fields": {
    -        "title":      "Yahoo Inc (main office)",
    -        "mainloc":    { "lat": 37.4181488, "lng": -122.0256157 }
    -    }
    -}
    -{% endhighlight %}
    - - - -

    Restrict

    -

    - The API for adding a geographical restriction is to use a - geoLocation clause in the YQL statement, - specifying a point and a maximum distance from that point: -

    -$ curl -H "Content-Type: application/json" \
    -  --data '{"yql" : "select * from sources * where title contains \"office\" and geoLocation(mainloc, 37.416383, -122.024683, \"20 miles\")"}' \
    -  http://localhost:8080/search/
    -
    -

    - One can also build or modify the query programmatically by adding a - - GeoLocationItem anywhere in the query tree. -

    -

    - To use a position for ranking only (without any requirement for a matching position), - specify it as a ranking-only term. - Use the rank() operation in YQL for this, or a - RankItem - when building the query programmatically. -

    -

    - At the same time, specify a negative radius (for example -1 m). - This matches any position, and computes distance etc. for the closest position in the document. Example: -

    -
    -$ curl -H "Content-Type: application/json" \
    -  --data '{"yql" : "select * from sources * where rank(title contains \"office\", geoLocation(mainloc, 37.416383, -122.024683, \"-1 m\"))"}' \
    -  http://localhost:8080/search/
    -
    - - - -

    Ranking from a position match

    -

    - The main rank feature to use for the example above would be - distance(mainloc).km - and doing further calculation on it, giving better rank to documents - that are closer to the wanted (query) position. - Here one needs to take into consideration what sort of distances is practical; - traveling on foot, by car, or by plane should have quite different - ranking scales - using different rank profiles would be one natural - way to support that. If the query specifies a maximum distance, that - could be sent as an input to ranking as well, and used for scaling. -

    -

    - There is also a closeness(mainloc) - which goes from 1.0 at the exact location to 0.0 at a tunable maximum distance, - which is enough for many needs. -

    - - -

    Useful summary-features

    -

    - To do further processing, it may be useful to get the computed distance back. - The preferred way to do this is to use the associated rank features as - summary-features. - In particular, - distance(fieldname).km - gives the geographical distance in kilometers, while - distance(fieldname).latitude and - distance(fieldname).longitude - gives the geographical coordinates for the best location directly, in degrees. - These are easy to use programmatically from a searcher, accessing - feature values in results - for further processing. -

    - -{% include note.html content="geoLocation doesn't do proper great-circle-distance calculations. -It works well for 'local' search (city or metro area), using simpler distance calculations. -For positions which are very distant or close to the international date line (e.g. the Bering sea), -the computed results may be inaccurate." %} - -

    Using multiple position fields

    -

    - For some applications, it can be useful to have several position attributes that may be searched. - For example, we could expand the above examples with the locations of subsidiary offices: -

    -schema biz {
    -    document biz {
    -        field title type string {
    -            indexing: index
    -        }
    -        field mainloc type position {
    -            indexing: attribute | summary
    -        }
    -        field otherlocs type array<position> {
    -            indexing: attribute
    -        }
    -    }
    -    fieldset default {
    -        fields: title
    -    }
    -}
    -
    -

    Expanding the example business with an office in Australia and one in Norway could look like:

    -
    {% highlight json %}
    -{
    -    "put": "id:mynamespace:biz::business-1",
    -    "fields": {
    -        "title":       "Yahoo Inc (some offices)",
    -        "mainloc":     { "lat": 37.4, "lng": -122.0 },
    -        "otherlocs": [ { "lat": -33.9, "lng": 151.2 },
    -                       { "lat": 63.4, "lng": 10.4 } ]
    -    }
    -}
    -{% endhighlight %}
    -

    - A single query item can only search in one of the position attributes. - For a search that spans several fields, - use YQL to combine several geoLocation items inside an or clause, - or combine several fields into a combined array field - (so in the above example, one could duplicate the "mainloc" position into the "otherlocs" array as well, - possibly changing the name from "otherlocs" to "all_locs"). -

    - - -

    Example with airport positions

    -To give some more example positions, here is a list of some airports with their locations in JSON format: - - - - - - - - - - - - - - - - - - - - - - - -
    Airport codeCityLocation
    SFOSan Francisco, USA { "lat": 37.618806, "lng": -122.375416 }
    LAXLos Angeles, USA { "lat": 33.942496, "lng": -118.408048 }
    JFKNew York, USA { "lat": 40.639928, "lng": -73.778692 }
    LHRLondon, UK { "lat": 51.477500, "lng": -0.461388 }
    SYDSydney, Australia { "lat": -33.946110, "lng": 151.177222 }
    TRDTrondheim, Norway { "lat": 63.457556, "lng": 10.924250 }
    OSLOslo, Norway { "lat": 60.193917, "lng": 11.100361 }
    GRUSão Paulo, Brazil { "lat": -23.435555, "lng": -46.473055 }
    GIGRio de Janeiro, Brazil { "lat": -22.809999, "lng": -43.250555 }
    BLRBangalore, India { "lat": 13.198867, "lng": 77.705472 }
    FCORome, Italy { "lat": 41.804475, "lng": 12.250797 }
    NRTTokyo, Japan { "lat": 35.765278, "lng": 140.385556 }
    PEKBeijing, China { "lat": 40.073, "lng": 116.598 }
    CPTCape Town, South Africa { "lat": -33.971368, "lng": 18.604292 }
    ACCAccra, Ghana { "lat": 5.605186, "lng": -0.166785 }
    TBUNuku'alofa, Tonga { "lat": -21.237999, "lng": -175.137166 }
    - - - -

    Distance to path

    -

    - This example provides an overview of the - DistanceToPath - rank feature. This feature matches document locations to a path given in the query. - Not only does this feature return the closest distance for each document to the path, - it also includes the length traveled along the path before reaching the closest point, - or intersection. - This feature has been nick named the gas feature - because of its obvious use case of finding gas stations along a planned trip. -

    -

    - In this example we have been traveling from the US to Bangalore, and we - are now planning our trip back. We have decided to rent a car in - Bangalore that we are to return upon arrival at the airport in - Chennai. We are already quite hungry and wish to stop for a meal once - we are outside of town. To avoid having to pay an additional fueling - premium, we also wish to refuel just before reaching the airport. We - need to figure out what roads to take, what restaurants are available - outside of Bangalore, and what fuel stations are available once we get close to Chennai. - Here we have plotted our trip from Bangalore to the airport: -

    -Trip from Bangalore to the airport -

    - If we search for restaurants along the path, - we only see a small subset of all restaurants present in the window of our quite large map. - Here you see how the most relevant results are actually all in Bangalore or Chennai: -

    -Most relevant results -

    - To find the best results, move the map - window to just about where we expect to be eating, and redo the search: -

    -redo search with adjusted map -

    - This has to be done similarly for finding a gas station near the airport. - This illustrates searching for restaurants in a smaller window along the - planned trip without DistanceToPath. - Next, we outline how DistanceToPath - can be used to quickly and easily improve this type of - planning to be more convenient for the user. -

    -

    - The nature of this feature requires that the search corpus contains documents with position data. - A searcher component needs to be - written that is able to pass paths with the queries that lie in the - same coordinate space as the searchable documents. - Finally, a rank-profile needs to defined that scores - documents according to how they match some target distance traveled - and at the same time lies close "enough" to the path. -

    - - -

    Query Syntax

    -

    - This document does not describe how to write a searcher plugin for the Container, - refer to the container documentation. - However, let us review the syntax expected by DistanceToPath. - As noted in the - rank features reference, - the path is supplied as a query parameter by name of the feature and the path keyword: -

    -
    -yql=(…)&rankproperty.distanceToPath(name).path=(x1,y1,x2,y2,…,xN,yN)
    -
    -

    - Here name has to match the name of the position attribute that holds the positions data. -

    -

    - The path itself is parsed as a list of N coordinate pairs - that together form N-1 line segments: -

    -

    - $$(x_1,y_1) \rightarrow (x_2,y_2), (x_2,y_2) \rightarrow (x_3,y_3), (…), (x_{N-1},y_{N-1}) \rightarrow (x_N,y_N)$$ -

    - {% include note.html content="The path is not in a readable (latitude, longitude) format, - but is a pair of integers in the internal format (degrees multiplied by 1 million). - If a transform is required from geographic coordinates to this, the search plugin must do it; - note that the first number in each pair (the 'x') is longitude (degrees East or West) - while the second ('y') is latitude (degrees North or South), corresponding to the usual orientation for maps - - opposite to the usual order of latitude/longitude."%} - - -

    Rank profile

    -

    - If we were to disregard our scenario for a few moments, we could suggest the following rank profile: -

    -
    -rank-profile default {
    -    first-phase {
    -        expression: nativeRank
    -    }
    -    second-phase {
    -        expression: firstPhase * if (distanceToPath(ll).distance < 10000, 1, 0)
    -    }
    -}
    -
    -

    - This profile will first rank all documents according to Vespa's - nativeRank feature, and then do a second pass over the top 100 - results and order these based on their distance to our path. - If a document lies within 100 metres of our path it - retains its relevancy, otherwise its relevancy is set to 0. Such a - rank profile would indeed solve the current problem, - but Vespa's ranking model allows for us to take this a lot further. -

    -

    - The following is a rank profile that ranks documents - according to a query-specified target distance to path and distance traveled: -

    -
    -rank-profile default {
    -    first-phase {
    -        expression {
    -            max(0,    query(distance) - distanceToPath(ll).distance) *
    -            (1 - fabs(query(traveled) - distanceToPath(ll).traveled))
    -        }
    -    }
    -}
    -
    -

    - The expression is two-fold; a first component determines a rank based - on the document's distance to the given path as compared to - the query parameter - distance. If the allowed distance is exceeded, this - component's contribution is 0. The distance contribution is then - multiplied by the difference of the actual distance traveled as - compared to the query parameter traveled. In short, this - profile will include all documents that lie close enough to the path, - ranked according to their actual distance and traveled measure. -

    -{% include note.html content="DistanceToPath is only compatible -with 2D coordinates because pathing in 1 dimension makes no sense."%} - - -

    Results

    -

    - For the sake of this example, assume that we have implemented a - custom path searcher that is able to pass the path found by the user's - initial directions query to Vespa's query syntax. - There are then two more parameters that must be supplied - by the user; distance and traveled. Vespa - expects these parameters to be supplied in a scale compatible with the - feature's output, and should probably also be mapped by the container - plugin. The feature's distance output is given in - Vespa's internal resolution, which is approximately 10 units per - meter. The traveled output is a normalized number - between 0 and 1, where 0 represents the beginning of the path, and 1 - is the end of the path. -

    -

    - This illustrates how these parameters can be used to return - the most appropriate hits for our scenario. Note that the figures only - show the top hit for each query: -

    -Top tip 1 -Top tip 2 -
      -
    1. Searching for restaurants with the DistanceToPath feature. - distance = 1000, traveled = 0.1
    2. -
    3. Searching for gas stations with the DistanceToPath feature. - distance = 1000, traveled = 0.9
    4. -
    diff --git a/en/querying/geo-search.mdx b/en/querying/geo-search.mdx new file mode 100644 index 0000000000..8b451fdaec --- /dev/null +++ b/en/querying/geo-search.mdx @@ -0,0 +1,319 @@ +--- +title: "Geo Search" +--- + +To model a geographical position in documents, use a field where the type is [position](../reference/schemas/schemas#position) for a single, required position. To allow any number of positions (including none at all) use `array` instead. This can be used to limit hits (only those documents with a position inside a circular area will be hits), the distance from a point can be used as input to ranking functions, or both. + +A geographical point in Vespa is specified using the geographical [latitude](https://en.wikipedia.org/wiki/Latitude) and [longitude](https://en.wikipedia.org/wiki/Longitude). As an example, a location in [Sunnyvale, California](https://www.google.com/maps/place/721+1st+Ave,+Sunnyvale,+CA+94089/@37.4181488,-122.0256157,12z) could be latitude 37.4181488 degrees North, longitude 122.0256157 degrees West. This would be represented as `{ "lat": 37.4181488, "lng": -122.0256157 }` in JSON. + +As seen above, positive numbers are used for north (latitudes) and east (longitudes); negative numbers are used for south and west. This is the usual convention. + + +**Note:** + +Old formats for position (those used in Vespa 5, 6, and 7) are still accepted as feed input; enabling legacy output is temporarily possible also. See [legacy flag v7-geo-positions](../reference/querying/default-result-format#geo-position-rendering). + + +## Sample schema and document + +A sample schema could be a business directory, where every business has a position (for its main office or contact point): + +```js +schema biz { + document biz { + field title type string { + indexing: index + } + field mainloc type position { + indexing: attribute | summary + } + } + fieldset default { + fields: title + } +} +``` + +Using this schema is one possible business entry with its location: + +```json +{ + "put": "id:mynamespace:biz::business-1", + "fields": { + "title": "Yahoo Inc (main office)", + "mainloc": { "lat": 37.4181488, "lng": -122.0256157 } + } +} +``` + +## Restrict + +The API for adding a geographical restriction is to use a [geoLocation](../reference/querying/yql#geolocation) clause in the YQL statement, specifying a point and a maximum distance from that point: + +```bash +$ curl -H "Content-Type: application/json" \ + --data '{"yql" : "select * from sources * where title contains \"office\" and geoLocation(mainloc, 37.416383, -122.024683, \"20 miles\")"}' \ + http://localhost:8080/search/ +``` + +One can also build or modify the query programmatically by adding a [GeoLocationItem](https://javadoc.io/doc/com.yahoo.vespa/container-search/latest/com/yahoo/prelude/query/GeoLocationItem) anywhere in the query tree. + +To use a position for ranking only (without *any* requirement for a matching position), specify it as a ranking-only term. Use the [rank()](../reference/querying/yql#rank) operation in YQL for this, or a [RankItem](https://javadoc.io/doc/com.yahoo.vespa/container-search/latest/com/yahoo/prelude/query/RankItem) when building the query programmatically. + +At the *same time*, specify a negative radius (for example `-1 m`). This matches any position, and computes distance etc. for the closest position in the document. Example: + +```bash +$ curl -H "Content-Type: application/json" \ + --data '{"yql" : "select * from sources * where title contains \"office\" and geoLocation(mainloc, 37.416383, -122.024683, \"20 miles\")"}' \ + http://localhost:8080/search/ +``` + +## Ranking from a position match + +The main rank feature to use for the example above would be [distance(mainloc).km](../reference/ranking/rank-features#distance\(name\).km) and doing further calculation on it, giving better rank to documents that are closer to the wanted (query) position. Here one needs to take into consideration what sort of distances is practical; traveling on foot, by car, or by plane should have quite different ranking scales - using different rank profiles would be one natural way to support that. If the query specifies a maximum distance, that could be sent as an input to ranking as well, and used for scaling. + +There is also a [closeness(mainloc)](../reference/ranking/rank-features#closeness\(name\)) which goes from 1.0 at the exact location to 0.0 at a tunable maximum distance, which is enough for many needs. + +### Useful summary-features + +To do further processing, it may be useful to get the computed distance back. The preferred way to do this is to use the associated rank features as [summary-features](../reference/schemas/schemas#summary-features). In particular, [distance(*fieldname*).km](../reference/ranking/rank-features#distance\(name\).km) gives the geographical distance in kilometers, while [distance(*fieldname*).latitude](../reference/ranking/rank-features#distance\(name\).latitude) and [distance(*fieldname*).longitude](../reference/ranking/rank-features#distance\(name\).longitude) gives the geographical coordinates for the best location directly, in degrees. These are easy to use programmatically from a searcher, accessing [feature values in results](../ranking/ranking-expressions-features#accessing-feature-function-values-in-results) for further processing. + + +**Note:** + +`geoLocation` doesn't do proper great-circle-distance calculations. It works well for 'local' search (city or metro area), using simpler distance calculations. For positions which are very distant or close to the international date line (e.g. the Bering sea), the computed results may be inaccurate. + + +## Using multiple position fields + +For some applications, it can be useful to have several position attributes that may be searched. For example, we could expand the above examples with the locations of subsidiary offices: + +```js +schema biz { + document biz { + field title type string { + indexing: index + } + field mainloc type position { + indexing: attribute | summary + } + field otherlocs type array { + indexing: attribute + } + } + fieldset default { + fields: title + } +} +``` + +Expanding the example business with an office in Australia and one in Norway could look like: + +```json +{ + "put": "id:mynamespace:biz::business-1", + "fields": { + "title": "Yahoo Inc (some offices)", + "mainloc": { "lat": 37.4, "lng": -122.0 }, + "otherlocs": [ { "lat": -33.9, "lng": 151.2 }, + { "lat": 63.4, "lng": 10.4 } ] + } +} +``` + +A single query item can only search in one of the position attributes. For a search that spans several fields, use YQL to combine several `geoLocation` items inside an `or` clause, or combine several fields into a combined array field (so in the above example, one could duplicate the "mainloc" position into the "otherlocs" array as well, possibly changing the name from "otherlocs" to "all_locs"). + +## Example with airport positions + +To give some more example positions, here is a list of some airports with their locations in JSON format: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Airport codeCityLocation
    SFOSan Francisco, USA{`{ "lat": 37.618806, "lng": -122.375416 }`}
    LAXLos Angeles, USA{`{ "lat": 33.942496, "lng": -118.408048 }`}
    JFKNew York, USA{`{ "lat": 40.639928, "lng": -73.778692 }`}
    LHRLondon, UK{`{ "lat": 51.477500, "lng": -0.461388 }`}
    SYDSydney, Australia{`{ "lat": -33.946110, "lng": 151.177222 }`}
    TRDTrondheim, Norway{`{ "lat": 63.457556, "lng": 10.924250 }`}
    OSLOslo, Norway{`{ "lat": 60.193917, "lng": 11.100361 }`}
    GRUSão Paulo, Brazil{`{ "lat": -23.435555, "lng": -46.473055 }`}
    GIGRio de Janeiro, Brazil{`{ "lat": -22.809999, "lng": -43.250555 }`}
    BLRBangalore, India{`{ "lat": 13.198867, "lng": 77.705472 }`}
    FCORome, Italy{`{ "lat": 41.804475, "lng": 12.250797 }`}
    NRTTokyo, Japan{`{ "lat": 35.765278, "lng": 140.385556 }`}
    PEKBeijing, China{`{ "lat": 40.073, "lng": 116.598 }`}
    CPTCape Town, South Africa{`{ "lat": -33.971368, "lng": 18.604292 }`}
    ACCAccra, Ghana{`{ "lat": 5.605186, "lng": -0.166785 }`}
    TBUNuku'alofa, Tonga{`{ "lat": -21.237999, "lng": -175.137166 }`}
    + +## Distance to path + +This example provides an overview of the [DistanceToPath](../reference/ranking/rank-features#distanceToPath\(name\).distance) rank feature. This feature matches *document locations* to a path given in the query. Not only does this feature return the closest distance for each document to the path, it also includes the length traveled *along* the path before reaching the closest point, or *intersection*. This feature has been nick named the *gas* feature because of its obvious use case of finding gas stations along a planned trip. + +In this example we have been traveling from the US to Bangalore, and we are now planning our trip back. We have decided to rent a car in Bangalore that we are to return upon arrival at the airport in Chennai. We are already quite hungry and wish to stop for a meal once we are outside of town. To avoid having to pay an additional fueling premium, we also wish to refuel just before reaching the airport. We need to figure out what roads to take, what restaurants are available outside of Bangalore, and what fuel stations are available once we get close to Chennai. Here we have plotted our trip from Bangalore to the airport: + + +![](/assets/img/geo/path1.png) + +If we search for restaurants along the path, we only see a small subset of all restaurants present in the window of our quite large map. Here you see how the most relevant results are actually all in Bangalore or Chennai: + +![](/assets/img/geo/path2.png) + +To find the best results, move the map window to just about where we expect to be eating, and redo the search: + +![](/assets/img/geo/path3.png) + + +This has to be done similarly for finding a gas station near the airport. This illustrates searching for restaurants in a smaller window along the planned trip without *DistanceToPath*. Next, we outline how *DistanceToPath* can be used to quickly and easily improve this type of planning to be more convenient for the user. + +The nature of this feature requires that the search corpus contains documents with position data. A [searcher component](../applications/searchers) needs to be written that is able to pass paths with the queries that lie in the same coordinate space as the searchable documents. Finally, a [rank-profile](../basics/ranking) needs to defined that scores documents according to how they match some target distance traveled and at the same time lies close "enough" to the path. + +### Query Syntax + +This document does not describe how to write a searcher plugin for the Container, refer to the [container documentation](../applications/searchers). However, let us review the syntax expected by *DistanceToPath*. As noted in the [rank features reference](../reference/ranking/rank-features#distanceToPath\(name\).distance), the path is supplied as a query parameter by name of the feature and the `path` keyword: + +```js +yql=(…)&rankproperty.distanceToPath(name).path=(x1,y1,x2,y2,…,xN,yN) +``` + +Here `name` has to match the name of the position attribute that holds the positions data. + +The path itself is parsed as a list of `N` coordinate pairs that together form `N-1` line segments: + +$$ +(x_1,y_1) \rightarrow (x_2,y_2),\ (x_2,y_2) \rightarrow (x_3,y_3),\ (\ldots),\ (x_{N-1},y_{N-1}) \rightarrow (x_N,y_N) +$$ + + +**Note:** + +The path is *not* in a readable (latitude, longitude) format, but is a pair of integers in the internal format (degrees multiplied by 1 million). If a transform is required from geographic coordinates to this, the search plugin must do it; note that the first number in each pair (the 'x') is longitude (degrees East or West) while the second ('y') is latitude (degrees North or South), corresponding to the usual orientation for maps - *opposite* to the usual order of latitude/longitude. + + +### Rank profile + +If we were to disregard our scenario for a few moments, we could suggest the following rank profile: + +```js +rank-profile default { + first-phase { + expression: nativeRank + } + second-phase { + expression: firstPhase * if (distanceToPath(ll).distance < 10000, 1, 0) + } +} +``` + +This profile will first rank all documents according to Vespa's *nativeRank* feature, and then do a second pass over the top 100 results and order these based on their distance to our path. If a document lies within 100 metres of our path it retains its relevancy, otherwise its relevancy is set to 0. Such a rank profile would indeed solve the current problem, but Vespa's ranking model allows for us to take this a lot further. + +The following is a rank profile that ranks documents according to a query-specified target distance to path and distance traveled: + +```js +rank-profile default { + first-phase { + expression { + max(0, query(distance) - distanceToPath(ll).distance) * + (1 - fabs(query(traveled) - distanceToPath(ll).traveled)) + } + } +} +``` + +The expression is two-fold; a first component determines a rank based on the document's distance to the given path as compared to the [query parameter](../reference/ranking/ranking-expressions) `distance`. If the allowed distance is exceeded, this component's contribution is 0. The distance contribution is then multiplied by the difference of the actual distance traveled as compared to the query parameter `traveled`. In short, this profile will include all documents that lie close enough to the path, ranked according to their actual distance and traveled measure. + + +**Note:** + +*DistanceToPath* is only compatible with *2D coordinates* because pathing in 1 dimension makes no sense. + + +### Results + +For the sake of this example, assume that we have implemented a custom path searcher that is able to pass the path found by the user's initial directions query to Vespa's [query syntax](#query-syntax). There are then two more parameters that must be supplied by the user; `distance` and `traveled`. Vespa expects these parameters to be supplied in a scale compatible with the feature's output, and should probably also be mapped by the container plugin. The feature's *distance* output is given in Vespa's internal resolution, which is approximately 10 units per meter. The *traveled* output is a normalized number between 0 and 1, where 0 represents the beginning of the path, and 1 is the end of the path. + +This illustrates how these parameters can be used to return the most appropriate hits for our scenario. Note that the figures only show the top hit for each query: + + +![](/assets/img/geo/path4.png) + + +![](/assets/img/geo/path5.png) + + +1. Searching for restaurants with the DistanceToPath feature. `distance = 1000, traveled = 0.1` +2. Searching for gas stations with the DistanceToPath feature. `distance = 1000, traveled = 0.9` + diff --git a/en/querying/grouping.html b/en/querying/grouping.html deleted file mode 100644 index 70e4ac3398..0000000000 --- a/en/querying/grouping.html +++ /dev/null @@ -1,2227 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Grouping and aggregation" -redirect_from: -- /en/grouping.html ---- - - - -
    -

    Grouping Interface

    -

    - Try running requests on the - grouping example data: -

    - -
    - - - -
    -
    -
    -
    -
    - - -

    - The Vespa grouping language is a list-processing language - which describes how the query hits should be grouped, aggregated, and presented in result sets. - A grouping statement takes the list of all matches to a query as input and groups/aggregates - it, possibly in multiple nested and parallel ways to produce the output. - This is a logical specification and does not indicate how it is executed, - as instantiating the list of all matches to the query somewhere would be too expensive, - and execution is distributed instead. -

    -

    - Refer to the Query API reference - for how to set the select parameter, - and the Grouping reference for details. - Fields used in grouping must be defined as attribute in the document schema. - Grouping supports continuation objects for pagination. -

    -

    - The Grouping Results - sample application is a practical example. -

    - - - -

    The grouping language structure

    -

    - The operations defining the structure of a grouping are: -

    -
      -
    • all(statement): Execute the nested statement once on the input list as a whole.
    • -
    • each(statement): Execute the nested statement on each element of the input list.
    • -
    • group(specification): - Turn the input list into a list of lists according to the grouping specification.
    • -
    • output: Output some value(s) at the current location in the structure.
    • -
    -

    - The parallel and nested collection of these operations defines both the structure of the computation - and of the result it produces. - For example, all(group(customer) each(output(count()))) - will take all matches, group them by customer id, and for each group, output the count of hits in the group. -

    -

    - Vespa distributes and executes the grouping program on content nodes and merges results on container nodes - - in multiple phases, as needed. - As realizing such programs over a distributed data set requires more network round-trips than a regular search query, - these queries may be more expensive than regular queries - - see defaultMaxGroups and the likes - for how to control resource usage. -

    - - - -

    Grouping by example

    -

    -For the entirety of this document, assume an index of engine part purchases: -

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    DatePriceTaxItemCustomerIs paid
    2006-09-06 09:00:00$1 0000.24Intake valveSmithtrue
    2006-09-07 10:00:00$1 0000.12Rocker armSmithfalse
    2006-09-07 11:00:00$2 0000.24SpringSmithtrue
    2006-09-08 12:00:00$3 0000.12Valve coverJonesfalse
    2006-09-08 10:00:00$5 0000.24Intake portJonestrue
    2006-09-08 11:00:00$8 0000.12HeadBrownfalse
    2006-09-09 12:00:00$1 3000.24CoolantSmithtrue
    2006-09-09 10:00:00$2 1000.12Engine blockJonesfalse
    2006-09-09 11:00:00$3 4000.24Oil panBrowntrue
    2006-09-09 12:00:00$5 5000.12Oil sumpSmithfalse
    2006-09-10 10:00:00$8 9000.24CamshaftJonestrue
    2006-09-10 11:00:00$1 4400.12Exhaust valveBrownfalse
    2006-09-10 12:00:00$2 3300.24Rocker armBrowntrue
    2006-09-10 10:00:00$3 7700.12SpringBrownfalse
    2006-09-10 11:00:00$6 1000.24Spark plugSmithtrue
    2006-09-11 12:00:00$9 8700.12Exhaust portJonesfalse
    2006-09-11 10:00:00$1 5970.24PistonBrowntrue
    2006-09-11 11:00:00$2 5840.12Connection rodSmithfalse
    2006-09-11 12:00:00$4 1810.24Rod bearingJonestrue
    2006-09-11 13:00:00$6 7650.12CrankshaftJonesfalse
    - - - -

    Basic Grouping

    -

    -Example: Return the total sum of purchases per customer - steps: -

    -
      -
    1. - Select all documents: -
      /search/?yql=select * from sources * where true
      -
    2. -
    3. - Take the list of all hits: -
      all(...)
      -
    4. -
    5. - Turn it into a list of lists of all hits having the same customer id: -
      group(customer)
      -
    6. -
    7. - For each of those lists of same-customer hits: - each(...) -
    8. -
    9. - Output the sum (an aggregator) of the price over all items in that list of hits: -
      output(sum(price))
      -
    10. -
    -

    - Final query, producing the sum of the price of all purchases for each customer: -

    - -
    -/search/?yql=select * from sources * where true limit 0 |
    -  all( group(customer) each(output(sum(price))) )
    -
    -

    - Here, limit is set to zero to get the grouping output only. - URL encoded equivalent: -

    -
    -/search/?yql=select%20%2A%20from%20sources%20%2A%20where%20true%20limit%200%20%7C%20
    -  all%28%20group%28customer%29%20each%28output%28sum%28price%29%29%29%20%29
    -
    -

    Result:

    - - - - - - - - - - - - - - - - - - - - - -
    GroupIdSum(price)
    Brown$20 537
    Jones$39 816
    Smith$19 484
    -

    -Example: Sum price of purchases per date: -

    -
    -select (…) | all(group(time.date(date)) each(output(sum(price))))
    -
    -

    -Note: in examples above, all documents are evaluated. -Modify the query to add filters (and thus cut latency), like (remember to URL encode): -

    -
    /search/?yql=select * from sources * where customer contains "smith"
    - - - - -

    Ordering and Limiting Groups

    -

    -In many scenarios, a large collection of groups is produced, possibly too large to display or process. -This is handled by ordering groups, then limiting the number of groups to return. -

    -The order clause accepts a list of one or more expressions. -Each of the arguments to order is prefixed by either a plus/minus for ascending/descending order. -

    -Limit the number of groups using max and precision - -the latter is the number of groups returned per content node to be merged to the global result. -Larger document distribution skews hence require a higher precision for accurate results. -

    -

    - An implicit limit can be specified through the - grouping.defaultMaxGroups query parameter. - This value will always be overridden if max is explicitly specified in the query. - Use max(inf) to retrieve all groups when the query parameter is set. -

    -

    - If precision is not specified, it will default to a factor times max. - This factor can be overridden through the - grouping.defaultPrecisionFactor - query parameter. -

    -

    - Example: To find the 2 globally best groups, make an educated guess on how - many samples are needed to fetch from each node in order to get the right groups. - This is the precision. - An initial factor of 3 has proven to be quite good in most use cases. - If however, the data for customer 'Jones' was spread on 3 different content nodes, - 'Jones' might be among the 2 best on only one node. - But based on the distribution of the data, - we have concluded by earlier tests that if we fetch 5.67 as many groups as we need to, - we will have a correct answer with at least 99.999% confidence. - So then we just use 6 times as many groups when doing the merge. -

    -

    -However, there is one exception. -Without an order constraint, precision is not required. -Then, local ordering will be the same as global ordering. -Ordering will not change after a merge operation. -

    -

    Example

    -

    - Example: The two customers with most purchases, returning the sum for each: -

    -
    -select (…) | all(group(customer) max(2) precision(12) order(-count())
    -    each(output(sum(price))))
    -
    - - - -

    Hits per Group

    -

    -Use summary to print the fields for a hit, -and max to limit the number of hits per group. -

    -

    - An implicit limit can be specified through the - grouping.defaultMaxHits query parameter. - This value will always be overridden if max is explicitly specified in the query. - Use max(inf) to retrieve all hits when the query parameter is set. -

    - -

    Example

    -

    - Example: Return the three most expensive parts per customer: -

    -
    -/search/?yql=select * from sources * where true |
    -             all(group(customer) each(max(3) each(output(summary()))))
    -
    - -

    Notes on ordering in the example above:

    -
      -
    • - The order clause is a directive for group ordering, not hit ordering. - Here, there is no order clause on the groups, so default ordering -max(relevance()) is used. The - - denotes the sorting order, - means descending (higher score first). - In this case, the query is "all documents", so all groups are equally relevant and the group order is random. -
    • - To order hits inside groups, use ranking. Add ranking=pricerank to the query - to use the pricerank rank profile to rank by price: -
      -rank-profile pricerank inherits default {
      -    first-phase {
      -        expression: attribute(price)
      -    }
      -}
      -
    • -
    - -

    Filter within a group

    -

    - Use the filter clause to select which values to keep in a group. See the reference for details. -

    -

    Examples

    -

    - Example: Sum the price per customer of Bonn.* where price was over 1000. -

    -
    -/search/?yql=select * from sources * where true |
    -             all(group(customer) filter(regex("Bonn.*", attributes{"sales_rep"}) and not range(0, 1000, price)) each(output(sum(price)) each(output(summary()))))
    -
    -

    - With many faceted search UIs, you often have a filtering problem: if the user clicks on a facet option, - like a customer name, you want to still show all the customers, even though results are filtered by that customer. -

    -

    - Still, if there's another option from another facet (e.g., item name), you want the other facet options, - such as customer names, to only include results matching that item name. In other words, clicking on - a facet option should apply that filter to everything else (other facet options and results list), - but not to itself. -

    -

    - Here's how users expect a UI where they selected both a customer and an item: -

    -Faceted search example -

    - To implement this, you need one grouping expression for each filter combination. In this - case, one for the main results list, one for the customer facet, and one for the item facet. - NOTE: using grouping filters for this is a lot slower than running separate - queries and filtering there. -

    -
    -/search/?yql=select * from sources * where true |
    -    all( group(1) filter(regex('Brown',customer) and regex('Rocker arm',item)) each(output(count())  each(output(summary())) ) ) |
    -    all( group(customer) filter(regex('Rocker arm',item)) each(output(count()) ) ) |
    -    all( group(item) filter(regex('Brown',customer)) each(output(count()) ) ) 
    -
    -

    - Use istrue to filter using a boolean attribute field directly. For instance, if you want to output how - many purchases have been paid by each customer, you could express that as: -

    -
    -/search/?yql=select * from sources * where true |
    -    all(group(customer) filter(istrue(is_paid)) each(output(count())))
    -
    -

    - To find out how many purchases have not been paid yet, prepend istrue with not like this: - not istrue(is_paid). -

    - -

    Global limit for grouping queries

    - -

    Use the grouping.globalMaxGroups query parameter -to restrict execution of queries that are potentially too expensive in terms of compute and bandwidth. -Queries that may return a result exceeding this threshold are failed preemptively. -This limit is compared against the total number of groups and hits that query could return at worst-case.

    - -

    Examples

    -

    - The following query may return 5 groups and 0 hits. - It will be rejected when grouping.globalMaxGroups < 5 -

    -
    -  select (…) | all(group(item) max(5) each(output(count())))
    -
    - -

    - The following query may return 5 groups and 35 hits. - It will be rejected when grouping.globalMaxGroups < 5+5*7. -

    -
    -  select (…) | all(
    -    group(customer) max(5)
    -      each(
    -        output(count()) max(7)
    -          each(output(summary()))
    -      )
    -  )
    -
    - -

    - The following query may return 6 groups and 30 hits. - It will be rejected when grouping.globalMaxGroups < 2*(3+3*5). -

    -
    -select (…) |
    -all(
    -    all(group(item) max(3)
    -      each(output(count()) max(5)
    -        each(output(summary()))))
    -    all(group(customer) max(3)
    -      each(output(count()) max(5)
    -        each(output(summary())))))
    -
    -
    - -

    Combining with default limits for groups/hits

    -

    - The grouping.globalMaxGroups restriction will utilize the - grouping.defaultMaxGroups/ - grouping.defaultMaxHits - values for grouping statements without a max. The two queries below are identical, assuming - defaultMaxGroups=5 and defaultMaxHits=7, and both will be rejected when - globalMaxGroups < 5+5*7. -

    -
    -select (…) |
    -all(
    -  group(customer) max(5)
    -    each(
    -      output(count()) max(7)
    -        each(output(summary()))
    -    )
    -)
    -
    -
    -select (…) |
    -all(
    -  group(customer)
    -    each(
    -      output(count())
    -        each(output(summary()))
    -    )
    -)
    -
    -

    - A grouping without max combined with defaultMaxGroups=-1/defaultMaxHits=-1 - will be rejected unless globalMaxGroups=-1. This is because the query produces an unbounded result, - an infinite number of groups if defaultMaxGroups=-1 or an infinite number of summaries if - defaultMaxHits=-1. - An unintentional DoS (Denial of Service) could be the utter consequence if a query returns thousands of groups and summaries. - This is why setting globalMaxGroups=-1 is risky. -

    - -

    Recommended settings

    -

    - The best practice is to always specify max in groupings, - making it easy to reason about the worst-case cardinality of the query results. The performance will also benefit. - Set globalMaxGroups to the overall worst-case result cardinality with some margin. - The defaultMaxGroups/defaultMaxHits - should be overridden in a query profile if some groupings do not use max and the default values are too low. -

    - -
    -  <query-profile id="default">
    -    <field name="grouping.defaultMaxGroups">20</field>
    -    <field name="grouping.defaultMaxHits">100</field>
    -    <field name="grouping.globalMaxGroups">8000</field>
    -  </query-profile>
    -
    - -

    Performance and Correctness

    -

    - Grouping is, by default, tuned to favor performance over correctness. - Perfect correctness may not be achievable; result of queries using non-default ordering - can be approximate, and correctness can only be partially achieved by a larger precision value that sacrifices performance. -

    -

    - The grouping session cache is enabled by default. - Disabling it will improve correctness, especially for queries using order and max. - The cost of multi-level grouping expressions will increase, though. -

    -

    - Consider increasing the precision value when using max in combination with order. - The default precision may not achieve the required correctness for your use case. -

    - -

    Nested Groups

    -

    -Groups can be nested. This offers great drilling capabilities, -as there are no limits to nesting depth or presented information on any level. -Example: How much each customer has spent per day by grouping on customer, then date: -

    -
    -select (…) | all(group(customer) each(group(time.date(date)) each(output(sum(price)))))
    -
    -

    -Use this to query for all items on a per-customer basis, displaying the most expensive hit for each customer, -with subgroups of purchases on a per-date basis. -Use the summary clause -to show hits inside any group at any nesting level. -Include the sum price for each customer, both as a grand total and broken down on a per-day basis: -

    -
    -/search/?yql=select * from sources * where true limit 0|
    -             all(group(customer)
    -                 each(max(1) output(sum(price)) each(output(summary())))
    -                      each(group(time.date(date))
    -                      each(max(10) output(sum(price)) each(output(summary())))))
    -        &ranking=pricerank
    -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    GroupIdsum(price)
    Brown$20 537
    DatePriceTaxItemCustomer
    2006-09-08 11:00$8 0000.12HeadBrown
    GroupIdSum(price)
    2006-09-08$8 000
    DatePriceTaxItemCustomer
    2006-09-08 11:00$8 0000.12HeadBrown
    2006-09-09$3 400
    DatePriceTaxItemCustomer
    2006-09-09 11:00$3 4000.12Oil panBrown
    2006-09-10$7 540
    DatePriceTaxItemCustomer
    2006-09-10 10:00$3 7700.12SpringBrown
    2006-09-10 12:00$2 3300.24Rocker armBrown
    2006-09-10 11:00$1 4400.12Exhaust valveBrown
    2006-09-11$1 597
    DatePriceTaxItemCustomer
    2006-09-11 10:00$1 5970.24PistonBrown
    Jones$39 816
    DatePriceTaxItemCustomer
    2006-09-11 12:00$9 8700.12Exhaust portJones
    GroupIdSum(price)
    2006-09-08$8 000
    DatePriceTaxItemCustomer
    2006-09-08 10:00$5 0000.24Intake portJones
    2006-09-08 12:00$3 0000.12Valve coverJones
    2006-09-09$2 100
    DatePriceTaxItemCustomer
    2006-09-09 10:00$2 1000,12Engine blockJones
    2006-09-10$8 900
    DatePriceTaxItemCustomer
    2006-09-10 10:00$8 9000.24CamshaftJones
    2006-09-11$20 816
    DatePriceTaxItemCustomer
    2006-09-11 12:00$9 8700.12Exhaust portJones
    2006-09-11 13:00$6 7650.12CrankshaftJones
    2006-09-11 12:00$4 1810.24Rod bearingJones
    Smith$19 484
    DatePriceTaxItemCustomer
    2006-09-10 11:00$6 1000.24Spark plugSmith
    GroupIdSum(price)
    2006-09-06$1 000
    DatePriceTaxItemCustomer
    2006-09-06 09:00$1 0000.24Intake valveSmith
    2006-09-07$3 000
    DatePriceTaxItemCustomer
    2006-09-07 11:00$2 0000.24SpringSmith
    2006-09-07 10:00$1 0000.12Rocker armSmith
    2006-09-09$6 800
    DatePriceTaxItemCustomer
    2006-09-09 12:00$5 5000.12Oil sumpSmith
    2006-09-09 12:00$1 3000.24CoolantSmith
    2006-09-10$6 100
    DatePriceTaxItemCustomer
    2006-09-10 11:00$6 1000.24Spark plugSmith
    2006-09-11$2 584
    DatePriceTaxItemCustomer
    2006-09-11 11:00$2 5840.12Connection rodSmith
    - - -

    Structured grouping

    -

    -Structured grouping is nested grouping over an array of structs or maps. -In this case, each array element is treated as a sub-document and may -be grouped separately. See the reference for grouping on -multivalue attributes -for details. It is also possible to -filter the groups -so only matching elements are considered. An example could be: -

    -  select (…) | all(group(attributes.value) filter(regex("delivery_method",attributes.key)) each(output(sum(price)) each(output(summary()))))
    -
    -

    - -

    Range grouping

    -

    -In the examples above, results are grouped on distinct values, like customer or date. -To group on price: -

    -
    -select (…) | all(group(price) each(each(output(summary()))))
    -
    -

    -This gives one group per price. To group on price ranges, one could compress the price range. -This gives prices in $0 - $999 in bucket 0, $1 000 - $2 000 in bucket 1 and so on: -

    -
    -select (…) | all(group(price/1000) each(each(output(summary()))))
    -
    -

    -An alternative is using bucket expressions - -think of a bucket as the range per group. -Group on price, make groups have a width of 1000: -

    -
    -select (…) | all(group(fixedwidth(price,1000)) each(each(output(summary()))))
    -
    -

    Use predefined to configure group sizes individually (the two below are equivalent):

    -
    -select (…) |
    -all(
    -  group(predefined(price,
    -    bucket(0,1000),
    -    bucket(1000,2000),
    -    bucket(2000,5000),
    -    bucket(5000,inf)))
    -  each(each(output(summary())))
    -)
    -
    -

    This works with strings as well - put Jones and Smith in the second group:

    -
    -select (…) | all(group(predefined(customer, bucket(-inf,"Jones"), bucket("Jones", inf))) each(each(output(summary()))))
    -
    -

    ... or have Jones in his own group:

    -
    -select (…) | all(group(predefined(customer, bucket<-inf,"Jones">, bucket["Jones"], bucket<"Jones", inf>)) each(each(output(summary()))))
    -
    -

    Use decimal numbers in bucket definitions if the expression evaluates to a double or float:

    -
    -select (…) 
    -| all(
    -  group(predefined(tax,
    -      bucket(0.0, 0.2),
    -      bucket(0.2, 0.5),
    -      bucket(0.5, inf)))
    -    each(
    -      each(output(summary()))
    -    )
    -)
    -
    - -

    Pagination

    -

    - Grouping supports continuation objects - that are passed as annotations to the grouping statement. - The continuations annotation is a list of zero or more continuation strings, - returned in the grouping result. - For example, given the result: -

    -
    {% highlight json %}
    -{
    -    "root": {
    -        "children": [
    -            {
    -                "children": [
    -                    {
    -                        "children": [
    -                            {
    -                                "fields": {
    -                                    "count()": 7
    -                                },
    -                                "value": "Jones",
    -                                "id": "group:string:Jones",
    -                                "relevance": 1.0
    -                            }
    -                        ],
    -                        "continuation": {
    -                            "next": "BGAAABEBEBC",
    -                            "prev": "BGAAABEABC"
    -                        },
    -                        "id": "grouplist:customer",
    -                        "label": "customer",
    -                        "relevance": 1.0
    -                    }
    -                ],
    -                "continuation": {
    -                    "this": "BGAAABEBCA"
    -                },
    -                "id": "group:root:0",
    -                "relevance": 1.0
    -            }
    -        ],
    -        "fields": {
    -            "totalCount": 20
    -        },
    -        "id": "toplevel",
    -        "relevance": 1.0
    -    }
    -}
    -{% endhighlight %}
    -

    -reproduce the same result by passing the this-continuation along the original select: -

    -
    -select (…) | { 'continuations':['BGAAABEBCA'] }all(…)
    -
    -

    -To display the next page of customers, pass the this-continuation of the root -group, and the next continuation of the customer list: -

    -
    -select (…) | { 'continuations':['BGAAABEBCA', 'BGAAABEBEBC'] }all(…)
    -
    -

    - To display the previous page of customers, pass the this-continuation of the root group, - and the prev continuation of the customer list: -

    -
    -select (…) | { 'continuations':['BGAAABEBCA', 'BGAAABEABC'] }all(…)
    -
    -

    - The continuations annotation is an ordered list of continuation strings. - These are combined by replacement - so that a continuation given later will replace any shared state with a continuation given before. - Also, when using the continuations annotation, - always pass the this-continuation as its first element. -

    -{% include note.html content="Continuations work best when the ordering of hits is stable - -which can be achieved by using ranking or -ordering. -Adding a tie-breaker might be needed - like random.match -or a random double value stored in each document - -to keep the ordering stable in case of multiple documents that would otherwise get the same rank score -or the same value used for ordering."%} - - - -

    Expressions

    -

    -Instead of just grouping on some attribute value, -the group clause may contain arbitrarily complex expressions - -see group in the -grouping reference for an exhaustive list. -Examples: -

    -
      -
    • Select everything. For example, group("all") each(output(sum(price))) gives total revenue
    • -
    • Select the minimum or maximum of sub-expressions
    • -
    • Addition, subtraction, multiplication, division, and even modulo of sub-expressions
    • -
    • Bitwise operations on sub-expressions
    • -
    • Concatenation of the results of sub-expressions
    • -
    -

    - Sum the prices of purchases on a per-hour-of-day basis: -

    -
    -select (…) | all(group(mod(div(date,mul(60,60)),24)) each(output(sum(price))))
    -
    - -

    -These types of expressions may also be used inside output operations, -so instead of simply calculating the sum price of the grouped purchases, -calculate the sum income after taxes per customer: -

    -
    -select (…) | all(group(customer) each(output(sum(mul(price,sub(1,tax))))))
    -
    -

    -Note that the validity of an expression depends on the current nesting level. -For, while sum(price) would be a valid expression for a group of hits, price would not. -As a general rule, each operator within an expression either applies to a single hit or aggregates values across a group. -

    - - - -

    Search Container API

    -

    -As an alternative to a textual representation, -one can use the programmatic API to execute grouping requests. -This allows multiple grouping requests to run in parallel, -and does not collide with the yql parameter - example: -

    -
    {% highlight java %}
    -@Override
    -public Result search(Query query, Execution execution) {
    -    // Create grouping request.
    -    GroupingRequest request = GroupingRequest.newInstance(query);
    -    request.setRootOperation(new AllOperation()
    -            .setGroupBy(new AttributeValue("foo"))
    -            .addChild(new EachOperation()
    -                .addOutput(new CountAggregator().setLabel("count"))));
    -
    -    // Perform grouping request.
    -    Result result = execution.search(query);
    -
    -    // Process grouping result.
    -    Group root = request.getResultGroup(result);
    -    GroupList foo = root.getGroupList("foo");
    -    for (Hit hit : foo) {
    -        Group group = (Group)hit;
    -        Long count = (Long)group.getField("count");
    -        // TODO: Process group and count.
    -    }
    -
    -    // Pass results back to calling searcher.
    -    return result;
    -}
    -{% endhighlight %}
    -

    -Refer to the - -API documentation for the complete reference. -

    - - - -

    TopN / Full corpus

    -

    -Simple grouping: count the number of documents in each group: -

    -
    select * from purchase where true | all( group(customer) each(output(count())) )
    -

    Two parallel groupings:

    -
    select * from purchase where true | all(
    -  all(
    -      group(customer) each(output(count()))
    -  )
    -  all(
    -      group(item) each(output(count()))
    -  )
    -)
    -

    Only the 1000 best hits will be grouped at each content node. Lower accuracy, but higher speed:

    -
    select * from purchase where true limit 0 |
    -  all(
    -    max(1000)
    -    all(
    -      group(customer)
    -      each(output(count()))
    -    )
    -  )
    - - - -

    Selecting groups

    -

    Do a modulo 3 operation before selecting the group:

    -
    select * from purchase where true limit 0 |
    -  all(
    -    group(price % 3)
    -    each(output(count()))
    -  )
    -

    Do price + tax * price before selecting the group:

    -
    select * from purchase where true limit 0 | all(
    -  group(price + tax * price)
    -  each(output(count()))
    -)
    - - - -

    Ordering groups

    -

    -Do a modulo 5 operation before selecting the group - -the groups are then ordered by their aggregated sum of attribute "tax": -

    -
    select * from purchase where true limit 0 | all(
    -  group(price % 5)
    -  order(sum(tax))
    -  each(output(count()))
    -)
    -
    -

    -Do price + tax * price before selecting the group. -Ordering is given by the maximum value of attribute "price" in each group: -

    -
    select * from purchase where true limit 0 |
    -  all(
    -    group(price + tax * price)
    -    order(max(price))
    -    each(output(count()))
    -  )
    -  
    -

    - Take the average relevance of the groups and multiply it with - the number of groups to get a cumulative count: -

    -
     select * from purchase where true limit 0 |
    -  all(
    -  group(customer)
    -  order(avg(relevance()) * count())
    -  each(output(count()))
    -)
    -

    One can not directly reference an attribute in the order clause, as this:

    -
    select * from purchase where true limit 0 |
    -  all(
    -  group(customer)
    -  order(price * count())
    -  each(output(count()))
    -)
    -

    However, one can do this:

    -
    select * from purchase where true limit 0 | all(
    -  group(customer)
    -  order(max(price) * count())
    -  each(output(count()))
    -)
    -

    Ordering alphabetically works in a similar way: -

    select * from purchase where true limit 0 | all(
    -  group(customer)
    -  order(max(customer))
    -  each(output(count()))
    -)
    -{% include note.html content="You can control non-ASCII character folding behavior with unicode collation."%} - -

    Collecting aggregates

    -

    -Simple grouping to count the number of documents in each group and return the best hit in each group: -

    -
    select * from purchase where true limit 0 | 
    -  all(
    -  group(customer)
    -  each(
    -    max(1)
    -    each(output(summary()))
    -  )
    -)
    -

    Also return the sum of attribute "price":

    -
    select * from purchase where true limit 0 | 
    -  all( group(customer) each(max(1) output(count(), sum(price)) each(output(summary()))) )
    -  
    -

    - Also, return an XOR of the 64 most significant bits of an MD5 - over the concatenation of attributes "customer", "price" and "tax": -

    -
    select * from purchase where true limit 0 | 
    -  all(group(customer) each(max(1) output(count(), sum(price), xor(md5(cat(customer, price, tax), 64))) each(output(summary()))))
    -  
    -

    - It is also possible to return quantiles, for instance, the p50 and p90 of the price. -

    -
    select * from purchase where true limit 0 |
    -  all(group(customer) each(output(quantiles([0.5,0.9], price))))
    -  
    - - - -

    Grouping

    -

    - Single-level grouping on "customer" attribute, returning at most 5 groups with full hit count as well as the 69 best hits. -

    -
    select * from purchase where true limit 0 | all(group(customer) max(5) each(max(69) output(count()) each(output(summary())))) 
    -

    Two level grouping on "customer" and "item" attribute:

    -
    select * from purchase where true limit 0 | all(group(customer) max(5) each(output(count()) all(group(item) max(5) each(max(69) output(count()) each(output(summary())))))) 
    -

    Three-level grouping on "customer", "item" and "attributes.key(coupon)" attribute:

    -
    select * from purchase where true limit 0 | all(group(customer) max(1) each(output(count()) all(group(item) max(1) each(output(count()) max(1) all(group(attributes.key) max(1) each(output(count()) each(output(summary()))))))))
    -

    As above, but also collect best hit in level 2:

    -
    select * from purchase where true limit 0 | all(group(customer) max(5) each(output(count()) all(group(item) max(5) each(output(count()) all(max(1) each(output(summary()))) all(group(attributes.key) max(5) each(max(69) output(count()) each(output(summary())))))))) 
    -

    As above, but also collect best hit in level 1:

    -
    select * from purchase where true limit 0 | all(group(customer) max(5) each(output(count()) all(max(1) each(output(summary()))) all(group(item) max(5) each(output(count()) all(max(1) each(output(summary()))) all(group(attributes.key) max(5) each(max(69) output(count()) each(output(summary())))))))) 
    -

    As above, but using different document summaries on each level:

    -
     select * from purchase where true limit 0 |
    -  all( group(customer) max(5) each(output(count())
    -     all(max(1) each(output(summary(complexsummary))))
    -     all(group(item) max(5) each(output(count())
    -         all(max(1) each(output(summary(simplesummary))))
    -         all(group(price) max(5) each(max(69) output(count())
    -             each(output(summary(fastsummary)))))) )
    -
    -
    -

    Deep grouping with counting and hit collection on all levels:

    -
     select * from purchase where true limit 0 |
    -  all( group(customer) max(5) each(output(count())
    -     all(max(1) each(output(summary())))
    -     all(group(item) each(output(count())
    -         all(max(1) each(output(summary())))
    -         all(group(price) each(output(count())
    -             all(max(1) each(output(summary())))))))) )
    -
    -
    - - - -

    Time and date

    -

    - The field (time below, but can have any name) - must be a long, with second resolution (unix timestamp/epoch). - See the reference for all time-functions. -

    -

    Group by year:

    -
    select * from purchase where true limit 0 | all(group(time.year(date)) each(output(count()))) 
    -

    Group by year, then by month:

    -
    select * from purchase where true limit 0 |
    -  all( group(time.year(date)) each(output(count())
    -     all(group(time.monthofyear(date)) each(output(count())))) )
    - - Groups today, yesterday, lastweek, and lastmonth - using predefined aggregator, and groups each day within each of these separately: -

    -
     select * from purchase where true limit 0 |
    -  all( 
    -    group(
    -      predefined((now() - date) / (60 * 60 * 24),
    -      bucket(0,1),
    -      bucket(1,2),
    -      bucket(3,7),
    -      bucket(8,31))
    -      )
    -    each(output(count())
    -      all(max(2) each(output(summary())))
    -      all(group((now() - date) / (60 * 60 * 24)) 
    -        each(output(count())
    -          all(max(2) each(output(summary())))
    -        )
    -      )
    -    ) 
    -  )
    -
    - -

    Timezones in grouping

    - -The timezone query parameter can be used to rewrite each time-function with a timezone offset. -See the reference. Example: - -
    -$ vespa query "select * from purchase where true | \
    -               all( group(time.hourofday(date)) each(output(count()))" \
    -              "timezone=America/Los_Angeles"
    -
    - -

    - This query selects all documents from purchase, groups them by the - hour they were made (adjusted to the local time - in America/Los_Angeles), and counts how many purchases fall into - each hour. -

    - - -

    Geo distance

    -

    - Use geo_distance to compute the great-circle distance from a - position field to a given point. - Append .km or .miles to select the output unit. - The function works on both position and array<position> fields. - For arrays, the minimum distance is returned. - See the reference. -

    -

    Group into 100 km distance buckets from a point:

    -
    -select * from purchase where true limit 0 |
    -  all( group(fixedwidth(geo_distance(attribute(location), 63.4, 10.4).km, 100.0)) each(output(count())) )
    -
    -

    Output the minimum and maximum distance per customer:

    -
    -select * from purchase where true limit 0 |
    -  all( group(customer) each(output(min(geo_distance(attribute(location), 63.4, 10.4).km),
    -                                    max(geo_distance(attribute(location), 63.4, 10.4).km))) )
    -
    - - -

    Counting unique groups

    -

    - The count aggregator can be applied on a list of groups to determine the number of unique groups - without having to explicitly retrieve all groups. - Note that this count is an estimate using HyperLogLog++ which is an algorithm for the count-distinct problem. - To get an accurate count, one needs to explicitly retrieve all groups - and count them in a custom component or in the middle tier calling out to Vespa. - This is network intensive and might not be feasible in cases with many unique groups. -

    -

    - Another use case for this aggregator is counting the number of unique instances matching a given expression. -

    -

    - Output an estimate of the number of groups, which is equivalent to the number of unique values for attribute "customer": -

    -
    select * from purchase where true limit 0 | all( group(customer) each(output(count())) ) 
    -

    Output an estimate of the number of unique string lengths for the attribute "item":

    -
    select * from purchase where true limit 0 | all(group(strlen(item)) each(output(count()))) 
    -

    - Output the sum of the "price" attribute for each group - in addition to the accurate count of the overall number of unique groups - as the inner each causes all groups to be returned. -

    -
    select * from purchase where true limit 0 | all(group(customer) output(count()) each(output(sum(price)))) 
    -

    - The max clause is used to restrict the number of groups returned. - The query outputs the sum for the 3 best groups. - The count clause outputs the estimated number of groups (potentially >3). - The count becomes an estimate here as the number of groups is limited by max, - while in the above example, it's not limited by max: -

    -
    select * from purchase where true limit 0 | all(group(customer) max(3) output(count()) each(output(sum(price)))) 
    -

    - Output the number of top-level groups, and for the 10 best groups, - output the number of unique values for attribute "item": -

    -
    select * from purchase where true limit 0 | all(group(customer) max(10) output(count()) each(group(item) output(count()))) 
    - - -

    Counting unique groups - multivalue fields

    -

    - A multivalue attribute is a - weighted set, - array or - map. - Most grouping functions will just handle the elements of multivalued attributes separately, - as if they were all individual values in separate documents. - If you are grouping over array of struct or maps, scoping will be used to preserve structure. - Each entry in the array/map will be treated as a separate sub-document, - so documents can be counted twice or more - see - #33646 for details. -

    -

    - This could be solved by adding an additional level of grouping, - where you group on a field that is unique for each document (grouping on document ID is not supported). - You may then count the unique groups to determine the unique document count: -

    -
    select * from purchase where true limit 0 | all(group(customer) each(group(item) output(count()))) 
    - - - -

    Impression forecasting

    -

    -Using impression logs for a given user, -one can make a function that maps from rank score to the number of impressions an advertisement would get - example: -

    -
    -Score   Integer (# impressions for this user)
    -0.200   0
    -0.210   1
    -0.220   2
    -0.240   3
    -0.320   4
    -0.420   5
    -0.560   6
    -0.700   7
    -0.800   8
    -0.880   9
    -0.920  10
    -0.940  11
    -0.950  12
    -
    -

    -Storing just the first column (the rank scores, including a rank score for 0 impressions) -in an array attribute named impressions, the grouping operation -interpolatedlookup(impressions, relevance()) -can be used to figure out how many times a given advertisement would have been shown to this particular user. -

    -So if the rank score is 0.420 for a specific user/ad/bid combination, -then interpolatedlookup(impressions, relevance()) would return 5.0. -If the bid is increased so the rank score gets to 0.490, -it would get 5.5 as the return value instead. -

    -In this context, a count of 5.5 isn't meaningful for the past of a single user, -but it gives more information that may be used as a forecast. -Summing this across more, different users may then be used to forecast -the total of future impressions for the advertisement. -

    - - - -

    Aggregating over all documents

    -

    - Grouping is useful for analyzing data. - To aggregate over the full document set, create one group (which will have all documents) - by using a constant (here 1) - example: -

    -
    -select rating from restaurant where true | all(group(1) each(output(avg(price))))
    -
    -

    Make sure all documents have a value for the given field, if not, NaN is used, and the final result is also NaN:

    -
    {% highlight json %}
    -{
    -    "id": "group:long:1",
    -    "relevance": 0.0,
    -    "value": "1",
    -    "fields": {
    -        "avg(rating)": "NaN"
    -    }
    -}
    -{% endhighlight %}
    - - - -

    Count fields with NaN

    -

    - Count number of documents missing a value for an attribute field - (actually, in this example, unset or less than 0, see the bucket expression below). - Set a higher query timeout, just in case. - Example, analyzing a field called price: -

    -
    -select rating from restaurant where true | all( group(predefined(price, bucket[-inf, 0>, bucket[0, inf>)) each(output(count())) )
    -
    -

    Example output, counting 2 documents with -inf in rating:

    -
    {% highlight json %}
    -"children": [
    -    {
    -        "id": "group:long_bucket:-9223372036854775808:0",
    -        "relevance": 0.0,
    -        "limits": {
    -            "from": "-9223372036854775808",
    -            "to": "0"
    -        },
    -        "fields": {
    -            "count()": 2
    -        }
    -    },
    -    {
    -        "id": "group:long_bucket:0:9223372036854775807",
    -        "relevance": 0.0,
    -        "limits": {
    -            "from": "0",
    -            "to": "9223372036854775807"
    -        },
    -        "fields": {
    -            "count()": 8
    -        }
    -    }
    -]
    -{% endhighlight %}
    -

    - See analyzing field values - for how to export ids of documents meeting given criteria from the full corpus. -

    - - - -

    List fields with NaN

    -

    - This is similar to the counting of NaN above, - but instead of aggregating the count, for each hit, - print a document summary: -

    -
    -select rating from restaurant where true |
    -    all( group(predefined(price, bucket[-inf, 0>, bucket[0, inf>))
    -    order(max(price))
    -    max(1)
    -    each( max(100) each(output(summary()))) )
    -
    -

    Notes:

    -
      -
    • - We are only interested in the first group, - so order by max(price) and use max(1) to get only the first -
    • -
    • - Uses max(100) in order to limit result set sizes. - Read more about grouping.defaultmaxhits. -
    • - -
    • - Use the continuation token to iterate over the result set. -
    • -
    - - - -

    Grouping over a Map field

    -

    In the example data, a record looks like:

    -
    {% highlight json %}
    -{
    -  "fields": {
    -    "attributes": {
    -      "delivery_method": "Curbside Pickup",
    -      "sales_rep": "Bonnie",
    -      "coupon": "SAVE10"
    -    },
    -    "customer": "Smith",
    -    "date": 1157526000,
    -    "item": "Intake valve",
    -    "price": "1000",
    -    "tax": "0.24"
    -  }
    -}
    -{% endhighlight %}
    -

    The map field schema definition is:

    -
    -field attributes type map<string, string> {
    -    indexing: summary
    -    struct-field key   { indexing: attribute }
    -    struct-field value { indexing: attribute }
    -}
    -
    -

    - With this, one can group on both key (delivery_method, sales_rep, and coupon) - and values (here counting each value). - Try the link to see the output: -

    -

    -

    select * from purchase where true limit 0 |
    -    all(
    -      group(attributes.key)
    -      each( group(attributes.value) each(output(count())))
    -    )
    -

    -

    - A more interesting example is to see the sum per sales rep: -

    -

    -

    select * from purchase where true limit 0 |
    -    all(
    -      group(attributes.key)
    -      each( group(attributes.value) each(output(sum(price))))
    -    )
    -

    - - - diff --git a/en/querying/grouping.mdx b/en/querying/grouping.mdx new file mode 100644 index 0000000000..98b0133261 --- /dev/null +++ b/en/querying/grouping.mdx @@ -0,0 +1,1725 @@ +--- +title: "Grouping and aggregation" +--- + +## Grouping Interface + +Try running requests on the [grouping example data](https://github.com/vespa-engine/sample-apps/blob/master/examples/part-purchases-demo/ext/feed.jsonl): + +```js +all( group(customer) each(output(sum(price))) ) +``` + +The Vespa grouping language is a list-processing language which describes how the query hits should be grouped, aggregated, and presented in result sets. A grouping statement takes the list of all matches to a query as input and groups/aggregates it, possibly in multiple nested and parallel ways to produce the output. This is a logical specification and does not indicate how it is executed, as instantiating the list of all matches to the query somewhere would be too expensive, and execution is distributed instead. + +Refer to the [Query API reference](../reference/api/query#select) for how to set the *select* parameter, and the [Grouping reference](../reference/querying/grouping-language) for details. Fields used in grouping must be defined as [attribute](../content/attributes) in the document schema. Grouping supports continuation objects for [pagination](#pagination). + +The [Grouping Results](https://github.com/vespa-engine/sample-apps/tree/master/examples/part-purchases-demo) sample application is a practical example. + +## The grouping language structure + +The operations defining the structure of a grouping are: + +- `all(statement)`: Execute the nested statement once on the input list as a whole. +- `each(statement)`: Execute the nested statement on each element of the input list. +- `group(specification)`: Turn the input list into a list of lists according to the grouping specification. +- `output`: Output some value(s) at the current location in the structure. + +The parallel and nested collection of these operations defines both the structure of the computation and of the result it produces. For example, `all(group(customer) each(output(count())))` will take all matches, group them by customer id, and for each group, output the count of hits in the group. + +Vespa distributes and executes the grouping program on content nodes and merges results on container nodes - in multiple phases, as needed. As realizing such programs over a distributed data set requires more network round-trips than a regular search query, these queries may be more expensive than regular queries - see [defaultMaxGroups](../reference/api/query#grouping.defaultmaxgroups) and the likes for how to control resource usage. + +## Grouping by example + +For the entirety of this document, assume an index of engine part purchases: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    DatePriceTaxItemCustomerIs paid
    2006-09-06 09:00:00$1 0000.24Intake valveSmithtrue
    2006-09-07 10:00:00$1 0000.12Rocker armSmithfalse
    2006-09-07 11:00:00$2 0000.24SpringSmithtrue
    2006-09-08 12:00:00$3 0000.12Valve coverJonesfalse
    2006-09-08 10:00:00$5 0000.24Intake portJonestrue
    2006-09-08 11:00:00$8 0000.12HeadBrownfalse
    2006-09-09 12:00:00$1 3000.24CoolantSmithtrue
    2006-09-09 10:00:00$2 1000.12Engine blockJonesfalse
    2006-09-09 11:00:00$3 4000.24Oil panBrowntrue
    2006-09-09 12:00:00$5 5000.12Oil sumpSmithfalse
    2006-09-10 10:00:00$8 9000.24CamshaftJonestrue
    2006-09-10 11:00:00$1 4400.12Exhaust valveBrownfalse
    2006-09-10 12:00:00$2 3300.24Rocker armBrowntrue
    2006-09-10 10:00:00$3 7700.12SpringBrownfalse
    2006-09-10 11:00:00$6 1000.24Spark plugSmithtrue
    2006-09-11 12:00:00$9 8700.12Exhaust portJonesfalse
    2006-09-11 10:00:00$1 5970.24PistonBrowntrue
    2006-09-11 11:00:00$2 5840.12Connection rodSmithfalse
    2006-09-11 12:00:00$4 1810.24Rod bearingJonestrue
    2006-09-11 13:00:00$6 7650.12CrankshaftJonesfalse
    + +## Basic Grouping + +Example: *Return the total sum of purchases per customer* - steps: + + + +Select all documents: + +```js +/search/?yql=select * from sources * where true +``` + + +Take the list of all hits: + +```js +all(...) +``` + + +Turn it into a list of lists of all hits having the same customer id: + +```js +group(customer) +``` + + +For each of those lists of same-customer hits: +each(...) + + +Output the sum (an aggregator) of the price over all items in that list of hits: + +```js +output(sum(price)) +``` + + + + +Final query, producing the sum of the price of all purchases for each customer: + +```js +/search/?yql=select * from sources * where true limit 0 | + all( group(customer) each(output(sum(price))) ) +``` + +Here, limit is set to zero to get the grouping output only. URL encoded equivalent: + +```js +/search/?yql=select%20%2A%20from%20sources%20%2A%20where%20true%20limit%200%20%7C%20 + all%28%20group%28customer%29%20each%28output%28sum%28price%29%29%29%20%29 +``` + +Result: + + + + + + + + + + + + + + + + + + + + + + +
    GroupIdSum(price)
    Brown$20 537
    Jones$39 816
    Smith$19 484
    + +Example: *Sum price of purchases [per date](#time-and-date):* + +```js +select (…) | all(group(time.date(date)) each(output(sum(price)))) +``` + +Note: in examples above, *all* documents are evaluated. Modify the query to add filters (and thus cut latency), like (remember to URL encode): + +```js +/search/?yql=select * from sources * where customer contains "smith" +``` + +## Ordering and Limiting Groups + +In many scenarios, a large collection of groups is produced, possibly too large to display or process. This is handled by ordering groups, then limiting the number of groups to return. + +The `order` clause accepts a list of one or more expressions. Each of the arguments to `order` is prefixed by either a plus/minus for ascending/descending order. + +Limit the number of groups using `max` and `precision` - the latter is the number of groups returned per content node to be merged to the global result. Larger document distribution skews hence require a higher `precision` for accurate results. + +An implicit limit can be specified through the [grouping.defaultMaxGroups](../reference/api/query#grouping.defaultmaxgroups) query parameter. This value will always be overridden if `max` is explicitly specified in the query. Use `max(inf)` to retrieve all groups when the query parameter is set. + +If `precision` is not specified, it will default to a factor times `max`. This factor can be overridden through the [grouping.defaultPrecisionFactor](../reference/api/query#grouping.defaultprecisionfactor) query parameter. + +Example: To find the 2 globally best groups, make an educated guess on how many samples are needed to fetch from each node in order to get the right groups. This is the `precision`. An initial factor of 3 has proven to be quite good in most use cases. If however, the data for customer 'Jones' was spread on 3 different content nodes, 'Jones' might be among the 2 best on only one node. But based on the distribution of the data, we have concluded by earlier tests that if we fetch 5.67 as many groups as we need to, we will have a correct answer with at least 99.999% confidence. So then we just use 6 times as many groups when doing the merge. + +However, there is one exception. Without an `order` constraint, `precision` is not required. Then, local ordering will be the same as global ordering. Ordering will not change after a merge operation. + +### Example + +Example: *The two customers with most purchases, returning the sum for each:* + +```js +select (…) | all(group(customer) max(2) precision(12) order(-count()) + each(output(sum(price)))) +``` + +## Hits per Group + +Use `summary` to print the fields for a hit, and `max` to limit the number of hits per group. + +An implicit limit can be specified through the [grouping.defaultMaxHits](../reference/api/query#grouping.defaultmaxhits) query parameter. This value will always be overridden if `max` is explicitly specified in the query. Use `max(inf)` to retrieve all hits when the query parameter is set. + +### Example + +Example: Return the three most expensive parts per customer: + +```js +/search/?yql=select * from sources * where true | + all(group(customer) each(max(3) each(output(summary())))) +``` + +Notes on ordering in the example above: + +- The `order` clause is a directive for *group* ordering, not *hit* ordering. Here, there is no order clause on the groups, so default ordering `-max(relevance())` is used. The *\-* denotes the sorting order, *\-* means descending (higher score first). In this case, the query is "all documents", so all groups are equally relevant and the group order is random. +- To order hits inside groups, use ranking. Add `ranking=pricerank` to the query to use the pricerank [rank profile](../basics/ranking) to rank by price: + + ```sd + rank-profile pricerank inherits default { + first-phase { + expression: attribute(price) + } + } + ``` + + +## Filter within a group + +Use the `filter` clause to select which values to keep in a group. See the [reference](../reference/querying/grouping-language#filtering-groups) for details. + +### Examples + +Example: Sum the price per customer of `Bonn.*` where price was over 1000. + +```js +/search/?yql=select * from sources * where true | + all(group(customer) filter(regex("Bonn.*", attributes{"sales_rep"}) and not range(0, 1000, price)) each(output(sum(price)) each(output(summary())))) +``` + +With many faceted search UIs, you often have a filtering problem: if the user clicks on a facet option, like a customer name, you want to still show all the customers, even though results are filtered by that customer. + +Still, if there's another option from another facet (e.g., item name), you want the other facet options, such as customer names, to only include results matching that item name. In other words, clicking on a facet option should apply that filter to everything else (other facet options and results list), but not to itself. + +Here's how users expect a UI where they selected both a customer and an item: + + +![](/assets/img/ecommerce-facets.png) + + +To implement this, you need one grouping expression for each filter combination. In this case, one for the main results list, one for the customer facet, and one for the item facet. using grouping filters for this is **a lot slower** than running separate queries and filtering there. + +```js +/search/?yql=select * from sources * where true | + all( group(1) filter(regex('Brown',customer) and regex('Rocker arm',item)) each(output(count()) each(output(summary())) ) ) | + all( group(customer) filter(regex('Rocker arm',item)) each(output(count()) ) ) | + all( group(item) filter(regex('Brown',customer)) each(output(count()) ) ) +``` + +Use `istrue` to filter using a boolean attribute field directly. For instance, if you want to output how many purchases have been paid by each customer, you could express that as: + +```js +/search/?yql=select * from sources * where true | + all(group(customer) filter(istrue(is_paid)) each(output(count()))) +``` + +To find out how many purchases have not been paid yet, prepend `istrue` with `not` like this: `not istrue(is_paid)`. + +## Global limit for grouping queries + +Use the [grouping.globalMaxGroups](../reference/api/query#grouping.globalmaxgroups) query parameter to restrict execution of queries that are potentially too expensive in terms of compute and bandwidth. Queries that may return a result exceeding this threshold are failed preemptively. This limit is compared against the total number of groups and hits that query could return at worst-case. + +### Examples + +The following query may return 5 groups and 0 hits. It will be rejected when `grouping.globalMaxGroups < 5` + +```js +select (…) | all(group(item) max(5) each(output(count()))) +``` + +The following query may return 5 groups and 35 hits. It will be rejected when `grouping.globalMaxGroups < 5+5*7`. + +```js +select (…) | all( + group(customer) max(5) + each( + output(count()) max(7) + each(output(summary())) + ) + ) +``` + +The following query may return 6 groups and 30 hits. It will be rejected when `grouping.globalMaxGroups < 2*(3+3*5)`. + +```js +select (…) | +all( + all(group(item) max(3) + each(output(count()) max(5) + each(output(summary())))) + all(group(customer) max(3) + each(output(count()) max(5) + each(output(summary()))))) +``` + +### Combining with default limits for groups/hits + +The `grouping.globalMaxGroups` restriction will utilize the [grouping.defaultMaxGroups](../reference/api/query#grouping.defaultmaxgroups)/ [grouping.defaultMaxHits](../reference/api/query#grouping.defaultmaxhits) values for grouping statements without a `max`. The two queries below are identical, assuming `defaultMaxGroups=5` and `defaultMaxHits=7`, and both will be rejected when `globalMaxGroups < 5+5*7`. + +```js +select (…) | +all( + group(customer) max(5) + each( + output(count()) max(7) + each(output(summary())) + ) +) +text +select (…) | +all( + group(customer) + each( + output(count()) + each(output(summary())) + ) +) +``` + +A grouping without `max` combined with `defaultMaxGroups=-1`/`defaultMaxHits=-1` will be rejected unless `globalMaxGroups=-1`. This is because the query produces an unbounded result, an infinite number of groups if `defaultMaxGroups=-1` or an infinite number of summaries if `defaultMaxHits=-1`. An unintentional DoS (Denial of Service) could be the utter consequence if a query returns thousands of groups and summaries. This is why setting `globalMaxGroups=-1` is risky. + +### Recommended settings + +The best practice is to always specify `max` in groupings, making it easy to reason about the worst-case cardinality of the query results. The performance will also benefit. Set `globalMaxGroups` to the overall worst-case result cardinality with some margin. The `defaultMaxGroups`/`defaultMaxHits` should be overridden in a query profile if some groupings do not use `max` and the default values are too low. + +```xml + + 20 + 100 + 8000 + +``` + +## Performance and Correctness + +Grouping is, by default, tuned to favor performance over correctness. Perfect correctness may not be achievable; result of queries using [non-default ordering](#ordering-and-limiting-groups) can be approximate, and correctness can only be partially achieved by a larger `precision` value that sacrifices performance. + +The [grouping session cache](../reference/querying/grouping-language#grouping-session-cache) is enabled by default. Disabling it will improve correctness, especially for queries using `order` and `max`. The cost of multi-level grouping expressions will increase, though. + +Consider increasing the [precision](#ordering-and-limiting-groups) value when using `max` in combination with `order`. The default precision may not achieve the required correctness for your use case. + +## Nested Groups + +Groups can be nested. This offers great drilling capabilities, as there are no limits to nesting depth or presented information on any level. Example: How much each customer has spent per day by grouping on customer, then date: + +```js +select (…) | all(group(customer) each(group(time.date(date)) each(output(sum(price))))) +``` + +Use this to query for all items on a per-customer basis, displaying the most expensive hit for each customer, with subgroups of purchases on a per-date basis. Use the [summary](#hits-per-group) clause to show hits inside any group at any nesting level. Include the sum price for each customer, both as a grand total and broken down on a per-day basis: + +```js +/search/?yql=select * from sources * where true limit 0| + all(group(customer) + each(max(1) output(sum(price)) each(output(summary()))) + each(group(time.date(date)) + each(max(10) output(sum(price)) each(output(summary()))))) + &ranking=pricerank +``` + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    GroupIdsum(price)
    Brown$20 537
    DatePriceTaxItemCustomer
    2006-09-08 11:00$8 0000.12HeadBrown
    GroupIdSum(price)
    2006-09-08$8 000
    DatePriceTaxItemCustomer
    2006-09-08 11:00$8 0000.12HeadBrown
    2006-09-09$3 400
    DatePriceTaxItemCustomer
    2006-09-09 11:00$3 4000.12Oil panBrown
    2006-09-10$7 540
    DatePriceTaxItemCustomer
    2006-09-10 10:00$3 7700.12SpringBrown
    2006-09-10 12:00$2 3300.24Rocker armBrown
    2006-09-10 11:00$1 4400.12Exhaust valveBrown
    2006-09-11$1 597
    DatePriceTaxItemCustomer
    2006-09-11 10:00$1 5970.24PistonBrown
    Jones$39 816
    DatePriceTaxItemCustomer
    2006-09-11 12:00$9 8700.12Exhaust portJones
    GroupIdSum(price)
    2006-09-08$8 000
    DatePriceTaxItemCustomer
    2006-09-08 10:00$5 0000.24Intake portJones
    2006-09-08 12:00$3 0000.12Valve coverJones
    2006-09-09$2 100
    DatePriceTaxItemCustomer
    2006-09-09 10:00$2 1000,12Engine blockJones
    2006-09-10$8 900
    DatePriceTaxItemCustomer
    2006-09-10 10:00$8 9000.24CamshaftJones
    2006-09-11$20 816
    DatePriceTaxItemCustomer
    2006-09-11 12:00$9 8700.12Exhaust portJones
    2006-09-11 13:00$6 7650.12CrankshaftJones
    2006-09-11 12:00$4 1810.24Rod bearingJones
    Smith$19 484
    DatePriceTaxItemCustomer
    2006-09-10 11:00$6 1000.24Spark plugSmith
    GroupIdSum(price)
    2006-09-06$1 000
    DatePriceTaxItemCustomer
    2006-09-06 09:00$1 0000.24Intake valveSmith
    2006-09-07$3 000
    DatePriceTaxItemCustomer
    2006-09-07 11:00$2 0000.24SpringSmith
    2006-09-07 10:00$1 0000.12Rocker armSmith
    2006-09-09$6 800
    DatePriceTaxItemCustomer
    2006-09-09 12:00$5 5000.12Oil sumpSmith
    2006-09-09 12:00$1 3000.24CoolantSmith
    2006-09-10$6 100
    DatePriceTaxItemCustomer
    2006-09-10 11:00$6 1000.24Spark plugSmith
    2006-09-11$2 584
    DatePriceTaxItemCustomer
    2006-09-11 11:00$2 5840.12Connection rodSmith
    + +## Structured grouping + +Structured grouping is nested grouping over an array of structs or maps. In this case, each array element is treated as a sub-document and may be grouped separately. See the reference for grouping on [multivalue attributes](../reference/querying/grouping-language#multivalue-attributes) for details. It is also possible to [filter the groups](../reference/querying/grouping-language#filtering-groups) so only matching elements are considered. An example could be: + +```js +select (…) | all(group(attributes.value) filter(regex("delivery_method",attributes.key)) each(output(sum(price)) each(output(summary())))) +``` + +## Range grouping + +In the examples above, results are grouped on distinct values, like customer or date. To group on price: + +```js +select (…) | all(group(price) each(each(output(summary())))) +``` + +This gives one group per price. To group on price *ranges*, one could compress the price range. This gives prices in `$0` - `$999` in bucket 0, `$1 000` - `$2 000` in bucket 1 and so on: + +```js +select (…) | all(group(price/1000) each(each(output(summary())))) +``` + +An alternative is using [bucket expressions](../reference/querying/grouping-language#bucket-expressions) - think of a bucket as the range per group. Group on price, make groups have a width of 1000: + +```js +select (…) | all(group(fixedwidth(price,1000)) each(each(output(summary())))) +``` + +Use `predefined` to configure group sizes individually (the two below are equivalent): + +```js +select (…) | +all( + group(predefined(price, + bucket(0,1000), + bucket(1000,2000), + bucket(2000,5000), + bucket(5000,inf))) + each(each(output(summary()))) +) +``` + +This works with strings as well - put Jones and Smith in the second group: + +```js +select (…) | all(group(predefined(customer, bucket(-inf,"Jones"), bucket("Jones", inf))) each(each(output(summary())))) +``` + +... or have Jones in his own group: + +```js +select (…) | all(group(predefined(customer, bucket<-inf,"Jones">, bucket["Jones"], bucket<"Jones", inf>)) each(each(output(summary())))) +``` + +Use decimal numbers in bucket definitions if the expression evaluates to a double or float: + +```js +select (…) +| all( + group(predefined(tax, + bucket(0.0, 0.2), + bucket(0.2, 0.5), + bucket(0.5, inf))) + each( + each(output(summary())) + ) +) +``` + +## Pagination + +Grouping supports [continuation](../reference/querying/grouping-language#continuations) objects that are passed as annotations to the grouping statement. The `continuations` annotation is a list of zero or more continuation strings, returned in the grouping result. For example, given the result: + +```json expandable +{ + "root": { + "children": [ + { + "children": [ + { + "children": [ + { + "fields": { + "count()": 7 + }, + "value": "Jones", + "id": "group:string:Jones", + "relevance": 1.0 + } + ], + "continuation": { + "next": "BGAAABEBEBC", + "prev": "BGAAABEABC" + }, + "id": "grouplist:customer", + "label": "customer", + "relevance": 1.0 + } + ], + "continuation": { + "this": "BGAAABEBCA" + }, + "id": "group:root:0", + "relevance": 1.0 + } + ], + "fields": { + "totalCount": 20 + }, + "id": "toplevel", + "relevance": 1.0 + } +} +``` + +reproduce the same result by passing the *this*\-continuation along the original select: + +```js +select (…) | { 'continuations':['BGAAABEBCA'] }all(…) +``` + +To display the next page of customers, pass the *this*\-continuation of the root group, and the *next* continuation of the customer list: + +```js +select (…) | { 'continuations':['BGAAABEBCA', 'BGAAABEBEBC'] }all(…) +``` + +To display the previous page of customers, pass the *this*\-continuation of the root group, and the *prev* continuation of the customer list: + +```js +select (…) | { 'continuations':['BGAAABEBCA', 'BGAAABEABC'] }all(…) +``` + +The `continuations` annotation is an ordered list of continuation strings. These are combined by replacement so that a continuation given later will replace any shared state with a continuation given before. Also, when using the `continuations` annotation, always pass the *this*\-continuation as its first element. + + +**Note:** + +Continuations work best when the ordering of hits is stable - which can be achieved by using [ranking](../basics/ranking) or [ordering](../reference/querying/grouping-language#order). Adding a tie-breaker might be needed - like [random.match](../reference/ranking/rank-features#random) or a random double value stored in each document - to keep the ordering stable in case of multiple documents that would otherwise get the same rank score or the same value used for ordering. + + +## Expressions + +Instead of just grouping on some attribute value, the `group` clause may contain arbitrarily complex expressions - see `group` in the [grouping reference](../reference/querying/grouping-language) for an exhaustive list. Examples: + +- Select everything. For example, `group("all") each(output(sum(price)))` gives total revenue +- Select the minimum or maximum of sub-expressions +- Addition, subtraction, multiplication, division, and even modulo of sub-expressions +- Bitwise operations on sub-expressions +- Concatenation of the results of sub-expressions + +Sum the prices of purchases on a per-hour-of-day basis: + +```js +select (…) | all(group(mod(div(date,mul(60,60)),24)) each(output(sum(price)))) +``` + +These types of expressions may also be used inside `output` operations, so instead of simply calculating the sum price of the grouped purchases, calculate the sum income after taxes per customer: + +```js +select (…) | all(group(customer) each(output(sum(mul(price,sub(1,tax)))))) +``` + +Note that the validity of an expression depends on the current nesting level. For, while `sum(price)` would be a valid expression for a group of hits, `price` would not. As a general rule, each operator within an expression either applies to a single hit or aggregates values across a group. + +## Search Container API + +As an alternative to a textual representation, one can use the programmatic API to execute grouping requests. This allows multiple grouping requests to run in parallel, and does not collide with the `yql` parameter - example: + +```js expandable +@Override +public Result search(Query query, Execution execution) { + // Create grouping request. + GroupingRequest request = GroupingRequest.newInstance(query); + request.setRootOperation(new AllOperation() + .setGroupBy(new AttributeValue("foo")) + .addChild(new EachOperation() + .addOutput(new CountAggregator().setLabel("count")))); + + // Perform grouping request. + Result result = execution.search(query); + + // Process grouping result. + Group root = request.getResultGroup(result); + GroupList foo = root.getGroupList("foo"); + for (Hit hit : foo) { + Group group = (Group)hit; + Long count = (Long)group.getField("count"); + // TODO: Process group and count. + } + + // Pass results back to calling searcher. + return result; +} +``` + +Refer to the [API documentation](https://javadoc.io/doc/com.yahoo.vespa/container-search/latest/com/yahoo/search/grouping/package-summary) for the complete reference. + +## TopN / Full corpus + +Simple grouping: count the number of documents in each group: + +```js +select * from purchase where true | all( group(customer) each(output(count())) ) +``` + +Two parallel groupings: + +```js +select * from purchase where true | all( + all( + group(customer) each(output(count())) + ) + all( + group(item) each(output(count())) + ) +) +``` + +Only the 1000 best hits will be grouped at each content node. Lower accuracy, but higher speed: + +```js +select * from purchase where true limit 0 | + all( + max(1000) + all( + group(customer) + each(output(count())) + ) + ) +``` + +## Selecting groups + +Do a modulo 3 operation before selecting the group: + +```js +select * from purchase where true limit 0 | + all( + group(price % 3) + each(output(count())) + ) +``` + +Do `price + tax * price` before selecting the group: + +```js +select * from purchase where true limit 0 | all( + group(price + tax * price) + each(output(count())) +) +``` + +## Ordering groups + +Do a modulo 5 operation before selecting the group - the groups are then ordered by their aggregated sum of attribute "tax": + +```js +select * from purchase where true limit 0 | all( + group(price % 5) + order(sum(tax)) + each(output(count())) +) +``` + +Do `price + tax * price` before selecting the group. Ordering is given by the maximum value of attribute "price" in each group: + +```js +select * from purchase where true limit 0 | + all( + group(price + tax * price) + order(max(price)) + each(output(count())) + ) +``` + +Take the average relevance of the groups and multiply it with the number of groups to get a cumulative count: + +```js +select * from purchase where true limit 0 | + all( + group(customer) + order(avg(relevance()) * count()) + each(output(count())) +) +``` + +One can not directly reference an attribute in the order clause, as this: + +```js +select * from purchase where true limit 0 | + all( + group(customer) + order(price * count()) + each(output(count())) +) +``` + +However, one can do this: + +```js +select * from purchase where true limit 0 | all( + group(customer) + order(max(price) * count()) + each(output(count())) +) +``` + +Ordering alphabetically works in a similar way: + +```js +select * from purchase where true limit 0 | all( + group(customer) + order(max(customer)) + each(output(count())) +) +``` + + +**Note:** + +You can control non-ASCII character folding behavior with [unicode collation](../reference/querying/grouping-language#other-expressions). + + +## Collecting aggregates + +Simple grouping to count the number of documents in each group and return the best hit in each group: + +```js +select * from purchase where true limit 0 | + all( + group(customer) + each( + max(1) + each(output(summary())) + ) +) +``` + +Also return the sum of attribute "price": + +```js +select * from purchase where true limit 0 | + all( group(customer) each(max(1) output(count(), sum(price)) each(output(summary()))) ) +``` + +Also, return an XOR of the 64 most significant bits of an MD5 over the concatenation of attributes "customer", "price" and "tax": + +```js +select * from purchase where true limit 0 | + all(group(customer) each(max(1) output(count(), sum(price), xor(md5(cat(customer, price, tax), 64))) each(output(summary())))) +``` + +It is also possible to return quantiles, for instance, the p50 and p90 of the price. + +```js +select * from purchase where true limit 0 | + all(group(customer) each(output(quantiles([0.5,0.9], price)))) +``` + +## Grouping + +Single-level grouping on "customer" attribute, returning at most 5 groups with full hit count as well as the 69 best hits. + +```js +select * from purchase where true limit 0 | all(group(customer) max(5) each(max(69) output(count()) each(output(summary())))) +``` + +Two level grouping on "customer" and "item" attribute: + +```js +select * from purchase where true limit 0 | all(group(customer) max(5) each(output(count()) all(group(item) max(5) each(max(69) output(count()) each(output(summary())))))) +``` + +Three-level grouping on "customer", "item" and "attributes.key(coupon)" attribute: + +```js +select * from purchase where true limit 0 | all(group(customer) max(1) each(output(count()) all(group(item) max(1) each(output(count()) max(1) all(group(attributes.key) max(1) each(output(count()) each(output(summary())))))))) +``` + +As above, but also collect best hit in level 2: + +```js +select * from purchase where true limit 0 | all(group(customer) max(5) each(output(count()) all(group(item) max(5) each(output(count()) all(max(1) each(output(summary()))) all(group(attributes.key) max(5) each(max(69) output(count()) each(output(summary())))))))) +``` + +As above, but also collect best hit in level 1: + +```js +select * from purchase where true limit 0 | all(group(customer) max(5) each(output(count()) all(max(1) each(output(summary()))) all(group(item) max(5) each(output(count()) all(max(1) each(output(summary()))) all(group(attributes.key) max(5) each(max(69) output(count()) each(output(summary())))))))) +``` + +As above, but using different document summaries on each level: + +```js +select * from purchase where true limit 0 | + all( group(customer) max(5) each(output(count()) + all(max(1) each(output(summary(complexsummary)))) + all(group(item) max(5) each(output(count()) + all(max(1) each(output(summary(simplesummary)))) + all(group(price) max(5) each(max(69) output(count()) + each(output(summary(fastsummary)))))) ) +``` + +Deep grouping with counting and hit collection on all levels: + +```js +select * from purchase where true limit 0 | + all( group(customer) max(5) each(output(count()) + all(max(1) each(output(summary()))) + all(group(item) each(output(count()) + all(max(1) each(output(summary()))) + all(group(price) each(output(count()) + all(max(1) each(output(summary())))))))) ) +``` + +## Time and date + +The field (`time` below, but can have any name) must be a [long](../reference/schemas/schemas#long), with second resolution (unix timestamp/epoch). See the [reference](../reference/querying/grouping-language#time-expressions) for all time-functions. + +Group by year: + +```js +select * from purchase where true limit 0 | all(group(time.year(date)) each(output(count()))) +``` + +Group by year, then by month: + +```js +select * from purchase where true limit 0 | + all( group(time.year(date)) each(output(count()) + all(group(time.monthofyear(date)) each(output(count())))) ) +``` + +Groups *today*, *yesterday*, *lastweek*, and *lastmonth* using `predefined` aggregator, and groups each day within each of these separately: + +```js +select * from purchase where true limit 0 | + all( + group( + predefined((now() - date) / (60 * 60 * 24), + bucket(0,1), + bucket(1,2), + bucket(3,7), + bucket(8,31)) + ) + each(output(count()) + all(max(2) each(output(summary()))) + all(group((now() - date) / (60 * 60 * 24)) + each(output(count()) + all(max(2) each(output(summary()))) + ) + ) + ) + ) +``` + +### Timezones in grouping + +The `timezone` query parameter can be used to rewrite each time-function with a timezone offset. See the [reference](../reference/api/query#timezone). Example: + +```bash +$ vespa query "select * from purchase where true | \ + all( group(time.hourofday(date)) each(output(count()))" \ + "timezone=America/Los_Angeles" +``` + +This query selects all documents from `purchase`, groups them by the hour they were made (adjusted to the local time in `America/Los_Angeles`), and counts how many purchases fall into each hour. + +## Geo distance + +Use `geo_distance` to compute the great-circle distance from a [position](../reference/schemas/schemas#position) field to a given point. Append `.km` or `.miles` to select the output unit. The function works on both `position` and `array` fields. For arrays, the minimum distance is returned. See the [reference](../reference/querying/grouping-language#geo_distance). + +Group into 100 km distance buckets from a point: + +```js +select * from purchase where true limit 0 | + all( group(fixedwidth(geo_distance(attribute(location), 63.4, 10.4).km, 100.0)) each(output(count())) ) +``` + +Output the minimum and maximum distance per customer: + +```js +select * from purchase where true limit 0 | + all( group(customer) each(output(min(geo_distance(attribute(location), 63.4, 10.4).km), + max(geo_distance(attribute(location), 63.4, 10.4).km))) ) +``` + +## Counting unique groups + +The `count` aggregator can be applied on a list of groups to determine the number of unique groups without having to explicitly retrieve all groups. Note that this count is an estimate using HyperLogLog++ which is an algorithm for the count-distinct problem. To get an accurate count, one needs to explicitly retrieve all groups and count them in a custom component or in the middle tier calling out to Vespa. This is network intensive and might not be feasible in cases with many unique groups. + +Another use case for this aggregator is counting the number of unique instances matching a given expression. + +Output an estimate of the number of groups, which is equivalent to the number of unique values for attribute "customer": + +```js +select * from purchase where true limit 0 | all( group(customer) each(output(count())) ) +``` + +Output an estimate of the number of unique string lengths for the attribute "item": + +```js +select * from purchase where true limit 0 | all(group(strlen(item)) each(output(count()))) +``` + +Output the sum of the "price" attribute for each group in addition to the accurate count of the overall number of unique groups as the inner each causes all groups to be returned. + +```js +select * from purchase where true limit 0 | all(group(customer) output(count()) each(output(sum(price)))) +``` + +The `max` clause is used to restrict the number of groups returned. The query outputs the sum for the 3 best groups. The `count` clause outputs the estimated number of groups (potentially >3). The `count` becomes an estimate here as the number of groups is limited by max, while in the above example, it's not limited by max: + +```js +select * from purchase where true limit 0 | all(group(customer) max(3) output(count()) each(output(sum(price)))) +``` + +Output the number of top-level groups, and for the 10 best groups, output the number of unique values for attribute "item": + +```js +select * from purchase where true limit 0 | all(group(customer) max(10) output(count()) each(group(item) output(count()))) +``` + +## Counting unique groups - multivalue fields + +A [multivalue](/en/querying/searching-multivalue-fields) attribute is a [weighted set](../reference/schemas/schemas#weightedset), [array](../reference/schemas/schemas#array) or [map](../reference/schemas/schemas#map). Most grouping functions will just handle the elements of multivalued attributes separately, as if they were all individual values in separate documents. If you are grouping over array of struct or maps, scoping will be used to preserve structure. Each entry in the array/map will be treated as a separate sub-document, so documents can be counted twice or more - see [#33646](https://github.com/vespa-engine/vespa/issues/33646) for details. + +This could be solved by adding an additional level of grouping, where you group on a field that is unique for each document (grouping on document ID is not supported). You may then count the unique groups to determine the unique document count: + +```js +select * from purchase where true limit 0 | all(group(customer) each(group(item) output(count()))) +``` + +## Impression forecasting + +Using impression logs for a given user, one can make a function that maps from rank score to the number of impressions an advertisement would get - example: + +```js +Score Integer (# impressions for this user) +0.200 0 +0.210 1 +0.220 2 +0.240 3 +0.320 4 +0.420 5 +0.560 6 +0.700 7 +0.800 8 +0.880 9 +0.920 10 +0.940 11 +0.950 12 +``` + +Storing just the first column (the rank scores, including a rank score for 0 impressions) in an array attribute named *impressions*, the grouping operation [interpolatedlookup(impressions, relevance())](../reference/querying/grouping-language#interpolatedlookup) can be used to figure out how many times a given advertisement would have been shown to this particular user. + +So if the rank score is 0.420 for a specific user/ad/bid combination, then `interpolatedlookup(impressions, relevance())` would return 5.0. If the bid is increased so the rank score gets to 0.490, it would get 5.5 as the return value instead. + +In this context, a count of 5.5 isn't meaningful for the past of a single user, but it gives more information that may be used as a forecast. Summing this across more, different users may then be used to forecast the total of future impressions for the advertisement. + +## Aggregating over all documents + +Grouping is useful for analyzing data. To aggregate over the full document set, create *one* group (which will have *all* documents) by using a constant (here 1) - example: + +```js +select rating from restaurant where true | all(group(1) each(output(avg(price)))) +``` + +Make sure all documents have a value for the given field, if not, NaN is used, and the final result is also NaN: + +```json +{ + "id": "group:long:1", + "relevance": 0.0, + "value": "1", + "fields": { + "avg(rating)": "NaN" + } +} +``` + +## Count fields with NaN + +Count number of documents missing a value for an [attribute](../content/attributes) field (actually, in this example, unset or less than 0, see the bucket expression below). Set a higher query timeout, just in case. Example, analyzing a field called *price*: + +```js +select rating from restaurant where true | all( group(predefined(price, bucket[-inf, 0>, bucket[0, inf>)) each(output(count())) ) +``` + +Example output, counting 2 documents with `-inf` in *rating*: + +```json expandable +"children": [ + { + "id": "group:long_bucket:-9223372036854775808:0", + "relevance": 0.0, + "limits": { + "from": "-9223372036854775808", + "to": "0" + }, + "fields": { + "count()": 2 + } + }, + { + "id": "group:long_bucket:0:9223372036854775807", + "relevance": 0.0, + "limits": { + "from": "0", + "to": "9223372036854775807" + }, + "fields": { + "count()": 8 + } + } +] +``` + +See [analyzing field values](../writing/visiting#analyzing-field-values) for how to export ids of documents meeting given criteria from the full corpus. + +## List fields with NaN + +This is similar to the counting of NaN above, but instead of aggregating the count, for each hit, print a [document summary](../reference/schemas/schemas#document-summary): + +```js +select rating from restaurant where true | + all( group(predefined(price, bucket[-inf, 0>, bucket[0, inf>)) + order(max(price)) + max(1) + each( max(100) each(output(summary()))) ) +``` + +Notes: + +- We are only interested in the first group, so order by `max(price)` and use `max(1)` to get only the first +- Uses `max(100)` in order to limit result set sizes. Read more about [grouping.defaultmaxhits](../reference/api/query#grouping.defaultmaxhits). +- Use the [continuation token](#pagination) to iterate over the result set. + +## Grouping over a Map field + +In the example data, a record looks like: + +```json +{ + "fields": { + "attributes": { + "delivery_method": "Curbside Pickup", + "sales_rep": "Bonnie", + "coupon": "SAVE10" + }, + "customer": "Smith", + "date": 1157526000, + "item": "Intake valve", + "price": "1000", + "tax": "0.24" + } +} +``` + +The map field [schema definition](../reference/schemas/schemas#map) is: + +```js +field attributes type map { + indexing: summary + struct-field key { indexing: attribute } + struct-field value { indexing: attribute } +} +``` + +With this, one can group on both key (`delivery_method`, `sales_rep`, and `coupon`) and values (here counting each value). Try the link to see the output: + +```js +select * from purchase where true limit 0 | + all( + group(attributes.key) + each( group(attributes.value) each(output(count()))) + ) +``` + +A more interesting example is to see the sum per sales rep: + +```js +select * from purchase where true limit 0 | + all( + group(attributes.key) + each( group(attributes.value) each(output(sum(price)))) + ) +``` diff --git a/en/querying/nearest-neighbor-search-guide.md b/en/querying/nearest-neighbor-search-guide.mdx similarity index 59% rename from en/querying/nearest-neighbor-search-guide.md rename to en/querying/nearest-neighbor-search-guide.mdx index e8b36f9420..d0ff249b8a 100644 --- a/en/querying/nearest-neighbor-search-guide.md +++ b/en/querying/nearest-neighbor-search-guide.mdx @@ -1,18 +1,11 @@ --- -# Copyright Vespa.ai. All rights reserved. title: "Vespa nearest neighbor search - a practical guide" -redirect_from: - - /en/nearest-neighbor-search-guide +sidebarTitle: "Nearest neighbor search guide" --- - This guide is a practical introduction to using Vespa nearest neighbor search query operator and how to combine nearest - neighbor search with other Vespa query operators. The guide uses Vespa's [embedding](../rag/embedding.html) - support to map text to vectors. The guide also covers diverse, efficient candidate retrievers - which can be used as candidate retrievers in a [multiphase ranking](../ranking/phased-ranking.html) funnel. +This guide is a practical introduction to using Vespa nearest neighbor search query operator and how to combine nearest neighbor search with other Vespa query operators. The guide uses Vespa's [embedding](../rag/embedding) support to map text to vectors. The guide also covers diverse, efficient candidate retrievers which can be used as candidate retrievers in a [multiphase ranking](../ranking/phased-ranking) funnel. - The guide uses the [Last.fm](http://millionsongdataset.com/lastfm/) tracks dataset for illustration. - Latency numbers mentioned in the guide are obtained from running this guide on a M1. - See also the generic [Vespa performance - a practical guide](../performance/practical-search-performance-guide). +The guide uses the [Last.fm](http://millionsongdataset.com/lastfm/) tracks dataset for illustration. Latency numbers mentioned in the guide are obtained from running this guide on a M1. See also the generic [Vespa performance - a practical guide](../performance/practical-search-performance-guide). This guide covers the following: @@ -28,49 +21,55 @@ This guide covers the following: The guide includes step-by-step instructions on how to reproduce the experiments. -{% include pre-req.html memory="4 GB" extra-reqs=" -
  • Python3 for converting the dataset to Vespa json.
  • -
  • curl to download the dataset.
  • "%} +This guide expects a Vespa deployment with at least **4 GB** memory. + + +**Prerequisites:** + +- Linux, macOS or Windows 10 Pro on x86\_64 or arm64, with [Podman Desktop](https://podman.io/) or [Docker Desktop](https://www.docker.com/products/docker-desktop/) installed, with an engine running. + - Alternatively, start the Podman daemon: + ``` + $ podman machine init --memory 6000 + $ podman machine start + ``` + - See [Docker Containers](/en/operations/self-managed/docker-containers.html) for system limits and other settings. +- For CPUs older than Haswell (2013), see [CPU Support](/en/operations/self-managed/cpu-support). +- Memory: Minimum 4 GB RAM dedicated to Docker/Podman. [Memory recommendations](/en/operations/self-managed/node-setup#memory-settings). +- Disk: Avoid `NO_SPACE` - the vespaengine/vespa container image + headroom for data requires disk space. [Read more](/en/writing/feed-block.html). +- [Homebrew](https://brew.sh/) to install the [Vespa CLI](/en/clients/vespa-cli.html), or download the Vespa CLI from [Github releases](https://github.com/vespa-engine/vespa/releases). +- Python3 for converting the dataset to Vespa json. +- `curl` to download the dataset. + ## Installing vespa-cli -This tutorial uses [Vespa-CLI](../clients/vespa-cli.html), -the official command-line client for Vespa.ai. -It is a single binary without any runtime dependencies and is available for Linux, macOS and Windows: -
    - -
    -$ brew install vespa-cli 
    -
    -
    +This tutorial uses [Vespa-CLI](../clients/vespa-cli), the official command-line client for Vespa.ai. It is a single binary without any runtime dependencies and is available for Linux, macOS and Windows: +```bash +$ brew install vespa-cli +``` ## Dataset -This guide uses the [Last.fm](http://millionsongdataset.com/lastfm/) tracks dataset. -Note that the dataset is released under the following terms: ->Research only, strictly non-commercial. For details, or if you are unsure, please contact Last.fm. ->Also, Last.fm has the right to advertise and refer to any work derived from the dataset. +This guide uses the [Last.fm](http://millionsongdataset.com/lastfm/) tracks dataset. Note that the dataset is released under the following terms: + +> *Research only, strictly non-commercial. For details, or if you are unsure, please contact Last.fm. Also, Last.fm has the right to advertise and refer to any work derived from the dataset.* To download the dataset execute the following (120 MB zip file): -
    - -
    +
    +```bash
     $ curl -L -o lastfm_test.zip \
         http://millionsongdataset.com/sites/default/files/lastfm/lastfm_test.zip 
     $ unzip lastfm_test.zip
    -
    -
    +``` -The downloaded data must be be converted to -[the Vespa JSON feed format](../reference/schemas/document-json-format.html). +The downloaded data must be be converted to [the Vespa JSON feed format](../reference/schemas/document-json-format). -This [python](https://www.python.org/) script can be used to traverse -the dataset files and create a JSONL formatted feed file with Vespa put operations. -The [schema)(schemas.html) is covered in the next section. -The number of unique `tags` is used as a proxy for the popularity of the track. +This [python](https://www.python.org/) script can be used to traverse the dataset files and create a JSONL formatted feed file with Vespa put operations. The [schema](/en/basics/schemas) is covered in the next section. The number of unique `tags` is used as a proxy for the popularity of the track. -
    +`Paste the above into file create-vespa-feed.py`
    +
    +```python expandable
     import os
     import sys
     import json
    @@ -122,10 +121,7 @@ for root, dirs, files in os.walk(directory):
     sorted_files.sort()
     for filename in sorted_files:
         process_file(filename)
    -
    -
    - -
    {% highlight python%}
    +python
     import os
     import sys
     import json
    @@ -177,44 +173,36 @@ for root, dirs, files in os.walk(directory):
     sorted_files.sort()
     for filename in sorted_files:
         process_file(filename)
    -{% endhighlight %}
    +``` + +Process the dataset and convert it to [Vespa JSON document operation](../reference/schemas/document-json-format) format. -Process the dataset and convert it to -[Vespa JSON document operation](../reference/schemas/document-json-format.html) format. -
    - -
    +```bash
     $ python3 create-vespa-feed.py lastfm_test > feed.jsonl
    -
    -
    +``` ## Create a Vespa Application Package -A [Vespa application package](../basics/applications.html) is the set -of configuration files and Java plugins that together define the behavior of a Vespa system: -what functionality to use, the available document types, how ranking will be done, -and how data will be processed during feeding and indexing. -The minimum required files to create the basic search application are `track.sd` and `services.xml`. -Create directories for the configuration files and embedding model: +A [Vespa application package](../basics/applications) is the set of configuration files and Java plugins that together define the behavior of a Vespa system: what functionality to use, the available document types, how ranking will be done, and how data will be processed during feeding and indexing. -
    - -
    +The minimum required files to create the basic search application are `track.sd` and `services.xml`. Create directories for the configuration files and embedding model:
    +
    +```bash
     $ mkdir -p app/schemas; mkdir -p app/search/query-profiles/; mkdir -p app/model
    -
    -
    +``` ### Schema -A [schema](../basics/schemas.html) is a configuration of a document type and additional synthetic fields and [ranking](../basics/ranking.html) -configuration. +A [schema](/en/basics/schemas) is a configuration of a document type and additional synthetic fields and [ranking](../basics/ranking) configuration. For this application, we define a `track` document type. Write the following to `app/schemas/track.sd`: -
    +`Paste the above into file app/schemas/track.sd`
    +
    +```js expandable
     schema track {
     
         document track {
    @@ -233,7 +221,7 @@ schema track {
                 indexing: summary | index
             }
     
    -        field tags type weightedset<string> {
    +        field tags type weightedset {
                 indexing: summary | attribute
                 attribute: fast-search
             }
    @@ -245,7 +233,7 @@ schema track {
             }
         }
     
    -    field embedding type tensor<float>(x[384]) {
    +    field embedding type tensor(x[384]) {
                 indexing: input title | embed e5 |attribute | index
                 attribute {
                     distance-metric: angular
    @@ -284,8 +272,8 @@ schema track {
             match-features: distance(field, embedding)
     
             inputs {
    -            query(q)  tensor<float>(x[384])
    -            query(q1) tensor<float>(x[384])
    +            query(q)  tensor(x[384])
    +            query(q1) tensor(x[384])
             } 
     
             first-phase {
    @@ -325,10 +313,7 @@ schema track {
             }
         }
     }
    -
    -
    - -
    +sd
     schema track {
     
         document track {
    @@ -347,7 +332,7 @@ schema track {
                 indexing: summary | index
             }
     
    -        field tags type weightedset<string> {
    +        field tags type weightedset {
                 indexing: summary | attribute
                 attribute: fast-search
             }
    @@ -359,7 +344,7 @@ schema track {
             }
         }
     
    -    field embedding type tensor<float>(x[384]) {
    +    field embedding type tensor(x[384]) {
                 indexing: input title | embed e5 |attribute | index
                 attribute {
                     distance-metric: angular
    @@ -397,8 +382,8 @@ schema track {
             match-features: distance(field, embedding)
     
             inputs {
    -            query(q)  tensor<float>(x[384])
    -            query(q1) tensor<float>(x[384])
    +            query(q)  tensor(x[384])
    +            query(q1) tensor(x[384])
             }
     
             first-phase {
    @@ -438,18 +423,14 @@ schema track {
             }
         }
     }
    -
    +``` -This document schema is explained in the [practical search performance guide](../performance/practical-search-performance-guide), -the addition is the `embedding` field which is defined as a synthetic field outside of the document. This -field is populated by Vespa's [embedding](../rag/embedding.html) functionality. Using the [E5](https://huggingface.co/intfloat/e5-small-v2) -text embedding model (described in this [blog post](https://blog.vespa.ai/enhancing-vespas-embedding-management-capabilities/)). +This document schema is explained in the [practical search performance guide](../performance/practical-search-performance-guide), the addition is the `embedding` field which is defined as a synthetic field outside of the document. This field is populated by Vespa's [embedding](../rag/embedding) functionality. Using the [E5](https://huggingface.co/intfloat/e5-small-v2) text embedding model (described in this [blog post](https://blog.vespa.ai/enhancing-vespas-embedding-management-capabilities/)). -Note that the `closeness` rank-profile defines two -query input tensors using [inputs](../reference/schemas/schemas.html#inputs). +Note that the `closeness` rank-profile defines two query input tensors using [inputs](../reference/schemas/schemas#inputs). -
    -field embedding type tensor<float>(x[384]) {
    +```js
    +field embedding type tensor(x[384]) {
         indexing: input title | embed e5 | attribute | index
         attribute {
             distance-metric: angular
    @@ -461,165 +442,138 @@ field embedding type tensor<float>(x[384]) {
             }
         }
      }
    -
    +``` -See [Approximate Nearest Neighbor Search using HNSW Index](approximate-nn-hnsw) -for an introduction to `HNSW` and the `HNSW` tuning parameters. +See [Approximate Nearest Neighbor Search using HNSW Index](/en/querying/approximate-nn-hnsw) for an introduction to `HNSW` and the `HNSW` tuning parameters. ### Services Specification -The [services.xml](../reference/applications/services/services.html) defines the services that make up -the Vespa application — which services to run and how many nodes per service. -Write the following to `app/services.xml`: - -
    -<?xml version="1.0" encoding="UTF-8"?>
    -<services version="1.0">
    -
    -    <container id="default" version="1.0">
    -        <search/>
    -        <document-api/>
    -        <component id="e5" type="hugging-face-embedder">
    -            <transformer-model path="model/e5-small-v2-int8.onnx"/>
    -            <tokenizer-model path="model/tokenizer.json"/>
    -        </component>
    -    </container>
    -
    -    <content id="tracks" version="1.0">
    -        <engine>
    -            <proton>
    -                <tuning>
    -                    <searchnode>
    -                        <requestthreads>
    -                            <persearch>4</persearch>
    -                        </requestthreads>
    -                    </searchnode>
    -                </tuning>
    -            </proton>
    -        </engine>
    -        <redundancy>1</redundancy>
    -        <documents>
    -            <document type="track" mode="index"></document>
    -        </documents>
    -        <nodes>
    -            <node distribution-key="0" hostalias="node1"></node>
    -        </nodes>
    -    </content>
    -</services>
    -
    - -The default [query profile](query-profiles.html) can be used to override -default query api settings for all queries. - -The following enables [presentation.timing](../reference/api/query.html#presentation.timing) and -renders `weightedset` fields as a JSON maps. - -
    -<query-profile id="default">
    -    <field name="presentation.timing">true</field>
    -    <field name="renderer.json.jsonWsets">true</field>
    -</query-profile>
    -
    +The [services.xml](../reference/applications/services/services) defines the services that make up the Vespa application — which services to run and how many nodes per service. Write the following to `app/services.xml`: + +```js expandable + + + + + + + + + + + + + + + + + + + 4 + + + + + + 1 + + + + + + + + +``` + +`Paste the above into file app/services.xml` + +The default [query profile](/en/querying/query-profiles) can be used to override default query api settings for all queries. + +The following enables [presentation.timing](../reference/api/query#presentation.timing) and renders `weightedset` fields as a JSON maps. + +```xml + + true + true + +``` + +`Paste the above into file app/search/query-profiles/default.xml` The final step is to download embedding model files -
    - -
    +```bash
     $ curl -L -o app/model/e5-small-v2-int8.onnx \
         https://github.com/vespa-engine/sample-apps/raw/master/examples/model-exporting/model/e5-small-v2-int8.onnx
     $ curl -L -o app/model/tokenizer.json \
         https://github.com/vespa-engine/sample-apps/raw/master/examples/model-exporting/model/tokenizer.json
    -
    -
    +``` ## Deploy the application package -The application package can now be deployed to a running Vespa instance. -See also the [Vespa quick start guide](../basics/deploy-an-application-local.html). +The application package can now be deployed to a running Vespa instance. See also the [Vespa quick start guide](../basics/deploy-an-application-local). Start the Vespa container image using Docker: -
    - -
    +```bash
     $ docker run --detach --name vespa --hostname vespa-container \
       --publish 8080:8080 --publish 19071:19071 \
       vespaengine/vespa
    -
    -
    +``` -Starting the container can take a short while. Before continuing, make sure -that the configuration service is running by using `vespa status deploy`. +Starting the container can take a short while. Before continuing, make sure that the configuration service is running by using `vespa status deploy`. -
    - -
    +
    +```bash
     $ vespa config set target local
    -$ vespa status deploy --wait 300 
    -
    -
    +$ vespa status deploy --wait 300 +``` Once ready, deploy the application using `vespa deploy`: -
    - -
    +```bash
     $ vespa deploy --wait 300 app
    -
    -
    +``` ## Index the dataset -Feed the dataset. During indexing, Vespa will invoke the embedding model (which is relatively computationally expensive), -so feeding and indexing this dataset takes about 180 seconds on a M1 laptop (535 inserts/s). +Feed the dataset. During indexing, Vespa will invoke the embedding model (which is relatively computationally expensive), so feeding and indexing this dataset takes about 180 seconds on a M1 laptop (535 inserts/s). + -
    - -
    +```bash
     $ vespa feed -t http://localhost:8080 feed.jsonl
    -
    -
    +``` ## Free-text search using Vespa weakAnd -The following sections uses the Vespa [query api](../reference/api/query.html) and -formulate queries using Vespa [query language](query-language.html). The examples uses the -[vespa-cli](../clients/vespa-cli.html) command which supports running queries. -The CLI uses the [Vespa query api](query-api.html). -Use `vespa query -v` to see the curl equivalent: +The following sections uses the Vespa [query api](../reference/api/query) and formulate queries using Vespa [query language](/en/querying/query-language). The examples uses the [vespa-cli](../clients/vespa-cli) command which supports running queries. -
    +The CLI uses the [Vespa query api](/en/querying/query-api). Use `vespa query -v` to see the curl equivalent:
    +
    +```bash
     $ vespa query -v 'yql=select ..'
    -
    +``` + +The first example is searching and ranking using the `bm25` rank profile defined in the schema. It uses the [bm25](../ranking/bm25) rank feature as the `first-phase` relevance score: -The first example is searching and ranking using the `bm25` rank profile defined in the schema. -It uses the [bm25](../ranking/bm25.html) rank feature as the `first-phase` relevance score: -
    - -
    +```bash
     $ vespa query \
         'yql=select artist, title, track_id from track where userQuery()' \
         'query=total eclipse of the heart' \
         'hits=1' \
         'type=all' \
         'ranking=bm25'
    -
    -
    +``` -This query combines YQL [userQuery()](../reference/querying/yql.html#userquery) -with Vespa's [simple query language](../reference/querying/simple-query-language.html). -The [query type](../reference/api/query.html#model.type) is -using `all`, requiring that all the terms match. +This query combines YQL [userQuery()](../reference/querying/yql#userquery) with Vespa's [simple query language](../reference/querying/simple-query-language). The [query type](../reference/api/query#model.type) is using `all`, requiring that all the terms match. -The above query example searches for *total AND eclipse AND of AND the AND heart* -in the `default` fieldset, which in the schema includes the `title` and `artist` fields. +The above query example searches for *total AND eclipse AND of AND the AND heart* in the `default` fieldset, which in the schema includes the `title` and `artist` fields. -The [result](../reference/querying/default-result-format.html) -for the above query will look something like this: +The [result](/en/reference/querying/default-result-format) for the above query will look something like this: -
    {% highlight json%}
    +```json expandable
     {
         "timing": {
             "querytime": 0.007,
    @@ -654,108 +608,77 @@ for the above query will look something like this:
             ]
         }
     }
    -{% endhighlight %}
    +``` -This query only matched one document because the query terms were `AND`ed. -We can change matching to use `type=any` instead of the default `type=all`. See -[supported query types](../reference/api/query.html#model.type). +This query only matched one document because the query terms were `AND`ed. We can change matching to use `type=any` instead of the default `type=all`. See [supported query types](../reference/api/query#model.type). -
    - -
    +```bash
     $ vespa query \
         'yql=select artist, title, track_id from track where userQuery()' \
         'query=total eclipse of the heart' \
         'hits=1' \
         'ranking=bm25' \
         'type=any'
    -
    -
    +``` -Now, the query matches 24,053 documents and is considerably slower than the previous query. -Comparing `querytime` of these two query examples, the one which matches the most documents have highest `querytime`. -In worst case, the search query matches all documents. +Now, the query matches 24,053 documents and is considerably slower than the previous query. Comparing `querytime` of these two query examples, the one which matches the most documents have highest `querytime`. In worst case, the search query matches all documents. -Query matching performance is greatly impacted by the number of documents that matches the query specification. -Type `any` queries requires more compute resources than type `all`. +Query matching performance is greatly impacted by the number of documents that matches the query specification. Type `any` queries requires more compute resources than type `all`. -There is an optimization available for `type=any` queries, using -the `weakAnd` query operator which implements the WAND algorithm. -See the [using wand with Vespa](../ranking/wand.html) guide for more details. +There is an optimization available for `type=any` queries, using the `weakAnd` query operator which implements the WAND algorithm. See the [using wand with Vespa](../ranking/wand) guide for more details. -Run the same query, but instead of `type=any` use `type=weakAnd`, -see [supported query types](../reference/api/query.html#model.type): +Run the same query, but instead of `type=any` use `type=weakAnd`, see [supported query types](../reference/api/query#model.type): -
    - -
    +
    +```bash
     $ vespa query \
         'yql=select artist, title, track_id from track where userQuery()' \
         'query=total eclipse of the heart' \
         'hits=1' \
         'ranking=bm25' \
         'type=weakAnd'
    -
    -
    - -Compared to the type `any` query which fully ranked 24,053 documents, -the query only expose about 3,600 documents to the `first-phase` ranking expression. -Also notice that the faster search returns the same document at the first position. +``` +Compared to the type `any` query which fully ranked 24,053 documents, the query only expose about 3,600 documents to the `first-phase` ranking expression. Also notice that the faster search returns the same document at the first position. -
    - -
    +```bash
     $ vespa query \
         'yql=select artist, title, track_id from track where userQuery()' \
         'query="total eclipse of the heart"' \
         'hits=1' \
         'ranking=bm25' \
         'type=weakAnd'
    -
    -
    -In this case, the query input *"total eclipse of the heart"* is parsed as a phrase query, and the -search only finds 1 document matching the exact phrase. +``` + +In this case, the query input *"total eclipse of the heart"* is parsed as a phrase query, and the search only finds 1 document matching the exact phrase. ## Maximum Inner Product Search using Vespa WAND -The previous section introduced the `weakAnd` query operator which integrates -with [linguistic processing](../linguistics/linguistics.html) and string matching using `match: text`. +The previous section introduced the `weakAnd` query operator which integrates with [linguistic processing](../linguistics/linguistics) and string matching using `match: text`. -The following examples uses the -[wand()](../reference/querying/yql.html#wand) query operator. -The `wand` query operator calculates the maximum inner product -between the sparse query and document feature integer -weights. The inner product ranking score calculated by the `wand` query operator -can be used in a ranking expression by the [rawScore(name)](../reference/ranking/rank-features.html#match-operator-scores) -rank feature. +The following examples uses the [wand()](../reference/querying/yql#wand) query operator. The `wand` query operator calculates the maximum inner product between the sparse query and document feature integer weights. The inner product ranking score calculated by the `wand` query operator can be used in a ranking expression by the [rawScore(name)](../reference/ranking/rank-features#match-operator-scores) rank feature. -
    +```js
     rank-profile tags {
         first-phase {
             expression: rawScore(tags)
         }
     }
    -
    +``` -This query searches the track document type using a learned sparse *userProfile* representation, -performing a maximum inner product search over the `tags` weightedset field. +This query searches the track document type using a learned sparse *userProfile* representation, performing a maximum inner product search over the `tags` weightedset field. -
    - -
    +```bash
     $ vespa query \
         'yql=select track_id, title, artist from track where {totalTargetHits:10}wand(tags, @userProfile)' \
         'userProfile={"pop":1, "love songs":1,"romantic":10, "80s":20 }' \
         'hits=2' \
         'ranking=tags'
    -
    -
    -The query asks for 2 hits to be returned, and uses the `tags` rank profile. -The [result](../reference/default-result-format.html) -for the above query will look something like this: +``` + +The query asks for 2 hits to be returned, and uses the `tags` rank profile. The [result](/en/reference/querying/default-result-format) for the above query will look something like this: -
    {% highlight json%}
    +```json expandable
     {
         "timing": {
             "querytime": 0.051000000000000004,
    @@ -800,55 +723,37 @@ for the above query will look something like this:
             ]
         }
     }
    +```
     
    -{% endhighlight %}
    -The `wand` query operator exposed a total of about 60 documents to the `first-phase` ranking which -uses the `rawScore(tag)` rank-feature directly, so the `relevancy` is the -result of the sparse dot product between the sparse user profile and the document tags. +The `wand` query operator exposed a total of about 60 documents to the `first-phase` ranking which uses the `rawScore(tag)` rank-feature directly, so the `relevancy` is the result of the sparse dot product between the sparse user profile and the document tags. -The `wand` query operator is safe, meaning, it returns the same top-k results as -the brute-force `dotProduct` query operator. `wand` is a type of query operator which -performs matching and ranking interleaved and skipping documents -which cannot compete into the final top-k results. -See the [using wand with Vespa](../ranking/wand.html) guide for more details on -using `wand` and `weakAnd` query operators. +The `wand` query operator is safe, meaning, it returns the same top-k results as the brute-force `dotProduct` query operator. `wand` is a type of query operator which performs matching and ranking interleaved and skipping documents which cannot compete into the final top-k results. See the [using wand with Vespa](../ranking/wand) guide for more details on using `wand` and `weakAnd` query operators. ## Exact nearest neighbor search -Vespa's nearest neighbor search operator supports doing exact brute force nearest neighbor search -using dense representations. The first query example uses [exact nearest neighbor search](nearest-neighbor-search) -and Vespa embed functionality: -
    - -
    +Vespa's nearest neighbor search operator supports doing exact brute force nearest neighbor search using dense representations. The first query example uses [exact nearest neighbor search](/en/querying/nearest-neighbor-search) and Vespa embed functionality: 
    +
    +```bash
     $ vespa query \
         'yql=select title, artist from track where {approximate:false,totalTargetHits:10}nearestNeighbor(embedding,q)' \
         'hits=1' \
         'ranking=closeness' \
         'input.query(q)=embed(e5, "Total Eclipse Of The Heart")'
    -
    -
    +``` Query breakdown: -- Search for a ten (`totalTargetHits:10`) nearest neighbors of the `query(q)` query tensor over the `embedding` -document tensor field. -- The annotation `approximate:false` tells Vespa to perform exact search. -- The `hits` parameter controls how many results are returned in the response. Number of `hits` -requested does not impact `totalTargetHits`. -- `ranking=closeness` tells Vespa which [rank-profile](../basics/ranking.html) to score documents. One must -specify how to *rank* the `totalTargetHits` documents retrieved and exposed to `first-phase` ranking expression -in the `rank-profile`. -- `input.query(q)` is the query vector produced by the [embedder](../rag/embedding.html#embedding-a-query-text). +- Search for a ten (`totalTargetHits:10`) nearest neighbors of the `query(q)` query tensor over the `embedding` document tensor field. +- The annotation `approximate:false` tells Vespa to perform exact search. +- The `hits` parameter controls how many results are returned in the response. Number of `hits` requested does not impact `totalTargetHits`. +- `ranking=closeness` tells Vespa which [rank-profile](../basics/ranking) to score documents. One must specify how to *rank* the `totalTargetHits` documents retrieved and exposed to `first-phase` ranking expression in the `rank-profile`. +- `input.query(q)` is the query vector produced by the [embedder](../rag/embedding#embedding-a-query-text). -Not specifying [ranking](../reference/api/query.html#ranking.profile) will cause -Vespa to use [nativeRank](../ranking/nativerank.html) which does not use the vector similarity, causing -results to be randomly sorted. +Not specifying [ranking](../reference/api/query#ranking.profile) will cause Vespa to use [nativeRank](../ranking/nativerank) which does not use the vector similarity, causing results to be randomly sorted. -The above exact nearest neighbor search will return the following -[result](../reference/querying/default-result-format.html): +The above exact nearest neighbor search will return the following [result](/en/reference/querying/default-result-format): -
    {% highlight json%}
    +```json expandable
     {
         "timing": {
             "querytime": 0.012,
    @@ -885,30 +790,23 @@ The above exact nearest neighbor search will return the following
             ]
         }
     }
    -{% endhighlight %}
    +``` -The exact search takes approximately 14ms, performing 95,666 distance calculations. -A total of about 101 documents were exposed to the first-phase ranking during the search as can be seen from -`totalCount`. The `relevance` is the result of the `rank-profile` scoring. +The exact search takes approximately 14ms, performing 95,666 distance calculations. A total of about 101 documents were exposed to the first-phase ranking during the search as can be seen from `totalCount`. The `relevance` is the result of the `rank-profile` scoring. -It is possible to reduce search latency of the exact search by throwing more CPU resources at it. -Changing the rank-profile to `closeness-t4` makes Vespa use four threads per query: +It is possible to reduce search latency of the exact search by throwing more CPU resources at it. Changing the rank-profile to `closeness-t4` makes Vespa use four threads per query: -
    - -
    +```bash
     $ vespa query \
         'yql=select title, artist from track where {approximate:false,totalTargetHits:10}nearestNeighbor(embedding,q)' \
         'hits=1' \
         'ranking=closeness-t4' \
         'input.query(q)=embed(e5, "Total Eclipse Of The Heart")'
    -
    -
    +``` -Now, the exact search latency is reduced by using more threads, -see [multithreaded searching and ranking](../performance/practical-search-performance-guide#multithreaded-search-and-ranking) -for more on this topic. -
    {% highlight json%}
    +Now, the exact search latency is reduced by using more threads, see [multithreaded searching and ranking](../performance/practical-search-performance-guide#multithreaded-search-and-ranking) for more on this topic.
    +
    +```json
     {
         "timing": {
             "querytime": 0.008,
    @@ -916,32 +814,25 @@ for more on this topic.
             "searchtime": 0.008
         }
     }
    -{% endhighlight %}
    +``` ## Approximate nearest neighbor search -This section covers using the faster, but approximate, nearest neighbor search. The -`track` schema's `embedding` field has the `index` property, which means Vespa builds a -`HNSW` index to support fast, approximate vector search. See -[Approximate Nearest Neighbor Search using HNSW Index](approximate-nn-hnsw) -for an introduction to `HNSW` and the tuning parameters. - -The default query behavior is using `approximate:true` when the `embedding` -field has `index`: - -
    - -
    +
    +This section covers using the faster, but approximate, nearest neighbor search. The `track` schema's `embedding` field has the `index` property,  which means Vespa builds a  `HNSW` index to support fast, approximate vector search. See [Approximate Nearest Neighbor Search using HNSW Index](/en/querying/approximate-nn-hnsw) for an introduction to `HNSW` and the tuning parameters.
    +
    +The default query behavior is using `approximate:true` when the `embedding` field has `index`:
    +
    +```bash
     $ vespa query \
         'yql=select title, artist from track where {totalTargetHits:10,hnsw.exploreAdditionalHits:20}nearestNeighbor(embedding,q)' \
         'hits=1' \
         'ranking=closeness' \
         'input.query(q)=embed(e5, "Total Eclipse Of The Heart")'
    -
    -
    +``` Which returns the following response: -
    {% highlight json%}
    +```json expandable
     {
         "timing": {
             "querytime": 0.004,
    @@ -975,49 +866,33 @@ Which returns the following response:
             ]
         }
     }
    -{% endhighlight %}
    +``` -Now, the query is faster, and also uses less resources during the search. -To get latency down to 20 ms with the exact search one had to use 4 matching threads. In this case the -result latency is down to 4ms with a single matching thread. -For this query example, the approximate search returned the exact same top-1 hit and there was -no accuracy loss for the top-1 position. Note that the overall query time is dominated -by the `embed` inference. +Now, the query is faster, and also uses less resources during the search. To get latency down to 20 ms with the exact search one had to use 4 matching threads. In this case the result latency is down to 4ms with a single matching thread. For this query example, the approximate search returned the exact same top-1 hit and there was no accuracy loss for the top-1 position. Note that the overall query time is dominated by the `embed` inference. A few key differences between `exact` and `approximate` neighbor search: -- `totalCount` is different, when using the approximate version, Vespa exposes exactly `targethits` to the -configurable `first-phase` rank expression in the chosen `rank-profile`. - The exact search is using a scoring heap during evaluation (chunked distance calculations), and documents which at some time -were put on the top-k heap are exposed to first phase ranking. +- `totalCount` is different, when using the approximate version, Vespa exposes exactly `targethits` to the configurable `first-phase` rank expression in the chosen `rank-profile`. The exact search is using a scoring heap during evaluation (chunked distance calculations), and documents which at some time were put on the top-k heap are exposed to first phase ranking. -- The search is approximate and might not return the exact top 10 closest vectors as with exact search. This -is a complex tradeoff between accuracy, query performance , and memory usage. -See [Billion-scale vector search with Vespa - part two](https://blog.vespa.ai/billion-scale-knn-part-two/) -for a deep-dive into these trade-offs. +- The search is approximate and might not return the exact top 10 closest vectors as with exact search. This is a complex tradeoff between accuracy, query performance , and memory usage. See [Billion-scale vector search with Vespa - part two](https://blog.vespa.ai/billion-scale-knn-part-two/) for a deep-dive into these trade-offs. -With the support for setting `approximate:false|true` a developer can quantify accuracy loss by comparing the -results of exact nearest neighbor search with the results of the approximate search. -By doing so, developers can quantify the recall@k or overlap@k, -and find the right balance between search performance and accuracy. Increasing `hnsw.exploreAdditionalHits` -improves accuracy (recall@k) at the cost of a slower query. +With the support for setting `approximate:false|true` a developer can quantify accuracy loss by comparing the results of exact nearest neighbor search with the results of the approximate search. By doing so, developers can quantify the recall@k or overlap@k, and find the right balance between search performance and accuracy. Increasing `hnsw.exploreAdditionalHits` improves accuracy (recall@k) at the cost of a slower query. ## Combining approximate nearest neighbor search with query filters -Vespa allows combining the search for nearest neighbors to be constrained by regular query filters. -In this query example the `title` field must contain the term `heart`: -
    - -
    +Vespa allows combining the search for nearest neighbors to be constrained by regular query filters. In this query example the `title` field must contain the term `heart`:
    +
    +```bash
     $ vespa query \
         'yql=select title, artist from track where {totalTargetHits:10}nearestNeighbor(embedding,q) and title contains "heart"' \
         'hits=2' \
         'ranking=closeness' \
         'input.query(q)=embed(e5, "Total Eclipse Of The Heart")'
    -
    -
    +``` + Which returns the following response: -
    {% highlight json%}
    +
    +```json expandable
     {
         "timing": {
             "querytime": 0.005,
    @@ -1066,120 +941,81 @@ Which returns the following response:
             ]
         }
     }
    -{% endhighlight %}
    -The query term `heart` does in this case not impact the ordering (ranking) of the results, as the -rank-profile used only uses the vector similarity closeness. +``` + +The query term `heart` does in this case not impact the ordering (ranking) of the results, as the rank-profile used only uses the vector similarity closeness. -When using filtering, it is important for performance reasons that the fields that are included in the filters have -been defined with `index` or `attribute:fast-search`. -See [searching attribute fields](../performance/practical-search-performance-guide#searching-attribute-fields). +When using filtering, it is important for performance reasons that the fields that are included in the filters have been defined with `index` or `attribute:fast-search`. See [searching attribute fields](../performance/practical-search-performance-guide#searching-attribute-fields). -The optimal performance for combining nearestNeighbor search with filtering, where the query term(s) does not influence ranking, is achieved -using `rank: filter` in the schema (See [ranking expressions](../reference/ranking/ranking-expressions.html)): +The optimal performance for combining nearestNeighbor search with filtering, where the query term(s) does not influence ranking, is achieved using `rank: filter` in the schema (See [ranking expressions](../reference/ranking/ranking-expressions)): -
    +```js
     field popularity type int {
         indexing: summary | attribute
         rank: filter
         attribute: fast-search
     }
    -
    +``` -Matching against the popularity field does not influence ranking, and Vespa can use the most efficient posting -list representation. Note that one can still access the value of -the `popularity` attribute in [ranking expressions](../ranking/ranking-expressions-features.html). +Matching against the popularity field does not influence ranking, and Vespa can use the most efficient posting list representation. Note that one can still access the value of the `popularity` attribute in [ranking expressions](../ranking/ranking-expressions-features). -
    +```js
     rank-profile popularity {
         first-phase {
             expression: attribute(popularity)
         }
     }
    -
    +``` +In the following example, since the `title` field does not have `rank: filter` one can instead flag that the term should not be used by any ranking expression by using the [`ranked` query annotation](../reference/querying/yql#ranked). -In the following example, since the `title` field does not have `rank: filter` one can instead -flag that the term should not be used by any ranking expression by -using the [`ranked` query annotation](../reference/querying/yql.html#ranked). +The following disables [term based ranking](../reference/querying/yql#ranked) and the matching against the `title` field can use the most efficient posting list representation. -The following disables [term based ranking](../reference/querying/yql.html#ranked) and -the matching against the `title` field can use the most efficient posting list representation. - -
    - -
    +```bash
     $ vespa query \
         'yql=select title, artist from track where {totalTargetHits:10}nearestNeighbor(embedding,q) and title contains ({ranked:false}"heart")' \
         'hits=2' \
         'ranking=closeness' \
         'input.query(q)=embed(e5, "Total Eclipse Of The Heart")'
    -
    -
    +``` -In the previous examples, since the rank-profile did only use the -[closeness](../reference/ranking/rank-features.html#closeness(dimension,name)) rank feature, -the matching would not impact the score anyway. +In the previous examples, since the rank-profile did only use the [closeness](../reference/ranking/rank-features#closeness(dimension,name)) rank feature, the matching would not impact the score anyway. -Vespa also allows combining the [nearestNeighbor](../reference/querying/yql.html#nearestneighbor) query operator -with any other Vespa query operator. +Vespa also allows combining the [nearestNeighbor](../reference/querying/yql#nearestneighbor) query operator with any other Vespa query operator. -
    - -
    +```bash
     $ vespa query \
         'yql=select title, popularity, artist from track where {totalTargetHits:10}nearestNeighbor(embedding,q) and popularity > 20 and artist contains "Bonnie Tyler"' \
         'hits=2' \
         'ranking=closeness' \
         'input.query(q)=embed(e5, "Total Eclipse Of The Heart")'
    -
    -
    +``` This query example restricts the search to tracks by `Bonnie Tyler` with `popularity > 20`. ### Strict filters and distant neighbors -When combining nearest neighbor search with strict filters that match less than 2 percent of the total number of documents, -Vespa will instead of searching the HNSW graph, constrained by the filter, fall back to using exact nearest neighbor search. -See [Controlling filter behavior](#controlling-filter-behavior) for how to adjust the threshold for which strategy that is used. -Since exact search may expose more than `totalTargetHits` hits to the `first-phase` ranking expression, -users will observe that `totalCount` increases and is higher than `totalTargetHits` when falling back to exact search. -This can be seen in the previous examples. -When using exact search with filters, the search can also use multiple threads to evaluate the query, which -helps reduce the latency impact. - -With strict filters that remove many hits, the hits (nearest neighbors) might not be *near* in the embedding space, but *far*, -or *distant* neighbors. Technically, all document vectors are a neighbor of the query vector, -but with a varying distance. - -With restrictive filters, the neighbors that are returned might be of low quality (far distance). -One way to combat this effect is to use the [distanceThreshold](../reference/querying/yql.html#distancethreshold) -query annotation parameter of the `nearestNeighbor` query operator. -The value of the `distance` depends on the [distance-metric](../reference/schemas/schemas.html#distance-metric) used. -By adding the [distance(field,embedding)](../reference/ranking/rank-features.html#distance(dimension,name)) rank-feature to -the `match-features` of the `closeness` rank-profiles, it is possible to analyze what distance -could be considered too far. -See [match-features reference](../reference/schemas/schemas.html#match-features). - - -Note that distance of 0 is perfect, while distance of 1 is distant. The `distanceThreshold` -remove hits that have a **higher** `distance(field, embedding)` than `distanceThreshold`. The -`distanceThreshold` is applied regardless of performing exact or approximate search. + +When combining nearest neighbor search with strict filters that match less than 2 percent of the total number of documents, Vespa will instead of searching the HNSW graph, constrained by the filter, fall back to using exact nearest neighbor search. See [Controlling filter behavior](#controlling-filter-behavior) for how to adjust the threshold for which strategy that is used. Since exact search may expose more than `totalTargetHits` hits to the `first-phase` ranking expression, users will observe that `totalCount` increases and is higher than `totalTargetHits` when falling back to exact search. This can be seen in the previous examples. When using exact search with filters, the search can also use multiple threads to evaluate the query, which helps reduce the latency impact. + +With strict filters that remove many hits, the hits (nearest neighbors) might not be *near* in the embedding space, but *far*, or *distant* neighbors. Technically, all document vectors are a neighbor of the query vector, but with a varying distance. + +With restrictive filters, the neighbors that are returned might be of low quality (far distance). One way to combat this effect is to use the [distanceThreshold](../reference/querying/yql#distancethreshold) query annotation parameter of the `nearestNeighbor` query operator. The value of the `distance` depends on the [distance-metric](../reference/schemas/schemas#distance-metric) used. By adding the [distance(field,embedding)](../reference/ranking/rank-features#distance(dimension,name)) rank-feature to the `match-features` of the `closeness` rank-profiles, it is possible to analyze what distance could be considered too far. See [match-features reference](../reference/schemas/schemas#match-features). + +Note that distance of 0 is perfect, while distance of 1 is distant. The `distanceThreshold` remove hits that have a **higher** `distance(field, embedding)` than `distanceThreshold`. The `distanceThreshold` is applied regardless of performing exact or approximate search. The following query with a restrictive filter on popularity is used for illustration: -
    - -
    +```bash
     $ vespa query \
         'yql=select title, popularity, artist from track where {totalTargetHits:10}nearestNeighbor(embedding,q) and popularity > 80' \
         'hits=2' \
         'ranking=closeness-t4' \
         'input.query(q)=embed(e5, "Total Eclipse Of The Heart")'
    -
    -
    +``` The above query returns -
    {% highlight json%}
    +```json expandable
     {
         "timing": {
             "querytime": 0.008,
    @@ -1230,23 +1066,19 @@ The above query returns
             ]
         }
     }
    -{% endhighlight %}
    +``` -By using a `distanceTreshold` of 0.2, the `Eclipse` track will be removed from the result -because it's `distance(field, embedding)` is close to 0.5. +By using a `distanceTreshold` of 0.2, the `Eclipse` track will be removed from the result because it's `distance(field, embedding)` is close to 0.5. -
    - -
    +```js
     $ vespa query \
         'yql=select title, popularity, artist from track where {distanceThreshold:0.2,totalTargetHits:10}nearestNeighbor(embedding,q) and popularity > 80' \
         'hits=2' \
         'ranking=closeness' \
         'input.query(q)=embed(e5, "Total Eclipse Of The Heart")'
    -
    -
    +``` -
    {% highlight json%}
    +```json expandable
     {
         "timing": {
             "querytime": 0.008,
    @@ -1284,33 +1116,17 @@ $ vespa query \
             ]
         }
     }
    -{% endhighlight %}
    - -Setting appropriate `distanceThreshold` is best handled by supervised learning as -the distance threshold should be calibrated based on the query complexity -and possibly also the feature distributions of the returned top-k hits. -Having the `distance` rank feature returned as `match-features`, -enables post-processing of the result using a custom -[re-ranking/filtering searcher](../ranking/reranking-in-searcher). -The post-processing searcher can analyze the score distributions of the returned top-k hits -(using the features returned with `match-features`), -remove low scoring hits before presenting the result to the end user, -or not return any results at all. +``` + +Setting appropriate `distanceThreshold` is best handled by supervised learning as the distance threshold should be calibrated based on the query complexity and possibly also the feature distributions of the returned top-k hits. Having the `distance` rank feature returned as `match-features`, enables post-processing of the result using a custom [re-ranking/filtering searcher](../ranking/reranking-in-searcher). The post-processing searcher can analyze the score distributions of the returned top-k hits (using the features returned with `match-features`), remove low scoring hits before presenting the result to the end user, or not return any results at all. ## Hybrid sparse and dense retrieval methods with Vespa -In the previous filtering examples the ranking was not impacted by the filters. -They were only used to impact recall, not the order of the results. The following examples -demonstrate how to perform hybrid retrieval combining the efficient query operators in -a single query. Hybrid retrieval can be used as the first phase in a multiphase ranking funnel, see -Vespa's [phased ranking](../ranking/phased-ranking.html). - -The first query example combines the `nearestNeighbor` operator with the `weakAnd` operator, -combining them using logical disjunction (`OR`). This type of query enables retrieving -both based on semantic (vector distance) and traditional sparse (exact) matching. - -
    - -
    +
    +In the previous filtering examples the ranking was not impacted by the filters. They were only used to impact recall, not the order of the results. The following examples demonstrate how to perform hybrid retrieval combining the efficient query operators in a single query. Hybrid retrieval can be used as the first phase in a multiphase ranking funnel, see Vespa's [phased ranking](../ranking/phased-ranking).
    +
    +The first query example combines the `nearestNeighbor` operator with the `weakAnd` operator, combining them using logical disjunction (`OR`). This type of query enables retrieving both based on semantic (vector distance) and traditional sparse (exact) matching. 
    +
    +```bash
     $ vespa query \
         'yql=select title, artist from track where {totalTargetHits:100}nearestNeighbor(embedding,q) or userQuery()' \
         'query=total eclipse of the heart' \
    @@ -1318,20 +1134,13 @@ $ vespa query \
         'hits=2' \
         'ranking=hybrid' \
         'input.query(q)=embed(e5, "Total Eclipse Of The Heart")'
    -
    -
    +``` -The query combines the sparse `weakAnd` and dense `nearestNeighbor` query operators -using logical disjunction. -Both query operator retrieves the target number of hits (or more), ranked by its inner -raw score function. +The query combines the sparse `weakAnd` and dense `nearestNeighbor` query operators using logical disjunction. Both query operator retrieves the target number of hits (or more), ranked by its inner raw score function. -The hits exposed to the configurable `first-phase` ranking expression is a combination -of the best hits from the two different retrieval strategies. -The ranking is performed using the `hybrid` rank profile which serves as an example -how to combine the different efficient retrievers. +The hits exposed to the configurable `first-phase` ranking expression is a combination of the best hits from the two different retrieval strategies. The ranking is performed using the `hybrid` rank profile which serves as an example how to combine the different efficient retrievers. -
    +```js expandable
     rank-profile hybrid inherits closeness {
             inputs {
                 query(wTags) : 1
    @@ -1354,11 +1163,11 @@ rank-profile hybrid inherits closeness {
                 closeness(field, embedding)
             }
         }
    -
    +``` The query returns the following result: -
    {% highlight json%}
    +```json expandable
     {
         "timing": {
             "querytime": 0.005,
    @@ -1416,17 +1225,14 @@ The query returns the following result:
                 }
             ]
         }
    -}{% endhighlight %}
    +} +``` -The result hits also include [match-features](../reference/schemas/schemas.html#match-features) which -can be used for feature logging for learning to rank, or to simply -debug the various feature components used to calculate the `relevance` score. +The result hits also include [match-features](../reference/schemas/schemas#match-features) which can be used for feature logging for learning to rank, or to simply debug the various feature components used to calculate the `relevance` score. In the below query, we lower the weight of the popularity factor by adjusting `query(wPopularity)` to 0.1: -
    - -
    +```bash
     $ vespa query \
         'yql=select title, artist from track where {totalTargetHits:100}nearestNeighbor(embedding,q) or userQuery()' \
         'query=total eclipse of the heart' \
    @@ -1435,12 +1241,11 @@ $ vespa query \
         'ranking=hybrid' \
         'input.query(q)=embed(e5, "Total Eclipse Of The Heart")' \
         'input.query(wPopularity)=0.1'
    -
    -
    +``` Which changes the order and a different hit is surfaced at position two: -
    {% highlight json%}
    +```json expandable
     {
         "timing": {
             "querytime": 0.005,
    @@ -1499,19 +1304,17 @@ Which changes the order and a different hit is surfaced at position two:
             ]
         }
     }
    +```
     
    -{% endhighlight %}
    +The following query adds the personalization component using the sparse user profile into the retriever mix. -The following query adds the personalization component using the sparse -user profile into the retriever mix. -
    +```text
     userProfile={"love songs":1, "love":1,"80s":1}
    -
    +``` + Which can be used with the `wand` query operator to retrieve personalized hits for ranking. -
    - -
    +```bash
     $ vespa query \
         'yql=select title, artist from track where {totalTargetHits:100}nearestNeighbor(embedding,q) or userQuery() or ({totalTargetHits:10}wand(tags, @userProfile))' \
         'query=total eclipse of the heart' \
    @@ -1520,15 +1323,12 @@ $ vespa query \
         'ranking=hybrid' \
         'input.query(q)=embed(e5, "Total Eclipse Of The Heart")' \
         'input.query(wPopularity)=0.1' \
    -    'userProfile={"love songs":1, "love":1,"80s":1}' 
    -
    -
    + 'userProfile={"love songs":1, "love":1,"80s":1}' +``` -Now we have new top ranking documents. Notice that `totalCount` increases as the -`wand` query operator retrieved more hits into `first-phase` ranking. Also notice that -the `relevance` score changes. +Now we have new top ranking documents. Notice that `totalCount` increases as the `wand` query operator retrieved more hits into `first-phase` ranking. Also notice that the `relevance` score changes. -
    {% highlight json%}
    +```json expandable
     {
         "timing": {
             "querytime": 0.01,
    @@ -1587,49 +1387,33 @@ the `relevance` score changes.
             ]
         }
     }
    -{% endhighlight %}
    +``` -Changing from logical `OR` to `AND` instead will intersect the result of the two efficient retrievers. -The search for nearest neighbors is constrained to documents that at least match one of -the query terms in the `weakAnd`. +Changing from logical `OR` to `AND` instead will intersect the result of the two efficient retrievers. The search for nearest neighbors is constrained to documents that at least match one of the query terms in the `weakAnd`. -
    - -
    +```bash
     $ vespa query \
         'yql=select title, artist from track where {totalTargetHits:100}nearestNeighbor(embedding,q) and userQuery()' \
         'query=total eclipse of the heart' \
         'type=weakAnd' \
         'hits=2' \
         'ranking=hybrid' \
    -    'input.query(q)=embed(e5, "Total Eclipse Of The Heart")' 
    -
    -
    + 'input.query(q)=embed(e5, "Total Eclipse Of The Heart")' +``` -In this case, the documents exposed to ranking must match at least one of the query terms (for WAND to retrieve it). -It is also possible to combine hybrid search with filters, this filters both the sparse and dense retrieval on popularity +In this case, the documents exposed to ranking must match at least one of the query terms (for WAND to retrieve it). It is also possible to combine hybrid search with filters, this filters both the sparse and dense retrieval on popularity -
    - -
    +```bash
     $ vespa query \
    -    'yql=select title, artist from track where {totalTargetHits:100}nearestNeighbor(embedding,q) and userQuery() and popularity < 75' \
    -    'query=total eclipse of the heart' \
    -    'type=weakAnd' \
    +    'yql=select title, artist from track where {totalTargetHits:100}nearestNeighbor(embedding,q) and userQuery() and popularity 99,{totalTargetHits:10}nearestNeighbor(embedding,q))' \
         'hits=2' \
    -    'ranking=hybrid' \
    -    'input.query(q)=embed(e5, "Total Eclipse Of The Heart")' 
    -
    -
    + 'ranking=closeness' \ + 'input.query(q)=embed(e5, "Total Eclipse Of The Heart")' +``` -Another interesting approach for hybrid retrieval is to use Vespa's -[rank()](../reference/querying/yql.html#rank) query operator. The first operand -of the `rank()` operator is used for retrieval, and the remaining operands are only used to compute -rank features for those hits retrieved by the first operand. +Another interesting approach for hybrid retrieval is to use Vespa's [rank()](../reference/querying/yql#rank) query operator. The first operand of the `rank()` operator is used for retrieval, and the remaining operands are only used to compute rank features for those hits retrieved by the first operand. -
    - -
    +```bash
     $ vespa query \
         'yql=select title, artist from track where rank({totalTargetHits:100}nearestNeighbor(embedding,q), userQuery())' \
         'query=total eclipse of the heart' \
    @@ -1637,15 +1421,11 @@ $ vespa query \
         'hits=2' \
         'ranking=hybrid' \
         'input.query(q)=embed(e5, "Total Eclipse Of The Heart")' 
    -
    -
    +``` -This query returns 100 documents, since only the first operand of the `rank` query operator was used for -*retrieval*, the sparse `userQuery()` representation was only used to calculate sparse -[rank features](../reference/ranking/rank-features.html) for -the results retrieved by the `nearestNeighbor`. Sparse rank features such as `bm25(title)` for example. +This query returns 100 documents, since only the first operand of the `rank` query operator was used for *retrieval*, the sparse `userQuery()` representation was only used to calculate sparse [rank features](../reference/ranking/rank-features.html) for the results retrieved by the `nearestNeighbor`. Sparse rank features such as `bm25(title)` for example. -
    {% highlight json%}
    +```json expandable
     {
         "timing": {
             "querytime": 0.005,
    @@ -1704,15 +1484,11 @@ the results retrieved by the `nearestNeighbor`. Sparse rank features such as `bm
             ]
         }
     }
    -{% endhighlight %}
    +``` -One can also do this the other way around, retrieve using the sparse representation, and have -Vespa calculate the `closeness(field, embedding)` or related rank features for the hits -retrieved by the sparse query representation. +One can also do this the other way around, retrieve using the sparse representation, and have Vespa calculate the `closeness(field, embedding)` or related rank features for the hits retrieved by the sparse query representation. -
    - -
    +```bash
     $ vespa query \
         'yql=select title, artist from track where rank(userQuery(),{totalTargetHits:100}nearestNeighbor(embedding,q))' \
         'query=total eclipse of the heart' \
    @@ -1720,54 +1496,41 @@ $ vespa query \
         'hits=2' \
         'ranking=hybrid' \
         'input.query(q)=embed(e5, "Total Eclipse Of The Heart")' 
    -
    -
    +``` -The `weakAnd` query operator exposes more hits to ranking than approximate nearest neighbor search, similar -to the `wand` query operator. Generally, using the `rank` query operator is more efficient than combining -query retriever operators using `or`. See also the -[Vespa passage ranking](https://github.com/vespa-engine/sample-apps/blob/master/msmarco-ranking/) -for complete examples of different retrieval strategies for [multiphase ranking](../ranking/phased-ranking.html) funnels. +The `weakAnd` query operator exposes more hits to ranking than approximate nearest neighbor search, similar to the `wand` query operator. Generally, using the `rank` query operator is more efficient than combining query retriever operators using `or`. See also the [Vespa passage ranking](https://github.com/vespa-engine/sample-apps/blob/master/msmarco-ranking/) for complete examples of different retrieval strategies for [multiphase ranking](../ranking/phased-ranking.html) funnels. One can also use the `rank` operator to first retrieve by some filter logic, and compute distance or similarity for the retrieved documents. -
    - -
    -$ vespa query \
    +```bash
    +vespa query \
         'yql=select title, popularity, artist from track where rank(popularity>99,{totalTargetHits:10}nearestNeighbor(embedding,q))' \
         'hits=2' \
         'ranking=closeness' \
    -    'input.query(q)=embed(e5, "Total Eclipse Of The Heart")' 
    -
    -
    + 'input.query(q)=embed(e5, "Total Eclipse Of The Heart")' +``` -Queries that only use the `nearestNeighbor` operator as the second operand of `rank` does not need to add `HNSW` indexing, which saves -a lot of indexing and memory resource footprint. +Queries that only use the `nearestNeighbor` operator as the second operand of `rank` does not need to add `HNSW` indexing, which saves a lot of indexing and memory resource footprint. ## Multiple nearest neighbor search operators in the same query -This section looks at how to use multiple `nearestNeighbor` query operator instances in the same Vespa query request. -The following Vespa query combines two `nearestNeighbor` query operators -using logical disjunction (`OR`) and referencing two different -query tensor inputs: + +This section looks at how to use multiple `nearestNeighbor` query operator instances in the same Vespa query request. The following Vespa query combines two `nearestNeighbor` query operators using logical disjunction (`OR`) and referencing two different query tensor inputs: - `input.query(q)` holding the *Total Eclipse Of The Heart* query vector. - `input.query(q1)` holding the *Summer of '69* query vector. -
    - -
    +```bash
     $ vespa query \
         'yql=select title from track where ({totalTargetHits:10}nearestNeighbor(embedding,q)) or ({totalTargetHits:10}nearestNeighbor(embedding,q1))' \
         'hits=2' \
         'ranking=closeness' \
         'input.query(q)=embed(e5, "Total Eclipse Of The Heart")'  \
    -    'input.query(q1)=embed(e5, "Summer of 69")'  
    -
    -
    + 'input.query(q1)=embed(e5, "Summer of 69")' +``` The query exposes 20 hits to first phase ranking, as seen from `totalCount`. Ten from each nearest neighbor query operator: -
    {% highlight json%}
    +
    +```json expandable
     {
         "timing": {
             "querytime": 0.004,
    @@ -1813,40 +1576,33 @@ The query exposes 20 hits to first phase ranking, as seen from `totalCount`. Ten
                 }
             ]
         }
    -}{% endhighlight %}
    -Utilizing a combination of various query embeddings within a single query request holds numerous applications, -particularly in cases involving shorter queries with inherent ambiguity. -In such scenarios, employing query expansion and query rewrites can facilitate retrieval by accommodating multiple interpretations. +} +``` +Utilizing a combination of various query embeddings within a single query request holds numerous applications, particularly in cases involving shorter queries with inherent ambiguity. In such scenarios, employing query expansion and query rewrites can facilitate retrieval by accommodating multiple interpretations. ### Using label -One can also use the [label](../reference/querying/yql.html#label) query term -annotation when there are multiple `nearestNeighbor` operators in the same query -to get the distance or closeness per query vector. Notice we use the `closeness-label` rank-profile defined -in the schema: -
    +One can also use the [label](../reference/querying/yql#label) query term annotation when there are multiple `nearestNeighbor` operators in the same query to get the distance or closeness per query vector. Notice we use the `closeness-label` rank-profile defined in the schema:
    +
    +```js
     rank-profile closeness-label inherits closeness {
         match-features: closeness(label, q) closeness(label, q1)
     }
    -
    +``` -
    - -
    +```bash
     $ vespa query \
         'yql=select title from track where ({ label:"q", totalTargetHits:10}nearestNeighbor(embedding,q)) or ({label:"q1",totalTargetHits:10}nearestNeighbor(embedding,q1))' \
         'hits=2' \
         'ranking=closeness-label' \
         'input.query(q)=embed(e5, "Total Eclipse Of The Heart")'  \
    -    'input.query(q1)=embed(e5, "Summer of 69")'  
    -
    -
    + 'input.query(q1)=embed(e5, "Summer of 69")' +``` -The above query annotates the two `nearestNeighbor` query operators using -[label](../reference/querying/yql.html#label) query annotation. +The above query annotates the two `nearestNeighbor` query operators using [label](../reference/querying/yql#label) query annotation. -
    {% highlight json%}
    +```json expandable
     {
         "timing": {
             "querytime": 0.004,
    @@ -1894,31 +1650,23 @@ The above query annotates the two `nearestNeighbor` query operators using
                 }
             ]
         }
    -}{% endhighlight %}
    +} +``` -Note that the previous examples used `or` to combine the two operators. Using `and` instead, requires -that there are documents that is in both the top-k results. Increasing `totalTargetHits` to 500, -finds a few tracks that overlap. +Note that the previous examples used `or` to combine the two operators. Using `and` instead, requires that there are documents that is in both the top-k results. Increasing `totalTargetHits` to 500, finds a few tracks that overlap. -
    - -
    +```bash
     $ vespa query \
         'yql=select title from track where ({label:"q", totalTargetHits:500}nearestNeighbor(embedding,q)) and ({label:"q1",totalTargetHits:500}nearestNeighbor(embedding,q1))' \
         'hits=2' \
         'ranking=closeness-label' \
         'input.query(q)=embed(e5, "Total Eclipse Of The Heart")'  \
    -    'input.query(q1)=embed(e5, "Summer of 69")' 
    -
    -
    + 'input.query(q1)=embed(e5, "Summer of 69")' +``` -Note that the `closeness-label` rank profile -uses `closeness(field, embedding)` which in the case of multiple nearest neighbor search operators -uses the maximum score to represent the unlabeled `closeness(field,embedding)`. This -can be seen from the `relevance` value, -compared with the labeled [closeness()](../reference/ranking/rank-features.html#closeness(dimension,name)) rank features. +Note that the `closeness-label` rank profile uses `closeness(field, embedding)` which in the case of multiple nearest neighbor search operators uses the maximum score to represent the unlabeled `closeness(field,embedding)`. This can be seen from the `relevance` value, compared with the labeled [closeness()](../reference/ranking/rank-features#closeness(dimension,name)) rank features. -
    {% highlight json%}
    +```json expandable
     {
         "timing": {
             "querytime": 0.008,
    @@ -1966,13 +1714,13 @@ compared with the labeled [closeness()](../reference/ranking/rank-features.html#
                 }
             ]
         }
    -}{% endhighlight %}
    +} +``` -Vespa also supports having multiple document side embedding fields, which also -can be searched using multiple `nearestNeighbor` operators in the query. +Vespa also supports having multiple document side embedding fields, which also can be searched using multiple `nearestNeighbor` operators in the query. -
    -field embedding type tensor<float>(x[384]) {
    +```js expandable
    +field embedding type tensor(x[384]) {
         indexing: attribute | index
         attribute {
             distance-metric: euclidean
    @@ -1984,7 +1732,7 @@ field embedding type tensor<float>(x[384]) {
             }
         }
      }
    - field embedding_two tensor<float>(x[768]) {
    + field embedding_two tensor(x[768]) {
         indexing: attribute | index
         attribute {
             distance-metric: euclidean
    @@ -1996,68 +1744,50 @@ field embedding type tensor<float>(x[384]) {
             }
         }
      }
    -
    +``` ## Controlling filter behavior -Vespa allows developers to control how filters are combined with nearestNeighbor query operator, see -[Query Time Constrained Approximate Nearest Neighbor Search](https://blog.vespa.ai/constrained-approximate-nearest-neighbor-search/) -for a detailed description of *pre-filtering* and *post-filtering* strategies. -The following query examples explore the two query-time parameters -which can be used to control the filtering behavior. The parameters are +Vespa allows developers to control how filters are combined with nearestNeighbor query operator, see [Query Time Constrained Approximate Nearest Neighbor Search](https://blog.vespa.ai/constrained-approximate-nearest-neighbor-search/) for a detailed description of *pre-filtering* and *post-filtering* strategies. The following query examples explore the two query-time parameters which can be used to control the filtering behavior. The parameters are -- [ranking.matching.postFilterThreshold](../reference/api/query.html#ranking.matching) default 1.0 -- [ranking.matching.approximateThreshold](../reference/api/query.html#ranking.matching) default 0.02 +- [ranking.matching.postFilterThreshold](../reference/api/query#ranking.matching) default 1.0 +- [ranking.matching.approximateThreshold](../reference/api/query#ranking.matching) default 0.02 -These parameters can be used per query or configured in the rank-profile in the -[document schema](../reference/schemas/schemas.html#post-filter-threshold). +These parameters can be used per query or configured in the rank-profile in the [document schema](../reference/schemas/schemas#post-filter-threshold). -The following query runs with the default setting for *ranking.matching.postFilterThreshold* which is 1, which means, -do not perform post-filtering, use *pre-filtering* strategy: +The following query runs with the default setting for *ranking.matching.postFilterThreshold* which is 1, which means, do not perform post-filtering, use *pre-filtering* strategy: -
    - -
    +```bash
     $ vespa query \
       'yql=select title, artist, tags from track where {totalTargetHits:10}nearestNeighbor(embedding,q) and tags contains "rock"' \
       'hits=2' \
       'ranking=closeness' \
       'ranking.matching.postFilterThreshold=1.0' \
       'ranking.matching.approximateThreshold=0.05' \
    -  'input.query(q)=embed(e5, "Total Eclipse Of The Heart")'  
    -
    -
    + 'input.query(q)=embed(e5, "Total Eclipse Of The Heart")' +``` -The query exposes `totalTargetHits` to ranking as seen from the `totalCount`. Now, repeating the query, but -forcing *post-filtering* instead by setting *ranking.matching.postFilterThreshold=0.0*: +The query exposes `totalTargetHits` to ranking as seen from the `totalCount`. Now, repeating the query, but forcing *post-filtering* instead by setting *ranking.matching.postFilterThreshold=0.0*: -
    - -
    +
    +```bash
     $ vespa query \
       'yql=select title, artist, tags from track where {totalTargetHits:10}nearestNeighbor(embedding,q) and tags contains "rock"' \
       'hits=2' \
       'ranking=closeness' \
       'ranking.matching.postFilterThreshold=0.0' \
       'ranking.matching.approximateThreshold=0.05' \
    -  'input.query(q)=embed(e5, "Total Eclipse Of The Heart")' 
    -
    -
    + 'input.query(q)=embed(e5, "Total Eclipse Of The Heart")' +``` -In this case, Vespa will estimate how many documents the filter matches and auto-adjust `targethits` internally to a -higher number, attempting to expose the `totalTargetHits` to first phase ranking: +In this case, Vespa will estimate how many documents the filter matches and auto-adjust `targethits` internally to a higher number, attempting to expose the `totalTargetHits` to first phase ranking: -The query exposes 16 documents to ranking as can be seen from `totalCount`. There are `8420` documents in the collection -that are tagged with the `rock` tag, so roughly 8%. +The query exposes 16 documents to ranking as can be seen from `totalCount`. There are `8420` documents in the collection that are tagged with the `rock` tag, so roughly 8%. -Auto adjusting `totalTargetHits` upwards for post-filtering is not always what you want, because it is slower than just retrieving -from the HNSW index without constraints. We can change the -`totalTargetHits` adjustment factor with the [ranking.matching.targetHitsMaxAdjustmentFactor](../reference/api/query.html#ranking.matching) parameter. -In this case, we set it to 1, which disables adjusting the `totalTargetHits` upwards. +Auto adjusting `totalTargetHits` upwards for post-filtering is not always what you want, because it is slower than just retrieving from the HNSW index without constraints. We can change the `totalTargetHits` adjustment factor with the [ranking.matching.targetHitsMaxAdjustmentFactor](../reference/api/query#ranking.matching) parameter. In this case, we set it to 1, which disables adjusting the `totalTargetHits` upwards. -
    - -
    +
    +```bash
     $ vespa query \
       'yql=select title, artist, tags from track where {totalTargetHits:10}nearestNeighbor(embedding,q) and tags contains "rock"' \
       'hits=2' \
    @@ -2066,17 +1796,13 @@ $ vespa query \
       'ranking.matching.approximateThreshold=0.05' \
       'ranking.matching.targetHitsMaxAdjustmentFactor=1' \
       'input.query(q)=embed(e5, "Total Eclipse Of The Heart")'
    -
    -
    +``` + Since we are post-filtering without upward adjusting totalTargetHits, we end up with fewer hits. -Changing the query to limit to a tag which is less frequent, for example, `90s`, which -matches 1,695 documents or roughly 1.7%, will cause Vespa to fall back to exact search as the estimated filter hit count -is less than the `approximateThreshold`. +Changing the query to limit to a tag which is less frequent, for example, `90s`, which matches 1,695 documents or roughly 1.7%, will cause Vespa to fall back to exact search as the estimated filter hit count is less than the `approximateThreshold`. -
    - -
    +```bash
     $ vespa query \
       'yql=select title, artist, tags from track where {totalTargetHits:10}nearestNeighbor(embedding,q) and tags contains "90s"' \
       'hits=2' \
    @@ -2084,23 +1810,16 @@ $ vespa query \
       'ranking.matching.postFilterThreshold=0.0' \
       'ranking.matching.approximateThreshold=0.05' \
       'input.query(q)=embed(e5, "Total Eclipse Of The Heart")'
    -
    -
    +``` -The fallback to exact search will expose more than `totalTargetHits` documents to ranking. -Read more about combining filters with nearest neighbor search in -the [Query Time Constrained Approximate Nearest Neighbor Search](https://blog.vespa.ai/constrained-approximate-nearest-neighbor-search/) -blog post. +The fallback to exact search will expose more than `totalTargetHits` documents to ranking. Read more about combining filters with nearest neighbor search in the [Query Time Constrained Approximate Nearest Neighbor Search](https://blog.vespa.ai/constrained-approximate-nearest-neighbor-search/) blog post. ## Tear down the container + This concludes this tutorial. The following removes the container and the data: -
    - -
    -$ docker rm -f vespa
    -
    -
    - +```bash +$ docker rm -f vespa +``` diff --git a/en/querying/nearest-neighbor-search.md b/en/querying/nearest-neighbor-search.md deleted file mode 100644 index af489eb156..0000000000 --- a/en/querying/nearest-neighbor-search.md +++ /dev/null @@ -1,441 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Nearest Neighbor Search" -redirect_from: - - /en/nearest-neighbor-search ---- - -Nearest neighbor search, or vector search, is a technique used -to find the closest data points to a given query point in a high-dimensional vector space. -This is supported in Vespa using the -[nearestNeighbor](../reference/querying/yql.html#nearestneighbor) query operator. -This operator can be combined with other filters or query terms using the -[Vespa query language](query-language.html), -making it easy to create hybrid solutions that combine modern vector based techniques with -[traditional information retrieval](text-matching.html). - -Also see the [pyvespa examples](https://vespa-engine.github.io/pyvespa/examples/pyvespa-examples.html#Neighbors). - - -## Minimal example - -A nearest neighbor search has at least these components: a document vector, a query vector, -a rank profile using `closeness()` and a query with the `nearestNeighbor` operator: -
    -    # Schema definition of the vector in documents
    -    document doc {
    -
    -        field d_vector type tensor<float>(d[3]) {
    -            indexing: attribute | index
    -            attribute {
    -                distance-metric: angular
    -            }
    -        }
    -
    -    }
    -
    -    # Rank profile definition in schema:
    -    #  - defining the q_vector type that must match the d_vector type
    -    #  - using the closeness() rank feature in the ranking expression
    -    rank-profile rank_docs inherits default {
    -        inputs {
    -            query(q_vector) tensor<float>(d[3])
    -        }
    -        first-phase {
    -            expression: closeness(field, d_vector)
    -        }
    -    }
    -
    -# Documents with vectors
    -{
    -    "put": "id:mynamespace:music::a-head-full-of-dreams",
    -    "fields": {
    -        "d_vector": [0,1,2]
    -    }
    -}
    -
    -# A query with
    -#  - a nearestNeighbor operator with document and query vectors
    -#  - selecting the rank_docs rank profile
    -$ vespa query 'select * from docs where {targetHits: 3}nearestNeighbor(d_vector, q_vector)' \
    -  ranking=rank_docs \
    -  'input.query(q_vector)'='[1,2,3]'
    -
    -
    -The `nearestNeighbor` query operator will calculate values -used by the [closeness()](../reference/ranking/rank-features.html#closeness(dimension,name)) rank feature. -{% include note.html content='closeness(`field`, d_vector) -means that the closeness rank feature shall use the d_vector field. -

    -Applications can have multiple vector fields. -These cases assign labels to the different `nearestNeighbor` operators, -so the closeness() rank feature refers to the different operators (using different fields) -See other examples of using closeness(`label`, q) -in the [nearest neighbor search guide](nearest-neighbor-search-guide#using-label).' %} - -Read more in this guide on tensor types, distance metrics, rank profiles -and approximate nearest neighbor search. - - -## Vectors -A vector is represented by a [tensor](../ranking/tensor-user-guide.html) with one indexed dimension. -Example [tensor type](../reference/ranking/tensor.html#tensor-type-spec) representing a float vector with 384 dimensions: -
    -tensor<float>(x[384])
    -
    - -Document vectors are stored in a -[tensor field](../reference/schemas/schemas.html#tensor) -defined in the document [schema](../reference/schemas/schemas.html). -A tensor type (dense) with one indexed dimension stores a single vector per document: -
    -field doc_embedding type tensor<float>(x[384]) {
    -    indexing: attribute
    -}
    -
    - -A tensor type (mixed) with one or more mapped dimensions and one indexed dimension stores multiple vectors per document: -
    -field doc_embeddings type tensor<float>(m{},x[384]) {
    -    indexing: attribute
    -}
    -
    - -Similarly, the type of a query vector is defined in a -[rank-profile](../reference/schemas/schemas.html#rank-profile): -
    -rank-profile my_profile {
    -    inputs {
    -        query(query_embedding) tensor<float>(x[384])
    -    }
    -    ...
    -}
    -
    - -This all ties together with the [nearestNeighbor](../reference/querying/yql.html#nearestneighbor) query operator -that expects two arguments; the document tensor field name which is searched and the input query tensor name. -The operator finds the documents that are closest to the query vector -using the [distance-metric](#distance-metrics-for-nearest-neighbor-search) defined in the tensor field. -Note that the document schema can have multiple tensor fields storing vectors, -and the query can have multiple `nearestNeighbor` operators searching different tensor fields. - -Support for using the `nearestNeighbor` operator with a mixed tensor with multiple vectors per document -is available in {% include version.html version="8.144.19" %}. - -To learn how Vespa can create the vectors for you, see [embedding](../rag/embedding.html). - - -## Using nearest neighbor search -The following sections demonstrates how to use the -[nearestNeighbor](../reference/querying/yql.html#nearestneighbor) query operator. - -These examples use **exact** nearest neighbor search with perfect accuracy, -but which is computationally expensive for large document volumes since -the distance metric must be calculated for every document that matches -the boolean query filters. -See also the [practical nearest neighbor search guide](nearest-neighbor-search-guide) for more examples. - -Exact nearest neighbor search scales close to linearly with -[number of threads used per query](../performance/practical-search-performance-guide#multithreaded-search-and-ranking). -This can be used to make exact nearest neighbor search run with acceptable serving latency. -Using more threads per search to reduce latency is still costly for larger vector volumes -or high query throughput applications, as the number of distance calculations -involved in the query does not change by changing number of threads performing the search. - -A cost-efficient approach is to use **approximate** search instead. -See how to use **approximate** nearest neighbor search with `HNSW` in -the [Approximate Nearest Neighbor Search](approximate-nn-hnsw) document. - -The following [document schema](../basics/schemas.html) is used to illustrate Vespa's support for -vector search, or nearest neighbor search: - -
    -schema product {
    -
    -    document product {
    -
    -        field in_stock type bool {
    -            indexing: summary | attribute
    -            rank: filter
    -            attribute: fast-search
    -        }
    -
    -        field popularity type float {
    -            indexing: summary | attribute
    -        }
    -
    -        field text_embedding type tensor<float>(x[384]) {
    -            indexing: summary | attribute
    -            attribute {
    -                distance-metric: prenormalized-angular
    -            }
    -        }
    -
    -        field image_embeddings type tensor<float>(i{},x[512]) {
    -            indexing: summary | attribute
    -            attribute {
    -                distance-metric: angular
    -            }
    -        }
    -
    -    }
    -
    -}
    -
    - -The `product` document schema has 4 fields. -The fields of type [tensor](../ranking/tensor-user-guide.html) represent vector embeddings: - -- `text_embedding` - float vector with 384 dimensions. -- `image_embeddings` - multiple float vectors with 512 dimensions. - -The `text_embedding` field stores a dense vectorized embedding representation -of the product description and which use [prenormalized-angular](../reference/schemas/schemas.html#distance-metric) -as `distance-metric`. See for example -[Dense Retrieval using bi-encoders over Transformer models](https://blog.vespa.ai/pretrained-transformer-language-models-for-search-part-2/). - -The `image_embeddings` field stores multiple dense vectorized embedding representations -of the product images and which use [angular](../reference/schemas/schemas.html#distance-metric) -as `distance-metric`. -See for example [text to image search using CLIP with Vespa](https://blog.vespa.ai/text-image-search/). - -### Distance metrics for nearest neighbor search -Vespa supports six different [distance-metrics](../reference/schemas/schemas.html#distance-metric): - -* `euclidean` -* `angular` -* `dotproduct` -* `prenormalized-angular` -* `hamming` -* `geodegrees` - -The distance-metric is a property of the field. -This is obvious when `index` is set on the vector field, -as the distance metric is used when building the index structure. -Without `index`, no extra data structures are built for the field, -and the distance-metric setting is used when calculating the distance at query-time. -It still makes sense to have the metric as a field-property, -as the field values are often produced using a specific distance metric. - -### Configure rank profiles for nearest neighbor search -Lastly, one need to configure how to [rank](../basics/ranking.html) products which -are retrieved by the nearest neighbor search: - -
    -rank-profile semantic_similarity {
    -    inputs {
    -        query(query_embedding) tensor<float>(x[384])
    -    }
    -    first-phase {
    -        expression: closeness(field, text_embedding)
    -    }
    -}
    -
    -rank-profile image_similarity {
    -    inputs {
    -        query(image_query_embeddings) tensor<float>(x[512]])
    -    }
    -    first-phase {
    -        expression: closeness(field, image_embeddings)
    -    }
    -}
    -
    - -The `rank-profile` specifies the query input tensor names and types. The query input tensors -must be of the same dimensionality as the document vector and have the same dimension name. - -Skipping the query tensor definition will cause a query time error: -``` -Expected a tensor value of 'query(query_embedding)' but has [...] -``` - -The `closeness(field, text_embedding)` is a [rank-feature](../reference/ranking/rank-features.html#closeness(dimension,name)) -calculated by the [nearestNeighbor](../reference/querying/yql.html#nearestneighbor) query operator. -This calculates a score in the range [0, 1], where 0 is infinite distance, -and 1 is zero distance. This is convenient because Vespa sorts hits by decreasing relevancy score, -and one usually want the closest hits to be ranked highest. - -The `closeness(field, image_embeddings)` [rank-feature](../reference/ranking/rank-features.html#closeness(dimension,name)) -operators over a tensor field that stores multiple vectors per document. -For each document, the vector that is closest to the query vector is used in the calculation. - -The `first-phase` is part of Vespa's [phased ranking](../ranking/phased-ranking.html) support. -Phased ranking enables re-ranking of the top-k best scoring hits as ranked -or retrieved from the previous ranking phase. The computed ranking score is rendered as `relevance` in -the default [Vespa JSON result format](../reference/querying/default-result-format.html). If the `relevance` field -of the hit becomes 0.0 one usually have forgotten to specify the correct ranking profile. - -An example of a `rank-profile` also specifying an additional [re-ranking phase](../ranking/phased-ranking.html): -
    -rank-profile image_similarity_with_reranking {
    -    inputs {
    -        query(image_query_embedding) tensor<float>(x[512]])
    -    }
    -    first-phase {
    -        expression: closeness(field, image_embeddings)
    -    } 
    -    second-phase {
    -        total-rerank-count: 1000
    -        expression: closeness(field, image_embeddings) * attribute(popularity)
    -    }
    -}
    -
    -In this case, hits retrieved by the -[nearestNeighbor](../reference/querying/yql.html#nearestneighbor) -query operator are re-scored also using -the product's popularity as a signal. The value of the `popularity` field can be read by the - `attribute(popularity)` rank-feature. The `second-phase` [ranking expression](../ranking/ranking-expressions-features.html) -combines the popularity with the `closeness(field, image_embeddings)` rank-feature using multiplication. - - -## Indexing product data -After deploying the application package with the document schema, you -can [index](../writing/reads-and-writes.html) the product data using the -[Vespa JSON feed format](../reference/schemas/document-json-format.html). - -In the example below there are two documents. -The vector embedding fields are using -[indexed tensor short form](../reference/schemas/document-json-format.html#tensor) -and [mixed tensor short form](../reference/schemas/document-json-format.html#tensor): -```json -[ - { - "put": "id:shopping:product::998211", - "fields": { - "in_stock": true, - "popularity": 0.342, - "text_embedding": [ - 0.16766378547490635, - 0.3737005826272204, - 0.040492891373747675, - .. - ], - "image_embeddings": { - "0": [ - 0.9147281579191466, - 0.5453696694173961, - 0.7529545687063771, - .. - ], - "1": [0.3737005826272204, ..] - } - } - }, - { - "put": "id:shopping:product::97711", - "fields": { - "in_stock": false, - "popularity": 0.538, - "text_embedding": [ - 0.03515862084651311, - 0.24585168798559187, - 0.6123057708571111, - .. - ], - "image_embeddings": { - "0": [ - 0.9785931815169806, - 0.5697209315543527, - 0.5352198004501647, - .. - ], - "1": [0.24585168798559187, ..] - } - } - } -] -``` - -The above JSON formatted data can be fed to Vespa using any of the -[Vespa feeding APIs](../writing/reads-and-writes.html#api-and-utilities). - -## Querying using nearestNeighbor query operator -The [nearestNeighbor](../reference/querying/yql.html#nearestneighbor) query operator -is used to search the product dataset. -The operator expects two arguments; the document tensor field which is searched and the input query tensor name. - -The [targetHits](../reference/querying/yql.html#targethits) -query annotation specifies the number of results to expose to `first-phase` -ranking per content node involved in the query. -`targetHits` is a required parameter and the query will fail if not specified. -With exact search, `targetHits` is a lower bound per content node: -At least but possibly more than `targetHits` hits are exposed to `first-phase` ranking on every content node -as long as `targetHits` hits are actually found and not filtered out afterwards. - -The query tensor is sent as a query input -and the query tensor name is referenced in the second argument of the `nearestNeighbor` operator. -In the following example, the `nearestNeighbor` operator -is used to recommend similar products based on image similarity. -For a given image (e.g. a product image shown in the product search result page) one can find -products which have similar product images. - -Note that the nearest neighbors search is limited to products where `in_stock` is `true`. - -The overall query is specified using the [Vespa query language](query-language.html) -using the [Query API](query-api.html#http): - -```json -{ - "yql": "select * from product where {totalTargetHits: 100}nearestNeighbor(image_embeddings, image_query_embedding) and in_stock = true", - "input.query(image_query_embedding)": [ - 0.22507139604882176, - 0.11696498718517367, - 0.9418422036734729, - .. - ], - "ranking.profile": "image_similarity", - "hits": 10 -} -``` - -The YQL query uses logical conjunction `and` to filter the `nearestNeighbor` -by a constraint on the `in_stock` field. - -The query request -also specifies [hits](../reference/api/query.html#hits), which determines how -many hits are returned to the client using the [JSON result format](../reference/querying/default-result-format.html). - -The total number of hits which is ranked by the ranking profile -depends on the query filters and how fast the nearest neighbor search algorithm converges (for exact search). - -The [ranking.profile](../reference/api/query.html#ranking.profile) parameter -controls which ranking profile is used. -In this case, it simply ranks documents based on how close they are in the CLIP embedding space. - - -## Using Nearest Neighbor from a Searcher Component -As with all query operators in Vespa, one can build the query tree programmatically -in a custom [searcher component](../applications/searchers.html). -See -[Centroids](https://github.com/vespa-engine/sample-apps/blob/master/billion-scale-image-search/app/src/main/java/ai/vespa/examples/Centroids.java) -in [Billion-Scale Image Search](https://github.com/vespa-engine/sample-apps/tree/master/billion-scale-image-search) -for an example of how the `NearestNeighborItem` is used. - - -## Using binary embeddings with hamming distance -The following packs a 128 bit embedding representation into a 16 dimensional dense tensor -using `int8` tensor value precision (16 x 8 = 128 bit): -
    -document vector {
    -    field vector type tensor<int8>(x[16]) {
    -        indexing: summary | attribute
    -        attribute {
    -            distance-metric: hamming
    -        }
    -    }
    -}
    -rank-profile hamming-nn {
    -    num-threads-per-search: 12
    -    first-phase {
    -        expression: closeness(field,vector)
    -    }
    -}
    -
    - -Hamming distance search over binary vectors is implemented with xor and pop count cpu instructions. -The rank-profile specifies -[num-threads-per-search](../reference/schemas/schemas.html#num-threads-per-search) to -reduce serving latency (but not cost). - -See the [Billion Scale Vector Search with Vespa](https://blog.vespa.ai/billion-scale-knn-part-two/) -blog post for a detailed introduction to using binary vectors with hamming distance. diff --git a/en/querying/nearest-neighbor-search.mdx b/en/querying/nearest-neighbor-search.mdx new file mode 100644 index 0000000000..1f174aa1d5 --- /dev/null +++ b/en/querying/nearest-neighbor-search.mdx @@ -0,0 +1,349 @@ +--- +title: "Nearest Neighbor Search" +--- + +Nearest neighbor search, or vector search, is a technique used to find the closest data points to a given query point in a high-dimensional vector space. This is supported in Vespa using the [nearestNeighbor](../reference/querying/yql#nearestneighbor) query operator. This operator can be combined with other filters or query terms using the [Vespa query language](/en/querying/query-language), making it easy to create hybrid solutions that combine modern vector based techniques with [traditional information retrieval](/en/querying/text-matching). + +Also see the [pyvespa examples](https://vespa-engine.github.io/pyvespa/examples/pyvespa-examples#Neighbors). + +## Minimal example + +A nearest neighbor search has at least these components: a document vector, a query vector, a rank profile using `closeness()` and a query with the `nearestNeighbor` operator: + +```js expandable +# Schema definition of the vector in documents + document doc { + + field `d_vector` type tensor(d[3]) { + indexing: attribute | index + attribute { + distance-metric: angular + } + } + + } + + # Rank profile definition in schema: + # - defining the q_vector type that must match the d_vector type + # - using the closeness() rank feature in the ranking expression + rank-profile rank_docs inherits default { + inputs { + query(`q_vector`) tensor(d[3]) + } + first-phase { + expression: `closeness`(field, d_vector) + } + } + +# Documents with vectors +{ + "put": "id:mynamespace:music::a-head-full-of-dreams", + "fields": { + "d_vector": [0,1,2] + } +} + +# A query with +# - a nearestNeighbor operator with document and query vectors +# - selecting the rank_docs rank profile +$ vespa query 'select * from docs where {targetHits: 3}`nearestNeighbor`(d_vector, q_vector)' \ + `ranking=rank_docs` \ + 'input.query(q_vector)'='[1,2,3]' +``` + +The `nearestNeighbor` query operator will calculate values used by the [closeness()](../reference/ranking/rank-features#closeness(dimension,name)) rank feature. + + +**Note:** + +closeness(`field`, d_vector) means that the closeness rank feature shall use the d_vector field. +Applications can have multiple vector fields. These cases assign labels to the different `nearestNeighbor` operators, so the closeness() rank feature refers to the different operators (using different fields) See other examples of using closeness(`label`, q) in the [nearest neighbor search guide](/en/querying/nearest-neighbor-search-guide#using-label). + + +Read more in this guide on tensor types, distance metrics, rank profiles and approximate nearest neighbor search. + +## Vectors + +A vector is represented by a [tensor](../ranking/tensor-user-guide) with one indexed dimension. Example [tensor type](../reference/ranking/tensor#tensor-type-spec) representing a float vector with 384 dimensions: + +```js +tensor(x[384]) +``` + +Document vectors are stored in a [tensor field](../reference/schemas/schemas#tensor) defined in the document [schema](../reference/schemas/schemas). A tensor type (dense) with one indexed dimension stores a single vector per document: + +```js +field doc_embedding type tensor(x[384]) { + indexing: attribute +} +``` + +A tensor type (mixed) with one or more mapped dimensions and one indexed dimension stores multiple vectors per document: + +```js +field doc_embeddings type tensor(m{},x[384]) { + indexing: attribute +} +``` + +Similarly, the type of a query vector is defined in a [rank-profile](../reference/schemas/schemas#rank-profile): + +```js +rank-profile my_profile { + inputs { + query(query_embedding) tensor(x[384]) + } + ... +} +``` + +This all ties together with the [nearestNeighbor](../reference/querying/yql#nearestneighbor) query operator that expects two arguments; the document tensor field name which is searched and the input query tensor name. The operator finds the documents that are closest to the query vector using the [distance-metric](#distance-metrics-for-nearest-neighbor-search) defined in the tensor field. Note that the document schema can have multiple tensor fields storing vectors, and the query can have multiple `nearestNeighbor` operators searching different tensor fields. + +Support for using the `nearestNeighbor` operator with a mixed tensor with multiple vectors per document is available in `Vespa 8.144.19`. + +To learn how Vespa can create the vectors for you, see [embedding](../rag/embedding). + +## Using nearest neighbor search + +The following sections demonstrates how to use the [nearestNeighbor](../reference/querying/yql#nearestneighbor) query operator. + +These examples use **exact** nearest neighbor search with perfect accuracy, but which is computationally expensive for large document volumes since the distance metric must be calculated for every document that matches the boolean query filters See also the [practical nearest neighbor search guide](/en/querying/nearest-neighbor-search-guide) for more examples. + +Exact nearest neighbor search scales close to linearly with [number of threads used per query](../performance/practical-search-performance-guide#multithreaded-search-and-ranking). This can be used to make exact nearest neighbor search run with acceptable serving latency. Using more threads per search to reduce latency is still costly for larger vector volumes or high query throughput applications, as the number of distance calculations involved in the query does not change by changing number of threads performing the search. + +A cost-efficient approach is to use **approximate** search instead. See how to use **approximate** nearest neighbor search with `HNSW` in the [Approximate Nearest Neighbor Search](/en/querying/approximate-nn-hnsw) document. + +The following [document schema](../basics/schemas) is used to illustrate Vespa's support for vector search, or nearest neighbor search: + +```js expandable +schema product { + + document product { + + field in_stock type bool { + indexing: summary | attribute + rank: filter + attribute: fast-search + } + + field popularity type float { + indexing: summary | attribute + } + + field text_embedding type tensor(x[384]) { + indexing: summary | attribute + attribute { + distance-metric: prenormalized-angular + } + } + + field image_embeddings type tensor(i{},x[512]) { + indexing: summary | attribute + attribute { + distance-metric: angular + } + } + + } + +} +``` + +The `product` document schema has 4 fields. The fields of type [tensor](../ranking/tensor-user-guide) represent vector embeddings: + +- `text_embedding` - float vector with 384 dimensions. +- `image_embeddings` - multiple float vectors with 512 dimensions. + +The `text_embedding` field stores a dense vectorized embedding representation of the product description and which use [prenormalized-angular](../reference/schemas/schemas#distance-metric) as `distance-metric`. See for example [Dense Retrieval using bi-encoders over Transformer models](https://blog.vespa.ai/pretrained-transformer-language-models-for-search-part-2/). + +The `image_embeddings` field stores multiple dense vectorized embedding representations of the product images and which use [angular](../reference/schemas/schemas#distance-metric) as `distance-metric`. See for example [text to image search using CLIP with Vespa](https://blog.vespa.ai/text-image-search/). + +### Distance metrics for nearest neighbor search + +Vespa supports six different [distance-metrics](../reference/schemas/schemas#distance-metric): + +* `euclidean` +* `angular` +* `dotproduct` +* `prenormalized-angular` +* `hamming` +* `geodegrees` + +The distance-metric is a property of the field. This is obvious when `index` is set on the vector field, as the distance metric is used when building the index structure. Without `index`, no extra data structures are built for the field, and the distance-metric setting is used when calculating the distance at query-time. It still makes sense to have the metric as a field-property, as the field values are often produced using a specific distance metric. + +### Configure rank profiles for nearest neighbor search + +Lastly, one need to configure how to [rank](../basics/ranking) products which are retrieved by the nearest neighbor search: + +```js +rank-profile semantic_similarity { + inputs { + query(query_embedding) tensor(x[384]) + } + first-phase { + expression: closeness(field, text_embedding) + } +} + +rank-profile image_similarity { + inputs { + query(image_query_embeddings) tensor(x[512]]) + } + first-phase { + expression: closeness(field, image_embeddings) + } +} +``` + +The `rank-profile` specifies the query input tensor names and types. The query input tensors must be of the same dimensionality as the document vector and have the same dimension name. + +Skipping the query tensor definition will cause a query time error: + +```bash +Expected a tensor value of 'query(query_embedding)' but has [...] +``` + +The `closeness(field, text_embedding)` is a [rank-feature](../reference/ranking/rank-features#closeness(dimension,name)) calculated by the [nearestNeighbor](../reference/querying/yql#nearestneighbor) query operator. This calculates a score in the range [0, 1], where 0 is infinite distance, and 1 is zero distance. This is convenient because Vespa sorts hits by decreasing relevancy score, and one usually want the closest hits to be ranked highest. + +The `closeness(field, image_embeddings)` [rank-feature](../reference/ranking/rank-features#closeness(dimension,name)) operators over a tensor field that stores multiple vectors per document. For each document, the vector that is closest to the query vector is used in the calculation. + +The `first-phase` is part of Vespa's [phased ranking](../ranking/phased-ranking) support. Phased ranking enables re-ranking of the top-k best scoring hits as ranked or retrieved from the previous ranking phase. The computed ranking score is rendered as `relevance` in the default [Vespa JSON result format](../reference/querying/default-result-format). If the `relevance` field of the hit becomes 0.0 one usually have forgotten to specify the correct ranking profile. + +An example of a `rank-profile` also specifying an additional [re-ranking phase](../ranking/phased-ranking): + +```js +rank-profile image_similarity_with_reranking { + inputs { + query(image_query_embedding) tensor(x[512]]) + } + first-phase { + expression: closeness(field, image_embeddings) + } + second-phase { + total-rerank-count: 1000 + expression: closeness(field, image_embeddings) * attribute(popularity) + } +} +``` + +In this case, hits retrieved by the [nearestNeighbor](../reference/querying/yql#nearestneighbor) query operator are re-scored also using the product's popularity as a signal. The value of the `popularity` field can be read by the `attribute(popularity)` rank-feature. The `second-phase` [ranking expression](../ranking/ranking-expressions-features) combines the popularity with the `closeness(field, image_embeddings)` rank-feature using multiplication. + +## Indexing product data + +After deploying the application package with the document schema, you can [index](../writing/reads-and-writes) the product data using the [Vespa JSON feed format](../reference/schemas/document-json-format). + +In the example below there are two documents. The vector embedding fields are using [indexed tensor short form](../reference/schemas/document-json-format#tensor) and [mixed tensor short form](../reference/schemas/document-json-format#tensor): + +```json expandable +[ + { + "put": "id:shopping:product::998211", + "fields": { + "in_stock": true, + "popularity": 0.342, + "text_embedding": [ + 0.16766378547490635, + 0.3737005826272204, + 0.040492891373747675, + .. + ], + "image_embeddings": { + "0": [ + 0.9147281579191466, + 0.5453696694173961, + 0.7529545687063771, + .. + ], + "1": [0.3737005826272204, ..] + } + } + }, + { + "put": "id:shopping:product::97711", + "fields": { + "in_stock": false, + "popularity": 0.538, + "text_embedding": [ + 0.03515862084651311, + 0.24585168798559187, + 0.6123057708571111, + .. + ], + "image_embeddings": { + "0": [ + 0.9785931815169806, + 0.5697209315543527, + 0.5352198004501647, + .. + ], + "1": [0.24585168798559187, ..] + } + } + } +] +``` + +The above JSON formatted data can be fed to Vespa using any of the [Vespa feeding APIs](../writing/reads-and-writes#api-and-utilities). + +## Querying using nearestNeighbor query operator + +The [nearestNeighbor](../reference/querying/yql#nearestneighbor) query operator is used to search the product dataset. The operator expects two arguments; the document tensor field which is searched and the input query tensor name. + +The [targetHits](../reference/querying/yql#targethits) query annotation specifies the number of results to expose to `first-phase` ranking per content node involved in the query. `targetHits` is a required parameter and the query will fail if not specified. With exact search, `targetHits` is a lower bound per content node: At least but possibly more than `targetHits` hits are exposed to `first-phase` ranking on every content node as long as `targetHits` hits are actually found and not filtered out afterwards. + +The query tensor is sent as a query input and the query tensor name is referenced in the second argument of the `nearestNeighbor` operator. In the following example, the `nearestNeighbor` operator is used to recommend similar products based on image similarity. For a given image (e.g. a product image shown in the product search result page) one can find products which have similar product images. + +Note that the nearest neighbors search is limited to products where `in_stock` is `true`. + +The overall query is specified using the [Vespa query language](/en/querying/query-language) using the [Query API](/en/querying/query-api#http): + +```json +{ + "yql": "select * from product where {totalTargetHits: 100}nearestNeighbor(image_embeddings, image_query_embedding) and in_stock = true", + "input.query(image_query_embedding)": [ + 0.22507139604882176, + 0.11696498718517367, + 0.9418422036734729, + .. + ], + "ranking.profile": "image_similarity", + "hits": 10 +} +``` + +The YQL query uses logical conjunction `and` to filter the `nearestNeighbor` by a constraint on the `in_stock` field. + +The query request also specifies [hits](../reference/api/query#hits), which determines how many hits are returned to the client using the [JSON result format](../reference/querying/default-result-format). + +The total number of hits which is ranked by the ranking profile depends on the query filters and how fast the nearest neighbor search algorithm converges (for exact search). + +The [ranking.profile](../reference/api/query#ranking.profile) parameter controls which ranking profile is used. In this case, it simply ranks documents based on how close they are in the CLIP embedding space. + +## Using Nearest Neighbor from a Searcher Component + +As with all query operators in Vespa, one can build the query tree programmatically in a custom [searcher component](../applications/searchers). See [Centroids](https://github.com/vespa-engine/sample-apps/blob/master/billion-scale-image-search/app/src/main/java/ai/vespa/examples/Centroids.java) in [Billion-Scale Image Search](https://github.com/vespa-engine/sample-apps/tree/master/billion-scale-image-search) for an example of how the `NearestNeighborItem` is used. + +## Using binary embeddings with hamming distance + +The following packs a 128 bit embedding representation into a 16 dimensional dense tensor using `int8` tensor value precision (16 x 8 = 128 bit): + +```js +document vector { + field vector type tensor(x[16]) { + indexing: summary | attribute + attribute { + distance-metric: hamming + } + } +} +rank-profile hamming-nn { + num-threads-per-search: 12 + first-phase { + expression: closeness(field,vector) + } +} +``` + +Hamming distance search over binary vectors is implemented with xor and pop count cpu instructions. The rank-profile specifies [num-threads-per-search](../reference/schemas/schemas#num-threads-per-search) to reduce serving latency (but not cost). + +See the [Billion Scale Vector Search with Vespa](https://blog.vespa.ai/billion-scale-knn-part-two/) blog post for a detailed introduction to using binary vectors with hamming distance. diff --git a/en/querying/page-templates.html b/en/querying/page-templates.html deleted file mode 100644 index 84fe05c1b1..0000000000 --- a/en/querying/page-templates.html +++ /dev/null @@ -1,385 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Page Templates" -redirect_from: -- /en/page-templates.html ---- - -

    -When multiple kinds of data is fetched for a request, the application -must decide how to lay out the data to return to the user. -Page templates allows such page layouts to be defined as XML -configuration files - one file per layout, corresponding to one use case. -

    -The layouts are structural - they do not specify widths -and heights, colors and similar, but define the various boxed -components that will make up the page, and their ordering and nesting. -It is also assumed that the complete application includes a frontend -which is capable of rendering finished pages from result laid out by a template. -

    -Page layouts may contain choices which specify alternative -versions of the template. The choices in a template are taken by -a resolver component at run time. Given an optimizing -resolver the system can then learn to make the right choices given -each particular query and result. An optimizing resolver is not -bundled with the platform but must be added as a component. -

    -This document describes how to get started, -explains the page template language -and how to add a choice resolver. -A complete reference of all the permissible content of page templates is found in the -page template reference. -

    - - - -

    Getting Started

    -

    -A page template is an XML file which is placed in the -directory search/page-templates/ in -the application package. -To start using page templates: -

    - -

    -The results returned will be as defined by the page template selected for each query. -

    -The source names used in page templates are the same as those defined -in the federation setup, -and/or of any internal search clusters defined in the application. -

    -A presentation layer (frontend) which understands the results created -by the page templates in use must be set up or created to produce rendered pages. -That is beyond the scope of this document. -

    - - - -

    Introduction to Page Templates

    -

    -A page template is an XML file which contains -a <page> tag at the top level. The page element -must have an id attribute, where the file name is the -same as the id, followed by .xml. If the id -is default, this template will be used whenever no template -is specified in the query. The templates may also be versioned, -see Component Versioning. -

    -A page template consist of nested sections which correspond -to screen areas in the final layout. The top level section is defined -by the page itself, while further sections can be defined by explicit -<section> tags. Each section may set a layout which will be -used by the frontend renderer to lay out its content -- column and row must be supported by -all renderers, while some renderers may specify additional layouts. -Each section may also specify sources of data which should be placed -in the section. Renderers must be able to render multiple data items -from different sources in a section. -

    -For example, this template creates a page consisting of four equally large regions containing one source each: -

    -
    -<page id="fourSquare" layout="column">
    -    <section layout="row">
    -        <section source="news"/>
    -        <section source="web"/>
    -    </section>
    -    <section layout="row">
    -        <section source="image"/>
    -        <section source="video"/>
    -    </section>
    -</page>
    -
    -

    -To use this template, save it as [application-package]/page-templates/fourSquare.xml. -

    -Suppose we want to extend this template to be able to also show blogs in the "news" section. -This can be done as follows: -

    -
    -<page id="fourSquare" layout="column">
    -    <section layout="row">
    -        <section source="news blog"/>
    -        <section source="web"/>
    -    </section>
    -    <section layout="row">
    -        <section source="image"/>
    -        <section source="video"/>
    -    </section>
    -</page>
    -
    -

    -Data items from each possible source has a rendering implemented by the frontend. -These renderers are used when nothing is specified in the template. -If some alternative rendering is desired, this can be -specified by a renderer tag. The same is true for -rendering of the sections themselves. Here we specify a different -renderer for blog data items (hits), as well as for the entire -news/blog section. -

    -
    -<page id="fourSquare" layout="column">
    -    <section layout="row">
    -        <section source="news">
    -            <renderer name="blueSection">
    -            <source name="blog">
    -                <renderer name="newBlogHitStyle"/>
    -            </source>
    -        <section/>
    -        <section source="web"/>
    -    </section>
    -    <section layout="row">
    -        <section source="image"/>
    -        <section source="video"/>
    -    </section>
    -</page>
    -
    -

    -Note that in order to add a renderer subelement, we now specify the -blog source by a tag rather than by an attribute. These two forms are -equivalent - the attribute variant is just a shorthand syntax. -

    -Sources and renderers can be given arbitrary key-value parameters -- see the reference for details. -

    -But what if we want to choose either news or blogs, but not both? -This can be achieved using a choice: -

    -
    -<page id="fourSquare" layout="column">
    -    <section layout="row"> <section;>
    -    <renderer name="blueSection">
    -            <choice>
    -                <source name="news"/>
    -                <source name="blog">
    -                    <renderer name="newBlogHitStyle"/>
    -                </source>
    -            </choice>
    -        <section/>
    -        <section source="web"/>
    -    </section>
    -    <section layout="row">
    -        <section source="image"/>
    -        <section source="video"/>
    -    </section>
    -</page>
    -
    -

    -We can insert choices anywhere in a template, for example choose to -show either the first or the second row rather than both: -

    -
    -<page id="fourSquare" layout="column">
    -    <choice>
    -        <section layout="row">
    -            <section;>
    -                <renderer name="blueSection">
    -                <choice>
    -                    <source name="news"/>
    -                    <source name="blog">
    -                        <renderer name="newBlogHitStyle"/>
    -                    </source>
    -                </choice>
    -            <section/>
    -            <section source="web"/>
    -        </section>
    -        <section layout="row">
    -            <section source="image"/>
    -            <section source="video"/>
    -        </section>
    -    </choice>
    -</page>
    -
    -

    -If we wanted to choose between two groups of multiple sections (or sources), -this can be done by adding an enclosing alternative tag around each group. -For the common special case of assigning a set of elements to a set of placeholders, -a choice can contain a map tag instead of a list of alternatives. -See the reference for details. -

    - - - -

    Using Choice Resolvers

    -

    -If templates including choices are used, some component must resolve -those choices given each query and result. The system includes some -resolvers for demo and testing purposes, but a proper optimizing -resolver must be deployed as part of the application. -This section describes how to create, deploy and choose a resolver to use at runtime. -

    - - -

    Writing a Resolver

    -

    -Resolvers are subclasses of - -com.yahoo.search.pagetemplates.engine.Resolver. -This API defines a method which accepts the page template in use -(which contains the choices), the Query/Result pair and returns a Resolution. -It is called at runtime once for every query which uses a page template. -

    -There are also some helper methods which makes it simple to write -resolvers which make each choice independently. Here is an example -resolver which makes all choices by random using this helper methods: -

    -
    -package com.yahoo.search.pagetemplates.engine.resolvers;
    -
    -import com.yahoo.search.Query;
    -import com.yahoo.search.Result;
    -import com.yahoo.search.pagetemplates.engine.*;
    -import com.yahoo.search.pagetemplates.model.*;
    -
    -import java.util.*;
    -
    -/** A resolver which makes all choices by random. */
    -public class RandomResolver extends Resolver {
    -
    -    private Random random=new Random(System.currentTimeMillis()); // Use of this is multithread safe
    -
    -    /** Chooses the last alternative of any choice */
    -    @Override
    -    public void resolve(Choice choice, Query query, Result result, Resolution resolution) {
    -        resolution.addChoiceResolution(choice,random.nextInt(choice.alternatives().size()));
    -    }
    -
    -    /** Chooses a mapping which is always by the literal order given in the source template */
    -    @Override
    -    public void resolve(MapChoice choice,Query query,Result result,Resolution resolution) {
    -        Map<String, List<PageElement>> mapping=new HashMap<String, List<PageElement>>();
    -        // Draw a random element from the value list on each iteration and assign it to a placeholder
    -        List<String> placeholderIds=choice.placeholderIds();
    -        List<List<PageElement>> valueList=new ArrayList<List<PageElement>>(choice.values());
    -        for (String placeholderId : placeholderIds)
    -            mapping.put(placeholderId,valueList.remove(random.nextInt(valueList.size())));
    -        resolution.addMapChoiceResolution(choice,mapping);
    -    }
    -
    -}
    -
    - - -

    Deploying a Resolver

    -

    -Resolvers must be packaged as -OSGI bundles for deployment, -see container components. -

    -The packaged component is added to the components/ -directory of the application package. -

    - The page template searcher must be configured with a list of the - resolvers which should be available. This is done by expanding the - page template searcher configuration with a components configuration: -

    -
    -<searcher id="com.yahoo.search.pagetemplates.PageTemplateSearcher" bundle="the name in <artifactId> in your pom.xml" >
    -    <config name="container.components">
    -        <component index="0">
    -            <id>default</id>
    -            <classId>com.yahoo.my.Resolver1</classId>
    -            <bundle>myBundleSymbolicName</bundle>
    -        </component>
    -
    -        <component index="1">
    -            <id>mySecondResolver</id>
    -            <classId>com.yahoo.my.Resolver2</classId>
    -            <bundle>myBundleSymbolicName</bundle>
    -        </component>
    -    </config>
    -</searcher>
    -
    -

    -With this, the application is -deployed as usual. -

    - - -

    Choosing a Resolver

    -

    -The resolver to use is determined by setting the query property -page.resolver to the id (and optionally version) of the resolver component - -either in the request, in a query profile or programmatically. -

    -Two templates suitable for testing purposes are always available: -native.random, which makes each choice by random, -and native.deterministic which selects the last -alternative of each choice. -

    - If the page.resolver parameter is not set, the resolver having the -id default is used. If no default resolver is deployed -the random resolver is used. -

    - - - -

    Examples

    -

    -This section contains a few complete examples of page templates. -

    -A blending search result page: -

    -
    -<page id="slottingSerp" layout="mainAndRight">
    -    <section layout="column" region="main" source="*"/>
    -    <section layout="column" region="right" source="ads"/>
    -</page>
    -
    -

    A richer search result page:

    -
    -<page id="richSerp" layout="mainAndRight">
    -    <section layout="row" placement="main">
    -        <section layout="column" description="left main pane">
    -            <section layout="row" max="5" description="Bar of images, from one of two possible sources">
    -                <choice>
    -                    <source name="images"/>
    -                    <source name="flickr"/>
    -                </choice>
    -            </section>
    -        <section max="1" source="local map video ticker weather"
    -                    description="A single relevant graphically rich element"/>
    -        <section max="10" source="web news"
    -                    description="Various kinds of traditional search results"/>
    -        </section>
    -            <section layout="column" order="[source]" source="answers blogs twitter"
    -                        description="right main pane, ugc stuff, grouped by source"/>
    -        </section>
    -    <section layout="column" source="ads" region="right"/>
    -</page>
    -
    -

    A mapping of multiple source modules to places on the page:

    -
    -<page id="MapSourcesToSections" layout="column" description="4 sources are assigned to a section each">
    -
    -    <section layout="row" description="row 1">
    -        <section id="box1"><placeholder id="box1source"/></section>
    -        <section id="box2"><placeholder id="box2source"/></section>
    -    </section>
    -    <section layout="row" description="row 2">
    -        <section id="box3"><placeholder id="box3source"/></section>
    -        <section id="box4"><placeholder id="box4source"/></section>
    -    </section>
    -
    -    <choice method="myMethod">
    -        <map to="box1source box2source box3source box4source">
    -            <source name="source1"/>
    -            <source name="source2"/>
    -            <source name="source3"/>
    -            <source name="source4"/>
    -        </map>
    -    </choice>
    -
    -</page>
    -
    diff --git a/en/querying/page-templates.mdx b/en/querying/page-templates.mdx new file mode 100644 index 0000000000..c68f01808b --- /dev/null +++ b/en/querying/page-templates.mdx @@ -0,0 +1,282 @@ +--- +title: "Page Templates" +--- + +When multiple kinds of data is fetched for a request, the application must decide how to lay out the data to return to the user. *Page templates* allows such page layouts to be defined as XML configuration files - one file per layout, corresponding to one use case. + +The layouts are *structural* - they do not specify widths and heights, colors and similar, but define the various boxed components that will make up the page, and their ordering and nesting. It is also assumed that the complete application includes a *frontend* which is capable of rendering finished pages from result laid out by a template. + +Page layouts may contain *choices* which specify alternative versions of the template. The choices in a template are taken by a *resolver* component at run time. Given an optimizing resolver the system can then learn to make the right choices given each particular query and result. An optimizing resolver is not bundled with the platform but must be added as a component. + +This document describes how to get started, explains the [page template language](#introduction) and [how to add a choice resolver](#using-choice-resolvers). A complete reference of all the permissible content of page templates is found in the [page template reference](../reference/querying/page-templates). + +## Getting Started + +A page template is an XML file which is placed in the directory `search/page-templates/` in the [application package](../basics/applications). To start using page templates: + +- Create template XML files as shown [below](#introduction) in `[app-package]/page-templates/` +- Add the searcher [com.yahoo.search.pagetemplates.PageTemplateSearcher](https://javadoc.io/doc/com.yahoo.vespa/container-search/latest/com/yahoo/search/pagetemplates/PageTemplateSearcher) to the default [search chain](../reference/applications/services/search) in *services.xml*. +- [deploy](../basics/applications#deploying-applications) the application package. +- Add these query parameters: `page.id=[comma-separated list of page id's]` and `presentation.format=[page](../reference/querying/page-result-format)`. + +The results returned will be as defined by the page template selected for each query. + +The source names used in page templates are the same as those defined in the [federation](/en/querying/federation) setup, and/or of any internal search clusters defined in the application. + +A presentation layer (frontend) which understands the results created by the page templates in use must be set up or created to produce rendered pages. That is beyond the scope of this document. + +## Introduction to Page Templates + +A page template is an XML file which contains a `` tag at the top level. The page element must have an `id` attribute, where the file name is the same as the id, followed by *.xml*. If the id is *default*, this template will be used whenever no template is specified in the query. The templates may also be versioned, see [Component Versioning](../reference/applications/components#component-versioning). + +A page template consist of nested *sections* which correspond to screen areas in the final layout. The top level section is defined by the page itself, while further sections can be defined by explicit `
    ` tags. Each section may set a layout which will be used by the frontend renderer to lay out its content - `column` and `row` must be supported by all renderers, while some renderers may specify additional layouts. Each section may also specify sources of data which should be placed in the section. Renderers must be able to render multiple data items from different sources in a section. + +For example, this template creates a page consisting of four equally large regions containing one source each: + +```xml + +
    +
    +
    +
    +
    +
    +
    +
    + +``` + +To use this template, save it as *[application-package]/page-templates/fourSquare.xml*. + +Suppose we want to extend this template to be able to also show blogs in the "news" section. This can be done as follows: + +```xml + +
    +
    +
    +
    +
    +
    +
    +
    + +``` + +Data items from each possible source has a rendering implemented by the frontend. These renderers are used when nothing is specified in the template. If some alternative rendering is desired, this can be specified by a `renderer` tag. The same is true for rendering of the sections themselves. Here we specify a different renderer for blog data items (hits), as well as for the entire *news/blog* section. + +```xml + +
    +
    + + + + +
    +
    +
    +
    +
    +
    +
    + +``` + +Note that in order to add a renderer subelement, we now specify the blog source by a tag rather than by an attribute. These two forms are equivalent - the attribute variant is just a shorthand syntax. + +Sources and renderers can be given arbitrary key-value parameters - see the [reference](../reference/querying/page-templates) for details. + +But what if we want to choose either news or blogs, but not both? This can be achieved using a choice: + +```xml + +
    + + + + + + + +
    +
    +
    +
    +
    +
    +
    + +``` + +We can insert choices anywhere in a template, for example choose to show either the first or the second row rather than both: + +```xml + + +
    + + + + + + + + +
    +
    +
    +
    +
    +
    +
    + + +``` + +If we wanted to choose between two groups of multiple sections (or sources), this can be done by adding an enclosing `alternative` tag around each group. For the common special case of assigning a set of elements to a set of placeholders, a choice can contain a `map` tag instead of a list of alternatives. See the [reference](../reference/querying/page-templates) for details. + +## Using Choice Resolvers + +If templates including choices are used, some component must resolve those choices given each query and result. The system includes some resolvers for demo and testing purposes, but a proper optimizing resolver must be deployed as part of the application. This section describes how to create, deploy and choose a resolver to use at runtime. + +### Writing a Resolver + +Resolvers are subclasses of [com.yahoo.search.pagetemplates.engine.Resolver](https://javadoc.io/doc/com.yahoo.vespa/container-search/latest/com/yahoo/search/pagetemplates/engine/Resolver). This API defines a method which accepts the page template in use (which contains the choices), the Query/Result pair and returns a Resolution. It is called at runtime once for every query which uses a page template. + +There are also some helper methods which makes it simple to write resolvers which make each choice independently. Here is an example resolver which makes all choices by random using this helper methods: + +```java expandable +package com.yahoo.search.pagetemplates.engine.resolvers; + +import com.yahoo.search.Query; +import com.yahoo.search.Result; +import com.yahoo.search.pagetemplates.engine.*; +import com.yahoo.search.pagetemplates.model.*; + +import java.util.*; + +/** A resolver which makes all choices by random. */ +public class RandomResolver extends Resolver { + + private Random random=new Random(System.currentTimeMillis()); // Use of this is multithread safe + + /** Chooses the last alternative of any choice */ + @Override + public void resolve(Choice choice, Query query, Result result, Resolution resolution) { + resolution.addChoiceResolution(choice,random.nextInt(choice.alternatives().size())); + } + + /** Chooses a mapping which is always by the literal order given in the source template */ + @Override + public void resolve(MapChoice choice,Query query,Result result,Resolution resolution) { + Map> mapping=new HashMap>(); + // Draw a random element from the value list on each iteration and assign it to a placeholder + List placeholderIds=choice.placeholderIds(); + List> valueList=new ArrayList>(choice.values()); + for (String placeholderId : placeholderIds) + mapping.put(placeholderId,valueList.remove(random.nextInt(valueList.size()))); + resolution.addMapChoiceResolution(choice,mapping); + } + +} +``` + +### Deploying a Resolver + +Resolvers must be packaged as [OSGI bundles](https://en.wikipedia.org/wiki/Osgi#Bundles) for deployment, see [container components](../applications/components). + +The packaged component is added to the `components/` directory of the [application package](../basics/applications). + +The page template searcher must be configured with a list of the resolvers which should be available. This is done by expanding the page template searcher configuration with a *components* configuration: + +```xml + + + + default + com.yahoo.my.Resolver1 + myBundleSymbolicName + + + + mySecondResolver + com.yahoo.my.Resolver2 + myBundleSymbolicName + + + +``` + +With this, the application is [deployed](../basics/applications#deploying-applications) as usual. + +### Choosing a Resolver + +The resolver to use is determined by setting the query property `page.resolver` to the id (and optionally version) of the resolver component - either in the request, in a query profile or programmatically. + +Two templates suitable for testing purposes are always available: `native.random`, which makes each choice by random, and `native.deterministic` which selects the last alternative of each choice. + +If the `page.resolver` parameter is not set, the resolver having the id `default` is used. If no default resolver is deployed the random resolver is used. + +## Examples + +This section contains a few complete examples of page templates. + +A blending search result page: + +```xml + +
    +
    + +``` + +A richer search result page: + +```xml + +
    +
    +
    + + + + +
    +
    +
    +
    +
    +
    +
    + +``` + +A mapping of multiple source modules to places on the page: + +```xml + + +
    +
    +
    +
    +
    +
    +
    +
    + + + + + + + + + + +
    +``` diff --git a/en/querying/query-api.html b/en/querying/query-api.html deleted file mode 100644 index e1b5d29dbb..0000000000 --- a/en/querying/query-api.html +++ /dev/null @@ -1,1027 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Query API" -redirect_from: -- /en/query-api.html ---- - -

    - Use the Vespa Query API to query, rank and organize data. Example: -

    -
    {% raw %}
    -$ vespa query "select * from music where year > 2001" \
    -  "ranking=rank_albums" \
    -  "input.query(user_profile)={{cat:pop}:0.8,{cat:rock}:0.2,{cat:jazz}:0.1}"
    -{% endraw %}
    -

    - Simplified, a query has the following components: -

    -
      -
    • Input data
    • -
    • Ranking and grouping specification
    • -
    • Results
    • -
    • Other execution parameters
    • -
    -

    - This guide is an introduction to the more important elements in the API - - refer to the Query API reference for details. - See query execution below for data flow. -

    -

    - Best practice for lexical queries is submitting the user-generated query as-is, - then use Searcher components to implement additional logic. -

    -

    - See the client library for options. -

    - - -

    Input

    -

    - Input data is both structured data and unstructured text: -

    -
    {% raw %}
    -$ vespa query "select * from music where artist contains \"coldplay\" and default contains text(@q)" \
    -  "q=head"
    -{% endraw %}
    -

    - The first line is the YQL query string, - that has both structured input (artist=coldplay) - and a reference to unstructured user input. - The user input is then given in the second line in the q parameter. -

    - Separating the structured data from the unstructured reliefs the application code from - interpreting and sanitizing the input data - it is essentially a blob. - Vespa can then use heuristics to deduct the user's intention. - User input can also be expressed in the - simple query language using - the userQuery operator. -

    - Finally, input data can also be ranking query features - - here the query feature is called user_profile: -

    -
    {% raw %}
    -$ vespa query "select * from music where artist contains \"coldplay\" and default contains text(@q)" \
    -  "q=head" \
    -  "ranking=rank_albums" \
    -  "input.query(user_profile)={{cat:pop}:0.8,{cat:rock}:0.2,{cat:jazz}:0.1}"
    -{% endraw %}
    -

    - See query execution below. -

    - - -

    Input examples

    -
    -$ vespa query "select * from sources * where default contains text(@animal)" \
    -  "animal=panda"
    -
    -

    - The text() operator will access the query property "animal", - and tokenize the property value into a weakAnd query, - resulting in: -

    -

    - -select * from sources * where weakAnd(default contains "panda") - -

    -
    -

    Changing the value of "animal" without changing the rest of the expression:

    -
    -$ vespa query "select * from sources * where default contains text(@animal)" \
    -  "animal=panda smokey"
    -
    -

    The result is:

    -

    - -select * from sources * where weakAnd(default contains "panda", default contains "smokey") - -

    -
    -

    Combining multiple query properties, and having a more complex expression:

    -
    -$ vespa query "select * from sources * where range(year, 1963, 2014) and (default contains text(@animal) or default contains text(@teddy))" \
    -  "animal=panda" \
    -  "teddy=bear roosevelt"
    -
    -

    The resulting YQL expression is:

    -

    - -select * from sources * where range(year, 1963, 2014) and (weakAnd(default contains "panda") or weakAnd(default contains "bear", default contains "roosevelt")) - -

    - - -

    Fieldset

    -

    Use a fieldset to query multiple fields in the where clause. In the schema:

    -
    -fieldset text_content {
    -    fields: title, description
    -}
    -
    -

    Query both fields using the fieldset:

    -
    -$ vespa query 'select * from sources where text_content contains text(@q)' \
    -  'q=query a fieldset'
    -
    -

    - Fields in the fields set must have the same match modes - - read more in the reference. -

    - - -

    Query Profiles

    -

    - Use a query profile to store - query parameters in configuration. - This makes query strings shorter, and makes it easy to modify queries by modifying configuration only. - Use cases are setting query properties for different markets, parameters that do not change, and so on. - Query profiles can be nested, versioned and use inheritance. -

    - - -

    Geo Filter and Ranking

    -

    - Filter by position using latitude and longitude to implement geo search. - DistanceToPath is a - rank function based on closeness. - Using ranking can often improve results instead of geo - filtering. -

    - - -

    Parameter substitution

    -

    - Parameter substitution lets you provide query values as request parameters - instead of inserting this into the YQL string itself. - This simplifies query generation, separating the value of the string/set/array from the YQL string - - i.e. the value will not corrupt the YQL string if it contains YQL-like syntax: -

    -
      -
    • - Simplify query generation, separating the value of the set/array from the YQL string. - -
    • -
    • Speed up query parsing. Using parameter substitution accelerates string parsing.
    • -
    • Reduce duplication.
    • -
    -

    - In its simplest form, - use text() for strings: -

    - -
    -... where default contains text(@user_input)&user_input=free+text
    -
    -

    Lists, maps and arrays can also be used - examples:

    -
    -# Simple example: provide a set for the IN operator
    -... where id in (@my_set)&my_set=10,20,30
    -
    -# Same set, but use the set as a block-list (exclude items in the set)
    -... where !(id in (@my_set))&my_set=10,20,30
    -
    -# Use a weightedSet operator
    -... where weightedSet(field, @my_set)&my_set={a:1,b:2}
    -
    -

    - It is also great to eliminate data duplication, - from Vespa 8.287 one can use parameter substitution with embed:

    -
    -$ vespa query \
    -  'yql=select id, from product where {targetHits:10}nearestNeighbor(embedding, query_embedding) or userQuery()' \
    -  'input.query(query_embedding)=embed(transformer, @query)' \
    -  'input.query(query_tokens)=embed(tokenizer, @query)' \
    -  'query=running shoes for kids, white'
    -
    -

    - Note the use of the parameter named query - used by the userQuery() operator. - Also note the value substituted in the embed functions. -

    -

    - See the reference - for a complete list of formats. -

    - - - -

    Ranking

    -

    - Ranking specifies the computation of the query and data. - It assigns scores to documents, and returns documents ordered by score. - A rank profile - is a specification for how to compute a document's score. - An application can have multiple rank profiles, to run different computations. - Example, a query specifies query categories and a user embedding - (from the tensor user guide): -

    -
    -rank-profile product_ranking inherits default {
    -    inputs {
    -        query(q_category) tensor<float>(category{})
    -        query(q_embedding) tensor<float>(x[4])
    -    }
    -
    -    function p_sales_score() {
    -        expression: sum(query(q_category) * attribute(sales_score))
    -    }
    -
    -    function p_embedding_score() {
    -        expression: closeness(field, embedding)
    -    }
    -
    -    first-phase {
    -        expression: p_sales_score() + p_embedding_score()
    -    }
    -    match-features: p_sales_score() p_embedding_score()
    -}
    -
    -
    {% highlight shell %}
    -vespa query 'yql=select * from product where {targetHits:1}nearestNeighbor(embedding,q_embedding)' \
    -  'input.query(q_embedding)=[1,2,3,4]' \
    -  'input.query(q_category)={"Tablet Keyboard Cases":0.8, "Keyboards":0.3}' \
    -  'ranking=product_ranking'
    -{% endhighlight %}
    -{% include note.html content='In this example, input.query(q_embedding) is short for -ranking.features.query(q_embedding) - -see the reference -for tensor formats.' %} -

    - Results can be ordered using sorting instead of ranking. -

    -

    - The above rank profile does not do text ranking - - there are however such profiles built-in. - Text search is described in more detail in Text Matching - - find information about normalizing, prefix search and linguistics there. -

    - - - -

    Grouping

    -

    - Grouping is a way to group documents in the result set after ranking. - Example, return max 3 albums per artist, grouped on year: -

    -
    -$ vespa query "select * from music where true limit 0 | all(group(year) each(max(3) each(output(summary())) ) )"
    -
    -

    - Fields used in grouping must be attributes. - The grouping expression is part of the YQL query string, appended at the end. -

    - Applications can group all documents (select all documents in YQL). - Using limit 0 returns grouping results only. -

    - - - -

    Results

    -

    -All fields are returned in results by default. -To specify a subset of fields, use document summaries. -When searching text, having a static abstract of the document in a field, or -using a dynamic summary -can both improve the visual relevance of the search, and cut bandwidth used. -

    -The default output format is JSON. -A binary CBOR format is also available via -format=cbor - -responses are smaller and faster to render, especially for numeric data, -and is a drop-in replacement that produces identical results when deserialized. -Write a custom Renderer to generate results in other formats. -

    -Read more on request-response processing - -use this to write code to manipulate results. -

    - - - -

    Query execution

    -Query execution - from query to response -

    - Phases: -

    -
      -
    1. Query processing: - Normalizations, rewriting and enriching. Custom logic in search chains
    2. -
    3. Matching, ranking and grouping/aggregation: - This phase dispatches the query to content nodes
    4. -
    5. Result processing, rendering: Content fetching - and snippeting of the top global hits found in the query phase
    6. -
    -

    - The above is a simplification - if the query also specifies result grouping, - the query phase might involve multiple phases or round-trips between the container and content nodes. - See life of a query - for a deeper dive into query execution details. -

    -

    - Use trace.explainlevel to analyze the query plan. - Use these hints to modify the query plan: -

    -
      -
    • - Use ranked: false query annotations - to speed up evaluation -
    • -
    • - Use capped range search - to efficiently implement top-k selection for ranking a subset of the documents in the index. -
    • -
    - - -

    Query processing and dispatch

    -
      -
    1. - A query is sent from a front-end application to a container node - using the Query API or in any custom request format handled by a - custom request handler, - which translates the custom request format to native Vespa APIs. -
    2. -
    3. -

      - Query pre-processing, like linguistic processing - and query rewriting, - is done in built-in and custom search chains - - see searcher development. -

      -

      - The default search chain is vespa - - find installed components in this chain by inspecting ApplicationStatus - like in the quick-start. - Adding &trace.level=4 (or higher) to the query will - emit the components invoked in the query, and is useful to analyze ordering. -

      -

      - This is the integration point to plug in code to enrich a query - example: - Look up user profile data from a user ID in the request. - Set &trace.level=2 to inspect the search chain components. -

      -
    4. -
    5. -

      - The query is sent from the container to the content cluster - - see federation for more details. - An application can have multiple content clusters - Vespa searches in all by default. - Federation controls how to query the clusters, - sources names the clusters - The illustration above has one content cluster but multiple is fully supported - and allows scaling document types differently. - E.g. a tweet document type can be indexed in a separate content cluster - from a user document type, enabling independent scaling of the two. -

      - Query processing and dispatch -
    6. -
    - - -

    Matching, ranking, grouping

    -
      -
    1. - At this point the query enters one or more content clusters. - In a content cluster with grouped distribution, - the query is dispatched to all content nodes within a single group using a - dispatch policy, - while with a flat single group content cluster the query is dispatched to all content nodes. -
    2. -

      - The query arrives at the content nodes which performs matching, - ranking and aggregation/grouping over the set of documents - in the Ready sub database. By default, Vespa uses DAAT where the matching and first-phase score calculation is interleaved and not two separate, sequential phases. - vespa-proton does matching over the ready documents - and ranks as specified with the request/schema. - Each content node matches and ranks a subset of the total document corpus - and returns the hits along with meta information - like total hits and sorting and grouping data, if requested. -

      - Queries -
    3. - Once the content nodes within the group have replied within the timeout, - max-hits / top-k - results are returned to the container for query phase result processing. - In this phase, the only per hit data available is the internal global document ID (gid) and the ranking score. - There is also result meta information like coverage and total hit count. - Additional hit specific data, like the contents of fields, - is not available until the result processing phase has completed the content fetching. -
    4. -
    - - -

    Result processing (fill) phase

    -
      -
    1. - When the result from the query phase is available, - a custom chained searcher component - can process the limited data available from the first search phase - before contents of the hits is fetched from the content nodes. - The fetching from content nodes is lazy and is not invoked before rendering the response, - unless asked for earlier by a custom searcher component. -
    2. - Only fields in the requested document summaries is fetched from content nodes. - The summary request goes directly to the content nodes that produced the result from the query phase. -
    3. - After the content node requests have completed, - the full result set can be processed further by custom components - (e.g. doing result deduping, top-k re-ranking), - before rendering the response. -
    4. -
    - - - -

    HTTP

    -

    - The Vespa Team does not recommend any specific HTTP client, since we haven't done any systematic evaluation. - We have most experience with the Apache HTTP client. - See also HTTP best practices - (for Vespa Cloud, but most of it is generally applicable). - Also see a discussion in #24534. -

    -

    -Use GET or POST. Parameters can either be sent as GET-parameters or posted as JSON, these are equivalent: -

    -
    -$ curl -H "Content-Type: application/json" \
    -    --data '{"yql" : "select * from sources * where default contains \"coldplay\""}' \
    -    http://localhost:8080/search/
    -
    -$ curl http://localhost:8080/search/?yql=select+%2A+from+sources+%2A+where+default+contains+%22coldplay%22
    -
    - - -

    Using POST

    -

    - The format is based on the Query API reference, - and has been converted from the flat dot notation to a nested JSON-structure. - The request-method must be POST and the Content-Type must be "application/json", e.g.: -

    -
    -$ curl -X POST -H "Content-Type: application/json" --data '
    -  {
    -      "yql": "select * from sources * where true",
    -      "offset": 5,
    -      "ranking": {
    -          "matchPhase": {
    -              "ascending": true,
    -              "maxHits": 15
    -          }
    -      },
    -      "presentation" : {
    -          "bolding": false,
    -          "format": "json"
    -      }
    -  }' \
    -  http://localhost:8080/search/
    -
    - -{% include note.html content="Try the -Query Builder -application!" %} - -{% include important.html content="Security filters can block GET and POST requests differently. -This can block POSTed queries." %} - - -

    HTTP

    -

    - Configure the http server - - e.g. set requestHeaderSize to configure URL length (including headers): -

    -
    {% highlight xml %}
    -
    -    
    -        
    -            
    -                32768
    -            
    -        
    -    
    -
    -{% endhighlight %}
    -

    - HTTP keepalive is supported. -

    - Values must be encoded according to standard URL encoding. - Thus, space is encoded as +, + as %2b and so on - - see RFC 2396. -

    - HTTP status codes are found in the - Query API reference. - Also see - Stack Overflow question. -

    - -

    When implementing a client for the query API, consider the following guidelines for handling HTTP status codes:

    -

    Client errors vs. server errors

    -

    - In general clients should only retry requests on server errors (5xx) - not on client errors (4xx). - For example, a client should not retry a request after receiving a 400 Bad Request response. -

    - -

    Back-pressure handling

    -

    - Be careful when handling 5xx responses, especially 503 Service Unavailable and 504 Gateway Timeout. - These responses typically indicate an overloaded system, and blindly retrying without backoff will only worsen the situation. - For example, 503 Service Unavailable is returned whenever there are no available search handler threads - to serve the request. This is a clear indication of back-pressure from the system, and clients should - reduce overall throughput and implement appropriate throttling mechanisms to avoid exacerbating the overload condition. -

    - - -

    Timeout

    -

    - See the reference for how to set the query timeout. - Common questions: -

    -
      -
    • -

      - Does the timeout apply to the whole query or just from when it is sent to the content cluster? - If a Searcher goes to sleep in the container for 2*timeout, - will the caller still get a response indicating a timeout? -

      -

      - The timeout applies to the whole query, both container and content node processing. - However, the timeout handling is cooperative - - if having Searchers that are time-consuming or access external resources, - the Searcher code should check - Query.getTimeLeft(). - So, in this case, you will time out, but only after 2*timeout + some more. -

      -
    • -
    • -

      - During multiphase searching, is the query timeout set for each individual searcher, - or is the query timeout set for the entire search chain? -

      -

      - The timeout is for the entire query (and most Searchers don’t check timeout - - use Query.getTimeLeft()). - E.g., if a Search Chain has 3 Searchers, - it is OK for 1 Searcher to take 497 ms and 2 Searchers to each take 1 ms for a query timeout of 500 ms. -

      -
    • -
    • -

      - If we asynchronously execute several search chains, - can we set different query timeouts for each of these chains - plus a separate overall timeout for the searcher that performs the asynchronous executions? -

      -

      - You can set a different timeout in each cloned query you send to any of those chains, - and you can specify the timeout when waiting for responses from them. -

      -
    • -
    - - - - - - -

    Error handling

    -

    - Check for a root: error element in the result: -

    -
    {% highlight json %}
    -{
    -    "root": {
    -        "errors": [
    -            {
    -                "code": 8,
    -                "summary": "Error in search reply.",
    -                "source": "music",
    -                "message": "Could not locate attribute for grouping number 0 : Failed locating attribute vector 'year'. Ignoring this grouping."
    -            }
    -        ],
    -{% endhighlight %}
    -

    - -

    - - - -

    Troubleshooting

    -

    - If Vespa cannot generate a valid search expression from the query string, - it will issue the error message Null query. - To troubleshoot, add &trace.level=2 to the request. - A missing yql parameter will also emit this error message. -

    - - -

    Query tracing

    -

    - Use query tracing to debug query execution. - Enable by using trace.level=1 (or higher). - Add trace.timestamps=true - for timing info for every searcher invoked. - Find a trace example in the result examples below, - and try the - practical search performance guide. -

    -

    - In custom code, use - Query.trace - to add trace output. -

    - - -

    Large memory usage

    -

    Queries that allocate more than 2G RAM will log messages like:

    -
    -mmap 2727 of size 8589934592 from : search::attribute::PostingListMerger<int>::reserveArray(unsigned int, unsigned long)(0x40001513eef0)
    -
    -(0x400013595334) from (0x400013593acc) from operator new(unsigned long)(0x400013592f88) from search::attribute::PostingListMerger<int>::reserveArray(unsigned int, unsigned long)(0x40001513eef0) from search::attribute::PostingListSearchContextT<int>::fetchPostings(search::queryeval::ExecuteInfo const&, bool)(0x400015159a38) from search::queryeval::SameElementBlueprint::fetchPostings(search::queryeval::ExecuteInfo const&)(0x4000154f36fc) from search::queryeval::IntermediateBlueprint::fetchPostings(search::queryeval::ExecuteInfo const&)(0x40001549ad38) from proton::matching::MatchToolsFactory::MatchToolsFactory(proton::matching::QueryLimiter&, vespalib::Doom const&, proton::matching::ISearchContext&, search::attribute::IAttributeContext&, search::engine::Trace&, std::basic_string_view<char, std::char_traits<char> >, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, ...
    -
    -1 mappings of accumulated size 8589934592
    -
    -

    This does not necessarily indicate that something is wrong, e.g., range searches use much memory.

    - - - -

    Result examples

    -

    A regular query result:

    -
    {% highlight json %}
    -{
    -    "root": {
    -        "id": "toplevel",
    -        "relevance": 1.0,
    -        "fields": {
    -            "totalCount": 1
    -        },
    -        "coverage": {
    -            "coverage": 100,
    -            "documents": 3,
    -            "full": true,
    -            "nodes": 1,
    -            "results": 1,
    -            "resultsFull": 1
    -        },
    -        "children": [
    -            {
    -                "id": "id:mynamespace:music::a-head-full-of-dreams",
    -                "relevance": 0.16343879032006284,
    -                "source": "music",
    -                "fields": {
    -                    "sddocname": "music",
    -                    "documentid": "id:mynamespace:music::a-head-full-of-dreams",
    -                    "artist": "Coldplay",
    -                    "album": "A Head Full of Dreams",
    -                    "year": 2015,
    -                    "category_scores": {
    -                        "cells": [
    -                            {
    -                                "address": {
    -                                    "cat": "pop"
    -                                },
    -                                "value": 1.0
    -                            },
    -                            {
    -                                "address": {
    -                                    "cat": "rock"
    -                                },
    -                                "value": 0.20000000298023224
    -                            },
    -                            {
    -                                "address": {
    -                                    "cat": "jazz"
    -                                },
    -                                "value": 0.0
    -                            }
    -                        ]
    -                    }
    -                }
    -            }
    -        ]
    -    }
    -}
    -{% endhighlight %}
    -

    An empty result:

    -
    {% highlight json %}
    -{
    -    "root": {
    -        "fields": {
    -            "totalCount": 0
    -        },
    -        "id": "toplevel",
    -        "relevance": 1.0
    -    }
    -}
    -{% endhighlight %}
    -

    An error result:

    -
    {% highlight json %}
    -{
    -    "root": {
    -        "id": "toplevel",
    -        "relevance": 1.0,
    -        "fields": {
    -            "totalCount": 2
    -        },
    -        "coverage": {
    -            "coverage": 100,
    -            "documents": 4,
    -            "full": true,
    -            "nodes": 2,
    -            "results": 2,
    -            "resultsFull": 2
    -        },
    -        "errors": [
    -            {
    -                "code": 8,
    -                "summary": "Error in search reply.",
    -                "source": "music",
    -                "message": "Could not locate attribute for grouping number 0 : Failed locating attribute vector 'year'. Ignoring this grouping."
    -            }
    -        ],
    -{% endhighlight %}
    -

    A simple search application, many undefined fields. Result for the query - /search/?query=blues&hits=3&trace.level=2

    -
    {% highlight json %}
    -{
    -
    -    "trace": {
    -        "children": [
    -            {
    -                "message": "No query profile is used"
    -            },
    -            {
    -                "message": "Invoking chain 'vespa' [com.yahoo.prelude.statistics.StatisticsSearcher@native -> com.yahoo.prelude.querytransform.PhrasingSearcher@vespa -> ... -> federation@native]"
    -            },
    -            {
    -                "children": [
    -                    {
    -                        "message": "Detected language: ENGLISH"
    -                    },
    -                    {
    -                        "message": "Language ENGLISH determined by the characters in the terms."
    -                    },
    -                    {
    -                        "message": "Query parsed to: select * from sources * where default contains \"blues\" limit 3"
    -                    },
    -                    {
    -                        "message": "Child execution",
    -                        "children": [
    -                            {
    -                                "message": "Stemming: [select * from sources * where default contains ({\"origin\": {\"original\": \"blues\", \"offset\": 0, \"length\": 5}, \"stem\": false}\"blue\") limit 3]"
    -                            },
    -                            {
    -                                "message": "Lowercasing: [select * from sources * where default contains ({\"origin\": {\"original\": \"blues\", \"offset\": 0, \"length\": 5}, \"stem\": false, \"normalizeCase\": false}\"blue\") limit 3]"
    -                            },
    -                            {
    -                                "message": "sc0.num0 search to dispatch: query=[blue] timeout=5000ms offset=0 hits=3 grouping=0 : collapse=false restrict=[music]"
    -                            },
    -                            {
    -                                "message": "Current state of query tree: WORD[connectedItem=null connectivity=0.0 creator=ORIG explicitSignificance=false fromSegmented=false index=\"\" isRanked=true origin=\"(0 5)\" segmentIndex=0 significance=0.0 stemmed=true uniqueID=1 usePositionData=true weight=100 words=true]{\n \"blue\"\n}\n"
    -                            },
    -                            {
    -                                "message": "YQL+ representation: select * from sources * where default contains ({\"origin\": {\"original\": \"blues\", \"offset\": 0, \"length\": 5}, \"stem\": false, \"normalizeCase\": false, \"id\": 1}\"blue\") limit 3"
    -                            },
    -                            {
    -                                "message": "sc0.num0 dispatch response: Result (3 of total 10 hits)"
    -                            },
    -                            {
    -                                "message": "sc0.num0 fill to dispatch: query=[blue] timeout=5000ms offset=0 hits=3 grouping=0 : collapse=false restrict=[music] summary=[null]"
    -                            },
    -                            {
    -                                "message": "Current state of query tree: WORD[connectedItem=null connectivity=0.0 creator=ORIG explicitSignificance=false fromSegmented=false index=\"\" isRanked=true origin=\"(0 5)\" segmentIndex=0 significance=0.0 stemmed=true uniqueID=1 usePositionData=true weight=100 words=true]{\n \"blue\"\n}\n"
    -                            },
    -                            {
    -                                "message": "YQL+ representation: select * from sources * where default contains ({\"origin\": {\"original\": \"blues\", \"offset\": 0, \"length\": 5}, \"stem\": false, \"normalizeCase\": false, \"id\": 1}\"blue\") limit 3"
    -                            }
    -                        ]
    -                    },
    -                    {
    -                        "message": "Child execution"
    -                    }
    -                ]
    -            }
    -        ]
    -    },
    -    "root": {
    -        "id": "toplevel",
    -        "relevance": 1,
    -        "fields": {
    -            "totalCount": 10
    -        },
    -        "coverage": {
    -            "coverage": 100,
    -            "documents": 10,
    -            "full": true,
    -            "nodes": 1,
    -            "results": 1,
    -            "resultsFull": 1
    -        },
    -        "children": [
    -            {
    -                "id": "index:0/0/0/dfd9fcfa650b44545ef0b8b2",
    -                "relevance": "-Infinity",
    -                "source": "basicsearch",
    -                "fields": {
    -                    "sddocname": "music",
    -                    "title": "Electric Blues",
    -                    "artist": "",
    -                    "song": "",
    -                    "bgndata": "",
    -                    "sales": "NaN",
    -                    "pto": -1,
    -                    "mid": 2,
    -                    "ew": "blues",
    -                    "surl": "https://shopping.yahoo.com/shop?d=hab&id=1807865261",
    -                    "userrate": "NaN",
    -                    "pid": "",
    -                    "weight": "NaN",
    -                    "url": "",
    -                    "isbn": "",
    -                    "fmt": "",
    -                    "albumid": "",
    -                    "disp_song": "",
    -                    "pfrom": "NaN",
    -                    "bgnpfrom": "NaN",
    -                    "categories": "Blues",
    -                    "data": "",
    -                    "numreview": "NaN",
    -                    "bgnsellers": 0,
    -                    "image": "",
    -                    "artistspid": "",
    -                    "newestedition": "NaN",
    -                    "bgnpto": "",
    -                    "year": "NaN",
    -                    "did": "NaN",
    -                    "scorekey": "NaN",
    -                    "cbid": "NaN",
    -                    "summaryfeatures": "",
    -                    "documentid": "id:test:music::https://shopping.yahoo.com/shop?d=hab&id=1807865261"
    -                }
    -            },
    -            {
    -                "id": "index:0/0/0/273d384dc214386c934d793f",
    -                "relevance": "-Infinity",
    -                "source": "basicsearch",
    -                "fields": {
    -                    "sddocname": "music",
    -                    "title": "Delta Blues",
    -                    "artist": "",
    -                    "song": "",
    -                    "bgndata": "",
    -                    "sales": "NaN",
    -                    "pto": -1,
    -                    "mid": 2,
    -                    "ew": "blues",
    -                    "surl": "https://shopping.yahoo.com/shop?d=hab&id=1804905714",
    -                    "userrate": "NaN",
    -                    "pid": "",
    -                    "weight": "NaN",
    -                    "url": "",
    -                    "isbn": "",
    -                    "fmt": "",
    -                    "albumid": "",
    -                    "disp_song": "",
    -                    "pfrom": "NaN",
    -                    "bgnpfrom": "NaN",
    -                    "categories": "Blues",
    -                    "data": "",
    -                    "numreview": "NaN",
    -                    "bgnsellers": 0,
    -                    "image": "",
    -                    "artistspid": "",
    -                    "newestedition": "NaN",
    -                    "bgnpto": "",
    -                    "year": "NaN",
    -                    "did": "NaN",
    -                    "scorekey": "NaN",
    -                    "cbid": "NaN",
    -                    "summaryfeatures": "",
    -                    "documentid": "id:test:music::https://shopping.yahoo.com/shop?d=hab&id=1804905714"
    -                }
    -            },
    -            {
    -                "id": "index:0/0/0/b3c74a9bf3aea1e2260311c0",
    -                "relevance": "-Infinity",
    -                "source": "basicsearch",
    -                "fields": {
    -                    "sddocname": "music",
    -                    "title": "Chicago Blues",
    -                    "artist": "",
    -                    "song": "",
    -                    "bgndata": "",
    -                    "sales": "NaN",
    -                    "pto": -1,
    -                    "mid": 2,
    -                    "ew": "blues",
    -                    "surl": "https://shopping.yahoo.com/shop?d=hab&id=1804905710",
    -                    "userrate": "NaN",
    -                    "pid": "",
    -                    "weight": "NaN",
    -                    "url": "",
    -                    "isbn": "",
    -                    "fmt": "",
    -                    "albumid": "",
    -                    "disp_song": "",
    -                    "pfrom": "NaN",
    -                    "bgnpfrom": "NaN",
    -                    "categories": "Blues",
    -                    "data": "",
    -                    "numreview": "NaN",
    -                    "bgnsellers": 0,
    -                    "image": "",
    -                    "artistspid": "",
    -                    "newestedition": "NaN",
    -                    "bgnpto": "",
    -                    "year": "NaN",
    -                    "did": "NaN",
    -                    "scorekey": "NaN",
    -                    "cbid": "NaN",
    -                    "summaryfeatures": "",
    -                    "documentid": "id:test:music::https://shopping.yahoo.com/shop?d=hab&id=1804905710"
    -                }
    -            }
    -        ]
    -    }
    -}
    -{% endhighlight %}
    -

    - Result for the grouping query - - /search/?hits=0&yql=select * from sources * where sddocname contains purchase | all(group(customer) each(output(sum(price)))) - -

    -
    {% highlight json %}
    -{
    -
    -    "trace": {
    -        "children": [
    -            {
    -                "children": [
    -                    {
    -                        "message": "Child execution"
    -                    }
    -                ]
    -            }
    -        ]
    -    },
    -    "root": {
    -        "id": "toplevel",
    -        "relevance": 1,
    -        "fields": {
    -            "totalCount": 20
    -        },
    -        "coverage": {
    -            "coverage": 100,
    -            "documents": 20,
    -            "full": true,
    -            "nodes": 1,
    -            "results": 1,
    -            "resultsFull": 1
    -        },
    -        "children": [
    -            {
    -                "id": "group:root:0",
    -                "relevance": 1,
    -                "continuation": {
    -                    "this": ""
    -                },
    -                "children": [
    -                    {
    -                        "id": "grouplist:customer",
    -                        "relevance": 1,
    -                        "label": "customer",
    -                        "children": [
    -                            {
    -                                "id": "group:string:Jones",
    -                                "relevance": 9870,
    -                                "value": "Jones",
    -                                "fields": {
    -                                    "sum(price)": 39816
    -                                }
    -                            },
    -                            {
    -                                "id": "group:string:Brown",
    -                                "relevance": 8000,
    -                                "value": "Brown",
    -                                "fields": {
    -                                    "sum(price)": 20537
    -                                }
    -                            },
    -                            {
    -                                "id": "group:string:Smith",
    -                                "relevance": 6100,
    -                                "value": "Smith",
    -                                "fields": {
    -                                    "sum(price)": 19484
    -                                }
    -                            }
    -                        ]
    -                    }
    -                ]
    -            }
    -        ]
    -    }
    -}
    -{% endhighlight %}
    diff --git a/en/querying/query-api.mdx b/en/querying/query-api.mdx new file mode 100644 index 0000000000..e8996c1412 --- /dev/null +++ b/en/querying/query-api.mdx @@ -0,0 +1,807 @@ +--- +title: "Query API" +--- + +Use the Vespa Query API to query, rank and organize data. Example: + +```bash +$ vespa query "select * from music where year > 2001" \ + "ranking=rank_albums" \ + "input.query(user_profile)={{cat:pop}:0.8,{cat:rock}:0.2,{cat:jazz}:0.1}" +``` + +Simplified, a query has the following components: + +- Input data +- Ranking and grouping specification +- Results +- Other execution parameters + +This guide is an introduction to the more important elements in the API - refer to the [Query API reference](../reference/api/query) for details. See [query execution](#query-execution) below for data flow. + +## Input + +Input data is both structured data and unstructured text: + +```bash +$ vespa query "select * from music where artist contains \"coldplay\" and default contains text(@q)" \ + "q=head" +```bash +$ vespa query "select * from music where artist contains \"coldplay\" and default contains text(@q)" \ + "q=head" +```bash +$ vespa query "select * from music where artist contains \"coldplay\" and default contains text(@q)" \ + "q=head" \ + "ranking=rank_albums" \ + "input.query(user_profile)={{cat:pop}:0.8,{cat:rock}:0.2,{cat:jazz}:0.1}" +``` + +See [query execution](#query-execution) below. + +### Input examples + +```bash +$ vespa query "select * from sources * where default contains text(@animal)" \ + "animal=panda" +``` + +The text() operator will access the query property "animal", and tokenize the property value into a [weakAnd](../reference/querying/yql#grammar) query, resulting in: + +`select * from sources * where weakAnd(default contains "panda")` + +Changing the value of "animal" without changing the rest of the expression: + +```bash +$ vespa query "select * from sources * where default contains text(@animal)" \ + "animal=panda smokey" +``` + +The result is: + +`select * from sources * where weakAnd(default contains "panda", default contains "smokey")` + +Combining multiple query properties, and having a more complex expression: + +```bash +$ vespa query "select * from sources * where range(year, 1963, 2014) and (default contains text(@animal) or default contains text(@teddy))" \ + "animal=panda" \ + "teddy=bear roosevelt" +``` + +The resulting YQL expression is: + +`select * from sources * where range(year, 1963, 2014) and (weakAnd(default contains "panda") or weakAnd(default contains "bear", default contains "roosevelt"))` + +### Fieldset + +Use a fieldset to query multiple fields in the `where` clause. In the schema: + +```sd +fieldset text_content { + fields: title, description +} +``` + +Query both fields using the fieldset: + +```bash +$ vespa query 'select * from sources where text_content contains text(@q)' \ + 'q=query a fieldset' +``` + +Fields in the fields set must have the same match modes - read more in the [reference](../reference/schemas/schemas#fieldset). + +### Query Profiles + +Use a [query profile](../reference/api/query#queryprofile) to store query parameters in configuration. This makes query strings shorter, and makes it easy to modify queries by modifying configuration only. Use cases are setting query properties for different markets, parameters that do not change, and so on. Query profiles can be nested, versioned and use inheritance. + +### Geo Filter and Ranking + +Filter by position using latitude and longitude to implement [geo search](/en/querying/geo-search). [DistanceToPath](../reference/ranking/rank-features#distanceToPath\(name\).distance) is a [rank function](../basics/ranking) based on closeness. Using ranking can often improve results instead of geo filtering. + +### Parameter substitution + +Parameter substitution lets you provide query values as request parameters instead of inserting this into the YQL string itself. This simplifies query generation, separating the value of the string/set/array from the YQL string - i.e. the value will not corrupt the YQL string if it contains YQL-like syntax: + +- Simplify query generation, separating the value of the set/array from the YQL string. +- Speed up query parsing. Using parameter substitution accelerates string parsing. +- Reduce duplication. + +In its simplest form, use [text()](../reference/querying/yql#text) for strings: + +```text +... where default contains text(@user_input)&user_input=free+text +``` + +Lists, maps and arrays can also be used - examples: + +```text +# Simple example: provide a set for the IN operator +... where id in (@my_set)&my_set=10,20,30 + +# Same set, but use the set as a block-list (exclude items in the set) +... where !(id in (@my_set))&my_set=10,20,30 + +# Use a weightedSet operator +... where weightedSet(field, @my_set)&my_set={a:1,b:2} +``` + +It is also great to eliminate data duplication, from Vespa 8.287 one can use parameter substitution with `embed`: + +```bash +$ vespa query \ + 'yql=select id, from product where {targetHits:10}nearestNeighbor(embedding, query_embedding) or userQuery()' \ + 'input.query(query_embedding)=embed(transformer, @query)' \ + 'input.query(query_tokens)=embed(tokenizer, @query)' \ + 'query=running shoes for kids, white' +``` + +Note the use of the parameter named [query](../reference/api/query#model.querystring) used by the [userQuery()](../reference/querying/yql#userquery) operator. Also note the value substituted in the [embed](../rag/embedding#embedding-a-query-text) functions. + +See the [reference](../reference/querying/yql#parameter-substitution) for a complete list of formats. + +## Ranking + +[Ranking](../basics/ranking) specifies the computation of the query and data. It assigns scores to documents, and returns documents ordered by score. A [rank profile](../reference/api/query#ranking.profile) is a specification for how to compute a document's score. An application can have multiple rank profiles, to run different computations. Example, a query specifies query categories and a user embedding (from the [tensor user guide](../ranking/tensor-user-guide#ranking-with-tensors)): + +```sd +rank-profile product_ranking inherits default { + inputs { + query(q_category) tensor(category{}) + query(q_embedding) tensor(x[4]) + } + + function p_sales_score() { + expression: sum(query(q_category) * attribute(sales_score)) + } + + function p_embedding_score() { + expression: closeness(field, embedding) + } + + first-phase { + expression: p_sales_score() + p_embedding_score() + } + match-features: p_sales_score() p_embedding_score() +} +``` + +```bash +vespa query 'yql=select * from product where {targetHits:1}nearestNeighbor(embedding,q_embedding)' \ + 'input.query(q_embedding)=[1,2,3,4]' \ + 'input.query(q_category)={"Tablet Keyboard Cases":0.8, "Keyboards":0.3}' \ + 'ranking=product_ranking' +``` + + +**Note:** + +In this example, `input.query(q_embedding)` is short for `ranking.features.query(q_embedding)` - see the [reference](../reference/api/query#ranking.features) for tensor formats. + + +Results can be ordered using [sorting](../reference/querying/sorting-language) instead of ranking. + +The above rank profile does not do text ranking - there are however such profiles built-in. Text search is described in more detail in [Text Matching](/en/querying/text-matching) - find information about normalizing, prefix search and linguistics there. + +## Grouping + +[Grouping](/en/querying/grouping) is a way to group documents in the result set after ranking. Example, return max 3 albums per artist, grouped on year: + +```bash +$ vespa query "select * from music where true limit 0 | all(group(year) each(max(3) each(output(summary())) ) )" +``` + +Fields used in grouping must be [attributes](../content/attributes). The grouping expression is part of the YQL query string, appended at the end. + +Applications can group *all* documents (select all documents in YQL). Using `limit 0` returns grouping results only. + +## Results + +All fields are returned in results by default. To specify a subset of fields, use [document summaries](/en/querying/document-summaries). When searching text, having a static abstract of the document in a field, or using a [dynamic summary](../reference/schemas/schemas#summary) can both improve the visual relevance of the search, and cut bandwidth used. + +The default output format is [JSON](../reference/querying/default-result-format). A binary [CBOR](https://cbor.io/) format is also available via [format=cbor](../reference/api/query#presentation.format) - responses are smaller and faster to render, especially for numeric data, and is a drop-in replacement that produces identical results when deserialized. Write a custom [Renderer](../applications/result-renderers) to generate results in other formats. + +Read more on [request-response](../applications/processing) processing - use this to write code to manipulate results. + +## Query execution + +![Query execution - from query to response](/assets/img/query-to-response.svg) + +Phases: + + + +**Query processing**: Normalizations, rewriting and enriching. Custom logic in search chains + + +**Matching, ranking and grouping/aggregation:** This phase dispatches the query to content nodes + + +**Result processing, rendering:** Content fetching and snippeting of the top global hits found in the query phase + + + +The above is a simplification - if the query also specifies [result grouping](/en/querying/grouping), the query phase might involve multiple phases or round-trips between the container and content nodes. See [life of a query](../performance/sizing-search#life-of-a-query-in-vespa) for a deeper dive into query execution details. + +Use [trace.explainlevel](../reference/api/query#trace.explainlevel) to analyze the query plan. Use these hints to modify the query plan: + +- Use [ranked: false](../reference/querying/yql#ranked) query annotations to speed up evaluation +- Use [capped range search](../reference/querying/yql#numeric) to efficiently implement top-k selection for ranking a subset of the documents in the index. + +### Query processing and dispatch + + + +A query is sent from a front-end application to a container node using the *Query API* or in any custom request format handled by a custom [request handler](../applications/request-handlers), which translates the custom request format to native Vespa APIs. + + +Query pre-processing, like [linguistic processing](../linguistics/linguistics) and [query rewriting](../linguistics/query-rewriting), is done in built-in and custom [search chains](../applications/chaining) - see [searcher development](../applications/searchers). + +The default search chain is *vespa* - find installed components in this chain by inspecting `ApplicationStatus` like in the [quick-start](../basics/deploy-an-application-local). Adding `&trace.level=4` (or higher) to the query will emit the components invoked in the query, and is useful to analyze ordering. + +This is the integration point to plug in code to enrich a query - example: Look up user profile data from a user ID in the request. Set *&trace.level=2* to inspect the search chain components. + + +The query is sent from the container to the *content cluster* - see [federation](/en/querying/federation) for more details. An application can have multiple content clusters - Vespa searches in all by default. [Federation](/en/querying/federation) controls how to query the clusters, [sources](../reference/api/query#model.sources) names the clusters The illustration above has one content cluster but multiple is fully supported and allows scaling [document types](../basics/schemas) differently. E.g. a *tweet* document type can be indexed in a separate content cluster from a *user* document type, enabling independent scaling of the two. + +![Query processing and dispatch](/assets/img/query-dispatch.svg) + + + +### Matching, ranking, grouping + + + +At this point the query enters one or more [content clusters](../reference/applications/services/content). In a content cluster with [grouped distribution](../content/elasticity#grouped-distribution), the query is dispatched to all content nodes within a single group using a [dispatch policy](../reference/applications/services/content#dispatch-tuning), while with a flat single group content cluster the query is dispatched to all content nodes. + + +The query arrives at the content nodes which performs matching, [ranking](../basics/ranking) and aggregation/grouping over the set of documents in the [Ready sub database](../content/proton). By default, Vespa uses [DAAT](../performance/feature-tuning#hybrid-taat-daat) where the matching and first-phase score calculation is interleaved and not two separate, sequential phases. *vespa-proton* does matching over the *ready* documents and [ranks](../basics/ranking) as specified with the request/schema. Each content node matches and ranks a subset of the total document corpus and returns the hits along with meta information like total hits and sorting and grouping data, if requested. + +![Queries](/assets/img/proton-query.svg) + + + +Once the content nodes within the group have replied within the [timeout](../performance/graceful-degradation), [max-hits / top-k](../reference/applications/services/content#dispatch-tuning) results are returned to the container for query phase result processing. In this phase, the only per hit data available is the internal global document ID (gid) and the ranking score. There is also result meta information like coverage and total hit count. Additional hit specific data, like the contents of fields, is not available until the result processing phase has completed the content fetching. + + + +### Result processing (fill) phase + + + +When the result from the query phase is available, a custom chained [searcher component](../applications/searchers#multiphase-searching) can process the limited data available from the first search phase before contents of the hits is fetched from the content nodes. The fetching from content nodes is lazy and is not invoked before rendering the response, unless asked for earlier by a custom searcher component. + + +Only fields in the requested [document summaries](/en/querying/document-summaries) is fetched from content nodes. The summary request goes directly to the content nodes that produced the result from the query phase. + + +After the content node requests have completed, the full result set can be processed further by custom components (e.g. doing result deduping, top-k re-ranking), before [rendering](../applications/result-renderers) the response. + + + +## HTTP + + +**Note:** + +Vespa does not provide a java client library for the query API. Best practice for queries is submitting the user-generated query as-is, then use [Searcher components](../applications/searchers) to implement additional logic. + + +The Vespa Team does not recommend any specific HTTP client, since we haven't done any systematic evaluation. We have most experience with the Apache HTTP client. See also [HTTP best practices](/en/clients/http-best-practices) (for Vespa Cloud, but most of it is generally applicable). Also see a discussion in [#24534](https://github.com/vespa-engine/vespa/issues/24534). + +Use GET or POST. Parameters can either be sent as GET-parameters or posted as JSON, these are equivalent: + +```bash +$ curl -H "Content-Type: application/json" \ + --data '{"yql" : "select * from sources * where default contains \"coldplay\""}' \ + http://localhost:8080/search/ + +$ curl http://localhost:8080/search/?yql=select+%2A+from+sources+%2A+where+default+contains+%22coldplay%22 +``` + +### Using POST + +The format is based on the [Query API reference](../reference/api/query), and has been converted from the *flat* dot notation to a *nested* JSON-structure. The request-method must be POST and the *Content-Type* must be *"application/json"*, e.g.: + +```bash +$ curl -X POST -H "Content-Type: application/json" --data ' + { + "yql": "select * from sources * where true", + "offset": 5, + "ranking": { + "matchPhase": { + "ascending": true, + "maxHits": 15 + } + }, + "presentation" : { + "bolding": false, + "format": "json" + } + }' \ + http://localhost:8080/search/ +``` + + +**Note:** + +Try the [Query Builder](https://github.com/vespa-engine/vespa/tree/master/client/js/app#query-builder) application! + +**Important:** + +Security filters can block GET and POST requests differently. This can block POSTed queries. + + +### HTTP + +Configure the [http server](../reference/applications/services/http#server) - e.g. set *requestHeaderSize* to configure URL length (including headers): + +```xml + + + + + 32768 + + + + +``` + +HTTP keepalive is supported. + +Values must be encoded according to standard URL encoding. Thus, space is encoded as +, + as %2b and so on - see [RFC 2396](https://www.ietf.org/rfc/rfc2396.txt). + +HTTP status codes are found in the [Query API reference](../reference/api/query#http-status-codes). Also see [Stack Overflow question](https://stackoverflow.com/questions/54340386/how-should-i-customize-my-search-result-in-vespa/54344429#54344429). + +When implementing a client for the query API, consider the following guidelines for handling HTTP status codes: + +#### Client errors vs. server errors + +In general clients should only retry requests on *server errors* (5xx) - not on *client errors* (4xx). For example, a client should **not** retry a request after receiving a `400 Bad Request` response. + +#### Back-pressure handling + +Be careful when handling 5xx responses, especially `503 Service Unavailable` and `504 Gateway Timeout`. These responses typically indicate an overloaded system, and blindly retrying without backoff will only worsen the situation. For example, `503 Service Unavailable` is returned whenever there are no available search handler threads to serve the request. This is a clear indication of back-pressure from the system, and clients should reduce overall throughput and implement appropriate throttling mechanisms to avoid exacerbating the overload condition. + +## Timeout + +See the [reference](../reference/api/query#timeout) for how to set the query timeout. Common questions: + +- *Does the timeout apply to the whole query or just from when it is sent to the content cluster? If a [Searcher](../applications/searchers) goes to sleep in the container for 2*timeout, will the caller still get a response indicating a timeout?* + + The timeout applies to the whole query, both container and content node processing. However, the timeout handling is cooperative - if having Searchers that are time-consuming or access external resources, the Searcher code should check [Query.getTimeLeft()](https://github.com/vespa-engine/vespa/blob/master/container-search/src/main/java/com/yahoo/search/Query.java). So, in this case, you will time out, but only after 2*timeout + some more. + +- *During multiphase searching, is the query timeout set for each individual searcher, or is the query timeout set for the entire search chain?* + + The timeout is for the entire query (and most Searchers don’t check timeout - use `Query.getTimeLeft()`). E.g., if a Search Chain has 3 Searchers, it is OK for 1 Searcher to take 497 ms and 2 Searchers to each take 1 ms for a query timeout of 500 ms. + +- *If we asynchronously execute several search chains, can we set different query timeouts for each of these chains plus a separate overall timeout for the searcher that performs the asynchronous executions?* + + You can set a different timeout in each cloned query you send to any of those chains, and you can specify the timeout when waiting for responses from them. + + +## Error handling + +Check for a `root: error` element in the [result](../reference/querying/default-result-format#error): + +```json +{ + "root": { + "errors": [ + { + "code": 8, + "summary": "Error in search reply.", + "source": "music", + "message": "Could not locate attribute for grouping number 0 : Failed locating attribute vector 'year'. Ignoring this grouping." + } + ], +``` + +## Troubleshooting + +If Vespa cannot generate a valid search expression from the query string, it will issue the error message *Null query*. To troubleshoot, add [&trace.level=2](../reference/api/query#trace.level) to the request. A missing *yql* parameter will also emit this error message. + +### Query tracing + +Use *query tracing* to debug query execution. Enable by using [trace.level=1](../reference/api/query#trace.level) (or higher). Add [trace.timestamps=true](../reference/api/query#trace.timestamps) for timing info for every searcher invoked. Find a trace example in the result examples below, and try the [practical search performance guide](../performance/practical-search-performance-guide#advanced-query-tracing). + +In custom code, use [Query.trace](https://javadoc.io/page/com.yahoo.vespa/container-search/latest/com/yahoo/search/Query) to add trace output. + +### Large memory usage + +Queries that allocate more than 2G RAM will log messages like: + +```text +mmap 2727 of size 8589934592 from : search::attribute::PostingListMerger::reserveArray(unsigned int, unsigned long)(0x40001513eef0) + +(0x400013595334) from (0x400013593acc) from operator new(unsigned long)(0x400013592f88) from search::attribute::PostingListMerger::reserveArray(unsigned int, unsigned long)(0x40001513eef0) from search::attribute::PostingListSearchContextT::fetchPostings(search::queryeval::ExecuteInfo const&, bool)(0x400015159a38) from search::queryeval::SameElementBlueprint::fetchPostings(search::queryeval::ExecuteInfo const&)(0x4000154f36fc) from search::queryeval::IntermediateBlueprint::fetchPostings(search::queryeval::ExecuteInfo const&)(0x40001549ad38) from proton::matching::MatchToolsFactory::MatchToolsFactory(proton::matching::QueryLimiter&, vespalib::Doom const&, proton::matching::ISearchContext&, search::attribute::IAttributeContext&, search::engine::Trace&, std::basic_string_view >, std::__cxx11::basic_string, std::allocator > const&, ... + +1 mappings of accumulated size 8589934592 +``` + +This does not necessarily indicate that something is wrong, e.g., range searches use much memory. + +## Result examples + +A regular query result: + +```json expandable +{ + "root": { + "id": "toplevel", + "relevance": 1.0, + "fields": { + "totalCount": 1 + }, + "coverage": { + "coverage": 100, + "documents": 3, + "full": true, + "nodes": 1, + "results": 1, + "resultsFull": 1 + }, + "children": [ + { + "id": "id:mynamespace:music::a-head-full-of-dreams", + "relevance": 0.16343879032006284, + "source": "music", + "fields": { + "sddocname": "music", + "documentid": "id:mynamespace:music::a-head-full-of-dreams", + "artist": "Coldplay", + "album": "A Head Full of Dreams", + "year": 2015, + "category_scores": { + "cells": [ + { + "address": { + "cat": "pop" + }, + "value": 1.0 + }, + { + "address": { + "cat": "rock" + }, + "value": 0.20000000298023224 + }, + { + "address": { + "cat": "jazz" + }, + "value": 0.0 + } + ] + } + } + } + ] + } +} +``` + +An empty result: + +```json +{ + "root": { + "fields": { + "totalCount": 0 + }, + "id": "toplevel", + "relevance": 1.0 + } +} +``` + +An error result: + +```json expandable +{ + "root": { + "id": "toplevel", + "relevance": 1.0, + "fields": { + "totalCount": 2 + }, + "coverage": { + "coverage": 100, + "documents": 4, + "full": true, + "nodes": 2, + "results": 2, + "resultsFull": 2 + }, + "errors": [ + { + "code": 8, + "summary": "Error in search reply.", + "source": "music", + "message": "Could not locate attribute for grouping number 0 : Failed locating attribute vector 'year'. Ignoring this grouping." + } + ], +``` + +A simple search application, many undefined fields. Result for the query `/search/?query=blues&hits=3&trace.level=2` + +```json expandable +{ + + "trace": { + "children": [ + { + "message": "No query profile is used" + }, + { + "message": "Invoking chain 'vespa' [com.yahoo.prelude.statistics.StatisticsSearcher@native -> com.yahoo.prelude.querytransform.PhrasingSearcher@vespa -> ... -> federation@native]" + }, + { + "children": [ + { + "message": "Detected language: ENGLISH" + }, + { + "message": "Language ENGLISH determined by the characters in the terms." + }, + { + "message": "Query parsed to: select * from sources * where default contains \"blues\" limit 3" + }, + { + "message": "Child execution", + "children": [ + { + "message": "Stemming: [select * from sources * where default contains ({\"origin\": {\"original\": \"blues\", \"offset\": 0, \"length\": 5}, \"stem\": false}\"blue\") limit 3]" + }, + { + "message": "Lowercasing: [select * from sources * where default contains ({\"origin\": {\"original\": \"blues\", \"offset\": 0, \"length\": 5}, \"stem\": false, \"normalizeCase\": false}\"blue\") limit 3]" + }, + { + "message": "sc0.num0 search to dispatch: query=[blue] timeout=5000ms offset=0 hits=3 grouping=0 : collapse=false restrict=[music]" + }, + { + "message": "Current state of query tree: WORD[connectedItem=null connectivity=0.0 creator=ORIG explicitSignificance=false fromSegmented=false index=\"\" isRanked=true origin=\"(0 5)\" segmentIndex=0 significance=0.0 stemmed=true uniqueID=1 usePositionData=true weight=100 words=true]{\n \"blue\"\n}\n" + }, + { + "message": "YQL+ representation: select * from sources * where default contains ({\"origin\": {\"original\": \"blues\", \"offset\": 0, \"length\": 5}, \"stem\": false, \"normalizeCase\": false, \"id\": 1}\"blue\") limit 3" + }, + { + "message": "sc0.num0 dispatch response: Result (3 of total 10 hits)" + }, + { + "message": "sc0.num0 fill to dispatch: query=[blue] timeout=5000ms offset=0 hits=3 grouping=0 : collapse=false restrict=[music] summary=[null]" + }, + { + "message": "Current state of query tree: WORD[connectedItem=null connectivity=0.0 creator=ORIG explicitSignificance=false fromSegmented=false index=\"\" isRanked=true origin=\"(0 5)\" segmentIndex=0 significance=0.0 stemmed=true uniqueID=1 usePositionData=true weight=100 words=true]{\n \"blue\"\n}\n" + }, + { + "message": "YQL+ representation: select * from sources * where default contains ({\"origin\": {\"original\": \"blues\", \"offset\": 0, \"length\": 5}, \"stem\": false, \"normalizeCase\": false, \"id\": 1}\"blue\") limit 3" + } + ] + }, + { + "message": "Child execution" + } + ] + } + ] + }, + "root": { + "id": "toplevel", + "relevance": 1, + "fields": { + "totalCount": 10 + }, + "coverage": { + "coverage": 100, + "documents": 10, + "full": true, + "nodes": 1, + "results": 1, + "resultsFull": 1 + }, + "children": [ + { + "id": "index:0/0/0/dfd9fcfa650b44545ef0b8b2", + "relevance": "-Infinity", + "source": "basicsearch", + "fields": { + "sddocname": "music", + "title": "Electric Blues", + "artist": "", + "song": "", + "bgndata": "", + "sales": "NaN", + "pto": -1, + "mid": 2, + "ew": "blues", + "surl": "https://shopping.yahoo.com/shop?d=hab&id=1807865261", + "userrate": "NaN", + "pid": "", + "weight": "NaN", + "url": "", + "isbn": "", + "fmt": "", + "albumid": "", + "disp_song": "", + "pfrom": "NaN", + "bgnpfrom": "NaN", + "categories": "Blues", + "data": "", + "numreview": "NaN", + "bgnsellers": 0, + "image": "", + "artistspid": "", + "newestedition": "NaN", + "bgnpto": "", + "year": "NaN", + "did": "NaN", + "scorekey": "NaN", + "cbid": "NaN", + "summaryfeatures": "", + "documentid": "id:test:music::https://shopping.yahoo.com/shop?d=hab&id=1807865261" + } + }, + { + "id": "index:0/0/0/273d384dc214386c934d793f", + "relevance": "-Infinity", + "source": "basicsearch", + "fields": { + "sddocname": "music", + "title": "Delta Blues", + "artist": "", + "song": "", + "bgndata": "", + "sales": "NaN", + "pto": -1, + "mid": 2, + "ew": "blues", + "surl": "https://shopping.yahoo.com/shop?d=hab&id=1804905714", + "userrate": "NaN", + "pid": "", + "weight": "NaN", + "url": "", + "isbn": "", + "fmt": "", + "albumid": "", + "disp_song": "", + "pfrom": "NaN", + "bgnpfrom": "NaN", + "categories": "Blues", + "data": "", + "numreview": "NaN", + "bgnsellers": 0, + "image": "", + "artistspid": "", + "newestedition": "NaN", + "bgnpto": "", + "year": "NaN", + "did": "NaN", + "scorekey": "NaN", + "cbid": "NaN", + "summaryfeatures": "", + "documentid": "id:test:music::https://shopping.yahoo.com/shop?d=hab&id=1804905714" + } + }, + { + "id": "index:0/0/0/b3c74a9bf3aea1e2260311c0", + "relevance": "-Infinity", + "source": "basicsearch", + "fields": { + "sddocname": "music", + "title": "Chicago Blues", + "artist": "", + "song": "", + "bgndata": "", + "sales": "NaN", + "pto": -1, + "mid": 2, + "ew": "blues", + "surl": "https://shopping.yahoo.com/shop?d=hab&id=1804905710", + "userrate": "NaN", + "pid": "", + "weight": "NaN", + "url": "", + "isbn": "", + "fmt": "", + "albumid": "", + "disp_song": "", + "pfrom": "NaN", + "bgnpfrom": "NaN", + "categories": "Blues", + "data": "", + "numreview": "NaN", + "bgnsellers": 0, + "image": "", + "artistspid": "", + "newestedition": "NaN", + "bgnpto": "", + "year": "NaN", + "did": "NaN", + "scorekey": "NaN", + "cbid": "NaN", + "summaryfeatures": "", + "documentid": "id:test:music::https://shopping.yahoo.com/shop?d=hab&id=1804905710" + } + } + ] + } +} +``` + +Result for the grouping query `/search/?hits=0&yql=select * from sources * where sddocname contains purchase | all(group(customer) each(output(sum(price))))` + +```json expandable +{ + + "trace": { + "children": [ + { + "children": [ + { + "message": "Child execution" + } + ] + } + ] + }, + "root": { + "id": "toplevel", + "relevance": 1, + "fields": { + "totalCount": 20 + }, + "coverage": { + "coverage": 100, + "documents": 20, + "full": true, + "nodes": 1, + "results": 1, + "resultsFull": 1 + }, + "children": [ + { + "id": "group:root:0", + "relevance": 1, + "continuation": { + "this": "" + }, + "children": [ + { + "id": "grouplist:customer", + "relevance": 1, + "label": "customer", + "children": [ + { + "id": "group:string:Jones", + "relevance": 9870, + "value": "Jones", + "fields": { + "sum(price)": 39816 + } + }, + { + "id": "group:string:Brown", + "relevance": 8000, + "value": "Brown", + "fields": { + "sum(price)": 20537 + } + }, + { + "id": "group:string:Smith", + "relevance": 6100, + "value": "Smith", + "fields": { + "sum(price)": 19484 + } + } + ] + } + ] + } + ] + } +} +``` diff --git a/en/querying/query-language.html b/en/querying/query-language.html deleted file mode 100644 index ffbeb77947..0000000000 --- a/en/querying/query-language.html +++ /dev/null @@ -1,152 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Vespa Query Language - YQL" -redirect_from: -- /en/query-language.html ---- - -

    - Vespa accepts unstructured human input and structured queries for application logic separately, - then combines them into a single data structure for executing. - Human input is parsed heuristically, see Query API input. - Application logic is expressed in YQL, use this guide for examples - - also see the YQL reference. -

    - - - -

    Live query examples

    -

    - The following are live YQL examples: -

    -
    -

    - Selection: Select all documents from the doc source. - This is the easiest way to count all documents in a source: -

    -

    select * from doc where true

    -
    - -

    Filtering: Find all documents with ranking in the title field:

    -

    select * from doc where title contains "ranking"

    -
    - -

    - Filtering: Find all documents with ranking in the default - fieldset. -

    -

    select * from doc where default contains "ranking"

    -
    - -

    Ordering: Order by number of terms in the document, descending.

    -

    select * from doc where true order by term_count desc

    -
    - -

    Pagination: Select all documents, return hits 6-15.

    -

    select * from doc where true limit 15 offset 5

    -
    - -

    Grouping:

    -
      -
    • Select all documents from the doc source.
    • -
    • Group by term count in buckets of 100, display average term count per bucket.
    • -
    • Note on limit 0: This returns zero regular hits, only the grouping result.
    • -
    -

    select * from doc where true limit 0 | - all( - group( fixedwidth(term_count,100) ) - each( output( avg(term_count) ) ) - )

    -

    Find more grouping examples.

    -
    - -

    Numeric: Select documents with attribute "last_updated" > 1646167144:

    -

    select * from doc where last_updated > 1646167144

    -

    - Numeric: Select documents with integer in field - works both for single-value fields and multivalue, - like array<int>: -

    -

    select * from doc where term_count = 258

    -
    - -

    Phrase: Select documents with the phrase "question answering":

    -

    select * from doc where default contains phrase("question","answering")

    -
    - -

    - Timeout: Time out query execution after 100 ms, returning hits found before timing out - - see ranking.softtimeout.enable: -

    -

    select * from doc where true timeout 100

    -
    - -

    - Regular expressions: Select documents matching the regular expression - in the namespace attribute field:

    -

    select * from doc where namespace matches "op.*"

    -
    - - -

    Command-line queries

    -

    Use the Vespa CLI to run a query from the command-line:

    -
    - -
    -$ vespa query 'select * from doc where true'
    -
    -
    -

    To use any HTTP client, use -v to generate the encoded YQL string:

    -
    -$ vespa query -v 'select * from doc where true'
    -
    -curl http://127.0.0.1:8080/search/\?timeout=10s\&yql=select+%2A+from+doc+where+true
    -
    -

    Run the query:

    -
    - -
    -$ curl http://127.0.0.1:8080/search/\?timeout=10s\&yql=select+%2A+from+doc+where+true
    -
    -
    -

    Alternatively, set the query as the yql parameter in a POST:

    -
    - -
    -$ curl --data-urlencode 'yql=select * from doc where true' \
    -  http://127.0.0.1:8080/search/
    -
    -
    - - - - -

    Query examples

    -

    Boolean:

    -
    - -
    -$ vespa query 'select * from doc where is_public = true'
    -
    -
    -

    Map:

    -
    -$ vespa query 'select * from doc where my_map contains sameElement(key contains "Coldplay", value > 10)'
    -
    -#
    -# Schema definition for my_map:
    -#
    -field my_map type map<string, int> {
    -    indexing: summary
    -    struct-field key   { indexing: attribute }
    -    struct-field value { indexing: attribute }
    -}
    -
    -

    Escapes - see the FAQ:

    -
    -#
    -# The artist field is:
    -#   "artist": "Meta..ica"
    -#
    -
    -$ vespa query -v 'select * from music where artist matches "M.ta\\.\\.ica"'
    -
    diff --git a/en/querying/query-language.mdx b/en/querying/query-language.mdx new file mode 100644 index 0000000000..b0f13fe7be --- /dev/null +++ b/en/querying/query-language.mdx @@ -0,0 +1,134 @@ +--- +title: "Vespa Query Language - YQL" +sidebarTitle: "The YQL query language" +--- + +Vespa accepts unstructured human input and structured queries for application logic separately, then combines them into a single data structure for executing. Human input is parsed heuristically, see [Query API input](/en/querying/query-api#input). Application logic is expressed in YQL, use this guide for examples - also see the [YQL reference](../reference/querying/yql). + +## Live query examples + +The following are live YQL examples: + + +Selection: Select all documents from the `doc` source. This is the easiest way to count all documents in a source: + +`select * from doc where true` + + +Filtering: Find all documents with `ranking` in the `title` field: + +`select * from doc where title contains "ranking"` + + +Filtering: Find all documents with `ranking` in the `default` [fieldset](../reference/schemas/schemas#fieldset). + +`select * from doc where default contains "ranking"` + + +Ordering: Order by number of terms in the document, descending. + +`select * from doc where true order by term_count desc` + + +Pagination: Select all documents, return hits 6-15. + +`select * from doc where true limit 15 offset 5` + + +Grouping: + +- Select all documents from the `doc` source. +- Group by term count in buckets of 100, display average term count per bucket. +- Note on `limit 0`: This returns zero regular hits, only the grouping result. + +`select * from doc where true limit 0 | all( group( fixedwidth(term_count,100) ) each( output( avg(term_count) ) ) )` + +Find more [grouping examples](/en/querying/grouping). + + +Numeric: Select documents with attribute "last_updated" > 1646167144: + +`select * from doc where last_updated > 1646167144` + +Numeric: Select documents with integer in field - works both for single-value fields and multivalue, like array``: + +`select * from doc where term_count = 258` + + +Phrase: Select documents with the phrase "question answering": + +`select * from doc where default contains phrase("question","answering")` + + +Timeout: Time out query execution after 100 ms, returning hits found before timing out - see [ranking.softtimeout.enable](../reference/api/query#ranking.softtimeout.enable): + +`select * from doc where true timeout 100` + + +Regular expressions: Select documents matching the regular expression in the `namespace` [attribute](../content/attributes) field: + +`select * from doc where namespace matches "op.*"` + + +### Command-line queries + +Use the [Vespa CLI](../clients/vespa-cli) to run a query from the command-line: + +```bash +$ vespa query 'select * from doc where true' +``` + +To use any HTTP client, use `-v` to generate the encoded YQL string: + +```bash +$ vespa query -v 'select * from doc where true' + +curl http://127.0.0.1:8080/search/?timeout=10s&yql=select+%2A+from+doc+where+true +``` + +Run the query: + +```bash +$ curl http://127.0.0.1:8080/search/?timeout=10s&yql=select+%2A+from+doc+where+true +``` + +Alternatively, set the query as the `yql` parameter in a POST: + +```bash +$ curl --data-urlencode 'yql=select * from doc where true' \ + http://127.0.0.1:8080/search/ +``` + +## Query examples + +Boolean: + +```bash +$ vespa query 'select * from doc where is_public = true' +``` + +Map: + +```bash +$ vespa query 'select * from doc where my_map contains sameElement(key contains "Coldplay", value > 10)' + +# +# Schema definition for my_map: +# +field my_map type map { + indexing: summary + struct-field key { indexing: attribute } + struct-field value { indexing: attribute } +} +``` + +Escapes - see the [FAQ](../learn/faq#how-does-backslash-escapes-work): + +```bash +# +# The artist field is: +# "artist": "Meta..ica" +# + +$ vespa query -v 'select * from music where artist matches "M.ta\\.\\.ica"' +``` diff --git a/en/querying/query-profiles.html b/en/querying/query-profiles.html deleted file mode 100644 index 58d0da9960..0000000000 --- a/en/querying/query-profiles.html +++ /dev/null @@ -1,479 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Query Profiles" -redirect_from: -- /en/query-profiles.html ---- - -

    -A Query Profile is a named collection of search request parameters given in the configuration. -The search request can specify a query profile -whose parameters will be used as parameters of that request. -This frees the client from having to manage and send a large number of parameters, -and enables the request parameters to use for a use case to be changed -without having to change the client. -Query profiles enables bucket tests, -where a part of the query stream is given some experimental treatment, -as well as differentiating behavior based on (OEM) customer, user type, region, frontend type etc. -This document explains how to create and use query profiles. -See also the query profile reference for the full syntax. -

    - - - -

    Using a Query Profile

    -

    -A Query Profile is an XML file containing the request parameter names and their values, e.g.: -

    -
    -<query-profile id="MyProfile">
    -    <field name="hits">20</field>
    -    <field name="maxHits">2000</field>
    -    <field name="unique">merchantid</field>
    -</query-profile>
    -
    -

    -See the query profile reference for the full syntax. -{% include important.html content='Note that full property names must be used, aliases like -input.query(...) are only supported in requests and programmatic lookup.'%} -See the Query API reference -for a list of the built-in query properties. -

    -To deploy a query profile: -

    -
      -
    1. Create a file for the profile, using the format above, - having the name [my-profile-name].xml, - e.g. MyProfile.xml (replace any / in the name by _)
    2. -
    3. Put this in the directory search/query-profiles in - the application package root
    4. -
    5. Redeploy the application package
    6. -
    -

    -Any number of query profile files may be added to this directory. -If the query profiles contains errors, like incorrect syntax and/or infinite reference loops, deployment will fail. -

    -To use a query profile in a query request, send the name of the profile as the parameter queryProfile: -

    -
    -queryProfile=MyProfile
    -
    -

    -If the request does not specify a query profile, -the profile named default will be used. -If no default profile is configured, no profile will be used. -If the queryProfile parameter is set but does not resolve to an existing profile, -an error message is returned. -Example, set default query timeout to 200ms for all queries not using a query profile: -

    -
    -$ cat search/query-profiles/default.xml
    -
    -<query-profile id="default">
    -    <field name="timeout">0.2</field>
    -</query-profile>
    -
    -

    - The query profile values (whether set from a configured query profile or by the request) - is available as query properties. - To look up a value from a Searcher component, use: -

    -
    -query.properties().get("myVariable")
    -
    -

    -Note that property names are case-sensitive. -

    - - -

    Example

    -

    - Use a query profile to modify the YQL query string - using an IN operator - and local substitution of cities: -

    -
    {% highlight xml %}
    -
    -    select * from restaurant where userQuery() and city in (%{cities})
    -    ""
    -
    -{% endhighlight %}
    -

    - Use match: word for the IN operator:

    -
    -field city type string {
    -    indexing: summary | index
    -    match:    word
    -}
    -
    -

    An example query passing the values for the IN operator to be substituted into the YQL string:

    -
    -$ vespa query \
    -  queryProfile=city-filter \
    -  cities='"berlin","paris"' \
    -  query='what the user typed'
    -
    -

    - With this, the application can use the city-filter query profile if there are cities in the filter, - if empty, use a no-filter query profile: -

    -
    {% highlight xml %}
    -
    -    select * from restaurant where userQuery()
    -
    -{% endhighlight %}
    -
    -$ vespa query \
    -  queryProfile=no-filter \
    -  query='what the user typed'
    -
    - - -

    Overrides

    -

    - The parameter values set in MyProfile.xml will be used - as if they were present directly in the request. - If a parameter is present both directly in the request and in a profile, - the request value takes precedence by default. - Individual query profile field can be made to take priority by setting - the overridable attribute to false. - Example: -

    -
    -<query-profile id="default">
    -    <field name="timeout" overridable="false">0.2</field>
    -</query-profile>
    -
    - - -

    Nested Structure

    -

    -To support structure in the set of request variables, a query profile -value may not be a string but a reference to another query profile. -In this case, the referenced query profile functions as a map -(or struct, if types are used, see below) in the referencing query profile. -The parameter names of the nested profile gets preceded by the name of the -reference variable and a dot. For example: -

    -
    -<query-profile id="MyProfile">
    -    <field name="hits">10</field>
    -    <field name="unique">merchantid</field>
    -    <field name="user"><ref>MyUserProfile</ref></field>
    -</query-profile>
    -
    -

    Where the referenced profile might look like:

    -
    -<query-profile id="MyUserProfile">
    -    <field name="age">20</field>
    -    <field name="profession">student</field>
    -</query-profile>
    -
    -

    If MyProfile is referenced in a query now, it will contain the variables

    -
    -hits=10
    -unique=merchantid
    -user.age=20
    -user.profession=student
    -
    -

    -References can be nested to provide a deeper structure, -producing variables having multiple dots. -The dotted variables can be overridden directly in the search request -(using the dotted name) just as other parameters. -

    -Note that the id value of a profile reference can also be set in the request, -making it possible to choose not just the top level profile -but also any number of specific subprofiles in the request. -For example, the request can contain -

    -
    -queryProfile=MyUserProfile&user=ref:MyOtherUserprofile
    -
    -

    -to change the reference in the above example to some other subprofile. -Note the ref: prefix which is required to identify this -as setting user to a query profile referenced by id rather than setting it to a string. -

    - - - -

    Inheritance

    -

    -A query profile may inherit one or more other query profiles. -This is useful when there is some common set of parameters applicable to -multiple use cases, and a smaller set of parameters which varies between them. -To inherit another query profile, reference it as follows: -

    -
    -<query-profile id="MyProfile" inherits="MyBaseProfile">
    -    …
    -</query-profile>
    -
    -

    -The parameters of MyBaseProfile will be present when this -profile is used exactly as if they were explicitly written in this profile. -

    -Multiple inheritance is supported by specifying multiple -space-separated profile ids in the inheritance attribute. -Order matters in this list - if a parameter is set in more than one of the inherited profiles, -the first one encountered in the depth first, left to right search order is used. -

    -Parameters specified in the child query profile will always override the same parameters in an inherited one. -

    - - - -

    Value Substitution

    -

    - Query profile values may contain substitution strings on the form - %{property-name}. Example: -

    -
    -<query-profile id="MyProfile">
    -    <field name="message">Hello %{world}!</field>
    -    <field name="world">Earth</field>
    -</query-profile>
    -
    -

    -The value returned by looking up message will be Hello Earth!. -

    - - -

    Global resolution

    -

    - Values are normally replaced by the value returned - from query.properties().get("property-name") at the time of the lookup. - Therefore, substituted values may be looked up in variants, in inherited profiles, - in values set at run time and by following query profile references. Details: -

    -
      -
    • No substitution will be performed in values set at run time
    • -
    • If the value referenced in a substitution returns null, - the reference is substituted by the empty string
    • -
    • Unclosed substitutions cause an error at deploy time, but unknown values do not - (they may exist at run time and will be replaced by an empty string if not)
    • -
    • Recursive substitution works as expected. However, there is no loop detection
    • -
    - - -

    Local substitution

    -

    - To substitute by a value in the same query profile (or variant), - prefix the property by a dot, as in -

    -
    -<query-profile id="MyProfile">
    -    <field name="message">Hello %{.world}!</field>
    -    <field name="world">Earth</field>
    -</query-profile>
    -
    -

    - Local substitutions can be verified at deploy time and will cause an error if not found. -

    - - - -

    Query Profile Variants

    -

    -In some cases, it is convenient to allow the values returned from a -query profile to vary depending on the values of some properties input in the request. -For example, a query profile may contain values which depend on both the market -in which the request originated (market), the kind of device (model) -and the bucket in question (bucket). -

    -Such variants over a set of request parameters may be represented in a single query profile, -by defining nested variants of the query profile for the relevant combinations of request values. -A complete example: -

    -
    -<query-profile id="multiprofile1"> <!-- A regular profile may define "virtual" children within itself -->
    -
    -    <!-- Names of the request parameters defining the variant profiles of this. Order matters as described below.
    -         Each individual value looked up in this profile is resolved from the most specific matching virtual
    -         variant profile -->
    -    <dimensions>region,model,bucket</dimensions>
    -
    -    <!-- Values may be set in the profile itself as usual, this becomes the default values given no matching
    -         virtual variant provides a value for the property -->
    -    <field name="a">My general a value</field>
    -
    -    <!-- The "for" attribute in a child profile supplies values in order for each of the dimensions -->
    -    <query-profile for="us,nokia,test1">
    -        <field name="a">My value of the combination us-nokia-test1-a</field>
    -    </query-profile>
    -
    -    <!-- Same as [us,*,*]  - trailing "*"'s may be omitted -->
    -    <query-profile for="us">
    -        <field name="a">My value of the combination us-a</field>
    -        <field name="b">My value of the combination us-b</field>
    -    </query-profile>
    -
    -    <!-- Given a request which matches both the below, the one which specifies concrete values to the left
    -         gets precedence over those specifying concrete values to the right
    -         (i.e the first one gets precedence here) -->
    -    <query-profile for="us,nokia,*">
    -        <field name="a">My value of the combination us-nokia-a</field>
    -        <field name="b">My value of the combination us-nokia-b</field>
    -    </query-profile>
    -    <query-profile for="us,*,test1">
    -        <field name="a">My value of the combination us-test1-a</field>
    -        <field name="b">My value of the combination us-test1-b</field>
    -    </query-profile>
    -
    -</query-profile>
    -
    - - -

    Variants and Inheritance

    -

    -It is possible to define variants across several levels in an inheritance hierarchy. -The variant dimensions are inherited from parent to child, -with the usual precedence rules (depth first left to right), -so a parent profile may define the dimensions and the child the values over which it should vary. -

    -Variant resolution within a profile has precedence over resolution in parents. -This means e.g. that a default value for a given property in a sub-profile will be chosen -over a perfect variant match in an inherited profile. -

    -Variants may specify their own inherited profiles, as in: -

    -
    -<query-profile id="multiprofile1">
    -    …
    -    <query-profile for="us,nokia,test1" inherits="parent1 parent2">
    -        …
    -    </query-profile>
    -</query-profile>
    -
    -

    -Values are resolved in this profile and inherited profiles "interleaved" by the variant resolution order -(which is specificity by default). E.g. by decreasing priority: -

    -
    -1.   Highest prioritized variant value
    -2.   Value in inherited from highest prioritized variant
    -3.   Next highest prioritized variant value
    -4.   Value in inherited from next highest prioritized variant
    -…
    -n.   Value defined at top level in profile
    -n+1. Value in inherited from query profile
    -
    - - - -

    Query Profile Types

    -

    -The query profiles may optionally be type checked. -Type checking is turned on by referencing a Query Profile Type from the query profile. -The type lists the legal set of parameters of the query profile, -whether additional parameters are allowed, and so on. -

    -A query profile type is referenced by: -

    -
    -<query-profile id="MyProfile" type="MyProfileType">
    -    …
    -</query-profile>
    -
    -

    And the type is defined as:

    -
    -<query-profile-type id="MyProfileType">
    -    <field name="age" type="integer"/>
    -    <field name="profession" type="string"/>
    -    <field name="user" type="query-profile:MyUserProfile">
    -</query-profile-type>
    -
    -

    -This specifies that these three parameters may be present in profiles using this type, -as well as the query profile type of the user parameter. -

    -It is also possible to specify that parameters are mandatory, -that no additional parameters are allowed (strict), -to inherit other types and so on, refer to the full syntax in -the query profile reference. -If the base profile type is strict, it must extend a built-in query profile type, -see the strict reference documentation. -

    -A query profile type is deployed by adding a file named [query-profile-type-name].xml -in the search/query-profiles/types directory in the application package. -

    -Query profile types may be useful even if query profiles are not used to set values. -As they define the names, types and structure of the parameters which can be accepted in the search request, -they can also be used to define, restrict and check the content of search requests. -For example, as the built-in search api parameters are also type checked if a typed query profile is used, -types can be used to restrict the parameters that can be set in a request, -or to mandate that some are always set. -The built-in parameters are defined in a set of query profile types which are always present -and which can be inherited and referenced in application-defined types. -These built-in types are defined in the Query API. -

    - - - -

    Path Matching

    -

    -By adding <match path="true"> to a profile type, -path name matching is used rather than the default exact matching -when a profile is looked up from a name. -Path matching interprets the profile name as a slash separated path -and matches references which are subpaths (more specific paths) to super-paths. -The most specific match becomes the target of the reference. For example: -

    -
    -Given the query profile names:
    -  a1
    -  a1/b1
    -
    -Then:
    -  a1/b1/c1/d1  resolves to a1/b1
    -  a1/b         resolves to a1
    -  a            does not resolve
    -
    -

    -This is useful to assign specific query profile id's to every client or bucket -without having to create a different configuration item for each of these cases. -If there is a need to provide a differentiated configuration for any such client or bucket in the future, -this can be done without having the client change its request parameter because a specific id is already used. -

    - - - -

    Versioning

    -

    -Query profiles (and types) may exist in multiple versions at the same time. -Wherever a name of a query profile (or type) is referenced, -the name may also append a version string, separated by a colon, e.g MyProfile:1.2.3. -The version number is resolved - if no version is given the highest version known is used. -If the version number is only partially specified, as in my-version:1, -the highest version starting by 1 is used. -

    -Where a query profile (or type) is defined, the id may specify the version, followed by a colon: -

    -
    -<query-profile id="MyProfile:1.2.3">
    -  …
    -</query-profile>
    -
    -

    -Any sub-number omitted is taken to mean 0 where a version is defined, -so id="MyProfile:1" is the same as "id=MyProfile:1.0.0". -

    -Query profiles (and types) which specifies a version in their id -must use a file name which includes the same version string after the name, -separated by a dash, e.g. MyProfile-1.2.3.xml. -

    - For more information on versions, - see component versioning. -

    - - - -

    Dump Tool

    -

    -It can sometimes be handy to be able to dump resolved query profiles offline. -Run without arguments to get usage: -

    -
    -$ vespa-query-profile-dump-tool
    -
    diff --git a/en/querying/query-profiles.mdx b/en/querying/query-profiles.mdx new file mode 100644 index 0000000000..1302b7107d --- /dev/null +++ b/en/querying/query-profiles.mdx @@ -0,0 +1,358 @@ +--- +title: "Query Profiles" +--- + +A Query Profile is a named collection of search request parameters given in the configuration. The search request can specify a query profile whose parameters will be used as parameters of that request. This frees the client from having to manage and send a large number of parameters, and enables the request parameters to use for a use case to be changed without having to change the client. Query profiles enables [bucket tests](../applications/testing#feature-switches-and-bucket-tests), where a part of the query stream is given some experimental treatment, as well as differentiating behavior based on (OEM) customer, user type, region, frontend type etc. This document explains how to create and use query profiles. See also the [query profile reference](../reference/querying/query-profiles) for the full syntax. + +## Using a Query Profile + +A Query Profile is an XML file containing the request parameter names and their values, e.g.: + +```xml + + 20 + 2000 + merchantid + +``` + +See the [query profile reference](../reference/querying/query-profiles) for the full syntax. + + +**Important:** + +Note that full property names must be used, aliases like `input.query(...)` are only supported in requests and programmatic lookup. + + +See the [Query API reference](../reference/api/query) for a list of the built-in query properties. + +To deploy a query profile: + + + +Create a file for the profile, using the format above, having the name *[my-profile-name].xml*, e.g. *MyProfile.xml* (replace any `/` in the name by `_`) + + +Put this in the directory *search/query-profiles* in the [application package](../basics/applications) root + + +[Redeploy](../basics/applications#deploying-applications) the application package + + + +Any number of query profile files may be added to this directory. If the query profiles contains errors, like incorrect syntax and/or infinite reference loops, deployment will fail. + +To use a query profile in a query request, send the name of the profile as the parameter `queryProfile`: + +```js +queryProfile=MyProfile +``` + +If the request does not specify a query profile, the profile named `default` will be used. If no `default` profile is configured, no profile will be used. If the queryProfile parameter is set but does not resolve to an existing profile, an error message is returned. Example, set default query timeout to 200ms for all queries not using a query profile: + +```bash +$ cat search/query-profiles/default.xml + + + 0.2 + +``` + +The query profile values (whether set from a configured query profile or by the request) is available as *query properties*. To look up a value from a [Searcher component](../applications/searchers), use: + +```js +query.properties().get("myVariable") +``` + +Note that property names are case-sensitive. + +### Example + +Use a query profile to modify the YQL query string using an [IN](../reference/querying/yql#in) operator and [local substitution](#local-substitution) of *cities*: + +```js + + select * from restaurant where userQuery() and city in (%{cities}) + "" + +``` + +Use [match: word](../reference/schemas/schemas#match) for the IN operator: + +```js +field city type string { + indexing: summary | index + match: word +} +``` + +An example query passing the values for the IN operator to be substituted into the YQL string: + +```bash +$ vespa query \ + queryProfile=city-filter \ + cities='"berlin","paris"' \ + query='what the user typed' +``` + +With this, the application can use the *city-filter* query profile if there are cities in the filter, if empty, use a *no-filter* query profile: + +```js + + select * from restaurant where userQuery() + + +``` + +```js +$ vespa query \ + queryProfile=no-filter \ + query='what the user typed' +``` + +### Overrides + +The parameter values set in *MyProfile.xml* will be used as if they were present directly in the request. If a parameter is present both directly in the request and in a profile, the request value takes precedence by default. Individual query profile field can be made to take priority by setting the [overridable](../reference/querying/query-profiles#overridable) attribute to `false`. Example: + +```xml + + 0.2 + +``` + +## Nested Structure + +To support structure in the set of request variables, a query profile value may not be a string but a reference to another query profile. In this case, the referenced query profile functions as a map (or struct, if types are used, see below) in the referencing query profile. The parameter names of the nested profile gets preceded by the name of the reference variable and a dot. For example: + +```xml + + 10 + merchantid + MyUserProfile + +``` + +Where the referenced profile might look like: + +```xml + + 20 + student + +``` + +If `MyProfile` is referenced in a query now, it will contain the variables + +```js +hits=10 +unique=merchantid +user.age=20 +user.profession=student +``` + +References can be nested to provide a deeper structure, producing variables having multiple dots. The dotted variables can be overridden directly in the search request (using the dotted name) just as other parameters. + +Note that the id value of a profile reference can also be set in the request, making it possible to choose not just the top level profile but also any number of specific subprofiles in the request. For example, the request can contain + +```text +queryProfile=MyUserProfile&user=ref:MyOtherUserprofile +``` + +to change the reference in the above example to some other subprofile. Note the `ref:` prefix which is required to identify this as setting user to a query profile referenced by id rather than setting it to a string. + +## Inheritance + +A query profile may inherit one or more other query profiles. This is useful when there is some common set of parameters applicable to multiple use cases, and a smaller set of parameters which varies between them. To inherit another query profile, reference it as follows: + +```xml + + … + +``` + +The parameters of `MyBaseProfile` will be present when this profile is used exactly as if they were explicitly written in this profile. + +Multiple inheritance is supported by specifying multiple space-separated profile ids in the inheritance attribute. Order matters in this list - if a parameter is set in more than one of the inherited profiles, the first one encountered in the depth first, left to right search order is used. + +Parameters specified in the child query profile will always override the same parameters in an inherited one. + +## Value Substitution + +Query profile values may contain substitution strings on the form `%{property-name}`. Example: + +```xml + + Hello %{world}! + Earth + +``` + +The value returned by looking up `message` will be *Hello Earth!*. + +### Global resolution + +Values are normally replaced by the value returned from `query.properties().get("property-name")` *at the time of the lookup*. Therefore, substituted values may be looked up in variants, in inherited profiles, in values set at run time and by following query profile references. Details: + +- No substitution will be performed *in* values set at run time +- If the value referenced in a substitution returns null, the reference is substituted by the empty string +- Unclosed substitutions cause an error at deploy time, but unknown values do not (they may exist at run time and will be replaced by an empty string if not) +- Recursive substitution works as expected. However, there is no loop detection + +### Local substitution + +To substitute by a value in the same query profile (or variant), prefix the property by a dot, as in + +```xml + + Hello %{.world}! + Earth + +``` + +Local substitutions can be verified at deploy time and will cause an error if not found. + +## Query Profile Variants + +In some cases, it is convenient to allow the values returned from a query profile to vary depending on the values of some properties input in the request. For example, a query profile may contain values which depend on both the market in which the request originated (`market`), the kind of device (`model`) *and* the bucket in question (`bucket`). + +Such variants over a set of request parameters may be represented in a single query profile, by defining nested variants of the query profile for the relevant combinations of request values. A complete example: + +```xml expandable + + + {/* Names of the request parameters defining the variant profiles of this. Order matters as described below. + Each individual value looked up in this profile is resolved from the most specific matching virtual + variant profile */} + region,model,bucket + + {/* Values may be set in the profile itself as usual, this becomes the default values given no matching + virtual variant provides a value for the property */} + My general a value + + {/* The "for" attribute in a child profile supplies values in order for each of the dimensions */} + + My value of the combination us-nokia-test1-a + + + {/* Same as [us,*,*] - trailing "*"'s may be omitted */} + + My value of the combination us-a + My value of the combination us-b + + + {/* Given a request which matches both the below, the one which specifies concrete values to the left + gets precedence over those specifying concrete values to the right + (i.e the first one gets precedence here) */} + + My value of the combination us-nokia-a + My value of the combination us-nokia-b + + + My value of the combination us-test1-a + My value of the combination us-test1-b + + + +``` + +### Variants and Inheritance + +It is possible to define variants across several levels in an inheritance hierarchy. The variant dimensions are inherited from parent to child, with the usual precedence rules (depth first left to right), so a parent profile may define the dimensions and the child the values over which it should vary. + +Variant resolution within a profile has precedence over resolution in parents. This means e.g. that a default value for a given property in a sub-profile will be chosen over a perfect variant match in an inherited profile. + +Variants may specify their own inherited profiles, as in: + +```xml + + … + + … + + +``` + +Values are resolved in this profile and inherited profiles "interleaved" by the variant resolution order (which is specificity by default). E.g. by decreasing priority: + +```text +1. Highest prioritized variant value +2. Value in inherited from highest prioritized variant +3. Next highest prioritized variant value +4. Value in inherited from next highest prioritized variant +… +n. Value defined at top level in profile +n+1. Value in inherited from query profile +``` + +## Query Profile Types + +The query profiles may optionally be *type checked*. Type checking is turned on by referencing a *Query Profile Type* from the query profile. The type lists the legal set of parameters of the query profile, whether additional parameters are allowed, and so on. + +A query profile type is referenced by: + +```xml + + … + +``` + +And the type is defined as: + +```xml + + + + + +``` + +This specifies that these three parameters may be present in profiles using this type, as well as the query profile type of the `user` parameter. + +It is also possible to specify that parameters are mandatory, that no additional parameters are allowed (strict), to inherit other types and so on, refer to the full syntax in [the query profile reference](../reference/querying/query-profiles#query-profile-types). If the base profile type is strict, it *must* extend a built-in query profile type, see the [strict reference documentation](../reference/querying/query-profiles#strict). + +A query profile type is deployed by adding a file named *[query-profile-type-name].xml* in the *search/query-profiles/types* directory in the application package. + +Query profile types may be useful even if query profiles are not used to set values. As they define the names, types and structure of the parameters which can be accepted in the search request, they can also be used to define, restrict and check the content of search requests. For example, as the built-in search api parameters are also type checked if a typed query profile is used, types can be used to restrict the parameters that can be set in a request, or to mandate that some are always set. The built-in parameters are defined in a set of query profile types which are always present and which can be inherited and referenced in application-defined types. These built-in types are defined in the [Query API](../reference/api/query). + +## Path Matching + +By adding `` to a profile type, *path* name matching is used rather than the default exact matching when a profile is looked up from a name. Path matching interprets the profile name as a slash separated path and matches references which are subpaths (more specific paths) to super-paths. The most specific match becomes the target of the reference. For example: + +```text +Given the query profile names: + a1 + a1/b1 + +Then: + a1/b1/c1/d1 resolves to a1/b1 + a1/b resolves to a1 + a does not resolve +``` + +This is useful to assign specific query profile id's to every client or bucket without having to create a different configuration item for each of these cases. If there is a need to provide a differentiated configuration for any such client or bucket in the future, this can be done without having the client change its request parameter because a specific id is already used. + +## Versioning + +Query profiles (and types) may exist in multiple versions at the same time. Wherever a name of a query profile (or type) is referenced, the name may also append a version string, separated by a colon, e.g `MyProfile:1.2.3`. The version number is *resolved* - if no version is given the highest version known is used. If the version number is only partially specified, as in `my-version:1`, the highest version starting by 1 is used. + +Where a query profile (or type) is defined, the id may specify the version, followed by a colon: + +```xml + + … + +``` + +Any sub-number omitted is taken to mean 0 where a version is defined, so `id="MyProfile:1"` is the same as `"id=MyProfile:1.0.0"`. + +Query profiles (and types) which specifies a version in their id must use a file name which includes the same version string after the name, separated by a dash, e.g. *MyProfile-1.2.3.xml*. + +For more information on versions, see [component versioning](../reference/applications/components#component-versioning). + +## Dump Tool + +It can sometimes be handy to be able to dump resolved query profiles offline. Run without arguments to get usage: + +```bash +$ vespa-query-profile-dump-tool +``` diff --git a/en/querying/result-diversity.md b/en/querying/result-diversity.md deleted file mode 100644 index 209608f722..0000000000 --- a/en/querying/result-diversity.md +++ /dev/null @@ -1,130 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Result Diversity" -redirect_from: - - /en/result-diversity ---- - -In Search and Recommendation applications, the highest-ranking documents are displayed to the user. -A document’s rank score is computed by a function over rank features. -The score is computed per document, independently of other documents’ scores -(except when using certain features in global-phase). - -This greedy approach gives the best overall result when documents are sufficiently dissimilar, -but if not it might look like this: - -![Result diversity example](/assets/img/diversity-1.png) - -This can be improved by preferring some less similar documents even though they produce a lower individual rank score, -which can usually be done by using the value of a field to create variation - like a domain field in the example above. - -Vespa has a set of features that can be used to create diversity, -this guide outlines strategies and tradeoffs to create good-looking result sets. - - - -## Diversity -You can use the [diversity](../reference/schemas/schemas.html#diversity) element in the schema definition -to filter out non-diverse results before second-phase ranking (and during match-phase if used): - -* The [attribute](../reference/schemas/schemas.html#diversity-attribute) value is the name of the attribute field - that holds the value to diversify over (think of the domain in the example above). -* [min-groups](../reference/schemas/schemas.html#diversity-min-groups) is the minimum number of different values - of that attribute that should be included in the result set (when available). - -Example: - -``` -field domain type string { - indexing: attribute | summary -} - -rank-profile diverse_example { - - first-phase { - expression: ... - } - - diversity { - attribute: domain - min-groups: 10 - } - - second-phase { - expression: ... - } - -} -``` - - - -## Grouping -Grouping is a general feature for organizing and aggregating in results, which can be used for diversification. -Grouping is specified at query time, so no rank-profile configuration is required. -For example, to get the highest ranking result per domain with grouping, use: - - all( group(domain) max(1) each( output(summary() ) ) ) - -With grouping, you have more accurate control over the behavior, -such as being able to specify the number of items per group. -You can also hierarchically group on multiple attributes, aggregate values etc. - -The grouping result structure comes in addition to (or instead of) the regular query results, -so enabling diversity using grouping will change the rendered result structure -(unless you flatten the grouped results in a Searcher before returning). - -Grouping runs on the results after second-phase ranking, and across all nodes. -Generally, this approach uses more resources compared to using the diversity element to filter out results after first-phase, -and means the second phase will be used to pick the best result also in each group (which gives better relevance at higher cost). - - - -## Match-phase diversity -Diversity above runs over the result after first-phase ranking. - -The [match-phase](../reference/schemas/schemas.html#match-phase) feature lets you increase performance -by limiting hits exposed to first-phase ranking to the highest (lowest) values of some attribute. -Adding the diversity element when using match-phase means that the diversity field attribute -is also used to produce the set of matches returned from the match-phase attribute. - -``` -field popularity type int { - indexing: attribute | summary -} - -field domain type string { - indexing: attribute | summary -} - -rank-profile diverse_example { - - match-phase { - attribute: popularity - total-max-hits: 1000 - max-filter-coverage: 1.0 - } - - diversity { - attribute: domain - min-groups: 10 - } - - first-phase { - expression: attribute(popularity) - } -} -``` - - - -## Collapsefield -The final processing before returning a result happens in a container node - -refer to [query execution](query-api.html#query-execution) for details. - -Setting the [collapsefield](../reference/api/query.html#collapsefield) parameter -lets you filter out hits which have the same value for one or more fields as a higher-ranked hit. - -Using collapsefield is a cheap option when results only contains a small number of duplicates on average, -since it defers all diversification work until the end. -When there may be many duplicates, using either the diversify tag or grouping should be preferred. diff --git a/en/querying/result-diversity.mdx b/en/querying/result-diversity.mdx new file mode 100644 index 0000000000..42080528fb --- /dev/null +++ b/en/querying/result-diversity.mdx @@ -0,0 +1,101 @@ +--- +title: "Result Diversity" +--- + +In Search and Recommendation applications, the highest-ranking documents are displayed to the user. A document’s rank score is computed by a function over rank features. The score is computed per document, independently of other documents’ scores (except when using certain features in global-phase). + +This greedy approach gives the best overall result when documents are sufficiently dissimilar, but if not it might look like this: + +![Result diversity example](/assets/img/diversity-1.png) + +This can be improved by preferring some less similar documents even though they produce a lower individual rank score, which can usually be done by using the value of a field to create variation - like a domain field in the example above. + +Vespa has a set of features that can be used to create diversity, this guide outlines strategies and tradeoffs to create good-looking result sets. + +## Diversity + +You can use the [diversity](../reference/schemas/schemas#diversity) element in the schema definition to filter out non-diverse results before second-phase ranking (and during match-phase if used): + +* The [attribute](../reference/schemas/schemas#diversity-attribute) value is the name of the attribute field that holds the value to diversify over (think of the domain in the example above). +* [min-groups](../reference/schemas/schemas#diversity-min-groups) is the minimum number of different values of that attribute that should be included in the result set (when available). + +Example: + +```js +field domain type string { + indexing: attribute | summary +} + +rank-profile diverse_example { + + first-phase { + expression: ... + } + + diversity { + attribute: domain + min-groups: 10 + } + + second-phase { + expression: ... + } + +} +``` + +## Grouping + +Grouping is a general feature for organizing and aggregating in results, which can be used for diversification. Grouping is specified at query time, so no rank-profile configuration is required. For example, to get the highest ranking result per domain with grouping, use: + +```bash +all( group(domain) max(1) each( output(summary() ) ) ) +``` + +With grouping, you have more accurate control over the behavior, such as being able to specify the number of items per group. You can also hierarchically group on multiple attributes, aggregate values etc. + +The grouping result structure comes in addition to (or instead of) the regular query results, so enabling diversity using grouping will change the rendered result structure (unless you flatten the grouped results in a Searcher before returning). + +Grouping runs on the results after second-phase ranking, and across all nodes. Generally, this approach uses more resources compared to using the diversity element to filter out results after first-phase, and means the second phase will be used to pick the best result also in each group (which gives better relevance at higher cost). + +## Match-phase diversity + +Diversity above runs over the result after first-phase ranking. + +The [match-phase](../reference/schemas/schemas#match-phase) feature lets you increase performance by limiting hits exposed to first-phase ranking to the highest (lowest) values of some attribute. Adding the diversity element when using match-phase means that the diversity field attribute is also used to produce the set of matches returned from the match-phase attribute. + +```js expandable +field popularity type int { + indexing: attribute | summary +} + +field domain type string { + indexing: attribute | summary +} + +rank-profile diverse_example { + + match-phase { + attribute: popularity + total-max-hits: 1000 + max-filter-coverage: 1.0 + } + + diversity { + attribute: domain + min-groups: 10 + } + + first-phase { + expression: attribute(popularity) + } +} +``` + +## Collapsefield + +The final processing before returning a result happens in a container node - refer to [query execution](/en/querying/query-api#query-execution) for details. + +Setting the [collapsefield](../reference/api/query#collapsefield) parameter lets you filter out hits which have the same value for one or more fields as a higher-ranked hit. + +Using collapsefield is a cheap option when results only contains a small number of duplicates on average, since it defers all diversification work until the end. When there may be many duplicates, using either the diversify tag or grouping should be preferred. diff --git a/en/querying/searching-multivalue-fields.md b/en/querying/searching-multivalue-fields.mdx similarity index 52% rename from en/querying/searching-multivalue-fields.md rename to en/querying/searching-multivalue-fields.mdx index 3adaabf95f..2f73072f9e 100644 --- a/en/querying/searching-multivalue-fields.md +++ b/en/querying/searching-multivalue-fields.mdx @@ -1,53 +1,51 @@ --- -# Copyright Vespa.ai. All rights reserved. title: "Searching and ranking of multivalued fields" -redirect_from: - - /en/searching-multi-valued-fields.html --- -This guide explains how to search and rank over structured multivalued fields. -The examples in this guide use the [weightedset](../reference/schemas/schemas.html#weightedset) -[field type](../reference/schemas/schemas.html#field). -The generic [map](../reference/schemas/schemas.html#map) field type -does not currently support ranking and can only be used for matching and filtering. +This guide explains how to search and rank over structured multivalued fields. The examples in this guide use the [weightedset](../reference/schemas/schemas#weightedset) [field type](../reference/schemas/schemas#field). The generic [map](../reference/schemas/schemas#map) field type does not currently support ranking and can only be used for matching and filtering. When building a search application we need to think about: -- How to [match](query-language.html) a user specified query against a [document schema](../basics/schemas.html) - using Vespa [query language](query-language.html). -- How to [rank](../basics/ranking.html) documents matching the query. - +- How to [match](/en/querying/query-language) a user specified query against a [document schema](../basics/schemas) using Vespa [query language](/en/querying/query-language). +- How to [rank](../basics/ranking) documents matching the query. ## Matching documents -There is a lot of text matching options we should think about when -designing and mapping our document model to a Vespa document schema: -- For string fields we should think about using text style matching or database-style exact matching. -- For string fields there are also several -[linguistic processing](../linguistics/linguistics.html) options like [tokenization](../linguistics/linguistics-opennlp.html#tokenization), -normalization and language dependent [stemming](../linguistics/linguistics-opennlp.html#stemming). -- String fields which shares the same [match](../reference/schemas/schemas.html#match) -and linguistic processing settings can be combined using [fieldsets](../reference/schemas/schemas.html#fieldset). +There is a lot of text matching options we should think about when designing and mapping our document model to a Vespa document schema: -At query time, we can take the user query and translate it into a valid Vespa query request which implements -our matching and retrieval strategy over the designed document schema. +- For string fields we should think about using text style matching or database-style exact matching. +- For string fields there are also several [linguistic processing](../linguistics/linguistics) options like [tokenization](../linguistics/linguistics-opennlp#tokenization), normalization and language dependent [stemming](../linguistics/linguistics-opennlp#stemming). +- String fields which shares the same [match](../reference/schemas/schemas#match) and linguistic processing settings can be combined using [fieldsets](../reference/schemas/schemas#fieldset). +At query time, we can take the user query and translate it into a valid Vespa query request which implements our matching and retrieval strategy over the designed document schema. ## Ranking documents -The documents which match the query and are retrieved by the query are scored using a ranking model. -Once a document is retrieved by the query logic the document can be scored using the full -flexibility of the Vespa [ranking](../basics/ranking.html) framework. +The documents which match the query and are retrieved by the query are scored using a ranking model. Once a document is retrieved by the query logic the document can be scored using the full flexibility of the Vespa [ranking](../basics/ranking) framework. ## A minimal Vespa application -In the following sections we explore matching and ranking over multivalued string fields. -{% include pre-req.html memory="4 GB" %} - -Assuming we have the following sample data document where we have a structured -tag-like field where there is a weight associated with each element. +In the following sections we explore matching and ranking over multivalued string fields. -
    +
    +**Prerequisites:**
    +
    +- Linux, macOS or Windows 10 Pro on x86\_64 or arm64, with [Podman Desktop](https://podman.io/) or [Docker Desktop](https://www.docker.com/products/docker-desktop/) installed, with an engine running.
    +	- Alternatively, start the Podman daemon:
    +        ```
    +        $ podman machine init --memory 6000
    +		$ podman machine start
    +        ```
    +	- See [Docker Containers](/en/operations/self-managed/docker-containers.html) for system limits and other settings.
    +- For CPUs older than Haswell (2013), see [CPU Support](/en/operations/self-managed/cpu-support).
    +- Memory: Minimum 4 GB RAM dedicated to Docker/Podman. [Memory recommendations](/en/operations/self-managed/node-setup#memory-settings).
    +- Disk: Avoid `NO_SPACE` - the vespaengine/vespa container image + headroom for data requires disk space. [Read more](/en/writing/feed-block.html).
    +- [Homebrew](https://brew.sh/) to install the [Vespa CLI](/en/clients/vespa-cli.html), or download the Vespa CLI from [Github releases](https://github.com/vespa-engine/vespa/releases).
    +
    +
    +Assuming we have the following sample data document where we have a structured tag-like field where there is a weight associated with each element. 
    +
    +```json
     {
         "put": "id:photos:photo::0",
         "fields": {
    @@ -62,24 +60,22 @@ tag-like field where there is a weight associated with each element.
             }
         }
     }
    -
    +``` + +`Paste the above into file doc.json` -Structured data like the tags, where we both want to match and rank is best represented using -the [weightedset](../reference/schemas/schemas.html#weightedset) [field type](../reference/schemas/schemas.html#field). -The Vespa weightedset field type can be used to represent: +Structured data like the `tags`, where we both want to match and rank is best represented using the [weightedset](../reference/schemas/schemas#weightedset) [field type](../reference/schemas/schemas#field). The Vespa weightedset field type can be used to represent: - Document side tags like in the above example. - [Document expansion by query prediction](https://github.com/castorini/docTTTTTquery). - Editorial ranking overrides, for example sponsored search listings. -How should we design our Vespa schema, -and how should we match and search this data model for end-user free text queries? +How should we design our Vespa schema, and how should we match and search this data model for end-user free text queries? - We want to use text matching when searching the title and description. -- We also want to match the free form tags field as these tags might increase recall - and the weight of the matched element(s) could influence ranking of documents matched - schema: +- We also want to match the free form tags field as these tags might increase recall and the weight of the matched element(s) could influence ranking of documents matched - schema: -
    +```js expandable
     schema photo {
     
         stemming: none
    @@ -102,7 +98,7 @@ schema photo {
                 indexing: summary | attribute
             }
     
    -        field tags type weightedset<string> {
    +        field tags type weightedset {
                 indexing: summary | index
                 match:text
                 index: enable-bm25
    @@ -120,199 +116,167 @@ schema photo {
             }
         }
     }
    -
    +``` -In the schema we disable [stemming](../reference/schemas/schemas.html#stemming) and -also enable [bm25](../ranking/bm25.html) text ranking feature for all string fields. +`Paste the above into file my-app/schemas/photo.sd` -Since all string fields shares the same [match](../reference/schemas/schemas.html#match) -settings we can use a [fieldset](../reference/schemas/schemas.html#fieldset) -so that queries does not need to mention all three fields. +In the schema we disable [stemming](../reference/schemas/schemas#stemming) and also enable [bm25](../ranking/bm25) text ranking feature for all string fields. -We also include a default rank profile (this is the implicit default rank profile) -using the Vespa [nativeRank](../ranking/nativerank.html) text matching rank feature. +Since all string fields shares the same [match](../reference/schemas/schemas#match) settings we can use a [fieldset](../reference/schemas/schemas#fieldset) so that queries does not need to mention all three fields. -Along with the schema, we also need a [services.xml](../reference/applications/services/services.html) file -to make up a Vespa [application package](../reference/applications/application-packages.html): +We also include a default rank profile (this is the implicit default rank profile) using the Vespa [nativeRank](../ranking/nativerank) text matching rank feature. -
    -<?xml version="1.0" encoding="UTF-8"?>
    -<services version="1.0">
    +Along with the schema, we also need a [services.xml](../reference/applications/services/services) file to make up a Vespa [application package](../reference/applications/application-packages):
     
    -    <container id="default" version="1.0">
    -        <search />
    -        <document-api />
    -        <nodes>
    -            <node hostalias="node1"></node>
    -        </nodes>
    -    </container>
    +```xml
    +
    +
     
    -    <content id="photos" version="1.0">
    -        <redundancy>1</redundancy>
    -        <documents>
    -            <document type="photo" mode="index"/>
    -        </documents>
    -        <nodes>
    -            <node hostalias="node1" distribution-key="0" />
    -        </nodes>
    -    </content>
    +    
    +        
    +        
    +        
    +            
    +        
    +    
     
    -</services>
    -
    + + 1 + + + + + + + + +``` + +`Paste the above into file my-app/services.xml` ## Starting Vespa + This example uses the vespa container image: -
    - -
    +
    +```bash
     $ docker pull vespaengine/vespa
     $ docker run --detach --name vespa --hostname vespa-container \
       --publish 8080:8080 --publish 19071:19071 \
       vespaengine/vespa
    -
    -
    +``` -Install [Vespa-cli](../clients/vespa-cli.html) using Homebrew: +Install [Vespa-cli](../clients/vespa-cli) using Homebrew: -
    +```bash
     $ brew install vespa-cli
    -
    +``` Deploy the application: -
    - -
    -$ vespa deploy --wait 300 my-app
    -
    -
    + +```bash +$ vespa deploy --wait 300 my-app +``` ## Feeding to Vespa Feed a sample document: -
    - -
    -$ vespa document -v doc.json 
    -
    -
    + +```bash +$ vespa document -v doc.json +``` ## Query the data -Assuming a free text query *sunset photos featuring dogs*, -translate the user query into a Vespa query request using YQL: -
    +Assuming a free text query *sunset photos featuring dogs*, translate the user query into a Vespa query request using YQL:
    +
    +```bash
     $ vespa query 'yql=select * from photos where userQuery()' \
       'query=sunset photos featuring dogs' \
       'type=all'
    -
    +``` -The above query returns 0 hits, since the query requires that *all* query terms matches the document. -By adding [trace.level](../reference/api/query.html#trace.level) to the query request we can see -how the query is parsed and executed against the content nodes: +The above query returns 0 hits, since the query requires that *all* query terms matches the document. By adding [trace.level](../reference/api/query#trace.level) to the query request we can see how the query is parsed and executed against the content nodes: -
    +```bash
     $ vespa query 'yql=select * from photos where userQuery()' \
       'query=sunset photos featuring dogs' \
       'type=all' \
       'trace.level=3'
    -
    +``` -In the trace we can see the query which is dispatched to the content nodes: -`query=[AND sunshot photos featuring dogs]` +In the trace we can see the query which is dispatched to the content nodes: `query=[AND sunshot photos featuring dogs]` Using tracing is very useful when debugging why documents match or does not match. -Since the sample document does not contain the term *featuring* or *photos*, -the query fails to retrieve the example document. -Relax the query matching to instead of requiring that **all** terms match, to use **any**. -See [model.type](../reference/api/query.html#model.type) query api reference for supported query types: +Since the sample document does not contain the term *featuring* or *photos*, the query fails to retrieve the example document. Relax the query matching to instead of requiring that **all** terms match, to use **any**. See [model.type](../reference/api/query#model.type) query api reference for supported query types: -
    +```bash
     $ vespa query 'yql=select * from photos where userQuery()' \
       'query=sunset photos featuring dogs' \
       'type=any'
    -
    +``` -Changing the type to `any`, recalls the sample document as we no longer require that all query terms must match. -With `type` it also possible to require that individual query terms match by using `+`: +Changing the type to `any`, recalls the sample document as we no longer require that all query terms must match. With `type` it also possible to require that individual query terms match by using `+`: -
    +```bash
     $ vespa query 'yql=select * from photos where userQuery()' \
       'query=+sunset photos featuring +dogs' \
       'type=any'
    -
    +``` -In this example `sunset` and `dogs` must be matched. Note that we have disabled stemming so querying -for `dogs` won't recall documents with `dog`. This is one of the reasons we disabled stemming, to demonstrate -that stemming has impact on recall. Requiring `dog` will cause the query to not recall our sample document. +In this example `sunset` and `dogs` must be matched. Note that we have disabled stemming so querying for `dogs` won't recall documents with `dog`. This is one of the reasons we disabled stemming, to demonstrate that stemming has impact on recall. Requiring `dog` will cause the query to not recall our sample document. -
    +```bash
     $ vespa query 'yql=select * from photos where userQuery()' \
       'query=+sunset photos featuring +dog' \
       'type=any'
    -
    +``` -Now, explore how Vespa matches the multivalued tags field of -type [weightedset](../reference/schemas/schemas.html#weightedset). -Notice that we change back to `type=all`. -In this example we also use the [default-index](../reference/api/query.html#model.defaultindex) -query parameter to limit matching to the `tags` field. +Now, explore how Vespa matches the multivalued tags field of type [weightedset](../reference/schemas/schemas#weightedset). Notice that we change back to `type=all`. In this example we also use the [default-index](../reference/api/query#model.defaultindex) query parameter to limit matching to the `tags` field. -
    +```bash
     $ vespa query 'yql=select * from photos where userQuery()' \
       'query=clear sky' \
       'type=all' \
       'default-index=tags'
    -
    +``` -The query matches the document which is no surprise since a tag contains the exact content `clear sky`. -Let us search for just `clear` instead: +The query matches the document which is no surprise since a tag contains the exact content `clear sky`. Let us search for just `clear` instead: -
    +```bash
     $ vespa query 'yql=select * from photos where userQuery()' \
       'query=clear' \
       'type=all' \
       'default-index=tags'
    -
    +``` -Also matches the document, this demonstrates that matching is partial, it does not require -to match the set element exactly. `clear` matches `clear sky` and `sky` will match `clear sky`. +Also matches the document, this demonstrates that matching is partial, it does not require to match the set element exactly. `clear` matches `clear sky` and `sky` will match `clear sky`. But what about `black sky`: -
    +```bash
     $ vespa query 'yql=select * from photos where userQuery()' \
       'query=black sky' \
       'type=all' \
       'default-index=tags'
    -
    +``` -Also matches the document. This is an example of cross-element matching. With weightedset -using `indexing:index` with `match:text` multi term queries match across elements. - -This might be a good decision, as we increase recall, -however in some cases we want to differentiate an exact match from a partial match during ranking, -so that exact matches are ranked higher than partial matches. +Also matches the document. This is an example of cross-element matching. With weightedset using `indexing:index` with `match:text` multi term queries match across elements. +This might be a good decision, as we increase recall, however in some cases we want to differentiate an exact match from a partial match during ranking, so that exact matches are ranked higher than partial matches. ## Ranking -We have now explored querying and matching, now it's time to focus on how to rank the documents matched. -You might not have noticed, but in the above examples, each of the queries produced a `relevance` score per hit, -this score was in our previous examples calculated using the `default` rank profile -which in our case used [nativeRank](../ranking/nativerank.html). - -We can start by analyzing other [rank features](../reference/ranking/rank-features.html) by asking Vespa to produce them for us. -We use [match-features](../reference/schemas/schemas.html#match-features) -to return rank features with the retrieved documents. -We explicitly mention which ranking features we want to have calculated and returned. -Notice that we don't change the actual scoring, we still use `nativeRank` as the scoring function: - -
    +
    +We have now explored querying and matching, now it's time to focus on how to rank the documents matched. You might not have noticed, but in the above examples, each of the queries produced a `relevance` score per hit, this score was in our previous examples calculated using the `default` rank profile which in our case used [nativeRank](../ranking/nativerank).
    +
    +We can start by analyzing other [rank features](../reference/ranking/rank-features) by asking Vespa to produce them for us. We use [match-features](../reference/schemas/schemas#match-features) to return rank features with the retrieved documents. We explicitly mention which ranking features we want to have calculated and returned. Notice that we don't change the actual scoring, we still use `nativeRank` as the scoring function:
    +
    +```js expandable
     schema photo {
     
         stemming: none
    @@ -335,7 +299,7 @@ schema photo {
                 indexing: summary | attribute
             }
     
    -        field tags type weightedset<string> {
    +        field tags type weightedset {
                 indexing: summary | index
                 match:text
                 index: enable-bm25
    @@ -369,44 +333,39 @@ schema photo {
             }
         }
     }
    -
    +``` + +`Paste the above into file my-app/schemas/photo.sd` Re-deploy with the changed rank profile: -
    - -
    +
    +```bash
     $ vespa deploy --wait 300 my-app
    -
    -
    +``` Now we will see a list of features in the response: -
    +
    +```bash
     $ vespa query 'yql=select * from photos where userQuery()' \
       'query=clear sky' \
       'type=any'
    -
    +``` -The output includes [matchfeatures](../reference/querying/default-result-format.html#matchfeatures) -where we can see the various scores for the features: +The output includes [matchfeatures](../reference/querying/default-result-format#matchfeatures) where we can see the various scores for the features: -Especially look at the `elementCompleteness` and `elementSimilarity` rank features which -are example of [features for indexed multivalued string -fields](../reference/ranking/rank-features.html#features-for-indexed-multivalue-string-fields). +Especially look at the `elementCompleteness` and `elementSimilarity` rank features which are example of [features for indexed multivalued string fields](../reference/ranking/rank-features#features-for-indexed-multivalue-string-fields). -We can also notice that `elementCompleteness(tags).fieldCompleteness` is 1.0 which means -that the tag was matched exactly and the `"elementCompleteness(tags).elementWeight` outputs -the weight of the best matched element. +We can also notice that `elementCompleteness(tags).fieldCompleteness` is 1.0 which means that the tag was matched exactly and the `"elementCompleteness(tags).elementWeight` outputs the weight of the best matched element. -The `elementSimilarity(tags)` ranking feature is very flexible and even allow us to override -the [calculation and output new features](../reference/ranking/rank-feature-configuration.html#elementSimilarity). +The `elementSimilarity(tags)` ranking feature is very flexible and even allow us to override the [calculation and output new features](../reference/ranking/rank-feature-configuration#elementSimilarity). In this example we defined two new ranking features: - `elementSimilarity(tags).sumWeight` which uses the sum of matching elements using field completeness x weight. - `elementSimilarity(tags).maxWeight` which uses the max over the matching elements using field completeness x weight. -
    +```js expandable
     schema photo {
     
         stemming: none
    @@ -429,7 +388,7 @@ schema photo {
                 indexing: summary | attribute
             }
     
    -        field tags type weightedset<string> {
    +        field tags type weightedset {
                 indexing: summary | index
                 match:text
                 index: enable-bm25
    @@ -471,36 +430,30 @@ schema photo {
             }
         }
     }
    -
    +``` + +`Paste the above into file my-app/schemas/photo.sd` Re-deploy with the changed rank profile: -
    - -
    +```bash
     $ vespa deploy --wait 300 my-app
    -
    -
    +``` Now we will see a list of features in the response: -
    +
    +```bash
     $ vespa query 'yql=select * from photos where userQuery()' \
       'query=clear sky' 'type=any'
    -
    +``` -Each hit returned contains a [matchfeatures](../reference/querying/default-result-format.html#matchfeatures) field -where we can see the various scores for the features. +Each hit returned contains a [matchfeatures](../reference/querying/default-result-format#matchfeatures) field where we can see the various scores for the features. -Now, we can include these features in a ranking expression used in `first-phase` to actually change the ranking. -The actual _best_ scoring function is data dependent. -A trained function using machine learning is by far the easiest way. +Now, we can include these features in a ranking expression used in `first-phase` to actually change the ranking. The actual _best_ scoring function is data dependent. A trained function using machine learning is by far the easiest way. -The bag of words [bm25](../ranking/bm25.html) ranking feature is not normalized, -so combining it in a linear function is challenging, as the score range of the feature is unbound. -To overcome this, and allow easy exploration without changing the rank profile, -make the parameters in the function overridable on a per-query basis by: +The bag of words [bm25](../ranking/bm25) ranking feature is not normalized, so combining it in a linear function is challenging, as the score range of the feature is unbound. To overcome this, and allow easy exploration without changing the rank profile, make the parameters in the function overridable on a per-query basis by: -
    +```js
     first-phase {
         expression {
             query(titleWeight)*bm25(title) +
    @@ -508,11 +461,11 @@ first-phase {
             query(tagWeight)*elementSimilarity(tags).maxWeight
         }
     }
    -
    +``` -See [using query variables](../ranking/ranking-expressions-features.html#using-query-variables). +See [using query variables](../ranking/ranking-expressions-features#using-query-variables). -
    +```js expandable
     schema photo {
     
         stemming: none
    @@ -535,7 +488,7 @@ schema photo {
                 indexing: summary | attribute
             }
     
    -        field tags type weightedset<string> {
    +        field tags type weightedset {
                 indexing: summary | index
                 match:text
                 index: enable-bm25
    @@ -574,43 +527,40 @@ schema photo {
             }
         }
     }
    -
    +``` + +`Paste the above into file my-app/schemas/photo.sd` Re-deploy: -
    - -
    +```bash
     $ vespa deploy --wait 300 my-app
    -
    -
    +``` Run a query with the new rank profile: -
    +```bash
     $ vespa query 'yql=select * from photos where userQuery()' \
       'query=clear sky' 'type=any' 'ranking=tunable'
    -
    +``` -With the function above, since 'clear sky' does not match any of the title or description fields, -the bm25 features becomes zero. +With the function above, since 'clear sky' does not match any of the title or description fields, the bm25 features becomes zero. -Our `elementSimilarity(tags).maxWeight` feature is 2.0 and the first phase -expression becomes 4 which is reflected in the hit relevance score. +Our `elementSimilarity(tags).maxWeight` feature is 2.0 and the first phase expression becomes 4 which is reflected in the hit relevance score. -Change the query(tagWeight) with the query request and observe that the relevance becomes 6.0: +Change the `query(tagWeight)` with the query request and observe that the relevance becomes 6.0: -
    +```bash
     $ vespa query 'yql=select * from photos where userQuery()' \
       'query=clear sky' \
       'type=any' \
       'ranking=tunable' \
       'input.query(tagWeight)=3'
    -
    +``` Similar, we could also include a document-only signal to our ranking function by: -
    +```js expandable
     inputs {
         query(titleWeight): 2
         query(descriptionWeight): 1
    @@ -629,12 +579,10 @@ first-phase {
             query(tagWeight)*elementSimilarity(tags).maxWeight + query(staticWeight)*attribute(interestingness)
         }
     }
    -
    +``` That concludes the matching and ranking experiments. To shut down the container: -
    +```bash
     $ docker rm -f vespa
    -
    - - +``` diff --git a/en/querying/text-matching.html b/en/querying/text-matching.html deleted file mode 100644 index 058e1dafe6..0000000000 --- a/en/querying/text-matching.html +++ /dev/null @@ -1,485 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Text Matching" -redirect_from: -- /en/text-matching-ranking.html -- /en/text-matching.html ---- - - - -

    - This guide demonstrates tokenization, linguistic processing and matching over string - fields in Vespa. The guide features examples based on the quick start. -

    - -

    - Refer to the ranking introduction for ranking, - and review the different match modes that Vespa supports per field. - See the text search and - text search through ML tutorials. - Finally, refer to linguistics for linguistic processing in Vespa. -

    -

    Using query tracing is useful when debugging text matching.

    - -

    Index and attribute

    -

    - Vespa string fields can have a mix of settings specified per field, - such as the indexing and - match modes. -

    -
      -
    • - The index for free-text search with default match mode text, integrating with linguistic - processing such as tokenization and stemming. -
    • -
    • - The attribute indexing is used for - database-style of string matching without linguistic processing and where the exact string contents are matched. -
    • -
    -

    Free-text search is normally solved using a string field in index mode:

    -
    -field album type string {
    -    indexing: summary | index
    -}
    -
    -

    - If both index and attribute are configured for string-type fields, Vespa will search and match against the - index with default match text. When the field is both index and attribute, the index - aspect is used for matching. (The attribute could still be useful in general, for grouping and sorting.) - To get multiple match modes on a single source field you could define a synthetic field outside the document block: -

    -
    -schema music {
    -
    -  document music {
    -
    -    field album type string {
    -      indexing: summary | index
    -    }
    -
    -  }
    -
    -  field album_as_attribute type string {
    -    indexing: input album | attribute
    -  }
    -
    -}
    -
    -

    - If you want substring matching for indexed search, consider using n-gram matching. -

    - - -

    Below, find details on transformations to the text for text indexing and search using the quick start - sample application as an example.

    - -

    - The album field has index mode. - For text fields, this enables transformations of the string field to increase query recall. -

    - -

    -The following is useful for dumping the resulting text tokens after indexing, to understand the transformations. This - coupled with query tracing can help us understand why a document field doesn't match or match a query.

    -

    -Add another document summary -to schemas/music.sd that contains an extra summary field -using tokens -with the source -set to the proper index field: -

    -
    -document-summary my-debug-summary {
    -    summary album { }
    -    summary album_tokens {
    -        source: album
    -        tokens
    -    }
    -    from-disk
    -}
    -
    -fieldset default {
    -    fields: artist, album
    -}
    -
    -

    Redeploy the application to enable the new document summary:

    -
    -$ vespa deploy --wait 300
    -
    -

    Show original content of album field:

    -
    -$ vespa query "select * from music where true" summary=my-debug-summary | \
    -  jq -c '.root.children[].fields.album'
    -"Liebe ist für alle da"
    -"A Head Full of Dreams"
    -"Hardwired...To Self-Destruct"
    -"When We All Fall Asleep, Where Do We Go?"
    -"Love Is Here To Stay"
    -
    -

    Show tokens used for indexing the album field:

    -
    -$ vespa query "select * from music where true" summary=my-debug-summary | \
    -  jq -c '.root.children[].fields.album_tokens'
    -["lieb","ist","fur","all","da"]
    -["a","head","full","of","dream"]
    -["hardwire","to","self","destruct"]
    -["when","we","all","fall","asleep","where","do","we","go"]
    -["love","is","here","to","stay"]
    -
    -

    Observe the linguistic transformations to the data before indexed:

    - - - - - - - - - - - - - - - - - - -
    TransformationType
    Hardwired...To → hardwire toTokenization - split terms on non-characters, here "..."
    Head → headLowercasing
    für → furNormalizing
    dreams → dreamStemming
    -

    -Then, change from index to attribute -in -schemas/music.sd (and remove all bm25 settings): -

    -
    -@@ -13,8 +13,7 @@
    -         }
    - 
    -         field album type string {
    --            indexing: summary | index
    --            index: enable-bm25
    -+            indexing: summary | attribute
    -         }
    - 
    -         field year type int {
    -@@ -51,7 +50,7 @@
    -             query(user_profile) tensor<float>(cat{})
    -         }
    -         first-phase {
    --            expression: bm25(album) + 0.25 * sum(query(user_profile) * attribute(category_scores))
    -+            expression: 0.25 * sum(query(user_profile) * attribute(category_scores))
    -         }
    -     }
    -
    -

    -Run the tutorial again using the new schema, stop after feeding. -Show tokens used for indexing the album field: -

    -
    -$ vespa query "select * from music where true" summary=my-debug-summary | \
    -  jq -c '.root.children[].fields.album_tokens'
    -["a head full of dreams"]
    -["love is here to stay"]
    -["when we all fall asleep, where do we go?"]
    -["liebe ist für alle da"]
    -["hardwired...to self-destruct"]
    -
    -

    -The most important observation is that the strings are added as-is for attributes and matching considers the full value, including whitespace (no tokenization). -

    -

    -The only transformation is lowercasing, both query terms and attribute data are lowercased before matching unless the match -setting for the field has been set to cased. -Read more about the attribute word match mode. -

    - - - -

    Prefix match

    -

    - Use the prefix annotation - to match string prefixes in attributes of type string: -

    -
    -field album type string {
    -    indexing: summary | attribute
    -}
    -
    -

    Note that regular index fields does not support prefix matching.

    -
    -$ vespa query 'select * from music where album contains ({prefix: true}"a hea")'
    -
    -

    - The - search-suggestions sample application uses prefix search, see README for a design discussion. -

    -

    - To prefix-match individual terms in a string, - use an attribute with array of strings in addition to the index string field, e.g.: -

    -
    -schema company {
    -    document company {
    -        field company_name_string type string {
    -            indexing: index | summary
    -        }
    -    }
    -    field company_name_array type array<string> {
    -        indexing: input company_name_string | trim | split " +" | attribute | summary
    -    }
    -}
    -
    -

    - Use the indexing-language to split the string, as shown above. - Adding "Goldman" and "Sachs" will match query terms like "Gold" and "Sach". -

    - - - -

    Fuzzy match

    -

    - Use fuzzy matching - to match in string attributes with configurable edit distance. Field configuration: -

    -
    -field album type string {
    -    indexing:  summary | attribute
    -    attribute: fast-search
    -}
    -
    -
    -$ vespa query 'select * from music where album contains ({maxEditDistance: 1}fuzzy("A Head Full of Dreems"))'
    -
    -

    - Fuzzy matching is great for misspellings. - See use of prefixLength and fast-search in the - reference. -

    -

    - Character normalization is not performed for fuzzy matches. -

    -

    Fuzzy prefix match

    -

    - By default, fuzzy matches full strings against the query. For use-cases such as - type-ahead search this means a user query such as "Ahead Full" will fail to match the document string - "A Head Full of Dreams", both when using fuzzy matching (too many characters missing) as well - as regular, non-fuzzy prefix matching (prefixes do not exactly match). -

    -

    - Adding prefix:true enables fuzzy prefix semantics. If a string has a prefix - that can match the query string within the specified maximum number of edits, it will be considered a match. -

    -
    -$ vespa query 'select * from music where album contains ({maxEditDistance: 1, prefix: true}fuzzy("Ahead Full"))'
    -
    -

    - This query will match strings such as "A Head Full of Dreams", "A Head Full of Clouds", "Ahead Full Steam" etc. -

    -

    - Exact prefix locking (prefixLength:n) can be used alongside fuzzy prefix matching to constrain the - candidate set to strings that have prefix that exactly matches n characters of the query. Fuzzy prefix - matching then applies to the remainder (suffix) of the candidate string. This greatly speeds up dictionary scans - since only a subset of the dictionary needs to be considered. -

    - {% include important.html content="Fuzzy prefix matching often matches many more documents than non-prefix - fuzzy matching. For instance, a query such as {maxEditDistance:2,prefix:true}fuzzy(\"XY\") will - end up matching every document, since all possible strings can have their prefix transformed to \"XY\" - with at most 2 edits. This is the case for all fuzzy prefix queries where the length of the query string is equal to, - or lower than, maxEditDistance. This should be taken into consideration when constructing queries - based on user input."%} - - - -

    Test fuzzy matching

    -

    Use a synthetic (derived) field to easily test fuzzy matching:

    -
    -schema music {
    -
    -    document music {
    -        field songs type array<string> {
    -            indexing: summary | index
    -            index: enable-bm25
    -        }
    -    }
    -
    -    field songs_attr type array<string> {
    -        indexing: input songs | attribute | summary
    -        attribute: fast-search
    -    }
    -}
    -
    -

    - In the example above, you have a field with an array of song titles (in the document) section in the schema. - Take the value of that field, and input into an attribute, - which is a field type that supports fuzzy matching. -

    -

    - Add this field and deploy to Vespa. - On Vespa Cloud, the data is automatically reindexed, so the songs_attr field is populated with data. - This might take a little time on a large corpus. - Then test with a query: -

    -
    -$ vespa query 'select * from music where songs_attr contains ({maxEditDistance: 1}fuzzy("enter zandman"))'
    -
    - - - -

    Regular expression match

    -

    - Using regular expressions - is supported in attributes. - In the general case, there are however no optimizing data structures for query speed, - it runs the expression over all attribute values. - If fast-search is present on the attribute, explicitly prefix anchored regex queries - (e.g. ^abc.*") will run more efficiently, i.e. only over the range of potentially - matching attribute values (must start with "abc"). -

    -
    -field album type string {
    -    indexing:  summary | attribute
    -}
    -
    -

    Example, matching from start of string:

    -
    -$ vespa query 'select * from music where album matches "^a head fu[l]+ of dreams"'
    -
    -

    A substring search:

    -
    -$ vespa query 'select * from music where album matches "head"'
    -
    - -

    - Character normalization is not performed for regular expression matches. -

    - -

    N-Gram match

    - -

    N-gram matching splits text into subword tokens ("grams") of the specified N size. -This is useful with CJK languages which does not use space, as well as for substring matching -in text indexes.

    - -

    Vespa will by default combine the grams it creates from a text in queries into an AND item, -requiring all of them to match. This can be overridden by sending the query parameter gram.match, -which can be set to the name of any composite query item: -all (default), any, weakAnd, phrase, near, or onear.

    - -

    -

    -
    -Example: Input text "滿腦子的夢想" with these settings
    -
    -    field album type string {
    -        indexing: {
    -            "zh-hant" | set_language;
    -            summary | index
    -        }
    -        match {
    -            gram
    -            gram-size: 2
    -        }
    -    }
    -
    -Produces the tokens 子的 夢想 滿腦 的夢 腦子
    -
    -Any query which contains at least two tokens frmo this text will therefore be matched.
    -
    - - -

    Example use case

    -

    - What is the best way to index short word-length documents, - like names of all locations/towns in the world, such that they: -

    -
      -
    • Are robust to misspelling in user queries eg: "Amsterdam" --> "amstredam"
    • -
    • Are cross-lingual for search, e.g.: "America" --> "美國"
    • -
    -

    - To make this multilingual, - use an array<string> field to store all the alternatives. - One can also translate to a canonical single language used in indexing at query time, - but in cases with very short documents, opt for doing it indexing time. -

    -

    Alternatives for matching with spell checking:

    -
      -
    1. Make the field an attribute and use fuzzy matching.
    2. -
    3. Make the field an index with gram matching.
    4. -
    5. - Having an array of alternatives anyway, just stuff all the misspellings to match into it. - Consider using a weighted set instead - to weight them by closeness to the original. -
    6. -
    -

    - 3. will give the cheapest queries and exact control over misspelled matching, - but a larger index, more work for the developer, and adjusting spell correction becomes more complicated. - 1. will be most expensive, but maybe also most convenient - There are currently no rank signals giving you the match quality. - 2. Is in between, and will probably work best when incorporating ranking signals that use proximity - (such as e.g. nativeRank but not bm25). -

    -

    - Read - Simplify Search with Multilingual Embedding Models for semantic matching and ranking. -

    - - - -

    Query Trace

    -

    - Adding trace.level=2 - gives insight when testing queries - example attribute lowercasing - (observe that queries with "Liebe" and "liebe" give the same result): -

    -
    -$ vespa config set target local
    -$ vespa query 'select * from music where album contains "Liebe ist für alle da"' \
    -  ranking=rank_albums \
    -  trace.level=2
    -
    -

    Also try query tracing to see how query parsing changes with index and attribute indexing modes.

    - - - -

    Appendix: Match Configuration Debugging

    -

    - Inspect generated configuration to understand or validate the match configuration. - Run this to find the value of the -i argument used below: -

    -
    -$ docker exec vespa sh -c vespa-configproxy-cmd | grep IndexingProcessor
    -
    -  vespa.configdefinition.ilscripts,default/docprocchains/chain/indexing/component/com.yahoo.docprocs.indexing.IndexingProcessor, ...
    -
    -

    - Start over, deploy with the indexing settings below and feed data. - Note the difference for the artist (with exact matching) and album fields: -

    -
    -field artist type string {
    -    indexing: summary | index
    -    match   : exact
    -}
    -field album type string {
    -    indexing: summary | index
    -}
    -
    -$ docker exec vespa sh -c 'vespa-get-config \
    -  -n vespa.configdefinition.ilscripts \
    -  -i default/docprocchains/chain/indexing/component/com.yahoo.docprocs.indexing.IndexingProcessor'
    -
    -maxtermoccurrences 100
    -fieldmatchmaxlength 1000000
    -ilscript[0].doctype "music"
    -ilscript[0].docfield[0] "artist"
    -ilscript[0].docfield[1] "album"
    -ilscript[0].docfield[2] "year"
    -ilscript[0].docfield[3] "category_scores"
    -ilscript[0].content[0] "clear_state | guard { input artist | exact | summary artist | index artist; }"
    -ilscript[0].content[1] "clear_state | guard { input album | tokenize normalize stem:"BEST" | summary album | index album; }"
    -ilscript[0].content[2] "clear_state | guard { input year | summary year | attribute year; }"
    -ilscript[0].content[3] "clear_state | guard { input category_scores | summary category_scores | attribute category_scores; }"
    -
    diff --git a/en/querying/text-matching.mdx b/en/querying/text-matching.mdx new file mode 100644 index 0000000000..af9264ff82 --- /dev/null +++ b/en/querying/text-matching.mdx @@ -0,0 +1,388 @@ +--- +title: "Text Matching" +--- + +This guide demonstrates tokenization, linguistic processing and matching over [string](../reference/schemas/schemas#string) fields in Vespa. The guide features examples based on the [quick start](../basics/deploy-an-application-local). + +Refer to the [ranking](../basics/ranking) introduction for ranking, and review the different [match modes](../reference/schemas/schemas#match) that Vespa supports per field. See the [text search](../learn/tutorials/text-search) and [text search through ML](../learn/tutorials/text-search-ml) tutorials. Finally, refer to [linguistics](../linguistics/linguistics) for linguistic processing in Vespa. + +Using [query tracing](#query-trace) is useful when debugging text matching. + +## Index and attribute + +Vespa string fields can have a mix of settings specified per field, such as the [indexing](../reference/schemas/schemas#indexing) and [match modes](../reference/schemas/schemas#match). + +- The *index* for free-text search with default match mode *text*, integrating with linguistic processing such as [tokenization and stemming](../linguistics/linguistics). +- The *attribute* indexing is used for database-style of string matching without linguistic processing and where the exact string contents are matched. + +Free-text search is normally solved using a string field in *index* mode: + +```js +field album type string { + indexing: summary | index +} +``` + +If both index and attribute are configured for string-type fields, Vespa will search and match against the index with default match `text`. When the field is both index and attribute, the index aspect is used for matching. (The attribute could still be useful in general, for grouping and sorting.) To get multiple match modes on a single source field you could define a synthetic field outside the document block: + +```js +schema music { + + document music { + + field album type string { + indexing: summary | index + } + + } + + field album_as_attribute type string { + indexing: input album | attribute + } + +} +``` + +If you want substring matching for indexed search, consider using [n-gram matching](#n-gram-match). + +Below, find details on transformations to the text for text indexing and search using the quick start sample application as an example. + +The *album* field has [index](../reference/schemas/schemas#indexing) mode. For text fields, this enables transformations of the string field to increase query recall. + +The following is useful for dumping the resulting text tokens after indexing, to understand the transformations. This coupled with [query tracing](#query-trace) can help us understand why a document field doesn't match or match a query. + +Add another [document summary](/en/querying/document-summaries) to [schemas/music.sd](https://github.com/vespa-engine/sample-apps/blob/master/album-recommendation/app/schemas/music.sd) that contains an extra summary field using [tokens](../reference/schemas/schemas#tokens) with the [source](../reference/schemas/schemas#source) set to the proper index field: + +```js +document-summary my-debug-summary { + summary album { } + summary album_tokens { + source: album + tokens + } + from-disk +} + +fieldset default { + fields: artist, album +} +``` + +Redeploy the application to enable the new document summary: + +```bash +$ vespa deploy --wait 300 +``` + +Show original content of album field: + +```bash +$ vespa query "select * from music where true" summary=my-debug-summary | \ + jq -c '.root.children[].fields.album' +"Liebe ist für alle da" +"A Head Full of Dreams" +"Hardwired...To Self-Destruct" +"When We All Fall Asleep, Where Do We Go?" +"Love Is Here To Stay" +``` + +Show tokens used for indexing the album field: + +```bash +$ vespa query "select * from music where true" summary=my-debug-summary | \ + jq -c '.root.children[].fields.album_tokens' +["lieb","ist","fur","all","da"] +["a","head","full","of","dream"] +["hardwire","to","self","destruct"] +["when","we","all","fall","asleep","where","do","we","go"] +["love","is","here","to","stay"] +``` + +Observe the [linguistic transformations](../linguistics/linguistics) to the data before indexed: + + + + + + + + + + + + + + + + + + + + + + + + + + +
    TransformationType
    Hardwired...To → hardwire toTokenization - split terms on non-characters, here "..."
    Head → headLowercasing
    für → furNormalizing
    dreams → dreamStemming
    + +Then, change from *index* to [attribute](../reference/schemas/schemas#indexing) in [schemas/music.sd](https://github.com/vespa-engine/sample-apps/blob/master/album-recommendation/app/schemas/music.sd) (and remove all bm25 settings): + +```js +@@ -13,8 +13,7 @@ + } + + field album type string { +- indexing: summary | index +- index: enable-bm25 ++ indexing: summary | attribute + } + + field year type int { +@@ -51,7 +50,7 @@ + query(user_profile) tensor(cat{}) + } + first-phase { +- expression: bm25(album) + 0.25 * sum(query(user_profile) * attribute(category_scores)) ++ expression: 0.25 * sum(query(user_profile) * attribute(category_scores)) + } + } +``` + +Run the tutorial again using the new schema, stop after feeding. Show tokens used for indexing the album field: + +```bash +$ vespa query "select * from music where true" summary=my-debug-summary | \ + jq -c '.root.children[].fields.album_tokens' +["a head full of dreams"] +["love is here to stay"] +["when we all fall asleep, where do we go?"] +["liebe ist für alle da"] +["hardwired...to self-destruct"] +``` + +The most important observation is that the strings are added *as-is* for attributes and matching considers the full value, including whitespace (no tokenization). + +The only transformation is lowercasing, both query terms and attribute data are lowercased before matching unless the `match` setting for the field has been set to `cased`. Read more about the attribute [word match mode](../reference/schemas/schemas#match). + +## Prefix match + +Use the [prefix](../reference/querying/yql#prefix) annotation to match string prefixes in attributes of type string: + +```js +field album type string { + indexing: summary | attribute +} +``` + +Note that regular *index* fields does not support prefix matching. + +```bash +$ vespa query 'select * from music where album contains ({prefix: true}"a hea")' +``` + +The [search-suggestions](https://github.com/vespa-engine/sample-apps/tree/master/incremental-search/search-suggestions) sample application uses prefix search, see README for a design discussion. + +To prefix-match individual terms in a string, use an attribute with array of strings in addition to the index string field, e.g.: + +```js +schema company { + document company { + field company_name_string type string { + indexing: index | summary + } + } + field company_name_array type array { + indexing: input company_name_string | trim | split " +" | attribute | summary + } +} +``` + +Use the [indexing-language](../reference/writing/indexing-language) to split the string, as shown above. Adding "Goldman" and "Sachs" will match query terms like "Gold" and "Sach". + +## Fuzzy match + +Use [fuzzy](../reference/querying/yql#fuzzy) matching to match in string attributes with configurable edit distance. Field configuration: + +```js +field album type string { + indexing: summary | attribute + attribute: fast-search +} +``` + +```bash +$ vespa query 'select * from music where album contains ({maxEditDistance: 1}fuzzy("A Head Full of Dreems"))' +``` + +Fuzzy matching is great for misspellings. See use of *prefixLength* and *fast-search* in the [reference](../reference/querying/yql#fuzzy). + +Character [normalization](../linguistics/linguistics-opennlp#normalization) is not performed for fuzzy matches. + +### Fuzzy prefix match + +By default, `fuzzy` matches *full* strings against the query. For use-cases such as type-ahead search this means a user query such as "Ahead Full" will fail to match the document string "A Head Full of Dreams", both when using fuzzy matching (too many characters missing) as well as regular, *non-fuzzy* prefix matching (prefixes do not exactly match). + +Adding `prefix:true` enables *fuzzy prefix* semantics. If a string has a *prefix* that can match the query string within the specified maximum number of edits, it will be considered a match. + +```bash +$ vespa query 'select * from music where album contains ({maxEditDistance: 1, prefix: true}fuzzy("Ahead Full"))' +``` + +This query will match strings such as "A Head Full of Dreams", "A Head Full of Clouds", "Ahead Full Steam" etc. + +Exact prefix locking (`prefixLength:n`) can be used alongside fuzzy prefix matching to constrain the candidate set to strings that have prefix that *exactly* matches *n* characters of the query. Fuzzy prefix matching then applies to the remainder (suffix) of the candidate string. This greatly speeds up dictionary scans since only a subset of the dictionary needs to be considered. + + +**Important:** + +Fuzzy prefix matching often matches many more documents than non-prefix fuzzy matching. For instance, a query such as `{maxEditDistance:2,prefix:true}fuzzy("XY")` will end up matching *every* document, since *all possible strings* can have their prefix transformed to "XY" with at most 2 edits. This is the case for all fuzzy prefix queries where the length of the query string is equal to, or lower than, `maxEditDistance`. This should be taken into consideration when constructing queries based on user input. + + +### Test fuzzy matching + +Use a synthetic (derived) field to easily test fuzzy matching: + +```js expandable +schema music { + + document music { + field songs type array { + indexing: summary | index + index: enable-bm25 + } + } + + field songs_attr type array { + indexing: input songs | attribute | summary + attribute: fast-search + } +} +``` + +In the example above, you have a field with an array of song titles (in the `document`) section in the schema. Take the value of that field, and input into an [attribute](../content/attributes), which is a field type that supports fuzzy matching. + +Add this field and deploy to Vespa. On Vespa Cloud, the data is automatically reindexed, so the `songs_attr` field is populated with data. This might take a little time on a large corpus. Then test with a query: + +```bash +$ vespa query 'select * from music where songs_attr contains ({maxEditDistance: 1}fuzzy("enter zandman"))' +``` + +## Regular expression match + +Using [regular expressions](../reference/querying/yql#matches) is supported in attributes. In the general case, there are however no optimizing data structures for query speed, it runs the expression over all attribute values. If *fast-search* is present on the attribute, explicitly prefix anchored regex queries (e.g. `^abc.*`") will run more efficiently, i.e. only over the range of potentially matching attribute values (must start with "abc"). + +```js +field album type string { + indexing: summary | attribute +} +``` + +Example, matching from start of string: + +```bash +$ vespa query 'select * from music where album matches "^a head fu[l]+ of dreams"' +``` + +A substring search: + +```bash +$ vespa query 'select * from music where album matches "head"' +``` + +Character [normalization](../linguistics/linguistics-opennlp#normalization) is not performed for regular expression matches. + +## N-Gram match + +N-gram matching splits text into subword tokens ("grams") of the specified N size. This is useful with CJK languages which does not use space, as well as for substring matching in text indexes. + +Vespa will by default combine the grams it creates from a text in queries into an AND item, requiring all of them to match. This can be overridden by sending the query parameter `gram.match`, which can be set to the name of any composite query item: `all` (default), `any`, `weakAnd`, `phrase`, `near`, or `onear`. + +```js +Example: Input text "滿腦子的夢想" with these settings + + field album type string { + indexing: { + "zh-hant" | set_language; + summary | index + } + match { + gram + gram-size: 2 + } + } + +Produces the tokens 子的 夢想 滿腦 的夢 腦子 + +Any query which contains at least two tokens frmo this text will therefore be matched. +``` + +## Example use case + +*What is the best way to index short word-length documents, like names of all locations/towns in the world, such that they:* + +- *Are robust to misspelling in user queries eg: "Amsterdam" --> "amstredam"* +- *Are cross-lingual for search, e.g.: "America" --> "美國"* + +To make this multilingual, use an [array``](../reference/schemas/schemas#array) field to store all the alternatives. One can also translate to a canonical single language used in indexing at query time, but in cases with very short documents, opt for doing it indexing time. + +Alternatives for matching with spell checking: + 1. Make the field an attribute and use [fuzzy matching](#fuzzy-match). + 2. Make the field an index with [gram matching](#n-gram-match). + 3. Having an array of alternatives anyway, just stuff all the misspellings to match into it. Consider using a [weighted set](../reference/schemas/schemas#weightedset) instead to weight them by closeness to the original. + +3\. will give the cheapest queries and exact control over misspelled matching, but a larger index, more work for the developer, and adjusting spell correction becomes more complicated. 1. will be most expensive, but maybe also most convenient There are currently no rank signals giving you the match quality. 2. Is in between, and will probably work best when incorporating ranking signals that use proximity (such as e.g. [nativeRank](../ranking/nativerank) but not [bm25](../ranking/bm25)). + +Read [Simplify Search with Multilingual Embedding Models](https://blog.vespa.ai/simplify-search-with-multilingual-embeddings/) for semantic matching and ranking. + +## Query Trace + +Adding [trace.level=2](../reference/api/query#trace.level) gives insight when testing queries - example attribute lowercasing (observe that queries with "Liebe" and "liebe" give the same result): + +```bash +$ vespa config set target local +$ vespa query 'select * from music where album contains "Liebe ist für alle da"' \ + ranking=rank_albums \ + trace.level=2 +``` + +Also try query tracing to see how query parsing changes with *index* and *attribute* indexing modes. + +## Appendix: Match Configuration Debugging + +Inspect generated configuration to understand or validate the match configuration. Run this to find the value of the -i argument used below: + +```bash +$ docker exec vespa sh -c vespa-configproxy-cmd | grep IndexingProcessor + + vespa.configdefinition.ilscripts,default/docprocchains/chain/indexing/component/com.yahoo.docprocs.indexing.IndexingProcessor, ... +``` + +Start over, deploy with the indexing settings below and feed data. Note the difference for the *artist* (with exact matching) and *album* fields: + +```js +field artist type string { + indexing: summary | index + match : exact +} +field album type string { + indexing: summary | index +} + +$ docker exec vespa sh -c 'vespa-get-config \ + -n vespa.configdefinition.ilscripts \ + -i default/docprocchains/chain/indexing/component/com.yahoo.docprocs.indexing.IndexingProcessor' + +maxtermoccurrences 100 +fieldmatchmaxlength 1000000 +ilscript[0].doctype "music" +ilscript[0].docfield[0] "artist" +ilscript[0].docfield[1] "album" +ilscript[0].docfield[2] "year" +ilscript[0].docfield[3] "category_scores" +ilscript[0].content[0] "clear_state | guard { input artist | exact | summary artist | index artist; }" +ilscript[0].content[1] "clear_state | guard { input album | tokenize normalize stem:"BEST" | summary album | index album; }" +ilscript[0].content[2] "clear_state | guard { input year | summary year | attribute year; }" +ilscript[0].content[3] "clear_state | guard { input category_scores | summary category_scores | attribute category_scores; }" +``` diff --git a/en/querying/vector-search-intro.html b/en/querying/vector-search-intro.html deleted file mode 100644 index ea3aac0f49..0000000000 --- a/en/querying/vector-search-intro.html +++ /dev/null @@ -1,255 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "An intro to vector search" -redirect_from: -- /en/vector-search.html ---- - -

    - Vector Search is a method to search objects using a digital representation of both the query and the objects - - easier explained by example: -

    -

    - Consider a user searching for the flower "Dandelion". - A regular text search will match this Wikipedia article, - as the "dandelion" term is in the text: -

    -

    - - Taraxacum -

    -


    -

    - From Wikipedia, the free encyclopedia (Redirected from Dandelion) -

    -
    - "Dandelion" redirects here. - It may refer to any species of the genus Taraxacum or specifically to Taraxacum officinale. - For similar plants, see False dandelion. - For other uses, see Dandelion (disambiguation) -
    -

    - Taraxacum (/təˈræksəkʊm/) is a large genus of flowering plants in the family Asteraceae, - which consists of species commonly known as dandelions. -

    -

    - ... -

    -
    -

    - This document is about dandelions, for sure. - However, if the user searches for "blowball" (the mature spherical seed head of a dandelion), - the text above will not match, although the document is a good match for "blowball". - Example images of a dandelion: -

    -DandelionFlower.jpg -DandelionFlower.jpg -

    - Photos by Greg Hume -

    -

    - This gets more complicated with more content types. - It is hard to use text search to match the images without any text. - The same goes for videos, podcasts, songs, TV shows, and so on - this is often called multi-modal search. - A workaround is to search the object’s textual metadata, like a song’s title, the podcast episode summary, - and the image’s alt text on the webpage. - Such metadata is only sometimes available and is often too short/imprecise - this does not solve the root problem. -

    -

    - The query can be an image itself or a song, where the users want more like this, - in the context of the current song or an image being viewed. - This is a problem found in recommender systems. -

    - - - -

    Digital representations

    -

    - A solution to the synonym or multi-modal problem is to change from matching in the textual to the digital domain. - This means transforming text, images, and songs into a set of numbers that indicate what it is about. -

    -

    - Examples of different objects, and their vector representation: -

    - - - - - - - - - - - - - - - - - - -
    DandelionFlower.jpg[0.560, 0.001, 0.223, ...]
    Dandelion bu Anna of the North[0.0, 0.011, 0.0, ...]
    - "Taraxacum is a large genus of flowering plants in the family Asteraceae, - which consists of species commonly known as dandelions."[0.002, 0.001, 0.411, ...]
    dandelions[0.002, 0.021, 0.355, ...]
    -

    - The digital representation of the object is hence a sequence of numbers, - called a vector, also called an embedding. -

    -

    - By converting all the objects searched for and the query to vectors (digital representations), - the search problem is changed into finding similar vectors - i.e., vector search. -

    - - - -

    How vectors are created

    -

    - Creating vectors from the objects can be done in multiple ways. - Machine learning is often used; there are many ready-made models to get you started. -

    -

    - The quality of the vectors will decide the quality of the search, - so this is where organizations will want to spend their effort improving search. -

    -

    - A vector has a dimension (length) and type (type of each cell). -

    -

    - The cost/quality tradeoff is essential - - given a vector, it can be represented with lower precision or shortened, keeping the most relevant dimensions. - This reduces search precision, but cuts costs into a fraction. - A 75% cost reduction might reduce precision, but acceptable for the use case. - Vespa supports four types, with an 8x difference in memory cost: -

    - - - - - -
    Int88 bits, 1 byte per dimension
    bfloat16  16 bits, 2 bytes per dimension
    float32 bits, 4 bytes per dimension
    double64 bits, 8 bytes per dimension
    -

    - Read more on selecting the optimal type: -

    - -

    - Vectors are often some hundred numbers long, like 768 or 384. - A longer vector can hold more information, but some dimensions are more information-rich than others. - If most of the values in a dimension are equal or very close to each other, - the dimension has little value and can be eliminated: -

    - -

    - A better approach is often to use an ML model with the optimal size from the start for the use case, once found. -

    - - - - -

    - "dandelion" matches "dandelion", but "rose" does not. - Text search has a binary nature. - However, both are flowers and considered more similar to each other than "airplane". - Unlike text matching, a vector search will not match items exactly. - In the following example, - we are using a very short 3-dim vector for simplicity and using the dimensions as coordinates in a 3D space: -

    -Nearest neighbor scatter plot -

    - We see that [0.000, 0.497, 0.110] - is not equal to [0.101, 0.560, 0.093], but quite close - - closer than [0.611, 0.000, 0.217]. -

    -

    - In other words, if the values for each dimension are close, the vectors are similar - - and this can be used in search to find the nearest neighbor or NN. - There are multiple ways to calculate vector similarity using a distance metric: -

    - - - - -

    Approximate for speed

    -

    - Calculating vector similarity can be thought of as multiplying the number of dimensions with the number of vectors. - If you are a Canadian, represented by a 768-dim vector, - this is 768 x 39,000,000 = 29,952,000,000, or 29 billion calculations to find the other Canadian most similar to you. -

    -

    - There are ways to approximate this by doing fewer calculations, - and still finding the closest vector with high probability - - this is called Approximate Nearest Neighbor search, or ANN search. - Vespa supports both exact nearest neighbor search and ANN search: -

    - -

    - Note that ANNs require some kind of indexing to speed up search, - so inserts (adding a new vector) are more expensive (uses more CPU) and takes more space (memory and disk). - When evaluating different kinds of ANN indexing, - consider if your use case requires updates, including deletes, to the vectors - Vespa supports all. -

    - - - - - - - diff --git a/en/querying/vector-search-intro.mdx b/en/querying/vector-search-intro.mdx new file mode 100644 index 0000000000..748b1e05ab --- /dev/null +++ b/en/querying/vector-search-intro.mdx @@ -0,0 +1,134 @@ +--- +title: "An intro to vector search" +--- + +Vector Search is a method to search objects using a digital representation of both the query and the objects - easier explained by example: + +Consider a user searching for the flower "Dandelion". A regular text search will match [this Wikipedia article](https://en.wikipedia.org/wiki/Taraxacum), as the "dandelion" term is in the text: + +***Taraxacum*** + +From Wikipedia, the free encyclopedia (Redirected from Dandelion) + +> "Dandelion" redirects here. It may refer to any species of the genus Taraxacum or specifically to Taraxacum officinale. For similar plants, see False dandelion. For other uses, see Dandelion (disambiguation) + +***Taraxacum*** (/təˈræksəkʊm/) is a large genus of flowering plants in the family Asteraceae, which consists of species commonly known as dandelions. + +... + + +This document is about dandelions, for sure. However, if the user searches for "blowball" (the mature spherical seed head of a dandelion), the text above will not match, although the document is a good match for "blowball". Example images of a dandelion: + + + +![](https://upload.wikimedia.org/wikipedia/commons/4/4f/DandelionFlower.jpg) + + +![](https://upload.wikimedia.org/wikipedia/commons/5/54/TaraxacumOfficinaleSeed.JPG) + + + + +This gets more complicated with more content types. It is hard to use text search to match the images without any text. The same goes for videos, podcasts, songs, TV shows, and so on - this is often called multi-modal search. A workaround is to search the object’s textual metadata, like a song’s title, the podcast episode summary, and the image’s *alt* text on the webpage. Such metadata is only sometimes available and is often too short/imprecise - this does not solve the root problem. + +The query can be an image itself or a song, where the users want *more like this*, in the context of the current song or an image being viewed. This is a problem found in recommender systems. + +## Digital representations + +A solution to the synonym or multi-modal problem is to change from matching in the textual to the digital domain. This means transforming text, images, and songs into a set of numbers that indicate what it is *about*. + +Examples of different objects, and their vector representation: + + + + + + + + + + + + + + + + + + + + + + +
    <Columns cols={3}><Frame>!DandelionFlower.jpg</Frame></Columns>[0.560, 0.001, 0.223, ...]
    <Columns cols={2}><Frame>!Dandelion bu Anna of the North</Frame></Columns>[0.0, 0.011, 0.0, ...]
    "Taraxacum is a large genus of flowering plants in the family Asteraceae, which consists of species commonly known as dandelions."[0.002, 0.001, 0.411, ...]
    dandelions[0.002, 0.021, 0.355, ...]
    + +The digital representation of the object is hence a sequence of numbers, called a *vector*, also called an *embedding*. + +By converting all the objects searched for *and* the query to vectors (digital representations), the search problem is changed into finding similar vectors - i.e., *vector search*. + +## How vectors are created + +Creating vectors from the objects can be done in multiple ways. Machine learning is often used; there are many ready-made models to get you started. + +The quality of the vectors will decide the quality of the search, so this is where organizations will want to spend their effort improving search. + +A vector has a *dimension* (length) and type (type of each cell). + +The cost/quality tradeoff is essential - given a vector, it can be represented with lower precision or shortened, keeping the most relevant dimensions. This reduces search precision, but cuts costs into a fraction. A 75% cost reduction might reduce precision, but acceptable for the use case. Vespa supports [four types](../reference/ranking/tensor), with an 8x difference in memory cost: + + + + + + + + + + + + + + + + + + + + + + +
    Int88 bits, 1 byte per dimension
    bfloat1616 bits, 2 bytes per dimension
    float32 bits, 4 bytes per dimension
    double64 bits, 8 bytes per dimension
    + +Read more on selecting the optimal type: + +- [billion-scale-knn](https://blog.vespa.ai/billion-scale-knn/) + +Vectors are often some hundred numbers long, like 768 or 384. A longer vector can hold more information, but some dimensions are more information-rich than others. If most of the values in a dimension are equal or very close to each other, the dimension has little value and can be eliminated: + +- [Dimension reduction using Principal Component Analysis (PCA)](https://blog.vespa.ai/building-billion-scale-vector-search-part-two/#dimension-reduction-using-principal-component-analysis-pca) + +A better approach is often to use an ML model with the optimal size from the start for the use case, once found. + +## Doing a vector search + +"dandelion" matches "dandelion", but "rose" does not. Text search has a binary nature. However, both are flowers and considered more similar to each other than "airplane". Unlike text matching, a vector search will not match items exactly. In the following example, we are using a very short 3-dim vector for simplicity and using the dimensions as coordinates in a 3D space: + + +![](/assets/img/3Dplot.png) + + +We see that [0.000, 0.497, 0.110] is not equal to [0.101, 0.560, 0.093], but quite close - closer than [0.611, 0.000, 0.217]. + +In other words, if the values for each dimension are close, the vectors are similar - and this can be used in search to find the *nearest neighbor* or NN. There are multiple ways to calculate vector similarity using a *distance metric*: + +- [distance-metric](../reference/schemas/schemas#distance-metric) + +## Approximate for speed + +Calculating vector similarity can be thought of as multiplying the number of dimensions with the number of vectors. If you are a Canadian, represented by a 768-dim vector, this is 768 x 39,000,000 = 29,952,000,000, or 29 billion calculations to find the other Canadian most similar to you. + +There are ways to approximate this by doing fewer calculations, and still finding the closest vector with high probability - this is called *Approximate Nearest Neighbor search*, or *ANN search*. Vespa supports both exact nearest neighbor search and ANN search: + +- [approximate-nn-hnsw](/en/querying/approximate-nn-hnsw) + +Note that ANNs require some kind of indexing to speed up search, so inserts (adding a new vector) are more expensive (uses more CPU) and takes more space (memory and disk). When evaluating different kinds of ANN indexing, consider if your use case requires updates, including deletes, to the vectors - Vespa supports all. diff --git a/en/rag/binarizing-vectors.md b/en/rag/binarizing-vectors.mdx similarity index 63% rename from en/rag/binarizing-vectors.md rename to en/rag/binarizing-vectors.mdx index a215e8d2d1..26c016c7eb 100644 --- a/en/rag/binarizing-vectors.md +++ b/en/rag/binarizing-vectors.mdx @@ -1,63 +1,70 @@ --- -# Copyright Vespa.ai. All rights reserved. title: "Binarizing Vectors" -redirect_from: - - /en/binarizing-vectors --- - - -Binarization in this context is mapping numbers in a vector (embedding) to bits (reducing the value range), -and representing the vector of bits efficiently using the `int8` data type. Examples: - -| input vector | binarized floats | pack_bits (to INT8) | -|--------------------------------------------------|------------------------------------------|---------------------:| -| [1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0] | [1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0] | -1 | -| [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0] | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0] | 0 | -| [-1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0] | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0] | 0 | -| [1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0] | [1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0] | -128 | -| [2.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0] | [1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0] | -127 | - -Binarization is key to reducing memory requirements and, therefore, cost. -Binarization can also improve feeding performance, as the memory bandwidth requirements go down accordingly. - -Refer to [embedding](../rag/embedding.html) for more details on how to create embeddings from text. - +Binarization in this context is mapping numbers in a vector (embedding) to bits (reducing the value range), and representing the vector of bits efficiently using the `int8` data type. Examples: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    input vectorbinarized floatspack_bits (to INT8)
    [1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0][1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0]-1
    [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0][0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]0
    [-1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0][0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]0
    [1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0][1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]-128
    [2.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0][1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0]-127
    + +Binarization is key to reducing memory requirements and, therefore, cost. Binarization can also improve feeding performance, as the memory bandwidth requirements go down accordingly. + +Refer to [embedding](/en/rag/embedding) for more details on how to create embeddings from text. ## Summary -This guide maps all the steps required to run a successful binarization project using Vespa only - -there is no need to re-feed data. -This makes a project feasible with limited incremental resource usage and man-hours required. -Approximate Nearest Neighbor vector operations are run using an HNSW index in Vespa, with online data structures. -The cluster is operational during the procedure, gradually building the required data structures. +This guide maps all the steps required to run a successful binarization project using Vespa only - there is no need to re-feed data. This makes a project feasible with limited incremental resource usage and man-hours required. + +Approximate Nearest Neighbor vector operations are run using an HNSW index in Vespa, with online data structures. The cluster is operational during the procedure, gradually building the required data structures. -This guide is useful to map the steps and tradeoffs made for a successful vector binarization. -Other relevant articles on how to reduce vector size in memory are: -* [Exploring the potential of OpenAI Matryoshka 🪆 embeddings with Vespa](https://blog.vespa.ai/matryoshka-embeddings-in-vespa/) -* [Matryoshka 🤝 Binary vectors: Slash vector search costs with Vespa](https://blog.vespa.ai/combining-matryoshka-with-binary-quantization-using-embedder/) +This guide is useful to map the steps and tradeoffs made for a successful vector binarization. Other relevant articles on how to reduce vector size in memory are: -Adding to this, using algorithms like SPANN can solve problems with huge vector data sizes, -read more in [Billion-scale vector search using hybrid HNSW-IF](https://blog.vespa.ai/vespa-hybrid-billion-scale-vector-search/). +- [Exploring the potential of OpenAI Matryoshka 🪆 embeddings with Vespa](https://blog.vespa.ai/matryoshka-embeddings-in-vespa/) +- [Matryoshka 🤝 Binary vectors: Slash vector search costs with Vespa](https://blog.vespa.ai/combining-matryoshka-with-binary-quantization-using-embedder/) -A binarization project normally involves iteration over different configuration settings, -measuring quality loss for each iteration - this procedure it built with that in mind. +Adding to this, using algorithms like SPANN can solve problems with huge vector data sizes, read more in [Billion-scale vector search using hybrid HNSW-IF](https://blog.vespa.ai/vespa-hybrid-billion-scale-vector-search/). +A binarization project normally involves iteration over different configuration settings, measuring quality loss for each iteration - this procedure it built with that in mind. ## Converters -Vespa’s built-in indexing language [converters](../reference/writing/indexing-language.html#converters) -`binarize` and `pack_bits` let you easily generate binarized vectors. -Example schema definitions used to generate the vectors in the table above: -``` +Vespa’s built-in indexing language [converters](/en/reference/writing/indexing-language#converters)`binarize` and `pack_bits` let you easily generate binarized vectors. Example schema definitions used to generate the vectors in the table above: + +```bash schema doc { document doc { @@ -76,50 +83,39 @@ schema doc { } ``` -We see that the `binarize` function itself will not compress vectors to a smaller size, -as the output cell type is the same as the input - it is only the values that are mapped to 0 or 1. -Above, the vectors are binarized using a threshold value of 0, the Vespa default - -any number > 0 will map to 1 - this threshold is configurable. +We see that the `binarize` function itself will not compress vectors to a smaller size, as the output cell type is the same as the input - it is only the values that are mapped to 0 or 1. Above, the vectors are binarized using a threshold value of 0, the Vespa default - any number \> 0 will map to 1 - this threshold is configurable. `pack_bits` reads binarized vectors and represents them using int8. In the example above: -* `tensor(x[8])` is 8 x sizeof(float) = 8 x 32 bits = 256 bits = 32 bytes -* `tensor(x[1])` is 1 x sizeof(int8) = 1 x 8 bits = 8 bits = 1 byte +- `tensor(x[8])` is 8 x sizeof(float) = 8 x 32 bits = 256 bits = 32 bytes +- `tensor(x[1])` is 1 x sizeof(int8) = 1 x 8 bits = 8 bits = 1 byte In other words, a compression factor of 32, which is expected, mapping a 32-bit float into 1 bit. -As memory usage often is the cost driver for applications, this has huge potential. -However, there is a loss of precision, so the tradeoff must be evaluated. -Read more in [billion-scale-knn](https://blog.vespa.ai/billion-scale-knn/) and -[combining-matryoshka-with-binary-quantization-using-embedder](https://blog.vespa.ai/combining-matryoshka-with-binary-quantization-using-embedder/). - +As memory usage often is the cost driver for applications, this has huge potential. However, there is a loss of precision, so the tradeoff must be evaluated. Read more in [billion-scale-knn](https://blog.vespa.ai/billion-scale-knn/) and [combining-matryoshka-with-binary-quantization-using-embedder](https://blog.vespa.ai/combining-matryoshka-with-binary-quantization-using-embedder/). ## Binarizing an existing embedding field -In the example above, we see that `doc_embedding` has the original embedding data, -and the fields `doc_embedding_binarized_floats` and `doc_embedding_binarized` are generated from `doc_embedding`. -This is configured through the `indexing: input …` statement, -and defining the generated fields outside the `document { … }` block. -{% include note.html content='The `doc_embedding_binarized_floats` field is just for illustration purposes, -as input to the `doc_embedding_binarized` field, which is the target binarized and packed field with low memory requirements. -From here, we will call this the binarized embedding.' %} +In the example above, we see that `doc_embedding` has the original embedding data, and the fields `doc_embedding_binarized_floats` and `doc_embedding_binarized` are generated from `doc_embedding`. This is configured through the `indexing: input …` statement, and defining the generated fields outside the `document { … }` block. + + + **Note:** The `doc_embedding_binarized_floats` field is just for illustration purposes, as input to the `doc_embedding_binarized` field, which is the target binarized and packed field with low memory requirements. From here, we will call this the binarized embedding. + -This is a common case for many applications - how to safely binarize and evaluate the binarized data for subsequent use. -The process can be broken down into: -* Pre-requisites. -* Define the new binarized embedding, normally as an addition to the original field. -* Deploy and re-index the data to populate the binarized embedding. -* Create new ranking profiles with the binarized embeddings. -* Evaluate the quality of the binarized embedding. -* Remove the original embedding field from memory to save cost. +This is a common case for many applications - how to safely binarize and evaluate the binarized data for subsequent use. The process can be broken down into: +- Pre-requisites. +- Define the new binarized embedding, normally as an addition to the original field. +- Deploy and re-index the data to populate the binarized embedding. +- Create new ranking profiles with the binarized embeddings. +- Evaluate the quality of the binarized embedding. +- Remove the original embedding field from memory to save cost. ## Pre-requisites -Adding a new field takes resources, on disk and in memory. -A new binarized embedding field is smaller - above, it is 1/32 of the original field. -Also note that embedding fields often have an index configured, like: -``` +Adding a new field takes resources, on disk and in memory. A new binarized embedding field is smaller - above, it is 1/32 of the original field. Also note that embedding fields often have an index configured, like: + +```bash field doc_embeddings type tensor(x[8]) { indexing: summary | attribute | index attribute { @@ -138,19 +134,20 @@ The index is used for approximate nearest neighbor (ANN) searches, and also cons Use the Vespa Cloud console to evaluate the size of original fields and size of indexes to make sure that there is room for the new embedding field, possibly with an index. -{% include note.html content='The size of an index is a function of the number of documents, regardless of tensor type. -In this context, this means that adding a new field with and index, -the new index will have the same size as the index of the existing embedding field.' %} + +**Note:** The size of an index is a function of the number of documents, regardless of tensor type. In this context, this means that adding a new field with and index, the new index will have the same size as the index of the existing embedding field. + Use status pages to find the index size in memory - example: -https://api-ctl.vespa-cloud.com/application/v4/tenant/\ -TENANT_NAME/application/APP_NAME/instance/INSTANCE_NAME/environment/prod/region/REGION/\ -service/searchnode/NODE_HOSTNAME/\ -state/v1/custom/component/documentdb/SCHEMA/subdb/ready/attribute/ATTRIBUTE_NAME +https://api-ctl.vespa-cloud.com/application/v4/tenant/ +TENANT\_NAME/application/APP\_NAME/instance/INSTANCE\_NAME/environment/prod/region/REGION/ +service/searchnode/NODE\_HOSTNAME/ +state/v1/custom/component/documentdb/SCHEMA/subdb/ready/attribute/ATTRIBUTE\_NAME ### Example -
    +
    +```json
     tensor: {
         compact_generation: 33946879,
         ref_vector: {
    @@ -165,7 +162,7 @@ tensor: {
             memory_usage: {
                 used: 205348904436,
                 dead: 10248636768,
    -            allocated: 206719921232,
    +            allocated:206719921232,
                 onHold: 0
             }
         },
    @@ -174,21 +171,18 @@ tensor: {
                 all: {
                     used: 10452397992,
                     dead: 360247164,
    -                allocated: 13346516304,
    +                allocated:13346516304,
                     onHold: 0
                 }
    -
    - -In this example, the index is 13G, the tensor data is 206G, so the index is 6.3% of the tensor data. -The original tensor is of type `bfloat16`, a binarized version is 1/16 of this and hence 13G. -As an extra index is 13G, the temporal incremental memory usage is approximately 26G during the procedure. - - +``` +In this example, the index is 13G, the tensor data is 206G, so the index is 6.3% of the tensor data. The original tensor is of type `bfloat16`, a binarized version is 1/16 of this and hence 13G. As an extra index is 13G, the temporal incremental memory usage is approximately 26G during the procedure. ## Define the binarized embedding field + The new field is _added_ to the schema, example schema, before: -``` + +```bash schema doc { document doc { @@ -200,7 +194,8 @@ schema doc { ``` After: -``` + +```bash schema doc { document doc { @@ -215,10 +210,9 @@ schema doc { } ``` -The above are simple examples, with no ANN settings on the fields. -Following is a more complex example - schema before: +The above are simple examples, with no ANN settings on the fields. Following is a more complex example - schema before: -``` +```bash schema doc { document doc { @@ -239,7 +233,8 @@ schema doc { ``` Schema after: -``` + +```bash schema doc { document doc { @@ -274,13 +269,13 @@ schema doc { Note that we replicate the index settings to the new field. - ## Deploy and reindex the binarized embedding field + Deploying the field will trigger a reindexing on Vespa Cloud to populate the binarized embedding, fully automated. -Self-hosted, the `deploy` operation will output the below - [trigger a reindex](../operations/reindexing.html). +Self-hosted, the `deploy` operation will output the below - [trigger a reindex](/en/operations/reindexing). -``` +```sh $ vespa deploy Uploading application package... done @@ -292,14 +287,12 @@ reindexing: Consider re-indexing document type 'doc' in cluster 'doc' because: ``` Depending on the size of the corpus and resources configured, the reindexing process takes time. - - ## Create new ranking profiles and queries using the binarized embeddings -After reindexing, you can query using the new, binarized embedding field. -Assuming a query using the doc_embedding field: -``` +After reindexing, you can query using the new, binarized embedding field. Assuming a query using the doc\_embedding field: + +```sh $ vespa query \ 'yql=select * from doc where {totalTargetHits:5}nearestNeighbor(doc_embedding, q)' \ 'input.query(q)=[1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0]' \ @@ -308,50 +301,69 @@ $ vespa query \ The same query, with a binarized query vector, to the binarized field: -``` +```sh $ vespa query \ 'yql=select * from doc where {totalTargetHits:5}nearestNeighbor(doc_embedding_binarized, q_bin)' \ 'input.query(q_bin)=[-119]' \ 'ranking=app_ranking_bin' ``` -See [tensor-hex-dump](../reference/schemas/document-json-format.html#tensor-hex-dump) -for more information about how to create the int8-typed tensor. - +See [tensor-hex-dump](/en/reference/schemas/document-json-format#tensor-hex-dump) for more information about how to create the int8-typed tensor. ### Quick Hamming distance intro -Example embeddings: -| document embedding | binarized floats | pack_bits (to INT8) | -|--------------------------------------------------|--------------------------------------------|--------------------:| -| [-1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0] | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0] | 0 | -| **query embedding** | **binarized floats** | **to INT8** | -| [1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0] | [1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0] | -119 | +Example embeddings: + + + + + + + + + + + + + + + + + + + + + + + + + +
    document embeddingbinarized floatspack_bits (to INT8)
    [-1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0][0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]0
    **query embedding****binarized floats****to INT8**
    [1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0][1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0]-119
    + +Use [matchfeatures](/en/reference/schemas/schemas#match-features) to debug ranking (see ranking profile `app_ranking_bin` below): -Use [matchfeatures](../reference/schemas/schemas.html#match-features) -to debug ranking (see ranking profile `app_ranking_bin` below): ```json "matchfeatures": { "attribute(doc_embedding_binarized)": { "type": "tensor(x[1])", - "values": [ 0 ] + "values": [0] }, "distance(field,doc_embedding_binarized)": 3.0, "query(q_bin)": { "type": "tensor(x[1])", - "values": [ -119 ] + "values": [-119] } } ``` See distance calculated to 3.0, which is the number of bits different in the binarized vectors, which is the hamming distance. - ## Rank profiles and queries + Assuming a rank profile like: -``` +```bash rank-profile app_ranking { match-features { distance(field, doc_embedding) @@ -368,7 +380,8 @@ rank-profile app_ranking { ``` Query: -``` + +```sh $ vespa query \ 'yql=select * from doc where {totalTargetHits:5}nearestNeighbor(doc_embedding, q)' \ 'input.query(q)=[2.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0]' \ @@ -377,7 +390,7 @@ $ vespa query \ A binarized version is like: -``` +```bash rank-profile app_ranking_bin { match-features { distance(field, doc_embedding_binarized) @@ -395,7 +408,7 @@ rank-profile app_ranking_bin { Query: -``` +```bash $ vespa query \ 'yql=select * from doc where {totalTargetHits:5}nearestNeighbor(doc_embedding_binarized, q_bin)' \ 'input.query(q_bin)=[-119]' \ @@ -404,7 +417,7 @@ $ vespa query \ Query with full-precision query vector, against a binarized vector - rank profile: -``` +```bash rank-profile app_ranking_bin_full { match-features { distance(field, doc_embedding_binarized) @@ -419,7 +432,7 @@ rank-profile app_ranking_bin_full { expression: sum(query(q) * unpack_to_float) } inputs { - query(q) tensor(x[8]) + query(q) tensor(x[8]) query(q_bin) tensor(x[1]) } first-phase { @@ -432,13 +445,14 @@ rank-profile app_ranking_bin_full { ``` Notes: -* The first-phase ranking is as the binarized query above. -* The second-phase ranking is using the full-precision query vector query(q) - with a bit-precision vector cast to float for type match. -* Both query vectors must be supplied in the query. + +- The first-phase ranking is as the binarized query above. +- The second-phase ranking is using the full-precision query vector query(q) with a bit-precision vector cast to float for type match. +- Both query vectors must be supplied in the query. Note the differences when using full values in the query tensor, see the relevance score for the results: -``` + +```sh $ vespa query \ 'yql=select * from music where {totalTargetHits:5}nearestNeighbor(doc_embedding_binarized, q_bin)' \ 'input.query(q)=[1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0]' \ @@ -450,7 +464,7 @@ $ vespa query \ "relevance": 3.0 ``` -``` +```sh $ vespa query \ 'yql=select * from music where {totalTargetHits:5}nearestNeighbor(doc_embedding_binarized, q_bin)' \ 'input.query(q)=[2.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0]' \ @@ -460,43 +474,40 @@ $ vespa query \ "relevance": 4.0 ``` -Read the [closeness](../reference/ranking/rank-features.html#closeness(dimension,name)) reference documentation. - +Read the [closeness](/en/reference/ranking/rank-features#closeness(dimension,name)) reference documentation. ### TargetHits for ANN -Given the lower precision with binarization, it might be a good idea to increase the `{targetHits:5}` annotation in the query, -to generate more candidates for later ranking phases. +Given the lower precision with binarization, it might be a good idea to increase the `{targetHits:5}` annotation in the query, to generate more candidates for later ranking phases. ## Evaluate the quality of the binarized embeddings -This exercise is about evaluating a lower-precision retrieval phase, -using the original full-sized (here we use floats) query-result pairs as reference. -Experiments, query-document precision: + +This exercise is about evaluating a lower-precision retrieval phase, using the original full-sized (here we use floats) query-result pairs as reference. Experiments, query-document precision: + 1. float-float 2. binarized-binarized 3. float-binarized 4. float-float, with binarized retrieval To evaluate the precision, compute the differences for each query @10, like: + ```python def compute_list_differences(list1, list2): set1 = set(list1) set2 = set(list2) return len(set1 - set2) - list1 = [1, 3, 5, 7, 9, 11, 13, 15, 17, 20] list2 = [2, 3, 5, 7, 9, 11, 14, 15, 18, 20] num_hits = compute_list_differences(list1, list2) print(f"Hits different: {num_hits}") ``` - ## Remove the original embedding field from memory -The purpose of the binarization is reducing memory footprint. -Given the results of the evaluation above, store the full-precision embeddings on disk or remove them altogether. -Example with paging the attribute to disk-only: -``` + +The purpose of the binarization is reducing memory footprint. Given the results of the evaluation above, store the full-precision embeddings on disk or remove them altogether. Example with paging the attribute to disk-only: + +```bash schema doc { document doc { @@ -523,7 +534,7 @@ schema doc { This example only indexes the binarized embedding, with data binarized before indexing: -``` +```bash schema doc { document doc { @@ -543,14 +554,12 @@ schema doc { } ``` - - ## Appendix: Binarizing from text input -To generate the embedding from other data types, like text, -use the [converters](../reference/writing/indexing-language.html#converters) - example: -``` - field doc_embedding type tensor(x[1]) { +To generate the embedding from other data types, like text, use the [converters](/en/reference/writing/indexing-language#converters) - example: + +```bash +field doc_embedding type tensor(x[1]) { indexing: (input title || "") . " " . (input content || "") | embed | attribute attribute { distance-metric: hamming @@ -560,44 +569,38 @@ use the [converters](../reference/writing/indexing-language.html#converters) - e Find examples in [Matryoshka 🤝 Binary vectors: Slash vector search costs with Vespa](https://blog.vespa.ai/combining-matryoshka-with-binary-quantization-using-embedder/). - ## Appendix: conversion to int8 + Find examples of how to binarize values in code: ```python import numpy as np - def floats_to_bits(floats): if len(floats) != 8: raise ValueError("Input must be a list of 8 floats.") bits = [1 if f > 0 else 0 for f in floats] return bits - def bits_to_int8(bits): bit_string = ''.join(str(bit) for bit in bits) int_value = int(bit_string, 2) int8_value = np.int8(int_value) return int8_value - def floats_to_int8(floats): bits = floats_to_bits(floats) int8_value = bits_to_int8(bits) return int8_value - floats = [0.5, -1.2, 3.4, 0.0, -0.5, 2.3, -4.5, 1.2] int8_value = floats_to_int8(floats) print(f"The int8 value is: {int8_value}") ``` - ```python import numpy as np - def binarize_tensor(tensor: torch.Tensor) -> str: """ Binarize a floating-point 1-d tensor by thresholding at zero @@ -610,16 +613,13 @@ def binarize_tensor(tensor: torch.Tensor) -> str: ) ``` +Multivector example, from [ColPali: Efficient Document Retrieval with Vision Language Models](https://vespa-engine.github.io/pyvespa/examples/colpali-document-retrieval-vision-language-models-cloud.html): -Multivector example, from -[ColPali: Efficient Document Retrieval with Vision Language Models](https://vespa-engine.github.io/pyvespa/examples/colpali-document-retrieval-vision-language-models-cloud.html): - -```python +```python expandable import numpy as np from typing import Dict, List from binascii import hexlify - def binarize_token_vectors_hex(vectors: List[torch.Tensor]) -> Dict[str, str]: vespa_tensor = list() for page_id in range(0, len(vectors)): @@ -633,7 +633,7 @@ def binarize_token_vectors_hex(vectors: List[torch.Tensor]) -> Dict[str, str]: ) if ( values == "00000000000000000000000000000000" - ): # skip empty vectors due to padding of batch + ): # skip empty vectors due to padding of batch continue vespa_tensor_cell = { "address": {"page": page_id, "patch": patch_index}, @@ -642,4 +642,4 @@ def binarize_token_vectors_hex(vectors: List[torch.Tensor]) -> Dict[str, str]: vespa_tensor.append(vespa_tensor_cell) return vespa_tensor -``` +``` \ No newline at end of file diff --git a/en/rag/document-enrichment.md b/en/rag/document-enrichment.mdx similarity index 84% rename from en/rag/document-enrichment.md rename to en/rag/document-enrichment.mdx index 4141fc24fa..d5bf4c8047 100644 --- a/en/rag/document-enrichment.md +++ b/en/rag/document-enrichment.mdx @@ -1,12 +1,8 @@ --- -# Copyright Vespa.ai. All rights reserved. title: "Document enrichment with LLMs" -redirect_from: - - /en/llms-document-enrichment --- -Document enrichment enables automatic generation of document field values using large language models (LLMs) or custom code during feeding. -It can be used to transform raw text into a more structured representation or expand it with additional contextual information. +Document enrichment enables automatic generation of document field values using large language models (LLMs) or custom code during feeding. It can be used to transform raw text into a more structured representation or expand it with additional contextual information. Examples of enrichment tasks include: - Extraction of named entities (e.g., names of people, organizations, locations, and products) for fuzzy matching and customized ranking @@ -26,10 +22,10 @@ This section provides guidelines for configuring document enrichment, using the ### Defining generated fields -Enrichments are defined in a schema using a [generate indexing expression](../reference/writing/indexing-language.html#generate). -For example the following schema defines two [synthetic fields](../operations/reindexing.html) with `generate`: +Enrichments are defined in a schema using a [generate indexing expression](/en/reference/writing/indexing-language#generate). +For example the following schema defines two [synthetic fields](/en/operations/reindexing) with `generate`: -``` +```yaml schema passage { document passage { field id type string { @@ -89,7 +85,7 @@ Example of a document generated with this schema: A schema can contain multiple generated fields that use one or multiple field generators. All used field generators should be configured in `services.xml`, e.g. -```xml +```xml expandable ... @@ -129,7 +125,7 @@ When both are provided, `` has precedence. A prompt template must contain `{input}` placeholder, which will be replaced with the input value. It is possible to combine several fields into one input by concatenating them into a single string, e.g. -``` +```yaml input "title: " . title . " text: " . text | generate names_extractor | summary | index ``` @@ -162,8 +158,8 @@ Overview of all the field generator parameters is available in the Field generators specify `` to reference a language model client to be used for generation, which is either a local LLM, an OpenAI client or a custom component. -Configuration details for local LLM and OpenAI client are covered in [local LLM](local-llms.html) -and [OpenAI client](external-llms.html) documentation. +Configuration details for local LLM and OpenAI client are covered in [local LLM](/en/rag/local-llms) +and [OpenAI client](/en/rag/external-llms) documentation. This section focuses on configuration parameters that are important for document enrichment. Both local LLM and OpenAI client can be configured with different models. @@ -184,7 +180,7 @@ Here is an example of a OpenAI client configured with GPT-4o mini model: ... ... - + {/* OpenAI client */} openai-key @@ -203,46 +199,46 @@ In addition to the model, local LLM client has several other parameters that are important for performance of document enrichment. The following configuration is a good starting point: -```xml +```xml expandable ... ... - + {/* For Vespa Cloud, specify model by model-id to speed-up deployment */} - - + {/* For self-hosted Vespa and Vespa Cloud, specify model by URL */} + {/* */} - + {/* Number of tokens a LLM can do inference with. + This includes prompt and completion tokens for all parallel request. */} 5000 - + {/* Requests are processed in parallel using continuous batching. + Each request is allocated 5000 / 5 = 1000 context tokens. */} 5 - + {/* Request context size split between prompt and completion tokens: 500 + 500 = 1000 */} 500 500 - + {/* A waiting line for requests to start processing. + It is reasonable to set it to <= parallelRequests */} 3 - + {/* How long a request can wait until added to the queue, otherwise timeout. + On average, Ca. = number of milliseconds it takes to process all parallel requests. */} 60000 - + {/* How long a request can wait in the queue until starting processing + In the worst case, ca. = number of milliseconds it takes to process all requests in the queue. */} 60000 - + In this case, we want the request to fail so we know if some configuration is wrong. */} FAIL @@ -255,31 +251,22 @@ The following configuration is a good starting point: There are three important aspects of this configuration in addition to the model used. 1. `model`, `contextSize` and `parallelRequests` determine compute resources necessary to run the model. -2. `contextSize`, `parallelRequests`, `maxPromptTokens` and `maxTokens` - should be configured to avoid context overflow - a situation when context size - is too small to process multiple parallel requests with the given number of prompt and completion tokens. -3. `maxQueueSize`, `maxEnqueueWait` and `maxQueueWait` are related to managing the queue - used for storing and feeding parallel requests into LLM runtime (llama.cpp). +2. `contextSize`, `parallelRequests`, `maxPromptTokens` and `maxTokens` should be configured to avoid context overflow - a situation when context size is too small to process multiple parallel requests with the given number of prompt and completion tokens. +3. `maxQueueSize`, `maxEnqueueWait` and `maxQueueWait` are related to managing the queue used for storing and feeding parallel requests into LLM runtime (llama.cpp). -[Local LLMs documentation](local-llms.html) explains how to configure +[Local LLMs documentation](/en/rag/local-llms) explains how to configure `model`, `contextSize` and `parallelRequests` with respect to the model and compute resources used. Memory usage (RAM or GPU VRAM) is especially important to considered when configuring these parameters. To avoid context overflow, configure `contextSize`, `parallelRequests`, `maxPromptTokens` and `maxTokens` -parameters so that `contextSize / parallelRequests >= maxPromptTokens + maxTokens`. -Also consider that larger `contextSize` takes longer to process. +parameters so that `contextSize / parallelRequests >= maxPromptTokens + maxTokens`. Also consider that larger `contextSize` takes longer to process. -The queue related parameters are used to balance latency with throughput. -Values for these parameters heavily depends on underlying compute resources. -Local LLM configuration presented above is optimized for CPU nodes with 16 cores and 32GB RAM -as well as GPU nodes with NVIDIA T4 GPUs 16GB VRAM. +The queue related parameters are used to balance latency with throughput. Values for these parameters heavily depends on underlying compute resources. Local LLM configuration presented above is optimized for CPU nodes with 16 cores and 32GB RAM as well as GPU nodes with NVIDIA T4 GPUs 16GB VRAM. ### Configuring compute resources -Provisioned compute resources only affect local LLM performance, -as OpenAI client merely calls a remote API that leverages the service provider's infrastructure. -In practice, GPU is highly recommended for running local LLMs. -It provided order of magnitude speedup compared to CPU. +Provisioned compute resources only affect local LLM performance, as OpenAI client merely calls a remote API that leverages the service provider's infrastructure. +In practice, GPU is highly recommended for running local LLMs. It provided order of magnitude speedup compared to CPU. For Vespa Cloud, a reasonable starting configuration is as follows: ```xml @@ -305,8 +292,7 @@ combined throughput will be close to 12 generations/second. ### Feeding configuration -Generated fields introduce considerable latency during feeding. -Large number of high-latency parallel requests might lead to timeouts in the document processing pipeline. +Generated fields introduce considerable latency during feeding. Large number of high-latency parallel requests might lead to timeouts in the document processing pipeline. To avoid this, it is recommended to reduce the number of connections during feeding. A reasonable starting point is to use three connections per GPU node and one connection per CPU node. Example for one GPU node: @@ -318,9 +304,7 @@ vespa feed data.json --connections 3 ## Structured output Document enrichment generates field values based on the data types defined in a document schema. -Both local LLMs and the OpenAI client support structured output, forcing LLMs to produce JSON that conforms to a specified schema. -This JSON schema is automatically constructed by a field generator according to the data type of the field being created. -For example, a JSON schema for `field questions type array` in document `passage` will be as follows: +Both local LLMs and the OpenAI client support structured output, forcing LLMs to produce JSON that conforms to a specified schema. This JSON schema is automatically constructed by a field generator according to the data type of the field being created. For example, a JSON schema for `field questions type array` in document `passage` will be as follows: ```json { @@ -341,7 +325,7 @@ For example, a JSON schema for `field questions type array` in document ``` Constructed schemas for different data types correspond to the -[document JSON format](../reference/schemas/document-json-format.html#) used for feeding. +[document JSON format](/en/reference/schemas/document-json-format) used for feeding. The following field types are supported: - string - bool @@ -364,7 +348,7 @@ Types that are not supported: ## Custom field generator -As usual with Vespa, existing functionality can be extended by developing [custom application components](../applications/developer-guide.html). +As usual with Vespa, existing functionality can be extended by developing [custom application components](/en/applications/developer-guide). A custom generator component can be used to implement application-specific logic to construct prompts, transform and validate LLM inputs and output, combine outputs of several LLMs or use other sources such a knowledge graph. @@ -372,7 +356,7 @@ A custom field generator compatible with `generate` should implement `com.yahoo. interface with `generate` method that returns a field value. Here is toy example of a custom field generator: -```java +```java expandable package ai.vespa.test; import ai.vespa.llm.completion.Prompt; @@ -406,7 +390,7 @@ public class MockFieldGenerator implements FieldGenerator { ``` The config definition for this component looks as follows: -``` +```yaml namespace=ai.vespa.test package=ai.vespa.test @@ -414,7 +398,7 @@ repetitions int default=1 ``` To be used with `generate` indexing expression this component should be added to `services.xml`: -```xml +```xml expandable ... @@ -427,7 +411,7 @@ To be used with `generate` indexing expression this component should be added to ``` The last step is to use it in a document schema, e.g.: -``` +```yaml schema passage { document passage { field text type string { diff --git a/en/rag/embedding.html b/en/rag/embedding.html deleted file mode 100644 index 1d2c51a24a..0000000000 --- a/en/rag/embedding.html +++ /dev/null @@ -1,1033 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Embedding" -redirect_from: -- /en/embedding.html ---- - -

    - A common technique is to map unstructured data - say, text or images - - to points in an abstract vector space and then do the computation in that space. - For example, retrieve - similar data by finding nearby points in the vector space, - or using the vectors as input to a neural net. - This mapping is referred to as embedding. - Read more about embedding and embedding management in this - blog post. -

    - -

    Embedding vectors can be sent to Vespa in queries and writes:

    -document- and query-embeddings - -

    Alternatively, you can use the embed function to generate the embeddings inside Vespa -to reduce vector transfer costs and make clients simpler: -

    -Vespa's embedding feature, creating embeddings from text - -

    - Adding embeddings to schemas will change the characteristics of an application; - Memory usage will grow, and feeding latency might increase. - Read more on how to address this in binarizing vectors. -

    - -

    Configuring embedders

    - -

    Embedders are components which must be configured in your -services.xml. Components are shared and can be used across schemas.

    - -
    {% highlight xml %}
    -
    -    
    -        
    -        
    -        
    -            query:
    -            passage:
    -        
    -    
    -    ...
    -
    -{% endhighlight %}
    - -

    You can write your own, - or use embedders provided in Vespa.

    - -

    - If you have multiple container clusters that are using the same embedder, - consider using an include statement - to avoid duplicating component config. -

    - - -

    Embedding a query text

    - -

    Where you would otherwise supply a tensor in a query request, -you can (with an embedder configured) instead supply any text enclosed in embed():

    - -
    -input.query(q)=embed(myEmbedderId, "Hello%20world")
    -
    - -

    Both single and double quotes are permitted, and if you have only configured a single embedder, -you can skip the embedder id argument and the quotes.

    - -

    The text argument can be supplied by a referenced parameter instead, using the @parameter syntax:

    -
    {% highlight json %}
    -{
    -    "yql": "select * from doc where {totalTargetHits:10}nearestNeighbor(embedding_field, query_embedding)",
    -    "text": "my text to embed",
    -    "input.query(query_embedding)": "embed(@text)",
    -}
    -{% endhighlight %}
    - -

    Remember that regardless of whether you are using embedders, input tensors -must always be defined in the schema's rank-profile.

    - - -

    Embedding a document field

    - -

    Use the embed function -of the indexing language -to convert strings into embeddings: - -

    -schema doc {
    -
    -    document doc {
    -
    -        field title type string {
    -            indexing: summary | index
    -        }
    -
    -    }
    -
    -    field embeddings type tensor<bfloat16>(x[384]) {
    -        indexing {
    -            input title | embed embedderId | attribute | index
    -        }
    -    }
    -
    -}
    -
    - -

    Notice that the embedding field is defined outside the document clause in the schema. -If you have only configured a single embedder, you can skip the embedder id argument.

    - -

    The input field can also be an array, where the output becomes a rank two tensor, see -this blog post:

    - -
    -schema doc {
    -
    -    document doc {
    -
    -        field chunks type array<string> {
    -            indexing: index | summary
    -        }
    -
    -    }
    -
    -    field embeddings type tensor<bfloat16>(p{},x[5]) {
    -        indexing: input chunks | embed embedderId | attribute | index
    -    }
    -
    -}
    -
    - - - -

    Provided embedders

    -

    Vespa provides several embedders as part of the platform.

    - - -

    Huggingface Embedder

    - -

    An embedder using any Huggingface tokenizer, -including multilingual tokenizers, -to produce tokens which are then input to a supplied transformer model in ONNX model format:

    - -
    {% highlight xml %}
    -
    -    
    -        
    -        
    -    
    -    ...
    -
    -{% endhighlight %}
    - -

    The huggingface-embedder supports all -Huggingface tokenizer implementations.

    - - - -

    Use path to supply the model files from the application package, -url to supply them from a remote server, or -model-id to use a -model supplied by Vespa Cloud. -You can also use a model hosted in private Huggingface Model Hub by adding your Huggingface API token -to the secret store and referring to the secret -using secret-ref in the model tag. -See model config reference for more details. -

    -
    {% highlight xml %}
    -
    -    
    -        
    -        
    -    
    -    ...
    -{% endhighlight %}
    - -

    See the reference -for all configuration parameters.

    - -

    Huggingface embedder models

    -

    - The following are examples of text embedding models that can be used with the hugging-face-embedder - and their output tensor dimensionality. - The resulting tensor type can be float, - bfloat16 or using binarized quantization into int8. - See blog post Combining matryoshka with binary-quantization - for more examples of using the Huggingface embedder with binary quantization. -

    - -

    The following models use pooling-strategy mean, - which is the default pooling-strategy:

    - -

    - The following models are useful for binarization and Matryoshka dimensionality flexibility where only the first k - dimensions are retained. - - Matryoshka 🤝 Binary vectors: Slash vector search costs with Vespa is a great read on this subject. - When enabling binarization with int8 use distance-metric hamming: -

    -
      -
    • mxbai-embed-large-v1 produces tensor<float>(x[1024]). This model - is also useful for binarization, which can be triggered by using destination tensor<int8>(x[128]). - Use pooling-strategy cls and normalize true.
    • -
    • nomic-embed-text-v1.5 produces tensor<float>(x[768]). This model - is also useful for binarization, which can be triggered by using destination tensor<int8>(x[96]). Use normalize true.
    • -
    - -

    Snowflake arctic model series:

    - - -

    All of these example text embedding models can be used in combination with Vespa's -nearest neighbor search -using the appropriate distance-metric. -Notice that to use the distance-metric: prenormalized-angular, -the normalize configuration must be set to true.

    - -

    Check the Massive Text Embedding Benchmark (MTEB) benchmark and -MTEB leaderboard -for help with choosing an embedding model.

    - - -

    Bert embedder

    - -

    - DEPRECATED; prefer using the Huggingface -Embedder instead of the Bert embedder. -

    - -

    An embedder using the WordPiece embedder to produce tokens -which are then input to a supplied ONNX model on the form expected by a BERT base model:

    - -
    {% highlight xml %}
    -
    -    
    -        
    -        
    -        128
    -        last_hidden_state
    -    
    -
    -{% endhighlight %}
    -
      -
    • - The transformer-model specifies the embedding model in ONNX format. - See exporting models to ONNX, - for how to export embedding models from Huggingface to compatible ONNX format. -
    • -
    • - The tokenizer-vocab specifies the Huggingface vocab.txt file, with one valid token per line. - Note that the Bert embedder does not support the tokenizer.json formatted tokenizer configuration files. - This means that tokenization settings like max tokens should be set explicitly. -
    • -
    • - The transformer-output specifies the name given - to to embedding output in the model.onnx file; - this will differ depending on how the model is exported to - ONNX format. One common name is last_hidden_state, - especially in transformer-based models. Other common names are - output or - output_0, - embedding or - embeddings, - sentence_embedding, - pooled_output, - or - encoder_last_hidden_state. - The default is output_0. -
    • -
    - -

    The Bert embedder is limited to English (WordPiece) and -BERT-styled transformer models with three model inputs -(input_ids, attention_mask, token_type_ids). -Prefer using the Huggingface Embedder instead of the Bert embedder.

    - -

    See configuration reference for all configuration options.

    - - -

    ColBERT embedder

    - -

    An embedder supporting ColBERT models. The -ColBERT embedder maps text to token embeddings, representing a text as multiple -contextualized embeddings. This produces better quality than reducing all tokens into a single vector.

    - -

    Read more about ColBERT and the ColBERT embedder in blog post form - Announcing the Vespa ColBERT embedder - and Announcing Vespa Long-Context ColBERT.

    - -
    {% highlight xml %}
    -
    -    
    -        
    -        
    -        32
    -        128
    -    
    -
    -{% endhighlight %}
    - -
      -
    • - The transformer-model specifies the ColBERT embedding model in ONNX format. - See exporting models to ONNX - for how to export embedding models from Huggingface to compatible ONNX format. - The vespa-engine/col-minilm page on the HF - model hub has a detailed example of how to export a colbert checkpoint to ONNX format for accelerated inference. -
    • -
    • - The tokenizer-model specifies the Huggingface tokenizer.json formatted file. - See HF loading tokenizer from a JSON file. -
    • -
    • - The max-query-tokens controls the maximum number of query text tokens that are represented as vectors, - and similarly, max-document-tokens controls the document side. These parameters - can be used to control resource usage. -
    • -
    -

    See configuration reference for all -configuration options and defaults.

    - -

    The ColBERT token embeddings are represented as a -mixed tensor: tensor<float>(token{}, x[dim]) where -dim is the vector dimensionality of the contextualized token embeddings. -The colbert model checkpoint on Hugging Face hub -uses 128 dimensions.

    - -

    The embedder destination tensor is defined in the schema, and -depending on the target tensor cell precision definition -the embedder can compress the representation: - - If the target tensor cell type is int8, the ColBERT embedder compresses the token embeddings with binarization for - the document to reduce storage to 1-bit per value, reducing the token embedding storage footprint - by 32x compared to using float. The query representation is not compressed with binarization. - The following demonstrates two ways to use the ColBERT embedder in - the document schema to embed a document field. -

    - -
    -schema doc {
    -    document doc {
    -        field text type string {..}
    -    }
    -    field colbert_tokens type tensor<float>(token{}, x[128]) {
    -        indexing: input text | embed colbert | attribute
    -    }
    -    field colbert_tokens_compressed type tensor<int8>(token{}, x[16]) {
    -        indexing: input text | embed colbert | attribute
    -    }
    -}
    -
    - -

    The first field colbert_tokens stores the original representation as the tensor destination -cell type is float. The second field, the colbert_tokens_compressed tensor is compressed. -When using int8 tensor cell precision, -one should divide the original vector size by 8 (128/8 = 16).

    - -

    You can also use bfloat16 instead of float to reduce storage by 2x compared to float.

    -
    -field colbert_tokens type tensor<bfloat16>(token{}, x[128]) {
    -    indexing: input text | embed colbert | attribute
    -}
    -
    - -

    You can also use the ColBERT embedder with an array of strings (representing chunks):

    -
    -schema doc {
    -    document doc {
    -        field chunks type array<string> {..}
    -    }
    -    field colbert_tokens_compressed type tensor<int8>(chunk{}, token{}, x[16]) {
    -        indexing: input text | embed colbert chunk | attribute
    -    }
    -}
    -
    - -

    Here, we need a second mapped dimension in the target tensor and a second argument to embed, -telling the ColBERT embedder the name of the tensor dimension to use for the chunks.

    - -

    Notice that the examples above did not specify the index function for creating a -HNSW index. -The colbert representation is intended to be used as a ranking model -and not for retrieval with Vespa's nearestNeighbor query operator, -where you can e.g., use a document-level vector and/or lexical matching.

    - -

    To reduce memory footprint, use paged attributes.

    - -

    ColBERT ranking

    -

    -See the sample applications for using ColBERT in ranking with variants of the MaxSim similarity operator -expressed using Vespa tensor computation expressions. See: -colbert and - colbert-long. -

    - - -

    SPLADE embedder

    - -

    An embedder supporting SPLADE models. The -SPLADE embedder maps text to mapped tensor, representing a text as a sparse vector of unique tokens and their weights.

    - -
    {% highlight xml %}
    -
    -    
    -        
    -        
    -    
    -
    -{% endhighlight %}
    - - -

    See configuration reference for all -configuration options and defaults.

    - -

    - The splade token weights are represented as a - mapped tensor: tensor<float>(token{}). -

    -

    - The embedder destination tensor is defined in the schema. - The following demonstrates how to use the SPLADE embedder in the document schema to - embed a document field. -

    - -
    -schema doc {
    -    document doc {
    -        field text type string {..}
    -    }
    -    field splade_tokens type tensor<float>(token{}) {
    -        indexing: input text | embed splade | attribute
    -    }
    -}
    -
    - -

    You can also use the SPLADE embedder with an array of strings (representing chunks). Here, also - using lower tensor cell precision bfloat16:

    -
    -schema doc {
    -    document doc {
    -        field chunks type array<string> {..}
    -    }
    -    field splade_tokens type tensor<bfloat16>(chunk{}, token{}) {
    -        indexing: input text | embed splade chunk | attribute
    -    }
    -}
    -
    - -

    Here, we need a second mapped dimension in the target tensor and a second argument to embed, -telling the splade embedder the name of the tensor dimension to use for the chunks.

    - -

    To reduce memory footprint, use paged attributes.

    - -

    SPLADE ranking

    -

    - See the splade sample application for how to use SPLADE in ranking, - including also how to use the SPLADE embedder with an array of strings (representing chunks). -

    - -

    SPLADE retrieval

    -

    - Sparse retrieval in Vespa - is a good discussion on how SPLADE can be used for retrieval. -

    - - -{% include chip-cloud.html %} -

    VoyageAI Embedder

    - -

    An embedder that uses the VoyageAI embedding API -to generate high-quality embeddings for semantic search. This embedder calls the VoyageAI API service -and does not require local model files or ONNX inference. All embeddings returned by VoyageAI are normalized -to unit length, making them suitable for cosine similarity and -prenormalized-angular distance metrics -(see VoyageAI FAQ).

    - -
    {% highlight xml %}
    -
    -    
    -        voyage-4
    -        voyage_api_key
    -        1024
    -    
    -
    -{% endhighlight %}
    - -
      -
    • - The model specifies which VoyageAI model to use. -
    • -
    • - The api-key-secret-ref references a secret in Vespa's - secret store containing your VoyageAI API key. - This is required for authentication. -
    • -
    - -

    See the reference -for all configuration parameters.

    - -

    VoyageAI embedder models

    -

    For the complete list of available models and their specifications, see:

    - - -

    Contextualized chunk embeddings

    -

    To use contextualized chunk embeddings, -configure the VoyageAI embedder with a voyage-context-* model and use it to embed an -array<string> field containing your document chunks:

    - -
    -schema doc {
    -    document doc {
    -        field chunks type array<string> {
    -            indexing: index | summary
    -        }
    -    }
    -    field embeddings type tensor<float>(chunk{}, x[1024]) {
    -        indexing: input chunks | embed voyage | attribute | index
    -        attribute {
    -            distance-metric: prenormalized-angular
    -        }
    -    }
    -}
    -
    - -

    - When embedding array fields with a contextualized chunk embedding model, Vespa sends all chunks from a document in a single API request, - allowing Voyage to encode each chunk with context from the other chunks. - Be aware that the combined size of all chunks in a document must fit within the VoyageAI API's input token limit. - See Working with chunks for chunking strategies. -

    - -

    Input type detection

    -

    VoyageAI models distinguish between query and document embeddings for improved retrieval quality. -The embedder automatically detects the context and sets the appropriate input type based on whether -the embedding is performed during feed (indexing) or query processing in Vespa.

    - -

    For advanced use cases where you need to control the input type programmatically, -you can use the destination property of the -Embedder.Context -when calling the embedder from Java code.

    - -

    Using voyage-4-nano for local query inference

    -

    The voyage-4-nano - model is available as an ONNX model for use with the - Hugging Face embedder. - Since it shares the same embedding space as the larger - Voyage 4 models, - it can be used for query embeddings with local inference — trading some accuracy for lower cost - by eliminating API usage for queries entirely.

    - -

    OpenAI Embedder

    - -

    Available since {% include version.html version="8.678" %}

    - -

    An embedder that uses the OpenAI embeddings API -to generate embeddings for semantic search. The embedder can target any OpenAI-compatible API.

    - -
    {% highlight xml %}
    -
    -    
    -        text-embedding-3-small
    -        openai_api_key
    -        1536
    -    
    -
    -{% endhighlight %}
    - -
      -
    • - The model specifies which OpenAI model to use. -
    • -
    • - The api-key-secret-ref references a secret in Vespa's - secret store containing your OpenAI API key. - For self-hosted OpenAI-compatible endpoints that do not require authentication, this element can be omitted. -
    • -
    - -

    See the reference -for all configuration parameters.

    - -

    Mistral Embedder

    - -

    Available since {% include version.html version="8.678" %}

    - -

    An embedder that uses the Mistral -embeddings API to generate embeddings for semantic search.

    - -
    {% highlight xml %}
    -
    -    
    -        mistral-embed
    -        mistral_api_key
    -        1024
    -    
    -
    -{% endhighlight %}
    - -
      -
    • - The model specifies which Mistral model to use. -
    • -
    • - The api-key-secret-ref references a secret in Vespa's - secret store containing your Mistral API key. - This is required for authentication. -
    • -
    - -

    Mistral supports output quantization on models that offer it, such as codestral-embed. - See the reference - for all configuration parameters.

    - -

    Embedder performance

    - -

    Embedding inference can be resource-intensive for larger embedding models. Factors that impact performance:

    - -
      -
    • The embedding model parameters. Larger models are more expensive to evaluate than smaller models.
    • -
    • The sequence input length. Transformer models scale quadratically with input length. Since queries - are typically shorter than documents, embedding queries is less computationally intensive than embedding documents. -
    • -
    • - The number of inputs to the embed call. When encoding arrays, consider how many inputs a single document can have. - For local CPU inference, increasing feed timeout settings - might be required when documents have many embedinputs. -
    • -
    -

    For local ONNX-based embedders (such as the Hugging Face, -Bert, ColBERT, and SPLADE embedders), -using GPU, especially for longer sequence lengths (documents), -can dramatically improve performance and reduce cost. -See the blog post on GPU-accelerated ML inference in Vespa Cloud. -With GPU-accelerated instances, using fp16 models instead of fp32 can increase throughput by as much as 3x compared to fp32.

    -

    For cloud embedders that call an external API -(VoyageAI, OpenAI, Mistral), -throughput is bound by API latency and rate limits rather than local hardware. -See Thread pool tuning for cloud embedders and -dynamic batching for tuning guidance.

    -

    - Refer to binarizing vectors for how to reduce vector size. -

    - - - -

    Metrics

    - -

    Vespa's built-in embedders emit metrics for computation time and token sequence length. -These metrics are prefixed with embedder. -and listed in the Container Metrics reference documentation. -Third-party embedder implementations may inject the ai.vespa.embedding.Embedder.Runtime component to easily -emit the same predefined metrics, although emitting custom metrics is perfectly fine.

    - - - -

    Sample applications

    - -

    These sample applications use embedders:

    -
      -
    • commerce-product-ranking - - demonstrates using multiple embedders
    • -
    • multi-vector-indexing - demonstrates how to use embedders with multiple document field inputs
    • -
    • colbert - demonstrates how to use the colbert-embedder
    • -
    • colbert-long - demonstrates how to use the colbert-embedder with long contexts (array input)
    • -
    • splade demonstrates - how to use the splade-embedder.
    • -
    - - - -

    Tricks and tips

    - -

    Various tricks that are useful with embedders.

    - - -

    Adding a fixed string to a query text

    -

    - Embedding models might require text to be prepended with a fixed string, e.g.: -

    -
    {% highlight xml %}
    -
    -    
    -    
    -    
    -        query:
    -        passage:
    -    
    -
    -{% endhighlight %}
    -

    - The above configuration prepends text in queries and field data. - Find a complete example in the ColBERT - sample application. -

    -

    - The <prepend> element is also supported by the - OpenAI embedder, - which is useful for OpenAI-compatible instruction-tuned models that expect a task-specific prefix. -

    -

    - An alternative approach is using query profiles to prepend query data. - If you need to add a standard wrapper or a prefix instruction around the input text you want to embed - use parameter substitution to supply the text, as in embed(myEmbedderId, @text), - and let the parameter (text here) be defined in a query profile, - which in turn uses value substitution - to place another query request with a supplied text value within it. The following is a concrete example - where queries should have a prefix instruction before being embedded in a vector representation. The following - defines a text input field to search/query-profiles/default.xml: -

    -
    {% highlight xml %}
    -
    -    "Represent this sentence for searching relevant passages: %{user_query}
    -
    -{% endhighlight %}
    - -

    Then, at query request time, we can pass user_query as a request parameter, this parameter is then used to produce -the text value which then is embedded.

    -
    {% highlight json %}
    -{
    -    "yql": "select * from doc where userQuery() or (totalTtargetHits: 100}nearestNeighbor(embedding, e))",
    -    "input.query(e)": "embed(mxbai, @text)",
    -    "user_query": "space contains many suns"
    -}
    -{% endhighlight %}
    -

    -The text that is embedded by the embedder is then: -Represent this sentence for searching relevant passages: space contains many suns. -

    - - -

    Concatenating input fields

    - -

    You can concatenate values in indexing using ".", and handle missing field values using - choice - to produce a single input for an embedder: -

    - -
    -schema doc {
    -
    -    document doc {
    -
    -        field title type string {
    -            indexing: summary | index
    -        }
    -
    -        field body type string {
    -            indexing: summary | index
    -        }
    -
    -    }
    -
    -    field embeddings type tensor<bfloat16>(x[384]) {
    -        indexing {
    -            (input title || "") . " " . (input body || "") | embed embedderId | attribute | index
    -        }
    -        index: hnsw
    -    }
    -
    -}
    -
    - -

    You can also use concatenation to add a fixed preamble to the string to embed.

    - - -

    Combining with foreach

    - -

    The indexing expression can also use for_each and include other document fields. - For example, the E5 family of embedding models uses instructions along with the input. The following - expression prefixes the input with passage: followed by a concatenation of the title and a text chunk.

    - -
    -schema doc {
    -
    -    document doc {
    -
    -        field title type string {
    -            indexing: summary | index
    -        }
    -
    -        field chunks type array<string> {
    -            indexing: index | summary
    -        }
    -
    -    }
    -    field embedding type tensor<bfloat16>(p{}, x[384]) {
    -        indexing {
    -            input chunks |
    -                for_each {
    -                    "passage: " . (input title || "") . " " . ( _ || "")
    -                } | embed e5 | attribute | index
    -        }
    -        attribute {
    -            distance-metric: prenormalized-angular
    -        }
    -    }
    -}
    -
    -

    See Indexing language execution valuefor details.

    - - -

    Separate feed and search embedders

    - -

    In Vespa Cloud, it is general practice to configure separate container clusters for feed and search, so that -bursty feed load cannot affect query latency. When using HTTP-based cloud embedders -(VoyageAI, OpenAI, Mistral), -configure a separate embedder component in each cluster. This lets you pick different models and API keys per workload, -and gives two additional benefits: cost optimization (via model variants) and -rate limit isolation.

    - -
    {% highlight xml %}
    -
    -    
    -        voyage-4-large
    -        1024
    -        voyage_feed_api_key
    -    
    -    
    -
    -
    -
    -    
    -        voyage-4-lite
    -        1024
    -        voyage_search_api_key
    -    
    -    
    -
    -{% endhighlight %}
    - -

    Cost optimization with model variants

    -

    When a provider offers multiple model sizes that share the same embedding space, you can use a more powerful - (and more expensive) model for document embeddings while using a smaller, cheaper model for query embeddings. - Since document embedding happens once during indexing but query embedding occurs on every search request, - this can significantly reduce operational costs while maintaining retrieval quality.

    - -

    For example, the Voyage 4 model family shares a - vector space across sizes, making it a natural fit for this pattern: - use voyage-4-large in the feed cluster and voyage-4-lite in the search cluster as shown above. - See also Using voyage-4-nano for local query inference - for an even more cost-effective query-side option.

    - -

    Rate limit isolation

    -

    Separating feed and search operations is particularly important for managing API rate limits. - Bursty document feeding operations can consume significant API quota, potentially causing rate limit errors - that affect search queries. By using separate API keys for feed and search embedders, - you ensure that feeding bursts don't negatively impact search.

    - -

    Thread pool tuning for cloud embedders

    -

    When using an HTTP-based cloud embedder (VoyageAI, OpenAI, Mistral), container feed throughput is primarily - limited by embedding API latency combined with the document processing thread pool size, not by CPU. - Each document being fed blocks a thread while waiting for the embedding API response. To improve throughput, - you likely have to increase the - document processing thread pool size, - assuming the content cluster is not the bottleneck.

    - -

    For example, consider a container cluster with 2 nodes, each with 8 vCPUs. With the default document processing - thread pool size of 1 thread per vCPU, you have 16 total threads. If the average embedding API latency is 200ms, - the maximum throughput is approximately 16 / 0.2 = 80 documents/second. - See container tuning for more on container tuning.

    - -

    Note that the effective throughput can never exceed the rate limit of your API key. - Use the embedder metrics - to determine embedder latency and throughput. - For additional throughput improvements, consider enabling dynamic batching.

    - -

    Dynamic batching

    -

    Dynamic batching combines multiple concurrent embedding requests into a single embedding invocation. - This is useful when throughput is constrained by the provider's - requests-per-minute (RPM) limit rather than the tokens-per-minute (TPM) limit. - Batching reduces RPM usage by combining requests; TPM usage is unaffected.

    - -

    Dynamic batching is supported by the VoyageAI, - OpenAI, and Mistral embedders.

    - -
    {% highlight xml %}
    -
    -    
    -        voyage-4-large
    -        1024
    -        voyage_feed_api_key
    -        
    -    
    -    
    -
    -{% endhighlight %}
    - -

    The max-size attribute sets the maximum number of requests in a single batch, - and max-delay sets the maximum time to wait for a full batch before sending a partial one. - Batching is disabled by default.

    - -

    The document processing thread pool size - should be at least max-size, since each thread contributes one request to the batch.

    - - -

    Troubleshooting

    - -

    This section covers common issues and how to resolve them.

    -

    Model download failure

    -

    - If models fail to download, it will cause the Vespa stateless container service to not start with - RuntimeException: Not able to create config builder for payload - - see example. -

    -

    - This usually means that the model download failed. Check the Vespa log for more details. - The most common reasons for download failure are network issues or incorrect URLs. -

    - -

    This will also be visible in the Vespa status output as the container will not listen to its port:

    -
    -vespa status -t http://127.0.0.1:8080            
    -Container at http://127.0.0.1:8080 is not ready: unhealthy container at http://127.0.0.1:8080/status.html: Get "http://127.0.0.1:8080/status.html": EOF
    -Error: services not ready: http://127.0.0.1:8080
    -
    - - -

    Tensor shape mismatch

    -

    - The native embedder implementations expect that the output tensor has a specific shape. - If the shape is incorrect, you will see an error message during feeding like: -

    -
    -feed: got status 500 ({"pathId":"..","..","message":"[UNKNOWN(252001) @ tcp/vespa-container:19101/chain.indexing]:
    -Processing failed. Error message: java.lang.IllegalArgumentException: Expected 3 output dimensions for output name 'sentence_embedding': [batch, sequence, embedding], got 2 -- See Vespa log for details. "}) for put xx:not retryable
    -
    -

    -This means that the exported ONNX model output tensor does not have the expected shape. For example, the above is -logged by the hf-embedder that expects the output shape to be [batch, sequence, embedding] (A 3D tensor). This is because the embedder -implementation performs the pooling-strategy over the sequence dimension to produce a single embedding vector. -The batch size is always 1 for Vespa embeddings. -

    -

    -See onnx export for how to export models to ONNX format with the correct output shapes and -onnx debug for debugging input and output names. -

    - -

    Input names

    -

    The native embedder implementations expect that the ONNX model accepts certain input names. - If the names are incorrect, it will cause the Vespa container service to not start, - and you will see an error message in the vespa log like:

    -
    -WARNING container        Container.com.yahoo.container.di.Container
    -Caused by: java.lang.IllegalArgumentException: Model does not contain required input: 'input_ids'. Model contains: my_input
    -
    - -

    This means that the ONNX model accepts "my_input", while our configuration attempted to use "input_ids". The default - input names for the hf-embedder are "input_ids", "attention_mask" and "token_type_ids". These are overridable - in the configuration (reference). Some embedding models do not - use the "token_type_ids" input. We can specify this in the configuration by setting transformer-token-type-ids to empty, - illustrated by the following example.

    -
    -{% highlight xml %}
    -
    -    
    -    
    -          
    -
    -{% endhighlight %}
    - -

    Output names

    -

    The native embedder implementations expect that the ONNX model produces certain output names. - It will cause the Vespa stateless container service to not start, - and you will see an error message in the vespa log like:

    -
    -    Model does not contain required output: 'test'. Model contains: last_hidden_state
    -
    -

    This means that the ONNX model produces "last_hidden_state", while our configuration attempted to use "test". The default - output name for the hf-embedder is "last_hidden_state". This is overridable - in the configuration. See reference.

    - -

    EOF

    -

    If vespa status shows that the container is healthy, but you observe an EOF error during feeding, this means that the stateless container service has - crashed and stopped listening to its port. This could be related to the embedder ONNX model size, docker container memory resource constraints, - or the configured JVM heap size of the Vespa stateless container service. -

    -
    -vespa feed ext/1.json 
    -feed: got error "Post "http://127.0.0.1:8080/document/v1/doc/doc/docid/1": unexpected EOF" (no body) for put id:doc:doc::1: giving up after 10 attempts
    -
    -

    This could be related to insufficient stateless container (JVM) memory. - Check the container logs for OOM errors. See jvm-tuning for JVM tuning options (The default heap size is 1.5GB). - Container crashes could also be caused by too little memory allocated to the docker or podman container, which can cause the Linux kernel to kill processes to free memory. - See the docker containers memory documentation. - Vespa estimates the memory needed for embedder model inference automatically, but the estimate can be inaccurate. - On Vespa Cloud, you can override it with <inference><memory> to reserve a specific amount of container memory for inference. -

    diff --git a/en/rag/embedding.mdx b/en/rag/embedding.mdx new file mode 100644 index 0000000000..abeb16ca27 --- /dev/null +++ b/en/rag/embedding.mdx @@ -0,0 +1,700 @@ +--- +title: "Embedding" +--- + +A common technique is to map unstructured data - say, text or images - to points in an abstract vector space and then do the computation in that space. For example, retrieve similar data by [finding nearby points in the vector space](/en/querying/approximate-nn-hnsw), or [using the vectors as input to a neural net](/en/ranking/onnx). This mapping is referred to as _embedding_. Read more about embedding and embedding management in this [blog post](https://blog.vespa.ai/tailoring-frozen-embeddings-with-vespa/). + +Embedding vectors can be sent to Vespa in queries and writes: + + + ![document- and query-embeddings](/assets/img/vespa-overview-embeddings-1.svg) + + +Alternatively, you can use the `embed` function to generate the embeddings inside Vespa to reduce vector transfer costs and make clients simpler: + + + ![Vespa's embedding feature, creating embeddings from text](/assets/img/vespa-overview-embeddings-2.svg) + + +Adding embeddings to schemas will change the characteristics of an application; Memory usage will grow, and feeding latency might increase. Read more on how to address this in [binarizing vectors](/en/rag/binarizing-vectors). + +## Configuring embedders + +Embedders are [components](/en/applications/components) which must be configured in your [services.xml](/en/reference/applications/services/services). Components are shared and can be used across schemas. + +```xml + + + + + + query: + passage: + + + ... + +``` + +You can [write your own](https://javadoc.io/doc/com.yahoo.vespa/linguistics/latest/com/yahoo/language/process/Embedder.html), or use [embedders provided in Vespa](#provided-embedders). + +If you have multiple container clusters that are using the same embedder, consider using an [include](/en/reference/applications/services/container#include) statement to avoid duplicating component config. + +## Embedding a query text + +Where you would otherwise supply a tensor in a query request, you can (with an embedder configured) instead supply any text enclosed in `embed()`: + +```bash +input.query(q)=embed(myEmbedderId, "Hello%20world") +``` + +Both single and double quotes are permitted, and if you have only configured a single embedder, you can skip the embedder id argument and the quotes. + +The text argument can be supplied by a referenced parameter instead, using the `@parameter` syntax: + +```json +{ + "yql": "select * from doc where {totalTargetHits:10}nearestNeighbor(embedding_field, query_embedding)", + "text": "my text to embed", + "input.query(query_embedding)": "embed(@text)", +} +``` + +Remember that regardless of whether you are using embedders, input tensors must always be [defined in the schema's rank-profile](/en/reference/schemas/schemas#inputs). + +## Embedding a document field + +Use the `embed` function of the [indexing language](/en/reference/writing/indexing-language#indexing-statement) to convert strings into embeddings: + +```text +schema doc { + + document doc { + + field title type string { + indexing: summary | index + } + + } + + field embeddings type tensor(x[384]) { + indexing { + input title |embed embedderId| attribute | index + } + } + +} +``` + +Notice that the embedding field is defined outside the `document` clause in the schema. If you have only configured a single embedder, you can skip the embedder id argument. + +The input field can also be an array, where the output becomes a rank two tensor, see [this blog post](https://blog.vespa.ai/semantic-search-with-multi-vector-indexing/): + +```bash +schema doc { + + document doc { + + field chunks type array { + indexing: index | summary + } + + } + + field embeddings type tensor(p{},x[5]) { + indexing: input chunks |embed embedderId| attribute | index + } + +} +``` + +## Provided embedders + +Vespa provides several embedders as part of the platform. + +### Huggingface Embedder + +An embedder using any [Huggingface tokenizer](https://huggingface.co/docs/tokenizers/index), including multilingual tokenizers, to produce tokens which are then input to a supplied transformer model in [ONNX](https://onnx.ai/) model format: + +```xml + + + + + + ... + +``` + +The huggingface-embedder supports all [Huggingface tokenizer implementations](https://huggingface.co/docs/tokenizers/index). + +- The `transformer-model` specifies the embedding model in [ONNX](https://onnx.ai/) format. See [exporting models to ONNX](/en/ranking/onnx#using-optimum-to-export-models-to-onnx-format) for how to export embedding models from Huggingface to be compatible with Vespa's `hugging-face-embedder`. See [Limitations on Model Size and Complexity](/en/ranking/onnx#limitations-on-model-size-and-complexity) for details on the ONNX model format supported by Vespa. +- The `tokenizer-model` specifies the Huggingface `tokenizer.json` formatted file. See [HF loading tokenizer from a JSON file.](https://huggingface.co/transformers/v4.8.0/fast_tokenizers.html#loading-from-a-json-file) + +Use `path` to supply the model files from the application package, `url` to supply them from a remote server, or `model-id` to use a [model supplied by Vespa Cloud](/en/rag/model-hub). You can also use a model hosted in private Huggingface Model Hub by adding your Huggingface API token to the [secret store](/en/security/secret-store) and referring to the secret using `secret-ref` in the model tag. See [model config reference](/en/reference/rag/embedding#model-config-reference) for more details. + +```xml + + + + + + ... + +``` + +See the [reference](/en/reference/rag/embedding#huggingface-embedder-reference-config) for all configuration parameters. + +#### Huggingface embedder models + +The following are examples of text embedding models that can be used with the hugging-face-embedder and their output [tensor](/en/ranking/tensor-user-guide) dimensionality. The resulting [tensor type](/en/reference/ranking/tensor#tensor-type-spec) can be `float`, `bfloat16` or using binarized quantization into `int8`. See blog post [Combining matryoshka with binary-quantization](https://blog.vespa.ai/combining-matryoshka-with-binary-quantization-using-embedder/) for more examples of using the Huggingface embedder with binary quantization. + +The following models use `pooling-strategy` `mean`, which is the default [pooling-strategy](/en/reference/rag/embedding#huggingface-embedder-reference-config): + +- [intfloat/e5-small-v2](https://huggingface.co/intfloat/e5-small-v2) produces `tensor(x[384])` +- [intfloat/e5-base-v2](https://huggingface.co/intfloat/e5-base-v2) produces `tensor(x[768])` +- [intfloat/e5-large-v2](https://huggingface.co/intfloat/e5-large-v2) produces `tensor(x[1024])` +- [intfloat/multilingual-e5-base](https://huggingface.co/intfloat/multilingual-e5-base) produces `tensor(x[768])` + +The following models are useful for binarization and Matryoshka dimensionality flexibility where only the first k dimensions are retained. [Matryoshka 🤝 Binary vectors: Slash vector search costs with Vespa](https://blog.vespa.ai/combining-matryoshka-with-binary-quantization-using-embedder/) is a great read on this subject. When enabling binarization with `int8` use [distance-metric hamming](/en/reference/schemas/schemas#hamming): + +- [mxbai-embed-large-v1](https://huggingface.co/mixedbread-ai/mxbai-embed-large-v1) produces `tensor(x[1024])`. This model is also useful for binarization, which can be triggered by using destination `tensor(x[128])`. Use `pooling-strategy` `cls` and `normalize` `true`. +- [nomic-embed-text-v1.5](https://huggingface.co/nomic-ai/nomic-embed-text-v1.5) produces `tensor(x[768])`. This model is also useful for binarization, which can be triggered by using destination `tensor(x[96])`. Use `normalize` `true`. + +Snowflake arctic model series: + +- [snowflake-arctic-embed-xs](https://huggingface.co/Snowflake/snowflake-arctic-embed-xs) produces `tensor(x[384])`. Use `pooling-strategy` `cls` and `normalize` `true`. +- [snowflake-arctic-embed-m](https://huggingface.co/Snowflake/snowflake-arctic-embed-m) produces `tensor(x[768])`. Use `pooling-strategy` `cls` and `normalize` `true`. + +All of these example text embedding models can be used in combination with Vespa's [nearest neighbor search](/en/querying/nearest-neighbor-search) using the appropriate [distance-metric](/en/reference/schemas/schemas#distance-metric). Notice that to use the [distance-metric: prenormalized-angular](/en/reference/schemas/schemas#prenormalized-angular), the `normalize` configuration must be set to `true`. + +Check the [Massive Text Embedding Benchmark](https://huggingface.co/blog/mteb) (MTEB) benchmark and [MTEB leaderboard](https://huggingface.co/spaces/mteb/leaderboard) for help with choosing an embedding model. + +### Bert embedder + +DEPRECATED; prefer using the [Huggingface Embedder](#huggingface-embedder) instead of the Bert embedder. + +An embedder using the [WordPiece](/en/reference/rag/embedding#wordpiece-embedder) embedder to produce tokens which are then input to a supplied [ONNX](https://onnx.ai/) model on the form expected by a BERT base model: + +```xml + + + + + 128 + last_hidden_state + + +``` + +- The `transformer-model` specifies the embedding model in [ONNX](https://onnx.ai/) format. See [exporting models to ONNX](/en/ranking/onnx#using-optimum-to-export-models-to-onnx-format), for how to export embedding models from Huggingface to compatible [ONNX](https://onnx.ai/) format. +- The `tokenizer-vocab` specifies the Huggingface `vocab.txt` file, with one valid token per line. Note that the Bert embedder does not support the `tokenizer.json` formatted tokenizer configuration files. This means that tokenization settings like max tokens should be set explicitly. +- The `transformer-output` specifies the name given to to embedding output in the `model.onnx` file; this will differ depending on how the model is exported to ONNX format. One common name is `last_hidden_state`, especially in transformer-based models. Other common names are `output` or `output_0`, `embedding` or `embeddings`, `sentence_embedding`, `pooled_output`, or `encoder_last_hidden_state`. The default is `output_0`. + +The Bert embedder is limited to English ([WordPiece](/en/reference/rag/embedding#wordpiece-embedder)) and BERT-styled transformer models with three model inputs (_input\_ids, attention\_mask, token\_type\_ids_). Prefer using the [Huggingface Embedder](#huggingface-embedder) instead of the Bert embedder. + +See [configuration reference](/en/reference/rag/embedding#bert-embedder-reference-config) for all configuration options. + +### ColBERT embedder + +An embedder supporting [ColBERT](https://github.com/stanford-futuredata/ColBERT) models. The ColBERT embedder maps text to _token_ embeddings, representing a text as multiple contextualized embeddings. This produces better quality than reducing all tokens into a single vector. + +Read more about ColBERT and the ColBERT embedder in blog post form [Announcing the Vespa ColBERT embedder](https://blog.vespa.ai/announcing-colbert-embedder-in-vespa/) and [Announcing Vespa Long-Context ColBERT](https://blog.vespa.ai/announcing-long-context-colbert-in-vespa/). + +```xml + + + + + 32 + 128 + + +``` + +- The `transformer-model` specifies the ColBERT embedding model in [ONNX](https://onnx.ai/) format. See [exporting models to ONNX](/en/ranking/onnx#using-optimum-to-export-models-to-onnx-format) for how to export embedding models from Huggingface to compatible [ONNX](https://onnx.ai/) format. The [vespa-engine/col-minilm](https://huggingface.co/vespa-engine/col-minilm) page on the HF model hub has a detailed example of how to export a colbert checkpoint to ONNX format for accelerated inference. +- The `tokenizer-model` specifies the Huggingface `tokenizer.json` formatted file. See [HF loading tokenizer from a JSON file.](https://huggingface.co/transformers/v4.8.0/fast_tokenizers.html#loading-from-a-json-file) +- The `max-query-tokens` controls the maximum number of query text tokens that are represented as vectors, and similarly, `max-document-tokens` controls the document side. These parameters can be used to control resource usage. + +See [configuration reference](/en/reference/rag/embedding#colbert-embedder-reference-config) for all configuration options and defaults. + +The ColBERT token embeddings are represented as a [mixed tensor](/en/ranking/tensor-user-guide#tensor-concepts): `tensor(token{}, x[dim])` where `dim` is the vector dimensionality of the contextualized token embeddings. The [colbert model checkpoint](https://huggingface.co/colbert-ir/colbertv2.0) on Hugging Face hub uses 128 dimensions. + +The embedder destination tensor is defined in the [schema](/en/basics/schemas), and depending on the target [tensor cell precision](/en/reference/ranking/tensor#tensor-type-spec) definition the embedder can compress the representation: If the target tensor cell type is `int8`, the ColBERT embedder compresses the token embeddings with binarization for the document to reduce storage to 1-bit per value, reducing the token embedding storage footprint by 32x compared to using float. The _query_ representation is not compressed with binarization. The following demonstrates two ways to use the ColBERT embedder in the document schema to [embed a document field](#embedding-a-document-field). + +```text +schema doc { + document doc { + field text type string {..} + } + field colbert_tokens type tensor(token{}, x[128]) { + indexing: input text | embed colbert | attribute + } + field colbert_tokens_compressed type tensor(token{}, x[16]) { + indexing: input text | embed colbert | attribute + } +} +``` + +The first field `colbert_tokens` stores the original representation as the tensor destination cell type is float. The second field, the `colbert_tokens_compressed` tensor is compressed. When using `int8` tensor cell precision, one should divide the original vector size by 8 (128/8 = 16). + +You can also use `bfloat16` instead of `float` to reduce storage by 2x compared to `float`. + +```text +field colbert_tokens type tensor(token{}, x[128]) { + indexing: input text | embed colbert | attribute +} +``` + +You can also use the ColBERT embedder with an array of strings (representing chunks): + +```text +schema doc { + document doc { + field chunks type array {..} + } + field colbert_tokens_compressed type tensor(chunk{}, token{}, x[16]) { + indexing: input text | embed colbert chunk | attribute + } +} +``` + +Here, we need a second mapped dimension in the target tensor and a second argument to embed, telling the ColBERT embedder the name of the tensor dimension to use for the chunks. + +Notice that the examples above did not specify the `index` function for creating a [HNSW](/en/querying/approximate-nn-hnsw) index. The colbert representation is intended to be used as a ranking model and not for retrieval with Vespa's nearestNeighbor query operator, where you can e.g., use a document-level vector and/or lexical matching. + +To reduce memory footprint, use [paged attributes](/en/content/attributes#paged-attributes). + +#### ColBERT ranking + +See the sample applications for using ColBERT in ranking with variants of the MaxSim similarity operator expressed using Vespa tensor computation expressions. See: [colbert](https://github.com/vespa-engine/sample-apps/tree/master/colbert) and [colbert-long](https://github.com/vespa-engine/sample-apps/tree/master/colbert-long). + +### SPLADE embedder + +An embedder supporting [SPLADE](https://github.com/naver/splade) models. The SPLADE embedder maps text to mapped tensor, representing a text as a sparse vector of unique tokens and their weights. + +```xml + + + + + + +``` + +- The `transformer-model` specifies the SPLADE embedding model in [ONNX](https://onnx.ai/) format. See [exporting models to ONNX](/en/ranking/onnx#using-optimum-to-export-models-to-onnx-format) for how to export embedding models from Huggingface to compatible [ONNX](https://onnx.ai/) format. +- The `tokenizer-model` specifies the Huggingface `tokenizer.json` formatted file. See [HF loading tokenizer from a JSON file.](https://huggingface.co/transformers/v4.8.0/fast_tokenizers.html#loading-from-a-json-file) + +See [configuration reference](/en/reference/rag/embedding#splade-embedder-reference-config) for all configuration options and defaults. + +The splade token weights are represented as a [mapped tensor](/en/ranking/tensor-user-guide#tensor-concepts): `tensor(token{})`. + +The embedder destination tensor is defined in the [schema](/en/basics/schemas). The following demonstrates how to use the SPLADE embedder in the document schema to [embed a document field](#embedding-a-document-field). + +```text +schema doc { + document doc { + field text type string {..} + } + field splade_tokens type tensor(token{}) { + indexing: input text | embed splade | attribute + } +} +``` + +You can also use the SPLADE embedder with an array of strings (representing chunks). Here, also using lower tensor cell precision `bfloat16`: + +```text +schema doc { + document doc { + field chunks type array {..} + } + field splade_tokens type tensor(chunk{}, token{}) { + indexing: input text | embed splade chunk | attribute + } +} +``` + +Here, we need a second mapped dimension in the target tensor and a second argument to embed, telling the splade embedder the name of the tensor dimension to use for the chunks. + +To reduce memory footprint, use [paged attributes](/en/content/attributes#paged-attributes). + +#### SPLADE ranking + +See the [splade](https://github.com/vespa-engine/sample-apps/tree/master/splade) sample application for how to use SPLADE in ranking, including also how to use the SPLADE embedder with an array of strings (representing chunks). + +#### SPLADE retrieval + +[Sparse retrieval in Vespa](https://blog.vespa.ai/redefining-hybrid-search-possibilities-with-vespa/#sparse-retrieval-in-vespa) is a good discussion on how SPLADE can be used for retrieval. + +### VoyageAI Embedder + +An embedder that uses the [VoyageAI](https://www.voyageai.com/) embedding API to generate high-quality embeddings for semantic search. This embedder calls the VoyageAI API service and does not require local model files or ONNX inference. All embeddings returned by VoyageAI are normalized to unit length, making them suitable for cosine similarity and [prenormalized-angular](/en/reference/schemas/schemas#prenormalized-angular) distance metrics (see [VoyageAI FAQ](https://docs.voyageai.com/docs/faq#which-similarity-function-should-i-use)). + +```xml + + + voyage-4 + voyage_api_key + 1024 + + +``` + +- The `model` specifies which VoyageAI model to use. +- The `api-key-secret-ref` references a secret in Vespa's [secret store](/en/security/secret-store) containing your VoyageAI API key. This is required for authentication. + +See the [reference](/en/reference/rag/embedding#voyageai-embedder-reference-config) for all configuration parameters. + +#### VoyageAI embedder models + +For the complete list of available models and their specifications, see: + +- [VoyageAI Embeddings Documentation](https://docs.voyageai.com/docs/embeddings) - General-purpose and specialized models +- [Contextualized Chunk Embeddings](https://docs.voyageai.com/docs/contextualized-chunk-embeddings) - Models for embedding document chunks with surrounding context. See [Working with chunks](../rag/working-with-chunks.html). +- [Multimodal Embeddings](https://docs.voyageai.com/docs/multimodal-embeddings) - Multimodal models for text, images, and video + +#### Contextualized chunk embeddings + +To use [contextualized chunk embeddings](https://docs.voyageai.com/docs/contextualized-chunk-embeddings), configure the VoyageAI embedder with a `voyage-context-*` model and use it to embed an `array` field containing your document chunks: + +```js +schema doc { + document doc { + field chunks type array { + indexing: index | summary + } + } + field embeddings type tensor(chunk{}, x[1024]) { + indexing: input chunks | embed voyage | attribute | index + attribute { + distance-metric: prenormalized-angular + } + } +} +``` + +When embedding array fields with a contextualized chunk embedding model, Vespa sends all chunks from a document in a single API request, allowing Voyage to encode each chunk with context from the other chunks. Be aware that the combined size of all chunks in a document must fit within the VoyageAI API's input token limit. See [Working with chunks](/en/rag/working-with-chunks) for chunking strategies. + +#### Input type detection + +VoyageAI models distinguish between query and document embeddings for improved retrieval quality. The embedder automatically detects the context and sets the appropriate input type based on whether the embedding is performed during feed (/en/writing/indexing ) or query processing in Vespa. + +For advanced use cases where you need to control the input type programmatically, you can use the `destination` property of the [Embedder.Context](https://javadoc.io/static/com.yahoo.vespa/linguistics/8.620.35/com/yahoo/language/process/Embedder.Context.html) when calling the embedder from Java code. + +#### Using voyage-4-nano for local query inference + +The [voyage-4-nano](/en/rag/model-hub#voyage-4-nano) model is available as an ONNX model for use with the [Hugging Face embedder](#huggingface-embedder). Since it shares the same embedding space as the larger [Voyage 4](https://blog.voyageai.com/2026/01/15/voyage-4/) models, it can be used for query embeddings with local inference — trading some accuracy for lower cost by eliminating API usage for queries entirely. + +### OpenAI Embedder + +Available since Vespa 8.678 + +An embedder that uses the [OpenAI](https://platform.openai.com/docs/guides/embeddings) embeddings API to generate embeddings for semantic search. The embedder can target any OpenAI-compatible API. + +```xml + + + text-embedding-3-small + openai_api_key + 1536 + + +``` + +- The `model` specifies which OpenAI model to use. +- The `api-key-secret-ref` references a secret in Vespa's [secret store](/en/security/secret-store) containing your OpenAI API key. For self-hosted OpenAI-compatible endpoints that do not require authentication, this element can be omitted. + +See the [reference](/en/reference/rag/embedding#openai-embedder-reference-config) for all configuration parameters. + +### Mistral Embedder + +Available since Vespa 8.678 + +An embedder that uses the [Mistral](https://docs.mistral.ai/capabilities/embeddings/overview/) embeddings API to generate embeddings for semantic search. + +```xml + + + mistral-embed + mistral_api_key + 1024 + + +``` + +- The `model` specifies which Mistral model to use. +- The `api-key-secret-ref` references a secret in Vespa's [secret store](/en/security/secret-store) containing your Mistral API key. This is required for authentication. + +Mistral supports output quantization on models that offer it, such as `codestral-embed`. See the [reference](/en/reference/rag/embedding#mistral-embedder-reference-config) for all configuration parameters. + +## Embedder performance + +Embedding inference can be resource-intensive for larger embedding models. Factors that impact performance: + +- The embedding model parameters. Larger models are more expensive to evaluate than smaller models. +- The sequence input length. Transformer models scale quadratically with input length. Since queries are typically shorter than documents, embedding queries is less computationally intensive than embedding documents. +- The number of inputs to the `embed` call. When encoding arrays, consider how many inputs a single document can have. For local CPU inference, increasing [feed timeout](/en/reference/api/document-v1#timeout) settings might be required when documents have many `embed`inputs. + +For local ONNX-based embedders (such as the [Hugging Face](#huggingface-embedder), [Bert](#bert-embedder), [ColBERT](#colbert-embedder), and [SPLADE](#splade-embedder) embedders), using [GPU](/en/reference/rag/embedding#embedder-onnx-reference-config), especially for longer sequence lengths (documents), can dramatically improve performance and reduce cost. See the blog post on [GPU-accelerated ML inference in Vespa Cloud](https://blog.vespa.ai/gpu-accelerated-ml-inference-in-vespa-cloud/). With GPU-accelerated instances, using fp16 models instead of fp32 can increase throughput by as much as 3x compared to fp32. + +For cloud embedders that call an external API ([VoyageAI](#voyageai-embedder), [OpenAI](#openai-embedder), [Mistral](#mistral-embedder)), throughput is bound by API latency and rate limits rather than local hardware. See [Thread pool tuning for cloud embedders](#thread-pool-tuning) and [dynamic batching](#dynamic-batching) for tuning guidance. + +Refer to [binarizing vectors](/en/rag/binarizing-vectors) for how to reduce vector size. + +## Metrics + +Vespa's built-in embedders emit metrics for computation time and token sequence length. These metrics are prefixed with `embedder.` and listed in the [Container Metrics](/en/reference/operations/metrics/container) reference documentation. Third-party embedder implementations may inject the `ai.vespa.embedding.Embedder.Runtime` component to easily emit the same predefined metrics, although emitting custom metrics is perfectly fine. + +## Sample applications + +These sample applications use embedders: + +- [commerce-product-ranking](https://github.com/vespa-engine/sample-apps/tree/master/commerce-product-ranking) - demonstrates using multiple embedders +- [multi-vector-indexing](https://github.com/vespa-engine/sample-apps/tree/master/multi-vector-indexing) demonstrates how to use embedders with multiple document field inputs +- [colbert](https://github.com/vespa-engine/sample-apps/tree/master/colbert) demonstrates how to use the colbert-embedder +- [colbert-long](https://github.com/vespa-engine/sample-apps/tree/master/colbert-long) demonstrates how to use the colbert-embedder with long contexts (array input) +- [splade](https://github.com/vespa-engine/sample-apps/tree/master/splade) demonstrates how to use the splade-embedder. + +## Tricks and tips + +Various tricks that are useful with embedders. + +### Adding a fixed string to a query text + +Embedding models might require text to be prepended with a fixed string, e.g.: + +```xml + + + + + query: + passage: + + +``` + +The above configuration prepends text in queries and field data. Find a complete example in the [ColBERT](https://github.com/vespa-engine/sample-apps/tree/master/colbert) sample application. + +The `` element is also supported by the [OpenAI embedder](/en/reference/rag/embedding#openai-embedder-reference-config), which is useful for OpenAI-compatible instruction-tuned models that expect a task-specific prefix. + +An alternative approach is using query profiles to prepend query data. If you need to add a standard wrapper or a prefix instruction around the input text you want to embed use parameter substitution to supply the text, as in `embed(myEmbedderId, @text)`, and let the parameter (`text` here) be defined in a [query profile](/en/querying/query-profiles), which in turn uses [value substitution](/en/querying/query-profiles#value-substitution) to place another query request with a supplied text value within it. The following is a concrete example where queries should have a prefix instruction before being embedded in a vector representation. The following defines a `text` input field to `search/query-profiles/default.xml`: + +```xml + + "Represent this sentence for searching relevant passages: %{user_query} + +``` + +Then, at query request time, we can pass `user_query` as a request parameter, this parameter is then used to produce the `text` value which then is embedded. + +```json +{ + "yql": "select * from doc where userQuery() or (totalTtargetHits: 100}nearestNeighbor(embedding, e))", + "input.query(e)": "embed(mxbai, @text)", + "user_query": "space contains many suns" +} +``` + +The text that is embedded by the embedder is then: _Represent this sentence for searching relevant passages: space contains many suns_. + +### Concatenating input fields + +You can concatenate values in indexing using "`.`", and handle missing field values using [choice](/en/writing/indexing#choice-example) to produce a single input for an embedder: + +```text +schema doc { + + document doc { + + field title type string { + indexing: summary | index + } + + field body type string { + indexing: summary | index + } + + } + + field embeddings type tensor(x[384]) { + indexing { + (input title || "") . " " . (input body || "") |embed embedderId| attribute | index + } + index: hnsw + } + +} +``` + +You can also use concatenation to add a fixed preamble to the string to embed. + +### Combining with foreach + +The indexing expression can also use `for_each` and include other document fields. For example, the _E5_ family of embedding models uses instructions along with the input. The following expression prefixes the input with _passage:_ followed by a concatenation of the title and a text chunk. + +```text +schema doc { + + document doc { + + field title type string { + indexing: summary | index + } + + field chunks type array { + indexing: index | summary + } + + } + field embedding type tensor(p{}, x[384]) { + indexing { + input chunks | + for_each { + "passage: " . (input title || "") . " " . ( _ || "") + } | embed e5 | attribute | index + } + attribute { + distance-metric: prenormalized-angular + } + } +} +``` + +See [Indexing language execution value](/en/writing/indexing#execution-value-example)for details. + +### Separate feed and search embedders + +In Vespa Cloud, it is general practice to configure separate container clusters for feed and search, so that bursty feed load cannot affect query latency. When using HTTP-based cloud embedders ([VoyageAI](#voyageai-embedder), [OpenAI](#openai-embedder), [Mistral](#mistral-embedder)), configure a separate embedder component in each cluster. This lets you pick different models and API keys per workload, and gives two additional benefits: **cost optimization** (via model variants) and **rate limit isolation**. + +```xml + + + voyage-4-large + 1024 + voyage_feed_api_key + + + + + + + voyage-4-lite + 1024 + voyage_search_api_key + + + +``` + +#### Cost optimization with model variants + +When a provider offers multiple model sizes that share the same embedding space, you can use a more powerful (and more expensive) model for document embeddings while using a smaller, cheaper model for query embeddings. Since document embedding happens once during indexing but query embedding occurs on every search request, this can significantly reduce operational costs while maintaining retrieval quality. + +For example, the [Voyage 4 model family](https://blog.voyageai.com/2026/01/15/voyage-4/) shares a vector space across sizes, making it a natural fit for this pattern: use `voyage-4-large` in the feed cluster and `voyage-4-lite` in the search cluster as shown above. See also [Using voyage-4-nano for local query inference](#voyageai-local-query-inference) for an even more cost-effective query-side option. + +#### Rate limit isolation + +Separating feed and search operations is particularly important for managing API rate limits. Bursty document feeding operations can consume significant API quota, potentially causing rate limit errors that affect search queries. By using **separate API keys** for feed and search embedders, you ensure that feeding bursts don't negatively impact search. + +### Thread pool tuning for cloud embedders + +When using an HTTP-based cloud embedder (VoyageAI, OpenAI, Mistral), container feed throughput is primarily limited by embedding API latency combined with the document processing thread pool size, not by CPU. Each document being fed blocks a thread while waiting for the embedding API response. To improve throughput, you likely have to increase the [document processing thread pool size](../reference/applications/services/docproc#threadpool), assuming the content cluster is not the bottleneck. + +For example, consider a container cluster with 2 nodes, each with 8 vCPUs. With the default document processing thread pool size of 1 thread per vCPU, you have 16 total threads. If the average embedding API latency is 200ms, the maximum throughput is approximately 16 / 0.2 = 80 documents/second. See [container tuning](/en/performance/container-tuning) for more on container tuning. + +Note that the effective throughput can never exceed the rate limit of your API key. Use the [embedder metrics](/en/reference/operations/metrics/container) to determine embedder latency and throughput. For additional throughput improvements, consider enabling [dynamic batching](#dynamic-batching). + +### Dynamic batching + +Dynamic batching combines multiple concurrent embedding requests into a single embedding invocation. This is useful when throughput is constrained by the provider's requests-per-minute (RPM) limit rather than the tokens-per-minute (TPM) limit. Batching reduces RPM usage by combining requests; TPM usage is unaffected. + +Dynamic batching is supported by the [VoyageAI](#voyageai-embedder), [OpenAI](#openai-embedder), and [Mistral](#mistral-embedder) embedders. + +```xml + + + voyage-4-large + 1024 + voyage_feed_api_key + + + + +``` + +The `max-size` attribute sets the maximum number of requests in a single batch, and `max-delay` sets the maximum time to wait for a full batch before sending a partial one. Batching is disabled by default. + +The [document processing thread pool size](/en/reference/applications/services/docproc#threadpool) should be at least `max-size`, since each thread contributes one request to the batch. + +## Troubleshooting + +This section covers common issues and how to resolve them. + +### Model download failure + +If models fail to download, it will cause the Vespa stateless container service to not start with `RuntimeException: Not able to create config builder for payload` - see [example](/en/applications/components#component-load). + +This usually means that the model download failed. Check the Vespa log for more details. The most common reasons for download failure are network issues or incorrect URLs. + +This will also be visible in the Vespa status output as the container will not listen to its port: + +```sh +vespa status -t http://127.0.0.1:8080 +Container at http://127.0.0.1:8080 is not ready: unhealthy container at http://127.0.0.1:8080/status.html: Get "http://127.0.0.1:8080/status.html": EOF +Error: services not ready: http://127.0.0.1:8080 +``` + +### Tensor shape mismatch + +The native embedder implementations expect that the output tensor has a specific shape. If the shape is incorrect, you will see an error message during feeding like: + +```json +feed: got status 500 ({"pathId":"..","..","message":"[UNKNOWN(252001) @ tcp/vespa-container:19101/chain.indexing]: +Processing failed. Error message: java.lang.IllegalArgumentException: Expected 3 output dimensions for output name 'sentence_embedding': [batch, sequence, embedding], got 2 -- See Vespa log for details. "}) for put xx:not retryable +``` + +This means that the exported ONNX model output tensor does not have the expected shape. For example, the above is logged by the [hf-embedder](#huggingface-embedder) that expects the output shape to be [batch, sequence, embedding] (A 3D tensor). This is because the embedder implementation performs the [pooling-strategy](/en/reference/rag/embedding#huggingface-embedder) over the sequence dimension to produce a single embedding vector. The batch size is always 1 for Vespa embeddings. + +See [onnx export](/en/ranking/onnx#using-optimum-to-export-models-to-onnx-format) for how to export models to ONNX format with the correct output shapes and [onnx debug](/en/ranking/onnx#debugging-onnx-models) for debugging input and output names. + +### Input names + +The native embedder implementations expect that the ONNX model accepts certain input names. If the names are incorrect, it will cause the Vespa container service to not start, and you will see an error message in the vespa log like: + +```sh +WARNING container Container.com.yahoo.container.di.Container +Caused by: java.lang.IllegalArgumentException: Model does not contain required input: 'input_ids'. Model contains: my_input +``` + +This means that the ONNX model accepts "my\_input", while our configuration attempted to use "input\_ids". The default input names for the [hf-embedder](#huggingface-embedder) are "input\_ids", "attention\_mask" and "token\_type\_ids". These are overridable in the configuration ([reference](/en/reference/rag/embedding#huggingface-embedder)). Some embedding models do not use the "token\_type\_ids" input. We can specify this in the configuration by setting `transformer-token-type-ids` to empty, illustrated by the following example. + +```xml + + + + + +``` + +### Output names + +The native embedder implementations expect that the ONNX model produces certain output names. It will cause the Vespa stateless container service to not start, and you will see an error message in the vespa log like: + +```sh +Model does not contain required output: 'test'. Model contains: last_hidden_state +``` + +This means that the ONNX model produces "last\_hidden\_state", while our configuration attempted to use "test". The default output name for the [hf-embedder](#huggingface-embedder) is "last\_hidden\_state". This is overridable in the configuration. See [reference](/en/reference/rag/embedding#huggingface-embedder). + +### EOF + +If vespa status shows that the container is healthy, but you observe an EOF error during feeding, this means that the stateless container service has crashed and stopped listening to its port. This could be related to the embedder ONNX model size, docker container memory resource constraints, or the configured JVM heap size of the Vespa stateless container service. + +```sh +vespa feed ext/1.json +feed: got error "Post "http://127.0.0.1:8080/document/v1/doc/doc/docid/1": unexpected EOF" (no body) for put id:doc:doc::1: giving up after 10 attempts +``` + +This could be related to insufficient stateless container (JVM) memory. Check the container logs for OOM errors. See [jvm-tuning](/en/performance/container-tuning#jvm-tuning) for JVM tuning options (The default heap size is 1.5GB). Container crashes could also be caused by too little memory allocated to the docker or podman container, which can cause the Linux kernel to kill processes to free memory. See the [docker containers memory](/en/operations/self-managed/docker-containers#memory) documentation. Vespa estimates the memory needed for embedder model inference automatically, but the estimate can be inaccurate. Override it with [\\](/en/reference/applications/services/container#inference-memory) to reserve a specific amount of container memory for inference. diff --git a/en/rag/external-llms.md b/en/rag/external-llms.mdx similarity index 64% rename from en/rag/external-llms.md rename to en/rag/external-llms.mdx index 9a52de67c6..96bdd8eb9a 100644 --- a/en/rag/external-llms.md +++ b/en/rag/external-llms.mdx @@ -1,11 +1,9 @@ --- -# Copyright Vespa.ai. All rights reserved. title: "External LLMs in Vespa" -redirect_from: - - /en/llms-external +sidebarTitle: "Using external LLMs" --- -Please refer to [Large Language Models in Vespa](llms-in-vespa.html) for an +Please refer to [Large Language Models in Vespa](/en/rag/llms-in-vespa) for an introduction to using LLMs in Vespa. Vespa provides a client for integration with OpenAI compatible APIs. @@ -19,13 +17,15 @@ You can also host your own OpenAI-compatible server using for example [VLLM](https://docs.vllm.ai/en/latest/getting_started/quickstart.html#quickstart-online) or [llama-cpp-server](https://llama-cpp-python.readthedocs.io/en/latest/server/). -{% include note.html content='Note that this is currently a Beta feature so changes can be expected.' %} + +**Note:** This is currently a Beta feature so changes can be expected. + ### Configuring the OpenAI client To set up a connection to an LLM service such as OpenAI's ChatGPT, you need to define a component in your application's -[services.xml](../reference/applications/services/services.html): +[services.xml](/en/reference/applications/services/services): ```xml @@ -35,10 +35,10 @@ define a component in your application's - + {/* Optional configuration: */} ... - + {/* endpoint example: https://openai-compatible-api.com/v1/ */} ... @@ -53,13 +53,13 @@ define a component in your application's To see the full list of available configuration parameters, refer to the [llm-client config definition file](https://github.com/vespa-engine/vespa/blob/master/model-integration/src/main/resources/configdefinitions/llm-client.def). This sets up a client component that can be used in a -[searcher](../learn/glossary.html#searcher) or a [document processor](../learn/glossary.html#document-processor). +[searcher](/en/learn/glossary#searcher) or a [document processor](/en/learn/glossary#document-processor). ### API key configuration Vespa provides several options to configure the API key used by the client. -1. Using the [Vespa Cloud secret store](../security/secret-store) to store the API key. +1. Using the [Vespa Cloud secret store](/en/security/secret-store) to store the API key. This is done by setting the `apiKeySecretRef` configuration parameter to the name of the secret in the secret store. This is the recommended way for Vespa Cloud users. 2. For self-managed Vespa, you can provide secrets via environment variables. @@ -75,17 +75,17 @@ connections, modify the `id` attribute in the component specification. We will see below how this is used to control which LLM is used for which task. As a reminder, Vespa also has the option of running custom LLMs locally. Please refer to -[running LLMs in your application](local-llms.html) for more information. +[running LLMs in your application](/en/rag/local-llms) for more information. ### Inference parameters -Please refer to the general discussion in [LLM parameters](llms-in-vespa.html#llm-parameters) for setting inference +Please refer to the general discussion in [LLM parameters](/en/rag/llms-in-vespa#llm-parameters) for setting inference parameters. The OpenAI-client also has the following inference parameters that can be sent along with the query: - +
    @@ -95,43 +95,44 @@ with the query: - - + + - - + + - - + + - - + + - - + + - - + + - - + +
    Parameter (Vespa)
    maxTokensmax_completion_tokens{`maxTokens`}{`max_completion_tokens`} Maximum number of tokens that can be generated in the chat completion.
    temperaturetemperature{`temperature`}{`temperature`} Number between 0 and 2. Higher values like 0.8 make output more random, while lower values like 0.2 make it more focused and deterministic.
    topPtop_p{`topP`}{`top_p`} An alternative to temperature sampling. Model considers tokens with top_p probability mass (0-1). Value of 0.1 means only tokens comprising top 10% probability are considered.
    seedseed{`seed`}{`seed`} If specified, the system will attempt to sample deterministically, so repeated requests with the same seed should return similar results. Determinism is not guaranteed.
    npredictn{`npredict`}{`n`} How many chat completion choices to generate for each input message. Note that you will be charged based on the number of generated tokens across all choices.
    frequencypenaltyfrequency_penalty{`frequencypenalty`}{`frequency_penalty`} Number between -2.0 and 2.0. Positive values penalize new tokens based on their frequency in the text so far, decreasing the likelihood of repetition. Negative values encourage repetition.
    presencepenaltypresence_penalty{`presencepenalty`}{`presence_penalty`} Number between -2.0 and 2.0. Positive values penalize new tokens based on whether they appear in the text so far, increasing the model's likelihood to talk about new topics. Negative values encourage repeating content from the prompt.
    + Any parameter sent with the query will override configuration specified for the client component in `services.xml`. Note that if you are not using OpenAI's API, the parameters may be handled differently than the descriptions above. @@ -139,17 +140,23 @@ Note that if you are not using OpenAI's API, the parameters may be handled diffe ### Connecting to other OpenAI-compatible providers -By default, this particular client connects to the OpenAI service, but can be used against any -OpenAI chat completion compatible API -by changing the `endpoint` configuration parameter. +By default, this particular client connects to the OpenAI service, but can be used against any [OpenAI chat completion compatible API](https://platform.openai.com/docs/guides/text-generation/chat-completions-api) by changing the `endpoint` configuration parameter. ### FAQ -- **Q: How do I know if my LLM is compatible with the OpenAI client?** - - A: The OpenAI client is compatible with any LLM that implements the OpenAI chat completion API. You can check the documentation of your LLM provider to see if they support this API. -- **Q: Can I use the [Responses](https://platform.openai.com/docs/api-reference/responses/create) provided by OpenAI** - - A: No, currently only the [Chat Completion API](https://platform.openai.com/docs/api-reference/chat) is supported. -- **Q: Can I use the OpenAI client for reranking?** - - A: Yes, but currently, you need to implement a [custom searcher](../applications/searchers.html) that uses the OpenAI client to rerank the results. -- **Q: Can I use the OpenAI client for retrieving embeddings?** - - A: No, currently, only the [Chat Completion API](https://platform.openai.com/docs/api-reference/chat) is supported. \ No newline at end of file + + +The OpenAI client is compatible with any LLM that implements the OpenAI chat completion API. You can check the documentation of your LLM provider to see if they support this API. + + +[Responses](https://platform.openai.com/docs/api-reference/responses/create) + +No, currently only the [Chat Completion API](https://platform.openai.com/docs/api-reference/chat) is supported. + + +Yes, but currently, you need to implement a [custom searcher](/en/applications/searchers) that uses the OpenAI client to rerank the results. + + +No, currently, only the [Chat Completion API](https://platform.openai.com/docs/api-reference/chat) is supported. + + \ No newline at end of file diff --git a/en/rag/llms-in-vespa.md b/en/rag/llms-in-vespa.mdx similarity index 65% rename from en/rag/llms-in-vespa.md rename to en/rag/llms-in-vespa.mdx index dbac37405e..bab3f30331 100644 --- a/en/rag/llms-in-vespa.md +++ b/en/rag/llms-in-vespa.mdx @@ -1,35 +1,23 @@ --- -# Copyright Vespa.ai. All rights reserved. title: "Large Language Models in Vespa" -redirect_from: - - /en/llms-in-vespa +sidebarTitle: "LLMs in Vespa" --- -Large Language Models (LLMs) are AI systems that generate human-like text, -supporting a variety of applications like chatbots and content generation. In -Vespa, LLMs can enhance search relevance, create dynamic content based on search -results, and understand natural language by integrating into Vespa's processing -chain structure, which handles querying and data ingestion. This allows Vespa to -apply LLMs' deep linguistic and semantic capabilities across different stages, -improving tasks such as document enrichment, query comprehension, summarization and question-answering. +Large Language Models (LLMs) are AI systems that generate human-like text, supporting a variety of applications like chatbots and content generation. In Vespa, LLMs can enhance search relevance, create dynamic content based on search +results, and understand natural language by integrating into Vespa's processing chain structure, which handles querying and data ingestion. This allows Vespa to apply LLMs' deep linguistic and semantic capabilities across different stages, improving tasks such as document enrichment, query comprehension, summarization and question-answering. -Vespa is ideally suited for retrieval-augmented generation (RAG). This technique -allows these models to access relevant and up-to-date information beyond their -training in real-time, enabling Vespa's output to be contextually informed. For -more information, refer to [Retrieval-Augmented Generation in Vespa](rag). +Vespa is ideally suited for retrieval-augmented generation (RAG). This technique allows these models to access relevant and up-to-date information beyond their training in real-time, enabling Vespa's output to be contextually informed. For more information, refer to [Retrieval-Augmented Generation in Vespa](/en/rag/rag). -The advantage of setting up a client connection to an LLM from within your Vespa application compared to doing the API call(s) -from your client after responses are returned from Vespa is that you eliminate an extra network hop, which means lower latency for end users. -The importance of this is amplified if you want to leverage multiple LLM calls for eg. agentic applications or reranking. +The advantage of setting up a client connection to an LLM from within your Vespa application compared to doing the API call(s) from your client after responses are returned from Vespa is that you eliminate an extra network hop, which means lower latency for end users. The importance of this is amplified if you want to leverage multiple LLM calls for eg. agentic applications or reranking. Vespa supports LLMs in three ways: -1. [**External LLMs**](external-llms): Vespa can connect to any external LLM provider that serves an +1. [**External LLMs**](/en/rag/external-llms): Vespa can connect to any external LLM provider that serves an OpenAI-compatible API. -2. [**Local LLMs**](local-llms): Vespa can run LLMs within the Vespa application itself. +2. [**Local LLMs**](/en/rag/local-llms): Vespa can run LLMs within the Vespa application itself. This allows for customized models and avoids sending data outside the application. This is particularly useful for applications with strict data privacy requirements or those needing specific model configurations. -3. [**Custom language models**](#custom-language-model-components): Vespa can be extended to support any language model, including those not based on OpenAI's API. This allows for flexibility in integrating various LLMs into Vespa applications. +3. [**Custom language models**](/en/rag/llms-in-vespa#custom-language-model-components): Vespa can be extended to support any language model, including those not based on OpenAI's API. This allows for flexibility in integrating various LLMs into Vespa applications. This document will focus on features that are common to both external and local LLMs. For more information on configuration details for each type, please refer to the respective sections. @@ -39,28 +27,25 @@ which demonstrates setting up Vespa for RAG, using either an external LLM servic ### Using LLMs -{% include note.html content='This feature is available in Vespa versions >= 8.327' %} + +**Note:** This feature is available in Vespa versions >= 8.327 + -Vespa distinguishes between the clients used to connect to LLMs and components that uses these clients. -You can, for instance, set up a single client connection to an LLM, and use this connection -for both [document enrichment](document-enrichment) and retrieval-augmented generation (RAG). +Vespa distinguishes between the clients used to connect to LLMs and components that uses these clients. You can, for instance, set up a single client connection to an LLM, and use this connection for both [document enrichment](/en/rag/document-enrichment) and retrieval-augmented generation (RAG). -![LLM/RAG searcher](../../assets/img/llm-rag-searcher.svg) + +![LLM/RAG searcher](/assets/img/llm-rag-searcher.svg) + +After adding a client connection to your `services.xml`, you can use the same client for various tasks such as retrieval-augmented generation. To do this, you need to set up the searchers or document processors that will use them. An example of a simple searcher that uses the client component is the `LLMSearcher`, which can be set up like this: -After adding a client connection to your `services.xml`, you can use the same client for various -tasks such as retrieval-augmented generation. To do this, you need to set up the -searchers or document processors that will use them. An example of a simple -searcher that uses the client component is the `LLMSearcher`, which can be set -up like this: - -``` +```xml ... - + {/* Configure as required */} @@ -79,38 +64,28 @@ up like this: ``` -This sets up a new [search chain](../reference/applications/services/search.html#chain) which -includes an `LLMSearcher`. This searcher has the responsibility of calling out to -the LLM connection using some prompt that has been sent along with the query. +This sets up a new [search chain](/en/reference/applications/services/search#chain) which includes an `LLMSearcher`. This searcher has the responsibility of calling out to the LLM connection using some prompt that has been sent along with the query. -Note the `providerId` configuration parameter: this must match the `id` given in -the component specification. Using this, one can set up as many clients and -searchers and combinations of these as one needs. If you do not specify a +Note the `providerId` configuration parameter: this must match the `id` given in the component specification. Using this, one can set up as many clients and searchers and combinations of these as one needs. If you do not specify a `providerId`, the searcher will use the first available LLM connection. -This particular searcher doesn't provide a lot of functionality, it only calls -out to the LLM service using a provided prompt sent along with the query. The -searcher expects the prompt to be passed in the query parameter `prompt`. For -instance, using the Vespa CLI: +This particular searcher doesn't provide a lot of functionality, it only calls out to the LLM service using a provided prompt sent along with the query. The searcher expects the prompt to be passed in the query parameter `prompt`. For instance, using the Vespa CLI: -``` +```sh $ vespa query \ --header="X-LLM-API-KEY:..." \ searchChain=llm \ prompt="what was the manhattan project?" ``` -Here, we first pass along the API key to the OpenAI API. You need to provide your -own OpenAI key for this. The `searchChain` parameter selects the `llm` chain set -up in `services.xml`. Finally, the `prompt` parameter determines what is sent to -the language model. +Here, we first pass along the API key to the OpenAI API. You need to provide your own OpenAI key for this. The `searchChain` parameter selects the `llm` chain set +up in `services.xml`. Finally, the `prompt` parameter determines what is sent to the language model. -Note that if the `prompt` query parameter is not provided, the `LLMSearcher` will -try to use the `query` query parameter. +Note that if the `prompt` query parameter is not provided, the `LLMSearcher` will try to use the `query` query parameter. By running the above command you will get something like the following: -``` +```json expandable { "root": { "id": "token_stream", @@ -159,12 +134,10 @@ By running the above command you will get something like the following: ### Streaming with Server-Sent Events -By running the above, you will have to wait until the entire response is -generated from the underlying LLM. This can take a while, as LLMs generate one -token at a time. To stream the tokens as they arrive, use the `sse` (Server-Sent +By running the above, you will have to wait until the entire response is generated from the underlying LLM. This can take a while, as LLMs generate one token at a time. To stream the tokens as they arrive, use the `sse` (Server-Sent Events) renderer by adding the `format` query parameter: -``` +```sh $ vespa query \ --header="X-LLM-API-KEY:..." \ searchChain=llm \ @@ -178,7 +151,7 @@ The Vespa CLI understands this format and will stream the tokens as they arrive. The underlying format is [Server-Sent Events](https://html.spec.whatwg.org/multipage/server-sent-events.html), and the output from Vespa is like this: -``` +```sh $ vespa query \ --format=plain \ --header="X-LLM-API-KEY:..." \ @@ -207,15 +180,12 @@ data: {"token":" a"} Notice the use of the `--format=plain` in the Vespa CLI here to output exactly what is sent from Vespa. -These events can be consumed by using a `EventSource` as described in the -[HTML specification](https://html.spec.whatwg.org/multipage/server-sent-events.html#server-sent-events), -or however you see fit as the format is fairly simple. Each `data` element -contains a small JSON object which must be parsed, and contains a single `token` -element containing the actual token. +These events can be consumed by using a `EventSource` as described in the [HTML specification](https://html.spec.whatwg.org/multipage/server-sent-events.html#server-sent-events), +or however you see fit as the format is fairly simple. Each `data` element contains a small JSON object which must be parsed, and contains a single `token` element containing the actual token. Errors are also sent in such events: -``` +```sh $ vespa query \ --header="X-LLM-API-KEY: banana" \ prompt="what was the manhattan project?" \ @@ -232,19 +202,17 @@ data: { ### LLM parameters -The LLM service typically has a set of inference parameters that can be set. This can -be parameters such as: +The LLM service typically has a set of inference parameters that can be set. This can be parameters such as: - `model` - for OpenAI can be any valid model such as `gpt-4o` or `gpt-4o-mini` etc. - `temperature` - for setting the model temperature - `maxTokens` - for setting the maximum number of tokens to produce -Note that these parameters are common to both [Local LLMs](local-llms) and [External LLMs](external-llms), -but each of them also supports additional inference parameters. See the respective sections for more details on these. +Note that these parameters are common to both [Local LLMs](/en/rag/local-llms) and [External LLMs](/en/rag/external-llms), but each of them also supports additional inference parameters. See the respective sections for more details on these. To provide inference parameters, you pass these along with the query: -``` +```sh $ vespa query \ --header="X-LLM-API-KEY: ..." \ prompt="what was the manhattan project?" \ @@ -254,11 +222,9 @@ $ vespa query \ llm.maxTokens=10 ``` -Note that these parameters are prepended with `llm`. This is so that you can -have multiple LLM searchers and control them independently by setting them up -with different property prefixes in `services.xml`. For instance: +Note that these parameters are prepended with `llm`. This is so that you can have multiple LLM searchers and control them independently by setting them up with different property prefixes in `services.xml`. For instance: -``` +```xml @@ -275,39 +241,28 @@ with different property prefixes in `services.xml`. For instance: ``` -Here, we have set up a chain with two LLM searchers, that have set up different -`propertyPrefix`s. The searchers use this to get their specific properties. This -also includes prompts. The prompt for the first searcher would thus be -`rag.prompt` and the second would be `llm.prompt`. +Here, we have set up a chain with two LLM searchers, that have set up different `propertyPrefix`s. The searchers use this to get their specific properties. This also includes prompts. The prompt for the first searcher would thus be `rag.prompt` and the second would be `llm.prompt`. -Note that if this `propertyPrefix` is not set, the default is `llm` and all LLM -searchers would share the same parameters. +Note that if this `propertyPrefix` is not set, the default is `llm` and all LLM searchers would share the same parameters. -Also note that `prompt` does not need to be prefixed in the query, however the -other parameters do need to. +Also note that `prompt` does not need to be prefixed in the query, however the other parameters do need to. -If you are using different LLM services, you can also distinguish between API -keys sent along with the query by prepending them as well with the -`propertyPrefix`. +If you are using different LLM services, you can also distinguish between API keys sent along with the query by prepending them as well with the `propertyPrefix`. ### Retrieval-Augmented Generation (RAG) -Above we used the `LLMSearcher` to call out to LLMs using a pre-specified -prompt. Vespa provides the `RAGSearcher` to construct a prompt based on search -results. This enables a flexible way of first searching for content in Vespa, -and using the results to generate a response. +Above we used the `LLMSearcher` to call out to LLMs using a pre-specified prompt. Vespa provides the `RAGSearcher` to construct a prompt based on search results. This enables a flexible way of first searching for content in Vespa, and using the results to generate a response. -Please refer to [RAG in Vespa](rag) for more details. +Please refer to [RAG in Vespa](/en/rag/rag) for more details. ### Structured output Both the `OpenAI` and `LocalLLM` clients in Vespa can also be configured to return [structured output](https://platform.openai.com/docs/guides/structured-outputs). This is done by providing an `llm.json_schema` in the query. (Assuming you are using the `LLMSearcher` or `RAGSearcher` with `propertyPrefix=llm`). -This can be useful for different use cases. -Examples include applying moderation of the output or providing the response in different styles and/or languages. +This can be useful for different use cases. Examples include applying moderation of the output or providing the response in different styles and/or languages. -```json +```json expandable { "type": "object", "properties": { @@ -334,7 +289,7 @@ Examples include applying moderation of the output or providing the response in The `json_schema` can be passed with the query using the `llm.json_schema` parameter: -``` +```sh $ vespa query \ --timeout 60 \ --header="X-LLM-API-KEY:" \ @@ -355,26 +310,21 @@ Which for example, using `gpt-4o-mini` returns } ``` -This can also leveraged for automated [Document Enrichment](document-enrichment) during ingestion. With this approach, the `json_schema` is automatically generated based on the Vespa schema (and your prompt). +This can also leveraged for automated [Document Enrichment](/en/rag/document-enrichment) during ingestion. With this approach, the `json_schema` is automatically generated based on the Vespa schema (and your prompt). ### Query profiles -In all the above you have sent parameters along with each query. It is worth -mentioning that Vespa supports [query profiles](../querying/query-profiles.html), which are -named collections of search parameters. This frees the client from having to -manage and send a large number of parameters, and enables the request parameters -for a use case to be changed without having to change the client. +In all the above you have sent parameters along with each query. It is worth mentioning that Vespa supports [query profiles](/en/querying/query-profiles), which are +named collections of search parameters. This frees the client from having to manage and send a large number of parameters, and enables the request parameters for a use case to be changed without having to change the client. ### Custom language model components -Vespa also allows you to create your own language model components. -This is useful in cases where you want to use a language model that is not [supported](local-llms#valid-llm-models) as local LLM through [llama.cpp](https://github.com/ggml-org/llama.cpp), -or if you want to use an external LLM service that is incompatible with the OpenAI API. +Vespa also allows you to create your own language model components. This is useful in cases where you want to use a language model that is not [supported](/en/rag/local-llms#valid-llm-models) as local LLM through [llama.cpp](https://github.com/ggml-org/llama.cpp), or if you want to use an external LLM service that is incompatible with the OpenAI API. To create your own language model component, you need to implement the `ai.vespa.llm.LanguageModel` interface. Minimal example shown below: -```java +```java expandable // Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. package ai.vespa.test; @@ -417,8 +367,7 @@ public class MockLanguageModel implements ai.vespa.llm.LanguageModel { } ``` -You can also create a [config definition](../applications/configuring-components.html#config-definition) -that will make your component configurable through the `services.xml` file. +You can also create a [config definition](/en/applications/configuring-components#config-definition) that will make your component configurable through the `services.xml` file. Example of a minimal config definition: @@ -430,16 +379,11 @@ package=ai.vespa.test repetitions int default=1 ``` -See also [developer guide](../applications/developer-guide.html) for more information on how to create your own components. +See also [developer guide](/en/applications/developer-guide) for more information on how to create your own components. ### Creating your own searchers in Java The above example uses the `LLMSearcher` -[class](https://github.com/vespa-engine/vespa/blob/master/container-search/src/main/java/ai/vespa/search/llm/LLMSearcher.java). -You can easily create your own LLM searcher in Java by either specifically -[injecting](../applications/dependency-injection.html) the connection component, or -subclassing the `LLMSearcher`. Please refer to [Searcher Development](../applications/searchers.html) or -[Document Processor Development](../applications/document-processors.html) for more information on creating your own components. - -Note that it should not be necessary to create your own components in Java to -use this functionality. +[class](https://github.com/vespa-engine/vespa/blob/master/container-search/src/main/java/ai/vespa/search/llm/LLMSearcher.java). You can easily create your own LLM searcher in Java by either specifically [injecting](/en/applications/dependency-injection) the connection component, or subclassing the `LLMSearcher`. Please refer to [Searcher Development](/en/applications/searchers) or [Document Processor Development](/en/applications/document-processors) for more information on creating your own components. + +Note that it should not be necessary to create your own components in Java to use this functionality. diff --git a/en/rag/local-llms.md b/en/rag/local-llms.mdx similarity index 77% rename from en/rag/local-llms.md rename to en/rag/local-llms.mdx index a8d568c0aa..4d13d37aa5 100644 --- a/en/rag/local-llms.md +++ b/en/rag/local-llms.mdx @@ -1,36 +1,29 @@ --- -# Copyright Vespa.ai. All rights reserved. title: "Running LLMs inside your Vespa application" -redirect_from: - - /en/llms-local +sidebarTitle: "Using local LLMs" --- -Please refer to [Large Language Models in Vespa](llms-in-vespa.html) for an -introduction to using LLMs in Vespa. +Please refer to [Large Language Models in Vespa](/en/rag/llms-in-vespa) for an introduction to using LLMs in Vespa. Vespa supports evaluating LLMs within your application, both on CPU and GPU. -{% include note.html content='Note that this is currently a Beta feature so changes can be expected.' %} + +**Note:** This is currently a Beta feature so changes can be expected. + Running large language models (LLMs) directly in your Vespa application offers various advantages, particularly in terms of data security and privacy. By running LLMs locally, sensitive information remains within the confines of the application or network, eliminating the risks associated with data transmission -to external services. This is especially important for [RAG use cases](rag) that handle sensitive or proprietary data, such as +to external services. This is especially important for [RAG use cases](/en/rag/rag) that handle sensitive or proprietary data, such as healthcare, finance, and legal services, where privacy compliance and data security are valid concerns. -Moreover, hosting LLMs locally allows applications to select from a wider -range of models that best suit their specific needs, rather than being limited -to the models offered by external providers. This flexibility enables -businesses to optimize performance, cost, and efficiency tailored to their -operational requirements. Additionally, managing LLMs in-house provides control -over model versions, allowing companies to maintain stable and consistent +Moreover, hosting LLMs locally allows applications to select from a wider range of models that best suit their specific needs, rather than being limited to the models offered by external providers. This flexibility enables +businesses to optimize performance, cost, and efficiency tailored to their operational requirements. Additionally, managing LLMs in-house provides control over model versions, allowing companies to maintain stable and consistent outputs by choosing when and how to update their models. -Finally, while massively large foundation models dominates the generalist use -case, the smaller, more specialized, models (sometimes called "small language -models") have become much more capable. +Finally, while massively large foundation models dominates the generalist use case, the smaller, more specialized, models (sometimes called "small language models") have become much more capable. For a quick start, check out the [RAG sample app](https://github.com/vespa-engine/sample-apps/tree/master/retrieval-augmented-generation) which demonstrates how to set up a local LLM. @@ -38,11 +31,13 @@ which demonstrates how to set up a local LLM. ### Setting up LLM clients in services.xml -{% include note.html content='This feature is available in Vespa versions >= 8.331' %} + +**Note:** This feature is available in Vespa versions >= 8.331 + To set up the required inference engine for running your model, you need to define a `LocalLLM` component in your application's -[services.xml](../reference/applications/services/services.html): +[services.xml](/en/reference/applications/services/services): ```xml @@ -62,29 +57,22 @@ define a `LocalLLM` component in your application's ``` -This component will ensure that the underlying inference engine is started and -load the model when the container nodes are started. Each container node in the +This component will ensure that the underlying inference engine is started and load the model when the container nodes are started. Each container node in the cluster will load the LLM. Note that you can set up -[multiple clusters of container nodes](../applications/containers.html). -This can be helpful for instance if you have multiple LLMs that don't fit in the -available GPU memory, or you would like to offload LLM inference to dedicated +[multiple clusters of container nodes](/en/applications/containers). This can be helpful for instance if you have multiple LLMs that don't fit in the available GPU memory, or you would like to offload LLM inference to dedicated nodes for performance reasons. -The [`model`](../reference/applications/config-files.html#parameter-types) configuration -parameter can be either set to a known `model-id` for Vespa Cloud, a `url` or a -`path` to the model inside the application package. Usually, however, LLM files -are too large to practically be included in the application package, so the -`url` attribute is used. See [below](#valid-llm-models) for more information on +The [`model`](/en/reference/applications/config-files#parameter-types) configuration +parameter can be either set to a known `model-id` for Vespa Cloud, a `url` or a `path` to the model inside the application package. Usually, however, LLM files +are too large to practically be included in the application package, so the `url` attribute is used. See [below](/en/rag/local-llms#valid-llm-models) for more information on model types that can be used in Vespa. -There are many other configuration parameters to customize how inference is run, -please see the [configuration](#local-llm-configuration) section for more details. +There are many other configuration parameters to customize how inference is run, please see the [configuration](/en/rag/local-llms#local-llm-configuration) section for more details. ### Valid LLM models Under the hood, Vespa uses [llama.cpp](https://github.com/ggerganov/llama.cpp). -Any model file that works with `llama.cpp` can be used in Vespa. This includes the -following base models and finetunes of them: +Any model file that works with `llama.cpp` can be used in Vespa. This includes the following base models and finetunes of them: - LLama 2/3 - Mistral 7B @@ -97,18 +85,11 @@ following base models and finetunes of them: Refer to the [supported models](https://github.com/ggerganov/llama.cpp?tab=readme-ov-file#description) section of `llama.cpp` for a full list of updated models. -Vespa supports the `GGUF` file format. `GGUF` models can be found on -[HuggingFace](https://huggingface.co/models), by searching for `GGUF`. Other -LLM formats such safetensors and pytorch.bin models need to be converted GGUF +Vespa supports the `GGUF` file format. `GGUF` models can be found on [HuggingFace](https://huggingface.co/models), by searching for `GGUF`. Other LLM formats such safetensors and pytorch.bin models need to be converted GGUF before use. Please refer to `llama.cpp` for conversion tools. -Quantized models are also supported. Models are typically trained to -[FP16](https://en.wikipedia.org/wiki/Half-precision_floating-point_format) -precision, but `GGUF` files support reduced precision to 8-bit or lower. This -can save space so larger models can fit in less memory. Be aware however, that -inference time can increase when using reduced precision, so be sure to -benchmark your application accordingly, both in token generation performance -but also in terms of output quality. +Quantized models are also supported. Models are typically trained to [FP16](https://en.wikipedia.org/wiki/Half-precision_floating-point_format) precision, but `GGUF` files support reduced precision to 8-bit or lower. This +can save space so larger models can fit in less memory. Be aware however, that inference time can increase when using reduced precision, so be sure to benchmark your application accordingly, both in token generation performance but also in terms of output quality. ### Local LLM configuration @@ -155,7 +136,7 @@ for an updated list of configuration parameters. Some important points are worth considering here. First is the context window, given by the `contextSize` parameter, which is the size (in number of tokens) that the model uses to generate the next token. In general, larger context -windows are required in [RAG applications](rag) to hold the context +windows are required in [RAG applications](/en/rag/rag) to hold the context from the retrieval stage. Models are trained with a certain context length, but this context length can typically be increased up to 4x without much loss in text generation quality. @@ -196,16 +177,10 @@ context window is required, the number of parallel requests must be decreased. Likewise, the number of parallel requests can be increased by decreasing the context size. This depends on the requirements of your application. - - ### Inference parameters -Please refer to the general discussion in [LLM parameters](llms-in-vespa.html#llm-parameters) for setting inference +Please refer to the general discussion in [LLM parameters](/en/rag/llms-in-vespa#llm-parameters) for setting inference parameters. Local LLM inference has the following inference parameters that can be sent along @@ -221,7 +196,7 @@ with the query: diversity and creativity but potentially more errors or incoherence. - `jsonSchema`: JSON schema to use for structured output. Specifying this parameter also enables structured output. - See [structured output](llms-in-vespa.html#structured-output) for more details. + See [structured output](/en/rag/llms-in-vespa#structured-output) for more details. The most significant here are `npredict` which will stop the token generation process after a certain number of tokens has been generated. Some models can @@ -244,13 +219,13 @@ GPU. - + {/* Sets up the inference on a mistral 7B model */} 10 40960 - true + true {/* default is true */} 100 @@ -264,17 +239,11 @@ Here, the model itself has 33 layers, and all are offloaded to the GPU. If your model is too large to fit on the GPU, you can speed up model evaluation by offloading parts of the model to the GPU. -To set up GPUs on self-hosted, please refer to [Container GPU setup](../operations/self-managed/vespa-gpu-container.html) +To set up GPUs on self-hosted, please refer to [Container GPU setup](/en/operations/self-managed/vespa-gpu-container) for more details. It is very easy to use GPU acceleration on Vespa Cloud. To enable GPU -inference, you need to [request GPUs](../reference/applications/services/services.html#gpu) on the container +inference, you need to [request GPUs](/en/reference/applications/services/services#gpu) on the container nodes. For a more practical introduction, please take a look at the [RAG sample app](https://github.com/vespa-engine/sample-apps/tree/master/retrieval-augmented-generation) -which also demonstrates how to evaluate the LLM on GPUs on Vespa Cloud. - - - - +which also demonstrates how to evaluate the LLM on GPUs on Vespa Cloud. \ No newline at end of file diff --git a/en/rag/model-hub.html b/en/rag/model-hub.html deleted file mode 100644 index 0520f5260f..0000000000 --- a/en/rag/model-hub.html +++ /dev/null @@ -1,565 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: Using machine-learned models from Vespa Cloud -applies_to: cloud -redirect_from: -- /en/cloud/model-hub.html ---- - -

    Vespa Cloud provides a set of machine-learned models that you can use -in your applications. These models will always be available on Vespa Cloud and are -frozen models. You -can also bring your own embedding model, by deploying it in the Vespa application package.

    - -

    You specify to use a model provided by Vespa Cloud by setting the model-id -attribute where you specify a model config. For example, when configuring the -Huggingface embedder -provided by Vespa, you can write:

    - -
    {% highlight xml %}
    -
    -    
    -        
    -    
    -    ...
    -
    -{% endhighlight %}
    - -

    With this, your application will have support for -text embedding -inference for both queries and documents. Nodes that have been provisioned with GPU acceleration, will automatically -use GPU for embedding inference.

    - - - -

    Vespa Cloud Embedding Models

    -

    Models on Vespa model hub are selected open-source embedding models with -great performance. See the Vespa blog on embedding tradeoffs for details on performance and quality. -These embedding models are useful for retrieval (semantic search), re-ranking, clustering, classification, and more.

    - - -

    Huggingface Embedder

    -

    These models are available for the Huggingface Embedder type="hugging-face-embedder". -All these models support mapping from string or array<string> to tensor representations.

    - -

    The output tensor cell-precision -can be <float>, <bfloat16>, or <int8>.

    - -

    Most models also support binarization, which requires using -distance-metric hamming instead of angular. -The E5 and multilingual-e5 models do not support binarization. -See the nanobeir hybrid evaluation leaderboard -for details on quality impact.

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

    alibaba-gte-modernbert

    -

    GTE (General Text Embedding) model trained from ModernBERT-base.

    -
    Model idalibaba-gte-modernbert
    Tensor definitiontensor<float>(x[768])
    Matryoshka dimensionsx[768], x[256]
    distance-metricangular
    Licenseapache-2.0
    Sourcehttps://huggingface.co/Alibaba-NLP/gte-modernbert-base @ 3ab3f8c
    LanguageEnglish
    Component declaration -
    - -
    {% highlight xml %}
    -    
    -        
    -        8192
    -        cls
    -    
    -{% endhighlight %}
    -
    -

    alibaba-gte-modernbert-int8

    - INT8 quantized variant of alibaba-gte-modernbert. Offers faster inference with minimal accuracy loss. -

    Model idalibaba-gte-modernbert-int8
    Tensor definitiontensor<float>(x[768])
    Matryoshka dimensionsx[768], x[256]
    distance-metricangular
    Licenseapache-2.0
    Sourcehttps://huggingface.co/Alibaba-NLP/gte-modernbert-base @ e7f32e3
    LanguageEnglish
    Component declaration -
    - -
    {% highlight xml %}
    -    
    -        
    -        8192
    -        cls
    -    
    -    {% endhighlight %}
    -
    -

    e5-base-v2

    -

    The base model of the E5 family.

    -
    Model-ide5-base-v2
    Tensor definitiontensor<float>(x[768]) or tensor<float>(p{},x[768])
    distance-metricangular
    LicenseMIT
    Sourcehttps://huggingface.co/intfloat/e5-base-v2
    LanguageEnglish
    Component declaration -
    - -
    {% highlight xml %}
    -    
    -        
    -        512
    -        
    -            query: 
    -            passage: 
    -        
    -    
    -{% endhighlight %}
    -
    -

    e5-large-v2

    -

    The largest model of the E5 family, at time of writing, this is the best performing - embedding model on the MTEB benchmark.

    -
    Model-ide5-large-v2
    Tensor definitiontensor<float>(x[1024]) or tensor<float>(p{},x[1024])
    distance-metricangular
    LicenseMIT
    Sourcehttps://huggingface.co/intfloat/e5-large-v2
    LanguageEnglish
    Component declaration -
    - -
    {% highlight xml %}
    -    
    -        
    -        512
    -        
    -            query: 
    -            passage: 
    -        
    -    
    -{% endhighlight %}
    -
    -

    e5-small-v2

    -

    The smallest and most cost-efficient model from the E5 family.

    -
    Model-ide5-small-v2
    Tensor definitiontensor<float>(x[384]) or tensor<float>(p{},x[384])
    distance-metricangular
    LicenseMIT
    Sourcehttps://huggingface.co/intfloat/e5-small-v2
    LanguageEnglish
    Component declaration -
    - -
    {% highlight xml %}
    -    
    -        
    -        512
    -        
    -            query: 
    -            passage: 
    -        
    -    
    -{% endhighlight %}
    -
    -

    lightonai-modernbert-large

    - Trained from ModernBERT-large on the Nomic Embed datasets, bringing the new advances of ModernBERT to embeddings. -

    Model idlightonai-modernbert-large
    Tensor definitiontensor<float>(x[1024])
    distance-metricangular
    Licenseapache-2.0
    Sourcehttps://huggingface.co/lightonai/modernbert-embed-large @ b3a781f
    LanguageEnglish
    Component declaration -
    - -
    {% highlight xml %}
    -    
    -        
    -        8192
    -        
    -            search_query: 
    -            search_document: 
    -        
    -    
    -{% endhighlight %}
    -
    -

    lightonai-modernbert-large-int8

    - INT8 quantized variant of lightonai-modernbert-large. Offers faster inference with minimal accuracy loss. -

    Model idlightonai-modernbert-large-int8
    Tensor definitiontensor<float>(x[1024])
    distance-metricangular
    Licenseapache-2.0
    Sourcehttps://huggingface.co/lightonai/modernbert-embed-large @ 95a19bf
    LanguageEnglish
    Component declaration -
    - -
    {% highlight xml %}
    -    
    -        
    -        8192
    -        
    -            search_query: 
    -            search_document: 
    -        
    -    
    -{% endhighlight %}
    -
    -

    multilingual-e5-base

    -

    The multilingual model of the E5 family. Use this model for multilingual queries and documents.

    -
    Model-idmultilingual-e5-base
    Tensor definitiontensor<float>(x[768]) or tensor<float>(p{},x[768])
    distance-metricangular
    LicenseMIT
    Sourcehttps://huggingface.co/intfloat/multilingual-e5-base
    LanguageMultilingual
    Component declaration -
    - -
    {% highlight xml %}
    -    
    -        
    -        512
    -        
    -            query: 
    -            passage: 
    -        
    -    
    -{% endhighlight %}
    -
    -

    nomic-ai-modernbert

    - Trained from ModernBERT-base on the Nomic Embed datasets, bringing the new advances of ModernBERT to embeddings. -

    Model idnomic-ai-modernbert
    Tensor definitiontensor<float>(x[768])
    Matryoshka dimensionsx[768], x[256]
    distance-metricangular
    Licenseapache-2.0
    Sourcehttps://huggingface.co/nomic-ai/modernbert-embed-base @ 92168cb
    LanguageEnglish
    Component declaration -
    - -
    {% highlight xml %}
    -    
    -        
    -        token_embeddings
    -        8192
    -        
    -            search_query: 
    -            search_document: 
    -        
    -    
    -{% endhighlight %}
    -
    -

    nomic-ai-modernbert-int8

    - INT8 quantized variant of nomic-ai-modernbert. Offers faster inference with minimal accuracy loss. -

    Model idnomic-ai-modernbert-int8
    Tensor definitiontensor<float>(x[768])
    distance-metricangular
    Licenseapache-2.0
    Sourcehttps://huggingface.co/nomic-ai/modernbert-embed-base @ d556a88
    LanguageEnglish
    Component declaration -
    - -
    {% highlight xml %}
    -    
    -        
    -        8192
    -        token_embeddings
    -        
    -            search_query: 
    -            search_document: 
    -        
    -    
    -{% endhighlight %}
    -
    -

    snowflake-arctic-embed-m-v2.0

    - Embedding model based on snowflake-arctic-embed-m-v2.0. -

    Model idsnowflake-arctic-embed-m-v2.0
    Tensor definitiontensor<float>(x[768])
    distance-metricangular
    Licenseapache-2.0
    Sourcehttps://huggingface.co/Snowflake/snowflake-arctic-embed-m-v2.0 @ 95c2741
    LanguageMultilingual
    Component declaration -
    - -
    {% highlight xml %}
    -    
    -        
    -        8192
    -        token_embeddings
    -        cls
    -        true
    -        
    -            query: 
    -        
    -    
    -{% endhighlight %}
    -
    -

    snowflake-arctic-embed-m-v2.0-int8

    - INT8 quantized variant of snowflake-arctic-embed-m-v2.0. Offers faster inference with minimal accuracy loss. -

    Model idsnowflake-arctic-embed-m-v2.0-int8
    Tensor definitiontensor<float>(x[768])
    distance-metricangular
    Licenseapache-2.0
    Sourcehttps://huggingface.co/Snowflake/snowflake-arctic-embed-m-v2.0 @ 95c2741
    LanguageMultilingual
    Component declaration -
    - -
    {% highlight xml %}
    -    
    -        
    -        8192
    -        token_embeddings
    -        cls
    -        true
    -        
    -            query: 
    -        
    -    
    -{% endhighlight %}
    -
    -

    voyage-4-nano

    - Embedding model based on voyage-4-nano-ONNX. -

    Model idvoyage-4-nano
    Tensor definitiontensor<float>(x[2048])
    Matryoshka dimensionsx[2048], x[1024], x[512], x[256]
    distance-metricangular
    Licenseapache-2.0
    Sourcehttps://huggingface.co/thomasht86/voyage-4-nano-ONNX @ fcf290d
    LanguageEnglish
    Component declaration -
    - -
    {% highlight xml %}
    -    
    -        
    -        32768
    -        mean
    -        true
    -        
    -            Represent the query for retrieving supporting documents: 
    -            Represent the document for retrieval: 
    -        
    -    
    -{% endhighlight %}
    -
    -

    voyage-4-nano-int8

    - INT8 quantized variant of voyage-4-nano. Offers faster inference with minimal accuracy loss. -

    Model idvoyage-4-nano-int8
    Tensor definitiontensor<float>(x[2048])
    Matryoshka dimensionsx[2048], x[1024], x[512], x[256]
    distance-metricangular
    Licenseapache-2.0
    Sourcehttps://huggingface.co/thomasht86/voyage-4-nano-ONNX @ fcf290d
    LanguageEnglish
    Component declaration -
    - -
    {% highlight xml %}
    -    
    -        
    -        32768
    -        mean
    -        true
    -        
    -            Represent the query for retrieving supporting documents: 
    -            Represent the document for retrieval: 
    -        
    -    
    -{% endhighlight %}
    -
    -
    - -

    Bert Embedder

    -

    These models are available for the Bert Embedder -type="bert-embedder":

    - -
    {% highlight xml %}
    -
    -    
    -        
    -        
    -    
    -    ...
    -
    -{% endhighlight %}
    -

    Note bert-embedder requires both transformer-model and tokenizer-vocab.

    - - - - - - - - - - - - - - - - - - - - - - -

    minilm-l6-v2

    -

    A small, fast sentence-transformer model.

    -
    Model-idminilm-l6-v2
    Tensor definitiontensor<float>(x[384]) or tensor<float>(p{},x[384])
    distance-metricangular
    Licenseapache-2.0
    Sourcehttps://huggingface.co/sentence-transformers/all-MiniLM-L6-v2
    LanguageEnglish

    mpnet-base-v2

    -

    A larger, but better than minilm-l6-v2 sentence-transformer model.

    -
    Model-idmpnet-base-v2
    Tensor definitiontensor<float>(x[768]) or tensor<float>(p{},x[768])
    distance-metricangular
    Licenseapache-2.0
    Sourcehttps://huggingface.co/sentence-transformers/all-mpnet-base-v2
    LanguageEnglish
    - - -

    Tokenization Embedders

    -

    - These are embedder implementations that tokenize text and embed string to the vocabulary identifiers. - These are most useful for creating the tensor inputs to re-ranking models that take both the query and document token identifiers as input. - Find examples in the - sample applications. -

    - - - - - - - - - - - - - - - - - - - - - - - - -

    bert-base-uncased

    - A vocabulary text (vocab.txt) file on the format expected by - WordPiece: - A text token per line. -
    Model-idbert-base-uncased
    Licenseapache-2.0
    Sourcehttps://huggingface.co/sentence-transformers/all-MiniLM-L6-v2

    e5-base-v2-vocab

    - A tokenizer.json configuration file on the format expected by - HF tokenizer. - This tokenizer configuration can be used with e5-base-v2, e5-small-v2 and e5-large-v2. -
    Model-ide5-base-v2-vocab
    LicenseMIT
    Sourcehttps://huggingface.co/intfloat/e5-base-v2
    LanguageEnglish

    multilingual-e5-base-vocab

    - A tokenizer.json configuration file on the format expected by - HF tokenizer. - This tokenizer configuration can be used with multilingual-e5-base-vocab. -
    Model-idmultilingual-e5-base-vocab
    LicenseMIT
    Sourcehttps://huggingface.co/intfloat/multilingual-e5-base
    LanguageMultilingual
    - - -

    Significance models

    -

    - These are global significance models - that can be added to significance element in services.xml. -

    - - - - - - - - - - - -

    significance-en-wikipedia-v1

    - This significance model was generated from English Wikipedia dump data from 2024-08-01. - Available in Vespa as of version 8.426.8. -
    Model-idsignificance-en-wikipedia-v1
    LicenseCreative Commons Attribution-ShareAlike 3.0 Unported (CC BY-SA 3.0) License.
    Sourcehttps://data.vespa-cloud.com/significance_models/significance-en-wikipedia-v1.json.zst
    LanguageEnglish
    - - - -

    Creating applications working both self-hosted and on Vespa Cloud

    - -

    You can also specify both a model-id, which will be used on Vespa Cloud, -and a url/path, which will be used on self-hosted deployments:

    - -
    {% highlight xml %}
    -
    -{% endhighlight%}
    - -

    This can be useful for example to create an application package which uses models from Vespa Cloud -for production and a scaled-down or dummy model for self-hosted development.

    - - - -

    Using Vespa Cloud models with any config

    - -

    Specifying a model-id can be done for any -config field of type model, -whether the config is from Vespa or defined by you.

    diff --git a/en/rag/model-hub.mdx b/en/rag/model-hub.mdx new file mode 100644 index 0000000000..65ced10a2d --- /dev/null +++ b/en/rag/model-hub.mdx @@ -0,0 +1,922 @@ +--- +title: Using machine-learned models from Vespa Cloud +sidebarTitle: "Model hub" +--- + +Vespa Cloud provides a set of machine-learned models that you can use in your applications. These models will always be available on Vespa Cloud and are [frozen models](https://blog.vespa.ai/tailoring-frozen-embeddings-with-vespa/). You can also bring your own embedding model, by deploying it in the Vespa application package. + +You specify to use a model provided by Vespa Cloud by setting the `model-id` attribute where you specify a model config. For example, when configuring the [Huggingface embedder](/en/rag/embedding#huggingface-embedder) provided by Vespa, you can write: + +```xml + + + + + ... + +``` + +With this, your application will have support for [text embedding](/en/rag/embedding#embedding-a-query-text) inference for both queries and documents. Nodes that have been provisioned with GPU acceleration, will automatically use GPU for embedding inference. + +## Vespa Cloud Embedding Models + +Models on Vespa model hub are selected open-source embedding models with great performance. See the [Vespa blog on embedding tradeoffs](https://blog.vespa.ai/embedding-tradeoffs-quantified/) for details on performance and quality. These embedding models are useful for retrieval (semantic search), re-ranking, clustering, classification, and more. + +### Huggingface Embedder + +These models are available for the Huggingface Embedder `type="hugging-face-embedder"`. All these models support mapping from `string` or `array` to tensor representations. + +The output tensor [cell-precision](/en/performance/feature-tuning#cell-value-types) can be ``, ``, or ``. + +Most models also support [binarization](/en/rag/binarizing-vectors), which requires using [distance-metric](/en/reference/schemas/schemas#distance-metric) `hamming` instead of `angular`. The E5 and multilingual-e5 models do not support binarization. See the [nanobeir hybrid evaluation leaderboard](https://huggingface.co/spaces/vespa-engine/nanobeir-hybrid-evaluation) for details on quality impact. + + +#### alibaba-gte-modernbert + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    GTE (General Text Embedding) model trained from ModernBERT-base.
    Model id{`alibaba-gte-modernbert`}
    Tensor definition{`tensor(x[768])`}
    Matryoshka dimensions{`x[768]`}, {`x[256]`}
    distance-metric{`angular`}
    Licenseapache-2.0
    Sourcehttps://huggingface.co/Alibaba-NLP/gte-modernbert-base @ 3ab3f8c
    LanguageEnglish
    Component declaration
    {`8192cls`}
    + +#### alibaba-gte-modernbert-int8 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    INT8 quantized variant of alibaba-gte-modernbert. Offers faster inference with minimal accuracy loss.
    Model id{`alibaba-gte-modernbert-int8`}
    Tensor definition{`tensor(x[768])`}
    Matryoshka dimensions{`x[768]`}, {`x[256]`}
    distance-metric{`angular`}
    Licenseapache-2.0
    Sourcehttps://huggingface.co/Alibaba-NLP/gte-modernbert-base @ e7f32e3
    LanguageEnglish
    Component declaration
    {`8192cls`}
    + +#### e5-base-v2 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    The base model of the _E5_ family.
    Model id{`e5-base-v2`}
    Tensor definition{`tensor(x[768])`} or {`tensor(p{},x[768])`}
    distance-metric{`angular`}
    LicenseMIT
    Sourcehttps://huggingface.co/intfloat/e5-base-v2 @ 121b23b
    LanguageEnglish
    Component declaration
    {`512query: passage: `}
    + + +#### e5-large-v2 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    The largest model of the _E5_ family, at time of writing, this is the best performing embedding model on the MTEB benchmark.
    Model id{`e5-large-v2`}
    Tensor definition{`tensor(x[1024])`} or {`tensor(p{},x[1024])`}
    distance-metric{`angular`}
    LicenseMIT
    Sourcehttps://huggingface.co/intfloat/e5-large-v2
    LanguageEnglish
    Component declaration
    {`512query: passage: `}
    + +#### e5-small-v2 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    The smallest and most cost-efficient model from the _E5_ family.
    Model id{`e5-small-v2`}
    Tensor definition{`tensor(x[384])`} or {`tensor(p{},x[384])`}
    distance-metric{`angular`}
    LicenseMIT
    Sourcehttps://huggingface.co/intfloat/e5-small-v2
    LanguageEnglish
    Component declaration
    {`512query: passage: `}
    + +#### lightonai-modernbert-large + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Trained from ModernBERT-large on the Nomic Embed datasets, bringing the new advances of ModernBERT to embeddings.
    Model id{`lightonai-modernbert-large`}
    Tensor definition{`tensor(x[1024])`}
    distance-metric{`angular`}
    Licenseapache-2.0
    Sourcehttps://huggingface.co/lightonai/modernbert-embed-large @ b3a781f
    LanguageEnglish
    Component declaration
    {`8192search_query: search_document: `}
    +#### lightonai-modernbert-large-int8 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    INT8 quantized variant of lightonai-modernbert-large. Offers faster inference with minimal accuracy loss.
    Model id{`lightonai-modernbert-large-int8`}
    Tensor definition{`tensor(x[1024])`}
    distance-metric{`angular`}
    Licenseapache-2.0
    Sourcehttps://huggingface.co/lightonai/modernbert-embed-large @ 95a19bf
    LanguageEnglish
    Component declaration
    {`8192search_query: search_document: `}
    + +#### multilingual-e5-base + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    The multilingual model of the _E5_ family. Use this model for multilingual queries and documents.
    Model id{`multilingual-e5-base`}
    Tensor definition{`tensor(x[768])`} or {`tensor(p{},x[768])`}
    distance-metric{`angular`}
    LicenseMIT
    Sourcehttps://huggingface.co/intfloat/multilingual-e5-base
    LanguageMultilingual
    Component declaration
    {`512query: passage: `}
    + + +#### nomic-ai-modernbert + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Trained from ModernBERT-base on the Nomic Embed datasets, bringing the new advances of ModernBERT to embeddings.
    Model id{`nomic-ai-modernbert`}
    Tensor definition{`tensor(x[768])`}
    Matryoshka dimensions{`x[768]`}, {`x[256]`}
    distance-metric{`angular`}
    Licenseapache-2.0
    Sourcehttps://huggingface.co/nomic-ai/modernbert-embed-base @ 92168cb
    LanguageEnglish
    Component declaration
    {`token_embeddings8192search_query: search_document: `}
    + +#### nomic-ai-modernbert-int8 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    INT8 quantized variant of nomic-ai-modernbert. Offers faster inference with minimal accuracy loss.
    Model id{`nomic-ai-modernbert-int8`}
    Tensor definition{`tensor(x[768])`}
    distance-metric{`angular`}
    Licenseapache-2.0
    Sourcehttps://huggingface.co/nomic-ai/modernbert-embed-base @ d556a88
    LanguageEnglish
    Component declaration
    {`token_embeddings8192search_query: search_document: `}
    + +#### snowflake-arctic-embed-m-v2.0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Embedding model based on snowflake-arctic-embed-m-v2.0.
    Model id{`snowflake-arctic-embed-m-v2.0`}
    Tensor definition{`tensor(x[768])`}
    distance-metric{`angular`}
    Licenseapache-2.0
    Sourcehttps://huggingface.co/Snowflake/snowflake-arctic-embed-m-v2.0 @ 95c2741
    LanguageMultilingual
    Component declaration
    {`8192token_embeddingsclstruequery: `}
    + +#### snowflake-arctic-embed-m-v2.0-int8 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    INT8 quantized variant of snowflake-arctic-embed-m-v2.0. Offers faster inference with minimal accuracy loss.
    Model id{`snowflake-arctic-embed-m-v2.0-int8`}
    Tensor definition{`tensor(x[768])`}
    distance-metric{`angular`}
    Licenseapache-2.0
    Sourcehttps://huggingface.co/Snowflake/snowflake-arctic-embed-m-v2.0 @ 95c2741
    LanguageMultilingual
    Component declaration
    {`8192token_embeddingsclstruequery: `}
    + + +#### voyage-4-nano + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Embedding model based on voyage-4-nano-ONNX.
    Model id{`voyage-4-nano`}
    Tensor definition{`tensor(x[2048])`}
    Matryoshka dimensions{`x[2048]`}, {`x[1024]`}, {`x[512]`}, {`x[256]`}
    distance-metric{`angular`}
    Licenseapache-2.0
    Sourcehttps://huggingface.co/thomasht86/voyage-4-nano-ONNX @ fcf290d
    LanguageEnglish
    Component declaration
    {`32768meantrueRepresent the query for retrieving supporting documents: Represent the document for retrieval: `}
    + +#### voyage-4-nano-int8 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    INT8 quantized variant of voyage-4-nano. Offers faster inference with minimal accuracy loss.
    Model id{`voyage-4-nano-int8`}
    Tensor definition{`tensor(x[2048])`}
    Matryoshka dimensions{`x[2048]`}, {`x[1024]`}, {`x[512]`}, {`x[256]`}
    distance-metric{`angular`}
    Licenseapache-2.0
    Sourcehttps://huggingface.co/thomasht86/voyage-4-nano-ONNX @ fcf290d
    LanguageEnglish
    Component declaration
    {`32768meantrueRepresent the query for retrieving supporting documents: Represent the document for retrieval: `}
    + +### Bert Embedder + +These models are available for the [Bert Embedder](/en/rag/embedding#bert-embedder)`type="bert-embedder"`: + +```xml + + + + + + ... + +``` + +Note bert-embedder requires both `transformer-model` and `tokenizer-vocab`. + +#### minilm-l6-v2 + +A small, fast sentence-transformer model. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Model-idminilm-l6-v2
    Tensor definition{`tensor(x[384])`} or {`tensor(p{},x[384])`}
    distance-metric{`angular`}
    Licenseapache-2.0
    Sourcehttps://huggingface.co/sentence-transformers/all-MiniLM-L6-v2
    LanguageEnglish
    +| +#### mpnet-base-v2 + +A larger, but better than **minilm-l6-v2** sentence-transformer model. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Model-idmpnet-base-v2
    Tensor definition{`tensor(x[768])`} or {`tensor(p{},x[768])`}
    distance-metric{`angular`}
    Licenseapache-2.0
    Sourcehttps://huggingface.co/sentence-transformers/all-mpnet-base-v2
    LanguageEnglish
    + +### Tokenization Embedders + +These are embedder implementations that tokenize text and embed string to the vocabulary identifiers. These are most useful for creating the tensor inputs to re-ranking models that take both the query and document token identifiers as input. Find examples in the [sample applications](https://github.com/vespa-engine/sample-apps/blob/master/README.md#vector-search-hybrid-search-and-embeddings). + + +#### bert-base-uncased + + + + + + + + + + + + + + + + + + + + + + + + +
    A vocabulary text (_vocab.txt_) file on the format expected by WordPiece: A text token per line.
    Model-idbert-base-uncased
    Licenseapache-2.0
    Sourcehttps://huggingface.co/sentence-transformers/all-MiniLM-L6-v2
    + +#### e5-base-v2-vocab + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    A _tokenizer.json_ configuration file on the format expected by HF tokenizer. This tokenizer configuration can be used with {`e5-base-v2`}, {`e5-small-v2`} and {`e5-large-v2`}.
    Model-ide5-base-v2-vocab
    LicenseMIT
    Sourcehttps://huggingface.co/intfloat/e5-base-v2
    LanguageEnglish
    +| +#### multilingual-e5-base-vocab + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    A _tokenizer.json_ configuration file on the format expected by HF tokenizer. This tokenizer configuration can be used with {`multilingual-e5-base-vocab`}.
    Model-idmultilingual-e5-base-vocab
    LicenseMIT
    Sourcehttps://huggingface.co/intfloat/multilingual-e5-base
    LanguageMultilingual
    + +### Significance models + +These are [global significance models](/en/ranking/significance#significance-models-in-servicesxml) that can be added to [significance element in services.xml](/en/reference/applications/services/search#significance). + +#### significance-en-wikipedia-v1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    This significance model was generated from English Wikipedia dump data from 2024-08-01. Available in Vespa as of version 8.426.8.
    Model-idsignificance-en-wikipedia-v1
    LicenseCreative Commons Attribution-ShareAlike 3.0 Unported (CC BY-SA 3.0) License.
    Sourcehttps://data.vespa-cloud.com/significance_models/significance-en-wikipedia-v1.json.zst
    LanguageEnglish
    + +## Creating applications working both self-hosted and on Vespa Cloud + +You can also specify both a `model-id`, which will be used on Vespa Cloud, and a url/path, which will be used on self-hosted deployments: + +```bash + +``` + +This can be useful for example to create an application package which uses models from Vespa Cloud for production and a scaled-down or dummy model for self-hosted development. + +## Using Vespa Cloud models with any config + +Specifying a model-id can be done for any [config field of type `model`](/en/applications/configuring-components#adding-files-to-the-component-configuration), whether the config is from Vespa or defined by you. + diff --git a/en/rag/rag.md b/en/rag/rag.mdx similarity index 69% rename from en/rag/rag.md rename to en/rag/rag.mdx index abd2846dc0..6bf676212e 100644 --- a/en/rag/rag.md +++ b/en/rag/rag.mdx @@ -1,20 +1,13 @@ --- -# Copyright Vespa.ai. All rights reserved. title: "Retrieval-augmented generation (RAG) in Vespa" -redirect_from: - - /en/llms-rag +sidebarTitle: "RAG in Vespa" --- -Please refer to [Large Language Models in Vespa](llms-in-vespa) for an +Please refer to [Large Language Models in Vespa](/en/rag/llms-in-vespa) for an introduction to using LLMs in Vespa. -Retrieval-Augmented Generation (RAG) is a technique that merges retrieval -systems with generative models to enhance language model outputs. It works by -first using a retrieval system like Vespa to fetch relevant documents based on -an input query, and then a generative model, like an LLM, to generate more -contextually relevant responses. This method allows language models to access -up-to-date or specific domain knowledge beyond their training, improving -performance in tasks such as question answering and dynamic content creation. +Retrieval-Augmented Generation (RAG) is a technique that merges retrieval systems with generative models to enhance language model outputs. It works by first using a retrieval system like Vespa to fetch relevant documents based on +an input query, and then a generative model, like an LLM, to generate more contextually relevant responses. This method allows language models to access up-to-date or specific domain knowledge beyond their training, improving performance in tasks such as question answering and dynamic content creation. In Vespa, the `RAGSearcher` first performs the query as specified by the user, creates a prompt based on the results, and queries the language model to @@ -35,7 +28,7 @@ In `services.xml`, specify your LLM connection and the `RAGSearcher`: ... - + {/* Configure as required */} @@ -54,10 +47,10 @@ In `services.xml`, specify your LLM connection and the `RAGSearcher`:
    ``` -As mentioned in [LLMs in Vespa](llms-in-vespa.html), you can call this chain +As mentioned in [LLMs in Vespa](/en/rag/llms-in-vespa), you can call this chain using the Vespa CLI: -``` +```bash $ vespa query \ --header="X-LLM-API-KEY:..." \ query="what was the manhattan project?" \ @@ -66,16 +59,16 @@ $ vespa query \ ``` However, notice here the use of the `query` query parameter. In [LLMs in -Vespa](llms-in-vespa.html), we used a `prompt` parameter to set up the prompt +Vespa](/en/rag/llms-in-vespa), we used a `prompt` parameter to set up the prompt to send to the LLM. You can also do that in the `RAGSearcher`, however this means that no actual query is run in Vespa. For Vespa to run a search, you need to specify a `yql` or `query` parameter. By using `query` here, this text is used as both query text for the document retrieval, and in the prompt sent to the LLM, as we will see below. -Indeed, with the `RAGSearcher` you can use any type of [search in Vespa](../querying/query-api.html), -including [text search based on BM25](../learn/tutorials/text-search.html) -and advanced [approximate vector search](../querying/approximate-nn-hnsw). +Indeed, with the `RAGSearcher` you can use any type of [search in Vespa](/en/querying/query-api), +including [text search based on BM25](/en/learn/tutorials/text-search) +and advanced [approximate vector search](/en/querying/approximate-nn-hnsw). This makes the retrieval part of RAG very flexible. ### Controlling the prompt @@ -83,7 +76,7 @@ This makes the retrieval part of RAG very flexible. Based on the query, Vespa will retrieve a set of documents. The `RAGSearcher` will create a context from these documents looking like this: -``` +```text field1: ... field2: ... field3: ... @@ -97,12 +90,12 @@ field3: ... ``` Here, `field1` and so on are the actual fields as returned from the search. For -instance, the [text search tutorial](../learn/tutorials/text-search) defines a +instance, the [text search tutorial](/en/learn/tutorials/text-search) defines a document schema consisting of fields: `id`, `title`, `url`, and `body`. If you only want to include the `title` and `body` fields for use in the context, you can issue a query like this: -``` +```bash $ vespa query \ --header="X-LLM-API-KEY:..." \ yql="select title,body from msmarco where userQuery()" \ @@ -113,21 +106,18 @@ $ vespa query \ The actual prompt that will be sent to the LLM will, by default, look like this: -``` +```text {context} {@prompt or @query} ``` -where `{context}` is as given above, and `@prompt` is replaced with the `prompt` -query parameter if given, and `@query` is replaced with the user query if given. -This means you can customize the actual prompt by passing in a `prompt` -parameter, and thus distinguish between what is searched for in Vespa, and what -is asked for from the LLM. +where `{context}` is as given above, and `@prompt` is replaced with the `prompt` query parameter if given, and `@query` is replaced with the user query if given. +This means you can customize the actual prompt by passing in a `prompt` parameter, and thus distinguish between what is searched for in Vespa, and what is asked for from the LLM. For instance: -``` +```bash $ vespa query \ --header="X-LLM-API-KEY:..." \ yql="select title,body from msmarco where userQuery()" \ @@ -139,7 +129,7 @@ $ vespa query \ will results in a prompt like this: -``` +```text title: body: <body of first document> @@ -151,21 +141,15 @@ body: <body of second document> what was the manhattan project? Be as concise as possible. ``` -Note that if your `prompt` does not contain `{context}`, the context will -automatically be prepended to your prompt. However, if `@query` is not -found in the prompt, it will not automatically be added to the prompt. +Note that if your `prompt` does not contain `{context}`, the context will automatically be prepended to your prompt. However, if `@query` is not found in the prompt, it will not automatically be added to the prompt. -Please be advised that all documents as returned by Vespa will be used in the -context. Most LLMs have some form of limit for how large the prompt can be. LLM -services also typically have a cost per query based on number of tokens both in -input and output. To reduce context size it is important to control the number -of results by using the `hits` [query parameter](../reference/api/query.html#hits). Also, using the query above +Please be advised that all documents as returned by Vespa will be used in the context. Most LLMs have some form of limit for how large the prompt can be. LLM services also typically have a cost per query based on number of tokens both in input and output. To reduce context size it is important to control the number of results by using the `hits` [query parameter](/en/reference/api/query#hits). Also, using the query above limit the fields to only what is strictly required. To debug the prompt, i.e. what is actually sent to the LLM, you can use the `traceLevel` query parameter, and set that to a value larger than `0`: -``` +```bash $ vespa query \ --header="X-LLM-API-KEY:..." \ query="what was the manhattan project?" \ diff --git a/en/rag/working-with-chunks.html b/en/rag/working-with-chunks.html deleted file mode 100644 index 1fb0c06d61..0000000000 --- a/en/rag/working-with-chunks.html +++ /dev/null @@ -1,299 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Working with chunks" -redirect_from: -- /en/working-with-chunks.html ---- - -<p>A key technique in RAG applications, and vector search applications in general, is to split longer text into -chunks. This lets you:</p> - -<ul> -<li>Generate a vector embedding for each chunk rather than for an entire document text, to capture the semantic information -of the text at a meaningful level. -<li>Select specific chunks to add to the context window in GenAI applications rather than the entire document content. -</ul> - -<p>Vespa contains the following functionality for working with chunks. Each is covered in a section below.</p> - -<ul> -<li><a href="#including-chunks-in-documents">Including chunks in documents</a></li> -<li><a href="#creating-vector-embeddings-from-chunks">Creating vector embeddings from chunks</a></li> -<li><a href="#searching-chunks">Searching chunks</a></li> -<li><a href="#ranking-with-chunks">Ranking with chunks</a></li> -<li><a href="#layered-ranking-selecting-chunks-to-return">Layered ranking: Selecting chunks to return</a></li> -</ul> - - -<h2 id="including-chunks-in-documents">Including chunks in documents</h2> - -<p>Chunks that belong to the same text should be added to the same document. -The chunks are represented as arrays of string.</p> - -<p>You can split text into chunks yourself, using a schema like this:</p> - -<pre> -search myDocumentType { - document myDocumentType { - field myChunks type array<string> { - indexing: summary | index - } - } -} -</pre> - -<p>You can then write your chunks into Vespa using this <a href="../reference/schemas/document-json-format.html">document JSON</a>:</p> - -<pre> -"myChunks": ["My first chunk text", "My second chunk text"] -</pre> - -<p>Alternatively you can let Vespa do the chunking for you, by using a synthetic field outside the document:</p> - -<pre> -search myDocumentType { - document myDocumentType { - field myText type string { - } - } - field myChunks type array<string> { - indexing: input myText | chunk fixed-length 500 | summary | index - } -} -</pre> - -<p>In the <a href="../reference/writing/indexing-language.html#chunk">chunk expression</a> -you can choose between chunkers provided by Vespa, or plug in your own, -see the <a href="../reference/rag/chunking.html">chunking reference documentation</a>. - - -<h2 id="creating-vector-embeddings-from-chunks">Creating vector embeddings from chunks</h2> - -<p>To add embeddings to your documents, use a tensor field:</p> - -<pre> -search myDocumentType { - document myDocumentType { - field myEmbedding type tensor<float>(x[384]) { - indexing: attribute | index - } - } -} -</pre> - -<p>This lets you add a single embedding to each document, but usually you want to have many. -In Vespa you can do that by adding <a href="../ranking/tensor-user-guide.html#tensor-concepts">mapped dimensions</a> -to your tensor:</p> - -<pre> -search myDocumentType { - document myDocumentType { - field myEmbeddings type tensor<float>(chunk{}, x[384]) { - indexing: attribute | index - } - } -} -</pre> - -<p>With this you can feed <a href="../reference/schemas/document-json-format.html#tensor-short-form-mixed">tensors in JSON format</a> -as part of your writes, e.g. writing an embedding tensor with chunks numbered 1 and 2:</p> - -<pre>{% highlight json %} -"myEmbeddings": { - "1":[2.0,3.0,...], - "2":[4.0,5.0,...] -} -{% endhighlight %}</pre> - -<p>You may notice that parsing such JSON consumes a lot of CPU on container clusters. To avoid that you can also feed -embeddings <a href="../reference/schemas/document-json-format.html#tensor-hex-dump">hex encoded raw data</a>.</p> - -<p>You can also let Vespa do the embedding for you, either using a model provided by Vespa, -or one you decide in your application package: -</p> - -<pre> -search myDocumentType { - document myDocumentType { - field myChunks type array<string> { - } - } - field myEmbeddings type tensor<float>(chunk{}, x[384]) { - indexing: input myChunks | embed | attribute | index - } -} -</pre> - -<p>See the <a href="embedding.html">embedding guide</a> on how to configure embedders.</p> - -<p>You can of course combine this with chunking to have a single text field -chunked and embedded automatically:</p> - -<pre> -search myDocumentType { - document myDocumentType { - field myText type string { - } - } - field myChunks type array<string> { - indexing: input myText | chunk sentence | summary | index - } - field myEmbeddings type tensor<float>(chunk{}, x[384]) { - indexing: input myText | chunk sentence | embed | attribute | index - } -} -</pre> - -<p>Some things to note:</p> - -<ul> - <li>All fields of Vespa documents are stored and here we represent the text both as a single field and as - chunks of text, won't that consume a lot of unnecessary space? No, thanks to the wonders of modern compression, - the overhead from this can be ignored. - <li>Why return the chunk array in results and not the full text field? This is because for large text we need to - select a subset of the text chunks rather than returning the full text. - <li>We are chunking twice here, won't this be inefficient? No, Vespa will reuse the result of the first invocation - in cases like this. -</ul> - - -<h2 id="searching-chunks">Searching chunks</h2> - -<p>You can search in chunk text (if you added <code>index</code>), and in chunk embeddings (if you created embeddings). -Usually, you want to do both (<a href="../learn/tutorials/hybrid-search">hybrid search</a>) -since text search gives you precise matches, and embedding nearest neighbor search gives you imprecise semantic matching.</p> - -<p>A simple hybrid query can look like this:</p> - -<pre> -yql=select * from doc where myField contains text(@query) or ({totalTargetHits:10}nearestNeighbor(myEmbeddings, e)) -input.query(e)=embed(@query) -query=Do Cholesterol Statin Drugs Cause Breast Cancer? -</pre> - -<p>The <code>embed</code> function shown here can be used to embed a query text using the same model(s) as used for chunks. -If embedding outside Vespa you can -<a href="https://docs.vespa.ai/en/reference/tensor.html#tensor-literal-form">pass the tensor value</a> instead. See the -<a href="../querying/nearest-neighbor-search-guide#hybrid-sparse-and-dense-retrieval-methods-with-vespa">nearest neighbor guide</a> -for more.</p> - -<p>Text matching works across chunks as if the chunks were re-joined into one text field. However, a proximity -gap is inserted between each chunks so that tokens in different chunks are by default very (infinitely) far away when -evaluating phrase and near matches (however, see -<a href="../reference/schemas/schemas.html#rank-element-gap">on configuring this</a>).</p> - -<p>Nearest neighbor search with many chunks will retrieve the documents where any single chunk embedding -is close to the query embedding.</p> - - -<h2 id="ranking-with-chunks">Ranking with chunks</h2> - -<p>Ranking in Vespa is done by <a href="../ranking/ranking-expressions-features.html">mathematical expressions</a> -(hand-written or machine-learned) combining rank features. You'll typically want to use features that capture -both how well vector embeddings and textual query terms matched the chunks.</p> - -<p>For vector search, the <code>closeness(dimension,field)</code> feature will contain the distance between the -query vector and the <i>closest</i> chunk embedding. In addition, the <code>closest(field)</code> -feature will return a tensor providing the label(s) of the chunk which was closest.</p> - -<p>For text matching, all features are available as if the entire chunk array was a single string field, but with -an infinitely large proximity gap between each element to treat each element as independent. When the array elements -are chunks of the same text, you'd prefer to get a relevance contribution from matching adjacent elements since it means -you are matching adjacent words in the source text. To achieve this, configure the elementGap in your chunk array to -a low value (e.g. 0 to 3, depending on how well your chunking strategy identifies semantic transitions):</p> - -<pre> - rank-profile myProfile { - rank myChunks { - element-gap: 1 - } - } -</pre> - -<p>Using vector closeness and the normal text match features will help you rank documents mostly based -on the text having the single best match to the query. Sometimes it is also useful to capture how well the -text as a whole matches the query. For vectors, you can do this by computing and aggregating closeness -to each vector using a <a href="../ranking/tensor-user-guide.html#ranking-with-tensors">tensor expression</a> -in your ranking expression, while for text matching you can use the <code>elementSimilarity(field)</code> feature, -or the <code><a href="../reference/ranking/rank-features.html#elementwise-bm25">elementwise(bm25(field),dimension,cell_type)</a></code> -feature which returns a tensor containing the bm25 score of each chunk.</p> - - -<h2 id="layered-ranking-selecting-chunks-to-return">Layered ranking: Selecting chunks to return</h2> - -<p>A search result will contain the top ranked documents including all fields you are requesting or -<a href="../querying/document-summaries.html#">configuring</a>, including all chunks of those documents, -whether relevant or not. This is fine when every document has few chunks, but when they can have many, there -are two problems:</p> -<ul> - <li>Putting many irrelevant chunks into the context window of the LLM decreases quality, or may - make the context window infeasibly large.</li> - <li>Sending many chunks over the network increases latency and can impacting other queries - running at the same time.</li> -</ul> - -<p>To solve both of these, we can use -<a href="https://blog.vespa.ai/introducing-layered-ranking-for-rag-applications/">layered ranking</a>: -Rank the chunks in the highest ranked documents, and select only the best ones.</p> - -<p>To do this, specify the ranking function that will select the chunks to return, -using <code><a href="../reference/schemas/schemas.html#select-elements-by">select-elements-by</a></code>. -Here's a full example:</p> - -<pre> -schema docs { - document docs { - - field myEmbeddings type tensor<float>(chunk{}, x[386]) { - indexing: attribute - } - - field myChunks type array<string> { - indexing: index | summary - summary { - select-elements-by: best_chunks - } - } - - } - - rank-profile default { - - inputs { - query(embedding) tensor<float>(x[386]) - } - - function my_distance() { - expression: euclidean_distance(query(embedding), attribute(myEmbeddings), x) - } - - function my_distance_scores() { - expression: 1 / (1+my_distance) - } - - function my_text_scores() { - expression: elementwise(bm25(myChunks), chunk, float) - } - - function chunk_scores() { - expression: merge(my_distance_scores, my_text_scores, f(a,b)(a+b)) - } - - function best_chunks() { - expression: top(3, chunk_scores) - } - - first-phase { - expression: sum(chunk_scores()) - } - - summary-features { - best_chunks - } - - } -} -</pre> - -<p>With this, we can use the powerful ranking framework in Vespa to select the best chunks to provide to the LLM, -without sending any chunks that won't be used over the network.</p> diff --git a/en/rag/working-with-chunks.mdx b/en/rag/working-with-chunks.mdx new file mode 100644 index 0000000000..6c0a83c491 --- /dev/null +++ b/en/rag/working-with-chunks.mdx @@ -0,0 +1,236 @@ +--- +title: "Working with chunks" +--- + +A key technique in RAG applications, and vector search applications in general, is to split longer text into chunks. This lets you: + +- Generate a vector embedding for each chunk rather than for an entire document text, to capture the semantic information of the text at a meaningful level. +- Select specific chunks to add to the context window in GenAI applications rather than the entire document content. + +Vespa contains the following functionality for working with chunks. Each is covered in a section below. + +<CardGroup> +<Card title="Including chunks in documents" icon="file" href="#including-chunks-in-documents" horizontal/> +<Card title="Creating vector embeddings from chunks" icon="file" href="#creating-vector-embeddings-from-chunks" horizontal/> +<Card title="Searching chunks" icon="file" href="#searching-chunks" horizontal/> +<Card title="Ranking with chunks" icon="file" href="#ranking-with-chunks" horizontal/> +<Card title="Layered ranking: Selecting chunks to return" icon="file" href="#layered-ranking-selecting-chunks-to-return" horizontal/> +</CardGroup> + +## Including chunks in documents + +Chunks that belong to the same text should be added to the same document. The chunks are represented as arrays of string. + +You can split text into chunks yourself, using a schema like this: + +```js +search myDocumentType { + document myDocumentType { + field myChunks type array<string> { + indexing: summary | index + } + } +} +``` + +You can then write your chunks into Vespa using this [document JSON](../reference/schemas/document-json-format.html): + +```json +"myChunks": ["My first chunk text", "My second chunk text"] +``` + +Alternatively you can let Vespa do the chunking for you, by using a synthetic field outside the document: + +```js +search myDocumentType { + document myDocumentType { + field myText type string { + } + } + field myChunks type array<string> { + indexing: input myText | chunk fixed-length 500 | summary | index + } +} +``` + +In the [chunk expression](/en/reference/writing/indexing-language#chunk) you can choose between chunkers provided by Vespa, or plug in your own, see the [chunking reference documentation](/en/reference/rag/chunking). + +## Creating vector embeddings from chunks + +To add embeddings to your documents, use a tensor field: + +```js +search myDocumentType { + document myDocumentType { + field myEmbedding type tensor<float>(x[384]) { + indexing: attribute | index + } + } +} +``` + +This lets you add a single embedding to each document, but usually you want to have many. In Vespa you can do that by adding [mapped dimensions](/en/ranking/tensor-user-guide#tensor-concepts) to your tensor: + +```js +search myDocumentType { + document myDocumentType { + field myEmbeddings type tensor<float>(chunk{}, x[384]) { + indexing: attribute | index + } + } +} +``` + +With this you can feed [tensors in JSON format](../reference/schemas/document-json-format#tensor-short-form-mixed) as part of your writes, e.g. writing an embedding tensor with chunks numbered 1 and 2: + +```js +"myEmbeddings": { + "1":[2.0,3.0,...], + "2":[4.0,5.0,...] +} +``` + +You may notice that parsing such JSON consumes a lot of CPU on container clusters. To avoid that you can also feed embeddings [hex encoded raw data](/en/reference/schemas/document-json-format#tensor-hex-dump). + +You can also let Vespa do the embedding for you, either using a model provided by Vespa, or one you decide in your application package: + +```js +search myDocumentType { + document myDocumentType { + field myChunks type array<string> { + } + } + field myEmbeddings type tensor<float>(chunk{}, x[384]) { + indexing: input myChunks | embed | attribute | index + } +} +``` + +See the [embedding guide](/en/rag/embedding) on how to configure embedders. + +You can of course combine this with chunking to have a single text field chunked and embedded automatically: + +```js +search myDocumentType { + document myDocumentType { + field myText type string { + } + } + field myChunks type array<string> { + indexing: input myText | chunk sentence | summary | index + } + field myEmbeddings type tensor<float>(chunk{}, x[384]) { + indexing: input myText | chunk sentence | embed | attribute | index + } +} +``` + +Some things to note: + +- All fields of Vespa documents are stored and here we represent the text both as a single field and as chunks of text, won't that consume a lot of unnecessary space? No, thanks to the wonders of modern compression, the overhead from this can be ignored. +- Why return the chunk array in results and not the full text field? This is because for large text we need to select a subset of the text chunks rather than returning the full text. +- We are chunking twice here, won't this be inefficient? No, Vespa will reuse the result of the first invocation in cases like this. + +## Searching chunks + +You can search in chunk text (if you added `index`), and in chunk embeddings (if you created embeddings). Usually, you want to do both ([hybrid search](/en/learn/tutorials/hybrid-search)) since text search gives you precise matches, and embedding nearest neighbor search gives you imprecise semantic matching. + +A simple hybrid query can look like this: + +```bash +yql=select * from doc where myField contains text(@query) or ({totalTargetHits:10}nearestNeighbor(myEmbeddings, e)) +input.query(e)=embed(@query) +query=Do Cholesterol Statin Drugs Cause Breast Cancer? +``` + +The `embed` function shown here can be used to embed a query text using the same model(s) as used for chunks. If embedding outside Vespa you can [pass the tensor value](/en/reference/ranking/tensor#tensor-literal-form) instead. See the [nearest neighbor guide](/en/querying/nearest-neighbor-search-guide#hybrid-sparse-and-dense-retrieval-methods-with-vespa) for more. + +Text matching works across chunks as if the chunks were re-joined into one text field. However, a proximity gap is inserted between each chunks so that tokens in different chunks are by default very (infinitely) far away when evaluating phrase and near matches (however, see [on configuring this](/en/reference/schemas/schemas#rank-element-gap)). + +Nearest neighbor search with many chunks will retrieve the documents where any single chunk embedding is close to the query embedding. + +## Ranking with chunks + +Ranking in Vespa is done by [mathematical expressions](/en/ranking/ranking-expressions-features) (hand-written or machine-learned) combining rank features. You'll typically want to use features that capture both how well vector embeddings and textual query terms matched the chunks. + +For vector search, the `closeness(dimension,field)` feature will contain the distance between the query vector and the _closest_ chunk embedding. In addition, the `closest(field)` feature will return a tensor providing the label(s) of the chunk which was closest. + +For text matching, all features are available as if the entire chunk array was a single string field, but with an infinitely large proximity gap between each element to treat each element as independent. When the array elements are chunks of the same text, you'd prefer to get a relevance contribution from matching adjacent elements since it means you are matching adjacent words in the source text. To achieve this, configure the elementGap in your chunk array to a low value (e.g. 0 to 3, depending on how well your chunking strategy identifies semantic transitions): + +```js +rank-profile myProfile { + rank myChunks { + element-gap: 1 + } + } +``` + +Using vector closeness and the normal text match features will help you rank documents mostly based on the text having the single best match to the query. Sometimes it is also useful to capture how well the text as a whole matches the query. For vectors, you can do this by computing and aggregating closeness to each vector using a [tensor expression](/en/ranking/tensor-user-guide#ranking-with-tensors) in your ranking expression, while for text matching you can use the `elementSimilarity(field)` feature, or the [elementwise(bm25(field),dimension,cell_type)](/en/reference/ranking/rank-features#elementwise-bm25) feature which returns a tensor containing the bm25 score of each chunk. + +## Layered ranking: Selecting chunks to return + +A search result will contain the top ranked documents including all fields you are requesting or [configuring](/en/querying/document-summaries), including all chunks of those documents, whether relevant or not. This is fine when every document has few chunks, but when they can have many, there are two problems: + +- Putting many irrelevant chunks into the context window of the LLM decreases quality, or may make the context window infeasibly large. +- Sending many chunks over the network increases latency and can impacting other queries running at the same time. + +To solve both of these, we can use [layered ranking](https://blog.vespa.ai/introducing-layered-ranking-for-rag-applications/): Rank the chunks in the highest ranked documents, and select only the best ones. + +To do this, specify the ranking function that will select the chunks to return, using `select-elements-by`. Here's a full example: + +```js expandable +schema docs { + document docs { + + field myEmbeddings type tensor<float>(chunk{}, x[386]) { + indexing: attribute + } + + field myChunks type array<string> { + indexing: index | summary + summary { + select-elements-by: best_chunks + } + } + + } + + rank-profile default { + + inputs { + query(embedding) tensor<float>(x[386]) + } + + function my_distance() { + expression: euclidean_distance(query(embedding), attribute(myEmbeddings), x) + } + + function my_distance_scores() { + expression: 1 / (1+my_distance) + } + + function my_text_scores() { + expression: elementwise(bm25(myChunks), chunk, float) + } + + function chunk_scores() { + expression: merge(my_distance_scores, my_text_scores, f(a,b)(a+b)) + } + + function best_chunks() { + expression: top(3, chunk_scores) + } + + first-phase { + expression: sum(chunk_scores()) + } + + summary-features { + best_chunks + } + + } +} +``` + +With this, we can use the powerful ranking framework in Vespa to select the best chunks to provide to the LLM, without sending any chunks that won't be used over the network. diff --git a/en/ranking/bm25.html b/en/ranking/bm25.html deleted file mode 100644 index aa4d28ccd0..0000000000 --- a/en/ranking/bm25.html +++ /dev/null @@ -1,377 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "The BM25 rank feature" -redirect_from: -- /en/reference/bm25.html ---- - -<p> -The -<a href="../reference/ranking/rank-features.html#bm25">bm25 rank feature</a> -implements the -<a href="https://en.wikipedia.org/wiki/Okapi_BM25">Okapi BM25</a> -ranking function used to estimate the relevance of a text document given a search query. -It is a pure text ranking feature which operates over an -<a href="../reference/schemas/schemas.html#indexing-index">indexed string field</a>. -The feature is cheap to compute, about 3-4 times faster than -<a href="nativerank.html">nativeRank</a>, -while still providing a good rank score quality wise. -It is a good candidate to use in a first phase ranking function when ranking text documents. -</p> - - -<h2 id="ranking-function">Ranking function</h2> -<p> -The <em>bm25</em> feature calculates a score for how good a query with terms -<math xmlns="http://www.w3.org/1998/Math/MathML" display="inline"> - <semantics> - <mrow> - <msub> - <mi>q</mi> - <mi>1</mi> - </msub> - <mo>,</mo> - <mo>.</mo> - <mo>.</mo> - <mo>.</mo> - <mo>,</mo> - <msub> - <mi>q</mi> - <mi>n</mi> - </msub> - </mrow> - </semantics> -</math> -matches an indexed string field <em>t</em> in a document <em>D</em>. -The score is calculated as follows: -</p> - -<figure style="font-size: 1.2rem"> - <div> - <math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> - <semantics> - <mrow> - <munderover> - <mo>∑</mo> - <mi>i</mi> - <mi>n</mi> - </munderover> - <mi>I</mi> - <mi>D</mi> - <mi>F</mi> - <mrow> - <mo>(</mo> - <msub> - <mi>q</mi> - <mi>i</mi> - </msub> - <mo>)</mo> - </mrow> - <mo>⋅<!--MULTIPLICATION SIGN--></mo> - <mfrac> - <mrow> - <mi>f</mi> - <mo>(</mo> - <msub> - <mi>q</mi> - <mi>i</mi> - </msub> - <mo>,</mo> - <mi>D</mi> - <mo>)</mo> - <mo>⋅<!--MULTIPLICATION SIGN--></mo> - <mo>(</mo> - <msub> - <mi>k</mi> - <mn>1</mn> - </msub> - <mo>+</mo> - <mn>1</mn> - <mo>)</mo> - </mrow> - <mrow> - <mi>f</mi> - <mo stretchy="false">(</mo> - <msub> - <mi>q</mi> - <mi>i</mi> - </msub> - <mo>,</mo> - <mi>D</mi> - <mo stretchy="false">)</mo> - <mo>+</mo> - <msub> - <mi>k</mi> - <mn>1</mn> - </msub> - <mo>⋅<!--MULTIPLICATION SIGN--></mo> - <mo>(</mo> - <mn>1</mn> - <mo>-</mo> - <mi>b</mi> - <mo>+</mo> - <mi>b</mi> - <mo>⋅<!--MULTIPLICATION SIGN--></mo> - <mfrac> - <mrow> - <mi>f</mi> - <mi>i</mi> - <mi>e</mi> - <mi>l</mi> - <mi>d</mi> - <mi>_</mi> - <mi>l</mi> - <mi>e</mi> - <mi>n</mi> - </mrow> - <mrow> - <mi>a</mi> - <mi>v</mi> - <mi>g</mi> - <mi>_</mi> - <mi>f</mi> - <mi>i</mi> - <mi>e</mi> - <mi>l</mi> - <mi>d</mi> - <mi>_</mi> - <mi>l</mi> - <mi>e</mi> - <mi>n</mi> - </mrow> - </mfrac> - <mo>)</mo> - </mrow> - </mfrac> - </mrow> - </semantics> - </math> - </div> -</figure> -<p>Where the components in the function are:</p> -<ul> - <li> - <p> - <math xmlns="http://www.w3.org/1998/Math/MathML" display="inline"> - <semantics> - <mrow> - <mi>I</mi> - <mi>D</mi> - <mi>F</mi> - <mo>(</mo> - <msub> - <mi>q</mi> - <mi>i</mi> - </msub> - <mo>)</mo> - </mrow> - </semantics> - </math>: The - <a href="https://en.wikipedia.org/wiki/Tf%E2%80%93idf#Inverse_document_frequency">inverse document frequency</a> - (<em>IDF</em>) of query term <em>i</em> in field <em>t</em>. This is calculated as: - </p> - <figure style="font-size: 1.2rem"> - <div> - <math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> - <semantics> - <mrow> - <mi>l</mi> - <mi>o</mi> - <mi>g</mi> - <mo>(</mo> - <mn>1</mn> - <mo>+</mo> - <mfrac> - <mrow> - <mi>N</mi> - <mo>-</mo> - <mi>n</mi> - <mo>(</mo> - <msub> - <mi>q</mi> - <mi>i</mi> - </msub> - <mo>)</mo> - <mo>+</mo> - <mn>0.5</mn> - </mrow> - <mrow> - <mi>n</mi> - <mo>(</mo> - <msub> - <mi>q</mi> - <mi>i</mi> - </msub> - <mo>)</mo> - <mo>+</mo> - <mn>0.5</mn> - </mrow> - </mfrac> - <mo>)</mo> - </mrow> - </semantics> - </math> - </div> - </figure> - <p> - <em>N</em> is the total number of documents on the content node. - <math xmlns="http://www.w3.org/1998/Math/MathML" display="inline"> - <semantics> - <mrow> - <mi>n</mi> - <mo>(</mo> - <msub> - <mi>q</mi> - <mi>i</mi> - </msub> - <mo>)</mo> - </mrow> - </semantics> - </math> - is the number of documents containing query term <em>i</em> for field <em>t</em>, - which is calculated per index existing for that field. - The max value among the indexes is used in the calculation, - which typically comes from the largest <a href="../content/proton.html#index">disk index</a>. - </p> - <p> - As the <em>IDF</em> is calculated per content node and index, slight variations might occur. - To use the same <em>IDF</em> across all content nodes, set it as the <em>significance</em> - on each query term using <a href="../reference/querying/yql.html#annotations">annotations</a>. - </p> - </li> - <li> - <math xmlns="http://www.w3.org/1998/Math/MathML" display="inline"> - <semantics> - <mrow> - <mi>f</mi> - <mo>(</mo> - <msub> - <mi>q</mi> - <mi>i</mi> - </msub> - <mo>,</mo> - <mi>D</mi> - <mo>)</mo> - </mrow> - </semantics> - </math>: The number of occurrences (term frequency) of query term <em>i</em> - in the field <em>t</em> of document <em>D</em>. - For multi-value fields we use the sum of occurrences over all elements. - </li> - <li> - <math xmlns="http://www.w3.org/1998/Math/MathML" display="inline"> - <semantics> - <mrow> - <mi>f</mi> - <mi>i</mi> - <mi>e</mi> - <mi>l</mi> - <mi>d</mi> - <mi>_</mi> - <mi>l</mi> - <mi>e</mi> - <mi>n</mi> - </mrow> - </semantics> - </math>: The field length (in number of words) of field <em>t</em> in document <em>D</em>. - For multi-value fields we use the sum of field lengths over all elements. - </li> - <li> - <math xmlns="http://www.w3.org/1998/Math/MathML" display="inline"> - <semantics> - <mrow> - <mi>a</mi> - <mi>v</mi> - <mi>g</mi> - <mi>_</mi> - <mi>f</mi> - <mi>i</mi> - <mi>e</mi> - <mi>l</mi> - <mi>d</mi> - <mi>_</mi> - <mi>l</mi> - <mi>e</mi> - <mi>n</mi> - </mrow> - </semantics> - </math>: The average field length of field <em>t</em> among the documents on the content node. - Can be configured using - <a href="../reference/ranking/rank-feature-configuration.html#bm25">rank-properties</a>. - The actual index statistic is also available as the - <a href="../reference/ranking/rank-features.html#averageFieldLength(name)">averageFieldLength(name)</a> - rank feature. - </li> - <li> - <math xmlns="http://www.w3.org/1998/Math/MathML" display="inline"> - <semantics> - <mrow> - <msub> - <mi>k</mi> - <mn>1</mn> - </msub> - </mrow> - </semantics> - </math>: A parameter used to limit how much a single query term can affect the score for document <em>D</em>. - With a higher value the score for a single term can continue to go up relatively more - when more occurrences for that term exists. Default value is 1.2. - Can be configured using - <a href="../reference/ranking/rank-feature-configuration.html#bm25">rank-properties</a>. - </li> - <li> - <math xmlns="http://www.w3.org/1998/Math/MathML" display="inline"> - <semantics> - <mrow> - <mi>b</mi> - </mrow> - </semantics> - </math>: A parameter used to control the effect of the field length of field <em>t</em> - compared to the average field length. Default value is 0.75. - Can be configured using - <a href="../reference/ranking/rank-feature-configuration.html#bm25">rank-properties</a>. - </li> -</ul> - - -<h2 id="example">Example</h2> -<p> -In the following example we have an indexed string field <em>content</em>, -and a rank profile using the <em>bm25</em> rank feature. -Note that the field must be enabled for usage with the bm25 feature -by setting the <em>enable-bm25</em> flag in the -<a href="../reference/schemas/schemas.html#index">index</a> -section of the field definition. -</p> -<pre> -schema example { - document example { - field content type string { - indexing: index | summary - index: enable-bm25 - } - } - rank-profile default { - first-phase { - expression { - bm25(content) - } - } - } -} -</pre> -<p> -If the <em>enable-bm25</em> flag is turned on after documents are -already fed then <a href="../content/proton.html">proton</a> performs -a <a href="../content/proton.html#memory-index-flush">memory index flush</a> -followed by a <a href="../content/proton.html#disk-index-fusion">disk index -fusion</a> to prepare the posting lists for use with <em>bm25</em>. -</p> -<p> -Use the <a href="../content/proton.html#custom-component-state-api">custom -component state API</a> on each content node and -examine <code>pending_urgent_flush</code> to determine if the -preparation is still ongoing: -</p> -<pre> -/state/v1/custom/component/documentdb/mydoctype/subdb/ready/index -</pre> diff --git a/en/ranking/bm25.mdx b/en/ranking/bm25.mdx new file mode 100644 index 0000000000..d78258e5f4 --- /dev/null +++ b/en/ranking/bm25.mdx @@ -0,0 +1,57 @@ +--- +title: "The BuM25 rank featre" +--- + +The [bm25 rank feature](/en/reference/ranking/rank-features#bm25) implements the [Okapi BM25](https://en.wikipedia.org/wiki/Okapi_BM25) ranking function used to estimate the relevance of a text document given a search query. It is a pure text ranking feature which operates over an [indexed string field](/en/reference/schemas/schemas#indexing-index). The feature is cheap to compute, about 3-4 times faster than [nativeRank](/en/ranking/nativerank), while still providing a good rank score quality wise. It is a good candidate to use in a first phase ranking function when ranking text documents. + +## Ranking function + +The *bm25* feature calculates a score for how good a query with terms $q_{1} , . . . , q_{n}$ matches an indexed string field *t* in a document *D*. The score is calculated as follows: + +$$ +\sum_{i}^{n} I D F \left(\right. q_{i} \left.\right) \cdot \frac{f \left(\right. q_{i} , D \left.\right) \cdot \left(\right. k_{1} + 1 \left.\right)}{f \left(\right. q_{i} , D \left.\right) + k_{1} \cdot \left(\right. 1 - b + b \cdot \frac{f i e l d _ l e n}{a v g _ f i e l d _ l e n} \left.\right)} +$$ + +Where the components in the function are: + +- $I D F \left(\right. q_{i} \left.\right)$: The [inverse document frequency](https://en.wikipedia.org/wiki/Tf%E2%80%93idf#Inverse_document_frequency) (*IDF*) of query term *i* in field *t*. This is calculated as: + $$ + l o g \left(\right. 1 + \frac{N - n \left(\right. q_{i} \left.\right) + 0.5}{n \left(\right. q_{i} \left.\right) + 0.5} \left.\right) + $$ + *N* is the total number of documents on the content node. $n \left(\right. q_{i} \left.\right)$ is the number of documents containing query term *i* for field *t*, which is calculated per index existing for that field. The max value among the indexes is used in the calculation, which typically comes from the largest [disk index](/en/content/proton#index). + As the *IDF* is calculated per content node and index, slight variations might occur. To use the same *IDF* across all content nodes, set it as the *significance* on each query term using [annotations](/en/reference/querying/yql#annotations). +- $f \left(\right. q_{i} , D \left.\right)$: The number of occurrences (term frequency) of query term *i* in the field *t* of document *D*. For multi-value fields we use the sum of occurrences over all elements. +- f i e l d __ l e n : The field length (in number of words) of field *t* in document *D*. For multi-value fields we use the sum of field lengths over all elements. +- a v g _ f i e l d _ l e n: The average field length of field *t* among the documents on the content node. Can be configured using [rank-properties](/en/reference/ranking/rank-feature-configuration#bm25). +- $k_{1}$: A parameter used to limit how much a single query term can affect the score for document *D*. With a higher value the score for a single term can continue to go up relatively more when more occurrences for that term exists. Default value is 1.2. Can be configured using [rank-properties](/en/reference/ranking/rank-feature-configuration#bm25). +- $b$: A parameter used to control the effect of the field length of field *t* compared to the average field length. Default value is 0.75. Can be configured using [rank-properties](/en/reference/ranking/rank-feature-configuration#bm25). + +## Example + +In the following example we have an indexed string field *content*, and a rank profile using the *bm25* rank feature. Note that the field must be enabled for usage with the bm25 feature by setting the *enable-bm25* flag in the [index](/en/reference/schemas/schemas#index) section of the field definition. + +```js +schema example { + document example { + field content type string { + indexing: index | summary + index: enable-bm25 + } + } + rank-profile default { + first-phase { + expression { + bm25(content) + } + } + } +} +``` + +If the *enable-bm25* flag is turned on after documents are already fed then [proton](/en/content/proton) performs a [memory index flush](/en/content/proton#memory-index-flush) followed by a [disk index fusion](/en/content/proton#disk-index-fusion) to prepare the posting lists for use with *bm25*. + +Use the [custom component state API](/en/content/proton#custom-component-state-api) on each content node and examine `pending_urgent_flush` to determine if the preparation is still ongoing: + +```bash +/state/v1/custom/component/documentdb/mydoctype/subdb/ready/index +``` \ No newline at end of file diff --git a/en/ranking/cross-encoders.md b/en/ranking/cross-encoders.mdx similarity index 50% rename from en/ranking/cross-encoders.md rename to en/ranking/cross-encoders.mdx index b776e08fe7..3cbda39e55 100644 --- a/en/ranking/cross-encoders.md +++ b/en/ranking/cross-encoders.mdx @@ -1,114 +1,109 @@ --- -# Copyright Vespa.ai. All rights reserved. title: "Ranking With Transformer Cross-Encoder Models" -redirect_from: - - /en/cross-encoders +sidebarTitle: "Cross-encoder transformer ranking" --- -[Cross-Encoder Transformer](https://blog.vespa.ai/pretrained-transformer-language-models-for-search-part-4/) -based text ranking models are generally more effective than [text embedding](../rag/embedding.html) models -as they take both the query and the document as input with full cross-attention between all the query and document tokens. +[Cross-Encoder Transformer](https://blog.vespa.ai/pretrained-transformer-language-models-for-search-part-4/) based text ranking models are generally more effective than [text embedding](/en/rag/embedding) models as they take both the query and the document as input with full cross-attention between all the query and document tokens. -The downside of cross-encoder models is the computational complexity. This document is a guide -on how to export cross-encoder Transformer based models from [huggingface](https://huggingface.co/), -and how to configure them for use in Vespa. +The downside of cross-encoder models is the computational complexity. This document is a guide on how to export cross-encoder Transformer based models from [huggingface](https://huggingface.co/), and how to configure them for use in Vespa. ## Exporting cross-encoder models -For exporting models from HF to [ONNX](onnx), we recommend the [Optimum](https://huggingface.co/docs/optimum/main/en/index) -library. Example usage for two relevant ranking models. +For exporting models from HF to [ONNX](/en/ranking/onnx), we recommend the [Optimum](https://huggingface.co/docs/optimum/main/en/index) library. Example usage for two relevant ranking models. -Export [intfloat/simlm-msmarco-reranker](https://huggingface.co/intfloat/simlm-msmarco-reranker), which is -a BERT-based transformer model for English texts: +Export [intfloat/simlm-msmarco-reranker](https://huggingface.co/intfloat/simlm-msmarco-reranker), which is a BERT-based transformer model for English texts: -<pre> + +```bash $ optimum-cli export onnx --task text-classification -m intfloat/simlm-msmarco-reranker ranker -</pre> +``` + -Export [BAAI/bge-reranker-base](https://huggingface.co/BAAI/bge-reranker-base), which is -a ROBERTA-based transformer model for English and Chinese texts (multilingual): +Export [BAAI/bge-reranker-base](https://huggingface.co/BAAI/bge-reranker-base), which is a ROBERTA-based transformer model for English and Chinese texts (multilingual): -<pre> + +```bash $ optimum-cli export onnx --task text-classification -m BAAI/bge-reranker-base ranker -</pre> +``` + -These two example ranking models use different -language model [tokenization](../reference/rag/embedding.html#huggingface-tokenizer-embedder) and also -different transformer inputs. +These two example ranking models use different language model [tokenization](/en/reference/rag/embedding#huggingface-tokenizer-embedder) and also different transformer inputs. -After the above Optimum export command you have two important -files that is needed for importing the model to Vespa: +After the above Optimum export command you have two important files that is needed for importing the model to Vespa: -<pre> + +```bash ├── ranker │   └── model.onnx └── tokenizer.json -</pre> +``` + -The Optimum tool also supports various Transformer optimizations, including quantization to -optimize the model for faster inference. +The Optimum tool also supports various Transformer optimizations, including quantization to optimize the model for faster inference. ## Importing ONNX and tokenizer model files to Vespa -Add the generated `model.onnx` and `tokenizer.json` files from the `ranker` directory -created by Optimum to the Vespa [application package](../basics/applications.html): +Add the generated `model.onnx` and `tokenizer.json` files from the `ranker` directory created by Optimum to the Vespa [application package](/en/basics/applications): -<pre> + +```bash ├── models │   └── model.onnx └── tokenizer.json ├── schemas │   └── doc.sd └── services.xml -</pre> +``` + ## Configure tokenizer embedder -To speed up inference, Vespa avoids re-tokenizing the document tokens, so we need to configure the -[huggingface-tokenizer-embedder](../reference/rag/embedding.html#huggingface-tokenizer-embedder) -in the `services.xml` file: +To speed up inference, Vespa avoids re-tokenizing the document tokens, so we need to configure the [huggingface-tokenizer-embedder](/en/reference/rag/embedding#huggingface-tokenizer-embedder) in the `services.xml` file: -<pre> -<container id="default" version="1.0"> + +```xml +<container id="default" version="1.0"> .. - <component id="tokenizer" type="hugging-face-tokenizer"> - <model path="models/tokenizer.json"> - </component> + <component id="tokenizer" type="hugging-face-tokenizer"> + <model path="models/tokenizer.json"> + </component> .. -</container> -</pre> +</container> +``` + + +This allows us to use the tokenizer while indexing documents in Vespa and also at query time to map (embed) query text to language model tokens. + -This allows us to use the tokenizer while indexing documents in Vespa and also at query time to -map (embed) query text to language model tokens. +## Using tokenizer in schema +Assuming we have two fields that we want to index and use for re-ranking (title, body), we can use the `embed` indexing expression to invoke the tokenizer configured above: -## Using tokenizer in schema -Assuming we have two fields that we want to index and use for re-ranking (title, body), we -can use the `embed` indexing expression to invoke the tokenizer configured above: -<pre> +```js schema my_document { document my_document { field title type string {..} field body type string {..} } - field tokens type tensor<float>(d0[512]) { + field tokens type tensor<float>(d0[512]) { indexing: (input title || "") . " " . (input body || "") | embed tokenizer | attribute } -}</pre> +} +``` + + +The above will concat the title and body input document fields, and input to the `hugging-face-tokenizer` tokenizer which saves the output tokens as float (101.0). To use the generated `tokens` tensor in ranking, the tensor field must be defined with [attribute](/en/content/attributes). -The above will concat the title and body input document fields, and input to the -`hugging-face-tokenizer` tokenizer which saves the output tokens as float (101.0). -To use the generated `tokens` tensor in ranking, the tensor field must be defined with [attribute](../content/attributes.html). +## Using the cross-encoder model in ranking -## Using the cross-encoder model in ranking -Cross-encoder models are not practical for *retrieval* over large document volumes due to their complexity, so we configure them -using [phased ranking](phased-ranking.html). +Cross-encoder models are not practical for *retrieval* over large document volumes due to their complexity, so we configure them using [phased ranking](/en/ranking/phased-ranking). ### Bert-based model + Bert-based models have three inputs: - input_ids @@ -116,16 +111,13 @@ Bert-based models have three inputs: - attention_mask -The [onnx-model](../reference/schemas/schemas.html#onnx-model) configuration specifies the input names -of the model and how to calculate them. It also specifies the file `models/model.onnx`. -Notice also the [GPU](../operations/self-managed/vespa-gpu-container.html). -GPU inference is not required, and Vespa will fall back to CPU if no GPU device is found. -See the section on [performance](#performance). +The [onnx-model](/en/reference/schemas/schemas#onnx-model) configuration specifies the input names of the model and how to calculate them. It also specifies the file `models/model.onnx`. Notice also the [GPU](/en/operations/self-managed/vespa-gpu-container). GPU inference is not required, and Vespa will fall back to CPU if no GPU device is found. See the section on [performance](#performance). + -<pre> +```js expandable rank-profile bert-ranker inherits default { inputs { - query(q_tokens) tensor<float>(d0[32]) + query(q_tokens) tensor<float>(d0[32]) } onnx-model cross_encoder { file: models/model.onnx @@ -155,26 +147,22 @@ rank-profile bert-ranker inherits default { rerank-count: 25 expression: onnx(cross_encoder){d0:0,d1:0} } -}</pre> +} +``` + -The example above limits the sequence length to `256` using the built-in -[convenience functions](../reference/ranking/rank-features.html#tokenInputIds(length,%20input_1,%20input_2,%20...)) -for generating token sequence input to Transformer models. Note that `tokenInputIds` uses 101 as start of sequence -and 102 as padding. This is only compatible with BERT-based tokenizers. See section on [performance](#performance) -about sequence length and impact on inference performance. +The example above limits the sequence length to `256` using the built-in [convenience functions](/en/reference/ranking/rank-features#tokenInputIds(length, input_1, input_2, ...)) for generating token sequence input to Transformer models. Note that `tokenInputIds` uses 101 as start of sequence and 102 as padding. This is only compatible with BERT-based tokenizers. See section on [performance](#performance) about sequence length and impact on inference performance. ### Roberta-based model -ROBERTA-based models only have two inputs (input_ids and attention_mask). In addition, the default tokenizer -start of sequence token is 1 and end of sequence is 2. In this case we use the -`customTokenInputIds` function in `my_input_ids` function. See -[customTokenInputIds](../reference/ranking/rank-features.html#customTokenInputIds(start_sequence_id, sep_sequence_id, length, input_1, input_2, ...)). +ROBERTA-based models only have two inputs (input_ids and attention_mask). In addition, the default tokenizer start of sequence token is 1 and end of sequence is 2. In this case we use the `customTokenInputIds` function in `my_input_ids` function. See [customTokenInputIds](/en/reference/ranking/rank-features#customTokenInputIds(start_sequence_id, sep_sequence_id, length, input_1, input_2, ...)). -<pre> + +```js expandable rank-profile roberta-ranker inherits default { inputs { - query(q_tokens) tensor<float>(d0[32]) + query(q_tokens) tensor<float>(d0[32]) } onnx-model cross_encoder { file: models/model.onnx @@ -199,72 +187,72 @@ rank-profile roberta-ranker inherits default { rerank-count: 25 expression: onnx(cross_encoder){d0:0,d1:0} } -}</pre> - +} +``` ## Using the cross-encoder model at query time -At query time, we need to tokenize the user query using the [embed](../rag/embedding.html#embedding-a-query-text) support. +At query time, we need to tokenize the user query using the [embed](/en/rag/embedding#embedding-a-query-text) support. + +The `embed` of the query text, sets the `query(q_tokens)` tensor that we defined in the ranking profile. -The `embed` of the query text, sets the `query(q_tokens)` -tensor that we defined in the ranking profile. -<pre>{% highlight json %} - { +```json +{ "yql": "select title,body from doc where userQuery()", "query": "semantic search", "input.query(q_tokens)": "embed(tokenizer, \"semantic search\")", "ranking": "bert-ranker", -}{% endhighlight %}</pre> +} +``` + -The retriever (query + first-phase ranking) can be anything, including -[nearest neighbor search](../querying/nearest-neighbor-search) a.k.a. dense retrieval using bi-encoders. +The retriever (query + first-phase ranking) can be anything, including [nearest neighbor search](/en/querying/nearest-neighbor-search) a.k.a. dense retrieval using bi-encoders. ## Performance There are three major scaling dimensions: -- The number of hits that are re-ranked [rerank-count](../reference/schemas/schemas.html#globalphase-rerank-count) Complexity is linear with the number of hits that are re-ranked. +- The number of hits that are re-ranked [rerank-count](/en/reference/schemas/schemas#globalphase-rerank-count) Complexity is linear with the number of hits that are re-ranked. - The size of the transformer model used. - The sequence input length. Transformer models scales quadratic with the input sequence length. -For models larger than 30-40M parameters, we recommend using GPU to accelerate inference. -Quantization of model weights can drastically improve serving efficiency on CPU. See -[Optimum Quantization](https://huggingface.co/docs/optimum/onnxruntime/usage_guides/quantization) +For models larger than 30-40M parameters, we recommend using GPU to accelerate inference. Quantization of model weights can drastically improve serving efficiency on CPU. See [Optimum Quantization](https://huggingface.co/docs/optimum/onnxruntime/usage_guides/quantization) ## Examples -The [MS Marco](https://github.com/vespa-engine/sample-apps/tree/master/msmarco-ranking) -sample application demonstrates using cross-encoders. +The [MS Marco](https://github.com/vespa-engine/sample-apps/tree/master/msmarco-ranking) sample application demonstrates using cross-encoders. ## Using cross-encoders with multi-vector indexing -When using [multi-vector indexing](https://blog.vespa.ai/semantic-search-with-multi-vector-indexing/) -we can do the following to feed the best (closest) paragraph using the -[closest()](../reference/ranking/rank-features.html#closest(name)) feature into re-ranking with the cross-encoder model. +When using [multi-vector indexing](https://blog.vespa.ai/semantic-search-with-multi-vector-indexing/) we can do the following to feed the best (closest) paragraph using the [closest()](../reference/ranking/rank-features#closest(name)) feature into re-ranking with the cross-encoder model. -<pre> + +```js schema my_document { document my_document { - field paragraphs type array<string>string {..} + field paragraphs type array<string>string {..} } - field tokens type tensor<float>(p{}, d0[512]) { + field tokens type tensor<float>(p{}, d0[512]) { indexing: input paragraphs | embed tokenizer | attribute } - field embedding type tensor<float>(p{}, x[768]) { + field embedding type tensor<float>(p{}, x[768]) { indexing: input paragraphs | embed embedder | attribute } -}</pre> +} +``` + Notice that both tokens use the same mapped embedding dimension name `p`. -<pre> + +```js expandable rank-profile max-paragraph-into-cross-encoder inherits default { inputs { - query(tokens) tensor<float>(d0[32]) - query(q) tensor<float>(x[768]) + query(tokens) tensor<float>(d0[32]) + query(q) tensor<float>(x[768]) } first-phase { expression: closeness(field, embedding) @@ -288,12 +276,11 @@ rank-profile max-paragraph-into-cross-encoder inherits default { expression: onnx(cross_encoder){d0:0,d1:0} #Slice } } -</pre> +``` + -The `best_input` uses a tensor join between the `closest(embedding)` tensor and the `tokens` tensor, -which then returns the tokens of the best-matching (closest) paragraph. +The `best_input` uses a tensor join between the `closest(embedding)` tensor and the `tokens` tensor, which then returns the tokens of the best-matching (closest) paragraph. -This tensor is used in the other Transformer-related functions -(`tokenTypeIds tokenAttentionMask tokenInputIds`) as the document tokens. +This tensor is used in the other Transformer-related functions (`tokenTypeIds tokenAttentionMask tokenInputIds`) as the document tokens. diff --git a/en/ranking/lightgbm.md b/en/ranking/lightgbm.md deleted file mode 100644 index 36a4fba2ef..0000000000 --- a/en/ranking/lightgbm.md +++ /dev/null @@ -1,265 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Ranking with LightGBM Models" -redirect_from: - - /en/lightgbm ---- - -[LightGBM](https://github.com/microsoft/LightGBM) is a gradient boosting -framework, similar to [XGBoost](xgboost). Among other -[advantages](https://github.com/microsoft/LightGBM/blob/master/docs/Experiments.rst#comparison-experiment), -one defining feature of LightGBM over XGBoost is that it directly supports -categorical features. If you have models that are trained with -[LightGBM](https://github.com/microsoft/LightGBM), Vespa can import the models -and use them directly. - - -## Exporting models from LightGBM - -Vespa supports importing LightGBM's -[`dump_model`](https://lightgbm.readthedocs.io/en/latest/pythonapi/lightgbm.Booster.html#lightgbm.Booster.dump_model). -This dumps the tree model and other useful data such as feature names, -objective functions, and values of categorical features to a JSON file. -An example of training and saving a model suitable for use in Vespa is as follows. - -<pre>{% highlight python %} -import json -import lightgbm as lgb -import numpy as np -import pandas as pd - -# Create random training set -features = pd.DataFrame({ - "feature_1": np.random.random(100), - "feature_2": np.random.random(100), - }) -targets = ((features["feature_1"] + features["feature_2"]) > 1.0) * 1.0 -training_set = lgb.Dataset(features, targets) - -# Train the model -params = { - 'objective': 'binary', - 'metric': 'binary_logloss', - 'num_leaves': 3, -} -model = lgb.train(params, training_set, num_boost_round=5) - -# Save the model -with open("lightgbm_model.json", "w") as f: - json.dump(model.dump_model(), f, indent=2) -{% endhighlight %}</pre> - -While this particular model isn't doing anything really useful, the output -file `lightgbm_model.json` can be imported directly into Vespa. - -See also a complete example of how to train a ranking function, using learning to rank -with ranking losses, in this -[notebook](https://github.com/vespa-engine/sample-apps/blob/master/commerce-product-ranking/notebooks/Train-lightgbm.ipynb). - -## Importing LightGBM models - -To import the LightGBM model into Vespa, add the model file to the -application package under a directory named `models`, or a -subdirectory under `models`. For instance, for the above model `lightgbm_model.json`, -add it to the application package resulting in a directory structure like this: - -<pre> -├── models -│   └── lightgbm_model.json -├── schemas -│   └── main.sd -└── services.xml -</pre> - -Note that an application package can have multiple models. After putting the -model in the `models` directory, it is available for both ranking and -[stateless model evaluation](stateless-model-evaluation.html). - - -## Ranking with LightGBM models - -Vespa has a [ranking feature](../reference/ranking/rank-features.html) -called `lightgbm`. This ranking feature specifies the model to use in a ranking -expression, relative under the `models` directory. Consider the following example: - -<pre> -schema test { - rank-profile classify inherits default { - first-phase { - expression: lightgbm("lightgbm_model.json") - } - } -} -</pre> - -Here, we specify that the model `lightgbm_model.json` (directly under the -`models` directory) is applied to all documents matching a query which uses -rank-profile `classify`. One important issue to consider is how to map features -in the model to features that are available for Vespa to use in ranking. - -Take a look at the JSON file dumped from the example above: - -<pre> -{ - "name": "tree", - "version": "v3", - "num_class": 1, - "num_tree_per_iteration": 1, - "label_index": 0, - "max_feature_idx": 1, - "average_output": false, - "objective": "binary sigmoid:1", - "feature_names": [ - "feature_1", - "feature_2" - ], - "monotone_constraints": [], - "tree_info": [ - .... - ], - "pandas_categorical": [] -} -</pre> - -Here, the section `feature_names` consists of the feature names used in the -training set. When this model is evaluated in Vespa, Vespa expects that these -feature names are valid [rank features](../reference/ranking/rank-features.html). -Examples are `attribute(field_name)` for a value that should be retrieved from -a document, `query(name)` for a value that should be retrieved from the query, -or possibly from other more complex rank features such as `fieldMatch(name)`. -You can also define [functions](/en/ranking/ranking-expressions-features.html#function-snippets) (which are valid rank features) with the LightGBM -feature name to perform the mapping. An example: - -<pre> -schema test { - document test { - field doc_attrib type double { - indexing: summary | attribute - } - } - rank-profile classify inherits default { - function feature_1() { - expression: attribute(doc_attrib) - } - function feature_2() { - expression: query(query_value) - } - first-phase { - expression: nativeRank - } - second-phase { - expression: lightgbm("lightgbm_model.json") - } - } -} -</pre> - -Here, when Vespa evaluates the model, it retrieves the value of `feature_1` -from a document attribute called `doc_attrib`, and the value if `feature_2` -from a query value passed along with the query. - -One can also use `attribute(doc_attrib)` directly as a feature name when -training the LightGBM model. This allows dumping rank features from Vespa -to train a model directly. - -Here, we specify that the model `lightgbm_model.json` is applied to the top ranking documents by the first-phase ranking expression. -The query request must specify `classify` as the [ranking.profile](../reference/api/query.html#ranking.profile). -See also [Phased ranking](phased-ranking.html) on how to control number of data points/documents which is exposed to the model. - -Generally the run time complexity is determined by: - -* The number of documents evaluated [per thread](../performance/sizing-search.html) / number of nodes and the query filter -* The complexity of computing features. For example `fieldMatch` features are 100x more expensive that `nativeFieldMatch/nativeRank`. -* The number of trees and the maximum depth per tree - -Serving latency can be brought down by [using multiple threads per query request](../performance/practical-search-performance-guide.html#multithreaded-search-and-ranking). - - -## Objective functions - -If you have used XGBoost with Vespa previously, you might have noticed you -have to wrap the `xgboost` feature in for instance a `sigmoid` function if -using a binary classifier. That should not be needed in LightGBM, as that -information is passed along in the model dump as seen in the `objective` section -in the JSON output above. - -Currently, Vespa supports importing models trained with the following objectives: - -- `binary` -- `regression` -- `lambdarank` -- `rank_xendcg` -- `rank_xendcg` - -For more information on LightGBM and objective functions, see -[`objective`](https://lightgbm.readthedocs.io/en/latest/Parameters.html#objective). - - -## Using categorical features - -LightGBM has the option of directly training on categorical features. Example: - -<pre> -features = pd.DataFrame({ - "numerical": np.random.random(5), - "categorical": pd.Series(np.random.permutation(["a", "b", "c", "d", "e"])), dtype="category"), - }) -</pre> - -Here, the `categorical` feature is marked with the Pandas dtype `category`. This -tells LightGBM to send the categorical values in the `pandas_categorical` section -in the JSON example above. This allows Vespa to extract the proper categorical values -to use. This is important, because other methods of using categorical variables -will result in the category values being "1", "2", ... "n", and sending in "a" in -this case for model evaluation will probably result in an erroneous result. To ensure -that categorical variables are properly handled, construct training data based -on Pandas tables and use the `category` dtype on categorical columns. - -In Vespa categorical features are strings, so mapping the above feature -for instance to a document field would be: - -<pre> -schema test { - document test { - field numeric_attrib type double { - indexing: summary | attribute - } - field string_attrib type string { - indexing: summary | attribute - } - } - rank-profile classify inherits default { - function numerical() { - expression: attribute(numeric_attrib) - } - function categorical() { - expression: attribute(string_attrib) - } - first-phase { - expression: lightgbm("lightgbm_model.json") - } - } -} -</pre> - -Here, the string value of the document would be used as the feature value when evaluating -this model for every document. - - -## Debugging Vespa inference score versus LightGBM predict score - -* When dumping LightGBM models to a JSON representation some of the model information is lost - (e.g. the `base_score` or the optimal number of trees if trained with early stopping). - -* For training, features should be scraped from Vespa, using either `match-features` or `summary-features` so - that features from offline training matches the online Vespa computed features. Dumping - features can also help debug any differences by zooming into specific query,document pairs - using [recall](../reference/api/query.html#recall) parameter. - -* It's also important to use the highest possible precision - when reading Vespa features for training as Vespa outputs features using `double` precision. - If the training routine rounds features to `float` or other more compact floating number representations, feature split decisions might differ in Vespa versus XGboost. -* In a distributed setting when multiple nodes uses the model, text matching features such as `nativeRank`, `nativFieldMatch`, `bm25` and `fieldMatch` - might differ, depending on which node produced the hit. The reason is that all these features use [term(n).significance](../reference/ranking/rank-features.html#query-features), which is computed locally indexed corpus. The `term(n).significance` feature - is related to *Inverse Document Frequency (IDF)*. The `term(n).significance` should be set by a searcher in the container for global correctness as each node will estimate the significance values from the local corpus. - diff --git a/en/ranking/lightgbm.mdx b/en/ranking/lightgbm.mdx new file mode 100644 index 0000000000..475b08a79f --- /dev/null +++ b/en/ranking/lightgbm.mdx @@ -0,0 +1,197 @@ +--- +title: "Ranking with LightGBM Models" +sidebarTitle: "Using LightGBM models" +--- + +[LightGBM](https://github.com/microsoft/LightGBM) is a gradient boosting framework, similar to [XGBoost](/en/ranking/xgboost). Among other [advantages](https://github.com/microsoft/LightGBM/blob/master/docs/Experiments.rst#comparison-experiment), one defining feature of LightGBM over XGBoost is that it directly supports categorical features. If you have models that are trained with [LightGBM](https://github.com/microsoft/LightGBM), Vespa can import the models and use them directly. + +## Exporting models from LightGBM + +Vespa supports importing LightGBM's [`dump_model`](https://lightgbm.readthedocs.io/en/latest/pythonapi/lightgbm.Booster.html#lightgbm.Booster.dump_model). This dumps the tree model and other useful data such as feature names, objective functions, and values of categorical features to a JSON file. An example of training and saving a model suitable for use in Vespa is as follows. + +```python expandable +import json +import lightgbm as lgb +import numpy as np +import pandas as pd + +# Create random training set +features = pd.DataFrame({ + "feature_1": np.random.random(100), + "feature_2": np.random.random(100), + }) +targets = ((features["feature_1"] + features["feature_2"]) > 1.0) * 1.0 +training_set = lgb.Dataset(features, targets) + +# Train the model +params = { + 'objective': 'binary', + 'metric': 'binary_logloss', + 'num_leaves': 3, +} +model = lgb.train(params, training_set, num_boost_round=5) + +# Save the model +with open("lightgbm_model.json", "w") as f: + json.dump(model.dump_model(), f, indent=2) +``` + +While this particular model isn't doing anything really useful, the output file `lightgbm_model.json` can be imported directly into Vespa. + +See also a complete example of how to train a ranking function, using learning to rank with ranking losses, in this [notebook](https://github.com/vespa-engine/sample-apps/blob/master/commerce-product-ranking/notebooks/Train-lightgbm.ipynb). + +## Importing LightGBM models + +To import the LightGBM model into Vespa, add the model file to the application package under a directory named `models`, or a subdirectory under `models`. For instance, for the above model `lightgbm_model.json`, add it to the application package resulting in a directory structure like this: + +```bash +├── models +│ └── lightgbm_model.json +├── schemas +│ └── main.sd +└── services.xml +``` + +Note that an application package can have multiple models. After putting the model in the `models` directory, it is available for both ranking and [stateless model evaluation](/en/ranking/stateless-model-evaluation). + +## Ranking with LightGBM models + +Vespa has a [ranking feature](/en/reference/ranking/rank-features) called `lightgbm`. This ranking feature specifies the model to use in a ranking expression, relative under the `models` directory. Consider the following example: + +```js +schema test { + rank-profile classify inherits default { + first-phase { + expression: lightgbm("lightgbm_model.json") + } + } +} +``` + +Here, we specify that the model `lightgbm_model.json` (directly under the `models` directory) is applied to all documents matching a query which uses rank-profile `classify`. One important issue to consider is how to map features in the model to features that are available for Vespa to use in ranking. + +Take a look at the JSON file dumped from the example above: + +```json +{ + "name": "tree", + "version": "v3", + "num_class": 1, + "num_tree_per_iteration": 1, + "label_index": 0, + "max_feature_idx": 1, + "average_output": false, + "objective": "binary sigmoid:1", + "feature_names": [ + "feature_1", + "feature_2" + ], + "monotone_constraints": [], + "tree_info": [ + .... + ], + "pandas_categorical": [] +} +``` + +Here, the section `feature_names` consists of the feature names used in the training set. When this model is evaluated in Vespa, Vespa expects that these feature names are valid [rank features](/en/reference/ranking/rank-features). Examples are `attribute(field_name)` for a value that should be retrieved from a document, `query(name)` for a value that should be retrieved from the query, or possibly from other more complex rank features such as `fieldMatch(name)`. You can also define [functions](/en/ranking/ranking-expressions-features#function-snippets) (which are valid rank features) with the LightGBM feature name to perform the mapping. An example: + +```js +schema test { + document test { + field doc_attrib type double { + indexing: summary | attribute + } + } + rank-profile classify inherits default { + function feature_1() { + expression: attribute(doc_attrib) + } + function feature_2() { + expression: query(query_value) + } + first-phase { + expression: nativeRank + } + second-phase { + expression: lightgbm("lightgbm_model.json") + } + } +} +``` + +Here, when Vespa evaluates the model, it retrieves the value of `feature_1` from a document attribute called `doc_attrib`, and the value if `feature_2` from a query value passed along with the query. + +One can also use `attribute(doc_attrib)` directly as a feature name when training the LightGBM model. This allows dumping rank features from Vespa to train a model directly. + +Here, we specify that the model `lightgbm_model.json` is applied to the top ranking documents by the first-phase ranking expression. The query request must specify `classify` as the [ranking.profile](/en/reference/api/query#ranking.profile). See also [Phased ranking](/en/ranking/phased-ranking) on how to control number of data points/documents which is exposed to the model. + +Generally the run time complexity is determined by: + +- The number of documents evaluated [per thread](/en/performance/sizing-search) / number of nodes and the query filter +- The complexity of computing features. For example `fieldMatch` features are 100x more expensive that `nativeFieldMatch/nativeRank`. +- The number of trees and the maximum depth per tree + +Serving latency can be brought down by [using multiple threads per query request](/en/performance/practical-search-performance-guide#multithreaded-search-and-ranking). + +## Objective functions + +If you have used XGBoost with Vespa previously, you might have noticed you have to wrap the `xgboost` feature in for instance a `sigmoid` function if using a binary classifier. That should not be needed in LightGBM, as that information is passed along in the model dump as seen in the `objective` section in the JSON output above. + +Currently, Vespa supports importing models trained with the following objectives: + +- `binary` +- `regression` +- `lambdarank` +- `rank_xendcg` +- `rank_xendcg` + +For more information on LightGBM and objective functions, see [`objective`](https://lightgbm.readthedocs.io/en/latest/Parameters.html#objective). + +## Using categorical features + +LightGBM has the option of directly training on categorical features. Example: + +```js +features = pd.DataFrame({ + "numerical": np.random.random(5), + "categorical": pd.Series(np.random.permutation(["a", "b", "c", "d", "e"])), dtype="category"), + }) +``` + +Here, the `categorical` feature is marked with the Pandas dtype `category`. This tells LightGBM to send the categorical values in the `pandas_categorical` section in the JSON example above. This allows Vespa to extract the proper categorical values to use. This is important, because other methods of using categorical variables will result in the category values being "1", "2", … "n", and sending in "a" in this case for model evaluation will probably result in an erroneous result. To ensure that categorical variables are properly handled, construct training data based on Pandas tables and use the `category` dtype on categorical columns. + +In Vespa categorical features are strings, so mapping the above feature for instance to a document field would be: + +```js +schema test { + document test { + field numeric_attrib type double { + indexing: summary | attribute + } + field string_attrib type string { + indexing: summary | attribute + } + } + rank-profile classify inherits default { + function numerical() { + expression: attribute(numeric_attrib) + } + function categorical() { + expression: attribute(string_attrib) + } + first-phase { + expression: lightgbm("lightgbm_model.json") + } + } +} +``` + +Here, the string value of the document would be used as the feature value when evaluating this model for every document. + +## Debugging Vespa inference score versus LightGBM predict score + +- When dumping LightGBM models to a JSON representation some of the model information is lost (e.g. the `base_score` or the optimal number of trees if trained with early stopping). +- For training, features should be scraped from Vespa, using either `match-features` or `summary-features` so that features from offline training matches the online Vespa computed features. Dumping features can also help debug any differences by zooming into specific query,document pairs using [recall](/en/reference/api/query#recall) parameter. +- It's also important to use the highest possible precision when reading Vespa features for training as Vespa outputs features using `double` precision. If the training routine rounds features to `float` or other more compact floating number representations, feature split decisions might differ in Vespa versus XGboost. +- In a distributed setting when multiple nodes uses the model, text matching features such as `nativeRank`, `nativFieldMatch`, `bm25` and `fieldMatch` might differ, depending on which node produced the hit. The reason is that all these features use [term(n).significance](/en/reference/ranking/rank-features#query-features), which is computed locally indexed corpus. The `term(n).significance` feature is related to *Inverse Document Frequency (IDF)*. The `term(n).significance` should be set by a searcher in the container for global correctness as each node will estimate the significance values from the local corpus. \ No newline at end of file diff --git a/en/ranking/multivalue-query-operators.html b/en/ranking/multivalue-query-operators.html deleted file mode 100644 index bb3af1c2ac..0000000000 --- a/en/ranking/multivalue-query-operators.html +++ /dev/null @@ -1,321 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Multivalue Query Operators" -redirect_from: -- /en/multivalue-query-operators.html ---- - -<p> -This article is a followup to the <a href="ranking-intro">ranking introduction</a>. -Some use cases in this guide are better solved using <a href="tensor-user-guide.html">tensors</a>. -</p> - - - -<h2 id="dotproduct-and-wand">dotProduct and wand</h2> -<p> -<em>wand</em> (aka Parallel Wand) is a search operator that can be used for efficient top-k retrieval. -It implements the <em>Weak AND/Weighted AND</em> algorithm as described by Broder et al. in -<a href="https://dl.acm.org/doi/10.1145/956863.956944">Efficient query evaluation using a two-level retrieval process</a>. -See <a href="wand.html">using Wand with Vespa</a> for details. -</p> -<p> -<em>dotProduct</em> is the brute force equivalent to <em>wand</em>. -They are both used to search for documents -where weighted tokens in a field matches a subset of weighted tokens in the query. -The <a href="#raw-scores-and-query-item-labeling">raw scores</a> produced by -<em>dotProduct</em> are equivalent to those produced by <em>wand</em>. -</p><p> -The difference is that <em>wand</em> will perform local optimizations -in order to retrieve the top-k targetHits results that would be returned by inner maximum <em>dotproduct</em>. -Which one of these are most cost-efficient is complex as it depends on the size of the vocabulary (features) and: -</p> -<ul> -<li>Number of query terms and their weight distribution</li> -<li>Number of document terms and their weight distribution</li> -</ul> -<p> It is easy to compare the two approaches. One can run benchmarks using either and compare latency and total number of hits, if on average, -total number of hits approaches the total number of documents matching the other filters in the query, it is cheaper -to use tensor dot product. -</p> - - - -<h2 id="dotproduct-example">dotProduct example</h2> -<p> -Refer to the <a href="../reference/querying/yql.html#dotproduct">dotProduct</a> reference. -<em>dotProduct</em> calculates the dot product of a weighted set in the query and a weighted set in a field - -and stores the result in <a href="#raw-scores-and-query-item-labeling">raw scores</a>, -which is used in ranking expressions. -</p><p> -Use a weighted set field (use <a href="../content/attributes.html">attribute</a> with <em>fast-search</em> for higher performance) -in the document to store the tokens: -</p> -<pre> -field features type weightedset<string> { - indexing: summary | attribute - attribute: fast-search -} -</pre> -<p> -The query needs to be prepared by a custom searcher or sent -using <a href="../reference/querying/yql.html#dotproduct">YQL</a>. -The code below shows the relevant part. -If using multiple dot products in the same query it is a good idea to label them. -This enables us to use individual dot product scores when ranking results later. -</p> -<pre> -Item makeDotProduct(String label, String field, Map<String, Integer> token_map) { - DotProductItem item = new DotProductItem(field); - item.setLabel(label); - for (Map.Entry<String, Integer> entry : token_map.entrySet()) { - item.addToken(entry.getKey(), entry.getValue()); - } - return item; -} -</pre> -<p> -<em>dotProduct</em> produces -<a href="#raw-scores-and-query-item-labeling">raw scores</a> -that can be used in a ranking expression. -The simplest approach is to use the sum of all raw scores for the field containing the tokens: -</p> -<pre> -rank-profile default { - first-phase { - expression: rawScore(features) - } -} -</pre> -<p>For better control, label each dot product in the query and use their scores individually:</p> -<pre> -rank-profile default { - first-phase { - expression: itemRawScore(dp1) + itemRawScore(dp2) - } -} -</pre> - - - -<h2 id="in-example">IN operator example</h2> -<p> -Refer to the <a href="../reference/querying/yql.html#in">in operator</a> reference. -The use cases for the <em>in</em> operator are for limiting the search result -to documents with specific properties that can have a large number of distinct values, like: -</p> -<ul> - <li>We know who the user is, and want to restrict to documents - written by one of the user's friends</li> - <li>We have the topic area the user is interested in, and want to - restrict to the top-ranked authors for this topic</li> - <li>We have recorded the documents that have been clicked by users - in the last 10 minutes, and want to search only in these</li> -</ul> -<p>Using the <em>in</em> operator is more performant than a big OR expression:</p> -<pre> -select * from data where category in ('cat1', 'cat2', 'cat3') -select * from data where category = 'cat1' OR category = 'cat2' OR category = 'cat3' ... -</pre> -<p> - See <a href="../performance/feature-tuning.html#multi-lookup-set-filtering">multi-lookup set filtering</a> for details. -</p> -<p> -Note that in most actual use cases, -the field we are searching in is some sort of user ID, topic ID, group ID, or document ID -and can often be modeled as a number - -usually in a field of type <code>long</code> -(or <code>array<long></code> if multiple values are needed). -If a string field is used, it will usually also be some sort of ID; -if you have data in a string field intended for searching with the <em>in</em> operator, -then using <code>match: word</code> (default for attribute string fields) is recommended. -</p><p> -The following example shows how to use the <em>in</em> operator -programmatically in a <a href="../applications/searchers.html">Java Searcher</a> -to search a category field: -<pre> -field category type string { - indexing: attribute | summary - attribute: fast-search - rank: filter -} -</pre> -<p>The Searcher will typically do the following:</p> -<ul> - <li>Create a new <code>StringInItem</code> (or <code>NumericInIterm</code>) for the field you want to use as filter.</li> - <li>Find the tokens to insert into the query item.</li> - <li>Combine the new <code>StringInItem</code> with the original query by using an <code>AndItem</code>.</li> -</ul> -<p>A simple code example adding a hardcoded filter containing 10 tokens:</p> -<pre> -private Result hardCoded(Query query, Execution execution) { - var filter = new StringInItem("category"); - filter.addToken("magazine1"); - filter.addToken("magazine2"); - filter.addToken("magazine3"); - filter.addToken("tv"); - filter.addToken("tabloid1"); - filter.addToken("tabloid2"); - filter.addToken("tabloid3"); - filter.addToken("tabloid4"); - filter.addToken("tabloid5"); - filter.addToken("tabloid6"); - var tree = query.getModel().getQueryTree(); - var oldRoot = tree.getRoot(); - var newRoot = new AndItem(); - newRoot.addItem(oldRoot); - newRoot.addItem(filter); - tree.setRoot(newRoot); - query.trace("MyCustomFilterSearcher added hardcoded filter: ", true, 2); - return execution.search(query); -} -</pre> -<p> -The biggest challenge here is finding the tokens to insert; -normally the incoming search request URL might not contain all the tokens directly. -For example, the search request could contain the user id, -and a lookup (in a database or a Vespa index) would fetch the relevant categories for this user. -</p><p> -Refer to javadoc for more details: -<a href="https://javadoc.io/doc/com.yahoo.vespa/container-search/latest/com/yahoo/prelude/query/NumericInItem.html">NumericInItem</a> and -<a href="https://javadoc.io/doc/com.yahoo.vespa/container-search/latest/com/yahoo/prelude/query/StringInItem.html">StringInItem</a>. -</p> - - - -<h2 id="pin-results-example">Pin results example</h2> -<p> - To pin a result to a fixed position, use a tiered ranking function. - This means, define a range in ranking scores for the pinned queries, and rank the organic results below. - (It is <em>possible</em> to mix organic/pinned, to keep the example below simple, these are separate ranges) -</p> -<p> - First, define the key for the query - the key is the ID used to pin results. - How to define the key is user defined, - a simple way is to hash the query string, like: -</p> -<pre> -$ md5 -q -s "65 inch tv set" -</pre> -<p> - As a document can be pinned for multiple queries, - use a <a href="../reference/schemas/schemas.html#weightedset">weightedset</a> - to store multiple key/position entries: -</p> -<pre> -field pinned_query_position type weightedset<string> { - indexing: index | summary -} -</pre> -<p> - In this set, we will map from key to a value - - the value (<em>elementWeight</em>) will be used in a ranking profile for position. - In this example, the "organic" ranking is a closeness function: -</p> -<pre> -rank-profile semantic_pin inherits default { - inputs { - query(query_embedding) tensor<float>(x[384]) - } - - function pin_topten(fieldname) { - expression { - if (elementCompleteness(fieldname).elementWeight > 0, - 11-elementCompleteness(fieldname).elementWeight, 0) - } - } - - first-phase { - expression { - # restrict the "organic" ranking contribution to [0-1>, add top ten contributions - min(closeness(field, doc_embedding), 0.99999) + - pin_topten(pinned_query_position) - } - } - - match-features { - # Use match-features to easily debug ranking - closeness(field, doc_embedding) - elementCompleteness(pinned_query_position).elementWeight - } -} -</pre> -<p> - In short, we cap the organic range to 0-1, and add more to the ranking the higher the pinned position (11-pos). - Add an OR-term that adds the rank contribution from matching in the weighted set <code>pinned_query_position</code>: -</p> -<pre> -$ vespa query 'select * from items where true - or rank (pinned_query_position contains "af6f44472a1d3b00d40d04309067b739")' \ - ranking=semantic_pin -</pre> -<p>Above, a <code>true</code> statement is used for simplicity, replace this with the real query.</p> -<p> - A sample document, with query keys and positions - - here, a document is pinned for two queries: -</p> -<pre>{% highlight json%} -[ - { - "put": "id:mynamespace:items::1", - "fields": { - "pinned_query_position": { - "af6f44472a1d3b00d40d04309067b739": 1, - "fe5e6fc6358aa1c59b8838852040bfb4": 2 - } - } - } -] -{% endhighlight %}</pre> -<p>Snippet from a sample query result:</p> -<pre>{% highlight json %} -{ - "id": "id:mynamespace:items::1", - "relevance": 10.0, - "source": "items", - "fields": { - "matchfeatures": { - "closeness(field,doc_embedding)": 0.0, - "elementCompleteness(pinned_query_position).elementWeight": 1.0 - }, - "sddocname": "items", - "documentid": "id:mynamespace:items::1", - "pinned_query_position": { - "fe5e6fc6358aa1c59b8838852040bfb4": 2, - "af6f44472a1d3b00d40d04309067b739": 1 - } - } -} -{% endhighlight %}</pre> -<p>Observe how a pinned result for position 1 gets a 10 relevance (rank) score.</p> - - - -<h2 id="raw-scores-and-query-item-labeling">Raw scores and query item labeling</h2> -<p> -Vespa ranking is flexible and relatively decoupled from document matching. -The output from the matching pipeline typically indicates -how the different words in the query matches a specific document -and lets the ranking framework figure out how this translates to match quality. -</p><p> -However, some of the more complex match operators will produce scores directly, -rather than expose underlying match information. -A good example is the <em>wand</em> operator. -During ranking, a wand will look like a single word that has no detailed match information, -but rather a numeric score attached to it. -This is called a <em>raw score</em>, -and can be included in ranking expressions using the <code>rawScore</code> feature. -</p><p> -The <code>rawScore</code> feature takes a field name as parameter -and gives the sum of all raw scores produced by the query for that field. -If more fine-grained control is needed -(the query contains multiple operators producing raw scores for the same field, -but we want to handle those scores separately in the ranking expression), -the <code>itemRawScore</code> feature may be used. -This feature takes a query item <em>label</em> as parameter -and gives the raw score produced by that item only. -</p><p> -Query item labeling is a generic mechanism that can be used to attach symbolic names to query items. -A query item is labeled by using the <code>setLabel</code> method on a query item -in the search container query API. -</p> diff --git a/en/ranking/multivalue-query-operators.mdx b/en/ranking/multivalue-query-operators.mdx new file mode 100644 index 0000000000..60e4642a5c --- /dev/null +++ b/en/ranking/multivalue-query-operators.mdx @@ -0,0 +1,238 @@ +--- +title: "Multivalue Query Operators" +--- + +This article is a followup to the [ranking introduction](/en/ranking/ranking-intro). Some use cases in this guide are better solved using [tensors](/en/ranking/tensor-user-guide). + +## dotProduct and wand + +_wand_ (aka Parallel Wand) is a search operator that can be used for efficient top-k retrieval. It implements the _Weak AND/Weighted AND_ algorithm as described by Broder et al. in [Efficient query evaluation using a two-level retrieval process](https://dl.acm.org/doi/10.1145/956863.956944). See [using Wand with Vespa](/en/ranking/wand) for details. + +_dotProduct_ is the brute force equivalent to _wand_. They are both used to search for documents where weighted tokens in a field matches a subset of weighted tokens in the query. The [raw scores](#raw-scores-and-query-item-labeling) produced by _dotProduct_ are equivalent to those produced by _wand_. + +The difference is that _wand_ will perform local optimizations in order to retrieve the top-k targetHits results that would be returned by inner maximum _dotproduct_. Which one of these are most cost-efficient is complex as it depends on the size of the vocabulary (features) and: + +- Number of query terms and their weight distribution +- Number of document terms and their weight distribution + +It is easy to compare the two approaches. One can run benchmarks using either and compare latency and total number of hits, if on average, total number of hits approaches the total number of documents matching the other filters in the query, it is cheaper to use tensor dot product. + +## dotProduct example + +Refer to the [dotProduct](/en/reference/querying/yql#dotproduct) reference. _dotProduct_ calculates the dot product of a weighted set in the query and a weighted set in a field - and stores the result in [raw scores](#raw-scores-and-query-item-labeling), which is used in ranking expressions. + +Use a weighted set field (use [attribute](/en/content/attributes) with _fast-search_ for higher performance) in the document to store the tokens: + +```bash +field features type weightedset<string> { + indexing: summary | attribute + attribute: fast-search +} +``` + +The query needs to be prepared by a custom searcher or sent using [YQL](/en/reference/querying/yql#dotproduct). The code below shows the relevant part. If using multiple dot products in the same query it is a good idea to label them. This enables us to use individual dot product scores when ranking results later. + +```java +Item makeDotProduct(String label, String field, Map<String, Integer> token_map) { + DotProductItem item = new DotProductItem(field); + item.setLabel(label); + for (Map.Entry<String, Integer> entry : token_map.entrySet()) { + item.addToken(entry.getKey(), entry.getValue()); + } + return item; +} +``` + +_dotProduct_ produces [raw scores](#raw-scores-and-query-item-labeling) that can be used in a ranking expression. The simplest approach is to use the sum of all raw scores for the field containing the tokens: + +```bash +rank-profile default { + first-phase { + expression: rawScore(features) + } +} +``` + +For better control, label each dot product in the query and use their scores individually: + +```bash +rank-profile default { + first-phase { + expression: itemRawScore(dp1) + itemRawScore(dp2) + } +} +``` + +## IN operator example + +Refer to the [in operator](/en/reference/querying/yql#in) reference. The use cases for the _in_ operator are for limiting the search result to documents with specific properties that can have a large number of distinct values, like: + +- We know who the user is, and want to restrict to documents written by one of the user's friends +- We have the topic area the user is interested in, and want to restrict to the top-ranked authors for this topic +- We have recorded the documents that have been clicked by users in the last 10 minutes, and want to search only in these + +Using the _in_ operator is more performant than a big OR expression: + +```bash +select * from data where category in ('cat1', 'cat2', 'cat3') +select * from data where category = 'cat1' OR category = 'cat2' OR category = 'cat3' ... +``` + +See [multi-lookup set filtering](/en/performance/feature-tuning#multi-lookup-set-filtering) for details. + +Note that in most actual use cases, the field we are searching in is some sort of user ID, topic ID, group ID, or document ID and can often be modeled as a number - usually in a field of type `long` (or `array<long>` if multiple values are needed). If a string field is used, it will usually also be some sort of ID; if you have data in a string field intended for searching with the _in_ operator, then using `match: word` (default for attribute string fields) is recommended. + +The following example shows how to use the _in_ operator programmatically in a [Java Searcher](/en/applications/searchers) to search a category field: + +```js +field category type string { + indexing: attribute | summary + attribute: fast-search + rank: filter +} +``` + +The Searcher will typically do the following: + +- Create a new `StringInItem` (or `NumericInIterm`) for the field you want to use as filter. +- Find the tokens to insert into the query item. +- Combine the new `StringInItem` with the original query by using an `AndItem`. + +A simple code example adding a hardcoded filter containing 10 tokens: + +```java +private Result hardCoded(Query query, Execution execution) { + var filter = new StringInItem("category"); + filter.addToken("magazine1"); + filter.addToken("magazine2"); + filter.addToken("magazine3"); + filter.addToken("tv"); + filter.addToken("tabloid1"); + filter.addToken("tabloid2"); + filter.addToken("tabloid3"); + filter.addToken("tabloid4"); + filter.addToken("tabloid5"); + filter.addToken("tabloid6"); + var tree = query.getModel().getQueryTree(); + var oldRoot = tree.getRoot(); + var newRoot = new AndItem(); + newRoot.addItem(oldRoot); + newRoot.addItem(filter); + tree.setRoot(newRoot); + query.trace("MyCustomFilterSearcher added hardcoded filter: ", true, 2); + return execution.search(query); +} +``` + +The biggest challenge here is finding the tokens to insert; normally the incoming search request URL might not contain all the tokens directly. For example, the search request could contain the user id, and a lookup (in a database or a Vespa index) would fetch the relevant categories for this user. + +Refer to javadoc for more details: [NumericInItem](https://javadoc.io/doc/com.yahoo.vespa/container-search/latest/com/yahoo/prelude/query/NumericInItem.html) and [StringInItem](https://javadoc.io/doc/com.yahoo.vespa/container-search/latest/com/yahoo/prelude/query/StringInItem.html). + +## Pin results example + +To pin a result to a fixed position, use a tiered ranking function. This means, define a range in ranking scores for the pinned queries, and rank the organic results below. (It is _possible_ to mix organic/pinned, to keep the example below simple, these are separate ranges) + +First, define the key for the query - the key is the ID used to pin results. How to define the key is user defined, a simple way is to hash the query string, like: + +```bash +$ md5 -q -s "65 inch tv set" +``` + +As a document can be pinned for multiple queries, use a [weightedset](/en/reference/schemas/schemas#weightedset) to store multiple key/position entries: + +```js +field pinned_query_position type weightedset<string> { + indexing: index | summary +} +``` + +In this set, we will map from key to a value - the value (_elementWeight_) will be used in a ranking profile for position. In this example, the "organic" ranking is a closeness function: + +```js expandable +rank-profile semantic_pin inherits default { + inputs { + query(query_embedding) tensor<float>(x[384]) + } + + function pin_topten(fieldname) { + expression { + if (elementCompleteness(fieldname).elementWeight > 0, + 11-elementCompleteness(fieldname).elementWeight, 0) + } + } + + first-phase { + expression { + # restrict the "organic" ranking contribution to [0-1>, add top ten contributions + min(closeness(field, doc_embedding), 0.99999) + + pin_topten(pinned_query_position) + } + } + + match-features { + # Use match-features to easily debug ranking + closeness(field, doc_embedding) + elementCompleteness(pinned_query_position).elementWeight + } +} +``` + +In short, we cap the organic range to 0-1, and add more to the ranking the higher the pinned position (11-pos). Add an OR-term that adds the rank contribution from matching in the weighted set `pinned_query_position`: + +```bash +$ vespa query 'select * from items where true + or rank (pinned_query_position contains "af6f44472a1d3b00d40d04309067b739")' \ + ranking=semantic_pin +``` + +Above, a `true` statement is used for simplicity, replace this with the real query. + +A sample document, with query keys and positions - here, a document is pinned for two queries: + +```json +[ + { + "put": "id:mynamespace:items::1", + "fields": { + "pinned_query_position": { + "af6f44472a1d3b00d40d04309067b739": 1, + "fe5e6fc6358aa1c59b8838852040bfb4": 2 + } + } + } +] +``` + +Snippet from a sample query result: + +```json +{ + "id": "id:mynamespace:items::1", + "relevance": 10.0, + "source": "items", + "fields": { + "matchfeatures": { + "closeness(field,doc_embedding)": 0.0, + "elementCompleteness(pinned_query_position).elementWeight": 1.0 + }, + "sddocname": "items", + "documentid": "id:mynamespace:items::1", + "pinned_query_position": { + "fe5e6fc6358aa1c59b8838852040bfb4": 2, + "af6f44472a1d3b00d40d04309067b739": 1 + } + } +} +``` + +Observe how a pinned result for position 1 gets a 10 relevance (rank) score. + +## Raw scores and query item labeling + +Vespa ranking is flexible and relatively decoupled from document matching. The output from the matching pipeline typically indicates how the different words in the query matches a specific document and lets the ranking framework figure out how this translates to match quality. + +However, some of the more complex match operators will produce scores directly, rather than expose underlying match information. A good example is the _wand_ operator. During ranking, a wand will look like a single word that has no detailed match information, but rather a numeric score attached to it. This is called a _raw score_, and can be included in ranking expressions using the `rawScore` feature. + +The `rawScore` feature takes a field name as parameter and gives the sum of all raw scores produced by the query for that field. If more fine-grained control is needed (the query contains multiple operators producing raw scores for the same field, but we want to handle those scores separately in the ranking expression), the `itemRawScore` feature may be used. This feature takes a query item _label_ as parameter and gives the raw score produced by that item only. + +Query item labeling is a generic mechanism that can be used to attach symbolic names to query items. A query item is labeled by using the `setLabel` method on a query item in the search container query API. \ No newline at end of file diff --git a/en/ranking/nativerank.html b/en/ranking/nativerank.html deleted file mode 100644 index cd464b8cb2..0000000000 --- a/en/ranking/nativerank.html +++ /dev/null @@ -1,259 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "The nativeRank rank feature" -redirect_from: -- /en/nativerank.html ---- - -<p> - The <em>nativeRank</em> text match score is a reasonably good text - feature score which is computed at an acceptable performance by Vespa. - It computes a normalized rank score - which tries to capture how well query terms matched the set of searched index fields. -</p> -<p> - The <em>nativeRank</em> feature is computed as a linear combination of - three other matching features: <em>nativeFieldMatch</em>, <em>nativeProximity</em> - and <em>nativeAttributeMatch</em>, - see <a href="../reference/ranking/nativerank.html"> the nativeRank reference for details</a>. -</p> -<p> - Ranking signals that might be useful, - like freshness (the age of the document compared to the time of the query) - or any other document or query features, - are <span style="text-decoration: underline;">not</span> a part of the <em>nativeRank</em> calculation. - These need to be added to the final ranking function depending on application specifics. -</p> - - - -<h3 id="weight-significance-and-connectedness">Weight, significance and connectedness</h3> -<p> - Modify the values of the match features from the query - by sending <em>weight</em>, <em>significance</em> and <em>connectedness</em> with the query: -</p> -<table class="table"> -<thead> - <tr> - <th>Feature input</th><th>Description</th> - </tr> -</thead> - <tbody> -<tr><th>Weight</th><td> - <p> - Set query term <a href="../reference/querying/yql.html#weight">weight</a>. - Example: <code>... where (title contains ({weight:200}"heads") AND title contains "tails")</code> - specifies that <code>heads</code> is twice as important for the final rank score than <code>tails</code> - (the default weight is 100). - </p> - <p> - The term weight is used in several text scoring features, including <a href="../reference/ranking/rank-features.html#fieldMatch(name).weight"> - fieldMatch(<em>name</em>).weight</a> and <a href="../reference/ranking/nativerank.html">nativeRank</a>. Note that the term weight - is not applicable for all text scoring features, for example <a href="bm25.html">bm25</a> does not use the term weight. - </p> - <p> - Configure static field weights in the <a href="../reference/schemas/schemas.html#weight">schema</a>. - </p> -</td> -</tr><tr><th>Significance</th><td> - <p> - Significance is an indication of how rare a term is in the corpus of the language, - used by a number of text matching <a href="../reference/ranking/rank-features.html">rank features</a>. - This can be set explicitly for each term in - <a href="../reference/querying/yql.html#significance">the query</a>, - or by calling item.setSignificance() in a <a href="../applications/searchers.html">Searcher</a>. - </p> - <p> - With <em>indexed search</em>, default significance values are calculated automatically during indexing. - However, unless the indexed corpus is representative of the word frequencies in the user's language, - relevance can be improved by passing significances derived from a representative corpus. - Relative significance is accessible in ranking through the - <a href="../reference/ranking/rank-features.html#fieldMatch(name).significance"> - fieldMatch(<em>name</em>).significance</a> feature. - Weight and significance are also averaged into - <a href="../reference/ranking/rank-features.html#fieldMatch(name).importance"> - fieldMatch(<em>name</em>).importance</a> for convenience. - </p> - <p> - <em>Streaming search</em> does not compute term significance, queries should pass this with the query terms. - <a href="../performance/streaming-search.html#differences-in-streaming-search">Read more</a>. - </p> -</td> -</tr><tr><th>Connectedness</th><td> - <p> - Signify the degree of connection between adjacent terms in the query - - set query term <a href="../reference/querying/yql.html#connectivity">connectivity</a> to another term. - </p> - <p> - For example, the query <code>new york newspaper</code> should have a higher connectedness - between the terms "new" and "york" than between "york" and "newspaper" to rank documents higher - if they contain "new york" as a phrase. - </p> - <p> - Term connectedness is taken into account by - <a href="../reference/ranking/rank-features.html#fieldMatch(name).proximity"> - fieldMatch(<em>name</em>).proximity</a>, - which is also an important contribution to - <a href="../reference/ranking/rank-features.html#fieldMatch(name)">fieldMatch(<em>name</em>)</a>. - Connectedness is a normalized value which is 0.1 by default. - It must be set by a custom Searcher, - looking up connectivity information from somewhere - there is no query syntax for it. - </p> -</td> - <!-- ToDo: Use connectivity and id query annotations to do this? --> -</tr> -</tbody> -</table> - - -<h2 id="using-nativeRank">Using nativeRank</h2> - -<p> - In this section we describe a blog search application that uses - <em>nativeRank</em> as the core text matching rank feature, - in combination with other signals that could be important for a blog search type of application: -</p> -<pre> -schema blog { - document blog { - field title type string { - indexing: summary | index - } - field body type string { - indexing: summary | index - } - #The quality of the source in the range 0 - 1.0 - field sourcequality type float { - indexing: summary | attribute - } - #seconds since epoch - field timestamp type long { - indexing: summary | attribute - } - field url type string { - indexing: summary - } - } - fieldset default { - fields: title, body - } -} -</pre> -<p> - In addition to the core text match feature (<em>nativeRank</em>), - we have a pre-calculated document feature which indicates the quality of - the document represented by the field <em>sourcequality</em> of type float. - The <em>sourcequality</em> field has - the <a href="../reference/schemas/schemas.html#attribute">attribute</a> - property which is required to refer that field in a ranking expression: - <em>attribute(name)</em>. - The sourcequality score could be calculated from a web map, - or any other source and is outside the scope of this document. -</p> -<p> - We also know when the documented was published (timestamp) - and this document attribute can be used to calculate the age of the document. - To summarize, we have three main rank signals that we would like our blog ranking function to consist of: -</p> -<ul> - <li>How well the query match the document text, where we use - the <em>nativeRank</em> feature score.</li> - <li>How fresh the document is, where we use the built-in - <em>age(name)</em> feature to built our own feature score.</li> - <li>The quality of the document, calculated outside of Vespa and - referenced in a ranking expression by <em>attribute(name)</em>.</li> -</ul> - - - -<h2 id="designing-our-own-blog-freshness-ranking-function">Designing our own blog freshness ranking function</h2> - -<p> - Vespa has several <a href="../reference/ranking/rank-features.html">built in rank-features</a> that we can use directly, - or we can design our own as well if the built-in features doesn't meet our requirements. - The built in <em>freshness(name)</em> rank-feature is linearly decreasing from 0 age (now) to the configured max age. - Ideally we would like to have a different shape for our blog application, - we define the following feature which has the characteristic we want: -</p> -<pre> -function freshness() { - expression: exp(-1 * age(timestamp)/(3600*12)) -} -</pre> -<p> - Timestamp resolution is seconds, so we divide by 3600 to go to an hour resolution, - and further we divide with 12 to control the slope of the freshness function. - Below is a plot of two freshness functions with different slope numbers for comparison: -</p> -<img src="/assets/img/relevance/blog-freshness.png" alt="Blog freshness ranking plot: freshness score"/> -<p> - The beauty is that we can control and experiment with the freshness rank score given the document age. - We can define any shape over any resolution that we think will fit the exact application requirements. - In our case we would like to have a non-linear relationship between the age of the document and the freshness score. - We achieve this with an exponential decreasing function (exp(-x)), - where the sensitivity of x is higher when the document is really fresh - compared to an old blog post (24 hours). -</p> - - - -<h2 id="putting-our-features-together-into-a-ranking-expression">Putting our features together into a ranking expression</h2> - -<p> - We now need to put our three main ranking signals together into one ranking expression. - We would like to control the weight of each component at query time, - so we can at query time do analysis to figure out if a certain signal should be weighted more than others. - We chose to combine our three signals into a normalized weighted sum of the three signals. - The shape of each of the three signals might be tuned individually - as we have seen with design of our own freshness feature and <em>nativeRank</em> tuning. - Below is the final blog rank-profile with all relevant settings (properties) and ranking expressions: -</p> -<pre> -rank-profile blog inherits default { - weight title: 200 - weight body: 100 - rank-type body: about - rank-properties { - nativeFieldMatch.occurrenceCountTable.title: "linear(0,8000)" - } - - # our freshness rank feature - function freshness() { - expression: exp(-1 * age(timestamp)/(3600*12)) - } - - # our quality rank feature - function quality() { - expression: attribute(sourcequality) - } - - # normalization factor for the weighted sum - function normalization() { - expression: query(textMatchWeight) + query(qualityWeight) + query(deservesFreshness) - } - - # ranking function that runs over all matched documents, determined by the boolean query logic - first-phase { - expression: (query(textMatchWeight) * (nativeRank(title,body) + query(qualityWeight) * quality + query(deservesFreshness) * freshness))/normalization - } - - summary-features: nativeRank(title,body) age(timestamp) freshness quality - } -} -</pre> -<p> - We can override the weight of each signal at query time with - the <a href="../reference/api/query.html">query api</a>, - passing down the weights: -</p> -<pre> -/search/?query=vespa+ranking&datetime=now&ranking.profile=blog&input.query(textMatchWeight)=0.1&input.query(deservesFreshness)=0.85 -</pre> -<p> - It is also possible to override the user-defined rank-features in a custom searcher plugin, - note that we also use the <em>datetime</em> parameter to be able to calculate the age of the document. -</p> -<p> - The <a href="../reference/schemas/schemas.html#summary-features">summary-features</a> - allows us to have access to the individual ranking signals along with the hit's summary fields. -</p> diff --git a/en/ranking/nativerank.mdx b/en/ranking/nativerank.mdx new file mode 100644 index 0000000000..2a4c68968d --- /dev/null +++ b/en/ranking/nativerank.mdx @@ -0,0 +1,142 @@ +--- +title: "The nativeRank rank feature" +--- + +The *nativeRank* text match score is a reasonably good text feature score which is computed at an acceptable performance by Vespa. It computes a normalized rank score which tries to capture how well query terms matched the set of searched index fields. + +The *nativeRank* feature is computed as a linear combination of three other matching features: *nativeFieldMatch*, *nativeProximity* and *nativeAttributeMatch*, see [the nativeRank reference for details](/en/reference/ranking/nativerank). + +Ranking signals that might be useful, like freshness (the age of the document compared to the time of the query) or any other document or query features, are not a part of the *nativeRank* calculation. These need to be added to the final ranking function depending on application specifics. + +### Weight, significance and connectedness + +Modify the values of the match features from the query by sending *weight*, *significance* and *connectedness* with the query: + +<table className="vespa-table"> + <thead> + <tr> + <th>Feature input</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>Weight</td> + <td>Set query term <a href="/en/reference/querying/yql#weight" aria-label="weight">weight</a>. Example: <code>{`... where (title contains ({weight:200}"heads") AND title contains "tails")`}</code> specifies that <code>{`heads`}</code> is twice as important for the final rank score than <code>{`tails`}</code> (the default weight is 100). <br/><br/> The term weight is used in several text scoring features, including <a href="/en/reference/ranking/rank-features#fieldMatch(name).weight" aria-label="fieldMatch(*name*).weight">fieldMatch(*name*).weight</a> and <a href="/en/ranking/nativerank" aria-label="nativeRank">nativeRank</a>. Note that the term weight is not applicable for all text scoring features, for example <a href="/en/ranking/bm25" aria-label="bm25">bm25</a> does not use the term weight.<br/><br/> Configure static field weights in the <a href="/en/reference/schemas/schemas#weight" aria-label="schema">schema</a>.</td> + </tr> + <tr> + <td>Significance</td> + <td>Significance is an indication of how rare a term is in the corpus of the language, used by a number of text matching <a href="/en/reference/ranking/rank-features" aria-label="rank features">rank features</a>. This can be set explicitly for each term in <a href="/en/reference/querying/yql#significance" aria-label="the query">the query</a>, or by calling item.setSignificance() in a <a href="/en/applications/searchers" aria-label="Searcher">Searcher</a>.<br/><br/> With *indexed search*, default significance values are calculated automatically during indexing. However, unless the indexed corpus is representative of the word frequencies in the user's language, relevance can be improved by passing significances derived from a representative corpus. Relative significance is accessible in ranking through the <a href="/en/reference/ranking/rank-features#fieldMatch(name).significance" aria-label="fieldMatch(*name*).significance">fieldMatch(*name*).significance</a> feature. Weight and significance are also averaged into <a href="/en/reference/ranking/rank-features#fieldMatch(name).importance" aria-label="fieldMatch(*name*).importance">fieldMatch(*name*).importance</a> for convenience.<br/><br/> *Streaming search* does not compute term significance, queries should pass this with the query terms. <a href="/en/performance/streaming-search#differences-in-streaming-search" aria-label="Read more">Read more</a>.</td> + </tr> + <tr> + <td>Connectedness</td> + <td>Signify the degree of connection between adjacent terms in the query - set query term <a href="/en/reference/querying/yql#connectivity" aria-label="connectivity">connectivity</a> to another term. <br/><br/> For example, the query <code>{`new york newspaper`}</code> should have a higher connectedness between the terms "new" and "york" than between "york" and "newspaper" to rank documents higher if they contain "new york" as a phrase. <br/><br/> Term connectedness is taken into account by <a href="/en/reference/ranking/rank-features#fieldMatch(name).proximity" aria-label="fieldMatch(*name*).proximity">fieldMatch(*name*).proximity</a>, which is also an important contribution to <a href="/en/reference/ranking/rank-features#fieldMatch(name)" aria-label="fieldMatch(*name*)">fieldMatch(*name*)</a>. Connectedness is a normalized value which is 0.1 by default. It must be set by a custom Searcher, looking up connectivity information from somewhere - there is no query syntax for it.</td> + </tr> + </tbody> +</table> + +## Using nativeRank + +In this section we describe a blog search application that uses *nativeRank* as the core text matching rank feature, in combination with other signals that could be important for a blog search type of application: + +```js +schema blog { + document blog { + field title type string { + indexing: summary | index + } + field body type string { + indexing: summary | index + } + #The quality of the source in the range 0 - 1.0 + field sourcequality type float { + indexing: summary | attribute + } + #seconds since epoch + field timestamp type long { + indexing: summary | attribute + } + field url type uri { + indexing: summary + } + } + fieldset default { + fields: title, body + } +} +``` + +In addition to the core text match feature (*nativeRank*), we have a pre-calculated document feature which indicates the quality of the document represented by the field *sourcequality* of type float. The *sourcequality* field has the [attribute](/en/reference/schemas/schemas#attribute) property which is required to refer that field in a ranking expression: *attribute(name)*. The sourcequality score could be calculated from a web map, or any other source and is outside the scope of this document. + +We also know when the documented was published (timestamp) and this document attribute can be used to calculate the age of the document. To summarize, we have three main rank signals that we would like our blog ranking function to consist of: + +- How well the query match the document text, where we use the *nativeRank* feature score. +- How fresh the document is, where we use the built-in *age(name)* feature to built our own feature score. +- The quality of the document, calculated outside of Vespa and referenced in a ranking expression by *attribute(name)*. + +## Designing our own blog freshness ranking function + +Vespa has several [built in rank-features](/en/reference/ranking/rank-features) that we can use directly, or we can design our own as well if the built-in features doesn't meet our requirements. The built in *freshness(name)* rank-feature is linearly decreasing from 0 age (now) to the configured max age. Ideally we would like to have a different shape for our blog application, we define the following feature which has the characteristic we want: + +```js +function freshness() { + expression: exp(-1 * age(timestamp)/(3600*12)) +} +``` + + +Timestamp resolution is seconds, so we divide by 3600 to go to an hour resolution, and further we divide with 12 to control the slope of the freshness function. Below is a plot of two freshness functions with different slope numbers for comparison: + +<Frame> +![Blog freshness ranking plot: freshness score](/assets/img/relevance/blog-freshness.png) +</Frame> + +The beauty is that we can control and experiment with the freshness rank score given the document age. We can define any shape over any resolution that we think will fit the exact application requirements. In our case we would like to have a non-linear relationship between the age of the document and the freshness score. We achieve this with an exponential decreasing function (exp(-x)), where the sensitivity of x is higher when the document is really fresh compared to an old blog post (24 hours). + +## Putting our features together into a ranking expression + +We now need to put our three main ranking signals together into one ranking expression. We would like to control the weight of each component at query time, so we can at query time do analysis to figure out if a certain signal should be weighted more than others. We chose to combine our three signals into a normalized weighted sum of the three signals. The shape of each of the three signals might be tuned individually as we have seen with design of our own freshness feature and *nativeRank* tuning. Below is the final blog rank-profile with all relevant settings (properties) and ranking expressions: + +```js expandable +rank-profile blog inherits default { + weight title: 200 + weight body: 100 + rank-type body: about + rank-properties { + nativeFieldMatch.occurrenceCountTable.title: "linear(0,8000)" + } + + # our freshness rank feature + function freshness() { + expression: exp(-1 * age(timestamp)/(3600*12)) + } + + # our quality rank feature + function quality() { + expression: attribute(sourcequality) + } + + # normalization factor for the weighted sum + function normalization() { + expression: query(textMatchWeight) + query(qualityWeight) + query(deservesFreshness) + } + + # ranking function that runs over all matched documents, determined by the boolean query logic + first-phase { + expression: (query(textMatchWeight) * (nativeRank(title,body) + query(qualityWeight) * quality + query(deservesFreshness) * freshness))/normalization + } + + summary-features: nativeRank(title,body) age(timestamp) freshness quality + } +} +``` + +We can override the weight of each signal at query time with the [query api](/en/reference/api/query), passing down the weights: + +```bash +/search/?query=vespa+ranking&datetime=now&ranking.profile=blog&input.query(textMatchWeight)=0.1&input.query(deservesFreshness)=0.85 +``` + +It is also possible to override the user-defined rank-features in a custom searcher plugin, note that we also use the *datetime* parameter to be able to calculate the age of the document. + +The [summary-features](/en/reference/schemas/schemas#summary-features) allows us to have access to the individual ranking signals along with the hit's summary fields. \ No newline at end of file diff --git a/en/ranking/onnx.md b/en/ranking/onnx.mdx similarity index 85% rename from en/ranking/onnx.md rename to en/ranking/onnx.mdx index a9a5a1577e..f5f354b8fa 100644 --- a/en/ranking/onnx.md +++ b/en/ranking/onnx.mdx @@ -1,13 +1,11 @@ --- -# Copyright Vespa.ai. All rights reserved. title: "Ranking With ONNX Models" -redirect_from: - - /en/onnx +sidebarTitle: "Using ONNX Models" --- Vespa supports advanced ranking models through its tensor API. If your model is in the [ONNX format](https://onnx.ai/), Vespa can import and use the model directly. -You can use ONNX models with Vespa [embedder](../rag/embedding.html) functionality or in [ranking](ranking-intro). +You can use ONNX models with Vespa [embedder](/en/rag/embedding) functionality or in [ranking](/en/ranking/ranking-intro). @@ -17,7 +15,7 @@ Add the file containing the ONNX models somewhere under the application package. For instance, if your model file is `my_model.onnx`, you could add it to the application package under a `files` directory, something like: -``` +```bash ├── files │   └── my_model.onnx ├── schemas @@ -26,7 +24,7 @@ you could add it to the application package under a `files` directory, something ``` An application package can have multiple onnx models. To download models during deployment, -see [deploying remote models](../applications/deployment.html#deploying-remote-models). +see [deploying remote models](/en/applications/deployment#deploying-remote-models). @@ -35,7 +33,7 @@ see [deploying remote models](../applications/deployment.html#deploying-remote-m To make the above model available for ranking, you define the model in the schema, and then you can refer to the model using the `onnx` (or `onnxModel`) ranking feature: -``` +```js schema my_schema { document my_document { @@ -71,7 +69,7 @@ schema my_schema { ``` This defines the model called `my_onnx_model`. It is evaluated using the -`onnx` [rank feature](../reference/ranking/rank-features.html). +`onnx` [rank feature](/en/reference/ranking/rank-features). This rank feature specifies which model to evaluate in the ranking expression and, optionally, which output to use from the model. @@ -97,11 +95,11 @@ If this is omitted, the first output in the ONNX file will be used. The output of a model is a tensor, however the rank score should result in a single scalar value. In the example above we use `sum` to sum all the elements of the tensor to a single value. You can also slice out parts of -the result using Vespa's [tensor API](../reference/ranking/ranking-expressions.html#tensor-functions). +the result using Vespa's [tensor API](/en/reference/ranking/ranking-expressions#tensor-functions). For instance, if the output of the example above is a tensor with the two dimensions `d0` and `d1`, and you want to extract the first value, this can be expressed by: -``` +```js onnx(my_onnx_model).output_name{d0:0,d1:0} ``` @@ -115,14 +113,12 @@ You can pass tensors in HTTP requests by using the HTTP parameter "input.query(myTensor)" (assuming the ranking expression contains "query(myTensor)"). A tensor example can be found in the -[sample application](https://github.com/vespa-engine/sample-apps/tree/master/album-recommendation). +[sample application](https://github.com/vespa-engine/sample-apps/tree/master/album-recommendation). https://github.com/vespa-engine/sample-apps/tree/master/album-recommendation #### Batch dimensions -When training your model you will typically have an input that contains a -dimension for batches, for instance an input with sizes `[-1, 784]`. Here, -1 -typically denotes the batch dimension. During ONNX inference in ranking, Vespa uses batch size 1. +When training your model you will typically have an input that contains a dimension for batches, for instance an input with sizes `[-1, 784]`. Here, -1 typically denotes the batch dimension. During ONNX inference in ranking, Vespa uses batch size 1. ## Limitations on model size and complexity @@ -130,13 +126,13 @@ typically denotes the batch dimension. During ONNX inference in ranking, Vespa u Note that in the above rank profile example, the `onnx` model evaluation was evaluated in the first phase. In general, evaluating these types of models are more suitable in the `second-phase` or `global-phase` phases. -See [phased ranking](../ranking/phased-ranking.html). +See [phased ranking](/en/ranking/phased-ranking). Models in which data tensors are located in external data files, are **only** supported when the following conditions are met: -- The model is used in an _embedder_, e.g <code>hugging-face-embedder</code> (see [embedding](../rag/embedding.html)). -- The model is referenced using a URL, e.g. <code>url="https://example.com/my-onnx-model/model.onnx"</code>. +- The model is used in an _embedder_, e.g `hugging-face-embedder` (see [embedding](/en/rag/embedding)). +- The model is referenced using a URL, e.g. `url="https://example.com/my-onnx-model/model.onnx"`. - All external data files are located in the same parent path/directory as the model file, - e.g. <code>https://example.com/my-onnx-model/model.onnx_data</code>. + e.g. `https://example.com/my-onnx-model/model.onnx_data`. For _ranking_, Vespa supports only ONNX models that are self-contained (has no external data files) and below 2GB in size. The same restriction applies to models included in the application package. @@ -160,13 +156,13 @@ for exporting models hosted on Huggingface model hub. For example, to export [BAAI/bge-small-en](https://huggingface.co/BAAI/bge-small-en) from the model hub to onnx format: -``` +```bash $ python3 -m pip install optimum onnx onnxruntime $ optimum-cli export onnx --library transformers --task feature-extraction -m BAAI/bge-small-en --optimize O3 model-output-dir ``` The exported files in `model-output-dir`: `model.onnx` and `tokenizer.json` imported directly -into the Vespa [huggingface-embedder](../rag/embedding.html#huggingface-embedder). +into the Vespa [huggingface-embedder](/en/rag/embedding#huggingface-embedder). Refer to [debugging onnx](#debugging-onnx-models). @@ -193,30 +189,30 @@ The url must point to the actual file, not the model card. See #[Limitations on Model Size and Complexity](#limitations-on-model-size-and-complexity) for how to use models having external data files. -See [cross-encoders](cross-encoders#exporting-cross-encoder-models) documentation for examples on how to +See [cross-encoders](/en/ranking/cross-encoders#exporting-cross-encoder-models) documentation for examples on how to export cross-encoder re-rankers using the Optimum library. ### Using Auto Classes to export HF models to ONNX format Transformer-based models have named inputs and outputs that must be compatible -with the input and output names used by the [embedder](../rag/embedding.html). +with the input and output names used by the [embedder](/en/rag/embedding). The [model-exporting](https://github.com/vespa-engine/sample-apps/tree/master/examples/model-exporting) example includes two scripts to export models and vocabulary files using the default expected input and output names for embedders using ONNX models. The input and output names to the embedder are tunable via the `transformer-`parameters in the -[config of the embedder in question](../reference/rag/embedding.html). +[config of the embedder in question](/en/reference/rag/embedding). ### Debugging ONNX models -When loading [ONNX](https://onnx.ai/) models for Vespa native [embedders](../rag/embedding.html), +When loading [ONNX](https://onnx.ai/) models for Vespa native [embedders](/en/rag/embedding), the model must have correct inputs and output parameters. Vespa offers tools to inspect ONNX model files. Here, _minilm-l6-v2.onnx_ is in the current working directory: -``` +```bash $ docker run -v `pwd`:/w \ --entrypoint /opt/vespa/bin/vespa-analyze-onnx-model \ vespaengine/vespa \ @@ -233,10 +229,10 @@ model meta-data: ``` The above model input and output names conform with the -default [bert-embedder parameters](../reference/rag/embedding.html#bert-embedder-reference-config). +default [bert-embedder parameters](/en/reference/rag/embedding#bert-embedder-reference-config). -Similarly, a model for the [hugging-face-embedder parameters](../reference/rag/embedding.html#huggingface-embedder-reference-config) defaults: -``` +Similarly, a model for the [hugging-face-embedder parameters](/en/reference/rag/embedding#huggingface-embedder-reference-config) defaults: +```bash model meta-data: input[0]: 'input_ids' long[batch][sequence] input[1]: 'attention_mask' long[batch][sequence] @@ -245,7 +241,7 @@ model meta-data: ``` The shape of the output should be: -``` +```bash float[batch][sequence][vector-embedding-dimensionality] ``` Vespa embedders implement the pooling strategy over the output vectors (one per sequence length). @@ -253,13 +249,13 @@ Vespa embedders implement the pooling strategy over the output vectors (one per If loading models without the expected input and output parameter names, the container service will not start (check _vespa.log_ in the container running Vespa): -``` +```bash WARNING container Container.com.yahoo.container.di.Container Caused by: java.lang.IllegalArgumentException: Model does not contain required input: 'input_ids'. Model contains: input ``` When this happens, a deploy looks like: -``` +```bash $ vespa deploy --wait 300 Uploading application package ... done @@ -270,16 +266,16 @@ Error: service 'query' is unavailable: services have not converged ``` Embedders supports changing the input and output names, -consult [embedding reference](../reference/rag/embedding.html) documentation. +consult [embedding reference](/en/reference/rag/embedding) documentation. ### Using vespa-analyze-onnx-model -[vespa-analyze-onnx-model](../reference/operations/tools.html#vespa-analyze-onnx-model) +[vespa-analyze-onnx-model](/en/reference/operations/tools#vespa-analyze-onnx-model) is useful to find model inputs and outputs - example run on a config server where an application package with a model is deployed to: -``` +```bash $ docker exec vespa vespa-analyze-onnx-model \ /opt/vespa/var/db/vespa/config_server/serverdb/tenants/default/sessions/1/files/Network.onnx @@ -300,7 +296,7 @@ estimated model evaluation time: 0.00227701 ms ``` The corresponding input/output tensors should be defined as: -``` +```js document doc { ... field flowercategory type tensor<float>(d0[1],d1[3]) { diff --git a/en/ranking/phased-ranking.html b/en/ranking/phased-ranking.html deleted file mode 100644 index 7172006207..0000000000 --- a/en/ranking/phased-ranking.html +++ /dev/null @@ -1,541 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Phased Ranking" -redirect_from: -- /en/phased-ranking.html ---- - -<p> -Vespa allows expressing multiphased retrieval and ranking of documents. The retrieval phase is done close to the data in the content nodes, -while the ranking phase(s) can be done in the content nodes or in the stateless container after scatter-and-gather from content node(s). -The retrieval part of a phased pipeline is expressed by using the <a href="../querying/query-api.html">query API</a> -and the ranking part is expressed by using the <a href="../reference/schemas/schemas.html#rank-profile">rank-profile</a> in the schema. -</p> - -<ul> - <li><strong>Retrieval:</strong><a href="#top-k-query-operators"> <em>Top-k</em> query operators</a>, like <a href="wand.html">weakAnd/wand</a> and - <a href="../querying/nearest-neighbor-search">nearest neighbor search</a> allow retrieval with sublinear complexity. These - query operators use simple scoring functions that are computationally cheap to evaluate over Vespa indexes. Using - the expressiveness of the Vespa query language, developers can combine multiple retrievers in the query, and expose - the union of retrieved documents into Vespa ranking phases.</li> - - <li> - <strong>Per node ranking:</strong>The query specification retrieves documents and ranks them using declarative phases evaluated within - the <a href="#two-phase-ranking-on-content-nodes">content nodes</a>: - <ul> - <li><a href="#first-phase-ranking-on-content-nodes">first-phase expression</a>; - configured in <a href="../reference/schemas/schemas.html#rank-profile">rank-profile</a>. - This phase is evaluated for <em>all</em> hits retrieved by the query logic. This phase can also remove - retrieved documents using <a href="../reference/schemas/schemas.html#rank-score-drop-limit">rank-score-drop-limit</a>. - </li> - <li><a href="#two-phase-ranking-on-content-nodes">second-phase ranking</a>; - configured in <a href="../reference/schemas/schemas.html#rank-profile">rank-profile</a>. - Optionally re-rank the top-scoring hits from the first-phase ranking using a more complex expression. The - <a href="../reference/schemas/schemas.html#secondphase-total-rerank-count">total-rerank-count</a> sets a strict upper bound on the - number of documents that are re-ranked in total over the nodes. - </li> - </ul> - <li><strong>Global ranking:</strong>Following the per content node local ranking phases, - re-ranking can occur after the content nodes have returned the merged top-scoring hits to the stateless container. - This phase is specified using a <a href="#using-a-global-phase-expression">global-phase</a> expression in the - <a href="../reference/schemas/schemas.html#rank-profile">rank-profile</a>. - Additionally, the global-phase can conduct <a href="#cross-hit-normalization-including-reciprocal-rank-fusion"> - cross-hit normalization</a> to combine unrelated scoring methods. - </li> - <li>Finally, for customized ranking that is difficult to express in declarative phases, one can implement - re-ranking using <a href="reranking-in-searcher">reranking in searcher</a>. - </li> -</ul> -<img src="/assets/img/phased-ranking.png" alt="Ranking in 3 phases"/> - - - -<h2 id="first-phase-ranking-on-content-nodes">First-phase ranking on content nodes</h2> - -<p> - Normally, you will always start by having one ranking expression - that is evaluated on the content nodes. This is configured in - the <code>rank-profile</code> section of a - <a href="../reference/schemas/schemas.html#rank-profile">schema</a> - as a <code>first-phase</code> expression. This expression can - use various <a href="ranking-expressions-features.html">rank features</a> - with information extracted during the matching phase to evaluate - the relevance of each document. The first-phase expression - is computed for every document retrieved by the query. The computational cost - is bounded by the number of documents exposed to the ranking phase on each content node multiplied with - the complexity of the first-phase expression; therefore the expression - needs to be simple and cheap to allow scaling to large amounts of retrieved docs. Alternatively, - use retrieval operators that will expose only the top-k hits to the first-phase expression. -</p> - - - -<h2 id="two-phase-ranking-on-content-nodes">Two-phase ranking on content nodes</h2> -<p> - While some use cases only require one (simple) first-phase - ranking expression, for more advanced use cases it's possible to - add a <code>second-phase</code> ranking expression in a - <a href="../reference/schemas/schemas.html#rank-profile">rank-profile</a> - in the schema. - This enables more expensive computations than would be feasible - to run as a first-phase computation, with predictable - upper bounds for the cost. -</p> -<p> - By default, second-phase ranking (if specified) is evaluated for the 100 best hits - from the first-phase ranking per content node. The number that is reranked over all nodes can be set by - <a href="../reference/schemas/schemas.html#secondphase-total-rerank-count">total-rerank-count</a>. -</p> -<pre> -schema myapp { - … - rank-profile title-freshness inherits default { - first-phase { - expression { - bm25(title) + 3*freshness(timestamp) - } - } - second-phase { - expression { - xgboost("my-model.json") - } - total-rerank-count: 50 - } - } -} -</pre> -<p> - In this example, the first phase uses the text matching feature - <a href="bm25.html">bm25</a> scoped to the <em>title</em> field - plus one of the built-in document <a href="../reference/ranking/rank-features.html">rank feature</a> - named <em>freshness</em> over a <em>timestamp</em> field which stores the epoch time in second resolution. - For each content node, the top 50 hits from the first phase function is re-ranked using - a trained <a href="xgboost">xgboost</a> model. -</p> - - -<h2 id="using-a-global-phase-expression">Using a global-phase expression</h2> -<p> - Using a rank expression configured as a - <a href="../reference/schemas/schemas.html#globalphase-rank">global-phase</a> - in the <code>rank-profile</code> section of a schema, you can add - a ranking phase that will run in the stateless container after - gathering the best hits from the content node phases; this can be used - instead of or in addition to - <a href="#two-phase-ranking-on-content-nodes">second-phase</a> ranking. - The global-phase can also perform - <a href="#cross-hit-normalization-including-reciprocal-rank-fusion">cross-hit normalization</a> - to combine unrelated scoring methods. -</p> -<p id="globalphase-rerank-count"> - By default, global-phase ranking runs on the 100 globally best hits for - a schema; this can be tuned in the rank-profile using - <a href="../reference/schemas/schemas.html#globalphase-rank"> - <code>rerank-count</code> - </a> - or per-query using the - <a href="../reference/api/query.html#ranking.globalphase.rerankcount"> - <code>ranking.globalPhase.rerankCount</code> - </a> - query property. -</p> -<p> - This phase is optimized for inference with <a href="onnx">ONNX</a> models, taking - some input data from the document and some from the query, and - finding a score for how well they match. A typical use case is - re-ranking using <a href="cross-encoders">cross-encoders</a>. -</p> -<p> - It's possible to specify <em>gpu-device</em> to get GPU-accelerated computation of the - model as well. You can compute or re-shape the inputs to the - ONNX model in a function if necessary, and use the output in some - further calculation to compute the final score. -</p> -<p> - If you have large and complex expressions (including <a href="xgboost">xgboost</a>, <a href="lightgbm">lightgbm</a>), - instead of an ONNX model, it's more efficient to use the highly optimized - <a href="#two-phase-ranking-on-content-nodes">second-phase</a> - computation on content nodes. This is also true for sub-expressions that require lots of vector data, moving - vector data across the network is expensive. -</p> -{% include note.html content='You can force a sub-expression -to be computed on the content nodes by making it a function and -adding it to match-features' %} -<p id="myapp-with-global-model"> -By adding the feature to <a href="../reference/schemas/schemas.html#match-features">match-features</a> in the ranking profile, the -global-phase expression can re-use the function output without the complexity of transferring the data across the network -and performing inference in the stateless container (which is less optimized). -</p> -<pre> -schema myapp { - document myapp { - field per_doc_vector type tensor<float>(x[784]) { - indexing: attribute - } - … - } - … - rank-profile with-global-model inherits default { - inputs { - query(per_query_vector) tensor<float>(d0[32]) - } - first-phase { - expression: bm25(title) - } - - function my_expensive_function() { - expression: # some expensive computation better done on content nodes - } - - function per_doc_input() { - # simple reshaping: ONNX input wants the dimension name "d0" - expression: rename(attribute(per_doc_vector), x, d0) - } - onnx-model my_ranking_model { - file: files/my_ranking_model.onnx - input "model_input_1": per_doc_input - input "model_input_2": query(per_query_vector) - output "model_output_1": out - } - global-phase { - expression { - my_expensive_function + sum(onnx(my_ranking_model).out) - } - rerank-count: 50 - } - match-features { - my_expensive_function - } - } -} -</pre> -<p> - In the example above, <em>my_expensive_function</em> will be evaluated on the content nodes - for the 50 top-ranking documents from the first-phase so that the global-phase does not need to re-evaluate. -</p> - - - -<h2 id="cross-hit-normalization-including-reciprocal-rank-fusion">Cross-hit normalization including reciprocal rank fusion</h2> - -<p> - The ranking expressions configured for global-phase may perform - cross-hit normalization of input rank features or functions. This - is designed to make it easy to combine unrelated scoring methods - into one final relevance score. - The syntax looks like a special pseudo-function call: -</p> - <ul> - <li> <code>normalize_linear(<em>my_function_or_feature</em>)</code> </li> - <li> <code>reciprocal_rank(<em>my_function_or_feature</em>)</code> </li> - <li> <code>reciprocal_rank(<em>my_function_or_feature</em>, <em>k</em>)</code> </li> - <li> <code>reciprocal_rank_fusion(<em>score_1</em>, <em>score_2</em> ...)</code> </li> - </ul> -<p> - The normalization will be performed across the hits that global-phase - reranks (see <a href="#globalphase-rerank-count">configuration</a> above). - This means that first, the input (<em>my_function_or_feature</em>) - is computed or extracted from each hit that global-phase will - rerank; then the normalization step is applied; afterwards, when - computing the actual global-phase expression, the normalized output - is used. - As an example, assume some text fields with bm25 enabled, an ONNX - model (from the <a href="#myapp-with-global-model">example</a> in - the previous section), and a "popularity" numeric attribute: -</p> -<pre> - rank-profile with-normalizers inherits with-global-model { - function my_bm25_sum() { - expression: bm25(title) + bm25(abstract) - } - function my_model_out() { - expression: sum(onnx(my_ranking_model).out) - } - global-phase { - expression { - normalize_linear(my_bm25_sum) + normalize_linear(my_model_out) + normalize_linear(attribute(popularity)) - } - rerank-count: 200 - } - } -</pre> -<p> - The <code>normalize_linear</code> normalizer takes a single argument, which must be - a rank-feature or the name of a function. It computes the maximum and minimum - values of that input and scales linearly to the range [0, 1], basically using - the formula <code>output = (input - min) / (max - min)</code> -</p> -<p> - The <code>reciprocal_rank</code> normalizer takes one or two arguments; the first - must be a rank-feature or the name of a function, while the second (if present) - must be a numerical constant, called <code>k</code> with default value 60.0. - It sorts the input values and finds their <em>rank</em> (so the highest score gets - rank 1, next highest 2, and so on). The output from reciprocal_rank is computed - with the formula <code> output = 1.0 / (k + rank) </code>, so note that even the best - input only gets <code>1.0 / 61 = 0.016393</code> as output with the default k. -</p> -<p> - The <code>reciprocal_rank_fusion</code> pseudo-function takes at least two arguments - and expands to the sum of their <code>reciprocal_rank</code>; it's just a - convenient way to write -</p> -<pre class=code> -reciprocal_rank(a) + reciprocal_rank(b) + reciprocal_rank(c) -</pre> -<p>as</p> -<pre class=code> -reciprocal_rank_fusion(a,b,c) -</pre> -<p>for example.</p> -<p> - See the <a href="https://github.com/vespa-engine/sample-apps/tree/master/colbert">Simple Hybrid Search with ColBERT</a> - sample application for a practical example of using reciprocal rank fusion. -</p> - - -<h2 id="stateless-re-ranking">Stateless re-ranking</h2> -<p> - If the logic required is not suited for the <a href="#using-a-global-phase-expression">global-phase</a> above, - it's possible to write <a href="../applications/searchers.html">stateless searchers</a> - which can re-rank hits using any custom scoring function or model. - The searcher can also blend and re-order hits from multiple sources - when using <a href="../querying/federation.html">federation</a> of content sources. -</p> -<p> - The searcher might request rank features calculated by the content nodes to be returned - along with the hit fields using <a href="../applications/inspecting-structured-data.html">summary-features</a>. - The features returned can be configured in the <em>rank-profile</em> - as <a href="../reference/schemas/schemas.html#summary-features">summary-features</a>. -</p><p> - The number of <em>hits</em> is limited by the query api - <a href="../reference/api/query.html#hits">hits</a> parameter and - <a href="../reference/api/query.html#queryprofile">maxHits</a> setting. - The hits available for container-level re-ranking are the global top-ranking hits - after content nodes have retrieved and ranked the hits, - and global top-ranking hits have been found by merging the responses from the content nodes. -</p> - - - -<h2 id="top-k-query-operators">Top-K Query Operators</h2> -<p> - If the first-phase ranking function can be approximated as a simple linear function, - and the query mode is <em>weakAnd</em>, - the <a href="wand.html">Weak And/WAND</a> implementations in Vespa - allows avoiding fully evaluating all the documents matching the query with the <em>first-phase</em> function. - Instead, only the top-K hits using the internal wand scoring are exposed - to the <em>first-phase</em> ranking expression. -</p> -<p> - The <a href="../querying/nearest-neighbor-search">nearest neighbor search</a> operator is also a top-k - retrieval operator, and the two operators can be combined in the same query. -</p> - - - -<h2 id="choosing-phased-ranking-functions">Choosing phased ranking functions</h2> - -<p> -A good quality ranking expression will for most applications consume too much CPU -to be runnable on all retrieved or matched documents within the latency budget/SLA. -The application ranking function should hence in most cases be a second-phase function. -The task then becomes to find a first-phase function, -which correlates sufficiently well with the second-phase function. -</p> - - - -<h2 id="rank-phase-statistics">Rank phase statistics</h2> - -<p> - Use <a href="../reference/schemas/schemas.html#match-features">match-features</a> - and <a href="../reference/schemas/schemas.html#summary-features">summary-features</a> - to export detailed match- and rank-information per query. - This requires post-processing and aggregation in an external system for analysis. -</p> -<p> - To evaluate how well the document corpus matches the queries, - use <a href="../reference/schemas/schemas.html#mutate">mutable attributes</a> - to track how often each document survives each match- and ranking-phase. - This is aggregated per document and makes it easy to analyse using the query and grouping APIs in Vespa - - and no other processing/storage is required. -</p> -<p> - A mutable attribute is a number where an operation can be executed in 4 phases: -</p> -<ol> - <li>on-match</li> - <li>on-first-phase</li> - <li>on-second-phase</li> - <li>on-summary</li> -</ol> -<p> - The common use case is to increase the value by 1 for each execution. - With this, it is easy to evaluate the document's performance to the queries, - e.g. find the documents that appear in most queries, or the ones that never matched - - run a query and order by the mutable attribute. -</p> -{% include note.html content='The mutable attributes are just counters and memory-operations only - -the values might or might not survive content node restarts. -The values cannot be compared across nodes. -Use the values to assess relative document matching and ranking performance since Vespa start' %} -<p> - This example is based on the album-recommendation sample application - (see <a href="../basics/deploy-an-application.html">deploying an application</a>). - It uses 4 attributes that each track how many times a document participates in any of the 4 phases. - This is tracked only if using rank-profile <code>rank_albums_track</code> in the query: -</p> -<pre> -schema music { - - document music { - - field artist type string { - indexing: summary | index - } - - field album type string { - indexing: summary | index - } - - field year type int { - indexing: summary | attribute - } - - field category_scores type tensor<float>(cat{}) { - indexing: summary | attribute - } - - } - - field match_count type long { - indexing: attribute | summary - attribute: mutable - } - field first_phase_count type long { - indexing: attribute | summary - attribute: mutable - } - field second_phase_count type long { - indexing: attribute | summary - attribute: mutable - } - field summary_count type long { - indexing: attribute | summary - attribute: mutable - } - - fieldset default { - fields: artist, album - } - - rank-profile rank_albums inherits default { - first-phase { - expression: sum(query(user_profile) * attribute(category_scores)) - } - second-phase { - expression: attribute(year) - rerank-count: 1 - } - summary-features: attribute(year) - } - - rank-profile rank_albums_track inherits rank_albums { - mutate { - on-match { match_count += 1 } - on-first-phase { first_phase_count += 1 } - on-second-phase { second_phase_count += 1 } - on-summary { summary_count += 1 } # this only happens when summary-features are present! - } - } - - rank-profile rank_albums_reset_on_match inherits rank_albums { - mutate { - on-match { match_count = 0 } - } - } - rank-profile rank_albums_reset_on_first_phase inherits rank_albums { - mutate { - on-match { first_phase_count = 0 } - } - } - rank-profile rank_albums_reset_on_second_phase inherits rank_albums { - mutate { - on-match { second_phase_count = 0 } - } - } - rank-profile rank_albums_reset_on_summary inherits rank_albums { - mutate { - on-match { summary_count = 0 } - } - } -} -</pre> -<pre> -$ vespa query \ - "select * from music where album contains 'head'" \ - "ranking=rank_albums_track" -</pre> - - -<h3 id="usage">Usage</h3> -<p> - The framework is flexible in use; the normal use case is: -</p> -<ol> - <li> - Reset the mutable attribute on all content nodes - - use <a href="../reference/api/query.html#model.searchpath">searchPath</a> - to make sure all nodes are reset by sending a query using a rank profile that resets the value. - For each phase, run a query that <em>matches</em> all documents, and reset the attribute - e.g.: -<pre> -$ for phase in match first_phase second_phase summary; do \ - for node in {0..3}; do vespa query \ - "select * from music where true" \ - "ranking=rank_albums_reset_on_$phase" \ - "model.searchPath=$node/0"; \ - done \ - done -</pre> - Alternatively, run a query against a group - and verify that <a href="../reference/querying/default-result-format.html">coverage</a> is 100%. - <!-- ToDo: add note on what to do for the other mutable attributes --> - </li> - <li> - Run query load, using the tracking rank-profile, like <code>rank_albums_track</code> above - </li> - <li> - Run queries using <a href="../reference/querying/sorting-language.html">sorting</a> or <a href="../querying/grouping.html">grouping</a> - on the mutable attributes. - </li> -</ol> - -{% include note.html content='Make sure that only the relevant query load uses the tracking rank profile. -E.g. exclude monitoring queries / automation by using a separate ranking profile.' %} - -<p> - To initialize a mutable attribute with a different value than 0 when a document is PUT, use: -</p> -<pre> -field match_count type long { - indexing: 7 | to_long | attribute | summary # Initialized to 7 for a new document. The default is 0. - attribute: mutable -} -</pre> -<p> - To dump values fast, from memory only (assuming the schema has an <code>id</code> field): -</p> -<pre> -document-summary rank_phase_statistics { - summary id {} - summary match_count {} - summary first_phase_count {} - summary second_phase_count {} - summary summary_count {} -} -</pre> -<pre> -$ vespa query \ - "select * from music where true" \ - "presentation.summary=rank_phase_statistics" -</pre> diff --git a/en/ranking/phased-ranking.mdx b/en/ranking/phased-ranking.mdx new file mode 100644 index 0000000000..cee7d63842 --- /dev/null +++ b/en/ranking/phased-ranking.mdx @@ -0,0 +1,341 @@ +--- +title: "Phased Ranking" +--- + +Vespa allows expressing multiphased retrieval and ranking of documents. The retrieval phase is done close to the data in the content nodes, while the ranking phase(s) can be done in the content nodes or in the stateless container after scatter-and-gather from content node(s). The retrieval part of a phased pipeline is expressed by using the [query API](/en/querying/query-api) and the ranking part is expressed by using the [rank-profile](/en/reference/schemas/schemas#rank-profile) in the schema. + +- **Retrieval:**[_Top-k_ query operators](#top-k-query-operators), like [weakAnd/wand](/en/ranking/wand) and [nearest neighbor search](/en/querying/nearest-neighbor-search) allow retrieval with sublinear complexity. These query operators use simple scoring functions that are computationally cheap to evaluate over Vespa indexes. Using the expressiveness of the Vespa query language, developers can combine multiple retrievers in the query, and expose the union of retrieved documents into Vespa ranking phases. +- **Per node ranking:** The query specification retrieves documents and ranks them using declarative phases evaluated within the [content nodes](#two-phase-ranking-on-content-nodes): + - [first-phase expression](#first-phase-ranking-on-content-nodes); configured in [rank-profile](/en/reference/schemas/schemas#rank-profile). This phase is evaluated for _all_ hits retrieved by the query logic. This phase can also remove retrieved documents using [rank-score-drop-limit](/en/reference/schemas/schemas#rank-score-drop-limit). + - [second-phase ranking](#two-phase-ranking-on-content-nodes); configured in [rank-profile](/en/reference/schemas/schemas#rank-profile). Optionally re-rank the top-scoring hits from the first-phase ranking using a more complex expression. The [total-rerank-count](/en/reference/schemas/schemas#secondphase-total-rerank-count) sets a strict upper bound on the number of documents that are re-ranked in total over the nodes. + +- **Global ranking:** Following the per content node local ranking phases, re-ranking can occur after the content nodes have returned the merged top-scoring hits to the stateless container. This phase is specified using a [global-phase](#using-a-global-phase-expression) expression in the [rank-profile](/en/reference/schemas/schemas#rank-profile). Additionally, the global-phase can conduct [cross-hit normalization](#cross-hit-normalization-including-reciprocal-rank-fusion) to combine unrelated scoring methods. +- Finally, for customized ranking that is difficult to express in declarative phases, one can implement re-ranking using [reranking in searcher](/en/ranking/reranking-in-searcher). + +<Frame> + ![Ranking in 3 phases](/assets/img/phased-ranking.png) +</Frame> + +## First-phase ranking on content nodes + +Normally, you will always start by having one ranking expression that is evaluated on the content nodes. This is configured in the `rank-profile` section of a [schema](/en/reference/schemas/schemas#rank-profile) as a `first-phase` expression. This expression can use various [rank features](/en/ranking/ranking-expressions-features) with information extracted during the matching phase to evaluate the relevance of each document. The first-phase expression is computed for every document retrieved by the query. The computational cost is bounded by the number of documents exposed to the ranking phase on each content node multiplied with the complexity of the first-phase expression; therefore the expression needs to be simple and cheap to allow scaling to large amounts of retrieved docs. Alternatively, use retrieval operators that will expose only the top-k hits to the first-phase expression. + +## Two-phase ranking on content nodes + +While some use cases only require one (simple) first-phase ranking expression, for more advanced use cases it's possible to add a `second-phase` ranking expression in a [rank-profile](/en/reference/schemas/schemas#rank-profile) in the schema. This enables more expensive computations than would be feasible to run as a first-phase computation, with predictable upper bounds for the cost. + +By default, second-phase ranking (if specified) is evaluated for the 100 best hits from the first-phase ranking per content node. The number that is reranked over all nodes can be set by [total-rerank-count](/en/reference/schemas/schemas#secondphase-total-rerank-count). + +```js +schema myapp { + … + rank-profile title-freshness inherits default { + first-phase { + expression { + bm25(title) + 3*freshness(timestamp) + } + } + second-phase { + expression { + xgboost("my-model.json") + } + total-rerank-count: 50 + } + } +} +``` + +In this example, the first phase uses the text matching feature [bm25](/en/ranking/bm25) scoped to the _title_ field plus one of the built-in document [rank feature](/en/reference/ranking/rank-features) named _freshness_ over a _timestamp_ field which stores the epoch time in second resolution. For each content node, the top 50 hits from the first phase function is re-ranked using a trained [xgboost](/en/ranking/xgboost) model. + +## Using a global-phase expression + +Using a rank expression configured as a [global-phase](/en/reference/schemas/schemas#globalphase-rank) in the `rank-profile` section of a schema, you can add a ranking phase that will run in the stateless container after gathering the best hits from the content node phases; this can be used instead of or in addition to [second-phase](#two-phase-ranking-on-content-nodes) ranking. The global-phase can also perform [cross-hit normalization](#cross-hit-normalization-including-reciprocal-rank-fusion) to combine unrelated scoring methods. + +By default, global-phase ranking runs on the 100 globally best hits for a schema; this can be tuned in the rank-profile using [`rerank-count`](/en/reference/schemas/schemas#globalphase-rank) or per-query using the [`ranking.globalPhase.rerankCount`](/en/reference/api/query#ranking.globalphase.rerankcount) query property. + +This phase is optimized for inference with [ONNX](/en/ranking/onnx) models, taking some input data from the document and some from the query, and finding a score for how well they match. A typical use case is re-ranking using [cross-encoders](/en/ranking/cross-encoders). + +It's possible to specify _gpu-device_ to get GPU-accelerated computation of the model as well. You can compute or re-shape the inputs to the ONNX model in a function if necessary, and use the output in some further calculation to compute the final score. + +If you have large and complex expressions (including [xgboost](/en/ranking/xgboost), [lightgbm](/en/ranking/lightgbm)), instead of an ONNX model, it's more efficient to use the highly optimized [second-phase](#two-phase-ranking-on-content-nodes) computation on content nodes. This is also true for sub-expressions that require lots of vector data, moving vector data across the network is expensive. + +<Info> + **Note:** You can force a sub-expression to be computed on the content nodes by making it a function and adding it to match-features +</Info> + +By adding the feature to [match-features](/en/reference/schemas/schemas#match-features) in the ranking profile, the global-phase expression can re-use the function output without the complexity of transferring the data across the network and performing inference in the stateless container (which is less optimized). + +```js expandable +schema myapp { + document myapp { + field per_doc_vector type tensor<float>(x[784]) { + indexing: attribute + } + … + } + … + rank-profile with-global-model inherits default { + inputs { + query(per_query_vector) tensor<float>(d0[32]) + } + first-phase { + expression: bm25(title) + } + + function my_expensive_function() { + expression: # some expensive computation better done on content nodes + } + + function per_doc_input() { + # simple reshaping: ONNX input wants the dimension name "d0" + expression: rename(attribute(per_doc_vector), x, d0) + } + onnx-model my_ranking_model { + file: files/my_ranking_model.onnx + input "model_input_1": per_doc_input + input "model_input_2": query(per_query_vector) + output "model_output_1": out + } + global-phase { + expression { + my_expensive_function + sum(onnx(my_ranking_model).out) + } + rerank-count: 50 + } + match-features { + my_expensive_function + } + } +} +``` + +In the example above, _my\_expensive\_function_ will be evaluated on the content nodes for the 50 top-ranking documents from the first-phase so that the global-phase does not need to re-evaluate. + +## Cross-hit normalization including reciprocal rank fusion + +The ranking expressions configured for global-phase may perform cross-hit normalization of input rank features or functions. This is designed to make it easy to combine unrelated scoring methods into one final relevance score. The syntax looks like a special pseudo-function call: + +- `normalize_linear(my_function_or_feature)` +- `reciprocal_rank(my_function_or_feature)` +- `reciprocal_rank(my_function_or_feature, k)` +- `reciprocal_rank_fusion(score_1, score_2 ...)` + +The normalization will be performed across the hits that global-phase reranks (see [configuration](#globalphase-rerank-count) above). This means that first, the input (_my\_function\_or\_feature_) is computed or extracted from each hit that global-phase will rerank; then the normalization step is applied; afterwards, when computing the actual global-phase expression, the normalized output is used. As an example, assume some text fields with bm25 enabled, an ONNX model (from the [example](#myapp-with-global-model) in the previous section), and a "popularity" numeric attribute: + +```js expandable +rank-profile with-normalizers inherits with-global-model { + function my_bm25_sum() { + expression: bm25(title) + bm25(abstract) + } + function my_model_out() { + expression: sum(onnx(my_ranking_model).out) + } + global-phase { + expression { + normalize_linear(my_bm25_sum) + normalize_linear(my_model_out) + normalize_linear(attribute(popularity)) + } + rerank-count: 200 + } + } +``` + +The `normalize_linear` normalizer takes a single argument, which must be a rank-feature or the name of a function. It computes the maximum and minimum values of that input and scales linearly to the range [0, 1], basically using the formula `output = (input - min) / (max - min)` + +The `reciprocal_rank` normalizer takes one or two arguments; the first must be a rank-feature or the name of a function, while the second (if present) must be a numerical constant, called `k` with default value 60.0. It sorts the input values and finds their _rank_ (so the highest score gets rank 1, next highest 2, and so on). The output from reciprocal\_rank is computed with the formula ` output = 1.0 / (k + rank) `, so note that even the best input only gets `1.0 / 61 = 0.016393` as output with the default k. + +The `reciprocal_rank_fusion` pseudo-function takes at least two arguments and expands to the sum of their `reciprocal_rank`; it's just a convenient way to write + +```js +reciprocal_rank(a) + reciprocal_rank(b) + reciprocal_rank(c) +``` + +as + +```js +reciprocal_rank_fusion(a,b,c) +``` + +for example. + +See the [Simple Hybrid Search with ColBERT](https://github.com/vespa-engine/sample-apps/tree/master/colbert) sample application for a practical example of using reciprocal rank fusion. + +## Stateless re-ranking + +If the logic required is not suited for the [global-phase](#using-a-global-phase-expression) above, it's possible to write [stateless searchers](/en/applications/searchers) which can re-rank hits using any custom scoring function or model. The searcher can also blend and re-order hits from multiple sources when using [federation](/en/querying/federation) of content sources. + +The searcher might request rank features calculated by the content nodes to be returned along with the hit fields using [summary-features](/en/applications/inspecting-structured-data). The features returned can be configured in the _rank-profile_ as [summary-features](/en/reference/schemas/schemas#summary-features). + +The number of _hits_ is limited by the query api [hits](/en/reference/api/query#hits) parameter and [maxHits](/en/reference/api/query#queryprofile) setting. The hits available for container-level re-ranking are the global top-ranking hits after content nodes have retrieved and ranked the hits, and global top-ranking hits have been found by merging the responses from the content nodes. + +## Top-K Query Operators + +If the first-phase ranking function can be approximated as a simple linear function, and the query mode is _weakAnd_, the [Weak And/WAND](/en/ranking/wand) implementations in Vespa allows avoiding fully evaluating all the documents matching the query with the _first-phase_ function. Instead, only the top-K hits using the internal wand scoring are exposed to the _first-phase_ ranking expression. + +The [nearest neighbor search](/en/querying/nearest-neighbor-search) operator is also a top-k retrieval operator, and the two operators can be combined in the same query. + +## Choosing phased ranking functions + +A good quality ranking expression will for most applications consume too much CPU to be runnable on all retrieved or matched documents within the latency budget/SLA. The application ranking function should hence in most cases be a second-phase function. The task then becomes to find a first-phase function, which correlates sufficiently well with the second-phase function. + +## Rank phase statistics + +Use [match-features](/en/reference/schemas/schemas#match-features) and [summary-features](/en/reference/schemas/schemas#summary-features) to export detailed match- and rank-information per query. This requires post-processing and aggregation in an external system for analysis. + +To evaluate how well the document corpus matches the queries, use [mutable attributes](/en/reference/schemas/schemas#mutate) to track how often each document survives each match- and ranking-phase. This is aggregated per document and makes it easy to analyse using the query and grouping APIs in Vespa - and no other processing/storage is required. + +A mutable attribute is a number where an operation can be executed in 4 phases: + +1. on-match +2. on-first-phase +3. on-second-phase +4. on-summary + +The common use case is to increase the value by 1 for each execution. With this, it is easy to evaluate the document's performance to the queries, e.g. find the documents that appear in most queries, or the ones that never matched - run a query and order by the mutable attribute. + +<Info> + **Note:** The mutable attributes are just counters and memory-operations only - the values might or might not survive content node restarts. The values cannot be compared across nodes. Use the values to assess relative document matching and ranking performance since Vespa start +</Info> + +This example is based on the album-recommendation sample application (see [deploying an application](/en/basics/deploy-an-application)). It uses 4 attributes that each track how many times a document participates in any of the 4 phases. This is tracked only if using rank-profile `rank_albums_track` in the query: + +```js expandable +schema music { + + document music { + + field artist type string { + indexing: summary | index + } + + field album type string { + indexing: summary | index + } + + field year type int { + indexing: summary | attribute + } + + field category_scores type tensor<float>(cat{}) { + indexing: summary | attribute + } + + } + + field match_count type long { + indexing: attribute | summary + attribute: mutable + } + field first_phase_count type long { + indexing: attribute | summary + attribute: mutable + } + field second_phase_count type long { + indexing: attribute | summary + attribute: mutable + } + field summary_count type long { + indexing: attribute | summary + attribute: mutable + } + + fieldset default { + fields: artist, album + } + + rank-profile rank_albums inherits default { + first-phase { + expression: sum(query(user_profile) * attribute(category_scores)) + } + second-phase { + expression: attribute(year) + rerank-count: 1 + } + summary-features: attribute(year) + } + + rank-profile rank_albums_track inherits rank_albums { + mutate { + on-match { match_count += 1 } + on-first-phase { first_phase_count += 1 } + on-second-phase { second_phase_count += 1 } + on-summary { summary_count += 1 } # this only happens when summary-features are present! + } + } + + rank-profile rank_albums_reset_on_match inherits rank_albums { + mutate { + on-match { match_count = 0 } + } + } + rank-profile rank_albums_reset_on_first_phase inherits rank_albums { + mutate { + on-match { first_phase_count = 0 } + } + } + rank-profile rank_albums_reset_on_second_phase inherits rank_albums { + mutate { + on-match { second_phase_count = 0 } + } + } + rank-profile rank_albums_reset_on_summary inherits rank_albums { + mutate { + on-match { summary_count = 0 } + } + } +} +``` + +```bash +$ vespa query \ + "select * from music where album contains 'head'" \ + "ranking=rank_albums_track" +``` + +### Usage + +The framework is flexible in use; the normal use case is: + +1. Reset the mutable attribute on all content nodes - use [searchPath](/en/reference/api/query#model.searchpath) to make sure all nodes are reset by sending a query using a rank profile that resets the value. For each phase, run a query that _matches_ all documents, and reset the attribute - e.g.: +```bash +$ for phase in match first_phase second_phase summary; do \ + for node in {0..3}; do vespa query \ + "select * from music where true" \ + "ranking=rank_albums_reset_on_$phase" \ + "model.searchPath=$node/0"; \ + done \ + done +``` + Alternatively, run a query against a group and verify that [coverage](/en/reference/querying/default-result-format) is 100%. +2. Run query load, using the tracking rank-profile, like `rank_albums_track` above +3. Run queries using [sorting](/en/reference/querying/sorting-language) or [grouping](/en/querying/grouping) on the mutable attributes. + +<Info> +**Note:** Make sure that only the relevant query load uses the tracking rank profile. E.g. exclude monitoring queries / automation by using a separate ranking profile. +</Info> + +To initialize a mutable attribute with a different value than 0 when a document is PUT, use: + +```js +field match_count type long { + indexing: 7 | to_long | attribute | summary # Initialized to 7 for a new document. The default is 0. + attribute: mutable +} +``` + +To dump values fast, from memory only (assuming the schema has an `id` field): + +```js +document-summary rank_phase_statistics { + summary id {} + summary match_count {} + summary first_phase_count {} + summary second_phase_count {} + summary summary_count {} +} +``` + +```bash +$ vespa query \ + "select * from music where true" \ + "presentation.summary=rank_phase_statistics" +``` diff --git a/en/ranking/ranking-expressions-features.html b/en/ranking/ranking-expressions-features.html deleted file mode 100644 index 4f6234b1f9..0000000000 --- a/en/ranking/ranking-expressions-features.html +++ /dev/null @@ -1,475 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Ranking Expressions and Features" -redirect_from: -- /en/ranking-expressions-features.html ---- - -<p> -Read the <a href="../basics/ranking.html">ranking introduction</a> first. -This guide is about <a href="../reference/ranking/ranking-expressions.html">ranking expressions</a> -and <a href="../reference/ranking/rank-features.html">rank features</a>, find guides and examples below. -</p> - - - -<h2 id="ranking-expressions">Ranking expressions</h2> - -<p> -Vespa uses <a href="../reference/ranking/ranking-expressions.html">ranking expressions</a> -to rank documents matching a query, computing a rank score per document. -A ranking expression is stored in a <em>rank profile</em>. -</p> - -<p> -Ranking expressions are mathematical functions. -The function may contain anything from a single reference to a built-in feature to a machine-learned model. -Ranking expressions support the usual operators and functions, -as well as an <em>if</em> function - enabling decision trees and conditional business logic. -They support a comprehensive set of <a href="../reference/ranking/tensor.html">tensor functions</a>, -which allows expressing machine-learned functions such as deep neural nets. -Refer to <a href="multivalue-query-operators.html">multivalue query operators</a> -for details on using dot products, tensors and wand. -</p> - -<p> -Ranking is most often the resource driver - this is where is application's logic is implemented. -Use <a href="phased-ranking.html">two-phase ranking</a> to optimize, -using an inexpensive <em>first-phase</em> ranking to eliminate the lowest ranked candidates, -then focus the resources on a strong <em>second-phase</em> ranking. -</p> - -<p> -Ranking expressions can be <em>handwritten</em> - -works well if the ranking is well-defined enough to be easily mappable into a ranking expression. -Alternatively, make the ranking expression automatically using <em>machine learning</em>. -Ranking expressions can be large, and can be imported using -<a href="../reference/schemas/schemas.html#expression">file:filename</a>. -</p> - - - -<h2 id="rank-features">Rank features</h2> -<p> -The primitive values used in ranking expressions -are called <a href="../reference/ranking/rank-features.html">rank features</a>. -Rank features can be <a href="tensor-user-guide.html">tensors</a>, -<a href="../searching-multi-valued-fields">multivalue fields</a> or scalars, and one of: -</p> -<ul> - <li>Constants set in the application package</li> - <li>Values sent with the query or set in the document</li> - <li>Computed by Vespa from the query and the document - to provide information about how well the query matched the document</li> -</ul> -<p> -Vespa's <a href="../reference/ranking/rank-features.html">rank feature set</a> -contains a large set of low level features, as well as some higher level features. -If automated training is used, all features can often just be handed to the training algorithm -to let it choose which ones to use. -Depending on the algorithm, it can be a good idea to leave out the un-normalized features -to avoid spending learning power on having to learn to normalize these features -and determine that they really represent the same information as some of the normalized features. -</p><p> -Include <a href="https://github.com/vespa-engine/system-test/blob/master/tests/search/rankfeatures/dump.txt">default rank features</a> -in query results by adding -<a href="../reference/api/query.html#ranking.listfeatures">ranking.listFeatures</a> to the query. -This is useful for tasks like recording the rank feature values for automated training - -learn more in the <a href="../learn/tutorials/text-search-ml">tutorial</a>. -If more rank features than is available in the default set is wanted, -they can be added to the set in the <a href="../reference/schemas/schemas.html#rank-features">rank profile</a>: -</p> -<pre> -rank-features: feature1 feature2 … -</pre> -<p>It is also possible to control which features to dump - add this to the rank profile:</p> -<pre> -ignore-default-rank-features -</pre> -<p> -This will make the explicitly listed rank features the only ones dumped when requesting rankfeatures in the query. -</p> - - -<h3 id="normalization">Normalization</h3> -<p> -The rank features provided includes both features normalized to the range 0-1, -and un-normalized features like counts and positions. -Whenever possible, prefer the normalized features. -They capture the same information (and more), -but are easier to use because they can be combined more easily with other features. -In addition, try to write ranking expressions such that the combined rank score is also normalized, -for example by taking averages not sums. -The resulting normalized rank scores makes it possible to implement relevance based blending, -search assistance triggering when there are no good hits, and so on. -</p> - - -<h3 id="configuration">Configuration</h3> -<p> -Some features, most notably the <a href="../reference/ranking/rank-features.html#field-match-features-normalized">fieldMatch</a> features, -have configuration parameters that enables the feature calculation to be tweaked per field for performance or relevance. -Feature configuration values are set by adding to the rank profile: -</p> -<pre> -rank-properties { - featureName.configurationProperty: "value" -} -</pre> -<p>The values are set per field, like:</p> -<pre> -rank-properties { - fieldMatch(title).maxAlternativeSegmentations: 10 - fieldMatch(title).maxOccurrences: 5 - fieldMatch(description).maxOccurrences: 20 -} -</pre> -<p> -Refer to the -<a href="../reference/ranking/rank-feature-configuration.html">rank feature configuration</a> reference. -</p> - - -<h3 id="feature-contribution-functions">Feature contribution functions</h3> - -<p> -Vespa ranking features are linear. -For example, the <a href="../reference/ranking/rank-features.html#fieldMatch(name).earliness">earliness</a> feature -is 0 if the match is at the end of the field, -1 if the match is at the start of the field, -and 0.5 if the match is exactly in the middle of the field. -In many cases, the contribution of a feature should not be linear with its "goodness". -For example, <em>earliness</em> could decay quickly in the beginning and slowly at the end of the field. -This from the intuition that it matters more if the match is of the first or the twentieth word in the field, -but it doesn't matter as much if the match is at the thousands or thousand-and-twentieths. -</p><p> -To achieve this, pass the feature value through a function -which turns the line into a curve matching the intent. -This is easiest with normalized fields. -The function begins and ends in the same point, f(0)=0 and f(1)=1, but which curves in between. -To get the effect described above, a curve which starts almost flat and ends steep works - example: -</p> -<pre> -pow(0-x,2) -</pre> -<p> -The second number decides how pronounced the curving is. -A larger number will make changes to higher x values even more important -relative to the same change to lower x values. -</p> - - -<h3 id="dumping-rank-features-for-specific-documents">Dumping rank features for specific documents</h3> - -<p> -For a training set containing judgements for certain documents, -it is useful to select those documents in the query by adding a term matching the document ID, -but without impacting the values of any rank features. -To do this, add that term with <a href="../reference/querying/yql.html#ranked">ranked</a> set to false: -</p> -<pre> -select * from mydocumenttype where myidfield contains ({ranked: false}"mydocumentid" and ...) -</pre> - - -<h3 id="accessing-feature-function-values-in-results">Accessing feature/function values in results</h3> - -<p> - Any feature can be returned in the hit producing it by adding it to the list of - <a href="../reference/schemas/schemas.html#summary-features">summary-features</a> of the rank profile. - As all functions are features this allows the result of any computation to be accessible in results. Example: -</p> -<pre> -rank-profile test { - - summary-features: tensor_join join_sum - - function tensor_join() { - expression: attribute(my_tensor_field) * query(my_query_tensor) - } - - function join_sum() { - expression: sum(tensor_join()) - } - -} -</pre> -<p>The results of these functions will be available in the Hits of the result as follows:</p> -<pre>{% highlight java %} -import com.yahoo.search.result.FeatureData; - - FeatureData featureData = (FeatureData)hit.getField("summaryfeatures"); - Tensor tensor_join_value = featureData.getTensor("rankingExpression(tensor_join)"); - double join_sum_value = featureData.getDouble("rankingExpression(join_sum)"); -{% endhighlight %}</pre> -<p> - Do further computation on the returned tensors, - such as e.g <code>Tensor larger = tensor_join_value.map((value) -> 3 * value)</code>. -</p> -<p> - If also leveraging <a href="../applications/searchers.html#multiphase-searching">multiphase searching</a>, - it is possible to get rank features returned in the first phase using - <a href="../reference/schemas/schemas.html#match-features">match-features</a>. - This pre-populates the <a href="../reference/querying/default-result-format.html#matchfeatures">matchfeatures</a> field. - The effects which can be observed in the results are the same, so this may seem like the same functionality, - but the performance trade-off is different: -</p> -<ul> - <li>The expressions in <em>match-features</em> must be computed for all hits returned - in the first phase, before selecting which hits to <em>fill</em>. But that also means it's - possible to use the <code>matchfeatures</code> field to select which hits to keep and which to - remove before calling <code>fill()</code> at all.</li> - <li>The expressions in <em>summary-features</em> are not available before the <em>fill</em> phase, - but only need to be calculated for those hits that are actually filled.</li> -</ul> -<p> - The difference is most pronounced when the corpus is divided onto many content nodes. - Consider a case with 7 content nodes, fetching 100 matches from each. - These are merged (by relevance score) into a list of 700 hits, - and the 100 with the best relevance are selected and <em>filled</em>. - If you use <em>match-features</em>, they need to be calculated for all 700 hits. - Compare with <em>summary-features</em>, where only the final 100 hits need to be considered for calculating those. -</p> - - - -<h2 id="conditional-expressions">Conditional expressions</h2> - -<p> -Ranking expressions support conditional logic to choose between different sub-expressions based on document attributes, query parameters, or other features. -This enables ranking to adapt to different document types, user segments, or business rules within a single rank profile. -</p> - - -<h3 id="the-if-function">The if function</h3> -<span id="the-if-function-and-string-equality-tests"></span> - -<p> -<code>if</code> can be used for other purposes than encoding MLR trained decision trees. -One use is to choose different ranking functions for different types of documents in the same search. -Ranking expressions are able to do string equality tests, -so to choose between different ranking sub-functions based on the value of a string attribute (say, "category"), -use an expression like: -</p> -<pre> -if (attribute(category)=="restaurant",<em>…restaurant function</em>, if (attribute(category)=="hotel",<em>…hotel function</em>, …)) -</pre> -<p> -This method is also used automatically when multiple schemas are deployed to the same cluster, -and all is included in the same query to choose the ranking expression from the correct schema for each document. -</p><p> -By using <code>if</code> functions, one can also implement strict tiering, -ensuring that documents having some criterion always gets a higher score than the other documents. Example: -</p> -<pre> -if (fieldMatch(business).fieldCompleteness==1, 0.8+document.distance*0.2, - if (attribute(category)=="shop", 0.6+fieldMatch(title)*0.2, - match*attribute(popularity)*0.6 ) -</pre> -<p> -This function puts all exact matches on business names first, -sorted by geographical distance, followed by all shops sorted by title match, -followed by everything else sorted by the overall match quality and popularity. -</p> -<p> - Also see <a href="multivalue-query-operators.html#pin-results-example">pin results</a> - for a comprehensive examples of using a tiered ranking function to pin queries and results. -</p> - - -<h3 id="the-switch-function">The switch function</h3> -<p> -When comparing many values against the same discriminant value, -the <code>switch</code> function provides a more readable alternative to deeply nested <code>if</code> statements. -For example, the nested if expression above can be written more clearly as: -</p> -<pre> -switch (attribute(category)) { - case "restaurant": <em>…restaurant function</em>, - case "hotel": <em>…hotel function</em>, - default: <em>…default function</em> -} -</pre> -<p> -Use <code>switch</code> when testing a single expression for equality against multiple values. -Continue using <code>if</code> for different comparison operators or when each condition tests different expressions, -such as in the tiering example above. -See the <a href="../reference/ranking/ranking-expressions.html#the-switch-function">switch function reference</a> for details. -</p> - - -<h2 id="using-constants">Using constants</h2> - -<p> -Ranking expressions can refer to constants defined in a <code>constants</code> clause: -</p> -<pre> -first-phase { - expression: myConst1 + myConst2 -} -constants { - myConst1: 1.5 - myConst2: 2.5 - ... -} -</pre> -<p> -Constants lists are inherited and can be overridden in sub-profiles. -This is useful to create a set of rank profiles that use the same broad ranking -but differs by constants values. -</p><p> -For performance, always prefer constants to query variables (see below) -whenever the constant values to use can be enumerated in a set of rank profiles. -Constants are applied to ranking expressions at configuration time, -and the resulting constant parts of expressions calculated, -which may lead to reduced execution cost, especially with tensor constants. -</p> - - -<h2 id="using-query-variables">Using query variables</h2> - -<p> -As ranking expressions can refer to any feature by name, -one can use <a href="../reference/ranking/rank-features.html#feature-list">query features</a> as ranking variables. -These variables can be used for example to allow the query -to specify the degree of importance to various parts of a ranking expression, -or to quickly search large parameter spaces to find a good ranking, -by trying different values in each query. -These variables can be assigned default values in the <a href="../reference/schemas/schemas.html#rank-profile">rank profile</a> -by adding: -</p> -<pre> -inputs { - query(myvalue) double: 0.5 -} -</pre> -<p>to the rank profile. - -These variables can then be overridden in the query by adding:</p> -<pre> -input.query(myvalue)=0.1 -</pre> -<p> -to it - see the <a href="../reference/api/query.html#ranking.features">Query API</a>. -</p> - - - - -<h2 id="query-feature-types">Query feature types</h2> - -<p>The default type of all features are scalar. To use query feature <em>tensors</em> we must -<a href="../reference/schemas/schemas.html#inputs">define their type in the rank profile</a>.</p> - -<p>Without the correct tensor type, a passed query feature is handled as a string to be converted to a scalar, -which will <em>not give an error but will produce incorrect results</em>.</p> - -<p> -Tensors can be passed in requests using the <a href="../reference/ranking/tensor.html#tensor-literal-form">tensor literal form</a>, -for example: -</p> -<pre> -input.query(user_profile)=%7B%7Bcat%3Apop%7D%3A0.8%2C%7Bcat%3Arock%7D%3A0.2%2C%7Bcat%3Ajazz%7D%3A0.1%7D -</pre> -<p> -However, it is usually preferable instead to create them in a <a href="../applications/searchers.html">Searcher</a>. -Set the tensor value using the -<a href="https://github.com/vespa-engine/vespa/blob/master/container-search/src/main/java/com/yahoo/search/query/ranking/RankFeatures.java"> -RankFeatures</a> instance associated with -<a href="https://github.com/vespa-engine/vespa/blob/master/container-search/src/main/java/com/yahoo/search/Query.java"> -Query</a> instance. This example makes a tensor with a single cell with value 500: -</p> -<pre>{% highlight java%} -package com.yahoo.example; - -import com.yahoo.search.Query; -import com.yahoo.search.Result; -import com.yahoo.search.Searcher; -import com.yahoo.search.searchchain.Execution; -import com.yahoo.tensor.Tensor; -import com.yahoo.tensor.TensorType; - -public class TensorInQuerySearcher extends Searcher { - @Override - public Result search(Query query, Execution execution) { - // The Tensor type must also be in the application package as described in the above section - query.getRanking().getFeatures().put("query(tensor_feature)", - Tensor.Builder.of(TensorType.fromSpec("tensor<float>(x{})")).cell().label("x", "foo").value(500).build()); - return execution.search(query); - } -} -{% endhighlight %}</pre> -Refer to the <a href="https://javadoc.io/doc/com.yahoo.vespa/vespajlib/latest/com/yahoo/tensor/Tensor.html">Tensor</a> -Java API for details on how to construct tensors programmatically. - - - -<h2 id="function-snippets">Function snippets</h2> - -<p> -When using machine learned ranking, we are searching a function space -which is much more limited than the space of functions supported by ranking expressions. -We can increase the space of functions available to MLR -because the primitive features used in MLR training do not need to be primitive features in Vespa - -they can just as well be ranking expression snippets. -If there are certain mathematical combinations of features believed to be useful in an application, -these can be pre-calculated from the actual primitive features of Vespa -and given to MLR as primitives. -Such primitives can then be replaced textually by the corresponding ranking expression snippet, -before the learned expression is deployed on Vespa. -</p><p> -Vespa supports <a href="../reference/schemas/schemas.html#function-rank">expression functions</a>. -Functions having zero arguments can be used as summary- and rank-features. -For example, the function "myfeature": -</p> -<pre> -rank-profile myrankprofile inherits default { - function myfeature() { - expression: fieldMatch(title).completeness * pow(0 - fieldMatch(title).earliness, 2) - } -} -</pre> -<p>becomes available as a feature as follows:</p> -<pre> -summary-features { - myfeature -} -</pre> - - - -<h2 id="tracking-relevance-variations-over-time">Tracking relevance variations over time</h2> - -<p> -Vespa comes with a few simple metrics for relevance -that enables applications to see how relevance changes over time, -either as a result of changes to how relevance is computed, -changes to query construction, changes to the content ingested, -or as a result of changing user behavior. -</p><p> -The relevance metrics are <code>relevance.at_1</code>, -<code>relevance.at_3</code> and <code>relevance.at_10</code>. -See <a href="../operations/metrics.html">metrics</a> for more information. -</p> - - - -<h2 id="examples">Examples</h2> -<p> -If the user is underage, assign 0 to adult content -and use the average of match quality in the title field and popularity among kids. -If the user is not, use the match quality in the title field: -</p> -<pre> -if ( query(userage) < 18, - if ( attribute(adultness) > 0.1, 0 , (fieldMatch(title)+attribute(kidspopularity)) / 2 ), - fieldMatch(title) ) -</pre> -<p> -Use a weighted average of the match quality in some fields, -multiplied by 1-exp of the document age: -</p> -<pre> -( 10*fieldMatch(title) + 5*fieldMatch(description) + - 7*attributeMatch(tags).normalizedWeight ) /22 * ( 1 - age(creationtime) ) -</pre> diff --git a/en/ranking/ranking-expressions-features.mdx b/en/ranking/ranking-expressions-features.mdx new file mode 100644 index 0000000000..3443eec417 --- /dev/null +++ b/en/ranking/ranking-expressions-features.mdx @@ -0,0 +1,280 @@ +--- +title: "Ranking Expressions and Features" +--- + +Read the [ranking introduction](/en/basics/ranking) first. This guide is about [ranking expressions](/en/reference/ranking/ranking-expressions) and [rank features](/en/reference/ranking/rank-features), find guides and examples below. + +## Ranking expressions + +Vespa uses [ranking expressions](/en/reference/ranking/ranking-expressions) to rank documents matching a query, computing a rank score per document. A ranking expression is stored in a _rank profile_. + +Ranking expressions are mathematical functions. The function may contain anything from a single reference to a built-in feature to a machine-learned model. Ranking expressions support the usual operators and functions, as well as an _if_ function - enabling decision trees and conditional business logic. They support a comprehensive set of [tensor functions](/en/reference/ranking/tensor), which allows expressing machine-learned functions such as deep neural nets. Refer to [multivalue query operators](/en/ranking/multivalue-query-operators) for details on using dot products, tensors and wand. + +Ranking is most often the resource driver - this is where is application's logic is implemented. Use [two-phase ranking](/en/ranking/phased-ranking) to optimize, using an inexpensive _first-phase_ ranking to eliminate the lowest ranked candidates, then focus the resources on a strong _second-phase_ ranking. + +Ranking expressions can be _handwritten_ - works well if the ranking is well-defined enough to be easily mappable into a ranking expression. Alternatively, make the ranking expression automatically using _machine learning_. Ranking expressions can be large, and can be imported using [file:filename](/en/reference/schemas/schemas#expression). + +## Rank features + +The primitive values used in ranking expressions are called [rank features](/en/reference/ranking/rank-features). Rank features can be [tensors](/en/ranking/tensor-user-guide), [multivalue fields](/en/querying/searching-multivalue-fields) or scalars, and one of: + +- Constants set in the application package +- Values sent with the query or set in the document +- Computed by Vespa from the query and the document to provide information about how well the query matched the document + +Vespa's [rank feature set](/en/reference/ranking/rank-features) contains a large set of low level features, as well as some higher level features. If automated training is used, all features can often just be handed to the training algorithm to let it choose which ones to use. Depending on the algorithm, it can be a good idea to leave out the un-normalized features to avoid spending learning power on having to learn to normalize these features and determine that they really represent the same information as some of the normalized features. + +Include [default rank features](https://github.com/vespa-engine/system-test/blob/master/tests/search/rankfeatures/dump.txt) in query results by adding [ranking.listFeatures](/en/reference/api/query#ranking.listfeatures) to the query. This is useful for tasks like recording the rank feature values for automated training - learn more in the [tutorial](/en/learn/tutorials/text-search-ml). If more rank features than is available in the default set is wanted, they can be added to the set in the [rank profile](/en/reference/schemas/schemas#rank-features): + +```bash +rank-features: feature1 feature2 … +``` + +It is also possible to control which features to dump - add this to the rank profile: + +```bash +ignore-default-rank-features +``` + +This will make the explicitly listed rank features the only ones dumped when requesting rankfeatures in the query. + +### Normalization + +The rank features provided includes both features normalized to the range 0-1, and un-normalized features like counts and positions. Whenever possible, prefer the normalized features. They capture the same information (and more), but are easier to use because they can be combined more easily with other features. In addition, try to write ranking expressions such that the combined rank score is also normalized, for example by taking averages not sums. The resulting normalized rank scores makes it possible to implement relevance based blending, search assistance triggering when there are no good hits, and so on. + +### Configuration + +Some features, most notably the [fieldMatch](/en/reference/ranking/rank-features#fieldMatch(name)) features, have configuration parameters that enables the feature calculation to be tweaked per field for performance or relevance. Feature configuration values are set by adding to the rank profile: + +```bash +rank-properties { + featureName.configurationProperty: "value" +} +``` + +The values are set per field, like: + +```js +rank-properties { + fieldMatch(title).maxAlternativeSegmentations: 10 + fieldMatch(title).maxOccurrences: 5 + fieldMatch(description).maxOccurrences: 20 +} +``` + +Refer to the [rank feature configuration](/en/reference/ranking/rank-feature-configuration) reference. + +### Feature contribution functions + +Vespa ranking features are linear. For example, the [earliness](/en/reference/ranking/rank-features#fieldMatch(name).earliness) feature is 0 if the match is at the end of the field, 1 if the match is at the start of the field, and 0.5 if the match is exactly in the middle of the field. In many cases, the contribution of a feature should not be linear with its "goodness". For example, _earliness_ could decay quickly in the beginning and slowly at the end of the field. This from the intuition that it matters more if the match is of the first or the twentieth word in the field, but it doesn't matter as much if the match is at the thousands or thousand-and-twentieths. + +To achieve this, pass the feature value through a function which turns the line into a curve matching the intent. This is easiest with normalized fields. The function begins and ends in the same point, f(0)=0 and f(1)=1, but which curves in between. To get the effect described above, a curve which starts almost flat and ends steep works - example: + +```bash +pow(0-x,2) +``` + +The second number decides how pronounced the curving is. A larger number will make changes to higher x values even more important relative to the same change to lower x values. + +### Dumping rank features for specific documents + +For a training set containing judgements for certain documents, it is useful to select those documents in the query by adding a term matching the document ID, but without impacting the values of any rank features. To do this, add that term with [ranked](/en/reference/querying/yql#ranked) set to false: + +```bash +select * from mydocumenttype where myidfield contains ({ranked: false}"mydocumentid" and ...) +``` + +### Accessing feature/function values in results + +Any feature can be returned in the hit producing it by adding it to the list of [summary-features](/en/reference/schemas/schemas#summary-features) of the rank profile. As all functions are features this allows the result of any computation to be accessible in results. Example: + +```bash +rank-profile test { + + summary-features: tensor_join join_sum + + function tensor_join() { + expression: attribute(my_tensor_field) * query(my_query_tensor) + } + + function join_sum() { + expression: sum(tensor_join()) + } + +} +``` + +The results of these functions will be available in the Hits of the result as follows: + +```js +import com.yahoo.search.result.FeatureData; + + FeatureData featureData = (FeatureData)hit.getField("summaryfeatures"); + Tensor tensor_join_value = featureData.getTensor("rankingExpression(tensor_join)"); + double join_sum_value = featureData.getDouble("rankingExpression(join_sum)"); +``` + +Do further computation on the returned tensors, such as e.g `Tensor larger = tensor_join_value.map((value) -> 3 * value)`. + +If also leveraging [multiphase searching](/en/applications/searchers#multiphase-searching), it is possible to get rank features returned in the first phase using [match-features](/en/reference/schemas/schemas#match-features). This pre-populates the [matchfeatures](/en/reference/querying/default-result-format#matchfeatures) field. The effects which can be observed in the results are the same, so this may seem like the same functionality, but the performance trade-off is different: + +- The expressions in _match-features_ must be computed for all hits returned in the first phase, before selecting which hits to _fill_. But that also means it's possible to use the `matchfeatures` field to select which hits to keep and which to remove before calling `fill()` at all. +- The expressions in _summary-features_ are not available before the _fill_ phase, but only need to be calculated for those hits that are actually filled. + +The difference is most pronounced when the corpus is divided onto many content nodes. Consider a case with 7 content nodes, fetching 100 matches from each. These are merged (by relevance score) into a list of 700 hits, and the 100 with the best relevance are selected and _filled_. If you use _match-features_, they need to be calculated for all 700 hits. Compare with _summary-features_, where only the final 100 hits need to be considered for calculating those. + +## Conditional expressions + +Ranking expressions support conditional logic to choose between different sub-expressions based on document attributes, query parameters, or other features. This enables ranking to adapt to different document types, user segments, or business rules within a single rank profile. + +### The if function + +`if` can be used for other purposes than encoding MLR trained decision trees. One use is to choose different ranking functions for different types of documents in the same search. Ranking expressions are able to do string equality tests, so to choose between different ranking sub-functions based on the value of a string attribute (say, "category"), use an expression like: + +```py +if (attribute(category)=="restaurant",_…restaurant function_, if (attribute(category)=="hotel",_…hotel function_, …)) +``` + +This method is also used automatically when multiple schemas are deployed to the same cluster, and all is included in the same query to choose the ranking expression from the correct schema for each document. + +By using `if` functions, one can also implement strict tiering, ensuring that documents having some criterion always gets a higher score than the other documents. Example: + +```py +if (fieldMatch(business).fieldCompleteness==1, 0.8+document.distance*0.2, + if (attribute(category)=="shop", 0.6+fieldMatch(title)*0.2, + match*attribute(popularity)*0.6 ) +``` + +This function puts all exact matches on business names first, sorted by geographical distance, followed by all shops sorted by title match, followed by everything else sorted by the overall match quality and popularity. + +Also see [pin results](/en/ranking/multivalue-query-operators#pin-results-example) for a comprehensive examples of using a tiered ranking function to pin queries and results. + +### The switch function + +When comparing many values against the same discriminant value, the `switch` function provides a more readable alternative to deeply nested `if` statements. For example, the nested if expression above can be written more clearly as: + +```py +switch (attribute(category)) { + case "restaurant":_…restaurant function_, + case "hotel":_…hotel function_, + default:_…default function_} +``` + +Use `switch` when testing a single expression for equality against multiple values. Continue using `if` for different comparison operators or when each condition tests different expressions, such as in the tiering example above. See the [switch function reference](/en/reference/ranking/ranking-expressions#the-switch-function) for details. + +## Using constants + +Ranking expressions can refer to constants defined in a `constants` clause: + +```bash +first-phase { + expression: myConst1 + myConst2 +} +constants { + myConst1: 1.5 + myConst2: 2.5 + ... +} +``` + +Constants lists are inherited and can be overridden in sub-profiles. This is useful to create a set of rank profiles that use the same broad ranking but differs by constants values. + +For performance, always prefer constants to query variables (see below) whenever the constant values to use can be enumerated in a set of rank profiles. Constants are applied to ranking expressions at configuration time, and the resulting constant parts of expressions calculated, which may lead to reduced execution cost, especially with tensor constants. + +## Using query variables + +As ranking expressions can refer to any feature by name, one can use [query features](/en/reference/ranking/rank-features#feature-list) as ranking variables. These variables can be used for example to allow the query to specify the degree of importance to various parts of a ranking expression, or to quickly search large parameter spaces to find a good ranking, by trying different values in each query. These variables can be assigned default values in the [rank profile](/en/reference/schemas/schemas#rank-profile) by adding: + +```bash +inputs { + query(myvalue) double: 0.5 +} +``` + +to the rank profile. These variables can then be overridden in the query by adding: + +```bash +input.query(myvalue)=0.1 +``` + +to it - see the [Query API](/en/reference/api/query#ranking.features). + +## Query feature types + +The default type of all features are scalar. To use query feature _tensors_ we must [define their type in the rank profile](/en/reference/schemas/schemas#inputs). + +Without the correct tensor type, a passed query feature is handled as a string to be converted to a scalar, which will _not give an error but will produce incorrect results_. + +Tensors can be passed in requests using the [tensor literal form](/en/reference/ranking/tensor#tensor-literal-form), for example: + +```bash +input.query(user_profile)=%7B%7Bcat%3Apop%7D%3A0.8%2C%7Bcat%3Arock%7D%3A0.2%2C%7Bcat%3Ajazz%7D%3A0.1%7D +``` + +However, it is usually preferable instead to create them in a [Searcher](/en/applications/searchers). Set the tensor value using the [RankFeatures](https://github.com/vespa-engine/vespa/blob/master/container-search/src/main/java/com/yahoo/search/query/ranking/RankFeatures.java) instance associated with [Query](https://github.com/vespa-engine/vespa/blob/master/container-search/src/main/java/com/yahoo/search/Query.java) instance. This example makes a tensor with a single cell with value 500: + +```java +package com.yahoo.example; + +import com.yahoo.search.Query; +import com.yahoo.search.Result; +import com.yahoo.search.Searcher; +import com.yahoo.search.searchchain.Execution; +import com.yahoo.tensor.Tensor; +import com.yahoo.tensor.TensorType; + +public class TensorInQuerySearcher extends Searcher { + @Override + public Result search(Query query, Execution execution) { + // The Tensor type must also be in the application package as described in the above section + query.getRanking().getFeatures().put("query(tensor_feature)", + Tensor.Builder.of(TensorType.fromSpec("tensor<float>(x{})")).cell().label("x", "foo").value(500).build()); + return execution.search(query); + } +} +``` +Refer to the [Tensor](https://javadoc.io/doc/com.yahoo.vespa/vespajlib/latest/com/yahoo/tensor/Tensor.html) Java API for details on how to construct tensors programmatically. +## Function snippets + +When using machine learned ranking, we are searching a function space which is much more limited than the space of functions supported by ranking expressions. We can increase the space of functions available to MLR because the primitive features used in MLR training do not need to be primitive features in Vespa - they can just as well be ranking expression snippets. If there are certain mathematical combinations of features believed to be useful in an application, these can be pre-calculated from the actual primitive features of Vespa and given to MLR as primitives. Such primitives can then be replaced textually by the corresponding ranking expression snippet, before the learned expression is deployed on Vespa. + +Vespa supports [expression functions](/en/reference/schemas/schemas#function-rank). Functions having zero arguments can be used as summary- and rank-features. For example, the function "myfeature": + +```bash +rank-profile myrankprofile inherits default { + function myfeature() { + expression: fieldMatch(title).completeness * pow(0 - fieldMatch(title).earliness, 2) + } +} +``` + +becomes available as a feature as follows: + +```bash +summary-features { + myfeature +} +``` + +## Tracking relevance variations over time + +Vespa comes with a few simple metrics for relevance that enables applications to see how relevance changes over time, either as a result of changes to how relevance is computed, changes to query construction, changes to the content ingested, or as a result of changing user behavior. + +The relevance metrics are `relevance.at_1`, `relevance.at_3` and `relevance.at_10`. See [metrics](/en/operations/metrics) for more information. + +## Examples + +If the user is underage, assign 0 to adult content and use the average of match quality in the title field and popularity among kids. If the user is not, use the match quality in the title field: + +```py +if ( query(userage) < 18, + if ( attribute(adultness) > 0.1, 0 , (fieldMatch(title)+attribute(kidspopularity)) / 2 ), + fieldMatch(title) ) +``` + +Use a weighted average of the match quality in some fields, multiplied by 1-exp of the document age: + +```py +( 10*fieldMatch(title) + 5*fieldMatch(description) + + 7*attributeMatch(tags).normalizedWeight ) /22 * ( 1 - age(creationtime) ) +``` \ No newline at end of file diff --git a/en/ranking/ranking-intro.md b/en/ranking/ranking-intro.md deleted file mode 100644 index da9b394dee..0000000000 --- a/en/ranking/ranking-intro.md +++ /dev/null @@ -1,408 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Introduction to ranking" -redirect_from: - - /en/getting-started-ranking.html ---- - -Learn how [ranking](../basics/ranking.html) works in Vespa by using the open [query API](../querying/query-api.html) of -[vespa-documentation-search](https://github.com/vespa-cloud/vespa-documentation-search). -In this article, find a set of queries invoking different `rank-profiles`, which is the ranking definition. - -Ranking is the user-defined computation that scores documents to a query, -here configured in [doc.sd](https://github.com/vespa-cloud/vespa-documentation-search/blob/main/src/main/application/schemas/doc.sd), -also see [schema documentation](../basics/schemas.html). -This schema has a set of (contrived) ranking functions, to help learn Vespa ranking. - - -## Ranking using document features only - -Let's start with something simple: _Irrespective of the query, score all documents by the number of in-links to it_. -That is, for any query, return the documents with most in-links first in the result set (these queries are clickable!): - -<a class="querystring-x">yql=select * from doc where true&ranking=inlinks</a> - -The score, named `relevance` in query results, is the size of the `inlinks` attribute array in the document, -as configured in the `expression`: - -<pre> -rank-profile inlinks { - first-phase { - expression: attribute(inlinks).count - } - summary-features { - attribute(inlinks).count - } -} -</pre> - - -Count the number of entries in `inlinks` in the result and compare with `relevance` - it will be the same. -Observe that the ranking expression does not use any features from the query, -it only uses `attribute(inlinks).count`, -which is a [document feature](../reference/ranking/rank-features.html#document-features). - - -## Observing values used in ranking - -When developing ranking expressions, it is useful to observe the input values. -Output the input values using [summary-features](../reference/schemas/schemas.html#summary-features). -In this experiment, we will use another rank function, still counting in-links but scoring older documents lower: - - -<p class="equation-container"><!-- depends on mathjax --> - $$ num\_inlinks * {decay\_const}^{doc\_age\_seconds/3600} $$ -</p> - -Notes: -* use of the `now` [ranking feature](../reference/ranking/rank-features.html) -* use `pow`, a mathematical function in [ranking expressions](../reference/ranking/ranking-expressions.html) -* use of constants and functions to write better code - -<a class="querystring-x">yql=select * from doc where true&ranking=inlinks_age</a> - -<pre> -rank-profile inlinks_age { - first-phase { - expression: rank_score - } - summary-features { - attribute(inlinks).count - attribute(last_updated) - now - doc_age_seconds - age_decay - num_inlinks - rank_score - } - constants { - decay_const: 0.9 - } - function doc_age_seconds() { - expression: now - attribute(last_updated) - } - function age_decay() { - expression: pow(decay_const, doc_age_seconds/3600) - } - function num_inlinks() { - expression: attribute(inlinks).count - } - function rank_score() { - expression: num_inlinks * age_decay - } -} -</pre> -In the query results, here we observe a document with 27 in-links, 9703 seconds old, get at relevance at 20.32 -(the age of documents will vary with query time): -<pre> -"relevance": 20.325190122213748, -... -"summaryfeatures": { - "attribute(inlinks).count": 27.0, - "attribute(last_updated)": 1.615971522E9, - "now": 1.615981225E9, - "rankingExpression(age_decay)": 0.7527848193412499, - "rankingExpression(doc_age_seconds)": 9703.0, - "rankingExpression(num_inlinks)": 27.0, - "rankingExpression(rank_score)": 20.325190122213748, -} -</pre> -Using `summary-features` makes it easy to validate and develop the ranking expression. - -<!-- ToDo: also use reference/schemas.html#match-features --> - - - -## Ranking with query features - -Let's assume we want to find similar documents, and we define document similarity as having the same number of words. -From most perspectives, this is a poor similarity function, better functions are described later. - -The documents have a `term_count` field - -so let's add an [input.query()](../reference/api/query.html#ranking.features) for term count: - -<a class="querystring-x">yql=select * from doc where true;&ranking=term_count_similarity&input.query(q_term_count)=1000</a> - -<p class="equation-container"><!-- depends on mathjax --> - $$ 1 - \frac{fabs(attribute(term\_count) - query(q\_term\_count))}{1 + attribute(term\_count) + query(q\_term\_count)} $$ -</p> - -<pre> -rank-profile term_count_similarity { - first-phase { - expression { - 1 - - fabs( attribute(term_count) - query(q_term_count) ) / - (1 + attribute(term_count) + query(q_term_count) ) - } - } - summary-features { - attribute(term_count) - query(q_term_count) - } -} -</pre> -This rank function will score documents [0-1>, closer to 1 is more similar: -<pre> -"relevance": 0.9985029940119761, -... -"summaryfeatures": { - "attribute(term_count)": 1003.0, - "query(q_term_count)": 1000.0, -} -</pre> -The key learning here is how to transfer ranking features in the query, using `input.query()`. -Use different names for more query features. - - -## Ranking with a query tensor - -Another similarity function can be overlap in in-links. -We will map the inlinks [weightedset](../reference/schemas/schemas.html#weightedset) into a -[tensor](../reference/schemas/schemas.html#tensor), -query with a tensor of same type and create a scalar using a tensor product as the rank score. -We use a [mapped](../reference/ranking/tensor.html#general-literal-form) query tensor, -where the document name is the address in the tensor, using a value of 1 for each in-link: -``` -{ - {links:/en/query-profiles.html}:1, - {links:/en/page-templates.html}:1, - {links:/en/overview.html}:1 -} -``` - -{% include important.html content="Vespa cannot know the query tensor type from looking at it - -it must be configured using [inputs](../reference/schemas/schemas.html#inputs)."%} - -As the in-link data is represented in a weightedset, -we use the [tensorFromWeightedSet](../reference/ranking/rank-features.html#document-features) -rank feature to transform it into a tensor named _links_: -<pre> -rank-profile inlink_similarity { - inputs { - query(links) tensor<float>(links{}) - } - first-phase { - expression: sum(tensorFromWeightedSet(attribute(inlinks), links) * query(links)) - } - summary-features { - query(links) - tensorFromWeightedSet(attribute(inlinks), links) - } -} -</pre> - -<a class="querystring-x">yql=select * from doc where true&ranking=inlink_similarity&input.query(links)={ - {links:/en/query-profiles.html}:1, - {links:/en/page-templates.html}:1, - {links:/en/overview.html}:1 }</a> - -Inspect relevance and summary-features: - -<pre> -"relevance": 2.0 -... -"summaryfeatures": { - "query(links)": { - "type": "tensor<float>(links{})", - "cells": [ - { "address": { "links": "/en/query-profiles.html" }, "value": 1 }, - { "address": { "links": "/en/page-templates.html" }, "value": 1 }, - { "address": { "links": "/en/overview.html" }, "value": 1 } ] - }, - "tensorFromWeightedSet(attribute(inlinks),links)": { - "type": "tensor(links{})", - "cells": [ - { "address": { "links": "/en/page-templates.html" }, "value": 1 }, - { "address": { "links": "/en/jdisc/container-components.html" }, "value": 1 }, - { "address": { "links": "/en/query-profiles.html" }, "value": 1 } ] - } -} -</pre> - -Here, the tensors have one dimension, so they are vectors - -the sum of the tensor product is hence the doc product. -As all values are 1, all products are 1 and the sum is 2: - -<table class="table" style="width: auto"> -<thead> - <tr><th>document</th> - <th>query</th> - <th>value</th></tr> -</thead> -<tbody> - <tr><td>/en/jdisc/container-components.html</td><td></td><td>0</td></tr> - <tr><td></td><td>/en/overview.html</td><td>0</td></tr> - <tr><td>/en/page-templates.html</td><td>/en/page-templates.html</td><td>1</td></tr> - <tr><td>/en/query-profiles.html</td><td>/en/query-profiles.html</td><td>1</td></tr> -</tbody> -</table> - -Change values in the query tensor to see difference in rank score, setting different weights for links. - -Summary: The problem of comparing two lists of links is transformed into a numerical problem -of multiplying two occurrence vectors, summing co-occurrences and ranking by this sum: -<pre> -sum(tensorFromWeightedSet(attribute(inlinks), links) * query(links)) -</pre> - -Notes: -* Query tensors can grow large. - Applications will normally create the tensor in code using a [Searcher](../applications/searchers.html), - also see [example](ranking-expressions-features.html#query-feature-types). -* Here the document tensor is created from a weighted set - - a better way would be to store this in a tensor in the document to avoid the transformation. - - -## Retrieval and ranking - -So far in this guide, we have run the ranking function over _all_ documents. -This is a valid use case for many applications. -However, ranking documents is generally CPU-expensive, -optimizing by reducing the candidate set will increase performance. -Example query using text matching, -dumping [calculated rank features](../reference/api/query.html#ranking.listfeatures): - -<a class="querystring-x">yql=select * from doc where title contains "document"&ranking.listFeatures</a> - -See the **long** list of rank features calculated per result. -However, the query filters on documents with "document" in the title, -so the features are only calculated for the small set of matching documents. - -Running a filter like this is _document retrieval_. Another good example is web search - -the user query terms are used to _retrieve_ the candidate set cheaply (from billions of documents), -then one or more _ranking functions_ are applied to the much smaller candidate set to generate the ranked top-ten. -Another way to look at it is: -* In the retrieval (recall) phase, _find all relevant documents_ -* In the ranking phase, _show only relevant documents_. - -Still, the candidate set after retrieval can be big, a query can hit all documents. -Ranking all candidates is not possible in many applications. - -Splitting the ranking into two phases is another optimization - -use an inexpensive ranking expression to sort out the least promising candidates -before spending most resources on the highest ranking candidates. -In short, use increasingly more power per document as the candidate set shrinks: - -<img src="/assets/img/retrieval-ranking.svg" width="584" height="auto" alt="Retrieval and ranking]"/> - -Let's try the same query again, with a two-phase rank-profile that also does an explicit rank score cutoff: - -<a class="querystring-x">yql=select * from doc where title contains "attribute"&ranking=inlinks_twophase</a> - -<pre> -rank-profile inlinks_twophase inherits inlinks_age { - first-phase { - total-keep-rank-count : 50 - rank-score-drop-limit : 10 - expression : num_inlinks - } - second-phase { - expression : rank_score - } -} -</pre> - -Note how using rank-profile `inherits` is a smart way to define functions once, -then use in multiple rank-profiles. -Read more about [schema inheritance](../schemas/inheritance-in-schemas.html). -Here, `num_inlinks` and `rank_score` are defined in a rank profile we used earlier: - -<pre> - function num_inlinks() { - expression: attribute(inlinks).count - } -</pre> - -In the results, observe that no document has a _rankingExpression(num_inlinks)_ less than or equal to 10.0, -meaning all such documents were purged in the first ranking phase due to the `rank-score-drop-limit`. -Normally, the `rank-score-drop-limit` is not used, as the `total-keep-rank-count` is most important. -Read more in the [reference](../reference/schemas/schemas.html#rank-score-drop-limit). - -For a dynamic limit, pass a ranking feature like `query(threshold)` -and use an `if` statement to check if the score is above the threshold or not - -if below, assign -1 (something lower than the `rank-score-drop-limit`) and have it dropped. -Read more in [ranking expressions](ranking-expressions-features.html#the-if-function-and-string-equality-tests). - -Two-phased ranking is a performance optimization - this guide is about functionality, -so the rest of the examples will only be using one ranking phase. -Read more in [first-phase](../reference/schemas/schemas.html#firstphase-rank). - - -## Retrieval: AND, OR, weakAnd - -This guide will not go deep in query operators in the retrieval phase, -see [query-api](../querying/query-api.html) for details. - -Consider a query like _"vespa documents about ranking and retrieval"_. -A query AND-ing these terms hits less than 3% of the document corpus, -missing some of the documents about ranking and retrieval: - -<a class="querystring-x">yql=select * from doc where (default contains "vespa" -AND default contains "documents" -AND default contains "about" -AND default contains "ranking" -AND default contains "and" -AND default contains "retrieval")</a> - -Alternatively, OR-ing the terms hits more than 95% of the documents, -unable to filter out irrelevant documents in the retrieval phase: - -<a class="querystring-x">yql=select * from doc where (default contains "vespa" -OR default contains "documents" -OR default contains "about" -OR default contains "ranking" -OR default contains "and" -OR default contains "retrieval")</a> - -Using a "weak AND" can address the problems of too few (AND) or too many (OR) hits in the retrieval phase. -Think of it as an _optimized OR_, where the least relevant candidates are discarded from further evaluation. -To find the least relevant candidates, a simple scoring function is used: - - rank_score = sum_n(term(n).significance * term(n).weight) - -As the point of [weakAnd](../reference/querying/yql.html#weakand) is to early discard the worst candidates, -_totalCount_ is an approximation: - -<a class="querystring-x">yql=select * from doc where -{scoreThreshold: 0, totalTargetHits: 10}weakAnd( -default contains "vespa", -default contains "documents", -default contains "about", -default contains "ranking", -default contains "and", -default contains "retrieval")</a> - -Note that this blurs the distinction between filtering (retrieval) and ranking a little - -here the `weakAnd` does <span style="text-decoration: underline">both</span> filtering and ranking -to optimize the number of candidates for the later rank phases. -The default rank-profile is used: -<pre> -rank-profile documentation inherits default { - inputs { - query(titleWeight): 2.0 - query(contentsWeight): 1.0 - } - first-phase { - expression: query(titleWeight) * bm25(title) + query(contentsWeight) * bm25(content) - } -} -</pre> -Observe we are here using text matching rank features, -which fits well with weakAnd's scoring function that also uses text matching features. - -Read more in [the wand documentation](wand.html). - -<!-- -ToDo, next steps: -* look at retrieval, start easy with AND, then OR -* OR degenerates quickly to _all_ documents, so describe WAND in layman terms -* describe when rank features are calculated to illustrate importance of recall phase -* from here, POST YQL for better readability -* explain, tracing ... ---> - -## Next steps -* -* Read more about custom re-ranking of the final result set in - [reranking in searcher](reranking-in-searcher). diff --git a/en/ranking/ranking-intro.mdx b/en/ranking/ranking-intro.mdx new file mode 100644 index 0000000000..6c5e910f43 --- /dev/null +++ b/en/ranking/ranking-intro.mdx @@ -0,0 +1,354 @@ +--- +title: "Introduction to ranking" +--- + +Learn how [ranking](/en/basics/ranking) works in Vespa by using the open [query API](/en/querying/query-api) of [vespa-documentation-search](https://github.com/vespa-cloud/vespa-documentation-search).In this article, find a set of queries invoking different `rank-profiles`, which is the ranking definition. + +Ranking is the user-defined computation that scores documents to a query, here configured in [doc.sd](https://github.com/vespa-cloud/vespa-documentation-search/blob/main/src/main/application/schemas/doc.sd), also see [schema documentation](/en/reference/schemas/schemas). This schema has a set of (contrived) ranking functions, to help learn Vespa ranking. + + +## Ranking using document features only + +Let's start with something simple: _Irrespective of the query, score all documents by the number of in-links to it_. That is, for any query, return the documents with most in-links first in the result set (these queries are clickable!): + +[`yql=select * from doc where true&ranking=inlinks`](https://api.search.vespa.ai/search/?yql=select%20*%20from%20doc%20where%20true&ranking=inlinks&_gl=1*2d8dpa*_gcl_au*MjA3MzE0NDM4Ny4xNzc4ODIyODQ4) + +The score, named `relevance` in query results, is the size of the `inlinks` attribute array in the document, as configured in the `expression`: + + +```js +rank-profile inlinks { + first-phase { + expression: attribute(inlinks).count + } + summary-features { + attribute(inlinks).count + } +} +``` + +Count the number of entries in `inlinks` in the result and compare with `relevance` - it will be the same. Observe that the ranking expression does not use any features from the query, it only uses `attribute(inlinks).count`, which is a [document feature](/en/reference/ranking/rank-features#document-features). + + +## Observing values used in ranking + +When developing ranking expressions, it is useful to observe the input values. Output the input values using [summary-features](/en/reference/schemas/schemas#summary-features). In this experiment, we will use another rank function, still counting in-links but scoring older documents lower: + +$$ +\text{num\_inlinks} \times {\text{decay\_const}}^{\frac{\text{doc\_age\_seconds}}{3600}} +$$ + + +Notes: +- use of the `now` [ranking feature](/en/reference/ranking/rank-features#now) +- use `pow`, a mathematical function in [ranking expressions](/en/reference/ranking/ranking-expressions) +- use of constants and functions to write better code + +[`yql=select * from doc where true&ranking=inlinks_age`](https://api.search.vespa.ai/search/?yql=select%20*%20from%20doc%20where%20true&ranking=inlinks_age) + + +```js expandable +rank-profile inlinks_age { + first-phase { + expression: rank_score + } + summary-features { + attribute(inlinks).count + attribute(last_updated) + now + doc_age_seconds + age_decay + num_inlinks + rank_score + } + constants { + decay_const: 0.9 + } + function doc_age_seconds() { + expression: now - attribute(last_updated) + } + function age_decay() { + expression: pow(decay_const, doc_age_seconds/3600) + } + function num_inlinks() { + expression: attribute(inlinks).count + } + function rank_score() { + expression: num_inlinks * age_decay + } +} +``` + +In the query results, here we observe a document with 27 in-links, 9703 seconds old, get at relevance at 20.32 (the age of documents will vary with query time): + +```js expandable +"relevance": 20.325190122213748, +... +"summaryfeatures": { + "attribute(inlinks).count": 27.0, + "attribute(last_updated)": 1.615971522E9, + "now": 1.615981225E9, + "rankingExpression(age_decay)": 0.7527848193412499, + "rankingExpression(doc_age_seconds)": 9703.0, + "rankingExpression(num_inlinks)": 27.0, + "rankingExpression(rank_score)": 20.325190122213748, +} +``` + +Using `summary-features` makes it easy to validate and develop the ranking expression. + + +## Ranking with query features + +Let's assume we want to find similar documents, and we define document similarity as having the same number of words. From most perspectives, this is a poor similarity function, better functions are described later. + +The documents have a `term_count` field - so let's add an [input.query()](/en/reference/api/query#ranking.features) for term count: + +[`yql=select * from doc where true &ranking=term_count_similarity&input.query(q_term_count)=1000`](https://api.search.vespa.ai/search/?yql=select%20*%20from%20doc%20where%20true;&ranking=term_count_similarity&input.query(q_term_count)=1000&_gl=1*2d8dpa*_gcl_au*MjA3MzE0NDM4Ny4xNzc4ODIyODQ4) + + +$$ +1 - \frac{fabs(attribute(term\_count) - query(q\_term\_count))}{1 + attribute(term\_count) + query(q\_term\_count)} +$$ + + +```js +rank-profile term_count_similarity { + first-phase { + expression { + 1 - + fabs( attribute(term_count) - query(q_term_count) ) / + (1 + attribute(term_count) + query(q_term_count) ) + } + } + summary-features { + attribute(term_count) + query(q_term_count) + } +} +``` + +This rank function will score documents [0-1>, closer to 1 is more similar: + +```js +"relevance": 0.9985029940119761, +... +"summaryfeatures": { + "attribute(term_count)": 1003.0, + "query(q_term_count)": 1000.0, +} +``` + +The key learning here is how to transfer ranking features in the query, using `input.query()`. Use different names for more query features. + + +## Ranking with a query tensor + +Another similarity function can be overlap in in-links. We will map the inlinks [weightedset](/en/reference/schemas/schemas#weightedset) into a [tensor](/en/reference/schemas/schemas#tensor), query with a tensor of same type and create a scalar using a tensor product as the rank score. We use a [mapped](/en/reference/ranking/tensor#general-literal-form) query tensor, where the document name is the address in the tensor, using a value of 1 for each in-link: + +```js +{ + {links:/en/query-profiles.html}:1, + {links:/en/page-templates.html}:1, + {links:/en/overview.html}:1 +} +``` + + +<Warning> +**Important:** + +Vespa cannot know the query tensor type from looking at it - it must be configured using [inputs](/en/reference/schemas/schemas#inputs). +</Warning> + + +As the in-link data is represented in a weightedset, we use the [tensorFromWeightedSet](/en/reference/ranking/rank-features#tensorFromWeightedSet(source,dimension)) rank feature to transform it into a tensor named _links_: + +```js +rank-profile inlink_similarity { + inputs { + query(links) tensor<float>(links{}) + } + first-phase { + expression: sum(tensorFromWeightedSet(attribute(inlinks), links) * query(links)) + } + summary-features { + query(links) + tensorFromWeightedSet(attribute(inlinks), links) + } +} +``` + +[`yql=select * from doc where true&ranking=inlink_similarity&input.query(links)={{links:/en/query-profiles.html}:1,{links:/en/page-templates.html}:1,{links:/en/overview.html}:1}`](https://api.search.vespa.ai/search/?yql=select%20*%20from%20doc%20where%20true&ranking=inlink_similarity&input.query(links)=%7B%20%7Blinks:/en/query-profiles.html%7D:1,%20%7Blinks:/en/page-templates.html%7D:1,%20%7Blinks:/en/overview.html%7D:1%20%7D&_gl=1*j8zfax*_gcl_au*MjA3MzE0NDM4Ny4xNzc4ODIyODQ4) + +Inspect relevance and summary-features: + + +```js expandable +"relevance": 2.0 +... +"summaryfeatures": { + "query(links)": { + "type": "tensor<float>(links{})", + "cells": [ + { "address": { "links": "/en/query-profiles.html" }, "value": 1 }, + { "address": { "links": "/en/page-templates.html" }, "value": 1 }, + { "address": { "links": "/en/overview.html" }, "value": 1 } ] + }, + "tensorFromWeightedSet(attribute(inlinks),links)": { + "type": "tensor(links{})", + "cells": [ + { "address": { "links": "/en/page-templates.html" }, "value": 1 }, + { "address": { "links": "/en/jdisc/container-components.html" }, "value": 1 }, + { "address": { "links": "/en/query-profiles.html" }, "value": 1 } ] + } +} +``` + + +Here, the tensors have one dimension, so they are vectors - the sum of the tensor product is hence the doc product. As all values are 1, all products are 1 and the sum is 2: + + +<table className="vespa-table"> + <thead> + <tr> + <th>document</th> + <th>query</th> + <th>value</th> + </tr> + </thead> + <tbody> + <tr> + <td>/en/jdisc/container-components.html</td> + <td></td> + <td>0</td> + </tr> + <tr> + <td></td> + <td>/en/overview.html</td> + <td>0</td> + </tr> + <tr> + <td>/en/page-templates.html</td> + <td>/en/page-templates.html</td> + <td>1</td> + </tr> + <tr> + <td>/en/query-profiles.html</td> + <td>/en/query-profiles.html</td> + <td>1</td> + </tr> + </tbody> +</table> + + +Change values in the query tensor to see difference in rank score, setting different weights for links. + +Summary: The problem of comparing two lists of links is transformed into a numerical problem of multiplying two occurrence vectors, summing co-occurrences and ranking by this sum: + +```js +sum(tensorFromWeightedSet(attribute(inlinks), links) * query(links)) +``` + + +Notes: +- Query tensors can grow large. Applications will normally create the tensor in code using a [Searcher](/en/applications/searchers), also see [example](/en/ranking/ranking-expressions-features#query-feature-types). +- Here the document tensor is created from a weighted set - a better way would be to store this in a tensor in the document to avoid the transformation. + + +## Retrieval and ranking + +So far in this guide, we have run the ranking function over _all_ documents. This is a valid use case for many applications. However, ranking documents is generally CPU-expensive, optimizing by reducing the candidate set will increase performance. Example query using text matching, dumping [calculated rank features](/en/reference/api/query#ranking.listfeatures): + +[`yql=select * from doc where title contains "document"&ranking.listFeatures`](https://api.search.vespa.ai/search/?yql=select%20*%20from%20doc%20where%20title%20contains%20%22document%22&ranking.listFeatures&_gl=1*1jqth3v*_gcl_au*MjA3MzE0NDM4Ny4xNzc4ODIyODQ4) + +See the **long** list of rank features calculated per result. However, the query filters on documents with "document" in the title, so the features are only calculated for the small set of matching documents. + +Running a filter like this is _document retrieval_. Another good example is web search - the user query terms are used to _retrieve_ the candidate set cheaply (from billions of documents), then one or more _ranking functions_ are applied to the much smaller candidate set to generate the ranked top-ten. Another way to look at it is: +- In the retrieval (recall) phase, _find all relevant documents_ +- In the ranking phase, _show only relevant documents_. + +Still, the candidate set after retrieval can be big, a query can hit all documents. Ranking all candidates is not possible in many applications. + +Splitting the ranking into two phases is another optimization - use an inexpensive ranking expression to sort out the least promising candidates before spending most resources on the highest ranking candidates. In short, use increasingly more power per document as the candidate set shrinks: + +<Frame> + <img src="/assets/img/retrieval-ranking.svg" width="584" height="auto" alt="Retrieval and ranking"/> +</Frame> + +Let's try the same query again, with a two-phase rank-profile that also does an explicit rank score cutoff: + +[`yql=select * from doc where title contains "attribute"&ranking=inlinks_twophase`](https://api.search.vespa.ai/search/?yql=select%20*%20from%20doc%20where%20title%20contains%20%22attribute%22&ranking=inlinks_twophase&_gl=1*1jqth3v*_gcl_au*MjA3MzE0NDM4Ny4xNzc4ODIyODQ4) + + +```js +rank-profile inlinks_twophase inherits inlinks_age { + first-phase { + total-keep-rank-count : 50 + rank-score-drop-limit : 10 + expression : num_inlinks + } + second-phase { + expression : rank_score + } +} +``` + + +Note how using rank-profile `inherits` is a smart way to define functions once, then use in multiple rank-profiles. Read more about [schema inheritance](/en/schemas/inheritance-in-schemas). Here, `num_inlinks` and `rank_score` are defined in a rank profile we used earlier: + + +```js +function num_inlinks() { + expression: attribute(inlinks).count + } +``` + + +In the results, observe that no document has a _rankingExpression(num_inlinks)_ less than or equal to 10.0, meaning all such documents were purged in the first ranking phase due to the `rank-score-drop-limit`. Normally, the `rank-score-drop-limit` is not used, as the `total-keep-rank-count` is most important. Read more in the [reference](/en/reference/schemas/schemas#rank-score-drop-limit). + +For a dynamic limit, pass a ranking feature like `query(threshold)` and use an `if` statement to check if the score is above the threshold or not - if below, assign -1 (something lower than the `rank-score-drop-limit`) and have it dropped. Read more in [ranking expressions](/en/ranking/ranking-expressions-features#the-if-function-and-string-equality-tests). + +Two-phased ranking is a performance optimization - this guide is about functionality, so the rest of the examples will only be using one ranking phase. Read more in [first-phase](/en/reference/schemas/schemas#firstphase-rank). + + +## Retrieval: AND, OR, weakAnd + +This guide will not go deep in query operators in the retrieval phase, see [query-api](/en/querying/query-api) for details. + +Consider a query like _"vespa documents about ranking and retrieval"_. A query AND-ing these terms hits less than 3% of the document corpus, missing some of the documents about ranking and retrieval: + +[`yql=select * from doc where (default contains "vespa"AND default contains "documents"AND default contains "about"AND default contains "ranking"AND default contains "and"AND default contains "retrieval")`](https://api.search.vespa.ai/search/?yql=select%20*%20from%20doc%20where%20(default%20contains%20%22vespa%22%20AND%20default%20contains%20%22documents%22%20AND%20default%20contains%20%22about%22%20AND%20default%20contains%20%22ranking%22%20AND%20default%20contains%20%22and%22%20AND%20default%20contains%20%22retrieval%22)&_gl=1*1jqth3v*_gcl_au*MjA3MzE0NDM4Ny4xNzc4ODIyODQ4) + +Alternatively, OR-ing the terms hits more than 95% of the documents, unable to filter out irrelevant documents in the retrieval phase: [`yql=select * from doc where (default contains "vespa"OR default contains "documents"OR default contains "about"OR default contains "ranking"OR default contains "and"OR default contains "retrieval")`](https://api.search.vespa.ai/search/?yql=select%20*%20from%20doc%20where%20(default%20contains%20%22vespa%22%20OR%20default%20contains%20%22documents%22%20OR%20default%20contains%20%22about%22%20OR%20default%20contains%20%22ranking%22%20OR%20default%20contains%20%22and%22%20OR%20default%20contains%20%22retrieval%22)&_gl=1*n39r85*_gcl_au*MjA3MzE0NDM4Ny4xNzc4ODIyODQ4) + +Using a "weak AND" can address the problems of too few (AND) or too many (OR) hits in the retrieval phase. Think of it as an _optimized OR_, where the least relevant candidates are discarded from further evaluation. To find the least relevant candidates, a simple scoring function is used: + +```bash + rank_score = sum_n(term(n).significance * term(n).weight) +``` + +As the point of [weakAnd](/en/reference/querying/yql#weakand) is to early discard the worst candidates, _totalCount_ is an approximation: + +[`yql=select * from doc where{scoreThreshold: 0, totalTargetHits: 10}weakAnd(default contains "vespa", default contains "documents", default contains "about", default contains "ranking", default contains "and", default contains "retrieval")`](https://api.search.vespa.ai/search/?yql=select%20*%20from%20doc%20where%20%7BscoreThreshold:%200,%20totalTargetHits:%2010%7DweakAnd(%20default%20contains%20%22vespa%22,%20default%20contains%20%22documents%22,%20default%20contains%20%22about%22,%20default%20contains%20%22ranking%22,%20default%20contains%20%22and%22,%20default%20contains%20%22retrieval%22)&_gl=1*1psdofr*_gcl_au*MjA3MzE0NDM4Ny4xNzc4ODIyODQ4) + +Note that this blurs the distinction between filtering (retrieval) and ranking a little - here the `weakAnd` does **both** filtering and ranking to optimize the number of candidates for the later rank phases. The default rank-profile is used: + +```js +rank-profile documentation inherits default { + inputs { + query(titleWeight): 2.0 + query(contentsWeight): 1.0 + } + first-phase { + expression: query(titleWeight) * bm25(title) + query(contentsWeight) * bm25(content) + } +} +``` + +Observe we are here using text matching rank features, which fits well with weakAnd's scoring function that also uses text matching features. + +Read more in [the wand documentation](/en/ranking/wand). + +## Next steps +- Read more about custom re-ranking of the final result set in [reranking in searcher](/en/ranking/reranking-in-searcher). diff --git a/en/ranking/reranking-in-searcher.md b/en/ranking/reranking-in-searcher.md deleted file mode 100644 index 344bcc8f4f..0000000000 --- a/en/ranking/reranking-in-searcher.md +++ /dev/null @@ -1,345 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Re-ranking using a custom Searcher" -redirect_from: - - /en/reranking-in-searcher ---- - -This guide demonstrates how to deploy a [searcher](../applications/searchers.html) -implementing a last stage of [phased ranking](phased-ranking.html). The searcher re-ranks the -global top 200 documents which have been ranked by the content nodes using the configurable [ranking](ranking-intro) -specification in the document [schema(s)](../basics/schemas.html). - -The reranking searcher uses [multiphase searching](../applications/searchers.html#multiphase-searching): - -**Matching query protocol phase:** The matching protocol phase which asks each content node involved in the query -to return the locally best ranking hits (ranked by the configurable ranking expressions defined in the schema). -This matching query protocol phase can include several ranking phases which are executed per content node. -In the query protocol phase the content nodes can also return [match-features](../reference/schemas/schemas.html#match-features) which -a re-ranking searcher can use to re-rank results (or feature logging). -In the custom searcher one is working on the global best ranking hits from the content nodes, and -can have access to aggregated features which is calculated across the top-ranking documents (the global best documents). - -**Fill query protocol phase:** Fill summary data for the global top ranking hits after all ranking phases. -If one needs access to the document fields, -the searcher would need to call `execution.fill` before the re-ranking logic, -this would then cost more resources -than just using `match-features` which is delivered in the first protocol matching phase. -If one needs access to a subset of fields during stateless re-ranking, -consider configuring a dedicated [document summary](../querying/document-summaries.html). - -See also [life of a query in Vespa](../performance/sizing-search.html#life-of-a-query-in-vespa). - -{% include pre-req.html memory="4 GB" extra-reqs=' -<li><a href="https://openjdk.org/projects/jdk/17/" data-proofer-ignore>Java 17</a>.</li> -<li><a href="https://maven.apache.org/install.html">Apache Maven</a> is used to build the application.</li>' %} - - -### A minimal Vespa application - -To define the Vespa app package using our custom reranking searcher, four files is needed: - -- The schema -- The deployment specification `services.xml` -- The custom reranking searcher -- [pom.xml](https://maven.apache.org/guides/introduction/introduction-to-the-pom.html) - -Start by defining a simple schema with two fields. -We also define a rank profile with two [rank features](../reference/ranking/rank-features.html) -to be used in the searcher for re-ranking: - -<pre data-test="file" data-path="my-app/src/main/application/schemas/doc.sd"> -schema doc { - - document doc { - field name type string { - indexing: summary | index - match: text - index: enable-bm25 - } - - field downloads type int { - indexing: summary | attribute - } - } - - fieldset default { - fields: name - } - - rank-profile rank-profile-with-match { - first-phase { - expression: bm25(name) - } - match-features { - bm25(name) - attribute(downloads) - } - } -} -</pre> - -The searcher implementing the re-ranking logic: - -<pre data-test="file" data-path="my-app/src/main/java/ai/vespa/example/searcher/ReRankingSearcher.java"> -package ai.vespa.example.searcher; - -import com.yahoo.search.Query; -import com.yahoo.search.Result; -import com.yahoo.search.Searcher; -import com.yahoo.search.result.FeatureData; -import com.yahoo.search.result.Hit; -import com.yahoo.search.searchchain.Execution; - -public class ReRankingSearcher extends Searcher { - @Override - public Result search(Query query, Execution execution) { - int hits = query.getHits(); - query.setHits(200); //Re-ranking window - query.getRanking().setProfile("rank-profile-with-match"); - Result result = execution.search(query); - if(result.getTotalHitCount() == 0 - || result.hits().getErrorHit() != null) - return result; - double max = 0; - //Find max value of the window - for (Hit hit : result.hits()) { - FeatureData featureData = (FeatureData) hit.getField("matchfeatures"); - if(featureData == null) - throw new RuntimeException("No 'matchfeatures' found - wrong rank profile used?"); - double downloads = featureData.getDouble("attribute(downloads)"); - if (downloads > max) - max = downloads; - } - //re-rank using normalized value - for (Hit hit : result.hits()) { - FeatureData featureData = (FeatureData) hit.getField("matchfeatures"); - if(featureData == null) - throw new RuntimeException("No 'matchfeatures' found - wrong rank profile used?"); - double downloads = featureData.getDouble("attribute(downloads)"); - double normalizedByMax = downloads / max; //Change me - double bm25Name = featureData.getDouble("bm25(name)"); - double newScore = bm25Name + normalizedByMax; - hit.setField("rerank-score",newScore); - hit.setRelevance(newScore); - } - result.hits().sort(); - //trim the result down to the requested number of hits - result.hits().trim(0, hits); - return result; - } -} -</pre> - -[services.xml](../reference/applications/services/services.html) is needed -to make up a Vespa [application package](../reference/applications/application-packages.html). -Here we include the custom searcher in the `default` [search chain](../applications/chaining.html): - -<pre data-test="file" data-path="my-app/src/main/application/services.xml"> -<?xml version="1.0" encoding="utf-8" ?> -<services version="1.0" xmlns:deploy="vespa" xmlns:preprocess="properties"> - <container id="default" version="1.0"> - <document-api/> - <search> - <chain id="default" inherits="vespa"> - <searcher id="ai.vespa.example.searcher.ReRankingSearcher" bundle="ranking"/> - </chain> - </search> - <nodes> - <node hostalias="node1" /> - </nodes> - </container> - - <content id="docs" version="1.0"> - <redundancy>2</redundancy> - <documents> - <document type="doc" mode="index" /> - </documents> - <nodes> - <node hostalias="node1" distribution-key="0" /> - </nodes> - </content> -</services> -</pre> - -Notice the `bundle` name of the searcher, this needs to be in synch with the `artifactId` defined in `pom.xml`: - -<pre data-test="file" data-path="my-app/pom.xml"> -<?xml version="1.0"?> -<project xmlns="http://maven.apache.org/POM/4.0.0" - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 - http://maven.apache.org/xsd/maven-4.0.0.xsd"> - <modelVersion>4.0.0</modelVersion> - <groupId>ai.vespa.example</groupId> - <artifactId>ranking</artifactId> <!-- Note: When changing this, also change bundle names in services.xml --> - <version>1.0.0</version> - <packaging>container-plugin</packaging> - <parent> - <groupId>com.yahoo.vespa</groupId> - <artifactId>cloud-tenant-base</artifactId> - <version>[7,999)</version> <!-- Use the latest Vespa release on each build --> - <relativePath/> - </parent> - <properties> - <bundle-plugin.failOnWarnings>true</bundle-plugin.failOnWarnings> - <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> - <test.hide>true</test.hide> - </properties> -</project> -</pre> - - -### Starting Vespa -Now, we have the files and can start Vespa: - -<div class="pre-parent"> - <button class="d-icon d-duplicate pre-copy-button" onclick="copyPreContent(this)"></button> -<pre data-test="exec"> -$ docker pull vespaengine/vespa -$ docker run --detach --name vespa --hostname vespa-container \ - --publish 8080:8080 --publish 19071:19071 \ - vespaengine/vespa -</pre> -</div> - -Install [vespa-cli](../clients/vespa-cli.html) using Homebrew: - -<div class="pre-parent"> - <button class="d-icon d-duplicate pre-copy-button" onclick="copyPreContent(this)"></button> -<pre> -$ brew install vespa-cli -</pre> -</div> - -Build the Maven project, this step creates the application package including the custom searcher: - -<div class="pre-parent"> - <button class="d-icon d-duplicate pre-copy-button" onclick="copyPreContent(this)"></button> -<pre data-test="exec"> -$ (cd my-app && mvn package) -</pre> -</div> - -Deploy the application to Vespa using vespa-cli: - -<div class="pre-parent"> - <button class="d-icon d-duplicate pre-copy-button" onclick="copyPreContent(this)"></button> -<pre data-test="exec"> -$ vespa deploy --wait 300 my-app -</pre> -</div> - - -### Feed data - -Create a few sample docs: - -<pre data-test="file" data-path="doc-1.json"> -{ - "put": "id:docs:doc::0", - "fields": { - "name": "A sample document", - "downloads": 100 - } -} -</pre> - -<pre data-test="file" data-path="doc-2.json"> -{ - "put": "id:docs:doc::1", - "fields": { - "name": "Another sample document", - "downloads": 10 - } -} -</pre> - -Feed them using the CLI: - -<div class="pre-parent"> - <button class="d-icon d-duplicate pre-copy-button" onclick="copyPreContent(this)"></button> -<pre data-test="exec"> -$ vespa document doc-1.json && vespa document doc-2.json -</pre> -</div> - -### Query the data -Run a query - this will invoke the reranking searcher since it was included in a the `default` search chain: -<div class="pre-parent"> - <button class="d-icon d-duplicate pre-copy-button" onclick="copyPreContent(this)"></button> -<pre data-test="exec" data-test-assert-contains='"totalCount": 2'> -$ vespa query 'yql=select * from doc where userQuery()' \ - 'query=sample' -</pre> -</div> - -<pre>{% highlight json %} -{ - "root": { - "id": "toplevel", - "relevance": 1.0, - "fields": { - "totalCount": 2 - }, - "coverage": { - "coverage": 100, - "documents": 2, - "full": true, - "nodes": 1, - "results": 1, - "resultsFull": 1 - }, - "children": [ - { - "id": "id:docs:doc::0", - "relevance": 1.1823215567939547, - "source": "docs", - "fields": { - "matchfeatures": { - "attribute(downloads)": 100.0, - "bm25(name)": 0.1823215567939546 - }, - "rerank-score": 1.1823215567939547, - "sddocname": "doc", - "documentid": "id:docs:doc::0", - "name": "A sample document", - "downloads": 100 - } - }, - { - "id": "id:docs:doc::1", - "relevance": 0.2823215567939546, - "source": "docs", - "fields": { - "matchfeatures": { - "attribute(downloads)": 10.0, - "bm25(name)": 0.1823215567939546 - }, - "rerank-score": 0.2823215567939546, - "sddocname": "doc", - "documentid": "id:docs:doc::1", - "name": "Another sample document", - "downloads": 10 - } - } - ] - } -} -{% endhighlight %}</pre> - -<pre style="display:none" data-test="exec" data-test-assert-contains='"rerank-score": 1.18'> -$ vespa query 'yql=select * from doc where userQuery()' \ - 'query=sample' -</pre> - -### Teardown -Remove app and data: -<div class="pre-parent"> - <button class="d-icon d-duplicate pre-copy-button" onclick="copyPreContent(this)"></button> -<pre data-test="after"> -$ docker rm -f vespa -</pre> -</div> - -<script src="/js/process_pre.js"></script> diff --git a/en/ranking/reranking-in-searcher.mdx b/en/ranking/reranking-in-searcher.mdx new file mode 100644 index 0000000000..7e6845f047 --- /dev/null +++ b/en/ranking/reranking-in-searcher.mdx @@ -0,0 +1,338 @@ +--- +title: "Re-ranking using a custom Searcher" +sidebarTitle: "Searcher re-ranking" +--- + +This guide demonstrates how to deploy a [searcher](/en/applications/searchers) implementing a last stage of [phased ranking](/en/ranking/phased-ranking). The searcher re-ranks the global top 200 documents which have been ranked by the content nodes using the configurable [ranking](/en/ranking/ranking-intro) specification in the document [schema(s)](/en/basics/schemas). + +The reranking searcher uses [multiphase searching](/en/applications/searchers#multiphase-searching): + +**Matching query protocol phase:** The matching protocol phase which asks each content node involved in the query to return the locally best ranking hits (ranked by the configurable ranking expressions defined in the schema). This matching query protocol phase can include several ranking phases which are executed per content node. In the query protocol phase the content nodes can also return [match-features](/en/reference/schemas/schemas#match-features) which a re-ranking searcher can use to re-rank results (or feature logging). In the custom searcher one is working on the global best ranking hits from the content nodes, and can have access to aggregated features which is calculated across the top-ranking documents (the global best documents). + +**Fill query protocol phase:** Fill summary data for the global top ranking hits after all ranking phases. If one needs access to the document fields, the searcher would need to call `execution.fill` before the re-ranking logic, this would then cost more resources than just using `match-features` which is delivered in the first protocol matching phase. If one needs access to a subset of fields during stateless re-ranking, consider configuring a dedicated [document summary](/en/querying/document-summaries). + +See also [life of a query in Vespa](/en/performance/sizing-search#life-of-a-query-in-vespa). + +<Info> +**Prerequisites:** + +- Linux, macOS or Windows 10 Pro on x86\_64 or arm64, with [Podman Desktop](https://podman.io/) or [Docker Desktop](https://www.docker.com/products/docker-desktop/) installed, with an engine running. + - Alternatively, start the Podman daemon: + + ```bash + $ podman machine init --memory 6000 + $ podman machine start + ``` + + - See [Docker Containers](/en/operations/self-managed/docker-containers.html) for system limits and other settings. +- For CPUs older than Haswell (2013), see [CPU Support](/en/operations/self-managed/cpu-support). +- Memory: Minimum 4 GB RAM dedicated to Docker/Podman. [Memory recommendations](/en/operations/self-managed/node-setup#memory-settings). +- Disk: Avoid `NO_SPACE` - the vespaengine/vespa container image + headroom for data requires disk space. [Read more](/en/writing/feed-block.html). +- [Homebrew](https://brew.sh/) to install the [Vespa CLI](/en/clients/vespa-cli.html), or download the Vespa CLI from [Github releases](https://github.com/vespa-engine/vespa/releases). +- [Java 17](https://openjdk.org/projects/jdk/17/). +- [Apache Maven](https://maven.apache.org/install.html) is used to build the application. +</Info> + +### A minimal Vespa application + +To define the Vespa app package using our custom reranking searcher, four files is needed: + +- The schema +- The deployment specification `services.xml` +- The custom reranking searcher +- [pom.xml](https://maven.apache.org/guides/introduction/introduction-to-the-pom.html) + +Start by defining a simple schema with two fields. We also define a rank profile with two [rank features](/en/reference/ranking/rank-features) to be used in the searcher for re-ranking: + +```js +schema doc { + + document doc { + field name type string { + indexing: summary | index + match: text + index: enable-bm25 + } + + field downloads type int { + indexing: summary | attribute + } + } + + fieldset default { + fields: name + } + + rank-profile rank-profile-with-match { + first-phase { + expression: bm25(name) + } + match-features { + bm25(name) + attribute(downloads) + } + } +} +``` + +<Card> +Paste the above into file my-app/src/main/application/schemas/doc.sd +</Card> + +The searcher implementing the re-ranking logic: + +```java expandable +package ai.vespa.example.searcher; + +import com.yahoo.search.Query; +import com.yahoo.search.Result; +import com.yahoo.search.Searcher; +import com.yahoo.search.result.FeatureData; +import com.yahoo.search.result.Hit; +import com.yahoo.search.searchchain.Execution; + +public class ReRankingSearcher extends Searcher { + @Override + public Result search(Query query, Execution execution) { + int hits = query.getHits(); + query.setHits(200); //Re-ranking window + query.getRanking().setProfile("rank-profile-with-match"); + Result result = execution.search(query); + if(result.getTotalHitCount() == 0 + || result.hits().getErrorHit() != null) + return result; + double max = 0; + //Find max value of the window + for (Hit hit : result.hits()) { + FeatureData featureData = (FeatureData) hit.getField("matchfeatures"); + if(featureData == null) + throw new RuntimeException("No 'matchfeatures' found - wrong rank profile used?"); + double downloads = featureData.getDouble("attribute(downloads)"); + if (downloads > max) + max = downloads; + } + //re-rank using normalized value + for (Hit hit : result.hits()) { + FeatureData featureData = (FeatureData) hit.getField("matchfeatures"); + if(featureData == null) + throw new RuntimeException("No 'matchfeatures' found - wrong rank profile used?"); + double downloads = featureData.getDouble("attribute(downloads)"); + double normalizedByMax = downloads / max; //Change me + double bm25Name = featureData.getDouble("bm25(name)"); + double newScore = bm25Name + normalizedByMax; + hit.setField("rerank-score",newScore); + hit.setRelevance(newScore); + } + result.hits().sort(); + //trim the result down to the requested number of hits + result.hits().trim(0, hits); + return result; + } +} +``` + +<Card> +Paste the above into file my-app/src/main/java/ai/vespa/example/searcher/ReRankingSearcher.java +</Card> + +[services.xml](/en/reference/applications/services/services) is needed to make up a Vespa [application package](/en/reference/applications/application-packages). Here we include the custom searcher in the `default` [search chain](/en/applications/chaining): + +```xml expandable +<?xml version="1.0" encoding="utf-8" ?> +<services version="1.0" xmlns:deploy="vespa" xmlns:preprocess="properties"> + <container id="default" version="1.0"> + <document-api/> + <search> + <chain id="default" inherits="vespa"> + <searcher id="ai.vespa.example.searcher.ReRankingSearcher" bundle="ranking"/> + </chain> + </search> + <nodes> + <node hostalias="node1" /> + </nodes> + </container> + + <content id="docs" version="1.0"> + <redundancy>2</redundancy> + <documents> + <document type="doc" mode="index" /> + </documents> + <nodes> + <node hostalias="node1" distribution-key="0" /> + </nodes> + </content> +</services> +``` + +<Card> +Paste the above into file my-app/src/main/application/services.xml +</Card> + +Notice the `bundle` name of the searcher, this needs to be in synch with the `artifactId` defined in `pom.xml`: + +```xml expandable +<?xml version="1.0"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 + http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + <groupId>ai.vespa.example</groupId> + <artifactId>ranking</artifactId> <!-- Note: When changing this, also change bundle names in services.xml --> + <version>1.0.0</version> + <packaging>container-plugin</packaging> + <parent> + <groupId>com.yahoo.vespa</groupId> + <artifactId>cloud-tenant-base</artifactId> + <version>[7,999)</version> <!-- Use the latest Vespa release on each build --> + <relativePath/> + </parent> + <properties> + <bundle-plugin.failOnWarnings>true</bundle-plugin.failOnWarnings> + <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> + <test.hide>true</test.hide> + </properties> +</project> +``` + +<Card> +Paste the above into file my-app/pom.xml +</Card> + +### Starting Vespa + +Now, we have the files and can start Vespa: + +```bash +$ docker pull vespaengine/vespa +$ docker run --detach --name vespa --hostname vespa-container \ + --publish 8080:8080 --publish 19071:19071 \ + vespaengine/vespa +``` + +Install [vespa-cli](/en/clients/vespa-cli) using Homebrew: + +```bash +$ brew install vespa-cli +``` + +Build the Maven project, this step creates the application package including the custom searcher: + +```bash +$ (cd my-app && mvn package) +``` + +Deploy the application to Vespa using vespa-cli: + +```bash +$ vespa deploy --wait 300 my-app +``` + +### Feed data + +Create a few sample docs: + +```json +{ + "put": "id:docs:doc::0", + "fields": { + "name": "A sample document", + "downloads": 100 + } +} +``` + +<Card> +Paste the above into file doc-1.json +</Card> + +```json +{ + "put": "id:docs:doc::1", + "fields": { + "name": "Another sample document", + "downloads": 10 + } +} +``` + +<Card> +Paste the above into file doc-2.json +</Card> + +Feed them using the CLI: + +```bash +$ vespa document doc-1.json && vespa document doc-2.json +``` + +### Query the data + +Run a query - this will invoke the reranking searcher since it was included in a the `default` search chain: + +```bash +$ vespa query 'yql=select * from doc where userQuery()' \ + 'query=sample' +``` + +```json expandable +{ + "root": { + "id": "toplevel", + "relevance": 1.0, + "fields": { + "totalCount": 2 + }, + "coverage": { + "coverage": 100, + "documents": 2, + "full": true, + "nodes": 1, + "results": 1, + "resultsFull": 1 + }, + "children": [ + { + "id": "id:docs:doc::0", + "relevance": 1.1823215567939547, + "source": "docs", + "fields": { + "matchfeatures": { + "attribute(downloads)": 100.0, + "bm25(name)": 0.1823215567939546 + }, + "rerank-score": 1.1823215567939547, + "sddocname": "doc", + "documentid": "id:docs:doc::0", + "name": "A sample document", + "downloads": 100 + } + }, + { + "id": "id:docs:doc::1", + "relevance": 0.2823215567939546, + "source": "docs", + "fields": { + "matchfeatures": { + "attribute(downloads)": 10.0, + "bm25(name)": 0.1823215567939546 + }, + "rerank-score": 0.2823215567939546, + "sddocname": "doc", + "documentid": "id:docs:doc::1", + "name": "Another sample document", + "downloads": 10 + } + } + ] + } +} +``` + +### Teardown + +Remove app and data: + +```bash +$ docker rm -f vespa +``` \ No newline at end of file diff --git a/en/ranking/significance.md b/en/ranking/significance.md deleted file mode 100644 index af433c9642..0000000000 --- a/en/ranking/significance.md +++ /dev/null @@ -1,181 +0,0 @@ ---- -# Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -title: "Significance Model" -redirect_from: - - /en/significance ---- - -*Significance* is a measure of how rare a term is in a collection of documents. -Rare terms like "neurotransmitter" are weighted higher during ranking than common terms like "the". -Significance is often calculated as the inverse document frequency (IDF): - -$$ IDF(t, N) = log(\frac{N}{n_t}) $$ - -where: -- $$ N $$ is the total number of documents in the collection -- $$ n_t $$ is the number of documents containing the term $$ t $$ - -Variations of IDF are used in [bm25](bm25.html) and [nativeRank](nativerank.html). - -*Significance model* provides the data necessary to calculate IDF, i.e. $$ n_t $$ for each term and $$ N $$ for the document collection. -We distinguish between *local and global* significance models. -A local model is node-specific and a global model is shared across nodes. - - -## Local significance model - -For `string` fields indexed with [bm25](bm25.html) or [nativeRank](nativerank.html), -Vespa creates a local significance model on each content node from the documents it contains. -For each query term, the document frequency is computed by aggregating the per-field document frequencies across all fields being searched. - -Different nodes can have different significance values for the same term. -In large collections, this difference is usually small and doesn’t affect ranking quality. - -One issue with the local models is that ranking is non-deterministic in the following cases: -1. When new documents are added, local models on affected content nodes are updated. -2. When the content cluster [redistributes documents](../content/elasticity.html) across nodes, e.g. adding, removing nodes for scaling and failure recovery, the models change on the nodes involved. -3. When using [grouped distribution](../content/elasticity.html#grouped-distribution), -queries can return different results depending on which group processes them. - -Another issue is that local significance models are not available in [streaming search](../performance/streaming-search.html) -because inverted indexes are not constructed so IDF values can't be extracted. -All significance values are set to 1, which is the default value for unknown terms. -The lack of significance values may degrade the ranking quality. - -A global significance model addresses these issues. - -## Global significance model - -In a *global significance model*, significance values are shared across nodes and don’t change when new documents are added. There are two ways to provide a global model: - -1. Include [significance values in a query](#significance-values-in-a-query). -2. Set [significance values in a searcher](#significance-values-in-a-searcher). -3. Specify [models in services.xml](#significance-models-in-servicesxml). - -### Significance values in a query - -Document frequency and document count can be specified in YQL, e.g.: -```sql -select * from example where content contains ({documentFrequency: {frequency: 13, count: 101}}"colors") -``` - -Alternatively, significance values can be specified in YQL directly and used instead of computed IDF values, e.g.: -```sql -select * from example where content contains ({significance:0.9}"neurotransmitter") -``` - -### Significance values in a searcher - -Document frequency and significance values can be also set in a [custom searcher](../applications/searchers.html#writing-a-searcher): - -```java -private void setDocumentFrequency(WordItem item, long frequency, long numDocuments) { - var word = item.getWord(); - word.setDocumentFrequency(new DocumentFrequency(frequency, numDocuments)); -} - -private void setSignificance(WordItem item, float significance) { - var word = item.getWord(); - word.setSignificance(significance); -} -``` - - -### Significance models in services.xml - -[`significance` element in services.xml](../reference/applications/services/search.html#significance) specifies one or more models: - -```xml -<container version="1.0"> - <search> - <significance> - <model model-id="significance-en-wikipedia-v1"/> - <model url="https://some/uri/mymodel.multilingual.json" /> - <model path="models/mymodel.no.json.zst" /> - </significance> - </search> -</container> -``` - -Vespa Cloud users have access to [pre-built models](https://cloud.vespa.ai/en/model-hub#significance-models), identified by `model-id`. -In addition, all users can specify their own models by providing a `url` to an external resource or a `path` to a model file within the application package. -Vespa provides a [command line tool](../reference/operations/self-managed/tools.html#vespa-significance) to generate [model files](#significance-model-file) from documents. -The order in which the models are specified determines the model precedence, see [model resolution](#model-resolution) for details. - -In addition to adding models in [services.xml](../reference/applications/services/search.html#significance), -the `significance` feature must be enabled in the [`rank-profile` section of the schema](../reference/schemas/schemas.html#significance), e.g. - -```xml -schema example { - document example { - field content type string { - indexing: index | summary - index: enable-bm25 - } - } - - rank-profile default { - significance { - use-model: true - } - } -} -``` - -The model will be applied to all query terms except those that already have significance values from the query. - -Specifying significance models in services.xml is available in Vespa as of version 8.426.8. - -#### Significance model file - -The significance model file is a JSON file that contains term document frequencies and document count for one or more languages, e.g. - -```json -{ - "version": 1, - "id": "wikipedia", - "description": "Some optional description", - "languages": { - "en": { - "description": "Some optional description for English model", - "document-count": 1000, - "document-frequencies": { - "and": 500, - "car": 100, - ... - } - }, - "no": { - "description": "Some optional description for Norwegian model", - "document-count": 800, - "document-frequencies": { - "bil": 80, - "og": 400, - ... - } - } - } -} -``` - -A significance model file can be compressed with [zstandard](https://facebook.github.io/zstd/) -when included in the application package or made available via a URL. - -Vespa provides a <a href="../reference/operations/self-managed/tools.html#vespa-significance">CLI tool</a> for generating significance model files from Vespa documents. -The tool uses the same <a href="../linguistics/linguistics.html">linguistic module</a> as query processing to extract tokens and their document frequencies. -The CLI can also export local significance models from content nodes to an intermediate file format and merge these intermediate files into a single file, which can then be converted into a global significance model file. - -#### Model resolution - -Model resolution selects a model from the models specified in [services.xml](#significance-models-in-servicesxml) based on the language of the query. -The language can be either [explicitly tagged](../reference/api/query.html#model.language) or [implicitly detected](../linguistics/linguistics.html#query-language-detection). - -The resolution logic is as follows: -- When language is explicitly tagged - - Select the last specified model that has the tagged language. - Fail if none are available. - - If the language is tagged as “un” (unknown), select the model for “un” first, fall back to “en” (English). - Fail if none are available. -- When language is implicitly detected - - Select the last specified model with the detected language. If not available, try “un” and then “en” languages. - Fail if none are available. diff --git a/en/ranking/significance.mdx b/en/ranking/significance.mdx new file mode 100644 index 0000000000..0c21b65f56 --- /dev/null +++ b/en/ranking/significance.mdx @@ -0,0 +1,164 @@ +--- +title: "Significance Model" +--- + +*Significance* is a measure of how rare a term is in a collection of documents. Rare terms like "neurotransmitter" are weighted higher during ranking than common terms like "the". Significance is often calculated as the inverse document frequency (IDF): + + +$$ +I D F \left(\right. t , N \left.\right) = l o g \left(\right. \frac{N}{n_{t}} \left.\right) +$$ + + +where: + +- $N$ is the total number of documents in the collection +- $n_{t}$ is the number of documents containing the term $t$ + +Variations of IDF are used in [bm25](/en/ranking/bm25) and [nativeRank](/en/ranking/nativerank). + +*Significance model* provides the data necessary to calculate IDF, i.e. $n_{t}$ for each term and $N$ for the document collection. We distinguish between *local and global* significance models. A local model is node-specific and a global model is shared across nodes. + +## Local significance model + +For `string` fields indexed with [bm25](/en/ranking/bm25) or [nativeRank](/en/ranking/nativerank), Vespa creates a local significance model on each content node from the documents it contains. For each query term, the document frequency is computed by aggregating the per-field document frequencies across all fields being searched. + +Different nodes can have different significance values for the same term. In large collections, this difference is usually small and doesn’t affect ranking quality. + +One issue with the local models is that ranking is non-deterministic in the following cases: + +1. When new documents are added, local models on affected content nodes are updated. +2. When the content cluster [redistributes documents](/en/content/elasticity) across nodes, e.g. adding, removing nodes for scaling and failure recovery, the models change on the nodes involved. +3. When using [grouped distribution](/en/content/elasticity#grouped-distribution), queries can return different results depending on which group processes them. + +Another issue is that local significance models are not available in [streaming search](/en/performance/streaming-search) because inverted indexes are not constructed so IDF values can't be extracted. All significance values are set to 1, which is the default value for unknown terms. The lack of significance values may degrade the ranking quality. + +A global significance model addresses these issues. + +## Global significance model + +In a *global significance model*, significance values are shared across nodes and don’t change when new documents are added. There are two ways to provide a global model: + +1. Include [significance values in a query](/en/ranking/significance#significance-values-in-a-query) +2. Set [significance values in a searcher](/en/ranking/significance#significance-values-in-a-searcher) +3. Specify [models in services.xml](/en/ranking/significance#significance-models-in-services-xml) + + +### Significance values in a query + +Document frequency and document count can be specified in YQL, e.g.: + +```js +select * from example where content contains ({documentFrequency: {frequency: 13, count: 101}}"colors") +``` + +Alternatively, significance values can be specified in YQL directly and used instead of computed IDF values, e.g.: + +```js +select * from example where content contains ({significance:0.9}"neurotransmitter") +``` + +### Significance values in a searcher + +Document frequency and significance values can be also set in a [custom searcher](../applications/searchers#writing-a-searcher): + +```js +private void setDocumentFrequency(WordItem item, long frequency, long numDocuments) { + var word = item.getWord(); + word.setDocumentFrequency(new DocumentFrequency(frequency, numDocuments)); +} + +private void setSignificance(WordItem item, float significance) { + var word = item.getWord(); + word.setSignificance(significance); +} +``` + +### Significance models in services.xml + +[`significance` element in services.xml](/en/reference/applications/services/search#significance) specifies one or more models: + +```xml +<container version="1.0"> + <search> + <significance> + <model model-id="significance-en-wikipedia-v1"/> + <model url="https://some/uri/mymodel.multilingual.json" /> + <model path="models/mymodel.no.json.zst" /> + </significance> + </search> +</container> +``` + +Vespa Cloud users have access to [pre-built models](/en/rag/model-hub), identified by `model-id`. In addition, all users can specify their own models by providing a `url` to an external resource or a `path` to a model file within the application package. Vespa provides a [command line tool](/en/reference/operations/self-managed/tools#vespa-significance) to generate [model files](#significance-model-file) from documents. The order in which the models are specified determines the model precedence, see [model resolution](#model-resolution) for details. + +In addition to adding models in [services.xml](/en/reference/applications/services/search#significance), the `significance` feature must be enabled in the [`rank-profile` section of the schema](/en/reference/schemas/schemas#significance), e.g. + +```js +schema example { + document example { + field content type string { + indexing: index | summary + index: enable-bm25 + } + } + + rank-profile default { + significance { + use-model: true + } + } +} +``` + +The model will be applied to all query terms except those that already have significance values from the query. + +Specifying significance models in services.xml is available in Vespa as of version 8.426.8. + +#### Significance model file + +The significance model file is a JSON file that contains term document frequencies and document count for one or more languages, e.g. + +```json expandable highlight= {12,21} +{ + "version": 1, + "id": "wikipedia", + "description": "Some optional description", + "languages": { + "en": { + "description": "Some optional description for English model", + "document-count": 1000, + "document-frequencies": { + "and": 500, + "car": 100, + ... + } + }, + "no": { + "description": "Some optional description for Norwegian model", + "document-count": 800, + "document-frequencies": { + "bil": 80, + "og": 400, + ... + } + } + } +} +``` + +A significance model file can be compressed with [zstandard](https://facebook.github.io/zstd/) when included in the application package or made available via a URL. + +Vespa provides a [CLI tool](/en/reference/operations/self-managed/tools#vespa-significance) for generating significance model files from Vespa documents. The tool uses the same [linguistic module](/en/linguistics/linguistics) as query processing to extract tokens and their document frequencies. The CLI can also export local significance models from content nodes to an intermediate file format and merge these intermediate files into a single file, which can then be converted into a global significance model file. + +#### Model resolution + +Model resolution selects a model from the models specified in [services.xml](#significance-models-in-servicesxml) based on the language of the query. The language can be either [explicitly tagged](/en/reference/api/query#model.language) or [implicitly detected](/en/linguistics/linguistics#query-language-detection). + +The resolution logic is as follows: + +- When language is explicitly tagged + - Select the last specified model that has the tagged language. Fail if none are available. + - If the language is tagged as “un” (unknown), select the model for “un” first, fall back to “en” (English). Fail if none are available. +- When language is implicitly detected + - Select the last specified model with the detected language. If not available, try “un” and then “en” languages. Fail if none are available. \ No newline at end of file diff --git a/en/ranking/stateless-model-evaluation.html b/en/ranking/stateless-model-evaluation.html deleted file mode 100644 index d582d2e22a..0000000000 --- a/en/ranking/stateless-model-evaluation.html +++ /dev/null @@ -1,332 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Stateless Model Evaluation" -redirect_from: -- /en/stateless-model-evaluation ---- - -<p> -Vespa's speciality is evaluating machine-learned models quickly over large numbers of data points. -However, it can also be used to evaluate models once on request in stateless containers. -By enabling a feature in <a href="../reference/applications/services/services.html">services.xml</a>, -all machine-learned models - -<a href="tensorflow">TensorFlow</a>, -<a href="onnx">Onnx</a>, -<a href="xgboost">XGBoost</a>, -<a href="lightgbm">LightGBM</a> and -<a href="../reference/ranking/model-files.html">Vespa stateless models</a> - -added to the <code>models/</code> directory of the -<a href="../reference/applications/application-packages.html">application package</a>, -are made available through both a REST API and a Java API -where you can compute inferences from your own code. -</p><p> -An example application package can be found at in the -<a href="https://github.com/vespa-engine/system-test/tree/master/tests/container/model_evaluation/app"> -model-evaluation system test</a>. -</p> - - -<h3 id="add-the-model-evaluation-tag">The model evaluation tag</h3> - -<p>To enable both the REST API and the Java API, add the <code>model-evaluation</code> tag -inside the <a href="../applications/containers.html">container</a> clusters where -it is needed in <a href="../reference/applications/services/services.html">services.xml</a>: -</p> -<pre> -<container> - ... - <span class="pre-hilite"><model-evaluation/></span> - ... -</container> -</pre> -<p> -The <code>model-evaluation</code> section can optionally contain inference session options for -ONNX models. See <a href="#onnx-inference-options">ONNX inference options</a>. -</p> - - - -<h2 id="model-inference-using-the-rest-api">Model inference using the REST API</h2> - -<p>The simplest way to evaluate the model is to use the REST API. -After enabling it as above, a new API path is made available: -<code>/model-evaluation/v1/</code>. -To discover and find information about the models -(including expected input parameters to the model) in your application package, -simply follow the links from this root. -To evaluate a model add <code>/eval</code> to the query path: -</p> -<pre> -http://host:port/model-evaluation/v1/<model-name>/<function>/eval?<param1=...>&... -</pre> -<p> -Here <model-name> signifies which model to evaluate as you can deploy -multiple models in your application package. -The <function> specifies -which signature and output to evaluate as a model might have multiple -signatures and outputs you can evaluate. -If a model only has one function, this can be omitted. -Inputs to the model are specified as query parameters for GET requests, -and they can also be in the body part of the request for POST requests. -The expected format for input parameters are tensors as specified -with the <a href="../reference/ranking/tensor.html#tensor-literal-form">literal form</a>. -</p> - -<p> -See the -<a href="https://github.com/vespa-engine/sample-apps/tree/master/model-inference"> -model-inference sample app</a> for an example of this. -</p> - -<h3 id="model-evaluation-rest-api-parameters">Model evaluation REST API parameters</h3> - -<p>Model evaluation requests accepts these request parameters:</p> - -<table class="table"> - <thead> - <tr> - <th>Parameter</th> - <th>Type</th> - <th>Description</th> - </tr> - </thead><tbody> -<tr> - <th>format.tensors</th> - <td>String</td> - <td> - <p id="format.tensors"> - Controls how tensors are rendered in the result. - </p> - <table class="table"> - <thead> - <tr> - <th>Value</th> - <th>Description</th> - </tr> - </thead> - <tbody> - <tr> - <td><code>short</code></td> - <td> - <b>Default</b>. Render the tensor value in a JSON object having two keys, "type" containing the value, - and "cells"/"blocks"/"values" (<a href="../reference/schemas/document-json-format.html#tensor">depending on the type</a>) containing the tensor content.<br/> - Render the tensor content in the <a href="../reference/schemas/document-json-format.html#tensor">type-appropriate short form</a>. - </td> - </tr> - <tr> - <td><code>long</code></td> - <td> - Render the tensor value in a JSON object having two keys, "type" containing the value, - and "cells" containing the tensor content.<br/> - Render the tensor content in the <a href="../reference/schemas/document-json-format.html#tensor">general verbose form</a>. - </td> - </tr> - <tr> - <td><code>short-value</code></td> - <td> - Render the tensor content directly as a JSON value.<br/> - Render the tensor content in the <a href="../reference/schemas/document-json-format.html#tensor">type-appropriate short form</a>. - </td> - </tr> - <tr> - <td><code>long-value</code></td> - <td> - Render the tensor content directly as a JSON value.<br/> - Render the tensor content in the <a href="../reference/schemas/document-json-format.html#tensor">general verbose form</a>. - </td> - </tr> - <tr> - <td><code>string</code></td> - <td> - Render the tensor content as a string on the <a href="../reference/ranking/tensor.html#tensor-literal-form">appropriate literal short form</a>. - </td> - </tr> - <tr> - <td><code>string-long</code></td> - <td> - Render the tensor content as a string on the <a href="../reference/ranking/tensor.html#general-literal-form">general literal form</a>. - </td> - </tr> - </table> - </td> -</tr> -</table> - - -<h2 id="model-inference-using-java">Model inference using Java</h2> - -<p>While the REST API gives a basic interface to run model inference, -the Java interface offers far more control -allowing you to for instance implement custom input and output formats.</p> - -<p>First, add the following dependency in <code>pom.xml</code>:</p> - -<pre> -<dependency> - <groupId>com.yahoo.vespa</groupId> - <artifactId>container</artifactId> - <scope>provided</scope> -</dependency> -</pre> - -<p>(Or, if you want the minimal dependency, -depend on <code>model-evaluation</code> instead of <code>container</code>.)</p> - -<p>With the dependency above and the <code>model-evaluation</code> element -added to <code>services.xml</code>, -you can now have your Java component that should evaluate models -take a <code>ai.vespa.models.evaluation.ModelsEvaluator</code> -(see <a href="https://github.com/vespa-engine/vespa/blob/master/model-evaluation/src/main/java/ai/vespa/models/evaluation/ModelsEvaluator.java"> -ModelsEvaluator.java</a>) instance as a constructor argument -(Vespa will <a href="../applications/dependency-injection.html">automatically inject it</a>).</p> - -<p>Use the <code>ModelsEvaluator</code> API (from any thread) to make inferences. Sample code:</p> - -<pre>{% highlight java %} -import ai.vespa.models.evaluation.ModelsEvaluator; -import ai.vespa.models.evaluation.FunctionEvaluator; -import com.yahoo.tensor.Tensor; - -// ... - -// Create evaluator -FunctionEvaluator evaluator = modelsEvaluator.evaluatorOf("myModel", "mySignature", "myOutput"); // Unambiguous args may be skipped - -// Get model inputs for instance from query (here we just construct a sample tensor) -Tensor.Builder b = Tensor.Builder.of(new TensorType.Builder().indexed("d0", 3)); -b.cell(0.1, 0); -b.cell(0.2, 0); -b.cell(0.3, 0); -Tensor input = b.build(); - -// Bind inputs to the evaluator -evaluator.bind("myInput", input); - -// Evaluate model. Note: Evaluator must be discarded after a single use -Tensor result = evaluator.evaluate()); - -// Do something with the result -{% endhighlight %}</pre> - -<p>The <a href="https://github.com/vespa-engine/sample-apps/tree/master/model-inference"> -model-inference sample app</a> also has an example of this.</p> - - -<h2 id="unit-testing-model-evaluation-in-java">Unit testing model evaluation in Java</h2> - -<p>When developing your application it can be helpful to unit test your -models and/or your searchers and document processors during development. Vespa -provides a <code>ModelsEvaluatorTester</code> which can be constructed from the -contents of your "models" directory. This allows for testing that the model -works as expected in context of Vespa, and that your searcher or document -processor gets the correct results from your models.</p> - -<p>The following dependency is needed in <code>pom.xml</code>:</p> - -<pre> -<dependency> - <groupId>com.yahoo.vespa</groupId> - <artifactId>container-test</artifactId> - <scope>test</scope> -</dependency> -</pre> - -<p>With this you can construct a testable <code>ModelsEvaluator</code>:</p> - -<pre>{% highlight java %} -import com.yahoo.vespa.model.container.ml.ModelsEvaluatorTester; - -public class ModelsTest { - @Test - public void testModels() { - ModelsEvaluator modelsEvaluator = ModelsEvaluatorTester.create("src/main/application/models"); - - // Test the modelsEvaluator directly or construct a searcher and pass it in - - } -} -{% endhighlight %}</pre> - -<p>The <code>ModelsEvaluator</code> object that is returned contains all models -found under the directory pass in. Note that this should only be used in unit -testing.</p> - -<p>The <a href="https://github.com/vespa-engine/sample-apps/tree/master/model-inference"> -model-inference sample app</a> uses this for testing handlers, searchers, and document -processors.</p> - - -<h2 id="onnx-inference-options">ONNX inference options</h2> - -<p>ONNX models are evaluated using <a href="https://onnxruntime.ai/">ONNX Runtime</a>. -Vespa provides the following options to tune inference:</p> - -<pre> -<model-evaluation> - <onnx> - <models> - <model name="reranker_margin_loss_v4"> - <intraop-threads>[number]</intraop-threads> - <interop-threads>[number]</interop-threads> - <execution-mode>parallel | sequential</execution-mode> - <gpu-device>[number]</gpu-device> - </model> - </models> - </onnx> -</model-evaluation> -</pre> - -<table class="table"> - <thead> - <tr> - <th>Attribute</th> - <th>Required</th> - <th>Value</th> - <th>Default</th> - <th>Description</th> - </tr> - </thead> - <tbody> - <tr> - <td>intraop-threads</td> - <td>optional</td> - <td>number</td> - <td>max(1, CPU count / 4)</td> - <td>The number of threads available for running operations with - multithreaded implementations. - </td> - </tr> - <tr> - <td>interop-threads</td> - <td>optional</td> - <td>number</td> - <td><code>max(1, CPU count / 4)</code> if execution mode <code>parallel</code></td> - <td>The number of threads available for running multiple operations in - parallel. This is only applicable for <code>parallel</code> execution mode. - </td> - </tr> - <tr> - <td>execution-mode</td> - <td>optional</td> - <td>string</td> - <td>sequential</td> - <td>Controls how the operators of a graph are executed, - either <code>sequential</code> or <code>parallel</code>. - </td> - </tr> - <tr> - <td>gpu-device</td> - <td>optional</td> - <td>number</td> - <td></td> - <td>Set the GPU device number to use for computation, starting at 0, i.e. - if your GPU is <code>/dev/nvidia0</code> set this to 0. This must be an - Nvidia CUDA-enabled GPU.</td> - </tr> - </tbody> -</table> - -<p>Since stateless model evaluation is based on auto-discovery of models under the -<code>models</code> directory in the application package, the above would only -be needed for models that should not use the default settings, or should run on -a GPU.</p> diff --git a/en/ranking/stateless-model-evaluation.mdx b/en/ranking/stateless-model-evaluation.mdx new file mode 100644 index 0000000000..10a14f0138 --- /dev/null +++ b/en/ranking/stateless-model-evaluation.mdx @@ -0,0 +1,199 @@ +--- +title: "Stateless Model Evaluation" +--- + +Vespa's speciality is evaluating machine-learned models quickly over large numbers of data points. However, it can also be used to evaluate models once on request in stateless containers. By enabling a feature in [services.xml](/en/reference/applications/services/services), all machine-learned models - [TensorFlow](/en/ranking/tensorflow), [Onnx](/en/ranking/onnx), [XGBoost](/en/ranking/xgboost), [LightGBM](/en/ranking/lightgbm) and [Vespa stateless models](/en/reference/ranking/model-files) - added to the `models/` directory of the [application package](/en/reference/applications/application-packages), are made available through both a REST API and a Java API where you can compute inferences from your own code. + +An example application package can be found at in the [model-evaluation system test](https://github.com/vespa-engine/system-test/tree/master/tests/container/model_evaluation/app). + +### The model evaluation tag + +To enable both the REST API and the Java API, add the `model-evaluation` tag inside the [container](/en/applications/containers) clusters where it is needed in [services.xml](/en/reference/applications/services/services): + +```bash highlight= {3} +<container> + ... + <model-evaluation/> + ... +</container> +``` + +The `model-evaluation` section can optionally contain inference session options for ONNX models. See [ONNX inference options](#onnx-inference-options). + +## Model inference using the REST API + +The simplest way to evaluate the model is to use the REST API. After enabling it as above, a new API path is made available: `/model-evaluation/v1/`. To discover and find information about the models (including expected input parameters to the model) in your application package, simply follow the links from this root. To evaluate a model add `/eval` to the query path: + +```bash +http://host:port/model-evaluation/v1/<model-name>/<function>/eval?<param1=...>&... +``` + +Here `<model-name>` signifies which model to evaluate as you can deploy multiple models in your application package. The `<function>` specifies which signature and output to evaluate as a model might have multiple signatures and outputs you can evaluate. If a model only has one function, this can be omitted. Inputs to the model are specified as query parameters for GET requests, and they can also be in the body part of the request for POST requests. The expected format for input parameters are tensors as specified with the [literal form](/en/reference/ranking/tensor#tensor-literal-form). + +See the [model-inference sample app](https://github.com/vespa-engine/sample-apps/tree/master/model-inference) for an example of this. + +### Model evaluation REST API parameters + +Model evaluation requests accepts these request parameters: + +<table className="vespa-table"> + <thead> + <tr> + <th>Parameter</th> + <th>Type</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>**format.tensors**</td> + <td>String</td> + <td>Controls how tensors are rendered in the result.<br/><br/> Value: <code>{`short`}</code> <br/>Description:<br/> **Default**. Render the tensor value in a JSON object having two keys, "type" containing the value, and "cells"/"blocks"/"values" (<a href="/en/reference/schemas/document-json-format#tensor" aria-label="depending on the type">depending on the type</a>) containing the tensor content. Render the tensor content in the <a href="/en/reference/schemas/document-json-format#tensor" aria-label="type-appropriate short form">type-appropriate short form</a>. <br/><br/> Value: <code>{`long`}</code><br/>Description:<br/> Render the tensor value in a JSON object having two keys, "type" containing the value, and "cells" containing the tensor content. <br/> Render the tensor content in the <a href="/en/reference/schemas/document-json-format#tensor" aria-label="general verbose form">general verbose form</a>. <br/><br/> Value: <code>{`short-value`}</code> <br/>Description:<br/> Render the tensor content directly as a JSON value.<br/> Render the tensor content in the <a href="/en/reference/schemas/document-json-format#tensor" aria-label="type-appropriate short form">type-appropriate short form</a>. <br/><br/> Value: <code>{`long-value`}</code> <br/>Description:<br/> Render the tensor content directly as a JSON value. <br/> Render the tensor content in the <a href="/en/reference/schemas/document-json-format#tensor" aria-label="general verbose form">general verbose form</a>. <br/><br/> Value: <code>{`string`}</code> <br/>Description:<br/> Render the tensor content as a string on the <a href="/en/reference/ranking/tensor#tensor-literal-form" aria-label="appropriate literal short form">appropriate literal short form</a>. <br/><br/> Value: <code>{`string-long`}</code><br/>Description:<br/> Render the tensor content as a string on the <a href="/en/reference/ranking/tensor#general-literal-form" aria-label="general literal form">general literal form</a>.</td> + </tr> + </tbody> +</table> + +## Model inference using Java + +While the REST API gives a basic interface to run model inference, the Java interface offers far more control allowing you to for instance implement custom input and output formats. + +First, add the following dependency in `pom.xml`: + +```xml +<dependency> + <groupId>com.yahoo.vespa</groupId> + <artifactId>container</artifactId> + <scope>provided</scope> +</dependency> +``` + +(Or, if you want the minimal dependency, depend on `model-evaluation` instead of `container`.) + +With the dependency above and the `model-evaluation` element added to `services.xml`, you can now have your Java component that should evaluate models take a `ai.vespa.models.evaluation.ModelsEvaluator` (see [ModelsEvaluator.java](https://github.com/vespa-engine/vespa/blob/master/model-evaluation/src/main/java/ai/vespa/models/evaluation/ModelsEvaluator.java)) instance as a constructor argument (Vespa will [automatically inject it](/en/applications/dependency-injection)). + +Use the `ModelsEvaluator` API (from any thread) to make inferences. Sample code: + +```java expandable +import ai.vespa.models.evaluation.ModelsEvaluator; +import ai.vespa.models.evaluation.FunctionEvaluator; +import com.yahoo.tensor.Tensor; + +// ... + +// Create evaluator +FunctionEvaluator evaluator = modelsEvaluator.evaluatorOf("myModel", "mySignature", "myOutput"); // Unambiguous args may be skipped + +// Get model inputs for instance from query (here we just construct a sample tensor) +Tensor.Builder b = Tensor.Builder.of(new TensorType.Builder().indexed("d0", 3)); +b.cell(0.1, 0); +b.cell(0.2, 0); +b.cell(0.3, 0); +Tensor input = b.build(); + +// Bind inputs to the evaluator +evaluator.bind("myInput", input); + +// Evaluate model. Note: Evaluator must be discarded after a single use +Tensor result = evaluator.evaluate()); + +// Do something with the result +``` + +The [model-inference sample app](https://github.com/vespa-engine/sample-apps/tree/master/model-inference) also has an example of this. + +## Unit testing model evaluation in Java + +When developing your application it can be helpful to unit test your models and/or your searchers and document processors during development. Vespa provides a `ModelsEvaluatorTester` which can be constructed from the contents of your "models" directory. This allows for testing that the model works as expected in context of Vespa, and that your searcher or document processor gets the correct results from your models. + +The following dependency is needed in `pom.xml`: + +```bash +<dependency> + <groupId>com.yahoo.vespa</groupId> + <artifactId>container-test</artifactId> + <scope>test</scope> +</dependency> +``` + +With this you can construct a testable `ModelsEvaluator`: + +```java +import com.yahoo.vespa.model.container.ml.ModelsEvaluatorTester; + +public class ModelsTest { + @Test + public void testModels() { + ModelsEvaluator modelsEvaluator = ModelsEvaluatorTester.create("src/main/application/models"); + + // Test the modelsEvaluator directly or construct a searcher and pass it in + + } +} +``` + +The `ModelsEvaluator` object that is returned contains all models found under the directory pass in. Note that this should only be used in unit testing. + +The [model-inference sample app](https://github.com/vespa-engine/sample-apps/tree/master/model-inference) uses this for testing handlers, searchers, and document processors. + +## ONNX inference options + +ONNX models are evaluated using [ONNX Runtime](https://onnxruntime.ai/). Vespa provides the following options to tune inference: + +```bash +<model-evaluation> + <onnx> + <models> + <model name="reranker_margin_loss_v4"> + <intraop-threads>[number]</intraop-threads> + <interop-threads>[number]</interop-threads> + <execution-mode>parallel | sequential</execution-mode> + <gpu-device>[number]</gpu-device> + </model> + </models> + </onnx> +</model-evaluation> +``` + +<table className="vespa-table"> + <thead> + <tr> + <th>Attribute</th> + <th>Required</th> + <th>Value</th> + <th>Default</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>intraop-threads</td> + <td>optional</td> + <td>number</td> + <td>max(1, CPU count / 4)</td> + <td>The number of threads available for running operations with multithreaded implementations.</td> + </tr> + <tr> + <td>interop-threads</td> + <td>optional</td> + <td>number</td> + <td><code>{`max(1, CPU count / 4)`}</code> if execution mode <code>{`parallel`}</code></td> + <td>The number of threads available for running multiple operations in parallel. This is only applicable for <code>{`parallel`}</code> execution mode.</td> + </tr> + <tr> + <td>execution-mode</td> + <td>optional</td> + <td>string</td> + <td>sequential</td> + <td>Controls how the operators of a graph are executed, either <code>{`sequential`}</code> or <code>{`parallel`}</code>.</td> + </tr> + <tr> + <td>gpu-device</td> + <td>optional</td> + <td>number</td> + <td></td> + <td>Set the GPU device number to use for computation, starting at 0, i.e. if your GPU is <code>{`/dev/nvidia0`}</code> set this to 0. This must be an Nvidia CUDA-enabled GPU.</td> + </tr> + </tbody> +</table> + +Since stateless model evaluation is based on auto-discovery of models under the `models` directory in the application package, the above would only be needed for models that should not use the default settings, or should run on a GPU. \ No newline at end of file diff --git a/en/ranking/tensor-examples.html b/en/ranking/tensor-examples.html deleted file mode 100644 index d258c72eee..0000000000 --- a/en/ranking/tensor-examples.html +++ /dev/null @@ -1,328 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Tensor Computation Examples" -redirect_from: -- /en/tensor-examples.html ---- - -<p>Tensors can be used to express machine-learned models such as neural nets, -but they can be used for much more than that. The tensor model in Vespa is powerful, -since it supports sparse dimensions, dimension names and lambda computations. - -Whatever you want to compute, it is probably possible to express it succinctly as a tensor expression - the problem -is learning how. This page collects some real-world examples of tensor usage to provide some inspiration.</p> - - -<h2 id="tensor-playground-examples">Tensor playground</h2> - -<p>The tensor playground is a tool to get familiar with and explore tensor algebra. It can be found -at <a href="https://docs.vespa.ai/playground/">docs.vespa.ai/playground</a>. Below are some examples - of common tensor compute operations using <a href="../reference/ranking/ranking-expressions.html#tensor-functions">tensor functions</a>. - - Feel free to play around with them to explore further:</p> - -<ul> - <li><a href="https://docs.vespa.ai/playground/#N4KABGBEBmkFxgNrgmUrWQPYAd5QGNIAaFDSPBdDTAF30gBEBTAOwGdmxa32sAnMABMstMDn5YhAVwK0AdGABiAS37sxAdy5Dm0Fay4BDbrwHswAAwCClsEdZCrAITtGLujlx4dzcSGSoAL6BQaQY1OS4DMwkgRAU+JE0kKwM1nE0mLEIkD58-AAUAB6IAMwAugCUcIgADMRgdfIAjI11FQFZIRhhgckJ0bmx4SmUaPGYabnOmVlQOVD5AiXl1bXNbc0ATMQdXTQ9waMQA1BDhHPk42cJ9LkAKgAW3ppYpr7qYE9GAG7eLzA7CMAFsdCowRwVFhWGBWKDmOxGnx7GAQdIADa0FQ4DEqAhGbEwsAqCzMDHMSG0AC0mlJzH8kyOED6EUm2HGkBG7JukwS0ygVxSi0g1jAACowLMmaEThMshyGEQ5QlefMlgxnjpROJJDI5CSLLRAexpCCLFh-oJjVwCFgQSDiUIIbxoaxGd1Zf12RcuULVUk+VABZB-dkGKaQYVrOLnFUDr1QigKiAgkA">Dense tensor dot product</a> - <li><a href="https://docs.vespa.ai/playground/#N4KABGBEBmkFxgNrgmUrWQPYAd5QGNIAaFDSPBdDTAF30gGUcBDAJwGcBTMWrgOw5Y2YACZZaYHGyyiArgVoA6MADEAlp1rEwAdx6iu0dfx61dWMB1aczAoZzAADAIJOwLfqOcAhJyoAdfgA5CXUCMwALHgiAG1iOXkiWSWSANwEAckkAIy4BMSMTLm9xLkT+CTB0nhYwNJZYuS44SDJUAF92jtIManJcBi4SdogKfH6aSH4GFxGaTGGESD5BYQAKAA9gDoBKOGAARjgABiVDjraFrowe9smxweXh3qnKNFHMGeWfeYWoJZQVYOLY7fbAE6nc7EY5nABMl0+N06rwgDygT0If3I73RY3oywAkvwkjwALZyWK0dQ4WLhFLqLD8HRM2IAT1JYDJKQIkRMAHMwHEEh42DFGgRKSkSq0kd1UR8Fth3pAXp9xlRPmNvlBsVNAZAXGAAFRgX5y24K9HKhhEBVjXFaoEMAAq0TEVWksgUknUiVo7opVJpdIIDKZYGgWHiWH03hyHLqHDkZLAWAyIgDMSwZLJEdE6jJ9kZ-Fl13l93VmNVeodEyd0wYtcWDGTZPWLmNPh0m12VxoyIgdz6VZVdvVjv+zuWbr9YDnXAAjnJ1A1YgJJLQsGWBxWR0rq2qlZP-o3ls2xgaxfIIusAFZYEwdnTdyNbYhs3ZbY2f3Y6Nt9hanQoAAuiAHRAA">Sparse tensor dot product</a></li> - <li><a href="https://docs.vespa.ai/playground/#N4KABGBEBmkFxgNrgmUrWQPYAd5QGNIAaFDSPBdDTAF30gDUBTA2rAJwFoBbAQ1ocAlgA8wODlgAmAVzYA6MADEhHAM60wAd2ZgpzaEIB2uvmABurdhzAADRrbB8jUp2H6DRdgLK24kMlQAX0Cg0gxqclwGZhJAiAp8SJpIIwZGOJpMWIRIWmYjNU4AChFEAGYAXQBKOEQABnkAVmIwRpa25sqArJCMMMDkhOjc2PCUyjR4zDTc70ysqByofMKSggrK4jKq2sQG+XrWxoBGY-kAJi2kE8PW27OwW6vWxAu7sHfH96vu6b7guMIEMoCNCAtyJMQQl6LkAIIWKycXgCYRiCTSOSaIRqMC0AAWuh4MgANrQhDgSUICAIhFgjGAsNA8YTEWxOE4XO5UV5nK41DIeDxjABzRmWGwE3QELBC+l6IQ8ApqOlGfz-UJAqZZbCTSBjaaJKjTBKzKAQlLLSACnjFRhgABUYG8rRE1R6NABEAGEUNYMgRC1CShJpWDAAclh8iyBCzdJx9DYme5SeTKdTafKpFhmGojAByTQefIcRQASWxuKlCqVhVVeKwYBt4uYkvxseLrbU8g9-U1gz9eoNOpDiygZsgFvIVptxW8jrAjFd7o1-S1IN1DEDhtHizyDAAKqy+AAjLCWMA4sDMACOMiE5j4JIKmnY6t6-d9Ov9w4mSVDqQMFO2QMBwzCyAQzDFAAVlgxh2q0LpgNApTEAAntUpQLhh1StDay69sEoQoJUIBBEAA">Vector-matrix product</a></li> - <li><a href="https://docs.vespa.ai/playground/#N4KABGBEBmkFxgNrgmUrWQPYAd5QGNIAaFDSPBdDTAF30gFEAPAQwFscAbAUzC2hhWYdq1oAnAJbMAtKInSwOcVgAmAVwK0AdGABik8QGdaYAO59VPaJIB2fWmawixUgjyPEwAAwCC3oVtVHwAhb21IMlQAXyjo0gxqclwGHhIoiAp8JJpIWwZfdJpMNIRIWh5bIyxxAApmRAAmAF1iAE9EAGZmgEo4REQABm0ARmJhxvHtbuJEEdHiecn57ubI4tiMeKiczJSytITcyjQMzHyykKLiqFKoCqqa2o6ZgC8m3v6hxdahhfmRr8JottC01mdNjEjhBdlB9oRruQTrDMvQygAVAAWfGUak0pgELgU7iMPn8gWC3jC5kkXC4YExrAAbnxWHSwAQsOwAEZ2HjBVSSdiVIySLBVXRYvic9jscVgQXCqpi2w+NoBdjqEwAHVsjJZYFo2LARg4fFFrz4djA3KwRsNIpqpNq0BqCpFDkdxj66xokIg20SZ2wJ0gh2DyLOmQuUERuTukF8YAAVGArhC4tDTsUQwwiFnMpGbvcGFKiVJmCJ1FxaJJuJICGIVWBJKSjeb1Ox+CzxIbjTK5arFSLm951V5xB5q7XbABzFuq4TyCv8QQWvjeZjEV4BViknjMHA8LT8uC+raZnbB+FhuOF7JRqAxyB3koMIyd2pJ1MhLxtHrnjEl5BjmN75hGD7FuUpaYq2Lb7gAjuokhMmylSmLQWBnhmWxZrCuYHK+WRUI+eQMK+mQJpOGjuLUABWWB2F+xC-tA9TtD09TJv+PTEB+7AcYBAZxCgzQgNEQA">Matrix multiplication</a></li> - <li><a href="https://docs.vespa.ai/playground/#N4KABGBEBmkFxgNrgmUrWQPYAd5QGNIAaFDSPBdDTAF30gBUBTAOwGcsAnMAczeZcAhrQCWWVsTAATUQFs27cazBc2QuaNa8wQ1tLAEJBEerESAdGABioru1pgA7sxnNoW17SdYwtRdzsYAAUAG7MBLSBAJRgADqsAAYAgom6+mCJAEJpAK5K2rp+ATz8rIIiymDMAB44auxKEnCQZKgAvm3tpBjU5LgMzCRtEBT4fTSQrAzJwzSYQwiQ-hzcwaKIAMwAutHr0a3znRjdbROjA0tDPZOUaCOY00tZc-NQi1ArnFzriABMu3WAGpNgcHscOjcIOcoJdCK9yHcYaN6EsAOquEwqIysEwrUx+AAWzDkfiw-FoxJ4EiJmKwcjkNNkCg4yha4K6UPu82wd0g1weYyoD1GTygCMmH0gOLxwWSxCyxFEYKOnLOgrh0oloyRIs+DAASupNIVKa5mYoqolRGktGAsmTMgArRLs1UnLkw3mDbVQXVvKBiyAAYV9oylalYGmYwUVomITpVNAhEFOvQ1fKIXJ14z1ywYGMMekMxlMUf8mVS6QMiWDaRpzFEZp4FtZNOt9Z4iRdbuTavTPM1Ap5-reUwYYfeDBlIjlxGDSqTJ37qC9Q99Qu5Y6Dk-509LtDnC8Thz7y4g2xA7SAA">Tensor generation, dimension renaming and concatenation</a></li> - <li><a href="https://docs.vespa.ai/playground/#N4KABGBEBmkFxgNrgmUrWQPYAd5QFNIAaFDSPBdDTAO30gEMAXZgJwEsAjAV2YIAUAYxYEA5lk4EAzgEoSZGpCIJI-WtMkAeDrWYAOAHzCWwAL6y4wADqRpOSc2m2EARmJhbtAgHdn8d08YXUZaIQIXVzNIRQgzRTNSDGpyXAYiJKVKNFi6BgBHHgI2AE8TfgkpOQUaTBUodU02HT0jcvNLGzsHNicXMHdbaBCwiIDo3PiMRMUUzDTVDNyKfDnyelUAK0YhETYAEwB9LG9DgBtdJdqIZQZpHgBbAULispEKyQ4ZWTAAKjAWOxuHxBO9xJ9vrIAPRgARge5PB7FMSCF6lcrgqqyDyAzi8fgYypfOQeaACAAexBKsgErlk9LA8kmCRQAF0QGYgA">Jaccard similarity between mapped (sparse) tensors</a></li> - <li><a href="https://docs.vespa.ai/playground/#N4KABGBEBmkFxgNrgmUrWQPYAd5QGNIAaFDSPBdDTAF30gDkBTAVwCcBDAGzADtmtAO5Z2AazDMAHpwC2ObszAAKAMxSArFICMASgB0YAGIBLdgGdaYIUoAmzaCYFhaACyUDhoiU5ytacJBkqAC+wSGkGNTkuAzMJMEQFPjRNJB8DL7+CTSY8QiQtMx85qLKWbSIqgC6unCIAAz62sRgTS1tzdVBuWEYEcGpSbEFRJFplGiJdAwAKu581nYOTkpuSq4mtvaL3JwAnszsSyYA5q605mCcfLZgAEYmnOaB032h4xBDUCNQ8Z+YSbfJIZApCTbbYo5XJ-BhFEplCpVarECE7RAaWr1AA6iyQAFomgB2bQADgAnAAmYjafTk8naSmk4hNBoAFjZZOIhOapMpbNULP0DX59JRuIgjX0ADZSUTSRpubTpfyNMympTVNL6ULpRoiUS2UKNOS9aTxXjEPjaRoOdKjRryQ12ULtNLpdpFbTDdpVOr9KptALMbjum9wgDvthJpB-tNklRpiCGPc0VCAeR8lB4aV2Mo03wMVipUyRUShSL9YrWc6PULKR7tC0eW7pQ1yWHehHBvHfpAxvGgUns3MFkswPZHM51mApzwwHtDscbGcLlcbndHs9Xl3+pHezG47kE1MYVBQVAhHPuNDj1nCsVc-mtujMcRr4htMWJQTpfo+RospKjaGiUvK3KsoG5Llqo+gaPqDYQQG0pEqoRLVKGPQ0O8EADFEB5xLegIpMO6QpteRGZnCj5lB+X51FKDR6kSbadth3b4cefYDseQ5noUDCmBYViLkc1wELQJgAG6cJJWB8K0rDmE4pzXGAAAG7DMNwrDqeJkkyXJizQKwfASSY8n6Fh-QcagUZ9keEwkfxF6QJReQMOYrCyOUfB+FYABU1gFq0FS6GAADUDwFtZoS2V8BEFI55B8TCZEFKJ7DaO5ST3lpOnKF5PkVGAQXgi+xShX5-jhVFqYVXwuixbh8Wnmk3HuSewIjgUACiACOrDSTwxS0Nw+yKcpfCqTO6lSEIAD6ChKQt9x6SZfBGTu7F7j2XGHp1qXHq5PBFOwfCydJzALZl2UZh5BT5awyjzUtOnmKtvn+a05WQgp0UNVV-m6E14a7Zx7UxjxTmJvx9AFKYF28Jl+nDUZk0qWpymnLIWBbKjhkWcZplbc1YA4eT+77YR91daRrk5bCBRFcot2ldY16tAWtUPBRYMfHtkM04Ozlpa5mWUozsaeWcuNbIV3mswcRzaOzV5ODwXMNTz9zXqDu4CxDMRQ4dovHvDUCI-OXB8BI5gEKIzDbTZ4N2YlsK00daQM-duWeYrEv6ztcUQNUIAhEAA">Neural network</a></li> -</ul> - - -<h2 id="values-that-depend-on-the-current-time">Values that depend on the current time</h2> - -<p> -In an ecommerce application you may have promotions that sets a different product price in given -time intervals. Since the price is used for ranking, the correct price must be computed in ranking. -Can tensors be used to specify prices in arbitrary time intervals in documents and pick the right price -during ranking? -</p> -<p>To do this, add three tensors to the document type as follows:</p> -<pre> -field startTime type tensor(id{}} { - indexing: attribute -} -field endTime type tensor(id{}} { - indexing: attribute -} -field price type tensor(id{}} { - indexing: attribute -} -</pre> -<p> -Here the id is an arbitrary label for the promotion which must be unique within the document, and -startTime and endTime are epoch timestamps. -</p> -<p>Now documents can include promotions as follows -(<a href="../reference/schemas/document-json-format.html">document JSON syntax</a>): -</p> -<pre> -"startTime": { "cells": { "promo1": 40, "promo2": 60, "promo3": 80 } -"endTime": { "cells": { "promo1": 50, "promo2": 70, "promo3": 90 } -"price": { "cells": { "promo1": 16, "promo2": 18, "promo3": 10 } -</pre> -<p>And we can retrieve the currently valid price by the expression</p> -<pre> -reduce((attribute(startTime) < now) * (attribute(endTime) > now) * attribute(price), max) -</pre> -<p>This will return 0 if there is no matching interval, so a full expression will probably -wrap this in a function and check if it returns 0 (using an if expression) and return the -default price of that product otherwise.</p> - -<p>To see why this retrieves the right price, notice that <code>(attribute(startTime) < now)</code> -<a href="../reference/ranking/ranking-expressions.html#non-primitive-functions">is a shorthand for</a></p> - -<pre>join(attribute(startTime), now, f(x,y)(x < y))</pre> - -<p>That is joining all the cells of the <code>startTime</code> tensor by the zero-dimensional <code>now</code> -tensor (i.e a number), and setting the cell value in the joined tensor to 1 if now is larger than the cell -timestamp and 0 otherwise. -When this tensor is joined by multiplication with one that has 1's only where now is smaller, -the result is a tensor with 1's for promotion ids whose interval is currently valid and 0 otherwise. -Then we can just join by multiplication with the price tensor to get the final tensor (on which we just pick the -max value to retrieve the non-zero value.</p> - -<p><a href="https://docs.vespa.ai/playground/#N4KABGBEBmkFxgNrgmUrWQPYAd5QFNIAaFDSPBdDTAO30gGcAXAQwCdmAVASwFsipGpiIJIzArUZZ2ACh4ATYAF8AlHGA52WPlgCMCACwAGYmC06sAJgQA2U+e26AzAgAcx5ZDKplP5UKo1OS4DII+EBT4wTSQ9GKSCrwCJBEiDBJSMvJKahoWugZgAKwOBdYIAOxlTliuYACcnt7CfhgBPjGRoQmpwlFUaZHxUFo8AMbhwpGiUJnScooq6pq1RXq2ZuU2YHpuW7X1es1pbb6BEF1QPYR9sZRoQ1AjcVgA7nf9s5C2xS00ZwgHQwV2wD0gU3u0SecQYn1i3xYHG4-AIYAAPGBaO9-u1-BdHv0bhD4ZEHldhnCCeRvolkmiAHxYnGnfGdNJgsKk0bQ6bPKkw76yJGcekY5lvVRgABUYFkdNRYCZ2MluN8bJBHOJkPI5JhL25MwYwrYosVmJVUtl8toSUVyveVscEyIrPaBNB2u5A0J01hYkNtzE7AICgArpNZCbkWKLY6ZXKFQIlRKnWNJmY+KwAB6qNVA-woAC6IGUQA">Play around with this example in the playground</a> - - -<h2 id="adding-scalars-to-a-tensor">Adding scalars to a tensor</h2> - -<p>A common situation is that you have dense embedding vectors to which you want to add some scalar attributes -(or function return values) as input to a machine-learned model. This can be done by the following expression -(assuming the dense vector dimension is named "x":</p> - -<pre> -concat(concat(query(embedding),attribute(embedding),x), tensor(x[2]):[bm25(title),attribute(popularity)], x) -</pre> - -<p>This creates a tensor from a set of scalar expressions, and concatenates it to the query and document embedding vectors.</p> - -<p><a href="https://docs.vespa.ai/playground/#N4KABGBEBmkFxgNrgmUrWQPYAd5QFNIAaFDSPBdDTAO30gEMAXZgJwEsAjAV2YIAUBALZcCAE3EdaAcwCUJMjUhEEkfrQDOWNgIAeiAEwBdOXEQBGYichKIAXyX3SGauVwMiL5ZTR26DACOPARsAJ5CohJSsgreNIQMGtq6BiZmiADMAHQADMRgACx5xrYJjhjOSm6YHmpe-hT4NeT0alzChgCsAswczAA2BHH+ECoMhsWZZTQVqFWujXWJ8Zi+LQFqLOzcfII4uDwDjJzMYSMJY6pQXTOVTqsb2L7jq2Pro1BtUIqXiWoAYywtABLAEQJBYOCoQiIjEkmk8mI204vH4kXhMSRejkBWSOn0RlM5g63V6-SGuJRu3RBxwRxO-XOxgKOLu8ycKGMIHsQA">Play around with this example in the playground</a> - - -<h2 id="dot-product-between-query-and-document-vectors">Dot Product between query and document vectors</h2> - -<p>Assume we have a set of documents where each document contains a vector of size 4. -We want to calculate the dot product between the document vectors and a vector passed down with the query -and rank the results according to the dot product score.</p> - -<p>The following schema file defines an attribute tensor field -with a tensor type that has one indexed dimension <code>x</code> of size 4. -In addition, we define a rank profile with the input and the dot product calculation:</p> - -<pre> -schema example { - document example { - field document_vector type tensor<float>(x[4]) { - indexing: attribute | summary - } - } - rank-profile dot_product { - inputs { - query(query_vector) tensor<float>(x[4]) - } - first-phase { - expression: sum(query(query_vector)*attribute(document_vector)) - } - } -} -</pre> - -<p>Example <a href="../reference/schemas/document-json-format.html#tensor">JSON</a> document with the - vector [1.0, 2.0, 3.0, 5.0], using indexed tensors short form:</p> - -<pre> -[ - { - "put": "id:example:example::0", - "fields": { - "document_vector" : [1.0, 2.0, 3.0, 5.0] - } - } -] -</pre> - -<p>Example query set in a searcher with the vector [1.0, 2.0, 3.0, 5.0]:</p> - -<pre> -public Result search(Query query, Execution execution) { - query.getRanking().getFeatures().put("query(query_vector)", - Tensor.Builder.of(TensorType.fromSpec("tensor<float>(x[4])")). - cell().label("x", 0).value(1.0). - cell().label("x", 1).value(2.0). - cell().label("x", 2).value(3.0). - cell().label("x", 3).value(5.0).build()); - return execution.search(query); -} -</pre> - -<p><a href="https://docs.vespa.ai/playground/#N4KABGBEBmkFxgNrgmUrWQPYAd5QFNIAaFDSPBdDTAO30gEcBXAgJwE8AKF9jgfQBuBAMYAXLGwCUJMjUhEEkMQVoBnSVwAeiACwBdKXEQBGYgCZiAZmIBWfZDkQAvnOekM1crgZEP8yjQnOgYAQzExNgBLACNmFS4AEywRZgBbVTEhUQlpWRpMRSgVdU0dAyNTC2s7B2DXDHc5L0wfJT9ginwW8nolfILCBjV0nlZOMb5s8UkpMAAqMHDI2PiCJJT0zOncqRl6txR9EGcgA">Play around with this example in the playground</a> - -<p>Note that this example calculates the dot product for every document retrieved by the query. Consider - using <a href="../querying/approximate-nn-hnsw">approximate nearest neighbor</a> search with - <code>distance-metric</code> <a href="../reference/schemas/schemas.html#distance-metric">dotproduct</a>. -</p> - - -<h2 id="logistic-regression-models-with-cross-features">Logistic regression models with cross features</h2> - -<p>One simple way to use machine-learning is to generate cross features from a set of base features -and then do a logistic regression on these. How can this be expressed as Vespa tensors?</p> - -<p>Assume we have three base features:</p> - -<pre> -query(interests): tensor(interest{}) - A sparse, weighted set of the interests of a user. -query(location): tensor(location{}) - A sparse set of the location(s) of the user. -attribute(topics): tensor(topic{}) - A sparse, weighted set of the topics of a given document. -</pre> - -<p>From these we have generated all 3d combinations of these features and trained a logistic regression model, -leading to a weight for each possible combination:</p> - -<pre> -tensor(interest{}, location{}, topic{}) -</pre> - -<p>This weight tensor can be added as a -<a href="../reference/schemas/schemas.html#constant">constant tensor</a> -to the application package, say <code>constant(model)</code>. With that we can compute the model -in a rank profile by the expression</p> - -<pre> -sum(query(interests) * query(location) * attribute(topics) * constant(model)) -</pre> - -<p>Where the first three factors generates the 3d cross feature tensor and the last combines -them with the learned weights.</p> - -<p><a href="https://docs.vespa.ai/playground/#N4KABGBEBmkFxgNrgmUrWQPYAd5QFNIAaFDSPBdDTAO30gEcBXAgJwE8AKAS1oBd2BAM79hAShJkakIgkiDawrG14Cho4AF9xcYAGMAhm2FwADADoAHMTABbZsJ77zFgKxbI0iFulbSGNTkuAxEATKUaN50DCzs3AA2WEb8PFi0kuE0hAyKyqpJKWm02rrAtAQA7gD6HCoA1nAAjJ7Rvhj+0kGYIfJh0RT43eT08ob8-Gw8AEbMglz8uM4SUtkQsrkESioLS-qlek0ATADMzRZmrdntqJ2BA705WZiRwzHy+umihgJcdlgAEwICUy0XWcigeR2fEEbBE-G0xEK42KiMWOGcBzQAB1aBBgDCNPw4EYTEjkij0nAKjU6mx6sR0c5mqctK4msRcfjCXDRCTjMJyUUqTTag1GXs4CcACweVxHTl4tA8+H8snI1JU4Q-arQNg-fQ8YSfCUYlzHE5sywnRXc9S84mkwUa4pwbW0XX62iG41YU3MmVyyzS23K+2qhxOfRCym0alVMX0-3m1muNyhgnhvmR5wxzVx0V0hlMlyBq0WABsGZV2ccuZdWp1eoNRpNJZZltcVa5YdhEbr0YbcfdnpbvuTUtl5YA7NWs8TplgsPVnRT8-HaeL2xbyzYe5m+3zF8vV8KCwmixOy64AJxzw8LpcrvOukfN72tv3b1NNC73olwMez5Dm6TZej6baStev5NLiVw0DcPjPG82CRBszzrK8YJQKMUCrGsGzyMIzB2FwcScGoD4SGAABUYDkYka7FOItFgOMkwzHMBC7Ga1F0Z8Sj8D8-B-ICwLiJIbR+CgAC6IBaEAA">Play around with this example in the playground</a> - - -<h2 id="matrix-product-between-1d-vector-and-2d-matrix">Matrix Product between 1d vector and 2d matrix</h2> - -<p>Assume we have a 3x2 matrix represented in an attribute tensor field <code>document_matrix</code> -with a tensor type <code>tensor<float>(x[3],y[2])</code> with content:</p> - -<pre> -{ {x:0,y:0}:1.0, {x:1,y:0}:3.0, {x:2,y:0}:5.0, {x:0,y:1}:7.0, {x:1,y:1}:11.0, {x:2,y:1}:13.0 } -</pre> - -<p>Also assume we have 1x3 vector passed down with the query as a tensor -with type <code>tensor<float>(x[3])</code> with content:</p> - -<pre> -{ {x:0}:1.0, {x:1}:3.0, {x:2}:5.0 } -</pre> - -<p>that is set as <code>query(query_vector)</code> in a searcher -as specified in <a href="ranking-expressions-features.html#using-query-variables">query feature</a>.</p> - -<p>To calculate the matrix product between the 1x3 vector and 3x2 matrix (to get a 1x2 vector) -use the following ranking expression:</p> - -<pre> -sum(query(query_vector) * attribute(document_matrix),x) -</pre> - -<p>This is a sparse tensor product over the shared dimension <code>x</code>, -followed by a sum over the same dimension.</p> - -<p> - <a href="https://docs.vespa.ai/playground/#N4KABGBEBmkFxgNrgmUrWQPYAd5QFNIAaFDSPBdDTAO30gEMAXZgJwEsAjAV2YIAUAEywBjHgFsCtZgH0JLTgA8AlCTI1IRBJH60AzljYAeaABssLAHwCliAMwBdYgE9EAJkcq4iRAEZiAHZnB2I-PxCAVjCnR0gNCABfDUTSDGpyXAYiNM1KNAS6BgBHHgI2FwFS8pdZADcCUWYjNVyaQgY9QxNzS2YbOydvf2J7Yki4wuSMVI0MzCydHMKKfHnyeh11dogtBn1JKrKKo5r6xua2FTAAKjAWdm4+QRFxKRl5RQ5VYlV49umqEBjhAiSAA">Play around with this example in the playground</a> -</p> - - -<h2 id="using-a-tensor-as-a-lookup-structure">Using a tensor as a lookup structure</h2> - -<p> - Tensors with mapped dimensions look similar to maps, but are more general. - What if all needed is a simple map lookup? - See <a href="../performance/feature-tuning.html#mapped-lookups">tensor performance</a> for more details. -</p> -<p> - Assume a tensor attribute <code>my_map</code> and this is the value for a specific document: -</p> -<pre> -tensor<float>(x{},y[3]):{a:[1,2,3],b:[4,5,6],c:[7,8,9]} -</pre> -<p> - To create a query to select which of the 3 named vectors (a,b,c) to use for some other calculation, - wrap the wanted label to look up inside a tensor. - Assume a query tensor <code>my_key</code> with type/value: -</p> -<pre> -tensor<float>(x{}):{b:1.0} -</pre> -<p>Do the lookup, returning a tensor of type <code>tensor<float>(y[3])</code>:</p> -<pre> -sum(query(my_key)*attribute(my_map),x) -</pre> -<!-- same as reduce(query(my_key)*attribute(my_map),sum,x) --> -<p> - If the key does not match anything, the result will be empty: <code>tensor<float>(y[3]):[0,0,0]</code>. - For something else, add a check up-front to check if the lookup will be successful - and run a fallback expression if it is not, like: -</p> -<pre> -if(reduce(query(my_key)*attribute(my_map),count) == 3, - reduce(query(my_key)*attribute(my_map),sum,x), - tensor<float>(y[3]):[0.5,0.5,0.5]) -</pre> -{% include note.html content='The above can be considered the same as creating a -<a href="../reference/ranking/ranking-expressions.html#slice">slice</a>, like <code>(y*x){x:b}</code>. -The above syntax allows an optimized execution, find an example in the -<a href="https://docs.vespa.ai/playground/#N4KABGBEBmkFxgNrgmUrWQPYAd5QFNIAaFDSPBdDTAO30gFssATAgGwGcSybIiEkAC4FanLACcAFIwD6ASxbAAvsQAeiAMwBdAJRxgjAIxxEAFgB0ABmJgArNdsA2a9tuMATKYDsjsAA4-AE5XZUheCGVeVV5qclwGIlIaKEo0CLoGZjZ2BRYeFIh+BhExSRk8lX1DLyNrMIyojBiMOMwEwSSMinw28npBRgBDHBwCFll2LCwAawBXPGTC4sFOOcYZVg5OACpsjjziOUVdcJSm1BbUPqgOwgK+NJuigahOdnkAYy7C+8FNnK7fa5E6GPJwTwNc7RFDaEDKIA"> - Tensor Playground</a>.' %} - - -<h2 id="slicing-with-lambda">Slicing with lambda</h2> - -<p> - A common use case is to use a tensor lambda function to slice out the first <code>k</code> dimensions of a vector representation of <code>m</code> - dimensions where <code>m</code> is larger than <code>k</code>. - Slicing with lambda functions is great for representing vectors from <a href="https://arxiv.org/abs/2205.13147">Matryoshka Representation Learning</a>. -</p> -<blockquote>Matryoshka Representation Learning (MRL) which encodes information at different granularities and allows a single embedding to adapt to the computational constraints of downstream tasks.</blockquote> -<p>The following slices the first 256 dimensions of a tensor <code>t</code>:</p> -<pre> - tensor<float>(x[256])(t{x:(x)}) -</pre> -<p>Importantly, this does only reference into the original tensor, avoiding copying the tensor to a smaller tensor.</p> -<p> -The following is a complete example where we have stored an original vector representation with 3072 dimensions, And - we slice the first 256 dimensions of the original representation to perform a dot product in the first-phase expression, - followed by a full computation over all dimensions in the second-phase expression. See <a href="phased-ranking.html">phased ranking</a> - for context on using Vespa phased computations and <a href="https://blog.vespa.ai/tailoring-frozen-embeddings-with-vespa/">customizing reusable frozen embeddings with Vespa</a>. -</p> -<pre>{% raw %} -schema example { - document example { - field document_vector type tensor<float>(x[3072]) { - indexing: attribute | summary - } - } - rank-profile small-256-first-phase { - inputs { - query(query_vector) tensor<float>(x[3072]) - } - function slice_first_dims(t) { - expression: l2_normalize(tensor<float>(x[256])(t{x:(x)}), x) - } - first-phase { - expression: sum( slice_first_dims(query(query_vector)) * slice_first_dims(attribute(document_vector)) ) - } - second-phase { - expression: sum( query(query_vector) * attribute(document_vector) ) - } - } -} -{% endraw %}</pre> -<p> - See also a runnable example in this - <a href="https://docs.vespa.ai/playground/#N4KABGBEBmkFxgNrgmUrWQPYAd5QFNIAaFDSPBdDTAO30gFsBPAfQAsBLAc3dYBNOjVgDcCAYwAuWAE4kyNSEQSRJBWgGdZAHmgAbLAENJAPgAUAD0QBGAEwBdAJRwbxW8QDMxACzEArMQAbMQA7MQAHMQAnMTWAAyx1rEOkAoQAL4K6aQY1OS4DEQ5ipRoaXQM0JwyGpKsgQJCGqxY0KwsHDx8gsJiUrLyNJjKUGqaspaIgU5mHVy8jb0S0jLAFnCWjumOqUOZGNkKeZgFKkXlFPjH5PQqtLKMhnqcAF7nQxBKDHq2rPcyj2ebzMVRqdQaPWarXabHm3SEomWsmIYAsO3K+1QmPsIHSQA"> - tensor playground example</a>. -</p> diff --git a/en/ranking/tensor-examples.mdx b/en/ranking/tensor-examples.mdx new file mode 100644 index 0000000000..aeb4551eab --- /dev/null +++ b/en/ranking/tensor-examples.mdx @@ -0,0 +1,275 @@ +--- +title: "Tensor Computation Examples" +sidebarTitle: "Tensor Examples" +--- + +Tensors can be used to express machine-learned models such as neural nets, but they can be used for much more than that. The tensor model in Vespa is powerful, since it supports sparse dimensions, dimension names and lambda computations. Whatever you want to compute, it is probably possible to express it succinctly as a tensor expression - the problem is learning how. This page collects some real-world examples of tensor usage to provide some inspiration. + +## Tensor playground + +The tensor playground is a tool to get familiar with and explore tensor algebra. It can be found at [docs.vespa.ai/playground](https://docs.vespa.ai/playground/). Below are some examples of common tensor compute operations using [tensor functions](../reference/ranking/ranking-expressions#tensor-functions). Feel free to play around with them to explore further: + +<CardGroup> + <Card title="Dense tensor dot product" icon="grid-2" href="https://docs.vespa.ai/playground/#N4KABGBEBmkFxgNrgmUrWQPYAd5QGNIAaFDSPBdDTAF30gBEBTAOwGdmxa32sAnMABMstMDn5YhAVwK0AdGABiAS37sxAdy5Dm0Fay4BDbrwHswAAwCClsEdZCrAITtGLujlx4dzcSGSoAL6BQaQY1OS4DMwkgRAU+JE0kKwM1nE0mLEIkD58-AAUAB6IAMwAugCUcIgADMRgdfIAjI11FQFZIRhhgckJ0bmx4SmUaPGYabnOmVlQOVD5AiXl1bXNbc0ATMQdXTQ9waMQA1BDhHPk42cJ9LkAKgAW3ppYpr7qYE9GAG7eLzA7CMAFsdCowRwVFhWGBWKDmOxGnx7GAQdIADa0FQ4DEqAhGbEwsAqCzMDHMSG0AC0mlJzH8kyOED6EUm2HGkBG7JukwS0ygVxSi0g1jAACowLMmaEThMshyGEQ5QlefMlgxnjpROJJDI5CSLLRAexpCCLFh-oJjVwCFgQSDiUIIbxoaxGd1Zf12RcuULVUk+VABZB-dkGKaQYVrOLnFUDr1QigKiAgkA" horizontal /> + <Card title="Sparse tensor dot product" icon="grid" href="https://docs.vespa.ai/playground/#N4KABGBEBmkFxgNrgmUrWQPYAd5QGNIAaFDSPBdDTAF30gGUcBDAJwGcBTMWrgOw5Y2YACZZaYHGyyiArgVoA6MADEAlp1rEwAdx6iu0dfx61dWMB1aczAoZzAADAIJOwLfqOcAhJyoAdfgA5CXUCMwALHgiAG1iOXkiWSWSANwEAckkAIy4BMSMTLm9xLkT+CTB0nhYwNJZYuS44SDJUAF92jtIManJcBi4SdogKfH6aSH4GFxGaTGGESD5BYQAKAA9gDoBKOGAARjgABiVDjraFrowe9smxweXh3qnKNFHMGeWfeYWoJZQVYOLY7fbAE6nc7EY5nABMl0+N06rwgDygT0If3I73RY3oywAkvwkjwALZyWK0dQ4WLhFLqLD8HRM2IAT1JYDJKQIkRMAHMwHEEh42DFGgRKSkSq0kd1UR8Fth3pAXp9xlRPmNvlBsVNAZAXGAAFRgX5y24K9HKhhEBVjXFaoEMAAq0TEVWksgUknUiVo7opVJpdIIDKZYGgWHiWH03hyHLqHDkZLAWAyIgDMSwZLJEdE6jJ9kZ-Fl13l93VmNVeodEyd0wYtcWDGTZPWLmNPh0m12VxoyIgdz6VZVdvVjv+zuWbr9YDnXAAjnJ1A1YgJJLQsGWBxWR0rq2qlZP-o3ls2xgaxfIIusAFZYEwdnTdyNbYhs3ZbY2f3Y6Nt9hanQoAAuiAHRAA" horizontal /> + <Card title="Vector-matrix product" icon="chart-bar" href="https://docs.vespa.ai/playground/#N4KABGBEBmkFxgNrgmUrWQPYAd5QGNIAaFDSPBdDTAF30gDUBTA2rAJwFoBbAQ1ocAlgA8wODlgAmAVzYA6MADEhHAM60wAd2ZgpzaEIB2uvmABurdhzAADRrbB8jUp2H6DRdgLK24kMlQAX0Cg0gxqclwGZhJAiAp8SJpIIwZGOJpMWIRIWmYjNU4AChFEAGYAXQBKOEQABnkAVmIwRpa25sqArJCMMMDkhOjc2PCUyjR4zDTc70ysqByofMKSggrK4jKq2sQG+XrWxoBGY-kAJi2kE8PW27OwW6vWxAu7sHfH96vu6b7guMIEMoCNCAtyJMQQl6LkAIIWKycXgCYRiCTSOSaIRqMC0AAWuh4MgANrQhDgSUICAIhFgjGAsNA8YTEWxOE4XO5UV5nK41DIeDxjABzRmWGwE3QELBC+l6IQ8ApqOlGfz-UJAqZZbCTSBjaaJKjTBKzKAQlLLSACnjFRhgABUYG8rRE1R6NABEAGEUNYMgRC1CShJpWDAAclh8iyBCzdJx9DYme5SeTKdTafKpFhmGojAByTQefIcRQASWxuKlCqVhVVeKwYBt4uYkvxseLrbU8g9-U1gz9eoNOpDiygZsgFvIVptxW8jrAjFd7o1-S1IN1DEDhtHizyDAAKqy+AAjLCWMA4sDMACOMiE5j4JIKmnY6t6-d9Ov9w4mSVDqQMFO2QMBwzCyAQzDFAAVlgxh2q0LpgNApTEAAntUpQLhh1StDay69sEoQoJUIBBEAA" horizontal /> + <Card title="Matrix multiplication" icon="table-layout" href="https://docs.vespa.ai/playground/#N4KABGBEBmkFxgNrgmUrWQPYAd5QGNIAaFDSPBdDTAF30gFEAPAQwFscAbAUzC2hhWYdq1oAnAJbMAtKInSwOcVgAmAVwK0AdGABik8QGdaYAO59VPaJIB2fWmawixUgjyPEwAAwCC3oVtVHwAhb21IMlQAXyjo0gxqclwGHhIoiAp8JJpIWwZfdJpMNIRIWh5bIyxxAApmRAAmAF1iAE9EAGZmgEo4REQABm0ARmJhxvHtbuJEEdHiecn57ubI4tiMeKiczJSytITcyjQMzHyykKLiqFKoCqqa2o6ZgC8m3v6hxdahhfmRr8JottC01mdNjEjhBdlB9oRruQTrDMvQygAVAAWfGUak0pgELgU7iMPn8gWC3jC5kkXC4YExrAAbnxWHSwAQsOwAEZ2HjBVSSdiVIySLBVXRYvic9jscVgQXCqpi2w+NoBdjqEwAHVsjJZYFo2LARg4fFFrz4djA3KwRsNIpqpNq0BqCpFDkdxj66xokIg20SZ2wJ0gh2DyLOmQuUERuTukF8YAAVGArhC4tDTsUQwwiFnMpGbvcGFKiVJmCJ1FxaJJuJICGIVWBJKSjeb1Ox+CzxIbjTK5arFSLm951V5xB5q7XbABzFuq4TyCv8QQWvjeZjEV4BViknjMHA8LT8uC+raZnbB+FhuOF7JRqAxyB3koMIyd2pJ1MhLxtHrnjEl5BjmN75hGD7FuUpaYq2Lb7gAjuokhMmylSmLQWBnhmWxZrCuYHK+WRUI+eQMK+mQJpOGjuLUABWWB2F+xC-tA9TtD09TJv+PTEB+7AcYBAZxCgzQgNEQA" horizontal /> + <Card title="Tensor generation, dimension renaming and concatenation" icon="shuffle" href="https://docs.vespa.ai/playground/#N4KABGBEBmkFxgNrgmUrWQPYAd5QGNIAaFDSPBdDTAF30gBUBTAOwGcsAnMAczeZcAhrQCWWVsTAATUQFs27cazBc2QuaNa8wQ1tLAEJBEerESAdGABioru1pgA7sxnNoW17SdYwtRdzsYAAUAG7MBLSBAJRgADqsAAYAgom6+mCJAEJpAK5K2rp+ATz8rIIiymDMAB44auxKEnCQZKgAvm3tpBjU5LgMzCRtEBT4fTSQrAzJwzSYQwiQ-hzcwaKIAMwAutHr0a3znRjdbROjA0tDPZOUaCOY00tZc-NQi1ArnFzriABMu3WAGpNgcHscOjcIOcoJdCK9yHcYaN6EsAOquEwqIysEwrUx+AAWzDkfiw-FoxJ4EiJmKwcjkNNkCg4yha4K6UPu82wd0g1weYyoD1GTygCMmH0gOLxwWSxCyxFEYKOnLOgrh0oloyRIs+DAASupNIVKa5mYoqolRGktGAsmTMgArRLs1UnLkw3mDbVQXVvKBiyAAYV9oylalYGmYwUVomITpVNAhEFOvQ1fKIXJ14z1ywYGMMekMxlMUf8mVS6QMiWDaRpzFEZp4FtZNOt9Z4iRdbuTavTPM1Ap5-reUwYYfeDBlIjlxGDSqTJ37qC9Q99Qu5Y6Dk-509LtDnC8Thz7y4g2xA7SAA" horizontal /> + <Card title="Jaccard similarity between mapped (sparse) tensors" icon="code-branch" href="https://docs.vespa.ai/playground/#N4KABGBEBmkFxgNrgmUrWQPYAd5QFNIAaFDSPBdDTAO30gEMAXZgJwEsAjAV2YIAUAYxYEA5lk4EAzgEoSZGpCIJI-WtMkAeDrWYAOAHzCWwAL6y4wADqRpOSc2m2EARmJhbtAgHdn8d08YXUZaIQIXVzNIRQgzRTNSDGpyXAYiJKVKNFi6BgBHHgI2AE8TfgkpOQUaTBUodU02HT0jcvNLGzsHNicXMHdbaBCwiIDo3PiMRMUUzDTVDNyKfDnyelUAK0YhETYAEwB9LG9DgBtdJdqIZQZpHgBbAULispEKyQ4ZWTAAKjAWOxuHxBO9xJ9vrIAPRgARge5PB7FMSCF6lcrgqqyDyAzi8fgYypfOQeaACAAexBKsgErlk9LA8kmCRQAF0QGYgA" horizontal /> + <Card title="Neural network" icon="microchip" href="https://docs.vespa.ai/playground/#N4KABGBEBmkFxgNrgmUrWQPYAd5QGNIAaFDSPBdDTAF30gDkBTAVwCcBDAGzADtmtAO5Z2AazDMAHpwC2ObszAAKAMxSArFICMASgB0YAGIBLdgGdaYIUoAmzaCYFhaACyUDhoiU5ytacJBkqAC+wSGkGNTkuAzMJMEQFPjRNJB8DL7+CTSY8QiQtMx85qLKWbSIqgC6unCIAAz62sRgTS1tzdVBuWEYEcGpSbEFRJFplGiJdAwAKu581nYOTkpuSq4mtvaL3JwAnszsSyYA5q605mCcfLZgAEYmnOaB032h4xBDUCNQ8Z+YSbfJIZApCTbbYo5XJ-BhFEplCpVarECE7RAaWr1AA6iyQAFomgB2bQADgAnAAmYjafTk8naSmk4hNBoAFjZZOIhOapMpbNULP0DX59JRuIgjX0ADZSUTSRpubTpfyNMympTVNL6ULpRoiUS2UKNOS9aTxXjEPjaRoOdKjRryQ12ULtNLpdpFbTDdpVOr9KptALMbjum9wgDvthJpB-tNklRpiCGPc0VCAeR8lB4aV2Mo03wMVipUyRUShSL9YrWc6PULKR7tC0eW7pQ1yWHehHBvHfpAxvGgUns3MFkswPZHM51mApzwwHtDscbGcLlcbndHs9Xl3+pHezG47kE1MYVBQVAhHPuNDj1nCsVc-mtujMcRr4htMWJQTpfo+RospKjaGiUvK3KsoG5Llqo+gaPqDYQQG0pEqoRLVKGPQ0O8EADFEB5xLegIpMO6QpteRGZnCj5lB+X51FKDR6kSbadth3b4cefYDseQ5noUDCmBYViLkc1wELQJgAG6cJJWB8K0rDmE4pzXGAAAG7DMNwrDqeJkkyXJizQKwfASSY8n6Fh-QcagUZ9keEwkfxF6QJReQMOYrCyOUfB+FYABU1gFq0FS6GAADUDwFtZoS2V8BEFI55B8TCZEFKJ7DaO5ST3lpOnKF5PkVGAQXgi+xShX5-jhVFqYVXwuixbh8Wnmk3HuSewIjgUACiACOrDSTwxS0Nw+yKcpfCqTO6lSEIAD6ChKQt9x6SZfBGTu7F7j2XGHp1qXHq5PBFOwfCydJzALZl2UZh5BT5awyjzUtOnmKtvn+a05WQgp0UNVV-m6E14a7Zx7UxjxTmJvx9AFKYF28Jl+nDUZk0qWpymnLIWBbKjhkWcZplbc1YA4eT+77YR91daRrk5bCBRFcot2ldY16tAWtUPBRYMfHtkM04Ozlpa5mWUozsaeWcuNbIV3mswcRzaOzV5ODwXMNTz9zXqDu4CxDMRQ4dovHvDUCI-OXB8BI5gEKIzDbTZ4N2YlsK00daQM-duWeYrEv6ztcUQNUIAhEAA" horizontal /> +</CardGroup> + +## Values that depend on the current time + +In an ecommerce application you may have promotions that sets a different product price in given time intervals. Since the price is used for ranking, the correct price must be computed in ranking. Can tensors be used to specify prices in arbitrary time intervals in documents and pick the right price during ranking? + +To do this, add three tensors to the document type as follows: + +```js +field startTime type tensor(id{}} { + indexing: attribute +} +field endTime type tensor(id{}} { + indexing: attribute +} +field price type tensor(id{}} { + indexing: attribute +} +``` + +Here the id is an arbitrary label for the promotion which must be unique within the document, and startTime and endTime are epoch timestamps. + +Now documents can include promotions as follows ([document JSON syntax](/en/reference/schemas/document-json-format)): + +```json +"startTime": { "cells": { "promo1": 40, "promo2": 60, "promo3": 80 } +"endTime": { "cells": { "promo1": 50, "promo2": 70, "promo3": 90 } +"price": { "cells": { "promo1": 16, "promo2": 18, "promo3": 10 } +``` + +And we can retrieve the currently valid price by the expression + +```js +reduce((attribute(startTime) < now) * (attribute(endTime) > now) * attribute(price), max) +``` + +This will return 0 if there is no matching interval, so a full expression will probably wrap this in a function and check if it returns 0 (using an if expression) and return the default price of that product otherwise. + +To see why this retrieves the right price, notice that `(attribute(startTime) < now)`[is a shorthand for](../reference/ranking/ranking-expressions#non-primitive-functions) + +```js +join(attribute(startTime), now, f(x,y)(x < y)) +``` + +That is joining all the cells of the `startTime` tensor by the zero-dimensional `now` tensor (i.e a number), and setting the cell value in the joined tensor to 1 if now is larger than the cell timestamp and 0 otherwise. When this tensor is joined by multiplication with one that has 1's only where now is smaller, the result is a tensor with 1's for promotion ids whose interval is currently valid and 0 otherwise. Then we can just join by multiplication with the price tensor to get the final tensor (on which we just pick the max value to retrieve the non-zero value. + +<CardGroup cols={1}> + <Card title="Play around with this example in the playground" icon="play-circle" href="https://docs.vespa.ai/playground/#N4KABGBEBmkFxgNrgmUrWQPYAd5QFNIAaFDSPBdDTAO30gGcAXAQwCdmAVASwFsipGpiIJIzArUZZ2ACh4ATYAF8AlHGA52WPlgCMCACwAGYmC06sAJgQA2U+e26AzAgAcx5ZDKplP5UKo1OS4DII+EBT4wTSQ9GKSCrwCJBEiDBJSMvJKahoWugZgAKwOBdYIAOxlTliuYACcnt7CfhgBPjGRoQmpwlFUaZHxUFo8AMbhwpGiUJnScooq6pq1RXq2ZuU2YHpuW7X1es1pbb6BEF1QPYR9sZRoQ1AjcVgA7nf9s5C2xS00ZwgHQwV2wD0gU3u0SecQYn1i3xYHG4-AIYAAPGBaO9-u1-BdHv0bhD4ZEHldhnCCeRvolkmiAHxYnGnfGdNJgsKk0bQ6bPKkw76yJGcekY5lvVRgABUYFkdNRYCZ2MluN8bJBHOJkPI5JhL25MwYwrYosVmJVUtl8toSUVyveVscEyIrPaBNB2u5A0J01hYkNtzE7AICgArpNZCbkWKLY6ZXKFQIlRKnWNJmY+KwAB6qNVA-woAC6IGUQA" horizontal /> +</CardGroup> + +## Adding scalars to a tensor + +A common situation is that you have dense embedding vectors to which you want to add some scalar attributes (or function return values) as input to a machine-learned model. This can be done by the following expression (assuming the dense vector dimension is named "x": + +```js +concat(concat(query(embedding),attribute(embedding),x), tensor(x[2]):[bm25(title),attribute(popularity)], x) +``` + +This creates a tensor from a set of scalar expressions, and concatenates it to the query and document embedding vectors. + +<CardGroup cols={1}> + <Card title="Play around with this example in the playground" icon="play-circle" href="https://docs.vespa.ai/playground/#N4KABGBEBmkFxgNrgmUrWQPYAd5QFNIAaFDSPBdDTAO30gEMAXZgJwEsAjAV2YIAUBALZcCAE3EdaAcwCUJMjUhEEkfrQDOWNgIAeiAEwBdOXEQBGYichKIAXyX3SGauVwMiL5ZTR26DACOPARsAJ5CohJSsgreNIQMGtq6BiZmiADMAHQADMRgACx5xrYJjhjOSm6YHmpe-hT4NeT0alzChgCsAswczAA2BHH+ECoMhsWZZTQVqFWujXWJ8Zi+LQFqLOzcfII4uDwDjJzMYSMJY6pQXTOVTqsb2L7jq2Pro1BtUIqXiWoAYywtABLAEQJBYOCoQiIjEkmk8mI204vH4kXhMSRejkBWSOn0RlM5g63V6-SGuJRu3RBxwRxO-XOxgKOLu8ycKGMIHsQA" horizontal /> +</CardGroup> + +## Dot Product between query and document vectors + +Assume we have a set of documents where each document contains a vector of size 4. We want to calculate the dot product between the document vectors and a vector passed down with the query and rank the results according to the dot product score. + +The following schema file defines an attribute tensor field with a tensor type that has one indexed dimension `x` of size 4. In addition, we define a rank profile with the input and the dot product calculation: + +```js +schema example { + document example { + field document_vector type tensor<float>(x[4]) { + indexing: attribute | summary + } + } + rank-profile dot_product { + inputs { + query(query_vector) tensor<float>(x[4]) + } + first-phase { + expression: sum(query(query_vector)*attribute(document_vector)) + } + } +} +``` + +Example [JSON](/en/reference/schemas/document-json-format#tensor) document with the vector [1.0, 2.0, 3.0, 5.0], using indexed tensors short form: + +```json +[ + { + "put": "id:example:example::0", + "fields": { + "document_vector" : [1.0, 2.0, 3.0, 5.0] + } + } +] +``` + +Example query set in a searcher with the vector [1.0, 2.0, 3.0, 5.0]: + +```java +public Result search(Query query, Execution execution) { + query.getRanking().getFeatures().put("query(query_vector)", + Tensor.Builder.of(TensorType.fromSpec("tensor<float>(x[4])")). + cell().label("x", 0).value(1.0). + cell().label("x", 1).value(2.0). + cell().label("x", 2).value(3.0). + cell().label("x", 3).value(5.0).build()); + return execution.search(query); +} +``` + +<CardGroup cols={1}> + <Card title="Play around with this example in the playground" icon="play-circle" href="https://docs.vespa.ai/playground/#N4KABGBEBmkFxgNrgmUrWQPYAd5QFNIAaFDSPBdDTAO30gEcBXAgJwE8AKF9jgfQBuBAMYAXLGwCUJMjUhEEkMQVoBnSVwAeiACwBdKXEQBGYgCZiAZmIBWfZDkQAvnOekM1crgZEP8yjQnOgYAQzExNgBLACNmFS4AEywRZgBbVTEhUQlpWRpMRSgVdU0dAyNTC2s7B2DXDHc5L0wfJT9ginwW8nolfILCBjV0nlZOMb5s8UkpMAAqMHDI2PiCJJT0zOncqRl6txR9EGcgA" horizontal /> +</CardGroup> + +Note that this example calculates the dot product for every document retrieved by the query. Consider using [approximate nearest neighbor](/en/querying/approximate-nn-hnsw) search with `distance-metric` [dotproduct](/en/reference/schemas/schemas#distance-metric). + +## Logistic regression models with cross features + +One simple way to use machine-learning is to generate cross features from a set of base features and then do a logistic regression on these. How can this be expressed as Vespa tensors? + +Assume we have three base features: + +```js +query(interests): tensor(interest{}) - A sparse, weighted set of the interests of a user. +query(location): tensor(location{}) - A sparse set of the location(s) of the user. +attribute(topics): tensor(topic{}) - A sparse, weighted set of the topics of a given document. +``` + +From these we have generated all 3d combinations of these features and trained a logistic regression model, leading to a weight for each possible combination: + +```js +tensor(interest{}, location{}, topic{}) +``` + +This weight tensor can be added as a [constant tensor](/en/reference/schemas/schemas#constant) to the application package, say `constant(model)`. With that we can compute the model in a rank profile by the expression + +```js +sum(query(interests) * query(location) * attribute(topics) * constant(model)) +``` + +Where the first three factors generates the 3d cross feature tensor and the last combines them with the learned weights. + +<CardGroup cols={1}> + <Card title="Play around with this example in the playground" icon="play-circle" href="https://docs.vespa.ai/playground/#N4KABGBEBmkFxgNrgmUrWQPYAd5QFNIAaFDSPBdDTAO30gEcBXAgJwE8AKAS1oBd2BAM79hAShJkakIgkiDawrG14Cho4AF9xcYAGMAhm2FwADADoAHMTABbZsJ77zFgKxbI0iFulbSGNTkuAxEATKUaN50DCzs3AA2WEb8PFi0kuE0hAyKyqpJKWm02rrAtAQA7gD6HCoA1nAAjJ7Rvhj+0kGYIfJh0RT43eT08ob8-Gw8AEbMglz8uM4SUtkQsrkESioLS-qlek0ATADMzRZmrdntqJ2BA705WZiRwzHy+umihgJcdlgAEwICUy0XWcigeR2fEEbBE-G0xEK42KiMWOGcBzQAB1aBBgDCNPw4EYTEjkij0nAKjU6mx6sR0c5mqctK4msRcfjCXDRCTjMJyUUqTTag1GXs4CcACweVxHTl4tA8+H8snI1JU4Q-arQNg-fQ8YSfCUYlzHE5sywnRXc9S84mkwUa4pwbW0XX62iG41YU3MmVyyzS23K+2qhxOfRCym0alVMX0-3m1muNyhgnhvmR5wxzVx0V0hlMlyBq0WABsGZV2ccuZdWp1eoNRpNJZZltcVa5YdhEbr0YbcfdnpbvuTUtl5YA7NWs8TplgsPVnRT8-HaeL2xbyzYe5m+3zF8vV8KCwmixOy64AJxzw8LpcrvOukfN72tv3b1NNC73olwMez5Dm6TZej6baStev5NLiVw0DcPjPG82CRBszzrK8YJQKMUCrGsGzyMIzB2FwcScGoD4SGAABUYDkYka7FOItFgOMkwzHMBC7Ga1F0Z8Sj8D8-B-ICwLiJIbR+CgAC6IBaEAA" horizontal /> +</CardGroup> + +## Matrix Product between 1d vector and 2d matrix + +Assume we have a 3x2 matrix represented in an attribute tensor field `document_matrix` with a tensor type `tensor<float>(x[3],y[2])` with content: + +```json +{ {x:0,y:0}:1.0, {x:1,y:0}:3.0, {x:2,y:0}:5.0, {x:0,y:1}:7.0, {x:1,y:1}:11.0, {x:2,y:1}:13.0 } +``` + +Also assume we have 1x3 vector passed down with the query as a tensor with type `tensor<float>(x[3])` with content: + +```json +{ {x:0}:1.0, {x:1}:3.0, {x:2}:5.0 } +``` + +that is set as `query(query_vector)` in a searcher as specified in [query feature](/en/ranking/ranking-expressions-features#using-query-variables). + +To calculate the matrix product between the 1x3 vector and 3x2 matrix (to get a 1x2 vector) use the following ranking expression: + +```bash +sum(query(query_vector) * attribute(document_matrix),x) +``` + +This is a sparse tensor product over the shared dimension `x`, followed by a sum over the same dimension. + +<CardGroup cols={1}> + <Card title="Play around with this example in the playground" icon="play-circle" href="https://docs.vespa.ai/playground/#N4KABGBEBmkFxgNrgmUrWQPYAd5QFNIAaFDSPBdDTAO30gEMAXZgJwEsAjAV2YIAUAEywBjHgFsCtZgH0JLTgA8AlCTI1IRBJH60AzljYAeaABssLAHwCliAMwBdYgE9EAJkcq4iRAEZiAHZnB2I-PxCAVjCnR0gNCABfDUTSDGpyXAYiNM1KNAS6BgBHHgI2FwFS8pdZADcCUWYjNVyaQgY9QxNzS2YbOydvf2J7Yki4wuSMVI0MzCydHMKKfHnyeh11dogtBn1JKrKKo5r6xua2FTAAKjAWdm4+QRFxKRl5RQ5VYlV49umqEBjhAiSAA" horizontal /> +</CardGroup> + +## Using a tensor as a lookup structure + +Tensors with mapped dimensions look similar to maps, but are more general. What if all needed is a simple map lookup? See [tensor performance](/en/performance/feature-tuning#mapped-lookups) for more details. + +Assume a tensor attribute `my_map` and this is the value for a specific document: + +```json +tensor<float>(x{},y[3]):{a:[1,2,3],b:[4,5,6],c:[7,8,9]} +``` + +To create a query to select which of the 3 named vectors (a,b,c) to use for some other calculation, wrap the wanted label to look up inside a tensor. Assume a query tensor `my_key` with type/value: + +```json +tensor<float>(x{}):{b:1.0} +``` + +Do the lookup, returning a tensor of type `tensor<float>(y[3])`: + +```js +sum(query(my_key)*attribute(my_map),x) +``` + +If the key does not match anything, the result will be empty: `tensor<float>(y[3]):[0,0,0]`. For something else, add a check up-front to check if the lookup will be successful and run a fallback expression if it is not, like: + +```js +if(reduce(query(my_key)*attribute(my_map),count) == 3, + reduce(query(my_key)*attribute(my_map),sum,x), + tensor<float>(y[3]):[0.5,0.5,0.5]) +``` + +<Note> + **Note:** + +The above can be considered the same as creating a [slice](../reference/ranking/ranking-expressions#slice), like `(y*x){x:b}`. The above syntax allows an optimized execution, find an example in the [Tensor Playground](https://docs.vespa.ai/playground/#N4KABGBEBmkFxgNrgmUrWQPYAd5QFNIAaFDSPBdDTAO30gFssATAgGwGcSybIiEkAC4FanLACcAFIwD6ASxbAAvsQAeiAMwBdAJRxgjAIxxEAFgB0ABmJgArNdsA2a9tuMATKYDsjsAA4-AE5XZUheCGVeVV5qclwGIlIaKEo0CLoGZjZ2BRYeFIh+BhExSRk8lX1DLyNrMIyojBiMOMwEwSSMinw28npBRgBDHBwCFll2LCwAawBXPGTC4sFOOcYZVg5OACpsjjziOUVdcJSm1BbUPqgOwgK+NJuigahOdnkAYy7C+8FNnK7fa5E6GPJwTwNc7RFDaEDKIA). +</Note> + +## Slicing with lambda + +A common use case is to use a tensor lambda function to slice out the first `k` dimensions of a vector representation of `m` dimensions where `m` is larger than `k`. Slicing with lambda functions is great for representing vectors from [Matryoshka Representation Learning](https://arxiv.org/abs/2205.13147). + +> Matryoshka Representation Learning (MRL) which encodes information at different granularities and allows a single embedding to adapt to the computational constraints of downstream tasks. + +The following slices the first 256 dimensions of a tensor `t`: + +```js +tensor<float>(x[256])(t{x:(x)}) +``` + +Importantly, this does only reference into the original tensor, avoiding copying the tensor to a smaller tensor. + +The following is a complete example where we have stored an original vector representation with 3072 dimensions, And we slice the first 256 dimensions of the original representation to perform a dot product in the first-phase expression, followed by a full computation over all dimensions in the second-phase expression. See [phased ranking](/en/ranking/phased-ranking) for context on using Vespa phased computations and [customizing reusable frozen embeddings with Vespa](https://blog.vespa.ai/tailoring-frozen-embeddings-with-vespa/). + +```js +schema example { + document example { + field document_vector type tensor<float>(x[3072]) { + indexing: attribute | summary + } + } + rank-profile small-256-first-phase { + inputs { + query(query_vector) tensor<float>(x[3072]) + } + function slice_first_dims(t) { + expression: l2_normalize(tensor<float>(x[256])(t{x:(x)}), x) + } + first-phase { + expression: sum( slice_first_dims(query(query_vector)) * slice_first_dims(attribute(document_vector)) ) + } + second-phase { + expression: sum( query(query_vector) * attribute(document_vector) ) + } + } +} +``` + +See also a runnable example in this [tensor playground example](https://docs.vespa.ai/playground/#N4KABGBEBmkFxgNrgmUrWQPYAd5QFNIAaFDSPBdDTAO30gFsBPAfQAsBLAc3dYBNOjVgDcCAYwAuWAE4kyNSEQSRJBWgGdZAHmgAbLAENJAPgAUAD0QBGAEwBdAJRwbxW8QDMxACzEArMQAbMQA7MQAHMQAnMTWAAyx1rEOkAoQAL4K6aQY1OS4DEQ5ipRoaXQM0JwyGpKsgQJCGqxY0KwsHDx8gsJiUrLyNJjKUGqaspaIgU5mHVy8jb0S0jLAFnCWjumOqUOZGNkKeZgFKkXlFPjH5PQqtLKMhnqcAF7nQxBKDHq2rPcyj2ebzMVRqdQaPWarXabHm3SEomWsmIYAsO3K+1QmPsIHSQA). diff --git a/en/ranking/tensor-user-guide.html b/en/ranking/tensor-user-guide.html deleted file mode 100644 index 23a6370ce5..0000000000 --- a/en/ranking/tensor-user-guide.html +++ /dev/null @@ -1,450 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Tensor Guide" -redirect_from: -- /en/tensor-user-guide.html ---- - -<p> -Vespa provides a <em>tensor</em> data model and computation engine to support advanced computations over data. -This guide explains the tensor support in Vespa. See also the <a href="../reference/ranking/tensor.html">tensor reference</a>, -and our <a href="https://dl.acm.org/doi/10.1145/3459104.3459152">published paper</a> -(<a href="../../assets/a_tensor_formalism_for_computer_science.pdf">pdf</a>). -</p> - - -<h2 id="tensor-concepts">Tensor concepts</h2> - -<p> -A tensor in Vespa is a data structure which generalizes scalars, vectors and matrices to any number of dimensions: -</p> -<ul> - <li>A scalar is a tensor of rank 0</li> - <li>A vector is a tensor of rank 1</li> - <li>A matrix is a tensor of rank 2</li> - <li>...</li> -</ul> - -<p> -Tensors consist of a set of scalar valued <i>cells</i>, with each cell having a unique <i>address</i>. -A cell's address is specified by its index or label in all the dimensions of that tensor. -The number of dimensions in a tensor is the <em>rank</em> of the tensor. -Each dimension can be either <em>mapped</em> or <em>indexed</em>. -Mapped dimensions are sparse and allow any label (string identifier) designating their address, -while indexed dimensions use dense numeric indices starting at 0. -</p> - -<p> -Example: Using <a href="../reference/ranking/tensor.html#tensor-literal-form">literal form</a>, the tensor: -</p> -<pre> -{ - {user:bob, movie:"Heat"}:0.1, - {user:alice, movie:"Frozen"}:0.9, - {user:carol, movie:"Top Gun"}:0.3, -} -</pre> -<p> -has two dimensions named <code>user</code> and <code>movie</code>, and has three cells with defined values: -</p> -<img src="/assets/img/tensor-mapped.png" alt="Tensor graphical representation" /> - -<p> -A tensor has a <i>type</i>, which consists of a set of dimension names, dimension types, and -a <a href="../reference/ranking/tensor.html#tensor-type-spec">tensor cell value type</a>. - -The dimension name can be anything. - -This defines a 2-dimensional mapped tensor (sparse 2D matrix) of floats as illustrated above: -<pre> -tensor<float>(user{},movie{}) -</pre> -This is a 2-dimensional indexed tensor (a 2D 1280x720 matrix). For example, used to represent an image: -<pre> -tensor<int8>(x[1280],y[720]) -</pre> -This is a 3-dimensional indexed tensor. For example, used to represent spatial data: -<pre> -tensor<float>(x[256], y[256], z[128]) -</pre> -This is a <i>mixed</i> tensor combining a <i>mapped</i> dimension and an <i>indexed</i> dimension. For example, used -to represent word2vec: -<pre> -tensor<bfloat16>(word_id{},vec[300]) -</pre> -Another mixed tensor used to represent paragraph <a href="../rag/embedding.html">embeddings</a> -used to power <a href="https://blog.vespa.ai/semantic-search-with-multi-vector-indexing/">multi-vector indexing</a>. -<pre> -tensor<float>(paragraph{},embedding[768]) -</pre> - -<p>Vespa uses the tensor type information to optimize tensor expression execution plans at configuration time.</p> - - -<h2 id="tensor-document-fields">Tensor document fields</h2> - -<p>Document fields in <a href="../basics/schemas.html">schemas</a> can be of any tensor type: -<pre> -schema product { - - document product { - - field title type string { - indexing: summary | index - } - - field price type int { - indexing: summary | attribute - } - - field popularity type float { - indexing: summary | attribute - } - - field sales_score type tensor<float>(category{}) { - indexing: summary | attribute - } - - field embedding type tensor<float>(x[4]) { - indexing: summary | attribute | index - attribute { - distance-metric: dotproduct - } - } - } -} -</pre> -<p>The above schema exemplifies a <em>product</em> with two tensor fields. - The <code>sales_score</code> tensor field represents - how popular a product is per unique <em>category</em>. This information could be used when - <a href="../basics/ranking.html">ranking</a> products for a user query. The <code>embedding</code> - tensor field represents an embedding vector representation of the product. -</p> - <ul> - <li><b>sales_score</b> is a <em>mapped</em> tensor with a single mapped dimension <code>category</code>. - Mapped dimensions are sparse and allow any label (string identifier) designating their address.</li> - <li><b>embedding</b> is an <em>indexed</em> tensor. Indexed dimensions use dense numeric indices starting at 0.</li> - </ul> - -<p>To perform computations over a document tensor field in <a href="ranking-intro">ranking</a>, - the field must be defined with <a href="../content/attributes.html">attribute</a>. - <p>Tensors with the following types can be indexed with <a href="../querying/approximate-nn-hnsw">HNSW</a> and searched - efficiently using the <a href="../reference/querying/yql.html#nearestneighbor">nearestNeighbor</a> query operator:</p> - <ul> - <li>One indexed dimension - single vector per tensor field</li> - <li>One mapped and one indexed dimension - multiple vectors per tensor field</li> - </ul> - <p>See <a href="../querying/nearest-neighbor-search">nearest neighbor search</a> and - <a href="../querying/approximate-nn-hnsw">approximate nearest neighbor search</a>.</p> -<p> - - -<h2 id="feeding-tensors">Feeding tensors</h2> - -<p>An example <em>product</em> document in Vespa JSON format. -This example uses the product category string as the mapped label key. -The <em>embedding</em> tensor stores and indexes (<a href="../querying/approximate-nn-hnsw">HNSW</a>) a -dense <a href="../rag/embedding.html">embedding</a>.</p> - -<pre>{% highlight json %} -{ - "put": "id:shopping:product::B0BFW5SXX2", - "fields": { - "title": "Keyboard Case for iPad Pro 12.9 inch", - "price": 29, - "popularity": 0.8, - "sales_score": { - "Tablet Keyboard Cases": 8.0, - "Keyboards": 2.0, - "Personal Computers": 0.1 - }, - "embedding": [ - 1, - 2, - 3, - 4 - ] - } -}{% endhighlight %}</pre> - -<p>The JSON feed format example above uses short value form. -Tensor fields can be represented using different -<a href="../reference/schemas/document-json-format.html#tensor">JSON format</a> verbosity.</p> - -<p>You can use <a href="../writing/partial-updates.html">partial updates</a> of tensor fields with -<a href="../reference/schemas/document-json-format.html#tensor-add">add</a>, -<a href="../reference/schemas/document-json-format.html#tensor-remove">remove</a> and -<a href="../reference/schemas/document-json-format.html#tensor-modify">modify</a> tensor cells, or -<a href="../reference/schemas/document-json-format.html#tensor-field">assign</a> a completely new tensor value. -From <a href="../applications/components.html">container components</a> you can create and modify tensor values using the -<a href="https://javadoc.io/doc/com.yahoo.vespa/vespajlib/latest/com/yahoo/tensor/Tensor.html">tensor Java API</a>.</p> - -<p>You would typically re-calculate the per category sales scores outside Vespa and update them continuously -using <a href="../writing/partial-updates.html">partial updates</a> to avoid re-feeding or re-indexing of other fields.</p> - -<h2 id="querying-with-tensors">Querying with tensors</h2> - -<p>Query input tensors <b>must</b> be defined in the schema <a href="ranking-intro">rank-profile</a> using -<a href="../reference/schemas/schemas.html#inputs">inputs</a>:</p> - -<pre> -rank-profile product_ranking inherits default { - inputs { - query(q_category) tensor<float>(category{}) - query(q_embedding) tensor<float>(x[4]) - } - ..... -} -</pre> -<p>The above defines two query input tensors that we can reference in <a href="ranking-intro">ranking</a> expressions. - With the tensor query name and tensor type defined, you can:</p> - <ul> - <li>Add it to the query in a <a href="../applications/searchers.html">Searcher</a> using the - <a href="https://javadoc.io/doc/com.yahoo.vespa/vespajlib/latest/com/yahoo/tensor/Tensor.html">Tensor class</a> - and setting it by <code>Query.getRanking().getFeatures.put("query(q_embedding)", myTensorInstance)</code>, or - <li>Pass it in the request, using an HTTP parameter like <code>input.query(q_embedding)</code> and - passing a tensor <a href="../reference/ranking/tensor.html#tensor-literal-form">value</a>. - </ul> -<p>An example query request using <a href="../clients/vespa-cli.html#queries">Vespa CLI query request</a>: -<pre>{% highlight shell %} -vespa query 'yql=select * from product where {targetHits:1}nearestNeighbor(embedding,q_embedding)' \ - 'input.query(q_embedding)=[1,2,3,4]' \ - 'input.query(q_category)={"Tablet Keyboard Cases":0.8, "Keyboards":0.3}' \ - 'ranking=product_ranking' {% endhighlight %}</pre> -<p> -This query request example assumes that the user query has been mapped (classified) to be related to the -<em>Tablet Keyboard Cases</em> and <em>Keyboards</em> categories. Similarly, the user query has been mapped to -a dense vector representation (<code>query(q_embedding</code>) and is used as -input to the <a href="../reference/querying/yql.html#nearestneighbor">nearestNeighbor</a> -query operator, expressed with the <a href="../querying/query-language.html">YQL query language</a>. -</p> - -<p> -The Vespa CLI uses HTTP GET and you can use the <em>-v</em> flag to see the curl GET equivalent. For -<a href="../querying/query-api.html#using-post">POST</a> query requests using JSON, the equivalent JSON is:</p> - -<pre>{% highlight json %} -{ - "yql": "select * from product where {targetHits:1}nearestNeighbor(embedding,q_embedding)", - "input": { - "query(q_embedding)": [ - 1, - 2, - 3, - 4 - ], - "query(q_category)": { - "Tablet Keyboard Cases": 0.8, - "Keyboards":0.3 - } - }, - "ranking": "product_ranking" -}{% endhighlight %}</pre> - -<p>If the input query tensor used for the <a href="../reference/querying/yql.html#nearestneighbor">nearestNeighbor</a> - operator is not defined in the schema rank-profile, the request will fail:</p> -<pre> - "Expected 'query(q_embedding)' to be a tensor, but it is the string '[1,2,3,4]'" -</pre> - - -<h2 id="ranking-with-tensors">Ranking with tensors</h2> - -<p> - Tensors can be used in making inference computations over documents that are matched by a query. - These computations are expressed with <a href="../reference/ranking/ranking-expressions.html">ranking expressions</a> in - schema <a href="../reference/schemas/schemas.html#rank-profile">rank profiles</a>. We can use this support - to rank products by both the dense embedding dot product similarity and the category sales score. -</p> -<pre> -rank-profile product_ranking inherits default { - - inputs { - query(q_category) tensor<float>(category{}) - query(q_embedding) tensor<float>(x[4]) - } - - function p_sales_score() { - expression: sum(query(q_category) * attribute(sales_score)) - } - - function p_embedding_score() { - expression: closeness(field, embedding) - } - - first-phase { - expression: p_sales_score() + p_embedding_score() - } - match-features: p_sales_score() p_embedding_score() -} -</pre> -<p>The above profile uses a combination of two dot product calculations in the <a href="phased-ranking.html">first phase</a> expression. - -The <code>first-phase</code> expression is invoked for all documents that are <b>retrieved</b> -by the <a href="../querying/query-language.html">YQL query language</a>.</p> - -<ul> - <li> - The <em>p_sales_score</em> function calculates the sparse tensor dotproduct between the <em>query(q_category)</em> and <em>attribute(sales_score)</em> tensor. - </li> - <li> - The <em>p_embedding_score</em> calculates the dense tensor dotproduct between the <em>query(q_embedding)</em> and <em>attribute(embedding)</em> tensors. - The function uses the <a href="../reference/ranking/rank-features.html#closeness(dimension,name)">closeness(dimension,name)</a> <a href="../reference/ranking/rank-features.html">rank-feature</a> - which is calculated by the <a href="../reference/querying/yql.html#nearestneighbor">nearestNeighbor</a> query operator. Alternatively, if we don't - use the <em>nearestNeighbor</em> operator in the query request, we could use sparse tensor dotproduct: -<pre> -function p_embedding_score() { - expression: sum(query(q_embedding) * attribute(embedding)) -}</pre> - </li> -</ul> -<p> -The full list of tensor functions are listed in the -<a href="../reference/ranking/ranking-expressions.html#tensor-functions">ranking expression reference</a>. -Using <a href="../reference/schemas/schemas.html#match-features">match-features</a>, developers -can debug, or log function outputs in the search result.</p> -<pre> -"matchfeatures": { - "p_embedding_score": 30.0, - "p_sales_score": 8.0, -}, -"documentid": "id:shopping:product::B0BFW5SXX2", -"title": "Keyboard Case for iPad Pro 12.9 inch" -</pre> - - -<h2 id="creating-tensors-from-document-fields">Creating tensors from document fields</h2> - -<p>If you need to make tensor computations from non-tensor single-valued attributes, arrays, weighted sets, -or array<struct> fields, you can convert them in a ranking expression:</p> - -<ul> - <li>Creating an <i>indexed</i> tensor where the <i>values</i> are lifted from single-value - attributes (price and popularity) using the tensor generate function: -<pre> -function to_indexed_tensor() { - expression: tensor(x[2]):[attribute(price),attribute(popularity)] -} -</pre> - </li> - - <li>Creating a <i>mapped</i> tensor where the <i>values</i> are lifted from single-value - attributes using the tensor generate function: -<pre> -function to_mapped_tensor() { - expression: tensor(x{}):{key1:attribute(price),key2:attribute(popularity)} -} -</pre> - </li> - - <li>Creating a <i>mapped</i> tensor where the <i>label(s)</i> are lifted from a string array or - single-value attribute can be done with the - <a href="../reference/ranking/rank-features.html#document-features">document feature</a> <code>tensorFromLabels</code>. - </li> - - <li>Creating a <i>mapped</i> tensor where the labels <i>and</i> values are lifted from a weighted set can be done with the - <a href="../reference/ranking/rank-features.html#document-features">document feature</a> <code>tensorFromWeightedSet</code>. - </li> - - <li>Creating a <i>mapped</i> tensor where labels and values are lifted from an - <code>array<struct></code> attribute can be done with the - <a href="../reference/ranking/rank-features.html#document-features">document feature</a> <code>tensorFromStructs</code>. - </li> -</ul> - -<p> - Converting non-tensor fields to tensors at query runtime has a performance penalty that is linear with the number of elements - in the array, weighted set, or struct array. Prefer using native tensor fields instead. - The benefit of converting non-tensor fields is that non-tensor fields like - <code>int</code>, <code>float</code>, <code>weightedset</code>, or <code>array<struct></code> can be efficiently queried. - Only specific tensor types can be searched efficiently using the - <a href="../reference/querying/yql.html#nearestneighbor">nearestNeighbor</a> query operator. -</p> - - -<h2 id="constant-tensors">Constant tensors</h2> - -<p>In addition to document tensors and query tensors, -<a href="../reference/schemas/schemas.html#constant">constant tensors</a> -can be put in the <a href="../reference/applications/application-packages.html">application package</a>. -This is useful for adding machine learned models. Example: -</p> -<pre> -constants { - my_tensor_constant tensor<float>(x[4]): file: constants/constant_tensor_file.json -} -</pre> -<p> -This defines a new tensor with the type as defined and the -contents distributed with the application package in the file -<em>constants/constant_tensor_file.json</em>. The format of this file is the -<a href="../reference/ranking/constant-tensor-json-format.html">constant tensor JSON format</a>: -</p> -<pre>{% highlight json %} -{ - "type": "tensor<float>(x[4])", - "values": [ - 0, - 0, - 0, - 1.0 - ] -}{% endhighlight%}</pre> -<p> -To use this constant tensor in a ranking expression, encapsulate the constant name with <code>constant(...)</code>: -<pre> -rank-profile use_constant_tensor inherits product_ranking { - constants { - my_tensor_constant tensor<float>(x[4]): file: constants/constant_tensor_file.json - } - first-phase { - expression: sum(query(q_embedding) * attribute(embedding) * constant(my_tensor_constant)) - } -}</pre> -<p> -Note that the rank profile <code>inherit</code> the inputs we defined in the <code>product_ranking</code> profile. -With the example data used, the first-phase expression returns the 16.0 since: -</p> -<pre> - "embedding": [ - 1.0, - 2.0, - 3.0, - 4.0 - ], - "query(q_embedding)": [ - 1.0, - 2.0, - 3.0, - 4.0 - ], - "constant(my_tensor_constant)": [ - 0.0, - 0.0, - 0.0, - 1.0 - ] -</pre> - - -<h2 id="tensors-with-strings">Tensors with strings</h2> - -<p>Tensors in Vespa cannot have strings as values, since the mathematical tensor functions -would be undefined for such "tensors". However, you can still represent sets of strings in tensors -by using the strings as keys in a mapped tensor dimensions, using e.g 1.0 as values. -This allows you to perform set operations on strings and similar without making those tensors -incompatible with other tensors and with normal tensor operations.</p> - - -<h2 id="further-reading">Further reading</h2> - -<p>See also:</p> -<ul> - <li><a href="https://blog.vespa.ai/computing-with-tensors/">Blog post: Computing with tensors in Vespa</a>.</li> - <li><a href="onnx">Using ONNX models</a> to use machine-learned models taking tensor input in Vespa.</li> - <li>Some <a href="tensor-examples.html">practical tensor computation examples</a>.</li> - <li>The <a href="https://docs.vespa.ai/playground/#N4KABGBEBmkFxgNrgmUrWQPYAd5QGNIAaFDSPBdDTAF30gAkBTAJ2bAE8sBXMAgIYA7MDgA2AzmAGteQgCbSFYZgA8cbAJYBbZkNpgA7ptoALMLT0BnLKytLFloTdYr17K1c1ZnAOjAs7GCa9gJgVqa2BgA6QmoC2uLMxFy8-MJg0JrK2rYc2dC22gK03iIlYLGIZhxOLmA8VmxgAOY8mvLMALoAFKa0tDhWcAD0I-JYBFa+AG7MVjgCvgKaI3ojdbYAtI1sW20dzL792mIAlJBkqAC+V9ekGNTkuAxEDzQU+E8f9AiQAIIWay2YKhMDyEphKy0Vg8Ai0HhBMwVELSVp6NgCMSaABeJTKYCw0HCggkdhSc3htlCORKrE0BHm-liABVgXYwKYBHM0UIeNoAEbNIngnTWMr2ZEGQQiIUqEymZqdZwcEELGRNQmuMIELCC7L4ny+WL-ETxRJiVXEqGLOy1dlwS40MC3DD3K7fCDYShQZgkK5en2ezBCBjqu0AfU2rH9zq9fr+0Z6qmA1zOcGAAjgAEZiAK4AAma5Omium7vCDB72vWMfIMBugMQHKzXRowKiyGLCi3TOCUpTS+I5o4owzSqR0NssQd2PBvVv5+iuYetxqChv4t5hR9m152QBNQJOqRAFrrETiIADMXXTiEQuYLxBvxEQABZiABWYgANi6XRLN07mXKsXj+N551XONIF+KA2Wcal0hELkeQFLAzHCW1NWERQtx7cUfCsFIPB4MRSiEFpgnKMBtHHZhHHZfxAg4YwMJqMAAANoDELASg42IIVoMJaE4DRJ2dacXRA+cwN9PcVy+BsvQ3KBaNUeidwQmNl3IQ8YPZAAebjeNoAA+ZNUwva9bwzLMH2IJ8X3zd8v1-LpiynYCPRkn1IAg-coP3WDIHglx7BQjgoWYAwRVwTFSkI6QxB44wKMyEEwnkTQ5jtcJosJYldUSHghISoR-AAMRBbJoWERkUjCDiACssGyDjxNLLy533WSD3kwNFLXSAVMgfrMD0lrsh6cMmk0lwUjUjToxSaAegEPMzjWgAqAUzguTy3WknrfP8utBug4KWVMVFUWYABHdoZixPQDFoLAOqAw7vOOhgl0g879xGsb4zDLDtzbLaaLo+Q5tsQCbi61BQJOsbPioJSjwYKrXFyIJzSSIi8o4RAJimWZ5kWZZVnWaMtnxy1phOMRen6QZhjGUnpjmdUqbWIQNnZOnVASAnjloU59oku4UC6EBriAA">tensor playground</a> - which allows you to create and compute with Vespa tensors in your browser.</li> - <li>The <a href="https://javadoc.io/doc/com.yahoo.vespa/vespajlib/latest/com/yahoo/tensor/Tensor.html">Tensor Java API</a>.</li> - <li><a href="../performance/feature-tuning.html#tensor-ranking">Tensor ranking performance</a>.</li> -</ul> diff --git a/en/ranking/tensor-user-guide.mdx b/en/ranking/tensor-user-guide.mdx new file mode 100644 index 0000000000..3c7d40590c --- /dev/null +++ b/en/ranking/tensor-user-guide.mdx @@ -0,0 +1,340 @@ +--- +title: "Tensor Guide" +sidebarTitle: "Tensor user guide" +--- + +Vespa provides a _tensor_ data model and computation engine to support advanced computations over data. This guide explains the tensor support in Vespa. See also the [tensor reference](/en/reference/ranking/tensor), and our [published paper](https://dl.acm.org/doi/10.1145/3459104.3459152) ([pdf](/assets/a_tensor_formalism_for_computer_science.pdf)). + +## Tensor concepts + +A tensor in Vespa is a data structure which generalizes scalars, vectors and matrices to any number of dimensions: + +- A scalar is a tensor of rank 0 +- A vector is a tensor of rank 1 +- A matrix is a tensor of rank 2 +- ... + +Tensors consist of a set of scalar valued _cells_, with each cell having a unique _address_. A cell's address is specified by its index or label in all the dimensions of that tensor. The number of dimensions in a tensor is the _rank_ of the tensor. Each dimension can be either _mapped_ or _indexed_. Mapped dimensions are sparse and allow any label (string identifier) designating their address, while indexed dimensions use dense numeric indices starting at 0. + +Example: Using [literal form](/en/reference/ranking/tensor#tensor-literal-form), the tensor: + +```js +{ + {user:bob, movie:"Heat"}:0.1, + {user:alice, movie:"Frozen"}:0.9, + {user:carol, movie:"Top Gun"}:0.3, +} +``` + +has two dimensions named `user` and `movie`, and has three cells with defined values: + +<Frame> + ![Tensor graphical representation](/assets/img/tensor-mapped.png) +</Frame> + +A tensor has a _type_, which consists of a set of dimension names, dimension types, and a [tensor cell value type](/en/reference/ranking/tensor#tensor-type-spec). The dimension name can be anything. This defines a 2-dimensional mapped tensor (sparse 2D matrix) of floats as illustrated above: + +```bash +tensor<float>(user{},movie{}) +``` +This is a 2-dimensional indexed tensor (a 2D 1280x720 matrix). For example, used to represent an image: +```bash +tensor<int8>(x[1280],y[720]) +``` +This is a 3-dimensional indexed tensor. For example, used to represent spatial data: +```bash +tensor<float>(x[256], y[256], z[128]) +``` +This is a _mixed_ tensor combining a _mapped_ dimension and an _indexed_ dimension. For example, used to represent word2vec: +```bash +tensor<bfloat16>(word_id{},vec[300]) +``` +Another mixed tensor used to represent paragraph [embeddings](/en/rag/embedding) used to power [multi-vector indexing](https://blog.vespa.ai/semantic-search-with-multi-vector-indexing/). +```bash +tensor<float>(paragraph{},embedding[768]) +``` + +Vespa uses the tensor type information to optimize tensor expression execution plans at configuration time. + +## Tensor document fields + +Document fields in [schemas](/en/basics/schemas) can be of any tensor type: + +```js expandable +schema product { + + document product { + + field title type string { + indexing: summary | index + } + + field price type int { + indexing: summary | attribute + } + + field popularity type float { + indexing: summary | attribute + } + + field sales_score type tensor<float>(category{}) { + indexing: summary | attribute + } + + field embedding type tensor<float>(x[4]) { + indexing: summary | attribute | index + attribute { + distance-metric: dotproduct + } + } + } +} +``` + +The above schema exemplifies a _product_ with two tensor fields. The `sales_score` tensor field represents how popular a product is per unique _category_. This information could be used when [ranking](/en/basics/ranking) products for a user query. The `embedding` tensor field represents an embedding vector representation of the product. + +- **sales\_score** is a _mapped_ tensor with a single mapped dimension `category`. Mapped dimensions are sparse and allow any label (string identifier) designating their address. +- **embedding** is an _indexed_ tensor. Indexed dimensions use dense numeric indices starting at 0. + +To perform computations over a document tensor field in [ranking](/en/ranking/ranking-intro), the field must be defined with [attribute](/en/content/attributes). + +Tensors with the following types can be indexed with [HNSW](/en/querying/approximate-nn-hnsw) and searched efficiently using the [nearestNeighbor](/en/reference/querying/yql#nearestneighbor) query operator: + +- One indexed dimension - single vector per tensor field +- One mapped and one indexed dimension - multiple vectors per tensor field + +See [nearest neighbor search](/en/querying/nearest-neighbor-search) and [approximate nearest neighbor search](/en/querying/approximate-nn-hnsw). + +## Feeding tensors + +An example _product_ document in Vespa JSON format. This example uses the product category string as the mapped label key. The _embedding_ tensor stores and indexes ([HNSW](/en/querying/approximate-nn-hnsw)) a dense [embedding](/en/rag/embedding). + +```json +{ + "put": "id:shopping:product::B0BFW5SXX2", + "fields": { + "title": "Keyboard Case for iPad Pro 12.9 inch", + "price": 29, + "popularity": 0.8, + "sales_score": { + "Tablet Keyboard Cases": 8.0, + "Keyboards": 2.0, + "Personal Computers": 0.1 + }, + "embedding": [ + 1, + 2, + 3, + 4 + ] + } +} +``` + +The JSON feed format example above uses short value form. Tensor fields can be represented using different [JSON format](/en/reference/schemas/document-json-format#tensor) verbosity. + +You can use [partial updates](/en/writing/partial-updates) of tensor fields with [add](/en/reference/schemas/document-json-format#tensor-add), [remove](/en/reference/schemas/document-json-format#tensor-remove) and [modify](/en/reference/schemas/document-json-format#tensor-modify) tensor cells, or [assign](/en/reference/schemas/document-json-format#tensor-field) a completely new tensor value. From [container components](/en/applications/components) you can create and modify tensor values using the [tensor Java API](https://javadoc.io/doc/com.yahoo.vespa/vespajlib/latest/com/yahoo/tensor/Tensor.html). + +You would typically re-calculate the per category sales scores outside Vespa and update them continuously using [partial updates](/en/writing/partial-updates) to avoid re-feeding or re-indexing of other fields. + +## Querying with tensors + +Query input tensors **must** be defined in the schema [rank-profile](/en/ranking/ranking-intro) using [inputs](/en/reference/schemas/schemas#inputs): + +```js +rank-profile product_ranking inherits default { + inputs { + query(q_category) tensor<float>(category{}) + query(q_embedding) tensor<float>(x[4]) + } + ..... +} +``` + +The above defines two query input tensors that we can reference in [ranking](/en/ranking/ranking-intro) expressions. With the tensor query name and tensor type defined, you can: + +- Add it to the query in a [Searcher](/en/applications/searchers) using the [Tensor class](https://javadoc.io/doc/com.yahoo.vespa/vespajlib/latest/com/yahoo/tensor/Tensor.html) and setting it by `Query.getRanking().getFeatures.put("query(q_embedding)", myTensorInstance)`, or +- Pass it in the request, using an HTTP parameter like `input.query(q_embedding)` and passing a tensor [value](/en/reference/ranking/tensor#tensor-literal-form). + +An example query request using [Vespa CLI query request](/en/clients/vespa-cli#queries): + +```bash +vespa query 'yql=select * from product where {targetHits:1}nearestNeighbor(embedding,q_embedding)' \ + 'input.query(q_embedding)=[1,2,3,4]' \ + 'input.query(q_category)={"Tablet Keyboard Cases":0.8, "Keyboards":0.3}' \ + 'ranking=product_ranking' +``` + +This query request example assumes that the user query has been mapped (classified) to be related to the _Tablet Keyboard Cases_ and _Keyboards_ categories. Similarly, the user query has been mapped to a dense vector representation (`query(q_embedding`) and is used as input to the [nearestNeighbor](/en/reference/querying/yql#nearestneighbor) query operator, expressed with the [YQL query language](/en/querying/query-language). + +The Vespa CLI uses HTTP GET and you can use the _-v_ flag to see the curl GET equivalent. For [POST](/en/querying/query-api#using-post) query requests using JSON, the equivalent JSON is: + +```json +{ + "yql": "select * from product where {targetHits:1}nearestNeighbor(embedding,q_embedding)", + "input": { + "query(q_embedding)": [ + 1, + 2, + 3, + 4 + ], + "query(q_category)": { + "Tablet Keyboard Cases": 0.8, + "Keyboards":0.3 + } + }, + "ranking": "product_ranking" +} +``` + +If the input query tensor used for the [nearestNeighbor](/en/reference/querying/yql#nearestneighbor) operator is not defined in the schema rank-profile, the request will fail: + +```json +"Expected 'query(q_embedding)' to be a tensor, but it is the string '[1,2,3,4]'" +``` + +## Ranking with tensors + +Tensors can be used in making inference computations over documents that are matched by a query. These computations are expressed with [ranking expressions](/en/reference/ranking/ranking-expressions) in schema [rank profiles](/en/reference/schemas/schemas#rank-profile). We can use this support to rank products by both the dense embedding dot product similarity and the category sales score. + +```js +rank-profile product_ranking inherits default { + + inputs { + query(q_category) tensor<float>(category{}) + query(q_embedding) tensor<float>(x[4]) + } + + function p_sales_score() { + expression: sum(query(q_category) * attribute(sales_score)) + } + + function p_embedding_score() { + expression: closeness(field, embedding) + } + + first-phase { + expression: p_sales_score() + p_embedding_score() + } + match-features: p_sales_score() p_embedding_score() +} +``` + +The above profile uses a combination of two dot product calculations in the [first phase](/en/ranking/phased-ranking) expression. The `first-phase` expression is invoked for all documents that are **retrieved** by the [YQL query language](/en/querying/query-language). + +- The _p\_sales\_score_ function calculates the sparse tensor dotproduct between the _query(q\_category)_ and _attribute(sales\_score)_ tensor. +- The _p\_embedding\_score_ calculates the dense tensor dotproduct between the _query(q\_embedding)_ and _attribute(embedding)_ tensors. The function uses the [closeness(dimension,name)](/en/reference/ranking/rank-features#closeness(dimension,name)) [rank-feature](/en/reference/ranking/rank-features) which is calculated by the [nearestNeighbor](/en/reference/querying/yql#nearestneighbor) query operator. Alternatively, if we don't use the _nearestNeighbor_ operator in the query request, we could use sparse tensor dotproduct: +```js +function p_embedding_score() { + expression: sum(query(q_embedding) * attribute(embedding)) +} +``` + +The full list of tensor functions are listed in the [ranking expression reference](/en/reference/ranking/ranking-expressions#tensor-functions). Using [match-features](/en/reference/schemas/schemas#match-features), developers can debug, or log function outputs in the search result. + +```json +"matchfeatures": { + "p_embedding_score": 30.0, + "p_sales_score": 8.0, +}, +"documentid": "id:shopping:product::B0BFW5SXX2", +"title": "Keyboard Case for iPad Pro 12.9 inch" +``` + +## Creating tensors from document fields + +If you need to make tensor computations from non-tensor single-valued attributes, arrays, weighted sets, or array\<struct\> fields, you can convert them in a ranking expression: + +- Creating an _indexed_ tensor where the _values_ are lifted from single-value attributes (price and popularity) using the tensor generate function: +```js +function to_indexed_tensor() { + expression: tensor(x[2]):[attribute(price),attribute(popularity)] +} +``` +- Creating a _mapped_ tensor where the _values_ are lifted from single-value attributes using the tensor generate function: +```js +function to_mapped_tensor() { + expression: tensor(x{}):{key1:attribute(price),key2:attribute(popularity)} +} +``` +- Creating a _mapped_ tensor where the _label(s)_ are lifted from a string array or single-value attribute can be done with the [document feature](/en/reference/ranking/rank-features#document-features) `tensorFromLabels`. +- Creating a _mapped_ tensor where the labels _and_ values are lifted from a weighted set can be done with the [document feature](/en/reference/ranking/rank-features#document-features) `tensorFromWeightedSet`. +- Creating a _mapped_ tensor where labels and values are lifted from an `array<struct>` attribute can be done with the [document feature](/en/reference/ranking/rank-features#document-features) `tensorFromStructs`. + +Converting non-tensor fields to tensors at query runtime has a performance penalty that is linear with the number of elements in the array, weighted set, or struct array. Prefer using native tensor fields instead. The benefit of converting non-tensor fields is that non-tensor fields like `int`, `float`, `weightedset`, or `array<struct>` can be efficiently queried. Only specific tensor types can be searched efficiently using the [nearestNeighbor](/en/reference/querying/yql#nearestneighbor) query operator. + +## Constant tensors + +In addition to document tensors and query tensors, [constant tensors](/en/reference/schemas/schemas#constant) can be put in the [application package](/en/reference/applications/application-packages). This is useful for adding machine learned models. Example: + +```js +constants { + my_tensor_constant tensor<float>(x[4]): file: constants/constant_tensor_file.json +} +``` + +This defines a new tensor with the type as defined and the contents distributed with the application package in the file _constants/constant\_tensor\_file.json_. The format of this file is the [constant tensor JSON format](/en/reference/ranking/constant-tensor-json-format): + +```json +{ + "type": "tensor<float>(x[4])", + "values": [ + 0, + 0, + 0, + 1.0 + ] +} +``` + +To use this constant tensor in a ranking expression, encapsulate the constant name with `constant(...)`: + +```js +rank-profile use_constant_tensor inherits product_ranking { + constants { + my_tensor_constant tensor<float>(x[4]): file: constants/constant_tensor_file.json + } + first-phase { + expression: sum(query(q_embedding) * attribute(embedding) * constant(my_tensor_constant)) + } +} +``` + +Note that the rank profile `inherit` the inputs we defined in the `product_ranking` profile. With the example data used, the first-phase expression returns the 16.0 since: + +```json +"embedding": [ + 1.0, + 2.0, + 3.0, + 4.0 + ], + "query(q_embedding)": [ + 1.0, + 2.0, + 3.0, + 4.0 + ], + "constant(my_tensor_constant)": [ + 0.0, + 0.0, + 0.0, + 1.0 + ] +``` + +## Tensors with strings + +Tensors in Vespa cannot have strings as values, since the mathematical tensor functions would be undefined for such "tensors". However, you can still represent sets of strings in tensors by using the strings as keys in a mapped tensor dimensions, using e.g 1.0 as values. This allows you to perform set operations on strings and similar without making those tensors incompatible with other tensors and with normal tensor operations. + +## Further reading + +See also: + +- [Blog post: Computing with tensors in Vespa](https://blog.vespa.ai/computing-with-tensors/). +- [Using ONNX models](/en/ranking/onnx) to use machine-learned models taking tensor input in Vespa. +- Some [practical tensor computation examples](/en/ranking/tensor-examples). +- The [tensor playground](https://docs.vespa.ai/playground/#N4KABGBEBmkFxgNrgmUrWQPYAd5QGNIAaFDSPBdDTAF30gAkBTAJ2bAE8sBXMAgIYA7MDgA2AzmAGteQgCbSFYZgA8cbAJYBbZkNpgA7ptoALMLT0BnLKytLFloTdYr17K1c1ZnAOjAs7GCa9gJgVqa2BgA6QmoC2uLMxFy8-MJg0JrK2rYc2dC22gK03iIlYLGIZhxOLmA8VmxgAOY8mvLMALoAFKa0tDhWcAD0I-JYBFa+AG7MVjgCvgKaI3ojdbYAtI1sW20dzL792mIAlJBkqAC+V9ekGNTkuAxEDzQU+E8f9AiQAIIWay2YKhMDyEphKy0Vg8Ai0HhBMwVELSVp6NgCMSaABeJTKYCw0HCggkdhSc3htlCORKrE0BHm-liABVgXYwKYBHM0UIeNoAEbNIngnTWMr2ZEGQQiIUqEymZqdZwcEELGRNQmuMIELCC7L4ny+WL-ETxRJiVXEqGLOy1dlwS40MC3DD3K7fCDYShQZgkK5en2ezBCBjqu0AfU2rH9zq9fr+0Z6qmA1zOcGAAjgAEZiAK4AAma5Omium7vCDB72vWMfIMBugMQHKzXRowKiyGLCi3TOCUpTS+I5o4owzSqR0NssQd2PBvVv5+iuYetxqChv4t5hR9m152QBNQJOqRAFrrETiIADMXXTiEQuYLxBvxEQABZiABWYgANi6XRLN07mXKsXj+N551XONIF+KA2Wcal0hELkeQFLAzHCW1NWERQtx7cUfCsFIPB4MRSiEFpgnKMBtHHZhHHZfxAg4YwMJqMAAANoDELASg42IIVoMJaE4DRJ2dacXRA+cwN9PcVy+BsvQ3KBaNUeidwQmNl3IQ8YPZAAebjeNoAA+ZNUwva9bwzLMH2IJ8X3zd8v1-LpiynYCPRkn1IAg-coP3WDIHglx7BQjgoWYAwRVwTFSkI6QxB44wKMyEEwnkTQ5jtcJosJYldUSHghISoR-AAMRBbJoWERkUjCDiACssGyDjxNLLy533WSD3kwNFLXSAVMgfrMD0lrsh6cMmk0lwUjUjToxSaAegEPMzjWgAqAUzguTy3WknrfP8utBug4KWVMVFUWYABHdoZixPQDFoLAOqAw7vOOhgl0g879xGsb4zDLDtzbLaaLo+Q5tsQCbi61BQJOsbPioJSjwYKrXFyIJzSSIi8o4RAJimWZ5kWZZVnWaMtnxy1phOMRen6QZhjGUnpjmdUqbWIQNnZOnVASAnjloU59oku4UC6EBriAA) which allows you to create and compute with Vespa tensors in your browser. +- The [Tensor Java API](https://javadoc.io/doc/com.yahoo.vespa/vespajlib/latest/com/yahoo/tensor/Tensor.html). +- [Tensor ranking performance](/en/performance/feature-tuning#tensor-ranking). \ No newline at end of file diff --git a/en/ranking/tensorflow.md b/en/ranking/tensorflow.mdx similarity index 72% rename from en/ranking/tensorflow.md rename to en/ranking/tensorflow.mdx index bfee52d085..157a9f2737 100644 --- a/en/ranking/tensorflow.md +++ b/en/ranking/tensorflow.mdx @@ -1,34 +1,34 @@ --- # Copyright Vespa.ai. All rights reserved. title: "Ranking with TensorFlow Models" -redirect_from: - - /en/tensorflow +sidebarTitle: "Using TensorFlow models" --- -Vespa can import TensorFlow models converted to the ONNX format. The tutorial -[TensorFlow: Deploy model to Vespa through ONNX](https://vespa-engine.github.io/learntorank-DEPRECATED/notebooks/tensorflow-via-onnx.html) -shows an end-to-end example from training a Learning-to-Rank (LTR) model to -deploying it to Vespa. The tutorial can be reproduced by running the -[Jupyter Notebook](https://github.com/vespa-engine/learntorank-DEPRECATED/blob/main/notebooks/tensorflow-via-onnx.ipynb). +Vespa can import TensorFlow models converted to the ONNX format. The tutorial [TensorFlow: Deploy model to Vespa through ONNX](https://vespa-engine.github.io/learntorank-DEPRECATED/notebooks/tensorflow-via-onnx.html) shows an end-to-end example from training a Learning-to-Rank (LTR) model to deploying it to Vespa. The tutorial can be reproduced by running the [Jupyter Notebook](https://github.com/vespa-engine/learntorank-DEPRECATED/blob/main/notebooks/tensorflow-via-onnx.ipynb). Key steps covered in the tutorial above: - Define and train a `tf_model`. - Save the model to disk: `tf_model.save("tf_model_file")` - Convert the model to ONNX with the `tf2onnx` library: -<pre>{% highlight sh %} + +```sh $ python3 -m tf2onnx.convert --saved-model tf_model_file --output tf_model.onnx -{% endhighlight %}</pre> +``` + - Inspect expected input/output format with the `onnx` library: -<pre>{% highlight python %} + +```python import onnx m = onnx.load("simpler_keras_model.onnx") m.graph.input # check input format m.graph.output # check output format -{% endhighlight %}</pre> +``` + - Include the model on Vespa .sd file -<pre> - schema msmarco { + +```js expandable +schema msmarco { document msmarco { field id type string { indexing: summary | attribute @@ -45,7 +45,7 @@ m.graph.output # check output format rank-profile tensorflow { function vespa_input() { expression { - tensor<float>(x[1],y[3]):[ + tensor<float>(x[1],y[3]):[ [fieldMatch(text).queryCompleteness, fieldMatch(text).significance, nativeRank(text)] @@ -63,4 +63,5 @@ m.graph.output # check output format } } } -</pre> +``` + diff --git a/en/ranking/wand.html b/en/ranking/wand.html deleted file mode 100644 index d9042b7378..0000000000 --- a/en/ranking/wand.html +++ /dev/null @@ -1,315 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "WAND: Accelerated OR search" -redirect_from: -- /en/using-wand-with-vespa.html ---- - -<p> - This document describes how to use the Weak And algorithm for accelerated OR like search. -<p> -<p> - The WAND algorithm is described in detail in - <a href="https://www.researchgate.net/profile/David-Carmel-3/publication/221613425_Efficient_query_evaluation_using_a_two-level_retrieval_process/links/02bfe50e6854500153000000/Efficient-query-evaluation-using-a-two-level-retrieval-process.pdf" - data-proofer-ignore> - Efficient Query Evaluation using a Two-Level Retrieval Process (PDF)</a> by Andrei Z. Broder et al.: -</p> -<blockquote cite="https://www.researchgate.net/profile/David-Carmel-3/publication/221613425_Efficient_query_evaluation_using_a_two-level_retrieval_process/links/02bfe50e6854500153000000/Efficient-query-evaluation-using-a-two-level-retrieval-process.pdf"> - We have determined that our algorithm significantly reduces the total number of full evaluations by more than 90%, - almost without any loss in precision or recall. - At the heart of our approach there is an efficient implementation of a new Boolean construct called WAND or Weak AND - that might be of independent interest -</blockquote> -<p> - Vespa has two different implementations of the WAND dynamic pruning algorithm. - Both implementations attempt to retrieve the best top-k scoring documents - without exhaustive scoring all documents which matches any of the terms in the query. -</p> -<p> - Consider a query example <strong><em>is cdg airport in main paris</em>?</strong> from the - <a href="https://github.com/microsoft/MSMARCO-Passage-Ranking">MS Marco Passage Ranking</a> query set. - If we run this query over the 8.8M passage documents using OR - we retrieve and rank <em>7,926,256</em> documents out of <em>8,841,823</em> documents. - That is in other words <a href="ranking-intro">ranking</a> 89% of the total corpus size. - It's close to brute force evaluating all documents (100%). -</p> -<p> - If we instead change to the boolean retrieval logic to AND, - we only retrieve 2 documents and fail to retrieve the relevant document(s). -</p> -<p> - The WAND algorithm tries to address this problem by starting the search for candidate documents using OR, - limiting the number of documents that are ranked, saving both latency and resource usage (cost) - while still returning the same or almost the same top-k results as the brute force OR. - For the example, using WAND with <em>K</em> or <em>totalTargetHits</em> to 1000, only 196,900 documents are fully ranked. - That is a huge improvement over the exhaustive OR search which retrieves and ranks <em>7,926,256</em> documents - and at the same time retrieving the same results as the exhaustive OR search. -</p> -<p> - So what is the catch? Why not use WAND algorithm all the time? - The catch is that the inner wand scoring function can only be a linear sum of the ranking contribution - from each of the query terms, - and one cannot override the score calculation (with a <a href="ranking-expressions-features.html">ranking expression</a>). - The inner scoring function of both WAND implementations in Vespa cannot be overridden - like the ranking expression controlled by the <code>first-phase</code> ranking expression in a rank profile. - Users can only control the weight of the terms or generally features in the query and in the document. - The weights can be adjusted and both WAND implementations in Vespa - attempt to calculate the top-k documents with the highest maximum inner dot product. - WAND could be looked at as performing the maximum inner product search in a sparse vector space, - without brute force calculating it all over all candidates exhaustively. -</p> -<p> - Vespa has two query operators which implements the WAND algorithm; <code>weakAnd</code> and <code>wand</code>. - These two query operators have different characteristics: -</p> -<ul> -<li> - <code>weakAnd</code> is designed for single valued indexed string fields - (or fieldset combining multiple indexed string fields). - <code>weakAnd</code> integrates with linguistic processing (tokenization and stemming). - It uses the per-term inverted document frequency and query term weight in the inner scoring - but does not use document term frequency in the scoring. - Note that when searching a fieldset, the document frequency is aggregated across all member fields, - which can cause common terms like <em>"the"</em> to be pruned from the query — see - <a href="../learn/faq.html#why-can-common-words-like-the-hurt-recall-and-collapse-significance-across-a-fieldset">this FAQ entry</a>. -</li> -<li> - The <code>wand</code> query operator which does not integrate with linguistic processing - like tokenization, stemming and normalization - and the user (you) must specify the query features and their weight and the document features and their weight. - The features do not need to be string, and it is recommended to map from string to numeric types. - For example the pre-trained language model <a href="https://en.wikipedia.org/wiki/BERT_(language_model)">BERT</a> - uses a fixed vocabulary of about 30K tokens and text snippets are tokenized into a set of token ids. - We can then represent the document as a bag of BERT token ids (e.g. using weightedset<int> data type) - where each token id has a weight which is computed during document processing, - e.g. using <a href="https://github.com/AdeDZY/DeepCT">DeepCT or HDCT</a> weighting. - Similar approaches exist for other high dimensional sparse vector spaces - which do not relate to text matching but where one wants to efficiently perform a maximum inner dot product search. -</li> -</ul> -<p> - If you are in doubt whether you can use WAND algorithm to accelerate retrieval, - you can evaluate using a query set and perform the query exhaustive using brute force OR - and compare the top-k results returned when using the approximative WAND. - If top-k as measured by Recall@K is high, you could save compute resources (and get lower latency) by using WAND. -</p> -<p> -In the following sections we discuss these two WAND implementations in detail. -</p> - - - -<h2 id="weakand">weakAnd</h2> -<p> - The <a href="../reference/querying/yql.html#weakand">weakAnd query operator</a> - accepts terms searching over multiple fields and also logical conjunctions using OR/AND. - It's designed to retrieve over indexed string fields and fieldsets (single-valued or multivalued) - and integrates fully with linguistic processing like tokenization and stemming. -</p> -<p> - When using weakAnd via <a href="../querying/query-language.html">YQL</a> - or a <a href="../applications/searchers.html">Searcher plugin</a>, - specify the target for minimum number of hits the operator should produce per content node involved in the query. -</p> -<p> - The effect of tuning <code>totalTargetHits</code> may not be intuitive. - To ensure that you get the best hits possible with a weakAnd, - set the target number somewhat higher than the number of hits returned to the user; - setting it 10 times higher should be more than enough. -</p> -<p> - The reason for increasing the target number is that weakAnd uses a ranking function internally (inner product) - and the hits which are evaluated by the weakAnd scorer is also evaluated by the <code>first-phase</code> ranking expression. -</p> -<p> - Anything similar to classic vector ranking should correlate well with weakAnd inner product scoring, - e.g. <code>nativeFieldMatch</code> or <code>bm25</code> ranking features. -</p> -<p> - Note that because weakAnd relies on feedback identifying which hits are used for first phase ranking - to increase its threshold for what's considered a good hit, - the special <a href="../reference/schemas/schemas.html#rank-profile">unranked rank profile</a> - (which turns off ranking completely) may cause weakAnd queries to become slower than using a real rank profile. -</p> -<p> - The query example expressed in YQL: -</p> -<pre> -select * from passages where ( - default contains "is" OR default contains "cdg" OR - default contains "airport" OR default contains "in" OR default contains "main" OR - default contains "paris" -) -</pre> -<p> - Alternatively using a combination of YQL and user query language -</p> -<pre> -{ - "yql": "select * from passages where userQuery()", - "query": "is cdg airport in main paris?", - "type": "any" -} -</pre> -<p> - Where type <em>any</em> means OR. -</p> -<p> - Using the weakAnd query operator, the query is: -</p> -<pre> -select * from passages where ( - {totalTargetHits: 200} - weakAnd( - default contains "is", default contains "cdg", default contains "airport", - default contains "in", default contains "main", default contains "paris" - ) -) -</pre> -<p> - We specify that the <a href="../reference/querying/yql.html#targethits">target number of hits (top k)</a> - should be 200 (Default 100), - and this number is used per content node if the content is distributed over more than one node. -</p> - - -<h3 id="weakand-inner-scoring">weakAnd inner scoring</h3> -<p> - The weakAnd query operator uses the following ranking features when calculating the inner score dot product: -</p> -<ul> - <li><a href="../reference/ranking/rank-features.html#term(n).significance">term(n).significance</a></li> - <li><a href="../reference/ranking/rank-features.html#term(n).weight">term(n).weight</a></li> -</ul> -<p> - Note that the number of times the term occurs in the document is not used in the inner scoring. -</p><p> - Both term significance and weight features could be overridden in the query using -<a href="../reference/querying/yql.html#annotations">annotations</a>. - If the term significance is not overridden with the query, - the significance is calculated from the indexed corpus using a formula loosely based on - <a href="https://en.wikipedia.org/wiki/Tf%E2%80%93idf">Inverse Document Frequency</a>. -</p> -<p> - Documents that could not potentially compete with any of the hits already in heap (size targetHits) of top hits are skipped, - while the weakAnd implementation still exposes the hits which were evaluated to the first phase ranking function, - and not only the top k hits. - When configured to use multiple threads per search, - each thread maintains a top-k scoring heap but communicates score thresholds. -</p> -<p> - Often times, for the performance reasons listed above, it is preferable to use weakAnd instead of OR. - To enable this behavior, set the query property - <a href="../reference/api/query.html#weakand.replace">weakAnd.replace</a> to true. -</p> - - - -<h2 id="wand">wand</h2> -<p> - The <a href="../reference/querying/yql.html#wand">wand query operator</a> - works over a single <a href="../reference/schemas/schemas.html#weightedset">weightedset field</a> - which can be both string or numeric (int/long) - the weight is always int. -</p><p> - Weighted sets of string must be configured with - <code>match:word</code> or <code>match:exact</code> - - see <a href="../reference/schemas/schemas.html#match">match documentation</a>. - There is no linguistic processing of strings for string features when using the wand query operator. -</p> -<p> - Below is an example passage document type where we pre-process the text using a BERT tokenizer - and map text to bert token ids and assign a weight to each unique token id. -</p> -<pre> -document passage { - field text type string { - indexing: summary | index - } - field deep_ct_tokens type weightedset<int> { - indexing: summary |attribute - attribute:fast-search - } -} -</pre> -<p> -We can process text and tokenize the text with a BERT tokenizer and set the weight per token id: -</p> -<pre> -{ - "put": "id:msmarco:passage::8433854", - "fields": { - "text": "Charles de Gaulle airport (CDG) is the main international airport for Paris", - "deep_ct_tokens": { - "2248": 12, - "1996": 5, - "3729": 9, - "2003": 8, - "2798": 1, - "2139": 1, - "1006": 3, - "1007": 1, - "2290": 5, - "28724": 6, - "3000": 3, - "2005": 5, - "2364": 1, - "3199": 15 - } - } -} -</pre> -<p> - The wand query operator allows full control over both query side weights and document side weights, - and it is guaranteed that it will find the top k best hits ranked by the inner dot product - between the sparse query vector and the sparse document vector. -</p> -<p> - The Vespa <a href="../reference/querying/yql.html#rank">rank query operator</a> - can be used to create a query tree, - where a bag of features is used in the wand for efficient retrieval with normal lexical query terms - to produce matching ranking features like bm25 for the configurable first phase/second-phase ranking. -</p> -<p> - The example below uses the <code>rank</code> operator to also produce normal text matching features - for those top-k documents which are retrieved by the inner product search performed by the wand operator. - The userQuery() does not impact recall, - but creates "normal" ranking features for first-phase or second-phase ranking. - Similar at query time we can use the same type of text to feature mapping - (in this case all query terms have weight 1): -</p> -<pre> -{ - "yql":"select * from passages where rank( - ({totalTargetHits: 25} - wand(deep_ct_tokens, @tokens)), - userQuery())", - "tokens": "{2003: 1, 3729: 1, 2290: 1, 3199: 1, 1999: 1, 2364: 1, 3000: 1}", - "query": "is cdg airport in main paris?", - "type": "any", - "ranking.profile": "deep_bm25" -} -</pre> -<p> -With the rank profile <em>deep_bm25</em> defined as: -</p> -<pre> -rank-profile deep { - first-phase { - expression: rawScore(deep_ct_tokens) + bm25(text) - } - summary-features { - bm25(text) - rawScore(deep_ct_tokens) - } -} -</pre> -<p> - The <a href="../reference/ranking/rank-features.html#rawScore(field)">rawScore</a> ranking feature - is the inner dot product calculated by the wand query operator. - For the 25 documents (per node) with the highest inner product score - there is also a bm25(text) score which we combine with the inner product score. - Note that the bm25 is <strong>only</strong> calculated for the top-k hits returned by the wand. -</p> -<p> -In this example, the inner product score is 41, and the bm25 of the text is 35.003. -</p> diff --git a/en/ranking/wand.mdx b/en/ranking/wand.mdx new file mode 100644 index 0000000000..b3e6bcfe8e --- /dev/null +++ b/en/ranking/wand.mdx @@ -0,0 +1,177 @@ +--- +title: "WAND: Accelerated OR search" +description: "This document describes how to use the Weak And algorithm for accelerated OR like search." +--- + +The WAND algorithm is described in detail in [Efficient Query Evaluation using a Two-Level Retrieval Process (PDF)](https://www.researchgate.net/profile/David-Carmel-3/publication/221613425_Efficient_query_evaluation_using_a_two-level_retrieval_process/links/02bfe50e6854500153000000/Efficient-query-evaluation-using-a-two-level-retrieval-process.pdf) by Andrei Z. Broder et al.: + +> We have determined that our algorithm significantly reduces the total number of full evaluations by more than 90%, almost without any loss in precision or recall. At the heart of our approach there is an efficient implementation of a new Boolean construct called WAND or Weak AND that might be of independent interest + +Vespa has two different implementations of the WAND dynamic pruning algorithm. Both implementations attempt to retrieve the best top-k scoring documents without exhaustive scoring all documents which matches any of the terms in the query. + +Consider a query example ***is cdg airport in main paris*?** from the [MS Marco Passage Ranking](https://github.com/microsoft/MSMARCO-Passage-Ranking) query set. If we run this query over the 8.8M passage documents using OR we retrieve and rank *7,926,256* documents out of *8,841,823* documents. That is in other words [ranking](/en/ranking/ranking-intro) 89% of the total corpus size. It's close to brute force evaluating all documents (100%). + +If we instead change to the boolean retrieval logic to AND, we only retrieve 2 documents and fail to retrieve the relevant document(s). + +The WAND algorithm tries to address this problem by starting the search for candidate documents using OR, limiting the number of documents that are ranked, saving both latency and resource usage (cost) while still returning the same or almost the same top-k results as the brute force OR. For the example, using WAND with *K* or *totalTargetHits* to 1000, only 196,900 documents are fully ranked. That is a huge improvement over the exhaustive OR search which retrieves and ranks *7,926,256* documents and at the same time retrieving the same results as the exhaustive OR search. + +So what is the catch? Why not use WAND algorithm all the time? The catch is that the inner wand scoring function can only be a linear sum of the ranking contribution from each of the query terms, and one cannot override the score calculation (with a [ranking expression](/en/ranking/ranking-expressions-features)). The inner scoring function of both WAND implementations in Vespa cannot be overridden like the ranking expression controlled by the `first-phase` ranking expression in a rank profile. Users can only control the weight of the terms or generally features in the query and in the document. The weights can be adjusted and both WAND implementations in Vespa attempt to calculate the top-k documents with the highest maximum inner dot product. WAND could be looked at as performing the maximum inner product search in a sparse vector space, without brute force calculating it all over all candidates exhaustively. + +Vespa has two query operators which implements the WAND algorithm; `weakAnd` and `wand`. These two query operators have different characteristics: + +- `weakAnd` is designed for single valued indexed string fields (or fieldset combining multiple indexed string fields). `weakAnd` integrates with linguistic processing (tokenization and stemming). It uses the per-term inverted document frequency and query term weight in the inner scoring but does not use document term frequency in the scoring. Note that when searching a fieldset, the document frequency is aggregated across all member fields, which can cause common terms like *"the"* to be pruned from the query — see [this FAQ entry](/en/learn/faq#why-can-common-words-like-the-hurt-recall-and-collapse-significance-across-a-fieldset). +- The `wand` query operator which does not integrate with linguistic processing like tokenization, stemming and normalization and the user (you) must specify the query features and their weight and the document features and their weight. The features do not need to be string, and it is recommended to map from string to numeric types. For example the pre-trained language model [BERT](https://en.wikipedia.org/wiki/BERT_\(language_model\)) uses a fixed vocabulary of about 30K tokens and text snippets are tokenized into a set of token ids. We can then represent the document as a bag of BERT token ids (e.g. using weightedset`<int>` data type) where each token id has a weight which is computed during document processing, e.g. using [DeepCT or HDCT](https://github.com/AdeDZY/DeepCT) weighting. Similar approaches exist for other high dimensional sparse vector spaces which do not relate to text matching but where one wants to efficiently perform a maximum inner dot product search. + +If you are in doubt whether you can use WAND algorithm to accelerate retrieval, you can evaluate using a query set and perform the query exhaustive using brute force OR and compare the top-k results returned when using the approximative WAND. If top-k as measured by Recall@K is high, you could save compute resources (and get lower latency) by using WAND. + +In the following sections we discuss these two WAND implementations in detail. + +## weakAnd + +The [weakAnd query operator](/en/reference/querying/yql#weakand) accepts terms searching over multiple fields and also logical conjunctions using OR/AND. It's designed to retrieve over indexed string fields and fieldsets (single-valued or multivalued) and integrates fully with linguistic processing like tokenization and stemming. + +When using weakAnd via [YQL](/en/querying/query-language) or a [Searcher plugin](/en/applications/searchers), specify the target for minimum number of hits the operator should produce per content node involved in the query. + +The effect of tuning `totalTargetHits` may not be intuitive. To ensure that you get the best hits possible with a weakAnd, set the target number somewhat higher than the number of hits returned to the user; setting it 10 times higher should be more than enough. + +The reason for increasing the target number is that weakAnd uses a ranking function internally (inner product) and the hits which are evaluated by the weakAnd scorer is also evaluated by the `first-phase` ranking expression. + +Anything similar to classic vector ranking should correlate well with weakAnd inner product scoring, e.g. `nativeFieldMatch` or `bm25` ranking features. + +Note that because weakAnd relies on feedback identifying which hits are used for first phase ranking to increase its threshold for what's considered a good hit, the special [unranked rank profile](/en/reference/schemas/schemas#rank-profile) (which turns off ranking completely) may cause weakAnd queries to become slower than using a real rank profile. + +The query example expressed in YQL: + +```sql +select * from passages where ( + default contains "is" OR default contains "cdg" OR + default contains "airport" OR default contains "in" OR default contains "main" OR + default contains "paris" +) +``` + +Alternatively using a combination of YQL and user query language + +```json +{ + "yql": "select * from passages where userQuery()", + "query": "is cdg airport in main paris?", + "type": "any" +} +``` + +Where type *any* means OR. + +Using the weakAnd query operator, the query is: + +```sql +select * from passages where ( + {totalTargetHits: 200} + weakAnd( + default contains "is", default contains "cdg", default contains "airport", + default contains "in", default contains "main", default contains "paris" + ) +) +``` + +We specify that the [target number of hits (top k)](/en/reference/querying/yql#targethits) should be 200 (Default 100), and this number is used per content node if the content is distributed over more than one node. + +### weakAnd inner scoring + +The weakAnd query operator uses the following ranking features when calculating the inner score dot product: + +- [term(n).significance](/en/reference/ranking/rank-features#term(n).significance) +- [term(n).weight](/en/reference/ranking/rank-features#term(n).weight) + +Note that the number of times the term occurs in the document is not used in the inner scoring. + +Both term significance and weight features could be overridden in the query using [annotations](/en/reference/querying/yql#annotations). If the term significance is not overridden with the query, the significance is calculated from the indexed corpus using a formula loosely based on [Inverse Document Frequency](https://en.wikipedia.org/wiki/Tf%E2%80%93idf). + +Documents that could not potentially compete with any of the hits already in heap (size targetHits) of top hits are skipped, while the weakAnd implementation still exposes the hits which were evaluated to the first phase ranking function, and not only the top k hits. When configured to use multiple threads per search, each thread maintains a top-k scoring heap but communicates score thresholds. + +Often times, for the performance reasons listed above, it is preferable to use weakAnd instead of OR. To enable this behavior, set the query property [weakAnd.replace](/en/reference/api/query#weakand.replace) to true. + +## wand + +The [wand query operator](/en/reference/querying/yql#wand) works over a single [weightedset field](/en/reference/schemas/schemas#weightedset) which can be both string or numeric (int/long) - the weight is always int. + +Weighted sets of string must be configured with `match:word` or `match:exact` - see [match documentation](/en/reference/schemas/schemas#match). There is no linguistic processing of strings for string features when using the wand query operator. + +Below is an example passage document type where we pre-process the text using a BERT tokenizer and map text to bert token ids and assign a weight to each unique token id. + +```js +document passage { + field text type string { + indexing: summary | index + } + field deep_ct_tokens type weightedset<int> { + indexing: summary |attribute + attribute:fast-search + } +} +``` + +We can process text and tokenize the text with a BERT tokenizer and set the weight per token id: + +```json +{ + "put": "id:msmarco:passage::8433854", + "fields": { + "text": "Charles de Gaulle airport (CDG) is the main international airport for Paris", + "deep_ct_tokens": { + "2248": 12, + "1996": 5, + "3729": 9, + "2003": 8, + "2798": 1, + "2139": 1, + "1006": 3, + "1007": 1, + "2290": 5, + "28724": 6, + "3000": 3, + "2005": 5, + "2364": 1, + "3199": 15 + } + } +} +``` + +The wand query operator allows full control over both query side weights and document side weights, and it is guaranteed that it will find the top k best hits ranked by the inner dot product between the sparse query vector and the sparse document vector. + +The Vespa [rank query operator](/en/reference/querying/yql#rank) can be used to create a query tree, where a bag of features is used in the wand for efficient retrieval with normal lexical query terms to produce matching ranking features like bm25 for the configurable first phase/second-phase ranking. + +The example below uses the `rank` operator to also produce normal text matching features for those top-k documents which are retrieved by the inner product search performed by the wand operator. The userQuery() does not impact recall, but creates "normal" ranking features for first-phase or second-phase ranking. Similar at query time we can use the same type of text to feature mapping (in this case all query terms have weight 1): + +```json +{ + "yql":"select * from passages where rank( + ({totalTargetHits: 25} + wand(deep_ct_tokens, @tokens)), + userQuery())", + "tokens": "{2003: 1, 3729: 1, 2290: 1, 3199: 1, 1999: 1, 2364: 1, 3000: 1}", + "query": "is cdg airport in main paris?", + "type": "any", + "ranking.profile": "deep_bm25" +} +``` + + +With the rank profile *deep\_bm25* defined as: + +```js +rank-profile deep { + first-phase { + expression: rawScore(deep_ct_tokens) + bm25(text) + } + summary-features { + bm25(text) + rawScore(deep_ct_tokens) + } +} +``` + +The [rawScore](/en/reference/ranking/rank-features#rawScore(field)) ranking feature is the inner dot product calculated by the wand query operator. For the 25 documents (per node) with the highest inner product score there is also a bm25(text) score which we combine with the inner product score. Note that the bm25 is **only** calculated for the top-k hits returned by the wand. + +In this example, the inner product score is 41, and the bm25 of the text is 35.003. \ No newline at end of file diff --git a/en/ranking/xgboost.md b/en/ranking/xgboost.mdx similarity index 57% rename from en/ranking/xgboost.md rename to en/ranking/xgboost.mdx index baee1177b3..476a939199 100644 --- a/en/ranking/xgboost.md +++ b/en/ranking/xgboost.mdx @@ -1,12 +1,9 @@ --- -# Copyright Vespa.ai. All rights reserved. title: "Ranking with XGBoost Models" -redirect_from: - - /en/xgboost +sidebarTitle: "Using XGBoost models" --- -Vespa supports importing Gradient Boosting Decision Tree (GBDT) models trained with -[XGBoost](https://xgboost.readthedocs.io/). +Vespa supports importing Gradient Boosting Decision Tree (GBDT) models trained with [XGBoost](https://xgboost.readthedocs.io/). ## Exporting models from XGBoost @@ -14,15 +11,12 @@ Vespa supports two XGBoost model formats: UBJ (recommended) and JSON (legacy). ### UBJ format (recommended) -{% include version.html version="8.656.31" %} +`Vespa 8.656.31+` -The recommended way to export an XGBoost model for Vespa is using -[`save_model()`](https://xgboost.readthedocs.io/en/latest/python/python_api.html#xgboost.Booster.save_model) -with the `.ubj` (Universal Binary JSON) extension. -UBJ has been the default XGBoost model format since XGBoost 2.1.0 and -preserves all model information: tree structure, `base_score`, feature names, and objective. +The recommended way to export an XGBoost model for Vespa is using [`save_model()`](https://xgboost.readthedocs.io/en/latest/python/python_api.html#xgboost.Booster.save_model) with the `.ubj` (Universal Binary JSON) extension. UBJ has been the default XGBoost model format since XGBoost 2.1.0 and preserves all model information: tree structure, `base_score`, feature names, and objective. -<pre>{% highlight python %} + +```python import xgboost as xgb import numpy as np @@ -34,13 +28,19 @@ model = xgb.train(param, dtrain, num_boost_round=10) # Export as UBJ model.save_model("my_model.ubj") -{% endhighlight %}</pre> +``` + + + +<Warning> +**Important:** + +Do **not** use `save_model("model.json")` — this produces a different JSON structure +(with a `learner` wrapper) that Vespa cannot parse. Only `dump_model()` with `dump_format="json"` is supported for the JSON path. +</Warning> -{% include warning.html content='Do **not** use `save_model("model.json")` — this produces a different JSON structure -(with a `learner` wrapper) that Vespa cannot parse. Only `dump_model()` with `dump_format="json"` is supported for the JSON path.' %} -Since the UBJ format preserves the objective, Vespa automatically applies the correct -transformation (e.g. sigmoid for logistic objectives) — no need to wrap the ranking expression manually. +Since the UBJ format preserves the objective, Vespa automatically applies the correct transformation (e.g. sigmoid for logistic objectives) — no need to wrap the ranking expression manually. ### JSON format (legacy) @@ -48,18 +48,21 @@ Vespa also supports importing XGBoost's JSON model dump via [`dump_model()`](https://xgboost.readthedocs.io/en/latest/python/python_api.html#xgboost.Booster.dump_model) with `dump_format="json"`. -<pre>{% highlight python %} + +```python import xgboost as xgb dtrain = xgb.DMatrix("training-vectors.txt") param = {"base_score": 0, "max_depth": 1, "objective": "reg:squarederror"} bst = xgb.train(param, dtrain, 2) bst.dump_model("trained-model.json", fmap="feature-map.txt", with_stats=False, dump_format="json") -{% endhighlight %}</pre> +``` + This produces a JSON array of tree objects: -<pre>{% highlight json %} + +```json [ { "nodeid": 0, "depth": 0, "split": "fieldMatch(title).completeness", "split_condition": 0.772132337, "yes": 1, "no": 2, "missing": 1, "children": [ { "nodeid": 1, "leaf": 0.673938096 }, @@ -70,41 +73,45 @@ This produces a JSON array of tree objects: { "nodeid": 2, "leaf": 0.55586201 } ]} ] -{% endhighlight %}</pre> +``` + + +The `split` attribute represents the Vespa feature name and must resolve to a Vespa [rank feature](/en/reference/ranking/rank-features) defined in the [document schema](/en/basics/schemas), or a user-defined [function](/en/ranking/ranking-expressions-features#function-snippets). -The `split` attribute represents the Vespa feature name and must resolve to a Vespa -[rank feature](../reference/ranking/rank-features.html) defined in the [document schema](../basics/schemas.html), -or a user-defined [function](ranking-expressions-features.html#function-snippets). +The training data is represented using [LibSVM text format](https://xgboost.readthedocs.io/en/latest/tutorials/input_format.html). See also a complete [XGBoost training notebook](https://github.com/vespa-engine/sample-apps/blob/master/commerce-product-ranking/notebooks/Train-xgboost.ipynb) using `ranking` objective. -The training data is represented using [LibSVM text format](https://xgboost.readthedocs.io/en/latest/tutorials/input_format.html). -See also a complete [XGBoost training notebook](https://github.com/vespa-engine/sample-apps/blob/master/commerce-product-ranking/notebooks/Train-xgboost.ipynb) using `ranking` objective. -{% include warning.html content='`dump_model()` JSON does **not** preserve `base_score`. +<Warning> +**Important:** + +`dump_model()` JSON does **not** preserve `base_score`. Set `base_score=0` during training, or accept that Vespa predictions will be offset. -For logistic objectives, you must manually wrap the expression in `sigmoid()` (see [Objective types](#xgboost-objective-types)).' %} +For logistic objectives, you must manually wrap the expression in `sigmoid()` (see [Objective types](#xgboost-objective-types)). +</Warning> + ## Feature mappings from XGBoost to Vespa -Model feature names must map to Vespa [rank features](../reference/ranking/rank-features.html). -The mapping method depends on the model format. +Model feature names must map to Vespa [rank features](/en/reference/ranking/rank-features). The mapping method depends on the model format. ### UBJ feature mapping -For UBJ models, place a features file named `<model_name>-features.txt` alongside the `.ubj` file -in the `models` directory. The file contains one feature name per line, matching the training column order: +For UBJ models, place a features file named `<model_name>-features.txt` alongside the `.ubj` file in the `models` directory. The file contains one feature name per line, matching the training column order: + -<pre> +```txt feature_1 feature_2 feature_3 -</pre> +``` + For a model file named `my_model.ubj`, the features file must be named `my_model-features.txt`. -Then define rank profile [functions](ranking-expressions-features.html#function-snippets) -that match the feature names and map them to Vespa document attributes or query features: +Then define rank profile [functions](/en/ranking/ranking-expressions-features#function-snippets) that match the feature names and map them to Vespa document attributes or query features: + -<pre> +```js schema my_app { document my_app { field price type double { @@ -129,25 +136,24 @@ schema my_app { } } } -</pre> +``` -If the model was trained with feature names that are valid Vespa rank features -(e.g. `attribute(price)`), the functions are not needed — Vespa resolves them directly. + +If the model was trained with feature names that are valid Vespa rank features (e.g. `attribute(price)`), the functions are not needed — Vespa resolves them directly. ### JSON feature mapping -When using `dump_model()`, XGBoost names features by array index (`f0`, `f1`, ...) unless a feature map file (`fmap`) is provided. -The `fmap` maps feature indices to named Vespa features: +When using `dump_model()`, XGBoost names features by array index (`f0`, `f1`, ...) unless a feature map file (`fmap`) is provided. The `fmap` maps feature indices to named Vespa features: + -<pre> +```bash $ cat feature-map.txt | egrep "fieldMatch\(title\).completeness|fieldMatch\(title\).importance" 36 fieldMatch(title).completeness q 39 fieldMatch(title).importance q -</pre> +``` -In this example, feature at index 36 maps to -[fieldMatch(title).completeness](../reference/ranking/rank-features.html#fieldMatch(name).completeness) -and index 39 maps to [fieldMatch(title).importance](../reference/ranking/rank-features.html#fieldMatch(name).importance). + +In this example, feature at index 36 maps to [fieldMatch(title).completeness](/en/reference/ranking/rank-features#fieldMatch(name).completeness) and index 39 maps to [fieldMatch(title).importance](/en/reference/ranking/rank-features#fieldMatch(name).importance). Format of `feature-map.txt: <featureid> <featurename> <q or i or int>\n`: - Feature id must be from 0 to number of features, in sorted order @@ -155,16 +161,14 @@ Format of `feature-map.txt: <featureid> <featurename> <q or i or int>\n`: - `q` means this feature is a quantitative value, such as age, time, can be missing - `int` means this feature is an integer value (when int is hinted, the decision boundary will be integer) -When using Pandas `DataFrame`s with column names, the feature names are embedded directly in the JSON dump -and a feature map file is not needed. +When using Pandas `DataFrame`s with column names, the feature names are embedded directly in the JSON dump and a feature map file is not needed. ## Importing XGBoost models -To import an XGBoost model, add the model file to your application package -under the `models` directory. -For UBJ models, also include the corresponding `-features.txt` file: +To import an XGBoost model, add the model file to your application package under the `models` directory. For UBJ models, also include the corresponding `-features.txt` file: + -<pre> +```bash ├── models │ ├── my_model.ubj │ ├── my_model-features.txt @@ -172,17 +176,17 @@ For UBJ models, also include the corresponding `-features.txt` file: ├── schemas │ └── main.sd └── services.xml -</pre> +``` + An application package can have multiple models. ## Ranking with XGBoost models -Vespa has a `xgboost` [ranking feature](../reference/ranking/rank-features.html). -This ranking feature specifies the model to use in a ranking expression. -Both UBJ and JSON models use the same ranking feature: +Vespa has a `xgboost` [ranking feature](/en/reference/ranking/rank-features). This ranking feature specifies the model to use in a ranking expression. Both UBJ and JSON models use the same ranking feature: + -<pre> +```js schema my_app { rank-profile prediction inherits default { first-phase { @@ -193,12 +197,10 @@ schema my_app { } } } -</pre> +``` + -Here, we specify that the model `my_model.ubj` is applied to the top ranking documents -by the first-phase ranking expression. -The query request must specify `prediction` as the [ranking.profile](../reference/api/query.html#ranking.profile). -See also [Phased ranking](phased-ranking.html) on how to control number of data points/documents which is exposed to the model. +Here, we specify that the model `my_model.ubj` is applied to the top ranking documents by the first-phase ranking expression. The query request must specify `prediction` as the [ranking.profile](/en/reference/api/query#ranking.profile). See also [Phased ranking](/en/ranking/phased-ranking) on how to control number of data points/documents which is exposed to the model. Generally the run time complexity is determined by: @@ -206,17 +208,24 @@ Generally the run time complexity is determined by: * The complexity of computing features. For example `fieldMatch` features are 100x more expensive than `nativeFieldMatch/nativeRank`. * The number of XGBoost trees and the maximum depth per tree -Serving latency can be brought down by [using multiple threads per query request](../performance/practical-search-performance-guide.html#multithreaded-search-and-ranking). +Serving latency can be brought down by [using multiple threads per query request](/en/performance/practical-search-performance-guide#multithreaded-search-and-ranking). ## Categorical features -{% include warning.html content="Vespa does **not** support XGBoost's native categorical splits + +<Warning> +**Important:** + +Vespa does **not** support XGBoost's native categorical splits (`enable_categorical=True`). Deploying a model with native categorical splits will **silently produce -wrong predictions** — Vespa interprets the categorical split condition as a numerical threshold." %} +wrong predictions** — Vespa interprets the categorical split condition as a numerical threshold. +</Warning> + To use categorical features with XGBoost models in Vespa, integer-encode them before training: -<pre>{% highlight python %} + +```python import xgboost as xgb import pandas as pd @@ -229,34 +238,29 @@ dtrain = xgb.DMatrix(df[feature_cols], label=targets) param = {"max_depth": 4, "objective": "binary:logistic"} model = xgb.train(param, dtrain, num_boost_round=100) model.save_model("my_model.ubj") -{% endhighlight %}</pre> +``` -In the Vespa schema, store integer-encoded categoricals as `int` attributes -and map them via rank profile functions like any other numerical feature. -Note: Vespa's [LightGBM](lightgbm.html) importer does support native categorical splits. +In the Vespa schema, store integer-encoded categoricals as `int` attributes and map them via rank profile functions like any other numerical feature. + +Note: Vespa's [LightGBM](/en/ranking/lightgbm) importer does support native categorical splits. ## XGBoost objective types -Vespa can import XGBoost models trained with any -[objective](https://xgboost.readthedocs.io/en/stable/parameter.html#learning-task-parameters). -Common objectives include: +Vespa can import XGBoost models trained with any [objective](https://xgboost.readthedocs.io/en/stable/parameter.html#learning-task-parameters). Common objectives include: * Regression `reg:squarederror` / `reg:logistic` * Classification `binary:logistic` * Ranking `rank:pairwise`, `rank:ndcg` and `rank:map` -Vespa evaluates XGBoost models by summing the tree outputs. -The only objective-specific behavior is for logistic objectives (`reg:logistic` and `binary:logistic`), -where the raw tree sum must be passed through a sigmoid function to produce a probability. +Vespa evaluates XGBoost models by summing the tree outputs. The only objective-specific behavior is for logistic objectives (`reg:logistic` and `binary:logistic`), where the raw tree sum must be passed through a sigmoid function to produce a probability. ### UBJ models -For UBJ models, Vespa reads the objective from the model file. -For logistic objectives, the `base_score` is automatically transformed (logit) -so the model output matches XGBoost's predictions without manual adjustment: +For UBJ models, Vespa reads the objective from the model file. For logistic objectives, the `base_score` is automatically transformed (logit) so the model output matches XGBoost's predictions without manual adjustment: + -<pre> +```js schema my_app { rank-profile classify inherits default { first-phase { @@ -264,12 +268,13 @@ schema my_app { } } } -</pre> +``` -Note that UBJ does not automatically apply a sigmoid to the final output. -For logistic objectives, wrap the expression in `sigmoid()` if you need a probability: -<pre> +Note that UBJ does not automatically apply a sigmoid to the final output. For logistic objectives, wrap the expression in `sigmoid()` if you need a probability: + + +```js schema my_app { rank-profile classify inherits default { first-phase { @@ -277,7 +282,8 @@ schema my_app { } } } -</pre> +``` + For ranking objectives and `reg:squarederror`, the raw tree sum can be used directly. @@ -285,15 +291,12 @@ For ranking objectives and `reg:squarederror`, the raw tree sum can be used dire For JSON models exported with `dump_model()`, the objective and `base_score` are **not** preserved. -For `reg:logistic` and `binary:logistic`, the raw margin tree sum -needs to be passed through the [sigmoid function](../reference/ranking/ranking-expressions.html) -to represent the probability of class 1. -For regression, the model can be directly imported -but `base_score` should be set to 0 during training as it is not included in the dump. +For `reg:logistic` and `binary:logistic`, the raw margin tree sum needs to be passed through the [sigmoid function](/en/reference/ranking/ranking-expressions) to represent the probability of class 1. For regression, the model can be directly imported but `base_score` should be set to 0 during training as it is not included in the dump. An example using the sklearn toy datasets: -<pre>{% highlight python %} + +```python from sklearn import datasets import xgboost as xgb breast_cancer = datasets.load_breast_cancer() @@ -301,12 +304,13 @@ c = xgb.XGBClassifier(n_estimators=20, objective="binary:logistic") c.fit(breast_cancer.data, breast_cancer.target) c.get_booster().dump_model("binary_breast_cancer.json", fmap="feature-map.txt", dump_format="json") c.predict_proba(breast_cancer.data)[:, 1] -{% endhighlight %}</pre> +``` + -To represent the `predict_proba` function of XGBoost for the binary classifier in Vespa, -use the [sigmoid function](../reference/ranking/ranking-expressions.html): +To represent the `predict_proba` function of XGBoost for the binary classifier in Vespa, use the [sigmoid function](/en/reference/ranking/ranking-expressions): -<pre> + +```js schema my_app { rank-profile prediction-binary inherits default { first-phase { @@ -314,12 +318,13 @@ schema my_app { } } } -</pre> +``` + -When the `base_score` is not the default (0.5), the sigmoid alone is insufficient. -The full formula accounting for `base_score` is: +When the `base_score` is not the default (0.5), the sigmoid alone is insufficient. The full formula accounting for `base_score` is: -<pre> + +```js schema my_app { rank-profile prediction-binary inherits default { constants { @@ -330,22 +335,13 @@ schema my_app { } } } -</pre> +``` + -Replace `0.5` with the actual `base_score` used during training. -See the [XGBoost System Test](https://github.com/vespa-engine/system-test/tree/master/tests/search/xgboost) for a complete working example. +Replace `0.5` with the actual `base_score` used during training. See the [XGBoost System Test](https://github.com/vespa-engine/system-test/tree/master/tests/search/xgboost) for a complete working example. ## Debugging Vespa inference score versus XGBoost predict score -* For JSON models, the `base_score` and optimal number of trees (if trained with early stopping) are lost in the dump. - UBJ models preserve this information. - XGBoost also has different predict functions (e.g. predict/predict_proba). - The following [XGBoost System Test](https://github.com/vespa-engine/system-test/tree/master/tests/search/xgboost) - demonstrates how to represent different types of XGBoost models in Vespa. -* For training, features should be scraped from Vespa, using either `match-features` or `summary-features` so - that features from offline training matches the online Vespa computed features. - Dumping features can also help debug any differences by zooming into specific query,document pairs - using [recall](../reference/api/query.html#recall) parameter. -* In a distributed setting when multiple nodes use the model, text matching features such as `nativeRank`, `nativeFieldMatch`, `bm25` and `fieldMatch` - might differ, depending on which node produced the hit. The reason is that all these features use [term(n).significance](../reference/ranking/rank-features.html#query-features), which is computed from the locally indexed corpus. The `term(n).significance` feature - is related to *Inverse Document Frequency (IDF)*. The `term(n).significance` should be set by a searcher in the container for global correctness as each node will estimate the significance values from the local corpus. +* For JSON models, the `base_score` and optimal number of trees (if trained with early stopping) are lost in the dump. UBJ models preserve this information. XGBoost also has different predict functions (e.g. predict/predict_proba). The following [XGBoost System Test](https://github.com/vespa-engine/system-test/tree/master/tests/search/xgboost) demonstrates how to represent different types of XGBoost models in Vespa. +* For training, features should be scraped from Vespa, using either `match-features` or `summary-features` so that features from offline training matches the online Vespa computed features. Dumping features can also help debug any differences by zooming into specific query,document pairs using [recall](/en/reference/api/query#recall) parameter. +* In a distributed setting when multiple nodes use the model, text matching features such as `nativeRank`, `nativeFieldMatch`, `bm25` and `fieldMatch` might differ, depending on which node produced the hit. The reason is that all these features use [term(n).significance](/en/reference/ranking/rank-features#query-features), which is computed from the locally indexed corpus. The `term(n).significance` feature is related to *Inverse Document Frequency (IDF)*. The `term(n).significance` should be set by a searcher in the container for global correctness as each node will estimate the significance values from the local corpus. diff --git a/en/reference/api/api.html b/en/reference/api/api.html deleted file mode 100644 index 6e3afdf40c..0000000000 --- a/en/reference/api/api.html +++ /dev/null @@ -1,76 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Vespa API and interfaces" -redirect_from: -- /en/api.html ---- - -<h2 id="deployment-and-configuration">Deployment and configuration</h2> -<ul> - <li><a href="deploy-v2.html">Deploy API</a>: - Deploy <a href="../../basics/applications.html">application packages</a> - to configure a Vespa application</li> - <li><a href="config-v2.html">Config API</a>: - Get and Set configuration</li> - <li><a href="application-v2.html">Tenant API</a>: - Configure multiple tenants in the config servers</li> -</ul> - - -<h2 id="document-api">Document API</h2> -<ul> - <li><a href="../../writing/reads-and-writes.html">Reads and writes</a>: - APIs and binaries to read and update documents</li> - <li><a href="document-v1.html">/document/v1/</a>: - REST API for operations based on document ID (get, put, remove, update)</li> - <li><a href="../../clients/vespa-feed-client.html">Feeding API</a>: - High performance feeding API, the recommended API for feeding data</li> - <li><a href="../schemas/document-json-format.html">JSON feed format</a>: - The Vespa Document format</li> - <li><a href="../../writing/document-api-guide.html">Vespa Java Document API</a></li> -</ul> - - -<h2 id="query-and-grouping">Query and grouping</h2> -<ul> - <li><a href="../../querying/query-api.html">Query API</a>, - <a href="query.html">Query API reference</a></li> - <li><a href="../../querying/query-language.html">Query Language</a>, - <a href="../querying/yql.html">Query Language reference</a>, - <a href="../querying/simple-query-language.html">Simple Query Language reference</a>, - <a href="../../schemas/predicate-fields.html">Predicate fields</a></li> - <li><a href="../../querying/query-profiles.html">Vespa Query Profiles </a></li> - <li><a href="../../querying/grouping.html">Grouping API</a>, - <a href="../querying/grouping-language.html">Grouping API reference</a></li> -</ul> - - -<h2 id="processing">Processing</h2> -<ul> - <li><a href="../../applications/processing.html">Vespa Processing</a>: - Request-Response processing</li> - <li><a href="../../applications/document-processors.html">Vespa Document Processing</a>: - Feed processing</li> -</ul> - - -<h2 id="request-processing">Request processing</h2> -<ul> - <li><a href="../../applications/searchers.html">Searcher API</a></li> - <li><a href="../../querying/federation.html">Federation API</a></li> - <li><a href="../../applications/web-services.html">Web service API</a></li> -</ul> - - -<h2 id="result-processing">Result processing</h2> -<ul> - <li><a href="../../applications/result-renderers.html">Custom renderer API</a></li> -</ul> - - - -<h2 id="status">Status and state</h2> -<ul> - <li><a href="../../operations/metrics.html">Health and Metric APIs</a></li> - <li><a href="cluster-v2.html">/cluster/v2 API</a></li> -</ul> diff --git a/en/reference/api/api.mdx b/en/reference/api/api.mdx new file mode 100644 index 0000000000..0b9c053b01 --- /dev/null +++ b/en/reference/api/api.mdx @@ -0,0 +1,60 @@ +--- +title: "Vespa API and interfaces" +description: "Vespa API and interfaces in Vespa applications." +--- + +## Deployment and configuration + +- [Deploy API](/en/reference/api/deploy-v2): +Deploy [application packages](/en/basics/applications) +to configure a Vespa application +- [Config API](/en/reference/api/config-v2): +Get and Set configuration +- [Tenant API](/en/reference/api/application-v2): +Configure multiple tenants in the config servers + +## Document API + +- [Reads and writes](/en/writing/reads-and-writes): +APIs and binaries to read and update documents +- [/document/v1/](/en/reference/api/document-v1): +REST API for operations based on document ID (get, put, remove, update) +- [Feeding API](/en/clients/vespa-feed-client): +High performance feeding API, the recommended API for feeding data +- [JSON feed format](/en/reference/schemas/document-json-format): +The Vespa Document format +- [Vespa Java Document API](/en/writing/document-api-guide) + +## Query and grouping + +- [Query API](/en/querying/query-api), +[Query API reference](/en/reference/api/query) +- [Query Language](/en/querying/query-language), +[Query Language reference](/en/reference/querying/yql), +[Simple Query Language reference](/en/reference/querying/simple-query-language), +[Predicate fields](/en/schemas/predicate-fields) +- [Vespa Query Profiles](/en/querying/query-profiles) +- [Grouping API](/en/querying/grouping), +[Grouping API reference](/en/reference/querying/grouping-language) + +## Processing + +- [Vespa Processing](/en/applications/processing): +Request-Response processing +- [Vespa Document Processing](/en/applications/document-processors): +Feed processing + +## Request processing + +- [Searcher API](/en/applications/searchers) +- [Federation API](/en/querying/federation) +- [Web service API](/en/applications/web-services) + +## Result processing + +- [Custom renderer API](/en/applications/result-renderers) + +## Status and state + +- [Health and Metric APIs](/en/operations/metrics) +- [/cluster/v2 API](/en/reference/api/cluster-v2) diff --git a/en/reference/api/application-v2.html b/en/reference/api/application-v2.html deleted file mode 100644 index c223d2ea01..0000000000 --- a/en/reference/api/application-v2.html +++ /dev/null @@ -1,185 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "/application/v2/tenant API reference" -redirect_from: -- /en/cloudconfig/tenant-rest-api.html -- /en/reference/tenant-rest-api.html -- /en/reference/application-v2-tenant.html ---- - -<p> - This is the /application/v2/tenant API reference with examples for the HTTP REST API to - <a href="#list-tenants">list</a>, - <a href="#create-tenant">create</a> and - <a href="#delete-tenant">delete</a> a tenant, - which can be used to <a href="deploy-v2.html">deploy</a> an application. -</p> -<p> - The response format is JSON. The tenant value is "default". -</p> -<p> - The current API version is 2. The API port is 19071 - use - <a href="/en/reference/operations/self-managed/tools.html#vespa-model-inspect">vespa-model-inspect</a> service configserver - to find config server hosts - example: - <code>http://myconfigserver.mydomain.com:19071/application/v2/tenant/</code> -</p> - - - -<h2 id="http-requests">HTTP requests</h2> -<table class="table"> - <thead> - <tr> - <th>HTTP request</th> - <th>application/v2/tenant operation</th> - <th>Description</th> - </tr> - </thead> - <tbody> - <tr> - <th>GET</th> - <td colspan="2"> - <p id="get"> - List tenant information. - </p> - </td> - </tr> - <tr> - <td></td> - <th>List tenants</th> - <td> - <p id="list-tenants"></p> - <pre>/application/v2/tenant/</pre> - <p>Example response:</p> -<pre>{% highlight json %} -[ - "default" -] -{% endhighlight %}</pre> - </td> - </tr> - <tr> - <td></td> - <th>Get tenant</th> - <td> - <p id="get-tenant"></p> - <pre>/application/v2/tenant/default</pre> - <p>Example response:</p> -<pre>{% highlight json %} -{ - "message": "Tenant 'default' exists." -} -{% endhighlight %}</pre> - </td> - </tr> - <tr> - <th>PUT</th> - <td colspan="2"> - <p id="put"> - Create a new tenant. - </p> - </td> - </tr> - <tr> - <td></td> - <th>Create tenant</th> - <td> - <p id="create-tenant"></p> - <pre>/application/v2/tenant/default</pre> - <p> - Response: A message with the name of the tenant created - example: - </p> -<pre>{% highlight json %} -{ - "message" : "Tenant default created." -} -{% endhighlight %}</pre> - {% include note.html content="This operation is asynchronous, - it will eventually propagate to all config servers." %} - </td> - </tr> - <tr> - <th>DELETE</th> - <td colspan="2"> - <p id="delete"> - Delete a tenant. - </p> - </td> - </tr> - <tr> - <td></td> - <th>Delete tenant</th> - <td> - <p id="delete-tenant"></p> - <pre>/application/v2/tenant/default</pre> - <p> - Response: A message with the deleted tenant: - </p> -<pre>{% highlight json %} -{ - "message" : "Tenant default deleted." -} -{% endhighlight %}</pre> - {% include note.html content="This operation is asynchronous, - it will eventually propagate to all config servers." %} - </td> - </tr> - </tbody> -</table> - - - -<h2 id="request-parameters">Request parameters</h2> -<p> - None. -</p> - - - -<h2 id="http-status-codes">HTTP status codes</h2> -<p> - Non-exhaustive list of status codes. - Any additional info is included in the body of the return call, JSON-formatted. -</p> -<table class="table"> - <thead> - <tr> - <th>Code</th><th>Description</th> - </tr> - </thead><tbody> - <tr> - <td>400</td> - <td>Bad request. Client error. The error message should indicate the cause.</td> - </tr><tr> - <td>404</td> - <td>Not found. For example using a session id that does not exist.</td> - </tr><tr> - <td>405</td> - <td>Method not implemented. E.g. using GET where only POST or PUT is allowed.</td> - </tr><tr> - <td>500</td> - <td>Internal server error. Generic error. The error message should indicate the cause.</td> - </tr> - </tbody> -</table> - - - -<h2 id="response-format">Response format</h2> -<p> - Responses are in JSON format, with the following fields: -</p> -<table class="table"> - <thead> - <tr> - <th>Field</th> - <th>Description</th> - </tr> - </thead> - <tbody> - <tr> - <th>message</th> - <td>An info/error message.</td> - </tr> - </tbody> -</table> diff --git a/en/reference/api/application-v2.mdx b/en/reference/api/application-v2.mdx new file mode 100644 index 0000000000..052a23847b --- /dev/null +++ b/en/reference/api/application-v2.mdx @@ -0,0 +1,51 @@ +--- +title: "/application/v2/tenant API reference" +description: "/application/v2/tenant API reference in Vespa applications." +--- +<table className="vespa-table"> + <thead> + <tr> + <th>HTTP request</th> + <th>application/v2/tenant operation</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>GET</td> + <td>List tenant information.</td> + <td></td> + </tr> + <tr> + <td></td> + <td>List tenants</td> + <td><code>{`/application/v2/tenant/`}</code> Example response: <code>{`[`}</code> <code>{`"default"`}</code> <code>{`]`}</code></td> + </tr> + <tr> + <td></td> + <td>Get tenant</td> + <td><code>{`/application/v2/tenant/default`}</code> Example response: <code>{`{`}</code> <code>{`"message": "Tenant 'default' exists."`}</code> <code>{`}`}</code></td> + </tr> + <tr> + <td>PUT</td> + <td>Create a new tenant.</td> + <td></td> + </tr> + <tr> + <td></td> + <td>Create tenant</td> + <td><code>{`/application/v2/tenant/default`}</code> Response: A message with the name of the tenant created - example: <code>{`{`}</code> <code>{`"message" : "Tenant default created."`}</code> <code>{`}`}</code> **Note:** This operation is asynchronous, it will eventually propagate to all config servers.</td> + </tr> + <tr> + <td>DELETE</td> + <td>Delete a tenant.</td> + <td></td> + </tr> + <tr> + <td></td> + <td>Delete tenant</td> + <td><code>{`/application/v2/tenant/default`}</code> Response: A message with the deleted tenant: <code>{`{`}</code> <code>{`"message" : "Tenant default deleted."`}</code> <code>{`}`}</code> **Note:** This operation is asynchronous, it will eventually propagate to all config servers.</td> + </tr> + </tbody> +</table> + diff --git a/en/reference/api/cluster-v2.html b/en/reference/api/cluster-v2.html deleted file mode 100644 index cfb2a7bd02..0000000000 --- a/en/reference/api/cluster-v2.html +++ /dev/null @@ -1,407 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "/cluster/v2 API reference" -redirect_from: -- /en/content/api-state-rest-api.html -- /en/reference/cluster-v2.html ---- - -<p> - The cluster controller has a /cluster/v2 API for viewing and modifying a content cluster state. - To find the URL to access this API, identify the <a href="../../content/content-nodes.html#cluster-controller"> - cluster controller services</a> in the application. - Only the master cluster controller will be able to respond. - The master cluster controller is the cluster controller alive that has the lowest index. - Thus, one will typically use cluster controller 0, but if contacting it fails, try number 1 and so on. - Using <a href="/en/reference/operations/self-managed/tools.html#vespa-model-inspect">vespa-model-inspect</a>: -</p> -<pre> -$ vespa-model-inspect service -u container-clustercontroller - -container-clustercontroller @ hostname.domain.com : admin -admin/cluster-controllers/0 - http://hostname.domain.com:19050/ (STATE EXTERNAL QUERY HTTP) - http://hostname.domain.com:19117/ (EXTERNAL HTTP) - tcp/hostname.domain.com:19118 (MESSAGING RPC) - tcp/hostname.domain.com:19119 (ADMIN RPC) -</pre> -<p> - In this example, there is only one clustercontroller, and the State Rest API is - available on the port marked STATE and HTTP, 19050 in this example. - This information can also be retrieved through the model config in the config server. -</p> -<p> - Find examples of API usage in <a href="../../content/content-nodes.html#cluster-v2-API-examples">content nodes</a>. -</p> - - - -<h2 id="http-requests">HTTP requests</h2> -<table class="table"> - <thead> - <tr> - <th>HTTP request</th> - <th>cluster/v2 operation</th> - <th>Description</th> - </tr> - </thead> - <tbody> - <tr> - <th>GET</th> - <td colspan="2"> - <p id="get"> - List cluster and nodes. - Get cluster, node or disk states. - </p> - </td> - </tr> - <tr> - <td></td> - <th id="list-content-clusters">List content clusters</th> - <td> - <pre>/cluster/v2/</pre> - </td> - </tr> - <tr> - <td></td> - <th id="get-cluster-state-and-list-service-types-within-cluster"> - Get cluster state and list service types within cluster</th> - <td> - <pre>/cluster/v2/<cluster></pre> - </td> - </tr> - <tr> - <td></td> - <th id="list-nodes-per-service-type-for-cluster">List nodes per service type for cluster</th> - <td> - <pre>/cluster/v2/<cluster>/<service-type></pre> - </td> - </tr> - <tr> - <td></td> - <th id="get-node-state">Get node state</th> - <td> - <pre>/cluster/v2/<cluster>/<service-type>/<node></pre> - </td> - </tr> - <tr> - <th>PUT</th> - <td colspan="2"> - <p id="put"> - Set node state - </p> - </td> - </tr> - <tr> - <td></td> - <th id="set-node-user-state">Set node user state</th> - <td> - <pre>/cluster/v2/<cluster>/<service-type>/<node></pre> - </td> - </tr> - </tbody> -</table> - - -<h2 id="node-state">Node state</h2> -<p> - Content and distributor nodes have state: -</p> -<table class="table"> - <thead> - <tr> - <th>State</th> - <th>Description</th> - </tr> - </thead> - <tbody> - <tr> - <td><p id="up"><code>Up</code></p></td> - <td>The node is up and available to keep buckets and serve requests.</td> - </tr><tr> - <td><p id="down"><code>Down</code></p></td> - <td>The node is not available, and can not be used.</td> - </tr><tr> - <td><p id="stopping"><code>Stopping</code></p></td> - <td> - This node is stopping and is expected to be down soon. - This state is typically only exposed to the cluster controller - to tell why the node stopped. - The cluster controller will expose the node as down - or in maintenance mode for the rest of the cluster. - This state is thus not seen by the distribution algorithm. - </td> - </tr><tr> - <td><p id="maintenance"><code>Maintenance</code></p></td> - <td> - This node is temporarily unavailable. - The node is available for bucket placement, so redundancy is lower. <!-- ToDo rewrite --> - Using this mode, new replicas of the documents stored on this node will not be created, - allowing the node to be down with less of a performance impact on the rest of the cluster. - This mode is typically used to mask a down state during controlled node restarts, - or by an administrator that need to do some short maintenance work, - like upgrading software or restart the node. - </td> - </tr><tr> - <td><p id="retired"><code>Retired</code></p></td> - <td> - A retired node is available and serves requests. - This state is used to remove nodes while keeping redundancy. - Buckets are moved to other nodes (with low priority), until empty. - Special considerations apply when using - <a href="../../content/elasticity.html#grouped-distribution">grouped distribution</a> - as buckets are not necessarily removed. - </td> - </tr> - </tbody> -</table> -<p> - Distributor nodes start / transfer buckets quickly - and are hence not in <code>maintenance</code> or <code>retired</code>. - <!-- ToDo: it should not be possible to set a distributor in this mode, check --> -</p> -<p> - Refer to <a href="../../content/content-nodes.html#cluster-v2-API-examples">examples</a> of manipulating states. -</p> - - - -<h2 id="types">Types</h2> -<table class="table"> -<thead> -<tr> - <th>Type</th> - <th>Spec</th> - <th>Description</th> -</tr> -</thead><tbody> -<tr> - <td><p id="cluster">cluster</p></td> - <td><em><identifier></em></td> - <td> - The name given to a content cluster in a Vespa application. - </td> -</tr> -<tr> - <td><p id="description">description</p></td> - <td><em>.*</em></td> - <td> - Description can contain anything that is valid JSON. However, as the - information is presented in various interfaces, some which may present reasons - for all the states in a cluster or similar, keeping it short and to the - point makes it easier to fit the information neatly into a table and get a - better cluster overview. - </td> -</tr> -<tr> - <td><p id="group-spec">group-spec</p></td> - <td><em><identifier></em>(\.<em><identifier></em>)*</td> - <td> - The hierarchical group assignment of a given content node. - This is a dot separated list of identifiers given in the application services.xml configuration. - </td> -</tr> -<tr> - <td><p id="node">node</p></td> - <td>[0-9]+</td> - <td> - The index or distribution key identifying a given node within the - context of a content cluster and a service type. - </td> -</tr> -<tr> - <td><p id="service-type">service-type</p></td> - <td>(distributor|storage)</td> - <td> - The type of the service to look at state for, within the context of a given content cluster. - </td> -</tr> -<tr> - <td><p id="state-disk">state-disk</p></td> - <td>(up|down)</td> - <td> - One of the valid disk states. - </td> <!-- ToDo: Deprecate disk state? --> -</tr> -<tr> - <td><p id="state-unit">state-unit</p></td> - <td> - <a href="#up">up</a> | <a href="#stopping">stopping</a> | <a href="#down">down</a> - </td> - <td> - <p> - The cluster controller fetches states from all nodes, called <em>unit states</em>. - States reported from the nodes are either <code>up</code> or <code>stopping</code>. - If the node can not be reached, a <code>down</code> state is assumed. - </p> - <p> - This means, the cluster controller detects failed nodes. - The subsequent <em>generated states</em> will have nodes in <code>down</code>, - and the <a href="../../content/idealstate.html">ideal state algorithm</a> will redistribute - <a href="../../content/buckets.html">buckets</a> of documents. - </p> - </td> -</tr> -<tr> - <td><p id="state-user">state-user</p></td> - <td> - <a href="#up">up</a> | <a href="#down">down</a> | - <a href="#maintenance">maintenance</a> | <a href="#retired">retired</a> - </td> - <td> - <p> - Use tools for <a href="/en/operations/self-managed/admin-procedures.html#cluster-state">user state management</a>. - </p> - <ul> - <li>Retire a node from a cluster - - use <code>retired</code> to move buckets to other nodes</li> - <li>Short-lived maintenance work - - use <code>maintenance</code> to avoid merging buckets to other nodes</li> - <li>Fail a bad node. The cluster controller or an operator can set a node <code>down</code></li> - </ul> - </td> -</tr> -<tr> - <td><p id="state-generated">state-generated</p></td> - <td> - <a href="#up">up</a> | <a href="#down">down</a> | - <a href="#maintenance">maintenance</a> | <a href="#retired">retired</a> - </td> - <td> - <p> - The cluster controller generates the cluster state - from the <code>unit</code> and <code>user</code> states, over time. - The generated state is called the <em>cluster state</em>. - </p> - </td> -</tr> -</tbody> -</table> - - - -<h2 id="request-parameters">Request parameters</h2> -<table class="table"> - <thead> - <tr> - <th>Parameter</th> - <th>Type</th> - <th>Description</th> - </tr> - </thead><tbody> -<tr> - <td>recursive</td> - <td>number</td> - <td> - <p id="recursive"> - <p> - Number of levels, or <code>true</code> for all levels. Examples: - </p> - <ul> - <li>Use <code>recursive=1</code> for a node request to also see all data</li> - <li>use <code>recursive=2</code> to see all the node data within each service type</li> - </ul> - <p> - In recursive mode, you will see the same output as found in the spec below. - However, where there is a <code>{ "link" : "<url-path>" }</code> element, - this element will be replaced by the content of that request, - given a recursive value of one less than the request above. - </p> - </td> -</tr> -</tbody> -</table> - - - -<h2 id="http-status-codes">HTTP status codes</h2> -<p> - Non-exhaustive list of status codes: -</p> -<table class="table"> - <thead> - <tr> - <th>Code</th> - <th>Description</th> - </tr> - </thead> - <tbody> - <tr> - <td>200</td> - <td>OK.</td> - </tr> - <tr> - <td>303</td> - <td> - <p>Cluster controller not master - master known.</p> - <p> - This error means communicating with the wrong cluster controller. - This returns a standard HTTP redirect, - so the HTTP client can automatically redo the request on the correct cluster controller. - </p> - <p> - As the cluster controller available with the lowest index will be the master, - the cluster controllers are normally queried in index order. - Hence, it is unlikely to ever get this error, - but rather fail to connect to the cluster controller if it is not the current master. - </p> -<pre> -HTTP/1.1 303 See Other -Location: http://<span class="pre-hilite"><master></span>/<span class="pre-hilite"><current-request></span> -Content-Type: application/json - -{ - "message" : "Cluster controller <span class="pre-hilite">index</span> not master. Use master at index <span class="pre-hilite">index</span>. -} -</pre> - </td> - </tr> - <tr> - <td>503</td> - <td> - <p>Cluster controller not master - unknown or no master.</p> - <p> - This error is used if the cluster controller asked is not master, - and it doesn't know who the master is. - This can happen, e.g. in a network split, - where cluster controller 0 no longer can reach cluster controller 1 and 2, - in which case cluster controller 0 knows it is not master, as it can't see the majority, - and cluster controller 1 and 2 will vote 1 to master. - </p> -<pre> -HTTP/1.1 503 Service Unavailable -Content-Type: application/json - -{ - "message" : "No known master cluster controller currently exist." -} -</pre> - </td> - </tr> - </tbody> -</table> - - - -<h2 id="response-format">Response format</h2> -<p> - Responses are in JSON format, with the following fields: -</p> -<table class="table"> - <thead> - <tr> - <th>Field</th> - <th>Description</th> - </tr> - </thead> - <tbody> - <tr> - <th>message</th> - <td>An error message — included for failed requests.</td> - </tr> - <tr> - <th>ToDo</th> - <td>Add more fields here.</td> - </tr> - </tbody> -</table> diff --git a/en/reference/api/cluster-v2.mdx b/en/reference/api/cluster-v2.mdx new file mode 100644 index 0000000000..8831c326fa --- /dev/null +++ b/en/reference/api/cluster-v2.mdx @@ -0,0 +1,156 @@ +--- +title: "/cluster/v2 API reference" +description: "/cluster/v2 API reference in Vespa applications." +--- +<table className="vespa-table"> + <thead> + <tr> + <th>HTTP request</th> + <th>cluster/v2 operation</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>GET</td> + <td>List cluster and nodes. Get cluster, node or disk states.</td> + <td></td> + </tr> + <tr> + <td></td> + <td>List content clusters</td> + <td><code>{`/cluster/v2/`}</code></td> + </tr> + <tr> + <td></td> + <td>Get cluster state and list service types within cluster</td> + <td><code>{`/cluster/v2/<cluster>`}</code></td> + </tr> + <tr> + <td></td> + <td>List nodes per service type for cluster</td> + <td><code>{`/cluster/v2/<cluster>/<service-type>`}</code></td> + </tr> + <tr> + <td></td> + <td>Get node state</td> + <td><code>{`/cluster/v2/<cluster>/<service-type>/<node>`}</code></td> + </tr> + <tr> + <td>PUT</td> + <td>Set node state</td> + <td></td> + </tr> + <tr> + <td></td> + <td>Set node user state</td> + <td><code>{`/cluster/v2/<cluster>/<service-type>/<node>`}</code></td> + </tr> + </tbody> +</table> + +<table className="vespa-table"> + <thead> + <tr> + <th>State</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td><code>{`Up`}</code></td> + <td>The node is up and available to keep buckets and serve requests.</td> + </tr> + <tr> + <td><code>{`Down`}</code></td> + <td>The node is not available, and can not be used.</td> + </tr> + <tr> + <td><code>{`Stopping`}</code></td> + <td>This node is stopping and is expected to be down soon. This state is typically only exposed to the cluster controller to tell why the node stopped. The cluster controller will expose the node as down or in maintenance mode for the rest of the cluster. This state is thus not seen by the distribution algorithm.</td> + </tr> + <tr> + <td><code>{`Maintenance`}</code></td> + <td>This node is temporarily unavailable. The node is available for bucket placement, so redundancy is lower. Using this mode, new replicas of the documents stored on this node will not be created, allowing the node to be down with less of a performance impact on the rest of the cluster. This mode is typically used to mask a down state during controlled node restarts, or by an administrator that need to do some short maintenance work, like upgrading software or restart the node.</td> + </tr> + <tr> + <td><code>{`Retired`}</code></td> + <td>A retired node is available and serves requests. This state is used to remove nodes while keeping redundancy. Buckets are moved to other nodes (with low priority), until empty. Special considerations apply when using <a href="/en/content/elasticity#grouped-distribution" aria-label="grouped distribution">grouped distribution</a> as buckets are not necessarily removed.</td> + </tr> + </tbody> +</table> + +<table className="vespa-table"> + <thead> + <tr> + <th>Type</th> + <th>Spec</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>cluster</td> + <td>*<code>{`<identifier>`}</code>*</td> + <td>The name given to a content cluster in a Vespa application.</td> + </tr> + <tr> + <td>description</td> + <td>*.**</td> + <td>Description can contain anything that is valid JSON. However, as the information is presented in various interfaces, some which may present reasons for all the states in a cluster or similar, keeping it short and to the point makes it easier to fit the information neatly into a table and get a better cluster overview.</td> + </tr> + <tr> + <td>group-spec</td> + <td>*<code>{`<identifier>`}</code>* (. *<code>{`<identifier>`}</code>* )*</td> + <td>The hierarchical group assignment of a given content node. This is a dot separated list of identifiers given in the application services.xml configuration.</td> + </tr> + <tr> + <td>node</td> + <td>[0-9]+</td> + <td>The index or distribution key identifying a given node within the context of a content cluster and a service type.</td> + </tr> + <tr> + <td>service-type</td> + <td>(distributor|storage)</td> + <td>The type of the service to look at state for, within the context of a given content cluster.</td> + </tr> + <tr> + <td>state-disk</td> + <td>(up|down)</td> + <td>One of the valid disk states.</td> + </tr> + <tr> + <td>state-unit</td> + <td><a href="#up" aria-label="up">up</a> | <a href="#stopping" aria-label="stopping">stopping</a> | <a href="#down" aria-label="down">down</a></td> + <td>The cluster controller fetches states from all nodes, called *unit states*. States reported from the nodes are either <code>{`up`}</code> or <code>{`stopping`}</code>. If the node can not be reached, a <code>{`down`}</code> state is assumed. This means, the cluster controller detects failed nodes. The subsequent *generated states* will have nodes in <code>{`down`}</code>, and the <a href="/en/content/idealstate" aria-label="ideal state algorithm">ideal state algorithm</a> will redistribute <a href="/en/content/buckets" aria-label="buckets">buckets</a> of documents.</td> + </tr> + <tr> + <td>state-user</td> + <td><a href="#up" aria-label="up">up</a> | <a href="#down" aria-label="down">down</a> | <a href="#maintenance" aria-label="maintenance">maintenance</a> | <a href="#retired" aria-label="retired">retired</a></td> + <td>Use tools for <a href="/en/operations/self-managed/admin-procedures#cluster-state" aria-label="user state management">user state management</a>. Retire a node from a cluster - use <code>{`retired`}</code> to move buckets to other nodes Short-lived maintenance work - use <code>{`maintenance`}</code> to avoid merging buckets to other nodes Fail a bad node. The cluster controller or an operator can set a node <code>{`down`}</code></td> + </tr> + <tr> + <td>state-generated</td> + <td><a href="#up" aria-label="up">up</a> | <a href="#down" aria-label="down">down</a> | <a href="#maintenance" aria-label="maintenance">maintenance</a> | <a href="#retired" aria-label="retired">retired</a></td> + <td>The cluster controller generates the cluster state from the <code>{`unit`}</code> and <code>{`user`}</code> states, over time. The generated state is called the *cluster state*.</td> + </tr> + </tbody> +</table> + +<table className="vespa-table"> + <thead> + <tr> + <th>Parameter</th> + <th>Type</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>recursive</td> + <td>number</td> + <td>Number of levels, or <code>{`true`}</code> for all levels. Examples: Use <code>{`recursive=1`}</code> for a node request to also see all datause <code>{`recursive=2`}</code> to see all the node data within each service type In recursive mode, you will see the same output as found in the spec below. However, where there is a <code>{`{ "link" : "<url-path>" }`}</code> element, this element will be replaced by the content of that request, given a recursive value of one less than the request above.</td> + </tr> + </tbody> +</table> + diff --git a/en/reference/api/config-v2.html b/en/reference/api/config-v2.html deleted file mode 100644 index 74de26667d..0000000000 --- a/en/reference/api/config-v2.html +++ /dev/null @@ -1,355 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Config API" -redirect_from: -- /en/cloudconfig/config-rest-api-v2.html -- /en/reference/config-rest-api-v2.html ---- - -<p> - Vespa provides a REST API for listing and retrieving config - - alternatives are the <a href="../../applications/configapi-dev.html#the-java-config-api">programmatic Java API</a>. - The Config API provides a way to inspect and retrieve all the config - that can be generated by the config model for a given - <a href="deploy-v2.html">tenant's active application</a>. - Some, but not necessarily all, of those configs are used by services by - <a href="../../applications/configapi-dev.html">subscribing</a> to them. -</p> -<p> - The response format is JSON. - The current API version is 2. All config servers provide the REST API. - The API port is 19071 - use - <a href="../operations/self-managed/tools.html#vespa-model-inspect">vespa-model-inspect</a> service configserver - to find config server hosts. - Example: <code>http://myconfigserver.mydomain.com:19071/config/v2/tenant/msbe/application/articlesearch/</code> -</p> -<p> - The API is available after an application has been - <a href="../../basics/applications.html#deploying-applications">deployed and activated</a>. -</p> - - - -<h2 id="application-id">The application id</h2> -<p> - The API provides two ways to identify your application, given a tenant: - one using only an application name, and one using application name, environment, region and instance. - For the former, "short" form, a default environment, region and instance is used. -</p> -<p> - More formally, an <em>application id</em> is a tuple of the form - (<em>application</em>, <em>environment</em>, <em>region</em>, <em>instance</em>). - The system currently provides shorthand to the id (<em>application</em>, "default", "default", "default"). -</p> -<p> - Note: Multiple environments, regions and instances are not currently supported for application deployments, - <em>default</em> is always used. -</p> -<p> - Example URL using only application name: - <code>http://myconfigserver.mydomain.com:19071/config/v2/tenant/media/application/articlesearch/media.config.server-list/clusters/0</code> -</p> -<table class="table"> -<thead> - <tr> - <th>Part</th><th>Description</th> - </tr> -</thead><tbody> - <tr> - <td>media</td><td>Tenant</td> - </tr><tr> - <td>articlesearch</td><td>Application</td> - </tr><tr> - <td>media.config</td><td>Namespace of the requested config</td> - </tr><tr> - <td>server-list</td><td>Name of the requested config</td> - </tr><tr> - <td>clusters/0</td><td>Config id of the requested config</td> - </tr> - </tbody> -</table> -<p> -Example URL using full application id: -<code>http://myconfigserver.mydomain.com:19071/config/v2/tenant/media/application/articlesearch/environment/test/region/us/instance/staging/media.config.server-list/clusters/0</code> -</p> -<table class="table"> -<thead> - <tr> - <th>Part</th><th>Description</th> - </tr> -</thead><tbody> - <tr> - <td>media</td><td>Tenant</td> - </tr><tr> - <td>articlesearch</td><td>Name of the application</td> - </tr><tr> - <td>test</td><td>Environment</td> - </tr><tr> - <td>us</td><td>Region</td> - </tr><tr> - <td>staging</td><td>Instance</td> - </tr><tr> - <td>media.config</td><td>Namespace of the requested config</td> - </tr><tr> - <td>server-list</td><td>Name of the requested config</td> - </tr><tr> - <td>clusters/0</td><td>Config id of the requested config</td> - </tr> - </tbody> -</table> -<p> -In this API specification, the short form of the application id, -i.e. only the application name, is used. The tenant <code>mytenant</code> and the -application name <code>myapplication</code> is used throughout in examples. -</p> - - - -<h2 id="list-configs">GET /config/v2/tenant/mytenant/application/myapplication/</h2> -<p> - List the configs in the model, - as <a href="../../applications/configapi-dev.html#config-id">config id</a> specific URLs. -</p> -<table class="table"> - <thead> - </thead><tbody> - <tr><th>Parameters</th> - <td> - <p id="list-configs-params"></p> - <table class="table"> - <thead> - <tr> - <th>Parameter</th><th>Default</th><th>Description</th> - </tr> - </thead><tbody> - <tr> - <td>recursive</td> - <td>false</td> - <td>If true, include each config id in the model which produces - the config, and list only the links to the config payload. - If false, include the first level of the config ids in the listing of new list URLs, as explained above.</td> - </tr> - </tbody> - </table> - </td> - </tr> - <tr><th>Request body</th> - <td> - None - </td> - </tr> - <tr><th>Response</th> - <td> - <p id="list-configs-response"> - A list response includes two arrays: - </p> - <ul> - <li>List-links to descend one level down in the config id hierarchy, - named <code>children</code>.</li> - <li><a href="#payload">Config payload</a> links for the current (top) level, - named <code>configs</code>.</li> - </ul> - </td> - </tr> - <tr><th>Error Response</th> - <td> - <p id="list-configs-error"> - N/A - </p> - </td> - </tr> - </tbody> -</table> -<p>Examples:</p> -<p><code>GET /config/v2/tenant/mytenant/application/myapplication/</code></p> -<pre>{% highlight json %} -{ - "children": [ - "http://myconfigserver.mydomain.com:19071/config/v2/tenant/mytenant/application/myapplication/config.sentinel/myconfigserver.mydomain.com/", - "http://myconfigserver.mydomain.com:19071/config/v2/tenant/mytenant/application/myapplication/config.sentinel/hosts/", - "http://myconfigserver.mydomain.com:19071/config/v2/tenant/mytenant/application/myapplication/config.model/admin/", - "http://myconfigserver.mydomain.com:19071/config/v2/tenant/mytenant/application/myapplication/container.components/search/" - ], - - "configs": [ - "http://myconfigserver.mydomain.com:19071/config/v2/tenant/mytenant/application/myapplication/config.sentinel", - "http://myconfigserver.mydomain.com:19071/config/v2/tenant/mytenant/application/myapplication/config.model", - "http://myconfigserver.mydomain.com:19071/config/v2/tenant/mytenant/application/myapplication/container.components" - ] - -{% endhighlight %}</pre> -<p><code>GET /config/v2/tenant/mytenant/application/myapplication/?recursive=true</code></p> -<pre>{% highlight json %} -{ - "configs": [ - "http://myconfigserver.mydomain.com:19071/config/v2/tenant/mytenant/application/myapplication/config.sentinel/myconfigserver.mydomain.com", - "http://myconfigserver.mydomain.com:19071/config/v2/tenant/mytenant/application/myapplication/config.sentinel/hosts/myconfigserver.mydomain.com" -{% endhighlight %}</pre> - - - -<h2 id="list-namespace">GET /config/v2/tenant/mytenant/application/myapplication/[namespace.name]/</h2> -<table class="table"> - <thead> - </thead><tbody> - <tr><th>Parameters</th> - <td> - <p id="list-namespace-params"> - Same as above. - </p> - </td> - </tr> - <tr><th>Request body</th> - <td> - None - </td> - </tr> - <tr><th>Response</th> - <td> - <p id="list-namespace-response"> - List the configs in the model with the given namespace and name. - List semantics as above. - </p> - </td> - </tr> - <tr><th>Error Response</th> - <td> - <p id="list-namespace-error"> - 404 if the given namespace.name is not known to the config model. - </p> - </td> - </tr> - </tbody> -</table> -<p>Examples:</p> -<p><code>GET /config/v2/tenant/mytenant/application/myapplication/vespaclient.config.feeder/</code></p> -<pre>{% highlight json %} -{ - "children": [ - "http://myconfigserver.mydomain.com:19071/config/v2/tenant/mytenant/application/myapplication/vespaclient.config.feeder/search/", - "http://myconfigserver.mydomain.com:19071/config/v2/tenant/mytenant/application/myapplication/vespaclient.config.feeder/clients/", - "http://myconfigserver.mydomain.com:19071/config/v1/vespaclient.config.feeder/docproc/" - ] - "configs": [ - "http://myconfigserver.mydomain.com:19071/config/v2/tenant/mytenant/application/myapplication/vespaclient.config.feeder", - ] -} -{% endhighlight %}</pre> -<p><code>GET /config/v2/tenant/mytenant/application/myapplication/vespaclient.config.feeder/?recursive=true</code></p> -<pre>{% highlight json %} -{ - "configs": [ - "http://myconfigserver.mydomain.com:19071/config/v2/tenant/mytenant/application/myapplication/vespaclient.config.feeder/search/qrsclusters/default", - "http://myconfigserver.mydomain.com:19071/config/v2/tenant/mytenant/application/myapplication/vespaclient.config.feeder/clients/gateways", - "http://myconfigserver.mydomain.com:19071/config/v2/tenant/mytenant/application/myapplication/vespaclient.config.feeder/clients/gateways/gateway", -{% endhighlight %}</pre> - - - -<h2 id="list-prefix">GET /config/v2/tenant/mytenant/application/myapplication/[namespace.name]/[config/subid]/</h2> -<table class="table"> - <thead> - </thead><tbody> - <tr><th>Parameters</th> - <td> - <p id="list-prefix-params"> - Same as above. - </p> - </td> - </tr> - <tr><th>Request body</th> - <td> - None - </td> - </tr> - <tr><th>Response</th> - <td> - <p id="list-prefix-response"> - List the configs in the model with the given namespace and name, and - for which the given config id segment is a prefix. - </p> - </td> - </tr> - <tr><th>Error Response</th> - <td> - <p id="list-prefix-error"></p> - <ul> - <li>404 if the given namespace.name is not known to the config - model.</li> - <li>404 if the given config id is not in the model.</li> - </ul> - </td> - </tr> - </tbody> -</table> -<p>Examples:</p> -<p><code>GET /config/v2/tenant/mytenant/application/myapplication/vespaclient.config.feeder/search/</code></p> -<pre>{% highlight json %} -{ - "children": [ - "http://myconfigserver.mydomain.com:19071/config/v2/tenant/mytenant/application/myapplication/vespaclient.config.feeder/search/qrsclusters/" - ] - "configs": [ - "http://myconfigserver.mydomain.com:19071/config/v2/tenant/mytenant/application/myapplication/vespaclient.config.feeder/search" - ] -} -{% endhighlight %}</pre> -<p><code>GET /config/v2/tenant/mytenant/application/myapplication/vespaclient.config.feeder/search/?recursive=true</code></p> -<pre>{% highlight json %} -{ - "configs": [ - "http://myhost.mydomain.com:19071/config/v2/tenant/mytenant/application/myapplication/vespaclient.config.feeder/search/qrsclusters/default" - ] -} -{% endhighlight %}</pre> - - - -<h2 id="payload">GET /config/v2/tenant/mytenant/application/myapplication/[namespace.name]/[config/id]</h2> -<table class="table"> - <thead> - </thead><tbody> - <tr><th>Parameters</th> - <td> - <p id="payload-params"> - None - </p> - </td> - </tr> - <tr><th>Request body</th> - <td> - None - </td> - </tr> - <tr><th>Response</th> - <td> - <p id="payload-response"> - Returns the config payload of the - given <code>namespace.name/config/id</code>, formatted as JSON. - </p> - </td> - </tr> - <tr><th>Error Response</th> - <td> - <p id="payload-error"> - Same as above. - </p> - </td> - </tr> - </tbody> -</table> -<p>Example:</p> -<p><code>GET /config/v2/tenant/mytenant/application/myapplication/container.core.container-http/search/qrsclusters/default/qrserver.0</code></p> -<pre>{% highlight json %} -{ - "enabled": "true", - "requestbuffersize": "65536", - "port": { - "search": "8080", - "host": "" - }, - "fileserver": { - "throughsearch": "true" - } -} -{% endhighlight %}</pre> diff --git a/en/reference/api/config-v2.mdx b/en/reference/api/config-v2.mdx new file mode 100644 index 0000000000..4ab5c15bec --- /dev/null +++ b/en/reference/api/config-v2.mdx @@ -0,0 +1,112 @@ +--- +title: "Config API" +description: "Config API in Vespa applications." +--- +<table className="vespa-table"> + <thead> + <tr> + <th>Term</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>Parameters</td> + <td>**Parameter:** recursive; **Default:** false; **Description:** If true, include each config id in the model which produces the config, and list only the links to the config payload. If false, include the first level of the config ids in the listing of new list URLs, as explained above.</td> + </tr> + <tr> + <td>Request body</td> + <td>None</td> + </tr> + <tr> + <td>Response</td> + <td>A list response includes two arrays: List-links to descend one level down in the config id hierarchy, named <code>{`children`}</code>. <a href="#payload" aria-label="Config payload">Config payload</a> links for the current (top) level, named <code>{`configs`}</code>.</td> + </tr> + <tr> + <td>Error Response</td> + <td>N/A</td> + </tr> + </tbody> +</table> + +<table className="vespa-table"> + <thead> + <tr> + <th>Term</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>Parameters</td> + <td>Same as above.</td> + </tr> + <tr> + <td>Request body</td> + <td>None</td> + </tr> + <tr> + <td>Response</td> + <td>List the configs in the model with the given namespace and name. List semantics as above.</td> + </tr> + <tr> + <td>Error Response</td> + <td>404 if the given namespace.name is not known to the config model.</td> + </tr> + </tbody> +</table> + +<table className="vespa-table"> + <thead> + <tr> + <th>Term</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>Parameters</td> + <td>Same as above.</td> + </tr> + <tr> + <td>Request body</td> + <td>None</td> + </tr> + <tr> + <td>Response</td> + <td>List the configs in the model with the given namespace and name, and for which the given config id segment is a prefix.</td> + </tr> + <tr> + <td>Error Response</td> + <td>404 if the given namespace.name is not known to the config model. 404 if the given config id is not in the model.</td> + </tr> + </tbody> +</table> + +<table className="vespa-table"> + <thead> + <tr> + <th>Term</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>Parameters</td> + <td>None</td> + </tr> + <tr> + <td>Request body</td> + <td>None</td> + </tr> + <tr> + <td>Response</td> + <td>Returns the config payload of the given <code>{`namespace.name/config/id`}</code>, formatted as JSON.</td> + </tr> + <tr> + <td>Error Response</td> + <td>Same as above.</td> + </tr> + </tbody> +</table> + diff --git a/en/reference/api/deploy-v2.html b/en/reference/api/deploy-v2.html deleted file mode 100644 index f3a17c4076..0000000000 --- a/en/reference/api/deploy-v2.html +++ /dev/null @@ -1,1115 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Deploy API" -redirect_from: -- /en/cloudconfig/deploy-rest-api-v2.html -- /en/reference/deploy-rest-api-v2.html ---- - -<p> - This is the API specification and some examples for the HTTP Deploy API that can be used to deploy an application: -</p> -<ul> - <li><a href="#create-session">upload</a></li> - <li><a href="#prepare-session">prepare</a></li> - <li><a href="#activate-session">activate</a></li> -</ul> -<p> - The response format is JSON. Examples are found in the <a href="#use-cases">use-cases</a>. - Also see the <a href="/en/basics/applications.html#deploying-applications">deploy guide</a>. -</p> -{% include note.html content='To build a multi-application system, use one or three config server(s) per application. -Best practise is using a <a href="/en/operations/self-managed/docker-containers.html">containerized</a> architecture, -also see <a href="https://github.com/vespa-engine/sample-apps/tree/master/examples/operations/multinode-HA">multinode-HA</a>.' %} -<p> - The current API version is 2. The API port is 19071 - use - <a href="/en/reference/operations/self-managed/tools.html#vespa-model-inspect">vespa-model-inspect</a> service configserver - to find config server hosts. Example: - <code>http://myconfigserver.mydomain.com:19071/application/v2/tenant/default/session</code>. - Write operations return successfully after a majority of config servers have persisted - changes (e.g. 2 out of 3 config servers). -</p> -<p>Entities:</p> -<table class="table"> -<thead> -</thead><tbody> - <tr><th style="white-space:nowrap;">session-id</th> - <td> - <p id="session-id"> - The session-id used in this API is generated by the server and is required - for all operations after <a href="#create-session">creating</a> a session. - The session-id is valid if it is an active session, or it was created before - <a href="https://github.com/vespa-engine/vespa/blob/master/configdefinitions/src/vespa/configserver.def">session lifetime</a> - has expired, the default value being 1 hour. - </p> - </td> - </tr> - <tr><th>path</th> - <td> - <p id="path"> - An application file path in a request URL or parameter refers to a - relative path in the application package. - A path ending with "/" refers to a directory. - </p> - </td> - </tr> -</tbody> -</table> -<p> - Use <a href="../../clients/vespa-cli.html">Vespa CLI</a> to deploy from the command line. -</p> - - - -<h2 id="prepareandactivate">POST /application/v2/tenant/default/prepareandactivate</h2> -<p> - Creates a new session with the application package that is included in the request, - prepares it and then activates it. - See details in the steps later in this document -</p> -<table class="table"> - <thead> - </thead><tbody> -<tr><th>Parameters</th> - <td> - <p id="prepareandactivate-params"></p> - <table class="table"> - <thead> - <tr><th>Name</th><th>Default</th><th>Description</th></tr> - </thead> - <tbody> - <tr> - <td></td> - <td></td> - <td></td> - </tr> - </tbody> - </table> - </td> -</tr> -<tr><th>Request body</th> - <td> - <p id="create-request-body"></p> - <table class="table"> - <thead> - <tr><th>Required</th><th>Content</th><th>Note</th></tr> - </thead> - <tbody> - <tr> - <td>Yes</td> - <td>A compressed <a href="../applications/application-packages.html">application package</a> - (with gzip or zip compression)</td> - <td>Set <code>Content-Type</code> HTTP header to <code>application/x-gzip</code> - or <code>application/zip</code>.</td> - </tr> - </tbody> - </table> - </td> -</tr> -<tr><th>Response</th> - <td> - <p id="create-session-response"> - See <a href="#activate-session">active</a>. - </p> - </td> -</tr> -</tbody> -</table> -<p>Example:</p> -<pre> -$ (cd src/main/application && zip -r - .) | \ - curl --header Content-Type:application/zip --data-binary @- \ - localhost:19071/application/v2/tenant/default/prepareandactivate -</pre> -<pre>{% highlight json %} -{ - "log": [ - { - "time": 1619448107299, - "level": "WARNING", - "message": "Host named 'vespa-container' may not receive any config since it is not a canonical hostname. Disregard this warning when testing in a Docker container." - } - ], - "tenant": "default", - "session-id": "3", - "url": "http://localhost:19071/application/v2/tenant/default/application/default/environment/prod/region/default/instance/default", - "message": "Session 3 for tenant 'default' prepared and activated.", - "configChangeActions": { - "restart": [], - "refeed": [], - "reindex": [] - } -} -{% endhighlight %}</pre> - - - -<h2 id="create-session">POST /application/v2/tenant/default/session</h2> -<p>Creates a new session with the application package that is included in the request.</p> -<table class="table"> -<thead> -</thead><tbody> - <tr><th>Parameters</th> - <td> - <table class="table"> - <thead> - <tr><th>Name</th><th>Default</th><th>Description</th></tr> - </thead> - <tbody> - <tr> - <td>from</td> - <td>N/A</td> - <td>Use when you want to create a new session based on an active application. - The value supplied should be a URL to an active application. - </td> - </tr> - </tbody> - </table> - </td> - </tr> - <tr><th>Request body</th> - <td> - <table class="table"> - <thead> - <tr><th>Required</th><th>Content</th><th>Note</th></tr> - </thead> - <tbody> - <tr> - <td>Yes, unless <code>from</code> parameter is used</td> - <td>A compressed <a href="../applications/application-packages.html">application - package</a> (with gzip or zip compression)</td> - <td> It is required to set the <code>Content-Type</code> HTTP - header to <code>application/x-gzip</code> or <code>application/zip</code>, unless the - <code>from</code> parameter is used.</td> - </tr> - </tbody> - </table> - </td> - </tr> - <tr><th>Response</th> - <td> - The response contains: - <ul> - <li>A <a href="#session-id">session-id</a> to the application that was created.</li> - <li>A <a href="#prepare-session">prepared</a> URL for preparing the application.</li> - </ul> - </td> - </tr> -</tbody> -</table> -<p>Examples (both requests return the same response):</p> -<ul> - <li><code>POST /application/v2/tenant/default/session</code></li> - <li><code>POST /application/v2/tenant/default/session?from=http://myconfigserver.mydomain.com:19071/application/v2/tenant/default/application/default/environment/default/region/default/instance/default</code></li> -</ul> -<pre> -{ - "tenant": "default", - "session-id": "1", - "prepared": "http://myconfigserver.mydomain.com:19071/application/v2/tenant/default/session/<span class="pre-hilite">session-id</span>/prepared/", - "content": "http://myconfigserver.mydomain.com:19071/application/v2/tenant/default/session/<span class="pre-hilite">session-id</span>/content/", - "message": "Session 1 for tenant 'default' created." -} -</pre> - - - -<h2 id="content-put">PUT /application/v2/tenant/default/session/[<a href="#session-id">session-id</a>]/content/[<a href="#path">path</a>]</h2> -<p>Writes the content to the given path, or creates a directory if the path ends with '/'.</p> -<table class="table"> - <thead> - </thead><tbody> - <tr><th>Parameters</th> - <td> - None - </td> - </tr> - <tr><th>Request body</th> - <td> - <p id="content-put-request-body"></p> - <ul> - <li>If path is a directory, none.</li> - <li>If path is a file, the contents of the file.</li> - </ul> - </td> - </tr> - <tr><th>Response</th> - <td> - <p id="content-put-response">None</p> - <ul> - <li>Any errors or warnings from writing the file/creating the directory.</li> - </ul> - </td> - </tr> - </tbody> -</table> - - - - -<h2 id="content-get">GET /application/v2/tenant/default/session/[<a href="#session-id">session-id</a>]/content/[<a href="#path">path</a>]</h2> -<p>Returns the content of the file at this path, or lists files and directories if <code>path</code> ends with '/'.</p> -<table class="table"> - <thead> - </thead><tbody> - <tr><th>Parameters</th> - <td> - <p id="content-get-params"></p> - <table class="table"> - <thead> - <tr> - <th>Name</th><th>Default</th><th>Description</th></tr> - </thead> - <tbody> - <tr> - <td>recursive</td> - <td>false</td> - <td>If <em>true</em>, directory content will be listed recursively.</td> - </tr> - <tr> - <td>return</td> - <td>content</td> - <td> - <ul> - <li>If set to content and path refers to a file, the content will be - returned.</li> - <li>If set to content and path refers to a directory, the files and - subdirectories in the directory will be listed.</li> - <li>If set to status and path refers to a file, the file status and hash will - be returned.</li> - <li>If set to status and path refers to a directory, a list of file/subdirectory - statuses and hashes will be returned.</li> - </ul> - </td> - </tr> - </tbody> - </table> - </td> - </tr> - <tr><th>Request body</th> - <td> - None. - </td> - </tr> - <tr><th>Response</th> - <td> - <p id="content-get-response"></p> - <ul> - <li>If path is a directory: a JSON array of URLs to the files and subdirectories of - that directory.</li> - <li>If path is a file: the contents of the file.</li> - <li>If status parameter is set, the status and hash will be returned.</li> - </ul> - </td> - </tr> - </tbody> -</table> -<p>Examples:</p> -<p><code>GET /application/v2/tenant/default/session/3/content/</code></p> -<pre>{% highlight json %} -[ - "http://myconfigserver.mydomain.com:19071/application/v2/tenant/default/session/3/content/hosts.xml", - "http://myconfigserver.mydomain.com:19071/application/v2/tenant/default/session/3/content/services.xml", - "http://myconfigserver.mydomain.com:19071/application/v2/tenant/default/session/3/content/schemas/" -] -{% endhighlight %}</pre> -<p><code>GET /application/v2/tenant/default/session/3/content/?recursive=true</code></p> -<pre>{% highlight json %} -[ - "http://myconfigserver.mydomain.com:19071/application/v2/tenant/default/session/3/content/hosts.xml", - "http://myconfigserver.mydomain.com:19071/application/v2/tenant/default/session/3/content/services.xml", - "http://myconfigserver.mydomain.com:19071/application/v2/tenant/default/session/3/content/schemas/", - "http://myconfigserver.mydomain.com:19071/application/v2/tenant/default/session/3/content/schemas/music.sd", - "http://myconfigserver.mydomain.com:19071/application/v2/tenant/default/session/3/content/schemas/video.sd" -] -{% endhighlight %}</pre> -<p><code>GET /application/v2/tenant/default/session/3/content/hosts.xml</code></p> -<pre>{% highlight xml %} -<?xml version="1.0" encoding="utf-8" ?> -<hosts> - <host name="myhost.mydomain.com"> - <alias>vespa1</alias> - </host> - <host name="myhost.mydomain.com"> - <alias>vespa2</alias> - </host> -</hosts> -{% endhighlight %}</pre> -<p><code>GET /application/v2/tenant/default/session/3/content/hosts.xml?return=status</code></p> -<pre>{% highlight json %} -{ - "name": "http://myconfigserver.mydomain.com:19071/application/v2/tenant/default/session/3/content/hosts.xml", - "status": "new", - "md5": "03d7cff861fcc2d88db70b7857d4d452" -} -{% endhighlight %}</pre> -<p><code>GET /application/v2/tenant/default/session/3/content/schemas/?return=status</code></p> -<pre>{% highlight json %} -[ - { - "name": "http://myconfigserver.mydomain.com:19071/application/v2/tenant/default/session/3/content/schemas/music.sd", - "status": "new", - "md5": "03d7cff861fcc2d88db70b7857d4d452" - }, - { - "name": "http://myconfigserver.mydomain.com:19071/application/v2/tenant/default/session/3/content/schemas/video.sd", - "status": "changed", - "md5": "03d7cff861fcc2d88db70b7857d4d452" - }, - { - "name": "http://myconfigserver.mydomain.com:19071/application/v2/tenant/default/session/3/content/schemas/book.sd", - "status": "deleted", - "md5": "03d7cff861fcc2d88db70b7857d4d452" - } -] -{% endhighlight %}</pre> - - - -<h2 id="content-delete">DELETE /application/v2/tenant/default/session/[<a href="#session-id">session-id</a>]/content/[<a href="#path">path</a>]</h2> -<p>Deletes the resource at the given path.</p> -<table class="table"> - <thead> - </thead><tbody> - <tr><th>Parameters</th> - <td> - None - </td> - </tr> - <tr><th>Request body</th> - <td> - <p id="content-delete-request-body">None</p> - </td> - </tr> - <tr><th>Response</th> - <td> - <p id="content-delete-response">Any errors or warnings from deleting the resource.</p> - </td> - </tr> - </tbody> -</table> - - - -<h2 id="prepare-session">PUT /application/v2/tenant/default/session/[<a href="#session-id">session-id</a>]/prepared</h2> -<p>Prepares an application with the <a href="#session-id">session-id</a> given.</p> -<table class="table"> - <thead> - </thead><tbody> - <tr><th>Parameters</th> - <td> - <p id="prepare-put-params"></p> - <table class="table"> - <thead> - <tr> - <th>Parameter</th><th>Default</th><th>Description</th> - </tr> - </thead> - <tbody> - <tr> - <td>applicationName</td> - <td>N/A</td> - <td>Name of the application to be deployed</td> - </tr> - <tr> - <td>environment</td> - <td>default</td> - <td>Environment where application should be deployed</td> - </tr> - <tr> - <td>region</td> - <td>default</td> - <td>Region where application should be deployed</td> - </tr> - <tr> - <td>instance</td> - <td>default</td> - <td>Name of application instance</td> - </tr> - <tr> - <td>debug</td> - <td>false</td> - <td>If true, include stack trace in response if prepare fails.</td> - </tr> - <tr> - <td>timeout</td> - <td>360 seconds</td> - <td>Timeout in seconds to wait for session to be prepared.</td> - </tr> - </tbody> - </table> - </td> - </tr> - <tr><th>Request body</th> - <td> - <p id="prepare-session-request-body">None</p> - </td> - </tr> - <tr><th>Response</th> - <td> - <p id="prepare-session-response"> - Returns a <a href="#session-id">session-id</a> and a link to activate the session. - </p> - <ul> - <li>Log with any errors or warnings from preparing the application.</li> - <li>An <a href="#activate-session">activate</a> URL for activating - the application with this <a href="#session-id">session-id</a>, if there were no errors.</li> - <li>A list of actions (possibly empty) that must be performed in order to apply some config changes - between the current active application and this next prepared application. - These actions are organized into three categories; <em>restart</em>, <em>reindex</em>, and <em>refeed</em>: - <ul> - <li><em>Restart</em> actions are done after the application has been activated - and are handled by restarting all listed services. - See <a href="../schemas/schemas.html#modifying-schemas"> - schemas</a> for details.</li> - <li><em>Reindex</em> actions are special refeed actions that Vespa - <a href="../../operations/reindexing.html">handles automatically</a>, if the - <a href="#reindex">reindex</a> endpoint below is used.</li> - <li><em>Refeed</em> actions require several steps to handle. - See <a href="../schemas/schemas.html#modifying-schemas">schemas</a> for details.</li> - </ul> - </li> - </ul> - </td> - </tr> - </tbody> -</table> -<p>Example:</p> -<p><code>PUT /application/v2/tenant/default/session/3/prepared</code></p> -<pre>{% highlight json %} -{ - "tenant": "default", - "session-id": "3", - "activate": "http://myconfigserver.mydomain.com:19071/application/v2/tenant/default/session/3/active", - "message": "Session 3 for tenant 'default' prepared.", - "log": [ - { "level": "WARNING", - "message": "Warning message 1", - "time": 1430134091319 - }, - { "level": "WARNING", - "message": "Warning message 2", - "time": 1430134091320 - } - ], - "configChangeActions": { - "restart": [ { - "clusterName": "mycluster", - "clusterType": "search", - "serviceType": "searchnode", - "messages": [ "Document type 'test': Field 'f1' changed: add attribute aspect" ], - "services": [ { - "serviceName": "searchnode", - "serviceType": "searchnode", - "configId": "mycluster/search/cluster.mycluster/0", - "hostName": "myhost.mydomain.com" - } ] - } ], - "reindex": [ { - "documentType": "test", - "clusterName": "mycluster", - "messages": [ "Document type 'test': Field 'f1' changed: add index aspect" ], - "services": [ { - "serviceName": "searchnode", - "serviceType": "searchnode", - "configId": "mycluster/search/cluster.mycluster/0", - "hostName": "myhost.mydomain.com" - } ] - } ] - } -} -{% endhighlight %}</pre> - - - -<h2 id="get-prepare-session">GET /application/v2/tenant/default/session/[<a href="#session-id">session-id</a>]/prepared</h2> -<p> - Returns the state of a prepared session. - The response is the same as a successful <a href="#prepare-session">prepare</a> operation (above), - however the <em>configChangeActions</em> element will be empty. -</p> - - - -<h2 id="activate-session">PUT /application/v2/tenant/default/session/[<a href="#session-id">session-id</a>]/active</h2> -<p> - Activates an application with the <a href="#session-id">session-id</a> given. - The <a href="#session-id">session-id</a> must be for a <a href="#prepare-session">prepared session</a>. - The operation will make sure the session is activated on all config servers. -</p> -<table class="table"> - <thead> - </thead><tbody> - <tr><th>Parameters</th> - <td> - <p id="activate-params"></p> - <table class="table"> - <thead> - <tr> - <th>Parameter</th><th>Default</th><th>Description</th> - </tr> - </thead> - <tbody> - <tr> - <td>timeout</td> - <td>60 seconds</td> - <td>Timeout in seconds to wait for session to be activated (when - several config servers are used, they might need to sync - before activate can be done).</td> - </tr> - </tbody> - </table> - </td> - </tr> - <tr><th>Request body</th> - <td> - None - </td> - </tr> - <tr><th>Response</th> - <td> - <p id="activate-session-response"> - Returns a <a href="#session-id">session-id</a>, a message and a URL to the activated application. - </p> - <ul> - <li><a href="#session-id">session-id</a></li> - <li>Message</li> - </ul> - </td> - </tr> - </tbody> -</table> -<p>Example:</p> -<p><code>PUT /application/v2/tenant/default/session/3/active</code></p> -<pre>{% highlight json %} -{ - "tenant": "default", - "session-id": "3", - "message": "Session 3 for tenant 'default' activated.", - "url": "http://myconfigserver.mydomain.com:19071/application/v2/tenant/default/application/default/environment/default/region/default/instance/default" -} -{% endhighlight %}</pre> - - - -<h2 id="get-application">GET /application/v2/tenant/default/application/</h2> -<p>Returns a list of the currently active applications for the given tenant.</p> -<table class="table"> - <thead> - </thead><tbody> - <tr><th>Parameters</th> - <td> - None - </td> - </tr> - <tr><th>Request body</th> - <td> - None - </td> - </tr> - <tr><th>Response</th> - <td> - <p id="get-application-response">Returns a list of applications</p> - <ul> - <li>Array of active applications</li><!-- ToDo: this array will have max one element --> - </ul> - </td> - </tr> - </tbody> -</table> -<p>Example:</p> -<p><code>GET /application/v2/tenant/default/application/</code></p> -<pre>{% highlight json %} -{ - ["http://myconfigserver.mydomain.com:19071/application/v2/tenant/default/application/default/environment/default/region/default/instance/default"] -} -{% endhighlight %}</pre> - - - -<h2 id="get-application-info">GET /application/v2/tenant/default/application/default</h2> -<p>Gets info about the application.</p> -<table class="table"> - <thead> - </thead><tbody> - <tr><th>Parameters</th> - <td> - None - </td> - </tr> - <tr><th>Request body</th> - <td> - None - </td> - </tr> - <tr><th>Response</th> - <td> - <p id="get-application-info-response">Returns information about the application specified.</p> - <ul> - <li>config generation</li> - </ul> - </td> - </tr> - </tbody> -</table> -<p>Example:</p> -<p><code>GET /application/v2/tenant/default/application/default</code></p> -<pre>{% highlight json %} -{ - "generation": 2 -} -{% endhighlight %}</pre> - - - -<h2 id="reindexing">GET /application/v2/tenant/default/application/default/environment/default/region/default/instance/default/reindexing</h2> -<p>Returns <a href="../../operations/reindexing.html">reindexing</a> status for the given application.</p> -<table class="table"> - <thead> - </thead><tbody> - <tr><th>Parameters</th> - <td> - N/A - </td> - </tr> - <tr><th>Request body</th> - <td> - N/A - </td> - </tr> - <tr><th>Response</th> - <td> - JSON detailing current reindexing status for the application, with all its clusters and document types. - <ul> - <li>Status for each content cluster in the application, by name: - <ul> - <li>Status of each document type in the cluster, by name: - <ul> - <li>Last time reindexing was triggered for this document type.</li> - <li>Current status of reindexing.</li> - <li>Optional start time of reindexing.</li> - <li>Optional end time of reindexing.</li> - <li>Optional progress of reindexing, from 0 to 1.</li> - <li>Pseudo-speed of reindexing.</li> - </ul> - </li> - </ul> - </li> - </ul> - </td> - </tr> - </tbody> -</table> -<p>Example:</p> -<p><code>GET /application/v2/tenant/default/application/default/environment/default/region/default/instance/default/reindexing</code></p> -<pre>{% highlight json %} -{ - "clusters": { - "db": { - "ready": { - "test_artifact": { - "readyMillis": 1607937250998, - "startedMillis": 1607940060012, - "state": "running", - "speed": 1.0, - "progress": 0.04013824462890625 - }, - "test_result": { - "readyMillis": 1607688477294, - "startedMillis": 1607690520026, - "endedMillis": 1607709294236, - "speed": 0.1, - "state": "successful" - }, - "test_run": { - "readyMillis": 1607937250998, - "state": "pending" - } - } - } - } -} -{% endhighlight %}</pre> - - - -<h2 id="reindex">POST /application/v2/tenant/default/application/default/environment/default/region/default/instance/default/reindex</h2> -<p> - Marks specified document types in specified clusters of an application - as ready for <a href="../../operations/reindexing.html">reindexing</a>. - Reindexing itself starts with the next redeployment of the application. - To stop an ongoing reindexing, see <a href="#update-reindexing">updating reindexing</a> below. - All document types in all clusters are reindexed unless restricted, using parameters as specified: -</p> -<table class="table"> - <thead> - </thead><tbody> - <tr><th>Parameters</th> - <td> - <table class="table"> - <thead> - <tr><th>Name</th><th>Description</th></tr> - </thead> - <tbody> - <tr> - <td>clusterId</td> - <td>A comma-separated list of content clusters to limit reindexing to. - All clusters are reindexed if this is not present.</td> - </tr> - <tr> - <td>documentType</td> - <td>A comma-separated list of document types to limit reindexing to. - All document types are reindexed if this is not present.</td> - </tr> - <tr> - <td>indexedOnly</td> - <td>Boolean: whether to mark reindexing ready only for document types with indexing - mode <em>index</em> and at least one field with the indexing statement - <code>index</code>. Default is <code>false</code>. - </td> - </tr> - <tr> - <td>speed</td> - <td>Number (0–10], default 1: Indexing pseudo speed - - balance speed vs. resource use. Example: speed=0.1</td> - </tr> - </tbody> - </table> - </td> - </tr> - <tr><th>Request body</th> - <td> - N/A - </td> - </tr> - <tr><th>Response</th> - <td> - A human-readable message indicating what reindexing was marked as ready. - </td> - </tr> - </tbody> -</table> -<p>Example:</p> -<p><code>POST /application/v2/tenant/default/application/default/environment/default/region/default/instance/default/reindex?clusterId=foo,bar&documentType=moo,baz&indexedOnly=true</code></p> -<pre>{% highlight json %} -{ - "message": "Reindexing document types [moo, baz] in 'foo', [moo] in 'bar' of application default.default" -} -{% endhighlight %}</pre> - - - -<h2 id="update-reindexing">PUT /application/v2/tenant/default/application/default/environment/default/region/default/instance/default/reindex</h2> -<p> - Modifies <a href="../../operations/reindexing.html">reindexing</a> of specified document types in - specified clusters of an application. - Specifically, this can be used to alter the pseudo-speed of the reindexing, optionally halting it by specifying - a speed of <code>0</code>; reindexing for the specified types will remain dormant until either speed is increased again, - or a new reindexing is triggered (see <a href="#reindex">trigger reindexing</a>). - Speed changes become effective with the next redeployment of the application. - Reindexing for all document types in all clusters are affected if no other parameters are specified: -</p> -<table class="table"> - <thead> - </thead><tbody> - <tr><th>Parameters</th> - <td> - <table class="table"> - <thead> - <tr><th>Name</th><th>Description</th></tr> - </thead> - <tbody> - <tr> - <td>clusterId</td> - <td>A comma-separated list of content clusters to limit the changes to. - Reindexing for all clusters are modified if this is not present.</td> - </tr> - <tr> - <td>documentType</td> - <td>A comma-separated list of document types to limit the changes to. - Reindexing for all document types are modified if this is not present.</td> - </tr> - <tr> - <td>indexedOnly</td> - <td>Boolean: whether to modify reindexing only for document types with indexing - mode <em>index</em> and at least one field with the indexing statement - <code>index</code>. Default is <code>false</code>. - </td> - </tr> - <tr> - <td>speed</td> - <td>Number [0–10], required: Indexing pseudo speed - - balance speed vs. resource use. Example: speed=0.1</td> - </tr> - </tbody> - </table> - </td> - </tr> - <tr><th>Request body</th> - <td> - N/A - </td> - </tr> - <tr><th>Response</th> - <td> - A human-readable message indicating what reindexing was modified. - </td> - </tr> - </tbody> -</table> -<p>Example:</p> -<p><code>PUT /application/v2/tenant/default/application/default/environment/default/region/default/instance/default/reindex?clusterId=foo,bar&documentType=moo,baz&speed=0.618</code></p> -<pre>{% highlight json %} -{ - "message": "Set reindexing speed to '0.618' for document types [moo, baz] in 'foo', [moo] in 'bar' of application default.default" -} -{% endhighlight %}</pre> - - - -<h2 id="get-application-content">GET /application/v2/tenant/default/application/default/environment/default/region/default/instance/default/content/[<a href="#path">path</a>]</h2> -<p> - Returns content at the given path for an application. - See <a href="#content-get">getting content</a> for usage and response. -</p> - - - -<h2 id="delete-application">DELETE /application/v2/tenant/default/application/default</h2> -<p>Deletes an active application.</p> -<table class="table"> - <thead> - </thead><tbody> - <tr><th>Parameters</th> - <td> - None - </td> - </tr> - <tr><th>Request body</th> - <td> - None - </td> - </tr> - <tr><th>Response</th> - <td> - <p id="delete-application-response">Returns a message stating if the operation was successful or not</p> - </td> - </tr> - </tbody> -</table> -<p>Example:</p> -<p><code>DELETE /application/v2/tenant/default/application/default</code></p> -<pre>{% highlight json %} -{ - "message": "Application 'default' was deleted" -} -{% endhighlight %}</pre> - - - -<h2 id="get-host-info">GET /application/v2/host/[hostname]</h2> -<p>Gets information about which tenant and application a hostname is used by.</p> -<table class="table"> - <thead> - </thead><tbody> - <tr><th>Parameters</th> - <td> - None - </td> - </tr> - <tr><th>Request body</th> - <td> - None - </td> - </tr> - <tr><th>Response</th> - <td> - <p id="get-host-info-response">Returns a message with tenant and application details.</p> - </td> - </tr> - </tbody> -</table> -<p>Example:</p> -<p><code>GET /application/v2/host/myhost.mydomain.com</code></p> -<pre>{% highlight json %} -{ - "tenant": "default" - "application": "default" - "environment": "default" - "region": "default" - "instance": "default" -} -{% endhighlight %}</pre> - - - -<h2 id="error-handling">Error Handling</h2> -<p> - Errors are returned using standard HTTP status codes. - Any additional info is included in the body of the return call, JSON-formatted. - The general format for an error response is: -</p> -<pre>{% highlight json %} -{ - "error-code": "ERROR_CODE", - "message": "An error message" -} -{% endhighlight %}</pre> -<table class="table"> - <thead> - <tr> - <th>HTTP status code</th><th>Error code</th><th>Description</th> - </tr> - </thead><tbody> - <tr> - <td>400</td><td>BAD_REQUEST</td> - <td>Bad request. Client error. The error message should indicate the cause.</td> - </tr><tr> - <td>400</td><td>INVALID_APPLICATION_PACKAGE</td> - <td>There is an error in the application package. The error message should indicate the cause.</td> - </tr><tr> - <td>400</td><td>OUT_OF_CAPACITY</td> - <td>Not enough nodes available for the request to be fulfilled.</td> - </tr><tr> - <td>401</td><td></td> - <td>Not authorized. The error message should indicate the cause.</td> - </tr><tr> - <td>404</td><td>NOT_FOUND</td> - <td>Not found. E.g. when using a session-id that doesn't exist.</td> - </tr><tr> - <td>405</td><td>METHOD_NOT_ALLOWED</td> - <td>Method not implemented. E.g. using GET where only POST or PUT is allowed.</td> - </tr><tr> - <td>409</td><td>ACTIVATION_CONFLICT</td> - <td>Conflict, returned when activating an application fails due - to a conflict with other changes to the same application (in - another session). Client should retry.</td> - </tr><tr> - <td>500</td><td>INTERNAL_SERVER_ERROR</td> - <td>Internal server error. Generic error. The error message should indicate the cause.</td> - </tr> - </tbody> -</table> - - - -<h2 id="access-log">Access log</h2> -<p> - Requests are logged in the <a href="../../operations/access-logging.html">access log</a> which can be found at - <em>$VESPA_HOME/logs/vespa/configserver/access-json.log</em>, example: -</p> -<pre>{% highlight json %} -{ - "ip": "172.17.0.2", - "time": 1655665104.751, - "duration": 1.581, - "responsesize": 230, - "requestsize": 0, - "code": 200, - "method": "PUT", - "uri": "/application/v2/tenant/default/session/2/prepared", - "version": "HTTP/2.0", - "agent": "vespa-deploy", - "host": "b614c9ff04d7:19071", - "scheme": "https", - "localport": 19071, - "peeraddr": "172.17.0.2", - "peerport": 47480, - "attributes": { - "http2-stream-id":"1" - } -} -{% endhighlight %}</pre> - - -<h2 id="use-cases">Use Cases</h2> -<p> - It is assumed that the tenant <em>default</em> is already created in these use cases, - and the application package is in <em>app</em>. -</p> - - -<h3 id="use-case-start">Create, prepare and activate an application</h3> -<p>Create a session with the application package:</p> -<pre> -$ (cd app && zip -r - .) | \ - curl -s --header Content-Type:application/zip --data-binary @- \ - "http://host:19071/application/v2/tenant/default/session" -</pre> -<p>Prepare the application with the URL in the <em>prepared</em> link from the response:</p> -<pre> -$ curl -s -X PUT "http://host:19071/application/v2/tenant/default/session/1/prepared?applicationName=default" -</pre> -<p>Activate the application with the URL in the <em>activate</em> link from the response:</p> -<pre> -$ curl -s -X PUT "http://host:19071/application/v2/tenant/default/session/1/active" -</pre> - - -<h3 id="use-case-modify">Modify the application package</h3> -<p>Dump <em>services.xml</em> from session 1:</p> -<pre> -$ curl -s -X GET "http://host:19071/application/v2/tenant/default/session/1/content/services.xml" -</pre> -<pre>{% highlight xml %} -<?xml version="1.0" encoding="utf-8" ?> -<services version="1.0"> - <admin version="2.0"> - <config name="config.logd"> - <logserver> - <rpcport>12345</rpcport> - </logserver> - </config> - <adminserver hostalias="node1" /> - </admin> -</services> -{% endhighlight %}</pre> -<p>Session 1 is activated and cannot be changed - create a new session based on the active session:</p> -<pre> -$ curl -s -X POST "http://host:19071/application/v2/tenant/default/session?from=http://host:19071/application/v2/tenant/default/application/default/environment/default/region/default/instance/default" -</pre> -<p>Modify rpcport to 12346 in <em>services.xml</em>, deploy the change:</p> -<pre> -$ curl -s -X PUT --data-binary @app/services.xml \ - "http://host:19071/application/v2/tenant/default/session/2/content/services.xml" -</pre> -<p>Get <em>services.xml</em> from session 2 to validate:</p> -<pre> -$ curl -s -X GET "http://host:19071/application/v2/tenant/default/session/2/content/services.xml" -</pre> -<pre>{% highlight xml %} -<?xml version="1.0" encoding="utf-8" ?> -<services version="1.0"> - <admin version="2.0"> - <config name="config.logd"> - <logserver> - <rpcport>12346</rpcport> - </logserver> - </config> - <adminserver hostalias="node1" /> - </admin> -</services> -{% endhighlight %}</pre> -<p>To add the file <em>files/test1.txt</em>, first create the directory, then add the file:</p> -<pre> -$ curl -s -X PUT "http://host:19071/application/v2/tenant/default/session/2/content/files/" -$ curl -s -X PUT --data-binary @app/files/test1.txt \ - "http://host:19071/application/v2/tenant/default/session/2/content/files/test1.txt" -</pre> -<p>Prepare and activate the session:</p> -<pre> -$ curl -s -X PUT "http://host:19071/application/v2/tenant/default/session/2/prepared?applicationName=fooapp" -$ curl -s -X PUT "http://host:19071/application/v2/tenant/default/session/2/active" -</pre> - - -<h3 id="rollback">Rollback</h3> -<p> - If you need to roll back to a previous version of the application package - this can be achieved by creating a new session based on the previous known - working version by passing the corresponding session-id in the - <em>from</em> argument, see <a href="#create-session">creating a session</a> -</p> -<p>Also see <a href="/en/applications/deployment.html#rollback">rollback</a>.</p> - -<!-- TODO: Need multiple instances to support this. --> diff --git a/en/reference/api/deploy-v2.mdx b/en/reference/api/deploy-v2.mdx new file mode 100644 index 0000000000..57098ab7ec --- /dev/null +++ b/en/reference/api/deploy-v2.mdx @@ -0,0 +1,345 @@ +--- +title: "Deploy API" +description: "Example:" +--- +<table className="vespa-table"> + <thead> + <tr> + <th>Term</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>session-id</td> + <td>The session-id used in this API is generated by the server and is required for all operations after <a href="#create-session" aria-label="creating">creating</a> a session. The session-id is valid if it is an active session, or it was created before <a href="https://github.com/vespa-engine/vespa/blob/master/configdefinitions/src/vespa/configserver.def" aria-label="session lifetime">session lifetime</a> has expired, the default value being 1 hour.</td> + </tr> + <tr> + <td>path</td> + <td>An application file path in a request URL or parameter refers to a relative path in the application package. A path ending with "/" refers to a directory.</td> + </tr> + </tbody> +</table> + +<table className="vespa-table"> + <thead> + <tr> + <th>Term</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>Parameters</td> + <td></td> + </tr> + <tr> + <td>Request body</td> + <td>**Required:** Yes; **Content:** A compressed <a href="/en/reference/applications/application-packages" aria-label="application package">application package</a> (with gzip or zip compression); **Note:** Set <code>{`Content-Type`}</code> HTTP header to <code>{`application/x-gzip`}</code> or <code>{`application/zip`}</code> .</td> + </tr> + <tr> + <td>Response</td> + <td>See <a href="#activate-session" aria-label="active">active</a>.</td> + </tr> + </tbody> +</table> + +<table className="vespa-table"> + <thead> + <tr> + <th>Term</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>Parameters</td> + <td>**Name:** from; **Default:** N/A; **Description:** Use when you want to create a new session based on an active application. The value supplied should be a URL to an active application.</td> + </tr> + <tr> + <td>Request body</td> + <td>**Required:** Yes, unless <code>{`from`}</code> parameter is used; **Content:** A compressed <a href="/en/reference/applications/application-packages" aria-label="application package">application package</a> (with gzip or zip compression); **Note:** It is required to set the <code>{`Content-Type`}</code> HTTP header to <code>{`application/x-gzip`}</code> or <code>{`application/zip`}</code> , unless the <code>{`from`}</code> parameter is used.</td> + </tr> + <tr> + <td>Response</td> + <td>The response contains: A <a href="#session-id" aria-label="session-id">session-id</a> to the application that was created. A <a href="#prepare-session" aria-label="prepared">prepared</a> URL for preparing the application.</td> + </tr> + </tbody> +</table> + +<table className="vespa-table"> + <thead> + <tr> + <th>Term</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>Parameters</td> + <td>None</td> + </tr> + <tr> + <td>Request body</td> + <td>If path is a directory, none. If path is a file, the contents of the file.</td> + </tr> + <tr> + <td>Response</td> + <td>None Any errors or warnings from writing the file/creating the directory.</td> + </tr> + </tbody> +</table> + +<table className="vespa-table"> + <thead> + <tr> + <th>Term</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>Parameters</td> + <td>**Name:** recursive; **Default:** false; **Description:** If *true* , directory content will be listed recursively.<br />**Name:** return; **Default:** content; **Description:** If set to content and path refers to a file, the content will be returned. If set to content and path refers to a directory, the files and subdirectories in the directory will be listed. If set to status and path refers to a file, the file status and hash will be returned. If set to status and path refers to a directory, a list of file/subdirectory statuses and hashes will be returned.</td> + </tr> + <tr> + <td>Request body</td> + <td>None.</td> + </tr> + <tr> + <td>Response</td> + <td>If path is a directory: a JSON array of URLs to the files and subdirectories of that directory. If path is a file: the contents of the file. If status parameter is set, the status and hash will be returned.</td> + </tr> + </tbody> +</table> + +<table className="vespa-table"> + <thead> + <tr> + <th>Term</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>Parameters</td> + <td>None</td> + </tr> + <tr> + <td>Request body</td> + <td>None</td> + </tr> + <tr> + <td>Response</td> + <td>Any errors or warnings from deleting the resource.</td> + </tr> + </tbody> +</table> + +<table className="vespa-table"> + <thead> + <tr> + <th>Term</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>Parameters</td> + <td>**Parameter:** applicationName; **Default:** N/A; **Description:** Name of the application to be deployed<br />**Parameter:** environment; **Default:** default; **Description:** Environment where application should be deployed<br />**Parameter:** region; **Default:** default; **Description:** Region where application should be deployed<br />**Parameter:** instance; **Default:** default; **Description:** Name of application instance<br />**Parameter:** debug; **Default:** false; **Description:** If true, include stack trace in response if prepare fails.<br />**Parameter:** timeout; **Default:** 360 seconds; **Description:** Timeout in seconds to wait for session to be prepared.</td> + </tr> + <tr> + <td>Request body</td> + <td>None</td> + </tr> + <tr> + <td>Response</td> + <td>Returns a <a href="#session-id" aria-label="session-id">session-id</a> and a link to activate the session. Log with any errors or warnings from preparing the application. An <a href="#activate-session" aria-label="activate">activate</a> URL for activating the application with this <a href="#session-id" aria-label="session-id">session-id</a>, if there were no errors. A list of actions (possibly empty) that must be performed in order to apply some config changes between the current active application and this next prepared application. These actions are organized into three categories; *restart*, *reindex*, and *refeed*: *Restart* actions are done after the application has been activated and are handled by restarting all listed services. See <a href="/en/reference/schemas/schemas#modifying-schemas" aria-label="schemas">schemas</a> for details. *Reindex* actions are special refeed actions that Vespa <a href="/en/operations/reindexing" aria-label="handles automatically">handles automatically</a>, if the <a href="#reindex" aria-label="reindex">reindex</a> endpoint below is used. *Refeed* actions require several steps to handle. See <a href="/en/reference/schemas/schemas#modifying-schemas" aria-label="schemas">schemas</a> for details.</td> + </tr> + </tbody> +</table> + +<table className="vespa-table"> + <thead> + <tr> + <th>Term</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>Parameters</td> + <td>**Parameter:** timeout; **Default:** 60 seconds; **Description:** Timeout in seconds to wait for session to be activated (when several config servers are used, they might need to sync before activate can be done).</td> + </tr> + <tr> + <td>Request body</td> + <td>None</td> + </tr> + <tr> + <td>Response</td> + <td>Returns a <a href="#session-id" aria-label="session-id">session-id</a>, a message and a URL to the activated application. <a href="#session-id" aria-label="session-id">session-id</a> Message</td> + </tr> + </tbody> +</table> + +<table className="vespa-table"> + <thead> + <tr> + <th>Term</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>Parameters</td> + <td>None</td> + </tr> + <tr> + <td>Request body</td> + <td>None</td> + </tr> + <tr> + <td>Response</td> + <td>Returns a list of applications Array of active applications</td> + </tr> + </tbody> +</table> + +<table className="vespa-table"> + <thead> + <tr> + <th>Term</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>Parameters</td> + <td>None</td> + </tr> + <tr> + <td>Request body</td> + <td>None</td> + </tr> + <tr> + <td>Response</td> + <td>Returns information about the application specified. config generation</td> + </tr> + </tbody> +</table> + +<table className="vespa-table"> + <thead> + <tr> + <th>Term</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>Parameters</td> + <td>N/A</td> + </tr> + <tr> + <td>Request body</td> + <td>N/A</td> + </tr> + <tr> + <td>Response</td> + <td>JSON detailing current reindexing status for the application, with all its clusters and document types. Status for each content cluster in the application, by name: Status of each document type in the cluster, by name: Last time reindexing was triggered for this document type.Current status of reindexing.Optional start time of reindexing.Optional end time of reindexing.Optional progress of reindexing, from 0 to 1.Pseudo-speed of reindexing.</td> + </tr> + </tbody> +</table> + +<table className="vespa-table"> + <thead> + <tr> + <th>Term</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>Parameters</td> + <td>**Name:** clusterId; **Description:** A comma-separated list of content clusters to limit reindexing to. All clusters are reindexed if this is not present.<br />**Name:** documentType; **Description:** A comma-separated list of document types to limit reindexing to. All document types are reindexed if this is not present.<br />**Name:** indexedOnly; **Description:** Boolean: whether to mark reindexing ready only for document types with indexing mode *index* and at least one field with the indexing statement <code>{`index`}</code> . Default is <code>{`false`}</code> .<br />**Name:** speed; **Description:** Number (0–10], default 1: Indexing pseudo speed - balance speed vs. resource use. Example: speed=0.1</td> + </tr> + <tr> + <td>Request body</td> + <td>N/A</td> + </tr> + <tr> + <td>Response</td> + <td>A human-readable message indicating what reindexing was marked as ready.</td> + </tr> + </tbody> +</table> + +<table className="vespa-table"> + <thead> + <tr> + <th>Term</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>Parameters</td> + <td>**Name:** clusterId; **Description:** A comma-separated list of content clusters to limit the changes to. Reindexing for all clusters are modified if this is not present.<br />**Name:** documentType; **Description:** A comma-separated list of document types to limit the changes to. Reindexing for all document types are modified if this is not present.<br />**Name:** indexedOnly; **Description:** Boolean: whether to modify reindexing only for document types with indexing mode *index* and at least one field with the indexing statement <code>{`index`}</code> . Default is <code>{`false`}</code> .<br />**Name:** speed; **Description:** Number [0–10], required: Indexing pseudo speed - balance speed vs. resource use. Example: speed=0.1</td> + </tr> + <tr> + <td>Request body</td> + <td>N/A</td> + </tr> + <tr> + <td>Response</td> + <td>A human-readable message indicating what reindexing was modified.</td> + </tr> + </tbody> +</table> + +<table className="vespa-table"> + <thead> + <tr> + <th>Term</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>Parameters</td> + <td>None</td> + </tr> + <tr> + <td>Request body</td> + <td>None</td> + </tr> + <tr> + <td>Response</td> + <td>Returns a message stating if the operation was successful or not</td> + </tr> + </tbody> +</table> + +<table className="vespa-table"> + <thead> + <tr> + <th>Term</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>Parameters</td> + <td>None</td> + </tr> + <tr> + <td>Request body</td> + <td>None</td> + </tr> + <tr> + <td>Response</td> + <td>Returns a message with tenant and application details.</td> + </tr> + </tbody> +</table> + diff --git a/en/reference/api/document-v1.html b/en/reference/api/document-v1.html deleted file mode 100644 index 3ab938ddde..0000000000 --- a/en/reference/api/document-v1.html +++ /dev/null @@ -1,1096 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "/document/v1 API reference" -redirect_from: -- /en/reference/document-v1-api-reference.html ---- - -<p> - This is the /document/v1 API reference documentation. - Use this API for synchronous <a href="../../schemas/documents.html">Document</a> operations to a Vespa endpoint - - refer to <a href="../../writing/reads-and-writes.html">reads and writes</a> for other options. -</p><p> - The <a href="../../writing/document-v1-api-guide.html">document/v1 API guide</a> has examples and use cases. -</p> -{% include note.html content=' -Mapping from document IDs to /document/v1/ URLs is found in <a href="../../schemas/documents.html#id-scheme">Document IDs</a> - -also see <a href="../../writing/document-v1-api-guide.html#troubleshooting">troubleshooting</a>.'%} -<p> - Some examples use <em>number</em> and <em>group</em> - <a href="../../schemas/documents.html#document-ids">document ID</a> modifiers. - These are special cases that only work as expected for document types - with <a href="../applications/services/content.html#document">mode=streaming or mode=store-only</a>. - Do not use group or number modifiers with regular indexed mode document types. -</p> - - - -<h2 id="configuration">Configuration</h2> -<p> - To enable the API, add <code>document-api</code> in the serving container cluster - - <a href="../applications/services/container.html">services.xml</a>: -</p> -<pre> -<services> - <container> - <span class="pre-hilite"><document-api/></span> -</pre> - - - -<h2 id="http-requests">HTTP requests</h2> -<table class="table"> - <thead> - <tr> - <th>HTTP request</th> - <th>document/v1 operation</th> - <th>Description</th> - </tr> - </thead> - <tbody> - <tr> - <th>GET</th> - <td colspan="2"> - <p id="get"> - <em>Get</em> a document by ID or <em>Visit</em> a set of documents by selection. - </p> - </td> - </tr> - <tr> - <td></td> - <th>Get</th> - <td> - Get a document: -<pre> -/document/v1/<namespace>/<document-type>/docid/<document-id> -/document/v1/<namespace>/<document-type>/number/<numeric-group-id>/<document-id> -/document/v1/<namespace>/<document-type>/group/<text-group-id>/<document-id> -</pre> - Optional parameters: - <ul> - <li><a href="#cluster">cluster</a></li> - <li><a href="#fieldset">fieldSet</a></li> - <li><a href="#timeout">timeout</a></li> - <li><a href="#tracelevel">tracelevel</a></li> - </ul> - </td> - </tr> - <tr> - <td></td> - <th>Visit</th> - <td> - <p id="visit"> - Iterate over and get all documents, or a <a href="#selection">selection</a> of documents, - in chunks, using <a href="#continuation">continuation</a> tokens to track progress. - Visits are a linear scan over the documents in the cluster. - </p> -<pre> -/document/v1/ -</pre> - It is possible to specify namespace and document type with the visit path: -<pre> -/document/v1/<namespace>/<document-type>/docid -</pre> - Documents can be grouped to limit accesses to a subset. - A group is defined by a numeric ID or string — see <a href="../../schemas/documents.html#id-scheme">id scheme</a>. -<pre> -/document/v1/<namespace>/<document-type>/group/<group> -/document/v1/<namespace>/<document-type>/number/<number> -</pre> - Mandatory parameters: - <ul> - <li><a href="#cluster">cluster</a> - - Visits can only retrieve data from <em>one</em> content cluster, - so <code>cluster</code> <strong>must</strong> be specified - for requests at the root <code>/document/v1/</code> level, or when there is ambiguity. - This is required even if the application has only one content cluster.</li> - </ul> - Optional parameters: - <ul> - <li><a href="#bucketspace">bucketSpace</a> - - Parent documents are <a href="../applications/services/content.html#document">global</a> - and in the <code>global</code> <a href="#bucketspace">bucket space</a>. - By default, visit will visit non-global documents - in the <code>default</code> bucket space, unless document type is indicated, - and is a global document type. - </li> - <li><a href="#concurrency">concurrency</a> - - Use to configure backend parallelism for each visit HTTP request.</li> - <li><a href="#continuation">continuation</a></li> - <li><a href="#fieldset">fieldSet</a></li> - <li><a href="#selection">selection</a></li> - <li><a href="#sliceid">sliceId</a></li> - <li><a href="#slices">slices</a> - - Split visiting of the document corpus across more than one HTTP - request—thus allowing the concurrent use of more HTTP containers—use the - <code>slices</code> and <code>sliceId</code> parameters.</li> - <li><a href="#stream">stream</a> - - It's recommended enabling streamed HTTP responses, - with the <a href="#stream">stream</a> parameter, - as this reduces memory consumption and reduces HTTP overhead.</li> - <li><a href="#timeout">timeout</a></li> - <li><a href="#tracelevel">tracelevel</a></li> - <li><a href="#wanteddocumentcount">wantedDocumentCount</a></li> - <li><a href="#fromtimestamp">fromTimestamp</a></li> - <li><a href="#totimestamp">toTimestamp</a></li> - <li><a href="#includeRemoves">includeRemoves</a></li> - </ul> - Optional request headers: - <ul> - <li><a href="#accept">Accept</a> - specify the desired response format.</li> - </ul> - </td> - </tr> - <tr> - <th>POST</th> - <td colspan="2"> - <p id="post"> - <em>Put</em> a given document, by ID, - or <em>Copy</em> a set of documents by selection from one content cluster to another. - </p> - </tr> - <tr> - <td></td> - <th>Put</th> - <td> - Write the document contained in the request body in JSON format. -<pre> -/document/v1/<namespace>/<document-type>/docid/<document-id> -/document/v1/<namespace>/<document-type>/group/<group> -/document/v1/<namespace>/<document-type>/number/<number> -</pre> - Optional parameters: - <ul> - <li><a href="#condition">condition</a> - - Use for conditional writes.</li> - <li><a href="#route">route</a></li> - <li><a href="#timeout">timeout</a></li> - <li><a href="#tracelevel">tracelevel</a></li> - </ul> - </td> - </tr> - <tr> - <td></td> - <th>Copy</th> - <td> - <p id="copy"> - Write documents visited in source <a href="#cluster">cluster</a> to the - <a href="#destinationcluster">destinationCluster</a> in the same application. - A <a href="#selection">selection</a> is mandatory — typically the document type. - Supported paths (see <a href="#visit">visit</a> above for semantics): - </p> -<pre> -/document/v1/ -/document/v1/<namespace>/<document-type>/docid/ -/document/v1/<namespace>/<document-type>/group/<group> -/document/v1/<namespace>/<document-type>/number/<number> -</pre> - Mandatory parameters: - <ul> - <li><a href="#cluster">cluster</a></li> - <li><a href="#destinationcluster">destinationCluster</a></li> - <li><a href="#selection">selection</a></li> - </ul> - Optional parameters: - <ul> - <li><a href="#bucketspace">bucketSpace</a></li> - <li><a href="#continuation">continuation</a></li> - <li><a href="#timechunk">timeChunk</a></li> - <li><a href="#timeout">timeout</a></li> - <li><a href="#tracelevel">tracelevel</a></li> - </ul> - </td> - </tr> - <tr> - <th>PUT</th> - <td colspan="2"> - <p id="put"> - <em>Update</em> a document with the given partial update, by ID, - or <em>Update where</em> the given selection is true. - </p> - </td> - </tr> - <tr> - <td></td> - <th>Update</th> - <td> - Update a document with the partial update contained in the request body in the - <a href="../schemas/document-json-format.html#update">document update JSON format</a>. -<pre> -/document/v1/<namespace>/<document-type>/docid/<document-id> -</pre> - Optional parameters: - <ul> - <li><a href="#condition">condition</a> - - use for conditional writes</li> - <li><a href="#create">create</a> - - use to create empty documents when updating non-existent ones.</li> - <li><a href="#route">route</a></li> - <li><a href="#timeout">timeout</a></li> - <li><a href="#tracelevel">tracelevel</a></li> - </ul> - </td> - </tr> - <tr> - <td></td> - <th>Update where</th> - <td> - <p id="update-where"> - Update visited documents in <a href="#cluster">cluster</a> with the partial update - contained in the request body in the - <a href="../schemas/document-json-format.html#update">document update JSON format</a>. - Supported paths (see <a href="#visit">visit</a> above for semantics): - </p> -<pre> -/document/v1/<namespace>/<document-type>/docid/ -/document/v1/<namespace>/<document-type>/group/<group> -/document/v1/<namespace>/<document-type>/number/<number> -</pre> - Mandatory parameters: - <ul> - <li><a href="#cluster">cluster</a></li> - <li><a href="#selection">selection</a></li> - </ul> - Optional parameters: - <ul> - <li><a href="#bucketspace">bucketSpace</a> - - See <a href="#visit">visit</a>, <code>default</code> or <code>global</code> bucket space</li> - <li><a href="#continuation">continuation</a></li> - <li><a href="#stream">stream</a></li> - <li><a href="#timechunk">timeChunk</a></li> - <li><a href="#timeout">timeout</a></li> - <li><a href="#tracelevel">tracelevel</a></li> - <!-- ToDo: is slices/Id supported here? --> - </ul> - </td> - </tr> - <tr> - <th>DELETE</th> - <td colspan="2"> - <p id="delete"> - <em>Remove</em> a document, by ID, or <em>Remove where</em> the given selection is true. - </p> - </td> - </tr> - <tr> - <td></td> - <th>Remove</th> - <td> - Remove a document. -<pre> -/document/v1/<namespace>/<document-type>/docid/<document-id> -</pre> - Optional parameters: - <ul> - <li><a href="#condition">condition</a></li> - <li><a href="#route">route</a></li> - <li><a href="#timeout">timeout</a></li> - <li><a href="#tracelevel">tracelevel</a></li> - </ul> - </td> - </tr> - <tr> - <td></td> - <th>Delete where</th> - <td> - <p id="delete-where"> - Delete visited documents from <a href="#cluster">cluster</a>. - Supported paths (see <a href="#visit">visit</a> above for semantics): - </p> -<pre> -/document/v1/ -/document/v1/<namespace>/<document-type>/docid/ -/document/v1/<namespace>/<document-type>/group/<group> -/document/v1/<namespace>/<document-type>/number/<number> -</pre> - Mandatory parameters: - <ul> - <li><a href="#cluster">cluster</a></li> - <li><a href="#selection">selection</a></li> - </ul> - Optional parameters: - <ul> - <li><a href="#bucketspace">bucketSpace</a> - - See <a href="#visit">visit</a>, <code>default</code> or <code>global</code> bucket space</li> - <li><a href="#continuation">continuation</a></li> - <li><a href="#stream">stream</a></li> - <li><a href="#timechunk">timeChunk</a></li> - <li><a href="#timeout">timeout</a></li> - <li><a href="#tracelevel">tracelevel</a></li> - <!-- ToDo: is slices/Id supported here? --> - </ul> - </td> - </tr> - </tbody> -</table> - - - -<h2 id="request-parameters">Request parameters</h2> -<table class="table"> -<thead> -<tr> - <th>Parameter</th> - <th>Type</th> - <th>Description</th> -</tr> -</thead><tbody> -<tr> - <th>bucketSpace</th> - <td>String</td> - <td> - <p id="bucketspace"> - Specify the bucket space to visit. - Document types marked as <code>global</code> exist in a separate <em>bucket space</em> from non-global document types. - When visiting a particular document type, - the bucket space is automatically deduced based on the provided type name. - When visiting at a root <code>/document/v1/</code> level this information is not available, - and the non-global ("default") bucket space is visited by default. - Specify <code>global</code> to visit global documents instead. - Supported values: <code>default</code> (for non-global documents) and <code>global</code>. - </p> -</tr><tr> - <th>cluster</th> - <td>String</td> - <td> - <p id="cluster">Name of <a href="../../content/content-nodes.html">content cluster</a> to GET from, or visit.</p> - </td> -</tr><tr> - <th>concurrency</th> - <td>Integer</td> - <td> - <p id="concurrency"> - Sends the given number of visitors in parallel to the backend, - improving throughput at the cost of resource usage. - Default is 1. - When <code>stream=true</code>, concurrency limits the maximum concurrency, - which is otherwise unbounded, but controlled by a dynamic throttle policy. - {% include important.html content="Given a concurrency parameter of <em>N</em>, - the worst case for memory used while processing the request grows linearly with <em>N</em>, - unless <a href='#stream'>stream</a> mode is turned on. - This is because the container currently buffers all response data in memory before sending them to the client, - and all sent visitors must complete before the response can be sent." %} - </p> - </td> -</tr><tr> - <th>condition</th> - <td>String</td> - <td> - <p id="condition"> - For test-and-set. - Run a document operation conditionally — if the condition fails, - a <em>412 Precondition Failed</em> is returned. - See <a href="../../writing/document-v1-api-guide.html#conditional-writes">example</a>. - </p> - </td> -</tr><tr> - <th>continuation</th> - <td>String</td> - <td> - <p id="continuation"> - When visiting, a continuation token is returned as the <code>"continuation"</code> field - in the JSON response, as long as more documents remain. - Use this token as the <code>continuation</code> parameter to visit the next chunk of documents. - See <a href="../../writing/document-v1-api-guide.html#data-dump">example</a>. - </p> - </td> -</tr><tr> - <th>create</th> - <td>Boolean</td> - <td> - <p id="create"> - If <code>true</code>, updates to non-existent documents will create an empty document to update. - See <a href="../../writing/document-v1-api-guide.html#create-if-nonexistent">create if nonexistent</a>. - </p> - </td> -</tr><tr> - <th>destinationCluster</th> - <td>String</td> - <td> - <p id="destinationcluster"> - Name of <a href="../../content/content-nodes.html">content cluster</a> to copy to, during a copy visit. - </p> - </td> -</tr><tr> - <th>dryRun</th> - <td>Boolean</td> - <td> - <p id="dryrun"> - Used by the <a href="../../clients/vespa-feed-client.html">vespa-feed-client</a> - using <code>--speed-test</code> for bandwidth testing, - by setting to <code>true</code>. - </p> - </td> -</tr><tr> - <th>fieldSet</th> - <td>String</td> - <td> - <p id="fieldset"> - A <a href="../../schemas/documents.html#fieldsets">field set string</a> - with the set of document fields to fetch from the backend. - Default is the special <code>[document]</code> fieldset, returning all <em>document</em> fields. - To fetch specific fields, use the name of the document type, followed by a comma-separated list of fields - (for example <code>music:artist,song</code> to fetch two fields declared in <code>music.sd</code>). - </p> - </td> -</tr><tr> - <th>route</th> - <td>String</td> - <td> - <p id="route"> - The route for single document operations, and for operations generated - by <a href="#copy">copy</a>, <a href="#update-where">update</a> or - <a href="#delete-where">deletion</a> visits. Default value is <code>default</code>. - See <a href="../../writing/document-routing.html">routes</a>. - </p> - </td> -</tr><tr> - <th>selection</th> - <td>String</td> - <td> - <p id="selection"> - Select only a subset of documents when <a href="../../writing/visiting.html">visiting</a> — - details in <a href="../writing/document-selector-language.html">document selector language</a>. - </p> - </td> -</tr><tr> - <th>sliceId</th> - <td>Integer</td> - <td> - <p id="sliceid"> - The slice number of the visit represented by this HTTP request. This number must be non-negative - and less than the number of <a href="#slices">slices</a> specified for the visit - - e.g., if the number of slices is 10, <code>sliceId</code> is in the range [0-9]. - {% include note.html content="If the number of distribution bits change during a sliced visit, - the results are undefined. - Thankfully, this is a very rare occurrence and is only triggered when adding content nodes." %} - </p> - </td> -</tr><tr> - <th>slices</th> - <td>Integer</td> - <td> - <p id="slices"> - Split the document corpus into this number of independent slices. This lets multiple, concurrent series of HTTP - requests advance the same logical visit independently, by specifying a different <a href="#sliceid">sliceId</a> for each. - </p> - </td> -</tr><tr> - <th>stream</th> - <td>Boolean</td> - <td> - <p id="stream"> - Whether to stream the HTTP response, allowing data to flow as soon as documents arrive from the backend. - This obsoletes the <a href="#wanteddocumentcount">wantedDocumentCount</a> parameter. - The HTTP status code will always be 200 if the visit is successfully initiated. Default value is false. - </p> - </td> -</tr> -<tr> - <th>format.tensors</th> - <td>String</td> - <td> - <p id="format.tensors"> - Controls how tensors are rendered in the result. - </p> - <table class="table"> - <thead> - <tr> - <th>Value</th> - <th>Description</th> - </tr> - </thead> - <tbody> - <tr> - <td><code>short</code></td> - <td> - <b>Default</b>. Render the tensor value in an object having two keys, "type" containing the value, - and "cells"/"blocks"/"values" (<a href="../schemas/document-json-format.html#tensor">depending on the type</a>) containing the tensor content.<br/> - Render the tensor content in the <a href="../schemas/document-json-format.html#tensor">type-appropriate short form</a>. - </td> - </tr> - <tr> - <td><code>long</code></td> - <td> - Render the tensor value in an object having two keys, "type" containing the value, - and "cells" containing the tensor content.<br/> - Render the tensor content in the <a href="../schemas/document-json-format.html#tensor">general verbose form</a>. - </td> - </tr> - <tr> - <td><code>short-value</code></td> - <td> - Render the tensor content directly.<br/> - Render the tensor content in the <a href="../schemas/document-json-format.html#tensor">type-appropriate short form</a>. - </td> - </tr> - <tr> - <td><code>long-value</code></td> - <td> - Render the tensor content directly.<br/> - Render the tensor content in the <a href="../schemas/document-json-format.html#tensor">general verbose form</a>. - </td> - </tr> - </table> - </td> -</tr> -<tr> - <th>timeChunk</th> - <td>String</td> - <td> - <p id="timechunk"> - Target time to spend on one chunk of a copy, update or remove visit; with optional ks, s, ms or µs unit. - Default value is 60. - </p> - </td> -</tr><tr> - <th>timeout</th> - <td>String</td> - <td><p id="timeout">Request timeout in seconds, or with optional ks, s, ms or µs unit. Default value is 180s.</p></td> -</tr><tr> - <th>tracelevel</th> - <td>Integer</td> - <td> - <p id="tracelevel"> - Number in the range [0,9], where higher gives more details. - The trace dumps which nodes and chains the document operation has touched. - See <a href="../../writing/document-routing.html">routes</a>. - </p> - </td> -</tr><tr> - <th>wantedDocumentCount</th> - <td>Integer</td> - <td> - <p id="wanteddocumentcount"> - Best effort attempt to not respond to the client before <code>wantedDocumentCount</code> - number of documents have been visited. - Response may still contain fewer documents if there are not enough matching documents left - to visit in the cluster, or if the visiting times out. - This parameter is intended for the case when you have relatively few documents in your cluster - and where each visit request would otherwise process only a handful of documents. - </p><p> - The maximum value of <code>wantedDocumentCount</code> is bounded - by an implementation-specific limit to prevent excessive resource usage. - If the cluster has many documents (on the order of tens of millions), - there is no need to set this value. - </p> - </td> -</tr><tr> - <th>fromTimestamp</th> - <td>Integer</td> - <td> - <p id="fromtimestamp"> - Filters the returned document set to only include documents that were last modified - at a time point equal to or higher to the specified value, in microseconds from UTC epoch. - Default value is 0 (include all documents). - </p> - </td> -</tr><tr> - <th>toTimestamp</th> - <td>Integer</td> - <td> - <p id="totimestamp"> - Filters the returned document set to only include documents that were last modified - at a time point lower than the specified value, in microseconds from UTC epoch. - Default value is 0 (sentinel value; include all documents). If non-zero, must be - greater than, or equal to, <code>fromTimestamp</code>. - </p> - </td> -</tr><tr> - <th>includeRemoves</th> - <td>Boolean</td> - <td> - <p id="includeRemoves"> - Include recently removed document IDs, along with the set of returned documents. - By default, only documents currently present in the corpus are returned in the - <code>"documents"</code> array of the response; when this parameter is set to - <code>"true"</code>, documents that were recently removed, and whose tombstones - still exist, are also included in that array, as entries on the form - <code>{ "remove": "id:ns:type::foobar" }</code>. See - <a href="/en/operations/self-managed/admin-procedures.html#data-retention-vs-size">here</a> - for specifics on tombstones, including their lifetime. - </p> - </td> -</tr> -</tbody> -</table> - - - -<h2 id="http-request-headers">HTTP request headers</h2> - -<table class="table"> - <thead> - <tr> - <th>Header</th> - <th>Values</th> - <th>Description</th> - </tr> - </thead> - <tbody> - <tr> - <th>Accept</th> - <td> - <code>application/json</code> or <code>application/jsonl</code> - </td> - <td> - <p id="accept"> - The <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Accept">Accept</a> header - lets the client specify to the server what - <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/MIME_types">media (MIME) types</a> it - accepts as the response format. - </p> - <p> - All Document V1 API calls support <code>application/json</code> for returning <a href="#json">JSON</a> responses. - <a href="#stream">Streaming visiting</a> additionally supports <code>application/jsonl</code> for returning - <a href="#json-lines">JSON Lines</a> (JSONL) since Vespa 8.593. - </p> - <p> - To ensure compatibility with older versions, make sure to check the <code>Content-Type</code> - <a href="#http-response-headers">HTTP response header</a>. A JSONL response will always have a - <code>Content-Type</code> media type of <code>application/jsonl</code>, and JSON wil always have - a media type of <code>application/json</code>. - </p> - Multiple acceptable types can be specified. JSONL will be returned if (and only if) - <code>application/jsonl</code> is part of the list <em>and</em> no other media - types have a higher <a href="https://httpwg.org/specs/rfc9110.html#quality.values">quality value</a>. - </p> - <p> - Example: - </p> -<pre> -Accept: application/jsonl -</pre> - <p> - If the client accepts both JSON and JSONL, the server will respond with JSONL: - </p> -<pre> -Accept: application/json, application/jsonl -</pre> - <p> - For backwards compatibility, if no <code>Accept</code> header is provided - (or if no provided media types are acceptable) <code>application/json</code> - is assumed. - </p> - </td> - </tr> - </tbody> -</table> - - - -<h2 id="request-body">Request body</h2> -<p> - POST and PUT requests must include a body for single document operations; PUT must - also include a body for <a href="#update-where">update where</a> visits. - A field has a <em>value</em> for a POST and an <em>update operation object</em> for PUT. - Documents and operations use the <a href="../schemas/document-json-format.html">document JSON format</a>. - The document fields must match the <a href="../../basics/schemas.html">schema</a>: -</p> -<pre>{% highlight json %} -{ - "fields": { - "<fieldname>": "<value>" - } -} -{% endhighlight %}</pre> -<pre>{% highlight json %} -{ - "fields": { - "<fieldname>": { - "<update-operation>" : "<value>" - } - } -} -{% endhighlight %}</pre> -<p> - The <em>update-operation</em> is most often <code>assign</code> - see - <a href="../schemas/document-json-format.html#update-operations">update operations</a> for the full list. - Values for <code>id</code> / <code>put</code> / <code>update</code> in the request body are silently dropped. - The ID is generated from the request path, regardless of request body data - example: -</p> -<pre>{% highlight json %} -{ - "put" : "id:mynamespace:music::123", - "fields": { - "title": "Best of" - } -} -{% endhighlight %}</pre> -<p> - This makes it easier to generate a feed file that can be used for both the - <a href="../../clients/vespa-feed-client.html">vespa-feed-client</a> and this API. -</p> - - - -<h2 id="http-status-codes">HTTP status codes</h2> - -<table class="table"> - <thead> - <tr> - <th>Code</th> - <th>Description</th> - </tr> - </thead> - <tbody> -<tr> - <th>200</th> - <td>OK. Attempts to remove or update a non-existent document also yield this status code (see 412 below).</td> -</tr> -<tr> - <th>204</th> - <td>No Content. Successful response to OPTIONS request.</td> -</tr> -<tr> - <th>400</th> - <td>Bad request. Returned for undefined document types + other request errors. - See <a href="https://github.com/vespa-engine/vespa/issues/13465">13465</a> - for defined document types not assigned to a content cluster when using PUT. - Inspect <code>message</code> for details.</td> -</tr><tr> - <th>404</th> - <td>Not found; the document was not found. This is only used when getting documents.</td> -</tr> -<tr> - <th>405</th> - <td>Method Not Allowed. HTTP method is not supported by the endpoint. Valid combinations are listed - <a href="#http-requests">above</a></td> -</tr> -<tr> - <th>412</th> - <td><a href="#condition">condition</a> is not met. - Inspect <code>message</code> for details. This is also the result when - a condition if specified, but the document does not exist.</td> -</tr><tr> - <th>413</th> - <td>Content too large; used for POST and PUT requests that are above the <a href="../../writing/document-v1-api-guide.html#request-size-limit">request size limit</a>.</td> -</tr><tr> - <th>429</th> - <td>Too many requests; the document API has too many inflight feed operations, retry later.</td> -</tr><tr> - <th>500</th> - <td>Server error; an unspecified error occurred when processing the request/response.</td> -</tr><tr> - <th>503</th> - <td>Service unavailable; the document API was unable to produce a response at this time.</td> -</tr><tr> - <th>504</th> - <td>Gateway timeout; the document API failed to respond within the given (or default 180s) timeout.</td> -</tr><tr> - <th>507</th> - <td>Insufficient storage; the content cluster is out of memory or disk space.</td> -</tr> - </tbody> -</table> - - - -<h2 id="http-response-headers">HTTP response headers</h2> - -<table class="table"> - <thead> - <tr> - <th>Header</th> - <th>Values</th> - <th>Description</th> - </tr> - </thead> - <tbody> - <tr> - <th>X-Vespa-Ignored-Operation</th> - <th>true</th> - <td> - <p id="x-vespa-ignored-operation"> - Will be present and set to the string 'true' - if (and only if) the operation was ignored during routing. - Operations are ignored when not matching the - <a href="../writing/document-selector-language.html">document selection</a> - for <em>all</em> content clusters, e.g., for expired documents. - Since {% include version.html version="8.680" %} - </p> - </td> - </tr> - <tr> - <th>X-Vespa-Ignored-Fields</th> - <th>true</th> - <td> - <p id="x-vespa-ignored-fields"> - Will be present and set to 'true' only when a put or update contains one or more fields - which were <a href="../applications/services/container.html#ignore-undefined-fields">ignored since they are not present in the document type</a>. - Such operations will be applied exactly as if they did not contain the field operations - referencing non-existing fields. - References to non-existing fields in field <i>paths</i> are not detected. - </p> - </td> - </tr> - <tr> - <th>Content-Type</th> - <th><code>application/json</code> or <code>application/jsonl</code></th> - <td> - <p id="content-type"> - The <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/MIME_types">media type</a> - (MIME type) of the response body. - </p> - <p> - Either <code>application/json</code> for <a href="#json">JSON</a> responses or - <code>application/jsonl</code> for <a href="#json-lines">JSON Lines</a> (JSONL) responses. - <p> - The content type may include additional parameters such as <code>charset</code>. - </p> - <p> - Example header: -<pre> -Content-Type: application/json; charset=UTF-8 -</pre> - </p> - </td> - </tr> - </tbody> -</table> - - -<h2 id="response-formats">Response formats</h2> -<p> -Responses are by default in JSON format. <a href="#stream">Streaming visiting</a> -supports an optional <a href="#json-lines">JSON Lines</a> (JSONL) response format -since Vespa 8.593. -</p> -<h3 id="json">JSON</h3> -<p> -JSON responses have the following fields: -</p> -<table class="table"> - <thead> - <tr> - <th>Field</th> - <th>Description</th> - </tr> - </thead> - <tbody> - <tr><th>pathId</th><td>Request URL path — always included.</td></tr> - <tr><th>message</th><td>An error message — included for all failed requests.</td></tr> - <tr><th>id</th><td>Document ID — always included for single document operations, including <em>Get</em>.</td></tr> - <tr><th>fields</th><td>The requested document fields — included for successful <em>Get</em> operations.</td></tr> - <tr><th>documents[]</th><td>Array of documents in a visit result — - each document has the <em>id</em> and <em>fields</em>.</td></tr> - <tr><th>documentCount</th><td>Number of visited and selected documents. - If <a href="#includeRemoves">includeRemoves</a> is <code>true</code>, this also includes - the number of returned removes (tombstones).</td></tr> - <tr><th>continuation</th><td>Token to be used to get the next chunk of the corpus - - see <a href="#continuation">continuation</a>.</td></tr> - </tbody> -</table> -<p> -GET can include a <code>fields</code> object if a document was found in a <em>GET</em> request -</p> -<pre>{% highlight json %} -{ - "pathId": "<pathid>", - "id": "<id>", - "fields": { - } -} -{% endhighlight %}</pre> -<p> -A GET <em>visit</em> result can include an array of <code>documents</code> -plus a <a href="#continuation">continuation</a>: -</p> -<pre>{% highlight json %} -{ - "pathId": "<pathid>", - "documents": [ - { - "id": "<id>", - "fields": { - } - } - ], - "continuation": "<continuation string>", - "documentCount": 123 -} -{% endhighlight %}</pre> -<p> -A continuation indicates the client should make further requests to get more data, while lack of a -continuation indicates an error occurred, and that visiting should cease, or that there are no more documents. -</p> -<p> -A <code>message</code> can be returned for failed operations: -</p> -<pre>{% highlight json %} -{ - "pathId": "<pathid>", - "message": "<message text>" -} -{% endhighlight %}</pre> - -<h3 id="json-lines">JSON Lines</h3> -<p> - A JSON Lines (JSONL) response is a stream of newline-separated JSON objects. Each line contains - exactly one JSON object, and each JSON object takes up exactly one line. No line breaks are - allowed within an object. -</p> -<p> - JSONL is an optional response format for <a href="#stream">streaming visiting</a>, enabling - efficient client-side parsing and fine-grained, continuous tracking of visitor progress. - The JSONL response format is currently not supported for any other operations than streaming visiting. -</p> -<p> - The JSONL response format is enabled by providing a HTTP <a href="#accept">Accept</a> request - header that specifies <code>application/jsonl</code> as the preferred response type, and will - have a <a href="#content-type">Content-Type</a> of <code>application/jsonl</code> if the server - is on a version that supports JSONL visiting. Clients must check the <code>Content-Type</code> - header to ensure they are getting the format they expect. -</p> -<p> - JSONL support requires Vespa 8.593 or newer. -</p> -<p> -Example response body: -</p> -<pre>{% highlight json %} -{"put":"id:ns:music::one","fields":{"foo":"bar"}} -{"put":"id:ns:music::two","fields":{"foo":"baz"}} -{"continuation":{"token":"...","percentFinished":40.0}} -{"put":"id:ns:music::three","fields":{"foo":"zoid"}} -{"remove":"id:ns:music::four"} -{"continuation":{"token":"...","percentFinished":50.0}} -{"continuation":{"token":"...","percentFinished":60.0}} -{"put":"id:ns:music::five","fields":{"foo":"berg"}} -{"continuation":{"token":"...","percentFinished":70.0}} -{"sessionStats":{"documentCount":5}} -{"continuation":{"percentFinished":100.0}} -{% endhighlight %}</pre> -<p> - Note that the <code>"..."</code> values are placeholders for (from a client's perspective) opaque - string values. -</p> -<p> -<h4 id="jsonl-response-objects">JSONL response objects</h4> -<p> -{% include note.html content=' - To be forwards compatible with future extensions to the response format, ignore unknown objects and fields.'%} -</p> -<table class="table"> - <thead> - <tr> - <th>Object</th> - <th>Description</th> - </tr> - </thead> - <tbody> - <tr><th>put</th><td>A document <a href="../schemas/document-json-format.html#put">Put</a> operation in the same - format as that accepted by Vespa's JSONL feed API.</td></tr> - <tr><th>remove</th><td>A document <a href="../schemas/document-json-format.html#remove">Remove</a> operation in - the same format as that accepted by Vespa's JSONL feed API. - Only present if <a href="#includeRemoves">includeRemoves</a> is <code>true</code>.</td></tr> - <tr><th>continuation</th><td> - <p>A visitor <a href="#continuation">continuation</a>.</p> - <p>Possible sub-object fields:</p> - <table class="table"> - <thead> - <tr> - <th>Field name</th> - <th>Description</th> - </tr> - </thead> - <tbody> - <tr> - <td><code>token</code></td> - <td> - <p>An opaque string value representing the current visitor progress through the data space. - This value can be provided as part of a subsequent visitor request to continue visiting - from where the last request left off. Clients should not attempt to parse the contents - of this string, as it's considered an internal implementation detail and may be changed - (in a backwards compatible way) without any prior announcement.</p> - </td> - </tr> - <tr> - <td><code>percentFinished</code></td> - <td> - A floating point number between 0 and 100 (inclusive) that gives an approximation of how - far the visitor has progressed through the data space. - </td> - </tr> - </table> - <p> - The last line of a successful request should always be a <code>continuation</code> object. - </p> - <p> - If (and only if) visiting has completed, the last <code>continuation</code> object will have a - <code>percentFinished</code> value of <code>100</code> and will <em>not</em> have a <code>token</code> field. - </p> - </td></tr> - <tr><th>message</th><td> - <p> - A message received from the backend visitor session. Can be used by clients to report - problems encountered during visiting. - </p> - <p>Possible sub-object fields:</p> - <table class="table"> - <thead> - <tr> - <th>Field name</th> - <th>Description</th> - </tr> - </thead> - <tbody> - <tr> - <td><code>text</code></td> - <td>The actual message, in unstructured text</td> - </tr> - <tr> - <td><code>severity</code></td> - <td>The severity of the message. One of <code>info</code>, <code>warning</code> or <code>error</code>.</td> - </tr> - </table> - </td></tr> - <tr><th>sessionStats</th><td> - <p>Statistics from the backend visitor session.</p> - <p>Possible sub-object fields:</p> - <table class="table"> - <thead> - <tr> - <th>Field name</th> - <th>Description</th> - </tr> - </thead> - <tbody> - <tr> - <td><code>documentCount</code></td> - <td> - The number of visited and selected documents. If <a href="#includeRemoves">includeRemoves</a> - is <code>true</code>, this also includes the number of returned removes (tombstones). - </td> - </tr> - </table> - </td></tr> - </tbody> -</table> -<p> - Note that it's possible for a successful response to contain zero <code>put</code> or <code>remove</code> - objects if the <a href="#selection">selection</a> did not match any documents. -</p> - -<h4 id="differences-from-json-response-format">Differences from the JSON format</h4> -<p> - The biggest difference in semantics between the JSON and JSONL response formats is when, and how, - <a href="#continuation">continuation</a> objects are returned. -</p> -<p> - In the JSON format a continuation is included <em>once</em> at the very end of the response object - and covers the progress made by the entire request. If the request somehow fails after receiving - 99% of all documents but prior to receiving the continuation field, the client must retry the - entire request from the previously known continuation value. This can result in getting many requested - documents twice; once from the incomplete first request and once more from the second request that - covers the same part of the data space. -</p> -<p> - In the JSON Lines format, a contination object is emitted to the stream <em>every time</em> a backend - data <a href="../../content/buckets.html">bucket</a> has been fully visited, as well as at the end of the - response stream. This may happen many times in a response. Each continuation object <em>subsumes</em> - the progress of previously emitted continuations, meaning that a client only needs to remember the - <em>most recent</em> continuation value it observed in the response. If the request fails prior to - completion, the client can specify the most recent continuation in the next request; it will then only - receive duplicates for the data buckets that were actively being processed when the request failed. -</p> diff --git a/en/reference/api/document-v1.mdx b/en/reference/api/document-v1.mdx new file mode 100644 index 0000000000..cd8ca40999 --- /dev/null +++ b/en/reference/api/document-v1.mdx @@ -0,0 +1,71 @@ +--- +title: "/document/v1 API reference" +description: "/document/v1 API reference in Vespa applications." +--- +<table className="vespa-table"> + <thead> + <tr> + <th>HTTP request</th> + <th>document/v1 operation</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>GET</td> + <td>*Get* a document by ID or *Visit* a set of documents by selection.</td> + <td></td> + </tr> + <tr> + <td></td> + <td>Get</td> + <td>Get a document: <code>{`/document/v1/<namespace>/<document-type>/docid/<document-id>`}</code> <code>{`/document/v1/<namespace>/<document-type>/number/<numeric-group-id>/<document-id>`}</code> <code>{`/document/v1/<namespace>/<document-type>/group/<text-group-id>/<document-id>`}</code> Optional parameters: <a href="#cluster" aria-label="cluster">cluster</a> <a href="#fieldset" aria-label="fieldSet">fieldSet</a> <a href="#timeout" aria-label="timeout">timeout</a> <a href="#tracelevel" aria-label="tracelevel">tracelevel</a></td> + </tr> + <tr> + <td></td> + <td>Visit</td> + <td>Iterate over and get all documents, or a <a href="#selection" aria-label="selection">selection</a> of documents, in chunks, using <a href="#continuation" aria-label="continuation">continuation</a> tokens to track progress. Visits are a linear scan over the documents in the cluster. <code>{`/document/v1/`}</code> It is possible to specify namespace and document type with the visit path: <code>{`/document/v1/<namespace>/<document-type>/docid`}</code> Documents can be grouped to limit accesses to a subset. A group is defined by a numeric ID or string — see <a href="/en/schemas/documents#id-scheme" aria-label="id scheme">id scheme</a> . <code>{`/document/v1/<namespace>/<document-type>/group/<group>`}</code> <code>{`/document/v1/<namespace>/<document-type>/number/<number>`}</code> Mandatory parameters: <a href="#cluster" aria-label="cluster">cluster</a> - Visits can only retrieve data from *one* content cluster, so <code>{`cluster`}</code> **must** be specified for requests at the root <code>{`/document/v1/`}</code> level, or when there is ambiguity. This is required even if the application has only one content cluster. Optional parameters: <a href="#bucketspace" aria-label="bucketSpace">bucketSpace</a> - Parent documents are <a href="/en/reference/applications/services/content#document" aria-label="global">global</a> and in the <code>{`global`}</code> <a href="#bucketspace" aria-label="bucket space">bucket space</a>. By default, visit will visit non-global documents in the <code>{`default`}</code> bucket space, unless document type is indicated, and is a global document type. <a href="#concurrency" aria-label="concurrency">concurrency</a> - Use to configure backend parallelism for each visit HTTP request. <a href="#continuation" aria-label="continuation">continuation</a> <a href="#fieldset" aria-label="fieldSet">fieldSet</a> <a href="#selection" aria-label="selection">selection</a> <a href="#sliceid" aria-label="sliceId">sliceId</a> <a href="#slices" aria-label="slices">slices</a> - Split visiting of the document corpus across more than one HTTP request—thus allowing the concurrent use of more HTTP containers—use the <code>{`slices`}</code> and <code>{`sliceId`}</code> parameters. <a href="#stream" aria-label="stream">stream</a> - It's recommended enabling streamed HTTP responses, with the <a href="#stream" aria-label="stream">stream</a> parameter, as this reduces memory consumption and reduces HTTP overhead. <a href="#timeout" aria-label="timeout">timeout</a> <a href="#tracelevel" aria-label="tracelevel">tracelevel</a> <a href="#wanteddocumentcount" aria-label="wantedDocumentCount">wantedDocumentCount</a> <a href="#fromtimestamp" aria-label="fromTimestamp">fromTimestamp</a> <a href="#totimestamp" aria-label="toTimestamp">toTimestamp</a> <a href="#includeRemoves" aria-label="includeRemoves">includeRemoves</a> Optional request headers: <a href="#accept" aria-label="Accept">Accept</a> - specify the desired response format.</td> + </tr> + <tr> + <td>POST</td> + <td>*Put* a given document, by ID, or *Copy* a set of documents by selection from one content cluster to another.</td> + <td></td> + </tr> + <tr> + <td></td> + <td>Copy</td> + <td>Write documents visited in source <a href="#cluster" aria-label="cluster">cluster</a> to the <a href="#destinationcluster" aria-label="destinationCluster">destinationCluster</a> in the same application. A <a href="#selection" aria-label="selection">selection</a> is mandatory — typically the document type. Supported paths (see <a href="#visit" aria-label="visit">visit</a> above for semantics): <code>{`/document/v1/`}</code> <code>{`/document/v1/<namespace>/<document-type>/docid/`}</code> <code>{`/document/v1/<namespace>/<document-type>/group/<group>`}</code> <code>{`/document/v1/<namespace>/<document-type>/number/<number>`}</code> Mandatory parameters: <a href="#cluster" aria-label="cluster">cluster</a> <a href="#destinationcluster" aria-label="destinationCluster">destinationCluster</a> <a href="#selection" aria-label="selection">selection</a> Optional parameters: <a href="#bucketspace" aria-label="bucketSpace">bucketSpace</a> <a href="#continuation" aria-label="continuation">continuation</a> <a href="#timechunk" aria-label="timeChunk">timeChunk</a> <a href="#timeout" aria-label="timeout">timeout</a> <a href="#tracelevel" aria-label="tracelevel">tracelevel</a></td> + </tr> + <tr> + <td>PUT</td> + <td>*Update* a document with the given partial update, by ID, or *Update where* the given selection is true.</td> + <td></td> + </tr> + <tr> + <td></td> + <td>Update</td> + <td>Update a document with the partial update contained in the request body in the <a href="/en/reference/schemas/document-json-format#update" aria-label="document update JSON format">document update JSON format</a> . <code>{`/document/v1/<namespace>/<document-type>/docid/<document-id>`}</code> Optional parameters: <a href="#condition" aria-label="condition">condition</a> - use for conditional writes <a href="#create" aria-label="create">create</a> - use to create empty documents when updating non-existent ones. <a href="#route" aria-label="route">route</a> <a href="#timeout" aria-label="timeout">timeout</a> <a href="#tracelevel" aria-label="tracelevel">tracelevel</a></td> + </tr> + <tr> + <td></td> + <td>Update where</td> + <td>Update visited documents in <a href="#cluster" aria-label="cluster">cluster</a> with the partial update contained in the request body in the <a href="/en/reference/schemas/document-json-format#update" aria-label="document update JSON format">document update JSON format</a>. Supported paths (see <a href="#visit" aria-label="visit">visit</a> above for semantics): <code>{`/document/v1/<namespace>/<document-type>/docid/`}</code> <code>{`/document/v1/<namespace>/<document-type>/group/<group>`}</code> <code>{`/document/v1/<namespace>/<document-type>/number/<number>`}</code> Mandatory parameters: <a href="#cluster" aria-label="cluster">cluster</a> <a href="#selection" aria-label="selection">selection</a> Optional parameters: <a href="#bucketspace" aria-label="bucketSpace">bucketSpace</a> - See <a href="#visit" aria-label="visit">visit</a>, <code>{`default`}</code> or <code>{`global`}</code> bucket space <a href="#continuation" aria-label="continuation">continuation</a> <a href="#stream" aria-label="stream">stream</a> <a href="#timechunk" aria-label="timeChunk">timeChunk</a> <a href="#timeout" aria-label="timeout">timeout</a> <a href="#tracelevel" aria-label="tracelevel">tracelevel</a></td> + </tr> + <tr> + <td>DELETE</td> + <td>*Remove* a document, by ID, or *Remove where* the given selection is true.</td> + <td></td> + </tr> + <tr> + <td></td> + <td>Remove</td> + <td>Remove a document. <code>{`/document/v1/<namespace>/<document-type>/docid/<document-id>`}</code> Optional parameters: <a href="#condition" aria-label="condition">condition</a> <a href="#route" aria-label="route">route</a> <a href="#timeout" aria-label="timeout">timeout</a> <a href="#tracelevel" aria-label="tracelevel">tracelevel</a></td> + </tr> + <tr> + <td></td> + <td>Delete where</td> + <td>Delete visited documents from <a href="#cluster" aria-label="cluster">cluster</a>. Supported paths (see <a href="#visit" aria-label="visit">visit</a> above for semantics): <code>{`/document/v1/`}</code> <code>{`/document/v1/<namespace>/<document-type>/docid/`}</code> <code>{`/document/v1/<namespace>/<document-type>/group/<group>`}</code> <code>{`/document/v1/<namespace>/<document-type>/number/<number>`}</code> Mandatory parameters: <a href="#cluster" aria-label="cluster">cluster</a> <a href="#selection" aria-label="selection">selection</a> Optional parameters: <a href="#bucketspace" aria-label="bucketSpace">bucketSpace</a> - See <a href="#visit" aria-label="visit">visit</a>, <code>{`default`}</code> or <code>{`global`}</code> bucket space <a href="#continuation" aria-label="continuation">continuation</a> <a href="#stream" aria-label="stream">stream</a> <a href="#timechunk" aria-label="timeChunk">timeChunk</a> <a href="#timeout" aria-label="timeout">timeout</a> <a href="#tracelevel" aria-label="tracelevel">tracelevel</a></td> + </tr> + </tbody> +</table> + diff --git a/en/reference/api/metrics-v1.html b/en/reference/api/metrics-v1.html deleted file mode 100644 index 2d2948a6ff..0000000000 --- a/en/reference/api/metrics-v1.html +++ /dev/null @@ -1,179 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "/metrics/v1 API reference" -redirect_from: -- /en/reference/metrics-v1.html ---- - -<p> - The <em>node metrics API</em> is available on each <em>node</em> at - the metrics proxy port, default <em>http://host:19092/metrics/v1/values</em>. -</p> -<p> - Refer to <a href="/en/operations/self-managed/monitoring.html">monitoring</a> - for an overview of nodes, services and metrics APIs for self-hosted applications. -</p> - -<h2 id="http-requests">HTTP requests</h2> -<table class="table"> - <thead> - <tr> - <th>HTTP request</th> - <th>metrics/v1 operation</th> - <th>Description</th> - </tr> - </thead> - <tbody> - <tr> - <th>GET</th> - <td colspan="2"> - <p id="get"> - </p> - </td> - </tr> - <tr> - <td></td> - <th>Node metrics</th> - <td> - <p id="metrics-v1-values"></p> - <pre>/metrics/v1/values</pre> - <p> - See <a href="/en/operations/self-managed/monitoring.html#metrics-v1-values">monitoring</a> for examples. - </p> - </td> - </tr> - </tbody> -</table> - - - -<h2 id="request-parameters">Request parameters</h2> -<table class="table"> - <thead> - <tr> - <th>Parameter</th> - <th>Type</th> - <th>Description</th> - </tr> - </thead><tbody> -<tr> - <td>consumer</td> - <td>String</td> - <td> - <p id="consumer"> - Specify response <a href="../applications/services/admin.html#consumer">consumer</a>, i.e. set of metrics. - An unknown / empty value will return the <code>default</code> metric set. - Built-in: - </p> - <ul> - <li><code>default</code> - see <a href="../operations/metrics/default-metric-set.html"> - DefaultMetrics</a>.</li> - <li><code>vespa</code> - see <a href="../operations/metrics/vespa-metric-set.html"> - VespaMetricSet</a>.</li> - </ul> - </td> -</tr> -</tbody> -</table> - - - -<h2 id="http-status-codes">HTTP status codes</h2> -<p> - Non-exhaustive list of status codes: -</p> -<table class="table"> - <thead> - <tr> - <th>Code</th> - <th>Description</th> - </tr> - </thead> - <tbody> - <tr> - <td>200</td> - <td>OK.</td> - </tr> <!-- ToDo: more codes --> - </tbody> -</table> - - - -<h2 id="response-format">Response format</h2> -<p> - Responses are in JSON format, with the following fields: -</p> -<table class="table"> - <thead> - <tr> - <th>Element</th> - <th>Parent</th> - <th>Type</th> - <th>Description</th> - </tr> - </thead> - <tbody> - <tr> - <td><p id="services">services</p></td> - <td></td> - <td>Object</td> - <td>Root for /metrics/v1/values. Contains service objects.</td> - </tr> - <tr> - <td><p id="name">name</p></td> - <td>services</td> - <td>String</td> - <td>Service name.</td> - </tr> - <tr> - <td><p id="timestamp">timestamp</p></td> - <td>services</td> - <td>Number</td> - <td>EPOCH in seconds - time of metrics fetch from service.</td> - </tr> - <tr> - <td><p id="status">status</p></td> - <td>services</td> - <td>Object</td> - <td>Status from metrics fetch.</td> - </tr> - <tr> - <td><p id="code">code</p></td> - <td>status</td> - <td>String</td> - <td> - The status for each service is one of: - <ul> - <li><code>up</code></li> - <li><code>down</code></li> - <li><code>unknown</code></li> - </ul> - <code>unknown</code> is used if the service seems to be alive, but does not report metrics. - </td> - </tr> - <tr> - <td><p id="description">description</p></td> - <td>status</td> - <td>String</td> - <td>Textual status.</td> - </tr> - <tr> - <td><p id="metrics">metrics</p></td> - <td>services</td> - <td>Array</td> - <td>Array of metric objects.</td> - </tr> - <tr> - <td><p id="values">values</p></td> - <td>metrics</td> - <td>Object</td> - <td>Set of metric-name/value pairs.</td> - </tr> - <tr> - <td><p id="dimensions">dimensions</p></td> - <td>metrics</td> - <td>Object</td> - <td>Set of metric dimension-name/value pairs.</td> - </tr> - </tbody> -</table> diff --git a/en/reference/api/metrics-v1.mdx b/en/reference/api/metrics-v1.mdx new file mode 100644 index 0000000000..bbdc6e1e6d --- /dev/null +++ b/en/reference/api/metrics-v1.mdx @@ -0,0 +1,110 @@ +--- +title: "/metrics/v1 API reference" +description: "/metrics/v1 API reference in Vespa applications." +--- +<table className="vespa-table"> + <thead> + <tr> + <th>HTTP request</th> + <th>metrics/v1 operation</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>GET</td> + <td></td> + <td></td> + </tr> + <tr> + <td></td> + <td>Node metrics</td> + <td><code>{`/metrics/v1/values`}</code> See <a href="/en/operations/self-managed/monitoring#metrics-v1-values" aria-label="monitoring">monitoring</a> for examples.</td> + </tr> + </tbody> +</table> + +<table className="vespa-table"> + <thead> + <tr> + <th>Parameter</th> + <th>Type</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>consumer</td> + <td>String</td> + <td>Specify response <a href="/en/reference/applications/services/admin#consumer" aria-label="consumer">consumer</a>, i.e. set of metrics. An unknown / empty value will return the <code>{`default`}</code> metric set. Built-in: <code>{`default`}</code> - see <a href="/en/reference/operations/metrics/default-metric-set" aria-label="DefaultMetrics">DefaultMetrics</a>. <code>{`vespa`}</code> - see <a href="/en/reference/operations/metrics/vespa-metric-set" aria-label="VespaMetricSet">VespaMetricSet</a>.</td> + </tr> + </tbody> +</table> + +<table className="vespa-table"> + <thead> + <tr> + <th>Element</th> + <th>Parent</th> + <th>Type</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>services</td> + <td></td> + <td>Object</td> + <td>Root for /metrics/v1/values. Contains service objects.</td> + </tr> + <tr> + <td>name</td> + <td>services</td> + <td>String</td> + <td>Service name.</td> + </tr> + <tr> + <td>timestamp</td> + <td>services</td> + <td>Number</td> + <td>EPOCH in seconds - time of metrics fetch from service.</td> + </tr> + <tr> + <td>status</td> + <td>services</td> + <td>Object</td> + <td>Status from metrics fetch.</td> + </tr> + <tr> + <td>code</td> + <td>status</td> + <td>String</td> + <td>The status for each service is one of: <code>{`up`}</code> <code>{`down`}</code> <code>{`unknown`}</code> <code>{`unknown`}</code> is used if the service seems to be alive, but does not report metrics.</td> + </tr> + <tr> + <td>description</td> + <td>status</td> + <td>String</td> + <td>Textual status.</td> + </tr> + <tr> + <td>metrics</td> + <td>services</td> + <td>Array</td> + <td>Array of metric objects.</td> + </tr> + <tr> + <td>values</td> + <td>metrics</td> + <td>Object</td> + <td>Set of metric-name/value pairs.</td> + </tr> + <tr> + <td>dimensions</td> + <td>metrics</td> + <td>Object</td> + <td>Set of metric dimension-name/value pairs.</td> + </tr> + </tbody> +</table> + diff --git a/en/reference/api/metrics-v2.html b/en/reference/api/metrics-v2.html deleted file mode 100644 index a035d4675d..0000000000 --- a/en/reference/api/metrics-v2.html +++ /dev/null @@ -1,140 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "/metrics/v2 API reference" -redirect_from: -- /en/reference/metrics-v2.html ---- - -<p> - The <em>application metrics API</em> is available on each <em>node</em> at - the metrics proxy port, default <em>http://host:19092/metrics/v2/values</em>. - A container service on the same node as the metrics proxy might forward <em>/metrics/v2/values</em> - on its own port, normally 8080. -</p> -<p> - <em>/metrics/v2/values</em> is an aggregation of the application instance nodes <em>/metrics/v1/values</em>. - Refer to <a href="/en/operations/self-managed/monitoring.html">monitoring</a> - for an overview of nodes, services and metrics APIs. <!-- ToDo: link to generic monitoring doc once moved --> -</p> - - - -<h2 id="http-requests">HTTP requests</h2> -<table class="table"> - <thead> - <tr> - <th>HTTP request</th> - <th>metrics/v2 operation</th> - <th>Description</th> - </tr> - </thead> - <tbody> - <tr> - <th>GET</th> - <td colspan="2"> - <p id="get"> - </p> - </td> - </tr> - <tr> - <td></td> - <th>Application metrics</th> - <td> - <p id="metrics-v2-values"></p> - <pre>/metrics/v2/values</pre> - <p>See <a href="/en/operations/self-managed/monitoring.html#metrics-v2-values">monitoring</a> for examples.</p> - </td> - </tr> - </tbody> -</table> - - - -<h2 id="request-parameters">Request parameters</h2> -<table class="table"> - <thead> - <tr> - <th>Parameter</th> - <th>Type</th> - <th>Description</th> - </tr> - </thead><tbody> -<tr> - <td>consumer</td> - <td>String</td> - <td> - <p id="consumer"> - Specify response <a href="../applications/services/admin.html#consumer">consumer</a>, i.e. set of metrics. - See <a href="metrics-v1.html#consumer">metrics/v1</a> for details. - </td> -</tr> -</tbody> -</table> - - - -<h2 id="http-status-codes">HTTP status codes</h2> -<p> - Non-exhaustive list of status codes: -</p> -<table class="table"> - <thead> - <tr> - <th>Code</th> - <th>Description</th> - </tr> - </thead> - <tbody> - <tr> - <td>200</td> - <td>OK.</td> - </tr> <!-- ToDo: more codes --> - </tbody> -</table> - - - -<h2 id="response-format">Response format</h2> -<p> - Responses are in JSON format, with the following fields: -</p> -<table class="table"> - <thead> - <tr> - <th>Element</th> - <th>Parent</th> - <th>Type</th> - <th>Description</th> - </tr> - </thead> - <tbody> - <tr> - <td><p id="nodes">nodes</p></td> - <td></td> - <td>Array</td> - <td>Root element for /metrics/v2/values. Returns an array of node objects with metrics</td> - </tr> - <tr> - <td><p id="hostname">hostname</p></td> - <td>nodes</td> - <td>String</td> - <td>Node hostname.</td> - </tr> - <tr> - <td><p id="role">role</p></td> - <td>nodes</td> - <td>String</td> - <td>Node role.</td> - </tr> - <tr> - <td><p id="services">services</p></td> - <td>nodes</td> - <td>Array</td> - <td> - Array of service objects, the are services running on the node. - The <code>service</code> object is defined in - <a href="metrics-v1.html#metrics-v1-values">/metrics/v1/values</a>. - </td> - </tr> - </tbody> -</table> diff --git a/en/reference/api/metrics-v2.mdx b/en/reference/api/metrics-v2.mdx new file mode 100644 index 0000000000..e3321a0ace --- /dev/null +++ b/en/reference/api/metrics-v2.mdx @@ -0,0 +1,43 @@ +--- +title: "/metrics/v2 API reference" +description: "/metrics/v2 API reference in Vespa applications." +--- +<table className="vespa-table"> + <thead> + <tr> + <th>HTTP request</th> + <th>metrics/v2 operation</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>GET</td> + <td></td> + <td></td> + </tr> + <tr> + <td></td> + <td>Application metrics</td> + <td><code>{`/metrics/v2/values`}</code> See <a href="/en/operations/self-managed/monitoring#metrics-v2-values" aria-label="monitoring">monitoring</a> for examples.</td> + </tr> + </tbody> +</table> + +<table className="vespa-table"> + <thead> + <tr> + <th>Parameter</th> + <th>Type</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>consumer</td> + <td>String</td> + <td>Specify response <a href="/en/reference/applications/services/admin#consumer" aria-label="consumer">consumer</a>, i.e. set of metrics. See <a href="/en/reference/api/metrics-v1#consumer" aria-label="metrics/v1">metrics/v1</a> for details.</td> + </tr> + </tbody> +</table> + diff --git a/en/reference/api/prometheus-v1.html b/en/reference/api/prometheus-v1.html deleted file mode 100644 index b667d0b943..0000000000 --- a/en/reference/api/prometheus-v1.html +++ /dev/null @@ -1,112 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "/prometheus/v1 API reference" -redirect_from: -- /en/reference/prometheus-v1.html ---- - -<p> - The <em>prometheus node metrics API</em> is available on each <em>node</em> at - the metrics proxy port, default <em>http://host:19092/prometheus/v1/values</em>. -</p> -<p> - This API has the same content as in <em>/metrics/v1/values</em>, - in a <a href="https://prometheus.io/docs/instrumenting/exposition_formats/">format</a> that can be - scraped by <a href="https://prometheus.io/docs/introduction/overview/">Prometheus</a>. -</p> -<p> - Refer to <a href="/en/operations/self-managed/monitoring.html">monitoring</a> - for an overview of nodes, services and metrics APIs. <!-- ToDo: link to generic monitoring doc once moved --> -</p> - - - -<h2 id="http-requests">HTTP requests</h2> -<table class="table"> - <thead> - <tr> - <th>HTTP request</th> - <th>prometheus/v1 operation</th> - <th>Description</th> - </tr> - </thead> - <tbody> - <tr> - <th>GET</th> - <td colspan="2"> - <p id="get"> - </p> - </td> - </tr> - <tr> - <td></td> - <th>Node metrics</th> - <td> - <p id="prometheus-v1-values"></p> - <pre>/prometheus/v1/values</pre> - <p> - See <a href="/en/operations/self-managed/monitoring.html#prometheus-v1-values">monitoring</a> for examples. - </p> - </td> - </tr> - </tbody> -</table> - - - -<h2 id="request-parameters">Request parameters</h2> -<table class="table"> - <thead> - <tr> - <th>Parameter</th> - <th>Type</th> - <th>Description</th> - </tr> - </thead><tbody> -<tr> - <td>consumer</td> - <td>String</td> - <td> - <p id="consumer"> - Specify response <a href="../applications/services/admin.html#consumer">consumer</a>, i.e. set of metrics. - An unknown / empty value will return the <code>default</code> metric set. - Built-in (note: case-sensitive): - </p> - <ul> - <li><code>default</code></li> - <li><code>Vespa</code></li> - </ul> - </td> -</tr> -</tbody> -</table> - - - -<h2 id="http-status-codes">HTTP status codes</h2> -<p> - Non-exhaustive list of status codes: -</p> -<table class="table"> - <thead> - <tr> - <th>Code</th> - <th>Description</th> - </tr> - </thead> - <tbody> - <tr> - <td>200</td> - <td>OK.</td> - </tr> <!-- ToDo: more codes --> - </tbody> -</table> - - - -<h2 id="response-format">Response format</h2> -<p> - Responses are in Prometheus format, the values are the same as in - <a href="metrics-v1.html#metrics-v1-values">/metrics/v1/values</a> -</p> - diff --git a/en/reference/api/prometheus-v1.mdx b/en/reference/api/prometheus-v1.mdx new file mode 100644 index 0000000000..5adb2e418e --- /dev/null +++ b/en/reference/api/prometheus-v1.mdx @@ -0,0 +1,43 @@ +--- +title: "/prometheus/v1 API reference" +description: "/prometheus/v1 API reference in Vespa applications." +--- +<table className="vespa-table"> + <thead> + <tr> + <th>HTTP request</th> + <th>prometheus/v1 operation</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>GET</td> + <td></td> + <td></td> + </tr> + <tr> + <td></td> + <td>Node metrics</td> + <td><code>{`/prometheus/v1/values`}</code> See <a href="/en/operations/self-managed/monitoring#prometheus-v1-values" aria-label="monitoring">monitoring</a> for examples.</td> + </tr> + </tbody> +</table> + +<table className="vespa-table"> + <thead> + <tr> + <th>Parameter</th> + <th>Type</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>consumer</td> + <td>String</td> + <td>Specify response <a href="/en/reference/applications/services/admin#consumer" aria-label="consumer">consumer</a>, i.e. set of metrics. An unknown / empty value will return the <code>{`default`}</code> metric set. Built-in (note: case-sensitive): <code>{`default`}</code> <code>{`Vespa`}</code></td> + </tr> + </tbody> +</table> + diff --git a/en/reference/api/query.html b/en/reference/api/query.html deleted file mode 100644 index 24300a9ff9..0000000000 --- a/en/reference/api/query.html +++ /dev/null @@ -1,2518 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Query API Reference" -redirect_from: -- /en/reference/search-api-reference.html -- /en/reference/query-api-reference.html ---- - -<p>Refer to the <a href="../../querying/query-api.html">Query API guide</a> for API examples.</p> - -<p>All the request parameters listed below can be set in query profiles. -The first four blocks of properties are also modeled as -<a href="../../querying/query-profiles.html#query-profile-types">query profile types</a>. -These types can be referred from query profiles (and inheriting types) -to provide type checking on the parameters.</p> - -<p>Parameters have <i>full name</i> and can have one or more shorter aliases, shown in square brackets below. -Both names can be used in requests and query profiles. -Aliases are case-insensitive in requests.</p> - -<p>When accessing parameters from Searcher components, the full names correspond to the path from -the root Query object to that parameter.</p> - - -<h2 id="parameters">Parameters</h2> -<dl> -<dt>Query</dt> -<dd> - <ul> - <li><a href="#yql">yql</a></li> - </ul> -</dd> - -<dt>Native Execution Parameters</dt> -<dd> - <ul> - <li><a href="#hits">hits</a> [<em>count</em>]</li> - <li><a href="#offset">offset </a>[<em>start</em>]</li> - <li><a href="#queryprofile">queryProfile</a></li> - <li><a href="#groupingsessioncache">groupingSessionCache</a></li> - <li><a href="#searchchain">searchChain</a></li> - <li><a href="#timeout">timeout</a></li> - </ul> -</dd> - -<dt>Query Model</dt> -<dd> - <ul> - <li><a href="#model.defaultindex">model.defaultIndex</a> [<em>default-index</em>]</li> - <li><a href="#model.encoding">model.encoding</a> [<em>encoding</em>]</li> - <li><a href="#model.filter">model.filter</a> [<em>filter</em>]</li> - <li><a href="#model.locale">model.locale</a> [<em>locale</em>]</li> - <li><a href="#model.language">model.language</a> [<em>lang, language</em>]</li> - <li><a href="#model.querystring">model.queryString</a> [<em>query</em>]</li> - <li><a href="#model.restrict">model.restrict</a> [<em>restrict</em>]</li> - <li><a href="#model.searchpath">model.searchPath</a> [<em>path</em>]</li> - <li><a href="#model.searchgroup">model.searchGroup</a> [<em>searchgroup</em>]</li> - <li><a href="#model.sources">model.sources</a> [<em>search, sources</em>]</li> - <li><a href="#model.type">model.type</a> [<em>type</em>]</li> - <li><a href="#model.type.composite">model.type.composite</a></li> - <li><a href="#model.type.tokenization">model.type.tokenization</a></li> - <li><a href="#model.type.syntax">model.type.syntax</a></li> - <li><a href="#model.type.profile">model.type.profile</a></li> - <li><a href="#model.type.isYqlDefault">model.type.isYqlDefault</a></li> - </ul> -</dd> - -<dt>Ranking</dt> -<dd> - <ul> - <li><a href="#ranking.elementGap">ranking.elementGap.<em>fieldName</em></a></li> - <li><a href="#ranking.features">ranking.features</a> [<em>input</em>, <em>rankfeature</em>]</li> - <li><a href="#ranking.freshness">ranking.freshness</a></li> - <li><a href="#ranking.keeprankcount">ranking.keepRankCount</a></li> - <li><a href="#ranking.totalkeeprankcount">ranking.totalKeepRankCount</a></li> - <li><a href="#ranking.listfeatures">ranking.listFeatures</a> [<em>rankfeatures</em>]</li> - <li><a href="#ranking.matchPhase">ranking.matchPhase</a></li> - <li><a href="#ranking.matching">ranking.matching</a></li> - <li><a href="#ranking.profile">ranking.profile</a> [<em>ranking</em>]</li> - <li><a href="#ranking.properties">ranking.properties</a> [<em>rankproperty</em>]</li> - <li><a href="#ranking.querycache">ranking.queryCache</a></li> - <li><a href="#ranking.rankscoredroplimit">ranking.rankScoreDropLimit</a></li> - <li><a href="#ranking.matchphase.attribute">ranking.matchphase.attribute</a></li> - <li><a href="#ranking.matchphase.totalmaxhits">ranking.matchPhase.totalMaxHits</a></li> - <li><a href="#ranking.matchphase.maxhits">ranking.matchPhase.maxHits</a></li> - <li><a href="#ranking.matchphase.ascending">ranking.matchPhase.ascending</a></li> - <li><a href="#ranking.matchphase.diversity.attribute">matchPhase.diversity.attribute</a></li> - <li><a href="#ranking.matchphase.diversity.mingroups">matchPhase.diversity.minGroups</a></li> - <li><a href="#ranking.secondphase.totalrerankcount">ranking.secondPhase.totalRerankCount</a></li> - <li><a href="#ranking.secondphase.rerankcount">ranking.secondPhase.rerankCount</a></li> - <li><a href="#ranking.secondphase.rankscoredroplimit">ranking.secondPhase.rankScoreDropLimit</a></li> - <li><a href="#ranking.globalphase.rankscoredroplimit">ranking.globalPhase.rankScoreDropLimit</a></li> - <li><a href="#ranking.globalphase.rerankcount">ranking.globalPhase.rerankCount</a></li> - <li><a href="#ranking.significance.useModel">ranking.significance.useModel</a></li> - <li><a href="#ranking.softtimeout.enable">ranking.softtimeout.enable</a></li> - <li><a href="#ranking.sorting">ranking.sorting</a> [<em>sorting</em>]</li> - </ul> -</dd> - -<dt>Presentation</dt> -<dd> - <ul> - <li><a href="#presentation.bolding">presentation.bolding</a> [<em>bolding</em>]</li> - <li><a href="#presentation.format">presentation.format</a> [<em>format</em>]</li> - <li><a href="#presentation.template">presentation.template</a></li> - <li><a href="#presentation.summary">presentation.summary</a> [<em>summary</em>]</li> - <li><a href="#presentation.timing">presentation.timing</a></li> - </ul> -</dd> - -<dt>Grouping</dt> -<dd> - <ul> - <li><a href="#select">select</a></li> - <li><a href="#collapse.summary">collapse.summary</a></li> - <li><a href="#collapsefield">collapsefield</a></li> - <li><a href="#collapsesize">collapsesize</a> - <li><a href="#collapsesize.fieldname">collapsesize</a> [<em>fieldname</em>]</li> - <li><a href="#grouping.defaultmaxgroups">grouping.defaultMaxGroups</a></li> - <li><a href="#grouping.defaultmaxhits">grouping.defaultMaxHits</a></li> - <li><a href="#grouping.globalmaxgroups">grouping.globalMaxGroups</a></li> - <li><a href="#grouping.defaultprecisionfactor">grouping.defaultPrecisionFactor</a></li> - <li><a href="#timezone">timezone</a></li> - </ul> -</dd> - -<dt>Streaming</dt> -<dd> - <ul> - <li><a href="#streaming.groupname">streaming.groupname</a></li> - <li><a href="#streaming.selection">streaming.selection</a></li> - <li><a href="#streaming.maxbucketspervisitor">streaming.maxbucketspervisitor</a></li> - </ul> -</dd> - -<dt>Tracing</dt> -<dd> - <ul> - <li><a href="#trace.profile">trace.profile</a></li> - <li><a href="#trace.level">trace.level</a></li> - <li><a href="#trace.explainlevel">trace.explainLevel</a></li> - <li><a href="#trace.profiledepth">trace.profileDepth</a></li> - <li><a href="#trace.profiling.matching.depth">trace.profiling.matching.depth</a></li> - <li><a href="#trace.profiling.firstPhaseRanking.depth">trace.profiling.firstPhaseRanking.depth</a></li> - <li><a href="#trace.profiling.secondPhaseRanking.depth">trace.profiling.secondPhaseRanking.depth</a></li> - <li><a href="#trace.timestamps">trace.timestamps</a></li> - <li><a href="#trace.query">trace.query</a></li> - </ul> -</dd> - -<dt>Semantic Rules</dt> -<dd> - <ul> - <li><a href="#rules.off">rules.off</a></li> - <li><a href="#rules.rulebase">rules.rulebase</a></li> - <li><a href="#tracelevel.rules">tracelevel.rules</a></li> - </ul> -</dd> - -<dt>Dispatch</dt> -<dd> - <ul> - <li><a href="#dispatch.topkprobability">dispatch.topKProbability</a></li> - </ul> -</dd> - -<dt>Other</dt> -<dd> - <ul> - <li><a href="#recall">recall</a></li> - <li><a href="#user">user</a></li> - <li><a href="#hitcountestimate">hitcountestimate</a></li> - <li><a href="#metrics.ignore">metrics.ignore</a></li> - <li><a href="#weakand.replace">weakAnd.replace</a></li> - <li><a href="#wand.hits">wand.hits</a></li> - <li><a href="#sorting.degrading">sorting.degrading</a></li> - <li><a href="#nocache">noCache</a></li> - </ul> -</dd> -</dl> - - - -<h2 id="query">Query</h2> -<table class="table"> - <thead> - <tr> - <th>Parameter</th> - <th>Alias</th> - <th>Type</th> - <th>Default</th> - <th>Description</th> - </tr> - </thead> - <tbody> - <tr> - <th>yql</th> - <td></td> - <td>String</td> - <td></td> - <td> - <p id="yql"> - See the <a href="../../querying/query-language.html">YQL query guide</a> for examples, - and the <a href="../querying/yql.html">reference</a> for details. - </p> - </td> - </tr> - </tbody> -</table> - - - -<h2 id="native-execution-parameters">Native Execution Parameters</h2> -<p> -These parameters are defined in the <code>native</code> query profile type. -</p> -<table class="table"> - <thead> - <tr> - <th>Parameter</th> - <th>Alias</th> - <th>Type</th> - <th>Default</th> - <th>Description</th> - </tr> - </thead> - <tbody> - <tr> - <th>hits</th> - <td>count</td> - <td>Number</td> - <td>10</td> - <td> - <p id="hits"> - A positive integer, including 0. - The maximum number of hits to return from the result set. - </p> - <p> - <code>hits</code> is capped at <code>maxHits</code>, default 400. - <code>maxHits</code> can be set in a <a href="../../querying/query-profiles.html">query profile</a>. - </p> - <p> - Number of hits can also be set in <a href="../querying/yql.html#limit-offset">YQL</a>. - </p> - </td> - </tr> - <tr> - <th>offset</th> - <td>start</td> - <td>Number</td> - <td>0</td> - <td> - <p id="offset"> - To implement pagination: The number of hits to skip when returning the result. - A positive integer, including 0. - </p> - <p> - <code>offset</code> is capped at <code>maxOffset</code>, default 1000. - <code>maxOffset</code> can be set in a <a href="../../querying/query-profiles.html">query profile</a>. - </p> - <p> - Offset can also be set in <a href="../querying/yql.html#limit-offset">YQL</a>. - </p> - </td> - </tr> - <tr> - <th>queryProfile</th> - <td></td> - <td>String</td> - <td><code>default</code></td> - <td> - <p id="queryprofile"> - A query profile id with format <code>name:version</code>, - where version can be omitted or partially specified, - e.g. <code>myprofile:2.1</code>. - A <a href="../../querying/query-profiles.html">query profile</a> has default properties for a query. - The default query profile is named <em>default</em>. - </td> - </tr> - <tr> - <th>groupingSessionCache</th> - <td></td> - <td>Boolean</td> - <td>true</td> - <td> - <p id="groupingsessioncache"> - Set to true to enable grouping session cache. - See the <a href="../querying/grouping-language.html#grouping-session-cache">grouping reference</a> for details. - </p> - </td> - </tr> - <tr> - <th>searchChain</th> - <td></td> - <td>String</td> - <td><code>default</code></td> - <td> - <p id="searchchain"> - A search chain id with format <code>name:version</code>, - where version can be omitted or partially specified, - e.g. <code>mychain:2.1.3</code>. - The <a href="../../applications/chaining.html">search chain</a> - initially invoked when processing the query. - This search chain may invoke other chains. - </p> - </td> - </tr> - <tr> - <th>timeout</th> - <td></td> - <td>String</td> - <td>0.5s</td> - <td> - <p id="timeout"> - Positive floating point number with an optional unit. Default unit - is seconds (s), valid unit strings are e.g. <em>ms</em> and <em>s</em>. To set - a timeout of one minute, the argument could be set to <em>60 s</em>. - Space between the number and the unit is optional. - </p> - <p> - It specifies the overall timeout of the query execution - and can be defined in a <a href="../../querying/query-profiles.html">query profile</a>. - Different classes of queries can then easily have a different latency budget/timeout using different profiles. - </p> - <p> - At timeout, the hits generated thus far are returned, - refer to <a href="#ranking.softtimeout.enable">ranking.softtimeout.enable</a> - for details on HTTP status codes and response elements. - </p> - <p>Refer to the <a href="../../querying/query-api.html#timeout">Query API guide</a> for more details on timeout handling.</p> - </td> - </tr> - </tbody> -</table> - - - -<h2 id="query-model">Query Model Parameters</h2> -<table class="table"> - <thead> - <tr> - <th>Parameter</th> - <th>Alias</th> - <th>Type</th> - <th>Default</th> - <th>Description</th> - </tr> - </thead> - <tbody> - <tr> - <th>model.defaultIndex</th> - <td>default-index</td> - <td>String</td> - <td><code>default</code></td> - <td> - <p id="model.defaultindex"> - An index name. - The field which is searched for query terms which doesn't explicitly specify an index. - Also see the <a href="../querying/yql.html#defaultindex">defaultIndex</a> query annotation. - </p> - </td> - </tr> - <tr> - <th>model.encoding</th> - <td>encoding</td> - <td>String</td> - <td><code>utf-8</code></td> - <td> - <p id="model.encoding"> - Encoding names or aliases defined in the - <a href="https://www.iana.org/assignments/character-sets/character-sets.xhtml">IANA character sets</a>. - Sets the encoding to use when returning a result. - The query is always encoded as UTF-8, independently of how the result will be encoded. - </p> - <p> - The encodings <code>big5</code>, <code>euc-jp</code>, <code>euc-kr</code>, <code>gb2312</code>, - <code>iso-2022-jp</code> and <code>shift-jis</code> - also influences how <a href="../../linguistics/linguistics-opennlp.html#tokenization">tokenization</a> is done - in the absence of an explicit language setting. - </p> - </td> - </tr> - <tr> - <th>model.filter</th> - <td>filter</td> - <td>String</td> - <td></td> - <td> - <p id="model.filter"> - A filter string in the <a href="../querying/simple-query-language.html">Simple Query Language</a>. - Sets a filter to be combined with the <a href="#model.querystring">model.queryString</a>. - Typical use of a filter is to add machine generated or preferences based filter terms to the user query. - </p> - <p> - Terms which are passed in the filter are not <a href="#presentation.bolding">bolded</a>. - The filter is parsed the same way as a query of type <code>any</code>, - the full syntax is available. - The positive terms (preceded by +) and phrases act as AND filters, - the negative terms (preceded by -) act as NOT filters, - while the unprefixed terms will be used to RANK the results. - Unless the query has no positive terms, - the filter will only restrict and influence ranking of the result set, - never cause more matches than the query. - </p> - <p> - The <a href="#model.querystring">model.queryString</a> must be present for this to have any effect. - To add filters to the YQL string, use query profiles. See <a href="/en/querying/query-profiles.html#example">example</a>. - </p> - </td> - </tr> - <tr> - <th>model.locale</th> - <td>locale</td> - <td>String</td> - <td></td> - <td> - <p id="model.locale"> - A language tag from <a href="https://www.rfc-editor.org/rfc/rfc5646">RFC 5646</a>. - Sets the locale and language to use when parsing queries from a language tag, such as <code>en-US</code>. - This attribute should always be set when it is known. - If this parameter is not set, it will be guessed from the query and encoding, - and default to english if it cannot be guessed. - </p> - </td> - </tr> - <tr> - <th>model.language</th> - <td>lang, language</td> - <td>String</td> - <td></td> - <td> - <p id="model.language"> - A language tag from <a href="https://www.rfc-editor.org/rfc/rfc5646">RFC 5646</a>, - but allowing underscore instead of dash as separator character. - A legacy alternative to locale. When this value is accessed, - underscores will be replaced by dashes in the returned value. - Also see the <a href="../querying/yql.html#language">language</a> query term annotation. - <!-- ToDo: refer to model.locale for how this will default to English. - Also link to linguistics docs for examples / more info ... --> - </p> - </td> - </tr> - <tr> - <th>model.queryString</th> - <td>query</td> - <td>String</td> - <td></td> - <td> - <p id="model.querystring"> - A query string in the <a href="../querying/simple-query-language.html">Simple Query Language</a>. - It is combined with <a href="#model.filter">model.filter</a>. - See the <a href="../querying/yql.html#userquery">userQuery</a> operator for how to combine with YQL. - Can also be used without YQL. - </p> - </td> - </tr> - <tr> - <th>model.restrict</th> - <td>restrict</td> - <td>String</td> - <td></td> - <td> - <p id="model.restrict"> - A comma-delimited list of document type (schema) names, defaulting to all schemas if not set. - See <a href="../../querying/federation.html">federation</a>. - </p> - <p> - Use <a href="#model.sources">model.sources</a> to restrict to content cluster names or other source names. - </p> - </td> - </tr> - <tr> - <th>model.searchPath</th> - <td>path</td> - <td>String</td> - <td></td> - <td> - <p id="model.searchpath"> - Specification of which content nodes a query should be sent to. - This is useful for debugging/monitoring - and when using <a href="../../ranking/phased-ranking.html#rank-phase-statistics">Rank phase statistics</a>. - Note that in a content cluster with flat distribution (i.e. no <group> element in <em>services.xml</em>), - there is 1 implicit group. - </p> - <p> - If not set, defaults to all nodes in one group, selected by load balancing. - </p> - <p><code>searchpath::ELEMENT [';' ELEMENT]*</code></p> - <p><code>ELEMENT::NODE ['/' GROUP]</code></p> - <p><code>NODE::EXP [',' EXP]*</code></p> - <p><code>EXP::NUM | RANGE</code></p> - <p><code>GROUP::NUM | '*'</code></p> - <p><code>RANGE::'['NUM ',' NUM ' >'</code></p> - <p>Examples:</p> - <ul> - <li><code>7/3</code> = node 7, group 3.</li> - <li><code>7/</code> = node 7, any group.</li> - <li><code>*/0</code> = all nodes in group 0</li> - <li><code>7,1,9/0</code> = nodes 1,7 and 9, group 0.</li> - <li><code>1,[3,9>/0</code> = nodes 1,3,4,5,6,7,8, group 0.</li> - </ul> - </td> - </tr> - <tr> - <th>model.searchGroup</th> - <td>searchgroup</td> - <td>Integer</td> - <td></td> - <td> - <p id="model.searchgroup"> - The index of the group that should be preferred if possible when executing this query, for - <a href="../../content/elasticity.html#pinning-groups">group pinning</a>. - </td> - </tr> - <tr> - <th>model.sources</th> - <td>search, sources</td> - <td>String</td> - <td></td> - <td> - <p id="model.sources"> - A comma-separated list of content cluster names or other source names, - defaulting to all sources/clusters if not set. - The names of the sources to query, e.g., one or more content clusters and/or federated sources - - see <a href="/en/querying/federation.html">federation</a>. - </p> - <p> - Use <a href="#model.restrict">model.restrict</a> to only search a subset of the schemas in a cluster. - </p> - </td> - </tr> - <tr> - <th>model.type</th> - <td>type</td> - <td>String</td> - <td><code>weakAnd</code></td> - <td> - <p id="model.type"> - Sets all the model.type parameters (composite, tokenization, and syntax) specifying how to - parse a <a href="#model.querystring">model.queryString</a> parameter at once, - according to the given table: - </p> - <table> - <tr><th rowspan="2">Value <th colspan="3">Results in</tr> - <tr> <th>composite<th>tokenization<th>syntax</tr> - <tr><td>all <td>and <td>internal <td>simple</tr> - <tr><td>any <td>or <td>internal <td>simple</tr> - <tr><td>linguistics<td>weakAnd <td>linguistics <td>none </tr> - <tr><td>phrase <td>phrase <td>internal <td>none </tr> - <tr><td>tokenize <td>weakAnd <td>internal <td>none </tr> - <tr><td>weakAnd <td>weakAnd <td>internal <td>simple</tr> - <tr><td>web <td>and <td>internal <td>web </tr> - <tr><td>yql <td>and <td>internal <td>yql </tr> - </table> - <p> - Also see <a href="../querying/yql.html#userinput">YQL grammar</a>. - </p> - </td> - </tr> - <tr> - <th>model.type.composite</th> - <td></td> - <td>String</td> - <td><code>Determined by model.type</code></td> - <td> - <p id="model.type.composite"> - Sets the Vespa query composite type that will collect parsed terms of the query by default. - </p> - <table> - <tr><td>and <td>Create an AndItem which only matches if <i>all</i> terms are present.</tr> - <tr><td>near <td>Create a NearItem which matches if all the terms appear near each other (gap of 1 by default).</tr> - <tr><td>oNear <td>Create an ONearItem which matches if all the terms appear near each other (gap of 1 by default), in the given order.</tr> - <tr><td>or <td>Create an OrItem which matches if <i>any</i> of the terms are present.</tr> - <tr><td>phrase <td>Create a PhraseItem which matches if all the terms are present in the given order with no gaps.</tr> - <tr><td>weakAnd<td>Create a <a href="https://docs.vespa.ai/en/using-wand-with-vespa.html#weakand">WeakAndItem</a> which has the semantics of <code>or</code> with performance approaching <code>and</code>.</tr> - </table> - </td> - </tr> - <tr> - <th>model.type.tokenization</th> - <td></td> - <td>String</td> - <td><code>Determined by model.type</code></td> - <td> - <p id="model.type.tokenization"> - Sets the tokenizer used to split the query string into tokens. - </p> - <table> - <tr><td>internal <td>Use the tokenizer built into the query parser.</tr> - <tr><td>linguistics<td>Pass the full query string as-is to the linguistics component - for tokenization, exactly as on the indexing side, and collect any text and numeric token - returned as-is, with no further stemming or normalization even when specified in the schema. - This is only supported in conjunction - with the <code>none</code> syntax option. - </table> - </td> - </tr> - <tr> - <th>model.type.syntax</th> - <td></td> - <td>String</td> - <td><code>Determined by model.type</code></td> - <td> - <p id="model.type.syntax"> - Sets the syntax used to interpret the query string. Options: - </p> - <table> - <tr><td>none</td><td>No syntax: Disregard any non-searchable terms</td></tr> - <tr><td>simple</td><td>Use the <a href="../querying/simple-query-language.html">simple query language</a> suitable for end users.</td></tr> - <tr><td>web</td><td>Like the <a href="../querying/simple-query-language.html">simple query language</a>, but '+' - in front of a term means "search for this term as-is", and 'a OR b' (capital OR) - means match either a or b.</td></tr> - <tr><td>yql</td><td>Parse as a <a href="https://docs.vespa.ai/en/reference/query-language-reference.html">YQL query</a>.</td></tr> - </table> - </td> - </tr> - <tr> - <th>model.type.profile</th> - <td></td> - <td>String</td> - <td><code><i>(null)</i></code></td> - <td> - <p id="model.type.profile">Overrides the linguistics profile assigned to the field(s) searched. - The linguistics profile is used to choose the processing done in the - <a href="../../linguistics/linguistics.html">linguistics component</a>.</p> - </td> - </tr> - <tr> - <th>model.type.isYqlDefault</th> - <td></td> - <td>Boolean</td> - <td><code>false</code></td> - <td> - <p id="model.type.isYqlDefault"> - Whether the model.type settings should be used as the default settings for terms in YQL queries. - With this parameter turned on, the model.type settings become the default "grammar" settings in - userQuery, and with tokenization set to <code>linguistics</code> this will also cause "contains" terms - to not undergo stemming, normalization and lowercasing as separate operations, as using this mode - delegates all token processing to a single pass through the lingustics module. - </p> - </td> - </tr> - </tbody> -</table> - - -<h2 id="ranking">Ranking</h2> -<table class="table"> - <thead> - <tr> - <th>Parameter</th> - <th>Alias</th> - <th>Type</th> - <th>Default</th> - <th>Description</th> - </tr> - </thead> - <tbody> - <tr> - <th>ranking.location</th> - <td></td> - <td>String</td> - <td></td> - <td> - <p id="ranking.location"> - See <a href="../../querying/geo-search.html">Geo search</a>. - Point (two-dimensional location) to use as base for location ranking. - </p> - {% include deprecated.html content=" Deprecated in favor of adding a - <a href='../querying/yql.html#geolocation'>geoLocation</a> - item to the query tree. - Use inside a <a href='../querying/yql.html#rank'>rank</a> operator - if it should be used only for ranking)."%} - </td> - </tr> - <tr> - <th>ranking.features<br/>.<em>featurename</em></th> - <td>input<br/>.<em>featurename</em>, rankfeature<br/>.<em>featurename</em></td> - <td>String</td> - <td></td> - <td> - <p id="ranking.features"> - Set a query rank feature input to a value. - The key must be a query feature - <code>query(anyname)</code>, - and the value must be a double, string (to be hashed to a double), - or a tensor matching the <a href="../schemas/schemas.html#inputs">declared input type</a> - on <a href="../ranking/tensor.html#tensor-literal-form">tensor literal form</a> - - see the <a href="../../ranking/tensor-user-guide.html#querying-with-tensors">tensor user guide</a>. - Examples: - </p> - <p><code>input.query(userageDouble)=42.1</code></p> - <p><code>input.query(stringToBeHashed)=abcd</code></p> - <p><code>input.query(myIndexedTensor)=[1.0, 2.0, 3.0]</code></p> - <p><code>input.query(myMappedTensor)={"Tablet Keyboard Cases": 0.8, "Keyboards":0.3}</code></p> - </td> - </tr> - <tr> - <th>ranking.listFeatures</th> - <td>rankfeatures</td> - <td>Boolean</td> - <td>false</td> - <td> - <p id="ranking.listfeatures"> - Set to true to request <em>all</em> - <a href="../schemas/schemas.html#rank-features">rank-features</a> to be calculated and returned. - The rank features will be returned in the summary field <em>rankfeatures</em>. - This option is typically used for MLR training, should not to be used for production. - </p> - </td> - </tr> - <tr> - <th>ranking.profile</th> - <td>ranking</td> - <td>String</td> - <td><code>default</code></td> - <td> - <p id="ranking.profile"> - Sets <a href="../schemas/schemas.html#rank-profile">rank profile</a> - to use for assigning rank scores for documents. - The <code>default</code> rank profile will be used for backends which does not have the given rank profile. - </p> - </td> - </tr> - <tr> - <th>ranking.properties<br/>.<em>propertyname</em></th> - <td>rankproperty<br/>.<em>propertyname</em></td> - <td>String</td> - <td></td> - <td> - <p id="ranking.properties"> - Set a <a href="../schemas/schemas.html#rank-properties">rank property</a> - that is passed to, and used by a feature executor for this query. - Example: <code>query=foo&ranking.properties.dotProduct.X={a:1,b:2}</code> - </p> - </td> - </tr> - <tr> - <th>ranking.softtimeout<br/>.enable</th> - <td></td> - <td>Boolean</td> - <td>true</td> - <td> - <p id="ranking.softtimeout.enable"> - By default, the hits available are returned on <a href="#timeout">timeout</a>. - To return no hits at timeout instead, set <code>ranking.softtimeout.enable=false</code>. - </p> - <p> - The soft timeout uses <code>ranking.softtimeout.factor</code> of the <a href="#timeout">timeout</a>, default 70%. - The rest of the time budget is spent on later ranking phases. - </p> - <p> - The factor is adaptive, per rank profile - - the factor is adjusted based on remaining time after all ranking phases, - unless overridden in the query using <code>ranking.softtimeout.factor</code>. - </p> - <p> - A <a href="../querying/default-result-format.html#timeout">timeout</a> element is returned in the query response at timeout. - </p> - <p> - Example: query with 500ms timeout, use 300ms in first-phase ranking: - <code>&ranking.softtimeout.enable=true<br/>&ranking.softtimeout.factor=0.6<br/>&timeout=0.5</code> - </p> - <p> - The <code>ranking.softtimeout</code> settings controls what the content nodes should do - in the case where the latency budget has almost been used (timeout times a factor). - Return the documents recalled and ranked with the - <a href="../../ranking/phased-ranking.html">first phase function</a> within the time used, - or simply don't produce a result: - </p> - <ul> - <li> - With soft timeout disabled, the Vespa container will return a 504 timeout without any results. - </li> - <li> - When enabled, it will return the documents matched and ranked up until the timeout was reached, - with a 200 OK response along with the reason the result set was degraded. - </li> - </ul> - <p> - The container might respond with a timeout error with HTTP response code 504 even with soft timeout enabled - if the timeout is set so low that the query does not make it to the content nodes, - or the container does not have any time left after input and query processing - to dispatch the query to the content nodes. - </p> - <p> - Read more about soft timeout in <a href="../../performance/graceful-degradation.html">coverage degradation</a>. - </p> - </td> - </tr> - <tr> - <th>ranking.softtimeout<br/>.factor</th> - <td></td> - <td>Number</td> - <td>0.7</td> - <td> - <p id="ranking.softtimeout.factor"> - See <a href="#ranking.softtimeout.enable">ranking.softtimeout.enable</a>. - </p> - </td> - </tr> - <!-- Internal note: No need to document tailcost - this is a parameter end users should not need to use - ref SoftTimeout.java --> - <tr> - <th>ranking.sorting</th> - <td>sorting</td> - <td>String</td> - <td></td> - <td> - <p id="ranking.sorting"> - A valid <a href="../querying/sorting-language.html">sort specification</a>. - Fields you want to sort on must be stored as document attributes in the index structure - by adding <a href="../schemas/schemas.html#attribute">attribute</a> to the indexing statement. - <!-- ToDo: Add example / link to example. Move the sortspec here instead--> - </p> - </td> - </tr> - <tr> - <th>ranking.significance.useModel</th> - <td></td> - <td>Boolean</td> - <td>false</td> - <td> - <p id="ranking.significance.useModel"> - Enables or disables the use of significance models specified in <a href="../applications/services/search.html#significance">service.xml</a>. - Overrides <a href="../schemas/schemas.html#significance">use-model</a> set in the rank profile. - </p> - </td> - </tr> - <tr> - <th>ranking.freshness</th> - <td></td> - <td>String</td> - <td></td> - <td> - <p id="ranking.freshness"> - Sets the time which will be used as <em>now</em> during execution. - </p> - <p> - <code>[integer]</code>, an absolute time in seconds since epoch, or <code>now-[number]</code>, - to use a time [integer] seconds into the past, or <code>now</code> to use the current time. - </p> - <!-- ToDo: This doc is confusing, rewrite, and add/link to examples - Link to now rank feature--> - </td> - </tr> - <tr> - <th>ranking.queryCache</th> - <td></td> - <td>Boolean</td> - <td>false</td> - <td> - <p id="ranking.querycache"> - Turns query cache on or off. Query is a two-phase process. - If the query cache is on, the query is stored on the content nodes between the first and second phase, - saving network bandwidth and also query setup time, at the expense of using more memory. - It only affects the protocol phase two, - see <a href="../../performance/caches-in-vespa.html">caches in Vespa</a>. - It does not cache the result, it just saves resources by not forwarding the query twice - (one for the first protocol phase which is find the best k documents from all nodes, - to the second phase which is to fill summary data - and potentially ranking features listed in summary-features in the rank profile). - </p> - <p> - The <a href="../schemas/schemas.html#summary-features">summary-features</a> - are re-calculated but this setting avoids sending the query down once more. - There is little downside of using it, - and it can save resources and latency in cases where the query tree and query ranking features - (e.g. tensors used in ranking) are large. - As this is a protocol optimization, it also works with changing filter, - it's not cached cross independent queries, - it's just saving having to send the <span style="text-decoration: underline;">same</span> query twice. - </p> - </td> - </tr> - <tr> - <th>ranking.secondPhase.totalRerankCount</th> - <td></td> - <td>Number</td> - <td></td> - <td> - <p id="ranking.secondphase.totalrerankcount"> - Specifies the number of hits that should be ranked in the second ranking phase in total over the queried - content nodes. - Overrides the <a href="../schemas/schemas.html#secondphase-total-rerank-count">total-rerank-count</a> set in the rank profile. - Setting to 0 disables second phase reranking. - </p> - </td> - </tr> - <tr> - <th>ranking.secondPhase.rerankCount</th> - <td></td> - <td>Number</td> - <td></td> - <td> - <p id="ranking.secondphase.rerankcount"> - Specifies the number of hits that should be ranked in the second phase <i>per node</i>. - Prefer using <a href="#ranking.secondphase.totalrerankcount">totalRerankCount</a> over this. - </p> - </td> - </tr> - <tr> - <th>ranking.totalKeepRankCount</th> - <td></td> - <td>Number</td> - <td></td> - <td> - <p id="ranking.totalkeeprankcount"> - Specifies the number of hits for which the rank score should be kept after first phase ranking - in total over the nodes participating in the query. - Overrides the <a href="../schemas/schemas.html#total-keep-rank-count">total-keep-rank-count</a> set in the rank profile. - </p> - </td> - </tr> - <tr> - <th>ranking.keepRankCount</th> - <td></td> - <td>Number</td> - <td></td> - <td> - <p id="ranking.keeprankcount"> - Specifies the number of hits for which the rank score should be kept after first phase ranking - on each node. - Overrides the <a href="../schemas/schemas.html#keep-rank-count">keep-rank-count</a> set in the rank profile. - Prefer <a href="#ranking.totalkeeprankcount">total-keep-rank-count</a> over this. - </p> - </td> - </tr> - <tr> - <th>ranking.rankScoreDropLimit</th> - <td></td> - <td>Number</td> - <td></td> - <td> - <p id="ranking.rankscoredroplimit"> - Minimum rankscore for a document to be considered a hit. - Overrides the <a href="../schemas/schemas.html#rank-score-drop-limit">rank-score-drop-limit</a> set in the rank profile. - </p> - </td> - </tr> - <tr> - <th>ranking.secondPhase.rankScoreDropLimit</th> - <td></td> - <td>Number</td> - <td></td> - <td> - <p id="ranking.secondphase.rankscoredroplimit"> - Minimum rank score for a document to be considered a hit after second phase reranking or rescoring. - Overrides the <a href="../schemas/schemas.html#secondphase-rank-score-drop-limit">second phase rank-score-drop-limit</a> set in the rank profile. - </p> - </td> - </tr> - <tr> - <th>ranking.globalPhase.rerankCount</th> - <td></td> - <td>Number</td> - <td></td> - <td> - <p id="ranking.globalphase.rerankcount"> - Specifies the number of hits that should be re-ranked in the global ranking phase. - Overrides the <a href="../schemas/schemas.html#globalphase-rerank-count">rerank-count</a> set in the rank profile. - Setting to 0 disables the global phase reranking. - </p> - </td> - </tr> - <tr> - <th>ranking.globalPhase.rankScoreDropLimit</th> - <td></td> - <td>Number</td> - <td></td> - <td> - <p id="ranking.globalphase.rankscoredroplimit"> - Minimum rank score for a document to be considered a hit after global phase reranking or rescoring. - Overrides the <a href="../schemas/schemas.html#globalphase-rank-score-drop-limit">global phase rank-score-drop-limit</a> set in the rank profile. - </p> - </td> - </tr> - <tr> - <th>ranking.elementGap.<em>fieldName</em></th> - <td></td> - <td>Integer</td> - <td></td> - <td> - <p id="ranking.elementGap"> - Set or overrides <a href="../schemas/schemas.html#rank-element-gap">element-gap</a> configured - for a given <em>fieldName</em> in the rank profile. Note: Can be the integer "0" to - consider elements to be adjacent, or the string "infinity" to signal that words in different - elements never are considered "close". - </p> - </td> - </tr> - </tbody> -</table> - - - -<h2 id="ranking.matching">ranking.matching</h2> -<p> - Settings to control behavior during matching of query evaluation. - If these are set in the query, they will override any equivalent settings in the - <a href="../schemas/schemas.html#rank-profile">rank profile</a>. - Detailed descriptions are found in the rank profile documentation. -</p> -<table class="table"> - <thead> - <tr> - <th>Parameter</th> - <th>Alias</th> - <th>Type</th> - <th>Default</th> - <th>Description</th> - </tr> - </thead> - <tbody> - <tr> - <th>ranking.matching<br/>.numThreadsPerSearch</th> - <td></td> - <td>integer</td> - <td></td> - <td> - <p id="ranking.matching.numThreadsPerSearch"> - Rank profile equivalent: <a href="../schemas/schemas.html#num-threads-per-search">num-threads-per-search</a> - </p> - <p> - Overrides the global <a href="../applications/services/content.html#requestthreads-persearch">persearch</a> threads - to a <strong>lower</strong> value. - </p> - </td> - </tr> - <tr> - <th>ranking.matching<br/>.minHitsPerThread</th> - <td></td> - <td>integer</td> - <td></td> - <td> - <p id="ranking.matching.minHitsPerThread"> - Rank profile equivalent: <a href="../schemas/schemas.html#min-hits-per-thread">min-hits-per-thread</a></p> - <p> - After estimating the number of hits for a query, - this number is used to decide how many search threads to use. - </p> - </td> - </tr> - <tr> - <th>ranking.matching<br/>.numSearchPartitions</th> - <td></td> - <td>integer</td> - <td></td> - <td> - <p id="ranking.matching.numSearchPartitions"> - Rank profile equivalent: <a href="../schemas/schemas.html#num-search-partitions">num-search-partitions</a> - </p> - <p> - Number of logical partitions the corpus on a content node is divided in. - A partition is the smallest unit a search thread will handle. - </p> - </td> - </tr> - <tr> - <th>ranking.matching<br/>.termwiseLimit</th> - <td></td> - <td>double [0.0, 1.0]</td> - <td></td> - <td> - <p id="ranking.matching.termwiseLimit"> - Rank profile equivalent: <a href="../schemas/schemas.html#termwise-limit">termwise-limit</a> - </p> - <p> - If estimated number of hits > corpus * termwise-limit, - document candidates are pruned with a - <a href="../../performance/feature-tuning.html#hybrid-taat-daat">TAAT</a> evaluation - for query terms not needed for ranking. - </p> - </td> - </tr> - <tr> - <th>ranking.matching<br/>.postFilterThreshold</th> - <td></td> - <td>double [0.0, 1.0]</td> - <td>1.0</td> - <td> - <p id="ranking.matching.postFilterThreshold"> - Rank profile equivalent: <a href="../schemas/schemas.html#post-filter-threshold">post-filter-threshold</a> - </p> - <p> - Threshold value deciding if a query with an approximate - <a href="../querying/yql.html#nearestneighbor">nearestNeighbor</a> - operator combined with filters is evaluated using post-filtering. - </p> - </td> - </tr> - <tr> - <th>ranking.matching<br/>.approximateThreshold</th> - <td></td> - <td>double [0.0, 1.0]</td> - <td>0.02</td> - <td> - <p id="ranking.matching.approximateThreshold"> - Rank profile equivalent: <a href="../schemas/schemas.html#approximate-threshold">approximate-threshold</a> - </p> - <p> - Threshold value deciding if a query with an approximate - <a href="../querying/yql.html#nearestneighbor">nearestNeighbor</a> - operator combined with filters is evaluated by searching for approximate or exact nearest neighbors. - </p> - </td> - </tr> - <tr> - <th>ranking.matching<br/>.filterFirstThreshold</th> - <td></td> - <td>double [0.0, 1.0]</td> - <td>0.2</td> - <td> - <p id="ranking.matching.filterFirstThreshold"> - Rank profile equivalent: <a href="../schemas/schemas.html#filter-first-threshold">filter-first-threshold</a> - </p> - <p> - Threshold value deciding if the filter is checked before computing a distance - (<em>filter-first heuristic</em>) while searching the - <a href="../schemas/schemas.html#index-hnsw">HNSW</a> - graph for approximate neighbors with filtering. - </p> - </td> - </tr> - <tr> - <th>ranking.matching<br/>.filterFirstExploration</th> - <td></td> - <td>double [0.0, 1.0]</td> - <td>0.01</td> - <td> - <p id="ranking.matching.filterFirstExploration"> - Rank profile equivalent: <a href="../schemas/schemas.html#filter-first-exploration">filter-first-exploration</a> - </p> - <p> - Value specifying how aggressively the filter-first heuristic searches the - <a href="../schemas/schemas.html#index-hnsw">HNSW</a> - graph for approximate neighbors with filtering. - </p> - </td> - </tr> - <tr> - <th>ranking.matching<br/>.explorationSlack</th> - <td></td> - <td>double [0.0, 1.0]</td> - <td>0.0</td> - <td> - <p id="ranking.matching.explorationSlack"> - Rank profile equivalent: <a href="../schemas/schemas.html#exploration-slack">exploration-slack</a> - </p> - <p> - Value specifying slack to delay the termination of the search of the - <a href="../schemas/schemas.html#index-hnsw">HNSW</a> - graph for nearest neighbors with or without filtering. - </p> - </td> - </tr> - <tr> - <th>ranking.matching<br/>.targetHitsMaxAdjustmentFactor</th> - <td></td> - <td>double [1.0, inf]</td> - <td></td> - <td> - <p id="ranking.matching.targetHitsMaxAdjustmentFactor"> - Rank profile equivalent: <a href="../schemas/schemas.html#target-hits-max-adjustment-factor">target-hits-max-adjustment-factor</a> - </p> - <p> - Value used to control the auto-adjustment of - <a href="../querying/yql.html#totaltargethits">totalTargetHits</a> used when evaluating an approximate - <a href="../querying/yql.html#nearestneighbor">nearestNeighbor</a> - operator with post-filtering. - </p> - </td> - </tr> - <tr> - <th>ranking.matching<br/>.anntimebudget</th> - <td></td> - <td>String</td> - <td></td> - <td> - <p id="ranking.matching.anntimebudget"> - This parameter limits the time spent on a single ANN search. - That is, if an ANN search did not terminate within the specified - time, it terminates early and returns the best results found so far. - This parameter is specified as a positive floating point number with an optional unit, cf. - <a href="#timeout">timeout</a>. - </p> - <p> - When a single query contains multiple nearestNeighbor operators, - every operator gets the specified time budget, i.e., the total time that - can be spent on ANN searches for that query is bounded by - the number of nearestNeighbor operators times the specified time budget. - </p> - <p> - An early termination caused - by this parameter is not considered a failure and not reported - as a degraded search result. - </p> - <p> - Example: To specify a time budget of 25 milliseconds per ANN search, use - <code>&ranking.matching.anntimebudget=25ms</code>. - </p> - <p> - Read more about the ANN time budget in <a href="../../performance/graceful-degradation.html#early-termination-of-approximate-nearest-neighbor-search">Early termination of approximate nearest neighbor search</a>. - </p> - <p> - Available since {% include version.html version="8.684.9" %}. - </p> - </td> - </tr> - <tr> - <th>ranking.matching.anntimeout<br/>.enable</th> - <td></td> - <td>Boolean</td> - <td>true</td> - <td> - <p id="ranking.matching.anntimeout.enable"> - This parameter enables an additional ANN timeout, - which makes ANN searches terminate early if a specified point in time - before the soft timeout is reached. - The goal of this is to leave enough time for the actual matching process - without hitting the soft timeout, cf. - <a href="#ranking.softtimeout.enable">ranking.softtimeout.enable</a>. - </p> - <p> - The point in time at which ANN searches have to be finished is specified - as a factor of the actual soft timeout used by the search node via - <a href="#ranking.matching.anntimeout.factor">ranking.matching.anntimeout.factor</a>. - The default for this is 0.9, i.e., the ANN search of a query with a single nearestNeighbor operator - will terminate early if 90% of the time until the soft timeout is used up. - If a query uses multiple ANN searches, the available time until the specified - timeout is split evenly between all ANN searches. - </p> - <p> - The ANN timeout functions similarly to the ANN time budget specified by - <a href="#ranking.matching.anntimebudget">ranking.matching.anntimebudget</a> - with the following two differences: - <br/>1. The timeout is specified as a factor - of the actual soft timeout being used, which means that it automatically adapts - to the soft timeout and, hence, also the <a href="#timeout">timeout</a>. - <br/>2. An ANN search hitting the ANN timeout is considered a failure and results in - the query being reported as degraded: - an <a href="../querying/default-result-format.html#anntimeout">anntimeout</a> - element is returned in the query response at timeout. - </p> - </p> - <p> - For this parameter to have an effect, the soft timeout has to be enabled, cf. - <a href="#ranking.softtimeout.enable">ranking.softtimeout.enable</a>. - </p> - <p> - The number of such timeouts can be observed using the - <a href="../operations/metrics/searchnode.html#content_proton_documentdb_matching_approximate_nns_timed_out_queries">approximate_nns_timed_out_queries</a> - metric. - </p> - <p> - Read more about the ANN timeout in <a href="../../performance/graceful-degradation.html#early-termination-of-approximate-nearest-neighbor-search">Early termination of approximate nearest neighbor search</a>. - </p> - <p> - Available since {% include version.html version="8.696.20" %}. - Enabled by default since {% include version.html version="8.709.26" %}. - </p> - </td> - </tr> - <tr> - <th>ranking.matching.anntimeout<br/>.factor</th> - <td></td> - <td>Number</td> - <td>0.9</td> - <td> - <p id="ranking.matching.anntimeout.factor"> - Sets the point in time at which ANN searches have to be finished. - Specified as a factor of the actual soft timeout used by the search node. - See <a href="#ranking.matching.anntimeout.enable">ranking.matching.anntimeout.enable</a> - for more information on the ANN timeout. - </p> - <p> - Available since {% include version.html version="8.696.20" %}. - </p> - </td> - </tr> - <tr> - <th>ranking.sorting</th> - <td>sorting</td> - <td>String</td> - <td></td> - <td> - <p id="ranking.sorting"> - A valid <a href="../querying/sorting-language.html">sort specification</a>. - Fields you want to sort on must be stored as document attributes in the index structure - by adding <a href="../schemas/schemas.html#attribute">attribute</a> to the indexing statement. - <!-- ToDo: Add example / link to example. Move the sortspec here instead--> - </p> - </td> - </tr> - <tr> - <th>ranking.matching<br/>.filterThreshold</th> - <td></td> - <td>double [0.0, 1.0]</td> - <td></td> - <td> - <p id="ranking.matching.filterThreshold"> - Rank profile equivalent: <a href="../schemas/schemas.html#filter-threshold">filter-threshold</a> - </p> - <p> - Threshold value (in the range [0, 1]) deciding when matching in <em>index</em> fields should be treated as filters. - This happens for query terms with <a href="../../learn/glossary.html#estimated-hit-ratio">estimated hit ratios</a> - that are above the <em>filterThreshold</em>. - </p> - </td> - </tr> - <tr> - <th>ranking.matching.weakand<br/>.stopwordLimit</th> - <td></td> - <td>double [0.0, 1.0]</td> - <td></td> - <td> - <p id="ranking.matching.weakand.stopwordLimit"> - Rank profile equivalent: <a href="../schemas/schemas.html#weakand-stopword-limit">weakand stopword-limit</a> - </p> - <p> - A number in the range [0, 1] representing the maximum - <a href="../../learn/glossary.html#document-frequency-normalized">normalized document frequency</a> - a query term can have in the corpus before it's considered a stopword and - dropped entirely from being a part of the <code>weakAnd</code> evaluation. - </p> - </td> - </tr> - <tr> - <th>ranking.matching.weakand<br/>.adjustTarget</th> - <td></td> - <td>double [0.0, 1.0]</td> - <td></td> - <td> - <p id="ranking.matching.weakand.adjustTarget"> - Rank profile equivalent: <a href="../schemas/schemas.html#weakand-adjust-target">weakand adjust-target</a> - </p> - <p> - A number in the range [0, 1] representing - <a href="../../learn/glossary.html#document-frequency-normalized">normalized document frequency</a>. - Used to derive a per-query document score threshold, where documents scoring - lower than the threshold will not be considered as potential hits from the - <code>weakAnd</code> operator. - </p> - </td> - </tr> - <tr> - <th>ranking.matching.weakand<br/>.allowDropAll</th> - <td></td> - <td>boolean</td> - <td>false</td> - <td> - <p id="ranking.matching.weakand.allowDropAll"> - Rank profile equivalent: <a href="../schemas/schemas.html#weakand-allow-drop-all">weakand allow-drop-all</a> - </p> - <p> - A boolean value that, if set to <code>true</code>, will allow the - <code>weakAnd</code> operator to drop <em>all</em> terms from the query if all terms are - considered stopwords (i.e. by setting <code>weakAnd.stopwordLimit</code>). - </p> - <p> - Typically used in conjunction with - <a href="../../querying/nearest-neighbor-search#querying-using-nearestneighbor-query-operator">nearestNeighbor</a> or other operators to - ensure that the query will return hits even when all terms are considered stopwords. - </p> - </td> - </tr> - </tbody> -</table> - - - -<h2 id="ranking.matchPhase">ranking.matchPhase</h2> -<p> - Settings to control behavior during the match phase of query evaluation. - If these are set in the query, they will override any - <a href="../schemas/schemas.html#match-phase">match-phase</a> settings in the rank profile. - Detailed descriptions are found in the rank profile documentation. -</p> -<table class="table"> - <thead> - <tr> - <th>Parameter</th> - <th>Alias</th> - <th>Type</th> - <th>Default</th> - <th>Description</th> - </tr> - </thead> - <tbody> - <tr> - <th>ranking.matchPhase<br/>.attribute</th> - <td></td> - <td>string</td> - <td></td> - <td> - <p id="ranking.matchphase.attribute"> - Rank profile equivalent: <a href="../schemas/schemas.html#match-phase-attribute">match-phase: attribute</a> - </p> - <p>The attribute used to limit matches by if more than maxHits hits will be produced.</p> - </td> - </tr> - <tr> - <th>ranking.matchPhase<br/>.totalMaxHits</th> - <td></td> - <td>long</td> - <td></td> - <td> - <p id="ranking.matchphase.totalmaxhits"> - The max number of hits that should be generated in total over the content nodes during the match phase. - Setting the value to `0` disables match phase early termination. - Rank profile equivalent: <a href="../schemas/schemas.html#match-phase-total-max-hits">match-phase: total-max-hits</a> - </p> - </td> - </tr> - <tr> - <th>ranking.matchPhase<br/>.maxHits</th> - <td></td> - <td>long</td> - <td></td> - <td> - <p id="ranking.matchphase.maxhits"> - The max number of hits that should be generated on eache content nodes during the match phase. - Prefer using <a href="#ranking.matchphase.totalmaxhits">totalMaxHits</a> over this. - Rank profile equivalent: <a href="../schemas/schemas.html#match-phase-max-hits">match-phase: max-hits</a> - </td> - </tr> - <tr> - <th>ranking.matchPhase<br/>.ascending</th> - <td></td> - <td>boolean</td> - <td></td> - <td> - <p id="ranking.matchphase.ascending"> - Rank profile equivalent: <a href="../schemas/schemas.html#match-phase-order">match-phase: order</a> - </p> - <p> - Whether to keep the documents having the highest (false) or lowest (true) values of the match phase attribute. - </p> - </td> - </tr> - <tr> - <th>ranking.matchPhase<br/>.diversity.attribute</th> - <td></td> - <td>string</td> - <td></td> - <td> - <p id="ranking.matchphase.diversity.attribute"> - Rank profile equivalent: <a href="../schemas/schemas.html#diversity-attribute">diversity: attribute</a> - </p> - <p>The attribute to use when deciding diversity.</p> - </td> - </tr> - <tr> - <th>ranking.matchPhase<br/>.diversity.minGroups</th> - <td></td> - <td>long</td> - <td></td> - <td> - <p id="ranking.matchphase.diversity.mingroups"> - Rank profile equivalent: <a href="../schemas/schemas.html#diversity-min-groups">diversity: min-groups</a> - </p> - <p> - The minimum number of groups that should be returned from the match phase grouped by the diversity attribute. - </p> - </td> - </tr> - </tbody> -</table> - - - -<h2 id="dispatch">Dispatch</h2> -<table class="table"> - <thead> - <tr> - <th>Parameter</th> - <th>Alias</th> - <th>Type</th> - <th>Default</th> - <th>Description</th> - </tr> - </thead> - <tbody> - <tr> - <th>dispatch.topKProbability</th> - <td></td> - <td>double</td> - <td></td> - <td> - <p id="dispatch.topkprobability"> - Probability to use when computing how many hits to fetch from each partition - when merging and creating the final result set. - See <a href="../applications/services/content.html#top-k-probability">services</a> for details. - </p> - <p> - Default: <a href="../applications/services/content.html#top-k-probability">none</a>. - <!-- ToDo: Check this default, copied as-is, need some context --> - </p> - </td> - </tr> - </tbody> -</table> - - - -<h2 id="presentation">Presentation</h2> -<table class="table"> - <thead> - <tr> - <th>Parameter</th> - <th>Alias</th> - <th>Type</th> - <th>Default</th> - <th>Description</th> - </tr> - </thead> - <tbody> - <tr> - <th>presentation.bolding</th> - <td>bolding</td> - <td>Boolean</td> - <td>true</td> - <td> - <p id="presentation.bolding"> - Whether to bold query terms in <a href="../schemas/schemas.html">schema</a> - fields defined with <a href="../schemas/schemas.html#bolding">bolding: on</a> - or <a href="../schemas/schemas.html#summary">summary: dynamic</a>. - </p> - </td> - </tr> - <tr> - <th>presentation.format</th> - <td>format</td> - <td>String</td> - <td><code>default</code></td> <!-- ToDo: easier to understand if we just write json, which is the same? --> - <td> - <p id="presentation.format"></p> - <table class="table"> - <thead> - <tr> - <th>Value</th> - <th>Description</th> - </tr> - </thead> - <tbody> - <tr> - <td><em>No value</em> or <a href="../querying/default-result-format.html">default</a></td> - <td>The default, builtin JSON format</td> - <!-- ToDo: combine with below, and link to json format --> - </tr> - <tr> - <td><a href="../querying/default-result-format.html">json</a></td> - <td>Builtin JSON format</td> - </tr> - <tr> - <td><code>cbor</code></td> - <td> - Builtin <a href="https://cbor.io/">CBOR</a> format. - Binary encoding, responses are smaller and faster to render than JSON, especially for numeric data. - Semantically equivalent to JSON. - Cannot be used with <code>jsoncallback</code> (JSONP). - Requires Vespa 8.623.5 or later. - </td> - </tr> - <tr> - <td><code>xml</code></td> - <td> - Builtin XML format. - {% include deprecated.html content="See <a href='../release-notes/vespa8.html'>deprecations</a>."%} - </td> - </tr> - <tr> - <td><a href="../querying/page-result-format.html">page</a></td> - <td> - XML format which is suitable for use with <a href="../../querying/page-templates.html">page templates</a>. - {% include deprecated.html content="See <a href='../release-notes/vespa8.html'>deprecations</a>."%} - </td> - </tr> - <tr> - <td><em>Any other value</em></td> - <td>A custom <a href="../../applications/result-renderers.html">result renderer</a> supplied by the application</td> - </tr> - </tbody> - </table> - <p> - The response format can also be selected via the HTTP <code>Accept</code> header. - If the Accept header specifies <code>application/cbor</code> with higher priority than - <code>application/json</code>, CBOR will be used. - The <code>format</code> query parameter overrides the Accept header. - </p> - </td> - </tr> - <tr> - <th>presentation.summary</th> - <td>summary</td> - <td>String</td> - <td></td> - <td> - <p id="presentation.summary"> - The name of the <a href="../../querying/document-summaries.html">summary class</a> - used to select fields in results. - </p> - <p>Default: The default summary class of the schema.</p><!-- ToDo: rewrite --> - </td> - </tr> - <tr> - <th>presentation.template</th> - <td></td> - <td>String</td> - <td></td> - <td> - <p id="presentation.template"> - The id of a deployed page template to use for this result. - This should be used with the <a href="../querying/page-result-format.html">page</a> result format. - </p> - </td> - </tr> - <tr> - <th>presentation.timing</th> - <td></td> - <td>Boolean</td> - <td>false</td> - <td> - <p id="presentation.timing"> - Whether a result renderer should try to add optional timing information to the rendered page - - see the <a href="../querying/default-result-format.html#timing">result reference</a>. - </p> - </td> - </tr> - <tr> - <th>presentation.format.tensors</th> - <td></td> - <td>String</td> - <td><code>short</code></td> - <td> - <p id="presentation.format.tensors"> - Controls how tensors are rendered in the result. - </p> - <table class="table"> - <thead> - <tr> - <th>Value</th> - <th>Description</th> - </tr> - </thead> - <tbody> - <tr> - <td><code>short</code></td> - <td> - Render the tensor value in an object having two keys, "type" containing the value, - and "cells"/"blocks"/"values" (<a href="../schemas/document-json-format.html#tensor">depending on the type</a>) containing the tensor content.<br/> - Render the tensor content in the <a href="../schemas/document-json-format.html#tensor">type-appropriate short form</a>. - </td> - </tr> - <tr> - <td><code>long</code></td> - <td> - Render the tensor value in an object having two keys, "type" containing the value, - and "cells" containing the tensor content.<br/> - Render the tensor content in the <a href="../schemas/document-json-format.html#tensor">general verbose form</a>. - </td> - </tr> - <tr> - <td><code>short-value</code></td> - <td> - Render the tensor content directly.<br/> - Render the tensor content in the <a href="../schemas/document-json-format.html#tensor">type-appropriate short form</a>. - </td> - </tr> - <tr> - <td><code>long-value</code></td> - <td> - Render the tensor content directly.<br/> - Render the tensor content in the <a href="../schemas/document-json-format.html#tensor">general verbose form</a>. - </td> - </tr> - <tr> - <td><code>hex</code></td> - <td> - Use <code>short</code> form, and render dense values <a href="../ranking/tensor.html#indexed-hex-form">hex encoded</a>.<br/> - </td> - </tr> - <tr> - <td><code>hex-value</code></td> - <td> - Use <code>short-value</code> form, and render dense values <a href="../ranking/tensor.html#indexed-hex-form">hex encoded</a>.<br/> - </td> - </tr> - </table> - </tr> - </tbody> -</table> - - - -<h2 id="grouping-and-aggregation">Grouping and Aggregation</h2> -<table class="table"> - <thead> - <tr> - <th>Parameter</th> - <th>Alias</th> - <th>Type</th> - <th>Default</th> - <th>Description</th> - </tr> - </thead> - <tbody> - <tr> - <th>select</th> - <td></td> - <td>String</td> - <td></td> - <td> - <p id="select"> - Requests specific multi-level result set statistics and/or hit groups to be returned in the result. - Fields you want to retrieve statistics or hit groups for must be stored as document attributes - in the index structure by adding attribute to the indexing statement. - </p> - <p> - Default is no grouping. - </p> - <p> - See the <a href="../../querying/grouping.html">grouping guide</a> for examples. - </p> - </td> - </tr> - <tr> - <th>collapsefield</th> - <td></td> - <td>String</td> - <td></td> - <td> - <p id="collapsefield"> - Comma-separated list of <a href="../schemas/schemas.html#summary">field names</a>, that should only - appear uniquely in a result. Hits with values in these fields which are already present in a higher-ranked - hit will be filtered out. - </p> - <p>Read more in <a href="../../querying/result-diversity">result diversity</a> to compare this with other options.</p> - <p> - Default is no field collapsing. - </p> - </td> - </tr> - <tr> - <th>collapsesize</th> - <td></td> - <td>Number</td> - <td>1</td> - <td> - <p id="collapsesize"> - The number of hits to keep in each collapsed bucket - used for all collapsefields. - </p> - </td> - </tr> - <tr> - <th>collapsesize.<em>fieldname</em></th> - <td></td> - <td>Number</td> - <td>1</td> - <td> - <p id="collapsesize.fieldname"> - The number of hits to keep in each collapsed bucket - used for the specified field. - This value takes precedence over the value specified in <code>collapsesize</code>. - </p> - </td> - </tr> - <tr> - <th>collapse.summary</th> - <td></td> - <td>String</td> - <td></td> - <td> - <p id="collapse.summary"> - A valid name of a document summary class. - Use this summary class to fetch the fields used for collapsing. - </p> - <p>Default: Use default summary or attributes.</p> - </td> - </tr> - <tr> - <th>grouping.defaultMaxGroups</th> - <td></td> - <td>Number</td> - <td>10</td> - <td> - <p id="grouping.defaultmaxgroups"> - Positive integer or <code>-1</code> to disable. - <!-- ToDo: clarify what this means --> - </p> - <p> - The default number of groups to return when - <a href="../../querying/grouping.html#ordering-and-limiting-groups">max</a> is not specified. - </p> - </td> - </tr> - <tr> - <th>grouping.defaultMaxHits</th> - <td></td> - <td>Number</td> - <td>10</td> - <td> - <p id="grouping.defaultmaxhits"> - Positive integer or <code>-1</code> to disable. - <!-- ToDo: clarify what this means --> - </p> - <p> - The default number of hits to return when - <a href="../../querying/grouping.html#hits-per-group">max</a> is not specified. - </p> - </td> - </tr> - <tr> - <th>grouping.globalMaxGroups</th> - <td></td> - <td>Number</td> - <td>10000</td> - <td> - <p id="grouping.globalmaxgroups"> - Positive integer or <code>-1</code> to disable. - <!-- ToDo: clarify what this means --> - </p> - <p> - A cost limit for grouping queries. - Any query that may exceed this threshold will be preemptively failed by the container. - The limit is defined as the total number of groups and document summaries a query may produce. - A query that does not have an implicit or explicit <code>max</code> defined for all levels - will always fail if limit is enabled. - This parameter can only be overridden in a <a href="../../querying/query-profiles.html">query profile</a>. - </p> - <p> - See the <a href="../../querying/grouping.html#global-limit">grouping guide</a> for practical examples. - </p> - </td> - </tr> - <tr> - <th>grouping.defaultPrecisionFactor</th> - <td></td> - <td>Decimal<br/>number</td> - <td>2.0</td> - <td> - <p id="grouping.defaultprecisionfactor"> - The default precision scale factor when - <a href="../../querying/grouping.html#ordering-and-limiting-groups">precision</a> is not specified. - The final precision value is calculated by multiplying the effective <code>max</code> value - with the scale factor. - </p> - </td> - </tr> - <tr> - <th>timezone</th> - <td></td> - <td>String</td> - <td><code>utc</code></td> - <td> - <p id="timezone"> - Specifies a timezone that will be used to offset all <code>time</code> related expressions in - grouping. See <a href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/TimeZone.html#getTimeZone(java.lang.String)">Java's definition</a> - for valid timezones. - </p> - <p> - See the <a href="../../querying/grouping.html#timezone-grouping">grouping guide</a> for examples. - </p> - </td> - </tr> - </tbody> -</table> - -<h2 id="streaming">Streaming</h2> -<p> - Parameters for <a href="../../performance/streaming-search.html">streaming search mode</a>. -</p> - -<table class="table"> - <thead> - <tr> - <th>Parameter</th> - <th>Alias</th> - <th>Type</th> - <th>Default</th> - <th>Description</th> - </tr> - </thead> - <tbody> - <tr> - <th>streaming.groupname</th> - <td></td> - <td>A string</td> - <td></td> - <td> - <p id="streaming.groupname"> - Sets the group (specified by <a href="../../schemas/documents.html#id-scheme">g=<groupname></a>) of the documents to stream through. - </p> - </td> - </tr> - <tr> - <th>streaming.selection</th> - <td></td> - <td>A <a href="../writing/document-selector-language.html">document selection</a></td> - <td></td> - <td> - <p id="streaming.selection"> - Restricts streaming search using a selection expression instead of a group id. - </p> - <p> - If the selection is on the form <code>id.group == "foo" or id.group == "bar" or id.group == ...</code> - this will only stream documents in those groups, which is efficient for a small number of groups. - </p> - <p> - If any other selection is used, this will stream through <i>all</i> groups, which is very costly. - </p> - </td> - </tr> - <tr> - <th>streaming.maxbucketspervisitor</th> - <td></td> - <td>An integer - <td>Positive infinity</td> - <td> - <p id="streaming.maxbucketspervisitor"> - If set, limit backend bucket concurrency to the specified number of buckets. - Can be used to explicitly control resource usage for extremely large streaming search locations. - This is an expert option. - </p> - </td> - </tr> - </tbody> -</table> - - -<h2 id="tracing">Tracing</h2> -<p> - Parameters controlling trace information returning with the result for diagnostics. -</p> -<table class="table"> - <thead> - <tr> - <th>Parameter</th> - <th>Alias</th> - <th>Type</th> - <th>Default</th> - <th>Description</th> - </tr> - </thead> - <tbody> - <tr> - <th>trace.profile</th> - <td>profile</td> - <td>Boolean</td> - <td>false</td> - <td> - <p id="trace.profile">True to produce a structured trace for performance analysis.</p> - - <p>Returns a structured trace for performance analysis. This is a shorthand to set various other parameters - to the suitable values to generate a performance trace.</p> - </td> - </tr> - <tr> - <th>trace.level</th> - <td>tracelevel</td> - <td>Number</td> - <td>0</td> - <td> - <p id="trace.level"> - A positive number. Default is no tracing. - </p> - <p> - Collect trace information for debugging when running a query. - Higher numbers give progressively more detail on query transformations, - searcher execution and content node(s) query execution. - See <a href="../../querying/query-api.html#query-tracing">query tracing</a> for details and examples. - </p> <!-- ToDo: Add links, rewrite --> - <p> - Tracing is subject to change at any time, the below is a guide: - </p> - <table class="table"> - <thead> - <tr><th>Level</th><th>Description</th></tr> - </thead> - <tbody> - <tr><td>1</td><td>Basic tracing in container</td></tr> - <tr><td>2</td><td>Basic tracing, more details</td></tr> - <tr><td>3</td><td>Basic tracing, even more details</td></tr> - <tr><td>4</td><td>Include timing info from content nodes</td></tr> - <tr><td>5</td><td>Even more timing info from content nodes</td></tr> - <tr><td>6</td><td>Include the query execution plan (blueprint)</td></tr> - <tr><td>7</td><td>Include the query execution tree</td></tr> - </tbody> - </table> - </td> - </tr> - <tr> - <th>trace.explainLevel</th> - <td>explainlevel</td> - <td>Number</td> - <td>0</td> - <td> - <p id="trace.explainlevel"> - Set to a positive number to collect query execution information for debugging when running a query. - Higher numbers give progressively more detail on content node query execution. Tuning this parameter - is useful if we want to get more information from the content nodes without gathering lots of trace information from the container chain. - </p> - <p>Explanation is subject to change at any time, the below is a guide:</p> - <table class="table"> - <thead> - <tr><th>Level</th><th>Description</th></tr> - </thead> - <tbody> - <tr><td>1</td><td>Timing and overall query plan (blueprint) from each content node</td></tr> - <tr><td>2</td><td>Timing per search thread and execution tree (search iterator tree)</td></tr> - </tbody> - </table> - <p> - Note that you might get the same at <a href="#trace.level">trace.level</a> 5 and above. - Default is no explanation. - </p> - <p>Tracing with <code>trace.explainLevel</code> also requires that <a href="#trace.level">trace.level</a> is positive.</p> - </td> - </tr> - <tr> - <th>trace.profileDepth</th> - <td></td> - <td>Number</td> - <td>0</td> - <td> - <p id="trace.profiledepth"> - Turns on performance profiling of the content node query execution for - <a href="#trace.profiling.matching.depth">matching</a>, - <a href="#trace.profiling.firstPhaseRanking.depth">first-phase ranking</a>, and - <a href="#trace.profiling.secondPhaseRanking.depth">second-phase ranking</a>. - How profiling is performed is based on whether <code>trace.profileDepth</code> is positive or negative: - </p> - <table class="table"> - <thead> - <tr><th>Type</th><th>Description</th></tr> - </thead> - <tbody> - <tr><td>Tree</td> - <td>A positive number specifies the depth used by a tree profiler. - A higher number means more profiler data. - The output resembles the structure of the search iterator tree or rank expression tree being profiled, - with total time and self time tracked per component (node in the tree). - </td></tr> - <tr><td>Flat</td><td>A negative number specifies the topn (cut-off) used by a flat profiler. - The output returns the topn components that use the most self time. - </td></tr> - </tbody> - </table> - <p> - The performance profiling output is subject to change at any time. - Default is no information. - </p> - <p>Tracing with <code>trace.profileDepth</code> also requires that <a href="#trace.level">trace.level</a> is positive.</p> - </td> - </tr> - - <tr> - <th>trace.profiling.matching.depth</th> - <td></td> - <td>Number</td> - <td>0</td> - <td> - <p id="trace.profiling.matching.depth"> - Turns on profiling of - <a href="../../performance/sizing-search.html#life-of-a-query-in-vespa">matching</a> - of the content node query execution. - This exposes information about how time spent on matching is distributed - between individual search iterators. - The profiling output is tagged <em>match_profiling</em> and is subject to change at any time. - Default is no information. - See <a href="#trace.profiledepth">trace.profileDepth</a> for semantics of this parameter. - </p> - <p>Tracing with <code>trace.profiling.matching.depth</code> requires that <a href="#trace.level">trace.level</a> is positive.</p> - </td> - </tr> - - <tr> - <th>trace.profiling.firstPhaseRanking.depth</th> - <td></td> - <td>Number</td> - <td>0</td> - <td> - <p id="trace.profiling.firstPhaseRanking.depth"> - Turns on profiling of the <a href="../../basics/ranking.html">first-phase ranking</a> - of the content node query execution. - This exposes information about how time spent on first-phase ranking is distributed - between individual <a href="../ranking/rank-features.html">rank features</a>. - The profiling output is tagged <em>first_phase_profiling</em> and is subject to change at any time. - Default is no information. - See <a href="#trace.profiledepth">trace.profileDepth</a> for semantics of this parameter. - </p> - <p>Tracing with <code>trace.profiling.firstPhaseRanking.depth</code> also requires that <a href="#trace.level">trace.level</a> is positive.</p> - </td> - </tr> - - <tr> - <th>trace.profiling.secondPhaseRanking.depth</th> - <td></td> - <td>Number</td> - <td>0</td> - <td> - <p id="trace.profiling.secondPhaseRanking.depth"> - Turns on profiling of the <a href="../../basics/ranking.html">second-phase ranking</a> - of the content node query execution. - This exposes information about how time spent on second-phase ranking is distributed - between individual <a href="../ranking/rank-features.html">rank features</a>. - The profiling output is tagged <em>second_phase_profiling</em> and is subject to change at any time. - Default is no information. - See <a href="#trace.profiledepth">trace.profileDepth</a> for semantics of this parameter. - </p> - <p>Tracing with <code>trace.profiling.secondPhaseRanking.depth</code> also requires that <a href="#trace.level">trace.level</a> is positive.</p> - </td> - </tr> - - <tr> - <th>trace.timestamps</th> - <td></td> - <td>Boolean</td> - <td>false</td> - <td> - <p id="trace.timestamps"> - Enable to get timing information already at <a href="#trace.level">trace.level=1</a>. - This is useful for debugging latency spent at different components in the container search chain - without rendering a lot of string data which is associated with higher trace levels. - </p> - </td> - </tr> - <tr> - <th>trace.query</th> - <td></td> - <td>Boolean</td> - <td>true</td> - <td> - <p id="trace.query"> - Whether to include the query in any trace messages. - This is useful for avoiding query serialization with very large queries - to avoid impact from it on performance and excessively large traces. - </p> - </td> - </tr> - </tbody> -</table> - - - -<h2 id="semantic-rules">Semantic Rules</h2> -<p> -Refer to <a href="../querying/semantic-rules.html">semantic rules</a>. -</p> -<table class="table"> - <thead> - <tr> - <th>Parameter</th> - <th>Alias</th> - <th>Type</th> - <th>Default</th> - <th>Description</th> - </tr> - </thead> - <tbody> - <tr> - <th>rules.off</th> - <td></td> - <td>Boolean</td> - <td>true</td> - <td> - <p id="rules.off">Turn rule evaluation off for this query.</p> - </td> - </tr> - <tr> - <th>rules.rulebase</th> - <td></td> - <td>String</td> - <td></td> - <td> - <p id="rules.rulebase">A rule base name - the name of the rule base to use for these queries.</p> - </td> - </tr> - <tr> - <th>tracelevel.rules</th> - <td></td> - <td>Number</td> - <td></td> - <td> - <p id="tracelevel.rules"> - The amount of rule evaluation trace output to show, higher number means more details. - This is useful to see a trace from rule evaluation - without having to see trace from all other searchers at the same time. - </p> - <!-- ToDo: something about the default, which is probably no trace --> - </td> - </tr> - </tbody> -</table> - - - -<h2 id="other">Other</h2> -<table class="table"> - <thead> - <tr> - <th>Parameter</th> - <th>Alias</th> - <th>Type</th> - <th>Default</th> - <th>Description</th> - </tr> - </thead> - <tbody> - <tr> - <th>recall</th> - <td></td> - <td>String</td> - <td></td> - <td> - <p id="recall"> - Any allowed collection of recall terms. - Sets a recall parameter to be combined with the query. - This is identical to <a href="#model.filter">filter</a>, - except that recall terms are not exposed to the ranking framework and thus not ranked. - As such, one can not use unprefixed terms; they must either be positive or negative. - </p> - </td> - </tr> - <tr> - <th>user</th> - <td></td> - <td>String</td> - <td></td> - <td> - <p id="user"> - The id of the user making the query. - The content of the argument is made available to the search chain, - but it triggers no features in Vespa apart from being propagated to the access log. - </p> - <!-- ToDo: Remove in Vespa 9 or before --> - </td> - </tr> - <tr> - <th>hitcountestimate</th> - <td></td> - <td>Boolean</td> - <td>false</td> - <td> - <p id="hitcountestimate"> - Make this an estimation query. - No hits will be returned, - and total hit count will be set to an estimate of what executing the query as a normal query would give. - </p> - </td> - </tr> - <tr> - <th>metrics.ignore</th> - <td></td> - <td>Boolean</td> - <td>false</td> - <td> - <p id="metrics.ignore"> - Ignore metric collection for this query request, - useful for <a href="../../performance/container-tuning.html#container-warmup">warm-up queries</a>. - </p> - </td> - </tr> - <tr> - <th>weakAnd.replace</th> - <td></td> - <td>Boolean</td> - <td>false</td> - <td> - <p id="weakand.replace"> - Replace all instances of OR in the query tree with weakAnd. - </p> - <!-- ToDo: add links to/from --> - </td> - </tr> - <tr> - <th>wand.hits</th> - <td></td> - <td>Number</td> - <td>100</td> - <td> - <p id="wand.hits"> - Used in combination with <a href="#weakand.replace">weakAnd.replace</a>. - Sets the targetHits of the new weakAnds to the specified value. - </p> - <!-- ToDo: link to targethits --> - </td> - </tr> - <tr> - <th>sorting.degrading</th> - <td></td> - <td>Boolean</td> - <td>true</td> - <td> - <p id="sorting.degrading"> - When sorting on a <a href="../../content/attributes.html">single-value numeric attribute with fast-search</a> - an optimization is activated to return early, with an inaccurate total-hits count. - Set <code>sorting.degrading</code> to false to disable this optimization. - </p> - <p> - This optimization sets the primary sorting attribute as the - <a href="#ranking.matchphase.attribute">match phase attribute</a>, and - <a href="#ranking.matchphase.maxhits">match phase maxHits</a> equal to - <code>max(10000, maxHits+maxOffset)</code>. - <a href="#hits">maxHits</a> and - <a href="#offset">maxOffset</a> can be set in a query profile. - </p> - </td> - </tr> - <tr> - <th>noCache</th> - <td>nocache</td> - <td>Boolean</td> - <td>false</td> - <td> - <p id="nocache"> - Sets whether this query should never be served from a cache. - Vespa has <a href="../../performance/caches-in-vespa.html">few caches</a>, - and this parameter does not control any of them. - Therefore, this parameter has no effect - </p> - <!-- ToDo: Remove in Vespa 9 or before --> - </td> - </tr> - </tbody> -</table> - - - -<h2 id="http-status-codes">HTTP status codes</h2> -<p>The following rules determine which HTTP status code is returned:</p> -<ul> - <li> - If the Result contains no errors (Result.hits().getError()==null): 200 OK is returned. - </li> - <li> - If the Result contains errors and no regular hits: - <ul> - <li> - If the error code of any ErrorMessage in the Result - (Result.hits().getErrorHit().errorIterator()) is a "WEB SERVICE ERROR CODE", - the first of those is returned. - </li> - <li> - Otherwise, if it is an "HTTP COMPATIBLE ERROR CODE", the mapping of it is returned. - </li> - <li> - Otherwise 500 INTERNAL_SERVER_ERROR is returned. - </li> - </ul> - </li> - <li> - If the Result contains errors and also contains valid hits: - The same as above, but 200 OK is returned by default instead of 500. - </li> -</ul> -<table class="table"> - <caption>List of possible HTTP status codes and their descriptions.</caption> - <thead> - <tr> - <th>Code</th> - <th>Description</th> - </tr> - </thead> - <tbody> - <tr> - <th>200</th> - <td>OK</td> - </tr> - <tr> - <th>400</th> - <td>Bad Request</td> - </tr> - <tr> - <th>401</th> - <td>Unauthorized</td> - </tr> - <tr> - <th>403</th> - <td>Forbidden</td> - </tr> - <tr> - <th>404</th> - <td>Not Found</td> - </tr> - <tr> - <th>405</th> - <td>Method Not Allowed</td> - </tr> - <tr> - <th>408</th> - <td>Request Timeout</td> - </tr> - <tr> - <th>428</th> - <td>Precondition Required</td> - </tr> - <tr> - <th>431</th> - <td>Request Header Fields Too Large</td> - </tr> - <tr> - <th>500</th> - <td>Internal Server Error</td> - </tr> - <tr> - <th>502</th> - <td>Bad Gateway</td> - </tr> - <tr> - <th>503</th> - <td> - Service Unavailable; no available search handler threads in the jdisc container to serve the request. - See <a href="../../performance/container-tuning.html#container-worker-threads">Container Tuning</a> on sizing thread pools. - </td> - </tr> - <tr> - <th>504</th> - <td>Gateway Timeout</td> - </tr> - <tr> - <th>507</th> - <td>Insufficient Storage</td> - </tr> - </tbody> -</table> -<h3>Error code to status code mapping</h3> -<p>Mapping of internal error codes to HTTP status codes.</p> -<table class="table"> - <thead> - <tr> - <th>Error Code</th> - <th>HTTP Code</th> - </tr> - </thead> - <tbody> - <tr> - <th>com.yahoo.container.protect.Error.BAD_REQUEST</th> - <td>400</td> - </tr> - <tr> - <th>com.yahoo.container.protect.Error.UNAUTHORIZED</th> - <td>401</td> - </tr> - <tr> - <th>com.yahoo.container.protect.Error.FORBIDDEN</th> - <td>403</td> - </tr> - <tr> - <th>com.yahoo.container.protect.Error.NOT_FOUND</th> - <td>404</td> - </tr> - <tr> - <th>com.yahoo.container.protect.Error.INTERNAL_SERVER_ERROR</th> - <td>500</td> - </tr> - <tr> - <th>com.yahoo.container.protect.Error.INSUFFICIENT_STORAGE</th> - <td>507</td> - </tr> - </tbody> -</table> - -<hr/> - -<!-- ToDo: Consider deprecating for Vespa 9 --> -<h2 id="select.json">select</h2> -<p> - A <code>select</code> query is equivalent in structure to YQL, written in JSON. - Contains subparameters <code>where</code>, <code>grouping</code> and <code>fields</code>. -</p> -<table class="table"> - <thead> - <tr> - <th>Parameter</th> - <th>Alias</th> - <th>Type</th> - <th>Default</th> - <th>Description</th> - </tr> - </thead> - <tbody> - <tr> - <th>where</th> - <td></td> - <td>String</td> - <td></td> - <td> - <p id="select.where"> - A string with JSON. - Refer to the <a href="../querying/json-query-language">select reference</a> for details. - </p> - </td> - </tr> - <tr> - <th>grouping</th> - <td></td> - <td>String</td> - <td></td> - <td> - <p id="select.grouping"> - A string with JSON. - Refer to the <a href="../querying/json-query-language">select reference</a> for details. - </p> - </td> - </tr> - <tr> - <th>fields</th> - <td></td> - <td>String</td> - <td></td> - <td> - <p id="select.fields"> - A JSON array of <a href="../../querying/document-summaries.html#selecting-summary-fields-in-yql">summary field</a> - names to include in each hit. Equivalent to the field list in a YQL <code>select</code> clause. - Refer to the <a href="../querying/json-query-language">select reference</a> for details. - </p> - </td> - </tr> - </tbody> -</table> diff --git a/en/reference/api/query.mdx b/en/reference/api/query.mdx new file mode 100644 index 0000000000..57b1107aa4 --- /dev/null +++ b/en/reference/api/query.mdx @@ -0,0 +1,1247 @@ +--- +title: "Query API Reference" +description: "Query API Reference in Vespa applications." +--- + +Refer to the [Query API guide](/en/querying/query-api) for API examples. + +All the request parameters listed below can be set in query profiles. +The first four blocks of properties are also modeled as +[query profile types](/en/querying/query-profiles#query-profile-types). +These types can be referred from query profiles (and inheriting types) +to provide type checking on the parameters. + +Parameters have *full name* and can have one or more shorter aliases, shown in square brackets below. +Both names can be used in requests and query profiles. +Aliases are case-insensitive in requests. + +When accessing parameters from Searcher components, the full names correspond to the path from +the root Query object to that parameter. + +## Parameters + +#### Query + +- [yql](#yql) + +#### Native Execution Parameters + +- [hits](#hits) [*count*] +- [offset](#offset)[*start*] +- [queryProfile](#queryprofile) +- [groupingSessionCache](#groupingsessioncache) +- [searchChain](#searchchain) +- [timeout](#timeout) + +#### Query Model + +- [model.defaultIndex](#model.defaultindex) [*default-index*] +- [model.encoding](#model.encoding) [*encoding*] +- [model.filter](#model.filter) [*filter*] +- [model.locale](#model.locale) [*locale*] +- [model.language](#model.language) [*lang, language*] +- [model.queryString](#model.querystring) [*query*] +- [model.restrict](#model.restrict) [*restrict*] +- [model.searchPath](#model.searchpath) [*path*] +- [model.sources](#model.sources) [*search, sources*] +- [model.type](#model.type) [*type*] +- [model.type.composite](#model.type.composite) +- [model.type.tokenization](#model.type.tokenization) +- [model.type.syntax](#model.type.syntax) +- [model.type.profile](#model.type.profile) +- [model.type.isYqlDefault](#model.type.isYqlDefault) + +#### Ranking + +- [ranking.elementGap.*fieldName*](#ranking.elementGap) +- [ranking.features](#ranking.features) [*input*, *rankfeature*] +- [ranking.freshness](#ranking.freshness) +- [ranking.keepRankCount](#ranking.keeprankcount) +- [ranking.totalKeepRankCount](#ranking.totalkeeprankcount) +- [ranking.listFeatures](#ranking.listfeatures) [*rankfeatures*] +- [ranking.matchPhase](#ranking.matchPhase) +- [ranking.matching](#ranking.matching) +- [ranking.profile](#ranking.profile) [*ranking*] +- [ranking.properties](#ranking.properties) [*rankproperty*] +- [ranking.queryCache](#ranking.querycache) +- [ranking.rankScoreDropLimit](#ranking.rankscoredroplimit) +- [ranking.matchphase.attribute](#ranking.matchphase.attribute) +- [ranking.matchPhase.totalMaxHits](#ranking.matchphase.totalmaxhits) +- [ranking.matchPhase.maxHits](#ranking.matchphase.maxhits) +- [ranking.matchPhase.ascending](#ranking.matchphase.ascending) +- [matchPhase.diversity.attribute](#ranking.matchphase.diversity.attribute) +- [matchPhase.diversity.minGroups](#ranking.matchphase.diversity.mingroups) +- [ranking.secondPhase.totalRerankCount](#ranking.secondphase.totalrerankcount) +- [ranking.secondPhase.rerankCount](#ranking.secondphase.rerankcount) +- [ranking.secondPhase.rankScoreDropLimit](#ranking.secondphase.rankscoredroplimit) +- [ranking.globalPhase.rankScoreDropLimit](#ranking.globalphase.rankscoredroplimit) +- [ranking.globalPhase.rerankCount](#ranking.globalphase.rerankcount) +- [ranking.significance.useModel](#ranking.significance.useModel) +- [ranking.softtimeout.enable](#ranking.softtimeout.enable) +- [ranking.sorting](#ranking.sorting) [*sorting*] + +#### Presentation + +- [presentation.bolding](#presentation.bolding) [*bolding*] +- [presentation.format](#presentation.format) [*format*] +- [presentation.template](#presentation.template) +- [presentation.summary](#presentation.summary) [*summary*] +- [presentation.timing](#presentation.timing) + +#### Grouping + +- [select](#select) +- [collapse.summary](#collapse.summary) +- [collapsefield](#collapsefield) +- [collapsesize](#collapsesize) +- [collapsesize](#collapsesize.fieldname) [*fieldname*] +- [grouping.defaultMaxGroups](#grouping.defaultmaxgroups) +- [grouping.defaultMaxHits](#grouping.defaultmaxhits) +- [grouping.globalMaxGroups](#grouping.globalmaxgroups) +- [grouping.defaultPrecisionFactor](#grouping.defaultprecisionfactor) +- [timezone](#timezone) + +#### Streaming + +- [streaming.groupname](#streaming.groupname) +- [streaming.selection](#streaming.selection) +- [streaming.maxbucketspervisitor](#streaming.maxbucketspervisitor) + +#### Tracing + +- [trace.profile](#trace.profile) +- [trace.level](#trace.level) +- [trace.explainLevel](#trace.explainlevel) +- [trace.profileDepth](#trace.profiledepth) +- [trace.profiling.matching.depth](#trace.profiling.matching.depth) +- [trace.profiling.firstPhaseRanking.depth](#trace.profiling.firstPhaseRanking.depth) +- [trace.profiling.secondPhaseRanking.depth](#trace.profiling.secondPhaseRanking.depth) +- [trace.timestamps](#trace.timestamps) +- [trace.query](#trace.query) + +#### Semantic Rules + +- [rules.off](#rules.off) +- [rules.rulebase](#rules.rulebase) +- [tracelevel.rules](#tracelevel.rules) + +#### Dispatch + +- [dispatch.topKProbability](#dispatch.topkprobability) + +#### Other + +- [recall](#recall) +- [user](#user) +- [hitcountestimate](#hitcountestimate) +- [metrics.ignore](#metrics.ignore) +- [weakAnd.replace](#weakand.replace) +- [wand.hits](#wand.hits) +- [sorting.degrading](#sorting.degrading) +- [noCache](#nocache) + + +## Query + +<table className="vespa-table"> + <thead> + <tr> + <th>Parameter</th> + <th>Alias</th> + <th>Type</th> + <th>Default</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>yql</td> + <td></td> + <td>String</td> + <td></td> + <td>See the <a href="/en/querying/query-language" aria-label="YQL query guide">YQL query guide</a> for examples, and the <a href="/en/reference/querying/yql" aria-label="reference">reference</a> for details.</td> + </tr> + </tbody> +</table> + +## Native Execution Parameters + +These parameters are defined in the `native` query profile type. + +<table className="vespa-table"> + <thead> + <tr> + <th>Parameter</th> + <th>Alias</th> + <th>Type</th> + <th>Default</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>hits</td> + <td>count</td> + <td>Number</td> + <td>10</td> + <td>A positive integer, including 0. The maximum number of hits to return from the result set. <code>{`hits`}</code> is capped at <code>{`maxHits`}</code>, default 400. <code>{`maxHits`}</code> can be set in a <a href="/en/querying/query-profiles" aria-label="query profile">query profile</a>. Number of hits can also be set in <a href="/en/reference/querying/yql#limit-offset" aria-label="YQL">YQL</a>.</td> + </tr> + <tr> + <td>offset</td> + <td>start</td> + <td>Number</td> + <td>0</td> + <td>To implement pagination: The number of hits to skip when returning the result. A positive integer, including 0. <code>{`offset`}</code> is capped at <code>{`maxOffset`}</code>, default 1000. <code>{`maxOffset`}</code> can be set in a <a href="/en/querying/query-profiles" aria-label="query profile">query profile</a>. Offset can also be set in <a href="/en/reference/querying/yql#limit-offset" aria-label="YQL">YQL</a>.</td> + </tr> + <tr> + <td>queryProfile</td> + <td></td> + <td>String</td> + <td><code>{`default`}</code></td> + <td>A query profile id with format <code>{`name:version`}</code>, where version can be omitted or partially specified, e.g. <code>{`myprofile:2.1`}</code>. A <a href="/en/querying/query-profiles" aria-label="query profile">query profile</a> has default properties for a query. The default query profile is named *default*.</td> + </tr> + <tr> + <td>groupingSessionCache</td> + <td></td> + <td>Boolean</td> + <td>true</td> + <td>Set to true to enable grouping session cache. See the <a href="/en/reference/querying/grouping-language#grouping-session-cache" aria-label="grouping reference">grouping reference</a> for details.</td> + </tr> + <tr> + <td>searchChain</td> + <td></td> + <td>String</td> + <td><code>{`default`}</code></td> + <td>A search chain id with format <code>{`name:version`}</code>, where version can be omitted or partially specified, e.g. <code>{`mychain:2.1.3`}</code>. The <a href="/en/applications/chaining" aria-label="search chain">search chain</a> initially invoked when processing the query. This search chain may invoke other chains.</td> + </tr> + <tr> + <td>timeout</td> + <td></td> + <td>String</td> + <td>0.5s</td> + <td>Positive floating point number with an optional unit. Default unit is seconds (s), valid unit strings are e.g. *ms* and *s*. To set a timeout of one minute, the argument could be set to *60 s*. Space between the number and the unit is optional. It specifies the overall timeout of the query execution and can be defined in a <a href="/en/querying/query-profiles" aria-label="query profile">query profile</a>. Different classes of queries can then easily have a different latency budget/timeout using different profiles. At timeout, the hits generated thus far are returned, refer to <a href="#ranking.softtimeout.enable" aria-label="ranking.softtimeout.enable">ranking.softtimeout.enable</a> for details on HTTP status codes and response elements. Refer to the <a href="/en/querying/query-api#timeout" aria-label="Query API guide">Query API guide</a> for more details on timeout handling.</td> + </tr> + </tbody> +</table> + +## Query Model Parameters + +<table className="vespa-table"> + <thead> + <tr> + <th>Parameter</th> + <th>Alias</th> + <th>Type</th> + <th>Default</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>model.defaultIndex</td> + <td>default-index</td> + <td>String</td> + <td><code>{`default`}</code></td> + <td>An index name. The field which is searched for query terms which doesn't explicitly specify an index. Also see the <a href="/en/reference/querying/yql#defaultindex" aria-label="defaultIndex">defaultIndex</a> query annotation.</td> + </tr> + <tr> + <td>model.encoding</td> + <td>encoding</td> + <td>String</td> + <td><code>{`utf-8`}</code></td> + <td>Encoding names or aliases defined in the <a href="https://www.iana.org/assignments/character-sets/character-sets.xhtml" aria-label="IANA character sets">IANA character sets</a>. Sets the encoding to use when returning a result. The query is always encoded as UTF-8, independently of how the result will be encoded. The encodings <code>{`big5`}</code>, <code>{`euc-jp`}</code>, <code>{`euc-kr`}</code>, <code>{`gb2312`}</code>, <code>{`iso-2022-jp`}</code> and <code>{`shift-jis`}</code> also influences how <a href="/en/linguistics/linguistics-opennlp#tokenization" aria-label="tokenization">tokenization</a> is done in the absence of an explicit language setting.</td> + </tr> + <tr> + <td>model.filter</td> + <td>filter</td> + <td>String</td> + <td></td> + <td>A filter string in the <a href="/en/reference/querying/simple-query-language" aria-label="Simple Query Language">Simple Query Language</a>. Sets a filter to be combined with the <a href="#model.querystring" aria-label="model.queryString">model.queryString</a>. Typical use of a filter is to add machine generated or preferences based filter terms to the user query. Terms which are passed in the filter are not <a href="#presentation.bolding" aria-label="bolded">bolded</a>. The filter is parsed the same way as a query of type <code>{`any`}</code>, the full syntax is available. The positive terms (preceded by +) and phrases act as AND filters, the negative terms (preceded by -) act as NOT filters, while the unprefixed terms will be used to RANK the results. Unless the query has no positive terms, the filter will only restrict and influence ranking of the result set, never cause more matches than the query. The <a href="#model.querystring" aria-label="model.queryString">model.queryString</a> must be present for this to have any effect. To add filters to the YQL string, use query profiles. See <a href="/en/querying/query-profiles#example" aria-label="example">example</a>.</td> + </tr> + <tr> + <td>model.locale</td> + <td>locale</td> + <td>String</td> + <td></td> + <td>A language tag from <a href="https://www.rfc-editor.org/rfc/rfc5646" aria-label="RFC 5646">RFC 5646</a>. Sets the locale and language to use when parsing queries from a language tag, such as <code>{`en-US`}</code>. This attribute should always be set when it is known. If this parameter is not set, it will be guessed from the query and encoding, and default to english if it cannot be guessed.</td> + </tr> + <tr> + <td>model.language</td> + <td>lang, language</td> + <td>String</td> + <td></td> + <td>A language tag from <a href="https://www.rfc-editor.org/rfc/rfc5646" aria-label="RFC 5646">RFC 5646</a>, but allowing underscore instead of dash as separator character. A legacy alternative to locale. When this value is accessed, underscores will be replaced by dashes in the returned value. Also see the <a href="/en/reference/querying/yql#language" aria-label="language">language</a> query term annotation.</td> + </tr> + <tr> + <td>model.queryString</td> + <td>query</td> + <td>String</td> + <td></td> + <td>A query string in the <a href="/en/reference/querying/simple-query-language" aria-label="Simple Query Language">Simple Query Language</a>. It is combined with <a href="#model.filter" aria-label="model.filter">model.filter</a>. See the <a href="/en/reference/querying/yql#userquery" aria-label="userQuery">userQuery</a> operator for how to combine with YQL. Can also be used without YQL.</td> + </tr> + <tr> + <td>model.restrict</td> + <td>restrict</td> + <td>String</td> + <td></td> + <td>A comma-delimited list of document type (schema) names, defaulting to all schemas if not set. See <a href="/en/querying/federation" aria-label="federation">federation</a>. Use <a href="#model.sources" aria-label="model.sources">model.sources</a> to restrict to content cluster names or other source names.</td> + </tr> + <tr> + <td>model.searchPath</td> + <td>searchpath</td> + <td>String</td> + <td></td> + <td>Specification of which content nodes a query should be sent to. This is useful for debugging/monitoring and when using <a href="/en/ranking/phased-ranking#rank-phase-statistics" aria-label="Rank phase statistics">Rank phase statistics</a>. Note that in a content cluster with flat distribution (i.e. no &lt;group&gt; element in *services.xml*), there is 1 implicit group. If not set, defaults to all nodes in one group, selected by load balancing. <code>{`searchpath::ELEMENT [';' ELEMENT]*`}</code> <code>{`ELEMENT::NODE ['/' GROUP]`}</code> <code>{`NODE::EXP [',' EXP]*`}</code> <code>{`EXP::NUM | RANGE`}</code> <code>{`GROUP::NUM | '*'`}</code> <code>{`RANGE::'['NUM ',' NUM ' >'`}</code> Examples: <code>{`7/3`}</code> = node 7, group 3. <code>{`7/`}</code> = node 7, any group. <code>{`*/0`}</code> = all nodes in group 0 <code>{`7,1,9/0`}</code> = nodes 1,7 and 9, group 0. <code>{`1,[3,9>/0`}</code> = nodes 1,3,4,5,6,7,8, group 0.</td> + </tr> + <tr> + <td>model.sources</td> + <td>search, sources</td> + <td>String</td> + <td></td> + <td>A comma-separated list of content cluster names or other source names, defaulting to all sources/clusters if not set. The names of the sources to query, e.g., one or more content clusters and/or federated sources - see <a href="/en/querying/federation" aria-label="federation">federation</a>. Use <a href="#model.restrict" aria-label="model.restrict">model.restrict</a> to only search a subset of the schemas in a cluster.</td> + </tr> + <tr> + <td>model.type</td> + <td>type</td> + <td>String</td> + <td><code>{`weakAnd`}</code></td> + <td>Sets all the model.type parameters (composite, tokenization, and syntax) specifying how to parse a <a href="#model.querystring" aria-label="model.queryString">model.queryString</a> parameter at once, according to the given table: <code>{`all`}</code> → composite <code>{`and`}</code>, tokenization <code>{`internal`}</code>, syntax <code>{`simple`}</code> <code>{`any`}</code> → composite <code>{`or`}</code>, tokenization <code>{`internal`}</code>, syntax <code>{`simple`}</code> <code>{`linguistics`}</code> → composite <code>{`weakAnd`}</code>, tokenization <code>{`linguistics`}</code>, syntax <code>{`none`}</code> <code>{`phrase`}</code> → composite <code>{`phrase`}</code>, tokenization <code>{`internal`}</code>, syntax <code>{`none`}</code> <code>{`tokenize`}</code> → composite <code>{`weakAnd`}</code>, tokenization <code>{`internal`}</code>, syntax <code>{`none`}</code> <code>{`weakAnd`}</code> → composite <code>{`weakAnd`}</code>, tokenization <code>{`internal`}</code>, syntax <code>{`simple`}</code> <code>{`web`}</code> → composite <code>{`and`}</code>, tokenization <code>{`internal`}</code>, syntax <code>{`web`}</code> <code>{`yql`}</code> → composite <code>{`and`}</code>, tokenization <code>{`internal`}</code>, syntax <code>{`yql`}</code> Also see <a href="/en/reference/querying/yql#userinput" aria-label="YQL grammar">YQL grammar</a>.</td> + </tr> + <tr> + <td>model.type.composite</td> + <td></td> + <td>String</td> + <td><code>{`Determined by model.type`}</code></td> + <td>Sets the Vespa query composite type that will collect parsed terms of the query by default. and Create an AndItem which only matches if *all* terms are present. nearCreate a NearItem which matches if all the terms appear near each other (gap of 1 by default). oNear — Create an ONearItem which matches if all the terms appear near each other (gap of 1 by default), in the given order. or — Create an OrItem which matches if *any* of the terms are present. phrase — Create a PhraseItem which matches if all the terms are present in the given order with no gaps. weakAnd — Create a <a href="/en/ranking/wand#weakand" aria-label="WeakAndItem">WeakAndItem</a> which has the semantics of <code>{`or`}</code> with performance approaching <code>{`and`}</code> .</td> + </tr> + <tr> + <td>model.type.tokenization</td> + <td></td> + <td>String</td> + <td><code>{`Determined by model.type`}</code></td> + <td>Sets the tokenizer used to split the query string into tokens. internal Use the tokenizer built into the query parser. linguisticsPass the full query string as-is to the linguistics component for tokenization, exactly as on the indexing side, and collect any text and numeric token returned as-is, with no further stemming or normalization even when specified in the schema. This is only supported in conjunction with the <code>{`none`}</code> syntax option.</td> + </tr> + <tr> + <td>model.type.syntax</td> + <td></td> + <td>String</td> + <td><code>{`Determined by model.type`}</code></td> + <td>Sets the syntax used to interpret the query string. Options: <code>{`none`}</code>: No syntax: Disregard any non-searchable terms <code>{`simple`}</code>: Use the <a href="/en/reference/querying/simple-query-language" aria-label="simple query language">simple query language</a> suitable for end users. <code>{`web`}</code>: Like the <a href="/en/reference/querying/simple-query-language" aria-label="simple query language">simple query language</a> , but '+' in front of a term means "search for this term as-is", and 'a OR b' (capital OR) means match either a or b. <code>{`yql`}</code>: Parse as a <a href="/en/reference/querying/yql" aria-label="YQL query">YQL query</a> .</td> + </tr> + <tr> + <td>model.type.profile</td> + <td></td> + <td>String</td> + <td><code>{`*(null)*`}</code></td> + <td>Overrides the linguistics profile assigned to the field(s) searched. The linguistics profile is used to choose the processing done in the <a href="/en/linguistics/linguistics" aria-label="linguistics component">linguistics component</a>.</td> + </tr> + <tr> + <td>model.type.isYqlDefault</td> + <td></td> + <td>Boolean</td> + <td><code>{`false`}</code></td> + <td>Whether the model.type settings should be used as the default settings for terms in YQL queries. With this parameter turned on, the model.type settings become the default "grammar" settings in userQuery, and with tokenization set to <code>{`linguistics`}</code> this will also cause "contains" terms to not undergo stemming, normalization and lowercasing as separate operations, as using this mode delegates all token processing to a single pass through the lingustics module.</td> + </tr> + </tbody> +</table> + +## Ranking + +<table className="vespa-table"> + <thead> + <tr> + <th>Parameter</th> + <th>Alias</th> + <th>Type</th> + <th>Default</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>ranking.location</td> + <td></td> + <td>String</td> + <td></td> + <td>See <a href="/en/querying/geo-search" aria-label="Geo search">Geo search</a>. Point (two-dimensional location) to use as base for location ranking. **Important:** Deprecated in favor of adding a <a href="/en/reference/querying/yql#geolocation" aria-label="geoLocation">geoLocation</a> item to the query tree. Use inside a <a href="/en/reference/querying/yql#rank" aria-label="rank">rank</a> operator if it should be used only for ranking).</td> + </tr> + <tr> + <td>ranking.features<br />.*featurename*</td> + <td>input <br /> . *featurename* , rankfeature <br /> . *featurename*</td> + <td>String</td> + <td></td> + <td>Set a query rank feature input to a value. The key must be a query feature - <code>{`query(anyname)`}</code>, and the value must be a double, string (to be hashed to a double), or a tensor matching the <a href="/en/reference/schemas/schemas#inputs" aria-label="declared input type">declared input type</a> on <a href="/en/reference/ranking/tensor#tensor-literal-form" aria-label="tensor literal form">tensor literal form</a> - see the <a href="/en/ranking/tensor-user-guide#querying-with-tensors" aria-label="tensor user guide">tensor user guide</a>. Examples: <code>{`input.query(userageDouble)=42.1`}</code> <code>{`input.query(stringToBeHashed)=abcd`}</code> <code>{`input.query(myIndexedTensor)=[1.0, 2.0, 3.0]`}</code> <code>{`input.query(myMappedTensor)={"Tablet Keyboard Cases": 0.8, "Keyboards":0.3}`}</code></td> + </tr> + <tr> + <td>ranking.listFeatures</td> + <td>rankfeatures</td> + <td>Boolean</td> + <td>false</td> + <td>Set to true to request *all* <a href="/en/reference/schemas/schemas#rank-features" aria-label="rank-features">rank-features</a> to be calculated and returned. The rank features will be returned in the summary field *rankfeatures*. This option is typically used for MLR training, should not to be used for production.</td> + </tr> + <tr> + <td>ranking.profile</td> + <td>ranking</td> + <td>String</td> + <td><code>{`default`}</code></td> + <td>Sets <a href="/en/reference/schemas/schemas#rank-profile" aria-label="rank profile">rank profile</a> to use for assigning rank scores for documents. The <code>{`default`}</code> rank profile will be used for backends which does not have the given rank profile.</td> + </tr> + <tr> + <td>ranking.properties<br />.*propertyname*</td> + <td>rankproperty <br /> . *propertyname*</td> + <td>String</td> + <td></td> + <td>Set a <a href="/en/reference/schemas/schemas#rank-properties" aria-label="rank property">rank property</a> that is passed to, and used by a feature executor for this query. Example: <code>{`query=foo&ranking.properties.dotProduct.X={a:1,b:2}`}</code></td> + </tr> + <tr> + <td>ranking.softtimeout<br />.enable</td> + <td></td> + <td>Boolean</td> + <td>true</td> + <td>By default, the hits available are returned on <a href="#timeout" aria-label="timeout">timeout</a>. To return no hits at timeout instead, set <code>{`ranking.softtimeout.enable=false`}</code>. Softtimeout uses <code>{`ranking.softtimeout.factor`}</code> of the <a href="#timeout" aria-label="timeout">timeout</a>, default 70%. The rest of the time budget is spent on later ranking phases. The factor is adaptive, per rank profile - the factor is adjusted based on remaining time after all ranking phases, unless overridden in the query using <code>{`ranking.softtimeout.factor`}</code>. A <a href="/en/reference/querying/default-result-format#timeout" aria-label="timeout">timeout</a> element is returned in the query response at timeout. Example: query with 500ms timeout, use 300ms in first-phase ranking: <code>{`&ranking.softtimeout.enable=true<br />&ranking.softtimeout.factor=0.6<br />&timeout=0.5`}</code> The <code>{`ranking.softtimeout`}</code> settings controls what the content nodes should do in the case where the latency budget has almost been used (timeout times a factor). Return the documents recalled and ranked with the <a href="/en/ranking/phased-ranking" aria-label="first phase function">first phase function</a> within the time used, or simply don't produce a result: With soft timeout disabled, the Vespa container will return a 504 timeout without any results. When enabled, it will return the documents matched and ranked up until the timeout was reached, with a 200 OK response along with the reason the result set was degraded. The container might respond with a timeout error with HTTP response code 504 even with soft timeout enabled if the timeout is set so low that the query does not make it to the content nodes, or the container does not have any time left after input and query processing to dispatch the query to the content nodes. Read more about soft timeout in <a href="/en/performance/graceful-degradation" aria-label="coverage degradation">coverage degradation</a>.</td> + </tr> + <tr> + <td>ranking.softtimeout<br />.factor</td> + <td></td> + <td>Number</td> + <td>0.7</td> + <td>See <a href="#ranking.softtimeout.enable" aria-label="ranking.softtimeout.enable">ranking.softtimeout.enable</a>.</td> + </tr> + <tr> + <td>ranking.sorting</td> + <td>sorting</td> + <td>String</td> + <td></td> + <td>A valid <a href="/en/reference/querying/sorting-language" aria-label="sort specification">sort specification</a>. Fields you want to sort on must be stored as document attributes in the index structure by adding <a href="/en/reference/schemas/schemas#attribute" aria-label="attribute">attribute</a> to the indexing statement.</td> + </tr> + <tr> + <td>ranking.significance.useModel</td> + <td></td> + <td>Boolean</td> + <td>false</td> + <td>Enables or disables the use of significance models specified in <a href="/en/reference/applications/services/search#significance" aria-label="service.xml">service.xml</a>. Overrides <a href="/en/reference/schemas/schemas#significance" aria-label="use-model">use-model</a> set in the rank profile.</td> + </tr> + <tr> + <td>ranking.freshness</td> + <td></td> + <td>String</td> + <td></td> + <td>Sets the time which will be used as *now* during execution. <code>{`[integer]`}</code>, an absolute time in seconds since epoch, or <code>{`now-[number]`}</code>, to use a time [integer] seconds into the past, or <code>{`now`}</code> to use the current time.</td> + </tr> + <tr> + <td>ranking.queryCache</td> + <td></td> + <td>Boolean</td> + <td>false</td> + <td>Turns query cache on or off. Query is a two-phase process. If the query cache is on, the query is stored on the content nodes between the first and second phase, saving network bandwidth and also query setup time, at the expense of using more memory. It only affects the protocol phase two, see <a href="/en/performance/caches-in-vespa" aria-label="caches in Vespa">caches in Vespa</a>. It does not cache the result, it just saves resources by not forwarding the query twice (one for the first protocol phase which is find the best k documents from all nodes, to the second phase which is to fill summary data and potentially ranking features listed in summary-features in the rank profile). The <a href="/en/reference/schemas/schemas#summary-features" aria-label="summary-features">summary-features</a> are re-calculated but this setting avoids sending the query down once more. There is little downside of using it, and it can save resources and latency in cases where the query tree and query ranking features (e.g. tensors used in ranking) are large. As this is a protocol optimization, it also works with changing filter, it's not cached cross independent queries, it's just saving having to send the same query twice.</td> + </tr> + <tr> + <td>ranking.secondPhase.totalRerankCount</td> + <td></td> + <td>Number</td> + <td></td> + <td>Specifies the number of hits that should be ranked in the second ranking phase in total over the queried content nodes. Overrides the <a href="/en/reference/schemas/schemas#secondphase-total-rerank-count" aria-label="total-rerank-count">total-rerank-count</a> set in the rank profile. Setting to 0 disables second phase reranking.</td> + </tr> + <tr> + <td>ranking.secondPhase.rerankCount</td> + <td></td> + <td>Number</td> + <td></td> + <td>Specifies the number of hits that should be ranked in the second phase *per node*. Prefer using <a href="#ranking.secondphase.totalrerankcount" aria-label="totalRerankCount">totalRerankCount</a> over this.</td> + </tr> + <tr> + <td>ranking.totalKeepRankCount</td> + <td></td> + <td>Number</td> + <td></td> + <td>Specifies the number of hits for which the rank score should be kept after first phase ranking in total over the nodes participating in the query. Overrides the <a href="/en/reference/schemas/schemas#total-keep-rank-count" aria-label="total-keep-rank-count">total-keep-rank-count</a> set in the rank profile.</td> + </tr> + <tr> + <td>ranking.keepRankCount</td> + <td></td> + <td>Number</td> + <td></td> + <td>Specifies the number of hits for which the rank score should be kept after first phase ranking on each node. Overrides the <a href="/en/reference/schemas/schemas#keep-rank-count" aria-label="keep-rank-count">keep-rank-count</a> set in the rank profile. Prefer <a href="#ranking.totalkeeprankcount" aria-label="total-keep-rank-count">total-keep-rank-count</a> over this.</td> + </tr> + <tr> + <td>ranking.rankScoreDropLimit</td> + <td></td> + <td>Number</td> + <td></td> + <td>Minimum rankscore for a document to be considered a hit. Overrides the <a href="/en/reference/schemas/schemas#rank-score-drop-limit" aria-label="rank-score-drop-limit">rank-score-drop-limit</a> set in the rank profile.</td> + </tr> + <tr> + <td>ranking.secondPhase.rankScoreDropLimit</td> + <td></td> + <td>Number</td> + <td></td> + <td>Minimum rank score for a document to be considered a hit after second phase reranking or rescoring. Overrides the <a href="/en/reference/schemas/schemas#secondphase-rank-score-drop-limit" aria-label="second phase rank-score-drop-limit">second phase rank-score-drop-limit</a> set in the rank profile.</td> + </tr> + <tr> + <td>ranking.globalPhase.rerankCount</td> + <td></td> + <td>Number</td> + <td></td> + <td>Specifies the number of hits that should be re-ranked in the global ranking phase. Overrides the <a href="/en/reference/schemas/schemas#globalphase-rerank-count" aria-label="rerank-count">rerank-count</a> set in the rank profile. Setting to 0 disables the global phase reranking.</td> + </tr> + <tr> + <td>ranking.globalPhase.rankScoreDropLimit</td> + <td></td> + <td>Number</td> + <td></td> + <td>Minimum rank score for a document to be considered a hit after global phase reranking or rescoring. Overrides the <a href="/en/reference/schemas/schemas#globalphase-rank-score-drop-limit" aria-label="global phase rank-score-drop-limit">global phase rank-score-drop-limit</a> set in the rank profile.</td> + </tr> + <tr> + <td>ranking.elementGap.*fieldName*</td> + <td></td> + <td>Integer</td> + <td></td> + <td>Set or overrides <a href="/en/reference/schemas/schemas#rank-element-gap" aria-label="element-gap">element-gap</a> configured for a given *fieldName* in the rank profile. Note: Can be the integer "0" to consider elements to be adjacent, or the string "infinity" to signal that words in different elements never are considered "close".</td> + </tr> + </tbody> +</table> + +## ranking.matching + +Settings to control behavior during matching of query evaluation. + If these are set in the query, they will override any equivalent settings in the + [rank profile](/en/reference/schemas/schemas#rank-profile). + Detailed descriptions are found in the rank profile documentation. + +<table className="vespa-table"> + <thead> + <tr> + <th>Parameter</th> + <th>Alias</th> + <th>Type</th> + <th>Default</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>ranking.matching<br />.numThreadsPerSearch</td> + <td></td> + <td>integer</td> + <td></td> + <td>Rank profile equivalent: <a href="/en/reference/schemas/schemas#num-threads-per-search" aria-label="num-threads-per-search">num-threads-per-search</a> Overrides the global <a href="/en/reference/applications/services/content#requestthreads-persearch" aria-label="persearch">persearch</a> threads to a **lower** value.</td> + </tr> + <tr> + <td>ranking.matching<br />.minHitsPerThread</td> + <td></td> + <td>integer</td> + <td></td> + <td>Rank profile equivalent: <a href="/en/reference/schemas/schemas#min-hits-per-thread" aria-label="min-hits-per-thread">min-hits-per-thread</a> After estimating the number of hits for a query, this number is used to decide how many search threads to use.</td> + </tr> + <tr> + <td>ranking.matching<br />.numSearchPartitions</td> + <td></td> + <td>integer</td> + <td></td> + <td>Rank profile equivalent: <a href="/en/reference/schemas/schemas#num-search-partitions" aria-label="num-search-partitions">num-search-partitions</a> Number of logical partitions the corpus on a content node is divided in. A partition is the smallest unit a search thread will handle.</td> + </tr> + <tr> + <td>ranking.matching<br />.termwiseLimit</td> + <td></td> + <td>double [0.0, 1.0]</td> + <td></td> + <td>Rank profile equivalent: <a href="/en/reference/schemas/schemas#termwise-limit" aria-label="termwise-limit">termwise-limit</a> If estimated number of hits > corpus * termwise-limit, document candidates are pruned with a <a href="/en/performance/feature-tuning#hybrid-taat-daat" aria-label="TAAT">TAAT</a> evaluation for query terms not needed for ranking.</td> + </tr> + <tr> + <td>ranking.matching<br />.postFilterThreshold</td> + <td></td> + <td>double [0.0, 1.0]</td> + <td>1.0</td> + <td>Rank profile equivalent: <a href="/en/reference/schemas/schemas#post-filter-threshold" aria-label="post-filter-threshold">post-filter-threshold</a> Threshold value deciding if a query with an approximate <a href="/en/reference/querying/yql#nearestneighbor" aria-label="nearestNeighbor">nearestNeighbor</a> operator combined with filters is evaluated using post-filtering.</td> + </tr> + <tr> + <td>ranking.matching<br />.approximateThreshold</td> + <td></td> + <td>double [0.0, 1.0]</td> + <td>0.02</td> + <td>Rank profile equivalent: <a href="/en/reference/schemas/schemas#approximate-threshold" aria-label="approximate-threshold">approximate-threshold</a> Threshold value deciding if a query with an approximate <a href="/en/reference/querying/yql#nearestneighbor" aria-label="nearestNeighbor">nearestNeighbor</a> operator combined with filters is evaluated by searching for approximate or exact nearest neighbors.</td> + </tr> + <tr> + <td>ranking.matching<br />.filterFirstThreshold</td> + <td></td> + <td>double [0.0, 1.0]</td> + <td>0.2</td> + <td>Rank profile equivalent: <a href="/en/reference/schemas/schemas#filter-first-threshold" aria-label="filter-first-threshold">filter-first-threshold</a> Threshold value deciding if the filter is checked before computing a distance (*filter-first heuristic*) while searching the <a href="/en/reference/schemas/schemas#index-hnsw" aria-label="HNSW">HNSW</a> graph for approximate neighbors with filtering.</td> + </tr> + <tr> + <td>ranking.matching<br />.filterFirstExploration</td> + <td></td> + <td>double [0.0, 1.0]</td> + <td>0.01</td> + <td>Rank profile equivalent: <a href="/en/reference/schemas/schemas#filter-first-exploration" aria-label="filter-first-exploration">filter-first-exploration</a> Value specifying how aggressively the filter-first heuristic searches the <a href="/en/reference/schemas/schemas#index-hnsw" aria-label="HNSW">HNSW</a> graph for approximate neighbors with filtering.</td> + </tr> + <tr> + <td>ranking.matching<br />.explorationSlack</td> + <td></td> + <td>double [0.0, 1.0]</td> + <td>0.0</td> + <td>Rank profile equivalent: <a href="/en/reference/schemas/schemas#exploration-slack" aria-label="exploration-slack">exploration-slack</a> Value specifying slack to delay the termination of the search of the <a href="/en/reference/schemas/schemas#index-hnsw" aria-label="HNSW">HNSW</a> graph for nearest neighbors with or without filtering.</td> + </tr> + <tr> + <td>ranking.matching<br />.targetHitsMaxAdjustmentFactor</td> + <td></td> + <td>double [1.0, inf]</td> + <td></td> + <td>Rank profile equivalent: <a href="/en/reference/schemas/schemas#target-hits-max-adjustment-factor" aria-label="target-hits-max-adjustment-factor">target-hits-max-adjustment-factor</a> Value used to control the auto-adjustment of <a href="/en/reference/querying/yql#totaltargethits" aria-label="totalTargetHits">totalTargetHits</a> used when evaluating an approximate <a href="/en/reference/querying/yql#nearestneighbor" aria-label="nearestNeighbor">nearestNeighbor</a> operator with post-filtering.</td> + </tr> + <tr> + <td>ranking.matching<br />.filterThreshold</td> + <td></td> + <td>double [0.0, 1.0]</td> + <td></td> + <td>Rank profile equivalent: <a href="/en/reference/schemas/schemas#filter-threshold" aria-label="filter-threshold">filter-threshold</a> Threshold value (in the range [0, 1]) deciding when matching in *index* fields should be treated as filters. This happens for query terms with <a href="/en/learn/glossary#estimated-hit-ratio" aria-label="estimated hit ratios">estimated hit ratios</a> that are above the *filterThreshold*.</td> + </tr> + <tr> + <td>ranking.matching.weakand<br />.stopwordLimit</td> + <td></td> + <td>double [0.0, 1.0]</td> + <td></td> + <td>Rank profile equivalent: <a href="/en/reference/schemas/schemas#weakand-stopword-limit" aria-label="weakand stopword-limit">weakand stopword-limit</a> A number in the range [0, 1] representing the maximum <a href="/en/learn/glossary#document-frequency-normalized" aria-label="normalized document frequency">normalized document frequency</a> a query term can have in the corpus before it's considered a stopword and dropped entirely from being a part of the <code>{`weakAnd`}</code> evaluation.</td> + </tr> + <tr> + <td>ranking.matching.weakand<br />.adjustTarget</td> + <td></td> + <td>double [0.0, 1.0]</td> + <td></td> + <td>Rank profile equivalent: <a href="/en/reference/schemas/schemas#weakand-adjust-target" aria-label="weakand adjust-target">weakand adjust-target</a> A number in the range [0, 1] representing <a href="/en/learn/glossary#document-frequency-normalized" aria-label="normalized document frequency">normalized document frequency</a>. Used to derive a per-query document score threshold, where documents scoring lower than the threshold will not be considered as potential hits from the <code>{`weakAnd`}</code> operator.</td> + </tr> + <tr> + <td>ranking.matching.weakand<br />.allowDropAll</td> + <td></td> + <td>boolean</td> + <td>false</td> + <td>Rank profile equivalent: <a href="/en/reference/schemas/schemas#weakand-allow-drop-all" aria-label="weakand allow-drop-all">weakand allow-drop-all</a> A boolean value that, if set to <code>{`true`}</code>, will allow the <code>{`weakAnd`}</code> operator to drop *all* terms from the query if all terms are considered stopwords (i.e. by setting <code>{`weakAnd.stopwordLimit`}</code>). Typically used in conjunction with <a href="/en/querying/nearest-neighbor-search#querying-using-nearestneighbor-query-operator" aria-label="nearestNeighbor">nearestNeighbor</a> or other operators to ensure that the query will return hits even when all terms are considered stopwords.</td> + </tr> + </tbody> +</table> + +## ranking.matchPhase + +Settings to control behavior during the match phase of query evaluation. + If these are set in the query, they will override any + [match-phase](/en/reference/schemas/schemas#match-phase) settings in the rank profile. + Detailed descriptions are found in the rank profile documentation. + +<table className="vespa-table"> + <thead> + <tr> + <th>Parameter</th> + <th>Alias</th> + <th>Type</th> + <th>Default</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>ranking.matchPhase<br />.attribute</td> + <td></td> + <td>string</td> + <td></td> + <td>Rank profile equivalent: <a href="/en/reference/schemas/schemas#match-phase-attribute" aria-label="match-phase: attribute">match-phase: attribute</a> The attribute used to limit matches by if more than maxHits hits will be produced.</td> + </tr> + <tr> + <td>ranking.matchPhase<br />.totalMaxHits</td> + <td></td> + <td>long</td> + <td></td> + <td>The max number of hits that should be generated in total over the content nodes during the match phase. Setting the value to <code>{`0`}</code> disables match phase early termination. Rank profile equivalent: <a href="/en/reference/schemas/schemas#match-phase-total-max-hits" aria-label="match-phase: total-max-hits">match-phase: total-max-hits</a></td> + </tr> + <tr> + <td>ranking.matchPhase<br />.maxHits</td> + <td></td> + <td>long</td> + <td></td> + <td>The max number of hits that should be generated on eache content nodes during the match phase. Prefer using <a href="#ranking.matchphase.totalmaxhits" aria-label="totalMaxHits">totalMaxHits</a> over this. Rank profile equivalent: <a href="/en/reference/schemas/schemas#match-phase-max-hits" aria-label="match-phase: max-hits">match-phase: max-hits</a></td> + </tr> + <tr> + <td>ranking.matchPhase<br />.ascending</td> + <td></td> + <td>boolean</td> + <td></td> + <td>Rank profile equivalent: <a href="/en/reference/schemas/schemas#match-phase-order" aria-label="match-phase: order">match-phase: order</a> Whether to keep the documents having the highest (false) or lowest (true) values of the match phase attribute.</td> + </tr> + <tr> + <td>ranking.matchPhase<br />.diversity.attribute</td> + <td></td> + <td>string</td> + <td></td> + <td>Rank profile equivalent: <a href="/en/reference/schemas/schemas#diversity-attribute" aria-label="diversity: attribute">diversity: attribute</a> The attribute to use when deciding diversity.</td> + </tr> + <tr> + <td>ranking.matchPhase<br />.diversity.minGroups</td> + <td></td> + <td>long</td> + <td></td> + <td>Rank profile equivalent: <a href="/en/reference/schemas/schemas#diversity-min-groups" aria-label="diversity: min-groups">diversity: min-groups</a> The minimum number of groups that should be returned from the match phase grouped by the diversity attribute.</td> + </tr> + </tbody> +</table> + +## Dispatch + +<table className="vespa-table"> + <thead> + <tr> + <th>Parameter</th> + <th>Alias</th> + <th>Type</th> + <th>Default</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>dispatch.topKProbability</td> + <td></td> + <td>double</td> + <td></td> + <td>Probability to use when computing how many hits to fetch from each partition when merging and creating the final result set. See <a href="/en/reference/applications/services/content#top-k-probability" aria-label="services">services</a> for details. Default: <a href="/en/reference/applications/services/content#top-k-probability" aria-label="none">none</a>.</td> + </tr> + </tbody> +</table> + +## Presentation + +<table className="vespa-table"> + <thead> + <tr> + <th>Parameter</th> + <th>Alias</th> + <th>Type</th> + <th>Default</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>presentation.bolding</td> + <td>bolding</td> + <td>Boolean</td> + <td>true</td> + <td>Whether to bold query terms in <a href="/en/reference/schemas/schemas" aria-label="schema">schema</a> fields defined with <a href="/en/reference/schemas/schemas#bolding" aria-label="bolding: on">bolding: on</a> or <a href="/en/reference/schemas/schemas#summary" aria-label="summary: dynamic">summary: dynamic</a>.</td> + </tr> + <tr> + <td>presentation.format</td> + <td>format</td> + <td>String</td> + <td><code>{`default`}</code></td> + <td><code>{`Value`}</code>: Description <code>{`*No value* or [default](/en/reference/querying/default-result-format)`}</code>: The default, builtin JSON format <code>{`[json](/en/reference/querying/default-result-format)`}</code>: Builtin JSON format <code>{``}</code>cbor<code>{``}</code>: Builtin <a href="https://cbor.io/" aria-label="CBOR">CBOR</a> format. Binary encoding, responses are smaller and faster to render than JSON, especially for numeric data. Semantically equivalent to JSON. Cannot be used with <code>{`jsoncallback`}</code> (JSONP). Requires Vespa 8.623.5 or later. <code>{``}</code>xml<code>{``}</code>: Builtin XML format. **Important:** See <a href="/en/reference/release-notes/vespa8" aria-label="deprecations">deprecations</a>. <code>{`[page](/en/reference/querying/page-result-format)`}</code>: XML format which is suitable for use with <a href="/en/querying/page-templates" aria-label="page templates">page templates</a> . **Important:** See <a href="/en/reference/release-notes/vespa8" aria-label="deprecations">deprecations</a>. <code>{`*Any other value*`}</code>: A custom <a href="/en/applications/result-renderers" aria-label="result renderer">result renderer</a> supplied by the application The response format can also be selected via the HTTP <code>{`Accept`}</code> header. If the Accept header specifies <code>{`application/cbor`}</code> with higher priority than <code>{`application/json`}</code>, CBOR will be used. The <code>{`format`}</code> query parameter overrides the Accept header.</td> + </tr> + <tr> + <td>presentation.summary</td> + <td>summary</td> + <td>String</td> + <td></td> + <td>The name of the <a href="/en/querying/document-summaries" aria-label="summary class">summary class</a> used to select fields in results. Default: The default summary class of the schema.</td> + </tr> + <tr> + <td>presentation.template</td> + <td></td> + <td>String</td> + <td></td> + <td>The id of a deployed page template to use for this result. This should be used with the <a href="/en/reference/querying/page-result-format" aria-label="page">page</a> result format.</td> + </tr> + <tr> + <td>presentation.timing</td> + <td></td> + <td>Boolean</td> + <td>false</td> + <td>Whether a result renderer should try to add optional timing information to the rendered page - see the <a href="/en/reference/querying/default-result-format#timing" aria-label="result reference">result reference</a>.</td> + </tr> + <tr> + <td>presentation.format.tensors</td> + <td></td> + <td>String</td> + <td><code>{`short`}</code></td> + <td>Controls how tensors are rendered in the result. <code>{`Value`}</code>: Description <code>{``}</code>short<code>{``}</code>: Render the tensor value in an object having two keys, "type" containing the value, and "cells"/"blocks"/"values" ( <a href="/en/reference/schemas/document-json-format#tensor" aria-label="depending on the type">depending on the type</a> ) containing the tensor content. <br /> Render the tensor content in the <a href="/en/reference/schemas/document-json-format#tensor" aria-label="type-appropriate short form">type-appropriate short form</a> . <code>{``}</code>long<code>{``}</code>: Render the tensor value in an object having two keys, "type" containing the value, and "cells" containing the tensor content. <br /> Render the tensor content in the <a href="/en/reference/schemas/document-json-format#tensor" aria-label="general verbose form">general verbose form</a> . <code>{``}</code>short-value<code>{``}</code>: Render the tensor content directly. <br /> Render the tensor content in the <a href="/en/reference/schemas/document-json-format#tensor" aria-label="type-appropriate short form">type-appropriate short form</a> . <code>{``}</code>long-value<code>{``}</code>: Render the tensor content directly. <br /> Render the tensor content in the <a href="/en/reference/schemas/document-json-format#tensor" aria-label="general verbose form">general verbose form</a> . <code>{``}</code>hex<code>{``}</code>: Use <code>{`short`}</code> form, and render dense values <a href="/en/reference/ranking/tensor#indexed-hex-form" aria-label="hex encoded">hex encoded</a> . <br /> <code>{``}</code>hex-value<code>{``}</code>: Use <code>{`short-value`}</code> form, and render dense values <a href="/en/reference/ranking/tensor#indexed-hex-form" aria-label="hex encoded">hex encoded</a> . <br /></td> + </tr> + </tbody> +</table> + +## Grouping and Aggregation + +<table className="vespa-table"> + <thead> + <tr> + <th>Parameter</th> + <th>Alias</th> + <th>Type</th> + <th>Default</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>select</td> + <td></td> + <td>String</td> + <td></td> + <td>Requests specific multi-level result set statistics and/or hit groups to be returned in the result. Fields you want to retrieve statistics or hit groups for must be stored as document attributes in the index structure by adding attribute to the indexing statement. Default is no grouping. See the <a href="/en/querying/grouping" aria-label="grouping guide">grouping guide</a> for examples.</td> + </tr> + <tr> + <td>collapsefield</td> + <td></td> + <td>String</td> + <td></td> + <td>Comma-separated list of <a href="/en/reference/schemas/schemas#summary" aria-label="field names">field names</a>, that should only appear uniquely in a result. Hits with values in these fields which are already present in a higher-ranked hit will be filtered out. Read more in <a href="/en/querying/result-diversity" aria-label="result diversity">result diversity</a> to compare this with other options. Default is no field collapsing.</td> + </tr> + <tr> + <td>collapsesize</td> + <td></td> + <td>Number</td> + <td>1</td> + <td>The number of hits to keep in each collapsed bucket - used for all collapsefields.</td> + </tr> + <tr> + <td>collapsesize.*fieldname*</td> + <td></td> + <td>Number</td> + <td>1</td> + <td>The number of hits to keep in each collapsed bucket - used for the specified field. This value takes precedence over the value specified in <code>{`collapsesize`}</code>.</td> + </tr> + <tr> + <td>collapse.summary</td> + <td></td> + <td>String</td> + <td></td> + <td>A valid name of a document summary class. Use this summary class to fetch the fields used for collapsing. Default: Use default summary or attributes.</td> + </tr> + <tr> + <td>grouping.defaultMaxGroups</td> + <td></td> + <td>Number</td> + <td>10</td> + <td>Positive integer or <code>{`-1`}</code> to disable. The default number of groups to return when <a href="/en/querying/grouping#ordering-and-limiting-groups" aria-label="max">max</a> is not specified.</td> + </tr> + <tr> + <td>grouping.defaultMaxHits</td> + <td></td> + <td>Number</td> + <td>10</td> + <td>Positive integer or <code>{`-1`}</code> to disable. The default number of hits to return when <a href="/en/querying/grouping#hits-per-group" aria-label="max">max</a> is not specified.</td> + </tr> + <tr> + <td>grouping.globalMaxGroups</td> + <td></td> + <td>Number</td> + <td>10000</td> + <td>Positive integer or <code>{`-1`}</code> to disable. A cost limit for grouping queries. Any query that may exceed this threshold will be preemptively failed by the container. The limit is defined as the total number of groups and document summaries a query may produce. A query that does not have an implicit or explicit <code>{`max`}</code> defined for all levels will always fail if limit is enabled. This parameter can only be overridden in a <a href="/en/querying/query-profiles" aria-label="query profile">query profile</a>. See the <a href="/en/querying/grouping#global-limit" aria-label="grouping guide">grouping guide</a> for practical examples.</td> + </tr> + <tr> + <td>grouping.defaultPrecisionFactor</td> + <td></td> + <td>Decimal <br /> number</td> + <td>2.0</td> + <td>The default precision scale factor when <a href="/en/querying/grouping#ordering-and-limiting-groups" aria-label="precision">precision</a> is not specified. The final precision value is calculated by multiplying the effective <code>{`max`}</code> value with the scale factor.</td> + </tr> + <tr> + <td>timezone</td> + <td></td> + <td>String</td> + <td><code>{`utc`}</code></td> + <td>Specifies a timezone that will be used to offset all <code>{`time`}</code> related expressions in grouping. See <a href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/TimeZone.html#getTimeZone(java.lang.String)" aria-label="Java's definition">Java's definition</a> for valid timezones. See the <a href="/en/querying/grouping#timezone-grouping" aria-label="grouping guide">grouping guide</a> for examples.</td> + </tr> + </tbody> +</table> + +## Streaming + +Parameters for [streaming search mode](/en/performance/streaming-search). + +<table className="vespa-table"> + <thead> + <tr> + <th>Parameter</th> + <th>Alias</th> + <th>Type</th> + <th>Default</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>streaming.groupname</td> + <td></td> + <td>A string</td> + <td></td> + <td>Sets the group (specified by <a href="/en/schemas/documents#id-scheme" aria-label="g=&lt;groupname&gt;">g=&lt;groupname&gt;</a>) of the documents to stream through.</td> + </tr> + <tr> + <td>streaming.selection</td> + <td></td> + <td>A <a href="/en/reference/writing/document-selector-language" aria-label="document selection">document selection</a></td> + <td></td> + <td>Restricts streaming search using a selection expression instead of a group id. If the selection is on the form <code>{`id.group == "foo" or id.group == "bar" or id.group == ...`}</code> this will only stream documents in those groups, which is efficient for a small number of groups. If any other selection is used, this will stream through *all* groups, which is very costly.</td> + </tr> + <tr> + <td>streaming.maxbucketspervisitor</td> + <td></td> + <td>An integer Positive infinity If set, limit backend bucket concurrency to the specified number of buckets. Can be used to explicitly control resource usage for extremely large streaming search locations. This is an expert option.</td> + <td></td> + <td></td> + </tr> + </tbody> +</table> + +## Tracing + +Parameters controlling trace information returning with the result for diagnostics. + +<table className="vespa-table"> + <thead> + <tr> + <th>Parameter</th> + <th>Alias</th> + <th>Type</th> + <th>Default</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>trace.profile</td> + <td>profile</td> + <td>Boolean</td> + <td>false</td> + <td>True to produce a structured trace for performance analysis. Returns a structured trace for performance analysis. This is a shorthand to set various other parameters to the suitable values to generate a performance trace.</td> + </tr> + <tr> + <td>trace.level</td> + <td>tracelevel</td> + <td>Number</td> + <td>0</td> + <td>A positive number. Default is no tracing. Collect trace information for debugging when running a query. Higher numbers give progressively more detail on query transformations, searcher execution and content node(s) query execution. See <a href="/en/querying/query-api#query-tracing" aria-label="query tracing">query tracing</a> for details and examples. Tracing is subject to change at any time, the below is a guide: <code>{`Level`}</code>: Description <code>{`1`}</code>: Basic tracing in container <code>{`2`}</code>: Basic tracing, more details <code>{`3`}</code>: Basic tracing, even more details <code>{`4`}</code>: Include timing info from content nodes <code>{`5`}</code>: Even more timing info from content nodes <code>{`6`}</code>: Include the query execution plan (blueprint) <code>{`7`}</code>: Include the query execution tree</td> + </tr> + <tr> + <td>trace.explainLevel</td> + <td>explainlevel</td> + <td>Number</td> + <td>0</td> + <td>Set to a positive number to collect query execution information for debugging when running a query. Higher numbers give progressively more detail on content node query execution. Tuning this parameter is useful if we want to get more information from the content nodes without gathering lots of trace information from the container chain. Explanation is subject to change at any time, the below is a guide: <code>{`Level`}</code>: Description <code>{`1`}</code>: Timing and overall query plan (blueprint) from each content node <code>{`2`}</code>: Timing per search thread and execution tree (search iterator tree) Note that you might get the same at <a href="#trace.level" aria-label="trace.level">trace.level</a> 5 and above. Default is no explanation. Tracing with <code>{`trace.explainLevel`}</code> also requires that <a href="#trace.level" aria-label="trace.level">trace.level</a> is positive.</td> + </tr> + <tr> + <td>trace.profileDepth</td> + <td></td> + <td>Number</td> + <td>0</td> + <td>Turns on performance profiling of the content node query execution for <a href="#trace.profiling.matching.depth" aria-label="matching">matching</a>, <a href="#trace.profiling.firstPhaseRanking.depth" aria-label="first-phase ranking">first-phase ranking</a>, and <a href="#trace.profiling.secondPhaseRanking.depth" aria-label="second-phase ranking">second-phase ranking</a>. How profiling is performed is based on whether <code>{`trace.profileDepth`}</code> is positive or negative: <code>{`Type`}</code>: Description <code>{`Tree`}</code>: A positive number specifies the depth used by a tree profiler. A higher number means more profiler data. The output resembles the structure of the search iterator tree or rank expression tree being profiled, with total time and self time tracked per component (node in the tree). <code>{`Flat`}</code>: A negative number specifies the topn (cut-off) used by a flat profiler. The output returns the topn components that use the most self time. The performance profiling output is subject to change at any time. Default is no information. Tracing with <code>{`trace.profileDepth`}</code> also requires that <a href="#trace.level" aria-label="trace.level">trace.level</a> is positive.</td> + </tr> + <tr> + <td>trace.profiling.matching.depth</td> + <td></td> + <td>Number</td> + <td>0</td> + <td>Turns on profiling of <a href="/en/performance/sizing-search#life-of-a-query-in-vespa" aria-label="matching">matching</a> of the content node query execution. This exposes information about how time spent on matching is distributed between individual search iterators. The profiling output is tagged *match_profiling* and is subject to change at any time. Default is no information. See <a href="#trace.profiledepth" aria-label="trace.profileDepth">trace.profileDepth</a> for semantics of this parameter. Tracing with <code>{`trace.profiling.matching.depth`}</code> requires that <a href="#trace.level" aria-label="trace.level">trace.level</a> is positive.</td> + </tr> + <tr> + <td>trace.profiling.firstPhaseRanking.depth</td> + <td></td> + <td>Number</td> + <td>0</td> + <td>Turns on profiling of the <a href="/en/basics/ranking" aria-label="first-phase ranking">first-phase ranking</a> of the content node query execution. This exposes information about how time spent on first-phase ranking is distributed between individual <a href="/en/reference/ranking/rank-features" aria-label="rank features">rank features</a>. The profiling output is tagged *first_phase_profiling* and is subject to change at any time. Default is no information. See <a href="#trace.profiledepth" aria-label="trace.profileDepth">trace.profileDepth</a> for semantics of this parameter. Tracing with <code>{`trace.profiling.firstPhaseRanking.depth`}</code> also requires that <a href="#trace.level" aria-label="trace.level">trace.level</a> is positive.</td> + </tr> + <tr> + <td>trace.profiling.secondPhaseRanking.depth</td> + <td></td> + <td>Number</td> + <td>0</td> + <td>Turns on profiling of the <a href="/en/basics/ranking" aria-label="second-phase ranking">second-phase ranking</a> of the content node query execution. This exposes information about how time spent on second-phase ranking is distributed between individual <a href="/en/reference/ranking/rank-features" aria-label="rank features">rank features</a>. The profiling output is tagged *second_phase_profiling* and is subject to change at any time. Default is no information. See <a href="#trace.profiledepth" aria-label="trace.profileDepth">trace.profileDepth</a> for semantics of this parameter. Tracing with <code>{`trace.profiling.secondPhaseRanking.depth`}</code> also requires that <a href="#trace.level" aria-label="trace.level">trace.level</a> is positive.</td> + </tr> + <tr> + <td>trace.timestamps</td> + <td></td> + <td>Boolean</td> + <td>false</td> + <td>Enable to get timing information already at <a href="#trace.level" aria-label="trace.level=1">trace.level=1</a>. This is useful for debugging latency spent at different components in the container search chain without rendering a lot of string data which is associated with higher trace levels.</td> + </tr> + <tr> + <td>trace.query</td> + <td></td> + <td>Boolean</td> + <td>true</td> + <td>Whether to include the query in any trace messages. This is useful for avoiding query serialization with very large queries to avoid impact from it on performance and excessively large traces.</td> + </tr> + </tbody> +</table> + +## Semantic Rules + +Refer to [semantic rules](/en/reference/querying/semantic-rules). + +<table className="vespa-table"> + <thead> + <tr> + <th>Parameter</th> + <th>Alias</th> + <th>Type</th> + <th>Default</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>rules.off</td> + <td></td> + <td>Boolean</td> + <td>true</td> + <td>Turn rule evaluation off for this query.</td> + </tr> + <tr> + <td>rules.rulebase</td> + <td></td> + <td>String</td> + <td></td> + <td>A rule base name - the name of the rule base to use for these queries.</td> + </tr> + <tr> + <td>tracelevel.rules</td> + <td></td> + <td>Number</td> + <td></td> + <td>The amount of rule evaluation trace output to show, higher number means more details. This is useful to see a trace from rule evaluation without having to see trace from all other searchers at the same time.</td> + </tr> + </tbody> +</table> + +## Other + +<table className="vespa-table"> + <thead> + <tr> + <th>Parameter</th> + <th>Alias</th> + <th>Type</th> + <th>Default</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>recall</td> + <td></td> + <td>String</td> + <td></td> + <td>Any allowed collection of recall terms. Sets a recall parameter to be combined with the query. This is identical to <a href="#model.filter" aria-label="filter">filter</a>, except that recall terms are not exposed to the ranking framework and thus not ranked. As such, one can not use unprefixed terms; they must either be positive or negative.</td> + </tr> + <tr> + <td>user</td> + <td></td> + <td>String</td> + <td></td> + <td>The id of the user making the query. The content of the argument is made available to the search chain, but it triggers no features in Vespa apart from being propagated to the access log.</td> + </tr> + <tr> + <td>hitcountestimate</td> + <td></td> + <td>Boolean</td> + <td>false</td> + <td>Make this an estimation query. No hits will be returned, and total hit count will be set to an estimate of what executing the query as a normal query would give.</td> + </tr> + <tr> + <td>metrics.ignore</td> + <td></td> + <td>Boolean</td> + <td>false</td> + <td>Ignore metric collection for this query request, useful for <a href="/en/performance/container-tuning#container-warmup" aria-label="warm-up queries">warm-up queries</a>.</td> + </tr> + <tr> + <td>weakAnd.replace</td> + <td></td> + <td>Boolean</td> + <td>false</td> + <td>Replace all instances of OR in the query tree with weakAnd.</td> + </tr> + <tr> + <td>wand.hits</td> + <td></td> + <td>Number</td> + <td>100</td> + <td>Used in combination with <a href="#weakand.replace" aria-label="weakAnd.replace">weakAnd.replace</a>. Sets the targetHits of the new weakAnds to the specified value.</td> + </tr> + <tr> + <td>sorting.degrading</td> + <td></td> + <td>Boolean</td> + <td>true</td> + <td>When sorting on a <a href="/en/content/attributes" aria-label="single-value numeric attribute with fast-search">single-value numeric attribute with fast-search</a> an optimization is activated to return early, with an inaccurate total-hits count. Set <code>{`sorting.degrading`}</code> to false to disable this optimization. This optimization sets the primary sorting attribute as the <a href="#ranking.matchphase.attribute" aria-label="match phase attribute">match phase attribute</a>, and <a href="#ranking.matchphase.maxhits" aria-label="match phase maxHits">match phase maxHits</a> equal to <code>{`max(10000, maxHits+maxOffset)`}</code>. <a href="#hits" aria-label="maxHits">maxHits</a> and <a href="#offset" aria-label="maxOffset">maxOffset</a> can be set in a query profile.</td> + </tr> + <tr> + <td>noCache</td> + <td>nocache</td> + <td>Boolean</td> + <td>false</td> + <td>Sets whether this query should never be served from a cache. Vespa has <a href="/en/performance/caches-in-vespa" aria-label="few caches">few caches</a>, and this parameter does not control any of them. Therefore, this parameter has no effect</td> + </tr> + </tbody> +</table> + +## HTTP status codes + +The following rules determine which HTTP status code is returned: + +- If the Result contains no errors (Result.hits().getError()==null): 200 OK is returned. + +- If the Result contains errors and no regular hits: + + + If the error code of any ErrorMessage in the Result + (Result.hits().getErrorHit().errorIterator()) is a "WEB SERVICE ERROR CODE", + the first of those is returned. + + + Otherwise, if it is an "HTTP COMPATIBLE ERROR CODE", the mapping of it is returned. + + + Otherwise 500 INTERNAL_SERVER_ERROR is returned. + +*List of possible HTTP status codes and their descriptions.* + +<table className="vespa-table"> + <thead> + <tr> + <th>Code</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>200</td> + <td>OK</td> + </tr> + <tr> + <td>400</td> + <td>Bad Request</td> + </tr> + <tr> + <td>401</td> + <td>Unauthorized</td> + </tr> + <tr> + <td>403</td> + <td>Forbidden</td> + </tr> + <tr> + <td>404</td> + <td>Not Found</td> + </tr> + <tr> + <td>405</td> + <td>Method Not Allowed</td> + </tr> + <tr> + <td>408</td> + <td>Request Timeout</td> + </tr> + <tr> + <td>428</td> + <td>Precondition Required</td> + </tr> + <tr> + <td>431</td> + <td>Request Header Fields Too Large</td> + </tr> + <tr> + <td>500</td> + <td>Internal Server Error</td> + </tr> + <tr> + <td>502</td> + <td>Bad Gateway</td> + </tr> + <tr> + <td>503</td> + <td>Service Unavailable; no available search handler threads in the jdisc container to serve the request. See <a href="/en/performance/container-tuning#container-worker-threads" aria-label="Container Tuning">Container Tuning</a> on sizing thread pools.</td> + </tr> + <tr> + <td>504</td> + <td>Gateway Timeout</td> + </tr> + <tr> + <td>507</td> + <td>Insufficient Storage</td> + </tr> + </tbody> +</table> + +Mapping of internal error codes to HTTP status codes. + +<table className="vespa-table"> + <thead> + <tr> + <th>Error Code</th> + <th>HTTP Code</th> + </tr> + </thead> + <tbody> + <tr> + <td>com.yahoo.container.protect.Error.BAD_REQUEST</td> + <td>400</td> + </tr> + <tr> + <td>com.yahoo.container.protect.Error.UNAUTHORIZED</td> + <td>401</td> + </tr> + <tr> + <td>com.yahoo.container.protect.Error.FORBIDDEN</td> + <td>403</td> + </tr> + <tr> + <td>com.yahoo.container.protect.Error.NOT_FOUND</td> + <td>404</td> + </tr> + <tr> + <td>com.yahoo.container.protect.Error.INTERNAL_SERVER_ERROR</td> + <td>500</td> + </tr> + <tr> + <td>com.yahoo.container.protect.Error.INSUFFICIENT_STORAGE</td> + <td>507</td> + </tr> + </tbody> +</table> + +## select + +A `select` query is equivalent in structure to YQL, written in JSON. + Contains subparameters `where`, `grouping` and `fields`. + +<table className="vespa-table"> + <thead> + <tr> + <th>Parameter</th> + <th>Alias</th> + <th>Type</th> + <th>Default</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>where</td> + <td></td> + <td>String</td> + <td></td> + <td>A string with JSON. Refer to the <a href="/en/reference/querying/json-query-language" aria-label="select reference">select reference</a> for details.</td> + </tr> + <tr> + <td>grouping</td> + <td></td> + <td>String</td> + <td></td> + <td>A string with JSON. Refer to the <a href="/en/reference/querying/json-query-language" aria-label="select reference">select reference</a> for details.</td> + </tr> + <tr> + <td>fields</td> + <td></td> + <td>String</td> + <td></td> + <td>A JSON array of <a href="/en/querying/document-summaries#selecting-summary-fields-in-yql" aria-label="summary field">summary field</a> names to include in each hit. Equivalent to the field list in a YQL <code>{`select`}</code> clause. Refer to the <a href="/en/reference/querying/json-query-language" aria-label="select reference">select reference</a> for details.</td> + </tr> + </tbody> +</table> diff --git a/en/reference/api/state-v1.html b/en/reference/api/state-v1.html deleted file mode 100644 index 8ba536ca86..0000000000 --- a/en/reference/api/state-v1.html +++ /dev/null @@ -1,422 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "/state/v1 API reference" -redirect_from: -- /en/reference/state-config-inspect.html -- /en/reference/state-v1.html ---- - -<p> - Every service exposes the <code>/state/v1</code> API - use <a href="/en/reference/operations/self-managed/tools.html#vespa-model-inspect"> - vespa-model-inspect</a> to find ports, - see the <a href="https://github.com/vespa-engine/sample-apps/tree/master/examples/operations/multinode-HA">multinode-HA</a> - sample application for practical examples. - Besides the endpoints listed below, a service might have additional endpoints specific to that service - - this is the case for <a href="/en/content/proton.html#state-v1-api">Proton</a>. -</p> - -<h2 id="http-requests">HTTP requests</h2> -<table class="table"> - <thead> - <tr> - <th>HTTP request</th> - <th>state/v1 operation</th> - <th>Description</th> - </tr> - </thead> - <tbody> - <tr> - <th>GET</th> - <td colspan="2"> - <p id="get"> - </p> - </td> - </tr> - <tr> - <td></td> - <th>Service config generation</th> - <td> - <p id="state-v1-config"></p> - <pre>/state/v1/config</pre> - <p> - In the response, <a href="#config">config</a> has a mandatory <a href="#generation">generation</a> - and one or more <service> elements: - </p> - <ul> - <li>sentinel</li> - <li>container</li> - <li>distributor</li> - <li>logd</li> - <li>slobroks</li> - <li>servicelayer</li> - <li>proton</li> - </ul> - <p> - Note: Other configuration elements can also be added as a service. - <!-- Decided not to document this, irrelevant, ToDo: remove in future releases? - { - "config": { - "generation": 11, - "proton": { - "generation": 11 - }, - "proton.documentdb.music": { - "generation": 11 - } - } - } - --> - A <service> has a mandatory <a href="#generation">generation</a>. - An optional <a href="#message">message</a> can be returned. Example: - </p> -<pre>{% highlight json %} -{ - "config": { - "generation": 11, - "slobroks": { - "generation": 11, - "message": "ok" - } - } -} -{% endhighlight %}</pre> - </td> - </tr> - <tr> - <td></td> - <th>Service version</th> - <td> - <p id="state-v1-version"></p> - <pre>/state/v1/version</pre> - <p> - Returns a mandatory service <a href="#version">version</a>. Example: - </p> -<pre>{% highlight json %} -{ - "version" : "8.43.64" -} -{% endhighlight %}</pre> - </td> - </tr> - <tr> - <td></td> - <th>Service health</th> - <td> - <p id="state-v1-health"></p> - <pre>/state/v1/health</pre> - <p> - Returns the service status, - with <a href="#time">time</a>, <a href="#status">status</a> and <a href="#metrics">metrics</a>. - Metrics contains <code>requestsPerSecond</code> and <code>latencySeconds</code>, - see <a href="https://github.com/vespa-engine/vespa/blob/master/container-disc/src/main/java/com/yahoo/container/jdisc/state/StateHandler.java"> - StateHandler</a>. - </p> - <p> - Example: - </p> -<pre>{% highlight json %} -{ - "time": 1661863544346, - "status": { - "code": "up" - }, - "metrics": { - "snapshot": { - "from": 1661863483.422, - "to": 1661863543.38 - }, - "values": [ - { - "name": "requestsPerSecond", - "values": { - "count": 30, - "rate": 0.5 - } - }, - { - "name": "latencySeconds", - "values": { - "average": 0.001, - "sum": 0, - "count": 0, - "last": 0.001, - "max": 0.001, - "min": 0.001, - "rate": 0 - } - } - ] - } -} -{% endhighlight %}</pre> - </td> - </tr> - <tr> - <td></td> - <th>Service metrics</th> - <td> - <p id="state-v1-metrics"></p> - <pre>/state/v1/metrics</pre> - <p> - Same as <code>/state/v1/health</code>, but with a full metrics set. - <!-- ToDo: link to metrics doc ++ --> - </p> - <p> - A metric has a <a href="#name">name</a> and <a href="#values">values</a>, - and can have a <a href="#description">description</a> and a set of <a href="#dimensions">dimensions</a>: - </p> -<pre>{% highlight json %} -{ - "name": "content.proton.documentdb.matching.rank_profile.query_setup_time", - "description": "Average time (sec) spent setting up and tearing down queries", - "values": { - "average": 0, - "sum": 0, - "count": 0, - "rate": 0, - "min": 0, - "max": 0, - "last": 0 - }, - "dimensions": { - "documenttype": "music", - "rankProfile": "default" - } -} -{% endhighlight %}</pre> - </td> - </tr> - <tr> - <td></td> - <th>Service metric histograms</th> - <td> - <p id="state-v1-metrics-histograms"></p> - <pre>/state/v1/metrics/histograms</pre> - <p> - See <a href="/en/operations/self-managed/monitoring.html#histograms">histograms</a> for usage. - The histograms are implemented using <a href="http://hdrhistogram.org/">HdrHistogram</a>, - and the CSV result is what that library generates. - </p> - </td> - </tr> - - </tbody> -</table> - - - -<h2 id="http-status-codes">HTTP status codes</h2> -<p> - Non-exhaustive list of status codes: -</p> -<table class="table"> - <thead> - <tr> - <th>Code</th> - <th>Description</th> - </tr> - </thead> - <tbody> - <tr> - <th>200</th> - <td>OK.</td> - </tr> <!-- ToDo: more codes --> - </tbody> -</table> - - - -<h2 id="response-format">Response format</h2> -<p> - Responses are in JSON format, with the following fields: -</p> -<table class="table"> - <thead> - <tr> - <th>Element</th> - <th>Parent</th> - <th>Type</th> - <th>Description</th> - </tr> - </thead> - <tbody> - <tr> - <td><p id="config">config</p></td> - <td></td> - <td>Object</td> - <td>Root element for /state/v1/config.</td> - </tr> - <tr> - <td><p id="generation">generation</p></td> - <td>config</td> - <td>Number</td> - <td> - The generation number is the number for the config that is active in the application. - </td> - <!-- ToDo: link to more info on config generation, it is a key concept --> - </tr> - <tr> - <td><p id="message">message</p></td> - <td>config</td> - <td>String</td> - <td>An info or error message.</td> - </tr> - <tr> - <td><p id="version">version</p></td> - <td></td> - <td>String</td> - <td>Vespa version.</td> - </tr> - <tr> - <td><p id="time">time</p></td> - <td></td> - <td>Number</td> - <td>Epoch in microseconds.</td> - </tr> - <tr> - <td><p id="status">status</p></td> - <td></td> - <td>Object</td> - <td></td> - </tr> - <tr> - <td><p id="code">code</p></td> - <td>status</td> - <td>String</td> - <td> - Service status code - one of: - <ul> - <li>up</li> - <li>down</li> - <li>initializing</li> - </ul> - <p> - Containers with the <a href="../../querying/query-api.html">query API</a> enabled return <code>initializing</code> - while waiting for content nodes to start, - see <a href="https://github.com/vespa-engine/sample-apps/tree/master/examples/operations/multinode-HA"> - example</a>. - <code>up</code> means that the service is fully up. - Assume status <code>down</code> if no response. - Refer to <a href="https://github.com/vespa-engine/vespa/blob/master/container-disc/src/main/java/com/yahoo/container/jdisc/state/StateMonitor.java"> - StateMonitor</a> for implementation. - </p> - </td> - </tr> - <tr> - <td><p id="message-status">message</p></td> - <td>status</td> - <td>String</td> - <td> - Message is optional - it is normally empty if the service is up, - while it is set to a textual reason for why it is unavailable, if so. - </td> - </tr> - <tr> - <td><p id="metrics">metrics</p></td> - <td></td> - <td>Object</td> - <td>Snapshot of metric values.</td> - </tr> - <tr> - <td><p id="snapshot">snapshot</p></td> - <td>metrics</td> - <td>Object</td> - <td>Time period for metrics snapshot.</td> - </tr> - <tr> - <td><p id="from">from</p></td> - <td>snapshot</td> - <td>Number</td> - <td>Epoch in seconds, with microseconds fraction.</td> - <!-- ToDo: Why seconds here when microseconds in "time" element ? --> - </tr> - <tr> - <td><p id="to">to</p></td> - <td>snapshot</td> - <td>Number</td> - <td>Epoch in seconds, with microseconds fraction.</td> - </tr> - <tr> - <td><p id="values">values</p></td> - <td>metrics</td> - <td>Array</td> - <td>Array of metric objects.</td> - </tr> - <tr> - <td><p id="name">name</p></td> - <td>values</td> - <td>String</td> - <td>Metric name.</td> - </tr> - <tr> - <td><p id="description">description</p></td> - <td>values</td> - <td>String</td> - <td>Textual description of the metric.</td> - </tr> - <tr> - <td><p id="dimensions">dimensions</p></td> - <td>values</td> - <td>Object</td> - <td>Set of dimension name/value pairs.</td> - </tr> - <tr> - <td><p id="values-metric">values</p></td> - <td>values</td> - <td>Object</td> - <td>Set of metric values.</td> - </tr> - <tr> - <td><p id="average">average</p></td> - <td>values</td> - <td>Number</td> - <td>Average metric value, typically <em>sum</em> divided by <em>count</em>.</td> - </tr> - <tr> - <td><p id="sum">sum</p></td> - <td>values</td> - <td>Number</td> - <td>Sum of metric values in snapshot.</td> <!-- ToDo: is this useful? --> - </tr> - <tr> - <td><p id="count">count</p></td> - <td>values</td> - <td>Number</td> - <td> - Number of times metric has been set. - For instance in a metric counting number of operations done, - it will give the number of operations added for that snapshot period. - For a value metric, for instance latency of operations, - the count will give how many times latencies have been added to the metric. - </td> - </tr> - <tr> - <td><p id="last">last</p></td> - <td>values</td> - <td>Number</td> - <td>Last metric value.</td> - </tr> - <tr> - <td><p id="max">max</p></td> - <td>values</td> - <td>Number</td> - <td>Max metric value in snapshot.</td> - </tr> - <tr> - <td><p id="min">min</p></td> - <td>values</td> - <td>Number</td> - <td>Min metric value in snapshot.</td> - </tr> - <tr> - <td><p id="rate">rate</p></td> - <td>values</td> - <td>Number</td> - <td>Metric rate: <em>count</em> divided by <em>snapshot interval</em>.</td> - </tr> - <!-- ToDo: still valid? - "median" : 124.0, - "std_deviation": 5.43 - --> - </tbody> -</table> diff --git a/en/reference/api/state-v1.mdx b/en/reference/api/state-v1.mdx new file mode 100644 index 0000000000..7eea28bc7c --- /dev/null +++ b/en/reference/api/state-v1.mdx @@ -0,0 +1,203 @@ +--- +title: "/state/v1 API reference" +description: "/state/v1 API reference in Vespa applications." +--- +<table className="vespa-table"> + <thead> + <tr> + <th>HTTP request</th> + <th>state/v1 operation</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>GET</td> + <td></td> + <td></td> + </tr> + <tr> + <td></td> + <td>Service config generation</td> + <td><code>{`/state/v1/config`}</code> In the response, <a href="#config" aria-label="config">config</a> has a mandatory <a href="#generation" aria-label="generation">generation</a> and one or more <service> elements: sentinel container distributor logd slobroks servicelayer proton Note: Other configuration elements can also be added as a service. A <service> has a mandatory <a href="#generation" aria-label="generation">generation</a>. An optional <a href="#message" aria-label="message">message</a> can be returned. Example: <code>{``}</code><code>{`{ "config": {"generation": 11, "slobroks": {"generation": 11, "message": "ok"}}}`}</code><code>{``}</code></td> + </tr> + <tr> + <td></td> + <td>Service version</td> + <td><code>{`/state/v1/version`}</code> Returns a mandatory service <a href="#version" aria-label="version">version</a>. Example: <code>{``}</code><code>{`{ "version": "8.43.64"}`}</code><code>{``}</code></td> + </tr> + <tr> + <td></td> + <td>Service health</td> + <td><code>{`/state/v1/health`}</code> Returns the service status, with <a href="#time" aria-label="time">time</a>, <a href="#status" aria-label="status">status</a> and <a href="#metrics" aria-label="metrics">metrics</a>. Metrics contains <code>{`requestsPerSecond`}</code> and <code>{`latencySeconds`}</code>, see <a href="https://github.com/vespa-engine/vespa/blob/master/container-disc/src/main/java/com/yahoo/container/jdisc/state/StateHandler.java" aria-label="StateHandler">StateHandler</a>. Example: <code>{`{`}</code> <code>{`"time": 1661863544346,`}</code> <code>{`"status": {`}</code> <code>{`"code": "up"`}</code> <code>{`},`}</code> <code>{`"metrics": {`}</code> <code>{`"snapshot": {`}</code> <code>{`"from": 1661863483.422,`}</code> <code>{`"to": 1661863543.38`}</code> <code>{`},`}</code> <code>{`"values": [`}</code> <code>{`{`}</code> <code>{`"name": "requestsPerSecond",`}</code> <code>{`"values": {`}</code> <code>{`"count": 30,`}</code> <code>{`"rate": 0.5`}</code> <code>{`}`}</code> <code>{`},`}</code> <code>{`{`}</code> <code>{`"name": "latencySeconds",`}</code> <code>{`"values": {`}</code> <code>{`"average": 0.001,`}</code> <code>{`"sum": 0,`}</code> <code>{`"count": 0,`}</code> <code>{`"last": 0.001,`}</code> <code>{`"max": 0.001,`}</code> <code>{`"min": 0.001,`}</code> <code>{`"rate": 0`}</code> <code>{`}`}</code> <code>{`}`}</code> <code>{`]`}</code> <code>{`}`}</code> <code>{`}`}</code></td> + </tr> + <tr> + <td></td> + <td>Service metrics</td> + <td><code>{`/state/v1/metrics`}</code> Same as <code>{`/state/v1/health`}</code>, but with a full metrics set. A metric has a <a href="#name" aria-label="name">name</a> and <a href="#values" aria-label="values">values</a>, and can have a <a href="#description" aria-label="description">description</a> and a set of <a href="#dimensions" aria-label="dimensions">dimensions</a>: <code>{`{`}</code> <code>{`"name": "content.proton.documentdb.matching.rank_profile.query_setup_time",`}</code> <code>{`"description": "Average time (sec) spent setting up and tearing down queries",`}</code> <code>{`"values": {`}</code> <code>{`"average": 0,`}</code> <code>{`"sum": 0,`}</code> <code>{`"count": 0,`}</code> <code>{`"rate": 0,`}</code> <code>{`"min": 0,`}</code> <code>{`"max": 0,`}</code> <code>{`"last": 0`}</code> <code>{`},`}</code> <code>{`"dimensions": {`}</code> <code>{`"documenttype": "music",`}</code> <code>{`"rankProfile": "default"`}</code> <code>{`}`}</code> <code>{`}`}</code></td> + </tr> + <tr> + <td></td> + <td>Service metric histograms</td> + <td><code>{`/state/v1/metrics/histograms`}</code> See <a href="/en/operations/self-managed/monitoring#histograms" aria-label="histograms">histograms</a> for usage. The histograms are implemented using <a href="http://hdrhistogram.org/" aria-label="HdrHistogram">HdrHistogram</a>, and the CSV result is what that library generates.</td> + </tr> + </tbody> +</table> + +<table className="vespa-table"> + <thead> + <tr> + <th>Element</th> + <th>Parent</th> + <th>Type</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>config</td> + <td></td> + <td>Object</td> + <td>Root element for /state/v1/config.</td> + </tr> + <tr> + <td>generation</td> + <td>config</td> + <td>Number</td> + <td>The generation number is the number for the config that is active in the application.</td> + </tr> + <tr> + <td>message</td> + <td>config</td> + <td>String</td> + <td>An info or error message.</td> + </tr> + <tr> + <td>version</td> + <td></td> + <td>String</td> + <td>Vespa version.</td> + </tr> + <tr> + <td>time</td> + <td></td> + <td>Number</td> + <td>Epoch in microseconds.</td> + </tr> + <tr> + <td>status</td> + <td></td> + <td>Object</td> + <td></td> + </tr> + <tr> + <td>code</td> + <td>status</td> + <td>String</td> + <td>Service status code - one of: up down initializing Containers with the <a href="/en/querying/query-api" aria-label="query API">query API</a> enabled return <code>{`initializing`}</code> while waiting for content nodes to start, see <a href="https://github.com/vespa-engine/sample-apps/tree/master/examples/operations/multinode-HA" aria-label="example">example</a>. <code>{`up`}</code> means that the service is fully up. Assume status <code>{`down`}</code> if no response. Refer to <a href="https://github.com/vespa-engine/vespa/blob/master/container-disc/src/main/java/com/yahoo/container/jdisc/state/StateMonitor.java" aria-label="StateMonitor">StateMonitor</a> for implementation.</td> + </tr> + <tr> + <td>message</td> + <td>status</td> + <td>String</td> + <td>Message is optional - it is normally empty if the service is up, while it is set to a textual reason for why it is unavailable, if so.</td> + </tr> + <tr> + <td>metrics</td> + <td></td> + <td>Object</td> + <td>Snapshot of metric values.</td> + </tr> + <tr> + <td>snapshot</td> + <td>metrics</td> + <td>Object</td> + <td>Time period for metrics snapshot.</td> + </tr> + <tr> + <td>from</td> + <td>snapshot</td> + <td>Number</td> + <td>Epoch in seconds, with microseconds fraction.</td> + </tr> + <tr> + <td>to</td> + <td>snapshot</td> + <td>Number</td> + <td>Epoch in seconds, with microseconds fraction.</td> + </tr> + <tr> + <td>values</td> + <td>metrics</td> + <td>Array</td> + <td>Array of metric objects.</td> + </tr> + <tr> + <td>name</td> + <td>values</td> + <td>String</td> + <td>Metric name.</td> + </tr> + <tr> + <td>description</td> + <td>values</td> + <td>String</td> + <td>Textual description of the metric.</td> + </tr> + <tr> + <td>dimensions</td> + <td>values</td> + <td>Object</td> + <td>Set of dimension name/value pairs.</td> + </tr> + <tr> + <td>values</td> + <td>values</td> + <td>Object</td> + <td>Set of metric values.</td> + </tr> + <tr> + <td>average</td> + <td>values</td> + <td>Number</td> + <td>Average metric value, typically *sum* divided by *count* .</td> + </tr> + <tr> + <td>sum</td> + <td>values</td> + <td>Number</td> + <td>Sum of metric values in snapshot.</td> + </tr> + <tr> + <td>count</td> + <td>values</td> + <td>Number</td> + <td>Number of times metric has been set. For instance in a metric counting number of operations done, it will give the number of operations added for that snapshot period. For a value metric, for instance latency of operations, the count will give how many times latencies have been added to the metric.</td> + </tr> + <tr> + <td>last</td> + <td>values</td> + <td>Number</td> + <td>Last metric value.</td> + </tr> + <tr> + <td>max</td> + <td>values</td> + <td>Number</td> + <td>Max metric value in snapshot.</td> + </tr> + <tr> + <td>min</td> + <td>values</td> + <td>Number</td> + <td>Min metric value in snapshot.</td> + </tr> + <tr> + <td>rate</td> + <td>values</td> + <td>Number</td> + <td>Metric rate: *count* divided by *snapshot interval* .</td> + </tr> + </tbody> +</table> + diff --git a/en/reference/applications/application-packages.html b/en/reference/applications/application-packages.html deleted file mode 100644 index a31e417d66..0000000000 --- a/en/reference/applications/application-packages.html +++ /dev/null @@ -1,296 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Application package reference" -redirect_from: -- en/reference/application-packages-reference.html ---- - -<p> -This is the <a href="../../basics/applications.html">application package</a> reference. -An application package is the deployment unit in Vespa. -To deploy an application, create an application package -and <a href="../../clients/vespa-cli.html#deployment">vespa deploy</a> or -use the <a href="../api/deploy-v2.html">deploy API</a>. -The application package is a directory of files and subdirectories: -</p> -<table class="table"> - <thead> - <tr> - <th>Directory/file</th><th>Required</th><th>Description</th> - </tr> - </thead> - <tbody> -<tr> - <td><a href="services/services.html">services.xml</a></td> - <td>Yes</td> - <td>Describes which services to run where, and their main configuration.</td> -</tr> -<tr> - <td><a href="hosts.html">hosts.xml</a></td> - <td>No</td> - <td> - <p> - Vespa Cloud: Not used. - See node counts in <a href="services/services.html">services.xml</a>. - </p> - <p> - Self-managed: The mapping from logical nodes to actual hosts. - </p> - </td> -</tr> -<tr> - <td><a href="deployment.html">deployment.xml</a></td> - <td>Yes, for Vespa Cloud</td> - <td> - <p> - Specifies which environments and regions the application is deployed to during automated application deployment, - as which application instances. - </p> - <p> - This file also specifies other deployment-related configurations like <a href="../../operations/enclave/enclave">cloud accounts</a> and - <a href="../../operations/private-endpoints.html">private endpoints</a>. - </p> - <p> - The file is required when deploying to the <a href="../../operations/environments.html#prod">prod environment</a> - - it is ignored (with some exceptions) when deploying to the <em>dev</em> environment. - </p> - </td> -</tr> -<tr> - <td style="white-space: nowrap"><a href="validation-overrides.html">validation-overrides.xml</a></td> - <td>No</td> - <td>Override, allowing this package to deploy even if it fails validation.</td> -</tr> -<tr> - <td><a href="../../applications/vespaignore.html">.vespaignore</a></td> - <td>No</td> - <td>Contains a list of path patterns that should be excluded from the - <code>application.zip</code> deployed to Vespa.</td> -</tr> -<tr> - <td><a href="../ranking/model-files.html">models</a>/</td> - <td>No</td> - <td>Machine-learned models in the application package. - Refer to <a href="../../ranking/stateless-model-evaluation.html">stateless model evaluation</a>, - <a href="../../ranking/tensorflow">Tensorflow</a>, <a href="../../ranking/onnx">Onnx</a>, - <a href="../../ranking/xgboost">XGBoost</a>, and <a href="../../ranking/lightgbm">LightGBM</a>. - </td> -</tr> -<tr> - <td><a href="../../basics/schemas.html">schemas</a>/</td> - <td>No</td> - <td>Contains the *.sd files describing the document types of the application and how they should be queried and processed.</td> -</tr> -<tr> - <td><a href="../schemas/schemas.html#rank-profile">schemas/[schema]</a>/</td> - <td>No</td> - <td>Contains *.profile files defining <a href="../../basics/ranking.html#rank-profiles">rank profiles</a>. - This is an alternative to defining rank profiles inside the schema.</td> -</tr> -<tr> - <td><a href="../../security/guide">security/clients.pem</a></td> - <td>Yes, for Vespa Cloud</td> - <td> - PEM encoded X.509 certificates for data plane access. - See the <a href="../../security/guide">security guide</a> for how to generate and use. - </td> -</tr> -<tr> - <td><a href="../../applications/components.html">components</a>/</td> - <td>No</td> - <td>Contains *.jar files containing searcher(s) for the JDisc Container.</td> -</tr> -<tr> - <td><a href="../querying/semantic-rules.html">rules</a>/</td> - <td>No</td> - <td>Contains *.sr files containing rule bases for semantic recognition and translation of the query</td> -</tr> -<tr> - <td><a href="../querying/query-profiles.html">search/query-profiles</a>/</td> - <td>No</td> - <td>Contains *.xml files containing a named set of search request parameters with values</td> -</tr> -<tr> - <td><a href="../../ranking/tensor-user-guide.html#constant-tensors">constants</a>/</td> - <td>No</td> - <td>Constant tensors</td> -</tr> -<tr> - <td><a href="testing.html">tests</a>/</td> - <td>No</td> - <td>Test files for automated tests</td> -</tr> -<tr> - <td>ext/</td> - <td>No</td> - <td>Files that are guaranteed to be ignored by Vespa: They - are excluded when processing the application package and - cannot be referenced from any other element in it.</td> -</tr> - </tbody> -</table> -<p> -Additional files and directories can be placed anywhere in the application package. -These will be not be processed explicitly by Vespa when deploying the application package -(i.e. they will only be considered if they are referred to from within the application package), -but there is no guarantee to how these might be processed in a future release. -To extend the application package in a way that is guaranteed -to be ignored by Vespa in all future releases, use the <em>ext/</em> directory. -</p> - - - -<h2 id="deploy">Deploy</h2> -<table class="table"> - <thead> - <tr> - <th>Command</th> - <th>Description</th> - </tr> - </thead> - <tbody> - <tr> - <th>upload</th> - <td> - Uploads an application package to the config server. - Normally not used, as <em>prepare</em> includes <em>upload</em> - </td> - </tr><tr> - <th>prepare</th> - <td> - <ol> - <li> - Verifies that a configuration server is up and running - </li><li> - Uploads the application to the configuration server, which stores it in - <em>$VESPA_HOME/var/db/vespa/config_server/serverdb/tenants/default/sessions/[sessionid]</em>. - <em>[sessionid]</em> increases for each <em>prepare</em>-call. - The config server also stores the application in a - <a href="/en/operations/self-managed/configuration-server.html">ZooKeeper</a> - instance at <em>/config/v2/tenants/default/sessions/[sessionid]</em> - - this distributes the application to all config servers <!-- ToDo: CHECK files ... --> - </li><li> - Creates metadata about the deployed the applications package (which user deployed it, - which directory was it deployed from and at what time was it deployed) and stores it in - <em>...sessions/[sessionid]/.applicationMetaData</em> - </li><li> - Verifies that the application package contains the required files and performs a consistency check - </li><li> - Validates the xml config files using the - <a href="https://github.com/vespa-engine/vespa/tree/master/config-model/src/main/resources/schema">schema</a>, - found in <em>$VESPA_HOME/share/vespa/schema</em> - </li><li> - Checks if there are config changes between the active application and this prepared application - that require actions like restart or re-feed (like changes to - <a href="../../basics/schemas.html">schemas</a>). - These actions are returned as part of the prepare step in the - <a href="../api/deploy-v2.html#prepare-session">deployment API</a>. - This prevents breaking changes to production - also read about - <a href="validation-overrides.html">validation overrides</a> - </li><li> - Distributes constant tensors and bundles with <a href="../../applications/components.html">components</a> - to nodes using <a href="/en/applications/deployment.html#file-distribution">file distribution</a>. - Files are downloaded to <em>$VESPA_HOME/var/db/vespa/filedistribution</em>, - URL download starts downloading to <em>$VESPA_HOME/var/db/vespa/download</em> - </li> - </ol> - </td> - </tr><tr> - <th>activate</th> - <td> - <ol> - <li> - Waits for prepare to complete - <!-- ToDo write something about timeout, and how to force etc. --> - </li><li> - Activates new configuration version - </li><li> - Signals to containers to load new bundles - - read more in <a href="../../applications/components.html">container components</a> - <!-- ToDo should add something on restart-on-deploy --> - </li> - </ol> - </td> - </tr><tr> - <th>fetch</th> - <td> - Use <em>fetch</em> to download the active application package - </td> - </tr> - </tbody> -</table> -<p> - An application package can be zipped for deployment: -</p> -<pre> -$ zip -r ../app.zip . -</pre> -<p> - Use any name for the zip file - then refer to the file instead of the path in <a href="../../clients/vespa-cli.html#deployment">deploy</a> commands. -</p> -{% include important.html content='Using <code>tar</code> / <code>gzip</code> is not supported. -<a href="https://github.com/vespa-engine/vespa/issues/17837">Details</a>.'%} -<!-- ToDo: reconcile this with reference/deploy-v2.html - that says "Set Content-Type HTTP header to application/x-gzip or application/zip" --> - - - -<h2 id="preprocess-directives">Preprocess directives</h2> -<p> -Use preprocess directives to: -</p> -<ul> - <li><em>preprocess:properties</em>: - define properties that one can refer to everywhere in <em>services.xml</em></li> - <li><em>preprocess:include</em>: - split <em>services.xml</em> in smaller chunks</li> -</ul> -<p> - Below, <em>${container.port}</em> is replaced by <em>4099</em>. - The contents of <em>content.xml</em> is placed at the <em>include</em> point. - This is applied recursively, one can use preprocess directives in included files, - as long as namespaces are defined in the top level file: -</p> -<pre> -<services version="1.0" <span class="pre-hilite">xmlns:preprocess="properties"</span>> -<span class="pre-hilite"> <preprocess:properties> - <container.port>4099</container.port> - </preprocess:properties></span> - <container version="1.0"> - <http> - <server id="container" port="${container.port}" /> - </http> - <search /> - </container> -<span class="pre-hilite"> <preprocess:include file="content.xml" /></span> -</services> -</pre> -<p>Sample <em>content.xml</em>:</p> -<pre> -<content version="1.0" > - <redundancy>1</redundancy> - <documents> - <document type="music.sd" mode="index" /> - </documents> - <nodes> - <node hostalias="node0"/> - <node hostalias="node1"/> - <node hostalias="node2"/> - </nodes> -</content> -</pre> - -<h2 id="versioning-application-packages">Versioning application packages</h2> -<p> -An application can be given a user-defined version, available at -<a href="../../applications/components.html#monitoring-the-active-application">/ApplicationStatus</a>. -Configure the version in <a href="../applications/services/services.html">services.xml</a> (at top level): -</p> -<pre> -<services> - <config name="container.handler.observability.application-userdata"> - <version>42</version> - </config> - ... -</services> -</pre> diff --git a/en/reference/applications/application-packages.mdx b/en/reference/applications/application-packages.mdx new file mode 100644 index 0000000000..088fc9960e --- /dev/null +++ b/en/reference/applications/application-packages.mdx @@ -0,0 +1,191 @@ +--- +title: "Application package reference" +sidebarTitle: "Application packages" +--- + +This is the [application package](/en/basics/applications) reference. An application package is the deployment unit in Vespa. To deploy an application, create an application package and [vespa deploy](/en/clients/vespa-cli#deployment) or use the [deploy API](/en/reference/api/deploy-v2). The application package is a directory of files and subdirectories: + +<table className="vespa-table"> + <thead> + <tr> + <th>Directory/file</th> + <th>Required</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td><a href="/en/reference/applications/services/services" aria-label="services.xml">services.xml</a></td> + <td>Yes</td> + <td>Describes which services to run where, and their main configuration.</td> + </tr> + <tr> + <td><a href="/en/reference/applications/hosts" aria-label="hosts.xml">hosts.xml</a></td> + <td>No</td> + <td>Vespa Cloud: Not used. See node counts in <a href="/en/reference/applications/services/services" aria-label="services.xml">services.xml</a>. <br/><br/> Self-managed: The mapping from logical nodes to actual hosts.</td> + </tr> + <tr> + <td><a href="/en/reference/applications/deployment" aria-label="deployment.xml">deployment.xml</a></td> + <td>Yes, for Vespa Cloud</td> + <td>Specifies which environments and regions the application is deployed to during automated application deployment, as which application instances. <br/><br/> This file also specifies other deployment-related configurations like <a href="/en/operations/enclave/enclave" aria-label="cloud accounts">cloud accounts</a> and <a href="/en/operations/private-endpoints" aria-label="private endpoints">private endpoints</a>. <br/><br/> The file is required when deploying to the <a href="/en/operations/environments#prod" aria-label="prod environment">prod environment</a> - it is ignored (with some exceptions) when deploying to the *dev* environment.</td> + </tr> + <tr> + <td><a href="/en/reference/applications/validation-overrides" aria-label="validation-overrides.xml">validation-overrides.xml</a></td> + <td>No</td> + <td>Override, allowing this package to deploy even if it fails validation.</td> + </tr> + <tr> + <td><a href="/en/applications/vespaignore" aria-label=".vespaignore">.vespaignore</a></td> + <td>No</td> + <td>Contains a list of path patterns that should be excluded from the <code>{`application.zip`}</code> deployed to Vespa.</td> + </tr> + <tr> + <td><a href="/en/reference/ranking/model-files" aria-label="models">models</a>/</td> + <td>No</td> + <td>Machine-learned models in the application package. Refer to <a href="/en/ranking/stateless-model-evaluation" aria-label="stateless model evaluation">stateless model evaluation</a>, <a href="/en/ranking/tensorflow" aria-label="Tensorflow">Tensorflow</a>, <a href="/en/ranking/onnx" aria-label="Onnx">Onnx</a>, <a href="/en/ranking/xgboost" aria-label="XGBoost">XGBoost</a>, and <a href="/en/ranking/lightgbm" aria-label="LightGBM">LightGBM</a>.</td> + </tr> + <tr> + <td><a href="/en/basics/schemas" aria-label="schemas">schemas</a>/</td> + <td>No</td> + <td>Contains the *.sd files describing the document types of the application and how they should be queried and processed.</td> + </tr> + <tr> + <td><a href="/en/reference/schemas/schemas#rank-profile" aria-label="schemas/[schema]">schemas/[schema]</a>/</td> + <td>No</td> + <td>Contains *.profile files defining <a href="/en/basics/ranking#rank-profiles" aria-label="rank profiles">rank profiles</a>. This is an alternative to defining rank profiles inside the schema.</td> + </tr> + <tr> + <td><a href="/en/security/guide" aria-label="security/clients.pem">security/clients.pem</a></td> + <td>Yes, for Vespa Cloud</td> + <td>PEM encoded X.509 certificates for data plane access. See the <a href="/en/security/guide" aria-label="security guide">security guide</a> for how to generate and use.</td> + </tr> + <tr> + <td><a href="/en/applications/components" aria-label="components">components</a>/</td> + <td>No</td> + <td>Contains *.jar files containing searcher(s) for the JDisc Container.</td> + </tr> + <tr> + <td><a href="/en/reference/querying/semantic-rules" aria-label="rules">rules</a>/</td> + <td>No</td> + <td>Contains *.sr files containing rule bases for semantic recognition and translation of the query</td> + </tr> + <tr> + <td><a href="/en/reference/querying/query-profiles" aria-label="search/query-profiles">search/query-profiles</a>/</td> + <td>No</td> + <td>Contains *.xml files containing a named set of search request parameters with values</td> + </tr> + <tr> + <td><a href="/en/ranking/tensor-user-guide#constant-tensors" aria-label="constants">constants</a>/</td> + <td>No</td> + <td>Constant tensors</td> + </tr> + <tr> + <td><a href="/en/reference/applications/testing" aria-label="tests">tests</a>/</td> + <td>No</td> + <td>Test files for automated tests</td> + </tr> + <tr> + <td>ext/</td> + <td>No</td> + <td>Files that are guaranteed to be ignored by Vespa: They are excluded when processing the application package and cannot be referenced from any other element in it.</td> + </tr> + </tbody> +</table> + +Additional files and directories can be placed anywhere in the application package. These will be not be processed explicitly by Vespa when deploying the application package (i.e. they will only be considered if they are referred to from within the application package), but there is no guarantee to how these might be processed in a future release. To extend the application package in a way that is guaranteed to be ignored by Vespa in all future releases, use the *ext/* directory. + +## Deploy + +<table className="vespa-table"> + <thead> + <tr> + <th>Command</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>**upload**</td> + <td>Uploads an application package to the config server. Normally not used, as *prepare* includes *upload*</td> + </tr> + <tr> + <td>**prepare**</td> + <td>1. Verifies that a configuration server is up and running <br/><br/>2. Uploads the application to the configuration server, which stores it in *<code>{`$VESPA_HOME/var/db/vespa/config_server/serverdb/tenants/default/sessions/[sessionid]`}</code>*. *[sessionid]* increases for each *prepare*-call. The config server also stores the application in a <a href="/en/operations/self-managed/configuration-server.html" aria-label="ZooKeeper">ZooKeeper</a> instance at */config/v2/tenants/default/sessions/[sessionid]* - this distributes the application to all config servers <br/><br/>3. Creates metadata about the deployed the applications package (which user deployed it, which directory was it deployed from and at what time was it deployed) and stores it in *...sessions/[sessionid]/.applicationMetaData*<br/><br/> 4. Verifies that the application package contains the required files and performs a consistency check <br/><br/>5. Validates the xml config files using the <a href="https://github.com/vespa-engine/vespa/tree/master/config-model/src/main/resources/schema" aria-label="schema">schema</a>, found in *<code>{`$VESPA_HOME/share/vespa/schema`}</code>* <br/><br/>6. Checks if there are config changes between the active application and this prepared application that require actions like restart or re-feed (like changes to <a href="/en/basics/schemas" aria-label="schemas">schemas</a>). These actions are returned as part of the prepare step in the <a href="/en/reference/api/deploy-v2#prepare-session" aria-label="deployment API">deployment API</a>.<br/> This prevents breaking changes to production - also read about <a href="/en/reference/applications/validation-overrides" aria-label="validation overrides">validation overrides</a><br/><br/> 7. Distributes constant tensors and bundles with <a href="/en/applications/components" aria-label="components">components</a> to nodes using <a href="/en/applications/deployment#file-distribution" aria-label="file distribution">file distribution</a>. Files are downloaded to *<code>{`$VESPA_HOME/var/db/vespa/filedistribution`}</code>*, URL download starts downloading to *<code>{`$VESPA_HOME/var/db/vespa/download`}</code>*</td> + </tr> + <tr> + <td>**activate**</td> + <td>1. Waits for prepare to complete <br/><br/>2. Activates new configuration version <br/><br/>3. Signals to containers to load new bundles - read more in <a href="/en/applications/components" aria-label="container components">container components</a></td> + </tr> + <tr> + <td>**fetch**</td> + <td>Use *fetch* to download the active application package</td> + </tr> + </tbody> +</table> + +An application package can be zipped for deployment: + +```bash +$ zip -r ../app.zip . +``` + +Use any name for the zip file - then refer to the file instead of the path in [deploy](/en/clients/vespa-cli#deployment) commands. + +<Warning> +**Important:** + +Using `tar` / `gzip` is not supported. [Details](https://github.com/vespa-engine/vespa/issues/17837). +</Warning> + +## Preprocess directives + +Use preprocess directives to: + +- *preprocess:properties*: define properties that one can refer to everywhere in *services.xml* +- *preprocess:include*: split *services.xml* in smaller chunks + +Below, *`${container.port}`* is replaced by *4099*. The contents of *content.xml* is placed at the *include* point. This is applied recursively, one can use preprocess directives in included files, as long as namespaces are defined in the top level file: + +```xml +<services version="1.0" xmlns:preprocess="properties"> + <preprocess:properties> + <container.port>4099</container.port> + </preprocess:properties> + <container version="1.0"> + <http> + <server id="container" port="${container.port}" /> + </http> + <search /> + </container> + <preprocess:include file="content.xml" /> +</services> +``` + +Sample *content.xml*: + +```xml +<content version="1.0" > + <redundancy>1</redundancy> + <documents> + <document type="music.sd" mode="index" /> + </documents> + <nodes> + <node hostalias="node0"/> + <node hostalias="node1"/> + <node hostalias="node2"/> + </nodes> +</content> +``` + +## Versioning application packages + +An application can be given a user-defined version, available at [/ApplicationStatus](/en/applications/components#monitoring-the-active-application). Configure the version in [services.xml](/en/reference/applications/services/services) (at top level): + +```xml +<services> + <config name="container.handler.observability.application-userdata"> + <version>42</version> + </config> + ... +</services> +``` diff --git a/en/reference/applications/components.html b/en/reference/applications/components.html deleted file mode 100644 index 961b766967..0000000000 --- a/en/reference/applications/components.html +++ /dev/null @@ -1,449 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Component reference" -redirect_from: -- /en/jdisc/component-versioning.html -- /en/reference/component-reference.html ---- - -<p> - A component is any Java class whose lifetime is controlled by the container, - see the <a href="../../applications/developer-guide.html">Developer Guide</a> for an introduction. - Components are specified and configured in services.xml and can have other components, and config - (represented by generated "Config" classes) <a href="../../applications/dependency-injection.html">injected</a> - at construction time, and in turn be injected into other components. -</p> -<p> - Whenever a component or a resource your component depends on is changed by a redeployment, - your component is reconstructed. Once all changed components are reconstructed, new requests - are atomically switched to use the new set and the old ones are destructed. -</p> -<p> - If you have multiple constructors in your component, annotate the one to use for injection by - <code>@com.yahoo.component.annotation.Inject</code>. -</p> -<p> - Identifiable components must implement <code>com.yahoo.component.Component</code>, and components that need to - destruct resources at removal must subclass <code>com.yahoo.component.AbstractComponent</code> - and implement <code>deconstruct()</code>. -</p> -<p> - See the <a href="../../operations/metrics.html#example-qa">example</a> - for common questions about component uniqueness / lifetime. -</p> - - - -<h2 id="component-types">Component Types</h2> -<p> - Vespa defined various component types (superclasses) for common tasks: -</p> -<table class="table"> - <thead> - <tr> - <th>Component type</th> - <th>Description</th> - </tr> - </thead> - <tbody> - <tr> - <th>Request handler</th> - <td> - <p id="request-handlers"> - <a href="../../applications/request-handlers.html">Request handlers</a> allow applications - to implement arbitrary HTTP APIs. A request handler accepts a request and returns a response. - Custom request handlers are subclasses of - <a href="https://javadoc.io/doc/com.yahoo.vespa/container-disc/latest/com/yahoo/container/jdisc/ThreadedHttpRequestHandler.html"> - ThreadedHttpRequestHandler</a>. - </p> - </td> - </tr> - <tr> - <th>Processor</th> - <td> - <p id="processors"> - The <a href="../../applications/processing.html">processing framework</a> can be used to create general - composable synchronous request-response systems. Searchers and search chains are an instantiation - (through subclasses) of this general framework for a specific domain. - Processors are invoked synchronously and the response is a tree of arbitrary data elements. - Custom output formats can be defined by adding <a href="#renderers">renderers</a>. - </p> - </td> - </tr> - <tr> - <th>Renderer</th> - <td> - <p id="renderers"> - Renderers convert a Response (or query Result) into a serialized form sent over the network. - Renderers are subclasses of - <a href="https://github.com/vespa-engine/vespa/blob/master/container-disc/src/main/java/com/yahoo/processing/rendering/Renderer.java"> - com.yahoo.processing.rendering.Renderer</a>. - </p> - </td> - </tr> - <tr> - <th>Searcher</th> - <td> - <p id="searchers"> - Searchers processes Queries and their Results. - Since they are synchronous, they can issue multiple queries serially or in parallel to - e.g. implement federation or decorate queries with information fetched from a content cluster. - Searchers are composed into <i>search chains</i> defined in services.xml. - A query request selects a particular search chain which implements the logic of that query. - <a href="../../applications/searchers.html">Read more</a>. - </p> - </td> - </tr> - <tr> - <th>Document processor</th> - <td> - <p id="document-processors"> - Document processors processes incoming document operations. - Similar to Searchers and Processors they can be composed in chains, - but document processors are asynchronous. - <a href="../../applications/document-processors.html">Read more</a>. - </p> - </td> - </tr> - <tr> - <th>Binding</th> - <td> - <p id="binding"> - A binding matches a request URI - to the correct <a href="#filter">filter chain</a> or <a href="#request-handlers">request handler</a>, - and route outgoing requests to the correct <a href="#client">client</a>. - For instance, the binding <em>http://*/*</em> would match any HTTP request, - while <em>http://*/processing</em> would only match that specific path. - If several bindings match, the most specific one is chosen. - </p> - <table class="table"> - <tr> - <th style="white-space:nowrap">Server binding</th> - <td> - <p id="server-binding"> - A server binding is a rule for matching incoming requests to the correct request handler, - basically the JDisc building block for implementing RESTful APIs. - </p> - </td> - </tr> - <tr> - <th>Client binding</th> - <td> - <p id="client-binding"> - A client binding is a pattern which is used to match requests originating inside the container, - e.g. when doing federation, to a client provider. - That is, it is a rule which determines what code should handle a given outgoing request. - </p> - </td> - </tr> - </table> - </td> - </tr> - <tr> - <th>Filter</th> - <td> - <p id="filter"> - A filter is a lightweight request checker. - It may set some specific request property, - or it may do security checking and simply block requests missing some mandatory property or header. - </p> - </td> - </tr> - <tr> - <th>Client</th> - <td> - <p id="client"> - Clients, or client providers, are implementations of clients for different protocols, - or special rules for given protocols. - When a JDisc application acts as a client, e.g. fetches a web page from another host, - it is a client provider that handles the transaction. - Bindings are used, as with request handlers and filters, to choose the correct client, - matching protocol, server, etc., and then hands off the request to the client provider. - There is no problem in using arbitrary other types of clients - for external services in processors and request handlers. - </p> - </td> - </tr> - </tbody> -</table> - - - -<h2 id="component-configurations">Component configurations</h2> -<p> - This illustrates a typical component configuration set up by the Vespa container: - <img src="/assets/img/container-components.svg" alt="Vespa container component configuration" /> -</p> -<p> - The network layer associates a Request with a <em>response handler</em> - and routes it to the correct type of <a href="#request-handlers">request handler</a> - (typically based on URI binding patterns). -</p> -<p> - If an application needs lightweight request-response processing - using decomposition by a series of chained logical units, - the <a href="../../applications/processing.html">processing framework</a> - is the correct family of components to use. - The request will be routed from ProcessingHandler through one or more chains of - <a href="#processors">Processor</a> instances. - The exact format of the output is customizable using a <a href="#renderers">Renderer</a>. -</p> -<p> - If doing queries, SearchHandler will create a Query object, - route that to the pertinent chain of <a href="#searchers">Searcher</a> instances, - and associate the returned Result with the correct <a href="#renderers">Renderer</a> instance - for optional customization of the output format. -</p> -<p> - The DocumentProcessingHandler is usually invoked from messagebus, - and used for feeding documents into an index or storage. - The incoming data is used to build a Document object, - and this is then feed through a chain of <a href="#document-processors">DocumentProcessor</a> instances. -</p> -<p> - If building an application with custom HTTP APIs, for instance arbitrary REST APIs, - the easiest way is building a custom <a href="#request-handlers">RequestHandler</a>. - This gets the Request, which is basically a set of key-value pairs, - and returns a stream of arbitrary data back to the network. -</p> - - - -<h2 id="injectable-components">Injectable Components</h2> -<p> - These components are available from Vespa for <a href="../../applications/dependency-injection.html">injection</a> - into applications in various contexts: -</p> -<table class="table"> - <thead> - <tr> - <th>Component</th> - <th>Description</th> - </tr> - </thead> - <tbody> - <tr> - <th colspan="2" style="background-color: lightgrey;">Always available</th> - </tr> - <tr> - <th><a href="https://github.com/vespa-engine/vespa/blob/master/container-disc/src/main/java/com/yahoo/container/jdisc/athenz/AthenzIdentityProvider.java"> - AthenzIdentityProvider</a></th> - <td> - Provides the application's Athenz-identity and gives access to identity/role certificate and tokens. - </td> - </tr> - <tr> - <th><a href="https://github.com/vespa-engine/vespa/blob/master/model-integration/src/main/java/ai/vespa/embedding/BertBaseEmbedder.java"> - BertBaseEmbedder</a></th> - <td> - A BERT-Base compatible embedder, - see <a href="../../rag/embedding.html#bert-embedder">BertBase embedder</a>. - </td> - </tr> - <tr> - <th><a href="https://github.com/vespa-engine/vespa/blob/master/config-lib/src/main/java/com/yahoo/config/ConfigInstance.java"> - ConfigInstance</a></th> - <td> - Configuration is injected into components as <code>ConfigInstance</code> components - - see <a href="../../applications/configuring-components.html">configuring components</a>. - </td> - </tr> - <tr> - <th><a href="https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/Executor.html">Executor</a></th> - <td>Default threadpool for processing requests in threaded request handler</td> - </tr> - <tr> - <th><a href="https://github.com/vespa-engine/vespa/blob/master/linguistics/src/main/java/com/yahoo/language/Linguistics.java"> - Linguistics</a></th> - <td> - Inject a Linguistics component like - <a href="https://github.com/vespa-engine/vespa/blob/master/linguistics/src/main/java/com/yahoo/language/simple/SimpleLinguistics.java"> - SimpleLinguistics</a> or provide a custom implementation - - see <a href="../../linguistics/linguistics.html">linguistics</a>. - </td> - </tr> - <tr> - <th><a href="https://github.com/vespa-engine/vespa/blob/master/jdisc_core/src/main/java/com/yahoo/jdisc/Metric.java"> - Metric</a></th> - <td>Jdisc core interface for metrics. Required by all subclasses of ThreadedRequestHandler.</td> - </tr> - <tr> - <th><a href="https://github.com/vespa-engine/vespa/blob/master/container-disc/src/main/java/com/yahoo/metrics/simple/MetricReceiver.java"> - MetricReceiver</a></th> - <td> - Use to emit metrics from a component. - Find an example in the <a href="../../operations/metrics.html#metrics-from-custom-components">metrics</a> guide. - </td> - </tr> - <tr> - <th><a href="https://github.com/vespa-engine/vespa/blob/master/model-evaluation/src/main/java/ai/vespa/models/evaluation/ModelsEvaluator.java"> - ModelsEvaluator</a></th> - <td> - Evaluates machine-learned models added to Vespa applications and available as config form. - </td> - </tr> - <tr> - <th><a href="https://github.com/vespa-engine/vespa/blob/master/linguistics-components/src/main/java/com/yahoo/language/sentencepiece/SentencePieceEmbedder.java"> - SentencePieceEmbedder</a></th> - <td> - A native Java implementation of SentencePiece, - see <a href="../rag/embedding.html#sentencepiece-embedder">SentencePiece embedder</a>. - </td> - </tr> - <tr> - <th><a href="https://github.com/vespa-engine/vespa/blob/master/zkfacade/src/main/java/com/yahoo/vespa/curator/api/VespaCurator.java"> - VespaCurator</a></th> - <td> - <p> - A client for ZooKeeper. For use in container clusters that have - ZooKeeper enabled. See <a href="../../applications/using-zookeeper">using ZooKeeper</a>. - </p> - </td> - </tr> - <tr> - <th><a href="https://github.com/vespa-engine/vespa/blob/master/container-disc/src/main/java/com/yahoo/container/handler/VipStatus.java"> - VipStatus</a></th> - <td>Use this to gain control over the service status (up/down) to be emitted from this container.</td> - </tr> - <tr> - <th><a href="https://github.com/vespa-engine/vespa/blob/master/linguistics-components/src/main/java/com/yahoo/language/wordpiece/WordPieceEmbedder.java"> - WordPieceEmbedder</a></th> - <td> - An implementation of the WordPiece embedder, usually used with BERT models. - Refer to <a href="../rag/embedding.html#wordpiece-embedder">WordPiece embedder</a>. - </td> - </tr> - <tr> - <th><a href="https://github.com/vespa-engine/vespa/blob/master/hosted-zone-api/src/main/java/ai/vespa/cloud/SystemInfo.java"> - SystemInfo</a></th> - <td> - Vespa Cloud: Provides information about the environment the component is running in. - <a href="/en/applications/components.html#the-systeminfo-injectable-component">Read more</a>. - </td> - </tr> - <tr> - <th colspan="2" style="background-color: lightgrey;">Available in containers having <code>search</code></th> - </tr> - <tr> - <th><a href="https://github.com/vespa-engine/vespa/blob/master/documentapi/src/main/java/com/yahoo/documentapi/DocumentAccess.java"> - DocumentAccess</a></th> - <td>To use the <a href="../../writing/document-api-guide.html">Document API</a>. - </tr> - <tr> - <th><a href="https://github.com/vespa-engine/vespa/blob/master/container-search/src/main/java/com/yahoo/search/searchchain/ExecutionFactory.java"> - ExecutionFactory</a></th> - <td> - To execute new queries from code. - <a href="../../applications/web-services.html#queries">Read more</a>. - </td> - </tr> - <tr> - <th><a href="https://github.com/vespa-engine/vespa/blob/master/model-evaluation/src/main/java/ai/vespa/models/evaluation/Model.java"> - Map<String, Model></a></th> - <td> - Use to inject a set of Models, - see <a href="../../ranking/stateless-model-evaluation.html">Stateless Model Evaluation</a>. - </td> - </tr> - <tr> - <th colspan="2" style="background-color: lightgrey;">Available in containers having <code>document-api</code> or <code>document-processing</code></th> - </tr> - <tr> - <th><a href="https://github.com/vespa-engine/vespa/blob/master/documentapi/src/main/java/com/yahoo/documentapi/DocumentAccess.java"> - DocumentAccess</a></th> - <td>To use the <a href="../../writing/document-api-guide.html">Document API</a>. - </tr> - </tbody> -</table> - - - -<h2 id="component-versioning">Component Versioning</h2> -<p> - Components as well as many other artifacts in the container can be versioned. - This document explains the format and semantics of these versions and how they are referred. -</p> - - -<h3 id="format">Format</h3> -<p> - Versions are on the form: -</p> -<pre> -version ::= major [ "." minor [ "." micro [ "." qualifier ]]] -</pre> -<p> - Where <code>major</code>, <code>minor</code>, and <code>micro</code> are integers - and <code>qualifier</code> is any string. -</p><p> - A version is appended to an id separated by a colon. - In cases where a file is created for each component version, - the colon is replaced by a dash in the file name. -</p> - - -<h3 id="ordering">Ordering</h3> -<p> - Versions are ordered first by major, then minor, then micro and then - by doing a lexical ordering on the qualifier. - This means that <code>a:1 < a:1.0 < a:1.0.0 < a:1.1 < a:1.1.0 < a:2</code> -</p> - - -<h3 id="referencing-a-versioned-component">Referencing a versioned Component</h3> -<p> - Whenever component is referenced by id (in code or configuration), - a fully or partially specified version may be included in the reference - by using the form <code>id:versionSpecification</code>. - Such references are resolved using the following rules: -</p> -<ul> - <li>An id without any version specification resolves to the highest - version not having a qualifier.</li> - <li>A partially or full version specification resolves to the highest - version not having a qualifier which matches the specification.</li> - <li>Versions with qualifiers are matched only by exact match.</li> -</ul> -<p> - Example: Given a component with id <code>a</code> having these versions: <code>[1.1, 1.2, 1.2, 1.3.test, 2.0]</code> -</p> -<ul> - <li>The reference <code>a</code> will resolve to <code>a:2.0</code></li> - <li>The reference <code>a:1</code> will resolve to <code>a:1.2</code></li> - <li>The only way to resolve to the "test" qualified version - is by using the exact reference <code>a:1.3.test</code></li> - <li>These references will not resolve: <code>a:1.3</code>, <code>a:3</code>, <code>1.2.3</code></li> -</ul> - - -<h3 id="merging-specifications-for-chained-components">Merging specifications for chained Components</h3> -<p> - In some cases, there is a need for merging multiple references into one. - An example is inheritance of chains of version references, - where multiple inherited chains may reference the same component. -</p><p> - Two version references are said to be <em>compatible</em> if one is a prefix of the other. - In this case the most specific version is used. - If they are not compatible they are <em>conflicting</em>. Example: -</p> -<pre> -<search> - <searcher id="Searcher:2.3" class="com.yahoo.search.example.Searcher" bundle="the name in <artifactId> in your pom.xml" /> - <searcher id="Searcher:2.4" class="com.yahoo.search.example.Searcher" bundle="the name in <artifactId> in your pom.xml" /> - <chain id="parenta"> - <searcher id="Searcher:2"> bundle="the name in <artifactId> in your pom.xml" </searcher> - </chain> - <chain id="parentb"> - <searcher id="Searcher:2.3"> bundle="the name in <artifactId> in your pom.xml" </searcher> - </chain> - <chain id="parentc"> - <searcher id="Searcher:2.4"> bundle="the name in <artifactId> in your pom.xml" </searcher> - </chain> - - <!-- This chain will get Searcher:2.3 --> - <chain id="childa" inherits="parenta parentb" /> - - <!-- Error, as Searcher:2.3 and Searcher:2.4 are conflicting --> - <chain id="childb" inherits="parentb parentc" /> -</search> -</pre> diff --git a/en/reference/applications/components.mdx b/en/reference/applications/components.mdx new file mode 100644 index 0000000000..5710301f80 --- /dev/null +++ b/en/reference/applications/components.mdx @@ -0,0 +1,238 @@ +--- +title: "Component reference" +sidebarTitle: "Components" +--- + + +A component is any Java class whose lifetime is controlled by the container, see the [Developer Guide](/en/applications/developer-guide) for an introduction. Components are specified and configured in services.xml and can have other components, and config (represented by generated "Config" classes) [injected](/en/applications/dependency-injection) at construction time, and in turn be injected into other components. + +Whenever a component or a resource your component depends on is changed by a redeployment, your component is reconstructed. Once all changed components are reconstructed, new requests are atomically switched to use the new set and the old ones are destructed. + +If you have multiple constructors in your component, annotate the one to use for injection by `@com.yahoo.component.annotation.Inject`. + +Identifiable components must implement `com.yahoo.component.Component`, and components that need to destruct resources at removal must subclass `com.yahoo.component.AbstractComponent` and implement `deconstruct()`. + +See the [example](/en/operations/metrics#example-qa) for common questions about component uniqueness / lifetime. + +## Component Types + +Vespa defined various component types (superclasses) for common tasks: + +<table className="vespa-table"> + <thead> + <tr> + <th>Component type</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>**Request handler**</td> + <td><a href="/en/applications/request-handlers" aria-label="Request handlers">Request handlers</a> allow applications to implement arbitrary HTTP APIs. A request handler accepts a request and returns a response. Custom request handlers are subclasses of <a href="https://javadoc.io/doc/com.yahoo.vespa/container-disc/latest/com/yahoo/container/jdisc/ThreadedHttpRequestHandler.html" aria-label="ThreadedHttpRequestHandler">ThreadedHttpRequestHandler</a>.</td> + </tr> + <tr> + <td>**Processor**</td> + <td>The <a href="/en/applications/processing" aria-label="processing framework">processing framework</a> can be used to create general composable synchronous request-response systems. Searchers and search chains are an instantiation (through subclasses) of this general framework for a specific domain. Processors are invoked synchronously and the response is a tree of arbitrary data elements. Custom output formats can be defined by adding <a href="#renderers" aria-label="renderers">renderers</a>.</td> + </tr> + <tr> + <td>**Renderer**</td> + <td>Renderers convert a Response (or query Result) into a serialized form sent over the network. Renderers are subclasses of <a href="https://github.com/vespa-engine/vespa/blob/master/container-disc/src/main/java/com/yahoo/processing/rendering/Renderer.java" aria-label="com.yahoo.processing.rendering.Renderer">com.yahoo.processing.rendering.Renderer</a>.</td> + </tr> + <tr> + <td>**Searcher**</td> + <td>Searchers processes Queries and their Results. Since they are synchronous, they can issue multiple queries serially or in parallel to e.g. implement federation or decorate queries with information fetched from a content cluster. Searchers are composed into *search chains* defined in services.xml. A query request selects a particular search chain which implements the logic of that query. <a href="/en/applications/searchers" aria-label="Read more">Read more</a>.</td> + </tr> + <tr> + <td>**Document processor**</td> + <td>Document processors processes incoming document operations. Similar to Searchers and Processors they can be composed in chains, but document processors are asynchronous. <a href="/en/applications/document-processors" aria-label="Read more">Read more</a>.</td> + </tr> + <tr> + <td>**Binding**</td> + <td>A binding matches a request URI to the correct <a href="#filter" aria-label="filter chain">filter chain</a> or <a href="#request-handlers" aria-label="request handler">request handler</a>, and route outgoing requests to the correct <a href="#client" aria-label="client">client</a>. For instance, the binding *http://*/** would match any HTTP request, while *http://*/processing* would only match that specific path. If several bindings match, the most specific one is chosen.<br/><br/> **Server binding**<br/> A server binding is a rule for matching incoming requests to the correct request handler, basically the JDisc building block for implementing RESTful APIs. <br/><br/> **Client binding** <br/>A client binding is a pattern which is used to match requests originating inside the container, e.g. when doing federation, to a client provider. That is, it is a rule which determines what code should handle a given outgoing request. |</td> + </tr> + <tr> + <td>**Filter**</td> + <td>A filter is a lightweight request checker. It may set some specific request property, or it may do security checking and simply block requests missing some mandatory property or header.</td> + </tr> + <tr> + <td>**Client**</td> + <td>Clients, or client providers, are implementations of clients for different protocols, or special rules for given protocols. When a JDisc application acts as a client, e.g. fetches a web page from another host, it is a client provider that handles the transaction. Bindings are used, as with request handlers and filters, to choose the correct client, matching protocol, server, etc., and then hands off the request to the client provider. There is no problem in using arbitrary other types of clients for external services in processors and request handlers.</td> + </tr> + </tbody> +</table> + +## Component configurations + +This illustrates a typical component configuration set up by the Vespa container: + +<Frame> +![Vespa container component configuration](/assets/img/container-components.svg) +</Frame> + +The network layer associates a Request with a *response handler* and routes it to the correct type of [request handler](#request-handlers) (typically based on URI binding patterns). + +If an application needs lightweight request-response processing using decomposition by a series of chained logical units, the [processing framework](/en/applications/processing) is the correct family of components to use. The request will be routed from ProcessingHandler through one or more chains of [Processor](#processors) instances. The exact format of the output is customizable using a [Renderer](#renderers). + +If doing queries, SearchHandler will create a Query object, route that to the pertinent chain of [Searcher](#searchers) instances, and associate the returned Result with the correct [Renderer](#renderers) instance for optional customization of the output format. + +The DocumentProcessingHandler is usually invoked from messagebus, and used for feeding documents into an index or storage. The incoming data is used to build a Document object, and this is then feed through a chain of [DocumentProcessor](#document-processors) instances. + +If building an application with custom HTTP APIs, for instance arbitrary REST APIs, the easiest way is building a custom [RequestHandler](#request-handlers). This gets the Request, which is basically a set of key-value pairs, and returns a stream of arbitrary data back to the network. + +## Injectable Components + +These components are available from Vespa for [injection](/en/applications/dependency-injection) into applications in various contexts: + +<table className="vespa-table"> + <thead> + <tr> + <th>Component</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>**Always available**</td> + <td></td> + </tr> + <tr> + <td><a href="https://github.com/vespa-engine/vespa/blob/master/container-disc/src/main/java/com/yahoo/container/jdisc/athenz/AthenzIdentityProvider.java" aria-label="AthenzIdentityProvider">AthenzIdentityProvider</a></td> + <td>Provides the application's Athenz-identity and gives access to identity/role certificate and tokens.</td> + </tr> + <tr> + <td><a href="https://github.com/vespa-engine/vespa/blob/master/model-integration/src/main/java/ai/vespa/embedding/BertBaseEmbedder.java" aria-label="BertBaseEmbedder">BertBaseEmbedder</a></td> + <td>A BERT-Base compatible embedder, see <a href="/en/rag/embedding#bert-embedder" aria-label="BertBase embedder">BertBase embedder</a>.</td> + </tr> + <tr> + <td><a href="https://github.com/vespa-engine/vespa/blob/master/config-lib/src/main/java/com/yahoo/config/ConfigInstance.java" aria-label="ConfigInstance">ConfigInstance</a></td> + <td>Configuration is injected into components as <code>{`ConfigInstance`}</code> components - see <a href="/en/applications/configuring-components" aria-label="configuring components">configuring components</a>.</td> + </tr> + <tr> + <td><a href="https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/Executor.html" aria-label="Executor">Executor</a></td> + <td>Default threadpool for processing requests in threaded request handler</td> + </tr> + <tr> + <td><a href="https://github.com/vespa-engine/vespa/blob/master/linguistics/src/main/java/com/yahoo/language/Linguistics.java" aria-label="Linguistics">Linguistics</a></td> + <td>Inject a Linguistics component like <a href="https://github.com/vespa-engine/vespa/blob/master/linguistics/src/main/java/com/yahoo/language/simple/SimpleLinguistics.java" aria-label="SimpleLinguistics">SimpleLinguistics</a> or provide a custom implementation - see <a href="/en/linguistics/linguistics" aria-label="linguistics">linguistics</a>.</td> + </tr> + <tr> + <td><a href="https://github.com/vespa-engine/vespa/blob/master/jdisc_core/src/main/java/com/yahoo/jdisc/Metric.java" aria-label="Metric">Metric</a></td> + <td>Jdisc core interface for metrics. Required by all subclasses of ThreadedRequestHandler.</td> + </tr> + <tr> + <td><a href="https://github.com/vespa-engine/vespa/blob/master/container-disc/src/main/java/com/yahoo/metrics/simple/MetricReceiver.java" aria-label="MetricReceiver">MetricReceiver</a></td> + <td>Use to emit metrics from a component. Find an example in the <a href="/en/operations/metrics#metrics-from-custom-components" aria-label="metrics">metrics</a> guide.</td> + </tr> + <tr> + <td><a href="https://github.com/vespa-engine/vespa/blob/master/model-evaluation/src/main/java/ai/vespa/models/evaluation/ModelsEvaluator.java" aria-label="ModelsEvaluator">ModelsEvaluator</a></td> + <td>Evaluates machine-learned models added to Vespa applications and available as config form.</td> + </tr> + <tr> + <td><a href="https://github.com/vespa-engine/vespa/blob/master/linguistics-components/src/main/java/com/yahoo/language/sentencepiece/SentencePieceEmbedder.java" aria-label="SentencePieceEmbedder">SentencePieceEmbedder</a></td> + <td>A native Java implementation of SentencePiece, see <a href="/en/reference/rag/embedding#sentencepiece-embedder" aria-label="SentencePiece embedder">SentencePiece embedder</a>.</td> + </tr> + <tr> + <td><a href="https://github.com/vespa-engine/vespa/blob/master/zkfacade/src/main/java/com/yahoo/vespa/curator/api/VespaCurator.java" aria-label="VespaCurator">VespaCurator</a></td> + <td>A client for ZooKeeper. For use in container clusters that have ZooKeeper enabled. See <a href="/en/applications/using-zookeeper" aria-label="using ZooKeeper">using ZooKeeper</a>.</td> + </tr> + <tr> + <td><a href="https://github.com/vespa-engine/vespa/blob/master/container-disc/src/main/java/com/yahoo/container/handler/VipStatus.java" aria-label="VipStatus">VipStatus</a></td> + <td>Use this to gain control over the service status (up/down) to be emitted from this container.</td> + </tr> + <tr> + <td><a href="https://github.com/vespa-engine/vespa/blob/master/linguistics-components/src/main/java/com/yahoo/language/wordpiece/WordPieceEmbedder.java" aria-label="WordPieceEmbedder">WordPieceEmbedder</a></td> + <td>An implementation of the WordPiece embedder, usually used with BERT models. Refer to <a href="/en/reference/rag/embedding#wordpiece-embedder" aria-label="WordPiece embedder">WordPiece embedder</a>.</td> + </tr> + <tr> + <td><a href="https://github.com/vespa-engine/vespa/blob/master/hosted-zone-api/src/main/java/ai/vespa/cloud/SystemInfo.java" aria-label="SystemInfo">SystemInfo</a></td> + <td>Vespa Cloud: Provides information about the environment the component is running in. <a href="/en/applications/components#the-systeminfo-injectable-component" aria-label="Read more">Read more</a>.</td> + </tr> + <tr> + <td>**Available in containers having <code>{`search`}</code>**</td> + <td></td> + </tr> + <tr> + <td><a href="https://github.com/vespa-engine/vespa/blob/master/documentapi/src/main/java/com/yahoo/documentapi/DocumentAccess.java" aria-label="DocumentAccess">DocumentAccess</a></td> + <td>To use the <a href="/en/writing/document-api-guide" aria-label="Document API">Document API</a>.</td> + </tr> + <tr> + <td><a href="https://github.com/vespa-engine/vespa/blob/master/container-search/src/main/java/com/yahoo/search/searchchain/ExecutionFactory.java" aria-label="ExecutionFactory">ExecutionFactory</a></td> + <td>To execute new queries from code. <a href="/en/applications/web-services#queries" aria-label="Read more">Read more</a>.</td> + </tr> + <tr> + <td><a href="https://github.com/vespa-engine/vespa/blob/master/model-evaluation/src/main/java/ai/vespa/models/evaluation/Model.java" aria-label="Map`<String, Model>`">Map<code>{`<String, Model>`}</code></a></td> + <td>Use to inject a set of Models, see <a href="/en/ranking/stateless-model-evaluation" aria-label="Stateless Model Evaluation">Stateless Model Evaluation</a>.</td> + </tr> + <tr> + <td>**Available in containers having <code>{`document-api`}</code> or <code>{`document-processing`}</code>**</td> + <td></td> + </tr> + <tr> + <td><a href="https://github.com/vespa-engine/vespa/blob/master/documentapi/src/main/java/com/yahoo/documentapi/DocumentAccess.java" aria-label="DocumentAccess">DocumentAccess</a></td> + <td>To use the <a href="/en/writing/document-api-guide" aria-label="Document API">Document API</a>.</td> + </tr> + </tbody> +</table> + +## Component Versioning + +Components as well as many other artifacts in the container can be versioned. This document explains the format and semantics of these versions and how they are referred. + +### Format + +Versions are on the form: + +```js +version ::= major [ "." minor [ "." micro [ "." qualifier ]]] +``` + +Where `major`, `minor`, and `micro` are integers and `qualifier` is any string. + +A version is appended to an id separated by a colon. In cases where a file is created for each component version, the colon is replaced by a dash in the file name. + +### Ordering + +Versions are ordered first by major, then minor, then micro and then by doing a lexical ordering on the qualifier. This means that `a:1 < a:1.0 < a:1.0.0 < a:1.1 < a:1.1.0 < a:2` + +### Referencing a versioned Component + +Whenever component is referenced by id (in code or configuration), a fully or partially specified version may be included in the reference by using the form `id:versionSpecification`. Such references are resolved using the following rules: + +- An id without any version specification resolves to the highest version not having a qualifier. +- A partially or full version specification resolves to the highest version not having a qualifier which matches the specification. +- Versions with qualifiers are matched only by exact match. + +Example: Given a component with id `a` having these versions: `[1.1, 1.2, 1.2, 1.3.test, 2.0]` + +- The reference `a` will resolve to `a:2.0` +- The reference `a:1` will resolve to `a:1.2` +- The only way to resolve to the "test" qualified version is by using the exact reference `a:1.3.test` +- These references will not resolve: `a:1.3`, `a:3`, `1.2.3` + +### Merging specifications for chained Components + +In some cases, there is a need for merging multiple references into one. An example is inheritance of chains of version references, where multiple inherited chains may reference the same component. + +Two version references are said to be *compatible* if one is a prefix of the other. In this case the most specific version is used. If they are not compatible they are *conflicting*. Example: + +```xml +<search> + <searcher id="Searcher:2.3" class="com.yahoo.search.example.Searcher" bundle="the name in <artifactId> in your pom.xml" /> + <searcher id="Searcher:2.4" class="com.yahoo.search.example.Searcher" bundle="the name in <artifactId> in your pom.xml" /> + <chain id="parenta"> + <searcher id="Searcher:2"> bundle="the name in <artifactId> in your pom.xml" </searcher> + </chain> + <chain id="parentb"> + <searcher id="Searcher:2.3"> bundle="the name in <artifactId> in your pom.xml" </searcher> + </chain> + <chain id="parentc"> + <searcher id="Searcher:2.4"> bundle="the name in <artifactId> in your pom.xml" </searcher> + </chain> + + <!-- This chain will get Searcher:2.3 --> + <chain id="childa" inherits="parenta parentb" /> + + <!-- Error, as Searcher:2.3 and Searcher:2.4 are conflicting --> + <chain id="childb" inherits="parentb parentc" /> +</search> +``` diff --git a/en/reference/applications/config-files.html b/en/reference/applications/config-files.html deleted file mode 100644 index 50d66de533..0000000000 --- a/en/reference/applications/config-files.html +++ /dev/null @@ -1,294 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Custom Configuration File Reference" -redirect_from: -- /en/reference/config-files.html ---- - -<p> -This is the reference for config file definitions. -It is useful for developing applications that has -<a href="../../applications/configuring-components.html">configurable components</a> -for the <a href="../../applications/containers.html">Vespa Container</a>, -where configuration for individual components may be provided by defining -<a href="#generic-configuration-in-services-xml"><code><config></code></a> -elements within the component's scope in services.xml. -</p> - - -<h2 id="config-definition-files">Config definition files</h2> - -<p>Config definition files are part of the source code of your application and have a <em>.def</em> suffix. -Each file defines and documents the content and semantics of one configuration type. -Vespa's builtin <em>.def</em> files are found in -<code>$VESPA_HOME/share/vespa/configdefinitions/</code>. -</p> - - -<h3 id="package">Package</h3> - -<p>Package is a mandatory statement that is used to define the package for the java class generated to represent -the file. For <a href="../../applications/components.html">container component</a> developers, -it is recommended to use a separate package for each bundle that needs to export config classes, -to avoid conflicts between bundles that contain configurable components. -Package must be the first non-comment line, and can only contain lower-case characters and dots: -</p> -<pre> -package=com.mydomain.mypackage -</pre> - - - -<h3 id="parameter-names">Parameter names</h3> - -<p>Config definition files contain lines on the form:</p> -<pre> -parameterName type [default=value] [range=[min,max]] -</pre> -<p>camelCase in parameter names is recommended for readability.</p> - - -<h3 id="parameter-types">Parameter types</h3> - -<p>Supported types for variables in the <em>.def</em> file:</p> - -<table class="table"> -<tr> - <th>int</th> - <td><p id="int">32 bit signed integer value</p></td> -</tr> -<tr> - <th>long</th> - <td><p id="long">64 bit signed integer value</p></td> -</tr> -<tr> - <th>double</th> - <td><p id="double">64 bit IEEE float value</p></td> -</tr> -<tr> - <th>enum</th> - <td> - <p id="enum">Enumerated types. A set of strings representing the valid values for the parameter, e.g:</p> -<pre> - foo enum {BAR, BAZ, QUUX} default=BAR -</pre> - </td> -</tr> -<tr> - <th>bool</th> - <td><p id="bool">A boolean (true/false) value</p></td> -</tr> -<tr> - <th>string</th> - <td><p id="string">A String value. - Default values must be enclosed in quotation marks (" "), - and any internal quotation marks must be escaped by backslash. - Likewise, newlines must be escaped to <code>\n</code></p> - </td> -</tr> -<tr> - <th>path</th> - <td> - <p id="path">A path to a physical file or directory in the application package. - This makes it possible to access files from the application package in container components. - The path is relative to the root of the <a href="../../basics/applications.html">application package</a>. - A path parameter cannot have a default value, but may be optional (using the - <em>optional</em> keyword after the type). An optional path does not have to be - set, in which case it will be an empty value. - The content will be available as a <code>java.nio.file.Path</code> instance when the - component accessing this config is constructed, or an <code>Optional<Path></code> if the <em>optional</em> - keyword is used.</p> -</tr> -<tr> - <th>url</th> - <td> - <p id="url">Similar to <code>path</code>, an arbitrary URL of a file that - should be downloaded and made available to container components. - The file content will be available as a java.io.File instance when the - component accessing this config is constructed. - Note that if the file takes a long time to download, it will also - take a long time for the container to come up with the configuration referencing it. - See also the <a href="../../applications/configuring-components.html#adding-files-to-the-component-configuration"> - note about changing contents for such a url</a>. - </p> - </td> -</tr> - <tr> - <th>model</th> - <td> - <p id="model">A pointer to a machine-learned model. This can be a model-id, url or path, and multiple - of these can be specified as a single config value, where one is used depending on the deployment environment: - </p> - <ul> - <li>If a model-id is specified and the application is deployed on Vespa Cloud, the model-id is used. - <li>Otherwise, if a URL is specified, it is used. - <li>Otherwise, path is used.</li> - </ul> - <p> - You may also use remote URLs protected by bearer-token authentication by supplying the optional <code>secret-ref</code> attribute. - See <a href="../rag/embedding.html#private-model-hub">using private Huggingface models</a>. - </p> - On the receiving side, this config value is simply represented as a file path regardless of how it is resolved. - This makes it easy to refer to models in multiple ways such that the appropriate one is used depending on the context. - The special syntax for setting these config values is documented in - <a href="../../applications/configuring-components.html#adding-files-to-the-component-configuration">adding files to the configuration</a>. - </td> - </tr> -<tr> - <th>reference</th> - <td><p id="reference">A config id to another configuration (only for internal vespa usage)</p></td> -</tr> -</table> - - -<h3 id="structs">Structs</h3> -<p> -Structs are used to group a number of parameters that naturally belong together. -A struct is declared by adding a '.' between the struct name and each member's name: -</p> -<pre> -basicStruct.foo string -basicStruct.bar int -</pre> - - - -<h3 id="arrays">Arrays</h3> -<p> -Arrays are declared by appending square brackets to the parameter name. -Arrays can either contain simple values, or have children. -Children can be simple parameters and/or structs and/or other arrays. -Arbitrarily complex structures can be built to any depth. Examples: -<pre> -intArr[] int # Integer value array -row[].column[] int # Array of integer value arrays -complexArr[].foo string # Complex array that contains -complexArr[].bar double # … two simple parameters -complexArr[].coord.x int # … and a struct called 'coord' -complexArr[].coord.y int -complexArr[].coord.depths[] double # … that contains a double array -</pre> -<p> -Note that arrays cannot have default values, even for simple value arrays. -An array that has children cannot contain simple values, and vice versa. -In the example above, <code>intArr</code> and <code>row.column</code> could not have children, -while <code>row</code> and <code>complexArr</code> are not allowed to contain values. -</p> - - -<h3 id="maps">Maps</h3> -<p> -Maps are declared by appending curly brackets to the parameter name. -Arbitrarily complex structures are supported also here. Examples: -</p> -<pre> -myMap{} int -complexMap{}.nestedMap{}.id int -complexMap{}.nestedMap{}.name string -</pre> - - - -<h2 id="generic-configuration-in-services-xml">Generic configuration in services.xml</h2> -<p> -<code>services.xml</code>has four types of elements: -<table class="table"> -<tr> - <th>individual service elements</th> - <td>(e.g. <em>searcher</em>, <em>handler</em>, <em>searchnode</em>) - - creates a service, but has no child elements that create services</td> -</tr> -<tr> - <th>service group elements</th> - <td>(e.g. <em>content</em>, <em>container</em>, <em>document-processing</em> - - creates a group of services and can have all types of child elements</td> -</tr> -<tr> - <th>dedicated config elements</th> - <td>(e.g. <em>accesslog</em>) - - configures a service or a group of services - and can only have other dedicated config elements as children</td> -</tr> -<tr> - <th>generic config elements</th> - <td>always named <em>config</em></td> -</tr> -</table> - -<p>Generic config elements can be added to most elements that lead to one or -more services being created - i.e. service group elements and individual service elements. -The config is then applied to all services created by that element and all descendant elements.</p> - -<p>For example, by adding <em>config</em> for <em>container</em>, -the config will be applied to all container components in that cluster. -Config at a deeper level has priority, -so this config can be overridden for individual components -by setting the same config values in e.g. <em>handler</em> or <em>server</em> elements. -</p> - -<p>Given the following config definition, let's say its name is <code>type-examples.def</code>: -<pre> -package=com.mydomain - -stringVal string -myArray[].name string -myArray[].type enum {T1, T2, T3} default=T1 -myArray[].intArr[] int -myMap{} string -basicStruct.foo string -basicStruct.bar int default=0 range=[-100,100] -boolVal bool -myFile path -myUrl url -myOptionalPath path optional -</pre> -To set all the values for this config in <code>services.xml</code>, -add the following xml at the desired element (the name should be <em><package>.<config definition file name></em>): -<pre> -<config name="com.mydomain.type-examples"> - <stringVal>val</stringVal> - <myArray> - <item> - <name>elem_0</name> - <type>T2</type> - <intArr> - <item>0</item> - <item>1</item> - </intArr> - </item> - <item> - <name>elem_1</name> - <type>T3</type> - <intArr> - <item>0</item> - <item>1</item> - </intArr> - </item> - </myArray> - <myMap> - <item key="key1">val1</item> - <item key="key2">val2</item> - </myMap> - <basicStruct> - <foo>str</foo> - <bar>3</bar> - </basicStruct> - <boolVal>true</boolVal> - <myFile>components/file1.txt</myFile> - <myUrl>https://docs.vespa.ai/en/reference/query-api-reference.html</myUrl> -</config> -</pre> -<p> -Note that each '.' in the parameter's definition corresponds to a child element in the xml. -It is not necessary to set values that already have a default in the <em>.def</em> file, -if you want to keep the default value. -Hence, in the example above, <code>basicStruct.bar</code> and <code>myArray[].type</code> -could have been omitted in the xml without generating any errors when deploying the application. -</p> - - -<h3 id="configuring-arrays">Configuring arrays</h3> - -<p>Assigning values to <em>arrays</em> is done by using the <code><item></code> element. -This ensures that the given config values do not overwrite any existing array elements -from higher-level xml elements in services, or from Vespa itself.</p> diff --git a/en/reference/applications/config-files.mdx b/en/reference/applications/config-files.mdx new file mode 100644 index 0000000000..e34f3fbb8c --- /dev/null +++ b/en/reference/applications/config-files.mdx @@ -0,0 +1,207 @@ +--- +title: "Custom Configuration File Reference" +sidebarTitle: "Configuration files" +--- + + +This is the reference for config file definitions. It is useful for developing applications that has [configurable components](/en/applications/configuring-components) for the [Vespa Container](/en/applications/containers), where configuration for individual components may be provided by defining [`<config>`](#generic-configuration-in-services-xml) elements within the component's scope in services.xml. + +## Config definition files + +Config definition files are part of the source code of your application and have a *.def* suffix. Each file defines and documents the content and semantics of one configuration type. Vespa's builtin *.def* files are found in `$VESPA_HOME/share/vespa/configdefinitions/`. + +### Package + +Package is a mandatory statement that is used to define the package for the java class generated to represent the file. For [container component](/en/applications/components) developers, it is recommended to use a separate package for each bundle that needs to export config classes, to avoid conflicts between bundles that contain configurable components. Package must be the first non-comment line, and can only contain lower-case characters and dots: + +```bash +package=com.mydomain.mypackage +``` + +### Parameter names + +Config definition files contain lines on the form: + +```bash +parameterName type [default=value] [range=[min,max]] +``` + +camelCase in parameter names is recommended for readability. + +### Parameter types + +Supported types for variables in the *.def* file: + +<table className="vespa-table"> + <thead> + <tr> + <th>int</th> + <th>32 bit signed integer value</th> + </tr> + </thead> + <tbody> + <tr> + <td>long</td> + <td>64 bit signed integer value</td> + </tr> + <tr> + <td>double</td> + <td>64 bit IEEE float value</td> + </tr> + <tr> + <td>enum</td> + <td>Enumerated types. A set of strings representing the valid values for the parameter, e.g: <br/><br/><code>{`foo enum {BAR, BAZ, QUUX} default=BAR`}</code></td> + </tr> + <tr> + <td>bool</td> + <td>A boolean (true/false) value</td> + </tr> + <tr> + <td>string</td> + <td>A String value. Default values must be enclosed in quotation marks (" "), and any internal quotation marks must be escaped by backslash. Likewise, newlines must be escaped to <code>{`\\n`}</code></td> + </tr> + <tr> + <td>path</td> + <td>A path to a physical file or directory in the application package. This makes it possible to access files from the application package in container components. The path is relative to the root of the <a href="/en/basics/applications" aria-label="application package">application package</a>. A path parameter cannot have a default value, but may be optional (using the *optional* keyword after the type). An optional path does not have to be set, in which case it will be an empty value. The content will be available as a <code>{`java.nio.file.Path`}</code> instance when the component accessing this config is constructed, or an <code>{`Optional<Path>`}</code> if the *optional* keyword is used.</td> + </tr> + <tr> + <td>url</td> + <td>Similar to <code>{`path`}</code>, an arbitrary URL of a file that should be downloaded and made available to container components. The file content will be available as a java.io.File instance when the component accessing this config is constructed. Note that if the file takes a long time to download, it will also take a long time for the container to come up with the configuration referencing it. See also the <a href="/en/applications/configuring-components#adding-files-to-the-component-configuration" aria-label="note about changing contents for such a url">note about changing contents for such a url</a>.</td> + </tr> + <tr> + <td>model</td> + <td>A pointer to a machine-learned model. This can be a model-id, url or path, and multiple of these can be specified as a single config value, where one is used depending on the deployment environment: <br/><br/>• If a model-id is specified and the application is deployed on Vespa Cloud, the model-id is used. <br/>• Otherwise, if a URL is specified, it is used. <br/>• Otherwise, path is used. <br/><br/> You may also use remote URLs protected by bearer-token authentication by supplying the optional <code>{`secret-ref`}</code> attribute. See <a href="/en/reference/rag/embedding#private-model-hub" aria-label="using private Huggingface models">using private Huggingface models</a>. <br/><br/> On the receiving side, this config value is simply represented as a file path regardless of how it is resolved. This makes it easy to refer to models in multiple ways such that the appropriate one is used depending on the context. The special syntax for setting these config values is documented in <a href="/en/applications/configuring-components#adding-files-to-the-component-configuration" aria-label="adding files to the configuration">adding files to the configuration</a>.</td> + </tr> + <tr> + <td>reference</td> + <td>A config id to another configuration (only for internal vespa usage)</td> + </tr> + </tbody> +</table> + +### Structs + +Structs are used to group a number of parameters that naturally belong together. A struct is declared by adding a '.' between the struct name and each member's name: + +```bash +basicStruct.foo string +basicStruct.bar int +``` + +### Arrays + +Arrays are declared by appending square brackets to the parameter name. Arrays can either contain simple values, or have children. Children can be simple parameters and/or structs and/or other arrays. Arbitrarily complex structures can be built to any depth. Examples: + +```bash +intArr[] int # Integer value array +row[].column[] int # Array of integer value arrays +complexArr[].foo string # Complex array that contains +complexArr[].bar double # … two simple parameters +complexArr[].coord.x int # … and a struct called 'coord' +complexArr[].coord.y int +complexArr[].coord.depths[] double # … that contains a double array +``` + +Note that arrays cannot have default values, even for simple value arrays. An array that has children cannot contain simple values, and vice versa. In the example above, `intArr` and `row.column` could not have children, while `row` and `complexArr` are not allowed to contain values. + +### Maps + +Maps are declared by appending curly brackets to the parameter name. Arbitrarily complex structures are supported also here. Examples: + +```bash +myMap{} int +complexMap{}.nestedMap{}.id int +complexMap{}.nestedMap{}.name string +``` + +## Generic configuration in services.xml + +`services.xml`has four types of elements: + +<table className="vespa-table"> + <thead> + <tr> + <th>individual service elements</th> + <th>(e.g. *searcher*, *handler*, *searchnode*) - creates a service, but has no child elements that create services</th> + </tr> + </thead> + <tbody> + <tr> + <td>**service group elements**</td> + <td>(e.g. *content*, *container*, *document-processing* - creates a group of services and can have all types of child elements</td> + </tr> + <tr> + <td>**dedicated config elements**</td> + <td>(e.g. *accesslog*) - configures a service or a group of services and can only have other dedicated config elements as children</td> + </tr> + <tr> + <td>**generic config elements**</td> + <td>always named *config*</td> + </tr> + </tbody> +</table> + +Generic config elements can be added to most elements that lead to one or more services being created - i.e. service group elements and individual service elements. The config is then applied to all services created by that element and all descendant elements. + +For example, by adding *config* for *container*, the config will be applied to all container components in that cluster. Config at a deeper level has priority, so this config can be overridden for individual components by setting the same config values in e.g. *handler* or *server* elements. + +Given the following config definition, let's say its name is `type-examples.def`: + +```bash +package=com.mydomain + +stringVal string +myArray[].name string +myArray[].type enum {T1, T2, T3} default=T1 +myArray[].intArr[] int +myMap{} string +basicStruct.foo string +basicStruct.bar int default=0 range=[-100,100] +boolVal bool +myFile path +myUrl url +myOptionalPath path optional +``` + +To set all the values for this config in `services.xml`, add the following xml at the desired element (the name should be *`<package>.<config definition file name>`*): + +```xml expandable +<config name="com.mydomain.type-examples"> + <stringVal>val</stringVal> + <myArray> + <item> + <name>elem_0</name> + <type>T2</type> + <intArr> + <item>0</item> + <item>1</item> + </intArr> + </item> + <item> + <name>elem_1</name> + <type>T3</type> + <intArr> + <item>0</item> + <item>1</item> + </intArr> + </item> + </myArray> + <myMap> + <item key="key1">val1</item> + <item key="key2">val2</item> + </myMap> + <basicStruct> + <foo>str</foo> + <bar>3</bar> + </basicStruct> + <boolVal>true</boolVal> + <myFile>components/file1.txt</myFile> + <myUrl>/en/reference/query-api-reference.html</myUrl> +</config> +``` + +Note that each '.' in the parameter's definition corresponds to a child element in the xml. It is not necessary to set values that already have a default in the *.def* file, if you want to keep the default value. Hence, in the example above, `basicStruct.bar` and `myArray[].type` could have been omitted in the xml without generating any errors when deploying the application. + +### Configuring arrays + +Assigning values to *arrays* is done by using the `<item>` element. This ensures that the given config values do not overwrite any existing array elements from higher-level xml elements in services, or from Vespa itself. diff --git a/en/reference/applications/deployment.html b/en/reference/applications/deployment.html deleted file mode 100644 index 17754d8294..0000000000 --- a/en/reference/applications/deployment.html +++ /dev/null @@ -1,1294 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: deployment.xml reference -applies_to: cloud -redirect_from: -- /en/reference/deployment.html -- /en/reference/services/deployment.html ---- - -<p><em>deployment.xml</em> controls how an application is deployed.</p> - -<p> - <em>deployment.xml</em> is placed in the root of the <a href="../../basics/applications.html">application package</a> - and specifies which environments and regions the application is deployed to during - <a href="../../operations/automated-deployments.html">automated application deployment</a>, as which application instances. -</p> -<p> - Deployment progresses through the <code>test</code> and <code>staging</code> - environments to the <code>prod</code> environments listed in <em>deployment.xml</em>. -</p> -<p>Simple example:</p> -<pre>{% highlight xml %} -<deployment version="1.0"> - <prod> - <region name="aws-us-east-1c"/> - <region name="aws-us-west-2a"/> - </prod> -</deployment> -{% endhighlight %}</pre> -<p>More complex example:</p> -<pre>{% highlight xml %} -<deployment version="1.0"> - <instance id="beta"> - <prod> - <region name="aws-us-east-1c"/> - </prod> - </instance> - <instance id="default"> - <block-change revision="false" - days="mon,wed-fri" - hours="16-23" - time-zone="UTC" /> - <backup frequency="7d" granularity="cluster" /> - <prod> - <region name="aws-us-east-1c"/> - <delay hours="3" minutes="7" seconds="13" /> - <parallel> - <region name="aws-us-west-1c"/> - <steps> - <region name="aws-eu-west-1a"/> - <delay hours="3" /> - <test>aws-us-west-2a</test> - </steps> - </parallel> - </prod> - <endpoints> - <endpoint container-id="my-container-service"> - <region name="aws-us-east-1c"/> - </endpoint> - </endpoints> - </instance> - <endpoints> - <endpoint id="my-weighted-endpoint" - container-id="my-container-service" - region="aws-us-east-1c"> - <instance weight="1">beta</instance> - </endpoint> - </endpoints> -</deployment> -{% endhighlight %}</pre> -<p> - Some of the elements can be declared <em>either</em> under the <code><deployment></code> root, - <strong>or</strong>, if one or more <code><instance></code> tags are listed, under these. These - have a bold <strong>or</strong> when listing where they may be present. -</p> - - - -<h2 id="deployment">deployment</h2> -<p>The root element.</p> -<table class="table"> - <thead> - <tr> - <th style="width:150px">Attribute</th> - <th style="width:100px">Mandatory</th> - <th>Values</th> - </tr> - </thead> - <tbody> - <tr> - <td>version</td> - <td>Yes</td> - <td>1.0</td> - </tr><tr> - <td>major-version</td> - <td>No</td> - <td>The major version number this application is valid for.</td> - </tr> - <tr> - <td>cloud-account</td> - <td>No</td> - <td>Account to deploy to with <a href="../../operations/enclave/enclave">Vespa Cloud Enclave</a>.</td> - </tr> - </tbody> -</table> - - - -<h2 id="instance">instance</h2> -<p> -In <code><deployment></code> or <code><parallel></code> (which must be a direct descendant of the root). -An instance of the application; several of these may be simultaneously deployed in the same zone. -If no <code><instance></code> is specified, all children of the root are implicitly children of -an <code><instance></code> with <code>id="default"</code>, as in the simple example at the top. -</p> -<table class="table"> - <thead> - <tr> - <th style="width:150px">Attribute</th> - <th style="width:100px">Mandatory</th> - <th>Values</th> - </tr> - </thead> - <tbody> - <tr> - <td>id</td> - <td>Yes</td> - <td>The unique name of the instance.</td> - </tr> - <tr> - <td>tags</td> - <td>No</td> - <td>Space-separated tags which can be referenced to make <a href="../../operations/deployment-variants.html">deployment variants</a>.</td> - </tr> - <tr> - <td>cloud-account</td> - <td>No</td> - <td>Account to deploy to with <a href="../../operations/enclave/enclave">Vespa Cloud Enclave</a>. Overrides parent's use of cloud-account.</td> - </tr> - </tbody> -</table> - - - -<h2 id="block-change">block-change</h2> -<p> -In <code><deployment></code>, <strong>or</strong> <code><instance></code>. -This blocks changes from being deployed to production in the matching time interval. -Changes are nevertheless tested while blocked. -</p> -<p> -By default, both application revision changes and Vespa platform changes -(upgrades) are blocked. It is possible to block just one kind of change using -the <code>revision</code> and <code>version</code> attributes. -</p> -<p> - Any combination of the attributes below can be specified. Changes on a given - date will be blocked if all conditions are met. - Invalid <code><block-change></code> tags (i.e. that contains conditions - that never match an actual date) are rejected by the system. -</p> -<p> -This tag must be placed after any <code><test></code> and <code><staging></code> tags, -and before <code><prod></code>. It can be declared multiple times. -</p> -<table class="table"> - <thead> - <tr> - <th style="width:150px">Attribute</th> - <th style="width:100px">Mandatory</th> - <th>Values</th> - </tr> - </thead> - <tbody> - <tr> - <td>revision - <td>No, default <code>true</code> - <td>Set to <code>false</code> to allow application deployments - </tr> - <tr> - <td>version - <td>No, default <code>true</code> - <td>Set to <code>false</code> to allow Vespa platform upgrades - </tr> - <tr> - <td>maintenance - <td>No, default <code>false</code> - <td>Set to <code>true</code> to disallow Vespa maintenance operations. This is best effort, maintenance - can still happen (e.g. for security reasons). The block window for maintenance should be open at least - 10% of the time calculated over a week, that is, at least 17 hours per week. - </tr> - <tr> - <td>days - <td>No, default <code>mon-sun</code> - <td>List of days this block is effective - a comma-separated list of single days or day intervals - where the start and end day are separated by a dash and are inclusive. Each day is identified by its english - name or three-letter abbreviation. - </tr> - <tr> - <td>hours - <td>No, default <code>0-23</code> - <td>List of hours this block is effective - a comma-separated list of single hours or hour intervals - where the start and end hour are separated by a dash and are inclusive. Each hour is identified by a number - in the range 0 to 23. - </tr> - <tr> - <td>time-zone - <td>No, default UTC - <td>The name of the time zone used to interpret the hours attribute. - Time zones are full names or short forms, when the latter is unambiguous. - See - <a href="https://docs.oracle.com/javase/8/docs/api/java/time/ZoneId.html#of-java.lang.String-">ZoneId.of</a> - for the full spec of acceptable values. - </tr> - <tr> - <td>from-date - <td>No - <td>The inclusive starting date of this block (ISO-8601, <code>YYYY-MM-DD</code>). - </tr> - <tr> - <td>to-date - <td>No - <td>The inclusive ending date of this block (ISO-8601, <code>YYYY-MM-DD</code>). - </tr> - </tbody> -</table> -<p> - The below example blocks all changes on weekends, and blocks revisions outside working hours, - in the PST time zone: -</p> -<pre>{% highlight xml %} -<block-change days="sat-sun" - hours="0-23" - time-zone="America/Los_Angeles"/> -<block-change revision="false" - days="mon-fri,sat,sun" - hours="0-8,16-23" - time-zone="America/Los_Angeles"/> -{% endhighlight %}</pre> -<p>The below example blocks:</p> - <ul> - <li>all changes on Sundays starting on 2022-03-01</li> - <li>all changes in the hours 16-23 between 2022-02-10 and 2022-02-15</li> - <li>all changes until 2022-01-05</li> - </ul> -<pre>{% highlight xml %} -<block-change days="sun" - from-date="2022-03-01" - time-zone="America/Los_Angeles"/> -<block-change hours="16-23" - from-date="2022-02-10" - to-date="2022-02-15" - time-zone="America/Los_Angeles"/> -<block-change to-date="2022-01-05" - time-zone="America/Los_Angeles"/> -{% endhighlight %}</pre> - - - -<h2 id="backup">backup</h2> -<p> -In <code><deployment></code>, <strong>or</strong> <code><instance></code>. -Configures scheduled backups of production content clusters. When present, backups will -be created at the specified frequency. Must be placed after any <code><test></code> and <code><staging></code> tags, -and before <code><prod></code>. -</p> -<table class="table"> - <thead> - <tr> - <th style="width:150px">Attribute</th> - <th style="width:100px">Mandatory</th> - <th>Values</th> - </tr> - </thead> - <tbody> - <tr> - <td>frequency</td> - <td>Yes</td> - <td>A positive integer with a suffix <code>h</code> (hours) or <code>d</code> (days), - e.g. <code>12h</code> or <code>7d</code>. Minimum 1h.</td> - </tr> - <tr> - <td>granularity</td> - <td>No, default <code>cluster</code></td> - <td> - <ul> - <li><code>cluster</code>: all content nodes in the cluster</li> - <li><code>group</code>: all content nodes in a single group</li> - </ul> - </td> - </tr> - </tbody> -</table> -<p> - Backup activity does not affect service availability, but has costs in terms of performance. You can use <code>granularity</code> - to control the tradeoff between backup and restoration speed. - <ul> - <li> - A <code>cluster</code> backup will take longer, - as each content node must be temporarily suspended to ensure data integrity. Restoration will however require - effectively zero content redistribution. - </li> - <li> - A <code>group</code> backup will be faster, as an entire group will be suspended and backed up simultaneously. - Restoration may however require a significant amount of content redistribution, depending on the cluster topology. - </li> - </ul> - <br/> - In most situations we recommend <code>cluster</code> backups. -</p> -<p> - <a href="#block-change">Block windows</a> also prevent new backups from starting in the given period. - If the available time is too short for a full backup to complete, the process will, however, extend beyond the block window. -</p> -<p> - <a href="../../operations/data-management.html#backup">Read more</a>. -</p> - - - -<h2 id="resource-tags">resource-tags</h2> -<p> -In <code><deployment></code>, <strong>or</strong> <code><instance></code>. -Specifies custom tags to apply to cloud resources (virtual machines and attached disks) provisioned -in the tenant cloud account. Only available for -<a href="../../operations/enclave/enclave">Vespa Cloud Enclave</a> deployments, where a -<code>cloud-account</code> is set. Commonly used for cost tracking and resource management. -</p> -<p> -Tags declared at the <code><deployment></code> level apply to all instances. Tags at the -<code><instance></code> level are merged with deployment-level tags; on key conflict, the -instance-level value wins. -</p> -<pre>{% highlight xml %} -<deployment version="1.0" cloud-account="aws:123456789012"> - <resource-tags> - <tag key="cost-center" value="engineering"/> - <tag key="env" value="${environment}"/> - </resource-tags> - - <instance id="prod"> - <resource-tags> - <tag key="cost-center" value="search-team"/> - </resource-tags> - <prod> - <region name="aws-us-east-1c"/> - </prod> - </instance> -</deployment> -{% endhighlight %}</pre> -<p> - The <code><resource-tags></code> element contains one or more <code><tag></code> children. - Each <code><tag></code> has two mandatory attributes: -</p> -<table class="table"> - <thead> - <tr> - <th style="width:150px">Attribute</th> - <th style="width:100px">Mandatory</th> - <th>Values</th> - </tr> - </thead> - <tbody> - <tr> - <td>key</td> - <td>Yes</td> - <td> - The tag key. Must be non-empty. Allowed characters and maximum length depend on the - target cloud; see <a href="#resource-tags-per-cloud-rules">per-cloud rules</a> below. - Certain keys are <a href="#resource-tags-reserved-keys">reserved</a> by the platform. - </td> - </tr> - <tr> - <td>value</td> - <td>Yes</td> - <td> - The tag value. Must be non-empty. May contain - <a href="#resource-tags-template-variables">template variables</a>. - Allowed characters and maximum length depend on the target cloud; - see <a href="#resource-tags-per-cloud-rules">per-cloud rules</a> below. - </td> - </tr> - </tbody> -</table> -<p> - A maximum of 20 tags may be defined per instance (after merging deployment-level and - instance-level tags). This limit is the same for all clouds; the remaining tag capacity at - each provider is reserved for platform-owned tags. -</p> - -<p id="resource-tags-per-cloud-rules"><strong>Per-cloud rules.</strong> - Allowed characters and length limits vary by cloud provider. - A single deployment can span multiple clouds, so tags are validated against the rules of each - target cloud at deploy time. If a tag is valid for AWS but not for GCP, the deployment will - succeed in AWS regions but fail in GCP regions. -</p> -<table class="table"> - <thead> - <tr> - <th>Constraint</th> - <th>AWS</th> - <th>Azure</th> - <th>GCP</th> - </tr> - </thead> - <tbody> - <tr> - <td>Key characters</td> - <td><code>[a-zA-Z0-9 +-=._:/@]</code></td> - <td>Unicode, except <code>< > % & \ ? /</code></td> - <td><code>[a-z][a-z0-9_-]*</code> (must start with lowercase letter)</td> - </tr> - <tr> - <td>Value characters</td> - <td><code>[a-zA-Z0-9 +-=._:/@]</code></td> - <td>No restrictions</td> - <td><code>[a-z0-9_-]*</code></td> - </tr> - <tr> - <td>Key max length</td> - <td>128</td> - <td>512</td> - <td>63</td> - </tr> - <tr> - <td>Value max length</td> - <td>256</td> - <td>256</td> - <td>63</td> - </tr> - </tbody> -</table> - -<p id="resource-tags-reserved-keys"><strong>Reserved keys.</strong> -The following tag keys are reserved by the platform and cannot be used. -All comparisons are case-insensitive.</p> -<table class="table"> - <thead> - <tr> - <th style="width:150px">Type</th> - <th>Reserved keys</th> - </tr> - </thead> - <tbody> - <tr> - <td>Exact names</td> - <td> - <code>app</code>, - <code>application</code>, - <code>applicationid</code>, - <code>athenz</code>, - <code>athenz-domain</code>, - <code>athenzservice</code>, - <code>auth-method</code>, - <code>cluster</code>, - <code>clusterid</code>, - <code>fqdn</code>, - <code>generation</code>, - <code>name</code>, - <code>owner</code>, - <code>preprovisioned</code>, - <code>system</code>, - <code>tenant</code>, - <code>tenantName</code>, - <code>zone</code> - </td> - </tr> - <tr> - <td>Key prefixes</td> - <td> - <code>vai_</code>, - <code>corp_</code>, - <code>corp:</code>, - <code>bastion_</code>, - <code>bastion:</code> - </td> - </tr> - </tbody> -</table> - -<p id="resource-tags-template-variables"><strong>Template variables.</strong> -Tag values may reference the following template variables. -Resolved values are always lowercased regardless of cloud. -Template-variable placeholders are excluded when checking per-cloud character rules, -so only the literal parts of the value are validated. -Referencing an unknown variable causes the deployment to fail. -Variables can be combined, e.g. <code>value="${environment}-${clustertype}"</code>.</p> -<table class="table"> - <thead> - <tr> - <th style="width:200px">Variable</th> - <th>Description</th> - </tr> - </thead> - <tbody> - <tr> - <td><code>${tenant}</code></td> - <td>The tenant name, e.g. <code>mytenant</code>.</td> - </tr> - <tr> - <td><code>${application}</code></td> - <td>The application name, e.g. <code>myapp</code>.</td> - </tr> - <tr> - <td><code>${instance}</code></td> - <td>The instance name, e.g. <code>default</code>, <code>beta</code>.</td> - </tr> - <tr> - <td><code>${environment}</code></td> - <td>The deployment environment, e.g. <code>prod</code>, <code>dev</code>.</td> - </tr> - <tr> - <td><code>${region}</code></td> - <td>The deployment region, e.g. <code>aws-us-east-1c</code>.</td> - </tr> - <tr> - <td><code>${clustername}</code></td> - <td>The cluster ID from <a href="services/services.html">services.xml</a>, - e.g. <code>default</code>, <code>music</code>.</td> - </tr> - <tr> - <td><code>${clustertype}</code></td> - <td>The Vespa cluster type: <code>container</code>, <code>content</code>, or <code>admin</code>.</td> - </tr> - </tbody> -</table> - -<p><strong>Reconciliation.</strong> -Tags are applied to virtual machines and attached disks. When tags are changed, added, or removed -in <em>deployment.xml</em>, the existing resources are updated by a background reconciliation -process. Tags that were previously applied by Vespa Cloud but are no longer listed are removed -from the resources. Tags added manually by the tenant in the cloud console are preserved. -</p> - - - -<h2 id="upgrade">upgrade</h2> -<p> -In <code><deployment></code>, or <code><instance></code>. -Determines the strategy for upgrading the application, or one of its instances. -By default, application revision changes deploy independently of platform upgrades, -and an application revision can catch up to and pass an ongoing platform upgrade. -See the <code>rollout</code> attribute below to change this behavior. -</p> -<table class="table"> - <thead> - <tr> - <th style="width:150px">Attribute</th> - <th style="width:100px">Mandatory</th> - <th>Values</th> - </tr> - </thead> - <tbody> - <tr> - <td>rollout</td> - <td>No, default <code>simultaneous</code></td> - <td> - <ul> - <li><code>separate</code>: When a revision catches up to a platform upgrade, it stays behind, unless the upgrade alone fails.</li> - <li><code>leading</code>: When a revision catches up to a platform upgrade, they fuse and roll out together.</li> - <li><code>simultaneous</code> is the default, and favors revision roll-out. Revision changes deploy independently of platform upgrades. When a revision catches up to a platform upgrade, it joins, and then passes the upgrade.</li> - </ul> - </td> - </tr> - <tr> - <td>revision-target</td> - <td>No, default <code>latest</code></td> - <td> - <ul> - <li><code>latest</code> is the default. When rolling out a new revision to an instance, the latest available revision is chosen.</li> - <li><code>next</code> trades speed for smaller changes. When rolling out a new revision to an instance, the next available revision is chosen.</li> - </ul> - The available revisions for an instance are revisions which are not yet deployed, or revisions which have rolled out in previous instances. - </td> - </tr> - <tr> - <td>revision-change</td> - <td>No, default <code>when-failing</code></td> - <td> - <ul> - <li><code>always</code> is the most aggressive setting. A new, available revision may always replace the one which is currently rolling out.</li> - <li><code>when-failing</code> is the default. A new, available revision may replace the one which is currently rolling out if this is failing.</li> - <li><code>when-clear</code> is the most conservative setting. A new, available revision may never replace one which is currently rolling out.</li> - </ul> - Revision targets will never automatically change inside <a href="#block-change">revision block window</a>, - but may be set by manual intervention at any time. - </td> - </tr> - <tr> - <td>max-risk</td> - <td>No, default <code>0</code></td> - <td> - May only be used with <code>revision-change="when-clear"</code> and <code>revision-target="next"</code>. - The maximum amount of <em>risk</em> to roll out per new revision target. - The default of <code>0</code> results in the next build always being chosen, while a higher value allows - skipping intermediate builds, as long as the cumulative risk does not exceed what is configured here. - </td> - </tr> - <tr> - <td>min-risk</td> - <td>No, default <code>0</code></td> - <td> - Must be less than or equal to the configured <code>max-risk</code>. - The minimum amount of <em>risk</em> - to start rolling out a new revision. - The default of <code>0</code> results in a new revision rolling out as soon as anything is ready, while a higher - value lets the system wait until enough cumulative risk is available. This can be used to avoid blocking a lengthy - deployment process with trivial changes. - </td> - </tr> - <tr> - <td>max-idle-hours</td> - <td>No, default <code>8</code></td> - <td> - May only be used when <code>min-risk</code> is specified, and greater than <code>0</code>. - The maximum number of hours to wait for enough cumulative risk to be available, before rolling out a new revision. - </td> - </tr> - </tbody> -</table> - - - -<h2 id="test">test</h2> -<p>Meaning depends on where it is located:</p> -<table class="table"> - <thead> - <tr> - <th>Parent</th> - <th>Description</th> - </tr> - </thead> - <tbody> - <tr> - <td><code><deployment></code> <code><instance></code></td> - <td>If present, the application is deployed to the <a href="../../operations/environments.html#test"><code>test</code></a> environment, - and system tested there, even if no prod zones are deployed to. Also, when specified, system tests <em>must</em> - be present in the application test package. - See guides for <a href="../../operations/production-deployment.html">getting to production</a>.<br> - If present in an <code><instance></code> element, system tests are run for that specific instance before any - production deployments of the instance may proceed — otherwise, previous system tests for any instance are acceptable.</td> - </tr> - <tr> - <td><code><prod></code> <code><parallel></code> <code><steps></code></td> - <td>If present, production tests are run against the production region with id contained in this element. - A test must be <em>after</em> a corresponding <a href="#region">region</a> element. - When specified, production tests <em>must</em> be preset in the application test package. - See guides for <a href="../../operations/production-deployment.html">getting to production</a>.</td> - </tr> - </tbody> -</table> - -<table class="table"> - <thead> - <tr> - <th style="width:150px">Attribute</th> - <th style="width:100px">Mandatory</th> - <th>Values</th> - </tr> - </thead> - <tbody> - <tr> - <td>cloud-account</td> - <td>No</td> - <td>For <a href="../../operations/automated-deployments.html#system-tests">system tests</a> only: - account to deploy to with <a href="../../operations/enclave/enclave">Vespa Cloud Enclave</a>. Overrides parent's use of cloud-account. - Cloud account <em>must not</em> be specified for - <a href="../../operations/automated-deployments.html#production-tests">production tests</a>, - which always run in the account of the corresponding deployment.</td> - </tr> - </tbody> -</table> - - - -<h2 id="staging">staging</h2> -<p> -In <code><deployment></code>, or <code><instance></code>. -If present, the application is deployed to the -<a href="../../operations/environments.html#staging"><code>staging</code></a> environment, -and tested there, even if no prod zones are deployed to. -If present in an <code><instance></code> element, staging tests are run for that specific instance before any -production deployments of the instance may proceed — otherwise, previous staging tests for any instance are acceptable. -When specified, staging tests <em>must</em> be preset in the application test package. -See guides for <a href="../../operations/production-deployment.html">getting to production</a>.<br> -</p> -<table class="table"> - <thead> - <tr> - <th style="width:150px">Attribute</th> - <th style="width:100px">Mandatory</th> - <th>Values</th> - </tr> - </thead> - <tbody> - <tr> - <td>cloud-account</td> - <td>No</td> - <td>Account to deploy to with <a href="../../operations/enclave/enclave">Vespa Cloud Enclave</a>. Overrides parent's use of cloud-account.</td> - </tr> - </tbody> -</table> - - - -<h2 id="prod">prod</h2> -<p> -In <code><deployment></code>, <strong>or</strong> in <code><instance></code>. -If present, the application is deployed to the production regions listed inside this element, under the specified instance, -after deployments and tests in the <code>test</code> and <code>staging</code> environments. -</p> -<table class="table"> - <thead> - <tr> - <th style="width:150px">Attribute</th> - <th style="width:100px">Mandatory</th> - <th>Values</th> - </tr> - </thead> - <tbody> - <tr> - <td>cloud-account</td> - <td>No</td> - <td>Account to deploy to with <a href="../../operations/enclave/enclave">Vespa Cloud Enclave</a>. Overrides parent's use of cloud-account.</td> - </tr> - </tbody> -</table> - - - -<h2 id="region">region</h2> -<p> -In <code><prod></code>, <code><parallel></code>, <code><steps></code>, or <code><group></code>. -The application is deployed to the production -<a href="../../operations/zones.html">region</a> identified by the <code>name</code> attribute. -</p> -<table class="table"> -<thead> -<tr> -<th style="width:150px">Attribute</th> -<th style="width:100px">Mandatory</th> -<th>Values</th> -</tr> -</thead> -<tbody> -<tr> -<td>name</td> -<td>Yes</td> -<td> - The region identifier, e.g. <code>aws-us-east-1c</code>. See <a href="../../operations/zones.html">zones</a> - for the list of available regions. - Mandatory <em>unless</em> the region name is specified in the element body, but this is not compatible with - specifying <a href="#availability-zone"><code><availability-zone></code></a> children.</td> -</tr> -<tr> -<td>fraction</td> -<td>No</td> -<td> -Only when this region is inside a group: The fractional membership in the group. -</td> -</tr> -<tr> - <td>cloud-account</td> - <td>No</td> - <td>Account to deploy to with <a href="../../operations/enclave/enclave">Enclave</a>. Overrides parent's use of cloud-account.</td> -</tr> -</tbody> -</table> - - -<h3 id="availability-zone">availability-zone</h3> -<p> -In <code><region></code>. The element body must be one of the availability zone identifiers listed in -<a href="../../operations/zones.html">zones</a>. -At least one availability zone <em>must</em> be specified when deploying to a production region that supports -more than one availability zone. The application instance will be spread out evenly across these availability zones -for resiliency. -See <a href="../../operations/az.html">availability zones</a> for more. -</p> -<p>Example:</p> -<pre> -{% highlight xml %} -<region name="aws-us-east-1"> - <availability-zone>use1-az1</availability-zone> - <availability-zone>use1-az2</availability-zone> -</region> -{% endhighlight %}</pre> - - -<h2 id="dev">dev</h2> -<p> -In <code><deployment></code>. -Optionally used to control deployment settings for the <a href="../../operations/environments.html">dev environment</a>. -This can be used specify a different cloud account, tags, and private endpoints. -</p> -<table class="table"> - <thead> - <tr> - <th style="width:150px">Attribute</th> - <th style="width:100px">Mandatory</th> - <th>Values</th> - </tr> - </thead> - <tbody> - <tr> - <td>tags</td> - <td>No</td> - <td>Space-separated tags which can be referenced to make <a href="../../operations/deployment-variants.html">deployment variants</a>.</td> - </tr> - <tr> - <td>cloud-account</td> - <td>No</td> - <td>Account to deploy to with <a href="../../operations/enclave/enclave">Vespa Cloud Enclave</a>. Overrides parent's use of cloud-account.</td> - </tr> - </tbody> -</table> - - - -<h2 id="delay">delay</h2> -<p> -In <code><deployment></code>, <code><instance></code>, <code><prod></code>, <code><parallel></code>, or <code><steps></code>. -Introduces a delay which must pass after completion of all previous steps, before subsequent steps may proceed. -This may be useful to allow some grace time to discover errors before deploying a change in additional zones, -or to gather higher-level metrics for a production deployment for a while, before evaluating these in a production test. -The maximum total delay for the whole deployment spec is 48 hours. -The delay is specified by any combination of the <code>hours</code>, <code>minutes</code> and <code>seconds</code> attributes. -</p> -<p> -By default, a delay applies to both application revision changes and Vespa platform -upgrades. It is possible to restrict the delay to just one kind of change -using the <code>revision</code> and <code>version</code> attributes, with the same -semantics as on <a href="#block-change">block-change</a>. When a delay applies only to one -kind of change, the other kind passes through the step with no delay. -</p> -<table class="table"> - <thead> - <tr> - <th style="width:150px">Attribute</th> - <th style="width:100px">Mandatory</th> - <th>Values</th> - </tr> - </thead> - <tbody> - <tr> - <td>hours - <td>No, default <code>0</code> - <td>The number of hours to delay. - </tr> - <tr> - <td>minutes - <td>No, default <code>0</code> - <td>The number of minutes to delay. - </tr> - <tr> - <td>seconds - <td>No, default <code>0</code> - <td>The number of seconds to delay. - </tr> - <tr> - <td>revision - <td>No, default <code>true</code> - <td>Set to <code>false</code> to let application revision deployments proceed without this delay. - </tr> - <tr> - <td>version - <td>No, default <code>true</code> - <td>Set to <code>false</code> to let Vespa platform upgrades proceed without this delay. - </tr> - </tbody> -</table> -<p> - The below example delays Vespa platform upgrades by 1 hour between the surrounding - steps, while application revision deployments proceed with no delay: -</p> -<pre>{% highlight xml %} -<delay hours="1" revision="false" version="true"/> -{% endhighlight %}</pre> - - - -<h2 id="parallel">parallel</h2> -<p> -In <code><deployment></code>, <code><prod></code>, or <code><steps></code>. -Runs the contained steps in parallel: instances if in <code><deployment></code>, -or primitive steps (deployments, tests or delays) or a series of these (see <a href="#steps">steps</a>) otherwise. -Multiple <code><parallel></code> elements are permitted. The following example will deploy -to <code>us-west-1</code> first, then to <code>us-east-3</code> and <code>us-central-1</code> -simultaneously, and, finally to <code>eu-west-1</code>, once both parallel deployments -have completed: -</p> -<pre>{% highlight xml %} -<region name="us-west-1"/> -<parallel> - <region name="us-east-3"/> - <region name="us-central-1"/> -</parallel> -<region name="eu-west-1"/> -{% endhighlight %}</pre> - - - -<h2 id="steps">steps</h2> -<p> -In <code><parallel></code>. -Runs the contained parallel or primitive steps (deployments, tests or delays) serially. -The following example will in parallel:</p> -<ol> - <li>deploy to <code>us-east-3</code>,</li> - <li>deploy to <code>us-west-1</code>, then delay 1 hour, and run tests for <code>us-west-1</code>, and</li> - <li>delay for two hours.</li> -</ol> -<p> -Thus, the parallel block is complete when both deployments are complete, tests are successful for -the second deployment, and at least two hours have passed since the block began executing. -</p> -<pre>{% highlight xml %} -<parallel> - <region name="us-east-3"/> - <steps> - <region name="us-west-1"/> - <delay hours="1" /> - <test>us-west-1</test> - </steps> - <delay hours="2" /> -</parallel> -{% endhighlight %}</pre> - - - -<h2 id="tester">tester</h2> -<p> -In <code><test></code>, <code><staging></code> and <code><prod></code>. -Specifies container settings for the tester application container, which is used to run -system, staging and production verification tests. -</p> -<p>The allowed elements inside this are <a href="../applications/services/services.html#nodes"><code><nodes></code></a>.</p> -<pre>{% highlight xml %} -<staging> - <tester> - <nodes count="1"> - <resources vcpu="8" memory="32Gb" disk="30Gb" /> - </nodes> - </tester> -</staging> -{% endhighlight %}</pre> - - - -<h2 id="endpoints-global">endpoints (global)</h2> -<p> -In <code><deployment></code>, without any <code><instance></code> -declared <strong>or</strong> in <code><instance></code>: This allows -<em>global</em> endpoints, via one or -more <a href="#endpoint-global"><code><endpoint></code></a> elements; -and <a href="#endpoint-zone">zone endpoint</a> and <a href="#endpoint-private">private endpoint</a> -elements for cloud-native private network configuration. -</p> - - - -<h2 id="endpoints-dev">endpoints (dev)</h2> -<p> -In <code><dev></code>. This allows -<a href="#endpoint-zone">zone endpoint</a> -elements for cloud-native private network configuration for -<a href="../../operations/environments.html#dev">dev</a> deployments. -Note that <a href="#endpoint-private">private endpoints</a> are only supported in <code>prod</code>. -</p> - - - -<h2 id="endpoint-global">endpoint (global)</h2> -<p> -In <code><endpoints></code> or <code><group></code>. -Specifies a global endpoint for this application. -Each endpoint will point to the regions that are declared in the endpoint. -If no regions are specified, -the endpoint defaults to the regions declared in the <code><prod></code> element. -The following example creates a default endpoint to all regions, -and a <em>us</em> endpoint pointing only to US regions. -</p> -<pre>{% highlight xml %} -<endpoints> - <endpoint container-id="my-container-service"/> - <endpoint id="us" container-id="my-container-service"> - <region name="aws-us-east-1c"/> - <region name="aws-us-west-2a"/> - </endpoint> -</endpoints> -{% endhighlight %}</pre> -<table class="table"> - <thead> - <tr> - <th style="width:150px">Attribute</th> - <th style="width:100px">Mandatory</th> - <th>Values</th> - </tr> - </thead> - <tbody> - <tr> - <td>id</td> - <td>No</td> - <td> - The identifier for the endpoint. This will be part of the endpoint name that is generated. - If not specified, the endpoint will be the default global endpoint for the application. - </td> - </tr><tr> - <td>container-id</td> - <td>Yes</td> - <td> - The id of the <a href="/en/reference/applications/services/container.html">container cluster</a> - to which requests to the global endpoint is forwarded. - </td> - </tr> - </tbody> -</table> -<p> - Global endpoints are implemented using Route 53 and healthchecks, - to keep active zones in rotation. - See <a href="#bcp">BCP</a> for advanced configurations. -</p> - - - -<h2 id="endpoint-zone">endpoint (zone)</h2> -<p> -In <code><endpoints></code> or <code><group></code>, with <code>type='zone'</code>. -Used to disable public zone endpoints. <em>Non-public endpoints can not be used in -global endpoints, which require that all constituent endpoints are public.</em> -The example disables the public zone endpoint for the <code>my-container</code> -container cluster in all regions, except where it is explicitly enabled, in <code>region-1</code>. -Changing endpoint visibility will make the service unavailable for a short period of time. -</p> -<pre>{% highlight xml %} -<endpoints> - <endpoint type='zone' container-id='my-container' enabled='false' /> - <endpoint type='zone' container-id='my-container' enabled='true'> - <region name="region-1"/> - </endpoint> -</endpoints> -{% endhighlight %}</pre> -<table class="table"> - <thead> - <tr> - <th style="width:150px">Attribute</th> - <th style="width:100px">Mandatory</th> - <th>Values</th> - </tr> - </thead> - <tbody> - <tr> - <td>type</td> - <td>Yes</td> - <td> - Private endpoints are specified with <code>type='zone'</code>. - </td> - </tr><tr> - <td>container-id</td> - <td>Yes</td> - <td> - The id of the <a href="/en/reference/applications/services/container.html">container cluster</a> - to disable public endpoints for. - </td> - </tr><tr> - <td>enabled</td> - <td>No</td> - <td> - Whether a public endpoint for this container cluster should be enabled; default <code>true</code>. - </td> - </tr> - </tbody> -</table> - - - -<h2 id="endpoint-private">endpoint (private)</h2> -<p> -In <code><endpoints></code> or <code><group></code>, with <code>type='private'</code>. -Specifies a private endpoint service for this application. -Each service will be launched in the regions that are declared in the endpoint. -If no regions are specified, the service is launched in all regions declared in the -<code><prod></code> element, that support any of the declared <a href="#allow">access types</a>. -The following example creates a private endpoint in two specific regions. -</p> -<pre>{% highlight xml %} -<endpoints> - <endpoint type='private' container-id='my-container'> - <region name="aws-us-east-1c"/> - <allow with='aws-private-link' arn='arn:aws:iam::123123123123:root' /> - </endpoint> - <endpoint type='private' container-id='my-container'> - <region name="gcp-us-central1-f"/> - <allow with='gcp-service-connect' project='user-project' /> - </endpoint> -</endpoints> -{% endhighlight %}</pre> -<table class="table"> - <thead> - <tr> - <th style="width:150px">Attribute</th> - <th style="width:100px">Mandatory</th> - <th>Values</th> - </tr> - </thead> - <tbody> - <tr> - <td>type</td> - <td>Yes</td> - <td> - Private endpoints are specified with <code>type='private'</code>. - </td> - </tr> - <tr> - <td>container-id</td> - <td>Yes</td> - <td> - The id of the <a href="/en/reference/applications/services/container.html">container cluster</a> - to which requests to the private endpoint service is forwarded. - </td> - </tr> - <tr> - <td>auth-method</td> - <td>No</td> - <td> - The authentication method to use with this <a href="/en/operations/private-endpoints.html">private endpoint</a>. - <br/> - Must be either <code>mtls</code> or <code>token</code>. - Defaults to mTLS if not included. - </td> - </tr> - </tbody> -</table> - - - -<h2 id="allow">allow</h2> -<p> -In <code><endpoint type='private'></code>. -Allows a principal identified by the URN to set up a connection to the declared private endpoint service. -This element must be repeated for each additional URN. -An endpoint service will only consider allowed URNs of a compatible type, and will only be created if -at least one compatible access type-and-URN is given: -</p> -<ul> -<li>For AWS deployments, specify <code>aws-private-link</code>, and an <em>ARN</em>. -<li>For GCP deployments, specify <code>gcp-service-connect</code>, and a <em>project ID</em></li> -</ul> -<pre>{% highlight xml %} -<endpoint type='private' container-id="my-container"> - <allow with='aws-private-link' arn='arn:aws:iam::123123123123:root' /> - <allow with='aws-private-link' arn='arn:aws:iam::321321321321:role/my-role' /> - <allow with='aws-private-link' arn='arn:aws:iam::321321321321:user/my-user' /> - <allow with='gcp-service-connect' project='my-project' /> -</endpoint> -{% endhighlight %}</pre> -<table class="table"> - <thead> - <tr> - <th style="width:150px">Attribute</th> - <th style="width:100px">Mandatory</th> - <th>Values</th> - </tr> - </thead> - <tbody> - <tr> - <td>with</td> - <td>Yes</td> - <td> - The private endpoint access type; must be <code>aws-private-link</code> or <code>gcp-service-connect</code>. - </td> - </tr><tr> - <td>arn</td> - <td>Maybe</td> - <td> - Must be specified with <code>aws-private-link</code>. - See <a href="https://docs.aws.amazon.com/vpc/latest/privatelink/configure-endpoint-service.html">AWS documentation</a> - for more details. - </td> - </tr><tr> - <td>project</td> - <td>Maybe</td> - <td> - Must be specified with <code>gcp-service-connect</code>. - See <a href="https://cloud.google.com/vpc/docs/configure-private-service-connect-services">GCP documentation</a> - for more details. - </td> - </tr> - </tbody> -</table> - - - -<h2 id="bcp">bcp</h2> -<p>In <code><instance></code> or <code><deployment></code>. -Defines the BCP (Business Continuity Planning) structure of this instance: -Which zones should take over for which others -during the outage of a zone and how fast they must have the capacity ready. -Autoscaling uses this information to decide the ideal cpu load of a zone. -If this element is not defined, it is assumed that all regions covers -for an equal share of the traffic of all other regions and must have that capacity ready at all times. -</p> -<p>If a bcp element is specified at the root, and explicit instances are used, that bcp -element becomes the default for all instances that does not contain a bcp element themselves. -If a BCP element contains no group elements it will implicitly define a single group of all the regions -of the instance in which it is used. -</p> -<p> - See <a href="https://cloud.vespa.ai/en/reference/bcp-test.html">BCP test</a> - for a procedure to verify that your BCP configuration is correct. -</p> -<table class="table"> - <thead> - <tr> - <th style="width:150px">Attribute</th> - <th style="width:100px">Mandatory</th> - <th>Values</th> - </tr> - </thead> - <tbody> - <tr> - <td>deadline</td> - <td>No</td> - <td> - <p>The max time after a region becomes unreachable until the other regions in its BCP group must be able to handle - the traffic of it, given as a number of minutes followed by 'm', 'h' or 'd' (for minutes, hours or days). - The default deadline is 0: Regions must at all times have capacity to handle BCP traffic immediately.</p> - - <p>By providing a deadline, autoscaling can avoid the cost - of provisioning additional resources for BCP capacity if it predicts that it can grow to handle the traffic - faster than the deadline in a given cluster.</p> - - <p>This is the default deadline to be used for all groups that don't specify one themselves.</p> - </td> - </tr> - </tbody> -</table> -<p>Example:</p> -<pre>{% highlight xml %} -<bcp> - <group deadline="15m"> - <endpoint id="foo" container-id="bar"/> - <region name="us-east1"/> - <region name="us-east2"/> - <region name="us-central1" fraction="0.5"/> - </group> - <group> - <region name="us-west1"/> - <region name="us-west2"/> - <region name="us-central1" fraction="0.5"/> - </group> -</bcp> -{% endhighlight %}</pre> - - - -<h2 id="group">group</h2> -<p>In <code><bcp></code>. Defines a bcp group: -A set of regions whose members cover for each other during a regional outage. - -<p>Each region in a group will (as allowed, when autoscaling ranges are configured) provision resources -sufficient to handle that any other single region in the group goes down. -The traffic of the region is assumed to be rerouted in equal amount to the remaining regions in the group. -That is, if a group has one member, no resources will be provisioned to handle an outage in that member. -If a group has two members, each will aim to provision sufficient resources to handle the actual traffic of the other. -If a group has three members, each will provision to handle half of the traffic observed in the region among the -two others which receives the most traffic.</p> - -<p>A region may have fractional membership in multiple groups, meaning it will handle just that fraction of the -traffic of the remaining members, and vice versa. A regions total membership among groups must always -sum to exactly 1.</p> - -<p>A group may also define global endpoints for the region members in the group. -This is exactly the same as defining the endpoint separately and repeating the regions -of the group under the endpoint. Endpoints under a group cannot contain explicit region sub-elements.</p> - -<table class="table"> -<thead> -<tr> -<th style="width:150px">Attribute</th> -<th style="width:100px">Mandatory</th> -<th>Values</th> -</tr> -</thead> -<tbody> -<tr> -<td>deadline</td> -<td>No</td> -<td> -<p>The deadline of this BCP group. See deadline on the BCP element.</p> -</td> -</tr> -</tbody> -</table> diff --git a/en/reference/applications/deployment.mdx b/en/reference/applications/deployment.mdx new file mode 100644 index 0000000000..7b5a7f45cd --- /dev/null +++ b/en/reference/applications/deployment.mdx @@ -0,0 +1,880 @@ +--- +title: "deployment.xml reference" +sidebarTitle: "deployment.xml" +--- + + +*deployment.xml* controls how an application is deployed. + +*deployment.xml* is placed in the root of the [application package](/en/basics/applications) and specifies which environments and regions the application is deployed to during [automated application deployment](/en/operations/automated-deployments), as which application instances. + +Deployment progresses through the `test` and `staging` environments to the `prod` environments listed in *deployment.xml*. + +Simple example: + +```xml +<deployment version="1.0"> + <prod> + <region>aws-us-east-1c</region> + <region>aws-us-west-2a</region> + </prod> +</deployment> +``` + +More complex example: + +```xml expandable +<deployment version="1.0"> + <instance id="beta"> + <prod> + <region>aws-us-east-1c</region> + </prod> + </instance> + <instance id="default"> + <block-change revision="false" + days="mon,wed-fri" + hours="16-23" + time-zone="UTC" /> + <backup frequency="7d" granularity="cluster" /> + <prod> + <region>aws-us-east-1c</region> + <delay hours="3" minutes="7" seconds="13" /> + <parallel> + <region>aws-us-west-1c</region> + <steps> + <region>aws-eu-west-1a</region> + <delay hours="3" /> + <test>aws-us-west-2a</test> + </steps> + </parallel> + </prod> + <endpoints> + <endpoint container-id="my-container-service"> + <region>aws-us-east-1c</region> + </endpoint> + </endpoints> + </instance> + <endpoints> + <endpoint id="my-weighted-endpoint" + container-id="my-container-service" + region="aws-us-east-1c"> + <instance weight="1">beta</instance> + </endpoint> + </endpoints> +</deployment> +``` + +Some of the elements can be declared *either* under the `<deployment>` root, **or**, if one or more `<instance>` tags are listed, under these. These have a bold **or** when listing where they may be present. + +## deployment + +The root element. + +<table className="vespa-table"> + <thead> + <tr> + <th>Attribute</th> + <th>Mandatory</th> + <th>Values</th> + </tr> + </thead> + <tbody> + <tr> + <td>version</td> + <td>Yes</td> + <td>1.0</td> + </tr> + <tr> + <td>major-version</td> + <td>No</td> + <td>The major version number this application is valid for.</td> + </tr> + <tr> + <td>cloud-account</td> + <td>No</td> + <td>Account to deploy to with <a href="/en/operations/enclave/enclave" aria-label="Vespa Cloud Enclave">Vespa Cloud Enclave</a>.</td> + </tr> + </tbody> +</table> + +## instance + +In `<deployment>` or `<parallel>` (which must be a direct descendant of the root). An instance of the application; several of these may be simultaneously deployed in the same zone. If no `<instance>` is specified, all children of the root are implicitly children of an `<instance>` with `id="default"`, as in the simple example at the top. + +<table className="vespa-table"> + <thead> + <tr> + <th>Attribute</th> + <th>Mandatory</th> + <th>Values</th> + </tr> + </thead> + <tbody> + <tr> + <td>id</td> + <td>Yes</td> + <td>The unique name of the instance.</td> + </tr> + <tr> + <td>tags</td> + <td>No</td> + <td>Space-separated tags which can be referenced to make <a href="/en/operations/deployment-variants" aria-label="deployment variants">deployment variants</a>.</td> + </tr> + <tr> + <td>cloud-account</td> + <td>No</td> + <td>Account to deploy to with <a href="/en/operations/enclave/enclave" aria-label="Vespa Cloud Enclave">Vespa Cloud Enclave</a>. Overrides parent's use of cloud-account.</td> + </tr> + </tbody> +</table> + +## block-change + +In `<deployment>`, **or** `<instance>`. This blocks changes from being deployed to production in the matching time interval. Changes are nevertheless tested while blocked. + +By default, both application revision changes and Vespa platform changes (upgrades) are blocked. It is possible to block just one kind of change using the `revision` and `version` attributes. + +Any combination of the attributes below can be specified. Changes on a given date will be blocked if all conditions are met. Invalid `<block-change>` tags (i.e. that contains conditions that never match an actual date) are rejected by the system. + +This tag must be placed after any `<test>` and `<staging>` tags, and before `<prod>`. It can be declared multiple times. + +<table className="vespa-table"> + <thead> + <tr> + <th>Attribute</th> + <th>Mandatory</th> + <th>Values</th> + </tr> + </thead> + <tbody> + <tr> + <td>revision</td> + <td>No, default <code>{`true`}</code></td> + <td>Set to <code>{`false`}</code> to allow application deployments</td> + </tr> + <tr> + <td>version</td> + <td>No, default <code>{`true`}</code></td> + <td>Set to <code>{`false`}</code> to allow Vespa platform upgrades</td> + </tr> + <tr> + <td>maintenance</td> + <td>No, default <code>{`false`}</code></td> + <td>Set to <code>{`true`}</code> to disallow Vespa maintenance operations. This is best effort, maintenance can still happen (e.g. for security reasons). The block window for maintenance should be open at least 10% of the time calculated over a week, that is, at least 17 hours per week.</td> + </tr> + <tr> + <td>days</td> + <td>No, default <code>{`mon-sun`}</code></td> + <td>List of days this block is effective - a comma-separated list of single days or day intervals where the start and end day are separated by a dash and are inclusive. Each day is identified by its english name or three-letter abbreviation.</td> + </tr> + <tr> + <td>hours</td> + <td>No, default <code>{`0-23`}</code></td> + <td>List of hours this block is effective - a comma-separated list of single hours or hour intervals where the start and end hour are separated by a dash and are inclusive. Each hour is identified by a number in the range 0 to 23.</td> + </tr> + <tr> + <td>time-zone</td> + <td>No, default UTC</td> + <td>The name of the time zone used to interpret the hours attribute. Time zones are full names or short forms, when the latter is unambiguous. See <a href="https://docs.oracle.com/javase/8/docs/api/java/time/ZoneId.html#of-java.lang.String-" aria-label="ZoneId.of">ZoneId.of</a> for the full spec of acceptable values.</td> + </tr> + <tr> + <td>from-date</td> + <td>No</td> + <td>The inclusive starting date of this block (ISO-8601, <code>{`YYYY-MM-DD`}</code>).</td> + </tr> + <tr> + <td>to-date</td> + <td>No</td> + <td>The inclusive ending date of this block (ISO-8601, <code>{`YYYY-MM-DD`}</code>).</td> + </tr> + </tbody> +</table> + +The below example blocks all changes on weekends, and blocks revisions outside working hours, in the PST time zone: + +```xml +<block-change days="sat-sun" + hours="0-23" + time-zone="America/Los_Angeles"/> +<block-change revision="false" + days="mon-fri,sat,sun" + hours="0-8,16-23" + time-zone="America/Los_Angeles"/> +``` + +The below example blocks: + +- all changes on Sundays starting on 2022-03-01 +- all changes in the hours 16-23 between 2022-02-10 and 2022-02-15 +- all changes until 2022-01-05 + +```xml +<block-change days="sun" + from-date="2022-03-01" + time-zone="America/Los_Angeles"/> +<block-change hours="16-23" + from-date="2022-02-10" + to-date="2022-02-15" + time-zone="America/Los_Angeles"/> +<block-change to-date="2022-01-05" + time-zone="America/Los_Angeles"/> +``` + +## backup + +In `<deployment>`, **or** `<instance>`. Configures scheduled backups of production content clusters. When present, backups will be created at the specified frequency. Must be placed after any `<test>` and `<staging>` tags, and before `<prod>`. + +<table className="vespa-table"> + <thead> + <tr> + <th>Attribute</th> + <th>Mandatory</th> + <th>Values</th> + </tr> + </thead> + <tbody> + <tr> + <td>frequency</td> + <td>Yes</td> + <td>A positive integer with a suffix <code>{`h`}</code> (hours) or <code>{`d`}</code> (days), e.g. <code>{`12h`}</code> or <code>{`7d`}</code>. Minimum 1h.</td> + </tr> + <tr> + <td>granularity</td> + <td>No, default <code>{`cluster`}</code></td> + <td>• <code>{`cluster`}</code>: all content nodes in the cluster<br/><br/> • <code>{`group`}</code>: all content nodes in a single group</td> + </tr> + </tbody> +</table> + +Backup activity does not affect service availability, but has costs in terms of performance. You can use `granularity` to control the tradeoff between backup and restoration speed. + +- A `cluster` backup will take longer, as each content node must be temporarily suspended to ensure data integrity. Restoration will however require effectively zero content redistribution. +- A `group` backup will be faster, as an entire group will be suspended and backed up simultaneously. Restoration may however require a significant amount of content redistribution, depending on the cluster topology. + +In most situations we recommend `cluster` backups. + +[Block windows](#block-change) also prevent new backups from starting in the given period. If the available time is too short for a full backup to complete, the process will, however, extend beyond the block window. [Read more](/en/operations/data-management#backup). + +## resource-tags + +In `<deployment>`, **or** `<instance>`. Specifies custom tags to apply to cloud resources (virtual machines and attached disks) provisioned in the tenant cloud account. Only available for [Vespa Cloud Enclave](/en/operations/enclave/enclave) deployments, where a `cloud-account` is set. Commonly used for cost tracking and resource management. + +Tags declared at the `<deployment>` level apply to all instances. Tags at the `<instance>` level are merged with deployment-level tags; on key conflict, the instance-level value wins. + +```xml +<deployment version="1.0" cloud-account="aws:123456789012"> + <resource-tags> + <tag key="cost-center" value="engineering"/> + <tag key="env" value="${environment}"/> + </resource-tags> + + <instance id="prod"> + <resource-tags> + <tag key="cost-center" value="search-team"/> + </resource-tags> + <prod> + <region>aws-us-east-1c</region> + </prod> + </instance> +</deployment> +``` + +The `<resource-tags>` element contains one or more `<tag>` children. Each `<tag>` has two mandatory attributes: + +<table className="vespa-table"> + <thead> + <tr> + <th>Attribute</th> + <th>Mandatory</th> + <th>Values</th> + </tr> + </thead> + <tbody> + <tr> + <td>key</td> + <td>Yes</td> + <td>The tag key. Must be non-empty. Allowed characters and maximum length depend on the target cloud; see <a href="#resource-tags-per-cloud-rules" aria-label="per-cloud rules">per-cloud rules</a> below. The <code>{`vai_`}</code> prefix is reserved for internal use.</td> + </tr> + <tr> + <td>value</td> + <td>Yes</td> + <td>The tag value. Must be non-empty. May contain <a href="#resource-tags-template-variables" aria-label="template variables">template variables</a>. Allowed characters and maximum length depend on the target cloud; see <a href="#resource-tags-per-cloud-rules" aria-label="per-cloud rules">per-cloud rules</a> below.</td> + </tr> + </tbody> +</table> + +The maximum number of tags per instance (after merging deployment-level and instance-level tags) depends on the cloud; see [per-cloud rules](#resource-tags-per-cloud-rules) below. + +**Per-cloud rules.** Allowed characters and length limits vary by cloud provider. A single deployment can span multiple clouds, so tags are validated against the rules of each target cloud at deploy time. If a tag is valid for AWS but not for GCP, the deployment will succeed in AWS regions but fail in GCP regions. + +<table className="vespa-table"> + <thead> + <tr> + <th>Constraint</th> + <th>AWS</th> + <th>Azure</th> + <th>GCP</th> + </tr> + </thead> + <tbody> + <tr> + <td>Key characters</td> + <td><code>{`[a-zA-Z0-9 +-=._:/@]`}</code></td> + <td>Unicode, except <code>{`< > % & \\ ? /`}</code></td> + <td><code>{`[a-z][a-z0-9_-]*`}</code> (must start with lowercase letter)</td> + </tr> + <tr> + <td>Value characters</td> + <td><code>{`[a-zA-Z0-9 +-=._:/@]`}</code></td> + <td>No restrictions</td> + <td><code>{`[a-z0-9_-]*`}</code></td> + </tr> + <tr> + <td>Key max length</td> + <td>128</td> + <td>512</td> + <td>63</td> + </tr> + <tr> + <td>Value max length</td> + <td>256</td> + <td>256</td> + <td>63</td> + </tr> + <tr> + <td>Max tags</td> + <td>50</td> + <td>50</td> + <td>64</td> + </tr> + </tbody> +</table> + +**Template variables.** Tag values may reference the following template variables. Resolved values are always lowercased regardless of cloud. Template-variable placeholders are excluded when checking per-cloud character rules, so only the literal parts of the value are validated. Referencing an unknown variable causes the deployment to fail. Variables can be combined, e.g. `value="${environment}-${clustertype}"`. + +<table className="vespa-table"> + <thead> + <tr> + <th>Variable</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td><code>{`\${tenant}`}</code></td> + <td>The tenant name, e.g. <code>{`mytenant`}</code>.</td> + </tr> + <tr> + <td><code>{`\${application}`}</code></td> + <td>The application name, e.g. <code>{`myapp`}</code>.</td> + </tr> + <tr> + <td><code>{`\${instance}`}</code></td> + <td>The instance name, e.g. <code>{`default`}</code>, <code>{`beta`}</code>.</td> + </tr> + <tr> + <td><code>{`\${environment}`}</code></td> + <td>The deployment environment, e.g. <code>{`prod`}</code>, <code>{`dev`}</code>.</td> + </tr> + <tr> + <td><code>{`\${region}`}</code></td> + <td>The deployment region, e.g. <code>{`aws-us-east-1c`}</code>.</td> + </tr> + <tr> + <td><code>{`\${clustername}`}</code></td> + <td>The cluster ID from <a href="/en/reference/applications/services/services" aria-label="services.xml">services.xml</a>, e.g. <code>{`default`}</code>, <code>{`music`}</code>.</td> + </tr> + <tr> + <td><code>{`\${clustertype}`}</code></td> + <td>The Vespa cluster type: <code>{`container`}</code>, <code>{`content`}</code>, or <code>{`admin`}</code>.</td> + </tr> + </tbody> +</table> + +**Reconciliation.** Tags are applied to virtual machines and attached disks. When tags are changed, added, or removed in *deployment.xml*, the existing resources are updated by a background reconciliation process. Tags that were previously applied by Vespa Cloud but are no longer listed are removed from the resources. Tags added manually by the tenant in the cloud console are preserved. + +## upgrade + +In `<deployment>`, or `<instance>`. Determines the strategy for upgrading the application, or one of its instances. By default, application revision changes deploy independently of platform upgrades, and an application revision can catch up to and pass an ongoing platform upgrade. See the `rollout` attribute below to change this behavior. + +<table className="vespa-table"> + <thead> + <tr> + <th>Attribute</th> + <th>Mandatory</th> + <th>Values</th> + </tr> + </thead> + <tbody> + <tr> + <td>rollout</td> + <td>No, default <code>{`simultaneous`}</code></td> + <td>• <code>{`separate`}</code>: When a revision catches up to a platform upgrade, it stays behind, unless the upgrade alone fails.<br/><br/> • <code>{`leading`}</code>: When a revision catches up to a platform upgrade, they fuse and roll out together.<br/><br/>• <code>{`simultaneous`}</code> is the default, and favors revision roll-out. Revision changes deploy independently of platform upgrades. When a revision catches up to a platform upgrade, it joins, and then passes the upgrade.</td> + </tr> + <tr> + <td>revision-target</td> + <td>No, default <code>{`latest`}</code></td> + <td>• <code>{`latest`}</code> is the default. When rolling out a new revision to an instance, the latest available revision is chosen.<br/><br/> • <code>{`next`}</code> trades speed for smaller changes. When rolling out a new revision to an instance, the next available revision is chosen.<br/><br/> The available revisions for an instance are revisions which are not yet deployed, or revisions which have rolled out in previous instances.</td> + </tr> + <tr> + <td>revision-change</td> + <td>No, default <code>{`when-failing`}</code></td> + <td>• <code>{`always`}</code> is the most aggressive setting. A new, available revision may always replace the one which is currently rolling out.<br/><br/> • <code>{`when-failing`}</code> is the default. A new, available revision may replace the one which is currently rolling out if this is failing.<br/><br/> • <code>{`when-clear`}</code> is the most conservative setting. A new, available revision may never replace one which is currently rolling out.<br/><br/> Revision targets will never automatically change inside <a href="#block-change" aria-label="revision block window">revision block window</a>, but may be set by manual intervention at any time.</td> + </tr> + <tr> + <td>max-risk</td> + <td>No, default <code>{`0`}</code></td> + <td>May only be used with <code>{`revision-change="when-clear"`}</code> and <code>{`revision-target="next"`}</code>. The maximum amount of *risk* to roll out per new revision target. The default of <code>{`0`}</code> results in the next build always being chosen, while a higher value allows skipping intermediate builds, as long as the cumulative risk does not exceed what is configured here.</td> + </tr> + <tr> + <td>min-risk</td> + <td>No, default <code>{`0`}</code></td> + <td>Must be less than or equal to the configured <code>{`max-risk`}</code>. The minimum amount of *risk* to start rolling out a new revision. The default of <code>{`0`}</code> results in a new revision rolling out as soon as anything is ready, while a higher value lets the system wait until enough cumulative risk is available. This can be used to avoid blocking a lengthy deployment process with trivial changes.</td> + </tr> + <tr> + <td>max-idle-hours</td> + <td>No, default <code>{`8`}</code></td> + <td>May only be used when <code>{`min-risk`}</code> is specified, and greater than <code>{`0`}</code>. The maximum number of hours to wait for enough cumulative risk to be available, before rolling out a new revision.</td> + </tr> + </tbody> +</table> + +## test + +Meaning depends on where it is located: + +<table className="vespa-table"> + <thead> + <tr> + <th>Parent</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td><code>{`<deployment>`}</code> <code>{`<instance>`}</code></td> + <td>If present, the application is deployed to the <a href="/en/operations/environments#test" aria-label="`test`"><code>{`test`}</code></a> environment, and system tested there, even if no prod zones are deployed to. Also, when specified, system tests *must* be present in the application test package. See guides for <a href="/en/operations/production-deployment" aria-label="getting to production">getting to production</a>. <br/><br/> If present in an <code>{`<instance>`}</code> element, system tests are run for that specific instance before any production deployments of the instance may proceed — otherwise, previous system tests for any instance are acceptable.</td> + </tr> + <tr> + <td><code>{`<prod>`}</code> <code>{`<parallel>`}</code> <code>{`<steps>`}</code></td> + <td>If present, production tests are run against the production region with id contained in this element. A test must be *after* a corresponding <a href="#region" aria-label="region">region</a> element. When specified, production tests *must* be preset in the application test package. See guides for <a href="/en/operations/production-deployment" aria-label="getting to production">getting to production</a>.</td> + </tr> + </tbody> +</table> + +<table className="vespa-table"> + <thead> + <tr> + <th>Attribute</th> + <th>Mandatory</th> + <th>Values</th> + </tr> + </thead> + <tbody> + <tr> + <td>cloud-account</td> + <td>No</td> + <td>For <a href="/en/operations/automated-deployments#system-tests" aria-label="system tests">system tests</a> only: account to deploy to with <a href="/en/operations/enclave/enclave" aria-label="Vespa Cloud Enclave">Vespa Cloud Enclave</a>. Overrides parent's use of cloud-account. Cloud account *must not* be specified for <a href="/en/operations/automated-deployments#production-tests" aria-label="production tests">production tests</a>, which always run in the account of the corresponding deployment.</td> + </tr> + </tbody> +</table> + +## staging + +In `<deployment>`, or `<instance>`. If present, the application is deployed to the [`staging`](/en/operations/environments#staging) environment, and tested there, even if no prod zones are deployed to. If present in an `<instance>` element, staging tests are run for that specific instance before any production deployments of the instance may proceed — otherwise, previous staging tests for any instance are acceptable. When specified, staging tests *must* be preset in the application test package. See guides for [getting to production](/en/operations/production-deployment). + +<table className="vespa-table"> + <thead> + <tr> + <th>Attribute</th> + <th>Mandatory</th> + <th>Values</th> + </tr> + </thead> + <tbody> + <tr> + <td>cloud-account</td> + <td>No</td> + <td>Account to deploy to with <a href="/en/operations/enclave/enclave" aria-label="Vespa Cloud Enclave">Vespa Cloud Enclave</a>. Overrides parent's use of cloud-account.</td> + </tr> + </tbody> +</table> + +## prod + +In `<deployment>`, **or** in `<instance>`. If present, the application is deployed to the production regions listed inside this element, under the specified instance, after deployments and tests in the `test` and `staging` environments. + +<table className="vespa-table"> + <thead> + <tr> + <th>Attribute</th> + <th>Mandatory</th> + <th>Values</th> + </tr> + </thead> + <tbody> + <tr> + <td>cloud-account</td> + <td>No</td> + <td>Account to deploy to with <a href="/en/operations/enclave/enclave" aria-label="Vespa Cloud Enclave">Vespa Cloud Enclave</a>. Overrides parent's use of cloud-account.</td> + </tr> + </tbody> +</table> + +## region + +In `<prod>`, `<parallel>`, `<steps>`, or `<group>`. The application is deployed to the production [region](/en/operations/zones) with id contained in this element. + +<table className="vespa-table"> + <thead> + <tr> + <th>Attribute</th> + <th>Mandatory</th> + <th>Values</th> + </tr> + </thead> + <tbody> + <tr> + <td>fraction</td> + <td>No</td> + <td>Only when this region is inside a group: The fractional membership in the group.</td> + </tr> + <tr> + <td>cloud-account</td> + <td>No</td> + <td>Account to deploy to with <a href="/en/operations/enclave/enclave" aria-label="Enclave">Enclave</a>. Overrides parent's use of cloud-account.</td> + </tr> + </tbody> +</table> + +## dev + +In `<deployment>`. Optionally used to control deployment settings for the [dev environment](/en/operations/environments). This can be used specify a different cloud account, tags, and private endpoints. + +<table className="vespa-table"> + <thead> + <tr> + <th>Attribute</th> + <th>Mandatory</th> + <th>Values</th> + </tr> + </thead> + <tbody> + <tr> + <td>tags</td> + <td>No</td> + <td>Space-separated tags which can be referenced to make <a href="/en/operations/deployment-variants" aria-label="deployment variants">deployment variants</a>.</td> + </tr> + <tr> + <td>cloud-account</td> + <td>No</td> + <td>Account to deploy to with <a href="/en/operations/enclave/enclave" aria-label="Vespa Cloud Enclave">Vespa Cloud Enclave</a>. Overrides parent's use of cloud-account.</td> + </tr> + </tbody> +</table> + +## delay + +In `<deployment>`, `<instance>`, `<prod>`, `<parallel>`, or `<steps>`. Introduces a delay which must pass after completion of all previous steps, before subsequent steps may proceed. This may be useful to allow some grace time to discover errors before deploying a change in additional zones, or to gather higher-level metrics for a production deployment for a while, before evaluating these in a production test. The maximum total delay for the whole deployment spec is 48 hours. The delay is specified by any combination of the `hours`, `minutes` and `seconds` attributes. + +## parallel + +In `<deployment>`, `<prod>`, or `<steps>`. Runs the contained steps in parallel: instances if in `<deployment>`, or primitive steps (deployments, tests or delays) or a series of these (see [steps](#steps)) otherwise. Multiple `<parallel>` elements are permitted. The following example will deploy to `us-west-1` first, then to `us-east-3` and `us-central-1` simultaneously, and, finally to `eu-west-1`, once both parallel deployments have completed: + +```xml +<region>us-west-1</region> +<parallel> + <region>us-east-3</region> + <region>us-central-1</region> +</parallel> +<region>eu-west-1</region> +``` + +## steps + +In `<parallel>`. Runs the contained parallel or primitive steps (deployments, tests or delays) serially. The following example will in parallel: + +<Steps> +<Step> +deploy to `us-east-3`, +</Step> +<Step> +deploy to `us-west-1`, then delay 1 hour, and run tests for `us-west-1`, and +</Step> +<Step> +delay for two hours. +</Step> +</Steps> + +Thus, the parallel block is complete when both deployments are complete, tests are successful for the second deployment, and at least two hours have passed since the block began executing. + +```xml +<parallel> + <region>us-east-3</region> + <steps> + <region>us-west-1</region> + <delay hours="1" /> + <test>us-west-1</test> + </steps> + <delay hours="2" /> +</parallel> +``` + +## tester + +In `<test>`, `<staging>` and `<prod>`. Specifies container settings for the tester application container, which is used to run system, staging and production verification tests. + +The allowed elements inside this are [`<nodes>`](/en/reference/applications/services/services#nodes). + +```xml +<staging> + <tester> + <nodes count="1"> + <resources vcpu="8" memory="32Gb" disk="30Gb" /> + </nodes> + </tester> +</staging> +``` + +## endpoints (global) + +In `<deployment>`, without any `<instance>` declared **or** in `<instance>`: This allows *global* endpoints, via one or more [`<endpoint>`](#endpoint-global) elements; and [zone endpoint](#endpoint-zone) and [private endpoint](#endpoint-private) elements for cloud-native private network configuration. + +## endpoints (dev) + +In `<dev>`. This allows [zone endpoint](#endpoint-zone) elements for cloud-native private network configuration for [dev](/en/operations/environments#dev) deployments. Note that [private endpoints](#endpoint-private) are only supported in `prod`. + +## endpoint (global) + +In `<endpoints>` or `<group>`. Specifies a global endpoint for this application. Each endpoint will point to the regions that are declared in the endpoint. If no regions are specified, the endpoint defaults to the regions declared in the `<prod>` element. The following example creates a default endpoint to all regions, and a *us* endpoint pointing only to US regions. + +```xml +<endpoints> + <endpoint container-id="my-container-service"/> + <endpoint id="us" container-id="my-container-service"> + <region>aws-us-east-1c</region> + <region>aws-us-west-2a</region> + </endpoint> +</endpoints> +``` + +<table className="vespa-table"> + <thead> + <tr> + <th>Attribute</th> + <th>Mandatory</th> + <th>Values</th> + </tr> + </thead> + <tbody> + <tr> + <td>id</td> + <td>No</td> + <td>The identifier for the endpoint. This will be part of the endpoint name that is generated. If not specified, the endpoint will be the default global endpoint for the application.</td> + </tr> + <tr> + <td>container-id</td> + <td>Yes</td> + <td>The id of the <a href="/en/reference/applications/services/container" aria-label="container cluster">container cluster</a> to which requests to the global endpoint is forwarded.</td> + </tr> + </tbody> +</table> + +Global endpoints are implemented using Route 53 and healthchecks, to keep active zones in rotation. See [BCP](#bcp) for advanced configurations. + +## endpoint (zone) + +In `<endpoints>` or `<group>`, with `type='zone'`. Used to disable public zone endpoints. *Non-public endpoints can not be used in global endpoints, which require that all constituent endpoints are public.* The example disables the public zone endpoint for the `my-container` container cluster in all regions, except where it is explicitly enabled, in `region-1`. Changing endpoint visibility will make the service unavailable for a short period of time. + +```xml +<endpoints> + <endpoint type='zone' container-id='my-container' enabled='false' /> + <endpoint type='zone' container-id='my-container' enabled='true'> + <region>region-1</region> + </endpoint> +</endpoints> +``` + +<table className="vespa-table"> + <thead> + <tr> + <th>Attribute</th> + <th>Mandatory</th> + <th>Values</th> + </tr> + </thead> + <tbody> + <tr> + <td>type</td> + <td>Yes</td> + <td>Private endpoints are specified with <code>{`type='zone'`}</code>.</td> + </tr> + <tr> + <td>container-id</td> + <td>Yes</td> + <td>The id of the <a href="/en/reference/applications/services/container" aria-label="container cluster">container cluster</a> to disable public endpoints for.</td> + </tr> + <tr> + <td>enabled</td> + <td>No</td> + <td>Whether a public endpoint for this container cluster should be enabled; default <code>{`true`}</code>.</td> + </tr> + </tbody> +</table> + +## endpoint (private) + +In `<endpoints>` or `<group>`, with `type='private'`. Specifies a private endpoint service for this application. Each service will be launched in the regions that are declared in the endpoint. If no regions are specified, the service is launched in all regions declared in the `<prod>` element, that support any of the declared [access types](#allow). The following example creates a private endpoint in two specific regions. + +```xml +<endpoints> + <endpoint type='private' container-id='my-container'> + <region>aws-us-east-1c</region> + <allow with='aws-private-link' arn='arn:aws:iam::123123123123:root' /> + </endpoint> + <endpoint type='private' container-id='my-container'> + <region>gcp-us-central1-f</region> + <allow with='gcp-service-connect' project='user-project' /> + </endpoint> +</endpoints> +``` + +<table className="vespa-table"> + <thead> + <tr> + <th>Attribute</th> + <th>Mandatory</th> + <th>Values</th> + </tr> + </thead> + <tbody> + <tr> + <td>type</td> + <td>Yes</td> + <td>Private endpoints are specified with <code>{`type='private'`}</code>.</td> + </tr> + <tr> + <td>container-id</td> + <td>Yes</td> + <td>The id of the <a href="/en/reference/applications/services/container" aria-label="container cluster">container cluster</a> to which requests to the private endpoint service is forwarded.</td> + </tr> + <tr> + <td>auth-method</td> + <td>No</td> + <td>The authentication method to use with this <a href="/en/operations/private-endpoints" aria-label="private endpoint">private endpoint</a>. Must be either <code>{`mtls`}</code> or <code>{`token`}</code>. Defaults to mTLS if not included.</td> + </tr> + </tbody> +</table> + +## allow + +In `<endpoint type='private'>`. Allows a principal identified by the URN to set up a connection to the declared private endpoint service. This element must be repeated for each additional URN. An endpoint service will only consider allowed URNs of a compatible type, and will only be created if at least one compatible access type-and-URN is given: + +- For AWS deployments, specify `aws-private-link`, and an *ARN*. +- For GCP deployments, specify `gcp-service-connect`, and a *project ID* + +```xml +<endpoint type='private' container-id="my-container"> + <allow with='aws-private-link' arn='arn:aws:iam::123123123123:root' /> + <allow with='aws-private-link' arn='arn:aws:iam::321321321321:role/my-role' /> + <allow with='aws-private-link' arn='arn:aws:iam::321321321321:user/my-user' /> + <allow with='gcp-service-connect' project='my-project' /> +</endpoint> +``` + +<table className="vespa-table"> + <thead> + <tr> + <th>Attribute</th> + <th>Mandatory</th> + <th>Values</th> + </tr> + </thead> + <tbody> + <tr> + <td>with</td> + <td>Yes</td> + <td>The private endpoint access type; must be <code>{`aws-private-link`}</code> or <code>{`gcp-service-connect`}</code>.</td> + </tr> + <tr> + <td>arn</td> + <td>Maybe</td> + <td>Must be specified with <code>{`aws-private-link`}</code>. See <a href="https://docs.aws.amazon.com/vpc/latest/privatelink/configure-endpoint-service.html" aria-label="AWS documentation">AWS documentation</a> for more details.</td> + </tr> + <tr> + <td>project</td> + <td>Maybe</td> + <td>Must be specified with <code>{`gcp-service-connect`}</code>. See <a href="https://cloud.google.com/vpc/docs/configure-private-service-connect-services" aria-label="GCP documentation">GCP documentation</a> for more details.</td> + </tr> + </tbody> +</table> + +## bcp + +In `<instance>` or `<deployment>`. Defines the BCP (Business Continuity Planning) structure of this instance: Which zones should take over for which others during the outage of a zone and how fast they must have the capacity ready. Autoscaling uses this information to decide the ideal cpu load of a zone. If this element is not defined, it is assumed that all regions covers for an equal share of the traffic of all other regions and must have that capacity ready at all times. + +If a bcp element is specified at the root, and explicit instances are used, that bcp element becomes the default for all instances that does not contain a bcp element themselves. If a BCP element contains no group elements it will implicitly define a single group of all the regions of the instance in which it is used. + +See [BCP test](https://cloud.vespa.ai/en/reference/bcp-test.html?_gl=1*1lsxxnq*_gcl_au*ODE0ODM4MTI2LjE3Nzk3MjQ3OTY) for a procedure to verify that your BCP configuration is correct. + +<table className="vespa-table"> + <thead> + <tr> + <th>Attribute</th> + <th>Mandatory</th> + <th>Values</th> + </tr> + </thead> + <tbody> + <tr> + <td>deadline</td> + <td>No</td> + <td>The max time after a region becomes unreachable until the other regions in its BCP group must be able to handle the traffic of it, given as a number of minutes followed by 'm', 'h' or 'd' (for minutes, hours or days). The default deadline is 0: Regions must at all times have capacity to handle BCP traffic immediately.<br/><br/> By providing a deadline, autoscaling can avoid the cost of provisioning additional resources for BCP capacity if it predicts that it can grow to handle the traffic faster than the deadline in a given cluster.<br/><br/> This is the default deadline to be used for all groups that don't specify one themselves.</td> + </tr> + </tbody> +</table> + +Example: + +```xml +<bcp> + <group deadline="15m"> + <endpoint id="foo" container-id="bar"/> + <region>us-east1</region> + <region>us-east2</region> + <region fraction="0.5">us-central1</region> + </group> + <group> + <region>us-west1</region> + <region>us-west2</region> + <region fraction="0.5">us-central1</region> + </group> +</bcp> +``` + +## group + +In `<bcp>`. Defines a bcp group: A set of regions whose members cover for each other during a regional outage. + +Each region in a group will (as allowed, when autoscaling ranges are configured) provision resources sufficient to handle that any other single region in the group goes down. The traffic of the region is assumed to be rerouted in equal amount to the remaining regions in the group. That is, if a group has one member, no resources will be provisioned to handle an outage in that member. If a group has two members, each will aim to provision sufficient resources to handle the actual traffic of the other. If a group has three members, each will provision to handle half of the traffic observed in the region among the two others which receives the most traffic. + +A region may have fractional membership in multiple groups, meaning it will handle just that fraction of the traffic of the remaining members, and vice versa. A regions total membership among groups must always sum to exactly 1. + +A group may also define global endpoints for the region members in the group. This is exactly the same as defining the endpoint separately and repeating the regions of the group under the endpoint. Endpoints under a group cannot contain explicit region sub-elements. + +<table className="vespa-table"> + <thead> + <tr> + <th>Attribute</th> + <th>Mandatory</th> + <th>Values</th> + </tr> + </thead> + <tbody> + <tr> + <td>deadline</td> + <td>No</td> + <td>The deadline of this BCP group. See deadline on the BCP element.</td> + </tr> + </tbody> +</table> diff --git a/en/reference/applications/hosts.html b/en/reference/applications/hosts.html deleted file mode 100644 index 7c9c5ea41e..0000000000 --- a/en/reference/applications/hosts.html +++ /dev/null @@ -1,50 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "hosts.xml" -applies_to: self-managed -redirect_from: -- en/reference/hosts.html ---- - -<p> - <em>hosts.xml</em> is a configuration file in an - <a href="application-packages.html">application package</a>. Elements: -</p> -<pre class="pre-config"> -hosts - <a href="#host">host [name]</a> - <a href="#alias">alias</a> -</pre> -<p> - The purpose of <em>hosts.xml</em> is to add aliases for real hostnames to self-defined aliases. - The aliases are used in <a href="services/services.html">services.xml</a> to map service instances to hosts. - It is only needed when deploying to multiple hosts. -</p> - - - -<h2 id="host">host</h2> -<p>Sub-elements:</p> -<ul> - <li><a href="#alias"><code>alias</code></a></li> -</ul> -<p>Example:</p> -<pre>{% highlight xml %} -<hosts> - <host name="myserver0.mydomain.com"> - <alias>SEARCH0</alias> - <alias>CONTAINER0</alias> - </host> - <host name="myserver1.mydomain.com"> - <alias>SEARCH1</alias> - <alias>CONTAINER1</alias> - </host> -</hosts> -{% endhighlight %}</pre> - - - -<h2 id="alias">alias</h2> -<p> -Alias used in <a href="services/services.html">services.xml</a> to refer to the host. -</p> diff --git a/en/reference/applications/hosts.mdx b/en/reference/applications/hosts.mdx new file mode 100644 index 0000000000..ba0c11b38a --- /dev/null +++ b/en/reference/applications/hosts.mdx @@ -0,0 +1,37 @@ +--- +title: "hosts.xml" +--- + + +*hosts.xml* is a configuration file in an [application package](/en/reference/applications/application-packages). Elements: + +hosts + [host \[name\]](#host) + [alias](#alias) + +The purpose of *hosts.xml* is to add aliases for real hostnames to self-defined aliases. The aliases are used in [services.xml](/en/reference/applications/services/services) to map service instances to hosts. It is only needed when deploying to multiple hosts. + +## host + +Sub-elements: + +- [`alias`](#alias) + +Example: + +```xml +<hosts> + <host name="myserver0.mydomain.com"> + <alias>SEARCH0</alias> + <alias>CONTAINER0</alias> + </host> + <host name="myserver1.mydomain.com"> + <alias>SEARCH1</alias> + <alias>CONTAINER1</alias> + </host> +</hosts> +``` + +## alias + +Alias used in [services.xml](/en/reference/applications/services/services) to refer to the host. diff --git a/en/reference/applications/services/admin.html b/en/reference/applications/services/admin.html deleted file mode 100644 index 4848daa2d0..0000000000 --- a/en/reference/applications/services/admin.html +++ /dev/null @@ -1,465 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "services.xml - 'admin'" -redirect_from: -- /en/reference/services-admin.html ---- - -<!-- ToDo: most of this is for self-hosted only, except maybe metrics stuff --> -<p> - Reference documentation for <code><admin></code> in <a href="services.html">services.xml</a>. - Find a working example of this configuration in the sample application <em>multinode-HA</em> - <a href="https://github.com/vespa-engine/sample-apps/blob/master/examples/operations/multinode-HA/services.xml"> - services.xml</a>. -</p> -<pre class="pre-config"> -admin [version] - <a href="#adminserver">adminserver [hostalias]</a> - <a href="#cluster-controllers">cluster-controllers</a> - <a href="#cluster-controller">cluster-controller [hostalias, baseport, jvm-options, jvm-gc-options]</a> - <a href="#configservers">configservers</a> - <a href="#configserver">configserver [hostalias, baseport]</a> - <a href="#logserver">logserver [jvm-options, jvm-gc-options]</a> - <a href="#slobroks">slobroks</a> - <a href="#slobrok">slobrok [hostalias, baseport]</a> - <a href="#monitoring">monitoring [systemname<!--, interval (currently unused)-->]</a> - <a href="#metrics">metrics</a> - <a href="#consumer">consumer [id]</a> - <a href="#metric-set">metric-set [id]</a> - <a href="#metric">metric [id]</a> - <a href="#cloudwatch">cloudwatch [region, namespace]</a> - <a href="#shared-credentials">shared-credentials [file, profile]</a> - <a href="#logging">logging</a> -</pre> -<table class="table"> - <thead> - <tr><th>Attribute</th><th>Required</th><th>Value</th><th>Default</th><th>Description</th></tr> - </thead><tbody> - <tr><th>version</th> - <td>required</td> - <td>number</td> - <td></td> - <td><p id="admin.version">2.0</p></td></tr> - </tbody> -</table> - - - -<h2 id="adminserver">adminserver</h2> -<p> -The configured node will be the default administration node in your Vespa system, -which means that unless configured otherwise all administrative services - -i.e. the log server, the configuration server, the slobrok, and so on - will run on this node. -Use <a href="#configservers">configservers</a>, <a href="#logserver">logserver</a>, -<a href="#slobroks">slobroks</a> elements if you need to specify baseport or jvm options for any -of these services. -</p> -<table class="table"> - <thead> - <tr><th>Attribute</th><th>Required</th><th>Value</th><th>Default</th><th>Description</th></tr> - </thead><tbody> - <tr><th>hostalias</th> - <td>required</td> - <td>string</td> - <td></td> - <td><p id="adminserver.hostalias"></p></td></tr> - <tr><th>baseport</th> - <td>optional</td> - <td>number</td> - <td></td> - <td><p id="adminserver.baseport"></p></td></tr> - </tbody> -</table> - - - -<h2 id="cluster-controllers">cluster-controllers</h2> -<p> -Container for one or more <a href="#cluster-controller">cluster-controller</a> elements. -When having one or more <a href="content.html">content</a> clusters, -configuring at least one cluster controller is required. -</p> -<!-- ToDo check / rewrite - one gets an implicit cluster controller, but maybe not if admin element is set ... --> -<table class="table"> - <thead> - <tr><th>Attribute</th><th>Required</th><th>Value</th><th>Default</th><th>Description</th></tr> - </thead><tbody> - <tr><th>standalone-zookeeper</th> - <td>optional</td> - <td>true/false</td> - <td>false</td> - <td> - <p id="standalone-zookeeper"> - Will by default share the ZooKeeper instance with configserver. - If configured to true a separate ZooKeeper instance will be configured - and started on the set of nodes where you run cluster controller on. - The set of cluster controllers nodes cannot overlap with the set of nodes where config server is running. - If this setting is changed from false to true in a running system, - all previous cluster state information will be lost as the underlying ZooKeeper changes. - Cluster controllers will re-discover the state, - but nodes that have been manually set as down will again be considered to be up. - </p> - </td></tr> - </tbody> -</table> - - - -<h2 id="cluster-controller">cluster-controller</h2> -<p> - Specifies a host on which to run the - <a href="../../../content/content-nodes.html#cluster-controller">Cluster Controller</a> service. - The Cluster Controller manages the state of the cluster in order to provide elasticity and failure detection. -</p> -<table class="table"> - <thead> - <tr><th>Attribute</th><th>Required</th><th>Value</th><th>Default</th><th>Description</th></tr> - </thead><tbody> - <tr><th>hostalias</th> - <td>required</td> - <td>string</td> - <td></td> - <td><p id="cluster-controller.hostalias"></p></td></tr> - <tr><th>baseport</th> - <td>optional</td> - <td>number</td> - <td></td> - <td><p id="cluster-controller.baseport"></p></td></tr> - <tr><th>jvm-options</th> - <td>optional</td> - <td>string</td> - <td></td> - <td><p id="cluster-controller.jvm-options"></p></td></tr> - </tbody> -</table> - - - -<h2 id="configservers">configservers</h2> -<p> -Container for one or more <code>configserver</code> elements. -</p> - - - -<h2 id="configserver">configserver</h2> -<p> - Specifies a host on which to run the - <a href="/en/operations/self-managed/configuration-server.html">Configuration Server</a> service. - If contained directly below <code><admin></code> you may only have one, - so if you need to configure multiple instances of this service, - contain them within the <a href="#configservers"><code><configservers></code></a> element. -</p> -<table class="table"> - <thead> - <tr><th>Attribute</th><th>Required</th><th>Value</th><th>Default</th><th>Description</th></tr> - </thead><tbody> - <tr><th>hostalias</th> - <td>required</td> - <td>string</td> - <td></td> - <td><p id="configserver.hostalias"></p></td></tr> - <tr><th>baseport</th> - <td>optional</td> - <td>number</td> - <td></td> - <td><p id="configserver.baseport"></p></td></tr> - </tbody> -</table> - - -<h2 id="logserver">logserver</h2> -<p> - Specifies a host on which to run the <a href="../../operations/log-files.html#log-server">Vespa Log Server</a> service. - If not specified, the logserver is placed on the <a href="#adminserver">adminserver</a>, - like in the - <a href='https://github.com/vespa-engine/sample-apps/blob/master/examples/operations/multinode-HA/services.xml'> - example</a>. -</p> -<table class="table"> - <thead> - <tr><th>Attribute</th><th>Required</th><th>Value</th><th>Default</th><th>Description</th></tr> - </thead><tbody> - <tr><th>hostalias</th> - <td>required</td> - <td>string</td> - <td></td> - <td><p id="logserver.hostalias"></p></td></tr> - <tr><th>baseport</th> - <td>optional</td> - <td>number</td> - <td></td> - <td><p id="logserver.baseport"></p></td></tr> - <tr><th>jvm-options</th> - <td>optional</td> - <td>string</td> - <td></td> - <td><p id="logserver.jvm-options"></p></td></tr> - <tr><th>jvm-gc-options</th> - <td>optional</td> - <td>string</td> - <td></td> - <td><p id="logserver.jvm-gc-options"></p></td></tr> - </tbody> -</table> -<p>Example:</p> -<pre>{% highlight xml %} -<logserver hostalias="node1" /> -{% endhighlight %}</pre> - - - -<h2 id="slobroks">slobroks</h2> -<p> -This is a container for one or more <code>slobrok</code> elements. -</p> - - - -<h2 id="slobrok">slobrok</h2> -<p> - Specifies a host on which to run the - <a href="/en/operations/self-managed/slobrok.html">Service Location Broker (slobrok)</a> service. -</p> -<table class="table"> - <thead> - <tr><th>Attribute</th><th>Required</th><th>Value</th><th>Default</th><th>Description</th></tr> - </thead><tbody> - <tr><th>hostalias</th> - <td>required</td> - <td>string</td> - <td></td> - <td><p id="slobrok.hostalias"></p></td></tr> - <tr><th>baseport</th> - <td>optional</td> - <td>number</td> - <td></td> - <td><p id="slobrok.baseport"></p></td></tr> - </tbody> -</table> - - - -<h2 id="monitoring">monitoring</h2> -<p> - Settings for how to pass metrics to a monitoring service - - see <a href="/en/operations/self-managed/monitoring.html">monitoring</a>. -</p> -<pre>{% highlight xml %} -<monitoring systemname="name-in-metrics-system" /> -{% endhighlight %}</pre> -<table class="table"> - <thead></thead><tbody> - <!-- <tr> - <th>interval</th> - <td> - Currently unused, (the rate at which metrics are passed to the monitoring system, in minutes) - </td> - </tr> --> - <tr> - <th>systemname</th> - <td> - The name of the application in question in the monitoring system, default is "vespa" - </td> - </tr> - </tbody> -</table> - - -<h2 id="logging">logging</h2> -<p> - Used for tuning log levels of Java plug-ins. - If you (temporarily) need to enable debug logging from some class - or package, or if some third-party component is spamming your log - with unnecessary INFO level messages, you can turn levels on or off. - Example: -</p> -<pre>{% highlight xml %} -<logging> - <class name="org.myorg.MyHelperClass" levels="+debug" /> - <class name="org.thirdparty.TooSpammyClass" levels="-info" /> - <package name="org.myorg.tricky.package" levels="all" /> - <package name="org.anotherorg" levels="all -info -debug -spam" /> -</logging> -{% endhighlight %}</pre> -<p> - Note that tuning also affects sub-packages, so the above would - also affect all packages with <code>org.anotherorg.</code> as prefix. - And if there is a <code>org.myorg.tricky.package.foo.InternalClass</code> - you will get even "spam" level logging from it! -</p> -<p> - The default for <code>levels</code> is <code>"all -debug -spam"</code> - and as seen above you can add and remove specific levels. -</p> - -<h2 id="metrics">metrics</h2> -<p> - Used for configuring the forwarding of metrics to graphing applications - - add <code>consumer</code> child elements. - Also see <a href="/en/operations/self-managed/monitoring.html">monitoring</a>. Example: -</p> -<pre>{% highlight xml %} -<metrics> - <consumer id="my-metric-namespace"> - <metric-set id="default" /> - <metric id="my-custom-metric" /> - <cloudwatch region="us-east-1" namespace="my-vespa"> - <shared-credentials file="/path/to/credentials-file" profile="default" /> - </cloudwatch> - </consumer> -</metrics> -{% endhighlight %}</pre> - - - -<h2 id="consumer">consumer</h2> -<p> - Configure a metrics consumer. - The metrics contained in this element will be exported to the consumer with the given id. - <code>consumer</code> is a request parameter in - <a href="../../api/metrics-v1.html">/metrics/v1/values</a>, - <a href="../../api/metrics-v2.html">/metrics/v2/values</a> and - <a href="../../api/prometheus-v1.html">/prometheus/v1/values</a>. -</p> -<p> - Add <code>metric</code> and/or <code>metric-set</code> children. -</p> -<table class="table"> - <thead> - <tr><th>Attribute</th><th>Required</th><th>Value</th><th>Default</th><th>Description</th></tr> - </thead><tbody> - <tr> - <th>id</th> - <td>required</td> - <td>string</td> - <td></td> - <td> - <p id="consumer.id">The name of the consumer to export metrics to.</p> - </td></tr> - </tbody> -</table> - - - -<h2 id="metric-set">metric-set</h2> -<p> -Include a pre-defined set of metrics to the consumer. -</p> -<table class="table"> - <thead> - <tr><th>Attribute</th><th>Required</th><th>Value</th><th>Default</th><th>Description</th></tr> - </thead><tbody> - <tr> - <th>id</th> - <td>required</td> - <td>string</td> - <td></td> - <td> - <p id="metric-set.id"> - The id of the metric set to include. - Built-in metric sets are: - </p> - <ul> - <li><code>default</code></li> - <li><code>Vespa</code></li> - </ul> - </td></tr> - </tbody> -</table> - - - -<h2 id="metric">metric</h2> -<p> -Configure a metric. -</p> -<table class="table"> - <thead> - <tr><th>Attribute</th><th>Required</th><th>Value</th><th>Default</th><th>Description</th></tr> - </thead><tbody> - <tr> - <th>id</th> - <td>required</td> - <td>string</td> - <td></td> - <td> - <p id="metric.id"> - The name of the metric as defined in custom code or in - <a href="../../api/state-v1.html#state-v1-metrics">process metrics api</a> - </p> - </td></tr> - </tbody> -</table> -<p> - Note that metric id needs to include the metric specific suffix, e.g. <em>.average</em>. -</p> -<p> - In this example, there is one metric added to a custom consumer in addition to the default metric set. - Use <em>&consumer=my-custom-consumer</em> parameter for the prometheus endpoint. - Also notice the .count suffix, see <a href="../../api/state-v1.html#state-v1-metrics">process metrics api</a>. -</p> -<p> - The per process metrics api endpoint <em>/state/v1/metrics</em> also includes a description of each emitted metric. - The <em>/state/v1/metrics</em> endpoint also includes the metric aggregates (.count, .average, .rate, .max). -</p> -<pre>{% highlight xml %} -<metrics> - <consumer id="my-custom-consumer"> - <metric-set id="default" /> - <metric id="vds.idealstate.garbage_collection.documents_removed.count" /> - </consumer> -</metrics> -{% endhighlight %}</pre> - - - -<h2 id="cloudwatch">cloudwatch</h2> -<p> - Specifies that the metrics from this consumer should be forwarded to CloudWatch. -</p> -<table class="table"> - <thead> - <tr><th>Attribute</th><th>Required</th><th>Value</th><th>Default</th><th>Description</th></tr> - </thead><tbody> -<tr><th>region</th> - <td>required</td> - <td>string</td> - <td></td> - <td><p id="region">Your AWS region</p></td></tr> -<tr><th>namespace</th> - <td>required</td> - <td>string</td> - <td></td> - <td><p id="namespace">The metrics namespace in CloudWatch</p></td></tr> -</tbody> -</table> -<p>Example:</p> -<pre>{% highlight xml %} -<cloudwatch region="us-east-1" namespace="my-vespa"> - <shared-credentials file="/path/to/credentials-file" profile="default" /> -</cloudwatch> -{% endhighlight %}</pre> - - - -<h2 id="shared-credentials">shared-credentials</h2> -<p> - Specifies that a profile from a shared-credentials file should be used for authentication to CloudWatch. -</p> -<table class="table"> - <thead> - <tr><th>Attribute</th><th>Required</th><th>Value</th><th>Default</th><th>Description</th></tr> - </thead><tbody> -<tr><th>file</th> - <td>required</td> - <td>string</td> - <td></td> - <td><p id="file">The path to the shared-credentials file</p></td></tr> -<tr><th>profile</th> - <td>optional</td> - <td>string</td> - <td>default</td> - <td><p id="profile">The profile in the shared-credentials file</p></td></tr> -</tbody> -</table> diff --git a/en/reference/applications/services/admin.mdx b/en/reference/applications/services/admin.mdx new file mode 100644 index 0000000000..7a2bed3ca9 --- /dev/null +++ b/en/reference/applications/services/admin.mdx @@ -0,0 +1,488 @@ +--- +title: "services.xml - 'admin'" +sidebarTitle: "services.xml" +--- + +Reference documentation for `<admin>` in [services.xml](/en/reference/applications/services/services). Find a working example of this configuration in the sample application *multinode-HA* [services.xml](https://github.com/vespa-engine/sample-apps/blob/master/examples/operations/multinode-HA/services.xml). + +```yaml +admin [version] + adminserver [hostalias] + cluster-controllers + cluster-controller [hostalias, baseport, jvm-options, jvm-gc-options] + configservers + configserver [hostalias, baseport] + logserver [jvm-options, jvm-gc-options] + slobroks + slobrok [hostalias, baseport] + monitoring [systemname] + metrics + consumer [id] + metric-set [id] + metric [id] + cloudwatch [region, namespace] + shared-credentials [file, profile] + logging +``` + +<table className="vespa-table"> + <thead> + <tr> + <th>Attribute</th> + <th>Required</th> + <th>Value</th> + <th>Default</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>**version**</td> + <td>required</td> + <td>number</td> + <td></td> + <td>2.0</td> + </tr> + </tbody> +</table> + +## adminserver + +The configured node will be the default administration node in your Vespa system, which means that unless configured otherwise all administrative services - i.e. the log server, the configuration server, the slobrok, and so on - will run on this node. Use [configservers](#configservers), [logserver](#logserver), [slobroks](#slobroks) elements if you need to specify baseport or jvm options for any of these services. + +<table className="vespa-table"> + <thead> + <tr> + <th>Attribute</th> + <th>Required</th> + <th>Value</th> + <th>Default</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>**hostalias**</td> + <td>required</td> + <td>string</td> + <td></td> + <td></td> + </tr> + <tr> + <td>**baseport**</td> + <td>optional</td> + <td>number</td> + <td></td> + <td></td> + </tr> + </tbody> +</table> + +## cluster-controllers + +Container for one or more [cluster-controller](#cluster-controller) elements. When having one or more [content](/en/reference/applications/services/content) clusters, configuring at least one cluster controller is required. + +<table className="vespa-table"> + <thead> + <tr> + <th>Attribute</th> + <th>Required</th> + <th>Value</th> + <th>Default</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>**standalone-zookeeper**</td> + <td>optional</td> + <td>true/false</td> + <td>false</td> + <td>Will by default share the ZooKeeper instance with configserver. If configured to true a separate ZooKeeper instance will be configured and started on the set of nodes where you run cluster controller on. The set of cluster controllers nodes cannot overlap with the set of nodes where config server is running. If this setting is changed from false to true in a running system, all previous cluster state information will be lost as the underlying ZooKeeper changes. Cluster controllers will re-discover the state, but nodes that have been manually set as down will again be considered to be up.</td> + </tr> + </tbody> +</table> + +## cluster-controller + +Specifies a host on which to run the [Cluster Controller](/en/content/content-nodes#cluster-controller) service. The Cluster Controller manages the state of the cluster in order to provide elasticity and failure detection. + +<table className="vespa-table"> + <thead> + <tr> + <th>Attribute</th> + <th>Required</th> + <th>Value</th> + <th>Default</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>**hostalias**</td> + <td>required</td> + <td>string</td> + <td></td> + <td></td> + </tr> + <tr> + <td>**baseport**</td> + <td>optional</td> + <td>number</td> + <td></td> + <td></td> + </tr> + <tr> + <td>**jvm-options**</td> + <td>optional</td> + <td>string</td> + <td></td> + <td></td> + </tr> + </tbody> +</table> + +## configservers + +Container for one or more `configserver` elements. + +## configserver + +Specifies a host on which to run the [Configuration Server](/en/operations/self-managed/configuration-server) service. If contained directly below `<admin>` you may only have one, so if you need to configure multiple instances of this service, contain them within the [`<configservers>`](#configservers) element. + +<table className="vespa-table"> + <thead> + <tr> + <th>Attribute</th> + <th>Required</th> + <th>Value</th> + <th>Default</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>**hostalias**</td> + <td>required</td> + <td>string</td> + <td></td> + <td></td> + </tr> + <tr> + <td>**baseport**</td> + <td>optional</td> + <td>number</td> + <td></td> + <td></td> + </tr> + </tbody> +</table> + +## logserver + +Specifies a host on which to run the [Vespa Log Server](/en/reference/operations/log-files#log-server) service. If not specified, the logserver is placed on the [adminserver](#adminserver), like in the [example](https://github.com/vespa-engine/sample-apps/blob/master/examples/operations/multinode-HA/services.xml). + +<table className="vespa-table"> + <thead> + <tr> + <th>Attribute</th> + <th>Required</th> + <th>Value</th> + <th>Default</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>**hostalias**</td> + <td>required</td> + <td>string</td> + <td></td> + <td></td> + </tr> + <tr> + <td>**baseport**</td> + <td>optional</td> + <td>number</td> + <td></td> + <td></td> + </tr> + <tr> + <td>**jvm-options**</td> + <td>optional</td> + <td>string</td> + <td></td> + <td></td> + </tr> + <tr> + <td>**jvm-gc-options**</td> + <td>optional</td> + <td>string</td> + <td></td> + <td></td> + </tr> + </tbody> +</table> + +Example: + +```xml +<logserver hostalias="node1" /> +``` + +## slobroks + +This is a container for one or more `slobrok` elements. + +## slobrok + +Specifies a host on which to run the [Service Location Broker (slobrok)](/en/operations/self-managed/slobrok) service. + +<table className="vespa-table"> + <thead> + <tr> + <th>Attribute</th> + <th>Required</th> + <th>Value</th> + <th>Default</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>**hostalias**</td> + <td>required</td> + <td>string</td> + <td></td> + <td></td> + </tr> + <tr> + <td>**baseport**</td> + <td>optional</td> + <td>number</td> + <td></td> + <td></td> + </tr> + </tbody> +</table> + +## monitoring + +Settings for how to pass metrics to a monitoring service - see [monitoring](/en/operations/self-managed/monitoring). + +```xml +<monitoring systemname="name-in-metrics-system" /> +``` + +<table className="vespa-table"> + <thead> + <tr> + <th></th> + <th></th> + </tr> + </thead> + <tbody> + <tr> + <td>systemname</td> + <td>The name of the application in question in the monitoring system, default is "vespa"</td> + </tr> + </tbody> +</table> + + +## logging + +Used for tuning log levels of Java plug-ins. If you (temporarily) need to enable debug logging from some class or package, or if some third-party component is spamming your log with unnecessary INFO level messages, you can turn levels on or off. Example: + +```xml +<logging> + <class name="org.myorg.MyHelperClass" levels="+debug" /> + <class name="org.thirdparty.TooSpammyClass" levels="-info" /> + <package name="org.myorg.tricky.package" levels="all" /> + <package name="org.anotherorg" levels="all -info -debug -spam" /> +</logging> +``` + +Note that tuning also affects sub-packages, so the above would also affect all packages with `org.anotherorg.` as prefix. And if there is a `org.myorg.tricky.package.foo.InternalClass` you will get even "spam" level logging from it! + +The default for `levels` is `"all -debug -spam"` and as seen above you can add and remove specific levels. + +## metrics + +Used for configuring the forwarding of metrics to graphing applications - add `consumer` child elements. Also see [monitoring](/en/operations/self-managed/monitoring). Example: + +```xml +<metrics> + <consumer id="my-metric-namespace"> + <metric-set id="default" /> + <metric id="my-custom-metric" /> + <cloudwatch region="us-east-1" namespace="my-vespa"> + <shared-credentials file="/path/to/credentials-file" profile="default" /> + </cloudwatch> + </consumer> +</metrics> +``` + +## consumer + +Configure a metrics consumer. The metrics contained in this element will be exported to the consumer with the given id. `consumer` is a request parameter in [/metrics/v1/values](/en/reference/api/metrics-v1), [/metrics/v2/values](/en/reference/api/metrics-v2) and [/prometheus/v1/values](/en/reference/api/prometheus-v1). + +Add `metric` and/or `metric-set` children. + +<table className="vespa-table"> + <thead> + <tr> + <th>Attribute</th> + <th>Required</th> + <th>Value</th> + <th>Default</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>**id**</td> + <td>required</td> + <td>string</td> + <td></td> + <td>The name of the consumer to export metrics to.</td> + </tr> + </tbody> +</table> + +## metric-set + +Include a pre-defined set of metrics to the consumer. + +<table className="vespa-table"> + <thead> + <tr> + <th>Attribute</th> + <th>Required</th> + <th>Value</th> + <th>Default</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>**id**</td> + <td>required</td> + <td>string</td> + <td></td> + <td>The id of the metric set to include. Built-in metric sets are:<br/><br/> • <code>{`default`}</code><br/> • <code>{`Vespa`}</code></td> + </tr> + </tbody> +</table> + +## metric + +Configure a metric. + +<table className="vespa-table"> + <thead> + <tr> + <th>Attribute</th> + <th>Required</th> + <th>Value</th> + <th>Default</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>**id**</td> + <td>required</td> + <td>string</td> + <td></td> + <td>The name of the metric as defined in custom code or in <a href="/en/reference/api/state-v1#state-v1-metrics" aria-label="process metrics api">process metrics api</a></td> + </tr> + </tbody> +</table> + +Note that metric id needs to include the metric specific suffix, e.g. *.average*. + +In this example, there is one metric added to a custom consumer in addition to the default metric set. Use *&consumer=my-custom-consumer* parameter for the prometheus endpoint. Also notice the .count suffix, see [process metrics api](/en/reference/api/state-v1#state-v1-metrics). + +The per process metrics api endpoint */state/v1/metrics* also includes a description of each emitted metric. The */state/v1/metrics* endpoint also includes the metric aggregates (.count, .average, .rate, .max). + +```xml +<metrics> + <consumer id="my-custom-consumer"> + <metric-set id="default" /> + <metric id="vds.idealstate.garbage_collection.documents_removed.count" /> + </consumer> +</metrics> +``` + +## cloudwatch + +Specifies that the metrics from this consumer should be forwarded to CloudWatch. + +<table className="vespa-table"> + <thead> + <tr> + <th>Attribute</th> + <th>Required</th> + <th>Value</th> + <th>Default</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>**region**</td> + <td>required</td> + <td>string</td> + <td></td> + <td>Your AWS region</td> + </tr> + <tr> + <td>**namespace**</td> + <td>required</td> + <td>string</td> + <td></td> + <td>The metrics namespace in CloudWatch</td> + </tr> + </tbody> +</table> + +Example: + +```xml +<cloudwatch region="us-east-1" namespace="my-vespa"> + <shared-credentials file="/path/to/credentials-file" profile="default" /> +</cloudwatch> +``` + +## shared-credentials + +Specifies that a profile from a shared-credentials file should be used for authentication to CloudWatch. + +<table className="vespa-table"> + <thead> + <tr> + <th>Attribute</th> + <th>Required</th> + <th>Value</th> + <th>Default</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>**file**</td> + <td>required</td> + <td>string</td> + <td></td> + <td>The path to the shared-credentials file</td> + </tr> + <tr> + <td>**profile**</td> + <td>optional</td> + <td>string</td> + <td>default</td> + <td>The profile in the shared-credentials file</td> + </tr> + </tbody> +</table> diff --git a/en/reference/applications/services/container.html b/en/reference/applications/services/container.html deleted file mode 100644 index 10db23df49..0000000000 --- a/en/reference/applications/services/container.html +++ /dev/null @@ -1,933 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "services.xml - container" -redirect_from: -- /en/reference/services-container.html ---- - -<p>This is the reference for the container cluster configuration in <a href="services.html">services.xml</a>.</p> - -<pre class="pre-config"> -container [version, id] - <a href="http.html">http</a> - <a href="http.html#server">server [id, port]</a> - <a href="http.html#filtering">filtering</a> - <a href="#handler">handler [id, class, bundle]</a> - <a href="#binding">binding</a> - <a href="#component">component</a> - <a href="#server">server [id, class, bundle]</a> - <a href="#clients">clients</a> - <a href="#client">client [id, permissions]</a> - <a href="#certificate">certificate [file]</a> - <a href="#token">token [id]</a> - <a href="#components">components</a> - <a href="#component">component</a> - <a href="search.html">search</a> - <a href="#include">include [dir]</a> - <a href="search.html#binding">binding</a> - <a href="search.html#searcher">searcher</a> - <a href="search.html#federation">federation</a> - <a href="search.html#provider">provider</a> - <a href="processing.html#chain">chain</a> - <a href="search.html#renderer">renderer</a> - <a href="search.html#threadpool">threadpool</a> - <a href="search.html#significance">significance</a> - <a href="docproc.html">document-processing</a> - <a href="#include">include [dir]</a> - <a href="docproc.html#documentprocessor">documentprocessor</a> - <a href="processing.html#chain">chain</a> - <a href="docproc.html#threadpool">threadpool</a> - <a href="processing.html">processing</a> - <a href="#include">include [dir]</a> - <a href="processing.html#binding">binding</a> - <a href="processing.html#processor">processor</a> - <a href="processing.html#chain">chain</a> - <a href="processing.html#renderer">renderer</a> - <a href="#document-api">document-api</a> - <a href="#abortondocumenterror">abortondocumenterror</a> - <a href="#retryenabled">retryenabled</a> - <a href="#route">route</a> - <a href="#maxpendingdocs">maxpendingdocs</a> - <a href="#maxpendingbytes">maxpendingbytes</a> - <a href="#retrydelay">retrydelay</a> - <a href="#timeout">timeout</a> - <a href="#tracelevel">tracelevel</a> - <a href="#mbusport">mbusport</a> - <a href="#ignore-undefined-fields">ignore-undefined-fields</a> - <a href="#max-document-size">max-document-size</a> - <a href="../../../ranking/stateless-model-evaluation.html">model-evaluation</a> - <a href="../../../ranking/stateless-model-evaluation.html#onnx-inference-options">onnx</a> - <a href="#inference">inference</a> - <a href="#inference-memory">memory</a> - <a href="#document">document [type, class, bundle]</a> - <a href="#accesslog">accesslog [type, fileNamePattern, symlinkName, rotationInterval, rotationScheme]</a> - <a href="#request-content">request-content [samples-per-second, path-prefix, max-bytes]</a> - <a href="../config-files.html#generic-configuration-in-services-xml">config</a> - <a href="#nodes">nodes [count, allocated-memory, jvm-gc-options, jvm-options]</a> - <a href="services.html#resources">resources</a> (Vespa Cloud) - <a href="services.html#node">node [hostalias]</a> (Self managed) - <a href="#environment-variables">environment-variables</a> - <a href="#jvm">jvm [allocated-memory, options, gc-options]</a> - <a href="#secrets">secrets</a> - <a href="#secret-store">secret-store [type]</a> - <a href="#group">group [name, environment]</a> - <a href="#zookeeper">zookeeper</a> - <a href="#threadpool">threadpool</a> -</pre> - -<p><a href="../config-files.html#generic-configuration-in-services-xml">config</a> elements are also allowed most places.</p> - -<p>Example:</p> -<pre> - <container version="1.0" id="queryfeedendpoint"> - <search/> - <document-api/> - <nodes count="2"> - <resources vcpu="4" memory="16Gb" disk="50Gb"/> - </nodes> - </container> -</pre> - - -<h2><container></h2> - -<p>Contained in <a href="services.html"><services></a>. Each container tag specifies a separate container cluster.</p> - -<table class="table"> - <thead> - <tr><th>Attribute</th><th>Required</th><th>Value</th><th>Default</th><th>Description</th></tr> - </thead> - <tbody> - <tr> - <td>version</td> - <td>required</td> - <td>number</td> - <td></td> - <td><p id="container.version">1.0 in this version of Vespa</p></td></tr> - <tr> - <td>id</td> - <td>required</td> - <td>string</td> - <td></td> - <td><p id="container.id">the id of this cluster</p></td></tr> - </tbody> -</table> - - - -<h2 id="handler">handler</h2> -<p> -The <code>handler</code> element holds the configuration of a request handler. -For each <code>binding</code> tag, the handler will be bound -to the pertinent JDisc interfaces using the given binding. -</p> -<ul> - <li><code>binding</code> For JDisc request handlers, add this server binding to this handler.</li> - <li><a href="#component"><code>component</code></a> for injecting another component. - Must be a declaration of a new component, not a reference.</li> -</ul> -<table class="table"> - <thead> - <tr><th>Attribute</th><th>Required</th><th>Value</th><th>Default</th><th>Description</th></tr> - </thead><tbody> - <tr><th>id</th> - <td>required</td> - <td>string</td> - <td></td> - <td><p id="handler.id">The component ID</p></td></tr> - <tr><th>class</th> - <td>optional</td> - <td>string</td> - <td></td> - <td><p id="handler.class">The class of the handler, defaults to id</p></td></tr> - <tr><th>bundle</th> - <td>optional</td> - <td>string</td> - <td></td> - <td> - <p id="handler.bundle"> - The bundle to load the handler from: The name in <artifactId> in pom.xml. - Defaults to class or id (if no class is given) - </p> - </td></tr> - </tbody> -</table> -<p>Example:</p> -<pre> -<container id="default" version="1.0"> - <handler id="com.yahoo.search.handler.LegacyBridge"> - <binding>http://*/*</binding> - </handler> - <handler bundle="the name in <artifactId> in pom.xml" id="com.mydomain.vespatest.RedirectingHandler"/> - <handler bundle="the name in <artifactId> in pom.xml" id="com.mydomain.vespatest.ExampleHandler"/> - <nodes> - <node hostalias="node1"/> - </nodes> -</container> -</pre> - - - -<h2 id="binding">binding</h2> -<p> -The URI to map a Handler to. Multiple elements are allowed. See example above. -</p> - - - -<h2 id="server">server</h2> -<p> -The <code>server</code> element holds the configuration of a JDisc server provider. -</p> -<table class="table"> - <thead> - <tr><th>Attribute</th><th>Required</th><th>Value</th><th>Default</th><th>Description</th></tr> - </thead><tbody> - <tr><th>id</th> - <td>required</td> - <td>string</td> - <td></td> - <td><p id="server.id">The component ID</p></td></tr> - <tr><th>class</th> - <td>optional</td> - <td>string</td> - <td></td> - <td><p id="server.class">The class of the server, defaults to id</p></td></tr> - <tr><th>bundle</th> - <td>optional</td> - <td>string</td> - <td></td> - <td> - <p id="server.bundle"> - The bundle to load the server from: The name in <artifactId> in the pom.xml. - Defaults to class or id (if no class is given). - </p> - </td></tr> - </tbody> -</table> -Example: -<pre> -<server id="com.mydomain.vespatest.DemoServer"> - <config name="vespatest.demo-server"> - <response>Hello, world! - </response> - <port>16889</port> - </config> -</server> -</pre> - - - -<h2 id="clients">clients</h2> -<p> - Vespa Cloud only. - The <code>clients</code> element is a parent element for <a href="#client">client</a> security configuration. - Find details and practical examples in the <a href="../../../security/guide#configuring-tokens">security guide</a>. - Example: -</p> -<pre>{% highlight xml %} -<container id="query" version="1.0"> - <clients> - <client id="mtls" permissions="read"> - <certificate file="security/clients.pem"/> - </client> - <client id="query-token-client" permissions="read"> - <token id="query-token"/> - </client> - </clients> -</container> -{% endhighlight %}</pre> - - - -<h2 id="client">client</h2> -<p> - Vespa Cloud only. - Child element of <a href="#clients">clients</a>. - Use to configure security credentials for a container cluster, - using <a href="#certificate">certificate</a> and/or <a href="#token">token</a>. -</p> -<table class="table"> - <thead> - <tr><th>Attribute</th><th>Required</th><th>Value</th><th>Default</th><th>Description</th></tr> - </thead> - <tbody> - <tr> - <th>id</th> - <td>required</td> - <td>string</td> - <td></td> - <td><p id="client.id">The client ID</p></td> - </tr> - <tr> - <th>permissions</th> - <td>required</td> - <td>string</td> - <td></td> - <td> - <p id="client.permissions"> - Permissions, see the <a href="../../../security/guide#permissions">security guide</a>. - One of:</p> - <ul> - <li><code>read</code></li> - <li><code>write</code></li> - <li><code>read,write</code></li> - </ul> - </td> - </tr> -</tbody> -</table> - - -<h2 id="certificate">certificate</h2> -<p> - Vespa Cloud only. - Child element of <a href="#client">client</a>. - Configure certificates using the <em>file</em> attribute. -</p> -<table class="table"> - <thead> - <tr><th>Attribute</th><th>Required</th><th>Value</th><th>Default</th><th>Description</th></tr> - </thead> - <tbody> - <tr> - <th>file</th> - <td>required</td> - <td>string</td> - <td></td> - <td> - <p id="certificate.file"> - Path to the certificate file, - see the <a href="../../../security/guide#configuring-mtls">security guide</a>. - </p> - </td> - </tr> - </tbody> -</table> - - -<h2 id="token">token</h2> -<p> - Vespa Cloud only. - Child element of <a href="#client">client</a>. - Configure tokens using the <em>id</em> attribute. -</p> -<table class="table"> - <thead> - <tr><th>Attribute</th><th>Required</th><th>Value</th><th>Default</th><th>Description</th></tr> - </thead> - <tbody> - <tr> - <th>id</th> - <td>required</td> - <td>string</td> - <td></td> - <td> - <p id="token.id"> - Token ID, see the <a href="../../../security/guide#configuring-tokens">security guide</a>. - </p> - </td> - </tr> - </tbody> -</table> - - - -<h2 id="components">components</h2> -<p> -Contains <a href="#component">component</a> elements. -Can be used in conjunction with <a href="#include">include</a> for modular config of components. -</p> - - - -<h2 id="component">component</h2> -<p> -The <code>component</code> element holds the configuration of a -<a href="../../../applications/dependency-injection.html#special-components">generic component</a>. -Must be a declaration of a new component, not a reference. -</p> -<p> -Nested <a href="#component"><code>component</code></a> child elements can be added for injecting -specific component instances. This is useful if there is more than one declared component -of the same Java class. Refer to <a href="../../../applications/dependency-injection.html">Injecting -components</a> for details and examples. -</p> -<table class="table"> - <thead> - <tr><th>Attribute</th><th>Required</th><th>Value</th><th>Default</th><th>Description</th></tr> - </thead><tbody> - <tr><th>id</th> - <td>required</td> - <td>string</td> - <td></td> - <td><p id="component.id">The component ID</p></td></tr> - <tr><th>class</th> - <td>optional</td> - <td>string</td> - <td></td> - <td><p id="component.class">The class of the component, defaults to id</p></td></tr> - <tr><th>bundle</th> - <td>optional</td> - <td>string</td> - <td></td> - <td> - <p id="component.bundle"> - The bundle to load the component from: The name in <artifactId> in the pom.xml. - Defaults to class or id (if no class is given). - </p> - </td></tr> - </tbody> -</table> -<p>Example:</p> -<pre> -<component id="com.mydomain.demo.DemoComponent" bundle="the name in <artifactId> in pom.xml" /> -</pre> - - - -<h2 id="document-api">document-api</h2> -<p> -Use to enable <a href="../../api/api.html">Document API</a> operations to a container cluster. -Children elements: -</p> -<table class="table"> - <thead> - <tr><th>Name</th><th>Required</th><th>Value</th><th>Default</th><th>Description</th></tr> - </thead><tbody> - <tr><th>binding</th> - <td>optional</td> - <td>string</td> - <td>http://*/</td> - <td> - <p id="document-api-binding"> - The URI to map the document-api handlers to. Multiple bindings are valid. Must end with a '/'. - Note that each document-api handler will get its individual binding by adding a suffix, - e.g. the feed handler will add 'feed/', the remove handler will add 'remove/' and so on. Example: - </p> -<pre> -<document-api> - <binding>http://*/document-api/</binding> - <binding>https://*/document-api/</binding> -</document-api> -</pre> - With these configured bindings, the feed handler will be available at - <code>http://*/document-api/feed/</code> and <code>https://*/document-api/feed/</code>. - For other handlers, just replace 'feed/' with the appropriate suffix, e.g. 'get/', 'remove/' etc. - </td></tr> - <tr><th>abortondocumenterror</th> - <td>optional</td> - <td>true/false</td> - <td>true</td> - <td> - <p id="abortondocumenterror"> - Controls whether to abort the entire feed or not if a document-related error occurs, - i.e. if a document contains an unknown field. - Setting this field to <code>true</code> will abort the feed on such errors, - while setting it to <code>false</code> will cause Vespa to simply skip to the next document in the feed. - Note that malformed XML in the input will abort the feed regardless of this setting. - </p> - </td></tr> - <tr><th>maxpendingbytes</th> - <td>optional</td> - <td>number</td> - <td></td> - <td> - <p id="maxpendingbytes"> - The maximum number of pending bytes. - If <code><maxpendingdocs></code> is 0 and this is set to 0, this defaults to 100 MB. - If <code><maxpendingdocs></code> is more than 0, and this is set to 0, - the send-window is only limited by number of messages sent, not the memory footprint. - </p> - </td></tr> - <tr><th>maxpendingdocs</th> - <td>optional</td> - <td>number</td> - <td></td> - <td> - <p id="maxpendingdocs"> - The maximum number of pending documents the client can have. - By default, the client will dynamically adjust the window size - based on the latency of the performed operations. - If the parameter is set, dynamic window sizing will be turned off in favor of the configured value. - </p> - </td></tr> - <tr><th>mbusport</th> - <td>optional</td> - <td>number</td> - <td></td> - <td><p id="mbusport">Set the MessageBus port</p></td></tr> - <tr><th>retrydelay</th> - <td>optional</td> - <td>double</td> - <td>1.0</td> - <td><p id="retrydelay">Delay in seconds between retries</p></td></tr> - <tr><th>retryenabled</th> - <td>optional</td> - <td>true/false</td> - <td></td> - <td> - <p id="retryenabled"> - Enable or disable retrying documents that have failed. - </p> - <!-- ToDo: find defaults/values - probably true/false --> - </td></tr> - <tr><th>route</th> - <td>optional</td> - <td>string</td> - <td>default</td> - <td><p id="route">Set the route to feed documents to</p></td></tr> - <tr><th>timeout</th> - <td>optional</td> - <td>double</td> - <td>180.0</td> - <td><p id="timeout">Set the timeout value in seconds for an operation</p></td></tr> - <tr><th>tracelevel</th> - <td>optional</td> - <td>0-9</td> - <td>0</td> - <td> - <p id="tracelevel"> - Configure the level of which to trace messages sent. - The higher the level, the more detailed descriptions. - </p> - </td> - </tr> - <tr><th>ignore-undefined-fields</th> - <td>optional</td> - <td>true/false</td> - <td>false</td> - <td> - <p id="ignore-undefined-fields"> - Set to true to ignore undefined fields in document API operations and - let such operations complete successfully, rather than fail. - A <a href="../../api/document-v1.html#x-vespa-ignored-fields">response header is returned</a> - when field operations are ignored. - </p> - </td> - </tr> - <tr><th>max-document-size</th> - <td>optional</td> - <td>string</td> - <td>100MiB</td> - <td> - <p id="max-document-size"> - Specifies the maximum size of a document operation request accepted by the container, - measured as the uncompressed size of the request body. The limit applies to all document types - in the container cluster. A request larger than this limit will be rejected by the container - before the operation is forwarded to the content cluster. - </p> - <p> - Valid values are numbers including a unit (e.g. <em>10MiB</em>) and the value must be - between 1MiB and 2048MiB (inclusive). Values will be rounded to the nearest MiB, - so using MiB as a unit is preferable. - </p> - <p> - The value should normally not exceed the smallest - <a href="content.html#max-document-size">max-document-size</a> configured in any - content cluster that this container feeds to; a deployment warning is emitted otherwise. - </p> - <p>Example:</p> -<pre>{% highlight xml %} -<document-api> - <max-document-size>10MiB</max-document-size> -</document-api> -{% endhighlight %}</pre> - </td> - </tr> - </tbody> -</table> -<p>Example:</p> -<pre> -<document-api> - <binding>http://*/document-api/</binding> - <binding>https://*/document-api/</binding> - <abortondocumenterror>false</abortondocumenterror> - <maxpendingbytes>1048576</maxpendingbytes> - <maxpendingdocs>1000</maxpendingdocs> - <mbusport>1234</mbusport> - <retrydelay>5.5</retrydelay> - <retryenabled>false</retryenabled> - <route>default</route> - <timeout>250.5</timeout> - <tracelevel>3</tracelevel> - <max-document-size>10MiB</max-document-size> -</document-api> -</pre> - - - - - - -<h2 id="inference">inference</h2> -<p> - Vespa Cloud only. - Configures resources used for model inference in the container, - for example <a href="../../../rag/embedding.html">embedders</a>, - <a href="../../../rag/local-llms.html">local LLMs</a>, and - <a href="../../../ranking/stateless-model-evaluation.html">stateless model evaluation</a>. -</p> -<table class="table"> - <thead> - <tr><th>Element</th><th>Required</th><th>Value</th><th>Default</th><th>Description</th></tr> - </thead> - <tbody> - <tr><th>memory</th> - <td>optional</td> - <td>string</td> - <td>auto-estimated</td> - <td> - <p id="inference-memory"> - Container memory reserved for model inference, - covering both model weights and inference requests. - This memory is subtracted from the memory available to the JVM heap on the same node. - </p> - <p> - When not set, Vespa estimates the required inference memory automatically. - The automatic estimate can be inaccurate for some models and workloads, - which may lead to out-of-memory errors. Set this element explicitly to override the estimate. - </p> - </td> - </tr> - </tbody> -</table> -<p>Example:</p> -<pre>{% highlight xml %} -<container id="default" version="1.0"> - <inference> - <memory>1500Mb</memory> - </inference> - ... -</container> -{% endhighlight %}</pre> - - -<h2 id="document">document</h2> -<p> -<a href="../../../schemas/concrete-documents.html">Concrete document type</a> bindings for the container. Example: -</p> -<pre> -<container id="default" version="1.0"> - <document class="com.mydomain.concretedocs.Vehicle" - bundle="the name in <artifactId> in pom.xml" - type="vehicle"/> - <document class="com.mydomain.concretedocs.Vehicle" - bundle="the name in <artifactId> in pom.xml" - type="ship"/> - <document class="com.mydomain.concretedocs2.Disease" - bundle="the name in <artifactId> in pom.xml" - type="disease"/> - <search/> - <document-processing> - <chain id="default"> - <documentprocessor bundle="the name in <artifactId> in pom.xml" - id="concretedocs.ConcreteDocDocProc"/> - </chain> - </document-processing> - ... -</container> -</pre> - - - -<h2 id="accesslog">accesslog</h2> -<p> - Configures properties of the accesslog. - The default type is <code>json</code> that will give output in (line-based) - <a href="../../../operations/access-logging.html">JSON format</a>. - See <a href="../../../operations/access-logging.html">Access logging</a> for configuration details. - Setting the type to <code>vespa</code> gives a classic Apache CLF-like format. -</p> -<p> -Access logging can be disabled by setting the type to <code>disabled</code>. -</p> -<table class="table"> - <thead> - <tr><th>Attribute</th><th>Required</th><th>Value</th><th>Default</th><th>Description</th></tr> - </thead><tbody> - <tr><th>type</th> - <td>optional</td> - <td>string</td> - <td>json</td> - <td><p id="accesslog.type">The accesslog type: <em>json</em>, <em>vespa</em> or <em>disabled</em></p></td></tr> - <tr><th>fileNamePattern</th> - <td>required*</td> - <td>string</td> - <td>JsonAccessLog.<container id>.%Y%m%d%H%M%S</td> - <td><p id="accesslog.fileNamePattern">File name pattern. * Note: Optional when <em>type</em> is <em>disabled</em></p></td></tr> - <tr><th>symlinkName</th> - <td>optional</td> - <td>string</td> - <td>JsonAccessLog.<container id></td> - <td><p id="accesslog.symlinkName">Symlink name</p></td></tr> - <tr><th>rotationInterval</th> - <td>optional</td> - <td>string</td> - <td>0 60 ...</td> - <td><p id="accesslog.rotationInterval">Rotation interval</p></td></tr> - <tr><th>rotationScheme</th> - <td>optional</td> - <td>string</td> - <td>date</td> - <td><p id="accesslog.rotationScheme">Valid values are <em>date</em> or <em>sequence</em></p></td></tr> - </tbody> -</table> - -<h3 id="request-content">request-content</h3> -<p> - The <code>request-content</code> element is a child of <code>accesslog</code> and configures logging of request content. - Multiple <code>request-content</code> elements can be specified to log different request paths with different configurations. -</p> -<table class="table"> - <thead> - <tr><th>Element</th><th>Required</th><th>Value</th><th>Default</th><th>Description</th></tr> - </thead><tbody> - <tr><th>samples-per-second</th> - <td>required</td> - <td>double</td> - <td></td> - <td><p id="request-content.samples-per-second">Probabilistic sample rate per second</p></td></tr> - <tr><th>path-prefix</th> - <td>required</td> - <td>string</td> - <td></td> - <td><p id="request-content.path-prefix">URI path prefix to match for logging</p></td></tr> - <tr><th>max-bytes</th> - <td>required</td> - <td>integer</td> - <td></td> - <td><p id="request-content.max-bytes">Maximum size in bytes to log, only prefix will be kept for larger requests</p></td></tr> - </tbody> -</table> - -<p>Example:</p> -<pre> - <accesslog fileNamePattern="$VESPA_HOME/logs/vespa/access/JsonAccessLog.<container id>.%Y%m%d%H%M%S" - symlinkName="JsonAccessLog.<container id>" - rotationInterval="0 1 ..." - type="json" > - <request-content> - <samples-per-second>0.2</samples-per-second> - <path-prefix>/search</path-prefix> - <max-bytes>65536</max-bytes> - </request-content> - </accesslog> -</pre> - - - -<h2 id="include">include</h2> -<p> -Allows including XML snippets contained in external files. -All files from all listed directories will be included. -All files must have the same outer tag as they were referred from, -i.e. search, document-processing or processing. -The path must be relative to the application package root, -and must never point outside the package. -</p> -<table class="table"> - <thead> - <tr><th>Attribute</th><th>Required</th><th>Value</th><th>Default</th><th>Description</th></tr> - </thead><tbody> - <tr><th>dir</th> - <td>required</td> - <td>string</td> - <td></td> - <td><p id="include.dir">The directory to include files from. File inclusion order is undefined.</p></td></tr> - </tbody> -</table> -<p>Example:</p> -<pre> -<include dir="included_configs/search" /> -</pre> - - - -<h2 id="nodes">nodes</h2> - -<p>See <a href="services.html#nodes">nodes</a> in the general services.xml documentation.</p> - -<p>Additional container cluster specific attributes:</p> - -<table class="table"> - <thead> - <tr><th>Attribute</th><th>Required</th><th>Value</th><th>Default</th><th>Description</th></tr> - </thead><tbody> - <tr><th>allocated-memory</th> - <td>optional</td> - <td>percentage</td> - <td></td> - <td><p id="nodes.allocated-memory"></p>{% include deprecated.html content="See <a href='#jvm'>jvm</a>."%}</td></tr> - <tr><th>jvm-options</th> - <td>optional</td> - <td>string</td> - <td></td> - <td><p id="nodes.jvm-options"></p>{% include deprecated.html content="See <a href='#jvm'>jvm</a>."%}</td></tr> - <tr><th>jvm-gc-options</th> - <td>optional</td> - <td>string</td> - <td></td> - <td><p id="nodes.jvm-gc-options"></p>{% include deprecated.html content="See <a href='#jvm'>jvm</a>."%}</td></tr> - </tbody> -</table> - - - -<h2 id="environment-variables">environment-variables</h2> - -<p>Add child elements to set environment variables accessible in the container JVM runtime.</p> - -<p>Example:</p> -<pre>{% highlight xml %} -<nodes> - <environment-variables> - <KMP_SETTING>1</KMP_SETTING> - <KMP_AFFINITY>granularity=fine,verbose,compact,1,0</KMP_AFFINITY> - </environment-variables> -</nodes> -{% endhighlight %}</pre> - - -<h2 id="jvm">jvm</h2> - -<p>JVM settings for container nodes.</p> - -<table class="table"> - <thead> - <tr><th>Attribute</th><th>Required</th><th>Value</th><th>Default</th><th>Description</th></tr> - </thead> - <tbody> - <tr> - <th>allocated-memory</th> - <td>optional</td> - <td>percentage</td> - <td></td> - <td>Memory to allocate to each JVM instance as a percentage of available memory. - Must be an integer percentage followed by <em>%</em></td></tr> - <tr> - <th>options</th> - <td>optional</td> - <td>string</td> - <td></td> - <td>Generic JVM options</td> - </tr> - <tr> - <th>gc-options</th> - <td>optional</td> - <td>string</td> - <td></td> - <td>JVM GC options. Garbage Collector specific parameters</td> - </tr> - </tbody> -</table> - -<p>Example where 50% of the node total memory is used as the Max heap size of the JVM:</p> - -<pre>{% highlight xml %} -<nodes> - <jvm gc-options="-XX:+UseG1GC -XX:MaxTenuringThreshold=10" - options="-XX:+PrintCommandLineFlags" - allocated-memory="50%" /> -</nodes> -{% endhighlight %}</pre> - - -<h2 id="secrets">secrets</h2> - -<p>Use to access secrets configured in Vespa Cloud - -refer to the <a href="../../../security/secret-store">secret store</a>.</p> - - -<h2 id="secret-store">secret-store</h2> - -<p>The <code>secret-store</code> element holds configuration for custom implementations. -Contains one or more <code>group</code> elements.</p> - -<!-- ToDo: fix this later --> -<table class="table"> - <thead> - <tr><th>Attribute</th><th>Required</th><th>Value</th><th>Default</th><th>Description</th></tr> - </thead><tbody> - <tr><th>type</th> - <td>required</td> - <td>string</td> - <td></td> - <td><p id="secret-store.type">Value: "oath-ckms"</p></td></tr> - </tbody> -</table> - - - -<h2 id="group">group</h2> -<table class="table"> - <thead> - <tr><th>Attribute</th><th>Required</th><th>Value</th><th>Default</th><th>Description</th></tr> - </thead><tbody> - <tr><th>name</th> - <td>required</td> - <td>string</td> - <td></td> - <td><p id="group.name">Key group name</p></td></tr> - <tr><th>environment</th> - <td>required</td> - <td>string</td> - <td></td> - <td><p id="group.environment">Value one of: "alpha" "corp" "prod" "aws" "aws_stage"</p></td></tr> - </tbody> -</table> -<p>Example:</p> -<pre> -<secret-store type="my-ckms"> - <group name="[key group]" environment="[environment]"/> -</secret-store> -</pre> - - - -<h2 id="zookeeper">zookeeper</h2> -<p> - The <em>zookeeper</em> element declares that the container cluster should run ZooKeeper - and configure the necessary components. - This element has no attributes or children. -</p> - - -<h2 id="threadpool">threadpool</h2> -<p> - Available since {% include version.html version="8.611.13" %}. -</p> -<p> - Specifies configuration for the default thread pool in the container. - All parameters are relative to the number of CPU cores—see the - <a href="../../../performance/container-tuning.html#container-worker-threads-example">container tuning example</a>. - This thread pool also supports the optional <code>max</code> attribute, which lets the pool grow up to - <code>max * vCPU</code> threads under load before shrinking back after 5 seconds of idleness. Requests are rejected once the allowed - number of threads is reached, all are busy, and the queue is full. -</p> - -<h3 id="threadpool-threads">threads</h3> -<p> - The number of permanent threads relative to number of vCPU cores. Default value is <code>2</code>. -</p> -<table class="table"> - <thead> - <tr><th>Attribute</th><th>Required</th><th>Value</th><th>Default</th><th>Description</th></tr> - </thead><tbody> -<tr><th>max</th> - <td>optional</td> - <td>number</td> - <td>100</td> - <td> - <p id="threads.max"> - The maximum number of threads relative to vCPU cores. Value must be greater than or equal to <code><threads></code>. - </p> - </td></tr> -</tbody> -</table> - -<h3 id="threadpool-queue">queue</h3> -<p> - The size of the request queue relative to effective number of threads. - Specify <code>0</code> to disable queuing. Queueing is disabled by default. -</p> diff --git a/en/reference/applications/services/container.mdx b/en/reference/applications/services/container.mdx new file mode 100644 index 0000000000..5c1221b511 --- /dev/null +++ b/en/reference/applications/services/container.mdx @@ -0,0 +1,917 @@ +--- +title: "services.xml - container" +--- + +This is the reference for the container cluster configuration in [services.xml](/en/reference/applications/services/services). + +```yaml expandable +container [version, id] + http + server [id, port] + filtering + handler [id, class, bundle] + binding + component + server [id, class, bundle] + clients + client [id, permissions] + certificate [file] + token [id] + components + component + search + include [dir] + binding + searcher + federation + provider + chain + renderer + threadpool + significance + document-processing + include [dir] + documentprocessor + chain + threadpool + processing + include [dir] + binding + processor + chain + renderer + document-api + abortondocumenterror + retryenabled + route + maxpendingdocs + maxpendingbytes + retrydelay + timeout + tracelevel + mbusport + ignore-undefined-fields + max-document-size + model-evaluation + onnx + inference + memory + document [type, class, bundle] + accesslog [type, fileNamePattern, symlinkName, rotationInterval, rotationScheme] + request-content [samples-per-second, path-prefix, max-bytes] + config + nodes [count, allocated-memory, jvm-gc-options, jvm-options] + resources (Vespa Cloud) + node [hostalias] (Self managed) + environment-variables + jvm [allocated-memory, options, gc-options] + secrets + secret-store [type] + group [name, environment] + zookeeper + threadpool +``` + +[config](/en/reference/applications/config-files#generic-configuration-in-services-xml) elements are also allowed most places. + +Example: + +```xml + + + + + + + + +``` + +## `<container>` + +Contained in [`<services>`](/en/reference/applications/services/services). Each container tag specifies a separate container cluster. + +<table className="vespa-table"> + <thead> + <tr> + <th>Attribute</th> + <th>Required</th> + <th>Value</th> + <th>Default</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>**version**</td> + <td>required</td> + <td>number</td> + <td></td> + <td>1.0 in this version of Vespa</td> + </tr> + <tr> + <td>**id**</td> + <td>required</td> + <td>string</td> + <td></td> + <td>the id of this cluster</td> + </tr> + </tbody> +</table> + +## handler + +The `handler` element holds the configuration of a request handler. For each `binding` tag, the handler will be bound to the pertinent JDisc interfaces using the given binding. + +- `binding` For JDisc request handlers, add this server binding to this handler. +- [`component`](#component) for injecting another component. Must be a declaration of a new component, not a reference. + +<table className="vespa-table"> + <thead> + <tr> + <th>Attribute</th> + <th>Required</th> + <th>Value</th> + <th>Default</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>**id**</td> + <td>required</td> + <td>string</td> + <td></td> + <td>The component ID</td> + </tr> + <tr> + <td>**class**</td> + <td>optional</td> + <td>string</td> + <td></td> + <td>The class of the handler, defaults to id</td> + </tr> + <tr> + <td>**bundle**</td> + <td>optional</td> + <td>string</td> + <td></td> + <td>The bundle to load the handler from: The name in <code>{`<artifactId>`}</code> in pom.xml. Defaults to class or id (if no class is given)</td> + </tr> + </tbody> +</table> + +Example: + +```xml +<container id="default" version="1.0"> + <handler id="com.yahoo.search.handler.LegacyBridge"> + <binding>http://*/*</binding> + </handler> + <handler bundle="the name in <artifactId> in pom.xml" id="com.mydomain.vespatest.RedirectingHandler"/> + <handler bundle="the name in <artifactId> in pom.xml" id="com.mydomain.vespatest.ExampleHandler"/> + <nodes> + <node hostalias="node1"/> + </nodes> +</container> +``` + +## binding + +The URI to map a Handler to. Multiple elements are allowed. See example above. + +## server + +The `server` element holds the configuration of a JDisc server provider. + +<table className="vespa-table"> + <thead> + <tr> + <th>Attribute</th> + <th>Required</th> + <th>Value</th> + <th>Default</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>**id**</td> + <td>required</td> + <td>string</td> + <td></td> + <td>The component ID</td> + </tr> + <tr> + <td>**class**</td> + <td>optional</td> + <td>string</td> + <td></td> + <td>The class of the server, defaults to id</td> + </tr> + <tr> + <td>**bundle**</td> + <td>optional</td> + <td>string</td> + <td></td> + <td>The bundle to load the server from: The name in <code>{`<artifactId>`}</code> in the pom.xml. Defaults to class or id (if no class is given).</td> + </tr> + </tbody> +</table> + +Example: + +```xml +<server id="com.mydomain.vespatest.DemoServer"> + <config name="vespatest.demo-server"> + <response>Hello, world! + </response> + <port>16889</port> + </config> +</server> +``` + +## clients + +Vespa Cloud only. The `clients` element is a parent element for [client](#client) security configuration. Find details and practical examples in the [security guide](/en/security/guide#configuring-tokens). Example: + +```xml +<container id="query" version="1.0"> + <clients> + <client id="mtls" permissions="read"> + <certificate file="security/clients.pem"/> + </client> + <client id="query-token-client" permissions="read"> + <token id="query-token"/> + </client> + </clients> +</container> +``` + +## client + +Vespa Cloud only. Child element of [clients](#clients). Use to configure security credentials for a container cluster, using [certificate](#certificate) and/or [token](#token). + +<table className="vespa-table"> + <thead> + <tr> + <th>Attribute</th> + <th>Required</th> + <th>Value</th> + <th>Default</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>**id**</td> + <td>required</td> + <td>string</td> + <td></td> + <td>The client ID</td> + </tr> + <tr> + <td>**permissions**</td> + <td>required</td> + <td>string</td> + <td></td> + <td>Permissions, see the <a href="/en/security/guide#permissions" aria-label="security guide">security guide</a>. One of: <br/><br/> • <code>{`read`}</code><br/> • <code>{`write`}</code><br/> • <code>{`read,write`}</code></td> + </tr> + </tbody> +</table> + +## certificate + +Vespa Cloud only. Child element of [client](#client). Configure certificates using the *file* attribute. + +<table className="vespa-table"> + <thead> + <tr> + <th>Attribute</th> + <th>Required</th> + <th>Value</th> + <th>Default</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>**file**</td> + <td>required</td> + <td>string</td> + <td></td> + <td>Path to the certificate file, see the <a href="/en/security/guide#configuring-mtls" aria-label="security guide">security guide</a>.</td> + </tr> + </tbody> +</table> + +## token + +Vespa Cloud only. Child element of [client](#client). Configure tokens using the *id* attribute. + +<table className="vespa-table"> + <thead> + <tr> + <th>Attribute</th> + <th>Required</th> + <th>Value</th> + <th>Default</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>**id**</td> + <td>required</td> + <td>string</td> + <td></td> + <td>Token ID, see the <a href="/en/security/guide#configuring-tokens" aria-label="security guide">security guide</a>.</td> + </tr> + </tbody> +</table> + +## components + +Contains [component](#component) elements. Can be used in conjunction with [include](#include) for modular config of components. + +## component + +The `component` element holds the configuration of a [generic component](/en/applications/dependency-injection#special-components). Must be a declaration of a new component, not a reference. + +Nested [`component`](#component) child elements can be added for injecting specific component instances. This is useful if there is more than one declared component of the same Java class. Refer to [Injecting components](/en/applications/dependency-injection) for details and examples. + +<table className="vespa-table"> + <thead> + <tr> + <th>Attribute</th> + <th>Required</th> + <th>Value</th> + <th>Default</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>**id**</td> + <td>required</td> + <td>string</td> + <td></td> + <td>The component ID</td> + </tr> + <tr> + <td>**class**</td> + <td>optional</td> + <td>string</td> + <td></td> + <td>The class of the component, defaults to id</td> + </tr> + <tr> + <td>**bundle**</td> + <td>optional</td> + <td>string</td> + <td></td> + <td>The bundle to load the component from: The name in <code>{`<artifactId>`}</code> in the pom.xml. Defaults to class or id (if no class is given).</td> + </tr> + </tbody> +</table> + +Example: + +```xml +<component id="com.mydomain.demo.DemoComponent" bundle="the name in <artifactId> in pom.xml" /> +``` + +## document-api + +Use to enable [Document API](../../api/api.html) operations to a container cluster. Children elements: + +<table className="vespa-table"> + <thead> + <tr> + <th>Name</th> + <th>Required</th> + <th>Value</th> + <th>Default</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>**binding**</td> + <td>optional</td> + <td>string</td> + <td>http://*/</td> + <td>The URI to map the document-api handlers to. Multiple bindings are valid. Must end with a '/'. Note that each document-api handler will get its individual binding by adding a suffix, e.g. the feed handler will add 'feed/', the remove handler will add 'remove/' and so on. Example:<br/><br/><code>{`<document-api>`}</code><br/><code>{`<binding>http://*/document-api/</binding>`}</code><br/><code>{`<binding>https://*/document-api/</binding>`}</code><br/><code>{`</document-api>`}</code><br/><br/> With these configured bindings, the feed handler will be available at <code>{`http://*/document-api/feed/`}</code> and <code>{`https://*/document-api/feed/`}</code>. For other handlers, just replace 'feed/' with the appropriate suffix, e.g. 'get/', 'remove/' etc.</td> + </tr> + <tr> + <td>**abortondocumenterror**</td> + <td>optional</td> + <td>true/false</td> + <td>true</td> + <td>Controls whether to abort the entire feed or not if a document-related error occurs, i.e. if a document contains an unknown field. Setting this field to <code>{`true`}</code> will abort the feed on such errors, while setting it to <code>{`false`}</code> will cause Vespa to simply skip to the next document in the feed. Note that malformed XML in the input will abort the feed regardless of this setting.</td> + </tr> + <tr> + <td>**maxpendingbytes**</td> + <td>optional</td> + <td>number</td> + <td></td> + <td>The maximum number of pending bytes. If <code>{`<maxpendingdocs>`}</code> is 0 and this is set to 0, this defaults to 100 MB. If <code>{`<maxpendingdocs>`}</code> is more than 0, and this is set to 0, the send-window is only limited by number of messages sent, not the memory footprint.</td> + </tr> + <tr> + <td>**maxpendingdocs**</td> + <td>optional</td> + <td>number</td> + <td></td> + <td>The maximum number of pending documents the client can have. By default, the client will dynamically adjust the window size based on the latency of the performed operations. If the parameter is set, dynamic window sizing will be turned off in favor of the configured value.</td> + </tr> + <tr> + <td>**mbusport**</td> + <td>optional</td> + <td>number</td> + <td></td> + <td>Set the MessageBus port</td> + </tr> + <tr> + <td>**retrydelay**</td> + <td>optional</td> + <td>double</td> + <td>1.0</td> + <td>Delay in seconds between retries</td> + </tr> + <tr> + <td>**retryenabled**</td> + <td>optional</td> + <td>true/false</td> + <td></td> + <td>Enable or disable retrying documents that have failed.</td> + </tr> + <tr> + <td>**route**</td> + <td>optional</td> + <td>string</td> + <td>default</td> + <td>Set the route to feed documents to</td> + </tr> + <tr> + <td>**timeout**</td> + <td>optional</td> + <td>double</td> + <td>180.0</td> + <td>Set the timeout value in seconds for an operation</td> + </tr> + <tr> + <td>**tracelevel**</td> + <td>optional</td> + <td>0-9</td> + <td>0</td> + <td>Configure the level of which to trace messages sent. The higher the level, the more detailed descriptions.</td> + </tr> + <tr> + <td>**ignore-undefined-fields**</td> + <td>optional</td> + <td>true/false</td> + <td>false</td> + <td>Set to true to ignore undefined fields in document API operations and let such operations complete successfully, rather than fail. A <a href="/en/reference/api/document-v1#x-vespa-ignored-fields" aria-label="response header is returned">response header is returned</a> when field operations are ignored.</td> + </tr> + <tr> + <td>**max-document-size**</td> + <td>optional</td> + <td>string</td> + <td>100MiB</td> + <td>Specifies the maximum size of a document operation request accepted by the container, measured as the uncompressed size of the request body. The limit applies to all document types in the container cluster. A request larger than this limit will be rejected by the container before the operation is forwarded to the content cluster. <br/><br/> Valid values are numbers including a unit (e.g. *10MiB*) and the value must be between 1MiB and 2048MiB (inclusive). Values will be rounded to the nearest MiB, so using MiB as a unit is preferable.<br/><br/> The value should normally not exceed the smallest <a href="/en/reference/applications/services/content#max-document-size" aria-label="max-document-size">max-document-size</a> configured in any content cluster that this container feeds to; a deployment warning is emitted otherwise. <br/><br/> Example:<br/><br/> <code>{``}</code><code>{`xml <document-api>`}</code><code>{``}</code><br/><code>{``}</code><code>{`<max-document-size>10MiB</max-document-size>`}</code><code>{``}</code><br/> <code>{``}</code><code>{`</document-api`}</code><code>{``}</code></td> + </tr> + </tbody> +</table> + +Example: + +```xml +<document-api> + <binding>http://*/document-api/</binding> + <binding>https://*/document-api/</binding> + <abortondocumenterror>false</abortondocumenterror> + <maxpendingbytes>1048576</maxpendingbytes> + <maxpendingdocs>1000</maxpendingdocs> + <mbusport>1234</mbusport> + <retrydelay>5.5</retrydelay> + <retryenabled>false</retryenabled> + <route>default</route> + <timeout>250.5</timeout> + <tracelevel>3</tracelevel> + <max-document-size>10MiB</max-document-size> +</document-api> +``` + +## inference + +Configures resources used for model inference in the container, for example [embedders](/en/rag/embedding), [local LLMs](/en/rag/local-llms), and [stateless model evaluation](/en/ranking/stateless-model-evaluation). + +<table className="vespa-table"> + <thead> + <tr> + <th>Element</th> + <th>Required</th> + <th>Value</th> + <th>Default</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>**memory**</td> + <td>optional</td> + <td>string</td> + <td>auto-estimated</td> + <td>Container memory reserved for model inference, covering both model weights and inference requests. This memory is subtracted from the memory available to the JVM heap on the same node.<br/><br/> When not set, Vespa estimates the required inference memory automatically. The automatic estimate can be inaccurate for some models and workloads, which may lead to out-of-memory errors. Set this element explicitly to override the estimate.</td> + </tr> + </tbody> +</table> + +Example: + +```xml +<container id="default" version="1.0"> + <inference> + <memory>1500Mb</memory> + </inference> + ... +</container> +``` + +## document + +[Concrete document type](/en/schemas/concrete-documents) bindings for the container. Example: + +```xml +<container id="default" version="1.0"> + <document class="com.mydomain.concretedocs.Vehicle" + bundle="the name in <artifactId> in pom.xml" + type="vehicle"/> + <document class="com.mydomain.concretedocs.Vehicle" + bundle="the name in <artifactId> in pom.xml" + type="ship"/> + <document class="com.mydomain.concretedocs2.Disease" + bundle="the name in <artifactId> in pom.xml" + type="disease"/> + <search/> + <document-processing> + <chain id="default"> + <documentprocessor bundle="the name in <artifactId> in pom.xml" + id="concretedocs.ConcreteDocDocProc"/> + </chain> + </document-processing> + ... +</container> +``` + +## accesslog + +Configures properties of the accesslog. The default type is `json` that will give output in (line-based) [JSON format](/en/operations/access-logging). See [Access logging](/en/operations/access-logging) for configuration details. Setting the type to `vespa` gives a classic Apache CLF-like format. + +Access logging can be disabled by setting the type to `disabled`. + +<table className="vespa-table"> + <thead> + <tr> + <th>Attribute</th> + <th>Required</th> + <th>Value</th> + <th>Default</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>**type**</td> + <td>optional</td> + <td>string</td> + <td>json</td> + <td>The accesslog type: *json*, *vespa* or *disabled*</td> + </tr> + <tr> + <td>**fileNamePattern**</td> + <td>required*</td> + <td>string</td> + <td><code>{`JsonAccessLog.<container id>.%Y%m%d%H%M%S`}</code></td> + <td>File name pattern. * Note: Optional when *type* is *disabled*</td> + </tr> + <tr> + <td>**symlinkName**</td> + <td>optional</td> + <td>string</td> + <td><code>{`JsonAccessLog.<container id>`}</code></td> + <td>Symlink name</td> + </tr> + <tr> + <td>**rotationInterval**</td> + <td>optional</td> + <td>string</td> + <td>0 60 ...</td> + <td>Rotation interval</td> + </tr> + <tr> + <td>**rotationScheme**</td> + <td>optional</td> + <td>string</td> + <td>date</td> + <td>Valid values are *date* or *sequence*</td> + </tr> + </tbody> +</table> + +### request-content + +The `request-content` element is a child of `accesslog` and configures logging of request content. Multiple `request-content` elements can be specified to log different request paths with different configurations. + +<table className="vespa-table"> + <thead> + <tr> + <th>Element</th> + <th>Required</th> + <th>Value</th> + <th>Default</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>**samples-per-second**</td> + <td>required</td> + <td>double</td> + <td></td> + <td>Probabilistic sample rate per second</td> + </tr> + <tr> + <td>**path-prefix**</td> + <td>required</td> + <td>string</td> + <td></td> + <td>URI path prefix to match for logging</td> + </tr> + <tr> + <td>**max-bytes**</td> + <td>required</td> + <td>integer</td> + <td></td> + <td>Maximum size in bytes to log, only prefix will be kept for larger requests</td> + </tr> + </tbody> +</table> + +Example: + +```xml + <accesslog fileNamePattern="$VESPA_HOME/logs/vespa/access/JsonAccessLog.<container id>.%Y%m%d%H%M%S" + symlinkName="JsonAccessLog.<container id>" + rotationInterval="0 1 ..." + type="json" > + <request-content> + <samples-per-second>0.2</samples-per-second> + <path-prefix>/search</path-prefix> + <max-bytes>65536</max-bytes> + </request-content> + </accesslog> +``` + +## include + + +Allows including XML snippets contained in external files. All files from all listed directories will be included. All files must have the same outer tag as they were referred from, i.e. search, document-processing or processing. The path must be relative to the application package root, and must never point outside the package. + +<table className="vespa-table"> + <thead> + <tr> + <th>Attribute</th> + <th>Required</th> + <th>Value</th> + <th>Default</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>**dir**</td> + <td>required</td> + <td>string</td> + <td></td> + <td>The directory to include files from. File inclusion order is undefined.</td> + </tr> + </tbody> +</table> + +Example: + +```xml +<include dir="included_configs/search" /> +``` + +## nodes + +See [nodes](/en/reference/applications/services/services#nodes) in the general services.xml documentation. + +Additional container cluster specific attributes: + +<table className="vespa-table"> + <thead> + <tr> + <th>Attribute</th> + <th>Required</th> + <th>Value</th> + <th>Default</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>**allocated-memory**</td> + <td>optional</td> + <td>percentage</td> + <td></td> + <td><Danger>**Deprecated:** See <a href="#jvm" aria-label="jvm">jvm</a>.</Danger></td> + </tr> + <tr> + <td>**jvm-options**</td> + <td>optional</td> + <td>string</td> + <td></td> + <td><Danger>**Deprecated:** See <a href="#jvm" aria-label="jvm">jvm</a>.</Danger></td> + </tr> + <tr> + <td>**jvm-gc-options**</td> + <td>optional</td> + <td>string</td> + <td></td> + <td><Danger>**Deprecated:** See <a href="#jvm" aria-label="jvm">jvm</a>.</Danger></td> + </tr> + </tbody> +</table> + +## environment-variables + +Add child elements to set environment variables accessible in the container JVM runtime. + +Example: + +```xml +<nodes> + <environment-variables> + <KMP_SETTING>1</KMP_SETTING> + <KMP_AFFINITY>granularity=fine,verbose,compact,1,0</KMP_AFFINITY> + </environment-variables> +</nodes> +``` + +## jvm + +JVM settings for container nodes. + +<table className="vespa-table"> + <thead> + <tr> + <th>Attribute</th> + <th>Required</th> + <th>Value</th> + <th>Default</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>**allocated-memory**</td> + <td>optional</td> + <td>percentage</td> + <td></td> + <td>Memory to allocate to each JVM instance as a percentage of available memory. Must be an integer percentage followed by *%*</td> + </tr> + <tr> + <td>**options**</td> + <td>optional</td> + <td>string</td> + <td></td> + <td>Generic JVM options</td> + </tr> + <tr> + <td>**gc-options**</td> + <td>optional</td> + <td>string</td> + <td></td> + <td>JVM GC options. Garbage Collector specific parameters</td> + </tr> + </tbody> +</table> + +Example where 50% of the node total memory is used as the Max heap size of the JVM: + +```xml +<nodes> + <jvm gc-options="-XX:+UseG1GC -XX:MaxTenuringThreshold=10" + options="-XX:+PrintCommandLineFlags" + allocated-memory="50%" /> +</nodes> +``` + +## secrets + +Use to access secrets configured in Vespa Cloud - refer to the [secret store](/en/security/secret-store). + +## secret-store + +The `secret-store` element holds configuration for custom implementations. Contains one or more `group` elements. + +<table className="vespa-table"> + <thead> + <tr> + <th>Attribute</th> + <th>Required</th> + <th>Value</th> + <th>Default</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>**type**</td> + <td>required</td> + <td>string</td> + <td></td> + <td>Value: "oath-ckms"</td> + </tr> + </tbody> +</table> + +## group + +<table className="vespa-table"> + <thead> + <tr> + <th>Attribute</th> + <th>Required</th> + <th>Value</th> + <th>Default</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>**name**</td> + <td>required</td> + <td>string</td> + <td></td> + <td>Key group name</td> + </tr> + <tr> + <td>**environment**</td> + <td>required</td> + <td>string</td> + <td></td> + <td>Value one of: "alpha" "corp" "prod" "aws" "aws_stage"</td> + </tr> + </tbody> +</table> + +Example: + +```xml +<secret-store type="my-ckms"> + <group name="[key group]" environment="[environment]"/> +</secret-store> +``` + +## zookeeper + +The *zookeeper* element declares that the container cluster should run ZooKeeper and configure the necessary components. This element has no attributes or children. + +## threadpool + +Available since `Vespa 8.611.13`. + +Specifies configuration for the default thread pool in the container. All parameters are relative to the number of CPU cores—see the [container tuning example](/en/performance/container-tuning#container-worker-threads-example). This thread pool also supports the optional `max` attribute, which lets the pool grow up to `max * vCPU` threads under load before shrinking back after 5 seconds of idleness. Requests are rejected once the allowed number of threads is reached, all are busy, and the queue is full. + +### threads + +The number of permanent threads relative to number of vCPU cores. Default value is `2`. + +<table className="vespa-table"> + <thead> + <tr> + <th>Attribute</th> + <th>Required</th> + <th>Value</th> + <th>Default</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>**max**</td> + <td>optional</td> + <td>number</td> + <td>100</td> + <td>The maximum number of threads relative to vCPU cores. Value must be greater than or equal to <code>{`<threads>`}</code>.</td> + </tr> + </tbody> +</table> + +### queue + +The size of the request queue relative to effective number of threads. Specify `0` to disable queuing. Queueing is disabled by default. diff --git a/en/reference/applications/services/content.html b/en/reference/applications/services/content.html deleted file mode 100644 index 6417777b04..0000000000 --- a/en/reference/applications/services/content.html +++ /dev/null @@ -1,1856 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "services.xml - 'content'" -redirect_from: -- /en/reference/services-content.html ---- - -<pre class="pre-config"> -<a href="#content">content</a> - <a href="#documents">documents</a> - <a href="#document">document</a> - <a href="#document-processing">document-processing</a> - <a href="#min-redundancy">min-redundancy</a> - <a href="#redundancy">redundancy</a> - <a href="#coverage-policy">coverage-policy</a> - <a href="services.html#nodes">nodes</a> - <a href="#node">node</a> - <a href="#group">group</a> - <a href="#distribution">distribution</a> - <a href="#node">node</a> - <a href="#group">group</a> - <a href="#engine">engine</a> - <a href="#proton">proton</a> - <a href="#searchable-copies">searchable-copies</a> - <a href="#tuning-proton">tuning</a> - <a href="#searchnode">searchnode</a> - <a href="#lidspace">lidspace</a> - <a href="#lidspace-max-bloat-factor">max-bloat-factor</a> - <a href="#requestthreads">requestthreads</a> - <a href="#requestthreads-search">search</a> - <a href="#requestthreads-persearch">persearch</a> - <a href="#requestthreads-summary">summary</a> - <a href="#flushstrategy">flushstrategy</a> - <a href="#flushstrategy-native">native</a> - <a href="#flushstrategy-native-total">total</a> - <a href="#flushstrategy-native-total-maxmemorygain">maxmemorygain</a> - <a href="#flushstrategy-native-total-diskbloatfactor">diskbloatfactor</a> - <a href="#flushstrategy-native-component">component</a> - <a href="#flushstrategy-native-component-maxmemorygain">maxmemorygain</a> - <a href="#flushstrategy-native-component-diskbloatfactor">diskbloatfactor</a> - <a href="#flushstrategy-native-component-maxage">maxage</a> - <a href="#flushstrategy-native-transactionlog">transactionlog</a> - <a href="#flushstrategy-native-transactionlog-maxsize">maxsize</a> - <a href="#flushstrategy-native-conservative">conservative</a> - <a href="#flushstrategy-native-conservative-memory-limit-factor">memory-limit-factor</a> - <a href="#flushstrategy-native-conservative-disk-limit-factor">disk-limit-factor</a> - <a href="#initialize">initialize</a> - <a href="#initialize-threads">threads</a> - <a href="#feeding">feeding</a> - <a href="#feeding-concurrency">concurrency</a> - <a href="#feeding-niceness">niceness</a> - <a href="#index">index</a> - <a href="#index-io">io</a> - <a href="#index-io-search">search</a> - <a href="#index-warmup">warmup</a> - <a href="#index-warmup-time">time</a> - <a href="#index-warmup-unpack">unpack</a> - <a href="#removed-db">removed-db</a> - <a href="#removed-db-prune">prune</a> - <a href="#removed-db-prune-age">age</a> - <a href="#removed-db-prune-interval">interval</a> - <a href="#summary">summary</a> - <a href="#summary-io">io</a> - <a href="#summary-io-read">read</a> - <a href="#summary-store">store</a> - <a href="#summary-store-cache">cache</a> - <a href="#summary-store-cache-maxsize">maxsize</a> - <a href="#summary-store-cache-maxsize-percent">maxsize-percent</a> - <a href="#summary-store-cache-compression">compression</a> - <a href="#summary-store-cache-compression-type">type</a> - <a href="#summary-store-cache-compression-level">level</a> - <a href="#summary-store-logstore">logstore</a> - <a href="#summary-store-logstore-maxfilesize">maxfilesize</a> - <a href="#summary-store-logstore-chunk">chunk</a> - <a href="#summary-store-logstore-chunk-maxsize">maxsize</a> - <a href="#summary-store-logstore-chunk-compression">compression</a> - <a href="#summary-store-logstore-chunk-compression-type">type</a> - <a href="#summary-store-logstore-chunk-compression-level">level</a> - <a href="#sync-transactionlog">sync-transactionlog</a> - <a href="#flush-on-shutdown">flush-on-shutdown</a> - <a href="#resource-limits-proton">resource-limits</a> - <a href="#disk">disk</a> - <a href="#memory">memory</a> - <a href="#search">search</a> - <a href="#query-timeout">query-timeout</a> - <a href="#visibility-delay">visibility-delay</a> - <a href="#coverage">coverage</a> - <a href="#minimum">minimum</a> - <a href="#min-wait-after-coverage-factor">min-wait-after-coverage-factor</a> - <a href="#max-wait-after-coverage-factor">max-wait-after-coverage-factor</a> - <a href="#tuning">tuning</a> - <a href="#bucket-splitting">bucket-splitting</a> - <a href="#min-node-ratio-per-group">min-node-ratio-per-group</a> - <a href="#distribution_type">distribution</a> - <a href="#max-document-size">max-document-size</a> - <a href="#merges">merges</a> - <a href="#persistence-threads">persistence-threads</a> - <a href="#resource-limits">resource-limits</a> - <a href="#visitors">visitors</a> - <a href="#max-concurrent">max-concurrent</a> - <a href="#dispatch-tuning">dispatch</a> - <a href="#max-hits-per-partition">max-hits-per-partition</a> - <a href="#dispatch-policy">dispatch-policy</a> - <a href="#prioritize-availability">prioritize-availability</a> - <a href="#min-active-docs-coverage">min-active-docs-coverage</a> - <a href="#top-k-probability">top-k-probability</a> - <a href="#cluster-controller">cluster-controller</a> - <a href="#init-progress-time">init-progress-time</a> - <a href="#transition-time">transition-time</a> - <a href="#max-premature-crashes">max-premature-crashes</a> - <a href="#stable-state-period">stable-state-period</a> - <a href="#min-distributor-up-ratio">min-distributor-up-ratio</a> - <a href="#min-storage-up-ratio">min-storage-up-ratio</a> - <a href="#groups-allowed-down-ratio">groups-allowed-down-ratio</a> -</pre> - - - -<h2 id="content">content</h2> -<p> -The root element of a Content cluster definition. -Creates a content cluster. A content cluster stores and/or indexes documents. -The xml file may have zero or more such tags. -</p><p> -Contained in <a href="services.html">services</a>. -</p> -<table class="table"> - <thead> - <tr><th>Attribute</th><th>Required</th><th>Value</th><th>Default</th><th>Description</th></tr> - </thead><tbody> -<tr><th>version</th> - <td>required</td> - <td>number</td> - <td></td> - <td>1.0 in this version of Vespa</td></tr> -<tr><th>id</th> - <td>required for multiple clusters</td> - <td>string</td> - <td></td> - <td> - <p> - Name of the content cluster. - If none is supplied, the cluster name will be <code>content</code>. - Cluster names must be unique within the application, - if multiple clusters are configured, the name must be set for all but one at minimum. - </p> - {% include note.html content=" - Renaming a cluster is the same as dropping the current cluster and adding a new one. - This makes data unavailable or lost, depending on hosting model. - Deploying with a changed cluster id will therefore fail with a validation override requirement: - <code>Content cluster 'music' is removed. This will cause loss of all data in this cluster. - To allow this add <allow until='yyyy-mm-dd'>content-cluster-removal</allow> to validation-overrides.xml, - see https://docs.vespa.ai/en/reference/validation-overrides.html</code>."%} - </td> -</tr> -</tbody> -</table> -<p>Subelements:</p> -<ul> - <li><a href="#documents">documents</a> (required)</li> - <li><a href="#min-redundancy">min-redundancy</a></li> - <li><a href="#redundancy">redundancy</a></li> - <li><a href="#coverage-policy">coverage-policy</a></li> - <li><a href="services.html#nodes">nodes</a></li> - <li><a href="#group">group</a></li> - <li><a href="#engine">engine</a></li> - <li><a href="#search">search</a></li> - <li><a href="#tuning">tuning</a></li> -</ul> - - - -<h2 id="documents">documents</h2> -<p> -Contained in <a href="#content">content</a>. -Defines which document types should be routed to this content cluster using the default route, -and what documents should be kept in the cluster if the garbage collector runs. -Read more on <a href="../../../schemas/documents.html#document-expiry">expiring documents</a>. -Also have some backend specific configuration for whether documents should be searchable or not. -</p> -<table class="table"> -<thead> -<tr><th>Attribute</th><th>Required</th><th>Value</th><th>Default</th><th>Description</th></tr> -</thead><tbody> -<tr><th>selection</th> - <td>optional</td> - <td>string</td> - <td></td> - <td> - <p id="documents.selection"> - A <a href="../../writing/document-selector-language.html">document selection</a>, - restricting documents that are routed to this cluster. - Defaults to a selection expression matching everything. - </p><p> - This selection can be specified to match document identifier specifics - that are <em>independent</em> of document types. - For restrictions that apply only to a <em>specific</em> document type, - this must be done within that particular document type's - <a href="#document">document</a> element. - Trying to use document type references in this selection makes an error during deployment. - The selection given here will be merged with per-document - type selections specified within document tags, if any, - meaning that any document in the cluster must match <em>both</em> selections to be accepted and kept. - </p><p> - This feature is primarily used to <a href="../../../schemas/documents.html#document-expiry">expire documents</a>. - </p> - </td></tr> -<tr><th>garbage-collection</th> - <td>optional</td> - <td>true / false</td> - <td>false</td> - <td> - <p id="documents.garbage-collection"> - If true, regularly verify the documents stored in the cluster to see if - they belong in the cluster, and delete them if not. - If false, garbage collection is not run. - </p> - </td></tr> -<tr><th style="white-space: nowrap">garbage-collection-interval</th> - <td>optional</td> - <td>integer</td> - <td>3600</td> - <td> - <p id="documents.garbage-collection-interval">Time (in seconds) between garbage collection cycles. - Note that the deletion of documents is spread over this interval, so more resources will be - used for deleting a set of documents with a small interval than with a larger interval.</p> - </td></tr> -</tbody> -</table> -<p>Subelements:</p> -<ul> - <li><a href="#document">document</a> (required)</li> - <li><a href="#document-processing">document-processing</a> (optional)</li> -</ul> - - - -<h2 id="document">document</h2> -<p> - Contained in <a href="#documents">documents</a>. - The document type to be routed to this content cluster. -</p> -<table class="table"> - <thead> - <tr><th>Attribute</th><th>Required</th><th>Value</th><th>Default</th><th>Description</th></tr> - </thead><tbody> - <tr><th>type</th> - <td>required</td> - <td>string</td> - <td></td> - <td> - <p id="document.type"><a href="../../schemas/schemas.html#document">Document type name</a></p> - </td></tr> - <tr><th>mode</th> - <td>required</td> - <td style="white-space: nowrap">index /<br/>store-only /<br/>streaming</td> - <td></td> - <td> - <p id="document.mode"> - The mode of storing and indexing. - Refer to <a href="../../../performance/streaming-search.html">streaming search</a> for <em>store-only</em>, as documents are stored - the same way for both cases. - </p><p> - Changing mode requires an <em>indexing-mode-change</em> - <a href="../validation-overrides.html">validation override</a>, - and documents must be re-fed. - </p> - </td></tr> - <tr><th>selection</th> - <td>optional</td> - <td>string</td> - <td></td> - <td> - <p id="document.selection"> - A <a href="../../writing/document-selector-language.html">document selection</a>, - restricting documents that are routed to this cluster. - Defaults to a selection expression matching everything. - </p><p> - This selection must apply to fields in <em>this document type only</em>. - Selection will be merged together with selection for other types and global selection from - <a href="#documents">documents</a> to form a full expression for what documents belong to this cluster. - </p> - </td></tr> - <tr><th>global</th> - <td>optional</td> - <td>true / false</td> - <td>false</td> - <td> - <p id="document.global"> - Set to <em>true</em> to distribute all documents of this type to all nodes - in the content cluster it is defined. - </p><p> - Fields in global documents can be imported into documents to implement joins - - read more in <a href="../../../schemas/parent-child.html">parent/child</a>. - Vespa will detect when a new (or outdated) node is added to the cluster - and prevent it from taking part in searches until it has received all global documents. - </p><p> - Changing from <em>false</em> to <em>true</em> or vice versa requires a <em>global-document-change</em> - <a href="../validation-overrides.html">validation override</a>. - First, <a href="/en/operations/self-managed/admin-procedures.html#vespa-start-stop-restart">stop services</a> - on all content nodes. - Then, deploy with the validation override. - Finally, <a href="/en/operations/self-managed/admin-procedures.html#vespa-start-stop-restart">start services</a> - on all content nodes. - </p><p> - Note: <em>global</em> is only supported for <em>mode="index"</em>. - </p> - </td></tr> - </tbody> -</table> - - - -<h2 id="document-processing">document-processing</h2> -<p> -Contained in <a href="#documents">documents</a>. -Vespa Search specific configuration for which document processing cluster and chain to run index preprocessing. -</p> -<table class="table"> - <thead> - <tr><th>Attribute</th><th>Required</th><th>Value</th><th>Default</th><th>Description</th></tr> - </thead><tbody> - <tr><th>cluster</th> - <td>optional</td> - <td>string</td> - <td>Container cluster on content node</td> - <td> - <p id="document-processing.cluster"> - Name of a <a href="docproc.html">document-processing</a> - container cluster that does index preprocessing. - Use cluster to specify an alternative cluster, other than the default cluster on content nodes. - </p> - </td></tr> - <tr><th>chain</th> - <td>optional</td> - <td>string</td> - <td><code>indexing</code> chain</td> - <td> - <p id="document-processing.chain"> - A document processing chain in the container cluster specified by <em>cluster</em> - to use for index preprocessing. - The chain must inherit the <code>indexing</code> chain. - </p> - </td></tr> - </tbody> -</table> -<p> -Example - the container cluster enables <a href="docproc.html">document-processing</a>, -referred to by the content cluster: -</p> -<pre>{% highlight xml %} -<container id="my-indexing-cluster" version="1.0"> - <document-api/> - <document-processing/> -</container> -<content id="music" version="1.0"> - <documents> - <document-processing cluster="my-indexing-cluster"/> - </documents> -</content> -{% endhighlight %}</pre> -<p> - To add document processors either before or after the indexer, - declare a chain (inherit <em>indexing</em>) in a <em>document-processing</em> container cluster - and add document processors. - Annotate document processors with <code>before=indexingStart</code> or <code>after=indexingEnd</code>. - Configure this cluster and chain as the indexing chain in the content cluster - example: -</p> -<pre>{% highlight xml %} -<container id="my-indexing-cluster" version="1.0"> - <document-api/> - <document-processing> - <chain id="my-document-processors" - inherits="indexing"> - <documentprocessor id="MyDocproc"> - <before>indexingStart</before> - </documentprocessor> - <documentprocessor id="MyOtherDocproc"> - <after>indexingEnd</after> - </documentprocessor> - </chain> - </document-processing> -</container> -<content id="music" version="1.0"> - <documents> - <document-processing cluster="my-indexing-cluster" - chain="my-document-processors" /> - </documents> -</content> -{% endhighlight %}</pre> -{% include important.html content=' -Note the <a href="container.html#document-api">document-api</a> configuration. -Set up this API on the same nodes as <code>document-processing</code> - -find details in <a href="../../../writing/indexing.html">indexing</a>.' %} - - - -<h2 id="min-redundancy">min-redundancy</h2> - -<p>Contained in <a href="#content">content</a>. -The minimum total data copies the cluster will maintain. -This can be set instead of (or in addition to) redundancy to ensure that a -minimum number of copies are always maintained regardless of other configuration. -</p> -<p><code>min-redundancy</code> can be changed without node restart - replicas will be added or removed automatically.</p> - -<h3 id="min-redundancy-and-groups">min-redundancy and groups</h3> -<p> - A group will always have minimum one copy of each document in the cluster. - This is also the most commonly used configuration; - Increase replica level with more groups to improve query capacity. -</p> -<ul> -<li> - Example 1: If <em>min-redundancy</em> is 2 and there is 1 content group, there will be 2 - data copies in the group (2 copies for the cluster). If the number of groups is - changed to 2 there will be 1 data copy in each group (still 2 copies for the cluster). -</li> -<li> - Example 2: A cluster is configured to <a href="../../../operations/autoscaling.html">autoscale</a> using <code>groups="[2,3]"</code>. - Here, configure min-redundancy to 2, as each group will have 1 replica irrespective of number of groups, here 2 or 3 - - see <a href="../../../content/elasticity.html#replicas">replicas</a>. - Setting the lower bound ensures correct replica level for 2 groups. -</li> -</ul> -<p> - For self-managed Vespa: Read more about the actual number of replicas when using <a href="#group">groups</a> - in <a href="/en/content/elasticity.html#changing-topology">topology change</a>. -</p> - - - -<h2 id="redundancy">redundancy</h2> -<p>Contained in <a href="#content">content</a>.</p> -{% include note.html content=' -Use <a href="#min-redundancy">min-redundancy</a> instead of <code>redundancy</code>.' %} -<p>Vespa Cloud: The number of data copies <em>per group</em>.</p> -<p>Self-managed: The total data copies the cluster will maintain to avoid data loss.</p> -<p> - Example: with a redundancy of 2, the system tolerates 1 node failure before data becomes unavailable - (until the system has managed to create new replicas on other online nodes). -</p> -<p>Redundancy can be changed without node restart - replicas will be added or removed automatically.</p> - - - -<h2 id="coverage-policy">coverage-policy</h2> -<p>Contained in <a href="#content">content</a>.</p> -<p> - Specifies the coverage policy for the content cluster. Valid values are <code>group</code> or <code>node</code>. - The default value is <code>group</code>.</p> -<p> - If the policy is <code>group</code> coverage is maintained per group, meaning that when doing maintenance, - upgrades etc. one group is allowed to be down at a time. If there is only one group in the cluster, - coverage will be the same as policy <code>node</code>.</p> -<p> - If the policy is <code>node</code> coverage is maintained on a node level, meaning that when doing - maintenance, upgrades etc. coverage will be maintained on a node level, so in practice 1 node in the whole - cluster is allowed to be down at a time. -</p> -<p> - When having several groups the common reason for changing policy away from the default <code>group</code> policy - is when the load added to the remaining groups will increase too much when a whole group is allowed to - go down. In that case it will be better to use the <code>node</code> policy, as taking one node at a time - will give just a minor increase in load. -</p> - - -<h2 id="node">node</h2> - -<p>Contained in <a href="services.html#nodes">nodes</a> or <a href="#group">group</a>. -Configures a content node to the cluster, see <a href="services.html#node">node</a> -in the general services.xml documentation.</p> - -<p>Additional node attributes for content nodes:</p> - -<table class="table"> -<thead> -<tr><th>Attribute</th><th>Required</th><th>Value</th><th>Default</th><th>Description</th></tr> -</thead> -<tbody> -<tr> - <th style="white-space: nowrap">distribution-key</th> - <td>required</td> - <td>integer</td> - <td></td> - <td> - <p id="node.distribution-key"> - The unique data distribution id of this node. This <b>must</b> remain unchanged for the host's lifetime. - Distribution keys of a fresh system should be contiguous and start from zero.</p> - - <p>Distribution keys are used to identify nodes and groups for the - <a href="../../../content/idealstate.html">distribution algorithm</a>. - If a node changes distribution key, the distribution algorithm regards it as a new node, - so buckets are redistributed.</p> - </td> -</tr> -<tr> - <th>capacity</th> - <td>optional</td> - <td>double</td> - <td>1</td> - <td> - <p id="node.capacity"> - {% include deprecated.html content="Capacity of this node, relative to other nodes. - A node with capacity 2 will get double the data and feed requests of a node with capacity 1. - This feature is deprecated and expert mode only. Don't use in production, Vespa assumes - homogenous cluster capacity."%} - </p> - </td> -</tr> -<tr> - <th>baseport</th> - <td>optional</td> - <td>integer</td> - <td></td> - <td> - <p id="node.baseport">baseport - The first port in the port range allocated by this node. - </p> - </td> -</tr> -</tbody> -</table> - - - -<h2 id="group">group</h2> - -<p>Contained in <a href="#content">content</a> or -<a href="#group">group</a> - groups can be nested. -Defines the <a href="../../../content/elasticity.html#grouped-distribution">hierarchical structure</a> of the cluster. -Can not be used in conjunction with the <a href="services.html#nodes">nodes</a> element. -Groups can contain other groups or nodes, but not both. -There can only be a single level of leaf groups under the top group.</p> - -<table class="table"> -<thead> -<tr><th>Attribute</th><th>Required</th><th>Value</th><th>Default</th><th>Description</th></tr> -</thead><tbody> -<tr><th style="white-space: nowrap">distribution-key</th> - <td>required</td> - <td>integer</td> - <td></td> - <td> - <p id="group.distribution-key"> - Sets the distribution key of a group. It is not allowed to change this for a given group. - Group distribution keys only need to be unique among groups that share the same parent group. - </p> - </td></tr> -<tr><th>name</th> - <td>required</td> - <td>string</td> - <td></td> - <td><p id="group.name">The name of the group, used for access from status pages and the like.</p> - </td></tr> -</tbody> -</table> -{% include important.html content=" -There is no deployment-time verification that the distribution key remains unchanged for any given node or group. -Consequently, take great care when modifying the set of nodes in a content cluster. -Assigning a new distribution key to an existing node is undefined behavior; -Best case, the existing data will be temporarily unavailable until the error has been corrected. -Worst case, risk crashes or data loss."%} -<p> -See <a href="../../../performance/sizing-search.html">Vespa Serving Scaling Guide</a> -for when to consider using grouped distribution. -</p> - - - -<h2 id="distribution">distribution (in group)</h2> -<p> -Contained in <a href="#group">group</a>. -Defines the data distribution to subgroups of this group. -<em>distribution</em> should not be in the lowest level group containing storage nodes, -as here the ideal state algorithm is used directly. -In higher level groups, <em>distribution</em> is mandatory. -</p> -<table class="table"> - <thead> - <tr><th>Attribute</th><th>Required</th><th>Value</th><th>Default</th><th>Description</th></tr> - </thead><tbody> -<tr><th style="white-space: nowrap">partitions</th> - <td>required if there are subgroups in the group</td> - <td>string</td> - <td></td> - <td> - String conforming to the partition specification: - <table class="table"> - <thead> - <tr><th>Partition specification</th><th>Description</th></tr> - </thead><tbody> - <tr><th>*</th><td>Distribute all copies over 1 of N groups</td></tr> - <tr><th>1|*</th><td>Distribute all copies over 2 of N groups</td></tr> - <tr><th>1|1|*</th><td>Distribute all copies over 3 of N groups</td></tr> - </tbody> - </table> - </td></tr> -</tbody> -</table> -<p> - The partition specification is used to evenly distribute content copies across groups. - Set a number or <code>*</code> per group separated by pipes (e.g. <code>1|*</code> for two groups). - See <a href="../../../operations/self-managed/sizing-examples.html">sample deployment configurations</a>. -</p> - - - -<h2 id="engine">engine</h2> -<p> -Contained in <a href="#content">content</a>. -Specify the content engine to use, and/or adjust tuning parameters for the engine. -Allowed engines are <code>proton</code> and <code>dummy</code>, -the latter being used for debugging purposes. If no engine is given, proton is used. -Sub-element: <a href="#proton">proton</a>. -</p> - - - -<h2 id="proton">proton</h2> -<p> -Contained in <a href="#engine">engine</a>. -If specified, the content cluster will use the Proton content engine. -This engine supports storage, indexed search and secondary indices. -Optional sub-elements are <a href="#searchable-copies">searchable-copies</a>, -<a href="#tuning-proton">tuning</a>, -<a href="#sync-transactionlog">sync-transactionlog</a>, -<a href="#flush-on-shutdown">flush-on-shutdown</a>, and -<a href="#resource-limits-proton">resource-limits (in proton)</a>. -</p> - - - -<h2 id="searchable-copies">searchable-copies</h2> -<p> -Contained in <a href="#proton">proton</a>. -Default value is 2, or <a href="#redundancy">redundancy</a>, if lower. -If set to less than redundancy, only some of the stored copies are ready for searching at any time. -This means that node failures causes temporary data unavailability -while the alternate copies are being indexed for search. -The benefit is using less memory, trading off availability during transitions. -Refer to <a href="../../../content/proton.html#bucket-move">bucket move</a>. -</p><p> -If updating documents or using <a href="#documents">document selection</a> for garbage collection, -consider setting <a href="../../schemas/schemas.html#attribute">fast-access</a> -on the subset of attribute fields used for this to make sure that these attributes are always kept -in memory for fast access. -Note that this is only useful if <code>searchable-copies</code> is less than <code>redundancy</code>. -Read more in <a href="../../../content/proton.html">proton</a>. -</p><p> - <code>searchable-copies</code> can be changed without node restart. Note that when reducing - <code>searchable-copies</code> resource usage will not be reduced until content nodes are restarted. -</p> - - - -<h2 id="tuning-proton">tuning</h2> -<p> -Contained in <a href="#proton">proton</a>, optional. -Tune settings for the search nodes in a content cluster - sub-element: -</p> -<table class="table"> - <thead> - <tr> - <th>Element</th><th>Required</th><th>Quantity</th> - </tr> - </thead><tbody> - <tr> - <td><a href="#searchnode">searchnode</a></td> - <td class="required">No</td> - <td>Zero or one</td> - </tr> - </tbody> -</table> - - - -<h2 id="searchnode">searchnode</h2> -<p> -Contained in <a href="#tuning-proton">tuning</a>, optional. -Tune settings for search nodes in a content cluster - sub-elements: -</p> -<table class="table"> - <thead> - <tr> - <th>Element</th><th>Required</th><th>Quantity</th> - </tr> - </thead><tbody> - <tr> - <td><a href="#lidspace">lidspace</a></td> - <td class="required">No</td> - <td>Zero or one</td> - </tr><tr> - <tr> - <td><a href="#requestthreads">requestthreads</a></td> - <td class="required">No</td> - <td>Zero or one</td> - </tr><tr> - <td><a href="#flushstrategy">flushstrategy</a></td> - <td class="required">No</td> - <td>Zero or one</td> - </tr><tr> - <td><a href="#initialize">initialize</a></td> - <td class="required">No</td> - <td>Zero or one</td> - </tr><tr> - <td><a href="#feeding">feeding</a></td> - <td class="required">No</td> - <td>Zero or one</td> - </tr><tr> - <td><a href="#index">index</a></td> - <td class="required">No</td> - <td>Zero or one</td> - </tr><tr> - <td><a href="#summary">summary</a></td> - <td class="required">No</td> - <td>Zero or one</td> - </tr> - </tbody> -</table> -<pre>{% highlight xml %} -<tuning> - <searchnode> - <lidspace></lidspace> - <requestthreads></requestthreads> - <flushstrategy></flushstrategy> - <initialize></initialize> - <feeding></feeding> - <index></index> - <summary></summary> - </searchnode> -</tuning> -{% endhighlight %}</pre> - - - -<h2 id="requestthreads">requestthreads</h2> -<p> - Contained in <a href="#searchnode">searchnode</a>, optional. - Tune the number of request threads used on a content node, - see <a href="../../../performance/sizing-search.html#thread-configuration">thread-configuration</a> for details. - Sub-elements: -</p> -<table class="table"> - <thead> - <tr> - <th>Element</th><th>Required</th><th>Default</th><th>Description</th> - </tr> - </thead> - <tbody> - <tr> - <th>search</th> - <td>Optional</td> - <td><strong>Vespa Cloud:</strong> min(vcpu*4 + persearch - 1, vcpu*persearch)<br/> - <strong>Self-hosted:</strong> 64.</td> - <td><p id="requestthreads-search">Total size of the match engine thread pool. - Together with <code>persearch</code>, this determines the maximum number of queries - that can execute concurrently: <code>search / persearch</code>. - See the <a href="../../../performance/sizing-search.html#thread-configuration">Vespa serving scaling guide</a> - for sizing guidance.</p></td> - </tr> - <tr> - <th>persearch</th> - <td>Optional</td> - <td>1</td> - <td><p id="requestthreads-persearch"> - Maximum number of threads used per search. - A higher value reduces the time queries spend in query evaluation, except - time spent in ANN which is single-threaded. - This number of threads is held for each query for the duration of the query, - also when much of the time is spent on single-threaded operations. - See the <a href="../../../performance/sizing-search.html">Vespa serving scaling guide</a> - for an introduction of using multiple threads per search per node to reduce query latency. - Number of threads per search can be adjusted down per <em>rank-profile</em> - using <a href="../../schemas/schemas.html#num-threads-per-search">num-threads-per-search</a>.</p></td> - </tr> - <tr> - <th>summary</th> - <td>Optional</td> - <td><strong>Vespa Cloud:</strong> vcpu - <strong>Self-hosted:</strong> 16</td> - <td><p id="requestthreads-summary">Number of summary threads.</p></td> - </tr> - </tbody> -</table> -<pre>{% highlight xml %} -<requestthreads> - <search>64</search> - <persearch>1</persearch> - <summary>16</summary> -</requestthreads> -{% endhighlight %}</pre> - - - -<h2 id="flushstrategy">flushstrategy</h2> -<p> -Contained in <a href="#searchnode">searchnode</a>, optional. -Tune the <em>native</em>-strategy for flushing components to disk - -a smaller number means more frequent flush: -</p> -<ul> - <li><em>Memory gain</em> is how much memory can be freed by flushing a component</li> - <li><em>Disk gain</em> is how much disk space can be freed by flushing a - component (typically by using compaction)</li> -</ul> -<p> -Refer to <a href="../../../content/proton.html#proton-maintenance-jobs">Proton maintenance jobs</a>. -Optional sub-elements: -</p> -<ul> - <li id="flushstrategy-native"><code>native</code>: - <ul> - <li id="flushstrategy-native-total"><code>total</code> - <ul> - <li id="flushstrategy-native-total-maxmemorygain"><code>maxmemorygain</code>: - The total maximum memory gain (in bytes) for <em>all</em> components - before running flush, default 4294967296 (4 GB) - </li> - <li id="flushstrategy-native-total-diskbloatfactor"><code>diskbloatfactor</code>: - Trigger flush if the total disk gain (in bytes) for <em>all</em> components is larger - than the factor times current total disk usage, default 0.25 - </li> - </ul> - </li><li id="flushstrategy-native-component"><code>component</code> - <ul> - <li id="flushstrategy-native-component-maxmemorygain"><code>maxmemorygain</code>: - The maximum memory gain (in bytes) by <em>a single</em> component - before running flush, default 1073741824 (1 GB) - </li> - <li id="flushstrategy-native-component-diskbloatfactor"><code>diskbloatfactor</code>: - Trigger flush if the disk gain (in bytes) by <em>a single</em> component is larger than - the given factor times the current disk usage by that component, default 0.25 - </li> - <li id="flushstrategy-native-component-maxage"><code>maxage</code>: - The maximum age (in seconds) of unflushed content for a single component - before running flush, default 111600 (31h) - </li> - </ul> - </li><li id="flushstrategy-native-transactionlog"><code>transactionlog</code> - <ul> - <li id="flushstrategy-native-transactionlog-maxsize"><code>maxsize</code>: - The total maximum size (in bytes) of <a href="../../../content/proton.html#transaction-log">transaction logs</a> - for all document types before running flush, default 21474836480 (20 GB) - </li> - </ul> - </li><li id="flushstrategy-native-conservative"><code>conservative</code> - <ul> - <li id="flushstrategy-native-conservative-memory-limit-factor"><code>memory-limit-factor</code>: - When <a href="#resource-limits-proton">resource-limits (in proton)</a> for memory is reached, - flush more often by downscaling <code>total.maxmemorygain</code> and - <code>component.maxmemorygain</code>, default 0.5 - </li> - <li id="flushstrategy-native-conservative-disk-limit-factor"><code>disk-limit-factor</code>: - When <a href="#resource-limits-proton">resource-limits (in proton)</a> for disk is reached, - flush more often by downscaling <code>transactionlog.maxsize</code>, default 0.5 - </li> - </ul> - </li> - </ul> - </li> -</ul> -<pre>{% highlight xml %} -<flushstrategy> - <native> - <total> - <maxmemorygain>4294967296</maxmemorygain> - <diskbloatfactor>0.2</diskbloatfactor> - </total> - <component> - <maxmemorygain>1073741824</maxmemorygain> - <diskbloatfactor>0.2</diskbloatfactor> - <maxage>111600</maxage> - </component> - <transactionlog> - <maxsize>21474836480</maxsize> - </transactionlog> - <conservative> - <memory-limit-factor>0.5</memory-limit-factor> - <disk-limit-factor>0.5</disk-limit-factor> - </conservative> - </native> -</flushstrategy> -{% endhighlight %}</pre> - - - -<h2 id="initialize">initialize</h2> -<p> -Contained in <a href="#searchnode">searchnode</a>, optional. -Tune settings related to how the search node (proton) is initialized. Optional sub-elements: -</p> -<ul> - <li id="initialize-threads"><code>threads</code>: - The number of initializer threads used for loading structures from disk at proton startup. - The threads are shared between document databases when the value is larger than 0. - Default value is the number of document databases + 1. - <ul> - <li>When set to larger than 1, document databases are initialized in parallel</li> - <li>When set to 1, document databases are initialized in sequence</li> - <li>When set to 0, 1 separate thread is used per document database, - and they are initialized in parallel.</li> - </ul> - </li> -</ul> -<pre>{% highlight xml %} -<initialize> - <threads>2</threads> -</initialize> -{% endhighlight %}</pre> - -<h2 id="lidspace">lidspace</h2> -<p> -Contained in <a href="#searchnode">searchnode</a>, optional. -Tune settings related to how lidspace is managed. Optional sub-elements: -</p> -<ul> - <li id="lidspace-max-bloat-factor"><code>max-bloat-factor</code>: - Maximum bloat allowed before lidspace compaction is started. Compaction is moving a document - from a high lid to a lower lid. Cost is similar to feeding a document and removing it. - Also see description in <a href="../../../content/proton.html#lid-space-compaction">lidspace compaction maintenance job</a>. - Default value is 0.01 or 1% of total lidspace. Will be increased to target of 0.50 or 50%. - </li> -</ul> -<pre>{% highlight xml %} -<lidspace> - <max-bloat-factor>0.5</max-bloat-factor> -</lidspace> -{% endhighlight %}</pre> - - - -<h2 id="feeding">feeding</h2> -<p> -Contained in <a href="#searchnode">searchnode</a>, optional. -Tune <a href="../../../content/proton.html">proton</a> settings for feed operations. Optional sub-elements: -</p> -<ul> - <li id="feeding-concurrency"> - <p><code>concurrency</code>: - A number between 0.0 and 1.0 that specifies the concurrency when handling feed operations, default 0.5. - When set to 1.0, all cores on the cpu can be used for feeding. - Changing this value requires a restart of the nodes to take effect. - </p> - <p> - Feed operations in this context also includes data migration operations inside a cluster, e.g., when resizing. - Data migrations might therefore limit feeding capacity while running. - All such feed operations run with the same priority. - </p> - </li> - <li id="feeding-niceness"><code>niceness</code>: - A number between 0.0 and 1.0 that specifies the niceness of the feeding threads, default 0.0 => not any nicer than anyone else. - Increasing this number will reduce the priority of feeding compared to search. The real-world effect is hard to predict as the magic - exists in the OS level scheduler. - Changing this value requires a restart of the nodes to take effect. - </li> -</ul> -<pre>{% highlight xml %} -<feeding> - <concurrency>0.8</concurrency> - <niceness>0.5</niceness> -</feeding> -{% endhighlight %}</pre> -<p> - Note: Queries can always use <em>all</em> available cores; - concurrency caps how many can be used for writes and maintenance, which includes redistribution. -</p> - - -<h2 id="index">index</h2> -<p> -Contained in <a href="#searchnode">searchnode</a>, optional. -Tune various aspect with the handling of disk and memory indexes. Optional sub-elements: -</p> -<ul> - <li id="index-io"><code>io</code> - <ul> - <li id="index-io-search"><code>search</code>: - Controls io read options used during search, - values={mmap,populate}, default <code>mmap</code>. Using <code>populate</code> will eagerly touch all pages when index is loaded (after re-start or after index fusion is complete). - </li> - </ul> - </li> - <li id="index-warmup"><code>warmup</code> - <ul> - <li id="index-warmup-time"> - <p> - <code>time</code>: - Specifies in seconds how long the index shall be warmed up before being switched in for serving. - During warmup, it will receive queries and posting lists will be iterated, but results ignored - as they are duplicates of the live index. This will pull in the most important ones in the cache. - However, as warming up an index will occupy more memory, do not turn it on unless you suspect you need it. - And always benchmark to see if it is worth it. - </p> - <p> - It's only potentially relevant for fields with indexing setting <a href="../../../basics/schemas.html#document-fields">index</a>, - which have regular disk based indexes, - and where the disk indexes are merged/fused in the background. - When switching the index, warmup can be used. - Also note that <a href="../../api/state-v1.html#state-v1-health">state-v1-health</a> - is independent of <code>warmup</code> - the node can be "up" before warmup. - </p> - </li> - <li id="index-warmup-unpack"><code>unpack</code>: - Controls whether all posting features are pulled in to the cache, or only the most important. - values={true, false}, default false. - </li> - </ul> - </li> -</ul> -<pre>{% highlight xml %} -<index> - <io> - <search>mmap</search> - </io> - <warmup> - <time>60</time> - <unpack>true</unpack> - </warmup> -</index> -{% endhighlight %}</pre> - - -<h2 id="removed-db">removed-db</h2> -<p> -Contained in <a href="#searchnode">searchnode</a>, optional. -Tune various aspect of the db of removed documents. Optional sub-elements: -</p> -<ul> - <li id="removed-db-prune"><code>prune</code> - <ul> - <li id="removed-db-prune-age"><code>age</code>: - Specifies how long (in seconds) we must remember removed documents before we can prune them away. - Default is 2 weeks. - This sets the upper limit on how long a node can be down and still be accepted back in the system, - without having the index wiped. - There is no point in having this any higher than the age of the documents. - If corpus is re-fed every day, there is no point in having this longer than 24 hours. - </li> - <li id="removed-db-prune-interval"><code>interval</code>: - Specifies how often (in seconds) to prune old documents. Default is 3.36 hours (prune age / 100). - No need to change default. Exposed here for reference and for testing. - </li> - </ul> - </li> -</ul> -<pre>{% highlight xml %} -<removed-db> - <prune> - <age>86400</age> - </prune> -</removed-db> -{% endhighlight %}</pre> - - - -<h2 id="summary">summary</h2> -<p> -Contained in <a href="#searchnode">searchnode</a>, optional. -Tune various aspect with the handling of document summary. Optional sub-elements: -</p> -<ul> - <li id="summary-io"><code>io</code> - <ul> - <li id="summary-io-read"><code>read</code>: - Controls io read options used during reading of stored documents. - Values are <code>directio</code> <code>mmap</code> <code>populate</code>. - Default is <code>mmap</code>. <code>populate</code> will do an eager mmap and touch all pages.</li> - </ul> - </li> - <li id="summary-store"><code>store</code> - <ul> - <li id="summary-store-cache"><code>cache</code>: Used to tune the cache used by the document store. - Enabled by default, using up to 5% of available memory. - <ul> - <li id="summary-store-cache-maxsize"><code>maxsize</code>: - The maximum size of the cache in bytes. - If set, it takes precedence over <a href="#summary-store-cache-maxsize-percent">maxsize-percent</a>. - Default is unset. </li> - <li id="summary-store-cache-maxsize-percent"><code>maxsize-percent</code>: - The maximum size of the cache in percent of available memory. Default is 5%.</li> - <li id="summary-store-cache-compression"><code>compression</code> - <ul> - <li id="summary-store-cache-compression-type"><code>type</code>: - The compression type of the documents while in the cache. - Possible values are , <code>none</code> <code>lz4</code> <code>zstd</code>. - Default is <code>lz4</code></li> - <li id="summary-store-cache-compression-level"><code>level</code>: - The compression level of the documents while in cache. - Default is 6</li> - </ul> - </li> - </ul> - </li> - <li id="summary-store-logstore"><code>logstore</code>: - Used to tune the actual document store implementation (log-based). - <ul> - <li id="summary-store-logstore-maxfilesize"><code>maxfilesize</code>: - The maximum size (in bytes) per summary file on disk. Default value is 1GB. - <a href="../../../content/proton.html#document-store-compaction">document-store-compaction</a></li> - <li id="summary-store-logstore-chunk"><code>chunk</code> - <ul> - <li id="summary-store-logstore-chunk-maxsize"><code>maxsize</code>: - Maximum size (in bytes) of a chunk. Default value is 64KB.</li> - <li id="summary-store-logstore-chunk-compression"><code>compression</code> - <ul> - <li id="summary-store-logstore-chunk-compression-type"><code>type</code>: - Compression type for the documents, <code>none</code> <code>lz4</code> <code>zstd</code>. - Default is <code>zstd</code>.</li> - <li id="summary-store-logstore-chunk-compression-level"><code>level</code>: - Compression level for the documents. Default is 3.</li> - </ul> - </li> - </ul> - </li> - </ul> - </li> <!-- summary-store-logstore --> - </ul> - </li> <!-- summary-store --> -</ul> -<pre>{% highlight xml %} -<summary> - <io> - <read>directio</read> - </io> - <store> - <cache> - <maxsize-percent>5</maxsize-percent> - <compression> - <type>none</type> - </compression> - </cache> - <logstore> - <chunk> - <maxsize>16384</maxsize> - <compression> - <type>zstd</type> - <level>3</level> - </compression> - </chunk> - </logstore> - </store> -</summary> -{% endhighlight %}</pre> - - - -<h2 id="flush-on-shutdown">flush-on-shutdown</h2> -<p> -Contained in <a href="#proton">proton</a>. Default value is true. -If set to true, search nodes will flush a set of components (e.g. memory index, attributes) to disk -before shutting down such that the time it takes to flush these components -plus the time it takes to replay the <a href="../../../content/proton.html#transaction-log">transaction log</a> -after restart is as low as possible. -The time it takes to replay the transaction log depends on the amount of data to replay, -so by flushing, some components before restart the transaction log will be pruned, -and we reduce the replay time significantly. -Refer to <a href="../../../content/proton.html#proton-maintenance-jobs">Proton maintenance jobs</a>. -</p> - -<h2 id="sync-transactionlog">sync-transactionlog</h2> -<p> - Contained in <a href="#proton">proton</a>. Default value is true. - If true, the transactionlog is synced to disk after every write. - This enables the transactionlog to survive power failures and kernel panic. - The sync cost is amortized over multiple feed operations. - The faster you feed the more operations it is amortized over. - So with a local disk this is not known to be a performance issue. - However, if using NAS (Network Attached Storage) like EBS on AWS one can see significant - feed performance impact. For one particular case, turning off sync-transactionlog for EBS gave a 60x improvement. -</p> -<p> - With sync-transactionlog turned off, the risk of losing data depends on the kernel's - <a href="https://www.kernel.org/doc/html/latest/admin-guide/sysctl/vm.html#dirty-background-bytes">sysctl settings.</a> - For example, this is a common default: -</p> -<pre> -# sysctl -a -... -vm.dirty_expire_centisecs = 3000 -vm.dirty_ratio = 20 -vm.dirty_writeback_centisecs = 500 -... -</pre> -<p> - With this configuration, the worse case scenario is to lose 35 seconds worth of transactionlog, but no more than 1/20 - of the free memory. Because kernel flusher threads wake up every 5s (dirty_writeback_centisecs) and write data - older than 30s (dirty_expire_centisecs) from memory to disk. But if un-synced data exceeds 1/20 of the free memory, - the Vespa process will sync it (dirty_ratio). -</p> -<p> - The above also assumes that all copies of the data are lost at the same time <b>and</b> that kernels on all these nodes - flush at the same time: realistic scenario only with one copy. -</p> -<p> - Adjust these <a href="https://www.kernel.org/doc/html/latest/admin-guide/sysctl/vm.html#dirty-background-bytes">sysctl settings</a> - to manage the trade-off between data loss and performance. You'll see more in those kernel docs: for example, - thresholds can be expressed in bytes. -</p> - - -<h2 id="resource-limits-proton">resource-limits (in proton)</h2> -<p> -Contained in <a href="#proton">proton</a>. -Specifies resource limits used by proton to reject both external and internal write operations (on this content node) when a limit is reached. -</p> -{% include warning.html content=" -These proton limits should almost never be changed directly. -Instead, change <a href='#resource-limits'>resource-limits</a> -that controls when external write operations are blocked in the entire content cluster. -Be aware of the risks of tuning resource limits as seen in the link."%} -<p> -The local proton limits are derived from the cluster limits if not specified, using this formula: -</p> -<p class="equation-container"><!-- depends on mathjax --> -$${L_{proton}} = {L_{cluster}} + \frac{1-L_{cluster}}{2}$$ -</p> -<table class="table"> - <thead> - <tr><th>Element</th><th>Required</th><th>Value</th><th>Default</th><th>Description</th></tr> - </thead><tbody> - <tr><th>disk</th> - <td>optional</td> - <td>float<br/>[0, 1]</td> - <td>0.9</td> - <td> - <p id="disk-proton"> - Fraction of total space on the disk partition used before put and update operations are rejected - </p> - </td></tr> - <tr><th>memory</th> - <td>optional</td> - <td>float<br/>[0, 1]</td> - <td>0.9</td> - <td> - <p id="memory-proton"> - Fraction of physical memory that can be resident memory in anonymous mapping by proton - before put and update operations are rejected - </p> - </td></tr> - </tbody> -</table> -<p>Example:</p> -<pre>{% highlight xml %} -<proton> - <resource-limits> - <disk>0.83</disk> - <memory>0.82</memory> -{% endhighlight %}</pre> - - - -<h2 id="search">search</h2> -<p> -Contained in <a href="#content">content</a>, optional. -Declares search configuration for this content cluster. Optional sub-elements are -<a href="#query-timeout">query-timeout</a>, -<a href="#visibility-delay">visibility-delay</a> and -<a href="#coverage">coverage</a>. -</p> - - - -<h2 id="query-timeout">query-timeout</h2> -<p> -Contained in <a href="#search">search</a>. -Specifies the query timeout in seconds for queries against the search interface on the content nodes. -The default is 0.5 (500ms), the max is 600.0. -For query timeout also see the request parameter <a href="../../api/query.html#timeout">timeout</a>. -</p> -{% include note.html content='One can not override this value using the -<a href="../../api/query.html#timeout">timeout</a> request parameter.' %} - - - -<h2 id="visibility-delay">visibility-delay</h2> -<p>Contained in <a href="#search">search</a>. Default 0, max 1, seconds.</p> -<p> -This setting controls the TTL caching for <a href="../../../schemas/parent-child.html">parent-child</a> imported fields. -See <a href="../../../performance/feature-tuning.html#parent-child-and-search-performance">feature tuning</a>. -</p> - - - -<h2 id="coverage">coverage</h2> -<p>Contained in <a href="#search">search</a>. -Declares search coverage configuration for this content cluster. Optional sub-elements are -<a href="#minimum">minimum</a>, -<a href="#min-wait-after-coverage-factor">min-wait-after-coverage-factor</a> and -<a href="#max-wait-after-coverage-factor">max-wait-after-coverage-factor</a>. -Search coverage configuration controls how many nodes the query dispatcher process -should wait for, trading search coverage versus search performance. -</p> - - -<h2 id="minimum">minimum</h2> -<p> -Contained in <a href="#coverage">coverage</a>. -Declares the minimum search coverage required before returning the results of a query. -This number is in the range <code>[0, 1]</code>, with 0 being no coverage and 1 being full coverage. -</p><p> -The default is 1; unless configured otherwise a query will not return -until all search nodes have responded within the specified timeout. -</p> - - - -<h2 id="min-wait-after-coverage-factor">min-wait-after-coverage-factor</h2> -<p> -Contained in <a href="#coverage">coverage</a>. -Declares the minimum time for a query to wait for full coverage once the declared -<a href="#minimum">minimum</a> has been reached. This number is a factor that is -multiplied with the time remaining at the time of reaching minimum coverage. -</p><p> -The default is 0; unless configured otherwise a query will return as soon as the -minimum coverage has been reached, and the remaining search nodes appear to be lagging. -</p> - - - -<h2 id="max-wait-after-coverage-factor">max-wait-after-coverage-factor</h2> -<p> -Contained in <a href="#coverage">coverage</a>. -Declares the maximum time for a query to wait for full coverage once the declared -<a href="#minimum">minimum</a> has been reached. -This number is a factor that is multiplied with the time remaining -at the time of reaching minimum coverage. -</p><p> -The default is 1; unless configured otherwise a query is allowed to wait its full -timeout for full coverage even after reaching the minimum. -</p> - - -<h2 id="tuning">tuning</h2> -<p> -Contained in <a href="#content">content</a>, optional. Optional tuning parameters are: -<a href="#bucket-splitting">bucket-splitting</a>, -<a href="#min-node-ratio-per-group">min-node-ratio-per-group</a>, -<a href="#cluster-controller">cluster-controller</a>, -<a href="#dispatch-tuning">dispatch</a>, -<a href="#distribution_type">distribution</a>, -<a href="#max-document-size">max-document-size</a>, -<a href="#merges">merges</a>, -<a href="#persistence-threads">persistence-threads</a> and -<a href="#visitors">visitors</a>. -</p> - - - -<h2 id="bucket-splitting">bucket-splitting</h2> -<p> -Contained in <a href="#tuning">tuning</a>. -The <a href="../../../content/buckets.html">bucket</a> is the fundamental unit of distribution -and management in a content cluster. -Buckets are auto-split, no need to configure for most applications. -</p> -<table class="table"> - <thead> - <tr><th>Attribute</th><th>Required</th><th>Value</th><th>Default</th><th>Description</th></tr> - </thead><tbody> - <tr><th style="white-space: nowrap">max-documents</th> - <td>optional</td> - <td>integer</td> - <td>1024</td> - <td> - <p id="max-documents"> - Maximum number of documents per content bucket. - Buckets are split in two if they have more documents than this. - Keep this value below 16K. - </p> - </td></tr> - <tr><th>max-size</th> - <td>optional</td> - <td>integer</td> - <td>32MiB</td> - <td> - <p id="max-size"> - Maximum size (in bytes) of a bucket. - This is the sum of the serialized size of all documents kept in the bucket. - Buckets are split in two if they have a larger size than this. - Keep this value below 100 MiB. - </p> - </td></tr> - <tr><th>minimum-bits</th> - <td>optional</td> - <td>integer</td> - <td></td> - <td> - <p id="minimum-bits"> - Override the ideal distribution bit count configured for this cluster. - Prefer to use the <a href="#distribution_type">distribution type</a> - setting instead if the default distribution bit count does not fit the cluster. - This variable is intended for testing and to work around possible distribution bit issues. - Most users should not need this option. - </p> - </td></tr> - </tbody> -</table> - - - -<h2 id="min-node-ratio-per-group">min-node-ratio-per-group</h2> -{% include important.html content='This is configuration for the cluster controller. -Most users are normally looking for -<a href="#min-active-docs-coverage">min-active-docs-coverage</a> -which controls how many nodes can be down before query load is routed to other groups.' %} -<p> -Contained in <a href="#tuning">tuning</a>. -States a lower bound requirement on the ratio of nodes within <em>individual</em> <a href="#group">groups</a> -that must be online and able to accept traffic before the entire group is automatically taken out of service. -Groups are automatically brought back into service when the availability -of its nodes has been restored to a level equal to or above this limit. -</p><p> -Elastic content clusters are often configured to use multiple groups -for the sake of horizontal traffic scaling and/or data availability. -The content distribution system will try to ensure a configured number of replicas is always present -within a group in order to maintain data redundancy. -If the number of available nodes in a group drops too far, -it is possible for the remaining nodes in the group to not have sufficient capacity to take over -storage and serving for the replicas they now must assume responsibility for. -Such situations are likely to result in increased latencies and/or feed rejections caused by resource exhaustion. -Setting this tuning parameter allows the system to instead automatically take down the remaining nodes in the group, -allowing feed and query traffic to fail completely over to the remaining groups. -</p><p> -Valid parameter is a decimal value in the range [0, 1]. -Default is 0, which means that the automatic group out-of-service functionality will <em>not</em> automatically take effect. -</p><p> -Example: assume a cluster has been configured with <em>n</em> groups of 4 nodes each -and the following tuning config: -</p> -<pre>{% highlight xml %} -<tuning> - <min-node-ratio-per-group>0.75</min-node-ratio-per-group> -</tuning> -{% endhighlight %}</pre> -<p> -This tuning allows for 1 node in a group to be down. If 2 or more nodes go down, -all nodes in the group will be marked as down, letting the <em>n-1</em> remaining groups handle all the traffic. -</p><p> -This configuration can be changed live as the system is running and altered limits will take effect immediately. -</p> - - - -<h2 id="distribution_type">distribution (in tuning)</h2> -<p> -Contained in <a href="#tuning">tuning</a>. -Tune the distribution algorithm used in the cluster. -</p> -<table class="table"> - <thead> - <tr><th>Attribute</th><th>Required</th><th>Value</th><th>Default</th><th>Description</th></tr> - </thead><tbody> -<tr><th>type</th> - <td>optional</td> - <td>loose | strict | legacy</td> - <td>loose</td> - <td> - <p>When the number of a nodes configured in a system changes over certain limits, the system will - automatically trigger major redistributions of documents. This is to ensure that - the number of buckets is appropriate for the number of nodes in the cluster. This enum - value specifies how aggressive the system should be in triggering such distribution changes.</p> - <p>The default of <code>loose</code> strikes a balance between rarely altering the distribution - of the cluster and keeping the skew in document distribution low. It is recommended that you - use the default mode unless you have empirically observed that it causes too much skew in load - or document distribution.</p> - <p>Note that specifying <code>minimum-bits</code> under <a href="#bucket-splitting">bucket-splitting</a> - overrides this setting and effectively "locks" the distribution in place.</p> - </td></tr> -</tbody> -</table> - - -<h2 id="max-document-size">max-document-size</h2> -<p> -Contained in <a href="#tuning">tuning</a>. -Specifies max document size in the content cluster, measured as the uncompressed size -of a document operation arriving over the wire by the distributor service. The limit will -be used for all document types. A document larger than this limit will be rejected by the -distributor. Note that some document operations that don't contain the entire document, like -<a href="../../../writing/document-api-guide.html#document-updates">document updates</a> -might increase the size of a document above this limit. See -also <a href="container.html#document-api">max-document-size</a> -for document api, which is probably what you want to configure, make sure to keep the -value documented here larger. -</p> -<p> -Valid values are numbers including a unit (e.g. <em>10MiB</em>) and the value must be between 1Mib and 2048 Mib (inclusive). -Values will be rounded to nearest MiB, so using MiB as a unit is preferrable. -It is strongly recommended to make sure this is not set too high, 10 MiB is a reasonable setting for most use cases, -setting it above 100 MiB is not recommended, as allowing large documents might impact operations, e.g. when -restarting nodes, moving documents between nodes etc. Default value is 128 MiB. -</p> -<p> - Example: -</p> -<pre>{% highlight xml %} -<tuning> - <max-document-size>10MiB</max-document-size> -</tuning> -{% endhighlight %}</pre> - -<h2 id="merges">merges</h2> -<p> -Contained in <a href="#tuning">tuning</a>. -Defines throttling parameters for bucket merge operations. -</p> -<table class="table"> - <thead> - <tr><th>Attribute</th><th>Required</th><th>Value</th><th>Default</th><th>Description</th></tr> - </thead><tbody> -<tr><th>max-per-node</th> - <td>optional</td> - <td>number</td> - <td></td> - <td> - Maximum number of parallel active bucket merge operations. - </td></tr> -<tr><th>max-queue-size</th> - <td>optional</td> - <td>number</td> - <td></td> - <td> - Maximum size of the merge bucket queue, before reporting BUSY back to the distributors. - </td></tr> -</tbody> -</table> - - -<h2 id="persistence-threads">persistence-threads</h2> -<p> -Contained in <a href="#tuning">tuning</a>. -Defines the number of persistence threads per partition on each content node. -A content node executes bucket operations against the persistence engine synchronously in each of these threads. -8 threads are used by default. Override with the <strong>count</strong> attribute. -</p> - - - -<h2 id="visitors">visitors</h2> -<p> -Contained in <a href="#tuning">tuning</a>. -Tuning parameters for visitor operations. -Might contain <a href="#max-concurrent">max-concurrent</a>. -</p> -<table class="table"> - <thead> - <tr><th>Attribute</th><th>Required</th><th>Value</th><th>Default</th><th>Description</th></tr> - </thead><tbody> -<tr><th>thread-count</th> - <td>optional</td> - <td>number</td> - <td></td> - <td> - The maximum number of threads in which to execute visitor operations. - A higher number of threads may increase performance, but may use more memory. - </td></tr> -<tr><th>max-queue-size</th> - <td>optional</td> - <td>number</td> - <td></td> - <td> - Maximum size of the pending visitor queue, before reporting BUSY back to the distributors. - </td></tr> -</tbody> -</table> - - - -<h2 id="max-concurrent">max-concurrent</h2> -<p> -Contained in <a href="#visitors">visitors</a>. -Defines how many visitors can be active concurrently on each storage node. -The number allowed depends on priority - lower priority visitors should not block higher priority visitors completely. -To implement this, specify a fixed and a variable number. -The maximum active is calculated by adjusting the variable component using the priority, -and adding the fixed component. -</p> -<table class="table"> - <thead> - <tr><th>Attribute</th><th>Required</th><th>Value</th><th>Default</th><th>Description</th></tr> - </thead><tbody> - <tr><th>fixed</th> - <td>optional</td> - <td>number</td> - <td><a href="https://github.com/vespa-engine/vespa/blob/master/storage/src/vespa/storage/visiting/stor-visitor.def">16</a></td> - <td>The fixed component of the maximum active count</td></tr> - <tr><th>variable</th> - <td>optional</td> - <td>number</td> - <td><a href="https://github.com/vespa-engine/vespa/blob/master/storage/src/vespa/storage/visiting/stor-visitor.def">64</a></td> - <td>The variable component of the maximum active count</td></tr> - </tbody> -</table> - - - -<h2 id="resource-limits">resource-limits</h2> -<p> -Contained in <a href="#tuning">tuning</a>. -Specifies resource limits used to decide whether external write operations should be blocked in the entire content cluster, -based on the reported resource usage by content nodes. -See <a href="../../../writing/feed-block.html">feed block</a> for more details. -</p> -<p> -<strong>Warning:</strong> The content nodes require resource headroom to handle -extra documents as part of re-distribution during node failure, -and spikes when running -<a href="../../../content/proton.html#proton-maintenance-jobs">maintenance jobs</a>. -Tuning these limits should be done with extreme care, -and setting them too high might lead to permanent data loss. -They are best left untouched, using the defaults, and cannot be set in Vespa Cloud. -</p> -<table class="table"> - <thead> - <tr><th>Element</th><th>Required</th><th>Value</th><th>Default</th><th>Description</th></tr> - </thead><tbody> - <tr><th>disk</th> - <td>optional</td> - <td>float<br/>[0, 1]</td> - <td>0.8</td> - <td> - <p id="disk">Fraction of total space on the disk partition used on a content node before feed is blocked</p> - </td></tr> - <tr><th>memory</th> - <td>optional</td> - <td>float<br/>[0, 1]</td> - <td>0.8/0.75</td> - <td> - <p id="memory"> - Fraction of physical memory that can be resident memory in anonymous mapping on a content node before feed is blocked. - Total physical memory is sampled as the minimum of <code>sysconf(_SC_PHYS_PAGES) * sysconf(_SC_PAGESIZE)</code> - and the cgroup (v1 or v2) memory limit. Nodes with 8 Gib or less memory in Vespa Cloud has a limit of 0.75. - </p> - </td></tr> - </tbody> -</table> -Example - in the content tag: -<pre>{% highlight xml %} -<tuning> - <resource-limits> - <disk>0.78</disk> - <memory>0.77</memory> - </resource-limits> -</tuning> -{% endhighlight %}</pre> - - - -<h2 id="dispatch-tuning">dispatch</h2> -<p> -Contained in <a href="#tuning">tuning</a>. -Tune the query dispatch behavior - child elements: -</p> -<table class="table"> - <thead> - <tr><th>Element</th><th>Required</th><th>Value</th><th>Default</th><th>Description</th></tr> - </thead><tbody> - <tr><th>max-hits-per-partition</th> - <td>optional</td> - <td>Integer</td> - <td>No capping: Return all</td> - <td><p id="max-hits-per-partition"> - Maximum number of hits to return from a content node. - By default, a query returns the requested number of hits + - offset from every content node to the container. - The container orders the hits globally according to the query, - then discards all hits beyond the number requested. - </p><p> - In a system with a large fan-out, - this consumes network bandwidth and the container nodes easily network saturated. - Containers will also sort and discard more hits than optimal. - </p><p> - When there are sufficiently many search nodes, - assuming an even distribution of the hits, - it suffices to only return a fraction of the request number of hits from each node. - Note that changing this number will have global ordering impact. - See <em>top-k-probability</em> below for improving performance with fewer hits.</p></td></tr> - <tr><th>dispatch-policy</th> - <td>optional</td> - <td>adaptive / best-of-random-2 / round-robin</td> - <td>adaptive</td> - <td> - <p id="dispatch-policy"> - With <a href="../../../performance/sizing-search.html#data-distribution">grouped distribution</a>: - Configure policy for choosing which group shall receive the next query request. - Coverage requirements is considered when choosing a group. - Note that multiphase requests that requires or benefits from hitting the same group - in all phases are always hashed. - </p> - <table class="table"> - <tr> - <th>adaptive</th> - <td>Measures latency, preferring lower latency groups, selecting group <code>i</code> has a probability proportional to 1 / (latency for group <code>i</code>).</td> - <tr> - <th style="white-space: nowrap">best-of-random-2</th> - <td>Selects 2 random groups and selects the one with the lowest latency.</td> - </tr><tr> - <th>round-robin</th> - <td>Selects groups in a round-robin manner, giving fair distribution of queries to each group.</td> - </tr> - </table> - <tr><th style="white-space: nowrap">prioritize-availability</th> - <td>optional</td> - <td>Boolean</td> - <td>true</td> - <td> - <p id="prioritize-availability"> - With <a href="../../../performance/sizing-search.html#data-distribution">grouped distribution</a>: - If true, or by default, all groups that are within min-active-docs-coverage of the <b>median</b> - of the document count of other groups will be used to service queries. If set to false, only - groups within min-active-docs-coverage of the <b>max</b> document count will be used, - with the consequence that full coverage is prioritized over availability when - multiple groups are lacking content, since the remaining groups may not be able - to service the full query load. - </p></td></tr> - <tr><th style="white-space: nowrap">min-active-docs-coverage</th> - <td>optional</td> - <td>A float percentage</td> - <td>97</td> - <td> - <p id="min-active-docs-coverage"> - With <a href="../../../performance/sizing-search.html#data-distribution">grouped distribution</a>: - The percentage of active documents a group must have, relative to the median across all groups - in the content cluster, to be considered active for serving queries. - Because of measurement timing differences, it is not advisable to tune this above 99 percent. - </p></td></tr> - <tr><th>top-k-probability</th> - <td>optional</td> - <td>Double</td> - <td>0.9999</td> - <td><p id="top-k-probability"> - Probability that the top K hits will be the globally best. - Based on this probability, the dispatcher will fetch enough hits from each node to achieve this. - The only way to guarantee a probability of 1.0 is to fetch K hits from each partition. - However, by reducing the probability from 1.0 to 0.99999, one can significantly reduce number of hits fetched - and save both bandwidth and latency. - The number of hits to fetch from each partition is computed as: - </p><p class="equation-container"><!-- depends on mathjax --> - $${q}={\frac{k}{n}}+{qT}({p},{30})×{\sqrt{ {k}×{\frac{1}{n}}×({1}-{\frac{1}{n}}) }}$$ - </p><p> - where qT is a Student's t-distribution. - With n=10 partitions, k=200 hits and p=0.99999, only 45 hits per partition is needed, - as opposed to 200 when p=1.0. - </p><p> - Use this option to reduce network and container cpu/memory in clusters with many nodes per group - - see <a href="../../../performance/sizing-search.html">Vespa Serving Scaling Guide</a>.</p></td></tr> - </tbody> -</table> - - - -<h2 id="cluster-controller">cluster-controller</h2> -<p> -Contained in <a href="#tuning">tuning</a>. -Tuning parameters for the cluster controller managing this cluster - child elements: -<table class="table"> - <thead> - <tr><th>Element</th><th>Required</th><th>Value</th><th>Default</th><th>Description</th></tr> - </thead><tbody> - <tr><th>init-progress-time</th> - <td>optional</td> - <td></td> - <td></td> - <td> - <p id="init-progress-time"> - If the initialization progress count have not been altered for this amount of seconds, - the node is assumed to have deadlocked and is set down. - Note that initialization may actually be prioritized lower now, - so setting a low value here might cause false positives. - Though if it is set down for wrong reason, - when it will finish initialization and then be set up again. - </p> - </td></tr> - <tr><th>transition-time</th> - <td>optional</td> - <td></td> - <td><a href="https://github.com/vespa-engine/vespa/blob/master/configdefinitions/src/vespa/fleetcontroller.def"> - storage_transition_time</a> - <a href="https://github.com/vespa-engine/vespa/blob/master/configdefinitions/src/vespa/fleetcontroller.def"> - distributor_transition_time</a></td> - <td> - <p id="transition-time"> - The transition time states how long (in seconds) a node will be in maintenance mode - during what looks like a controlled restart. - Keeping a node in maintenance mode during a restart allows a restart - without the cluster trying to create new copies of all the data immediately. - If the node has not started or got back up within the transition time, - the node is set down, in which case, new full bucket copies will be created. - Note separate defaults for distributor and storage (i.e. search) nodes. - </p> - </td></tr> - <tr><th style="white-space: nowrap">max-premature-crashes</th> - <td>optional</td> - <td></td> - <td><a href="https://github.com/vespa-engine/vespa/blob/master/configdefinitions/src/vespa/fleetcontroller.def"> - max_premature_crashes</a></td> - <td> - <p id="max-premature-crashes"> - The maximum number of crashes allowed before a content node is permanently - set down by the cluster controller. - If the node has a stable up or down state for more than the <em>stable-state-period</em>, - the crash count is reset. - However, resetting the count will not re-enable the node again if it has been disabled - - restart the cluster controller to reset. - </p> - </td></tr> - <tr><th>stable-state-period</th> - <td>optional</td> - <td></td> - <td><a href="https://github.com/vespa-engine/vespa/blob/master/configdefinitions/src/vespa/fleetcontroller.def"> - stable_state_time_period</a></td> - <td> - <p id="stable-state-period"> - If a content node's state doesn't change for this many seconds, - it's state is considered <em>stable</em>, clearing the premature crash count. - </p> - </td></tr> - <tr><th>min-distributor-up-ratio</th> - <td>optional</td> - <td></td> - <td><a href="https://github.com/vespa-engine/vespa/blob/master/configdefinitions/src/vespa/fleetcontroller.def"> - min_distributor_up_ratio</a></td> - <td> - <p id="min-distributor-up-ratio"> - The minimum ratio of distributors that are required to be <em>up</em> for the - cluster state to be <em>up</em>. - </p> - </td></tr> - <tr><th>min-storage-up-ratio</th> - <td>optional</td> - <td></td> - <td><a href="https://github.com/vespa-engine/vespa/blob/master/configdefinitions/src/vespa/fleetcontroller.def"> - min_storage_up_ratio</a></td> - <td> - <p id="min-storage-up-ratio"> - The minimum ratio of content nodes that are required to be <em>up</em> for the - cluster state to be <em>up</em>. - </p> - </td></tr> - <tr><th>groups-allowed-down-ratio</th> - <td>optional</td> - <td></td> - <td><a href="https://github.com/vespa-engine/vespa/blob/master/configdefinitions/src/vespa/fleetcontroller.def"> - groups-allowed-down-ratio</a></td> - <td> - <p id="groups-allowed-down-ratio"> - A ratio for the number of content groups that are allowed to be - down simultaneously. A value of 0.5 means that 50% of the groups are - allowed to be down. The default is to allow only one group to be down - at a time. - </p> - </td></tr> - </tbody> -</table> diff --git a/en/reference/applications/services/content.mdx b/en/reference/applications/services/content.mdx new file mode 100644 index 0000000000..e7723a387f --- /dev/null +++ b/en/reference/applications/services/content.mdx @@ -0,0 +1,1299 @@ +--- +title: "services.xml - 'content'" +sidebarTitle: "services.xml - content" +--- + + +```yaml expandable +content + documents + document + document-processing + min-redundancy + redundancy + coverage-policy + nodes + node + group + distribution + node + group + engine + proton + searchable-copies + tuning + searchnode + lidspace + max-bloat-factor + requestthreads + search + persearch + summary + flushstrategy + native + total + maxmemorygain + diskbloatfactor + component + maxmemorygain + diskbloatfactor + maxage + transactionlog + maxsize + conservative + memory-limit-factor + disk-limit-factor + initialize + threads + feeding + concurrency + niceness + index + io + search + warmup + time + unpack + removed-db + prune + age + interval + summary + io + read + store + cache + maxsize + maxsize-percent + compression + type + level + logstore + maxfilesize + chunk + maxsize + compression + type + level + sync-transactionlog + flush-on-shutdown + resource-limits + disk + memory + search + query-timeout + visibility-delay + coverage + minimum + min-wait-after-coverage-factor + max-wait-after-coverage-factor + tuning + bucket-splitting + min-node-ratio-per-group + distribution + max-document-size + merges + persistence-threads + resource-limits + visitors + max-concurrent + dispatch + max-hits-per-partition + dispatch-policy + prioritize-availability + min-active-docs-coverage + top-k-probability + cluster-controller + init-progress-time + transition-time + max-premature-crashes + stable-state-period + min-distributor-up-ratio + min-storage-up-ratio + groups-allowed-down-ratio +``` + +## content + +The root element of a Content cluster definition. Creates a content cluster. A content cluster stores and/or indexes documents. The xml file may have zero or more such tags. + +Contained in [services](/en/reference/applications/services/services). + +<table className="vespa-table"> + <thead> + <tr> + <th>Attribute</th> + <th>Required</th> + <th>Value</th> + <th>Default</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>**version**</td> + <td>required</td> + <td>number</td> + <td></td> + <td>1.0 in this version of Vespa</td> + </tr> + <tr> + <td>**id**</td> + <td>required for multiple clusters</td> + <td>string</td> + <td></td> + <td>Name of the content cluster. If none is supplied, the cluster name will be <code>{`content`}</code>. Cluster names must be unique within the application, if multiple clusters are configured, the name must be set for all but one at minimum. <br/><br/><Note> **Note:**<br/><br/> Renaming a cluster is the same as dropping the current cluster and adding a new one. This makes data unavailable or lost, depending on hosting model. Deploying with a changed cluster id will therefore fail with a validation override requirement: <code>{`Content cluster 'music' is removed. This will cause loss of all data in this cluster. To allow this add <allow until='yyyy-mm-dd'>content-cluster-removal</allow> to validation-overrides.xml, see /en/reference//en/reference/applications/validation-overrides`}</code>. </Note></td> + </tr> + </tbody> +</table> + +Subelements: + +- [documents](#documents) (required) +- [min-redundancy](#min-redundancy) +- [redundancy](#redundancy) +- [coverage-policy](#coverage-policy) +- [nodes](/en/reference/applications/services/services#nodes) +- [group](#group) +- [engine](#engine) +- [search](#search) +- [tuning](#tuning) + +## documents + +Contained in [content](#content). Defines which document types should be routed to this content cluster using the default route, and what documents should be kept in the cluster if the garbage collector runs. Read more on [expiring documents](/en/schemas/documents#document-expiry). Also have some backend specific configuration for whether documents should be searchable or not. + +<table className="vespa-table"> + <thead> + <tr> + <th>Attribute</th> + <th>Required</th> + <th>Value</th> + <th>Default</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>**selection**</td> + <td>optional</td> + <td>string</td> + <td></td> + <td>A <a href="/en/reference/writing/document-selector-language" aria-label="document selection">document selection</a>, restricting documents that are routed to this cluster. Defaults to a selection expression matching everything.<br/><br/> This selection can be specified to match document identifier specifics that are *independent* of document types. For restrictions that apply only to a *specific* document type, this must be done within that particular document type's <a href="#document" aria-label="document">document</a> element. Trying to use document type references in this selection makes an error during deployment. The selection given here will be merged with per-document type selections specified within document tags, if any, meaning that any document in the cluster must match *both* selections to be accepted and kept. <br/><br/> This feature is primarily used to <a href="/en/schemas/documents#document-expiry" aria-label="expire documents">expire documents</a>.</td> + </tr> + <tr> + <td>**garbage-collection**</td> + <td>optional</td> + <td>true / false</td> + <td>false</td> + <td>If true, regularly verify the documents stored in the cluster to see if they belong in the cluster, and delete them if not. If false, garbage collection is not run.</td> + </tr> + <tr> + <td>**garbage-collection-interval**</td> + <td>optional</td> + <td>integer</td> + <td>3600</td> + <td>Time (in seconds) between garbage collection cycles. Note that the deletion of documents is spread over this interval, so more resources will be used for deleting a set of documents with a small interval than with a larger interval.</td> + </tr> + </tbody> +</table> + +Subelements: + +- [document](#document) (required) +- [document-processing](#document-processing) (optional) + +## document + +Contained in [documents](#documents). The document type to be routed to this content cluster. + +<table className="vespa-table"> + <thead> + <tr> + <th>Attribute</th> + <th>Required</th> + <th>Value</th> + <th>Default</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>**type**</td> + <td>required</td> + <td>string</td> + <td></td> + <td><a href="/en/reference/schemas/schemas#document" aria-label="Document type name">Document type name</a></td> + </tr> + <tr> + <td>**mode**</td> + <td>required</td> + <td>index / store-only / streaming</td> + <td></td> + <td>The mode of storing and indexing. Refer to <a href="/en/performance/streaming-search" aria-label="streaming search">streaming search</a> for *store-only*, as documents are stored the same way for both cases.<br/><br/> Changing mode requires an *indexing-mode-change* <a href="/en/reference/applications/validation-overrides" aria-label="validation override">validation override</a>, and documents must be re-fed.</td> + </tr> + <tr> + <td>**selection**</td> + <td>optional</td> + <td>string</td> + <td></td> + <td>A <a href="/en/reference/writing/document-selector-language" aria-label="document selection">document selection</a>, restricting documents that are routed to this cluster. Defaults to a selection expression matching everything.<br/><br/> This selection must apply to fields in *this document type only*. Selection will be merged together with selection for other types and global selection from <a href="#documents" aria-label="documents">documents</a> to form a full expression for what documents belong to this cluster.</td> + </tr> + <tr> + <td>**global**</td> + <td>optional</td> + <td>true / false</td> + <td>false</td> + <td>Set to *true* to distribute all documents of this type to all nodes in the content cluster it is defined.<br/><br/> Fields in global documents can be imported into documents to implement joins - read more in <a href="/en/schemas/parent-child" aria-label="parent/child">parent/child</a>. Vespa will detect when a new (or outdated) node is added to the cluster and prevent it from taking part in searches until it has received all global documents.<br/><br/> Changing from *false* to *true* or vice versa requires a *global-document-change* <a href="/en/reference/applications/validation-overrides" aria-label="validation override">validation override</a>. First, <a href="/en/operations/self-managed/admin-procedures#vespa-start-stop-restart" aria-label="stop services">stop services</a> on all content nodes. Then, deploy with the validation override. Finally, <a href="/en/operations/self-managed/admin-procedures#vespa-start-stop-restart" aria-label="start services">start services</a> on all content nodes.<br/><br/> <Note> **Note:**<br/><br/> *global* is only supported for *mode="index"*.</Note></td> + </tr> + </tbody> +</table> + +## document-processing + +Contained in [documents](#documents). Vespa Search specific configuration for which document processing cluster and chain to run index preprocessing. + +<table className="vespa-table"> + <thead> + <tr> + <th>Attribute</th> + <th>Required</th> + <th>Value</th> + <th>Default</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>**cluster**</td> + <td>optional</td> + <td>string</td> + <td>Container cluster on content node</td> + <td>Name of a <a href="/en/reference/applications/services/docproc" aria-label="document-processing">document-processing</a> container cluster that does index preprocessing. Use cluster to specify an alternative cluster, other than the default cluster on content nodes.</td> + </tr> + <tr> + <td>**chain**</td> + <td>optional</td> + <td>string</td> + <td><code>{`indexing`}</code> chain</td> + <td>A document processing chain in the container cluster specified by *cluster* to use for index preprocessing. The chain must inherit the <code>{`indexing`}</code> chain.</td> + </tr> + </tbody> +</table> + +Example - the container cluster enables [document-processing](/en/reference/applications/services/docproc), referred to by the content cluster: + +```xml +<container id="my-indexing-cluster" version="1.0"> + <document-api/> + <document-processing/> +</container> +<content id="music" version="1.0"> + <documents> + <document-processing cluster="my-indexing-cluster"/> + </documents> +</content> +``` + +To add document processors either before or after the indexer, declare a chain (inherit *indexing*) in a *document-processing* container cluster and add document processors. Annotate document processors with `before=indexingStart` or `after=indexingEnd`. Configure this cluster and chain as the indexing chain in the content cluster - example: + +```xml +<container id="my-indexing-cluster" version="1.0"> + <document-api/> + <document-processing> + <chain id="my-document-processors" + inherits="indexing"> + <documentprocessor id="MyDocproc"> + <before>indexingStart</before> + </documentprocessor> + <documentprocessor id="MyOtherDocproc"> + <after>indexingEnd</after> + </documentprocessor> + </chain> + </document-processing> +</container> +<content id="music" version="1.0"> + <documents> + <document-processing cluster="my-indexing-cluster" + chain="my-document-processors" /> + </documents> +</content> +``` + +<Warning> +**Important:** + +Note the [document-api](/en/reference/applications/services/container#document-api) configuration. Set up this API on the same nodes as `document-processing` - find details in [indexing](/en/writing/indexing). +</Warning> + +## min-redundancy + +Contained in [content](#content). The minimum total data copies the cluster will maintain. This can be set instead of (or in addition to) redundancy to ensure that a minimum number of copies are always maintained regardless of other configuration. + +`min-redundancy` can be changed without node restart - replicas will be added or removed automatically. + +### min-redundancy and groups + +A group will always have minimum one copy of each document in the cluster. This is also the most commonly used configuration; Increase replica level with more groups to improve query capacity. + +- Example 1: If *min-redundancy* is 2 and there is 1 content group, there will be 2 data copies in the group (2 copies for the cluster). If the number of groups is changed to 2 there will be 1 data copy in each group (still 2 copies for the cluster). +- Example 2: A cluster is configured to [autoscale](/en/operations/autoscaling) using `groups="[2,3]"`. Here, configure min-redundancy to 2, as each group will have 1 replica irrespective of number of groups, here 2 or 3 - see [replicas](/en/content/elasticity#replicas). Setting the lower bound ensures correct replica level for 2 groups. + +For self-managed Vespa: Read more about the actual number of replicas when using [groups](#group) in [topology change](/en/content/elasticity#changing-topology). + +## redundancy + +Contained in [content](#content). + +<Note> +**Note:** + +Use [min-redundancy](#min-redundancy) instead of `redundancy`. +</Note> + +Vespa Cloud: The number of data copies *per group*. + +Self-managed: The total data copies the cluster will maintain to avoid data loss. + +Example: with a redundancy of 2, the system tolerates 1 node failure before data becomes unavailable (until the system has managed to create new replicas on other online nodes). + +Redundancy can be changed without node restart - replicas will be added or removed automatically. + +## coverage-policy + +Contained in [content](#content). + +Specifies the coverage policy for the content cluster. Valid values are `group` or `node`. The default value is `group`. + +If the policy is `group` coverage is maintained per group, meaning that when doing maintenance, upgrades etc. one group is allowed to be down at a time. If there is only one group in the cluster, coverage will be the same as policy `node`. + +If the policy is `node` coverage is maintained on a node level, meaning that when doing maintenance, upgrades etc. coverage will be maintained on a node level, so in practice 1 node in the whole cluster is allowed to be down at a time. + +When having several groups the common reason for changing policy away from the default `group` policy is when the load added to the remaining groups will increase too much when a whole group is allowed to go down. In that case it will be better to use the `node` policy, as taking one node at a time will give just a minor increase in load. + +## node + +Contained in [nodes](/en/reference/applications/services/services#nodes) or [group](#group). Configures a content node to the cluster,/en/reference/applications/services/services#node) in the general services.xml documentation. + +Additional node attributes for content nodes: + +<table className="vespa-table"> + <thead> + <tr> + <th>Attribute</th> + <th>Required</th> + <th>Value</th> + <th>Default</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>distribution-key</td> + <td>required</td> + <td>integer</td> + <td></td> + <td>The unique data distribution id of this node. This **must** remain unchanged for the host's lifetime. Distribution keys of a fresh system should be contiguous and start from zero.<br/><br/> Distribution keys are used to identify nodes and groups for the <a href="/en/content/idealstate" aria-label="distribution algorithm">distribution algorithm</a>. If a node changes distribution key, the distribution algorithm regards it as a new node, so buckets are redistributed.</td> + </tr> + <tr> + <td>capacity</td> + <td>optional</td> + <td>double</td> + <td>1</td> + <td><Danger> **Deprecated:**<br/><br/> Capacity of this node, relative to other nodes. A node with capacity 2 will get double the data and feed requests of a node with capacity 1. This feature is deprecated and expert mode only. Don't use in production, Vespa assumes homogenous cluster capacity.</Danger></td> + </tr> + <tr> + <td>baseport</td> + <td>optional</td> + <td>integer</td> + <td></td> + <td>baseport The first port in the port range allocated by this node.</td> + </tr> + </tbody> +</table> + +## group + +Contained in [content](#content) or [group](#group) - groups can be nested. Defines the [hierarchical structure](/en/content/elasticity#grouped-distribution) of the cluster. Can not be used in conjunction with the [nodes](/en/reference/applications/services/services#nodes) element. Groups can contain other groups or nodes, but not both. There can only be a single level of leaf groups under the top group. + +<table className="vespa-table"> + <thead> + <tr> + <th>Attribute</th> + <th>Required</th> + <th>Value</th> + <th>Default</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>**distribution-key**</td> + <td>required</td> + <td>integer</td> + <td></td> + <td>Sets the distribution key of a group. It is not allowed to change this for a given group. Group distribution keys only need to be unique among groups that share the same parent group.</td> + </tr> + <tr> + <td>**name**</td> + <td>required</td> + <td>string</td> + <td></td> + <td>The name of the group, used for access from status pages and the like.</td> + </tr> + </tbody> +</table> + +<Warning> +**Important:** + +There is no deployment-time verification that the distribution key remains unchanged for any given node or group. Consequently, take great care when modifying the set of nodes in a content cluster. Assigning a new distribution key to an existing node is undefined behavior; Best case, the existing data will be temporarily unavailable until the error has been corrected. Worst case, risk crashes or data loss. +</Warning> + +See [Vespa Serving Scaling Guide](/en/performance/sizing-search) for when to consider using grouped distribution. + +## distribution (in group) + +Contained in [group](#group). Defines the data distribution to subgroups of this group. *distribution* should not be in the lowest level group containing storage nodes, as here the ideal state algorithm is used directly. In higher level groups, *distribution* is mandatory. + +<table className="vespa-table"> + <thead> + <tr> + <th>Attribute</th> + <th>Required</th> + <th>Value</th> + <th>Default</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>**partitions**</td> + <td>required if there are subgroups in the group</td> + <td>string</td> + <td></td> + <td>String conforming to the partition specification:<br/> <br/>Partition specification &emsp;&emsp; Description <br/><br/>* &emsp;&emsp;Distribute all copies over 1 of N groups <br/>1|* &emsp;&emsp; Distribute all copies over 2 of N groups <br/> 1|1|* &emsp;&emsp;Distribute all copies over 3 of N groups</td> + </tr> + </tbody> +</table> + +The partition specification is used to evenly distribute content copies across groups. Set a number or `*` per group separated by pipes (e.g. `1|*` for two groups). See [sample deployment configurations](/en/operations/self-managed/sizing-examples). + +## engine + +Contained in [content](#content). Specify the content engine to use, and/or adjust tuning parameters for the engine. Allowed engines are `proton` and `dummy`, the latter being used for debugging purposes. If no engine is given, proton is used. Sub-element: [proton](#proton). + +## proton + +Contained in [engine](#engine). If specified, the content cluster will use the Proton content engine. This engine supports storage, indexed search and secondary indices. Optional sub-elements are [searchable-copies](#searchable-copies), [tuning](#tuning-proton), [sync-transactionlog](#sync-transactionlog), [flush-on-shutdown](#flush-on-shutdown), and [resource-limits (in proton)](#resource-limits-proton). + +## searchable-copies + +Contained in [proton](#proton). Default value is 2, or [redundancy](#redundancy), if lower. If set to less than redundancy, only some of the stored copies are ready for searching at any time. This means that node failures causes temporary data unavailability while the alternate copies are being indexed for search. The benefit is using less memory, trading off availability during transitions. Refer to [bucket move](/en/content/proton#bucket-move). + +If updating documents or using [document selection](#documents) for garbage collection, consider setting [fast-access](/en/reference/schemas/schemas#attribute) on the subset of attribute fields used for this to make sure that these attributes are always kept in memory for fast access. Note that this is only useful if `searchable-copies` is less than `redundancy`. Read more in [proton](/en/content/proton). + +`searchable-copies` can be changed without node restart. Note that when reducing `searchable-copies` resource usage will not be reduced until content nodes are restarted. + +## tuning + +Contained in [proton](#proton), optional. Tune settings for the search nodes in a content cluster - sub-element: + +<table className="vespa-table"> + <thead> + <tr> + <th>Element</th> + <th>Required</th> + <th>Quantity</th> + </tr> + </thead> + <tbody> + <tr> + <td><a href="#searchnode" aria-label="searchnode">searchnode</a></td> + <td>No</td> + <td>Zero or one</td> + </tr> + </tbody> +</table> + +## searchnode + +Contained in [tuning](#tuning-proton), optional. Tune settings for search nodes in a content cluster - sub-elements: + +<table className="vespa-table"> + <thead> + <tr> + <th>Element</th> + <th>Required</th> + <th>Quantity</th> + </tr> + </thead> + <tbody> + <tr> + <td><a href="#lidspace" aria-label="lidspace">lidspace</a></td> + <td>No</td> + <td>Zero or one</td> + </tr> + <tr> + <td colSpan={3}></td> + </tr> + <tr> + <td><a href="#requestthreads" aria-label="requestthreads">requestthreads</a></td> + <td>No</td> + <td>Zero or one</td> + </tr> + <tr> + <td><a href="#flushstrategy" aria-label="flushstrategy">flushstrategy</a></td> + <td>No</td> + <td>Zero or one</td> + </tr> + <tr> + <td><a href="#initialize" aria-label="initialize">initialize</a></td> + <td>No</td> + <td>Zero or one</td> + </tr> + <tr> + <td><a href="#feeding" aria-label="feeding">feeding</a></td> + <td>No</td> + <td>Zero or one</td> + </tr> + <tr> + <td><a href="#index" aria-label="index">index</a></td> + <td>No</td> + <td>Zero or one</td> + </tr> + <tr> + <td><a href="#summary" aria-label="summary">summary</a></td> + <td>No</td> + <td>Zero or one</td> + </tr> + </tbody> +</table> + +```xml +<tuning> + <searchnode> + <lidspace></lidspace> + <requestthreads></requestthreads> + <flushstrategy></flushstrategy> + <initialize></initialize> + <feeding></feeding> + <index></index> + <summary></summary> + </searchnode> +</tuning> +``` + +## requestthreads + +Contained in [searchnode](#searchnode), optional. Tune the number of request threads used on a content node, see [thread-configuration](/en/performance/sizing-search#thread-configuration) for details. Sub-elements: + +<table className="vespa-table"> + <thead> + <tr> + <th>Element</th> + <th>Required</th> + <th>Default</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>**search**</td> + <td>Optional</td> + <td>**Vespa Cloud:** min(vcpu*4 + persearch - 1, vcpu*persearch) **Self-hosted:** 64.</td> + <td>Total size of the match engine thread pool. Together with <code>{`persearch`}</code>, this determines the maximum number of queries that can execute concurrently: <code>{`search / persearch`}</code>. See the <a href="/en/performance/sizing-search#thread-configuration" aria-label="Vespa serving scaling guide">Vespa serving scaling guide</a> for sizing guidance.</td> + </tr> + <tr> + <td>**persearch**</td> + <td>Optional</td> + <td>1</td> + <td>Maximum number of threads used per search. A higher value reduces the time queries spend in query evaluation, except time spent in ANN which is single-threaded. This number of threads is held for each query for the duration of the query, also when much of the time is spent on single-threaded operations. See the <a href="/en/performance/sizing-search" aria-label="Vespa serving scaling guide">Vespa serving scaling guide</a> for an introduction of using multiple threads per search per node to reduce query latency. Number of threads per search can be adjusted down per *rank-profile* using <a href="/en/reference/schemas/schemas#num-threads-per-search" aria-label="num-threads-per-search">num-threads-per-search</a>.</td> + </tr> + <tr> + <td>**summary**</td> + <td>Optional</td> + <td>**Vespa Cloud:** vcpu **Self-hosted:** 16</td> + <td>Number of summary threads.</td> + </tr> + </tbody> +</table> + +```xml +<requestthreads> + <search>64</search> + <persearch>1</persearch> + <summary>16</summary> +</requestthreads> +``` + +## flushstrategy + +Contained in [searchnode](#searchnode), optional. Tune the *native*\-strategy for flushing components to disk - a smaller number means more frequent flush: + +- *Memory gain* is how much memory can be freed by flushing a component +- *Disk gain* is how much disk space can be freed by flushing a component (typically by using compaction) + +Refer to [Proton maintenance jobs](/en/content/proton#proton-maintenance-jobs). Optional sub-elements: + +- `native`: + - `total` + - `maxmemorygain`: The total maximum memory gain (in bytes) for *all* components before running flush, default 4294967296 (4 GB) + - `diskbloatfactor`: Trigger flush if the total disk gain (in bytes) for *all* components is larger than the factor times current total disk usage, default 0.25 + - `component` + - `maxmemorygain`: The maximum memory gain (in bytes) by *a single* component before running flush, default 1073741824 (1 GB) + - `diskbloatfactor`: Trigger flush if the disk gain (in bytes) by *a single* component is larger than the given factor times the current disk usage by that component, default 0.25 + - `maxage`: The maximum age (in seconds) of unflushed content for a single component before running flush, default 111600 (31h) + - `transactionlog` + - `maxsize`: The total maximum size (in bytes) of [transaction logs](/en/content/proton#transaction-log) for all document types before running flush, default 21474836480 (20 GB) + - `conservative` + - `memory-limit-factor`: When [resource-limits (in proton)](#resource-limits-proton) for memory is reached, flush more often by downscaling `total.maxmemorygain` and `component.maxmemorygain`, default 0.5 + - `disk-limit-factor`: When [resource-limits (in proton)](#resource-limits-proton) for disk is reached, flush more often by downscaling `transactionlog.maxsize`, default 0.5 + +```xml +<flushstrategy> + <native> + <total> + <maxmemorygain>4294967296</maxmemorygain> + <diskbloatfactor>0.2</diskbloatfactor> + </total> + <component> + <maxmemorygain>1073741824</maxmemorygain> + <diskbloatfactor>0.2</diskbloatfactor> + <maxage>111600</maxage> + </component> + <transactionlog> + <maxsize>21474836480</maxsize> + </transactionlog> + <conservative> + <memory-limit-factor>0.5</memory-limit-factor> + <disk-limit-factor>0.5</disk-limit-factor> + </conservative> + </native> +</flushstrategy> +``` + +## initialize + +Contained in [searchnode](#searchnode), optional. Tune settings related to how the search node (proton) is initialized. Optional sub-elements: + +- `threads`: The number of initializer threads used for loading structures from disk at proton startup. The threads are shared between document databases when the value is larger than 0. Default value is the number of document databases + 1. + - When set to larger than 1, document databases are initialized in parallel + - When set to 1, document databases are initialized in sequence + - When set to 0, 1 separate thread is used per document database, and they are initialized in parallel. + +```xml +<initialize> + <threads>2</threads> +</initialize> +``` + +## lidspace + +Contained in [searchnode](#searchnode), optional. Tune settings related to how lidspace is managed. Optional sub-elements: + +- `max-bloat-factor`: Maximum bloat allowed before lidspace compaction is started. Compaction is moving a document from a high lid to a lower lid. Cost is similar to feeding a document and removing it. Also see description in [lidspace compaction maintenance job](/en/content/proton#lid-space-compaction). Default value is 0.01 or 1% of total lidspace. Will be increased to target of 0.50 or 50%. + +```xml +<lidspace> + <max-bloat-factor>0.5</max-bloat-factor> +</lidspace> +``` + +## feeding + +Contained in [searchnode](#searchnode), optional. Tune [proton](/en/content/proton) settings for feed operations. Optional sub-elements: + +- `concurrency`: A number between 0.0 and 1.0 that specifies the concurrency when handling feed operations, default 0.5. When set to 1.0, all cores on the cpu can be used for feeding. Changing this value requires a restart of the nodes to take effect. + Feed operations in this context also includes data migration operations inside a cluster, e.g., when resizing. Data migrations might therefore limit feeding capacity while running. All such feed operations run with the same priority. +- `niceness`: A number between 0.0 and 1.0 that specifies the niceness of the feeding threads, default 0.0 => not any nicer than anyone else. Increasing this number will reduce the priority of feeding compared to search. The real-world effect is hard to predict as the magic exists in the OS level scheduler. Changing this value requires a restart of the nodes to take effect. + +```xml +<feeding> + <concurrency>0.8</concurrency> + <niceness>0.5</niceness> +</feeding> +``` + +<Note> +**Note:** + +Queries can always use *all* available cores; concurrency caps how many can be used for writes and maintenance, which includes redistribution. +</Note> + +## index + +Contained in [searchnode](#searchnode), optional. Tune various aspect with the handling of disk and memory indexes. Optional sub-elements: + +- `io` + - `search`: Controls io read options used during search, `values={mmap,populate}`, default `mmap`. Using `populate` will eagerly touch all pages when index is loaded (after re-start or after index fusion is complete). +- `warmup` + - `time`: Specifies in seconds how long the index shall be warmed up before being switched in for serving. During warmup, it will receive queries and posting lists will be iterated, but results ignored as they are duplicates of the live index. This will pull in the most important ones in the cache. However, as warming up an index will occupy more memory, do not turn it on unless you suspect you need it. And always benchmark to see if it is worth it.<br/> + It's only potentially relevant for fields with indexing setting [index](/en/basics/schemas#document-fields), which have regular disk based indexes, and where the disk indexes are merged/fused in the background. When switching the index, warmup can be used. Also note that [state-v1-health](/en/reference/api/state-v1#state-v1-health) is independent of `warmup` - the node can be "up" before warmup. + - `unpack`: Controls whether all posting features are pulled in to the cache, or only the most important. ` values={true, false}`, default false. + +```xml +<index> + <io> + <search>mmap</search> + </io> + <warmup> + <time>60</time> + <unpack>true</unpack> + </warmup> +</index> +``` + +## removed-db + +Contained in [searchnode](#searchnode), optional. Tune various aspect of the db of removed documents. Optional sub-elements: + +- `prune` + - `age`: Specifies how long (in seconds) we must remember removed documents before we can prune them away. Default is 2 weeks. This sets the upper limit on how long a node can be down and still be accepted back in the system, without having the index wiped. There is no point in having this any higher than the age of the documents. If corpus is re-fed every day, there is no point in having this longer than 24 hours. + - `interval`: Specifies how often (in seconds) to prune old documents. Default is 3.36 hours (prune age / 100). No need to change default. Exposed here for reference and for testing. + +```xml +<removed-db> + <prune> + <age>86400</age> + </prune> +</removed-db> +``` + +## summary + +Contained in [searchnode](#searchnode), optional. Tune various aspect with the handling of document summary. Optional sub-elements: + +- `io` + - `read`: Controls io read options used during reading of stored documents. Values are `directio` `mmap` `populate`. Default is `mmap`. `populate` will do an eager mmap and touch all pages. +- `store` + - `cache`: Used to tune the cache used by the document store. Enabled by default, using up to 5% of available memory. + - `maxsize`: The maximum size of the cache in bytes. If set, it takes precedence over [maxsize-percent](#summary-store-cache-maxsize-percent). Default is unset. + - `maxsize-percent`: The maximum size of the cache in percent of available memory. Default is 5%. + - `compression` + - `type`: The compression type of the documents while in the cache. Possible values are , `none` `lz4` `zstd`. Default is `lz4` + - `level`: The compression level of the documents while in cache. Default is 6 + - `logstore`: Used to tune the actual document store implementation (log-based). + - `maxfilesize`: The maximum size (in bytes) per summary file on disk. Default value is 1GB. [document-store-compaction](/en/content/proton#document-store-compaction) + - `chunk` + - `maxsize`: Maximum size (in bytes) of a chunk. Default value is 64KB. + - `compression` + - `type`: Compression type for the documents, `none` `lz4` `zstd`. Default is `zstd`. + - `level`: Compression level for the documents. Default is 3. + +```xml expandable +<summary> + <io> + <read>directio</read> + </io> + <store> + <cache> + <maxsize-percent>5</maxsize-percent> + <compression> + <type>none</type> + </compression> + </cache> + <logstore> + <chunk> + <maxsize>16384</maxsize> + <compression> + <type>zstd</type> + <level>3</level> + </compression> + </chunk> + </logstore> + </store> +</summary> +``` + +## flush-on-shutdown + +Contained in [proton](#proton). Default value is true. If set to true, search nodes will flush a set of components (e.g. memory index, attributes) to disk before shutting down such that the time it takes to flush these components plus the time it takes to replay the [transaction log](/en/content/proton#transaction-log) after restart is as low as possible. The time it takes to replay the transaction log depends on the amount of data to replay, so by flushing, some components before restart the transaction log will be pruned, and we reduce the replay time significantly. Refer to [Proton maintenance jobs](/en/content/proton#proton-maintenance-jobs). + +## sync-transactionlog + +Contained in [proton](#proton). Default value is true. If true, the transactionlog is synced to disk after every write. This enables the transactionlog to survive power failures and kernel panic. The sync cost is amortized over multiple feed operations. The faster you feed the more operations it is amortized over. So with a local disk this is not known to be a performance issue. However, if using NAS (Network Attached Storage) like EBS on AWS one can see significant feed performance impact. For one particular case, turning off sync-transactionlog for EBS gave a 60x improvement. + +With sync-transactionlog turned off, the risk of losing data depends on the kernel's [sysctl settings.](https://www.kernel.org/doc/html/latest/admin-guide/sysctl/vm.html#dirty-background-bytes) For example, this is a common default: + +```bash +# sysctl -a +... +vm.dirty_expire_centisecs = 3000 +vm.dirty_ratio = 20 +vm.dirty_writeback_centisecs = 500 +... +``` + +With this configuration, the worse case scenario is to lose 35 seconds worth of transactionlog, but no more than 1/20 of the free memory. Because kernel flusher threads wake up every 5s (dirty\_writeback\_centisecs) and write data older than 30s (dirty\_expire\_centisecs) from memory to disk. But if un-synced data exceeds 1/20 of the free memory, the Vespa process will sync it (dirty\_ratio). + +The above also assumes that all copies of the data are lost at the same time **and** that kernels on all these nodes flush at the same time: realistic scenario only with one copy. + +Adjust these [sysctl settings](https://www.kernel.org/doc/html/latest/admin-guide/sysctl/vm.html#dirty-background-bytes) to manage the trade-off between data loss and performance. You'll see more in those kernel docs: for example, thresholds can be expressed in bytes. + +## resource-limits (in proton) + +Contained in [proton](#proton). Specifies resource limits used by proton to reject both external and internal write operations (on this content node) when a limit is reached. + +<Warning> +**Warning:** + +These proton limits should almost never be changed directly. Instead, change [resource-limits](#resource-limits) that controls when external write operations are blocked in the entire content cluster. Be aware of the risks of tuning resource limits as seen in the link. +</Warning> + +The local proton limits are derived from the cluster limits if not specified, using this formula: + +$$ +L_{p r o t o n} = L_{c l u s t e r} + \frac{1 - L_{c l u s t e r}}{2} +$$ + +<table className="vespa-table"> + <thead> + <tr> + <th>Element</th> + <th>Required</th> + <th>Value</th> + <th>Default</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>**disk**</td> + <td>optional</td> + <td>float [0, 1]</td> + <td>0.9</td> + <td>Fraction of total space on the disk partition used before put and update operations are rejected</td> + </tr> + <tr> + <td>**memory**</td> + <td>optional</td> + <td>float [0, 1]</td> + <td>0.9</td> + <td>Fraction of physical memory that can be resident memory in anonymous mapping by proton before put and update operations are rejected</td> + </tr> + </tbody> +</table> + +Example: + +```xml +<proton> + <resource-limits> + <disk>0.83</disk> + <memory>0.82</memory> +``` + +## search + +Contained in [content](#content), optional. Declares search configuration for this content cluster. Optional sub-elements are [query-timeout](#query-timeout), [visibility-delay](#visibility-delay) and [coverage](#coverage). + +## query-timeout + +Contained in [search](#search). Specifies the query timeout in seconds for queries against the search interface on the content nodes. The default is 0.5 (500ms), the max is 600.0. For query timeout also see the request parameter [timeout](/en/reference/api/query#timeout). + +**Note:** One can not override this value using the [timeout](/en/reference/api/query#timeout) request parameter. + +## visibility-delay + +Contained in [search](#search). Default 0, max 1, seconds. + +This setting controls the TTL caching for [parent-child](/en/schemas/parent-child) imported fields. See [feature tuning](/en/performance/feature-tuning#parent-child-and-search-performancemance). + +## coverage + +Contained in [search](#search). Declares search coverage configuration for this content cluster. Optional sub-elements are [minimum](#minimum), [min-wait-after-coverage-factor](#min-wait-after-coverage-factor) and [max-wait-after-coverage-factor](#max-wait-after-coverage-factor). Search coverage configuration controls how many nodes the query dispatcher process should wait for, trading search coverage versus search performance. + +## minimum + +Contained in [coverage](#coverage). Declares the minimum search coverage required before returning the results of a query. This number is in the range `[0, 1]`, with 0 being no coverage and 1 being full coverage. + +The default is 1; unless configured otherwise a query will not return until all search nodes have responded within the specified timeout. + +## min-wait-after-coverage-factor + +Contained in [coverage](#coverage). Declares the minimum time for a query to wait for full coverage once the declared [minimum](#minimum) has been reached. This number is a factor that is multiplied with the time remaining at the time of reaching minimum coverage. + +The default is 0; unless configured otherwise a query will return as soon as the minimum coverage has been reached, and the remaining search nodes appear to be lagging. + +## max-wait-after-coverage-factor + +Contained in [coverage](#coverage). Declares the maximum time for a query to wait for full coverage once the declared [minimum](#minimum) has been reached. This number is a factor that is multiplied with the time remaining at the time of reaching minimum coverage. + +The default is 1; unless configured otherwise a query is allowed to wait its full timeout for full coverage even after reaching the minimum. + +## tuning + +Contained in [content](#content), optional. Optional tuning parameters are: [bucket-splitting](#bucket-splitting), [min-node-ratio-per-group](#min-node-ratio-per-group), [cluster-controller](#cluster-controller), [dispatch](#dispatch-tuning), [distribution](#distribution_type), [max-document-size](#max-document-size), [merges](#merges), [persistence-threads](#persistence-threads) and [visitors](#visitors). + +## bucket-splitting + +Contained in [tuning](#tuning). The [bucket](/en/content/buckets) is the fundamental unit of distribution and management in a content cluster. Buckets are auto-split, no need to configure for most applications. + +<table className="vespa-table"> + <thead> + <tr> + <th>Attribute</th> + <th>Required</th> + <th>Value</th> + <th>Default</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>**max-documents**</td> + <td>optional</td> + <td>integer</td> + <td>1024</td> + <td>Maximum number of documents per content bucket. Buckets are split in two if they have more documents than this. Keep this value below 16K.</td> + </tr> + <tr> + <td>**max-size**</td> + <td>optional</td> + <td>integer</td> + <td>32MiB</td> + <td>Maximum size (in bytes) of a bucket. This is the sum of the serialized size of all documents kept in the bucket. Buckets are split in two if they have a larger size than this. Keep this value below 100 MiB.</td> + </tr> + <tr> + <td>**minimum-bits**</td> + <td>optional</td> + <td>integer</td> + <td></td> + <td>Override the ideal distribution bit count configured for this cluster. Prefer to use the <a href="#distribution_type" aria-label="distribution type">distribution type</a> setting instead if the default distribution bit count does not fit the cluster. This variable is intended for testing and to work around possible distribution bit issues. Most users should not need this option.</td> + </tr> + </tbody> +</table> + +## min-node-ratio-per-group + +<Warning> +**Important:** + +This is configuration for the cluster controller. Most users are normally looking for [min-active-docs-coverage](#min-active-docs-coverage) which controls how many nodes can be down before query load is routed to other groups. +</Warning> + +Contained in [tuning](#tuning). States a lower bound requirement on the ratio of nodes within *individual* [groups](#group) that must be online and able to accept traffic before the entire group is automatically taken out of service. Groups are automatically brought back into service when the availability of its nodes has been restored to a level equal to or above this limit. + +Elastic content clusters are often configured to use multiple groups for the sake of horizontal traffic scaling and/or data availability. The content distribution system will try to ensure a configured number of replicas is always present within a group in order to maintain data redundancy. If the number of available nodes in a group drops too far, it is possible for the remaining nodes in the group to not have sufficient capacity to take over storage and serving for the replicas they now must assume responsibility for. Such situations are likely to result in increased latencies and/or feed rejections caused by resource exhaustion. Setting this tuning parameter allows the system to instead automatically take down the remaining nodes in the group, allowing feed and query traffic to fail completely over to the remaining groups. + +Valid parameter is a decimal value in the range \[0, 1\]. Default is 0, which means that the automatic group out-of-service functionality will *not* automatically take effect. + +Example: assume a cluster has been configured with *n* groups of 4 nodes each and the following tuning config: + +```xml +<tuning> + <min-node-ratio-per-group>0.75</min-node-ratio-per-group> +</tuning> +``` + +This tuning allows for 1 node in a group to be down. If 2 or more nodes go down, all nodes in the group will be marked as down, letting the *n-1* remaining groups handle all the traffic. + +This configuration can be changed live as the system is running and altered limits will take effect immediately. + +## distribution (in tuning) + +Contained in [tuning](#tuning). Tune the distribution algorithm used in the cluster. + +<table className="vespa-table"> + <thead> + <tr> + <th>Attribute</th> + <th>Required</th> + <th>Value</th> + <th>Default</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>**type**</td> + <td>optional</td> + <td>loose | strict | legacy</td> + <td>loose</td> + <td>When the number of a nodes configured in a system changes over certain limits, the system will automatically trigger major redistributions of documents. This is to ensure that the number of buckets is appropriate for the number of nodes in the cluster. This enum value specifies how aggressive the system should be in triggering such distribution changes.<br/><br/> The default of <code>{`loose`}</code> strikes a balance between rarely altering the distribution of the cluster and keeping the skew in document distribution low. It is recommended that you use the default mode unless you have empirically observed that it causes too much skew in load or document distribution.<br/><br/> Note that specifying <code>{`minimum-bits`}</code> under <a href="#bucket-splitting" aria-label="bucket-splitting">bucket-splitting</a> overrides this setting and effectively "locks" the distribution in place.</td> + </tr> + </tbody> +</table> + +## max-document-size + +Contained in [tuning](#tuning). Specifies max document size in the content cluster, measured as the uncompressed size of a document operation arriving over the wire by the distributor service. The limit will be used for all document types. A document larger than this limit will be rejected by the distributor. Note that some document operations that don't contain the entire document, like [document updates](/en/writing/document-api-guide#document-updates) might increase the size of a document above this limit. See also [max-document-size](/en/reference/applications/services/container#document-api) for document api, which is probably what you want to configure, make sure to keep the value documented here larger. + +Valid values are numbers including a unit (e.g. *10MiB*) and the value must be between 1Mib and 2048 Mib (inclusive). Values will be rounded to nearest MiB, so using MiB as a unit is preferrable. It is strongly recommended to make sure this is not set too high, 10 MiB is a reasonable setting for most use cases, setting it above 100 MiB is not recommended, as allowing large documents might impact operations, e.g. when restarting nodes, moving documents between nodes etc. Default value is 128 MiB. + +Example: + +```xml +<tuning> + <max-document-size>10MiB</max-document-size> +</tuning> +``` + +## merges + +Contained in [tuning](#tuning). Defines throttling parameters for bucket merge operations. + +<table className="vespa-table"> + <thead> + <tr> + <th>Attribute</th> + <th>Required</th> + <th>Value</th> + <th>Default</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>**max-per-node**</td> + <td>optional</td> + <td>number</td> + <td></td> + <td>Maximum number of parallel active bucket merge operations.</td> + </tr> + <tr> + <td>**max-queue-size**</td> + <td>optional</td> + <td>number</td> + <td></td> + <td>Maximum size of the merge bucket queue, before reporting BUSY back to the distributors.</td> + </tr> + </tbody> +</table> + +## persistence-threads + +Contained in [tuning](#tuning). Defines the number of persistence threads per partition on each content node. A content node executes bucket operations against the persistence engine synchronously in each of these threads. 8 threads are used by default. Override with the **count** attribute. + +## visitors + +Contained in [tuning](#tuning). Tuning parameters for visitor operations. Might contain [max-concurrent](#max-concurrent). + +<table className="vespa-table"> + <thead> + <tr> + <th>Attribute</th> + <th>Required</th> + <th>Value</th> + <th>Default</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>**thread-count**</td> + <td>optional</td> + <td>number</td> + <td></td> + <td>The maximum number of threads in which to execute visitor operations. A higher number of threads may increase performance, but may use more memory.</td> + </tr> + <tr> + <td>**max-queue-size**</td> + <td>optional</td> + <td>number</td> + <td></td> + <td>Maximum size of the pending visitor queue, before reporting BUSY back to the distributors.</td> + </tr> + </tbody> +</table> + +## max-concurrent + +Contained in [visitors](#visitors). Defines how many visitors can be active concurrently on each storage node. The number allowed depends on priority - lower priority visitors should not block higher priority visitors completely. To implement this, specify a fixed and a variable number. The maximum active is calculated by adjusting the variable component using the priority, and adding the fixed component. + +<table className="vespa-table"> + <thead> + <tr> + <th>Attribute</th> + <th>Required</th> + <th>Value</th> + <th>Default</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>**fixed**</td> + <td>optional</td> + <td>number</td> + <td><a href="https://github.com/vespa-engine/vespa/blob/master/storage/src/vespa/storage/visiting/stor-visitor.def" aria-label="16">16</a></td> + <td>The fixed component of the maximum active count</td> + </tr> + <tr> + <td>**variable**</td> + <td>optional</td> + <td>number</td> + <td><a href="https://github.com/vespa-engine/vespa/blob/master/storage/src/vespa/storage/visiting/stor-visitor.def" aria-label="64">64</a></td> + <td>The variable component of the maximum active count</td> + </tr> + </tbody> +</table> + +## resource-limits + +Contained in [tuning](#tuning). Specifies resource limits used to decide whether external write operations should be blocked in the entire content cluster, based on the reported resource usage by content nodes. See [feed block](../../../writing/feed-block.html) for more details. + +<Warning> +**Warning:** + +The content nodes require resource headroom to handle extra documents as part of re-distribution during node failure, and spikes when running [maintenance jobs](/en/content/proton#proton-maintenance-jobs). Tuning these limits should be done with extreme care, and setting them too high might lead to permanent data loss. They are best left untouched, using the defaults, and cannot be set in Vespa Cloud. +</Warning> + +<table className="vespa-table"> + <thead> + <tr> + <th>Element</th> + <th>Required</th> + <th>Value</th> + <th>Default</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>**disk**</td> + <td>optional</td> + <td>float [0, 1]</td> + <td>0.8</td> + <td>Fraction of total space on the disk partition used on a content node before feed is blocked</td> + </tr> + <tr> + <td>**memory**</td> + <td>optional</td> + <td>float [0, 1]</td> + <td>0.8/0.75</td> + <td>Fraction of physical memory that can be resident memory in anonymous mapping on a content node before feed is blocked. Total physical memory is sampled as the minimum of <code>{`sysconf(_SC_PHYS_PAGES) * sysconf(_SC_PAGESIZE)`}</code> and the cgroup (v1 or v2) memory limit. Nodes with 8 Gib or less memory in Vespa Cloud has a limit of 0.75.</td> + </tr> + </tbody> +</table> + +Example - in the content tag: +```xml +<tuning> + <resource-limits> + <disk>0.78</disk> + <memory>0.77</memory> + </resource-limits> +</tuning> +``` + +## dispatch + +Contained in [tuning](#tuning). Tune the query dispatch behavior - child elements: + +<table className="vespa-table"> + <thead> + <tr> + <th>Element</th> + <th>Required</th> + <th>Value</th> + <th>Default</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>**max-hits-per-partition**</td> + <td>optional</td> + <td>Integer</td> + <td>No capping: Return all</td> + <td>Maximum number of hits to return from a content node. By default, a query returns the requested number of hits + offset from every content node to the container. The container orders the hits globally according to the query, then discards all hits beyond the number requested.<br/><br/> In a system with a large fan-out, this consumes network bandwidth and the container nodes easily network saturated. Containers will also sort and discard more hits than optimal.<br/><br/> When there are sufficiently many search nodes, assuming an even distribution of the hits, it suffices to only return a fraction of the request number of hits from each node. Note that changing this number will have global ordering impact. See *top-k-probability* below for improving performance with fewer hits.</td> + </tr> + <tr> + <td>**dispatch-policy</td> + <td>optional**</td> + <td>adaptive / best-of-random-2 / round-robin</td> + <td>adaptive</td> + <td>With <a href="/en/performance/sizing-search#data-distribution" aria-label="grouped distribution">grouped distribution</a>: Configure policy for choosing which group shall receive the next query request. Coverage requirements is considered when choosing a group. Note that multiphase requests that requires or benefits from hitting the same group in all phases are always hashed. <br/><br/>**adaptive**<br/> Measures latency, preferring lower latency groups, selecting group <code>{`i`}</code> has a probability proportional to 1 / (latency for group <code>{`i`}</code>). <br/><br/> **best-of-random-2** <br/>Selects 2 random groups and selects the one with the lowest latency. <br/><br/> **round-robin** <br/>Selects groups in a round-robin manner, giving fair distribution of queries to each group.</td> + </tr> + <tr> + <td>**prioritize-availability**</td> + <td>optional</td> + <td>Boolean</td> + <td>true</td> + <td>With <a href="/en/performance/sizing-search#data-distribution" aria-label="grouped distribution">grouped distribution</a>: If true, or by default, all groups that are within min-active-docs-coverage of the **median** of the document count of other groups will be used to service queries. If set to false, only groups within min-active-docs-coverage of the **max** document count will be used, with the consequence that full coverage is prioritized over availability when multiple groups are lacking content, since the remaining groups may not be able to service the full query load.</td> + </tr> + <tr> + <td>**min-active-docs-coverage**</td> + <td>optional</td> + <td>A float percentage</td> + <td>97</td> + <td>With <a href="/en/performance/sizing-search#data-distribution" aria-label="grouped distribution">grouped distribution</a>: The percentage of active documents a group must have, relative to the median across all groups in the content cluster, to be considered active for serving queries. Because of measurement timing differences, it is not advisable to tune this above 99 percent.</td> + </tr> + <tr> + <td>**top-k-probability**</td> + <td>optional</td> + <td>Double</td> + <td>0.9999</td> + <td>Probability that the top K hits will be the globally best. Based on this probability, the dispatcher will fetch enough hits from each node to achieve this. The only way to guarantee a probability of 1.0 is to fetch K hits from each partition. However, by reducing the probability from 1.0 to 0.99999, one can significantly reduce number of hits fetched and save both bandwidth and latency. The number of hits to fetch from each partition is computed as: <br/><br/> $$ q = \frac{k}{n} + q T \left(\right. p , 30 \left.\right) \times \sqrt{k \times \frac{1}{n} \times \left(\right. 1 - \frac{1}{n} \left.\right)} $$ <br/><br/> where qT is a Student's t-distribution. With n=10 partitions, k=200 hits and p=0.99999, only 45 hits per partition is needed, as opposed to 200 when p=1.0.<br/><br/> Use this option to reduce network and container cpu/memory in clusters with many nodes per group - see <a href="/en/performance/sizing-search" aria-label="Vespa Serving Scaling Guide">Vespa Serving Scaling Guide</a>.</td> + </tr> + </tbody> +</table> + +## cluster-controller + +Contained in [tuning](#tuning). Tuning parameters for the cluster controller managing this cluster - child elements: + +<table className="vespa-table"> + <thead> + <tr> + <th>Element</th> + <th>Required</th> + <th>Value</th> + <th>Default</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>**init-progress-time**</td> + <td>optional</td> + <td></td> + <td></td> + <td>If the initialization progress count have not been altered for this amount of seconds, the node is assumed to have deadlocked and is set down. Note that initialization may actually be prioritized lower now, so setting a low value here might cause false positives. Though if it is set down for wrong reason, when it will finish initialization and then be set up again.</td> + </tr> + <tr> + <td>**transition-time**</td> + <td>optional</td> + <td></td> + <td><a href="https://github.com/vespa-engine/vespa/blob/master/configdefinitions/src/vespa/fleetcontroller.def" aria-label="storage_transition_time">storage_transition_time</a> <a href="https://github.com/vespa-engine/vespa/blob/master/configdefinitions/src/vespa/fleetcontroller.def" aria-label="distributor_transition_time">distributor_transition_time</a></td> + <td>The transition time states how long (in seconds) a node will be in maintenance mode during what looks like a controlled restart. Keeping a node in maintenance mode during a restart allows a restart without the cluster trying to create new copies of all the data immediately. If the node has not started or got back up within the transition time, the node is set down, in which case, new full bucket copies will be created. Note separate defaults for distributor and storage (i.e. search) nodes.</td> + </tr> + <tr> + <td>**max-premature-crashes**</td> + <td>optional</td> + <td></td> + <td><a href="https://github.com/vespa-engine/vespa/blob/master/configdefinitions/src/vespa/fleetcontroller.def" aria-label="max_premature_crashes">max_premature_crashes</a></td> + <td>The maximum number of crashes allowed before a content node is permanently set down by the cluster controller. If the node has a stable up or down state for more than the *stable-state-period*, the crash count is reset. However, resetting the count will not re-enable the node again if it has been disabled - restart the cluster controller to reset.</td> + </tr> + <tr> + <td>**stable-state-period**</td> + <td>optional</td> + <td></td> + <td><a href="https://github.com/vespa-engine/vespa/blob/master/configdefinitions/src/vespa/fleetcontroller.def" aria-label="stable_state_time_period">stable_state_time_period</a></td> + <td>If a content node's state doesn't change for this many seconds, it's state is considered *stable*, clearing the premature crash count.</td> + </tr> + <tr> + <td>**min-distributor-up-ratio**</td> + <td>optional</td> + <td></td> + <td><a href="https://github.com/vespa-engine/vespa/blob/master/configdefinitions/src/vespa/fleetcontroller.def" aria-label="min_distributor_up_ratio">min_distributor_up_ratio</a></td> + <td>The minimum ratio of distributors that are required to be *up* for the cluster state to be *up*.</td> + </tr> + <tr> + <td>**min-storage-up-ratio**</td> + <td>optional</td> + <td></td> + <td><a href="https://github.com/vespa-engine/vespa/blob/master/configdefinitions/src/vespa/fleetcontroller.def" aria-label="min_storage_up_ratio">min_storage_up_ratio</a></td> + <td>The minimum ratio of content nodes that are required to be *up* for the cluster state to be *up*.</td> + </tr> + <tr> + <td>**groups-allowed-down-ratio**</td> + <td>optional</td> + <td></td> + <td><a href="https://github.com/vespa-engine/vespa/blob/master/configdefinitions/src/vespa/fleetcontroller.def" aria-label="groups-allowed-down-ratio">groups-allowed-down-ratio</a></td> + <td>A ratio for the number of content groups that are allowed to be down simultaneously. A value of 0.5 means that 50% of the groups are allowed to be down. The default is to allow only one group to be down at a time.</td> + </tr> + </tbody> +</table> diff --git a/en/reference/applications/services/docproc.html b/en/reference/applications/services/docproc.html deleted file mode 100644 index 6f450c378a..0000000000 --- a/en/reference/applications/services/docproc.html +++ /dev/null @@ -1,314 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "services.xml - document-processing" -redirect_from: -- /en/reference/services-docproc.html ---- - -<p> -This is the <a href="../../../applications/document-processors.html">document-processing</a> -reference in <a href="services.html">services.xml</a>: -</p> -<pre class="pre-config"> -<a href="container.html">container</a> - <a href="">document-processing [numnodesperclient, preferlocalnode, maxmessagesinqueue, maxqueuebytesize, - maxqueuewait, maxconcurrentfactor, documentexpansionfactor, containercorememory]</a> - <a href="container.html#include">include</a> - <a href="#documentprocessor">documentprocessor [class, bundle, id, idref, provides, before, after]</a> - <a href="#provides">provides</a> - <a href="processing.html#before">before</a> - <a href="processing.html#after">after</a> - <a href="#map">map</a> - <a href="#map">field [doctype, in-document, in-processor]</a> - <a href="#chain">chain [name, id, idref, inherits, excludes, documentprocessors]</a> - <a href="#map">map</a> - <a href="#map">field [doctype, in-document, in-processor]</a> - <a href="processing.html#inherits">inherits</a> - <a href="processing.html#chain">chain</a> - <a href="processing.html#exclude">exclude</a> - <a href="#documentprocessor">documentprocessor [class, bundle, id, idref, provides, before, after]</a> - <a href="#provides">provides</a> - <a href="processing.html#before">before</a> - <a href="processing.html#after">after</a> - <a href="#map">map</a> - <a href="#map">field [doctype, in-document, in-processor]</a> - <a href="processing.html#phase">phase [id, idref, before, after]</a> - <a href="processing.html#before">before</a> - <a href="processing.html#after">after</a> - <a href="#threadpool">threadpool</a> -</pre> -<p>The root element of the <em>document-processing</em> configuration model.</p> -<table class="table"> - <thead> - <tr><th>Attribute</th><th>Required</th><th>Value</th><th>Default</th><th>Description</th></tr> - </thead><tbody> - <tr> - <th>numnodesperclient</th> - <td>optional</td> - <td></td> - <td></td> - <td> - <p id="numnodesperclient"> - {% include deprecated.html content="Ignored and deprecated, will be removed in Vespa 9."%} - Set to some number below the amount of nodes in the cluster - to limit how many nodes a single client can connect to. - If you have <u>many</u> clients, this can reduce the memory usage - on both document-processing and client nodes. - </p> - </td> - </tr><tr> - <th>preferlocalnode</th> - <td>optional</td> - <td></td> - <td>false</td> - <td> - <p id="preferlocalnode"> - {% include deprecated.html content="Ignored and deprecated, will be removed in Vespa 9."%} - Set to always prefer sending to a document-processing node running on the same host as the client. - You should use this if you are running a client on each document-processing node. - </p> - </td> - </tr><tr> - <th>maxmessagesinqueue</th> - <td></td> - <td></td> - <td></td> - <td><p id="maxmessagesinqueue"></p></td> - </tr><tr> - <th>maxqueuebytesize</th> - <td></td> - <td></td> - <td></td> - <td> - <p id="maxqueuebytesize"> - {% include deprecated.html content="Ignored and deprecated, will be removed in Vespa 9."%} - </p> - </td> - </tr><tr> - <th>maxqueuewait</th> - <td>optional</td> - <td></td> - <td></td> - <td> - <p id="maxqueuewait"> - The maximum number of seconds a message should wait in queue before being processed. - Docproc will adapt its queue size to adhere to this. - If the queue is full, new messages will be replied to with SESSION_BUSY. - </p> - </td> - </tr><tr> - <th>maxconcurrentfactor</th> - <td></td> - <td></td> - <td></td> - <td><p id="maxconcurrentfactor"></p></td> - </tr><tr> - <th>documentexpansionfactor</th> - <td>optional</td> - <td></td> - <td></td> - <td><p id="documentexpansionfactor"></p></td> - </tr><tr> - <th>containercorememory</th> - <td></td> - <td></td> - <td></td> - <td><p id="containercorememory"></p></td> - </tr> - </tbody> -</table> - - - -<h2 id="document-processor-elements">Document Processor elements</h2> -<p> -<em>documentprocessor</em> elements are contained in <a href="#chain">docproc chain elements</a> -or in the <em>document-processing</em> root. -</p><p> -A documentprocessor element is either a document processor definition or document processor reference. -The rest of this section deals with document processor definitions; document processor references are -described in <a href="#docproc-chain-elements">docproc chain elements</a>. -</p><p> -A documentprocessor definition causes the creation of exactly one document processor instance. -This instance is set up according to the content of the documentprocessor element. -</p><p> -A documentprocessor definition contained in a docproc chain element defines an -<em>inner document processor</em>. Otherwise, it defines an <em>outer document processor.</em> -</p><p> -For inner documentprocessors, the name must be unique inside the docproc chain. -For outer documentprocessors, the component id must be unique. -An inner documentprocessor is not permitted to have the same name as an outer documentprocessor. -</p><p>Optional sub-elements:</p> -<ul> - <li>provides, a single name that should be added to the provides list</li> - <li>before, a single name that should be added to the before list</li> - <li>after, a single name that should be added to the after list</li> - <li>config (one or more)</li> -</ul> -<p> -For more information on provides, before and after, -see <a href="../../../applications/chaining.html">Chained components</a>. -</p> -<table class="table"> - <thead> - <tr><th>Attribute</th><th>Required</th><th>Value</th><th>Default</th><th>Description</th></tr> - </thead><tbody> - <tr> - <th>class</th><td></td><td></td><td></td><td></td> - </tr><tr> - <th>bundle</th><td></td><td></td><td></td><td></td> - </tr><tr> - <th>id</th><td>required</td><td></td><td></td> - <td><p id="id">The component id of the documentprocessor instance.</p></td> - </tr><tr> - <th>idref</th><td></td><td></td><td></td><td></td> - </tr><tr> - <th>provides</th><td>optional</td><td></td><td></td> - <td> - <p id="provides">A space-separated list of names that represents what this documentprocessor produces.</p> - </td> - </tr><tr> - <th>before</th><td>optional</td><td></td><td></td> - <td> - <p id="before"> - A space-separated list of phase or provided names. - Phases or documentprocessors providing these names will be placed later in the docproc chain - than this document processor. - </p> - </td> - </tr><tr> - <th>after</th><td>optional</td><td></td><td></td> - <td> - <p id="after"> - A space-separated list of phase or provided names. - Phases or documentprocessors providing these names will be placed earlier in the docproc chain - than this document processor. - </p> - </td> - </tr> - </tbody> -</table> - - -<h3 id="documentprocessor">documentprocessor</h3> -<p> -Defines a documentprocessor instance of a user specified class. -</p> -<pre> -<documentprocessor id="componentId" - class="className:versionSpecification" - bundle="the name in <artifactId> in pom.xml"> - ... -</documentprocessor> -</pre> -<table class="table"> - <thead> - <tr><th>Attribute</th><th>Required</th><th>Value</th><th>Default</th><th>Description</th></tr> - </thead><tbody> -<tr> - <th>id</th><td>required</td><td></td><td></td><td>The component id of the documentprocessor instance.</td> -</tr><tr> - <th>class</th><td>optional</td><td></td><td></td> - <td>A component specification containing the name of the class to instantiate - to create the document processor instance. If missing, copied from id.</td> -</tr><tr> - <th>bundle</th><td>optional</td><td></td><td></td> - <td>The bundle containing the class: The name in <artifactId> in pom.xml. - If a bundle is not specified, the bundle containing document processors bundled with Vespa is used.</td> -</tr> -</tbody> -</table> - - - -<h2 id="docproc-chain-elements">Docproc chain elements</h2> -<p> -Specifies how a docproc chain should be instantiated, -and how the contained document processors should be ordered. -</p> - - -<h3 id="chain">chain</h3> -<p> -Contained in <em>document-processing</em>. -Refer to the <a href="processing.html#chain">chain reference</a>. -Chains can <a href="processing.html#inherits">inherit</a> document processors from other chains -and use <a href="processing.html#phase">phases</a> for ordering. -Optional sub-elements: -</p> -<ul> - <li> - <a href="#documentprocessor">documentprocessor element</a> (one or more), - either a documentprocessor reference or documentprocessor definition. - If the name given for a documentprocessor matches an <em>outer documentprocessor</em>, - it is a <em>documentprocessor reference</em> - otherwise, it is a <em>documentprocessor definition</em>. - If it is a documentprocessor definition, it is also an implicit documentprocessor reference saying: use - <em>exactly</em> this documentprocessor. All these documentprocessor elements must have different name. - </li><li> - <a href="processing.html#phase">phase</a> (one or more). - </li><li> - <a href="../config-files.html#generic-configuration-in-services-xml">config</a> - (one or more - will apply to all <em>inner</em> documentprocessors in this docproc chain, unless - overridden by individual inner documentprocessors). - </li> -</ul> - - - -<h2 id="map">Map</h2> -<p> - Set up a field name mapping from the name(s) of field(s) in the input documents - to the names used in a deployed docproc. - The purpose is to reuse functionality without changing the field names. - The example below shows the configuration: -<pre> -<chain name="myChain"> - <map> - <field in-document="key" in-processor="id"/> - </map> - <documentprocessor type="CityDocProc"> - <map> - <field in-document="town" in-processor="city" doctype="restaurant"/> - </map> - </documentprocessor> - <documentprocessor type="CarDocProc"> - <map> - <field in-document="engine.cylinders" in-processor="cyl"/> - </map> - </documentprocessor> -</chain> -</pre> -<p> -In the example, a chain is deployed with 2 docprocs. -</p><p> -For the chain, a mapping from <em>key</em> to <em>id</em> is set up. -Imagine that some or all of the docprocs in the chain read and write to a field called <em>id</em>, -but we want this functionality to the document field <em>key</em>. -</p><p> -Furthermore, a similar thing is done for the <code>CityDocProc</code>: The docproc accesses the field -<em>city</em>, whereas it's called <em>town</em> in the feed. -The mapping only applies to the document type <em>restaurant</em>. -</p><p> -The <code>CarDocProc</code> accesses a field called <em>cyl</em>. -In this example this is mapped to the field <em>cylinders</em> of a struct <em>engine</em> -using a dotted notation. -</p> -<p> -If you specify mappings on different levels of the config (say both for a cluster and a docproc), -the mapping closest to the actual docproc will take precedence. -</p> - - -<h2 id="threadpool">threadpool</h2> -<p>Available since {% include version.html version="8.601.12" %}</p> -<p> - Specifies configuration for the thread pool used by document processor chains. All values scale with the number of vCPU—see the - <a href="../../../performance/container-tuning.html#container-worker-threads-example">container tuning example</a>. When all workers are busy, - new document processing requests are rejected. -</p> - -<h3 id="threadpool-threads">threads</h3> -<p> - Number of worker threads per vCPU. Default value is <code>1</code>. The pool runs with - <code>threads * vCPU</code> workers. -</p> diff --git a/en/reference/applications/services/docproc.mdx b/en/reference/applications/services/docproc.mdx new file mode 100644 index 0000000000..0b9dbd9b60 --- /dev/null +++ b/en/reference/applications/services/docproc.mdx @@ -0,0 +1,292 @@ +--- +title: "services.xml - document-processing" +sidebarTitle: "services.xml - docproc" +--- + +This is the [document-processing](/en/applications/document-processors) reference in [services.xml](/en/reference/applications/services/services): + +```yaml expandable +container + document-processing [numnodesperclient, preferlocalnode, maxmessagesinqueue, maxqueuebytesize, + maxqueuewait, maxconcurrentfactor, documentexpansionfactor, containercorememory] + include + documentprocessor [class, bundle, id, idref, provides, before, after] + provides + before + after + map + field [doctype, in-document, in-processor] + chain [name, id, idref, inherits, excludes, documentprocessors] + map + field [doctype, in-document, in-processor] + inherits + chain + exclude + documentprocessor [class, bundle, id, idref, provides, before, after] + provides + before + after + map + field [doctype, in-document, in-processor] + phase [id, idref, before, after] + before + after + threadpool +``` + +The root element of the *document-processing* configuration model. + +<table className="vespa-table"> + <thead> + <tr> + <th>Attribute</th> + <th>Required</th> + <th>Value</th> + <th>Default</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>**numnodesperclient**</td> + <td>optional</td> + <td></td> + <td></td> + <td><Danger>**Deprecated:** Ignored and deprecated, will be removed in Vespa 9.</Danger><br/><br/> Set to some number below the amount of nodes in the cluster to limit how many nodes a single client can connect to. If you have many clients, this can reduce the memory usage on both document-processing and client nodes.</td> + </tr> + <tr> + <td>**preferlocalnode**</td> + <td>optional</td> + <td></td> + <td>false</td> + <td><Danger>**Deprecated:** Ignored and deprecated, will be removed in Vespa 9.</Danger><br/><br/> Set to always prefer sending to a document-processing node running on the same host as the client. You should use this if you are running a client on each document-processing node.</td> + </tr> + <tr> + <td>**maxmessagesinqueue**</td> + <td></td> + <td></td> + <td></td> + <td></td> + </tr> + <tr> + <td>**maxqueuebytesize**</td> + <td></td> + <td></td> + <td></td> + <td><Danger>**Deprecated:** Ignored and deprecated, will be removed in Vespa 9.</Danger></td> + </tr> + <tr> + <td>**maxqueuewait**</td> + <td>optional</td> + <td></td> + <td></td> + <td>The maximum number of seconds a message should wait in queue before being processed. Docproc will adapt its queue size to adhere to this. If the queue is full, new messages will be replied to with SESSION_BUSY.</td> + </tr> + <tr> + <td>**maxconcurrentfactor**</td> + <td></td> + <td></td> + <td></td> + <td></td> + </tr> + <tr> + <td>**documentexpansionfactor**</td> + <td>optional</td> + <td></td> + <td></td> + <td></td> + </tr> + <tr> + <td>**containercorememory**</td> + <td></td> + <td></td> + <td></td> + <td></td> + </tr> + </tbody> +</table> + +## Document Processor elements + +*documentprocessor* elements are contained in [docproc chain elements](#chain) or in the *document-processing* root. + +A documentprocessor element is either a document processor definition or document processor reference. The rest of this section deals with document processor definitions; document processor references are described in [docproc chain elements](#docproc-chain-elements). + +A documentprocessor definition causes the creation of exactly one document processor instance. This instance is set up according to the content of the documentprocessor element. + +A documentprocessor definition contained in a docproc chain element defines an *inner document processor*. Otherwise, it defines an *outer document processor.* + +For inner documentprocessors, the name must be unique inside the docproc chain. For outer documentprocessors, the component id must be unique. An inner documentprocessor is not permitted to have the same name as an outer documentprocessor. + +Optional sub-elements: + +- provides, a single name that should be added to the provides list +- before, a single name that should be added to the before list +- after, a single name that should be added to the after list +- config (one or more) + +For more information on provides, before and after, see [Chained components](/en/applications/chaining). + +<table className="vespa-table"> + <thead> + <tr> + <th>Attribute</th> + <th>Required</th> + <th>Value</th> + <th>Default</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>class</td> + <td></td> + <td></td> + <td></td> + <td></td> + </tr> + <tr> + <td>bundle</td> + <td></td> + <td></td> + <td></td> + <td></td> + </tr> + <tr> + <td>id</td> + <td>required</td> + <td></td> + <td></td> + <td>The component id of the documentprocessor instance.</td> + </tr> + <tr> + <td>idref</td> + <td></td> + <td></td> + <td></td> + <td></td> + </tr> + <tr> + <td>provides</td> + <td>optional</td> + <td></td> + <td></td> + <td>A space-separated list of names that represents what this documentprocessor produces.</td> + </tr> + <tr> + <td>before</td> + <td>optional</td> + <td></td> + <td></td> + <td>A space-separated list of phase or provided names. Phases or documentprocessors providing these names will be placed later in the docproc chain than this document processor.</td> + </tr> + <tr> + <td>after</td> + <td>optional</td> + <td></td> + <td></td> + <td>A space-separated list of phase or provided names. Phases or documentprocessors providing these names will be placed earlier in the docproc chain than this document processor.</td> + </tr> + </tbody> +</table> + +### documentprocessor + +Defines a documentprocessor instance of a user specified class. + +```xml +<documentprocessor id="componentId" + class="className:versionSpecification" + bundle="the name in <artifactId> in pom.xml"> + ... +</documentprocessor> +``` + +<table className="vespa-table"> + <thead> + <tr> + <th>Attribute</th> + <th>Required</th> + <th>Value</th> + <th>Default</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>id</td> + <td>required</td> + <td></td> + <td></td> + <td>The component id of the documentprocessor instance.</td> + </tr> + <tr> + <td>class</td> + <td>optional</td> + <td></td> + <td></td> + <td>A component specification containing the name of the class to instantiate to create the document processor instance. If missing, copied from id.</td> + </tr> + <tr> + <td>bundle</td> + <td>optional</td> + <td></td> + <td></td> + <td>The bundle containing the class: The name in <code>{`<artifactId>`}</code> in pom.xml. If a bundle is not specified, the bundle containing document processors bundled with Vespa is used.</td> + </tr> + </tbody> +</table> + +## Docproc chain elements + +Specifies how a docproc chain should be instantiated, and how the contained document processors should be ordered. + +### chain + +Contained in *document-processing*. Refer to the [chain reference](/en/reference/applications/services/processing#chain). Chains can [inherit](/en/reference/applications/services/processing#inherits) document processors from other chains and use [phases](/en/reference/applications/services/processing#phase) for ordering. Optional sub-elements: + +- [documentprocessor element](#documentprocessor) (one or more), either a documentprocessor reference or documentprocessor definition. If the name given for a documentprocessor matches an *outer documentprocessor*, it is a *documentprocessor reference* - otherwise, it is a *documentprocessor definition*. If it is a documentprocessor definition, it is also an implicit documentprocessor reference saying: use *exactly* this documentprocessor. All these documentprocessor elements must have different name. +- [phase](/en/reference/applications/services/processing#phase) (one or more). +- [config](/en/reference/applications/config-files#generic-configuration-in-services-xml) (one or more - will apply to all *inner* documentprocessors in this docproc chain, unless overridden by individual inner documentprocessors). + +## Map + +Set up a field name mapping from the name(s) of field(s) in the input documents to the names used in a deployed docproc. The purpose is to reuse functionality without changing the field names. The example below shows the configuration: + +```xml +<chain name="myChain"> + <map> + <field in-document="key" in-processor="id"/> + </map> + <documentprocessor type="CityDocProc"> + <map> + <field in-document="town" in-processor="city" doctype="restaurant"/> + </map> + </documentprocessor> + <documentprocessor type="CarDocProc"> + <map> + <field in-document="engine.cylinders" in-processor="cyl"/> + </map> + </documentprocessor> +</chain> +``` + +In the example, a chain is deployed with 2 docprocs. + +For the chain, a mapping from *key* to *id* is set up. Imagine that some or all of the docprocs in the chain read and write to a field called *id*, but we want this functionality to the document field *key*. + +Furthermore, a similar thing is done for the `CityDocProc`: The docproc accesses the field *city*, whereas it's called *town* in the feed. The mapping only applies to the document type *restaurant*. + +The `CarDocProc` accesses a field called *cyl*. In this example this is mapped to the field *cylinders* of a struct *engine* using a dotted notation. + +If you specify mappings on different levels of the config (say both for a cluster and a docproc), the mapping closest to the actual docproc will take precedence. + +## threadpool + +Available since `Vespa 8.601.12` + +Specifies configuration for the thread pool used by document processor chains. All values scale with the number of vCPU—see the [container tuning example](/en/performance/container-tuning#container-worker-threads-example). When all workers are busy, new document processing requests are rejected. + +### threads + +Number of worker threads per vCPU. Default value is `1`. The pool runs with `threads * vCPU` workers. diff --git a/en/reference/applications/services/http.html b/en/reference/applications/services/http.html deleted file mode 100644 index 16f286d903..0000000000 --- a/en/reference/applications/services/http.html +++ /dev/null @@ -1,516 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "services.xml - http" -redirect_from: -- /en/reference/services-http.html ---- - -<p> -This is the reference for the <code>http</code> subelement of -<a href="container.html">container</a> in <a href="services.html">services.xml</a>. -The http block is used to configure http servers and filters - -when this element is present, the default http server is disabled. -</p> -<pre class="pre-config"> -http - <a href="#server">server [id, port]</a> - <a href=#ssl>ssl</a> - <a href=#private-key-file>private-key-file</a> - <a href=#certificate-file>certificate-file</a> - <a href=#ca-certificates-file>ca-certificates-file</a> - <a href=#client-authentication>client-authentication</a> - <a href=#protocols>protocols</a> - <a href=#cipher-suites>cipher-suites</a> - <a href="#ssl-provider">ssl-provider [class, bundle]</a> - <a href="#filtering">filtering</a> - <a href="#filter">filter [id, class, bundle, provides, before, after]</a> - <a href="#provides">provides</a> - <a href="#before">before</a> - <a href="#after">after</a> - <a href="#filter-config">filter-config</a> - <a href="#chain">request-/response-chain [id, inherits, excludes]</a> - <a href="#binding">binding</a> - <a href="#filter">filter [id, class, bundle, provides, before, after]</a> - <a href="#provides">provides</a> - <a href="#before">before</a> - <a href="#after">after</a> - <a href="#filter-config">filter-config</a> - <a href="#inherits">inherits</a> - <a href="#inheritedchain">chain</a> - <a href="#exclude">exclude</a> - <a href="#phase">phase [id, before, after]</a> - <a href="#before">before</a> - <a href="#after">after</a> -</pre> -<p> -Most elements takes optional <a href="../config-files.html#generic-configuration-in-services-xml">config</a> elements, -see example in <a href="#server">server</a>. -</p> -<p> -Note: To bind the search handler port (i.e. the handler for queries), -refer to <a href="search.html#binding">search bindings</a>. -</p> -<p>Example:</p> -<pre> -<http> - <server id="server1" port="8080" /> - <server id="server2" port="9000" /> - - <filtering> - <filter id="request-filter1" class="com.yahoo.test.RequestFilter1" /> - <filter id="response-filter1" class="com.yahoo.test.ResponseFilter1" /> - - <request-chain id="test-request-chain"> - <binding>http://*/*</binding> - <filter id="request-filter1"/> - <filter id="request-filter2" class="com.yahoo.test.RequestFilter2" /> - </request-chain> - - <response-chain id="test-response-chain"> - <binding>http://*:8080/*</binding> - <binding>http://*:9000/path</binding> - <filter id="response-filter1"/> - <filter id="response-filter2" class="com.yahoo.test.ResponseFilter2" /> - </response-chain> - </filtering> -</http> -</pre> - - - -<h2 id="server">server</h2> -<p> -The definition of a http server. -Configure the server using -<a href="https://github.com/vespa-engine/vespa/blob/master/container-disc/src/main/resources/configdefinitions/jdisc.http.jdisc.http.connector.def"> - jdisc.http.connector.def</a>. -<table class="table"> - <thead> - <tr><th>Attribute</th><th>Required</th><th>Value</th><th>Default</th><th>Description</th></tr> - </thead><tbody> - <tr><th>id</th> - <td>required</td> - <td>string</td> - <td></td> - <td><p id="server.id">The component ID</p></td></tr> - <tr><th>port</th> - <td>optional</td> - <td>number</td> - <td>The web services port of the - <a href="/en/operations/self-managed/files-processes-and-ports.html#environment-variables">environment variables</a></td> - <td><p id="server.port">Server port</p></td></tr> - <tr><th>default-request-chain</th> - <td>optional</td> - <td>string</td> - <td></td> - <td><p id="server.default-request-chain">The default request chain to use for unmatched requests</p></td></tr> - <tr><th>default-response-chain</th> - <td>optional</td> - <td>string</td> - <td></td> - <td><p id="server.default-response-chain">The default response chain to use for unmatched requests</p></td></tr> - </tbody> -</table> -<p>Example:</p> -<pre> -<server id="server1" port="8080"> - <config name="jdisc.http.connector"> - <idleTimeout>90</idleTimeout> - </config> -</server> -</pre> - - - -<h2 id="ssl">ssl</h2> -<p> - Setup TLS on HTTP server using credentials provided in PEM format. -</p> - - - -<h2 id="private-key-file">private-key-file</h2> -<p> - Path to private key file in PEM format. -</p> - - - -<h2 id="certificate-file">certificate-file</h2> -<p> - Path to certificate file in PEM format. -</p> - - - -<h2 id="ca-certificates-file">ca-certificates-file</h2> -<p> - Path to CA certificates file in PEM format. -</p> - - - -<h2 id="client-authentication">client-authentication</h2> -<p> - Client authentication. Supported values: <i>disabled</i>, <i>want</i> or <i>need</i>. -</p> - - - -<h2 id="protocols">protocols</h2> -<p> - Comma-separated list of TLS protocol versions to enable. Example: <i>TLSv1.2,TLSv1.3</i>. -</p> - - - -<h2 id="cipher-suites">cipher-suites</h2> -<p> - Comma-separated list of TLS cipher suites to enable. The specified ciphers must be supported by JDK installation. Example: <i>TLS_AES_256_GCM_SHA384,TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384</i>. -</p> - - - -<h2 id="ssl-provider">ssl-provider</h2> -<p> - Setup TLS on the HTTP server through a programmatic Java interface. - The specified class must implement the - <a href="https://javadoc.io/doc/com.yahoo.vespa/container-disc/latest/com/yahoo/jdisc/http/SslProvider.html"> - SslProvider</a> interface. -</p> - <table class="table"> - <thead> - <tr><th>Attribute</th><th>Required</th><th>Value</th><th>Default</th><th>Description</th></tr> - </thead><tbody> - <tr><th>class</th> - <td>required</td> - <td>string</td> - <td></td> - <td><p id="ssl-provider.class">The class name</p></td></tr> - <tr><th>bundle</th> - <td>required</td> - <td>string</td> - <td></td> - <td><p id="ssl-provider.bundle">The bundle name</p></td></tr> - </tbody> - </table> - - - -<h2 id="filtering">filtering</h2> -<p> -<code>filtering</code> is for configuring http filter chains. Sub-elements: -</p> -<ul> - <li><a href="#filter">filter</a></li> - <li><a href="#chain">request-chain</a></li> - <li><a href="#chain">response-chain</a></li> -</ul> -<p>Example:</p> -<pre> -<filtering> - <filter id="request-filter1" class="com.yahoo.test.RequestFilter1" /> - <filter id="response-filter1" class="com.yahoo.test.ResponseFilter1" /> - - <request-chain id="test-request-chain"> - <binding>http://*/</binding> - <filter id="request-filter1"/> - <filter id="request-filter2" class="com.yahoo.test.RequestFilter2" /> - </request-chain> - - <response-chain id="test-response-chain"> - <binding>http://*/</binding> - <filter id="response-filter1"/> - <filter id="response-filter2" class="com.yahoo.test.ResponseFilter2" /> - </response-chain> -</filtering> -</pre> -<table class="table"> - <thead> - <tr><th>Attribute</th><th>Required</th><th>Value</th><th>Default</th><th>Description</th></tr> - </thead><tbody> - <tr><th>strict-mode</th> - <td>optional</td> - <td>boolean</td> - <td>true</td> - <td> - <p id="filtering.strict-mode"> - When set to true, all requests must match a filter. - For any requests not matching, an HTTP 403 response is returned. - </p> - </td></tr> -</tbody> -</table> - - - -<h2 id="binding">binding</h2> -<p> - Specifies that requests/responses matching the given URI pattern - should be sent through the <a href="#chain">request-chain/response-chain</a>. -</p> - -<h2 id="filter">filter</h2> -<p> - The definition of a single filter, for referencing when defining chains. - If a single filter is to be used in different chains, - it is cleaner to define it directly under <code>http</code> - and then refer to it with <code>id</code>, - than defining it inline separately for each chain. - The following filter types are supported: -</p> -<ul> - <li>RequestFilter</li> - <li>ResponseFilter</li> - <li>SecurityRequestFilter</li> - <li>SecurityResponseFilter</li> -</ul> -<p> -Security[Request/Response]Filters are automatically wrapped in Security[Request/Response]FilterChains. -This makes them behave like regular Request/Response filters with respect to chaining. -</p> -<table class="table"> - <thead> - <tr><th>Attribute</th><th>Required</th><th>Value</th><th>Default</th><th>Description</th></tr> - </thead><tbody> - <tr><th>id</th> - <td>required</td> - <td>string</td> - <td></td> - <td><p id="filter.id">The component ID</p></td></tr> - <tr><th>class</th> - <td>optional</td> - <td>string</td> - <td>id</td> - <td><p id="filter.class">The class of the component, defaults to id</p></td></tr> - <tr><th>bundle</th> - <td>optional</td> - <td>string</td> - <td>id or class</td> - <td> - <p id="filter.bundle">The bundle to load the component from, defaults to class or id (if no class is given)</p> - </td></tr> - <tr><th>before</th> - <td>optional</td> - <td>string</td> - <td></td> - <td> - <p id="filter.before">Space separated list of phases and/or filters which should succeed this phase</p> - </td></tr> - <tr><th>class</th> - <td>optional</td> - <td>string</td> - <td>id</td> - <td> - <p id="filter.after">Space separated list of phases and/or filters which should precede this phase</p> - </td></tr> - </tbody> -</table> -<p>Sub-elements:</p> -<ul> - <li><a href="#provides">provides</a></li> - <li><a href="#before">before</a></li> - <li><a href="#after">after</a></li> - <li><a href="#filter-config">filter-config</a></li> -</ul> -<p>Example:</p> -<pre> -<filter id="filter2" class="com.yahoo.test.Filter2"/> -</pre> - - - -<h2 id="provides">provides</h2> -<p> - A name provided by a filter for phases and other filters to use as dependencies. - Contained in <a href="#filter">filter</a> - and <a href="#filter">filter</a> (in chain). -</p> - - - -<h2 id="before">before</h2> -<p> - The name of a phase or filter which should succeed this phase or filter. - <code>before</code> tags may be used if it is necessary to define filters or phases - which always should succeed this filter or phase in a chain. - In other words, the phase or filter defined is placed <em>before</em> name in the tag. - Contained in <a href="#filter">filter</a>, - <a href="#filter">filter</a> (in chain) - and <a href="#phase">phase</a>. -</p> - - - -<h2 id="after">after</h2> -<p> - The name of a phase or filter which should precede this phase or filter. - <code>after</code> tags may be used if it is necessary to define filters or phases - which always should precede this filter or phase in a chain. - In other words, the phase or filter defined is placed - <em>after</em> the name in the tag. - Contained in <a href="#filter">filter</a>, - <a href="#filter">filter</a> (in chain) - and <a href="#phase">phase</a>. Example: -</p> -<pre> -<filter id="filterauth" class="com.yahoo.test.auth"> - <provides>Authorization</provides> - <before>LastFilters</before> - <after>Earlyfilters</after> -</filter> -</pre> - - - -<h2 id="filter-config">filter-config</h2> -<p> - Only used to configure filters that are configured with - <code>com.yahoo.jdisc.http.filter.security.FilterConfig</code>. - This is the case for all filters provided in JDisc bundles. -</p> - - - -<h2 id="chain">request-chain/response-chain</h2> -<p> -Defines a chain of request filters or response filters, respectively. -A chain is a set ordered by dependencies. -Dependencies are expressed through phases, which may depend upon other phases, or filters. -</p> -<table class="table"> - <thead> - <tr><th>Attribute</th><th>Required</th><th>Value</th><th>Default</th><th>Description</th></tr> - </thead><tbody> - <tr><th>inherits</th> - <td></td> - <td>string</td> - <td></td> - <td> - <p id="chain.inherits">A space separated list of chains this chain should include the contents of</p> - </td></tr> - <tr><th>excludes</th> - <td></td> - <td>string</td> - <td></td> - <td> - <p id="chain.excludes"> - A space separated list of filters (contained in an inherited chain) this chain should not include - </p> - </td></tr> - </tbody> -</table> -<p>Sub-elements:</p> -<ul> - <li><a href="#binding">binding</a></li> - <li><a href="#filter">filter</a>. Refer to or define a filter. - <em>config</em> or <em>filter-config</em> can not be added to references, only filter definitions.</li> - <li><a href="#inherits">inherits</a></li> - <li><a href="#phase">phase</a></li> -</ul> -<p>Examples:</p> -<pre> -<request-chain id="default-request-filters"> - <binding>http://*/*</binding> - <filter id="com.yahoo.test.RequestFilter"/> -</request-chain> -<response-chain id="response-filters"> - <binding>http://*:8080/*</binding> - <binding>http://*:9000/path</binding> - <filter id="com.yahoo.test.ResponseFilter"/> -</response-chain> -</pre> - - - -<h2 id="inherits">inherits</h2> -<p> - Wrapper element for information about which chains, if any, a chain should inherit, and how. - Contained in <a href="#chain">request-chain</a> and - <a href="#chain">response-chain</a>. - Sub-elements: -</p> -<ul> - <li>(inherited) <a href="#inheritedchain">chain</a></li> - <li><a href="#exclude">exclude</a></li> -</ul> - - - -<h2 id="inheritedchain">(inherited) chain</h2> -<p> - The ID of a chain which this chain should inherit, i.e. include all filters and phases from. - Use multiple <code>chain</code> tags if it is necessary to combine the filters from multiple chains. - Contained in <a href="#inherits">inherits</a>. -</p> - - - -<h2 id="exclude">exclude</h2> -<p> - A filter the chain under definition should exclude from the chain or chains it inherits from. - Use multiple <code>exclude</code> tags to exclude multiple filters. - Contained in <a href="#inherits">inherits</a>. Example: -</p> -<pre> -<request-chain id="demo"> - <inherits> - <chain>idOfSomeInheritedChain</chain> - <exclude>idOfUnwantedFilter</exclude> - <exclude>idOfYetAnotherUnwantedFilter</exclude> - </inherits> - <filter id="filter2" class="com.yahoo.test.Filter2"/> -</request-chain> -</pre> - - - -<h2 id="phase">phase</h2> -<p>Defines a phase, which is a checkpoint to help order filters. -Filters and other phases may depend on a phase to be able to make assumptions about the order of filters. -Contained in <a href="#chain">chain</a>. -</p> -<table class="table"> - <thead> - <tr><th>Attribute</th><th>Required</th><th>Value</th><th>Default</th><th>Description</th></tr> - </thead><tbody> - <tr><th>id</th> - <td>required</td> - <td>string</td> - <td></td> - <td> - <p id="phase.id"> - The ID, or name, which other phases and filters may depend upon as a - <a href="#before">successor</a> or <a href="#after">predecessor</a> - </p> - </td></tr> - <tr><th>before</th> - <td>optional</td> - <td>string</td> - <td></td> - <td> - <p id="phase.before">Space separated list of phases and/or filters which should succeed this phase</p> - </td></tr> - <tr><th>after</th> - <td>optional</td> - <td>string</td> - <td></td> - <td> - <p id="phase.after">Space separated list of phases and/or filters which should precede this phase</p> - </td></tr> - </tbody> -</table> -<p>Sub-elements:</p> -<ul> - <li><a href="#before">before</a></li> - <li><a href="#after">after</a></li> -</ul> -<p>Example:</p> -<pre> -<request-chain id="demo"> - <phase id="CheckpointName"> - <before>Authorization</before> - </phase> - <filter id="filter2" class="com.yahoo.test.Filter2"/> -</request-chain> -</pre> diff --git a/en/reference/applications/services/http.mdx b/en/reference/applications/services/http.mdx new file mode 100644 index 0000000000..73a159ea9a --- /dev/null +++ b/en/reference/applications/services/http.mdx @@ -0,0 +1,469 @@ +--- +title: "services.xml - http" +--- + +This is the reference for the `http` subelement of [container](/en/reference/applications/services/container) in [services.xml](/en/reference/applications/services/services). The http block is used to configure http servers and filters - when this element is present, the default http server is disabled. + +```yml exoandable +http + server [id, port] + ssl + private-key-file + certificate-file + ca-certificates-file + client-authentication + protocols + cipher-suites + ssl-provider [class, bundle] + filtering + filter [id, class, bundle, provides, before, after] + provides + before + after + filter-config + request-/response-chain [id, inherits, excludes] + binding + filter [id, class, bundle, provides, before, after] + provides + before + after + filter-config + inherits + chain + exclude + phase [id, before, after] + before + after +``` + +Most elements takes optional [config](/en/reference/applications/config-files#generic-configuration-in-services-xml) elements, see example in [server](#server). + +Note: To bind the search handler port (i.e. the handler for queries), refer to [search bindings](/en/reference/applications/services/search#binding). + +Example: + +```xml +<http> + <server id="server1" port="8080" /> + <server id="server2" port="9000" /> + + <filtering> + <filter id="request-filter1" class="com.yahoo.test.RequestFilter1" /> + <filter id="response-filter1" class="com.yahoo.test.ResponseFilter1" /> + + <request-chain id="test-request-chain"> + <binding>http://*/*</binding> + <filter id="request-filter1"/> + <filter id="request-filter2" class="com.yahoo.test.RequestFilter2" /> + </request-chain> + + <response-chain id="test-response-chain"> + <binding>http://*:8080/*</binding> + <binding>http://*:9000/path</binding> + <filter id="response-filter1"/> + <filter id="response-filter2" class="com.yahoo.test.ResponseFilter2" /> + </response-chain> + </filtering> +</http> +``` + +## server + +The definition of a http server. Configure the server using [jdisc.http.connector.def](https://github.com/vespa-engine/vespa/blob/master/container-disc/src/main/resources/configdefinitions/jdisc.http.jdisc.http.connector.def). + +<table className="vespa-table"> + <thead> + <tr> + <th>Attribute</th> + <th>Required</th> + <th>Value</th> + <th>Default</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>**id**</td> + <td>required</td> + <td>string</td> + <td></td> + <td>The component ID</td> + </tr> + <tr> + <td>**port**</td> + <td>optional</td> + <td>number</td> + <td>The web services port of the <a href="/en/operations/self-managed/files-processes-and-ports#environment-variables" aria-label="environment variables">environment variables</a></td> + <td>Server port</td> + </tr> + <tr> + <td>**default-request-chain**</td> + <td>optional</td> + <td>string</td> + <td></td> + <td>The default request chain to use for unmatched requests</td> + </tr> + <tr> + <td>**default-response-chain**</td> + <td>optional</td> + <td>string</td> + <td></td> + <td>The default response chain to use for unmatched requests</td> + </tr> + </tbody> +</table> + +Example: + +```xml +<server id="server1" port="8080"> + <config name="jdisc.http.connector"> + <idleTimeout>90</idleTimeout> + </config> +</server> +``` + +## ssl + +Setup TLS on HTTP server using credentials provided in PEM format. + +## private-key-file + +Path to private key file in PEM format. + +## certificate-file + +Path to certificate file in PEM format. + +## ca-certificates-file + +Path to CA certificates file in PEM format. + +## client-authentication + +Client authentication. Supported values: *disabled*, *want* or *need*. + +## protocols + +Comma-separated list of TLS protocol versions to enable. Example: *TLSv1.2,TLSv1.3*. + +## cipher-suites + +Comma-separated list of TLS cipher suites to enable. The specified ciphers must be supported by JDK installation. Example: *TLS\_AES\_256\_GCM\_SHA384,TLS\_ECDHE\_ECDSA\_WITH\_AES\_256\_GCM\_SHA384*. + +## ssl-provider + +Setup TLS on the HTTP server through a programmatic Java interface. The specified class must implement the [SslProvider](https://javadoc.io/doc/com.yahoo.vespa/container-disc/latest/com/yahoo/jdisc/http/SslProvider.html) interface. + +<table className="vespa-table"> + <thead> + <tr> + <th>Attribute</th> + <th>Required</th> + <th>Value</th> + <th>Default</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>**class**</td> + <td>required</td> + <td>string</td> + <td></td> + <td>The class name</td> + </tr> + <tr> + <td>**bundle**</td> + <td>required</td> + <td>string</td> + <td></td> + <td>The bundle name</td> + </tr> + </tbody> +</table> + +## filtering + +`filtering` is for configuring http filter chains. Sub-elements: + +- [filter](#filter) +- [request-chain](#chain) +- [response-chain](#chain) + +Example: + +```xml +<filtering> + <filter id="request-filter1" class="com.yahoo.test.RequestFilter1" /> + <filter id="response-filter1" class="com.yahoo.test.ResponseFilter1" /> + + <request-chain id="test-request-chain"> + <binding>http://*/</binding> + <filter id="request-filter1"/> + <filter id="request-filter2" class="com.yahoo.test.RequestFilter2" /> + </request-chain> + + <response-chain id="test-response-chain"> + <binding>http://*/</binding> + <filter id="response-filter1"/> + <filter id="response-filter2" class="com.yahoo.test.ResponseFilter2" /> + </response-chain> +</filtering> +``` + +<table className="vespa-table"> + <thead> + <tr> + <th>Attribute</th> + <th>Required</th> + <th>Value</th> + <th>Default</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>**strict-mode**</td> + <td>optional</td> + <td>boolean</td> + <td>true</td> + <td>When set to true, all requests must match a filter. For any requests not matching, an HTTP 403 response is returned.</td> + </tr> + </tbody> +</table> + +## binding + +Specifies that requests/responses matching the given URI pattern should be sent through the [request-chain/response-chain](#chain). + +## filter + +The definition of a single filter, for referencing when defining chains. If a single filter is to be used in different chains, it is cleaner to define it directly under `http` and then refer to it with `id`, than defining it inline separately for each chain. The following filter types are supported: + +- RequestFilter +- ResponseFilter +- SecurityRequestFilter +- SecurityResponseFilter + +Security\[Request/Response\]Filters are automatically wrapped in Security\[Request/Response\]FilterChains. This makes them behave like regular Request/Response filters with respect to chaining. + +<table className="vespa-table"> + <thead> + <tr> + <th>Attribute</th> + <th>Required</th> + <th>Value</th> + <th>Default</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>**id**</td> + <td>required</td> + <td>string</td> + <td></td> + <td>The component ID</td> + </tr> + <tr> + <td>**class**</td> + <td>optional</td> + <td>string</td> + <td>id</td> + <td>The class of the component, defaults to id</td> + </tr> + <tr> + <td>**bundle**</td> + <td>optional</td> + <td>string</td> + <td>id or class</td> + <td>The bundle to load the component from, defaults to class or id (if no class is given)</td> + </tr> + <tr> + <td>**before**</td> + <td>optional</td> + <td>string</td> + <td></td> + <td>Space separated list of phases and/or filters which should succeed this phase</td> + </tr> + <tr> + <td>**class**</td> + <td>optional</td> + <td>string</td> + <td>id</td> + <td>Space separated list of phases and/or filters which should precede this phase</td> + </tr> + </tbody> +</table> + +Sub-elements: + +- [provides](#provides) +- [before](#before) +- [after](#after) +- [filter-config](#filter-config) + +Example: + +```bash +<filter id="filter2" class="com.yahoo.test.Filter2"/> +``` + +## provides + +A name provided by a filter for phases and other filters to use as dependencies. Contained in [filter](#filter) and [filter](#filter) (in chain). + +## before + +The name of a phase or filter which should succeed this phase or filter. `before` tags may be used if it is necessary to define filters or phases which always should succeed this filter or phase in a chain. In other words, the phase or filter defined is placed *before* name in the tag. Contained in [filter](#filter), [filter](#filter) (in chain) and [phase](#phase). + +## after + +The name of a phase or filter which should precede this phase or filter. `after` tags may be used if it is necessary to define filters or phases which always should precede this filter or phase in a chain. In other words, the phase or filter defined is placed *after* the name in the tag. Contained in [filter](#filter), [filter](#filter) (in chain) and [phase](#phase). Example: + +```xml +<filter id="filterauth" class="com.yahoo.test.auth"> + <provides>Authorization</provides> + <before>LastFilters</before> + <after>Earlyfilters</after> +</filter> +``` + +## filter-config + +Only used to configure filters that are configured with `com.yahoo.jdisc.http.filter.security.FilterConfig`. This is the case for all filters provided in JDisc bundles. + +## request-chain/response-chain + +Defines a chain of request filters or response filters, respectively. A chain is a set ordered by dependencies. Dependencies are expressed through phases, which may depend upon other phases, or filters. + +<table className="vespa-table"> + <thead> + <tr> + <th>Attribute</th> + <th>Required</th> + <th>Value</th> + <th>Default</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>**inherits**</td> + <td></td> + <td>string</td> + <td></td> + <td>A space separated list of chains this chain should include the contents of</td> + </tr> + <tr> + <td>**excludes**</td> + <td></td> + <td>string</td> + <td></td> + <td>A space separated list of filters (contained in an inherited chain) this chain should not include</td> + </tr> + </tbody> +</table> + +Sub-elements: + +- [binding](#binding) +- [filter](#filter). Refer to or define a filter. *config* or *filter-config* can not be added to references, only filter definitions. +- [inherits](#inherits) +- [phase](#phase) + +Examples: + +```xml +<request-chain id="default-request-filters"> + <binding>http://*/*</binding> + <filter id="com.yahoo.test.RequestFilter"/> +</request-chain> +<response-chain id="response-filters"> + <binding>http://*:8080/*</binding> + <binding>http://*:9000/path</binding> + <filter id="com.yahoo.test.ResponseFilter"/> +</response-chain> +``` + +## inherits + +Wrapper element for information about which chains, if any, a chain should inherit, and how. Contained in [request-chain](#chain) and [response-chain](#chain). Sub-elements: + +- (inherited) [chain](#inheritedchain) +- [exclude](#exclude) + +## (inherited) chain + +The ID of a chain which this chain should inherit, i.e. include all filters and phases from. Use multiple `chain` tags if it is necessary to combine the filters from multiple chains. Contained in [inherits](#inherits). + +## exclude + +A filter the chain under definition should exclude from the chain or chains it inherits from. Use multiple `exclude` tags to exclude multiple filters. Contained in [inherits](#inherits). Example: + +```xml +<request-chain id="demo"> + <inherits> + <chain>idOfSomeInheritedChain</chain> + <exclude>idOfUnwantedFilter</exclude> + <exclude>idOfYetAnotherUnwantedFilter</exclude> + </inherits> + <filter id="filter2" class="com.yahoo.test.Filter2"/> +</request-chain> +``` + +## phase + +Defines a phase, which is a checkpoint to help order filters. Filters and other phases may depend on a phase to be able to make assumptions about the order of filters. Contained in [chain](#chain). + +<table className="vespa-table"> + <thead> + <tr> + <th>Attribute</th> + <th>Required</th> + <th>Value</th> + <th>Default</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>**id**</td> + <td>required</td> + <td>string</td> + <td></td> + <td>The ID, or name, which other phases and filters may depend upon as a <a href="#before" aria-label="successor">successor</a> or <a href="#after" aria-label="predecessor">predecessor</a></td> + </tr> + <tr> + <td>**before**</td> + <td>optional</td> + <td>string</td> + <td></td> + <td>Space separated list of phases and/or filters which should succeed this phase</td> + </tr> + <tr> + <td>**after**</td> + <td>optional</td> + <td>string</td> + <td></td> + <td>Space separated list of phases and/or filters which should precede this phase</td> + </tr> + </tbody> +</table> + +Sub-elements: + +- [before](#before) +- [after](#after) + +Example: + +```xml +<request-chain id="demo"> + <phase id="CheckpointName"> + <before>Authorization</before> + </phase> + <filter id="filter2" class="com.yahoo.test.Filter2"/> +</request-chain> +``` diff --git a/en/reference/applications/services/processing.html b/en/reference/applications/services/processing.html deleted file mode 100644 index 018b8f180b..0000000000 --- a/en/reference/applications/services/processing.html +++ /dev/null @@ -1,395 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "services.xml - processing" -redirect_from: -- /en/reference/services-processing.html ---- - -<p> - This document explains the syntax and semantics of the <code>processing</code> subelement of the - <a href="container.html"><code>container</code></a> part of <code>services.xml</code>. - <code>processing</code> is for configuring a pure - <a href="../../../applications/processing.html">request-response processing</a> application, - with no particular dependency to search or query handling. - The processing block is used to configure <a href="../../../applications/processing.html">processors</a>: -</p> -<pre class="pre-config"> -processing - <a href="#binding">binding</a> - <a href="#processor">processor [id, class, bundle, provides, before, after]</a> - <a href="#provides">provides</a> - <a href="#before">before</a> - <a href="#after">after</a> - <a href="#renderer">renderer [id, class, bundle]</a> - <a href="#chain">chain [id, inherits, excludes]</a> - <a href="#processor-in-chain">processor [idref, id, class, bundle, provides, before, after]</a> - <a href="#provides">provides</a> - <a href="#before">before</a> - <a href="#after">after</a> - <a href="#inherits">inherits</a> - <a href="#chain">chain</a> - <a href="#exclude">exclude</a> - <a href="#phase">phase [id, before, after]</a> - <a href="#before">before</a> - <a href="#after">after</a> -</pre> -<p>Example:</p> -<pre> -<processing> - <processor id="processor1" class="com.yahoo.test.Processor1" /> - <chain id="default"> - <processor idref="processor1"/> - <processor id="processor2" class="com.yahoo.test.Processor2"/> - </chain> - <renderer id="renderer1" class="com.yahoo.test.Renderer1" /> -</processing> -</pre> - - - -<h2 id="binding">binding</h2> -<p> -The URI to map the ProcessingHandler to. The default binding is -<code>http://*/processing/*</code>. Multiple elements are allowed. -Example: -</p> -<pre> -<binding>http://*/processing/*</binding> -</pre> - - - -<h2 id="processor">processor</h2> -<p> - The definition of a single processor, for referencing when defining chains. - If a single processor is to be used in multiple chains, - it is cleaner to define it directly under <code>processing</code> - and then refer to it with <code>idref</code>, - than defining it inline separately for each chain. -</p> -<table class="table"> - <thead> - <tr><th>Attribute</th><th>Required</th><th>Value</th><th>Default</th><th>Description</th></tr> - </thead><tbody> - <tr><th>id</th> - <td>required</td> - <td>string</td> - <td></td> - <td><p id="processor.id">The component ID</p></td></tr> - <tr><th>class</th> - <td>optional</td> - <td>string</td> - <td></td> - <td><p id="processor.class">The class of the component, defaults to id</p></td></tr> - <tr><th>bundle</th> - <td>optional</td> - <td>string</td> - <td></td> - <td> - <p id="processor.bundle"> - The bundle to load the component from, defaults to class or id (if no class is given) - </p> - </td></tr> - <tr><th>before</th> - <td>optional</td> - <td>string</td> - <td></td> - <td> - <p id="processor.before"> - Space separated list of phases and/or processors which should succeed this processor - </p> - </td></tr> - <tr><th>after</th> - <td>optional</td> - <td>string</td> - <td></td> - <td> - <p id="processor.after"> - Space separated list of phases and/or processors which should precede this processor - </p> - </td></tr> - </tbody> -</table> -<p>Example:</p> -<pre> -<processor id="processor2" class="com.yahoo.test.Processor2"/> -</pre> - - - -<h2 id="renderer">renderer</h2> -<p> - The definition of a renderer, for use by a Handler. -</p> -<table class="table"> - <thead> - <tr><th>Attribute</th><th>Required</th><th>Value</th><th>Default</th><th>Description</th></tr> - </thead><tbody> - <tr><th>id</th> - <td>required</td> - <td>string</td> - <td></td> - <td><p id="renderer.id">The component ID</p></td></tr> - <tr><th>class</th> - <td>optional</td> - <td>string</td> - <td></td> - <td><p id="renderer.class">The class of the component, defaults to id</p></td></tr> - <tr><th>bundle</th> - <td>optional</td> - <td>string</td> - <td></td> - <td> - <p id="renderer.bundle"> - The bundle to load the component from, defaults to class or id (if no class is given) - </p> - </td></tr> - </tbody> -</table> -<p>Example:</p> -<pre> -<renderer id="renderer1" class="com.yahoo.test.Renderer1" /> -</pre> - - - -<h2 id="processor-in-chain">processor (in chain)</h2> -<p> -Reference to or inline definition of a processor in a chain. -If inlining, same as <a href="#processor">processor</a> - if referring to, -use <em>idref</em> attribute: -</p> -<table class="table"> - <thead> - <tr><th>Attribute</th><th>Required</th><th>Value</th><th>Default</th><th>Description</th></tr> - </thead><tbody> - <tr><th>idref</th> - <td></td> - <td>string</td> - <td></td> - <td><p id="processor-in-chain.idref">Reference to the definition of this processor.</p></td></tr> - </tbody> -</table> -<p>Example:</p> -<pre> -<chain id="default"> - <processor idref="processor2" /> -</chain> -</pre> - - - -<h2 id="provides">provides</h2> -<p> - A name provided by a processor for phases and other processors to use as dependencies. Example: -</p> -<pre> -<provides>IntentAnalysis</provides> -</pre> - - - -<h2 id="chain">chain</h2> -<p> - An element for defining a chain of - <a href="processing.html">processors</a> / - <a href="search.html">searchers </a> / - <a href="docproc.html">document processors</a> (i.e. components). - A chain is a set ordered by dependencies. - Dependencies are expressed through phases, which may depend upon other phases, or components. - For an incoming request from the network, the chain named <em>default</em> will be invoked. - Refer to <a href="../../../applications/chaining.html">Chained components</a> for a guide. - Requires one of <em>id</em> or <em>idref</em>. -</p> -<p> - Searcher, Document processing and Processing chains can be modified at runtime without restarts. - Modification includes adding/removing processors in chains and changing names of chains and processors. - Make the change and <a href="/en/basics/applications.html#deploying-applications">deploy</a>. - Some changes require a container restart, refer to - <a href="/en/applications/document-processors.html#reconfiguring-document-processing">reconfiguring document processing</a>. -</p> -<table class="table"> - <thead> - <tr><th>Attribute</th><th>Required</th><th>Value</th><th>Default</th><th>Description</th></tr> - </thead><tbody> - <tr><th>idref</th> - <td></td> - <td>string</td> - <td></td> - <td> - <p id="chain.idref"> - A reference to a defined chain. Mutually exclusive with <em>id</em>. - If <em>idref</em> is used, no other attributes apply. - </p> - </td></tr> - <tr><th>id</th> - <td>required</td> - <td>string</td> - <td></td> - <td><p id="chain.id">The chain ID. Required unless <em>idref</em> is used</p></td></tr> - <tr><th>inherits</th> - <td>optional</td> - <td>string</td> - <td></td> - <td> - <p id="chain.inherits"> - A space-separated list of chains this chain should include the contents of - - see example below. - </p> - </td></tr> - <tr><th>excludes</th> - <td>optional</td> - <td>string</td> - <td></td> - <td> - <p id="chain.excludes"> - A space-separated list of processors (contained in an inherited chain) this chain should not include. - The exclusion is done before any consolidation of component references when inheriting chains. Example: - </p> -<pre> -<chain id="demo" inherits="idOfInheritedChain" - excludes="idOfProc1 idOfProc2"> - <processor id="proc2" class="com.yahoo.test.Proc2"/> -</chain> -</pre> - </td></tr> - <tr><th>class</th> - <td>optional</td> - <td>string</td> - <td></td> - <td><p id="chain.class"></p><!-- ToDo --></td></tr> - <tr><th>name</th> - <td></td> - <td></td> - <td></td> - <td><p id="chain.name"></p><!-- ToDo: copied from docproc.html - is name used? --></td></tr> - <tr><th>documentprocessors</th> - <td></td> - <td></td> - <td></td> - <td><p id="chain.documentprocessors"></p> - <!-- ToDo: copied from docproc.html - is documentprocessors used? --></td></tr> - </tbody> -</table> - - - -<h2 id="inherits">inherits</h2> -<p> - Inherit from one or more parent chain(s). -</p> -<!-- ToDo: make example --> -<p> -When a search chain inherits from another search chain, it subsumes the phases -and the <em>searcher references</em> (both implicitly and explicitly defined) from the -parent chain. -</p><p> -<!-- ToDo: rewrite!!! moved from searcher/docproc documents --> -If two or more inherited component references have the same name, a new component -specification matching those will be used instead. If that is not possible, an -error will be signaled (i.e. if the version specifications can not be -consolidated or if they require component definitions from different chains). -</p><p> -<!-- ToDo Rewrite - moved from search docproc documents --> -The component references determines which instances are used in the resulting chain instance. -</p><p> -A component reference is a component specification that says: there shall be -exactly one component in this chain with the given name, -and this component must match the version specification. -</p><p> -A component reference <em>overrides</em> any inherited -component references with the same name (i.e. the inherited ones are ignored). -</p><p> -If several components match a given component reference, the newest -(as determined by the version) is used. -</p> - - - -<h2 id="exclude">exclude</h2> -<p> - Exclude components from inherited chains. -</p> -<!-- ToDo: make example --> - - -<h2 id="phase">phase</h2> -<p> - Defines a phase, which is a named checkpoint to help order components inside a chain. - Components and other phases may depend on a phase to be able to make assumptions about the order of components. - Refer to the <a href="../../../applications/chaining.html">Chained components</a> guide. -</p> -<table class="table"> - <thead> - <tr><th>Attribute</th><th>Required</th><th>Value</th><th>Default</th><th>Description</th></tr> - </thead><tbody> - <tr><th>id</th> - <td>required</td> - <td>string</td> - <td></td> - <td> - <p id="phase.id"> - The ID, or name, which other phases and processors may depend upon as a - <a href="#before">successor</a> or <a href="#after">predecessor</a>. - </p> - </td></tr> - <tr><th>before</th> - <td>optional</td> - <td>string</td> - <td></td> - <td> - <p id="phase.before">Space-separated list of phases and/or processors which should succeed this phase</p> - </td></tr> - <tr><th>after</th> - <td>optional</td> - <td>string</td> - <td></td> - <td> - <p id="phase.after">Space-separated list of phases and/or processors which should precede this phase</p> - </td></tr> - </tbody> -</table> -<p>Optional sub-elements:</p> -<ul> - <li>before: same as the attribute</li> - <li>after: same as the attribute</li> -</ul> -<p>These are equivalent:</p> -<pre> -<phase id="name"> - <before>phaseName1</before> - <after>phaseName2</after> -</phase> - -<phase id="name" before="phaseName1" after="phaseName2" /> -</pre> -<p>Example:</p> -<pre> -<chain id="demo"> - <phase id="CheckpointName"> - <before>IntentAnalysis</before> - <after>OtherAnalysis</after> - </phase> - <processor id="processor2" class="com.yahoo.test.Processor2"/> -</chain> -</pre> - - - -<h2 id="before">before</h2> -<p> - The name of a phase or component which should succeed this phase or component. - Multiple <code>before</code> elements can be used - to define multiple components or phases which always should succeed this component or phase in a chain. - In other words, the phase or component defined is placed <em>before</em> name in the element. -</p> - - - -<h2 id="after">after</h2> -<p> - The name of a phase or component which should precede this phase or component. - Multiple <code>after</code> elements can be used - to define multiple component or phases which always should precede this component or phase in a chain. - In other words, the phase or component defined is placed <em>after</em> the name in the element. -</p> diff --git a/en/reference/applications/services/processing.mdx b/en/reference/applications/services/processing.mdx new file mode 100644 index 0000000000..a747ae738c --- /dev/null +++ b/en/reference/applications/services/processing.mdx @@ -0,0 +1,357 @@ +--- +title: "services.xml - processing" +--- + + +This document explains the syntax and semantics of the `processing` subelement of the [`container`](/en/reference/applications/services/container) part of `services.xml`. `processing` is for configuring a pure [request-response processing](/en/applications/processing) application, with no particular dependency to search or query handling. The processing block is used to configure [processors](/en/applications/processing): + +```yaml expandable +processing + binding + processor [id, class, bundle, provides, before, after] + provides + before + after + renderer [id, class, bundle] + chain [id, inherits, excludes] + processor [idref, id, class, bundle, provides, before, after] + provides + before + after + inherits + chain + exclude + phase [id, before, after] + before + after +``` + +Example: + +```xml +<processing> + <processor id="processor1" class="com.yahoo.test.Processor1" /> + <chain id="default"> + <processor idref="processor1"/> + <processor id="processor2" class="com.yahoo.test.Processor2"/> + </chain> + <renderer id="renderer1" class="com.yahoo.test.Renderer1" /> +</processing> +``` + +## binding + +The URI to map the ProcessingHandler to. The default binding is `http://*/processing/*`. Multiple elements are allowed. Example: + +```xml +<binding>http://*/processing/*</binding> +``` + +## processor + +The definition of a single processor, for referencing when defining chains. If a single processor is to be used in multiple chains, it is cleaner to define it directly under `processing` and then refer to it with `idref`, than defining it inline separately for each chain. + +<table className="vespa-table"> + <thead> + <tr> + <th>Attribute</th> + <th>Required</th> + <th>Value</th> + <th>Default</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>**id**</td> + <td>required</td> + <td>string</td> + <td></td> + <td>The component ID</td> + </tr> + <tr> + <td>**class**</td> + <td>optional</td> + <td>string</td> + <td></td> + <td>The class of the component, defaults to id</td> + </tr> + <tr> + <td>**bundle**</td> + <td>optional</td> + <td>string</td> + <td></td> + <td>The bundle to load the component from, defaults to class or id (if no class is given)</td> + </tr> + <tr> + <td>**before**</td> + <td>optional</td> + <td>string</td> + <td></td> + <td>Space separated list of phases and/or processors which should succeed this processor</td> + </tr> + <tr> + <td>**after**</td> + <td>optional</td> + <td>string</td> + <td></td> + <td>Space separated list of phases and/or processors which should precede this processor</td> + </tr> + </tbody> +</table> + +Example: + +```xml +<processor id="processor2" class="com.yahoo.test.Processor2"/> +``` + +## renderer + +The definition of a renderer, for use by a Handler. + +<table className="vespa-table"> + <thead> + <tr> + <th>Attribute</th> + <th>Required</th> + <th>Value</th> + <th>Default</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>**id**</td> + <td>required</td> + <td>string</td> + <td></td> + <td>The component ID</td> + </tr> + <tr> + <td>**class**</td> + <td>optional</td> + <td>string</td> + <td></td> + <td>The class of the component, defaults to id</td> + </tr> + <tr> + <td>**bundle**</td> + <td>optional</td> + <td>string</td> + <td></td> + <td>The bundle to load the component from, defaults to class or id (if no class is given)</td> + </tr> + </tbody> +</table> + +Example: + +```xml +<renderer id="renderer1" class="com.yahoo.test.Renderer1" /> +``` + +## processor (in chain) + +Reference to or inline definition of a processor in a chain. If inlining, same as [processor](#processor) - if referring to, use *idref* attribute: + +<table className="vespa-table"> + <thead> + <tr> + <th>Attribute</th> + <th>Required</th> + <th>Value</th> + <th>Default</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>**idref**</td> + <td></td> + <td>string</td> + <td></td> + <td>Reference to the definition of this processor.</td> + </tr> + </tbody> +</table> + +Example: + +```xml +<chain id="default"> + <processor idref="processor2" /> +</chain> +``` + +## provides + +A name provided by a processor for phases and other processors to use as dependencies. Example: + +```xml +<provides>IntentAnalysis</provides> +``` + +## chain + +An element for defining a chain of [processors](/en/reference/applications/services/processing) / [searchers](/en/reference/applications/services/search) / [document processors](/en/reference/applications/services/docproc) (i.e. components). A chain is a set ordered by dependencies. Dependencies are expressed through phases, which may depend upon other phases, or components. For an incoming request from the network, the chain named *default* will be invoked. Refer to [Chained components](/en/applications/chaining) for a guide. Requires one of *id* or *idref*. + +Searcher, Document processing and Processing chains can be modified at runtime without restarts. Modification includes adding/removing processors in chains and changing names of chains and processors. Make the change and [deploy](/en/basics/applications#deploying-applications). Some changes require a container restart, refer to [reconfiguring document processing](/en/applications/document-processors#reconfiguring-document-processing). + +<table className="vespa-table"> + <thead> + <tr> + <th>Attribute</th> + <th>Required</th> + <th>Value</th> + <th>Default</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>**idref**</td> + <td></td> + <td>string</td> + <td></td> + <td>A reference to a defined chain. Mutually exclusive with *id*. If *idref* is used, no other attributes apply.</td> + </tr> + <tr> + <td>**id**</td> + <td>required</td> + <td>string</td> + <td></td> + <td>The chain ID. Required unless *idref* is used</td> + </tr> + <tr> + <td>**inherits**</td> + <td>optional</td> + <td>string</td> + <td></td> + <td>A space-separated list of chains this chain should include the contents of - see example below.</td> + </tr> + <tr> + <td>**excludes**</td> + <td>optional</td> + <td>string</td> + <td></td> + <td>A space-separated list of processors (contained in an inherited chain) this chain should not include. The exclusion is done before any consolidation of component references when inheriting chains. Example:<br/><br/><code>{`<chain id="demo" inherits="idOfInheritedChain"`}</code><br/> <code>{`excludes="idOfProc1 idOfProc2">`}</code><br/><code>{`<processor id="proc2" class="com.yahoo.test.Proc2"/>`}</code><br/> <code>{`</chain>`}</code></td> + </tr> + <tr> + <td>**class**</td> + <td>optional</td> + <td>string</td> + <td></td> + <td></td> + </tr> + <tr> + <td>**name**</td> + <td></td> + <td></td> + <td></td> + <td></td> + </tr> + <tr> + <td>**documentprocessors**</td> + <td></td> + <td></td> + <td></td> + <td></td> + </tr> + </tbody> +</table> + +## inherits + +Inherit from one or more parent chain(s). + +When a search chain inherits from another search chain, it subsumes the phases and the *searcher references* (both implicitly and explicitly defined) from the parent chain. + +If two or more inherited component references have the same name, a new component specification matching those will be used instead. If that is not possible, an error will be signaled (i.e. if the version specifications can not be consolidated or if they require component definitions from different chains). + +The component references determines which instances are used in the resulting chain instance. + +A component reference is a component specification that says: there shall be exactly one component in this chain with the given name, and this component must match the version specification. + +A component reference *overrides* any inherited component references with the same name (i.e. the inherited ones are ignored). + +If several components match a given component reference, the newest (as determined by the version) is used. + +## exclude + +Exclude components from inherited chains. + +## phase + +Defines a phase, which is a named checkpoint to help order components inside a chain. Components and other phases may depend on a phase to be able to make assumptions about the order of components. Refer to the [Chained components](/en/applications/chaining) guide. + +<table className="vespa-table"> + <thead> + <tr> + <th>Attribute</th> + <th>Required</th> + <th>Value</th> + <th>Default</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>**id**</td> + <td>required</td> + <td>string</td> + <td></td> + <td>The ID, or name, which other phases and processors may depend upon as a <a href="#before" aria-label="successor">successor</a> or <a href="#after" aria-label="predecessor">predecessor</a>.</td> + </tr> + <tr> + <td>**before**</td> + <td>optional</td> + <td>string</td> + <td></td> + <td>Space-separated list of phases and/or processors which should succeed this phase</td> + </tr> + <tr> + <td>**after**</td> + <td>optional</td> + <td>string</td> + <td></td> + <td>Space-separated list of phases and/or processors which should precede this phase</td> + </tr> + </tbody> +</table> + +Optional sub-elements: + +- before: same as the attribute +- after: same as the attribute + +These are equivalent: + +```xml +<phase id="name"> + <before>phaseName1</before> + <after>phaseName2</after> +</phase> + +<phase id="name" before="phaseName1" after="phaseName2" /> +``` + +Example: + +```xml +<chain id="demo"> + <phase id="CheckpointName"> + <before>IntentAnalysis</before> + <after>OtherAnalysis</after> + </phase> + <processor id="processor2" class="com.yahoo.test.Processor2"/> +</chain> +``` + +## before + +The name of a phase or component which should succeed this phase or component. Multiple `before` elements can be used to define multiple components or phases which always should succeed this component or phase in a chain. In other words, the phase or component defined is placed *before* name in the element. + +## after + +The name of a phase or component which should precede this phase or component. Multiple `after` elements can be used to define multiple component or phases which always should precede this component or phase in a chain. In other words, the phase or component defined is placed *after* the name in the element. diff --git a/en/reference/applications/services/search.html b/en/reference/applications/services/search.html deleted file mode 100644 index c29526d380..0000000000 --- a/en/reference/applications/services/search.html +++ /dev/null @@ -1,574 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "services.xml - 'search'" -redirect_from: -- /en/reference/services-search.html ---- - -<p> - This is the reference for the <em>search</em> part of the container config. - Related: <a href="../../../applications/chaining.html">Chained components</a> - and the <a href="../../../querying/federation.html">federation tutorial</a>. - The root element of the search configuration, - declared as a subelement to <a href="container.html">container</a>: -</p> -<pre class="pre-config"> -<a href="">search</a> - <a href="#binding">binding</a> - <a href="#searcher">searcher [id, class, bundle, provides, before, after]</a> - <a href="#federation">federation [id]</a> - <a href="#source-reference">source [idref]</a> - <a href="#federationoptions">federationoptions [timeout, requestTimeout, optional]</a> - <a href="#source-set">source-set [inherits]</a> - <a href="#target-selector">target-selector</a> - <a href="#chain">chain [id, inherits, excludes]</a> - <a href="#searcher">searcher [id, class, bundle, provides, before, after]</a> - <a href="#federation">federation [id]</a> - <a href="#source-reference">source [idref]</a> - <a href="#federationoptions">federationoptions [timeout, requestTimeout, optional]</a> - <a href="#source-set">source-set [inherits]</a> - <a href="#target-selector">target-selector</a> - <a href="#provider">provider [id, type, cluster, excludes]</a> - <a href="#federationoptions">federationoptions [timeout, requestTimeout, optional]</a> - <a href="#source">source [id]</a> - <a href="#searcher">searcher [id, class, bundle, provides, before, after]</a> - <a href="#renderer">renderer [id, class, bundle]</a> - <a href="#significance">significance</a> - <a href="#threadpool">threadpool</a> - <a href="#threadpool-threads">threads [ max ]</a> - <a href="#threadpool-queue">queue</a> -</pre> -<p> - <a href="../config-files.html#generic-configuration-in-services-xml">config</a> - applies to all searchers in the JDisc cluster's search chains, - unless overridden by individual search chains or searchers. -</p> - - - -<h2 id="binding">binding</h2> -<p> - The URI to map the SearchHandler to. - The default binding is <code>http://*/search/*</code>. - Multiple elements are allowed. Example: -</p> -<pre> -<binding>http://*/search/*</binding> -</pre> - - - -<h2 id="searcher">searcher</h2> -<p> -Searcher elements are contained in <a href="#chain">chain</a> elements or in the <a href="">search</a> root. -<!-- ToDo: and/or in source as in the example at the end? --> -</p><p> -A searcher element is either a <em>definition</em> (using <em>id</em>) or a <em>reference</em> (using <em>idref</em>). -</p><p> -A searcher definition causes the creation of exactly one searcher instance. -This instance is set up according to the content of the searcher element. -A searcher definition contained in a search chain element defines an <em>inner searcher</em>. -Otherwise, it defines an <em>outer searcher.</em> -</p><p> -Searcher definition: -</p> -<table class="table"> - <thead> - <tr><th>Attribute</th><th>Required</th><th>Value</th><th>Default</th><th>Description</th></tr> - </thead><tbody> - <tr><th>id</th> - <td>required</td> - <td>string</td> - <td></td> - <td> - <p id="searcher.id"> - The component id of the searcher instance. - For inner searchers, the id must be unique inside the search chain. - For outer searchers, the id must be unique. - An inner searcher is not permitted to have the same id as an outer searcher. - </p> - </td></tr> - <tr><th>class</th> - <td>optional</td> - <td></td> - <td></td> - <td> - <p id="searcher.class"> - A component specification containing the name of the class - to instantiate to create the searcher instance. If missing, copied from id - </p> - </td></tr> - <tr><th>bundle</th> - <td>optional</td> - <td></td> - <td></td> - <td> - <p id="searcher.bundle"> - A component specification containing the bundle symbolic name - and version used to select the bundle: The name in <artifactId> in pom.xml. - The class is loaded from this bundle. - If no bundle is specified, it defaults to the bundle containing the searchers bundled with Vespa. - </p> - </td></tr> - <tr><th>provides</th> - <td>optional</td> - <td></td> - <td></td> - <td> - <p id="searcher.provides"> - A space-separated list of names that represents what this searcher produces. - For more information on provides, before and after, - see <a href="../../../applications/chaining.html">chained components</a> - </p> - </td></tr> - <tr><th>before</th> - <td>optional</td> - <td></td> - <td></td> - <td> - <p id="searcher.before"> - A space-separated list of phase or provided names. - Phases or searchers providing these names will be - placed later in the search chain than this searcher - </p> - </td></tr> - <tr><th>after</th> - <td>optional</td> - <td></td> - <td></td> - <td> - <p id="searcher.after"> - A space-separated list of phase or provided names. - Phases or searchers providing these names will be - placed earlier in the search chain than this searcher - </p> - </td></tr> - </tbody> -</table> -<p>Example:</p> -<pre> -<searcher id="componentId" - class="className:versionSpecification" - bundle="the name in <artifactId> in pom.xml" /> -</pre> -<p>Searcher reference:</p> -<table class="table"> - <thead> - <tr><th>Attribute</th><th>Required</th><th>Value</th><th>Default</th><th>Description</th></tr> - </thead><tbody> - <tr><th>idref</th> - <td>required</td> - <td>string</td> - <td></td> - <td><p id="searcher.idref">Reference to a searcher definition</p></td></tr> - </tbody> -</table> -<p>Example:</p> -<pre> -<searcher idref="componentId" /> -</pre> - - - -<h2 id="federation">federation</h2> -<p> - A federation is a <a href="../../../applications/searchers.html#searchers">searcher</a> - see above. - This element implements <em>federation</em> - - it defines a searcher instance that sends each query to a set of search chains in parallel and combines the results. - Read the <a href="../../../querying/federation.html">federation guide</a> to learn more - and find examples for use of federation and its children - <a href="#source">source</a>, <a href="#source-set">source-set</a> and <a href="#target-selector">target-selector</a>, - as well as <a href="#provider">provider</a>. -</p> -<pre> -<federation id="componentId"> - <source idref="componentSpecification" /> - <target-selector /> -</federation> -</pre> - - - -<h2 id="target-selector">target-selector</h2> -<p> - Specifies a component that should be used to select search chains to federate to. - This component must inherit from com.yahoo.search.federation.selection.TargetSelector. - See <a href="container.html#component">component</a> for attributes and subelements. -</p> - - - -<h2 id="source-set">source-set</h2> -<p> - Used to duplicate the sources of e.g. the built-in federation searcher: -</p> -<pre> -<federation id="combinator"> - <source-set inherits="default" /> - … -</federation> -</pre> - - - - -<h2 id="source-reference">source</h2> -<p> - Reference to a source that should be used by the enclosing federation searcher. - Child element <a href="#federationoptions">federationoptions</a> is optional. -</p> -<pre> -<source idref="componentSpecification"> - <federationoptions/> -</source> -</pre> - - - -<h2 id="federationoptions">federationoptions</h2> -<p> - Contained in <a href="#source">source</a> or <a href="#provider">provider</a>. - Specifies <em>how</em> a federation searcher should federate to a given search chain. - If a federation options A <em>overrides</em> another federation options B, - the result is a new federation options containing: -</p> -<ul> - <li>all the options in B not present in A</li> - <li>all the options in A</li> -</ul> -<p> -When federating to a source or provider, the federation searcher per default -uses the federation options from the search chain. -If a <a href="#source-reference">source reference</a> contains federation options, -it overrides the options of the search chain when used from the enclosing federation searcher. -</p> -<table class="table"> - <thead> - <tr><th>Attribute</th><th>Required</th><th>Value</th><th>Default</th><th>Description</th></tr> - </thead><tbody> - <tr><th>timeout</th> - <td>optional</td> - <td>number</td> - <td></td> - <td> - <p id="federationoptions.timeout"> - The minimum number of seconds or milliseconds (if ms is present) - the federation searcher waits for the federated search chain executing the query - </p> - </td></tr> - <tr><th>requestTimeout</th> - <td>optional</td> - <td>number</td> - <td></td> - <td> - <p id="federationoptions.requestTimeout"> - The minimum number of seconds or milliseconds (if ms is present) - the search chain executing the query should continue execution. - In some cases it is useful to set this higher than the timeout, - such that a chain can keep waiting for requested data longer than the query is waiting for the chain. - This allows queries to populate caches within the search chain even though - populating the caches requires waiting longer than the query timeout - </p> - </td></tr> - <tr><th>optional</th> - <td>optional</td> - <td>true/false</td> - <td>false</td> - <td> - <p id="federationoptions.optional"> - Determines if the federation searcher should wait for this search chain at all. - Normally, it only waits for mandatory (i.e. not optional) search chains, and when they are done, - cancels the remaining search chains that are not finished. - If all the search chains federated to are optional, all of them will be treated as mandatory. - All search chains are per default mandatory - </p> - </td></tr> - </tbody> -</table> -<p>Example:</p> -<pre> -<federationoptions timeout="2.0" requestTimeout="2500ms" optional="true" /> -</pre> - - - -<h2 id="renderer">renderer</h2> -<p> - The definition of a <a href="../../../applications/result-renderers.html">search result renderer</a>. -</p> -<table class="table"> - <thead> - <tr><th>Attribute</th><th>Required</th><th>Value</th><th>Default</th><th>Description</th></tr> - </thead><tbody> - <tr><th>id</th> - <td>required</td> - <td>string</td> - <td></td> - <td><p id="renderer.id">The component ID</p></td></tr> - <tr><th>class</th> - <td>optional</td> - <td>string</td> - <td></td> - <td><p id="renderer.class">The class of the component, defaults to id</p></td></tr> - <tr><th>bundle</th> - <td>optional</td> - <td>string</td> - <td></td> - <td> - <p id="renderer.bundle"> - The bundle to load the component from: The name in <artifactId> in your pom.xml. - If no bundle is given, the bundle containing renderers provided by Vespa is used. - </p> - </td></tr> - </tbody> -</table> -<p>Example:</p> -<pre> -<renderer id="componentId" - class="className:versionSpecification" - bundle="the name in <artifactId> in pom.xml" /> -</pre> - -<h2 id="significance">significance</h2> -<p> -Contained in <a href="#searcher">searcher</a>. -Specifies one or more global significance <a href="#model">models</a>. -</p> - -<pre data-test="file" data-path="my-app/src/main/application/services.xml"> -<significance> - <model model-id="significance-en-wikipedia-v1"/> - <model url="https://some/uri/my-model.model.multilingual.json"/> - <model path="models/my-model.no.json.zst"/> -</significance> -</pre> -<p> -The models are either provided by <em>Vespa</em> or generated with <a href="../../operations/self-managed/tools.html#vespa-significance">vespa-significance tool</a>. -The order determines model precedence - with the last element having the highest priority. -To use these models, schema needs to <a href="../../schemas/schemas.html#significance">enable significance models in the rank-profile</a>. -</p> -<p>Sub-elements:</p> -<ul> - <li><a href="#model">model</a> (required, one or more)</li> -</ul> - - -<h2 id="model">model</h2> -<p> -Contained in <a href="#significance">significance</a>. -Specifies <a href="../../../ranking/significance#global-significance-model">global significance model</a>. -Models are identified by <code>model-id</code> or by providing <code>url</code> or <code>path</code> to a model file in the application package. -</p> -<p> -Models with <code>model-id</code> are provided by <em>Vespa</em> and listed <a href="../../../rag/model-hub.html#significance-models">here</a>. -Example with <code>model-id</code>: -</p> -<pre> -<model model-id="significance-en-wikipedia-v1"/> -</pre> -<p> -A model specified with <code>url</code> and <code>path</code> are JSON files, which can be also compressed with <a href="https://facebook.github.io/zstd/">zstandard</a>. -Model files can be generated using <a href="../../operations/self-managed/tools.html#vespa-significance">vespa-significance tool</a>. -Example with <code>url</code>: -</p> -<pre> -<model url="https://some/uri/mymodel.multilingual.json"/> -</pre> -<p> -Models with <code>path</code> should be placed in the application package. -The path is relative to the application package root. -Example with <code>path</code>: -</p> -<pre> -<model path="models/mymodel.no.json.zst"/> -</pre> - - -<h2 id="chain">chain</h2> -<p> - Specifies how a search chain should be instantiated, and how the contained searchers should be ordered. - Refer to the <a href="processing.html#chain">chain reference</a> for attributes and child elements. - Chains can <a href="processing.html#inherits">inherit</a> searchers from other chains - and use <a href="processing.html#phase">phases</a> for ordering. - Note that <a href="#provider">provider</a> and <a href="#source">source</a> elements are also chains. - Specify a search chain in a query using <a href="../../api/query.html#searchchain">searchChain</a>. -</p> -<p>Example which inherits from the built-in <em>vespa</em> chain so that - the searcher can dispatch queries to the content clusters:</p> -<pre> -<chain id="common" inherits="vespa"> - <searcher class="com.yahoo.vespatest.ExtraHitSearcher" id="CommonSearcher" bundle="the name in <artifactId> in your pom.xml" > - <config name="vespatest.extra-hit"> - <exampleString>A searcher for ...</exampleString> - </config> - </searcher> -</chain> -</pre> -<p>Optional sub-elements:</p> -<ul> - <li>searcher or federation (one or more), either a reference or definition. - If the name given for a searcher matches an <em>outer searcher</em>, - it is a <em>searcher reference</em>. - Otherwise, it is a <em>searcher definition</em>. - If it is a searcher definition, it is also an implicit searcher reference saying: use - <em>exactly</em> this searcher. All these searcher elements must have different name.</li> - <li><a href="processing.html#phase">phase</a> (one or more).</li> - <li><a href="../config-files.html#generic-configuration-in-services-xml">config</a> - (one or more - will apply to all <em>inner</em> searchers in this search chain, - unless overridden by individual inner searchers).</li> -</ul> -<p> - You can put search config in separate files in a directory under - the application package using <a href="container.html#include">include</a>. - Each file must contain one <code><search></code> element like above. - Vespa behaves as if each chain configured within was "inline" in - <code>services.xml</code>. This is handy when multiple developers need - to deploy individual search chains, say in different packages. -</p> -{% include note.html content="If using multiple container clusters, -the modular search chains will be available in all the clusters."%} -<p> - Each searcher reference must match the <em>type</em> of the searcher definition. - So for example the searcher reference <em>federation idref="myId"</em> must match an outer - searcher defined as <em>federation id="myId"</em>, not <em>searcher id="myId"</em>. -</p> - - - -<h2 id="provider">provider</h2> -<p> - A provider is a search chain responsible for talking to an external service. - Everything covered in <a href="#chain">chain</a> is also valid for providers. -</p> -<table class="table"> - <thead> - <tr><th>Attribute</th><th>Required</th><th>Value</th><th>Default</th><th>Description</th></tr> - </thead><tbody> - <tr><th>id</th> - <td>required</td> - <td>string</td> - <td></td> - <td><p id="provider.id">ID</p></td></tr> - <tr><th>excludes</th> - <td>optional</td> - <td></td> - <td></td> - <td><p id="provider.excludes"></p></td></tr> - <tr><th>type</th> - <td>optional</td> - <td>local</td> - <td></td> - <td> - <p id="provider.type"> - Determines which searchers are implicitly added to this search chain - to talk to the external service. - </p> - </td></tr> - </tbody> -</table> - - -<h3 id="local-provider">local provider</h3> -<p> - Local providers are providers with the type set to <em>local</em>, - accessing a local Vespa cluster (i.e. a content cluster in the same application). -</p> -<table class="table"> - <thead> - <tr><th>Attribute</th><th>Required</th><th>Value</th><th>Default</th><th>Description</th></tr> - </thead><tbody> - <tr><th>cluster</th> - <td>required</td> - <td>string</td> - <td></td> - <td> - <p id="provider.type.local">The name of the local cluster.</p> - </td></tr> - </tbody> -</table> -<pre> -<provider id="music" cluster="music" type="local" excludes="com.yahoo.prelude.querytransform.StemmingSearcher" /> -</pre> - - -<h2 id="source">source</h2> -<p> -Defines a source search chain and an associated source. -</p> -<pre> -<provider id="providerA"> - <source id="commonSource"> - <searcher id="com.yahoo.example.AddHitSearcher" bundle="the name in <artifactId> in your pom.xml" > - <config name="vespatest.hit-title"> - <hitTitle>providerA</hitTitle> - </config> - </searcher> - </source> -</provider> -</pre> -<p> - The component id specified is the id of the associated source. - The associated source consists of all the source search chains with the same source name. -</p><p> - Only a single source search chain can specify the source name using the "id" attribute. - This search chain is called the <em>leader</em>. - The other source search chains must specify the source name using the "idref" attribute. - The latter search chains are called participants. -</p><p> - A source can be used for <em>federation</em>. - When federating to a source, the leader search chain is normally used. - To use one of the participant search chains, the following query parameter must be set: - source.<em>sourceId</em>.provider.<em>providerId</em>. -</p><p> - The id of the source search chain is <em>sourceId@providerId</em>. - This search chain automatically inherits from the enclosing provider. - It also automatically inherits the federation options of the enclosing provider. - If the source contains federation options, they override the inherited ones. - In all other respects, this search chain behaves like any other search chain. -</p> - - -<h2 id="threadpool">threadpool</h2> - -<p> - Specifies configuration for the thread pool for the jdisc search handler. All parameters are relative to the number of CPU cores—see the - <a href="../../../performance/container-tuning.html#container-worker-threads-example">container tuning example</a>. - This thread pool also supports the optional <code>max</code> attribute, which lets the pool grow up to - <code>max * vCPU</code> threads under load before shrinking back after 5 seconds of idleness. Requests are rejected once the allowed - number of threads is reached, all are busy, and the queue is full. -</p> - -<p>Optional sub-elements:</p> -<ul> - <li><a href="#threadpool-threads">threads</a></li> - <li><a href="#threadpool-queue">queue</a></li> -</ul> - - -<h3 id="threadpool-threads">threads</h3> -<p> - The number of permanent threads relative to number of vCPU cores. Default value is <code>10</code>. -</p> -<table class="table"> - <thead> - <tr><th>Attribute</th><th>Required</th><th>Value</th><th>Default</th><th>Description</th></tr> - </thead><tbody> -<tr><th>max</th> - <td>optional</td> - <td>number</td> - <td>equal to <code><threads></code></td> - <td> - <span id="threads.boost"></span> - <p id="threads.max"> - The maximum number of threads relative to vCPU cores. Value must be greater than or equal to <code><threads></code>. - </p> - </td></tr> -</tbody> -</table> - -<h3 id="threadpool-queue">queue</h3> - -<p> - The size of the request queue relative to effective number of threads. - Specify <code>0</code> to disable queuing. Default value is <code>40</code>. -</p> diff --git a/en/reference/applications/services/search.mdx b/en/reference/applications/services/search.mdx new file mode 100644 index 0000000000..9d74fbfa3b --- /dev/null +++ b/en/reference/applications/services/search.mdx @@ -0,0 +1,481 @@ +--- +title: "services.xml - 'search'" +sidebarTitle: "services.xml - search" +--- + +This is the reference for the *search* part of the container config. Related: [Chained components](/en/applications/chaining) and the [federation tutorial](/en/querying/federation). The root element of the search configuration, declared as a subelement to [container](/en/reference/applications/services/container): + +```yaml +search + binding + searcher [id, class, bundle, provides, before, after] + federation [id] + source [idref] + federationoptions [timeout, requestTimeout, optional] + source-set [inherits] + target-selector + chain [id, inherits, excludes] + searcher [id, class, bundle, provides, before, after] + federation [id] + source [idref] + federationoptions [timeout, requestTimeout, optional] + source-set [inherits] + target-selector + provider [id, type, cluster, excludes] + federationoptions [timeout, requestTimeout, optional] + source [id] + searcher [id, class, bundle, provides, before, after] + renderer [id, class, bundle] + significance + threadpool + threads [ max ] + queue +``` + +[config](/en/reference/applications/config-files#generic-configuration-in-services-xml) applies to all searchers in the JDisc cluster's search chains, unless overridden by individual search chains or searchers. + +## binding + +The URI to map the SearchHandler to. The default binding is `http://*/search/*`. Multiple elements are allowed. Example: + +```xml +<binding>http://*/search/*</binding> +``` + +## searcher + +Searcher elements are contained in [chain](#chain) elements or in the search root. + +A searcher element is either a *definition* (using *id*) or a *reference* (using *idref*). + +A searcher definition causes the creation of exactly one searcher instance. This instance is set up according to the content of the searcher element. A searcher definition contained in a search chain element defines an *inner searcher*. Otherwise, it defines an *outer searcher.* + +Searcher definition: + +<table className="vespa-table"> + <thead> + <tr> + <th>Attribute</th> + <th>Required</th> + <th>Value</th> + <th>Default</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>**id**</td> + <td>required</td> + <td>string</td> + <td></td> + <td>The component id of the searcher instance. For inner searchers, the id must be unique inside the search chain. For outer searchers, the id must be unique. An inner searcher is not permitted to have the same id as an outer searcher.</td> + </tr> + <tr> + <td>**class**</td> + <td>optional</td> + <td></td> + <td></td> + <td>A component specification containing the name of the class to instantiate to create the searcher instance. If missing, copied from id</td> + </tr> + <tr> + <td>**bundle**</td> + <td>optional</td> + <td></td> + <td></td> + <td>A component specification containing the bundle symbolic name and version used to select the bundle: The name in <code>{`<artifactId>`}</code> in pom.xml. The class is loaded from this bundle. If no bundle is specified, it defaults to the bundle containing the searchers bundled with Vespa.</td> + </tr> + <tr> + <td>**provides**</td> + <td>optional</td> + <td></td> + <td></td> + <td>A space-separated list of names that represents what this searcher produces. For more information on provides, before and after, see <a href="/en/applications/chaining" aria-label="chained components">chained components</a></td> + </tr> + <tr> + <td>**before**</td> + <td>optional</td> + <td></td> + <td></td> + <td>A space-separated list of phase or provided names. Phases or searchers providing these names will be placed later in the search chain than this searcher</td> + </tr> + <tr> + <td>**after**</td> + <td>optional</td> + <td></td> + <td></td> + <td>A space-separated list of phase or provided names. Phases or searchers providing these names will be placed earlier in the search chain than this searcher</td> + </tr> + </tbody> +</table> + +Example: + +```xml +<searcher id="componentId" + class="className:versionSpecification" + bundle="the name in <artifactId> in pom.xml" /> +``` + +Searcher reference: + +<table className="vespa-table"> + <thead> + <tr> + <th>Attribute</th> + <th>Required</th> + <th>Value</th> + <th>Default</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>**idref**</td> + <td>required</td> + <td>string</td> + <td></td> + <td>Reference to a searcher definition</td> + </tr> + </tbody> +</table> + +Example: + +```xml +<searcher idref="componentId" /> +``` + +## federation + +A federation is a [searcher](/en/applications/searchers#searchers) - see above. This element implements *federation* - it defines a searcher instance that sends each query to a set of search chains in parallel and combines the results. Read the [federation guide](/en/querying/federation) to learn more and find examples for use of federation and its children [source](#source), [source-set](#source-set) and [target-selector](#target-selector), as well as [provider](#provider). + +```xml +<federation id="componentId"> + <source idref="componentSpecification" /> + <target-selector /> +</federation> +``` + +## target-selector + +Specifies a component that should be used to select search chains to federate to. This component must inherit from com.yahoo.search.federation.selection.TargetSelector. See [component](/en/reference/applications/services/container#component) for attributes and subelements. + +## source-set + +Used to duplicate the sources of e.g. the built-in federation searcher: + +```xml +<federation id="combinator"> + <source-set inherits="default" /> + … +</federation> +``` + +## source + +Reference to a source that should be used by the enclosing federation searcher. Child element [federationoptions](#federationoptions) is optional. + +```xml +<source idref="componentSpecification"> + <federationoptions/> +</source> +``` + +## federationoptions + +Contained in [source](#source) or [provider](#provider). Specifies *how* a federation searcher should federate to a given search chain. If a federation options A *overrides* another federation options B, the result is a new federation options containing: + +- all the options in B not present in A +- all the options in A + +When federating to a source or provider, the federation searcher per default uses the federation options from the search chain. If a [source reference](#source-reference) contains federation options, it overrides the options of the search chain when used from the enclosing federation searcher. + +<table className="vespa-table"> + <thead> + <tr> + <th>Attribute</th> + <th>Required</th> + <th>Value</th> + <th>Default</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>**timeout**</td> + <td>optional</td> + <td>number</td> + <td></td> + <td>The minimum number of seconds or milliseconds (if ms is present) the federation searcher waits for the federated search chain executing the query</td> + </tr> + <tr> + <td>**requestTimeout**</td> + <td>optional</td> + <td>number</td> + <td></td> + <td>The minimum number of seconds or milliseconds (if ms is present) the search chain executing the query should continue execution. In some cases it is useful to set this higher than the timeout, such that a chain can keep waiting for requested data longer than the query is waiting for the chain. This allows queries to populate caches within the search chain even though populating the caches requires waiting longer than the query timeout</td> + </tr> + <tr> + <td>**optional**</td> + <td>optional</td> + <td>true/false</td> + <td>false</td> + <td>Determines if the federation searcher should wait for this search chain at all. Normally, it only waits for mandatory (i.e. not optional) search chains, and when they are done, cancels the remaining search chains that are not finished. If all the search chains federated to are optional, all of them will be treated as mandatory. All search chains are per default mandatory</td> + </tr> + </tbody> +</table> + +Example: + +```xml +<federationoptions timeout="2.0" requestTimeout="2500ms" optional="true" /> +``` + +## renderer + +The definition of a [search result renderer](/en/applications/result-renderers). + +<table className="vespa-table"> + <thead> + <tr> + <th>Attribute</th> + <th>Required</th> + <th>Value</th> + <th>Default</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>**id**</td> + <td>required</td> + <td>string</td> + <td></td> + <td>The component ID</td> + </tr> + <tr> + <td>**class**</td> + <td>optional</td> + <td>string</td> + <td></td> + <td>The class of the component, defaults to id</td> + </tr> + <tr> + <td>**bundle**</td> + <td>optional</td> + <td>string</td> + <td></td> + <td>The bundle to load the component from: The name in <code>{`<artifactId>`}</code> in your pom.xml. If no bundle is given, the bundle containing renderers provided by Vespa is used.</td> + </tr> + </tbody> +</table> + +Example: + +```xml +<renderer id="componentId" + class="className:versionSpecification" + bundle="the name in <artifactId> in pom.xml" /> +``` + +## significance + +Contained in [searcher](#searcher). Specifies one or more global significance [models](#model). + +```xml +<significance> + <model model-id="significance-en-wikipedia-v1"/> + <model url="https://some/uri/my-model.model.multilingual.json"/> + <model path="models/my-model.no.json.zst"/> +</significance> +``` + +The models are either provided by *Vespa* or generated with [vespa-significance tool](/en/reference/operations/self-managed/tools#vespa-significance). The order determines model precedence - with the last element having the highest priority. To use these models, schema needs to [enable significance models in the rank-profile](/en/reference/schemas/schemas#significance). + +Sub-elements: + +- [model](#model) (required, one or more) + +## model + +Contained in [significance](#significance). Specifies [global significance model](/en/ranking/significance#global-significance-model). Models are identified by `model-id` or by providing `url` or `path` to a model file in the application package. + +Models with `model-id` are provided by *Vespa* and listed [here](/en/rag/model-hub#significance-models). Example with `model-id`: + +```xml +<model model-id="significance-en-wikipedia-v1"/> +``` + +A model specified with `url` and `path` are JSON files, which can be also compressed with [zstandard](https://facebook.github.io/zstd/). Model files can be generated using [vespa-significance tool](/en/reference/operations/self-managed/tools#vespa-significance). Example with `url`: + +```xml +<model url="https://some/uri/mymodel.multilingual.json"/> +``` + +Models with `path` should be placed in the application package. The path is relative to the application package root. Example with `path`: + +```xml +<model path="models/mymodel.no.json.zst"/> +``` + +## chain + +Specifies how a search chain should be instantiated, and how the contained searchers should be ordered. Refer to the [chain reference](/en/reference/applications/services/processing#chain) for attributes and child elements. Chains can [inherit](/en/reference/applications/services/processing#inherits) searchers from other chains and use [phases](/en/reference/applications/services/processing#phase) for ordering. Note that [provider](#provider) and [source](#source) elements are also chains. Specify a search chain in a query using [searchChain](/en/reference/api/query#searchchain). + +Example which inherits from the built-in *vespa* chain so that the searcher can dispatch queries to the content clusters: + +```xml +<chain id="common" inherits="vespa"> + <searcher class="com.yahoo.vespatest.ExtraHitSearcher" id="CommonSearcher" bundle="the name in <artifactId> in your pom.xml" > + <config name="vespatest.extra-hit"> + <exampleString>A searcher for ...</exampleString> + </config> + </searcher> +</chain> +``` + +Optional sub-elements: + +- searcher or federation (one or more), either a reference or definition. If the name given for a searcher matches an *outer searcher*, it is a *searcher reference*. Otherwise, it is a *searcher definition*. If it is a searcher definition, it is also an implicit searcher reference saying: use *exactly* this searcher. All these searcher elements must have different name. +- [phase](/en/reference/applications/services/processing#phase) (one or more). +- [config](/en/reference/applications/config-files#generic-configuration-in-services-xml) (one or more - will apply to all *inner* searchers in this search chain, unless overridden by individual inner searchers). + +You can put search config in separate files in a directory under the application package using [include](/en/reference/applications/services/container#include). Each file must contain one `<search>` element like above. Vespa behaves as if each chain configured within was "inline" in `services.xml`. This is handy when multiple developers need to deploy individual search chains, say in different packages. + +<Note> +**Note:** + +If using multiple container clusters, the modular search chains will be available in all the clusters. +</Note> + +Each searcher reference must match the *type* of the searcher definition. So for example the searcher reference *federation idref="myId"* must match an outer searcher defined as *federation id="myId"*, not *searcher id="myId"*. + +## provider + +A provider is a search chain responsible for talking to an external service. Everything covered in [chain](#chain) is also valid for providers. + +<table className="vespa-table"> + <thead> + <tr> + <th>Attribute</th> + <th>Required</th> + <th>Value</th> + <th>Default</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>**id**</td> + <td>required</td> + <td>string</td> + <td></td> + <td>ID</td> + </tr> + <tr> + <td>**excludes**</td> + <td>optional</td> + <td></td> + <td></td> + <td></td> + </tr> + <tr> + <td>**type**</td> + <td>optional</td> + <td>local</td> + <td></td> + <td>Determines which searchers are implicitly added to this search chain to talk to the external service.</td> + </tr> + </tbody> +</table> + +### local provider + +Local providers are providers with the type set to *local*, accessing a local Vespa cluster (i.e. a content cluster in the same application). + +<table className="vespa-table"> + <thead> + <tr> + <th>Attribute</th> + <th>Required</th> + <th>Value</th> + <th>Default</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>**cluster**</td> + <td>required</td> + <td>string</td> + <td></td> + <td>The name of the local cluster.</td> + </tr> + </tbody> +</table> + +```xml +<provider id="music" cluster="music" type="local" excludes="com.yahoo.prelude.querytransform.StemmingSearcher" /> +``` + +## source + +Defines a source search chain and an associated source. + +```xml +<provider id="providerA"> + <source id="commonSource"> + <searcher id="com.yahoo.example.AddHitSearcher" bundle="the name in <artifactId> in your pom.xml" > + <config name="vespatest.hit-title"> + <hitTitle>providerA</hitTitle> + </config> + </searcher> + </source> +</provider> +``` + +The component id specified is the id of the associated source. The associated source consists of all the source search chains with the same source name. + +Only a single source search chain can specify the source name using the "id" attribute. This search chain is called the *leader*. The other source search chains must specify the source name using the "idref" attribute. The latter search chains are called participants. + +A source can be used for *federation*. When federating to a source, the leader search chain is normally used. To use one of the participant search chains, the following query parameter must be set: source.*sourceId*.provider.*providerId*. + +The id of the source search chain is *sourceId@providerId*. This search chain automatically inherits from the enclosing provider. It also automatically inherits the federation options of the enclosing provider. If the source contains federation options, they override the inherited ones. In all other respects, this search chain behaves like any other search chain. + +## threadpool + +Specifies configuration for the thread pool for the jdisc search handler. All parameters are relative to the number of CPU cores—see the [container tuning example](/en/performance/container-tuning#container-worker-threads-example). This thread pool also supports the optional `max` attribute, which lets the pool grow up to `max * vCPU` threads under load before shrinking back after 5 seconds of idleness. Requests are rejected once the allowed number of threads is reached, all are busy, and the queue is full. + +Optional sub-elements: + +- [threads](#threadpool-threads) +- [queue](#threadpool-queue) + +### threads + +The number of permanent threads relative to number of vCPU cores. Default value is `10`. + +<table className="vespa-table"> + <thead> + <tr> + <th>Attribute</th> + <th>Required</th> + <th>Value</th> + <th>Default</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>**max**</td> + <td>optional</td> + <td>number</td> + <td>equal to <code>{`<threads>`}</code></td> + <td>The maximum number of threads relative to vCPU cores. Value must be greater than or equal to <code>{`<threads>`}</code>.</td> + </tr> + </tbody> +</table> + +### queue + +The size of the request queue relative to effective number of threads. Specify `0` to disable queuing. Default value is `40`. diff --git a/en/reference/applications/services/services.html b/en/reference/applications/services/services.html deleted file mode 100644 index bbd04c0a29..0000000000 --- a/en/reference/applications/services/services.html +++ /dev/null @@ -1,337 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "services.xml" -redirect_from: -- /en/reference/services.html -- /en/reference/services/services.html ---- - -<p><em>services.xml</em> specifies the clusters an application should have and their capabilities. -It is placed in the root of the -<a href="../../../basics/applications.html">application package</a>.</p> - -<p>Elements:</p> -<pre class="pre-config"> -<a href="#services">services [version]</a> - <a href="container.html">container [version]</a> - specifies a container cluster - <a href="content.html">content [version]</a> - specifies a content cluster - <a href="admin.html">admin [version]</a> - control plane configuration (rarely needed) - <a href="../../../writing/document-routing.html#routing-services">routing [version] - how content should be routed (rarely needed)</a> -</pre> - - - -<h2 id="services"><services></h2> - -<table class="table"> - <thead> - <tr><th>Attribute</th><th>Required</th><th>Value</th><th>Default</th><th>Description</th></tr> - </thead> - <tbody> - <tr> - <td>version</td> - <td>required</td> - <td>number</td> - <td></td> - <td>1.0 in this version of Vespa</td> - </tr> - </tbody> -</table> - -<p>Optional subelements (one or more of <em>container</em> or <em>content</em> is required):</p> - -<ul> - <li><a href="container.html"><container></a></li> - <li><a href="content.html"><content></a></li> - <li><a href="admin.html"><admin></a></li> - <li><a href="../../../writing/document-routing.html#routing-services"><routing></a></li> -</ul> - -<p>The rest of this document describes tags that are used within multiple services tags.</p> - - -<h2 id="nodes"><nodes></h2> - -<p>The <em>nodes</em> element configures the hardware resources of a cluster, and so is used in both -container and content clusters. This tag works differently on Vespa Cloud and self-managed instances:</p> - -<ul> - <li> - Vespa Cloud: The number of nodes are specified by a <em>count</em> attribute, - and the resources of each node by a <a href="#resources">resource</a> child element. - </li> - <li> - Self-managed: <em>nodes</em> have a <a href="#node">node</a> child element for each node, - A node referred to in <em>services.xml</em> must be defined in - <a href="../hosts.html">hosts.xml</a> using <em>hostalias</em>. - </li> -</ul> - -<p>It is possible to specify both to make an application package work in both environments, and -it is always possible to deploy either type for development on the other: -When the nodes tag has Vespa Cloud content it is interpreted as a single-node cluster -in a self-hosted environment and vice versa.</p> - -<table class="table"> - <thead> - <tr> - <th>Attribute</th> - <th>type</th> - <th>Default</th> - <th>Description</th> - </tr> - </thead> - <tbody> - <tr> - <th><strong>count</strong></th> - <td>integer or range</td> - <td></td> - <td>Vespa Cloud: The number of nodes of the cluster.</td> - </tr> - <tr> - <th><strong>exclusive</strong></th> - <td>boolean</td> - <td>false</td> - <td> - Optional. Vespa Cloud: If true these nodes will never be placed on shared hosts even when this would - otherwise be allowed (which is only for content nodes in some environments). - When nodes are allocated exclusively, the resources must match the resources of the host exactly. - </td> - </tr> - <tr> - <th><strong>groups</strong></th> - <td>integer or range</td> - <td></td> - <td> - Vespa Cloud content nodes only, optional: Integer or range. - Sets the number of groups into which content nodes should be divided. - Each group will have an equal share of the nodes, and one or more complete copies of the corpus and index, - and each query will be routed to just one group - - see <a href="/en/content/elasticity.html#grouped-distribution">grouped distribution</a>. - This allows scaling to a higher query load than is possible with just a single group. - </td> - </tr> - <tr> - <th><strong>group-size</strong></th> - <td>integer or range</td> - <td></td> - <td> - Vespa Cloud content nodes only, optional: Integer or range where either value can be skipped - (replaced by an empty string) to create a one-sided limit. - This can be set as an alternative to explicitly setting <code>groups</code>: - The group sizes used will always be within these limits (inclusive), for any <code>count</code>. - </td> - </tr> - </tbody> -</table> - -<p>If neither <em>groups</em> nor <em>group-size</em> is set, all nodes belong to a single group. -Read more in <a href="../../../performance/topology-and-resizing">topology</a>.</p> - -<p>Ranges are expressed by the syntax <code>[lower-limit, upper-limit]</code>; Both limits are inclusive. -Any value set as a range will be <a href="../../../operations/autoscaling.html">autoscaled</a>.</p> - - -<h2 id="resources"><resources></h2> - -<p>Under <a href="#nodes">nodes</a> on Vespa Cloud: Specifies the resources each node in the cluster should have.</p> - -<p>The resources must match a node flavor in -<a href="https://cloud.vespa.ai/en/reference/aws-flavors.html">AWS</a>, -<a href="https://cloud.vespa.ai/en/reference/gcp-flavors.html">GCP</a> -<a href="https://cloud.vespa.ai/en/reference/azure-flavors.html">Azure</a>, -depending on where you are deploying. -Exception: If you use remote disk, you can specify any number lower than the max size.</p> - -<p><strong>Subelements:</strong> <a href="#gpu"><gpu></a></p> -<table class="table"> - <thead> - <tr> - <th>Attribute</th> - <th>type</th> - <th>Default</th> - <th>Description</th> - </tr> - </thead> - <tbody> - <tr> - <th><b>vcpu</b></th> - <td>float or range</td> - <td>2</td> - <td>CPU (virtual threads)</td> - </tr> - <tr> - <th><b>memory</b></th> - <td>float or range, each followed by a byte unit, such as "Gb"</td> - <td>8 Gb in container clusters, 16 Gb in content clusters</td> - <td>Memory</td> - </tr> - <tr> - <th><b>disk</b></th> - <td>float or range, each followed by a byte unit, such as "Gb"</td> - <td>50 in container clusters, 300 in content clusters</td> - <td> - Disk space. To fit core dumps/heap dumps, the disk space should be larger than 3 x memory size for content nodes, - 2 x memory size for container nodes. If disk size is not explicitly specified, Vespa Cloud chooses a default disk size. - The default may be automatically increased to satisfy the minimum disk-to-memory ratio. When both disk and memory are - explicitly specified, Vespa Cloud enforces the same minimum ratios. - </td> - </tr> - <tr> - <th><b>storage-type</b> - <td>string (enum)</td> - <td><code>any</code></td> - <td> - The type of storage to use. - This is useful to specify local storage when network storage provides insufficient io operations or too noisy io performance: - <ul> - <li><code>local</code>: Node-local storage is required.</li> - <li><code>remote</code>: Network storage must be used.</li> - <li><code>any</code>: Both remote or local storage may be used.</li> - </ul> - </td> - </tr> - <tr> - <th><b>disk-speed</b> - <td>string (enum)</td> - <td><code>fast</code></td> - <td>The required disk speed category: - <ul> - <li><code>fast</code>: SSD-like disk speed is required</li> - <li><code>slow</code>: This is sized for spinning disk speed</li> - <li><code>any</code>: Performance does not depend on disk speed (often suitable for container clusters).</li> - </ul> - </td> - </tr> - <tr> - <th><strong>architecture</strong></th> - <td>string (enum)</td> - <td><code>any</code></td> - <td>Node CPU architecture: - <ul class="table"> - <li><code>x86_64</code></li> - <li><code>arm64</code></li> - <li><code>any</code>: Use any of the available architectures.</li> - </ul> - </td> - </tr> - </tbody> -</table> - -<p>Ranges are expressed by the syntax <code>[lower-limit, upper-limit]</code>; Both limits are inclusive. -Any value set as a range will be <a href="../../../operations/autoscaling.html">autoscaled</a>.</p> - - -<h2 id="node"><node></h2> - -<p>Under <a href="#nodes">nodes</a> on self-managed systems: Specifies a node that should be a member in the cluster.</p> - -<table class="table"> - <thead> - <tr><th>Attribute</th><th>Required</th><th>Value</th><th>Default</th><th>Description</th></tr> - </thead> - <tbody> - <tr> - <th>hostalias</th> - <td>required</td> - <td>string</td> - <td></td> - <td><p id="node.hostalias">a host name which must be mapped to a full hostname in <a href="../hosts.html">hosts.xml</a></p></td> - </tr> -</tbody> -</table> - - -<h2 id="gpu"><gpu></h2> - -<p>Under <a href="#resources">resources</a> on Vespa Cloud: Declares GPU resources to provision.</p> - -<p>Limitations:</p> - -<ul> - <li>Available in AWS zones only</li> - <li>Valid for container clusters only</li> -</ul> - -<table class="table"> - <thead> - <tr> - <th>Attribute</th><th>type</th><th>Description</th> - </tr> - </thead> - <tbody> - <tr> - <th><b>count</b></th> - <td>integer</td> - <td>Number of GPUs</td> - </tr> - <tr> - <th><b>memory</b></th> - <td>integer, followed by a byte unit, such as "Gb"</td> - <td> - Amount of memory per GPU. Total amount of GPU memory available is this - number multiplied by <code>count</code>. - </td> - </tr> - </tbody> -</table> - -<p id="gpu-example">Example:</p> -<pre>{% highlight xml %} -<nodes count="2"> - <resources vcpu="4" memory="16Gb" disk="125Gb"> - <gpu count="1" memory="16Gb"/> - </resources> -</nodes> -{% endhighlight %}</pre> - - - -<h2 id="zone-specific-configuration">Zone-specific configuration</h2> -<p> - Use <em>deployment variants</em> to express configuration like: -</p> -<pre>{% highlight xml %} -<nodes deploy:environment="prod" - deploy:region="aws-use1-az4" - count="20"> - <resources vcpu="4" memory="16Gb" disk="125Gb"/> -</nodes> -{% endhighlight %}</pre> -<p> - Refer to <a href="../../../operations/deployment-variants.html">deployment variants</a> for details and more options. -</p> - - - -<h2 id="generic-config">Generic configuration using <config></h2> - -<p>Most elements in <em>services.xml</em> accept a sub-element named <em>config</em>. -<em>config</em> elements can be included on different levels in the XML structure -and the lower-level ones will override values in the higher-level ones (example below). -The <em>config</em> element must include the attribute <em>name</em>, -which gives the full name of the configuration option in question, including the namespace. -The name can either refer to configuration definitions that are shipped with Vespa -or ones that are part of the <a href="../config-files.html">application package</a>. For a -complete example on generic configuration see the -<a href="../config-files.html#generic-configuration-in-services-xml">application package</a> reference. -</p> -<pre>{% highlight xml %} -<container id="default" version="1.0"> - <handler id="com.yahoo.vespatest.ConfiguredHandler"> - <config name="vespatest.response"> - <response>configured string</response> - </config> - </handler> -</container> -{% endhighlight %}</pre> - - -<h2 id="modular">Modular Configuration</h2> - -<p>Some features are configurable using XML files in subdirectories of the application package. -This means that the configuration found in these XML files -will be used as if it was inlined in <em>services.xml</em>. -This is supported for <a href="search.html#chain">search chains</a>, -<a href="docproc.html">docproc chains</a> and -<a href="../../../writing/document-routing.html#routing-services">routing tables</a>.</p> diff --git a/en/reference/applications/services/services.mdx b/en/reference/applications/services/services.mdx new file mode 100644 index 0000000000..bc97ef04db --- /dev/null +++ b/en/reference/applications/services/services.mdx @@ -0,0 +1,255 @@ +--- +title: "services.xml" +--- + + +*services.xml* specifies the clusters an application should have and their capabilities. It is placed in the root of the [application package](/en/basics/applications). + +Elements: + +<Card> +[services \[version\]](#services)<br/> +   [container   \[version\]](/en/reference/applications/services/container) - specifies a container cluster<br/> +   [content    \[version\]](/en/reference/applications/services/content) - specifies a content cluster<br/> +   [admin      \[version\]](/en/reference/applications/services/admin) - control plane configuration (rarely needed)<br/> +   [routing     \[version\] - how content should be routed (rarely needed)](/en/writing/document-routing#routing-services)<br/> +</Card> + +## `<services>` + +<table className="vespa-table"> + <thead> + <tr> + <th>Attribute</th> + <th>Required</th> + <th>Value</th> + <th>Default</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>version</td> + <td>required</td> + <td>number</td> + <td></td> + <td>1.0 in this version of Vespa</td> + </tr> + </tbody> +</table> + +Optional subelements (one or more of *container* or *content* is required): + +- [`<container>`](/en/reference/applications/services/container) +- [`<content>`](/en/reference/applications/services/content) +- [`<admin>`](/en/reference/applications/services/admin) +- [`<routing>`](/en/writing/document-routing#routing-services) + +The rest of this document describes tags that are used within multiple services tags. + +## `<nodes>` + +The *nodes* element configures the hardware resources of a cluster, and so is used in both container and content clusters. This tag works differently on Vespa Cloud and self-managed instances: + +- Vespa Cloud: The number of nodes are specified by a *count* attribute, and the resources of each node by a [resource](#resources) child element. +- Self-managed: *nodes* have a [node](#node) child element for each node, A node referred to in *services.xml* must be defined in [hosts.xml](/en/reference/applications/hosts) using *hostalias*. + +It is possible to specify both to make an application package work in both environments, and it is always possible to deploy either type for development on the other: When the nodes tag has Vespa Cloud content it is interpreted as a single-node cluster in a self-hosted environment and vice versa. + +<table className="vespa-table"> + <thead> + <tr> + <th>Attribute</th> + <th>type</th> + <th>Default</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>**count**</td> + <td>integer or range</td> + <td></td> + <td>Vespa Cloud: The number of nodes of the cluster.</td> + </tr> + <tr> + <td>**exclusive**</td> + <td>boolean</td> + <td>false</td> + <td>Optional. Vespa Cloud: If true these nodes will never be placed on shared hosts even when this would otherwise be allowed (which is only for content nodes in some environments). When nodes are allocated exclusively, the resources must match the resources of the host exactly.</td> + </tr> + <tr> + <td>**groups**</td> + <td>integer or range</td> + <td></td> + <td>Vespa Cloud content nodes only, optional: Integer or range. Sets the number of groups into which content nodes should be divided. Each group will have an equal share of the nodes, and one or more complete copies of the corpus and index, and each query will be routed to just one group - see <a href="/en/content/elasticity#grouped-distribution" aria-label="grouped distribution">grouped distribution</a>. This allows scaling to a higher query load than is possible with just a single group.</td> + </tr> + <tr> + <td>**group-size**</td> + <td>integer or range</td> + <td></td> + <td>Vespa Cloud content nodes only, optional: Integer or range where either value can be skipped (replaced by an empty string) to create a one-sided limit. This can be set as an alternative to explicitly setting <code>{`groups`}</code>: The group sizes used will always be within these limits (inclusive), for any <code>{`count`}</code>.</td> + </tr> + </tbody> +</table> + +If neither *groups* nor *group-size* is set, all nodes belong to a single group. Read more in [topology](/en/performance/topology-and-resizing). + +Ranges are expressed by the syntax `[lower-limit, upper-limit]`; Both limits are inclusive. Any value set as a range will be [autoscaled](/en/operations/autoscaling). + +## `<resources>` + +Under [nodes](#nodes) on Vespa Cloud: Specifies the resources each node in the cluster should have. + +The resources must match a node flavor in [AWS](/en/performance/instance-types/aws-instance-types), [GCP](/en/performance/instance-types/gcp-instance-types) [Azure](/en/performance/instance-types/azure-instance-types), depending on where you are deploying. Exception: If you use remote disk, you can specify any number lower than the max size. + +**Subelements:** [`<gpu>`](#gpu) + +<table className="vespa-table"> + <thead> + <tr> + <th>Attribute</th> + <th>type</th> + <th>Default</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>**vcpu**</td> + <td>float or range</td> + <td>2</td> + <td>CPU (virtual threads)</td> + </tr> + <tr> + <td>**memory**</td> + <td>float or range, each followed by a byte unit, such as "Gb"</td> + <td>8 Gb in container clusters, 16 Gb in content clusters</td> + <td>Memory</td> + </tr> + <tr> + <td>**disk**</td> + <td>float or range, each followed by a byte unit, such as "Gb"</td> + <td>50 in container clusters, 300 in content clusters</td> + <td>Disk space. To fit core dumps/heap dumps, the disk space should be larger than 3 x memory size for content nodes, 2 x memory size for container nodes. If disk size is not explicitly specified, Vespa Cloud chooses a default disk size. The default may be automatically increased to satisfy the minimum disk-to-memory ratio. When both disk and memory are explicitly specified, Vespa Cloud enforces the same minimum ratios.</td> + </tr> + <tr> + <td>**storage-type**</td> + <td>string (enum)</td> + <td><code>{`any`}</code></td> + <td>The type of storage to use. This is useful to specify local storage when network storage provides insufficient io operations or too noisy io performance:<br/><br/> • <code>{`local`}</code>: Node-local storage is required. <br/>• <code>{`remote`}</code>: Network storage must be used.<br/> • <code>{`any`}</code>: Both remote or local storage may be used.</td> + </tr> + <tr> + <td>**disk-speed**</td> + <td>string (enum)</td> + <td><code>{`fast`}</code></td> + <td>The required disk speed category:<br/><br/> • <code>{`fast`}</code>: SSD-like disk speed is required<br/> • <code>{`slow`}</code>: This is sized for spinning disk speed <br/>• <code>{`any`}</code>: Performance does not depend on disk speed (often suitable for container clusters).</td> + </tr> + <tr> + <td>**architecture**</td> + <td>string (enum)</td> + <td><code>{`any`}</code></td> + <td>Node CPU architecture:<br/><br/> • <code>{`x86_64`}</code> <br/>• <code>{`arm64`}</code><br/> • <code>{`any`}</code>: Use any of the available architectures.</td> + </tr> + </tbody> +</table> + +Ranges are expressed by the syntax `[lower-limit, upper-limit]`; Both limits are inclusive. Any value set as a range will be [autoscaled](/en/operations/autoscaling). + +## `<node>` + +Under [nodes](#nodes) on self-managed systems: Specifies a node that should be a member in the cluster. + +<table className="vespa-table"> + <thead> + <tr> + <th>Attribute</th> + <th>Required</th> + <th>Value</th> + <th>Default</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>hostalias</td> + <td>required</td> + <td>string</td> + <td></td> + <td>a host name which must be mapped to a full hostname in <a href="/en/reference/applications/hosts" aria-label="hosts.xml">hosts.xml</a></td> + </tr> + </tbody> +</table> + +## `<gpu>` + +Under [resources](#resources) on Vespa Cloud: Declares GPU resources to provision. + +Limitations: + +- Available in AWS zones only +- Valid for container clusters only + +<table className="vespa-table"> + <thead> + <tr> + <th>Attribute</th> + <th>type</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>**count**</td> + <td>integer</td> + <td>Number of GPUs</td> + </tr> + <tr> + <td>**memory**</td> + <td>integer, followed by a byte unit, such as "Gb"</td> + <td>Amount of memory per GPU. Total amount of GPU memory available is this number multiplied by <code>{`count`}</code>.</td> + </tr> + </tbody> +</table> + +Example: + +```xml +<nodes count="2"> + <resources vcpu="4" memory="16Gb" disk="125Gb"> + <gpu count="1" memory="16Gb"/> + </resources> +</nodes> +``` + +## Zone-specific configuration + +Use *deployment variants* to express configuration like: + +```xml +<nodes deploy:environment="prod" + deploy:region="aws-use1-az4" + count="20"> + <resources vcpu="4" memory="16Gb" disk="125Gb"/> +</nodes> +``` + +Refer to [deployment variants](/en/operations/deployment-variants) for details and more options. + +## Generic configuration using `<config>` + +Most elements in *services.xml* accept a sub-element named *config*. *config* elements can be included on different levels in the XML structure and the lower-level ones will override values in the higher-level ones (example below). The *config* element must include the attribute *name*, which gives the full name of the configuration option in question, including the namespace. The name can either refer to configuration definitions that are shipped with Vespa or ones that are part of the [application package](../config-files.html). For a complete example on generic configuration see the [application package](/en/reference/applications/config-files#generic-configuration-in-services-xml) reference. + +```xml +<container id="default" version="1.0"> + <handler id="com.yahoo.vespatest.ConfiguredHandler"> + <config name="vespatest.response"> + <response>configured string</response> + </config> + </handler> +</container> +``` + +## Modular Configuration + +Some features are configurable using XML files in subdirectories of the application package. This means that the configuration found in these XML files will be used as if it was inlined in *services.xml*. This is supported for [search chains](/en/reference/applications/services/search#chain), [docproc chains](/en/reference/applications/services/docproc) and [routing tables](/en/writing/document-routing#routing-services). diff --git a/en/reference/applications/testing-java.html b/en/reference/applications/testing-java.html deleted file mode 100644 index 068f4a4e8a..0000000000 --- a/en/reference/applications/testing-java.html +++ /dev/null @@ -1,92 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: Testing with Java JUnit tests -redirect_from: -- en/reference/testing-java.html ---- - -<p> - This is the Vespa Testing reference for <a href="../../applications/testing.html">Vespa application system tests</a> - written in Java, as JUnit 5 unit test. -</p> -<p> - These tests verify the behaviour of a Vespa application by using its HTTP interfaces. - To write tests without Java, see <a href="testing.html">basic HTTP test reference</a>. -</p> -<p>See the <a href="../../applications/testing.html">testing guide</a> for examples of how to run the tests.</p> - - - -<h2 id="test-suites">Test suites</h2> -<p> - The <a href="../../applications/testing.html">testing documentation</a> defines three test scenarios, - comprised of four test code categories. The <em>system test framework</em> in - <a href="https://search.maven.org/artifact/com.yahoo.vespa/tenant-cd-api">com.yahoo.vespa:tenant-cd-api</a> - uses Java annotations to declare what category a JUnit test class belongs to. - To run tests with Maven belonging to a specific category, a JUnit 5 test <em>tag</em> must be specified: -</p> -<pre>{% highlight sh %} -$ mvn test -D test.categories=system -D vespa.test.config=/path-to/test-config.json -{% endhighlight %}</pre> -<table class="table"> - <thead> - <tr> - <th style="width:200px;font-weight:bold">Category</th> - <th style="width:200px">Annotation</th> - <th style="width:200px">JUnit tag</th> - <th>Description</th> - </tr> - </thead> - <tbody> - <tr> - <td style="font-weight:bold">System test</td> - <td>@SystemTest</td> - <td>system</td> - <td>Independent, functional tests</td> - </tr><tr> - <td style="font-weight:bold">Staging setup</td> - <td>@StagingSetup</td> - <td>staging-setup</td> - <td>Set state before upgrade</td> - </tr><tr> - <td style="font-weight:bold">Staging test</td> - <td>@StagingTest</td> - <td>staging</td> - <td>Verify state after upgrade</td> - </tr><tr> - <td style="font-weight:bold">Production test</td> - <td>@ProductionTest</td> - <td>production</td> - <td>Verify domain specific metrics</td> - </tr> -</table> -<p> - For an example including system and staging tests, check out the - <a href="https://github.com/vespa-cloud/examples/tree/main/CI-CD/production-deployment-with-tests-java"> - sample application test suite</a>. -</p> - -<h2 id="testng">TestNG</h2> -<p> - Combining Vespa JUnit 5 test suites with unit tests in TestNG is possible. You'll need to explicitly configure - Maven's surefire plugin to enable integration for both frameworks. To execute the Vespa test suites specify - <code>-D test.categories=[tag]</code>, where <em>[tag]</em> is one of the values listed in <a href="#test-suites">Test suites</a>. -</p> -<pre>{% highlight xml %} -<plugin> - <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-surefire-plugin</artifactId> - <dependencies> - <dependency> - <groupId>org.apache.maven.surefire</groupId> - <artifactId>surefire-junit-platform</artifactId> - <version>${surefire.vespa.tenant.version}</version> - </dependency> - <dependency> - <groupId>org.apache.maven.surefire</groupId> - <artifactId>surefire-testng</artifactId> - <version>${surefire.vespa.tenant.version}</version> - </dependency> - </dependencies> -</plugin> -{% endhighlight %}</pre> diff --git a/en/reference/applications/testing-java.mdx b/en/reference/applications/testing-java.mdx new file mode 100644 index 0000000000..946075629a --- /dev/null +++ b/en/reference/applications/testing-java.mdx @@ -0,0 +1,81 @@ +--- +title: "Testing with Java JUnit tests" +sidebarTitle: "System test (Java)" +--- + + +This is the Vespa Testing reference for [Vespa application system tests](/en/applications/testing) written in Java, as JUnit 5 unit test. + +These tests verify the behaviour of a Vespa application by using its HTTP interfaces. To write tests without Java, see [basic HTTP test reference](/en/reference/applications/testing). + +See the [testing guide](/en/applications/testing) for examples of how to run the tests. + +## Test suites + +The [testing documentation](/en/applications/testing) defines three test scenarios, comprised of four test code categories. The *system test framework* in [com.yahoo.vespa:tenant-cd-api](https://search.maven.org/artifact/com.yahoo.vespa/tenant-cd-api) uses Java annotations to declare what category a JUnit test class belongs to. To run tests with Maven belonging to a specific category, a JUnit 5 test *tag* must be specified: + +```sh +$ mvn test -D test.categories=system -D vespa.test.config=/path-to/test-config.json +``` + +<table className="vespa-table"> + <thead> + <tr> + <th>Category</th> + <th>Annotation</th> + <th>JUnit tag</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>System test</td> + <td>@SystemTest</td> + <td>system</td> + <td>Independent, functional tests</td> + </tr> + <tr> + <td>Staging setup</td> + <td>@StagingSetup</td> + <td>staging-setup</td> + <td>Set state before upgrade</td> + </tr> + <tr> + <td>Staging test</td> + <td>@StagingTest</td> + <td>staging</td> + <td>Verify state after upgrade</td> + </tr> + <tr> + <td>Production test</td> + <td>@ProductionTest</td> + <td>production</td> + <td>Verify domain specific metrics</td> + </tr> + </tbody> +</table> + +For an example including system and staging tests, check out the [sample application test suite](https://github.com/vespa-cloud/examples/tree/main/CI-CD/production-deployment-with-tests-java). + +## TestNG + +Combining Vespa JUnit 5 test suites with unit tests in TestNG is possible. You'll need to explicitly configure Maven's surefire plugin to enable integration for both frameworks. To execute the Vespa test suites specify `-D test.categories=[tag]`, where *\[tag\]* is one of the values listed in [Test suites](#test-suites). + +```xml +<plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-surefire-plugin</artifactId> + <dependencies> + <dependency> + <groupId>org.apache.maven.surefire</groupId> + <artifactId>surefire-junit-platform</artifactId> + <version>${surefire.vespa.tenant.version}</version> + </dependency> + <dependency> + <groupId>org.apache.maven.surefire</groupId> + <artifactId>surefire-testng</artifactId> + <version>${surefire.vespa.tenant.version}</version> + </dependency> + </dependencies> +</plugin> +``` diff --git a/en/reference/applications/testing.html b/en/reference/applications/testing.html deleted file mode 100644 index e95fae2750..0000000000 --- a/en/reference/applications/testing.html +++ /dev/null @@ -1,272 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: Basic HTTP testing -redirect_from: -- en/reference/testing.html ---- - -<p> - This is the Vespa Testing reference for basic HTTP tests, used to write - <a href="../../applications/testing.html">Vespa application system tests</a>. -</p> -<p> - These tests verify the behaviour of a Vespa application by using its HTTP interfaces. - Basic HTTP tests are written in JSON; to write more advanced tests, see - the <a href="testing-java.html">Java testing reference</a>. -</p> -<p>See the <a href="../../applications/testing.html">testing guide</a> for examples of how to run the tests.</p> - - - -<h2 id="test-suites">Test suites</h2> -<p> - The <a href="../../applications/testing.html">testing documentation</a> defines three test scenarios, - comprised of four test code categories. For basic HTTP tests, the category of a test is defined - by its placement in the application tests directory: -</p> -<table class="table"> - <thead> - <tr> - <th style="width:200px;font-weight:bold">Category</th> - <th style="width:300px">Directory</th> - <th>Description</th> - </tr> - </thead> - <tbody> - <tr> - <td style="font-weight:bold">System test</td> - <td>tests/system-test/</td> - <td>Independent, functional tests</td> - </tr><tr> - <td style="font-weight:bold">Staging setup</td> - <td>tests/staging-setup/</td> - <td>Set state before upgrade</td> - </tr><tr> - <td style="font-weight:bold">Staging test</td> - <td>tests/staging-test/</td> - <td>Verify state after upgrade</td> - </tr><tr> - <td style="font-weight:bold">Production test</td> - <td>tests/production-test/</td> - <td>Verify domain specific metrics</td> - </tr> -</table> -{% include note.html content="If the application package has Java code, -the <code>tests</code> directory is <code>src/test/application/tests</code>"%} -<p> - Each test is described by a JSON file, and may include other files using relative paths: -</p> -<pre> -$ ls -1 tests/*/* - -tests/production-test/metrics-test.json -tests/staging-setup/set-up-old-documents.json -tests/staging-test/verify-search-still-works.json -tests/system-test/data/document.json -tests/system-test/feed-and-search-test.json -tests/system-test/ranking-test.json -</pre> -<p> - For an example with actual system and staging tests, check out a Vespa Cloud - <a href="https://github.com/vespa-engine/sample-apps/tree/master/examples/operations/CI-CD">sample test suite</a>. - Since production tests are highly application-specific, this suite has none, but such a test could be: -</p> -<pre>{% highlight json %} -{ - "steps": [ - { - "request": { - "uri": "https://my.external.service/metrics/?query=customer-engagement" - } - } - ] -} -{% endhighlight %}</pre> - - - -<h2 id="test-file-structure">Test file structure</h2> -<p> -Each <code>.json</code> file directly under any of the directories listed above describes one test. -Each test consists of a series of steps, and each step specifies an HTTP request to run -and some assertions about the response to obtain. -Some additional properties may also be specified on both the test and step levels. -A full example, with <code>//</code> comments: -</p> -<pre> -{ - "name": "my test", - "defaults": { - "cluster": "default", - "parameters": { - "timeout": "1.618s" - } - }, - "steps": [ - { - "name": "clear existing documents", - "request": { - "method": "DELETE", - "uri": "/document/v1/", - "parameters": { - "cluster": "music", - "selection": "true" - } - } - }, - { - "name": "feed foo", - "request": { - "method": "POST", - // should contain payload as expected by /document/v1/ - "body": "foo/body.json", - // specify only the path and query for Vespa requests - "uri": "/document/v1/test/music/docid/foo?timeout=8s", - // JSON object file; merged with query from "uri" - "parameters": "foo/parameters.json" - } - // no response spec: just assert code 200 - }, - { - "name": "query for foo", - "request": { - // no "uri": defaults to "/search/" - "parameters": { - "query": "artist: foo" - } - }, - "response": { - "body": { - "root": { - "children": [ - // assert "children" has a single element ... - { - // ... which has the field "fields" ... - "fields": { - // ... where the field "artist" is "Foo Fighters" ... - "artist": "Foo Fighters" - }, - // ... and the field "relevance" close to 0.381862383599 - "relevance": 0.381862383599 - } - ] - } - } - } - } - ] -} -</pre> - - -<h3 id="test-json-specification">Test JSON specification</h3> -<p>A full list of fields, with description:</p> -<table class="table"> - <thead> - <tr> - <th style="width:100px;font-weight:bold">Name</th> - <th style="width:100px">Parent</th> - <th style="width:100px">Type</th> - <th style="width:150px">Default</th> - <th>Description</th> - </tr> - </thead> - <tbody> - <tr> - <td style="font-weight:bold">name</td> - <td>root<br>step</td> - <td>string</td> - <td>file name, step <em>n</em></td> - <td>Name used for display purposes in the test report. The file name is used by default - for the test, while the 1-indexed "step n" is used for steps.</td> - </tr><tr> - <td style="font-weight:bold">defaults</td> - <td>root</td> - <td>object</td> - <td></td> - <td>Default settings for all steps in this test. May be overridden in each step.</td> - </tr><tr> - <td style="font-weight:bold">steps</td> - <td>root</td> - <td>array</td> - <td></td> - <td>The non-empty list of steps that constitute this test.</td> - </tr><tr> - <td style="font-weight:bold">request</td> - <td>step</td> - <td>object</td> - <td></td> - <td>A specification of a request to send, to Vespa, or to an external service.</td> - </tr><tr> - <td style="font-weight:bold">cluster</td> - <td>defaults request</td> - <td>string</td> - <td></td> - <td>The name of the Vespa cluster to send a request to, as specified in <a href="services/services.html">services.xml</a>. - If this is not specified, and the application has a single container cluster, this is used.</td> - </tr><tr> - <td style="font-weight:bold">method</td> - <td>request</td> - <td>string</td> - <td>"GET"</td> - <td>The HTTP method to use for a request.</td> - </tr><tr> - <td style="font-weight:bold">uri</td> - <td>request</td> - <td>string</td> - <td>"/search/"</td> - <td>When this is path + (encoded) query, the host is determined by the specified cluster; - otherwise, it must be an absolute URI (with scheme), and its host is used. - Query parameters specified here override those specified in the defaults.</td> - </tr><tr> - <td style="font-weight:bold">parameters</td> - <td>defaults request</td> - <td>string object</td> - <td></td> - <td>HTTP request query parameters. The values should not be encoded. These are merged - with parameters from the specified URI, and override those specified in the defaults. - If the value is a string, it must be a relative file reference to a parameters object.</td> - </tr><tr> - <td style="font-weight:bold">body</td> - <td>request response</td> - <td>string object</td> - <td></td> - <td>The body for a request, or the partial body (see <a href="#json-matching">matching</a>) - for a response. If the value is a string, it must be a relative file reference to - a JSON object to be used in its place.</td> - </tr><tr> - <td style="font-weight:bold">response</td> - <td>step</td> - <td>object</td> - <td></td> - <td>A specification for assertions to make on the body of the HTTP response obtained - by executing the HTTP request in the same step.</td> - </tr><tr> - <td style="font-weight:bold">code</td> - <td>response</td> - <td>number</td> - <td>200</td> - <td>The status code the response should have.</td> - </tr> - </tbody> -</table> - - -<h3 id="json-matching">JSON matching</h3> -<p> -All requests and responses must be in JSON format. The tests allow simple JSON verification, -by describing <em>what should be present</em> in the actual response. This is done by -specifying a JSON structure, a <em>template</em>, for each response, and requiring each field -present in the template to match fields in the actual response. Unmatched fields result in a test -failure, with the following rules: -</p> -<ul> - <li>Objects must contain all listed fields, and may also contain unlisted ones.</li> - <li>Arrays must match element-by-element.</li> - <li>Numbers must match within precision <code>1e-9</code>.</li> - <li>All other values must match exactly.</li> -</ul> -<p> -Note that the empty object <code>{ }</code> matches any other object, and can be used to fill -elements of an array that require no further validation. -</p> diff --git a/en/reference/applications/testing.mdx b/en/reference/applications/testing.mdx new file mode 100644 index 0000000000..e1eb8b19aa --- /dev/null +++ b/en/reference/applications/testing.mdx @@ -0,0 +1,254 @@ +--- +title: "Basic HTTP testing" +sidebarTitle: "System test" +--- + +This is the Vespa Testing reference for basic HTTP tests, used to write [Vespa application system tests](/en/applications/testing). + +These tests verify the behaviour of a Vespa application by using its HTTP interfaces. Basic HTTP tests are written in JSON; to write more advanced tests, see the [Java testing reference](/en/reference/applications/testing-java). + +See the [testing guide](/en/applications/testing) for examples of how to run the tests. + +## Test suites + +The [testing documentation](/en/applications/testing) defines three test scenarios, comprised of four test code categories. For basic HTTP tests, the category of a test is defined by its placement in the application tests directory: + +<table className="vespa-table"> + <thead> + <tr> + <th>Category</th> + <th>Directory</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>System test</td> + <td>tests/system-test/</td> + <td>Independent, functional tests</td> + </tr> + <tr> + <td>Staging setup</td> + <td>tests/staging-setup/</td> + <td>Set state before upgrade</td> + </tr> + <tr> + <td>Staging test</td> + <td>tests/staging-test/</td> + <td>Verify state after upgrade</td> + </tr> + <tr> + <td>Production test</td> + <td>tests/production-test/</td> + <td>Verify domain specific metrics</td> + </tr> + </tbody> +</table> + +<Note> +**Note:** + +If the application package has Java code, the `tests` directory is `src/test/application/tests` +</Note> + +Each test is described by a JSON file, and may include other files using relative paths: + +```bash +$ ls -1 tests/*/* + +tests/production-test/metrics-test.json +tests/staging-setup/set-up-old-documents.json +tests/staging-test/verify-search-still-works.json +tests/system-test/data/document.json +tests/system-test/feed-and-search-test.json +tests/system-test/ranking-test.json +``` + +For an example with actual system and staging tests, check out a Vespa Cloud [sample test suite](https://github.com/vespa-engine/sample-apps/tree/master/examples/operations/CI-CD). Since production tests are highly application-specific, this suite has none, but such a test could be: + +```json +{ + "steps": [ + { + "request": { + "uri": "https://my.external.service/metrics/?query=customer-engagement" + } + } + ] +} +``` + +## Test file structure + +Each `.json` file directly under any of the directories listed above describes one test. Each test consists of a series of steps, and each step specifies an HTTP request to run and some assertions about the response to obtain. Some additional properties may also be specified on both the test and step levels. A full example, with `//` comments: + +```json expandable +{ + "name": "my test", + "defaults": { + "cluster": "default", + "parameters": { + "timeout": "1.618s" + } + }, + "steps": [ + { + "name": "clear existing documents", + "request": { + "method": "DELETE", + "uri": "/document/v1/", + "parameters": { + "cluster": "music", + "selection": "true" + } + } + }, + { + "name": "feed foo", + "request": { + "method": "POST", + // should contain payload as expected by /document/v1/ + "body": "foo/body.json", + // specify only the path and query for Vespa requests + "uri": "/document/v1/test/music/docid/foo?timeout=8s", + // JSON object file; merged with query from "uri" + "parameters": "foo/parameters.json" + } + // no response spec: just assert code 200 + }, + { + "name": "query for foo", + "request": { + // no "uri": defaults to "/search/" + "parameters": { + "query": "artist: foo" + } + }, + "response": { + "body": { + "root": { + "children": [ + // assert "children" has a single element ... + { + // ... which has the field "fields" ... + "fields": { + // ... where the field "artist" is "Foo Fighters" ... + "artist": "Foo Fighters" + }, + // ... and the field "relevance" close to 0.381862383599 + "relevance": 0.381862383599 + } + ] + } + } + } + } + ] +} +``` + +### Test JSON specification + +A full list of fields, with description: + +<table className="vespa-table"> + <thead> + <tr> + <th>Name</th> + <th>Parent</th> + <th>Type</th> + <th>Default</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>name</td> + <td>root step</td> + <td>string</td> + <td>file name, step *n*</td> + <td>Name used for display purposes in the test report. The file name is used by default for the test, while the 1-indexed "step n" is used for steps.</td> + </tr> + <tr> + <td>defaults</td> + <td>root</td> + <td>object</td> + <td></td> + <td>Default settings for all steps in this test. May be overridden in each step.</td> + </tr> + <tr> + <td>steps</td> + <td>root</td> + <td>array</td> + <td></td> + <td>The non-empty list of steps that constitute this test.</td> + </tr> + <tr> + <td>request</td> + <td>step</td> + <td>object</td> + <td></td> + <td>A specification of a request to send, to Vespa, or to an external service.</td> + </tr> + <tr> + <td>cluster</td> + <td>defaults request</td> + <td>string</td> + <td></td> + <td>The name of the Vespa cluster to send a request to, as specified in <a href="/en/reference/applications/services/services" aria-label="services.xml">services.xml</a>. If this is not specified, and the application has a single container cluster, this is used.</td> + </tr> + <tr> + <td>method</td> + <td>request</td> + <td>string</td> + <td>"GET"</td> + <td>The HTTP method to use for a request.</td> + </tr> + <tr> + <td>uri</td> + <td>request</td> + <td>string</td> + <td>"/search/"</td> + <td>When this is path + (encoded) query, the host is determined by the specified cluster; otherwise, it must be an absolute URI (with scheme), and its host is used. Query parameters specified here override those specified in the defaults.</td> + </tr> + <tr> + <td>parameters</td> + <td>defaults request</td> + <td>string object</td> + <td></td> + <td>HTTP request query parameters. The values should not be encoded. These are merged with parameters from the specified URI, and override those specified in the defaults. If the value is a string, it must be a relative file reference to a parameters object.</td> + </tr> + <tr> + <td>body</td> + <td>request response</td> + <td>string object</td> + <td></td> + <td>The body for a request, or the partial body (see <a href="#json-matching" aria-label="matching">matching</a>) for a response. If the value is a string, it must be a relative file reference to a JSON object to be used in its place.</td> + </tr> + <tr> + <td>response</td> + <td>step</td> + <td>object</td> + <td></td> + <td>A specification for assertions to make on the body of the HTTP response obtained by executing the HTTP request in the same step.</td> + </tr> + <tr> + <td>code</td> + <td>response</td> + <td>number</td> + <td>200</td> + <td>The status code the response should have.</td> + </tr> + </tbody> +</table> + +### JSON matching + +All requests and responses must be in JSON format. The tests allow simple JSON verification, by describing *what should be present* in the actual response. This is done by specifying a JSON structure, a *template*, for each response, and requiring each field present in the template to match fields in the actual response. Unmatched fields result in a test failure, with the following rules: + +- Objects must contain all listed fields, and may also contain unlisted ones. +- Arrays must match element-by-element. +- Numbers must match within precision `1e-9`. +- All other values must match exactly. + +Note that the empty object `{ }` matches any other object, and can be used to fill elements of an array that require no further validation. diff --git a/en/reference/applications/validation-overrides.html b/en/reference/applications/validation-overrides.html deleted file mode 100644 index b5b0eda2d8..0000000000 --- a/en/reference/applications/validation-overrides.html +++ /dev/null @@ -1,79 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: validation-overrides.xml -redirect_from: -- en/reference/validation-overrides.html ---- - -<p> - <em>validation-overrides.xml</em> is added to the root of an - <a href="application-packages.html">application package</a> - (i.e. next to <a href="services/services.html">services.xml</a>) - to allow a deployment that otherwise fails to validate to proceed. - The message will explain what the validation concerns and how to add - a validation override -</p> -<p> - Validations protect against inadvertently corrupting a production instance. - Overriding them may be useful e.g. if the application is not in production yet - or if you think the consequences of inconsistencies or loss of the data in a particular field are fine. -</p> -<p> - Read more about schema changes in the <a href="../schemas/schemas.html#modifying-schemas">schema reference</a>. -</p> - - - -<h2 id="structure">Structure</h2> -<pre>{% highlight xml %} -<validation-overrides> - <allow until="iso-8601-date" comment="Optional note">validation-id</allow> -</validation-overrides> -{% endhighlight %}</pre> -<p> - Any number of <code>allow</code> tags is permissible. Example: -</p> -<pre>{% highlight xml %} -<validation-overrides> - <allow - until="2025-01-31" - comment="Reduce to needed cluster size after benchmarking">resources-reduction</allow> - <allow - until="2025-02-03">field-type-change</allow> -</validation-overrides> -{% endhighlight %}</pre> - - - -<h2 id="allow">allow</h2> -<p> - An <code>allow</code> tag disables a particular validation for a limited time and - contains a single validation id. <code>allow</code> tags with unknown ids are ignored. -</p> -<table class="table"> - <thead> - <tr> - <th>Attribute</th><th>Mandatory</th><th>Value</th> - </tr> - </thead> - <tbody> -<tr> - <td>until</td> - <td>Yes</td> - <td>The last day this change is allowed, as a ISO-8601-format date in UTC, e.g. 2016-01-30. - Dates may at most be 30 days in the future, but should be as close to now as possible for safety, - while allowing time for review and propagation to all deployed zones. - <code>allow</code>-tags with dates in the past are ignored.</td> -</tr> -<tr> - <td>comment</td> - <td>No</td> - <td>Text explaining the reason for the change to humans.</td> -</tr> - </tbody> -</table> - -<h2 id="list">List of validation overrides</h2> - -<p>See <a href="https://github.com/vespa-engine/vespa/blob/master/config-model-api/src/main/java/com/yahoo/config/application/api/ValidationId.java"> -ValidationId.java</a> for a complete list of validation overrides.</p> diff --git a/en/reference/applications/validation-overrides.mdx b/en/reference/applications/validation-overrides.mdx new file mode 100644 index 0000000000..edb28cc9f1 --- /dev/null +++ b/en/reference/applications/validation-overrides.mdx @@ -0,0 +1,61 @@ +--- +title: "validation-overrides.xml" +sidebarTitle: "" +--- + + +*validation-overrides.xml* is added to the root of an [application package](/en/reference/applications/application-packages) (i.e. next to [services.xml](/en/reference/applications/services/services)) to allow a deployment that otherwise fails to validate to proceed. The message will explain what the validation concerns and how to add a validation override + +Validations protect against inadvertently corrupting a production instance. Overriding them may be useful e.g. if the application is not in production yet or if you think the consequences of inconsistencies or loss of the data in a particular field are fine. + +Read more about schema changes in the [schema reference](/en/reference/schemas/schemas#modifying-schemas). + +## Structure + +```xml +<validation-overrides> + <allow until="iso-8601-date" comment="Optional note">validation-id</allow> +</validation-overrides> +``` + +Any number of `allow` tags is permissible. Example: + +```xml +<validation-overrides> + <allow + until="2025-01-31" + comment="Reduce to needed cluster size after benchmarking">resources-reduction</allow> + <allow + until="2025-02-03">field-type-change</allow> +</validation-overrides> +``` + +## allow + +An `allow` tag disables a particular validation for a limited time and contains a single validation id. `allow` tags with unknown ids are ignored. + +<table className="vespa-table"> + <thead> + <tr> + <th>Attribute</th> + <th>Mandatory</th> + <th>Value</th> + </tr> + </thead> + <tbody> + <tr> + <td>until</td> + <td>Yes</td> + <td>The last day this change is allowed, as a ISO-8601-format date in UTC, e.g. 2016-01-30. Dates may at most be 30 days in the future, but should be as close to now as possible for safety, while allowing time for review and propagation to all deployed zones. <code>{`allow`}</code>-tags with dates in the past are ignored.</td> + </tr> + <tr> + <td>comment</td> + <td>No</td> + <td>Text explaining the reason for the change to humans.</td> + </tr> + </tbody> +</table> + +## List of validation overrides + +See [ValidationId.java](https://github.com/vespa-engine/vespa/blob/master/config-model-api/src/main/java/com/yahoo/config/application/api/ValidationId.java) for a complete list of validation overrides. diff --git a/en/reference/clients/vespa-cli/vespa.md b/en/reference/clients/vespa-cli/vespa.md deleted file mode 100644 index 69f3f6a465..0000000000 --- a/en/reference/clients/vespa-cli/vespa.md +++ /dev/null @@ -1,63 +0,0 @@ ---- -title: vespa -render_with_liquid: false ---- - -## vespa - -The command-line tool for Vespa.ai - -### Synopsis - -The command-line tool for Vespa.ai. - -Use it on Vespa instances running locally, remotely or in Vespa Cloud - -use a token for Vespa Cloud access: - -$ export VESPA_CLI_DATA_PLANE_TOKEN='value-of-token' - -To get started, follow [https://docs.vespa.ai/en/basics/deploy-an-application.html](https://docs.vespa.ai/en/basics/deploy-an-application.html) - -The complete Vespa documentation is available at https://docs.vespa.ai. - -For detailed description of flags and configuration, see 'vespa help config'. - - -``` -vespa [flags] -``` - -### Options - -``` - -a, --application string The application to use. Format "tenant.application.instance" - instance is optional (tenant required for cloud targets) - -C, --cluster string The container cluster to use. This is only required for applications with multiple clusters - -c, --color string Whether to use colors in output. Must be "auto", "never", or "always" (default "auto") - -h, --help help for vespa - -i, --instance string The instance of the application to use - -q, --quiet Print only errors - -t, --target string The target platform to use. Must be "local", "cloud", "hosted" or an URL (default "local") - -z, --zone string The zone to use. This defaults to a dev zone (cloud only) -``` - -### SEE ALSO - -* [vespa activate](vespa_activate.html) - Activate (deploy) a previously prepared application package -* [vespa auth](vespa_auth.html) - Manage Vespa Cloud credentials -* [vespa clone](vespa_clone.html) - Create files and directory structure from a Vespa sample application -* [vespa config](vespa_config.html) - Manage persistent values for global flags -* [vespa curl](vespa_curl.html) - Access Vespa directly using curl -* [vespa deploy](vespa_deploy.html) - Deploy (prepare and activate) an application package -* [vespa destroy](vespa_destroy.html) - Remove a deployed Vespa application and its data -* [vespa document](vespa_document.html) - Issue a single document operation to Vespa -* [vespa feed](vespa_feed.html) - Feed multiple document operations to Vespa -* [vespa fetch](vespa_fetch.html) - Download a deployed application package -* [vespa log](vespa_log.html) - Show the Vespa log -* [vespa prepare](vespa_prepare.html) - Prepare an application package for activation -* [vespa prod](vespa_prod.html) - Deploy an application package to production in Vespa Cloud -* [vespa query](vespa_query.html) - Issue a query to Vespa -* [vespa status](vespa_status.html) - Show Vespa endpoints and status -* [vespa test](vespa_test.html) - Run a test suite, or a single test -* [vespa version](vespa_version.html) - Show current CLI version and check for updates -* [vespa visit](vespa_visit.html) - Retrieve and print all documents from Vespa - diff --git a/en/reference/clients/vespa-cli/vespa.mdx b/en/reference/clients/vespa-cli/vespa.mdx new file mode 100644 index 0000000000..ba43db76bf --- /dev/null +++ b/en/reference/clients/vespa-cli/vespa.mdx @@ -0,0 +1,60 @@ +--- +title: "vespa" +description: "The command-line tool for Vespa.ai" +--- + +### Synopsis + +The command-line tool for Vespa.ai. + +Use it on Vespa instances running locally, remotely or in Vespa Cloud - use a token for Vespa Cloud access: + +```bash +$ export VESPA_CLI_DATA_PLANE_TOKEN='value-of-token' +``` + +To get started, follow [/en/basics/deploy-an-application.html](/en/basics/deploy-an-application) + +The complete Vespa documentation is available at https://docs.vespa.ai. + +For detailed description of flags and configuration, see 'vespa help config'. + + +```bash +vespa [flags] +``` + +### Options + +```bash + -a, --application string The application to use. Format "tenant.application.instance" - instance is optional (tenant required for cloud targets) + -C, --cluster string The container cluster to use. This is only required for applications with multiple clusters + -c, --color string Whether to use colors in output. Must be "auto", "never", or "always" (default "auto") + -h, --help help for vespa + -i, --instance string The instance of the application to use + -q, --quiet Print only errors + -t, --target string The target platform to use. Must be "local", "cloud", "hosted" or an URL (default "local") + -z, --zone string The zone to use. This defaults to a dev zone (cloud only) +``` + +### SEE ALSO + +* [vespa activate](/en/reference/clients/vespa-cli/vespa_activate) - Activate (deploy) a previously prepared application package +* [vespa auth](/en/reference/clients/vespa-cli/vespa_auth) - Manage Vespa Cloud credentials +* [vespa clone](/en/reference/clients/vespa-cli/vespa_clone) - Create files and directory structure from a Vespa sample application +* [vespa config](/en/reference/clients/vespa-cli/vespa_config) - Manage persistent values for global flags +* [vespa curl](/en/reference/clients/vespa-cli/vespa_curl) - Access Vespa directly using curl +* [vespa deploy](/en/reference/clients/vespa-cli/vespa_deploy) - Deploy (prepare and activate) an application package +* [vespa destroy](/en/reference/clients/vespa-cli/vespa_destroy) - Remove a deployed Vespa application and its data +* [vespa document](/en/reference/clients/vespa-cli/vespa_document) - Issue a single document operation to Vespa +* [vespa feed](/en/reference/clients/vespa-cli/vespa_feed) - Feed multiple document operations to Vespa +* [vespa fetch](/en/reference/clients/vespa-cli/vespa_fetch) - Download a deployed application package +* [vespa log](/en/reference/clients/vespa-cli/vespa_log) - Show the Vespa log +* [vespa prepare](/en/reference/clients/vespa-cli/vespa_prepare) - Prepare an application package for activation +* [vespa prod](/en/reference/clients/vespa-cli/vespa_prod) - Deploy an application package to production in Vespa Cloud +* [vespa query](/en/reference/clients/vespa-cli/vespa_query) - Issue a query to Vespa +* [vespa status](/en/reference/clients/vespa-cli/vespa_status) - Show Vespa endpoints and status +* [vespa test](/en/reference/clients/vespa-cli/vespa_test) - Run a test suite, or a single test +* [vespa version](/en/reference/clients/vespa-cli/vespa_version) - Show current CLI version and check for updates +* [vespa visit](/en/reference/clients/vespa-cli/vespa_visit) - Retrieve and print all documents from Vespa + diff --git a/en/reference/clients/vespa-cli/vespa_activate.md b/en/reference/clients/vespa-cli/vespa_activate.mdx similarity index 82% rename from en/reference/clients/vespa-cli/vespa_activate.md rename to en/reference/clients/vespa-cli/vespa_activate.mdx index 6d9a394caf..a7d2e8cb7e 100644 --- a/en/reference/clients/vespa-cli/vespa_activate.md +++ b/en/reference/clients/vespa-cli/vespa_activate.mdx @@ -1,26 +1,22 @@ --- -title: vespa activate -render_with_liquid: false +title: "vespa activate" +description: "Activate (deploy) a previously prepared application package" --- -## vespa activate - -Activate (deploy) a previously prepared application package - -``` +```bash vespa activate [flags] ``` ### Options -``` +```bash -h, --help help for activate -w, --wait int Number of seconds to wait for service(s) to become ready. 0 to disable (default 0) ``` ### Options inherited from parent commands -``` +```bash -a, --application string The application to use. Format "tenant.application.instance" - instance is optional (tenant required for cloud targets) -C, --cluster string The container cluster to use. This is only required for applications with multiple clusters -c, --color string Whether to use colors in output. Must be "auto", "never", or "always" (default "auto") @@ -32,5 +28,5 @@ vespa activate [flags] ### SEE ALSO -* [vespa](vespa.html) - The command-line tool for Vespa.ai +* [vespa](/en/reference/clients/vespa-cli/vespa) - The command-line tool for Vespa.ai diff --git a/en/reference/clients/vespa-cli/vespa_auth.md b/en/reference/clients/vespa-cli/vespa_auth.mdx similarity index 50% rename from en/reference/clients/vespa-cli/vespa_auth.md rename to en/reference/clients/vespa-cli/vespa_auth.mdx index cfda9750e7..774d23948e 100644 --- a/en/reference/clients/vespa-cli/vespa_auth.md +++ b/en/reference/clients/vespa-cli/vespa_auth.mdx @@ -1,29 +1,25 @@ --- -title: vespa auth -render_with_liquid: false +title: "vespa auth" +description: "Manage Vespa Cloud credentials" --- -## vespa auth - -Manage Vespa Cloud credentials - ### Synopsis Manage Vespa Cloud credentials. -``` +```bash vespa auth [flags] ``` ### Options -``` +```bash -h, --help help for auth ``` ### Options inherited from parent commands -``` +```bash -a, --application string The application to use. Format "tenant.application.instance" - instance is optional (tenant required for cloud targets) -C, --cluster string The container cluster to use. This is only required for applications with multiple clusters -c, --color string Whether to use colors in output. Must be "auto", "never", or "always" (default "auto") @@ -35,10 +31,10 @@ vespa auth [flags] ### SEE ALSO -* [vespa](vespa.html) - The command-line tool for Vespa.ai -* [vespa auth api-key](vespa_auth_api-key.html) - Create a new developer key for headless authentication with Vespa Cloud control plane -* [vespa auth cert](vespa_auth_cert.html) - Create a new self-signed certificate for authentication with Vespa Cloud data plane -* [vespa auth login](vespa_auth_login.html) - Authenticate Vespa CLI with Vespa Cloud control plane. This is preferred over api-key for interactive use -* [vespa auth logout](vespa_auth_logout.html) - Sign out of Vespa Cloud -* [vespa auth show](vespa_auth_show.html) - Show authenticated user +* [vespa](/en/reference/clients/vespa-cli/vespa) - The command-line tool for Vespa.ai +* [vespa auth api-key](/en/reference/clients/vespa-cli/vespa_auth_api-key) - Create a new developer key for headless authentication with Vespa Cloud control plane +* [vespa auth cert](/en/reference/clients/vespa-cli/vespa_auth_cert) - Create a new self-signed certificate for authentication with Vespa Cloud data plane +* [vespa auth login](/en/reference/clients/vespa-cli/vespa_auth_login) - Authenticate Vespa CLI with Vespa Cloud control plane. This is preferred over api-key for interactive use +* [vespa auth logout](/en/reference/clients/vespa-cli/vespa_auth_logout) - Sign out of Vespa Cloud +* [vespa auth show](/en/reference/clients/vespa-cli/vespa_auth_show) - Show authenticated user diff --git a/en/reference/clients/vespa-cli/vespa_auth_api-key.md b/en/reference/clients/vespa-cli/vespa_auth_api-key.mdx similarity index 66% rename from en/reference/clients/vespa-cli/vespa_auth_api-key.md rename to en/reference/clients/vespa-cli/vespa_auth_api-key.mdx index 08fc4faa8d..6dbb1eacaa 100644 --- a/en/reference/clients/vespa-cli/vespa_auth_api-key.md +++ b/en/reference/clients/vespa-cli/vespa_auth_api-key.mdx @@ -1,26 +1,17 @@ --- -title: vespa auth api-key -render_with_liquid: false +title: "vespa auth api-key" +description: "Create a new developer key for headless authentication with Vespa Cloud control plane" --- -## vespa auth api-key - -Create a new developer key for headless authentication with Vespa Cloud control plane - ### Synopsis Create a new developer key for headless authentication with Vespa Cloud control plane -A developer key is a tenant-wide secret, intended for headless communication -with the Vespa Cloud control plane. For example when deploying from a continuous -integration system. +A developer key is a tenant-wide secret, intended for headless communication with the Vespa Cloud control plane. For example when deploying from a continuous integration system. -The developer key will be stored in the Vespa CLI home directory -(see 'vespa help config'). Other commands will then automatically load the developer -key as necessary. +The developer key will be stored in the Vespa CLI home directory (see 'vespa help config'). Other commands will then automatically load the developer key as necessary. -It's possible to override the developer key used through environment variables. This -can be useful in continuous integration systems. +It's possible to override the developer key used through environment variables. This can be useful in continuous integration systems. Example of setting the key in-line: @@ -30,32 +21,30 @@ Example of loading the key from a custom path: export VESPA_CLI_API_KEY_FILE=/path/to/api-key -Note that when overriding the developer key through environment variables, -that key will always be used. It's not possible to specify a tenant-specific -key through the environment. +Note that when overriding the developer key through environment variables, that key will always be used. It's not possible to specify a tenant-specific key through the environment. -See [https://docs.vespa.ai/en/security/guide.html](https://docs.vespa.ai/en/security/guide.html) for more details about developer keys. +See [/en/security/guide.html](/en/security/guide) for more details about developer keys. -``` +```bash vespa auth api-key [flags] ``` ### Examples -``` +```bash $ vespa auth api-key -a my-tenant.my-app.my-instance ``` ### Options -``` +```bash -f, --force Force overwrite of existing developer key -h, --help help for api-key ``` ### Options inherited from parent commands -``` +```bash -a, --application string The application to use. Format "tenant.application.instance" - instance is optional (tenant required for cloud targets) -C, --cluster string The container cluster to use. This is only required for applications with multiple clusters -c, --color string Whether to use colors in output. Must be "auto", "never", or "always" (default "auto") @@ -67,5 +56,5 @@ $ vespa auth api-key -a my-tenant.my-app.my-instance ### SEE ALSO -* [vespa auth](vespa_auth.html) - Manage Vespa Cloud credentials +* [vespa auth](/en/reference/clients/vespa-cli/vespa_auth) - Manage Vespa Cloud credentials diff --git a/en/reference/clients/vespa-cli/vespa_auth_cert.md b/en/reference/clients/vespa-cli/vespa_auth_cert.mdx similarity index 66% rename from en/reference/clients/vespa-cli/vespa_auth_cert.md rename to en/reference/clients/vespa-cli/vespa_auth_cert.mdx index 37cee545cf..8641303e63 100644 --- a/en/reference/clients/vespa-cli/vespa_auth_cert.md +++ b/en/reference/clients/vespa-cli/vespa_auth_cert.mdx @@ -1,28 +1,17 @@ --- -title: vespa auth cert -render_with_liquid: false +title: "vespa auth cert" +description: "Create a new self-signed certificate for authentication with Vespa Cloud data plane" --- -## vespa auth cert - -Create a new self-signed certificate for authentication with Vespa Cloud data plane - ### Synopsis Create a new self-signed certificate for authentication with Vespa Cloud data plane. -The private key and certificate will be stored in the Vespa CLI home directory -(see 'vespa help config'). Other commands will then automatically load the -certificate as necessary. The certificate will be added to your application -package specified as an argument to this command (default '.'). +The private key and certificate will be stored in the Vespa CLI home directory (see 'vespa help config'). Other commands will then automatically load the certificate as necessary. The certificate will be added to your application package specified as an argument to this command (default '.'). -It's possible to override the private key and certificate used through -environment variables. This can be useful in continuous integration systems. +It's possible to override the private key and certificate used through environment variables. This can be useful in continuous integration systems. -It's also possible override the CA certificate which can be useful when using -self-signed certificates with a self-hosted Vespa service. -See [https://docs.vespa.ai/en/security/mtls.html](https://docs.vespa.ai/en/security/mtls.html) for more -information. +It's also possible override the CA certificate which can be useful when using self-signed certificates with a self-hosted Vespa service. See [/en/security/mtls.html](/en/security/mtls) for more information. Example of setting the CA certificate, certificate and key in-line: @@ -41,19 +30,17 @@ hostname: export VESPA_CLI_DATA_PLANE_TRUST_ALL=true -Note that when overriding key pair through environment variables, that key pair -will always be used for all applications. It's not possible to specify an -application-specific key. +Note that when overriding key pair through environment variables, that key pair will always be used for all applications. It's not possible to specify an application-specific key. -See [https://docs.vespa.ai/en/security/guide.html](https://docs.vespa.ai/en/security/guide.html) for more details. +See [/en/security/guide.html](/en/security/guide) for more details. -``` +```bash vespa auth cert [flags] ``` ### Examples -``` +```bash $ vespa auth cert $ vespa auth cert -a my-tenant.my-app.my-instance $ vespa auth cert -a my-tenant.my-app.my-instance path/to/application/package @@ -61,7 +48,7 @@ $ vespa auth cert -a my-tenant.my-app.my-instance path/to/application/package ### Options -``` +```bash -f, --force Force overwrite of existing certificate and private key -h, --help help for cert -N, --no-add Do not add certificate to the application package @@ -69,7 +56,7 @@ $ vespa auth cert -a my-tenant.my-app.my-instance path/to/application/package ### Options inherited from parent commands -``` +```bash -a, --application string The application to use. Format "tenant.application.instance" - instance is optional (tenant required for cloud targets) -C, --cluster string The container cluster to use. This is only required for applications with multiple clusters -c, --color string Whether to use colors in output. Must be "auto", "never", or "always" (default "auto") @@ -81,6 +68,6 @@ $ vespa auth cert -a my-tenant.my-app.my-instance path/to/application/package ### SEE ALSO -* [vespa auth](vespa_auth.html) - Manage Vespa Cloud credentials -* [vespa auth cert add](vespa_auth_cert_add.html) - Add certificate to application package +* [vespa auth](/en/reference/clients/vespa-cli/vespa_auth) - Manage Vespa Cloud credentials +* [vespa auth cert add](/en/reference/clients/vespa-cli/vespa_auth_cert_add) - Add certificate to application package diff --git a/en/reference/clients/vespa-cli/vespa_auth_cert_add.md b/en/reference/clients/vespa-cli/vespa_auth_cert_add.mdx similarity index 79% rename from en/reference/clients/vespa-cli/vespa_auth_cert_add.md rename to en/reference/clients/vespa-cli/vespa_auth_cert_add.mdx index c19971bb85..d3f1547db3 100644 --- a/en/reference/clients/vespa-cli/vespa_auth_cert_add.md +++ b/en/reference/clients/vespa-cli/vespa_auth_cert_add.mdx @@ -1,43 +1,37 @@ --- -title: vespa auth cert add -render_with_liquid: false +title: "vespa auth cert add" +description: "Add certificate to application package" --- -## vespa auth cert add - -Add certificate to application package - ### Synopsis Add an existing self-signed certificate for Vespa Cloud deployment to your application package. -The certificate will be loaded from the Vespa CLI home directory (see 'vespa -help config') by default. +The certificate will be loaded from the Vespa CLI home directory (see 'vespa help config') by default. -The location of the application package can be specified as an argument to this -command (default '.'). +The location of the application package can be specified as an argument to this command (default '.'). -``` +```bash vespa auth cert add [flags] ``` ### Examples -``` +```bash $ vespa auth cert add -a my-tenant.my-app.my-instance $ vespa auth cert add -a my-tenant.my-app.my-instance path/to/application/package ``` ### Options -``` +```bash -f, --force Force overwrite of existing certificate -h, --help help for add ``` ### Options inherited from parent commands -``` +```bash -a, --application string The application to use. Format "tenant.application.instance" - instance is optional (tenant required for cloud targets) -C, --cluster string The container cluster to use. This is only required for applications with multiple clusters -c, --color string Whether to use colors in output. Must be "auto", "never", or "always" (default "auto") @@ -49,5 +43,5 @@ $ vespa auth cert add -a my-tenant.my-app.my-instance path/to/application/packag ### SEE ALSO -* [vespa auth cert](vespa_auth_cert.html) - Create a new self-signed certificate for authentication with Vespa Cloud data plane +* [vespa auth cert](/en/reference/clients/vespa-cli/vespa_auth_cert) - Create a new self-signed certificate for authentication with Vespa Cloud data plane diff --git a/en/reference/clients/vespa-cli/vespa_auth_login.md b/en/reference/clients/vespa-cli/vespa_auth_login.mdx similarity index 54% rename from en/reference/clients/vespa-cli/vespa_auth_login.md rename to en/reference/clients/vespa-cli/vespa_auth_login.mdx index 7605bd6b45..86a5121cc1 100644 --- a/en/reference/clients/vespa-cli/vespa_auth_login.md +++ b/en/reference/clients/vespa-cli/vespa_auth_login.mdx @@ -1,53 +1,37 @@ --- -title: vespa auth login -render_with_liquid: false +title: "vespa auth login" +description: "Authenticate Vespa CLI with Vespa Cloud control plane. This is preferred over api-key for interactive use" --- -## vespa auth login - -Authenticate Vespa CLI with Vespa Cloud control plane. This is preferred over api-key for interactive use - ### Synopsis Authenticate Vespa CLI with Vespa Cloud control plane. This is preferred over api-key for interactive use. This command runs a browser-based authentication flow for the Vespa Cloud control plane. -Use --file-storage flag to store the refresh token in unencrypted files instead of the system keyring. -This is useful in SSH/CI/Docker environments where keyring access may not be available. - -Use --no-browser to print the login URL to standard output instead of opening a browser automatically. -This is useful in non-interactive terminals (e.g. Git Bash on Windows) or remote sessions where -a browser cannot be opened. - -Use --batch to suppress all human-readable output and print only machine-readable key=value pairs: - code=<device-confirmation-code> - url=<verification-url> -This is useful for scripting or automation where the output is parsed programmatically. +Use --file-storage flag to store the refresh token in unencrypted files instead of the system keyring. This is useful in SSH/CI/Docker environments where keyring access may not be available. -``` +```bash vespa auth login [flags] ``` ### Examples -``` +```bash $ vespa auth login ``` ### Options -``` - --batch Print machine-readable key=value pairs (code, url) and suppress all other output +```bash --file-storage Use file storage (unencrypted) instead of keyring for storing refresh token -h, --help help for login - --no-browser Print login URL to standard output without confirmation and without automatic opening of browser ``` ### Options inherited from parent commands -``` +```bash -a, --application string The application to use. Format "tenant.application.instance" - instance is optional (tenant required for cloud targets) -C, --cluster string The container cluster to use. This is only required for applications with multiple clusters -c, --color string Whether to use colors in output. Must be "auto", "never", or "always" (default "auto") @@ -59,5 +43,5 @@ $ vespa auth login ### SEE ALSO -* [vespa auth](vespa_auth.html) - Manage Vespa Cloud credentials +* [vespa auth](/en/reference/clients/vespa-cli/vespa_auth) - Manage Vespa Cloud credentials diff --git a/en/reference/clients/vespa-cli/vespa_auth_logout.md b/en/reference/clients/vespa-cli/vespa_auth_logout.mdx similarity index 82% rename from en/reference/clients/vespa-cli/vespa_auth_logout.md rename to en/reference/clients/vespa-cli/vespa_auth_logout.mdx index b14319ec0d..9ac187413a 100644 --- a/en/reference/clients/vespa-cli/vespa_auth_logout.md +++ b/en/reference/clients/vespa-cli/vespa_auth_logout.mdx @@ -1,31 +1,27 @@ --- -title: vespa auth logout -render_with_liquid: false +title: "vespa auth logout" +description: "Sign out of Vespa Cloud" --- -## vespa auth logout - -Sign out of Vespa Cloud - -``` +```bash vespa auth logout [flags] ``` ### Examples -``` +```bash $ vespa auth logout ``` ### Options -``` +```bash -h, --help help for logout ``` ### Options inherited from parent commands -``` +```bash -a, --application string The application to use. Format "tenant.application.instance" - instance is optional (tenant required for cloud targets) -C, --cluster string The container cluster to use. This is only required for applications with multiple clusters -c, --color string Whether to use colors in output. Must be "auto", "never", or "always" (default "auto") @@ -37,5 +33,5 @@ $ vespa auth logout ### SEE ALSO -* [vespa auth](vespa_auth.html) - Manage Vespa Cloud credentials +* [vespa auth](/en/reference/clients/vespa-cli/vespa_auth) - Manage Vespa Cloud credentials diff --git a/en/reference/clients/vespa-cli/vespa_auth_show.md b/en/reference/clients/vespa-cli/vespa_auth_show.mdx similarity index 83% rename from en/reference/clients/vespa-cli/vespa_auth_show.md rename to en/reference/clients/vespa-cli/vespa_auth_show.mdx index 1cfcf423c6..98af7a06d7 100644 --- a/en/reference/clients/vespa-cli/vespa_auth_show.md +++ b/en/reference/clients/vespa-cli/vespa_auth_show.mdx @@ -1,36 +1,32 @@ --- -title: vespa auth show -render_with_liquid: false +title: "vespa auth show" +description: "Show authenticated user" --- -## vespa auth show - -Show authenticated user - ### Synopsis Show which user (if any) is authenticated with "auth login" -``` +```bash vespa auth show [flags] ``` ### Examples -``` +```bash $ vespa auth show ``` ### Options -``` +```bash -h, --help help for show ``` ### Options inherited from parent commands -``` +```bash -a, --application string The application to use. Format "tenant.application.instance" - instance is optional (tenant required for cloud targets) -C, --cluster string The container cluster to use. This is only required for applications with multiple clusters -c, --color string Whether to use colors in output. Must be "auto", "never", or "always" (default "auto") @@ -42,5 +38,5 @@ $ vespa auth show ### SEE ALSO -* [vespa auth](vespa_auth.html) - Manage Vespa Cloud credentials +* [vespa auth](/en/reference/clients/vespa-cli/vespa_auth) - Manage Vespa Cloud credentials diff --git a/en/reference/clients/vespa-cli/vespa_clone.md b/en/reference/clients/vespa-cli/vespa_clone.mdx similarity index 75% rename from en/reference/clients/vespa-cli/vespa_clone.md rename to en/reference/clients/vespa-cli/vespa_clone.mdx index 2db00fa507..76c6370c26 100644 --- a/en/reference/clients/vespa-cli/vespa_clone.md +++ b/en/reference/clients/vespa-cli/vespa_clone.mdx @@ -1,36 +1,29 @@ --- -title: vespa clone -render_with_liquid: false +title: "vespa clone" +description: "Create files and directory structure from a Vespa sample application" --- -## vespa clone - -Create files and directory structure from a Vespa sample application - ### Synopsis Create files and directory structure from a Vespa sample application. -Sample applications are downloaded from -https://github.com/vespa-engine/sample-apps. +Sample applications are downloaded from https://github.com/vespa-engine/sample-apps. -By default, sample applications are cached in the user's cache directory. This -directory can be overridden by setting the VESPA_CLI_CACHE_DIR environment -variable. +By default, sample applications are cached in the user's cache directory. This directory can be overridden by setting the VESPA_CLI_CACHE_DIR environment variable. -``` +```bash vespa clone sample-application-path target-directory [flags] ``` ### Examples -``` +```bash $ vespa clone album-recommendation my-app ``` ### Options -``` +```bash -f, --force Ignore cache and force downloading the latest sample application from GitHub -h, --help help for clone -l, --list List available sample applications @@ -38,7 +31,7 @@ $ vespa clone album-recommendation my-app ### Options inherited from parent commands -``` +```bash -a, --application string The application to use. Format "tenant.application.instance" - instance is optional (tenant required for cloud targets) -C, --cluster string The container cluster to use. This is only required for applications with multiple clusters -c, --color string Whether to use colors in output. Must be "auto", "never", or "always" (default "auto") @@ -50,5 +43,5 @@ $ vespa clone album-recommendation my-app ### SEE ALSO -* [vespa](vespa.html) - The command-line tool for Vespa.ai +* [vespa](/en/reference/clients/vespa-cli/vespa) - The command-line tool for Vespa.ai diff --git a/en/reference/clients/vespa-cli/vespa_completion.md b/en/reference/clients/vespa-cli/vespa_completion.mdx similarity index 50% rename from en/reference/clients/vespa-cli/vespa_completion.md rename to en/reference/clients/vespa-cli/vespa_completion.mdx index 4633966dbc..2a08136484 100644 --- a/en/reference/clients/vespa-cli/vespa_completion.md +++ b/en/reference/clients/vespa-cli/vespa_completion.mdx @@ -1,27 +1,22 @@ --- -title: vespa completion -render_with_liquid: false +title: "vespa completion" +description: "Generate the autocompletion script for the specified shell" --- -## vespa completion - -Generate the autocompletion script for the specified shell - ### Synopsis -Generate the autocompletion script for vespa for the specified shell. -See each sub-command's help for details on how to use the generated script. +Generate the autocompletion script for vespa for the specified shell. See each sub-command's help for details on how to use the generated script. ### Options -``` +```bash -h, --help help for completion ``` ### Options inherited from parent commands -``` +```bash -a, --application string The application to use (cloud only) -C, --cluster string The container cluster to use. This is only required for applications with multiple clusters -c, --color string Whether to use colors in output. Must be "auto", "never", or "always" (default "auto") @@ -33,9 +28,9 @@ See each sub-command's help for details on how to use the generated script. ### SEE ALSO -* [vespa](vespa.html) - The command-line tool for Vespa.ai -* [vespa completion bash](vespa_completion_bash.html) - Generate the autocompletion script for bash -* [vespa completion fish](vespa_completion_fish.html) - Generate the autocompletion script for fish -* [vespa completion powershell](vespa_completion_powershell.html) - Generate the autocompletion script for powershell -* [vespa completion zsh](vespa_completion_zsh.html) - Generate the autocompletion script for zsh +* [vespa](/en/reference/clients/vespa-cli/vespa) - The command-line tool for Vespa.ai +* [vespa completion bash](/en/reference/clients/vespa-cli/vespa_completion_bash) - Generate the autocompletion script for bash +* [vespa completion fish](/en/reference/clients/vespa-cli/vespa_completion_fish) - Generate the autocompletion script for fish +* [vespa completion powershell](/en/reference/clients/vespa-cli/vespa_completion_powershell) - Generate the autocompletion script for powershell +* [vespa completion zsh](/en/reference/clients/vespa-cli/vespa_completion_zsh) - Generate the autocompletion script for zsh diff --git a/en/reference/clients/vespa-cli/vespa_completion_bash.md b/en/reference/clients/vespa-cli/vespa_completion_bash.mdx similarity index 74% rename from en/reference/clients/vespa-cli/vespa_completion_bash.md rename to en/reference/clients/vespa-cli/vespa_completion_bash.mdx index 9af07b1d14..7406fb2510 100644 --- a/en/reference/clients/vespa-cli/vespa_completion_bash.md +++ b/en/reference/clients/vespa-cli/vespa_completion_bash.mdx @@ -1,22 +1,19 @@ --- -title: vespa completion bash -render_with_liquid: false +title: "vespa completion bash" +description: "Generate the autocompletion script for bash" --- -## vespa completion bash - -Generate the autocompletion script for bash - ### Synopsis Generate the autocompletion script for the bash shell. -This script depends on the 'bash-completion' package. -If it is not installed already, you can install it via your OS's package manager. +This script depends on the 'bash-completion' package. If it is not installed already, you can install it via your OS's package manager. To load completions in your current shell session: - source <(vespa completion bash) +```bash +source <(vespa completion bash) +``` To load completions for every new session, execute once: @@ -31,20 +28,20 @@ To load completions for every new session, execute once: You will need to start a new shell for this setup to take effect. -``` +```bash vespa completion bash ``` ### Options -``` +```bash -h, --help help for bash --no-descriptions disable completion descriptions ``` ### Options inherited from parent commands -``` +```bash -a, --application string The application to use (cloud only) -C, --cluster string The container cluster to use. This is only required for applications with multiple clusters -c, --color string Whether to use colors in output. Must be "auto", "never", or "always" (default "auto") @@ -56,5 +53,5 @@ vespa completion bash ### SEE ALSO -* [vespa completion](vespa_completion.html) - Generate the autocompletion script for the specified shell +* [vespa completion](/en/reference/clients/vespa-cli/vespa_completion) - Generate the autocompletion script for the specified shell diff --git a/en/reference/clients/vespa-cli/vespa_completion_fish.md b/en/reference/clients/vespa-cli/vespa_completion_fish.mdx similarity index 83% rename from en/reference/clients/vespa-cli/vespa_completion_fish.md rename to en/reference/clients/vespa-cli/vespa_completion_fish.mdx index 435c324c87..57491de378 100644 --- a/en/reference/clients/vespa-cli/vespa_completion_fish.md +++ b/en/reference/clients/vespa-cli/vespa_completion_fish.mdx @@ -1,12 +1,8 @@ --- -title: vespa completion fish -render_with_liquid: false +title: "vespa completion fish" +description: "Generate the autocompletion script for fish" --- -## vespa completion fish - -Generate the autocompletion script for fish - ### Synopsis Generate the autocompletion script for the fish shell. @@ -22,20 +18,20 @@ To load completions for every new session, execute once: You will need to start a new shell for this setup to take effect. -``` +```bash vespa completion fish [flags] ``` ### Options -``` +```bash -h, --help help for fish --no-descriptions disable completion descriptions ``` ### Options inherited from parent commands -``` +```bash -a, --application string The application to use (cloud only) -C, --cluster string The container cluster to use. This is only required for applications with multiple clusters -c, --color string Whether to use colors in output. Must be "auto", "never", or "always" (default "auto") @@ -47,5 +43,5 @@ vespa completion fish [flags] ### SEE ALSO -* [vespa completion](vespa_completion.html) - Generate the autocompletion script for the specified shell +* [vespa completion](/en/reference/clients/vespa-cli/vespa_completion) - Generate the autocompletion script for the specified shell diff --git a/en/reference/clients/vespa-cli/vespa_completion_powershell.md b/en/reference/clients/vespa-cli/vespa_completion_powershell.mdx similarity index 78% rename from en/reference/clients/vespa-cli/vespa_completion_powershell.md rename to en/reference/clients/vespa-cli/vespa_completion_powershell.mdx index 86a4953557..4c77110f7e 100644 --- a/en/reference/clients/vespa-cli/vespa_completion_powershell.md +++ b/en/reference/clients/vespa-cli/vespa_completion_powershell.mdx @@ -1,12 +1,8 @@ --- -title: vespa completion powershell -render_with_liquid: false +title: "vespa completion powershell" +description: "Generate the autocompletion script for powershell" --- -## vespa completion powershell - -Generate the autocompletion script for powershell - ### Synopsis Generate the autocompletion script for powershell. @@ -15,24 +11,23 @@ To load completions in your current shell session: vespa completion powershell | Out-String | Invoke-Expression -To load completions for every new session, add the output of the above command -to your powershell profile. +To load completions for every new session, add the output of the above command to your powershell profile. -``` +```bash vespa completion powershell [flags] ``` ### Options -``` +```bash -h, --help help for powershell --no-descriptions disable completion descriptions ``` ### Options inherited from parent commands -``` +```bash -a, --application string The application to use (cloud only) -C, --cluster string The container cluster to use. This is only required for applications with multiple clusters -c, --color string Whether to use colors in output. Must be "auto", "never", or "always" (default "auto") @@ -44,5 +39,5 @@ vespa completion powershell [flags] ### SEE ALSO -* [vespa completion](vespa_completion.html) - Generate the autocompletion script for the specified shell +* [vespa completion](/en/reference/clients/vespa-cli/vespa_completion) - Generate the autocompletion script for the specified shell diff --git a/en/reference/clients/vespa-cli/vespa_completion_zsh.md b/en/reference/clients/vespa-cli/vespa_completion_zsh.mdx similarity index 79% rename from en/reference/clients/vespa-cli/vespa_completion_zsh.md rename to en/reference/clients/vespa-cli/vespa_completion_zsh.mdx index b2c02a50f6..d3a20d384d 100644 --- a/en/reference/clients/vespa-cli/vespa_completion_zsh.md +++ b/en/reference/clients/vespa-cli/vespa_completion_zsh.mdx @@ -1,24 +1,21 @@ --- -title: vespa completion zsh -render_with_liquid: false +title: "vespa completion zsh" +description: "Generate the autocompletion script for zsh" --- -## vespa completion zsh - -Generate the autocompletion script for zsh - ### Synopsis Generate the autocompletion script for the zsh shell. -If shell completion is not already enabled in your environment you will need -to enable it. You can execute the following once: +If shell completion is not already enabled in your environment you will need to enable it. You can execute the following once: echo "autoload -U compinit; compinit" >> ~/.zshrc To load completions in your current shell session: - source <(vespa completion zsh) +```bash +source <(vespa completion zsh) +``` To load completions for every new session, execute once: @@ -33,20 +30,20 @@ To load completions for every new session, execute once: You will need to start a new shell for this setup to take effect. -``` +```bash vespa completion zsh [flags] ``` ### Options -``` +```bash -h, --help help for zsh --no-descriptions disable completion descriptions ``` ### Options inherited from parent commands -``` +```bash -a, --application string The application to use (cloud only) -C, --cluster string The container cluster to use. This is only required for applications with multiple clusters -c, --color string Whether to use colors in output. Must be "auto", "never", or "always" (default "auto") @@ -58,5 +55,5 @@ vespa completion zsh [flags] ### SEE ALSO -* [vespa completion](vespa_completion.html) - Generate the autocompletion script for the specified shell +* [vespa completion](/en/reference/clients/vespa-cli/vespa_completion) - Generate the autocompletion script for the specified shell diff --git a/en/reference/clients/vespa-cli/vespa_config.md b/en/reference/clients/vespa-cli/vespa_config.md deleted file mode 100644 index 4e0e8a2733..0000000000 --- a/en/reference/clients/vespa-cli/vespa_config.md +++ /dev/null @@ -1,133 +0,0 @@ ---- -title: vespa config -render_with_liquid: false ---- - -## vespa config - -Manage persistent values for global flags - -### Synopsis - -Manage persistent values for global flags. - -This command allows setting persistent values for the global flags found in -Vespa CLI. On future invocations the flag can then be omitted as it is read -from the config file instead. - -Configuration is written to $HOME/.vespa by default. This path can be -overridden by setting the VESPA_CLI_HOME environment variable. - -When setting an option locally, the configuration is written to .vespa in the -working directory. When reading configuration, Vespa CLI searches for .vespa -in the current directory and parent directories, allowing you to run commands -from subdirectories of your application. This allows you to have separate -configuration options per application. - -Vespa CLI chooses the value for a given option in the following order, from -most to least preferred: - -1. Flag value specified on the command line -2. Local config value -3. Global config value -4. Default value - -The following global flags/options can be configured: - -application - -Specifies the application ID to manage. It has three parts, separated by -dots, with the third part being optional. If the third part is omitted it -defaults to "default". This is only relevant for the "cloud" and "hosted" -targets. See [https://docs.vespa.ai/en/learn/tenant-apps-instances.html](https://docs.vespa.ai/en/learn/tenant-apps-instances.html) for more details. -This has no default value. Examples: tenant1.app1, tenant1.app1.instance1 - -cluster - -Specifies the container cluster to manage. If left empty (default) and the -application has only one container cluster, that cluster is chosen -automatically. When an application has multiple cluster this must specify a -valid cluster name, as specified in services.xml. See -[https://docs.vespa.ai/en/reference/applications/services/container.html](https://docs.vespa.ai/en/reference/applications/services/container.html) for more details. - -color - -Controls how Vespa CLI uses colors. Setting this to "auto" (default) enables -colors if supported by the terminal, "never" completely disables colors and -"always" enables colors unilaterally. - -instance - -Specifies the instance of the application to manage. When specified, this takes -precedence over the instance specified as part of the 'application' option. -This has no default value and is only relevant for the "cloud" and "hosted" -targets. Example: instance2 - -quiet - -Suppress informational output. Errors are still printed. - -target - -Specifies the target to use for commands that interact with a Vespa platform, -e.g. vespa deploy or vespa query. Possible values are: - -- local: (default) Connect to a Vespa platform running at localhost. When using - this target, container clusters are automatically discovered and are - chosen with the cluster option. This assumes that the configserver is - available on port 19071 (the default when using the Vespa container - image). -- cloud: Connect to Vespa Cloud. When using this target, container clusters are - automatically discovered and can be selected with the cluster option. -- hosted: Connect to hosted Vespa (reserved for internal use) -- *url*: Connect to a platform running at given URL. This instructs the command - you're running to target a concrete URL. The cluster option cannot be - used with this target. - -Authentication is configured automatically for the cloud and hosted targets. To -set a custom private key and certificate, e.g. for use with a self-hosted Vespa -installation configured with mTLS, see the documentation of 'vespa auth cert'. - -zone - -Specifies a custom zone to use when connecting to a Vespa Cloud application. -This is only relevant for cloud and hosted targets and defaults to a dev zone. -See [https://docs.vespa.ai/en/operations/zones.html](https://docs.vespa.ai/en/operations/zones.html) for available zones. Examples: -dev.aws-us-east-1c, dev.gcp-us-central1-f - -default_config_scope - -Controls whether config set and config unset write to local or global -configuration by default. Valid values are "local" and "global". When unset, -defaults to "global". In Vespa 9, the default will change to "local". Use ---local or --global on individual commands to override this setting. - -``` -vespa config [flags] -``` - -### Options - -``` - -h, --help help for config -``` - -### Options inherited from parent commands - -``` - -a, --application string The application to use. Format "tenant.application.instance" - instance is optional (tenant required for cloud targets) - -C, --cluster string The container cluster to use. This is only required for applications with multiple clusters - -c, --color string Whether to use colors in output. Must be "auto", "never", or "always" (default "auto") - -i, --instance string The instance of the application to use - -q, --quiet Print only errors - -t, --target string The target platform to use. Must be "local", "cloud", "hosted" or an URL (default "local") - -z, --zone string The zone to use. This defaults to a dev zone (cloud only) -``` - -### SEE ALSO - -* [vespa](vespa.html) - The command-line tool for Vespa.ai -* [vespa config get](vespa_config_get.html) - Show given configuration option, or all configuration options -* [vespa config set](vespa_config_set.html) - Set a configuration option. -* [vespa config unset](vespa_config_unset.html) - Unset a configuration option. - diff --git a/en/reference/clients/vespa-cli/vespa_config.mdx b/en/reference/clients/vespa-cli/vespa_config.mdx new file mode 100644 index 0000000000..2816228495 --- /dev/null +++ b/en/reference/clients/vespa-cli/vespa_config.mdx @@ -0,0 +1,88 @@ +--- +title: "vespa config" +description: "Manage persistent values for global flags" +--- + +### Synopsis + +Manage persistent values for global flags. + +This command allows setting persistent values for the global flags found in Vespa CLI. On future invocations the flag can then be omitted as it is read from the config file instead. + +Configuration is written to $HOME/.vespa by default. This path can be overridden by setting the VESPA_CLI_HOME environment variable. + +When setting an option locally, the configuration is written to .vespa in the working directory. When reading configuration, Vespa CLI searches for .vespa in the current directory and parent directories, allowing you to run commands from subdirectories of your application. This allows you to have separate configuration options per application. + +Vespa CLI chooses the value for a given option in the following order, from most to least preferred: + +1. Flag value specified on the command line +2. Local config value +3. Global config value +4. Default value + +The following global flags/options can be configured: + +application + +Specifies the application ID to manage. It has three parts, separated by dots, with the third part being optional. If the third part is omitted it defaults to "default". This is only relevant for the "cloud" and "hostedtargets. See [/en/learn/tenant-apps-instances.html](/en/learn/tenant-apps-instances) for more details. This has no default value. Examples: tenant1.app1, tenant1.app1.instance1 + +cluster + +Specifies the container cluster to manage. If left empty (default) and the application has only one container cluster, that cluster is chosen automatically. When an application has multiple cluster this must specify a valid cluster name, as specified in services.xml. See [/en/reference/applications/services/container.html](/en/reference/applications/services/container) for more details. + +color + +Controls how Vespa CLI uses colors. Setting this to "auto" (default) enables colors if supported by the terminal, "never" completely disables colors and "always" enables colors unilaterally. + +instance + +Specifies the instance of the application to manage. When specified, this takes precedence over the instance specified as part of the 'application' option. This has no default value and is only relevant for the "cloud" and "hosted" targets. Example: instance2 + +quiet + +Suppress informational output. Errors are still printed. + +target + +Specifies the target to use for commands that interact with a Vespa platform, e.g. vespa deploy or vespa query. Possible values are: + +- local: (default) Connect to a Vespa platform running at localhost. When using this target, container clusters are automatically discovered and are chosen with the cluster option. This assumes that the configserver is available on port 19071 (the default when using the Vespa container image). +- cloud: Connect to Vespa Cloud. When using this target, container clusters are automatically discovered and can be selected with the cluster option. +- hosted: Connect to hosted Vespa (reserved for internal use) +- *url*: Connect to a platform running at given URL. This instructs the command you're running to target a concrete URL. The cluster option cannot be used with this target. + +Authentication is configured automatically for the cloud and hosted targets. To set a custom private key and certificate, e.g. for use with a self-hosted Vespa nstallation configured with mTLS, see the documentation of 'vespa auth cert'. + +zone + +Specifies a custom zone to use when connecting to a Vespa Cloud application. This is only relevant for cloud and hosted targets and defaults to a dev zone. See [/en/operations/zones.html](/en/operations/zones) for available zones. Examples: dev.aws-us-east-1c, dev.gcp-us-central1-f + +```bash +vespa config [flags] +``` + +### Options + +```bash + -h, --help help for config +``` + +### Options inherited from parent commands + +```bash + -a, --application string The application to use. Format "tenant.application.instance" - instance is optional (tenant required for cloud targets) + -C, --cluster string The container cluster to use. This is only required for applications with multiple clusters + -c, --color string Whether to use colors in output. Must be "auto", "never", or "always" (default "auto") + -i, --instance string The instance of the application to use + -q, --quiet Print only errors + -t, --target string The target platform to use. Must be "local", "cloud", "hosted" or an URL (default "local") + -z, --zone string The zone to use. This defaults to a dev zone (cloud only) +``` + +### SEE ALSO + +* [vespa](/en/reference/clients/vespa-cli/vespa) - The command-line tool for Vespa.ai +* [vespa config get](/en/reference/clients/vespa-cli/vespa_config_get) - Show given configuration option, or all configuration options +* [vespa config set](/en/reference/clients/vespa-cli/vespa_config_set) - Set a configuration option. +* [vespa config unset](/en/reference/clients/vespa-cli/vespa_config_unset) - Unset a configuration option. + diff --git a/en/reference/clients/vespa-cli/vespa_config_get.md b/en/reference/clients/vespa-cli/vespa_config_get.mdx similarity index 61% rename from en/reference/clients/vespa-cli/vespa_config_get.md rename to en/reference/clients/vespa-cli/vespa_config_get.mdx index 6d99e9bf2d..46525318d8 100644 --- a/en/reference/clients/vespa-cli/vespa_config_get.md +++ b/en/reference/clients/vespa-cli/vespa_config_get.mdx @@ -1,48 +1,37 @@ --- -title: vespa config get -render_with_liquid: false +title: "vespa config get" +description: "Show given configuration option, or all configuration options" --- -## vespa config get - -Show given configuration option, or all configuration options - ### Synopsis Show given configuration option, or all configuration options. -By default, this command prints the effective configuration for the current -application, i.e. it takes into account any local configuration located in -[working-directory]/.vespa. +By default, this command prints the effective configuration for the current application, i.e. it takes into account any local configuration located in [working-directory]/.vespa. -When default_config_scope is set to "local", this command shows only local -configuration by default, if any local configuration is present. - -``` +```bash vespa config get [option-name] [flags] ``` ### Examples -``` +```bash $ vespa config get $ vespa config get target $ vespa config get --local -$ vespa config get --global ``` ### Options -``` - -g, --global Show global configuration, overriding default_config_scope - -h, --help help for get - -l, --local Show only local configuration, if any +```bash + -h, --help help for get + -l, --local Show only local configuration, if any ``` ### Options inherited from parent commands -``` +```bash -a, --application string The application to use. Format "tenant.application.instance" - instance is optional (tenant required for cloud targets) -C, --cluster string The container cluster to use. This is only required for applications with multiple clusters -c, --color string Whether to use colors in output. Must be "auto", "never", or "always" (default "auto") @@ -54,5 +43,5 @@ $ vespa config get --global ### SEE ALSO -* [vespa config](vespa_config.html) - Manage persistent values for global flags +* [vespa config](/en/reference/clients/vespa-cli/vespa_config) - Manage persistent values for global flags diff --git a/en/reference/clients/vespa-cli/vespa_config_set.md b/en/reference/clients/vespa-cli/vespa_config_set.mdx similarity index 68% rename from en/reference/clients/vespa-cli/vespa_config_set.md rename to en/reference/clients/vespa-cli/vespa_config_set.mdx index 912a287f64..1031d016a8 100644 --- a/en/reference/clients/vespa-cli/vespa_config_set.md +++ b/en/reference/clients/vespa-cli/vespa_config_set.mdx @@ -1,19 +1,15 @@ --- -title: vespa config set -render_with_liquid: false +title: "vespa config set" +description: "Set a configuration option." --- -## vespa config set - -Set a configuration option. - -``` +```bash vespa config set option-name value [flags] ``` ### Examples -``` +```bash # Set the target to Vespa Cloud $ vespa config set target cloud @@ -26,27 +22,20 @@ $ vespa config set application my-tenant.my-application.my-instance # Set the instance explicitly. This will take precedence over an instance specified as part of the application option. $ vespa config set instance other-instance -# Control whether config set/unset writes to local or global config by default -$ vespa config set default_config_scope local - -# Set an option in local configuration, for the current application only. Overrides default_config_scope +# Set an option in local configuration, for the current application only $ vespa config set --local zone dev.aws-us-east-1c - -# Set an option in global configuration. Overrides default_config_scope -$ vespa config set --global target cloud ``` ### Options -``` - -g, --global Write option to global configuration, overriding default_config_scope - -h, --help help for set - -l, --local Write option to local configuration, i.e. for the current application +```bash + -h, --help help for set + -l, --local Write option to local configuration, i.e. for the current application ``` ### Options inherited from parent commands -``` +```bash -a, --application string The application to use. Format "tenant.application.instance" - instance is optional (tenant required for cloud targets) -C, --cluster string The container cluster to use. This is only required for applications with multiple clusters -c, --color string Whether to use colors in output. Must be "auto", "never", or "always" (default "auto") @@ -58,5 +47,5 @@ $ vespa config set --global target cloud ### SEE ALSO -* [vespa config](vespa_config.html) - Manage persistent values for global flags +* [vespa config](/en/reference/clients/vespa-cli/vespa_config) - Manage persistent values for global flags diff --git a/en/reference/clients/vespa-cli/vespa_config_unset.md b/en/reference/clients/vespa-cli/vespa_config_unset.mdx similarity index 69% rename from en/reference/clients/vespa-cli/vespa_config_unset.md rename to en/reference/clients/vespa-cli/vespa_config_unset.mdx index 41424c0692..beb4240c49 100644 --- a/en/reference/clients/vespa-cli/vespa_config_unset.md +++ b/en/reference/clients/vespa-cli/vespa_config_unset.mdx @@ -1,12 +1,8 @@ --- -title: vespa config unset -render_with_liquid: false +title: "vespa config unset" +description: "Unset a configuration option." --- -## vespa config unset - -Unset a configuration option. - ### Synopsis Unset a configuration option. @@ -14,34 +10,30 @@ Unset a configuration option. Unsetting a configuration option will reset it to its default value, which may be empty. -``` +```bash vespa config unset option-name [flags] ``` ### Examples -``` +```bash # Reset target to its default value $ vespa config unset target # Stop overriding application option in local config $ vespa config unset --local application - -# Unset a global option, overriding default_config_scope -$ vespa config unset --global target ``` ### Options -``` - -g, --global Unset option in global configuration, overriding default_config_scope - -h, --help help for unset - -l, --local Unset option in local configuration, i.e. for the current application +```bash + -h, --help help for unset + -l, --local Unset option in local configuration, i.e. for the current application ``` ### Options inherited from parent commands -``` +```bash -a, --application string The application to use. Format "tenant.application.instance" - instance is optional (tenant required for cloud targets) -C, --cluster string The container cluster to use. This is only required for applications with multiple clusters -c, --color string Whether to use colors in output. Must be "auto", "never", or "always" (default "auto") @@ -53,5 +45,5 @@ $ vespa config unset --global target ### SEE ALSO -* [vespa config](vespa_config.html) - Manage persistent values for global flags +* [vespa config](/en/reference/clients/vespa-cli/vespa_config) - Manage persistent values for global flags diff --git a/en/reference/clients/vespa-cli/vespa_curl.md b/en/reference/clients/vespa-cli/vespa_curl.mdx similarity index 86% rename from en/reference/clients/vespa-cli/vespa_curl.md rename to en/reference/clients/vespa-cli/vespa_curl.mdx index 354ba3077c..30ee2984bd 100644 --- a/en/reference/clients/vespa-cli/vespa_curl.md +++ b/en/reference/clients/vespa-cli/vespa_curl.mdx @@ -1,29 +1,24 @@ --- -title: vespa curl -render_with_liquid: false +title: "vespa curl" +description: "Access Vespa directly using curl" --- -## vespa curl - -Access Vespa directly using curl - ### Synopsis Access Vespa directly using curl. -Execute curl with the appropriate URL, certificate and private key for your application. -Assumes MTLS authentication. +Execute curl with the appropriate URL, certificate and private key for your application. Assumes MTLS authentication. For a more high-level interface to query and feeding, see the 'query' and 'document' commands. -``` +```bash vespa curl [curl-options] path [flags] ``` ### Examples -``` +```bash $ vespa curl /ApplicationStatus $ vespa curl -- -X POST -H "Content-Type:application/json" --data-binary @src/test/resources/A-Head-Full-of-Dreams.json /document/v1/namespace/music/docid/1 $ vespa curl -- -v --data-urlencode "yql=select * from music where album contains 'head'" /search/\?hits=5 @@ -31,7 +26,7 @@ $ vespa curl -- -v --data-urlencode "yql=select * from music where album contain ### Options -``` +```text -n, --dry-run Print the curl command that would be executed -h, --help help for curl -w, --wait int Number of seconds to wait for service(s) to become ready. 0 to disable (default 0) @@ -39,7 +34,7 @@ $ vespa curl -- -v --data-urlencode "yql=select * from music where album contain ### Options inherited from parent commands -``` +```text -a, --application string The application to use. Format "tenant.application.instance" - instance is optional (tenant required for cloud targets) -C, --cluster string The container cluster to use. This is only required for applications with multiple clusters -c, --color string Whether to use colors in output. Must be "auto", "never", or "always" (default "auto") @@ -51,5 +46,5 @@ $ vespa curl -- -v --data-urlencode "yql=select * from music where album contain ### SEE ALSO -* [vespa](vespa.html) - The command-line tool for Vespa.ai +* [vespa](/en/reference/clients/vespa-cli/vespa) - The command-line tool for Vespa.ai diff --git a/en/reference/clients/vespa-cli/vespa_deploy.md b/en/reference/clients/vespa-cli/vespa_deploy.mdx similarity index 72% rename from en/reference/clients/vespa-cli/vespa_deploy.md rename to en/reference/clients/vespa-cli/vespa_deploy.mdx index ae5a2dff44..af103fe2b0 100644 --- a/en/reference/clients/vespa-cli/vespa_deploy.md +++ b/en/reference/clients/vespa-cli/vespa_deploy.mdx @@ -1,41 +1,30 @@ --- -title: vespa deploy -render_with_liquid: false +title: "vespa deploy" +description: "Deploy (prepare and activate) an application package" --- -## vespa deploy - -Deploy (prepare and activate) an application package - ### Synopsis Deploy (prepare and activate) an application package. -An application package defines a deployable Vespa application. See -[https://docs.vespa.ai/en/reference/applications/application-packages.html](https://docs.vespa.ai/en/reference/applications/application-packages.html) for -details about the files contained in this package. +An application package defines a deployable Vespa application. See [/en/reference/applications/application-packages.html](/en/reference/applications/application-packages) for details about the files contained in this package. To get started, 'vespa clone' can be used to download a sample application. -This command deploys an application package. When deploy returns successfully -the application package has been validated and activated on config servers. The -process of applying it on individual nodes has started but may not have -completed. +This command deploys an application package. When deploy returns successfully the application package has been validated and activated on config servers. The process of applying it on individual nodes has started but may not have completed. If application directory is not specified, it defaults to working directory. -In Vespa Cloud you may override the Vespa runtime version (--version) for your -deployment. This option should only be used if you have a reason for using a -specific version. By default, Vespa Cloud chooses a suitable version for you. +In Vespa Cloud you may override the Vespa runtime version (--version) for your deployment. This option should only be used if you have a reason for using a specific version. By default, Vespa Cloud chooses a suitable version for you. -``` +```bash vespa deploy [application-directory-or-file] [flags] ``` ### Examples -``` +```bash $ vespa deploy . $ vespa deploy -t cloud $ vespa deploy -t cloud -z dev.aws-us-east-1c # -z can be omitted here as this zone is the default @@ -44,7 +33,7 @@ $ vespa deploy -t cloud -z dev.gcp-us-central1-f ### Options -``` +```bash -A, --add-cert Copy certificate of the configured application to the current application package -h, --help help for deploy -l, --log-level string Log level for Vespa logs. Must be "error", "warning", "info" or "debug" (default "error") @@ -54,7 +43,7 @@ $ vespa deploy -t cloud -z dev.gcp-us-central1-f ### Options inherited from parent commands -``` +```bash -a, --application string The application to use. Format "tenant.application.instance" - instance is optional (tenant required for cloud targets) -C, --cluster string The container cluster to use. This is only required for applications with multiple clusters -c, --color string Whether to use colors in output. Must be "auto", "never", or "always" (default "auto") @@ -66,5 +55,5 @@ $ vespa deploy -t cloud -z dev.gcp-us-central1-f ### SEE ALSO -* [vespa](vespa.html) - The command-line tool for Vespa.ai +* [vespa](/en/reference/clients/vespa-cli/vespa) - The command-line tool for Vespa.ai diff --git a/en/reference/clients/vespa-cli/vespa_destroy.md b/en/reference/clients/vespa-cli/vespa_destroy.mdx similarity index 62% rename from en/reference/clients/vespa-cli/vespa_destroy.md rename to en/reference/clients/vespa-cli/vespa_destroy.mdx index 5f7bb22ba3..bb305e4196 100644 --- a/en/reference/clients/vespa-cli/vespa_destroy.md +++ b/en/reference/clients/vespa-cli/vespa_destroy.mdx @@ -1,38 +1,27 @@ --- -title: vespa destroy -render_with_liquid: false +title: "vespa destroy" +description: "Remove a deployed Vespa application and its data" --- -## vespa destroy - -Remove a deployed Vespa application and its data - ### Synopsis Remove a deployed Vespa application and its data. -This command removes the currently deployed application and permanently -deletes its data. +This command removes the currently deployed application and permanently deletes its data. -When run interactively, the command will prompt for confirmation before -removing the application. When run non-interactively, the command will refuse -to remove the application unless the --force option is given. +When run interactively, the command will prompt for confirmation before removing the application. When run non-interactively, the command will refuse to remove the application unless the --force option is given. -This command can only be used to remove non-production deployments, in Vespa -Cloud. See [https://docs.vespa.ai/en/operations/deleting-applications.html](https://docs.vespa.ai/en/operations/deleting-applications.html) for how to remove -production deployments. +This command can only be used to remove non-production deployments, in Vespa Cloud. See [/en/operations/deleting-applications.html](/en/operations/deleting-applications) for how to remove production deployments. -For other systems, destroy the application by removing the -containers in use by the application. For example: -https://github.com/vespa-engine/sample-apps/tree/master/examples/operations/multinode-HA#clean-up-after-testing +For other systems, destroy the application by removing the containers in use by the application. For example: https://github.com/vespa-engine/sample-apps/tree/master/examples/operations/multinode-HA#clean-up-after-testing -``` +```bash vespa destroy [flags] ``` ### Examples -``` +```bash $ vespa destroy $ vespa destroy -a mytenant.myapp.myinstance $ vespa destroy --force @@ -40,14 +29,14 @@ $ vespa destroy --force ### Options -``` +```bash --force Disable confirmation (default false) -h, --help help for destroy ``` ### Options inherited from parent commands -``` +```bash -a, --application string The application to use. Format "tenant.application.instance" - instance is optional (tenant required for cloud targets) -C, --cluster string The container cluster to use. This is only required for applications with multiple clusters -c, --color string Whether to use colors in output. Must be "auto", "never", or "always" (default "auto") @@ -59,5 +48,5 @@ $ vespa destroy --force ### SEE ALSO -* [vespa](vespa.html) - The command-line tool for Vespa.ai +* [vespa](/en/reference/clients/vespa-cli/vespa) - The command-line tool for Vespa.ai diff --git a/en/reference/clients/vespa-cli/vespa_document.md b/en/reference/clients/vespa-cli/vespa_document.mdx similarity index 60% rename from en/reference/clients/vespa-cli/vespa_document.md rename to en/reference/clients/vespa-cli/vespa_document.mdx index 4b125be42c..27ce312477 100644 --- a/en/reference/clients/vespa-cli/vespa_document.md +++ b/en/reference/clients/vespa-cli/vespa_document.mdx @@ -1,38 +1,31 @@ --- -title: vespa document -render_with_liquid: false +title: "vespa document" +description: "Issue a single document operation to Vespa" --- -## vespa document - -Issue a single document operation to Vespa - ### Synopsis Issue a single document operation to Vespa. -The operation must be on the format documented in -[https://docs.vespa.ai/en/reference/schemas/document-json-format.html#document-operations](https://docs.vespa.ai/en/reference/schemas/document-json-format.html#document-operations) +The operation must be on the format documented in [/en/reference/schemas/document-json-format#document-operations](/en/reference/schemas/document-json-format#document-operations) -When this returns successfully, the document is guaranteed to be visible in any -subsequent get or query operation. +When this returns successfully, the document is guaranteed to be visible in any subsequent get or query operation. -To feed with high throughput, [https://docs.vespa.ai/en/reference/clients/vespa-cli/vespa_feed.html](https://docs.vespa.ai/en/reference/clients/vespa-cli/vespa_feed.html) -should be used instead of this. +To feed with high throughput, [/en/reference/clients/vespa-cli/vespa_feed.html](/en/reference/clients/vespa-cli/vespa_feed) should be used instead of this. -``` +```bash vespa document json-file [flags] ``` ### Examples -``` +```bash $ vespa document src/test/resources/A-Head-Full-of-Dreams.json ``` ### Options -``` +```bash -d, --data string Document data to use instead of reading from file or stdin --header strings Add a header to the HTTP request, on the format 'Header: Value'. This can be specified multiple times -h, --help help for document @@ -43,7 +36,7 @@ $ vespa document src/test/resources/A-Head-Full-of-Dreams.json ### Options inherited from parent commands -``` +```bash -a, --application string The application to use. Format "tenant.application.instance" - instance is optional (tenant required for cloud targets) -C, --cluster string The container cluster to use. This is only required for applications with multiple clusters -c, --color string Whether to use colors in output. Must be "auto", "never", or "always" (default "auto") @@ -55,9 +48,9 @@ $ vespa document src/test/resources/A-Head-Full-of-Dreams.json ### SEE ALSO -* [vespa](vespa.html) - The command-line tool for Vespa.ai -* [vespa document get](vespa_document_get.html) - Gets one or more documents -* [vespa document put](vespa_document_put.html) - Writes a document to Vespa -* [vespa document remove](vespa_document_remove.html) - Removes a document from Vespa -* [vespa document update](vespa_document_update.html) - Modifies some fields of an existing document +* [vespa](/en/reference/clients/vespa-cli/vespa) - The command-line tool for Vespa.ai +* [vespa document get](/en/reference/clients/vespa-cli/vespa_document_get) - Gets one or more documents +* [vespa document put](/en/reference/clients/vespa-cli/vespa_document_put) - Writes a document to Vespa +* [vespa document remove](/en/reference/clients/vespa-cli/vespa_document_remove) - Removes a document from Vespa +* [vespa document update](/en/reference/clients/vespa-cli/vespa_document_update) - Modifies some fields of an existing document diff --git a/en/reference/clients/vespa-cli/vespa_document_get.md b/en/reference/clients/vespa-cli/vespa_document_get.mdx similarity index 88% rename from en/reference/clients/vespa-cli/vespa_document_get.md rename to en/reference/clients/vespa-cli/vespa_document_get.mdx index e8e2a01b75..bd3350872b 100644 --- a/en/reference/clients/vespa-cli/vespa_document_get.md +++ b/en/reference/clients/vespa-cli/vespa_document_get.mdx @@ -1,26 +1,22 @@ --- -title: vespa document get -render_with_liquid: false +title: "vespa document get" +description: "Gets one or more documents" --- -## vespa document get - -Gets one or more documents - -``` +```bash vespa document get id(s) [flags] ``` ### Examples -``` +```bash $ vespa document get id:mynamespace:music::song-1 $ vespa document get id:mynamespace:music::song-1 id:mynamespace:music::song-2 ``` ### Options -``` +```bash -d, --data string Document data to use instead of reading from file or stdin --field-set string Fields to include when reading document --header strings Add a header to the HTTP request, on the format 'Header: Value'. This can be specified multiple times @@ -33,7 +29,7 @@ $ vespa document get id:mynamespace:music::song-1 id:mynamespace:music::song-2 ### Options inherited from parent commands -``` +```bash -a, --application string The application to use. Format "tenant.application.instance" - instance is optional (tenant required for cloud targets) -C, --cluster string The container cluster to use. This is only required for applications with multiple clusters -c, --color string Whether to use colors in output. Must be "auto", "never", or "always" (default "auto") @@ -45,5 +41,5 @@ $ vespa document get id:mynamespace:music::song-1 id:mynamespace:music::song-2 ### SEE ALSO -* [vespa document](vespa_document.html) - Issue a single document operation to Vespa +* [vespa document](/en/reference/clients/vespa-cli/vespa_document) - Issue a single document operation to Vespa diff --git a/en/reference/clients/vespa-cli/vespa_document_put.md b/en/reference/clients/vespa-cli/vespa_document_put.mdx similarity index 76% rename from en/reference/clients/vespa-cli/vespa_document_put.md rename to en/reference/clients/vespa-cli/vespa_document_put.mdx index fe28f1a654..6dc0546e6a 100644 --- a/en/reference/clients/vespa-cli/vespa_document_put.md +++ b/en/reference/clients/vespa-cli/vespa_document_put.mdx @@ -1,29 +1,22 @@ --- -title: vespa document put -render_with_liquid: false +title: "vespa document put" +description: "Writes a document to Vespa" --- -## vespa document put - -Writes a document to Vespa - ### Synopsis -Writes the document in the given file to Vespa. -If the document already exists, all its values will be replaced by this document. -If the document id is specified both as an argument and in the file the argument takes precedence. +Writes the document in the given file to Vespa. If the document already exists, all its values will be replaced by this document. If the document ID is specified both as an argument and in the file the argument takes precedence. -If json-file is a single dash ('-'), the document will be read from standard input. -Alternatively, you can use the --data parameter to provide the document data directly. +If json-file is a single dash ('-'), the document will be read from standard input. Alternatively, you can use the --data parameter to provide the document data directly. -``` +```bash vespa document put [id] json-file [flags] ``` ### Examples -``` +```bash $ vespa document put src/test/resources/A-Head-Full-of-Dreams.json $ vespa document put id:mynamespace:music::a-head-full-of-dreams src/test/resources/A-Head-Full-of-Dreams.json $ vespa document put id:mynamespace:music::a-head-full-of-dreams --data '{"fields":{"title":"My Title","artist":"My Artist"}}' @@ -31,7 +24,7 @@ $ vespa document put id:mynamespace:music::a-head-full-of-dreams --data '{"field ### Options -``` +```bash -d, --data string Document data to use instead of reading from file or stdin --header strings Add a header to the HTTP request, on the format 'Header: Value'. This can be specified multiple times -h, --help help for put @@ -42,7 +35,7 @@ $ vespa document put id:mynamespace:music::a-head-full-of-dreams --data '{"field ### Options inherited from parent commands -``` +```bash -a, --application string The application to use. Format "tenant.application.instance" - instance is optional (tenant required for cloud targets) -C, --cluster string The container cluster to use. This is only required for applications with multiple clusters -c, --color string Whether to use colors in output. Must be "auto", "never", or "always" (default "auto") @@ -54,5 +47,5 @@ $ vespa document put id:mynamespace:music::a-head-full-of-dreams --data '{"field ### SEE ALSO -* [vespa document](vespa_document.html) - Issue a single document operation to Vespa +* [vespa document](/en/reference/clients/vespa-cli/vespa_document) - Issue a single document operation to Vespa diff --git a/en/reference/clients/vespa-cli/vespa_document_remove.md b/en/reference/clients/vespa-cli/vespa_document_remove.mdx similarity index 79% rename from en/reference/clients/vespa-cli/vespa_document_remove.md rename to en/reference/clients/vespa-cli/vespa_document_remove.mdx index 4ce44a308b..6190c9b141 100644 --- a/en/reference/clients/vespa-cli/vespa_document_remove.md +++ b/en/reference/clients/vespa-cli/vespa_document_remove.mdx @@ -1,31 +1,26 @@ --- -title: vespa document remove -render_with_liquid: false +title: "vespa document remove" +description: "Removes a document from Vespa" --- -## vespa document remove - -Removes a document from Vespa - ### Synopsis -Removes the document specified either as a document id or given in the json file. -If the document id is specified both as an argument and in the file the argument takes precedence. +Removes the document specified either as a document ID or given in the json file. If the document ID is specified both as an argument and in the file the argument takes precedence. -``` +```bash vespa document remove id | json-file [flags] ``` ### Examples -``` +```bash $ vespa document remove src/test/resources/A-Head-Full-of-Dreams-Remove.json $ vespa document remove id:mynamespace:music::a-head-full-of-dreams ``` ### Options -``` +```bash -d, --data string Document data to use instead of reading from file or stdin --header strings Add a header to the HTTP request, on the format 'Header: Value'. This can be specified multiple times -h, --help help for remove @@ -36,7 +31,7 @@ $ vespa document remove id:mynamespace:music::a-head-full-of-dreams ### Options inherited from parent commands -``` +```bash -a, --application string The application to use. Format "tenant.application.instance" - instance is optional (tenant required for cloud targets) -C, --cluster string The container cluster to use. This is only required for applications with multiple clusters -c, --color string Whether to use colors in output. Must be "auto", "never", or "always" (default "auto") @@ -48,5 +43,5 @@ $ vespa document remove id:mynamespace:music::a-head-full-of-dreams ### SEE ALSO -* [vespa document](vespa_document.html) - Issue a single document operation to Vespa +* [vespa document](/en/reference/clients/vespa-cli/vespa_document) - Issue a single document operation to Vespa diff --git a/en/reference/clients/vespa-cli/vespa_document_update.md b/en/reference/clients/vespa-cli/vespa_document_update.mdx similarity index 82% rename from en/reference/clients/vespa-cli/vespa_document_update.md rename to en/reference/clients/vespa-cli/vespa_document_update.mdx index 9634a033d0..2de8237c3e 100644 --- a/en/reference/clients/vespa-cli/vespa_document_update.md +++ b/en/reference/clients/vespa-cli/vespa_document_update.mdx @@ -1,31 +1,27 @@ --- -title: vespa document update -render_with_liquid: false +title: "vespa document update" +description: "Modifies some fields of an existing document" --- -## vespa document update - -Modifies some fields of an existing document ### Synopsis -Updates the values of the fields given in a json file as specified in the file. -If the document id is specified both as an argument and in the file the argument takes precedence. +Updates the values of the fields given in a json file as specified in the file. If the document ID is specified both as an argument and in the file the argument takes precedence. -``` +```bash vespa document update [id] json-file [flags] ``` ### Examples -``` +```bash $ vespa document update src/test/resources/A-Head-Full-of-Dreams-Update.json $ vespa document update id:mynamespace:music::a-head-full-of-dreams src/test/resources/A-Head-Full-of-Dreams.json ``` ### Options -``` +```bash -d, --data string Document data to use instead of reading from file or stdin --header strings Add a header to the HTTP request, on the format 'Header: Value'. This can be specified multiple times -h, --help help for update @@ -36,7 +32,7 @@ $ vespa document update id:mynamespace:music::a-head-full-of-dreams src/test/res ### Options inherited from parent commands -``` +```bash -a, --application string The application to use. Format "tenant.application.instance" - instance is optional (tenant required for cloud targets) -C, --cluster string The container cluster to use. This is only required for applications with multiple clusters -c, --color string Whether to use colors in output. Must be "auto", "never", or "always" (default "auto") @@ -48,5 +44,5 @@ $ vespa document update id:mynamespace:music::a-head-full-of-dreams src/test/res ### SEE ALSO -* [vespa document](vespa_document.html) - Issue a single document operation to Vespa +* [vespa document](/en/reference/clients/vespa-cli/vespa_document) - Issue a single document operation to Vespa diff --git a/en/reference/clients/vespa-cli/vespa_feed.md b/en/reference/clients/vespa-cli/vespa_feed.mdx similarity index 89% rename from en/reference/clients/vespa-cli/vespa_feed.md rename to en/reference/clients/vespa-cli/vespa_feed.mdx index 8a9249ce91..53b0772840 100644 --- a/en/reference/clients/vespa-cli/vespa_feed.md +++ b/en/reference/clients/vespa-cli/vespa_feed.mdx @@ -1,29 +1,21 @@ --- -title: vespa feed -render_with_liquid: false +title: "vespa feed" +description: "Feed multiple document operations to Vespa" --- -## vespa feed - -Feed multiple document operations to Vespa - ### Synopsis Feed multiple document operations to Vespa. -This command can be used to feed large amounts of documents to a Vespa cluster -efficiently. +This command can be used to feed large amounts of documents to a Vespa cluster efficiently. -The contents of json-file must be either a JSON array or JSON objects separated by -newline (JSONL). +The contents of json-file must be either a JSON array or JSON objects separated by newline (JSONL). If json-file is a single dash ('-'), documents will be read from standard input. -Once feeding completes, metrics of the feed session are printed to standard out -in a JSON format: +Once feeding completes, metrics of the feed session are printed to standard out in a JSON format: -- feeder.operation.count: Number of operations passed to the feeder by the user, - not counting retries. +- feeder.operation.count: Number of operations passed to the feeder by the user, not counting retries. - feeder.seconds: Total time spent feeding. - feeder.ok.count: Number of successful operations. - feeder.ok.rate: Number of successful operations per second. @@ -32,10 +24,8 @@ in a JSON format: - http.request.count: Number of HTTP requests made, including retries. - http.request.bytes: Number of bytes sent. - http.request.MBps: Request throughput measured in MB/s. This is the raw - operation throughput, and not the network throughput, - I.e. using compression does not affect this number. -- http.exception.count: Same as feeder.error.count. Present for compatibility - with vespa-feed-client. + operation throughput, and not the network throughput, I.e. using compression does not affect this number. +- http.exception.count: Same as feeder.error.count. Present for compatibility with vespa-feed-client. - http.response.count: Number of HTTP responses received. - http.response.bytes: Number of bytes received. - http.response.MBps: Response throughput measured in MB/s. @@ -46,20 +36,20 @@ in a JSON format: - http.response.code.counts: Number of responses grouped by their HTTP code. -``` +```bash vespa feed json-file [json-file]... [flags] ``` ### Examples -``` +```bash $ vespa feed docs.jsonl moredocs.json $ cat docs.jsonl | vespa feed - ``` ### Options -``` +```bash --compression string Whether to compress the document data when sending the HTTP request. Default is "auto", which compresses large documents. Must be "auto", "gzip" or "none" (default "auto") --connections int The number of connections to use (default 8) --deadline int Exit if this number of seconds elapse without any successful operations. 0 to disable (default 0) @@ -78,7 +68,7 @@ $ cat docs.jsonl | vespa feed - ### Options inherited from parent commands -``` +```bash -a, --application string The application to use. Format "tenant.application.instance" - instance is optional (tenant required for cloud targets) -C, --cluster string The container cluster to use. This is only required for applications with multiple clusters -c, --color string Whether to use colors in output. Must be "auto", "never", or "always" (default "auto") @@ -90,5 +80,5 @@ $ cat docs.jsonl | vespa feed - ### SEE ALSO -* [vespa](vespa.html) - The command-line tool for Vespa.ai +* [vespa](/en/reference/clients/vespa-cli/vespa) - The command-line tool for Vespa.ai diff --git a/en/reference/clients/vespa-cli/vespa_fetch.md b/en/reference/clients/vespa-cli/vespa_fetch.mdx similarity index 77% rename from en/reference/clients/vespa-cli/vespa_fetch.md rename to en/reference/clients/vespa-cli/vespa_fetch.mdx index 083b85f4dd..9ff1387c9b 100644 --- a/en/reference/clients/vespa-cli/vespa_fetch.md +++ b/en/reference/clients/vespa-cli/vespa_fetch.mdx @@ -1,27 +1,21 @@ --- -title: vespa fetch -render_with_liquid: false +title: "vespa fetch" +description: "Download a deployed application package" --- -## vespa fetch - -Download a deployed application package - ### Synopsis Download a deployed application package. -This command can be used to download an already deployed Vespa application -package. The package is written as a ZIP file to the given path, or current -directory if no path is given. +This command can be used to download an already deployed Vespa application package. The package is written as a ZIP file to the given path, or current directory if no path is given. -``` +```bash vespa fetch [path] [flags] ``` ### Examples -``` +```bash $ vespa fetch $ vespa fetch mydir/ $ vespa fetch -t cloud mycloudapp.zip @@ -30,13 +24,13 @@ $ vespa fetch -t cloud mycloudapp.zip ### Options -``` +```bash -h, --help help for fetch ``` ### Options inherited from parent commands -``` +```bash -a, --application string The application to use. Format "tenant.application.instance" - instance is optional (tenant required for cloud targets) -C, --cluster string The container cluster to use. This is only required for applications with multiple clusters -c, --color string Whether to use colors in output. Must be "auto", "never", or "always" (default "auto") @@ -48,5 +42,5 @@ $ vespa fetch -t cloud mycloudapp.zip ### SEE ALSO -* [vespa](vespa.html) - The command-line tool for Vespa.ai +* [vespa](/en/reference/clients/vespa-cli/vespa) - The command-line tool for Vespa.ai diff --git a/en/reference/clients/vespa-cli/vespa_inspect.md b/en/reference/clients/vespa-cli/vespa_inspect.mdx similarity index 77% rename from en/reference/clients/vespa-cli/vespa_inspect.md rename to en/reference/clients/vespa-cli/vespa_inspect.mdx index 116f5d07a4..f6889b6933 100644 --- a/en/reference/clients/vespa-cli/vespa_inspect.md +++ b/en/reference/clients/vespa-cli/vespa_inspect.mdx @@ -1,25 +1,21 @@ --- -title: vespa inspect -render_with_liquid: false +title: "vespa inspect" +description: "Provides insight" --- -## vespa inspect - -Provides insight - ### Synopsis Provides subcommands to inspect various things in more detail ### Options -``` +```text -h, --help help for inspect ``` ### Options inherited from parent commands -``` +```text -a, --application string The application to use (cloud only). Format "tenant.application.instance" - instance is optional -C, --cluster string The container cluster to use. This is only required for applications with multiple clusters -c, --color string Whether to use colors in output. Must be "auto", "never", or "always" (default "auto") @@ -31,6 +27,6 @@ Provides subcommands to inspect various things in more detail ### SEE ALSO -* [vespa](vespa.html) - The command-line tool for Vespa.ai -* [vespa inspect profile](vespa_inspect_profile.html) - Inspect profiling results +* [vespa](/en/reference/clients/vespa-cli/vespa) - The command-line tool for Vespa.ai +* [vespa inspect profile](/en/reference/clients/vespa-cli/vespa_inspect_profile) - Inspect profiling results diff --git a/en/reference/clients/vespa-cli/vespa_inspect_profile.md b/en/reference/clients/vespa-cli/vespa_inspect_profile.mdx similarity index 81% rename from en/reference/clients/vespa-cli/vespa_inspect_profile.md rename to en/reference/clients/vespa-cli/vespa_inspect_profile.mdx index 5556069fa7..ce817130a0 100644 --- a/en/reference/clients/vespa-cli/vespa_inspect_profile.md +++ b/en/reference/clients/vespa-cli/vespa_inspect_profile.mdx @@ -1,33 +1,33 @@ --- -title: vespa inspect profile -render_with_liquid: false +title: "vespa inspect profile" +description: "Inspect profiling results" --- -## vespa inspect profile - -Inspect profiling results - ### Synopsis Inspect profiling results previously obtained by vespa query --profile -Note: this feature is experimental and currently under development +<Note> +**Note:** + +this feature is experimental and currently under development +</Note> profiling results can also be analyzed with vespa-query-analyzer (part of vespa installation) -``` +```bash vespa inspect profile [flags] ``` ### Options -``` +```text -h, --help help for profile -f, --profile-file string Name of the profile file to inspect (default "vespa_query_profile_result.json") ``` ### Options inherited from parent commands -``` +```text -a, --application string The application to use (cloud only). Format "tenant.application.instance" - instance is optional -C, --cluster string The container cluster to use. This is only required for applications with multiple clusters -c, --color string Whether to use colors in output. Must be "auto", "never", or "always" (default "auto") @@ -39,5 +39,5 @@ vespa inspect profile [flags] ### SEE ALSO -* [vespa inspect](vespa_inspect.html) - Provides insight +* [vespa inspect](/en/reference/clients/vespa-cli/vespa_inspect) - Provides insight diff --git a/en/reference/clients/vespa-cli/vespa_log.md b/en/reference/clients/vespa-cli/vespa_log.mdx similarity index 90% rename from en/reference/clients/vespa-cli/vespa_log.md rename to en/reference/clients/vespa-cli/vespa_log.mdx index 3539fb2cbe..2129b15efe 100644 --- a/en/reference/clients/vespa-cli/vespa_log.md +++ b/en/reference/clients/vespa-cli/vespa_log.mdx @@ -1,12 +1,8 @@ --- -title: vespa log -render_with_liquid: false +title: "vespa log" +description: "Show the Vespa log" --- -## vespa log - -Show the Vespa log - ### Synopsis Show the Vespa log. @@ -16,13 +12,13 @@ The logs shown can be limited to a relative or fixed period. All timestamps are Logs for the past hour are shown if no arguments are given. -``` +```bash vespa log [relative-period] [flags] ``` ### Examples -``` +```bash $ vespa log 1h $ vespa log --nldequote=false 10m $ vespa log --from 2021-08-25T15:00:00Z --to 2021-08-26T02:00:00Z @@ -31,7 +27,7 @@ $ vespa log --follow ### Options -``` +```text -f, --follow Follow logs -F, --from string Include logs since this timestamp (RFC3339 format) -h, --help help for log @@ -42,7 +38,7 @@ $ vespa log --follow ### Options inherited from parent commands -``` +```text -a, --application string The application to use. Format "tenant.application.instance" - instance is optional (tenant required for cloud targets) -C, --cluster string The container cluster to use. This is only required for applications with multiple clusters -c, --color string Whether to use colors in output. Must be "auto", "never", or "always" (default "auto") @@ -54,5 +50,5 @@ $ vespa log --follow ### SEE ALSO -* [vespa](vespa.html) - The command-line tool for Vespa.ai +* [vespa](/en/reference/clients/vespa-cli/vespa) - The command-line tool for Vespa.ai diff --git a/en/reference/clients/vespa-cli/vespa_prepare.md b/en/reference/clients/vespa-cli/vespa_prepare.mdx similarity index 83% rename from en/reference/clients/vespa-cli/vespa_prepare.md rename to en/reference/clients/vespa-cli/vespa_prepare.mdx index 2b7ea11e91..88ca9da25f 100644 --- a/en/reference/clients/vespa-cli/vespa_prepare.md +++ b/en/reference/clients/vespa-cli/vespa_prepare.mdx @@ -1,26 +1,22 @@ --- -title: vespa prepare -render_with_liquid: false +title: "vespa prepare" +description: "Prepare an application package for activation" --- -## vespa prepare - -Prepare an application package for activation - -``` +```bash vespa prepare [application-directory-or-file] [flags] ``` ### Options -``` +```bash -h, --help help for prepare -w, --wait int Number of seconds to wait for service(s) to become ready. 0 to disable (default 0) ``` ### Options inherited from parent commands -``` +```bash -a, --application string The application to use. Format "tenant.application.instance" - instance is optional (tenant required for cloud targets) -C, --cluster string The container cluster to use. This is only required for applications with multiple clusters -c, --color string Whether to use colors in output. Must be "auto", "never", or "always" (default "auto") @@ -32,5 +28,5 @@ vespa prepare [application-directory-or-file] [flags] ### SEE ALSO -* [vespa](vespa.html) - The command-line tool for Vespa.ai +* [vespa](/en/reference/clients/vespa-cli/vespa) - The command-line tool for Vespa.ai diff --git a/en/reference/clients/vespa-cli/vespa_prod.md b/en/reference/clients/vespa-cli/vespa_prod.mdx similarity index 69% rename from en/reference/clients/vespa-cli/vespa_prod.md rename to en/reference/clients/vespa-cli/vespa_prod.mdx index 68a5dedfba..e0c8b3a0cd 100644 --- a/en/reference/clients/vespa-cli/vespa_prod.md +++ b/en/reference/clients/vespa-cli/vespa_prod.mdx @@ -1,38 +1,34 @@ --- -title: vespa prod -render_with_liquid: false +title: "vespa prod" +description: "Deploy an application package to production in Vespa Cloud" --- -## vespa prod - -Deploy an application package to production in Vespa Cloud - ### Synopsis Deploy an application package to production in Vespa Cloud. Configure and deploy your application package to production in Vespa Cloud. -``` +```bash vespa prod [flags] ``` ### Examples -``` +```bash $ vespa prod init $ vespa prod deploy ``` ### Options -``` +```text -h, --help help for prod ``` ### Options inherited from parent commands -``` +```text -a, --application string The application to use. Format "tenant.application.instance" - instance is optional (tenant required for cloud targets) -C, --cluster string The container cluster to use. This is only required for applications with multiple clusters -c, --color string Whether to use colors in output. Must be "auto", "never", or "always" (default "auto") @@ -44,7 +40,7 @@ $ vespa prod deploy ### SEE ALSO -* [vespa](vespa.html) - The command-line tool for Vespa.ai -* [vespa prod deploy](vespa_prod_deploy.html) - Deploy an application to production -* [vespa prod init](vespa_prod_init.html) - Modify service.xml and deployment.xml for production deployment +* [vespa](/en/reference/clients/vespa-cli/vespa) - The command-line tool for Vespa.ai +* [vespa prod deploy](/en/reference/clients/vespa-cli/vespa_prod_deploy) - Deploy an application to production +* [vespa prod init](/en/reference/clients/vespa-cli/vespa_prod_init) - Modify service.xml and deployment.xml for production deployment diff --git a/en/reference/clients/vespa-cli/vespa_prod_deploy.md b/en/reference/clients/vespa-cli/vespa_prod_deploy.mdx similarity index 78% rename from en/reference/clients/vespa-cli/vespa_prod_deploy.md rename to en/reference/clients/vespa-cli/vespa_prod_deploy.mdx index 44f11003c4..bc391e1b1b 100644 --- a/en/reference/clients/vespa-cli/vespa_prod_deploy.md +++ b/en/reference/clients/vespa-cli/vespa_prod_deploy.mdx @@ -1,41 +1,35 @@ --- -title: vespa prod deploy -render_with_liquid: false +title: "vespa prod deploy" +description: "Deploy an application to production" --- -## vespa prod deploy - -Deploy an application to production - ### Synopsis Deploy an application to production. -This commands uploads an application package to Vespa Cloud and deploys it to -the production zones specified in deployment.xml. +This commands uploads an application package to Vespa Cloud and deploys it to the production zones specified in deployment.xml. -Nodes are allocated to the application according to resources specified in -services.xml. +Nodes are allocated to the application according to resources specified in services.xml. For more information about production deployments in Vespa Cloud see: -[https://docs.vespa.ai/en/operations/production-deployment.html](https://docs.vespa.ai/en/operations/production-deployment.html) -[https://docs.vespa.ai/en/operations/automated-deployments.html](https://docs.vespa.ai/en/operations/automated-deployments.html) +en/operations/production-deployment](/en/operations/production-deployment) +[/en/operations/automated-deployments.html](/en/operations/automated-deployments) -``` +```bash vespa prod deploy [application-directory-or-file] [flags] ``` ### Examples -``` +```bash $ mvn package # when adding custom Java components $ vespa prod deploy ``` ### Options -``` +```bash -A, --add-cert Copy certificate of the configured application to the current application package (default false) --author-email string Email of the author of the commit being deployed --commit string Identifier of the source code being deployed. For example a commit hash @@ -48,7 +42,7 @@ $ vespa prod deploy ### Options inherited from parent commands -``` +```bash -a, --application string The application to use. Format "tenant.application.instance" - instance is optional (tenant required for cloud targets) -C, --cluster string The container cluster to use. This is only required for applications with multiple clusters -c, --color string Whether to use colors in output. Must be "auto", "never", or "always" (default "auto") @@ -60,5 +54,5 @@ $ vespa prod deploy ### SEE ALSO -* [vespa prod](vespa_prod.html) - Deploy an application package to production in Vespa Cloud +* [vespa prod](/en/reference/clients/vespa-cli/vespa_prod) - Deploy an application package to production in Vespa Cloud diff --git a/en/reference/clients/vespa-cli/vespa_prod_init.md b/en/reference/clients/vespa-cli/vespa_prod_init.mdx similarity index 60% rename from en/reference/clients/vespa-cli/vespa_prod_init.md rename to en/reference/clients/vespa-cli/vespa_prod_init.mdx index cf822f3cc2..3fc319e902 100644 --- a/en/reference/clients/vespa-cli/vespa_prod_init.md +++ b/en/reference/clients/vespa-cli/vespa_prod_init.mdx @@ -1,37 +1,31 @@ --- -title: vespa prod init -render_with_liquid: false +title: "vespa prod init" +description: "Modify service.xml and deployment.xml for production deployment" --- -## vespa prod init - -Modify service.xml and deployment.xml for production deployment - ### Synopsis Modify service.xml and deployment.xml for production deployment. -Only basic deployment configuration is available through this command. For -advanced configuration see the relevant Vespa Cloud documentation and make -changes to deployment.xml and services.xml directly. +Only basic deployment configuration is available through this command. For advanced configuration see the relevant Vespa Cloud documentation and make changes to deployment.xml and services.xml directly. Reference: -[https://docs.vespa.ai/en/reference/applications/services/services.html](https://docs.vespa.ai/en/reference/applications/services/services.html) -[https://docs.vespa.ai/en/reference/applications/deployment.html](https://docs.vespa.ai/en/reference/applications/deployment.html) +[/en/reference/applications/services/services](/en/reference/applications/services/services) +[/en/reference/applications/deployment](/en/reference/applications/deployment) -``` +```bash vespa prod init [flags] ``` ### Options -``` +```bash -h, --help help for init ``` ### Options inherited from parent commands -``` +```bash -a, --application string The application to use. Format "tenant.application.instance" - instance is optional (tenant required for cloud targets) -C, --cluster string The container cluster to use. This is only required for applications with multiple clusters -c, --color string Whether to use colors in output. Must be "auto", "never", or "always" (default "auto") @@ -43,5 +37,5 @@ vespa prod init [flags] ### SEE ALSO -* [vespa prod](vespa_prod.html) - Deploy an application package to production in Vespa Cloud +* [vespa prod](/en/reference/clients/vespa-cli/vespa_prod) - Deploy an application package to production in Vespa Cloud diff --git a/en/reference/clients/vespa-cli/vespa_query.md b/en/reference/clients/vespa-cli/vespa_query.mdx similarity index 84% rename from en/reference/clients/vespa-cli/vespa_query.md rename to en/reference/clients/vespa-cli/vespa_query.mdx index f52271f366..0ccde77e26 100644 --- a/en/reference/clients/vespa-cli/vespa_query.md +++ b/en/reference/clients/vespa-cli/vespa_query.mdx @@ -1,26 +1,21 @@ --- -title: vespa query -render_with_liquid: false +title: "vespa query" +description: "Issue a query to Vespa" --- -## vespa query - -Issue a query to Vespa - ### Synopsis Issue a query to Vespa. -Any parameter from [https://docs.vespa.ai/en/reference/api/query.html](https://docs.vespa.ai/en/reference/api/query.html) -can be set by the syntax [parameter-name]=[value]. +Any parameter from [/en/reference/api/query.html](/en/reference/api/query) can be set by the syntax [parameter-name]=[value]. -``` +```bash vespa query query-parameters [flags] ``` ### Examples -``` +```bash $ vespa query 'yql=select * from music where album contains "head"' hits=5 $ vespa query --format=plain 'yql=select * from music where album contains "head"' hits=5 $ vespa query --file q-vector.json @@ -29,7 +24,7 @@ $ vespa query --header='X-First-Name: Joe' 'yql=select * from music where album ### Options -``` +```bash --file string Read query parameters from the given JSON file and send a POST request, with overrides from arguments --format string Output format. Must be 'human' (human-readable) or 'plain' (no formatting) (default "human") --header strings Add a header to the HTTP request, on the format 'Header: Value'. This can be specified multiple times @@ -41,7 +36,7 @@ $ vespa query --header='X-First-Name: Joe' 'yql=select * from music where album ### Options inherited from parent commands -``` +```bash -a, --application string The application to use. Format "tenant.application.instance" - instance is optional (tenant required for cloud targets) -C, --cluster string The container cluster to use. This is only required for applications with multiple clusters -c, --color string Whether to use colors in output. Must be "auto", "never", or "always" (default "auto") @@ -53,5 +48,5 @@ $ vespa query --header='X-First-Name: Joe' 'yql=select * from music where album ### SEE ALSO -* [vespa](vespa.html) - The command-line tool for Vespa.ai +* [vespa](/en/reference/clients/vespa-cli/vespa) - The command-line tool for Vespa.ai diff --git a/en/reference/clients/vespa-cli/vespa_status.md b/en/reference/clients/vespa-cli/vespa_status.mdx similarity index 71% rename from en/reference/clients/vespa-cli/vespa_status.md rename to en/reference/clients/vespa-cli/vespa_status.mdx index 6e307033a0..efd5d15b83 100644 --- a/en/reference/clients/vespa-cli/vespa_status.md +++ b/en/reference/clients/vespa-cli/vespa_status.mdx @@ -1,26 +1,21 @@ --- -title: vespa status -render_with_liquid: false +title: "vespa status" +description: "Show Vespa endpoints and status" --- -## vespa status - -Show Vespa endpoints and status - ### Synopsis Show Vespa endpoints and status. -This command shows the current endpoints, and their status, of a deployed Vespa -application. +This command shows the current endpoints, and their status, of a deployed Vespa application. -``` +```bash vespa status [flags] ``` ### Examples -``` +```bash $ vespa status $ vespa status --cluster mycluster $ vespa status --cluster mycluster --wait 600 @@ -30,7 +25,7 @@ $ vespa status --no-verify ### Options -``` +```bash --format string Output format. Must be 'human' (human-readable), 'plain' (cluster URL only), or 'json' (default "human") -h, --help help for status --no-verify Skip checking service status (control plane only) @@ -39,7 +34,7 @@ $ vespa status --no-verify ### Options inherited from parent commands -``` +```bash -a, --application string The application to use. Format "tenant.application.instance" - instance is optional (tenant required for cloud targets) -C, --cluster string The container cluster to use. This is only required for applications with multiple clusters -c, --color string Whether to use colors in output. Must be "auto", "never", or "always" (default "auto") @@ -51,8 +46,8 @@ $ vespa status --no-verify ### SEE ALSO -* [vespa](vespa.html) - The command-line tool for Vespa.ai -* [vespa status deploy](vespa_status_deploy.html) - Show status of the Vespa deploy service -* [vespa status deployment](vespa_status_deployment.html) - Show status of a Vespa deployment -* [vespa status endpoint](vespa_status_endpoint.html) - Show Vespa endpoints without checking their status +* [vespa](/en/reference/clients/vespa-cli/vespa) - The command-line tool for Vespa.ai +* [vespa status deploy](/en/reference/clients/vespa-cli/vespa_status_deploy) - Show status of the Vespa deploy service +* [vespa status deployment](/en/reference/clients/vespa-cli/vespa_status_deployment) - Show status of a Vespa deployment +* [vespa status endpoint](/en/reference/clients/vespa-cli/vespa_status_endpoint) - Show Vespa endpoints without checking their status diff --git a/en/reference/clients/vespa-cli/vespa_status_deploy.md b/en/reference/clients/vespa-cli/vespa_status_deploy.mdx similarity index 84% rename from en/reference/clients/vespa-cli/vespa_status_deploy.md rename to en/reference/clients/vespa-cli/vespa_status_deploy.mdx index 940f31d3c6..eaf50e64a7 100644 --- a/en/reference/clients/vespa-cli/vespa_status_deploy.md +++ b/en/reference/clients/vespa-cli/vespa_status_deploy.mdx @@ -1,25 +1,21 @@ --- -title: vespa status deploy -render_with_liquid: false +title: "vespa status deploy" +description: "Show status of the Vespa deploy service" --- -## vespa status deploy - -Show status of the Vespa deploy service - -``` +```bash vespa status deploy [flags] ``` ### Examples -``` +```bash $ vespa status deploy ``` ### Options -``` +```bash --format string Output format. Must be 'human' (human-readable text), 'plain' (cluster URL only), or 'json' (default "human") -h, --help help for deploy -w, --wait int Number of seconds to wait for service(s) to become ready. 0 to disable (default 0) @@ -27,7 +23,7 @@ $ vespa status deploy ### Options inherited from parent commands -``` +```bash -a, --application string The application to use. Format "tenant.application.instance" - instance is optional (tenant required for cloud targets) -C, --cluster string The container cluster to use. This is only required for applications with multiple clusters -c, --color string Whether to use colors in output. Must be "auto", "never", or "always" (default "auto") @@ -39,5 +35,5 @@ $ vespa status deploy ### SEE ALSO -* [vespa status](vespa_status.html) - Show Vespa endpoints and status +* [vespa status](/en/reference/clients/vespa-cli/vespa_status) - Show Vespa endpoints and status diff --git a/en/reference/clients/vespa-cli/vespa_status_deployment.md b/en/reference/clients/vespa-cli/vespa_status_deployment.mdx similarity index 79% rename from en/reference/clients/vespa-cli/vespa_status_deployment.md rename to en/reference/clients/vespa-cli/vespa_status_deployment.mdx index e1931074a9..4e1d30b92f 100644 --- a/en/reference/clients/vespa-cli/vespa_status_deployment.md +++ b/en/reference/clients/vespa-cli/vespa_status_deployment.mdx @@ -1,28 +1,22 @@ --- -title: vespa status deployment -render_with_liquid: false +title: "vespa status deployment" +description: "Show status of a Vespa deployment" --- -## vespa status deployment - -Show status of a Vespa deployment - ### Synopsis Show status of a Vespa deployment. -This commands shows whether a Vespa deployment has converged on the latest run -(Vespa Cloud) or config generation (self-hosted). If an argument is given, -show the convergence status of that particular run or generation. +This commands shows whether a Vespa deployment has converged on the latest run (Vespa Cloud) or config generation (self-hosted). If an argument is given, show the convergence status of that particular run or generation. -``` +```bash vespa status deployment [flags] ``` ### Examples -``` +```bash $ vespa status deployment $ vespa status deployment -t cloud [run-id] $ vespa status deployment -t local [session-id] @@ -32,14 +26,14 @@ $ vespa status deployment -t local [session-id] --wait 600 ### Options -``` +```bash -h, --help help for deployment -w, --wait int Number of seconds to wait for service(s) to become ready. 0 to disable (default 0) ``` ### Options inherited from parent commands -``` +```bash -a, --application string The application to use. Format "tenant.application.instance" - instance is optional (tenant required for cloud targets) -C, --cluster string The container cluster to use. This is only required for applications with multiple clusters -c, --color string Whether to use colors in output. Must be "auto", "never", or "always" (default "auto") @@ -52,5 +46,5 @@ $ vespa status deployment -t local [session-id] --wait 600 ### SEE ALSO -* [vespa status](vespa_status.html) - Show Vespa endpoints and status +* [vespa status](/en/reference/clients/vespa-cli/vespa_status) - Show Vespa endpoints and status diff --git a/en/reference/clients/vespa-cli/vespa_status_document.md b/en/reference/clients/vespa-cli/vespa_status_document.md deleted file mode 100644 index f97ae069ca..0000000000 --- a/en/reference/clients/vespa-cli/vespa_status_document.md +++ /dev/null @@ -1,112 +0,0 @@ ---- -title: vespa status document -render_with_liquid: false ---- - ---- -title: vespa status document -render_with_liquid: false ---- - ---- -title: vespa status document -render_with_liquid: false ---- - ---- -title: vespa status document -render_with_liquid: false ---- - ---- -title: vespa status document -render_with_liquid: false ---- - ---- -title: vespa status document -render_with_liquid: false ---- - ---- -title: vespa status document -render_with_liquid: false ---- - ---- -title: vespa status document -render_with_liquid: false ---- - ---- -title: vespa status document -render_with_liquid: false ---- - ---- -title: vespa status document -render_with_liquid: false ---- - ---- -title: vespa status document -render_with_liquid: false ---- - ---- -title: vespa status document -render_with_liquid: false ---- - ---- -title: vespa status document -render_with_liquid: false ---- - ---- -title: vespa status document -render_with_liquid: false ---- - ---- -title: vespa status document -render_with_liquid: false ---- - -## vespa status document - -Verify that the document service is ready to use - -``` -vespa status document [flags] -``` - -### Examples - -``` -$ vespa status document -``` - -### Options - -``` - -h, --help help for document - -w, --wait int Number of seconds to wait for a service to become ready. 0 to disable (default 0) -``` - -### Options inherited from parent commands - -``` - -a, --application string The application to use - -C, --cluster string The container cluster to use. This is only required for applications with multiple clusters - -c, --color string Whether to use colors in output. Must be "auto", "never", or "always" (default "auto") - -i, --instance string The instance of the application to use - -q, --quiet Print only errors - -t, --target string The target platform to use. Must be "local", "cloud", "hosted" or an URL (default "local") - -z, --zone string The zone to use. This defaults to a dev zone -``` - -### SEE ALSO - -* [vespa status](vespa_status.html) - Verify that a service is ready to use (query by default) - diff --git a/en/reference/clients/vespa-cli/vespa_status_document.mdx b/en/reference/clients/vespa-cli/vespa_status_document.mdx new file mode 100644 index 0000000000..23a7cef4bc --- /dev/null +++ b/en/reference/clients/vespa-cli/vespa_status_document.mdx @@ -0,0 +1,38 @@ +--- +title: "vespa status document" +description: "Verify that the document service is ready to use" +--- + +```bash +vespa status document [flags] +``` + +### Examples + +```bash +$ vespa status document +``` + +### Options + +```bash + -h, --help help for document + -w, --wait int Number of seconds to wait for a service to become ready. 0 to disable (default 0) +``` + +### Options inherited from parent commands + +```bash + -a, --application string The application to use + -C, --cluster string The container cluster to use. This is only required for applications with multiple clusters + -c, --color string Whether to use colors in output. Must be "auto", "never", or "always" (default "auto") + -i, --instance string The instance of the application to use + -q, --quiet Print only errors + -t, --target string The target platform to use. Must be "local", "cloud", "hosted" or an URL (default "local") + -z, --zone string The zone to use. This defaults to a dev zone +``` + +### SEE ALSO + +* [vespa status](/en/reference/clients/vespa-cli/vespa_status) - Verify that a service is ready to use (query by default) + diff --git a/en/reference/clients/vespa-cli/vespa_status_endpoint.md b/en/reference/clients/vespa-cli/vespa_status_endpoint.mdx similarity index 78% rename from en/reference/clients/vespa-cli/vespa_status_endpoint.md rename to en/reference/clients/vespa-cli/vespa_status_endpoint.mdx index 3dc91728d1..0698337115 100644 --- a/en/reference/clients/vespa-cli/vespa_status_endpoint.md +++ b/en/reference/clients/vespa-cli/vespa_status_endpoint.mdx @@ -1,29 +1,23 @@ --- -title: vespa status endpoint -render_with_liquid: false +title: "vespa status endpoint" +description: "Show Vespa endpoints without checking their status" --- -## vespa status endpoint - -Show Vespa endpoints without checking their status - ### Synopsis Show Vespa endpoints without checking their status. -This command shows the current endpoints of a deployed Vespa application, -discovered from the control plane, without contacting the data plane to check -their status. This is useful when you only have control plane credentials. +This command shows the current endpoints of a deployed Vespa application, discovered from the control plane, without contacting the data plane to check their status. This is useful when you only have control plane credentials. This is equivalent to: vespa status --no-verify -``` +```bash vespa status endpoint [flags] ``` ### Examples -``` +```bash $ vespa status endpoint $ vespa status endpoint --cluster mycluster $ vespa status endpoint --format plain @@ -31,7 +25,7 @@ $ vespa status endpoint --format plain ### Options -``` +```bash --format string Output format. Must be 'human' (human-readable), 'plain' (cluster URL only), or 'json' (default "human") -h, --help help for endpoint -w, --wait int Number of seconds to wait for service(s) to become ready. 0 to disable (default 0) @@ -39,7 +33,7 @@ $ vespa status endpoint --format plain ### Options inherited from parent commands -``` +```bash -a, --application string The application to use. Format "tenant.application.instance" - instance is optional (tenant required for cloud targets) -C, --cluster string The container cluster to use. This is only required for applications with multiple clusters -c, --color string Whether to use colors in output. Must be "auto", "never", or "always" (default "auto") @@ -51,5 +45,5 @@ $ vespa status endpoint --format plain ### SEE ALSO -* [vespa status](vespa_status.html) - Show Vespa endpoints and status +* [vespa status](/en/reference/clients/vespa-cli/vespa_status) - Show Vespa endpoints and status diff --git a/en/reference/clients/vespa-cli/vespa_status_query.md b/en/reference/clients/vespa-cli/vespa_status_query.md deleted file mode 100644 index ad4bb9e806..0000000000 --- a/en/reference/clients/vespa-cli/vespa_status_query.md +++ /dev/null @@ -1,112 +0,0 @@ ---- -title: vespa status query -render_with_liquid: false ---- - ---- -title: vespa status query -render_with_liquid: false ---- - ---- -title: vespa status query -render_with_liquid: false ---- - ---- -title: vespa status query -render_with_liquid: false ---- - ---- -title: vespa status query -render_with_liquid: false ---- - ---- -title: vespa status query -render_with_liquid: false ---- - ---- -title: vespa status query -render_with_liquid: false ---- - ---- -title: vespa status query -render_with_liquid: false ---- - ---- -title: vespa status query -render_with_liquid: false ---- - ---- -title: vespa status query -render_with_liquid: false ---- - ---- -title: vespa status query -render_with_liquid: false ---- - ---- -title: vespa status query -render_with_liquid: false ---- - ---- -title: vespa status query -render_with_liquid: false ---- - ---- -title: vespa status query -render_with_liquid: false ---- - ---- -title: vespa status query -render_with_liquid: false ---- - -## vespa status query - -Verify that the query service is ready to use (default) - -``` -vespa status query [flags] -``` - -### Examples - -``` -$ vespa status query -``` - -### Options - -``` - -h, --help help for query - -w, --wait int Number of seconds to wait for a service to become ready. 0 to disable (default 0) -``` - -### Options inherited from parent commands - -``` - -a, --application string The application to use - -C, --cluster string The container cluster to use. This is only required for applications with multiple clusters - -c, --color string Whether to use colors in output. Must be "auto", "never", or "always" (default "auto") - -i, --instance string The instance of the application to use - -q, --quiet Print only errors - -t, --target string The target platform to use. Must be "local", "cloud", "hosted" or an URL (default "local") - -z, --zone string The zone to use. This defaults to a dev zone -``` - -### SEE ALSO - -* [vespa status](vespa_status.html) - Verify that a service is ready to use (query by default) - diff --git a/en/reference/clients/vespa-cli/vespa_status_query.mdx b/en/reference/clients/vespa-cli/vespa_status_query.mdx new file mode 100644 index 0000000000..86531956a2 --- /dev/null +++ b/en/reference/clients/vespa-cli/vespa_status_query.mdx @@ -0,0 +1,38 @@ +--- +title: "vespa status query" +description: "Verify that the query service is ready to use (default)" +--- + +```bash +vespa status query [flags] +``` + +### Examples + +```bash +$ vespa status query +``` + +### Options + +```bash + -h, --help help for query + -w, --wait int Number of seconds to wait for a service to become ready. 0 to disable (default 0) +``` + +### Options inherited from parent commands + +```bash + -a, --application string The application to use + -C, --cluster string The container cluster to use. This is only required for applications with multiple clusters + -c, --color string Whether to use colors in output. Must be "auto", "never", or "always" (default "auto") + -i, --instance string The instance of the application to use + -q, --quiet Print only errors + -t, --target string The target platform to use. Must be "local", "cloud", "hosted" or an URL (default "local") + -z, --zone string The zone to use. This defaults to a dev zone +``` + +### SEE ALSO + +* [vespa status](/en/reference/clients/vespa-cli/vespa_status) - Verify that a service is ready to use (query by default) + diff --git a/en/reference/clients/vespa-cli/vespa_test.md b/en/reference/clients/vespa-cli/vespa_test.mdx similarity index 80% rename from en/reference/clients/vespa-cli/vespa_test.md rename to en/reference/clients/vespa-cli/vespa_test.mdx index 64f6996936..f7fce534b6 100644 --- a/en/reference/clients/vespa-cli/vespa_test.md +++ b/en/reference/clients/vespa-cli/vespa_test.mdx @@ -1,41 +1,37 @@ --- -title: vespa test -render_with_liquid: false +title: "vespa test" +description: "Run a test suite, or a single test" --- -## vespa test - -Run a test suite, or a single test - ### Synopsis Run a test suite, or a single test Runs all JSON test files in the specified directory, or the single JSON test file specified. -See [https://docs.vespa.ai/en/reference/applications/testing.html](https://docs.vespa.ai/en/reference/applications/testing.html) for details. +See [/en/reference/applications/testing.html](/en/reference/applications/testing) for details. -``` +```bash vespa test test-directory-or-file [flags] ``` ### Examples -``` +```bash $ vespa test src/test/application/tests/system-test $ vespa test src/test/application/tests/system-test/feed-and-query.json ``` ### Options -``` +```bash -h, --help help for test -w, --wait int Number of seconds to wait for service(s) to become ready. 0 to disable (default 0) ``` ### Options inherited from parent commands -``` +```bash -a, --application string The application to use. Format "tenant.application.instance" - instance is optional (tenant required for cloud targets) -C, --cluster string The container cluster to use. This is only required for applications with multiple clusters -c, --color string Whether to use colors in output. Must be "auto", "never", or "always" (default "auto") @@ -47,5 +43,5 @@ $ vespa test src/test/application/tests/system-test/feed-and-query.json ### SEE ALSO -* [vespa](vespa.html) - The command-line tool for Vespa.ai +* [vespa](/en/reference/clients/vespa-cli/vespa) - The command-line tool for Vespa.ai diff --git a/en/reference/clients/vespa-cli/vespa_version.md b/en/reference/clients/vespa-cli/vespa_version.mdx similarity index 82% rename from en/reference/clients/vespa-cli/vespa_version.md rename to en/reference/clients/vespa-cli/vespa_version.mdx index 4407fe7d94..242a01bf73 100644 --- a/en/reference/clients/vespa-cli/vespa_version.md +++ b/en/reference/clients/vespa-cli/vespa_version.mdx @@ -1,26 +1,22 @@ --- -title: vespa version -render_with_liquid: false +title: "vespa version" +description: "Show current CLI version and check for updates" --- -## vespa version - -Show current CLI version and check for updates - -``` +```bash vespa version [flags] ``` ### Options -``` +```text -h, --help help for version -n, --no-check Do not check if a new version is available ``` ### Options inherited from parent commands -``` +```bash -a, --application string The application to use. Format "tenant.application.instance" - instance is optional (tenant required for cloud targets) -C, --cluster string The container cluster to use. This is only required for applications with multiple clusters -c, --color string Whether to use colors in output. Must be "auto", "never", or "always" (default "auto") @@ -32,5 +28,5 @@ vespa version [flags] ### SEE ALSO -* [vespa](vespa.html) - The command-line tool for Vespa.ai +* [vespa](/en/reference/clients/vespa-cli/vespa) - The command-line tool for Vespa.ai diff --git a/en/reference/clients/vespa-cli/vespa_visit.md b/en/reference/clients/vespa-cli/vespa_visit.mdx similarity index 92% rename from en/reference/clients/vespa-cli/vespa_visit.md rename to en/reference/clients/vespa-cli/vespa_visit.mdx index e5914b924b..0041197d63 100644 --- a/en/reference/clients/vespa-cli/vespa_visit.md +++ b/en/reference/clients/vespa-cli/vespa_visit.mdx @@ -1,12 +1,8 @@ --- -title: vespa visit -render_with_liquid: false +title: "vespa visit" +description: "Retrieve and print all documents from Vespa" --- -## vespa visit - -Retrieve and print all documents from Vespa - ### Synopsis Retrieve and print all documents from Vespa. @@ -14,13 +10,13 @@ Retrieve and print all documents from Vespa. By default, prints each document received on its own line (JSONL format). -``` +```bash vespa visit [flags] ``` ### Examples -``` +```bash $ vespa visit # get documents from any cluster $ vespa visit --content-cluster search # get documents from cluster named "search" $ vespa visit --field-set "[id]" # list document IDs @@ -29,7 +25,7 @@ $ vespa visit --field-set "[id]" # list document IDs ### Options -``` +```bash --bucket-space strings The "default" or "global" bucket space (default [global,default]) --chunk-count int Chunk by count (default 1000) --content-cluster string Which content cluster to visit documents from (default "*") @@ -51,7 +47,7 @@ $ vespa visit --field-set "[id]" # list document IDs ### Options inherited from parent commands -``` +```bash -a, --application string The application to use. Format "tenant.application.instance" - instance is optional (tenant required for cloud targets) -C, --cluster string The container cluster to use. This is only required for applications with multiple clusters -c, --color string Whether to use colors in output. Must be "auto", "never", or "always" (default "auto") @@ -63,5 +59,5 @@ $ vespa visit --field-set "[id]" # list document IDs ### SEE ALSO -* [vespa](vespa.html) - The command-line tool for Vespa.ai +* [vespa](/en/reference/clients/vespa-cli/vespa) - The command-line tool for Vespa.ai diff --git a/en/reference/clustercontroller-metrics-reference.html b/en/reference/clustercontroller-metrics-reference.html deleted file mode 100644 index 3d2b8873f5..0000000000 --- a/en/reference/clustercontroller-metrics-reference.html +++ /dev/null @@ -1,127 +0,0 @@ ---- -# Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -title: "ClusterController Metrics" ---- - -<table class="table"> - <thead> - <tr><th>Name</th><th>Unit</th><th>Description</th></tr> - </thead> - <tbody> - <tr> - <td><p id="cluster-controller_down_count">cluster-controller.down.count</p></td> - <td>node</td> - <td>Number of content nodes down</td> - </tr> - <tr> - <td><p id="cluster-controller_initializing_count">cluster-controller.initializing.count</p></td> - <td>node</td> - <td>Number of content nodes initializing</td> - </tr> - <tr> - <td><p id="cluster-controller_maintenance_count">cluster-controller.maintenance.count</p></td> - <td>node</td> - <td>Number of content nodes in maintenance</td> - </tr> - <tr> - <td><p id="cluster-controller_retired_count">cluster-controller.retired.count</p></td> - <td>node</td> - <td>Number of content nodes that are retired</td> - </tr> - <tr> - <td><p id="cluster-controller_stopping_count">cluster-controller.stopping.count</p></td> - <td>node</td> - <td>Number of content nodes currently stopping</td> - </tr> - <tr> - <td><p id="cluster-controller_up_count">cluster-controller.up.count</p></td> - <td>node</td> - <td>Number of content nodes up</td> - </tr> - <tr> - <td><p id="cluster-controller_cluster-state-change_count">cluster-controller.cluster-state-change.count</p></td> - <td>node</td> - <td>Number of nodes changing state</td> - </tr> - <tr> - <td><p id="cluster-controller_nodes-not-converged">cluster-controller.nodes-not-converged</p></td> - <td>node</td> - <td>Number of nodes not converging to the latest cluster state version</td> - </tr> - <tr> - <td><p id="cluster-controller_stored-document-count">cluster-controller.stored-document-count</p></td> - <td>document</td> - <td>Total number of unique documents stored in the cluster</td> - </tr> - <tr> - <td><p id="cluster-controller_stored-document-bytes">cluster-controller.stored-document-bytes</p></td> - <td>byte</td> - <td>Combined byte size of all unique documents stored in the cluster (not including replication)</td> - </tr> - <tr> - <td><p id="cluster-controller_cluster-buckets-out-of-sync-ratio">cluster-controller.cluster-buckets-out-of-sync-ratio</p></td> - <td>fraction</td> - <td>Ratio of buckets in the cluster currently in need of syncing</td> - </tr> - <tr> - <td><p id="cluster-controller_busy-tick-time-ms">cluster-controller.busy-tick-time-ms</p></td> - <td>millisecond</td> - <td>Time busy</td> - </tr> - <tr> - <td><p id="cluster-controller_idle-tick-time-ms">cluster-controller.idle-tick-time-ms</p></td> - <td>millisecond</td> - <td>Time idle</td> - </tr> - <tr> - <td><p id="cluster-controller_work-ms">cluster-controller.work-ms</p></td> - <td>millisecond</td> - <td>Time used for actual work</td> - </tr> - <tr> - <td><p id="cluster-controller_is-master">cluster-controller.is-master</p></td> - <td>binary</td> - <td>1 if this cluster controller is currently the master, or 0 if not</td> - </tr> - <tr> - <td><p id="cluster-controller_remote-task-queue_size">cluster-controller.remote-task-queue.size</p></td> - <td>operation</td> - <td>Number of remote tasks queued</td> - </tr> - <tr> - <td><p id="cluster-controller_node-event_count">cluster-controller.node-event.count</p></td> - <td>operation</td> - <td>Number of node events</td> - </tr> - <tr> - <td><p id="cluster-controller_resource_usage_nodes_above_limit">cluster-controller.resource_usage.nodes_above_limit</p></td> - <td>node</td> - <td>The number of content nodes above resource limit, blocking feed</td> - </tr> - <tr> - <td><p id="cluster-controller_resource_usage_max_memory_utilization">cluster-controller.resource_usage.max_memory_utilization</p></td> - <td>fraction</td> - <td>Current memory utilisation, for content node with the highest value</td> - </tr> - <tr> - <td><p id="cluster-controller_resource_usage_max_disk_utilization">cluster-controller.resource_usage.max_disk_utilization</p></td> - <td>fraction</td> - <td>Current disk space utilisation, for content node with the highest value</td> - </tr> - <tr> - <td><p id="cluster-controller_resource_usage_memory_limit">cluster-controller.resource_usage.memory_limit</p></td> - <td>fraction</td> - <td>Memory space limit as a fraction of available memory</td> - </tr> - <tr> - <td><p id="cluster-controller_resource_usage_disk_limit">cluster-controller.resource_usage.disk_limit</p></td> - <td>fraction</td> - <td>Disk space limit as a fraction of available disk space</td> - </tr> - <tr> - <td><p id="reindexing_progress">reindexing.progress</p></td> - <td>fraction</td> - <td>Re-indexing progress</td> - </tr> - </tbody> -</table> diff --git a/en/reference/container-metrics-reference.html b/en/reference/container-metrics-reference.html deleted file mode 100644 index c04a7fc220..0000000000 --- a/en/reference/container-metrics-reference.html +++ /dev/null @@ -1,952 +0,0 @@ ---- -# Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -title: "Container Metrics" ---- - -<table class="table"> - <thead> - <tr><th>Name</th><th>Unit</th><th>Description</th></tr> - </thead> - <tbody> - <tr> - <td><p id="http_status_1xx">http.status.1xx</p></td> - <td>response</td> - <td>Number of responses with a 1xx status</td> - </tr> - <tr> - <td><p id="http_status_2xx">http.status.2xx</p></td> - <td>response</td> - <td>Number of responses with a 2xx status</td> - </tr> - <tr> - <td><p id="http_status_3xx">http.status.3xx</p></td> - <td>response</td> - <td>Number of responses with a 3xx status</td> - </tr> - <tr> - <td><p id="http_status_4xx">http.status.4xx</p></td> - <td>response</td> - <td>Number of responses with a 4xx status</td> - </tr> - <tr> - <td><p id="http_status_5xx">http.status.5xx</p></td> - <td>response</td> - <td>Number of responses with a 5xx status</td> - </tr> - <tr> - <td><p id="application_generation">application_generation</p></td> - <td>version</td> - <td>The currently live application config generation (aka session id)</td> - </tr> - <tr> - <td><p id="in_service">in_service</p></td> - <td>binary</td> - <td>This will have the value 1 if the node is in service, 0 if not.</td> - </tr> - <tr> - <td><p id="jdisc_gc_count">jdisc.gc.count</p></td> - <td>operation</td> - <td>Number of JVM garbage collections done</td> - </tr> - <tr> - <td><p id="jdisc_gc_ms">jdisc.gc.ms</p></td> - <td>millisecond</td> - <td>Time spent in JVM garbage collection</td> - </tr> - <tr> - <td><p id="jdisc_jvm">jdisc.jvm</p></td> - <td>version</td> - <td>JVM runtime version</td> - </tr> - <tr> - <td><p id="cpu">cpu</p></td> - <td>thread</td> - <td>Container service CPU pressure</td> - </tr> - <tr> - <td><p id="jdisc_memory_mappings">jdisc.memory_mappings</p></td> - <td>operation</td> - <td>JDISC Memory mappings</td> - </tr> - <tr> - <td><p id="jdisc_open_file_descriptors">jdisc.open_file_descriptors</p></td> - <td>item</td> - <td>JDISC Open file descriptors</td> - </tr> - <tr> - <td><p id="jdisc_thread_pool_unhandled_exceptions">jdisc.thread_pool.unhandled_exceptions</p></td> - <td>thread</td> - <td>Number of exceptions thrown by tasks</td> - </tr> - <tr> - <td><p id="jdisc_thread_pool_work_queue_capacity">jdisc.thread_pool.work_queue.capacity</p></td> - <td>thread</td> - <td>Capacity of the task queue</td> - </tr> - <tr> - <td><p id="jdisc_thread_pool_work_queue_size">jdisc.thread_pool.work_queue.size</p></td> - <td>thread</td> - <td>Size of the task queue</td> - </tr> - <tr> - <td><p id="jdisc_thread_pool_rejected_tasks">jdisc.thread_pool.rejected_tasks</p></td> - <td>thread</td> - <td>Number of tasks rejected by the thread pool</td> - </tr> - <tr> - <td><p id="jdisc_thread_pool_size">jdisc.thread_pool.size</p></td> - <td>thread</td> - <td>Size of the thread pool</td> - </tr> - <tr> - <td><p id="jdisc_thread_pool_max_allowed_size">jdisc.thread_pool.max_allowed_size</p></td> - <td>thread</td> - <td>The maximum allowed number of threads in the pool</td> - </tr> - <tr> - <td><p id="jdisc_thread_pool_active_threads">jdisc.thread_pool.active_threads</p></td> - <td>thread</td> - <td>Number of threads that are active</td> - </tr> - <tr> - <td><p id="jdisc_deactivated_containers_total">jdisc.deactivated_containers.total</p></td> - <td>item</td> - <td>JDISC Deactivated container instances</td> - </tr> - <tr> - <td><p id="jdisc_deactivated_containers_with_retained_refs_last">jdisc.deactivated_containers.with_retained_refs.last</p></td> - <td>item</td> - <td>JDISC Deactivated container nodes with retained refs</td> - </tr> - <tr> - <td><p id="jdisc_application_failed_component_graphs">jdisc.application.failed_component_graphs</p></td> - <td>item</td> - <td>JDISC Application failed component graphs</td> - </tr> - <tr> - <td><p id="jdisc_application_component_graph_creation_time_millis">jdisc.application.component_graph.creation_time_millis</p></td> - <td>millisecond</td> - <td>JDISC Application component graph creation time</td> - </tr> - <tr> - <td><p id="jdisc_application_component_graph_reconfigurations">jdisc.application.component_graph.reconfigurations</p></td> - <td>item</td> - <td>JDISC Application component graph reconfigurations</td> - </tr> - <tr> - <td><p id="jdisc_singleton_is_active">jdisc.singleton.is_active</p></td> - <td>item</td> - <td>JDISC Singleton is active</td> - </tr> - <tr> - <td><p id="jdisc_singleton_activation_count">jdisc.singleton.activation.count</p></td> - <td>operation</td> - <td>JDISC Singleton activations</td> - </tr> - <tr> - <td><p id="jdisc_singleton_activation_failure_count">jdisc.singleton.activation.failure.count</p></td> - <td>operation</td> - <td>JDISC Singleton activation failures</td> - </tr> - <tr> - <td><p id="jdisc_singleton_activation_millis">jdisc.singleton.activation.millis</p></td> - <td>millisecond</td> - <td>JDISC Singleton activation time</td> - </tr> - <tr> - <td><p id="jdisc_singleton_deactivation_count">jdisc.singleton.deactivation.count</p></td> - <td>operation</td> - <td>JDISC Singleton deactivations</td> - </tr> - <tr> - <td><p id="jdisc_singleton_deactivation_failure_count">jdisc.singleton.deactivation.failure.count</p></td> - <td>operation</td> - <td>JDISC Singleton deactivation failures</td> - </tr> - <tr> - <td><p id="jdisc_singleton_deactivation_millis">jdisc.singleton.deactivation.millis</p></td> - <td>millisecond</td> - <td>JDISC Singleton deactivation time</td> - </tr> - <tr> - <td><p id="jdisc_http_ssl_handshake_failure_missing_client_cert">jdisc.http.ssl.handshake.failure.missing_client_cert</p></td> - <td>operation</td> - <td>JDISC HTTP SSL Handshake failures due to missing client certificate</td> - </tr> - <tr> - <td><p id="jdisc_http_ssl_handshake_failure_expired_client_cert">jdisc.http.ssl.handshake.failure.expired_client_cert</p></td> - <td>operation</td> - <td>JDISC HTTP SSL Handshake failures due to expired client certificate</td> - </tr> - <tr> - <td><p id="jdisc_http_ssl_handshake_failure_invalid_client_cert">jdisc.http.ssl.handshake.failure.invalid_client_cert</p></td> - <td>operation</td> - <td>JDISC HTTP SSL Handshake failures due to invalid client certificate</td> - </tr> - <tr> - <td><p id="jdisc_http_ssl_handshake_failure_incompatible_protocols">jdisc.http.ssl.handshake.failure.incompatible_protocols</p></td> - <td>operation</td> - <td>JDISC HTTP SSL Handshake failures due to incompatible protocols</td> - </tr> - <tr> - <td><p id="jdisc_http_ssl_handshake_failure_incompatible_chifers">jdisc.http.ssl.handshake.failure.incompatible_chifers</p></td> - <td>operation</td> - <td>JDISC HTTP SSL Handshake failures due to incompatible chifers</td> - </tr> - <tr> - <td><p id="jdisc_http_ssl_handshake_failure_connection_closed">jdisc.http.ssl.handshake.failure.connection_closed</p></td> - <td>operation</td> - <td>JDISC HTTP SSL Handshake failures due to connection closed</td> - </tr> - <tr> - <td><p id="jdisc_http_ssl_handshake_failure_unknown">jdisc.http.ssl.handshake.failure.unknown</p></td> - <td>operation</td> - <td>JDISC HTTP SSL Handshake failures for unknown reason</td> - </tr> - <tr> - <td><p id="jdisc_http_request_prematurely_closed">jdisc.http.request.prematurely_closed</p></td> - <td>request</td> - <td>HTTP requests prematurely closed</td> - </tr> - <tr> - <td><p id="jdisc_http_request_requests_per_connection">jdisc.http.request.requests_per_connection</p></td> - <td>request</td> - <td>HTTP requests per connection</td> - </tr> - <tr> - <td><p id="jdisc_http_request_uri_length">jdisc.http.request.uri_length</p></td> - <td>byte</td> - <td>HTTP URI length</td> - </tr> - <tr> - <td><p id="jdisc_http_request_content_size">jdisc.http.request.content_size</p></td> - <td>byte</td> - <td>HTTP request content size</td> - </tr> - <tr> - <td><p id="jdisc_http_requests">jdisc.http.requests</p></td> - <td>request</td> - <td>HTTP requests</td> - </tr> - <tr> - <td><p id="jdisc_http_requests_status">jdisc.http.requests.status</p></td> - <td>request</td> - <td>Number of requests to the built-in status handler</td> - </tr> - <tr> - <td><p id="jdisc_http_filter_rule_blocked_requests">jdisc.http.filter.rule.blocked_requests</p></td> - <td>request</td> - <td>Number of requests blocked by filter</td> - </tr> - <tr> - <td><p id="jdisc_http_filter_rule_allowed_requests">jdisc.http.filter.rule.allowed_requests</p></td> - <td>request</td> - <td>Number of requests allowed by filter</td> - </tr> - <tr> - <td><p id="jdisc_http_filtering_request_handled">jdisc.http.filtering.request.handled</p></td> - <td>request</td> - <td>Number of filtering requests handled</td> - </tr> - <tr> - <td><p id="jdisc_http_filtering_request_unhandled">jdisc.http.filtering.request.unhandled</p></td> - <td>request</td> - <td>Number of filtering requests unhandled</td> - </tr> - <tr> - <td><p id="jdisc_http_filtering_response_handled">jdisc.http.filtering.response.handled</p></td> - <td>request</td> - <td>Number of filtering responses handled</td> - </tr> - <tr> - <td><p id="jdisc_http_filtering_response_unhandled">jdisc.http.filtering.response.unhandled</p></td> - <td>request</td> - <td>Number of filtering responses unhandled</td> - </tr> - <tr> - <td><p id="jdisc_http_handler_unhandled_exceptions">jdisc.http.handler.unhandled_exceptions</p></td> - <td>request</td> - <td>Number of unhandled exceptions in handler</td> - </tr> - <tr> - <td><p id="jdisc_tls_capability_checks_succeeded">jdisc.tls.capability_checks.succeeded</p></td> - <td>operation</td> - <td>Number of TLS capability checks succeeded</td> - </tr> - <tr> - <td><p id="jdisc_tls_capability_checks_failed">jdisc.tls.capability_checks.failed</p></td> - <td>operation</td> - <td>Number of TLS capability checks failed</td> - </tr> - <tr> - <td><p id="jdisc_http_jetty_threadpool_thread_max">jdisc.http.jetty.threadpool.thread.max</p></td> - <td>thread</td> - <td>Configured maximum number of threads</td> - </tr> - <tr> - <td><p id="jdisc_http_jetty_threadpool_thread_min">jdisc.http.jetty.threadpool.thread.min</p></td> - <td>thread</td> - <td>Configured minimum number of threads</td> - </tr> - <tr> - <td><p id="jdisc_http_jetty_threadpool_thread_reserved">jdisc.http.jetty.threadpool.thread.reserved</p></td> - <td>thread</td> - <td>Configured number of reserved threads or -1 for heuristic</td> - </tr> - <tr> - <td><p id="jdisc_http_jetty_threadpool_thread_busy">jdisc.http.jetty.threadpool.thread.busy</p></td> - <td>thread</td> - <td>Number of threads executing internal and transient jobs</td> - </tr> - <tr> - <td><p id="jdisc_http_jetty_threadpool_thread_idle">jdisc.http.jetty.threadpool.thread.idle</p></td> - <td>thread</td> - <td>Number of idle threads</td> - </tr> - <tr> - <td><p id="jdisc_http_jetty_threadpool_thread_total">jdisc.http.jetty.threadpool.thread.total</p></td> - <td>thread</td> - <td>Current number of threads</td> - </tr> - <tr> - <td><p id="jdisc_http_jetty_threadpool_queue_size">jdisc.http.jetty.threadpool.queue.size</p></td> - <td>thread</td> - <td>Current size of the job queue</td> - </tr> - <tr> - <td><p id="jdisc_http_jetty_http_compliance_violation">jdisc.http.jetty.http_compliance.violation</p></td> - <td>failure</td> - <td>Number of HTTP compliance violations</td> - </tr> - <tr> - <td><p id="serverNumOpenConnections">serverNumOpenConnections</p></td> - <td>connection</td> - <td>The number of currently open connections</td> - </tr> - <tr> - <td><p id="serverNumConnections">serverNumConnections</p></td> - <td>connection</td> - <td>The total number of connections opened</td> - </tr> - <tr> - <td><p id="serverBytesReceived">serverBytesReceived</p></td> - <td>byte</td> - <td>The number of bytes received by the server</td> - </tr> - <tr> - <td><p id="serverBytesSent">serverBytesSent</p></td> - <td>byte</td> - <td>The number of bytes sent from the server</td> - </tr> - <tr> - <td><p id="handled_requests">handled.requests</p></td> - <td>operation</td> - <td>The number of requests handled per metrics snapshot</td> - </tr> - <tr> - <td><p id="handled_latency">handled.latency</p></td> - <td>millisecond</td> - <td>The time used for requests during this metrics snapshot</td> - </tr> - <tr> - <td><p id="httpapi_latency">httpapi_latency</p></td> - <td>millisecond</td> - <td>Duration for requests to the HTTP document APIs</td> - </tr> - <tr> - <td><p id="httpapi_pending">httpapi_pending</p></td> - <td>operation</td> - <td>Document operations pending execution</td> - </tr> - <tr> - <td><p id="httpapi_num_operations">httpapi_num_operations</p></td> - <td>operation</td> - <td>Total number of document operations performed</td> - </tr> - <tr> - <td><p id="httpapi_num_updates">httpapi_num_updates</p></td> - <td>operation</td> - <td>Document update operations performed</td> - </tr> - <tr> - <td><p id="httpapi_num_removes">httpapi_num_removes</p></td> - <td>operation</td> - <td>Document remove operations performed</td> - </tr> - <tr> - <td><p id="httpapi_num_puts">httpapi_num_puts</p></td> - <td>operation</td> - <td>Document put operations performed</td> - </tr> - <tr> - <td><p id="httpapi_ops_per_sec">httpapi_ops_per_sec</p></td> - <td>operation_per_second</td> - <td>Document operations per second</td> - </tr> - <tr> - <td><p id="httpapi_succeeded">httpapi_succeeded</p></td> - <td>operation</td> - <td>Document operations that succeeded</td> - </tr> - <tr> - <td><p id="httpapi_failed">httpapi_failed</p></td> - <td>operation</td> - <td>Document operations that failed</td> - </tr> - <tr> - <td><p id="httpapi_parse_error">httpapi_parse_error</p></td> - <td>operation</td> - <td>Document operations that failed due to document parse errors</td> - </tr> - <tr> - <td><p id="httpapi_condition_not_met">httpapi_condition_not_met</p></td> - <td>operation</td> - <td>Document operations not applied due to condition not met</td> - </tr> - <tr> - <td><p id="httpapi_not_found">httpapi_not_found</p></td> - <td>operation</td> - <td>Document operations not applied due to document not found</td> - </tr> - <tr> - <td><p id="httpapi_failed_unknown">httpapi_failed_unknown</p></td> - <td>operation</td> - <td>Document operations failed by unknown cause</td> - </tr> - <tr> - <td><p id="httpapi_failed_timeout">httpapi_failed_timeout</p></td> - <td>operation</td> - <td>Document operations failed by timeout</td> - </tr> - <tr> - <td><p id="httpapi_failed_insufficient_storage">httpapi_failed_insufficient_storage</p></td> - <td>operation</td> - <td>Document operations failed by insufficient storage</td> - </tr> - <tr> - <td><p id="httpapi_queued_operations">httpapi_queued_operations</p></td> - <td>operation</td> - <td>Document operations queued for execution in /document/v1 API handler</td> - </tr> - <tr> - <td><p id="httpapi_queued_bytes">httpapi_queued_bytes</p></td> - <td>byte</td> - <td>Total operation bytes queued for execution in /document/v1 API handler</td> - </tr> - <tr> - <td><p id="httpapi_queued_age">httpapi_queued_age</p></td> - <td>second</td> - <td>Age in seconds of the oldest operation in the queue for /document/v1 API handler</td> - </tr> - <tr> - <td><p id="httpapi_mbus_window_size">httpapi_mbus_window_size</p></td> - <td>operation</td> - <td>The window size of Messagebus's dynamic throttle policy for /document/v1 API handler</td> - </tr> - <tr> - <td><p id="mem_heap_total">mem.heap.total</p></td> - <td>byte</td> - <td>Total available heap memory</td> - </tr> - <tr> - <td><p id="mem_heap_free">mem.heap.free</p></td> - <td>byte</td> - <td>Free heap memory</td> - </tr> - <tr> - <td><p id="mem_heap_used">mem.heap.used</p></td> - <td>byte</td> - <td>Currently used heap memory</td> - </tr> - <tr> - <td><p id="mem_direct_total">mem.direct.total</p></td> - <td>byte</td> - <td>Total available direct memory</td> - </tr> - <tr> - <td><p id="mem_direct_free">mem.direct.free</p></td> - <td>byte</td> - <td>Currently free direct memory</td> - </tr> - <tr> - <td><p id="mem_direct_used">mem.direct.used</p></td> - <td>byte</td> - <td>Direct memory currently used</td> - </tr> - <tr> - <td><p id="mem_direct_count">mem.direct.count</p></td> - <td>byte</td> - <td>Number of direct memory allocations</td> - </tr> - <tr> - <td><p id="mem_native_total">mem.native.total</p></td> - <td>byte</td> - <td>Total available native memory</td> - </tr> - <tr> - <td><p id="mem_native_free">mem.native.free</p></td> - <td>byte</td> - <td>Currently free native memory</td> - </tr> - <tr> - <td><p id="mem_native_used">mem.native.used</p></td> - <td>byte</td> - <td>Native memory currently used</td> - </tr> - <tr> - <td><p id="athenz-tenant-cert_expiry_seconds">athenz-tenant-cert.expiry.seconds</p></td> - <td>second</td> - <td>Time remaining until Athenz tenant certificate expires</td> - </tr> - <tr> - <td><p id="container-iam-role_expiry_seconds">container-iam-role.expiry.seconds</p></td> - <td>second</td> - <td>Time remaining until IAM role expires</td> - </tr> - <tr> - <td><p id="peak_qps">peak_qps</p></td> - <td>query_per_second</td> - <td>The highest number of qps for a second for this metrics snapshot</td> - </tr> - <tr> - <td><p id="search_connections">search_connections</p></td> - <td>connection</td> - <td>Number of search connections</td> - </tr> - <tr> - <td><p id="feed_operations">feed.operations</p></td> - <td>operation</td> - <td>Number of document feed operations</td> - </tr> - <tr> - <td><p id="feed_latency">feed.latency</p></td> - <td>millisecond</td> - <td>Feed latency</td> - </tr> - <tr> - <td><p id="feed_http-requests">feed.http-requests</p></td> - <td>operation</td> - <td>Feed HTTP requests</td> - </tr> - <tr> - <td><p id="queries">queries</p></td> - <td>operation</td> - <td>Query volume</td> - </tr> - <tr> - <td><p id="query_container_latency">query_container_latency</p></td> - <td>millisecond</td> - <td>The query execution time consumed in the container</td> - </tr> - <tr> - <td><p id="query_latency">query_latency</p></td> - <td>millisecond</td> - <td>The overall query latency as seen by the container</td> - </tr> - <tr> - <td><p id="query_timeout">query_timeout</p></td> - <td>millisecond</td> - <td>The amount of time allowed for query execution, from the client</td> - </tr> - <tr> - <td><p id="failed_queries">failed_queries</p></td> - <td>operation</td> - <td>The number of failed queries</td> - </tr> - <tr> - <td><p id="degraded_queries">degraded_queries</p></td> - <td>operation</td> - <td>The number of degraded queries, e.g. due to some content nodes not responding in time</td> - </tr> - <tr> - <td><p id="hits_per_query">hits_per_query</p></td> - <td>hit_per_query</td> - <td>The number of hits returned</td> - </tr> - <tr> - <td><p id="query_hit_offset">query_hit_offset</p></td> - <td>hit</td> - <td>The offset for hits returned</td> - </tr> - <tr> - <td><p id="documents_covered">documents_covered</p></td> - <td>document</td> - <td>The combined number of documents considered during query evaluation</td> - </tr> - <tr> - <td><p id="documents_total">documents_total</p></td> - <td>document</td> - <td>The number of documents to be evaluated if all requests had been fully executed</td> - </tr> - <tr> - <td><p id="documents_target_total">documents_target_total</p></td> - <td>document</td> - <td>The target number of total documents to be evaluated when all data is in sync</td> - </tr> - <tr> - <td><p id="jdisc_render_latency">jdisc.render.latency</p></td> - <td>nanosecond</td> - <td>The time used by the container to render responses</td> - </tr> - <tr> - <td><p id="query_item_count">query_item_count</p></td> - <td>item</td> - <td>The number of query items (terms, phrases, etc.)</td> - </tr> - <tr> - <td><p id="docproc_proctime">docproc.proctime</p></td> - <td>millisecond</td> - <td>Time spent processing document</td> - </tr> - <tr> - <td><p id="docproc_documents">docproc.documents</p></td> - <td>document</td> - <td>Number of processed documents</td> - </tr> - <tr> - <td><p id="totalhits_per_query">totalhits_per_query</p></td> - <td>hit_per_query</td> - <td>The total number of documents found to match queries</td> - </tr> - <tr> - <td><p id="empty_results">empty_results</p></td> - <td>operation</td> - <td>Number of queries matching no documents</td> - </tr> - <tr> - <td><p id="requestsOverQuota">requestsOverQuota</p></td> - <td>operation</td> - <td>The number of requests rejected due to exceeding quota</td> - </tr> - <tr> - <td><p id="relevance_at_1">relevance.at_1</p></td> - <td>score</td> - <td>The relevance of hit number 1</td> - </tr> - <tr> - <td><p id="relevance_at_3">relevance.at_3</p></td> - <td>score</td> - <td>The relevance of hit number 3</td> - </tr> - <tr> - <td><p id="relevance_at_10">relevance.at_10</p></td> - <td>score</td> - <td>The relevance of hit number 10</td> - </tr> - <tr> - <td><p id="error_timeout">error.timeout</p></td> - <td>operation</td> - <td>Requests that timed out</td> - </tr> - <tr> - <td><p id="error_backends_oos">error.backends_oos</p></td> - <td>operation</td> - <td>Requests that failed due to no available backends nodes</td> - </tr> - <tr> - <td><p id="error_plugin_failure">error.plugin_failure</p></td> - <td>operation</td> - <td>Requests that failed due to plugin failure</td> - </tr> - <tr> - <td><p id="error_backend_communication_error">error.backend_communication_error</p></td> - <td>operation</td> - <td>Requests that failed due to backend communication error</td> - </tr> - <tr> - <td><p id="error_empty_document_summaries">error.empty_document_summaries</p></td> - <td>operation</td> - <td>Requests that failed due to missing document summaries</td> - </tr> - <tr> - <td><p id="error_illegal_query">error.illegal_query</p></td> - <td>operation</td> - <td>Requests that failed due to illegal queries</td> - </tr> - <tr> - <td><p id="error_invalid_query_parameter">error.invalid_query_parameter</p></td> - <td>operation</td> - <td>Requests that failed due to invalid query parameters</td> - </tr> - <tr> - <td><p id="error_internal_server_error">error.internal_server_error</p></td> - <td>operation</td> - <td>Requests that failed due to internal server error</td> - </tr> - <tr> - <td><p id="error_misconfigured_server">error.misconfigured_server</p></td> - <td>operation</td> - <td>Requests that failed due to misconfigured server</td> - </tr> - <tr> - <td><p id="error_invalid_query_transformation">error.invalid_query_transformation</p></td> - <td>operation</td> - <td>Requests that failed due to invalid query transformation</td> - </tr> - <tr> - <td><p id="error_results_with_errors">error.results_with_errors</p></td> - <td>operation</td> - <td>The number of queries with error payload</td> - </tr> - <tr> - <td><p id="error_unspecified">error.unspecified</p></td> - <td>operation</td> - <td>Requests that failed for an unspecified reason</td> - </tr> - <tr> - <td><p id="error_unhandled_exception">error.unhandled_exception</p></td> - <td>operation</td> - <td>Requests that failed due to an unhandled exception</td> - </tr> - <tr> - <td><p id="serverRejectedRequests">serverRejectedRequests</p></td> - <td>operation</td> - <td>Deprecated. Use jdisc.thread_pool.rejected_tasks instead.</td> - </tr> - <tr> - <td><p id="serverThreadPoolSize">serverThreadPoolSize</p></td> - <td>thread</td> - <td>Deprecated. Use jdisc.thread_pool.size instead.</td> - </tr> - <tr> - <td><p id="serverActiveThreads">serverActiveThreads</p></td> - <td>thread</td> - <td>Deprecated. Use jdisc.thread_pool.active_threads instead.</td> - </tr> - <tr> - <td><p id="jrt_transport_tls-certificate-verification-failures">jrt.transport.tls-certificate-verification-failures</p></td> - <td>failure</td> - <td>TLS certificate verification failures</td> - </tr> - <tr> - <td><p id="jrt_transport_peer-authorization-failures">jrt.transport.peer-authorization-failures</p></td> - <td>failure</td> - <td>TLS peer authorization failures</td> - </tr> - <tr> - <td><p id="jrt_transport_server_tls-connections-established">jrt.transport.server.tls-connections-established</p></td> - <td>connection</td> - <td>TLS server connections established</td> - </tr> - <tr> - <td><p id="jrt_transport_client_tls-connections-established">jrt.transport.client.tls-connections-established</p></td> - <td>connection</td> - <td>TLS client connections established</td> - </tr> - <tr> - <td><p id="jrt_transport_server_unencrypted-connections-established">jrt.transport.server.unencrypted-connections-established</p></td> - <td>connection</td> - <td>Unencrypted server connections established</td> - </tr> - <tr> - <td><p id="jrt_transport_client_unencrypted-connections-established">jrt.transport.client.unencrypted-connections-established</p></td> - <td>connection</td> - <td>Unencrypted client connections established</td> - </tr> - <tr> - <td><p id="max_query_latency">max_query_latency</p></td> - <td>millisecond</td> - <td>Deprecated. Use query_latency.max instead</td> - </tr> - <tr> - <td><p id="mean_query_latency">mean_query_latency</p></td> - <td>millisecond</td> - <td>Deprecated. Use the expression (query_latency.sum / query_latency.count) instead</td> - </tr> - <tr> - <td><p id="jdisc_http_filter_athenz_accepted_requests">jdisc.http.filter.athenz.accepted_requests</p></td> - <td>request</td> - <td>Number of requests accepted by the AthenzAuthorization filter</td> - </tr> - <tr> - <td><p id="jdisc_http_filter_athenz_rejected_requests">jdisc.http.filter.athenz.rejected_requests</p></td> - <td>request</td> - <td>Number of requests rejected by the AthenzAuthorization filter</td> - </tr> - <tr> - <td><p id="jdisc_http_filter_athenz_grid_requests">jdisc.http.filter.athenz.grid_requests</p></td> - <td>request</td> - <td>Number of grid requests</td> - </tr> - <tr> - <td><p id="serverConnectionsOpenMax">serverConnectionsOpenMax</p></td> - <td>connection</td> - <td>Maximum number of open connections</td> - </tr> - <tr> - <td><p id="serverConnectionDurationMax">serverConnectionDurationMax</p></td> - <td>millisecond</td> - <td>Longest duration a connection is kept open</td> - </tr> - <tr> - <td><p id="serverConnectionDurationMean">serverConnectionDurationMean</p></td> - <td>millisecond</td> - <td>Average duration a connection is kept open</td> - </tr> - <tr> - <td><p id="serverConnectionDurationStdDev">serverConnectionDurationStdDev</p></td> - <td>millisecond</td> - <td>Standard deviation of open connection duration</td> - </tr> - <tr> - <td><p id="serverNumRequests">serverNumRequests</p></td> - <td>request</td> - <td>Number of requests</td> - </tr> - <tr> - <td><p id="serverNumSuccessfulResponses">serverNumSuccessfulResponses</p></td> - <td>request</td> - <td>Number of successful responses</td> - </tr> - <tr> - <td><p id="serverNumFailedResponses">serverNumFailedResponses</p></td> - <td>request</td> - <td>Number of failed responses</td> - </tr> - <tr> - <td><p id="serverNumSuccessfulResponseWrites">serverNumSuccessfulResponseWrites</p></td> - <td>request</td> - <td>Number of successful response writes</td> - </tr> - <tr> - <td><p id="serverNumFailedResponseWrites">serverNumFailedResponseWrites</p></td> - <td>request</td> - <td>Number of failed response writes</td> - </tr> - <tr> - <td><p id="serverTotalSuccessfulResponseLatency">serverTotalSuccessfulResponseLatency</p></td> - <td>millisecond</td> - <td>Total duration for execution of successful responses</td> - </tr> - <tr> - <td><p id="serverTotalFailedResponseLatency">serverTotalFailedResponseLatency</p></td> - <td>millisecond</td> - <td>Total duration for execution of failed responses</td> - </tr> - <tr> - <td><p id="serverTimeToFirstByte">serverTimeToFirstByte</p></td> - <td>millisecond</td> - <td>Time from request has been received by the server until the first byte is returned to the client</td> - </tr> - <tr> - <td><p id="serverStartedMillis">serverStartedMillis</p></td> - <td>millisecond</td> - <td>Time since the service was started</td> - </tr> - <tr> - <td><p id="embedder_latency">embedder.latency</p></td> - <td>millisecond</td> - <td>Time spent creating an embedding</td> - </tr> - <tr> - <td><p id="embedder_sequence_length">embedder.sequence_length</p></td> - <td>byte</td> - <td>Size of sequence produced by tokenizer</td> - </tr> - <tr> - <td><p id="jvm_buffer_count">jvm.buffer.count</p></td> - <td>buffer</td> - <td>An estimate of the number of buffers in the pool</td> - </tr> - <tr> - <td><p id="jvm_buffer_memory_used">jvm.buffer.memory.used</p></td> - <td>byte</td> - <td>An estimate of the memory that the Java virtual machine is using for this buffer pool</td> - </tr> - <tr> - <td><p id="jvm_buffer_total_capacity">jvm.buffer.total.capacity</p></td> - <td>byte</td> - <td>An estimate of the total capacity of the buffers in this pool</td> - </tr> - <tr> - <td><p id="jvm_classes_loaded">jvm.classes.loaded</p></td> - <td>class</td> - <td>The number of classes that are currently loaded in the Java virtual machine</td> - </tr> - <tr> - <td><p id="jvm_classes_unloaded">jvm.classes.unloaded</p></td> - <td>class</td> - <td>The total number of classes unloaded since the Java virtual machine has started execution</td> - </tr> - <tr> - <td><p id="jvm_gc_concurrent_phase_time">jvm.gc.concurrent.phase.time</p></td> - <td>second</td> - <td>Time spent in concurrent phase</td> - </tr> - <tr> - <td><p id="jvm_gc_live_data_size">jvm.gc.live.data.size</p></td> - <td>byte</td> - <td>Size of long-lived heap memory pool after reclamation</td> - </tr> - <tr> - <td><p id="jvm_gc_max_data_size">jvm.gc.max.data.size</p></td> - <td>byte</td> - <td>Max size of long-lived heap memory pool</td> - </tr> - <tr> - <td><p id="jvm_gc_memory_allocated">jvm.gc.memory.allocated</p></td> - <td>byte</td> - <td>Incremented for an increase in the size of the (young) heap memory pool after one GC to before the next</td> - </tr> - <tr> - <td><p id="jvm_gc_memory_promoted">jvm.gc.memory.promoted</p></td> - <td>byte</td> - <td>Count of positive increases in the size of the old generation memory pool before GC to after GC</td> - </tr> - <tr> - <td><p id="jvm_gc_overhead">jvm.gc.overhead</p></td> - <td>percentage</td> - <td>An approximation of the percent of CPU time used by GC activities</td> - </tr> - <tr> - <td><p id="jvm_gc_pause">jvm.gc.pause</p></td> - <td>second</td> - <td>Time spent in GC pause</td> - </tr> - <tr> - <td><p id="jvm_memory_committed">jvm.memory.committed</p></td> - <td>byte</td> - <td>The amount of memory in bytes that is committed for the Java virtual machine to use</td> - </tr> - <tr> - <td><p id="jvm_memory_max">jvm.memory.max</p></td> - <td>byte</td> - <td>The maximum amount of memory in bytes that can be used for memory management</td> - </tr> - <tr> - <td><p id="jvm_memory_usage_after_gc">jvm.memory.usage.after.gc</p></td> - <td>percentage</td> - <td>The percentage of long-lived heap pool used after the last GC event</td> - </tr> - <tr> - <td><p id="jvm_memory_used">jvm.memory.used</p></td> - <td>byte</td> - <td>The amount of used memory</td> - </tr> - <tr> - <td><p id="jvm_threads_daemon">jvm.threads.daemon</p></td> - <td>thread</td> - <td>The current number of live daemon threads</td> - </tr> - <tr> - <td><p id="jvm_threads_live">jvm.threads.live</p></td> - <td>thread</td> - <td>The current number of live threads including both daemon and non-daemon threads</td> - </tr> - <tr> - <td><p id="jvm_threads_peak">jvm.threads.peak</p></td> - <td>thread</td> - <td>The peak live thread count since the Java virtual machine started or peak was reset</td> - </tr> - <tr> - <td><p id="jvm_threads_started">jvm.threads.started</p></td> - <td>thread</td> - <td>The total number of application threads started in the JVM</td> - </tr> - <tr> - <td><p id="jvm_threads_states">jvm.threads.states</p></td> - <td>thread</td> - <td>The current number of threads (in each state)</td> - </tr> - </tbody> -</table> diff --git a/en/reference/default-set-metrics-reference.html b/en/reference/default-set-metrics-reference.html deleted file mode 100644 index a2b3492f1d..0000000000 --- a/en/reference/default-set-metrics-reference.html +++ /dev/null @@ -1,432 +0,0 @@ ---- -# Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -# Note: This file is generated by -# https://github.com/vespa-engine/vespa/blob/master/metrics/src/main/java/ai/vespa/metrics/docs/MetricSetDocumentation.java -title: "Default Metric Set" ---- -<p> - This document provides reference documentation for the Default metric set, including suffixes present per metric. - If the suffix column contains "N/A" then the base name of the corresponding metric is used with no suffix. -</p> -<h2 id="clustercontroller-metrics">ClusterController Metrics</h2> -<table class="table"> - <thead> - <tr><th>Name</th><th>Unit</th><th>Suffixes</th><th>Description</th></tr> - </thead> - <tbody> - <tr> - <td><p id="cluster-controller_down_count">cluster-controller.down.count</p></td> - <td>node</td> - <td>last, max</td> - <td>Number of content nodes down</td> - </tr> - <tr> - <td><p id="cluster-controller_maintenance_count">cluster-controller.maintenance.count</p></td> - <td>node</td> - <td>last, max</td> - <td>Number of content nodes in maintenance</td> - </tr> - <tr> - <td><p id="cluster-controller_up_count">cluster-controller.up.count</p></td> - <td>node</td> - <td>last, max</td> - <td>Number of content nodes up</td> - </tr> - <tr> - <td><p id="cluster-controller_is-master">cluster-controller.is-master</p></td> - <td>binary</td> - <td>last, max</td> - <td>1 if this cluster controller is currently the master, or 0 if not</td> - </tr> - <tr> - <td><p id="cluster-controller_resource_usage_nodes_above_limit">cluster-controller.resource_usage.nodes_above_limit</p></td> - <td>node</td> - <td>last, max</td> - <td>The number of content nodes above resource limit, blocking feed</td> - </tr> - <tr> - <td><p id="cluster-controller_resource_usage_max_memory_utilization">cluster-controller.resource_usage.max_memory_utilization</p></td> - <td>fraction</td> - <td>last, max</td> - <td>Current memory utilisation, for content node with the highest value</td> - </tr> - <tr> - <td><p id="cluster-controller_resource_usage_max_disk_utilization">cluster-controller.resource_usage.max_disk_utilization</p></td> - <td>fraction</td> - <td>last, max</td> - <td>Current disk space utilisation, for content node with the highest value</td> - </tr> - </tbody> -</table> - -<h2 id="container-metrics">Container Metrics</h2> -<table class="table"> - <thead> - <tr><th>Name</th><th>Unit</th><th>Suffixes</th><th>Description</th></tr> - </thead> - <tbody> - <tr> - <td><p id="http_status_1xx">http.status.1xx</p></td> - <td>response</td> - <td>rate</td> - <td>Number of responses with a 1xx status</td> - </tr> - <tr> - <td><p id="http_status_2xx">http.status.2xx</p></td> - <td>response</td> - <td>rate</td> - <td>Number of responses with a 2xx status</td> - </tr> - <tr> - <td><p id="http_status_3xx">http.status.3xx</p></td> - <td>response</td> - <td>rate</td> - <td>Number of responses with a 3xx status</td> - </tr> - <tr> - <td><p id="http_status_4xx">http.status.4xx</p></td> - <td>response</td> - <td>rate</td> - <td>Number of responses with a 4xx status</td> - </tr> - <tr> - <td><p id="http_status_5xx">http.status.5xx</p></td> - <td>response</td> - <td>rate</td> - <td>Number of responses with a 5xx status</td> - </tr> - <tr> - <td><p id="jdisc_gc_ms">jdisc.gc.ms</p></td> - <td>millisecond</td> - <td>average, max</td> - <td>Time spent in JVM garbage collection</td> - </tr> - <tr> - <td><p id="jdisc_thread_pool_work_queue_capacity">jdisc.thread_pool.work_queue.capacity</p></td> - <td>thread</td> - <td>max</td> - <td>Capacity of the task queue</td> - </tr> - <tr> - <td><p id="jdisc_thread_pool_work_queue_size">jdisc.thread_pool.work_queue.size</p></td> - <td>thread</td> - <td>count, max, min, sum</td> - <td>Size of the task queue</td> - </tr> - <tr> - <td><p id="jdisc_thread_pool_size">jdisc.thread_pool.size</p></td> - <td>thread</td> - <td>max</td> - <td>Size of the thread pool</td> - </tr> - <tr> - <td><p id="jdisc_thread_pool_active_threads">jdisc.thread_pool.active_threads</p></td> - <td>thread</td> - <td>count, max, min, sum</td> - <td>Number of threads that are active</td> - </tr> - <tr> - <td><p id="jdisc_application_failed_component_graphs">jdisc.application.failed_component_graphs</p></td> - <td>item</td> - <td>rate</td> - <td>JDISC Application failed component graphs</td> - </tr> - <tr> - <td><p id="jdisc_singleton_is_active">jdisc.singleton.is_active</p></td> - <td>item</td> - <td>last, max</td> - <td>JDISC Singleton is active</td> - </tr> - <tr> - <td><p id="jdisc_http_ssl_handshake_failure_missing_client_cert">jdisc.http.ssl.handshake.failure.missing_client_cert</p></td> - <td>operation</td> - <td>rate</td> - <td>JDISC HTTP SSL Handshake failures due to missing client certificate</td> - </tr> - <tr> - <td><p id="jdisc_http_ssl_handshake_failure_incompatible_protocols">jdisc.http.ssl.handshake.failure.incompatible_protocols</p></td> - <td>operation</td> - <td>rate</td> - <td>JDISC HTTP SSL Handshake failures due to incompatible protocols</td> - </tr> - <tr> - <td><p id="jdisc_http_ssl_handshake_failure_incompatible_chifers">jdisc.http.ssl.handshake.failure.incompatible_chifers</p></td> - <td>operation</td> - <td>rate</td> - <td>JDISC HTTP SSL Handshake failures due to incompatible chifers</td> - </tr> - <tr> - <td><p id="jdisc_http_ssl_handshake_failure_unknown">jdisc.http.ssl.handshake.failure.unknown</p></td> - <td>operation</td> - <td>rate</td> - <td>JDISC HTTP SSL Handshake failures for unknown reason</td> - </tr> - <tr> - <td><p id="mem_heap_free">mem.heap.free</p></td> - <td>byte</td> - <td>average</td> - <td>Free heap memory</td> - </tr> - <tr> - <td><p id="athenz-tenant-cert_expiry_seconds">athenz-tenant-cert.expiry.seconds</p></td> - <td>second</td> - <td>last, max, min</td> - <td>Time remaining until Athenz tenant certificate expires</td> - </tr> - <tr> - <td><p id="feed_operations">feed.operations</p></td> - <td>operation</td> - <td>rate</td> - <td>Number of document feed operations</td> - </tr> - <tr> - <td><p id="feed_latency">feed.latency</p></td> - <td>millisecond</td> - <td>count, sum</td> - <td>Feed latency</td> - </tr> - <tr> - <td><p id="queries">queries</p></td> - <td>operation</td> - <td>rate</td> - <td>Query volume</td> - </tr> - <tr> - <td><p id="query_latency">query_latency</p></td> - <td>millisecond</td> - <td>average, count, max, sum</td> - <td>The overall query latency as seen by the container</td> - </tr> - <tr> - <td><p id="failed_queries">failed_queries</p></td> - <td>operation</td> - <td>rate</td> - <td>The number of failed queries</td> - </tr> - <tr> - <td><p id="degraded_queries">degraded_queries</p></td> - <td>operation</td> - <td>rate</td> - <td>The number of degraded queries, e.g. due to some content nodes not responding in time</td> - </tr> - <tr> - <td><p id="hits_per_query">hits_per_query</p></td> - <td>hit_per_query</td> - <td>average, count, max, sum</td> - <td>The number of hits returned</td> - </tr> - <tr> - <td><p id="docproc_documents">docproc.documents</p></td> - <td>document</td> - <td>sum</td> - <td>Number of processed documents</td> - </tr> - <tr> - <td><p id="totalhits_per_query">totalhits_per_query</p></td> - <td>hit_per_query</td> - <td>average, count, max, sum</td> - <td>The total number of documents found to match queries</td> - </tr> - <tr> - <td><p id="serverActiveThreads">serverActiveThreads</p></td> - <td>thread</td> - <td>average</td> - <td>Deprecated. Use jdisc.thread_pool.active_threads instead.</td> - </tr> - </tbody> -</table> - -<h2 id="distributor-metrics">Distributor Metrics</h2> -<table class="table"> - <thead> - <tr><th>Name</th><th>Unit</th><th>Suffixes</th><th>Description</th></tr> - </thead> - <tbody> - <tr> - <td><p id="vds_distributor_docsstored">vds.distributor.docsstored</p></td> - <td>document</td> - <td>average</td> - <td>Number of documents stored in all buckets controlled by this distributor</td> - </tr> - <tr> - <td><p id="vds_bouncer_clock_skew_aborts">vds.bouncer.clock_skew_aborts</p></td> - <td>operation</td> - <td>count</td> - <td>Number of client operations that were aborted due to clock skew between sender and receiver exceeding acceptable range</td> - </tr> - </tbody> -</table> - -<h2 id="nodeadmin-metrics">NodeAdmin Metrics</h2> -<table class="table"> - <thead> - <tr><th>Name</th><th>Unit</th><th>Suffixes</th><th>Description</th></tr> - </thead> - <tbody> - <tr> - <td><p id="endpoint_certificate_expiry_seconds">endpoint.certificate.expiry.seconds</p></td> - <td>second</td> - <td>N/A</td> - <td>Time until node endpoint certificate expires</td> - </tr> - <tr> - <td><p id="node-certificate_expiry_seconds">node-certificate.expiry.seconds</p></td> - <td>second</td> - <td>N/A</td> - <td>Time until node certificate expires</td> - </tr> - </tbody> -</table> - -<h2 id="searchnode-metrics">SearchNode Metrics</h2> -<table class="table"> - <thead> - <tr><th>Name</th><th>Unit</th><th>Suffixes</th><th>Description</th></tr> - </thead> - <tbody> - <tr> - <td><p id="content_proton_documentdb_documents_total">content.proton.documentdb.documents.total</p></td> - <td>document</td> - <td>last, max</td> - <td>The total number of documents in this documents db (ready + not-ready)</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_documents_ready">content.proton.documentdb.documents.ready</p></td> - <td>document</td> - <td>last, max</td> - <td>The number of ready documents in this document db</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_documents_active">content.proton.documentdb.documents.active</p></td> - <td>document</td> - <td>last, max</td> - <td>The number of active / searchable documents in this document db</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_disk_usage">content.proton.documentdb.disk_usage</p></td> - <td>byte</td> - <td>last</td> - <td>The total disk usage (in bytes) for this document db</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_memory_usage_allocated_bytes">content.proton.documentdb.memory_usage.allocated_bytes</p></td> - <td>byte</td> - <td>last</td> - <td>The number of allocated bytes</td> - </tr> - <tr> - <td><p id="content_proton_search_protocol_query_latency">content.proton.search_protocol.query.latency</p></td> - <td>second</td> - <td>average, count, max, sum</td> - <td>Query request latency (seconds)</td> - </tr> - <tr> - <td><p id="content_proton_search_protocol_docsum_latency">content.proton.search_protocol.docsum.latency</p></td> - <td>second</td> - <td>average, count, max, sum</td> - <td>Docsum request latency (seconds)</td> - </tr> - <tr> - <td><p id="content_proton_search_protocol_docsum_requested_documents">content.proton.search_protocol.docsum.requested_documents</p></td> - <td>document</td> - <td>rate</td> - <td>Total requested document summaries</td> - </tr> - <tr> - <td><p id="content_proton_resource_usage_disk">content.proton.resource_usage.disk</p></td> - <td>fraction</td> - <td>average</td> - <td>The relative amount of disk used by this content node (transient usage not included, value in the range [0, 1]). Same value as reported to the cluster controller</td> - </tr> - <tr> - <td><p id="content_proton_resource_usage_memory">content.proton.resource_usage.memory</p></td> - <td>fraction</td> - <td>average</td> - <td>The relative amount of memory used by this content node (transient usage not included, value in the range [0, 1]). Same value as reported to the cluster controller</td> - </tr> - <tr> - <td><p id="content_proton_resource_usage_feeding_blocked">content.proton.resource_usage.feeding_blocked</p></td> - <td>binary</td> - <td>last, max</td> - <td>Whether feeding is blocked due to resource limits being reached (value is either 0 or 1)</td> - </tr> - <tr> - <td><p id="content_proton_transactionlog_disk_usage">content.proton.transactionlog.disk_usage</p></td> - <td>byte</td> - <td>last</td> - <td>The disk usage (in bytes) of the transaction log</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_matching_docs_matched">content.proton.documentdb.matching.docs_matched</p></td> - <td>document</td> - <td>rate</td> - <td>Number of documents matched</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_matching_docs_reranked">content.proton.documentdb.matching.docs_reranked</p></td> - <td>document</td> - <td>rate</td> - <td>Number of documents re-ranked (second phase)</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_matching_rank_profile_query_latency">content.proton.documentdb.matching.rank_profile.query_latency</p></td> - <td>second</td> - <td>average, count, max, sum</td> - <td>Total average latency (sec) when matching and ranking a query</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_matching_rank_profile_query_setup_time">content.proton.documentdb.matching.rank_profile.query_setup_time</p></td> - <td>second</td> - <td>average, count, max, sum</td> - <td>Average time (sec) spent setting up and tearing down queries</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_matching_rank_profile_rerank_time">content.proton.documentdb.matching.rank_profile.rerank_time</p></td> - <td>second</td> - <td>average, count, max, sum</td> - <td>Average time (sec) spent on 2nd phase ranking</td> - </tr> - </tbody> -</table> - -<h2 id="sentinel-metrics">Sentinel Metrics</h2> -<table class="table"> - <thead> - <tr><th>Name</th><th>Unit</th><th>Suffixes</th><th>Description</th></tr> - </thead> - <tbody> - <tr> - <td><p id="sentinel_totalRestarts">sentinel.totalRestarts</p></td> - <td>restart</td> - <td>last, max, sum</td> - <td>Total number of service restarts done by the sentinel since the sentinel was started</td> - </tr> - </tbody> -</table> - -<h2 id="storage-metrics">Storage Metrics</h2> -<table class="table"> - <thead> - <tr><th>Name</th><th>Unit</th><th>Suffixes</th><th>Description</th></tr> - </thead> - <tbody> - <tr> - <td><p id="vds_filestor_allthreads_put_count">vds.filestor.allthreads.put.count</p></td> - <td>operation</td> - <td>rate</td> - <td>Number of requests processed.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_remove_count">vds.filestor.allthreads.remove.count</p></td> - <td>operation</td> - <td>rate</td> - <td>Number of requests processed.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_update_count">vds.filestor.allthreads.update.count</p></td> - <td>request</td> - <td>rate</td> - <td>Number of requests processed.</td> - </tr> - </tbody> -</table> diff --git a/en/reference/logd-metrics-reference.html b/en/reference/logd-metrics-reference.html deleted file mode 100644 index 3350dfb4e1..0000000000 --- a/en/reference/logd-metrics-reference.html +++ /dev/null @@ -1,17 +0,0 @@ ---- -# Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -title: "Logd Metrics" ---- - -<table class="table"> - <thead> - <tr><th>Name</th><th>Unit</th><th>Description</th></tr> - </thead> - <tbody> - <tr> - <td><p id="logd_processed_lines">logd.processed.lines</p></td> - <td>item</td> - <td>Number of log lines processed</td> - </tr> - </tbody> -</table> diff --git a/en/reference/nodeadmin-metrics-reference.html b/en/reference/nodeadmin-metrics-reference.html deleted file mode 100644 index f8b1f4b532..0000000000 --- a/en/reference/nodeadmin-metrics-reference.html +++ /dev/null @@ -1,22 +0,0 @@ ---- -# Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -title: "NodeAdmin Metrics" ---- - -<table class="table"> - <thead> - <tr><th>Name</th><th>Unit</th><th>Description</th></tr> - </thead> - <tbody> - <tr> - <td><p id="endpoint_certificate_expiry_seconds">endpoint.certificate.expiry.seconds</p></td> - <td>second</td> - <td>Time until node endpoint certificate expires</td> - </tr> - <tr> - <td><p id="node-certificate_expiry_seconds">node-certificate.expiry.seconds</p></td> - <td>second</td> - <td>Time until node certificate expires</td> - </tr> - </tbody> -</table> diff --git a/en/reference/operations/health-checks.html b/en/reference/operations/health-checks.html deleted file mode 100644 index 5848b484aa..0000000000 --- a/en/reference/operations/health-checks.html +++ /dev/null @@ -1,73 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Health checks reference" -redirect_from: -- /en/reference/healthchecks.html ---- - -<p> - This is the reference for loadbalancer healthchecks to <a href="../../applications/containers.html">containers</a>. -</p><p> - By default, a container configures an instance of - <a href="https://github.com/vespa-engine/vespa/blob/master/container-disc/src/main/java/com/yahoo/container/handler/VipStatusHandler.java"> - VipStatusHandler</a> to serve <code>/status.html</code>. - This will respond with status code 200 and text <em>OK</em> if content clusters are UP. - See <a href="https://github.com/vespa-engine/vespa/blob/master/container-disc/src/main/java/com/yahoo/container/handler/VipStatus.java"> - VipStatus.java</a> for details. -</p><p> - Applications with multiple content clusters should implement custom handlers for healthchecks, - if the built-in logic is inadequate for the usage. - Also refer to <a href="../../querying/federation.html">federation</a> for how to manage data sources. -</p> - - - -<h2 id="override-using-a-status-file">Override using a status file</h2> -<p> - Use <code>container.core.vip-status</code> to make <code>VipStatusHandler</code> use a file for health status: -</p> -<pre> -<container> - <config name="container.core.vip-status"> - <accessdisk>true</accessdisk> - <statusfile>/full-path-to/status-response.html</statusfile> - </config> -</pre> -<p> - If the file exists, its contents will be served on <code>/status.html</code>, - otherwise an error message will be generated. - To remove a container from service, delete or rename the file to serve. -</p> - - - -<h2 id="alternative-multiple-paths">Alternative / multiple paths</h2> -<p> - <code>VipStatusHandler</code> only looks at a single file path by default. - As it is independent of the URI path, - it is possible to configure multiple handler instances to serve alternative or custom messages - example: -</p> -<pre> -<handler id="vipFreshness" class="com.yahoo.container.handler.VipStatusHandler"> - <binding>http://*:*/docproc/freshness-data.xml</binding> - <config name="container.core.vip-status"> - <accessdisk>true</accessdisk> - <statusfile>/full-path-to/freshness-data.xml</statusfile> - </config> -</handler> -<handler id="vipClustering" class="com.yahoo.container.handler.VipStatusHandler"> - <binding>http://*:*/docproc/ClusteringDocproc.status</binding> - <config name="container.core.vip-status"> - <accessdisk>true</accessdisk> - <statusfile>/full-path-to/ClusteringDocproc.status</statusfile> - </config> -</handler> -</pre> -<p> - The paths <code>/docproc/freshness-data.xml</code> and - <code>/docproc/ClusteringDocproc.status</code> serves the files located at - <code>/full-path-to/freshness-data.xml</code> and - <code>/full-path-to/ClusteringDocproc.status</code>, respectively. - As the handler instances are independent, - a container can be taken out of one type of rotation without affecting another. -</p> diff --git a/en/reference/operations/health-checks.mdx b/en/reference/operations/health-checks.mdx new file mode 100644 index 0000000000..4df3cdd5c1 --- /dev/null +++ b/en/reference/operations/health-checks.mdx @@ -0,0 +1,47 @@ +--- +title: "Health checks reference" +sidebarTitle: "Health checks" +--- + +This is the reference for loadbalancer healthchecks to [containers](/en/applications/containers). + +By default, a container configures an instance of [VipStatusHandler](https://github.com/vespa-engine/vespa/blob/master/container-disc/src/main/java/com/yahoo/container/handler/VipStatusHandler.java) to serve `/status.html`. This will respond with status code 200 and text *OK* if content clusters are UP. See [VipStatus.java](https://github.com/vespa-engine/vespa/blob/master/container-disc/src/main/java/com/yahoo/container/handler/VipStatus.java) for details. + +Applications with multiple content clusters should implement custom handlers for healthchecks, if the built-in logic is inadequate for the usage. Also refer to [federation](/en/querying/federation) for how to manage data sources. + +## Override using a status file + +Use `container.core.vip-status` to make `VipStatusHandler` use a file for health status: + +```xml +<container> + <config name="container.core.vip-status"> + <accessdisk>true</accessdisk> + <statusfile>/full-path-to/status-response.html</statusfile> + </config> +``` + +If the file exists, its contents will be served on `/status.html`, otherwise an error message will be generated. To remove a container from service, delete or rename the file to serve. + +## Alternative / multiple paths + +`VipStatusHandler` only looks at a single file path by default. As it is independent of the URI path, it is possible to configure multiple handler instances to serve alternative or custom messages - example: + +```xml +<handler id="vipFreshness" class="com.yahoo.container.handler.VipStatusHandler"> + <binding>http://*:*/docproc/freshness-data.xml</binding> + <config name="container.core.vip-status"> + <accessdisk>true</accessdisk> + <statusfile>/full-path-to/freshness-data.xml</statusfile> + </config> +</handler> +<handler id="vipClustering" class="com.yahoo.container.handler.VipStatusHandler"> + <binding>http://*:*/docproc/ClusteringDocproc.status</binding> + <config name="container.core.vip-status"> + <accessdisk>true</accessdisk> + <statusfile>/full-path-to/ClusteringDocproc.status</statusfile> + </config> +</handler> +``` + +The paths `/docproc/freshness-data.xml` and `/docproc/ClusteringDocproc.status` serves the files located at `/full-path-to/freshness-data.xml` and `/full-path-to/ClusteringDocproc.status`, respectively. As the handler instances are independent, a container can be taken out of one type of rotation without affecting another. \ No newline at end of file diff --git a/en/reference/operations/log-files.html b/en/reference/operations/log-files.html deleted file mode 100644 index b1fcbc7e2b..0000000000 --- a/en/reference/operations/log-files.html +++ /dev/null @@ -1,351 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Log file reference" -redirect_from: -- /en/reference/logs.html ---- - -<p> - All Vespa components use a common log module for logging. - These log messages are added to a local log file in <em>$VESPA_HOME/logs/vespa/</em> and filtered, - then forwarded, to the log server on the administration node. - The log archive and rotation is explained in <a href="#log-server">log server</a>. -</p> -{% include note.html content=' -If Vespa is running in a local container (named "vespa"), run <code>docker exec vespa vespa-logfmt</code> -to quickly dump logs.'%} - - - -<h2 id="log-file-fields">Log file fields</h2> -<p> - Log files are in a machine-readable log format, made more human-readable by - <a href="self-managed/tools.html#vespa-logfmt">vespa-logfmt</a> - - it can filter out log messages from specific programs, only show certain log levels, - print the time in a more directly understandable format and so on. - Each line that is logged consists of the following fields, in order, separated by a TAB (ASCII 9) character: -</p> -<pre> -time host pid service component level message -</pre> -<table class="table"> - <thead> - <tr><th>Log field</th><th>Description</th></tr> - </thead><tbody> - <tr><td><em>time</em></td><td> - Time in seconds since 1970-01-01 UTC, with optional fractional seconds after. E.g. 1102675319.726342 - </td></tr> - <tr><td><em>host</em></td><td> - The hostname of the machine that produced this log entry - </td></tr> - <tr><td><em>pid</em></td><td> - The process id, and an optional thread-id of the process/thread that logged the message - </td></tr> - <tr><td><em>service</em></td><td> - The Vespa service name of the logger - </td></tr> - <tr><td><em>component</em></td><td> - The component name that logged. - An application may have multiple subcomponents with their own component names, - usually starts with the name of the binary - </td></tr> - <tr><td><em>level</em></td><td> - One of fatal, error, warning, info, config, event, debug, or spam - </td></tr> - <tr><td><em>message</em></td><td> - The log message itself. All dangerous characters are escaped - (CR, NL, TAB, \, ASCII < 32 and ASCII 128..159) - </td></tr> - </tbody> -</table> -<table class="table"> - <thead> - <tr><th>Log level</th><th>Description</th></tr> - </thead><tbody> - <tr><td><em>fatal</em></td><td> - Fatal error messages. The application must exit immediately, - and restarting it will not help - </td></tr> - <tr><td><em>error</em></td><td> - Error messages. These are serious, the application cannot function correctly - </td></tr> - <tr><td><em>warning</em></td><td> - Warnings - the application may be able to continue, but the situation should be looked into - </td></tr> - <tr><td><em>info</em></td><td> - Informational messages that are not reporting error conditions, - but should still be useful to the operator - </td></tr> - <tr><td><em>config</em></td><td> - Configuration settings - </td></tr> - <tr><td><em>event</em></td><td> - <a href="#log-events">Machine-readable events</a>. - May contain information about processes starting and stopping, and various metrics - </td></tr> - <tr><td><em>debug</em></td><td> - Debug messages - normally suppressed - </td></tr> - <tr><td><em>spam</em></td><td> - Low-level debug messages, normally suppressed. - Generates massive amounts of logs when enabled - </td></tr> - </tbody> -</table> - - -<h2 id="controlling-log-levels">Controlling log levels</h2> -<p> -Use <a href="/en/reference/operations/self-managed/tools.html#vespa-logctl">vespa-logctl</a> -to change active log levels of Vespa programs running as services, run-time. -When running in the cloud, tuning can be done in services.xml - see -<a href="../applications/services/admin.html#logging">admin element (logging)</a>. -Standalone programs will instead read the environment variable <em>VESPA_LOG_LEVEL</em> -on startup to determine which log levels should be active. -The default setting for <em>VESPA_LOG_LEVEL</em> is <em>"all -debug -spam"</em>, -which enables all log levels except debug and spam. -</p><p> -<em>vespa-logctl</em> shows or modifies the active log levels for a program, -or for parts of a program, while it is running. -This is useful for enabling debug output in parts of a live system for diagnosing problems. -It can also be used to silence programs that logs too verbose. -</p><p> -Programs that can be controlled by <em>vespa-logctl</em> put log control files in -<em>$VESPA_HOME/var/db/vespa/logcontrol/service.logcontrol</em>. -If this file exists on program startup, it will be used to set the logging levels. -This means that log level modifications done with <em>vespa-logctl</em> are sticky, -and can also be performed even if the program is not running. -</p> - - - -<h2 id="log-events">Log events</h2> -<p> -Event messages are log messages of the <em>event</em> type. -Events contain a well-defined payload which makes them suitable -for automated processing of various kinds, like alerting. -An event is emitted by a component when something of interest happens to it, -or when it has some metric data it wants to share with the world. -As all other log messages, events are collected to the admin nodes by the logserver component, -where they may be found in the Vespa log or intercepted programmatically by a logserver plugin. -</p><p> -Metrics are used to report on internal variables detailing the -processing performed by a particular component. -<em>VALUES</em> are numbers with momentarily significance, -such as queue lengths and latencies. -<em>COUNTER</em> are numbers increasing monotonically with each processing step, -such as number of documents processes, or number of queries. -Refer to the <a href="../../operations/metrics.html">metrics API</a>. -</p><p> -Each event has an event <em>type</em>, a <em>version</em> and an optional <em>payload</em>. -In the log format, event types are expressed as a single word, versions as a simple integer, -and the payload as a set of <em>key=value</em> pairs. -The event payload is backslash-quoted just like log messages are in general. -This means that events may be double-quoted during transport. -Double-quote delimiters are not supported. -</p> -<table class="table"> - <thead> - <tr> - <th>Event</th> - <th>Description</th> - </tr> - </thead> - <tbody> - <tr> - <th>starting</th> - <td> - Payload: <em>name=<name></em><br/> - This event is sent by processes when they are about to start another - process. Typical for, but not limited to, shell scripts. This event - is not required to track processes, but is useful in cases where a - sub-process may fail during startup. Example: - <pre>starting container for default/container.0</pre> - </td> - </tr> - <tr> - <th>started</th> - <td> - Payload: <em>name=<name></em><br/> - The <em>started</em> event is sent by a service that just started up. Example: - <pre>started/1 name="vespa-proton"</pre> - </td> - </tr> - <tr> - <th>stopping</th> - <td> - Payload: <em>name=<name> why=<why></em><br/> - The <em>stopping</em> event is sent by a process that is about to exit. Example: - <pre>stopping/1 name="vespa-proton" why="clean shutdown"</pre> - </td> - </tr> - <tr> - <th>stopped</th> - <td> - Payload: <em>name=<name> pid=<pid> exitcode=<exitcode></em><br/> - This event is sent by a process monitoring when a sub-process exits. Example: - <pre>stopped/1 name="vespa-proton" pid=14523 exitcode=0</pre> - </td> - </tr> - <tr> - <th>crash</th> - <td> - Payload: <em>name=<name> pit=<pid> signal=<signal></em><br/> - Submitted by a process monitoring a sub-process when the sub-process - crashes (dumps core etc.). Example: - <pre>crash/1 name="vespa-proton" pid=12345 signal=11</pre> - </td> - </tr> - <tr> - <th>count</th> - <td> - Payload: <em>name=<name> value=<value></em><br/> - General event for counts - for tracking any type of counter metric. - The <em>name</em> is specific to each library/application. Counters are assumed - to increase with time, counting the number of events since the program was started. Example: - <pre>count/1 name="queries" value=10</pre> - </td> - </tr> - <tr> - <th>value</th> - <td> - Payload: <em>name=<name> value=<value></em><br/> - General event for values - for tracking any type of value metric. - <em>Value is for values that cannot be counts</em>. - Typical values are queue lengths, transaction frequencies and so on. Example: - <pre>value/1 name="peak_qps" value=200</pre> - </td> - </tr> - <tr> - <th>state</th> - <td> - Payload: <em>name=<name> value=<value></em><br/> - General event for components in a process. - <em>value</em> contains a string with more detailed information on what has happened. - Note that the format and content of such strings can change between releases. Example: - <pre>state/1 name="transactionlog.replay.start" value="{"domain":"test","serialnum":{"first":1,"last":1000}}"</pre> - </td> - </tr> - </tbody> -</table> - - - -<h2 id="logd">Logd</h2> -<p> -A small program named <em>logd</em> is responsible for rotating the -<code>vespa.log</code> file and also forwarding most log messages -(see next section for details) to the log server. -The log file is rotated after 24 hours, or if it grows too large. -Rotated logs are removed by logd after 30 days, or if the total size -grows above 1000 MB. -</p> - - - -<h2 id="log-server">Log server</h2> -<p> -On the log server on the administration node, the <em>Archiver</em> -plugin will write the log messages from each -node to a log archive. These messages are written to the -log file based on the message timestamp. The log files are located -in the <code>$VESPA_HOME/logs/vespa/logarchive</code> directory. -The catalog structure is like: -</p> -<pre> -logarchive/<year>/<month>/<day>/<hour>-<serial> -</pre> -<p>For instance will a message logged at 2016-07-22 08:05:00 be found in:</p> -<pre> -logarchive/2016/07/22/08-0 -</pre> -<p> -All dates and times are in UTC. If the log file exceeds 20 Mb, -the file will be rotated and the serial number will increase. -Rotated log files more than <em>two hours</em> old, will be compressed to -save disk space. Archived log files in the log archive will be deleted for two reasons: -</p> -<ul> - <li>Log file is more than 30 days old</li> - <li>The full size of the log archive exceeds 30GB</li> -</ul> -{% include note.html content="If you need to remove log files more aggressively than this to e.g. prevent -running out of storage space, you need to add a way of purging log files no longer needed yourself."%} -<p> -Events and log messages with level <em>debug</em> and <em>spam</em> are normally filtered out -before sending to the log archive. -As an example, to forward events and <em>debug</em> log messages, add this to <em>services.xml</em>: -</p> -<pre> -<services> - <config name="cloud.config.log.logd"> - <loglevel> - <event> - <forward>true</forward> - </event> - <debug> - <forward>true</forward> - </debug> - </loglevel> - </config> -</pre> - - - -<h2 id="access-log-file-content">Access log file content</h2> -<p> -The Container logs each request in its access log. -The log files are found in <em>$VESPA_HOME/logs/vespa/access/</em>. See -<a href="../../operations/access-logging.html">access logging</a> for details. -</p> - - -<h3 id="time-values-in-the-access-log-compared-to-metrics-and-log-events"> -Time values in the access log compared to metrics and log events</h3> -<p> -The timing in the access log will in general be slightly off compared -to the timing values in vespa.log. -The reason is the "probes" into the system are placed at slightly different levels of abstraction. -The explanations here are directed at experienced users and troubleshooting. -</p> - -<h4 id="definition-of-processing-time-in-the-access-log">Definition of processing time in the access log</h4> -<p> -Processing time in the access log starts when the execution is first -invoked from the search handler. The end is dependent on whether -the response is asynchronous or not. For a synchronous response, the -end is after the renderer has been invoked, but before the rendering -buffer is flushed. For an asynchronous response, e.g. a normal -search response, the end is defined as when the completion handler -is created. That means after control flow has returned from the -search chains, but before any network traffic or rendering has been done. -</p> - -<h4 id="definition-of-processing-time-in-the-vespa.log">Definition of processing time in the vespa.log</h4> -<p> -StatisticsSearcher defines the metric <em>query_latency</em> and the log event -<em>mean_query_latency</em>. The data fed into both is the same. -The start of the interval is defined as when the control flow enters -StatisticsSearcher, the end as when the next searcher after -StatisticsSearcher returns from search(). This has the side -effect of <em>not</em> including fill time if the result was not already -filled when passed on from StatisticsSearcher. This may happen if the -SearchHandler has to invoke fill() itself, e.g. if no searchers need to access hit fields. -</p> - -<h4 id="timing-summary">Timing summary</h4> -<p> -The access log includes everything happening before rendering, but -will exclude expensive rendering logic and slow networks. The query -latency event and metrics only covers what happens inside the search -chain where StatisticsSearcher is placed, and may exclude summary fetching. -</p> - - -<h3 id="zookeeper-log">ZooKeeper Log</h3> -<p> -The ZooKeeper log file is normally not necessary to monitor on a regular basis, -but is mentioned here as a possible source of information in case you -should ever need to debug the Vespa configuration system. -It is located at <code>$VESPA_HOME/logs/vespa/zookeeper.<servicename>.log</code>. -</p> diff --git a/en/reference/operations/log-files.mdx b/en/reference/operations/log-files.mdx new file mode 100644 index 0000000000..94ee1e8e11 --- /dev/null +++ b/en/reference/operations/log-files.mdx @@ -0,0 +1,230 @@ +--- +title: "Log file reference" +sidebarTitle: "Log files" +--- + +All Vespa components use a common log module for logging. These log messages are added to a local log file in *`$VESPA_HOME/logs/vespa/`* and filtered, then forwarded, to the log server on the administration node. The log archive and rotation is explained in [log server](#log-server). + +<Note> +**Note:** + +If Vespa is running in a local container (named "vespa"), run `docker exec vespa vespa-logfmt` to quickly dump logs. +</Note> + +## Log file fields + +Log files are in a machine-readable log format, made more human-readable by [vespa-logfmt](/en/reference/operations/self-managed/tools#vespa-logfmt) - it can filter out log messages from specific programs, only show certain log levels, print the time in a more directly understandable format and so on. Each line that is logged consists of the following fields, in order, separated by a TAB (ASCII 9) character: + +```bash +time host pid service component level message +``` + +<table className="vespa-table"> + <thead> + <tr> + <th>Log field</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>*time*</td> + <td>Time in seconds since 1970-01-01 UTC, with optional fractional seconds after. E.g. 1102675319.726342</td> + </tr> + <tr> + <td>*host*</td> + <td>The hostname of the machine that produced this log entry</td> + </tr> + <tr> + <td>*pid*</td> + <td>The process id, and an optional thread-id of the process/thread that logged the message</td> + </tr> + <tr> + <td>*service*</td> + <td>The Vespa service name of the logger</td> + </tr> + <tr> + <td>*component*</td> + <td>The component name that logged. An application may have multiple subcomponents with their own component names, usually starts with the name of the binary</td> + </tr> + <tr> + <td>*level*</td> + <td>One of fatal, error, warning, info, config, event, debug, or spam</td> + </tr> + <tr> + <td>*message*</td> + <td>The log message itself. All dangerous characters are escaped (CR, NL, TAB, \, ASCII < 32 and ASCII 128..159)</td> + </tr> + </tbody> +</table> + +<table className="vespa-table"> + <thead> + <tr> + <th>Log level</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>*fatal*</td> + <td>Fatal error messages. The application must exit immediately, and restarting it will not help</td> + </tr> + <tr> + <td>*error*</td> + <td>Error messages. These are serious, the application cannot function correctly</td> + </tr> + <tr> + <td>*warning*</td> + <td>Warnings - the application may be able to continue, but the situation should be looked into</td> + </tr> + <tr> + <td>*info*</td> + <td>Informational messages that are not reporting error conditions, but should still be useful to the operator</td> + </tr> + <tr> + <td>*config*</td> + <td>Configuration settings</td> + </tr> + <tr> + <td>*event*</td> + <td><a href="#log-events" aria-label="Machine-readable events">Machine-readable events</a>. May contain information about processes starting and stopping, and various metrics</td> + </tr> + <tr> + <td>*debug*</td> + <td>Debug messages - normally suppressed</td> + </tr> + <tr> + <td>*spam*</td> + <td>Low-level debug messages, normally suppressed. Generates massive amounts of logs when enabled</td> + </tr> + </tbody> +</table> + +## Controlling log levels + +Use [vespa-logctl](/en/reference/operations/self-managed/tools#vespa-logctl) to change active log levels of Vespa programs running as services, run-time. When running in the cloud, tuning can be done in services.xml - see [admin element (logging)](/en/reference/applications/services/admin#logging). Standalone programs will instead read the environment variable *VESPA\_LOG\_LEVEL* on startup to determine which log levels should be active. The default setting for *VESPA\_LOG\_LEVEL* is *"all -debug -spam"*, which enables all log levels except debug and spam. + +*vespa-logctl* shows or modifies the active log levels for a program, or for parts of a program, while it is running. This is useful for enabling debug output in parts of a live system for diagnosing problems. It can also be used to silence programs that logs too verbose. + +Programs that can be controlled by *vespa-logctl* put log control files in *`$VESPA_HOME/var/db/vespa/logcontrol/service.logcontrol`*. If this file exists on program startup, it will be used to set the logging levels. This means that log level modifications done with *vespa-logctl* are sticky, and can also be performed even if the program is not running. + +## Log events + +Event messages are log messages of the *event* type. Events contain a well-defined payload which makes them suitable for automated processing of various kinds, like alerting. An event is emitted by a component when something of interest happens to it, or when it has some metric data it wants to share with the world. As all other log messages, events are collected to the admin nodes by the logserver component, where they may be found in the Vespa log or intercepted programmatically by a logserver plugin. + +Metrics are used to report on internal variables detailing the processing performed by a particular component. *VALUES* are numbers with momentarily significance, such as queue lengths and latencies. *COUNTER* are numbers increasing monotonically with each processing step, such as number of documents processes, or number of queries. Refer to the [metrics API](/en/operations/metrics). + +Each event has an event *type*, a *version* and an optional *payload*. In the log format, event types are expressed as a single word, versions as a simple integer, and the payload as a set of *key=value* pairs. The event payload is backslash-quoted just like log messages are in general. This means that events may be double-quoted during transport. Double-quote delimiters are not supported. + +<table className="vespa-table"> + <thead> + <tr> + <th>Event</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>starting</td> + <td>Payload: *<code>{`name=<name>`}</code>*<br/><br/> This event is sent by processes when they are about to start another process. Typical for, but not limited to, shell scripts. This event is not required to track processes, but is useful in cases where a sub-process may fail during startup. Example:<br/><br/><code>{`starting container for default/container.0`}</code></td> + </tr> + <tr> + <td>started</td> + <td>Payload: *<code>{`name=<name>`}</code>* <br/><br/> The *started* event is sent by a service that just started up. Example:<br/><br/><code>{`started/1 name="vespa-proton"`}</code></td> + </tr> + <tr> + <td>stopping</td> + <td>Payload: *<code>{`name=<name> why=<why>`}</code>*<br/><br/> The *stopping* event is sent by a process that is about to exit. Example:<br/><br/><code>{`stopping/1 name="vespa-proton" why="clean shutdown"`}</code></td> + </tr> + <tr> + <td>stopped</td> + <td>Payload: *<code>{`name=<name> pid=<pid> exitcode=<exitcode>`}</code>*<br/><br/> This event is sent by a process monitoring when a sub-process exits. Example:<br/><br/><code>{`stopped/1 name="vespa-proton" pid=14523 exitcode=0`}</code></td> + </tr> + <tr> + <td>crash</td> + <td>Payload: *<code>{`name=<name> pit=<pid> signal=<signal>`}</code>* <br/><br/> Submitted by a process monitoring a sub-process when the sub-process crashes (dumps core etc.). Example:<br/><br/><code>{`crash/1 name="vespa-proton" pid=12345 signal=11`}</code></td> + </tr> + <tr> + <td>count</td> + <td>Payload: *<code>{`name=<name> value=<value>`}</code>* <br/><br/> General event for counts - for tracking any type of counter metric. The *name* is specific to each library/application. Counters are assumed to increase with time, counting the number of events since the program was started. Example:<br/><br/><code>{`count/1 name="queries" value=10`}</code></td> + </tr> + <tr> + <td>value</td> + <td>Payload: *<code>{`name=<name> value=<value>`}</code>* <br/><br/> General event for values - for tracking any type of value metric. *Value is for values that cannot be counts*. Typical values are queue lengths, transaction frequencies and so on. Example:<br/><br/><code>{`value/1 name="peak_qps" value=200`}</code></td> + </tr> + <tr> + <td>state</td> + <td>Payload: *<code>{`name=<name> value=<value>`}</code>*<br/><br/> General event for components in a process. *value* contains a string with more detailed information on what has happened. Note that the format and content of such strings can change between releases. Example:<br/><br/><code>{`state/1 name="transactionlog.replay.start" value="{"domain":"test","serialnum":{"first":1,"last":1000}}"`}</code></td> + </tr> + </tbody> +</table> + +## Logd + +A small program named *logd* is responsible for rotating the `vespa.log` file and also forwarding most log messages (see next section for details) to the log server. The log file is rotated after 24 hours, or if it grows too large. Rotated logs are removed by logd after 30 days, or if the total size grows above 1000 MB. + +## Log server + +On the log server on the administration node, the *Archiver* plugin will write the log messages from each node to a log archive. These messages are written to the log file based on the message timestamp. The log files are located in the `$VESPA_HOME/logs/vespa/logarchive` directory. The catalog structure is like: + +```bash +logarchive/<year>/<month>/<day>/<hour>-<serial> +``` + +For instance will a message logged at 2016-07-22 08:05:00 be found in: + +```bash +logarchive/2016/07/22/08-0 +``` + +All dates and times are in UTC. If the log file exceeds 20 Mb, the file will be rotated and the serial number will increase. Rotated log files more than *two hours* old, will be compressed to save disk space. Archived log files in the log archive will be deleted for two reasons: + +- Log file is more than 30 days old +- The full size of the log archive exceeds 30GB + +<Note> +**Note:** + +If you need to remove log files more aggressively than this to e.g. prevent running out of storage space, you need to add a way of purging log files no longer needed yourself. +</Note> + +Events and log messages with level *debug* and *spam* are normally filtered out before sending to the log archive. As an example, to forward events and *debug* log messages, add this to *services.xml*: + +```xml +<services> + <config name="cloud.config.log.logd"> + <loglevel> + <event> + <forward>true</forward> + </event> + <debug> + <forward>true</forward> + </debug> + </loglevel> + </config> +``` + +## Access log file content + +The Container logs each request in its access log. The log files are found in *`$VESPA_HOME/logs/vespa/access/`*. See [access logging](/en/operations/access-logging) for details. + +### Time values in the access log compared to metrics and log events + +The timing in the access log will in general be slightly off compared to the timing values in vespa.log. The reason is the "probes" into the system are placed at slightly different levels of abstraction. The explanations here are directed at experienced users and troubleshooting. + +#### Definition of processing time in the access log + +Processing time in the access log starts when the execution is first invoked from the search handler. The end is dependent on whether the response is asynchronous or not. For a synchronous response, the end is after the renderer has been invoked, but before the rendering buffer is flushed. For an asynchronous response, e.g. a normal search response, the end is defined as when the completion handler is created. That means after control flow has returned from the search chains, but before any network traffic or rendering has been done. + +#### Definition of processing time in the vespa.log + +StatisticsSearcher defines the metric *query\_latency* and the log event *mean\_query\_latency*. The data fed into both is the same. The start of the interval is defined as when the control flow enters StatisticsSearcher, the end as when the next searcher after StatisticsSearcher returns from search(). This has the side effect of *not* including fill time if the result was not already filled when passed on from StatisticsSearcher. This may happen if the SearchHandler has to invoke fill() itself, e.g. if no searchers need to access hit fields. + +#### Timing summary + +The access log includes everything happening before rendering, but will exclude expensive rendering logic and slow networks. The query latency event and metrics only covers what happens inside the search chain where StatisticsSearcher is placed, and may exclude summary fetching. + +### ZooKeeper Log + +The ZooKeeper log file is normally not necessary to monitor on a regular basis, but is mentioned here as a possible source of information in case you should ever need to debug the Vespa configuration system. It is located at `$VESPA_HOME/logs/vespa/zookeeper.<servicename>.log`. \ No newline at end of file diff --git a/en/reference/operations/metrics/clustercontroller.html b/en/reference/operations/metrics/clustercontroller.html deleted file mode 100644 index 601c10f68a..0000000000 --- a/en/reference/operations/metrics/clustercontroller.html +++ /dev/null @@ -1,129 +0,0 @@ ---- -# Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -title: "ClusterController Metrics" -redirect_from: -- en/reference/clustercontroller-metrics-reference.html ---- - -<table class="table"> - <thead> - <tr><th>Name</th><th>Unit</th><th>Description</th></tr> - </thead> - <tbody> - <tr> - <td><p id="cluster-controller_down_count">cluster-controller.down.count</p></td> - <td>node</td> - <td>Number of content nodes down</td> - </tr> - <tr> - <td><p id="cluster-controller_initializing_count">cluster-controller.initializing.count</p></td> - <td>node</td> - <td>Number of content nodes initializing</td> - </tr> - <tr> - <td><p id="cluster-controller_maintenance_count">cluster-controller.maintenance.count</p></td> - <td>node</td> - <td>Number of content nodes in maintenance</td> - </tr> - <tr> - <td><p id="cluster-controller_retired_count">cluster-controller.retired.count</p></td> - <td>node</td> - <td>Number of content nodes that are retired</td> - </tr> - <tr> - <td><p id="cluster-controller_stopping_count">cluster-controller.stopping.count</p></td> - <td>node</td> - <td>Number of content nodes currently stopping</td> - </tr> - <tr> - <td><p id="cluster-controller_up_count">cluster-controller.up.count</p></td> - <td>node</td> - <td>Number of content nodes up</td> - </tr> - <tr> - <td><p id="cluster-controller_cluster-state-change_count">cluster-controller.cluster-state-change.count</p></td> - <td>node</td> - <td>Number of nodes changing state</td> - </tr> - <tr> - <td><p id="cluster-controller_nodes-not-converged">cluster-controller.nodes-not-converged</p></td> - <td>node</td> - <td>Number of nodes not converging to the latest cluster state version</td> - </tr> - <tr> - <td><p id="cluster-controller_stored-document-count">cluster-controller.stored-document-count</p></td> - <td>document</td> - <td>Total number of unique documents stored in the cluster</td> - </tr> - <tr> - <td><p id="cluster-controller_stored-document-bytes">cluster-controller.stored-document-bytes</p></td> - <td>byte</td> - <td>Combined byte size of all unique documents stored in the cluster (not including replication)</td> - </tr> - <tr> - <td><p id="cluster-controller_cluster-buckets-out-of-sync-ratio">cluster-controller.cluster-buckets-out-of-sync-ratio</p></td> - <td>fraction</td> - <td>Ratio of buckets in the cluster currently in need of syncing</td> - </tr> - <tr> - <td><p id="cluster-controller_busy-tick-time-ms">cluster-controller.busy-tick-time-ms</p></td> - <td>millisecond</td> - <td>Time busy</td> - </tr> - <tr> - <td><p id="cluster-controller_idle-tick-time-ms">cluster-controller.idle-tick-time-ms</p></td> - <td>millisecond</td> - <td>Time idle</td> - </tr> - <tr> - <td><p id="cluster-controller_work-ms">cluster-controller.work-ms</p></td> - <td>millisecond</td> - <td>Time used for actual work</td> - </tr> - <tr> - <td><p id="cluster-controller_is-master">cluster-controller.is-master</p></td> - <td>binary</td> - <td>1 if this cluster controller is currently the master, or 0 if not</td> - </tr> - <tr> - <td><p id="cluster-controller_remote-task-queue_size">cluster-controller.remote-task-queue.size</p></td> - <td>operation</td> - <td>Number of remote tasks queued</td> - </tr> - <tr> - <td><p id="cluster-controller_node-event_count">cluster-controller.node-event.count</p></td> - <td>operation</td> - <td>Number of node events</td> - </tr> - <tr> - <td><p id="cluster-controller_resource_usage_nodes_above_limit">cluster-controller.resource_usage.nodes_above_limit</p></td> - <td>node</td> - <td>The number of content nodes above resource limit, blocking feed</td> - </tr> - <tr> - <td><p id="cluster-controller_resource_usage_max_memory_utilization">cluster-controller.resource_usage.max_memory_utilization</p></td> - <td>fraction</td> - <td>Current memory utilisation, for content node with the highest value</td> - </tr> - <tr> - <td><p id="cluster-controller_resource_usage_max_disk_utilization">cluster-controller.resource_usage.max_disk_utilization</p></td> - <td>fraction</td> - <td>Current disk space utilisation, for content node with the highest value</td> - </tr> - <tr> - <td><p id="cluster-controller_resource_usage_memory_limit">cluster-controller.resource_usage.memory_limit</p></td> - <td>fraction</td> - <td>Memory space limit as a fraction of available memory</td> - </tr> - <tr> - <td><p id="cluster-controller_resource_usage_disk_limit">cluster-controller.resource_usage.disk_limit</p></td> - <td>fraction</td> - <td>Disk space limit as a fraction of available disk space</td> - </tr> - <tr> - <td><p id="reindexing_progress">reindexing.progress</p></td> - <td>fraction</td> - <td>Re-indexing progress</td> - </tr> - </tbody> -</table> diff --git a/en/reference/operations/metrics/clustercontroller.mdx b/en/reference/operations/metrics/clustercontroller.mdx new file mode 100644 index 0000000000..dcc5a5e9f3 --- /dev/null +++ b/en/reference/operations/metrics/clustercontroller.mdx @@ -0,0 +1,131 @@ +--- +title: "ClusterController Metrics" +sidebarTitle: "Cluster controller metrics" +--- + +<table className="vespa-table"> + <thead> + <tr> + <th>Name</th> + <th>Unit</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>cluster-controller.down.count</td> + <td>node</td> + <td>Number of content nodes down</td> + </tr> + <tr> + <td>cluster-controller.initializing.count</td> + <td>node</td> + <td>Number of content nodes initializing</td> + </tr> + <tr> + <td>cluster-controller.maintenance.count</td> + <td>node</td> + <td>Number of content nodes in maintenance</td> + </tr> + <tr> + <td>cluster-controller.retired.count</td> + <td>node</td> + <td>Number of content nodes that are retired</td> + </tr> + <tr> + <td>cluster-controller.stopping.count</td> + <td>node</td> + <td>Number of content nodes currently stopping</td> + </tr> + <tr> + <td>cluster-controller.up.count</td> + <td>node</td> + <td>Number of content nodes up</td> + </tr> + <tr> + <td>cluster-controller.cluster-state-change.count</td> + <td>node</td> + <td>Number of nodes changing state</td> + </tr> + <tr> + <td>cluster-controller.nodes-not-converged</td> + <td>node</td> + <td>Number of nodes not converging to the latest cluster state version</td> + </tr> + <tr> + <td>cluster-controller.stored-document-count</td> + <td>document</td> + <td>Total number of unique documents stored in the cluster</td> + </tr> + <tr> + <td>cluster-controller.stored-document-bytes</td> + <td>byte</td> + <td>Combined byte size of all unique documents stored in the cluster (not including replication)</td> + </tr> + <tr> + <td>cluster-controller.cluster-buckets-out-of-sync-ratio</td> + <td>fraction</td> + <td>Ratio of buckets in the cluster currently in need of syncing</td> + </tr> + <tr> + <td>cluster-controller.busy-tick-time-ms</td> + <td>millisecond</td> + <td>Time busy</td> + </tr> + <tr> + <td>cluster-controller.idle-tick-time-ms</td> + <td>millisecond</td> + <td>Time idle</td> + </tr> + <tr> + <td>cluster-controller.work-ms</td> + <td>millisecond</td> + <td>Time used for actual work</td> + </tr> + <tr> + <td>cluster-controller.is-master</td> + <td>binary</td> + <td>1 if this cluster controller is currently the master, or 0 if not</td> + </tr> + <tr> + <td>cluster-controller.remote-task-queue.size</td> + <td>operation</td> + <td>Number of remote tasks queued</td> + </tr> + <tr> + <td>cluster-controller.node-event.count</td> + <td>operation</td> + <td>Number of node events</td> + </tr> + <tr> + <td>cluster-controller.resource_usage.nodes_above_limit</td> + <td>node</td> + <td>The number of content nodes above resource limit, blocking feed</td> + </tr> + <tr> + <td>cluster-controller.resource_usage.max_memory_utilization</td> + <td>fraction</td> + <td>Current memory utilisation, for content node with the highest value</td> + </tr> + <tr> + <td>cluster-controller.resource_usage.max_disk_utilization</td> + <td>fraction</td> + <td>Current disk space utilisation, for content node with the highest value</td> + </tr> + <tr> + <td>cluster-controller.resource_usage.memory_limit</td> + <td>fraction</td> + <td>Memory space limit as a fraction of available memory</td> + </tr> + <tr> + <td>cluster-controller.resource_usage.disk_limit</td> + <td>fraction</td> + <td>Disk space limit as a fraction of available disk space</td> + </tr> + <tr> + <td>reindexing.progress</td> + <td>fraction</td> + <td>Re-indexing progress</td> + </tr> + </tbody> +</table> \ No newline at end of file diff --git a/en/reference/operations/metrics/configserver.html b/en/reference/operations/metrics/configserver.html deleted file mode 100644 index b98630bd5e..0000000000 --- a/en/reference/operations/metrics/configserver.html +++ /dev/null @@ -1,709 +0,0 @@ ---- -# Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -title: "ConfigServer Metrics" -redirect_from: -- en/reference/configserver-metrics-reference.html ---- - -<table class="table"> - <thead> - <tr><th>Name</th><th>Unit</th><th>Description</th></tr> - </thead> - <tbody> - <tr> - <td><p id="configserver_requests">configserver.requests</p></td> - <td>request</td> - <td>Number of requests processed</td> - </tr> - <tr> - <td><p id="configserver_failedRequests">configserver.failedRequests</p></td> - <td>request</td> - <td>Number of requests that failed</td> - </tr> - <tr> - <td><p id="configserver_latency">configserver.latency</p></td> - <td>millisecond</td> - <td>Time to complete requests</td> - </tr> - <tr> - <td><p id="configserver_cacheConfigElems">configserver.cacheConfigElems</p></td> - <td>item</td> - <td>Time to complete requests</td> - </tr> - <tr> - <td><p id="configserver_cacheChecksumElems">configserver.cacheChecksumElems</p></td> - <td>item</td> - <td>Number of checksum elements in the cache</td> - </tr> - <tr> - <td><p id="configserver_hosts">configserver.hosts</p></td> - <td>node</td> - <td>The number of nodes being served configuration from the config server cluster</td> - </tr> - <tr> - <td><p id="configserver_tenants">configserver.tenants</p></td> - <td>instance</td> - <td>The number of tenants being served configuration from the config server cluster</td> - </tr> - <tr> - <td><p id="configserver_applications">configserver.applications</p></td> - <td>instance</td> - <td>The number of applications being served configuration from the config server cluster</td> - </tr> - <tr> - <td><p id="configserver_delayedResponses">configserver.delayedResponses</p></td> - <td>response</td> - <td>Number of delayed responses</td> - </tr> - <tr> - <td><p id="configserver_sessionChangeErrors">configserver.sessionChangeErrors</p></td> - <td>session</td> - <td>Number of session change errors</td> - </tr> - <tr> - <td><p id="configserver_unknownHostRequests">configserver.unknownHostRequests</p></td> - <td>request</td> - <td>Config requests from unknown hosts</td> - </tr> - <tr> - <td><p id="configserver_newSessions">configserver.newSessions</p></td> - <td>session</td> - <td>New config sessions</td> - </tr> - <tr> - <td><p id="configserver_preparedSessions">configserver.preparedSessions</p></td> - <td>session</td> - <td>Prepared config sessions</td> - </tr> - <tr> - <td><p id="configserver_activeSessions">configserver.activeSessions</p></td> - <td>session</td> - <td>Active config sessions</td> - </tr> - <tr> - <td><p id="configserver_inactiveSessions">configserver.inactiveSessions</p></td> - <td>session</td> - <td>Inactive config sessions</td> - </tr> - <tr> - <td><p id="configserver_addedSessions">configserver.addedSessions</p></td> - <td>session</td> - <td>Added config sessions</td> - </tr> - <tr> - <td><p id="configserver_removedSessions">configserver.removedSessions</p></td> - <td>session</td> - <td>Removed config sessions</td> - </tr> - <tr> - <td><p id="configserver_rpcServerWorkQueueSize">configserver.rpcServerWorkQueueSize</p></td> - <td>item</td> - <td>Number of elements in the RPC server work queue</td> - </tr> - <tr> - <td><p id="maintenanceDeployment_transientFailure">maintenanceDeployment.transientFailure</p></td> - <td>operation</td> - <td>Number of maintenance deployments that failed with a transient failure</td> - </tr> - <tr> - <td><p id="maintenanceDeployment_failure">maintenanceDeployment.failure</p></td> - <td>operation</td> - <td>Number of maintenance deployments that failed with a permanent failure</td> - </tr> - <tr> - <td><p id="maintenanceDeployment_reason">maintenanceDeployment.reason</p></td> - <td>operation</td> - <td>Reason for maintenance deployment</td> - </tr> - <tr> - <td><p id="maintenance_successFactorDeviation">maintenance.successFactorDeviation</p></td> - <td>fraction</td> - <td>Configserver: Maintenance Success Factor Deviation</td> - </tr> - <tr> - <td><p id="maintenance_duration">maintenance.duration</p></td> - <td>millisecond</td> - <td>Configserver: Maintenance Duration</td> - </tr> - <tr> - <td><p id="maintenance_congestion">maintenance.congestion</p></td> - <td>failure</td> - <td>Configserver: Maintenance Congestion</td> - </tr> - <tr> - <td><p id="configserver_zkConnectionLost">configserver.zkConnectionLost</p></td> - <td>connection</td> - <td>Number of ZooKeeper connections lost</td> - </tr> - <tr> - <td><p id="configserver_zkReconnected">configserver.zkReconnected</p></td> - <td>connection</td> - <td>Number of ZooKeeper reconnections</td> - </tr> - <tr> - <td><p id="configserver_zkConnected">configserver.zkConnected</p></td> - <td>node</td> - <td>Number of ZooKeeper nodes connected</td> - </tr> - <tr> - <td><p id="configserver_zkSuspended">configserver.zkSuspended</p></td> - <td>node</td> - <td>Number of ZooKeeper nodes suspended</td> - </tr> - <tr> - <td><p id="configserver_zkZNodes">configserver.zkZNodes</p></td> - <td>node</td> - <td>Number of ZooKeeper nodes present</td> - </tr> - <tr> - <td><p id="configserver_zkAvgLatency">configserver.zkAvgLatency</p></td> - <td>millisecond</td> - <td>Average latency for ZooKeeper requests</td> - </tr> - <tr> - <td><p id="configserver_zkMaxLatency">configserver.zkMaxLatency</p></td> - <td>millisecond</td> - <td>Max latency for ZooKeeper requests</td> - </tr> - <tr> - <td><p id="configserver_zkConnections">configserver.zkConnections</p></td> - <td>connection</td> - <td>Number of ZooKeeper connections</td> - </tr> - <tr> - <td><p id="configserver_zkOutstandingRequests">configserver.zkOutstandingRequests</p></td> - <td>request</td> - <td>Number of ZooKeeper requests in flight</td> - </tr> - <tr> - <td><p id="orchestrator_lock_acquire-latency">orchestrator.lock.acquire-latency</p></td> - <td>second</td> - <td>Time to acquire zookeeper lock</td> - </tr> - <tr> - <td><p id="orchestrator_lock_acquire-success">orchestrator.lock.acquire-success</p></td> - <td>operation</td> - <td>Number of times zookeeper lock has been acquired successfully</td> - </tr> - <tr> - <td><p id="orchestrator_lock_acquire-timedout">orchestrator.lock.acquire-timedout</p></td> - <td>operation</td> - <td>Number of times zookeeper lock couldn't be acquired within timeout</td> - </tr> - <tr> - <td><p id="orchestrator_lock_acquire">orchestrator.lock.acquire</p></td> - <td>operation</td> - <td>Number of attempts to acquire zookeeper lock</td> - </tr> - <tr> - <td><p id="orchestrator_lock_acquired">orchestrator.lock.acquired</p></td> - <td>operation</td> - <td>Number of times zookeeper lock was acquired</td> - </tr> - <tr> - <td><p id="orchestrator_lock_hold-latency">orchestrator.lock.hold-latency</p></td> - <td>second</td> - <td>Time zookeeper lock was held before it was released</td> - </tr> - <tr> - <td><p id="nodes_active">nodes.active</p></td> - <td>node</td> - <td>The number of active nodes in a cluster</td> - </tr> - <tr> - <td><p id="nodes_nonActive">nodes.nonActive</p></td> - <td>node</td> - <td>The number of non-active nodes in a cluster</td> - </tr> - <tr> - <td><p id="nodes_nonActiveFraction">nodes.nonActiveFraction</p></td> - <td>node</td> - <td>The fraction of non-active nodes vs total nodes in a cluster</td> - </tr> - <tr> - <td><p id="nodes_exclusiveSwitchFraction">nodes.exclusiveSwitchFraction</p></td> - <td>fraction</td> - <td>The fraction of nodes in a cluster on exclusive network switches</td> - </tr> - <tr> - <td><p id="nodes_emptyExclusive">nodes.emptyExclusive</p></td> - <td>node</td> - <td>The number of exclusive hosts that do not have any nodes allocated to them</td> - </tr> - <tr> - <td><p id="nodes_expired_deprovisioned">nodes.expired.deprovisioned</p></td> - <td>node</td> - <td>The number of deprovisioned nodes that have expired</td> - </tr> - <tr> - <td><p id="nodes_expired_dirty">nodes.expired.dirty</p></td> - <td>node</td> - <td>The number of dirty nodes that have expired</td> - </tr> - <tr> - <td><p id="nodes_expired_inactive">nodes.expired.inactive</p></td> - <td>node</td> - <td>The number of inactive nodes that have expired</td> - </tr> - <tr> - <td><p id="nodes_expired_provisioned">nodes.expired.provisioned</p></td> - <td>node</td> - <td>The number of provisioned nodes that have expired</td> - </tr> - <tr> - <td><p id="nodes_expired_reserved">nodes.expired.reserved</p></td> - <td>node</td> - <td>The number of reserved nodes that have expired</td> - </tr> - <tr> - <td><p id="cluster_cost">cluster.cost</p></td> - <td>dollar_per_hour</td> - <td>The cost of the nodes allocated to a certain cluster, in $/hr</td> - </tr> - <tr> - <td><p id="cluster_load_ideal_cpu">cluster.load.ideal.cpu</p></td> - <td>fraction</td> - <td>The ideal cpu load of a certain cluster</td> - </tr> - <tr> - <td><p id="cluster_load_ideal_memory">cluster.load.ideal.memory</p></td> - <td>fraction</td> - <td>The ideal memory load of a certain cluster</td> - </tr> - <tr> - <td><p id="cluster_load_ideal_disk">cluster.load.ideal.disk</p></td> - <td>fraction</td> - <td>The ideal disk load of a certain cluster</td> - </tr> - <tr> - <td><p id="cluster_load_peak_cpu">cluster.load.peak.cpu</p></td> - <td>fraction</td> - <td>The peak cpu load in the period considered of a certain cluster</td> - </tr> - <tr> - <td><p id="cluster_load_peak_memory">cluster.load.peak.memory</p></td> - <td>fraction</td> - <td>The peak memory load in the period considered of a certain cluster</td> - </tr> - <tr> - <td><p id="cluster_load_peak_disk">cluster.load.peak.disk</p></td> - <td>fraction</td> - <td>The peak disk load in the period considered of a certain cluster</td> - </tr> - <tr> - <td><p id="cluster_backup_age">cluster.backup.age</p></td> - <td>fraction</td> - <td>Age of the most recent cluster backup as a fraction of the backup interval</td> - </tr> - <tr> - <td><p id="cluster_snapshot_busySeconds">cluster.snapshot.busySeconds</p></td> - <td>second</td> - <td>The maximum time a snapshot has been busy (creating or restoring) for a cluster</td> - </tr> - <tr> - <td><p id="zone_working">zone.working</p></td> - <td>binary</td> - <td>The value 1 if zone is considered healthy, 0 if not. This is decided by considering the number of non-active nodes vs the number of active nodes in a zone</td> - </tr> - <tr> - <td><p id="cache_nodeObject_hitRate">cache.nodeObject.hitRate</p></td> - <td>fraction</td> - <td>The fraction of cache hits vs cache lookups for the node object cache</td> - </tr> - <tr> - <td><p id="cache_nodeObject_evictionCount">cache.nodeObject.evictionCount</p></td> - <td>item</td> - <td>The number of cache elements evicted from the node object cache</td> - </tr> - <tr> - <td><p id="cache_nodeObject_size">cache.nodeObject.size</p></td> - <td>item</td> - <td>The number of cache elements in the node object cache</td> - </tr> - <tr> - <td><p id="cache_curator_hitRate">cache.curator.hitRate</p></td> - <td>fraction</td> - <td>The fraction of cache hits vs cache lookups for the curator cache</td> - </tr> - <tr> - <td><p id="cache_curator_evictionCount">cache.curator.evictionCount</p></td> - <td>item</td> - <td>The number of cache elements evicted from the curator cache</td> - </tr> - <tr> - <td><p id="cache_curator_size">cache.curator.size</p></td> - <td>item</td> - <td>The number of cache elements in the curator cache</td> - </tr> - <tr> - <td><p id="wantedRestartGeneration">wantedRestartGeneration</p></td> - <td>generation</td> - <td>Wanted restart generation for tenant node</td> - </tr> - <tr> - <td><p id="currentRestartGeneration">currentRestartGeneration</p></td> - <td>generation</td> - <td>Current restart generation for tenant node</td> - </tr> - <tr> - <td><p id="wantToRestart">wantToRestart</p></td> - <td>binary</td> - <td>One if node wants to restart, zero if not</td> - </tr> - <tr> - <td><p id="wantedRebootGeneration">wantedRebootGeneration</p></td> - <td>generation</td> - <td>Wanted reboot generation for tenant node</td> - </tr> - <tr> - <td><p id="currentRebootGeneration">currentRebootGeneration</p></td> - <td>generation</td> - <td>Current reboot generation for tenant node</td> - </tr> - <tr> - <td><p id="wantToReboot">wantToReboot</p></td> - <td>binary</td> - <td>One if node wants to reboot, zero if not</td> - </tr> - <tr> - <td><p id="retired">retired</p></td> - <td>binary</td> - <td>One if node is retired, zero if not</td> - </tr> - <tr> - <td><p id="wantedVespaVersion">wantedVespaVersion</p></td> - <td>version</td> - <td>Wanted vespa version for the node, in the form MINOR.PATCH. Major version is not included here</td> - </tr> - <tr> - <td><p id="currentVespaVersion">currentVespaVersion</p></td> - <td>version</td> - <td>Current vespa version for the node, in the form MINOR.PATCH. Major version is not included here</td> - </tr> - <tr> - <td><p id="wantToChangeVespaVersion">wantToChangeVespaVersion</p></td> - <td>binary</td> - <td>One if node want to change Vespa version, zero if not</td> - </tr> - <tr> - <td><p id="hasWireguardKey">hasWireguardKey</p></td> - <td>binary</td> - <td>One if node has a WireGuard key, zero if not</td> - </tr> - <tr> - <td><p id="wantToRetire">wantToRetire</p></td> - <td>binary</td> - <td>One if node wants to retire, zero if not</td> - </tr> - <tr> - <td><p id="wantToDeprovision">wantToDeprovision</p></td> - <td>binary</td> - <td>One if node wants to be deprovisioned, zero if not</td> - </tr> - <tr> - <td><p id="failReport">failReport</p></td> - <td>binary</td> - <td>One if there is a fail report for the node, zero if not</td> - </tr> - <tr> - <td><p id="suspended">suspended</p></td> - <td>binary</td> - <td>One if the node is suspended, zero if not</td> - </tr> - <tr> - <td><p id="suspendedSeconds">suspendedSeconds</p></td> - <td>second</td> - <td>The number of seconds the node has been suspended</td> - </tr> - <tr> - <td><p id="activeSeconds">activeSeconds</p></td> - <td>second</td> - <td>The number of seconds the node has been active</td> - </tr> - <tr> - <td><p id="numberOfServicesUp">numberOfServicesUp</p></td> - <td>instance</td> - <td>The number of services confirmed to be running on a node</td> - </tr> - <tr> - <td><p id="numberOfServicesNotChecked">numberOfServicesNotChecked</p></td> - <td>instance</td> - <td>The number of services supposed to run on a node, that has not checked</td> - </tr> - <tr> - <td><p id="numberOfServicesDown">numberOfServicesDown</p></td> - <td>instance</td> - <td>The number of services confirmed to not be running on a node</td> - </tr> - <tr> - <td><p id="someServicesDown">someServicesDown</p></td> - <td>binary</td> - <td>One if one or more services has been confirmed to not run on a node, zero if not</td> - </tr> - <tr> - <td><p id="numberOfServicesUnknown">numberOfServicesUnknown</p></td> - <td>instance</td> - <td>The number of services the config server does not know is running on a node</td> - </tr> - <tr> - <td><p id="nodeFailerBadNode">nodeFailerBadNode</p></td> - <td>binary</td> - <td>One if the node is failed due to being bad, zero if not</td> - </tr> - <tr> - <td><p id="downInNodeRepo">downInNodeRepo</p></td> - <td>binary</td> - <td>One if the node is registered as being down in the node repository, zero if not</td> - </tr> - <tr> - <td><p id="numberOfServices">numberOfServices</p></td> - <td>instance</td> - <td>Number of services supposed to run on a node</td> - </tr> - <tr> - <td><p id="lockAttempt_acquireMaxActiveLatency">lockAttempt.acquireMaxActiveLatency</p></td> - <td>second</td> - <td>Maximum duration for keeping a lock, ending during the metrics snapshot, or still being kept at the end or this snapshot period</td> - </tr> - <tr> - <td><p id="lockAttempt_acquireHz">lockAttempt.acquireHz</p></td> - <td>operation_per_second</td> - <td>Average number of locks acquired per second the snapshot period</td> - </tr> - <tr> - <td><p id="lockAttempt_acquireLoad">lockAttempt.acquireLoad</p></td> - <td>operation</td> - <td>Average number of locks held concurrently during the snapshot period</td> - </tr> - <tr> - <td><p id="lockAttempt_lockedLatency">lockAttempt.lockedLatency</p></td> - <td>second</td> - <td>Longest lock duration in the snapshot period</td> - </tr> - <tr> - <td><p id="lockAttempt_lockedLoad">lockAttempt.lockedLoad</p></td> - <td>operation</td> - <td>Average number of locks held concurrently during the snapshot period</td> - </tr> - <tr> - <td><p id="lockAttempt_acquireTimedOut">lockAttempt.acquireTimedOut</p></td> - <td>operation</td> - <td> Number of locking attempts that timed out during the snapshot period</td> - </tr> - <tr> - <td><p id="lockAttempt_deadlock">lockAttempt.deadlock</p></td> - <td>operation</td> - <td>Number of lock grab deadlocks detected during the snapshot period</td> - </tr> - <tr> - <td><p id="lockAttempt_errors">lockAttempt.errors</p></td> - <td>operation</td> - <td>Number of other lock related errors detected during the snapshot period</td> - </tr> - <tr> - <td><p id="hostedVespa_docker_totalCapacityCpu">hostedVespa.docker.totalCapacityCpu</p></td> - <td>vcpu</td> - <td>Total number of VCPUs on tenant hosts managed by hosted Vespa in a zone</td> - </tr> - <tr> - <td><p id="hostedVespa_docker_totalCapacityMem">hostedVespa.docker.totalCapacityMem</p></td> - <td>gigabyte</td> - <td>Total amount of memory on tenant hosts managed by hosted Vespa in a zone</td> - </tr> - <tr> - <td><p id="hostedVespa_docker_totalCapacityDisk">hostedVespa.docker.totalCapacityDisk</p></td> - <td>gigabyte</td> - <td>Total amount of disk space on tenant hosts managed by hosted Vespa in a zone</td> - </tr> - <tr> - <td><p id="hostedVespa_docker_freeCapacityCpu">hostedVespa.docker.freeCapacityCpu</p></td> - <td>vcpu</td> - <td>Total number of free VCPUs on tenant hosts managed by hosted Vespa in a zone</td> - </tr> - <tr> - <td><p id="hostedVespa_docker_freeCapacityMem">hostedVespa.docker.freeCapacityMem</p></td> - <td>gigabyte</td> - <td>Total amount of free memory on tenant hosts managed by hosted Vespa in a zone</td> - </tr> - <tr> - <td><p id="hostedVespa_docker_freeCapacityDisk">hostedVespa.docker.freeCapacityDisk</p></td> - <td>gigabyte</td> - <td>Total amount of free disk space on tenant hosts managed by hosted Vespa in a zone</td> - </tr> - <tr> - <td><p id="hostedVespa_docker_allocatedCapacityCpu">hostedVespa.docker.allocatedCapacityCpu</p></td> - <td>vcpu</td> - <td>Total number of allocated VCPUs on tenant hosts managed by hosted Vespa in a zone</td> - </tr> - <tr> - <td><p id="hostedVespa_docker_allocatedCapacityMem">hostedVespa.docker.allocatedCapacityMem</p></td> - <td>gigabyte</td> - <td>Total amount of allocated memory on tenant hosts managed by hosted Vespa in a zone</td> - </tr> - <tr> - <td><p id="hostedVespa_docker_allocatedCapacityDisk">hostedVespa.docker.allocatedCapacityDisk</p></td> - <td>gigabyte</td> - <td>Total amount of allocated disk space on tenant hosts managed by hosted Vespa in a zone</td> - </tr> - <tr> - <td><p id="hostedVespa_pendingRedeployments">hostedVespa.pendingRedeployments</p></td> - <td>task</td> - <td>The number of hosted Vespa re-deployments pending</td> - </tr> - <tr> - <td><p id="hostedVespa_docker_skew">hostedVespa.docker.skew</p></td> - <td>fraction</td> - <td>A number in the range 0..1 indicating how well allocated resources are balanced with availability on hosts</td> - </tr> - <tr> - <td><p id="hostedVespa_activeHosts">hostedVespa.activeHosts</p></td> - <td>host</td> - <td>The number of managed hosts that are in state "active"</td> - </tr> - <tr> - <td><p id="hostedVespa_breakfixedHosts">hostedVespa.breakfixedHosts</p></td> - <td>host</td> - <td>The number of managed hosts that are in state "breakfixed"</td> - </tr> - <tr> - <td><p id="hostedVespa_deprovisionedHosts">hostedVespa.deprovisionedHosts</p></td> - <td>host</td> - <td>The number of managed hosts that are in state "deprovisioned"</td> - </tr> - <tr> - <td><p id="hostedVespa_dirtyHosts">hostedVespa.dirtyHosts</p></td> - <td>host</td> - <td>The number of managed hosts that are in state "dirty"</td> - </tr> - <tr> - <td><p id="hostedVespa_failedHosts">hostedVespa.failedHosts</p></td> - <td>host</td> - <td>The number of managed hosts that are in state "failed"</td> - </tr> - <tr> - <td><p id="hostedVespa_inactiveHosts">hostedVespa.inactiveHosts</p></td> - <td>host</td> - <td>The number of managed hosts that are in state "inactive"</td> - </tr> - <tr> - <td><p id="hostedVespa_parkedHosts">hostedVespa.parkedHosts</p></td> - <td>host</td> - <td>The number of managed hosts that are in state "parked"</td> - </tr> - <tr> - <td><p id="hostedVespa_provisionedHosts">hostedVespa.provisionedHosts</p></td> - <td>host</td> - <td>The number of managed hosts that are in state "provisioned"</td> - </tr> - <tr> - <td><p id="hostedVespa_readyHosts">hostedVespa.readyHosts</p></td> - <td>host</td> - <td>The number of managed hosts that are in state "ready"</td> - </tr> - <tr> - <td><p id="hostedVespa_reservedHosts">hostedVespa.reservedHosts</p></td> - <td>host</td> - <td>The number of managed hosts that are in state "reserved"</td> - </tr> - <tr> - <td><p id="hostedVespa_activeNodes">hostedVespa.activeNodes</p></td> - <td>host</td> - <td>The number of managed nodes that are in state "active"</td> - </tr> - <tr> - <td><p id="hostedVespa_breakfixedNodes">hostedVespa.breakfixedNodes</p></td> - <td>host</td> - <td>The number of managed nodes that are in state "breakfixed"</td> - </tr> - <tr> - <td><p id="hostedVespa_deprovisionedNodes">hostedVespa.deprovisionedNodes</p></td> - <td>host</td> - <td>The number of managed nodes that are in state "deprovisioned"</td> - </tr> - <tr> - <td><p id="hostedVespa_dirtyNodes">hostedVespa.dirtyNodes</p></td> - <td>host</td> - <td>The number of managed nodes that are in state "dirty"</td> - </tr> - <tr> - <td><p id="hostedVespa_failedNodes">hostedVespa.failedNodes</p></td> - <td>host</td> - <td>The number of managed nodes that are in state "failed"</td> - </tr> - <tr> - <td><p id="hostedVespa_inactiveNodes">hostedVespa.inactiveNodes</p></td> - <td>host</td> - <td>The number of managed nodes that are in state "inactive"</td> - </tr> - <tr> - <td><p id="hostedVespa_parkedNodes">hostedVespa.parkedNodes</p></td> - <td>host</td> - <td>The number of managed nodes that are in state "parked"</td> - </tr> - <tr> - <td><p id="hostedVespa_provisionedNodes">hostedVespa.provisionedNodes</p></td> - <td>host</td> - <td>The number of managed nodes that are in state "provisioned"</td> - </tr> - <tr> - <td><p id="hostedVespa_readyNodes">hostedVespa.readyNodes</p></td> - <td>host</td> - <td>The number of managed nodes that are in state "ready"</td> - </tr> - <tr> - <td><p id="hostedVespa_reservedNodes">hostedVespa.reservedNodes</p></td> - <td>host</td> - <td>The number of managed nodes that are in state "reserved"</td> - </tr> - <tr> - <td><p id="overcommittedHosts">overcommittedHosts</p></td> - <td>host</td> - <td>The number of hosts with over-committed resources</td> - </tr> - <tr> - <td><p id="spareHostCapacity">spareHostCapacity</p></td> - <td>host</td> - <td>The number of spare hosts</td> - </tr> - <tr> - <td><p id="throttledHostFailures">throttledHostFailures</p></td> - <td>host</td> - <td>Number of host failures stopped due to throttling</td> - </tr> - <tr> - <td><p id="throttledNodeFailures">throttledNodeFailures</p></td> - <td>host</td> - <td>Number of node failures stopped due to throttling</td> - </tr> - <tr> - <td><p id="nodeFailThrottling">nodeFailThrottling</p></td> - <td>binary</td> - <td>Metric indicating when node failure throttling is active. The value 1 means active, 0 means inactive</td> - </tr> - <tr> - <td><p id="clusterAutoscaled">clusterAutoscaled</p></td> - <td>operation</td> - <td>Number of times a cluster has been rescaled by the autoscaler</td> - </tr> - <tr> - <td><p id="clusterAutoscaleDuration">clusterAutoscaleDuration</p></td> - <td>second</td> - <td>The currently predicted duration of a rescaling of this cluster</td> - </tr> - <tr> - <td><p id="deployment_prepareMillis">deployment.prepareMillis</p></td> - <td>millisecond</td> - <td>Duration of deployment preparations</td> - </tr> - <tr> - <td><p id="deployment_activateMillis">deployment.activateMillis</p></td> - <td>millisecond</td> - <td>Duration of deployment activations</td> - </tr> - <tr> - <td><p id="throttledHostProvisioning">throttledHostProvisioning</p></td> - <td>binary</td> - <td>Value 1 if host provisioning is throttled, 0 if not</td> - </tr> - </tbody> -</table> diff --git a/en/reference/configserver-metrics-reference.html b/en/reference/operations/metrics/configserver.mdx similarity index 58% rename from en/reference/configserver-metrics-reference.html rename to en/reference/operations/metrics/configserver.mdx index ad422a99a1..c35e7cc134 100644 --- a/en/reference/configserver-metrics-reference.html +++ b/en/reference/operations/metrics/configserver.mdx @@ -1,692 +1,711 @@ ---- -# Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +--- title: "ConfigServer Metrics" +sidebarTitle: "Configserver metrics" --- -<table class="table"> +<table className="vespa-table"> <thead> - <tr><th>Name</th><th>Unit</th><th>Description</th></tr> + <tr> + <th>Name</th> + <th>Unit</th> + <th>Description</th> + </tr> </thead> <tbody> <tr> - <td><p id="configserver_requests">configserver.requests</p></td> + <td>configserver.requests</td> <td>request</td> <td>Number of requests processed</td> </tr> <tr> - <td><p id="configserver_failedRequests">configserver.failedRequests</p></td> + <td>configserver.failedRequests</td> <td>request</td> <td>Number of requests that failed</td> </tr> <tr> - <td><p id="configserver_latency">configserver.latency</p></td> + <td>configserver.latency</td> <td>millisecond</td> <td>Time to complete requests</td> </tr> <tr> - <td><p id="configserver_cacheConfigElems">configserver.cacheConfigElems</p></td> + <td>configserver.cacheConfigElems</td> <td>item</td> <td>Time to complete requests</td> </tr> <tr> - <td><p id="configserver_cacheChecksumElems">configserver.cacheChecksumElems</p></td> + <td>configserver.cacheChecksumElems</td> <td>item</td> <td>Number of checksum elements in the cache</td> </tr> <tr> - <td><p id="configserver_hosts">configserver.hosts</p></td> + <td>configserver.hosts</td> <td>node</td> <td>The number of nodes being served configuration from the config server cluster</td> </tr> <tr> - <td><p id="configserver_tenants">configserver.tenants</p></td> + <td>configserver.tenants</td> <td>instance</td> <td>The number of tenants being served configuration from the config server cluster</td> </tr> <tr> - <td><p id="configserver_applications">configserver.applications</p></td> + <td>configserver.applications</td> <td>instance</td> <td>The number of applications being served configuration from the config server cluster</td> </tr> <tr> - <td><p id="configserver_delayedResponses">configserver.delayedResponses</p></td> + <td>configserver.delayedResponses</td> <td>response</td> <td>Number of delayed responses</td> </tr> <tr> - <td><p id="configserver_sessionChangeErrors">configserver.sessionChangeErrors</p></td> + <td>configserver.sessionChangeErrors</td> <td>session</td> <td>Number of session change errors</td> </tr> <tr> - <td><p id="configserver_unknownHostRequests">configserver.unknownHostRequests</p></td> + <td>configserver.unknownHostRequests</td> <td>request</td> <td>Config requests from unknown hosts</td> </tr> <tr> - <td><p id="configserver_newSessions">configserver.newSessions</p></td> + <td>configserver.newSessions</td> <td>session</td> <td>New config sessions</td> </tr> <tr> - <td><p id="configserver_preparedSessions">configserver.preparedSessions</p></td> + <td>configserver.preparedSessions</td> <td>session</td> <td>Prepared config sessions</td> </tr> <tr> - <td><p id="configserver_activeSessions">configserver.activeSessions</p></td> + <td>configserver.activeSessions</td> <td>session</td> <td>Active config sessions</td> </tr> <tr> - <td><p id="configserver_inactiveSessions">configserver.inactiveSessions</p></td> + <td>configserver.inactiveSessions</td> <td>session</td> <td>Inactive config sessions</td> </tr> <tr> - <td><p id="configserver_addedSessions">configserver.addedSessions</p></td> + <td>configserver.addedSessions</td> <td>session</td> <td>Added config sessions</td> </tr> <tr> - <td><p id="configserver_removedSessions">configserver.removedSessions</p></td> + <td>configserver.removedSessions</td> <td>session</td> <td>Removed config sessions</td> </tr> <tr> - <td><p id="configserver_rpcServerWorkQueueSize">configserver.rpcServerWorkQueueSize</p></td> + <td>configserver.rpcServerWorkQueueSize</td> <td>item</td> <td>Number of elements in the RPC server work queue</td> </tr> <tr> - <td><p id="maintenanceDeployment_transientFailure">maintenanceDeployment.transientFailure</p></td> + <td>maintenanceDeployment.transientFailure</td> <td>operation</td> <td>Number of maintenance deployments that failed with a transient failure</td> </tr> <tr> - <td><p id="maintenanceDeployment_failure">maintenanceDeployment.failure</p></td> + <td>maintenanceDeployment.failure</td> <td>operation</td> <td>Number of maintenance deployments that failed with a permanent failure</td> </tr> <tr> - <td><p id="maintenance_successFactorDeviation">maintenance.successFactorDeviation</p></td> + <td>maintenanceDeployment.reason</td> + <td>operation</td> + <td>Reason for maintenance deployment</td> + </tr> + <tr> + <td>maintenance.successFactorDeviation</td> <td>fraction</td> <td>Configserver: Maintenance Success Factor Deviation</td> </tr> <tr> - <td><p id="maintenance_duration">maintenance.duration</p></td> + <td>maintenance.duration</td> <td>millisecond</td> <td>Configserver: Maintenance Duration</td> </tr> <tr> - <td><p id="maintenance_congestion">maintenance.congestion</p></td> + <td>maintenance.congestion</td> <td>failure</td> <td>Configserver: Maintenance Congestion</td> </tr> <tr> - <td><p id="configserver_zkConnectionLost">configserver.zkConnectionLost</p></td> + <td>configserver.zkConnectionLost</td> <td>connection</td> <td>Number of ZooKeeper connections lost</td> </tr> <tr> - <td><p id="configserver_zkReconnected">configserver.zkReconnected</p></td> + <td>configserver.zkReconnected</td> <td>connection</td> <td>Number of ZooKeeper reconnections</td> </tr> <tr> - <td><p id="configserver_zkConnected">configserver.zkConnected</p></td> + <td>configserver.zkConnected</td> <td>node</td> <td>Number of ZooKeeper nodes connected</td> </tr> <tr> - <td><p id="configserver_zkSuspended">configserver.zkSuspended</p></td> + <td>configserver.zkSuspended</td> <td>node</td> <td>Number of ZooKeeper nodes suspended</td> </tr> <tr> - <td><p id="configserver_zkZNodes">configserver.zkZNodes</p></td> + <td>configserver.zkZNodes</td> <td>node</td> <td>Number of ZooKeeper nodes present</td> </tr> <tr> - <td><p id="configserver_zkAvgLatency">configserver.zkAvgLatency</p></td> + <td>configserver.zkAvgLatency</td> <td>millisecond</td> <td>Average latency for ZooKeeper requests</td> </tr> <tr> - <td><p id="configserver_zkMaxLatency">configserver.zkMaxLatency</p></td> + <td>configserver.zkMaxLatency</td> <td>millisecond</td> <td>Max latency for ZooKeeper requests</td> </tr> <tr> - <td><p id="configserver_zkConnections">configserver.zkConnections</p></td> + <td>configserver.zkConnections</td> <td>connection</td> <td>Number of ZooKeeper connections</td> </tr> <tr> - <td><p id="configserver_zkOutstandingRequests">configserver.zkOutstandingRequests</p></td> + <td>configserver.zkOutstandingRequests</td> <td>request</td> <td>Number of ZooKeeper requests in flight</td> </tr> <tr> - <td><p id="orchestrator_lock_acquire-latency">orchestrator.lock.acquire-latency</p></td> + <td>orchestrator.lock.acquire-latency</td> <td>second</td> <td>Time to acquire zookeeper lock</td> </tr> <tr> - <td><p id="orchestrator_lock_acquire-success">orchestrator.lock.acquire-success</p></td> + <td>orchestrator.lock.acquire-success</td> <td>operation</td> <td>Number of times zookeeper lock has been acquired successfully</td> </tr> <tr> - <td><p id="orchestrator_lock_acquire-timedout">orchestrator.lock.acquire-timedout</p></td> + <td>orchestrator.lock.acquire-timedout</td> <td>operation</td> <td>Number of times zookeeper lock couldn't be acquired within timeout</td> </tr> <tr> - <td><p id="orchestrator_lock_acquire">orchestrator.lock.acquire</p></td> + <td>orchestrator.lock.acquire</td> <td>operation</td> <td>Number of attempts to acquire zookeeper lock</td> </tr> <tr> - <td><p id="orchestrator_lock_acquired">orchestrator.lock.acquired</p></td> + <td>orchestrator.lock.acquired</td> <td>operation</td> <td>Number of times zookeeper lock was acquired</td> </tr> <tr> - <td><p id="orchestrator_lock_hold-latency">orchestrator.lock.hold-latency</p></td> + <td>orchestrator.lock.hold-latency</td> <td>second</td> <td>Time zookeeper lock was held before it was released</td> </tr> <tr> - <td><p id="nodes_active">nodes.active</p></td> + <td>nodes.active</td> <td>node</td> <td>The number of active nodes in a cluster</td> </tr> <tr> - <td><p id="nodes_nonActive">nodes.nonActive</p></td> + <td>nodes.nonActive</td> <td>node</td> <td>The number of non-active nodes in a cluster</td> </tr> <tr> - <td><p id="nodes_nonActiveFraction">nodes.nonActiveFraction</p></td> + <td>nodes.nonActiveFraction</td> <td>node</td> <td>The fraction of non-active nodes vs total nodes in a cluster</td> </tr> <tr> - <td><p id="nodes_exclusiveSwitchFraction">nodes.exclusiveSwitchFraction</p></td> + <td>nodes.exclusiveSwitchFraction</td> <td>fraction</td> <td>The fraction of nodes in a cluster on exclusive network switches</td> </tr> <tr> - <td><p id="nodes_emptyExclusive">nodes.emptyExclusive</p></td> + <td>nodes.emptyExclusive</td> <td>node</td> <td>The number of exclusive hosts that do not have any nodes allocated to them</td> </tr> <tr> - <td><p id="nodes_expired_deprovisioned">nodes.expired.deprovisioned</p></td> + <td>nodes.expired.deprovisioned</td> <td>node</td> <td>The number of deprovisioned nodes that have expired</td> </tr> <tr> - <td><p id="nodes_expired_dirty">nodes.expired.dirty</p></td> + <td>nodes.expired.dirty</td> <td>node</td> <td>The number of dirty nodes that have expired</td> </tr> <tr> - <td><p id="nodes_expired_inactive">nodes.expired.inactive</p></td> + <td>nodes.expired.inactive</td> <td>node</td> <td>The number of inactive nodes that have expired</td> </tr> <tr> - <td><p id="nodes_expired_provisioned">nodes.expired.provisioned</p></td> + <td>nodes.expired.provisioned</td> <td>node</td> <td>The number of provisioned nodes that have expired</td> </tr> <tr> - <td><p id="nodes_expired_reserved">nodes.expired.reserved</p></td> + <td>nodes.expired.reserved</td> <td>node</td> <td>The number of reserved nodes that have expired</td> </tr> <tr> - <td><p id="cluster_cost">cluster.cost</p></td> + <td>cluster.cost</td> <td>dollar_per_hour</td> <td>The cost of the nodes allocated to a certain cluster, in $/hr</td> </tr> <tr> - <td><p id="cluster_load_ideal_cpu">cluster.load.ideal.cpu</p></td> + <td>cluster.load.ideal.cpu</td> <td>fraction</td> <td>The ideal cpu load of a certain cluster</td> </tr> <tr> - <td><p id="cluster_load_ideal_memory">cluster.load.ideal.memory</p></td> + <td>cluster.load.ideal.memory</td> <td>fraction</td> <td>The ideal memory load of a certain cluster</td> </tr> <tr> - <td><p id="cluster_load_ideal_disk">cluster.load.ideal.disk</p></td> + <td>cluster.load.ideal.disk</td> <td>fraction</td> <td>The ideal disk load of a certain cluster</td> </tr> <tr> - <td><p id="cluster_load_peak_cpu">cluster.load.peak.cpu</p></td> + <td>cluster.load.peak.cpu</td> <td>fraction</td> <td>The peak cpu load in the period considered of a certain cluster</td> </tr> <tr> - <td><p id="cluster_load_peak_memory">cluster.load.peak.memory</p></td> + <td>cluster.load.peak.memory</td> <td>fraction</td> <td>The peak memory load in the period considered of a certain cluster</td> </tr> <tr> - <td><p id="cluster_load_peak_disk">cluster.load.peak.disk</p></td> + <td>cluster.load.peak.disk</td> <td>fraction</td> <td>The peak disk load in the period considered of a certain cluster</td> </tr> <tr> - <td><p id="zone_working">zone.working</p></td> + <td>cluster.backup.age</td> + <td>fraction</td> + <td>Age of the most recent cluster backup as a fraction of the backup interval</td> + </tr> + <tr> + <td>cluster.snapshot.busySeconds</td> + <td>second</td> + <td>The maximum time a snapshot has been busy (creating or restoring) for a cluster</td> + </tr> + <tr> + <td>zone.working</td> <td>binary</td> <td>The value 1 if zone is considered healthy, 0 if not. This is decided by considering the number of non-active nodes vs the number of active nodes in a zone</td> </tr> <tr> - <td><p id="cache_nodeObject_hitRate">cache.nodeObject.hitRate</p></td> + <td>cache.nodeObject.hitRate</td> <td>fraction</td> <td>The fraction of cache hits vs cache lookups for the node object cache</td> </tr> <tr> - <td><p id="cache_nodeObject_evictionCount">cache.nodeObject.evictionCount</p></td> + <td>cache.nodeObject.evictionCount</td> <td>item</td> <td>The number of cache elements evicted from the node object cache</td> </tr> <tr> - <td><p id="cache_nodeObject_size">cache.nodeObject.size</p></td> + <td>cache.nodeObject.size</td> <td>item</td> <td>The number of cache elements in the node object cache</td> </tr> <tr> - <td><p id="cache_curator_hitRate">cache.curator.hitRate</p></td> + <td>cache.curator.hitRate</td> <td>fraction</td> <td>The fraction of cache hits vs cache lookups for the curator cache</td> </tr> <tr> - <td><p id="cache_curator_evictionCount">cache.curator.evictionCount</p></td> + <td>cache.curator.evictionCount</td> <td>item</td> <td>The number of cache elements evicted from the curator cache</td> </tr> <tr> - <td><p id="cache_curator_size">cache.curator.size</p></td> + <td>cache.curator.size</td> <td>item</td> <td>The number of cache elements in the curator cache</td> </tr> <tr> - <td><p id="wantedRestartGeneration">wantedRestartGeneration</p></td> + <td>wantedRestartGeneration</td> <td>generation</td> <td>Wanted restart generation for tenant node</td> </tr> <tr> - <td><p id="currentRestartGeneration">currentRestartGeneration</p></td> + <td>currentRestartGeneration</td> <td>generation</td> <td>Current restart generation for tenant node</td> </tr> <tr> - <td><p id="wantToRestart">wantToRestart</p></td> + <td>wantToRestart</td> <td>binary</td> <td>One if node wants to restart, zero if not</td> </tr> <tr> - <td><p id="wantedRebootGeneration">wantedRebootGeneration</p></td> + <td>wantedRebootGeneration</td> <td>generation</td> <td>Wanted reboot generation for tenant node</td> </tr> <tr> - <td><p id="currentRebootGeneration">currentRebootGeneration</p></td> + <td>currentRebootGeneration</td> <td>generation</td> <td>Current reboot generation for tenant node</td> </tr> <tr> - <td><p id="wantToReboot">wantToReboot</p></td> + <td>wantToReboot</td> <td>binary</td> <td>One if node wants to reboot, zero if not</td> </tr> <tr> - <td><p id="retired">retired</p></td> + <td>retired</td> <td>binary</td> <td>One if node is retired, zero if not</td> </tr> <tr> - <td><p id="wantedVespaVersion">wantedVespaVersion</p></td> + <td>wantedVespaVersion</td> <td>version</td> <td>Wanted vespa version for the node, in the form MINOR.PATCH. Major version is not included here</td> </tr> <tr> - <td><p id="currentVespaVersion">currentVespaVersion</p></td> + <td>currentVespaVersion</td> <td>version</td> <td>Current vespa version for the node, in the form MINOR.PATCH. Major version is not included here</td> </tr> <tr> - <td><p id="wantToChangeVespaVersion">wantToChangeVespaVersion</p></td> + <td>wantToChangeVespaVersion</td> <td>binary</td> <td>One if node want to change Vespa version, zero if not</td> </tr> <tr> - <td><p id="hasWireguardKey">hasWireguardKey</p></td> + <td>hasWireguardKey</td> <td>binary</td> <td>One if node has a WireGuard key, zero if not</td> </tr> <tr> - <td><p id="wantToRetire">wantToRetire</p></td> + <td>wantToRetire</td> <td>binary</td> <td>One if node wants to retire, zero if not</td> </tr> <tr> - <td><p id="wantToDeprovision">wantToDeprovision</p></td> + <td>wantToDeprovision</td> <td>binary</td> <td>One if node wants to be deprovisioned, zero if not</td> </tr> <tr> - <td><p id="failReport">failReport</p></td> + <td>failReport</td> <td>binary</td> <td>One if there is a fail report for the node, zero if not</td> </tr> <tr> - <td><p id="suspended">suspended</p></td> + <td>suspended</td> <td>binary</td> <td>One if the node is suspended, zero if not</td> </tr> <tr> - <td><p id="suspendedSeconds">suspendedSeconds</p></td> + <td>suspendedSeconds</td> <td>second</td> <td>The number of seconds the node has been suspended</td> </tr> <tr> - <td><p id="activeSeconds">activeSeconds</p></td> + <td>activeSeconds</td> <td>second</td> <td>The number of seconds the node has been active</td> </tr> <tr> - <td><p id="numberOfServicesUp">numberOfServicesUp</p></td> + <td>numberOfServicesUp</td> <td>instance</td> <td>The number of services confirmed to be running on a node</td> </tr> <tr> - <td><p id="numberOfServicesNotChecked">numberOfServicesNotChecked</p></td> + <td>numberOfServicesNotChecked</td> <td>instance</td> <td>The number of services supposed to run on a node, that has not checked</td> </tr> <tr> - <td><p id="numberOfServicesDown">numberOfServicesDown</p></td> + <td>numberOfServicesDown</td> <td>instance</td> <td>The number of services confirmed to not be running on a node</td> </tr> <tr> - <td><p id="someServicesDown">someServicesDown</p></td> + <td>someServicesDown</td> <td>binary</td> <td>One if one or more services has been confirmed to not run on a node, zero if not</td> </tr> <tr> - <td><p id="numberOfServicesUnknown">numberOfServicesUnknown</p></td> + <td>numberOfServicesUnknown</td> <td>instance</td> <td>The number of services the config server does not know is running on a node</td> </tr> <tr> - <td><p id="nodeFailerBadNode">nodeFailerBadNode</p></td> + <td>nodeFailerBadNode</td> <td>binary</td> <td>One if the node is failed due to being bad, zero if not</td> </tr> <tr> - <td><p id="downInNodeRepo">downInNodeRepo</p></td> + <td>downInNodeRepo</td> <td>binary</td> <td>One if the node is registered as being down in the node repository, zero if not</td> </tr> <tr> - <td><p id="numberOfServices">numberOfServices</p></td> + <td>numberOfServices</td> <td>instance</td> <td>Number of services supposed to run on a node</td> </tr> <tr> - <td><p id="lockAttempt_acquireMaxActiveLatency">lockAttempt.acquireMaxActiveLatency</p></td> + <td>lockAttempt.acquireMaxActiveLatency</td> <td>second</td> <td>Maximum duration for keeping a lock, ending during the metrics snapshot, or still being kept at the end or this snapshot period</td> </tr> <tr> - <td><p id="lockAttempt_acquireHz">lockAttempt.acquireHz</p></td> + <td>lockAttempt.acquireHz</td> <td>operation_per_second</td> <td>Average number of locks acquired per second the snapshot period</td> </tr> <tr> - <td><p id="lockAttempt_acquireLoad">lockAttempt.acquireLoad</p></td> + <td>lockAttempt.acquireLoad</td> <td>operation</td> <td>Average number of locks held concurrently during the snapshot period</td> </tr> <tr> - <td><p id="lockAttempt_lockedLatency">lockAttempt.lockedLatency</p></td> + <td>lockAttempt.lockedLatency</td> <td>second</td> <td>Longest lock duration in the snapshot period</td> </tr> <tr> - <td><p id="lockAttempt_lockedLoad">lockAttempt.lockedLoad</p></td> + <td>lockAttempt.lockedLoad</td> <td>operation</td> <td>Average number of locks held concurrently during the snapshot period</td> </tr> <tr> - <td><p id="lockAttempt_acquireTimedOut">lockAttempt.acquireTimedOut</p></td> + <td>lockAttempt.acquireTimedOut</td> <td>operation</td> - <td> Number of locking attempts that timed out during the snapshot period</td> + <td>Number of locking attempts that timed out during the snapshot period</td> </tr> <tr> - <td><p id="lockAttempt_deadlock">lockAttempt.deadlock</p></td> + <td>lockAttempt.deadlock</td> <td>operation</td> <td>Number of lock grab deadlocks detected during the snapshot period</td> </tr> <tr> - <td><p id="lockAttempt_errors">lockAttempt.errors</p></td> + <td>lockAttempt.errors</td> <td>operation</td> <td>Number of other lock related errors detected during the snapshot period</td> </tr> <tr> - <td><p id="hostedVespa_docker_totalCapacityCpu">hostedVespa.docker.totalCapacityCpu</p></td> + <td>hostedVespa.docker.totalCapacityCpu</td> <td>vcpu</td> <td>Total number of VCPUs on tenant hosts managed by hosted Vespa in a zone</td> </tr> <tr> - <td><p id="hostedVespa_docker_totalCapacityMem">hostedVespa.docker.totalCapacityMem</p></td> + <td>hostedVespa.docker.totalCapacityMem</td> <td>gigabyte</td> <td>Total amount of memory on tenant hosts managed by hosted Vespa in a zone</td> </tr> <tr> - <td><p id="hostedVespa_docker_totalCapacityDisk">hostedVespa.docker.totalCapacityDisk</p></td> + <td>hostedVespa.docker.totalCapacityDisk</td> <td>gigabyte</td> <td>Total amount of disk space on tenant hosts managed by hosted Vespa in a zone</td> </tr> <tr> - <td><p id="hostedVespa_docker_freeCapacityCpu">hostedVespa.docker.freeCapacityCpu</p></td> + <td>hostedVespa.docker.freeCapacityCpu</td> <td>vcpu</td> <td>Total number of free VCPUs on tenant hosts managed by hosted Vespa in a zone</td> </tr> <tr> - <td><p id="hostedVespa_docker_freeCapacityMem">hostedVespa.docker.freeCapacityMem</p></td> + <td>hostedVespa.docker.freeCapacityMem</td> <td>gigabyte</td> <td>Total amount of free memory on tenant hosts managed by hosted Vespa in a zone</td> </tr> <tr> - <td><p id="hostedVespa_docker_freeCapacityDisk">hostedVespa.docker.freeCapacityDisk</p></td> + <td>hostedVespa.docker.freeCapacityDisk</td> <td>gigabyte</td> <td>Total amount of free disk space on tenant hosts managed by hosted Vespa in a zone</td> </tr> <tr> - <td><p id="hostedVespa_docker_allocatedCapacityCpu">hostedVespa.docker.allocatedCapacityCpu</p></td> + <td>hostedVespa.docker.allocatedCapacityCpu</td> <td>vcpu</td> <td>Total number of allocated VCPUs on tenant hosts managed by hosted Vespa in a zone</td> </tr> <tr> - <td><p id="hostedVespa_docker_allocatedCapacityMem">hostedVespa.docker.allocatedCapacityMem</p></td> + <td>hostedVespa.docker.allocatedCapacityMem</td> <td>gigabyte</td> <td>Total amount of allocated memory on tenant hosts managed by hosted Vespa in a zone</td> </tr> <tr> - <td><p id="hostedVespa_docker_allocatedCapacityDisk">hostedVespa.docker.allocatedCapacityDisk</p></td> + <td>hostedVespa.docker.allocatedCapacityDisk</td> <td>gigabyte</td> <td>Total amount of allocated disk space on tenant hosts managed by hosted Vespa in a zone</td> </tr> <tr> - <td><p id="hostedVespa_pendingRedeployments">hostedVespa.pendingRedeployments</p></td> + <td>hostedVespa.pendingRedeployments</td> <td>task</td> <td>The number of hosted Vespa re-deployments pending</td> </tr> <tr> - <td><p id="hostedVespa_docker_skew">hostedVespa.docker.skew</p></td> + <td>hostedVespa.docker.skew</td> <td>fraction</td> <td>A number in the range 0..1 indicating how well allocated resources are balanced with availability on hosts</td> </tr> <tr> - <td><p id="hostedVespa_activeHosts">hostedVespa.activeHosts</p></td> + <td>hostedVespa.activeHosts</td> <td>host</td> <td>The number of managed hosts that are in state "active"</td> </tr> <tr> - <td><p id="hostedVespa_breakfixedHosts">hostedVespa.breakfixedHosts</p></td> + <td>hostedVespa.breakfixedHosts</td> <td>host</td> <td>The number of managed hosts that are in state "breakfixed"</td> </tr> <tr> - <td><p id="hostedVespa_deprovisionedHosts">hostedVespa.deprovisionedHosts</p></td> + <td>hostedVespa.deprovisionedHosts</td> <td>host</td> <td>The number of managed hosts that are in state "deprovisioned"</td> </tr> <tr> - <td><p id="hostedVespa_dirtyHosts">hostedVespa.dirtyHosts</p></td> + <td>hostedVespa.dirtyHosts</td> <td>host</td> <td>The number of managed hosts that are in state "dirty"</td> </tr> <tr> - <td><p id="hostedVespa_failedHosts">hostedVespa.failedHosts</p></td> + <td>hostedVespa.failedHosts</td> <td>host</td> <td>The number of managed hosts that are in state "failed"</td> </tr> <tr> - <td><p id="hostedVespa_inactiveHosts">hostedVespa.inactiveHosts</p></td> + <td>hostedVespa.inactiveHosts</td> <td>host</td> <td>The number of managed hosts that are in state "inactive"</td> </tr> <tr> - <td><p id="hostedVespa_parkedHosts">hostedVespa.parkedHosts</p></td> + <td>hostedVespa.parkedHosts</td> <td>host</td> <td>The number of managed hosts that are in state "parked"</td> </tr> <tr> - <td><p id="hostedVespa_provisionedHosts">hostedVespa.provisionedHosts</p></td> + <td>hostedVespa.provisionedHosts</td> <td>host</td> <td>The number of managed hosts that are in state "provisioned"</td> </tr> <tr> - <td><p id="hostedVespa_readyHosts">hostedVespa.readyHosts</p></td> + <td>hostedVespa.readyHosts</td> <td>host</td> <td>The number of managed hosts that are in state "ready"</td> </tr> <tr> - <td><p id="hostedVespa_reservedHosts">hostedVespa.reservedHosts</p></td> + <td>hostedVespa.reservedHosts</td> <td>host</td> <td>The number of managed hosts that are in state "reserved"</td> </tr> <tr> - <td><p id="hostedVespa_activeNodes">hostedVespa.activeNodes</p></td> + <td>hostedVespa.activeNodes</td> <td>host</td> <td>The number of managed nodes that are in state "active"</td> </tr> <tr> - <td><p id="hostedVespa_breakfixedNodes">hostedVespa.breakfixedNodes</p></td> + <td>hostedVespa.breakfixedNodes</td> <td>host</td> <td>The number of managed nodes that are in state "breakfixed"</td> </tr> <tr> - <td><p id="hostedVespa_deprovisionedNodes">hostedVespa.deprovisionedNodes</p></td> + <td>hostedVespa.deprovisionedNodes</td> <td>host</td> <td>The number of managed nodes that are in state "deprovisioned"</td> </tr> <tr> - <td><p id="hostedVespa_dirtyNodes">hostedVespa.dirtyNodes</p></td> + <td>hostedVespa.dirtyNodes</td> <td>host</td> <td>The number of managed nodes that are in state "dirty"</td> </tr> <tr> - <td><p id="hostedVespa_failedNodes">hostedVespa.failedNodes</p></td> + <td>hostedVespa.failedNodes</td> <td>host</td> <td>The number of managed nodes that are in state "failed"</td> </tr> <tr> - <td><p id="hostedVespa_inactiveNodes">hostedVespa.inactiveNodes</p></td> + <td>hostedVespa.inactiveNodes</td> <td>host</td> <td>The number of managed nodes that are in state "inactive"</td> </tr> <tr> - <td><p id="hostedVespa_parkedNodes">hostedVespa.parkedNodes</p></td> + <td>hostedVespa.parkedNodes</td> <td>host</td> <td>The number of managed nodes that are in state "parked"</td> </tr> <tr> - <td><p id="hostedVespa_provisionedNodes">hostedVespa.provisionedNodes</p></td> + <td>hostedVespa.provisionedNodes</td> <td>host</td> <td>The number of managed nodes that are in state "provisioned"</td> </tr> <tr> - <td><p id="hostedVespa_readyNodes">hostedVespa.readyNodes</p></td> + <td>hostedVespa.readyNodes</td> <td>host</td> <td>The number of managed nodes that are in state "ready"</td> </tr> <tr> - <td><p id="hostedVespa_reservedNodes">hostedVespa.reservedNodes</p></td> + <td>hostedVespa.reservedNodes</td> <td>host</td> <td>The number of managed nodes that are in state "reserved"</td> </tr> <tr> - <td><p id="overcommittedHosts">overcommittedHosts</p></td> + <td>overcommittedHosts</td> <td>host</td> <td>The number of hosts with over-committed resources</td> </tr> <tr> - <td><p id="spareHostCapacity">spareHostCapacity</p></td> + <td>spareHostCapacity</td> <td>host</td> <td>The number of spare hosts</td> </tr> <tr> - <td><p id="throttledHostFailures">throttledHostFailures</p></td> + <td>throttledHostFailures</td> <td>host</td> <td>Number of host failures stopped due to throttling</td> </tr> <tr> - <td><p id="throttledNodeFailures">throttledNodeFailures</p></td> + <td>throttledNodeFailures</td> <td>host</td> <td>Number of node failures stopped due to throttling</td> </tr> <tr> - <td><p id="nodeFailThrottling">nodeFailThrottling</p></td> + <td>nodeFailThrottling</td> <td>binary</td> <td>Metric indicating when node failure throttling is active. The value 1 means active, 0 means inactive</td> </tr> <tr> - <td><p id="clusterAutoscaled">clusterAutoscaled</p></td> + <td>clusterAutoscaled</td> <td>operation</td> <td>Number of times a cluster has been rescaled by the autoscaler</td> </tr> <tr> - <td><p id="clusterAutoscaleDuration">clusterAutoscaleDuration</p></td> + <td>clusterAutoscaleDuration</td> <td>second</td> <td>The currently predicted duration of a rescaling of this cluster</td> </tr> <tr> - <td><p id="deployment_prepareMillis">deployment.prepareMillis</p></td> + <td>deployment.prepareMillis</td> <td>millisecond</td> <td>Duration of deployment preparations</td> </tr> <tr> - <td><p id="deployment_activateMillis">deployment.activateMillis</p></td> + <td>deployment.activateMillis</td> <td>millisecond</td> <td>Duration of deployment activations</td> </tr> <tr> - <td><p id="throttledHostProvisioning">throttledHostProvisioning</p></td> + <td>throttledHostProvisioning</td> <td>binary</td> <td>Value 1 if host provisioning is throttled, 0 if not</td> </tr> </tbody> -</table> +</table> \ No newline at end of file diff --git a/en/reference/operations/metrics/container.html b/en/reference/operations/metrics/container.mdx similarity index 56% rename from en/reference/operations/metrics/container.html rename to en/reference/operations/metrics/container.mdx index 9876a41bd9..0652db2495 100644 --- a/en/reference/operations/metrics/container.html +++ b/en/reference/operations/metrics/container.mdx @@ -1,1009 +1,1011 @@ ---- -# Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +--- title: "Container Metrics" -redirect_from: -- en/reference/container-metrics-reference.html +sidebarTitle: "Container metrics" --- -<table class="table"> +<table className="vespa-table"> <thead> - <tr><th>Name</th><th>Unit</th><th>Description</th></tr> + <tr> + <th>Name</th> + <th>Unit</th> + <th>Description</th> + </tr> </thead> <tbody> <tr> - <td><p id="http_status_1xx">http.status.1xx</p></td> + <td>http.status.1xx</td> <td>response</td> <td>Number of responses with a 1xx status</td> </tr> <tr> - <td><p id="http_status_2xx">http.status.2xx</p></td> + <td>http.status.2xx</td> <td>response</td> <td>Number of responses with a 2xx status</td> </tr> <tr> - <td><p id="http_status_3xx">http.status.3xx</p></td> + <td>http.status.3xx</td> <td>response</td> <td>Number of responses with a 3xx status</td> </tr> <tr> - <td><p id="http_status_4xx">http.status.4xx</p></td> + <td>http.status.4xx</td> <td>response</td> <td>Number of responses with a 4xx status</td> </tr> <tr> - <td><p id="http_status_5xx">http.status.5xx</p></td> + <td>http.status.5xx</td> <td>response</td> <td>Number of responses with a 5xx status</td> </tr> <tr> - <td><p id="application_generation">application_generation</p></td> + <td>application_generation</td> <td>version</td> <td>The currently live application config generation (aka session id)</td> </tr> <tr> - <td><p id="in_service">in_service</p></td> + <td>in_service</td> <td>binary</td> <td>This will have the value 1 if the node is in service, 0 if not.</td> </tr> <tr> - <td><p id="jdisc_gc_count">jdisc.gc.count</p></td> + <td>jdisc.gc.count</td> <td>operation</td> <td>Number of JVM garbage collections done</td> </tr> <tr> - <td><p id="jdisc_gc_ms">jdisc.gc.ms</p></td> + <td>jdisc.gc.ms</td> <td>millisecond</td> <td>Time spent in JVM garbage collection</td> </tr> <tr> - <td><p id="jdisc_jvm">jdisc.jvm</p></td> + <td>jdisc.jvm</td> <td>version</td> <td>JVM runtime version</td> </tr> <tr> - <td><p id="cpu">cpu</p></td> + <td>cpu</td> <td>thread</td> <td>Container service CPU pressure</td> </tr> <tr> - <td><p id="jdisc_memory_mappings">jdisc.memory_mappings</p></td> + <td>jdisc.memory_mappings</td> <td>operation</td> <td>JDISC Memory mappings</td> </tr> <tr> - <td><p id="jdisc_open_file_descriptors">jdisc.open_file_descriptors</p></td> + <td>jdisc.open_file_descriptors</td> <td>item</td> <td>JDISC Open file descriptors</td> </tr> <tr> - <td><p id="jdisc_thread_pool_unhandled_exceptions">jdisc.thread_pool.unhandled_exceptions</p></td> + <td>jdisc.thread_pool.unhandled_exceptions</td> <td>thread</td> <td>Number of exceptions thrown by tasks</td> </tr> <tr> - <td><p id="jdisc_thread_pool_work_queue_capacity">jdisc.thread_pool.work_queue.capacity</p></td> + <td>jdisc.thread_pool.work_queue.capacity</td> <td>thread</td> <td>Capacity of the task queue</td> </tr> <tr> - <td><p id="jdisc_thread_pool_work_queue_size">jdisc.thread_pool.work_queue.size</p></td> + <td>jdisc.thread_pool.work_queue.size</td> <td>thread</td> <td>Size of the task queue</td> </tr> <tr> - <td><p id="jdisc_thread_pool_rejected_tasks">jdisc.thread_pool.rejected_tasks</p></td> + <td>jdisc.thread_pool.rejected_tasks</td> <td>thread</td> <td>Number of tasks rejected by the thread pool</td> </tr> <tr> - <td><p id="jdisc_thread_pool_size">jdisc.thread_pool.size</p></td> + <td>jdisc.thread_pool.size</td> <td>thread</td> <td>Size of the thread pool</td> </tr> <tr> - <td><p id="jdisc_thread_pool_max_allowed_size">jdisc.thread_pool.max_allowed_size</p></td> + <td>jdisc.thread_pool.max_allowed_size</td> <td>thread</td> <td>The maximum allowed number of threads in the pool</td> </tr> <tr> - <td><p id="jdisc_thread_pool_active_threads">jdisc.thread_pool.active_threads</p></td> + <td>jdisc.thread_pool.active_threads</td> <td>thread</td> <td>Number of threads that are active</td> </tr> <tr> - <td><p id="jdisc_deactivated_containers_total">jdisc.deactivated_containers.total</p></td> + <td>jdisc.deactivated_containers.total</td> <td>item</td> <td>JDISC Deactivated container instances</td> </tr> <tr> - <td><p id="jdisc_deactivated_containers_with_retained_refs_last">jdisc.deactivated_containers.with_retained_refs.last</p></td> + <td>jdisc.deactivated_containers.with_retained_refs.last</td> <td>item</td> <td>JDISC Deactivated container nodes with retained refs</td> </tr> <tr> - <td><p id="jdisc_application_failed_component_graphs">jdisc.application.failed_component_graphs</p></td> + <td>jdisc.application.failed_component_graphs</td> <td>item</td> <td>JDISC Application failed component graphs</td> </tr> <tr> - <td><p id="jdisc_application_component_graph_creation_time_millis">jdisc.application.component_graph.creation_time_millis</p></td> + <td>jdisc.application.component_graph.creation_time_millis</td> <td>millisecond</td> <td>JDISC Application component graph creation time</td> </tr> <tr> - <td><p id="jdisc_application_component_graph_reconfigurations">jdisc.application.component_graph.reconfigurations</p></td> + <td>jdisc.application.component_graph.reconfigurations</td> <td>item</td> <td>JDISC Application component graph reconfigurations</td> </tr> <tr> - <td><p id="jdisc_singleton_is_active">jdisc.singleton.is_active</p></td> + <td>jdisc.singleton.is_active</td> <td>item</td> <td>JDISC Singleton is active</td> </tr> <tr> - <td><p id="jdisc_singleton_activation_count">jdisc.singleton.activation.count</p></td> + <td>jdisc.singleton.activation.count</td> <td>operation</td> <td>JDISC Singleton activations</td> </tr> <tr> - <td><p id="jdisc_singleton_activation_failure_count">jdisc.singleton.activation.failure.count</p></td> + <td>jdisc.singleton.activation.failure.count</td> <td>operation</td> <td>JDISC Singleton activation failures</td> </tr> <tr> - <td><p id="jdisc_singleton_activation_millis">jdisc.singleton.activation.millis</p></td> + <td>jdisc.singleton.activation.millis</td> <td>millisecond</td> <td>JDISC Singleton activation time</td> </tr> <tr> - <td><p id="jdisc_singleton_deactivation_count">jdisc.singleton.deactivation.count</p></td> + <td>jdisc.singleton.deactivation.count</td> <td>operation</td> <td>JDISC Singleton deactivations</td> </tr> <tr> - <td><p id="jdisc_singleton_deactivation_failure_count">jdisc.singleton.deactivation.failure.count</p></td> + <td>jdisc.singleton.deactivation.failure.count</td> <td>operation</td> <td>JDISC Singleton deactivation failures</td> </tr> <tr> - <td><p id="jdisc_singleton_deactivation_millis">jdisc.singleton.deactivation.millis</p></td> + <td>jdisc.singleton.deactivation.millis</td> <td>millisecond</td> <td>JDISC Singleton deactivation time</td> </tr> <tr> - <td><p id="jdisc_http_ssl_handshake_failure_missing_client_cert">jdisc.http.ssl.handshake.failure.missing_client_cert</p></td> + <td>jdisc.http.ssl.handshake.failure.missing_client_cert</td> <td>operation</td> <td>JDISC HTTP SSL Handshake failures due to missing client certificate</td> </tr> <tr> - <td><p id="jdisc_http_ssl_handshake_failure_expired_client_cert">jdisc.http.ssl.handshake.failure.expired_client_cert</p></td> + <td>jdisc.http.ssl.handshake.failure.expired_client_cert</td> <td>operation</td> <td>JDISC HTTP SSL Handshake failures due to expired client certificate</td> </tr> <tr> - <td><p id="jdisc_http_ssl_handshake_failure_invalid_client_cert">jdisc.http.ssl.handshake.failure.invalid_client_cert</p></td> + <td>jdisc.http.ssl.handshake.failure.invalid_client_cert</td> <td>operation</td> <td>JDISC HTTP SSL Handshake failures due to invalid client certificate</td> </tr> <tr> - <td><p id="jdisc_http_ssl_handshake_failure_incompatible_protocols">jdisc.http.ssl.handshake.failure.incompatible_protocols</p></td> + <td>jdisc.http.ssl.handshake.failure.incompatible_protocols</td> <td>operation</td> <td>JDISC HTTP SSL Handshake failures due to incompatible protocols</td> </tr> <tr> - <td><p id="jdisc_http_ssl_handshake_failure_incompatible_chifers">jdisc.http.ssl.handshake.failure.incompatible_chifers</p></td> + <td>jdisc.http.ssl.handshake.failure.incompatible_chifers</td> <td>operation</td> <td>JDISC HTTP SSL Handshake failures due to incompatible chifers</td> </tr> <tr> - <td><p id="jdisc_http_ssl_handshake_failure_connection_closed">jdisc.http.ssl.handshake.failure.connection_closed</p></td> + <td>jdisc.http.ssl.handshake.failure.connection_closed</td> <td>operation</td> <td>JDISC HTTP SSL Handshake failures due to connection closed</td> </tr> <tr> - <td><p id="jdisc_http_ssl_handshake_failure_unknown">jdisc.http.ssl.handshake.failure.unknown</p></td> + <td>jdisc.http.ssl.handshake.failure.unknown</td> <td>operation</td> <td>JDISC HTTP SSL Handshake failures for unknown reason</td> </tr> <tr> - <td><p id="jdisc_http_latency">jdisc.http.latency</p></td> + <td>jdisc.http.latency</td> <td>millisecond</td> <td>Request latency including the HTTP layer</td> </tr> <tr> - <td><p id="jdisc_http_time_to_first_byte">jdisc.http.time_to_first_byte</p></td> + <td>jdisc.http.time_to_first_byte</td> <td>millisecond</td> <td>Time from request has been received by the server until the first byte is returned to the client</td> </tr> <tr> - <td><p id="jdisc_http_request_prematurely_closed">jdisc.http.request.prematurely_closed</p></td> + <td>jdisc.http.request.prematurely_closed</td> <td>request</td> <td>HTTP requests prematurely closed</td> </tr> <tr> - <td><p id="jdisc_http_request_requests_per_connection">jdisc.http.request.requests_per_connection</p></td> + <td>jdisc.http.request.requests_per_connection</td> <td>request</td> <td>HTTP requests per connection</td> </tr> <tr> - <td><p id="jdisc_http_request_uri_length">jdisc.http.request.uri_length</p></td> + <td>jdisc.http.request.uri_length</td> <td>byte</td> <td>HTTP URI length</td> </tr> <tr> - <td><p id="jdisc_http_request_content_size">jdisc.http.request.content_size</p></td> + <td>jdisc.http.request.content_size</td> <td>byte</td> <td>HTTP request content size</td> </tr> <tr> - <td><p id="jdisc_http_requests">jdisc.http.requests</p></td> + <td>jdisc.http.requests</td> <td>request</td> <td>HTTP requests</td> </tr> <tr> - <td><p id="jdisc_http_requests_status">jdisc.http.requests.status</p></td> + <td>jdisc.http.requests.status</td> <td>request</td> <td>Number of requests to the built-in status handler</td> </tr> <tr> - <td><p id="jdisc_http_filter_rule_blocked_requests">jdisc.http.filter.rule.blocked_requests</p></td> + <td>jdisc.http.filter.rule.blocked_requests</td> <td>request</td> <td>Number of requests blocked by filter</td> </tr> <tr> - <td><p id="jdisc_http_filter_rule_allowed_requests">jdisc.http.filter.rule.allowed_requests</p></td> + <td>jdisc.http.filter.rule.allowed_requests</td> <td>request</td> <td>Number of requests allowed by filter</td> </tr> <tr> - <td><p id="jdisc_http_filtering_request_handled">jdisc.http.filtering.request.handled</p></td> + <td>jdisc.http.filtering.request.handled</td> <td>request</td> <td>Number of filtering requests handled</td> </tr> <tr> - <td><p id="jdisc_http_filtering_request_unhandled">jdisc.http.filtering.request.unhandled</p></td> + <td>jdisc.http.filtering.request.unhandled</td> <td>request</td> <td>Number of filtering requests unhandled</td> </tr> <tr> - <td><p id="jdisc_http_filtering_response_handled">jdisc.http.filtering.response.handled</p></td> + <td>jdisc.http.filtering.response.handled</td> <td>request</td> <td>Number of filtering responses handled</td> </tr> <tr> - <td><p id="jdisc_http_filtering_response_unhandled">jdisc.http.filtering.response.unhandled</p></td> + <td>jdisc.http.filtering.response.unhandled</td> <td>request</td> <td>Number of filtering responses unhandled</td> </tr> <tr> - <td><p id="jdisc_http_handler_unhandled_exceptions">jdisc.http.handler.unhandled_exceptions</p></td> + <td>jdisc.http.handler.unhandled_exceptions</td> <td>request</td> <td>Number of unhandled exceptions in handler</td> </tr> <tr> - <td><p id="jdisc_tls_capability_checks_succeeded">jdisc.tls.capability_checks.succeeded</p></td> + <td>jdisc.tls.capability_checks.succeeded</td> <td>operation</td> <td>Number of TLS capability checks succeeded</td> </tr> <tr> - <td><p id="jdisc_tls_capability_checks_failed">jdisc.tls.capability_checks.failed</p></td> + <td>jdisc.tls.capability_checks.failed</td> <td>operation</td> <td>Number of TLS capability checks failed</td> </tr> <tr> - <td><p id="jdisc_http_jetty_threadpool_thread_max">jdisc.http.jetty.threadpool.thread.max</p></td> + <td>jdisc.http.jetty.threadpool.thread.max</td> <td>thread</td> <td>Configured maximum number of threads</td> </tr> <tr> - <td><p id="jdisc_http_jetty_threadpool_thread_min">jdisc.http.jetty.threadpool.thread.min</p></td> + <td>jdisc.http.jetty.threadpool.thread.min</td> <td>thread</td> <td>Configured minimum number of threads</td> </tr> <tr> - <td><p id="jdisc_http_jetty_threadpool_thread_reserved">jdisc.http.jetty.threadpool.thread.reserved</p></td> + <td>jdisc.http.jetty.threadpool.thread.reserved</td> <td>thread</td> <td>Configured number of reserved threads or -1 for heuristic</td> </tr> <tr> - <td><p id="jdisc_http_jetty_threadpool_thread_busy">jdisc.http.jetty.threadpool.thread.busy</p></td> + <td>jdisc.http.jetty.threadpool.thread.busy</td> <td>thread</td> <td>Number of threads executing internal and transient jobs</td> </tr> <tr> - <td><p id="jdisc_http_jetty_threadpool_thread_idle">jdisc.http.jetty.threadpool.thread.idle</p></td> + <td>jdisc.http.jetty.threadpool.thread.idle</td> <td>thread</td> <td>Number of idle threads</td> </tr> <tr> - <td><p id="jdisc_http_jetty_threadpool_thread_total">jdisc.http.jetty.threadpool.thread.total</p></td> + <td>jdisc.http.jetty.threadpool.thread.total</td> <td>thread</td> <td>Current number of threads</td> </tr> <tr> - <td><p id="jdisc_http_jetty_threadpool_queue_size">jdisc.http.jetty.threadpool.queue.size</p></td> + <td>jdisc.http.jetty.threadpool.queue.size</td> <td>thread</td> <td>Current size of the job queue</td> </tr> <tr> - <td><p id="jdisc_http_jetty_http_compliance_violation">jdisc.http.jetty.http_compliance.violation</p></td> + <td>jdisc.http.jetty.http_compliance.violation</td> <td>failure</td> <td>Number of HTTP compliance violations</td> </tr> <tr> - <td><p id="serverNumOpenConnections">serverNumOpenConnections</p></td> + <td>serverNumOpenConnections</td> <td>connection</td> <td>The number of currently open connections</td> </tr> <tr> - <td><p id="serverNumConnections">serverNumConnections</p></td> + <td>serverNumConnections</td> <td>connection</td> <td>The total number of connections opened</td> </tr> <tr> - <td><p id="serverBytesReceived">serverBytesReceived</p></td> + <td>serverBytesReceived</td> <td>byte</td> <td>The number of bytes received by the server</td> </tr> <tr> - <td><p id="serverBytesSent">serverBytesSent</p></td> + <td>serverBytesSent</td> <td>byte</td> <td>The number of bytes sent from the server</td> </tr> <tr> - <td><p id="handled_requests">handled.requests</p></td> + <td>handled.requests</td> <td>operation</td> <td>The number of requests handled per metrics snapshot</td> </tr> <tr> - <td><p id="handled_latency">handled.latency</p></td> + <td>handled.latency</td> <td>millisecond</td> <td>The time used for handling requests, excluding HTTP layer and rendering</td> </tr> <tr> - <td><p id="httpapi_latency">httpapi_latency</p></td> + <td>httpapi_latency</td> <td>millisecond</td> <td>Duration for requests to the HTTP document APIs</td> </tr> <tr> - <td><p id="httpapi_pending">httpapi_pending</p></td> + <td>httpapi_pending</td> <td>operation</td> <td>Document operations pending execution</td> </tr> <tr> - <td><p id="httpapi_num_operations">httpapi_num_operations</p></td> + <td>httpapi_num_operations</td> <td>operation</td> <td>Total number of document operations performed</td> </tr> <tr> - <td><p id="httpapi_num_updates">httpapi_num_updates</p></td> + <td>httpapi_num_updates</td> <td>operation</td> <td>Document update operations performed</td> </tr> <tr> - <td><p id="httpapi_num_removes">httpapi_num_removes</p></td> + <td>httpapi_num_removes</td> <td>operation</td> <td>Document remove operations performed</td> </tr> <tr> - <td><p id="httpapi_num_puts">httpapi_num_puts</p></td> + <td>httpapi_num_puts</td> <td>operation</td> <td>Document put operations performed</td> </tr> <tr> - <td><p id="httpapi_ops_per_sec">httpapi_ops_per_sec</p></td> + <td>httpapi_ops_per_sec</td> <td>operation_per_second</td> <td>Document operations per second</td> </tr> <tr> - <td><p id="httpapi_succeeded">httpapi_succeeded</p></td> + <td>httpapi_succeeded</td> <td>operation</td> <td>Document operations that succeeded</td> </tr> <tr> - <td><p id="httpapi_failed">httpapi_failed</p></td> + <td>httpapi_failed</td> <td>operation</td> <td>Document operations that failed</td> </tr> <tr> - <td><p id="httpapi_parse_error">httpapi_parse_error</p></td> + <td>httpapi_parse_error</td> <td>operation</td> <td>Document operations that failed due to document parse errors</td> </tr> <tr> - <td><p id="httpapi_condition_not_met">httpapi_condition_not_met</p></td> + <td>httpapi_condition_not_met</td> <td>operation</td> <td>Document operations not applied due to condition not met</td> </tr> <tr> - <td><p id="httpapi_not_found">httpapi_not_found</p></td> + <td>httpapi_not_found</td> <td>operation</td> <td>Document operations not applied due to document not found</td> </tr> <tr> - <td><p id="httpapi_failed_unknown">httpapi_failed_unknown</p></td> + <td>httpapi_failed_unknown</td> <td>operation</td> <td>Document operations failed by unknown cause</td> </tr> <tr> - <td><p id="httpapi_failed_timeout">httpapi_failed_timeout</p></td> + <td>httpapi_failed_timeout</td> <td>operation</td> <td>Document operations failed by timeout</td> </tr> <tr> - <td><p id="httpapi_failed_insufficient_storage">httpapi_failed_insufficient_storage</p></td> + <td>httpapi_failed_insufficient_storage</td> <td>operation</td> <td>Document operations failed by insufficient storage</td> </tr> <tr> - <td><p id="httpapi_queued_operations">httpapi_queued_operations</p></td> + <td>httpapi_queued_operations</td> <td>operation</td> <td>Document operations queued for execution in /document/v1 API handler</td> </tr> <tr> - <td><p id="httpapi_queued_bytes">httpapi_queued_bytes</p></td> + <td>httpapi_queued_bytes</td> <td>byte</td> <td>Total operation bytes queued for execution in /document/v1 API handler</td> </tr> <tr> - <td><p id="httpapi_queued_age">httpapi_queued_age</p></td> + <td>httpapi_queued_age</td> <td>second</td> <td>Age in seconds of the oldest operation in the queue for /document/v1 API handler</td> </tr> <tr> - <td><p id="httpapi_mbus_window_size">httpapi_mbus_window_size</p></td> + <td>httpapi_mbus_window_size</td> <td>operation</td> <td>The window size of Messagebus's dynamic throttle policy for /document/v1 API handler</td> </tr> <tr> - <td><p id="mem_heap_total">mem.heap.total</p></td> + <td>mem.heap.total</td> <td>byte</td> <td>Total available heap memory</td> </tr> <tr> - <td><p id="mem_heap_free">mem.heap.free</p></td> + <td>mem.heap.free</td> <td>byte</td> <td>Free heap memory</td> </tr> <tr> - <td><p id="mem_heap_used">mem.heap.used</p></td> + <td>mem.heap.used</td> <td>byte</td> <td>Currently used heap memory</td> </tr> <tr> - <td><p id="mem_direct_total">mem.direct.total</p></td> + <td>mem.direct.total</td> <td>byte</td> <td>Total available direct memory</td> </tr> <tr> - <td><p id="mem_direct_free">mem.direct.free</p></td> + <td>mem.direct.free</td> <td>byte</td> <td>Currently free direct memory</td> </tr> <tr> - <td><p id="mem_direct_used">mem.direct.used</p></td> + <td>mem.direct.used</td> <td>byte</td> <td>Direct memory currently used</td> </tr> <tr> - <td><p id="mem_direct_count">mem.direct.count</p></td> + <td>mem.direct.count</td> <td>byte</td> <td>Number of direct memory allocations</td> </tr> <tr> - <td><p id="mem_native_total">mem.native.total</p></td> + <td>mem.native.total</td> <td>byte</td> <td>Total available native memory</td> </tr> <tr> - <td><p id="mem_native_free">mem.native.free</p></td> + <td>mem.native.free</td> <td>byte</td> <td>Currently free native memory</td> </tr> <tr> - <td><p id="mem_native_used">mem.native.used</p></td> + <td>mem.native.used</td> <td>byte</td> <td>Native memory currently used</td> </tr> <tr> - <td><p id="athenz-tenant-cert_expiry_seconds">athenz-tenant-cert.expiry.seconds</p></td> + <td>athenz-tenant-cert.expiry.seconds</td> <td>second</td> <td>Time remaining until Athenz tenant certificate expires</td> </tr> <tr> - <td><p id="container-iam-role_expiry_seconds">container-iam-role.expiry.seconds</p></td> + <td>container-iam-role.expiry.seconds</td> <td>second</td> <td>Time remaining until IAM role expires</td> </tr> <tr> - <td><p id="peak_qps">peak_qps</p></td> + <td>peak_qps</td> <td>query_per_second</td> <td>The highest number of qps for a second for this metrics snapshot</td> </tr> <tr> - <td><p id="search_connections">search_connections</p></td> + <td>search_connections</td> <td>connection</td> <td>Number of search connections</td> </tr> <tr> - <td><p id="feed_operations">feed.operations</p></td> + <td>feed.operations</td> <td>operation</td> <td>Number of document feed operations</td> </tr> <tr> - <td><p id="feed_latency">feed.latency</p></td> + <td>feed.latency</td> <td>millisecond</td> <td>Feed latency</td> </tr> <tr> - <td><p id="feed_http-requests">feed.http-requests</p></td> + <td>feed.http-requests</td> <td>operation</td> <td>Feed HTTP requests</td> </tr> <tr> - <td><p id="queries">queries</p></td> + <td>queries</td> <td>operation</td> <td>Query volume</td> </tr> <tr> - <td><p id="query_container_latency">query_container_latency</p></td> + <td>query_container_latency</td> <td>millisecond</td> <td>The query execution time consumed in the container</td> </tr> <tr> - <td><p id="query_latency">query_latency</p></td> + <td>query_latency</td> <td>millisecond</td> <td>The overall query latency as observed by the container cluster, excluding HTTP layer and rendering</td> </tr> <tr> - <td><p id="query_timeout">query_timeout</p></td> + <td>query_timeout</td> <td>millisecond</td> <td>The amount of time allowed for query execution, from the client</td> </tr> <tr> - <td><p id="failed_queries">failed_queries</p></td> + <td>failed_queries</td> <td>operation</td> <td>The number of failed queries</td> </tr> <tr> - <td><p id="degraded_queries">degraded_queries</p></td> + <td>degraded_queries</td> <td>operation</td> <td>The number of degraded queries, e.g. due to some content nodes not responding in time</td> </tr> <tr> - <td><p id="hits_per_query">hits_per_query</p></td> + <td>hits_per_query</td> <td>hit_per_query</td> <td>The number of hits returned</td> </tr> <tr> - <td><p id="query_hit_offset">query_hit_offset</p></td> + <td>query_hit_offset</td> <td>hit</td> <td>The offset for hits returned</td> </tr> <tr> - <td><p id="documents_covered">documents_covered</p></td> + <td>documents_covered</td> <td>document</td> <td>The combined number of documents considered during query evaluation</td> </tr> <tr> - <td><p id="documents_total">documents_total</p></td> + <td>documents_total</td> <td>document</td> <td>The number of documents to be evaluated if all requests had been fully executed</td> </tr> <tr> - <td><p id="documents_target_total">documents_target_total</p></td> + <td>documents_target_total</td> <td>document</td> <td>The target number of total documents to be evaluated when all data is in sync</td> </tr> <tr> - <td><p id="jdisc_render_latency">jdisc.render.latency</p></td> + <td>jdisc.render.latency</td> <td>nanosecond</td> <td>The time used by the container to render responses</td> </tr> <tr> - <td><p id="query_item_count">query_item_count</p></td> + <td>query_item_count</td> <td>item</td> <td>The number of query items (terms, phrases, etc.)</td> </tr> <tr> - <td><p id="docproc_proctime">docproc.proctime</p></td> + <td>docproc.proctime</td> <td>millisecond</td> <td>Time spent processing document</td> </tr> <tr> - <td><p id="docproc_documents">docproc.documents</p></td> + <td>docproc.documents</td> <td>document</td> <td>Number of processed documents</td> </tr> <tr> - <td><p id="totalhits_per_query">totalhits_per_query</p></td> + <td>totalhits_per_query</td> <td>hit_per_query</td> <td>The total number of documents found to match queries</td> </tr> <tr> - <td><p id="empty_results">empty_results</p></td> + <td>empty_results</td> <td>operation</td> <td>Number of queries matching no documents</td> </tr> <tr> - <td><p id="requestsOverQuota">requestsOverQuota</p></td> + <td>requestsOverQuota</td> <td>operation</td> <td>The number of requests rejected due to exceeding quota</td> </tr> <tr> - <td><p id="relevance_at_1">relevance.at_1</p></td> + <td>relevance.at_1</td> <td>score</td> <td>The relevance of hit number 1</td> </tr> <tr> - <td><p id="relevance_at_3">relevance.at_3</p></td> + <td>relevance.at_3</td> <td>score</td> <td>The relevance of hit number 3</td> </tr> <tr> - <td><p id="relevance_at_10">relevance.at_10</p></td> + <td>relevance.at_10</td> <td>score</td> <td>The relevance of hit number 10</td> </tr> <tr> - <td><p id="error_timeout">error.timeout</p></td> + <td>error.timeout</td> <td>operation</td> <td>Requests that timed out</td> </tr> <tr> - <td><p id="error_backends_oos">error.backends_oos</p></td> + <td>error.backends_oos</td> <td>operation</td> <td>Requests that failed due to no available backends nodes</td> </tr> <tr> - <td><p id="error_plugin_failure">error.plugin_failure</p></td> + <td>error.plugin_failure</td> <td>operation</td> <td>Requests that failed due to plugin failure</td> </tr> <tr> - <td><p id="error_backend_communication_error">error.backend_communication_error</p></td> + <td>error.backend_communication_error</td> <td>operation</td> <td>Requests that failed due to backend communication error</td> </tr> <tr> - <td><p id="error_empty_document_summaries">error.empty_document_summaries</p></td> + <td>error.empty_document_summaries</td> <td>operation</td> <td>Requests that failed due to missing document summaries</td> </tr> <tr> - <td><p id="error_illegal_query">error.illegal_query</p></td> + <td>error.illegal_query</td> <td>operation</td> <td>Requests that failed due to illegal queries</td> </tr> <tr> - <td><p id="error_invalid_query_parameter">error.invalid_query_parameter</p></td> + <td>error.invalid_query_parameter</td> <td>operation</td> <td>Requests that failed due to invalid query parameters</td> </tr> <tr> - <td><p id="error_internal_server_error">error.internal_server_error</p></td> + <td>error.internal_server_error</td> <td>operation</td> <td>Requests that failed due to internal server error</td> </tr> <tr> - <td><p id="error_misconfigured_server">error.misconfigured_server</p></td> + <td>error.misconfigured_server</td> <td>operation</td> <td>Requests that failed due to misconfigured server</td> </tr> <tr> - <td><p id="error_invalid_query_transformation">error.invalid_query_transformation</p></td> + <td>error.invalid_query_transformation</td> <td>operation</td> <td>Requests that failed due to invalid query transformation</td> </tr> <tr> - <td><p id="error_results_with_errors">error.results_with_errors</p></td> + <td>error.results_with_errors</td> <td>operation</td> <td>The number of queries with error payload</td> </tr> <tr> - <td><p id="error_unspecified">error.unspecified</p></td> + <td>error.unspecified</td> <td>operation</td> <td>Requests that failed for an unspecified reason</td> </tr> <tr> - <td><p id="error_unhandled_exception">error.unhandled_exception</p></td> + <td>error.unhandled_exception</td> <td>operation</td> <td>Requests that failed due to an unhandled exception</td> </tr> <tr> - <td><p id="serverRejectedRequests">serverRejectedRequests</p></td> + <td>serverRejectedRequests</td> <td>operation</td> <td>Deprecated. Use jdisc.thread_pool.rejected_tasks instead.</td> </tr> <tr> - <td><p id="serverThreadPoolSize">serverThreadPoolSize</p></td> + <td>serverThreadPoolSize</td> <td>thread</td> <td>Deprecated. Use jdisc.thread_pool.size instead.</td> </tr> <tr> - <td><p id="serverActiveThreads">serverActiveThreads</p></td> + <td>serverActiveThreads</td> <td>thread</td> <td>Deprecated. Use jdisc.thread_pool.active_threads instead.</td> </tr> <tr> - <td><p id="jrt_transport_tls-certificate-verification-failures">jrt.transport.tls-certificate-verification-failures</p></td> + <td>jrt.transport.tls-certificate-verification-failures</td> <td>failure</td> <td>TLS certificate verification failures</td> </tr> <tr> - <td><p id="jrt_transport_peer-authorization-failures">jrt.transport.peer-authorization-failures</p></td> + <td>jrt.transport.peer-authorization-failures</td> <td>failure</td> <td>TLS peer authorization failures</td> </tr> <tr> - <td><p id="jrt_transport_server_tls-connections-established">jrt.transport.server.tls-connections-established</p></td> + <td>jrt.transport.server.tls-connections-established</td> <td>connection</td> <td>TLS server connections established</td> </tr> <tr> - <td><p id="jrt_transport_client_tls-connections-established">jrt.transport.client.tls-connections-established</p></td> + <td>jrt.transport.client.tls-connections-established</td> <td>connection</td> <td>TLS client connections established</td> </tr> <tr> - <td><p id="jrt_transport_server_unencrypted-connections-established">jrt.transport.server.unencrypted-connections-established</p></td> + <td>jrt.transport.server.unencrypted-connections-established</td> <td>connection</td> <td>Unencrypted server connections established</td> </tr> <tr> - <td><p id="jrt_transport_client_unencrypted-connections-established">jrt.transport.client.unencrypted-connections-established</p></td> + <td>jrt.transport.client.unencrypted-connections-established</td> <td>connection</td> <td>Unencrypted client connections established</td> </tr> <tr> - <td><p id="max_query_latency">max_query_latency</p></td> + <td>max_query_latency</td> <td>millisecond</td> <td>Deprecated. Use query_latency.max instead</td> </tr> <tr> - <td><p id="mean_query_latency">mean_query_latency</p></td> + <td>mean_query_latency</td> <td>millisecond</td> <td>Deprecated. Use the expression (query_latency.sum / query_latency.count) instead</td> </tr> <tr> - <td><p id="jdisc_http_filter_athenz_accepted_requests">jdisc.http.filter.athenz.accepted_requests</p></td> + <td>jdisc.http.filter.athenz.accepted_requests</td> <td>request</td> <td>Number of requests accepted by the AthenzAuthorization filter</td> </tr> <tr> - <td><p id="jdisc_http_filter_athenz_rejected_requests">jdisc.http.filter.athenz.rejected_requests</p></td> + <td>jdisc.http.filter.athenz.rejected_requests</td> <td>request</td> <td>Number of requests rejected by the AthenzAuthorization filter</td> </tr> <tr> - <td><p id="jdisc_http_filter_athenz_grid_requests">jdisc.http.filter.athenz.grid_requests</p></td> + <td>jdisc.http.filter.athenz.grid_requests</td> <td>request</td> <td>Number of grid requests</td> </tr> <tr> - <td><p id="serverConnectionsOpenMax">serverConnectionsOpenMax</p></td> + <td>serverConnectionsOpenMax</td> <td>connection</td> <td>Maximum number of open connections</td> </tr> <tr> - <td><p id="serverConnectionDurationMax">serverConnectionDurationMax</p></td> + <td>serverConnectionDurationMax</td> <td>millisecond</td> <td>Longest duration a connection is kept open</td> </tr> <tr> - <td><p id="serverConnectionDurationMean">serverConnectionDurationMean</p></td> + <td>serverConnectionDurationMean</td> <td>millisecond</td> <td>Average duration a connection is kept open</td> </tr> <tr> - <td><p id="serverConnectionDurationStdDev">serverConnectionDurationStdDev</p></td> + <td>serverConnectionDurationStdDev</td> <td>millisecond</td> <td>Standard deviation of open connection duration</td> </tr> <tr> - <td><p id="serverNumRequests">serverNumRequests</p></td> + <td>serverNumRequests</td> <td>request</td> <td>Number of requests</td> </tr> <tr> - <td><p id="serverNumSuccessfulResponses">serverNumSuccessfulResponses</p></td> + <td>serverNumSuccessfulResponses</td> <td>request</td> <td>Number of successful responses</td> </tr> <tr> - <td><p id="serverNumFailedResponses">serverNumFailedResponses</p></td> + <td>serverNumFailedResponses</td> <td>request</td> <td>Number of failed responses</td> </tr> <tr> - <td><p id="serverNumSuccessfulResponseWrites">serverNumSuccessfulResponseWrites</p></td> + <td>serverNumSuccessfulResponseWrites</td> <td>request</td> <td>Number of successful response writes</td> </tr> <tr> - <td><p id="serverNumFailedResponseWrites">serverNumFailedResponseWrites</p></td> + <td>serverNumFailedResponseWrites</td> <td>request</td> <td>Number of failed response writes</td> </tr> <tr> - <td><p id="serverStartedMillis">serverStartedMillis</p></td> + <td>serverStartedMillis</td> <td>millisecond</td> <td>Time since the service was started</td> </tr> <tr> - <td><p id="embedder_latency">embedder.latency</p></td> + <td>embedder.latency</td> <td>millisecond</td> <td>Time spent creating an embedding</td> </tr> <tr> - <td><p id="embedder_sequence_length">embedder.sequence_length</p></td> + <td>embedder.sequence_length</td> <td>item</td> <td>Number of tokens in the input sequence</td> </tr> <tr> - <td><p id="embedder_request_count">embedder.request.count</p></td> + <td>embedder.request.count</td> <td>request</td> <td>Number of embedder API requests</td> </tr> <tr> - <td><p id="embedder_request_failure_count">embedder.request.failure.count</p></td> + <td>embedder.request.failure.count</td> <td>request</td> <td>Number of failed embedder API requests</td> </tr> <tr> - <td><p id="embedder_batch_size">embedder.batch.size</p></td> + <td>embedder.batch.size</td> <td>item</td> <td>Number of items in each dispatched batch</td> </tr> <tr> - <td><p id="embedder_batch_queue_time">embedder.batch.queue_time</p></td> + <td>embedder.batch.queue_time</td> <td>millisecond</td> <td>Time spent waiting in queue before batch dispatch</td> </tr> <tr> - <td><p id="embedder_batch_count">embedder.batch.count</p></td> + <td>embedder.batch.count</td> <td>operation</td> <td>Number of batch dispatches</td> </tr> <tr> - <td><p id="inference_pending">inference.pending</p></td> + <td>inference.pending</td> <td>item</td> <td>Number of pending inference requests in a queue</td> </tr> <tr> - <td><p id="inference_request_rate">inference.request.rate</p></td> + <td>inference.request.rate</td> <td>operation_per_second</td> <td>Successful inference requests per second</td> </tr> <tr> - <td><p id="inference_failure_rate">inference.failure.rate</p></td> + <td>inference.failure.rate</td> <td>operation_per_second</td> <td>Failed inference requests per second</td> </tr> <tr> - <td><p id="inference_request_latency">inference.request.latency</p></td> + <td>inference.request.latency</td> <td>millisecond</td> <td>Average inference request latency</td> </tr> <tr> - <td><p id="inference_queue_latency">inference.queue.latency</p></td> + <td>inference.queue.latency</td> <td>millisecond</td> <td>Average inference queue latency</td> </tr> <tr> - <td><p id="inference_compute_latency">inference.compute.latency</p></td> + <td>inference.compute.latency</td> <td>millisecond</td> <td>Average inference compute latency</td> </tr> <tr> - <td><p id="inference_queue_compute_ratio">inference.queue_compute.ratio</p></td> + <td>inference.queue_compute.ratio</td> <td>ratio</td> <td>Ratio of inference queue time to compute time</td> </tr> <tr> - <td><p id="jvm_buffer_count">jvm.buffer.count</p></td> + <td>jvm.buffer.count</td> <td>buffer</td> <td>An estimate of the number of buffers in the pool</td> </tr> <tr> - <td><p id="jvm_buffer_memory_used">jvm.buffer.memory.used</p></td> + <td>jvm.buffer.memory.used</td> <td>byte</td> <td>An estimate of the memory that the Java virtual machine is using for this buffer pool</td> </tr> <tr> - <td><p id="jvm_buffer_total_capacity">jvm.buffer.total.capacity</p></td> + <td>jvm.buffer.total.capacity</td> <td>byte</td> <td>An estimate of the total capacity of the buffers in this pool</td> </tr> <tr> - <td><p id="jvm_classes_loaded">jvm.classes.loaded</p></td> + <td>jvm.classes.loaded</td> <td>class</td> <td>The number of classes that are currently loaded in the Java virtual machine</td> </tr> <tr> - <td><p id="jvm_classes_unloaded">jvm.classes.unloaded</p></td> + <td>jvm.classes.unloaded</td> <td>class</td> <td>The total number of classes unloaded since the Java virtual machine has started execution</td> </tr> <tr> - <td><p id="jvm_gc_concurrent_phase_time">jvm.gc.concurrent.phase.time</p></td> + <td>jvm.gc.concurrent.phase.time</td> <td>second</td> <td>Time spent in concurrent phase</td> </tr> <tr> - <td><p id="jvm_gc_live_data_size">jvm.gc.live.data.size</p></td> + <td>jvm.gc.live.data.size</td> <td>byte</td> <td>Size of long-lived heap memory pool after reclamation</td> </tr> <tr> - <td><p id="jvm_gc_max_data_size">jvm.gc.max.data.size</p></td> + <td>jvm.gc.max.data.size</td> <td>byte</td> <td>Max size of long-lived heap memory pool</td> </tr> <tr> - <td><p id="jvm_gc_memory_allocated">jvm.gc.memory.allocated</p></td> + <td>jvm.gc.memory.allocated</td> <td>byte</td> <td>Incremented for an increase in the size of the (young) heap memory pool after one GC to before the next</td> </tr> <tr> - <td><p id="jvm_gc_memory_promoted">jvm.gc.memory.promoted</p></td> + <td>jvm.gc.memory.promoted</td> <td>byte</td> <td>Count of positive increases in the size of the old generation memory pool before GC to after GC</td> </tr> <tr> - <td><p id="jvm_gc_overhead">jvm.gc.overhead</p></td> + <td>jvm.gc.overhead</td> <td>percentage</td> <td>An approximation of the percent of CPU time used by GC activities</td> </tr> <tr> - <td><p id="jvm_gc_pause">jvm.gc.pause</p></td> + <td>jvm.gc.pause</td> <td>second</td> <td>Time spent in GC pause</td> </tr> <tr> - <td><p id="jvm_memory_committed">jvm.memory.committed</p></td> + <td>jvm.memory.committed</td> <td>byte</td> <td>The amount of memory in bytes that is committed for the Java virtual machine to use</td> </tr> <tr> - <td><p id="jvm_memory_max">jvm.memory.max</p></td> + <td>jvm.memory.max</td> <td>byte</td> <td>The maximum amount of memory in bytes that can be used for memory management</td> </tr> <tr> - <td><p id="jvm_memory_usage_after_gc">jvm.memory.usage.after.gc</p></td> + <td>jvm.memory.usage.after.gc</td> <td>percentage</td> <td>The percentage of long-lived heap pool used after the last GC event</td> </tr> <tr> - <td><p id="jvm_memory_used">jvm.memory.used</p></td> + <td>jvm.memory.used</td> <td>byte</td> <td>The amount of used memory</td> </tr> <tr> - <td><p id="jvm_threads_daemon">jvm.threads.daemon</p></td> + <td>jvm.threads.daemon</td> <td>thread</td> <td>The current number of live daemon threads</td> </tr> <tr> - <td><p id="jvm_threads_live">jvm.threads.live</p></td> + <td>jvm.threads.live</td> <td>thread</td> <td>The current number of live threads including both daemon and non-daemon threads</td> </tr> <tr> - <td><p id="jvm_threads_peak">jvm.threads.peak</p></td> + <td>jvm.threads.peak</td> <td>thread</td> <td>The peak live thread count since the Java virtual machine started or peak was reset</td> </tr> <tr> - <td><p id="jvm_threads_started">jvm.threads.started</p></td> + <td>jvm.threads.started</td> <td>thread</td> <td>The total number of application threads started in the JVM</td> </tr> <tr> - <td><p id="jvm_threads_states">jvm.threads.states</p></td> + <td>jvm.threads.states</td> <td>thread</td> <td>The current number of threads (in each state)</td> </tr> </tbody> -</table> +</table> \ No newline at end of file diff --git a/en/reference/operations/metrics/default-metric-set.html b/en/reference/operations/metrics/default-metric-set.html deleted file mode 100644 index 790044a67a..0000000000 --- a/en/reference/operations/metrics/default-metric-set.html +++ /dev/null @@ -1,440 +0,0 @@ ---- -# Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -# Note: This file is generated by -# https://github.com/vespa-engine/vespa/blob/master/metrics/src/main/java/ai/vespa/metrics/docs/MetricSetDocumentation.java -title: "Default Metric Set" -redirect_from: -- en/reference/default-set-metrics-reference.html ---- -<p> - This document provides reference documentation for the Default metric set, including suffixes present per metric. - If the suffix column contains "N/A" then the base name of the corresponding metric is used with no suffix. -</p> -<h2 id="clustercontroller-metrics">ClusterController Metrics</h2> -<table class="table"> - <thead> - <tr><th>Name</th><th>Unit</th><th>Suffixes</th><th>Description</th></tr> - </thead> - <tbody> - <tr> - <td><p id="cluster-controller_down_count">cluster-controller.down.count</p></td> - <td>node</td> - <td>last, max</td> - <td>Number of content nodes down</td> - </tr> - <tr> - <td><p id="cluster-controller_maintenance_count">cluster-controller.maintenance.count</p></td> - <td>node</td> - <td>last, max</td> - <td>Number of content nodes in maintenance</td> - </tr> - <tr> - <td><p id="cluster-controller_up_count">cluster-controller.up.count</p></td> - <td>node</td> - <td>last, max</td> - <td>Number of content nodes up</td> - </tr> - <tr> - <td><p id="cluster-controller_is-master">cluster-controller.is-master</p></td> - <td>binary</td> - <td>last, max</td> - <td>1 if this cluster controller is currently the master, or 0 if not</td> - </tr> - <tr> - <td><p id="cluster-controller_resource_usage_nodes_above_limit">cluster-controller.resource_usage.nodes_above_limit</p></td> - <td>node</td> - <td>last, max</td> - <td>The number of content nodes above resource limit, blocking feed</td> - </tr> - <tr> - <td><p id="cluster-controller_resource_usage_max_memory_utilization">cluster-controller.resource_usage.max_memory_utilization</p></td> - <td>fraction</td> - <td>last, max</td> - <td>Current memory utilisation, for content node with the highest value</td> - </tr> - <tr> - <td><p id="cluster-controller_resource_usage_max_disk_utilization">cluster-controller.resource_usage.max_disk_utilization</p></td> - <td>fraction</td> - <td>last, max</td> - <td>Current disk space utilisation, for content node with the highest value</td> - </tr> - </tbody> -</table> - -<h2 id="container-metrics">Container Metrics</h2> -<table class="table"> - <thead> - <tr><th>Name</th><th>Unit</th><th>Suffixes</th><th>Description</th></tr> - </thead> - <tbody> - <tr> - <td><p id="http_status_1xx">http.status.1xx</p></td> - <td>response</td> - <td>rate</td> - <td>Number of responses with a 1xx status</td> - </tr> - <tr> - <td><p id="http_status_2xx">http.status.2xx</p></td> - <td>response</td> - <td>rate</td> - <td>Number of responses with a 2xx status</td> - </tr> - <tr> - <td><p id="http_status_3xx">http.status.3xx</p></td> - <td>response</td> - <td>rate</td> - <td>Number of responses with a 3xx status</td> - </tr> - <tr> - <td><p id="http_status_4xx">http.status.4xx</p></td> - <td>response</td> - <td>rate</td> - <td>Number of responses with a 4xx status</td> - </tr> - <tr> - <td><p id="http_status_5xx">http.status.5xx</p></td> - <td>response</td> - <td>rate</td> - <td>Number of responses with a 5xx status</td> - </tr> - <tr> - <td><p id="jdisc_gc_ms">jdisc.gc.ms</p></td> - <td>millisecond</td> - <td>average, max</td> - <td>Time spent in JVM garbage collection</td> - </tr> - <tr> - <td><p id="jdisc_thread_pool_work_queue_capacity">jdisc.thread_pool.work_queue.capacity</p></td> - <td>thread</td> - <td>max</td> - <td>Capacity of the task queue</td> - </tr> - <tr> - <td><p id="jdisc_thread_pool_work_queue_size">jdisc.thread_pool.work_queue.size</p></td> - <td>thread</td> - <td>count, max, min, sum</td> - <td>Size of the task queue</td> - </tr> - <tr> - <td><p id="jdisc_thread_pool_size">jdisc.thread_pool.size</p></td> - <td>thread</td> - <td>max</td> - <td>Size of the thread pool</td> - </tr> - <tr> - <td><p id="jdisc_thread_pool_active_threads">jdisc.thread_pool.active_threads</p></td> - <td>thread</td> - <td>count, max, min, sum</td> - <td>Number of threads that are active</td> - </tr> - <tr> - <td><p id="jdisc_application_failed_component_graphs">jdisc.application.failed_component_graphs</p></td> - <td>item</td> - <td>rate</td> - <td>JDISC Application failed component graphs</td> - </tr> - <tr> - <td><p id="jdisc_singleton_is_active">jdisc.singleton.is_active</p></td> - <td>item</td> - <td>last, max</td> - <td>JDISC Singleton is active</td> - </tr> - <tr> - <td><p id="jdisc_http_ssl_handshake_failure_missing_client_cert">jdisc.http.ssl.handshake.failure.missing_client_cert</p></td> - <td>operation</td> - <td>rate</td> - <td>JDISC HTTP SSL Handshake failures due to missing client certificate</td> - </tr> - <tr> - <td><p id="jdisc_http_ssl_handshake_failure_incompatible_protocols">jdisc.http.ssl.handshake.failure.incompatible_protocols</p></td> - <td>operation</td> - <td>rate</td> - <td>JDISC HTTP SSL Handshake failures due to incompatible protocols</td> - </tr> - <tr> - <td><p id="jdisc_http_ssl_handshake_failure_incompatible_chifers">jdisc.http.ssl.handshake.failure.incompatible_chifers</p></td> - <td>operation</td> - <td>rate</td> - <td>JDISC HTTP SSL Handshake failures due to incompatible chifers</td> - </tr> - <tr> - <td><p id="jdisc_http_ssl_handshake_failure_unknown">jdisc.http.ssl.handshake.failure.unknown</p></td> - <td>operation</td> - <td>rate</td> - <td>JDISC HTTP SSL Handshake failures for unknown reason</td> - </tr> - <tr> - <td><p id="jdisc_http_latency">jdisc.http.latency</p></td> - <td>millisecond</td> - <td>count, max, sum</td> - <td>Request latency including the HTTP layer</td> - </tr> - <tr> - <td><p id="mem_heap_free">mem.heap.free</p></td> - <td>byte</td> - <td>average</td> - <td>Free heap memory</td> - </tr> - <tr> - <td><p id="athenz-tenant-cert_expiry_seconds">athenz-tenant-cert.expiry.seconds</p></td> - <td>second</td> - <td>last, max, min</td> - <td>Time remaining until Athenz tenant certificate expires</td> - </tr> - <tr> - <td><p id="feed_operations">feed.operations</p></td> - <td>operation</td> - <td>rate</td> - <td>Number of document feed operations</td> - </tr> - <tr> - <td><p id="feed_latency">feed.latency</p></td> - <td>millisecond</td> - <td>count, sum</td> - <td>Feed latency</td> - </tr> - <tr> - <td><p id="queries">queries</p></td> - <td>operation</td> - <td>rate</td> - <td>Query volume</td> - </tr> - <tr> - <td><p id="query_latency">query_latency</p></td> - <td>millisecond</td> - <td>average, count, max, sum</td> - <td>The overall query latency as observed by the container cluster, excluding HTTP layer and rendering</td> - </tr> - <tr> - <td><p id="failed_queries">failed_queries</p></td> - <td>operation</td> - <td>rate</td> - <td>The number of failed queries</td> - </tr> - <tr> - <td><p id="degraded_queries">degraded_queries</p></td> - <td>operation</td> - <td>rate</td> - <td>The number of degraded queries, e.g. due to some content nodes not responding in time</td> - </tr> - <tr> - <td><p id="hits_per_query">hits_per_query</p></td> - <td>hit_per_query</td> - <td>average, count, max, sum</td> - <td>The number of hits returned</td> - </tr> - <tr> - <td><p id="docproc_documents">docproc.documents</p></td> - <td>document</td> - <td>sum</td> - <td>Number of processed documents</td> - </tr> - <tr> - <td><p id="totalhits_per_query">totalhits_per_query</p></td> - <td>hit_per_query</td> - <td>average, count, max, sum</td> - <td>The total number of documents found to match queries</td> - </tr> - <tr> - <td><p id="serverActiveThreads">serverActiveThreads</p></td> - <td>thread</td> - <td>average</td> - <td>Deprecated. Use jdisc.thread_pool.active_threads instead.</td> - </tr> - </tbody> -</table> - -<h2 id="distributor-metrics">Distributor Metrics</h2> -<table class="table"> - <thead> - <tr><th>Name</th><th>Unit</th><th>Suffixes</th><th>Description</th></tr> - </thead> - <tbody> - <tr> - <td><p id="vds_distributor_docsstored">vds.distributor.docsstored</p></td> - <td>document</td> - <td>average</td> - <td>Number of documents stored in all buckets controlled by this distributor</td> - </tr> - <tr> - <td><p id="vds_bouncer_clock_skew_aborts">vds.bouncer.clock_skew_aborts</p></td> - <td>operation</td> - <td>count</td> - <td>Number of client operations that were aborted due to clock skew between sender and receiver exceeding acceptable range</td> - </tr> - </tbody> -</table> - -<h2 id="nodeadmin-metrics">NodeAdmin Metrics</h2> -<table class="table"> - <thead> - <tr><th>Name</th><th>Unit</th><th>Suffixes</th><th>Description</th></tr> - </thead> - <tbody> - <tr> - <td><p id="endpoint_certificate_expiry_seconds">endpoint.certificate.expiry.seconds</p></td> - <td>second</td> - <td>N/A</td> - <td>Time until node endpoint certificate expires</td> - </tr> - <tr> - <td><p id="node-certificate_expiry_seconds">node-certificate.expiry.seconds</p></td> - <td>second</td> - <td>N/A</td> - <td>Time until node certificate expires</td> - </tr> - </tbody> -</table> - -<h2 id="searchnode-metrics">SearchNode Metrics</h2> -<table class="table"> - <thead> - <tr><th>Name</th><th>Unit</th><th>Suffixes</th><th>Description</th></tr> - </thead> - <tbody> - <tr> - <td><p id="content_proton_documentdb_documents_total">content.proton.documentdb.documents.total</p></td> - <td>document</td> - <td>last, max</td> - <td>The total number of documents in this documents db (ready + not-ready)</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_documents_ready">content.proton.documentdb.documents.ready</p></td> - <td>document</td> - <td>last, max</td> - <td>The number of ready documents in this document db</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_documents_active">content.proton.documentdb.documents.active</p></td> - <td>document</td> - <td>last, max</td> - <td>The number of active / searchable documents in this document db</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_disk_usage">content.proton.documentdb.disk_usage</p></td> - <td>byte</td> - <td>last</td> - <td>The total disk usage (in bytes) for this document db</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_memory_usage_allocated_bytes">content.proton.documentdb.memory_usage.allocated_bytes</p></td> - <td>byte</td> - <td>last</td> - <td>The number of allocated bytes</td> - </tr> - <tr> - <td><p id="content_proton_search_protocol_query_latency">content.proton.search_protocol.query.latency</p></td> - <td>second</td> - <td>average, count, max, sum</td> - <td>Query request latency (seconds)</td> - </tr> - <tr> - <td><p id="content_proton_search_protocol_docsum_latency">content.proton.search_protocol.docsum.latency</p></td> - <td>second</td> - <td>average, count, max, sum</td> - <td>Docsum request latency (seconds)</td> - </tr> - <tr> - <td><p id="content_proton_search_protocol_docsum_requested_documents">content.proton.search_protocol.docsum.requested_documents</p></td> - <td>document</td> - <td>rate</td> - <td>Total requested document summaries</td> - </tr> - <tr> - <td><p id="content_proton_resource_usage_disk">content.proton.resource_usage.disk</p></td> - <td>fraction</td> - <td>average</td> - <td>The relative amount of disk used by this content node (transient usage not included, value in the range [0, 1]). Same value as reported to the cluster controller</td> - </tr> - <tr> - <td><p id="content_proton_resource_usage_memory">content.proton.resource_usage.memory</p></td> - <td>fraction</td> - <td>average</td> - <td>The relative amount of memory used by this content node (transient usage not included, value in the range [0, 1]). Same value as reported to the cluster controller</td> - </tr> - <tr> - <td><p id="content_proton_resource_usage_feeding_blocked">content.proton.resource_usage.feeding_blocked</p></td> - <td>binary</td> - <td>last, max</td> - <td>Whether feeding is blocked due to resource limits being reached (value is either 0 or 1)</td> - </tr> - <tr> - <td><p id="content_proton_transactionlog_disk_usage">content.proton.transactionlog.disk_usage</p></td> - <td>byte</td> - <td>last</td> - <td>The disk usage (in bytes) of the transaction log</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_matching_docs_matched">content.proton.documentdb.matching.docs_matched</p></td> - <td>document</td> - <td>rate</td> - <td>Number of documents matched</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_matching_docs_reranked">content.proton.documentdb.matching.docs_reranked</p></td> - <td>document</td> - <td>rate</td> - <td>Number of documents re-ranked (second phase)</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_matching_rank_profile_query_latency">content.proton.documentdb.matching.rank_profile.query_latency</p></td> - <td>second</td> - <td>average, count, max, sum</td> - <td>Total average latency (sec) when matching and ranking a query</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_matching_rank_profile_query_setup_time">content.proton.documentdb.matching.rank_profile.query_setup_time</p></td> - <td>second</td> - <td>average, count, max, sum</td> - <td>Average time (sec) spent setting up and tearing down queries</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_matching_rank_profile_rerank_time">content.proton.documentdb.matching.rank_profile.rerank_time</p></td> - <td>second</td> - <td>average, count, max, sum</td> - <td>Average time (sec) spent on 2nd phase ranking</td> - </tr> - </tbody> -</table> - -<h2 id="sentinel-metrics">Sentinel Metrics</h2> -<table class="table"> - <thead> - <tr><th>Name</th><th>Unit</th><th>Suffixes</th><th>Description</th></tr> - </thead> - <tbody> - <tr> - <td><p id="sentinel_totalRestarts">sentinel.totalRestarts</p></td> - <td>restart</td> - <td>last, max, sum</td> - <td>Total number of service restarts done by the sentinel since the sentinel was started</td> - </tr> - </tbody> -</table> - -<h2 id="storage-metrics">Storage Metrics</h2> -<table class="table"> - <thead> - <tr><th>Name</th><th>Unit</th><th>Suffixes</th><th>Description</th></tr> - </thead> - <tbody> - <tr> - <td><p id="vds_filestor_allthreads_put_count">vds.filestor.allthreads.put.count</p></td> - <td>operation</td> - <td>rate</td> - <td>Number of requests processed.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_remove_count">vds.filestor.allthreads.remove.count</p></td> - <td>operation</td> - <td>rate</td> - <td>Number of requests processed.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_update_count">vds.filestor.allthreads.update.count</p></td> - <td>request</td> - <td>rate</td> - <td>Number of requests processed.</td> - </tr> - </tbody> -</table> diff --git a/en/reference/operations/metrics/default-metric-set.mdx b/en/reference/operations/metrics/default-metric-set.mdx new file mode 100644 index 0000000000..a10b4d1558 --- /dev/null +++ b/en/reference/operations/metrics/default-metric-set.mdx @@ -0,0 +1,477 @@ +--- +title: "Default Metric Set" +sidebarTitle: "Metrics" +--- + +This document provides reference documentation for the Default metric set, including suffixes present per metric. If the suffix column contains "N/A" then the base name of the corresponding metric is used with no suffix. + +## ClusterController Metrics + +<table className="vespa-table"> + <thead> + <tr> + <th>Name</th> + <th>Unit</th> + <th>Suffixes</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>cluster-controller.down.count</td> + <td>node</td> + <td>last, max</td> + <td>Number of content nodes down</td> + </tr> + <tr> + <td>cluster-controller.maintenance.count</td> + <td>node</td> + <td>last, max</td> + <td>Number of content nodes in maintenance</td> + </tr> + <tr> + <td>cluster-controller.up.count</td> + <td>node</td> + <td>last, max</td> + <td>Number of content nodes up</td> + </tr> + <tr> + <td>cluster-controller.is-master</td> + <td>binary</td> + <td>last, max</td> + <td>1 if this cluster controller is currently the master, or 0 if not</td> + </tr> + <tr> + <td>cluster-controller.resource_usage.nodes_above_limit</td> + <td>node</td> + <td>last, max</td> + <td>The number of content nodes above resource limit, blocking feed</td> + </tr> + <tr> + <td>cluster-controller.resource_usage.max_memory_utilization</td> + <td>fraction</td> + <td>last, max</td> + <td>Current memory utilisation, for content node with the highest value</td> + </tr> + <tr> + <td>cluster-controller.resource_usage.max_disk_utilization</td> + <td>fraction</td> + <td>last, max</td> + <td>Current disk space utilisation, for content node with the highest value</td> + </tr> + </tbody> +</table> + +## Container Metrics + +<table className="vespa-table"> + <thead> + <tr> + <th>Name</th> + <th>Unit</th> + <th>Suffixes</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>http.status.1xx</td> + <td>response</td> + <td>rate</td> + <td>Number of responses with a 1xx status</td> + </tr> + <tr> + <td>http.status.2xx</td> + <td>response</td> + <td>rate</td> + <td>Number of responses with a 2xx status</td> + </tr> + <tr> + <td>http.status.3xx</td> + <td>response</td> + <td>rate</td> + <td>Number of responses with a 3xx status</td> + </tr> + <tr> + <td>http.status.4xx</td> + <td>response</td> + <td>rate</td> + <td>Number of responses with a 4xx status</td> + </tr> + <tr> + <td>http.status.5xx</td> + <td>response</td> + <td>rate</td> + <td>Number of responses with a 5xx status</td> + </tr> + <tr> + <td>jdisc.gc.ms</td> + <td>millisecond</td> + <td>average, max</td> + <td>Time spent in JVM garbage collection</td> + </tr> + <tr> + <td>jdisc.thread_pool.work_queue.capacity</td> + <td>thread</td> + <td>max</td> + <td>Capacity of the task queue</td> + </tr> + <tr> + <td>jdisc.thread_pool.work_queue.size</td> + <td>thread</td> + <td>count, max, min, sum</td> + <td>Size of the task queue</td> + </tr> + <tr> + <td>jdisc.thread_pool.size</td> + <td>thread</td> + <td>max</td> + <td>Size of the thread pool</td> + </tr> + <tr> + <td>jdisc.thread_pool.active_threads</td> + <td>thread</td> + <td>count, max, min, sum</td> + <td>Number of threads that are active</td> + </tr> + <tr> + <td>jdisc.application.failed_component_graphs</td> + <td>item</td> + <td>rate</td> + <td>JDISC Application failed component graphs</td> + </tr> + <tr> + <td>jdisc.singleton.is_active</td> + <td>item</td> + <td>last, max</td> + <td>JDISC Singleton is active</td> + </tr> + <tr> + <td>jdisc.http.ssl.handshake.failure.missing_client_cert</td> + <td>operation</td> + <td>rate</td> + <td>JDISC HTTP SSL Handshake failures due to missing client certificate</td> + </tr> + <tr> + <td>jdisc.http.ssl.handshake.failure.incompatible_protocols</td> + <td>operation</td> + <td>rate</td> + <td>JDISC HTTP SSL Handshake failures due to incompatible protocols</td> + </tr> + <tr> + <td>jdisc.http.ssl.handshake.failure.incompatible_chifers</td> + <td>operation</td> + <td>rate</td> + <td>JDISC HTTP SSL Handshake failures due to incompatible chifers</td> + </tr> + <tr> + <td>jdisc.http.ssl.handshake.failure.unknown</td> + <td>operation</td> + <td>rate</td> + <td>JDISC HTTP SSL Handshake failures for unknown reason</td> + </tr> + <tr> + <td>jdisc.http.latency</td> + <td>millisecond</td> + <td>count, max, sum</td> + <td>Request latency including the HTTP layer</td> + </tr> + <tr> + <td>mem.heap.free</td> + <td>byte</td> + <td>average</td> + <td>Free heap memory</td> + </tr> + <tr> + <td>athenz-tenant-cert.expiry.seconds</td> + <td>second</td> + <td>last, max, min</td> + <td>Time remaining until Athenz tenant certificate expires</td> + </tr> + <tr> + <td>feed.operations</td> + <td>operation</td> + <td>rate</td> + <td>Number of document feed operations</td> + </tr> + <tr> + <td>feed.latency</td> + <td>millisecond</td> + <td>count, sum</td> + <td>Feed latency</td> + </tr> + <tr> + <td>queries</td> + <td>operation</td> + <td>rate</td> + <td>Query volume</td> + </tr> + <tr> + <td>query_latency</td> + <td>millisecond</td> + <td>average, count, max, sum</td> + <td>The overall query latency as observed by the container cluster, excluding HTTP layer and rendering</td> + </tr> + <tr> + <td>failed_queries</td> + <td>operation</td> + <td>rate</td> + <td>The number of failed queries</td> + </tr> + <tr> + <td>degraded_queries</td> + <td>operation</td> + <td>rate</td> + <td>The number of degraded queries, e.g. due to some content nodes not responding in time</td> + </tr> + <tr> + <td>hits_per_query</td> + <td>hit_per_query</td> + <td>average, count, max, sum</td> + <td>The number of hits returned</td> + </tr> + <tr> + <td>docproc.documents</td> + <td>document</td> + <td>sum</td> + <td>Number of processed documents</td> + </tr> + <tr> + <td>totalhits_per_query</td> + <td>hit_per_query</td> + <td>average, count, max, sum</td> + <td>The total number of documents found to match queries</td> + </tr> + <tr> + <td>serverActiveThreads</td> + <td>thread</td> + <td>average</td> + <td>Deprecated. Use jdisc.thread_pool.active_threads instead.</td> + </tr> + </tbody> +</table> + +## Distributor Metrics + +<table className="vespa-table"> + <thead> + <tr> + <th>Name</th> + <th>Unit</th> + <th>Suffixes</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>vds.distributor.docsstored</td> + <td>document</td> + <td>average</td> + <td>Number of documents stored in all buckets controlled by this distributor</td> + </tr> + <tr> + <td>vds.bouncer.clock_skew_aborts</td> + <td>operation</td> + <td>count</td> + <td>Number of client operations that were aborted due to clock skew between sender and receiver exceeding acceptable range</td> + </tr> + </tbody> +</table> + +## NodeAdmin Metrics + +<table className="vespa-table"> + <thead> + <tr> + <th>Name</th> + <th>Unit</th> + <th>Suffixes</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>endpoint.certificate.expiry.seconds</td> + <td>second</td> + <td>N/A</td> + <td>Time until node endpoint certificate expires</td> + </tr> + <tr> + <td>node-certificate.expiry.seconds</td> + <td>second</td> + <td>N/A</td> + <td>Time until node certificate expires</td> + </tr> + </tbody> +</table> + +## SearchNode Metrics + +<table className="vespa-table"> + <thead> + <tr> + <th>Name</th> + <th>Unit</th> + <th>Suffixes</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>content.proton.documentdb.documents.total</td> + <td>document</td> + <td>last, max</td> + <td>The total number of documents in this documents db (ready + not-ready)</td> + </tr> + <tr> + <td>content.proton.documentdb.documents.ready</td> + <td>document</td> + <td>last, max</td> + <td>The number of ready documents in this document db</td> + </tr> + <tr> + <td>content.proton.documentdb.documents.active</td> + <td>document</td> + <td>last, max</td> + <td>The number of active / searchable documents in this document db</td> + </tr> + <tr> + <td>content.proton.documentdb.disk_usage</td> + <td>byte</td> + <td>last</td> + <td>The total disk usage (in bytes) for this document db</td> + </tr> + <tr> + <td>content.proton.documentdb.memory_usage.allocated_bytes</td> + <td>byte</td> + <td>last</td> + <td>The number of allocated bytes</td> + </tr> + <tr> + <td>content.proton.search_protocol.query.latency</td> + <td>second</td> + <td>average, count, max, sum</td> + <td>Query request latency (seconds)</td> + </tr> + <tr> + <td>content.proton.search_protocol.docsum.latency</td> + <td>second</td> + <td>average, count, max, sum</td> + <td>Docsum request latency (seconds)</td> + </tr> + <tr> + <td>content.proton.search_protocol.docsum.requested_documents</td> + <td>document</td> + <td>rate</td> + <td>Total requested document summaries</td> + </tr> + <tr> + <td>content.proton.resource_usage.disk</td> + <td>fraction</td> + <td>average</td> + <td>The relative amount of disk used by this content node (transient usage not included, value in the range [0, 1]). Same value as reported to the cluster controller</td> + </tr> + <tr> + <td>content.proton.resource_usage.memory</td> + <td>fraction</td> + <td>average</td> + <td>The relative amount of memory used by this content node (transient usage not included, value in the range [0, 1]). Same value as reported to the cluster controller</td> + </tr> + <tr> + <td>content.proton.resource_usage.feeding_blocked</td> + <td>binary</td> + <td>last, max</td> + <td>Whether feeding is blocked due to resource limits being reached (value is either 0 or 1)</td> + </tr> + <tr> + <td>content.proton.transactionlog.disk_usage</td> + <td>byte</td> + <td>last</td> + <td>The disk usage (in bytes) of the transaction log</td> + </tr> + <tr> + <td>content.proton.documentdb.matching.docs_matched</td> + <td>document</td> + <td>rate</td> + <td>Number of documents matched</td> + </tr> + <tr> + <td>content.proton.documentdb.matching.docs_reranked</td> + <td>document</td> + <td>rate</td> + <td>Number of documents re-ranked (second phase)</td> + </tr> + <tr> + <td>content.proton.documentdb.matching.rank_profile.query_latency</td> + <td>second</td> + <td>average, count, max, sum</td> + <td>Total average latency (sec) when matching and ranking a query</td> + </tr> + <tr> + <td>content.proton.documentdb.matching.rank_profile.query_setup_time</td> + <td>second</td> + <td>average, count, max, sum</td> + <td>Average time (sec) spent setting up and tearing down queries</td> + </tr> + <tr> + <td>content.proton.documentdb.matching.rank_profile.rerank_time</td> + <td>second</td> + <td>average, count, max, sum</td> + <td>Average time (sec) spent on 2nd phase ranking</td> + </tr> + </tbody> +</table> + +## Sentinel Metrics + +<table className="vespa-table"> + <thead> + <tr> + <th>Name</th> + <th>Unit</th> + <th>Suffixes</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>sentinel.totalRestarts</td> + <td>restart</td> + <td>last, max, sum</td> + <td>Total number of service restarts done by the sentinel since the sentinel was started</td> + </tr> + </tbody> +</table> + +## Storage Metrics + +<table className="vespa-table"> + <thead> + <tr> + <th>Name</th> + <th>Unit</th> + <th>Suffixes</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>vds.filestor.allthreads.put.count</td> + <td>operation</td> + <td>rate</td> + <td>Number of requests processed.</td> + </tr> + <tr> + <td>vds.filestor.allthreads.remove.count</td> + <td>operation</td> + <td>rate</td> + <td>Number of requests processed.</td> + </tr> + <tr> + <td>vds.filestor.allthreads.update.count</td> + <td>request</td> + <td>rate</td> + <td>Number of requests processed.</td> + </tr> + </tbody> +</table> \ No newline at end of file diff --git a/en/reference/operations/metrics/distributor.html b/en/reference/operations/metrics/distributor.html deleted file mode 100644 index 1ee1f3627b..0000000000 --- a/en/reference/operations/metrics/distributor.html +++ /dev/null @@ -1,1119 +0,0 @@ ---- -# Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -title: "Distributor Metrics" -redirect_from: -- en/reference/distributor-metrics-reference.html ---- - -<table class="table"> - <thead> - <tr><th>Name</th><th>Unit</th><th>Description</th></tr> - </thead> - <tbody> - <tr> - <td><p id="vds_idealstate_buckets_rechecking">vds.idealstate.buckets_rechecking</p></td> - <td>bucket</td> - <td>The number of buckets that we are rechecking for ideal state operations</td> - </tr> - <tr> - <td><p id="vds_idealstate_idealstate_diff">vds.idealstate.idealstate_diff</p></td> - <td>bucket</td> - <td>A number representing the current difference from the ideal state. This is a number that decreases steadily as the system is getting closer to the ideal state</td> - </tr> - <tr> - <td><p id="vds_idealstate_buckets_toofewcopies">vds.idealstate.buckets_toofewcopies</p></td> - <td>bucket</td> - <td>The number of buckets the distributor controls that have less than the desired redundancy</td> - </tr> - <tr> - <td><p id="vds_idealstate_buckets_toomanycopies">vds.idealstate.buckets_toomanycopies</p></td> - <td>bucket</td> - <td>The number of buckets the distributor controls that have more than the desired redundancy</td> - </tr> - <tr> - <td><p id="vds_idealstate_buckets">vds.idealstate.buckets</p></td> - <td>bucket</td> - <td>The number of buckets the distributor controls</td> - </tr> - <tr> - <td><p id="vds_idealstate_buckets_notrusted">vds.idealstate.buckets_notrusted</p></td> - <td>bucket</td> - <td>The number of buckets that have no trusted copies.</td> - </tr> - <tr> - <td><p id="vds_idealstate_bucket_replicas_moving_out">vds.idealstate.bucket_replicas_moving_out</p></td> - <td>bucket</td> - <td>Bucket replicas that should be moved out, e.g. retirement case or node added to cluster that has higher ideal state priority.</td> - </tr> - <tr> - <td><p id="vds_idealstate_bucket_replicas_copying_out">vds.idealstate.bucket_replicas_copying_out</p></td> - <td>bucket</td> - <td>Bucket replicas that should be copied out, e.g. node is in ideal state but might have to provide data other nodes in a merge</td> - </tr> - <tr> - <td><p id="vds_idealstate_bucket_replicas_copying_in">vds.idealstate.bucket_replicas_copying_in</p></td> - <td>bucket</td> - <td>Bucket replicas that should be copied in, e.g. node does not have a replica for a bucket that it is in ideal state for</td> - </tr> - <tr> - <td><p id="vds_idealstate_bucket_replicas_syncing">vds.idealstate.bucket_replicas_syncing</p></td> - <td>bucket</td> - <td>Bucket replicas that need syncing due to mismatching metadata</td> - </tr> - <tr> - <td><p id="vds_idealstate_max_observed_time_since_last_gc_sec">vds.idealstate.max_observed_time_since_last_gc_sec</p></td> - <td>second</td> - <td>Maximum time (in seconds) since GC was last successfully run for a bucket. Aggregated max value across all buckets on the distributor.</td> - </tr> - <tr> - <td><p id="vds_idealstate_delete_bucket_done_ok">vds.idealstate.delete_bucket.done_ok</p></td> - <td>operation</td> - <td>The number of operations successfully performed</td> - </tr> - <tr> - <td><p id="vds_idealstate_delete_bucket_done_failed">vds.idealstate.delete_bucket.done_failed</p></td> - <td>operation</td> - <td>The number of operations that failed</td> - </tr> - <tr> - <td><p id="vds_idealstate_delete_bucket_pending">vds.idealstate.delete_bucket.pending</p></td> - <td>operation</td> - <td>The number of operations pending</td> - </tr> - <tr> - <td><p id="vds_idealstate_delete_bucket_blocked">vds.idealstate.delete_bucket.blocked</p></td> - <td>operation</td> - <td>The number of operations blocked by blocking operation starter</td> - </tr> - <tr> - <td><p id="vds_idealstate_delete_bucket_throttled">vds.idealstate.delete_bucket.throttled</p></td> - <td>operation</td> - <td>The number of operations throttled by throttling operation starter</td> - </tr> - <tr> - <td><p id="vds_idealstate_merge_bucket_done_ok">vds.idealstate.merge_bucket.done_ok</p></td> - <td>operation</td> - <td>The number of operations successfully performed</td> - </tr> - <tr> - <td><p id="vds_idealstate_merge_bucket_done_failed">vds.idealstate.merge_bucket.done_failed</p></td> - <td>operation</td> - <td>The number of operations that failed</td> - </tr> - <tr> - <td><p id="vds_idealstate_merge_bucket_pending">vds.idealstate.merge_bucket.pending</p></td> - <td>operation</td> - <td>The number of operations pending</td> - </tr> - <tr> - <td><p id="vds_idealstate_merge_bucket_blocked">vds.idealstate.merge_bucket.blocked</p></td> - <td>operation</td> - <td>The number of operations blocked by blocking operation starter</td> - </tr> - <tr> - <td><p id="vds_idealstate_merge_bucket_throttled">vds.idealstate.merge_bucket.throttled</p></td> - <td>operation</td> - <td>The number of operations throttled by throttling operation starter</td> - </tr> - <tr> - <td><p id="vds_idealstate_merge_bucket_source_only_copy_changed">vds.idealstate.merge_bucket.source_only_copy_changed</p></td> - <td>operation</td> - <td>The number of merge operations where source-only copy changed</td> - </tr> - <tr> - <td><p id="vds_idealstate_merge_bucket_source_only_copy_delete_blocked">vds.idealstate.merge_bucket.source_only_copy_delete_blocked</p></td> - <td>operation</td> - <td>The number of merge operations where delete of unchanged source-only copies was blocked</td> - </tr> - <tr> - <td><p id="vds_idealstate_merge_bucket_source_only_copy_delete_failed">vds.idealstate.merge_bucket.source_only_copy_delete_failed</p></td> - <td>operation</td> - <td>The number of merge operations where delete of unchanged source-only copies failed</td> - </tr> - <tr> - <td><p id="vds_idealstate_split_bucket_done_ok">vds.idealstate.split_bucket.done_ok</p></td> - <td>operation</td> - <td>The number of operations successfully performed</td> - </tr> - <tr> - <td><p id="vds_idealstate_split_bucket_done_failed">vds.idealstate.split_bucket.done_failed</p></td> - <td>operation</td> - <td>The number of operations that failed</td> - </tr> - <tr> - <td><p id="vds_idealstate_split_bucket_pending">vds.idealstate.split_bucket.pending</p></td> - <td>operation</td> - <td>The number of operations pending</td> - </tr> - <tr> - <td><p id="vds_idealstate_split_bucket_blocked">vds.idealstate.split_bucket.blocked</p></td> - <td>operation</td> - <td>The number of operations blocked by blocking operation starter</td> - </tr> - <tr> - <td><p id="vds_idealstate_split_bucket_throttled">vds.idealstate.split_bucket.throttled</p></td> - <td>operation</td> - <td>The number of operations throttled by throttling operation starter</td> - </tr> - <tr> - <td><p id="vds_idealstate_join_bucket_done_ok">vds.idealstate.join_bucket.done_ok</p></td> - <td>operation</td> - <td>The number of operations successfully performed</td> - </tr> - <tr> - <td><p id="vds_idealstate_join_bucket_done_failed">vds.idealstate.join_bucket.done_failed</p></td> - <td>operation</td> - <td>The number of operations that failed</td> - </tr> - <tr> - <td><p id="vds_idealstate_join_bucket_pending">vds.idealstate.join_bucket.pending</p></td> - <td>operation</td> - <td>The number of operations pending</td> - </tr> - <tr> - <td><p id="vds_idealstate_join_bucket_blocked">vds.idealstate.join_bucket.blocked</p></td> - <td>operation</td> - <td>The number of operations blocked by blocking operation starter</td> - </tr> - <tr> - <td><p id="vds_idealstate_join_bucket_throttled">vds.idealstate.join_bucket.throttled</p></td> - <td>operation</td> - <td>The number of operations throttled by throttling operation starter</td> - </tr> - <tr> - <td><p id="vds_idealstate_garbage_collection_done_ok">vds.idealstate.garbage_collection.done_ok</p></td> - <td>operation</td> - <td>The number of operations successfully performed</td> - </tr> - <tr> - <td><p id="vds_idealstate_garbage_collection_done_failed">vds.idealstate.garbage_collection.done_failed</p></td> - <td>operation</td> - <td>The number of operations that failed</td> - </tr> - <tr> - <td><p id="vds_idealstate_garbage_collection_pending">vds.idealstate.garbage_collection.pending</p></td> - <td>operation</td> - <td>The number of operations pending</td> - </tr> - <tr> - <td><p id="vds_idealstate_garbage_collection_documents_removed">vds.idealstate.garbage_collection.documents_removed</p></td> - <td>document</td> - <td>Number of documents removed by GC operations</td> - </tr> - <tr> - <td><p id="vds_idealstate_garbage_collection_blocked">vds.idealstate.garbage_collection.blocked</p></td> - <td>operation</td> - <td>The number of operations blocked by blocking operation starter</td> - </tr> - <tr> - <td><p id="vds_idealstate_garbage_collection_throttled">vds.idealstate.garbage_collection.throttled</p></td> - <td>operation</td> - <td>The number of operations throttled by throttling operation starter</td> - </tr> - <tr> - <td><p id="vds_distributor_puts_latency">vds.distributor.puts.latency</p></td> - <td>millisecond</td> - <td>The latency of put operations</td> - </tr> - <tr> - <td><p id="vds_distributor_puts_ok">vds.distributor.puts.ok</p></td> - <td>operation</td> - <td>The number of successful put operations performed</td> - </tr> - <tr> - <td><p id="vds_distributor_puts_failures_total">vds.distributor.puts.failures.total</p></td> - <td>operation</td> - <td>Sum of all failures</td> - </tr> - <tr> - <td><p id="vds_distributor_puts_failures_notfound">vds.distributor.puts.failures.notfound</p></td> - <td>operation</td> - <td>The number of operations that failed because the document did not exist</td> - </tr> - <tr> - <td><p id="vds_distributor_puts_failures_test_and_set_failed">vds.distributor.puts.failures.test_and_set_failed</p></td> - <td>operation</td> - <td>The number of mutating operations that failed because they specified a test-and-set condition that did not match the existing document</td> - </tr> - <tr> - <td><p id="vds_distributor_puts_failures_concurrent_mutations">vds.distributor.puts.failures.concurrent_mutations</p></td> - <td>operation</td> - <td>The number of operations that were transiently failed due to a mutating operation already being in progress for its document ID</td> - </tr> - <tr> - <td><p id="vds_distributor_puts_failures_notconnected">vds.distributor.puts.failures.notconnected</p></td> - <td>operation</td> - <td>The number of operations discarded because there were no available storage nodes to send to</td> - </tr> - <tr> - <td><p id="vds_distributor_puts_failures_notready">vds.distributor.puts.failures.notready</p></td> - <td>operation</td> - <td>The number of operations discarded because distributor was not ready</td> - </tr> - <tr> - <td><p id="vds_distributor_puts_failures_wrongdistributor">vds.distributor.puts.failures.wrongdistributor</p></td> - <td>operation</td> - <td>The number of operations discarded because they were sent to the wrong distributor</td> - </tr> - <tr> - <td><p id="vds_distributor_puts_failures_safe_time_not_reached">vds.distributor.puts.failures.safe_time_not_reached</p></td> - <td>operation</td> - <td>The number of operations that were transiently failed due to them arriving before the safe time point for bucket ownership handovers has passed</td> - </tr> - <tr> - <td><p id="vds_distributor_puts_failures_storagefailure">vds.distributor.puts.failures.storagefailure</p></td> - <td>operation</td> - <td>The number of operations that failed in storage</td> - </tr> - <tr> - <td><p id="vds_distributor_puts_failures_timeout">vds.distributor.puts.failures.timeout</p></td> - <td>operation</td> - <td>The number of operations that failed because the operation timed out towards storage</td> - </tr> - <tr> - <td><p id="vds_distributor_puts_failures_busy">vds.distributor.puts.failures.busy</p></td> - <td>operation</td> - <td>The number of messages from storage that failed because the storage node was busy</td> - </tr> - <tr> - <td><p id="vds_distributor_puts_failures_inconsistent_bucket">vds.distributor.puts.failures.inconsistent_bucket</p></td> - <td>operation</td> - <td>The number of operations failed due to buckets being in an inconsistent state or not found</td> - </tr> - <tr> - <td><p id="vds_distributor_removes_latency">vds.distributor.removes.latency</p></td> - <td>millisecond</td> - <td>The latency of remove operations</td> - </tr> - <tr> - <td><p id="vds_distributor_removes_ok">vds.distributor.removes.ok</p></td> - <td>operation</td> - <td>The number of successful removes operations performed</td> - </tr> - <tr> - <td><p id="vds_distributor_removes_failures_total">vds.distributor.removes.failures.total</p></td> - <td>operation</td> - <td>Sum of all failures</td> - </tr> - <tr> - <td><p id="vds_distributor_removes_failures_notfound">vds.distributor.removes.failures.notfound</p></td> - <td>operation</td> - <td>The number of operations that failed because the document did not exist</td> - </tr> - <tr> - <td><p id="vds_distributor_removes_failures_test_and_set_failed">vds.distributor.removes.failures.test_and_set_failed</p></td> - <td>operation</td> - <td>The number of mutating operations that failed because they specified a test-and-set condition that did not match the existing document</td> - </tr> - <tr> - <td><p id="vds_distributor_removes_failures_concurrent_mutations">vds.distributor.removes.failures.concurrent_mutations</p></td> - <td>operation</td> - <td>The number of operations that were transiently failed due to a mutating operation already being in progress for its document ID</td> - </tr> - <tr> - <td><p id="vds_distributor_removes_failures_busy">vds.distributor.removes.failures.busy</p></td> - <td>operation</td> - <td>The number of messages from storage that failed because the storage node was busy</td> - </tr> - <tr> - <td><p id="vds_distributor_removes_failures_inconsistent_bucket">vds.distributor.removes.failures.inconsistent_bucket</p></td> - <td>operation</td> - <td>The number of operations failed due to buckets being in an inconsistent state or not found</td> - </tr> - <tr> - <td><p id="vds_distributor_removes_failures_notconnected">vds.distributor.removes.failures.notconnected</p></td> - <td>operation</td> - <td>The number of operations discarded because there were no available storage nodes to send to</td> - </tr> - <tr> - <td><p id="vds_distributor_removes_failures_notready">vds.distributor.removes.failures.notready</p></td> - <td>operation</td> - <td>The number of operations discarded because distributor was not ready</td> - </tr> - <tr> - <td><p id="vds_distributor_removes_failures_safe_time_not_reached">vds.distributor.removes.failures.safe_time_not_reached</p></td> - <td>operation</td> - <td>The number of operations that were transiently failed due to them arriving before the safe time point for bucket ownership handovers has passed</td> - </tr> - <tr> - <td><p id="vds_distributor_removes_failures_storagefailure">vds.distributor.removes.failures.storagefailure</p></td> - <td>operation</td> - <td>The number of operations that failed in storage</td> - </tr> - <tr> - <td><p id="vds_distributor_removes_failures_timeout">vds.distributor.removes.failures.timeout</p></td> - <td>operation</td> - <td>The number of operations that failed because the operation timed out towards storage</td> - </tr> - <tr> - <td><p id="vds_distributor_removes_failures_wrongdistributor">vds.distributor.removes.failures.wrongdistributor</p></td> - <td>operation</td> - <td>The number of operations discarded because they were sent to the wrong distributor</td> - </tr> - <tr> - <td><p id="vds_distributor_updates_latency">vds.distributor.updates.latency</p></td> - <td>millisecond</td> - <td>The latency of update operations</td> - </tr> - <tr> - <td><p id="vds_distributor_updates_ok">vds.distributor.updates.ok</p></td> - <td>operation</td> - <td>The number of successful updates operations performed</td> - </tr> - <tr> - <td><p id="vds_distributor_updates_failures_total">vds.distributor.updates.failures.total</p></td> - <td>operation</td> - <td>Sum of all failures</td> - </tr> - <tr> - <td><p id="vds_distributor_updates_failures_notfound">vds.distributor.updates.failures.notfound</p></td> - <td>operation</td> - <td>The number of operations that failed because the document did not exist</td> - </tr> - <tr> - <td><p id="vds_distributor_updates_failures_test_and_set_failed">vds.distributor.updates.failures.test_and_set_failed</p></td> - <td>operation</td> - <td>The number of mutating operations that failed because they specified a test-and-set condition that did not match the existing document</td> - </tr> - <tr> - <td><p id="vds_distributor_updates_failures_concurrent_mutations">vds.distributor.updates.failures.concurrent_mutations</p></td> - <td>operation</td> - <td>The number of operations that were transiently failed due to a mutating operation already being in progress for its document ID</td> - </tr> - <tr> - <td><p id="vds_distributor_updates_diverging_timestamp_updates">vds.distributor.updates.diverging_timestamp_updates</p></td> - <td>operation</td> - <td>Number of updates that report they were performed against divergent version timestamps on different replicas</td> - </tr> - <tr> - <td><p id="vds_distributor_updates_failures_busy">vds.distributor.updates.failures.busy</p></td> - <td>operation</td> - <td>The number of messages from storage that failed because the storage node was busy</td> - </tr> - <tr> - <td><p id="vds_distributor_updates_failures_inconsistent_bucket">vds.distributor.updates.failures.inconsistent_bucket</p></td> - <td>operation</td> - <td>The number of operations failed due to buckets being in an inconsistent state or not found</td> - </tr> - <tr> - <td><p id="vds_distributor_updates_failures_notconnected">vds.distributor.updates.failures.notconnected</p></td> - <td>operation</td> - <td>The number of operations discarded because there were no available storage nodes to send to</td> - </tr> - <tr> - <td><p id="vds_distributor_updates_failures_notready">vds.distributor.updates.failures.notready</p></td> - <td>operation</td> - <td>The number of operations discarded because distributor was not ready</td> - </tr> - <tr> - <td><p id="vds_distributor_updates_failures_safe_time_not_reached">vds.distributor.updates.failures.safe_time_not_reached</p></td> - <td>operation</td> - <td>The number of operations that were transiently failed due to them arriving before the safe time point for bucket ownership handovers has passed</td> - </tr> - <tr> - <td><p id="vds_distributor_updates_failures_storagefailure">vds.distributor.updates.failures.storagefailure</p></td> - <td>operation</td> - <td>The number of operations that failed in storage</td> - </tr> - <tr> - <td><p id="vds_distributor_updates_failures_timeout">vds.distributor.updates.failures.timeout</p></td> - <td>operation</td> - <td>The number of operations that failed because the operation timed out towards storage</td> - </tr> - <tr> - <td><p id="vds_distributor_updates_failures_wrongdistributor">vds.distributor.updates.failures.wrongdistributor</p></td> - <td>operation</td> - <td>The number of operations discarded because they were sent to the wrong distributor</td> - </tr> - <tr> - <td><p id="vds_distributor_updates_fast_path_restarts">vds.distributor.updates.fast_path_restarts</p></td> - <td>operation</td> - <td>Number of safe path (write repair) updates that were restarted as fast path updates because all replicas returned documents with the same timestamp in the initial read phase</td> - </tr> - <tr> - <td><p id="vds_distributor_removelocations_ok">vds.distributor.removelocations.ok</p></td> - <td>operation</td> - <td>The number of successful removelocations operations performed</td> - </tr> - <tr> - <td><p id="vds_distributor_removelocations_failures_total">vds.distributor.removelocations.failures.total</p></td> - <td>operation</td> - <td>Sum of all failures</td> - </tr> - <tr> - <td><p id="vds_distributor_removelocations_failures_busy">vds.distributor.removelocations.failures.busy</p></td> - <td>operation</td> - <td>The number of messages from storage that failed because the storage node was busy</td> - </tr> - <tr> - <td><p id="vds_distributor_removelocations_failures_concurrent_mutations">vds.distributor.removelocations.failures.concurrent_mutations</p></td> - <td>operation</td> - <td>The number of operations that were transiently failed due to a mutating operation already being in progress for its document ID</td> - </tr> - <tr> - <td><p id="vds_distributor_removelocations_failures_inconsistent_bucket">vds.distributor.removelocations.failures.inconsistent_bucket</p></td> - <td>operation</td> - <td>The number of operations failed due to buckets being in an inconsistent state or not found</td> - </tr> - <tr> - <td><p id="vds_distributor_removelocations_failures_notconnected">vds.distributor.removelocations.failures.notconnected</p></td> - <td>operation</td> - <td>The number of operations discarded because there were no available storage nodes to send to</td> - </tr> - <tr> - <td><p id="vds_distributor_removelocations_failures_notfound">vds.distributor.removelocations.failures.notfound</p></td> - <td>operation</td> - <td>The number of operations that failed because the document did not exist</td> - </tr> - <tr> - <td><p id="vds_distributor_removelocations_failures_notready">vds.distributor.removelocations.failures.notready</p></td> - <td>operation</td> - <td>The number of operations discarded because distributor was not ready</td> - </tr> - <tr> - <td><p id="vds_distributor_removelocations_failures_safe_time_not_reached">vds.distributor.removelocations.failures.safe_time_not_reached</p></td> - <td>operation</td> - <td>The number of operations that were transiently failed due to them arriving before the safe time point for bucket ownership handovers has passed</td> - </tr> - <tr> - <td><p id="vds_distributor_removelocations_failures_storagefailure">vds.distributor.removelocations.failures.storagefailure</p></td> - <td>operation</td> - <td>The number of operations that failed in storage</td> - </tr> - <tr> - <td><p id="vds_distributor_removelocations_failures_test_and_set_failed">vds.distributor.removelocations.failures.test_and_set_failed</p></td> - <td>operation</td> - <td>The number of mutating operations that failed because they specified a test-and-set condition that did not match the existing document</td> - </tr> - <tr> - <td><p id="vds_distributor_removelocations_failures_timeout">vds.distributor.removelocations.failures.timeout</p></td> - <td>operation</td> - <td>The number of operations that failed because the operation timed out towards storage</td> - </tr> - <tr> - <td><p id="vds_distributor_removelocations_failures_wrongdistributor">vds.distributor.removelocations.failures.wrongdistributor</p></td> - <td>operation</td> - <td>The number of operations discarded because they were sent to the wrong distributor</td> - </tr> - <tr> - <td><p id="vds_distributor_removelocations_latency">vds.distributor.removelocations.latency</p></td> - <td>millisecond</td> - <td>The average latency of removelocations operations</td> - </tr> - <tr> - <td><p id="vds_distributor_gets_latency">vds.distributor.gets.latency</p></td> - <td>millisecond</td> - <td>The average latency of gets operations</td> - </tr> - <tr> - <td><p id="vds_distributor_gets_ok">vds.distributor.gets.ok</p></td> - <td>operation</td> - <td>The number of successful gets operations performed</td> - </tr> - <tr> - <td><p id="vds_distributor_gets_failures_total">vds.distributor.gets.failures.total</p></td> - <td>operation</td> - <td>Sum of all failures</td> - </tr> - <tr> - <td><p id="vds_distributor_gets_failures_notfound">vds.distributor.gets.failures.notfound</p></td> - <td>operation</td> - <td>The number of operations that failed because the document did not exist</td> - </tr> - <tr> - <td><p id="vds_distributor_gets_failures_busy">vds.distributor.gets.failures.busy</p></td> - <td>operation</td> - <td>The number of messages from storage that failed because the storage node was busy</td> - </tr> - <tr> - <td><p id="vds_distributor_gets_failures_concurrent_mutations">vds.distributor.gets.failures.concurrent_mutations</p></td> - <td>operation</td> - <td>The number of operations that were transiently failed due to a mutating operation already being in progress for its document ID</td> - </tr> - <tr> - <td><p id="vds_distributor_gets_failures_inconsistent_bucket">vds.distributor.gets.failures.inconsistent_bucket</p></td> - <td>operation</td> - <td>The number of operations failed due to buckets being in an inconsistent state or not found</td> - </tr> - <tr> - <td><p id="vds_distributor_gets_failures_notconnected">vds.distributor.gets.failures.notconnected</p></td> - <td>operation</td> - <td>The number of operations discarded because there were no available storage nodes to send to</td> - </tr> - <tr> - <td><p id="vds_distributor_gets_failures_notready">vds.distributor.gets.failures.notready</p></td> - <td>operation</td> - <td>The number of operations discarded because distributor was not ready</td> - </tr> - <tr> - <td><p id="vds_distributor_gets_failures_safe_time_not_reached">vds.distributor.gets.failures.safe_time_not_reached</p></td> - <td>operation</td> - <td>The number of operations that were transiently failed due to them arriving before the safe time point for bucket ownership handovers has passed</td> - </tr> - <tr> - <td><p id="vds_distributor_gets_failures_storagefailure">vds.distributor.gets.failures.storagefailure</p></td> - <td>operation</td> - <td>The number of operations that failed in storage</td> - </tr> - <tr> - <td><p id="vds_distributor_gets_failures_test_and_set_failed">vds.distributor.gets.failures.test_and_set_failed</p></td> - <td>operation</td> - <td>The number of mutating operations that failed because they specified a test-and-set condition that did not match the existing document</td> - </tr> - <tr> - <td><p id="vds_distributor_gets_failures_timeout">vds.distributor.gets.failures.timeout</p></td> - <td>operation</td> - <td>The number of operations that failed because the operation timed out towards storage</td> - </tr> - <tr> - <td><p id="vds_distributor_gets_failures_wrongdistributor">vds.distributor.gets.failures.wrongdistributor</p></td> - <td>operation</td> - <td>The number of operations discarded because they were sent to the wrong distributor</td> - </tr> - <tr> - <td><p id="vds_distributor_visitor_latency">vds.distributor.visitor.latency</p></td> - <td>millisecond</td> - <td>The average latency of visitor operations</td> - </tr> - <tr> - <td><p id="vds_distributor_visitor_ok">vds.distributor.visitor.ok</p></td> - <td>operation</td> - <td>The number of successful visitor operations performed</td> - </tr> - <tr> - <td><p id="vds_distributor_visitor_failures_total">vds.distributor.visitor.failures.total</p></td> - <td>operation</td> - <td>Sum of all failures</td> - </tr> - <tr> - <td><p id="vds_distributor_visitor_failures_notready">vds.distributor.visitor.failures.notready</p></td> - <td>operation</td> - <td>The number of operations discarded because distributor was not ready</td> - </tr> - <tr> - <td><p id="vds_distributor_visitor_failures_notconnected">vds.distributor.visitor.failures.notconnected</p></td> - <td>operation</td> - <td>The number of operations discarded because there were no available storage nodes to send to</td> - </tr> - <tr> - <td><p id="vds_distributor_visitor_failures_wrongdistributor">vds.distributor.visitor.failures.wrongdistributor</p></td> - <td>operation</td> - <td>The number of operations discarded because they were sent to the wrong distributor</td> - </tr> - <tr> - <td><p id="vds_distributor_visitor_failures_safe_time_not_reached">vds.distributor.visitor.failures.safe_time_not_reached</p></td> - <td>operation</td> - <td>The number of operations that were transiently failed due to them arriving before the safe time point for bucket ownership handovers has passed</td> - </tr> - <tr> - <td><p id="vds_distributor_visitor_failures_storagefailure">vds.distributor.visitor.failures.storagefailure</p></td> - <td>operation</td> - <td>The number of operations that failed in storage</td> - </tr> - <tr> - <td><p id="vds_distributor_visitor_failures_timeout">vds.distributor.visitor.failures.timeout</p></td> - <td>operation</td> - <td>The number of operations that failed because the operation timed out towards storage</td> - </tr> - <tr> - <td><p id="vds_distributor_visitor_failures_busy">vds.distributor.visitor.failures.busy</p></td> - <td>operation</td> - <td>The number of messages from storage that failed because the storage node was busy</td> - </tr> - <tr> - <td><p id="vds_distributor_visitor_failures_inconsistent_bucket">vds.distributor.visitor.failures.inconsistent_bucket</p></td> - <td>operation</td> - <td>The number of operations failed due to buckets being in an inconsistent state or not found</td> - </tr> - <tr> - <td><p id="vds_distributor_visitor_failures_notfound">vds.distributor.visitor.failures.notfound</p></td> - <td>operation</td> - <td>The number of operations that failed because the document did not exist</td> - </tr> - <tr> - <td><p id="vds_distributor_visitor_bytes_per_visitor">vds.distributor.visitor.bytes_per_visitor</p></td> - <td>operation</td> - <td>The number of bytes visited on content nodes as part of a single client visitor command</td> - </tr> - <tr> - <td><p id="vds_distributor_visitor_docs_per_visitor">vds.distributor.visitor.docs_per_visitor</p></td> - <td>operation</td> - <td>The number of documents visited on content nodes as part of a single client visitor command</td> - </tr> - <tr> - <td><p id="vds_distributor_visitor_failures_concurrent_mutations">vds.distributor.visitor.failures.concurrent_mutations</p></td> - <td>operation</td> - <td>The number of operations that were transiently failed due to a mutating operation already being in progress for its document ID</td> - </tr> - <tr> - <td><p id="vds_distributor_visitor_failures_test_and_set_failed">vds.distributor.visitor.failures.test_and_set_failed</p></td> - <td>operation</td> - <td>The number of mutating operations that failed because they specified a test-and-set condition that did not match the existing document</td> - </tr> - <tr> - <td><p id="vds_distributor_docsstored">vds.distributor.docsstored</p></td> - <td>document</td> - <td>Number of documents stored in all buckets controlled by this distributor</td> - </tr> - <tr> - <td><p id="vds_distributor_bytesstored">vds.distributor.bytesstored</p></td> - <td>byte</td> - <td>Number of bytes stored in all buckets controlled by this distributor</td> - </tr> - <tr> - <td><p id="metricmanager_periodichooklatency">metricmanager.periodichooklatency</p></td> - <td>millisecond</td> - <td>Time in ms used to update a single periodic hook</td> - </tr> - <tr> - <td><p id="metricmanager_resetlatency">metricmanager.resetlatency</p></td> - <td>millisecond</td> - <td>Time in ms used to reset all metrics.</td> - </tr> - <tr> - <td><p id="metricmanager_sleeptime">metricmanager.sleeptime</p></td> - <td>millisecond</td> - <td>Time in ms worker thread is sleeping</td> - </tr> - <tr> - <td><p id="metricmanager_snapshothooklatency">metricmanager.snapshothooklatency</p></td> - <td>millisecond</td> - <td>Time in ms used to update a single snapshot hook</td> - </tr> - <tr> - <td><p id="metricmanager_snapshotlatency">metricmanager.snapshotlatency</p></td> - <td>millisecond</td> - <td>Time in ms used to take a snapshot</td> - </tr> - <tr> - <td><p id="vds_distributor_activate_cluster_state_processing_time">vds.distributor.activate_cluster_state_processing_time</p></td> - <td>millisecond</td> - <td>Elapsed time where the distributor thread is blocked on merging pending bucket info into its bucket database upon activating a cluster state</td> - </tr> - <tr> - <td><p id="vds_distributor_bucket_db_memory_usage_allocated_bytes">vds.distributor.bucket_db.memory_usage.allocated_bytes</p></td> - <td>byte</td> - <td>The number of allocated bytes</td> - </tr> - <tr> - <td><p id="vds_distributor_bucket_db_memory_usage_dead_bytes">vds.distributor.bucket_db.memory_usage.dead_bytes</p></td> - <td>byte</td> - <td>The number of dead bytes (<= used_bytes)</td> - </tr> - <tr> - <td><p id="vds_distributor_bucket_db_memory_usage_onhold_bytes">vds.distributor.bucket_db.memory_usage.onhold_bytes</p></td> - <td>byte</td> - <td>The number of bytes on hold</td> - </tr> - <tr> - <td><p id="vds_distributor_bucket_db_memory_usage_used_bytes">vds.distributor.bucket_db.memory_usage.used_bytes</p></td> - <td>byte</td> - <td>The number of used bytes (<= allocated_bytes)</td> - </tr> - <tr> - <td><p id="vds_distributor_getbucketlists_failures_busy">vds.distributor.getbucketlists.failures.busy</p></td> - <td>operation</td> - <td>The number of messages from storage that failed because the storage node was busy</td> - </tr> - <tr> - <td><p id="vds_distributor_getbucketlists_failures_concurrent_mutations">vds.distributor.getbucketlists.failures.concurrent_mutations</p></td> - <td>operation</td> - <td>The number of operations that were transiently failed due to a mutating operation already being in progress for its document ID</td> - </tr> - <tr> - <td><p id="vds_distributor_getbucketlists_failures_inconsistent_bucket">vds.distributor.getbucketlists.failures.inconsistent_bucket</p></td> - <td>operation</td> - <td>The number of operations failed due to buckets being in an inconsistent state or not found</td> - </tr> - <tr> - <td><p id="vds_distributor_getbucketlists_failures_notconnected">vds.distributor.getbucketlists.failures.notconnected</p></td> - <td>operation</td> - <td>The number of operations discarded because there were no available storage nodes to send to</td> - </tr> - <tr> - <td><p id="vds_distributor_getbucketlists_failures_notfound">vds.distributor.getbucketlists.failures.notfound</p></td> - <td>operation</td> - <td>The number of operations that failed because the document did not exist</td> - </tr> - <tr> - <td><p id="vds_distributor_getbucketlists_failures_notready">vds.distributor.getbucketlists.failures.notready</p></td> - <td>operation</td> - <td>The number of operations discarded because distributor was not ready</td> - </tr> - <tr> - <td><p id="vds_distributor_getbucketlists_failures_safe_time_not_reached">vds.distributor.getbucketlists.failures.safe_time_not_reached</p></td> - <td>operation</td> - <td>The number of operations that were transiently failed due to them arriving before the safe time point for bucket ownership handovers has passed</td> - </tr> - <tr> - <td><p id="vds_distributor_getbucketlists_failures_storagefailure">vds.distributor.getbucketlists.failures.storagefailure</p></td> - <td>operation</td> - <td>The number of operations that failed in storage</td> - </tr> - <tr> - <td><p id="vds_distributor_getbucketlists_failures_test_and_set_failed">vds.distributor.getbucketlists.failures.test_and_set_failed</p></td> - <td>operation</td> - <td>The number of mutating operations that failed because they specified a test-and-set condition that did not match the existing document</td> - </tr> - <tr> - <td><p id="vds_distributor_getbucketlists_failures_timeout">vds.distributor.getbucketlists.failures.timeout</p></td> - <td>operation</td> - <td>The number of operations that failed because the operation timed out towards storage</td> - </tr> - <tr> - <td><p id="vds_distributor_getbucketlists_failures_total">vds.distributor.getbucketlists.failures.total</p></td> - <td>operation</td> - <td>Total number of failures</td> - </tr> - <tr> - <td><p id="vds_distributor_getbucketlists_failures_wrongdistributor">vds.distributor.getbucketlists.failures.wrongdistributor</p></td> - <td>operation</td> - <td>The number of operations discarded because they were sent to the wrong distributor</td> - </tr> - <tr> - <td><p id="vds_distributor_getbucketlists_latency">vds.distributor.getbucketlists.latency</p></td> - <td>millisecond</td> - <td>The average latency of getbucketlists operations</td> - </tr> - <tr> - <td><p id="vds_distributor_getbucketlists_ok">vds.distributor.getbucketlists.ok</p></td> - <td>operation</td> - <td>The number of successful getbucketlists operations performed</td> - </tr> - <tr> - <td><p id="vds_distributor_recoverymodeschedulingtime">vds.distributor.recoverymodeschedulingtime</p></td> - <td>millisecond</td> - <td>Time spent scheduling operations in recovery mode after receiving new cluster state</td> - </tr> - <tr> - <td><p id="vds_distributor_set_cluster_state_processing_time">vds.distributor.set_cluster_state_processing_time</p></td> - <td>millisecond</td> - <td>Elapsed time where the distributor thread is blocked on processing its bucket database upon receiving a new cluster state</td> - </tr> - <tr> - <td><p id="vds_distributor_state_transition_time">vds.distributor.state_transition_time</p></td> - <td>millisecond</td> - <td>Time it takes to complete a cluster state transition. If a state transition is preempted before completing, its elapsed time is counted as part of the total time spent for the final, completed state transition</td> - </tr> - <tr> - <td><p id="vds_distributor_stats_failures_busy">vds.distributor.stats.failures.busy</p></td> - <td>operation</td> - <td>The number of messages from storage that failed because the storage node was busy</td> - </tr> - <tr> - <td><p id="vds_distributor_stats_failures_concurrent_mutations">vds.distributor.stats.failures.concurrent_mutations</p></td> - <td>operation</td> - <td>The number of operations that were transiently failed due to a mutating operation already being in progress for its document ID</td> - </tr> - <tr> - <td><p id="vds_distributor_stats_failures_inconsistent_bucket">vds.distributor.stats.failures.inconsistent_bucket</p></td> - <td>operation</td> - <td>The number of operations failed due to buckets being in an inconsistent state or not found</td> - </tr> - <tr> - <td><p id="vds_distributor_stats_failures_notconnected">vds.distributor.stats.failures.notconnected</p></td> - <td>operation</td> - <td>The number of operations discarded because there were no available storage nodes to send to</td> - </tr> - <tr> - <td><p id="vds_distributor_stats_failures_notfound">vds.distributor.stats.failures.notfound</p></td> - <td>operation</td> - <td>The number of operations that failed because the document did not exist</td> - </tr> - <tr> - <td><p id="vds_distributor_stats_failures_notready">vds.distributor.stats.failures.notready</p></td> - <td>operation</td> - <td>The number of operations discarded because distributor was not ready</td> - </tr> - <tr> - <td><p id="vds_distributor_stats_failures_safe_time_not_reached">vds.distributor.stats.failures.safe_time_not_reached</p></td> - <td>operation</td> - <td>The number of operations that were transiently failed due to them arriving before the safe time point for bucket ownership handovers has passed</td> - </tr> - <tr> - <td><p id="vds_distributor_stats_failures_storagefailure">vds.distributor.stats.failures.storagefailure</p></td> - <td>operation</td> - <td>The number of operations that failed in storage</td> - </tr> - <tr> - <td><p id="vds_distributor_stats_failures_test_and_set_failed">vds.distributor.stats.failures.test_and_set_failed</p></td> - <td>operation</td> - <td>The number of mutating operations that failed because they specified a test-and-set condition that did not match the existing document</td> - </tr> - <tr> - <td><p id="vds_distributor_stats_failures_timeout">vds.distributor.stats.failures.timeout</p></td> - <td>operation</td> - <td>The number of operations that failed because the operation timed out towards storage</td> - </tr> - <tr> - <td><p id="vds_distributor_stats_failures_total">vds.distributor.stats.failures.total</p></td> - <td>operation</td> - <td>The total number of failures</td> - </tr> - <tr> - <td><p id="vds_distributor_stats_failures_wrongdistributor">vds.distributor.stats.failures.wrongdistributor</p></td> - <td>operation</td> - <td>The number of operations discarded because they were sent to the wrong distributor</td> - </tr> - <tr> - <td><p id="vds_distributor_stats_latency">vds.distributor.stats.latency</p></td> - <td>millisecond</td> - <td>The average latency of stats operations</td> - </tr> - <tr> - <td><p id="vds_distributor_stats_ok">vds.distributor.stats.ok</p></td> - <td>operation</td> - <td>The number of successful stats operations performed</td> - </tr> - <tr> - <td><p id="vds_distributor_update_gets_failures_busy">vds.distributor.update_gets.failures.busy</p></td> - <td>operation</td> - <td>The number of messages from storage that failed because the storage node was busy</td> - </tr> - <tr> - <td><p id="vds_distributor_update_gets_failures_concurrent_mutations">vds.distributor.update_gets.failures.concurrent_mutations</p></td> - <td>operation</td> - <td>The number of operations that were transiently failed due to a mutating operation already being in progress for its document ID</td> - </tr> - <tr> - <td><p id="vds_distributor_update_gets_failures_inconsistent_bucket">vds.distributor.update_gets.failures.inconsistent_bucket</p></td> - <td>operation</td> - <td>The number of operations failed due to buckets being in an inconsistent state or not found</td> - </tr> - <tr> - <td><p id="vds_distributor_update_gets_failures_notconnected">vds.distributor.update_gets.failures.notconnected</p></td> - <td>operation</td> - <td>The number of operations discarded because there were no available storage nodes to send to</td> - </tr> - <tr> - <td><p id="vds_distributor_update_gets_failures_notfound">vds.distributor.update_gets.failures.notfound</p></td> - <td>operation</td> - <td>The number of operations that failed because the document did not exist</td> - </tr> - <tr> - <td><p id="vds_distributor_update_gets_failures_notready">vds.distributor.update_gets.failures.notready</p></td> - <td>operation</td> - <td>The number of operations discarded because distributor was not ready</td> - </tr> - <tr> - <td><p id="vds_distributor_update_gets_failures_safe_time_not_reached">vds.distributor.update_gets.failures.safe_time_not_reached</p></td> - <td>operation</td> - <td>The number of operations that were transiently failed due to them arriving before the safe time point for bucket ownership handovers has passed</td> - </tr> - <tr> - <td><p id="vds_distributor_update_gets_failures_storagefailure">vds.distributor.update_gets.failures.storagefailure</p></td> - <td>operation</td> - <td>The number of operations that failed in storage</td> - </tr> - <tr> - <td><p id="vds_distributor_update_gets_failures_test_and_set_failed">vds.distributor.update_gets.failures.test_and_set_failed</p></td> - <td>operation</td> - <td>The number of mutating operations that failed because they specified a test-and-set condition that did not match the existing document</td> - </tr> - <tr> - <td><p id="vds_distributor_update_gets_failures_timeout">vds.distributor.update_gets.failures.timeout</p></td> - <td>operation</td> - <td>The number of operations that failed because the operation timed out towards storage</td> - </tr> - <tr> - <td><p id="vds_distributor_update_gets_failures_total">vds.distributor.update_gets.failures.total</p></td> - <td>operation</td> - <td>The total number of failures</td> - </tr> - <tr> - <td><p id="vds_distributor_update_gets_failures_wrongdistributor">vds.distributor.update_gets.failures.wrongdistributor</p></td> - <td>operation</td> - <td>The number of operations discarded because they were sent to the wrong distributor</td> - </tr> - <tr> - <td><p id="vds_distributor_update_gets_latency">vds.distributor.update_gets.latency</p></td> - <td>millisecond</td> - <td>The average latency of update_gets operations</td> - </tr> - <tr> - <td><p id="vds_distributor_update_gets_ok">vds.distributor.update_gets.ok</p></td> - <td>operation</td> - <td>The number of successful update_gets operations performed</td> - </tr> - <tr> - <td><p id="vds_distributor_update_metadata_gets_failures_busy">vds.distributor.update_metadata_gets.failures.busy</p></td> - <td>operation</td> - <td>The number of messages from storage that failed because the storage node was busy</td> - </tr> - <tr> - <td><p id="vds_distributor_update_metadata_gets_failures_concurrent_mutations">vds.distributor.update_metadata_gets.failures.concurrent_mutations</p></td> - <td>operation</td> - <td>The number of operations that were transiently failed due to a mutating operation already being in progress for its document ID</td> - </tr> - <tr> - <td><p id="vds_distributor_update_metadata_gets_failures_inconsistent_bucket">vds.distributor.update_metadata_gets.failures.inconsistent_bucket</p></td> - <td>operation</td> - <td>The number of operations failed due to buckets being in an inconsistent state or not found</td> - </tr> - <tr> - <td><p id="vds_distributor_update_metadata_gets_failures_notconnected">vds.distributor.update_metadata_gets.failures.notconnected</p></td> - <td>operation</td> - <td>The number of operations discarded because there were no available storage nodes to send to</td> - </tr> - <tr> - <td><p id="vds_distributor_update_metadata_gets_failures_notfound">vds.distributor.update_metadata_gets.failures.notfound</p></td> - <td>operation</td> - <td>The number of operations that failed because the document did not exist</td> - </tr> - <tr> - <td><p id="vds_distributor_update_metadata_gets_failures_notready">vds.distributor.update_metadata_gets.failures.notready</p></td> - <td>operation</td> - <td>The number of operations discarded because distributor was not ready</td> - </tr> - <tr> - <td><p id="vds_distributor_update_metadata_gets_failures_safe_time_not_reached">vds.distributor.update_metadata_gets.failures.safe_time_not_reached</p></td> - <td>operation</td> - <td>The number of operations that were transiently failed due to them arriving before the safe time point for bucket ownership handovers has passed</td> - </tr> - <tr> - <td><p id="vds_distributor_update_metadata_gets_failures_storagefailure">vds.distributor.update_metadata_gets.failures.storagefailure</p></td> - <td>operation</td> - <td>The number of operations that failed in storage</td> - </tr> - <tr> - <td><p id="vds_distributor_update_metadata_gets_failures_test_and_set_failed">vds.distributor.update_metadata_gets.failures.test_and_set_failed</p></td> - <td>operation</td> - <td>The number of mutating operations that failed because they specified a test-and-set condition that did not match the existing document</td> - </tr> - <tr> - <td><p id="vds_distributor_update_metadata_gets_failures_timeout">vds.distributor.update_metadata_gets.failures.timeout</p></td> - <td>operation</td> - <td>The number of operations that failed because the operation timed out towards storage</td> - </tr> - <tr> - <td><p id="vds_distributor_update_metadata_gets_failures_total">vds.distributor.update_metadata_gets.failures.total</p></td> - <td>operation</td> - <td>The total number of failures</td> - </tr> - <tr> - <td><p id="vds_distributor_update_metadata_gets_failures_wrongdistributor">vds.distributor.update_metadata_gets.failures.wrongdistributor</p></td> - <td>operation</td> - <td>The number of operations discarded because they were sent to the wrong distributor</td> - </tr> - <tr> - <td><p id="vds_distributor_update_metadata_gets_latency">vds.distributor.update_metadata_gets.latency</p></td> - <td>millisecond</td> - <td>The average latency of update_metadata_gets operations</td> - </tr> - <tr> - <td><p id="vds_distributor_update_metadata_gets_ok">vds.distributor.update_metadata_gets.ok</p></td> - <td>operation</td> - <td>The number of successful update_metadata_gets operations performed</td> - </tr> - <tr> - <td><p id="vds_distributor_update_puts_failures_busy">vds.distributor.update_puts.failures.busy</p></td> - <td>operation</td> - <td>The number of messages from storage that failed because the storage node was busy</td> - </tr> - <tr> - <td><p id="vds_distributor_update_puts_failures_concurrent_mutations">vds.distributor.update_puts.failures.concurrent_mutations</p></td> - <td>operation</td> - <td>The number of operations that were transiently failed due to a mutating operation already being in progress for its document ID</td> - </tr> - <tr> - <td><p id="vds_distributor_update_puts_failures_inconsistent_bucket">vds.distributor.update_puts.failures.inconsistent_bucket</p></td> - <td>operation</td> - <td>The number of operations failed due to buckets being in an inconsistent state or not found</td> - </tr> - <tr> - <td><p id="vds_distributor_update_puts_failures_notconnected">vds.distributor.update_puts.failures.notconnected</p></td> - <td>operation</td> - <td>The number of operations discarded because there were no available storage nodes to send to</td> - </tr> - <tr> - <td><p id="vds_distributor_update_puts_failures_notfound">vds.distributor.update_puts.failures.notfound</p></td> - <td>operation</td> - <td>The number of operations that failed because the document did not exist</td> - </tr> - <tr> - <td><p id="vds_distributor_update_puts_failures_notready">vds.distributor.update_puts.failures.notready</p></td> - <td>operation</td> - <td>The number of operations discarded because distributor was not ready</td> - </tr> - <tr> - <td><p id="vds_distributor_update_puts_failures_safe_time_not_reached">vds.distributor.update_puts.failures.safe_time_not_reached</p></td> - <td>operation</td> - <td>The number of operations that were transiently failed due to them arriving before the safe time point for bucket ownership handovers has passed</td> - </tr> - <tr> - <td><p id="vds_distributor_update_puts_failures_storagefailure">vds.distributor.update_puts.failures.storagefailure</p></td> - <td>operation</td> - <td>The number of operations that failed in storage</td> - </tr> - <tr> - <td><p id="vds_distributor_update_puts_failures_test_and_set_failed">vds.distributor.update_puts.failures.test_and_set_failed</p></td> - <td>operation</td> - <td>The number of mutating operations that failed because they specified a test-and-set condition that did not match the existing document</td> - </tr> - <tr> - <td><p id="vds_distributor_update_puts_failures_timeout">vds.distributor.update_puts.failures.timeout</p></td> - <td>operation</td> - <td>The number of operations that failed because the operation timed out towards storage</td> - </tr> - <tr> - <td><p id="vds_distributor_update_puts_failures_total">vds.distributor.update_puts.failures.total</p></td> - <td>operation</td> - <td>The total number of put failures</td> - </tr> - <tr> - <td><p id="vds_distributor_update_puts_failures_wrongdistributor">vds.distributor.update_puts.failures.wrongdistributor</p></td> - <td>operation</td> - <td>The number of operations discarded because they were sent to the wrong distributor</td> - </tr> - <tr> - <td><p id="vds_distributor_update_puts_latency">vds.distributor.update_puts.latency</p></td> - <td>millisecond</td> - <td>The average latency of update_puts operations</td> - </tr> - <tr> - <td><p id="vds_distributor_update_puts_ok">vds.distributor.update_puts.ok</p></td> - <td>operation</td> - <td>The number of successful update_puts operations performed</td> - </tr> - <tr> - <td><p id="vds_distributor_mutating_op_memory_usage">vds.distributor.mutating_op_memory_usage</p></td> - <td>byte</td> - <td>Estimated amount of memory used by active mutating operations across all distributor stripes, in bytes</td> - </tr> - <tr> - <td><p id="vds_idealstate_nodes_per_merge">vds.idealstate.nodes_per_merge</p></td> - <td>node</td> - <td>The number of nodes involved in a single merge operation.</td> - </tr> - <tr> - <td><p id="vds_idealstate_set_bucket_state_blocked">vds.idealstate.set_bucket_state.blocked</p></td> - <td>operation</td> - <td>The number of operations blocked by blocking operation starter</td> - </tr> - <tr> - <td><p id="vds_idealstate_set_bucket_state_done_failed">vds.idealstate.set_bucket_state.done_failed</p></td> - <td>operation</td> - <td>The number of operations that failed</td> - </tr> - <tr> - <td><p id="vds_idealstate_set_bucket_state_done_ok">vds.idealstate.set_bucket_state.done_ok</p></td> - <td>operation</td> - <td>The number of operations successfully performed</td> - </tr> - <tr> - <td><p id="vds_idealstate_set_bucket_state_pending">vds.idealstate.set_bucket_state.pending</p></td> - <td>operation</td> - <td>The number of operations pending</td> - </tr> - <tr> - <td><p id="vds_idealstate_set_bucket_state_throttled">vds.idealstate.set_bucket_state.throttled</p></td> - <td>operation</td> - <td>The number of operations throttled by throttling operation starter</td> - </tr> - <tr> - <td><p id="vds_bouncer_clock_skew_aborts">vds.bouncer.clock_skew_aborts</p></td> - <td>operation</td> - <td>Number of client operations that were aborted due to clock skew between sender and receiver exceeding acceptable range</td> - </tr> - </tbody> -</table> diff --git a/en/reference/distributor-metrics-reference.html b/en/reference/operations/metrics/distributor.mdx similarity index 53% rename from en/reference/distributor-metrics-reference.html rename to en/reference/operations/metrics/distributor.mdx index 2d66226351..a214f453d7 100644 --- a/en/reference/distributor-metrics-reference.html +++ b/en/reference/operations/metrics/distributor.mdx @@ -1,1117 +1,1121 @@ ---- -# Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +--- title: "Distributor Metrics" +sidebarTitle: "Distributor metrics" --- -<table class="table"> +<table className="vespa-table"> <thead> - <tr><th>Name</th><th>Unit</th><th>Description</th></tr> + <tr> + <th>Name</th> + <th>Unit</th> + <th>Description</th> + </tr> </thead> <tbody> <tr> - <td><p id="vds_idealstate_buckets_rechecking">vds.idealstate.buckets_rechecking</p></td> + <td>vds.idealstate.buckets_rechecking</td> <td>bucket</td> <td>The number of buckets that we are rechecking for ideal state operations</td> </tr> <tr> - <td><p id="vds_idealstate_idealstate_diff">vds.idealstate.idealstate_diff</p></td> + <td>vds.idealstate.idealstate_diff</td> <td>bucket</td> <td>A number representing the current difference from the ideal state. This is a number that decreases steadily as the system is getting closer to the ideal state</td> </tr> <tr> - <td><p id="vds_idealstate_buckets_toofewcopies">vds.idealstate.buckets_toofewcopies</p></td> + <td>vds.idealstate.buckets_toofewcopies</td> <td>bucket</td> <td>The number of buckets the distributor controls that have less than the desired redundancy</td> </tr> <tr> - <td><p id="vds_idealstate_buckets_toomanycopies">vds.idealstate.buckets_toomanycopies</p></td> + <td>vds.idealstate.buckets_toomanycopies</td> <td>bucket</td> <td>The number of buckets the distributor controls that have more than the desired redundancy</td> </tr> <tr> - <td><p id="vds_idealstate_buckets">vds.idealstate.buckets</p></td> + <td>vds.idealstate.buckets</td> <td>bucket</td> <td>The number of buckets the distributor controls</td> </tr> <tr> - <td><p id="vds_idealstate_buckets_notrusted">vds.idealstate.buckets_notrusted</p></td> + <td>vds.idealstate.buckets_notrusted</td> <td>bucket</td> <td>The number of buckets that have no trusted copies.</td> </tr> <tr> - <td><p id="vds_idealstate_bucket_replicas_moving_out">vds.idealstate.bucket_replicas_moving_out</p></td> + <td>vds.idealstate.bucket_replicas_moving_out</td> <td>bucket</td> <td>Bucket replicas that should be moved out, e.g. retirement case or node added to cluster that has higher ideal state priority.</td> </tr> <tr> - <td><p id="vds_idealstate_bucket_replicas_copying_out">vds.idealstate.bucket_replicas_copying_out</p></td> + <td>vds.idealstate.bucket_replicas_copying_out</td> <td>bucket</td> <td>Bucket replicas that should be copied out, e.g. node is in ideal state but might have to provide data other nodes in a merge</td> </tr> <tr> - <td><p id="vds_idealstate_bucket_replicas_copying_in">vds.idealstate.bucket_replicas_copying_in</p></td> + <td>vds.idealstate.bucket_replicas_copying_in</td> <td>bucket</td> <td>Bucket replicas that should be copied in, e.g. node does not have a replica for a bucket that it is in ideal state for</td> </tr> <tr> - <td><p id="vds_idealstate_bucket_replicas_syncing">vds.idealstate.bucket_replicas_syncing</p></td> + <td>vds.idealstate.bucket_replicas_syncing</td> <td>bucket</td> <td>Bucket replicas that need syncing due to mismatching metadata</td> </tr> <tr> - <td><p id="vds_idealstate_max_observed_time_since_last_gc_sec">vds.idealstate.max_observed_time_since_last_gc_sec</p></td> + <td>vds.idealstate.max_observed_time_since_last_gc_sec</td> <td>second</td> <td>Maximum time (in seconds) since GC was last successfully run for a bucket. Aggregated max value across all buckets on the distributor.</td> </tr> <tr> - <td><p id="vds_idealstate_delete_bucket_done_ok">vds.idealstate.delete_bucket.done_ok</p></td> + <td>vds.idealstate.delete_bucket.done_ok</td> <td>operation</td> <td>The number of operations successfully performed</td> </tr> <tr> - <td><p id="vds_idealstate_delete_bucket_done_failed">vds.idealstate.delete_bucket.done_failed</p></td> + <td>vds.idealstate.delete_bucket.done_failed</td> <td>operation</td> <td>The number of operations that failed</td> </tr> <tr> - <td><p id="vds_idealstate_delete_bucket_pending">vds.idealstate.delete_bucket.pending</p></td> + <td>vds.idealstate.delete_bucket.pending</td> <td>operation</td> <td>The number of operations pending</td> </tr> <tr> - <td><p id="vds_idealstate_delete_bucket_blocked">vds.idealstate.delete_bucket.blocked</p></td> + <td>vds.idealstate.delete_bucket.blocked</td> <td>operation</td> <td>The number of operations blocked by blocking operation starter</td> </tr> <tr> - <td><p id="vds_idealstate_delete_bucket_throttled">vds.idealstate.delete_bucket.throttled</p></td> + <td>vds.idealstate.delete_bucket.throttled</td> <td>operation</td> <td>The number of operations throttled by throttling operation starter</td> </tr> <tr> - <td><p id="vds_idealstate_merge_bucket_done_ok">vds.idealstate.merge_bucket.done_ok</p></td> + <td>vds.idealstate.merge_bucket.done_ok</td> <td>operation</td> <td>The number of operations successfully performed</td> </tr> <tr> - <td><p id="vds_idealstate_merge_bucket_done_failed">vds.idealstate.merge_bucket.done_failed</p></td> + <td>vds.idealstate.merge_bucket.done_failed</td> <td>operation</td> <td>The number of operations that failed</td> </tr> <tr> - <td><p id="vds_idealstate_merge_bucket_pending">vds.idealstate.merge_bucket.pending</p></td> + <td>vds.idealstate.merge_bucket.pending</td> <td>operation</td> <td>The number of operations pending</td> </tr> <tr> - <td><p id="vds_idealstate_merge_bucket_blocked">vds.idealstate.merge_bucket.blocked</p></td> + <td>vds.idealstate.merge_bucket.blocked</td> <td>operation</td> <td>The number of operations blocked by blocking operation starter</td> </tr> <tr> - <td><p id="vds_idealstate_merge_bucket_throttled">vds.idealstate.merge_bucket.throttled</p></td> + <td>vds.idealstate.merge_bucket.throttled</td> <td>operation</td> <td>The number of operations throttled by throttling operation starter</td> </tr> <tr> - <td><p id="vds_idealstate_merge_bucket_source_only_copy_changed">vds.idealstate.merge_bucket.source_only_copy_changed</p></td> + <td>vds.idealstate.merge_bucket.source_only_copy_changed</td> <td>operation</td> <td>The number of merge operations where source-only copy changed</td> </tr> <tr> - <td><p id="vds_idealstate_merge_bucket_source_only_copy_delete_blocked">vds.idealstate.merge_bucket.source_only_copy_delete_blocked</p></td> + <td>vds.idealstate.merge_bucket.source_only_copy_delete_blocked</td> <td>operation</td> <td>The number of merge operations where delete of unchanged source-only copies was blocked</td> </tr> <tr> - <td><p id="vds_idealstate_merge_bucket_source_only_copy_delete_failed">vds.idealstate.merge_bucket.source_only_copy_delete_failed</p></td> + <td>vds.idealstate.merge_bucket.source_only_copy_delete_failed</td> <td>operation</td> <td>The number of merge operations where delete of unchanged source-only copies failed</td> </tr> <tr> - <td><p id="vds_idealstate_split_bucket_done_ok">vds.idealstate.split_bucket.done_ok</p></td> + <td>vds.idealstate.split_bucket.done_ok</td> <td>operation</td> <td>The number of operations successfully performed</td> </tr> <tr> - <td><p id="vds_idealstate_split_bucket_done_failed">vds.idealstate.split_bucket.done_failed</p></td> + <td>vds.idealstate.split_bucket.done_failed</td> <td>operation</td> <td>The number of operations that failed</td> </tr> <tr> - <td><p id="vds_idealstate_split_bucket_pending">vds.idealstate.split_bucket.pending</p></td> + <td>vds.idealstate.split_bucket.pending</td> <td>operation</td> <td>The number of operations pending</td> </tr> <tr> - <td><p id="vds_idealstate_split_bucket_blocked">vds.idealstate.split_bucket.blocked</p></td> + <td>vds.idealstate.split_bucket.blocked</td> <td>operation</td> <td>The number of operations blocked by blocking operation starter</td> </tr> <tr> - <td><p id="vds_idealstate_split_bucket_throttled">vds.idealstate.split_bucket.throttled</p></td> + <td>vds.idealstate.split_bucket.throttled</td> <td>operation</td> <td>The number of operations throttled by throttling operation starter</td> </tr> <tr> - <td><p id="vds_idealstate_join_bucket_done_ok">vds.idealstate.join_bucket.done_ok</p></td> + <td>vds.idealstate.join_bucket.done_ok</td> <td>operation</td> <td>The number of operations successfully performed</td> </tr> <tr> - <td><p id="vds_idealstate_join_bucket_done_failed">vds.idealstate.join_bucket.done_failed</p></td> + <td>vds.idealstate.join_bucket.done_failed</td> <td>operation</td> <td>The number of operations that failed</td> </tr> <tr> - <td><p id="vds_idealstate_join_bucket_pending">vds.idealstate.join_bucket.pending</p></td> + <td>vds.idealstate.join_bucket.pending</td> <td>operation</td> <td>The number of operations pending</td> </tr> <tr> - <td><p id="vds_idealstate_join_bucket_blocked">vds.idealstate.join_bucket.blocked</p></td> + <td>vds.idealstate.join_bucket.blocked</td> <td>operation</td> <td>The number of operations blocked by blocking operation starter</td> </tr> <tr> - <td><p id="vds_idealstate_join_bucket_throttled">vds.idealstate.join_bucket.throttled</p></td> + <td>vds.idealstate.join_bucket.throttled</td> <td>operation</td> <td>The number of operations throttled by throttling operation starter</td> </tr> <tr> - <td><p id="vds_idealstate_garbage_collection_done_ok">vds.idealstate.garbage_collection.done_ok</p></td> + <td>vds.idealstate.garbage_collection.done_ok</td> <td>operation</td> <td>The number of operations successfully performed</td> </tr> <tr> - <td><p id="vds_idealstate_garbage_collection_done_failed">vds.idealstate.garbage_collection.done_failed</p></td> + <td>vds.idealstate.garbage_collection.done_failed</td> <td>operation</td> <td>The number of operations that failed</td> </tr> <tr> - <td><p id="vds_idealstate_garbage_collection_pending">vds.idealstate.garbage_collection.pending</p></td> + <td>vds.idealstate.garbage_collection.pending</td> <td>operation</td> <td>The number of operations pending</td> </tr> <tr> - <td><p id="vds_idealstate_garbage_collection_documents_removed">vds.idealstate.garbage_collection.documents_removed</p></td> + <td>vds.idealstate.garbage_collection.documents_removed</td> <td>document</td> <td>Number of documents removed by GC operations</td> </tr> <tr> - <td><p id="vds_idealstate_garbage_collection_blocked">vds.idealstate.garbage_collection.blocked</p></td> + <td>vds.idealstate.garbage_collection.blocked</td> <td>operation</td> <td>The number of operations blocked by blocking operation starter</td> </tr> <tr> - <td><p id="vds_idealstate_garbage_collection_throttled">vds.idealstate.garbage_collection.throttled</p></td> + <td>vds.idealstate.garbage_collection.throttled</td> <td>operation</td> <td>The number of operations throttled by throttling operation starter</td> </tr> <tr> - <td><p id="vds_distributor_puts_latency">vds.distributor.puts.latency</p></td> + <td>vds.distributor.puts.latency</td> <td>millisecond</td> <td>The latency of put operations</td> </tr> <tr> - <td><p id="vds_distributor_puts_ok">vds.distributor.puts.ok</p></td> + <td>vds.distributor.puts.ok</td> <td>operation</td> <td>The number of successful put operations performed</td> </tr> <tr> - <td><p id="vds_distributor_puts_failures_total">vds.distributor.puts.failures.total</p></td> + <td>vds.distributor.puts.failures.total</td> <td>operation</td> <td>Sum of all failures</td> </tr> <tr> - <td><p id="vds_distributor_puts_failures_notfound">vds.distributor.puts.failures.notfound</p></td> + <td>vds.distributor.puts.failures.notfound</td> <td>operation</td> <td>The number of operations that failed because the document did not exist</td> </tr> <tr> - <td><p id="vds_distributor_puts_failures_test_and_set_failed">vds.distributor.puts.failures.test_and_set_failed</p></td> + <td>vds.distributor.puts.failures.test_and_set_failed</td> <td>operation</td> <td>The number of mutating operations that failed because they specified a test-and-set condition that did not match the existing document</td> </tr> <tr> - <td><p id="vds_distributor_puts_failures_concurrent_mutations">vds.distributor.puts.failures.concurrent_mutations</p></td> + <td>vds.distributor.puts.failures.concurrent_mutations</td> <td>operation</td> <td>The number of operations that were transiently failed due to a mutating operation already being in progress for its document ID</td> </tr> <tr> - <td><p id="vds_distributor_puts_failures_notconnected">vds.distributor.puts.failures.notconnected</p></td> + <td>vds.distributor.puts.failures.notconnected</td> <td>operation</td> <td>The number of operations discarded because there were no available storage nodes to send to</td> </tr> <tr> - <td><p id="vds_distributor_puts_failures_notready">vds.distributor.puts.failures.notready</p></td> + <td>vds.distributor.puts.failures.notready</td> <td>operation</td> <td>The number of operations discarded because distributor was not ready</td> </tr> <tr> - <td><p id="vds_distributor_puts_failures_wrongdistributor">vds.distributor.puts.failures.wrongdistributor</p></td> + <td>vds.distributor.puts.failures.wrongdistributor</td> <td>operation</td> <td>The number of operations discarded because they were sent to the wrong distributor</td> </tr> <tr> - <td><p id="vds_distributor_puts_failures_safe_time_not_reached">vds.distributor.puts.failures.safe_time_not_reached</p></td> + <td>vds.distributor.puts.failures.safe_time_not_reached</td> <td>operation</td> <td>The number of operations that were transiently failed due to them arriving before the safe time point for bucket ownership handovers has passed</td> </tr> <tr> - <td><p id="vds_distributor_puts_failures_storagefailure">vds.distributor.puts.failures.storagefailure</p></td> + <td>vds.distributor.puts.failures.storagefailure</td> <td>operation</td> <td>The number of operations that failed in storage</td> </tr> <tr> - <td><p id="vds_distributor_puts_failures_timeout">vds.distributor.puts.failures.timeout</p></td> + <td>vds.distributor.puts.failures.timeout</td> <td>operation</td> <td>The number of operations that failed because the operation timed out towards storage</td> </tr> <tr> - <td><p id="vds_distributor_puts_failures_busy">vds.distributor.puts.failures.busy</p></td> + <td>vds.distributor.puts.failures.busy</td> <td>operation</td> <td>The number of messages from storage that failed because the storage node was busy</td> </tr> <tr> - <td><p id="vds_distributor_puts_failures_inconsistent_bucket">vds.distributor.puts.failures.inconsistent_bucket</p></td> + <td>vds.distributor.puts.failures.inconsistent_bucket</td> <td>operation</td> <td>The number of operations failed due to buckets being in an inconsistent state or not found</td> </tr> <tr> - <td><p id="vds_distributor_removes_latency">vds.distributor.removes.latency</p></td> + <td>vds.distributor.removes.latency</td> <td>millisecond</td> <td>The latency of remove operations</td> </tr> <tr> - <td><p id="vds_distributor_removes_ok">vds.distributor.removes.ok</p></td> + <td>vds.distributor.removes.ok</td> <td>operation</td> <td>The number of successful removes operations performed</td> </tr> <tr> - <td><p id="vds_distributor_removes_failures_total">vds.distributor.removes.failures.total</p></td> + <td>vds.distributor.removes.failures.total</td> <td>operation</td> <td>Sum of all failures</td> </tr> <tr> - <td><p id="vds_distributor_removes_failures_notfound">vds.distributor.removes.failures.notfound</p></td> + <td>vds.distributor.removes.failures.notfound</td> <td>operation</td> <td>The number of operations that failed because the document did not exist</td> </tr> <tr> - <td><p id="vds_distributor_removes_failures_test_and_set_failed">vds.distributor.removes.failures.test_and_set_failed</p></td> + <td>vds.distributor.removes.failures.test_and_set_failed</td> <td>operation</td> <td>The number of mutating operations that failed because they specified a test-and-set condition that did not match the existing document</td> </tr> <tr> - <td><p id="vds_distributor_removes_failures_concurrent_mutations">vds.distributor.removes.failures.concurrent_mutations</p></td> + <td>vds.distributor.removes.failures.concurrent_mutations</td> <td>operation</td> <td>The number of operations that were transiently failed due to a mutating operation already being in progress for its document ID</td> </tr> <tr> - <td><p id="vds_distributor_removes_failures_busy">vds.distributor.removes.failures.busy</p></td> + <td>vds.distributor.removes.failures.busy</td> <td>operation</td> <td>The number of messages from storage that failed because the storage node was busy</td> </tr> <tr> - <td><p id="vds_distributor_removes_failures_inconsistent_bucket">vds.distributor.removes.failures.inconsistent_bucket</p></td> + <td>vds.distributor.removes.failures.inconsistent_bucket</td> <td>operation</td> <td>The number of operations failed due to buckets being in an inconsistent state or not found</td> </tr> <tr> - <td><p id="vds_distributor_removes_failures_notconnected">vds.distributor.removes.failures.notconnected</p></td> + <td>vds.distributor.removes.failures.notconnected</td> <td>operation</td> <td>The number of operations discarded because there were no available storage nodes to send to</td> </tr> <tr> - <td><p id="vds_distributor_removes_failures_notready">vds.distributor.removes.failures.notready</p></td> + <td>vds.distributor.removes.failures.notready</td> <td>operation</td> <td>The number of operations discarded because distributor was not ready</td> </tr> <tr> - <td><p id="vds_distributor_removes_failures_safe_time_not_reached">vds.distributor.removes.failures.safe_time_not_reached</p></td> + <td>vds.distributor.removes.failures.safe_time_not_reached</td> <td>operation</td> <td>The number of operations that were transiently failed due to them arriving before the safe time point for bucket ownership handovers has passed</td> </tr> <tr> - <td><p id="vds_distributor_removes_failures_storagefailure">vds.distributor.removes.failures.storagefailure</p></td> + <td>vds.distributor.removes.failures.storagefailure</td> <td>operation</td> <td>The number of operations that failed in storage</td> </tr> <tr> - <td><p id="vds_distributor_removes_failures_timeout">vds.distributor.removes.failures.timeout</p></td> + <td>vds.distributor.removes.failures.timeout</td> <td>operation</td> <td>The number of operations that failed because the operation timed out towards storage</td> </tr> <tr> - <td><p id="vds_distributor_removes_failures_wrongdistributor">vds.distributor.removes.failures.wrongdistributor</p></td> + <td>vds.distributor.removes.failures.wrongdistributor</td> <td>operation</td> <td>The number of operations discarded because they were sent to the wrong distributor</td> </tr> <tr> - <td><p id="vds_distributor_updates_latency">vds.distributor.updates.latency</p></td> + <td>vds.distributor.updates.latency</td> <td>millisecond</td> <td>The latency of update operations</td> </tr> <tr> - <td><p id="vds_distributor_updates_ok">vds.distributor.updates.ok</p></td> + <td>vds.distributor.updates.ok</td> <td>operation</td> <td>The number of successful updates operations performed</td> </tr> <tr> - <td><p id="vds_distributor_updates_failures_total">vds.distributor.updates.failures.total</p></td> + <td>vds.distributor.updates.failures.total</td> <td>operation</td> <td>Sum of all failures</td> </tr> <tr> - <td><p id="vds_distributor_updates_failures_notfound">vds.distributor.updates.failures.notfound</p></td> + <td>vds.distributor.updates.failures.notfound</td> <td>operation</td> <td>The number of operations that failed because the document did not exist</td> </tr> <tr> - <td><p id="vds_distributor_updates_failures_test_and_set_failed">vds.distributor.updates.failures.test_and_set_failed</p></td> + <td>vds.distributor.updates.failures.test_and_set_failed</td> <td>operation</td> <td>The number of mutating operations that failed because they specified a test-and-set condition that did not match the existing document</td> </tr> <tr> - <td><p id="vds_distributor_updates_failures_concurrent_mutations">vds.distributor.updates.failures.concurrent_mutations</p></td> + <td>vds.distributor.updates.failures.concurrent_mutations</td> <td>operation</td> <td>The number of operations that were transiently failed due to a mutating operation already being in progress for its document ID</td> </tr> <tr> - <td><p id="vds_distributor_updates_diverging_timestamp_updates">vds.distributor.updates.diverging_timestamp_updates</p></td> + <td>vds.distributor.updates.diverging_timestamp_updates</td> <td>operation</td> <td>Number of updates that report they were performed against divergent version timestamps on different replicas</td> </tr> <tr> - <td><p id="vds_distributor_updates_failures_busy">vds.distributor.updates.failures.busy</p></td> + <td>vds.distributor.updates.failures.busy</td> <td>operation</td> <td>The number of messages from storage that failed because the storage node was busy</td> </tr> <tr> - <td><p id="vds_distributor_updates_failures_inconsistent_bucket">vds.distributor.updates.failures.inconsistent_bucket</p></td> + <td>vds.distributor.updates.failures.inconsistent_bucket</td> <td>operation</td> <td>The number of operations failed due to buckets being in an inconsistent state or not found</td> </tr> <tr> - <td><p id="vds_distributor_updates_failures_notconnected">vds.distributor.updates.failures.notconnected</p></td> + <td>vds.distributor.updates.failures.notconnected</td> <td>operation</td> <td>The number of operations discarded because there were no available storage nodes to send to</td> </tr> <tr> - <td><p id="vds_distributor_updates_failures_notready">vds.distributor.updates.failures.notready</p></td> + <td>vds.distributor.updates.failures.notready</td> <td>operation</td> <td>The number of operations discarded because distributor was not ready</td> </tr> <tr> - <td><p id="vds_distributor_updates_failures_safe_time_not_reached">vds.distributor.updates.failures.safe_time_not_reached</p></td> + <td>vds.distributor.updates.failures.safe_time_not_reached</td> <td>operation</td> <td>The number of operations that were transiently failed due to them arriving before the safe time point for bucket ownership handovers has passed</td> </tr> <tr> - <td><p id="vds_distributor_updates_failures_storagefailure">vds.distributor.updates.failures.storagefailure</p></td> + <td>vds.distributor.updates.failures.storagefailure</td> <td>operation</td> <td>The number of operations that failed in storage</td> </tr> <tr> - <td><p id="vds_distributor_updates_failures_timeout">vds.distributor.updates.failures.timeout</p></td> + <td>vds.distributor.updates.failures.timeout</td> <td>operation</td> <td>The number of operations that failed because the operation timed out towards storage</td> </tr> <tr> - <td><p id="vds_distributor_updates_failures_wrongdistributor">vds.distributor.updates.failures.wrongdistributor</p></td> + <td>vds.distributor.updates.failures.wrongdistributor</td> <td>operation</td> <td>The number of operations discarded because they were sent to the wrong distributor</td> </tr> <tr> - <td><p id="vds_distributor_updates_fast_path_restarts">vds.distributor.updates.fast_path_restarts</p></td> + <td>vds.distributor.updates.fast_path_restarts</td> <td>operation</td> <td>Number of safe path (write repair) updates that were restarted as fast path updates because all replicas returned documents with the same timestamp in the initial read phase</td> </tr> <tr> - <td><p id="vds_distributor_removelocations_ok">vds.distributor.removelocations.ok</p></td> + <td>vds.distributor.removelocations.ok</td> <td>operation</td> <td>The number of successful removelocations operations performed</td> </tr> <tr> - <td><p id="vds_distributor_removelocations_failures_total">vds.distributor.removelocations.failures.total</p></td> + <td>vds.distributor.removelocations.failures.total</td> <td>operation</td> <td>Sum of all failures</td> </tr> <tr> - <td><p id="vds_distributor_removelocations_failures_busy">vds.distributor.removelocations.failures.busy</p></td> + <td>vds.distributor.removelocations.failures.busy</td> <td>operation</td> <td>The number of messages from storage that failed because the storage node was busy</td> </tr> <tr> - <td><p id="vds_distributor_removelocations_failures_concurrent_mutations">vds.distributor.removelocations.failures.concurrent_mutations</p></td> + <td>vds.distributor.removelocations.failures.concurrent_mutations</td> <td>operation</td> <td>The number of operations that were transiently failed due to a mutating operation already being in progress for its document ID</td> </tr> <tr> - <td><p id="vds_distributor_removelocations_failures_inconsistent_bucket">vds.distributor.removelocations.failures.inconsistent_bucket</p></td> + <td>vds.distributor.removelocations.failures.inconsistent_bucket</td> <td>operation</td> <td>The number of operations failed due to buckets being in an inconsistent state or not found</td> </tr> <tr> - <td><p id="vds_distributor_removelocations_failures_notconnected">vds.distributor.removelocations.failures.notconnected</p></td> + <td>vds.distributor.removelocations.failures.notconnected</td> <td>operation</td> <td>The number of operations discarded because there were no available storage nodes to send to</td> </tr> <tr> - <td><p id="vds_distributor_removelocations_failures_notfound">vds.distributor.removelocations.failures.notfound</p></td> + <td>vds.distributor.removelocations.failures.notfound</td> <td>operation</td> <td>The number of operations that failed because the document did not exist</td> </tr> <tr> - <td><p id="vds_distributor_removelocations_failures_notready">vds.distributor.removelocations.failures.notready</p></td> + <td>vds.distributor.removelocations.failures.notready</td> <td>operation</td> <td>The number of operations discarded because distributor was not ready</td> </tr> <tr> - <td><p id="vds_distributor_removelocations_failures_safe_time_not_reached">vds.distributor.removelocations.failures.safe_time_not_reached</p></td> + <td>vds.distributor.removelocations.failures.safe_time_not_reached</td> <td>operation</td> <td>The number of operations that were transiently failed due to them arriving before the safe time point for bucket ownership handovers has passed</td> </tr> <tr> - <td><p id="vds_distributor_removelocations_failures_storagefailure">vds.distributor.removelocations.failures.storagefailure</p></td> + <td>vds.distributor.removelocations.failures.storagefailure</td> <td>operation</td> <td>The number of operations that failed in storage</td> </tr> <tr> - <td><p id="vds_distributor_removelocations_failures_test_and_set_failed">vds.distributor.removelocations.failures.test_and_set_failed</p></td> + <td>vds.distributor.removelocations.failures.test_and_set_failed</td> <td>operation</td> <td>The number of mutating operations that failed because they specified a test-and-set condition that did not match the existing document</td> </tr> <tr> - <td><p id="vds_distributor_removelocations_failures_timeout">vds.distributor.removelocations.failures.timeout</p></td> + <td>vds.distributor.removelocations.failures.timeout</td> <td>operation</td> <td>The number of operations that failed because the operation timed out towards storage</td> </tr> <tr> - <td><p id="vds_distributor_removelocations_failures_wrongdistributor">vds.distributor.removelocations.failures.wrongdistributor</p></td> + <td>vds.distributor.removelocations.failures.wrongdistributor</td> <td>operation</td> <td>The number of operations discarded because they were sent to the wrong distributor</td> </tr> <tr> - <td><p id="vds_distributor_removelocations_latency">vds.distributor.removelocations.latency</p></td> + <td>vds.distributor.removelocations.latency</td> <td>millisecond</td> <td>The average latency of removelocations operations</td> </tr> <tr> - <td><p id="vds_distributor_gets_latency">vds.distributor.gets.latency</p></td> + <td>vds.distributor.gets.latency</td> <td>millisecond</td> <td>The average latency of gets operations</td> </tr> <tr> - <td><p id="vds_distributor_gets_ok">vds.distributor.gets.ok</p></td> + <td>vds.distributor.gets.ok</td> <td>operation</td> <td>The number of successful gets operations performed</td> </tr> <tr> - <td><p id="vds_distributor_gets_failures_total">vds.distributor.gets.failures.total</p></td> + <td>vds.distributor.gets.failures.total</td> <td>operation</td> <td>Sum of all failures</td> </tr> <tr> - <td><p id="vds_distributor_gets_failures_notfound">vds.distributor.gets.failures.notfound</p></td> + <td>vds.distributor.gets.failures.notfound</td> <td>operation</td> <td>The number of operations that failed because the document did not exist</td> </tr> <tr> - <td><p id="vds_distributor_gets_failures_busy">vds.distributor.gets.failures.busy</p></td> + <td>vds.distributor.gets.failures.busy</td> <td>operation</td> <td>The number of messages from storage that failed because the storage node was busy</td> </tr> <tr> - <td><p id="vds_distributor_gets_failures_concurrent_mutations">vds.distributor.gets.failures.concurrent_mutations</p></td> + <td>vds.distributor.gets.failures.concurrent_mutations</td> <td>operation</td> <td>The number of operations that were transiently failed due to a mutating operation already being in progress for its document ID</td> </tr> <tr> - <td><p id="vds_distributor_gets_failures_inconsistent_bucket">vds.distributor.gets.failures.inconsistent_bucket</p></td> + <td>vds.distributor.gets.failures.inconsistent_bucket</td> <td>operation</td> <td>The number of operations failed due to buckets being in an inconsistent state or not found</td> </tr> <tr> - <td><p id="vds_distributor_gets_failures_notconnected">vds.distributor.gets.failures.notconnected</p></td> + <td>vds.distributor.gets.failures.notconnected</td> <td>operation</td> <td>The number of operations discarded because there were no available storage nodes to send to</td> </tr> <tr> - <td><p id="vds_distributor_gets_failures_notready">vds.distributor.gets.failures.notready</p></td> + <td>vds.distributor.gets.failures.notready</td> <td>operation</td> <td>The number of operations discarded because distributor was not ready</td> </tr> <tr> - <td><p id="vds_distributor_gets_failures_safe_time_not_reached">vds.distributor.gets.failures.safe_time_not_reached</p></td> + <td>vds.distributor.gets.failures.safe_time_not_reached</td> <td>operation</td> <td>The number of operations that were transiently failed due to them arriving before the safe time point for bucket ownership handovers has passed</td> </tr> <tr> - <td><p id="vds_distributor_gets_failures_storagefailure">vds.distributor.gets.failures.storagefailure</p></td> + <td>vds.distributor.gets.failures.storagefailure</td> <td>operation</td> <td>The number of operations that failed in storage</td> </tr> <tr> - <td><p id="vds_distributor_gets_failures_test_and_set_failed">vds.distributor.gets.failures.test_and_set_failed</p></td> + <td>vds.distributor.gets.failures.test_and_set_failed</td> <td>operation</td> <td>The number of mutating operations that failed because they specified a test-and-set condition that did not match the existing document</td> </tr> <tr> - <td><p id="vds_distributor_gets_failures_timeout">vds.distributor.gets.failures.timeout</p></td> + <td>vds.distributor.gets.failures.timeout</td> <td>operation</td> <td>The number of operations that failed because the operation timed out towards storage</td> </tr> <tr> - <td><p id="vds_distributor_gets_failures_wrongdistributor">vds.distributor.gets.failures.wrongdistributor</p></td> + <td>vds.distributor.gets.failures.wrongdistributor</td> <td>operation</td> <td>The number of operations discarded because they were sent to the wrong distributor</td> </tr> <tr> - <td><p id="vds_distributor_visitor_latency">vds.distributor.visitor.latency</p></td> + <td>vds.distributor.visitor.latency</td> <td>millisecond</td> <td>The average latency of visitor operations</td> </tr> <tr> - <td><p id="vds_distributor_visitor_ok">vds.distributor.visitor.ok</p></td> + <td>vds.distributor.visitor.ok</td> <td>operation</td> <td>The number of successful visitor operations performed</td> </tr> <tr> - <td><p id="vds_distributor_visitor_failures_total">vds.distributor.visitor.failures.total</p></td> + <td>vds.distributor.visitor.failures.total</td> <td>operation</td> <td>Sum of all failures</td> </tr> <tr> - <td><p id="vds_distributor_visitor_failures_notready">vds.distributor.visitor.failures.notready</p></td> + <td>vds.distributor.visitor.failures.notready</td> <td>operation</td> <td>The number of operations discarded because distributor was not ready</td> </tr> <tr> - <td><p id="vds_distributor_visitor_failures_notconnected">vds.distributor.visitor.failures.notconnected</p></td> + <td>vds.distributor.visitor.failures.notconnected</td> <td>operation</td> <td>The number of operations discarded because there were no available storage nodes to send to</td> </tr> <tr> - <td><p id="vds_distributor_visitor_failures_wrongdistributor">vds.distributor.visitor.failures.wrongdistributor</p></td> + <td>vds.distributor.visitor.failures.wrongdistributor</td> <td>operation</td> <td>The number of operations discarded because they were sent to the wrong distributor</td> </tr> <tr> - <td><p id="vds_distributor_visitor_failures_safe_time_not_reached">vds.distributor.visitor.failures.safe_time_not_reached</p></td> + <td>vds.distributor.visitor.failures.safe_time_not_reached</td> <td>operation</td> <td>The number of operations that were transiently failed due to them arriving before the safe time point for bucket ownership handovers has passed</td> </tr> <tr> - <td><p id="vds_distributor_visitor_failures_storagefailure">vds.distributor.visitor.failures.storagefailure</p></td> + <td>vds.distributor.visitor.failures.storagefailure</td> <td>operation</td> <td>The number of operations that failed in storage</td> </tr> <tr> - <td><p id="vds_distributor_visitor_failures_timeout">vds.distributor.visitor.failures.timeout</p></td> + <td>vds.distributor.visitor.failures.timeout</td> <td>operation</td> <td>The number of operations that failed because the operation timed out towards storage</td> </tr> <tr> - <td><p id="vds_distributor_visitor_failures_busy">vds.distributor.visitor.failures.busy</p></td> + <td>vds.distributor.visitor.failures.busy</td> <td>operation</td> <td>The number of messages from storage that failed because the storage node was busy</td> </tr> <tr> - <td><p id="vds_distributor_visitor_failures_inconsistent_bucket">vds.distributor.visitor.failures.inconsistent_bucket</p></td> + <td>vds.distributor.visitor.failures.inconsistent_bucket</td> <td>operation</td> <td>The number of operations failed due to buckets being in an inconsistent state or not found</td> </tr> <tr> - <td><p id="vds_distributor_visitor_failures_notfound">vds.distributor.visitor.failures.notfound</p></td> + <td>vds.distributor.visitor.failures.notfound</td> <td>operation</td> <td>The number of operations that failed because the document did not exist</td> </tr> <tr> - <td><p id="vds_distributor_visitor_bytes_per_visitor">vds.distributor.visitor.bytes_per_visitor</p></td> + <td>vds.distributor.visitor.bytes_per_visitor</td> <td>operation</td> <td>The number of bytes visited on content nodes as part of a single client visitor command</td> </tr> <tr> - <td><p id="vds_distributor_visitor_docs_per_visitor">vds.distributor.visitor.docs_per_visitor</p></td> + <td>vds.distributor.visitor.docs_per_visitor</td> <td>operation</td> <td>The number of documents visited on content nodes as part of a single client visitor command</td> </tr> <tr> - <td><p id="vds_distributor_visitor_failures_concurrent_mutations">vds.distributor.visitor.failures.concurrent_mutations</p></td> + <td>vds.distributor.visitor.failures.concurrent_mutations</td> <td>operation</td> <td>The number of operations that were transiently failed due to a mutating operation already being in progress for its document ID</td> </tr> <tr> - <td><p id="vds_distributor_visitor_failures_test_and_set_failed">vds.distributor.visitor.failures.test_and_set_failed</p></td> + <td>vds.distributor.visitor.failures.test_and_set_failed</td> <td>operation</td> <td>The number of mutating operations that failed because they specified a test-and-set condition that did not match the existing document</td> </tr> <tr> - <td><p id="vds_distributor_docsstored">vds.distributor.docsstored</p></td> + <td>vds.distributor.docsstored</td> <td>document</td> <td>Number of documents stored in all buckets controlled by this distributor</td> </tr> <tr> - <td><p id="vds_distributor_bytesstored">vds.distributor.bytesstored</p></td> + <td>vds.distributor.bytesstored</td> <td>byte</td> <td>Number of bytes stored in all buckets controlled by this distributor</td> </tr> <tr> - <td><p id="metricmanager_periodichooklatency">metricmanager.periodichooklatency</p></td> + <td>metricmanager.periodichooklatency</td> <td>millisecond</td> <td>Time in ms used to update a single periodic hook</td> </tr> <tr> - <td><p id="metricmanager_resetlatency">metricmanager.resetlatency</p></td> + <td>metricmanager.resetlatency</td> <td>millisecond</td> <td>Time in ms used to reset all metrics.</td> </tr> <tr> - <td><p id="metricmanager_sleeptime">metricmanager.sleeptime</p></td> + <td>metricmanager.sleeptime</td> <td>millisecond</td> <td>Time in ms worker thread is sleeping</td> </tr> <tr> - <td><p id="metricmanager_snapshothooklatency">metricmanager.snapshothooklatency</p></td> + <td>metricmanager.snapshothooklatency</td> <td>millisecond</td> <td>Time in ms used to update a single snapshot hook</td> </tr> <tr> - <td><p id="metricmanager_snapshotlatency">metricmanager.snapshotlatency</p></td> + <td>metricmanager.snapshotlatency</td> <td>millisecond</td> <td>Time in ms used to take a snapshot</td> </tr> <tr> - <td><p id="vds_distributor_activate_cluster_state_processing_time">vds.distributor.activate_cluster_state_processing_time</p></td> + <td>vds.distributor.activate_cluster_state_processing_time</td> <td>millisecond</td> <td>Elapsed time where the distributor thread is blocked on merging pending bucket info into its bucket database upon activating a cluster state</td> </tr> <tr> - <td><p id="vds_distributor_bucket_db_memory_usage_allocated_bytes">vds.distributor.bucket_db.memory_usage.allocated_bytes</p></td> + <td>vds.distributor.bucket_db.memory_usage.allocated_bytes</td> <td>byte</td> <td>The number of allocated bytes</td> </tr> <tr> - <td><p id="vds_distributor_bucket_db_memory_usage_dead_bytes">vds.distributor.bucket_db.memory_usage.dead_bytes</p></td> + <td>vds.distributor.bucket_db.memory_usage.dead_bytes</td> <td>byte</td> - <td>The number of dead bytes (<= used_bytes)</td> + <td>The number of dead bytes (<code>{`<=`}</code> used_bytes)</td> </tr> <tr> - <td><p id="vds_distributor_bucket_db_memory_usage_onhold_bytes">vds.distributor.bucket_db.memory_usage.onhold_bytes</p></td> + <td>vds.distributor.bucket_db.memory_usage.onhold_bytes</td> <td>byte</td> <td>The number of bytes on hold</td> </tr> <tr> - <td><p id="vds_distributor_bucket_db_memory_usage_used_bytes">vds.distributor.bucket_db.memory_usage.used_bytes</p></td> + <td>vds.distributor.bucket_db.memory_usage.used_bytes</td> <td>byte</td> - <td>The number of used bytes (<= allocated_bytes)</td> + <td>The number of used bytes (<code>{`<=`}</code> allocated_bytes)</td> </tr> <tr> - <td><p id="vds_distributor_getbucketlists_failures_busy">vds.distributor.getbucketlists.failures.busy</p></td> + <td>vds.distributor.getbucketlists.failures.busy</td> <td>operation</td> <td>The number of messages from storage that failed because the storage node was busy</td> </tr> <tr> - <td><p id="vds_distributor_getbucketlists_failures_concurrent_mutations">vds.distributor.getbucketlists.failures.concurrent_mutations</p></td> + <td>vds.distributor.getbucketlists.failures.concurrent_mutations</td> <td>operation</td> <td>The number of operations that were transiently failed due to a mutating operation already being in progress for its document ID</td> </tr> <tr> - <td><p id="vds_distributor_getbucketlists_failures_inconsistent_bucket">vds.distributor.getbucketlists.failures.inconsistent_bucket</p></td> + <td>vds.distributor.getbucketlists.failures.inconsistent_bucket</td> <td>operation</td> <td>The number of operations failed due to buckets being in an inconsistent state or not found</td> </tr> <tr> - <td><p id="vds_distributor_getbucketlists_failures_notconnected">vds.distributor.getbucketlists.failures.notconnected</p></td> + <td>vds.distributor.getbucketlists.failures.notconnected</td> <td>operation</td> <td>The number of operations discarded because there were no available storage nodes to send to</td> </tr> <tr> - <td><p id="vds_distributor_getbucketlists_failures_notfound">vds.distributor.getbucketlists.failures.notfound</p></td> + <td>vds.distributor.getbucketlists.failures.notfound</td> <td>operation</td> <td>The number of operations that failed because the document did not exist</td> </tr> <tr> - <td><p id="vds_distributor_getbucketlists_failures_notready">vds.distributor.getbucketlists.failures.notready</p></td> + <td>vds.distributor.getbucketlists.failures.notready</td> <td>operation</td> <td>The number of operations discarded because distributor was not ready</td> </tr> <tr> - <td><p id="vds_distributor_getbucketlists_failures_safe_time_not_reached">vds.distributor.getbucketlists.failures.safe_time_not_reached</p></td> + <td>vds.distributor.getbucketlists.failures.safe_time_not_reached</td> <td>operation</td> <td>The number of operations that were transiently failed due to them arriving before the safe time point for bucket ownership handovers has passed</td> </tr> <tr> - <td><p id="vds_distributor_getbucketlists_failures_storagefailure">vds.distributor.getbucketlists.failures.storagefailure</p></td> + <td>vds.distributor.getbucketlists.failures.storagefailure</td> <td>operation</td> <td>The number of operations that failed in storage</td> </tr> <tr> - <td><p id="vds_distributor_getbucketlists_failures_test_and_set_failed">vds.distributor.getbucketlists.failures.test_and_set_failed</p></td> + <td>vds.distributor.getbucketlists.failures.test_and_set_failed</td> <td>operation</td> <td>The number of mutating operations that failed because they specified a test-and-set condition that did not match the existing document</td> </tr> <tr> - <td><p id="vds_distributor_getbucketlists_failures_timeout">vds.distributor.getbucketlists.failures.timeout</p></td> + <td>vds.distributor.getbucketlists.failures.timeout</td> <td>operation</td> <td>The number of operations that failed because the operation timed out towards storage</td> </tr> <tr> - <td><p id="vds_distributor_getbucketlists_failures_total">vds.distributor.getbucketlists.failures.total</p></td> + <td>vds.distributor.getbucketlists.failures.total</td> <td>operation</td> <td>Total number of failures</td> </tr> <tr> - <td><p id="vds_distributor_getbucketlists_failures_wrongdistributor">vds.distributor.getbucketlists.failures.wrongdistributor</p></td> + <td>vds.distributor.getbucketlists.failures.wrongdistributor</td> <td>operation</td> <td>The number of operations discarded because they were sent to the wrong distributor</td> </tr> <tr> - <td><p id="vds_distributor_getbucketlists_latency">vds.distributor.getbucketlists.latency</p></td> + <td>vds.distributor.getbucketlists.latency</td> <td>millisecond</td> <td>The average latency of getbucketlists operations</td> </tr> <tr> - <td><p id="vds_distributor_getbucketlists_ok">vds.distributor.getbucketlists.ok</p></td> + <td>vds.distributor.getbucketlists.ok</td> <td>operation</td> <td>The number of successful getbucketlists operations performed</td> </tr> <tr> - <td><p id="vds_distributor_recoverymodeschedulingtime">vds.distributor.recoverymodeschedulingtime</p></td> + <td>vds.distributor.recoverymodeschedulingtime</td> <td>millisecond</td> <td>Time spent scheduling operations in recovery mode after receiving new cluster state</td> </tr> <tr> - <td><p id="vds_distributor_set_cluster_state_processing_time">vds.distributor.set_cluster_state_processing_time</p></td> + <td>vds.distributor.set_cluster_state_processing_time</td> <td>millisecond</td> <td>Elapsed time where the distributor thread is blocked on processing its bucket database upon receiving a new cluster state</td> </tr> <tr> - <td><p id="vds_distributor_state_transition_time">vds.distributor.state_transition_time</p></td> + <td>vds.distributor.state_transition_time</td> <td>millisecond</td> <td>Time it takes to complete a cluster state transition. If a state transition is preempted before completing, its elapsed time is counted as part of the total time spent for the final, completed state transition</td> </tr> <tr> - <td><p id="vds_distributor_stats_failures_busy">vds.distributor.stats.failures.busy</p></td> + <td>vds.distributor.stats.failures.busy</td> <td>operation</td> <td>The number of messages from storage that failed because the storage node was busy</td> </tr> <tr> - <td><p id="vds_distributor_stats_failures_concurrent_mutations">vds.distributor.stats.failures.concurrent_mutations</p></td> + <td>vds.distributor.stats.failures.concurrent_mutations</td> <td>operation</td> <td>The number of operations that were transiently failed due to a mutating operation already being in progress for its document ID</td> </tr> <tr> - <td><p id="vds_distributor_stats_failures_inconsistent_bucket">vds.distributor.stats.failures.inconsistent_bucket</p></td> + <td>vds.distributor.stats.failures.inconsistent_bucket</td> <td>operation</td> <td>The number of operations failed due to buckets being in an inconsistent state or not found</td> </tr> <tr> - <td><p id="vds_distributor_stats_failures_notconnected">vds.distributor.stats.failures.notconnected</p></td> + <td>vds.distributor.stats.failures.notconnected</td> <td>operation</td> <td>The number of operations discarded because there were no available storage nodes to send to</td> </tr> <tr> - <td><p id="vds_distributor_stats_failures_notfound">vds.distributor.stats.failures.notfound</p></td> + <td>vds.distributor.stats.failures.notfound</td> <td>operation</td> <td>The number of operations that failed because the document did not exist</td> </tr> <tr> - <td><p id="vds_distributor_stats_failures_notready">vds.distributor.stats.failures.notready</p></td> + <td>vds.distributor.stats.failures.notready</td> <td>operation</td> <td>The number of operations discarded because distributor was not ready</td> </tr> <tr> - <td><p id="vds_distributor_stats_failures_safe_time_not_reached">vds.distributor.stats.failures.safe_time_not_reached</p></td> + <td>vds.distributor.stats.failures.safe_time_not_reached</td> <td>operation</td> <td>The number of operations that were transiently failed due to them arriving before the safe time point for bucket ownership handovers has passed</td> </tr> <tr> - <td><p id="vds_distributor_stats_failures_storagefailure">vds.distributor.stats.failures.storagefailure</p></td> + <td>vds.distributor.stats.failures.storagefailure</td> <td>operation</td> <td>The number of operations that failed in storage</td> </tr> <tr> - <td><p id="vds_distributor_stats_failures_test_and_set_failed">vds.distributor.stats.failures.test_and_set_failed</p></td> + <td>vds.distributor.stats.failures.test_and_set_failed</td> <td>operation</td> <td>The number of mutating operations that failed because they specified a test-and-set condition that did not match the existing document</td> </tr> <tr> - <td><p id="vds_distributor_stats_failures_timeout">vds.distributor.stats.failures.timeout</p></td> + <td>vds.distributor.stats.failures.timeout</td> <td>operation</td> <td>The number of operations that failed because the operation timed out towards storage</td> </tr> <tr> - <td><p id="vds_distributor_stats_failures_total">vds.distributor.stats.failures.total</p></td> + <td>vds.distributor.stats.failures.total</td> <td>operation</td> <td>The total number of failures</td> </tr> <tr> - <td><p id="vds_distributor_stats_failures_wrongdistributor">vds.distributor.stats.failures.wrongdistributor</p></td> + <td>vds.distributor.stats.failures.wrongdistributor</td> <td>operation</td> <td>The number of operations discarded because they were sent to the wrong distributor</td> </tr> <tr> - <td><p id="vds_distributor_stats_latency">vds.distributor.stats.latency</p></td> + <td>vds.distributor.stats.latency</td> <td>millisecond</td> <td>The average latency of stats operations</td> </tr> <tr> - <td><p id="vds_distributor_stats_ok">vds.distributor.stats.ok</p></td> + <td>vds.distributor.stats.ok</td> <td>operation</td> <td>The number of successful stats operations performed</td> </tr> <tr> - <td><p id="vds_distributor_update_gets_failures_busy">vds.distributor.update_gets.failures.busy</p></td> + <td>vds.distributor.update_gets.failures.busy</td> <td>operation</td> <td>The number of messages from storage that failed because the storage node was busy</td> </tr> <tr> - <td><p id="vds_distributor_update_gets_failures_concurrent_mutations">vds.distributor.update_gets.failures.concurrent_mutations</p></td> + <td>vds.distributor.update_gets.failures.concurrent_mutations</td> <td>operation</td> <td>The number of operations that were transiently failed due to a mutating operation already being in progress for its document ID</td> </tr> <tr> - <td><p id="vds_distributor_update_gets_failures_inconsistent_bucket">vds.distributor.update_gets.failures.inconsistent_bucket</p></td> + <td>vds.distributor.update_gets.failures.inconsistent_bucket</td> <td>operation</td> <td>The number of operations failed due to buckets being in an inconsistent state or not found</td> </tr> <tr> - <td><p id="vds_distributor_update_gets_failures_notconnected">vds.distributor.update_gets.failures.notconnected</p></td> + <td>vds.distributor.update_gets.failures.notconnected</td> <td>operation</td> <td>The number of operations discarded because there were no available storage nodes to send to</td> </tr> <tr> - <td><p id="vds_distributor_update_gets_failures_notfound">vds.distributor.update_gets.failures.notfound</p></td> + <td>vds.distributor.update_gets.failures.notfound</td> <td>operation</td> <td>The number of operations that failed because the document did not exist</td> </tr> <tr> - <td><p id="vds_distributor_update_gets_failures_notready">vds.distributor.update_gets.failures.notready</p></td> + <td>vds.distributor.update_gets.failures.notready</td> <td>operation</td> <td>The number of operations discarded because distributor was not ready</td> </tr> <tr> - <td><p id="vds_distributor_update_gets_failures_safe_time_not_reached">vds.distributor.update_gets.failures.safe_time_not_reached</p></td> + <td>vds.distributor.update_gets.failures.safe_time_not_reached</td> <td>operation</td> <td>The number of operations that were transiently failed due to them arriving before the safe time point for bucket ownership handovers has passed</td> </tr> <tr> - <td><p id="vds_distributor_update_gets_failures_storagefailure">vds.distributor.update_gets.failures.storagefailure</p></td> + <td>vds.distributor.update_gets.failures.storagefailure</td> <td>operation</td> <td>The number of operations that failed in storage</td> </tr> <tr> - <td><p id="vds_distributor_update_gets_failures_test_and_set_failed">vds.distributor.update_gets.failures.test_and_set_failed</p></td> + <td>vds.distributor.update_gets.failures.test_and_set_failed</td> <td>operation</td> <td>The number of mutating operations that failed because they specified a test-and-set condition that did not match the existing document</td> </tr> <tr> - <td><p id="vds_distributor_update_gets_failures_timeout">vds.distributor.update_gets.failures.timeout</p></td> + <td>vds.distributor.update_gets.failures.timeout</td> <td>operation</td> <td>The number of operations that failed because the operation timed out towards storage</td> </tr> <tr> - <td><p id="vds_distributor_update_gets_failures_total">vds.distributor.update_gets.failures.total</p></td> + <td>vds.distributor.update_gets.failures.total</td> <td>operation</td> <td>The total number of failures</td> </tr> <tr> - <td><p id="vds_distributor_update_gets_failures_wrongdistributor">vds.distributor.update_gets.failures.wrongdistributor</p></td> + <td>vds.distributor.update_gets.failures.wrongdistributor</td> <td>operation</td> <td>The number of operations discarded because they were sent to the wrong distributor</td> </tr> <tr> - <td><p id="vds_distributor_update_gets_latency">vds.distributor.update_gets.latency</p></td> + <td>vds.distributor.update_gets.latency</td> <td>millisecond</td> <td>The average latency of update_gets operations</td> </tr> <tr> - <td><p id="vds_distributor_update_gets_ok">vds.distributor.update_gets.ok</p></td> + <td>vds.distributor.update_gets.ok</td> <td>operation</td> <td>The number of successful update_gets operations performed</td> </tr> <tr> - <td><p id="vds_distributor_update_metadata_gets_failures_busy">vds.distributor.update_metadata_gets.failures.busy</p></td> + <td>vds.distributor.update_metadata_gets.failures.busy</td> <td>operation</td> <td>The number of messages from storage that failed because the storage node was busy</td> </tr> <tr> - <td><p id="vds_distributor_update_metadata_gets_failures_concurrent_mutations">vds.distributor.update_metadata_gets.failures.concurrent_mutations</p></td> + <td>vds.distributor.update_metadata_gets.failures.concurrent_mutations</td> <td>operation</td> <td>The number of operations that were transiently failed due to a mutating operation already being in progress for its document ID</td> </tr> <tr> - <td><p id="vds_distributor_update_metadata_gets_failures_inconsistent_bucket">vds.distributor.update_metadata_gets.failures.inconsistent_bucket</p></td> + <td>vds.distributor.update_metadata_gets.failures.inconsistent_bucket</td> <td>operation</td> <td>The number of operations failed due to buckets being in an inconsistent state or not found</td> </tr> <tr> - <td><p id="vds_distributor_update_metadata_gets_failures_notconnected">vds.distributor.update_metadata_gets.failures.notconnected</p></td> + <td>vds.distributor.update_metadata_gets.failures.notconnected</td> <td>operation</td> <td>The number of operations discarded because there were no available storage nodes to send to</td> </tr> <tr> - <td><p id="vds_distributor_update_metadata_gets_failures_notfound">vds.distributor.update_metadata_gets.failures.notfound</p></td> + <td>vds.distributor.update_metadata_gets.failures.notfound</td> <td>operation</td> <td>The number of operations that failed because the document did not exist</td> </tr> <tr> - <td><p id="vds_distributor_update_metadata_gets_failures_notready">vds.distributor.update_metadata_gets.failures.notready</p></td> + <td>vds.distributor.update_metadata_gets.failures.notready</td> <td>operation</td> <td>The number of operations discarded because distributor was not ready</td> </tr> <tr> - <td><p id="vds_distributor_update_metadata_gets_failures_safe_time_not_reached">vds.distributor.update_metadata_gets.failures.safe_time_not_reached</p></td> + <td>vds.distributor.update_metadata_gets.failures.safe_time_not_reached</td> <td>operation</td> <td>The number of operations that were transiently failed due to them arriving before the safe time point for bucket ownership handovers has passed</td> </tr> <tr> - <td><p id="vds_distributor_update_metadata_gets_failures_storagefailure">vds.distributor.update_metadata_gets.failures.storagefailure</p></td> + <td>vds.distributor.update_metadata_gets.failures.storagefailure</td> <td>operation</td> <td>The number of operations that failed in storage</td> </tr> <tr> - <td><p id="vds_distributor_update_metadata_gets_failures_test_and_set_failed">vds.distributor.update_metadata_gets.failures.test_and_set_failed</p></td> + <td>vds.distributor.update_metadata_gets.failures.test_and_set_failed</td> <td>operation</td> <td>The number of mutating operations that failed because they specified a test-and-set condition that did not match the existing document</td> </tr> <tr> - <td><p id="vds_distributor_update_metadata_gets_failures_timeout">vds.distributor.update_metadata_gets.failures.timeout</p></td> + <td>vds.distributor.update_metadata_gets.failures.timeout</td> <td>operation</td> <td>The number of operations that failed because the operation timed out towards storage</td> </tr> <tr> - <td><p id="vds_distributor_update_metadata_gets_failures_total">vds.distributor.update_metadata_gets.failures.total</p></td> + <td>vds.distributor.update_metadata_gets.failures.total</td> <td>operation</td> <td>The total number of failures</td> </tr> <tr> - <td><p id="vds_distributor_update_metadata_gets_failures_wrongdistributor">vds.distributor.update_metadata_gets.failures.wrongdistributor</p></td> + <td>vds.distributor.update_metadata_gets.failures.wrongdistributor</td> <td>operation</td> <td>The number of operations discarded because they were sent to the wrong distributor</td> </tr> <tr> - <td><p id="vds_distributor_update_metadata_gets_latency">vds.distributor.update_metadata_gets.latency</p></td> + <td>vds.distributor.update_metadata_gets.latency</td> <td>millisecond</td> <td>The average latency of update_metadata_gets operations</td> </tr> <tr> - <td><p id="vds_distributor_update_metadata_gets_ok">vds.distributor.update_metadata_gets.ok</p></td> + <td>vds.distributor.update_metadata_gets.ok</td> <td>operation</td> <td>The number of successful update_metadata_gets operations performed</td> </tr> <tr> - <td><p id="vds_distributor_update_puts_failures_busy">vds.distributor.update_puts.failures.busy</p></td> + <td>vds.distributor.update_puts.failures.busy</td> <td>operation</td> <td>The number of messages from storage that failed because the storage node was busy</td> </tr> <tr> - <td><p id="vds_distributor_update_puts_failures_concurrent_mutations">vds.distributor.update_puts.failures.concurrent_mutations</p></td> + <td>vds.distributor.update_puts.failures.concurrent_mutations</td> <td>operation</td> <td>The number of operations that were transiently failed due to a mutating operation already being in progress for its document ID</td> </tr> <tr> - <td><p id="vds_distributor_update_puts_failures_inconsistent_bucket">vds.distributor.update_puts.failures.inconsistent_bucket</p></td> + <td>vds.distributor.update_puts.failures.inconsistent_bucket</td> <td>operation</td> <td>The number of operations failed due to buckets being in an inconsistent state or not found</td> </tr> <tr> - <td><p id="vds_distributor_update_puts_failures_notconnected">vds.distributor.update_puts.failures.notconnected</p></td> + <td>vds.distributor.update_puts.failures.notconnected</td> <td>operation</td> <td>The number of operations discarded because there were no available storage nodes to send to</td> </tr> <tr> - <td><p id="vds_distributor_update_puts_failures_notfound">vds.distributor.update_puts.failures.notfound</p></td> + <td>vds.distributor.update_puts.failures.notfound</td> <td>operation</td> <td>The number of operations that failed because the document did not exist</td> </tr> <tr> - <td><p id="vds_distributor_update_puts_failures_notready">vds.distributor.update_puts.failures.notready</p></td> + <td>vds.distributor.update_puts.failures.notready</td> <td>operation</td> <td>The number of operations discarded because distributor was not ready</td> </tr> <tr> - <td><p id="vds_distributor_update_puts_failures_safe_time_not_reached">vds.distributor.update_puts.failures.safe_time_not_reached</p></td> + <td>vds.distributor.update_puts.failures.safe_time_not_reached</td> <td>operation</td> <td>The number of operations that were transiently failed due to them arriving before the safe time point for bucket ownership handovers has passed</td> </tr> <tr> - <td><p id="vds_distributor_update_puts_failures_storagefailure">vds.distributor.update_puts.failures.storagefailure</p></td> + <td>vds.distributor.update_puts.failures.storagefailure</td> <td>operation</td> <td>The number of operations that failed in storage</td> </tr> <tr> - <td><p id="vds_distributor_update_puts_failures_test_and_set_failed">vds.distributor.update_puts.failures.test_and_set_failed</p></td> + <td>vds.distributor.update_puts.failures.test_and_set_failed</td> <td>operation</td> <td>The number of mutating operations that failed because they specified a test-and-set condition that did not match the existing document</td> </tr> <tr> - <td><p id="vds_distributor_update_puts_failures_timeout">vds.distributor.update_puts.failures.timeout</p></td> + <td>vds.distributor.update_puts.failures.timeout</td> <td>operation</td> <td>The number of operations that failed because the operation timed out towards storage</td> </tr> <tr> - <td><p id="vds_distributor_update_puts_failures_total">vds.distributor.update_puts.failures.total</p></td> + <td>vds.distributor.update_puts.failures.total</td> <td>operation</td> <td>The total number of put failures</td> </tr> <tr> - <td><p id="vds_distributor_update_puts_failures_wrongdistributor">vds.distributor.update_puts.failures.wrongdistributor</p></td> + <td>vds.distributor.update_puts.failures.wrongdistributor</td> <td>operation</td> <td>The number of operations discarded because they were sent to the wrong distributor</td> </tr> <tr> - <td><p id="vds_distributor_update_puts_latency">vds.distributor.update_puts.latency</p></td> + <td>vds.distributor.update_puts.latency</td> <td>millisecond</td> <td>The average latency of update_puts operations</td> </tr> <tr> - <td><p id="vds_distributor_update_puts_ok">vds.distributor.update_puts.ok</p></td> + <td>vds.distributor.update_puts.ok</td> <td>operation</td> <td>The number of successful update_puts operations performed</td> </tr> <tr> - <td><p id="vds_distributor_mutating_op_memory_usage">vds.distributor.mutating_op_memory_usage</p></td> + <td>vds.distributor.mutating_op_memory_usage</td> <td>byte</td> <td>Estimated amount of memory used by active mutating operations across all distributor stripes, in bytes</td> </tr> <tr> - <td><p id="vds_idealstate_nodes_per_merge">vds.idealstate.nodes_per_merge</p></td> + <td>vds.idealstate.nodes_per_merge</td> <td>node</td> <td>The number of nodes involved in a single merge operation.</td> </tr> <tr> - <td><p id="vds_idealstate_set_bucket_state_blocked">vds.idealstate.set_bucket_state.blocked</p></td> + <td>vds.idealstate.set_bucket_state.blocked</td> <td>operation</td> <td>The number of operations blocked by blocking operation starter</td> </tr> <tr> - <td><p id="vds_idealstate_set_bucket_state_done_failed">vds.idealstate.set_bucket_state.done_failed</p></td> + <td>vds.idealstate.set_bucket_state.done_failed</td> <td>operation</td> <td>The number of operations that failed</td> </tr> <tr> - <td><p id="vds_idealstate_set_bucket_state_done_ok">vds.idealstate.set_bucket_state.done_ok</p></td> + <td>vds.idealstate.set_bucket_state.done_ok</td> <td>operation</td> <td>The number of operations successfully performed</td> </tr> <tr> - <td><p id="vds_idealstate_set_bucket_state_pending">vds.idealstate.set_bucket_state.pending</p></td> + <td>vds.idealstate.set_bucket_state.pending</td> <td>operation</td> <td>The number of operations pending</td> </tr> <tr> - <td><p id="vds_idealstate_set_bucket_state_throttled">vds.idealstate.set_bucket_state.throttled</p></td> + <td>vds.idealstate.set_bucket_state.throttled</td> <td>operation</td> <td>The number of operations throttled by throttling operation starter</td> </tr> <tr> - <td><p id="vds_bouncer_clock_skew_aborts">vds.bouncer.clock_skew_aborts</p></td> + <td>vds.bouncer.clock_skew_aborts</td> <td>operation</td> <td>Number of client operations that were aborted due to clock skew between sender and receiver exceeding acceptable range</td> </tr> </tbody> -</table> +</table> \ No newline at end of file diff --git a/en/reference/operations/metrics/logd.html b/en/reference/operations/metrics/logd.html deleted file mode 100644 index 1388705d78..0000000000 --- a/en/reference/operations/metrics/logd.html +++ /dev/null @@ -1,19 +0,0 @@ ---- -# Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -title: "Logd Metrics" -redirect_from: -- en/reference/logd-metrics-reference.html ---- - -<table class="table"> - <thead> - <tr><th>Name</th><th>Unit</th><th>Description</th></tr> - </thead> - <tbody> - <tr> - <td><p id="logd_processed_lines">logd.processed.lines</p></td> - <td>item</td> - <td>Number of log lines processed</td> - </tr> - </tbody> -</table> diff --git a/en/reference/operations/metrics/logd.mdx b/en/reference/operations/metrics/logd.mdx new file mode 100644 index 0000000000..aeb682d05f --- /dev/null +++ b/en/reference/operations/metrics/logd.mdx @@ -0,0 +1,21 @@ +--- +title: "Logd Metrics" +sidebarTitle: "Logd metrics" +--- + +<table className="vespa-table"> + <thead> + <tr> + <th>Name</th> + <th>Unit</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>logd.processed.lines</td> + <td>item</td> + <td>Number of log lines processed</td> + </tr> + </tbody> +</table> \ No newline at end of file diff --git a/en/reference/operations/metrics/metric-units.html b/en/reference/operations/metrics/metric-units.html deleted file mode 100644 index 7bce9fdcde..0000000000 --- a/en/reference/operations/metrics/metric-units.html +++ /dev/null @@ -1,215 +0,0 @@ ---- -# Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -title: "Metric Units Reference" -redirect_from: -- en/reference/unit-metrics-reference.html ---- - - -<table class="table"> - <thead> - <tr><th>Unit</th><th>Description</th></tr> - </thead> - <tbody> - <tr> - <td>binary</td> - <td>Zero or one. Zero typically indicate "false" while one indicate "true"</td> - </tr> - <tr> - <td>bucket</td> - <td>A chunk of documents managed by a distributor service</td> - </tr> - <tr> - <td>buffer</td> - <td>A buffer</td> - </tr> - <tr> - <td>byte</td> - <td>A collection of 8 bits</td> - </tr> - <tr> - <td>byte/second</td> - <td>A unit of storage capable of holding 8 bits</td> - </tr> - <tr> - <td>class</td> - <td>A instance of a Java class</td> - </tr> - <tr> - <td>connection</td> - <td>A link used for communication between a client and a server</td> - </tr> - <tr> - <td>context switch</td> - <td>A context switch</td> - </tr> - <tr> - <td>deployment</td> - <td>A deployment on hosted Vespa</td> - </tr> - <tr> - <td>distance</td> - <td>A number describing the distance of two tensors</td> - </tr> - <tr> - <td>document</td> - <td>Vespa document, a collection of fields defined in a schema file</td> - </tr> - <tr> - <td>documentid</td> - <td>A unique document identifier</td> - </tr> - <tr> - <td>dollar</td> - <td>US dollar</td> - </tr> - <tr> - <td>dollar/hour</td> - <td>Total current cost of the cluster in $/hr</td> - </tr> - <tr> - <td>failure</td> - <td>Failures, typically for requests, operations or nodes</td> - </tr> - <tr> - <td>file</td> - <td>Data file stored on the disk on a node</td> - </tr> - <tr> - <td>fraction</td> - <td>A value in the range [0..1]. Higher values can occur for some metrics, but would indicate the value is outside the allowed range.</td> - </tr> - <tr> - <td>ratio</td> - <td>A dimensionless ratio between two values.</td> - </tr> - <tr> - <td>generation</td> - <td>Typically, generation of configuration or application package</td> - </tr> - <tr> - <td>gigabyte</td> - <td>One billion bytes</td> - </tr> - <tr> - <td>graph node</td> - <td>A node in a graph</td> - </tr> - <tr> - <td>hit</td> - <td>Document that meets the filtering/restriction criteria specified by a given query</td> - </tr> - <tr> - <td>hit/query</td> - <td>Number of hits per query over a period of time</td> - </tr> - <tr> - <td>host</td> - <td>Bare metal computer that contain nodes</td> - </tr> - <tr> - <td>instance</td> - <td>Typically, tenant or application</td> - </tr> - <tr> - <td>item</td> - <td>Object or unit maintained in e.g. a queue</td> - </tr> - <tr> - <td>millisecond</td> - <td>Millisecond, 1/1000 of a second</td> - </tr> - <tr> - <td>nanosecond</td> - <td>Nanosecond, 1/1000.000.000 of a second</td> - </tr> - <tr> - <td>node</td> - <td>(Virtual) computer that is part of a Vespa cluster</td> - </tr> - <tr> - <td>packet</td> - <td>Collection of data transmitted over the network as a single unit</td> - </tr> - <tr> - <td>operation</td> - <td>A clearly defined task</td> - </tr> - <tr> - <td>operation/second</td> - <td>Number of operations per second</td> - </tr> - <tr> - <td>percentage</td> - <td>A number expressed as a fraction of 100, normally in the range [0..100].</td> - </tr> - <tr> - <td>query</td> - <td>A request for matching, grouping and/or scoring documents stored in Vespa</td> - </tr> - <tr> - <td>query/second</td> - <td>Number of queries per second.</td> - </tr> - <tr> - <td>record</td> - <td>A collection of information, typically a set of key/value, e.g. stored in a transaction log</td> - </tr> - <tr> - <td>request</td> - <td>A request sent from a client to a server</td> - </tr> - <tr> - <td>response</td> - <td>A response from a server to a client, typically as a response to a request</td> - </tr> - <tr> - <td>restart</td> - <td>A service or node restarts</td> - </tr> - <tr> - <td>routing rotation</td> - <td>Routing rotation</td> - </tr> - <tr> - <td>score</td> - <td>Relevance score for a document</td> - </tr> - <tr> - <td>second</td> - <td>Time span of 1 second</td> - </tr> - <tr> - <td>seconds since epoch</td> - <td>Seconds since Unix Epoch</td> - </tr> - <tr> - <td>session</td> - <td>A set of operations taking place during one connection or as part of a higher level operation</td> - </tr> - <tr> - <td>task</td> - <td>Piece of work executed by a server, e.g. to perform back-ground data maintenance</td> - </tr> - <tr> - <td>tenant</td> - <td>Tenant that owns zero or more applications in a managed Vespa system</td> - </tr> - <tr> - <td>thread</td> - <td>Computer thread for executing e.g. tasks, operations or queries</td> - </tr> - <tr> - <td>vcpu</td> - <td>Virtual CPU</td> - </tr> - <tr> - <td>version</td> - <td>Software or config version</td> - </tr> - <tr> - <td>wakeup</td> - <td>Computer thread wake-ups for doing some work</td> - </tr> - </tbody> -</table> diff --git a/en/reference/unit-metrics-reference.html b/en/reference/operations/metrics/metric-units.mdx similarity index 95% rename from en/reference/unit-metrics-reference.html rename to en/reference/operations/metrics/metric-units.mdx index c586ad108e..7ecc103aa2 100644 --- a/en/reference/unit-metrics-reference.html +++ b/en/reference/operations/metrics/metric-units.mdx @@ -1,12 +1,14 @@ ---- -# Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +--- title: "Metric Units Reference" +sidebarTitle: "Metric units" --- - -<table class="table"> +<table className="vespa-table"> <thead> - <tr><th>Unit</th><th>Description</th></tr> + <tr> + <th>Unit</th> + <th>Description</th> + </tr> </thead> <tbody> <tr> @@ -77,6 +79,10 @@ <td>fraction</td> <td>A value in the range [0..1]. Higher values can occur for some metrics, but would indicate the value is outside the allowed range.</td> </tr> + <tr> + <td>ratio</td> + <td>A dimensionless ratio between two values.</td> + </tr> <tr> <td>generation</td> <td>Typically, generation of configuration or application package</td> @@ -205,5 +211,5 @@ <td>wakeup</td> <td>Computer thread wake-ups for doing some work</td> </tr> - </tbody> -</table> + </tbody> +</table> \ No newline at end of file diff --git a/en/reference/operations/metrics/metrics.md b/en/reference/operations/metrics/metrics.md deleted file mode 100644 index b74241c6f3..0000000000 --- a/en/reference/operations/metrics/metrics.md +++ /dev/null @@ -1,50 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Metrics Reference" -redirect_from: - - en/reference/metrics-reference.html ---- - -# Metrics reference documentation - -## Metric types - -Each metric have one of the types documented [here](metric-units.html). - -## Metric aggregator suffixes - -Metrics are collected over a time period so a metric reading must aggregate individual samples over this period. -These are specified by adding the aggregator suffix to the metric name: <code>metric.name.aggregator</code>. - -The following aggregators are available: - -| Aggregator name (metric suffix) | Explanation | -|---------------------------------|---------------------------------------------------------------------| -| 95percentile | The 95 percentile of samples in the period | -| 99percentile | The 99 percentile of samples in the period | -| average | The average of samples in the period | -| count | The count of samples in the period | -| last | The last value sampled in the period | -| max | The max value sampled in the period | -| min | The min value sampled in the period | -| rate | The count of samples divided by the length of the period in seconds | -| sum | The sum of the sampled values in the period | - -## Metric sets defined in Vespa - -A metric set is a collection of metrics which can be referenced for convenience. -The following metric sets are defined in Vespa. - -* [Vespa Metric Set Reference](vespa-metric-set.html) -* [Default Metric Set Reference](default-metric-set.html) -* [Metric Units Reference](metric-units.html) -* [Container Metrics Reference](container.html) -* [Distributor Metrics Reference](distributor.html) -* [Searchnode Metrics Reference](searchnode.html) -* [Storage Metrics Reference](storage.html) -* [Configserver Metrics Reference](configserver.html) -* [Logd Metrics Reference](logd.html) -* [Node Admin Metrics Reference](nodeadmin.html) -* [Slobrok Metrics Reference](slobrok.html) -* [Clustercontroller Metrics Reference](clustercontroller.html) -* [Sentinel Metrics Reference](sentinel.html) diff --git a/en/reference/operations/metrics/metrics.mdx b/en/reference/operations/metrics/metrics.mdx new file mode 100644 index 0000000000..dd306d11a2 --- /dev/null +++ b/en/reference/operations/metrics/metrics.mdx @@ -0,0 +1,83 @@ +--- +title: "Metrics Reference" +--- + +## Metrics reference documentation + +### Metric types + +Each metric have one of the types documented [here](/en/reference/operations/metrics/metric-units). + +### Metric aggregator suffixes + +Metrics are collected over a time period so a metric reading must aggregate individual samples over this period. These are specified by adding the aggregator suffix to the metric name: <code>metric.name.aggregator</code>. + +The following aggregators are available: + +<table className="vespa-table"> + <thead> + <tr> + <th>Aggregator name (metric suffix)</th> + <th>Explanation</th> + </tr> + </thead> + <tbody> + <tr> + <td>95percentile</td> + <td>The 95 percentile of samples in the period</td> + </tr> + <tr> + <td>99percentile</td> + <td>The 99 percentile of samples in the period</td> + </tr> + <tr> + <td>average</td> + <td>The average of samples in the period</td> + </tr> + <tr> + <td>count</td> + <td>The count of samples in the period</td> + </tr> + <tr> + <td>last</td> + <td>The last value sampled in the period</td> + </tr> + <tr> + <td>max</td> + <td>The max value sampled in the period</td> + </tr> + <tr> + <td>min</td> + <td>The min value sampled in the period</td> + </tr> + <tr> + <td>rate</td> + <td>The count of samples divided by the length of the period in seconds</td> + </tr> + <tr> + <td>sum</td> + <td>The sum of the sampled values in the period</td> + </tr> + </tbody> +</table> + +### Metric sets defined in Vespa + +A metric set is a collection of metrics which can be referenced for convenience. +The following metric sets are defined in Vespa. + +<CardGroup> + <Card title="Vespa Metric Set Reference" icon="bar-chart" href="/en/reference/operations/metrics/vespa-metric-set" horizontal /> + <Card title="Default Metric Set Reference" icon="list" href="/en/reference/operations/metrics/default-metric-set" horizontal /> + <Card title="Metric Units Reference" icon="ruler" href="/en/reference/operations/metrics/metric-units" horizontal /> + <Card title="Container Metrics Reference" icon="box" href="/en/reference/operations/metrics/container" horizontal /> + <Card title="Distributor Metrics Reference" icon="code-branch" href="/en/reference/operations/metrics/distributor" horizontal /> + <Card title="Searchnode Metrics Reference" icon="search" href="/en/reference/operations/metrics/searchnode" horizontal /> + <Card title="Storage Metrics Reference" icon="database" href="/en/reference/operations/metrics/storage" horizontal /> + <Card title="Configserver Metrics Reference" icon="gear" href="/en/reference/operations/metrics/configserver" horizontal /> + <Card title="Logd Metrics Reference" icon="file-text" href="/en/reference/operations/metrics/logd" horizontal /> + <Card title="Node Admin Metrics Reference" icon="server" href="/en/reference/operations/metrics/nodeadmin" horizontal /> + <Card title="Slobrok Metrics Reference" icon="chart-bar" href="/en/reference/operations/metrics/slobrok" horizontal /> + <Card title="Clustercontroller Metrics Reference" icon="layer-group" href="/en/reference/operations/metrics/clustercontroller" horizontal /> + <Card title="Sentinel Metrics Reference" icon="shield" href="/en/reference/operations/metrics/sentinel" horizontal /> +</CardGroup> diff --git a/en/reference/operations/metrics/nodeadmin.html b/en/reference/operations/metrics/nodeadmin.html deleted file mode 100644 index d1a0bbee9d..0000000000 --- a/en/reference/operations/metrics/nodeadmin.html +++ /dev/null @@ -1,24 +0,0 @@ ---- -# Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -title: "NodeAdmin Metrics" -redirect_from: -- en/reference/nodeadmin-metrics-reference.html ---- - -<table class="table"> - <thead> - <tr><th>Name</th><th>Unit</th><th>Description</th></tr> - </thead> - <tbody> - <tr> - <td><p id="endpoint_certificate_expiry_seconds">endpoint.certificate.expiry.seconds</p></td> - <td>second</td> - <td>Time until node endpoint certificate expires</td> - </tr> - <tr> - <td><p id="node-certificate_expiry_seconds">node-certificate.expiry.seconds</p></td> - <td>second</td> - <td>Time until node certificate expires</td> - </tr> - </tbody> -</table> diff --git a/en/reference/operations/metrics/nodeadmin.mdx b/en/reference/operations/metrics/nodeadmin.mdx new file mode 100644 index 0000000000..54c09b786c --- /dev/null +++ b/en/reference/operations/metrics/nodeadmin.mdx @@ -0,0 +1,26 @@ +--- +title: "NodeAdmin Metrics" +sidebarTitle: "Node Admin metrics" +--- + +<table className="vespa-table"> + <thead> + <tr> + <th>Name</th> + <th>Unit</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>endpoint.certificate.expiry.seconds</td> + <td>second</td> + <td>Time until node endpoint certificate expires</td> + </tr> + <tr> + <td>node-certificate.expiry.seconds</td> + <td>second</td> + <td>Time until node certificate expires</td> + </tr> + </tbody> +</table> \ No newline at end of file diff --git a/en/reference/operations/metrics/searchnode.html b/en/reference/operations/metrics/searchnode.html deleted file mode 100644 index 9315c54935..0000000000 --- a/en/reference/operations/metrics/searchnode.html +++ /dev/null @@ -1,1324 +0,0 @@ ---- -# Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -title: "SearchNode Metrics" -redirect_from: -- en/reference/searchnode-metrics-reference.html ---- - -<table class="table"> - <thead> - <tr><th>Name</th><th>Unit</th><th>Description</th></tr> - </thead> - <tbody> - <tr> - <td><p id="content_proton_config_generation">content.proton.config.generation</p></td> - <td>version</td> - <td>The oldest config generation used by this search node</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_documents_total">content.proton.documentdb.documents.total</p></td> - <td>document</td> - <td>The total number of documents in this documents db (ready + not-ready)</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_documents_ready">content.proton.documentdb.documents.ready</p></td> - <td>document</td> - <td>The number of ready documents in this document db</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_documents_active">content.proton.documentdb.documents.active</p></td> - <td>document</td> - <td>The number of active / searchable documents in this document db</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_documents_removed">content.proton.documentdb.documents.removed</p></td> - <td>document</td> - <td>The number of removed documents in this document db</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_index_docs_in_memory">content.proton.documentdb.index.docs_in_memory</p></td> - <td>document</td> - <td>Number of documents in memory index</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_disk_usage">content.proton.documentdb.disk_usage</p></td> - <td>byte</td> - <td>The total disk usage (in bytes) for this document db</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_memory_usage_allocated_bytes">content.proton.documentdb.memory_usage.allocated_bytes</p></td> - <td>byte</td> - <td>The number of allocated bytes</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_memory_usage_dead_bytes">content.proton.documentdb.memory_usage.dead_bytes</p></td> - <td>byte</td> - <td>The number of dead bytes (<= used_bytes)</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_memory_usage_onhold_bytes">content.proton.documentdb.memory_usage.onhold_bytes</p></td> - <td>byte</td> - <td>The number of bytes on hold</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_memory_usage_used_bytes">content.proton.documentdb.memory_usage.used_bytes</p></td> - <td>byte</td> - <td>The number of used bytes (<= allocated_bytes)</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_heart_beat_age">content.proton.documentdb.heart_beat_age</p></td> - <td>second</td> - <td>How long ago (in seconds) heart beat maintenance job was run</td> - </tr> - <tr> - <td><p id="content_proton_docsum_count">content.proton.docsum.count</p></td> - <td>request</td> - <td>Docsum requests handled</td> - </tr> - <tr> - <td><p id="content_proton_docsum_docs">content.proton.docsum.docs</p></td> - <td>document</td> - <td>Total docsums returned</td> - </tr> - <tr> - <td><p id="content_proton_docsum_latency">content.proton.docsum.latency</p></td> - <td>millisecond</td> - <td>Docsum request latency</td> - </tr> - <tr> - <td><p id="content_proton_search_protocol_query_latency">content.proton.search_protocol.query.latency</p></td> - <td>second</td> - <td>Query request latency (seconds)</td> - </tr> - <tr> - <td><p id="content_proton_search_protocol_query_request_size">content.proton.search_protocol.query.request_size</p></td> - <td>byte</td> - <td>Query request size (network bytes)</td> - </tr> - <tr> - <td><p id="content_proton_search_protocol_query_reply_size">content.proton.search_protocol.query.reply_size</p></td> - <td>byte</td> - <td>Query reply size (network bytes)</td> - </tr> - <tr> - <td><p id="content_proton_search_protocol_docsum_latency">content.proton.search_protocol.docsum.latency</p></td> - <td>second</td> - <td>Docsum request latency (seconds)</td> - </tr> - <tr> - <td><p id="content_proton_search_protocol_docsum_request_size">content.proton.search_protocol.docsum.request_size</p></td> - <td>byte</td> - <td>Docsum request size (network bytes)</td> - </tr> - <tr> - <td><p id="content_proton_search_protocol_docsum_reply_size">content.proton.search_protocol.docsum.reply_size</p></td> - <td>byte</td> - <td>Docsum reply size (network bytes)</td> - </tr> - <tr> - <td><p id="content_proton_search_protocol_docsum_requested_documents">content.proton.search_protocol.docsum.requested_documents</p></td> - <td>document</td> - <td>Total requested document summaries</td> - </tr> - <tr> - <td><p id="content_proton_executor_proton_queuesize">content.proton.executor.proton.queuesize</p></td> - <td>task</td> - <td>Size of executor proton task queue</td> - </tr> - <tr> - <td><p id="content_proton_executor_proton_accepted">content.proton.executor.proton.accepted</p></td> - <td>task</td> - <td>Number of executor proton accepted tasks</td> - </tr> - <tr> - <td><p id="content_proton_executor_proton_wakeups">content.proton.executor.proton.wakeups</p></td> - <td>wakeup</td> - <td>Number of times an executor proton worker thread has been woken up</td> - </tr> - <tr> - <td><p id="content_proton_executor_proton_utilization">content.proton.executor.proton.utilization</p></td> - <td>fraction</td> - <td>Ratio of time the executor proton worker threads has been active</td> - </tr> - <tr> - <td><p id="content_proton_executor_proton_rejected">content.proton.executor.proton.rejected</p></td> - <td>task</td> - <td>Number of rejected tasks</td> - </tr> - <tr> - <td><p id="content_proton_executor_flush_queuesize">content.proton.executor.flush.queuesize</p></td> - <td>task</td> - <td>Size of executor flush task queue</td> - </tr> - <tr> - <td><p id="content_proton_executor_flush_accepted">content.proton.executor.flush.accepted</p></td> - <td>task</td> - <td>Number of accepted executor flush tasks</td> - </tr> - <tr> - <td><p id="content_proton_executor_flush_wakeups">content.proton.executor.flush.wakeups</p></td> - <td>wakeup</td> - <td>Number of times an executor flush worker thread has been woken up</td> - </tr> - <tr> - <td><p id="content_proton_executor_flush_utilization">content.proton.executor.flush.utilization</p></td> - <td>fraction</td> - <td>Ratio of time the executor flush worker threads has been active</td> - </tr> - <tr> - <td><p id="content_proton_executor_flush_rejected">content.proton.executor.flush.rejected</p></td> - <td>task</td> - <td>Number of rejected tasks</td> - </tr> - <tr> - <td><p id="content_proton_executor_match_queuesize">content.proton.executor.match.queuesize</p></td> - <td>task</td> - <td>Size of executor match task queue</td> - </tr> - <tr> - <td><p id="content_proton_executor_match_accepted">content.proton.executor.match.accepted</p></td> - <td>task</td> - <td>Number of accepted executor match tasks</td> - </tr> - <tr> - <td><p id="content_proton_executor_match_wakeups">content.proton.executor.match.wakeups</p></td> - <td>wakeup</td> - <td>Number of times an executor match worker thread has been woken up</td> - </tr> - <tr> - <td><p id="content_proton_executor_match_utilization">content.proton.executor.match.utilization</p></td> - <td>fraction</td> - <td>Ratio of time the executor match worker threads has been active</td> - </tr> - <tr> - <td><p id="content_proton_executor_match_rejected">content.proton.executor.match.rejected</p></td> - <td>task</td> - <td>Number of rejected tasks</td> - </tr> - <tr> - <td><p id="content_proton_executor_docsum_queuesize">content.proton.executor.docsum.queuesize</p></td> - <td>task</td> - <td>Size of executor docsum task queue</td> - </tr> - <tr> - <td><p id="content_proton_executor_docsum_accepted">content.proton.executor.docsum.accepted</p></td> - <td>task</td> - <td>Number of executor accepted docsum tasks</td> - </tr> - <tr> - <td><p id="content_proton_executor_docsum_wakeups">content.proton.executor.docsum.wakeups</p></td> - <td>wakeup</td> - <td>Number of times an executor docsum worker thread has been woken up</td> - </tr> - <tr> - <td><p id="content_proton_executor_docsum_utilization">content.proton.executor.docsum.utilization</p></td> - <td>fraction</td> - <td>Ratio of time the executor docsum worker threads has been active</td> - </tr> - <tr> - <td><p id="content_proton_executor_docsum_rejected">content.proton.executor.docsum.rejected</p></td> - <td>task</td> - <td>Number of rejected tasks</td> - </tr> - <tr> - <td><p id="content_proton_executor_shared_queuesize">content.proton.executor.shared.queuesize</p></td> - <td>task</td> - <td>Size of executor shared task queue</td> - </tr> - <tr> - <td><p id="content_proton_executor_shared_accepted">content.proton.executor.shared.accepted</p></td> - <td>task</td> - <td>Number of executor shared accepted tasks</td> - </tr> - <tr> - <td><p id="content_proton_executor_shared_wakeups">content.proton.executor.shared.wakeups</p></td> - <td>wakeup</td> - <td>Number of times an executor shared worker thread has been woken up</td> - </tr> - <tr> - <td><p id="content_proton_executor_shared_utilization">content.proton.executor.shared.utilization</p></td> - <td>fraction</td> - <td>Ratio of time the executor shared worker threads has been active</td> - </tr> - <tr> - <td><p id="content_proton_executor_shared_rejected">content.proton.executor.shared.rejected</p></td> - <td>task</td> - <td>Number of rejected tasks</td> - </tr> - <tr> - <td><p id="content_proton_executor_warmup_queuesize">content.proton.executor.warmup.queuesize</p></td> - <td>task</td> - <td>Size of executor warmup task queue</td> - </tr> - <tr> - <td><p id="content_proton_executor_warmup_accepted">content.proton.executor.warmup.accepted</p></td> - <td>task</td> - <td>Number of accepted executor warmup tasks</td> - </tr> - <tr> - <td><p id="content_proton_executor_warmup_wakeups">content.proton.executor.warmup.wakeups</p></td> - <td>wakeup</td> - <td>Number of times a warmup executor worker thread has been woken up</td> - </tr> - <tr> - <td><p id="content_proton_executor_warmup_utilization">content.proton.executor.warmup.utilization</p></td> - <td>fraction</td> - <td>Ratio of time the executor warmup worker threads has been active</td> - </tr> - <tr> - <td><p id="content_proton_executor_warmup_rejected">content.proton.executor.warmup.rejected</p></td> - <td>task</td> - <td>Number of rejected tasks</td> - </tr> - <tr> - <td><p id="content_proton_executor_field_writer_queuesize">content.proton.executor.field_writer.queuesize</p></td> - <td>task</td> - <td>Size of executor field writer task queue</td> - </tr> - <tr> - <td><p id="content_proton_executor_field_writer_accepted">content.proton.executor.field_writer.accepted</p></td> - <td>task</td> - <td>Number of accepted executor field writer tasks</td> - </tr> - <tr> - <td><p id="content_proton_executor_field_writer_wakeups">content.proton.executor.field_writer.wakeups</p></td> - <td>wakeup</td> - <td>Number of times an executor field writer worker thread has been woken up</td> - </tr> - <tr> - <td><p id="content_proton_executor_field_writer_utilization">content.proton.executor.field_writer.utilization</p></td> - <td>fraction</td> - <td>Ratio of time the executor fieldwriter worker threads has been active</td> - </tr> - <tr> - <td><p id="content_proton_executor_field_writer_saturation">content.proton.executor.field_writer.saturation</p></td> - <td>fraction</td> - <td>Ratio indicating the max saturation of underlying worker threads. A higher saturation than utilization indicates a bottleneck in one of the worker threads.</td> - </tr> - <tr> - <td><p id="content_proton_executor_field_writer_rejected">content.proton.executor.field_writer.rejected</p></td> - <td>task</td> - <td>Number of rejected tasks</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_job_total">content.proton.documentdb.job.total</p></td> - <td>fraction</td> - <td>The job load average total of all job metrics</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_job_attribute_flush">content.proton.documentdb.job.attribute_flush</p></td> - <td>fraction</td> - <td>Flushing of attribute vector(s) to disk</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_job_memory_index_flush">content.proton.documentdb.job.memory_index_flush</p></td> - <td>fraction</td> - <td>Flushing of memory index to disk</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_job_disk_index_fusion">content.proton.documentdb.job.disk_index_fusion</p></td> - <td>fraction</td> - <td>Fusion of disk indexes</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_job_document_store_flush">content.proton.documentdb.job.document_store_flush</p></td> - <td>fraction</td> - <td>Flushing of document store to disk</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_job_document_store_compact">content.proton.documentdb.job.document_store_compact</p></td> - <td>fraction</td> - <td>Compaction of document store on disk</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_job_bucket_move">content.proton.documentdb.job.bucket_move</p></td> - <td>fraction</td> - <td>Moving of buckets between 'ready' and 'notready' sub databases</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_job_lid_space_compact">content.proton.documentdb.job.lid_space_compact</p></td> - <td>fraction</td> - <td>Compaction of lid space in document meta store and attribute vectors</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_job_removed_documents_prune">content.proton.documentdb.job.removed_documents_prune</p></td> - <td>fraction</td> - <td>Pruning of removed documents in 'removed' sub database</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_threading_service_master_queuesize">content.proton.documentdb.threading_service.master.queuesize</p></td> - <td>task</td> - <td>Size of threading service master task queue</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_threading_service_master_accepted">content.proton.documentdb.threading_service.master.accepted</p></td> - <td>task</td> - <td>Number of accepted threading service master tasks</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_threading_service_master_wakeups">content.proton.documentdb.threading_service.master.wakeups</p></td> - <td>wakeup</td> - <td>Number of times a threading service master worker thread has been woken up</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_threading_service_master_utilization">content.proton.documentdb.threading_service.master.utilization</p></td> - <td>fraction</td> - <td>Ratio of time the threading service master worker threads has been active</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_threading_service_master_rejected">content.proton.documentdb.threading_service.master.rejected</p></td> - <td>task</td> - <td>Number of rejected tasks</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_threading_service_index_queuesize">content.proton.documentdb.threading_service.index.queuesize</p></td> - <td>task</td> - <td>Size of threading service index task queue</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_threading_service_index_accepted">content.proton.documentdb.threading_service.index.accepted</p></td> - <td>task</td> - <td>Number of accepted threading service index tasks</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_threading_service_index_wakeups">content.proton.documentdb.threading_service.index.wakeups</p></td> - <td>wakeup</td> - <td>Number of times a threading service index worker thread has been woken up</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_threading_service_index_utilization">content.proton.documentdb.threading_service.index.utilization</p></td> - <td>fraction</td> - <td>Ratio of time the threading service index worker threads has been active</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_threading_service_index_rejected">content.proton.documentdb.threading_service.index.rejected</p></td> - <td>task</td> - <td>Number of rejected tasks</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_threading_service_summary_queuesize">content.proton.documentdb.threading_service.summary.queuesize</p></td> - <td>task</td> - <td>Size of threading service summary task queue</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_threading_service_summary_accepted">content.proton.documentdb.threading_service.summary.accepted</p></td> - <td>task</td> - <td>Number of accepted threading service summary tasks</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_threading_service_summary_wakeups">content.proton.documentdb.threading_service.summary.wakeups</p></td> - <td>wakeup</td> - <td>Number of times a threading service summary worker thread has been woken up</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_threading_service_summary_utilization">content.proton.documentdb.threading_service.summary.utilization</p></td> - <td>fraction</td> - <td>Ratio of time the threading service summary worker threads has been active</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_threading_service_summary_rejected">content.proton.documentdb.threading_service.summary.rejected</p></td> - <td>task</td> - <td>Number of rejected tasks</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_threading_service_attribute_field_writer_accepted">content.proton.documentdb.threading_service.attribute_field_writer.accepted</p></td> - <td>task</td> - <td>Number of accepted tasks</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_threading_service_attribute_field_writer_queuesize">content.proton.documentdb.threading_service.attribute_field_writer.queuesize</p></td> - <td>task</td> - <td>Size of task queue</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_threading_service_attribute_field_writer_rejected">content.proton.documentdb.threading_service.attribute_field_writer.rejected</p></td> - <td>task</td> - <td>Number of rejected tasks</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_threading_service_attribute_field_writer_utilization">content.proton.documentdb.threading_service.attribute_field_writer.utilization</p></td> - <td>fraction</td> - <td>Ratio of time the worker threads has been active</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_threading_service_attribute_field_writer_wakeups">content.proton.documentdb.threading_service.attribute_field_writer.wakeups</p></td> - <td>wakeup</td> - <td>Number of times a worker thread has been woken up</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_threading_service_index_field_inverter_accepted">content.proton.documentdb.threading_service.index_field_inverter.accepted</p></td> - <td>task</td> - <td>Number of accepted tasks</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_threading_service_index_field_inverter_queuesize">content.proton.documentdb.threading_service.index_field_inverter.queuesize</p></td> - <td>task</td> - <td>Size of task queue</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_threading_service_index_field_inverter_rejected">content.proton.documentdb.threading_service.index_field_inverter.rejected</p></td> - <td>task</td> - <td>Number of rejected tasks</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_threading_service_index_field_inverter_utilization">content.proton.documentdb.threading_service.index_field_inverter.utilization</p></td> - <td>fraction</td> - <td>Ratio of time the worker threads has been active</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_threading_service_index_field_inverter_wakeups">content.proton.documentdb.threading_service.index_field_inverter.wakeups</p></td> - <td>wakeup</td> - <td>Number of times a worker thread has been woken up</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_threading_service_index_field_writer_accepted">content.proton.documentdb.threading_service.index_field_writer.accepted</p></td> - <td>task</td> - <td>Number of accepted tasks</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_threading_service_index_field_writer_queuesize">content.proton.documentdb.threading_service.index_field_writer.queuesize</p></td> - <td>task</td> - <td>Size of task queue</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_threading_service_index_field_writer_rejected">content.proton.documentdb.threading_service.index_field_writer.rejected</p></td> - <td>task</td> - <td>Number of rejected tasks</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_threading_service_index_field_writer_utilization">content.proton.documentdb.threading_service.index_field_writer.utilization</p></td> - <td>fraction</td> - <td>Ratio of time the worker threads has been active</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_threading_service_index_field_writer_wakeups">content.proton.documentdb.threading_service.index_field_writer.wakeups</p></td> - <td>wakeup</td> - <td>Number of times a worker thread has been woken up</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_ready_lid_space_lid_bloat_factor">content.proton.documentdb.ready.lid_space.lid_bloat_factor</p></td> - <td>fraction</td> - <td>The bloat factor of this lid space, indicating the total amount of holes in the allocated lid space ((lid_limit - used_lids) / lid_limit)</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_ready_lid_space_lid_fragmentation_factor">content.proton.documentdb.ready.lid_space.lid_fragmentation_factor</p></td> - <td>fraction</td> - <td>The fragmentation factor of this lid space, indicating the amount of holes in the currently used part of the lid space ((highest_used_lid - used_lids) / highest_used_lid)</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_ready_lid_space_lid_limit">content.proton.documentdb.ready.lid_space.lid_limit</p></td> - <td>documentid</td> - <td>The size of the allocated lid space</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_ready_lid_space_highest_used_lid">content.proton.documentdb.ready.lid_space.highest_used_lid</p></td> - <td>documentid</td> - <td>The highest used lid</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_ready_lid_space_used_lids">content.proton.documentdb.ready.lid_space.used_lids</p></td> - <td>documentid</td> - <td>The number of lids used</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_ready_lid_space_lowest_free_lid">content.proton.documentdb.ready.lid_space.lowest_free_lid</p></td> - <td>documentid</td> - <td>The lowest free local document id</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_notready_lid_space_lid_bloat_factor">content.proton.documentdb.notready.lid_space.lid_bloat_factor</p></td> - <td>fraction</td> - <td>The bloat factor of this lid space, indicating the total amount of holes in the allocated lid space ((lid_limit - used_lids) / lid_limit)</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_notready_lid_space_lid_fragmentation_factor">content.proton.documentdb.notready.lid_space.lid_fragmentation_factor</p></td> - <td>fraction</td> - <td>The fragmentation factor of this lid space, indicating the amount of holes in the currently used part of the lid space ((highest_used_lid - used_lids) / highest_used_lid)</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_notready_lid_space_lid_limit">content.proton.documentdb.notready.lid_space.lid_limit</p></td> - <td>documentid</td> - <td>The size of the allocated lid space</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_notready_lid_space_highest_used_lid">content.proton.documentdb.notready.lid_space.highest_used_lid</p></td> - <td>documentid</td> - <td>The highest used lid</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_notready_lid_space_used_lids">content.proton.documentdb.notready.lid_space.used_lids</p></td> - <td>documentid</td> - <td>The number of lids used</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_notready_lid_space_lowest_free_lid">content.proton.documentdb.notready.lid_space.lowest_free_lid</p></td> - <td>documentid</td> - <td>The lowest free local document id</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_removed_lid_space_lid_bloat_factor">content.proton.documentdb.removed.lid_space.lid_bloat_factor</p></td> - <td>fraction</td> - <td>The bloat factor of this lid space, indicating the total amount of holes in the allocated lid space ((lid_limit - used_lids) / lid_limit)</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_removed_lid_space_lid_fragmentation_factor">content.proton.documentdb.removed.lid_space.lid_fragmentation_factor</p></td> - <td>fraction</td> - <td>The fragmentation factor of this lid space, indicating the amount of holes in the currently used part of the lid space ((highest_used_lid - used_lids) / highest_used_lid)</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_removed_lid_space_lid_limit">content.proton.documentdb.removed.lid_space.lid_limit</p></td> - <td>documentid</td> - <td>The size of the allocated lid space</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_removed_lid_space_highest_used_lid">content.proton.documentdb.removed.lid_space.highest_used_lid</p></td> - <td>documentid</td> - <td>The highest used lid</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_removed_lid_space_used_lids">content.proton.documentdb.removed.lid_space.used_lids</p></td> - <td>documentid</td> - <td>The number of lids used</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_removed_lid_space_lowest_free_lid">content.proton.documentdb.removed.lid_space.lowest_free_lid</p></td> - <td>documentid</td> - <td>The lowest free local document id</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_bucket_move_buckets_pending">content.proton.documentdb.bucket_move.buckets_pending</p></td> - <td>bucket</td> - <td>The number of buckets left to move</td> - </tr> - <tr> - <td><p id="content_proton_resource_usage_disk">content.proton.resource_usage.disk</p></td> - <td>fraction</td> - <td>The relative amount of disk used by this content node (transient usage not included, value in the range [0, 1]). Same value as reported to the cluster controller</td> - </tr> - <tr> - <td><p id="content_proton_resource_usage_disk_usage_total">content.proton.resource_usage.disk_usage.total</p></td> - <td>fraction</td> - <td>The total relative amount of disk used by this content node (value in the range [0, 1])</td> - </tr> - <tr> - <td><p id="content_proton_resource_usage_disk_usage_total_utilization">content.proton.resource_usage.disk_usage.total_utilization</p></td> - <td>fraction</td> - <td>The relative amount of disk used compared to the content node disk resource limit</td> - </tr> - <tr> - <td><p id="content_proton_resource_usage_disk_usage_transient">content.proton.resource_usage.disk_usage.transient</p></td> - <td>fraction</td> - <td>The relative amount of transient disk used by this content node (value in the range [0, 1])</td> - </tr> - <tr> - <td><p id="content_proton_resource_usage_disk_usage_reserved">content.proton.resource_usage.disk_usage.reserved</p></td> - <td>fraction</td> - <td>The relative amount of reserved disk space for this content node (value in the range [0, 1])</td> - </tr> - <tr> - <td><p id="content_proton_resource_usage_disk_usage_used_and_reserved">content.proton.resource_usage.disk_usage.used_and_reserved</p></td> - <td>fraction</td> - <td>The relative amount of disk used and reserved disk space by this content node (transient usage not included, value in the range [0, 1])</td> - </tr> - <tr> - <td><p id="content_proton_resource_usage_memory">content.proton.resource_usage.memory</p></td> - <td>fraction</td> - <td>The relative amount of memory used by this content node (transient usage not included, value in the range [0, 1]). Same value as reported to the cluster controller</td> - </tr> - <tr> - <td><p id="content_proton_resource_usage_memory_usage_total">content.proton.resource_usage.memory_usage.total</p></td> - <td>fraction</td> - <td>The total relative amount of memory used by this content node (value in the range [0, 1])</td> - </tr> - <tr> - <td><p id="content_proton_resource_usage_memory_usage_total_utilization">content.proton.resource_usage.memory_usage.total_utilization</p></td> - <td>fraction</td> - <td>The relative amount of memory used compared to the content node memory resource limit</td> - </tr> - <tr> - <td><p id="content_proton_resource_usage_memory_usage_transient">content.proton.resource_usage.memory_usage.transient</p></td> - <td>fraction</td> - <td>The relative amount of transient memory used by this content node (value in the range [0, 1])</td> - </tr> - <tr> - <td><p id="content_proton_resource_usage_memory_usage_reserved">content.proton.resource_usage.memory_usage.reserved</p></td> - <td>fraction</td> - <td>The relative amount of reserved memory for this content node (value in the range [0, 1])</td> - </tr> - <tr> - <td><p id="content_proton_resource_usage_memory_usage_used_and_reserved">content.proton.resource_usage.memory_usage.used_and_reserved</p></td> - <td>fraction</td> - <td>The relative amount of used and reserved memory for this content node (transient usage not included, value in the range [0, 1])</td> - </tr> - <tr> - <td><p id="content_proton_resource_usage_memory_mappings">content.proton.resource_usage.memory_mappings</p></td> - <td>file</td> - <td>The number of memory mapped files</td> - </tr> - <tr> - <td><p id="content_proton_resource_usage_open_file_descriptors">content.proton.resource_usage.open_file_descriptors</p></td> - <td>file</td> - <td>The number of open files</td> - </tr> - <tr> - <td><p id="content_proton_resource_usage_feeding_blocked">content.proton.resource_usage.feeding_blocked</p></td> - <td>binary</td> - <td>Whether feeding is blocked due to resource limits being reached (value is either 0 or 1)</td> - </tr> - <tr> - <td><p id="content_proton_resource_usage_malloc_arena">content.proton.resource_usage.malloc_arena</p></td> - <td>byte</td> - <td>Size of malloc arena</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_attribute_resource_usage_address_space">content.proton.documentdb.attribute.resource_usage.address_space</p></td> - <td>fraction</td> - <td>The max relative address space used among components in all attribute vectors in this document db (value in the range [0, 1])</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_attribute_resource_usage_feeding_blocked">content.proton.documentdb.attribute.resource_usage.feeding_blocked</p></td> - <td>binary</td> - <td>Whether feeding is blocked due to attribute resource limits being reached (value is either 0 or 1)</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_attribute_memory_usage_allocated_bytes">content.proton.documentdb.attribute.memory_usage.allocated_bytes</p></td> - <td>byte</td> - <td>The number of allocated bytes</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_attribute_memory_usage_dead_bytes">content.proton.documentdb.attribute.memory_usage.dead_bytes</p></td> - <td>byte</td> - <td>The number of dead bytes (<= used_bytes)</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_attribute_memory_usage_onhold_bytes">content.proton.documentdb.attribute.memory_usage.onhold_bytes</p></td> - <td>byte</td> - <td>The number of bytes on hold</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_attribute_memory_usage_used_bytes">content.proton.documentdb.attribute.memory_usage.used_bytes</p></td> - <td>byte</td> - <td>The number of used bytes (<= allocated_bytes)</td> - </tr> - <tr> - <td><p id="content_proton_resource_usage_cpu_util_setup">content.proton.resource_usage.cpu_util.setup</p></td> - <td>fraction</td> - <td>cpu used by system init and (re-)configuration</td> - </tr> - <tr> - <td><p id="content_proton_resource_usage_cpu_util_read">content.proton.resource_usage.cpu_util.read</p></td> - <td>fraction</td> - <td>cpu used by reading data from the system</td> - </tr> - <tr> - <td><p id="content_proton_resource_usage_cpu_util_write">content.proton.resource_usage.cpu_util.write</p></td> - <td>fraction</td> - <td>cpu used by writing data to the system</td> - </tr> - <tr> - <td><p id="content_proton_resource_usage_cpu_util_compact">content.proton.resource_usage.cpu_util.compact</p></td> - <td>fraction</td> - <td>cpu used by internal data re-structuring</td> - </tr> - <tr> - <td><p id="content_proton_resource_usage_cpu_util_other">content.proton.resource_usage.cpu_util.other</p></td> - <td>fraction</td> - <td>cpu used by work not classified as a specific category</td> - </tr> - <tr> - <td><p id="content_proton_transactionlog_entries">content.proton.transactionlog.entries</p></td> - <td>record</td> - <td>The current number of entries in the transaction log</td> - </tr> - <tr> - <td><p id="content_proton_transactionlog_disk_usage">content.proton.transactionlog.disk_usage</p></td> - <td>byte</td> - <td>The disk usage (in bytes) of the transaction log</td> - </tr> - <tr> - <td><p id="content_proton_transactionlog_replay_time">content.proton.transactionlog.replay_time</p></td> - <td>second</td> - <td>The replay time (in seconds) of the transaction log during start-up</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_ready_document_store_disk_usage">content.proton.documentdb.ready.document_store.disk_usage</p></td> - <td>byte</td> - <td>Disk space usage in bytes</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_ready_document_store_disk_bloat">content.proton.documentdb.ready.document_store.disk_bloat</p></td> - <td>byte</td> - <td>Disk space bloat in bytes</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_ready_document_store_max_bucket_spread">content.proton.documentdb.ready.document_store.max_bucket_spread</p></td> - <td>fraction</td> - <td>Max bucket spread in underlying files (sum(unique buckets in each chunk)/unique buckets in file)</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_ready_document_store_memory_usage_allocated_bytes">content.proton.documentdb.ready.document_store.memory_usage.allocated_bytes</p></td> - <td>byte</td> - <td>The number of allocated bytes</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_ready_document_store_memory_usage_used_bytes">content.proton.documentdb.ready.document_store.memory_usage.used_bytes</p></td> - <td>byte</td> - <td>The number of used bytes (<= allocated_bytes)</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_ready_document_store_memory_usage_dead_bytes">content.proton.documentdb.ready.document_store.memory_usage.dead_bytes</p></td> - <td>byte</td> - <td>The number of dead bytes (<= used_bytes)</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_ready_document_store_memory_usage_onhold_bytes">content.proton.documentdb.ready.document_store.memory_usage.onhold_bytes</p></td> - <td>byte</td> - <td>The number of bytes on hold</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_notready_document_store_disk_usage">content.proton.documentdb.notready.document_store.disk_usage</p></td> - <td>byte</td> - <td>Disk space usage in bytes</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_notready_document_store_disk_bloat">content.proton.documentdb.notready.document_store.disk_bloat</p></td> - <td>byte</td> - <td>Disk space bloat in bytes</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_notready_document_store_max_bucket_spread">content.proton.documentdb.notready.document_store.max_bucket_spread</p></td> - <td>fraction</td> - <td>Max bucket spread in underlying files (sum(unique buckets in each chunk)/unique buckets in file)</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_notready_document_store_memory_usage_allocated_bytes">content.proton.documentdb.notready.document_store.memory_usage.allocated_bytes</p></td> - <td>byte</td> - <td>The number of allocated bytes</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_notready_document_store_memory_usage_used_bytes">content.proton.documentdb.notready.document_store.memory_usage.used_bytes</p></td> - <td>byte</td> - <td>The number of used bytes (<= allocated_bytes)</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_notready_document_store_memory_usage_dead_bytes">content.proton.documentdb.notready.document_store.memory_usage.dead_bytes</p></td> - <td>byte</td> - <td>The number of dead bytes (<= used_bytes)</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_notready_document_store_memory_usage_onhold_bytes">content.proton.documentdb.notready.document_store.memory_usage.onhold_bytes</p></td> - <td>byte</td> - <td>The number of bytes on hold</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_removed_document_store_disk_usage">content.proton.documentdb.removed.document_store.disk_usage</p></td> - <td>byte</td> - <td>Disk space usage in bytes</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_removed_document_store_disk_bloat">content.proton.documentdb.removed.document_store.disk_bloat</p></td> - <td>byte</td> - <td>Disk space bloat in bytes</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_removed_document_store_max_bucket_spread">content.proton.documentdb.removed.document_store.max_bucket_spread</p></td> - <td>fraction</td> - <td>Max bucket spread in underlying files (sum(unique buckets in each chunk)/unique buckets in file)</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_removed_document_store_memory_usage_allocated_bytes">content.proton.documentdb.removed.document_store.memory_usage.allocated_bytes</p></td> - <td>byte</td> - <td>The number of allocated bytes</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_removed_document_store_memory_usage_used_bytes">content.proton.documentdb.removed.document_store.memory_usage.used_bytes</p></td> - <td>byte</td> - <td>The number of used bytes (<= allocated_bytes)</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_removed_document_store_memory_usage_dead_bytes">content.proton.documentdb.removed.document_store.memory_usage.dead_bytes</p></td> - <td>byte</td> - <td>The number of dead bytes (<= used_bytes)</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_removed_document_store_memory_usage_onhold_bytes">content.proton.documentdb.removed.document_store.memory_usage.onhold_bytes</p></td> - <td>byte</td> - <td>The number of bytes on hold</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_ready_document_store_cache_elements">content.proton.documentdb.ready.document_store.cache.elements</p></td> - <td>item</td> - <td>Number of elements in the cache</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_ready_document_store_cache_memory_usage">content.proton.documentdb.ready.document_store.cache.memory_usage</p></td> - <td>byte</td> - <td>Memory usage of the cache (in bytes)</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_ready_document_store_cache_hit_rate">content.proton.documentdb.ready.document_store.cache.hit_rate</p></td> - <td>fraction</td> - <td>Rate of hits in the cache compared to number of lookups</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_ready_document_store_cache_lookups">content.proton.documentdb.ready.document_store.cache.lookups</p></td> - <td>operation</td> - <td>Number of lookups in the cache (hits + misses)</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_ready_document_store_cache_invalidations">content.proton.documentdb.ready.document_store.cache.invalidations</p></td> - <td>operation</td> - <td>Number of invalidations (erased elements) in the cache. </td> - </tr> - <tr> - <td><p id="content_proton_documentdb_notready_document_store_cache_elements">content.proton.documentdb.notready.document_store.cache.elements</p></td> - <td>item</td> - <td>Number of elements in the cache</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_notready_document_store_cache_memory_usage">content.proton.documentdb.notready.document_store.cache.memory_usage</p></td> - <td>byte</td> - <td>Memory usage of the cache (in bytes)</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_notready_document_store_cache_hit_rate">content.proton.documentdb.notready.document_store.cache.hit_rate</p></td> - <td>fraction</td> - <td>Rate of hits in the cache compared to number of lookups</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_notready_document_store_cache_lookups">content.proton.documentdb.notready.document_store.cache.lookups</p></td> - <td>operation</td> - <td>Number of lookups in the cache (hits + misses)</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_notready_document_store_cache_invalidations">content.proton.documentdb.notready.document_store.cache.invalidations</p></td> - <td>operation</td> - <td>Number of invalidations (erased elements) in the cache. </td> - </tr> - <tr> - <td><p id="content_proton_documentdb_removed_document_store_cache_elements">content.proton.documentdb.removed.document_store.cache.elements</p></td> - <td>item</td> - <td>Number of elements in the cache</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_removed_document_store_cache_hit_rate">content.proton.documentdb.removed.document_store.cache.hit_rate</p></td> - <td>fraction</td> - <td>Rate of hits in the cache compared to number of lookups</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_removed_document_store_cache_invalidations">content.proton.documentdb.removed.document_store.cache.invalidations</p></td> - <td>item</td> - <td>Number of invalidations (erased elements) in the cache. </td> - </tr> - <tr> - <td><p id="content_proton_documentdb_removed_document_store_cache_lookups">content.proton.documentdb.removed.document_store.cache.lookups</p></td> - <td>operation</td> - <td>Number of lookups in the cache (hits + misses)</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_removed_document_store_cache_memory_usage">content.proton.documentdb.removed.document_store.cache.memory_usage</p></td> - <td>byte</td> - <td>Memory usage of the cache (in bytes)</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_ready_attribute_memory_usage_allocated_bytes">content.proton.documentdb.ready.attribute.memory_usage.allocated_bytes</p></td> - <td>byte</td> - <td>The number of allocated bytes</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_ready_attribute_memory_usage_used_bytes">content.proton.documentdb.ready.attribute.memory_usage.used_bytes</p></td> - <td>byte</td> - <td>The number of used bytes (<= allocated_bytes)</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_ready_attribute_memory_usage_dead_bytes">content.proton.documentdb.ready.attribute.memory_usage.dead_bytes</p></td> - <td>byte</td> - <td>The number of dead bytes (<= used_bytes)</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_ready_attribute_memory_usage_onhold_bytes">content.proton.documentdb.ready.attribute.memory_usage.onhold_bytes</p></td> - <td>byte</td> - <td>The number of bytes on hold</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_ready_attribute_disk_usage">content.proton.documentdb.ready.attribute.disk_usage</p></td> - <td>byte</td> - <td>Disk space usage (in bytes) of the flushed snapshot of this attribute for this document type</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_notready_attribute_memory_usage_allocated_bytes">content.proton.documentdb.notready.attribute.memory_usage.allocated_bytes</p></td> - <td>byte</td> - <td>The number of allocated bytes</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_notready_attribute_memory_usage_used_bytes">content.proton.documentdb.notready.attribute.memory_usage.used_bytes</p></td> - <td>byte</td> - <td>The number of used bytes (<= allocated_bytes)</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_notready_attribute_memory_usage_dead_bytes">content.proton.documentdb.notready.attribute.memory_usage.dead_bytes</p></td> - <td>byte</td> - <td>The number of dead bytes (<= used_bytes)</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_notready_attribute_memory_usage_onhold_bytes">content.proton.documentdb.notready.attribute.memory_usage.onhold_bytes</p></td> - <td>byte</td> - <td>The number of bytes on hold</td> - </tr> - <tr> - <td><p id="content_proton_index_cache_postinglist_elements">content.proton.index.cache.postinglist.elements</p></td> - <td>item</td> - <td>Number of elements in the cache. Contains disk index posting list files across all document types</td> - </tr> - <tr> - <td><p id="content_proton_index_cache_postinglist_memory_usage">content.proton.index.cache.postinglist.memory_usage</p></td> - <td>byte</td> - <td>Memory usage of the cache (in bytes). Contains disk index posting list files across all document types</td> - </tr> - <tr> - <td><p id="content_proton_index_cache_postinglist_hit_rate">content.proton.index.cache.postinglist.hit_rate</p></td> - <td>fraction</td> - <td>Rate of hits in the cache compared to number of lookups. Contains disk index posting list files across all document types</td> - </tr> - <tr> - <td><p id="content_proton_index_cache_postinglist_lookups">content.proton.index.cache.postinglist.lookups</p></td> - <td>operation</td> - <td>Number of lookups in the cache (hits + misses). Contains disk index posting list files across all document types</td> - </tr> - <tr> - <td><p id="content_proton_index_cache_postinglist_invalidations">content.proton.index.cache.postinglist.invalidations</p></td> - <td>operation</td> - <td>Number of invalidations (erased elements) in the cache. Contains disk index posting list files across all document types</td> - </tr> - <tr> - <td><p id="content_proton_index_cache_bitvector_elements">content.proton.index.cache.bitvector.elements</p></td> - <td>item</td> - <td>Number of elements in the cache. Contains disk index bitvector files across all document types</td> - </tr> - <tr> - <td><p id="content_proton_index_cache_bitvector_memory_usage">content.proton.index.cache.bitvector.memory_usage</p></td> - <td>byte</td> - <td>Memory usage of the cache (in bytes). Contains disk index bitvector files across all document types</td> - </tr> - <tr> - <td><p id="content_proton_index_cache_bitvector_hit_rate">content.proton.index.cache.bitvector.hit_rate</p></td> - <td>fraction</td> - <td>Rate of hits in the cache compared to number of lookups. Contains disk index bitvector files across all document types</td> - </tr> - <tr> - <td><p id="content_proton_index_cache_bitvector_lookups">content.proton.index.cache.bitvector.lookups</p></td> - <td>operation</td> - <td>Number of lookups in the cache (hits + misses). Contains disk index bitvector files across all document types</td> - </tr> - <tr> - <td><p id="content_proton_index_cache_bitvector_invalidations">content.proton.index.cache.bitvector.invalidations</p></td> - <td>operation</td> - <td>Number of invalidations (erased elements) in the cache. Contains disk index bitvector files across all document types</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_index_memory_usage_allocated_bytes">content.proton.documentdb.index.memory_usage.allocated_bytes</p></td> - <td>byte</td> - <td>The number of allocated bytes for the memory index for this document type</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_index_memory_usage_used_bytes">content.proton.documentdb.index.memory_usage.used_bytes</p></td> - <td>byte</td> - <td>The number of used bytes (<= allocated_bytes) for the memory index for this document type</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_index_memory_usage_dead_bytes">content.proton.documentdb.index.memory_usage.dead_bytes</p></td> - <td>byte</td> - <td>The number of dead bytes (<= used_bytes) for the memory index for this document type</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_index_memory_usage_onhold_bytes">content.proton.documentdb.index.memory_usage.onhold_bytes</p></td> - <td>byte</td> - <td>The number of bytes on hold for the memory index for this document type</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_index_disk_usage">content.proton.documentdb.index.disk_usage</p></td> - <td>byte</td> - <td>Disk space usage (in bytes) of all disk indexes for this document type</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_index_indexes">content.proton.documentdb.index.indexes</p></td> - <td>item</td> - <td>Number of disk or memory indexes</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_index_io_search_read_bytes">content.proton.documentdb.index.io.search.read_bytes</p></td> - <td>byte</td> - <td>Bytes read from disk index posting list and bitvector files as part of search for this document type</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_index_io_search_cached_read_bytes">content.proton.documentdb.index.io.search.cached_read_bytes</p></td> - <td>byte</td> - <td>Bytes read from cached disk index posting list and bitvector files as part of search for this document type</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_ready_index_memory_usage_allocated_bytes">content.proton.documentdb.ready.index.memory_usage.allocated_bytes</p></td> - <td>byte</td> - <td>The number of allocated bytes for this index field in the memory index for this document type</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_ready_index_disk_usage">content.proton.documentdb.ready.index.disk_usage</p></td> - <td>byte</td> - <td>Disk space usage (in bytes) of this index field in all disk indexes for this document type</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_matching_queries">content.proton.documentdb.matching.queries</p></td> - <td>query</td> - <td>Number of queries executed</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_matching_approximate_nns_timed_out_queries">content.proton.documentdb.matching.approximate_nns_timed_out_queries</p></td> - <td>query</td> - <td>Number of queries hitting the approximate nearest-neighbor search timeout</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_matching_soft_doomed_queries">content.proton.documentdb.matching.soft_doomed_queries</p></td> - <td>query</td> - <td>Number of queries hitting the soft timeout</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_matching_query_latency">content.proton.documentdb.matching.query_latency</p></td> - <td>second</td> - <td>Total average latency (sec) when matching and ranking a query</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_matching_query_setup_time">content.proton.documentdb.matching.query_setup_time</p></td> - <td>second</td> - <td>Average time (sec) spent setting up and tearing down queries</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_matching_query_approximate_nns_time">content.proton.documentdb.matching.query_approximate_nns_time</p></td> - <td>second</td> - <td>Average time (sec) spent on approximate nearest-neighbor search in queries that perform such searches</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_matching_docs_matched">content.proton.documentdb.matching.docs_matched</p></td> - <td>document</td> - <td>Number of documents matched</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_matching_docs_ranked">content.proton.documentdb.matching.docs_ranked</p></td> - <td>document</td> - <td>Number of documents ranked (first phase)</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_matching_docs_reranked">content.proton.documentdb.matching.docs_reranked</p></td> - <td>document</td> - <td>Number of documents re-ranked (second phase)</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_matching_exact_nns_distances_computed">content.proton.documentdb.matching.exact_nns_distances_computed</p></td> - <td>distance</td> - <td>Number of distances computed in exact nearest-neighbor search</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_matching_approximate_nns_distances_computed">content.proton.documentdb.matching.approximate_nns_distances_computed</p></td> - <td>distance</td> - <td>Number of distances computed in approximate nearest-neighbor search</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_matching_approximate_nns_nodes_visited">content.proton.documentdb.matching.approximate_nns_nodes_visited</p></td> - <td>graph_node</td> - <td>Number of nodes visited in approximate nearest-neighbor search</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_matching_rank_profile_queries">content.proton.documentdb.matching.rank_profile.queries</p></td> - <td>query</td> - <td>Number of queries executed</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_matching_rank_profile_approximate_nns_timed_out_queries">content.proton.documentdb.matching.rank_profile.approximate_nns_timed_out_queries</p></td> - <td>query</td> - <td>Number of queries hitting the approximate nearest-neighbor search timeout</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_matching_rank_profile_soft_doomed_queries">content.proton.documentdb.matching.rank_profile.soft_doomed_queries</p></td> - <td>query</td> - <td>Number of queries hitting the soft timeout</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_matching_rank_profile_soft_doom_factor">content.proton.documentdb.matching.rank_profile.soft_doom_factor</p></td> - <td>fraction</td> - <td>Factor used to compute soft-timeout</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_matching_rank_profile_query_latency">content.proton.documentdb.matching.rank_profile.query_latency</p></td> - <td>second</td> - <td>Total average latency (sec) when matching and ranking a query</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_matching_rank_profile_query_setup_time">content.proton.documentdb.matching.rank_profile.query_setup_time</p></td> - <td>second</td> - <td>Average time (sec) spent setting up and tearing down queries</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_matching_rank_profile_query_approximate_nns_time">content.proton.documentdb.matching.rank_profile.query_approximate_nns_time</p></td> - <td>second</td> - <td>Average time (sec) spent on approximate nearest-neighbor search in queries that perform such searches</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_matching_rank_profile_grouping_time">content.proton.documentdb.matching.rank_profile.grouping_time</p></td> - <td>second</td> - <td>Average time (sec) spent on grouping</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_matching_rank_profile_rerank_time">content.proton.documentdb.matching.rank_profile.rerank_time</p></td> - <td>second</td> - <td>Average time (sec) spent on 2nd phase ranking</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_matching_rank_profile_docs_matched">content.proton.documentdb.matching.rank_profile.docs_matched</p></td> - <td>document</td> - <td>Number of documents matched</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_matching_rank_profile_docs_ranked">content.proton.documentdb.matching.rank_profile.docs_ranked</p></td> - <td>document</td> - <td>Number of documents ranked (first phase)</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_matching_rank_profile_docs_reranked">content.proton.documentdb.matching.rank_profile.docs_reranked</p></td> - <td>document</td> - <td>Number of documents re-ranked (second phase)</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_matching_rank_profile_exact_nns_distances_computed">content.proton.documentdb.matching.rank_profile.exact_nns_distances_computed</p></td> - <td>distance</td> - <td>Number of distances computed in exact nearest-neighbor search</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_matching_rank_profile_approximate_nns_distances_computed">content.proton.documentdb.matching.rank_profile.approximate_nns_distances_computed</p></td> - <td>distance</td> - <td>Number of distances computed in approximate nearest-neighbor search</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_matching_rank_profile_approximate_nns_nodes_visited">content.proton.documentdb.matching.rank_profile.approximate_nns_nodes_visited</p></td> - <td>graph_node</td> - <td>Number of nodes visited in approximate nearest-neighbor search</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_matching_rank_profile_limited_queries">content.proton.documentdb.matching.rank_profile.limited_queries</p></td> - <td>query</td> - <td>Number of queries limited in match phase</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_matching_rank_profile_docid_partition_active_time">content.proton.documentdb.matching.rank_profile.docid_partition.active_time</p></td> - <td>second</td> - <td>Time (sec) spent doing actual work</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_matching_rank_profile_docid_partition_docs_matched">content.proton.documentdb.matching.rank_profile.docid_partition.docs_matched</p></td> - <td>document</td> - <td>Number of documents matched</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_matching_rank_profile_docid_partition_docs_ranked">content.proton.documentdb.matching.rank_profile.docid_partition.docs_ranked</p></td> - <td>document</td> - <td>Number of documents ranked (first phase)</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_matching_rank_profile_docid_partition_docs_reranked">content.proton.documentdb.matching.rank_profile.docid_partition.docs_reranked</p></td> - <td>document</td> - <td>Number of documents re-ranked (second phase)</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_matching_rank_profile_docid_partition_wait_time">content.proton.documentdb.matching.rank_profile.docid_partition.wait_time</p></td> - <td>second</td> - <td>Time (sec) spent waiting for other external threads and resources</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_matching_rank_profile_match_time">content.proton.documentdb.matching.rank_profile.match_time</p></td> - <td>second</td> - <td>Average time (sec) for matching a query (1st phase)</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_feeding_commit_operations">content.proton.documentdb.feeding.commit.operations</p></td> - <td>operation</td> - <td>Number of operations included in a commit</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_feeding_commit_latency">content.proton.documentdb.feeding.commit.latency</p></td> - <td>second</td> - <td>Latency for commit in seconds</td> - </tr> - <tr> - <td><p id="content_proton_session_cache_grouping_num_cached">content.proton.session_cache.grouping.num_cached</p></td> - <td>session</td> - <td>Number of currently cached sessions</td> - </tr> - <tr> - <td><p id="content_proton_session_cache_grouping_num_dropped">content.proton.session_cache.grouping.num_dropped</p></td> - <td>session</td> - <td>Number of dropped cached sessions</td> - </tr> - <tr> - <td><p id="content_proton_session_cache_grouping_num_insert">content.proton.session_cache.grouping.num_insert</p></td> - <td>session</td> - <td>Number of inserted sessions</td> - </tr> - <tr> - <td><p id="content_proton_session_cache_grouping_num_pick">content.proton.session_cache.grouping.num_pick</p></td> - <td>session</td> - <td>Number if picked sessions</td> - </tr> - <tr> - <td><p id="content_proton_session_cache_grouping_num_timedout">content.proton.session_cache.grouping.num_timedout</p></td> - <td>session</td> - <td>Number of timed out sessions</td> - </tr> - <tr> - <td><p id="content_proton_session_cache_search_num_cached">content.proton.session_cache.search.num_cached</p></td> - <td>session</td> - <td>Number of currently cached sessions</td> - </tr> - <tr> - <td><p id="content_proton_session_cache_search_num_dropped">content.proton.session_cache.search.num_dropped</p></td> - <td>session</td> - <td>Number of dropped cached sessions</td> - </tr> - <tr> - <td><p id="content_proton_session_cache_search_num_insert">content.proton.session_cache.search.num_insert</p></td> - <td>session</td> - <td>Number of inserted sessions</td> - </tr> - <tr> - <td><p id="content_proton_session_cache_search_num_pick">content.proton.session_cache.search.num_pick</p></td> - <td>session</td> - <td>Number if picked sessions</td> - </tr> - <tr> - <td><p id="content_proton_session_cache_search_num_timedout">content.proton.session_cache.search.num_timedout</p></td> - <td>session</td> - <td>Number of timed out sessions</td> - </tr> - <tr> - <td><p id="metricmanager_periodichooklatency">metricmanager.periodichooklatency</p></td> - <td>millisecond</td> - <td>Time in ms used to update a single periodic hook</td> - </tr> - <tr> - <td><p id="metricmanager_resetlatency">metricmanager.resetlatency</p></td> - <td>millisecond</td> - <td>Time in ms used to reset all metrics.</td> - </tr> - <tr> - <td><p id="metricmanager_sleeptime">metricmanager.sleeptime</p></td> - <td>millisecond</td> - <td>Time in ms worker thread is sleeping</td> - </tr> - <tr> - <td><p id="metricmanager_snapshothooklatency">metricmanager.snapshothooklatency</p></td> - <td>millisecond</td> - <td>Time in ms used to update a single snapshot hook</td> - </tr> - <tr> - <td><p id="metricmanager_snapshotlatency">metricmanager.snapshotlatency</p></td> - <td>millisecond</td> - <td>Time in ms used to take a snapshot</td> - </tr> - </tbody> -</table> diff --git a/en/reference/operations/metrics/searchnode.mdx b/en/reference/operations/metrics/searchnode.mdx new file mode 100644 index 0000000000..7eaab79a59 --- /dev/null +++ b/en/reference/operations/metrics/searchnode.mdx @@ -0,0 +1,1296 @@ +--- +title: "SearchNode Metrics" +sidebarTitle: "Search node metrics" +--- + +<table className="vespa-table"> + <thead> + <tr> + <th>Name</th> + <th>Unit</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>content.proton.config.generation</td> + <td>version</td> + <td>The oldest config generation used by this search node</td> + </tr> + <tr> + <td>content.proton.documentdb.documents.total</td> + <td>document</td> + <td>The total number of documents in this documents db (ready + not-ready)</td> + </tr> + <tr> + <td>content.proton.documentdb.documents.ready</td> + <td>document</td> + <td>The number of ready documents in this document db</td> + </tr> + <tr> + <td>content.proton.documentdb.documents.active</td> + <td>document</td> + <td>The number of active / searchable documents in this document db</td> + </tr> + <tr> + <td>content.proton.documentdb.documents.removed</td> + <td>document</td> + <td>The number of removed documents in this document db</td> + </tr> + <tr> + <td>content.proton.documentdb.index.docs_in_memory</td> + <td>document</td> + <td>Number of documents in memory index</td> + </tr> + <tr> + <td>content.proton.documentdb.disk_usage</td> + <td>byte</td> + <td>The total disk usage (in bytes) for this document db</td> + </tr> + <tr> + <td>content.proton.documentdb.memory_usage.allocated_bytes</td> + <td>byte</td> + <td>The number of allocated bytes</td> + </tr> + <tr> + <td>content.proton.documentdb.memory_usage.dead_bytes</td> + <td>byte</td> + <td>The number of dead bytes (<code>{`<=`}</code> used_bytes)</td> + </tr> + <tr> + <td>content.proton.documentdb.memory_usage.onhold_bytes</td> + <td>byte</td> + <td>The number of bytes on hold</td> + </tr> + <tr> + <td>content.proton.documentdb.memory_usage.used_bytes</td> + <td>byte</td> + <td>The number of used bytes (<code>{`<=`}</code> allocated_bytes)</td> + </tr> + <tr> + <td>content.proton.documentdb.heart_beat_age</td> + <td>second</td> + <td>How long ago (in seconds) heart beat maintenance job was run</td> + </tr> + <tr> + <td>content.proton.docsum.count</td> + <td>request</td> + <td>Docsum requests handled</td> + </tr> + <tr> + <td>content.proton.docsum.docs</td> + <td>document</td> + <td>Total docsums returned</td> + </tr> + <tr> + <td>content.proton.docsum.latency</td> + <td>millisecond</td> + <td>Docsum request latency</td> + </tr> + <tr> + <td>content.proton.search_protocol.query.latency</td> + <td>second</td> + <td>Query request latency (seconds)</td> + </tr> + <tr> + <td>content.proton.search_protocol.query.request_size</td> + <td>byte</td> + <td>Query request size (network bytes)</td> + </tr> + <tr> + <td>content.proton.search_protocol.query.reply_size</td> + <td>byte</td> + <td>Query reply size (network bytes)</td> + </tr> + <tr> + <td>content.proton.search_protocol.docsum.latency</td> + <td>second</td> + <td>Docsum request latency (seconds)</td> + </tr> + <tr> + <td>content.proton.search_protocol.docsum.request_size</td> + <td>byte</td> + <td>Docsum request size (network bytes)</td> + </tr> + <tr> + <td>content.proton.search_protocol.docsum.reply_size</td> + <td>byte</td> + <td>Docsum reply size (network bytes)</td> + </tr> + <tr> + <td>content.proton.search_protocol.docsum.requested_documents</td> + <td>document</td> + <td>Total requested document summaries</td> + </tr> + <tr> + <td>content.proton.executor.proton.queuesize</td> + <td>task</td> + <td>Size of executor proton task queue</td> + </tr> + <tr> + <td>content.proton.executor.proton.accepted</td> + <td>task</td> + <td>Number of executor proton accepted tasks</td> + </tr> + <tr> + <td>content.proton.executor.proton.wakeups</td> + <td>wakeup</td> + <td>Number of times an executor proton worker thread has been woken up</td> + </tr> + <tr> + <td>content.proton.executor.proton.utilization</td> + <td>fraction</td> + <td>Ratio of time the executor proton worker threads has been active</td> + </tr> + <tr> + <td>content.proton.executor.proton.rejected</td> + <td>task</td> + <td>Number of rejected tasks</td> + </tr> + <tr> + <td>content.proton.executor.flush.queuesize</td> + <td>task</td> + <td>Size of executor flush task queue</td> + </tr> + <tr> + <td>content.proton.executor.flush.accepted</td> + <td>task</td> + <td>Number of accepted executor flush tasks</td> + </tr> + <tr> + <td>content.proton.executor.flush.wakeups</td> + <td>wakeup</td> + <td>Number of times an executor flush worker thread has been woken up</td> + </tr> + <tr> + <td>content.proton.executor.flush.utilization</td> + <td>fraction</td> + <td>Ratio of time the executor flush worker threads has been active</td> + </tr> + <tr> + <td>content.proton.executor.flush.rejected</td> + <td>task</td> + <td>Number of rejected tasks</td> + </tr> + <tr> + <td>content.proton.executor.match.queuesize</td> + <td>task</td> + <td>Size of executor match task queue</td> + </tr> + <tr> + <td>content.proton.executor.match.accepted</td> + <td>task</td> + <td>Number of accepted executor match tasks</td> + </tr> + <tr> + <td>content.proton.executor.match.wakeups</td> + <td>wakeup</td> + <td>Number of times an executor match worker thread has been woken up</td> + </tr> + <tr> + <td>content.proton.executor.match.utilization</td> + <td>fraction</td> + <td>Ratio of time the executor match worker threads has been active</td> + </tr> + <tr> + <td>content.proton.executor.match.rejected</td> + <td>task</td> + <td>Number of rejected tasks</td> + </tr> + <tr> + <td>content.proton.executor.docsum.queuesize</td> + <td>task</td> + <td>Size of executor docsum task queue</td> + </tr> + <tr> + <td>content.proton.executor.docsum.accepted</td> + <td>task</td> + <td>Number of executor accepted docsum tasks</td> + </tr> + <tr> + <td>content.proton.executor.docsum.wakeups</td> + <td>wakeup</td> + <td>Number of times an executor docsum worker thread has been woken up</td> + </tr> + <tr> + <td>content.proton.executor.docsum.utilization</td> + <td>fraction</td> + <td>Ratio of time the executor docsum worker threads has been active</td> + </tr> + <tr> + <td>content.proton.executor.docsum.rejected</td> + <td>task</td> + <td>Number of rejected tasks</td> + </tr> + <tr> + <td>content.proton.executor.shared.queuesize</td> + <td>task</td> + <td>Size of executor shared task queue</td> + </tr> + <tr> + <td>content.proton.executor.shared.accepted</td> + <td>task</td> + <td>Number of executor shared accepted tasks</td> + </tr> + <tr> + <td>content.proton.executor.shared.wakeups</td> + <td>wakeup</td> + <td>Number of times an executor shared worker thread has been woken up</td> + </tr> + <tr> + <td>content.proton.executor.shared.utilization</td> + <td>fraction</td> + <td>Ratio of time the executor shared worker threads has been active</td> + </tr> + <tr> + <td>content.proton.executor.shared.rejected</td> + <td>task</td> + <td>Number of rejected tasks</td> + </tr> + <tr> + <td>content.proton.executor.warmup.queuesize</td> + <td>task</td> + <td>Size of executor warmup task queue</td> + </tr> + <tr> + <td>content.proton.executor.warmup.accepted</td> + <td>task</td> + <td>Number of accepted executor warmup tasks</td> + </tr> + <tr> + <td>content.proton.executor.warmup.wakeups</td> + <td>wakeup</td> + <td>Number of times a warmup executor worker thread has been woken up</td> + </tr> + <tr> + <td>content.proton.executor.warmup.utilization</td> + <td>fraction</td> + <td>Ratio of time the executor warmup worker threads has been active</td> + </tr> + <tr> + <td>content.proton.executor.warmup.rejected</td> + <td>task</td> + <td>Number of rejected tasks</td> + </tr> + <tr> + <td>content.proton.executor.field_writer.queuesize</td> + <td>task</td> + <td>Size of executor field writer task queue</td> + </tr> + <tr> + <td>content.proton.executor.field_writer.accepted</td> + <td>task</td> + <td>Number of accepted executor field writer tasks</td> + </tr> + <tr> + <td>content.proton.executor.field_writer.wakeups</td> + <td>wakeup</td> + <td>Number of times an executor field writer worker thread has been woken up</td> + </tr> + <tr> + <td>content.proton.executor.field_writer.utilization</td> + <td>fraction</td> + <td>Ratio of time the executor fieldwriter worker threads has been active</td> + </tr> + <tr> + <td>content.proton.executor.field_writer.saturation</td> + <td>fraction</td> + <td>Ratio indicating the max saturation of underlying worker threads. A higher saturation than utilization indicates a bottleneck in one of the worker threads.</td> + </tr> + <tr> + <td>content.proton.executor.field_writer.rejected</td> + <td>task</td> + <td>Number of rejected tasks</td> + </tr> + <tr> + <td>content.proton.documentdb.job.total</td> + <td>fraction</td> + <td>The job load average total of all job metrics</td> + </tr> + <tr> + <td>content.proton.documentdb.job.attribute_flush</td> + <td>fraction</td> + <td>Flushing of attribute vector(s) to disk</td> + </tr> + <tr> + <td>content.proton.documentdb.job.memory_index_flush</td> + <td>fraction</td> + <td>Flushing of memory index to disk</td> + </tr> + <tr> + <td>content.proton.documentdb.job.disk_index_fusion</td> + <td>fraction</td> + <td>Fusion of disk indexes</td> + </tr> + <tr> + <td>content.proton.documentdb.job.document_store_flush</td> + <td>fraction</td> + <td>Flushing of document store to disk</td> + </tr> + <tr> + <td>content.proton.documentdb.job.document_store_compact</td> + <td>fraction</td> + <td>Compaction of document store on disk</td> + </tr> + <tr> + <td>content.proton.documentdb.job.bucket_move</td> + <td>fraction</td> + <td>Moving of buckets between 'ready' and 'notready' sub databases</td> + </tr> + <tr> + <td>content.proton.documentdb.job.lid_space_compact</td> + <td>fraction</td> + <td>Compaction of lid space in document meta store and attribute vectors</td> + </tr> + <tr> + <td>content.proton.documentdb.job.removed_documents_prune</td> + <td>fraction</td> + <td>Pruning of removed documents in 'removed' sub database</td> + </tr> + <tr> + <td>content.proton.documentdb.threading_service.master.queuesize</td> + <td>task</td> + <td>Size of threading service master task queue</td> + </tr> + <tr> + <td>content.proton.documentdb.threading_service.master.accepted</td> + <td>task</td> + <td>Number of accepted threading service master tasks</td> + </tr> + <tr> + <td>content.proton.documentdb.threading_service.master.wakeups</td> + <td>wakeup</td> + <td>Number of times a threading service master worker thread has been woken up</td> + </tr> + <tr> + <td>content.proton.documentdb.threading_service.master.utilization</td> + <td>fraction</td> + <td>Ratio of time the threading service master worker threads has been active</td> + </tr> + <tr> + <td>content.proton.documentdb.threading_service.master.rejected</td> + <td>task</td> + <td>Number of rejected tasks</td> + </tr> + <tr> + <td>content.proton.documentdb.threading_service.index.queuesize</td> + <td>task</td> + <td>Size of threading service index task queue</td> + </tr> + <tr> + <td>content.proton.documentdb.threading_service.index.accepted</td> + <td>task</td> + <td>Number of accepted threading service index tasks</td> + </tr> + <tr> + <td>content.proton.documentdb.threading_service.index.wakeups</td> + <td>wakeup</td> + <td>Number of times a threading service index worker thread has been woken up</td> + </tr> + <tr> + <td>content.proton.documentdb.threading_service.index.utilization</td> + <td>fraction</td> + <td>Ratio of time the threading service index worker threads has been active</td> + </tr> + <tr> + <td>content.proton.documentdb.threading_service.index.rejected</td> + <td>task</td> + <td>Number of rejected tasks</td> + </tr> + <tr> + <td>content.proton.documentdb.threading_service.summary.queuesize</td> + <td>task</td> + <td>Size of threading service summary task queue</td> + </tr> + <tr> + <td>content.proton.documentdb.threading_service.summary.accepted</td> + <td>task</td> + <td>Number of accepted threading service summary tasks</td> + </tr> + <tr> + <td>content.proton.documentdb.threading_service.summary.wakeups</td> + <td>wakeup</td> + <td>Number of times a threading service summary worker thread has been woken up</td> + </tr> + <tr> + <td>content.proton.documentdb.threading_service.summary.utilization</td> + <td>fraction</td> + <td>Ratio of time the threading service summary worker threads has been active</td> + </tr> + <tr> + <td>content.proton.documentdb.threading_service.summary.rejected</td> + <td>task</td> + <td>Number of rejected tasks</td> + </tr> + <tr> + <td>content.proton.documentdb.threading_service.attribute_field_writer.accepted</td> + <td>task</td> + <td>Number of accepted tasks</td> + </tr> + <tr> + <td>content.proton.documentdb.threading_service.attribute_field_writer.queuesize</td> + <td>task</td> + <td>Size of task queue</td> + </tr> + <tr> + <td>content.proton.documentdb.threading_service.attribute_field_writer.rejected</td> + <td>task</td> + <td>Number of rejected tasks</td> + </tr> + <tr> + <td>content.proton.documentdb.threading_service.attribute_field_writer.utilization</td> + <td>fraction</td> + <td>Ratio of time the worker threads has been active</td> + </tr> + <tr> + <td>content.proton.documentdb.threading_service.attribute_field_writer.wakeups</td> + <td>wakeup</td> + <td>Number of times a worker thread has been woken up</td> + </tr> + <tr> + <td>content.proton.documentdb.threading_service.index_field_inverter.accepted</td> + <td>task</td> + <td>Number of accepted tasks</td> + </tr> + <tr> + <td>content.proton.documentdb.threading_service.index_field_inverter.queuesize</td> + <td>task</td> + <td>Size of task queue</td> + </tr> + <tr> + <td>content.proton.documentdb.threading_service.index_field_inverter.rejected</td> + <td>task</td> + <td>Number of rejected tasks</td> + </tr> + <tr> + <td>content.proton.documentdb.threading_service.index_field_inverter.utilization</td> + <td>fraction</td> + <td>Ratio of time the worker threads has been active</td> + </tr> + <tr> + <td>content.proton.documentdb.threading_service.index_field_inverter.wakeups</td> + <td>wakeup</td> + <td>Number of times a worker thread has been woken up</td> + </tr> + <tr> + <td>content.proton.documentdb.threading_service.index_field_writer.accepted</td> + <td>task</td> + <td>Number of accepted tasks</td> + </tr> + <tr> + <td>content.proton.documentdb.threading_service.index_field_writer.queuesize</td> + <td>task</td> + <td>Size of task queue</td> + </tr> + <tr> + <td>content.proton.documentdb.threading_service.index_field_writer.rejected</td> + <td>task</td> + <td>Number of rejected tasks</td> + </tr> + <tr> + <td>content.proton.documentdb.threading_service.index_field_writer.utilization</td> + <td>fraction</td> + <td>Ratio of time the worker threads has been active</td> + </tr> + <tr> + <td>content.proton.documentdb.threading_service.index_field_writer.wakeups</td> + <td>wakeup</td> + <td>Number of times a worker thread has been woken up</td> + </tr> + <tr> + <td>content.proton.documentdb.ready.lid_space.lid_bloat_factor</td> + <td>fraction</td> + <td>The bloat factor of this lid space, indicating the total amount of holes in the allocated lid space ((lid_limit - used_lids) / lid_limit)</td> + </tr> + <tr> + <td>content.proton.documentdb.ready.lid_space.lid_fragmentation_factor</td> + <td>fraction</td> + <td>The fragmentation factor of this lid space, indicating the amount of holes in the currently used part of the lid space ((highest_used_lid - used_lids) / highest_used_lid)</td> + </tr> + <tr> + <td>content.proton.documentdb.ready.lid_space.lid_limit</td> + <td>documentid</td> + <td>The size of the allocated lid space</td> + </tr> + <tr> + <td>content.proton.documentdb.ready.lid_space.highest_used_lid</td> + <td>documentid</td> + <td>The highest used lid</td> + </tr> + <tr> + <td>content.proton.documentdb.ready.lid_space.used_lids</td> + <td>documentid</td> + <td>The number of lids used</td> + </tr> + <tr> + <td>content.proton.documentdb.ready.lid_space.lowest_free_lid</td> + <td>documentid</td> + <td>The lowest free local document id</td> + </tr> + <tr> + <td>content.proton.documentdb.notready.lid_space.lid_bloat_factor</td> + <td>fraction</td> + <td>The bloat factor of this lid space, indicating the total amount of holes in the allocated lid space ((lid_limit - used_lids) / lid_limit)</td> + </tr> + <tr> + <td>content.proton.documentdb.notready.lid_space.lid_fragmentation_factor</td> + <td>fraction</td> + <td>The fragmentation factor of this lid space, indicating the amount of holes in the currently used part of the lid space ((highest_used_lid - used_lids) / highest_used_lid)</td> + </tr> + <tr> + <td>content.proton.documentdb.notready.lid_space.lid_limit</td> + <td>documentid</td> + <td>The size of the allocated lid space</td> + </tr> + <tr> + <td>content.proton.documentdb.notready.lid_space.highest_used_lid</td> + <td>documentid</td> + <td>The highest used lid</td> + </tr> + <tr> + <td>content.proton.documentdb.notready.lid_space.used_lids</td> + <td>documentid</td> + <td>The number of lids used</td> + </tr> + <tr> + <td>content.proton.documentdb.notready.lid_space.lowest_free_lid</td> + <td>documentid</td> + <td>The lowest free local document id</td> + </tr> + <tr> + <td>content.proton.documentdb.removed.lid_space.lid_bloat_factor</td> + <td>fraction</td> + <td>The bloat factor of this lid space, indicating the total amount of holes in the allocated lid space ((lid_limit - used_lids) / lid_limit)</td> + </tr> + <tr> + <td>content.proton.documentdb.removed.lid_space.lid_fragmentation_factor</td> + <td>fraction</td> + <td>The fragmentation factor of this lid space, indicating the amount of holes in the currently used part of the lid space ((highest_used_lid - used_lids) / highest_used_lid)</td> + </tr> + <tr> + <td>content.proton.documentdb.removed.lid_space.lid_limit</td> + <td>documentid</td> + <td>The size of the allocated lid space</td> + </tr> + <tr> + <td>content.proton.documentdb.removed.lid_space.highest_used_lid</td> + <td>documentid</td> + <td>The highest used lid</td> + </tr> + <tr> + <td>content.proton.documentdb.removed.lid_space.used_lids</td> + <td>documentid</td> + <td>The number of lids used</td> + </tr> + <tr> + <td>content.proton.documentdb.removed.lid_space.lowest_free_lid</td> + <td>documentid</td> + <td>The lowest free local document id</td> + </tr> + <tr> + <td>content.proton.documentdb.bucket_move.buckets_pending</td> + <td>bucket</td> + <td>The number of buckets left to move</td> + </tr> + <tr> + <td>content.proton.resource_usage.disk</td> + <td>fraction</td> + <td>The relative amount of disk used by this content node (transient usage not included, value in the range [0, 1]). Same value as reported to the cluster controller</td> + </tr> + <tr> + <td>content.proton.resource_usage.disk_usage.total</td> + <td>fraction</td> + <td>The total relative amount of disk used by this content node (value in the range [0, 1])</td> + </tr> + <tr> + <td>content.proton.resource_usage.disk_usage.total_utilization</td> + <td>fraction</td> + <td>The relative amount of disk used compared to the content node disk resource limit</td> + </tr> + <tr> + <td>content.proton.resource_usage.disk_usage.transient</td> + <td>fraction</td> + <td>The relative amount of transient disk used by this content node (value in the range [0, 1])</td> + </tr> + <tr> + <td>content.proton.resource_usage.disk_usage.reserved</td> + <td>fraction</td> + <td>The relative amount of reserved disk space for this content node (value in the range [0, 1])</td> + </tr> + <tr> + <td>content.proton.resource_usage.disk_usage.used_and_reserved</td> + <td>fraction</td> + <td>The relative amount of disk used and reserved disk space by this content node (transient usage not included, value in the range [0, 1])</td> + </tr> + <tr> + <td>content.proton.resource_usage.memory</td> + <td>fraction</td> + <td>The relative amount of memory used by this content node (transient usage not included, value in the range [0, 1]). Same value as reported to the cluster controller</td> + </tr> + <tr> + <td>content.proton.resource_usage.memory_usage.total</td> + <td>fraction</td> + <td>The total relative amount of memory used by this content node (value in the range [0, 1])</td> + </tr> + <tr> + <td>content.proton.resource_usage.memory_usage.total_utilization</td> + <td>fraction</td> + <td>The relative amount of memory used compared to the content node memory resource limit</td> + </tr> + <tr> + <td>content.proton.resource_usage.memory_usage.transient</td> + <td>fraction</td> + <td>The relative amount of transient memory used by this content node (value in the range [0, 1])</td> + </tr> + <tr> + <td>content.proton.resource_usage.memory_mappings</td> + <td>file</td> + <td>The number of memory mapped files</td> + </tr> + <tr> + <td>content.proton.resource_usage.open_file_descriptors</td> + <td>file</td> + <td>The number of open files</td> + </tr> + <tr> + <td>content.proton.resource_usage.feeding_blocked</td> + <td>binary</td> + <td>Whether feeding is blocked due to resource limits being reached (value is either 0 or 1)</td> + </tr> + <tr> + <td>content.proton.resource_usage.malloc_arena</td> + <td>byte</td> + <td>Size of malloc arena</td> + </tr> + <tr> + <td>content.proton.documentdb.attribute.resource_usage.address_space</td> + <td>fraction</td> + <td>The max relative address space used among components in all attribute vectors in this document db (value in the range [0, 1])</td> + </tr> + <tr> + <td>content.proton.documentdb.attribute.resource_usage.feeding_blocked</td> + <td>binary</td> + <td>Whether feeding is blocked due to attribute resource limits being reached (value is either 0 or 1)</td> + </tr> + <tr> + <td>content.proton.documentdb.attribute.memory_usage.allocated_bytes</td> + <td>byte</td> + <td>The number of allocated bytes</td> + </tr> + <tr> + <td>content.proton.documentdb.attribute.memory_usage.dead_bytes</td> + <td>byte</td> + <td>The number of dead bytes (<code>{`<=`}</code> used_bytes)</td> + </tr> + <tr> + <td>content.proton.documentdb.attribute.memory_usage.onhold_bytes</td> + <td>byte</td> + <td>The number of bytes on hold</td> + </tr> + <tr> + <td>content.proton.documentdb.attribute.memory_usage.used_bytes</td> + <td>byte</td> + <td>The number of used bytes (<code>{`<=`}</code> allocated_bytes)</td> + </tr> + <tr> + <td>content.proton.resource_usage.cpu_util.setup</td> + <td>fraction</td> + <td>cpu used by system init and (re-)configuration</td> + </tr> + <tr> + <td>content.proton.resource_usage.cpu_util.read</td> + <td>fraction</td> + <td>cpu used by reading data from the system</td> + </tr> + <tr> + <td>content.proton.resource_usage.cpu_util.write</td> + <td>fraction</td> + <td>cpu used by writing data to the system</td> + </tr> + <tr> + <td>content.proton.resource_usage.cpu_util.compact</td> + <td>fraction</td> + <td>cpu used by internal data re-structuring</td> + </tr> + <tr> + <td>content.proton.resource_usage.cpu_util.other</td> + <td>fraction</td> + <td>cpu used by work not classified as a specific category</td> + </tr> + <tr> + <td>content.proton.transactionlog.entries</td> + <td>record</td> + <td>The current number of entries in the transaction log</td> + </tr> + <tr> + <td>content.proton.transactionlog.disk_usage</td> + <td>byte</td> + <td>The disk usage (in bytes) of the transaction log</td> + </tr> + <tr> + <td>content.proton.transactionlog.replay_time</td> + <td>second</td> + <td>The replay time (in seconds) of the transaction log during start-up</td> + </tr> + <tr> + <td>content.proton.documentdb.ready.document_store.disk_usage</td> + <td>byte</td> + <td>Disk space usage in bytes</td> + </tr> + <tr> + <td>content.proton.documentdb.ready.document_store.disk_bloat</td> + <td>byte</td> + <td>Disk space bloat in bytes</td> + </tr> + <tr> + <td>content.proton.documentdb.ready.document_store.max_bucket_spread</td> + <td>fraction</td> + <td>Max bucket spread in underlying files (sum(unique buckets in each chunk)/unique buckets in file)</td> + </tr> + <tr> + <td>content.proton.documentdb.ready.document_store.memory_usage.allocated_bytes</td> + <td>byte</td> + <td>The number of allocated bytes</td> + </tr> + <tr> + <td>content.proton.documentdb.ready.document_store.memory_usage.used_bytes</td> + <td>byte</td> + <td>The number of used bytes (<code>{`<=`}</code> allocated_bytes)</td> + </tr> + <tr> + <td>content.proton.documentdb.ready.document_store.memory_usage.dead_bytes</td> + <td>byte</td> + <td>The number of dead bytes (<code>{`<=`}</code> used_bytes)</td> + </tr> + <tr> + <td>content.proton.documentdb.ready.document_store.memory_usage.onhold_bytes</td> + <td>byte</td> + <td>The number of bytes on hold</td> + </tr> + <tr> + <td>content.proton.documentdb.notready.document_store.disk_usage</td> + <td>byte</td> + <td>Disk space usage in bytes</td> + </tr> + <tr> + <td>content.proton.documentdb.notready.document_store.disk_bloat</td> + <td>byte</td> + <td>Disk space bloat in bytes</td> + </tr> + <tr> + <td>content.proton.documentdb.notready.document_store.max_bucket_spread</td> + <td>fraction</td> + <td>Max bucket spread in underlying files (sum(unique buckets in each chunk)/unique buckets in file)</td> + </tr> + <tr> + <td>content.proton.documentdb.notready.document_store.memory_usage.allocated_bytes</td> + <td>byte</td> + <td>The number of allocated bytes</td> + </tr> + <tr> + <td>content.proton.documentdb.notready.document_store.memory_usage.used_bytes</td> + <td>byte</td> + <td>The number of used bytes (<code>{`<=`}</code> allocated_bytes)</td> + </tr> + <tr> + <td>content.proton.documentdb.notready.document_store.memory_usage.dead_bytes</td> + <td>byte</td> + <td>The number of dead bytes (<code>{`<=`}</code> used_bytes)</td> + </tr> + <tr> + <td>content.proton.documentdb.notready.document_store.memory_usage.onhold_bytes</td> + <td>byte</td> + <td>The number of bytes on hold</td> + </tr> + <tr> + <td>content.proton.documentdb.removed.document_store.disk_usage</td> + <td>byte</td> + <td>Disk space usage in bytes</td> + </tr> + <tr> + <td>content.proton.documentdb.removed.document_store.disk_bloat</td> + <td>byte</td> + <td>Disk space bloat in bytes</td> + </tr> + <tr> + <td>content.proton.documentdb.removed.document_store.max_bucket_spread</td> + <td>fraction</td> + <td>Max bucket spread in underlying files (sum(unique buckets in each chunk)/unique buckets in file)</td> + </tr> + <tr> + <td>content.proton.documentdb.removed.document_store.memory_usage.allocated_bytes</td> + <td>byte</td> + <td>The number of allocated bytes</td> + </tr> + <tr> + <td>content.proton.documentdb.removed.document_store.memory_usage.used_bytes</td> + <td>byte</td> + <td>The number of used bytes (<code>{`<=`}</code> allocated_bytes)</td> + </tr> + <tr> + <td>content.proton.documentdb.removed.document_store.memory_usage.dead_bytes</td> + <td>byte</td> + <td>The number of dead bytes (<code>{`<=`}</code> used_bytes)</td> + </tr> + <tr> + <td>content.proton.documentdb.removed.document_store.memory_usage.onhold_bytes</td> + <td>byte</td> + <td>The number of bytes on hold</td> + </tr> + <tr> + <td>content.proton.documentdb.ready.document_store.cache.elements</td> + <td>item</td> + <td>Number of elements in the cache</td> + </tr> + <tr> + <td>content.proton.documentdb.ready.document_store.cache.memory_usage</td> + <td>byte</td> + <td>Memory usage of the cache (in bytes)</td> + </tr> + <tr> + <td>content.proton.documentdb.ready.document_store.cache.hit_rate</td> + <td>fraction</td> + <td>Rate of hits in the cache compared to number of lookups</td> + </tr> + <tr> + <td>content.proton.documentdb.ready.document_store.cache.lookups</td> + <td>operation</td> + <td>Number of lookups in the cache (hits + misses)</td> + </tr> + <tr> + <td>content.proton.documentdb.ready.document_store.cache.invalidations</td> + <td>operation</td> + <td>Number of invalidations (erased elements) in the cache.</td> + </tr> + <tr> + <td>content.proton.documentdb.notready.document_store.cache.elements</td> + <td>item</td> + <td>Number of elements in the cache</td> + </tr> + <tr> + <td>content.proton.documentdb.notready.document_store.cache.memory_usage</td> + <td>byte</td> + <td>Memory usage of the cache (in bytes)</td> + </tr> + <tr> + <td>content.proton.documentdb.notready.document_store.cache.hit_rate</td> + <td>fraction</td> + <td>Rate of hits in the cache compared to number of lookups</td> + </tr> + <tr> + <td>content.proton.documentdb.notready.document_store.cache.lookups</td> + <td>operation</td> + <td>Number of lookups in the cache (hits + misses)</td> + </tr> + <tr> + <td>content.proton.documentdb.notready.document_store.cache.invalidations</td> + <td>operation</td> + <td>Number of invalidations (erased elements) in the cache.</td> + </tr> + <tr> + <td>content.proton.documentdb.removed.document_store.cache.elements</td> + <td>item</td> + <td>Number of elements in the cache</td> + </tr> + <tr> + <td>content.proton.documentdb.removed.document_store.cache.hit_rate</td> + <td>fraction</td> + <td>Rate of hits in the cache compared to number of lookups</td> + </tr> + <tr> + <td>content.proton.documentdb.removed.document_store.cache.invalidations</td> + <td>item</td> + <td>Number of invalidations (erased elements) in the cache.</td> + </tr> + <tr> + <td>content.proton.documentdb.removed.document_store.cache.lookups</td> + <td>operation</td> + <td>Number of lookups in the cache (hits + misses)</td> + </tr> + <tr> + <td>content.proton.documentdb.removed.document_store.cache.memory_usage</td> + <td>byte</td> + <td>Memory usage of the cache (in bytes)</td> + </tr> + <tr> + <td>content.proton.documentdb.ready.attribute.memory_usage.allocated_bytes</td> + <td>byte</td> + <td>The number of allocated bytes</td> + </tr> + <tr> + <td>content.proton.documentdb.ready.attribute.memory_usage.used_bytes</td> + <td>byte</td> + <td>The number of used bytes (<code>{`<=`}</code> allocated_bytes)</td> + </tr> + <tr> + <td>content.proton.documentdb.ready.attribute.memory_usage.dead_bytes</td> + <td>byte</td> + <td>The number of dead bytes (<code>{`<=`}</code> used_bytes)</td> + </tr> + <tr> + <td>content.proton.documentdb.ready.attribute.memory_usage.onhold_bytes</td> + <td>byte</td> + <td>The number of bytes on hold</td> + </tr> + <tr> + <td>content.proton.documentdb.ready.attribute.disk_usage</td> + <td>byte</td> + <td>Disk space usage (in bytes) of the flushed snapshot of this attribute for this document type</td> + </tr> + <tr> + <td>content.proton.documentdb.notready.attribute.memory_usage.allocated_bytes</td> + <td>byte</td> + <td>The number of allocated bytes</td> + </tr> + <tr> + <td>content.proton.documentdb.notready.attribute.memory_usage.used_bytes</td> + <td>byte</td> + <td>The number of used bytes (<code>{`<=`}</code> allocated_bytes)</td> + </tr> + <tr> + <td>content.proton.documentdb.notready.attribute.memory_usage.dead_bytes</td> + <td>byte</td> + <td>The number of dead bytes (<code>{`<=`}</code> used_bytes)</td> + </tr> + <tr> + <td>content.proton.documentdb.notready.attribute.memory_usage.onhold_bytes</td> + <td>byte</td> + <td>The number of bytes on hold</td> + </tr> + <tr> + <td>content.proton.index.cache.postinglist.elements</td> + <td>item</td> + <td>Number of elements in the cache. Contains disk index posting list files across all document types</td> + </tr> + <tr> + <td>content.proton.index.cache.postinglist.memory_usage</td> + <td>byte</td> + <td>Memory usage of the cache (in bytes). Contains disk index posting list files across all document types</td> + </tr> + <tr> + <td>content.proton.index.cache.postinglist.hit_rate</td> + <td>fraction</td> + <td>Rate of hits in the cache compared to number of lookups. Contains disk index posting list files across all document types</td> + </tr> + <tr> + <td>content.proton.index.cache.postinglist.lookups</td> + <td>operation</td> + <td>Number of lookups in the cache (hits + misses). Contains disk index posting list files across all document types</td> + </tr> + <tr> + <td>content.proton.index.cache.postinglist.invalidations</td> + <td>operation</td> + <td>Number of invalidations (erased elements) in the cache. Contains disk index posting list files across all document types</td> + </tr> + <tr> + <td>content.proton.index.cache.bitvector.elements</td> + <td>item</td> + <td>Number of elements in the cache. Contains disk index bitvector files across all document types</td> + </tr> + <tr> + <td>content.proton.index.cache.bitvector.memory_usage</td> + <td>byte</td> + <td>Memory usage of the cache (in bytes). Contains disk index bitvector files across all document types</td> + </tr> + <tr> + <td>content.proton.index.cache.bitvector.hit_rate</td> + <td>fraction</td> + <td>Rate of hits in the cache compared to number of lookups. Contains disk index bitvector files across all document types</td> + </tr> + <tr> + <td>content.proton.index.cache.bitvector.lookups</td> + <td>operation</td> + <td>Number of lookups in the cache (hits + misses). Contains disk index bitvector files across all document types</td> + </tr> + <tr> + <td>content.proton.index.cache.bitvector.invalidations</td> + <td>operation</td> + <td>Number of invalidations (erased elements) in the cache. Contains disk index bitvector files across all document types</td> + </tr> + <tr> + <td>content.proton.documentdb.index.memory_usage.allocated_bytes</td> + <td>byte</td> + <td>The number of allocated bytes for the memory index for this document type</td> + </tr> + <tr> + <td>content.proton.documentdb.index.memory_usage.used_bytes</td> + <td>byte</td> + <td>The number of used bytes (<code>{`<=`}</code> allocated_bytes) for the memory index for this document type</td> + </tr> + <tr> + <td>content.proton.documentdb.index.memory_usage.dead_bytes</td> + <td>byte</td> + <td>The number of dead bytes (<code>{`<=`}</code> used_bytes) for the memory index for this document type</td> + </tr> + <tr> + <td>content.proton.documentdb.index.memory_usage.onhold_bytes</td> + <td>byte</td> + <td>The number of bytes on hold for the memory index for this document type</td> + </tr> + <tr> + <td>content.proton.documentdb.index.disk_usage</td> + <td>byte</td> + <td>Disk space usage (in bytes) of all disk indexes for this document type</td> + </tr> + <tr> + <td>content.proton.documentdb.index.indexes</td> + <td>item</td> + <td>Number of disk or memory indexes</td> + </tr> + <tr> + <td>content.proton.documentdb.index.io.search.read_bytes</td> + <td>byte</td> + <td>Bytes read from disk index posting list and bitvector files as part of search for this document type</td> + </tr> + <tr> + <td>content.proton.documentdb.index.io.search.cached_read_bytes</td> + <td>byte</td> + <td>Bytes read from cached disk index posting list and bitvector files as part of search for this document type</td> + </tr> + <tr> + <td>content.proton.documentdb.ready.index.memory_usage.allocated_bytes</td> + <td>byte</td> + <td>The number of allocated bytes for this index field in the memory index for this document type</td> + </tr> + <tr> + <td>content.proton.documentdb.ready.index.disk_usage</td> + <td>byte</td> + <td>Disk space usage (in bytes) of this index field in all disk indexes for this document type</td> + </tr> + <tr> + <td>content.proton.documentdb.matching.queries</td> + <td>query</td> + <td>Number of queries executed</td> + </tr> + <tr> + <td>content.proton.documentdb.matching.soft_doomed_queries</td> + <td>query</td> + <td>Number of queries hitting the soft timeout</td> + </tr> + <tr> + <td>content.proton.documentdb.matching.query_latency</td> + <td>second</td> + <td>Total average latency (sec) when matching and ranking a query</td> + </tr> + <tr> + <td>content.proton.documentdb.matching.query_setup_time</td> + <td>second</td> + <td>Average time (sec) spent setting up and tearing down queries</td> + </tr> + <tr> + <td>content.proton.documentdb.matching.docs_matched</td> + <td>document</td> + <td>Number of documents matched</td> + </tr> + <tr> + <td>content.proton.documentdb.matching.docs_ranked</td> + <td>document</td> + <td>Number of documents ranked (first phase)</td> + </tr> + <tr> + <td>content.proton.documentdb.matching.docs_reranked</td> + <td>document</td> + <td>Number of documents re-ranked (second phase)</td> + </tr> + <tr> + <td>content.proton.documentdb.matching.exact_nns_distances_computed</td> + <td>distance</td> + <td>Number of distances computed in exact nearest-neighbor search</td> + </tr> + <tr> + <td>content.proton.documentdb.matching.approximate_nns_distances_computed</td> + <td>distance</td> + <td>Number of distances computed in approximate nearest-neighbor search</td> + </tr> + <tr> + <td>content.proton.documentdb.matching.approximate_nns_nodes_visited</td> + <td>graph_node</td> + <td>Number of nodes visited in approximate nearest-neighbor search</td> + </tr> + <tr> + <td>content.proton.documentdb.matching.rank_profile.queries</td> + <td>query</td> + <td>Number of queries executed</td> + </tr> + <tr> + <td>content.proton.documentdb.matching.rank_profile.soft_doomed_queries</td> + <td>query</td> + <td>Number of queries hitting the soft timeout</td> + </tr> + <tr> + <td>content.proton.documentdb.matching.rank_profile.soft_doom_factor</td> + <td>fraction</td> + <td>Factor used to compute soft-timeout</td> + </tr> + <tr> + <td>content.proton.documentdb.matching.rank_profile.query_latency</td> + <td>second</td> + <td>Total average latency (sec) when matching and ranking a query</td> + </tr> + <tr> + <td>content.proton.documentdb.matching.rank_profile.query_setup_time</td> + <td>second</td> + <td>Average time (sec) spent setting up and tearing down queries</td> + </tr> + <tr> + <td>content.proton.documentdb.matching.rank_profile.grouping_time</td> + <td>second</td> + <td>Average time (sec) spent on grouping</td> + </tr> + <tr> + <td>content.proton.documentdb.matching.rank_profile.rerank_time</td> + <td>second</td> + <td>Average time (sec) spent on 2nd phase ranking</td> + </tr> + <tr> + <td>content.proton.documentdb.matching.rank_profile.docs_matched</td> + <td>document</td> + <td>Number of documents matched</td> + </tr> + <tr> + <td>content.proton.documentdb.matching.rank_profile.docs_ranked</td> + <td>document</td> + <td>Number of documents ranked (first phase)</td> + </tr> + <tr> + <td>content.proton.documentdb.matching.rank_profile.docs_reranked</td> + <td>document</td> + <td>Number of documents re-ranked (second phase)</td> + </tr> + <tr> + <td>content.proton.documentdb.matching.rank_profile.exact_nns_distances_computed</td> + <td>distance</td> + <td>Number of distances computed in exact nearest-neighbor search</td> + </tr> + <tr> + <td>content.proton.documentdb.matching.rank_profile.approximate_nns_distances_computed</td> + <td>distance</td> + <td>Number of distances computed in approximate nearest-neighbor search</td> + </tr> + <tr> + <td>content.proton.documentdb.matching.rank_profile.approximate_nns_nodes_visited</td> + <td>graph_node</td> + <td>Number of nodes visited in approximate nearest-neighbor search</td> + </tr> + <tr> + <td>content.proton.documentdb.matching.rank_profile.limited_queries</td> + <td>query</td> + <td>Number of queries limited in match phase</td> + </tr> + <tr> + <td>content.proton.documentdb.matching.rank_profile.docid_partition.active_time</td> + <td>second</td> + <td>Time (sec) spent doing actual work</td> + </tr> + <tr> + <td>content.proton.documentdb.matching.rank_profile.docid_partition.docs_matched</td> + <td>document</td> + <td>Number of documents matched</td> + </tr> + <tr> + <td>content.proton.documentdb.matching.rank_profile.docid_partition.docs_ranked</td> + <td>document</td> + <td>Number of documents ranked (first phase)</td> + </tr> + <tr> + <td>content.proton.documentdb.matching.rank_profile.docid_partition.docs_reranked</td> + <td>document</td> + <td>Number of documents re-ranked (second phase)</td> + </tr> + <tr> + <td>content.proton.documentdb.matching.rank_profile.docid_partition.wait_time</td> + <td>second</td> + <td>Time (sec) spent waiting for other external threads and resources</td> + </tr> + <tr> + <td>content.proton.documentdb.matching.rank_profile.match_time</td> + <td>second</td> + <td>Average time (sec) for matching a query (1st phase)</td> + </tr> + <tr> + <td>content.proton.documentdb.feeding.commit.operations</td> + <td>operation</td> + <td>Number of operations included in a commit</td> + </tr> + <tr> + <td>content.proton.documentdb.feeding.commit.latency</td> + <td>second</td> + <td>Latency for commit in seconds</td> + </tr> + <tr> + <td>content.proton.session_cache.grouping.num_cached</td> + <td>session</td> + <td>Number of currently cached sessions</td> + </tr> + <tr> + <td>content.proton.session_cache.grouping.num_dropped</td> + <td>session</td> + <td>Number of dropped cached sessions</td> + </tr> + <tr> + <td>content.proton.session_cache.grouping.num_insert</td> + <td>session</td> + <td>Number of inserted sessions</td> + </tr> + <tr> + <td>content.proton.session_cache.grouping.num_pick</td> + <td>session</td> + <td>Number if picked sessions</td> + </tr> + <tr> + <td>content.proton.session_cache.grouping.num_timedout</td> + <td>session</td> + <td>Number of timed out sessions</td> + </tr> + <tr> + <td>content.proton.session_cache.search.num_cached</td> + <td>session</td> + <td>Number of currently cached sessions</td> + </tr> + <tr> + <td>content.proton.session_cache.search.num_dropped</td> + <td>session</td> + <td>Number of dropped cached sessions</td> + </tr> + <tr> + <td>content.proton.session_cache.search.num_insert</td> + <td>session</td> + <td>Number of inserted sessions</td> + </tr> + <tr> + <td>content.proton.session_cache.search.num_pick</td> + <td>session</td> + <td>Number if picked sessions</td> + </tr> + <tr> + <td>content.proton.session_cache.search.num_timedout</td> + <td>session</td> + <td>Number of timed out sessions</td> + </tr> + <tr> + <td>metricmanager.periodichooklatency</td> + <td>millisecond</td> + <td>Time in ms used to update a single periodic hook</td> + </tr> + <tr> + <td>metricmanager.resetlatency</td> + <td>millisecond</td> + <td>Time in ms used to reset all metrics.</td> + </tr> + <tr> + <td>metricmanager.sleeptime</td> + <td>millisecond</td> + <td>Time in ms worker thread is sleeping</td> + </tr> + <tr> + <td>metricmanager.snapshothooklatency</td> + <td>millisecond</td> + <td>Time in ms used to update a single snapshot hook</td> + </tr> + <tr> + <td>metricmanager.snapshotlatency</td> + <td>millisecond</td> + <td>Time in ms used to take a snapshot</td> + </tr> + </tbody> +</table> \ No newline at end of file diff --git a/en/reference/operations/metrics/sentinel.html b/en/reference/operations/metrics/sentinel.html deleted file mode 100644 index 12cfb4eb15..0000000000 --- a/en/reference/operations/metrics/sentinel.html +++ /dev/null @@ -1,34 +0,0 @@ ---- -# Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -title: "Sentinel Metrics" -redirect_from: -- en/reference/sentinel-metrics-reference.html ---- - -<table class="table"> - <thead> - <tr><th>Name</th><th>Unit</th><th>Description</th></tr> - </thead> - <tbody> - <tr> - <td><p id="sentinel_restarts">sentinel.restarts</p></td> - <td>restart</td> - <td>Number of service restarts done by the sentinel</td> - </tr> - <tr> - <td><p id="sentinel_totalRestarts">sentinel.totalRestarts</p></td> - <td>restart</td> - <td>Total number of service restarts done by the sentinel since the sentinel was started</td> - </tr> - <tr> - <td><p id="sentinel_uptime">sentinel.uptime</p></td> - <td>second</td> - <td>Time the sentinel has been running</td> - </tr> - <tr> - <td><p id="sentinel_running">sentinel.running</p></td> - <td>instance</td> - <td>Number of services the sentinel has running currently</td> - </tr> - </tbody> -</table> diff --git a/en/reference/operations/metrics/sentinel.mdx b/en/reference/operations/metrics/sentinel.mdx new file mode 100644 index 0000000000..4f98ccacbd --- /dev/null +++ b/en/reference/operations/metrics/sentinel.mdx @@ -0,0 +1,36 @@ +--- +title: "Sentinel Metrics" +sidebarTitle: "Sentinel metrics" +--- + +<table className="vespa-table"> + <thead> + <tr> + <th>Name</th> + <th>Unit</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>sentinel.restarts</td> + <td>restart</td> + <td>Number of service restarts done by the sentinel</td> + </tr> + <tr> + <td>sentinel.totalRestarts</td> + <td>restart</td> + <td>Total number of service restarts done by the sentinel since the sentinel was started</td> + </tr> + <tr> + <td>sentinel.uptime</td> + <td>second</td> + <td>Time the sentinel has been running</td> + </tr> + <tr> + <td>sentinel.running</td> + <td>instance</td> + <td>Number of services the sentinel has running currently</td> + </tr> + </tbody> +</table> \ No newline at end of file diff --git a/en/reference/operations/metrics/slobrok.html b/en/reference/operations/metrics/slobrok.html deleted file mode 100644 index e1c8d45ed1..0000000000 --- a/en/reference/operations/metrics/slobrok.html +++ /dev/null @@ -1,39 +0,0 @@ ---- -# Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -title: "Slobrok Metrics" -redirect_from: -- en/reference/slobrok-metrics-reference.html ---- - -<table class="table"> - <thead> - <tr><th>Name</th><th>Unit</th><th>Description</th></tr> - </thead> - <tbody> - <tr> - <td><p id="slobrok_heartbeats_failed">slobrok.heartbeats.failed</p></td> - <td>request</td> - <td>Number of heartbeat requests failed</td> - </tr> - <tr> - <td><p id="slobrok_requests_register">slobrok.requests.register</p></td> - <td>request</td> - <td>Number of register requests received</td> - </tr> - <tr> - <td><p id="slobrok_requests_mirror">slobrok.requests.mirror</p></td> - <td>request</td> - <td>Number of mirroring requests received</td> - </tr> - <tr> - <td><p id="slobrok_requests_admin">slobrok.requests.admin</p></td> - <td>request</td> - <td>Number of administrative requests received</td> - </tr> - <tr> - <td><p id="slobrok_missing_consensus">slobrok.missing.consensus</p></td> - <td>second</td> - <td>Number of seconds without full consensus with all other brokers</td> - </tr> - </tbody> -</table> diff --git a/en/reference/operations/metrics/slobrok.mdx b/en/reference/operations/metrics/slobrok.mdx new file mode 100644 index 0000000000..293f234597 --- /dev/null +++ b/en/reference/operations/metrics/slobrok.mdx @@ -0,0 +1,41 @@ +--- +title: "Slobrok Metrics" +sidebarTitle: "Slobrok metrics" +--- + +<table className="vespa-table"> + <thead> + <tr> + <th>Name</th> + <th>Unit</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>slobrok.heartbeats.failed</td> + <td>request</td> + <td>Number of heartbeat requests failed</td> + </tr> + <tr> + <td>slobrok.requests.register</td> + <td>request</td> + <td>Number of register requests received</td> + </tr> + <tr> + <td>slobrok.requests.mirror</td> + <td>request</td> + <td>Number of mirroring requests received</td> + </tr> + <tr> + <td>slobrok.requests.admin</td> + <td>request</td> + <td>Number of administrative requests received</td> + </tr> + <tr> + <td>slobrok.missing.consensus</td> + <td>second</td> + <td>Number of seconds without full consensus with all other brokers</td> + </tr> + </tbody> +</table> \ No newline at end of file diff --git a/en/reference/operations/metrics/storage.html b/en/reference/operations/metrics/storage.html deleted file mode 100644 index fbfcdc5d00..0000000000 --- a/en/reference/operations/metrics/storage.html +++ /dev/null @@ -1,1054 +0,0 @@ ---- -# Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -title: "Storage Metrics" -redirect_from: -- en/reference/storage-metrics-reference.html ---- - -<table class="table"> - <thead> - <tr><th>Name</th><th>Unit</th><th>Description</th></tr> - </thead> - <tbody> - <tr> - <td><p id="vds_datastored_alldisks_buckets">vds.datastored.alldisks.buckets</p></td> - <td>bucket</td> - <td>Number of buckets managed</td> - </tr> - <tr> - <td><p id="vds_datastored_alldisks_docs">vds.datastored.alldisks.docs</p></td> - <td>document</td> - <td>Number of documents stored</td> - </tr> - <tr> - <td><p id="vds_datastored_alldisks_bytes">vds.datastored.alldisks.bytes</p></td> - <td>byte</td> - <td>Number of bytes stored</td> - </tr> - <tr> - <td><p id="vds_datastored_alldisks_activebuckets">vds.datastored.alldisks.activebuckets</p></td> - <td>bucket</td> - <td>Number of active buckets on the node</td> - </tr> - <tr> - <td><p id="vds_datastored_alldisks_readybuckets">vds.datastored.alldisks.readybuckets</p></td> - <td>bucket</td> - <td>Number of ready buckets on the node</td> - </tr> - <tr> - <td><p id="vds_visitor_allthreads_averagevisitorlifetime">vds.visitor.allthreads.averagevisitorlifetime</p></td> - <td>millisecond</td> - <td>Average lifetime of a visitor</td> - </tr> - <tr> - <td><p id="vds_visitor_allthreads_averagequeuewait">vds.visitor.allthreads.averagequeuewait</p></td> - <td>millisecond</td> - <td>Average time an operation spends in input queue.</td> - </tr> - <tr> - <td><p id="vds_visitor_allthreads_queuesize">vds.visitor.allthreads.queuesize</p></td> - <td>operation</td> - <td>Size of input message queue.</td> - </tr> - <tr> - <td><p id="vds_visitor_allthreads_completed">vds.visitor.allthreads.completed</p></td> - <td>operation</td> - <td>Number of visitors completed</td> - </tr> - <tr> - <td><p id="vds_visitor_allthreads_created">vds.visitor.allthreads.created</p></td> - <td>operation</td> - <td>Number of visitors created.</td> - </tr> - <tr> - <td><p id="vds_visitor_allthreads_failed">vds.visitor.allthreads.failed</p></td> - <td>operation</td> - <td>Number of visitors failed</td> - </tr> - <tr> - <td><p id="vds_visitor_allthreads_averagemessagesendtime">vds.visitor.allthreads.averagemessagesendtime</p></td> - <td>millisecond</td> - <td>Average time it takes for messages to be sent to their target (and be replied to)</td> - </tr> - <tr> - <td><p id="vds_visitor_allthreads_averageprocessingtime">vds.visitor.allthreads.averageprocessingtime</p></td> - <td>millisecond</td> - <td>Average time used to process visitor requests</td> - </tr> - <tr> - <td><p id="vds_visitor_allthreads_aborted">vds.visitor.allthreads.aborted</p></td> - <td>instance</td> - <td>Number of visitors aborted.</td> - </tr> - <tr> - <td><p id="vds_visitor_allthreads_averagevisitorcreationtime">vds.visitor.allthreads.averagevisitorcreationtime</p></td> - <td>millisecond</td> - <td>Average time spent creating a visitor instance</td> - </tr> - <tr> - <td><p id="vds_visitor_allthreads_destination_failure_replies">vds.visitor.allthreads.destination_failure_replies</p></td> - <td>instance</td> - <td>Number of failure replies received from the visitor destination</td> - </tr> - <tr> - <td><p id="vds_filestor_queuesize">vds.filestor.queuesize</p></td> - <td>operation</td> - <td>Size of input message queue.</td> - </tr> - <tr> - <td><p id="vds_filestor_averagequeuewait">vds.filestor.averagequeuewait</p></td> - <td>millisecond</td> - <td>Average time an operation spends in input queue.</td> - </tr> - <tr> - <td><p id="vds_filestor_active_operations_size">vds.filestor.active_operations.size</p></td> - <td>operation</td> - <td>Number of concurrent active operations</td> - </tr> - <tr> - <td><p id="vds_filestor_active_operations_latency">vds.filestor.active_operations.latency</p></td> - <td>millisecond</td> - <td>Latency (in ms) for completed operations</td> - </tr> - <tr> - <td><p id="vds_filestor_throttle_window_size">vds.filestor.throttle_window_size</p></td> - <td>operation</td> - <td>Current size of async operation throttler window size</td> - </tr> - <tr> - <td><p id="vds_filestor_throttle_waiting_threads">vds.filestor.throttle_waiting_threads</p></td> - <td>thread</td> - <td>Number of threads waiting to acquire a throttle token</td> - </tr> - <tr> - <td><p id="vds_filestor_throttle_active_tokens">vds.filestor.throttle_active_tokens</p></td> - <td>instance</td> - <td>Current number of active throttle tokens</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_mergemetadatareadlatency">vds.filestor.allthreads.mergemetadatareadlatency</p></td> - <td>millisecond</td> - <td>Time spent in a merge step to check metadata of current node to see what data it has.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_mergedatareadlatency">vds.filestor.allthreads.mergedatareadlatency</p></td> - <td>millisecond</td> - <td>Time spent in a merge step to read data other nodes need.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_mergedatawritelatency">vds.filestor.allthreads.mergedatawritelatency</p></td> - <td>millisecond</td> - <td>Time spent in a merge step to write data needed to current node.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_mergeavgdatareceivedneeded">vds.filestor.allthreads.mergeavgdatareceivedneeded</p></td> - <td>byte</td> - <td>Amount of data transferred from previous node in chain that we needed to apply locally.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_mergebuckets_count">vds.filestor.allthreads.mergebuckets.count</p></td> - <td>request</td> - <td>Number of requests processed.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_mergebuckets_failed">vds.filestor.allthreads.mergebuckets.failed</p></td> - <td>request</td> - <td>Number of failed requests.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_mergebuckets_latency">vds.filestor.allthreads.mergebuckets.latency</p></td> - <td>millisecond</td> - <td>Latency of successful requests.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_mergelatencytotal">vds.filestor.allthreads.mergelatencytotal</p></td> - <td>millisecond</td> - <td>Latency of total merge operation, from master node receives it, until merge is complete and master node replies.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_merge_put_latency">vds.filestor.allthreads.merge_put_latency</p></td> - <td>millisecond</td> - <td>Latency of individual puts that are part of merge operations</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_merge_remove_latency">vds.filestor.allthreads.merge_remove_latency</p></td> - <td>millisecond</td> - <td>Latency of individual removes that are part of merge operations</td> - </tr> - <tr> - <td><p id="vds_filestor_allstripes_throttled_rpc_direct_dispatches">vds.filestor.allstripes.throttled_rpc_direct_dispatches</p></td> - <td>instance</td> - <td>Number of times an RPC thread could not directly dispatch an async operation directly to Proton because it was disallowed by the throttle policy</td> - </tr> - <tr> - <td><p id="vds_filestor_allstripes_throttled_persistence_thread_polls">vds.filestor.allstripes.throttled_persistence_thread_polls</p></td> - <td>instance</td> - <td>Number of times a persistence thread could not immediately dispatch a queued async operation because it was disallowed by the throttle policy</td> - </tr> - <tr> - <td><p id="vds_filestor_allstripes_timeouts_waiting_for_throttle_token">vds.filestor.allstripes.timeouts_waiting_for_throttle_token</p></td> - <td>instance</td> - <td>Number of times a persistence thread timed out waiting for an available throttle policy token</td> - </tr> - <tr> - <td><p id="vds_filestor_allstripes_averagequeuewait">vds.filestor.allstripes.averagequeuewait</p></td> - <td>millisecond</td> - <td>Average time an operation spends in input queue.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_put_count">vds.filestor.allthreads.put.count</p></td> - <td>operation</td> - <td>Number of requests processed.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_put_failed">vds.filestor.allthreads.put.failed</p></td> - <td>operation</td> - <td>Number of failed requests.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_put_test_and_set_failed">vds.filestor.allthreads.put.test_and_set_failed</p></td> - <td>operation</td> - <td>Number of operations that were skipped due to a test-and-set condition not met</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_put_latency">vds.filestor.allthreads.put.latency</p></td> - <td>millisecond</td> - <td>Latency of successful requests.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_put_request_size">vds.filestor.allthreads.put.request_size</p></td> - <td>byte</td> - <td>Size of requests, in bytes</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_remove_count">vds.filestor.allthreads.remove.count</p></td> - <td>operation</td> - <td>Number of requests processed.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_remove_failed">vds.filestor.allthreads.remove.failed</p></td> - <td>operation</td> - <td>Number of failed requests.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_remove_test_and_set_failed">vds.filestor.allthreads.remove.test_and_set_failed</p></td> - <td>operation</td> - <td>Number of operations that were skipped due to a test-and-set condition not met</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_remove_latency">vds.filestor.allthreads.remove.latency</p></td> - <td>millisecond</td> - <td>Latency of successful requests.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_remove_request_size">vds.filestor.allthreads.remove.request_size</p></td> - <td>byte</td> - <td>Size of requests, in bytes</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_remove_not_found">vds.filestor.allthreads.remove.not_found</p></td> - <td>request</td> - <td>Number of requests that could not be completed due to source document not found.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_get_count">vds.filestor.allthreads.get.count</p></td> - <td>operation</td> - <td>Number of requests processed.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_get_failed">vds.filestor.allthreads.get.failed</p></td> - <td>operation</td> - <td>Number of failed requests.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_get_latency">vds.filestor.allthreads.get.latency</p></td> - <td>millisecond</td> - <td>Latency of successful requests.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_get_request_size">vds.filestor.allthreads.get.request_size</p></td> - <td>byte</td> - <td>Size of requests, in bytes</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_get_not_found">vds.filestor.allthreads.get.not_found</p></td> - <td>request</td> - <td>Number of requests that could not be completed due to source document not found.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_update_count">vds.filestor.allthreads.update.count</p></td> - <td>request</td> - <td>Number of requests processed.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_update_failed">vds.filestor.allthreads.update.failed</p></td> - <td>request</td> - <td>Number of failed requests.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_update_test_and_set_failed">vds.filestor.allthreads.update.test_and_set_failed</p></td> - <td>request</td> - <td>Number of requests that were skipped due to a test-and-set condition not met</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_update_latency">vds.filestor.allthreads.update.latency</p></td> - <td>millisecond</td> - <td>Latency of successful requests.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_update_request_size">vds.filestor.allthreads.update.request_size</p></td> - <td>byte</td> - <td>Size of requests, in bytes</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_update_latency_read">vds.filestor.allthreads.update.latency_read</p></td> - <td>millisecond</td> - <td>Latency of the source read in the request.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_update_not_found">vds.filestor.allthreads.update.not_found</p></td> - <td>request</td> - <td>Number of requests that could not be completed due to source document not found.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_createiterator_count">vds.filestor.allthreads.createiterator.count</p></td> - <td>request</td> - <td>Number of requests processed.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_createiterator_latency">vds.filestor.allthreads.createiterator.latency</p></td> - <td>millisecond</td> - <td>Latency of successful requests.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_createiterator_failed">vds.filestor.allthreads.createiterator.failed</p></td> - <td>request</td> - <td>Number of failed requests.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_visit_count">vds.filestor.allthreads.visit.count</p></td> - <td>request</td> - <td>Number of requests processed.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_visit_latency">vds.filestor.allthreads.visit.latency</p></td> - <td>millisecond</td> - <td>Latency of successful requests.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_visit_docs">vds.filestor.allthreads.visit.docs</p></td> - <td>document</td> - <td>Number of entries read per iterate call</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_visit_failed">vds.filestor.allthreads.visit.failed</p></td> - <td>request</td> - <td>Number of failed requests.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_remove_location_count">vds.filestor.allthreads.remove_location.count</p></td> - <td>request</td> - <td>Number of requests processed.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_remove_location_latency">vds.filestor.allthreads.remove_location.latency</p></td> - <td>millisecond</td> - <td>Latency of successful requests.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_remove_location_failed">vds.filestor.allthreads.remove_location.failed</p></td> - <td>request</td> - <td>Number of failed requests.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_splitbuckets_count">vds.filestor.allthreads.splitbuckets.count</p></td> - <td>request</td> - <td>Number of requests processed.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_splitbuckets_failed">vds.filestor.allthreads.splitbuckets.failed</p></td> - <td>request</td> - <td>Number of failed requests.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_splitbuckets_latency">vds.filestor.allthreads.splitbuckets.latency</p></td> - <td>request</td> - <td>Latency of successful requests.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_joinbuckets_count">vds.filestor.allthreads.joinbuckets.count</p></td> - <td>request</td> - <td>Number of requests processed.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_joinbuckets_failed">vds.filestor.allthreads.joinbuckets.failed</p></td> - <td>request</td> - <td>Number of failed requests.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_joinbuckets_latency">vds.filestor.allthreads.joinbuckets.latency</p></td> - <td>millisecond</td> - <td>Latency of successful requests.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_deletebuckets_count">vds.filestor.allthreads.deletebuckets.count</p></td> - <td>request</td> - <td>Number of requests processed.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_deletebuckets_failed">vds.filestor.allthreads.deletebuckets.failed</p></td> - <td>request</td> - <td>Number of failed requests.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_deletebuckets_latency">vds.filestor.allthreads.deletebuckets.latency</p></td> - <td>millisecond</td> - <td>Latency of successful requests.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_remove_by_gid_count">vds.filestor.allthreads.remove_by_gid.count</p></td> - <td>request</td> - <td>Number of requests processed.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_remove_by_gid_failed">vds.filestor.allthreads.remove_by_gid.failed</p></td> - <td>request</td> - <td>Number of failed requests.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_remove_by_gid_latency">vds.filestor.allthreads.remove_by_gid.latency</p></td> - <td>millisecond</td> - <td>Latency of successful requests.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_setbucketstates_count">vds.filestor.allthreads.setbucketstates.count</p></td> - <td>request</td> - <td>Number of requests processed.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_setbucketstates_failed">vds.filestor.allthreads.setbucketstates.failed</p></td> - <td>request</td> - <td>Number of failed requests.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_setbucketstates_latency">vds.filestor.allthreads.setbucketstates.latency</p></td> - <td>millisecond</td> - <td>Latency of successful requests.</td> - </tr> - <tr> - <td><p id="vds_mergethrottler_averagequeuewaitingtime">vds.mergethrottler.averagequeuewaitingtime</p></td> - <td>millisecond</td> - <td>Time merges spent in the throttler queue</td> - </tr> - <tr> - <td><p id="vds_mergethrottler_queuesize">vds.mergethrottler.queuesize</p></td> - <td>instance</td> - <td>Length of merge queue</td> - </tr> - <tr> - <td><p id="vds_mergethrottler_active_window_size">vds.mergethrottler.active_window_size</p></td> - <td>instance</td> - <td>Number of merges active within the pending window size</td> - </tr> - <tr> - <td><p id="vds_mergethrottler_estimated_merge_memory_usage">vds.mergethrottler.estimated_merge_memory_usage</p></td> - <td>byte</td> - <td>An estimated upper bound of the memory usage (in bytes) of the merges currently in the active window</td> - </tr> - <tr> - <td><p id="vds_mergethrottler_bounced_due_to_back_pressure">vds.mergethrottler.bounced_due_to_back_pressure</p></td> - <td>instance</td> - <td>Number of merges bounced due to resource exhaustion back-pressure</td> - </tr> - <tr> - <td><p id="vds_mergethrottler_locallyexecutedmerges_ok">vds.mergethrottler.locallyexecutedmerges.ok</p></td> - <td>instance</td> - <td>The number of successful merges for 'locallyexecutedmerges'</td> - </tr> - <tr> - <td><p id="vds_mergethrottler_locallyexecutedmerges_failures_aborted">vds.mergethrottler.locallyexecutedmerges.failures.aborted</p></td> - <td>operation</td> - <td>The number of merges that failed because the storage node was (most likely) shutting down</td> - </tr> - <tr> - <td><p id="vds_mergethrottler_locallyexecutedmerges_failures_bucketnotfound">vds.mergethrottler.locallyexecutedmerges.failures.bucketnotfound</p></td> - <td>operation</td> - <td>The number of operations that failed because the bucket did not exist</td> - </tr> - <tr> - <td><p id="vds_mergethrottler_locallyexecutedmerges_failures_busy">vds.mergethrottler.locallyexecutedmerges.failures.busy</p></td> - <td>operation</td> - <td>The number of merges that failed because the storage node was busy</td> - </tr> - <tr> - <td><p id="vds_mergethrottler_locallyexecutedmerges_failures_exists">vds.mergethrottler.locallyexecutedmerges.failures.exists</p></td> - <td>operation</td> - <td>The number of merges that were rejected due to a merge operation for their bucket already being processed</td> - </tr> - <tr> - <td><p id="vds_mergethrottler_locallyexecutedmerges_failures_notready">vds.mergethrottler.locallyexecutedmerges.failures.notready</p></td> - <td>operation</td> - <td>The number of merges discarded because distributor was not ready</td> - </tr> - <tr> - <td><p id="vds_mergethrottler_locallyexecutedmerges_failures_other">vds.mergethrottler.locallyexecutedmerges.failures.other</p></td> - <td>operation</td> - <td>The number of other failures</td> - </tr> - <tr> - <td><p id="vds_mergethrottler_locallyexecutedmerges_failures_rejected">vds.mergethrottler.locallyexecutedmerges.failures.rejected</p></td> - <td>operation</td> - <td>The number of merges that were rejected</td> - </tr> - <tr> - <td><p id="vds_mergethrottler_locallyexecutedmerges_failures_timeout">vds.mergethrottler.locallyexecutedmerges.failures.timeout</p></td> - <td>operation</td> - <td>The number of merges that failed because they timed out towards storage</td> - </tr> - <tr> - <td><p id="vds_mergethrottler_locallyexecutedmerges_failures_total">vds.mergethrottler.locallyexecutedmerges.failures.total</p></td> - <td>operation</td> - <td>Sum of all failures</td> - </tr> - <tr> - <td><p id="vds_mergethrottler_locallyexecutedmerges_failures_wrongdistribution">vds.mergethrottler.locallyexecutedmerges.failures.wrongdistribution</p></td> - <td>operation</td> - <td>The number of merges that were discarded (flushed) because they were initiated at an older cluster state than the current</td> - </tr> - <tr> - <td><p id="vds_mergethrottler_mergechains_ok">vds.mergethrottler.mergechains.ok</p></td> - <td>operation</td> - <td>The number of successful merges for 'mergechains'</td> - </tr> - <tr> - <td><p id="vds_mergethrottler_mergechains_failures_busy">vds.mergethrottler.mergechains.failures.busy</p></td> - <td>operation</td> - <td>The number of merges that failed because the storage node was busy</td> - </tr> - <tr> - <td><p id="vds_mergethrottler_mergechains_failures_total">vds.mergethrottler.mergechains.failures.total</p></td> - <td>operation</td> - <td>Sum of all failures</td> - </tr> - <tr> - <td><p id="vds_mergethrottler_mergechains_failures_exists">vds.mergethrottler.mergechains.failures.exists</p></td> - <td>operation</td> - <td>The number of merges that were rejected due to a merge operation for their bucket already being processed</td> - </tr> - <tr> - <td><p id="vds_mergethrottler_mergechains_failures_notready">vds.mergethrottler.mergechains.failures.notready</p></td> - <td>operation</td> - <td>The number of merges discarded because distributor was not ready</td> - </tr> - <tr> - <td><p id="vds_mergethrottler_mergechains_failures_other">vds.mergethrottler.mergechains.failures.other</p></td> - <td>operation</td> - <td>The number of other failures</td> - </tr> - <tr> - <td><p id="vds_mergethrottler_mergechains_failures_rejected">vds.mergethrottler.mergechains.failures.rejected</p></td> - <td>operation</td> - <td>The number of merges that were rejected</td> - </tr> - <tr> - <td><p id="vds_mergethrottler_mergechains_failures_timeout">vds.mergethrottler.mergechains.failures.timeout</p></td> - <td>operation</td> - <td>The number of merges that failed because they timed out towards storage</td> - </tr> - <tr> - <td><p id="vds_mergethrottler_mergechains_failures_wrongdistribution">vds.mergethrottler.mergechains.failures.wrongdistribution</p></td> - <td>operation</td> - <td>The number of merges that were discarded (flushed) because they were initiated at an older cluster state than the current</td> - </tr> - <tr> - <td><p id="vds_server_network_tls-handshakes-failed">vds.server.network.tls-handshakes-failed</p></td> - <td>operation</td> - <td>Number of client or server connection attempts that failed during TLS handshaking</td> - </tr> - <tr> - <td><p id="vds_server_network_peer-authorization-failures">vds.server.network.peer-authorization-failures</p></td> - <td>failure</td> - <td>Number of TLS connection attempts failed due to bad or missing peer certificate credentials</td> - </tr> - <tr> - <td><p id="vds_server_network_client_tls-connections-established">vds.server.network.client.tls-connections-established</p></td> - <td>connection</td> - <td>Number of secure mTLS connections established</td> - </tr> - <tr> - <td><p id="vds_server_network_server_tls-connections-established">vds.server.network.server.tls-connections-established</p></td> - <td>connection</td> - <td>Number of secure mTLS connections established</td> - </tr> - <tr> - <td><p id="vds_server_network_client_insecure-connections-established">vds.server.network.client.insecure-connections-established</p></td> - <td>connection</td> - <td>Number of insecure (plaintext) connections established</td> - </tr> - <tr> - <td><p id="vds_server_network_server_insecure-connections-established">vds.server.network.server.insecure-connections-established</p></td> - <td>connection</td> - <td>Number of insecure (plaintext) connections established</td> - </tr> - <tr> - <td><p id="vds_server_network_tls-connections-broken">vds.server.network.tls-connections-broken</p></td> - <td>connection</td> - <td>Number of TLS connections broken due to failures during frame encoding or decoding</td> - </tr> - <tr> - <td><p id="vds_server_network_failed-tls-config-reloads">vds.server.network.failed-tls-config-reloads</p></td> - <td>failure</td> - <td>Number of times background reloading of TLS config has failed</td> - </tr> - <tr> - <td><p id="vds_bouncer_unavailable_node_aborts">vds.bouncer.unavailable_node_aborts</p></td> - <td>operation</td> - <td>Number of operations that were aborted due to the node (or target bucket space) being unavailable</td> - </tr> - <tr> - <td><p id="vds_changedbucketownershiphandler_avg_abort_processing_time">vds.changedbucketownershiphandler.avg_abort_processing_time</p></td> - <td>millisecond</td> - <td>Average time spent aborting operations for changed buckets</td> - </tr> - <tr> - <td><p id="vds_changedbucketownershiphandler_external_load_ops_aborted">vds.changedbucketownershiphandler.external_load_ops_aborted</p></td> - <td>operation</td> - <td>Number of outdated external load operations aborted</td> - </tr> - <tr> - <td><p id="vds_changedbucketownershiphandler_ideal_state_ops_aborted">vds.changedbucketownershiphandler.ideal_state_ops_aborted</p></td> - <td>operation</td> - <td>Number of outdated ideal state operations aborted</td> - </tr> - <tr> - <td><p id="vds_communication_bucket_space_mapping_failures">vds.communication.bucket_space_mapping_failures</p></td> - <td>operation</td> - <td>Number of messages that could not be resolved to a known bucket space</td> - </tr> - <tr> - <td><p id="vds_communication_convertfailures">vds.communication.convertfailures</p></td> - <td>operation</td> - <td>Number of messages that failed to get converted to storage API messages</td> - </tr> - <tr> - <td><p id="vds_communication_exceptionmessageprocesstime">vds.communication.exceptionmessageprocesstime</p></td> - <td>millisecond</td> - <td>Time transport thread uses to process a single message that fails with an exception thrown into communication manager</td> - </tr> - <tr> - <td><p id="vds_communication_messageprocesstime">vds.communication.messageprocesstime</p></td> - <td>millisecond</td> - <td>Time transport thread uses to process a single message</td> - </tr> - <tr> - <td><p id="vds_communication_messagequeue">vds.communication.messagequeue</p></td> - <td>item</td> - <td>Size of input message queue.</td> - </tr> - <tr> - <td><p id="vds_communication_sendcommandlatency">vds.communication.sendcommandlatency</p></td> - <td>millisecond</td> - <td>Average ms used to send commands to MBUS</td> - </tr> - <tr> - <td><p id="vds_communication_sendreplylatency">vds.communication.sendreplylatency</p></td> - <td>millisecond</td> - <td>Average ms used to send replies to MBUS</td> - </tr> - <tr> - <td><p id="vds_communication_toolittlememory">vds.communication.toolittlememory</p></td> - <td>operation</td> - <td>Number of messages failed due to too little memory available</td> - </tr> - <tr> - <td><p id="vds_datastored_bucket_space_active_buckets">vds.datastored.bucket_space.active_buckets</p></td> - <td>bucket</td> - <td>Number of active buckets in the bucket space</td> - </tr> - <tr> - <td><p id="vds_datastored_bucket_space_bucket_db_memory_usage_allocated_bytes">vds.datastored.bucket_space.bucket_db.memory_usage.allocated_bytes</p></td> - <td>byte</td> - <td>The number of allocated bytes</td> - </tr> - <tr> - <td><p id="vds_datastored_bucket_space_bucket_db_memory_usage_dead_bytes">vds.datastored.bucket_space.bucket_db.memory_usage.dead_bytes</p></td> - <td>byte</td> - <td>The number of dead bytes (<= used_bytes)</td> - </tr> - <tr> - <td><p id="vds_datastored_bucket_space_bucket_db_memory_usage_onhold_bytes">vds.datastored.bucket_space.bucket_db.memory_usage.onhold_bytes</p></td> - <td>byte</td> - <td>The number of bytes on hold</td> - </tr> - <tr> - <td><p id="vds_datastored_bucket_space_bucket_db_memory_usage_used_bytes">vds.datastored.bucket_space.bucket_db.memory_usage.used_bytes</p></td> - <td>byte</td> - <td>The number of used bytes (<= allocated_bytes)</td> - </tr> - <tr> - <td><p id="vds_datastored_bucket_space_buckets_total">vds.datastored.bucket_space.buckets_total</p></td> - <td>bucket</td> - <td>Total number buckets present in the bucket space (ready + not ready)</td> - </tr> - <tr> - <td><p id="vds_datastored_bucket_space_entries">vds.datastored.bucket_space.entries</p></td> - <td>document</td> - <td>Number of entries (documents + tombstones) stored in the bucket space</td> - </tr> - <tr> - <td><p id="vds_datastored_bucket_space_bytes">vds.datastored.bucket_space.bytes</p></td> - <td>byte</td> - <td>Bytes stored across all documents in the bucket space</td> - </tr> - <tr> - <td><p id="vds_datastored_bucket_space_docs">vds.datastored.bucket_space.docs</p></td> - <td>document</td> - <td>Documents stored in the bucket space</td> - </tr> - <tr> - <td><p id="vds_datastored_bucket_space_ready_buckets">vds.datastored.bucket_space.ready_buckets</p></td> - <td>bucket</td> - <td>Number of ready buckets in the bucket space</td> - </tr> - <tr> - <td><p id="vds_datastored_fullbucketinfolatency">vds.datastored.fullbucketinfolatency</p></td> - <td>millisecond</td> - <td>Amount of time spent to process a full bucket info request</td> - </tr> - <tr> - <td><p id="vds_datastored_fullbucketinforeqsize">vds.datastored.fullbucketinforeqsize</p></td> - <td>node</td> - <td>Amount of distributors answered at once in full bucket info requests.</td> - </tr> - <tr> - <td><p id="vds_datastored_simplebucketinforeqsize">vds.datastored.simplebucketinforeqsize</p></td> - <td>bucket</td> - <td>Amount of buckets returned in simple bucket info requests</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_applybucketdiff_count">vds.filestor.allthreads.applybucketdiff.count</p></td> - <td>request</td> - <td>Number of requests processed.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_applybucketdiff_failed">vds.filestor.allthreads.applybucketdiff.failed</p></td> - <td>request</td> - <td>Number of failed requests.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_applybucketdiff_latency">vds.filestor.allthreads.applybucketdiff.latency</p></td> - <td>millisecond</td> - <td>Latency of successful requests.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_applybucketdiffreply">vds.filestor.allthreads.applybucketdiffreply</p></td> - <td>request</td> - <td>Number of applybucketdiff replies that have been processed.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_bucketfixed">vds.filestor.allthreads.bucketfixed</p></td> - <td>bucket</td> - <td>Number of times bucket has been fixed because of corruption</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_bucketverified_count">vds.filestor.allthreads.bucketverified.count</p></td> - <td>request</td> - <td>Number of requests processed.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_bucketverified_failed">vds.filestor.allthreads.bucketverified.failed</p></td> - <td>request</td> - <td>Number of failed requests.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_bucketverified_latency">vds.filestor.allthreads.bucketverified.latency</p></td> - <td>request</td> - <td>Latency of successful requests.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_bytesmerged">vds.filestor.allthreads.bytesmerged</p></td> - <td>byte</td> - <td>Total number of bytes merged into this node.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_createbuckets_count">vds.filestor.allthreads.createbuckets.count</p></td> - <td>request</td> - <td>Number of requests processed.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_createbuckets_failed">vds.filestor.allthreads.createbuckets.failed</p></td> - <td>request</td> - <td>Number of failed requests.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_createbuckets_latency">vds.filestor.allthreads.createbuckets.latency</p></td> - <td>request</td> - <td>Latency of successful requests.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_failedoperations">vds.filestor.allthreads.failedoperations</p></td> - <td>operation</td> - <td>Number of operations throwing exceptions.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_getbucketdiff_count">vds.filestor.allthreads.getbucketdiff.count</p></td> - <td>request</td> - <td>Number of requests processed.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_getbucketdiff_failed">vds.filestor.allthreads.getbucketdiff.failed</p></td> - <td>request</td> - <td>Number of failed requests.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_getbucketdiff_latency">vds.filestor.allthreads.getbucketdiff.latency</p></td> - <td>request</td> - <td>Latency of successful requests.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_getbucketdiffreply">vds.filestor.allthreads.getbucketdiffreply</p></td> - <td>request</td> - <td>Number of getbucketdiff replies that have been processed.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_internaljoin_count">vds.filestor.allthreads.internaljoin.count</p></td> - <td>request</td> - <td>Number of requests processed.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_internaljoin_failed">vds.filestor.allthreads.internaljoin.failed</p></td> - <td>request</td> - <td>Number of failed requests.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_internaljoin_latency">vds.filestor.allthreads.internaljoin.latency</p></td> - <td>millisecond</td> - <td>Latency of successful requests.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_movedbuckets_count">vds.filestor.allthreads.movedbuckets.count</p></td> - <td>request</td> - <td>Number of requests processed.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_movedbuckets_failed">vds.filestor.allthreads.movedbuckets.failed</p></td> - <td>request</td> - <td>Number of failed requests.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_movedbuckets_latency">vds.filestor.allthreads.movedbuckets.latency</p></td> - <td>millisecond</td> - <td>Latency of successful requests.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_operations">vds.filestor.allthreads.operations</p></td> - <td>operation</td> - <td>Number of operations processed.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_readbucketinfo_count">vds.filestor.allthreads.readbucketinfo.count</p></td> - <td>request</td> - <td>Number of requests processed.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_readbucketinfo_failed">vds.filestor.allthreads.readbucketinfo.failed</p></td> - <td>request</td> - <td>Number of failed requests.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_readbucketinfo_latency">vds.filestor.allthreads.readbucketinfo.latency</p></td> - <td>request</td> - <td>Latency of successful requests.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_readbucketlist_count">vds.filestor.allthreads.readbucketlist.count</p></td> - <td>request</td> - <td>Number of requests processed.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_readbucketlist_failed">vds.filestor.allthreads.readbucketlist.failed</p></td> - <td>request</td> - <td>Number of failed requests.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_readbucketlist_latency">vds.filestor.allthreads.readbucketlist.latency</p></td> - <td>millisecond</td> - <td>Latency of successful requests.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_recheckbucketinfo_count">vds.filestor.allthreads.recheckbucketinfo.count</p></td> - <td>request</td> - <td>Number of requests processed.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_recheckbucketinfo_failed">vds.filestor.allthreads.recheckbucketinfo.failed</p></td> - <td>request</td> - <td>Number of failed requests.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_recheckbucketinfo_latency">vds.filestor.allthreads.recheckbucketinfo.latency</p></td> - <td>millisecond</td> - <td>Latency of successful requests.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_revert_count">vds.filestor.allthreads.revert.count</p></td> - <td>request</td> - <td>Number of requests processed.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_revert_failed">vds.filestor.allthreads.revert.failed</p></td> - <td>request</td> - <td>Number of failed requests.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_revert_latency">vds.filestor.allthreads.revert.latency</p></td> - <td>millisecond</td> - <td>Latency of successful requests.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_revert_not_found">vds.filestor.allthreads.revert.not_found</p></td> - <td>request</td> - <td>Number of requests that could not be completed due to source document not found.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_stat_bucket_count">vds.filestor.allthreads.stat_bucket.count</p></td> - <td>request</td> - <td>Number of requests processed.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_stat_bucket_failed">vds.filestor.allthreads.stat_bucket.failed</p></td> - <td>request</td> - <td>Number of failed requests.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_stat_bucket_latency">vds.filestor.allthreads.stat_bucket.latency</p></td> - <td>request</td> - <td>Latency of successful requests.</td> - </tr> - <tr> - <td><p id="vds_filestor_bucket_db_init_latency">vds.filestor.bucket_db_init_latency</p></td> - <td>millisecond</td> - <td>Time taken (in ms) to initialize bucket databases with information from the persistence provider</td> - </tr> - <tr> - <td><p id="vds_filestor_directoryevents">vds.filestor.directoryevents</p></td> - <td>operation</td> - <td>Number of directory events received.</td> - </tr> - <tr> - <td><p id="vds_filestor_diskevents">vds.filestor.diskevents</p></td> - <td>operation</td> - <td>Number of disk events received.</td> - </tr> - <tr> - <td><p id="vds_filestor_partitionevents">vds.filestor.partitionevents</p></td> - <td>operation</td> - <td>Number of partition events received.</td> - </tr> - <tr> - <td><p id="vds_filestor_pendingmerge">vds.filestor.pendingmerge</p></td> - <td>bucket</td> - <td>Number of buckets currently being merged.</td> - </tr> - <tr> - <td><p id="vds_filestor_waitingforlockrate">vds.filestor.waitingforlockrate</p></td> - <td>operation</td> - <td>Amount of times a filestor thread has needed to wait for lock to take next message in queue.</td> - </tr> - <tr> - <td><p id="vds_mergethrottler_mergechains_failures_aborted">vds.mergethrottler.mergechains.failures.aborted</p></td> - <td>operation</td> - <td>The number of merges that failed because the storage node was (most likely) shutting down</td> - </tr> - <tr> - <td><p id="vds_mergethrottler_mergechains_failures_bucketnotfound">vds.mergethrottler.mergechains.failures.bucketnotfound</p></td> - <td>operation</td> - <td>The number of operations that failed because the bucket did not exist</td> - </tr> - <tr> - <td><p id="vds_server_memoryusage">vds.server.memoryusage</p></td> - <td>byte</td> - <td>Amount of memory used by the storage subsystem</td> - </tr> - <tr> - <td><p id="vds_server_memoryusage_visiting">vds.server.memoryusage_visiting</p></td> - <td>byte</td> - <td>Message use from visiting</td> - </tr> - <tr> - <td><p id="vds_server_message_memory_use_highpri">vds.server.message_memory_use.highpri</p></td> - <td>byte</td> - <td>Message use from high priority storage messages</td> - </tr> - <tr> - <td><p id="vds_server_message_memory_use_lowpri">vds.server.message_memory_use.lowpri</p></td> - <td>byte</td> - <td>Message use from low priority storage messages</td> - </tr> - <tr> - <td><p id="vds_server_message_memory_use_normalpri">vds.server.message_memory_use.normalpri</p></td> - <td>byte</td> - <td>Message use from normal priority storage messages</td> - </tr> - <tr> - <td><p id="vds_server_message_memory_use_total">vds.server.message_memory_use.total</p></td> - <td>byte</td> - <td>Message use from storage messages</td> - </tr> - <tr> - <td><p id="vds_server_message_memory_use_veryhighpri">vds.server.message_memory_use.veryhighpri</p></td> - <td>byte</td> - <td>Message use from very high priority storage messages</td> - </tr> - <tr> - <td><p id="vds_state_manager_invoke_state_listeners_latency">vds.state_manager.invoke_state_listeners_latency</p></td> - <td>millisecond</td> - <td>Time spent (in ms) propagating state changes to internal state listeners</td> - </tr> - <tr> - <td><p id="vds_visitor_cv_queueevictedwaittime">vds.visitor.cv_queueevictedwaittime</p></td> - <td>millisecond</td> - <td>Milliseconds waiting in create visitor queue, for visitors that was evicted from queue due to higher priority visitors coming</td> - </tr> - <tr> - <td><p id="vds_visitor_cv_queuefull">vds.visitor.cv_queuefull</p></td> - <td>operation</td> - <td>Number of create visitor messages failed as queue is full</td> - </tr> - <tr> - <td><p id="vds_visitor_cv_queuesize">vds.visitor.cv_queuesize</p></td> - <td>item</td> - <td>Size of create visitor queue</td> - </tr> - <tr> - <td><p id="vds_visitor_cv_queuetimeoutwaittime">vds.visitor.cv_queuetimeoutwaittime</p></td> - <td>millisecond</td> - <td>Milliseconds waiting in create visitor queue, for visitors that timed out while in the visitor queue</td> - </tr> - <tr> - <td><p id="vds_visitor_cv_queuewaittime">vds.visitor.cv_queuewaittime</p></td> - <td>millisecond</td> - <td>Milliseconds waiting in create visitor queue, for visitors that was added to visitor queue but scheduled later</td> - </tr> - <tr> - <td><p id="vds_visitor_cv_skipqueue">vds.visitor.cv_skipqueue</p></td> - <td>operation</td> - <td>Number of times we could skip queue as we had free visitor spots</td> - </tr> - <tr> - <td><p id="vds_server_network_rpc-capability-checks-failed">vds.server.network.rpc-capability-checks-failed</p></td> - <td>failure</td> - <td>Number of RPC operations that failed due to one or more missing capabilities</td> - </tr> - <tr> - <td><p id="vds_server_network_status-capability-checks-failed">vds.server.network.status-capability-checks-failed</p></td> - <td>failure</td> - <td>Number of status page operations that failed due to one or more missing capabilities</td> - </tr> - <tr> - <td><p id="vds_server_fnet_num-connections">vds.server.fnet.num-connections</p></td> - <td>connection</td> - <td>Total number of connection objects</td> - </tr> - </tbody> -</table> diff --git a/en/reference/operations/metrics/storage.mdx b/en/reference/operations/metrics/storage.mdx new file mode 100644 index 0000000000..40e96ade97 --- /dev/null +++ b/en/reference/operations/metrics/storage.mdx @@ -0,0 +1,1056 @@ +--- +title: "Storage Metrics" +sidebarTitle: "Storage metrics" +--- + +<table className="vespa-table"> + <thead> + <tr> + <th>Name</th> + <th>Unit</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>vds.datastored.alldisks.buckets</td> + <td>bucket</td> + <td>Number of buckets managed</td> + </tr> + <tr> + <td>vds.datastored.alldisks.docs</td> + <td>document</td> + <td>Number of documents stored</td> + </tr> + <tr> + <td>vds.datastored.alldisks.bytes</td> + <td>byte</td> + <td>Number of bytes stored</td> + </tr> + <tr> + <td>vds.datastored.alldisks.activebuckets</td> + <td>bucket</td> + <td>Number of active buckets on the node</td> + </tr> + <tr> + <td>vds.datastored.alldisks.readybuckets</td> + <td>bucket</td> + <td>Number of ready buckets on the node</td> + </tr> + <tr> + <td>vds.visitor.allthreads.averagevisitorlifetime</td> + <td>millisecond</td> + <td>Average lifetime of a visitor</td> + </tr> + <tr> + <td>vds.visitor.allthreads.averagequeuewait</td> + <td>millisecond</td> + <td>Average time an operation spends in input queue.</td> + </tr> + <tr> + <td>vds.visitor.allthreads.queuesize</td> + <td>operation</td> + <td>Size of input message queue.</td> + </tr> + <tr> + <td>vds.visitor.allthreads.completed</td> + <td>operation</td> + <td>Number of visitors completed</td> + </tr> + <tr> + <td>vds.visitor.allthreads.created</td> + <td>operation</td> + <td>Number of visitors created.</td> + </tr> + <tr> + <td>vds.visitor.allthreads.failed</td> + <td>operation</td> + <td>Number of visitors failed</td> + </tr> + <tr> + <td>vds.visitor.allthreads.averagemessagesendtime</td> + <td>millisecond</td> + <td>Average time it takes for messages to be sent to their target (and be replied to)</td> + </tr> + <tr> + <td>vds.visitor.allthreads.averageprocessingtime</td> + <td>millisecond</td> + <td>Average time used to process visitor requests</td> + </tr> + <tr> + <td>vds.visitor.allthreads.aborted</td> + <td>instance</td> + <td>Number of visitors aborted.</td> + </tr> + <tr> + <td>vds.visitor.allthreads.averagevisitorcreationtime</td> + <td>millisecond</td> + <td>Average time spent creating a visitor instance</td> + </tr> + <tr> + <td>vds.visitor.allthreads.destination_failure_replies</td> + <td>instance</td> + <td>Number of failure replies received from the visitor destination</td> + </tr> + <tr> + <td>vds.filestor.queuesize</td> + <td>operation</td> + <td>Size of input message queue.</td> + </tr> + <tr> + <td>vds.filestor.averagequeuewait</td> + <td>millisecond</td> + <td>Average time an operation spends in input queue.</td> + </tr> + <tr> + <td>vds.filestor.active_operations.size</td> + <td>operation</td> + <td>Number of concurrent active operations</td> + </tr> + <tr> + <td>vds.filestor.active_operations.latency</td> + <td>millisecond</td> + <td>Latency (in ms) for completed operations</td> + </tr> + <tr> + <td>vds.filestor.throttle_window_size</td> + <td>operation</td> + <td>Current size of async operation throttler window size</td> + </tr> + <tr> + <td>vds.filestor.throttle_waiting_threads</td> + <td>thread</td> + <td>Number of threads waiting to acquire a throttle token</td> + </tr> + <tr> + <td>vds.filestor.throttle_active_tokens</td> + <td>instance</td> + <td>Current number of active throttle tokens</td> + </tr> + <tr> + <td>vds.filestor.allthreads.mergemetadatareadlatency</td> + <td>millisecond</td> + <td>Time spent in a merge step to check metadata of current node to see what data it has.</td> + </tr> + <tr> + <td>vds.filestor.allthreads.mergedatareadlatency</td> + <td>millisecond</td> + <td>Time spent in a merge step to read data other nodes need.</td> + </tr> + <tr> + <td>vds.filestor.allthreads.mergedatawritelatency</td> + <td>millisecond</td> + <td>Time spent in a merge step to write data needed to current node.</td> + </tr> + <tr> + <td>vds.filestor.allthreads.mergeavgdatareceivedneeded</td> + <td>byte</td> + <td>Amount of data transferred from previous node in chain that we needed to apply locally.</td> + </tr> + <tr> + <td>vds.filestor.allthreads.mergebuckets.count</td> + <td>request</td> + <td>Number of requests processed.</td> + </tr> + <tr> + <td>vds.filestor.allthreads.mergebuckets.failed</td> + <td>request</td> + <td>Number of failed requests.</td> + </tr> + <tr> + <td>vds.filestor.allthreads.mergebuckets.latency</td> + <td>millisecond</td> + <td>Latency of successful requests.</td> + </tr> + <tr> + <td>vds.filestor.allthreads.mergelatencytotal</td> + <td>millisecond</td> + <td>Latency of total merge operation, from master node receives it, until merge is complete and master node replies.</td> + </tr> + <tr> + <td>vds.filestor.allthreads.merge_put_latency</td> + <td>millisecond</td> + <td>Latency of individual puts that are part of merge operations</td> + </tr> + <tr> + <td>vds.filestor.allthreads.merge_remove_latency</td> + <td>millisecond</td> + <td>Latency of individual removes that are part of merge operations</td> + </tr> + <tr> + <td>vds.filestor.allstripes.throttled_rpc_direct_dispatches</td> + <td>instance</td> + <td>Number of times an RPC thread could not directly dispatch an async operation directly to Proton because it was disallowed by the throttle policy</td> + </tr> + <tr> + <td>vds.filestor.allstripes.throttled_persistence_thread_polls</td> + <td>instance</td> + <td>Number of times a persistence thread could not immediately dispatch a queued async operation because it was disallowed by the throttle policy</td> + </tr> + <tr> + <td>vds.filestor.allstripes.timeouts_waiting_for_throttle_token</td> + <td>instance</td> + <td>Number of times a persistence thread timed out waiting for an available throttle policy token</td> + </tr> + <tr> + <td>vds.filestor.allstripes.averagequeuewait</td> + <td>millisecond</td> + <td>Average time an operation spends in input queue.</td> + </tr> + <tr> + <td>vds.filestor.allthreads.put.count</td> + <td>operation</td> + <td>Number of requests processed.</td> + </tr> + <tr> + <td>vds.filestor.allthreads.put.failed</td> + <td>operation</td> + <td>Number of failed requests.</td> + </tr> + <tr> + <td>vds.filestor.allthreads.put.test_and_set_failed</td> + <td>operation</td> + <td>Number of operations that were skipped due to a test-and-set condition not met</td> + </tr> + <tr> + <td>vds.filestor.allthreads.put.latency</td> + <td>millisecond</td> + <td>Latency of successful requests.</td> + </tr> + <tr> + <td>vds.filestor.allthreads.put.request_size</td> + <td>byte</td> + <td>Size of requests, in bytes</td> + </tr> + <tr> + <td>vds.filestor.allthreads.remove.count</td> + <td>operation</td> + <td>Number of requests processed.</td> + </tr> + <tr> + <td>vds.filestor.allthreads.remove.failed</td> + <td>operation</td> + <td>Number of failed requests.</td> + </tr> + <tr> + <td>vds.filestor.allthreads.remove.test_and_set_failed</td> + <td>operation</td> + <td>Number of operations that were skipped due to a test-and-set condition not met</td> + </tr> + <tr> + <td>vds.filestor.allthreads.remove.latency</td> + <td>millisecond</td> + <td>Latency of successful requests.</td> + </tr> + <tr> + <td>vds.filestor.allthreads.remove.request_size</td> + <td>byte</td> + <td>Size of requests, in bytes</td> + </tr> + <tr> + <td>vds.filestor.allthreads.remove.not_found</td> + <td>request</td> + <td>Number of requests that could not be completed due to source document not found.</td> + </tr> + <tr> + <td>vds.filestor.allthreads.get.count</td> + <td>operation</td> + <td>Number of requests processed.</td> + </tr> + <tr> + <td>vds.filestor.allthreads.get.failed</td> + <td>operation</td> + <td>Number of failed requests.</td> + </tr> + <tr> + <td>vds.filestor.allthreads.get.latency</td> + <td>millisecond</td> + <td>Latency of successful requests.</td> + </tr> + <tr> + <td>vds.filestor.allthreads.get.request_size</td> + <td>byte</td> + <td>Size of requests, in bytes</td> + </tr> + <tr> + <td>vds.filestor.allthreads.get.not_found</td> + <td>request</td> + <td>Number of requests that could not be completed due to source document not found.</td> + </tr> + <tr> + <td>vds.filestor.allthreads.update.count</td> + <td>request</td> + <td>Number of requests processed.</td> + </tr> + <tr> + <td>vds.filestor.allthreads.update.failed</td> + <td>request</td> + <td>Number of failed requests.</td> + </tr> + <tr> + <td>vds.filestor.allthreads.update.test_and_set_failed</td> + <td>request</td> + <td>Number of requests that were skipped due to a test-and-set condition not met</td> + </tr> + <tr> + <td>vds.filestor.allthreads.update.latency</td> + <td>millisecond</td> + <td>Latency of successful requests.</td> + </tr> + <tr> + <td>vds.filestor.allthreads.update.request_size</td> + <td>byte</td> + <td>Size of requests, in bytes</td> + </tr> + <tr> + <td>vds.filestor.allthreads.update.latency_read</td> + <td>millisecond</td> + <td>Latency of the source read in the request.</td> + </tr> + <tr> + <td>vds.filestor.allthreads.update.not_found</td> + <td>request</td> + <td>Number of requests that could not be completed due to source document not found.</td> + </tr> + <tr> + <td>vds.filestor.allthreads.createiterator.count</td> + <td>request</td> + <td>Number of requests processed.</td> + </tr> + <tr> + <td>vds.filestor.allthreads.createiterator.latency</td> + <td>millisecond</td> + <td>Latency of successful requests.</td> + </tr> + <tr> + <td>vds.filestor.allthreads.createiterator.failed</td> + <td>request</td> + <td>Number of failed requests.</td> + </tr> + <tr> + <td>vds.filestor.allthreads.visit.count</td> + <td>request</td> + <td>Number of requests processed.</td> + </tr> + <tr> + <td>vds.filestor.allthreads.visit.latency</td> + <td>millisecond</td> + <td>Latency of successful requests.</td> + </tr> + <tr> + <td>vds.filestor.allthreads.visit.docs</td> + <td>document</td> + <td>Number of entries read per iterate call</td> + </tr> + <tr> + <td>vds.filestor.allthreads.visit.failed</td> + <td>request</td> + <td>Number of failed requests.</td> + </tr> + <tr> + <td>vds.filestor.allthreads.remove_location.count</td> + <td>request</td> + <td>Number of requests processed.</td> + </tr> + <tr> + <td>vds.filestor.allthreads.remove_location.latency</td> + <td>millisecond</td> + <td>Latency of successful requests.</td> + </tr> + <tr> + <td>vds.filestor.allthreads.remove_location.failed</td> + <td>request</td> + <td>Number of failed requests.</td> + </tr> + <tr> + <td>vds.filestor.allthreads.splitbuckets.count</td> + <td>request</td> + <td>Number of requests processed.</td> + </tr> + <tr> + <td>vds.filestor.allthreads.splitbuckets.failed</td> + <td>request</td> + <td>Number of failed requests.</td> + </tr> + <tr> + <td>vds.filestor.allthreads.splitbuckets.latency</td> + <td>request</td> + <td>Latency of successful requests.</td> + </tr> + <tr> + <td>vds.filestor.allthreads.joinbuckets.count</td> + <td>request</td> + <td>Number of requests processed.</td> + </tr> + <tr> + <td>vds.filestor.allthreads.joinbuckets.failed</td> + <td>request</td> + <td>Number of failed requests.</td> + </tr> + <tr> + <td>vds.filestor.allthreads.joinbuckets.latency</td> + <td>millisecond</td> + <td>Latency of successful requests.</td> + </tr> + <tr> + <td>vds.filestor.allthreads.deletebuckets.count</td> + <td>request</td> + <td>Number of requests processed.</td> + </tr> + <tr> + <td>vds.filestor.allthreads.deletebuckets.failed</td> + <td>request</td> + <td>Number of failed requests.</td> + </tr> + <tr> + <td>vds.filestor.allthreads.deletebuckets.latency</td> + <td>millisecond</td> + <td>Latency of successful requests.</td> + </tr> + <tr> + <td>vds.filestor.allthreads.remove_by_gid.count</td> + <td>request</td> + <td>Number of requests processed.</td> + </tr> + <tr> + <td>vds.filestor.allthreads.remove_by_gid.failed</td> + <td>request</td> + <td>Number of failed requests.</td> + </tr> + <tr> + <td>vds.filestor.allthreads.remove_by_gid.latency</td> + <td>millisecond</td> + <td>Latency of successful requests.</td> + </tr> + <tr> + <td>vds.filestor.allthreads.setbucketstates.count</td> + <td>request</td> + <td>Number of requests processed.</td> + </tr> + <tr> + <td>vds.filestor.allthreads.setbucketstates.failed</td> + <td>request</td> + <td>Number of failed requests.</td> + </tr> + <tr> + <td>vds.filestor.allthreads.setbucketstates.latency</td> + <td>millisecond</td> + <td>Latency of successful requests.</td> + </tr> + <tr> + <td>vds.mergethrottler.averagequeuewaitingtime</td> + <td>millisecond</td> + <td>Time merges spent in the throttler queue</td> + </tr> + <tr> + <td>vds.mergethrottler.queuesize</td> + <td>instance</td> + <td>Length of merge queue</td> + </tr> + <tr> + <td>vds.mergethrottler.active_window_size</td> + <td>instance</td> + <td>Number of merges active within the pending window size</td> + </tr> + <tr> + <td>vds.mergethrottler.estimated_merge_memory_usage</td> + <td>byte</td> + <td>An estimated upper bound of the memory usage (in bytes) of the merges currently in the active window</td> + </tr> + <tr> + <td>vds.mergethrottler.bounced_due_to_back_pressure</td> + <td>instance</td> + <td>Number of merges bounced due to resource exhaustion back-pressure</td> + </tr> + <tr> + <td>vds.mergethrottler.locallyexecutedmerges.ok</td> + <td>instance</td> + <td>The number of successful merges for 'locallyexecutedmerges'</td> + </tr> + <tr> + <td>vds.mergethrottler.locallyexecutedmerges.failures.aborted</td> + <td>operation</td> + <td>The number of merges that failed because the storage node was (most likely) shutting down</td> + </tr> + <tr> + <td>vds.mergethrottler.locallyexecutedmerges.failures.bucketnotfound</td> + <td>operation</td> + <td>The number of operations that failed because the bucket did not exist</td> + </tr> + <tr> + <td>vds.mergethrottler.locallyexecutedmerges.failures.busy</td> + <td>operation</td> + <td>The number of merges that failed because the storage node was busy</td> + </tr> + <tr> + <td>vds.mergethrottler.locallyexecutedmerges.failures.exists</td> + <td>operation</td> + <td>The number of merges that were rejected due to a merge operation for their bucket already being processed</td> + </tr> + <tr> + <td>vds.mergethrottler.locallyexecutedmerges.failures.notready</td> + <td>operation</td> + <td>The number of merges discarded because distributor was not ready</td> + </tr> + <tr> + <td>vds.mergethrottler.locallyexecutedmerges.failures.other</td> + <td>operation</td> + <td>The number of other failures</td> + </tr> + <tr> + <td>vds.mergethrottler.locallyexecutedmerges.failures.rejected</td> + <td>operation</td> + <td>The number of merges that were rejected</td> + </tr> + <tr> + <td>vds.mergethrottler.locallyexecutedmerges.failures.timeout</td> + <td>operation</td> + <td>The number of merges that failed because they timed out towards storage</td> + </tr> + <tr> + <td>vds.mergethrottler.locallyexecutedmerges.failures.total</td> + <td>operation</td> + <td>Sum of all failures</td> + </tr> + <tr> + <td>vds.mergethrottler.locallyexecutedmerges.failures.wrongdistribution</td> + <td>operation</td> + <td>The number of merges that were discarded (flushed) because they were initiated at an older cluster state than the current</td> + </tr> + <tr> + <td>vds.mergethrottler.mergechains.ok</td> + <td>operation</td> + <td>The number of successful merges for 'mergechains'</td> + </tr> + <tr> + <td>vds.mergethrottler.mergechains.failures.busy</td> + <td>operation</td> + <td>The number of merges that failed because the storage node was busy</td> + </tr> + <tr> + <td>vds.mergethrottler.mergechains.failures.total</td> + <td>operation</td> + <td>Sum of all failures</td> + </tr> + <tr> + <td>vds.mergethrottler.mergechains.failures.exists</td> + <td>operation</td> + <td>The number of merges that were rejected due to a merge operation for their bucket already being processed</td> + </tr> + <tr> + <td>vds.mergethrottler.mergechains.failures.notready</td> + <td>operation</td> + <td>The number of merges discarded because distributor was not ready</td> + </tr> + <tr> + <td>vds.mergethrottler.mergechains.failures.other</td> + <td>operation</td> + <td>The number of other failures</td> + </tr> + <tr> + <td>vds.mergethrottler.mergechains.failures.rejected</td> + <td>operation</td> + <td>The number of merges that were rejected</td> + </tr> + <tr> + <td>vds.mergethrottler.mergechains.failures.timeout</td> + <td>operation</td> + <td>The number of merges that failed because they timed out towards storage</td> + </tr> + <tr> + <td>vds.mergethrottler.mergechains.failures.wrongdistribution</td> + <td>operation</td> + <td>The number of merges that were discarded (flushed) because they were initiated at an older cluster state than the current</td> + </tr> + <tr> + <td>vds.server.network.tls-handshakes-failed</td> + <td>operation</td> + <td>Number of client or server connection attempts that failed during TLS handshaking</td> + </tr> + <tr> + <td>vds.server.network.peer-authorization-failures</td> + <td>failure</td> + <td>Number of TLS connection attempts failed due to bad or missing peer certificate credentials</td> + </tr> + <tr> + <td>vds.server.network.client.tls-connections-established</td> + <td>connection</td> + <td>Number of secure mTLS connections established</td> + </tr> + <tr> + <td>vds.server.network.server.tls-connections-established</td> + <td>connection</td> + <td>Number of secure mTLS connections established</td> + </tr> + <tr> + <td>vds.server.network.client.insecure-connections-established</td> + <td>connection</td> + <td>Number of insecure (plaintext) connections established</td> + </tr> + <tr> + <td>vds.server.network.server.insecure-connections-established</td> + <td>connection</td> + <td>Number of insecure (plaintext) connections established</td> + </tr> + <tr> + <td>vds.server.network.tls-connections-broken</td> + <td>connection</td> + <td>Number of TLS connections broken due to failures during frame encoding or decoding</td> + </tr> + <tr> + <td>vds.server.network.failed-tls-config-reloads</td> + <td>failure</td> + <td>Number of times background reloading of TLS config has failed</td> + </tr> + <tr> + <td>vds.bouncer.unavailable_node_aborts</td> + <td>operation</td> + <td>Number of operations that were aborted due to the node (or target bucket space) being unavailable</td> + </tr> + <tr> + <td>vds.changedbucketownershiphandler.avg_abort_processing_time</td> + <td>millisecond</td> + <td>Average time spent aborting operations for changed buckets</td> + </tr> + <tr> + <td>vds.changedbucketownershiphandler.external_load_ops_aborted</td> + <td>operation</td> + <td>Number of outdated external load operations aborted</td> + </tr> + <tr> + <td>vds.changedbucketownershiphandler.ideal_state_ops_aborted</td> + <td>operation</td> + <td>Number of outdated ideal state operations aborted</td> + </tr> + <tr> + <td>vds.communication.bucket_space_mapping_failures</td> + <td>operation</td> + <td>Number of messages that could not be resolved to a known bucket space</td> + </tr> + <tr> + <td>vds.communication.convertfailures</td> + <td>operation</td> + <td>Number of messages that failed to get converted to storage API messages</td> + </tr> + <tr> + <td>vds.communication.exceptionmessageprocesstime</td> + <td>millisecond</td> + <td>Time transport thread uses to process a single message that fails with an exception thrown into communication manager</td> + </tr> + <tr> + <td>vds.communication.messageprocesstime</td> + <td>millisecond</td> + <td>Time transport thread uses to process a single message</td> + </tr> + <tr> + <td>vds.communication.messagequeue</td> + <td>item</td> + <td>Size of input message queue.</td> + </tr> + <tr> + <td>vds.communication.sendcommandlatency</td> + <td>millisecond</td> + <td>Average ms used to send commands to MBUS</td> + </tr> + <tr> + <td>vds.communication.sendreplylatency</td> + <td>millisecond</td> + <td>Average ms used to send replies to MBUS</td> + </tr> + <tr> + <td>vds.communication.toolittlememory</td> + <td>operation</td> + <td>Number of messages failed due to too little memory available</td> + </tr> + <tr> + <td>vds.datastored.bucket_space.active_buckets</td> + <td>bucket</td> + <td>Number of active buckets in the bucket space</td> + </tr> + <tr> + <td>vds.datastored.bucket_space.bucket_db.memory_usage.allocated_bytes</td> + <td>byte</td> + <td>The number of allocated bytes</td> + </tr> + <tr> + <td>vds.datastored.bucket_space.bucket_db.memory_usage.dead_bytes</td> + <td>byte</td> + <td>The number of dead bytes (<code>{`<=`}</code> used_bytes)</td> + </tr> + <tr> + <td>vds.datastored.bucket_space.bucket_db.memory_usage.onhold_bytes</td> + <td>byte</td> + <td>The number of bytes on hold</td> + </tr> + <tr> + <td>vds.datastored.bucket_space.bucket_db.memory_usage.used_bytes</td> + <td>byte</td> + <td>The number of used bytes (<code>{`<=`}</code> allocated_bytes)</td> + </tr> + <tr> + <td>vds.datastored.bucket_space.buckets_total</td> + <td>bucket</td> + <td>Total number buckets present in the bucket space (ready + not ready)</td> + </tr> + <tr> + <td>vds.datastored.bucket_space.entries</td> + <td>document</td> + <td>Number of entries (documents + tombstones) stored in the bucket space</td> + </tr> + <tr> + <td>vds.datastored.bucket_space.bytes</td> + <td>byte</td> + <td>Bytes stored across all documents in the bucket space</td> + </tr> + <tr> + <td>vds.datastored.bucket_space.docs</td> + <td>document</td> + <td>Documents stored in the bucket space</td> + </tr> + <tr> + <td>vds.datastored.bucket_space.ready_buckets</td> + <td>bucket</td> + <td>Number of ready buckets in the bucket space</td> + </tr> + <tr> + <td>vds.datastored.fullbucketinfolatency</td> + <td>millisecond</td> + <td>Amount of time spent to process a full bucket info request</td> + </tr> + <tr> + <td>vds.datastored.fullbucketinforeqsize</td> + <td>node</td> + <td>Amount of distributors answered at once in full bucket info requests.</td> + </tr> + <tr> + <td>vds.datastored.simplebucketinforeqsize</td> + <td>bucket</td> + <td>Amount of buckets returned in simple bucket info requests</td> + </tr> + <tr> + <td>vds.filestor.allthreads.applybucketdiff.count</td> + <td>request</td> + <td>Number of requests processed.</td> + </tr> + <tr> + <td>vds.filestor.allthreads.applybucketdiff.failed</td> + <td>request</td> + <td>Number of failed requests.</td> + </tr> + <tr> + <td>vds.filestor.allthreads.applybucketdiff.latency</td> + <td>millisecond</td> + <td>Latency of successful requests.</td> + </tr> + <tr> + <td>vds.filestor.allthreads.applybucketdiffreply</td> + <td>request</td> + <td>Number of applybucketdiff replies that have been processed.</td> + </tr> + <tr> + <td>vds.filestor.allthreads.bucketfixed</td> + <td>bucket</td> + <td>Number of times bucket has been fixed because of corruption</td> + </tr> + <tr> + <td>vds.filestor.allthreads.bucketverified.count</td> + <td>request</td> + <td>Number of requests processed.</td> + </tr> + <tr> + <td>vds.filestor.allthreads.bucketverified.failed</td> + <td>request</td> + <td>Number of failed requests.</td> + </tr> + <tr> + <td>vds.filestor.allthreads.bucketverified.latency</td> + <td>request</td> + <td>Latency of successful requests.</td> + </tr> + <tr> + <td>vds.filestor.allthreads.bytesmerged</td> + <td>byte</td> + <td>Total number of bytes merged into this node.</td> + </tr> + <tr> + <td>vds.filestor.allthreads.createbuckets.count</td> + <td>request</td> + <td>Number of requests processed.</td> + </tr> + <tr> + <td>vds.filestor.allthreads.createbuckets.failed</td> + <td>request</td> + <td>Number of failed requests.</td> + </tr> + <tr> + <td>vds.filestor.allthreads.createbuckets.latency</td> + <td>request</td> + <td>Latency of successful requests.</td> + </tr> + <tr> + <td>vds.filestor.allthreads.failedoperations</td> + <td>operation</td> + <td>Number of operations throwing exceptions.</td> + </tr> + <tr> + <td>vds.filestor.allthreads.getbucketdiff.count</td> + <td>request</td> + <td>Number of requests processed.</td> + </tr> + <tr> + <td>vds.filestor.allthreads.getbucketdiff.failed</td> + <td>request</td> + <td>Number of failed requests.</td> + </tr> + <tr> + <td>vds.filestor.allthreads.getbucketdiff.latency</td> + <td>request</td> + <td>Latency of successful requests.</td> + </tr> + <tr> + <td>vds.filestor.allthreads.getbucketdiffreply</td> + <td>request</td> + <td>Number of getbucketdiff replies that have been processed.</td> + </tr> + <tr> + <td>vds.filestor.allthreads.internaljoin.count</td> + <td>request</td> + <td>Number of requests processed.</td> + </tr> + <tr> + <td>vds.filestor.allthreads.internaljoin.failed</td> + <td>request</td> + <td>Number of failed requests.</td> + </tr> + <tr> + <td>vds.filestor.allthreads.internaljoin.latency</td> + <td>millisecond</td> + <td>Latency of successful requests.</td> + </tr> + <tr> + <td>vds.filestor.allthreads.movedbuckets.count</td> + <td>request</td> + <td>Number of requests processed.</td> + </tr> + <tr> + <td>vds.filestor.allthreads.movedbuckets.failed</td> + <td>request</td> + <td>Number of failed requests.</td> + </tr> + <tr> + <td>vds.filestor.allthreads.movedbuckets.latency</td> + <td>millisecond</td> + <td>Latency of successful requests.</td> + </tr> + <tr> + <td>vds.filestor.allthreads.operations</td> + <td>operation</td> + <td>Number of operations processed.</td> + </tr> + <tr> + <td>vds.filestor.allthreads.readbucketinfo.count</td> + <td>request</td> + <td>Number of requests processed.</td> + </tr> + <tr> + <td>vds.filestor.allthreads.readbucketinfo.failed</td> + <td>request</td> + <td>Number of failed requests.</td> + </tr> + <tr> + <td>vds.filestor.allthreads.readbucketinfo.latency</td> + <td>request</td> + <td>Latency of successful requests.</td> + </tr> + <tr> + <td>vds.filestor.allthreads.readbucketlist.count</td> + <td>request</td> + <td>Number of requests processed.</td> + </tr> + <tr> + <td>vds.filestor.allthreads.readbucketlist.failed</td> + <td>request</td> + <td>Number of failed requests.</td> + </tr> + <tr> + <td>vds.filestor.allthreads.readbucketlist.latency</td> + <td>millisecond</td> + <td>Latency of successful requests.</td> + </tr> + <tr> + <td>vds.filestor.allthreads.recheckbucketinfo.count</td> + <td>request</td> + <td>Number of requests processed.</td> + </tr> + <tr> + <td>vds.filestor.allthreads.recheckbucketinfo.failed</td> + <td>request</td> + <td>Number of failed requests.</td> + </tr> + <tr> + <td>vds.filestor.allthreads.recheckbucketinfo.latency</td> + <td>millisecond</td> + <td>Latency of successful requests.</td> + </tr> + <tr> + <td>vds.filestor.allthreads.revert.count</td> + <td>request</td> + <td>Number of requests processed.</td> + </tr> + <tr> + <td>vds.filestor.allthreads.revert.failed</td> + <td>request</td> + <td>Number of failed requests.</td> + </tr> + <tr> + <td>vds.filestor.allthreads.revert.latency</td> + <td>millisecond</td> + <td>Latency of successful requests.</td> + </tr> + <tr> + <td>vds.filestor.allthreads.revert.not_found</td> + <td>request</td> + <td>Number of requests that could not be completed due to source document not found.</td> + </tr> + <tr> + <td>vds.filestor.allthreads.stat_bucket.count</td> + <td>request</td> + <td>Number of requests processed.</td> + </tr> + <tr> + <td>vds.filestor.allthreads.stat_bucket.failed</td> + <td>request</td> + <td>Number of failed requests.</td> + </tr> + <tr> + <td>vds.filestor.allthreads.stat_bucket.latency</td> + <td>request</td> + <td>Latency of successful requests.</td> + </tr> + <tr> + <td>vds.filestor.bucket_db_init_latency</td> + <td>millisecond</td> + <td>Time taken (in ms) to initialize bucket databases with information from the persistence provider</td> + </tr> + <tr> + <td>vds.filestor.directoryevents</td> + <td>operation</td> + <td>Number of directory events received.</td> + </tr> + <tr> + <td>vds.filestor.diskevents</td> + <td>operation</td> + <td>Number of disk events received.</td> + </tr> + <tr> + <td>vds.filestor.partitionevents</td> + <td>operation</td> + <td>Number of partition events received.</td> + </tr> + <tr> + <td>vds.filestor.pendingmerge</td> + <td>bucket</td> + <td>Number of buckets currently being merged.</td> + </tr> + <tr> + <td>vds.filestor.waitingforlockrate</td> + <td>operation</td> + <td>Amount of times a filestor thread has needed to wait for lock to take next message in queue.</td> + </tr> + <tr> + <td>vds.mergethrottler.mergechains.failures.aborted</td> + <td>operation</td> + <td>The number of merges that failed because the storage node was (most likely) shutting down</td> + </tr> + <tr> + <td>vds.mergethrottler.mergechains.failures.bucketnotfound</td> + <td>operation</td> + <td>The number of operations that failed because the bucket did not exist</td> + </tr> + <tr> + <td>vds.server.memoryusage</td> + <td>byte</td> + <td>Amount of memory used by the storage subsystem</td> + </tr> + <tr> + <td>vds.server.memoryusage_visiting</td> + <td>byte</td> + <td>Message use from visiting</td> + </tr> + <tr> + <td>vds.server.message_memory_use.highpri</td> + <td>byte</td> + <td>Message use from high priority storage messages</td> + </tr> + <tr> + <td>vds.server.message_memory_use.lowpri</td> + <td>byte</td> + <td>Message use from low priority storage messages</td> + </tr> + <tr> + <td>vds.server.message_memory_use.normalpri</td> + <td>byte</td> + <td>Message use from normal priority storage messages</td> + </tr> + <tr> + <td>vds.server.message_memory_use.total</td> + <td>byte</td> + <td>Message use from storage messages</td> + </tr> + <tr> + <td>vds.server.message_memory_use.veryhighpri</td> + <td>byte</td> + <td>Message use from very high priority storage messages</td> + </tr> + <tr> + <td>vds.state_manager.invoke_state_listeners_latency</td> + <td>millisecond</td> + <td>Time spent (in ms) propagating state changes to internal state listeners</td> + </tr> + <tr> + <td>vds.visitor.cv_queueevictedwaittime</td> + <td>millisecond</td> + <td>Milliseconds waiting in create visitor queue, for visitors that was evicted from queue due to higher priority visitors coming</td> + </tr> + <tr> + <td>vds.visitor.cv_queuefull</td> + <td>operation</td> + <td>Number of create visitor messages failed as queue is full</td> + </tr> + <tr> + <td>vds.visitor.cv_queuesize</td> + <td>item</td> + <td>Size of create visitor queue</td> + </tr> + <tr> + <td>vds.visitor.cv_queuetimeoutwaittime</td> + <td>millisecond</td> + <td>Milliseconds waiting in create visitor queue, for visitors that timed out while in the visitor queue</td> + </tr> + <tr> + <td>vds.visitor.cv_queuewaittime</td> + <td>millisecond</td> + <td>Milliseconds waiting in create visitor queue, for visitors that was added to visitor queue but scheduled later</td> + </tr> + <tr> + <td>vds.visitor.cv_skipqueue</td> + <td>operation</td> + <td>Number of times we could skip queue as we had free visitor spots</td> + </tr> + <tr> + <td>vds.server.network.rpc-capability-checks-failed</td> + <td>failure</td> + <td>Number of RPC operations that failed due to one or more missing capabilities</td> + </tr> + <tr> + <td>vds.server.network.status-capability-checks-failed</td> + <td>failure</td> + <td>Number of status page operations that failed due to one or more missing capabilities</td> + </tr> + <tr> + <td>vds.server.fnet.num-connections</td> + <td>connection</td> + <td>Total number of connection objects</td> + </tr> + </tbody> +</table> \ No newline at end of file diff --git a/en/reference/operations/metrics/vespa-metric-set.html b/en/reference/operations/metrics/vespa-metric-set.html deleted file mode 100644 index 6e3477ded2..0000000000 --- a/en/reference/operations/metrics/vespa-metric-set.html +++ /dev/null @@ -1,3236 +0,0 @@ ---- -# Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -# Note: This file is generated by -# https://github.com/vespa-engine/vespa/blob/master/metrics/src/main/java/ai/vespa/metrics/docs/MetricSetDocumentation.java -title: "Vespa Metric Set" -redirect_from: -- en/reference/vespa-set-metrics-reference.html ---- -<p> - This document provides reference documentation for the Vespa metric set, including suffixes present per metric. - If the suffix column contains "N/A" then the base name of the corresponding metric is used with no suffix. -</p> -<h2 id="clustercontroller-metrics">ClusterController Metrics</h2> -<table class="table"> - <thead> - <tr><th>Name</th><th>Unit</th><th>Suffixes</th><th>Description</th></tr> - </thead> - <tbody> - <tr> - <td><p id="cluster-controller_down_count">cluster-controller.down.count</p></td> - <td>node</td> - <td>last, max</td> - <td>Number of content nodes down</td> - </tr> - <tr> - <td><p id="cluster-controller_initializing_count">cluster-controller.initializing.count</p></td> - <td>node</td> - <td>last, max</td> - <td>Number of content nodes initializing</td> - </tr> - <tr> - <td><p id="cluster-controller_maintenance_count">cluster-controller.maintenance.count</p></td> - <td>node</td> - <td>last, max</td> - <td>Number of content nodes in maintenance</td> - </tr> - <tr> - <td><p id="cluster-controller_retired_count">cluster-controller.retired.count</p></td> - <td>node</td> - <td>last, max</td> - <td>Number of content nodes that are retired</td> - </tr> - <tr> - <td><p id="cluster-controller_stopping_count">cluster-controller.stopping.count</p></td> - <td>node</td> - <td>last</td> - <td>Number of content nodes currently stopping</td> - </tr> - <tr> - <td><p id="cluster-controller_up_count">cluster-controller.up.count</p></td> - <td>node</td> - <td>last, max</td> - <td>Number of content nodes up</td> - </tr> - <tr> - <td><p id="cluster-controller_nodes-not-converged">cluster-controller.nodes-not-converged</p></td> - <td>node</td> - <td>max</td> - <td>Number of nodes not converging to the latest cluster state version</td> - </tr> - <tr> - <td><p id="cluster-controller_stored-document-count">cluster-controller.stored-document-count</p></td> - <td>document</td> - <td>max</td> - <td>Total number of unique documents stored in the cluster</td> - </tr> - <tr> - <td><p id="cluster-controller_stored-document-bytes">cluster-controller.stored-document-bytes</p></td> - <td>byte</td> - <td>max</td> - <td>Combined byte size of all unique documents stored in the cluster (not including replication)</td> - </tr> - <tr> - <td><p id="cluster-controller_cluster-buckets-out-of-sync-ratio">cluster-controller.cluster-buckets-out-of-sync-ratio</p></td> - <td>fraction</td> - <td>max</td> - <td>Ratio of buckets in the cluster currently in need of syncing</td> - </tr> - <tr> - <td><p id="cluster-controller_busy-tick-time-ms">cluster-controller.busy-tick-time-ms</p></td> - <td>millisecond</td> - <td>count, last, max, sum</td> - <td>Time busy</td> - </tr> - <tr> - <td><p id="cluster-controller_idle-tick-time-ms">cluster-controller.idle-tick-time-ms</p></td> - <td>millisecond</td> - <td>count, last, max, sum</td> - <td>Time idle</td> - </tr> - <tr> - <td><p id="cluster-controller_work-ms">cluster-controller.work-ms</p></td> - <td>millisecond</td> - <td>count, last, sum</td> - <td>Time used for actual work</td> - </tr> - <tr> - <td><p id="cluster-controller_is-master">cluster-controller.is-master</p></td> - <td>binary</td> - <td>last, max</td> - <td>1 if this cluster controller is currently the master, or 0 if not</td> - </tr> - <tr> - <td><p id="cluster-controller_remote-task-queue_size">cluster-controller.remote-task-queue.size</p></td> - <td>operation</td> - <td>last</td> - <td>Number of remote tasks queued</td> - </tr> - <tr> - <td><p id="cluster-controller_resource_usage_nodes_above_limit">cluster-controller.resource_usage.nodes_above_limit</p></td> - <td>node</td> - <td>last, max</td> - <td>The number of content nodes above resource limit, blocking feed</td> - </tr> - <tr> - <td><p id="cluster-controller_resource_usage_max_memory_utilization">cluster-controller.resource_usage.max_memory_utilization</p></td> - <td>fraction</td> - <td>last, max</td> - <td>Current memory utilisation, for content node with the highest value</td> - </tr> - <tr> - <td><p id="cluster-controller_resource_usage_max_disk_utilization">cluster-controller.resource_usage.max_disk_utilization</p></td> - <td>fraction</td> - <td>last, max</td> - <td>Current disk space utilisation, for content node with the highest value</td> - </tr> - <tr> - <td><p id="cluster-controller_resource_usage_memory_limit">cluster-controller.resource_usage.memory_limit</p></td> - <td>fraction</td> - <td>last, max</td> - <td>Memory space limit as a fraction of available memory</td> - </tr> - <tr> - <td><p id="cluster-controller_resource_usage_disk_limit">cluster-controller.resource_usage.disk_limit</p></td> - <td>fraction</td> - <td>last, max</td> - <td>Disk space limit as a fraction of available disk space</td> - </tr> - <tr> - <td><p id="reindexing_progress">reindexing.progress</p></td> - <td>fraction</td> - <td>last, max</td> - <td>Re-indexing progress</td> - </tr> - </tbody> -</table> - -<h2 id="container-metrics">Container Metrics</h2> -<table class="table"> - <thead> - <tr><th>Name</th><th>Unit</th><th>Suffixes</th><th>Description</th></tr> - </thead> - <tbody> - <tr> - <td><p id="http_status_1xx">http.status.1xx</p></td> - <td>response</td> - <td>rate</td> - <td>Number of responses with a 1xx status</td> - </tr> - <tr> - <td><p id="http_status_2xx">http.status.2xx</p></td> - <td>response</td> - <td>rate</td> - <td>Number of responses with a 2xx status</td> - </tr> - <tr> - <td><p id="http_status_3xx">http.status.3xx</p></td> - <td>response</td> - <td>rate</td> - <td>Number of responses with a 3xx status</td> - </tr> - <tr> - <td><p id="http_status_4xx">http.status.4xx</p></td> - <td>response</td> - <td>rate</td> - <td>Number of responses with a 4xx status</td> - </tr> - <tr> - <td><p id="http_status_5xx">http.status.5xx</p></td> - <td>response</td> - <td>rate</td> - <td>Number of responses with a 5xx status</td> - </tr> - <tr> - <td><p id="application_generation">application_generation</p></td> - <td>version</td> - <td>N/A</td> - <td>The currently live application config generation (aka session id)</td> - </tr> - <tr> - <td><p id="jdisc_gc_count">jdisc.gc.count</p></td> - <td>operation</td> - <td>average, last, max</td> - <td>Number of JVM garbage collections done</td> - </tr> - <tr> - <td><p id="jdisc_gc_ms">jdisc.gc.ms</p></td> - <td>millisecond</td> - <td>average, last, max</td> - <td>Time spent in JVM garbage collection</td> - </tr> - <tr> - <td><p id="jdisc_jvm">jdisc.jvm</p></td> - <td>version</td> - <td>last</td> - <td>JVM runtime version</td> - </tr> - <tr> - <td><p id="jdisc_memory_mappings">jdisc.memory_mappings</p></td> - <td>operation</td> - <td>max</td> - <td>JDISC Memory mappings</td> - </tr> - <tr> - <td><p id="jdisc_open_file_descriptors">jdisc.open_file_descriptors</p></td> - <td>item</td> - <td>max</td> - <td>JDISC Open file descriptors</td> - </tr> - <tr> - <td><p id="jdisc_thread_pool_unhandled_exceptions">jdisc.thread_pool.unhandled_exceptions</p></td> - <td>thread</td> - <td>count, last, max, min, sum</td> - <td>Number of exceptions thrown by tasks</td> - </tr> - <tr> - <td><p id="jdisc_thread_pool_work_queue_capacity">jdisc.thread_pool.work_queue.capacity</p></td> - <td>thread</td> - <td>count, last, max, min, sum</td> - <td>Capacity of the task queue</td> - </tr> - <tr> - <td><p id="jdisc_thread_pool_work_queue_size">jdisc.thread_pool.work_queue.size</p></td> - <td>thread</td> - <td>count, last, max, min, sum</td> - <td>Size of the task queue</td> - </tr> - <tr> - <td><p id="jdisc_thread_pool_rejected_tasks">jdisc.thread_pool.rejected_tasks</p></td> - <td>thread</td> - <td>count, last, max, min, sum</td> - <td>Number of tasks rejected by the thread pool</td> - </tr> - <tr> - <td><p id="jdisc_thread_pool_size">jdisc.thread_pool.size</p></td> - <td>thread</td> - <td>count, last, max, min, sum</td> - <td>Size of the thread pool</td> - </tr> - <tr> - <td><p id="jdisc_thread_pool_max_allowed_size">jdisc.thread_pool.max_allowed_size</p></td> - <td>thread</td> - <td>count, last, max, min, sum</td> - <td>The maximum allowed number of threads in the pool</td> - </tr> - <tr> - <td><p id="jdisc_thread_pool_active_threads">jdisc.thread_pool.active_threads</p></td> - <td>thread</td> - <td>count, last, max, min, sum</td> - <td>Number of threads that are active</td> - </tr> - <tr> - <td><p id="jdisc_deactivated_containers_total">jdisc.deactivated_containers.total</p></td> - <td>item</td> - <td>last, sum</td> - <td>JDISC Deactivated container instances</td> - </tr> - <tr> - <td><p id="jdisc_deactivated_containers_with_retained_refs_last">jdisc.deactivated_containers.with_retained_refs.last</p></td> - <td>item</td> - <td>last</td> - <td>JDISC Deactivated container nodes with retained refs</td> - </tr> - <tr> - <td><p id="jdisc_application_failed_component_graphs">jdisc.application.failed_component_graphs</p></td> - <td>item</td> - <td>rate</td> - <td>JDISC Application failed component graphs</td> - </tr> - <tr> - <td><p id="jdisc_application_component_graph_creation_time_millis">jdisc.application.component_graph.creation_time_millis</p></td> - <td>millisecond</td> - <td>last</td> - <td>JDISC Application component graph creation time</td> - </tr> - <tr> - <td><p id="jdisc_application_component_graph_reconfigurations">jdisc.application.component_graph.reconfigurations</p></td> - <td>item</td> - <td>rate</td> - <td>JDISC Application component graph reconfigurations</td> - </tr> - <tr> - <td><p id="jdisc_singleton_is_active">jdisc.singleton.is_active</p></td> - <td>item</td> - <td>last, max, min</td> - <td>JDISC Singleton is active</td> - </tr> - <tr> - <td><p id="jdisc_singleton_activation_count">jdisc.singleton.activation.count</p></td> - <td>operation</td> - <td>last</td> - <td>JDISC Singleton activations</td> - </tr> - <tr> - <td><p id="jdisc_singleton_activation_failure_count">jdisc.singleton.activation.failure.count</p></td> - <td>operation</td> - <td>last</td> - <td>JDISC Singleton activation failures</td> - </tr> - <tr> - <td><p id="jdisc_singleton_activation_millis">jdisc.singleton.activation.millis</p></td> - <td>millisecond</td> - <td>last</td> - <td>JDISC Singleton activation time</td> - </tr> - <tr> - <td><p id="jdisc_singleton_deactivation_count">jdisc.singleton.deactivation.count</p></td> - <td>operation</td> - <td>last</td> - <td>JDISC Singleton deactivations</td> - </tr> - <tr> - <td><p id="jdisc_singleton_deactivation_failure_count">jdisc.singleton.deactivation.failure.count</p></td> - <td>operation</td> - <td>last</td> - <td>JDISC Singleton deactivation failures</td> - </tr> - <tr> - <td><p id="jdisc_singleton_deactivation_millis">jdisc.singleton.deactivation.millis</p></td> - <td>millisecond</td> - <td>last</td> - <td>JDISC Singleton deactivation time</td> - </tr> - <tr> - <td><p id="jdisc_http_ssl_handshake_failure_missing_client_cert">jdisc.http.ssl.handshake.failure.missing_client_cert</p></td> - <td>operation</td> - <td>rate</td> - <td>JDISC HTTP SSL Handshake failures due to missing client certificate</td> - </tr> - <tr> - <td><p id="jdisc_http_ssl_handshake_failure_expired_client_cert">jdisc.http.ssl.handshake.failure.expired_client_cert</p></td> - <td>operation</td> - <td>rate</td> - <td>JDISC HTTP SSL Handshake failures due to expired client certificate</td> - </tr> - <tr> - <td><p id="jdisc_http_ssl_handshake_failure_invalid_client_cert">jdisc.http.ssl.handshake.failure.invalid_client_cert</p></td> - <td>operation</td> - <td>rate</td> - <td>JDISC HTTP SSL Handshake failures due to invalid client certificate</td> - </tr> - <tr> - <td><p id="jdisc_http_ssl_handshake_failure_incompatible_protocols">jdisc.http.ssl.handshake.failure.incompatible_protocols</p></td> - <td>operation</td> - <td>rate</td> - <td>JDISC HTTP SSL Handshake failures due to incompatible protocols</td> - </tr> - <tr> - <td><p id="jdisc_http_ssl_handshake_failure_incompatible_chifers">jdisc.http.ssl.handshake.failure.incompatible_chifers</p></td> - <td>operation</td> - <td>rate</td> - <td>JDISC HTTP SSL Handshake failures due to incompatible chifers</td> - </tr> - <tr> - <td><p id="jdisc_http_ssl_handshake_failure_connection_closed">jdisc.http.ssl.handshake.failure.connection_closed</p></td> - <td>operation</td> - <td>rate</td> - <td>JDISC HTTP SSL Handshake failures due to connection closed</td> - </tr> - <tr> - <td><p id="jdisc_http_ssl_handshake_failure_unknown">jdisc.http.ssl.handshake.failure.unknown</p></td> - <td>operation</td> - <td>rate</td> - <td>JDISC HTTP SSL Handshake failures for unknown reason</td> - </tr> - <tr> - <td><p id="jdisc_http_latency">jdisc.http.latency</p></td> - <td>millisecond</td> - <td>count, max, sum</td> - <td>Request latency including the HTTP layer</td> - </tr> - <tr> - <td><p id="jdisc_http_request_prematurely_closed">jdisc.http.request.prematurely_closed</p></td> - <td>request</td> - <td>rate</td> - <td>HTTP requests prematurely closed</td> - </tr> - <tr> - <td><p id="jdisc_http_request_requests_per_connection">jdisc.http.request.requests_per_connection</p></td> - <td>request</td> - <td>average, count, max, min, sum</td> - <td>HTTP requests per connection</td> - </tr> - <tr> - <td><p id="jdisc_http_request_uri_length">jdisc.http.request.uri_length</p></td> - <td>byte</td> - <td>count, max, sum</td> - <td>HTTP URI length</td> - </tr> - <tr> - <td><p id="jdisc_http_request_content_size">jdisc.http.request.content_size</p></td> - <td>byte</td> - <td>count, max, sum</td> - <td>HTTP request content size</td> - </tr> - <tr> - <td><p id="jdisc_http_requests">jdisc.http.requests</p></td> - <td>request</td> - <td>count, rate</td> - <td>HTTP requests</td> - </tr> - <tr> - <td><p id="jdisc_http_filter_rule_blocked_requests">jdisc.http.filter.rule.blocked_requests</p></td> - <td>request</td> - <td>rate</td> - <td>Number of requests blocked by filter</td> - </tr> - <tr> - <td><p id="jdisc_http_filter_rule_allowed_requests">jdisc.http.filter.rule.allowed_requests</p></td> - <td>request</td> - <td>rate</td> - <td>Number of requests allowed by filter</td> - </tr> - <tr> - <td><p id="jdisc_http_filtering_request_handled">jdisc.http.filtering.request.handled</p></td> - <td>request</td> - <td>rate</td> - <td>Number of filtering requests handled</td> - </tr> - <tr> - <td><p id="jdisc_http_filtering_request_unhandled">jdisc.http.filtering.request.unhandled</p></td> - <td>request</td> - <td>rate</td> - <td>Number of filtering requests unhandled</td> - </tr> - <tr> - <td><p id="jdisc_http_filtering_response_handled">jdisc.http.filtering.response.handled</p></td> - <td>request</td> - <td>rate</td> - <td>Number of filtering responses handled</td> - </tr> - <tr> - <td><p id="jdisc_http_filtering_response_unhandled">jdisc.http.filtering.response.unhandled</p></td> - <td>request</td> - <td>rate</td> - <td>Number of filtering responses unhandled</td> - </tr> - <tr> - <td><p id="jdisc_http_handler_unhandled_exceptions">jdisc.http.handler.unhandled_exceptions</p></td> - <td>request</td> - <td>rate</td> - <td>Number of unhandled exceptions in handler</td> - </tr> - <tr> - <td><p id="jdisc_tls_capability_checks_succeeded">jdisc.tls.capability_checks.succeeded</p></td> - <td>operation</td> - <td>rate</td> - <td>Number of TLS capability checks succeeded</td> - </tr> - <tr> - <td><p id="jdisc_tls_capability_checks_failed">jdisc.tls.capability_checks.failed</p></td> - <td>operation</td> - <td>rate</td> - <td>Number of TLS capability checks failed</td> - </tr> - <tr> - <td><p id="jdisc_http_jetty_threadpool_thread_max">jdisc.http.jetty.threadpool.thread.max</p></td> - <td>thread</td> - <td>count, last, max, min, sum</td> - <td>Configured maximum number of threads</td> - </tr> - <tr> - <td><p id="jdisc_http_jetty_threadpool_thread_min">jdisc.http.jetty.threadpool.thread.min</p></td> - <td>thread</td> - <td>count, last, max, min, sum</td> - <td>Configured minimum number of threads</td> - </tr> - <tr> - <td><p id="jdisc_http_jetty_threadpool_thread_reserved">jdisc.http.jetty.threadpool.thread.reserved</p></td> - <td>thread</td> - <td>count, last, max, min, sum</td> - <td>Configured number of reserved threads or -1 for heuristic</td> - </tr> - <tr> - <td><p id="jdisc_http_jetty_threadpool_thread_busy">jdisc.http.jetty.threadpool.thread.busy</p></td> - <td>thread</td> - <td>count, last, max, min, sum</td> - <td>Number of threads executing internal and transient jobs</td> - </tr> - <tr> - <td><p id="jdisc_http_jetty_threadpool_thread_total">jdisc.http.jetty.threadpool.thread.total</p></td> - <td>thread</td> - <td>count, last, max, min, sum</td> - <td>Current number of threads</td> - </tr> - <tr> - <td><p id="jdisc_http_jetty_threadpool_queue_size">jdisc.http.jetty.threadpool.queue.size</p></td> - <td>thread</td> - <td>count, last, max, min, sum</td> - <td>Current size of the job queue</td> - </tr> - <tr> - <td><p id="jdisc_http_jetty_http_compliance_violation">jdisc.http.jetty.http_compliance.violation</p></td> - <td>failure</td> - <td>rate</td> - <td>Number of HTTP compliance violations</td> - </tr> - <tr> - <td><p id="serverNumOpenConnections">serverNumOpenConnections</p></td> - <td>connection</td> - <td>average, last, max</td> - <td>The number of currently open connections</td> - </tr> - <tr> - <td><p id="serverNumConnections">serverNumConnections</p></td> - <td>connection</td> - <td>average, last, max</td> - <td>The total number of connections opened</td> - </tr> - <tr> - <td><p id="serverBytesReceived">serverBytesReceived</p></td> - <td>byte</td> - <td>count, sum</td> - <td>The number of bytes received by the server</td> - </tr> - <tr> - <td><p id="serverBytesSent">serverBytesSent</p></td> - <td>byte</td> - <td>count, sum</td> - <td>The number of bytes sent from the server</td> - </tr> - <tr> - <td><p id="handled_requests">handled.requests</p></td> - <td>operation</td> - <td>count</td> - <td>The number of requests handled per metrics snapshot</td> - </tr> - <tr> - <td><p id="handled_latency">handled.latency</p></td> - <td>millisecond</td> - <td>count, max, sum</td> - <td>The time used for handling requests, excluding HTTP layer and rendering</td> - </tr> - <tr> - <td><p id="httpapi_latency">httpapi_latency</p></td> - <td>millisecond</td> - <td>count, max, sum</td> - <td>Duration for requests to the HTTP document APIs</td> - </tr> - <tr> - <td><p id="httpapi_pending">httpapi_pending</p></td> - <td>operation</td> - <td>count, max, sum</td> - <td>Document operations pending execution</td> - </tr> - <tr> - <td><p id="httpapi_num_operations">httpapi_num_operations</p></td> - <td>operation</td> - <td>rate</td> - <td>Total number of document operations performed</td> - </tr> - <tr> - <td><p id="httpapi_num_updates">httpapi_num_updates</p></td> - <td>operation</td> - <td>rate</td> - <td>Document update operations performed</td> - </tr> - <tr> - <td><p id="httpapi_num_removes">httpapi_num_removes</p></td> - <td>operation</td> - <td>rate</td> - <td>Document remove operations performed</td> - </tr> - <tr> - <td><p id="httpapi_num_puts">httpapi_num_puts</p></td> - <td>operation</td> - <td>rate</td> - <td>Document put operations performed</td> - </tr> - <tr> - <td><p id="httpapi_succeeded">httpapi_succeeded</p></td> - <td>operation</td> - <td>rate</td> - <td>Document operations that succeeded</td> - </tr> - <tr> - <td><p id="httpapi_failed">httpapi_failed</p></td> - <td>operation</td> - <td>rate</td> - <td>Document operations that failed</td> - </tr> - <tr> - <td><p id="httpapi_parse_error">httpapi_parse_error</p></td> - <td>operation</td> - <td>rate</td> - <td>Document operations that failed due to document parse errors</td> - </tr> - <tr> - <td><p id="httpapi_condition_not_met">httpapi_condition_not_met</p></td> - <td>operation</td> - <td>rate</td> - <td>Document operations not applied due to condition not met</td> - </tr> - <tr> - <td><p id="httpapi_not_found">httpapi_not_found</p></td> - <td>operation</td> - <td>rate</td> - <td>Document operations not applied due to document not found</td> - </tr> - <tr> - <td><p id="httpapi_failed_unknown">httpapi_failed_unknown</p></td> - <td>operation</td> - <td>rate</td> - <td>Document operations failed by unknown cause</td> - </tr> - <tr> - <td><p id="httpapi_failed_timeout">httpapi_failed_timeout</p></td> - <td>operation</td> - <td>rate</td> - <td>Document operations failed by timeout</td> - </tr> - <tr> - <td><p id="httpapi_failed_insufficient_storage">httpapi_failed_insufficient_storage</p></td> - <td>operation</td> - <td>rate</td> - <td>Document operations failed by insufficient storage</td> - </tr> - <tr> - <td><p id="httpapi_queued_operations">httpapi_queued_operations</p></td> - <td>operation</td> - <td>last</td> - <td>Document operations queued for execution in /document/v1 API handler</td> - </tr> - <tr> - <td><p id="httpapi_queued_bytes">httpapi_queued_bytes</p></td> - <td>byte</td> - <td>last</td> - <td>Total operation bytes queued for execution in /document/v1 API handler</td> - </tr> - <tr> - <td><p id="httpapi_queued_age">httpapi_queued_age</p></td> - <td>second</td> - <td>last</td> - <td>Age in seconds of the oldest operation in the queue for /document/v1 API handler</td> - </tr> - <tr> - <td><p id="httpapi_mbus_window_size">httpapi_mbus_window_size</p></td> - <td>operation</td> - <td>last</td> - <td>The window size of Messagebus's dynamic throttle policy for /document/v1 API handler</td> - </tr> - <tr> - <td><p id="mem_heap_total">mem.heap.total</p></td> - <td>byte</td> - <td>average</td> - <td>Total available heap memory</td> - </tr> - <tr> - <td><p id="mem_heap_free">mem.heap.free</p></td> - <td>byte</td> - <td>average</td> - <td>Free heap memory</td> - </tr> - <tr> - <td><p id="mem_heap_used">mem.heap.used</p></td> - <td>byte</td> - <td>average, max</td> - <td>Currently used heap memory</td> - </tr> - <tr> - <td><p id="mem_direct_total">mem.direct.total</p></td> - <td>byte</td> - <td>average</td> - <td>Total available direct memory</td> - </tr> - <tr> - <td><p id="mem_direct_free">mem.direct.free</p></td> - <td>byte</td> - <td>average</td> - <td>Currently free direct memory</td> - </tr> - <tr> - <td><p id="mem_direct_used">mem.direct.used</p></td> - <td>byte</td> - <td>average, max</td> - <td>Direct memory currently used</td> - </tr> - <tr> - <td><p id="mem_direct_count">mem.direct.count</p></td> - <td>byte</td> - <td>max</td> - <td>Number of direct memory allocations</td> - </tr> - <tr> - <td><p id="mem_native_total">mem.native.total</p></td> - <td>byte</td> - <td>average</td> - <td>Total available native memory</td> - </tr> - <tr> - <td><p id="mem_native_free">mem.native.free</p></td> - <td>byte</td> - <td>average</td> - <td>Currently free native memory</td> - </tr> - <tr> - <td><p id="mem_native_used">mem.native.used</p></td> - <td>byte</td> - <td>average</td> - <td>Native memory currently used</td> - </tr> - <tr> - <td><p id="athenz-tenant-cert_expiry_seconds">athenz-tenant-cert.expiry.seconds</p></td> - <td>second</td> - <td>last, max, min</td> - <td>Time remaining until Athenz tenant certificate expires</td> - </tr> - <tr> - <td><p id="container-iam-role_expiry_seconds">container-iam-role.expiry.seconds</p></td> - <td>second</td> - <td>N/A</td> - <td>Time remaining until IAM role expires</td> - </tr> - <tr> - <td><p id="peak_qps">peak_qps</p></td> - <td>query_per_second</td> - <td>max</td> - <td>The highest number of qps for a second for this metrics snapshot</td> - </tr> - <tr> - <td><p id="search_connections">search_connections</p></td> - <td>connection</td> - <td>count, max, sum</td> - <td>Number of search connections</td> - </tr> - <tr> - <td><p id="feed_operations">feed.operations</p></td> - <td>operation</td> - <td>rate</td> - <td>Number of document feed operations</td> - </tr> - <tr> - <td><p id="feed_latency">feed.latency</p></td> - <td>millisecond</td> - <td>count, max, sum</td> - <td>Feed latency</td> - </tr> - <tr> - <td><p id="feed_http-requests">feed.http-requests</p></td> - <td>operation</td> - <td>count, rate</td> - <td>Feed HTTP requests</td> - </tr> - <tr> - <td><p id="queries">queries</p></td> - <td>operation</td> - <td>rate</td> - <td>Query volume</td> - </tr> - <tr> - <td><p id="query_container_latency">query_container_latency</p></td> - <td>millisecond</td> - <td>count, max, sum</td> - <td>The query execution time consumed in the container</td> - </tr> - <tr> - <td><p id="query_latency">query_latency</p></td> - <td>millisecond</td> - <td>count, max, sum</td> - <td>The overall query latency as observed by the container cluster, excluding HTTP layer and rendering</td> - </tr> - <tr> - <td><p id="query_timeout">query_timeout</p></td> - <td>millisecond</td> - <td>count, max, min, sum</td> - <td>The amount of time allowed for query execution, from the client</td> - </tr> - <tr> - <td><p id="failed_queries">failed_queries</p></td> - <td>operation</td> - <td>rate</td> - <td>The number of failed queries</td> - </tr> - <tr> - <td><p id="degraded_queries">degraded_queries</p></td> - <td>operation</td> - <td>rate</td> - <td>The number of degraded queries, e.g. due to some content nodes not responding in time</td> - </tr> - <tr> - <td><p id="hits_per_query">hits_per_query</p></td> - <td>hit_per_query</td> - <td>count, max, sum</td> - <td>The number of hits returned</td> - </tr> - <tr> - <td><p id="query_hit_offset">query_hit_offset</p></td> - <td>hit</td> - <td>count, max, sum</td> - <td>The offset for hits returned</td> - </tr> - <tr> - <td><p id="documents_covered">documents_covered</p></td> - <td>document</td> - <td>count</td> - <td>The combined number of documents considered during query evaluation</td> - </tr> - <tr> - <td><p id="documents_total">documents_total</p></td> - <td>document</td> - <td>count</td> - <td>The number of documents to be evaluated if all requests had been fully executed</td> - </tr> - <tr> - <td><p id="documents_target_total">documents_target_total</p></td> - <td>document</td> - <td>count</td> - <td>The target number of total documents to be evaluated when all data is in sync</td> - </tr> - <tr> - <td><p id="jdisc_render_latency">jdisc.render.latency</p></td> - <td>nanosecond</td> - <td>average, count, last, max, min, sum</td> - <td>The time used by the container to render responses</td> - </tr> - <tr> - <td><p id="query_item_count">query_item_count</p></td> - <td>item</td> - <td>count, max, sum</td> - <td>The number of query items (terms, phrases, etc.)</td> - </tr> - <tr> - <td><p id="docproc_proctime">docproc.proctime</p></td> - <td>millisecond</td> - <td>count, max, sum</td> - <td>Time spent processing document</td> - </tr> - <tr> - <td><p id="docproc_documents">docproc.documents</p></td> - <td>document</td> - <td>count, max, min, sum</td> - <td>Number of processed documents</td> - </tr> - <tr> - <td><p id="totalhits_per_query">totalhits_per_query</p></td> - <td>hit_per_query</td> - <td>count, max, sum</td> - <td>The total number of documents found to match queries</td> - </tr> - <tr> - <td><p id="empty_results">empty_results</p></td> - <td>operation</td> - <td>rate</td> - <td>Number of queries matching no documents</td> - </tr> - <tr> - <td><p id="requestsOverQuota">requestsOverQuota</p></td> - <td>operation</td> - <td>count, rate</td> - <td>The number of requests rejected due to exceeding quota</td> - </tr> - <tr> - <td><p id="relevance_at_1">relevance.at_1</p></td> - <td>score</td> - <td>count, sum</td> - <td>The relevance of hit number 1</td> - </tr> - <tr> - <td><p id="relevance_at_3">relevance.at_3</p></td> - <td>score</td> - <td>count, sum</td> - <td>The relevance of hit number 3</td> - </tr> - <tr> - <td><p id="relevance_at_10">relevance.at_10</p></td> - <td>score</td> - <td>count, sum</td> - <td>The relevance of hit number 10</td> - </tr> - <tr> - <td><p id="error_timeout">error.timeout</p></td> - <td>operation</td> - <td>rate</td> - <td>Requests that timed out</td> - </tr> - <tr> - <td><p id="error_backends_oos">error.backends_oos</p></td> - <td>operation</td> - <td>rate</td> - <td>Requests that failed due to no available backends nodes</td> - </tr> - <tr> - <td><p id="error_plugin_failure">error.plugin_failure</p></td> - <td>operation</td> - <td>rate</td> - <td>Requests that failed due to plugin failure</td> - </tr> - <tr> - <td><p id="error_backend_communication_error">error.backend_communication_error</p></td> - <td>operation</td> - <td>rate</td> - <td>Requests that failed due to backend communication error</td> - </tr> - <tr> - <td><p id="error_empty_document_summaries">error.empty_document_summaries</p></td> - <td>operation</td> - <td>rate</td> - <td>Requests that failed due to missing document summaries</td> - </tr> - <tr> - <td><p id="error_invalid_query_parameter">error.invalid_query_parameter</p></td> - <td>operation</td> - <td>rate</td> - <td>Requests that failed due to invalid query parameters</td> - </tr> - <tr> - <td><p id="error_internal_server_error">error.internal_server_error</p></td> - <td>operation</td> - <td>rate</td> - <td>Requests that failed due to internal server error</td> - </tr> - <tr> - <td><p id="error_misconfigured_server">error.misconfigured_server</p></td> - <td>operation</td> - <td>rate</td> - <td>Requests that failed due to misconfigured server</td> - </tr> - <tr> - <td><p id="error_invalid_query_transformation">error.invalid_query_transformation</p></td> - <td>operation</td> - <td>rate</td> - <td>Requests that failed due to invalid query transformation</td> - </tr> - <tr> - <td><p id="error_results_with_errors">error.results_with_errors</p></td> - <td>operation</td> - <td>rate</td> - <td>The number of queries with error payload</td> - </tr> - <tr> - <td><p id="error_unspecified">error.unspecified</p></td> - <td>operation</td> - <td>rate</td> - <td>Requests that failed for an unspecified reason</td> - </tr> - <tr> - <td><p id="error_unhandled_exception">error.unhandled_exception</p></td> - <td>operation</td> - <td>rate</td> - <td>Requests that failed due to an unhandled exception</td> - </tr> - <tr> - <td><p id="serverRejectedRequests">serverRejectedRequests</p></td> - <td>operation</td> - <td>count, rate</td> - <td>Deprecated. Use jdisc.thread_pool.rejected_tasks instead.</td> - </tr> - <tr> - <td><p id="serverThreadPoolSize">serverThreadPoolSize</p></td> - <td>thread</td> - <td>last, max</td> - <td>Deprecated. Use jdisc.thread_pool.size instead.</td> - </tr> - <tr> - <td><p id="serverActiveThreads">serverActiveThreads</p></td> - <td>thread</td> - <td>count, last, max, min, sum</td> - <td>Deprecated. Use jdisc.thread_pool.active_threads instead.</td> - </tr> - <tr> - <td><p id="jrt_transport_tls-certificate-verification-failures">jrt.transport.tls-certificate-verification-failures</p></td> - <td>failure</td> - <td>N/A</td> - <td>TLS certificate verification failures</td> - </tr> - <tr> - <td><p id="jrt_transport_peer-authorization-failures">jrt.transport.peer-authorization-failures</p></td> - <td>failure</td> - <td>N/A</td> - <td>TLS peer authorization failures</td> - </tr> - <tr> - <td><p id="jrt_transport_server_tls-connections-established">jrt.transport.server.tls-connections-established</p></td> - <td>connection</td> - <td>N/A</td> - <td>TLS server connections established</td> - </tr> - <tr> - <td><p id="jrt_transport_client_tls-connections-established">jrt.transport.client.tls-connections-established</p></td> - <td>connection</td> - <td>N/A</td> - <td>TLS client connections established</td> - </tr> - <tr> - <td><p id="jrt_transport_server_unencrypted-connections-established">jrt.transport.server.unencrypted-connections-established</p></td> - <td>connection</td> - <td>N/A</td> - <td>Unencrypted server connections established</td> - </tr> - <tr> - <td><p id="jrt_transport_client_unencrypted-connections-established">jrt.transport.client.unencrypted-connections-established</p></td> - <td>connection</td> - <td>N/A</td> - <td>Unencrypted client connections established</td> - </tr> - <tr> - <td><p id="embedder_latency">embedder.latency</p></td> - <td>millisecond</td> - <td>count, max, sum</td> - <td>Time spent creating an embedding</td> - </tr> - <tr> - <td><p id="embedder_sequence_length">embedder.sequence_length</p></td> - <td>item</td> - <td>count, max, sum</td> - <td>Number of tokens in the input sequence</td> - </tr> - <tr> - <td><p id="embedder_request_count">embedder.request.count</p></td> - <td>request</td> - <td>count</td> - <td>Number of embedder API requests</td> - </tr> - <tr> - <td><p id="embedder_request_failure_count">embedder.request.failure.count</p></td> - <td>request</td> - <td>count</td> - <td>Number of failed embedder API requests</td> - </tr> - <tr> - <td><p id="embedder_batch_size">embedder.batch.size</p></td> - <td>item</td> - <td>count, max, sum</td> - <td>Number of items in each dispatched batch</td> - </tr> - <tr> - <td><p id="embedder_batch_queue_time">embedder.batch.queue_time</p></td> - <td>millisecond</td> - <td>count, max, sum</td> - <td>Time spent waiting in queue before batch dispatch</td> - </tr> - <tr> - <td><p id="embedder_batch_count">embedder.batch.count</p></td> - <td>operation</td> - <td>count</td> - <td>Number of batch dispatches</td> - </tr> - </tbody> -</table> - -<h2 id="distributor-metrics">Distributor Metrics</h2> -<table class="table"> - <thead> - <tr><th>Name</th><th>Unit</th><th>Suffixes</th><th>Description</th></tr> - </thead> - <tbody> - <tr> - <td><p id="vds_idealstate_buckets_rechecking">vds.idealstate.buckets_rechecking</p></td> - <td>bucket</td> - <td>average</td> - <td>The number of buckets that we are rechecking for ideal state operations</td> - </tr> - <tr> - <td><p id="vds_idealstate_idealstate_diff">vds.idealstate.idealstate_diff</p></td> - <td>bucket</td> - <td>average</td> - <td>A number representing the current difference from the ideal state. This is a number that decreases steadily as the system is getting closer to the ideal state</td> - </tr> - <tr> - <td><p id="vds_idealstate_buckets_toofewcopies">vds.idealstate.buckets_toofewcopies</p></td> - <td>bucket</td> - <td>average</td> - <td>The number of buckets the distributor controls that have less than the desired redundancy</td> - </tr> - <tr> - <td><p id="vds_idealstate_buckets_toomanycopies">vds.idealstate.buckets_toomanycopies</p></td> - <td>bucket</td> - <td>average</td> - <td>The number of buckets the distributor controls that have more than the desired redundancy</td> - </tr> - <tr> - <td><p id="vds_idealstate_buckets">vds.idealstate.buckets</p></td> - <td>bucket</td> - <td>average</td> - <td>The number of buckets the distributor controls</td> - </tr> - <tr> - <td><p id="vds_idealstate_buckets_notrusted">vds.idealstate.buckets_notrusted</p></td> - <td>bucket</td> - <td>average</td> - <td>The number of buckets that have no trusted copies.</td> - </tr> - <tr> - <td><p id="vds_idealstate_bucket_replicas_moving_out">vds.idealstate.bucket_replicas_moving_out</p></td> - <td>bucket</td> - <td>average</td> - <td>Bucket replicas that should be moved out, e.g. retirement case or node added to cluster that has higher ideal state priority.</td> - </tr> - <tr> - <td><p id="vds_idealstate_bucket_replicas_copying_out">vds.idealstate.bucket_replicas_copying_out</p></td> - <td>bucket</td> - <td>average</td> - <td>Bucket replicas that should be copied out, e.g. node is in ideal state but might have to provide data other nodes in a merge</td> - </tr> - <tr> - <td><p id="vds_idealstate_bucket_replicas_copying_in">vds.idealstate.bucket_replicas_copying_in</p></td> - <td>bucket</td> - <td>average</td> - <td>Bucket replicas that should be copied in, e.g. node does not have a replica for a bucket that it is in ideal state for</td> - </tr> - <tr> - <td><p id="vds_idealstate_bucket_replicas_syncing">vds.idealstate.bucket_replicas_syncing</p></td> - <td>bucket</td> - <td>average</td> - <td>Bucket replicas that need syncing due to mismatching metadata</td> - </tr> - <tr> - <td><p id="vds_idealstate_max_observed_time_since_last_gc_sec">vds.idealstate.max_observed_time_since_last_gc_sec</p></td> - <td>second</td> - <td>average</td> - <td>Maximum time (in seconds) since GC was last successfully run for a bucket. Aggregated max value across all buckets on the distributor.</td> - </tr> - <tr> - <td><p id="vds_idealstate_delete_bucket_done_ok">vds.idealstate.delete_bucket.done_ok</p></td> - <td>operation</td> - <td>rate</td> - <td>The number of operations successfully performed</td> - </tr> - <tr> - <td><p id="vds_idealstate_delete_bucket_done_failed">vds.idealstate.delete_bucket.done_failed</p></td> - <td>operation</td> - <td>rate</td> - <td>The number of operations that failed</td> - </tr> - <tr> - <td><p id="vds_idealstate_delete_bucket_pending">vds.idealstate.delete_bucket.pending</p></td> - <td>operation</td> - <td>average</td> - <td>The number of operations pending</td> - </tr> - <tr> - <td><p id="vds_idealstate_merge_bucket_done_ok">vds.idealstate.merge_bucket.done_ok</p></td> - <td>operation</td> - <td>rate</td> - <td>The number of operations successfully performed</td> - </tr> - <tr> - <td><p id="vds_idealstate_merge_bucket_done_failed">vds.idealstate.merge_bucket.done_failed</p></td> - <td>operation</td> - <td>rate</td> - <td>The number of operations that failed</td> - </tr> - <tr> - <td><p id="vds_idealstate_merge_bucket_pending">vds.idealstate.merge_bucket.pending</p></td> - <td>operation</td> - <td>average</td> - <td>The number of operations pending</td> - </tr> - <tr> - <td><p id="vds_idealstate_merge_bucket_blocked">vds.idealstate.merge_bucket.blocked</p></td> - <td>operation</td> - <td>rate</td> - <td>The number of operations blocked by blocking operation starter</td> - </tr> - <tr> - <td><p id="vds_idealstate_merge_bucket_throttled">vds.idealstate.merge_bucket.throttled</p></td> - <td>operation</td> - <td>rate</td> - <td>The number of operations throttled by throttling operation starter</td> - </tr> - <tr> - <td><p id="vds_idealstate_merge_bucket_source_only_copy_changed">vds.idealstate.merge_bucket.source_only_copy_changed</p></td> - <td>operation</td> - <td>rate</td> - <td>The number of merge operations where source-only copy changed</td> - </tr> - <tr> - <td><p id="vds_idealstate_merge_bucket_source_only_copy_delete_blocked">vds.idealstate.merge_bucket.source_only_copy_delete_blocked</p></td> - <td>operation</td> - <td>rate</td> - <td>The number of merge operations where delete of unchanged source-only copies was blocked</td> - </tr> - <tr> - <td><p id="vds_idealstate_merge_bucket_source_only_copy_delete_failed">vds.idealstate.merge_bucket.source_only_copy_delete_failed</p></td> - <td>operation</td> - <td>rate</td> - <td>The number of merge operations where delete of unchanged source-only copies failed</td> - </tr> - <tr> - <td><p id="vds_idealstate_split_bucket_done_ok">vds.idealstate.split_bucket.done_ok</p></td> - <td>operation</td> - <td>rate</td> - <td>The number of operations successfully performed</td> - </tr> - <tr> - <td><p id="vds_idealstate_split_bucket_done_failed">vds.idealstate.split_bucket.done_failed</p></td> - <td>operation</td> - <td>rate</td> - <td>The number of operations that failed</td> - </tr> - <tr> - <td><p id="vds_idealstate_split_bucket_pending">vds.idealstate.split_bucket.pending</p></td> - <td>operation</td> - <td>average</td> - <td>The number of operations pending</td> - </tr> - <tr> - <td><p id="vds_idealstate_join_bucket_done_ok">vds.idealstate.join_bucket.done_ok</p></td> - <td>operation</td> - <td>rate</td> - <td>The number of operations successfully performed</td> - </tr> - <tr> - <td><p id="vds_idealstate_join_bucket_done_failed">vds.idealstate.join_bucket.done_failed</p></td> - <td>operation</td> - <td>rate</td> - <td>The number of operations that failed</td> - </tr> - <tr> - <td><p id="vds_idealstate_join_bucket_pending">vds.idealstate.join_bucket.pending</p></td> - <td>operation</td> - <td>average</td> - <td>The number of operations pending</td> - </tr> - <tr> - <td><p id="vds_idealstate_garbage_collection_done_ok">vds.idealstate.garbage_collection.done_ok</p></td> - <td>operation</td> - <td>rate</td> - <td>The number of operations successfully performed</td> - </tr> - <tr> - <td><p id="vds_idealstate_garbage_collection_done_failed">vds.idealstate.garbage_collection.done_failed</p></td> - <td>operation</td> - <td>rate</td> - <td>The number of operations that failed</td> - </tr> - <tr> - <td><p id="vds_idealstate_garbage_collection_pending">vds.idealstate.garbage_collection.pending</p></td> - <td>operation</td> - <td>average</td> - <td>The number of operations pending</td> - </tr> - <tr> - <td><p id="vds_idealstate_garbage_collection_documents_removed">vds.idealstate.garbage_collection.documents_removed</p></td> - <td>document</td> - <td>count, rate</td> - <td>Number of documents removed by GC operations</td> - </tr> - <tr> - <td><p id="vds_distributor_puts_latency">vds.distributor.puts.latency</p></td> - <td>millisecond</td> - <td>count, max, sum</td> - <td>The latency of put operations</td> - </tr> - <tr> - <td><p id="vds_distributor_puts_ok">vds.distributor.puts.ok</p></td> - <td>operation</td> - <td>rate</td> - <td>The number of successful put operations performed</td> - </tr> - <tr> - <td><p id="vds_distributor_puts_failures_total">vds.distributor.puts.failures.total</p></td> - <td>operation</td> - <td>rate</td> - <td>Sum of all failures</td> - </tr> - <tr> - <td><p id="vds_distributor_puts_failures_notfound">vds.distributor.puts.failures.notfound</p></td> - <td>operation</td> - <td>rate</td> - <td>The number of operations that failed because the document did not exist</td> - </tr> - <tr> - <td><p id="vds_distributor_puts_failures_test_and_set_failed">vds.distributor.puts.failures.test_and_set_failed</p></td> - <td>operation</td> - <td>rate</td> - <td>The number of mutating operations that failed because they specified a test-and-set condition that did not match the existing document</td> - </tr> - <tr> - <td><p id="vds_distributor_puts_failures_concurrent_mutations">vds.distributor.puts.failures.concurrent_mutations</p></td> - <td>operation</td> - <td>rate</td> - <td>The number of operations that were transiently failed due to a mutating operation already being in progress for its document ID</td> - </tr> - <tr> - <td><p id="vds_distributor_puts_failures_notconnected">vds.distributor.puts.failures.notconnected</p></td> - <td>operation</td> - <td>rate</td> - <td>The number of operations discarded because there were no available storage nodes to send to</td> - </tr> - <tr> - <td><p id="vds_distributor_puts_failures_notready">vds.distributor.puts.failures.notready</p></td> - <td>operation</td> - <td>rate</td> - <td>The number of operations discarded because distributor was not ready</td> - </tr> - <tr> - <td><p id="vds_distributor_puts_failures_wrongdistributor">vds.distributor.puts.failures.wrongdistributor</p></td> - <td>operation</td> - <td>rate</td> - <td>The number of operations discarded because they were sent to the wrong distributor</td> - </tr> - <tr> - <td><p id="vds_distributor_puts_failures_safe_time_not_reached">vds.distributor.puts.failures.safe_time_not_reached</p></td> - <td>operation</td> - <td>rate</td> - <td>The number of operations that were transiently failed due to them arriving before the safe time point for bucket ownership handovers has passed</td> - </tr> - <tr> - <td><p id="vds_distributor_puts_failures_storagefailure">vds.distributor.puts.failures.storagefailure</p></td> - <td>operation</td> - <td>rate</td> - <td>The number of operations that failed in storage</td> - </tr> - <tr> - <td><p id="vds_distributor_puts_failures_timeout">vds.distributor.puts.failures.timeout</p></td> - <td>operation</td> - <td>rate</td> - <td>The number of operations that failed because the operation timed out towards storage</td> - </tr> - <tr> - <td><p id="vds_distributor_puts_failures_busy">vds.distributor.puts.failures.busy</p></td> - <td>operation</td> - <td>rate</td> - <td>The number of messages from storage that failed because the storage node was busy</td> - </tr> - <tr> - <td><p id="vds_distributor_puts_failures_inconsistent_bucket">vds.distributor.puts.failures.inconsistent_bucket</p></td> - <td>operation</td> - <td>rate</td> - <td>The number of operations failed due to buckets being in an inconsistent state or not found</td> - </tr> - <tr> - <td><p id="vds_distributor_removes_latency">vds.distributor.removes.latency</p></td> - <td>millisecond</td> - <td>count, max, sum</td> - <td>The latency of remove operations</td> - </tr> - <tr> - <td><p id="vds_distributor_removes_ok">vds.distributor.removes.ok</p></td> - <td>operation</td> - <td>rate</td> - <td>The number of successful removes operations performed</td> - </tr> - <tr> - <td><p id="vds_distributor_removes_failures_total">vds.distributor.removes.failures.total</p></td> - <td>operation</td> - <td>rate</td> - <td>Sum of all failures</td> - </tr> - <tr> - <td><p id="vds_distributor_removes_failures_notfound">vds.distributor.removes.failures.notfound</p></td> - <td>operation</td> - <td>rate</td> - <td>The number of operations that failed because the document did not exist</td> - </tr> - <tr> - <td><p id="vds_distributor_removes_failures_test_and_set_failed">vds.distributor.removes.failures.test_and_set_failed</p></td> - <td>operation</td> - <td>rate</td> - <td>The number of mutating operations that failed because they specified a test-and-set condition that did not match the existing document</td> - </tr> - <tr> - <td><p id="vds_distributor_removes_failures_concurrent_mutations">vds.distributor.removes.failures.concurrent_mutations</p></td> - <td>operation</td> - <td>rate</td> - <td>The number of operations that were transiently failed due to a mutating operation already being in progress for its document ID</td> - </tr> - <tr> - <td><p id="vds_distributor_updates_latency">vds.distributor.updates.latency</p></td> - <td>millisecond</td> - <td>count, max, sum</td> - <td>The latency of update operations</td> - </tr> - <tr> - <td><p id="vds_distributor_updates_ok">vds.distributor.updates.ok</p></td> - <td>operation</td> - <td>rate</td> - <td>The number of successful updates operations performed</td> - </tr> - <tr> - <td><p id="vds_distributor_updates_failures_total">vds.distributor.updates.failures.total</p></td> - <td>operation</td> - <td>rate</td> - <td>Sum of all failures</td> - </tr> - <tr> - <td><p id="vds_distributor_updates_failures_notfound">vds.distributor.updates.failures.notfound</p></td> - <td>operation</td> - <td>rate</td> - <td>The number of operations that failed because the document did not exist</td> - </tr> - <tr> - <td><p id="vds_distributor_updates_failures_test_and_set_failed">vds.distributor.updates.failures.test_and_set_failed</p></td> - <td>operation</td> - <td>rate</td> - <td>The number of mutating operations that failed because they specified a test-and-set condition that did not match the existing document</td> - </tr> - <tr> - <td><p id="vds_distributor_updates_failures_concurrent_mutations">vds.distributor.updates.failures.concurrent_mutations</p></td> - <td>operation</td> - <td>rate</td> - <td>The number of operations that were transiently failed due to a mutating operation already being in progress for its document ID</td> - </tr> - <tr> - <td><p id="vds_distributor_updates_diverging_timestamp_updates">vds.distributor.updates.diverging_timestamp_updates</p></td> - <td>operation</td> - <td>rate</td> - <td>Number of updates that report they were performed against divergent version timestamps on different replicas</td> - </tr> - <tr> - <td><p id="vds_distributor_removelocations_ok">vds.distributor.removelocations.ok</p></td> - <td>operation</td> - <td>rate</td> - <td>The number of successful removelocations operations performed</td> - </tr> - <tr> - <td><p id="vds_distributor_removelocations_failures_total">vds.distributor.removelocations.failures.total</p></td> - <td>operation</td> - <td>rate</td> - <td>Sum of all failures</td> - </tr> - <tr> - <td><p id="vds_distributor_gets_latency">vds.distributor.gets.latency</p></td> - <td>millisecond</td> - <td>count, max, sum</td> - <td>The average latency of gets operations</td> - </tr> - <tr> - <td><p id="vds_distributor_gets_ok">vds.distributor.gets.ok</p></td> - <td>operation</td> - <td>rate</td> - <td>The number of successful gets operations performed</td> - </tr> - <tr> - <td><p id="vds_distributor_gets_failures_total">vds.distributor.gets.failures.total</p></td> - <td>operation</td> - <td>rate</td> - <td>Sum of all failures</td> - </tr> - <tr> - <td><p id="vds_distributor_gets_failures_notfound">vds.distributor.gets.failures.notfound</p></td> - <td>operation</td> - <td>rate</td> - <td>The number of operations that failed because the document did not exist</td> - </tr> - <tr> - <td><p id="vds_distributor_visitor_latency">vds.distributor.visitor.latency</p></td> - <td>millisecond</td> - <td>count, max, sum</td> - <td>The average latency of visitor operations</td> - </tr> - <tr> - <td><p id="vds_distributor_visitor_ok">vds.distributor.visitor.ok</p></td> - <td>operation</td> - <td>rate</td> - <td>The number of successful visitor operations performed</td> - </tr> - <tr> - <td><p id="vds_distributor_visitor_failures_total">vds.distributor.visitor.failures.total</p></td> - <td>operation</td> - <td>rate</td> - <td>Sum of all failures</td> - </tr> - <tr> - <td><p id="vds_distributor_visitor_failures_notready">vds.distributor.visitor.failures.notready</p></td> - <td>operation</td> - <td>rate</td> - <td>The number of operations discarded because distributor was not ready</td> - </tr> - <tr> - <td><p id="vds_distributor_visitor_failures_notconnected">vds.distributor.visitor.failures.notconnected</p></td> - <td>operation</td> - <td>rate</td> - <td>The number of operations discarded because there were no available storage nodes to send to</td> - </tr> - <tr> - <td><p id="vds_distributor_visitor_failures_wrongdistributor">vds.distributor.visitor.failures.wrongdistributor</p></td> - <td>operation</td> - <td>rate</td> - <td>The number of operations discarded because they were sent to the wrong distributor</td> - </tr> - <tr> - <td><p id="vds_distributor_visitor_failures_safe_time_not_reached">vds.distributor.visitor.failures.safe_time_not_reached</p></td> - <td>operation</td> - <td>rate</td> - <td>The number of operations that were transiently failed due to them arriving before the safe time point for bucket ownership handovers has passed</td> - </tr> - <tr> - <td><p id="vds_distributor_visitor_failures_storagefailure">vds.distributor.visitor.failures.storagefailure</p></td> - <td>operation</td> - <td>rate</td> - <td>The number of operations that failed in storage</td> - </tr> - <tr> - <td><p id="vds_distributor_visitor_failures_timeout">vds.distributor.visitor.failures.timeout</p></td> - <td>operation</td> - <td>rate</td> - <td>The number of operations that failed because the operation timed out towards storage</td> - </tr> - <tr> - <td><p id="vds_distributor_visitor_failures_busy">vds.distributor.visitor.failures.busy</p></td> - <td>operation</td> - <td>rate</td> - <td>The number of messages from storage that failed because the storage node was busy</td> - </tr> - <tr> - <td><p id="vds_distributor_visitor_failures_inconsistent_bucket">vds.distributor.visitor.failures.inconsistent_bucket</p></td> - <td>operation</td> - <td>rate</td> - <td>The number of operations failed due to buckets being in an inconsistent state or not found</td> - </tr> - <tr> - <td><p id="vds_distributor_visitor_failures_notfound">vds.distributor.visitor.failures.notfound</p></td> - <td>operation</td> - <td>rate</td> - <td>The number of operations that failed because the document did not exist</td> - </tr> - <tr> - <td><p id="vds_distributor_docsstored">vds.distributor.docsstored</p></td> - <td>document</td> - <td>average</td> - <td>Number of documents stored in all buckets controlled by this distributor</td> - </tr> - <tr> - <td><p id="vds_distributor_bytesstored">vds.distributor.bytesstored</p></td> - <td>byte</td> - <td>average</td> - <td>Number of bytes stored in all buckets controlled by this distributor</td> - </tr> - <tr> - <td><p id="vds_distributor_mutating_op_memory_usage">vds.distributor.mutating_op_memory_usage</p></td> - <td>byte</td> - <td>max</td> - <td>Estimated amount of memory used by active mutating operations across all distributor stripes, in bytes</td> - </tr> - <tr> - <td><p id="vds_bouncer_clock_skew_aborts">vds.bouncer.clock_skew_aborts</p></td> - <td>operation</td> - <td>count</td> - <td>Number of client operations that were aborted due to clock skew between sender and receiver exceeding acceptable range</td> - </tr> - </tbody> -</table> - -<h2 id="logd-metrics">Logd Metrics</h2> -<table class="table"> - <thead> - <tr><th>Name</th><th>Unit</th><th>Suffixes</th><th>Description</th></tr> - </thead> - <tbody> - <tr> - <td><p id="logd_processed_lines">logd.processed.lines</p></td> - <td>item</td> - <td>count</td> - <td>Number of log lines processed</td> - </tr> - </tbody> -</table> - -<h2 id="nodeadmin-metrics">NodeAdmin Metrics</h2> -<table class="table"> - <thead> - <tr><th>Name</th><th>Unit</th><th>Suffixes</th><th>Description</th></tr> - </thead> - <tbody> - <tr> - <td><p id="endpoint_certificate_expiry_seconds">endpoint.certificate.expiry.seconds</p></td> - <td>second</td> - <td>N/A</td> - <td>Time until node endpoint certificate expires</td> - </tr> - <tr> - <td><p id="node-certificate_expiry_seconds">node-certificate.expiry.seconds</p></td> - <td>second</td> - <td>N/A</td> - <td>Time until node certificate expires</td> - </tr> - </tbody> -</table> - -<h2 id="searchnode-metrics">SearchNode Metrics</h2> -<table class="table"> - <thead> - <tr><th>Name</th><th>Unit</th><th>Suffixes</th><th>Description</th></tr> - </thead> - <tbody> - <tr> - <td><p id="content_proton_config_generation">content.proton.config.generation</p></td> - <td>version</td> - <td>last</td> - <td>The oldest config generation used by this search node</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_documents_total">content.proton.documentdb.documents.total</p></td> - <td>document</td> - <td>last, max</td> - <td>The total number of documents in this documents db (ready + not-ready)</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_documents_ready">content.proton.documentdb.documents.ready</p></td> - <td>document</td> - <td>last, max</td> - <td>The number of ready documents in this document db</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_documents_active">content.proton.documentdb.documents.active</p></td> - <td>document</td> - <td>last, max</td> - <td>The number of active / searchable documents in this document db</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_documents_removed">content.proton.documentdb.documents.removed</p></td> - <td>document</td> - <td>last, max</td> - <td>The number of removed documents in this document db</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_index_docs_in_memory">content.proton.documentdb.index.docs_in_memory</p></td> - <td>document</td> - <td>last, max</td> - <td>Number of documents in memory index</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_disk_usage">content.proton.documentdb.disk_usage</p></td> - <td>byte</td> - <td>last</td> - <td>The total disk usage (in bytes) for this document db</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_memory_usage_allocated_bytes">content.proton.documentdb.memory_usage.allocated_bytes</p></td> - <td>byte</td> - <td>max</td> - <td>The number of allocated bytes</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_heart_beat_age">content.proton.documentdb.heart_beat_age</p></td> - <td>second</td> - <td>last, min</td> - <td>How long ago (in seconds) heart beat maintenance job was run</td> - </tr> - <tr> - <td><p id="content_proton_docsum_docs">content.proton.docsum.docs</p></td> - <td>document</td> - <td>rate</td> - <td>Total docsums returned</td> - </tr> - <tr> - <td><p id="content_proton_docsum_latency">content.proton.docsum.latency</p></td> - <td>millisecond</td> - <td>count, max, sum</td> - <td>Docsum request latency</td> - </tr> - <tr> - <td><p id="content_proton_search_protocol_query_latency">content.proton.search_protocol.query.latency</p></td> - <td>second</td> - <td>count, max, sum</td> - <td>Query request latency (seconds)</td> - </tr> - <tr> - <td><p id="content_proton_search_protocol_query_request_size">content.proton.search_protocol.query.request_size</p></td> - <td>byte</td> - <td>count, max, sum</td> - <td>Query request size (network bytes)</td> - </tr> - <tr> - <td><p id="content_proton_search_protocol_query_reply_size">content.proton.search_protocol.query.reply_size</p></td> - <td>byte</td> - <td>count, max, sum</td> - <td>Query reply size (network bytes)</td> - </tr> - <tr> - <td><p id="content_proton_search_protocol_docsum_latency">content.proton.search_protocol.docsum.latency</p></td> - <td>second</td> - <td>average, count, max, sum</td> - <td>Docsum request latency (seconds)</td> - </tr> - <tr> - <td><p id="content_proton_search_protocol_docsum_request_size">content.proton.search_protocol.docsum.request_size</p></td> - <td>byte</td> - <td>count, max, sum</td> - <td>Docsum request size (network bytes)</td> - </tr> - <tr> - <td><p id="content_proton_search_protocol_docsum_reply_size">content.proton.search_protocol.docsum.reply_size</p></td> - <td>byte</td> - <td>count, max, sum</td> - <td>Docsum reply size (network bytes)</td> - </tr> - <tr> - <td><p id="content_proton_search_protocol_docsum_requested_documents">content.proton.search_protocol.docsum.requested_documents</p></td> - <td>document</td> - <td>count, max, sum</td> - <td>Total requested document summaries</td> - </tr> - <tr> - <td><p id="content_proton_executor_proton_queuesize">content.proton.executor.proton.queuesize</p></td> - <td>task</td> - <td>count, max, sum</td> - <td>Size of executor proton task queue</td> - </tr> - <tr> - <td><p id="content_proton_executor_proton_accepted">content.proton.executor.proton.accepted</p></td> - <td>task</td> - <td>rate</td> - <td>Number of executor proton accepted tasks</td> - </tr> - <tr> - <td><p id="content_proton_executor_proton_wakeups">content.proton.executor.proton.wakeups</p></td> - <td>wakeup</td> - <td>rate</td> - <td>Number of times an executor proton worker thread has been woken up</td> - </tr> - <tr> - <td><p id="content_proton_executor_proton_utilization">content.proton.executor.proton.utilization</p></td> - <td>fraction</td> - <td>count, max, sum</td> - <td>Ratio of time the executor proton worker threads has been active</td> - </tr> - <tr> - <td><p id="content_proton_executor_flush_queuesize">content.proton.executor.flush.queuesize</p></td> - <td>task</td> - <td>count, max, sum</td> - <td>Size of executor flush task queue</td> - </tr> - <tr> - <td><p id="content_proton_executor_flush_accepted">content.proton.executor.flush.accepted</p></td> - <td>task</td> - <td>rate</td> - <td>Number of accepted executor flush tasks</td> - </tr> - <tr> - <td><p id="content_proton_executor_flush_wakeups">content.proton.executor.flush.wakeups</p></td> - <td>wakeup</td> - <td>rate</td> - <td>Number of times an executor flush worker thread has been woken up</td> - </tr> - <tr> - <td><p id="content_proton_executor_flush_utilization">content.proton.executor.flush.utilization</p></td> - <td>fraction</td> - <td>count, max, sum</td> - <td>Ratio of time the executor flush worker threads has been active</td> - </tr> - <tr> - <td><p id="content_proton_executor_match_queuesize">content.proton.executor.match.queuesize</p></td> - <td>task</td> - <td>count, max, sum</td> - <td>Size of executor match task queue</td> - </tr> - <tr> - <td><p id="content_proton_executor_match_accepted">content.proton.executor.match.accepted</p></td> - <td>task</td> - <td>rate</td> - <td>Number of accepted executor match tasks</td> - </tr> - <tr> - <td><p id="content_proton_executor_match_wakeups">content.proton.executor.match.wakeups</p></td> - <td>wakeup</td> - <td>rate</td> - <td>Number of times an executor match worker thread has been woken up</td> - </tr> - <tr> - <td><p id="content_proton_executor_match_utilization">content.proton.executor.match.utilization</p></td> - <td>fraction</td> - <td>count, max, sum</td> - <td>Ratio of time the executor match worker threads has been active</td> - </tr> - <tr> - <td><p id="content_proton_executor_docsum_queuesize">content.proton.executor.docsum.queuesize</p></td> - <td>task</td> - <td>count, max, sum</td> - <td>Size of executor docsum task queue</td> - </tr> - <tr> - <td><p id="content_proton_executor_docsum_accepted">content.proton.executor.docsum.accepted</p></td> - <td>task</td> - <td>rate</td> - <td>Number of executor accepted docsum tasks</td> - </tr> - <tr> - <td><p id="content_proton_executor_docsum_wakeups">content.proton.executor.docsum.wakeups</p></td> - <td>wakeup</td> - <td>rate</td> - <td>Number of times an executor docsum worker thread has been woken up</td> - </tr> - <tr> - <td><p id="content_proton_executor_docsum_utilization">content.proton.executor.docsum.utilization</p></td> - <td>fraction</td> - <td>count, max, sum</td> - <td>Ratio of time the executor docsum worker threads has been active</td> - </tr> - <tr> - <td><p id="content_proton_executor_shared_queuesize">content.proton.executor.shared.queuesize</p></td> - <td>task</td> - <td>count, max, sum</td> - <td>Size of executor shared task queue</td> - </tr> - <tr> - <td><p id="content_proton_executor_shared_accepted">content.proton.executor.shared.accepted</p></td> - <td>task</td> - <td>rate</td> - <td>Number of executor shared accepted tasks</td> - </tr> - <tr> - <td><p id="content_proton_executor_shared_wakeups">content.proton.executor.shared.wakeups</p></td> - <td>wakeup</td> - <td>rate</td> - <td>Number of times an executor shared worker thread has been woken up</td> - </tr> - <tr> - <td><p id="content_proton_executor_shared_utilization">content.proton.executor.shared.utilization</p></td> - <td>fraction</td> - <td>count, max, sum</td> - <td>Ratio of time the executor shared worker threads has been active</td> - </tr> - <tr> - <td><p id="content_proton_executor_warmup_queuesize">content.proton.executor.warmup.queuesize</p></td> - <td>task</td> - <td>count, max, sum</td> - <td>Size of executor warmup task queue</td> - </tr> - <tr> - <td><p id="content_proton_executor_warmup_accepted">content.proton.executor.warmup.accepted</p></td> - <td>task</td> - <td>rate</td> - <td>Number of accepted executor warmup tasks</td> - </tr> - <tr> - <td><p id="content_proton_executor_warmup_wakeups">content.proton.executor.warmup.wakeups</p></td> - <td>wakeup</td> - <td>rate</td> - <td>Number of times a warmup executor worker thread has been woken up</td> - </tr> - <tr> - <td><p id="content_proton_executor_warmup_utilization">content.proton.executor.warmup.utilization</p></td> - <td>fraction</td> - <td>count, max, sum</td> - <td>Ratio of time the executor warmup worker threads has been active</td> - </tr> - <tr> - <td><p id="content_proton_executor_field_writer_queuesize">content.proton.executor.field_writer.queuesize</p></td> - <td>task</td> - <td>count, max, sum</td> - <td>Size of executor field writer task queue</td> - </tr> - <tr> - <td><p id="content_proton_executor_field_writer_accepted">content.proton.executor.field_writer.accepted</p></td> - <td>task</td> - <td>rate</td> - <td>Number of accepted executor field writer tasks</td> - </tr> - <tr> - <td><p id="content_proton_executor_field_writer_wakeups">content.proton.executor.field_writer.wakeups</p></td> - <td>wakeup</td> - <td>rate</td> - <td>Number of times an executor field writer worker thread has been woken up</td> - </tr> - <tr> - <td><p id="content_proton_executor_field_writer_utilization">content.proton.executor.field_writer.utilization</p></td> - <td>fraction</td> - <td>count, max, sum</td> - <td>Ratio of time the executor fieldwriter worker threads has been active</td> - </tr> - <tr> - <td><p id="content_proton_executor_field_writer_saturation">content.proton.executor.field_writer.saturation</p></td> - <td>fraction</td> - <td>count, max, sum</td> - <td>Ratio indicating the max saturation of underlying worker threads. A higher saturation than utilization indicates a bottleneck in one of the worker threads.</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_job_total">content.proton.documentdb.job.total</p></td> - <td>fraction</td> - <td>average</td> - <td>The job load average total of all job metrics</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_job_attribute_flush">content.proton.documentdb.job.attribute_flush</p></td> - <td>fraction</td> - <td>average</td> - <td>Flushing of attribute vector(s) to disk</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_job_memory_index_flush">content.proton.documentdb.job.memory_index_flush</p></td> - <td>fraction</td> - <td>average</td> - <td>Flushing of memory index to disk</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_job_disk_index_fusion">content.proton.documentdb.job.disk_index_fusion</p></td> - <td>fraction</td> - <td>average</td> - <td>Fusion of disk indexes</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_job_document_store_flush">content.proton.documentdb.job.document_store_flush</p></td> - <td>fraction</td> - <td>average</td> - <td>Flushing of document store to disk</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_job_document_store_compact">content.proton.documentdb.job.document_store_compact</p></td> - <td>fraction</td> - <td>average</td> - <td>Compaction of document store on disk</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_job_bucket_move">content.proton.documentdb.job.bucket_move</p></td> - <td>fraction</td> - <td>average</td> - <td>Moving of buckets between 'ready' and 'notready' sub databases</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_job_lid_space_compact">content.proton.documentdb.job.lid_space_compact</p></td> - <td>fraction</td> - <td>average</td> - <td>Compaction of lid space in document meta store and attribute vectors</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_job_removed_documents_prune">content.proton.documentdb.job.removed_documents_prune</p></td> - <td>fraction</td> - <td>average</td> - <td>Pruning of removed documents in 'removed' sub database</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_threading_service_master_queuesize">content.proton.documentdb.threading_service.master.queuesize</p></td> - <td>task</td> - <td>count, max, sum</td> - <td>Size of threading service master task queue</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_threading_service_master_accepted">content.proton.documentdb.threading_service.master.accepted</p></td> - <td>task</td> - <td>rate</td> - <td>Number of accepted threading service master tasks</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_threading_service_master_wakeups">content.proton.documentdb.threading_service.master.wakeups</p></td> - <td>wakeup</td> - <td>rate</td> - <td>Number of times a threading service master worker thread has been woken up</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_threading_service_master_utilization">content.proton.documentdb.threading_service.master.utilization</p></td> - <td>fraction</td> - <td>count, max, sum</td> - <td>Ratio of time the threading service master worker threads has been active</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_threading_service_index_queuesize">content.proton.documentdb.threading_service.index.queuesize</p></td> - <td>task</td> - <td>count, max, sum</td> - <td>Size of threading service index task queue</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_threading_service_index_accepted">content.proton.documentdb.threading_service.index.accepted</p></td> - <td>task</td> - <td>rate</td> - <td>Number of accepted threading service index tasks</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_threading_service_index_wakeups">content.proton.documentdb.threading_service.index.wakeups</p></td> - <td>wakeup</td> - <td>rate</td> - <td>Number of times a threading service index worker thread has been woken up</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_threading_service_index_utilization">content.proton.documentdb.threading_service.index.utilization</p></td> - <td>fraction</td> - <td>count, max, sum</td> - <td>Ratio of time the threading service index worker threads has been active</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_threading_service_summary_queuesize">content.proton.documentdb.threading_service.summary.queuesize</p></td> - <td>task</td> - <td>count, max, sum</td> - <td>Size of threading service summary task queue</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_threading_service_summary_accepted">content.proton.documentdb.threading_service.summary.accepted</p></td> - <td>task</td> - <td>rate</td> - <td>Number of accepted threading service summary tasks</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_threading_service_summary_wakeups">content.proton.documentdb.threading_service.summary.wakeups</p></td> - <td>wakeup</td> - <td>rate</td> - <td>Number of times a threading service summary worker thread has been woken up</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_threading_service_summary_utilization">content.proton.documentdb.threading_service.summary.utilization</p></td> - <td>fraction</td> - <td>count, max, sum</td> - <td>Ratio of time the threading service summary worker threads has been active</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_ready_lid_space_lid_bloat_factor">content.proton.documentdb.ready.lid_space.lid_bloat_factor</p></td> - <td>fraction</td> - <td>average</td> - <td>The bloat factor of this lid space, indicating the total amount of holes in the allocated lid space ((lid_limit - used_lids) / lid_limit)</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_ready_lid_space_lid_fragmentation_factor">content.proton.documentdb.ready.lid_space.lid_fragmentation_factor</p></td> - <td>fraction</td> - <td>average</td> - <td>The fragmentation factor of this lid space, indicating the amount of holes in the currently used part of the lid space ((highest_used_lid - used_lids) / highest_used_lid)</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_ready_lid_space_lid_limit">content.proton.documentdb.ready.lid_space.lid_limit</p></td> - <td>documentid</td> - <td>last, max</td> - <td>The size of the allocated lid space</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_ready_lid_space_highest_used_lid">content.proton.documentdb.ready.lid_space.highest_used_lid</p></td> - <td>documentid</td> - <td>last, max</td> - <td>The highest used lid</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_ready_lid_space_used_lids">content.proton.documentdb.ready.lid_space.used_lids</p></td> - <td>documentid</td> - <td>last, max</td> - <td>The number of lids used</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_notready_lid_space_lid_bloat_factor">content.proton.documentdb.notready.lid_space.lid_bloat_factor</p></td> - <td>fraction</td> - <td>average</td> - <td>The bloat factor of this lid space, indicating the total amount of holes in the allocated lid space ((lid_limit - used_lids) / lid_limit)</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_notready_lid_space_lid_fragmentation_factor">content.proton.documentdb.notready.lid_space.lid_fragmentation_factor</p></td> - <td>fraction</td> - <td>average</td> - <td>The fragmentation factor of this lid space, indicating the amount of holes in the currently used part of the lid space ((highest_used_lid - used_lids) / highest_used_lid)</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_notready_lid_space_lid_limit">content.proton.documentdb.notready.lid_space.lid_limit</p></td> - <td>documentid</td> - <td>last, max</td> - <td>The size of the allocated lid space</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_notready_lid_space_highest_used_lid">content.proton.documentdb.notready.lid_space.highest_used_lid</p></td> - <td>documentid</td> - <td>last, max</td> - <td>The highest used lid</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_notready_lid_space_used_lids">content.proton.documentdb.notready.lid_space.used_lids</p></td> - <td>documentid</td> - <td>last, max</td> - <td>The number of lids used</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_removed_lid_space_lid_bloat_factor">content.proton.documentdb.removed.lid_space.lid_bloat_factor</p></td> - <td>fraction</td> - <td>average</td> - <td>The bloat factor of this lid space, indicating the total amount of holes in the allocated lid space ((lid_limit - used_lids) / lid_limit)</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_removed_lid_space_lid_fragmentation_factor">content.proton.documentdb.removed.lid_space.lid_fragmentation_factor</p></td> - <td>fraction</td> - <td>average</td> - <td>The fragmentation factor of this lid space, indicating the amount of holes in the currently used part of the lid space ((highest_used_lid - used_lids) / highest_used_lid)</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_removed_lid_space_lid_limit">content.proton.documentdb.removed.lid_space.lid_limit</p></td> - <td>documentid</td> - <td>last, max</td> - <td>The size of the allocated lid space</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_removed_lid_space_highest_used_lid">content.proton.documentdb.removed.lid_space.highest_used_lid</p></td> - <td>documentid</td> - <td>last, max</td> - <td>The highest used lid</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_removed_lid_space_used_lids">content.proton.documentdb.removed.lid_space.used_lids</p></td> - <td>documentid</td> - <td>last, max</td> - <td>The number of lids used</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_bucket_move_buckets_pending">content.proton.documentdb.bucket_move.buckets_pending</p></td> - <td>bucket</td> - <td>last, max, sum</td> - <td>The number of buckets left to move</td> - </tr> - <tr> - <td><p id="content_proton_resource_usage_disk">content.proton.resource_usage.disk</p></td> - <td>fraction</td> - <td>average</td> - <td>The relative amount of disk used by this content node (transient usage not included, value in the range [0, 1]). Same value as reported to the cluster controller</td> - </tr> - <tr> - <td><p id="content_proton_resource_usage_disk_usage_total">content.proton.resource_usage.disk_usage.total</p></td> - <td>fraction</td> - <td>max</td> - <td>The total relative amount of disk used by this content node (value in the range [0, 1])</td> - </tr> - <tr> - <td><p id="content_proton_resource_usage_disk_usage_total_utilization">content.proton.resource_usage.disk_usage.total_utilization</p></td> - <td>fraction</td> - <td>max</td> - <td>The relative amount of disk used compared to the content node disk resource limit</td> - </tr> - <tr> - <td><p id="content_proton_resource_usage_disk_usage_transient">content.proton.resource_usage.disk_usage.transient</p></td> - <td>fraction</td> - <td>max</td> - <td>The relative amount of transient disk used by this content node (value in the range [0, 1])</td> - </tr> - <tr> - <td><p id="content_proton_resource_usage_memory">content.proton.resource_usage.memory</p></td> - <td>fraction</td> - <td>average</td> - <td>The relative amount of memory used by this content node (transient usage not included, value in the range [0, 1]). Same value as reported to the cluster controller</td> - </tr> - <tr> - <td><p id="content_proton_resource_usage_memory_usage_total">content.proton.resource_usage.memory_usage.total</p></td> - <td>fraction</td> - <td>max</td> - <td>The total relative amount of memory used by this content node (value in the range [0, 1])</td> - </tr> - <tr> - <td><p id="content_proton_resource_usage_memory_usage_total_utilization">content.proton.resource_usage.memory_usage.total_utilization</p></td> - <td>fraction</td> - <td>max</td> - <td>The relative amount of memory used compared to the content node memory resource limit</td> - </tr> - <tr> - <td><p id="content_proton_resource_usage_memory_usage_transient">content.proton.resource_usage.memory_usage.transient</p></td> - <td>fraction</td> - <td>max</td> - <td>The relative amount of transient memory used by this content node (value in the range [0, 1])</td> - </tr> - <tr> - <td><p id="content_proton_resource_usage_memory_mappings">content.proton.resource_usage.memory_mappings</p></td> - <td>file</td> - <td>max</td> - <td>The number of memory mapped files</td> - </tr> - <tr> - <td><p id="content_proton_resource_usage_open_file_descriptors">content.proton.resource_usage.open_file_descriptors</p></td> - <td>file</td> - <td>max</td> - <td>The number of open files</td> - </tr> - <tr> - <td><p id="content_proton_resource_usage_feeding_blocked">content.proton.resource_usage.feeding_blocked</p></td> - <td>binary</td> - <td>last, max</td> - <td>Whether feeding is blocked due to resource limits being reached (value is either 0 or 1)</td> - </tr> - <tr> - <td><p id="content_proton_resource_usage_malloc_arena">content.proton.resource_usage.malloc_arena</p></td> - <td>byte</td> - <td>max</td> - <td>Size of malloc arena</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_attribute_resource_usage_address_space">content.proton.documentdb.attribute.resource_usage.address_space</p></td> - <td>fraction</td> - <td>max</td> - <td>The max relative address space used among components in all attribute vectors in this document db (value in the range [0, 1])</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_attribute_resource_usage_feeding_blocked">content.proton.documentdb.attribute.resource_usage.feeding_blocked</p></td> - <td>binary</td> - <td>max</td> - <td>Whether feeding is blocked due to attribute resource limits being reached (value is either 0 or 1)</td> - </tr> - <tr> - <td><p id="content_proton_resource_usage_cpu_util_setup">content.proton.resource_usage.cpu_util.setup</p></td> - <td>fraction</td> - <td>count, max, sum</td> - <td>cpu used by system init and (re-)configuration</td> - </tr> - <tr> - <td><p id="content_proton_resource_usage_cpu_util_read">content.proton.resource_usage.cpu_util.read</p></td> - <td>fraction</td> - <td>count, max, sum</td> - <td>cpu used by reading data from the system</td> - </tr> - <tr> - <td><p id="content_proton_resource_usage_cpu_util_write">content.proton.resource_usage.cpu_util.write</p></td> - <td>fraction</td> - <td>count, max, sum</td> - <td>cpu used by writing data to the system</td> - </tr> - <tr> - <td><p id="content_proton_resource_usage_cpu_util_compact">content.proton.resource_usage.cpu_util.compact</p></td> - <td>fraction</td> - <td>count, max, sum</td> - <td>cpu used by internal data re-structuring</td> - </tr> - <tr> - <td><p id="content_proton_resource_usage_cpu_util_other">content.proton.resource_usage.cpu_util.other</p></td> - <td>fraction</td> - <td>count, max, sum</td> - <td>cpu used by work not classified as a specific category</td> - </tr> - <tr> - <td><p id="content_proton_transactionlog_entries">content.proton.transactionlog.entries</p></td> - <td>record</td> - <td>average</td> - <td>The current number of entries in the transaction log</td> - </tr> - <tr> - <td><p id="content_proton_transactionlog_disk_usage">content.proton.transactionlog.disk_usage</p></td> - <td>byte</td> - <td>average</td> - <td>The disk usage (in bytes) of the transaction log</td> - </tr> - <tr> - <td><p id="content_proton_transactionlog_replay_time">content.proton.transactionlog.replay_time</p></td> - <td>second</td> - <td>last, max</td> - <td>The replay time (in seconds) of the transaction log during start-up</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_ready_document_store_disk_usage">content.proton.documentdb.ready.document_store.disk_usage</p></td> - <td>byte</td> - <td>average</td> - <td>Disk space usage in bytes</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_ready_document_store_disk_bloat">content.proton.documentdb.ready.document_store.disk_bloat</p></td> - <td>byte</td> - <td>average</td> - <td>Disk space bloat in bytes</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_ready_document_store_max_bucket_spread">content.proton.documentdb.ready.document_store.max_bucket_spread</p></td> - <td>fraction</td> - <td>average</td> - <td>Max bucket spread in underlying files (sum(unique buckets in each chunk)/unique buckets in file)</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_ready_document_store_memory_usage_allocated_bytes">content.proton.documentdb.ready.document_store.memory_usage.allocated_bytes</p></td> - <td>byte</td> - <td>average</td> - <td>The number of allocated bytes</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_ready_document_store_memory_usage_used_bytes">content.proton.documentdb.ready.document_store.memory_usage.used_bytes</p></td> - <td>byte</td> - <td>average</td> - <td>The number of used bytes (<= allocated_bytes)</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_ready_document_store_memory_usage_onhold_bytes">content.proton.documentdb.ready.document_store.memory_usage.onhold_bytes</p></td> - <td>byte</td> - <td>average</td> - <td>The number of bytes on hold</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_notready_document_store_disk_usage">content.proton.documentdb.notready.document_store.disk_usage</p></td> - <td>byte</td> - <td>average</td> - <td>Disk space usage in bytes</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_notready_document_store_disk_bloat">content.proton.documentdb.notready.document_store.disk_bloat</p></td> - <td>byte</td> - <td>average</td> - <td>Disk space bloat in bytes</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_notready_document_store_max_bucket_spread">content.proton.documentdb.notready.document_store.max_bucket_spread</p></td> - <td>fraction</td> - <td>average</td> - <td>Max bucket spread in underlying files (sum(unique buckets in each chunk)/unique buckets in file)</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_notready_document_store_memory_usage_allocated_bytes">content.proton.documentdb.notready.document_store.memory_usage.allocated_bytes</p></td> - <td>byte</td> - <td>average</td> - <td>The number of allocated bytes</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_notready_document_store_memory_usage_used_bytes">content.proton.documentdb.notready.document_store.memory_usage.used_bytes</p></td> - <td>byte</td> - <td>average</td> - <td>The number of used bytes (<= allocated_bytes)</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_notready_document_store_memory_usage_dead_bytes">content.proton.documentdb.notready.document_store.memory_usage.dead_bytes</p></td> - <td>byte</td> - <td>average</td> - <td>The number of dead bytes (<= used_bytes)</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_notready_document_store_memory_usage_onhold_bytes">content.proton.documentdb.notready.document_store.memory_usage.onhold_bytes</p></td> - <td>byte</td> - <td>average</td> - <td>The number of bytes on hold</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_removed_document_store_disk_usage">content.proton.documentdb.removed.document_store.disk_usage</p></td> - <td>byte</td> - <td>average</td> - <td>Disk space usage in bytes</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_removed_document_store_disk_bloat">content.proton.documentdb.removed.document_store.disk_bloat</p></td> - <td>byte</td> - <td>average</td> - <td>Disk space bloat in bytes</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_removed_document_store_max_bucket_spread">content.proton.documentdb.removed.document_store.max_bucket_spread</p></td> - <td>fraction</td> - <td>average</td> - <td>Max bucket spread in underlying files (sum(unique buckets in each chunk)/unique buckets in file)</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_removed_document_store_memory_usage_allocated_bytes">content.proton.documentdb.removed.document_store.memory_usage.allocated_bytes</p></td> - <td>byte</td> - <td>average</td> - <td>The number of allocated bytes</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_removed_document_store_memory_usage_used_bytes">content.proton.documentdb.removed.document_store.memory_usage.used_bytes</p></td> - <td>byte</td> - <td>average</td> - <td>The number of used bytes (<= allocated_bytes)</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_removed_document_store_memory_usage_dead_bytes">content.proton.documentdb.removed.document_store.memory_usage.dead_bytes</p></td> - <td>byte</td> - <td>average</td> - <td>The number of dead bytes (<= used_bytes)</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_removed_document_store_memory_usage_onhold_bytes">content.proton.documentdb.removed.document_store.memory_usage.onhold_bytes</p></td> - <td>byte</td> - <td>average</td> - <td>The number of bytes on hold</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_ready_document_store_cache_memory_usage">content.proton.documentdb.ready.document_store.cache.memory_usage</p></td> - <td>byte</td> - <td>average</td> - <td>Memory usage of the cache (in bytes)</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_ready_document_store_cache_hit_rate">content.proton.documentdb.ready.document_store.cache.hit_rate</p></td> - <td>fraction</td> - <td>average</td> - <td>Rate of hits in the cache compared to number of lookups</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_ready_document_store_cache_lookups">content.proton.documentdb.ready.document_store.cache.lookups</p></td> - <td>operation</td> - <td>rate</td> - <td>Number of lookups in the cache (hits + misses)</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_ready_document_store_cache_invalidations">content.proton.documentdb.ready.document_store.cache.invalidations</p></td> - <td>operation</td> - <td>rate</td> - <td>Number of invalidations (erased elements) in the cache. </td> - </tr> - <tr> - <td><p id="content_proton_documentdb_notready_document_store_cache_memory_usage">content.proton.documentdb.notready.document_store.cache.memory_usage</p></td> - <td>byte</td> - <td>average</td> - <td>Memory usage of the cache (in bytes)</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_notready_document_store_cache_hit_rate">content.proton.documentdb.notready.document_store.cache.hit_rate</p></td> - <td>fraction</td> - <td>average</td> - <td>Rate of hits in the cache compared to number of lookups</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_notready_document_store_cache_lookups">content.proton.documentdb.notready.document_store.cache.lookups</p></td> - <td>operation</td> - <td>rate</td> - <td>Number of lookups in the cache (hits + misses)</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_notready_document_store_cache_invalidations">content.proton.documentdb.notready.document_store.cache.invalidations</p></td> - <td>operation</td> - <td>rate</td> - <td>Number of invalidations (erased elements) in the cache. </td> - </tr> - <tr> - <td><p id="content_proton_documentdb_ready_attribute_memory_usage_allocated_bytes">content.proton.documentdb.ready.attribute.memory_usage.allocated_bytes</p></td> - <td>byte</td> - <td>average</td> - <td>The number of allocated bytes</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_ready_attribute_memory_usage_used_bytes">content.proton.documentdb.ready.attribute.memory_usage.used_bytes</p></td> - <td>byte</td> - <td>average</td> - <td>The number of used bytes (<= allocated_bytes)</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_ready_attribute_memory_usage_dead_bytes">content.proton.documentdb.ready.attribute.memory_usage.dead_bytes</p></td> - <td>byte</td> - <td>average</td> - <td>The number of dead bytes (<= used_bytes)</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_ready_attribute_memory_usage_onhold_bytes">content.proton.documentdb.ready.attribute.memory_usage.onhold_bytes</p></td> - <td>byte</td> - <td>average</td> - <td>The number of bytes on hold</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_ready_attribute_disk_usage">content.proton.documentdb.ready.attribute.disk_usage</p></td> - <td>byte</td> - <td>average</td> - <td>Disk space usage (in bytes) of the flushed snapshot of this attribute for this document type</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_notready_attribute_memory_usage_allocated_bytes">content.proton.documentdb.notready.attribute.memory_usage.allocated_bytes</p></td> - <td>byte</td> - <td>average</td> - <td>The number of allocated bytes</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_notready_attribute_memory_usage_used_bytes">content.proton.documentdb.notready.attribute.memory_usage.used_bytes</p></td> - <td>byte</td> - <td>average</td> - <td>The number of used bytes (<= allocated_bytes)</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_notready_attribute_memory_usage_dead_bytes">content.proton.documentdb.notready.attribute.memory_usage.dead_bytes</p></td> - <td>byte</td> - <td>average</td> - <td>The number of dead bytes (<= used_bytes)</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_notready_attribute_memory_usage_onhold_bytes">content.proton.documentdb.notready.attribute.memory_usage.onhold_bytes</p></td> - <td>byte</td> - <td>average</td> - <td>The number of bytes on hold</td> - </tr> - <tr> - <td><p id="content_proton_index_cache_postinglist_memory_usage">content.proton.index.cache.postinglist.memory_usage</p></td> - <td>byte</td> - <td>average</td> - <td>Memory usage of the cache (in bytes). Contains disk index posting list files across all document types</td> - </tr> - <tr> - <td><p id="content_proton_index_cache_postinglist_hit_rate">content.proton.index.cache.postinglist.hit_rate</p></td> - <td>fraction</td> - <td>average</td> - <td>Rate of hits in the cache compared to number of lookups. Contains disk index posting list files across all document types</td> - </tr> - <tr> - <td><p id="content_proton_index_cache_postinglist_lookups">content.proton.index.cache.postinglist.lookups</p></td> - <td>operation</td> - <td>rate</td> - <td>Number of lookups in the cache (hits + misses). Contains disk index posting list files across all document types</td> - </tr> - <tr> - <td><p id="content_proton_index_cache_postinglist_invalidations">content.proton.index.cache.postinglist.invalidations</p></td> - <td>operation</td> - <td>rate</td> - <td>Number of invalidations (erased elements) in the cache. Contains disk index posting list files across all document types</td> - </tr> - <tr> - <td><p id="content_proton_index_cache_bitvector_memory_usage">content.proton.index.cache.bitvector.memory_usage</p></td> - <td>byte</td> - <td>average</td> - <td>Memory usage of the cache (in bytes). Contains disk index bitvector files across all document types</td> - </tr> - <tr> - <td><p id="content_proton_index_cache_bitvector_hit_rate">content.proton.index.cache.bitvector.hit_rate</p></td> - <td>fraction</td> - <td>average</td> - <td>Rate of hits in the cache compared to number of lookups. Contains disk index bitvector files across all document types</td> - </tr> - <tr> - <td><p id="content_proton_index_cache_bitvector_lookups">content.proton.index.cache.bitvector.lookups</p></td> - <td>operation</td> - <td>rate</td> - <td>Number of lookups in the cache (hits + misses). Contains disk index bitvector files across all document types</td> - </tr> - <tr> - <td><p id="content_proton_index_cache_bitvector_invalidations">content.proton.index.cache.bitvector.invalidations</p></td> - <td>operation</td> - <td>rate</td> - <td>Number of invalidations (erased elements) in the cache. Contains disk index bitvector files across all document types</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_index_memory_usage_allocated_bytes">content.proton.documentdb.index.memory_usage.allocated_bytes</p></td> - <td>byte</td> - <td>average</td> - <td>The number of allocated bytes for the memory index for this document type</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_index_memory_usage_used_bytes">content.proton.documentdb.index.memory_usage.used_bytes</p></td> - <td>byte</td> - <td>average</td> - <td>The number of used bytes (<= allocated_bytes) for the memory index for this document type</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_index_memory_usage_dead_bytes">content.proton.documentdb.index.memory_usage.dead_bytes</p></td> - <td>byte</td> - <td>average</td> - <td>The number of dead bytes (<= used_bytes) for the memory index for this document type</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_index_memory_usage_onhold_bytes">content.proton.documentdb.index.memory_usage.onhold_bytes</p></td> - <td>byte</td> - <td>average</td> - <td>The number of bytes on hold for the memory index for this document type</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_index_io_search_read_bytes">content.proton.documentdb.index.io.search.read_bytes</p></td> - <td>byte</td> - <td>count, sum</td> - <td>Bytes read from disk index posting list and bitvector files as part of search for this document type</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_index_io_search_cached_read_bytes">content.proton.documentdb.index.io.search.cached_read_bytes</p></td> - <td>byte</td> - <td>count, sum</td> - <td>Bytes read from cached disk index posting list and bitvector files as part of search for this document type</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_ready_index_disk_usage">content.proton.documentdb.ready.index.disk_usage</p></td> - <td>byte</td> - <td>average</td> - <td>Disk space usage (in bytes) of this index field in all disk indexes for this document type</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_matching_queries">content.proton.documentdb.matching.queries</p></td> - <td>query</td> - <td>rate</td> - <td>Number of queries executed</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_matching_approximate_nns_timed_out_queries">content.proton.documentdb.matching.approximate_nns_timed_out_queries</p></td> - <td>query</td> - <td>rate</td> - <td>Number of queries hitting the approximate nearest-neighbor search timeout</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_matching_soft_doomed_queries">content.proton.documentdb.matching.soft_doomed_queries</p></td> - <td>query</td> - <td>rate</td> - <td>Number of queries hitting the soft timeout</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_matching_query_latency">content.proton.documentdb.matching.query_latency</p></td> - <td>second</td> - <td>count, max, sum</td> - <td>Total average latency (sec) when matching and ranking a query</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_matching_query_setup_time">content.proton.documentdb.matching.query_setup_time</p></td> - <td>second</td> - <td>count, max, sum</td> - <td>Average time (sec) spent setting up and tearing down queries</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_matching_query_approximate_nns_time">content.proton.documentdb.matching.query_approximate_nns_time</p></td> - <td>second</td> - <td>count, max, sum</td> - <td>Average time (sec) spent on approximate nearest-neighbor search in queries that perform such searches</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_matching_docs_matched">content.proton.documentdb.matching.docs_matched</p></td> - <td>document</td> - <td>count, rate</td> - <td>Number of documents matched</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_matching_exact_nns_distances_computed">content.proton.documentdb.matching.exact_nns_distances_computed</p></td> - <td>distance</td> - <td>rate</td> - <td>Number of distances computed in exact nearest-neighbor search</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_matching_approximate_nns_distances_computed">content.proton.documentdb.matching.approximate_nns_distances_computed</p></td> - <td>distance</td> - <td>rate</td> - <td>Number of distances computed in approximate nearest-neighbor search</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_matching_approximate_nns_nodes_visited">content.proton.documentdb.matching.approximate_nns_nodes_visited</p></td> - <td>graph_node</td> - <td>rate</td> - <td>Number of nodes visited in approximate nearest-neighbor search</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_matching_rank_profile_queries">content.proton.documentdb.matching.rank_profile.queries</p></td> - <td>query</td> - <td>rate</td> - <td>Number of queries executed</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_matching_rank_profile_approximate_nns_timed_out_queries">content.proton.documentdb.matching.rank_profile.approximate_nns_timed_out_queries</p></td> - <td>query</td> - <td>rate</td> - <td>Number of queries hitting the approximate nearest-neighbor search timeout</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_matching_rank_profile_soft_doomed_queries">content.proton.documentdb.matching.rank_profile.soft_doomed_queries</p></td> - <td>query</td> - <td>rate</td> - <td>Number of queries hitting the soft timeout</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_matching_rank_profile_soft_doom_factor">content.proton.documentdb.matching.rank_profile.soft_doom_factor</p></td> - <td>fraction</td> - <td>count, max, min, sum</td> - <td>Factor used to compute soft-timeout</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_matching_rank_profile_query_latency">content.proton.documentdb.matching.rank_profile.query_latency</p></td> - <td>second</td> - <td>count, max, sum</td> - <td>Total average latency (sec) when matching and ranking a query</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_matching_rank_profile_query_setup_time">content.proton.documentdb.matching.rank_profile.query_setup_time</p></td> - <td>second</td> - <td>count, max, sum</td> - <td>Average time (sec) spent setting up and tearing down queries</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_matching_rank_profile_query_approximate_nns_time">content.proton.documentdb.matching.rank_profile.query_approximate_nns_time</p></td> - <td>second</td> - <td>count, max, sum</td> - <td>Average time (sec) spent on approximate nearest-neighbor search in queries that perform such searches</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_matching_rank_profile_grouping_time">content.proton.documentdb.matching.rank_profile.grouping_time</p></td> - <td>second</td> - <td>count, max, sum</td> - <td>Average time (sec) spent on grouping</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_matching_rank_profile_rerank_time">content.proton.documentdb.matching.rank_profile.rerank_time</p></td> - <td>second</td> - <td>count, max, sum</td> - <td>Average time (sec) spent on 2nd phase ranking</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_matching_rank_profile_docs_matched">content.proton.documentdb.matching.rank_profile.docs_matched</p></td> - <td>document</td> - <td>count, rate</td> - <td>Number of documents matched</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_matching_rank_profile_exact_nns_distances_computed">content.proton.documentdb.matching.rank_profile.exact_nns_distances_computed</p></td> - <td>distance</td> - <td>rate</td> - <td>Number of distances computed in exact nearest-neighbor search</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_matching_rank_profile_approximate_nns_distances_computed">content.proton.documentdb.matching.rank_profile.approximate_nns_distances_computed</p></td> - <td>distance</td> - <td>rate</td> - <td>Number of distances computed in approximate nearest-neighbor search</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_matching_rank_profile_approximate_nns_nodes_visited">content.proton.documentdb.matching.rank_profile.approximate_nns_nodes_visited</p></td> - <td>graph_node</td> - <td>rate</td> - <td>Number of nodes visited in approximate nearest-neighbor search</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_matching_rank_profile_limited_queries">content.proton.documentdb.matching.rank_profile.limited_queries</p></td> - <td>query</td> - <td>rate</td> - <td>Number of queries limited in match phase</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_feeding_commit_operations">content.proton.documentdb.feeding.commit.operations</p></td> - <td>operation</td> - <td>count, max, rate, sum</td> - <td>Number of operations included in a commit</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_feeding_commit_latency">content.proton.documentdb.feeding.commit.latency</p></td> - <td>second</td> - <td>count, max, sum</td> - <td>Latency for commit in seconds</td> - </tr> - </tbody> -</table> - -<h2 id="sentinel-metrics">Sentinel Metrics</h2> -<table class="table"> - <thead> - <tr><th>Name</th><th>Unit</th><th>Suffixes</th><th>Description</th></tr> - </thead> - <tbody> - <tr> - <td><p id="sentinel_restarts">sentinel.restarts</p></td> - <td>restart</td> - <td>count</td> - <td>Number of service restarts done by the sentinel</td> - </tr> - <tr> - <td><p id="sentinel_totalRestarts">sentinel.totalRestarts</p></td> - <td>restart</td> - <td>last, max, sum</td> - <td>Total number of service restarts done by the sentinel since the sentinel was started</td> - </tr> - <tr> - <td><p id="sentinel_uptime">sentinel.uptime</p></td> - <td>second</td> - <td>last</td> - <td>Time the sentinel has been running</td> - </tr> - <tr> - <td><p id="sentinel_running">sentinel.running</p></td> - <td>instance</td> - <td>count, last</td> - <td>Number of services the sentinel has running currently</td> - </tr> - </tbody> -</table> - -<h2 id="slobrok-metrics">Slobrok Metrics</h2> -<table class="table"> - <thead> - <tr><th>Name</th><th>Unit</th><th>Suffixes</th><th>Description</th></tr> - </thead> - <tbody> - <tr> - <td><p id="slobrok_heartbeats_failed">slobrok.heartbeats.failed</p></td> - <td>request</td> - <td>count</td> - <td>Number of heartbeat requests failed</td> - </tr> - <tr> - <td><p id="slobrok_missing_consensus">slobrok.missing.consensus</p></td> - <td>second</td> - <td>count</td> - <td>Number of seconds without full consensus with all other brokers</td> - </tr> - </tbody> -</table> - -<h2 id="storage-metrics">Storage Metrics</h2> -<table class="table"> - <thead> - <tr><th>Name</th><th>Unit</th><th>Suffixes</th><th>Description</th></tr> - </thead> - <tbody> - <tr> - <td><p id="vds_datastored_alldisks_buckets">vds.datastored.alldisks.buckets</p></td> - <td>bucket</td> - <td>average</td> - <td>Number of buckets managed</td> - </tr> - <tr> - <td><p id="vds_datastored_alldisks_docs">vds.datastored.alldisks.docs</p></td> - <td>document</td> - <td>average</td> - <td>Number of documents stored</td> - </tr> - <tr> - <td><p id="vds_datastored_alldisks_bytes">vds.datastored.alldisks.bytes</p></td> - <td>byte</td> - <td>average</td> - <td>Number of bytes stored</td> - </tr> - <tr> - <td><p id="vds_visitor_allthreads_averagevisitorlifetime">vds.visitor.allthreads.averagevisitorlifetime</p></td> - <td>millisecond</td> - <td>count, max, sum</td> - <td>Average lifetime of a visitor</td> - </tr> - <tr> - <td><p id="vds_visitor_allthreads_averagequeuewait">vds.visitor.allthreads.averagequeuewait</p></td> - <td>millisecond</td> - <td>count, max, sum</td> - <td>Average time an operation spends in input queue.</td> - </tr> - <tr> - <td><p id="vds_visitor_allthreads_queuesize">vds.visitor.allthreads.queuesize</p></td> - <td>operation</td> - <td>count, max, sum</td> - <td>Size of input message queue.</td> - </tr> - <tr> - <td><p id="vds_visitor_allthreads_completed">vds.visitor.allthreads.completed</p></td> - <td>operation</td> - <td>rate</td> - <td>Number of visitors completed</td> - </tr> - <tr> - <td><p id="vds_visitor_allthreads_created">vds.visitor.allthreads.created</p></td> - <td>operation</td> - <td>rate</td> - <td>Number of visitors created.</td> - </tr> - <tr> - <td><p id="vds_visitor_allthreads_failed">vds.visitor.allthreads.failed</p></td> - <td>operation</td> - <td>rate</td> - <td>Number of visitors failed</td> - </tr> - <tr> - <td><p id="vds_visitor_allthreads_averagemessagesendtime">vds.visitor.allthreads.averagemessagesendtime</p></td> - <td>millisecond</td> - <td>count, max, sum</td> - <td>Average time it takes for messages to be sent to their target (and be replied to)</td> - </tr> - <tr> - <td><p id="vds_visitor_allthreads_averageprocessingtime">vds.visitor.allthreads.averageprocessingtime</p></td> - <td>millisecond</td> - <td>count, max, sum</td> - <td>Average time used to process visitor requests</td> - </tr> - <tr> - <td><p id="vds_filestor_queuesize">vds.filestor.queuesize</p></td> - <td>operation</td> - <td>count, max, sum</td> - <td>Size of input message queue.</td> - </tr> - <tr> - <td><p id="vds_filestor_averagequeuewait">vds.filestor.averagequeuewait</p></td> - <td>millisecond</td> - <td>count, max, sum</td> - <td>Average time an operation spends in input queue.</td> - </tr> - <tr> - <td><p id="vds_filestor_active_operations_size">vds.filestor.active_operations.size</p></td> - <td>operation</td> - <td>count, max, sum</td> - <td>Number of concurrent active operations</td> - </tr> - <tr> - <td><p id="vds_filestor_active_operations_latency">vds.filestor.active_operations.latency</p></td> - <td>millisecond</td> - <td>count, max, sum</td> - <td>Latency (in ms) for completed operations</td> - </tr> - <tr> - <td><p id="vds_filestor_throttle_window_size">vds.filestor.throttle_window_size</p></td> - <td>operation</td> - <td>count, max, sum</td> - <td>Current size of async operation throttler window size</td> - </tr> - <tr> - <td><p id="vds_filestor_throttle_waiting_threads">vds.filestor.throttle_waiting_threads</p></td> - <td>thread</td> - <td>count, max, sum</td> - <td>Number of threads waiting to acquire a throttle token</td> - </tr> - <tr> - <td><p id="vds_filestor_throttle_active_tokens">vds.filestor.throttle_active_tokens</p></td> - <td>instance</td> - <td>count, max, sum</td> - <td>Current number of active throttle tokens</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_mergemetadatareadlatency">vds.filestor.allthreads.mergemetadatareadlatency</p></td> - <td>millisecond</td> - <td>count, max, sum</td> - <td>Time spent in a merge step to check metadata of current node to see what data it has.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_mergedatareadlatency">vds.filestor.allthreads.mergedatareadlatency</p></td> - <td>millisecond</td> - <td>count, max, sum</td> - <td>Time spent in a merge step to read data other nodes need.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_mergedatawritelatency">vds.filestor.allthreads.mergedatawritelatency</p></td> - <td>millisecond</td> - <td>count, max, sum</td> - <td>Time spent in a merge step to write data needed to current node.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_merge_put_latency">vds.filestor.allthreads.merge_put_latency</p></td> - <td>millisecond</td> - <td>count, max, sum</td> - <td>Latency of individual puts that are part of merge operations</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_merge_remove_latency">vds.filestor.allthreads.merge_remove_latency</p></td> - <td>millisecond</td> - <td>count, max, sum</td> - <td>Latency of individual removes that are part of merge operations</td> - </tr> - <tr> - <td><p id="vds_filestor_allstripes_throttled_rpc_direct_dispatches">vds.filestor.allstripes.throttled_rpc_direct_dispatches</p></td> - <td>instance</td> - <td>rate</td> - <td>Number of times an RPC thread could not directly dispatch an async operation directly to Proton because it was disallowed by the throttle policy</td> - </tr> - <tr> - <td><p id="vds_filestor_allstripes_throttled_persistence_thread_polls">vds.filestor.allstripes.throttled_persistence_thread_polls</p></td> - <td>instance</td> - <td>rate</td> - <td>Number of times a persistence thread could not immediately dispatch a queued async operation because it was disallowed by the throttle policy</td> - </tr> - <tr> - <td><p id="vds_filestor_allstripes_timeouts_waiting_for_throttle_token">vds.filestor.allstripes.timeouts_waiting_for_throttle_token</p></td> - <td>instance</td> - <td>rate</td> - <td>Number of times a persistence thread timed out waiting for an available throttle policy token</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_put_count">vds.filestor.allthreads.put.count</p></td> - <td>operation</td> - <td>rate</td> - <td>Number of requests processed.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_put_failed">vds.filestor.allthreads.put.failed</p></td> - <td>operation</td> - <td>rate</td> - <td>Number of failed requests.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_put_test_and_set_failed">vds.filestor.allthreads.put.test_and_set_failed</p></td> - <td>operation</td> - <td>rate</td> - <td>Number of operations that were skipped due to a test-and-set condition not met</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_put_latency">vds.filestor.allthreads.put.latency</p></td> - <td>millisecond</td> - <td>count, max, sum</td> - <td>Latency of successful requests.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_put_request_size">vds.filestor.allthreads.put.request_size</p></td> - <td>byte</td> - <td>count, max, sum</td> - <td>Size of requests, in bytes</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_remove_count">vds.filestor.allthreads.remove.count</p></td> - <td>operation</td> - <td>rate</td> - <td>Number of requests processed.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_remove_failed">vds.filestor.allthreads.remove.failed</p></td> - <td>operation</td> - <td>rate</td> - <td>Number of failed requests.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_remove_test_and_set_failed">vds.filestor.allthreads.remove.test_and_set_failed</p></td> - <td>operation</td> - <td>rate</td> - <td>Number of operations that were skipped due to a test-and-set condition not met</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_remove_latency">vds.filestor.allthreads.remove.latency</p></td> - <td>millisecond</td> - <td>count, max, sum</td> - <td>Latency of successful requests.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_remove_request_size">vds.filestor.allthreads.remove.request_size</p></td> - <td>byte</td> - <td>count, max, sum</td> - <td>Size of requests, in bytes</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_get_count">vds.filestor.allthreads.get.count</p></td> - <td>operation</td> - <td>rate</td> - <td>Number of requests processed.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_get_failed">vds.filestor.allthreads.get.failed</p></td> - <td>operation</td> - <td>rate</td> - <td>Number of failed requests.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_get_latency">vds.filestor.allthreads.get.latency</p></td> - <td>millisecond</td> - <td>count, max, sum</td> - <td>Latency of successful requests.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_get_request_size">vds.filestor.allthreads.get.request_size</p></td> - <td>byte</td> - <td>count, max, sum</td> - <td>Size of requests, in bytes</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_update_count">vds.filestor.allthreads.update.count</p></td> - <td>request</td> - <td>rate</td> - <td>Number of requests processed.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_update_failed">vds.filestor.allthreads.update.failed</p></td> - <td>request</td> - <td>rate</td> - <td>Number of failed requests.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_update_test_and_set_failed">vds.filestor.allthreads.update.test_and_set_failed</p></td> - <td>request</td> - <td>rate</td> - <td>Number of requests that were skipped due to a test-and-set condition not met</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_update_latency">vds.filestor.allthreads.update.latency</p></td> - <td>millisecond</td> - <td>count, max, sum</td> - <td>Latency of successful requests.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_update_request_size">vds.filestor.allthreads.update.request_size</p></td> - <td>byte</td> - <td>count, max, sum</td> - <td>Size of requests, in bytes</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_createiterator_count">vds.filestor.allthreads.createiterator.count</p></td> - <td>request</td> - <td>rate</td> - <td>Number of requests processed.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_createiterator_latency">vds.filestor.allthreads.createiterator.latency</p></td> - <td>millisecond</td> - <td>count, max, sum</td> - <td>Latency of successful requests.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_visit_count">vds.filestor.allthreads.visit.count</p></td> - <td>request</td> - <td>rate</td> - <td>Number of requests processed.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_visit_latency">vds.filestor.allthreads.visit.latency</p></td> - <td>millisecond</td> - <td>count, max, sum</td> - <td>Latency of successful requests.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_remove_location_count">vds.filestor.allthreads.remove_location.count</p></td> - <td>request</td> - <td>rate</td> - <td>Number of requests processed.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_remove_location_latency">vds.filestor.allthreads.remove_location.latency</p></td> - <td>millisecond</td> - <td>count, max, sum</td> - <td>Latency of successful requests.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_splitbuckets_count">vds.filestor.allthreads.splitbuckets.count</p></td> - <td>request</td> - <td>rate</td> - <td>Number of requests processed.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_joinbuckets_count">vds.filestor.allthreads.joinbuckets.count</p></td> - <td>request</td> - <td>rate</td> - <td>Number of requests processed.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_deletebuckets_count">vds.filestor.allthreads.deletebuckets.count</p></td> - <td>request</td> - <td>rate</td> - <td>Number of requests processed.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_deletebuckets_failed">vds.filestor.allthreads.deletebuckets.failed</p></td> - <td>request</td> - <td>rate</td> - <td>Number of failed requests.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_deletebuckets_latency">vds.filestor.allthreads.deletebuckets.latency</p></td> - <td>millisecond</td> - <td>count, max, sum</td> - <td>Latency of successful requests.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_remove_by_gid_count">vds.filestor.allthreads.remove_by_gid.count</p></td> - <td>request</td> - <td>rate</td> - <td>Number of requests processed.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_remove_by_gid_failed">vds.filestor.allthreads.remove_by_gid.failed</p></td> - <td>request</td> - <td>rate</td> - <td>Number of failed requests.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_remove_by_gid_latency">vds.filestor.allthreads.remove_by_gid.latency</p></td> - <td>millisecond</td> - <td>count, max, sum</td> - <td>Latency of successful requests.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_setbucketstates_count">vds.filestor.allthreads.setbucketstates.count</p></td> - <td>request</td> - <td>rate</td> - <td>Number of requests processed.</td> - </tr> - <tr> - <td><p id="vds_mergethrottler_averagequeuewaitingtime">vds.mergethrottler.averagequeuewaitingtime</p></td> - <td>millisecond</td> - <td>count, max, sum</td> - <td>Time merges spent in the throttler queue</td> - </tr> - <tr> - <td><p id="vds_mergethrottler_queuesize">vds.mergethrottler.queuesize</p></td> - <td>instance</td> - <td>count, max, sum</td> - <td>Length of merge queue</td> - </tr> - <tr> - <td><p id="vds_mergethrottler_active_window_size">vds.mergethrottler.active_window_size</p></td> - <td>instance</td> - <td>count, max, sum</td> - <td>Number of merges active within the pending window size</td> - </tr> - <tr> - <td><p id="vds_mergethrottler_estimated_merge_memory_usage">vds.mergethrottler.estimated_merge_memory_usage</p></td> - <td>byte</td> - <td>count, max, sum</td> - <td>An estimated upper bound of the memory usage (in bytes) of the merges currently in the active window</td> - </tr> - <tr> - <td><p id="vds_mergethrottler_bounced_due_to_back_pressure">vds.mergethrottler.bounced_due_to_back_pressure</p></td> - <td>instance</td> - <td>rate</td> - <td>Number of merges bounced due to resource exhaustion back-pressure</td> - </tr> - <tr> - <td><p id="vds_mergethrottler_locallyexecutedmerges_ok">vds.mergethrottler.locallyexecutedmerges.ok</p></td> - <td>instance</td> - <td>rate</td> - <td>The number of successful merges for 'locallyexecutedmerges'</td> - </tr> - <tr> - <td><p id="vds_mergethrottler_mergechains_ok">vds.mergethrottler.mergechains.ok</p></td> - <td>operation</td> - <td>rate</td> - <td>The number of successful merges for 'mergechains'</td> - </tr> - <tr> - <td><p id="vds_mergethrottler_mergechains_failures_busy">vds.mergethrottler.mergechains.failures.busy</p></td> - <td>operation</td> - <td>rate</td> - <td>The number of merges that failed because the storage node was busy</td> - </tr> - <tr> - <td><p id="vds_mergethrottler_mergechains_failures_total">vds.mergethrottler.mergechains.failures.total</p></td> - <td>operation</td> - <td>rate</td> - <td>Sum of all failures</td> - </tr> - <tr> - <td><p id="vds_server_network_tls-handshakes-failed">vds.server.network.tls-handshakes-failed</p></td> - <td>operation</td> - <td>count</td> - <td>Number of client or server connection attempts that failed during TLS handshaking</td> - </tr> - <tr> - <td><p id="vds_server_network_peer-authorization-failures">vds.server.network.peer-authorization-failures</p></td> - <td>failure</td> - <td>count</td> - <td>Number of TLS connection attempts failed due to bad or missing peer certificate credentials</td> - </tr> - <tr> - <td><p id="vds_server_network_client_tls-connections-established">vds.server.network.client.tls-connections-established</p></td> - <td>connection</td> - <td>count</td> - <td>Number of secure mTLS connections established</td> - </tr> - <tr> - <td><p id="vds_server_network_server_tls-connections-established">vds.server.network.server.tls-connections-established</p></td> - <td>connection</td> - <td>count</td> - <td>Number of secure mTLS connections established</td> - </tr> - <tr> - <td><p id="vds_server_network_client_insecure-connections-established">vds.server.network.client.insecure-connections-established</p></td> - <td>connection</td> - <td>count</td> - <td>Number of insecure (plaintext) connections established</td> - </tr> - <tr> - <td><p id="vds_server_network_server_insecure-connections-established">vds.server.network.server.insecure-connections-established</p></td> - <td>connection</td> - <td>count</td> - <td>Number of insecure (plaintext) connections established</td> - </tr> - <tr> - <td><p id="vds_server_network_tls-connections-broken">vds.server.network.tls-connections-broken</p></td> - <td>connection</td> - <td>count</td> - <td>Number of TLS connections broken due to failures during frame encoding or decoding</td> - </tr> - <tr> - <td><p id="vds_server_network_failed-tls-config-reloads">vds.server.network.failed-tls-config-reloads</p></td> - <td>failure</td> - <td>count</td> - <td>Number of times background reloading of TLS config has failed</td> - </tr> - <tr> - <td><p id="vds_server_network_rpc-capability-checks-failed">vds.server.network.rpc-capability-checks-failed</p></td> - <td>failure</td> - <td>count</td> - <td>Number of RPC operations that failed due to one or more missing capabilities</td> - </tr> - <tr> - <td><p id="vds_server_network_status-capability-checks-failed">vds.server.network.status-capability-checks-failed</p></td> - <td>failure</td> - <td>count</td> - <td>Number of status page operations that failed due to one or more missing capabilities</td> - </tr> - <tr> - <td><p id="vds_server_fnet_num-connections">vds.server.fnet.num-connections</p></td> - <td>connection</td> - <td>count</td> - <td>Total number of connection objects</td> - </tr> - </tbody> -</table> diff --git a/en/reference/operations/metrics/vespa-metric-set.mdx b/en/reference/operations/metrics/vespa-metric-set.mdx new file mode 100644 index 0000000000..34577141a5 --- /dev/null +++ b/en/reference/operations/metrics/vespa-metric-set.mdx @@ -0,0 +1,3260 @@ +--- +title: "Vespa Metric Set" +sidebarTitle: "Vespa metric set" +--- +This document provides reference documentation for the Vespa metric set, including suffixes present per metric. If the suffix column contains "N/A" then the base name of the corresponding metric is used with no suffix. + +## ClusterController Metrics + +<table className="vespa-table"> + <thead> + <tr> + <th>Name</th> + <th>Unit</th> + <th>Suffixes</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>cluster-controller.down.count</td> + <td>node</td> + <td>last, max</td> + <td>Number of content nodes down</td> + </tr> + <tr> + <td>cluster-controller.initializing.count</td> + <td>node</td> + <td>last, max</td> + <td>Number of content nodes initializing</td> + </tr> + <tr> + <td>cluster-controller.maintenance.count</td> + <td>node</td> + <td>last, max</td> + <td>Number of content nodes in maintenance</td> + </tr> + <tr> + <td>cluster-controller.retired.count</td> + <td>node</td> + <td>last, max</td> + <td>Number of content nodes that are retired</td> + </tr> + <tr> + <td>cluster-controller.stopping.count</td> + <td>node</td> + <td>last</td> + <td>Number of content nodes currently stopping</td> + </tr> + <tr> + <td>cluster-controller.up.count</td> + <td>node</td> + <td>last, max</td> + <td>Number of content nodes up</td> + </tr> + <tr> + <td>cluster-controller.nodes-not-converged</td> + <td>node</td> + <td>max</td> + <td>Number of nodes not converging to the latest cluster state version</td> + </tr> + <tr> + <td>cluster-controller.stored-document-count</td> + <td>document</td> + <td>max</td> + <td>Total number of unique documents stored in the cluster</td> + </tr> + <tr> + <td>cluster-controller.stored-document-bytes</td> + <td>byte</td> + <td>max</td> + <td>Combined byte size of all unique documents stored in the cluster (not including replication)</td> + </tr> + <tr> + <td>cluster-controller.cluster-buckets-out-of-sync-ratio</td> + <td>fraction</td> + <td>max</td> + <td>Ratio of buckets in the cluster currently in need of syncing</td> + </tr> + <tr> + <td>cluster-controller.busy-tick-time-ms</td> + <td>millisecond</td> + <td>count, last, max, sum</td> + <td>Time busy</td> + </tr> + <tr> + <td>cluster-controller.idle-tick-time-ms</td> + <td>millisecond</td> + <td>count, last, max, sum</td> + <td>Time idle</td> + </tr> + <tr> + <td>cluster-controller.work-ms</td> + <td>millisecond</td> + <td>count, last, sum</td> + <td>Time used for actual work</td> + </tr> + <tr> + <td>cluster-controller.is-master</td> + <td>binary</td> + <td>last, max</td> + <td>1 if this cluster controller is currently the master, or 0 if not</td> + </tr> + <tr> + <td>cluster-controller.remote-task-queue.size</td> + <td>operation</td> + <td>last</td> + <td>Number of remote tasks queued</td> + </tr> + <tr> + <td>cluster-controller.resource_usage.nodes_above_limit</td> + <td>node</td> + <td>last, max</td> + <td>The number of content nodes above resource limit, blocking feed</td> + </tr> + <tr> + <td>cluster-controller.resource_usage.max_memory_utilization</td> + <td>fraction</td> + <td>last, max</td> + <td>Current memory utilisation, for content node with the highest value</td> + </tr> + <tr> + <td>cluster-controller.resource_usage.max_disk_utilization</td> + <td>fraction</td> + <td>last, max</td> + <td>Current disk space utilisation, for content node with the highest value</td> + </tr> + <tr> + <td>cluster-controller.resource_usage.memory_limit</td> + <td>fraction</td> + <td>last, max</td> + <td>Memory space limit as a fraction of available memory</td> + </tr> + <tr> + <td>cluster-controller.resource_usage.disk_limit</td> + <td>fraction</td> + <td>last, max</td> + <td>Disk space limit as a fraction of available disk space</td> + </tr> + <tr> + <td>reindexing.progress</td> + <td>fraction</td> + <td>last, max</td> + <td>Re-indexing progress</td> + </tr> + </tbody> +</table> + +## Container Metrics + +<table className="vespa-table"> + <thead> + <tr> + <th>Name</th> + <th>Unit</th> + <th>Suffixes</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>http.status.1xx</td> + <td>response</td> + <td>rate</td> + <td>Number of responses with a 1xx status</td> + </tr> + <tr> + <td>http.status.2xx</td> + <td>response</td> + <td>rate</td> + <td>Number of responses with a 2xx status</td> + </tr> + <tr> + <td>http.status.3xx</td> + <td>response</td> + <td>rate</td> + <td>Number of responses with a 3xx status</td> + </tr> + <tr> + <td>http.status.4xx</td> + <td>response</td> + <td>rate</td> + <td>Number of responses with a 4xx status</td> + </tr> + <tr> + <td>http.status.5xx</td> + <td>response</td> + <td>rate</td> + <td>Number of responses with a 5xx status</td> + </tr> + <tr> + <td>application_generation</td> + <td>version</td> + <td>N/A</td> + <td>The currently live application config generation (aka session id)</td> + </tr> + <tr> + <td>jdisc.gc.count</td> + <td>operation</td> + <td>average, last, max</td> + <td>Number of JVM garbage collections done</td> + </tr> + <tr> + <td>jdisc.gc.ms</td> + <td>millisecond</td> + <td>average, last, max</td> + <td>Time spent in JVM garbage collection</td> + </tr> + <tr> + <td>jdisc.jvm</td> + <td>version</td> + <td>last</td> + <td>JVM runtime version</td> + </tr> + <tr> + <td>jdisc.memory_mappings</td> + <td>operation</td> + <td>max</td> + <td>JDISC Memory mappings</td> + </tr> + <tr> + <td>jdisc.open_file_descriptors</td> + <td>item</td> + <td>max</td> + <td>JDISC Open file descriptors</td> + </tr> + <tr> + <td>jdisc.thread_pool.unhandled_exceptions</td> + <td>thread</td> + <td>count, last, max, min, sum</td> + <td>Number of exceptions thrown by tasks</td> + </tr> + <tr> + <td>jdisc.thread_pool.work_queue.capacity</td> + <td>thread</td> + <td>count, last, max, min, sum</td> + <td>Capacity of the task queue</td> + </tr> + <tr> + <td>jdisc.thread_pool.work_queue.size</td> + <td>thread</td> + <td>count, last, max, min, sum</td> + <td>Size of the task queue</td> + </tr> + <tr> + <td>jdisc.thread_pool.rejected_tasks</td> + <td>thread</td> + <td>count, last, max, min, sum</td> + <td>Number of tasks rejected by the thread pool</td> + </tr> + <tr> + <td>jdisc.thread_pool.size</td> + <td>thread</td> + <td>count, last, max, min, sum</td> + <td>Size of the thread pool</td> + </tr> + <tr> + <td>jdisc.thread_pool.max_allowed_size</td> + <td>thread</td> + <td>count, last, max, min, sum</td> + <td>The maximum allowed number of threads in the pool</td> + </tr> + <tr> + <td>jdisc.thread_pool.active_threads</td> + <td>thread</td> + <td>count, last, max, min, sum</td> + <td>Number of threads that are active</td> + </tr> + <tr> + <td>jdisc.deactivated_containers.total</td> + <td>item</td> + <td>last, sum</td> + <td>JDISC Deactivated container instances</td> + </tr> + <tr> + <td>jdisc.deactivated_containers.with_retained_refs.last</td> + <td>item</td> + <td>last</td> + <td>JDISC Deactivated container nodes with retained refs</td> + </tr> + <tr> + <td>jdisc.application.failed_component_graphs</td> + <td>item</td> + <td>rate</td> + <td>JDISC Application failed component graphs</td> + </tr> + <tr> + <td>jdisc.application.component_graph.creation_time_millis</td> + <td>millisecond</td> + <td>last</td> + <td>JDISC Application component graph creation time</td> + </tr> + <tr> + <td>jdisc.application.component_graph.reconfigurations</td> + <td>item</td> + <td>rate</td> + <td>JDISC Application component graph reconfigurations</td> + </tr> + <tr> + <td>jdisc.singleton.is_active</td> + <td>item</td> + <td>last, max, min</td> + <td>JDISC Singleton is active</td> + </tr> + <tr> + <td>jdisc.singleton.activation.count</td> + <td>operation</td> + <td>last</td> + <td>JDISC Singleton activations</td> + </tr> + <tr> + <td>jdisc.singleton.activation.failure.count</td> + <td>operation</td> + <td>last</td> + <td>JDISC Singleton activation failures</td> + </tr> + <tr> + <td>jdisc.singleton.activation.millis</td> + <td>millisecond</td> + <td>last</td> + <td>JDISC Singleton activation time</td> + </tr> + <tr> + <td>jdisc.singleton.deactivation.count</td> + <td>operation</td> + <td>last</td> + <td>JDISC Singleton deactivations</td> + </tr> + <tr> + <td>jdisc.singleton.deactivation.failure.count</td> + <td>operation</td> + <td>last</td> + <td>JDISC Singleton deactivation failures</td> + </tr> + <tr> + <td>jdisc.singleton.deactivation.millis</td> + <td>millisecond</td> + <td>last</td> + <td>JDISC Singleton deactivation time</td> + </tr> + <tr> + <td>jdisc.http.ssl.handshake.failure.missing_client_cert</td> + <td>operation</td> + <td>rate</td> + <td>JDISC HTTP SSL Handshake failures due to missing client certificate</td> + </tr> + <tr> + <td>jdisc.http.ssl.handshake.failure.expired_client_cert</td> + <td>operation</td> + <td>rate</td> + <td>JDISC HTTP SSL Handshake failures due to expired client certificate</td> + </tr> + <tr> + <td>jdisc.http.ssl.handshake.failure.invalid_client_cert</td> + <td>operation</td> + <td>rate</td> + <td>JDISC HTTP SSL Handshake failures due to invalid client certificate</td> + </tr> + <tr> + <td>jdisc.http.ssl.handshake.failure.incompatible_protocols</td> + <td>operation</td> + <td>rate</td> + <td>JDISC HTTP SSL Handshake failures due to incompatible protocols</td> + </tr> + <tr> + <td>jdisc.http.ssl.handshake.failure.incompatible_chifers</td> + <td>operation</td> + <td>rate</td> + <td>JDISC HTTP SSL Handshake failures due to incompatible chifers</td> + </tr> + <tr> + <td>jdisc.http.ssl.handshake.failure.connection_closed</td> + <td>operation</td> + <td>rate</td> + <td>JDISC HTTP SSL Handshake failures due to connection closed</td> + </tr> + <tr> + <td>jdisc.http.ssl.handshake.failure.unknown</td> + <td>operation</td> + <td>rate</td> + <td>JDISC HTTP SSL Handshake failures for unknown reason</td> + </tr> + <tr> + <td>jdisc.http.latency</td> + <td>millisecond</td> + <td>count, max, sum</td> + <td>Request latency including the HTTP layer</td> + </tr> + <tr> + <td>jdisc.http.request.prematurely_closed</td> + <td>request</td> + <td>rate</td> + <td>HTTP requests prematurely closed</td> + </tr> + <tr> + <td>jdisc.http.request.requests_per_connection</td> + <td>request</td> + <td>average, count, max, min, sum</td> + <td>HTTP requests per connection</td> + </tr> + <tr> + <td>jdisc.http.request.uri_length</td> + <td>byte</td> + <td>count, max, sum</td> + <td>HTTP URI length</td> + </tr> + <tr> + <td>jdisc.http.request.content_size</td> + <td>byte</td> + <td>count, max, sum</td> + <td>HTTP request content size</td> + </tr> + <tr> + <td>jdisc.http.requests</td> + <td>request</td> + <td>count, rate</td> + <td>HTTP requests</td> + </tr> + <tr> + <td>jdisc.http.filter.rule.blocked_requests</td> + <td>request</td> + <td>rate</td> + <td>Number of requests blocked by filter</td> + </tr> + <tr> + <td>jdisc.http.filter.rule.allowed_requests</td> + <td>request</td> + <td>rate</td> + <td>Number of requests allowed by filter</td> + </tr> + <tr> + <td>jdisc.http.filtering.request.handled</td> + <td>request</td> + <td>rate</td> + <td>Number of filtering requests handled</td> + </tr> + <tr> + <td>jdisc.http.filtering.request.unhandled</td> + <td>request</td> + <td>rate</td> + <td>Number of filtering requests unhandled</td> + </tr> + <tr> + <td>jdisc.http.filtering.response.handled</td> + <td>request</td> + <td>rate</td> + <td>Number of filtering responses handled</td> + </tr> + <tr> + <td>jdisc.http.filtering.response.unhandled</td> + <td>request</td> + <td>rate</td> + <td>Number of filtering responses unhandled</td> + </tr> + <tr> + <td>jdisc.http.handler.unhandled_exceptions</td> + <td>request</td> + <td>rate</td> + <td>Number of unhandled exceptions in handler</td> + </tr> + <tr> + <td>jdisc.tls.capability_checks.succeeded</td> + <td>operation</td> + <td>rate</td> + <td>Number of TLS capability checks succeeded</td> + </tr> + <tr> + <td>jdisc.tls.capability_checks.failed</td> + <td>operation</td> + <td>rate</td> + <td>Number of TLS capability checks failed</td> + </tr> + <tr> + <td>jdisc.http.jetty.threadpool.thread.max</td> + <td>thread</td> + <td>count, last, max, min, sum</td> + <td>Configured maximum number of threads</td> + </tr> + <tr> + <td>jdisc.http.jetty.threadpool.thread.min</td> + <td>thread</td> + <td>count, last, max, min, sum</td> + <td>Configured minimum number of threads</td> + </tr> + <tr> + <td>jdisc.http.jetty.threadpool.thread.reserved</td> + <td>thread</td> + <td>count, last, max, min, sum</td> + <td>Configured number of reserved threads or -1 for heuristic</td> + </tr> + <tr> + <td>jdisc.http.jetty.threadpool.thread.busy</td> + <td>thread</td> + <td>count, last, max, min, sum</td> + <td>Number of threads executing internal and transient jobs</td> + </tr> + <tr> + <td>jdisc.http.jetty.threadpool.thread.total</td> + <td>thread</td> + <td>count, last, max, min, sum</td> + <td>Current number of threads</td> + </tr> + <tr> + <td>jdisc.http.jetty.threadpool.queue.size</td> + <td>thread</td> + <td>count, last, max, min, sum</td> + <td>Current size of the job queue</td> + </tr> + <tr> + <td>jdisc.http.jetty.http_compliance.violation</td> + <td>failure</td> + <td>rate</td> + <td>Number of HTTP compliance violations</td> + </tr> + <tr> + <td>serverNumOpenConnections</td> + <td>connection</td> + <td>average, last, max</td> + <td>The number of currently open connections</td> + </tr> + <tr> + <td>serverNumConnections</td> + <td>connection</td> + <td>average, last, max</td> + <td>The total number of connections opened</td> + </tr> + <tr> + <td>serverBytesReceived</td> + <td>byte</td> + <td>count, sum</td> + <td>The number of bytes received by the server</td> + </tr> + <tr> + <td>serverBytesSent</td> + <td>byte</td> + <td>count, sum</td> + <td>The number of bytes sent from the server</td> + </tr> + <tr> + <td>handled.requests</td> + <td>operation</td> + <td>count</td> + <td>The number of requests handled per metrics snapshot</td> + </tr> + <tr> + <td>handled.latency</td> + <td>millisecond</td> + <td>count, max, sum</td> + <td>The time used for handling requests, excluding HTTP layer and rendering</td> + </tr> + <tr> + <td>httpapi_latency</td> + <td>millisecond</td> + <td>count, max, sum</td> + <td>Duration for requests to the HTTP document APIs</td> + </tr> + <tr> + <td>httpapi_pending</td> + <td>operation</td> + <td>count, max, sum</td> + <td>Document operations pending execution</td> + </tr> + <tr> + <td>httpapi_num_operations</td> + <td>operation</td> + <td>rate</td> + <td>Total number of document operations performed</td> + </tr> + <tr> + <td>httpapi_num_updates</td> + <td>operation</td> + <td>rate</td> + <td>Document update operations performed</td> + </tr> + <tr> + <td>httpapi_num_removes</td> + <td>operation</td> + <td>rate</td> + <td>Document remove operations performed</td> + </tr> + <tr> + <td>httpapi_num_puts</td> + <td>operation</td> + <td>rate</td> + <td>Document put operations performed</td> + </tr> + <tr> + <td>httpapi_succeeded</td> + <td>operation</td> + <td>rate</td> + <td>Document operations that succeeded</td> + </tr> + <tr> + <td>httpapi_failed</td> + <td>operation</td> + <td>rate</td> + <td>Document operations that failed</td> + </tr> + <tr> + <td>httpapi_parse_error</td> + <td>operation</td> + <td>rate</td> + <td>Document operations that failed due to document parse errors</td> + </tr> + <tr> + <td>httpapi_condition_not_met</td> + <td>operation</td> + <td>rate</td> + <td>Document operations not applied due to condition not met</td> + </tr> + <tr> + <td>httpapi_not_found</td> + <td>operation</td> + <td>rate</td> + <td>Document operations not applied due to document not found</td> + </tr> + <tr> + <td>httpapi_failed_unknown</td> + <td>operation</td> + <td>rate</td> + <td>Document operations failed by unknown cause</td> + </tr> + <tr> + <td>httpapi_failed_timeout</td> + <td>operation</td> + <td>rate</td> + <td>Document operations failed by timeout</td> + </tr> + <tr> + <td>httpapi_failed_insufficient_storage</td> + <td>operation</td> + <td>rate</td> + <td>Document operations failed by insufficient storage</td> + </tr> + <tr> + <td>httpapi_queued_operations</td> + <td>operation</td> + <td>last</td> + <td>Document operations queued for execution in /document/v1 API handler</td> + </tr> + <tr> + <td>httpapi_queued_bytes</td> + <td>byte</td> + <td>last</td> + <td>Total operation bytes queued for execution in /document/v1 API handler</td> + </tr> + <tr> + <td>httpapi_queued_age</td> + <td>second</td> + <td>last</td> + <td>Age in seconds of the oldest operation in the queue for /document/v1 API handler</td> + </tr> + <tr> + <td>httpapi_mbus_window_size</td> + <td>operation</td> + <td>last</td> + <td>The window size of Messagebus's dynamic throttle policy for /document/v1 API handler</td> + </tr> + <tr> + <td>mem.heap.total</td> + <td>byte</td> + <td>average</td> + <td>Total available heap memory</td> + </tr> + <tr> + <td>mem.heap.free</td> + <td>byte</td> + <td>average</td> + <td>Free heap memory</td> + </tr> + <tr> + <td>mem.heap.used</td> + <td>byte</td> + <td>average, max</td> + <td>Currently used heap memory</td> + </tr> + <tr> + <td>mem.direct.total</td> + <td>byte</td> + <td>average</td> + <td>Total available direct memory</td> + </tr> + <tr> + <td>mem.direct.free</td> + <td>byte</td> + <td>average</td> + <td>Currently free direct memory</td> + </tr> + <tr> + <td>mem.direct.used</td> + <td>byte</td> + <td>average, max</td> + <td>Direct memory currently used</td> + </tr> + <tr> + <td>mem.direct.count</td> + <td>byte</td> + <td>max</td> + <td>Number of direct memory allocations</td> + </tr> + <tr> + <td>mem.native.total</td> + <td>byte</td> + <td>average</td> + <td>Total available native memory</td> + </tr> + <tr> + <td>mem.native.free</td> + <td>byte</td> + <td>average</td> + <td>Currently free native memory</td> + </tr> + <tr> + <td>mem.native.used</td> + <td>byte</td> + <td>average</td> + <td>Native memory currently used</td> + </tr> + <tr> + <td>athenz-tenant-cert.expiry.seconds</td> + <td>second</td> + <td>last, max, min</td> + <td>Time remaining until Athenz tenant certificate expires</td> + </tr> + <tr> + <td>container-iam-role.expiry.seconds</td> + <td>second</td> + <td>N/A</td> + <td>Time remaining until IAM role expires</td> + </tr> + <tr> + <td>peak_qps</td> + <td>query_per_second</td> + <td>max</td> + <td>The highest number of qps for a second for this metrics snapshot</td> + </tr> + <tr> + <td>search_connections</td> + <td>connection</td> + <td>count, max, sum</td> + <td>Number of search connections</td> + </tr> + <tr> + <td>feed.operations</td> + <td>operation</td> + <td>rate</td> + <td>Number of document feed operations</td> + </tr> + <tr> + <td>feed.latency</td> + <td>millisecond</td> + <td>count, max, sum</td> + <td>Feed latency</td> + </tr> + <tr> + <td>feed.http-requests</td> + <td>operation</td> + <td>count, rate</td> + <td>Feed HTTP requests</td> + </tr> + <tr> + <td>queries</td> + <td>operation</td> + <td>rate</td> + <td>Query volume</td> + </tr> + <tr> + <td>query_container_latency</td> + <td>millisecond</td> + <td>count, max, sum</td> + <td>The query execution time consumed in the container</td> + </tr> + <tr> + <td>query_latency</td> + <td>millisecond</td> + <td>count, max, sum</td> + <td>The overall query latency as observed by the container cluster, excluding HTTP layer and rendering</td> + </tr> + <tr> + <td>query_timeout</td> + <td>millisecond</td> + <td>count, max, min, sum</td> + <td>The amount of time allowed for query execution, from the client</td> + </tr> + <tr> + <td>failed_queries</td> + <td>operation</td> + <td>rate</td> + <td>The number of failed queries</td> + </tr> + <tr> + <td>degraded_queries</td> + <td>operation</td> + <td>rate</td> + <td>The number of degraded queries, e.g. due to some content nodes not responding in time</td> + </tr> + <tr> + <td>hits_per_query</td> + <td>hit_per_query</td> + <td>count, max, sum</td> + <td>The number of hits returned</td> + </tr> + <tr> + <td>query_hit_offset</td> + <td>hit</td> + <td>count, max, sum</td> + <td>The offset for hits returned</td> + </tr> + <tr> + <td>documents_covered</td> + <td>document</td> + <td>count</td> + <td>The combined number of documents considered during query evaluation</td> + </tr> + <tr> + <td>documents_total</td> + <td>document</td> + <td>count</td> + <td>The number of documents to be evaluated if all requests had been fully executed</td> + </tr> + <tr> + <td>documents_target_total</td> + <td>document</td> + <td>count</td> + <td>The target number of total documents to be evaluated when all data is in sync</td> + </tr> + <tr> + <td>jdisc.render.latency</td> + <td>nanosecond</td> + <td>average, count, last, max, min, sum</td> + <td>The time used by the container to render responses</td> + </tr> + <tr> + <td>query_item_count</td> + <td>item</td> + <td>count, max, sum</td> + <td>The number of query items (terms, phrases, etc.)</td> + </tr> + <tr> + <td>docproc.proctime</td> + <td>millisecond</td> + <td>count, max, sum</td> + <td>Time spent processing document</td> + </tr> + <tr> + <td>docproc.documents</td> + <td>document</td> + <td>count, max, min, sum</td> + <td>Number of processed documents</td> + </tr> + <tr> + <td>totalhits_per_query</td> + <td>hit_per_query</td> + <td>count, max, sum</td> + <td>The total number of documents found to match queries</td> + </tr> + <tr> + <td>empty_results</td> + <td>operation</td> + <td>rate</td> + <td>Number of queries matching no documents</td> + </tr> + <tr> + <td>requestsOverQuota</td> + <td>operation</td> + <td>count, rate</td> + <td>The number of requests rejected due to exceeding quota</td> + </tr> + <tr> + <td>relevance.at_1</td> + <td>score</td> + <td>count, sum</td> + <td>The relevance of hit number 1</td> + </tr> + <tr> + <td>relevance.at_3</td> + <td>score</td> + <td>count, sum</td> + <td>The relevance of hit number 3</td> + </tr> + <tr> + <td>relevance.at_10</td> + <td>score</td> + <td>count, sum</td> + <td>The relevance of hit number 10</td> + </tr> + <tr> + <td>error.timeout</td> + <td>operation</td> + <td>rate</td> + <td>Requests that timed out</td> + </tr> + <tr> + <td>error.backends_oos</td> + <td>operation</td> + <td>rate</td> + <td>Requests that failed due to no available backends nodes</td> + </tr> + <tr> + <td>error.plugin_failure</td> + <td>operation</td> + <td>rate</td> + <td>Requests that failed due to plugin failure</td> + </tr> + <tr> + <td>error.backend_communication_error</td> + <td>operation</td> + <td>rate</td> + <td>Requests that failed due to backend communication error</td> + </tr> + <tr> + <td>error.empty_document_summaries</td> + <td>operation</td> + <td>rate</td> + <td>Requests that failed due to missing document summaries</td> + </tr> + <tr> + <td>error.invalid_query_parameter</td> + <td>operation</td> + <td>rate</td> + <td>Requests that failed due to invalid query parameters</td> + </tr> + <tr> + <td>error.internal_server_error</td> + <td>operation</td> + <td>rate</td> + <td>Requests that failed due to internal server error</td> + </tr> + <tr> + <td>error.misconfigured_server</td> + <td>operation</td> + <td>rate</td> + <td>Requests that failed due to misconfigured server</td> + </tr> + <tr> + <td>error.invalid_query_transformation</td> + <td>operation</td> + <td>rate</td> + <td>Requests that failed due to invalid query transformation</td> + </tr> + <tr> + <td>error.results_with_errors</td> + <td>operation</td> + <td>rate</td> + <td>The number of queries with error payload</td> + </tr> + <tr> + <td>error.unspecified</td> + <td>operation</td> + <td>rate</td> + <td>Requests that failed for an unspecified reason</td> + </tr> + <tr> + <td>error.unhandled_exception</td> + <td>operation</td> + <td>rate</td> + <td>Requests that failed due to an unhandled exception</td> + </tr> + <tr> + <td>serverRejectedRequests</td> + <td>operation</td> + <td>count, rate</td> + <td>Deprecated. Use jdisc.thread_pool.rejected_tasks instead.</td> + </tr> + <tr> + <td>serverThreadPoolSize</td> + <td>thread</td> + <td>last, max</td> + <td>Deprecated. Use jdisc.thread_pool.size instead.</td> + </tr> + <tr> + <td>serverActiveThreads</td> + <td>thread</td> + <td>count, last, max, min, sum</td> + <td>Deprecated. Use jdisc.thread_pool.active_threads instead.</td> + </tr> + <tr> + <td>jrt.transport.tls-certificate-verification-failures</td> + <td>failure</td> + <td>N/A</td> + <td>TLS certificate verification failures</td> + </tr> + <tr> + <td>jrt.transport.peer-authorization-failures</td> + <td>failure</td> + <td>N/A</td> + <td>TLS peer authorization failures</td> + </tr> + <tr> + <td>jrt.transport.server.tls-connections-established</td> + <td>connection</td> + <td>N/A</td> + <td>TLS server connections established</td> + </tr> + <tr> + <td>jrt.transport.client.tls-connections-established</td> + <td>connection</td> + <td>N/A</td> + <td>TLS client connections established</td> + </tr> + <tr> + <td>jrt.transport.server.unencrypted-connections-established</td> + <td>connection</td> + <td>N/A</td> + <td>Unencrypted server connections established</td> + </tr> + <tr> + <td>jrt.transport.client.unencrypted-connections-established</td> + <td>connection</td> + <td>N/A</td> + <td>Unencrypted client connections established</td> + </tr> + <tr> + <td>embedder.latency</td> + <td>millisecond</td> + <td>count, max, sum</td> + <td>Time spent creating an embedding</td> + </tr> + <tr> + <td>embedder.sequence_length</td> + <td>item</td> + <td>count, max, sum</td> + <td>Number of tokens in the input sequence</td> + </tr> + <tr> + <td>embedder.request.count</td> + <td>request</td> + <td>count</td> + <td>Number of embedder API requests</td> + </tr> + <tr> + <td>embedder.request.failure.count</td> + <td>request</td> + <td>count</td> + <td>Number of failed embedder API requests</td> + </tr> + <tr> + <td>embedder.batch.size</td> + <td>item</td> + <td>count, max, sum</td> + <td>Number of items in each dispatched batch</td> + </tr> + <tr> + <td>embedder.batch.queue_time</td> + <td>millisecond</td> + <td>count, max, sum</td> + <td>Time spent waiting in queue before batch dispatch</td> + </tr> + <tr> + <td>embedder.batch.count</td> + <td>operation</td> + <td>count</td> + <td>Number of batch dispatches</td> + </tr> + </tbody> +</table> + +## Distributor Metrics + +<table className="vespa-table"> + <thead> + <tr> + <th>Name</th> + <th>Unit</th> + <th>Suffixes</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>vds.idealstate.buckets_rechecking</td> + <td>bucket</td> + <td>average</td> + <td>The number of buckets that we are rechecking for ideal state operations</td> + </tr> + <tr> + <td>vds.idealstate.idealstate_diff</td> + <td>bucket</td> + <td>average</td> + <td>A number representing the current difference from the ideal state. This is a number that decreases steadily as the system is getting closer to the ideal state</td> + </tr> + <tr> + <td>vds.idealstate.buckets_toofewcopies</td> + <td>bucket</td> + <td>average</td> + <td>The number of buckets the distributor controls that have less than the desired redundancy</td> + </tr> + <tr> + <td>vds.idealstate.buckets_toomanycopies</td> + <td>bucket</td> + <td>average</td> + <td>The number of buckets the distributor controls that have more than the desired redundancy</td> + </tr> + <tr> + <td>vds.idealstate.buckets</td> + <td>bucket</td> + <td>average</td> + <td>The number of buckets the distributor controls</td> + </tr> + <tr> + <td>vds.idealstate.buckets_notrusted</td> + <td>bucket</td> + <td>average</td> + <td>The number of buckets that have no trusted copies.</td> + </tr> + <tr> + <td>vds.idealstate.bucket_replicas_moving_out</td> + <td>bucket</td> + <td>average</td> + <td>Bucket replicas that should be moved out, e.g. retirement case or node added to cluster that has higher ideal state priority.</td> + </tr> + <tr> + <td>vds.idealstate.bucket_replicas_copying_out</td> + <td>bucket</td> + <td>average</td> + <td>Bucket replicas that should be copied out, e.g. node is in ideal state but might have to provide data other nodes in a merge</td> + </tr> + <tr> + <td>vds.idealstate.bucket_replicas_copying_in</td> + <td>bucket</td> + <td>average</td> + <td>Bucket replicas that should be copied in, e.g. node does not have a replica for a bucket that it is in ideal state for</td> + </tr> + <tr> + <td>vds.idealstate.bucket_replicas_syncing</td> + <td>bucket</td> + <td>average</td> + <td>Bucket replicas that need syncing due to mismatching metadata</td> + </tr> + <tr> + <td>vds.idealstate.max_observed_time_since_last_gc_sec</td> + <td>second</td> + <td>average</td> + <td>Maximum time (in seconds) since GC was last successfully run for a bucket. Aggregated max value across all buckets on the distributor.</td> + </tr> + <tr> + <td>vds.idealstate.delete_bucket.done_ok</td> + <td>operation</td> + <td>rate</td> + <td>The number of operations successfully performed</td> + </tr> + <tr> + <td>vds.idealstate.delete_bucket.done_failed</td> + <td>operation</td> + <td>rate</td> + <td>The number of operations that failed</td> + </tr> + <tr> + <td>vds.idealstate.delete_bucket.pending</td> + <td>operation</td> + <td>average</td> + <td>The number of operations pending</td> + </tr> + <tr> + <td>vds.idealstate.merge_bucket.done_ok</td> + <td>operation</td> + <td>rate</td> + <td>The number of operations successfully performed</td> + </tr> + <tr> + <td>vds.idealstate.merge_bucket.done_failed</td> + <td>operation</td> + <td>rate</td> + <td>The number of operations that failed</td> + </tr> + <tr> + <td>vds.idealstate.merge_bucket.pending</td> + <td>operation</td> + <td>average</td> + <td>The number of operations pending</td> + </tr> + <tr> + <td>vds.idealstate.merge_bucket.blocked</td> + <td>operation</td> + <td>rate</td> + <td>The number of operations blocked by blocking operation starter</td> + </tr> + <tr> + <td>vds.idealstate.merge_bucket.throttled</td> + <td>operation</td> + <td>rate</td> + <td>The number of operations throttled by throttling operation starter</td> + </tr> + <tr> + <td>vds.idealstate.merge_bucket.source_only_copy_changed</td> + <td>operation</td> + <td>rate</td> + <td>The number of merge operations where source-only copy changed</td> + </tr> + <tr> + <td>vds.idealstate.merge_bucket.source_only_copy_delete_blocked</td> + <td>operation</td> + <td>rate</td> + <td>The number of merge operations where delete of unchanged source-only copies was blocked</td> + </tr> + <tr> + <td>vds.idealstate.merge_bucket.source_only_copy_delete_failed</td> + <td>operation</td> + <td>rate</td> + <td>The number of merge operations where delete of unchanged source-only copies failed</td> + </tr> + <tr> + <td>vds.idealstate.split_bucket.done_ok</td> + <td>operation</td> + <td>rate</td> + <td>The number of operations successfully performed</td> + </tr> + <tr> + <td>vds.idealstate.split_bucket.done_failed</td> + <td>operation</td> + <td>rate</td> + <td>The number of operations that failed</td> + </tr> + <tr> + <td>vds.idealstate.split_bucket.pending</td> + <td>operation</td> + <td>average</td> + <td>The number of operations pending</td> + </tr> + <tr> + <td>vds.idealstate.join_bucket.done_ok</td> + <td>operation</td> + <td>rate</td> + <td>The number of operations successfully performed</td> + </tr> + <tr> + <td>vds.idealstate.join_bucket.done_failed</td> + <td>operation</td> + <td>rate</td> + <td>The number of operations that failed</td> + </tr> + <tr> + <td>vds.idealstate.join_bucket.pending</td> + <td>operation</td> + <td>average</td> + <td>The number of operations pending</td> + </tr> + <tr> + <td>vds.idealstate.garbage_collection.done_ok</td> + <td>operation</td> + <td>rate</td> + <td>The number of operations successfully performed</td> + </tr> + <tr> + <td>vds.idealstate.garbage_collection.done_failed</td> + <td>operation</td> + <td>rate</td> + <td>The number of operations that failed</td> + </tr> + <tr> + <td>vds.idealstate.garbage_collection.pending</td> + <td>operation</td> + <td>average</td> + <td>The number of operations pending</td> + </tr> + <tr> + <td>vds.idealstate.garbage_collection.documents_removed</td> + <td>document</td> + <td>count, rate</td> + <td>Number of documents removed by GC operations</td> + </tr> + <tr> + <td>vds.distributor.puts.latency</td> + <td>millisecond</td> + <td>count, max, sum</td> + <td>The latency of put operations</td> + </tr> + <tr> + <td>vds.distributor.puts.ok</td> + <td>operation</td> + <td>rate</td> + <td>The number of successful put operations performed</td> + </tr> + <tr> + <td>vds.distributor.puts.failures.total</td> + <td>operation</td> + <td>rate</td> + <td>Sum of all failures</td> + </tr> + <tr> + <td>vds.distributor.puts.failures.notfound</td> + <td>operation</td> + <td>rate</td> + <td>The number of operations that failed because the document did not exist</td> + </tr> + <tr> + <td>vds.distributor.puts.failures.test_and_set_failed</td> + <td>operation</td> + <td>rate</td> + <td>The number of mutating operations that failed because they specified a test-and-set condition that did not match the existing document</td> + </tr> + <tr> + <td>vds.distributor.puts.failures.concurrent_mutations</td> + <td>operation</td> + <td>rate</td> + <td>The number of operations that were transiently failed due to a mutating operation already being in progress for its document ID</td> + </tr> + <tr> + <td>vds.distributor.puts.failures.notconnected</td> + <td>operation</td> + <td>rate</td> + <td>The number of operations discarded because there were no available storage nodes to send to</td> + </tr> + <tr> + <td>vds.distributor.puts.failures.notready</td> + <td>operation</td> + <td>rate</td> + <td>The number of operations discarded because distributor was not ready</td> + </tr> + <tr> + <td>vds.distributor.puts.failures.wrongdistributor</td> + <td>operation</td> + <td>rate</td> + <td>The number of operations discarded because they were sent to the wrong distributor</td> + </tr> + <tr> + <td>vds.distributor.puts.failures.safe_time_not_reached</td> + <td>operation</td> + <td>rate</td> + <td>The number of operations that were transiently failed due to them arriving before the safe time point for bucket ownership handovers has passed</td> + </tr> + <tr> + <td>vds.distributor.puts.failures.storagefailure</td> + <td>operation</td> + <td>rate</td> + <td>The number of operations that failed in storage</td> + </tr> + <tr> + <td>vds.distributor.puts.failures.timeout</td> + <td>operation</td> + <td>rate</td> + <td>The number of operations that failed because the operation timed out towards storage</td> + </tr> + <tr> + <td>vds.distributor.puts.failures.busy</td> + <td>operation</td> + <td>rate</td> + <td>The number of messages from storage that failed because the storage node was busy</td> + </tr> + <tr> + <td>vds.distributor.puts.failures.inconsistent_bucket</td> + <td>operation</td> + <td>rate</td> + <td>The number of operations failed due to buckets being in an inconsistent state or not found</td> + </tr> + <tr> + <td>vds.distributor.removes.latency</td> + <td>millisecond</td> + <td>count, max, sum</td> + <td>The latency of remove operations</td> + </tr> + <tr> + <td>vds.distributor.removes.ok</td> + <td>operation</td> + <td>rate</td> + <td>The number of successful removes operations performed</td> + </tr> + <tr> + <td>vds.distributor.removes.failures.total</td> + <td>operation</td> + <td>rate</td> + <td>Sum of all failures</td> + </tr> + <tr> + <td>vds.distributor.removes.failures.notfound</td> + <td>operation</td> + <td>rate</td> + <td>The number of operations that failed because the document did not exist</td> + </tr> + <tr> + <td>vds.distributor.removes.failures.test_and_set_failed</td> + <td>operation</td> + <td>rate</td> + <td>The number of mutating operations that failed because they specified a test-and-set condition that did not match the existing document</td> + </tr> + <tr> + <td>vds.distributor.removes.failures.concurrent_mutations</td> + <td>operation</td> + <td>rate</td> + <td>The number of operations that were transiently failed due to a mutating operation already being in progress for its document ID</td> + </tr> + <tr> + <td>vds.distributor.updates.latency</td> + <td>millisecond</td> + <td>count, max, sum</td> + <td>The latency of update operations</td> + </tr> + <tr> + <td>vds.distributor.updates.ok</td> + <td>operation</td> + <td>rate</td> + <td>The number of successful updates operations performed</td> + </tr> + <tr> + <td>vds.distributor.updates.failures.total</td> + <td>operation</td> + <td>rate</td> + <td>Sum of all failures</td> + </tr> + <tr> + <td>vds.distributor.updates.failures.notfound</td> + <td>operation</td> + <td>rate</td> + <td>The number of operations that failed because the document did not exist</td> + </tr> + <tr> + <td>vds.distributor.updates.failures.test_and_set_failed</td> + <td>operation</td> + <td>rate</td> + <td>The number of mutating operations that failed because they specified a test-and-set condition that did not match the existing document</td> + </tr> + <tr> + <td>vds.distributor.updates.failures.concurrent_mutations</td> + <td>operation</td> + <td>rate</td> + <td>The number of operations that were transiently failed due to a mutating operation already being in progress for its document ID</td> + </tr> + <tr> + <td>vds.distributor.updates.diverging_timestamp_updates</td> + <td>operation</td> + <td>rate</td> + <td>Number of updates that report they were performed against divergent version timestamps on different replicas</td> + </tr> + <tr> + <td>vds.distributor.removelocations.ok</td> + <td>operation</td> + <td>rate</td> + <td>The number of successful removelocations operations performed</td> + </tr> + <tr> + <td>vds.distributor.removelocations.failures.total</td> + <td>operation</td> + <td>rate</td> + <td>Sum of all failures</td> + </tr> + <tr> + <td>vds.distributor.gets.latency</td> + <td>millisecond</td> + <td>count, max, sum</td> + <td>The average latency of gets operations</td> + </tr> + <tr> + <td>vds.distributor.gets.ok</td> + <td>operation</td> + <td>rate</td> + <td>The number of successful gets operations performed</td> + </tr> + <tr> + <td>vds.distributor.gets.failures.total</td> + <td>operation</td> + <td>rate</td> + <td>Sum of all failures</td> + </tr> + <tr> + <td>vds.distributor.gets.failures.notfound</td> + <td>operation</td> + <td>rate</td> + <td>The number of operations that failed because the document did not exist</td> + </tr> + <tr> + <td>vds.distributor.visitor.latency</td> + <td>millisecond</td> + <td>count, max, sum</td> + <td>The average latency of visitor operations</td> + </tr> + <tr> + <td>vds.distributor.visitor.ok</td> + <td>operation</td> + <td>rate</td> + <td>The number of successful visitor operations performed</td> + </tr> + <tr> + <td>vds.distributor.visitor.failures.total</td> + <td>operation</td> + <td>rate</td> + <td>Sum of all failures</td> + </tr> + <tr> + <td>vds.distributor.visitor.failures.notready</td> + <td>operation</td> + <td>rate</td> + <td>The number of operations discarded because distributor was not ready</td> + </tr> + <tr> + <td>vds.distributor.visitor.failures.notconnected</td> + <td>operation</td> + <td>rate</td> + <td>The number of operations discarded because there were no available storage nodes to send to</td> + </tr> + <tr> + <td>vds.distributor.visitor.failures.wrongdistributor</td> + <td>operation</td> + <td>rate</td> + <td>The number of operations discarded because they were sent to the wrong distributor</td> + </tr> + <tr> + <td>vds.distributor.visitor.failures.safe_time_not_reached</td> + <td>operation</td> + <td>rate</td> + <td>The number of operations that were transiently failed due to them arriving before the safe time point for bucket ownership handovers has passed</td> + </tr> + <tr> + <td>vds.distributor.visitor.failures.storagefailure</td> + <td>operation</td> + <td>rate</td> + <td>The number of operations that failed in storage</td> + </tr> + <tr> + <td>vds.distributor.visitor.failures.timeout</td> + <td>operation</td> + <td>rate</td> + <td>The number of operations that failed because the operation timed out towards storage</td> + </tr> + <tr> + <td>vds.distributor.visitor.failures.busy</td> + <td>operation</td> + <td>rate</td> + <td>The number of messages from storage that failed because the storage node was busy</td> + </tr> + <tr> + <td>vds.distributor.visitor.failures.inconsistent_bucket</td> + <td>operation</td> + <td>rate</td> + <td>The number of operations failed due to buckets being in an inconsistent state or not found</td> + </tr> + <tr> + <td>vds.distributor.visitor.failures.notfound</td> + <td>operation</td> + <td>rate</td> + <td>The number of operations that failed because the document did not exist</td> + </tr> + <tr> + <td>vds.distributor.docsstored</td> + <td>document</td> + <td>average</td> + <td>Number of documents stored in all buckets controlled by this distributor</td> + </tr> + <tr> + <td>vds.distributor.bytesstored</td> + <td>byte</td> + <td>average</td> + <td>Number of bytes stored in all buckets controlled by this distributor</td> + </tr> + <tr> + <td>vds.distributor.mutating_op_memory_usage</td> + <td>byte</td> + <td>max</td> + <td>Estimated amount of memory used by active mutating operations across all distributor stripes, in bytes</td> + </tr> + <tr> + <td>vds.bouncer.clock_skew_aborts</td> + <td>operation</td> + <td>count</td> + <td>Number of client operations that were aborted due to clock skew between sender and receiver exceeding acceptable range</td> + </tr> + </tbody> +</table> + +## Logd Metrics + +<table className="vespa-table"> + <thead> + <tr> + <th>Name</th> + <th>Unit</th> + <th>Suffixes</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>logd.processed.lines</td> + <td>item</td> + <td>count</td> + <td>Number of log lines processed</td> + </tr> + </tbody> +</table> + +## NodeAdmin Metrics + +<table className="vespa-table"> + <thead> + <tr> + <th>Name</th> + <th>Unit</th> + <th>Suffixes</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>endpoint.certificate.expiry.seconds</td> + <td>second</td> + <td>N/A</td> + <td>Time until node endpoint certificate expires</td> + </tr> + <tr> + <td>node-certificate.expiry.seconds</td> + <td>second</td> + <td>N/A</td> + <td>Time until node certificate expires</td> + </tr> + </tbody> +</table> + +## SearchNode Metrics + +<table className="vespa-table"> + <thead> + <tr> + <th>Name</th> + <th>Unit</th> + <th>Suffixes</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>content.proton.config.generation</td> + <td>version</td> + <td>last</td> + <td>The oldest config generation used by this search node</td> + </tr> + <tr> + <td>content.proton.documentdb.documents.total</td> + <td>document</td> + <td>last, max</td> + <td>The total number of documents in this documents db (ready + not-ready)</td> + </tr> + <tr> + <td>content.proton.documentdb.documents.ready</td> + <td>document</td> + <td>last, max</td> + <td>The number of ready documents in this document db</td> + </tr> + <tr> + <td>content.proton.documentdb.documents.active</td> + <td>document</td> + <td>last, max</td> + <td>The number of active / searchable documents in this document db</td> + </tr> + <tr> + <td>content.proton.documentdb.documents.removed</td> + <td>document</td> + <td>last, max</td> + <td>The number of removed documents in this document db</td> + </tr> + <tr> + <td>content.proton.documentdb.index.docs_in_memory</td> + <td>document</td> + <td>last, max</td> + <td>Number of documents in memory index</td> + </tr> + <tr> + <td>content.proton.documentdb.disk_usage</td> + <td>byte</td> + <td>last</td> + <td>The total disk usage (in bytes) for this document db</td> + </tr> + <tr> + <td>content.proton.documentdb.memory_usage.allocated_bytes</td> + <td>byte</td> + <td>max</td> + <td>The number of allocated bytes</td> + </tr> + <tr> + <td>content.proton.documentdb.heart_beat_age</td> + <td>second</td> + <td>last, min</td> + <td>How long ago (in seconds) heart beat maintenance job was run</td> + </tr> + <tr> + <td>content.proton.docsum.docs</td> + <td>document</td> + <td>rate</td> + <td>Total docsums returned</td> + </tr> + <tr> + <td>content.proton.docsum.latency</td> + <td>millisecond</td> + <td>count, max, sum</td> + <td>Docsum request latency</td> + </tr> + <tr> + <td>content.proton.search_protocol.query.latency</td> + <td>second</td> + <td>count, max, sum</td> + <td>Query request latency (seconds)</td> + </tr> + <tr> + <td>content.proton.search_protocol.query.request_size</td> + <td>byte</td> + <td>count, max, sum</td> + <td>Query request size (network bytes)</td> + </tr> + <tr> + <td>content.proton.search_protocol.query.reply_size</td> + <td>byte</td> + <td>count, max, sum</td> + <td>Query reply size (network bytes)</td> + </tr> + <tr> + <td>content.proton.search_protocol.docsum.latency</td> + <td>second</td> + <td>average, count, max, sum</td> + <td>Docsum request latency (seconds)</td> + </tr> + <tr> + <td>content.proton.search_protocol.docsum.request_size</td> + <td>byte</td> + <td>count, max, sum</td> + <td>Docsum request size (network bytes)</td> + </tr> + <tr> + <td>content.proton.search_protocol.docsum.reply_size</td> + <td>byte</td> + <td>count, max, sum</td> + <td>Docsum reply size (network bytes)</td> + </tr> + <tr> + <td>content.proton.search_protocol.docsum.requested_documents</td> + <td>document</td> + <td>count, max, sum</td> + <td>Total requested document summaries</td> + </tr> + <tr> + <td>content.proton.executor.proton.queuesize</td> + <td>task</td> + <td>count, max, sum</td> + <td>Size of executor proton task queue</td> + </tr> + <tr> + <td>content.proton.executor.proton.accepted</td> + <td>task</td> + <td>rate</td> + <td>Number of executor proton accepted tasks</td> + </tr> + <tr> + <td>content.proton.executor.proton.wakeups</td> + <td>wakeup</td> + <td>rate</td> + <td>Number of times an executor proton worker thread has been woken up</td> + </tr> + <tr> + <td>content.proton.executor.proton.utilization</td> + <td>fraction</td> + <td>count, max, sum</td> + <td>Ratio of time the executor proton worker threads has been active</td> + </tr> + <tr> + <td>content.proton.executor.flush.queuesize</td> + <td>task</td> + <td>count, max, sum</td> + <td>Size of executor flush task queue</td> + </tr> + <tr> + <td>content.proton.executor.flush.accepted</td> + <td>task</td> + <td>rate</td> + <td>Number of accepted executor flush tasks</td> + </tr> + <tr> + <td>content.proton.executor.flush.wakeups</td> + <td>wakeup</td> + <td>rate</td> + <td>Number of times an executor flush worker thread has been woken up</td> + </tr> + <tr> + <td>content.proton.executor.flush.utilization</td> + <td>fraction</td> + <td>count, max, sum</td> + <td>Ratio of time the executor flush worker threads has been active</td> + </tr> + <tr> + <td>content.proton.executor.match.queuesize</td> + <td>task</td> + <td>count, max, sum</td> + <td>Size of executor match task queue</td> + </tr> + <tr> + <td>content.proton.executor.match.accepted</td> + <td>task</td> + <td>rate</td> + <td>Number of accepted executor match tasks</td> + </tr> + <tr> + <td>content.proton.executor.match.wakeups</td> + <td>wakeup</td> + <td>rate</td> + <td>Number of times an executor match worker thread has been woken up</td> + </tr> + <tr> + <td>content.proton.executor.match.utilization</td> + <td>fraction</td> + <td>count, max, sum</td> + <td>Ratio of time the executor match worker threads has been active</td> + </tr> + <tr> + <td>content.proton.executor.docsum.queuesize</td> + <td>task</td> + <td>count, max, sum</td> + <td>Size of executor docsum task queue</td> + </tr> + <tr> + <td>content.proton.executor.docsum.accepted</td> + <td>task</td> + <td>rate</td> + <td>Number of executor accepted docsum tasks</td> + </tr> + <tr> + <td>content.proton.executor.docsum.wakeups</td> + <td>wakeup</td> + <td>rate</td> + <td>Number of times an executor docsum worker thread has been woken up</td> + </tr> + <tr> + <td>content.proton.executor.docsum.utilization</td> + <td>fraction</td> + <td>count, max, sum</td> + <td>Ratio of time the executor docsum worker threads has been active</td> + </tr> + <tr> + <td>content.proton.executor.shared.queuesize</td> + <td>task</td> + <td>count, max, sum</td> + <td>Size of executor shared task queue</td> + </tr> + <tr> + <td>content.proton.executor.shared.accepted</td> + <td>task</td> + <td>rate</td> + <td>Number of executor shared accepted tasks</td> + </tr> + <tr> + <td>content.proton.executor.shared.wakeups</td> + <td>wakeup</td> + <td>rate</td> + <td>Number of times an executor shared worker thread has been woken up</td> + </tr> + <tr> + <td>content.proton.executor.shared.utilization</td> + <td>fraction</td> + <td>count, max, sum</td> + <td>Ratio of time the executor shared worker threads has been active</td> + </tr> + <tr> + <td>content.proton.executor.warmup.queuesize</td> + <td>task</td> + <td>count, max, sum</td> + <td>Size of executor warmup task queue</td> + </tr> + <tr> + <td>content.proton.executor.warmup.accepted</td> + <td>task</td> + <td>rate</td> + <td>Number of accepted executor warmup tasks</td> + </tr> + <tr> + <td>content.proton.executor.warmup.wakeups</td> + <td>wakeup</td> + <td>rate</td> + <td>Number of times a warmup executor worker thread has been woken up</td> + </tr> + <tr> + <td>content.proton.executor.warmup.utilization</td> + <td>fraction</td> + <td>count, max, sum</td> + <td>Ratio of time the executor warmup worker threads has been active</td> + </tr> + <tr> + <td>content.proton.executor.field_writer.queuesize</td> + <td>task</td> + <td>count, max, sum</td> + <td>Size of executor field writer task queue</td> + </tr> + <tr> + <td>content.proton.executor.field_writer.accepted</td> + <td>task</td> + <td>rate</td> + <td>Number of accepted executor field writer tasks</td> + </tr> + <tr> + <td>content.proton.executor.field_writer.wakeups</td> + <td>wakeup</td> + <td>rate</td> + <td>Number of times an executor field writer worker thread has been woken up</td> + </tr> + <tr> + <td>content.proton.executor.field_writer.utilization</td> + <td>fraction</td> + <td>count, max, sum</td> + <td>Ratio of time the executor fieldwriter worker threads has been active</td> + </tr> + <tr> + <td>content.proton.executor.field_writer.saturation</td> + <td>fraction</td> + <td>count, max, sum</td> + <td>Ratio indicating the max saturation of underlying worker threads. A higher saturation than utilization indicates a bottleneck in one of the worker threads.</td> + </tr> + <tr> + <td>content.proton.documentdb.job.total</td> + <td>fraction</td> + <td>average</td> + <td>The job load average total of all job metrics</td> + </tr> + <tr> + <td>content.proton.documentdb.job.attribute_flush</td> + <td>fraction</td> + <td>average</td> + <td>Flushing of attribute vector(s) to disk</td> + </tr> + <tr> + <td>content.proton.documentdb.job.memory_index_flush</td> + <td>fraction</td> + <td>average</td> + <td>Flushing of memory index to disk</td> + </tr> + <tr> + <td>content.proton.documentdb.job.disk_index_fusion</td> + <td>fraction</td> + <td>average</td> + <td>Fusion of disk indexes</td> + </tr> + <tr> + <td>content.proton.documentdb.job.document_store_flush</td> + <td>fraction</td> + <td>average</td> + <td>Flushing of document store to disk</td> + </tr> + <tr> + <td>content.proton.documentdb.job.document_store_compact</td> + <td>fraction</td> + <td>average</td> + <td>Compaction of document store on disk</td> + </tr> + <tr> + <td>content.proton.documentdb.job.bucket_move</td> + <td>fraction</td> + <td>average</td> + <td>Moving of buckets between 'ready' and 'notready' sub databases</td> + </tr> + <tr> + <td>content.proton.documentdb.job.lid_space_compact</td> + <td>fraction</td> + <td>average</td> + <td>Compaction of lid space in document meta store and attribute vectors</td> + </tr> + <tr> + <td>content.proton.documentdb.job.removed_documents_prune</td> + <td>fraction</td> + <td>average</td> + <td>Pruning of removed documents in 'removed' sub database</td> + </tr> + <tr> + <td>content.proton.documentdb.threading_service.master.queuesize</td> + <td>task</td> + <td>count, max, sum</td> + <td>Size of threading service master task queue</td> + </tr> + <tr> + <td>content.proton.documentdb.threading_service.master.accepted</td> + <td>task</td> + <td>rate</td> + <td>Number of accepted threading service master tasks</td> + </tr> + <tr> + <td>content.proton.documentdb.threading_service.master.wakeups</td> + <td>wakeup</td> + <td>rate</td> + <td>Number of times a threading service master worker thread has been woken up</td> + </tr> + <tr> + <td>content.proton.documentdb.threading_service.master.utilization</td> + <td>fraction</td> + <td>count, max, sum</td> + <td>Ratio of time the threading service master worker threads has been active</td> + </tr> + <tr> + <td>content.proton.documentdb.threading_service.index.queuesize</td> + <td>task</td> + <td>count, max, sum</td> + <td>Size of threading service index task queue</td> + </tr> + <tr> + <td>content.proton.documentdb.threading_service.index.accepted</td> + <td>task</td> + <td>rate</td> + <td>Number of accepted threading service index tasks</td> + </tr> + <tr> + <td>content.proton.documentdb.threading_service.index.wakeups</td> + <td>wakeup</td> + <td>rate</td> + <td>Number of times a threading service index worker thread has been woken up</td> + </tr> + <tr> + <td>content.proton.documentdb.threading_service.index.utilization</td> + <td>fraction</td> + <td>count, max, sum</td> + <td>Ratio of time the threading service index worker threads has been active</td> + </tr> + <tr> + <td>content.proton.documentdb.threading_service.summary.queuesize</td> + <td>task</td> + <td>count, max, sum</td> + <td>Size of threading service summary task queue</td> + </tr> + <tr> + <td>content.proton.documentdb.threading_service.summary.accepted</td> + <td>task</td> + <td>rate</td> + <td>Number of accepted threading service summary tasks</td> + </tr> + <tr> + <td>content.proton.documentdb.threading_service.summary.wakeups</td> + <td>wakeup</td> + <td>rate</td> + <td>Number of times a threading service summary worker thread has been woken up</td> + </tr> + <tr> + <td>content.proton.documentdb.threading_service.summary.utilization</td> + <td>fraction</td> + <td>count, max, sum</td> + <td>Ratio of time the threading service summary worker threads has been active</td> + </tr> + <tr> + <td>content.proton.documentdb.ready.lid_space.lid_bloat_factor</td> + <td>fraction</td> + <td>average</td> + <td>The bloat factor of this lid space, indicating the total amount of holes in the allocated lid space ((lid_limit - used_lids) / lid_limit)</td> + </tr> + <tr> + <td>content.proton.documentdb.ready.lid_space.lid_fragmentation_factor</td> + <td>fraction</td> + <td>average</td> + <td>The fragmentation factor of this lid space, indicating the amount of holes in the currently used part of the lid space ((highest_used_lid - used_lids) / highest_used_lid)</td> + </tr> + <tr> + <td>content.proton.documentdb.ready.lid_space.lid_limit</td> + <td>documentid</td> + <td>last, max</td> + <td>The size of the allocated lid space</td> + </tr> + <tr> + <td>content.proton.documentdb.ready.lid_space.highest_used_lid</td> + <td>documentid</td> + <td>last, max</td> + <td>The highest used lid</td> + </tr> + <tr> + <td>content.proton.documentdb.ready.lid_space.used_lids</td> + <td>documentid</td> + <td>last, max</td> + <td>The number of lids used</td> + </tr> + <tr> + <td>content.proton.documentdb.notready.lid_space.lid_bloat_factor</td> + <td>fraction</td> + <td>average</td> + <td>The bloat factor of this lid space, indicating the total amount of holes in the allocated lid space ((lid_limit - used_lids) / lid_limit)</td> + </tr> + <tr> + <td>content.proton.documentdb.notready.lid_space.lid_fragmentation_factor</td> + <td>fraction</td> + <td>average</td> + <td>The fragmentation factor of this lid space, indicating the amount of holes in the currently used part of the lid space ((highest_used_lid - used_lids) / highest_used_lid)</td> + </tr> + <tr> + <td>content.proton.documentdb.notready.lid_space.lid_limit</td> + <td>documentid</td> + <td>last, max</td> + <td>The size of the allocated lid space</td> + </tr> + <tr> + <td>content.proton.documentdb.notready.lid_space.highest_used_lid</td> + <td>documentid</td> + <td>last, max</td> + <td>The highest used lid</td> + </tr> + <tr> + <td>content.proton.documentdb.notready.lid_space.used_lids</td> + <td>documentid</td> + <td>last, max</td> + <td>The number of lids used</td> + </tr> + <tr> + <td>content.proton.documentdb.removed.lid_space.lid_bloat_factor</td> + <td>fraction</td> + <td>average</td> + <td>The bloat factor of this lid space, indicating the total amount of holes in the allocated lid space ((lid_limit - used_lids) / lid_limit)</td> + </tr> + <tr> + <td>content.proton.documentdb.removed.lid_space.lid_fragmentation_factor</td> + <td>fraction</td> + <td>average</td> + <td>The fragmentation factor of this lid space, indicating the amount of holes in the currently used part of the lid space ((highest_used_lid - used_lids) / highest_used_lid)</td> + </tr> + <tr> + <td>content.proton.documentdb.removed.lid_space.lid_limit</td> + <td>documentid</td> + <td>last, max</td> + <td>The size of the allocated lid space</td> + </tr> + <tr> + <td>content.proton.documentdb.removed.lid_space.highest_used_lid</td> + <td>documentid</td> + <td>last, max</td> + <td>The highest used lid</td> + </tr> + <tr> + <td>content.proton.documentdb.removed.lid_space.used_lids</td> + <td>documentid</td> + <td>last, max</td> + <td>The number of lids used</td> + </tr> + <tr> + <td>content.proton.documentdb.bucket_move.buckets_pending</td> + <td>bucket</td> + <td>last, max, sum</td> + <td>The number of buckets left to move</td> + </tr> + <tr> + <td>content.proton.resource_usage.disk</td> + <td>fraction</td> + <td>average</td> + <td>The relative amount of disk used by this content node (transient usage not included, value in the range [0, 1]). Same value as reported to the cluster controller</td> + </tr> + <tr> + <td>content.proton.resource_usage.disk_usage.total</td> + <td>fraction</td> + <td>max</td> + <td>The total relative amount of disk used by this content node (value in the range [0, 1])</td> + </tr> + <tr> + <td>content.proton.resource_usage.disk_usage.total_utilization</td> + <td>fraction</td> + <td>max</td> + <td>The relative amount of disk used compared to the content node disk resource limit</td> + </tr> + <tr> + <td>content.proton.resource_usage.disk_usage.transient</td> + <td>fraction</td> + <td>max</td> + <td>The relative amount of transient disk used by this content node (value in the range [0, 1])</td> + </tr> + <tr> + <td>content.proton.resource_usage.memory</td> + <td>fraction</td> + <td>average</td> + <td>The relative amount of memory used by this content node (transient usage not included, value in the range [0, 1]). Same value as reported to the cluster controller</td> + </tr> + <tr> + <td>content.proton.resource_usage.memory_usage.total</td> + <td>fraction</td> + <td>max</td> + <td>The total relative amount of memory used by this content node (value in the range [0, 1])</td> + </tr> + <tr> + <td>content.proton.resource_usage.memory_usage.total_utilization</td> + <td>fraction</td> + <td>max</td> + <td>The relative amount of memory used compared to the content node memory resource limit</td> + </tr> + <tr> + <td>content.proton.resource_usage.memory_usage.transient</td> + <td>fraction</td> + <td>max</td> + <td>The relative amount of transient memory used by this content node (value in the range [0, 1])</td> + </tr> + <tr> + <td>content.proton.resource_usage.memory_mappings</td> + <td>file</td> + <td>max</td> + <td>The number of memory mapped files</td> + </tr> + <tr> + <td>content.proton.resource_usage.open_file_descriptors</td> + <td>file</td> + <td>max</td> + <td>The number of open files</td> + </tr> + <tr> + <td>content.proton.resource_usage.feeding_blocked</td> + <td>binary</td> + <td>last, max</td> + <td>Whether feeding is blocked due to resource limits being reached (value is either 0 or 1)</td> + </tr> + <tr> + <td>content.proton.resource_usage.malloc_arena</td> + <td>byte</td> + <td>max</td> + <td>Size of malloc arena</td> + </tr> + <tr> + <td>content.proton.documentdb.attribute.resource_usage.address_space</td> + <td>fraction</td> + <td>max</td> + <td>The max relative address space used among components in all attribute vectors in this document db (value in the range [0, 1])</td> + </tr> + <tr> + <td>content.proton.documentdb.attribute.resource_usage.feeding_blocked</td> + <td>binary</td> + <td>max</td> + <td>Whether feeding is blocked due to attribute resource limits being reached (value is either 0 or 1)</td> + </tr> + <tr> + <td>content.proton.resource_usage.cpu_util.setup</td> + <td>fraction</td> + <td>count, max, sum</td> + <td>cpu used by system init and (re-)configuration</td> + </tr> + <tr> + <td>content.proton.resource_usage.cpu_util.read</td> + <td>fraction</td> + <td>count, max, sum</td> + <td>cpu used by reading data from the system</td> + </tr> + <tr> + <td>content.proton.resource_usage.cpu_util.write</td> + <td>fraction</td> + <td>count, max, sum</td> + <td>cpu used by writing data to the system</td> + </tr> + <tr> + <td>content.proton.resource_usage.cpu_util.compact</td> + <td>fraction</td> + <td>count, max, sum</td> + <td>cpu used by internal data re-structuring</td> + </tr> + <tr> + <td>content.proton.resource_usage.cpu_util.other</td> + <td>fraction</td> + <td>count, max, sum</td> + <td>cpu used by work not classified as a specific category</td> + </tr> + <tr> + <td>content.proton.transactionlog.entries</td> + <td>record</td> + <td>average</td> + <td>The current number of entries in the transaction log</td> + </tr> + <tr> + <td>content.proton.transactionlog.disk_usage</td> + <td>byte</td> + <td>average</td> + <td>The disk usage (in bytes) of the transaction log</td> + </tr> + <tr> + <td>content.proton.transactionlog.replay_time</td> + <td>second</td> + <td>last, max</td> + <td>The replay time (in seconds) of the transaction log during start-up</td> + </tr> + <tr> + <td>content.proton.documentdb.ready.document_store.disk_usage</td> + <td>byte</td> + <td>average</td> + <td>Disk space usage in bytes</td> + </tr> + <tr> + <td>content.proton.documentdb.ready.document_store.disk_bloat</td> + <td>byte</td> + <td>average</td> + <td>Disk space bloat in bytes</td> + </tr> + <tr> + <td>content.proton.documentdb.ready.document_store.max_bucket_spread</td> + <td>fraction</td> + <td>average</td> + <td>Max bucket spread in underlying files (sum(unique buckets in each chunk)/unique buckets in file)</td> + </tr> + <tr> + <td>content.proton.documentdb.ready.document_store.memory_usage.allocated_bytes</td> + <td>byte</td> + <td>average</td> + <td>The number of allocated bytes</td> + </tr> + <tr> + <td>content.proton.documentdb.ready.document_store.memory_usage.used_bytes</td> + <td>byte</td> + <td>average</td> + <td>The number of used bytes (<code>{`<=`}</code> allocated_bytes)</td> + </tr> + <tr> + <td>content.proton.documentdb.ready.document_store.memory_usage.onhold_bytes</td> + <td>byte</td> + <td>average</td> + <td>The number of bytes on hold</td> + </tr> + <tr> + <td>content.proton.documentdb.notready.document_store.disk_usage</td> + <td>byte</td> + <td>average</td> + <td>Disk space usage in bytes</td> + </tr> + <tr> + <td>content.proton.documentdb.notready.document_store.disk_bloat</td> + <td>byte</td> + <td>average</td> + <td>Disk space bloat in bytes</td> + </tr> + <tr> + <td>content.proton.documentdb.notready.document_store.max_bucket_spread</td> + <td>fraction</td> + <td>average</td> + <td>Max bucket spread in underlying files (sum(unique buckets in each chunk)/unique buckets in file)</td> + </tr> + <tr> + <td>content.proton.documentdb.notready.document_store.memory_usage.allocated_bytes</td> + <td>byte</td> + <td>average</td> + <td>The number of allocated bytes</td> + </tr> + <tr> + <td>content.proton.documentdb.notready.document_store.memory_usage.used_bytes</td> + <td>byte</td> + <td>average</td> + <td>The number of used bytes (<code>{`<=`}</code> allocated_bytes)</td> + </tr> + <tr> + <td>content.proton.documentdb.notready.document_store.memory_usage.dead_bytes</td> + <td>byte</td> + <td>average</td> + <td>The number of dead bytes (<code>{`<=`}</code> used_bytes)</td> + </tr> + <tr> + <td>content.proton.documentdb.notready.document_store.memory_usage.onhold_bytes</td> + <td>byte</td> + <td>average</td> + <td>The number of bytes on hold</td> + </tr> + <tr> + <td>content.proton.documentdb.removed.document_store.disk_usage</td> + <td>byte</td> + <td>average</td> + <td>Disk space usage in bytes</td> + </tr> + <tr> + <td>content.proton.documentdb.removed.document_store.disk_bloat</td> + <td>byte</td> + <td>average</td> + <td>Disk space bloat in bytes</td> + </tr> + <tr> + <td>content.proton.documentdb.removed.document_store.max_bucket_spread</td> + <td>fraction</td> + <td>average</td> + <td>Max bucket spread in underlying files (sum(unique buckets in each chunk)/unique buckets in file)</td> + </tr> + <tr> + <td>content.proton.documentdb.removed.document_store.memory_usage.allocated_bytes</td> + <td>byte</td> + <td>average</td> + <td>The number of allocated bytes</td> + </tr> + <tr> + <td>content.proton.documentdb.removed.document_store.memory_usage.used_bytes</td> + <td>byte</td> + <td>average</td> + <td>The number of used bytes (<code>{`<=`}</code> allocated_bytes)</td> + </tr> + <tr> + <td>content.proton.documentdb.removed.document_store.memory_usage.dead_bytes</td> + <td>byte</td> + <td>average</td> + <td>The number of dead bytes (<code>{`<=`}</code> used_bytes)</td> + </tr> + <tr> + <td>content.proton.documentdb.removed.document_store.memory_usage.onhold_bytes</td> + <td>byte</td> + <td>average</td> + <td>The number of bytes on hold</td> + </tr> + <tr> + <td>content.proton.documentdb.ready.document_store.cache.memory_usage</td> + <td>byte</td> + <td>average</td> + <td>Memory usage of the cache (in bytes)</td> + </tr> + <tr> + <td>content.proton.documentdb.ready.document_store.cache.hit_rate</td> + <td>fraction</td> + <td>average</td> + <td>Rate of hits in the cache compared to number of lookups</td> + </tr> + <tr> + <td>content.proton.documentdb.ready.document_store.cache.lookups</td> + <td>operation</td> + <td>rate</td> + <td>Number of lookups in the cache (hits + misses)</td> + </tr> + <tr> + <td>content.proton.documentdb.ready.document_store.cache.invalidations</td> + <td>operation</td> + <td>rate</td> + <td>Number of invalidations (erased elements) in the cache.</td> + </tr> + <tr> + <td>content.proton.documentdb.notready.document_store.cache.memory_usage</td> + <td>byte</td> + <td>average</td> + <td>Memory usage of the cache (in bytes)</td> + </tr> + <tr> + <td>content.proton.documentdb.notready.document_store.cache.hit_rate</td> + <td>fraction</td> + <td>average</td> + <td>Rate of hits in the cache compared to number of lookups</td> + </tr> + <tr> + <td>content.proton.documentdb.notready.document_store.cache.lookups</td> + <td>operation</td> + <td>rate</td> + <td>Number of lookups in the cache (hits + misses)</td> + </tr> + <tr> + <td>content.proton.documentdb.notready.document_store.cache.invalidations</td> + <td>operation</td> + <td>rate</td> + <td>Number of invalidations (erased elements) in the cache.</td> + </tr> + <tr> + <td>content.proton.documentdb.ready.attribute.memory_usage.allocated_bytes</td> + <td>byte</td> + <td>average</td> + <td>The number of allocated bytes</td> + </tr> + <tr> + <td>content.proton.documentdb.ready.attribute.memory_usage.used_bytes</td> + <td>byte</td> + <td>average</td> + <td>The number of used bytes (<code>{`<=`}</code> allocated_bytes)</td> + </tr> + <tr> + <td>content.proton.documentdb.ready.attribute.memory_usage.dead_bytes</td> + <td>byte</td> + <td>average</td> + <td>The number of dead bytes (<code>{`<=`}</code> used_bytes)</td> + </tr> + <tr> + <td>content.proton.documentdb.ready.attribute.memory_usage.onhold_bytes</td> + <td>byte</td> + <td>average</td> + <td>The number of bytes on hold</td> + </tr> + <tr> + <td>content.proton.documentdb.ready.attribute.disk_usage</td> + <td>byte</td> + <td>average</td> + <td>Disk space usage (in bytes) of the flushed snapshot of this attribute for this document type</td> + </tr> + <tr> + <td>content.proton.documentdb.notready.attribute.memory_usage.allocated_bytes</td> + <td>byte</td> + <td>average</td> + <td>The number of allocated bytes</td> + </tr> + <tr> + <td>content.proton.documentdb.notready.attribute.memory_usage.used_bytes</td> + <td>byte</td> + <td>average</td> + <td>The number of used bytes (<code>{`<=`}</code> allocated_bytes)</td> + </tr> + <tr> + <td>content.proton.documentdb.notready.attribute.memory_usage.dead_bytes</td> + <td>byte</td> + <td>average</td> + <td>The number of dead bytes (<code>{`<=`}</code> used_bytes)</td> + </tr> + <tr> + <td>content.proton.documentdb.notready.attribute.memory_usage.onhold_bytes</td> + <td>byte</td> + <td>average</td> + <td>The number of bytes on hold</td> + </tr> + <tr> + <td>content.proton.index.cache.postinglist.memory_usage</td> + <td>byte</td> + <td>average</td> + <td>Memory usage of the cache (in bytes). Contains disk index posting list files across all document types</td> + </tr> + <tr> + <td>content.proton.index.cache.postinglist.hit_rate</td> + <td>fraction</td> + <td>average</td> + <td>Rate of hits in the cache compared to number of lookups. Contains disk index posting list files across all document types</td> + </tr> + <tr> + <td>content.proton.index.cache.postinglist.lookups</td> + <td>operation</td> + <td>rate</td> + <td>Number of lookups in the cache (hits + misses). Contains disk index posting list files across all document types</td> + </tr> + <tr> + <td>content.proton.index.cache.postinglist.invalidations</td> + <td>operation</td> + <td>rate</td> + <td>Number of invalidations (erased elements) in the cache. Contains disk index posting list files across all document types</td> + </tr> + <tr> + <td>content.proton.index.cache.bitvector.memory_usage</td> + <td>byte</td> + <td>average</td> + <td>Memory usage of the cache (in bytes). Contains disk index bitvector files across all document types</td> + </tr> + <tr> + <td>content.proton.index.cache.bitvector.hit_rate</td> + <td>fraction</td> + <td>average</td> + <td>Rate of hits in the cache compared to number of lookups. Contains disk index bitvector files across all document types</td> + </tr> + <tr> + <td>content.proton.index.cache.bitvector.lookups</td> + <td>operation</td> + <td>rate</td> + <td>Number of lookups in the cache (hits + misses). Contains disk index bitvector files across all document types</td> + </tr> + <tr> + <td>content.proton.index.cache.bitvector.invalidations</td> + <td>operation</td> + <td>rate</td> + <td>Number of invalidations (erased elements) in the cache. Contains disk index bitvector files across all document types</td> + </tr> + <tr> + <td>content.proton.documentdb.index.memory_usage.allocated_bytes</td> + <td>byte</td> + <td>average</td> + <td>The number of allocated bytes for the memory index for this document type</td> + </tr> + <tr> + <td>content.proton.documentdb.index.memory_usage.used_bytes</td> + <td>byte</td> + <td>average</td> + <td>The number of used bytes (<code>{`<=`}</code> allocated_bytes) for the memory index for this document type</td> + </tr> + <tr> + <td>content.proton.documentdb.index.memory_usage.dead_bytes</td> + <td>byte</td> + <td>average</td> + <td>The number of dead bytes (<code>{`<=`}</code> used_bytes) for the memory index for this document type</td> + </tr> + <tr> + <td>content.proton.documentdb.index.memory_usage.onhold_bytes</td> + <td>byte</td> + <td>average</td> + <td>The number of bytes on hold for the memory index for this document type</td> + </tr> + <tr> + <td>content.proton.documentdb.index.io.search.read_bytes</td> + <td>byte</td> + <td>count, sum</td> + <td>Bytes read from disk index posting list and bitvector files as part of search for this document type</td> + </tr> + <tr> + <td>content.proton.documentdb.index.io.search.cached_read_bytes</td> + <td>byte</td> + <td>count, sum</td> + <td>Bytes read from cached disk index posting list and bitvector files as part of search for this document type</td> + </tr> + <tr> + <td>content.proton.documentdb.ready.index.disk_usage</td> + <td>byte</td> + <td>average</td> + <td>Disk space usage (in bytes) of this index field in all disk indexes for this document type</td> + </tr> + <tr> + <td>content.proton.documentdb.matching.queries</td> + <td>query</td> + <td>rate</td> + <td>Number of queries executed</td> + </tr> + <tr> + <td>content.proton.documentdb.matching.soft_doomed_queries</td> + <td>query</td> + <td>rate</td> + <td>Number of queries hitting the soft timeout</td> + </tr> + <tr> + <td>content.proton.documentdb.matching.query_latency</td> + <td>second</td> + <td>count, max, sum</td> + <td>Total average latency (sec) when matching and ranking a query</td> + </tr> + <tr> + <td>content.proton.documentdb.matching.query_setup_time</td> + <td>second</td> + <td>count, max, sum</td> + <td>Average time (sec) spent setting up and tearing down queries</td> + </tr> + <tr> + <td>content.proton.documentdb.matching.docs_matched</td> + <td>document</td> + <td>count, rate</td> + <td>Number of documents matched</td> + </tr> + <tr> + <td>content.proton.documentdb.matching.exact_nns_distances_computed</td> + <td>distance</td> + <td>rate</td> + <td>Number of distances computed in exact nearest-neighbor search</td> + </tr> + <tr> + <td>content.proton.documentdb.matching.approximate_nns_distances_computed</td> + <td>distance</td> + <td>rate</td> + <td>Number of distances computed in approximate nearest-neighbor search</td> + </tr> + <tr> + <td>content.proton.documentdb.matching.approximate_nns_nodes_visited</td> + <td>graph_node</td> + <td>rate</td> + <td>Number of nodes visited in approximate nearest-neighbor search</td> + </tr> + <tr> + <td>content.proton.documentdb.matching.rank_profile.queries</td> + <td>query</td> + <td>rate</td> + <td>Number of queries executed</td> + </tr> + <tr> + <td>content.proton.documentdb.matching.rank_profile.soft_doomed_queries</td> + <td>query</td> + <td>rate</td> + <td>Number of queries hitting the soft timeout</td> + </tr> + <tr> + <td>content.proton.documentdb.matching.rank_profile.soft_doom_factor</td> + <td>fraction</td> + <td>count, max, min, sum</td> + <td>Factor used to compute soft-timeout</td> + </tr> + <tr> + <td>content.proton.documentdb.matching.rank_profile.query_latency</td> + <td>second</td> + <td>count, max, sum</td> + <td>Total average latency (sec) when matching and ranking a query</td> + </tr> + <tr> + <td>content.proton.documentdb.matching.rank_profile.query_setup_time</td> + <td>second</td> + <td>count, max, sum</td> + <td>Average time (sec) spent setting up and tearing down queries</td> + </tr> + <tr> + <td>content.proton.documentdb.matching.rank_profile.grouping_time</td> + <td>second</td> + <td>count, max, sum</td> + <td>Average time (sec) spent on grouping</td> + </tr> + <tr> + <td>content.proton.documentdb.matching.rank_profile.rerank_time</td> + <td>second</td> + <td>count, max, sum</td> + <td>Average time (sec) spent on 2nd phase ranking</td> + </tr> + <tr> + <td>content.proton.documentdb.matching.rank_profile.docs_matched</td> + <td>document</td> + <td>count, rate</td> + <td>Number of documents matched</td> + </tr> + <tr> + <td>content.proton.documentdb.matching.rank_profile.exact_nns_distances_computed</td> + <td>distance</td> + <td>rate</td> + <td>Number of distances computed in exact nearest-neighbor search</td> + </tr> + <tr> + <td>content.proton.documentdb.matching.rank_profile.approximate_nns_distances_computed</td> + <td>distance</td> + <td>rate</td> + <td>Number of distances computed in approximate nearest-neighbor search</td> + </tr> + <tr> + <td>content.proton.documentdb.matching.rank_profile.approximate_nns_nodes_visited</td> + <td>graph_node</td> + <td>rate</td> + <td>Number of nodes visited in approximate nearest-neighbor search</td> + </tr> + <tr> + <td>content.proton.documentdb.matching.rank_profile.limited_queries</td> + <td>query</td> + <td>rate</td> + <td>Number of queries limited in match phase</td> + </tr> + <tr> + <td>content.proton.documentdb.feeding.commit.operations</td> + <td>operation</td> + <td>count, max, rate, sum</td> + <td>Number of operations included in a commit</td> + </tr> + <tr> + <td>content.proton.documentdb.feeding.commit.latency</td> + <td>second</td> + <td>count, max, sum</td> + <td>Latency for commit in seconds</td> + </tr> + </tbody> +</table> + +## Sentinel Metrics + +<table className="vespa-table"> + <thead> + <tr> + <th>Name</th> + <th>Unit</th> + <th>Suffixes</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>sentinel.restarts</td> + <td>restart</td> + <td>count</td> + <td>Number of service restarts done by the sentinel</td> + </tr> + <tr> + <td>sentinel.totalRestarts</td> + <td>restart</td> + <td>last, max, sum</td> + <td>Total number of service restarts done by the sentinel since the sentinel was started</td> + </tr> + <tr> + <td>sentinel.uptime</td> + <td>second</td> + <td>last</td> + <td>Time the sentinel has been running</td> + </tr> + <tr> + <td>sentinel.running</td> + <td>instance</td> + <td>count, last</td> + <td>Number of services the sentinel has running currently</td> + </tr> + </tbody> +</table> + +## Slobrok Metrics + +<table className="vespa-table"> + <thead> + <tr> + <th>Name</th> + <th>Unit</th> + <th>Suffixes</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>slobrok.heartbeats.failed</td> + <td>request</td> + <td>count</td> + <td>Number of heartbeat requests failed</td> + </tr> + <tr> + <td>slobrok.missing.consensus</td> + <td>second</td> + <td>count</td> + <td>Number of seconds without full consensus with all other brokers</td> + </tr> + </tbody> +</table> + +## Storage Metrics + +<table className="vespa-table"> + <thead> + <tr> + <th>Name</th> + <th>Unit</th> + <th>Suffixes</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>vds.datastored.alldisks.buckets</td> + <td>bucket</td> + <td>average</td> + <td>Number of buckets managed</td> + </tr> + <tr> + <td>vds.datastored.alldisks.docs</td> + <td>document</td> + <td>average</td> + <td>Number of documents stored</td> + </tr> + <tr> + <td>vds.datastored.alldisks.bytes</td> + <td>byte</td> + <td>average</td> + <td>Number of bytes stored</td> + </tr> + <tr> + <td>vds.visitor.allthreads.averagevisitorlifetime</td> + <td>millisecond</td> + <td>count, max, sum</td> + <td>Average lifetime of a visitor</td> + </tr> + <tr> + <td>vds.visitor.allthreads.averagequeuewait</td> + <td>millisecond</td> + <td>count, max, sum</td> + <td>Average time an operation spends in input queue.</td> + </tr> + <tr> + <td>vds.visitor.allthreads.queuesize</td> + <td>operation</td> + <td>count, max, sum</td> + <td>Size of input message queue.</td> + </tr> + <tr> + <td>vds.visitor.allthreads.completed</td> + <td>operation</td> + <td>rate</td> + <td>Number of visitors completed</td> + </tr> + <tr> + <td>vds.visitor.allthreads.created</td> + <td>operation</td> + <td>rate</td> + <td>Number of visitors created.</td> + </tr> + <tr> + <td>vds.visitor.allthreads.failed</td> + <td>operation</td> + <td>rate</td> + <td>Number of visitors failed</td> + </tr> + <tr> + <td>vds.visitor.allthreads.averagemessagesendtime</td> + <td>millisecond</td> + <td>count, max, sum</td> + <td>Average time it takes for messages to be sent to their target (and be replied to)</td> + </tr> + <tr> + <td>vds.visitor.allthreads.averageprocessingtime</td> + <td>millisecond</td> + <td>count, max, sum</td> + <td>Average time used to process visitor requests</td> + </tr> + <tr> + <td>vds.filestor.queuesize</td> + <td>operation</td> + <td>count, max, sum</td> + <td>Size of input message queue.</td> + </tr> + <tr> + <td>vds.filestor.averagequeuewait</td> + <td>millisecond</td> + <td>count, max, sum</td> + <td>Average time an operation spends in input queue.</td> + </tr> + <tr> + <td>vds.filestor.active_operations.size</td> + <td>operation</td> + <td>count, max, sum</td> + <td>Number of concurrent active operations</td> + </tr> + <tr> + <td>vds.filestor.active_operations.latency</td> + <td>millisecond</td> + <td>count, max, sum</td> + <td>Latency (in ms) for completed operations</td> + </tr> + <tr> + <td>vds.filestor.throttle_window_size</td> + <td>operation</td> + <td>count, max, sum</td> + <td>Current size of async operation throttler window size</td> + </tr> + <tr> + <td>vds.filestor.throttle_waiting_threads</td> + <td>thread</td> + <td>count, max, sum</td> + <td>Number of threads waiting to acquire a throttle token</td> + </tr> + <tr> + <td>vds.filestor.throttle_active_tokens</td> + <td>instance</td> + <td>count, max, sum</td> + <td>Current number of active throttle tokens</td> + </tr> + <tr> + <td>vds.filestor.allthreads.mergemetadatareadlatency</td> + <td>millisecond</td> + <td>count, max, sum</td> + <td>Time spent in a merge step to check metadata of current node to see what data it has.</td> + </tr> + <tr> + <td>vds.filestor.allthreads.mergedatareadlatency</td> + <td>millisecond</td> + <td>count, max, sum</td> + <td>Time spent in a merge step to read data other nodes need.</td> + </tr> + <tr> + <td>vds.filestor.allthreads.mergedatawritelatency</td> + <td>millisecond</td> + <td>count, max, sum</td> + <td>Time spent in a merge step to write data needed to current node.</td> + </tr> + <tr> + <td>vds.filestor.allthreads.merge_put_latency</td> + <td>millisecond</td> + <td>count, max, sum</td> + <td>Latency of individual puts that are part of merge operations</td> + </tr> + <tr> + <td>vds.filestor.allthreads.merge_remove_latency</td> + <td>millisecond</td> + <td>count, max, sum</td> + <td>Latency of individual removes that are part of merge operations</td> + </tr> + <tr> + <td>vds.filestor.allstripes.throttled_rpc_direct_dispatches</td> + <td>instance</td> + <td>rate</td> + <td>Number of times an RPC thread could not directly dispatch an async operation directly to Proton because it was disallowed by the throttle policy</td> + </tr> + <tr> + <td>vds.filestor.allstripes.throttled_persistence_thread_polls</td> + <td>instance</td> + <td>rate</td> + <td>Number of times a persistence thread could not immediately dispatch a queued async operation because it was disallowed by the throttle policy</td> + </tr> + <tr> + <td>vds.filestor.allstripes.timeouts_waiting_for_throttle_token</td> + <td>instance</td> + <td>rate</td> + <td>Number of times a persistence thread timed out waiting for an available throttle policy token</td> + </tr> + <tr> + <td>vds.filestor.allthreads.put.count</td> + <td>operation</td> + <td>rate</td> + <td>Number of requests processed.</td> + </tr> + <tr> + <td>vds.filestor.allthreads.put.failed</td> + <td>operation</td> + <td>rate</td> + <td>Number of failed requests.</td> + </tr> + <tr> + <td>vds.filestor.allthreads.put.test_and_set_failed</td> + <td>operation</td> + <td>rate</td> + <td>Number of operations that were skipped due to a test-and-set condition not met</td> + </tr> + <tr> + <td>vds.filestor.allthreads.put.latency</td> + <td>millisecond</td> + <td>count, max, sum</td> + <td>Latency of successful requests.</td> + </tr> + <tr> + <td>vds.filestor.allthreads.put.request_size</td> + <td>byte</td> + <td>count, max, sum</td> + <td>Size of requests, in bytes</td> + </tr> + <tr> + <td>vds.filestor.allthreads.remove.count</td> + <td>operation</td> + <td>rate</td> + <td>Number of requests processed.</td> + </tr> + <tr> + <td>vds.filestor.allthreads.remove.failed</td> + <td>operation</td> + <td>rate</td> + <td>Number of failed requests.</td> + </tr> + <tr> + <td>vds.filestor.allthreads.remove.test_and_set_failed</td> + <td>operation</td> + <td>rate</td> + <td>Number of operations that were skipped due to a test-and-set condition not met</td> + </tr> + <tr> + <td>vds.filestor.allthreads.remove.latency</td> + <td>millisecond</td> + <td>count, max, sum</td> + <td>Latency of successful requests.</td> + </tr> + <tr> + <td>vds.filestor.allthreads.remove.request_size</td> + <td>byte</td> + <td>count, max, sum</td> + <td>Size of requests, in bytes</td> + </tr> + <tr> + <td>vds.filestor.allthreads.get.count</td> + <td>operation</td> + <td>rate</td> + <td>Number of requests processed.</td> + </tr> + <tr> + <td>vds.filestor.allthreads.get.failed</td> + <td>operation</td> + <td>rate</td> + <td>Number of failed requests.</td> + </tr> + <tr> + <td>vds.filestor.allthreads.get.latency</td> + <td>millisecond</td> + <td>count, max, sum</td> + <td>Latency of successful requests.</td> + </tr> + <tr> + <td>vds.filestor.allthreads.get.request_size</td> + <td>byte</td> + <td>count, max, sum</td> + <td>Size of requests, in bytes</td> + </tr> + <tr> + <td>vds.filestor.allthreads.update.count</td> + <td>request</td> + <td>rate</td> + <td>Number of requests processed.</td> + </tr> + <tr> + <td>vds.filestor.allthreads.update.failed</td> + <td>request</td> + <td>rate</td> + <td>Number of failed requests.</td> + </tr> + <tr> + <td>vds.filestor.allthreads.update.test_and_set_failed</td> + <td>request</td> + <td>rate</td> + <td>Number of requests that were skipped due to a test-and-set condition not met</td> + </tr> + <tr> + <td>vds.filestor.allthreads.update.latency</td> + <td>millisecond</td> + <td>count, max, sum</td> + <td>Latency of successful requests.</td> + </tr> + <tr> + <td>vds.filestor.allthreads.update.request_size</td> + <td>byte</td> + <td>count, max, sum</td> + <td>Size of requests, in bytes</td> + </tr> + <tr> + <td>vds.filestor.allthreads.createiterator.count</td> + <td>request</td> + <td>rate</td> + <td>Number of requests processed.</td> + </tr> + <tr> + <td>vds.filestor.allthreads.createiterator.latency</td> + <td>millisecond</td> + <td>count, max, sum</td> + <td>Latency of successful requests.</td> + </tr> + <tr> + <td>vds.filestor.allthreads.visit.count</td> + <td>request</td> + <td>rate</td> + <td>Number of requests processed.</td> + </tr> + <tr> + <td>vds.filestor.allthreads.visit.latency</td> + <td>millisecond</td> + <td>count, max, sum</td> + <td>Latency of successful requests.</td> + </tr> + <tr> + <td>vds.filestor.allthreads.remove_location.count</td> + <td>request</td> + <td>rate</td> + <td>Number of requests processed.</td> + </tr> + <tr> + <td>vds.filestor.allthreads.remove_location.latency</td> + <td>millisecond</td> + <td>count, max, sum</td> + <td>Latency of successful requests.</td> + </tr> + <tr> + <td>vds.filestor.allthreads.splitbuckets.count</td> + <td>request</td> + <td>rate</td> + <td>Number of requests processed.</td> + </tr> + <tr> + <td>vds.filestor.allthreads.joinbuckets.count</td> + <td>request</td> + <td>rate</td> + <td>Number of requests processed.</td> + </tr> + <tr> + <td>vds.filestor.allthreads.deletebuckets.count</td> + <td>request</td> + <td>rate</td> + <td>Number of requests processed.</td> + </tr> + <tr> + <td>vds.filestor.allthreads.deletebuckets.failed</td> + <td>request</td> + <td>rate</td> + <td>Number of failed requests.</td> + </tr> + <tr> + <td>vds.filestor.allthreads.deletebuckets.latency</td> + <td>millisecond</td> + <td>count, max, sum</td> + <td>Latency of successful requests.</td> + </tr> + <tr> + <td>vds.filestor.allthreads.remove_by_gid.count</td> + <td>request</td> + <td>rate</td> + <td>Number of requests processed.</td> + </tr> + <tr> + <td>vds.filestor.allthreads.remove_by_gid.failed</td> + <td>request</td> + <td>rate</td> + <td>Number of failed requests.</td> + </tr> + <tr> + <td>vds.filestor.allthreads.remove_by_gid.latency</td> + <td>millisecond</td> + <td>count, max, sum</td> + <td>Latency of successful requests.</td> + </tr> + <tr> + <td>vds.filestor.allthreads.setbucketstates.count</td> + <td>request</td> + <td>rate</td> + <td>Number of requests processed.</td> + </tr> + <tr> + <td>vds.mergethrottler.averagequeuewaitingtime</td> + <td>millisecond</td> + <td>count, max, sum</td> + <td>Time merges spent in the throttler queue</td> + </tr> + <tr> + <td>vds.mergethrottler.queuesize</td> + <td>instance</td> + <td>count, max, sum</td> + <td>Length of merge queue</td> + </tr> + <tr> + <td>vds.mergethrottler.active_window_size</td> + <td>instance</td> + <td>count, max, sum</td> + <td>Number of merges active within the pending window size</td> + </tr> + <tr> + <td>vds.mergethrottler.estimated_merge_memory_usage</td> + <td>byte</td> + <td>count, max, sum</td> + <td>An estimated upper bound of the memory usage (in bytes) of the merges currently in the active window</td> + </tr> + <tr> + <td>vds.mergethrottler.bounced_due_to_back_pressure</td> + <td>instance</td> + <td>rate</td> + <td>Number of merges bounced due to resource exhaustion back-pressure</td> + </tr> + <tr> + <td>vds.mergethrottler.locallyexecutedmerges.ok</td> + <td>instance</td> + <td>rate</td> + <td>The number of successful merges for 'locallyexecutedmerges'</td> + </tr> + <tr> + <td>vds.mergethrottler.mergechains.ok</td> + <td>operation</td> + <td>rate</td> + <td>The number of successful merges for 'mergechains'</td> + </tr> + <tr> + <td>vds.mergethrottler.mergechains.failures.busy</td> + <td>operation</td> + <td>rate</td> + <td>The number of merges that failed because the storage node was busy</td> + </tr> + <tr> + <td>vds.mergethrottler.mergechains.failures.total</td> + <td>operation</td> + <td>rate</td> + <td>Sum of all failures</td> + </tr> + <tr> + <td>vds.server.network.tls-handshakes-failed</td> + <td>operation</td> + <td>count</td> + <td>Number of client or server connection attempts that failed during TLS handshaking</td> + </tr> + <tr> + <td>vds.server.network.peer-authorization-failures</td> + <td>failure</td> + <td>count</td> + <td>Number of TLS connection attempts failed due to bad or missing peer certificate credentials</td> + </tr> + <tr> + <td>vds.server.network.client.tls-connections-established</td> + <td>connection</td> + <td>count</td> + <td>Number of secure mTLS connections established</td> + </tr> + <tr> + <td>vds.server.network.server.tls-connections-established</td> + <td>connection</td> + <td>count</td> + <td>Number of secure mTLS connections established</td> + </tr> + <tr> + <td>vds.server.network.client.insecure-connections-established</td> + <td>connection</td> + <td>count</td> + <td>Number of insecure (plaintext) connections established</td> + </tr> + <tr> + <td>vds.server.network.server.insecure-connections-established</td> + <td>connection</td> + <td>count</td> + <td>Number of insecure (plaintext) connections established</td> + </tr> + <tr> + <td>vds.server.network.tls-connections-broken</td> + <td>connection</td> + <td>count</td> + <td>Number of TLS connections broken due to failures during frame encoding or decoding</td> + </tr> + <tr> + <td>vds.server.network.failed-tls-config-reloads</td> + <td>failure</td> + <td>count</td> + <td>Number of times background reloading of TLS config has failed</td> + </tr> + <tr> + <td>vds.server.network.rpc-capability-checks-failed</td> + <td>failure</td> + <td>count</td> + <td>Number of RPC operations that failed due to one or more missing capabilities</td> + </tr> + <tr> + <td>vds.server.network.status-capability-checks-failed</td> + <td>failure</td> + <td>count</td> + <td>Number of status page operations that failed due to one or more missing capabilities</td> + </tr> + <tr> + <td>vds.server.fnet.num-connections</td> + <td>connection</td> + <td>count</td> + <td>Total number of connection objects</td> + </tr> + </tbody> +</table> \ No newline at end of file diff --git a/en/reference/operations/self-managed/tools.html b/en/reference/operations/self-managed/tools.html deleted file mode 100644 index 0106c78df6..0000000000 --- a/en/reference/operations/self-managed/tools.html +++ /dev/null @@ -1,2706 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Vespa Command-line Tools" -applies_to: self-managed -redirect_from: -- /en/reference/vespa-cmdline-tools.html -- /en/operations-selfhosted/vespa-cmdline-tools.html ---- - -<p> -This is the reference for the Vespa command-line tools. -</p> - -{% include note.html content='The tools listed on this page are primarily used -for operating and debugging a <em>self-hosted</em> Vespa instance. For most -use-cases, we recommend the <a href="../../../clients/vespa-cli.html">Vespa CLI</a>, which -should work against most Vespa applications regardless of how they are deployed.' %} - -<p> -You can run these tools in <a href="https://hub.docker.com/r/vespaengine/vespa/tags">Vespa Docker image</a>: -<p> -<pre> -docker run --entrypoint bash vespaengine/vespa ./opt/vespa/bin/[tool] [args] -</pre> - - -<!--h2 id="vespa-config-ctl">vespa-config-ctl</h2--> -<!--h2 id="vespa-config-loadtester">vespa-config-loadtester</h2--> - - -<h2 id="vespa-configproxy-cmd">vespa-configproxy-cmd</h2> -<p> -<em>vespa-configproxy-cmd</em> is a status and control tool for the -<a href="../../../operations/self-managed/config-proxy.html">config proxy</a>. -The config proxy runs on all nodes as a proxy for one or more -<a href="../../../operations/self-managed/configuration-server.html">config servers</a>. -It connects to the config proxy on localhost by default. -</p> -<p> - Run it without arguments to dump a list of active configIds - format specification: -</p> -<p> - <code><config definition name>,<config id>,<config generation>,<MD5 checksum>,<xxHash checksum></code> -</p> -<p> -Synopsis: <code>vespa-configproxy-cmd [args]</code> -</p><p> -Example: -</p> -<pre> -$ vespa-configproxy-cmd -m sources -</pre> -<p> - Find a more comprehensive example in - <a href="../../../operations/self-managed/config-proxy.html#inspecting-config">inspecting config</a>. -</p> -<table class="table"> - <thead> - <tr> - <th>Option</th> - <th>Description</th> - </tr> - </thead> - <tbody> - <tr> - <th>-m</th> - <td> - method, available methods are: - <table class="table"> - <thead></thead><tbody> - <tr> - <th>cache</th> - <td> - Output the config proxy cache content (overview) - </td> - </tr><tr> - <th>gc</th> - <td> - <!-- ToDo --> - </td> - </tr><tr> - <th>dumpcache</th> - <td> - <!-- ToDo --> - </td> - </tr><tr> - <th>statistics</th> - <td> - <!-- ToDo --> - </td> - </tr><tr> - <th>heap</th> - <td> - <!-- ToDo --> - </td> - </tr><tr> - <th>getConfig</th> - <td> - Get config (see <a href="#vespa-get-config">vespa-get-config</a>) - </td> - </tr><tr> - <th>getmode</th> - <td> - Outputs the current mode of the config proxy - </td> - </tr><tr> - <th>setmode</th> - <td> - Use <em>default</em> or <em>memorycache</em> - - <a href="../../../operations/self-managed/config-proxy.html">example</a> - </td><!-- ToDo need a list here - are more options supported? --> - </tr><tr> - <th>invalidatecache</th> - <td> - Clears the current cache in the config proxy - </td> - </tr><tr> - <th>cachefull</th> - <td> - Output the config proxy cache content (including config payload) - </td> - </tr><tr> - <th>sources</th> - <td> - Output the config proxy's upstream config sources - </td> - </tr><tr> - <th>updatesources</th> - <td> - Updates the config proxy's upstream config sources to the supplied ones - </td> - </tr> - </tbody> - </table> - </td> - </tr><tr> - <th>-p</th> - <td> - port number, optional - </td> - </tr><tr> - <th>-s</th> - <td> - hostname, optional - </td> - </tr><tr> - <th>-h</th> - <td> - help text and usage - </td> - </tr> - </tbody> -</table> - - - -<h2 id="vespa-configserver-remove-state">vespa-configserver-remove-state</h2> -<p> -<em>vespa-configserver-remove-state</em> removes the config server state on the host. -</p><p> -Synopsis: <code>vespa-configserver-remove-state [-force]</code> -</p> -<table class="table"> - <thead> - <tr> - <th>Option</th> - <th>Description</th> - </tr> - </thead> - <tbody> - <tr> - <th>-force</th> - <td>Do not ask for confirmation before removal</td> - </tr> - </tbody> -</table> - - - -<h2 id="vespa-config-status">vespa-config-status</h2> -<p> -<em>vespa-config-status</em> can run on any machine in a Vespa cluster -and outputs a list of all running services which is running with an outdated application package. -It can be invoked without any arguments or with optional arguments. -</p><p> -Synopsis: <code>vespa-config-status [-v] [-c host] [-c host:port] [-f host0,...,hostN]</code> -</p><p> -Example: -</p> -<pre> -$ vespa-config-status -</pre> -<table class="table"> - <thead> - <tr> - <th>Option</th> - <th>Description</th> - </tr> - </thead> - <tbody> - <tr> - <th>-v</th> - <td>Verbose - show all services, even if they are up-to-date</td> - </tr><tr> - <th>-c arg</th> - <td>Get the Vespa cluster configuration from the config server specified by host and port. - Use <em>host</em> or <em>host:port</em> for config server</td> - </tr><tr> - <th>-f</th> - <td>Filter to only query config status for the given comma-separated set of hosts</td> - </tr> - </tbody> -</table> - - - -<!--h2 id="vespa-config-verification">vespa-config-verification</h2--> -<!--h2 id="vespa-create-idx-from-dat">vespa-create-idx-from-dat</h2--> - - - -<h2 id="vespa-deploy">vespa-deploy</h2> -<p> -<em>vespa-deploy</em> is a standalone tool to deploy an application package. -Prefer the <a href="../../../clients/vespa-cli.html#deployment">Vespa CLI</a> instead. -Under the hood, deployment uses the <a href="/en/reference/api/deploy-v2.html">deploy REST API</a>, -which you can also use directly. -Refer to the <a href="../../applications/application-packages.html#deploy">deploy reference</a> for details. -</p><p> -Synopsis: <code>vespa-deploy [-h] [-v] [-n] [-f] [-t timeout] [-c hostname] [-p port] prepare|activate|upload|fetch|help [args]</code> -</p><p> -Example: -</p> -<pre> -$ vespa-deploy prepare [application-path|zip-file] && vespa-deploy activate -</pre> -<pre> -$ vespa-deploy prepare app.zip && vespa-deploy activate -</pre> -<table class="table"> - <thead> - <tr> - <th>Command</th> - <th>Description</th> - </tr> - </thead><tbody> - <tr> - <th>prepare</th> - <td> - <em>vespa-deploy prepare</em> combines the - <a href="../../api/deploy-v2.html#create-session">upload</a> and - <a href="../../api/deploy-v2.html#prepare-session">prepare</a> steps. - </td> - </tr><tr> - <th>activate</th> - <td> - <em>vespa-deploy activate</em> invokes the - <a href="../../api/deploy-v2.html#activate-session">activate</a> step. - </td> - </tr><tr> - <th>upload</th> - <td> - <em>vespa-deploy upload</em> uploads an application package - </td> - </tr><tr> - <th>fetch</th> - <td> - <em>vespa-deploy fetch</em> fetches an application package. - Useful to get the active configuration for an instance. - </td> - </tr><tr> - <th>help</th> - <td> - Same as <strong>-h</strong> - </td> - </tr> - </tbody> -</table> -<table class="table"> - <thead> - <tr> - <th>Option</th> - <th>Description</th> - </tr> - </thead><tbody> - <tr> - <th>-h</th> - <td> - Show help text - </td> - </tr><tr> - <th>-v</th> - <td> - Verbose - </td> - </tr><tr> - <th>-n</th> - <td> - Dry-run deployment - </td> - </tr><tr> - <th>-f</th> - <td> - Force - ignore validation errors - </td> - </tr><tr> - <th>-t</th> - <td> - Timeout - </td> - </tr><tr> - <th>-c</th> - <td> - Config server hostname - </td> - </tr><tr> - <th>-p</th> - <td> - Config server port - </td> - </tr> - </tbody> -</table> - - - -<h2 id="vespa-destination">vespa-destination</h2> -<p> -<em>vespa-destination</em> is a simple receiver for messagebus messages. -It outputs messages received on stdout. -Also see <a href="#vespa-visit-target">vespa-visit-target</a>. -</p><p> -Synopsis: <code>vespa-destination [options]</code> -</p><p> -Example: -</p> -<pre> -$ vespa-destination --name msg_sink -</pre> -<table class="table"> - <thead> - <tr> - <th>Option</th> - <th>Description</th> - </tr> - </thead> - <tbody> - <tr> - <th>--instant</th> - <td>Reply in message thread</td> - </tr><tr> - <th>--name arg</th> - <td>Slobrok name to register</td> - </tr><tr> - <th>--maxqueuetime arg</th> - <td>Adjust the in-queue size to have a maximum queue wait period of this many ms - (default -1 = unlimited)</td> - </tr><tr> - <th>--silent #nummsg</th> - <td>Do not dump anything, but progress every #nummsg</td> - </tr><tr> - <th>--sleeptime arg</th> - <td>The number of milliseconds to sleep per message, to simulate processing time</td> - </tr><tr> - <th>--threads arg</th> - <td>The number of threads to process the incoming data</td> - </tr><tr> - <th>--verbose</th> - <td>Dump the contents of certain messages to stdout</td> - </tr> - </tbody> -</table> - - - -<!--h2 id="vespa-doclocator">vespa-doclocator</h2--> -<!--h2 id="vespa-documentstore-benchmark">vespa-documentstore-benchmark</h2--> -<!--h2 id="vespa-documentstore-inspect">vespa-documentstore-inspect</h2--> -<!--h2 id="vespa-eval-expr">vespa-eval-expr</h2--> -<!--h2 id="vespa-expand-config.pl">vespa-expand-config.pl</h2--> -<!--h2 id="vespa-external-providerproxy-conformancetest">vespa-external-providerproxy-conformancetest</h2--> - - -<!-- ToDo: remove? --> -<h2 id="vespa-fbench-filter-file">vespa-fbench-filter-file</h2> -<pre> -usage: vespa-fbench-filter-file [-a] [-h] [-m maxLineSize] - -Read concatenated logs from stdin and write -extracted query urls to stdout. - - -a : all parameters to the original query urls are preserved. - If the -a switch is not given, only 'query' and 'type' - parameters are kept in the extracted query urls. - -h : print this usage information. - -m <num> : max line size for input/output lines. - Can not be less than the default [10240] -</pre> - - -<!-- ToDo: remove? --> -<h2 id="vespa-fbench-geturl">vespa-fbench-geturl</h2> -<p></p> -<pre> -usage: vespa-fbench-geturl <host> <port> <url> -</pre> - - -<!-- ToDo: remove? --> -<h2 id="vespa-fbench-split-file">vespa-fbench-split-file</h2> -<p></p> -<pre> -usage: vespa-fbench-split-file [-p pattern] [-m maxLineSize] <numparts> [<file>] - -Reads from <file> (stdin if <file> is not given) and -randomly distributes each line between <numpart> output -files. The names of the output files are generated by -combining the <pattern> with sequential numbers using -the sprintf function. - - -p pattern : output name pattern ['query%03d.txt'] - -m <num> : max line size for input/output lines. - Can not be less than the default [10240] - <numparts> : number of output files to generate. -</pre> - - - -<h2 id="vespa-feeder">vespa-feeder</h2> -<p> -<em>vespa-feeder</em> is a feeding client that parses -<a href="../../schemas/document-json-format.html">JSON</a> input -as Vespa document operations and sends to a Vespa application. -It parses the content of the input sequentially and feeds each operation in order. -However, since many operations will be pending at any time, -and because the processing time of an operation varies, -there is no guarantee as to which order operations will reach the content nodes. -As this can be important when it comes to operations that apply to the same document ID, -there is logic in place to not send an operation for a document ID to which there is already a pending operation. -</p><p> -<em>vespa-feeder</em> prints a report at the end of the feed. -To print this report once a minute, use <em>--verbose</em>: -</p> -<pre> -Messages sent to vespa (route default) : ----------------------------------------- -PutDocument: ok: 999997 msgs/sec: 411.38 failed: 0 <span class="pre-hilite">ignored: 0</span> latency(min, max, avg): 2, 4360, 99 -</pre> -<p> -<em>ignored</em> reports the number of documents that could not be -routed to any <a href="../../applications/services/content.html">content clusters</a> because they did not match any of the -<a href="../../applications/services/content.html#documents">configured document types</a> or selections - examples are: -</p> -<ul> - <li> - A document type is removed from the application - and the feed file contains documents of this type - </li><li> - One or more selection expressions restrict the documents the cluster accepts, - and the feed file contains documents that are excluded. - An example is feeding <a href="../../../schemas/documents.html#document-expiry">expired documents</a> - - a selection for documents that are less than 30 days old - and the feed file contains documents that are 30+ days old - </li> -</ul> -<p> -Synopsis: <code>vespa-feeder [--abortondataerror true|false] [--abortonsenderror true|false] [--file arg] [--maxpending arg] [--maxpendingsize arg] [--maxfeedrate arg] [mode standard|benchmark] [--noretry] [--retrydelay arg] [--route arg] [--timeout arg] [--trace arg] [--validate] [--dumpDocuments filename] [--numthreads arg] [create-if-non-existent] [-v,--verbose] filename</code> -</p><p> -Example: -</p> -<pre> -$ vespa-feeder file.json -</pre> -<table class="table"> - <thead> - <tr> - <th>Option</th> - <th>Description</th> - </tr> - </thead> - <tbody> - <tr> - <th>--abortondataerror arg</th> - <td> - Abort if the input has errors (true|false) - default true. - Set to <em>false</em> in case the input has errors (e.g., invalid characters). - <em>vespa-feeder</em> notifies on parsing errors at the end of the feed, but it will not abort - </td> - </tr><tr> - <th>--abortonsenderror arg</th> - <td> - Abort if an error occurred while sending operations to Vespa (true|false) - default true - </td> - </tr><tr> - <th>--file arg</th> - <td> - Input files to read. These can also be passed as arguments without the option prefix. - If none is given, this tool parses identifiers from stdin - </td> - </tr><tr> - <th>--maxpending arg</th> - <td> - Maximum number of pending operations. This disables dynamic throttling, use with care - <!-- ToDo link to dynamic throttling doc --> - </td> - </tr><tr> - <th>--maxpendingsize arg</th> - <td> - Maximum size (in bytes) of pending operations - </td> - </tr><tr> - <th>--maxfeedrate arg</th> - <td> - Limits the feed rate to the given number (operations/second) - </td> - </tr><tr> - <th>--mode</th> - <td> - The mode to run vespa-feeder in (standard|benchmark) - default standard - </td> - </tr><tr> - <th>--noretry</th> - <td> - Disables retries of recoverable failures - </td> - </tr><tr> - <th>--retrydelay arg</th> - <td> - The time (in seconds) to wait between retries of a failed operation. Default 1 - </td> - </tr><tr> - <th>--route arg</th> - <td> - The <a href="../../../writing/document-routing.html">route</a> to send the data to. Default the <em>default</em> route - </td> - </tr><tr> - <th>--timeout arg</th> - <td> - Time (in seconds) allowed for sending operations. Default 180 - </td> - </tr><tr> - <th>--trace arg</th> - <td> - Trace level of network traffic. Default 0 - </td> - </tr><tr> - <th>--validate</th> - <td> - Run validation tool on input files - do not feed - </td> - </tr><tr> - <th>--dumpDocuments <filename></th> - <td> - File where documents in the put are serialized - </td> - </tr><tr> - <th>--numthreads arg</th> - <td> - How many threads to use for sending. Default 1 - </td> - </tr><tr> - <th>--create-if-non-existent</th> - <td> - Enable setting of create-if-non-existent to true on all document updates in the given feed - </td> - </tr><tr> - <th>-v, --verbose</th> - <td> - Enable verbose output of progress - </td> - </tr> - </tbody> -</table> - - - -<!--h2 id="vespa-fileheader-inspect">vespa-fileheader-inspect</h2--> -<!--h2 id="vespa-fsadump">vespa-fsadump</h2--> -<!--h2 id="vespa-fsainfo">vespa-fsainfo</h2--> -<!--h2 id="vespa-gbdt-converter">vespa-gbdt-converter</h2--> -<!--h2 id="vespa-gen-testdocs">vespa-gen-testdocs</h2--> - - - -<h2 id="vespa-get">vespa-get</h2> -<p> -<em>vespa-get</em> retrieves documents from a Vespa content cluster, and prints to <em>stdout</em>. -<em>vespa-get</em> retrieves documents identified by the document IDs passed as command line arguments. -If no document IDs are passed through the command line interface, -ids will be read from <em>stdin</em> - separated by line breaks. -</p><p> -Synopsis: <code>vespa-get <options> [documentid...]</code> -</p> -<table class="table"> - <thead> - <tr> - <th>Option</th> - <th>Description</th> - </tr> - </thead> - <tbody> - <tr> - <th>-a,--trace <em>tracelevel</em></th> - <td>Trace level to use (default 0)</td> - </tr><tr> - <th>-c,--configid <em>configid</em></th><!-- ToDo link to config id --> - <td>Use the specified config id for messagebus configuration</td> - </tr><tr> - <th>-f,--fieldset <em>fieldset</em></th> - <td> - Retrieve the specified fields only - (see <a href="../../../schemas/documents.html#fieldsets">Document field sets</a>). - Default: <code>[document]</code> - </td> - </tr><tr> - <th>-h,--help</th> - <td>Show this syntax page</td> - </tr><tr> - <th>-i,--printids</th> - <td>Show only identifiers of retrieved documents</td> - </tr><tr> - <th>-j,--jsonoutput</th> - <td>JSON output (default)</td> - </tr><tr> - <th>-l,--loadtype <em>loadtype</em></th><!-- ToDo link to loadtypes --> - <td>Load type (default "")</td> - </tr><tr> - <th>-n,--noretry</th> - <td>Do not retry operation on transient errors, as is default</td> - </tr><tr> - <th>-r,--route <em>route</em></th> - <td>Send request to the given messagebus route</td> - </tr><tr> - <th>-s,--showdocsize</th> - <td>Show binary size of document</td> - </tr><tr> - <th>--shorttensors</th> - <td>Output using <a href="../../schemas/document-json-format.html#tensor">tensor short form</a></td> - </tr><tr> - <th>-t,--timeout <em>timeout</em></th> - <td>Set timeout for the request in seconds (default 0)</td> - </tr><tr> - <th>-u,--cluster <em>cluster</em></th> - <td>Send request to the given content cluster</td> - </tr> - </tbody> -</table> - - - -<h2 id="vespa-get-cluster-state">vespa-get-cluster-state</h2> -<p> -Get cluster state - refer to <a href="../../../content/content-nodes.html">content nodes</a>. -</p><p> -Synopsis: <code>vespa-get-cluster-state [options]</code> -</p> -<table class="table"> - <thead> - <tr> - <th>Option</th> - <th>Description</th> - </tr> - </thead> - <tbody> - <tr> - <th>-h, --help</th> - <td> - Show help - </td> - </tr><tr> - <th>-v</th> - <td> - More verbose output - </td> - </tr><tr> - <th>-s</th> - <td> - Less verbose output - </td> - </tr><tr> - <th>--show-hidden</th> - <td> - Also show hidden undocumented debug options - </td> - </tr><tr> - <th>-c, --cluster</th> - <td> - The cluster name of the cluster to query. - If unspecified, and vespa is installed on the current node, - information will be attempted auto-extracted - </td> - </tr><tr> - <th>-f, --force</th> - <td> - Force execution - </td> - </tr><tr> - <th>--config-server</th> - <td> - Host name of the config server to query - </td> - </tr><tr> - <th>--config-server-port</th> - <td> - Port to connect to the config server on - </td> - </tr><tr> - <th>--config-request-timeout</th> - <td> - Timeout of config request - </td> - </tr> - </tbody> -</table> - - - -<h2 id="vespa-get-config">vespa-get-config</h2> -<p> -<em>vespa-get-config</em> is a command-line tool to get configuration -from a <a href="../../../operations/self-managed/configuration-server.html">config server</a> -or <a href="../../../operations/self-managed/config-proxy.html">config proxy</a>. -By default, it connects to the config proxy on localhost, fetches config from its cache -and prints the config payload on stdout. -Configuration is addressed using name and <a href="../../../applications/configapi-dev.html#config-id">configId</a>. -If configId is omitted, the global and default data for that name is returned. -The default port number is 19090, the config proxy's port - use 19070 to access a config server. -Also check <a href="../../../operations/self-managed/files-processes-and-ports.html">ports</a>. -</p> - -<p> -Synopsis: <code>vespa-get-config -n defName -i configId <option> [args]</code> -</p><p> -Example: -</p> -<pre> -$ vespa-get-config -n container.statistics -i search/cluster.search -</pre> -<p> - Find a more comprehensive example in - <a href="../../../operations/self-managed/config-proxy.html#inspecting-config">inspecting config</a>. -</p> -<table class="table"> - <thead> - <tr> - <th>Option</th> - <th>Description</th> - </tr> - </thead> - <tbody> - <tr> - <th>-n</th> - <td> - config definition name, including namespace (on the form <namespace>.<name>) - </td> - </tr><tr> - <th>-i</th> - <td> - config id, optional - </td> - </tr><tr> - <th>-a</th> - <td> - config def schema file, optional (if you want to use another schema than the one known for the config server) - </td> - </tr><tr> - <th>-m</th> - <td> - defMd5, optional - </td> - </tr><tr> - <th>-c</th> - <td> - configMd5, optional - </td> - </tr><tr> - <th>-t</th> - <td> - server timeout, in seconds, default value 3, optional - </td> - </tr><tr> - <th>-w</th> - <td> - timeout, default value 10, optional - </td> - </tr><tr> - <th>-s</th> - <td> - server hostname, default localhost, optional - </td> - </tr><tr> - <th>-p</th> - <td> - port, default 19090, optional - </td> - </tr><tr> - <th>-d</th> - <td> - debug mode, optional - </td> - </tr><tr> - <th>-h</th> - <td> - help text and usage - </td> - </tr> - </tbody> -</table> - - - -<h2 id="vespa-get-node-state">vespa-get-node-state</h2> -<p> -Get the state of one or more storage services from the fleet controller - -refer to <a href="../../../content/content-nodes.html">content nodes</a>: -</p> -<table class="table"> - <thead> - <tr> - <th>State</th> - <th>Description</th> - </tr> - </thead> - <tbody> -<tr> - <th>Unit state</th> - <td>The state of the node seen from the cluster controller.</td> -</tr><tr> - <th>User state</th> - <td>The state the administrator wants the node to be in, default "up". - Can be set by using <a href="#vespa-set-node-state">vespa-set-node-state</a> - or by the cluster controller</td> -</tr><tr> - <th style="white-space:nowrap;">Generated state</th> - <td>The state of a given node in the current cluster state. - This is the state all the other nodes know about. - This state is a product of the other two states - and cluster controller logic to keep the cluster stable.</td> -</tr> - </tbody> -</table> -<p> -Synopsis: <code>vespa-get-node-state [options]</code> -</p> -<table class="table"> - <thead> - <tr> - <th>Option</th> - <th>Description</th> - </tr> - </thead> - <tbody> - <tr> - <th>-h, --help</th> - <td> - Show help - </td> - </tr><tr> - <th>-v</th> - <td> - More verbose output - </td> - </tr><tr> - <th>-s</th> - <td> - Less verbose output - </td> - </tr><tr> - <th>--show-hidden</th> - <td> - Also show hidden undocumented debug options - </td> - </tr><tr> - <th>-c, --cluster</th> - <td> - The cluster name of the cluster to query. - If unspecified, and vespa is installed on the current node, - information will be attempted auto-extracted - </td> - </tr><tr> - <th>-f, --force</th> - <td> - Force execution - </td> - </tr><tr> - <th>-t, --type</th> - <td> - Node type - can either be 'storage' or 'distributor'. - If not specified, the operation will use state for both types - </td> - </tr><tr> - <th>-i, --index</th> - <td> - Node index. If not specified, - all nodes found running on this host will be used - </td> - </tr><tr> - <th>--config-server</th> - <td> - Host name of the config server to query - </td> - </tr><tr> - <th>--config-server-port</th> - <td> - Port to connect to the config server on - </td> - </tr><tr> - <th>--config-request-timeout</th> - <td> - Timeout of config request - </td> - </tr> - </tbody> -</table> - - -<h2 id="vespa-index-inspect">vespa-index-inspect</h2> -<p> -Use <em>vespa-index-inspect</em> to inspect indexed data on a content node. -To troubleshoot <a href="../../../linguistics/query-rewriting.html">query rewriting</a> -and <a href="../../../linguistics/linguistics.html">linguistic transformations</a> -use this <a href="../../../querying/text-matching.html#index-and-attribute">guide</a> instead. -</p><p> -It shows posting list information (per token or all/range), or dumps the indexed tokens: -</p> -<pre> -vespa-index-inspect showpostings [--indexdir indexDir] --field field word - -vespa-index-inspect showpostings [--indexdir indexDir] [--field field] --transpose \ - [--docidlimit docIdLimit] [--mindocid mindocid] - -vespa-index-inspect dumpwords [--indexdir indexDir] --field field \ - [--minnumdocs minnumdocs] [--verbose] [--wordnum] -</pre> -<p> -Synopsis:<code>vespa-index-inspect showpostings|dumpwords [--indexdir path] [--field fieldname] [--transpose] [--minnumdocs count] [--docidlimit docIdLimit] [--mindocid mindocid] [--verbose] [--wordnum] [word]</code> -</p><p> -Example (make sure to flush the index before using): -</p> -<pre> -$ <a href="#vespa-proton-cmd">vespa-proton-cmd</a> --local triggerFlush && \ - vespa-index-inspect dumpwords \ - --indexdir /opt/vespa/var/db/vespa/search/cluster.music/n0/documents/music/0.ready/index/index.flush.1 \ - --field artist -bad 2 -so 1 -</pre> -<table class="table"> - <thead> - <tr> - <th>Option</th> - <th>Description</th> - </tr> - </thead> - <tbody> - <tr> - <th>--indexdir <em>path</em></th> - <td>Index location</td> - </tr><tr> - <th>--field <em>fieldname</em></th> - <td>Field to analyze</td> - </tr><tr> - <th>--transpose</th> - <td>Dump all tokens</td> - </tr><tr> - <th>--minnumdocs <em>count</em></th> - <td>Minimum number of documents to analyze</td> - </tr><tr> - <th>--docidlimit <em>docid</em></th> - <td>Dump up to this doc id</td> - </tr><tr> - <th>--mindocid <em>docid</em></th> - <td>Start from this docid</td> - </tr><tr> - <th>--wordnum</th> - <td>Also dump token numbers</td> - </tr><tr> - <th>--verbose</th> - <td>Verbose output</td> - </tr> - </tbody> -</table> - - -<h2 id="vespa-attribute-inspect">vespa-attribute-inspect</h2> -<p> -Use <em>vespa-attribute-inspect</em> to inspect the content of an attribute field on a content node. -To troubleshoot <a href="../../../linguistics/query-rewriting.html">query rewriting</a> -and <a href="../../../linguistics/linguistics.html">linguistic transformations</a> -use this <a href="../../../querying/text-matching.html#index-and-attribute">guide</a> instead. -</p><p> -Synopsis: <code>vespa-attribute-inspect [-p attribute] [-a] [-s attribute] <attribute></code> -</p><p> -Example (make sure to flush the attribute before using): -</p> -<pre> -$ <a href="#vespa-proton-cmd">vespa-proton-cmd</a> --local triggerFlush && \ - vespa-attribute-inspect -p /opt/vespa/var/db/vespa/search/cluster.music/n0/documents/music/0.ready/attribute/year/snapshot-10/year && \ - cat /opt/vespa/var/db/vespa/search/cluster.music/n0/documents/music/0.ready/attribute/year/snapshot-10/year.out -</pre> -<table class="table"> - <thead> - <tr> - <th>Option</th> - <th>Description</th> - </tr> - </thead> - <tbody> - <tr> - <th>-p</th> - <td>print content to <attribute>.out</td> - </tr><tr> - <th>-s</th> - <td>save attribute to <attribute>.save.dat</td> - </tr> - </tbody> -</table> - - -<h2 id="vespa-jvm-dumper">vespa-jvm-dumper</h2> -<p> - Dump JVM heap, thread stacks, and other debugging information from a Java-based Vespa service. -</p> -<p> - Invoke binary without arguments to print help and list the services running on this node. -</p> -<pre> -$ vespa-jvm-dumper -</pre> -<p> - Produce JVM debugging information by invoking the binary with the config ID of the target service and the output directory. -</p> -<pre> -$ vespa-jvm-dumper default/container.1 /opt/vespa/tmp/jvm-dump -</pre> - -<!--h2 id="vespa-log-conv">vespa-log-conv</h2--> - - - -<h2 id="vespa-logctl">vespa-logctl</h2> -<p> -Print or modify log levels for a VESPA service, stored in -<em>$VESPA_HOME/var/db/vespa/logcontrol/service.logcontrol</em>. -Refer to <a href="../log-files.html#controlling-log-levels">controlling log levels</a> for details. -<em>component-specification</em> specifies which subcomponents of the service should be controlled. -If empty, all components are controlled: -</p> -<ul> - <li><code>x.</code> : Matches only component x</li> - <li><code>x</code> : Matches component x and all its subcomponents</li> -</ul> -<p> -Synopsis (show log levels): <code>vespa-logctl [OPTION] <service>[:component-specification]</code> -</p><p> -Synopsis (set log levels): <code>vespa-logctl [OPTION] <service>[:component-specification] <level-mods></code> -</p><p> -<em>level-mods</em> are defined as : - <level>=<on|off>[,<level>=<on|off>]... -</p><p> -<em>level</em> is one of: -<code>all</code>, <code>fatal</code>, <code>error</code>, <code>warning</code>, -<code>info</code>, <code>event</code>, <code>config</code>, <code>debug</code>, <code>spam</code> -</p><p> -Example: For service <code>container</code>, set <code>com.yahoo.search.searchchain</code> -and all subcomponents of <code>com.yahoo.search.searchchain</code> to enable all except spam and debug: -</p> -<pre> -$ vespa-logctl container:com.yahoo.search.searchchain all=on,spam=off,debug=off -</pre> -<table class="table"> - <thead> - <tr> - <th>Option</th> - <th>Description</th> - </tr> - </thead> - <tbody> - <tr> - <th>-c</th> - <td>Create the control file if it does not exist (implies -n)</td> - </tr><tr> - <th>-a</th> - <td>Update all .logcontrol files</td> - </tr><tr> - <th>-r</th> - <td>Reset to default levels</td> - </tr><tr> - <th>-n</th> - <td>Create the component entry if it does not exist</td> - </tr><tr> - <th>-f <em>file</em></th> - <td>Use <file> as the log control file</td> - </tr><tr> - <th>-d <em>dir</em></th> - <td>Look in <dir> for log control files</td> - </tr> - </tbody> -</table> - - - -<h2 id="vespa-logfmt">vespa-logfmt</h2> -<p> -<code>vespa-logfmt</code> reads Vespa log files, selects messages, -and writes a formatted version of these messages to standard output. -If no file argument is given, vespa-logfmt will read the last Vespa log -file <code>$VESPA_HOME/logs/vespa/vespa.log</code> -(this also works with the <code>-f</code> option). -Otherwise, reads only the files given as arguments. -To read standard input, supply a single dash ’-’ as a file argument. -Refer to the <a href="../log-files.html">logs reference</a>. -</p><p> -Synopsis: <code> -vespa-logfmt [-l levellist ] [-s fieldlist ] [-p pid ] [-S service ] [-H host ] -[-c regex ] [-m regex ] [-f ] [-N ] [-t ] [-ts ] [file …]</code> -</p><p> -Examples: -</p><p> -Display only messages with log level "event", printing a -human-readable time (without any fractional seconds), -the service generating the event and the event message: -</p> -<pre> -$ vespa-logfmt -l event -s fmttime,service,message -... -[2017-09-05 06:16:16] config-sentinel stopped/1 name="sbin/vespa-config-sentinel -c hosts/vespa-container (pid 1558)" pid=1558 exitcode=1 -[2017-09-05 06:16:16] config-sentinel starting/1 name="sbin/vespa-config-sentinel -c hosts/vespa-container (pid 1564)" -[2017-09-05 06:16:16] config-sentinel started/1 name="config-sentinel" -[2017-09-05 06:17:00] configserver count/1 name=configserver.failedRequests value=0 -[2017-09-05 06:17:00] configserver count/1 name=procTime value=0 -[2017-09-05 06:17:00] configserver count/1 name=configserver.requests value=0 -</pre> -<p> -Display messages with log levels that are <em>not</em> any of -<em>info, debug,</em> or <em>event,</em> printing the time in seconds -and microseconds, the log level, the component name, and the message text: -</p> -<pre> -$ vespa-logfmt -l all-info,-debug -s level -s time,usecs,component,message -t -l -event -... -1504592294.738000 WARNING : configproxy.com No config found for name=sentinel,namespace=cloud.config,configId=hosts/vespa-container within timeout, will retry -1504592296.388000 WARNING : configproxy.com Request callback failed: APPLICATION_NOT_LOADED. Connection spec: tcp/localhost:19070, error message: Failed request (No application exists) from Connection { Socket[addr=/127.0.0.1,port=37806,localport=19070] } -1504592307.949461 WARNING : config-sentinel Connection to tcp/localhost:19090 failed or timed out -1504592307.949587 WARNING : config-sentinel FRT Connection tcp/localhost:19090 suspended until 2017-09-05 06:19:07 GMT -</pre> -<table class="table"> - <thead> - <tr> - <th>Option</th> - <th>Description</th> - </tr> - </thead> - <tbody> - <tr> - <th style="white-space:nowrap;">-l <em>levellist</em> (--level=<em>levellist</em>)</th> - <td> - Filter messages by log level. By default, only messages of - level <em>fatal, error, warning</em>, and <em>info</em> will be - included, while messages of level <em>config, event, debug</em>, - and <em>spam</em> will be ignored. This option allows you to replace - or modify the list of log levels to be included. - <em>levellist</em> is a comma-separated list of level names. - <ul> - <li>The name <em>all</em> may be used to add all known levels</li> - <li>You may use + or - in front of terms to add or remove from the - current (or default) list of levels instead of replacing it</li> - <li>Adding term</li> - </ul> - </td> - </tr><tr> - <th>-s <em>fieldlist</em></th> - <td>Select which fields of log messages to show. - The output field order is fixed. - When using this option, only the named fields will be printed. - The default fields are as - [<strong>-s fmttime,msecs,level,service,component,message</strong>]. - The fieldlist is a comma-separated list of field names. - The name <em>all</em> may be used to add all possible fields. - Prepending a minus sign will turn off the display of the named field. - Starting the list with a plus sign will add and remove fields - from the current (or default) list of fields instead of replacing it. - Using this option several times works as if the given <em>fieldlist</em> arguments - had been concatenated into one comma-separated list. Fields: - <table class="table"> - <thead></thead><tbody> - <tr> - <th>time</th> - <td> - Print the time in seconds since the epoch. - Ignored if <em>fmttime</em> is shown - </td> - </tr><tr> - <th>fmttime</th> - <td> - Print the time in human-readable [YYYY-MM-DD HH:mm:ss] format. - Note that the time is printed in the local timezone. - To get GMT output, use <code>env TZ=GMT vespa-logfmt</code> - </td> - </tr><tr> - <th>msecs</th> - <td> - Add milliseconds after the seconds in <em>time</em> and - <em>fmttime</em> output. Ignored if <em>usecs</em> is in effect - </td> - </tr><tr> - <th>usecs</th> - <td> - Add microseconds after the seconds in <em>time</em> and <em>fmttime</em> output - </td> - </tr><tr> - <th>host</th> - <td> - Print the hostname field - </td> - </tr><tr> - <th>level</th> - <td> - Print the level field (upper-cased) - </td> - </tr><tr> - <th>pid</th> - <td> - Print the pid field - </td> - </tr><tr> - <th>service</th> - <td> - Print the service field - </td> - </tr><tr> - <th>component</th> - <td> - Print the component field - </td> - </tr><tr> - <th>message</th> - <td> - Print the message text field. You probably always want to add this - </td> - </tr> - </tbody> - </table> - </td> - </tr><tr> - <th>-p <em>pid</em></th> - <td> - Select messages where the pid field matches the <em>pid</em> string - </td> - </tr><tr> - <th>-S <em>service</em></th> - <td> - Select messages where the service field matches the <em>service</em> string - </td> - </tr><tr> - <th>-H <em>host</em></th> - <td> - Select messages where the hostname field matches the <em>host</em> string - </td> - </tr><tr> - <th>-c <em>regex</em></th> - <td> - Select messages where the component field matches the <em>regex</em>, - using <em>perlre</em> regular expression matching - </td> - </tr><tr> - <th>-m <em>regex</em></th> - <td> - Select messages where the message text field matches the <em>regex</em>, - using <em>perlre</em> regular expression matching - </td> - </tr><tr> - <th>-f</th> - <td> - Invoke tail -F to follow the input file - </td> - </tr><tr> - <th>-N</th> - <td> - De-quote quoted newlines in the message text field to an actual newline plus tab - </td> - </tr><tr> - <th>-t</th> - <td> - Format the component field (if shown) as a fixed-width string, truncating if necessary - </td> - </tr><tr> - <th>-ts</th> - <td> - Format the service field (if shown) as a fixed-width string, truncating if necessary - </td> - </tr> - <tr> - <th>-i, --internal</th> - <td> - Only include log entries emitted by the Vespa platform, i.e., exclude log entries from custom components - </td> - </tr> - </tbody> -</table> - - - -<!--h2 id="vespa-log-replay">vespa-log-replay</h2--> -<!--h2 id="vespa-malloc-parse-memorydump.pl">vespa-malloc-parse-memorydump.pl</h2--> - - - -<h2 id="vespa-model-inspect">vespa-model-inspect</h2> -<p> -<em>vespa-model-inspect</em> is a tool for inspecting the topology and services of a Vespa system. -Hosts, services, clusters, ports, URLs, and config ids can be inspected. -It can run on any machine in a Vespa cluster that is running a Vespa configuration server. -</p><p> -Synopsis: <code>vespa-model-inspect [-c host | host:port] [-t tag] [-h] [-u] [-v] command</code> -</p> -<table class="table"> - <thead> - <tr> - <th>Command</th> - <th>Description</th> - </tr> - </thead> - <tbody> - <tr> - <th>hosts</th> - <td> - Show hostnames of all hosts in the Vespa system - </td> - </tr><tr> - <th>services</th> - <td> - Show a list of all service types in the Vespa system - </td> - </tr><tr> - <th>clusters</th> - <td> - Show a list of all named clusters in the Vespa system - </td> - </tr><tr> - <th>configids</th> - <td> - Show a list of all config ids in the Vespa system - </td> - </tr><tr> - <th>filter:ports</th> - <td> - List ports matching filter options - </td> - </tr><tr> - <th>host <em>hostname</em></th> - <td> - Show host details: What services are running, and what ports have they allocated - </td> - </tr><tr> - <th>service <em>servicetype</em></th> - <td> - Show service details: What instances of the service are running, - on what hosts, and what ports have they allocated - </td> - </tr><tr> - <th>cluster <em>clustername</em></th> - <td> - Show all services in the cluster, with details on hostname and allocated ports - </td> - </tr><tr> - <th>configid <em>configid</em></th> - <td> - Show all services using this configid - </td> - </tr><tr> - <th>get-index-of <em>servicetype</em> <em>host</em></th> - <td> - Show all indexes for instances of the service type on the given host - </td> - </tr> - </tbody> -</table> -<table class="table"> - <thead> - <tr> - <th>Option</th> - <th>Description</th> - </tr> - </thead> - <tbody> - <tr> - <th style="white-space:nowrap;">-c <em>host</em> | <em>host:port</em></th> - <td> - Specify host and port (or just host) to use for getting the config that this tool displays. - Default is to use the configserver. - You might want to use localhost:19090 if you are on a host with - a running Vespa system without a config server - </td> - </tr><tr> - <th>-h</th> - <td> - Show usage - </td> - </tr><tr> - <th>-t <em>tag</em></th> - <td> - to filter on a port tag - </td> - </tr><tr> - <th>-u</th> - <td> - Show URLs for services - </td> - </tr><tr> - <th>-v</th> - <td> - Verbose mode - </td> - </tr> - </tbody> -</table> -<p>Examples:</p> -<pre> -$ vespa-model-inspect hosts -mynode.mydomain.com - -$ vespa-model-inspect services -config-sentinel -configproxy -configserver -container -container-clustercontroller -distributor -docprocservice -filedistributorservice -logd -logserver -searchnode -slobrok -storagenode - -$ vespa-model-inspect -u service distributor -distributor @ myhost.mydomain.com : content -myapp/distributor/4 - tcp/myhost1.mydomain.com:19112 (MESSAGING) - tcp/myhost1.mydomain.com:19113 (STATUS RPC) - http://myhost1.mydomain.com:19114/ (STATE STATUS HTTP) -distributor @ myhost2.mydomain.com : content -myapp/distributor/5 - tcp/myhost2.mydomain.com:19112 (MESSAGING) - tcp/myhost2.mydomain.com:19113 (STATUS RPC) - http://myhost2.mydomain.com:19114/ (STATE STATUS HTTP) -distributor @ myhost3.mydomain.com : content -</pre> - - - -<!--h2 id="vespa-ping-configproxy">vespa-ping-configproxy</h2--> - - - -<h2 id="vespa-print-default">vespa-print-default</h2> -<p> - Internal script used by other scripts to find hostname, config server addresses/ports, version, and more. - Not intended for end-user usage. -</p> - - - -<h2 id="vespa-proton-cmd">vespa-proton-cmd</h2> -<p> - Use <em>vespa-proton-cmd</em> to send commands to <a href="../../../content/proton.html">proton</a>. -</p> -<p> - Synopsis: <code>vespa-proton-cmd HOSTSPEC COMMAND [ARGS]</code> -</p> -<p> - The <em>hostspec</em> argument is one of <code>port|spec|--local|--id=name</code>. - Use <a href="#vespa-model-inspect">vespa-model-inspect</a> to locate the search node ADMIN RPC port: -</p> -<pre> -$ vespa-model-inspect service searchnode -searchnode @ /mynode.myhost.com : search -music/search/cluster.music/0 - tcp/mynode.myhost.com:19108 (STATUS ADMIN RTC RPC) - tcp/mynode.myhost.com:19109 (FS4) - tcp/mynode.myhost.com:19110 (TEST HACK SRMP) - tcp/mynode.myhost.com:19111 (ENGINES-PROVIDER RPC) - tcp/mynode.myhost.com:19112 (STATE HEALTH JSON HTTP) -</pre> -<p>Example:</p> -<pre> -$ vespa-proton-cmd 19108 triggerFlush - OK: flush trigger enabled -</pre> -<p> - Unless the <strong>-h</strong> or <strong>--help</strong> option is used, - one of these commands must be present: -</p> -<table class="table"> - <thead> - <tr> - <th>Command</th> - <th>Description</th> - </tr> - </thead> - <tbody> - <tr> - <th>getProtonStatus</th> - <td> - Get the current proton state and its components. - </td> - </tr><tr> - <th>getState</th> - <td> - Get the current proton state. - </td> - </tr><tr> - <th>triggerFlush</th> - <td> - Trigger <a href="../../../content/proton.html#proton-maintenance-jobs">flush</a> as soon as possible for all document types. - </td> - </tr><tr> - <th>prepareRestart</th> - <td> - Estimates the cost of <a href="../../../content/proton.html#transaction-log">transaction log</a> replay, - and flushes data structures if that will speed up a subsequent start. - If this is not called before stopping proton, there is no estimation and no flush. - </td> - </tr> - </tbody> -</table> - - - - - -<!--h2 id="vespa-ranking-expression-analyzer">vespa-ranking-expression-analyzer</h2--> - - - -<h2 id="vespa-remove-index">vespa-remove-index</h2> -<p> -<em>vespa-remove-index</em> is a command-line tool to remove index data on a Vespa search node, -by wiping out selected files and subdirectories found in <em>$VESPA_HOME/var/db/vespa/</em>. -This process is irreversible, and the indexes deleted can not be recovered. -</p><p> -Stop <em>services</em> before running it - example: -</p> -<pre> -$ vespa-stop-services && vespa-remove-index -force && vespa-start-services -</pre> -<p> -Synopsis:<code>vespa-remove-index [-force] [-cluster name]</code> -</p><p> -Example: -</p> -<pre> -$ vespa-remove-index -[info] For cluster music distribution key 0 you have: -[info] 156 kilobytes of data in var/db/vespa/search/cluster.music/n0 -Really to remove this vespa index? Type "yes" if you are sure ==> yes -[info] removing data: rm -rf var/db/vespa/search/cluster.music/n0 -[info] removed. -</pre> -<table class="table"> - <thead> - <tr> - <th>Option</th> - <th>Description</th> - </tr> - </thead> - <tbody> - <tr> - <th>-force</th> - <td> - Do not require verification from the user before really removing index data - </td> - </tr><tr> - <th>-cluster <em>name</em></th> - <td> - Only remove data for the given cluster name - </td> - </tr> - </tbody> -</table> - - - -<h2 id="vespa-route">vespa-route</h2> -<p> -<em>vespa-route</em> is a tool to inspect Vespa routing configurations. -If file is set, it will be parsed as a feed -and the output will look similar to when using <a href="../../api/document-v1.html">/document/v1/</a> -with trace enabled. -</p><p> -Synopsis: <code>vespa-route [options] [file]</code> -</p><p> -Example: -</p> -<pre> -$ vespa-route -There are 5 route(s): - 1. default - 2. music - 3. music-direct - 4. music-index - 5. storage/cluster.music - -There are 2 hop(s): - 1. docproc/cluster.music.indexing/chain.indexing - 2. indexing -</pre> -<table class="table"> - <thead> - <tr> - <th>Option</th> - <th>Description</th> - </tr> - </thead> - <tbody> - <tr> - <th>--documentmanagerconfigid <id></th> - <td> - Sets the config id that supplies document configuration - </td> - </tr><tr> - <th>--dump</th> - <td> - Prints the complete content of the routing table - </td> - </tr><tr> - <th>--help</th> - <td> - Prints this help</td> - </tr><tr> - <th>--hop <name></th> - <td> - Prints detailed information about hop <name> - </td> - </tr><tr> - <th>--hops</th> - <td> - Prints a list of all available hops</td> - </tr><tr> - <th>--identity <id></th> - <td> - Sets the identity of message bus</td> - </tr><tr> - <th>--listenport <num></th> - <td> - Sets the port message bus will listen to</td> - </tr><tr> - <th>--oosserverpattern <id></th> - <td> - Sets the out-of-service server pattern for message bus - </td> - </tr><tr> - <th>--protocol <name></th> - <td> - Sets the name of the protocol whose routing to inspect</td> - </tr><tr> - <th>--route <name></th> - <td> - Prints detailed information about route <name></td> - </tr><tr> - <th>--routes</th> - <td> - Prints a list of all available routes</td> - </tr><tr> - <th>--routingconfigid <id></th> - <td> - Sets the config id that supplies the routing tables</td> - </tr><tr> - <th>--services</th> - <td> - Prints a list of all available services</td> - </tr><tr> - <th>--slobrokconfigid <id></th> - <td> - Sets the config id that supplies the slobrok server list</td> - </tr><tr> - <th>--trace <num></th> - <td> - Sets the trace level to use when visualizing the route - </td> - </tr><tr> - <th>--verify</th> - <td> - All hops and routes are verified when routing - </td> - </tr> - </tbody> -</table> - - - -<!--h2 id="vespa-rpc-info">vespa-rpc-info</h2--> - - - -<!--h2 id="vespa-rpc-invoke">vespa-rpc-invoke</h2--> - - - -<!--h2 id="vespa-rpc-proxy">vespa-rpc-proxy</h2--> - - - -<h2 id="vespa-sentinel-cmd">vespa-sentinel-cmd</h2> -<p> - Use <em>vespa-sentinel-cmd</em> to list, start and stop services - - refer to <a href="../../../operations/self-managed/config-sentinel.html">config sentinel</a> for examples. - It can also check for connectivity between nodes. -</p> -{% include important.html content=" -See <a href='../../../operations/self-managed/admin-procedures.html#vespa-start-stop-restart'>start / stop / restart</a> -for how to stop all services on a node, optimizing for restart time - -use this for tasks like software upgrade." %} -<p> - Synopsis: <code>vespa-sentinel-cmd [-h] list|start <service>|restart <service>|stop <service>|connectivity</code> -</p> -<table class="table"> - <thead> - <tr> - <th>Option</th> - <th>Description</th> - </tr> - </thead> - <tbody> - <tr> - <th>-h</th><td>Help text</td> - </tr> - </tbody> -</table> -<table class="table"> - <thead> - <tr> - <th>Command</th> - <th>Description</th> - </tr> - </thead> - <tbody> - <tr> - <th>list</th> - <td> - Lists the services running on this node and their status: - <table class="table"> - <thead></thead><tbody> - <tr> - <th style="white-space: nowrap">service name</th><td></td> - </tr> - <tr> - <th>state</th> - <td> - <ul> - <li>RUNNING: Service is running</li> - <li>FINISHED: Service has been stopped</li> - <li>FAILED: Service has crashed and failed to restart</li> - <li>TERMINATING: Service is stopping</li> - </ul> - </td> - </tr> - <tr> - <th>mode</th> - <td> - <ul> - <li>MANUAL: Service has to be started and stopped manually</li> - <li>AUTO: Service will restart automatically if it stops</li> - </ul> - </td> - </tr> - <tr> - <th>pid</th> - <td>Pid of the process (main thread)</td> - </tr> - <tr> - <th>exitstatus</th> - <td> - Exit code the last time the service stopped. - </td> - </tr> - <tr> - <th>id</th> - <td><a href="../../../applications/configapi-dev.html#config-id">Config ID</a> of the service</td> - </tr> - </tbody> - </table> - </td> - </tr> - <tr> - <th style="white-space: nowrap">restart [name]</th> - <td> - Restarts the service with the given name. - The name is the first string in the service list given by <code>list</code> - </td> - </tr> - <tr> - <th>stop [name]</th> - <td>Stops the service with the given name</td> - </tr> - <tr> - <th>start [name]</th> - <td> - Starts the service with the given name - </td> - </tr> - <tr> - <th>connectivity</th> - <td> - Use to troubleshoot startup issues/network configuration/ACLs/iptables: -<pre> -$ vespa-sentinel-cmd connectivity -vespa-sentinel-cmd 'connectivity' OK. -node0.vespanet -> ok -node1.vespanet -> ok -node2.vespanet -> ok -</pre> - </td> - </tr> - </tbody> -</table> - - - -<h2 id="vespa-set-node-state">vespa-set-node-state</h2> -<p> -Set the <a href="../../api/cluster-v2.html#state-user">user state</a> of a node. -This will set the generated state to the user state -if the user state is "better" than the generated state that would have been created -if the user state were up. -For instance, a node that is in <code>up</code> state can be forced into <code>down</code> state, -while a node that is currently <code>down</code> can not be forced into <code>retired</code> state, -but can be forced into maintenance state. -</p><p> -Synopsis: <code>vespa-set-node-state [options] up|down|maintenance|retired [description]</code> -</p><p> -Example: -</p> -<pre> -$ vespa-set-node-state -i 0 maintenance "Set to maintenance for software upgrade" -</pre> -<table class="table"> - <thead> - <tr> - <th>Option</th> - <th>Description</th> - </tr> - </thead> - <tbody> - <tr> - <th>-h, --help</th> - <td> - Show help - </td> - </tr><tr> - <th>-v</th> - <td> - More verbose output - </td> - </tr><tr> - <th>-s</th> - <td> - Less verbose output - </td> - </tr><tr> - <th>--show-hidden</th> - <td> - Also show hidden undocumented debug options - </td> - </tr><tr> - <th>-n, --no-wait</th> - <td> - Do not wait for node state changes to be visible in the cluster before returning - </td> - </tr><tr> - <th>-c, --cluster</th> - <td> - The cluster name of the cluster to query. - If unspecified, and vespa is installed on the current node, - information will be attempted auto-extracted - </td> - </tr><tr> - <th>-f, --force</th> - <td> - Force execution - </td> - </tr><tr> - <th>-t, --type</th> - <td> - Node type - can either be 'storage' or 'distributor'. - If not specified, the operation will use state for both types - </td> - </tr><tr> - <th>-i, --index</th> - <td> - Node index. If not specified, - all nodes found running on this host will be used - </td> - </tr><tr> - <th>--config-server</th> - <td> - Host name of the config server to query - </td> - </tr><tr> - <th>--config-server-port</th> - <td> - Port to connect to the config server on - </td> - </tr><tr> - <th>--config-request-timeout</th> - <td> - Timeout of config request - </td> - </tr> - </tbody> -</table> - - -<!--h2 id="vespa-slobrok-cmd">vespa-slobrok-cmd</h2--> - -<h2 id="vespa-significance">vespa-significance</h2> -<p> - Generates a <a href="../../../ranking/significance#significance-model-file">significance model file</a> for global significance. -</p> -<p>Synopsis: <code>vespa-significance <command> [options]</code></p> -<p>Commands:</p> -<ul> - <li><code>generate</code> – build a significance model from Vespa documents (JSONL) or Vespa Significance TSV (VSTSV) files.</li> - <li><code>export</code> – dump term statistics from an index to a VSTSV file.</li> - <li><code>merge</code> – merge multiple VSTSV files, optionally filtering low-frequency terms.</li> -</ul> -<h3 id="vespa-significance-generate">vespa-significance generate</h3> -<p> - The command uses the same tokenizer as the default query processor, see <a href="../../../linguistics/linguistics.html">linguistics in Vespa</a> for details. - Custom tokenizers require a matching extractor. Tokens are lower-cased without stemming to align with query processing. -</p> -<p>Synopsis: <code>vespa-significance generate [options] --in <FILE></code></p> -<p>Example:</p> -<pre> -$ vespa-significance generate --in vespa-dump.jsonl --out en_model.json --field text --language en -</pre> -<p> - This example generates a significance model called <code>en_mode.json</code> from a collection of Vespa Documents (the jsonl file). - This is available in Vespa as of version 8.426.8. -</p> -<p>When running in Docker, mount the data directory to persist input and output files, e.g.:</p> -<pre> -$ podman run -it --entrypoint bash -v $PWD/data:/data -w /data vespaengine/vespa:latest /opt/vespa/bin/vespa-significance generate --in docs.jsonl --out model.zst --field text --language en -</pre> -<p>To dump documents for JSONL input, use <a href="#vespa-visit">vespa-visit</a> with the <code>--field-set</code> option:</p> -<pre> -$ vespa visit --field-set mydocument:text_field > ./data/docs.jsonl -</pre> -<p>Inspect the resulting model file with <code>zstdcat</code> and <code>jq</code>:</p> -<pre> -$ zstdcat ./data/model.zst | jq -</pre> -<p>Example generating model from VSTSV:</p> -<pre> -$ vespa-significance generate --format vstsv --in dumped_term_df.vstsv --out un_model.json -</pre> -<p> - In this example the input is generated with <code>vespa-significance export</code>. - This is available in Vespa as of version 8.597.8. - When language is not specified, the default is set to unknown when using VSTSV format. -</p> -<table class="table"> - <thead> - <tr> - <th>Option</th> - <th>Description</th> - </tr> - </thead> - <tbody> - <tr> - <th>-h, --help</th> - <td>Show help for the subcommand.</td> - </tr> - <tr> - <th>-i, --in <FILE></th> - <td> - Input file. Default format is JSON Lines where each line is a <a href="../../schemas/document-json-format.html">Vespa document in JSON</a>. - Use <code>--format vstsv</code> to read VSTSV files. - </td> - </tr><tr> - <th>--format <FORMAT></th> - <td>Input format. Format can be <code>jsonl</code> or <code>vstsv</code>. Defaults to <code>jsonl</code>.</td> - </tr><tr> - <th>-o, --out <model.json[.zst]></th> - <td> - Output <a href="../../../ranking/significance#significance-model-file">significance model</a>. - </td> - </tr><tr> - <th>-f, --field <field></th> - <td> - JSONL: the name of the text field to analyse. VSTSV: No effect. - </td> - </tr><tr> - <th>-l, --language <tag[,tag...]></th> - <td> - Comma-separated ISO language tags. The first tag controls tokenization; additional tags are stored in the model. Required for JSONL, optional for VSTSV (defaults to <code>un</code>). - See supported tags in <a href="../../../linguistics/linguistics-opennlp.html#default-languages">linguistics in Vespa</a>. - </td> - </tr><tr> - <th>-zst, --zst-compression <ENABLED></th> - <td> - Enable Zstandard compression of the output. Enabled can be <code>true</code> or <code>false</code>. Default <code>false</code>. When <code>true</code>, the output file must end with <code>.zst</code>. - </td> - </tr> - </tbody> -</table> -<h3 id="vespa-significance-export">vespa-significance export</h3> -<p>Synopsis: <code>vespa-significance export [options] --field <FIELD></code></p> -<p>Example:</p> -<pre> -$ vespa-significance export --schema product --field title --out text.vstsv -</pre> -<p> - Locates an index on the content node and exports term document frequencies from that index using <code>vespa-index-inspect</code>. - The output VSTSV file can be passed to <code>generate --format vstsv</code> or merged with other exports. - This is available in Vespa as of version 8.597.8. -</p> -<p>This command must be executed on a content node.</p> -<table class="table"> - <thead> - <tr> - <th>Option</th> - <th>Description</th> - </tr> - </thead> - <tbody> - <tr> - <th>-h, --help</th> - <td>Show help for the subcommand.</td> - </tr> - <tr> - <th>--index-dir <path/to/index></th> - <td> - Explicit path to the index directory. If omitted, the tool locates the directory using cluster, schema, and node information. - </td> - </tr><tr> - <th>--out <FILE.vstsv[.zst]></th> - <td> - Output VSTSV file. Defaults to <code>export_<em>FIELD</em>.vstsv</code>. Compression adds <code>.zst</code> automatically. - </td> - </tr><tr> - <th>--field <FIELD></th> - <td> - Text field to export. Must correspond to a field directory within the selected index. This is the same as a field in the schema. - </td> - </tr><tr> - <th>--cluster <NAME></th> - <td>Specifies the content cluster name for locating the index directory. If multiple clusters exist per node, use --cluster to choose one.</td> - </tr><tr> - <th>--schema <NAME></th> - <td>Specifies the schema (document type) name used when locating the index directory.</td> - </tr><tr> - <th>--node-index <NUMBER></th> - <td>Specifies the content node index for locating the index directory. If there are multiple indexes on a node, use --node-index to choose one.</td> - </tr><tr> - <th>-zst, --zst-compression</th> - <td>Write the VSTSV output compressed with Zstandard.</td> - </tr> - </tbody> -</table> -<h3 id="vespa-significance-merge">vespa-significance merge</h3> -<p>Synopsis: <code>vespa-significance merge [options] <input.vstsv[.zst]> [<input2.vstsv[.zst]> ...]</code></p> -<p>Example:</p> -<pre> -$ vespa-significance merge --out merged_title.vstsv export_title_node1.vstsv export_title_node2.vstsv -</pre> -<p> - Merges multiple VSTSV files and preserves the total document count in the header. - Use this to combine exports before generating a model. - This is available in Vespa as of version 8.597.8. -</p> -<table class="table"> - <thead> - <tr> - <th>Option</th> - <th>Description</th> - </tr> - </thead> - <tbody> - <tr> - <th>-h, --help</th> - <td>Show help for the subcommand.</td> - </tr> - <tr> - <th>--out <FILE.vstsv[.zst]></th> - <td> - Output VSTSV file. Defaults to <code>merged.vstsv</code>. Compression adds <code>.zst</code>. - </td> - </tr><tr> - <th>--min-keep <NUMBER></th> - <td> - Filter out terms with document frequency strictly lower than <code>NUMBER</code>. - </td> - </tr><tr> - <th>-zst, --zst-compression</th> - <td>Write the merged VSTSV output compressed with Zstandard.</td> - </tr> - </tbody> -</table> - -<h2 id="vespa-start-configserver">vespa-start-configserver</h2> -<p> - Start a config server on a node, <a href="../../../operations/self-managed/admin-procedures.html#vespa-start-stop-restart">details</a>. - See <a href="#vespa-start-services">vespa-start-services</a> for node setup steps, before startup. -</p> -<p> - Synopsis: <code>vespa-start-configserver</code> -</p> - - - -<h2 id="vespa-start-services">vespa-start-services</h2> -<p> - Start all services on a node, <a href="../../../operations/self-managed/admin-procedures.html#vespa-start-stop-restart">details</a>. -</p> -<p> - As part of starting Vespa, the startup script calls - <a href="https://github.com/vespa-engine/vespa/blob/master/vespabase/src/rhel-prestart.sh">rhel-prestart.sh</a> - to set up directories and system limits - this requires privileges to do so - - run this command with sudo, - see <a href="../../../operations/self-managed/multinode-systems.html#config-server-cluster-setup">example</a>. - Refer to <a href="https://github.com/vespa-engine/vespa/blob/master/vespabase/src/vespa-start-services.sh"> - vespa-start-services.sh</a> and - <a href="../../../operations/self-managed/files-processes-and-ports.html#environment-variables">environment variables</a>. -</p> -<p> - Synopsis: <code>vespa-start-services</code> -</p> -<p> - When debugging a failed start, use <a href="#vespa-logfmt">vespa-logfmt</a> to inspect the log. - It is also useful to read up on the <a href="../../../operations/self-managed/configuration-server.html#start-sequence"> - start sequence</a> and make sure the config server is running - Vespa will not start with a running config server. -</p> -<p> - Vespa has a <em>Safe Cluster Startup</em> mode to only start vespa services after X% of nodes are running - - see <a href="../../../operations/self-managed/config-sentinel.html#cluster-startup">cluster startup</a>. -</p> - - - -<h2 id="vespa-stat">vespa-stat</h2> -<p> -<em>vespa-stat</em> is a tool to fetch statistics about a specific user, group, bucket, gid or document. -</p> -<p> -vespa-stat works in two stages. -The first stage is to figure out the actual buckets we want to look at. -In the second stage, it can dump the located buckets. -For each command line option, only the relevant documents will be dumped -(the document for <code>--document/--gid</code>, or the user/group's documents for <code>--user/--group</code>). -This stage can be turned on by adding <code>--dump</code>, -but is default on for the case of <code>--document/--gid</code>. -</p><p> -Synopsis: <code>vespa-stat [options]</code> -</p><p> -Example: -</p> -<pre> -$ vespa-stat --document id:my_namespace:my_search::12345678-4fb7-3797-ae9a-d4d7a4e6e085 -Bucket maps to the following actual files: - BucketInfo(BucketId(0x4000000000004800): [distributor:17] [node(idx=17,crc=0xe5ce35c7,docs=57/57,bytes=478040/478040,trusted=true,active=true,ready=true), node(idx=15,crc=0xe5ce35c7,docs=57/57,bytes=478040/478040,trusted=true,active=true,ready=true)]) - -Details for BucketId(0x4000000000004800): - Bucket information from node 15: -Persistence bucket BucketId(0x4000000000004800), partition 0 - Timestamp: 1452598747000000, Doc(id:my_namespace:my_search::12345678-4fb7-3797-ae9a-d4d7a4e6e085), gid(0x0048e840a48002b12abbb0a0), size: 101 - - Bucket information from node 17: -Persistence bucket BucketId(0x4000000000004800), partition 0 - Timestamp: 1452598747000000, Doc(id:my_namespace:my_search::12345678-4fb7-3797-ae9a-d4d7a4e6e085), gid(0x0048e840a48002b12abbb0a0), size: 101 -</pre> -<table class="table"> - <thead> - <tr> - <th>Option</th> - <th>Description</th> - </tr> - </thead> - <tbody> - <tr> - <th>-b, --bucket <bucketid></th> - <td> - Dump list of buckets that are contained in the given bucket, or that contain it - </td> - </tr><tr> - <th> -d, --dump</th> - <td> - Dump list of documents for all buckets matching the selection command. - </td> - </tr><tr> - <th>-g, --group <groupid></th> - <td> - Dump list of buckets that can contain the given group - </td> - </tr><tr> - <th>-h, --help</th> - <td> - Help text - </td> - </tr><tr> - <th>-l, --gid <globalid></th> - <td> - Dump information about one specific document, as given by the GID (implies --dump) - </td> - </tr><tr> - <th>-o, --document <docid></th> - <td> - Dump information about one specific document (implies --dump) - </td> - </tr><tr> - <th> -r, --route <route></th> - <td> - Route to send the messages to, usually the name of the storage cluster - </td> - </tr><tr> - <th> -s, --bucketspace <space></th> - <td> - <a href="../../../content/buckets.html#bucket-space">Bucket space</a> - (<em>default</em> or <em>global</em>). If not specified, <em>default</em> is used - </td> - </tr><tr> - <th> -u, --user <userid></th> - <td> - Dump list of buckets that can contain the given user - </td> - </tr> - </tbody> -</table> - - - -<h2 id="vespa-status-filedistribution">vespa-status-filedistribution</h2> -<p> - Use <em>vespa-status-filedistribution</em> to get status from file distribution. - Should be run on a config server, it connects to config server on localhost to get status. -</p><p> -Synopsis: <code>vespa-status-filedistribution [--application <applicationNameArg>] - [--debug] [--environment <environmentArg>] [(-h | --help)] - [--instance <instanceNameArg>] [--region <regionArg>] - [--tenant <tenantNameArg>] [--timeout <timeoutArg>]</code> -</p> -<table class="table"> - <thead> - <tr> - <th>Option</th> - <th>Description</th> - </tr> - </thead> - <tbody> - <tr> - <th>--application <applicationName></th> - <td> - Application name - </td> - </tr><tr> - <th>--debug</th> - <td> - Print debug log - </td> - </tr><tr> - <th>--environment <environment></th> - <td> - Environment name - </td> - </tr><tr> - <th>-h, --help</th> - <td> - Display help information - </td> - </tr><tr> - <th>--instance <instanceName></th> - <td> - Instance name - </td> - </tr><tr> - <th>--region <regionName></th> - <td> - Region name - </td> - </tr><tr> - <th>--tenant <tenantName></th> - <td> - Tenant name - </td> - </tr><tr> - <th>--timeout <timeout></th> - <td> - timeout (in seconds) - </td> - </tr> - </tbody> -</table> - - - -<h2 id="vespa-stop-configserver">vespa-stop-configserver</h2> -<p> - Stop a config server on a node, <a href="../../../operations/self-managed/admin-procedures.html#vespa-start-stop-restart">details</a>. -</p> -<p> - Synopsis: <code>vespa-stop-configserver</code> -</p> - - - -<h2 id="vespa-stop-services">vespa-stop-services</h2> -<p> - Stop all services on a node, <a href="../../../operations/self-managed/admin-procedures.html#vespa-start-stop-restart">details</a>. - Running <em>vespa-stop-services</em> on a content node will call - <a href="#vespa-proton-cmd">prepareRestart</a> to optimize restart time. -</p> -<p> - Synopsis: <code>vespa-stop-services</code> -</p> - - - -<h2 id="vespa-summary-benchmark">vespa-summary-benchmark</h2> -<p> -Tool for testing and benchmarking RPC docsum interface. Refer to -<a href="https://github.com/vespa-engine/vespa/blob/master/vespaclient-java/src/main/java/com/yahoo/vespasummarybenchmark/VespaSummaryBenchmark.java"> -VespaSummaryBenchmark.java</a> -</p> - - - -<!--h2 id="vespa-transactionlog-inspect">vespa-transactionlog-inspect</h2--> -<!--h2 id="vespa-treenet-converter">vespa-treenet-converter</h2--> -<!--h2 id="vespa-validate-application">vespa-validate-application</h2--> -<!--h2 id="vespa-verify-logdatastore">vespa-verify-logdatastore</h2--> -<!--h2 id="vespa-verify-ranksetup">vespa-verify-ranksetup</h2--> - - - -<h2 id="vespa-visit">vespa-visit</h2> -<p> - Used to run a <a href="../../../writing/visiting.html">visit</a> operation, - with more options than <a href="../../../clients/vespa-cli.html">vespa visit</a>. - It uses the <a href="../../../writing/document-routing.html">Vespa Message Bus</a> and must be run inside a Vespa application - - it does not use the Vespa HTTP APIs. -</p> -{% include note.html content='This tool is easily confused with <code>vespa visit</code> -in the <a href="../../../clients/vespa-cli.html">vespa CLI</a>. -The latter is the common tool for visiting, <code>vespa-visit</code> is built for debugging -and complex use cases.' %} -<p> - By default, vespa-visit gets visited documents and emits them to stdout. - However, the tool may specify a <a href="#vespa-visit-target">vespa-visit-target</a> - and be used as a tool to run reprocessing or migration. - It supports keeping a progress file on disk, - such that you can restart it if it should fail in the middle for some reason. -</p> -<p> - To migrate a set of documents from one cluster to another, use <em>visiting</em> - - as the data is transferred directly, using a compact - serialization format, from the source nodes to the targets, - this is performance optimal (data is not piped through the visit client). - Implement backup this way, or dump to file. -</p><p> - Search node recovery: Feed the documents directly to a search cluster. - Example, selecting documents of type <em>music</em>: -</p> -<pre> -$ vespa-visit --selection music --datahandler indexing -</pre> -<p> - This feeds from the source into the search cluster in the same application. - Note that simultaneous feed can make updates go lost. -</p><p> - Include <a href="../../../operations/self-managed/admin-procedures.html#data-retention-vs-size">remove-entries</a> - in the visit operation using <em>--visitremove</em> - this dumps the tombstones - of documents recently removed. -</p><p> - The <a href="../../../writing/document-routing.html#content">content policy</a> can be configured to - use a set of configuration servers from another cluster to configure - itself. This is specified with the <em>config</em> parameter. As - an example, the following route routes to the content cluster - <em>mycluster</em> with a configuration server on - <em>myconfigserver.mydomain.com:12345</em>: -<pre> -[Content:config=tcp/myconfigserver.mydomain.com:12345;cluster=mycluster] -</pre> -<p> - The following examples illustrate how to copy all data from a source cluster to another cluster using vespa-visit: -</p> -<pre> -# Copies all data in the local cluster, routing it to the remote <em>mycluster</em> -$ vespa-visit --datahandler '[Content:config=tcp/myconfigserver.mydomain.com:12345;cluster=mycluster]' - -# Limit to 'music' documents only -$ vespa-visit --datahandler '[Content:config=tcp/myconfigserver.mydomain.com:19070;cluster=mycluster]' \ - --selection music - -# Limit to all documents for user '1234' -$ vespa-visit --datahandler '[Content:config=tcp/myconfigserver.mydomain.com:12345;cluster=mycluster]' \ - --selection id.user=1234 -</pre> -<p>Visitor processor types:</p> -<table class="table"> - <thead> - <tr><th>Processor Type</th><th>Description</th></tr> - </thead><tbody> -<tr> - <th>Dump visitor</th> - <td> - The most commonly used visitor processor type is the dump visitor. - All it does is to send the read documents on to some external target - specified by the visitor. - Using the command line tool <em>vespa-visit</em>, - the default is to just send the documents back to the client, - and have them printed to stdout. - The dump visitor is used to implement reprocessing. - Typically, using a messagebus route, - which will send the documents through the document processing cluster - and then back to the content cluster. - Migration of documents from one cluster to another - is also implemented using a dump visitor. - </td> -</tr><tr> - <th style="white-space: nowrap">Streaming search visitor</th> - <td> - The <a href="../../../performance/streaming-search.html">streaming search</a> visitor runs in the Vespa container, - making it transparent whether search results were created from streaming or indexed search - - see <a href="../../applications/services/content.html#document">indexing mode</a>. - </td> -</tr> -</tbody> -</table> -<p>Requests sent from the visitor processor are sent to a visitor target - types:</p> -<table class="table"> - <thead> - <tr><th>Target Type</th><th>Description</th></tr> - </thead><tbody> -<tr> - <th style="white-space: nowrap">Message bus routes</th> - <td> - You can specify a <a href="../../../writing/document-routing.html">message bus route</a> name directly, - and this route will be used to send the results. - This is typically used when doing reprocessing or migration. - Message bus routes are set up in the application package. - In addition, some routes may have been auto-generated in simple setups, - for instance, a route called <em>default</em> is generated - if your setup is simple enough for the config model - to likely guess where you want to send your data. - </td> -</tr><tr> - <th>Slobrok address</th> - <td><p> - You can also specify a slobrok address for data to be sent to. - A slobrok address is a slash-separated path - where you can use an asterisk to mean any element within this path. - For instance, if you have a docproc cluster called <em>mydpcluster</em>, - it will have registered its nodes with slobrok names like - <em>docproc/cluster.mydpcluster/docproc/0/feed_processor</em>, - where the 0 here indicates the first node in the cluster. - You can thus specify to send visit data to this docproc cluster - by stating a slobrok address of <em>docproc/cluster.mydpcluster/docproc/*/feed_processor</em>. - Note that this will not send all the data to one or all the nodes. - The data sent from the visitor will be distributed among the matching nodes, - but each message will just be sent to one node. - </p><p> - Slobrok names can be used when using - <a href="#vespa-visit-target">vespa-visit-target</a> - to retrieve the data from some location. - If you start vespa-visit-target on two nodes, - listening to slobrok names <em>mynode/0/visit-destination</em> - and <em>mynode/1/visit-destination</em>, - you can send the results to these nodes by specifying - <em>mynode/*/visit-destination</em> as the data handler. - </p><p> - <a href="#vespa-destination">vespa-destination</a> - is similar to vespa-visit-target in that it can receive messages - from messagebus and print the contents to stdout. - It can be useful in situations where you want to debug a route or a docproc, - by using the vespadestination as the endpoint of your route.</p> - </td> -</tr><tr> - <th>TCP socket</th> - <td> - TCP sockets can also be specified directly. - This requires that the endpoint speaks FNET RPC. - This is typically done, either by using the <em>vespa-visit-target</em> tool, - or by using a visitor destination programmatically by using a utility class in the document API. - A socket address looks like the following: - tcp/<em>hostname</em>:<em>port</em>/<em>servicename</em>. - For instance, an address generated by <em>vespa-visit-target</em> - might look like: <em>tcp/myhost.mydomain.com:12345/visit-destination</em> - </td> -</tr> -</tbody> -</table> -<p>Also see <a href="#vespa-destination">vespa-destination</a>.</p> -<p>Synopsis: <code>vespa-visit [options]</code></p> -<table class="table"> - <thead> - <tr> - <th>Option</th> - <th>Description</th> - </tr> - </thead> - <tbody> - <tr> - <th style="width:300px">--abortonclusterdown</th> - <td>Abort if cluster is down</td> - </tr><tr> - <th>-b, --maxbuckets <num></th> - <td>Maximum buckets per visitor</td> - </tr><tr> - <th>--bucketspace <space></th> - <td> - <a href="../../../content/buckets.html#bucket-space">Bucket space</a> to visit - (<em>default</em> or <em>global</em>). If not specified, <em>default</em> is used - </td> - </tr><tr> - <th>-c, --cluster <cluster></th> - <td>Visit the given cluster</td> - </tr><tr> - <th>-d, --datahandler <target></th> - <td>Send results to the given target - see <a href="#vespa-visit-target">vespa-visit-target</a></td> - </tr><tr> - <th>-f, --from <timestamp></th> - <td>Only visit from the given timestamp (microseconds)</td> - </tr><tr> - <th>-h, --help</th> - <td>Show help text</td> - </tr><tr> - <th>-i, --printids</th> - <td>Display only document identifiers</td> - </tr><tr> - <th>--jsonoutput</th> - <td>Output a JSON array of document objects. This is the default output format.</td> - </tr><tr> - <th>--jsonl</th> - <td>Output documents as JSONL (JSON Lines format). Each individual document is output as a single line, - with a newline separating each document. Lines are not comma-separated, and there is no top-level - array wrapping the document objects. - </td> - </tr><tr> - <th>-l, --fieldset <fieldset></th> - <td> - Retrieve the specified fields only - (see <a href="../../../schemas/documents.html#fieldsets">Document field sets</a>). - Default: <code>[document]</code> - </td> - </tr><tr> - <th>--libraryparam <key> <val></th> - <td>Send parameter to the visitor library</td> - </tr><tr> - <th>-m, --maxpending <num></th> - <td>Maximum pending messages to data handlers per storage visitor</td> - </tr><tr> - <th>--maxpendingsuperbuckets <num></th> - <td> - Maximum pending visitor messages from the vespa-visit client. - If set, dynamic throttling of visitors is disabled <!-- ToDo: link to somewhere documenting throttling --> - </td> - </tr><tr> - <th>--maxtotalhits <num></th> - <td> - Abort visiting when received this many total documents. - This is only an approximate number, - all pending work will be completed, and those documents will also be returned - </td> - </tr><tr> - <th>-o, --timeout <milliseconds></th> - <td>Time out visitor after given time</td> - </tr><tr> - <th>-p, --progress <file></th> - <td> - Use the given file to track progress. - <code>-p progress-file</code> saves progress, allowing the visitor to resume at next startup. - Always remove the progress file to run the visiting operation from the start. - </td> - </tr><tr> - <th>--processtime <num></th> - <td> - Sleep for this number of milliseconds before processing the message. - (Debug option for pretending to be a slow client) - </td> - </tr><tr> - <th>-r, --visitremoves</th> - <td> - Return tombstone entries of documents that have been removed. - Tombstones will be output as <code>remove</code> objects, which only contain a document ID. - When using <code>--visitremoves</code>, regular (non-tombstone) documents will also be returned. - </td> - </tr><tr> - <th>-s, --selection <selection></th> - <td> - <p> - <a href="../../writing/document-selector-language.html">Selection</a> string for which documents to visit. - E.g., <code>-s 'id.hash().abs() % 100 == 0'</code> dumps 1% of the corpus - - see <a href="../../../writing/visiting.html#selection">selection</a>. - Note that this expression is evaluated for <em>every</em> document in the cluster, so running 100 - visits comparing against all values in [0, 99) end up reading all documents 100 times. Prefer - using <code>--slices</code> and <code>--sliceid</code> instead if available. - </p> - </td> - </tr><tr> - <th>--shorttensors</th> - <td>Output using <a href="../../schemas/document-json-format.html#tensor">tensor short form</a></td> - </tr><tr> - <th>--skipbucketsonfatalerrors</th> - <td>Skip visiting super buckets with fatal error codes</td> - </tr><tr> - <th>--sliceid <arg></th> - <td> - The slice number of the visit represented by this visitor. - This number must be non-negative and less than the number of slices specified for the visit. - </td> - </tr><tr> - <th>--slices <arg></th> - <td> - <p> - Split the document corpus into this number of independent slices. - This lets multiple, concurrent series of visitors advance the same logical visit independently, - by specifying a different <code>sliceid</code> for each. - </p> - <p> - E.g. <code>--slices 100 --sliceid 0</code> dumps 1% of the corpus by efficiently iterating - over only 1/100th of the data space. For a given number of <code>--slices</code>, it's - possible to visit the entire corpus (possibly in parallel) with non-overlapping output - by visiting with all <code>--sliceid</code> values from (and including) 0 up to (and excluding) - <code>--slices</code>. - </p> - </td> - </tr><tr> - <th>-t, --to <timestamp></th> - <td>Only visit up to the given timestamp (microseconds)</td> - </tr><tr> - <th>--tracelevel <level></th> - <td>Tracelevel ([0-9]), for debugging</td> - </tr><tr> - <th>-u, --buckettimeout <milliseconds></th> - <td>Fail visitor if visiting a single bucket takes longer than this (default same as timeout)</td> - </tr><tr> - <th>-v, --verbose</th> - <td>Show progress and info on STDERR</td> - </tr><tr> - <th>--visitinconsistentbuckets</th> - <td> - <p id="visitinconsistentbuckets"> - Don't wait for inconsistent buckets to become consistent. - See <a href="../../../content/consistency#read-consistency">read-consistency</a> for details. - </p> - </td> - </tr><tr> - <th>--visitlibrary <string></th> - <td>Use the given visitor library</td> - </tr> - </tbody> -</table> - - - -<h2 id="vespa-visit-target">vespa-visit-target</h2> -<p> - <a href="#vespa-visit-target">vespa-visit-target</a> - is a tool to set up an endpoint for <a href="../../../writing/visiting.html">visiting</a> data. - It binds to a socket or a slobrok address, - which is specified as a target in the visit client. - Also see <a href="#vespa-destination">vespa-destination</a>. -</p> -<p> -Synopsis: <code>vespa-visit-target [options]</code> -</p> -<table class="table"> - <thead> - <tr> - <th>Option</th> - <th>Description</th> - </tr> - </thead> - <tbody> - <tr> - <th style="width:300px">-c, --visithandler <classname></th> - <td>Use the given class as a visit handler (defaults to StdOutVisitorHandler)</td> - </tr><tr> - <th style="width:300px">-h, --help</th> - <td>Show help page</td> - </tr><tr> - <th style="width:300px">-i, --printids</th> - <td>Display document IDs only</td> - </tr><tr> - <th style="width:300px">-o, --visitoptions <args></th> - <td>Option arguments to pass through to the visitor handler instance</td> - </tr><tr> - <th style="width:300px">-p, --processtime <msecs></th> - <td>Sleep msecs milliseconds before processing the message. - (Debug option for pretending to be a slow client)</td> - </tr><tr> - <th style="width:300px">-s, --bindtoslobrok <address></th> - <td>Bind to slobrok address. - One, and only one, of the binding options must be set</td> - </tr><tr> - <th style="width:300px">-t, --bindtosocket <port></th> - <td>Bind to TCP port. - One, and only one, of the binding options must be set</td> - </tr><tr> - <th style="width:300px">-v, --verbose</th> - <td>Indent output, show progress and info on STDERR</td> - </tr> - </tbody> -</table> diff --git a/en/reference/operations/self-managed/tools.mdx b/en/reference/operations/self-managed/tools.mdx new file mode 100644 index 0000000000..8dd73caf29 --- /dev/null +++ b/en/reference/operations/self-managed/tools.mdx @@ -0,0 +1,1999 @@ +--- +title: "Vespa Command-line Tools" +sidebarTitle: "Tools" +description: "This is the reference for the Vespa command-line tools." +--- + +<Note> +**Note:** + +The tools listed on this page are primarily used for operating and debugging a *self-hosted* Vespa instance. For most use-cases, we recommend the [Vespa CLI](/en/clients/vespa-cli), which should work against most Vespa applications regardless of how they are deployed. +</Note> + +You can run these tools in [Vespa Docker image](https://hub.docker.com/r/vespaengine/vespa/tags): + +```bash +docker run --entrypoint bash vespaengine/vespa ./opt/vespa/bin/[tool] [args] +``` + +## vespa-configproxy-cmd + +*vespa-configproxy-cmd* is a status and control tool for the [config proxy](/en/operations/self-managed/config-proxy). The config proxy runs on all nodes as a proxy for one or more [config servers](/en/operations/self-managed/configuration-server). It connects to the config proxy on localhost by default. + +Run it without arguments to dump a list of active configIds - format specification: + +`<config definition name>,<config id>,<config generation>,<MD5 checksum>,<xxHash checksum>` + +Synopsis: `vespa-configproxy-cmd [args]` + +Example: + +```bash +$ vespa-configproxy-cmd -m sources +``` + +Find a more comprehensive example in [inspecting config](/en/operations/self-managed/config-proxy#inspecting-config). + +<table className="vespa-table"> + <thead> + <tr> + <th>Option</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>**-m**</td> + <td>method, available methods are: <br/><br/> **cache**<br/> Output the config proxy cache content (overview) <br/><br/> **dumpcache**<br/><br/> **statistics**<br/><br/> **heap** <br/><br/> **getConfig** <br/> Get config (see <a href="#vespa-get-config" aria-label="vespa-get-config">vespa-get-config</a>) <br/><br/> **getmode** <br/> Outputs the current mode of the config proxy <br/><br/> **setmode** <br/> Use *default* or *memorycache* - <a href="/en/operations/self-managed/config-proxy" aria-label="example">example</a> <br/><br/> **invalidatecache** <br/> Clears the current cache in the config proxy <br/><br/> **cachefull** <br/> Output the config proxy cache content (including config payload) <br/><br/> **sources** <br/> Output the config proxy's upstream config sources <br/><br/> **updatesources** <br/> Updates the config proxy's upstream config sources to the supplied ones</td> + </tr> + <tr> + <td>**-p**</td> + <td>port number, optional</td> + </tr> + <tr> + <td>**-s**</td> + <td>hostname, optional</td> + </tr> + <tr> + <td>**-h**</td> + <td>help text and usage</td> + </tr> + </tbody> +</table> + +## vespa-configserver-remove-state + +*vespa-configserver-remove-state* removes the config server state on the host. + +Synopsis: `vespa-configserver-remove-state [-force]` + +<table className="vespa-table"> + <thead> + <tr> + <th>Option</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>**-force**</td> + <td>Do not ask for confirmation before removal</td> + </tr> + </tbody> +</table> + +## vespa-config-status + +*vespa-config-status* can run on any machine in a Vespa cluster and outputs a list of all running services which is running with an outdated application package. It can be invoked without any arguments or with optional arguments. + +Synopsis: `vespa-config-status [-v] [-c host] [-c host:port] [-f host0,...,hostN]` + +Example: + +```bash +$ vespa-config-status +``` + +<table className="vespa-table"> + <thead> + <tr> + <th>Option</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>**-v**</td> + <td>Verbose - show all services, even if they are up-to-date</td> + </tr> + <tr> + <td>**-c arg**</td> + <td>Get the Vespa cluster configuration from the config server specified by host and port. Use *host* or *host:port* for config server</td> + </tr> + <tr> + <td>**-f**</td> + <td>Filter to only query config status for the given comma-separated set of hosts</td> + </tr> + </tbody> +</table> + +## vespa-deploy + +*vespa-deploy* is a standalone tool to deploy an application package. Prefer the [Vespa CLI](/en/clients/vespa-cli#deployment) instead. Under the hood, deployment uses the [deploy REST API](/en/reference/api/deploy-v2), which you can also use directly. Refer to the [deploy reference](/en/reference/applications/application-packages#deploy) for details. + +Synopsis: `vespa-deploy [-h] [-v] [-n] [-f] [-t timeout] [-c hostname] [-p port] prepare|activate|upload|fetch|help [args]` + +Example: + +```bash +$ vespa-deploy prepare [application-path|zip-file] && vespa-deploy activate +``` + +```bash +$ vespa-deploy prepare app.zip && vespa-deploy activate +``` + +<table className="vespa-table"> + <thead> + <tr> + <th>Command</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>**prepare**</td> + <td>*vespa-deploy prepare* combines the <a href="/en/reference/api/deploy-v2#create-session" aria-label="upload">upload</a> and <a href="/en/reference/api/deploy-v2#prepare-session" aria-label="prepare">prepare</a> steps.</td> + </tr> + <tr> + <td>**activate**</td> + <td>*vespa-deploy activate* invokes the <a href="/en/reference/api/deploy-v2#activate-session" aria-label="activate">activate</a> step.</td> + </tr> + <tr> + <td>**upload**</td> + <td>*vespa-deploy upload* uploads an application package</td> + </tr> + <tr> + <td>**fetch**</td> + <td>*vespa-deploy fetch* fetches an application package. Useful to get the active configuration for an instance.</td> + </tr> + <tr> + <td>**help**</td> + <td>Same as **-h**</td> + </tr> + </tbody> +</table> + +<table className="vespa-table"> + <thead> + <tr> + <th>Option</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>**-h**</td> + <td>Show help text</td> + </tr> + <tr> + <td>**-v**</td> + <td>Verbose</td> + </tr> + <tr> + <td>**-n**</td> + <td>Dry-run deployment</td> + </tr> + <tr> + <td>**-f**</td> + <td>Force - ignore validation errors</td> + </tr> + <tr> + <td>**-t**</td> + <td>Timeout</td> + </tr> + <tr> + <td>**-c**</td> + <td>Config server hostname</td> + </tr> + <tr> + <td>**-p**</td> + <td>Config server port</td> + </tr> + </tbody> +</table> + +## vespa-destination + +*vespa-destination* is a simple receiver for messagebus messages. It outputs messages received on stdout. Also see [vespa-visit-target](#vespa-visit-target). + +Synopsis: `vespa-destination [options]` + +Example: + +```bash +$ vespa-destination --name msg_sink +``` + +<table className="vespa-table"> + <thead> + <tr> + <th>Option</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>**--instant**</td> + <td>Reply in message thread</td> + </tr> + <tr> + <td>**--name arg**</td> + <td>Slobrok name to register</td> + </tr> + <tr> + <td>**--maxqueuetime arg**</td> + <td>Adjust the in-queue size to have a maximum queue wait period of this many ms (default -1 = unlimited)</td> + </tr> + <tr> + <td>**--silent #nummsg**</td> + <td>Do not dump anything, but progress every #nummsg</td> + </tr> + <tr> + <td>**--sleeptime arg**</td> + <td>The number of milliseconds to sleep per message, to simulate processing time</td> + </tr> + <tr> + <td>**--threads arg**</td> + <td>The number of threads to process the incoming data</td> + </tr> + <tr> + <td>**--verbose**</td> + <td>Dump the contents of certain messages to stdout</td> + </tr> + </tbody> +</table> + +## vespa-fbench-filter-file + +```bash +usage: vespa-fbench-filter-file [-a] [-h] [-m maxLineSize] + +Read concatenated logs from stdin and write +extracted query urls to stdout. + + -a : all parameters to the original query urls are preserved. + If the -a switch is not given, only 'query' and 'type' + parameters are kept in the extracted query urls. + -h : print this usage information. + -m <num> : max line size for input/output lines. + Can not be less than the default [10240] +``` + +## vespa-fbench-geturl + +```bash +usage: vespa-fbench-geturl <host> <port> <url> +``` + +## vespa-fbench-split-file + +```bash +usage: vespa-fbench-split-file [-p pattern] [-m maxLineSize] <numparts> [<file>] + +Reads from <file> (stdin if <file> is not given) and +randomly distributes each line between <numpart> output +files. The names of the output files are generated by +combining the <pattern> with sequential numbers using +the sprintf function. + + -p pattern : output name pattern ['query%03d.txt'] + -m <num> : max line size for input/output lines. + Can not be less than the default [10240] + <numparts> : number of output files to generate. +``` + +## vespa-feeder + +*vespa-feeder* is a feeding client that parses [JSON](/en/reference/schemas/document-json-format) input as Vespa document operations and sends to a Vespa application. It parses the content of the input sequentially and feeds each operation in order. However, since many operations will be pending at any time, and because the processing time of an operation varies, there is no guarantee as to which order operations will reach the content nodes. As this can be important when it comes to operations that apply to the same document ID, there is logic in place to not send an operation for a document ID to which there is already a pending operation. + +*vespa-feeder* prints a report at the end of the feed. To print this report once a minute, use *\--verbose*: + +```bash +Messages sent to vespa (route default) : +---------------------------------------- +PutDocument: ok: 999997 msgs/sec: 411.38 failed: 0 ignored: 0 latency(min, max, avg): 2, 4360, 99 +``` + +*ignored* reports the number of documents that could not be routed to any [content clusters](/en/reference/applications/services/content) because they did not match any of the [configured document types](/en/reference/applications/services/content#documents) or selections - examples are: + +- A document type is removed from the application and the feed file contains documents of this type +- One or more selection expressions restrict the documents the cluster accepts, and the feed file contains documents that are excluded. An example is feeding [expired documents](/en/schemas/documents#document-expiry) - a selection for documents that are less than 30 days old and the feed file contains documents that are 30+ days old + +Synopsis: `vespa-feeder [--abortondataerror true|false] [--abortonsenderror true|false] [--file arg] [--maxpending arg] [--maxpendingsize arg] [--maxfeedrate arg] [mode standard|benchmark] [--noretry] [--retrydelay arg] [--route arg] [--timeout arg] [--trace arg] [--validate] [--dumpDocuments filename] [--numthreads arg] [create-if-non-existent] [-v,--verbose] filename` + +Example: + +```bash +$ vespa-feeder file.json +``` + +<table className="vespa-table"> + <thead> + <tr> + <th>Option</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>**--abortondataerror arg**</td> + <td>Abort if the input has errors (true|false) - default true. Set to *false* in case the input has errors (e.g., invalid characters). *vespa-feeder* notifies on parsing errors at the end of the feed, but it will not abort</td> + </tr> + <tr> + <td>**--abortonsenderror arg**</td> + <td>Abort if an error occurred while sending operations to Vespa (true|false) - default true</td> + </tr> + <tr> + <td>**--file arg**</td> + <td>Input files to read. These can also be passed as arguments without the option prefix. If none is given, this tool parses identifiers from stdin</td> + </tr> + <tr> + <td>**--maxpending arg**</td> + <td>Maximum number of pending operations. This disables dynamic throttling, use with care</td> + </tr> + <tr> + <td>**--maxpendingsize arg**</td> + <td>Maximum size (in bytes) of pending operations</td> + </tr> + <tr> + <td>**--maxfeedrate arg**</td> + <td>Limits the feed rate to the given number (operations/second)</td> + </tr> + <tr> + <td>**--mode**</td> + <td>The mode to run vespa-feeder in (standard|benchmark) - default standard</td> + </tr> + <tr> + <td>**--noretry**</td> + <td>Disables retries of recoverable failures</td> + </tr> + <tr> + <td>**--retrydelay arg**</td> + <td>The time (in seconds) to wait between retries of a failed operation. Default 1</td> + </tr> + <tr> + <td>**--route arg**</td> + <td>The <a href="/en/writing/document-routing" aria-label="route">route</a> to send the data to. Default the *default* route</td> + </tr> + <tr> + <td>**--timeout arg**</td> + <td>Time (in seconds) allowed for sending operations. Default 180</td> + </tr> + <tr> + <td>**--trace arg**</td> + <td>Trace level of network traffic. Default 0</td> + </tr> + <tr> + <td>**--validate**</td> + <td>Run validation tool on input files - do not feed</td> + </tr> + <tr> + <td>**--dumpDocuments <code>{`<filename>`}</code>**</td> + <td>File where documents in the put are serialized</td> + </tr> + <tr> + <td>**--numthreads arg**</td> + <td>How many threads to use for sending. Default 1</td> + </tr> + <tr> + <td>**--create-if-non-existent**</td> + <td>Enable setting of create-if-non-existent to true on all document updates in the given feed</td> + </tr> + <tr> + <td>**-v, --verbose**</td> + <td>Enable verbose output of progress</td> + </tr> + </tbody> +</table> + +## vespa-get + +*vespa-get* retrieves documents from a Vespa content cluster, and prints to *stdout*. *vespa-get* retrieves documents identified by the document IDs passed as command line arguments. If no document IDs are passed through the command line interface, ids will be read from *stdin* - separated by line breaks. + +Synopsis: `vespa-get <options> [documentid...]` + +<table className="vespa-table"> + <thead> + <tr> + <th>Option</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>**-a,--trace *tracelevel***</td> + <td>Trace level to use (default 0)</td> + </tr> + <tr> + <td>**-c,--configid *configid***</td> + <td>Use the specified config id for messagebus configuration</td> + </tr> + <tr> + <td>**-f,--fieldset *fieldset***</td> + <td>Retrieve the specified fields only (see <a href="/en/schemas/documents#fieldsets" aria-label="Document field sets">Document field sets</a>). Default: <code>{`[document]`}</code></td> + </tr> + <tr> + <td>**-h,--help**</td> + <td>Show this syntax page</td> + </tr> + <tr> + <td>**-i,--printids**</td> + <td>Show only identifiers of retrieved documents</td> + </tr> + <tr> + <td>**-j,--jsonoutput**</td> + <td>JSON output (default)</td> + </tr> + <tr> + <td>**-l,--loadtype *loadtype***</td> + <td>Load type (default "")</td> + </tr> + <tr> + <td>**-n,--noretry**</td> + <td>Do not retry operation on transient errors, as is default</td> + </tr> + <tr> + <td>**-r,--route *route***</td> + <td>Send request to the given messagebus route</td> + </tr> + <tr> + <td>**-s,--showdocsize**</td> + <td>Show binary size of document</td> + </tr> + <tr> + <td>**--shorttensor**s</td> + <td>Output using <a href="/en/reference/schemas/document-json-format#tensor" aria-label="tensor short form">tensor short form</a></td> + </tr> + <tr> + <td>**-t,--timeout *timeout***</td> + <td>Set timeout for the request in seconds (default 0)</td> + </tr> + <tr> + <td>**-u,--cluster *cluster***</td> + <td>Send request to the given content cluster</td> + </tr> + </tbody> +</table> + +## vespa-get-cluster-state + +Get cluster state - refer to [content nodes](/en/content/content-nodes). + +Synopsis: `vespa-get-cluster-state [options]` + +<table className="vespa-table"> + <thead> + <tr> + <th>Option</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>**-h, --help**</td> + <td>Show help</td> + </tr> + <tr> + <td>**-v**</td> + <td>More verbose output</td> + </tr> + <tr> + <td>**-s**</td> + <td>Less verbose output</td> + </tr> + <tr> + <td>**--show-hidden**</td> + <td>Also show hidden undocumented debug options</td> + </tr> + <tr> + <td>**-c, --cluster**</td> + <td>The cluster name of the cluster to query. If unspecified, and vespa is installed on the current node, information will be attempted auto-extracted</td> + </tr> + <tr> + <td>**-f, --force**</td> + <td>Force execution</td> + </tr> + <tr> + <td>**--config-server**</td> + <td>Host name of the config server to query</td> + </tr> + <tr> + <td>**--config-server-port**</td> + <td>Port to connect to the config server on</td> + </tr> + <tr> + <td>**--config-request-timeout**</td> + <td>Timeout of config request</td> + </tr> + </tbody> +</table> + +## vespa-get-config + +*vespa-get-config* is a command-line tool to get configuration from a [config server](/en/operations/self-managed/configuration-server) or [config proxy](/en/operations/self-managed/config-proxy). By default, it connects to the config proxy on localhost, fetches config from its cache and prints the config payload on stdout. Configuration is addressed using name and [configId](/en/applications/configapi-dev#config-id). If configId is omitted, the global and default data for that name is returned. The default port number is 19090, the config proxy's port - use 19070 to access a config server. Also check [ports](/en/operations/self-managed/files-processes-and-ports). + +Synopsis: `vespa-get-config -n defName -i configId <option> [args]` + +Example: + +```bash +$ vespa-get-config -n container.statistics -i search/cluster.search +``` + +Find a more comprehensive example in [inspecting config](/en/operations/self-managed/config-proxy#inspecting-config). + +<table className="vespa-table"> + <thead> + <tr> + <th>Option</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>**-n**</td> + <td>config definition name, including namespace (on the form <code>{`<namespace>.<name>`}</code>)</td> + </tr> + <tr> + <td>**-i**</td> + <td>config id, optional</td> + </tr> + <tr> + <td>**-a**</td> + <td>config def schema file, optional (if you want to use another schema than the one known for the config server)</td> + </tr> + <tr> + <td>**-m**</td> + <td>defMd5, optional</td> + </tr> + <tr> + <td>**-c**</td> + <td>configMd5, optional</td> + </tr> + <tr> + <td>**-t**</td> + <td>server timeout, in seconds, default value 3, optional</td> + </tr> + <tr> + <td>**-w**</td> + <td>timeout, default value 10, optional</td> + </tr> + <tr> + <td>**-s**</td> + <td>server hostname, default localhost, optional</td> + </tr> + <tr> + <td>**-p**</td> + <td>port, default 19090, optional</td> + </tr> + <tr> + <td>**-d**</td> + <td>debug mode, optional</td> + </tr> + <tr> + <td>**-h**</td> + <td>help text and usage</td> + </tr> + </tbody> +</table> + +## vespa-get-node-state + +Get the state of one or more storage services from the fleet controller - refer to [content nodes](/en/content/content-nodes): + +<table className="vespa-table"> + <thead> + <tr> + <th>State</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>**Unit state**</td> + <td>The state of the node seen from the cluster controller.</td> + </tr> + <tr> + <td>**User state**</td> + <td>The state the administrator wants the node to be in, default "up". Can be set by using <a href="#vespa-set-node-state" aria-label="vespa-set-node-state">vespa-set-node-state</a> or by the cluster controller</td> + </tr> + <tr> + <td>**Generated state**</td> + <td>The state of a given node in the current cluster state. This is the state all the other nodes know about. This state is a product of the other two states and cluster controller logic to keep the cluster stable.</td> + </tr> + </tbody> +</table> + +Synopsis: `vespa-get-node-state [options]` + +<table className="vespa-table"> + <thead> + <tr> + <th>Option</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>**-h, --help**</td> + <td>Show help</td> + </tr> + <tr> + <td>**-v**</td> + <td>More verbose output</td> + </tr> + <tr> + <td>**-s**</td> + <td>Less verbose output</td> + </tr> + <tr> + <td>**--show-hidden**</td> + <td>Also show hidden undocumented debug options</td> + </tr> + <tr> + <td>**-c, --cluster**</td> + <td>The cluster name of the cluster to query. If unspecified, and vespa is installed on the current node, information will be attempted auto-extracted</td> + </tr> + <tr> + <td>**-f, --force**</td> + <td>Force execution</td> + </tr> + <tr> + <td>**-t, --type**</td> + <td>Node type - can either be 'storage' or 'distributor'. If not specified, the operation will use state for both types</td> + </tr> + <tr> + <td>**-i, --index**</td> + <td>Node index. If not specified, all nodes found running on this host will be used</td> + </tr> + <tr> + <td>**--config-server**</td> + <td>Host name of the config server to query</td> + </tr> + <tr> + <td>**--config-server-port**</td> + <td>Port to connect to the config server on</td> + </tr> + <tr> + <td>**--config-request-timeout**</td> + <td>Timeout of config request</td> + </tr> + </tbody> +</table> + +## vespa-index-inspect + +Use *vespa-index-inspect* to inspect indexed data on a content node. To troubleshoot [query rewriting](/en/linguistics/query-rewriting) and [linguistic transformations](/en/linguistics/linguistics) use this [guide](/en/querying/text-matching#index-and-attribute) instead. + +It shows posting list information (per token or all/range), or dumps the indexed tokens: + +```js +vespa-index-inspect showpostings [--indexdir indexDir] --field field word + +vespa-index-inspect showpostings [--indexdir indexDir] [--field field] --transpose \ + [--docidlimit docIdLimit] [--mindocid mindocid] + +vespa-index-inspect dumpwords [--indexdir indexDir] --field field \ + [--minnumdocs minnumdocs] [--verbose] [--wordnum] +``` + +Synopsis:`vespa-index-inspect showpostings|dumpwords [--indexdir path] [--field fieldname] [--transpose] [--minnumdocs count] [--docidlimit docIdLimit] [--mindocid mindocid] [--verbose] [--wordnum] [word]` + +Example (make sure to flush the index before using): + +`$` [`vespa-proton-cmd`](#vespa-proton-cmd) `--local triggerFlush && \ vespa-index-inspect dumpwords \ --indexdir /opt/vespa/var/db/vespa/search/cluster.music/n0/documents/music/0.ready/index/index.flush.1 \ --field artist bad 2 so 1` + + +<table className="vespa-table"> + <thead> + <tr> + <th>Option</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>**--indexdir *path***</td> + <td>Index location</td> + </tr> + <tr> + <td>**--field *fieldname***</td> + <td>Field to analyze</td> + </tr> + <tr> + <td>**--transpose**</td> + <td>Dump all tokens</td> + </tr> + <tr> + <td>**--minnumdocs *count***</td> + <td>Minimum number of documents to analyze</td> + </tr> + <tr> + <td>**--docidlimit *docid***</td> + <td>Dump up to this doc id</td> + </tr> + <tr> + <td>**--mindocid *docid***</td> + <td>Start from this docid</td> + </tr> + <tr> + <td>**--wordnum**</td> + <td>Also dump token numbers</td> + </tr> + <tr> + <td>**--verbose**</td> + <td>Verbose output</td> + </tr> + </tbody> +</table> + +## vespa-attribute-inspect + +Use *vespa-attribute-inspect* to inspect the content of an attribute field on a content node. To troubleshoot [query rewriting](/en/linguistics/query-rewriting) and [linguistic transformations](/en/linguistics/linguistics) use this [guide](/en/querying/text-matching#index-and-attribute) instead. + +Synopsis: `vespa-attribute-inspect [-p attribute] [-a] [-s attribute] <attribute>` + +Example (make sure to flush the attribute before using): + + +`$` [`vespa-proton-cmd`](#vespa-proton-cmd) `--local triggerFlush && \ vespa-attribute-inspect -p /opt/vespa/var/db/vespa/search/cluster.music/n0/documents/music/0.ready/attribute/year/snapshot-10/year && \ cat /opt/vespa/var/db/vespa/search/cluster.music/n0/documents/music/0.ready/attribute/year/snapshot-10/year.out` + +<table className="vespa-table"> + <thead> + <tr> + <th>Option</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>**-p**</td> + <td>print content to <code>{`<attribute>.out`}</code></td> + </tr> + <tr> + <td>**-s**</td> + <td>save attribute to <code>{`<attribute>.save.dat`}</code></td> + </tr> + </tbody> +</table> + +## vespa-jvm-dumper + +Dump JVM heap, thread stacks, and other debugging information from a Java-based Vespa service. + +Invoke binary without arguments to print help and list the services running on this node. + +```bash +$ vespa-jvm-dumper +``` + +Produce JVM debugging information by invoking the binary with the config ID of the target service and the output directory. + +```bash +$ vespa-jvm-dumper default/container.1 /opt/vespa/tmp/jvm-dump +``` + +## vespa-logctl + +Print or modify log levels for a VESPA service, stored in *$VESPA\_HOME/var/db/vespa/logcontrol/service.logcontrol*. Refer to [controlling log levels](/en/reference/operations/log-files#controlling-log-levels) for details. *component-specification* specifies which subcomponents of the service should be controlled. If empty, all components are controlled: + +- `x.` : Matches only component x +- `x` : Matches component x and all its subcomponents + +Synopsis (show log levels): `vespa-logctl [OPTION] <service>[:component-specification]` + +Synopsis (set log levels): `vespa-logctl [OPTION] <service>[:component-specification] <level-mods>` + +*level-mods* are defined as : `<level>=<on|off>[,<level>=<on|off>]...` + +*level* is one of: `all`, `fatal`, `error`, `warning`, `info`, `event`, `config`, `debug`, `spam` + +Example: For service `container`, set `com.yahoo.search.searchchain` and all subcomponents of `com.yahoo.search.searchchain` to enable all except spam and debug: + +```bash +$ vespa-logctl container:com.yahoo.search.searchchain all=on,spam=off,debug=off +``` + +<table className="vespa-table"> + <thead> + <tr> + <th>Option</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>**-c**</td> + <td>Create the control file if it does not exist (implies -n)</td> + </tr> + <tr> + <td>**-a**</td> + <td>Update all .logcontrol files</td> + </tr> + <tr> + <td>**-r**</td> + <td>Reset to default levels</td> + </tr> + <tr> + <td>**-n**</td> + <td>Create the component entry if it does not exist</td> + </tr> + <tr> + <td>**-f *file***</td> + <td>Use <code>{`<file>`}</code> as the log control file</td> + </tr> + <tr> + <td>**-d *dir***</td> + <td>Look in <code>{`<dir>`}</code> for log control files</td> + </tr> + </tbody> +</table> + +## vespa-logfmt + +`vespa-logfmt` reads Vespa log files, selects messages, and writes a formatted version of these messages to standard output. If no file argument is given, vespa-logfmt will read the last Vespa log file `$VESPA_HOME/logs/vespa/vespa.log` (this also works with the `-f` option). Otherwise, reads only the files given as arguments. To read standard input, supply a single dash ’-’ as a file argument. Refer to the [logs reference](/en/reference/operations/log-files). + +Synopsis: ` vespa-logfmt [-l levellist ] [-s fieldlist ] [-p pid ] [-S service ] [-H host ] [-c regex ] [-m regex ] [-f ] [-N ] [-t ] [-ts ] [file …]` + +Examples: + +Display only messages with log level "event", printing a human-readable time (without any fractional seconds), the service generating the event and the event message: + +```bash +$ vespa-logfmt -l event -s fmttime,service,message +... +[2017-09-05 06:16:16] config-sentinel stopped/1 name="sbin/vespa-config-sentinel -c hosts/vespa-container (pid 1558)" pid=1558 exitcode=1 +[2017-09-05 06:16:16] config-sentinel starting/1 name="sbin/vespa-config-sentinel -c hosts/vespa-container (pid 1564)" +[2017-09-05 06:16:16] config-sentinel started/1 name="config-sentinel" +[2017-09-05 06:17:00] configserver count/1 name=configserver.failedRequests value=0 +[2017-09-05 06:17:00] configserver count/1 name=procTime value=0 +[2017-09-05 06:17:00] configserver count/1 name=configserver.requests value=0 +``` + +Display messages with log levels that are *not* any of *info, debug,* or *event,* printing the time in seconds and microseconds, the log level, the component name, and the message text: + +```bash +$ vespa-logfmt -l all-info,-debug -s level -s time,usecs,component,message -t -l -event +... +1504592294.738000 WARNING : configproxy.com No config found for name=sentinel,namespace=cloud.config,configId=hosts/vespa-container within timeout, will retry +1504592296.388000 WARNING : configproxy.com Request callback failed: APPLICATION_NOT_LOADED. Connection spec: tcp/localhost:19070, error message: Failed request (No application exists) from Connection { Socket[addr=/127.0.0.1,port=37806,localport=19070] } +1504592307.949461 WARNING : config-sentinel Connection to tcp/localhost:19090 failed or timed out +1504592307.949587 WARNING : config-sentinel FRT Connection tcp/localhost:19090 suspended until 2017-09-05 06:19:07 GMT +``` + +<table className="vespa-table"> + <thead> + <tr> + <th>Option</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>**-l *levellist* (--level=*levellist*)**</td> + <td>Filter messages by log level. By default, only messages of level *fatal, error, warning*, and *info* will be included, while messages of level *config, event, debug*, and *spam* will be ignored. This option allows you to replace or modify the list of log levels to be included. *levellist* is a comma-separated list of level names.<br/><br/> • The name *all* may be used to add all known levels <br/>• You may use + or - in front of terms to add or remove from the current (or default) list of levels instead of replacing it <br/>• Adding term</td> + </tr> + <tr> + <td>**-s *fieldlist***</td> + <td>Select which fields of log messages to show. The output field order is fixed. When using this option, only the named fields will be printed. The default fields are as [**-s fmttime,msecs,level,service,component,message**]. The fieldlist is a comma-separated list of field names. The name *all* may be used to add all possible fields. Prepending a minus sign will turn off the display of the named field. Starting the list with a plus sign will add and remove fields from the current (or default) list of fields instead of replacing it. Using this option several times works as if the given *fieldlist* arguments had been concatenated into one comma-separated list. Fields:<br/><br/> **time** <br/> Print the time in seconds since the epoch. Ignored if *fmttime* is shown <br/><br/> **fmttime** <br/> Print the time in human-readable [YYYY-MM-DD HH:mm:ss] format. Note that the time is printed in the local timezone. To get GMT output, use <code>{`env TZ=GMT vespa-logfmt`}</code> <br/><br/> **msecs** <br/> Add milliseconds after the seconds in *time* and *fmttime* output. Ignored if *usecs* is in effect <br/><br/> **usecs** <br/> Add microseconds after the seconds in *time* and *fmttime* output <br/><br/> **host** <br/> Print the hostname field <br/><br/> **level** <br/><br/> Print the level field (upper-cased) <br/><br/> **pid** <br/> Print the pid field <br/><br/> **service** <br/>Print the service field <br/><br/> **component** <br/> Print the component field<br/><br/> **message** <br/> Print the message text field. You probably always want to add this</td> + </tr> + <tr> + <td>**-p *pid***</td> + <td>Select messages where the pid field matches the *pid* string</td> + </tr> + <tr> + <td>**-S *service***</td> + <td>Select messages where the service field matches the *service* string</td> + </tr> + <tr> + <td>**-H *host***</td> + <td>Select messages where the hostname field matches the *host* string</td> + </tr> + <tr> + <td>**-c *regex***</td> + <td>Select messages where the component field matches the *regex*, using *perlre* regular expression matching</td> + </tr> + <tr> + <td>**-m *regex***</td> + <td>Select messages where the message text field matches the *regex*, using *perlre* regular expression matching</td> + </tr> + <tr> + <td>**-f**</td> + <td>Invoke tail -F to follow the input file</td> + </tr> + <tr> + <td>**-N**</td> + <td>De-quote quoted newlines in the message text field to an actual newline plus tab</td> + </tr> + <tr> + <td>**-t**</td> + <td>Format the component field (if shown) as a fixed-width string, truncating if necessary</td> + </tr> + <tr> + <td>**-ts**</td> + <td>Format the service field (if shown) as a fixed-width string, truncating if necessary</td> + </tr> + <tr> + <td>**-i, --internal**</td> + <td>Only include log entries emitted by the Vespa platform, i.e., exclude log entries from custom components</td> + </tr> + </tbody> +</table> + +## vespa-model-inspect + +*vespa-model-inspect* is a tool for inspecting the topology and services of a Vespa system. Hosts, services, clusters, ports, URLs, and config ids can be inspected. It can run on any machine in a Vespa cluster that is running a Vespa configuration server. + +Synopsis: `vespa-model-inspect [-c host | host:port] [-t tag] [-h] [-u] [-v] command` + +<table className="vespa-table"> + <thead> + <tr> + <th>Command</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>**hosts**</td> + <td>Show hostnames of all hosts in the Vespa system</td> + </tr> + <tr> + <td>**services**</td> + <td>Show a list of all service types in the Vespa system</td> + </tr> + <tr> + <td>**clusters**</td> + <td>Show a list of all named clusters in the Vespa system</td> + </tr> + <tr> + <td>**configids**</td> + <td>Show a list of all config ids in the Vespa system</td> + </tr> + <tr> + <td>**filter:ports**</td> + <td>List ports matching filter options</td> + </tr> + <tr> + <td>**host *hostname***</td> + <td>Show host details: What services are running, and what ports have they allocated</td> + </tr> + <tr> + <td>**service *servicetype***</td> + <td>Show service details: What instances of the service are running, on what hosts, and what ports have they allocated</td> + </tr> + <tr> + <td>**cluster *clustername***</td> + <td>Show all services in the cluster, with details on hostname and allocated ports</td> + </tr> + <tr> + <td>**configid *configid***</td> + <td>Show all services using this configid</td> + </tr> + <tr> + <td>**get-index-of *servicetype* *host***</td> + <td>Show all indexes for instances of the service type on the given host</td> + </tr> + </tbody> +</table> + +<table className="vespa-table"> + <thead> + <tr> + <th>Option</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>**-c *host* | *host:port***</td> + <td>Specify host and port (or just host) to use for getting the config that this tool displays. Default is to use the configserver. You might want to use localhost:19090 if you are on a host with a running Vespa system without a config server</td> + </tr> + <tr> + <td>**-h**</td> + <td>Show usage</td> + </tr> + <tr> + <td>**-t *tag***</td> + <td>to filter on a port tag</td> + </tr> + <tr> + <td>**-u**</td> + <td>Show URLs for services</td> + </tr> + <tr> + <td>**-v**</td> + <td>Verbose mode</td> + </tr> + </tbody> +</table> + +Examples: + +```bash expandable +$ vespa-model-inspect hosts +mynode.mydomain.com + +$ vespa-model-inspect services +config-sentinel +configproxy +configserver +container +container-clustercontroller +distributor +docprocservice +filedistributorservice +logd +logserver +searchnode +slobrok +storagenode + +$ vespa-model-inspect -u service distributor +distributor @ myhost.mydomain.com : content +myapp/distributor/4 + tcp/myhost1.mydomain.com:19112 (MESSAGING) + tcp/myhost1.mydomain.com:19113 (STATUS RPC) + http://myhost1.mydomain.com:19114/ (STATE STATUS HTTP) +distributor @ myhost2.mydomain.com : content +myapp/distributor/5 + tcp/myhost2.mydomain.com:19112 (MESSAGING) + tcp/myhost2.mydomain.com:19113 (STATUS RPC) + http://myhost2.mydomain.com:19114/ (STATE STATUS HTTP) +distributor @ myhost3.mydomain.com : content +``` + +## vespa-print-default + +Internal script used by other scripts to find hostname, config server addresses/ports, version, and more. Not intended for end-user usage. + +## vespa-proton-cmd + +Use *vespa-proton-cmd* to send commands to [proton](/en/content/proton). + +Synopsis: `vespa-proton-cmd HOSTSPEC COMMAND [ARGS]` + +The *hostspec* argument is one of `port|spec|--local|--id=name`. Use [vespa-model-inspect](#vespa-model-inspect) to locate the search node ADMIN RPC port: + +```bash +$ vespa-model-inspect service searchnode +searchnode @ /mynode.myhost.com : search +music/search/cluster.music/0 + tcp/mynode.myhost.com:19108 (STATUS ADMIN RTC RPC) + tcp/mynode.myhost.com:19109 (FS4) + tcp/mynode.myhost.com:19110 (TEST HACK SRMP) + tcp/mynode.myhost.com:19111 (ENGINES-PROVIDER RPC) + tcp/mynode.myhost.com:19112 (STATE HEALTH JSON HTTP) +``` + +Example: + +```bash +$ vespa-proton-cmd 19108 triggerFlush + OK: flush trigger enabled +``` + +Unless the **\-h** or **\--help** option is used, one of these commands must be present: + +<table className="vespa-table"> + <thead> + <tr> + <th>Command</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>**getProtonStatus**</td> + <td>Get the current proton state and its components.</td> + </tr> + <tr> + <td>**getState**</td> + <td>Get the current proton state.</td> + </tr> + <tr> + <td>**triggerFlush**</td> + <td>Trigger <a href="/en/content/proton#proton-maintenance-jobs" aria-label="flush">flush</a> as soon as possible for all document types.</td> + </tr> + <tr> + <td>**prepareRestart**</td> + <td>Estimates the cost of <a href="/en/content/proton#transaction-log" aria-label="transaction log">transaction log</a> replay, and flushes data structures if that will speed up a subsequent start. If this is not called before stopping proton, there is no estimation and no flush.</td> + </tr> + </tbody> +</table> + +## vespa-remove-index + +*vespa-remove-index* is a command-line tool to remove index data on a Vespa search node, by wiping out selected files and subdirectories found in *`$VESPA_HOME/var/db/vespa/`*. This process is irreversible, and the indexes deleted can not be recovered. + +Stop *services* before running it - example: + +```bash +$ vespa-stop-services && vespa-remove-index -force && vespa-start-services +``` + +Synopsis:`vespa-remove-index [-force] [-cluster name]` + +Example: + +```bash +$ vespa-remove-index +[info] For cluster music distribution key 0 you have: +[info] 156 kilobytes of data in var/db/vespa/search/cluster.music/n0 +Really to remove this vespa index? Type "yes" if you are sure ==> yes +[info] removing data: rm -rf var/db/vespa/search/cluster.music/n0 +[info] removed. +``` + +<table className="vespa-table"> + <thead> + <tr> + <th>Option</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>**-force**</td> + <td>Do not require verification from the user before really removing index data</td> + </tr> + <tr> + <td>**-cluster *name***</td> + <td>Only remove data for the given cluster name</td> + </tr> + </tbody> +</table> + +## vespa-route + +*vespa-route* is a tool to inspect Vespa routing configurations. If file is set, it will be parsed as a feed and the output will look similar to when using [/document/v1/](/en/reference/api/document-v1) with trace enabled. + +Synopsis: `vespa-route [options] [file]` + +Example: + +```bash +$ vespa-route +There are 5 route(s): + 1. default + 2. music + 3. music-direct + 4. music-index + 5. storage/cluster.music + +There are 2 hop(s): + 1. docproc/cluster.music.indexing/chain.indexing + 2. indexing +``` + +<table className="vespa-table"> + <thead> + <tr> + <th>Option</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>**--documentmanagerconfigid <code>{`<id>`}</code>**</td> + <td>Sets the config id that supplies document configuration</td> + </tr> + <tr> + <td>**--dump**</td> + <td>Prints the complete content of the routing table</td> + </tr> + <tr> + <td>**--help**</td> + <td>Prints this help</td> + </tr> + <tr> + <td>**--hop <code>{`<name>`}</code>**</td> + <td>Prints detailed information about hop <code>{`<name>`}</code></td> + </tr> + <tr> + <td>**--hops**</td> + <td>Prints a list of all available hops</td> + </tr> + <tr> + <td>**--identity <code>{`<id>`}</code>**</td> + <td>Sets the identity of message bus</td> + </tr> + <tr> + <td>**--listenport <code>{`<num>`}</code>**</td> + <td>Sets the port message bus will listen to</td> + </tr> + <tr> + <td>**--oosserverpattern <code>{`<id>`}</code>**</td> + <td>Sets the out-of-service server pattern for message bus</td> + </tr> + <tr> + <td>**--protocol <code>{`<name>`}</code>**</td> + <td>Sets the name of the protocol whose routing to inspect</td> + </tr> + <tr> + <td>**--route <code>{`<name>`}</code>**</td> + <td>Prints detailed information about route <code>{`<name>`}</code></td> + </tr> + <tr> + <td>**--routes**</td> + <td>Prints a list of all available routes</td> + </tr> + <tr> + <td>**--routingconfigid <code>{`<id>`}</code>**</td> + <td>Sets the config id that supplies the routing tables</td> + </tr> + <tr> + <td>**--services**</td> + <td>Prints a list of all available services</td> + </tr> + <tr> + <td>**--slobrokconfigid <code>{`<id>`}</code>**</td> + <td>Sets the config id that supplies the slobrok server list</td> + </tr> + <tr> + <td>**--trace <code>{`<num>`}</code>**</td> + <td>Sets the trace level to use when visualizing the route</td> + </tr> + <tr> + <td>**--verify**</td> + <td>All hops and routes are verified when routing</td> + </tr> + </tbody> +</table> + +## vespa-sentinel-cmd + +Use *vespa-sentinel-cmd* to list, start and stop services - refer to [config sentinel](/en/operations/self-managed/config-sentinel) for examples. It can also check for connectivity between nodes. + +<Warning> +**Important:** + +See [start / stop / restart](/en/operations/self-managed/admin-procedures#vespa-start-stop-restart) for how to stop all services on a node, optimizing for restart time - use this for tasks like software upgrade. +</Warning> + +Synopsis: `vespa-sentinel-cmd [-h] list|start <service>|restart <service>|stop <service>|connectivity` + +<table className="vespa-table"> + <thead> + <tr> + <th>Option</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>**-h**</td> + <td>Help text</td> + </tr> + </tbody> +</table> + +<table className="vespa-table"> + <thead> + <tr> + <th>Command</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>**list**</td> + <td>Lists the services running on this node and their status: <br/><br/> **service name** <br/><br/>**state**<br/> • RUNNING: Service is running<br/> • FINISHED: Service has been stopped<br/> • FAILED: Service has crashed and failed to restart<br/> • TERMINATING: Service is stopping <br/><br/> **mode** <br/>• MANUAL: Service has to be started and stopped manually <br/>• AUTO: Service will restart automatically if it stops <br/><br/> **pid**<br/> Pid of the process (main thread) <br/><br/> **exitstatus** <br/> Exit code the last time the service stopped. <br/><br/> **id** <br/> <a href="/en/applications/configapi-dev#config-id" aria-label="Config ID">Config ID</a> of the service</td> + </tr> + <tr> + <td>**restart [name]**</td> + <td>Restarts the service with the given name. The name is the first string in the service list given by <code>{`list`}</code></td> + </tr> + <tr> + <td>**stop [name]**</td> + <td>Stops the service with the given name</td> + </tr> + <tr> + <td>**start [name]**</td> + <td>Starts the service with the given name</td> + </tr> + <tr> + <td>**connectivity**</td> + <td>Use to troubleshoot startup issues/network configuration/ACLs/iptables:<br/><br/><code>{`$ vespa-sentinel-cmd connectivity`}</code><br/> <code>{`vespa-sentinel-cmd 'connectivity' OK.`}</code><br/> <code>{`node0.vespanet -> ok`}</code><br/> <code>{`node1.vespanet -> ok`}</code><br/> <code>{`node2.vespanet -> ok`}</code></td> + </tr> + </tbody> +</table> + +## vespa-set-node-state + +Set the [user state](/en/reference/api/cluster-v2#state-user) of a node. This will set the generated state to the user state if the user state is "better" than the generated state that would have been created if the user state were up. For instance, a node that is in `up` state can be forced into `down` state, while a node that is currently `down` can not be forced into `retired` state, but can be forced into maintenance state. + +Synopsis: `vespa-set-node-state [options] up|down|maintenance|retired [description]` + +Example: + +```bash +$ vespa-set-node-state -i 0 maintenance "Set to maintenance for software upgrade" +``` + +<table className="vespa-table"> + <thead> + <tr> + <th>Option</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>**-h, --help**</td> + <td>Show help</td> + </tr> + <tr> + <td>**-v**</td> + <td>More verbose output</td> + </tr> + <tr> + <td>**-s**</td> + <td>Less verbose output</td> + </tr> + <tr> + <td>**--show-hidden**</td> + <td>Also show hidden undocumented debug options</td> + </tr> + <tr> + <td>**-n, --no-wait**</td> + <td>Do not wait for node state changes to be visible in the cluster before returning</td> + </tr> + <tr> + <td>**-c, --cluster**</td> + <td>The cluster name of the cluster to query. If unspecified, and vespa is installed on the current node, information will be attempted auto-extracted</td> + </tr> + <tr> + <td>**-f, --force**</td> + <td>Force execution</td> + </tr> + <tr> + <td>**-t, --type**</td> + <td>Node type - can either be 'storage' or 'distributor'. If not specified, the operation will use state for both types</td> + </tr> + <tr> + <td>**-i, --index**</td> + <td>Node index. If not specified, all nodes found running on this host will be used</td> + </tr> + <tr> + <td>**--config-server**</td> + <td>Host name of the config server to query</td> + </tr> + <tr> + <td>**--config-server-port**</td> + <td>Port to connect to the config server on</td> + </tr> + <tr> + <td>**--config-request-timeout**</td> + <td>Timeout of config request</td> + </tr> + </tbody> +</table> + +## vespa-significance + +Generates a [significance model file](/en/ranking/significance#significance-model-file) for global significance. + +Synopsis: `vespa-significance <command> [options]` + +Commands: + +- `generate` – build a significance model from Vespa documents (JSONL) or Vespa Significance TSV (VSTSV) files. +- `export` – dump term statistics from an index to a VSTSV file. +- `merge` – merge multiple VSTSV files, optionally filtering low-frequency terms. + +### vespa-significance generate + +The command uses the same tokenizer as the default query processor, see [linguistics in Vespa](/en/linguistics/linguistics) for details. Custom tokenizers require a matching extractor. Tokens are lower-cased without stemming to align with query processing. + +Synopsis: `vespa-significance generate [options] --in <FILE>` + +Example: + +```bash +$ vespa-significance generate --in vespa-dump.jsonl --out en_model.json --field text --language en +``` + +This example generates a significance model called `en_mode.json` from a collection of Vespa Documents (the jsonl file). This is available in Vespa as of version 8.426.8. + +When running in Docker, mount the data directory to persist input and output files, e.g.: + +```bash +$ podman run -it --entrypoint bash -v $PWD/data:/data -w /data vespaengine/vespa:latest /opt/vespa/bin/vespa-significance generate --in docs.jsonl --out model.zst --field text --language en +``` + +To dump documents for JSONL input, use [vespa-visit](#vespa-visit) with the `--field-set` option: + +```bash +$ vespa visit --field-set mydocument:text_field > ./data/docs.jsonl +``` + +Inspect the resulting model file with `zstdcat` and `jq`: + +```bash +$ zstdcat ./data/model.zst | jq +``` + +Example generating model from VSTSV: + +```bash +$ vespa-significance generate --format vstsv --in dumped_term_df.vstsv --out un_model.json +``` + +In this example the input is generated with `vespa-significance export`. This is available in Vespa as of version 8.597.8. When language is not specified, the default is set to unknown when using VSTSV format. + +<table className="vespa-table"> + <thead> + <tr> + <th>Option</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>**-h, --help**</td> + <td>Show help for the subcommand.</td> + </tr> + <tr> + <td>**-i, --in <code>{`<FILE>`}</code>**</td> + <td>Input file. Default format is JSON Lines where each line is a <a href="/en/reference/schemas/document-json-format" aria-label="Vespa document in JSON">Vespa document in JSON</a>. Use <code>{`--format vstsv`}</code> to read VSTSV files.</td> + </tr> + <tr> + <td>**--format <code>{`<FORMAT>`}</code>**</td> + <td>Input format. Format can be <code>{`jsonl`}</code> or <code>{`vstsv`}</code>. Defaults to <code>{`jsonl`}</code>.</td> + </tr> + <tr> + <td>**-o, --out <code>{`<model.json[.zst]>`}</code>**</td> + <td>Output <a href="/en/ranking/significance#significance-model-file" aria-label="significance model">significance model</a>.</td> + </tr> + <tr> + <td>**-f, --field <code>{`<field>`}</code>**</td> + <td>JSONL: the name of the text field to analyse. VSTSV: No effect.</td> + </tr> + <tr> + <td>**-l, --language <code>{`<tag[,tag...]>`}</code>**</td> + <td>Comma-separated ISO language tags. The first tag controls tokenization; additional tags are stored in the model. Required for JSONL, optional for VSTSV (defaults to <code>{`un`}</code>). See supported tags in <a href="/en/linguistics/linguistics-opennlp#default-languages" aria-label="linguistics in Vespa">linguistics in Vespa</a>.</td> + </tr> + <tr> + <td>**-zst, --zst-compression <code>{`<ENABLED>`}</code>**</td> + <td>Enable Zstandard compression of the output. Enabled can be <code>{`true`}</code> or <code>{`false`}</code>. Default <code>{`false`}</code>. When <code>{`true`}</code>, the output file must end with <code>{`.zst`}</code>.</td> + </tr> + </tbody> +</table> + +### vespa-significance export + +Synopsis: `vespa-significance export [options] --field <FIELD>` + +Example: + +```bash +$ vespa-significance export --schema product --field title --out text.vstsv +``` + +Locates an index on the content node and exports term document frequencies from that index using `vespa-index-inspect`. The output VSTSV file can be passed to `generate --format vstsv` or merged with other exports. This is available in Vespa as of version 8.597.8. + +This command must be executed on a content node. + +<table className="vespa-table"> + <thead> + <tr> + <th>Option</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>**-h, --help**</td> + <td>Show help for the subcommand.</td> + </tr> + <tr> + <td>**--index-dir <code>{`<path/to/index>`}</code>**</td> + <td>Explicit path to the index directory. If omitted, the tool locates the directory using cluster, schema, and node information.</td> + </tr> + <tr> + <td>**--out <code>{`<FILE.vstsv[.zst]>`}</code>**</td> + <td>Output VSTSV file. Defaults to <code>{`export_*FIELD*.vstsv`}</code>. Compression adds <code>{`.zst`}</code> automatically.</td> + </tr> + <tr> + <td>**--field <code>{`<FIELD>`}</code>**</td> + <td>Text field to export. Must correspond to a field directory within the selected index. This is the same as a field in the schema.</td> + </tr> + <tr> + <td>**--cluster <code>{`<NAME>`}</code>**</td> + <td>Specifies the content cluster name for locating the index directory. If multiple clusters exist per node, use --cluster to choose one.</td> + </tr> + <tr> + <td>**--schema <code>{`<NAME>`}</code>**</td> + <td>Specifies the schema (document type) name used when locating the index directory.</td> + </tr> + <tr> + <td>**--node-index <code>{`<NUMBER>`}</code>**</td> + <td>Specifies the content node index for locating the index directory. If there are multiple indexes on a node, use --node-index to choose one.</td> + </tr> + <tr> + <td>**-zst, --zst-compression**</td> + <td>Write the VSTSV output compressed with Zstandard.</td> + </tr> + </tbody> +</table> + +### vespa-significance merge + +Synopsis: `vespa-significance merge [options] <input.vstsv[.zst]> [<input2.vstsv[.zst]> ...]` + +Example: + +```bash +$ vespa-significance merge --out merged_title.vstsv export_title_node1.vstsv export_title_node2.vstsv +``` + +Merges multiple VSTSV files and preserves the total document count in the header. Use this to combine exports before generating a model. This is available in Vespa as of version 8.597.8. + +<table className="vespa-table"> + <thead> + <tr> + <th>Option</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>**-h, --help**</td> + <td>Show help for the subcommand.</td> + </tr> + <tr> + <td>**--out <code>{`<FILE.vstsv[.zst]>`}</code>**</td> + <td>Output VSTSV file. Defaults to <code>{`merged.vstsv`}</code>. Compression adds <code>{`.zst`}</code>.</td> + </tr> + <tr> + <td>**--min-keep <code>{`<NUMBER>`}</code>**</td> + <td>Filter out terms with document frequency strictly lower than <code>{`NUMBER`}</code>.</td> + </tr> + <tr> + <td>**-zst, --zst-compression**</td> + <td>Write the merged VSTSV output compressed with Zstandard.</td> + </tr> + </tbody> +</table> + +## vespa-start-configserver + +Start a config server on a node, [details](/en/operations/self-managed/admin-procedures#vespa-start-stop-restart). See [vespa-start-services](#vespa-start-services) for node setup steps, before startup. + +Synopsis: `vespa-start-configserver` + +## vespa-start-services + +Start all services on a node, [details](/en/operations/self-managed/admin-procedures#vespa-start-stop-restart). + +As part of starting Vespa, the startup script calls [rhel-prestart.sh](https://github.com/vespa-engine/vespa/blob/master/vespabase/src/rhel-prestart.sh) to set up directories and system limits - this requires privileges to do so - run this command with sudo, see [example](/en/operations/self-managed/multinode-systems#config-server-cluster-setup). Refer to [vespa-start-services.sh](https://github.com/vespa-engine/vespa/blob/master/vespabase/src/vespa-start-services.sh) and [environment variables](/en/operations/self-managed/files-processes-and-ports#environment-variables). + +Synopsis: `vespa-start-services` + +When debugging a failed start, use [vespa-logfmt](#vespa-logfmt) to inspect the log. It is also useful to read up on the [start sequence](/en/operations/self-managed/configuration-server#start-sequence) and make sure the config server is running - Vespa will not start with a running config server. + +Vespa has a *Safe Cluster Startup* mode to only start vespa services after X% of nodes are running - see [cluster startup](/en/operations/self-managed/config-sentinel#cluster-startup). + +## vespa-stat + +*vespa-stat* is a tool to fetch statistics about a specific user, group, bucket, gid or document. + +vespa-stat works in two stages. The first stage is to figure out the actual buckets we want to look at. In the second stage, it can dump the located buckets. For each command line option, only the relevant documents will be dumped (the document for `--document/--gid`, or the user/group's documents for `--user/--group`). This stage can be turned on by adding `--dump`, but is default on for the case of `--document/--gid`. + +Synopsis: `vespa-stat [options]` + +Example: + +```bash +$ vespa-stat --document id:my_namespace:my_search::12345678-4fb7-3797-ae9a-d4d7a4e6e085 +Bucket maps to the following actual files: + BucketInfo(BucketId(0x4000000000004800): [distributor:17] [node(idx=17,crc=0xe5ce35c7,docs=57/57,bytes=478040/478040,trusted=true,active=true,ready=true), node(idx=15,crc=0xe5ce35c7,docs=57/57,bytes=478040/478040,trusted=true,active=true,ready=true)]) + +Details for BucketId(0x4000000000004800): + Bucket information from node 15: +Persistence bucket BucketId(0x4000000000004800), partition 0 + Timestamp: 1452598747000000, Doc(id:my_namespace:my_search::12345678-4fb7-3797-ae9a-d4d7a4e6e085), gid(0x0048e840a48002b12abbb0a0), size: 101 + + Bucket information from node 17: +Persistence bucket BucketId(0x4000000000004800), partition 0 + Timestamp: 1452598747000000, Doc(id:my_namespace:my_search::12345678-4fb7-3797-ae9a-d4d7a4e6e085), gid(0x0048e840a48002b12abbb0a0), size: 101 +``` + +<table className="vespa-table"> + <thead> + <tr> + <th>Option</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>**-b, --bucket <code>{`<bucketid>`}</code>**</td> + <td>Dump list of buckets that are contained in the given bucket, or that contain it</td> + </tr> + <tr> + <td>**-d, --dump**</td> + <td>Dump list of documents for all buckets matching the selection command.</td> + </tr> + <tr> + <td>**-g, --group <code>{`<groupid>`}</code>**</td> + <td>Dump list of buckets that can contain the given group</td> + </tr> + <tr> + <td>**-h, --help</td> + <td>Help text</td> + </tr> + <tr> + <td>**-l, --gid <code>{`<globalid>`}</code>**</td> + <td>Dump information about one specific document, as given by the GID (implies --dump)</td> + </tr> + <tr> + <td>**-o, --document <code>{`<docid>`}</code>**</td> + <td>Dump information about one specific document (implies --dump)</td> + </tr> + <tr> + <td>**-r, --route <code>{`<route>`}</code>**</td> + <td>Route to send the messages to, usually the name of the storage cluster</td> + </tr> + <tr> + <td>**-s, --bucketspace <code>{`<space>`}</code>**</td> + <td><a href="/en/content/buckets#bucket-space" aria-label="Bucket space">Bucket space</a> (*default* or *global*). If not specified, *default* is used</td> + </tr> + <tr> + <td>**-u, --user <code>{`<userid>`}</code>**</td> + <td>Dump list of buckets that can contain the given user</td> + </tr> + </tbody> +</table> + +## vespa-status-filedistribution + +Use *vespa-status-filedistribution* to get status from file distribution. Should be run on a config server, it connects to config server on localhost to get status. + +Synopsis: `vespa-status-filedistribution [--application <applicationNameArg>] [--debug] [--environment <environmentArg>] [(-h | --help)] [--instance <instanceNameArg>] [--region <regionArg>] [--tenant <tenantNameArg>] [--timeout <timeoutArg>]` + +<table className="vespa-table"> + <thead> + <tr> + <th>Option</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>**--application <code>{`<applicationName>`}</code>**</td> + <td>Application name</td> + </tr> + <tr> + <td>**--debug**</td> + <td>Print debug log</td> + </tr> + <tr> + <td>**--environment <code>{`<environment>`}</code>**</td> + <td>Environment name</td> + </tr> + <tr> + <td>**-h, --help**</td> + <td>Display help information</td> + </tr> + <tr> + <td>**--instance <code>{`<instanceName>`}</code>**</td> + <td>Instance name</td> + </tr> + <tr> + <td>**--region <code>{`<regionName>`}</code>**</td> + <td>Region name</td> + </tr> + <tr> + <td>**--tenant <code>{`<tenantName>`}</code>**</td> + <td>Tenant name</td> + </tr> + <tr> + <td>**--timeout <code>{`<timeout>`}</code>**</td> + <td>timeout (in seconds)</td> + </tr> + </tbody> +</table> + +## vespa-stop-configserver + +Stop a config server on a node, [details](/en/operations/self-managed/admin-procedures#vespa-start-stop-restart). + +Synopsis: `vespa-stop-configserver` + +## vespa-stop-services + +Stop all services on a node, [details](/en/operations/self-managed/admin-procedures#vespa-start-stop-restart). Running *vespa-stop-services* on a content node will call [prepareRestart](#vespa-proton-cmd) to optimize restart time. + +Synopsis: `vespa-stop-services` + +## vespa-summary-benchmark + +Tool for testing and benchmarking RPC docsum interface. Refer to [VespaSummaryBenchmark.java](https://github.com/vespa-engine/vespa/blob/master/vespaclient-java/src/main/java/com/yahoo/vespasummarybenchmark/VespaSummaryBenchmark.java) + +## vespa-visit + +Used to run a [visit](/en/clients/vespa-cli) operation, with more options than [vespa visit](/en/clients/vespa-cli). It uses the [Vespa Message Bus](/en/writing/document-routing) and must be run inside a Vespa application - it does not use the Vespa HTTP APIs. + +<Note> +**Note:** + +This tool is easily confused with `vespa visit` in the [vespa CLI](/en/clients/vespa-cli). The latter is the common tool for visiting, `vespa-visit` is built for debugging and complex use cases. +</Note> + +By default, vespa-visit gets visited documents and emits them to stdout. However, the tool may specify a [vespa-visit-target](#vespa-visit-target) and be used as a tool to run reprocessing or migration. It supports keeping a progress file on disk, such that you can restart it if it should fail in the middle for some reason. + +To migrate a set of documents from one cluster to another, use *visiting* - as the data is transferred directly, using a compact serialization format, from the source nodes to the targets, this is performance optimal (data is not piped through the visit client). Implement backup this way, or dump to file. + +Search node recovery: Feed the documents directly to a search cluster. Example, selecting documents of type *music*: + +```bash +$ vespa-visit --selection music --datahandler indexing +``` + +This feeds from the source into the search cluster in the same application. Note that simultaneous feed can make updates go lost. + +Include [remove-entries](/en/operations/self-managed/admin-procedures#data-retention-vs-size) in the visit operation using *\--visitremove* - this dumps the tombstones of documents recently removed. + +The [content policy](/en/writing/document-routing#content) can be configured to use a set of configuration servers from another cluster to configure itself. This is specified with the *config* parameter. As an example, the following route routes to the content cluster *mycluster* with a configuration server on *myconfigserver.mydomain.com:12345*: + +```bash +[Content:config=tcp/myconfigserver.mydomain.com:12345;cluster=mycluster] +``` + +The following examples illustrate how to copy all data from a source cluster to another cluster using vespa-visit: + +```bash +# Copies all data in the local cluster, routing it to the remote mycluster +$ vespa-visit --datahandler '[Content:config=tcp/myconfigserver.mydomain.com:12345;cluster=mycluster]' + +# Limit to 'music' documents only +$ vespa-visit --datahandler '[Content:config=tcp/myconfigserver.mydomain.com:19070;cluster=mycluster]' \ + --selection music + +# Limit to all documents for user '1234' +$ vespa-visit --datahandler '[Content:config=tcp/myconfigserver.mydomain.com:12345;cluster=mycluster]' \ + --selection id.user=1234 +``` + +Visitor processor types: + +<table className="vespa-table"> + <thead> + <tr> + <th>Processor Type</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>**Dump visitor**</td> + <td>The most commonly used visitor processor type is the dump visitor. All it does is to send the read documents on to some external target specified by the visitor. Using the command line tool *vespa-visit*, the default is to just send the documents back to the client, and have them printed to stdout. The dump visitor is used to implement reprocessing. Typically, using a messagebus route, which will send the documents through the document processing cluster and then back to the content cluster. Migration of documents from one cluster to another is also implemented using a dump visitor.</td> + </tr> + <tr> + <td>**Streaming search visitor**</td> + <td>The <a href="/en/performance/streaming-search" aria-label="streaming search">streaming search</a> visitor runs in the Vespa container, making it transparent whether search results were created from streaming or indexed search - see <a href="/en/reference/applications/services/content#document" aria-label="indexing mode">indexing mode</a>.</td> + </tr> + </tbody> +</table> + +Requests sent from the visitor processor are sent to a visitor target - types: + +<table className="vespa-table"> + <thead> + <tr> + <th>Target Type</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>**Message bus routes**</td> + <td>You can specify a <a href="/en/writing/document-routing" aria-label="message bus route">message bus route</a> name directly, and this route will be used to send the results. This is typically used when doing reprocessing or migration. Message bus routes are set up in the application package. In addition, some routes may have been auto-generated in simple setups, for instance, a route called *default* is generated if your setup is simple enough for the config model to likely guess where you want to send your data.</td> + </tr> + <tr> + <td>**Slobrok address**</td> + <td>You can also specify a slobrok address for data to be sent to. A slobrok address is a slash-separated path where you can use an asterisk to mean any element within this path. For instance, if you have a docproc cluster called *mydpcluster*, it will have registered its nodes with slobrok names like *docproc/cluster.mydpcluster/docproc/0/feed_processor*, where the 0 here indicates the first node in the cluster. You can thus specify to send visit data to this docproc cluster by stating a slobrok address of *docproc/cluster.mydpcluster/docproc/*/feed_processor*. Note that this will not send all the data to one or all the nodes. The data sent from the visitor will be distributed among the matching nodes, but each message will just be sent to one node. <br/><br/> Slobrok names can be used when using <a href="#vespa-visit-target" aria-label="vespa-visit-target">vespa-visit-target</a> to retrieve the data from some location. If you start vespa-visit-target on two nodes, listening to slobrok names *mynode/0/visit-destination* and *mynode/1/visit-destination*, you can send the results to these nodes by specifying *mynode/*/visit-destination* as the data handler.<br/><br/> <a href="#vespa-destination" aria-label="vespa-destination">vespa-destination</a> is similar to vespa-visit-target in that it can receive messages from messagebus and print the contents to stdout. It can be useful in situations where you want to debug a route or a docproc, by using the vespadestination as the endpoint of your route.</td> + </tr> + <tr> + <td>**TCP socket**</td> + <td>TCP sockets can also be specified directly. This requires that the endpoint speaks FNET RPC. This is typically done, either by using the *vespa-visit-target* tool, or by using a visitor destination programmatically by using a utility class in the document API. A socket address looks like the following: tcp/*hostname*:*port*/*servicename*. For instance, an address generated by *vespa-visit-target* might look like: *tcp/myhost.mydomain.com:12345/visit-destination*</td> + </tr> + </tbody> +</table> + +Also see [vespa-destination](#vespa-destination). + +Synopsis: `vespa-visit [options]` + +<table className="vespa-table"> + <thead> + <tr> + <th>Option</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>**--abortonclusterdown**</td> + <td>Abort if cluster is down</td> + </tr> + <tr> + <td>**-b, --maxbuckets <code>{`<num>`}</code>**</td> + <td>Maximum buckets per visitor</td> + </tr> + <tr> + <td>**--bucketspace <code>{`<space>`}</code>**</td> + <td><a href="/en/content/buckets#bucket-space" aria-label="Bucket space">Bucket space</a> to visit (*default* or *global*). If not specified, *default* is used</td> + </tr> + <tr> + <td>**-c, --cluster <code>{`<cluster>`}</code>**</td> + <td>Visit the given cluster</td> + </tr> + <tr> + <td>**-d, --datahandler <code>{`<target>`}</code>**</td> + <td>Send results to the given target - see <a href="#vespa-visit-target" aria-label="vespa-visit-target">vespa-visit-target</a></td> + </tr> + <tr> + <td>**-f, --from <code>{`<timestamp>`}</code>**</td> + <td>Only visit from the given timestamp (microseconds)</td> + </tr> + <tr> + <td>**-h, --help**</td> + <td>Show help text</td> + </tr> + <tr> + <td>**-i, --printids**</td> + <td>Display only document identifiers</td> + </tr> + <tr> + <td>**--jsonoutput**</td> + <td>Output a JSON array of document objects. This is the default output format.</td> + </tr> + <tr> + <td>**--jsonl**</td> + <td>Output documents as JSONL (JSON Lines format). Each individual document is output as a single line, with a newline separating each document. Lines are not comma-separated, and there is no top-level array wrapping the document objects.</td> + </tr> + <tr> + <td>**-l, --fieldset <code>{`<fieldset>`}</code>**</td> + <td>Retrieve the specified fields only (see <a href="/en/schemas/documents#fieldsets" aria-label="Document field sets">Document field sets</a>). Default: <code>{`[document]`}</code></td> + </tr> + <tr> + <td>**--libraryparam <code>{`<key>`}</code> <code>{`<val>`}</code>**</td> + <td>Send parameter to the visitor library</td> + </tr> + <tr> + <td>**-m, --maxpending <code>{`<num>`}</code>**</td> + <td>Maximum pending messages to data handlers per storage visitor</td> + </tr> + <tr> + <td>**--maxpendingsuperbuckets <code>{`<num>`}</code>**</td> + <td>Maximum pending visitor messages from the vespa-visit client. If set, dynamic throttling of visitors is disabled</td> + </tr> + <tr> + <td>**--maxtotalhits <code>{`<num>`}</code>**</td> + <td>Abort visiting when received this many total documents. This is only an approximate number, all pending work will be completed, and those documents will also be returned</td> + </tr> + <tr> + <td>**-o, --timeout <code>{`<milliseconds>`}</code>**</td> + <td>Time out visitor after given time</td> + </tr> + <tr> + <td>**-p, --progress <code>{`<file>`}</code>**</td> + <td>Use the given file to track progress. <code>{`-p progress-file`}</code> saves progress, allowing the visitor to resume at next startup. Always remove the progress file to run the visiting operation from the start.</td> + </tr> + <tr> + <td>**--processtime <code>{`<num>`}</code>**</td> + <td>Sleep for this number of milliseconds before processing the message. (Debug option for pretending to be a slow client)</td> + </tr> + <tr> + <td>**-r, --visitremoves**</td> + <td>Return tombstone entries of documents that have been removed. Tombstones will be output as <code>{`remove`}</code> objects, which only contain a document ID. When using <code>{`--visitremoves`}</code>, regular (non-tombstone) documents will also be returned.</td> + </tr> + <tr> + <td>**-s, --selection <code>{`<selection>`}</code>**</td> + <td><a href="/en/reference/writing/document-selector-language" aria-label="Selection">Selection</a> string for which documents to visit. E.g., <code>{`-s 'id.hash().abs() % 100 == 0'`}</code> dumps 1% of the corpus - see <a href="/en/clients/vespa-cli#selection" aria-label="selection">selection</a>. Note that this expression is evaluated for *every* document in the cluster, so running 100 visits comparing against all values in [0, 99) end up reading all documents 100 times. Prefer using <code>{`--slices`}</code> and <code>{`--sliceid`}</code> instead if available.</td> + </tr> + <tr> + <td>**--shorttensors**</td> + <td>Output using <a href="/en/reference/schemas/document-json-format#tensor" aria-label="tensor short form">tensor short form</a></td> + </tr> + <tr> + <td>**--skipbucketsonfatalerrors**</td> + <td>Skip visiting super buckets with fatal error codes</td> + </tr> + <tr> + <td>**--sliceid <code>{`<arg>`}</code>**</td> + <td>The slice number of the visit represented by this visitor. This number must be non-negative and less than the number of slices specified for the visit.</td> + </tr> + <tr> + <td>**--slices <code>{`<arg>`}</code>**</td> + <td>Split the document corpus into this number of independent slices. This lets multiple, concurrent series of visitors advance the same logical visit independently, by specifying a different <code>{`sliceid`}</code> for each.<br/><br/> E.g. <code>{`--slices 100 --sliceid 0`}</code> dumps 1% of the corpus by efficiently iterating over only 1/100th of the data space. For a given number of <code>{`--slices`}</code>, it's possible to visit the entire corpus (possibly in parallel) with non-overlapping output by visiting with all <code>{`--sliceid`}</code> values from (and including) 0 up to (and excluding) <code>{`--slices`}</code>.</td> + </tr> + <tr> + <td>**-t, --to <code>{`<timestamp>`}</code>**</td> + <td>Only visit up to the given timestamp (microseconds)</td> + </tr> + <tr> + <td>**--tracelevel <code>{`<level>`}</code>**</td> + <td>Tracelevel ([0-9]), for debugging</td> + </tr> + <tr> + <td>**-u, --buckettimeout <code>{`<milliseconds>`}</code>**</td> + <td>Fail visitor if visiting a single bucket takes longer than this (default same as timeout)</td> + </tr> + <tr> + <td>**-v, --verbose**</td> + <td>Show progress and info on STDERR</td> + </tr> + <tr> + <td>**--visitinconsistentbuckets**</td> + <td>Don't wait for inconsistent buckets to become consistent. See <a href="/en/content/consistency#read-consistency" aria-label="read-consistency">read-consistency</a> for details.</td> + </tr> + <tr> + <td>**--visitlibrary <code>{`<string>`}</code>**</td> + <td>Use the given visitor library</td> + </tr> + </tbody> +</table> + +## vespa-visit-target + +[vespa-visit-target](#vespa-visit-target) is a tool to set up an endpoint for [visiting](/en/clients/vespa-cli) data. It binds to a socket or a slobrok address, which is specified as a target in the visit client. Also see [vespa-destination](#vespa-destination). + +Synopsis: `vespa-visit-target [options]` + +<table className="vespa-table"> + <thead> + <tr> + <th>Option</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>**-c, --visithandler <code>{`<classname>`}</code>**</td> + <td>Use the given class as a visit handler (defaults to StdOutVisitorHandler)</td> + </tr> + <tr> + <td>**-h, --help**</td> + <td>Show help page</td> + </tr> + <tr> + <td>**-i, --printids**</td> + <td>Display document IDs only</td> + </tr> + <tr> + <td>**-o, --visitoptions <code>{`<args>`}</code>**</td> + <td>Option arguments to pass through to the visitor handler instance</td> + </tr> + <tr> + <td>**-p, --processtime <code>{`<msecs>`}</code>**</td> + <td>Sleep msecs milliseconds before processing the message. (Debug option for pretending to be a slow client)</td> + </tr> + <tr> + <td>**-s, --bindtoslobrok <code>{`<address>`}</code>**</td> + <td>Bind to slobrok address. One, and only one, of the binding options must be set</td> + </tr> + <tr> + <td>**-t, --bindtosocket <code>{`<port>`}</code>**</td> + <td>Bind to TCP port. One, and only one, of the binding options must be set</td> + </tr> + <tr> + <td>**-v, --verbose**</td> + <td>Indent output, show progress and info on STDERR</td> + </tr> + </tbody> +</table> \ No newline at end of file diff --git a/en/reference/operations/tools.html b/en/reference/operations/tools.html deleted file mode 100644 index 2470e81420..0000000000 --- a/en/reference/operations/tools.html +++ /dev/null @@ -1,490 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Tools" -redirect_from: -- /en/operations/tools.html ---- - -<p>This is the command-line tools reference for various Vespa use cases.</p> - - -<h2 id="vespa-analyze-onnx-model">vespa-analyze-onnx-model</h2> -<p>Loads an ONNX-model to analyze memory usage and infer/probe output types based on input types.</p> -<p>Synopsis: <code>vespa-analyze-onnx-model <onnx-model> [options...]</code></p> -<p> - Example (refer to <a href="../../ranking/onnx">ONNX</a> for more examples): -</p> -<pre> -$ vespa-analyze-onnx-model Network.onnx -</pre> -<table class="table"> - <thead> - <tr> - <th>Option</th> - <th>Description</th> - </tr> - </thead> - <tbody> - <tr> - <th>--probe-types</th> - <td>Use onnx model to infer/probe output types based on input types</td> - </tr> - </tbody> -</table> - - - -<h2 id="vespa-fbench">vespa-fbench</h2> -<p> - <em>vespa-fbench</em> is used for benchmarking the capacity of a Vespa system. - Refer to <a href="../../performance/benchmarking.html">vespa-benchmarking</a> - for usage and examples. -</p> -<p> - Multiple hostnames and ports can be used, - to distribute load round-robin to clients. -</p> -<p>Synopsis: <code>vespa-fbench [options] <hostname> <port></code></p> -<p>Example:</p> -<pre> -$ vespa-fbench -n 10 -q query%03d.txt -s 300 -c 0 -o output%03d.txt -xy test.domain.com 8080 -</pre> -<table class="table"> - <thead> - <tr> - <th>Option</th> - <th>Description</th> - </tr> - </thead> - <tbody> - <tr> - <th>-H <em>header</em></th> - <td>append extra header to each get request.</td> - </tr> - <tr> - <th>-A <em>assign authority</em></th> - <td>hostname:port. Overrides Host: header sent.</td> - </tr> - <tr> - <th>-a <em>str</em></th> - <td>append string to each query</td> - </tr> - <tr> - <th style="width:200px">-n <em>numClients</em></th> - <td> - Run vespa-fbench with <em>numClients</em> clients in parallel. If not - specified, vespa-fbench will use a default value of <em>10</em> clients. - </td> - </tr> - <tr> - <th>-c <em>cycleTime</em></th> - <td> - each client will make a request each <num> milliseconds [1000] - ('-1' -> cycle time should be twice the response time) - </td> - </tr> - <tr> - <th>-l <em>limit</em></th> - <td>minimum response size for successful requests [0]</td> - </tr> - <tr> - <th>-i <em>ignoreCount</em></th> - <td>do not log the <num> first results. -1 means no logging [0]</td> - </tr> - <tr> - <th>-s <em>seconds</em></th> - <td>run the test for <num> seconds. -1 means forever [60]</td> - </tr> - <tr> - <th>-q <em>queryFilePattern</em></th> - <td> - pattern defining input query files, e.g. <em>query%03d.txt</em> - (the pattern is used with sprintf to generate filenames). - Unless using POST, a query file has one query per line, each line starting with <code>/search/</code>: - <pre> -/search/?yql=select%20%2A%20from%20sources%20%2A%20where%20true -</pre> - </td> - </tr> - <tr> - <th>-P</th> - <td> - use POST for requests instead of GET. Two lines per query, format: - <pre> -/search/ -{"yql" : "select * from sources * where true"} -</pre> - Any line starting with "/" will be taken as a URL path, - with the following lines taken as the content (these lines can NOT start with "/"). - The default content type is <em>"Content-Type: application/json"</em>; see <em>-H</em>. - </td> - </tr> - <tr> - <th>-o <em>outputFilePattern</em></th> - <td> - save query results to output files with the given pattern (default is - not saving.) - </td> - </tr> - <tr> - <th>-r <em>restartLimit</em></th> - <td>number of times to re-use each query file. -1 means no limit [-1]</td> - </tr> - <tr> - <th>-m <em>maxLineSize</em></th> - <td> - max line size in input query files [8192]. - Can not be less than the minimum [1024]. - </td> - </tr> - <tr> - <th>-p <em>seconds</em></th> - <td> - Print summary every <num> seconds. - Only available when installing vespa-fbench from test branch, - </td> - </tr> - <tr> - <th>-k</th> - <td>Enable HTTP keep-alive.</td> - </tr> - <tr> - <th>-d</th> - <td>Base64 decode POST request content</td> - </tr> - <tr> - <th>-x</th> - <td> - write benchmark data reporting to output file: - <table class="table"> - <thead></thead><tbody> - <tr> - <th style="width:200px">NumHits</th> - <td>Number of hits returned</td> - </tr> - <tr> - <th>NumFastHits</th> - <td>Number of actual document hits returned</td> - </tr> - <tr> - <th>TotalHitCount</th> - <td>Total number of hits for query</td> - </tr> - <tr> - <th>QueryHits</th> - <td>Hits as specified in query</td> - </tr> - <tr> - <th>QueryOffset</th> - <td>Offset as specified in query</td> - </tr> - <tr> - <th>NumErrors</th> - <td>Number of error hits returned</td> - </tr> - <tr> - <th>NumGroupHits</th> - <td>Number of grouping hits returned</td> - </tr> - <tr> - <th>SearchTime</th> - <td>Time used for searching. Entire query time for one phase search, - first phase for two-phase search</td> - </tr> - <tr> - <th>AttributeFetchTime</th> - <td>Time used for attribute fetching, or 0 for one phase search</td> - </tr> - <tr> - <th>FillTime</th> - <td>Time used for summary fetching, or 0 for one phase search</td> - </tr> - </tbody> - </table> - </td> - </tr> - <tr> - <th>-y</th> - <td> - write data on coverage to output file (must be used with -x). - <table class="table"> - <thead></thead><tbody> - <tr> - <th style="width:200px">DocsSearched</th> - <td>Total number of documents in nodes searched</td> - </tr> - <tr> - <th>NodesSearched</th> - <td>Total number of search nodes which were used</td> - </tr> - <tr> - <th>FullCoverage</th> - <td>1 if true, 0 if false</td> - </tr> - </tbody> - </table> - </td> - </tr> - <tr> - <th>-z</th> - <td>Use single query file to be distributed between clients.</td> - </tr> - <tr> - <th>-T <em>file</em></th> - <td> - CA certificate file to verify peer against. - Use to benchmark https enabled port. (e.g <em>-T /etc/ssl/certs/ca-bundle.crt</em>) - </td> - </tr> - <tr> - <th>-C <em>file</em></th> - <td>Client certificate file name</td> - </tr> - <tr> - <th>-K <em>file</em></th> - <td>Client private key file name</td> - </tr> - <tr> - <th>-D</th> - <td>Use TLS configuration from environment if T/C/K is not used</td> - </tr> - </tbody> -</table> -<p>Default output:</p> -<table class="table"> - <thead> - </thead> - <tbody> - <tr> - <th style="width:200px">connection reuse count</th> - <td> - Indicates how many times HTTP connections were reused to issue another request. - Note that this number will only be displayed if the -k switch (enable HTTP keep-alive) is used. - </td> - </tr> - <tr> - <th>clients</th> - <td>Echo of the -n parameter.</td> - </tr> - <tr> - <th>cycle time</th> - <td>Echo of the -c parameter.</td> - </tr> - <tr> - <th>lower response limit</th> - <td>Echo of the -l parameter.</td> - </tr> - <tr> - <th>skipped requests</th> - <td> - Number of requests that was skipped by vespa-fbench. - vespa-fbench will typically skip a request if the line containing the query url exceeds a pre-defined limit. - Skipped requests will have minimal impact on the statistical results. - </td> - </tr> - <tr> - <th>failed requests</th> - <td> - The number of failed requests. - A request will be marked as failed if en error occurred while reading the result - or if the result contained fewer bytes than 'lower response limit'. - </td> - </tr> - <tr> - <th>successful requests</th> - <td> - Number of successful requests. - Each performed request is counted as either successful or failed. - Skipped requests (see above) are not performed and therefore not counted. - </td> - </tr> - <tr> - <th>cycles not held</th> - <td> - Number of cycles not held. - The cycle time is specified with the -c parameter. - It defines how often a client should perform a new request. - However, a client may not perform another request before the result from the previous request has been obtained. - Whenever a client is unable to initiate a new request 'on time' due to not being finished with the previous request, - this value will be increased. - </td> - </tr> - <tr> - <th>minimum response time</th> - <td> - The minimum response time. - The response time is measured as the time period from just before the request is sent to the server, - till the result is obtained from the server. - </td> - </tr> - <tr> - <th>maximum response time</th> - <td> - The maximum response time. - The response time is measured as the time period from just before the request is sent to the server, - till the result is obtained from the server. - </td> - </tr> - <tr> - <th>average response time</th> - <td> - The average response time. - The response time is measured as the time period from just before the request is sent to the server, - till the result is obtained from the server. - </td> - </tr> - <tr> - <th>X percentile</th> - <td> - The X percentile of the response time samples; - a value selected such that X percent of the response time samples are below this value. - In order to calculate percentiles, - a histogram of response times is maintained for each client at runtime and merged after the test run ends. - If a percentile value exceeds the upper bound of this histogram, - it will be approximated (and thus less accurate) and marked with '(approx)'. - </td> - </tr> - <tr> - <th>actual query rate</th> - <td>The average number of queries per second; QPS.</td> - </tr> - <tr> - <th>utilization</th> - <td> - The percentage of time used waiting for the server to complete (successful) requests. - Note that if a request fails, - the utilization will drop since the client has 'wasted' the time spent on the failed request. - </td> - </tr> - <tr> - <th>zero hit queries</th> - <td>The number of queries that gave zero hits in Vespa</td> - </tr> - </tbody> -</table> - - - -<h2 id="vespa-makefsa">vespa-makefsa</h2> -<p> -Use <em>vespa-makefsa</em> to compile a list of phrases into a <em>finite state automation</em> (FSA) file. -FSA files are used in <a href="../../linguistics/query-rewriting.html">query phrasing and rewriting</a>. -</p><p> -If input file is not specified, standard input is used. -</p><p> -Synopsis: <code>vespa-makefsa [-h] [-b] [-B] [-e] [-n] [-s bytes] [-z bytes] [-t] [-p] [-i] [-S serial] [-v] [-V] [input_file] output_file</code> -</p> -<table class="table"> - <thead> - <tr> - <th>Option</th> - <th>Description</th> - </tr> - </thead> - <tbody> - <tr> - <th>-h</th> - <td> - Help text - </td> - </tr><tr> - <th>-b</th> - <td> - Use binary input format with Base64 encoded info - </td> - </tr><tr> - <th>-B</th> - <td> - Use binary input format with raw - </td> - </tr><tr> - <th>-e</th> - <td> - Use text input format with no info (default) - </td> - </tr><tr> - <th>-n</th> - <td> - Use text input format with (unsigned) numerical info - </td> - </tr><tr> - <th>-s bytes</th> - <td> - Data size for numerical info: 1,2 or 4(default) - </td> - </tr><tr> - <th>-z bytes</th> - <td> - Data size for binary info (-B) (0 means NUL terminated) - </td> - </tr><tr> - <th>-t</th> - <td> - Use text input format - </td> - </tr><tr> - <th>-p</th> - <td> - Build automaton with perfect hash - </td> - </tr><tr> - <th>-i</th> - <td> - Ignore info string, regardless of input format - </td> - </tr><tr> - <th>-S serial</th> - <td> - Serial number - </td> - </tr><tr> - <th>-v</th> - <td> - Verbose - </td> - </tr><tr> - <th>-V</th> - <td> - Display version - </td> - </tr> - </tbody> -</table> - - - -<h2 id="vespa-query-profile-dump-tool">vespa-query-profile-dump-tool</h2> -<p>Dumps all resolved query profile properties for a set of dimension values</p> -<p>Synopsis:<code>vespa-query-profile-dump-tool dump [query-profile] [dir]? [parameters]?</code></p> -<p>Examples:</p> -<pre> -dump default # dumps the 'default' profile non-variant values in the current dir - -dump default x=x1&y=y1 # dumps the 'default' profile resolved with dimensions values x=x1 and y=y1 in the current dir - -dump default myapppackage # dumps the 'default' profile non-variant values in myapppackage/search/query-profiles - -dump default dev/myprofiles x=x1&y=y1 # dumps the 'default' profile resolved with dimensions values x=x1 and y=y1 in dev/myprofiles -</pre> -<table class="table"> - <thead> - <tr> - <th>Option</th> - <th>Description</th> - </tr> - </thead> - <tbody> - <tr> - <th>query-profile</th> - <td>Name of the query profile to dump the values of</td> - </tr> - <tr> - <th>dir</th> - <td>Path to an application package or query profile directory. Default: current dir</td> - </tr> - <tr> - <th>parameters</th> - <td>HTTP request encoded dimension keys used during resolving. Default: none</td> - </tr> - </tbody> -</table> diff --git a/en/reference/operations/tools.mdx b/en/reference/operations/tools.mdx new file mode 100644 index 0000000000..467dba33f0 --- /dev/null +++ b/en/reference/operations/tools.mdx @@ -0,0 +1,325 @@ +--- +title: "Tools" +description: "This is the command-line tools reference for various Vespa use cases." +--- + +## vespa-analyze-onnx-model + +Loads an ONNX-model to analyze memory usage and infer/probe output types based on input types. + +Synopsis: `vespa-analyze-onnx-model <onnx-model> [options...]` + +Example (refer to [ONNX](/en/ranking/onnx) for more examples): + +```bash +$ vespa-analyze-onnx-model Network.onnx +``` + +<table className="vespa-table"> + <thead> + <tr> + <th>Option</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>**--probe-types**</td> + <td>Use onnx model to infer/probe output types based on input types</td> + </tr> + </tbody> +</table> + +## vespa-fbench + +*vespa-fbench* is used for benchmarking the capacity of a Vespa system. Refer to [vespa-benchmarking](/en/performance/benchmarking) for usage and examples. + +Multiple hostnames and ports can be used, to distribute load round-robin to clients. + +Synopsis: `vespa-fbench [options] <hostname> <port>` + +Example: + +```bash +$ vespa-fbench -n 10 -q query%03d.txt -s 300 -c 0 -o output%03d.txt -xy test.domain.com 8080 +``` + +<table className="vespa-table"> + <thead> + <tr> + <th>Option</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>**-H *header***</td> + <td>append extra header to each get request.</td> + </tr> + <tr> + <td>**-A *assign authority***</td> + <td>hostname:port. Overrides Host: header sent.</td> + </tr> + <tr> + <td>**-a *str***</td> + <td>append string to each query</td> + </tr> + <tr> + <td>**-n *numClients***</td> + <td>Run vespa-fbench with *numClients* clients in parallel. If not specified, vespa-fbench will use a default value of *10* clients.</td> + </tr> + <tr> + <td>**-c *cycleTime***</td> + <td>each client will make a request each <code>{`<num>`}</code> milliseconds [1000] ('-1' -> cycle time should be twice the response time)</td> + </tr> + <tr> + <td>**-l *limit***</td> + <td>minimum response size for successful requests [0]</td> + </tr> + <tr> + <td>**-i *ignoreCount***</td> + <td>do not log the <code>{`<num>`}</code> first results. -1 means no logging [0]</td> + </tr> + <tr> + <td>**-s *seconds***</td> + <td>run the test for <code>{`<num>`}</code> seconds. -1 means forever [60]</td> + </tr> + <tr> + <td>**-q *queryFilePattern***</td> + <td>pattern defining input query files, e.g. *query%03d.txt* (the pattern is used with sprintf to generate filenames). Unless using POST, a query file has one query per line, each line starting with <code>{`/search/`}</code>:<br/><br/><code>{`/search/?yql=select%20%2A%20from%20sources%20%2A%20where%20true`}</code></td> + </tr> + <tr> + <td>**-P**</td> + <td>use POST for requests instead of GET. Two lines per query, format:<br/><br/><code>{`/search/`}</code><br/><code>{`{"yql" : "select * from sources * where true"}`}</code> <br/><br/>Any line starting with "/" will be taken as a URL path, with the following lines taken as the content (these lines can NOT start with "/"). The default content type is *"Content-Type: application/json"*; see *-H*.</td> + </tr> + <tr> + <td>**-o *outputFilePattern***</td> + <td>save query results to output files with the given pattern (default is not saving.)</td> + </tr> + <tr> + <td>**-r *restartLimit***</td> + <td>number of times to re-use each query file. -1 means no limit [-1]</td> + </tr> + <tr> + <td>**-m *maxLineSize***</td> + <td>max line size in input query files [8192]. Can not be less than the minimum [1024].</td> + </tr> + <tr> + <td>**-p *seconds***</td> + <td>Print summary every <code>{`<num>`}</code> seconds. Only available when installing vespa-fbench from test branch,</td> + </tr> + <tr> + <td>**-k**</td> + <td>Enable HTTP keep-alive.</td> + </tr> + <tr> + <td>**-d**</td> + <td>Base64 decode POST request content</td> + </tr> + <tr> + <td>**-x**</td> + <td>write benchmark data reporting to output file: <br/><br/> **NumHits**<br/> Number of hits returned <br/><br/> **NumFastHits** <br/> Number of actual document hits returned <br/> <br/>**TotalHitCount** <br/> Total number of hits for query <br/> <br/> **QueryHits** <br/>Hits as specified in query <br/> <br/> **QueryOffset**<br/> Offset as specified in query <br/><br/> **NumErrors** <br/> Number of error hits returned <br/> <br/> **NumGroupHits** <br/> Number of grouping hits returned <br/><br/> **SearchTime** <br/> Time used for searching. Entire query time for one phase search, first phase for two-phase search <br/> <br/> **AttributeFetchTime** <br/> Time used for attribute fetching, or 0 for one phase search <br/> <br/> **FillTime** <br/> Time used for summary fetching, or 0 for one phase search</td> + </tr> + <tr> + <td>**-y**</td> + <td>write data on coverage to output file (must be used with -x). <br/><br/> **DocsSearched** <br/> Total number of documents in nodes searched <br/><br/> **NodesSearched** <br/> Total number of search nodes which were used <br/> <br/>**FullCoverage** <br/>1 if true, 0 if false</td> + </tr> + <tr> + <td>**-z**</td> + <td>Use single query file to be distributed between clients.</td> + </tr> + <tr> + <td>**-C *file***</td> + <td>Client certificate file name</td> + </tr> + <tr> + <td>**-K *file***</td> + <td>Client private key file name</td> + </tr> + <tr> + <td>**-D**</td> + <td>Use TLS configuration from environment if T/C/K is not used</td> + </tr> + </tbody> +</table> + +Default output: + +<table className="vespa-table"> + <thead> + <tr> + <th></th> + <th></th> + </tr> + </thead> + <tbody> + <tr> + <td>**connection reuse count**</td> + <td>Indicates how many times HTTP connections were reused to issue another request. Note that this number will only be displayed if the -k switch (enable HTTP keep-alive) is used.</td> + </tr> + <tr> + <td>**clients**</td> + <td>Echo of the -n parameter.</td> + </tr> + <tr> + <td>**cycle time**</td> + <td>Echo of the -c parameter.</td> + </tr> + <tr> + <td>**lower response limit**</td> + <td>Echo of the -l parameter.</td> + </tr> + <tr> + <td>**skipped requests**</td> + <td>Number of requests that was skipped by vespa-fbench. vespa-fbench will typically skip a request if the line containing the query url exceeds a pre-defined limit. Skipped requests will have minimal impact on the statistical results.</td> + </tr> + <tr> + <td>**failed requests**</td> + <td>The number of failed requests. A request will be marked as failed if en error occurred while reading the result or if the result contained fewer bytes than 'lower response limit'.</td> + </tr> + <tr> + <td>**successful requests**</td> + <td>Number of successful requests. Each performed request is counted as either successful or failed. Skipped requests (see above) are not performed and therefore not counted.</td> + </tr> + <tr> + <td>**cycles not held**</td> + <td>Number of cycles not held. The cycle time is specified with the -c parameter. It defines how often a client should perform a new request. However, a client may not perform another request before the result from the previous request has been obtained. Whenever a client is unable to initiate a new request 'on time' due to not being finished with the previous request, this value will be increased.</td> + </tr> + <tr> + <td>**minimum response time**</td> + <td>The minimum response time. The response time is measured as the time period from just before the request is sent to the server, till the result is obtained from the server.</td> + </tr> + <tr> + <td>**maximum response time**</td> + <td>The maximum response time. The response time is measured as the time period from just before the request is sent to the server, till the result is obtained from the server.</td> + </tr> + <tr> + <td>**average response time**</td> + <td>The average response time. The response time is measured as the time period from just before the request is sent to the server, till the result is obtained from the server.</td> + </tr> + <tr> + <td>**X percentile**</td> + <td>The X percentile of the response time samples; a value selected such that X percent of the response time samples are below this value. In order to calculate percentiles, a histogram of response times is maintained for each client at runtime and merged after the test run ends. If a percentile value exceeds the upper bound of this histogram, it will be approximated (and thus less accurate) and marked with '(approx)'.</td> + </tr> + <tr> + <td>**actual query rate**</td> + <td>The average number of queries per second; QPS.</td> + </tr> + <tr> + <td>**utilization**</td> + <td>The percentage of time used waiting for the server to complete (successful) requests. Note that if a request fails, the utilization will drop since the client has 'wasted' the time spent on the failed request.</td> + </tr> + <tr> + <td>**zero hit queries**</td> + <td>The number of queries that gave zero hits in Vespa</td> + </tr> + </tbody> +</table> + +## vespa-makefsa + +Use *vespa-makefsa* to compile a list of phrases into a *finite state automation* (FSA) file. FSA files are used in [query phrasing and rewriting](/en/linguistics/query-rewriting). + +If input file is not specified, standard input is used. + +Synopsis: `vespa-makefsa [-h] [-b] [-B] [-e] [-n] [-s bytes] [-z bytes] [-t] [-p] [-i] [-S serial] [-v] [-V] [input_file] output_file` + +<table className="vespa-table"> + <thead> + <tr> + <th>Option</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>**-h**</td> + <td>Help text</td> + </tr> + <tr> + <td>**-b**</td> + <td>Use binary input format with Base64 encoded info</td> + </tr> + <tr> + <td>**-B**</td> + <td>Use binary input format with raw</td> + </tr> + <tr> + <td>**-e**</td> + <td>Use text input format with no info (default)</td> + </tr> + <tr> + <td>**-s bytes**</td> + <td>Data size for numerical info: 1,2 or 4(default)</td> + </tr> + <tr> + <td>**-z bytes**</td> + <td>Data size for binary info (-B) (0 means NUL terminated)</td> + </tr> + <tr> + <td>**-t**</td> + <td>Use text input format</td> + </tr> + <tr> + <td>**-p**</td> + <td>Build automaton with perfect hash</td> + </tr> + <tr> + <td>**-i**</td> + <td>Ignore info string, regardless of input format</td> + </tr> + <tr> + <td>**-S serial**</td> + <td>Serial number</td> + </tr> + <tr> + <td>**-v**</td> + <td>Verbose</td> + </tr> + <tr> + <td>**-V**</td> + <td>Display version</td> + </tr> + </tbody> +</table> + +## vespa-query-profile-dump-tool + +Dumps all resolved query profile properties for a set of dimension values + +Synopsis:`vespa-query-profile-dump-tool dump [query-profile] [dir]? [parameters]?` + +Examples: + +```bash +dump default # dumps the 'default' profile non-variant values in the current dir + +dump default x=x1&y=y1 # dumps the 'default' profile resolved with dimensions values x=x1 and y=y1 in the current dir + +dump default myapppackage # dumps the 'default' profile non-variant values in myapppackage/search/query-profiles + +dump default dev/myprofiles x=x1&y=y1 # dumps the 'default' profile resolved with dimensions values x=x1 and y=y1 in dev/myprofiles +``` + +<table className="vespa-table"> + <thead> + <tr> + <th>Option</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>**query-profile**</td> + <td>Name of the query profile to dump the values of</td> + </tr> + <tr> + <td>**dir**</td> + <td>Path to an application package or query profile directory. Default: current dir</td> + </tr> + <tr> + <td>**parameters**</td> + <td>HTTP request encoded dimension keys used during resolving. Default: none</td> + </tr> + </tbody> +</table> \ No newline at end of file diff --git a/en/reference/querying/default-result-format.html b/en/reference/querying/default-result-format.html deleted file mode 100644 index 1f74cdca90..0000000000 --- a/en/reference/querying/default-result-format.html +++ /dev/null @@ -1,896 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Default JSON Result Format" -redirect_from: -- /en/reference/default-result-format.html ---- - -<p> - The default Vespa query response format is used when - <a href="../api/query.html#presentation.format">presentation.format</a> - is unset or set to <code>json</code>. - An alternative binary <a href="https://cbor.io/">CBOR</a> format is available by setting - <code>format=cbor</code> or using <code>Accept: application/cbor</code>. - CBOR is a drop-in replacement - when deserialized, the result is identical to JSON. - CBOR is both more compact and faster to generate, especially for numeric data such as tensors and embeddings. - Results are rendered with one or more objects: -</p> -<ul> - <li><code>root</code>: mandatory object with the tree of returned data</li> - <li><code>timing</code>: optional object with query timing information</li> - <li><code>trace</code>: optional object for metadata about query execution</li> -</ul> -<p> - Refer to the <a href="../../querying/query-api.html#result-examples">query API guide</a> for result and tracing examples. -</p> -<p> - All object names are literal strings, - the node <code>root</code> is the map key "root" in the return JSON object, - in other words, only strings are used as map keys. -</p> -<table class="table"> - <thead> - <tr> - <th>Element</th> - <th>Parent</th> - <th>Mandatory</th> - <th>Type</th> - <th>Description</th> - </tr> - </thead> - <tbody> - <tr> - <td colspan="5"><h2 id="root-header">root</h2></td> - </tr> - <tr> - <th>root</th> - <td></td> - <td>yes</td> - <td>Map of string to object</td> - <td><p id="root">The root of the tree of returned data.</p></td> - </tr> - <tr> - <th>children</th> - <td>root</td> - <td>no</td> - <td>Array of objects</td> - <td><p id="children">Array of JSON objects with the same structure as <code>root</code>.</p></td> - </tr> - <tr> - <th>fields</th> - <td>root</td> - <td>no</td> - <td>Map of string to object</td> - <td><p id="fields"></p></td> - </tr> - <tr> - <th>totalCount</th> - <td>fields</td> - <td>no</td> - <td>Integer</td> - <td> - <p id="totalcount">Number of documents matching the query. - Not accurate when using <em>nearestNeighbor</em>, - <em>wand</em> or <em>weakAnd</em> query operators.</p> - <p> - The value is the number of hits after - <a href="../schemas/schemas.html#rank-score-drop-limit">first-phase dropping</a>. - </p> - </td> - </tr> - <tr> - <th>searchGroup</th> - <td>fields</td> - <td>no</td> - <td>Integer</td> - <td> - <p id="searchGroups">The index of the group that produced this result, when informative and unique. - Useful for <a href="../../content/elasticity.html#pinning-groups">group pinning</a>. - </p> - </td> - </tr> - <tr> - <th>coverage</th> - <td>root</td> - <td>no</td> - <td>Map of string to string and number</td> - <td> - <p id="coverage-map"> - Map of metadata about how much of the total corpus has been searched to return the given documents. - </p> - </td> - </tr> - <tr> - <th>coverage</th> - <td>coverage</td> - <td>yes</td> - <td>Integer</td> - <td><p id="coverage">Percentage of total corpus searched (when lower than 100 this is an approximation and is a lower bound, as no info from nodes down is known)</p></td> - </tr> - <tr> - <th>documents</th> - <td>coverage</td> - <td>yes</td> - <td>Long</td> - <td><p id="documents">The number of active documents searched.</p></td> - </tr> - <tr> - <th>full</th> - <td>coverage</td> - <td>yes</td> - <td>Boolean</td> - <td><p id="full">Whether the full corpus was searched.</p></td> - </tr> - <tr> - <th>nodes</th> - <td>coverage</td> - <td>yes</td> - <td>Integer</td> - <td><p id="nodes">The number of search nodes returning results.</p></td> - </tr> - <tr> - <th>results</th> - <td>coverage</td> - <td>yes</td> - <td>Integer</td> - <td><p id="results">The number of results merged creating the final rendered result.</p></td> - </tr> - <tr> - <th>resultsFull</th> - <td>coverage</td> - <td>yes</td> - <td>Integer</td> - <td><p id="resultsfull">The number of full result sets merged, e.g. when there are several sources/clusters for the results.</p></td> - </tr> - <tr> - <th>degraded</th> - <td>coverage</td> - <td>no</td> - <td>Map of string to object</td> - <td><p id="degraded">Map of match-phase degradation elements.</p></td> - </tr> - <tr> - <th>match-phase</th> - <td>degraded</td> - <td>no</td> - <td>Boolean</td> - <td> - <p id="match-phase"> - Indicator whether <a href="../schemas/schemas.html#match-phase">match-phase degradation</a> has occurred. - </p> - </td> - </tr> - <tr> - <th>timeout</th> - <td>degraded</td> - <td>no</td> - <td>Boolean</td> - <td> - <p id="timeout"> - Indicator whether the query <a href="../api/query.html#timeout">timed out</a> before completion. - </p> - </td> - </tr> - <tr> - <th>adaptive-timeout</th> - <td>degraded</td> - <td>no</td> - <td>Boolean</td> - <td> - <p id="adaptive-timeout"> - Indicator whether the query timed out with - <a href="../api/query.html#ranking.softtimeout.enable">adaptive timeout</a> before completion. - </p> - </td> - </tr> - <tr> - <th>anntimeout</th> - <td>degraded</td> - <td>no</td> - <td>Boolean</td> - <td> - <p id="anntimeout"> - Indicator whether an ANN search timed out via the <a href="../api/query.html#ranking.matching.anntimeout.enable">ANN timeout</a> before completion. - </p> - </td> - </tr> - <tr> - <th>non-ideal-state</th> - <td>degraded</td> - <td>no</td> - <td>Boolean</td> - <td> - <p id="non-ideal-state"> - Indicator whether the content cluster is in <a href="../../content/idealstate.html">ideal state</a>. - </p> - </td> - </tr> - <tr> - <th>errors</th> - <td>root</td> - <td>no</td> - <td>Array of objects</td> - <td> - <p id="error"> - Array of error messages with the fields given below. - <a href="../../querying/query-api.html#error-result">Example</a>. - </p> - </td> - </tr> - <tr> - <th>code</th> - <td>errors</td> - <td>yes</td> - <td>Integer</td> - <td> - <p id="code"> - Numeric identifier used by the container application. - See <a href="https://github.com/vespa-engine/vespa/blob/master/container-disc/src/main/java/com/yahoo/container/protect/Error.java"> - error codes</a> and - <a href="https://github.com/vespa-engine/vespa/blob/master/container-search/src/main/java/com/yahoo/search/result/ErrorMessage.java"> - ErrorMessage.java</a> for a short description. - </p> - </td> - </tr> - <tr> - <th>message</th> - <td>errors</td> - <td>no</td> - <td>String</td> - <td><p id="message">Full error message.</p></td> - </tr> - <tr> - <th>source</th> - <td>errors</td> - <td>no</td> - <td>String</td> - <td><p id="error-source">Which <a href="../../querying/federation.html">data provider</a> logged the error condition.</p></td> - </tr> - <tr> - <th>stackTrace</th> - <td>errors</td> - <td>no</td> - <td>String</td> - <td><p id="stacktrace">Stack trace if an exception was involved.</p></td> - </tr> - <tr> - <th>summary</th> - <td>errors</td> - <td>yes</td> - <td>String</td> - <td><p id="summary">Short description of error.</p></td> - </tr> - <tr> - <th>transient</th> - <td>errors</td> - <td>no</td> - <td>Boolean</td> - <td> - <p id="transient"> - Whether the system is expected to recover from the faulty state on its own. - If the flag is not present, this may or may not be the case, or the flag is not applicable. - </p> - </td> - </tr> - <tr> - <th>fields</th> - <td>root</td> - <td>no</td> - <td>Map of string to object</td> - <td> - <p id="fields-map"> - The named document (schema) <a href="../schemas/schemas.html#field">fields</a>. - Fields without value are not rendered. - </p> - <p> - In addition to the fields defined in the schema, - the following might be returned: - </p> - <table> - <thead> - <tr> - <th>Fieldname</th> - <th>Description</th> - </tr> - </thead> - <tbody> - <tr> - <td>sddocname</td> - <td> - <p id="sddocname"> - Schema name. - Returned in the <a href="../../querying/document-summaries.html">default document summary</a>. - </p> - </td> - </tr> - <tr> - <td>documentid</td> - <td> - <p id="documentid"> - Document ID. - Returned in the <a href="../../querying/document-summaries.html">default document summary</a>. - </p> - </td> - </tr> - <tr> - <td>summaryfeatures</td> - <td> - <p id="summaryfeatures"> - Refer to <a href="../schemas/schemas.html#summary-features">summary-features</a> and - <a href="../../ranking/ranking-intro#observing-values-used-in-ranking">observing values used in ranking</a>. - </p> - </td> - </tr> - <tr> - <td>matchfeatures</td> - <td> - <p id="matchfeatures"> - Refer to <a href="../schemas/schemas.html#match-features">match-features</a> and - <a href="../../querying/nearest-neighbor-search-guide#strict-filters-and-distant-neighbors">example use</a>. - </p> - </td> - </tr> - </tbody> - </table> - </td> - </tr> - <tr> - <th>id</th> - <td>root</td> - <td>no</td> - <td>String</td> - <td> - <p id="id">String identifying the hit, document or other data type. - For document hits, this is the full string document ID if the hit is filled with a document summary - that includes the <code>documentid</code> field. - If it is not filled or only filled with a document summary without the <code>documentid</code> field, - it is an internally generated unique id on the form <code>index:[source]/[node-index]/[hex-gid]</code>.</p> - <p>See <a href="../../schemas/documents.html#docid-in-results">Document IDs in search results</a> - for how to ensure that the full string document ID (from memory) is returned.</p> - <p>For further information on the internally generated ids, - see the <a href="../../writing/document-v1-api-guide.html#troubleshooting">/document/v1/guide</a> - and also <a href="https://stackoverflow.com/questions/74033383/receiving-responses-of-different-formats-for-the-same-query-in-vespa"> - receiving-responses-of-different-formats-for-the-same-query-in-vespa</a> (outdated regarding document IDs being stored on disk only).</p> - </td> - </tr> - <tr> - <th>label</th> - <td>root</td> - <td>no</td> - <td>String</td> - <td><p id="label">The label of a grouping list.</p></td> - </tr> - <tr> - <th>limits</th> - <td>root</td> - <td>no</td> - <td>Object</td> - <td><p id="limits">Used in grouping, the limits of a bucket in histogram style data.</p></td> - </tr> - <tr> - <th>from</th> - <td>limits</td> - <td>no</td> - <td>String</td> - <td><p id="from">Lower bound of a bucket group.</p></td> - </tr> - <tr> - <th>to</th> - <td>limits</td> - <td>no</td> - <td>String</td> - <td><p id="to">Upper bound of a bucket group.</p></td> - </tr> - <tr> - <th>relevance</th> - <td>root</td> - <td>yes</td> - <td>Double</td> - <td> - <p id="relevance">Double value representing the rank score. - The rank score is returned from the <a href="../../ranking/ranking-intro.html">rank-profile</a>. - See the <a href="../../learn/faq.html#what-could-cause-the-relevance-field-to-be--infinity">FAQ</a> - for how to handle <code>"-Infinity"</code> (represented as string) values. - </p> - </td> - </tr> - <tr> - <th>source</th> - <td>root</td> - <td>no</td> - <td>String</td> - <td><p id="source">Which data provider created this node.</p></td> - </tr> - <tr> - <th>types</th> - <td>root</td> - <td>no</td> - <td>Array of string</td> - <td> - <p id="types">Metadata about what kind of document or other kind of node in the result set this object is.</p> - </td> - </tr> - <tr> - <th>value</th> - <td>root</td> - <td>no</td> - <td>String</td> - <td> - <p id="value">Used in grouping for value groups, the argument for the grouping data which is in the fields.</p> - </td> - </tr> - <tr> - <td colspan="5"></td> - </tr> - <tr> - <td colspan="5"><h2 id="timing-header">timing</h2></td> - </tr> - <tr> - <th>timing</th> - <td></td> - <td>no</td> - <td>Map of string to object</td> - <td> - <p id="timing"> - Query timing information, - enabled by <a href="../api/query.html#presentation.timing">presentation.timing</a>. - The <a href="../../performance/practical-search-performance-guide#basic-text-search-query-performance">query performance guide</a> - is a useful resource to understand the values in its child elements. - </p> - </td> - </tr> - <tr> - <th>querytime</th> - <td>timing</td> - <td>no</td> - <td>Double</td> - <td><p id="querytime">Time to execute the first protocol phase/matching phase, in seconds.</p></td> - </tr> - <tr> - <th>summaryfetchtime</th> - <td>timing</td> - <td>no</td> - <td>Double</td> - <td> - <p id="summaryfetchtime"> - <a href="../../querying/document-summaries.html">Document summary</a> fetch time, in seconds. - This is the time to execute the summary fill protocol phase for the globally ordered top-k hits. - </p> - </td> - </tr> - <tr> - <th>searchtime</th> - <td>timing</td> - <td>no</td> - <td>Double</td> - <td> - <p id="searchtime"> - Approximately the sum of <code>querytime</code> and <code>summaryfetchtime</code> - and is close to what a client will observe (except network latency). - In seconds. - </p> - </td> - </tr> - <tr> - <td colspan="5"></td> - </tr> - <tr> - <td colspan="5"><h2 id="trace-heading">trace</h2> - {% include note.html content="The tracing elements below is a subset of all elements. - Refer to the <a href='../../performance/practical-search-performance-guide#advanced-query-tracing'> - search performance guide</a> for examples." %}</td> - </tr> - <tr> - <th>trace</th> - <td></td> - <td>no</td> - <td>Map of string to object</td> - <td><p id="trace">Metadata about query execution.</p></td> - </tr> - <tr> - <th>children</th> - <td>trace</td> - <td>no</td> - <td>Array of object</td> - <td><p id="children-trace">Array of maps with exactly the same structure as <code>trace</code> itself.</p></td> - </tr> - <tr> - <th>timestamp</th> - <td>children</td> - <td>no</td> - <td>Long</td> - <td> - <p id="timestamp">Number of milliseconds since the start of query execution this node was added to the trace.</p> - </td> - </tr> - <tr> - <th>message</th> - <td>children</td> - <td>no</td> - <td>String</td> - <td><p id="message-trace">Descriptive trace text regarding this step of query execution.</p></td> - </tr> - <tr> - <th>message</th> - <td>children</td> - <td>no</td> - <td>Array of objects</td> - <td><p id="message-trace-map">Array of messages</p></td> - </tr> - <tr> - <th>start_time</th> - <td>message</td> - <td>no</td> - <td>String</td> - <td><p id="start-time">Timestamp, e.g. 2022-07-27 09:51:21.938 UTC</p></td><!-- ToDo: why not an ISO 8601 string --> - </tr> - <tr> - <th>traces</th> - <td>message or threads</td> - <td>no</td> - <td>Array of traces or objects</td> - <td><p id="traces"></p></td> - </tr> - <tr> - <th>distribution-key</th> - <td>message</td> - <td>no</td> - <td>Integer</td> - <td> - <p id="distribution-key"> - The <a href="../applications/services/content.html#node">distribution key</a> of the content node creating this span. - </p> - </td> - </tr> - <tr> - <th>duration_ms</th> - <td>message</td> - <td>no</td> - <td>float</td> - <td><p id="duration-ms">duration of span</p></td> - </tr> - <tr> - <th>timestamp_ms</th> - <td>traces</td> - <td>no</td> - <td>float</td> - <td><p id="timestamp-ms">time since start of parent, see <code>start_time</code>.</p></td> - </tr> - <tr> - <th>event</th> - <td>traces</td> - <td>no</td> - <td>String</td> - <td><p id="event">Description of span</p></td> - </tr> - <tr> - <th>tag</th> - <td>traces</td> - <td>no</td> - <td>String</td> - <td><p id="tag">Name of span</p></td> - </tr> - <tr> - <th>threads</th> - <td>traces</td> - <td>no</td> - <td>Array of objects</td> - <td><p id="threads">Array of object that again has traces elements.</p></td> - </tr> - </tbody> -</table> - - - -<h2 id="json-schema">JSON Schema</h2> -<p>Formal schema for the query API default result format:</p> -<pre>{% highlight json %} -{ - "$schema": "http://json-schema.org/draft-04/schema#", - "title": "Result", - "description": "Schema for Vespa results", - "type": "object", - - - "properties": { - "root": { - "type": "document_node", - "required": true - }, - "trace": { - "type": "trace_node", - "required": false - } - }, - - "definitions": { - "document_node": { - "properties": { - "children": { - "type": "array", - "items": { - "type": "document_node" - }, - "required": false - }, - "coverage": { - "type": "coverage", - "required": false - }, - "errors": { - "type": "array", - "items": { - "type": "error" - }, - "required": false - }, - "fields": { - "type": "object", - "additionalProperties": true, - "required": false - }, - "id": { - "type": "string", - "required": false - }, - "relevance": { - "type": "number", - "required": true - }, - "types": { - "type": "array", - "items": { - "type": "string" - }, - "required": false - }, - "source": { - "type": "string", - "required": false - }, - "value": { - "type": "string", - "required": false - }, - "limits": { - "type": "object", - "required": false - }, - "label": { - "type": "string", - "required": false - } - }, - "additionalProperties": true, - }, - "trace_node": { - "properties": { - "children": { - "type": "array", - "items": { - "type": "trace_node" - }, - "required": false - }, - "timestamp": { - "type": "number", - "required": false - }, - "message": { - "type": "string", - "required": false - } - } - }, - "fields": { - "properties": { - "totalCount": { - "type": "number", - "required": true - } - } - }, - "coverage": { - "properties": { - "coverage": { - "type": "number", - "required": true - }, - "documents": { - "type": "number", - "required": true - }, - "full": { - "type": "boolean", - "required": true - }, - "nodes": { - "type": "number", - "required": true - }, - "results": { - "type": "number", - "required": true - }, - "resultsFull": { - "type": "number", - "required": true - } - } - }, - "error": { - "properties": { - "code": { - "type": "number", - "required": true - }, - "message": { - "type": "string", - "required": false - }, - "source": { - "type": "string", - "required": false - }, - "stackTrace": { - "type": "string", - "required": false - }, - "summary": { - "type": "string", - "required": true - }, - "transient": { - "type": "boolean", - "required": false - } - } - } - } -} -{% endhighlight %}</pre> - - -<h2 id="appendix-legacy-vespa-7-json-rendering">Appendix: Legacy Vespa 7 JSON rendering</h2> -<p> - There were some inconsistencies between search results and document rendering in Vespa 7, which are fixed in Vespa 8. - This appendix describes the old behavior, what the changes are, - and how to configure to select a specific rendering. -</p> - - -<h3 id="inconsistent-weightedset-rendering">Inconsistent weightedset rendering</h3> -<p> - Fields with various weightedset types has a JSON input representation (for feeding) as a JSON object; - for example <code>{"one":1, "two":2,"three":3}</code> for - the value of a a <code>weightedset<string></code> field. - The same format is used when rendering a document (for example when visiting). -</p> -<p> - In search results however, there are intermediate processing steps during which the - field value is represented as an array of item/weight pairs, - so in a search result the field value would render as - <code>[ {"item":"one", "weight":1}, - {"item":"two", "weight":2}, - {"item":"three", "weight":3} ]</code> -</p> -<p> - In Vespa 8, the default JSON renderer for search results outputs the same format - as document rendering. If you have code that depends on the old format you can - turn off this by setting <code>renderer.json.jsonWsets=false</code> - in the query (usually via a <a href="../../querying/query-profiles.html">query profile</a>). -</p> - - -<h3 id="inconsistent-map-rendering">Inconsistent map rendering</h3> -<p> - Fields with various map types has a JSON input representation (for feeding) as a JSON object; - for example <code>{"1001":1.0, "1002":2.0, "1003":3.0}</code> for - the value of a a <code>map<int,float></code> field. - The same format is used when rendering a document (for example when visiting). -</p> -<p> - In search results however, there are intermediate processing steps and the field value - is represented as an array of key/value pairs, - so in a search results the field value would (in some cases) render as - <code>[ {"key":1001, "value":1.0}, - {"key":1002, "value":2.0}, - {"key":1003, "value":3.0} ]</code> -</p> -<p> - In Vespa 8, the default JSON renderer for search results output the same format as document rendering. - For code that depends on the old format one can - turn off this by setting <code>renderer.json.jsonMaps=false</code> - in the query (usually via a <a href="../../querying/query-profiles.html">query profile</a>). -</p> - - -<h3 id="geo-position-rendering">Geo position rendering</h3> -<p> - Fields with the type <code>position</code> would in Vespa 7 be rendered using the internal fields "x" and "y". - These are integers representing microdegrees, - aka geographical degrees times 1 million, of longitude (for x) and latitude (for y). - Also, any field <em>foo</em> of type <code>position</code> would trigger addition of - two extra synthetic summary fields <em>foo.position</em> and <em>foo.distance</em> (see below for details). -</p> -<p> - In Vespa 8, positions are rendered with two JSON fields "lat" and "lng", - both having a floating-point value. - The "lat" field is latitude (going from -90.0 at the South Pole to +90.0 at the North Pole). - The "lng" field is longitude (going from -180.0 at the dateline seen as extreme west, - via 0.0 at the Greenwich meridian, to +180.0 at the dateline again, now as extreme east). - The field names are chosen so the format is the same as used in the Google "places" API. -</p> -<p> - A closely related change is the removal of two synthetic summary fields - which would be returned in search results. For example with this in schema: -</p> -<pre> -field mainloc type position { - indexing: attribute | summary -} -</pre> -<p> - Vespa 7 would include the <em>mainloc</em> summary field, - but also <em>mainloc.position</em> and <em>mainloc.distance</em>; - the latter only when the query actually had a position to take the distance from. -</p> -<p> - The first of these (<em>mainloc.position</em> in this case) was - mainly useful for producing XML output in older Vespa versions, - and now contains just the same information as the <em>mainloc</em> summary field. - The second (<em>mainloc.distance</em> in this case) would return a distance in internal units, - and can be replaced by a summary feature - - here <code>distance(mainloc)</code> would give the same number, - while <code>distance(mainloc).km</code> would be the recommended replacement with suitable code changes. -<p> - - -<h3 id="summary-features-wrapped-in-rankingexpression">Summary-features wrapped in "rankingExpression"</h3> -<p> - In Vespa 7, if a rank profile wanted a function - <code>foobar</code> returned in summary-features (or match-features), - it would be rendered as <code>rankingExpression(foobar)</code> in the output. -</p> -<p> - For programmatic use, the <code>FeatureData</code> class has extra checking to allow lookup with - <code>getDouble("foobar")</code> or - <code>getTensor("foobar")</code>, - but now it's present and rendered with just the original name as specified. -</p> -<p> - If applications needs the JSON rendering to look exactly as in Vespa 7, one can specify that in the rank profile. - For example, with this in the schema: -</p> -<pre> -rank-profile whatever { - function lengthScore() { expression: matchCount(title)/fieldLength(title) } - summary-features { - matchCount(title) - lengthScore - ... -</pre> -<p>could, in Vespa 7, yield JSON output containing:</p> -<pre> - summaryfeatures: { - matchCount(title): 1, - rankingExpression(lengthScore): 0.25, - ... -</pre> -<p>in Vespa 8, you instead get the expected:</p> -<pre> - summaryfeatures: { - matchCount(title): 1, - lengthScore: 0.25, - ... -</pre> -<p>But to get the old behavior one can specify:</p> -<pre> -rank-profile whatever { - function lengthScore() { expression: matchCount(title)/fieldLength(title) } - summary-features { - matchCount(title) - rankingExpression(lengthScore) - ... -</pre> -<p>which gives you the same output as before.</p> diff --git a/en/reference/querying/default-result-format.mdx b/en/reference/querying/default-result-format.mdx new file mode 100644 index 0000000000..512cf42e9e --- /dev/null +++ b/en/reference/querying/default-result-format.mdx @@ -0,0 +1,680 @@ +--- +title: "Default JSON Result Format" +sidebarTitle: "The default result format" +--- +The default Vespa query response format is used when [presentation.format](/en/reference/api/query#presentation.format) is unset or set to `json`. An alternative binary [CBOR](https://cbor.io/) format is available by setting `format=cbor` or using `Accept: application/cbor`. CBOR is a drop-in replacement - when deserialized, the result is identical to JSON. CBOR is both more compact and faster to generate, especially for numeric data such as tensors and embeddings. Results are rendered with one or more objects: + +- `root`: mandatory object with the tree of returned data +- `timing`: optional object with query timing information +- `trace`: optional object for metadata about query execution + +Refer to the [query API guide](/en/querying/query-api#result-examples) for result and tracing examples. + +All object names are literal strings, the node `root` is the map key "root" in the return JSON object, in other words, only strings are used as map keys. + +## root + +<table className="vespa-table"> + <thead> + <tr> + <th>Element</th> + <th>Parent</th> + <th>Mandatory</th> + <th>Type</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>**root**</td> + <td></td> + <td>yes</td> + <td>Map of string to object</td> + <td>The root of the tree of returned data.</td> + </tr> + <tr> + <td>**children**</td> + <td>root</td> + <td>no</td> + <td>Array of objects</td> + <td>Array of JSON objects with the same structure as <code>{`root`}</code>.</td> + </tr> + <tr> + <td>**fields**</td> + <td>root</td> + <td>no</td> + <td>Map of string to object</td> + <td></td> + </tr> + <tr> + <td>**totalCount**</td> + <td>fields</td> + <td>no</td> + <td>Integer</td> + <td>Number of documents matching the query. Not accurate when using *nearestNeighbor*, *wand* or *weakAnd* query operators. The value is the number of hits after <a href="/en/reference/schemas/schemas#rank-score-drop-limit" aria-label="first-phase dropping">first-phase dropping</a>.</td> + </tr> + <tr> + <td>**searchGroup**</td> + <td>fields</td> + <td>no</td> + <td>Integer</td> + <td>The index of the group that produced this result, when informative and unique. Useful for <a href="/en/content/elasticity#pinning-groups" aria-label="group pinning">group pinning</a>.</td> + </tr> + <tr> + <td>**coverage**</td> + <td>root</td> + <td>no</td> + <td>Map of string to string/number</td> + <td>Map of metadata about how much of the total corpus has been searched to return the given documents.</td> + </tr> + <tr> + <td>**coverage**</td> + <td>coverage</td> + <td>yes</td> + <td>Integer</td> + <td>Percentage of total corpus searched (when lower than 100 this is an approximation and is a lower bound, as no info from nodes down is known)</td> + </tr> + <tr> + <td>**documents**</td> + <td>coverage</td> + <td>yes</td> + <td>Long</td> + <td>The number of active documents searched.</td> + </tr> + <tr> + <td>**full**</td> + <td>coverage</td> + <td>yes</td> + <td>Boolean</td> + <td>Whether the full corpus was searched.</td> + </tr> + <tr> + <td>**nodes**</td> + <td>coverage</td> + <td>yes</td> + <td>Integer</td> + <td>The number of search nodes returning results.</td> + </tr> + <tr> + <td>**results**</td> + <td>coverage</td> + <td>yes</td> + <td>Integer</td> + <td>The number of results merged creating the final rendered result.</td> + </tr> + <tr> + <td>**resultsFull**</td> + <td>coverage</td> + <td>yes</td> + <td>Integer</td> + <td>The number of full result sets merged, e.g. when there are several sources/clusters for the results.</td> + </tr> + <tr> + <td>**degraded**</td> + <td>coverage</td> + <td>no</td> + <td>Map of string to object</td> + <td>Map of match-phase degradation elements.</td> + </tr> + <tr> + <td>**match-phase**</td> + <td>degraded</td> + <td>no</td> + <td>Boolean</td> + <td>Indicator whether <a href="/en/reference/schemas/schemas#match-phase" aria-label="match-phase degradation">match-phase degradation</a> has occurred.</td> + </tr> + <tr> + <td>**timeout**</td> + <td>degraded</td> + <td>no</td> + <td>Boolean</td> + <td>Indicator whether the query <a href="/en/reference/api/query#timeout" aria-label="timed out">timed out</a> before completion.</td> + </tr> + <tr> + <td>**adaptive-timeout**</td> + <td>degraded</td> + <td>no</td> + <td>Boolean</td> + <td>Indicator whether the query timed out with <a href="/en/reference/api/query#ranking.softtimeout.enable" aria-label="adaptive timeout">adaptive timeout</a> before completion.</td> + </tr> + <tr> + <td>**non-ideal-state**</td> + <td>degraded</td> + <td>no</td> + <td>Boolean</td> + <td>Indicator whether the content cluster is in <a href="/en/content/idealstate" aria-label="ideal state">ideal state</a>.</td> + </tr> + <tr> + <td>**errors**</td> + <td>root</td> + <td>no</td> + <td>Array of objects</td> + <td>Array of error messages with the fields given below. <a href="/en/querying/query-api#error-result" aria-label="Example">Example</a>.</td> + </tr> + <tr> + <td>**code**</td> + <td>errors</td> + <td>yes</td> + <td>Integer</td> + <td>Numeric identifier used by the container application. See <a href="https://github.com/vespa-engine/vespa/blob/master/container-disc/src/main/java/com/yahoo/container/protect/Error.java" aria-label="error codes">error codes</a> and <a href="https://github.com/vespa-engine/vespa/blob/master/container-search/src/main/java/com/yahoo/search/result/ErrorMessage.java" aria-label="ErrorMessage.java">ErrorMessage.java</a> for a short description.</td> + </tr> + <tr> + <td>**message**</td> + <td>errors</td> + <td>no</td> + <td>String</td> + <td>Full error message.</td> + </tr> + <tr> + <td>**source**</td> + <td>errors</td> + <td>no</td> + <td>String</td> + <td>Which <a href="/en/querying/federation" aria-label="data provider">data provider</a> logged the error condition.</td> + </tr> + <tr> + <td>**stackTrace**</td> + <td>errors</td> + <td>no</td> + <td>String</td> + <td>Stack trace if an exception was involved.</td> + </tr> + <tr> + <td>**summary**</td> + <td>errors</td> + <td>yes</td> + <td>String</td> + <td>Short description of error.</td> + </tr> + <tr> + <td>**transient**</td> + <td>errors</td> + <td>no</td> + <td>Boolean</td> + <td>Whether the system is expected to recover from the faulty state on its own. If the flag is not present, this may or may not be the case, or the flag is not applicable.</td> + </tr> + <tr> + <td>**fields**</td> + <td>root</td> + <td>no</td> + <td>Map of string to object</td> + <td>The named document (schema) <a href="/en/reference/schemas/schemas#field" aria-label="fields">fields</a>. Fields without value are not rendered.<br/><br/>In addition to the fields defined in the schema, the following might be returned:<br/><br/> <ul><li><b>sddocname</b>: Schema name. Returned in the <a href="/en/querying/document-summaries" aria-label="default document summary">default document summary</a>.</li><li><b>documentid</b>: Document ID. Returned in the <a href="/en/querying/document-summaries" aria-label="default document summary">default document summary</a>.</li><li><b>summaryfeatures</b>: Refer to <a href="/en/reference/schemas/schemas#summary-features" aria-label="summary-features">summary-features</a> and <a href="/en/ranking/ranking-intro#observing-values-used-in-ranking" aria-label="observing values used in ranking">observing values used in ranking</a>.</li><li><b>matchfeatures</b>: Refer to <a href="/en/reference/schemas/schemas#match-features" aria-label="match-features">match-features</a> and <a href="/en/querying/nearest-neighbor-search-guide#strict-filters-and-distant-neighbors" aria-label="example use">example use</a>.</li></ul></td> + </tr> + <tr> + <td>**id**</td> + <td>root</td> + <td>no</td> + <td>String</td> + <td>String identifying the hit, document or other data type. For document hits, this is the full string document ID if the hit is filled with a document summary that includes the <code>{`documentid`}</code> field. If it is not filled or only filled with a document summary without the <code>{`documentid`}</code> field, it is an internally generated unique id on the form <code>{`index:[source]/[node-index]/[hex-gid]`}</code>.<br/><br/>See <a href="/en//schemas/documents#docid-in-results" aria-label="Document IDs in search results">Document IDs in search results</a> for how to ensure that the full string document ID (from memory) is returned.<br/><br/>For further information on the internally generated ids, see the <a href="/en/writing/document-v1-api-guide#troubleshooting" aria-label="/document/v1/guide">/document/v1/guide</a> and also <a href="https://stackoverflow.com/questions/74033383/receiving-responses-of-different-formats-for-the-same-query-in-vespa" aria-label="receiving-responses-of-different-formats-for-the-same-query-in-vespa">receiving-responses-of-different-formats-for-the-same-query-in-vespa</a> (outdated regarding document IDs being stored on disk only).</td> + </tr> + <tr> + <td>**label**</td> + <td>root</td> + <td>no</td> + <td>String</td> + <td>The label of a grouping list.</td> + </tr> + <tr> + <td>**limits**</td> + <td>root</td> + <td>no</td> + <td>Object</td> + <td>Used in grouping, the limits of a bucket in histogram style data.</td> + </tr> + <tr> + <td>**from**</td> + <td>limits</td> + <td>no</td> + <td>String</td> + <td>Lower bound of a bucket group.</td> + </tr> + <tr> + <td>**to**</td> + <td>limits</td> + <td>no</td> + <td>String</td> + <td>Upper bound of a bucket group.</td> + </tr> + <tr> + <td>**relevance**</td> + <td>root</td> + <td>yes</td> + <td>Double</td> + <td>Double value representing the rank score. The rank score is returned from the <a href="/en/ranking/ranking-intro" aria-label="rank-profile">rank-profile</a>. See the <a href="/en/learn/faq#what-could-cause-the-relevance-field-to-be--infinity" aria-label="FAQ">FAQ</a> for how to handle <code>"-Infinity"</code> (represented as string) values.</td> + </tr> + <tr> + <td>**source**</td> + <td>root</td> + <td>no</td> + <td>String</td> + <td>Which data provider created this node.</td> + </tr> + <tr> + <td>**types**</td> + <td>root</td> + <td>no</td> + <td>Array of string</td> + <td>Metadata about what kind of document or other kind of node in the result set this object is.</td> + </tr> + <tr> + <td>**value**</td> + <td>root</td> + <td>no</td> + <td>String</td> + <td>Used in grouping for value groups, the argument for the grouping data which is in the fields.</td> + </tr> + </tbody> +</table> + + +## timing + +<table className="vespa-table"> + <thead> + <tr> + <th>Element</th> + <th>Parent</th> + <th>Mandatory</th> + <th>Type</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>**timing**</td> + <td></td> + <td>no</td> + <td>Map of string to object</td> + <td>Query timing information, enabled by <a href="/en/reference/api/query#presentation.timing" aria-label="presentation.timing">presentation.timing</a>. The <a href="/en/performance/practical-search-performance-guide#basic-text-search-query-performance" aria-label="query performance guide">query performance guide</a> is a useful resource to understand the values in its child elements.</td> + </tr> + <tr> + <td>**querytime**</td> + <td>timing</td> + <td>no</td> + <td>Double</td> + <td>Time to execute the first protocol phase/matching phase, in seconds.</td> + </tr> + <tr> + <td>**summaryfetchtime**</td> + <td>timing</td> + <td>no</td> + <td>Double</td> + <td><a href="/en/querying/document-summaries" aria-label="Document summary">Document summary</a> fetch time, in seconds. This is the time to execute the summary fill protocol phase for the globally ordered top-k hits.</td> + </tr> + <tr> + <td>**searchtime**</td> + <td>timing</td> + <td>no</td> + <td>Double</td> + <td>Approximately the sum of <code>{`querytime`}</code> and <code>{`summaryfetchtime`}</code> and is close to what a client will observe (except network latency). In seconds.</td> + </tr> + </tbody> +</table> + +## trace + +<Info> +**Note:** The tracing elements below is a subset of all elements. Refer to the [search performance guide](/en/performance/practical-search-performance-guide#advanced-query-tracing) for examples. +</Info> + +<table className="vespa-table"> + <thead> + <tr> + <th>Element</th> + <th>Parent</th> + <th>Mandatory</th> + <th>Type</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>**trace**</td> + <td></td> + <td>no</td> + <td>Map of string to object</td> + <td>Metadata about query execution.<br/><br/>**Note:** The tracing elements below are a subset of all elements. Refer to the <a href="/en/performance/practical-search-performance-guide#advanced-query-tracing" aria-label="search performance guide">search performance guide</a> for examples.</td> + </tr> + <tr> + <td>**children**</td> + <td>trace</td> + <td>no</td> + <td>Array of object</td> + <td>Array of maps with exactly the same structure as <code>{`trace`}</code> itself.</td> + </tr> + <tr> + <td>**timestamp**</td> + <td>children</td> + <td>no</td> + <td>Long</td> + <td>Number of milliseconds since the start of query execution this node was added to the trace.</td> + </tr> + <tr> + <td>**message**</td> + <td>children</td> + <td>no</td> + <td>String</td> + <td>Descriptive trace text regarding this step of query execution.</td> + </tr> + <tr> + <td>**message**</td> + <td>children</td> + <td>no</td> + <td>Array of objects</td> + <td>Array of messages.</td> + </tr> + <tr> + <td>**start_time**</td> + <td>message</td> + <td>no</td> + <td>String</td> + <td>Timestamp, e.g. 2022-07-27 09:51:21.938 UTC</td> + </tr> + <tr> + <td>**traces**</td> + <td>message or threads</td> + <td>no</td> + <td>Array of traces or objects</td> + <td></td> + </tr> + <tr> + <td>**distribution-key**</td> + <td>message</td> + <td>no</td> + <td>Integer</td> + <td>The <a href="/en/reference/applications/services/content#node" aria-label="distribution key">distribution key</a> of the content node creating this span.</td> + </tr> + <tr> + <td>**duration_ms**</td> + <td>message</td> + <td>no</td> + <td>Float</td> + <td>Duration of span.</td> + </tr> + <tr> + <td>**timestamp_ms**</td> + <td>traces</td> + <td>no</td> + <td>Float</td> + <td>Time since start of parent, see <code>{`start_time`}</code>.</td> + </tr> + <tr> + <td>**event**</td> + <td>traces</td> + <td>no</td> + <td>String</td> + <td>Description of span.</td> + </tr> + <tr> + <td>**tag**</td> + <td>traces</td> + <td>no</td> + <td>String</td> + <td>Name of span.</td> + </tr> + <tr> + <td>**threads**</td> + <td>traces</td> + <td>no</td> + <td>Array of objects</td> + <td>Array of objects that again have <code>{`traces`}</code> elements.</td> + </tr> + </tbody> +</table> + + +## JSON Schema + +Formal schema for the query API default result format: + +```json expandable +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "Result", + "description": "Schema for Vespa results", + "type": "object", + + + "properties": { + "root": { + "type": "document_node", + "required": true + }, + "trace": { + "type": "trace_node", + "required": false + } + }, + + "definitions": { + "document_node": { + "properties": { + "children": { + "type": "array", + "items": { + "type": "document_node" + }, + "required": false + }, + "coverage": { + "type": "coverage", + "required": false + }, + "errors": { + "type": "array", + "items": { + "type": "error" + }, + "required": false + }, + "fields": { + "type": "object", + "additionalProperties": true, + "required": false + }, + "id": { + "type": "string", + "required": false + }, + "relevance": { + "type": "number", + "required": true + }, + "types": { + "type": "array", + "items": { + "type": "string" + }, + "required": false + }, + "source": { + "type": "string", + "required": false + }, + "value": { + "type": "string", + "required": false + }, + "limits": { + "type": "object", + "required": false + }, + "label": { + "type": "string", + "required": false + } + }, + "additionalProperties": true, + }, + "trace_node": { + "properties": { + "children": { + "type": "array", + "items": { + "type": "trace_node" + }, + "required": false + }, + "timestamp": { + "type": "number", + "required": false + }, + "message": { + "type": "string", + "required": false + } + } + }, + "fields": { + "properties": { + "totalCount": { + "type": "number", + "required": true + } + } + }, + "coverage": { + "properties": { + "coverage": { + "type": "number", + "required": true + }, + "documents": { + "type": "number", + "required": true + }, + "full": { + "type": "boolean", + "required": true + }, + "nodes": { + "type": "number", + "required": true + }, + "results": { + "type": "number", + "required": true + }, + "resultsFull": { + "type": "number", + "required": true + } + } + }, + "error": { + "properties": { + "code": { + "type": "number", + "required": true + }, + "message": { + "type": "string", + "required": false + }, + "source": { + "type": "string", + "required": false + }, + "stackTrace": { + "type": "string", + "required": false + }, + "summary": { + "type": "string", + "required": true + }, + "transient": { + "type": "boolean", + "required": false + } + } + } + } +} +``` + +## Appendix: Legacy Vespa 7 JSON rendering + +There were some inconsistencies between search results and document rendering in Vespa 7, which are fixed in Vespa 8. This appendix describes the old behavior, what the changes are, and how to configure to select a specific rendering. + +### Inconsistent weightedset rendering + +Fields with various weightedset types has a JSON input representation (for feeding) as a JSON object; for example `{"one":1, "two":2,"three":3}` for the value of a a `weightedset<string>` field. The same format is used when rendering a document (for example when visiting). + +In search results however, there are intermediate processing steps during which the field value is represented as an array of item/weight pairs, so in a search result the field value would render as `[ {"item":"one", "weight":1},{"item":"two", "weight":2},{"item":"three", "weight":3} ]` + +In Vespa 8, the default JSON renderer for search results outputs the same format as document rendering. If you have code that depends on the old format you can turn off this by setting `renderer.json.jsonWsets=false` in the query (usually via a [query profile](/en/querying/query-profiles)). + +### Inconsistent map rendering + +Fields with various map types has a JSON input representation (for feeding) as a JSON object; for example `{"1001":1.0, "1002":2.0, "1003":3.0}` for the value of a a `map<int,float>` field. The same format is used when rendering a document (for example when visiting). + +In search results however, there are intermediate processing steps and the field value is represented as an array of key/value pairs, so in a search results the field value would (in some cases) render as `[ {"key":1001, "value":1.0},{"key":1002, "value":2.0},{"key":1003, "value":3.0} ]` + +In Vespa 8, the default JSON renderer for search results output the same format as document rendering. For code that depends on the old format one can turn off this by setting `renderer.json.jsonMaps=false` in the query (usually via a [query profile](/en/querying/query-profiles)). + +### Geo position rendering + +Fields with the type `position` would in Vespa 7 be rendered using the internal fields "x" and "y". These are integers representing microdegrees, aka geographical degrees times 1 million, of longitude (for x) and latitude (for y). Also, any field _foo_ of type `position` would trigger addition of two extra synthetic summary fields _foo.position_ and _foo.distance_ (see below for details). + +In Vespa 8, positions are rendered with two JSON fields "lat" and "lng", both having a floating-point value. The "lat" field is latitude (going from -90.0 at the South Pole to +90.0 at the North Pole). The "lng" field is longitude (going from -180.0 at the dateline seen as extreme west, via 0.0 at the Greenwich meridian, to +180.0 at the dateline again, now as extreme east). The field names are chosen so the format is the same as used in the Google "places" API. + +A closely related change is the removal of two synthetic summary fields which would be returned in search results. For example with this in schema: + +```js +field mainloc type position { + indexing: attribute | summary +} +``` + +Vespa 7 would include the _mainloc_ summary field, but also _mainloc.position_ and _mainloc.distance_; the latter only when the query actually had a position to take the distance from. + +The first of these (_mainloc.position_ in this case) was mainly useful for producing XML output in older Vespa versions, and now contains just the same information as the _mainloc_ summary field. The second (_mainloc.distance_ in this case) would return a distance in internal units, and can be replaced by a summary feature - here `distance(mainloc)` would give the same number, while `distance(mainloc).km` would be the recommended replacement with suitable code changes. + +### Summary-features wrapped in "rankingExpression" + +In Vespa 7, if a rank profile wanted a function `foobar` returned in summary-features (or match-features), it would be rendered as `rankingExpression(foobar)` in the output. + +For programmatic use, the `FeatureData` class has extra checking to allow lookup with `getDouble("foobar")` or `getTensor("foobar")`, but now it's present and rendered with just the original name as specified. + +If applications needs the JSON rendering to look exactly as in Vespa 7, one can specify that in the rank profile. For example, with this in the schema: + +```js +rank-profile whatever { + function lengthScore() { expression: matchCount(title)/fieldLength(title) } + summary-features { + matchCount(title) + lengthScore + ... +``` + +could, in Vespa 7, yield JSON output containing: + +```json +summaryfeatures: { + matchCount(title): 1, + rankingExpression(lengthScore): 0.25, + ... +``` + +in Vespa 8, you instead get the expected: + +```json +summaryfeatures: { + matchCount(title): 1, + lengthScore: 0.25, + ... +``` + +But to get the old behavior one can specify: + +```js +rank-profile whatever { + function lengthScore() { expression: matchCount(title)/fieldLength(title) } + summary-features { + matchCount(title) + rankingExpression(lengthScore) + ... +``` + +which gives you the same output as before. diff --git a/en/reference/querying/grouping-language.html b/en/reference/querying/grouping-language.html deleted file mode 100644 index 8a8fd3c193..0000000000 --- a/en/reference/querying/grouping-language.html +++ /dev/null @@ -1,682 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Grouping Reference" -redirect_from: -- /en/reference/grouping.html -- /en/reference/grouping-syntax.html ---- - - -<p>Read the <a href="/en/querying/grouping.html">Vespa grouping guide</a> first, for examples and an introduction to grouping - -this is the Vespa grouping reference.</p> - -<p>Also note that using a <a href="/en/searching-multi-valued-fields.html">multivalued</a> attribute -(such as an array of doubles) in a grouping expression affects performance. -Such operations can hit a memory bandwidth bottleneck, -particularly if the set of hits to be processed is large, as more data is evaluated.</p> - - - -<h2 id="group">Group</h2> - -<p>Group query results using a custom expression (using the <code>group</code> clause):</p> - -<ul> - <li>A numerical or string constant (e.g., <code>group(1)</code> or <code>group("all")</code>) which makes one bucket with everything</li> - <li>A document <a href="../../content/attributes.html">attribute</a></li> - <li> - A function over another expression (<code>xorbit</code>, <code>md5</code>, <code>cat</code>, - <code>xor</code>, <code>and</code>, <code>or</code>, <code>add</code>, <code>sub</code>, - <code>mul</code>, <code>div</code>, <code>mod</code>) or any other <a href="#expressions">expression</a> - </li><li> - The datatype of an expression is resolved using best-effort, similarly to how common - programming languages do to resolve arithmetic of different data-typed operands - </li><li> - The results of any expression are either scalar or single-dimensional arrays - <ul> - <li><code>add(<array>)</code> adds all elements together to produce a scalar</li> - <li> - <code>add(<arrayA>, <arrayB>)</code> adds each element together producing a new - array whose size is <code>max(|<arrayA>|, |<arrayB>|)</code> - </li> - </ul> - </li> -</ul> - -<p>Groups can contain subgroups (by using <code>each</code> and <code>group</code> operations), -and may be nested to any level.</p> - -<p>Multiple sub-groupings or outputs can be created under the same group level, using multiple parallel <code>each</code> -or <code>all</code> clauses, and each one may be labelled using <a href="#labels">as(mylabel)</a>.</p> - -<p>When grouping results, <em>groups</em> that contain <em>outputs</em>, -<em>group lists</em> and <em>hit lists</em> are generated. -Group lists contain subgroups, and hit lists contain hits that are part of the owning group.</p> - -<p>The identity of a group is held by its <em>id</em>. -Scalar identities such as long, double, and string, are directly available from the <em>id</em>, -whereas range identities used for bucket aggregation are separated into the sub-nodes <em>from</em> and <em>to</em>. -Refer to the <a href="default-result-format.html">result format reference</a>.</p> - - -<h3 id="multivalue-attributes">Multivalue attributes</h3> - -<p>A <a href="../../querying/searching-multivalue-fields">multivalue</a> attribute is a -<a href="../schemas/schemas.html#weightedset">weighted set</a>, -<a href="../schemas/schemas.html#array">array</a> or -<a href="../schemas/schemas.html#map">map</a>. <!-- ToDo: check if more. link to schema --> -Most grouping functions will just handle the elements of -multivalued attributes separately, as if they were all individual values in separate documents. -If you are grouping over array of struct or maps, scoping will be used to preserve structure. -Each entry in the array/map will be treated as a separate sub-document. -The following syntax can be used when grouping on <em>map</em> attribute fields. -</p> -<p>Group on map keys:</p> -<pre>all( group(mymap.key) each(output(count())) )</pre> -<p>Group on map keys then on map values:</p> -<pre>all( group(mymap.key) each( group(mymap.value) each(output(count())) ))</pre> -<p>Group on values for key <em>my_key</em>:</p> -<pre>all( group(my_map{"my_key"}) each(output(count())) )</pre> -<p>Group on struct field <em>my_field</em> referenced in map element <em>my_key</em>:</p> -<pre>all( group(my_map{"my_key"}.my_field) each(output(count())) )</pre> -<p> -The key can either be specified directly (above) or indirectly via a key source attribute. -The key is retrieved from the key source attribute for each document. -Note that the key source attribute must be single-valued and have the same data type as the key type of the map: -</p> -<pre>all( group(my_map{attribute(my_key_source)}) each(output(count())) )</pre> -<!-- ToDo add weightedset doc <strong>Note:</strong> This syntax is map-specific; it does NOT apply to weighted sets. --> -<p>Group on array of integers field:</p> -<pre>all( group(my_array) each(output(count())) )</pre> -<p>Group on struct field <em>my_field</em> in the <em>my_array</em> array of structs:</p> -<pre>all( group(my_array.my_field) each(output(count())) )</pre> - -<p> -<a href="../schemas/schemas.html#tensor">Tensors</a> can not be used in grouping. -</p> - - - -<h2 id="filtering-groups">Filtering groups</h2> - -<p>When grouping on multivalue attributes, it may be useful to filter the groups so -that only some specific values are collected. This can be done by adding a -filter. The <code>filter</code> clause expects a filter <em>predicate</em>:</p> -<ul> - <li> <a href="#regex-filter">regex("regular expression", input-expression</em>)</a></li> - <li> <a href="#range-filter">range(min-limit, max-limit, input-expression</em>)</a></li> - <li> <a href="#range-filter">range(min-limit, max-limit, input-expression</em>, bool, bool)</a></li> - <li> <a href="#istrue-filter">istrue(input-expression)</a></li> - <li> <a href="#logical-predicates-filter">not <em>predicate</em></a></li> - <li> <a href="#logical-predicates-filter"><em>predicate</em> and <em>predicate</em></a></li> - <li> <a href="#logical-predicates-filter"><em>predicate</em> or <em>predicate</em></a></li> -</ul> - -<h3 id="regex-filter">Regex filter</h3> - -<p>Use a regular expression to match the input, and include only -documents that match in the grouping. The input will usually be the -same -expression as in the "group" clause. -Example:</p> - -<pre>all( group(my_array) filter(regex("foo.*", my_array)) ...)</pre> - -<p>Here only the values that start -with a "foo" prefix in <em>my_array</em> are collected into groups, all others are ignored. -See also <a href="../../querying/grouping.html#structured-grouping">this example</a>.</p> - -<p>Regex filtering works on the string representation of any field type. For example, -you can also filter on boolean values using regex(true) and regex(false).</p> - -<h3 id="range-filter">Range filter</h3> - -<p>Use a <code>range</code> filter to match documents where a field value is between a lower and an upper bound. Example: -<pre>all( group(some_field) filter(range(1990, 2012, year)) ...)</pre> -Here, the lower bound is <em>inclusive</em> (year ≥ 1990) and the upper bound is <em>exclusive</em> (year < 2012). -Use optional bools at the end to control if the lower and upper bounds are inclusive, respectively. -The first bool sets the lower bound inclusive, and the second sets the upper bound inclusive. -<pre>all( group(some_field) filter(range(1990, 2012, year, true, true)) ...)</pre> -Here, both lower and upper bounds are inclusive.</p> - -<h3 id="istrue-filter">IsTrue filter</h3> - -<p>Use <code>istrue</code> to filter using a boolean expression. -The expression must evaluate to a boolean value, e.g. a boolean attribute. Example: -<pre>all( group(some_field) filter(istrue(my_bool_attribute)) ...)</pre> -Here, only documents where <em>my_bool_attribute</em> evaluates to true are included in the grouping.</p> - -<h3 id="logical-predicates-filter">Logical predicates</h3> -<p> - Use <code>not</code> to negate another filter expression. It takes a single - sub-filter and matches when the sub-filter does not. Example: -<pre>all( group(my_field) filter( not regex("bar.*", my_other_field)) ...)</pre> -Use <code>or</code> to perform a logical disjunction across two -sub-filters. The combined filter matches if any of the sub-filters evaluate to -true. Example: -<pre>all( group(my_field) filter( regex("bar.*", my_field) or regex("baz.*", my_third_field) ) ...)</pre> -Use <code>and</code> to perform a logical conjunction across two -sub-filters. The combined filter matches only if all of the sub-filters evaluate -to true. Example: -<pre>all( group(my_field) filter( regex("bar.*", my_other_field) and regex("baz.*", my_third_field) ) ...)</pre> -These logical predicates can be nested to create complex filter conditions. -Filter expressions follow <em>conventional precedence</em> -rules: <code>not</code> is evaluated before <code>and</code>, -and <code>and</code> is evaluated before <code>or</code>. Operators of the same -precedence are evaluated left-to-right. Use parentheses <code>(...)</code> to -force a different grouping when needed. - -Example: -<pre> -all( - group(my_field) - filter( (regex("bar.*", some_field) or regex("baz.*", other_field)) and not regex(".*foo", some_field)) - each(...) -) -</pre> -</p> - - - -<h2 id="order">Order / max</h2> -<p> -Each level of grouping may specify how to order its groups (using <code>order</code>): -</p> -<ul> - <li>Ordering can be done using any of the available aggregates</li> - <li>Multi-level grouping allows strict ordering where primary aggregates may be equal</li> - <li>Ordering is either ascending or descending, specified per level of ordering</li> - <li>Groups are sorted using <a href="#uca">locale aware sorting</a></li> -</ul> -<p> -Limit the number of groups returned for each level using <code>max</code>, -returning only first <em>n</em> groups as specified by <code>order</code>: -</p> -<ul><li> - <code>order</code> changes the ordering of groups after a merge operation for the following - aggregators: <code>count</code>, <code>avg</code> and <code> sum</code> -</li><li> - <code>order</code> <strong>will not</strong> change ordering of groups after a merge operation - when <code>max</code> or <code>min</code> is used -</li><li> - Default order, <code>-max(relevance())</code>, <strong>does not</strong> require use of - <a href="#precision">precision</a> -</li></ul> - - - -<h2 id="continuations">Continuations</h2> -<p> -Pagination of grouping results is managed by <code>continuations</code>. -These are opaque objects that can be combined and resubmitted using the <code>continuations</code> annotation -on the grouping step of the query to move to the previous or next page in a result list. -</p><p> -All root groups contain a single <em>this</em> continuation per <code>select</code>. -That continuation represents the current view, and if submitted as the sole continuation, -it will reproduce the exact same result as the one that contained it. -</p><p> -There are zero or one <em>prev</em>/<em>next</em> continuation per group- and hit list. -Submit any number of these to retrieve the next/previous pages of the corresponding lists -</p><p> -Any number of continuations can be combined in a query, but the first must always be the <em>this</em>-continuation. -E.g., one may simultaneously move both to the next page of one list, and the previous page of another. -</p> -{% include note.html content="If more than one continuation object is provided for the same group- or hit-list, - the one given last is the one that takes effect. - This is because continuations are processed in the order given, - and they replace whatever continuations they collide with."%} -<p> -If working programmatically with grouping, find the -<a href="https://javadoc.io/doc/com.yahoo.vespa/container-search/latest/com/yahoo/search/grouping/Continuation.html">Continuation</a> -objects within -<a href="https://javadoc.io/doc/com.yahoo.vespa/container-search/latest/com/yahoo/search/grouping/result/RootGroup.html">RootGroup</a>, -<a href="https://javadoc.io/doc/com.yahoo.vespa/container-search/latest/com/yahoo/search/grouping/result/GroupList.html">GroupList</a> and -<a href="https://javadoc.io/doc/com.yahoo.vespa/container-search/latest/com/yahoo/search/grouping/result/HitList.html">HitList</a> -result objects. These can then be added back into the continuation list of the -<a href="https://javadoc.io/doc/com.yahoo.vespa/container-search/latest/com/yahoo/search/grouping/GroupingRequest.html">GroupingRequest</a> -to paginate. -</p><p> -Refer to the <a href="../../querying/grouping.html#pagination">grouping guide</a> for an example. -</p> - - - -<h2 id="labels">Labels</h2> -<p> -Lists created using the <code>each</code> keyword can be assigned a label -using the construct <code>each(...) as(mylabel)</code>. -The outputs created by each clause will be identified by this label. -</p> - -<h2 id="aliases">Aliases</h2> -<p> - Grouping expressions can be tagged with an <em>alias</em>. - An alias allows the expression to be reused without having to repeat the expression verbatim. -</p> -<pre>all(group(a) alias(myalias, count()) each(output($myalias)))</pre> is equivalent to -<pre>all(group(a) each(output(count())))</pre>. -<pre>all(group(a) order($myalias=count()) each(output($myalias)))</pre> is equivalent to -<pre>all(group(a) order(count()) each(output(count())))</pre>. - -<h2 id="precision">Precision</h2> -<p> -The number of intermediate groups returned from each content node -during expression evaluation to give the container node more data to consider -when selecting the groups that are to be evaluated further: -<code>each(...) precision(1000)</code> -A higher number costs more bandwidth, but leads to higher accuracy in some cases. -</p> - -<h2 id="query-parameters">Query parameters</h2> -<p> - The following <em>query parameters</em> are relevant for grouping. - See the <a href="../api/query.html#parameters">Query API Reference</a> for description. -</p> -<ul> - <li><a href="../api/query.html#select">select</a></li> - <li><a href="../api/query.html#groupingsessioncache">groupingSessionCache</a></li> - <li><a href="../api/query.html#grouping.defaultmaxgroups">grouping.defaultMaxGroups</a></li> - <li><a href="../api/query.html#grouping.defaultmaxhits">grouping.defaultMaxHits</a></li> - <li><a href="../api/query.html#grouping.globalmaxgroups">grouping.globalMaxGroups</a></li> - <li><a href="../api/query.html#grouping.defaultprecisionfactor">grouping.defaultPrecisionFactor</a></li> -</ul> - -<h2 id="grouping-session-cache">Grouping Session Cache</h2> -{% include important.html content="The grouping session cache is <strong>only useful if</strong> the grouping expression -uses default ordering. -The <strong>drawback</strong> is that when <code>max</code> is specified in the grouping expression, -it might cause inaccuracies in aggregated values such as <code>count</code>. -It is recommended to test whether this is an issue or not, -and if so, adjust the <code>precision</code> parameter to still get correct counts."%} -<p> - The session cache stores intermediate grouping results in the content nodes when using multi-level grouping expressions, - in order to speed up grouping at a potential loss of accuracy. This causes the query and grouping expression to be run only once. -</p> -<p> - When having multi-level grouping expressions, the search query is normally re-run for each level. - The drawback of this is that, with an expensive ranking function, the query will take more time than strictly necessary. -</p> - -<h2 id="aggregators">Aggregators</h2> -<p> -Each level of grouping specifies a set of aggregates to collect for all documents -that belong to that group (using the <code>output</code> operation): -</p> -<ul> - <li>The documents in a group, retrieved using a specified summary class</li> - <li>The count of documents in a group</li> - <li>The sum, average, min, max, xor or standard deviation of an expression</li> - <li>Multiple quantiles of an expressions value</li> -</ul> -<p> -When all arguments are numeric, the result type is resolved by looking at the argument types. -If all arguments are longs, the result is a long. -If at least one argument is a double, the result is a double. -</p><p> -When using <code>order</code>, aggregators can also be used in expressions in order to get increased control over group sorting. -This does not work with expressions that take attributes as an argument, unless the expression is enclosed within an aggregator. -</p><p> -Using sum, max on a multivalued attribute: -Doing an operation such as <code>output(sum(myarray))</code> will run the sum over each element value in each document. -The result is the sum of sums of values. -Similarly, <code>max(myarray)</code> will yield the maximal element over all elements in all documents, and so on. -</p><p> -Compute quantiles by listing the desired quantile values (comma-separated) in brackets, followed by a comma and the expression (e.g., a field): -<pre>all( group(city) each(output(quantiles([0.5], delivery_days) as(median_delivery_days) ) ) )</pre> -to compute the median, or -<pre>all( group(city) each(output(quantiles([0.5, 0.9], delivery_days))) )</pre> -This computes the median (p50) and 90th percentile (p90) time to delivery in days per city. Note that quantiles are computed using <a href="https://datasketches.apache.org/docs/KLL/KLLSketch.html">KLL Sketch</a>, so they are approximate. -</p><p> -Multivalue fields, such as maps and arrays, can be used for grouping. -However, using aggregation functions such as sum() on such fields can give misleading results. -Assume a map from strings to integers (<code>map<string, int></code>), -where the strings are some sort of key to use for grouping. -The following expression will provide the sum of the values for all keys: -</p> -<pre>all( group(mymap.key) each(output(sum(mymap.value))) )</pre> -<p> -and not the sum of the values within each key, as one would expect. -It is still, however, possible to run the following expression to get the sum of values within a specific key: -</p> -<pre>all( group("my_group") each(output(sum(mymap{"foo"}))) )</pre> -<p> -Refer to the system test for -<a href="https://github.com/vespa-engine/system-test/blob/master/tests/search/struct_and_map_types/struct_and_map_grouping.rb">grouping on struct and map types</a> -for more examples. -</p> -<table class="table"> - <tr><td colspan="4"><h3 id="group-list-aggregators">Group list aggregators</h3></td></tr> - <tr class="trx"><th class="thx">Name</th><th class="thx">Description</th><th class="thx">Arguments</th><th class="thx">Result</th></tr> - <tr><td>count</td><td> - Counts the number of unique groups (as produced by <code>group</code>). - Note that <code>count</code> operates independently of <code>max</code> - and that this count is an estimate using HyperLogLog++ - which is an algorithm for the count-distinct problem</td><td>None</td><td>Long</td></tr> - - <tr><td colspan="4"><h3 id="group-aggregators">Group aggregators</h3></td></tr> - <tr class="trx"><th class="thx">Name</th><th class="thx">Description</th><th class="thx">Arguments</th><th class="thx">Result</th></tr> - <tr><td>count</td><td>Increments a long counter every time it is invoked</td><td>None</td><td>Long</td></tr> - <tr><td>sum</td><td>Sums the argument over all selected documents</td><td>Numeric</td><td>Numeric</td></tr> - <tr><td>avg</td><td>Computes the average over all selected documents</td><td>Numeric</td><td>Numeric</td></tr> - <tr><td>min</td><td>Keeps the minimum value of selected documents</td><td>Numeric</td><td>Numeric</td></tr> - <tr><td>max</td><td>Keeps the maximum value of selected documents</td><td>Numeric</td><td>Numeric</td></tr> - <tr><td>xor</td><td>XOR the values (their least significant 64 bits) of all selected documents</td><td>Any</td><td>Long</td></tr> - <tr><td>stddev</td><td>Computes the population standard deviation over all selected documents</td><td>Numeric</td><td>Double</td></tr> - <tr><td>quantiles</td><td>Computes one or multiple quantiles of the values of an expression. Quantiles must be a number between 0 and 1 inclusive.</td><td>[Numeric+], Expr</td><td>[{"quantile":Double,"value":Double}+]</td></tr> - - <tr><td colspan="4"><h3 id="hit-aggregators">Hit aggregators</h3></td></tr> - <tr class="trx"><th class="thx">Name</th><th class="thx">Description</th><th class="thx">Arguments</th><th class="thx">Result</th></tr> - <tr><td><p id="summary">summary</p></td><td>Produces a summary of the requested <a href="/en/reference/schemas/schemas.html#document-summary">summary class</a></td><td>Name of summary class</td><td>Summary</td></tr> -</table> - - - -<h2 id="expressions">Expressions</h2> -<table class="table"> - <tr><td colspan="4"><h3 id="arithmetic-expressions">Arithmetic expressions</h3></td></tr> - <tr class="trx"><th class="thx">Name</th><th class="thx">Description</th><th class="thx">Arguments</th><th class="thx">Result</th></tr> - <tr><td>add</td><td>Add the arguments together</td><td>Numeric+</td><td>Numeric</td></tr> - <tr><td>+</td><td>Add left and right argument</td><td>Numeric, Numeric</td><td>Numeric</td></tr> - <tr><td>mul</td><td>Multiply the arguments together</td><td>Numeric+</td><td>Numeric</td></tr> - <tr><td>*</td><td>Multiply left and right argument</td><td>Numeric, Numeric</td><td>Numeric</td></tr> - <tr><td>sub</td><td>Subtract second argument from first, third from result, etc</td><td>Numeric+</td><td>Numeric</td></tr> - <tr><td>-</td><td>Subtract right argument from left</td><td>Numeric, Numeric</td><td>Numeric</td></tr> - <tr><td>div</td><td>Divide first argument by second, result by third, etc</td><td>Numeric+</td><td>Numeric</td></tr> - <tr><td>/</td><td>Divide left argument by right</td><td>Numeric, Numeric</td><td>Numeric</td></tr> - <tr><td>mod</td><td>Modulo first argument by second, result by third, etc</td><td>Numeric+</td><td>Numeric</td></tr> - <tr><td>%</td><td>Modulo left argument by right</td><td>Numeric, Numeric</td><td>Numeric</td></tr> - <tr><td>neg</td><td>Negate argument</td><td>Numeric</td><td>Numeric</td></tr> - <tr><td>-</td><td>Negate right argument</td><td>Numeric</td><td>Numeric</td></tr> - - <tr><td colspan="4"><h3 id="bitwise-expressions">Bitwise expressions</h3></td></tr> - <tr class="trx"><th class="thx">Name</th><th class="thx">Description</th><th class="thx">Arguments</th><th class="thx">Result</th></tr> - <tr><td>and</td><td>AND the arguments in order</td><td>Long+</td><td>Long</td></tr> - <tr><td>or</td><td>OR the arguments in order</td><td>Long+</td><td>Long</td></tr> - <tr><td>xor</td><td>XOR the arguments in order</td><td>Long+</td><td>Long</td></tr> - - <tr><td colspan="4"><h3 id="string-expressions">String expressions</h3></td></tr> - <tr class="trx"><th class="thx">Name</th><th class="thx">Description</th><th class="thx">Arguments</th><th class="thx">Result</th></tr> - <tr><td>strlen</td><td>Count the number of bytes in argument</td><td>String</td><td>Long</td></tr> - <tr><td>strcat</td><td>Concatenate arguments in order</td><td>String+</td><td>String</td></tr> - - <tr><td colspan="4"><h3 id="type-conversion-expressions">Type conversion expressions</h3></td></tr> - <tr class="trx"><th class="thx">Name</th><th class="thx">Description</th><th class="thx">Arguments</th><th class="thx">Result</th></tr> - <tr><td>todouble</td><td>Convert argument to double</td><td>Any</td><td>Double</td></tr> - <tr><td>tolong</td><td>Convert argument to long</td><td>Any</td><td>Long</td></tr> - <tr><td>tostring</td><td>Convert argument to string</td><td>Any</td><td>String</td></tr> - <tr><td>toraw</td><td>Convert argument to raw</td><td>Any</td><td>Raw</td></tr> - - <tr><td colspan="4"><h3 id="raw-data-expressions">Raw data expressions</h3></td></tr> - <tr class="trx"><th class="thx">Name</th><th class="thx">Description</th><th class="thx">Arguments</th><th class="thx">Result</th></tr> - <tr><td>cat</td><td>Cat the binary representation of the arguments together</td><td>Any+</td><td>Raw</td></tr> - <tr><td>md5</td><td>Does an MD5 over the binary representation of the argument, - and keeps the lowest 'width' bits</td><td>Any, Numeric(width)</td><td>Raw</td></tr> - <tr><td>xorbit</td><td>Does an XOR of 'width' bits over the binary representation of the argument. - Width is rounded up to a multiple of 8</td><td>Any, Numeric(width)</td><td>Raw</td></tr> - - <tr><td colspan="4"><h3 id="accessor-expressions">Accessor expressions</h3></td></tr> - <tr class="trx"><th class="thx">Name</th><th class="thx">Description</th><th class="thx">Arguments</th><th class="thx">Result</th></tr> - <tr><td>relevance</td><td>Return the computed rank of a document</td><td>None</td><td>Double</td></tr> - <tr><td><attribute-name></td><td>Return the value of the named attribute</td><td>None</td><td>Any</td></tr> - <tr><td>array.at</td><td> - Array element access. - The expression <code>array.at(myarray, idx)</code> returns one value per document - by evaluating the <code>idx</code> expression and using it as an index into the array. - The expression can then be used to build bigger expressions such as - <code>output(sum(array.at(myarray, 0)))</code> - which will sum the first element in the array of each document. - <ul> - <li>The <code>idx</code> expression is capped to <code>[0, size(myarray)-1]</code></li> - <li>If > array size, the last element is returned</li> - <li>If < 0, the first element is returned</li> - </ul> - </td><td>Array, Numeric</td><td>Any</td></tr> - <tr><td>interpolatedlookup</td><td> - <p id="interpolatedlookup"> - Counts elements in a sorted array that are less than an expression, - with linear interpolation if the expression is between element values. - The operation <code>interpolatedlookup(myarray, expr)</code> is intended for generic graph/function lookup. - The data in <code>myarray</code> should be numerical values sorted in ascending order. - The operation will then scan from the start of the array to find the position - where the element values become equal to (or greater than) the value of the <code>expr</code> lookup argument, - and return the index of that position. - </p><p> - When the lookup argument's value is between two consecutive array element values, - the returned position will be a linear interpolation between their respective indexes. - The return value is always in the range <code>[0, size(myarray)-1]</code> of the valid index values for an array. - </p><p> - Assume <code>myarray</code> is a sorted array of type <code>array<double></code> in each document: - The expression <code>interpolatedlookup(myarray, 4.2)</code> is now a per-document expression - that first evaluates the lookup argument, here a constant expression 4.2, - and then looks at the contents of <code>myarray</code> in the document. - The scan starts at the first element and proceeds until it hits an element value greater than 4.2 in the array. - This means that: - </p> - <ul> - <li>If the first element in the array is greater than 4.2, the expression returns 0</li> - <li>If the first element in the array is exactly 4.2, the expression still returns 0</li> - <li>If the first element in the array is 1.7 while the <strong>second</strong> element value is exactly 4.2, - the expression returns 1.0 - the index of the second element</li> - <li>If <strong>all</strong> the elements in the array are less than 4.2, - the last valid array index <code>size(myarray)-1</code> is returned</li> - <li>If the first 5 elements in the array have values smaller than the lookup argument, - and the lookup argument is halfway between the fifth and sixth element, - a value of 4.5 is returned - halfway between the array indexes of the fifth and sixth elements</li> - <li>Similarly, if the elements in the array are <code>{0, 1, 2, 4, 8}</code> - then passing a lookup argument of "5" would return 3.25 (linear interpolation between - <code>indexOf(4)==3</code> and <code>indexOf(8)==4</code>)</li> - </ul> - </td><td>Array, Numeric</td><td>Numeric</td></tr> - - <tr><td colspan="4"><h3 id="bucket-expressions">Bucket expressions</h3></td></tr> - <tr class="trx"><th class="thx">Name</th><th class="thx">Description</th><th class="thx">Arguments</th><th class="thx">Result</th></tr> - <tr><td>fixedwidth</td><td>Maps the value of the first argument into consecutive buckets - whose width equals the second argument</td><td>Any, Numeric</td><td>NumericBucketList</td></tr> - <tr><td>predefined</td><td><p>Maps the value of the first argument into the given buckets.</p> - <ul> - <li>Standard mathematical start and end specifiers may be used to define the width of a <code>bucket</code>. - The <code>(</code> and <code>)</code> evaluates to <code>[</code> and <code>></code> by default.</li> - <li>The buckets assume the type of the start/end specifiers - (<code>string</code>, <code>long</code>, <code>double</code> or <code>raw</code>). - Values are converted to this type before being compared with these specifiers. - (e.g., <code>double</code> values are rounded to the nearest integer for buckets of type <code>long</code>).</li> - <li>The end specifier can be skipped. The buckets - <code>bucket(3)</code>/<code>bucket[3]</code> are the same as <code>bucket[3,4></code>. - This is allowed for string expressions as well; - <code>bucket("c")</code> is identical to <code>bucket["c", "c "></code>.</li> - </ul> - </td><td>Any, Bucket+</td><td>BucketList</td></tr> - - <tr><td colspan="4"><h3 id="time-expressions">Time expressions</h3> - <!-- ToDo: we have not documented a "timezone" query parameter - put back if we have one ... - Use the query parameter "timezone" to set the timezone to use when running these expressions. - E.g. <code>&timezone=GMT-1</code>. Refer to Sun's - <a href="https://docs.oracle.com/javase/6/docs/api/java/util/TimeZone.html">TimeZone</a> reference --> - The field must be a <a href="../schemas/schemas.html#long">long</a>, - with second resolution (unix timestamp/epoch) - - <a href="../../querying/grouping.html#time-and-date">examples</a>. - Each of the time-functions will respect the <a href="../api/query.html#timezone">timezone</a> query parameter. - </td></tr> - <tr class="trx"><th class="thx">Name</th><th class="thx">Description</th><th class="thx">Arguments</th><th class="thx">Result</th></tr> - <tr><td>time.dayofmonth</td><td>Returns the day of month (1-31) for the given timestamp</td><td>Long</td><td>Long</td></tr> - <tr><td>time.dayofweek</td><td>Returns the day of week (0-6) for the given timestamp, Monday being 0</td><td>Long</td><td>Long</td></tr> - <tr><td>time.dayofyear</td><td>Returns the day of year (0-365) for the given timestamp</td><td>Long</td><td>Long</td></tr> - <tr><td>time.hourofday</td><td>Returns the hour of day (0-23) for the given timestamp</td><td>Long</td><td>Long</td></tr> - <tr><td>time.minuteofhour</td><td>Returns the minute of hour (0-59) for the given timestamp</td><td>Long</td><td>Long</td></tr> - <tr><td>time.monthofyear</td><td>Returns the month of year (1-12) for the given timestamp</td><td>Long</td><td>Long</td></tr> - <tr><td>time.secondofminute</td><td>Returns the second of minute (0-59) for the given timestamp</td><td>Long</td><td>Long</td></tr> - <tr><td>time.year</td><td>Returns the full year (e.g. 2009) of the given timestamp</td><td>Long</td><td>Long</td></tr> - <tr><td>time.date</td><td>Returns the date (e.g. 2009-01-10) of the given timestamp</td><td>Long</td><td>Long</td></tr> - - <tr><td colspan="4"><h3 id="list-expressions">List expressions</h3></td></tr> - <tr class="trx"><th class="thx">Name</th><th class="thx">Description</th><th class="thx">Arguments</th><th class="thx">Result</th></tr> - <tr><td>size</td><td>Return the number of elements in the argument if it is a list. If not return 1</td><td>Any</td><td>Long</td></tr> - <tr><td>sort</td><td>Sort the elements in the argument in ascending order if the argument is a list. If not, it is a NOP</td><td>Any</td><td>Any</td></tr> - <tr><td>reverse</td><td>Reverse the elements in the argument if the argument is a list. If not, it is a NOP</td><td>Any</td><td>Any</td></tr> - - <tr><td colspan="4"><h3 id="other-expressions">Other expressions</h3></td></tr> - <tr class="trx"><th class="thx">Name</th><th class="thx">Description</th><th class="thx">Arguments</th><th class="thx">Result</th></tr> - <tr><td>zcurve.x</td><td> - Returns the X component of the given <a href="https://en.wikipedia.org/wiki/Z-order_curve">zcurve</a> encoded 2d point. - All fields of type "position" have an accompanying "<fieldName>_zcurve" attribute - that can be decoded using this expression, e.g. <code>zcurve.x(foo_zcurve)</code> - </td><td>Long</td><td>Long</td> - </tr> - <tr><td>zcurve.y</td><td>Returns the Y component of the given zcurve encoded 2d point</td><td>Long</td><td>Long</td></tr> - <tr><td>geo_distance</td><td> - <p id="geo_distance"> - Computes the great-circle distance from a <a href="../schemas/schemas.html#position">position</a> field - to a given point. - The unit suffix <code>.km</code> or <code>.miles</code> selects the output unit. - Works on both <code>position</code> and <code>array<position></code> fields. - For arrays, the minimum distance across all positions in the document is returned. - </p> - <pre>all( group(fixedwidth(geo_distance(attribute(location), 63.4, 10.4).km, 10)) each(output(count())) )</pre> - <p>Available since {% include version.html version="8.664.22" %}.</p> - </td><td>Attribute(position), Double(lat), Double(lng)</td><td>Double</td></tr> - <tr><td>uca</td><td> - <p id="uca"> - Converts the attribute string using <a href="https://www.unicode.org/reports/tr10/">unicode collation algorithm</a>. - Groups are sorted using locale-aware sorting, with the default and primary strength values, respectively: - </p> - <pre>all( group(s) order(max(uca(s, "sv"))) each(output(count())) )</pre> - <pre>all( group(s) order(max(uca(s, "sv", "PRIMARY"))) each(output(count())) )</pre> - </td> - <td>Any, Locale(String), Strength(String)</td><td>Raw</td> - </tr> - - <tr><td colspan="4"><h3 id="single-argument-standard-mathematical-expressions">Single argument standard mathematical expressions</h3> - These are the standard mathematical functions as found in the Java - <a href="https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html">Math</a> class. - </td></tr> - <tr class="trx"><th class="thx">Name</th><th class="thx">Description</th><th class="thx">Arguments</th><th class="thx">Result</th></tr> - <tr><td>math.exp</td><td> </td><td>Double</td><td>Double</td></tr> - <tr><td>math.log</td><td> </td><td>Double</td><td>Double</td></tr> - <tr><td>math.log1p</td><td> </td><td>Double</td><td>Double</td></tr> - <tr><td>math.log10</td><td> </td><td>Double</td><td>Double</td></tr> - <tr><td>math.sqrt</td><td> </td><td>Double</td><td>Double</td></tr> - <tr><td>math.cbrt</td><td> </td><td>Double</td><td>Double</td></tr> - <tr><td>math.sin</td><td> </td><td>Double</td><td>Double</td></tr> - <tr><td>math.cos</td><td> </td><td>Double</td><td>Double</td></tr> - <tr><td>math.tan</td><td> </td><td>Double</td><td>Double</td></tr> - <tr><td>math.asin</td><td> </td><td>Double</td><td>Double</td></tr> - <tr><td>math.acos</td><td> </td><td>Double</td><td>Double</td></tr> - <tr><td>math.atan</td><td> </td><td>Double</td><td>Double</td></tr> - <tr><td>math.sinh</td><td> </td><td>Double</td><td>Double</td></tr> - <tr><td>math.cosh</td><td> </td><td>Double</td><td>Double</td></tr> - <tr><td>math.tanh</td><td> </td><td>Double</td><td>Double</td></tr> - <tr><td>math.asinh</td><td> </td><td>Double</td><td>Double</td></tr> - <tr><td>math.acosh</td><td> </td><td>Double</td><td>Double</td></tr> - <tr><td>math.atanh</td><td> </td><td>Double</td><td>Double</td></tr> - - <tr><td colspan="4"><h3 id="dual-argument-standard-mathematical-expressions">Dual argument standard mathematical expressions</h3> - </td></tr> - <tr class="trx"><th class="thx">Name</th><th class="thx">Description</th><th class="thx">Arguments</th><th class="thx">Result</th></tr> - <tr><td>math.pow</td><td>Return X^Y.</td><td>Double, Double</td><td>Double</td></tr> - <tr><td>math.hypot</td><td>Return length of hypotenuse given X and Y sqrt(X^2 + Y^2)</td><td>Double, Double</td><td>Double</td></tr> -</table> - -<h2 id="filters">Filters</h2> -<table class="table"> - <tr><td colspan="4"><h3 id="string-filters">String filters</h3></td></tr> - <tr class="trx"><th class="thx">Name</th><th class="thx">Description</th><th class="thx">Arguments</th><th class="thx">Result</th></tr> - <tr><td>regex</td><td>Matches a field against a regular expression string.</td><td>String, Expression</td><td>Bool</td></tr> - - <tr><td colspan="4"><h3 id="numeric-filters">Numeric filters</h3></td></tr> - <tr class="trx"><th class="thx">Name</th><th class="thx">Description</th><th class="thx">Arguments</th><th class="thx">Result</th></tr> - <tr><td>range</td><td>Matches when a field is between a lower and upper bound.</td><td>Numeric, Numeric, Expression, Bool?, Bool?</td><td>Bool</td></tr> - - <tr><td colspan="4"><h3 id="boolean-filters">Boolean filters</h3></td></tr> - <tr class="trx"><th class="thx">Name</th><th class="thx">Description</th><th class="thx">Arguments</th><th class="thx">Result</th></tr> - <tr><td>istrue</td><td>Matches when a boolean expression evaluates to true.</td><td>Expression</td><td>Bool</td></tr> - - <tr><td colspan="4"><h3 id="predicate-filters">Predicate filters</h3></td></tr> - <tr class="trx"><th class="thx">Name</th><th class="thx">Description</th><th class="thx">Arguments</th><th class="thx">Result</th></tr> - <tr><td>and</td><td>Logical <code>and</code> between the arguments.</td><td>Filter, Filter</td><td>Bool</td></tr> - <tr><td>not</td><td>Logical <code>not</code> on the argument.</td><td>Filter</td><td>Bool</td></tr> - <tr><td>or</td><td>Logical <code>or</code> between the arguments.</td><td>Filter, Filter</td><td>Bool</td></tr> -</table> - - -<h2 id="grouping-language-grammar">Grouping language grammar</h2> -<pre> -request ::= "all(" operations ")" -group ::= ( "all" | "each") "(" operations ")" [ "as" "(" identifier ")" ] -operations ::= [ "group" "(" exp ")" ] - ( ( "alias" "(" identifier "," exp ")" ) | - ( filter ) | - ( "max" "(" ( number | "inf" ) ")" ) | - ( "order" "(" expList | aggrList ")" ) | - ( "output" "(" aggrList ")" ) | - ( "precision" "(" number ")" ) )* - group* -aggrList ::= aggr ( "," aggr )* -aggr ::= ( ( "count" "(" ")" ) | - ( "sum" "(" exp ")" ) | - ( "avg" "(" exp ")" ) | - ( "max" "(" exp ")" ) | - ( "min" "(" exp ")" ) | - ( "xor" "(" exp ")" ) | - ( "stddev" "(" exp ")" ) | - ( "quantiles" "(" "[" number ( "," number )* "]" "," exp ")" ) | - ( "summary" "(" [ identifier ] ")" ) ) - [ "as" "(" identifier ")" ] -expList ::= exp ( "," exp )* -exp ::= ( "+" | "-") ( "$" identifier [ "=" math ] ) | ( math ) | ( aggr ) -filter ::= ( "filter" | "keep" ) "(" filterExp ")" -filterExp ::= filterAnd ( "or" filterAnd )* -filterAnd ::= filterNot ( "and" filterNot )* -filterNot ::= "not" filterNot | filterPrim -filterPrim ::= ( "(" filterExp ")" ) | - ( "regex" "(" string "," exp ")" ) | - ( "range" "(" number "," number "," exp [ "," boolean "," boolean ] ")" ) | - ( "istrue" "(" exp ")" ) -math ::= value [ ( "+" | "-" | "*" | "/" | "%" ) value ] -value ::= ( "(" exp ")" ) | - ( "add" "(" expList ")" ) | - ( "and" "(" expList ")" ) | - ( "cat" "(" expList ")" ) | - ( "div" "(" expList ")" ) | - ( "docidnsspecific" "(" ")" ) | - ( "fixedwidth" "(" exp "," number ")" ) | - ( "geo_distance" "(" "attribute" "(" attributeName ")" "," number "," number ")" "." ( "km" | "miles" ) ) | - ( "interpolatedlookup" "(" attributeName "," exp ")") | - ( "math" "." ( - ( - "exp" | "log" | "log1p" | "log10" | "sqrt" | "cbrt" | - "sin" | "cos" | "tan" | "asin" | "acos" | "atan" | - "sinh" | "cosh" | "tanh" | "asinh" | "acosh" | "atanh" - ) "(" exp ")" | - ( "pow" | "hypot" ) "(" exp "," exp ")" - )) | - ( "max" "(" expList ")" ) | - ( "md5" "(" exp "," number "," number ")" ) | - ( "min" "(" expList ")" ) | - ( "mod" "(" expList ")" ) | - ( "mul" "(" expList ")" ) | - ( "or" "(" expList ")" ) | - ( "predefined" "(" exp "," "(" bucket ( "," bucket )* ")" ")" ) | - ( "reverse" "(" exp ")" ) | - ( "relevance" "(" ")" ) | - ( "sort" "(" exp ")" ) | - ( "strcat" "(" expList ")" ) | - ( "strlen" "(" exp ")" ) | - ( "size" "(" exp")" ) | - ( "sub" "(" expList ")" ) | - ( "time" "." ( "date" | "year" | "monthofyear" | "dayofmonth" | "dayofyear" | "dayofweek" | - "hourofday" | "minuteofhour" | "secondofminute" ) "(" exp ")" ) | - ( "todouble" "(" exp ")" ) | - ( "tolong" "(" exp ")" ) | - ( "tostring" "(" exp ")" ) | - ( "toraw" "(" exp ")" ) | - ( "uca" "(" exp "," string [ "," string ] ")" ) | - ( "xor" "(" expList ")" ) | - ( "xorbit" "(" exp "," number ")" ) | - ( "zcurve" "." ( "x" | "y" ) "(" exp ")" ) | - ( attributeName "." "at" "(" number ")") | - ( attributeName ) -bucket ::= "bucket" ( "(" | "[" | "<" ) - ( "-inf" | rawvalue | number | string ) - [ "," ( "inf" | rawvalue | number | string ) ] - ( ")" | "]" | ">" ) -rawvalue ::= "{" ( ( string | number ) "," )* "}" -</pre> diff --git a/en/reference/querying/grouping-language.mdx b/en/reference/querying/grouping-language.mdx new file mode 100644 index 0000000000..11d91e4893 --- /dev/null +++ b/en/reference/querying/grouping-language.mdx @@ -0,0 +1,1133 @@ +--- +title: "Grouping Language Reference" +sidebarTitle: "Grouping" +--- + +Read the [Vespa grouping guide](/en/querying/grouping) first, for examples and an introduction to grouping - this is the Vespa grouping reference. + +Also note that using a [multivalued](/en/querying/searching-multivalue-fields) attribute (such as an array of doubles) in a grouping expression affects performance. Such operations can hit a memory bandwidth bottleneck, particularly if the set of hits to be processed is large, as more data is evaluated. + +## Group + +Group query results using a custom expression (using the `group` clause): + +- A numerical or string constant (e.g., `group(1)` or `group("all")`) which makes one bucket with everything +- A document [attribute](/en/content/attributes) +- A function over another expression (`xorbit`, `md5`, `cat`, `xor`, `and`, `or`, `add`, `sub`, `mul`, `div`, `mod`) or any other [expression](#expressions) +- The datatype of an expression is resolved using best-effort, similarly to how common programming languages do to resolve arithmetic of different data-typed operands +- The results of any expression are either scalar or single-dimensional arrays + - `add(<array>)` adds all elements together to produce a scalar + - `add(<arrayA>, <arrayB>)` adds each element together producing a new array whose size is `max(|<arrayA>|, |<arrayB>|)` + +Groups can contain subgroups (by using `each` and `group` operations), and may be nested to any level. + +Multiple sub-groupings or outputs can be created under the same group level, using multiple parallel `each` or `all` clauses, and each one may be labelled using [as(mylabel)](#labels). + +When grouping results, _groups_ that contain _outputs_, _group lists_ and _hit lists_ are generated. Group lists contain subgroups, and hit lists contain hits that are part of the owning group. + +The identity of a group is held by its _id_. Scalar identities such as long, double, and string, are directly available from the _id_, whereas range identities used for bucket aggregation are separated into the sub-nodes _from_ and _to_. Refer to the [result format reference](/en/reference/querying/default-result-format). + +### Multivalue attributes + +A [multivalue](/en/querying/searching-multivalue-fields) attribute is a [weighted set](/en/reference/schemas/schemas#weightedset), [array](/en/reference/schemas/schemas#array) or [map](/en/reference/schemas/schemas#map). Most grouping functions will just handle the elements of multivalued attributes separately, as if they were all individual values in separate documents. If you are grouping over array of struct or maps, scoping will be used to preserve structure. Each entry in the array/map will be treated as a separate sub-document. The following syntax can be used when grouping on _map_ attribute fields. + +Group on map keys: + +```bash +all( group(mymap.key) each(output(count())) ) +``` + +Group on map keys then on map values: + +```bash +all( group(mymap.key) each( group(mymap.value) each(output(count())) )) +``` + +Group on values for key _my\_key_: + +```bash +all( group(my_map{"my_key"}) each(output(count())) ) +``` + +Group on struct field _my\_field_ referenced in map element _my\_key_: + +```bash +all( group(my_map{"my_key"}.my_field) each(output(count())) ) +``` + +The key can either be specified directly (above) or indirectly via a key source attribute. The key is retrieved from the key source attribute for each document. Note that the key source attribute must be single-valued and have the same data type as the key type of the map: + +```bash +all( group(my_map{attribute(my_key_source)}) each(output(count())) ) +``` + +Group on array of integers field: + +```bash +all( group(my_array) each(output(count())) ) +``` + +Group on struct field _my\_field_ in the _my\_array_ array of structs: + +```bash +all( group(my_array.my_field) each(output(count())) ) +``` + +[Tensors](/en/reference/schemas/schemas#tensor) can not be used in grouping. + +## Filtering groups + +When grouping on multivalue attributes, it may be useful to filter the groups so that only some specific values are collected. This can be done by adding a filter. The `filter` clause expects a filter _predicate_: + +- [regex("regular expression", input-expression)](#regex-filter) +- [range(min-limit, max-limit, input-expression)](#range-filter) +- [range(min-limit, max-limit, input-expression, bool, bool)](#range-filter) +- [istrue(input-expression)](#istrue-filter) +- [not _predicate_](#logical-predicates-filter) +- [_predicate_ and _predicate_](#logical-predicates-filter) +- [_predicate_ or _predicate_](#logical-predicates-filter) + +### Regex filter + +Use a regular expression to match the input, and include only documents that match in the grouping. The input will usually be the same -expression as in the "group" clause. Example: + +```bash +all( group(my_array) filter(regex("foo.*", my_array)) ...) +``` + +Here only the values that start with a "foo" prefix in _my\_array_ are collected into groups, all others are ignored. See also [this example](/en/querying/grouping#structured-grouping). + +Regex filtering works on the string representation of any field type. For example, you can also filter on boolean values using regex(true) and regex(false). + +### Range filter + +Use a `range` filter to match documents where a field value is between a lower and an upper bound. Example: + +```bash +all( group(some_field) filter(range(1990, 2012, year)) ...) +``` +Here, the lower bound is _inclusive_ (year ≥ 1990) and the upper bound is _exclusive_ (year \< 2012). Use optional bools at the end to control if the lower and upper bounds are inclusive, respectively. The first bool sets the lower bound inclusive, and the second sets the upper bound inclusive. +```bash +all( group(some_field) filter(range(1990, 2012, year, true, true)) ...) +``` +Here, both lower and upper bounds are inclusive. +### IsTrue filter + +Use `istrue` to filter using a boolean expression. The expression must evaluate to a boolean value, e.g. a boolean attribute. Example: + +```bash +all( group(some_field) filter(istrue(my_bool_attribute)) ...) +``` +Here, only documents where _my\_bool\_attribute_ evaluates to true are included in the grouping. +### Logical predicates + +Use `not` to negate another filter expression. It takes a single sub-filter and matches when the sub-filter does not. Example: + +```bash +all( group(my_field) filter( not regex("bar.*", my_other_field)) ...) +``` +Use `or` to perform a logical disjunction across two sub-filters. The combined filter matches if any of the sub-filters evaluate to true. Example: +```bash +all( group(my_field) filter( regex("bar.*", my_field) or regex("baz.*", my_third_field) ) ...) +``` +Use `and` to perform a logical conjunction across two sub-filters. The combined filter matches only if all of the sub-filters evaluate to true. Example: +```bash +all( group(my_field) filter( regex("bar.*", my_other_field) and regex("baz.*", my_third_field) ) ...) +``` +These logical predicates can be nested to create complex filter conditions. Filter expressions follow _conventional precedence_ rules: `not` is evaluated before `and`, and `and` is evaluated before `or`. Operators of the same precedence are evaluated left-to-right. Use parentheses `(...)` to force a different grouping when needed. Example: +```bash +all( + group(my_field) + filter( (regex("bar.*", some_field) or regex("baz.*", other_field)) and not regex(".*foo", some_field)) + each(...) +) +``` + +## Order / max + +Each level of grouping may specify how to order its groups (using `order`): + +- Ordering can be done using any of the available aggregates +- Multi-level grouping allows strict ordering where primary aggregates may be equal +- Ordering is either ascending or descending, specified per level of ordering +- Groups are sorted using [locale aware sorting](#uca) + +Limit the number of groups returned for each level using `max`, returning only first _n_ groups as specified by `order`: + +- `order` changes the ordering of groups after a merge operation for the following aggregators: `count`, `avg` and ` sum` +- `order` **will not** change ordering of groups after a merge operation when `max` or `min` is used +- Default order, `-max(relevance())`, **does not** require use of [precision](#precision) + +## Continuations + +Pagination of grouping results is managed by `continuations`. These are opaque objects that can be combined and resubmitted using the `continuations` annotation on the grouping step of the query to move to the previous or next page in a result list. + +All root groups contain a single _this_ continuation per `select`. That continuation represents the current view, and if submitted as the sole continuation, it will reproduce the exact same result as the one that contained it. + +There are zero or one _prev_/_next_ continuation per group- and hit list. Submit any number of these to retrieve the next/previous pages of the corresponding lists + +Any number of continuations can be combined in a query, but the first must always be the _this_-continuation. E.g., one may simultaneously move both to the next page of one list, and the previous page of another. + +<Info> + **Note:** If more than one continuation object is provided for the same group- or hit-list, the one given last is the one that takes effect. This is because continuations are processed in the order given, and they replace whatever continuations they collide with. +</Info> + +If working programmatically with grouping, find the [Continuation](https://javadoc.io/doc/com.yahoo.vespa/container-search/latest/com/yahoo/search/grouping/Continuation.html) objects within [RootGroup](https://javadoc.io/doc/com.yahoo.vespa/container-search/latest/com/yahoo/search/grouping/result/RootGroup.html), [GroupList](https://javadoc.io/doc/com.yahoo.vespa/container-search/latest/com/yahoo/search/grouping/result/GroupList.html) and [HitList](https://javadoc.io/doc/com.yahoo.vespa/container-search/latest/com/yahoo/search/grouping/result/HitList.html) result objects. These can then be added back into the continuation list of the [GroupingRequest](https://javadoc.io/doc/com.yahoo.vespa/container-search/latest/com/yahoo/search/grouping/GroupingRequest.html) to paginate. + +Refer to the [grouping guide](/en/querying/grouping#pagination) for an example. + +## Labels + +Lists created using the `each` keyword can be assigned a label using the construct `each(...) as(mylabel)`. The outputs created by each clause will be identified by this label. + +## Aliases + +Grouping expressions can be tagged with an _alias_. An alias allows the expression to be reused without having to repeat the expression verbatim. + +```bash +all(group(a) alias(myalias, count()) each(output($myalias))) +``` + is equivalent to +```bash +all(group(a) each(output(count()))) +``` +. +```bash +all(group(a) order($myalias=count()) each(output($myalias))) +``` + is equivalent to +```bash +all(group(a) order(count()) each(output(count()))) +``` +. +## Precision + +The number of intermediate groups returned from each content node during expression evaluation to give the container node more data to consider when selecting the groups that are to be evaluated further: `each(...) precision(1000)` A higher number costs more bandwidth, but leads to higher accuracy in some cases. + +## Query parameters + +The following _query parameters_ are relevant for grouping. See the [Query API Reference](/en/reference/api/query#parameters) for description. + +- [select](/en/reference/api/query#select) +- [groupingSessionCache](/en/reference/api/query#groupingsessioncache) +- [grouping.defaultMaxGroups](/en/reference/api/query#grouping.defaultmaxgroups) +- [grouping.defaultMaxHits](/en/reference/api/query#grouping.defaultmaxhits) +- [grouping.globalMaxGroups](/en/reference/api/query#grouping.globalmaxgroups) +- [grouping.defaultPrecisionFactor](/en/reference/api/query#grouping.defaultprecisionfactor) + +## Grouping Session Cache + +<Danger> + **Important:** The grouping session cache is **only useful if** the grouping expression uses default ordering. The **drawback** is that when `max` is specified in the grouping expression, it might cause inaccuracies in aggregated values such as `count`. It is recommended to test whether this is an issue or not, and if so, adjust the `precision` parameter to still get correct counts. +</Danger> + +The session cache stores intermediate grouping results in the content nodes when using multi-level grouping expressions, in order to speed up grouping at a potential loss of accuracy. This causes the query and grouping expression to be run only once. + +When having multi-level grouping expressions, the search query is normally re-run for each level. The drawback of this is that, with an expensive ranking function, the query will take more time than strictly necessary. + +## Aggregators + +Each level of grouping specifies a set of aggregates to collect for all documents that belong to that group (using the `output` operation): + +- The documents in a group, retrieved using a specified summary class +- The count of documents in a group +- The sum, average, min, max, xor or standard deviation of an expression +- Multiple quantiles of an expressions value + +When all arguments are numeric, the result type is resolved by looking at the argument types. If all arguments are longs, the result is a long. If at least one argument is a double, the result is a double. + +When using `order`, aggregators can also be used in expressions in order to get increased control over group sorting. This does not work with expressions that take attributes as an argument, unless the expression is enclosed within an aggregator. + +Using sum, max on a multivalued attribute: Doing an operation such as `output(sum(myarray))` will run the sum over each element value in each document. The result is the sum of sums of values. Similarly, `max(myarray)` will yield the maximal element over all elements in all documents, and so on. + +Compute quantiles by listing the desired quantile values (comma-separated) in brackets, followed by a comma and the expression (e.g., a field): + +```bash +all( group(city) each(output(quantiles([0.5], delivery_days) as(median_delivery_days) ) ) ) +``` +to compute the median, or +```bash +all( group(city) each(output(quantiles([0.5, 0.9], delivery_days))) ) +``` +This computes the median (p50) and 90th percentile (p90) time to delivery in days per city. Note that quantiles are computed using [KLL Sketch](https://datasketches.apache.org/docs/KLL/KLLSketch.html), so they are approximate. + +Multivalue fields, such as maps and arrays, can be used for grouping. However, using aggregation functions such as sum() on such fields can give misleading results. Assume a map from strings to integers (`map<string, int>`), where the strings are some sort of key to use for grouping. The following expression will provide the sum of the values for all keys: + +```bash +all( group(mymap.key) each(output(sum(mymap.value))) ) +``` + +and not the sum of the values within each key, as one would expect. It is still, however, possible to run the following expression to get the sum of values within a specific key: + +``` +all( group("my_group") each(output(sum(mymap{"foo"}))) ) +``` + +Refer to the system test for [grouping on struct and map types](https://github.com/vespa-engine/system-test/blob/master/tests/search/struct_and_map_types/struct_and_map_grouping.rb) for more examples. + +### Group list aggregators + +<table className="vespa-table"> + <thead> + <tr> + <th>Name</th> + <th>Description</th> + <th>Arguments</th> + <th>Result</th> + </tr> + </thead> + <tbody> + <tr> + <td>count</td> + <td>Counts the number of unique groups (as produced by <code>{`group`}</code>). Note that <code>{`count`}</code> operates independently of <code>{`max`}</code> and that this count is an estimate using HyperLogLog++ which is an algorithm for the count-distinct problem</td> + <td>None</td> + <td>Long</td> + </tr> + </tbody> +</table> +| +### Group aggregators + +<table className="vespa-table"> + <thead> + <tr> + <th>Name</th> + <th>Description</th> + <th>Arguments</th> + <th>Result</th> + </tr> + </thead> + <tbody> + <tr> + <td>count</td> + <td>Increments a long counter every time it is invoked</td> + <td>None</td> + <td>Long</td> + </tr> + <tr> + <td>sum</td> + <td>Sums the argument over all selected documents</td> + <td>Numeric</td> + <td>Numeric</td> + </tr> + <tr> + <td>avg</td> + <td>Computes the average over all selected documents</td> + <td>Numeric</td> + <td>Numeric</td> + </tr> + <tr> + <td>min</td> + <td>Keeps the minimum value of selected documents</td> + <td>Numeric</td> + <td>Numeric</td> + </tr> + <tr> + <td>max</td> + <td>Keeps the maximum value of selected documents</td> + <td>Numeric</td> + <td>Numeric</td> + </tr> + <tr> + <td>xor</td> + <td>XOR the values (their least significant 64 bits) of all selected documents</td> + <td>Any</td> + <td>Long</td> + </tr> + <tr> + <td>stddev</td> + <td>Computes the population standard deviation over all selected documents</td> + <td>Numeric</td> + <td>Double</td> + </tr> + <tr> + <td>quantiles</td> + <td>Computes one or multiple quantiles of the values of an expression. Quantiles must be a number between 0 and 1 inclusive.</td> + <td>[Numeric+], Expr</td> + <td>[{"quantile":Double,"value":Double}+]</td> + </tr> + </tbody> +</table> + + +### Hit aggregators + +<table className="vespa-table"> + <thead> + <tr> + <th>Name</th> + <th>Description</th> + <th>Arguments</th> + <th>Result</th> + </tr> + </thead> + <tbody> + <tr> + <td>summary</td> + <td>Produces a summary of the requested <a href="/en/reference/schemas/schemas#document-summary" aria-label="summary class">summary class</a></td> + <td>Name of summary class</td> + <td>Summary</td> + </tr> + </tbody> +</table> + +## Expressions + +### Arithmetic expressions + +<table className="vespa-table"> + <thead> + <tr> + <th>Name</th> + <th>Description</th> + <th>Arguments</th> + <th>Result</th> + </tr> + </thead> + <tbody> + <tr> + <td>add</td> + <td>Add the arguments together</td> + <td>Numeric+</td> + <td>Numeric</td> + </tr> + <tr> + <td>+</td> + <td>Add left and right argument</td> + <td>Numeric, Numeric</td> + <td>Numeric</td> + </tr> + <tr> + <td>mul</td> + <td>Multiply the arguments together</td> + <td>Numeric+</td> + <td>Numeric</td> + </tr> + <tr> + <td>*</td> + <td>Multiply left and right argument</td> + <td>Numeric, Numeric</td> + <td>Numeric</td> + </tr> + <tr> + <td>sub</td> + <td>Subtract second argument from first, third from result, etc</td> + <td>Numeric+</td> + <td>Numeric</td> + </tr> + <tr> + <td>-</td> + <td>Subtract right argument from left</td> + <td>Numeric, Numeric</td> + <td>Numeric</td> + </tr> + <tr> + <td>div</td> + <td>Divide first argument by second, result by third, etc</td> + <td>Numeric+</td> + <td>Numeric</td> + </tr> + <tr> + <td>/</td> + <td>Divide left argument by right</td> + <td>Numeric, Numeric</td> + <td>Numeric</td> + </tr> + <tr> + <td>mod</td> + <td>Modulo first argument by second, result by third, etc</td> + <td>Numeric+</td> + <td>Numeric</td> + </tr> + <tr> + <td>%</td> + <td>Modulo left argument by right</td> + <td>Numeric, Numeric</td> + <td>Numeric</td> + </tr> + <tr> + <td>neg</td> + <td>Negate argument</td> + <td>Numeric</td> + <td>Numeric</td> + </tr> + <tr> + <td>-</td> + <td>Negate right argument</td> + <td>Numeric</td> + <td>Numeric</td> + </tr> + </tbody> +</table> + +### Bitwise expressions + +<table className="vespa-table"> + <thead> + <tr> + <th>Name</th> + <th>Description</th> + <th>Arguments</th> + <th>Result</th> + </tr> + </thead> + <tbody> + <tr> + <td>and</td> + <td>AND the arguments in order</td> + <td>Long+</td> + <td>Long</td> + </tr> + <tr> + <td>or</td> + <td>OR the arguments in order</td> + <td>Long+</td> + <td>Long</td> + </tr> + <tr> + <td>xor</td> + <td>XOR the arguments in order</td> + <td>Long+</td> + <td>Long</td> + </tr> + </tbody> +</table> + +### String expressions + +<table className="vespa-table"> + <thead> + <tr> + <th>Name</th> + <th>Description</th> + <th>Arguments</th> + <th>Result</th> + </tr> + </thead> + <tbody> + <tr> + <td>strlen</td> + <td>Count the number of bytes in argument</td> + <td>String</td> + <td>Long</td> + </tr> + <tr> + <td>strcat</td> + <td>Concatenate arguments in order</td> + <td>String+</td> + <td>String</td> + </tr> + </tbody> +</table> + +### Type conversion expressions + +<table className="vespa-table"> + <thead> + <tr> + <th>Name</th> + <th>Description</th> + <th>Arguments</th> + <th>Result</th> + </tr> + </thead> + <tbody> + <tr> + <td>todouble</td> + <td>Convert argument to double</td> + <td>Any</td> + <td>Double</td> + </tr> + <tr> + <td>tolong</td> + <td>Convert argument to long</td> + <td>Any</td> + <td>Long</td> + </tr> + <tr> + <td>tostring</td> + <td>Convert argument to string</td> + <td>Any</td> + <td>String</td> + </tr> + <tr> + <td>toraw</td> + <td>Convert argument to raw</td> + <td>Any</td> + <td>Raw</td> + </tr> + </tbody> +</table> + +### Raw data expressions + +<table className="vespa-table"> + <thead> + <tr> + <th>Name</th> + <th>Description</th> + <th>Arguments</th> + <th>Result</th> + </tr> + </thead> + <tbody> + <tr> + <td>cat</td> + <td>Cat the binary representation of the arguments together</td> + <td>Any+</td> + <td>Raw</td> + </tr> + <tr> + <td>md5</td> + <td>Does an MD5 over the binary representation of the argument, and keeps the lowest 'width' bits</td> + <td>Any, Numeric(width)</td> + <td>Raw</td> + </tr> + <tr> + <td>xorbit</td> + <td>Does an XOR of 'width' bits over the binary representation of the argument. Width is rounded up to a multiple of 8</td> + <td>Any, Numeric(width)</td> + <td>Raw</td> + </tr> + </tbody> +</table> + +### Accessor expressions + +<table className="vespa-table"> + <thead> + <tr> + <th>Name</th> + <th>Description</th> + <th>Arguments</th> + <th>Result</th> + </tr> + </thead> + <tbody> + <tr> + <td>relevance</td> + <td>Return the computed rank of a document</td> + <td>None</td> + <td>Double</td> + </tr> + <tr> + <td><attribute-name></td> + <td>Return the value of the named attribute</td> + <td>None</td> + <td>Any</td> + </tr> + <tr> + <td>array.at</td> + <td>Array element access. The expression <code>{`array.at(myarray, idx)`}</code> returns one value per document by evaluating the <code>{`idx`}</code> expression and using it as an index into the array. The expression can then be used to build bigger expressions such as <code>{`output(sum(array.at(myarray, 0)))`}</code> which will sum the first element in the array of each document. <br/>- The <code>{`idx`}</code> expression is capped to <code>{`[0, size(myarray)-1]`}</code><br/>- If > array size, the last element is returned<br/>- If < 0, the first element is returned</td> + <td>Array, Numeric</td> + <td>Any</td> + </tr> + <tr> + <td>interpolatedlookup</td> + <td colSpan={3}>Counts elements in a sorted array that are less than an expression, with linear interpolation if the expression is between element values. The operation <code>{`interpolatedlookup(myarray, expr)`}</code> is intended for generic graph/function lookup. The data in <code>{`myarray`}</code> should be numerical values sorted in ascending order. The operation will then scan from the start of the array to find the position where the element values become equal to (or greater than) the value of the <code>{`expr`}</code> lookup argument, and return the index of that position. <br/> When the lookup argument's value is between two consecutive array element values, the returned position will be a linear interpolation between their respective indexes. The return value is always in the range <code>{`[0, size(myarray)-1]`}</code> of the valid index values for an array.<br/>Assume <code>{`myarray`}</code> is a sorted array of type <code>{`array<double>`}</code> in each document: The expression <code>{`interpolatedlookup(myarray, 4.2)`}</code> is now a per-document expression that first evaluates the lookup argument, here a constant expression 4.2, and then looks at the contents of <code>{`myarray`}</code> in the document. The scan starts at the first element and proceeds until it hits an element value greater than 4.2 in the array. This means that:<br/>- If the first element in the array is greater than 4.2, the expression returns 0<br/>- If the first element in the array is exactly 4.2, the expression still returns 0<br/>- If the first element in the array is 1.7 while the **second** element value is exactly 4.2, the expression returns 1.0 - the index of the second element<br/>- If **all** the elements in the array are less than 4.2, the last valid array index <code>{`size(myarray)-1`}</code> is returned<br/>- If the first 5 elements in the array have values smaller than the lookup argument, and the lookup argument is halfway between the fifth and sixth element, a value of 4.5 is returned - halfway between the array indexes of the fifth and sixth elements<br/>- Similarly, if the elements in the array are <code>{`{0, 1, 2, 4, 8}`}</code> then passing a lookup argument of "5" would return 3.25 (linear interpolation between <code>{`indexOf(4)==3`}</code> and <code>{`indexOf(8)==4`}</code>)<br/></td> + </tr> + </tbody> +</table> + + + +### Bucket expressions + +<table className="vespa-table"> + <thead> + <tr> + <th>Name</th> + <th>Description</th> + <th>Arguments</th> + <th>Result</th> + </tr> + </thead> + <tbody> + <tr> + <td>fixedwidth</td> + <td>Maps the value of the first argument into consecutive buckets whose width equals the second argument</td> + <td>Any, Numeric</td> + <td>NumericBucketList</td> + </tr> + <tr> + <td>predefined</td> + <td>Maps the value of the first argument into the given buckets. <br/>- Standard mathematical start and end specifiers may be used to define the width of a <code>{`bucket`}</code>. The <code>{`(`}</code> and <code>{`)`}</code> evaluates to <code>{`[`}</code> and <code>{`>`}</code> by default. <br/>- The buckets assume the type of the start/end specifiers (<code>{`string`}</code>, <code>{`long`}</code>, <code>{`double`}</code> or <code>{`raw`}</code>). Values are converted to this type before being compared with these specifiers (e.g., <code>{`double`}</code> values are rounded to the nearest integer for buckets of type <code>{`long`}</code>). <br/>- The end specifier can be skipped. The buckets <code>{`bucket(3)`}</code>/<code>{`bucket[3]`}</code> are the same as <code>{`bucket[3,4>`}</code>. This is allowed for string expressions as well; <code>{`bucket("c")`}</code> is identical to <code>{`bucket["c", "c ">`}</code>.</td> + <td>Any, Bucket+</td> + <td>BucketList</td> + </tr> + </tbody> +</table> + +### Time expressions + +<table className="vespa-table"> + <thead> + <tr> + <th>Name</th> + <th>Description</th> + <th>Arguments</th> + <th>Result</th> + </tr> + </thead> + <tbody> + <tr> + <td>time.dayofmonth</td> + <td>Returns the day of month (1-31) for the given timestamp</td> + <td>Long</td> + <td>Long</td> + </tr> + <tr> + <td>time.dayofweek</td> + <td>Returns the day of week (0-6) for the given timestamp, Monday being 0</td> + <td>Long</td> + <td>Long</td> + </tr> + <tr> + <td>time.dayofyear</td> + <td>Returns the day of year (0-365) for the given timestamp</td> + <td>Long</td> + <td>Long</td> + </tr> + <tr> + <td>time.hourofday</td> + <td>Returns the hour of day (0-23) for the given timestamp</td> + <td>Long</td> + <td>Long</td> + </tr> + <tr> + <td>time.minuteofhour</td> + <td>Returns the minute of hour (0-59) for the given timestamp</td> + <td>Long</td> + <td>Long</td> + </tr> + <tr> + <td>time.monthofyear</td> + <td>Returns the month of year (1-12) for the given timestamp</td> + <td>Long</td> + <td>Long</td> + </tr> + <tr> + <td>time.secondofminute</td> + <td>Returns the second of minute (0-59) for the given timestamp</td> + <td>Long</td> + <td>Long</td> + </tr> + <tr> + <td>time.year</td> + <td>Returns the full year (e.g. 2009) of the given timestamp</td> + <td>Long</td> + <td>Long</td> + </tr> + <tr> + <td>time.date</td> + <td>Returns the date (e.g. 2009-01-10) of the given timestamp</td> + <td>Long</td> + <td>Long</td> + </tr> + </tbody> +</table> + +### List expressions + +<table className="vespa-table"> + <thead> + <tr> + <th>Name</th> + <th>Description</th> + <th>Arguments</th> + <th>Result</th> + </tr> + </thead> + <tbody> + <tr> + <td>size</td> + <td>Return the number of elements in the argument if it is a list. If not return 1</td> + <td>Any</td> + <td>Long</td> + </tr> + <tr> + <td>sort</td> + <td>Sort the elements in the argument in ascending order if the argument is a list. If not, it is a NOP</td> + <td>Any</td> + <td>Any</td> + </tr> + <tr> + <td>reverse</td> + <td>Reverse the elements in the argument if the argument is a list. If not, it is a NOP</td> + <td>Any</td> + <td>Any</td> + </tr> + </tbody> +</table> + +### Other expressions + +<table className="vespa-table"> + <thead> + <tr> + <th>Name</th> + <th>Description</th> + <th>Arguments</th> + <th>Result</th> + </tr> + </thead> + <tbody> + <tr> + <td>zcurve.x</td> + <td>Returns the X component of the given <a href="https://en.wikipedia.org/wiki/Z-order_curve" aria-label="zcurve">zcurve</a> encoded 2d point. All fields of type "position" have an accompanying "<fieldName>_zcurve" attribute that can be decoded using this expression, e.g. <code>{`zcurve.x(foo_zcurve)`}</code></td> + <td>Long</td> + <td>Long</td> + </tr> + <tr> + <td>zcurve.y</td> + <td>Returns the Y component of the given zcurve encoded 2d point</td> + <td>Long</td> + <td>Long</td> + </tr> + <tr> + <td>geo_distance</td> + <td>Computes the great-circle distance from a <a href="/en/reference/schemas/schemas#position" aria-label="position">position</a> field to a given point. The unit suffix <code>{`.km`}</code> or <code>{`.miles`}</code> selects the output unit. Works on both <code>{`position`}</code> and <code>{`array<position>`}</code> fields. For arrays, the minimum distance across all positions in the document is returned. <pre><code>{`( group(fixedwidth(geo_distance(attribute(location), 63.4, 10.4).km, 10)) each(output(count())) )`}</code></pre> Available since Vespa 8.664.22 .</td> + <td>Attribute(position), Double(lat), Double(lng)</td> + <td>Double</td> + </tr> + <tr> + <td>uca</td> + <td>Converts the attribute string using <a href="https://www.unicode.org/reports/tr10/" aria-label="unicode collation algorithm">unicode collation algorithm</a>. Groups are sorted using locale-aware sorting, with the default and primary strength values, respectively: <pre><code>{`( group(s) order(max(uca(s, "sv"))) each(output(count())) )`}</code></pre> <pre><code>{`( group(s) order(max(uca(s, "sv", "PRIMARY"))) each(output(count())) )`}</code></pre></td> + <td>Any, Locale(String), Strength(String)</td> + <td>Raw</td> + </tr> + </tbody> +</table> + +### Single argument standard mathematical expressions + These are the standard mathematical functions as found in the Java [Math](https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html) class. +<table className="vespa-table"> + <thead> + <tr> + <th>Name</th> + <th>Description</th> + <th>Arguments</th> + <th>Result</th> + </tr> + </thead> + <tbody> + <tr> + <td>math.exp</td> + <td>&nbsp;</td> + <td>Double</td> + <td>Double</td> + </tr> + <tr> + <td>math.log</td> + <td>&nbsp;</td> + <td>Double</td> + <td>Double</td> + </tr> + <tr> + <td>math.log1p</td> + <td>&nbsp;</td> + <td>Double</td> + <td>Double</td> + </tr> + <tr> + <td>math.log10</td> + <td>&nbsp;</td> + <td>Double</td> + <td>Double</td> + </tr> + <tr> + <td>math.sqrt</td> + <td>&nbsp;</td> + <td>Double</td> + <td>Double</td> + </tr> + <tr> + <td>math.cbrt</td> + <td>&nbsp;</td> + <td>Double</td> + <td>Double</td> + </tr> + <tr> + <td>math.sin</td> + <td>&nbsp;</td> + <td>Double</td> + <td>Double</td> + </tr> + <tr> + <td>math.cos</td> + <td>&nbsp;</td> + <td>Double</td> + <td>Double</td> + </tr> + <tr> + <td>math.tan</td> + <td>&nbsp;</td> + <td>Double</td> + <td>Double</td> + </tr> + <tr> + <td>math.asin</td> + <td>&nbsp;</td> + <td>Double</td> + <td>Double</td> + </tr> + <tr> + <td>math.acos</td> + <td>&nbsp;</td> + <td>Double</td> + <td>Double</td> + </tr> + <tr> + <td>math.atan</td> + <td>&nbsp;</td> + <td>Double</td> + <td>Double</td> + </tr> + <tr> + <td>math.sinh</td> + <td>&nbsp;</td> + <td>Double</td> + <td>Double</td> + </tr> + <tr> + <td>math.cosh</td> + <td>&nbsp;</td> + <td>Double</td> + <td>Double</td> + </tr> + <tr> + <td>math.tanh</td> + <td>&nbsp;</td> + <td>Double</td> + <td>Double</td> + </tr> + <tr> + <td>math.asinh</td> + <td>&nbsp;</td> + <td>Double</td> + <td>Double</td> + </tr> + <tr> + <td>math.acosh</td> + <td>&nbsp;</td> + <td>Double</td> + <td>Double</td> + </tr> + <tr> + <td>math.atanh</td> + <td>&nbsp;</td> + <td>Double</td> + <td>Double</td> + </tr> + </tbody> +</table> + +### Dual argument standard mathematical expressions + +<table className="vespa-table"> + <thead> + <tr> + <th>Name</th> + <th>Description</th> + <th>Arguments</th> + <th>Result</th> + </tr> + </thead> + <tbody> + <tr> + <td>math.pow</td> + <td>Return X^Y.</td> + <td>Double, Double</td> + <td>Double</td> + </tr> + <tr> + <td>math.hypot</td> + <td>Return length of hypotenuse given X and Y sqrt(X^2 + Y^2)</td> + <td>Double, Double</td> + <td>Double</td> + </tr> + </tbody> +</table> + +## Filters + +### String filters + +<table className="vespa-table"> + <thead> + <tr> + <th>Name</th> + <th>Description</th> + <th>Arguments</th> + <th>Result</th> + </tr> + </thead> + <tbody> + <tr> + <td>regex</td> + <td>Matches a field against a regular expression string.</td> + <td>String, Expression</td> + <td>Bool</td> + </tr> + </tbody> +</table> + +### Numeric filters + +<table className="vespa-table"> + <thead> + <tr> + <th>Name</th> + <th>Description</th> + <th>Arguments</th> + <th>Result</th> + </tr> + </thead> + <tbody> + <tr> + <td>range</td> + <td>Matches when a field is between a lower and upper bound.</td> + <td>Numeric, Numeric, Expression, Bool?, Bool?</td> + <td>Bool</td> + </tr> + </tbody> +</table> + +### Boolean filters + +<table className="vespa-table"> + <thead> + <tr> + <th>Name</th> + <th>Description</th> + <th>Arguments</th> + <th>Result</th> + </tr> + </thead> + <tbody> + <tr> + <td>istrue</td> + <td>Matches when a boolean expression evaluates to true.</td> + <td>Expression</td> + <td>Bool</td> + </tr> + </tbody> +</table> + +### Predicate filters + +<table className="vespa-table"> + <thead> + <tr> + <th>Name</th> + <th>Description</th> + <th>Arguments</th> + <th>Result</th> + </tr> + </thead> + <tbody> + <tr> + <td>and</td> + <td>Logical <code>{`and`}</code> between the arguments.</td> + <td>Filter, Filter</td> + <td>Bool</td> + </tr> + <tr> + <td>not</td> + <td>Logical <code>{`not`}</code> on the argument.</td> + <td>Filter</td> + <td>Bool</td> + </tr> + <tr> + <td>or</td> + <td>Logical <code>{`or`}</code> between the arguments.</td> + <td>Filter, Filter</td> + <td>Bool</td> + </tr> + </tbody> +</table> + +## Grouping language grammar + +```bash expandable +request ::= "all(" operations ")" +group ::= ( "all" | "each") "(" operations ")" ["as" "(" identifier ")"] +operations ::= ["group" "(" exp ")"] + ( ( "alias" "(" identifier "," exp ")" ) | + ( filter ) | + ( "max" "(" ( number | "inf" ) ")" ) | + ( "order" "(" expList | aggrList ")" ) | + ( "output" "(" aggrList ")" ) | + ( "precision" "(" number ")" ) )* + group* +aggrList ::= aggr ( "," aggr )* +aggr ::= ( ( "count" "(" ")" ) | + ( "sum" "(" exp ")" ) | + ( "avg" "(" exp ")" ) | + ( "max" "(" exp ")" ) | + ( "min" "(" exp ")" ) | + ( "xor" "(" exp ")" ) | + ( "stddev" "(" exp ")" ) | + ( "quantiles" "(" "[" number ( "," number )* "]" "," exp ")" ) | + ( "summary" "(" [identifier] ")" ) ) + ["as" "(" identifier ")"] +expList ::= exp ( "," exp )* +exp ::= ( "+" | "-") ( "$" identifier ["=" math] ) | ( math ) | ( aggr ) +filter ::= ( "filter" | "keep" ) "(" filterExp ")" +filterExp ::= filterAnd ( "or" filterAnd )* +filterAnd ::= filterNot ( "and" filterNot )* +filterNot ::= "not" filterNot | filterPrim +filterPrim ::= ( "(" filterExp ")" ) | + ( "regex" "(" string "," exp ")" ) | + ( "range" "(" number "," number "," exp ["," boolean "," boolean] ")" ) | + ( "istrue" "(" exp ")" ) +math ::= value [( "+" | "-" | "*" | "/" | "%" ) value] +value ::= ( "(" exp ")" ) | + ( "add" "(" expList ")" ) | + ( "and" "(" expList ")" ) | + ( "cat" "(" expList ")" ) | + ( "div" "(" expList ")" ) | + ( "docidnsspecific" "(" ")" ) | + ( "fixedwidth" "(" exp "," number ")" ) | + ( "geo_distance" "(" "attribute" "(" attributeName ")" "," number "," number ")" "." ( "km" | "miles" ) ) | + ( "interpolatedlookup" "(" attributeName "," exp ")") | + ( "math" "." ( + ( + "exp" | "log" | "log1p" | "log10" | "sqrt" | "cbrt" | + "sin" | "cos" | "tan" | "asin" | "acos" | "atan" | + "sinh" | "cosh" | "tanh" | "asinh" | "acosh" | "atanh" + ) "(" exp ")" | + ( "pow" | "hypot" ) "(" exp "," exp ")" + )) | + ( "max" "(" expList ")" ) | + ( "md5" "(" exp "," number "," number ")" ) | + ( "min" "(" expList ")" ) | + ( "mod" "(" expList ")" ) | + ( "mul" "(" expList ")" ) | + ( "or" "(" expList ")" ) | + ( "predefined" "(" exp "," "(" bucket ( "," bucket )* ")" ")" ) | + ( "reverse" "(" exp ")" ) | + ( "relevance" "(" ")" ) | + ( "sort" "(" exp ")" ) | + ( "strcat" "(" expList ")" ) | + ( "strlen" "(" exp ")" ) | + ( "size" "(" exp")" ) | + ( "sub" "(" expList ")" ) | + ( "time" "." ( "date" | "year" | "monthofyear" | "dayofmonth" | "dayofyear" | "dayofweek" | + "hourofday" | "minuteofhour" | "secondofminute" ) "(" exp ")" ) | + ( "todouble" "(" exp ")" ) | + ( "tolong" "(" exp ")" ) | + ( "tostring" "(" exp ")" ) | + ( "toraw" "(" exp ")" ) | + ( "uca" "(" exp "," string ["," string] ")" ) | + ( "xor" "(" expList ")" ) | + ( "xorbit" "(" exp "," number ")" ) | + ( "zcurve" "." ( "x" | "y" ) "(" exp ")" ) | + ( attributeName "." "at" "(" number ")") | + ( attributeName ) +bucket ::= "bucket" ( "(" | "[" | "<" ) + ( "-inf" | rawvalue | number | string ) + ["," ( "inf" | rawvalue | number | string )] + ( ")" | "]" | ">" ) +rawvalue ::= "{" ( ( string | number ) "," )* "}" +``` \ No newline at end of file diff --git a/en/reference/querying/json-query-language.md b/en/reference/querying/json-query-language.mdx similarity index 85% rename from en/reference/querying/json-query-language.md rename to en/reference/querying/json-query-language.mdx index 81d4b93d32..486e9e8459 100644 --- a/en/reference/querying/json-query-language.md +++ b/en/reference/querying/json-query-language.mdx @@ -1,12 +1,10 @@ --- -# Copyright Vespa.ai. All rights reserved. -title: "Select Query Reference" -redirect_from: - - /en/reference/select-reference +title: "JSON Select Query Reference" +sidebarTitle: "Select" --- -This document describes what the `select` parameter is and gives a few examples on how to use it. Refer to the [Query API](../../querying/query-api.html) for how to execute queries with POST. +This document describes what the `select` parameter is and gives a few examples on how to use it. Refer to the [Query API](/en/querying/query-api) for how to execute queries with POST. The query has JSON syntax, and can be used with queries that are executed with HTTP POST. The `select` parameter is equivalent with YQL, and can be used instead of, but not together with YQL. Nor can it be used together with the `query` parameter. @@ -44,14 +42,14 @@ curl -H "Content-Type: application/json" \ ### Where -Unlike the sql-like syntax in [YQL](../../querying/query-language.html), the *where* queries are written in a tree syntax. By combining YQLs functions and arguments, queries equivalent with YQL can be written in JSON. +Unlike the sql-like syntax in [YQL](/en/reference/querying/grouping-language), the *where* queries are written in a tree syntax. By combining YQLs functions and arguments, queries equivalent with YQL can be written in JSON. #### Formal structure Functions are nested like this: -``` +```json FUNCTION : { "children" : [ argument, argument,..], "attributes" : {annotations} @@ -60,7 +58,7 @@ FUNCTION : { or like this, by moving the `children`-key up, if attributes are not in use with the function: -``` +```json FUNCTION : [ argument, argument, @@ -73,7 +71,9 @@ with the `where` parameter in JSON. Let's take a look at this yql: `select * from sources * where default contains foo and rank(a contains "A", b contains "B")`, which will create the following query tree: +<Frame> <img src="/assets/img/querytree.svg" width="737px" height="auto" alt="Example query tree" /> +</Frame> The tree above can be written with the 'where' parameter, like this: @@ -94,10 +94,10 @@ which is equivalent with the YQL. ### Fields -Available since {% include version.html version="8.680.18" %}. +Available since Vespa `8.680.18`. The `fields` parameter restricts which -[summary fields](../../querying/document-summaries.html#selecting-summary-fields-in-yql) +[summary fields](/en/querying/document-summaries#selecting-summary-fields-in-yql) are included in each hit. It is a JSON array of field names, and is equivalent to the field list in a YQL `select` clause. @@ -117,12 +117,12 @@ Equivalent JSON: ``` If `fields` is omitted or empty, all fields of the chosen -[document summary class](../../querying/document-summaries.html) are returned. +[document summary class](/en/querying/document-summaries#selecting-summary-fields-in-yql) are returned. ### Grouping -One or more [grouping statements](../../querying/grouping.html) can be set as a JSON array in the `grouping` field. +One or more [grouping statements](/en/querying/grouping) can be set as a JSON array in the `grouping` field. Each array item is a grouping statement represented as JSON where - Each grouping function is represented by a JSON object where the name of the function is the field name and the value is the function content. @@ -132,7 +132,7 @@ Examples: Grouping statement: -``` +```json | all(group(time.year(a)) each(output(count()) all(group(time.monthofyear(a)) each(output(count()))) ``` @@ -155,7 +155,7 @@ equivalent JSON `grouping`-argument: Grouping statement: -``` +```bash all(group(predefined(foo, bucket[1, 2>, bucket[3, 4>))) ``` equivalent JSON `grouping`-argument: @@ -174,7 +174,7 @@ equivalent JSON `grouping`-argument: ### Other query parameters -[Query parameters](../api/query.html) not specific to the Select or YQL syntax will work as well. For example, to search for everything (`"where": true`) in the `music` document type: +[Query parameters](/en/reference/api/query) not specific to the Select or YQL syntax will work as well. For example, to search for everything (`"where": true`) in the `music` document type: ```json { @@ -239,8 +239,6 @@ A more complex example: } ``` - ---- ### Examples with the different functions @@ -258,7 +256,7 @@ Format of this in JSON: ##### CONTAINS with text() YQL: `where title contains ({language:'en'}text('hello world'))`. -Format of this [text() operator with annotations](yql.html#text) in JSON: +Format of this [text() operator with annotations](/en/reference/querying/yql#text) in JSON: ```json "where" : { @@ -292,16 +290,33 @@ Format of this in JSON: The range query accepts the following parameters: -<table class="table table-striped"> -<tr><td><b>Operator</b></td><td><b>Description</b></td></tr> -<tr><td>≥</td><td>Greater-than or equal to</td></tr> -<tr><td>></td><td>Greater-than</td></tr> -<tr><td><</td><td>Less-than</td></tr> -<tr><td>≤</td><td>Less-than or equal to</td></tr> -</table> - - +<table className="vespa-table"> + <thead> + <tr> + <th>Operator</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>≥</td> + <td>Greater-than or equal to</td> + </tr> + <tr> + <td><code>{`>`}</code></td> + <td>Greater-than</td> + </tr> + <tr> + <td><code>{`<`}</code></td> + <td>Less-than</td> + </tr> + <tr> + <td>≤</td> + <td>Less-than or equal to</td> + </tr> + </tbody> +</table> YQL: `where range(field, 0, 500)`. @@ -363,7 +378,7 @@ Format of this in JSON: Formal structure: -``` +```json "where" : { "and_not" : [ <Statement>, @@ -593,7 +608,7 @@ YQL: `where predicate(predicate_field,{"gender":"Female"},{"age":20L})`. Format of this in JSON: -``` +```js "where" : { "predicate" : [ "predicate_field", diff --git a/en/reference/querying/page-result-format.html b/en/reference/querying/page-result-format.html deleted file mode 100644 index 189be9c5d6..0000000000 --- a/en/reference/querying/page-result-format.html +++ /dev/null @@ -1,281 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "The 'page' result format" -redirect_from: -- /en/reference/page-result-format.html ---- - - -<p> - This document describes the <code>page</code> result format returned by Vespa. - This result format is used when <a href="../api/query.html#presentation.format">presentation.format</a> - is set to <code>page</code>. - This format is usually used with <a href="../../querying/page-templates.html">page templates</a>. -</p><p> - The tags of the format are described below. - Subtags will be rendered in the order listed here. - The format is <em>open</em>, - all parsers must ignore attributes and child tags not mentioned here. -</p> - - - -<h2 id="page"><page></h2> -<p> - The root tag of a page result: The single top-level section of the page. -</p> -<table class="table"> - <thead> - <tr> - <th>Attribute</th> - <th>Description</th> - <th>Present</th> - </tr> - </thead> - <tbody> - <tr> - <td>version</td> - <td>The version of this format - currently 1.0.</td> - <td>Always</td> - </tr> - <tr> - <td>layout</td> - <td>The name of the top-level layout to use for this page.</td> - <td>If specified in the page template used.</td> - </tr> - </tbody> -</table> -<p> - For regular permissible subtags, refer to <a href="#section">section</a>. -</p> - - - -<h2 id="section"><section></h2> -<p> - A layout "box" in a page. -</p> -<table class="table"> - <thead> - <tr> - <th>Attribute</th> - <th>Description</th> - <th>Present</th> - </tr> - </thead> - <tbody> - <tr> - <td>id</td> - <td>The id of this section.</td> - <td>If specified in the page template used.</td> - </tr> - <tr> - <td>layout</td> - <td>The name of the top-level layout to use for this page.</td> - <td>If specified in the page template used.</td> - </tr> - <tr> - <td>region</td> - <td>The id of the region in the layout of the parent section where this should be placed.</td> - <td>If specified in the page template used.</td> - </tr> - </tbody> -</table> -<table class="table"> - <thead> - <tr> - <th>Subtag</th> - <th>Description</th> - <th>Present</th> - </tr> - </thead> - <tbody> - <tr> - <td><a href="#section">section</a></td> - <td>A nested section of this page</td> - <td>Zero or more.</td> - </tr> - <tr> - <td><a href="#renderer">renderer</a></td> - <td>The name of the rendering to use for this section.</td> - <td>Zero or more.</td> - </tr> - <tr> - <td><a href="#source">source</a></td> - <td>Used to specify where to fetch the content of this section if it is not sent with this page in a content tag.</td> - <td>One or zero.</td> - </tr> - <tr> - <td><a href="#content">content</a></td> - <td>Contains some "payload" of this page - a set of <a href="#hit">hit</a> instances</td> - <td>One if this section has inlined content, zero otherwise.</td> - </tr> - </tbody> -</table> - - - -<h2 id="renderer"><renderer></h2> -<p> - The way this section, or some of its content should be rendered. -</p> -<table class="table"> - <thead> - <tr> - <th>Attribute</th> - <th>Description</th> - <th>Present</th> - </tr> - </thead> - <tbody> - <tr><td>for<td>The name of the content source which should use this renderer<td>If this is not present, the renderer should be used for the entire section.</tr> - </tbody> -</table> -<table class="table"> - <thead> - <tr> - <th>Subtag</th> - <th>Description</th> - <th>Present</th> - </tr> - </thead> - <tbody> - <tr><td>parameter<td>A parameter to this renderer<td>Zero or more</tr> - </tbody> -</table> - - - -<h2 id="source"><source></h2> -<p> -The source to be used to fetch the content of a section, -if it is not sent as inline <a href="#content">content</a>. -</p> -<table class="table"> - <thead> - <tr> - <th>Attribute</th> - <th>Description</th> - <th>Present</th> - </tr> - </thead> - <tbody> - <tr><td>url<td>The url at which the content should be fetched.<td>Always.</tr> - </tbody> -</table> -<table class="table"> - <thead> - <tr> - <th>Subtag</th> - <th>Description</th> - <th>Present</th> - </tr> - </thead> - <tbody> - <tr><td>parameter<td>A parameter to use when fetching this content.<td>Zero or more</tr> - </tbody> -</table> - - - -<h2 id="content"><content></h2> -<p> - The content to render in a section. -</p> -<table class="table"> - <thead> - <tr> - <th>Subtag</th> - <th>Description</th> - <th>Present</th> - </tr> - </thead> - <tbody> - <tr><td><a href="#hit">hit</a><td>A content hit.<td>Zero or more.</tr> - <tr><td><a href="#group">group</a><td>A group of content hits.<td>Zero or more.</tr> - </tbody> -</table> - - - -<h2 id="hit"><hit></h2> -<p> - A single result content item. -</p> -<table class="table"> - <thead> - <tr> - <th>Attribute</th> - <th>Description</th> - <th>Present</th> - </tr> - </thead> - <tbody> - <tr><td>relevance<td>The relevance of this item - usually a normalized number between 0 and 1.<td>Always</tr> - <tr><td>source<td>The name of the source producing this hit.<td>Always</tr> - <tr><td>type<td>A space-separated list of type identifiers of this hit.<td>If a type is set in the hit.</tr> - </tbody> -</table> -<p>Subtags:</p> -<p> -Hits have one subtag for every field they contain, -where the field name is the name of the tag and the toString of the field content is the content of the tag. -</p> - - - -<h2 id="group"><group></h2> -<p> -A <a href="#hit">hit</a> which contains nested hits. Used to organize hits hierarchically. -Has the name attributes and subtags as <a href="#hit">hit</a>, -but may also contain nested <a href="#hit">hit</a> and <a href="#group">group</a> tags. -</p> - - - -<h2 id="example">Example</h2> -<p>A page which should be rendered with two columns on top. -<pre> -<page version="1.0"> - - <renderer name="two-column"/> - - <section region="left"> - <source url="http://host:port/resource/[news article id]"/> - <renderer name="articleBodyRenderer"> - <parameter name="color">blue</parameter> - </renderer> - </section> - - <section region="right"> - <renderer name="multi-item-column"> - <parameter name="items">3</parameter> - </renderer> - <section region="1"> - <renderer for="newsImage" name="newsImageRenderer"/> - <renderer for="news" name="articleRenderer"/> - <renderer for="image" name="imageRenderer"/> - <content> - <hit relevance="1.0" source="news"> - <id>news-1</id> - </hit> - <hit relevance="0.5" source="news"> - <id>news-2</id> - </hit> - </content> - </section> - <section region="2"> - <source url="http://host:port/consumption-widget"/> - <renderer name="identityRenderer"/> - </section> - <section region="3"> - <renderer name="htmlRenderer"/> - <content> - <hit relevance="1.0" source="htmlSource"> - <id>htmlSource-1</id> - </hit> - </content> - </section> - </section> -</page> -</pre> diff --git a/en/reference/querying/page-result-format.mdx b/en/reference/querying/page-result-format.mdx new file mode 100644 index 0000000000..5546f7df22 --- /dev/null +++ b/en/reference/querying/page-result-format.mdx @@ -0,0 +1,289 @@ +--- +sidebarTitle: "The page result format" +title: "The 'page' result format" +--- + +This document describes the `page` result format returned by Vespa. This result format is used when [presentation.format](/en/reference/api/query#presentation.format) is set to `page`. This format is usually used with [page templates](/en/reference/querying/page-templates). + +The tags of the format are described below. Subtags will be rendered in the order listed here. The format is _open_, all parsers must ignore attributes and child tags not mentioned here. + +## \<page\> + +The root tag of a page result: The single top-level section of the page. + +<table className="vespa-table"> + <thead> + <tr> + <th>Attribute</th> + <th>Description</th> + <th>Present</th> + </tr> + </thead> + <tbody> + <tr> + <td>version</td> + <td>The version of this format - currently 1.0.</td> + <td>Always</td> + </tr> + <tr> + <td>layout</td> + <td>The name of the top-level layout to use for this page.</td> + <td>If specified in the page template used.</td> + </tr> + </tbody> +</table> + +For regular permissible subtags, refer to [section](#<section>). + +## \<section\> + +A layout "box" in a page. + +<table className="vespa-table"> + <thead> + <tr> + <th>Attribute</th> + <th>Description</th> + <th>Present</th> + </tr> + </thead> + <tbody> + <tr> + <td>id</td> + <td>The id of this section.</td> + <td>If specified in the page template used.</td> + </tr> + <tr> + <td>layout</td> + <td>The name of the top-level layout to use for this page.</td> + <td>If specified in the page template used.</td> + </tr> + <tr> + <td>region</td> + <td>The id of the region in the layout of the parent section where this should be placed.</td> + <td>If specified in the page template used.</td> + </tr> + </tbody> +</table> + +<table className="vespa-table"> + <thead> + <tr> + <th>Subtag</th> + <th>Description</th> + <th>Present</th> + </tr> + </thead> + <tbody> + <tr> + <td><a href="#<section>" aria-label="section">section</a></td> + <td>A nested section of this page</td> + <td>Zero or more.</td> + </tr> + <tr> + <td><a href="#<renderer>" aria-label="renderer">renderer</a></td> + <td>The name of the rendering to use for this section.</td> + <td>Zero or more.</td> + </tr> + <tr> + <td><a href="#<source>" aria-label="source">source</a></td> + <td>Used to specify where to fetch the content of this section if it is not sent with this page in a content tag.</td> + <td>One or zero.</td> + </tr> + <tr> + <td><a href="#<content>" aria-label="content">content</a></td> + <td>Contains some "payload" of this page - a set of <a href="#<hit>" aria-label="hit">hit</a> instances</td> + <td>One if this section has inlined content, zero otherwise.</td> + </tr> + </tbody> +</table> + +## \<renderer\> + +The way this section, or some of its content should be rendered. + +<table className="vespa-table"> + <thead> + <tr> + <th>Attribute</th> + <th>Description</th> + <th>Present</th> + </tr> + </thead> + <tbody> + <tr> + <td>for</td> + <td>The name of the content source which should use this renderer</td> + <td>If this is not present, the renderer should be used for the entire section.</td> + </tr> + </tbody> +</table> + +<table className="vespa-table"> + <thead> + <tr> + <th>Subtag</th> + <th>Description</th> + <th>Present</th> + </tr> + </thead> + <tbody> + <tr> + <td>parameter</td> + <td>A parameter to this renderer</td> + <td>Zero or more</td> + </tr> + </tbody> +</table> + +## \<source\> + +The source to be used to fetch the content of a section, if it is not sent as inline [content](#<content>). + +<table className="vespa-table"> + <thead> + <tr> + <th>Attribute</th> + <th>Description</th> + <th>Present</th> + </tr> + </thead> + <tbody> + <tr> + <td>url</td> + <td>The url at which the content should be fetched.</td> + <td>Always.</td> + </tr> + </tbody> +</table> + +<table className="vespa-table"> + <thead> + <tr> + <th>Subtag</th> + <th>Description</th> + <th>Present</th> + </tr> + </thead> + <tbody> + <tr> + <td>parameter</td> + <td>A parameter to use when fetching this content.</td> + <td>Zero or more</td> + </tr> + </tbody> +</table> + +## \<content\> + +The content to render in a section. + +<table className="vespa-table"> + <thead> + <tr> + <th>Subtag</th> + <th>Description</th> + <th>Present</th> + </tr> + </thead> + <tbody> + <tr> + <td><a href="#<hit>" aria-label="hit">hit</a></td> + <td>A content hit.</td> + <td>Zero or more.</td> + </tr> + <tr> + <td><a href="#<group>" aria-label="group">group</a></td> + <td>A group of content hits.</td> + <td>Zero or more.</td> + </tr> + </tbody> +</table> + +## \<hit\> + +A single result content item. + +<table className="vespa-table"> + <thead> + <tr> + <th>Attribute</th> + <th>Description</th> + <th>Present</th> + </tr> + </thead> + <tbody> + <tr> + <td>relevance</td> + <td>The relevance of this item - usually a normalized number between 0 and 1.</td> + <td>Always</td> + </tr> + <tr> + <td>source</td> + <td>The name of the source producing this hit.</td> + <td>Always</td> + </tr> + <tr> + <td>type</td> + <td>A space-separated list of type identifiers of this hit.</td> + <td>If a type is set in the hit.</td> + </tr> + </tbody> +</table> + +Subtags: + +Hits have one subtag for every field they contain, where the field name is the name of the tag and the toString of the field content is the content of the tag. + +## \<group\> + +A [hit](#<hit>) which contains nested hits. Used to organize hits hierarchically. Has the name attributes and subtags as [hit](#<hit>), but may also contain nested [hit](#<hit>) and [group](#<group>) tags. + +## Example + +A page which should be rendered with two columns on top. + +```xml expandable +<page version="1.0"> + + <renderer name="two-column"/> + + <section region="left"> + <source url="http://host:port/resource/[news article id]"/> + <renderer name="articleBodyRenderer"> + <parameter name="color">blue</parameter> + </renderer> + </section> + + <section region="right"> + <renderer name="multi-item-column"> + <parameter name="items">3</parameter> + </renderer> + <section region="1"> + <renderer for="newsImage" name="newsImageRenderer"/> + <renderer for="news" name="articleRenderer"/> + <renderer for="image" name="imageRenderer"/> + <content> + <hit relevance="1.0" source="news"> + <id>news-1</id> + </hit> + <hit relevance="0.5" source="news"> + <id>news-2</id> + </hit> + </content> + </section> + <section region="2"> + <source url="http://host:port/consumption-widget"/> + <renderer name="identityRenderer"/> + </section> + <section region="3"> + <renderer name="htmlRenderer"/> + <content> + <hit relevance="1.0" source="htmlSource"> + <id>htmlSource-1</id> + </hit> + </content> + </section> + </section> +</page> +``` \ No newline at end of file diff --git a/en/reference/querying/page-templates.html b/en/reference/querying/page-templates.html deleted file mode 100644 index af052f8352..0000000000 --- a/en/reference/querying/page-templates.html +++ /dev/null @@ -1,379 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Page templates reference" -redirect_from: -- /en/reference/page-templates-syntax.html ---- - -<p> - This document is a reference to the elements of the Page Template XML format. - Refer to the <a href="../../querying/page-templates.html">Introduction to Page Templates</a>. -</p><p> - A page template describes a particular way or set of ways of organizing data from some sources on a page. - It has the following structure: -</p> -<pre> -<<a href="#page">page</a> id="<a href="#id">[id]</a>"> <em><!-- The top-level section of this page --></em> - [page-element]* -</page> -</pre> -<p> -…where each <code>[page-element]</code> is one of: -</p> -<dl> -<dt><<a href="#section">section</a>>[page-element]*</section></dt> -<dd><em>A nested section (screen area)</em></dd> - -<dt><<a href="#source">source</a> name="[source-name]"> [renderer]* [parameter]* </source></dt> -<dd><em>A data source which should be placed in this section</em></dd> - -<dt><<a href="#renderer">renderer</a> name="[renderer-name]"> [parameter]* </renderer></dt> -<dd><em>The renderer to use for the source or section containing this</em></dd> - -<dt><<a href="#choice">choice</a>> [map] or [page-element]/[alternative]* </choice></dt> -<dd><em>A choice between alternative page elements resolved at runtime</em></dd> - -<dt><placeholder id="[id]"/></dt> -<dd><em>an element to be replaced by a map item at runtime</em></dd> - -<dt><<a href="#include">include</a> idref="[page-id]">/></dt> -<dd><em>Include the page elements contained in another page</em></dd> -</dl> -<p> - where the nested elements above are: -</p> -<dl> -<dt><parameter name="[name]">[value]</parameter></dt> -<dd> - <em>A parameter of the owning element. Renderer parameters are sent - as-is to the frontend in the result. Source parameters are sent to - the source by setting the query - parameter<code>source.[sourceName].[name]</code>.</em> -</dd> - -<dt><alternative> [page-element]* </alternative></dt> -<dd><em>multiple page elements constituting one choice alternative</em></dd> - -<dt><<a href="#map">map</a> to="placeholder-id1 placeholder-id2 …"> [page-element]/[item]* </alternative></dt> -<dd><em>a mapping of some page elements to placeholders</em></dd> - -<dt><<a href="#item">item</a>> [page-element]* </item></dt> -<dd><em>multiple page elements which should all map to one placeholder</em></dd> -</dl> -<p> - All tags may also include a <code>description</code> attribute to document the use of the tag. - Tags and attributes are described in detail in the following. -</p> - - - -<h2 id="id">id</h2> -<p> - An id has the format: -</p> -<pre> -id ::= name(:major(.minor(.micro(.qualifier)?)?)?)? -name ::= <em>identifier</em> -major ::= <em>integer</em> -minor ::= <em>integer</em> -micro ::= <em>integer</em> -qualifier ::= <em>identifier</em> -</pre> -<p> - Any omitted numeric version component missing is taken to mean 0, - while a missing qualifier is taken to mean the empty string. -</p> - - - -<h2 id="page">page</h2> -<p> - The root tag of a page template. Defines a page, is also its root section. - Attributes and subtags are the same as for <a href="#section">section</a>, - with the exception that the <code>id</code> attribute is mandatory for a page. -</p> - - - -<h2 id="section">section</h2> -<p> - A representation of an area of screen real-estate. - At runtime a section will contain content from various sources. - The final renderer will render the section with its data items - and/or subsections in an area of screen real-estate determined by its containing tag. -</p> -<table class="table"> - <thead> - <tr> - <th>Attribute</th> - <th>Description</th> - <th>Default</th> - </tr> - </thead> - <tbody> -<tr> - <td>id</td> - <td>A unique identifier of this section used for referring.</td> - <td><em>No id</em></td> - </tr> -<tr> - <td>layout</td> - <td>An identifier. Permissible values - are <code>row</code>, <code>column</code> and any additional - layouts supported by the renderer i of the returned page.</td> - <td><code>column</code></td> - </tr> -<tr> - <td>region</td> - <td>An identifier. The permissible values, and whether this is - mandatory is determined by the particular layout identifier of - the containing section (<code>row</code> and <code>column</code> - does not specify any region identifiers).</td> - <td><em>None</em></td> - </tr> -<tr> - <td>source - <td>A space-separated set of sources permissible within this. This - is a shorthand for defining sources as subtags. The total source - list of this section consists of both the sources listed here - and as subtags.</td> - <td><em>All sources are permissible if none are specified.</em></td> - </tr> -<tr> - <td>max - <td>The maximum number of items permissible within this section - (including any subsections). Regardless of the blending method - used, the most relevant items are kept.</td> - <td><em>Unrestricted</em></td> - </tr> -<tr> - <td>min</td> - <td>The minimum number of items desired within this.</td> - <td><em>Unrestricted</em></td> - </tr> -<tr> - <td>order</td> - <td>The method of ordering to use on the items displayed in this - container. This may be any <a href="sorting-language.html">sorting - specification</a> over the fields of the hits, plus the source - name and relevance score, for example <code>[source] - -[relevance] category</code> to group by source, sort each group - primarily by decreasing relevance and secondarily by the - "category" field. The <code>[source]</code> identifier will sort - sources by the order in which they are listed in the template in - use. - </td> - <td></td> -</tr> - </tbody> -</table> - - - -<h2 id="source">source</h2> -<p> - A data source whose data should be placed in the containing section. -</p> -<table class="table"> - <thead> - <tr> - <th>Attribute</th> - <th>Description</th> - <th>Default</th> - </tr> - </thead> - <tbody> -<tr> - <td>name</td> - <td>The name of this source.</td> - <td><em>Mandatory</em></td> - </tr> -<tr> - <td>url</td> - <td>The url of this source. If this is set, the data of this source - is <em>not</em> fetched, but instead the source tag (with url) - will appear in the returned page such that the frontend may - fetch it. This is provided primarily as a migration path, as - such data can not be inspected and processed to optimize the - returned page.</td> - <td><em>No url: Fetch this configured source from the container.</em></td> - </tr> - </tbody> -</table> - - - -<h2 id="renderer">renderer</h2> -<p> - A renderer to use to render a section of a data item (hit) of a particular type. -</p> -<table class="table"> - <thead> - <tr> - <th>Attribute</th> - <th>Description</th> - <th>Default</th> - </tr> - </thead> - <tbody> -<tr> - <td>name</td> - <td>The name of this renderer.</td> - <td><em>Mandatory</em> - </tr> -<tr> - <td>for</td> - <td>The name of a hit type or a source this is the renderer for.</td> - <td><em>If in a section: This is the renderer for the whole - section.<br />If in a source: This is the default renderer for - hits from this source.</em></td> - </tr> - </tbody> -</table> - - - -<h2 id="choice">choice</h2> -<p> - A choice between multiple alternative (lists of) page elements. - A resolver chooses between the possible alternatives for each request at runtime. - The <code>alternative</code> tag is used to enclose an alternative. - If an alternative consists of just one page element tag, - the enclosing alternative tag may be skipped. -</p> -<table class="table"> - <thead> - <tr> - <th>Attribute</th> - <th>Description</th> - <th>Default</th> - </tr> - </thead> - <tbody> -<tr> - <td>method</td> - <td>the name of the method for making the choice. Must be supported - by the optimizer in use.</td> - <td><em>Any method</em></td> - </tr> - </tbody> -</table> -<h3 id="contained-tags">Contained tags</h3> -<p> - Either: -</p> -<table class="table"> - <thead> - <tr> - <th>Tag</th> - <th>Description</th> - <th>Default</th> - </tr> - </thead> - <tbody> -<tr> - <td>[page-element]</td> - <td>An alternative consisting of a single page element.</td> - <td>0-n</td> - </tr> -<tr> - <td>alternative</td> - <td>An alternative consisting of multiple page elements.</td> - <td>0-n</td> - </tr> - </tbody> -</table> -or -<table class="table"> - <thead> - <tr> - <th>Tag</th> - <th>Description</th> - <th>Default</th> - </tr> - </thead> - <tbody> -<tr> - <td><a href="#map">map</a></td> - <td>Specify all alternatives as a single mapping function.</td> - <td>0-1</td> - </tr> - </tbody> -</table> - - - -<h2 id="map">map</h2> -<p> - Specify all the alternatives of a choice - as a mapping function of elements to placeholders. - A map is a convenience shorthand of writing many alternatives - in the case where a collection of elements should be mapped to a set of placeholders - with the constraint that each placeholder should get a unique element. - This is useful e.g. in the case where a set of sources are to be mapped to a set of sections. -</p> -<table class="table"> - <thead> - <tr> - <th>Attribute</th> - <th>Description</th> - <th>Default</th> - </tr> - </thead> - <tbody> -<tr> - <td>to</td> - <td>A space-separated list of the placeholder id's the map values should be mapped to. - There cannot be more placeholder id's than there are values in this map (but fewer is ok).</td> - </tr> - </tbody> -</table> -<table class="table"> - <thead> - <tr> - <th>Contained Tags</th> - <th>Description</th> - <th>Default</th> - </tr> - </thead> - <tbody> -<tr> - <td>[page-element]</td> - <td>A map item consisting of a single page element to map to a placeholder.</td> - <td>0-n</td> - </tr> -<tr> - <td>item</td> - <td><p id="item">An item containing multiple page elements to be mapped to a single placeholder.</p></td> - <td>0-n</td> - </tr> - </tbody> -</table> - - - -<h2 id="include">include</h2> -<p> - Includes the page elements contained directly in the <code>page</code> - element in the given page template (the page tag itself is not included). - Inclusion works exactly as if the <code>include</code> tag was - literally replaced by the content of the included page. -</p> -<table class="table"> - <thead> - <tr> - <th>Attribute</th> - <th>Description</th> - <th>Default</th> - </tr> - </thead> - <tbody> -<tr> - <td>idref</td> - <td>The id specification of the page to include. Portions of the - version may be left unspecified to get the latest matching version.</td> - <td><em>(Mandatory)</em></td> - </tr> - </tbody> -</table> - diff --git a/en/reference/querying/page-templates.mdx b/en/reference/querying/page-templates.mdx new file mode 100644 index 0000000000..6f96787292 --- /dev/null +++ b/en/reference/querying/page-templates.mdx @@ -0,0 +1,312 @@ +--- +title: "Page templates reference" +sidebarTitle: "Page templates" +--- + +This document is a reference to the elements of the Page Template XML format. Refer to the [Introduction to Page Templates](/en/querying/page-templates). + +A page template describes a particular way or set of ways of organizing data from some sources on a page. It has the following structure: + +<Card> +`<`[page](#page) id="[\[id\]](#id)"> *`<!-- The top-level section of this page -->`* +  \[page-element\]\* +`</page>` +</Card> + +...where each `[page-element]` is one of: + +\<[section](#section)\>[page-element]\*\</section\> + +_A nested section (screen area)_ + +\<[source](#source) name="[source-name]"\> [renderer]\* [parameter]\* \</source\> + +_A data source which should be placed in this section_ + +\<[renderer](#renderer) name="[renderer-name]"\> [parameter]\* \</renderer\> + +_The renderer to use for the source or section containing this_ + +\<[choice](#choice)\> [map] or [page-element]/[alternative]\* \</choice\> + +_A choice between alternative page elements resolved at runtime_ + +\<placeholder id="[id]"/\> + +_an element to be replaced by a map item at runtime_ + +\<[include](#include) idref="[page-id]"\>/\> + +_Include the page elements contained in another page_ + +where the nested elements above are: + +\<parameter name="[name]"\>[value]\</parameter\> + +_A parameter of the owning element. Renderer parameters are sent as-is to the frontend in the result. Source parameters are sent to the source by setting the query parameter`source.[sourceName].[name]`._ + +\<alternative\> [page-element]\* \</alternative\> + +_multiple page elements constituting one choice alternative_ + +\<[map](#map) to="placeholder-id1 placeholder-id2 ... "\> [page-element]/[item]\* \</alternative\> + +_a mapping of some page elements to placeholders_ + +\<[item](#item)\> [page-element]\* \</item\> + +_multiple page elements which should all map to one placeholder_ + +All tags may also include a `description` attribute to document the use of the tag. Tags and attributes are described in detail in the following. + +## id + +An id has the format: + +```bash +id ::= name(:major(.minor(.micro(.qualifier)?)?)?)? +name ::= identifier +major ::= integer +minor ::= integer +micro ::= integer +qualifier ::= identifier +``` + +Any omitted numeric version component missing is taken to mean 0, while a missing qualifier is taken to mean the empty string. + +## page + +The root tag of a page template. Defines a page, is also its root section. Attributes and subtags are the same as for [section](#section), with the exception that the `id` attribute is mandatory for a page. + +## section + +A representation of an area of screen real-estate. At runtime a section will contain content from various sources. The final renderer will render the section with its data items and/or subsections in an area of screen real-estate determined by its containing tag. + +<table className="vespa-table"> + <thead> + <tr> + <th>Attribute</th> + <th>Description</th> + <th>Default</th> + </tr> + </thead> + <tbody> + <tr> + <td>id</td> + <td>A unique identifier of this section used for referring.</td> + <td>_No id_</td> + </tr> + <tr> + <td>layout</td> + <td>An identifier. Permissible values are <code>{`row`}</code>, <code>{`column`}</code> and any additional layouts supported by the renderer i of the returned page.</td> + <td><code>{`column`}</code></td> + </tr> + <tr> + <td>region</td> + <td>An identifier. The permissible values, and whether this is mandatory is determined by the particular layout identifier of the containing section (<code>{`row`}</code> and <code>{`column`}</code> does not specify any region identifiers).</td> + <td>_None_</td> + </tr> + <tr> + <td>source</td> + <td>A space-separated set of sources permissible within this. This is a shorthand for defining sources as subtags. The total source list of this section consists of both the sources listed here and as subtags.</td> + <td>_All sources are permissible if none are specified._</td> + </tr> + <tr> + <td>max</td> + <td>The maximum number of items permissible within this section (including any subsections). Regardless of the blending method used, the most relevant items are kept.</td> + <td>_Unrestricted_</td> + </tr> + <tr> + <td>min</td> + <td>The minimum number of items desired within this.</td> + <td>_Unrestricted_</td> + </tr> + <tr> + <td>order</td> + <td>The method of ordering to use on the items displayed in this container. This may be any <a href="/en/reference/querying/sorting-language" aria-label="sorting specification">sorting specification</a> over the fields of the hits, plus the source name and relevance score, for example <code>{`[source]-[relevance] category`}</code> to group by source, sort each group primarily by decreasing relevance and secondarily by the "category" field. The <code>{`[source]`}</code> identifier will sort sources by the order in which they are listed in the template in use.</td> + <td></td> + </tr> + </tbody> +</table> + +## source + +A data source whose data should be placed in the containing section. + +<table className="vespa-table"> + <thead> + <tr> + <th>Attribute</th> + <th>Description</th> + <th>Default</th> + </tr> + </thead> + <tbody> + <tr> + <td>name</td> + <td>The name of this source.</td> + <td>_Mandatory_</td> + </tr> + <tr> + <td>url</td> + <td>The url of this source. If this is set, the data of this source is _not_ fetched, but instead the source tag (with url) will appear in the returned page such that the frontend may fetch it. This is provided primarily as a migration path, as such data can not be inspected and processed to optimize the returned page.</td> + <td>_No url: Fetch this configured source from the container._</td> + </tr> + </tbody> +</table> + +## renderer + +A renderer to use to render a section of a data item (hit) of a particular type. + +<table className="vespa-table"> + <thead> + <tr> + <th>Attribute</th> + <th>Description</th> + <th>Default</th> + </tr> + </thead> + <tbody> + <tr> + <td>name</td> + <td>The name of this renderer.</td> + <td>_Mandatory_</td> + </tr> + <tr> + <td>for</td> + <td>The name of a hit type or a source this is the renderer for.</td> + <td>_If in a section: This is the renderer for the whole section.</td> + </tr> + </tbody> +</table> +If in a source: This is the default renderer for hits from this source._ | + +## choice + +A choice between multiple alternative (lists of) page elements. A resolver chooses between the possible alternatives for each request at runtime. The `alternative` tag is used to enclose an alternative. If an alternative consists of just one page element tag, the enclosing alternative tag may be skipped. + +<table className="vespa-table"> + <thead> + <tr> + <th>Attribute</th> + <th>Description</th> + <th>Default</th> + </tr> + </thead> + <tbody> + <tr> + <td>method</td> + <td>the name of the method for making the choice. Must be supported by the optimizer in use.</td> + <td>_Any method_</td> + </tr> + </tbody> +</table> + +### Contained tags + +Either: + +<table className="vespa-table"> + <thead> + <tr> + <th>Tag</th> + <th>Description</th> + <th>Default</th> + </tr> + </thead> + <tbody> + <tr> + <td>[page-element]</td> + <td>An alternative consisting of a single page element.</td> + <td>0-n</td> + </tr> + <tr> + <td>alternative</td> + <td>An alternative consisting of multiple page elements.</td> + <td>0-n</td> + </tr> + </tbody> +</table> + +or + +<table className="vespa-table"> + <thead> + <tr> + <th>Tag</th> + <th>Description</th> + <th>Default</th> + </tr> + </thead> + <tbody> + <tr> + <td><a href="#map" aria-label="map">map</a></td> + <td>Specify all alternatives as a single mapping function.</td> + <td>0-1</td> + </tr> + </tbody> +</table> + +## map + +Specify all the alternatives of a choice as a mapping function of elements to placeholders. A map is a convenience shorthand of writing many alternatives in the case where a collection of elements should be mapped to a set of placeholders with the constraint that each placeholder should get a unique element. This is useful e.g. in the case where a set of sources are to be mapped to a set of sections. + +<table className="vespa-table"> + <thead> + <tr> + <th>Attribute</th> + <th>Description</th> + <th>Default</th> + </tr> + </thead> + <tbody> + <tr> + <td>to</td> + <td colSpan={2}>A space-separated list of the placeholder id's the map values should be mapped to. There cannot be more placeholder id's than there are values in this map (but fewer is ok).</td> + </tr> + </tbody> +</table> + +<table className="vespa-table"> + <thead> + <tr> + <th>Contained Tags</th> + <th>Description</th> + <th>Default</th> + </tr> + </thead> + <tbody> + <tr> + <td>[page-element]</td> + <td>A map item consisting of a single page element to map to a placeholder.</td> + <td>0-n</td> + </tr> + <tr> + <td>item</td> + <td>An item containing multiple page elements to be mapped to a single placeholder.</td> + <td>0-n</td> + </tr> + </tbody> +</table> + +## include + +Includes the page elements contained directly in the `page` element in the given page template (the page tag itself is not included). Inclusion works exactly as if the `include` tag was literally replaced by the content of the included page. + +<table className="vespa-table"> + <thead> + <tr> + <th>Attribute</th> + <th>Description</th> + <th>Default</th> + </tr> + </thead> + <tbody> + <tr> + <td>idref</td> + <td>The id specification of the page to include. Portions of the version may be left unspecified to get the latest matching version.</td> + <td>_(Mandatory)_</td> + </tr> + </tbody> +</table> \ No newline at end of file diff --git a/en/reference/querying/query-profiles.html b/en/reference/querying/query-profiles.html deleted file mode 100644 index cad51d1a7a..0000000000 --- a/en/reference/querying/query-profiles.html +++ /dev/null @@ -1,323 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Query Profile Reference" -redirect_from: -- /en/reference/query-profiles.html ---- - -<p> -This is a reference to the full format of <a href="#query-profiles">Query Profile</a> and -<a href="#query-profile-types">Query Profile Type</a> configuration files. -For an introduction to query profiles, please see <a href="../../querying/query-profiles.html">query profiles</a>. -</p> - - - -<h2 id="query-profiles">Query Profiles</h2> -<p> -A query profile defines a named set of search request parameters with values - structure: -</p> -<pre> -<query-profile id="<a href="#id">[id]</a>" <a href="#optional-query-profile-attributes">[optional attributes]</a>> - - <<a href="#description">description</a>></description> ? - - <<a href="#dimensions">dimensions</a>></dimensions> ? - - <<a href="#query-profile-field">field</a> name="<a href="#field-name">[name]</a>" <a href="#optional_field_attributes">[optional attributes]</a>><a href="#field-value">[value]</a></field> * - - <<a href="#query-profile-nested">query-profile</a> for="<a href="#query-profile-nested-for-attribute">[dimension values]</a> <a href="#optional-query-profile-attributes">[optional attributes]</a>> - <<a href="#query-profile-field">field</a> name="<a href="#field-name">[name]</a>"><a href="#field-value">[value]</a></field> * - </query-profile> * - -</query-profile> -</pre> -<p> -where <code>?</code> means optional and <code>*</code> means repeatable tag. -These items are described in the following sections. -</p> - - -<h3 id="id">id</h3> -<p> -The id has the format: -</p> -<pre> -id ::= name(:major(.minor(.micro(.qualifier)?)?)?)? -name ::= <em><a href="#identifiers">identifier</a></em> -major ::= <em>integer</em> -minor ::= <em>integer</em> -micro ::= <em>integer</em> -qualifier ::= <em><a href="#identifiers">identifier</a></em> -</pre> -<p> -Any omitted numeric version component missing is taken to mean 0, -while a missing qualifier is taken to mean the empty string. -If the name is exactly <code>default</code>, this profile will be the default. -If there are multiple profiles named <code>default</code>, the newest version is the default. -</p> - - -<h3 id="optional-query-profile-attributes">Optional <code>query-profile</code> attributes</h3> -<table class="table"> - <thead><tr><th>Name</th><th>Default</th><th>Description</th></tr></thead><tbody> - <tr> - <td>type</td> - <td><em>No type checking</em></td> - <td>The id of a query profile type which defines the possible content of this query profile</td> - </tr><tr> - <td>inherits</td> - <td><em>No inclusion</em></td> - <td>A space-separated list of id's of the query profiles whose fields should be included in this profile. - The fields are included exactly as if they were present in this profile. - Order matters: If a field is present in multiple inherited profiles, - the first one found in a depth first, left to right search will be used. - Fields present in this profile always overrides the same field name in an inherited profile.</td> - </tr> - </tbody> -</table> - - -<h3 id="description">Description</h3> -<p> -A textual description of the purpose of this. Used for documentation. -</p> - - -<h3 id="query-profile-field">Query profile <code>field</code></h3> -<p> -A field in a query profile defines a key-value pair. -</p><p> -If the value is a primitive (string, number), then this key value will -be available from the Query exactly as if it was submitted with the search request as a parameter -(if it is set both ways, the search request takes priority). -</p><p> -If the value is a reference to another query profile, -the key-values of the referenced profile will be available from the Query exactly as -if they were submitted with the search request as a parameter, -with the key of this value and a dot prepended to each key in the nested profile, i.e -<code>keyNameInReferringProfile.keyNameInReferencedProfile=value</code>. -</p> - - -<h3 id="field-name"><code>field</code> name</h3> -<p> -The name of the field, must be a valid <a href="#identifiers">identifier</a>. -</p> - - -<h3 id="optional_field_attributes">Optional <code>field</code> attributes</h3> -<table class="table"> - <thead><tr><th>Name</th><th>Default</th><th>Description</th></tr></thead><tbody> - <tr> - <td>overridable</td> - <td><code>true</code></td> - <td> - <p id="overridable"> - <code>true</code> or <code>false</code>. - If this is <code>true</code>, this field can be overridden by a parameter of the same name in the search request. - If it is <code>false</code>, it can not be overridden in the request. - This attribute overrides the overridable setting in the field definition for this field (if any). - If a non overridable value is attempted assigned a value later, - the assignment will <em>not</em> cause an error, but will simply be ignored. - </p> - </td> - </tr> - </tbody> -</table> - - -<h3 id="field-value"><code>field</code> value</h3> -<p> -This value of the field, may be either: -</p> -<ul> - <li>a primitive, encoded as any (XML escaped) string, or</li> - <li>a reference to another query profile encoded as <ref>[query-profile-id]</ref></li> -</ul> -<p> -If this field is defined in the query profile type referenced by this query profile, -then the value must be the valid value type defined by that query type field definition. -</p> - - -<h3 id="dimensions">Dimensions</h3> -<p> -A comma-separated list of dimensions over which variants of this profile may be created as -<a href="#query-profile-nested">nested query profiles</a>. -The names of the dimensions are the names of request parameters which, -when received in the request, will trigger the matching profile variants. -</p> - - -<h3 id="query-profile-nested">Query profile (nested)</h3> -<p> -A nested query profile defines variants of values returned from the enclosing query profile, -which are returned for variable requests where this variant is the most specific match to the request properties -named by the <a href="#dimensions">dimensions</a> -as defined by its <a href="#query-profile-nested-for-attribute">for attribute</a>. -No other attributes may be set in nested query profiles. -</p> - - -<h3 id="query-profile-nested-for-attribute">Query profile (nested) <code>for</code> attribute</h3> -<p> -This attribute defines the values of the <a href="#dimensions">dimensions</a> of the enclosing profile -for which this nested profile defined alternative values, as a comma-separated list. -The values are defined in the same order as the dimensions are defined. -Dimensions for which this should match any value may be denoted by a "*". -One or more trailing "*" may be omitted - example: -</p> -<pre> -for="a,b,*,c,*,*" -for="a,b,*,c" // equivalent to the above -</pre> - - - -<h2 id="query-profile-types">Query Profile Types</h2> -<p> -A query profile type defines a set of valid, typed values for a query profile - structure: -</p> -<pre> -<query-profile-type id="<a href="#id">[id]</a>" <a href="#optional-query-profile-type-attributes">[optional attributes]</a>> - - <<a href="#description">description</a>></description> ? - - <<a href="#match">match path="true"</a>/> ? - - <<a href="#strict">strict</a>/> ? - - <<a href="#query-profile-type-field">field</a> name="<a href="#field-name">[name]</a>" type="<a href="#field-type">[type]</a>" <a href="#optional-field-definition-attributes">[optional attributes]</a>/> * - -</query-profile-type> -</pre> -<p> -where <code>?</code> means optional tag and <code>*</code> means repeatable tag. -These items are described in the following sections. -</p> - - -<h3 id="optional-query-profile-type-attributes">Optional <code>query-profile-type</code> attributes</h3> -<table class="table"> - <thead><tr><th>Name</th><th>Default</th><th>Description</th></tr></thead><tbody> - <tr> - <td>inherits</td> - <td><em>No inclusion</em></td> - <td>A space-separated list of id's of the query profile types - whose field definitions should be included in this profile. - The fields are included exactly as if they were present in this profile type. - Order matters: If a field definition is present in multiple inherited profiles, - the first one found in a depth first, left to right search will be used. - A field definition in this type always overrides inherited ones. - The same rules apply to other elements than fields.</td> - </tr> - </tbody> -</table> - - -<h3 id="match"><code>match</code></h3> -<p> -If <match path="true"> is added to the query profile type, -the name of the profile will be understood as a slash separated path name -during matching of a query profile name to an actual profile. -If the query profile name is a <em>path component prefix</em> of the query profile name reference, -the profile matches the reference. -The profile having the most specific match is used as the target of the reference. -</p><p> -If <code>match</code> is not specified in the profile type, exact name matching is used. -The syntax is as specified for future extensions. -</p><p> -The match setting is inherited from supertypes to subtypes. -</p> - - -<h3 id="strict"><code>strict</code></h3> -<p> -If this element is added to a query profile type, -then that profile can only contain values explicitly defined in the profile, -whether that value is provided by a query profile, -the search request or programmatically. -</p><p> -It is possible to add strict sub-profiles to a non-strict profile and vice-versa, -making it possible to create respectively "structs in maps" and "maps in structs". -</p><p> -A profile which inherits a strict profile will also be strict, -i.e <code>strict</code> is inherited. -</p><p> -Some rules to note when using a top-level profile type which is declared as strict: -</p> -<ul> - <li>If the top-level profile is of a strict type, - that type should usually inherit the <code>native</code> type to allow the built-in parameters to be passed in. - This profile type and the subtypes it references are always available - - refer to the <a href="../api/query.html">Query API reference</a>.</li> - <li>Non-primitive model objects are permitted to be added to the query profiles - even if the top level profile is strict, but primitives (strings, numbers, booleans) - are <em>not</em> permitted but must either be declared in the strict profile type, - or wrapped in a proper model object</li> - <li>Feature specific properties like <code>select</code> are not automatically permitted, - the parameters of the features which should be exposed must be declared explicitly in a strict top level type.</li> -</ul> - - -<h3 id="query-profile-type-field">Query profile type <code>field</code></h3> -<p> -This defines the name and type of a field of query profiles of this type. -</p> - - -<h3 id="field-type"><code>field</code> type</h3> -<p> -This defines the type of this field. The type is one of: -</p> -<table class="table"> - <thead><tr><th>Type name</th><th>Description</th></tr></thead><tbody> - <tr><td>string</td><td>Any string</td></tr> - <tr><td>integer</td><td>A signed 32-bit whole number</td></tr> - <tr><td>long</td><td>A signed 64-bit whole number</td></tr> - <tr><td>float</td><td>A signed 32-bit float</td></tr> - <tr><td>double</td><td>A signed 64-bit float</td></tr> - <tr><td>boolean</td><td>A boolean value, <code>true</code> or <code>false</code></td></tr> - <tr><td><a href="../ranking/tensor.html#tensor-type-spec">[tensor-type-spec]</a></td><td>A tensor type spec</td></tr> - <tr><td>query-profile</td><td>A reference to a query profile of any type</td></tr> - <tr><td>query-profile:[query-profile-type-id]</td><td>A reference to a query profile of the given type</td></tr> - </tbody> -</table> - - -<h3 id="optional-field-definition-attributes">Optional <code>field</code> definition attributes</h3> -<table class="table"> - <thead><tr><th>Name</th><th>Default</th><th>Description</th></tr></thead><tbody> - <tr> - <td>mandatory</td> - <td><code>false</code></td> - <td><code>true</code> or <code>false</code>. - If this is <code>true</code>, this field <em>must</em> be present - in either the query profile of this type - or explicitly in the request referencing it</td> - </tr><tr> - <td>overridable</td> - <td><code>true</code></td> - <td><code>true</code> or <code>false</code>. - If this is <code>true</code>, instances of this field can be overridden - by a parameter of the same name in the search request. - If it is <code>false</code>, it can not be overridden in the request</td> - </tr><tr> - <td>alias</td> - <td><em>None</em></td> - <td>One or more space-separated aliases of the field name. - Unlike field names, aliases are case-insensitive</td> - </tr><tr> - <td>description</td> - <td><em>None</em></td> - <td>A textual description of the purpose of this field. Used for documentation</td> - </tr> - </tbody> -</table> - - -<h3 id="identifiers">Identifiers</h3> -<p> -An identifier is a string matches the pattern <code>[a-zA-Z_/][a-zA-Z0-9_/]*</code>. -</p> diff --git a/en/reference/querying/query-profiles.mdx b/en/reference/querying/query-profiles.mdx new file mode 100644 index 0000000000..0e5278bf1c --- /dev/null +++ b/en/reference/querying/query-profiles.mdx @@ -0,0 +1,282 @@ +--- +title: "Query Profile Reference" +sidebarTitle: "Query profiles" +--- + +This is a reference to the full format of [Query Profile](#query-profiles) and [Query Profile Type](#query-profile-types) configuration files. For an introduction to query profiles, please see [query profiles](/en/querying/query-profiles). + +## Query Profiles + +A query profile defines a named set of search request parameters with values - structure: + +```xml +<query-profile id="[[id]]" [[optional attributes]]> + <description></description>? + <dimensions></dimensions>? + + <field name="[[name]]" [[optional attributes]]> + [[value]] + </field>* + + <query-profile for="[[dimension values]]" [[optional attributes]]> + <field name="[[name]]">[[value]]</field>* + </query-profile>* + +</query-profile> +``` + +where `?` means optional and `*` means repeatable tag. These items are described in the following sections. + +### id + +The id has the format: + +<Card> +id ::= name(:major(.minor(.micro(.qualifier)?)?)?)?<br/> +name ::=_[identifier](#identifiers)_<br/> +major ::=_integer_<br/> +minor ::=_integer_<br/> +micro ::=_integer_<br/> +qualifier ::=_[identifier](#identifiers)_ +</Card> + +Any omitted numeric version component missing is taken to mean 0, while a missing qualifier is taken to mean the empty string. If the name is exactly `default`, this profile will be the default. If there are multiple profiles named `default`, the newest version is the default. + +### Optional `query-profile` attributes + +<table className="vespa-table"> + <thead> + <tr> + <th>Name</th> + <th>Default</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>type</td> + <td>_No type checking_</td> + <td>The id of a query profile type which defines the possible content of this query profile</td> + </tr> + <tr> + <td>inherits</td> + <td>_No inclusion_</td> + <td>A space-separated list of id's of the query profiles whose fields should be included in this profile. The fields are included exactly as if they were present in this profile. Order matters: If a field is present in multiple inherited profiles, the first one found in a depth first, left to right search will be used. Fields present in this profile always overrides the same field name in an inherited profile.</td> + </tr> + </tbody> +</table> + +### Description + +A textual description of the purpose of this. Used for documentation. + +### Query profile `field` + +A field in a query profile defines a key-value pair. + +If the value is a primitive (string, number), then this key value will be available from the Query exactly as if it was submitted with the search request as a parameter (if it is set both ways, the search request takes priority). + +If the value is a reference to another query profile, the key-values of the referenced profile will be available from the Query exactly as if they were submitted with the search request as a parameter, with the key of this value and a dot prepended to each key in the nested profile, i.e `keyNameInReferringProfile.keyNameInReferencedProfile=value`. + +### `field` name + +The name of the field, must be a valid [identifier](#identifiers). + +### Optional `field` attributes + +<table className="vespa-table"> + <thead> + <tr> + <th>Name</th> + <th>Default</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>overridable</td> + <td><code>{`true`}</code></td> + <td><code>{`true`}</code> or <code>{`false`}</code>. If this is <code>{`true`}</code>, this field can be overridden by a parameter of the same name in the search request. If it is <code>{`false`}</code>, it can not be overridden in the request. This attribute overrides the overridable setting in the field definition for this field (if any). If a non overridable value is attempted assigned a value later, the assignment will _not_ cause an error, but will simply be ignored.</td> + </tr> + </tbody> +</table> + +### `field` value + +This value of the field, may be either: + +- a primitive, encoded as any (XML escaped) string, or +- a reference to another query profile encoded as \<ref\>[query-profile-id]\</ref\> + +If this field is defined in the query profile type referenced by this query profile, then the value must be the valid value type defined by that query type field definition. + +### Dimensions + +A comma-separated list of dimensions over which variants of this profile may be created as [nested query profiles](#query-profile-nested). The names of the dimensions are the names of request parameters which, when received in the request, will trigger the matching profile variants. + +### Query profile (nested) + +A nested query profile defines variants of values returned from the enclosing query profile, which are returned for variable requests where this variant is the most specific match to the request properties named by the [dimensions](#dimensions) as defined by its [for attribute](#query-profile-nested-for-attribute). No other attributes may be set in nested query profiles. + +### Query profile (nested) `for` attribute + +This attribute defines the values of the [dimensions](#dimensions) of the enclosing profile for which this nested profile defined alternative values, as a comma-separated list. The values are defined in the same order as the dimensions are defined. Dimensions for which this should match any value may be denoted by a "\*". One or more trailing "\*" may be omitted - example: + +<Card> +for="a,b,*,c,*,*" +for="a,b,*,c" // equivalent to the above +</Card> + +## Query Profile Types + +A query profile type defines a set of valid, typed values for a query profile - structure: + +```xml +<query-profile-type id="[[id]]" [[optional attributes]]> + + <description></description> ? + + <match path="true"/> ? + + <strict/> ? + + <field name="[[name]]" type="[[type]]" [[optional attributes]]/> * + +</query-profile-type> +``` + +where `?` means optional tag and `*` means repeatable tag. These items are described in the following sections. + +### Optional `query-profile-type` attributes + +<table className="vespa-table"> + <thead> + <tr> + <th>Name</th> + <th>Default</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>inherits</td> + <td>_No inclusion_</td> + <td>A space-separated list of id's of the query profile types whose field definitions should be included in this profile. The fields are included exactly as if they were present in this profile type. Order matters: If a field definition is present in multiple inherited profiles, the first one found in a depth first, left to right search will be used. A field definition in this type always overrides inherited ones. The same rules apply to other elements than fields.</td> + </tr> + </tbody> +</table> + +### `match` + +If \<match path="true"\> is added to the query profile type, the name of the profile will be understood as a slash separated path name during matching of a query profile name to an actual profile. If the query profile name is a _path component prefix_ of the query profile name reference, the profile matches the reference. The profile having the most specific match is used as the target of the reference. + +If `match` is not specified in the profile type, exact name matching is used. The syntax is as specified for future extensions. + +The match setting is inherited from supertypes to subtypes. + +### `strict` + +If this element is added to a query profile type, then that profile can only contain values explicitly defined in the profile, whether that value is provided by a query profile, the search request or programmatically. + +It is possible to add strict sub-profiles to a non-strict profile and vice-versa, making it possible to create respectively "structs in maps" and "maps in structs". + +A profile which inherits a strict profile will also be strict, i.e `strict` is inherited. + +Some rules to note when using a top-level profile type which is declared as strict: + + - If the top-level profile is of a strict type, that type should usually inherit the `native` type to allow the built-in parameters to be passed in. This profile type and the subtypes it references are always available - refer to the [Query API reference](/en/reference/api/query). + - Non-primitive model objects are permitted to be added to the query profiles even if the top level profile is strict, but primitives (strings, numbers, booleans) are _not_ permitted but must either be declared in the strict profile type, or wrapped in a proper model object + - Feature specific properties like `select` are not automatically permitted, the parameters of the features which should be exposed must be declared explicitly in a strict top level type. + +### Query profile type `field` + +This defines the name and type of a field of query profiles of this type. + +### `field` type + +This defines the type of this field. The type is one of: + +<table className="vespa-table"> + <thead> + <tr> + <th>Type name</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>string</td> + <td>Any string</td> + </tr> + <tr> + <td>integer</td> + <td>A signed 32-bit whole number</td> + </tr> + <tr> + <td>long</td> + <td>A signed 64-bit whole number</td> + </tr> + <tr> + <td>float</td> + <td>A signed 32-bit float</td> + </tr> + <tr> + <td>double</td> + <td>A signed 64-bit float</td> + </tr> + <tr> + <td>boolean</td> + <td>A boolean value, <code>{`true`}</code> or <code>{`false`}</code></td> + </tr> + <tr> + <td><a href="/en/reference/ranking/tensor#tensor-type-spec" aria-label="[tensor-type-spec]">[tensor-type-spec]</a></td> + <td>A tensor type spec</td> + </tr> + <tr> + <td>query-profile</td> + <td>A reference to a query profile of any type</td> + </tr> + <tr> + <td>query-profile:[query-profile-type-id]</td> + <td>A reference to a query profile of the given type</td> + </tr> + </tbody> +</table> + +### Optional `field` definition attributes + +<table className="vespa-table"> + <thead> + <tr> + <th>Name</th> + <th>Default</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>mandatory</td> + <td><code>{`false`}</code></td> + <td><code>{`true`}</code> or <code>{`false`}</code>. If this is <code>{`true`}</code>, this field _must_ be present in either the query profile of this type or explicitly in the request referencing it</td> + </tr> + <tr> + <td>overridable</td> + <td><code>{`true`}</code></td> + <td><code>{`true`}</code> or <code>{`false`}</code>. If this is <code>{`true`}</code>, instances of this field can be overridden by a parameter of the same name in the search request. If it is <code>{`false`}</code>, it can not be overridden in the request</td> + </tr> + <tr> + <td>alias</td> + <td>_None_</td> + <td>One or more space-separated aliases of the field name. Unlike field names, aliases are case-insensitive</td> + </tr> + <tr> + <td>description</td> + <td>_None_</td> + <td>A textual description of the purpose of this field. Used for documentation</td> + </tr> + </tbody> +</table> + +### Identifiers + +An identifier is a string matches the pattern `[a-zA-Z_/][a-zA-Z0-9_/]*`. diff --git a/en/reference/querying/semantic-rules.html b/en/reference/querying/semantic-rules.html deleted file mode 100644 index 68187798ef..0000000000 --- a/en/reference/querying/semantic-rules.html +++ /dev/null @@ -1,413 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Semantic Rule Language Reference" -redirect_from: -- /en/reference/semantic-rules.html ---- - -<p> - This is the reference for the semantic rule language in Vespa. - For a guide on using this language, - see <a href="../../linguistics/query-rewriting.html">query rewriting</a>. - Refer to the <a href="../api/query.html#semantic-rules">Query API</a> for how to use in queries. -</p> - - - -<h2 id="rule-bases">Rule bases</h2> -<p> - Semantic rules are collected in files called <em>rule bases</em>. - The name of these files are <em>[rule-base-name].sr</em>. - They must be placed in <em>[application-package]/rules/</em> to be deployed. -</p> - - - -<h2 id="basic-syntax">Basic syntax</h2> -<p> - A rule base may contain any number of the following four constructs, - explained in the rest of this document: -</p> -<ul> - <li><a href="#directives">directives</a></li> - <li><a href="#production-rules">production rules</a></li> - <li><a href="#named-conditions">named conditions</a></li> - <li>comments, starting by # and ending by newline.</li> -</ul> -<p> - Production rules and named conditions are <em>statements</em>. - Statements may span multiple lines and are terminated by <code>;</code>. -</p> - - - -<h2 id="directives">Directives</h2> -<p> - A directive is a "meta-level" statement which is not used during rule evaluation, - but tells the rule engine how to use the rule base. - A statement starts by <code>@</code> and ends by newline. - They may take parameters. These directives exist: -</p> -<table class="table"> - <thead> - <tr><th>Statement</th><th>Usage</th><th>Location</th></tr> - </thead><tbody> - <tr> - <td>@default</td> - <td>Make this rule base the default, to be used with all queries</td> - <td>Anywhere outside other statements</td> - </tr> - <tr> - <td>@automata(<automata-filename>)</td> - <td>Use an automata file with this base</td> - <td>Anywhere outside other statements</td> - </tr> - <tr> - <td>@include(<rulebase-name>)</td> - <td>Include all the statements of another rule base in this</td> - <td>Anywhere outside other statements</td> - </tr> - <tr> - <td>@super</td> - <td>Include the conditions of the same-named conditions from the included rule base</td> - <td>In a condition</td> - </tr> - <tr> - <td>@stemming(<true|false>)</td> - <td>Whether terms should match after stemming or exactly (true by default)</td> - <td>Before any rule</td> - </tr> - <tr> - <td>@language(<<a href="https://en.wikipedia.org/wiki/ISO_639-1">language-code</a>>)</td> - <td>The language of the rule base, which should also be the query language. Influences stemming.</td> - <td>Before any rule</td> - </tr> - </tbody> -</table> - - - -<h2 id="production-rules">Production Rules</h2> -<p> -A production rule is of the form: -</p> -<pre> -<a href="#conditions"><condition></a> <operator> <a href="#production-list"><production-list></a>; -</pre> -<p> - This performs the production as defined by the operator if the condition matches. - There are two kinds of production rules (and two operators), replacing and adding: -</p> -<table class="table"> - <thead> - <tr> - <th>Rule kind</th> - <th>Operator</th> - <th>Meaning</th> - </tr> - </thead><tbody> - <tr> - <td>Replacing</td> - <td>-></td> - <td><em>Replace</em> the matched terms by the production</td> - </tr><tr> - <td>Adding</td> - <td>+></td> - <td><em>Add</em> the production to the matched terms</td> - </tr> - </tbody> -</table> - - - -<h2 id="namespaces">Namespaces</h2> -<p> -A namespace is a collection of facts which can be read from conditions and changed by productions. - Namespaces may be positional (sequences), or not. - A positional namespace will track the current fact and match and insert at the current position, - while non-positional namespaces will match any fact against any condition. -</p><p> - There is a default namespace which does not need an explicit reference. - For query rules, the default namespace is the query terms. -</p><p> - To determine the namespace used to read from conditions or change in productions, use: -<pre> -<namespace>.<a href="#conditions"><condition></a> -<namespace>.<a href="#production"><production></a> -</pre> -<p>There are two namespaces defined during query processing:</p> -<table class="table"> - <thead> - <tr> - <th>Namespace</th> - <th>Syntax</th> - <th>Positional</th> - <th>Description</th> - </tr> - </thead><tbody> - <tr> - <td>Query</td> - <td></td> - <td>Yes</td> - <td>The default namespace. References the terms of the - query. The condition value returned will be the term itself.</td> - </tr><tr> - <td>Parameter</td> - <td><code>parameter.</code></td> - <td>No</td> - <td>References the parameter of the query. Conditions will be - true if the parameter is set in the query. The value - returned from conditions is the value of the parameter. - Productions will need both a key and value specified to set - a parameter value.</td> - </tr> - </tbody> -</table> - - - -<h2 id="named-conditions">Named Conditions</h2> -<p> - A named condition is on the form: -</p> -<pre> -[condition-name] :- <a href="#conditions"><condition></a>; -</pre> -<p> - This simply assigns a name to the condition on the right, - so it can be referred to the conditions in rules and other named conditions. -</p> - - - -<h2 id="conditions">Conditions</h2> -<p> - A condition is an expression which evaluates to true or false - over the <em>facts</em> of a <a href="#namespaces">namespace</a>. - If the namespace is <em>positional</em> (a <em>sequence</em>), - evaluation starts at the <em>current position</em> in the namespace. - When evaluated true, conditions will also return a value which can be referenced by comparison conditions. -</p><p> - Conditions may be preceded by a reference name and a label: -</p> -<pre> -(<a href="#reference-name"><reference-name></a>/)?(<a href="#condition-label"><label></a>:)?<a href="#condition"><condition></a> -</pre> - - -<h3 id="reference-name">Reference Name</h3> -<p> - The reference name allows an explicit name to be set, - from which the terms matched by the condition can be referred in a condition. - This is useful when multiple conditions of the same type are used in the condition of the same rule. -</p><p> - If no reference name is given, - the text standing between the square brackets of the condition is used as reference name. -</p> - - -<h3 id="condition-label">Label</h3> -<p> - If a label is specified, - the condition will only match terms having that label (the label is the index in query terms). - If a label is not set, the term will match if a label is not set, or if it is <code>default</code>. -</p> - - -<h3 id="condition">Condition</h3> -<p> - These are the supported kinds of conditions: -</p> -<table class="table"> - <thead> - <tr> - <th>Condition</th> - <th>Syntax</th> - <th>Meaning</th> - <th>Returned value</th> - </tr> - </thead><tbody> - <tr> - <td>Term</td> - <td><term></td> - <td>True if this is the term at the current position</td> - <td>Determined by the <a href="#namespaces">namespace</a></td> - </tr> - <tr> - <td>Reference (produce the matched term(s))</td> - <td>[<condition-name>]</td> - <td>Evaluate a named condition</td> - <td>The matched term(s) of the condition</td> - </tr> - <tr> - <td>Reference (produce all terms in the condition)</td> - <td>[<condition-name>*]</td> - <td>Evaluate a named condition</td> - <td>All the terms in the condition</td> - </tr> - <tr> - <td>Sequence</td> - <td><condition> <condition></td> - <td>Match both conditions by consecutive terms in the right order in the sequence</td> - <td>The last nested condition value</td> - </tr> - <tr> - <td>Choice</td> - <td><condition>, <condition></td> - <td>Match any one of the conditions, each one tried at the current position</td> - <td>The last nested condition value</td> - </tr> - <tr> - <td>Group</td> - <td>(<condition>)</td> - <td>Evaluate the condition inside the grouping as a unit</td> - <td>The last nested condition value</td> - </tr> - <tr> - <td>Ellipsis</td> - <td>…</td> - <td>Matches any sequence to make the overall condition match</td> - <td>The matched sequence</td> - </tr> - <tr> - <td>Referable ellipsis</td> - <td>[…]</td> - <td>An ellipsis where the matched sequence can be referenced from the production</td> - <td>The matched sequence</td> - </tr> - <tr> - <td>Not</td> - <td>!<condition></td> - <td>Matches if the condition does not match</td> - <td>Nothing</td> - </tr> - <tr> - <td>And</td> - <td><condition> & <condition></td> - <td>Matches if all the conditions matches at the (same) current position</td> - <td>The last nested condition value</td> - </tr> - <tr> - <td>Comparison</td> - <td><condition> <a href="#operator"><operator></a> <condition></td> - <td>True if the comparison is true for the values returned from the conditions</td> - <td>The last nested condition value</td> - </tr> - <tr> - <td>Literal</td> - <td>'<literal>'</td> - <td>Returns a value for comparison. This always evaluates to true.</td> - <td>The literal value</td> - </tr> - <tr> - <td>Start anchor</td> - <td>. <condition></td> - <td>Matches condition only if it matches the query from the start</td> - <td>The matched sequence</td> - </tr> - <tr> - <td>End anchor</td> - <td><condition> .</td> - <td>Matches condition only if it matches the query to the end</td> - <td>The matched sequence</td> - </tr> - </tbody> -</table> - - -<h3 id="operator">Comparison Condition Operators</h3> -<p> - The possible operators of a comparison condition are: -</p> -<table class="table"> - <thead> - <tr> - <th>Operator</th> - <th>Meaning</th> - </tr> - </thead><tbody> - <tr><td>=</td><td>Left and right values are equal</td></tr> - <tr><td><=</td><td>Left value is smaller or equal</td></tr> - <tr><td>>=</td><td>Left value is larger or equal</td></tr> - <tr><td><</td><td>Left value is smaller</td></tr> - <tr><td>></td><td>Left value is larger</td></tr> - <tr><td>=~</td><td>Left value contains right value as a substring</td></tr> - </tbody> -</table> - - - -<h2 id="production-list">Production List</h2> -<p> - A production list consists of a space-separated list of <em>productions</em> - which are carried out when the production of a rule is matched. - A production can be preceded by the type of term to produce, a label (index in queries), - and followed by the weight (importance) of the produced value: -</p> -<pre> -(<a href="#term-type"><term-type></a>)?(<a href="#production-label"><label></a>:)?<a href="#production"><production></a>(!<a href="#weight"><weight></a>)? -</pre> - - -<h3 id="term-type">Term Type</h3> -<p> - The default term type is the term type of the context which the term is added to. - The possible explicit term types are: -</p> -<table class="table"> - <thead> - <tr><th>Syntax</th><th>Meaning</th></tr> - </thead><tbody> - <tr><td>?</td><td>Insert as an OR term</td></tr> - <tr><td>=</td><td>Insert as an EQUIV term</td></tr> - <tr><td>+</td><td>Insert as an AND term</td></tr> - <tr><td>$</td><td>Insert as a RANK term</td></tr> - <tr><td>-</td><td>Insert as a NOT term</td></tr> - </tbody> -</table> - - -<h3 id="production-label">Label</h3> -<p> - If included, the label decides the label the produced term(s) will have in the namespace. - This is the index in the query namespace. -</p> - - -<h3 id="production">Production</h3> -<p> - There are three types of productions: -</p> -<table class="table"> - <thead> - <tr><th>Production</th><th>Syntax</th><th>Meaning</th></tr> - </thead><tbody> - <tr> - <td>Literal term</td> - <td><term></td> - <td>Produce this term literally</td> - </tr><tr> - <td>Literal term with value</td> - <td><term>='<value>'</td> - <td>Produce this term and value literally.</td> - </tr><tr> - <td>Reference</td> - <td>[<condition-reference>]</td> - <td>Produce the terms matched by the referenced condition. The - reference name is either the name of a named condition used in - the condition, an - ellipsis - <code>…</code> - or an explicit - condition reference name. - </td> - </tr> - </tbody> -</table> - - -<h3 id="weight">Weight</h3> -<p> - The weight is a percentage integer denoting the importance of the produced term. - The default is 100. - In the query namespace the weight becomes the term weight, - determining the relevance contribution of the term. -</p> diff --git a/en/reference/querying/semantic-rules.mdx b/en/reference/querying/semantic-rules.mdx new file mode 100644 index 0000000000..3ed2918c7e --- /dev/null +++ b/en/reference/querying/semantic-rules.mdx @@ -0,0 +1,392 @@ +--- +title: "Semantic Rule Language Reference" +sidebarTitle: "Semantic rules" +--- + + +This is the reference for the semantic rule language in Vespa. For a guide on using this language, see [query rewriting](/en/linguistics/query-rewriting). Refer to the [Query API](/en/reference/api/query#semantic-rules) for how to use in queries. + +## Rule bases + +Semantic rules are collected in files called _rule bases_. The name of these files are _[rule-base-name].sr_. They must be placed in _[application-package]/rules/_ to be deployed. + +## Basic syntax + +A rule base may contain any number of the following four constructs, explained in the rest of this document: + +- [directives](#directives) +- [production rules](#production-rules) +- [named conditions](#named-conditions) +- comments, starting by # and ending by newline. + +Production rules and named conditions are _statements_. Statements may span multiple lines and are terminated by `;`. + +## Directives + +A directive is a "meta-level" statement which is not used during rule evaluation, but tells the rule engine how to use the rule base. A statement starts by `@` and ends by newline. They may take parameters. These directives exist: + +<table className="vespa-table"> + <thead> + <tr> + <th>Statement</th> + <th>Usage</th> + <th>Location</th> + </tr> + </thead> + <tbody> + <tr> + <td>@default</td> + <td>Make this rule base the default, to be used with all queries</td> + <td>Anywhere outside other statements</td> + </tr> + <tr> + <td>@automata(<automata-filename>)</td> + <td>Use an automata file with this base</td> + <td>Anywhere outside other statements</td> + </tr> + <tr> + <td>@include(<rulebase-name>)</td> + <td>Include all the statements of another rule base in this</td> + <td>Anywhere outside other statements</td> + </tr> + <tr> + <td>@super</td> + <td>Include the conditions of the same-named conditions from the included rule base</td> + <td>In a condition</td> + </tr> + <tr> + <td>@stemming(<true|false>)</td> + <td>Whether terms should match after stemming or exactly (true by default)</td> + <td>Before any rule</td> + </tr> + <tr> + <td>@language(<<a href="https://en.wikipedia.org/wiki/ISO_639-1" aria-label="language-code">language-code</a>>)</td> + <td>The language of the rule base, which should also be the query language. Influences stemming.</td> + <td>Before any rule</td> + </tr> + </tbody> +</table> + +## Production Rules + +A production rule is of the form: + +<Card> +[\<condition\>](#conditions)\<operator\>[\<production-list\>](#production-list); +</Card> + +This performs the production as defined by the operator if the condition matches. There are two kinds of production rules (and two operators), replacing and adding: + +<table className="vespa-table"> + <thead> + <tr> + <th>Rule kind</th> + <th>Operator</th> + <th>Meaning</th> + </tr> + </thead> + <tbody> + <tr> + <td>Replacing</td> + <td>-></td> + <td>_Replace_ the matched terms by the production</td> + </tr> + <tr> + <td>Adding</td> + <td>+></td> + <td>_Add_ the production to the matched terms</td> + </tr> + </tbody> +</table> + +## Namespaces + +A namespace is a collection of facts which can be read from conditions and changed by productions. Namespaces may be positional (sequences), or not. A positional namespace will track the current fact and match and insert at the current position, while non-positional namespaces will match any fact against any condition. + +There is a default namespace which does not need an explicit reference. For query rules, the default namespace is the query terms. + +To determine the namespace used to read from conditions or change in productions, use: + +<Card> +\<namespace>.[\<condition\>](#conditions)<br/> +\<namespace>.[\<production\>](#production) +</Card> + +There are two namespaces defined during query processing: + +<table className="vespa-table"> + <thead> + <tr> + <th>Namespace</th> + <th>Syntax</th> + <th>Positional</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>Query</td> + <td></td> + <td>Yes</td> + <td>The default namespace. References the terms of the query. The condition value returned will be the term itself.</td> + </tr> + <tr> + <td>Parameter</td> + <td><code>{`parameter.`}</code></td> + <td>No</td> + <td>References the parameter of the query. Conditions will be true if the parameter is set in the query. The value returned from conditions is the value of the parameter. Productions will need both a key and value specified to set a parameter value.</td> + </tr> + </tbody> +</table> + +## Named Conditions + +A named condition is on the form: + +<Card> +[condition-name] :-[\<condition\>](#conditions); +</Card> + +This simply assigns a name to the condition on the right, so it can be referred to the conditions in rules and other named conditions. + +## Conditions + +A condition is an expression which evaluates to true or false over the _facts_ of a [namespace](#namespaces). If the namespace is _positional_ (a _sequence_), evaluation starts at the _current position_ in the namespace. When evaluated true, conditions will also return a value which can be referenced by comparison conditions. + +Conditions may be preceded by a reference name and a label: + +<Card> +([\<reference-name\>](#reference-name)/)?([\<label\>](#condition-label):)?[\<condition\>](#condition) +</Card> + +### Reference Name + +The reference name allows an explicit name to be set, from which the terms matched by the condition can be referred in a condition. This is useful when multiple conditions of the same type are used in the condition of the same rule. + +If no reference name is given, the text standing between the square brackets of the condition is used as reference name. + +### Label + +If a label is specified, the condition will only match terms having that label (the label is the index in query terms). If a label is not set, the term will match if a label is not set, or if it is `default`. + +### Condition + +These are the supported kinds of conditions: + +<table className="vespa-table"> + <thead> + <tr> + <th>Condition</th> + <th>Syntax</th> + <th>Meaning</th> + <th>Returned value</th> + </tr> + </thead> + <tbody> + <tr> + <td>Term</td> + <td><term></td> + <td>True if this is the term at the current position</td> + <td>Determined by the <a href="#namespaces" aria-label="namespace">namespace</a></td> + </tr> + <tr> + <td>Reference (produce the matched term(s))</td> + <td>[<condition-name>]</td> + <td>Evaluate a named condition</td> + <td>The matched term(s) of the condition</td> + </tr> + <tr> + <td>Reference (produce all terms in the condition)</td> + <td>[<condition-name>*]</td> + <td>Evaluate a named condition</td> + <td>All the terms in the condition</td> + </tr> + <tr> + <td>Sequence</td> + <td><condition> <condition></td> + <td>Match both conditions by consecutive terms in the right order in the sequence</td> + <td>The last nested condition value</td> + </tr> + <tr> + <td>Choice</td> + <td><condition>, <condition></td> + <td>Match any one of the conditions, each one tried at the current position</td> + <td>The last nested condition value</td> + </tr> + <tr> + <td>Group</td> + <td>(<condition>)</td> + <td>Evaluate the condition inside the grouping as a unit</td> + <td>The last nested condition value</td> + </tr> + <tr> + <td>Ellipsis</td> + <td>…</td> + <td>Matches any sequence to make the overall condition match</td> + <td>The matched sequence</td> + </tr> + <tr> + <td>Referable ellipsis</td> + <td>[…]</td> + <td>An ellipsis where the matched sequence can be referenced from the production</td> + <td>The matched sequence</td> + </tr> + <tr> + <td>Not</td> + <td>!<condition></td> + <td>Matches if the condition does not match</td> + <td>Nothing</td> + </tr> + <tr> + <td>And</td> + <td><condition> & <condition></td> + <td>Matches if all the conditions matches at the (same) current position</td> + <td>The last nested condition value</td> + </tr> + <tr> + <td>Comparison</td> + <td><condition> <a href="#operator" aria-label="<operator>"><operator></a> <condition></td> + <td>True if the comparison is true for the values returned from the conditions</td> + <td>The last nested condition value</td> + </tr> + <tr> + <td>Literal</td> + <td>'<literal>'</td> + <td>Returns a value for comparison. This always evaluates to true.</td> + <td>The literal value</td> + </tr> + <tr> + <td>Start anchor</td> + <td>. <condition></td> + <td>Matches condition only if it matches the query from the start</td> + <td>The matched sequence</td> + </tr> + <tr> + <td>End anchor</td> + <td><condition> .</td> + <td>Matches condition only if it matches the query to the end</td> + <td>The matched sequence</td> + </tr> + </tbody> +</table> + +### Comparison Condition Operators + +The possible operators of a comparison condition are: + +<table className="vespa-table"> + <thead> + <tr> + <th>Operator</th> + <th>Meaning</th> + </tr> + </thead> + <tbody> + <tr> + <td>=</td> + <td>Left and right values are equal</td> + </tr> + <tr> + <td><=</td> + <td>Left value is smaller or equal</td> + </tr> + <tr> + <td>>=</td> + <td>Left value is larger or equal</td> + </tr> + <tr> + <td><</td> + <td>Left value is smaller</td> + </tr> + <tr> + <td>></td> + <td>Left value is larger</td> + </tr> + <tr> + <td>=~</td> + <td>Left value contains right value as a substring</td> + </tr> + </tbody> +</table> + +## Production List + +A production list consists of a space-separated list of _productions_ which are carried out when the production of a rule is matched. A production can be preceded by the type of term to produce, a label (index in queries), and followed by the weight (importance) of the produced value: + +<Card> +([\<term-type\>](#term-type))?([\<label\>](#production-label):)?[\<production\>](#production)(! [\<weight\>](#weight))? +</Card> + +### Term Type + +The default term type is the term type of the context which the term is added to. The possible explicit term types are: + +<table className="vespa-table"> + <thead> + <tr> + <th>Syntax</th> + <th>Meaning</th> + </tr> + </thead> + <tbody> + <tr> + <td>?</td> + <td>Insert as an OR term</td> + </tr> + <tr> + <td>=</td> + <td>Insert as an EQUIV term</td> + </tr> + <tr> + <td>+</td> + <td>Insert as an AND term</td> + </tr> + <tr> + <td>$</td> + <td>Insert as a RANK term</td> + </tr> + <tr> + <td>-</td> + <td>Insert as a NOT term</td> + </tr> + </tbody> +</table> + +### Label + +If included, the label decides the label the produced term(s) will have in the namespace. This is the index in the query namespace. + +### Production + +There are three types of productions: + +<table className="vespa-table"> + <thead> + <tr> + <th>Production</th> + <th>Syntax</th> + <th>Meaning</th> + </tr> + </thead> + <tbody> + <tr> + <td>Literal term</td> + <td><term></td> + <td>Produce this term literally</td> + </tr> + <tr> + <td>Literal term with value</td> + <td><term>='<value>'</td> + <td>Produce this term and value literally.</td> + </tr> + <tr> + <td>Reference</td> + <td>[<condition-reference>]</td> + <td>Produce the terms matched by the referenced condition. The reference name is either the name of a named condition used in the condition, an ellipsis - <code>{`...`}</code> - or an explicit condition reference name.</td> + </tr> + </tbody> +</table> + +### Weight + +The weight is a percentage integer denoting the importance of the produced term. The default is 100. In the query namespace the weight becomes the term weight, determining the relevance contribution of the term. \ No newline at end of file diff --git a/en/reference/querying/simple-query-language.html b/en/reference/querying/simple-query-language.html deleted file mode 100644 index 69726b3e87..0000000000 --- a/en/reference/querying/simple-query-language.html +++ /dev/null @@ -1,247 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Simple Query Language Reference" -redirect_from: -- /en/reference/simple-query-language-reference.html ---- - -<p> -The <em>simple query language</em> allows application end users -to issue more complex queries than a list of tokens. -It is a heuristic, non-structured language, -which attempts to do something probably-useful with any input given. -It is combined with the structured <a href="../../querying/query-language.html">YQL</a> -by using the <a href="yql.html#userquery">userQuery</a> operator. -</p> - -<h3 id="simple-syntax">Simple Query Syntax</h3> -<pre> -Query ::= Expr ( SPACE Expr )* -Expr ::= Term | Prefix? '(' SimpleTerm+ ')' -Term ::= Prefix? Field? CoreTerm Weight? -SimpleTerm ::= Field? CoreTerm Weight? -Prefix ::= '+' | '-' -Field ::= ID ':' /* A valid field name or alias */ -Weight ::= '!'+ | '!' NUM /* NUM is a percentage. */ -CoreTerm ::= WORD | Phrase | NumTerm | PrefixTerm | SubstringTerm | SuffixTerm | SameElement -Phrase ::= '"' WORD+ '"' -NumTerm ::= NUM | '<' NUM | '>' NUM | '[' NUM? ';' NUM? ';' HITLIMIT? ']' - /* NUM is any numeric type including floating point */ - /* HITLIMIT is a optional count of many hits you want as minimum from this range */ -PrefixTerm ::= WORD '*' -SubstringTerm ::= '*' WORD '*' -SuffixTerm ::= '*' WORD -SameElement ::= '{' Field CoreTerm ( SPACE Field CoreTerm )* '}' -</pre> - - -<h3 id="prefix-searching">Prefix searching</h3> -<p> -Prefix matching is only available for attributes. -A prefix search term (e.g. 'car*') behaves like a pattern match on the -given field: Documents that have a field value beginning with the -given prefix are matched and returned (or not returned if the '-' syntax is -used). A prefix search term does not add or change the ranking of the -documents in the result set. -</p> - - -<h3 id="term-weight">Term weight</h3> -<p> -The weight is either one or more ! characters, or a ! followed by an -integer. The integer is a fixed point scaling number with decimal -factor 100, i.e. it can be regarded as a percentage. When using -repeated ! characters, the weight is increased with 50 (from a default -value of 100) for each !. A weight expression may also be applied to a phrase. -</p><p> -A term weight is used to modify the relative importance of the terms -in your query. The term score is only one part of the overall rank -calculation, but by adding weight to the most important terms, you can -ensure that they contribute more. For more details on rank -calculation, see <a href="../../basics/ranking.html">Ranking guide</a>. -</p> - - -<h3 id="numerical-terms">Numerical terms</h3> -<p> -<code>[x;y]</code> matches any number between <em>x</em> and -<em>y</em>, including the endpoints <em>x</em> and -<em>y</em>. Note that <code>>number</code> is the same as -<code>[number+1;]</code> and <code><number</code> is the same -as <code>[;number-1]</code>. -</p><p> -A few examples using numerical terms: -<pre>perl size:<100</pre> -This query will get all documents with the word “perl” and -with size less than 100Kb. -<pre>chess kasparov -karpov date:[19990101;19991231]</pre> -<p> -This query will get all documents last modified in 1999 containing -<em>chess</em> and <em>kasparov</em>, but not <em>karpov</em>. -</p> - -<h4 id="fast-range-search">Advanced range search</h4> -<p> -In order to quickly fetch the best documents given a simple range you can do -that efficiently using capped range search. For it to be efficient it requires that -you use <a href="../schemas/schemas.html#attribute">fast-search</a> on the attribute -used for range search. -</p><p> -It is fast because it will start only scan enough terms in the dictionary -to satisfy the number of documents requested. A positive number will start from the -left of your range and work its way right. A negative number will start from right and go left. -</p> -<pre>date:[0;21000101;10]</pre> -Will give you the at least 10 first documents since the birth of Jesus. -<pre>date:[0;21000101;-10]</pre> -<p> -Will give you the at least 10 last documents since the birth of Jesus. -</p> - - -<h3 id="simple-grouping">Grouping in the simple query language</h3> -<p> -There is only one level of parentheses supported; any use of -additional parentheses within the expression will be ignored. In -addition, note that the terms within should not be prefixed with + or -. -</p><p> -When the parentheses are prefixed by a + (can be excluded -for <code>all</code> type, because expressions are + by default), the -search requires that at least one of the included terms is present in -the document. This effectively gives you a way of having alternative -terms expressing the same intent, while requiring that the concept is -covered in the document. -</p><p> -When the parentheses are prefixed by a -, the search excludes all -documents that include all the terms, but allows documents that only -use some of the terms in the expression. It is a bit more difficult to -find good use for this syntax; it could for instance be used to remove -documents that compare two different products, while still allowing -documents only discussing one of them. -</p> - - - -<h2 id="url_field">Search in URLs</h2> -<p> -Create a URL-field in the index by creating a field of type -<a href="../schemas/schemas.html#uri">uri</a> - -refer to this for how to build queries. -The indexer will report an ERROR in the log for invalid URLs. Notes: -</p> -<ul> - <li> - Note however that finding documents matching a full URL does not - behave like exact matching in i.e. string fields, but more like substring matching. - A search for <code>myurlfield:http://www.mydomain.com/</code> will match documents - where <em>myurlfield</em> is set to both <em>http://www.mydomain.com/</em>, - <em>http://www.mydomain.com/test</em>, and <em>http://redirect.com/?goto=http://www.mydomain.com/</em> - </li><li> - Hostname searches have an anchoring mechanism to limit which URLs match. - By default, queries are anchored in the end, - which means that a search for <code>mydomain.com</code> will match <code>www.mydomain.com</code>, - but not <code>mydomain.com.au</code>. - Adding a ^ (caret) to the start will turn on anchoring at the start, - meaning that the query will only return exact matches. - Adding a <code>*</code> at the end will turn off anchoring at the end. - The query <code>^mydomain.com*</code> will match <code>mydomain.com.au</code>, - but not <code>www.mydomain.com</code>. - </li> -</ul> - - - -<h2 id="map-syntax">Field Path Syntax</h2> -<p> -Streaming search supports the <a href="../schemas/document-field-path.html">field path</a> -syntax of the <a href="../writing/document-selector-language.html"> -document selection language</a> when searching structs and maps. -Special for the map type is the ability to select a subset of -map entries to search using the <code>mymap{"foo"} </code> syntax. -</p><p> -See the <a href="../schemas/document-field-path.html">field path</a> documentation -for use-cases of the map data type. -</p><p> -In the result output, a map is represented in the same way as in the Document XML: -</p> -<pre> -<field name="mymap"> - <item><key>foo</key><value>bar</value></item> - <item><key>fuz</key><value>baz</value></item> -</field> -</pre><!-- ToDo: rewrite to JSON --> - - - -<h2 id="removing-syntax-characters">Removing syntax characters from queries</h2> -<p> -It will sometimes be more robust to remove characters which are used -in the query syntax from a user's search terms. An example could be -URLs containing parentheses. Comma ("," ASCII 0x2C) may be -used as a safe replacement character in these cases. -</p> -<pre>(x url:http://site.com/a)b) y</pre> -<p>The URL <code>http://site.com/a)b</code> in this example could be quoted as following:</p> -<pre>(x url:http://site.com/a,b) y</pre> - - - -<h2 id="examples">Examples</h2> -<p> -The <em>simple</em> query language syntax accepts any input string and makes the most of it. -A basic query consists of words separated by spaces (encoded as %20). In addition, -</p> -<ul> - <li> - A phrase can be searched by enclosing it in quotes, like - <code>"match exactly this"</code> - </li><li> - Phrases and words may be preceded by -, meaning documents <em>must not</em> contain this - </li><li> - Phrases and words may be preceded by +, meaning documents - <em>must</em> contain this, currently only in use for subtype <code>any</code> - </li><li> - Groups of words and phrases may be grouped using parenthesis, like - <code>-(do not match if all of these words matches)</code> - </li><li> - Each word or phrase may be preceded by an index or attribute name and a colon, - like <code>indexname:word</code>, to match in that index. - If the index name is omitted the index named <em>default</em> is searched. - </li> -</ul> -<p> -Any <em>noise</em> (characters not in indexes or attributes, and with no query language meaning) -is ignored, all query strings are valid. -The exception is queries which have no meaningful interpretation. -An example is <code>-a</code>, which one would expect to return -all documents <em>not</em> containing <em>a</em>. -Vespa, however, will return the error message <em>Null query</em>. -All the following examples are of type <em>all</em>. -</p><p> -Get all documents with the term <em>word</em>, -having <em>microsoft</em> but not <em>bug</em> in the title: -<pre> -word title:microsoft -title:bug -</pre> -Search for all documents having the phrase "<em>to be or not to be</em>", -but excluding those having <em>shakespeare</em> in the title: -<pre> -"to be or not to be" -title:shakespeare -</pre> -Get all documents with the word <em>Christmas</em> in the title that -were last modified Christmas Day 2009: -<pre> -title:Christmas date:20091225 -</pre> -Get documents on US Foreign politics, excluding those matching both -rival presidential candidates: -<pre> -"us foreign politics" -(clinton trump) -</pre> -<p> - Get documents on US Foreign politics, including only those matching at - least one of the rival presidential candidates: -</p> -<pre> -"us foreign politics" (clinton trump) -</pre> diff --git a/en/reference/querying/simple-query-language.mdx b/en/reference/querying/simple-query-language.mdx new file mode 100644 index 0000000000..bc3c7427f1 --- /dev/null +++ b/en/reference/querying/simple-query-language.mdx @@ -0,0 +1,148 @@ +--- +title: "Simple Query Language Reference" +--- + +The _simple query language_ allows application end users to issue more complex queries than a list of tokens. It is a heuristic, non-structured language, which attempts to do something probably-useful with any input given. It is combined with the structured [YQL](/en/reference/querying/grouping-language) by using the [userQuery](/en/reference/querying/grouping-language#userquery) operator. + +### Simple Query Syntax + +```bash expandable +Query ::= Expr ( SPACE Expr )* +Expr ::= Term | Prefix? '(' SimpleTerm+ ')' +Term ::= Prefix? Field? CoreTerm Weight? +SimpleTerm ::= Field? CoreTerm Weight? +Prefix ::= '+' | '-' +Field ::= ID ':' /* A valid field name or alias */ +Weight ::= '!'+ | '!' NUM /* NUM is a percentage. */ +CoreTerm ::= WORD | Phrase | NumTerm | PrefixTerm | SubstringTerm | SuffixTerm | SameElement +Phrase ::= '"' WORD+ '"' +NumTerm ::= NUM | '<' NUM | '>' NUM | '[' NUM? ';' NUM? ';' HITLIMIT? ']' + /* NUM is any numeric type including floating point */ + /* HITLIMIT is a optional count of many hits you want as minimum from this range */ +PrefixTerm ::= WORD '*' +SubstringTerm ::= '*' WORD '*' +SuffixTerm ::= '*' WORD +SameElement ::= '{' Field CoreTerm ( SPACE Field CoreTerm )* '}' +``` + +### Prefix searching + +Prefix matching is only available for attributes. A prefix search term (e.g. 'car\*') behaves like a pattern match on the given field: Documents that have a field value beginning with the given prefix are matched and returned (or not returned if the '-' syntax is used). A prefix search term does not add or change the ranking of the documents in the result set. + +### Term weight + +The weight is either one or more ! characters, or a ! followed by an integer. The integer is a fixed point scaling number with decimal factor 100, i.e. it can be regarded as a percentage. When using repeated ! characters, the weight is increased with 50 (from a default value of 100) for each !. A weight expression may also be applied to a phrase. + +A term weight is used to modify the relative importance of the terms in your query. The term score is only one part of the overall rank calculation, but by adding weight to the most important terms, you can ensure that they contribute more. For more details on rank calculation, see [Ranking guide](/en/basics/ranking). + +### Numerical terms + +`[x;y]` matches any number between _x_ and _y_, including the endpoints _x_ and _y_. Note that `>number` is the same as `[number+1;]` and `<number` is the same as `[;number-1]`. + +A few examples using numerical terms: + +```bash +perl size:<100 +``` +This query will get all documents with the word “perl” and with size less than 100Kb. +```bash +chess kasparov -karpov date:[19990101;19991231] +``` + +This query will get all documents last modified in 1999 containing _chess_ and _kasparov_, but not _karpov_. + +#### Advanced range search + +In order to quickly fetch the best documents given a simple range you can do that efficiently using capped range search. For it to be efficient it requires that you use [fast-search](/en/reference/schemas/schemas#attribute) on the attribute used for range search. + +It is fast because it will start only scan enough terms in the dictionary to satisfy the number of documents requested. A positive number will start from the left of your range and work its way right. A negative number will start from right and go left. + +```bash +date:[0;21000101;10] +``` +Will give you the at least 10 first documents since the birth of Jesus. +```bash +date:[0;21000101;-10] +``` + +Will give you the at least 10 last documents since the birth of Jesus. + +### Grouping in the simple query language + +There is only one level of parentheses supported; any use of additional parentheses within the expression will be ignored. In addition, note that the terms within should not be prefixed with + or -. + +When the parentheses are prefixed by a + (can be excluded for `all` type, because expressions are + by default), the search requires that at least one of the included terms is present in the document. This effectively gives you a way of having alternative terms expressing the same intent, while requiring that the concept is covered in the document. + +When the parentheses are prefixed by a -, the search excludes all documents that include all the terms, but allows documents that only use some of the terms in the expression. It is a bit more difficult to find good use for this syntax; it could for instance be used to remove documents that compare two different products, while still allowing documents only discussing one of them. + +## Search in URLs + +Create a URL-field in the index by creating a field of type [uri](/en/reference/schemas/schemas#uri) - refer to this for how to build queries. The indexer will report an ERROR in the log for invalid URLs. Notes: + +- Note however that finding documents matching a full URL does not behave like exact matching in i.e. string fields, but more like substring matching. A search for `myurlfield:http://www.mydomain.com/` will match documents where _myurlfield_ is set to both _http://www.mydomain.com/_, _http://www.mydomain.com/test_, and _http://redirect.com/?goto=http://www.mydomain.com/_ +- Hostname searches have an anchoring mechanism to limit which URLs match. By default, queries are anchored in the end, which means that a search for `mydomain.com` will match `www.mydomain.com`, but not `mydomain.com.au`. Adding a ^ (caret) to the start will turn on anchoring at the start, meaning that the query will only return exact matches. Adding a `*` at the end will turn off anchoring at the end. The query `^mydomain.com*` will match `mydomain.com.au`, but not `www.mydomain.com`. + +## Field Path Syntax + +Streaming search supports the [field path](/en/reference/schemas/document-field-path) syntax of the [document selection language](/en/reference/writing/document-selector-language) when searching structs and maps. Special for the map type is the ability to select a subset of map entries to search using the `mymap{"foo"} ` syntax. + +See the [field path](/en/reference/schemas/document-field-path) documentation for use-cases of the map data type. + +In the result output, a map is represented in the same way as in the Document XML: + +```xml +<field name="mymap"> + <item><key>foo</key><value>bar</value></item> + <item><key>fuz</key><value>baz</value></item> +</field> +``` + +## Removing syntax characters from queries + +It will sometimes be more robust to remove characters which are used in the query syntax from a user's search terms. An example could be URLs containing parentheses. Comma ("," ASCII 0x2C) may be used as a safe replacement character in these cases. + +```yaml +(x url:http://site.com/a)b) y +``` + +The URL `http://site.com/a)b` in this example could be quoted as following: + +```yaml +(x url:http://site.com/a,b) y +``` + +## Examples + +The _simple_ query language syntax accepts any input string and makes the most of it. A basic query consists of words separated by spaces (encoded as %20). In addition, + +- A phrase can be searched by enclosing it in quotes, like `"match exactly this"` +- Phrases and words may be preceded by -, meaning documents _must not_ contain this +- Phrases and words may be preceded by +, meaning documents _must_ contain this, currently only in use for subtype `any` +- Groups of words and phrases may be grouped using parenthesis, like `-(do not match if all of these words matches)` +- Each word or phrase may be preceded by an index or attribute name and a colon, like `indexname:word`, to match in that index. If the index name is omitted the index named _default_ is searched. + +Any _noise_ (characters not in indexes or attributes, and with no query language meaning) is ignored, all query strings are valid. The exception is queries which have no meaningful interpretation. An example is `-a`, which one would expect to return all documents _not_ containing _a_. Vespa, however, will return the error message _Null query_. All the following examples are of type _all_. + +Get all documents with the term _word_, having _microsoft_ but not _bug_ in the title: + +```bash +word title:microsoft -title:bug +``` +Search for all documents having the phrase "_to be or not to be_", but excluding those having _shakespeare_ in the title: +```bash +"to be or not to be" -title:shakespeare +``` +Get all documents with the word _Christmas_ in the title that were last modified Christmas Day 2009: +```bash +title:Christmas date:20091225 +``` +Get documents on US Foreign politics, excluding those matching both rival presidential candidates: +```bash +"us foreign politics" -(clinton trump) +``` + +Get documents on US Foreign politics, including only those matching at least one of the rival presidential candidates: + +```bash +"us foreign politics" (clinton trump) +``` \ No newline at end of file diff --git a/en/reference/querying/sorting-language.html b/en/reference/querying/sorting-language.html deleted file mode 100644 index 901120c9c3..0000000000 --- a/en/reference/querying/sorting-language.html +++ /dev/null @@ -1,266 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Sorting Reference" -redirect_from: -- /en/reference/sorting.html ---- - -<p id="sortspec"> -A sorting specification in a query consists of one or more -sorting expressions. Each sorting expression is an optional sort order -followed by an attribute name or a function over an attribute. -Multiple expressions are separated by a single SPACE character. -</p><p> -Using more than one sort expression will give you multilevel -sorting. In this case, the most significant expression is the -first, while subsequent expressions detail sorting within groups -of equal values for the previous expression. -</p> -<pre> -Sorting ::= SortExpr ( ' ' SortExpr )* -SortExpr ::= [SortOrder] ( SortObject | MissingExpr ) -MissingExpr ::= MISSING '(' SortObject ',' MissingPolicy ')' -MissingPolicy ::= FIRST | - LAST | - AS ',' MissingValue -SortObject ::= SortAttribute | Function -Function ::= LOWERCASE '(' SortAttribute ')' | - RAW '(' SortAttribute ')' | - UCA '(' SortAttribute [ ',' Locale [ ',' Strength] ] ')' -MISSING ::= 'missing' -FIRST ::= 'first' -LAST ::= 'last' -AS ::= 'as' -LOWERCASE ::= 'lowercase' -UCA ::= 'uca' -RAW ::= 'raw' -Locale ::= An identifier following <a href="https://www.unicode.org/reports/tr35/#Unicode_Language_and_Locale_Identifiers">unicode locale identifiers,</a> fx 'en_US'. -Strength :: 'PRIMARY' | 'SECONDARY' | 'TERTIARY' | 'QUATERNARY' | 'IDENTICAL' -SortOrder ::= '+' | '-' -SortAttribute ::= ID | ID.key | ID.value /* A valid attribute name, with key/value appended for maps */ -MissingValue ::= QuotedString | Token -Token ::= TokenChar* -TokenChar ::= any non Delimiter -QuotedString ::= '"' ( StringChar | '\' QuotedChar )* '"' -StringChar ::= any non QuotedChar -Delimiter ::= ' ' | ',' | '(' | ')' | QuotedChar -QuotedChar ::= '\' | '"' -</pre> -<p> -See <a href="../../querying/geo-search.html">Geo search</a> for sorting by distance. -Refer to <a href="yql.html#order-by"> -YQL Vespa reference</a> for how to set sorting parameters in YQL. -</p> - - - -<h2 id="sort-order">Sort order</h2> -<p> -<code>+</code> denotes ascending sorting order, -while <code>-</code> gives descending order. -Ascending order is defined as lowest values first for numerical attributes. -Strings are sorted according to the sort function chosen. -Descending order is the reverse of ascending order. -</p><p> -Note: <code>+</code> in query URLs must be encoded as %2B - -for consistency, <code>-</code> can be encoded as %2D. -</p> - - -<h3 id="default-sort-order">Default sort order</h3> -<p> -If <code>+</code>/<code>-</code> is omitted, the default is used, -either the system-wide default of <code>+</code> -or any override in <a href="../schemas/schemas.html#sorting">schema</a>. -Default sort order is <code>+</code> or ascending, -except for <code>[rank]</code> or the special builtin <code>[relevance]</code>, -which has <code>-</code> or descending. -</p> - - - -<h2 id="sort-attribute">Sort attribute</h2> -<p> -The sorting attribute in a sort expression is the name of an -attribute in the index. Attribute names will often be the same -as field names. In the schema, an attribute is -indicated by the indexing language fragment for a field having -an <a href="../schemas/schemas.html#attribute">attribute</a> -statement. -</p><p> -When sorting on attributes, it is recommended to use the built-in <em>unranked</em> rank-profile. -This allows the search kernel to execute the query significantly faster than execution the ranking framework for many hits -and then finally ignore this score and sort by the specified sorting specification. -</p> - -<h3 id="multivalue-sort-attribute">Multivalue sort attribute</h3> -<p> -When sorting on a multivalue attribute (<a href="../schemas/schemas.html#map">map</a>, - <a href="../schemas/schemas.html#array">array</a>, or <a href="../schemas/schemas.html#weightedset">weightedset</a>) one of -the values for the document is selected to be used for -sorting. Ascending sort order uses the lowest value while descending -sort order uses the highest value. See the <a href="#missing">missing policies</a> section for how a document without values in the attribute is treated. -</p> - - -<h2 id="sort-function">Sort function</h2> -<p> - Refer to <a href="yql.html#function">function</a>. -</p> - - - -<h2 id="special-sorting-attributes">Special sorting attributes</h2> -<p> -Three special attributes are available for sorting in addition to the index specific attributes: -</p> -<table class="table"> -<thead> -</thead><tbody> - <tr><th>[relevance]</th> - <td> - The document's relevance score for this query. - This is the same as the default ordering when no sort specification - is given ([rank] is a legacy alias for the same thing). - </td> - </tr><tr><th>[source]</th> - <td> - The document's source name. This is only relevant when querying multiple sources. - </td> - </tr><tr><th>[docid]</th> - <td> - The document's identification in the search backend. - This will typically give you the documents in indexing order. - <strong>Keep in mind that this id is unique only to the backend node</strong>. - The same document might have different id on a different node. - The same way a different document might have the same id on another node. - This is just intended as a cheap way of getting an almost stable sort order. - </td> - </tr> -</tbody> -</table> -<p> -These special attributes are most useful as secondary sort expressions in a multilevel sort. -This will allow you to sort groups of equal values for the primary expression -in either relevancy or indexing order. -Without this additional sort expression, the order within each equal group is not deterministic. -{% include important.html content=' - In <a href="yql.html#order-by">YQL, using order by</a>, the special sorting - attributes must be enclosed in quotes. -'%} -</p> - -<h2 id="missing">Missing policies</h2> - -<p> - A document might not have a value in the attribute. One of the following missing policies will then be applied: -</p> -<table class="table"> - <thead> - <tr> - <th>Policy</th> - <th>Example</th> - <th>Description</th> - </tr> - </thead> - <tbody> - <tr> - <td>default</td> - <td><code>+attr</code></td> - <td> - <p> - If the sort order is ascending and the attribute is single-valued then the document is sorted before any documents with values in the attribute. If the attribute is multi-valued or the sort order is descending then the document is sorted after any documents with values in the attribute. - </p> - </td> - </tr> - <tr> - <td>first</td> - <td><code>+missing(attr,first)</code></td> - <td> - <p> - The document is sorted before any documents with values in the attribute. - </p> - </td> - </tr> - <tr> - <td>last</td> - <td><code>+missing(attr,last)</code></td> - <td> - <p> - The document is sorted after any documents with values in the attribute. - </p> - </td> - </tr> - <tr> - <td>as</td> - <td><code>+missing(attr,as,42)</code></td> - <td> - <p>The document is sorted as if it had the missing value specified in the <a href="#sortspec">sorting specification</a>. If the missing value cannot be converted to the attribute data type then an error is reported (query is aborted for indexed search, parts of the sort spec is ignored for streaming search). - </p> - </td> - </tr> - </tbody> -</table> - -Note that missing policies can be combined with other functions ,e.g. -<code>+missing(lowercase(attr),as,"nothing here")</code>. - -<h2 id="limitations">Limitations</h2> - -<h3 id="attribute-only">Attribute only</h3> -<p> - It is only possible to sort on <a href="../../content/attributes.html">attributes</a>. - Trying to sort on an <a href="../../basics/schemas.html#document-fields">index or summary field</a>, - without an associated attribute, will not work. -</p> -<p><!-- ToDo: Isn't this the same as the "Optimization" section right below? --> - Also note that <a href="../schemas/schemas.html#match-phase">match-phase</a> is enabled when sorting. -</p> - - -<h3 id="optimization-causing-incorrect-total-hit-count">Optimization causing incorrect total hit count</h3> -<p> - When sorting on a single-value numeric attribute with <a href="../../content/attributes.html#fast-search">fast-search</a> - Vespa will by default activate an optimization which makes delivering sorted results much faster, - but with inaccurate total-hit count. - To disable this optimization, - set the query parameter <code>sorting.degrading</code> to false - (in the request or a <a href="../../querying/query-profiles.html">query profile</a>). - See the <a href="../api/query.html#sorting.degrading">reference</a> for details. -</p> - - - -<h2 id="examples">Examples</h2> -<p> -Sort by surname in ascending order: -</p> -<pre>+surname</pre> -<p> -Sort by surname in ascending order after lowercasing the surname: -</p> -<pre>+lowercase(surname)</pre> -<p> -Sort by surname in ascending English US locale collation order. -</p> -<pre>+uca(surname,en_US)</pre> -<p> -Sort by surname in ascending Norwegian 'Bokmål' locale collation order: -</p> -<pre>+uca(surname,nb_NO)</pre> -<p> -Sort by surname in ascending Norwegian 'Bokmål' locale collation order, -but more attributes of a character are used to distinguish. -Now it is case-sensitive and 'aa' and 'å' are different: -</p> -<pre>+uca(surname,nb_NO,TERTIARY)</pre> -<p> -Sort by surname, with the youngest ones first when the names are equal: -</p> -<pre>+surname -yearofbirth</pre> -<p> -Sort in ascending order yearofbirth groups, -and sort by relevancy within each group of equal values -with the highest relevance first: -</p> -<pre>+yearofbirth -[relevance]</pre> diff --git a/en/reference/querying/sorting-language.mdx b/en/reference/querying/sorting-language.mdx new file mode 100644 index 0000000000..138f6a1054 --- /dev/null +++ b/en/reference/querying/sorting-language.mdx @@ -0,0 +1,198 @@ +--- +title: "Sorting Language Reference" +sidebarTitle: "Sorting" +--- + +A sorting specification in a query consists of one or more sorting expressions. Each sorting expression is an optional sort order followed by an attribute name or a function over an attribute. Multiple expressions are separated by a single SPACE character. + +Using more than one sort expression will give you multilevel sorting. In this case, the most significant expression is the first, while subsequent expressions detail sorting within groups of equal values for the previous expression. + +<Card> +Sorting ::= SortExpr ( ' ' SortExpr )\* <br/> +SortExpr ::= \[SortOrder\] ( SortObject | MissingExpr ) <br/> +MissingExpr ::= MISSING '(' SortObject ',' MissingPolicy ')' <br/> +MissingPolicy ::= FIRST | <br/> + LAST | <br/> + AS ',' MissingValue <br/> +SortObject ::= SortAttribute | Function <br/> +Function ::= LOWERCASE '(' SortAttribute ')' | <br/> + RAW '(' SortAttribute ')' | <br/> + UCA '(' SortAttribute \[ ',' Locale \[ ',' Strength\] \] ')' <br/> +MISSING ::= 'missing' <br/> +FIRST ::= 'first' <br/> +LAST ::= 'last' <br/> +AS ::= 'as' <br/> +LOWERCASE ::= 'lowercase'<br/> +UCA ::= 'uca'<br/> +RAW ::= 'raw'<br/> +Locale ::= An identifier following [unicode locale identifiers,](https://www.unicode.org/reports/tr35/#Unicode_Language_and_Locale_Identifiers) fx 'en\_US'.<br/> +Strength :: 'PRIMARY' | 'SECONDARY' | 'TERTIARY' | 'QUATERNARY' | 'IDENTICAL'<br/> +SortOrder ::= '+' | '-' <br/> +SortAttribute ::= ID | ID.key | ID.value /\* A valid attribute name, with key/value appended for maps \*/ <br/> +MissingValue ::= QuotedString | Token <br/> +Token ::= TokenChar\* <br/> +TokenChar ::= any non Delimiter <br/> +QuotedString ::= '"' ( StringChar | '\\' QuotedChar )\* '"' <br/> +StringChar ::= any non QuotedChar <br/> +Delimiter ::= ' ' | ',' | '(' | ')' | QuotedChar <br/> +QuotedChar ::= '\\' | '"' <br/> +</Card> + +See [Geo search](/en/querying/geo-search) for sorting by distance. Refer to [YQL Vespa reference](/en/reference/querying/yql#order-by) for how to set sorting parameters in YQL. + +## Sort order + +`+` denotes ascending sorting order, while `-` gives descending order. Ascending order is defined as lowest values first for numerical attributes. Strings are sorted according to the sort function chosen. Descending order is the reverse of ascending order. + +<Info> +Note: `+` in query URLs must be encoded as %2B - for consistency, `-` can be encoded as %2D. +</Info> + +### Default sort order + +If `+`/`-` is omitted, the default is used, either the system-wide default of `+` or any override in [schema](/en/reference/schemas/schemas#sorting). Default sort order is `+` or ascending, except for `[rank]` or the special builtin `[relevance]`, which has `-` or descending. + +## Sort attribute + +The sorting attribute in a sort expression is the name of an attribute in the index. Attribute names will often be the same as field names. In the schema, an attribute is indicated by the indexing language fragment for a field having an [attribute](/en/reference/schemas/schemas#attribute) statement. + +When sorting on attributes, it is recommended to use the built-in *unranked* rank-profile. This allows the search kernel to execute the query significantly faster than execution the ranking framework for many hits and then finally ignore this score and sort by the specified sorting specification. + +### Multivalue sort attribute + +When sorting on a multivalue attribute ([map](/en/reference/schemas/schemas#map), [array](/en/reference/schemas/schemas#array), or [weightedset](/en/reference/schemas/schemas#weightedset)) one of the values for the document is selected to be used for sorting. Ascending sort order uses the lowest value while descending sort order uses the highest value. See the [missing policies](#missing) section for how a document without values in the attribute is treated. + +## Sort function + +Refer to [function](/en/reference/querying/yql#function). + +## Special sorting attributes + +Three special attributes are available for sorting in addition to the index specific attributes: + +<table className="vespa-table"> + <thead> + <tr> + <th>Attribute</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>---</td> + <td>---</td> + </tr> + <tr> + <td>**[relevance]**</td> + <td>The document's relevance score for this query. This is the same as the default ordering when no sort specification is given ([rank] is a legacy alias for the same thing).</td> + </tr> + <tr> + <td>**[source]**</td> + <td>The document's source name. This is only relevant when querying multiple sources.</td> + </tr> + <tr> + <td>**[docid]**</td> + <td>The document's identification in the search backend. This will typically give you the documents in indexing order. **Keep in mind that this id is unique only to the backend node**. The same document might have different id on a different node. The same way a different document might have the same id on another node. This is just intended as a cheap way of getting an almost stable sort order.</td> + </tr> + </tbody> +</table> + +These special attributes are most useful as secondary sort expressions in a multilevel sort. This will allow you to sort groups of equal values for the primary expression in either relevancy or indexing order. Without this additional sort expression, the order within each equal group is not deterministic. + +<Danger> +**Important:** In [YQL, using order by](/en/reference/querying/yql#order-by), the special sorting attributes must be enclosed in quotes. +</Danger> + +## Missing policies + +A document might not have a value in the attribute. One of the following missing policies will then be applied: + +<table className="vespa-table"> + <thead> + <tr> + <th>Policy</th> + <th>Example</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>default</td> + <td><code>{`+attr`}</code></td> + <td>If the sort order is ascending and the attribute is single-valued then the document is sorted before any documents with values in the attribute. If the attribute is multi-valued or the sort order is descending then the document is sorted after any documents with values in the attribute.</td> + </tr> + <tr> + <td>first</td> + <td><code>{`+missing(attr,first)`}</code></td> + <td>The document is sorted before any documents with values in the attribute.</td> + </tr> + <tr> + <td>last</td> + <td><code>{`+missing(attr,last)`}</code></td> + <td>The document is sorted after any documents with values in the attribute.</td> + </tr> + <tr> + <td>as</td> + <td><code>{`+missing(attr,as,42)`}</code></td> + <td>The document is sorted as if it had the missing value specified in the <a href="#sortspec" aria-label="sorting specification">sorting specification</a>. If the missing value cannot be converted to the attribute data type then an error is reported (query is aborted for indexed search, parts of the sort spec is ignored for streaming search).</td> + </tr> + </tbody> +</table> + +Note that missing policies can be combined with other functions ,e.g. `+missing(lowercase(attr),as,"nothing here")`. + +## Limitations + +### Attribute only + +It is only possible to sort on [attributes](/en/content/attributes). Trying to sort on an [index or summary field](/en/basics/schemas#document-fields), without an associated attribute, will not work. + +Also note that [match-phase](/en/reference/schemas/schemas#match-phase) is enabled when sorting. + +### Optimization causing incorrect total hit count + +When sorting on a single-value numeric attribute with [fast-search](/en/content/attributes#fast-search) Vespa will by default activate an optimization which makes delivering sorted results much faster, but with inaccurate total-hit count. To disable this optimization, set the query parameter `sorting.degrading` to false (in the request or a [query profile](/en/querying/query-profiles)). See the [reference](/en/reference/api/query#sorting.degrading) for details. + +## Examples + +Sort by surname in ascending order: + +```bash ++surname +``` + +Sort by surname in ascending order after lowercasing the surname: + +```bash ++lowercase(surname) +``` + +Sort by surname in ascending English US locale collation order. + +```bash ++uca(surname,en_US) +``` + +Sort by surname in ascending Norwegian 'Bokmål' locale collation order: + +```bash ++uca(surname,nb_NO) +``` + +Sort by surname in ascending Norwegian 'Bokmål' locale collation order, but more attributes of a character are used to distinguish. Now it is case-sensitive and 'aa' and 'å' are different: + +```bash ++uca(surname,nb_NO,TERTIARY) +``` + +Sort by surname, with the youngest ones first when the names are equal: + +```bash ++surname -yearofbirth +``` + +Sort in ascending order yearofbirth groups, and sort by relevancy within each group of equal values with the highest relevance first: + +```bash ++yearofbirth -[relevance] +``` \ No newline at end of file diff --git a/en/reference/querying/yql.html b/en/reference/querying/yql.html deleted file mode 100644 index 2c1b74dc58..0000000000 --- a/en/reference/querying/yql.html +++ /dev/null @@ -1,2410 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "YQL Query Language Reference" -redirect_from: -- /en/reference/query-language-reference.html ---- - -<p> - Vespa accepts unstructured human input and structured queries for application logic separately, - then combines them into a single data structure for executing. - Human input is parsed heuristically, while application queries are formulated in YQL. -</p> -{% include note.html content='See the <a href="../../querying/query-language.html">Query Language Guide</a> -for query examples'%} -<p>A query URL looks like:</p> -<pre> -http://myhost.mydomain.com:8080/search/?yql=select%20%2A%20from%20sources%20%2A%20where%20text%20contains%20%22blues%22 -</pre> -<p>In other words, <code>yql</code> contains:</p> -<pre> -select * from sources * where text contains "blues" -</pre> -<p> - This <a href="../schemas/schemas.html#match">matches</a> all documents - where the field named <em>text</em> contains the word <em>blues</em>. -</p> -<p> - Quote <code>"</code> and backslash <code>\</code> characters in text values must be escaped by a backslash, - also see <a href="../../learn/faq#how-does-backslash-escapes-work">how does backslash escapes work</a>. -</p> -{% include important.html content='There is no way to query for a field that is not set -/ equals <code>null</code> or <code>NaN</code>. -Work around using a "magic" value (like MAXINT) that is not normally used in the documents.'%} - - -<h2 id="select">select</h2> -<p> -<em>select</em> is the list of <a href="/en/querying/document-summaries.html#selecting-summary-fields-in-yql">summary fields</a> requested -(a field with the <code>summary</code> index attribute). -Vespa will hide other fields in the matching documents. -</p> -<pre> -select price,isbn from sources * where title contains "madonna" -</pre> -<p> - The above explicitly requests the fields "price" and "isbn" (from all sources). - To request all fields, use an asterisk as field selection: -</p> -<pre> -select * from sources * where title contains "madonna" -</pre> - - - -<h2 id="from-sources">from sources</h2> -<!-- ToDo: describe how this is equal to model.sources --> -<p> - <em>from sources</em> specifies which content - <a href="../api/query.html#model.sources">sources</a> to query. Example: -</p> -<pre> -select * from music where title contains "madonna" -</pre> -<p> - queries all document types in the <em>music</em> content cluster or federation source. Query in: -</p> -<table class="table"> -<thead></thead><tbody> - <tr><td>all sources</td> - <td><code>select … from <strong>sources *</strong> where …</code></td></tr> - <tr><td>a set of sources</td> - <td><code>select … from <strong>sources source1, source2</strong> where …</code></td></tr> - <tr><td>a single source</td> - <td><code>select … from <strong>source1</strong> where …</code></td></tr> -</tbody> -</table> -<p> - In other words, <em>sources</em> is used for querying some/all sources. - If only a single source is queried, the <em>sources</em> keyword is dropped. - To restrict the query to only one schema (aka document type) use the - <a href="../api/query.html#model.restrict">model.restrict</a> URL parameter. - Also see <a href="../../querying/federation.html">federation</a>. -</p> - - - -<h2 id="where">where</h2> -<p>The <code>where</code> clause is a tree of operators:</p> -<table class="table"> - <thead></thead> - <tbody> - - <tr><th>numeric</th><td> - <p id="numeric"> - The following numeric operators are available: - <code>= < > <= >= range(field, lower bound, upper bound)</code>. - </p> -<pre> -where 500 >= price -</pre> -<pre> -where range(fieldname, 0, 5000000000L) -</pre> - <p> - Numbers must be in the signed 32-bit range. - Input 64-bit signed numbers using <code>L</code> as suffix. - </p> - <p> - For the <code>range</code> operator, one can also use - the strings <code>Infinity</code> or <code>-Infinity</code>: - </p> -<pre> -where (range(year, 2000, Infinity)) -</pre> - <table class="table"> - <thead> - <tr> - <th>Annotation</th> - <th>Effect</th> - </tr> - </thead> - <tbody> - <tr> - <td><a href="#bounds">bounds</a></td> - <td>Range: open or closed interval.</td> - </tr> - <tr> - <td><a href="#hitlimit">hitLimit</a></td> - <td> - Used for <em>capped range search</em>. - The <code>range()</code> query operator with <code>hitLimit</code> can be used to efficiently implement - top-k selection for ranking a subset of the documents in the index. - See <a href="../../performance/practical-search-performance-guide.html#advanced-range-search-with-hitlimit"> - example and use cases</a>. - </td> - </tr> - </tbody> - </table> - <p> - The <a href="../schemas/schemas.html#weightedset">weightedset</a> field does not support filtering on weight. - Solve this using the <a href="../schemas/schemas.html#map">map</a> type and - <a href="#sameelement">sameElement</a> query operator - - see <a href="../../querying/query-language.html#map">example</a>. - </p> - </td></tr> - - <tr><th>boolean</th><td> - <p id="boolean">The boolean operator is: <code>=</code></p> -<pre> -where alive = true -</pre> - </td></tr> - - <tr><th>contains</th><td> - <p id="contains"> - The right-hand side argument of the contains operator is either a string literal, - or a function, like <code>phrase</code>. - </p> - <p> - <code>contains</code> is the basic building block for text matching. - The kind of <a href="../schemas/schemas.html#match">matching</a> - to be done depends on the field settings in the schema. - </p> -<pre> -where title contains "madonna" -</pre> - <table class="table"> - <thead> - <tr> - <th>Annotation</th> - <th>Effect</th> - </tr> - </thead> - <tbody> - <tr> - <td><a href="#stem">stem</a></td> - <td> - By default, the string literal is <a href="../../linguistics/linguistics-opennlp.html#tokenization">tokenized</a> - to match the field(s) searched. - Explicitly control tokenization by using <a href="#stem">stem</a>: -<pre> -where title contains ({stem: false}"madonna") -</pre> - </td> - </tr> - </tbody> - </table> - <p> - The matched field must be an - <a href="../../basics/schemas.html#document-fields">indexed field or attribute</a>. - </p><p> - Fields inside structs are referenced using dot notation - - e.g <code>mystruct.mystructfield</code>. - </p> - </td> - </tr> - - <tr> - <th>and</th> - <td> - <p id="and"> - <code>and</code> accepts other <code>and</code> statements, <code>or</code> statements, - <a href="#userquery">userQuery</a>, logically inverted statements - - and contains statements as arguments: - </p> -<pre> -where title contains "madonna" and title contains "saint" -</pre> - </td> - </tr> - - <tr> - <th>or</th> - <td> - <p id="or"> - <code>or</code> accepts other <code>or</code> statements, <code>and</code> statements, - <a href="#userquery">userQuery</a> - and contains statements as arguments: - </p> -<pre> -where title contains "madonna" or title contains "saint" -</pre> - </td> - </tr> - - <tr> - <th>not</th> - <td> - <p id="not"> - Use the <code>!</code> operator to match document that does <i>not</i> satisfy some condition: - </p> -<pre> -where title contains "madonna" and !(title contains "saint") -</pre> - </td> - </tr> - - <tr> - <th>phrase</th> - <td> - <p id="phrase"> - Phrases are expressed as a function: - </p> -<pre> -where text contains phrase("st", "louis", "blues") -</pre> - </td> - </tr> - - <tr> - <th>near</th> - <td> - <p id="near"> - <code>near()</code> matches if all argument terms occur within the specified distance, - in any order. Negative terms (prefixed with <code>!</code>) exclude matches where those terms - appear within the exclusion distance. - </p> -<pre> -where field contains near("a", "b", "c") -where field contains ({distance: 5}near("web", "search")) -where field contains near("sql", "database", !"nosql") -</pre> - <table class="table"> - <thead> - <tr> - <th>Annotation</th> - <th>Default</th> - <th>Description</th> - </tr> - </thead> - <tbody> - <tr> - <td><a href="#distance">distance</a></td> - <td>2</td> - <td>Maximum position difference for terms to match.</td> - </tr> - <tr> - <td>exclusionDistance</td> - <td>(distance+1)/2</td> - <td>Exclusion zone size around negative terms.</td> - </tr> - </tbody> - </table> - <p> - Negative terms must come after all positive terms. - For multi-value fields, setting <a href="../schemas/schemas.html#rank-element-gap">element-gap</a> for the field in the rank profile enables distance calculation between adjacent elements. - </p> - <p> - Features below <code>near()</code> and <code>onear()</code> are filtered based on the spans for the operator match before they are exposed to the ranking features. Given the query - <pre>text contains ({distance:1}near("a","b"))</pre> - and two documents. - </p> - <p> - The text field in the first document is <code>"a a a a a a b b b b b b"</code>. The spans for the near match are <code>[[5,6]]</code>. Only the last occurrence of <code>"a"</code> and the first occurrence of <code>"b"</code> are kept. - </p> - <p> - The text field in the second document is <code>"a b c a b c a b c a b c"</code>. The spans for the near match are <code>[[0,1],[3,4],[6,7],[9,10]]</code>. All occurrences of <code>"a"</code> and <code>"b"</code> are kept. - </p> - </td> - </tr> - - <tr> - <th>onear</th> - <td> - <p id="onear"> - <code>onear()</code> (ordered near) is like <code>near()</code>, - but requires terms to appear in the same order as specified in the query. - With distance set to (number of terms - 1), <code>onear()</code> is equivalent to <code>phrase()</code>. - </p> -<pre> -where field contains onear("web", "search", "engine") -where field contains ({distance: 5}onear("neural", "network")) -where field contains onear("java", "tutorial", !"script") -</pre> - <table class="table"> - <thead> - <tr> - <th>Annotation</th> - <th>Default</th> - <th>Description</th> - </tr> - </thead> - <tbody> - <tr> - <td><a href="#distance">distance</a></td> - <td>2</td> - <td>Maximum position difference for terms to match.</td> - </tr> - <tr> - <td>exclusionDistance</td> - <td>(distance+1)/2</td> - <td>Exclusion zone size around negative terms.</td> - </tr> - </tbody> - </table> - <p> - Negative terms must come after all positive terms. - For multi-value fields, setting <a href="../schemas/schemas.html#rank-element-gap">element-gap</a> for the field in the rank profile enables distance calculation between adjacent elements. - </p> - </td> - </tr> - - <tr> - <th>sameElement</th> - <td> - <p id="sameelement">The <code>sameElement()</code> operator lets you denote conditions that must match within the - <i>same</i> element in multivalue fields containing structs or strings.</p> - - <p>By default, sameElement uses <code>AND</code> to combine the conditions: <i>All</i> the conditions - must match in the same element to produce a match.</p> - - <p>For example, given this <b>struct</b>:</p> -<pre> -struct person { - field first_name type string {} - field last_name type string {} - field year_of_birth type int {} -} - -field persons type array<person> { - indexing: summary - struct-field first_name { indexing: attribute } - struct-field last_name { indexing: attribute } - struct-field year_of_birth { indexing: attribute } -} -</pre> - <p>We can use this query:</p> -<pre> -where persons contains sameElement(first_name contains 'Joe', last_name contains 'Smith', year_of_birth < 1940) -</pre> - <p>to return all documents containing a Joe Smith born before 1940 in the <code>persons</code> array.</p> - - <p>Searching a <b>map</b> is done by treating it as an array of a struct with the field members <code>key</code> and <code>value</code>. - For example, given this map: - </p> -<pre> -field identities type map<string, person> { - indexing: summary - struct-field key { indexing: attribute } - struct-field value.first_name { indexing: attribute } - struct-field value.last_name { indexing: attribute } - struct-field value.year_of_birth { indexing: attribute } -} -</pre> - <p>We can use this query:</p> - <pre> -where identities contains sameElement(key contains 'father', value.first_name contains 'Joe', value.last_name contains 'Smith', value.year_of_birth < 1940) -</pre> - <p>to return all documents that have a Joe Smith born before 1940 keyed as a 'father'.</p> - - <p><code>sameElement()</code> may also be used to search <b>array of string</b> fields. Supported query operators - inside sameElement() are <code>and</code>, <code>equiv</code>, - <code>near</code>, <code>onear</code>, <code>or</code>, <code>rank</code> and <code>phrase</code>. - <code>and</code> can be used with <code>!</code>.</p> - <p>For example given this field: - <pre> -field chunks type array<string> { - indexing: index | summary -} -</pre> - - <p>We can use these queries:</p> - -<pre> -where chunks contains sameElement("one" and "two") -where chunks contains sameElement("one" and equiv("two","three")) -where chunks contains sameElement("one" and ({distance: 5}near("two","three",!"four"))) -where chunks contains sameElement("one" and phrase("two","three")) -where chunks contains sameElement("one" and !"two") -where chunks contains sameElement("one" or "two") -where chunks contains sameElement(rank("one" and "two", "three")) -</pre> - <p> - Features inside sameElement() for indexed fields are filtered based on the matching elements, e.g. - <a href="../ranking/rank-features.html#elementwise-bm25">elementwise(bm25(descriptions),x,double)</a> will only - contain tensor cells based on the matching elements. - </p> - - <p>Use the <a href="#elementfilter"><code>elementFilter</code></a> annotation - to restrict matching to specific element indices. - For example, given a field <code>my_numbers type array<int></code>:</p> -<pre> -where my_numbers contains ({elementFilter:[2]}sameElement("42")) -</pre> - <p>This only matches the element at index 2 in the array field. - Multiple indices can be given: <code>{elementFilter:[0, 2, 5]}</code>.</p> - - <p>A shorthand form is also available:</p> -<pre> -where my_numbers[2] = 42 -</pre> - <p>The shorthand form only supports a single index. - Use the <a href="#elementfilter"><code>elementFilter</code></a> annotation to match multiple indices.</p> - </td> - </tr> - - <tr><th>equiv</th><td> - <p id="equiv"> - For cases where two terms in the same field should produce exactly the same behavior when matched, - the <code>equiv()</code> operator can be used. This behaves like a special case of <code>or</code>. - </p> -<pre> -where fieldName contains equiv("A","B") -</pre> - <p> - The matching logic of equiv is the same as OR, - and an OR does not have the limitations that EQUIV does (below). - The difference is in how matches are visible to ranking functions. - All words that are children of an OR count for ranking, while with - EQUIV, they look like a single word to ranking: - </p> - <ul> - <li>Counts as only +1 for queryTermCount</li> - <li>Counts as 1 word for completeness measures</li> - <li>Proximity will not discriminate different words inside the EQUIV</li> - <li>Connectivity can be set between the entire EQUIV and the word before and after</li> - <li>Items inside the EQUIV are not directly visible to ranking features, - so weight and connectivity on those will have no effect</li> - </ul> - <p>Limitations on how <code>equiv</code> can be used in a query:</p> - <ul> - <li><code>equiv</code> may not appear inside a phrase</li> - <li>It may only contain <code>TermItem</code> and <code>PhraseItem</code> instances. - Operators like <code>and</code> cannot be placed inside <code>equiv</code></li> - <li><code>PhraseItems</code> inside <code>equiv</code> will rank like as if they have size 1</li> - </ul> - <p>Learn how to use <a href="../../linguistics/query-rewriting.html#equiv">equiv</a>.</p> - </td> - </tr> - - <tr> - <th>uri</th> - <td> - <p id="uri"> - {% include deprecated.html content="the uri type is deprecated, use string instead."%} - Used to search for urls indexed using the - <a href="../schemas/schemas.html#uri">uri field type</a>.</p> -<pre> -where myUrlField contains uri("vespa.ai/foo") -</pre> - <p>Various subfields are supported to search components of the URL, see the field type definition.</p> - <table class="table"> - <thead> - <tr> - <th>Annotation</th> - <th>Effect</th> - </tr> - </thead> - <tbody> - <tr> - <td><a href="#startanchor">startAnchor</a></td> - <td>Anchor uri.hostname at start.</td> - </tr> - <tr> - <td><a href="#endanchor">endAnchor</a></td> - <td>Anchor uri.hostname at end.</td> - </tr> - </tbody> - </table> - </td> - </tr> - - <tr> - <th>fuzzy</th> - <td> - <p id="fuzzy"> - <a href="https://en.wikipedia.org/wiki/Levenshtein_distance">Levenshtein</a> - edit distance search within a string or array<string> <a href='../schemas/schemas.html#attribute'>attribute</a>. - </p> -<pre> -where myStringAttribute contains ({prefixLength:1, maxEditDistance:2}fuzzy("parantesis")) -</pre> - <p>Annotations below are configuring <code>fuzzy</code>:</p> - <table class="table"> - <thead> - <tr> - <th>Annotation</th> - <th>Effect</th> - </tr> - </thead> - <tbody> - <tr> - <td><a href="#maxeditdistance">maxEditDistance</a></td> - <td>An inclusive upper bound of edit distance between query and string attribute (default is 2).</td> - </tr> - <tr> - <td><a href="#prefixlength">prefixLength</a></td> - <td>Number of characters that are considered frozen, - so the fuzzy match will be performed only with the suffix left. - Default is 0 (i.e. <code>fuzzy</code> will match across whole query)</td> - </tr> - <tr> - <td><a href="#prefix">prefix</a></td> - <td>If <code>true</code>, a string is considered a match when it's possible to transform a - <em>prefix</em> of the candidate string to the query string using at most <code>maxEditDistance</code> - edits. See <a href="../../querying/text-matching.html#fuzzy-prefix-match">fuzzy prefix match</a>. - Default is <code>false</code>, which means that the entire string is considered.</td> - </tr> - </tbody> - </table> - <p>Find an example in <a href="../../querying/text-matching.html#fuzzy-match">text matching</a>.</p> - {% include important.html content="Only string <a href='../schemas/schemas.html#attribute'>attribute</a> - fields in <a href='../applications/services/content.html#document'>documents</a> are supported (single, array or weightedset). - Matching is optimized internally when <code>maxEditDistance</code> is 1 or 2. - Setting <a href='#prefixlength'>prefixLength</a> greater than 0 - narrows the match for the <a href='../schemas/schemas.html#attribute'>fast-search</a>, - greatly reducing the number of terms that must be considered."%} - </td> - </tr> - <tr> - <th>matches</th> - <td> - <p id="matches"> - Regular expression match is supported using - <a href="https://en.wikibooks.org/wiki/Regular_Expressions/POSIX_Extended_Regular_Expressions">posix extended syntax</a>, - with the limitation that it is <strong>case-insensitive</strong>. - </p> - <p>Example matching both <code>madonna</code>, <code>madona</code> and with any number of <code>n</code>s:</p> -<pre> -where attribute_field matches "mado[n]+a" -</pre> - <p>Find more examples in the <a href="../../querying/text-matching.html#regular-expression-match">text matching</a> guide.</p> - {% include important.html content="Only <a href='../schemas/schemas.html#attribute'>attribute</a> - fields in <a href='../applications/services/content.html#document'>documents</a> is supported. - It is not optimized for performance. - Having a prefix using the <code>^</code> will be faster than not having one. - Additionally, fields that serve as both attributes and indexes are not compatible. - "%} - </td> - </tr> - - <tr> - <th>text</th> - <td> - <p id="text"> - <em>text()</em> accepts any text and tokenizes it into a set of tokens to be searched - in a given field or fieldSet. - By default, the tokens are searched with a <a href="#weakand">weakAnd</a> operator. - You can override the default behavior via annotations: <code>text()</code> supports the same - annotations as <a href="#userinput">userInput()</a>. - </p> - <p>Example:</p> - <pre> - where text_field contains text("some text") - </pre> - <p>With annotations:</p> - <pre> - where text_field contains ({language:'en'}text("some text")) - </pre> - - <p>The text argument can be given as a reference using "@parameterName":</p> - <pre> - yql=select * from sources * where text_field contains text(@text)&text=some text - </pre> - </tr> - - <tr> - <th>userInput</th> - <td> - <p id="userinput"> - <em>userInput()</em> parses text from end users or models that may contain query syntax - for choosing the fields to search, specifying phrases and negative terms etc. - Since the query in userInput can specify fields, there is no "contains field" - prefix before the userInput operator. - </p> -<pre> -yql=select * from sources * where userInput('some text') -</pre> - -<p>The argument can be given as a reference using "@parameterName":</p> -<pre> -yql=select * from sources * where userInput(@text)&text=some text -</pre> - -<p>Both of these will result in the query</p> - -<pre> -select * from sources * where weakAnd(default contains "some", default contains "text") -</pre> - -<p>The default behavior may be overridden by annotations:</p> -<pre> -yql=select * from sources * where ({grammar.syntax:'none',grammar.tokenization:'linguistics',grammar.composite:'near',distance:3}userInput('some text')) -</pre> - - <table class="table"> - <thead> - <tr> - <th>Annotation</th> - <th>Effect</th> - </tr> - </thead> - <tbody> - <tr> - <td><a href="#grammar">grammar</a></td> - <td> - <p>Sets the query parse type to apply when interpreting the user input text.</p> - <p>For any value of <code>grammar</code> other than <code>raw</code> or <code>segment</code>, - only the following annotations are applied:</p> - <ul> - <li><a href="#defaultindex">defaultIndex</a></li> - <li><a href="#totaltargethits">totalTargetHits</a></li> (for weakAnd) - <li><a href="#targethits">targetHits</a></li> (for weakAnd) - <li><a href="#distance">distance</a></li> (for near/oNear) - <li><a href="#ranked">ranked</a></li> - <li><a href="#filter">filter</a></li> - <li><a href="#stem">stem</a></li> - <li><a href="#normalizecase">normalizeCase</a></li> - <li><a href="#accentdrop">accentDrop</a></li> - <li><a href="#usepositiondata">usePositionData</a></li> - </ul> - <p>E.g. if annotating <code>userInput</code> with <code>phrase</code>, - a <code>filter</code> annotation will have effect, - but not <code>language</code>.</p> - <p>See <a href="../api/query.html#model.type.isYqlDefault">isYqlDefault</a> on setting a default grammar - in a request/query profile.</p> - </td> - </tr> - <tr> - <td><a href="#defaultindex">defaultIndex</a></td> - <td>Same as <a href="../api/query.html#model.defaultindex">model.defaultIndex</a> - in the query API.</td> - </tr> - <tr> - <td><a href="#language">language</a></td> - <td>Language setting for the linguistics treatment of this userInput() call.</td> - </tr> - <tr> - <td><a href="#allowempty">allowEmpty</a></td> - <td>Whether to allow empty input for query parsing and search terms.</td> - </tr> - </tbody> - </table> - <p> - In addition, other annotations, like <a href="#stem">stem</a> or <a href="#ranked">ranked</a>, - will take effect as normal. - </p> - <p>More examples can be found in the <a href="../../querying/query-api.html#input-examples">query API</a> guide.</p> - </td> - </tr> - - <tr> - <th>userQuery</th> - <td> - <p id="userquery"> - <em>userQuery()</em> reads from <a href="../api/query.html#model.querystring">model.queryString</a> - and parses the query using <a href="simple-query-language.html">simple query language</a>. - If set, <a href="../api/query.html#model.filter">model.filter</a> is combined with - <em>model.queryString</em> before the parsing. - </p> - <p> - The user query is first parsed, - then the resulting tree is inserted into the corresponding place in the YQL query tree. Example: - </p> -<pre> -$ vespa query 'select * from sources * where vendor contains "brick and mortar" AND price < 50 AND userQuery()' \ - query="abc def -ghi" \ - type=all -</pre> - <p>This evaluates to a query where:</p> - <ul> - <li>the numeric field <em>price</em> must be less than 50</li> - <li><em>vendor</em> must match <em>brick and mortar</em></li> - <li>the default index must contain the two terms <em>abc</em> and <em>def</em>, - <em>and not</em> contain <em>ghi</em>.</li> - </ul> - <p> - Use <a href="../api/query.html#model.defaultindex">model.defaultIndex</a> - to specify a field or fieldset if not using <em>default</em> - - see <a href="/en/querying/query-api.html#fieldset">example</a>. - </p> - </td> - </tr> - - <tr> - <th>rank</th> - <td> - <p id="rank"> - The first, and only the first, argument of the <em>rank()</em> function - determines whether a document is a match, - but all arguments are used for calculating rank features. The <code>rank</code> operator - is useful for boosting documents based on the presence of certain terms without impacting matching or retrieval logic. - </p> -<pre> -where rank(a contains "A", b contains "B", c contains "C") -</pre> - <p> - It's also useful in hybrid search use cases. See <a href="https://blog.vespa.ai/redefining-hybrid-search-possibilities-with-vespa/">blog post</a> - for usage examples. For example, retrieve using the <a href="#nearestneighbor">nearestNeighbor</a> query operator - as the first argument and have matching features calculated for the other arguments. - </p> -<pre> -where rank(nearestNeighbor(field, queryVector), a contains "A", b contains "B", c contains "C") -</pre> - </td> - </tr> - - <tr> - <th>in</th> - <td> - <p id="in"> - The <em>in</em> operator is used to match a set of values in an integer or string field. - A document is considered a match when at least one of the values matches the content of the field. - This is an optimized shorthand for multiple OR conditions, and is similar to the IN operator in SQL. - Available since {% include version.html version="8.293.15" %}. Example: - </p> -<pre> -where integer_field in (10, 20, 30) -where string_field in ('germany', 'france', 'norway') -</pre> -Where <code>string_field</code> is a field with <code>match:word</code>. -There is no <a href="/en/linguistics/linguistics.html">linguistic</a> processing like tokenization or stemming -of the string values used in the <em>in</em> operator except lowercasing. See string <a href="../schemas/schemas.html#match">match</a>. -<pre> -field string_field type string { - indexing: summary | index # or attribute - match: word - rank:filter - attribute: fast-search # if attribute - } -</pre> -<p>Using the <em>in</em> operator against string fields with <code>match:text</code> will cause recall issues because - the field contents will be tokenized during indexing while the <em>in</em> operator does not tokenize the values.</p> - <p> - The argument before <em>in</em> is the name of the field or <a href="../schemas/schemas.html#fieldset">fieldset</a> to search. - The argument after <em>in</em> is a comma-separated list of values, enclosed in parentheses. - String values must be single or double-quoted if passed inline in YQL - </p> - <p> - For multi-value fields (like arrays), the <em>in</em> operator works by checking if any element in the array matches any of the values in the set. - This is similar to SQL's IN operator but more streamlined for array comparisons. Example: - </p> -<pre> -where integer_array_field in (10, 20, 30) -</pre> - <p> - If integer_array_field = [5, 10, 15], it will match because 10 is in the array. - Similarly, if integer_array_field = [20, 25, 30], it will match because both 20 and 30 are in the array. - </p> - <p> - For faster query parsing use <a href="#parameter-substitution">parameter substitution</a> - to submit the values as an additional request parameter. - Quoting of string values are optional. Example: - </p> -<pre> -where integer_field in (@integer_values)&integer_values=10,20,30 -where string_field in (@string_values)&string_values=germany,france,norway -</pre> - <p> - The <em>in</em> operator acts as a single term in the query tree, - and does not provide any match information for text ranking features. - </p> - <p>For a discussion of usage and examples refer to:</p> - <ul> - <li><a href="../../ranking/multivalue-query-operators.html#in-example">multivalue query operators</a></li> - <li><a href="../../performance/feature-tuning.html#multi-lookup-set-filtering">multi-lookup set filtering</a></li> - <li><a href="https://github.com/vespa-engine/system-test/tree/master/tests/search/in_operator">in operator system test</a></li> - </ul> - - <table class="table"> - <thead></thead><tbody> - <tr> - <th>Field type</th> - <td>Singlevalue or <a href="../../querying/searching-multivalue-fields">multivalue</a> - <a href="/en/basics/schemas.html#document-fields">attribute or index field</a> - with basic type <a href="../schemas/schemas.html#byte">byte</a>, <a href="../schemas/schemas.html#int">int</a>, - <a href="../schemas/schemas.html#long">long</a> or <a href="../schemas/schemas.html#string">string</a>. String fields must have <code>match:word</code> or <code>match:exact</code>. - </td> - </tr><tr> - <th>Query model</th> - <td>A set of values/tokens.</td> - </tr><tr> - <th>Matching</th> - <td>Documents where the field contains at least one of the values in the query.</td> - </tr><tr> - <th>Ranking</th> - <td>None.</td> - </tr><tr> - <th style="white-space:nowrap;">Java Query Item</th> - <td><a href="https://javadoc.io/doc/com.yahoo.vespa/container-search/latest/com/yahoo/prelude/query/NumericInItem.html">NumericInItem</a> and - <a href="https://javadoc.io/doc/com.yahoo.vespa/container-search/latest/com/yahoo/prelude/query/StringInItem.html">StringInItem</a>.</td> - </tr> - </tbody> - </table> - <p> - {% include important.html content="When using the <em>in</em> operator with an attribute field, - set <a href='../../content/attributes.html#fast-search'>fast-search</a> and <a href='../schemas/schemas.html#filter'>rank: filter</a> - for best possible performance. Always use <code>match:word</code> for string fields." %} - </p> - </td> - </tr> - - <tr> - <th>dotProduct</th> - <td> - <p id="dotproduct"> - <em>dotProduct</em> calculates the dot product between the weighted set - in the query and a weighted set field in the document as its rank score contribution: - </p> -<pre> -where dotProduct(description, {"a":1, "b":2}) -</pre> - <p> - The result is stored as a <a href="../../ranking/multivalue-query-operators.html#raw-scores-and-query-item-labeling">raw score</a>. - </p> - <p> - A normal use case is a collection of weighted tokens produced by an algorithm, - to match against a corpus containing weighted tokens - produced by another algorithm in order to implement personalized content exploration. - See example usage of <em>dotProduct</em> - in <a href="../../performance/practical-search-performance-guide#multi-valued-query-operators"> - practical performance guide - </a>. - </p> - <p> - Refer to <a href="../../ranking/multivalue-query-operators.html">multivalue query operators</a> - for a discussion of usage and examples. - </p> - <p> - Keys must be single or double-quoted if passed inline in YQL - - alternatively, use <a href="#parameter-substitution">parameter substitution</a> - to submit the weighted set with a simple format for faster query parsing - - example: <code>where dotProduct(description, @myterms)</code>. - </p> - <table class="table"> - <thead></thead><tbody> - <tr> - <th>Field type</th> - <td>Weighted set attribute with fast-search. Note: Also supported for regular attribute or - index fields, but then with much weaker performance).</td> - </tr><tr> - <th>Query model</th> - <td>Weighted set with {token, weight} pairs</td> - </tr><tr> - <th>Matching</th> - <td>Documents where the weighted set field contains at least one of the tokens in the query.</td> - </tr><tr> - <th>Ranking</th> - <td>Dot product score between the weights of the matched query tokens and field tokens. - This score is available using - <a href="../ranking/rank-features.html#rawScore(field)">rawScore</a> or - <a href="../ranking/rank-features.html#itemRawScore(label)">itemRawScore</a> - rank features.</td> - </tr><tr> - <th style="white-space:nowrap;">Java Query Item</th> - <td><a href="https://javadoc.io/doc/com.yahoo.vespa/container-search/latest/com/yahoo/prelude/query/DotProductItem.html">DotProductItem</a></td> - </tr> - </tbody> - </table> - </td> - </tr> - - <tr> - <th>weightedSet</th> - <td> - <p id="weightedset"> - When using <em>weightedSet</em> to search a field, - all tokens present in the searched field will be matched against the weighted set in the query. - This means that using a weighted set to search a single-value attribute field - will have similar semantics to using a normal term to search a weighted set field. - The low-level matching information resulting from matching a document with a weighted set in the query - will contain the weights of all the matched tokens in descending order. - Each matched weight will be represented as a standard occurrence on position 0 in element 0. - </p> -<pre> -where weightedSet(description, {"a":1, "b":2}) -</pre> - <p> - <em>weightedSet</em> has similar semantics to <a href="#equiv">equiv</a>, - as it acts as a single term in the query. - However, the restriction dictating that it contains a collection of weighted tokens directly - enables specific back-end optimizations that improves performance - for large sets of tokens compared to using the generic <a href="#equiv">equiv</a> or <a href="#or">or</a> operators. - </p> - <p> - Keys must be single or double-quoted if passed inline in YQL - - alternatively, use <a href="#parameter-substitution">parameter substitution</a> - to submit the weighted set with a simple format for faster query parsing - - example: <code>where weightedSet(description, @myterms)</code>. - </p> - <table class="table"> - <thead></thead><tbody> - <tr> - <th>Field type</th> - <td>Singlevalue or <a href="../../querying/searching-multivalue-fields">multivalue</a> attribute or index field. - (Note: Most use cases operates on a single value field).</td> - </tr><tr> - <th>Query model</th> - <td>Weighted set with {token, weight} pairs.</td> - </tr><tr> - <th>Matching</th> - <td>Documents where the field contains at least one of the tokens in the query. - For filtering use cases we recommend using the <a href="#in">in operator</a> instead, - as it is simpler to use and has slightly better performance.</td> - </tr><tr> - <th>Ranking</th> - <td>The operator will act as a single term in the back-end. - The query term weight is the weight assigned to the operator itself - and the match weight is the largest weight among matching tokens from the weighted set. - This operator does not produce a raw score. - Due to better ranking and performance we recommend using <a href="#dotproduct">dotProduct</a> instead.</td> - </tr><tr> - <th style="white-space:nowrap;">Java Query Item</th> - <td><a href="https://javadoc.io/doc/com.yahoo.vespa/container-search/latest/com/yahoo/prelude/query/WeightedSetItem.html">WeightedSetItem</a></td> - </tr> - </tbody> - </table> - </td> - </tr> - - <tr><th>wand</th><td> - <p id="wand"> - <code>wand</code> can be used to search for documents - where weighted tokens in a field matches a subset of weighted tokens in the query. - At the same time, it internally calculates the dot product between token weights in the query and the field. - <code>wand</code> is guaranteed to return the top-k hits according to its internal dot product rank score. - It is an operator that scales adaptively from <a href="#or">or</a> to <a href="#and">and</a>. - </p> - <p>Note that total hit count becomes inaccurate when using wand.</p> - <p> - <code>wand</code> optimizes the performance of using multiple threads per search in the backend, - and is also called <em>Parallel Wand</em>. - </p> - <p> - <code>wand</code> also allows numeric arguments, then the search argument is an array of arrays of length two. - In each pair, the first number is the search term, the second its weight: -<pre> -where wand(description, [[11,1], [37,2]]) -</pre> - <p> - Keys must be single or double-quoted if passed inline in YQL - - alternatively, use <a href="#parameter-substitution">parameter substitution</a> - to submit the weighted set with a simple format for faster query parsing - - example: <code>where wand(description, @myterms)</code>. - </p> - <table class="table"> - <thead> - <tr> - <th>Annotation</th> - <th>Effect</th> - </tr> - </thead> - <tbody> - <tr> - <td><a href="#scorethreshold">scoreThreshold</a></td> - <td>Minimum rank score for hits to include.</td> - </tr> - <tr> - <td><a href="#totaltargethits">totalTargetHits</a></td> - <td>Wanted number of hits exposed to the first-phase ranking function in total over the content nodes - evaluating the query.</td> - </tr> - <tr> - <td><a href="#targethits">targetHits</a></td> - <td>Wanted number of hits exposed to the first-phase ranking function per content node. - Prefer using <a href="#totaltargethits">totalTargetHits</a> over this.</td> - </tr> - </tbody> - </table> -<pre> -where ({scoreThreshold: 0.13, totalTargetHits: 7}wand(description, {"a":1, "b":2})) -</pre> - <p> - Refer to <a href="../../ranking/wand.html">using wand</a> for introduction to the WAND - algorithm and example usage of <em>wand</em> - in <a href="../../performance/practical-search-performance-guide#multi-valued-query-operators"> - practical performance guide - </a>. - </p> - <table class="table"> - <thead></thead><tbody> - <tr> - <th>Field type</th> - <td>Weighted set attribute with fast-search. - Note: Also supported for regular attribute or index fields, - but then with much weaker performance).</td> - </tr><tr> - <th>Query model</th> - <td>Weighted set with {token, weight} pairs.</td> - </tr><tr> - <th>Matching</th> - <td>Documents where the weighted set field contains at least one of the tokens in the query - and where the internal dot product score for this document, - is larger than the worst among the current top-k best hits. - This means that more than top-k documents are matched and returned for ranking. - It also means that many documents are skipped, - even they match several tokens in the query because the dot product score is too low. - This skipping makes <em>wand</em> faster than <a href="#dotproduct">dotProduct</a> in some cases. - </td> - </tr><tr> - <th>Ranking</th> - <td>Dot product score between the weights of the matched query tokens and field tokens. - This score is available using - <a href="../ranking/rank-features.html#rawScore(field)">rawScore</a> or - <a href="../ranking/rank-features.html#itemRawScore(label)">itemRawScore</a> - rank features. - Note that the top-k best hits are only guaranteed to be returned - when using this internal score as the final ranking expression. - </td> - </tr><tr> - <th style="white-space:nowrap;">Java Query Item</th> - <td><a href="https://javadoc.io/doc/com.yahoo.vespa/container-search/latest/com/yahoo/prelude/query/WandItem.html">WandItem</a></td> - </tr> - </tbody> - </table> - </td> - </tr> - - <tr> - <th>weakAnd</th> - <td> - <p id="weakand"> - <code>weakAnd</code> is sometimes called <em>Vespa Wand</em>. - Unlike <a href="#wand">wand</a>, it accepts arbitrary word matches (across arbitrary fields) as arguments. - Only a limited number of documents are returned for ranking (default is 100), - but it does not guarantee to return the best k hits. - This function can be seen as an optimized <a href="#or">or</a>: - </p> -<pre> -where weakAnd(a contains "A", b contains "B") -</pre> - <table class="table"> - <thead> - <tr> - <th>Annotation</th> - <th>Effect</th> - </tr> - </thead> - <tbody> - <tr> - <td><a href="#totaltargethits">totalTargetHits</a></td> - <td>Wanted number of hits exposed to the first-phase ranking function in total over the content nodes evaluating the query.</td> - </tr> - <tr> - <td><a href="#targethits">targetHits</a></td> - <td>Wanted number of hits exposed to the first-phase ranking function per content node. - Prefer using <a href="#totaltargethits">totalTargetHits</a> over this.</td> - </tr> - </tbody> - </table> -<pre> -where ({totaltargetHits: 7}weakAnd(a contains "A", b contains "B")) -</pre> - <p> - Unlike <a href="#wand">wand</a>, <code>weakAnd</code> can be used - to search across several fields of various types, - but it does NOT guarantee to return the top-k best number of hits. - It can however be combined with any ranking expression. - Keep in mind that this expression should correlate with its simple internal ranking score - that uses query term weight and inverse document frequency for matching terms. - </p> - <p> - Refer to <a href="../../ranking/wand.html">using wand</a> for a usage and examples. - </p> - <table class="table"> - <thead></thead><tbody> - <tr> - <th>Field type</th> - <td>Multiple fields of all types (both attribute and index).</td> - </tr><tr> - <th>Query model</th> - <td>Arbitrary number of query items searching across different fields.</td> - </tr><tr> - <th>Matching</th> - <td>Documents that matches at least one of the tokens in the query - and where the internal operator score for this document - is larger than the worst among the current top-k best hits. - As with <a href="#wand">wand</a>, this means that typically more than top-k documents are matched - and a lot of documents are skipped. - </td> - </tr><tr> - <th>Ranking</th> - <td>Internal ranking score based on query term weight - and inverse document frequency for matching terms to find the top-k hits. - This score is currently not available to the ranking framework. - Matching terms are exposed to the ranking framework - (same as when using <a href="#and">and</a> or <a href="#or">or</a>), - so an arbitrary ranking expression can be used in combination with this operator. - Note that the ranking expression used should correlate with this internal ranking score. - <a href="../ranking/rank-features.html#bm25">bm25</a>, - <a href="../ranking/rank-features.html#nativeFieldMatch">nativeFieldMatch</a> and - <a href="../ranking/rank-features.html#nativeDotProduct(field)">nativeDotProduct</a> - rank features are good starting points. - </td> - </tr><tr> - <th style="white-space:nowrap;">Java Query Item</th> - <td><a href="https://javadoc.io/doc/com.yahoo.vespa/container-search/latest/com/yahoo/prelude/query/WeakAndItem.html">WeakAndItem</a></td> - </tr> - </tbody> - </table> - </td> - </tr> - - <tr> - <th>geoLocation</th> - <td> - <p id="geolocation"> - <code>geoLocation</code> matches a <a href="../schemas/schemas.html#position">position</a> - inside a geographical circle, specified as latitude, longitude, and a maximum distance (radius). - See also <a href="#geoboundingbox">geoBoundingBox</a>. - Example: - </p> -<pre> -where geoLocation(myfieldname, 63.5, 10.5, "200 km") -</pre> - <p> - In this example we search for documents near 63.5° north, 10.5° east, - and within a 200 km radius. - So a document with a "myfieldname" position in Trondheim, Norway at - N63°25'47;E10°23'36 would match. - The first parameter is the name of the attribute field. - The second parameter is the latitude (positive for north, negative for south). - The third parameter is the longitude (positive for east, negative for west). - The fourth parameter must be a string specifying the radius and its units, - where the supported units/suffixes include "km", "m" (abbr. for meters), "miles", "mi" (abbr. for miles), - "deg" (abbr. for degrees) and "d" (contextual abbr. for degrees). - The "deg" / "d" unit / suffix gives radius the same units as latitude. - Any negative number for radius (e.g. "-1 m") is interpreted as an "infinite" radius, - letting any geographical position at all match the geoLocation operator. - </p> - <p> - The position attribute in the schema could look like: - </p> -<pre> -field myfieldname type position { - indexing: attribute | summary -} -</pre> - <p>Arrays of positions are also possible:</p> -<pre> -field myfieldname type array<position> { - indexing: attribute -} -</pre> - <table class="table"> - <thead> - <tr> - <th>Annotation</th> - <th>Effect</th> - </tr> - </thead> - <tbody> - <tr> - <td><a href="#label">label</a></td> - <td>Label for referring to this term during ranking.</td> - </tr> - </tbody> - </table> - <p> - Properties: - </p> - <table class="table"> - <thead></thead><tbody> - <tr> - <th>Field type</th> - <td>position attribute (single-valued or array).</td> - </tr><tr> - <th>Query parameters</th> - <td>Field name, latitude, longitude, radius.</td> - </tr><tr> - <th>Matching</th> - <td>Returns documents inside the given geo circle.</td> - </tr><tr> - <th>Ranking</th> - <td>Use <code>closeness(myfieldname)</code>, or <code>distance(myfieldname)</code> - in ranking calculations. - See <a href="../ranking/rank-features.html#closeness(name)">closeness</a> - and <a href="../ranking/rank-features.html#distance(name)">distance</a> documentation.</td> - </tr><tr> - <th style="white-space:nowrap;">Java Query Item</th> - <td><a href="https://javadoc.io/doc/com.yahoo.vespa/container-search/latest/com/yahoo/prelude/query/GeoLocationItem.html">GeoLocationItem</a></td> - </tr> - </tbody> - </table> - </td> - </tr> - - <tr> - <th>geoBoundingBox</th> - <td> - <p id="geoboundingbox"> - <code>geoBoundingBox</code> requires a <a href="../schemas/schemas.html#position">position</a> - to be inside a geographical rectangle; specified as 4 numbers (in degrees). - The 4 numbers must be in a specific order: - south-western corner (minimum latitude, minimum longitude) - followed by - north-eastern corner (maximum latitude, maximum longitude). - Examples: - </p> -<pre> -where geoBoundingBox(myfieldname, 63.25, 10.01, 63.45, 10.61) -where geoBoundingBox(myfieldname, -23.12, -43.85, -22.59, -42.89) -</pre> - <p> - In the first example we search for documents inside a rectangular map view around - Trondheim, Norway. - So a document with a "myfieldname" position at - <a href="https://www.google.com/maps/place/63%C2%B025'50.0%22N+10%C2%B023'42.0%22E"> - 63°25'50"N 10°23'42"E - </a> - would match. - The second example surrounds - <a href="https://www.google.com/maps/place/22%C2%B059'13.0%22S+43%C2%B012'10.0%22W">Rio de Janeiro</a>, Brazil. - </p> - <ul> - <li>The first parameter is the name of the attribute field.</li> - <li>The 2nd parameter is the minimum (southern) latitude (positive for north, negative for south).</li> - <li>The 3rd parameter is the minimum (western) longitude (positive for east, negative for west).</li> - <li>The 4th parameter is the maximum (northern) latitude (positive for north, negative for south).</li> - <li>The 5th parameter is the maximum (eastern) longitude (positive for east, negative for west).</li> - </ul> - <p> - See the <a href="#geolocation">geoLocation</a> operator for more details about positions. - Note that there is no ranking contribution from this operator; if you want to get - the distance to the center of the box, you need an additional <code>geoLocation</code> item - with that point. - </p> - <p> - Properties: - </p> - <table class="table"> - <thead></thead><tbody> - <tr> - <th>Field type</th> - <td>position attribute (single-valued or array).</td> - </tr><tr> - <th>Query parameters</th> - <td>Field name, southern, western, northern, eastern limits.</td> - </tr><tr> - <th>Matching</th> - <td>Returns documents inside the given geo bounding box.</td> - </tr><tr> - <th>Ranking</th> - <td>None.</td> - </tr><tr> - <th>Java Query Item</th> - <td><a href="https://javadoc.io/doc/com.yahoo.vespa/container-search/latest/com/yahoo/prelude/query/GeoLocationItem.html">GeoLocationItem</a></td> - </tr> - </tbody> - </table> - </td> - </tr> - - <tr> - <th>nearestNeighbor</th> - <td> - <p id="nearestneighbor"> - <code>nearestNeighbor</code> matches the top-k nearest neighbors in a multidimensional vector space. - Points in the vector space are specified as - <a href="../../ranking/tensor-user-guide.html">tensors</a> with one indexed dimension, - where the size of that dimension is equal to the dimensionality of the vector space. - </p> - <p> - The document vectors are stored in a - <a href="../schemas/schemas.html#tensor">tensor field attribute</a>, - and the query vector is sent with the query request. - The following tensor field types are supported: - </p> - <ul> - <li>Single vector per document: Tensor type with one indexed dimension. - Example: <code>tensor<float>(x[3])</code></li> - - <li>Multiple vectors per document: Tensor type with one or more mapped dimensions and one indexed dimension. - Examples: <code>tensor<float>(m{},x[3])</code>, <code>tensor<float>(m{},n{},x[3])</code></li> - </ul> - <p> - Euclidean distance is used as the default - <a href="../schemas/schemas.html#distance-metric">distance metric</a> - and the exact nearest neighbors are returned. - When storing multiple vectors per document, the vector that is closest to the query vector - is used when calculating the distance between the document and the query. - If an <a href="../schemas/schemas.html#index-hnsw">HNSW index</a> is specified on the tensor field, - the <span style="text-decoration: underline">approximate</span> nearest neighbors are returned. Example: - </p> -<pre> -where ({totaltargetHits: 10}nearestNeighbor(doc_vector, query_vector))&input.query(query_vector)=[3,5,7]&ranking=semantic -</pre> - <p> - In this example we search for the top 10 nearest neighbors in a 3-dimensional vector space. - <em>totalTargetHits</em> specifies the top-k nearest neighbors to expose to a user defined <code>semantic</code> - <a href="../../basics/ranking.html">rank profile</a>. The <a href="#totaltargethits">totalTargetHits</a> annotation is required. - The first parameter of <em>nearestNeighbor</em> is the name of the tensor field attribute - containing the document vectors (<em>doc_vector</em>). - </p> - <p> - The second parameter is the name of the tensor sent with the query request (<em>query_vector</em>). - Specifying <em>query_vector</em> as the name means the query request must set this tensor as - <em>input.query(query_vector)</em> - see the <a href="../api/query.html#ranking.features">reference</a>. - The tensor type of the <b>input query vector must be defined</b> in the rank profile: - </p> -<pre> -rank-profile semantic { - inputs { - query(query_vector) tensor<float>(x[3]) - } - first-phase: closeness(field, doc_vector) -} -</pre> - <p> - Also see <a href="../../ranking/ranking-expressions-features.html#query-feature-types">defining query feature types</a>. - Failure to define the query input tensor in the schema will fail the request: - </p> -<pre> - Expected 'query(query_vector)' to be a tensor, but it is the string '[3,5,7]' -</pre> - <p>The document tensor field attribute is defined as follows:</p> -<pre> -field doc_vector type tensor<float>(x[3]) { - indexing: attribute | summary -} -</pre> - <p>The example above does not define HNSW <code>index</code> and the search for neighbors will be exact.</p> - <p> - See <a href="../../querying/nearest-neighbor-search">Nearest Neighbor Search</a>, - <a href="../../querying/approximate-nn-hnsw">Approximate Nearest Neighbor Search using HNSW Index</a> and - <a href="../../querying/nearest-neighbor-search-guide">Nearest Neighbor Search Guide</a> - for more detailed examples. - </p> - <table class="table"> - <thead> - <tr> - <th>Annotation</th> - <th>Effect</th> - </tr> - </thead> - <tbody> - <tr> - <td><a href="#totaltargethits">totalTargetHits</a></td> - <td> - Specifies the number of hits nearestNeighbor - should expose to <a href="../../basics/ranking.html">ranking</a> in total over the content - nodes evaluating the query. Note that more or less hits may actually be produced. - Setting target hits is required. - </td> - </tr> - <tr> - <td><a href="#mintargethits">minTargetHits</a></td> - <td> - Specifies the <i>minimum</i> target hits to produce in this nearest neighbor operator. - The default value is 100. Exploring too little in a graph leads to bad quality, - and this parameter protects against that when totalTargetHits leads to some node with - little content otherwise getting a low targetHits. - </td> - </tr> - <tr> - <td><a href="#targethits">targetHits</a></td> - <td> - Specifies the target hits <i>per node</i>. - Prefer using <a href="#totaltargethits">totalTargetHits</a> over this. - </td> - </tr> - <tr> - <td><a href="#approximate">approximate</a></td> - <td> - The optional <code>approximate</code> annotation may be set to <code>false</code> - to not use an approximate <a href="../schemas/schemas.html#index-hnsw">HNSW index</a>. - This is especially useful to compare exact and approximate results in order to perform - tuning of HNSW parameters. - This annotation is default <code>true</code> when an HNSW index is specified, - otherwise it is always <code>false</code>. Setting this to <code>false</code> - might trigger <a href="../../performance/graceful-degradation.html">graceful query degradation</a>. Adjust - <a href="#timeout">timeout</a> as needed. - </td> - </tr> - <tr> - <td><a href="#hnsw-exploreadditionalhits">hnsw.exploreAdditionalHits</a></td> - <td> - Tune how many extra nodes in the HNSW graph (in addition to <code>totalTargetHits</code>) - that should be explored before selecting the best hits. Default is <code>0</code>. Increasing - this parameter increases the accuracy of the approximate search, at the cost of more distance computations. - </td> - </tr> - <tr> - <td><a href="#label">label</a></td> - <td> - Use to mark the query operator with a label - that can be referred to from the ranking expression in the rank profile. - See the <a href="../ranking/rank-features.html#closeness(dimension,name)">closeness</a> and - <a href="../ranking/rank-features.html#distance(dimension,name)">distance</a> rank features. - Useful when having multiple <code>nearestNeighbor</code> operators in the same query, - e.g., when the schema has multiple vector fields. - See <a href="../../querying/nearest-neighbor-search-guide#multiple-nearest-neighbor-search-operators-in-the-same-query"> - nearest neighbor search guide - </a> for usage example. - </td> - </tr> - <tr> - <td><a href="#distancethreshold">distanceThreshold</a></td> - <td> - Use to filter out hits with a higher distance than a threshold. - See - <a href="../../querying/nearest-neighbor-search-guide#strict-filters-and-distant-neighbors"> - nearest neighbor search guide - </a> for usage example. - </td> - </tr> - </tbody> - </table> - <p>Properties:</p> - <table class="table"> - <thead></thead><tbody> - <tr> - <th>Field type</th> - <td>Tensor attribute with one indexed dimension of size N or with one or more mapped dimensions and one indexed dimension of size N.</td> - </tr><tr> - <th>Query model</th> - <td>Tensor with one indexed dimension of size N.</td> - </tr><tr> - <th>Matching</th> - <td>Returns documents where the distance (according to the - <a href="../schemas/schemas.html#distance-metric">distance metric</a> used) - between the document tensor - and the query tensor is less than the greatest distance among the current top-k best hits. - This means that typically more than top-k documents are matched and - returned for ranking. This is similar to the behavior of - <a href="#wand">wand</a>. - When an <a href="../schemas/schemas.html#index-hnsw">HNSW index</a> is used, - the top-k best hits are calculated before regular matching happens, - taking the rest of the query filters into account.</td> - </tr><tr> - <th>Ranking</th> - <td>Calculates a closeness score that is defined as - <code>1 / (1 + d)</code>, where <code>d</code> is the distance - between the document tensor and query tensor. - This score is available using - <a href="../ranking/rank-features.html#rawScore(field)">rawScore</a>, - <a href="../ranking/rank-features.html#itemRawScore(label)">itemRawScore</a>, or - <a href="../ranking/rank-features.html#closeness(dimension,name)">closeness</a> rank features. - The raw distance is available using the - <a href="../ranking/rank-features.html#distance(dimension,name)">distance</a> rank feature.</td> - </tr><tr> - <th style="white-space:nowrap;">Java Query Item</th> - <td><a href="https://javadoc.io/doc/com.yahoo.vespa/container-search/latest/com/yahoo/prelude/query/NearestNeighborItem.html">NearestNeighborItem</a></td> - </tr> - </tbody> - </table> - </td> - </tr> - - <tr> - <th>nonEmpty</th> - <td> - <p id="nonempty"> - <em>nonEmpty</em> takes as its only argument an arbitrary search expression. - It will then perform a set of checks on that expression. - If all the checks pass, the result is the same expression, otherwise the query will fail. - The checks are as follows: - </p> - <ol> - <li>No empty search term</li> - <li>No empty operators, like phrases without terms</li> - <li>No null markers (NullItem) from e.g. failed query parsing</li> - </ol> -<pre> -yql=select * from sources * where bar contains "a" and nonEmpty(bar contains "bar" and foo contains @foo)&foo= -</pre> - <p> - Note how "foo" is empty in this case, which will force the query to fail. - If "foo" contained a searchable term, the query would not have failed. - </p> - </td> - </tr> - - <tr> - <th>predicate</th> - <td> - <p id="predicate"> - <em>predicate()</em> specifies a predicate query - - see <a href="../../schemas/predicate-fields.html">predicate fields</a>. - It takes three arguments: the predicate field to search, a map of attributes, and a map of range attributes: - </p> -<pre> -where predicate(predicate_field,{"gender":"Female"},{"age":20L}) -</pre> - <p>Due to a quirk in YQL-parsing, one cannot specify an empty map, use the number 0 instead.</p> -<pre> -where predicate(predicate_field,0,{"age":20L}) -</pre> - </td> - </tr> - - <tr> - <th>true</th> - <td> - <p id="literal.true"> - Matches all documents of any type. Care must be taken when using this since processing all documents as matches - is expensive. At minimum, consider restricting to only one schema where you know the corpus isn't too big, - see the <a href="../api/query.html#model.restrict">model.restrict</a> URL parameter. - </p> - </td> - </tr> - - <tr> - <th>false</th> - <td> - <p id="literal.false"> - Does not match any document at all. Not useful in itself, but could potentially be used as - a placeholder in the query tree. - </p> - </td> - </tr> - -</tbody> -</table> - - - - -<h2 id="order-by">order by</h2> -<!-- ToDo ref reference/query.html#ranking.sorting --> -<p> - Sort using <code>order by</code>. - Add <code>asc</code> or <code>desc</code> after the name of an - <a href="../../content/attributes.html">attribute</a> to set sort order - - ascending order is default. Add another sorting attributes to get a secondary sort, that will be a tiebreaker for the - primary ordering attribute. This is typically used to get a predictable ordering when the primary ordering attribute - has the same value for multiple documents. -</p> -<pre> -where title contains "madonna" order by price asc, releasedate desc -</pre> -<p> - Sorting function, locale and strength are defined using the annotations "function", "locale" and "strength", as in: -</p> -<pre> -where title contains "madonna" order by {function: "uca", locale: "en_US", strength: "IDENTICAL"}other desc, {function: "lowercase"}something -</pre> -{% include note.html content="<a href='../schemas/schemas.html#match-phase'>match-phase</a> -is enabled when sorting - refer to the <a href='sorting-language.html'>sorting reference</a>."%} -<p> - The <a href="../../basics/ranking.html">rank profile</a> determines the rank score each document will get. - Results are ordered by that value by default, but <code>order by</code> overrides that ordering. - Vespa does not optimize away the rank score computation in this case, it is still executed, even if the model score is thrown away. Use the built-in rank-profile <em>unranked</em> for optimal performance of sorting queries. - </p> - <p> - To do a primary ordering on the rank score, and a secondary sort on an attribute, use <code>'[relevance]'</code> as the first order by attribute. - See <a href="sorting-language.html#special-sorting-attributes">Special sorting attributes</a> for more details. -</p> -<table class="table"> - <thead> - <tr> - <th>Annotation</th> - <th>Effect</th> - </tr> - </thead> - <tbody> - <tr> - <td><a href="#function">function</a></td> - <td> - Sort function, default UCA. - </td> - </tr> - <tr> - <td><a href="#locale">locale</a></td> - <td> - Locale identifier for the <a href="#function">UCA sort function</a>. - </td> - </tr> - <tr> - <td><a href="#strength">strength</a></td> - <td> - Strength setting for the <a href="#function">UCA sort function</a>. - </td> - </tr> - </tbody> -</table> - - - -<h2 id="limit-offset">limit / offset</h2> -<p> - To specify a slice / limit the number of hits returned / do pagination, - use <code>limit</code> and/or <code>offset</code>. This can also be controlled by using - <a href="../api/query.html#native-execution-parameters">native execution parameters</a>. -</p> - {% include important.html content='Values set in YQL takes precedence over - <a href="../api/query.html#hits">hits/offset</a>. - Values for hits/offset in query profiles are also overridden by YQL, - e.g., <code>limit 100</code> overrides <code><field name="hits" overridable="false">50</field></code>.' %} -<p> - Limited by <a href="../api/query.html#hits">maxHits</a> (default 400) - and <a href="../api/query.html#offset">maxOffset</a> (default 1000) - - these can be configured in a <a href="../../querying/query-profiles.html">queryProfile</a>. -</p> -<p>Example: This returns two hits (if there are sufficiently many hits matching the query), - skipping the 29 first documents</p> -<pre> -where title contains "madonna" limit 31 offset 29 -</pre> - - - -<h2 id="timeout">timeout</h2> -<p> - Set query timeout in milliseconds using <code>timeout</code>. This can also be controlled by using the native - execution parameter <a href="../api/query.html#timeout">timeout</a>. YQL specified values takes precedence.: -</p> -<pre> -where title contains "madonna" timeout 70 -</pre> -<p> - Only literal numbers are valid, i.e. setting another unit is not supported. -</p> -{% include note.html content='The value is in milliseconds, -whereas the native execution parameter <a href="../api/query.html#timeout">timeout</a> is in seconds' %} - - - -<h2 id="parameter-substitution">Parameter substitution</h2> -<p> - Use parameter substitution to separate the YQL string from user input values. - E.g., the <a href="#text">text(value)</a> query operator - supports parameter substitution for the <code>value</code> parameter: -</p> -<pre> -... where default contains text(@userinput)&userinput=free+text -</pre> -<p> - The query operators <a href="#in">field in (value)</a>, - <a href="#dotproduct">dotProduct(field, value)</a>, - <a href="#weightedset">weightedSet(field, value)</a> and <a href="#wand">wand(field, value)</a> - support parameter substitution for the <code>value</code> parameter. -</p> -<p> - The <code>value</code> string can be passed in one of the following forms - (quotes can be skipped unless the keys contain <code>,</code> or <code>:</code>.): -</p> -<ul> - <li>List: <code>value, ...</code>. - For the <a href="#in">in</a> operator only.</li> - <li>Array: <code>[[key, value], ...]</code>. - For <a href="#dotproduct">dotproduct</a>, <a href="#weightedset">weightedset</a> and <a href="#wand">wand</a>.</li> - <li>Map: <code>{key: value, ...}</code>. - For <a href="#dotproduct">dotproduct</a>, <a href="#weightedset">weightedset</a> and <a href="#wand">wand</a>.</li> -</ul> -<p>See the <a href="/en/querying/query-api.html#parameter-substitution">query API guide</a> for examples.</p> - - - -<h2 id="annotations">Annotations</h2> -<p> - Terms and phrases can be annotated to manipulate the behavior. - Add an annotation using <code>{}</code>: -</p> -<pre> -where text contains ({distance: 5}near("a", "b")) and text contains ({distance:2}near("c", "d")) -</pre> -<p>Note that the annotation is enclosed by parentheses to scope the annotation to the operator. -</p> -<p> - All annotations are supported by the string arguments to functions like - and phrase() and near() and also the string argument to the "contains" operator. - Some annotations are also supported by the functions which are handled like leaf nodes internally in the query tree: - phrase(), near(), onear(), range(), equiv(), dotProduct(), weightedSet(), weakAnd(), wand() and nearestNeighbor(). -</p> -<p> - Refer to <a href="https://github.com/vespa-engine/vespa/blob/master/container-search/src/test/java/com/yahoo/select/SelectTestCase.java"> - SelectTestCase.java</a> for sample usage. <!-- ToDo: improve this a little or add examples elsewhere, from this --> -</p> -<table class="table"> - <thead> - <tr> - <th>Annotation</th> - <th>Default</th> - <th>Values</th> - <th>Description</th> - </tr> - </thead> - <tbody> - <tr> - <td>accentDrop</td> - <td>true</td> - <td>boolean</td> - <td><p id="accentdrop"> - Remove accents from this term if it is the setting for this field. - Refer to <a href="../../linguistics/linguistics-opennlp.html#normalization">linguistics</a>. - </p></td> - </tr> - <tr> - <td>allowEmpty</td> - <td>false</td> - <td>boolean</td> - <td> - <p id="allowempty"> - Whether to allow empty input for query parsing and query terms in <a href="#text">text</a>/<a href="#userinput">userInput</a>. - If <code>true</code>, a NullItem instance is inserted in the proper place in the query tree. - If <code>false</code>, the query will fail if the user provided input can not be parsed or is empty. - </p> - </td> - </tr> - <tr> - <td>andSegmenting</td> - <td></td> - <td>true|false</td> - <td> - <p id="andsegmenting"> - Force phrase or AND operator if re-segmenting (e.g. in stemming) this term results in multiple terms. - Default is choosing from language settings. - </p> - <!-- Too: Add an example --> - </td> - </tr> - <tr> - <td>annotations</td> - <td></td> - <td>map</td> - <td> - <p id="annotations-table"> - Map of <code>string: string</code>. Custom annotations. No special semantics inside the YQL layer. Example: - </p> - <pre>annotations : {cox: "another"}</pre> - </td> - </tr> - <tr> - <td>approximate</td> - <td></td> - <td>boolean</td> - <td> - <p id="approximate"> - Used in <a href="#nearestneighbor">nearestNeighbor</a>. - The optional <em>approximate</em> annotation may be set to <code>false</code> to disallow usage of an approximate - <a href="../schemas/schemas.html#index-hnsw">HNSW index</a>. - This is especially useful to compare exact and approximate results in order to perform tuning of other parameters. - This annotation is default <code>true</code> when an HNSW index is specified, - otherwise it is always <code>false</code>. - </p> - </td> - </tr> - <tr> - <td>ascending</td> - <td></td> - <td>boolean</td> - <td> - <p id="ascending">Ascending hit order. Used by <a href="#hitlimit">hitLimit</a>.</p> - </td> - </tr> - <tr> - <td>bounds</td> - <td><code>closed</code></td> - <td>enum</td> - <td> - <p id="bounds"> - A <a href="#numeric">numeric</a> interval is by default a closed interval. - If the lower bound is exclusive, set to <code>leftOpen</code>. - If the upper bound is exclusive, set to <code>rightOpen</code>. - If both bounds are exclusive, set the annotation to <code>open</code>. - Example: - </p> - <pre>where ({bounds:"rightOpen"}range(year, 2000, 2018))</pre> - </td> - </tr> - <tr> - <td>connectivity</td> - <td></td> - <td>map</td> - <td> - <p id="connectivity"> - Map of <code>id: int, weight: double</code> of explicit connectivity between this item - and the item with the given <a href="#id">id</a> - - see <a href="../../ranking/nativerank.html#weight-significance-and-connectedness">text matching and ranking</a>. - Example: - </p> - <pre>connectivity: {id: 4, weight: 0.8}</pre> - <!-- ToDo: verify that the keys in the map do not have to be quoted --> - </td> - </tr> - <tr> - <td>descending</td> - <td></td> - <td>boolean</td> - <td> - <p id="descending">Descending hit order. Used by <a href="#hitlimit">hitLimit</a>.</p> - </td> - </tr> - <tr> - <td>defaultIndex</td> - <td><code>default</code></td> - <td>Any searchable field in the schema.</td> - <td> - <p id="defaultindex"> - Used by <a href="#userinput">userInput</a>. - Same as <a href="../api/query.html#model.defaultindex">model.defaultIndex</a> in the query API. - If <a href="#grammar">grammar</a> is set to <code>raw</code> or <code>segment</code>, - this will be the field searched. - </p> - </td> - </tr> - <tr> - <td>distance</td> - <td>2</td> - <td>int</td> - <td> - <p id="distance"> - The <em>distance</em> annotation sets the maximum position difference to count as a match, - see <a href="#near">near</a> / <a href="#onear">onear</a>. - All matching terms must fit within positions [P, P+distance] where P is the first term's position. - Default is 2. - </p> -<pre> -where text contains ({distance: 5}near("a", "b")) -</pre> - </td> - </tr> - <tr> - <td>elementFilter</td> - <td></td> - <td>list of int</td> - <td> - <p id="elementfilter"> - Used with <a href="#sameelement">sameElement</a>. - Restricts matching in an array field to specific indices. - A non-empty list makes sameElement match - if and only if there is a match at one or more of the specified indices. - </p> -<pre> -where my_numbers contains ({elementFilter:[0, 2, 5]}sameElement("42")) -</pre> - </td> - </tr> - <tr> - <td>distanceThreshold</td> - <td>+infinity</td> - <td>double</td> - <td> - <p id="distancethreshold"> - Used in <a href="#nearestneighbor">nearestNeighbor</a>. - The <code>distanceThreshold</code> annotation may be used to filter away hits - with a higher distance than the given threshold from the results. - Note that one will never get more hits with <code>distanceThreshold</code> than you would get without it - - to get more hits, increase <a href="#totaltargethits">totalTargetHits</a>, too. - The units for the threshold depends on the - <a href="../schemas/schemas.html#distance-metric">distance metric</a> used. - </p> - </td> - </tr> - <tr> - <td>endAnchor</td> - <td>true</td> - <td>boolean</td> - <td> - <p id="endanchor"> - {% include deprecated.html content="the uri type is deprecated."%} - The <code>hostname</code> subfield of <a href="#uri">uri</a> - supports anchoring to the start and/or end of the hostname, - controlled by the <code>startAnchor</code> and <code>endAnchor</code> annotations. - Anchoring to the end is on by default while anchoring to the start is not. Hence - </p> -<pre> -where myUrlField.hostname contains uri("vespa.ai") -</pre> - <p>will match <em>vespa.ai</em> and <em>docs.vespa.ai</em>, while</p> -<pre> -where myUrlField.hostname contains ({startAnchor: true}uri("vespa.ai")) -</pre> - <p>will only match vespa.ai.</p> - </td> - </tr> - <tr> - <td>filter</td> - <td>false</td> - <td>boolean</td> - <td><p id="filter"> - Regard this term as a "filter" term and not a term from the end user. - Terms that are annotated with "filter:true" are not bolded. - See also <a href="../api/query.html#model.filter">model.filter</a>. Bolding of terms - is controlled by <a href="../schemas/schemas.html#bolding">schema:bolding</a>. - </p></td> - </tr> - <tr> - <td>function</td> - <td></td> - <td></td> - <td> - <p id="function"> - Default sort function for strings is <code>uca</code>. - Field sort specification can be configured in the <a href="../schemas/schemas.html#sorting">schema</a>, - values in the query overrides the schema settings. - </p> - <p> - Numeric fields are numerically sorted. - </p> - <table class="table"> - <thead> - <tr> - <th>Function</th> - <th>Description</th> - </tr> - </thead> - <tbody> - <tr> - <td><code>uca</code></td> - <td> - <p id="uca"> - This sorting is based on the <a href="https://icu.unicode.org/">icu</a> library - that follows the <a href="https://unicode.org/reports/tr10/"> - Universal Collation Algorithm</a>. - The specifications of - <a href="https://unicode-org.github.io/icu-docs/apidoc/dev/icu4j/com/ibm/icu/util/ULocale.html">locale</a> - and <a href="https://unicode-org.github.io/icu-docs/apidoc/dev/icu4j/com/ibm/icu/text/Collator.html">strength</a> - are identical to how <a href="https://icu.unicode.org/">icu</a> specifies them. - </p> - <p> - Both <a href="#locale">locale</a> and <a href="#strength">strength</a> are optional, - however <code>strength</code> requires <code>locale</code>. - </p> - <p> - The <a href="#locale">locale</a> query annotation will override - locale-setting in the <a href="../schemas/schemas.html#sorting">schema</a>. - If <code>locale</code> is missing from both, the <code>lowercase</code> function will be used by default. - </p> - </td> - </tr> - <tr> - <td><code>lowercase</code></td> - <td> - <p id="lowercase"> - This improves the sorting by first lowercasing and normalising the strings before sorting. - This is slightly more correct and might be enough for the use case. - It is not that much more costly than <code>raw</code> sort, and less expensive than <code>uca</code>. - </p> - </td> - </tr> - <tr> - <td><code>raw</code></td> - <td> - <p id="raw"> - Raw byteorder is a simple and fast ordering based on memcmp of utf8 for strings - and correct sort order compliant binary rep for other fields is done. - However, that is not correct for anything except computers, - looking only at the binary representation. - </p> - </td> - </tr> - </tbody> - </table> - </td> - </tr> - <tr> - <td>grammar</td> - <td><code>weakAnd</code></td> - <td><code>raw</code>, <code>segment</code> and all values accepted for the - <a href="../api/query.html#model.type">model.type</a> argument in the query API.</td> - <td> - <p id="grammar"> - How to parse <a href="#userinput">userInput</a>. - <code>raw</code> will treat the user input as a string to be matched without any processing, - <code>segment</code> will do a first pass through the linguistic libraries, - while the rest of the values will treat the string as a query to be parsed. - </p> - <p> - The individual model.type settings can also be set, using - <code>grammar.composite</code>, <code>grammar.tokenization</code>, <code>grammar.syntax</code>, and - <code>grammar.profile</code>—refer to the <a href="../api/query.html#model.type">model.type</a> documentation. - </p> - <p> - See also <a href="../../querying/query-api.html#input-examples">userInput examples</a>. - </p> - </td> - </tr> - <tr> - <td>hitLimit</td> - <td></td> - <td>int</td> - <td> - <p id="hitlimit"> - <a href="#numeric">Numeric</a> operations support <code>hitLimit</code>. - This is used for <em>capped range search</em>. - An alternative to using negative and positive values for hitLimit is always using a positive number of hits - (as a negative number of hits does not make much sense) and combine this with either of the - <a href="#ascending">ascending</a> and <a href="#descending">descending</a> annotations (but not both). - Example: <code>{hitLimit: 38, descending: true}</code> would be equivalent to setting it to -38, - i.e. only populate with 38 hits and start from upper boundary, i.e. descending order. - </p> - <p> - Note that <code>hitLimit</code> will limit the number of documents that are considered. - This is a powerful optimisation that must be used with care, - particularly in combination with other filters. - The set of documents to be considered will be limited upfront - by only selecting the N best according to the range query and the hitLimit annotation, - for further query evaluation. - </p> - <p> - <code>hitLimit</code> is not exact, but "at least". - In addition, it will only kick in if the attribute has <a href="../schemas/schemas.html#attribute">fast-search</a>. - It will look up the upper or lower bound in the range in the dictionary and scan in ascending or descending order - and select entries until it has satisfied hitLimit. - You will get all documents for all the dictionary entries selected. - </p> - <p> - See the <a href="../../performance/practical-search-performance-guide#advanced-range-search-with-hitlimit"> - practical-search-performance-guide</a> for an example. - </p> - </td> - </tr> - <tr> - <td>hnsw.exploreAdditionalHits</td> - <td></td> - <td></td> - <td> - <p id="hnsw-exploreadditionalhits"> - Used in <a href="#nearestneighbor">nearestNeighbor</a>. - When using an <a href="../schemas/schemas.html#index-hnsw">HNSW index</a>, - the optional <code>hnsw.exploreAdditionalHits</code> annotation can be used to - tune how many extra nodes in the graph (in addition to <code>totalTargetHits</code>) - should be explored before selecting the best hits. - Using a greater number here gives better quality, but worse performance. - </p> - </td> - </tr> - <tr> - <td>id</td> - <td></td> - <td>int</td> - <td><p id="id">Unique ID used for e.g. <a href="#connectivity">connectivity</a>.</p></td> - </tr> - <tr> - <td>implicitTransforms</td> - <td>true</td> - <td>boolean</td> - <td> - <p id="implicittransforms"> - Implicit term transformations (field defaults). - If <code>implicitTransforms</code> is true, - the settings for the field in the schema will be honored in term transforms, - e.g. if the field has stemming, this term will be stemmed. - If <code>implicitTransforms</code> is false, - the search backend will receive the term exactly as written in the initial YQL expression. - This is in other words a top level switch to turn off all other - <a href="../../linguistics/linguistics-opennlp.html#stemming">stemming</a>, accent removal, Unicode - <a href="../../linguistics/linguistics-opennlp.html#normalization">normalizations</a> and so on. - </p> - </td> - </tr> - <tr> - <td>label</td> - <td></td> - <td>string</td> - <td> - <p id="label"> - Used by <a href="#geolocation">geoLocation</a> - and <a href="#nearestneighbor">nearestNeighbor</a>. <!-- ToDo: and probably others ... --> - Label for referring to this term/operator during ranking. - </p> - </td> - </tr> - <tr> - <td>language</td> - <td></td> - <td>RFC 3066 language code</td> - <td> - <p id="language"> - Language setting for the linguistics handling of <a href="#text">text</a> and <a href="#userinput">userInput</a>, - also see <a href="../api/query.html#model.language">model.language</a> in the query API reference. - </p> - </td> - </tr> - <tr> - <td>locale</td> - <td></td> - <td></td> - <td> - <p id="locale"> - Used by the <a href="#function">UCA sort function</a>. - An identifier following <a href="https://www.unicode.org/reports/tr35/#Unicode_Language_and_Locale_Identifiers"> - unicode locale identifiers</a>, e.g. <code>en_US</code>. - </p> - </td> - </tr> - <tr> - <td>maxEditDistance</td> - <td>2</td> - <td>int</td> - <td> - <p id="maxeditdistance"> - Used in <a href="#fuzzy">fuzzy</a>. An inclusive upper bound of edit distance between query and string attribute. - </p> - </td> - </tr> - <tr> - <td>nfkc</td> - <td>true</td> - <td>boolean</td> - <td><p id="nfkc">NFKC <a href="../../linguistics/linguistics-opennlp.html#normalization">normalization</a>.</p></td> - </tr> - <tr> - <td>normalizeCase</td> - <td>true</td> - <td>boolean</td> - <td><p id="normalizecase">Normalize casing of this term if it is the setting for this field.</p></td> - </tr> - <tr> - <td>origin</td> - <td></td> - <td>map</td> - <td> - <p id="origin"> - Map of <code>original: string, offset: int, length: int</code>. - The (sub-)string which produced this term. Default unset. Example: - </p> - <pre>origin: {original: "abc", offset: 1, length: 2}</pre> - </td> - </tr> - <tr> - <td>prefix</td> - <td>false</td> - <td>boolean</td> - <td><p id="prefix">Do <a href="../schemas/schemas.html#prefix">prefix matching</a> for this term, - e.g. search for "word*".</p></td> - </tr> - <tr> - <td>substring</td> - <td>false</td> - <td>boolean</td> - <td>Do substring matching for this word if available in the index. ("Search for "*word*".") - Only supported for <a href="../../performance/streaming-search.html">streaming search</a>.</td> - </tr> - <tr> - <td>prefixLength</td> - <td>0</td> - <td>int</td> - <td><p id="prefixlength">Used in <a href="#fuzzy">fuzzy</a>. Number of characters that are considered frozen, so the fuzzy match will be performed with the suffix left.</p></td> - </tr> - <tr> - <td>ranked</td> - <td>true</td> - <td>boolean</td> - <td> - <p id="ranked"> - Include this term for ranking calculation. Setting ranked to false can speed up query evaluation. - Read more about <a href="../schemas/schemas.html#rank">schema reference</a>. - <a href="../../ranking/ranking-expressions-features.html#dumping-rank-features-for-specific-documents">Example</a> - </p> - </td> - </tr> - <tr> - <td>scoreThreshold</td> - <td></td> - <td>double</td> - <td> - <p id="scorethreshold"> - A threshold in <a href="#wand">wand</a> for the minimum score of hits to include as matches. - </p> - </td> - </tr> - <tr> - <td>significance</td> - <td></td> - <td>double</td> - <td> - <p id="significance"> - Significance value for text ranking features - - see <a href="../../ranking/nativerank.html#weight-significance-and-connectedness">text matching and ranking</a>. - </p> - </td> - </tr> - <tr> - <td>startAnchor</td> - <td>false</td> - <td>boolean</td> - <td><p id="startanchor"> - {% include deprecated.html content="the uri type is deprecated."%} - See <a href="#endanchor">endAnchor</a>.</p></td> - </tr> - <tr> - <td>stem</td> - <td>true</td> - <td>boolean</td> - <td><p id="stem">Stem this term if it is the setting for this field.</p></td> - </tr> - <tr> - <td>strength</td> - <td><code>PRIMARY</code></td> - <td> - <ul> - <li><code>PRIMARY</code></li> - <li><code>SECONDARY</code></li> - <li><code>TERTIARY</code></li> - <li><code>QUATERNARY</code></li> - <li><code>IDENTICAL</code></li> - </ul> - </td> - <td> - <p id="strength"> - Used by the <a href="#function">UCA sort function</a>. - Default is <code>PRIMARY</code>, which only sorts on primary differentiating characteristics; - this means that letters in uppercase/lowercase or with differences in accents only are considered equal. - </p> - </td> - </tr> - <tr> - <td>suffix</td> - <td>false</td> - <td>boolean</td> - <td><p id="suffix">Do <em>suffix matching</em> for this term, e.g. search for "*word".</p></td> - </tr> - <tr> - <td>totalTargetHits</td> - <td>100</td> - <td>int</td> - <td> - <p id="totaltargethits"> - Used with <a href="#wand">wand</a> and <a href="#weakand">weakAnd</a>, where the default is 100, - and with <a href="#nearestneighbor">nearestNeighbor</a>, where it has no default. - This sets the wanted number of hits exposed to the first-phase ranking function in total - over the content nodes evaluating the query (a <i>group</i>). - If additional second phase ranking is used, - do not set <code>totalTargetHits</code> less than the configured rank-profile's - <a href="../schemas/schemas.html#secondphase-total-rerank-count">total-rerank-count</a>. - </p> - <p> - See examples in <a href="../../querying/nearest-neighbor-search">nearest neighbor search</a>. - </p> - </td> - </tr> - <tr> - <td>minTargetHits</td> - <td>100</td> - <td>int</td> - <td> - <p id="mintargethits"> - Used with <a href="#nearestneighbor">nearestNeighbor</a>. - Specifies the <i>minimum</i> target hits to produce in this nearest neighbor operator. - The default value is 100. Exploring too little in a graph leads to bad quality, - and this parameter protects against that when totalTargetHits leads to some node with - little content otherwise getting a low targetHits. - </p> - </td> - </tr> - <tr> - <td>targetHits</td> - <td>100</td> - <td>int</td> - <td> - <p id="targethits"> - Sets target hits per node. Prefer using <a href="#totaltargethits">totalTargetHits</a> over this. - </p> - </td> - </tr> - <tr> - <td>usePositionData</td> - <td>true</td> - <td>boolean</td> - <td> - <p id="usepositiondata"> - Use term position data for text ranking features such as <a href="../../ranking/nativerank.html">nativeRank</a>. - This is <em>term</em> position, not to be confused with - <a href="../../querying/geo-search.html">geo searches</a>. Setting "usePositionData:false" can improve query performance. - </p> - <!-- ToDo: Add more documentation / link somewhere. Not sure how to use this ... --> - </td> - </tr> - <tr> - <td>weight</td> - <td>100</td> - <td>int</td> - <td> - <p id="weight"> - Term weight, used in some text ranking features - - see <a href="../../ranking/nativerank.html#weight-significance-and-connectedness">text matching and ranking</a>. - </p> -<pre> -where title contains ({weight:200}"heads") -</pre> - </td> - </tr> - </tbody> -</table> - - -<h3 id="annotations-of-sub-expressions">Annotations of sub-expressions</h3> -<p> - Consider the following query: -</p> -<pre> -select * from sources * where ({stem: false}(foo contains "a" and bar contains "b")) or foo contains ({stem: false}"c") -</pre> -<p> - The "stem" annotation controls whether a given term should be stemmed if its - field is configured as a stemmed field (default is "true"). - The "AND" operator itself has no internal API for whether its operands should be stemmed or not, - but we can still annotate as such, - because when the value of a given annotation is determined, - the expression tree is followed from the term in question and up through its ancestors. - Traversing the tree stops when a value is found (or there is nothing more to traverse). - In other words, none of the terms in this example will be stemmed. -</p> -<p> - How annotations behave may be easier to understand of expressing a boolean query in the style of an S-expression: -</p> -<pre> -(AND term1 term2 (OR term3 term4) (OR term5 (AND term6 term7))) -</pre> -<p> - The annotation scopes would then be as follows, i.e. annotations on - which elements will be checked when determining the settings for a given term: -</p> -<table class="table"> - <thead></thead><tbody> -<tr><td>term1</td><td>term1 itself, and the first AND</td></tr> -<tr><td>term2</td><td>term2 itself, and the first AND</td></tr> -<tr><td>term3</td><td>term3 itself, the first OR and the first AND</td></tr> -<tr><td>term4</td><td>term4 itself, the first OR and the first AND</td></tr> -<tr><td>term5</td><td>term5 itself, the second OR and the first AND</td></tr> -<tr><td>term6</td><td>term6 itself, the second AND, the second OR and the first AND</td></tr> -<tr><td>term7</td><td>term7 itself, the second AND, the second OR and the first AND</td></tr> -</tbody> -</table> - - - -<h2 id="query-properties">Query properties</h2> -<p> - Use YQL variable syntax to initialize words in phrases and as single terms. - This removes the need for caring about quoting a term in YQL, as well as URL quoting. - The term will be used <em>exactly</em> as it is in the URL. - As an example, look at a query with a YQL argument, and the properties - <em>animal</em> and <em>syntaxExample</em>: -</p> -<pre> -yql=select * from sources * where foo contains @animal and foo contains phrase(@animal, @syntaxExample, @animal)&animal=panda&syntaxExample=syntactic -</pre> -<p> - This YQL expression will then access the query properties <em>animal</em> and - <em>syntaxExample</em> and evaluate to: -</p> -<pre> -select * from sources * where (foo contains "panda" AND foo contains phrase("panda", "syntactic", "panda")) -</pre> - - - -<h2 id="yql-in-query-profiles">YQL in query profiles</h2> -<p> - YQL requires quoting to be included in a URL. - Since YQL is well suited to application logic, while not being intended for end users, - a solution to this is storing the application's YQL queries into different - <a href="../../querying/query-profiles.html">query profiles</a>. - To add a default query profile, add <em>search/query-profiles/default.xml</em> to the - <a href="../applications/application-packages.html">application package</a>: -</p> -<pre> -<query-profile id="default"> - <field name="yql">select * from sources * where default contains "latest" or userQuery()</field> -</query-profile> -</pre> -<p> - This will add <em>latest</em> as an <em>OR term</em> to all queries not having an explicit query profile parameter. - The important thing to note is how it is not necessary to URL-quote anything in the query profiles files. - They operate independently of the HTTP parsing as such. -</p> - - - -<h2 id="query-rewriting-in-searchers">Query rewriting in Searchers</h2> -<p> - Searchers which modifies the textual YQL statement (not recommended) - should be annotated with <code>@Before("ExternalYql")</code>. - Searchers modifying query tree produced from an input YQL statement - should annotate with <code>@After("ExternalYql")</code>. -</p> - - - -<h2 id="grouping">Grouping</h2> -<p> - Group / aggregate results by adding a grouping expression after a <code>|</code> - - <a href="../../querying/grouping.html">read more</a>. -</p> -<pre> -select * from sources * where sddocname contains 'purchase' | all(group(customer) each(output(sum(price)))) -</pre> diff --git a/en/reference/querying/yql.mdx b/en/reference/querying/yql.mdx new file mode 100644 index 0000000000..9285eaab56 --- /dev/null +++ b/en/reference/querying/yql.mdx @@ -0,0 +1,681 @@ +--- +title: "YQL Query Language Reference" +sidebarTitle: "The YQL query language" +--- + +Vespa accepts unstructured human input and structured queries for application logic separately, then combines them into a single data structure for executing. Human input is parsed heuristically, while application queries are formulated in YQL. + +<Info> + **Note:** See the [Query Language Guide](/en/querying/query-language) for query examples +</Info> + +A query URL looks like: + +```bash +http://myhost.mydomain.com:8080/search/?yql=select%20%2A%20from%20sources%20%2A%20where%20text%20contains%20%22blues%22 +``` + +In other words, `yql` contains: + +```bash +select * from sources * where text contains "blues" +``` + +This [matches](/en/reference/schemas/schemas#match) all documents where the field named _text_ contains the word _blues_. + +Quote `"` and backslash `\` characters in text values must be escaped by a backslash, also see [how does backslash escapes work](/en/learn/faq#how-does-backslash-escapes-work). + +<Danger> + **Important:** There is no way to query for a field that is not set / equals `null` or `NaN`. Work around using a "magic" value (like MAXINT) that is not normally used in the documents. +</Danger> + +## select + +_select_ is the list of [summary fields](/en/querying/document-summaries#selecting-summary-fields-in-yql) requested (a field with the `summary` index attribute). Vespa will hide other fields in the matching documents. + +```bash +select price,isbn from sources * where title contains "madonna" +``` + +The above explicitly requests the fields "price" and "isbn" (from all sources). To request all fields, use an asterisk as field selection: + +```sql +select * from sources * where title contains "madonna" +``` + +## from sources + +_from sources_ specifies which content [sources](/en/reference/api/query#model.sources) to query. Example: + +```sql +select * from music where title contains "madonna" +``` + +queries all document types in the _music_ content cluster or federation source. Query in: + +<table className="vespa-table"> + <thead> + <tr> + <th></th> + <th></th> + </tr> + </thead> + <tbody> + <tr> + <td>all sources</td> + <td><code>{`select ... from sources * where ...`}</code></td> + </tr> + <tr> + <td>a set of sources</td> + <td><code>{`select ... from sources source1, source2 where ...`}</code></td> + </tr> + <tr> + <td>a single source</td> + <td><code>{`select ... from source1 where ...`}</code></td> + </tr> + </tbody> +</table> + +In other words, _sources_ is used for querying some/all sources. If only a single source is queried, the _sources_ keyword is dropped. To restrict the query to only one schema (aka document type) use the [model.restrict](/en/reference/api/query#model.restrict) URL parameter. Also see [federation](/en/querying/federation). + +## where + +The `where` clause is a tree of operators: + +<table className="vespa-table"> + <thead> + <tr> + <th></th> + <th></th> + </tr> + </thead> + <tbody> + <tr> + <td>numeric</td> + <td>The following numeric operators are available: <code>{`= < > <= >= range(field, lower bound, upper bound)`}</code>. where 500 >= price where range(fieldname, 0, 5000000000L) Numbers must be in the signed 32-bit range. Input 64-bit signed numbers using <code>{`L`}</code> as suffix. For the <code>{`range`}</code> operator, one can also use the strings <code>{`Infinity`}</code> or <code>{`-Infinity`}</code>: where (range(year, 2000, Infinity)) | Annotation | Effect | | --- | --- | | <a href="#bounds" aria-label="bounds">bounds</a> | Range: open or closed interval. | | <a href="#hitlimit" aria-label="hitLimit">hitLimit</a> | Used for *capped range search*. The <code>{`range()`}</code> query operator with <code>{`hitLimit`}</code> can be used to efficiently implement top-k selection for ranking a subset of the documents in the index. See <a href="/en/performance/practical-search-performance-guide#advanced-range-search-with-hitlimit" aria-label="example and use cases">example and use cases</a>. | The <a href="/en/reference/schemas/schemas#weightedset" aria-label="weightedset">weightedset</a> field does not support filtering on weight. Solve this using the <a href="/en/reference/schemas/schemas#map" aria-label="map">map</a> type and <a href="#sameelement" aria-label="sameElement">sameElement</a> query operator - see <a href="/en/querying/query-language#map" aria-label="example">example</a>.</td> + </tr> + <tr> + <td>boolean</td> + <td>The boolean operator is: <code>{`=`}</code> where alive = true</td> + </tr> + <tr> + <td>contains</td> + <td>The right-hand side argument of the contains operator is either a string literal, or a function, like <code>{`phrase`}</code>. <code>{`contains`}</code> is the basic building block for text matching. The kind of <a href="/en/reference/schemas/schemas#match" aria-label="matching">matching</a> to be done depends on the field settings in the schema. where title contains "madonna" | Annotation | Effect | | --- | --- | | <a href="#stem" aria-label="stem">stem</a> | By default, the string literal is <a href="/en/linguistics/linguistics-opennlp#tokenization" aria-label="tokenized">tokenized</a> to match the field(s) searched. Explicitly control tokenization by using <a href="#stem" aria-label="stem">stem</a>:where title contains <pre><code>{`({stem: false}"madonna")`}</code></pre> | The matched field must be an <a href="/en/basics/schemas#document-fields" aria-label="indexed field or attribute">indexed field or attribute</a>. Fields inside structs are referenced using dot notation - e.g <code>{`mystruct.mystructfield`}</code>.</td> + </tr> + <tr> + <td>and</td> + <td><code>{`and`}</code> accepts other <code>{`and`}</code> statements, <code>{`or`}</code> statements, <a href="#userquery" aria-label="userQuery">userQuery</a>, logically inverted statements - and contains statements as arguments: where title contains "madonna" and title contains "saint"</td> + </tr> + <tr> + <td>or</td> + <td><code>{`or`}</code> accepts other <code>{`or`}</code> statements, <code>{`and`}</code> statements, <a href="#userquery" aria-label="userQuery">userQuery</a> - and contains statements as arguments: where title contains "madonna" or title contains "saint"</td> + </tr> + <tr> + <td>not</td> + <td>Use the <code>{`!`}</code> operator to match document that does *not* satisfy some condition: where title contains "madonna" and !(title contains "saint")</td> + </tr> + <tr> + <td>phrase</td> + <td>Phrases are expressed as a function: where text contains phrase("st", "louis", "blues")</td> + </tr> + <tr> + <td>near</td> + <td><code>{`near()`}</code> matches if all argument terms occur within the specified distance, in any order. Negative terms (prefixed with <code>{`!`}</code>) exclude matches where those terms appear within the exclusion distance. where field contains near("a", "b", "c") where field contains <code>{``}</code><code>{`({distance: 5}near("web", "search"))`}</code><code>{``}</code> where field contains near("sql", "database", !"nosql") | Annotation | Default | Description | | --- | --- | --- | | <a href="#distance" aria-label="distance">distance</a> | 2 | Maximum position difference for terms to match. | | exclusionDistance | (distance+1)/2 | Exclusion zone size around negative terms. | Negative terms must come after all positive terms. For multi-value fields, setting <a href="/en/reference/schemas/schemas#rank-element-gap" aria-label="element-gap">element-gap</a> for the field in the rank profile enables distance calculation between adjacent elements. Features below <code>{`near()`}</code> and <code>{`onear()`}</code> are filtered based on the spans for the operator match before they are exposed to the ranking features. Given the query text contains <code>{``}</code><code>{`({distance:1}near("a","b"))`}</code><code>{``}</code>and two documents. The text field in the first document is <code>{`"a a a a a a b b b b b b"`}</code>. The spans for the near match are <code>{`[[5,6]]`}</code>. Only the last occurrence of <code>{`"a"`}</code> and the first occurrence of <code>{`"b"`}</code> are kept. The text field in the second document is <code>{`"a b c a b c a b c a b c"`}</code>. The spans for the near match are <code>{`[[0,1],[3,4],[6,7],[9,10]]`}</code>. All occurrences of <code>{`"a"`}</code> and <code>{`"b"`}</code> are kept.</td> + </tr> + <tr> + <td>onear</td> + <td><code>{`onear()`}</code> (ordered near) is like <code>{`near()`}</code>, but requires terms to appear in the same order as specified in the query. With distance set to (number of terms - 1), <code>{`onear()`}</code> is equivalent to <code>{`phrase()`}</code>. where field contains onear("web", "search", "engine") where field contains <code>{``}</code><code>{`({distance: 5}onear("neural", "network"))`}</code><code>{``}</code> where field contains onear("java", "tutorial", !"script") | Annotation | Default | Description | | --- | --- | --- | | <a href="#distance" aria-label="distance">distance</a> | 2 | Maximum position difference for terms to match. | | exclusionDistance | (distance+1)/2 | Exclusion zone size around negative terms. | Negative terms must come after all positive terms. For multi-value fields, setting <a href="/en/reference/schemas/schemas#rank-element-gap" aria-label="element-gap">element-gap</a> for the field in the rank profile enables distance calculation between adjacent elements.</td> + </tr> + <tr> + <td>sameElement</td> + <td>The <code>{`sameElement()`}</code> operator lets you denote conditions that must match within the *same* element in multivalue fields containing structs or strings. By default, sameElement uses <code>{`AND`}</code> to combine the conditions: *All* the conditions must match in the same element to produce a match. For example, given this **struct**: struct person <code>{``}</code><code>{`{ field first_name type string {} field last_name type string {} field year_of_birth type int {} } field persons type array<person> { indexing: summary struct-field first_name { indexing: attribute } struct-field last_name { indexing: attribute } struct-field year_of_birth { indexing: attribute } }`}</code><code>{``}</code> We can use this query: where persons contains sameElement(first_name contains 'Joe', last_name contains 'Smith', year_of_birth < 1940) to return all documents containing a Joe Smith born before 1940 in the <code>{`persons`}</code> array. Searching a **map** is done by treating it as an array of a struct with the field members <code>{`key`}</code> and <code>{`value`}</code>. For example, given this map: <code>{``}</code><code>{`field identities type map<string, person> { indexing: summary struct-field key { indexing: attribute } struct-field value.first_name { indexing: attribute } struct-field value.last_name { indexing: attribute } struct-field value.year_of_birth { indexing: attribute } }`}</code><code>{``}</code> We can use this query: where identities contains sameElement(key contains 'father', value.first_name contains 'Joe', value.last_name contains 'Smith', value.year_of_birth < 1940) to return all documents that have a Joe Smith born before 1940 keyed as a 'father'. <code>{`sameElement()`}</code> may also be used to search **array of string** fields. Supported query operators inside sameElement() are <code>{`and`}</code>, <code>{`equiv`}</code>, <code>{`near`}</code>, <code>{`onear`}</code>, <code>{`or`}</code>, <code>{`rank`}</code> and <code>{`phrase`}</code>. <code>{`and`}</code> can be used with <code>{`!`}</code>. For example given this field: field chunks type <code>{``}</code><code>{`array<string> { indexing: index | summary } `}</code><code>{``}</code> We can use these queries: where chunks contains sameElement("one" and "two") where chunks contains sameElement("one" and equiv("two","three")) where chunks contains <code>{``}</code><code>{`sameElement("one" and ({distance: 5}near("two","three",!"four"))) `}</code><code>{``}</code>where chunks contains sameElement("one" and phrase("two","three")) where chunks contains sameElement("one" and !"two") where chunks contains sameElement("one" or "two") where chunks contains sameElement(rank("one" and "two", "three")) Features inside sameElement() for indexed fields are filtered based on the matching elements, e.g. <a href="/en/reference/ranking/rank-features#elementwise-bm25" aria-label="elementwise(bm25(descriptions),x,double)">elementwise(bm25(descriptions),x,double)</a> will only contain tensor cells based on the matching elements. Use the <a href="#elementfilter" aria-label="`elementFilter`"><code>{`elementFilter`}</code></a> annotation to restrict matching to specific element indices. For example, given a field <code>{`my_numbers type array<int>`}</code>: where<code>{``}</code><code>{`bash my_numbers contains ({elementFilter:[2]}sameElement("42"))`}</code><code>{``}</code> This only matches the element at index 2 in the array field. Multiple indices can be given: <code>{`{elementFilter:[0, 2, 5]}`}</code>. A shorthand form is also available: where my_numbers[2] = 42 The shorthand form only supports a single index. Use the <a href="#elementfilter" aria-label="`elementFilter`"><code>{`elementFilter`}</code></a> annotation to match multiple indices.</td> + </tr> + <tr> + <td>equiv</td> + <td>For cases where two terms in the same field should produce exactly the same behavior when matched, the <code>{`equiv()`}</code> operator can be used. This behaves like a special case of <code>{`or`}</code>. where fieldName contains equiv("A","B") The matching logic of equiv is the same as OR, and an OR does not have the limitations that EQUIV does (below). The difference is in how matches are visible to ranking functions. All words that are children of an OR count for ranking, while with EQUIV, they look like a single word to ranking: - Counts as only +1 for queryTermCount - Counts as 1 word for completeness measures - Proximity will not discriminate different words inside the EQUIV - Connectivity can be set between the entire EQUIV and the word before and after - Items inside the EQUIV are not directly visible to ranking features, so weight and connectivity on those will have no effect Limitations on how <code>{`equiv`}</code> can be used in a query: - <code>{`equiv`}</code> may not appear inside a phrase - It may only contain <code>{`TermItem`}</code> and <code>{`PhraseItem`}</code> instances. Operators like <code>{`and`}</code> cannot be placed inside <code>{`equiv`}</code> - <code>{`PhraseItems`}</code> inside <code>{`equiv`}</code> will rank like as if they have size 1 Learn how to use <a href="/en/linguistics/query-rewriting#equiv" aria-label="equiv">equiv</a>.</td> + </tr> + <tr> + <td>uri</td> + <td>Used to search for urls indexed using the <a href="/en/reference/schemas/schemas#uri" aria-label="uri field type">uri field type</a>. where myUrlField contains uri("vespa.ai/foo") Various subfields are supported to search components of the URL, see the field type definition. | Annotation | Effect | | --- | --- | | <a href="#startanchor" aria-label="startAnchor">startAnchor</a> | Anchor uri.hostname at start. | | <a href="#endanchor" aria-label="endAnchor">endAnchor</a> | Anchor uri.hostname at end. |</td> + </tr> + <tr> + <td>fuzzy</td> + <td><a href="https://en.wikipedia.org/wiki/Levenshtein_distance" aria-label="Levenshtein">Levenshtein</a> edit distance search within a string or array<string> <a href="/en/reference/schemas/schemas#attribute" aria-label="attribute">attribute</a>. where myStringAttribute contains ({prefixLength:1, maxEditDistance:2}fuzzy("parantesis")) Annotations below are configuring <code>{`fuzzy`}</code>: | Annotation | Effect | | --- | --- | | <a href="#maxeditdistance" aria-label="maxEditDistance">maxEditDistance</a> | An inclusive upper bound of edit distance between query and string attribute (default is 2). | | <a href="#prefixlength" aria-label="prefixLength">prefixLength</a> | Number of characters that are considered frozen, so the fuzzy match will be performed only with the suffix left. Default is 0 (i.e. <code>{`fuzzy`}</code> will match across whole query) | | <a href="#prefix" aria-label="prefix">prefix</a> | If <code>{`true`}</code>, a string is considered a match when it's possible to transform a *prefix* of the candidate string to the query string using at most <code>{`maxEditDistance`}</code> edits. See <a href="/en/querying/text-matching#fuzzy-prefix-match" aria-label="fuzzy prefix match">fuzzy prefix match</a>. Default is <code>{`false`}</code>, which means that the entire string is considered. | Find an example in <a href="/en/querying/text-matching#fuzzy-match" aria-label="text matching">text matching</a>. **Important:** Only string <a href="/en/reference/schemas/schemas#attribute" aria-label="attribute">attribute</a> fields in <a href="/en/reference/applications/services/content#document" aria-label="documents">documents</a> are supported (single, array or weightedset). Matching is optimized internally when <code>{`maxEditDistance`}</code> is 1 or 2. Setting <a href="#prefixlength" aria-label="prefixLength">prefixLength</a> greater than 0 narrows the match for the <a href="/en/reference/schemas/schemas#attribute" aria-label="fast-search">fast-search</a>, greatly reducing the number of terms that must be considered.</td> + </tr> + <tr> + <td>matches</td> + <td>Regular expression match is supported using <a href="https://en.wikibooks.org/wiki/Regular_Expressions/POSIX_Extended_Regular_Expressions" aria-label="posix extended syntax">posix extended syntax</a>, with the limitation that it is **case-insensitive**. Example matching both <code>{`madonna`}</code>, <code>{`madona`}</code> and with any number of <code>{`n`}</code>s: where attribute_field matches "mado[n]+a" Find more examples in the <a href="/en/querying/text-matching#regular-expression-match" aria-label="text matching">text matching</a> guide. **Important:** Only <a href="/en/reference/schemas/schemas#attribute" aria-label="attribute">attribute</a> fields in <a href="/en/reference/applications/services/content#document" aria-label="documents">documents</a> is supported. It is not optimized for performance. Having a prefix using the <code>{`^`}</code> will be faster than not having one. Additionally, fields that serve as both attributes and indexes are not compatible.</td> + </tr> + <tr> + <td>text</td> + <td>*text()* accepts any text and tokenizes it into a set of tokens to be searched in a given field or fieldSet. By default, the tokens are searched with a <a href="#weakand" aria-label="weakAnd">weakAnd</a> operator. You can override the default behavior via annotations: <code>{`text()`}</code> supports the same annotations as <a href="#userinput" aria-label="userInput()">userInput()</a>. Example: where text_field contains text("some text") With annotations: where text_field contains ({language:'en'}text("some text")) The text argument can be given as a reference using "@parameterName": yql=select * from sources * where text_field contains (@text)&text=some text</td> + </tr> + <tr> + <td>userInput</td> + <td>*userInput()* parses text from end users or models that may contain query syntax for choosing the fields to search, specifying phrases and negative terms etc. Since the query in userInput can specify fields, there is no "contains field" prefix before the userInput operator. yql=select * from sources * where userInput('some text') The argument can be given as a reference using "@parameterName": yql=select * from sources * where userInput(@text)&text=some text Both of these will result in the query select * from sources * where weakAnd(default contains "some", default contains "text") The default behavior may be overridden by annotations: yql=select * from sources * where ({grammar.syntax:'none',grammar.tokenization:'linguistics',grammar.composite:'near',distance:3}userInput('some text')) | Annotation | Effect | | --- | --- | | <a href="#grammar" aria-label="grammar">grammar</a> | Sets the query parse type to apply when interpreting the user input text. For any value of <code>{`grammar`}</code> other than <code>{`raw`}</code> or <code>{`segment`}</code>, only the following annotations are applied: - <a href="#defaultindex" aria-label="defaultIndex">defaultIndex</a> - <a href="#totaltargethits" aria-label="totalTargetHits">totalTargetHits</a> (for weakAnd)- <a href="#targethits" aria-label="targetHits">targetHits</a> (for weakAnd)- <a href="#distance" aria-label="distance">distance</a> (for near/oNear)- <a href="#ranked" aria-label="ranked">ranked</a> - <a href="#filter" aria-label="filter">filter</a> - <a href="#stem" aria-label="stem">stem</a> - <a href="#normalizecase" aria-label="normalizeCase">normalizeCase</a> - <a href="#accentdrop" aria-label="accentDrop">accentDrop</a> - <a href="#usepositiondata" aria-label="usePositionData">usePositionData</a> E.g. if annotating <code>{`userInput`}</code> with <code>{`phrase`}</code>, a <code>{`filter`}</code> annotation will have effect, but not <code>{`language`}</code>. See <a href="/en/reference/api/query#model.type.isYqlDefault" aria-label="isYqlDefault">isYqlDefault</a> on setting a default grammar in a request/query profile. | | <a href="#defaultindex" aria-label="defaultIndex">defaultIndex</a> | Same as <a href="/en/reference/api/query#model.defaultindex" aria-label="model.defaultIndex">model.defaultIndex</a> in the query API. | | <a href="#language" aria-label="language">language</a> | Language setting for the linguistics treatment of this userInput() call. | | <a href="#allowempty" aria-label="allowEmpty">allowEmpty</a> | Whether to allow empty input for query parsing and search terms. | In addition, other annotations, like <a href="#stem" aria-label="stem">stem</a> or <a href="#ranked" aria-label="ranked">ranked</a>, will take effect as normal. More examples can be found in the <a href="/en/querying/query-api#input-examples" aria-label="query API">query API</a> guide.</td> + </tr> + <tr> + <td>userQuery</td> + <td>*userQuery()* reads from <a href="/en/reference/api/query#model.querystring" aria-label="model.queryString">model.queryString</a> and parses the query using <a href="/en/reference/querying/simple-query-language" aria-label="simple query language">simple query language</a>. If set, <a href="/en/reference/api/query#model.filter" aria-label="model.filter">model.filter</a> is combined with *model.queryString* before the parsing. The user query is first parsed, then the resulting tree is inserted into the corresponding place in the YQL query tree. Example: $ vespa query 'select * from sources * where vendor contains "brick and mortar" AND price < 50 AND userQuery()' \ query="abc def -ghi" \ type=all This evaluates to a query where: - the numeric field *price* must be less than 50 - *vendor* must match *brick and mortar* - the default index must contain the two terms *abc* and *def*, *and not* contain *ghi*. Use <a href="/en/reference/api/query#model.defaultindex" aria-label="model.defaultIndex">model.defaultIndex</a> to specify a field or fieldset if not using *default* - see <a href="/en/querying/query-api#fieldset" aria-label="example">example</a>.</td> + </tr> + <tr> + <td>rank</td> + <td>The first, and only the first, argument of the *rank()* function determines whether a document is a match, but all arguments are used for calculating rank features. The <code>{`rank`}</code> operator is useful for boosting documents based on the presence of certain terms without impacting matching or retrieval logic. where rank(a contains "A", b contains "B", c contains "C") It's also useful in hybrid search use cases. See <a href="https://blog.vespa.ai/redefining-hybrid-search-possibilities-with-vespa/" aria-label="blog post">blog post</a> for usage examples. For example, retrieve using the <a href="#nearestneighbor" aria-label="nearestNeighbor">nearestNeighbor</a> query operator as the first argument and have matching features calculated for the other arguments. where rank(nearestNeighbor(field, queryVector), a contains "A", b contains "B", c contains "C")</td> + </tr> + <tr> + <td>in</td> + <td>The *in* operator is used to match a set of values in an integer or string field. A document is considered a match when at least one of the values matches the content of the field. This is an optimized shorthand for multiple OR conditions, and is similar to the IN operator in SQL. Available since Vespa 8.293.15 . Example: where integer_field in (10, 20, 30) where string_field in ('germany', 'france', 'norway') Where <code>{`string_field`}</code> is a field with <code>{`match:word`}</code>. There is no <a href="/en/linguistics/linguistics.html" aria-label="linguistic">linguistic</a> processing like tokenization or stemming of the string values used in the *in* operator except lowercasing. See string <a href="/en/reference/schemas/schemas#match" aria-label="match">match</a>.field string_field type string { indexing: summary | index # or attribute match: word rank:filter attribute: fast-search # if attribute } Using the *in* operator against string fields with <code>{`match:text`}</code> will cause recall issues because the field contents will be tokenized during indexing while the *in* operator does not tokenize the values. The argument before *in* is the name of the field or <a href="/en/reference/schemas/schemas#fieldset" aria-label="fieldset">fieldset</a> to search. The argument after *in* is a comma-separated list of values, enclosed in parentheses. String values must be single or double-quoted if passed inline in YQL For multi-value fields (like arrays), the *in* operator works by checking if any element in the array matches any of the values in the set. This is similar to SQL's IN operator but more streamlined for array comparisons. Example: where integer_array_field in (10, 20, 30) If integer_array_field = [5, 10, 15], it will match because 10 is in the array. Similarly, if integer_array_field = [20, 25, 30], it will match because both 20 and 30 are in the array. For faster query parsing use <a href="#parameter-substitution" aria-label="parameter substitution">parameter substitution</a> to submit the values as an additional request parameter. Quoting of string values are optional. Example: where integer_field in (@integer_values)&integer_values=10,20,30 where string_field in (@string_values)&string_values=germany,france,norway The *in* operator acts as a single term in the query tree, and does not provide any match information for text ranking features. For a discussion of usage and examples refer to: - <a href="/en/ranking/multivalue-query-operators#in-example" aria-label="multivalue query operators">multivalue query operators</a> - <a href="/en/performance/feature-tuning#multi-lookup-set-filtering" aria-label="multi-lookup set filtering">multi-lookup set filtering</a> - <a href="https://github.com/vespa-engine/system-test/tree/master/tests/search/in_operator" aria-label="in operator system test">in operator system test</a> | Field type | Singlevalue or <a href="/en/querying/searching-multivalue-fields" aria-label="multivalue">multivalue</a> <a href="/en/basics/schemas#document-fields" aria-label="attribute or index field">attribute or index field</a> with basic type <a href="/en/reference/schemas/schemas#byte" aria-label="byte">byte</a>, <a href="/en/reference/schemas/schemas#int" aria-label="int">int</a>, <a href="/en/reference/schemas/schemas#long" aria-label="long">long</a> or <a href="/en/reference/schemas/schemas#string" aria-label="string">string</a>. String fields must have <code>{`match:word`}</code> or <code>{`match:exact`}</code>. | | --- | --- | | Query model | A set of values/tokens. | | Matching | Documents where the field contains at least one of the values in the query. | | Ranking | None. | | Java Query Item | <a href="https://javadoc.io/doc/com.yahoo.vespa/container-search/latest/com/yahoo/prelude/query/NumericInItem.html" aria-label="NumericInItem">NumericInItem</a> and <a href="https://javadoc.io/doc/com.yahoo.vespa/container-search/latest/com/yahoo/prelude/query/StringInItem.html" aria-label="StringInItem">StringInItem</a>. | **Important:** When using the *in* operator with an attribute field, set <a href="/en/content/attributes#fast-search" aria-label="fast-search">fast-search</a> and <a href="/en/reference/schemas/schemas#filter" aria-label="rank: filter">rank: filter</a> for best possible performance. Always use <code>{`match:word`}</code> for string fields.</td> + </tr> + <tr> + <td>dotProduct</td> + <td>*dotProduct* calculates the dot product between the weighted set in the query and a weighted set field in the document as its rank score contribution: where dotProduct(description, {"a":1, "b":2}) The result is stored as a <a href="/en/ranking/multivalue-query-operators#raw-scores-and-query-item-labeling" aria-label="raw score">raw score</a>. A normal use case is a collection of weighted tokens produced by an algorithm, to match against a corpus containing weighted tokens produced by another algorithm in order to implement personalized content exploration. See example usage of *dotProduct* in <a href="/en/performance/practical-search-performance-guide#multi-valued-query-operators" aria-label="practical performance guide">practical performance guide</a> . Refer to <a href="/en/ranking/multivalue-query-operators" aria-label="multivalue query operators">multivalue query operators</a> for a discussion of usage and examples. Keys must be single or double-quoted if passed inline in YQL - alternatively, use <a href="#parameter-substitution" aria-label="parameter substitution">parameter substitution</a> to submit the weighted set with a simple format for faster query parsing - example: <code>{`where dotProduct(description, @myterms)`}</code>. | Field type | Weighted set attribute with fast-search. Note: Also supported for regular attribute or index fields, but then with much weaker performance). | | --- | --- | | Query model | Weighted set with {token, weight} pairs | | Matching | Documents where the weighted set field contains at least one of the tokens in the query. | | Ranking | Dot product score between the weights of the matched query tokens and field tokens. This score is available using <a href="/en/reference/ranking/rank-features#rawScore(field)" aria-label="rawScore">rawScore</a> or <a href="/en/reference/ranking/rank-features#itemRawScore(label)" aria-label="itemRawScore">itemRawScore</a> rank features. | | Java Query Item | <a href="https://javadoc.io/doc/com.yahoo.vespa/container-search/latest/com/yahoo/prelude/query/DotProductItem.html" aria-label="DotProductItem">DotProductItem</a> |</td> + </tr> + <tr> + <td>weightedSet</td> + <td>When using *weightedSet* to search a field, all tokens present in the searched field will be matched against the weighted set in the query. This means that using a weighted set to search a single-value attribute field will have similar semantics to using a normal term to search a weighted set field. The low-level matching information resulting from matching a document with a weighted set in the query will contain the weights of all the matched tokens in descending order. Each matched weight will be represented as a standard occurrence on position 0 in element 0. where weightedSet(description, {"a":1, "b":2}) *weightedSet* has similar semantics to <a href="#equiv" aria-label="equiv">equiv</a>, as it acts as a single term in the query. However, the restriction dictating that it contains a collection of weighted tokens directly enables specific back-end optimizations that improves performance for large sets of tokens compared to using the generic <a href="#equiv" aria-label="equiv">equiv</a> or <a href="#or" aria-label="or">or</a> operators. Keys must be single or double-quoted if passed inline in YQL - alternatively, use <a href="#parameter-substitution" aria-label="parameter substitution">parameter substitution</a> to submit the weighted set with a simple format for faster query parsing - example: <code>{`where weightedSet(description, @myterms)`}</code>. | Field type | Singlevalue or <a href="/en/querying/searching-multivalue-fields" aria-label="multivalue">multivalue</a> attribute or index field. (Note: Most use cases operates on a single value field). | | --- | --- | | Query model | Weighted set with {token, weight} pairs. | | Matching | Documents where the field contains at least one of the tokens in the query. For filtering use cases we recommend using the <a href="#in" aria-label="in operator">in operator</a> instead, as it is simpler to use and has slightly better performance. | | Ranking | The operator will act as a single term in the back-end. The query term weight is the weight assigned to the operator itself and the match weight is the largest weight among matching tokens from the weighted set. This operator does not produce a raw score. Due to better ranking and performance we recommend using <a href="#dotproduct" aria-label="dotProduct">dotProduct</a> instead. | | Java Query Item | <a href="https://javadoc.io/doc/com.yahoo.vespa/container-search/latest/com/yahoo/prelude/query/WeightedSetItem.html" aria-label="WeightedSetItem">WeightedSetItem</a> |</td> + </tr> + <tr> + <td>wand</td> + <td><code>{`wand`}</code> can be used to search for documents where weighted tokens in a field matches a subset of weighted tokens in the query. At the same time, it internally calculates the dot product between token weights in the query and the field. <code>{`wand`}</code> is guaranteed to return the top-k hits according to its internal dot product rank score. It is an operator that scales adaptively from <a href="#or" aria-label="or">or</a> to <a href="#and" aria-label="and">and</a>. Note that total hit count becomes inaccurate when using wand. <code>{`wand`}</code> optimizes the performance of using multiple threads per search in the backend, and is also called *Parallel Wand*. <code>{`wand`}</code> also allows numeric arguments, then the search argument is an array of arrays of length two. In each pair, the first number is the search term, the second its weight: where wand(description, [[11,1], [37,2]]) Keys must be single or double-quoted if passed inline in YQL - alternatively, use <a href="#parameter-substitution" aria-label="parameter substitution">parameter substitution</a> to submit the weighted set with a simple format for faster query parsing - example: <code>{`where wand(description, @myterms)`}</code>. | Annotation | Effect | | --- | --- | | <a href="#scorethreshold" aria-label="scoreThreshold">scoreThreshold</a> | Minimum rank score for hits to include. | | <a href="#totaltargethits" aria-label="totalTargetHits">totalTargetHits</a> | Wanted number of hits exposed to the first-phase ranking function in total over the content nodes evaluating the query. | | <a href="#targethits" aria-label="targetHits">targetHits</a> | Wanted number of hits exposed to the first-phase ranking function per content node. Prefer using <a href="#totaltargethits" aria-label="totalTargetHits">totalTargetHits</a> over this. | where ({scoreThreshold: 0.13, totalTargetHits: 7}wand(description, {"a":1, "b":2})) Refer to <a href="/en/ranking/wand" aria-label="using wand">using wand</a> for introduction to the WAND algorithm and example usage of *wand* in <a href="/en/performance/practical-search-performance-guide#multi-valued-query-operators" aria-label="practical performance guide">practical performance guide</a> . | Field type | Weighted set attribute with fast-search. Note: Also supported for regular attribute or index fields, but then with much weaker performance). | | --- | --- | | Query model | Weighted set with {token, weight} pairs. | | Matching | Documents where the weighted set field contains at least one of the tokens in the query and where the internal dot product score for this document, is larger than the worst among the current top-k best hits. This means that more than top-k documents are matched and returned for ranking. It also means that many documents are skipped, even they match several tokens in the query because the dot product score is too low. This skipping makes *wand* faster than <a href="#dotproduct" aria-label="dotProduct">dotProduct</a> in some cases. | | Ranking | Dot product score between the weights of the matched query tokens and field tokens. This score is available using <a href="/en/reference/ranking/rank-features#rawScore(field)" aria-label="rawScore">rawScore</a> or <a href="/en/reference/ranking/rank-features#itemRawScore(label)" aria-label="itemRawScore">itemRawScore</a> rank features. Note that the top-k best hits are only guaranteed to be returned when using this internal score as the final ranking expression. | | Java Query Item | <a href="https://javadoc.io/doc/com.yahoo.vespa/container-search/latest/com/yahoo/prelude/query/WandItem.html" aria-label="WandItem">WandItem</a> |</td> + </tr> + <tr> + <td>weakAnd</td> + <td><code>{`weakAnd`}</code> is sometimes called *Vespa Wand*. Unlike <a href="#wand" aria-label="wand">wand</a>, it accepts arbitrary word matches (across arbitrary fields) as arguments. Only a limited number of documents are returned for ranking (default is 100), but it does not guarantee to return the best k hits. This function can be seen as an optimized <a href="#or" aria-label="or">or</a>: where weakAnd(a contains "A", b contains "B") | Annotation | Effect | | --- | --- | | <a href="#totaltargethits" aria-label="totalTargetHits">totalTargetHits</a> | Wanted number of hits exposed to the first-phase ranking function in total over the content nodes evaluating the query. | | <a href="#targethits" aria-label="targetHits">targetHits</a> | Wanted number of hits exposed to the first-phase ranking function per content node. Prefer using <a href="#totaltargethits" aria-label="totalTargetHits">totalTargetHits</a> over this. | where ({totaltargetHits: 7}weakAnd(a contains "A", b contains "B")) Unlike <a href="#wand" aria-label="wand">wand</a>, <code>{`weakAnd`}</code> can be used to search across several fields of various types, but it does NOT guarantee to return the top-k best number of hits. It can however be combined with any ranking expression. Keep in mind that this expression should correlate with its simple internal ranking score that uses query term weight and inverse document frequency for matching terms. Refer to <a href="/en/ranking/wand" aria-label="using wand">using wand</a> for a usage and examples. | Field type | Multiple fields of all types (both attribute and index). | | --- | --- | | Query model | Arbitrary number of query items searching across different fields. | | Matching | Documents that matches at least one of the tokens in the query and where the internal operator score for this document is larger than the worst among the current top-k best hits. As with <a href="#wand" aria-label="wand">wand</a>, this means that typically more than top-k documents are matched and a lot of documents are skipped. | | Ranking | Internal ranking score based on query term weight and inverse document frequency for matching terms to find the top-k hits. This score is currently not available to the ranking framework. Matching terms are exposed to the ranking framework (same as when using <a href="#and" aria-label="and">and</a> or <a href="#or" aria-label="or">or</a>), so an arbitrary ranking expression can be used in combination with this operator. Note that the ranking expression used should correlate with this internal ranking score. <a href="/en/reference/ranking/rank-features#bm25" aria-label="bm25">bm25</a>, <a href="/en/reference/ranking/rank-features#nativeFieldMatch" aria-label="nativeFieldMatch">nativeFieldMatch</a> and <a href="/en/reference/ranking/rank-features#nativeDotProduct(field)" aria-label="nativeDotProduct">nativeDotProduct</a> rank features are good starting points. | | Java Query Item | <a href="https://javadoc.io/doc/com.yahoo.vespa/container-search/latest/com/yahoo/prelude/query/WeakAndItem.html" aria-label="WeakAndItem">WeakAndItem</a> |</td> + </tr> + <tr> + <td>geoLocation</td> + <td><code>{`geoLocation`}</code> matches a <a href="/en/reference/schemas/schemas#position" aria-label="position">position</a> inside a geographical circle, specified as latitude, longitude, and a maximum distance (radius). See also <a href="#geoboundingbox" aria-label="geoBoundingBox">geoBoundingBox</a>. Example: where geoLocation(myfieldname, 63.5, 10.5, "200 km") In this example we search for documents near 63.5° north, 10.5° east, and within a 200 km radius. So a document with a "myfieldname" position in Trondheim, Norway at N63°25'47;E10°23'36 would match. The first parameter is the name of the attribute field. The second parameter is the latitude (positive for north, negative for south). The third parameter is the longitude (positive for east, negative for west). The fourth parameter must be a string specifying the radius and its units, where the supported units/suffixes include "km", "m" (abbr. for meters), "miles", "mi" (abbr. for miles), "deg" (abbr. for degrees) and "d" (contextual abbr. for degrees). The "deg" / "d" unit / suffix gives radius the same units as latitude. Any negative number for radius (e.g. "-1 m") is interpreted as an "infinite" radius, letting any geographical position at all match the geoLocation operator. The position attribute in the schema could look like: field myfieldname type position { indexing: attribute | summary } Arrays of positions are also possible: field myfieldname type array<position> { indexing: attribute } | Annotation | Effect | | --- | --- | | <a href="#label" aria-label="label">label</a> | Label for referring to this term during ranking. | Properties: | Field type | position attribute (single-valued or array). | | --- | --- | | Query parameters | Field name, latitude, longitude, radius. | | Matching | Returns documents inside the given geo circle. | | Ranking | Use <code>{`closeness(myfieldname)`}</code>, or <code>{`distance(myfieldname)`}</code> in ranking calculations. See <a href="/en/reference/ranking/rank-features#closeness(name)" aria-label="closeness">closeness</a> and <a href="/en/reference/ranking/rank-features#distance(name)" aria-label="distance">distance</a> documentation. | | Java Query Item | <a href="https://javadoc.io/doc/com.yahoo.vespa/container-search/latest/com/yahoo/prelude/query/GeoLocationItem.html" aria-label="GeoLocationItem">GeoLocationItem</a> |</td> + </tr> + <tr> + <td>geoBoundingBox</td> + <td><code>{`geoBoundingBox`}</code> requires a <a href="/en/reference/schemas/schemas#position" aria-label="position">position</a> to be inside a geographical rectangle; specified as 4 numbers (in degrees). The 4 numbers must be in a specific order: south-western corner (minimum latitude, minimum longitude) followed by north-eastern corner (maximum latitude, maximum longitude). Examples: where geoBoundingBox(myfieldname, 63.25, 10.01, 63.45, 10.61) where geoBoundingBox(myfieldname, -23.12, -43.85, -22.59, -42.89) In the first example we search for documents inside a rectangular map view around Trondheim, Norway. So a document with a "myfieldname" position at <a href="https://www.google.com/maps/place/63%C2%B025'50.0%22N+10%C2%B023'42.0%22E" aria-label="63°25'50"N 10°23'42"E">63°25'50"N 10°23'42"E</a> would match. The second example surrounds <a href="https://www.google.com/maps/place/22%C2%B059'13.0%22S+43%C2%B012'10.0%22W" aria-label="Rio de Janeiro">Rio de Janeiro</a>, Brazil. - The first parameter is the name of the attribute field. - The 2nd parameter is the minimum (southern) latitude (positive for north, negative for south). - The 3rd parameter is the minimum (western) longitude (positive for east, negative for west). - The 4th parameter is the maximum (northern) latitude (positive for north, negative for south). - The 5th parameter is the maximum (eastern) longitude (positive for east, negative for west). See the <a href="#geolocation" aria-label="geoLocation">geoLocation</a> operator for more details about positions. Note that there is no ranking contribution from this operator; if you want to get the distance to the center of the box, you need an additional <code>{`geoLocation`}</code> item with that point. Properties: | Field type | position attribute (single-valued or array). | | --- | --- | | Query parameters | Field name, southern, western, northern, eastern limits. | | Matching | Returns documents inside the given geo bounding box. | | Ranking | None. | | Java Query Item | <a href="https://javadoc.io/doc/com.yahoo.vespa/container-search/latest/com/yahoo/prelude/query/GeoLocationItem.html" aria-label="GeoLocationItem">GeoLocationItem</a> |</td> + </tr> + <tr> + <td>nearestNeighbor</td> + <td><code>{`nearestNeighbor`}</code> matches the top-k nearest neighbors in a multidimensional vector space. Points in the vector space are specified as <a href="/en/ranking/tensor-user-guide" aria-label="tensors">tensors</a> with one indexed dimension, where the size of that dimension is equal to the dimensionality of the vector space. The document vectors are stored in a <a href="/en/reference/schemas/schemas#tensor" aria-label="tensor field attribute">tensor field attribute</a>, and the query vector is sent with the query request. The following tensor field types are supported: - Single vector per document: Tensor type with one indexed dimension. Example: <code>{`tensor<float>(x[3])`}</code> - Multiple vectors per document: Tensor type with one or more mapped dimensions and one indexed dimension. Examples: <code>{`tensor<float>(m{},x[3])`}</code>, <code>{`tensor<float>(m{},n{},x[3])`}</code> Euclidean distance is used as the default <a href="/en/reference/schemas/schemas#distance-metric" aria-label="distance metric">distance metric</a> and the exact nearest neighbors are returned. When storing multiple vectors per document, the vector that is closest to the query vector is used when calculating the distance between the document and the query. If an <a href="/en/reference/schemas/schemas#index-hnsw" aria-label="HNSW index">HNSW index</a> is specified on the tensor field, the approximate nearest neighbors are returned. Example: where ({totaltargetHits: 10}nearestNeighbor(doc_vector, query_vector))&input.query(query_vector)=[3,5,7]&ranking=semantic In this example we search for the top 10 nearest neighbors in a 3-dimensional vector space. *totalTargetHits* specifies the top-k nearest neighbors to expose to a user defined <code>{`semantic`}</code> <a href="/en/basics/ranking" aria-label="rank profile">rank profile</a>. The <a href="#totaltargethits" aria-label="totalTargetHits">totalTargetHits</a> annotation is required. The first parameter of *nearestNeighbor* is the name of the tensor field attribute containing the document vectors (*doc_vector*). The second parameter is the name of the tensor sent with the query request (*query_vector*). Specifying *query_vector* as the name means the query request must set this tensor as *input.query(query_vector)* - see the <a href="/en/reference/api/query#ranking.features" aria-label="reference">reference</a>. The tensor type of the **input query vector must be defined** in the rank profile: rank-profile semantic { inputs { query(query_vector) tensor<float>(x[3]) } first-phase: closeness(field, doc_vector) } Also see <a href="../../ranking/ranking-expressions-features#query-feature-types" aria-label="defining query feature types">defining query feature types</a>. Failure to define the query input tensor in the schema will fail the request: Expected 'query(query_vector)' to be a tensor, but it is the string '[3,5,7]' The document tensor field attribute is defined as follows: field doc_vector type tensor<float>(x[3]) { indexing: attribute | summary } The example above does not define HNSW <code>{`index`}</code> and the search for neighbors will be exact. See <a href="/en/querying/nearest-neighbor-search" aria-label="Nearest Neighbor Search">Nearest Neighbor Search</a>, <a href="/en/querying/approximate-nn-hnsw" aria-label="Approximate Nearest Neighbor Search using HNSW Index">Approximate Nearest Neighbor Search using HNSW Index</a> and <a href="/en/querying/nearest-neighbor-search-guide" aria-label="Nearest Neighbor Search Guide">Nearest Neighbor Search Guide</a> for more detailed examples. | Annotation | Effect | | --- | --- | | <a href="#totaltargethits" aria-label="totalTargetHits">totalTargetHits</a> | Specifies the number of hits nearestNeighbor should expose to <a href="/en/basics/ranking" aria-label="ranking">ranking</a> in total over the content nodes evaluating the query. Note that more or less hits may actually be produced. Setting target hits is required. | | <a href="#mintargethits" aria-label="minTargetHits">minTargetHits</a> | Specifies the *minimum* target hits to produce in this nearest neighbor operator. The default value is 100. Exploring too little in a graph leads to bad quality, and this parameter protects against that when totalTargetHits leads to some node with little content otherwise getting a low targetHits. | | <a href="#targethits" aria-label="targetHits">targetHits</a> | Specifies the target hits *per node*. Prefer using <a href="#totaltargethits" aria-label="totalTargetHits">totalTargetHits</a> over this. | | <a href="#approximate" aria-label="approximate">approximate</a> | The optional <code>{`approximate`}</code> annotation may be set to <code>{`false`}</code> to not use an approximate <a href="/en/reference/schemas/schemas#index-hnsw" aria-label="HNSW index">HNSW index</a>. This is especially useful to compare exact and approximate results in order to perform tuning of HNSW parameters. This annotation is default <code>{`true`}</code> when an HNSW index is specified, otherwise it is always <code>{`false`}</code>. Setting this to <code>{`false`}</code> might trigger <a href="../../performance/graceful-degradation.html" aria-label="graceful query degradation">graceful query degradation</a>. Adjust <a href="#timeout" aria-label="timeout">timeout</a> as needed. | | <a href="#hnsw-exploreadditionalhits" aria-label="hnsw.exploreAdditionalHits">hnsw.exploreAdditionalHits</a> | Tune how many extra nodes in the HNSW graph (in addition to <code>{`totalTargetHits`}</code>) that should be explored before selecting the best hits. Default is <code>{`0`}</code>. Increasing this parameter increases the accuracy of the approximate search, at the cost of more distance computations. | | <a href="#label" aria-label="label">label</a> | Use to mark the query operator with a label that can be referred to from the ranking expression in the rank profile. See the <a href="/en/reference/ranking/rank-features#closeness(dimension,name)" aria-label="closeness">closeness</a> and <a href="/en/reference/ranking/rank-features#distance(dimension,name)" aria-label="distance">distance</a> rank features. Useful when having multiple <code>{`nearestNeighbor`}</code> operators in the same query, e.g., when the schema has multiple vector fields. See <a href="/en/querying/nearest-neighbor-search-guide#multiple-nearest-neighbor-search-operators-in-the-same-query" aria-label="nearest neighbor search guide">nearest neighbor search guide</a> for usage example. | | <a href="#distancethreshold" aria-label="distanceThreshold">distanceThreshold</a> | Use to filter out hits with a higher distance than a threshold. See <a href="/en/querying/nearest-neighbor-search-guide#strict-filters-and-distant-neighbors" aria-label="nearest neighbor search guide">nearest neighbor search guide</a> for usage example. | Properties: | Field type | Tensor attribute with one indexed dimension of size N or with one or more mapped dimensions and one indexed dimension of size N. | | --- | --- | | Query model | Tensor with one indexed dimension of size N. | | Matching | Returns documents where the distance (according to the <a href="/en/reference/schemas/schemas#distance-metric" aria-label="distance metric">distance metric</a> used) between the document tensor and the query tensor is less than the greatest distance among the current top-k best hits. This means that typically more than top-k documents are matched and returned for ranking. This is similar to the behavior of <a href="#wand" aria-label="wand">wand</a>. When an <a href="/en/reference/schemas/schemas#index-hnsw" aria-label="HNSW index">HNSW index</a> is used, the top-k best hits are calculated before regular matching happens, taking the rest of the query filters into account. | | Ranking | Calculates a closeness score that is defined as <code>{`1 / (1 + d)`}</code>, where <code>{`d`}</code> is the distance between the document tensor and query tensor. This score is available using <a href="/en/reference/ranking/rank-features#rawScore(field)" aria-label="rawScore">rawScore</a>, <a href="/en/reference/ranking/rank-features#itemRawScore(label)" aria-label="itemRawScore">itemRawScore</a>, or <a href="/en/reference/ranking/rank-features#closeness(dimension,name)" aria-label="closeness">closeness</a> rank features. The raw distance is available using the <a href="/en/reference/ranking/rank-features#distance(dimension,name)" aria-label="distance">distance</a> rank feature. | | Java Query Item | <a href="https://javadoc.io/doc/com.yahoo.vespa/container-search/latest/com/yahoo/prelude/query/NearestNeighborItem.html" aria-label="NearestNeighborItem">NearestNeighborItem</a> |</td> + </tr> + <tr> + <td>nonEmpty</td> + <td>*nonEmpty* takes as its only argument an arbitrary search expression. It will then perform a set of checks on that expression. If all the checks pass, the result is the same expression, otherwise the query will fail. The checks are as follows: <br/> 1. No empty search term <br/> 2. No empty operators, like phrases without terms <br/> 3. No null markers (NullItem) from e.g. failed query parsing <pre><code>{`=select * from sources * where bar contains "a" and nonEmpty(bar contains "bar" and foo contains @foo)&foo=`}</code></pre> Note how "foo" is empty in this case, which will force the query to fail. If "foo" contained a searchable term, the query would not have failed.</td> + </tr> + <tr> + <td>predicate</td> + <td>*predicate()* specifies a predicate query - see <a href="/en/schemas/predicate-fields" aria-label="predicate fields">predicate fields</a>. It takes three arguments: the predicate field to search, a map of attributes, and a map of range attributes: <pre><code>{`predicate(predicate_field,{"gender":"Female"},{"age":20L})`}</code></pre> Due to a quirk in YQL-parsing, one cannot specify an empty map, use the number 0 instead. <pre><code>{`where predicate(predicate_field,0,{"age":20L})`}</code></pre></td> + </tr> + <tr> + <td>true</td> + <td>Matches all documents of any type. Care must be taken when using this since processing all documents as matches is expensive. At minimum, consider restricting to only one schema where you know the corpus isn't too big, see the <a href="/en/reference/api/query#model.restrict" aria-label="model.restrict">model.restrict</a> URL parameter.</td> + </tr> + <tr> + <td>false</td> + <td>Does not match any document at all. Not useful in itself, but could potentially be used as a placeholder in the query tree.</td> + </tr> + </tbody> +</table> + +## order by + +Sort using `order by`. Add `asc` or `desc` after the name of an [attribute](/en/content/attributes) to set sort order - ascending order is default. Add another sorting attributes to get a secondary sort, that will be a tiebreaker for the primary ordering attribute. This is typically used to get a predictable ordering when the primary ordering attribute has the same value for multiple documents. + +where title contains "madonna" order by price asc, releasedate desc + +Sorting function, locale and strength are defined using the annotations "function", "locale" and "strength", as in: + +```bash +where title contains "madonna" order by {function: "uca", locale: "en\_US", strength: "IDENTICAL"}other desc, {function: "lowercase"}something +``` + +<Info> +**Note:** [match-phase](/en/reference/schemas/schemas#match-phase) is enabled when sorting - refer to the [sorting reference](/en/reference/querying/sorting-language). +</Info> +The [rank profile](/en/basics/ranking) determines the rank score each document will get. Results are ordered by that value by default, but `order by` overrides that ordering. Vespa does not optimize away the rank score computation in this case, it is still executed, even if the model score is thrown away. Use the built-in rank-profile *unranked* for optimal performance of sorting queries. + +To do a primary ordering on the rank score, and a secondary sort on an attribute, use `'[relevance]'` as the first order by attribute. See [Special sorting attributes](/en/reference/querying/sorting-language#special-sorting-attributes) for more details. + +<table className="vespa-table"> + <thead> + <tr> + <th>Annotation</th> + <th>Effect</th> + </tr> + </thead> + <tbody> + <tr> + <td><a href="#function" aria-label="function">function</a></td> + <td>Sort function, default UCA.</td> + </tr> + <tr> + <td><a href="#locale" aria-label="locale">locale</a></td> + <td>Locale identifier for the <a href="#function" aria-label="UCA sort function">UCA sort function</a>.</td> + </tr> + <tr> + <td><a href="#strength" aria-label="strength">strength</a></td> + <td>Strength setting for the <a href="#function" aria-label="UCA sort function">UCA sort function</a>.</td> + </tr> + </tbody> +</table> + +## limit / offset + +To specify a slice / limit the number of hits returned / do pagination, use `limit` and/or `offset`. This can also be controlled by using [native execution parameters](/en/reference/api/query#native-execution-parameters). + +<Danger> +**Important:** Values set in YQL takes precedence over [hits/offset](/en/reference/api/query#hits). Values for hits/offset in query profiles are also overridden by YQL, e.g., `limit 100` overrides `<field name="hits" overridable="false">50</field>`. +</Danger> + +Limited by [maxHits](/en/reference/api/query#hits) (default 400) and [maxOffset](/en/reference/api/query#offset) (default 1000) - these can be configured in a [queryProfile](/en/querying/query-profiles). + +Example: This returns two hits (if there are sufficiently many hits matching the query), skipping the 29 first documents + +<Card> +where title contains "madonna" limit 31 offset 29 +</Card> +## timeout + +Set query timeout in milliseconds using `timeout`. This can also be controlled by using the native execution parameter [timeout](/en/reference/api/query#timeout). YQL specified values takes precedence.: + +<Card> +where title contains "madonna" timeout 70 +</Card> +Only literal numbers are valid, i.e. setting another unit is not supported. + +<Note> +**Note:** The value is in milliseconds, whereas the native execution parameter [timeout](/en/reference/api/query#timeout) is in seconds +</Note> +## Parameter substitution + +Use parameter substitution to separate the YQL string from user input values. E.g., the [text(value)](#text) query operator supports parameter substitution for the `value` parameter: + +<Card> +... where default contains text(@userinput)&userinput=free+text +</Card> + +The query operators [field in (value)](#in), [dotProduct(field, value)](#dotproduct), [weightedSet(field, value)](#weightedset) and [wand(field, value)](#wand) support parameter substitution for the `value` parameter. + +The `value` string can be passed in one of the following forms (quotes can be skipped unless the keys contain `,` or `:`.): + +- List: `value, ...`. For the [in](#in) operator only. +- Array: `[[key, value], ...]`. For [dotproduct](#dotproduct), [weightedset](#weightedset) and [wand](#wand). +- Map: `{key: value, ...}`. For [dotproduct](#dotproduct), [weightedset](#weightedset) and [wand](#wand). + +See the [query API guide](/en/querying/query-api#parameter-substitution) for examples. + +## Annotations + +Terms and phrases can be annotated to manipulate the behavior. Add an annotation using `{}`: + +``` +where text contains ({distance: 5}near("a", "b")) and text contains ({distance:2}near("c", "d")) +``` + +Note that the annotation is enclosed by parentheses to scope the annotation to the operator. + +All annotations are supported by the string arguments to functions like and phrase() and near() and also the string argument to the "contains" operator. Some annotations are also supported by the functions which are handled like leaf nodes internally in the query tree: phrase(), near(), onear(), range(), equiv(), dotProduct(), weightedSet(), weakAnd(), wand() and nearestNeighbor(). + +Refer to [SelectTestCase.java](https://github.com/vespa-engine/vespa/blob/master/container-search/src/test/java/com/yahoo/select/SelectTestCase.java) for sample usage. + +<table className="vespa-table"> + <thead> + <tr> + <th>Annotation</th> + <th>Default</th> + <th>Values</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>accentDrop</td> + <td>true</td> + <td>boolean</td> + <td>Remove accents from this term if it is the setting for this field. Refer to <a href="/en/linguistics/linguistics-opennlp#normalization" aria-label="linguistics">linguistics</a>.</td> + </tr> + <tr> + <td>allowEmpty</td> + <td>false</td> + <td>boolean</td> + <td>Whether to allow empty input for query parsing and query terms in <a href="#text" aria-label="text">text</a>/<a href="#userinput" aria-label="userInput">userInput</a>. If <code>{`true`}</code>, a NullItem instance is inserted in the proper place in the query tree. If <code>{`false`}</code>, the query will fail if the user provided input can not be parsed or is empty.</td> + </tr> + <tr> + <td>andSegmenting</td> + <td></td> + <td>true|false</td> + <td>Force phrase or AND operator if re-segmenting (e.g. in stemming) this term results in multiple terms. Default is choosing from language settings.</td> + </tr> + <tr> + <td>annotations</td> + <td></td> + <td>map</td> + <td>Map of <code>{`string: string`}</code>. Custom annotations. No special semantics inside the YQL layer. Example: <code>{``}</code><code>{` annotations : {cox: "another"}`}</code><code>{``}</code></td> + </tr> + <tr> + <td>approximate</td> + <td></td> + <td>boolean</td> + <td>Used in <a href="#nearestneighbor" aria-label="nearestNeighbor">nearestNeighbor</a>. The optional *approximate* annotation may be set to <code>{`false`}</code> to disallow usage of an approximate <a href="/en/reference/schemas/schemas#index-hnsw" aria-label="HNSW index">HNSW index</a>. This is especially useful to compare exact and approximate results in order to perform tuning of other parameters. This annotation is default <code>{`true`}</code> when an HNSW index is specified, otherwise it is always <code>{`false`}</code>.</td> + </tr> + <tr> + <td>ascending</td> + <td></td> + <td>boolean</td> + <td>Ascending hit order. Used by <a href="#hitlimit" aria-label="hitLimit">hitLimit</a>.</td> + </tr> + <tr> + <td>bounds</td> + <td><code>{`closed`}</code></td> + <td>enum</td> + <td>A <a href="#numeric" aria-label="numeric">numeric</a> interval is by default a closed interval. If the lower bound is exclusive, set to <code>{`leftOpen`}</code>. If the upper bound is exclusive, set to <code>{`rightOpen`}</code>. If both bounds are exclusive, set the annotation to <code>{`open`}</code>. Example: <code>{``}</code><code>{`where ({bounds:"rightOpen"}range(year, 2000, 2018))`}</code><code>{``}</code></td> + </tr> + <tr> + <td>connectivity</td> + <td></td> + <td>map</td> + <td>Map of <code>{`id: int, weight: double`}</code> of explicit connectivity between this item and the item with the given <a href="#id" aria-label="id">id</a> - see <a href="/en/ranking/nativerank#weight-significance-and-connectedness" aria-label="text matching and ranking">text matching and ranking</a>. Example: <code>{``}</code><code>{`connectivity: {id: 4, weight: 0.8}`}</code><code>{``}</code></td> + </tr> + <tr> + <td>descending</td> + <td></td> + <td>boolean</td> + <td>Descending hit order. Used by <a href="#hitlimit" aria-label="hitLimit">hitLimit</a>.</td> + </tr> + <tr> + <td>defaultIndex</td> + <td><code>{`default`}</code></td> + <td>Any searchable field in the schema.</td> + <td>Used by <a href="#userinput" aria-label="userInput">userInput</a>. Same as <a href="/en/reference/api/query#model.defaultindex" aria-label="model.defaultIndex">model.defaultIndex</a> in the query API. If <a href="#grammar" aria-label="grammar">grammar</a> is set to <code>{`raw`}</code> or <code>{`segment`}</code>, this will be the field searched.</td> + </tr> + <tr> + <td>distance</td> + <td>2</td> + <td>int</td> + <td>The *distance* annotation sets the maximum position difference to count as a match, see <a href="#near" aria-label="near">near</a> / <a href="#onear" aria-label="onear">onear</a>. All matching terms must fit within positions [P, P+distance] where P is the first term's position. Default is 2. <code>{``}</code><code>{` where text contains ({distance: 5}near("a", "b"))`}</code><code>{``}</code></td> + </tr> + <tr> + <td>elementFilter</td> + <td></td> + <td>list of int</td> + <td>Used with <a href="#sameelement" aria-label="sameElement">sameElement</a>. Restricts matching in an array field to specific indices. A non-empty list makes sameElement match if and only if there is a match at one or more of the specified indices. where my_numbers contains <code>{``}</code><code>{`({elementFilter:[0, 2, 5]}sameElement("42")) `}</code><code>{``}</code></td> + </tr> + <tr> + <td>distanceThreshold</td> + <td>+infinity</td> + <td>double</td> + <td>Used in <a href="#nearestneighbor" aria-label="nearestNeighbor">nearestNeighbor</a>. The <code>{`distanceThreshold`}</code> annotation may be used to filter away hits with a higher distance than the given threshold from the results. Note that one will never get more hits with <code>{`distanceThreshold`}</code> than you would get without it - to get more hits, increase <a href="#totaltargethits" aria-label="totalTargetHits">totalTargetHits</a>, too. The units for the threshold depends on the <a href="/en/reference/schemas/schemas#distance-metric" aria-label="distance metric">distance metric</a> used.</td> + </tr> + <tr> + <td>endAnchor</td> + <td>true</td> + <td>boolean</td> + <td>The <code>{`hostname`}</code> subfield of <a href="#uri" aria-label="uri">uri</a> supports anchoring to the start and/or end of the hostname, controlled by the <code>{`startAnchor`}</code> and <code>{`endAnchor`}</code> annotations. Anchoring to the end is on by default while anchoring to the start is not. Hence where myUrlField.hostname contains uri("vespa.ai") will match *vespa.ai* and *docs.vespa.ai*, while <code>{``}</code><code>{`where myUrlField.hostname contains ({startAnchor: true}uri("vespa.ai")) `}</code><code>{``}</code> will only match vespa.ai.</td> + </tr> + <tr> + <td>filter</td> + <td>false</td> + <td>boolean</td> + <td>Regard this term as a "filter" term and not a term from the end user. Terms that are annotated with "filter:true" are not bolded. See also <a href="/en/reference/api/query#model.filter" aria-label="model.filter">model.filter</a>. Bolding of terms is controlled by <a href="/en/reference/schemas/schemas#bolding" aria-label="schema:bolding">schema:bolding</a>.</td> + </tr> + <tr> + <td>function</td> + <td></td> + <td></td> + <td>Default sort function for strings is <code>{`uca`}</code>. Field sort specification can be configured in the <a href="/en/reference/schemas/schemas#sorting" aria-label="schema">schema</a>, values in the query overrides the schema settings. Numeric fields are numerically sorted. | Function | Description | | --- | --- | | <code>{`uca`}</code> | This sorting is based on the <a href="https://icu.unicode.org/" aria-label="icu">icu</a> library that follows the <a href="https://unicode.org/reports/tr10/" aria-label="Universal Collation Algorithm">Universal Collation Algorithm</a>. The specifications of <a href="https://unicode-org.github.io/icu-docs/apidoc/dev/icu4j/com/ibm/icu/util/ULocale.html" aria-label="locale">locale</a> and <a href="https://unicode-org.github.io/icu-docs/apidoc/dev/icu4j/com/ibm/icu/text/Collator.html" aria-label="strength">strength</a> are identical to how <a href="https://icu.unicode.org/" aria-label="icu">icu</a> specifies them. Both <a href="#locale" aria-label="locale">locale</a> and <a href="#strength" aria-label="strength">strength</a> are optional, however <code>{`strength`}</code> requires <code>{`locale`}</code>. The <a href="#locale" aria-label="locale">locale</a> query annotation will override locale-setting in the <a href="/en/reference/schemas/schemas#sorting" aria-label="schema">schema</a>. If <code>{`locale`}</code> is missing from both, the <code>{`lowercase`}</code> function will be used by default. | | <code>{`lowercase`}</code> | This improves the sorting by first lowercasing and normalising the strings before sorting. This is slightly more correct and might be enough for the use case. It is not that much more costly than <code>{`raw`}</code> sort, and less expensive than <code>{`uca`}</code>. | | <code>{`raw`}</code> | Raw byteorder is a simple and fast ordering based on memcmp of utf8 for strings and correct sort order compliant binary rep for other fields is done. However, that is not correct for anything except computers, looking only at the binary representation. |</td> + </tr> + <tr> + <td>grammar</td> + <td><code>{`weakAnd`}</code></td> + <td><code>{`raw`}</code>, <code>{`segment`}</code> and all values accepted for the <a href="/en/reference/api/query#model.type" aria-label="model.type">model.type</a> argument in the query API.</td> + <td>How to parse <a href="#userinput" aria-label="userInput">userInput</a>. <code>{`raw`}</code> will treat the user input as a string to be matched without any processing, <code>{`segment`}</code> will do a first pass through the linguistic libraries, while the rest of the values will treat the string as a query to be parsed. The individual model.type settings can also be set, using <code>{`grammar.composite`}</code>, <code>{`grammar.tokenization`}</code>, <code>{`grammar.syntax`}</code>, and <code>{`grammar.profile`}</code>—refer to the <a href="/en/reference/api/query#model.type" aria-label="model.type">model.type</a> documentation. See also <a href="/en/querying/query-api#input-examples" aria-label="userInput examples">userInput examples</a>.</td> + </tr> + <tr> + <td>hitLimit</td> + <td></td> + <td>int</td> + <td><a href="#numeric" aria-label="Numeric">Numeric</a> operations support <code>{`hitLimit`}</code>. This is used for *capped range search*. An alternative to using negative and positive values for hitLimit is always using a positive number of hits (as a negative number of hits does not make much sense) and combine this with either of the <a href="#ascending" aria-label="ascending">ascending</a> and <a href="#descending" aria-label="descending">descending</a> annotations (but not both). Example: <code>{`{hitLimit: 38, descending: true}`}</code> would be equivalent to setting it to -38, i.e. only populate with 38 hits and start from upper boundary, i.e. descending order. Note that <code>{`hitLimit`}</code> will limit the number of documents that are considered. This is a powerful optimisation that must be used with care, particularly in combination with other filters. The set of documents to be considered will be limited upfront by only selecting the N best according to the range query and the hitLimit annotation, for further query evaluation. <code>{`hitLimit`}</code> is not exact, but "at least". In addition, it will only kick in if the attribute has <a href="/en/reference/schemas/schemas#attribute" aria-label="fast-search">fast-search</a>. It will look up the upper or lower bound in the range in the dictionary and scan in ascending or descending order and select entries until it has satisfied hitLimit. You will get all documents for all the dictionary entries selected. See the <a href="/en/performance/practical-search-performance-guide#advanced-range-search-with-hitlimit" aria-label="practical-search-performance-guide">practical-search-performance-guide</a> for an example.</td> + </tr> + <tr> + <td>hnsw.exploreAdditionalHits</td> + <td></td> + <td></td> + <td>Used in <a href="#nearestneighbor" aria-label="nearestNeighbor">nearestNeighbor</a>. When using an <a href="/en/reference/schemas/schemas#index-hnsw" aria-label="HNSW index">HNSW index</a>, the optional <code>{`hnsw.exploreAdditionalHits`}</code> annotation can be used to tune how many extra nodes in the graph (in addition to <code>{`totalTargetHits`}</code>) should be explored before selecting the best hits. Using a greater number here gives better quality, but worse performance.</td> + </tr> + <tr> + <td>id</td> + <td></td> + <td>int</td> + <td>Unique ID used for e.g. <a href="#connectivity" aria-label="connectivity">connectivity</a>.</td> + </tr> + <tr> + <td>implicitTransforms</td> + <td>true</td> + <td>boolean</td> + <td>Implicit term transformations (field defaults). If <code>{`implicitTransforms`}</code> is true, the settings for the field in the schema will be honored in term transforms, e.g. if the field has stemming, this term will be stemmed. If <code>{`implicitTransforms`}</code> is false, the search backend will receive the term exactly as written in the initial YQL expression. This is in other words a top level switch to turn off all other <a href="/en/linguistics/linguistics-opennlp#stemming" aria-label="stemming">stemming</a>, accent removal, Unicode <a href="/en/linguistics/linguistics-opennlp#normalization" aria-label="normalizations">normalizations</a> and so on.</td> + </tr> + <tr> + <td>label</td> + <td></td> + <td>string</td> + <td>Used by <a href="#geolocation" aria-label="geoLocation">geoLocation</a> and <a href="#nearestneighbor" aria-label="nearestNeighbor">nearestNeighbor</a>. Label for referring to this term/operator during ranking.</td> + </tr> + <tr> + <td>language</td> + <td></td> + <td>RFC 3066 language code</td> + <td>Language setting for the linguistics handling of <a href="#text" aria-label="text">text</a> and <a href="#userinput" aria-label="userInput">userInput</a>, also see <a href="/en/reference/api/query#model.language" aria-label="model.language">model.language</a> in the query API reference.</td> + </tr> + <tr> + <td>locale</td> + <td></td> + <td></td> + <td>Used by the <a href="#function" aria-label="UCA sort function">UCA sort function</a>. An identifier following <a href="https://www.unicode.org/reports/tr35/#Unicode_Language_and_Locale_Identifiers" aria-label="unicode locale identifiers">unicode locale identifiers</a>, e.g. <code>{`en_US`}</code>.</td> + </tr> + <tr> + <td>maxEditDistance</td> + <td>2</td> + <td>int</td> + <td>Used in <a href="#fuzzy" aria-label="fuzzy">fuzzy</a>. An inclusive upper bound of edit distance between query and string attribute.</td> + </tr> + <tr> + <td>nfkc</td> + <td>true</td> + <td>boolean</td> + <td>NFKC <a href="/en/linguistics/linguistics-opennlp#normalization" aria-label="normalization">normalization</a>.</td> + </tr> + <tr> + <td>normalizeCase</td> + <td>true</td> + <td>boolean</td> + <td>Normalize casing of this term if it is the setting for this field.</td> + </tr> + <tr> + <td>origin</td> + <td></td> + <td>map</td> + <td>Map of <code>{`original: string, offset: int, length: int`}</code>. The (sub-)string which produced this term. Default unset. Example: <code>{``}</code><code>{` origin: {original: "abc", offset: 1, length: 2}`}</code><code>{``}</code></td> + </tr> + <tr> + <td>prefix</td> + <td>false</td> + <td>boolean</td> + <td>Do <a href="/en/reference/schemas/schemas#prefix" aria-label="prefix matching">prefix matching</a> for this term, e.g. search for "word*".</td> + </tr> + <tr> + <td>substring</td> + <td>false</td> + <td>boolean</td> + <td>Do substring matching for this word if available in the index. ("Search for "*word*".") Only supported for <a href="/en/performance/streaming-search" aria-label="streaming search">streaming search</a>.</td> + </tr> + <tr> + <td>prefixLength</td> + <td>0</td> + <td>int</td> + <td>Used in <a href="#fuzzy" aria-label="fuzzy">fuzzy</a>. Number of characters that are considered frozen, so the fuzzy match will be performed with the suffix left.</td> + </tr> + <tr> + <td>ranked</td> + <td>true</td> + <td>boolean</td> + <td>Include this term for ranking calculation. Setting ranked to false can speed up query evaluation. Read more about <a href="/en/reference/schemas/schemas#rank" aria-label="schema reference">schema reference</a>. <a href="/en/ranking/ranking-expressions-features#dumping-rank-features-for-specific-documents" aria-label="Example">Example</a></td> + </tr> + <tr> + <td>scoreThreshold</td> + <td></td> + <td>double</td> + <td>A threshold in <a href="#wand" aria-label="wand">wand</a> for the minimum score of hits to include as matches.</td> + </tr> + <tr> + <td>significance</td> + <td></td> + <td>double</td> + <td>Significance value for text ranking features - see <a href="/en/ranking/nativerank#weight-significance-and-connectedness" aria-label="text matching and ranking">text matching and ranking</a>.</td> + </tr> + <tr> + <td>startAnchor</td> + <td>false</td> + <td>boolean</td> + <td>See <a href="#endanchor" aria-label="endAnchor">endAnchor</a>.</td> + </tr> + <tr> + <td>stem</td> + <td>true</td> + <td>boolean</td> + <td>Stem this term if it is the setting for this field.</td> + </tr> + <tr> + <td>strength</td> + <td><code>{`PRIMARY`}</code></td> + <td>- <code>{`PRIMARY`}</code> - <code>{`SECONDARY`}</code> - <code>{`TERTIARY`}</code> - <code>{`QUATERNARY`}</code> - <code>{`IDENTICAL`}</code></td> + <td>Used by the <a href="#function" aria-label="UCA sort function">UCA sort function</a>. Default is <code>{`PRIMARY`}</code>, which only sorts on primary differentiating characteristics; this means that letters in uppercase/lowercase or with differences in accents only are considered equal.</td> + </tr> + <tr> + <td>suffix</td> + <td>false</td> + <td>boolean</td> + <td>Do *suffix matching* for this term, e.g. search for "*word".</td> + </tr> + <tr> + <td>totalTargetHits</td> + <td>100</td> + <td>int</td> + <td>Used with <a href="#wand" aria-label="wand">wand</a> and <a href="#weakand" aria-label="weakAnd">weakAnd</a>, where the default is 100, and with <a href="#nearestneighbor" aria-label="nearestNeighbor">nearestNeighbor</a>, where it has no default. This sets the wanted number of hits exposed to the first-phase ranking function in total over the content nodes evaluating the query (a *group*). If additional second phase ranking is used, do not set <code>{`totalTargetHits`}</code> less than the configured rank-profile's <a href="/en/reference/schemas/schemas#secondphase-total-rerank-count" aria-label="total-rerank-count">total-rerank-count</a>. See examples in <a href="/en/querying/nearest-neighbor-search" aria-label="nearest neighbor search">nearest neighbor search</a>.</td> + </tr> + <tr> + <td>minTargetHits</td> + <td>100</td> + <td>int</td> + <td>Used with <a href="#nearestneighbor" aria-label="nearestNeighbor">nearestNeighbor</a>. Specifies the *minimum* target hits to produce in this nearest neighbor operator. The default value is 100. Exploring too little in a graph leads to bad quality, and this parameter protects against that when totalTargetHits leads to some node with little content otherwise getting a low targetHits.</td> + </tr> + <tr> + <td>targetHits</td> + <td>100</td> + <td>int</td> + <td>Sets target hits per node. Prefer using <a href="#totaltargethits" aria-label="totalTargetHits">totalTargetHits</a> over this.</td> + </tr> + <tr> + <td>usePositionData</td> + <td>true</td> + <td>boolean</td> + <td>Use term position data for text ranking features such as <a href="/en/ranking/nativerank" aria-label="nativeRank">nativeRank</a>. This is *term* position, not to be confused with <a href="/en/querying/geo-search" aria-label="geo searches">geo searches</a>. Setting "usePositionData:false" can improve query performance.</td> + </tr> + <tr> + <td>weight</td> + <td>100</td> + <td>int</td> + <td>Term weight, used in some text ranking features - see <a href="/en/ranking/nativerank#weight-significance-and-connectedness" aria-label="text matching and ranking">text matching and ranking</a>. <code>{``}</code><code>{`where title contains ({weight:200}"heads")`}</code><code>{``}</code></td> + </tr> + </tbody> +</table> + +### Annotations of sub-expressions + +Consider the following query: + +```sql +select * from sources * where ({stem: false}(foo contains "a" and bar contains "b")) or foo contains ({stem: false}"c") +``` + +The "stem" annotation controls whether a given term should be stemmed if its field is configured as a stemmed field (default is "true"). The "AND" operator itself has no internal API for whether its operands should be stemmed or not, but we can still annotate as such, because when the value of a given annotation is determined, the expression tree is followed from the term in question and up through its ancestors. Traversing the tree stops when a value is found (or there is nothing more to traverse). In other words, none of the terms in this example will be stemmed. + +How annotations behave may be easier to understand of expressing a boolean query in the style of an S-expression: + +```sql +(AND term1 term2 (OR term3 term4) (OR term5 (AND term6 term7))) +``` + +The annotation scopes would then be as follows, i.e. annotations on which elements will be checked when determining the settings for a given term: + +<table className="vespa-table"> + <thead> + <tr> + <th></th> + <th></th> + </tr> + </thead> + <tbody> + <tr> + <td>term1</td> + <td>term1 itself, and the first AND</td> + </tr> + <tr> + <td>term2</td> + <td>term2 itself, and the first AND</td> + </tr> + <tr> + <td>term3</td> + <td>term3 itself, the first OR and the first AND</td> + </tr> + <tr> + <td>term4</td> + <td>term4 itself, the first OR and the first AND</td> + </tr> + <tr> + <td>term5</td> + <td>term5 itself, the second OR and the first AND</td> + </tr> + <tr> + <td>term6</td> + <td>term6 itself, the second AND, the second OR and the first AND</td> + </tr> + <tr> + <td>term7</td> + <td>term7 itself, the second AND, the second OR and the first AND</td> + </tr> + </tbody> +</table> + +## Query properties + +Use YQL variable syntax to initialize words in phrases and as single terms. This removes the need for caring about quoting a term in YQL, as well as URL quoting. The term will be used _exactly_ as it is in the URL. As an example, look at a query with a YQL argument, and the properties _animal_ and _syntaxExample_: + +```bash +yql=select * from sources * where foo contains @animal and foo contains phrase(@animal, @syntaxExample, @animal)&animal=panda&syntaxExample=syntactic +``` + +This YQL expression will then access the query properties _animal_ and _syntaxExample_ and evaluate to: + +```sql +select * from sources * where (foo contains "panda" AND foo contains phrase("panda", "syntactic", "panda")) +``` + +## YQL in query profiles + +YQL requires quoting to be included in a URL. Since YQL is well suited to application logic, while not being intended for end users, a solution to this is storing the application's YQL queries into different [query profiles](/en/querying/query-profiles). To add a default query profile, add _search/query-profiles/default.xml_ to the [application package](/en/reference/applications/application-packages): + +```xml +<query-profile id="default"> + <field name="yql">select * from sources * where default contains "latest" or userQuery()</field> +</query-profile> +``` + +This will add _latest_ as an _OR term_ to all queries not having an explicit query profile parameter. The important thing to note is how it is not necessary to URL-quote anything in the query profiles files. They operate independently of the HTTP parsing as such. + +## Query rewriting in Searchers + +Searchers which modifies the textual YQL statement (not recommended) should be annotated with `@Before("ExternalYql")`. Searchers modifying query tree produced from an input YQL statement should annotate with `@After("ExternalYql")`. + +## Grouping + +Group / aggregate results by adding a grouping expression after a `|` - [read more](/en/querying/grouping). + +```sql +select * from sources * where sddocname contains 'purchase' | all(group(customer) each(output(sum(price)))) +``` diff --git a/en/reference/rag/chunking.html b/en/reference/rag/chunking.html deleted file mode 100644 index 8db4b27bd1..0000000000 --- a/en/reference/rag/chunking.html +++ /dev/null @@ -1,61 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Chunking Reference" -redirect_from: -- /en/reference/chunking-reference.html ---- - -<p>Reference configuration for <i>chunkers</i>: Components that splits text into pieces in -<a href="../writing/indexing-language.html#chunk">chunk indexing expressions</a>, as in</p> - -<pre>indexing: input myTextField | chunk fixed-length 500 | index</pre> - -<p>See also the <a href="../../rag/working-with-chunks.html">guide to working with chunks</a>.</p> - - -<h2 id="built-in-chunkers">Built-in chunkers</h2> - -<p>Vespa provides these built-in chunkers:</p> - -<table class="table"> -<thead> -<tr> -<th>Chunker id</th><th>Arguments</th><th>Description</th> -</tr> -</thead> -<tbody> -<tr> -<td>sentence</td> -<td>-</td> -<td>Splits the text into chunks at sentence boundaries.</td> -</tr> -<tr> -<td>fixed-length</td> -<td>target chunk length in characters</td> -<td>Splits the text into chunks with roughly equal length. This will prefer to make chunks of similar length, -and to split at reasonable locations over matching the target length exactly.</td> -</tr> -</tbody> -</table> - - - -<h2 id="chunker-components">Chunker components</h2> - -<p>Chunkers are <a href="../../applications/components.html">components</a>, so you can also add your own:</p> - -<pre>{% highlight xml %} -<container version="1.0"> - <component id="myChunker" - class="com.example.MyChunker" - bundle="the name in artifactId in pom.xml"> - <config name='com.example.my-chunker'> - <myValue>foo</myValue> - </config> - </component> -</container> -{% endhighlight %}</pre> - -<p>You create a chunker component by implementing the -<a href="https://github.com/vespa-engine/vespa/blob/master/linguistics/src/main/java/com/yahoo/language/process/Chunker.java">com.yahoo.language.process.Chunker</a> -interface, see <a href="https://github.com/vespa-engine/vespa/tree/master/linguistics/src/main/java/ai/vespa/language/chunker">these examples</a>.</p> diff --git a/en/reference/rag/chunking.mdx b/en/reference/rag/chunking.mdx new file mode 100644 index 0000000000..61ef05e0b9 --- /dev/null +++ b/en/reference/rag/chunking.mdx @@ -0,0 +1,56 @@ +--- +title: "Chunking Reference" +sidebarTitle: "Chunking" +--- + +Reference configuration for *chunkers*: Components that splits text into pieces in [chunk indexing expressions](/en/reference/writing/indexing-language#chunk), as in + +```bash +indexing: input myTextField | chunk fixed-length 500 | index +``` + +See also the [guide to working with chunks](/en/rag/working-with-chunks). + +## Built-in chunkers + +Vespa provides these built-in chunkers: + +<table className="vespa-table"> + <thead> + <tr> + <th>Chunker id</th> + <th>Arguments</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>sentence</td> + <td>-</td> + <td>Splits the text into chunks at sentence boundaries.</td> + </tr> + <tr> + <td>fixed-length</td> + <td>target chunk length in characters</td> + <td>Splits the text into chunks with roughly equal length. This will prefer to make chunks of similar length, and to split at reasonable locations over matching the target length exactly.</td> + </tr> + </tbody> +</table> + +## Chunker components + +Chunkers are [components](/en/applications/components), so you can also add your own: + +```xml +<container version="1.0"> + <component id="myChunker" + class="com.example.MyChunker" + bundle="the name in artifactId in pom.xml"> + <config name='com.example.my-chunker'> + <myValue>foo</myValue> + </config> + </component> +</container> +``` + +You create a chunker component by implementing the [com.yahoo.language.process.Chunker](https://github.com/vespa-engine/vespa/blob/master/linguistics/src/main/java/com/yahoo/language/process/Chunker.java) interface, see [these examples](https://github.com/vespa-engine/vespa/tree/master/linguistics/src/main/java/ai/vespa/language/chunker). \ No newline at end of file diff --git a/en/reference/rag/embedding.html b/en/reference/rag/embedding.html deleted file mode 100644 index 5208105f98..0000000000 --- a/en/reference/rag/embedding.html +++ /dev/null @@ -1,996 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Embedding Reference" -redirect_from: -- /en/reference/embedding-reference.html ---- - -<p>Reference configuration for <a href="../../rag/embedding.html">embedders</a>.</p> - -<h2 id="model-config-reference">Model config reference</h2> -<p> - Embedder models use the <a href="../applications/config-files.html#model">model</a> type configuration which accepts the attributes - <code>model-id</code>, <code>url</code> or <code>path</code>. - Multiple of these can be specified as a single config value, where one is used depending on the deployment environment: -</p> - <ul> - <li>If a <code>model-id</code> is specified and the application is deployed on Vespa Cloud, the <code>model-id</code> is used.</li> - <li>Otherwise, if a <code>url</code> is specified, it is used</li> - <li>Otherwise, <code>path</code> is used.</li> - </ul> -<p>When using <code>path</code>, the model files must be supplied in the application package.</p> - - - -<h2 id="huggingface-embedder">Huggingface Embedder</h2> -<p> - An embedder using any <a href="https://huggingface.co/docs/tokenizers/index">Huggingface tokenizer</a>, - including multilingual tokenizers, - to produce tokens which is then input to a supplied transformer model in ONNX model format. -</p> -<p> - The Huggingface embedder is configured in <a href="../applications/services/services.html">services.xml</a>, - within the <code>container</code> tag: -</p> -<pre>{% highlight xml %} -<container id="default" version="1.0"> - <component id="hf-embedder" type="hugging-face-embedder"> - <transformer-model path="my-models/model.onnx"/> - <tokenizer-model path="my-models/tokenizer.json"/> - <prepend> - <query>query:</query> - <document>passage:</document> - </prepend> - </component> - ... -</container> -{% endhighlight %}</pre> - -<h3 id="private-model-hub">Private Model Hub</h3> -<p> -You may also use models hosted in a -<a href="https://huggingface.co/docs/hub/en/repositories-settings#private-repositories">private Huggingface model hub</a>. -<p/> -<p> -Retrieve an API key from Huggingface with the appropriate permissions, and add it to the <a href="../../security/secret-store">vespa secret store.</a> -Add the secret to the container <code><secrets></code> and refer to it in your Huggingface model configuration: -</p> - -<pre>{% highlight xml %} -<container id="default" version="1.0"> - <secrets> - <myPrivateHubApiKey vault="my-vault" name="my-secret-name" /> - </secrets> - <component id="hf-embedder" type="hugging-face-embedder"> - <transformer-model url="my-url" secret-ref="myPrivateHubApiKey"/> - <tokenizer-model url="my-url" secret-ref="myPrivateHubApiKey"/> - </component> -</container>{% endhighlight %}</pre> - -<h3 id="huggingface-embedder-reference-config">Huggingface embedder reference config</h3> -<p>In addition to <a href="#embedder-onnx-reference-config">embedder ONNX parameters</a>:</p> -<table class="table"> - <thead> - <tr> - <th>Name</th> - <th>Occurrence</th> - <th>Description</th> - <th>Type</th> - <th>Default</th> - </tr> - </thead> - <tbody> - <tr> - <td>transformer-model</td> - <td>One</td> - <td>Use to point to the transformer ONNX model file</td> - <td><a href="#model-config-reference">model-type</a></td> - <td>N/A</td> - </tr> - <tr> - <td>tokenizer-model</td> - <td>One</td> - <td>Use to point to the <code>tokenizer.json</code> Huggingface tokenizer configuration file</td> - <td><a href="#model-config-reference">model-type</a></td> - <td>N/A</td> - </tr> - <tr> - <td>max-tokens</td> - <td>One</td> - <td>The maximum number of tokens accepted by the transformer model</td> - <td>numeric</td> - <td>512</td> - </tr> - <tr> - <td>transformer-input-ids</td> - <td>One</td> - <td>The name or identifier for the transformer input IDs</td> - <td>string</td> - <td>input_ids</td> - </tr> - <tr> - <td>transformer-attention-mask</td> - <td>One</td> - <td>The name or identifier for the transformer attention mask</td> - <td>string</td> - <td>attention_mask</td> - </tr> - <tr> - <td>transformer-token-type-ids</td> - <td>One</td> - <td>The name or identifier for the transformer token type IDs. If the model does not use <code>token_type_ids</code> use <code><transformer-token-type-ids/></code></td> - <td>string</td> - <td>token_type_ids</td> - </tr> - <tr> - <td>transformer-output</td> - <td>One</td> - <td>The name or identifier for the transformer output</td> - <td>string</td> - <td>last_hidden_state</td> - </tr> - <tr> - <td>pooling-strategy</td> - <td>One</td> - <td>How the output vectors of the ONNX model is pooled to obtain a single vector representation. Valid values are <code>mean</code>,<code>cls</code> and <code>none</code></td> - <td>string</td> - <td>mean</td> - </tr> - <tr> - <td>normalize</td> - <td>One</td> - <td>A boolean indicating whether to normalize the output embedding vector to unit length (length 1). Useful for <code>prenormalized-angular</code> - <a href="../schemas/schemas.html#distance-metric">distance-metric</a></td> - <td>boolean</td> - <td>false</td> - </tr> - <tr> - <td>prepend</td> - <td>Optional</td> - <td>Prepend instructions that are prepended to the text input before tokenization and inference. Useful - for models that have been trained with specific prompt instructions. The instructions are prepended to the input text. - <ul> - <li>Element <query> - Optional query prepend instruction.</li> - <li>Element <document> - Optional document prepend instruction.</li> - </ul> - <pre>{% highlight xml %} - <prepend> - <query>query:</query> - <document>passage:</document> - </prepend>{% endhighlight %}</pre> - </td> - <td>Optional <query> <document> elements.</td> - <td></td> - </tr> - </tbody> -</table> - - - -<h2 id="bert-embedder">Bert embedder</h2> -<p> - The Bert embedder is configured in <a href="../applications/services/services.html">services.xml</a>, - within the <code>container</code> tag: -</p> -<pre>{% highlight xml %} -<container version="1.0"> - <component id="myBert" type="bert-embedder"> - <transformer-model path="models/e5-small-v2.onnx"/> - <tokenizer-vocab url="https://huggingface.co/intfloat/e5-small-v2/raw/main/vocab.txt"/> - </component> -</container> -{% endhighlight %}</pre> - -<h3 id="bert-embedder-reference-config">Bert embedder reference config</h3> -<p>In addition to <a href="#embedder-onnx-reference-config">embedder ONNX parameters</a>:</p> -<table class="table"> - <thead> - <tr> - <th>Name</th> - <th>Occurrence</th> - <th>Description</th> - <th>Type</th> - <th>Default</th> - </tr> - </thead> - <tbody> - <tr> - <td>transformer-model</td> - <td>One</td> - <td>Use to point to the transformer ONNX model file</td> - <td><a href="#model-config-reference">model-type</a></td> - <td>N/A</td> - </tr> - <tr> - <td>tokenizer-vocab</td> - <td>One</td> - <td>Use to point to the Huggingface <code>vocab.txt</code> tokenizer file with valid wordpiece tokens. Does not support <code>tokenizer.json</code> format.</td> - <td><a href="#model-config-reference">model-type</a></td> - <td>N/A</td> - </tr> - <tr> - <td>max-tokens</td> - <td>One</td> - <td>The maximum number of tokens allowed in the input</td> - <td>integer</td> - <td>384</td> - </tr> - <tr> - <td>transformer-input-ids</td> - <td>One</td> - <td>The name or identifier for the transformer input IDs</td> - <td>string</td> - <td>input_ids</td> - </tr> - <tr> - <td>transformer-attention-mask</td> - <td>One</td> - <td>The name or identifier for the transformer attention mask</td> - <td>string</td> - <td>attention_mask</td> - </tr> - <tr> - <td>transformer-token-type-ids</td> - <td>One</td> - <td>The name or identifier for the transformer token type IDs. If the model does not use <code>token_type_ids</code> use <code><transformer-token-type-ids/></code></td> - <td>string</td> - <td>token_type_ids</td> - </tr> - <tr> - <td>transformer-output</td> - <td>One</td> - <td>The name or identifier for the transformer output</td> - <td>string</td> - <td>output_0</td> - </tr> - <tr> - <td>transformer-start-sequence-token</td> - <td>One</td> - <td>The start of sequence token</td> - <td>numeric</td> - <td>101</td> - </tr> - <tr> - <td>transformer-end-sequence-token</td> - <td>One</td> - <td>The start of sequence token</td> - <td>numeric</td> - <td>102</td> - </tr> - <tr> - <td>pooling-strategy</td> - <td>One</td> - <td>How the output vectors of the ONNX model is pooled to obtain a single vector representation. Valid values are <code>mean</code> and <code>cls</code></td> - <td>string</td> - <td>mean</td> - </tr> - </tbody> -</table> - -<h2 id="colbert-embedder">colbert embedder</h2> -<p> - The colbert embedder is configured in <a href="../applications/services/services.html">services.xml</a>, - within the <code>container</code> tag: -</p> -<pre>{% highlight xml %} -<container version="1.0"> - <component id="colbert" type="colbert-embedder"> - <transformer-model path="models/colbertv2.onnx"/> - <tokenizer-model url="https://huggingface.co/colbert-ir/colbertv2.0/raw/main/tokenizer.json"/> - <max-query-tokens>32</max-query-tokens> - <max-document-tokens>256</max-document-tokens> - </component> -</container> -{% endhighlight %}</pre> -<p>The Vespa colbert implementation works with default configurations for transformer models that use WordPiece tokenization. -</p> - -<h3 id="colbert-embedder-reference-config">colbert embedder reference config</h3> -<p>In addition to <a href="#embedder-onnx-reference-config">embedder ONNX parameters</a>:</p> -<table class="table"> - <thead> - <tr> - <th>Name</th> - <th>Occurrence</th> - <th>Description</th> - <th>Type</th> - <th>Default</th> - </tr> - </thead> - <tbody> - <tr> - <td>transformer-model</td> - <td>One</td> - <td>Use to point to the transformer ColBERT ONNX model file</td> - <td><a href="#model-config-reference">model-type</a></td> - <td>N/A</td> - </tr> - <tr> - <td>tokenizer-model</td> - <td>One</td> - <td>Use to point to the <code>tokenizer.json</code> Huggingface tokenizer configuration file</td> - <td><a href="#model-config-reference">model-type</a></td> - <td>N/A</td> - </tr> - <tr> - <td>max-tokens</td> - <td>One</td> - <td>Max length of token sequence the transformer-model can handle </td> - <td>numeric</td> - <td>512</td> - </tr> - <tr> - <td>max-query-tokens</td> - <td>One</td> - <td>The maximum number of ColBERT query token embeddings. Queries are padded to this length. Must be lower than max-tokens</td> - <td>numeric</td> - <td>32</td> - </tr> - <tr> - <td>max-document-tokens</td> - <td>One</td> - <td>The maximum number of ColBERT document token embeddings. Documents are not padded. Must be lower than max-tokens</td> - <td>numeric</td> - <td>512</td> - </tr> - <tr> - <td>transformer-input-ids</td> - <td>One</td> - <td>The name or identifier for the transformer input IDs</td> - <td>string</td> - <td>input_ids</td> - </tr> - <tr> - <td>transformer-attention-mask</td> - <td>One</td> - <td>The name or identifier for the transformer attention mask</td> - <td>string</td> - <td>attention_mask</td> - </tr> - <tr> - <td>transformer-mask-token</td> - <td>One</td> - <td>The mask token id used for ColBERT query padding</td> - <td>numeric</td> - <td>103</td> - </tr> - <tr> - <td>transformer-start-sequence-token</td> - <td>One</td> - <td>The start of sequence token id</td> - <td>numeric</td> - <td>101</td> - </tr> - <tr> - <td>transformer-end-sequence-token</td> - <td>One</td> - <td>The end of sequence token id</td> - <td>numeric</td> - <td>102</td> - </tr> - <tr> - <td>transformer-pad-token</td> - <td>One</td> - <td>The pad sequence token id</td> - <td>numeric</td> - <td>0</td> - </tr> - <tr> - <td>query-token-id</td> - <td>One</td> - <td>The colbert query token marker id</td> - <td>numeric</td> - <td>1</td> - </tr> - <tr> - <td>document-token-id</td> - <td>One</td> - <td>The colbert document token marker id</td> - <td>numeric</td> - <td>2</td> - </tr> - <tr> - <td>transformer-output</td> - <td>One</td> - <td>The name or identifier for the transformer output</td> - <td>string</td> - <td>contextual</td> - </tr> - </tbody> -</table> -<p>The Vespa colbert-embedder uses <code>[unused0]</code>token id 1 for <code>query-token-id</code>, and <code>[unused1]</code>, - token id 2 for <code> document-token-id</code>document marker. Document punctuation chars are filtered (not configurable). - The following characters are removed <code>!"#$%&'()*+,-./:;<=>?@[\]^_`{|}~</code>. -</p> - - -<h3 id="splade-embedder-reference-config">splade embedder reference config</h3> -<p>In addition to <a href="#embedder-onnx-reference-config">embedder ONNX parameters</a>:</p> -<table class="table"> - <thead> - <tr> - <th>Name</th> - <th>Occurrence</th> - <th>Description</th> - <th>Type</th> - <th>Default</th> - </tr> - </thead> - <tbody> - <tr> - <td>transformer-model</td> - <td>One</td> - <td>Use to point to the transformer ONNX model file</td> - <td><a href="#model-config-reference">model-type</a></td> - <td>N/A</td> - </tr> - <tr> - <td>tokenizer-model</td> - <td>One</td> - <td>Use to point to the <code>tokenizer.json</code> Huggingface tokenizer configuration file</td> - <td><a href="#model-config-reference">model-type</a></td> - <td>N/A</td> - </tr> - <tr> - <td>term-score-threshold</td> - <td>One</td> - <td>An optional threshold to increase sparseness, tokens/terms with a score lower than this is not retained.</td> - <td>numeric</td> - <td>N/A</td> - </tr> - <tr> - <td>max-tokens</td> - <td>One</td> - <td>The maximum number of tokens accepted by the transformer model</td> - <td>numeric</td> - <td>512</td> - </tr> - <tr> - <td>transformer-input-ids</td> - <td>One</td> - <td>The name or identifier for the transformer input IDs</td> - <td>string</td> - <td>input_ids</td> - </tr> - <tr> - <td>transformer-attention-mask</td> - <td>One</td> - <td>The name or identifier for the transformer attention mask</td> - <td>string</td> - <td>attention_mask</td> - </tr> - <tr> - <td>transformer-token-type-ids</td> - <td>One</td> - <td>The name or identifier for the transformer token type IDs. If the model does not use <code>token_type_ids</code> use <code><transformer-token-type-ids/></code></td> - <td>string</td> - <td>token_type_ids</td> - </tr> - <tr> - <td>transformer-output</td> - <td>One</td> - <td>The name or identifier for the transformer output</td> - <td>string</td> - <td>logits</td> - </tr> - </tbody> -</table> - - - -{% include chip-cloud.html %} -<h2 id="voyageai-embedder">VoyageAI Embedder</h2> -<p> - An embedder that uses the <a href="https://www.voyageai.com/">VoyageAI</a> API - to generate embeddings. -</p> -<p> - The VoyageAI embedder is configured in <a href="../applications/services/services.html">services.xml</a>, - within the <code>container</code> tag: -</p> -<pre>{% highlight xml %} -<container id="default" version="1.0"> - <component id="voyage" type="voyage-ai-embedder"> - <model>voyage-law-2</model> - <api-key-secret-ref>voyage_api_key</api-key-secret-ref> - <dimensions>1024</dimensions> - <endpoint>https://api.voyageai.com/v1/embeddings</endpoint> - <truncate>true</truncate> - <batching max-size="16" max-delay="200ms"/> - </component> -</container> -{% endhighlight %}</pre> - -<h3 id="voyageai-embedder-reference-config">VoyageAI embedder reference config</h3> -<table class="table"> - <thead> - <tr> - <th>Name</th> - <th>Occurrence</th> - <th>Description</th> - <th>Type</th> - <th>Default</th> - </tr> - </thead> - <tbody> - <tr> - <td>model</td> - <td>One</td> - <td><strong>Required</strong>. The VoyageAI model to use. See the <a href="https://docs.voyageai.com/docs/embeddings">VoyageAI embeddings documentation</a> for the complete list of available models including general-purpose, specialized, <a href="https://docs.voyageai.com/docs/contextualized-chunk-embeddings">contextualized</a>, and <a href="https://docs.voyageai.com/docs/multimodal-embeddings">multimodal</a> models. - </td> - <td>string</td> - <td>N/A</td> - </tr> - <tr> - <td>dimensions</td> - <td>One</td> - <td><strong>Required</strong>. The number of dimensions for the output embedding vectors. Must match the tensor field definition in your schema. Valid values are <code>256</code>, <code>512</code>, <code>1024</code>, <code>1536</code>, or <code>2048</code>. See the <a href="https://docs.voyageai.com/docs/embeddings">VoyageAI embeddings documentation</a> for model-specific dimension support.</td> - <td>integer</td> - <td>N/A</td> - </tr> - <tr> - <td>api-key-secret-ref</td> - <td>One</td> - <td><strong>Required</strong>. Reference to the secret in Vespa's <a href="/en/cloud/security/secret-store.html">secret store</a> containing the VoyageAI API key.</td> - <td>string</td> - <td>N/A</td> - </tr> - <tr> - <td>endpoint</td> - <td>Optional</td> - <td>VoyageAI API endpoint URL.</td> - <td>string</td> - <td>https://api.voyageai.com/v1/embeddings</td> - </tr> - <tr> - <td>truncate</td> - <td>Optional</td> - <td>Whether to truncate input text exceeding model limits. When enabled, text is automatically truncated. When disabled, requests with too-long text will fail.</td> - <td>boolean</td> - <td>true</td> - </tr> - <tr> - <td>quantization</td> - <td>Optional</td> - <td>Output quantization format for embedding vectors. Valid values are <code>auto</code>, <code>float</code>, - <code>int8</code>, or <code>binary</code>. When set to <code>auto</code>, - the embedder infers the appropriate quantization from the dimensions and cell type of the destination tensor in your schema. - The <code>float</code> value also applies to <code>bfloat16</code> destination tensors. - When using <code>binary</code> quantization, the destination tensor field must use <code>int8</code> cell type - with 1/8 of the dimensions specified in the embedder configuration (e.g., 1024 dimensions → <code>tensor<int8>(x[128])</code>). - See the <a href="https://docs.voyageai.com/docs/flexible-dimensions-and-quantization#quantization">VoyageAI quantization documentation</a> - for details on quantization options and <a href="../../rag/binarizing-vectors.html">binarizing vectors</a> for more on binary quantization in Vespa.</td> - <td>string</td> - <td>auto</td> - </tr> - <tr> - <td>batching</td> - <td>Optional</td> - <td>Enables dynamic batching of concurrent embedding requests into single VoyageAI API calls. - When enabled, the embedder collects concurrent requests and sends them as a single batch, - reducing the number of API calls and improving throughput. - <ul> - <li><code>max-size</code> — Maximum number of requests to include in a single batch.</li> - <li><code>max-delay</code> — Maximum time to wait for a full batch before sending a partial one (e.g., <code>200ms</code>).</li> - </ul> - </td> - <td>element</td> - <td>disabled</td> - </tr> - </tbody> -</table> - -<h2 id="openai-embedder">OpenAI Embedder</h2> -<p>Available since {% include version.html version="8.678" %}</p> -<p> - An embedder that uses the <a href="https://platform.openai.com/docs/guides/embeddings">OpenAI</a> embeddings API - to generate embeddings. -</p> -<p> - The OpenAI embedder is configured in <a href="../applications/services/services.html">services.xml</a>, - within the <code>container</code> tag: -</p> -<pre>{% highlight xml %} -<container id="default" version="1.0"> - <component id="openai" type="openai-embedder"> - <model>text-embedding-3-small</model> - <api-key-secret-ref>openai_api_key</api-key-secret-ref> - <dimensions>1536</dimensions> - <endpoint>https://api.openai.com/v1/embeddings</endpoint> - </component> -</container> -{% endhighlight %}</pre> - -<h3 id="openai-embedder-reference-config">OpenAI embedder reference config</h3> -<table class="table"> - <thead> - <tr> - <th>Name</th> - <th>Occurrence</th> - <th>Description</th> - <th>Type</th> - <th>Default</th> - </tr> - </thead> - <tbody> - <tr> - <td>model</td> - <td>One</td> - <td><strong>Required</strong>. The OpenAI model to use, for example <code>text-embedding-3-small</code> or - <code>text-embedding-3-large</code>. See the - <a href="https://platform.openai.com/docs/guides/embeddings">OpenAI embeddings documentation</a> - for the complete list of available models.</td> - <td>string</td> - <td>N/A</td> - </tr> - <tr> - <td>dimensions</td> - <td>One</td> - <td><strong>Required</strong>. The number of dimensions for the output embedding vectors. Must match the - tensor field definition in your schema. The destination tensor field must use <code>float</code> or - <code>bfloat16</code> cell type — the OpenAI API does not support quantization.</td> - <td>integer</td> - <td>N/A</td> - </tr> - <tr> - <td>api-key-secret-ref</td> - <td>Optional</td> - <td>Reference to the secret in Vespa's <a href="/en/cloud/security/secret-store.html">secret store</a> - containing the OpenAI API key. When unset, requests are sent without an <code>Authorization</code> header.</td> - <td>string</td> - <td>"" (no auth)</td> - </tr> - <tr> - <td>endpoint</td> - <td>Optional</td> - <td>OpenAI API endpoint URL. Set this to target a specific OpenAI-compatible API.</td> - <td>string</td> - <td>https://api.openai.com/v1/embeddings</td> - </tr> - <tr> - <td>batching</td> - <td>Optional</td> - <td>Enables dynamic batching of concurrent embedding requests into single OpenAI API calls. - When enabled, the embedder collects concurrent requests and sends them as a single batch, - reducing the number of API calls and improving throughput. - <ul> - <li><code>max-size</code> — Maximum number of requests to include in a single batch.</li> - <li><code>max-delay</code> — Maximum time to wait for a full batch before sending a partial one (e.g., <code>200ms</code>).</li> - </ul> - </td> - <td>element</td> - <td>disabled</td> - </tr> - <tr> - <td>prepend</td> - <td>Optional</td> - <td>Strings prepended to the text input before sending the embedding request. Useful for - OpenAI-compatible instruction-tuned models that expect a task-specific prefix. - <ul> - <li>Element <query> - Optional query prepend instruction.</li> - <li>Element <document> - Optional document prepend instruction.</li> - </ul> - <pre>{% highlight xml %} - <prepend> - <query>query: </query> - <document>passage: </document> - </prepend>{% endhighlight %}</pre> - </td> - <td>Optional <query> <document> elements.</td> - <td></td> - </tr> - </tbody> -</table> - -<h2 id="mistral-embedder">Mistral Embedder</h2> -<p>Available since {% include version.html version="8.678" %}</p> -<p> - An embedder that uses the <a href="https://docs.mistral.ai/capabilities/embeddings/overview/">Mistral</a> - embeddings API to generate embeddings. -</p> -<p> - The Mistral embedder is configured in <a href="../applications/services/services.html">services.xml</a>, - within the <code>container</code> tag: -</p> -<pre>{% highlight xml %} -<container id="default" version="1.0"> - <component id="mistral" type="mistral-embedder"> - <model>mistral-embed</model> - <api-key-secret-ref>mistral_api_key</api-key-secret-ref> - <dimensions>1024</dimensions> - <quantization>auto</quantization> - </component> -</container> -{% endhighlight %}</pre> - -<h3 id="mistral-embedder-reference-config">Mistral embedder reference config</h3> -<table class="table"> - <thead> - <tr> - <th>Name</th> - <th>Occurrence</th> - <th>Description</th> - <th>Type</th> - <th>Default</th> - </tr> - </thead> - <tbody> - <tr> - <td>model</td> - <td>One</td> - <td><strong>Required</strong>. The Mistral model to use, for example <code>mistral-embed</code> or - <code>codestral-embed</code>. See the - <a href="https://docs.mistral.ai/capabilities/embeddings/overview/">Mistral embeddings documentation</a> - for the complete list of available models.</td> - <td>string</td> - <td>N/A</td> - </tr> - <tr> - <td>api-key-secret-ref</td> - <td>One</td> - <td><strong>Required</strong>. Reference to the secret in Vespa's - <a href="/en/cloud/security/secret-store.html">secret store</a> containing the Mistral API key.</td> - <td>string</td> - <td>N/A</td> - </tr> - <tr> - <td>dimensions</td> - <td>One</td> - <td><strong>Required</strong>. The number of dimensions for the output embedding vectors. Must match the - tensor field definition in your schema. See the - <a href="https://docs.mistral.ai/capabilities/embeddings/overview/">Mistral embeddings documentation</a> - for model-specific dimension support.</td> - <td>integer</td> - <td>N/A</td> - </tr> - <tr> - <td>quantization</td> - <td>Optional</td> - <td>Output quantization format for embedding vectors. Valid values are <code>auto</code>, <code>float</code>, - <code>int8</code>, or <code>binary</code>. See the <code>quantization</code> row of the - <a href="#voyageai-embedder-reference-config">VoyageAI embedder reference config</a> - for details on <code>auto</code> resolution and the destination tensor layout required for <code>int8</code> - and <code>binary</code>. Note that not all Mistral models support <code>int8</code> and <code>binary</code> - quantization — see the - <a href="https://docs.mistral.ai/capabilities/embeddings/overview/">Mistral embeddings documentation</a> - for per-model support.</td> - <td>string</td> - <td>auto</td> - </tr> - <tr> - <td>batching</td> - <td>Optional</td> - <td>Enables dynamic batching of concurrent embedding requests into single Mistral API calls. - When enabled, the embedder collects concurrent requests and sends them as a single batch, - reducing the number of API calls and improving throughput. - <ul> - <li><code>max-size</code> — Maximum number of requests to include in a single batch.</li> - <li><code>max-delay</code> — Maximum time to wait for a full batch before sending a partial one (e.g., <code>200ms</code>).</li> - </ul> - </td> - <td>element</td> - <td>disabled</td> - </tr> - </tbody> -</table> - -<h2 id="huggingface-tokenizer-embedder">Huggingface tokenizer embedder</h2> - <p> - The Huggingface tokenizer embedder is configured in <a href="../applications/services/services.html">services.xml</a>, - within the <code>container</code> tag: - </p> -<pre>{% highlight xml %} - <container version="1.0"> - <component id="tokenizer" type="hugging-face-tokenizer"> - <model url="https://huggingface.co/bert-base-uncased/raw/main/tokenizer.json"/> - </component> - </container> -{% endhighlight %}</pre> - -<h3 id="huggingface-tokenizer-reference-config">Huggingface tokenizer reference config</h3> -<table class="table"> - <thead> - <tr> - <th>Name</th> - <th>Occurrence</th> - <th>Description</th> - <th>Type</th> - <th>Default</th> - </tr> - </thead> - <tbody> - <tr> - <td>model</td> - <td>One To Many</td> - <td>Use to point to the <code>tokenizer.json</code> Huggingface tokenizer configuration file. - Also supports <code>language</code>, which is only relevant if one wants to tokenize differently based on the document language. - Use "unknown" for a model to be used for any language (i.e. by default).</td> - <td><a href="#model-config-reference">model-type</a></td> - <td>N/A</td> - </tr> - </tbody> -</table> - - -<h2 id="embedder-onnx-reference-config">Embedder ONNX reference config</h2> -<p> - Vespa uses <a href="https://onnxruntime.ai/">ONNX Runtime</a> to accelerate inference of embedding models. - These parameters are valid for both <a href="#bert-embedder">Bert embedder</a> and - <a href="#huggingface-embedder">Huggingface embedder</a>. -</p> -<table class="table"> - <thead> - <tr> - <th>Name</th> - <th>Occurrence</th> - <th>Description</th> - <th>Type</th> - <th>Default</th> - </tr> - </thead> - <tbody> - <tr> - <td>onnx-execution-mode</td> - <td>One</td> - <td>Low level ONNX execution model. Valid values are <code>parallel</code> or <code>sequential</code>. - Only relevant for inference on CPU. - See <a href="https://onnxruntime.ai/docs/performance/tune-performance/threading.html">ONNX runtime documentation</a> on threading. - </td> - <td>string</td> - <td>sequential</td> - </tr> - <tr> - <td>onnx-interop-threads</td> - <td>One</td> - <td>Low level ONNX setting.Only relevant for inference on CPU.</td> - <td>numeric</td> - <td>1</td> - </tr> - <tr> - <td>onnx-intraop-threads</td> - <td>One</td> - <td>Low level ONNX setting. Only relevant for inference on CPU.</td> - <td>numeric</td> - <td>4</td> - </tr> - <tr> - <td>onnx-gpu-device</td> - <td>One</td> - <td>The GPU device to run the model on. - See <a href="/en/operations/self-managed/vespa-gpu-container.html">configuring GPU for Vespa container image</a>. Use <code>-1</code> to not use GPU for the model, even - if the instance has available GPUs. - </td> - <td>numeric</td> - <td>0</td> - </tr> - </tbody> -</table> - -<h2 id="sentencepiece-embedder">SentencePiece embedder</h2> - <p> - A native Java implementation of <a href="https://github.com/google/sentencepiece">SentencePiece</a>. - SentencePiece breaks text into chunks independent of spaces, - which is robust to misspellings and works with CJK languages. - Prefer the <a href="#huggingface-tokenizer-embedder">Huggingface tokenizer embedder</a> over this - for better compatibility with Huggingface models. - </p> - <p> - This is suitable to use in conjunction with <a href="../../applications/components.html">custom components</a>, - or the resulting tensor can be used in <a href="../../basics/ranking.html">ranking</a>. - </p> - <p> - To use the - <a href="https://github.com/vespa-engine/vespa/blob/master/linguistics-components/src/main/java/com/yahoo/language/sentencepiece/SentencePieceEmbedder.java"> - SentencePiece embedder</a>, add it to <a href="../applications/services/services.html">services.xml</a>: - </p> - <pre>{% highlight xml %} - <container version="1.0"> - <component id="mySentencePiece" - class="com.yahoo.language.sentencepiece.SentencePieceEmbedder" - bundle="linguistics-components"> - <config name="language.sentencepiece.sentence-piece">; - <model> - <item> - <language>unknown</language> - <path>model/en.wiki.bpe.vs10000.model</path> - </item> - </model> - </config> - </component> - </container> - {% endhighlight %}</pre> - <p> - See the options available for configuring SentencePiece in - <a href="https://github.com/vespa-engine/vespa/blob/master/linguistics-components/src/main/resources/configdefinitions/language.sentencepiece.sentence-piece.def"> - the full configuration definition</a>. - </p> - - - <h2 id="wordpiece-embedder">WordPiece embedder</h2> - <p> - A native Java implementation of - <a href="https://github.com/google-research/bert#tokenization">WordPiece</a>, - which is commonly used with BERT models. - Prefer the <a href="#huggingface-tokenizer-embedder">Huggingface tokenizer embedder</a> - over this for better compatibility with Huggingface models. - </p> - <p> - This is suitable to use in conjunction with <a href="../../applications/components.html">custom components</a>, - or the resulting tensor can be used in <a href="../../basics/ranking.html">ranking</a>. - </p> - <p> - To use the - <a href="https://github.com/vespa-engine/vespa/blob/master/linguistics-components/src/main/java/com/yahoo/language/wordpiece/WordPieceEmbedder.java"> - WordPiece embedder</a>, - add it to <a href="../applications/services/services.html">services.xml</a> within the <code>container</code> tag: - </p> - <pre>{% highlight xml %} - <container version="1.0"> - <component id="myWordPiece"> - class="com.yahoo.language.wordpiece.WordPieceEmbedder" - bundle="linguistics-components"> - <config name="language.wordpiece.word-piece"> - <model> - <item> - <language>unknown</language> - <path>models/bert-base-uncased-vocab.txt</path> - </item> - </model> - </config> - </component> - </container> - {% endhighlight %}</pre> - <p> - See the options available for configuring WordPiece in - <a href="https://github.com/vespa-engine/vespa/blob/master/linguistics-components/src/main/resources/configdefinitions/language.wordpiece.word-piece.def"> - the full configuration definition</a>. - </p> - <p> - WordPiece is suitable to use in conjunction with custom components, - or the resulting tensor can be used in <a href="../../basics/ranking.html">ranking</a>. - </p> - -<h2 id="using-an-embedder-from-java">Using an embedder from Java</h2> -<p> - When writing custom Java components (such as <a href="../../applications/searchers.html">Searchers</a> - or <a href="../../applications/document-processors.html#document-processors">Document processors</a>), - use embedders you have configured by - <a href="../../applications/dependency-injection.html">having them injected in the constructor</a>, - just as any other component: -</p> -<pre>{% highlight java %} -class MyComponent { - @Inject - public MyComponent(ComponentRegistry<Embedder> embedders) { - // embedders contains all the embedders configured in your services.xml - } -} -{% endhighlight %}</pre> -<p>See a concrete example of using an embedder in a custom searcher in -<a href="https://github.com/vespa-cloud/vespa-documentation-search/blob/main/src/main/java/ai/vespa/cloud/docsearch/LLMSearcher.java">LLMSearcher</a>. -</p> - -<h2 id="custom-embedders">Custom Embedders</h2> -<p>Vespa provides a Java interface for defining components which can provide embeddings of text: -<a href="https://github.com/vespa-engine/vespa/blob/master/linguistics/src/main/java/com/yahoo/language/process/Embedder.java"> -com.yahoo.language.process.Embedder</a>.</p> - -<p> - To define a custom embedder in an application and make it usable by Vespa - (see <a href="../../rag/embedding.html#embedding-a-query-text">embedding a query text</a>), - implement this interface and add it as a <a href="../../applications/developer-guide.html#developing-components">component</a> to - <a href="../applications/services/container.html">services.xml</a>: -</p> -<pre>{% highlight xml %} -<container version="1.0"> - <component id="myEmbedder" - class="com.example.MyEmbedder" - bundle="the name in artifactId in pom.xml"> - <config name='com.example.my-embedder'> - <model model-id="minilm-l6-v2"/> - <vocab path="files/vocab.txt"/> - <myValue>foo</myValue> - </config> - </component> -</container> -{% endhighlight %}</pre> diff --git a/en/reference/rag/embedding.mdx b/en/reference/rag/embedding.mdx new file mode 100644 index 0000000000..dfc042f517 --- /dev/null +++ b/en/reference/rag/embedding.mdx @@ -0,0 +1,856 @@ +--- +title: "Embedding Reference" +sidebarTitle: "Embedding" +--- +Reference configuration for [embedders](/en/rag/embedding). + +## Model config reference + +Embedder models use the [model](/en/reference/applications/config-files#model) type configuration which accepts the attributes `model-id`, `url` or `path`. Multiple of these can be specified as a single config value, where one is used depending on the deployment environment: + +- If a `model-id` is specified and the application is deployed on Vespa Cloud, the `model-id` is used. +- Otherwise, if a `url` is specified, it is used +- Otherwise, `path` is used. + +When using `path`, the model files must be supplied in the application package. + +## Huggingface Embedder + +An embedder using any [Huggingface tokenizer](https://huggingface.co/docs/tokenizers/index), including multilingual tokenizers, to produce tokens which is then input to a supplied transformer model in ONNX model format. + +The Huggingface embedder is configured in [services.xml](/en/reference/applications/services/services), within the `container` tag: + +```xml +<container id="default" version="1.0"> + <component id="hf-embedder" type="hugging-face-embedder"> + <transformer-model path="my-models/model.onnx"/> + <tokenizer-model path="my-models/tokenizer.json"/> + <prepend> + <query>query:</query> + <document>passage:</document> + </prepend> + </component> + ... +</container> +``` + +### Private Model Hub + +You may also use models hosted in a [private Huggingface model hub](https://huggingface.co/docs/hub/en/repositories-settings#private-repositories). + +Retrieve an API key from Huggingface with the appropriate permissions, and add it to the [vespa secret store.](/en/security/secret-store) Add the secret to the container `<secrets>` and refer to it in your Huggingface model configuration: + +```xml +<container id="default" version="1.0"> + <secrets> + <myPrivateHubApiKey vault="my-vault" name="my-secret-name" /> + </secrets> + <component id="hf-embedder" type="hugging-face-embedder"> + <transformer-model url="my-url" secret-ref="myPrivateHubApiKey"/> + <tokenizer-model url="my-url" secret-ref="myPrivateHubApiKey"/> + </component> +</container> +``` + +### Huggingface embedder reference config + +In addition to [embedder ONNX parameters](#embedder-onnx-reference-config): + +<table className="vespa-table"> + <thead> + <tr> + <th>Name</th> + <th>Occurrence</th> + <th>Description</th> + <th>Type</th> + <th>Default</th> + </tr> + </thead> + <tbody> + <tr> + <td>transformer-model</td> + <td>One</td> + <td>Use to point to the transformer ONNX model file</td> + <td><a href="#model-config-reference" aria-label="model-type">model-type</a></td> + <td>N/A</td> + </tr> + <tr> + <td>tokenizer-model</td> + <td>One</td> + <td>Use to point to the <code>{`tokenizer.json`}</code> Huggingface tokenizer configuration file</td> + <td><a href="#model-config-reference" aria-label="model-type">model-type</a></td> + <td>N/A</td> + </tr> + <tr> + <td>max-tokens</td> + <td>One</td> + <td>The maximum number of tokens accepted by the transformer model</td> + <td>numeric</td> + <td>512</td> + </tr> + <tr> + <td>transformer-input-ids</td> + <td>One</td> + <td>The name or identifier for the transformer input IDs</td> + <td>string</td> + <td>input_ids</td> + </tr> + <tr> + <td>transformer-attention-mask</td> + <td>One</td> + <td>The name or identifier for the transformer attention mask</td> + <td>string</td> + <td>attention_mask</td> + </tr> + <tr> + <td>transformer-token-type-ids</td> + <td>One</td> + <td>The name or identifier for the transformer token type IDs. If the model does not use <code>{`token_type_ids`}</code> use <code>{`<transformer-token-type-ids/>`}</code></td> + <td>string</td> + <td>token_type_ids</td> + </tr> + <tr> + <td>transformer-output</td> + <td>One</td> + <td>The name or identifier for the transformer output</td> + <td>string</td> + <td>last_hidden_state</td> + </tr> + <tr> + <td>pooling-strategy</td> + <td>One</td> + <td>How the output vectors of the ONNX model is pooled to obtain a single vector representation. Valid values are <code>{`mean`}</code>,<code>{`cls`}</code> and <code>{`none`}</code></td> + <td>string</td> + <td>mean</td> + </tr> + <tr> + <td>normalize</td> + <td>One</td> + <td>A boolean indicating whether to normalize the output embedding vector to unit length (length 1). Useful for <code>{`prenormalized-angular`}</code> <a href="/en/reference/schemas/schemas#distance-metric" aria-label="distance-metric">distance-metric</a></td> + <td>boolean</td> + <td>false</td> + </tr> + <tr> + <td>prepend</td> + <td>Optional</td> + <td>Prepend instructions that are prepended to the text input before tokenization and inference. Useful for models that have been trained with specific prompt instructions. The instructions are prepended to the input text.<br/><br/> • Element <code>{`<query>`}</code> - Optional query prepend instruction.<br/> • Element <code>{`<document>`}</code> - Optional document prepend instruction. <br/><br/><code>{`<prepend>`}</code><br/> <code>{`<query>query:</query>`}</code><br/> <code>{`<document>passage:</document>`}</code> <br/><code>{`</prepend>`}</code></td> + <td>Optional <code>{`<query> <document>`}</code> elements.</td> + <td></td> + </tr> + </tbody> +</table> + +## Bert embedder + +The Bert embedder is configured in [services.xml](/en/reference/applications/services/services), within the `container` tag: + +```xml +<container version="1.0"> + <component id="myBert" type="bert-embedder"> + <transformer-model path="models/e5-small-v2.onnx"/> + <tokenizer-vocab url="https://huggingface.co/intfloat/e5-small-v2/raw/main/vocab.txt"/> + </component> +</container> +``` + +### Bert embedder reference config + +In addition to [embedder ONNX parameters](#embedder-onnx-reference-config): + +<table className="vespa-table"> + <thead> + <tr> + <th>Name</th> + <th>Occurrence</th> + <th>Description</th> + <th>Type</th> + <th>Default</th> + </tr> + </thead> + <tbody> + <tr> + <td>transformer-model</td> + <td>One</td> + <td>Use to point to the transformer ONNX model file</td> + <td><a href="#model-config-reference" aria-label="model-type">model-type</a></td> + <td>N/A</td> + </tr> + <tr> + <td>tokenizer-vocab</td> + <td>One</td> + <td>Use to point to the Huggingface <code>{`vocab.txt`}</code> tokenizer file with valid wordpiece tokens. Does not support <code>{`tokenizer.json`}</code> format.</td> + <td><a href="#model-config-reference" aria-label="model-type">model-type</a></td> + <td>N/A</td> + </tr> + <tr> + <td>max-tokens</td> + <td>One</td> + <td>The maximum number of tokens allowed in the input</td> + <td>integer</td> + <td>384</td> + </tr> + <tr> + <td>transformer-input-ids</td> + <td>One</td> + <td>The name or identifier for the transformer input IDs</td> + <td>string</td> + <td>input_ids</td> + </tr> + <tr> + <td>transformer-attention-mask</td> + <td>One</td> + <td>The name or identifier for the transformer attention mask</td> + <td>string</td> + <td>attention_mask</td> + </tr> + <tr> + <td>transformer-token-type-ids</td> + <td>One</td> + <td>The name or identifier for the transformer token type IDs. If the model does not use <code>{`token_type_ids`}</code> use <code>{`<transformer-token-type-ids/>`}</code></td> + <td>string</td> + <td>token_type_ids</td> + </tr> + <tr> + <td>transformer-output</td> + <td>One</td> + <td>The name or identifier for the transformer output</td> + <td>string</td> + <td>output_0</td> + </tr> + <tr> + <td>transformer-start-sequence-token</td> + <td>One</td> + <td>The start of sequence token</td> + <td>numeric</td> + <td>101</td> + </tr> + <tr> + <td>transformer-end-sequence-token</td> + <td>One</td> + <td>The start of sequence token</td> + <td>numeric</td> + <td>102</td> + </tr> + <tr> + <td>pooling-strategy</td> + <td>One</td> + <td>How the output vectors of the ONNX model is pooled to obtain a single vector representation. Valid values are <code>{`mean`}</code> and <code>{`cls`}</code></td> + <td>string</td> + <td>mean</td> + </tr> + </tbody> +</table> + +## colbert embedder + +The colbert embedder is configured in [services.xml](/en/reference/applications/services/services), within the `container` tag: + +```xml +<container version="1.0"> + <component id="colbert" type="colbert-embedder"> + <transformer-model path="models/colbertv2.onnx"/> + <tokenizer-model url="https://huggingface.co/colbert-ir/colbertv2.0/raw/main/tokenizer.json"/> + <max-query-tokens>32</max-query-tokens> + <max-document-tokens>256</max-document-tokens> + </component> +</container> +``` + +The Vespa colbert implementation works with default configurations for transformer models that use WordPiece tokenization. + +### colbert embedder reference config + +In addition to [embedder ONNX parameters](#embedder-onnx-reference-config): + +<table className="vespa-table"> + <thead> + <tr> + <th>Name</th> + <th>Occurrence</th> + <th>Description</th> + <th>Type</th> + <th>Default</th> + </tr> + </thead> + <tbody> + <tr> + <td>transformer-model</td> + <td>One</td> + <td>Use to point to the transformer ColBERT ONNX model file</td> + <td><a href="#model-config-reference" aria-label="model-type">model-type</a></td> + <td>N/A</td> + </tr> + <tr> + <td>tokenizer-model</td> + <td>One</td> + <td>Use to point to the <code>{`tokenizer.json`}</code> Huggingface tokenizer configuration file</td> + <td><a href="#model-config-reference" aria-label="model-type">model-type</a></td> + <td>N/A</td> + </tr> + <tr> + <td>max-tokens</td> + <td>One</td> + <td>Max length of token sequence the transformer-model can handle</td> + <td>numeric</td> + <td>512</td> + </tr> + <tr> + <td>max-query-tokens</td> + <td>One</td> + <td>The maximum number of ColBERT query token embeddings. Queries are padded to this length. Must be lower than max-tokens</td> + <td>numeric</td> + <td>32</td> + </tr> + <tr> + <td>max-document-tokens</td> + <td>One</td> + <td>The maximum number of ColBERT document token embeddings. Documents are not padded. Must be lower than max-tokens</td> + <td>numeric</td> + <td>512</td> + </tr> + <tr> + <td>transformer-input-ids</td> + <td>One</td> + <td>The name or identifier for the transformer input IDs</td> + <td>string</td> + <td>input_ids</td> + </tr> + <tr> + <td>transformer-attention-mask</td> + <td>One</td> + <td>The name or identifier for the transformer attention mask</td> + <td>string</td> + <td>attention_mask</td> + </tr> + <tr> + <td>transformer-mask-token</td> + <td>One</td> + <td>The mask token id used for ColBERT query padding</td> + <td>numeric</td> + <td>103</td> + </tr> + <tr> + <td>transformer-start-sequence-token</td> + <td>One</td> + <td>The start of sequence token id</td> + <td>numeric</td> + <td>101</td> + </tr> + <tr> + <td>transformer-end-sequence-token</td> + <td>One</td> + <td>The end of sequence token id</td> + <td>numeric</td> + <td>102</td> + </tr> + <tr> + <td>transformer-pad-token</td> + <td>One</td> + <td>The pad sequence token id</td> + <td>numeric</td> + <td>0</td> + </tr> + <tr> + <td>query-token-id</td> + <td>One</td> + <td>The colbert query token marker id</td> + <td>numeric</td> + <td>1</td> + </tr> + <tr> + <td>document-token-id</td> + <td>One</td> + <td>The colbert document token marker id</td> + <td>numeric</td> + <td>2</td> + </tr> + <tr> + <td>transformer-output</td> + <td>One</td> + <td>The name or identifier for the transformer output</td> + <td>string</td> + <td>contextual</td> + </tr> + </tbody> +</table> + +The Vespa colbert-embedder uses `[unused0]`token id 1 for `query-token-id`, and `[unused1]`, token id 2 for ` document-token-id`document marker. Document punctuation chars are filtered (not configurable). The following characters are removed ``!"#$%&'()*+,-./:;<=>?@[\]^_`{|}~``. + +### splade embedder reference config + +In addition to [embedder ONNX parameters](#embedder-onnx-reference-config): + +<table className="vespa-table"> + <thead> + <tr> + <th>Name</th> + <th>Occurrence</th> + <th>Description</th> + <th>Type</th> + <th>Default</th> + </tr> + </thead> + <tbody> + <tr> + <td>transformer-model</td> + <td>One</td> + <td>Use to point to the transformer ONNX model file</td> + <td><a href="#model-config-reference" aria-label="model-type">model-type</a></td> + <td>N/A</td> + </tr> + <tr> + <td>tokenizer-model</td> + <td>One</td> + <td>Use to point to the <code>{`tokenizer.json`}</code> Huggingface tokenizer configuration file</td> + <td><a href="#model-config-reference" aria-label="model-type">model-type</a></td> + <td>N/A</td> + </tr> + <tr> + <td>term-score-threshold</td> + <td>One</td> + <td>An optional threshold to increase sparseness, tokens/terms with a score lower than this is not retained.</td> + <td>numeric</td> + <td>N/A</td> + </tr> + <tr> + <td>max-tokens</td> + <td>One</td> + <td>The maximum number of tokens accepted by the transformer model</td> + <td>numeric</td> + <td>512</td> + </tr> + <tr> + <td>transformer-input-ids</td> + <td>One</td> + <td>The name or identifier for the transformer input IDs</td> + <td>string</td> + <td>input_ids</td> + </tr> + <tr> + <td>transformer-attention-mask</td> + <td>One</td> + <td>The name or identifier for the transformer attention mask</td> + <td>string</td> + <td>attention_mask</td> + </tr> + <tr> + <td>transformer-token-type-ids</td> + <td>One</td> + <td>The name or identifier for the transformer token type IDs. If the model does not use <code>{`token_type_ids`}</code> use <code>{`<transformer-token-type-ids/>`}</code></td> + <td>string</td> + <td>token_type_ids</td> + </tr> + <tr> + <td>transformer-output</td> + <td>One</td> + <td>The name or identifier for the transformer output</td> + <td>string</td> + <td>logits</td> + </tr> + </tbody> +</table> + +<Tooltip tip="This content is applicable to Vespa Cloud deployments.">`Vespa Cloud`</Tooltip> + +## VoyageAI Embedder + +An embedder that uses the [VoyageAI](https://www.voyageai.com/) API to generate embeddings. + +The VoyageAI embedder is configured in [services.xml](/en/reference/applications/services/services), within the `container` tag: + +```xml +<container id="default" version="1.0"> + <component id="voyage" type="voyage-ai-embedder"> + <model>voyage-law-2</model> + <api-key-secret-ref>voyage_api_key</api-key-secret-ref> + <dimensions>1024</dimensions> + <endpoint>https://api.voyageai.com/v1/embeddings</endpoint> + <truncate>true</truncate> + <batching max-size="16" max-delay="200ms"/> + </component> +</container> +``` + +### VoyageAI embedder reference config + +<table className="vespa-table"> + <thead> + <tr> + <th>Name</th> + <th>Occurrence</th> + <th>Description</th> + <th>Type</th> + <th>Default</th> + </tr> + </thead> + <tbody> + <tr> + <td>model</td> + <td>One</td> + <td>**Required**. The VoyageAI model to use. See the <a href="https://docs.voyageai.com/docs/embeddings" aria-label="VoyageAI embeddings documentation">VoyageAI embeddings documentation</a> for the complete list of available models including general-purpose, specialized, <a href="https://docs.voyageai.com/docs/contextualized-chunk-embeddings" aria-label="contextualized">contextualized</a>, and <a href="https://docs.voyageai.com/docs/multimodal-embeddings" aria-label="multimodal">multimodal</a> models.</td> + <td>string</td> + <td>N/A</td> + </tr> + <tr> + <td>dimensions</td> + <td>One</td> + <td>**Required**. The number of dimensions for the output embedding vectors. Must match the tensor field definition in your schema. Valid values are <code>{`256`}</code>, <code>{`512`}</code>, <code>{`1024`}</code>, <code>{`1536`}</code>, or <code>{`2048`}</code>. See the <a href="https://docs.voyageai.com/docs/embeddings" aria-label="VoyageAI embeddings documentation">VoyageAI embeddings documentation</a> for model-specific dimension support.</td> + <td>integer</td> + <td>N/A</td> + </tr> + <tr> + <td>api-key-secret-ref</td> + <td>One</td> + <td>**Required**. Reference to the secret in Vespa's <a href="/en/security/secret-store" aria-label="secret store">secret store</a> containing the VoyageAI API key.</td> + <td>string</td> + <td>N/A</td> + </tr> + <tr> + <td>endpoint</td> + <td>Optional</td> + <td>VoyageAI API endpoint URL.</td> + <td>string</td> + <td>https://api.voyageai.com/v1/embeddings</td> + </tr> + <tr> + <td>truncate</td> + <td>Optional</td> + <td>Whether to truncate input text exceeding model limits. When enabled, text is automatically truncated. When disabled, requests with too-long text will fail.</td> + <td>boolean</td> + <td>true</td> + </tr> + <tr> + <td>quantization</td> + <td>Optional</td> + <td>Output quantization format for embedding vectors. Valid values are <code>{`auto`}</code>, <code>{`float`}</code>, <code>{`int8`}</code>, or <code>{`binary`}</code>. When set to <code>{`auto`}</code>, the embedder infers the appropriate quantization from the dimensions and cell type of the destination tensor in your schema. The <code>{`float`}</code> value also applies to <code>{`bfloat16`}</code> destination tensors. When using <code>{`binary`}</code> quantization, the destination tensor field must use <code>{`int8`}</code> cell type with 1/8 of the dimensions specified in the embedder configuration (e.g., 1024 dimensions → <code>{`tensor<int8>(x[128])`}</code>). See the <a href="https://docs.voyageai.com/docs/flexible-dimensions-and-quantization#quantization" aria-label="VoyageAI quantization documentation">VoyageAI quantization documentation</a> for details on quantization options and <a href="/en/rag/binarizing-vectors" aria-label="binarizing vectors">binarizing vectors</a> for more on binary quantization in Vespa.</td> + <td>string</td> + <td>auto</td> + </tr> + <tr> + <td>batching</td> + <td>Optional</td> + <td>Enables dynamic batching of concurrent embedding requests into single VoyageAI API calls. When enabled, the embedder collects concurrent requests and sends them as a single batch, reducing the number of API calls and improving throughput. <br/><br/>• <code>{`max-size`}</code> — Maximum number of requests to include in a single batch. <br/>• <code>{`max-delay`}</code> — Maximum time to wait for a full batch before sending a partial one (e.g., <code>{`200ms`}</code>).</td> + <td>element</td> + <td>disabled</td> + </tr> + </tbody> +</table> + +## OpenAI Embedder + +Available since `Vespa 8.678` + +An embedder that uses the [OpenAI](https://platform.openai.com/docs/guides/embeddings) embeddings API to generate embeddings. + +The OpenAI embedder is configured in [services.xml](/en/reference/applications/services/services), within the `container` tag: + +```xml +<container id="default" version="1.0"> + <component id="openai" type="openai-embedder"> + <model>text-embedding-3-small</model> + <api-key-secret-ref>openai_api_key</api-key-secret-ref> + <dimensions>1536</dimensions> + <endpoint>https://api.openai.com/v1/embeddings</endpoint> + </component> +</container> +``` + +### OpenAI embedder reference config + +<table className="vespa-table"> + <thead> + <tr> + <th>Name</th> + <th>Occurrence</th> + <th>Description</th> + <th>Type</th> + <th>Default</th> + </tr> + </thead> + <tbody> + <tr> + <td>model</td> + <td>One</td> + <td>**Required**. The OpenAI model to use, for example <code>{`text-embedding-3-small`}</code> or <code>{`text-embedding-3-large`}</code>. See the <a href="https://platform.openai.com/docs/guides/embeddings" aria-label="OpenAI embeddings documentation">OpenAI embeddings documentation</a> for the complete list of available models.</td> + <td>string</td> + <td>N/A</td> + </tr> + <tr> + <td>dimensions</td> + <td>One</td> + <td>**Required**. The number of dimensions for the output embedding vectors. Must match the tensor field definition in your schema. The destination tensor field must use <code>{`float`}</code> or <code>{`bfloat16`}</code> cell type — the OpenAI API does not support quantization.</td> + <td>integer</td> + <td>N/A</td> + </tr> + <tr> + <td>api-key-secret-ref</td> + <td>Optional</td> + <td>Reference to the secret in Vespa's <a href="/en/security/secret-store" aria-label="secret store">secret store</a> containing the OpenAI API key. When unset, requests are sent without an <code>{`Authorization`}</code> header.</td> + <td>string</td> + <td>"" (no auth)</td> + </tr> + <tr> + <td>endpoint</td> + <td>Optional</td> + <td>OpenAI API endpoint URL. Set this to target a specific OpenAI-compatible API.</td> + <td>string</td> + <td>https://api.openai.com/v1/embeddings</td> + </tr> + <tr> + <td>batching</td> + <td>Optional</td> + <td>Enables dynamic batching of concurrent embedding requests into single OpenAI API calls. When enabled, the embedder collects concurrent requests and sends them as a single batch, reducing the number of API calls and improving throughput.<br/><br/> • <code>{`max-size`}</code> — Maximum number of requests to include in a single batch. <br/>• <code>{`max-delay`}</code> — Maximum time to wait for a full batch before sending a partial one (e.g., <code>{`200ms`}</code>).</td> + <td>element</td> + <td>disabled</td> + </tr> + <tr> + <td>prepend</td> + <td>Optional</td> + <td>Strings prepended to the text input before sending the embedding request. Useful for OpenAI-compatible instruction-tuned models that expect a task-specific prefix.<br/><br/> • Element <code>{`<query>`}</code> - Optional query prepend instruction. <br/>• Element <code>{`<document>`}</code> - Optional document prepend instruction. <br/><br/><code>{`<prepend>`}</code><br/> <code>{`<query>query: </query>`}</code><br/> <code>{`<document>passage: </document>`}</code><br/> <code>{`</prepend>`}</code></td> + <td>Optional <code>{`<query> <document>`}</code> elements.</td> + <td></td> + </tr> + </tbody> +</table> + +## Mistral Embedder + +Available since `Vespa 8.678` + +An embedder that uses the [Mistral](https://docs.mistral.ai/capabilities/embeddings/overview/) embeddings API to generate embeddings. + +The Mistral embedder is configured in [services.xml](/en/reference/applications/services/services), within the `container` tag: + +```xml +<container id="default" version="1.0"> + <component id="mistral" type="mistral-embedder"> + <model>mistral-embed</model> + <api-key-secret-ref>mistral_api_key</api-key-secret-ref> + <dimensions>1024</dimensions> + <quantization>auto</quantization> + </component> +</container> +``` + +### Mistral embedder reference config + +<table className="vespa-table"> + <thead> + <tr> + <th>Name</th> + <th>Occurrence</th> + <th>Description</th> + <th>Type</th> + <th>Default</th> + </tr> + </thead> + <tbody> + <tr> + <td>model</td> + <td>One</td> + <td>**Required**. The Mistral model to use, for example <code>{`mistral-embed`}</code> or <code>{`codestral-embed`}</code>. See the <a href="https://docs.mistral.ai/capabilities/embeddings/overview/" aria-label="Mistral embeddings documentation">Mistral embeddings documentation</a> for the complete list of available models.</td> + <td>string</td> + <td>N/A</td> + </tr> + <tr> + <td>api-key-secret-ref</td> + <td>One</td> + <td>**Required**. Reference to the secret in Vespa's <a href="/en/security/secret-store" aria-label="secret store">secret store</a> containing the Mistral API key.</td> + <td>string</td> + <td>N/A</td> + </tr> + <tr> + <td>dimensions</td> + <td>One</td> + <td>**Required**. The number of dimensions for the output embedding vectors. Must match the tensor field definition in your schema. See the <a href="https://docs.mistral.ai/capabilities/embeddings/overview/" aria-label="Mistral embeddings documentation">Mistral embeddings documentation</a> for model-specific dimension support.</td> + <td>integer</td> + <td>N/A</td> + </tr> + <tr> + <td>quantization</td> + <td>Optional</td> + <td>Output quantization format for embedding vectors. Valid values are <code>{`auto`}</code>, <code>{`float`}</code>, <code>{`int8`}</code>, or <code>{`binary`}</code>. See the <code>{`quantization`}</code> row of the <a href="#voyageai-embedder-reference-config" aria-label="VoyageAI embedder reference config">VoyageAI embedder reference config</a> for details on <code>{`auto`}</code> resolution and the destination tensor layout required for <code>{`int8`}</code> and <code>{`binary`}</code>. Note that not all Mistral models support <code>{`int8`}</code> and <code>{`binary`}</code> quantization — see the <a href="https://docs.mistral.ai/capabilities/embeddings/overview/" aria-label="Mistral embeddings documentation">Mistral embeddings documentation</a> for per-model support.</td> + <td>string</td> + <td>auto</td> + </tr> + <tr> + <td>batching</td> + <td>Optional</td> + <td>Enables dynamic batching of concurrent embedding requests into single Mistral API calls. When enabled, the embedder collects concurrent requests and sends them as a single batch, reducing the number of API calls and improving throughput.<br/><br/>• <code>{`max-size`}</code> — Maximum number of requests to include in a single batch. <br/>• <code>{`max-delay`}</code> — Maximum time to wait for a full batch before sending a partial one (e.g., <code>{`200ms`}</code>).</td> + <td>element</td> + <td>disabled</td> + </tr> + </tbody> +</table> + +## Huggingface tokenizer embedder + +The Huggingface tokenizer embedder is configured in [services.xml](/en/reference/applications/services/services), within the `container` tag: + +```xml +<container version="1.0"> + <component id="tokenizer" type="hugging-face-tokenizer"> + <model url="https://huggingface.co/bert-base-uncased/raw/main/tokenizer.json"/> + </component> + </container> +``` + +### Huggingface tokenizer reference config + +<table className="vespa-table"> + <thead> + <tr> + <th>Name</th> + <th>Occurrence</th> + <th>Description</th> + <th>Type</th> + <th>Default</th> + </tr> + </thead> + <tbody> + <tr> + <td>model</td> + <td>One To Many</td> + <td>Use to point to the <code>{`tokenizer.json`}</code> Huggingface tokenizer configuration file. Also supports <code>{`language`}</code>, which is only relevant if one wants to tokenize differently based on the document language. Use "unknown" for a model to be used for any language (i.e. by default).</td> + <td><a href="#model-config-reference" aria-label="model-type">model-type</a></td> + <td>N/A</td> + </tr> + </tbody> +</table> + +## Embedder ONNX reference config + +Vespa uses [ONNX Runtime](https://onnxruntime.ai/) to accelerate inference of embedding models. These parameters are valid for both [Bert embedder](#bert-embedder) and [Huggingface embedder](#huggingface-embedder). + +<table className="vespa-table"> + <thead> + <tr> + <th>Name</th> + <th>Occurrence</th> + <th>Description</th> + <th>Type</th> + <th>Default</th> + </tr> + </thead> + <tbody> + <tr> + <td>onnx-execution-mode</td> + <td>One</td> + <td>Low level ONNX execution model. Valid values are <code>{`parallel`}</code> or <code>{`sequential`}</code>. Only relevant for inference on CPU. See <a href="https://onnxruntime.ai/docs/performance/tune-performance/threading.html" aria-label="ONNX runtime documentation">ONNX runtime documentation</a> on threading.</td> + <td>string</td> + <td>sequential</td> + </tr> + <tr> + <td>onnx-interop-threads</td> + <td>One</td> + <td>Low level ONNX setting.Only relevant for inference on CPU.</td> + <td>numeric</td> + <td>1</td> + </tr> + <tr> + <td>onnx-intraop-threads</td> + <td>One</td> + <td>Low level ONNX setting. Only relevant for inference on CPU.</td> + <td>numeric</td> + <td>4</td> + </tr> + <tr> + <td>onnx-gpu-device</td> + <td>One</td> + <td>The GPU device to run the model on. See <a href="/en/operations/self-managed/vespa-gpu-container" aria-label="configuring GPU for Vespa container image">configuring GPU for Vespa container image</a>. Use <code>{`-1`}</code> to not use GPU for the model, even if the instance has available GPUs.</td> + <td>numeric</td> + <td>0</td> + </tr> + </tbody> +</table> + +## SentencePiece embedder + +A native Java implementation of [SentencePiece](https://github.com/google/sentencepiece). SentencePiece breaks text into chunks independent of spaces, which is robust to misspellings and works with CJK languages. Prefer the [Huggingface tokenizer embedder](#huggingface-tokenizer-embedder) over this for better compatibility with Huggingface models. + +This is suitable to use in conjunction with [custom components](/en/applications/components), or the resulting tensor can be used in [ranking](/en/basics/ranking). + +To use the [SentencePiece embedder](https://github.com/vespa-engine/vespa/blob/master/linguistics-components/src/main/java/com/yahoo/language/sentencepiece/SentencePieceEmbedder.java), add it to [services.xml](/en/reference/applications/services/services): + +```xml +<container version="1.0"> + <component id="mySentencePiece" + class="com.yahoo.language.sentencepiece.SentencePieceEmbedder" + bundle="linguistics-components"> + <config name="language.sentencepiece.sentence-piece">; + <model> + <item> + <language>unknown</language> + <path>model/en.wiki.bpe.vs10000.model</path> + </item> + </model> + </config> + </component> + </container> +``` + +See the options available for configuring SentencePiece in [the full configuration definition](https://github.com/vespa-engine/vespa/blob/master/linguistics-components/src/main/resources/configdefinitions/language.sentencepiece.sentence-piece.def). + +## WordPiece embedder + +A native Java implementation of [WordPiece](https://github.com/google-research/bert#tokenization), which is commonly used with BERT models. Prefer the [Huggingface tokenizer embedder](#huggingface-tokenizer-embedder) over this for better compatibility with Huggingface models. + +This is suitable to use in conjunction with [custom components](/en/applications/components), or the resulting tensor can be used in [ranking](/en/basics/ranking). + +To use the [WordPiece embedder](https://github.com/vespa-engine/vespa/blob/master/linguistics-components/src/main/java/com/yahoo/language/wordpiece/WordPieceEmbedder.java), add it to [services.xml](/en/reference/applications/services/services) within the `container` tag: + +```xml +<container version="1.0"> + <component id="myWordPiece"> + class="com.yahoo.language.wordpiece.WordPieceEmbedder" + bundle="linguistics-components"> + <config name="language.wordpiece.word-piece"> + <model> + <item> + <language>unknown</language> + <path>models/bert-base-uncased-vocab.txt</path> + </item> + </model> + </config> + </component> + </container> +``` + +See the options available for configuring WordPiece in [the full configuration definition](https://github.com/vespa-engine/vespa/blob/master/linguistics-components/src/main/resources/configdefinitions/language.wordpiece.word-piece.def). + +WordPiece is suitable to use in conjunction with custom components, or the resulting tensor can be used in [ranking](/en/basics/ranking). + +## Using an embedder from Java + +When writing custom Java components (such as [Searchers](/en/applications/searchers) or [Document processors](/en/applications/document-processors#document-processors)), use embedders you have configured by [having them injected in the constructor](/en/applications/dependency-injection), just as any other component: + +```java +class MyComponent { + @Inject + public MyComponent(ComponentRegistry<Embedder> embedders) { + // embedders contains all the embedders configured in your services.xml + } +} +``` + +See a concrete example of using an embedder in a custom searcher in [LLMSearcher](https://github.com/vespa-cloud/vespa-documentation-search/blob/main/src/main/java/ai/vespa/cloud/docsearch/LLMSearcher.java). + +## Custom Embedders + +Vespa provides a Java interface for defining components which can provide embeddings of text: [com.yahoo.language.process.Embedder](https://github.com/vespa-engine/vespa/blob/master/linguistics/src/main/java/com/yahoo/language/process/Embedder.java). + +To define a custom embedder in an application and make it usable by Vespa (see [embedding a query text](/en/rag/embedding#embedding-a-query-text)), implement this interface and add it as a [component](/en/applications/developer-guide#developing-components) to [services.xml](/en/reference/applications/services/container): + +```xml +<container version="1.0"> + <component id="myEmbedder" + class="com.example.MyEmbedder" + bundle="the name in artifactId in pom.xml"> + <config name='com.example.my-embedder'> + <model model-id="minilm-l6-v2"/> + <vocab path="files/vocab.txt"/> + <myValue>foo</myValue> + </config> + </component> +</container> +``` \ No newline at end of file diff --git a/en/reference/ranking/constant-tensor-json-format.html b/en/reference/ranking/constant-tensor-json-format.html deleted file mode 100644 index 47b5e34ef5..0000000000 --- a/en/reference/ranking/constant-tensor-json-format.html +++ /dev/null @@ -1,201 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Constant Tensor JSON Format" -redirect_from: -- /en/ranking/constant-tensor-json-format.html ---- - -<p> - This document describes with examples the JSON formats accepted when reading tensor constants from a file. - For convenience, compactness, and readability there are various formats that can be used depending on the detailed tensor type: -</p> -<ul> - <li><a href="#dense-tensors">Dense tensors</a>: indexed dimensions only</li> - <li><a href="#sparse-tensors">Sparse tensors</a>: mapped dimensions only</li> - <li><a href="#mixed-tensors">Mixed tensors</a>: both indexed and mapped dimensions</li> -</ul> - - -<h2 id="canonical-type">Canonical type</h2> -<p> - A tensor type can be declared with its dimension in any order, but - internally they will always be sorted in alphabetical order. - So the type "<code>tensor(category{}, brand{}, a[3], x[768], d0[1])</code>" has - the canonical string representation "<code>tensor(a[3],brand{},category{},d0[1],x[768])</code>" - and the "x" dimension with size 768 is the innermost. - For constants, all indexed dimensions must have a known size. -</p> - -<h2 id="dense-tensors">Dense tensors</h2> -<p> - Tensors using only indexed dimensions are used for storing a vector, - a matrix, and so on and are collectively known as "dense" tensors. - These are particularly easy to handle, as they always have a known - number of cells in a well-defined order. - They can be input as nested arrays of numerical values. - Example with vector of size 5: -</p> -<pre> - { - "type": "tensor(x[5])", - "values": [ 13.25, -22, 0.4242, 0, -17.0 ] - } -</pre> -<p> - The "type" field is optional, but must match the - <a href="#canonical-type">canonical form of the tensor type</a> - if present. This format is similar to "Indexed tensors short form" in the - <a href="../schemas/document-json-format.html#tensor-short-form-indexed">document JSON format</a>. -</p> -<p> - Example of a 3x4 matrix; note that the dimension names will - always be processed in - <a href="#canonical-type">alphabetical order</a> - from outermost to innermost. -</p> -<pre> - { - "type": "tensor(bar[3],foo[4])", - "values": [ - [ 2.5, 1.0, 2.0, 3.0 ], - [ 1.0, 2.0, 3.0, 2.0 ], - [ 2.0, 3.0, 2.0, 1.5 ] - ] - } -</pre> -<p> - Note that the arrays must have exactly the declared number of - elements for each dimension, and be correctly nested. -</p> -<p> - Example of an ONNX model input where we have an extra "batch" dimension - which is unused (size 1) for this particular input, but still requires - extra brackets: -</p> -<pre> - { - "type": "tensor(d0[1],d1[5],d2[2])", - "values": [ [ - [ 1.1, 1.2 ], - [ 2.1, 2.2 ], - [ 3.1, 3.2 ], - [ 4.1, 4.2 ], - [ 5.1, 5.2 ] - ] ] - } -</pre> - - -<h2 id="sparse-tensors">Sparse tensors</h2> -<p> - Tensors using only mapped dimensions are collectively known as "sparse" tensors. - JSON input for these will list the cells directly. - Tensors with only one mapped dimension can use as simple JSON object as input: -</p> -<pre> - { - "type": "tensor(category{})", - "cells": { - "tag": 2.5, - "another": 2.75 - } - } -</pre> -<p> - The "type" field is optional. - This format is similar to "Short form for tensors with a single mapped dimension" - in the <a href="../schemas/document-json-format.html#tensor-short-form-mapped">document JSON format</a>. -</p> -<p> - Tensors with multiple mapped dimensions must use an array of objects, - where each object has an "address" containing the labels for all - dimensions, and a "value" with the cell value: -</p> -<pre> - { - "type": "tensor(category{},product{})", - "cells": [ - { - "address": { "category": "foo", "product": "bar" }, - "value": 1.5 - }, - { - "address": { "category": "qux", "product": "zap" }, - "value": 3.5 - }, - { - "address": { "category": "pop", "product": "rip" }, - "value": 6.5 - } - ] - } -</pre> -<p> - Again, the "type" field is optional, but must match the - <a href="#canonical-type">canonical form of the tensor type</a> - if present. -</p> -<p> - This format is also known as the - <a href="../schemas/document-json-format.html#tensor">general verbose form</a>, - and it's possible to use it for any tensor type. -</p> - -<h2 id="mixed-tensors">Mixed tensors</h2> -<p> - Tensors with both mapped and indexed dimensions can use - a "blocks" format; this is similar to the "cells" formats for sparse tensors, - but instead of a single cell value you get a block of - values for each address. - With one mapped dimension and two indexed dimensions: -</p> -<pre> - { - "type": "tensor(a{},x[3],y[4])", - "blocks": { - "bar": [ - [ 1.0, 2.0, 0.0, 3.0 ], - [ 2.0, 2.5, 2.0, 0.5 ], - [ 3.0, 6.0, 9.0, 9.0 ] - ], - "foo": [ - [ 1.0, 0.0, 2.0, 3.0 ], - [ 2.0, 2.5, 2.0, 0.5 ], - [ 3.0, 3.0, 6.0, 9.0 ] - ] - } - } -</pre> -<p> - The "type" field is optional, but must match the - <a href="#canonical-type">canonical form of the tensor type</a> - if present. - This format is similar to the first variant of "Mixed tensors short form" - in the <a href="../schemas/document-json-format.html#tensor-short-form-mixed">document JSON format</a>. -</p> -<p> - With two mapped dimensions and one indexed dimensions: -<pre> - { - "type": "tensor(a{},b{},x[3])", - "blocks": [ - { - "address": { "a": "qux", "b": "zap" }, - "values": [ 2.5, 3.5, 4.5 ] - }, - { - "address": { "a": "foo", "b": "bar" }, - "values": [ 1.5, 2.5, 3.5 ] - }, - { - "address": { "a": "pop", "b": "rip" }, - "values": [ 3.5, 4.5, 5.5 ] - } - ] - } -</pre> -<p> - Again, the "type" field is optional. - This format is similar to the second variant of "Mixed tensors short form" - in the <a href="../schemas/document-json-format.html#tensor-short-form-mixed">document JSON format</a>. -</p> diff --git a/en/reference/ranking/constant-tensor-json-format.mdx b/en/reference/ranking/constant-tensor-json-format.mdx new file mode 100644 index 0000000000..6863593f6c --- /dev/null +++ b/en/reference/ranking/constant-tensor-json-format.mdx @@ -0,0 +1,147 @@ +--- +title: "Constant Tensor JSON Format" +sidebarTitle: "constant tensor" +--- + +This document describes with examples the JSON formats accepted when reading tensor constants from a file. For convenience, compactness, and readability there are various formats that can be used depending on the detailed tensor type: + +- [Dense tensors](#dense-tensors): indexed dimensions only +- [Sparse tensors](#sparse-tensors): mapped dimensions only +- [Mixed tensors](#mixed-tensors): both indexed and mapped dimensions + +## Canonical type + +A tensor type can be declared with its dimension in any order, but internally they will always be sorted in alphabetical order. So the type "`tensor(category{}, brand{}, a[3], x[768], d0[1])`" has the canonical string representation "`tensor(a[3],brand{},category{},d0[1],x[768])`" and the "x" dimension with size 768 is the innermost. For constants, all indexed dimensions must have a known size. + +## Dense tensors + +Tensors using only indexed dimensions are used for storing a vector, a matrix, and so on and are collectively known as "dense" tensors. These are particularly easy to handle, as they always have a known number of cells in a well-defined order. They can be input as nested arrays of numerical values. Example with vector of size 5: + +```json +{ + "type": "tensor(x[5])", + "values": [13.25, -22, 0.4242, 0, -17.0] + } +``` + +The "type" field is optional, but must match the [canonical form of the tensor type](#canonical-type) if present. This format is similar to "Indexed tensors short form" in the [document JSON format](/en/reference/schemas/document-json-format#tensor-short-form-indexed). + +Example of a 3x4 matrix; note that the dimension names will always be processed in [alphabetical order](#canonical-type) from outermost to innermost. + +```json +{ + "type": "tensor(bar[3],foo[4])", + "values": [ + [2.5, 1.0, 2.0, 3.0], + [1.0, 2.0, 3.0, 2.0], + [2.0, 3.0, 2.0, 1.5] + ] + } +``` + +Note that the arrays must have exactly the declared number of elements for each dimension, and be correctly nested. + +Example of an ONNX model input where we have an extra "batch" dimension which is unused (size 1) for this particular input, but still requires extra brackets: + +```json +{ + "type": "tensor(d0[1],d1[5],d2[2])", + "values": [ [ + [1.1, 1.2], + [2.1, 2.2], + [3.1, 3.2], + [4.1, 4.2], + [5.1, 5.2] + ] ] + } +``` + +## Sparse tensors + +Tensors using only mapped dimensions are collectively known as "sparse" tensors. JSON input for these will list the cells directly. Tensors with only one mapped dimension can use as simple JSON object as input: + +```json +{ + "type": "tensor(category{})", + "cells": { + "tag": 2.5, + "another": 2.75 + } + } +``` + +The "type" field is optional. This format is similar to "Short form for tensors with a single mapped dimension" in the [document JSON format](/en/reference/schemas/document-json-format#tensor-short-form-mapped). + +Tensors with multiple mapped dimensions must use an array of objects, where each object has an "address" containing the labels for all dimensions, and a "value" with the cell value: + +```json +{ + "type": "tensor(category{},product{})", + "cells": [ + { + "address": { "category": "foo", "product": "bar" }, + "value": 1.5 + }, + { + "address": { "category": "qux", "product": "zap" }, + "value": 3.5 + }, + { + "address": { "category": "pop", "product": "rip" }, + "value": 6.5 + } + ] + } +``` + +Again, the "type" field is optional, but must match the [canonical form of the tensor type](#canonical-type) if present. + +This format is also known as the [general verbose form](/en/reference/schemas/document-json-format#tensor), and it's possible to use it for any tensor type. + +## Mixed tensors + +Tensors with both mapped and indexed dimensions can use a "blocks" format; this is similar to the "cells" formats for sparse tensors, but instead of a single cell value you get a block of values for each address. With one mapped dimension and two indexed dimensions: + +```json +{ + "type": "tensor(a{},x[3],y[4])", + "blocks": { + "bar": [ + [1.0, 2.0, 0.0, 3.0], + [2.0, 2.5, 2.0, 0.5], + [3.0, 6.0, 9.0, 9.0] + ], + "foo": [ + [1.0, 0.0, 2.0, 3.0], + [2.0, 2.5, 2.0, 0.5], + [3.0, 3.0, 6.0, 9.0] + ] + } + } +``` + +The "type" field is optional, but must match the [canonical form of the tensor type](#canonical-type) if present. This format is similar to the first variant of "Mixed tensors short form" in the [document JSON format](/en/reference/schemas/document-json-format#tensor-short-form-mixed). + +With two mapped dimensions and one indexed dimensions: + +```json +{ + "type": "tensor(a{},b{},x[3])", + "blocks": [ + { + "address": { "a": "qux", "b": "zap" }, + "values": [2.5, 3.5, 4.5] + }, + { + "address": { "a": "foo", "b": "bar" }, + "values": [1.5, 2.5, 3.5] + }, + { + "address": { "a": "pop", "b": "rip" }, + "values": [3.5, 4.5, 5.5] + } + ] + } +``` + +Again, the "type" field is optional. This format is similar to the second variant of "Mixed tensors short form" in the [document JSON format](/en/reference/schemas/document-json-format#tensor-short-form-mixed). \ No newline at end of file diff --git a/en/reference/ranking/model-files.html b/en/reference/ranking/model-files.html deleted file mode 100644 index 49f7ffe360..0000000000 --- a/en/reference/ranking/model-files.html +++ /dev/null @@ -1,121 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Stateless model reference" -redirect_from: -- /en/reference/stateless-model-reference.html ---- -<p> -<em>.model</em> files are used in -<a href="../../ranking/stateless-model-evaluation.html">stateless model evaluation</a>. -These are files with <a href="../../basics/ranking.html">ranking expressions</a>, -located in <a href="../applications/application-packages.html">models</a> / a subdirectory of <em>models</em>, -with <em>.model</em> suffix: -</p> -<pre> -├── models -│   └── my_model.model -└── services.xml -</pre> - - -<h2 id="model-file-format-specification">.model file format specification</h2> -<pre> -model [name] { - - inputs { - ([input-name] [<a href="tensor.html#tensor-type-spec">input-type</a>])* - } - - constants { - <a href="#constant">constant</a>* - } - - (function [name]([argument-name]*) { - expression: [<a href="ranking-expressions.html">ranking expression</a>] - })* - -} -</pre> -<p> -The elements can appear in any order (and number). -</p> - -<h3 id="constant">Constant element</h3> - -<pre>[constant-name] [type]?: [scalar, <a href="tensor.html#tensor-literal-form">tensor on literal form</a>, or <code>file:</code> followed by a file reference]</pre> - -<table class="table"> - <tr> - <th>Name</th> - <th>Description</th> - </tr> - <tr> - <td>name</td> - <td>The name of the constant, written either the full feature name <code>constant(myName)</code>, or just as <code>name</code>. - </tr> - <tr> - <td>type</td> - <td>The type of the constant, either <code>double</code> or a <a href="tensor.html#tensor-type-spec">tensor type</a>. - If omitted, the type is double. - </tr> - <tr> - <td>value</td> - <td>A number, a <a href="tensor.html#tensor-literal-form">tensor on literal form</a>, - or <code>file:</code> followed by a path relative to the model file to a - file containing the constant. - The file must be stored on the <a href="../schemas/document-json-format.html#tensor">tensor JSON Format</a> - and end with <code>.json</code>. The file may be lz4 compressed, in which case the ending must be - <code>.json.lz4</code>. - </td> - </tr> -</table> - -<p>Constant examples:</p> - -<pre> -constants { - myDouble: 0.5 - constant(myOtherDouble) double: 0.6 - constant(myArray) tensor(x[3]):[1, 2, 3] - constant(myMap) tensor(key{}):{key1: 1.0, key2: 2.0} - constant(myLargeTensor) tensor(x[10000]): file:constants/myTensor.json.lz4 -} -</pre> - - - - -<h2 id="model-example">Model example</h2> -<p> -This file must be saved as <code>example.model</code> somewhere in the -<a href="../applications/application-packages.html">models</a> directory tree, -and the same directory must also contain <code>myLargeConstant.json.lz4</code> with a tensor as compressed json. -</p> -<pre>{% raw %} -model example { - - # All inputs that are not scalar (aka 0-dimensional tensor) must be declared - inputs { - input1 tensor(name{},x[3]) - input2 tensor(x[3]) - } - - constants { - constant(constant1) tensor(x[3]):{{x:0}:0.5, {x:1}:1.5, {x:2}:2.5} - constant(constant2): 3.0 - constant(myLargeConstant) tensor(x[10000]): file:myLargeConstant.json.lz4 - } - - function foo1() { - expression: reduce(sum(input1 * input2, name) * constant1, max, x) * constant2 - } - - function foo2() { - expression: reduce(sum(input1 * input2, name) * constant(constant1asLarge), max, x) * constant2 - } - -} -</pre>{% endraw %} -<p> -This makes the model <em>example</em> available with the functions <em>foo1</em> and <em>foo2</em>. -</p> diff --git a/en/reference/ranking/model-files.mdx b/en/reference/ranking/model-files.mdx new file mode 100644 index 0000000000..3ed4bab983 --- /dev/null +++ b/en/reference/ranking/model-files.mdx @@ -0,0 +1,111 @@ +--- +title: "Stateless model reference" +sidebarTitle: "model files" +--- + +_.model_ files are used in [stateless model evaluation](/en/ranking/stateless-model-evaluation). These are files with [ranking expressions](/en/basics/ranking), located in [models](/en/reference/applications/application-packages) / a subdirectory of _models_, with _.model_ suffix: + +``` +├── models +│   └── my_model.model +└── services.xml +``` + +## .model file format specification + +```txt +model [name] { + + inputs { + ([input-name] [input-type])* + } + + constants { + [constant]* + } + + (function [name]([argument-name]*) { + expression: [ranking expression] + })* + +} +``` + +The elements can appear in any order (and number). + +### Constant element + +<Card> +[constant-name] [type]?: [scalar, [tensor on literal form](/en/reference/ranking/tensor#tensor-literal-form), or `file:` followed by a file reference] +</Card> + +<table className="vespa-table"> + <thead> + <tr> + <th>Name</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>name</td> + <td>The name of the constant, written either the full feature name <code>{`constant(myName)`}</code>, or just as <code>{`name`}</code>.</td> + </tr> + <tr> + <td>type</td> + <td>The type of the constant, either <code>{`double`}</code> or a <a href="/en/reference/ranking/tensor#tensor-type-spec" aria-label="tensor type">tensor type</a>. If omitted, the type is double.</td> + </tr> + <tr> + <td>value</td> + <td>A number, a <a href="/en/reference/ranking/tensor#tensor-literal-form" aria-label="tensor on literal form">tensor on literal form</a>, or <code>{`file:`}</code> followed by a path relative to the model file to a file containing the constant. The file must be stored on the <a href="/en/reference/schemas/schemas#tensor" aria-label="tensor JSON Format">tensor JSON Format</a> and end with <code>{`.json`}</code>. The file may be lz4 compressed, in which case the ending must be <code>{`.json.lz4`}</code>.</td> + </tr> + </tbody> +</table> + +Constant examples: + +```js +constants { + myDouble: 0.5 + constant(myOtherDouble) double: 0.6 + constant(myArray) tensor(x[3]):[1, 2, 3] + constant(myMap) tensor(key{}):\{key1: 1.0, key2: 2.0} + constant(myLargeTensor) tensor(x[10000]): file:constants/myTensor.json.lz4 +} +``` + +## Model example + +This file must be saved as `example.model` somewhere in the [models](/en/reference/applications/application-packages) directory tree, and the same directory must also contain `myLargeConstant.json.lz4` with a tensor as compressed json. + +```js expandable +model example { + + # All inputs that are not scalar (aka 0-dimensional tensor) must be declared + inputs { + + input1 tensor(name{},x[3]) + input2 tensor(x[3]) + } + + constants { + + constant(constant1) tensor(x[3]):{{x:0}:0.5, {x:1}:1.5, {x:2}:2.5} + constant(constant2): 3.0 + constant(myLargeConstant) tensor(x[10000]): file:myLargeConstant.json.lz4 + } + + function foo1() { + + expression: reduce(sum(input1 * input2, name) * constant1, max, x) * constant2 + } + + function foo2() { + + expression: reduce(sum(input1 * input2, name) * constant(constant1asLarge), max, x) * constant2 + } + +} +``` + +This makes the model _example_ available with the functions _foo1_ and _foo2_. \ No newline at end of file diff --git a/en/reference/ranking/nativerank.html b/en/reference/ranking/nativerank.html deleted file mode 100644 index e5d7115a25..0000000000 --- a/en/reference/ranking/nativerank.html +++ /dev/null @@ -1,1131 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "nativeRank Reference" -redirect_from: -- /en/reference/nativerank.html ---- - -<p> - The <em>nativeRank</em> feature produces a reasonable text ranking score which is - computed at an acceptable performance, - and is a good candidate for <a href="../../ranking/phased-ranking.html">first phase</a> ranking. - The <em>nativeRank</em> feature is a linear combination of the normalized scores - computed by the features <em>nativeFieldMatch</em>, <em>nativeProximity</em>, - and <em>nativeAttributeMatch</em>. - All these features are described in detail below. - See the <a href="#configuration-properties">configuration properties</a> - section for how to configure the features. -</p> - - - -<h2 id="nativeFieldMatch">nativeFieldMatch</h2> -<p> - The <em>nativeFieldMatch</em> feature captures how well query - terms match searched index fields by looking at the number of times a - term occurs in a field and how early in the field it occurs. The - significance and weight of the terms are also taken into account such - that unusual terms give a higher rank contribution than common ones. -</p><p> - The score for <em>nativeFieldMatch</em> is calculated as follows: -</p> -<figure style="font-size: 1.3rem"> - <div> - <math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> - <semantics> - <mrow> - <mi mathvariant="italic">nativeFieldMatch</mi> - <mo>=</mo> - <mfrac> - <mrow> - <munderover> - <mo>∑</mo> - <mi>i</mi> - <mi>n</mi> - </munderover> - <msub> - <mi mathvariant="italic">termSignificance</mi> - <mi>i</mi> - </msub> - <mo>×</mo> - <msub> - <mi mathvariant="italic">termWeight</mi> - <mi>i</mi> - </msub> - <munderover> - <mo>∑</mo> - <mi>j</mi> - <mi>m</mi> - </munderover> - <msub> - <mi mathvariant="italic">fieldWeight</mi> - <mi>j</mi> - </msub> - <mfenced close=")" open="("> - <mrow> - <msub> - <mi mathvariant="italic">firstOccImp</mi> - <mi>j</mi> - </msub> - <mo>×</mo> - <msub> - <mi mathvariant="italic">firstOccBoost</mi> - <mrow> - <mi>i</mi> - <mi>j</mi> - </mrow> - </msub> - <mo>+</mo> - <mfenced close=")" open="("> - <mrow> - <mn>1</mn> - <mo>-</mo> - <msub> - <mi mathvariant="italic">firstOccImp</mi> - <mi>j</mi> - </msub> - </mrow> - </mfenced> - <mo>×</mo> - <msub> - <mi mathvariant="italic">numOccBoost</mi> - <mrow> - <mi>i</mi> - <mi>j</mi> - </mrow> - </msub> - </mrow> - </mfenced> - </mrow> - <mrow> - <munderover> - <mo>∑</mo> - <mi>i</mi> - <mi>n</mi> - </munderover> - <msub> - <mi mathvariant="italic">termSignificance</mi> - <mi>i</mi> - </msub> - <mo>×</mo> - <msub> - <mi mathvariant="italic">termWeight</mi> - <mi>i</mi> - </msub> - <munderover> - <mo>∑</mo> - <mi>j</mi> - <mi>m</mi> - </munderover> - <msub> - <mi mathvariant="italic">fieldWeight</mi> - <mi>j</mi> - </msub> - <mo>×</mo> - <msub> - <mi mathvariant="italic">fmMaxTable</mi> - <mi>j</mi> - </msub> - </mrow> - </mfrac> - </mrow> - <annotation encoding="SnuggleTeX">\[ nativeFieldMatch = \frac{\sum_i^ntermSignificance_i \times termWeight_i\sum_j^mfieldWeight_j(firstOccImp_j \times firstOccBoost_{ij} + (1 - firstOccImp_j) \times numOccBoost_{ij})}{\sum_i^ntermSignificance_i \times termWeight_i\sum_j^mfieldWeight_j \times fmMaxTable_j} \]</annotation> - </semantics> - </math> - </div> -</figure> -<p> - where <em>n</em> is the number of query terms searched in index fields, - <em>m</em> is the number of fields searched by query term <em>i</em>, - <em>firstOccImp<sub>j</sub></em> is the <em>firstOccurrenceImportance</em> - for field <em>j</em>, and <em>firstOccBoost<sub>ij</sub></em>, - <em>numOccBoost<sub>ij</sub></em> and <em>fmMaxTable<sub>j</sub></em> are given below. -</p> -<figure style="font-size: 1.3rem"> - <div> - <math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> - <semantics> - <mrow> - <msub> - <mi mathvariant="italic">firstOccBoost</mi> - <mrow> - <mi>i</mi> - <mi>j</mi> - </mrow> - </msub> - <mo>=</mo> - <msub> - <mi mathvariant="italic">firstOccurrenceTable</mi> - <mi>j</mi> - </msub> - <mfenced close="]" open="["> - <mfrac> - <mrow> - <msub> - <mi mathvariant="italic">firstOcc</mi> - <mrow> - <mi>i</mi> - <mi>j</mi> - </mrow> - </msub> - <mo>×</mo> - <msub> - <mi mathvariant="italic">tableSize</mi> - <mi>j</mi> - </msub> - </mrow> - <mrow> - <mi>max</mi> - <mfenced close=")" open="("> - <mn>6</mn> - <mrow> - <msub> - <mi mathvariant="italic">fieldLength</mi> - <mi>j</mi> - </msub> - </mrow> - </mfenced> - </mrow> - </mfrac> - </mfenced> - </mrow> - <annotation encoding="SnuggleTeX">\[ firstOccBoost_{ij} = firstOccurrenceTable_j[\frac{firstOcc_{ij} \times tableSize_j}{max(6,fieldLength_j)}] \]</annotation> - </semantics> - </math> - </div> -</figure> -<p> - where <em>firstOccurrenceTable<sub>j</sub></em> is the boost table configured for field <em>j</em>, - typically an expdecay function (see the <a href="#boost-tables">boost tables</a> section below), - <em>firstOcc<sub>ij</sub></em> is the first occurrence of query term <em>i</em> in field <em>j</em>, - and <em>tableSize<sub>j</sub></em> is the size of the boost table. -</p> -<figure style="font-size: 1.3rem"> - <div> - <math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> - <semantics> - <mrow> - <msub> - <mi mathvariant="italic">numOccBoost</mi> - <mrow> - <mi>i</mi> - <mi>j</mi> - </mrow> - </msub> - <mo>=</mo> - <msub> - <mi mathvariant="italic">occurrenceCountTable</mi> - <mi>j</mi> - </msub> - <mfenced close="]" open="["> - <mfrac> - <mrow> - <msub> - <mi mathvariant="italic">numOccs</mi> - <mrow> - <mi>i</mi> - <mi>j</mi> - </mrow> - </msub> - <mo>×</mo> - <msub> - <mi mathvariant="italic">tableSize</mi> - <mi>j</mi> - </msub> - </mrow> - <mrow> - <mi>max</mi> - <mfenced close=")" open="("> - <mn>6</mn> - <mrow> - <msub> - <mi mathvariant="italic">fieldLength</mi> - <mi>j</mi> - </msub> - </mrow> - </mfenced> - </mrow> - </mfrac> - </mfenced> - </mrow> - <annotation encoding="SnuggleTeX">\[ numOccBoost_{ij} = occurrenceCountTable_j[\frac{numOccs_{ij} \times tableSize_j}{max(6,fieldLength_j)}] \]</annotation> - </semantics> - </math> - </div> -</figure> -<p> - where <em>occurrenceCountTable<sub>j</sub></em> is the boost table configured for field <em>j</em>, - typically a loggrowth function (see the <a href="#boost-tables">boost tables</a> section below), - <em>numOccs<sub>ij</sub></em> is the number of occurrences of query term <em>i</em> in field <em>j</em>, - and <em>tableSize<sub>j</sub></em> is the size of the boost table. -</p> -<figure style="font-size: 1.3rem"> - <div> - <math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> - <semantics> - <mrow> - <msub> - <mi mathvariant="italic">fmMaxTable</mi> - <mi>j</mi> - </msub> - <mo>=</mo> - <msub> - <mi mathvariant="italic">firstOccImp</mi> - <mi>j</mi> - </msub> - <mo>×</mo> - <mi>max</mi> - <mfenced close=")" open="("> - <mrow> - <msub> - <mi mathvariant="italic">firstOccurrenceTable</mi> - <mi>j</mi> - </msub> - </mrow> - </mfenced> - <mo>+</mo> - <mfenced close=")" open="("> - <mrow> - <mn>1</mn> - <mo>-</mo> - <msub> - <mi mathvariant="italic">firstOccImp</mi> - <mi>j</mi> - </msub> - </mrow> - </mfenced> - <mo>×</mo> - <mi>max</mi> - <mfenced close=")" open="("> - <mrow> - <msub> - <mi mathvariant="italic">occurrenceCountTable</mi> - <mi>j</mi> - </msub> - </mrow> - </mfenced> - </mrow> - <annotation encoding="SnuggleTeX">\[ fmMaxTable_j = firstOccImp_j \times max(firstOccurrenceTable_j) + (1 - firstOccImp_j) \times max(occurrenceCountTable_j) \]</annotation> - </semantics> - </math> - </div> -</figure> -<p> - where <em>max(boostTable<sub>j</sub>)</em> is the max value in that table. - <em>fmMaxTable<sub>j</sub></em> is 1 if table normalization is turned off - (see the property <em>nativeRank.useTableNormalization</em> in the - <a href="#configuration-properties">configuration properties</a> section). -</p><p> - The default behavior for <em>nativeFieldMatch</em> is to consider all query terms searching in all index fields - when calculating the score. - The calculation can be limited to a specified set of index fields as follows: -</p> -<p style="text-align: center;"> - <code>nativeFieldMatch(<em>f1</em>, <em>f2</em>)</code> -</p> -<p> - In this case only query terms searching in index fields <em>f1</em> and <em>f2</em> are considered. -</p> - - - -<h2 id="nativeProximity">nativeProximity</h2> -<p> - The <em>nativeProximity</em> feature captures how near the matched query terms occur - in searched index fields by looking at the word distance between query terms in query term pairs. - Two query terms that are close to each other should give a higher score than two terms that are far from each other. -</p><p> - The score for <em>nativeProximity</em> is calculated as follows: -</p> -<figure style="font-size: 1.3rem"> - <div> - <math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> - <semantics> - <mrow> - <mi mathvariant="italic">nativeProximity</mi> - <mo>=</mo> - <mfrac> - <mrow> - <munderover> - <mo>∑</mo> - <mi>j</mi> - <mi>m</mi> - </munderover> - <msub> - <mi mathvariant="italic">fieldWeight</mi> - <mi>j</mi> - </msub> - <munder> - <mo>∑</mo> - <mrow> - <mi>a</mi> - <mi>b</mi> - </mrow> - </munder> - <msub> - <mi mathvariant="italic">termPairWeight</mi> - <mrow> - <mi>a</mi> - <mi>b</mi> - </mrow> - </msub> - <mfenced close=")" open="("> - <mrow> - <msub> - <mi mathvariant="italic">proxImp</mi> - <mi>j</mi> - </msub> - <mo>×</mo> - <msub> - <mi mathvariant="italic">proxTable</mi> - <mi>j</mi> - </msub> - <mfenced close="]" open="["> - <mrow> - <msub> - <mi mathvariant="italic">dist</mi> - <mrow> - <mi>a</mi> - <mi>b</mi> - </mrow> - </msub> - <mo>-</mo> - <mn>1</mn> - </mrow> - </mfenced> - <mo>+</mo> - <mfenced close=")" open="("> - <mrow> - <mn>1</mn> - <mo>-</mo> - <msub> - <mi mathvariant="italic">proxImp</mi> - <mi>j</mi> - </msub> - </mrow> - </mfenced> - <mo>×</mo> - <msub> - <mi mathvariant="italic">revProxTable</mi> - <mi>j</mi> - </msub> - <mfenced close="]" open="["> - <mrow> - <msub> - <mi mathvariant="italic">dist</mi> - <mrow> - <mi>b</mi> - <mi>a</mi> - </mrow> - </msub> - <mo>-</mo> - <mn>1</mn> - </mrow> - </mfenced> - </mrow> - </mfenced> - </mrow> - <mrow> - <munderover> - <mo>∑</mo> - <mi>j</mi> - <mi>m</mi> - </munderover> - <msub> - <mi mathvariant="italic">fieldWeight</mi> - <mi>j</mi> - </msub> - <munder> - <mo>∑</mo> - <mrow> - <mi>a</mi> - <mi>b</mi> - </mrow> - </munder> - <msub> - <mi mathvariant="italic">termPairWeight</mi> - <mrow> - <mi>a</mi> - <mi>b</mi> - </mrow> - </msub> - <mo>×</mo> - <msub> - <mi mathvariant="italic">pMaxTable</mi> - <mi>j</mi> - </msub> - </mrow> - </mfrac> - </mrow> - <annotation encoding="SnuggleTeX">\[ nativeProximity = \frac{\sum_j^mfieldWeight_j\sum_{ab}termPairWeight_{ab}(proxImp_j \times proxTable_j[dist_{ab} - 1] + (1 - proxImp_j) \times revProxTable_j[dist_{ba} - 1])}{\sum_j^mfieldWeight_j\sum_{ab}termPairWeight_{ab} \times pMaxTable_j} \]</annotation> - </semantics> - </math> - </div> -</figure> -<p> -where <em>m</em> is the number of index fields, - <em>ab</em> is a term pair searched for in field <em>j</em>, - <em>proxImp<sub>j</sub></em> is the <em>proximityImportance</em> for field <em>j</em>, - <em>proxTable<sub>j</sub></em> is the forward proximity boost table for field <em>j</em>, - <em>dist<sub>ab</sub></em> is the minimum distance between occurrences of query terms <em>a</em> - and <em>b</em> in field <em>j</em>, - (<em>a</em> occurs before <em>b</em>), - <em>revProxTable<sub>j</sub></em> is the reverse proximity boost table for field <em>j</em>, - <em>dist<sub>ba</sub></em> is the minimum distance between occurrences of query terms - <em>b</em> and <em>a</em> in field <em>j</em> (<em>b</em> occurs before <em>a</em>), - and <em>termPairWeight<sub>ab</sub></em> and <em>pMaxTable<sub>j</sub></em> are given below. -</p> -<p> - For each field <em>j</em> we consider all query terms searched in this field and generate a set of term pairs. - The <em>slidingWindowSize</em> parameter determines how many pairs that are generated. - With a sliding window of size 3 over the terms <em>a b c d</em>, - we first consider the terms <em>a b c</em>, then the terms <em>b c d</em>, - and finally the terms <em>c d</em>. - The following pairs are generated: <em>ab</em>, <em>ac</em>, <em>bc</em>, <em>bd</em>, and <em>cd</em>. -</p> -<figure style="font-size: 1.3rem"> - <div> - <math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> - <semantics> - <mrow> - <msub> - <mi mathvariant="italic">termPairWeight</mi> - <mrow> - <mi>a</mi> - <mi>b</mi> - </mrow> - </msub> - <mo>=</mo> - <msub> - <mi mathvariant="italic">connectedness</mi> - <mrow> - <mi>a</mi> - <mi>b</mi> - </mrow> - </msub> - <mo>×</mo> - <mfenced close=")" open="("> - <mrow> - <msub> - <mi mathvariant="italic">termSignificance</mi> - <mi>a</mi> - </msub> - <mo>×</mo> - <msub> - <mi mathvariant="italic">termWeight</mi> - <mi>a</mi> - </msub> - <mo>+</mo> - <msub> - <mi mathvariant="italic">termSignificance</mi> - <mi>b</mi> - </msub> - <mo>×</mo> - <msub> - <mi mathvariant="italic">termWeight</mi> - <mi>b</mi> - </msub> - </mrow> - </mfenced> - </mrow> - <annotation encoding="SnuggleTeX">\[ termPairWeight_{ab} = connectedness_{ab} \times (termSignificance_a \times termWeight_a + termSignificance_b \times termWeight_b) \]</annotation> - </semantics> - </math> - </div> -</figure> - -<figure style="font-size: 1.3rem"> - <div> - <math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> - <semantics> - <mrow> - <msub> - <mi mathvariant="italic">connectedness</mi> - <mrow> - <mi>a</mi> - <mi>c</mi> - </mrow> - </msub> - <mo>=</mo> - <mfrac> - <mrow> - <mi>min</mi> - <mfenced close=")" open="("> - <mrow> - <msub> - <mi mathvariant="italic">connectedness</mi> - <mrow> - <mi>a</mi> - <mi>b</mi> - </mrow> - </msub> - </mrow> - <mrow> - <msub> - <mi mathvariant="italic">connectedness</mi> - <mrow> - <mi>b</mi> - <mi>c</mi> - </mrow> - </msub> - </mrow> - </mfenced> - </mrow> - <mrow> - <mi>d</mi> - <mi>i</mi> - <mi>s</mi> - <msub> - <mi>t</mi> - <mrow> - <mi>a</mi> - <mi>c</mi> - </mrow> - </msub> - </mrow> - </mfrac> - </mrow> - <annotation encoding="SnuggleTeX">\[ connectedness_{ac} = \frac{min(connectedness_{ab}, connectedness_{bc})}{dist_{ac}} \]</annotation> - </semantics> - </math> - </div> -</figure> -<p> - where <em>dist<sub>ac</sub></em> is the distance between term <em>a</em> and <em>c</em> in the query. -</p> -<figure style="font-size: 1.3rem"> - <div> - <math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> - <semantics> - <mrow> - <msub> - <mi mathvariant="italic">pMaxTable</mi> - <mi>j</mi> - </msub> - <mo>=</mo> - <msub> - <mi mathvariant="italic">proxImp</mi> - <mi>j</mi> - </msub> - <mo>×</mo> - <mi>max</mi> - <mfenced close=")" open="("> - <mrow> - <msub> - <mi mathvariant="italic">proxTable</mi> - <mi>j</mi> - </msub> - </mrow> - </mfenced> - <mo>+</mo> - <mfenced close=")" open="("> - <mrow> - <mn>1</mn> - <mo>-</mo> - <msub> - <mi mathvariant="italic">proxImp</mi> - <mi>j</mi> - </msub> - </mrow> - </mfenced> - <mo>×</mo> - <mi>max</mi> - <mfenced close=")" open="("> - <mrow> - <msub> - <mi mathvariant="italic">revProxTable</mi> - <mi>j</mi> - </msub> - </mrow> - </mfenced> - </mrow> - <annotation encoding="SnuggleTeX">\[ pMaxTable_j = proxImp_j \times max(proxTable_j) + (1 - proxImp_j) \times max(revProxTable_j) \]</annotation> - </semantics> - </math> - </div> -</figure> -<p> - where <em>max(boostTable<sub>j</sub>)</em> is the max value in that table. - <em>pMaxTable<sub>j</sub></em> is 1 if table normalization is turned off - (see the property <em>nativeRank.useTableNormalization</em> in the - <a href="#configuration-properties">configuration properties</a> section). -</p> -<p> - The default behavior for <em>nativeProximity</em> is to consider all index fields and all query terms - pairs searching in these fields when calculating the score. - The calculation can be limited to a specified set of index fields as follows: -</p> -<p style="text-align: center;"> - <code>nativeProximity(<em>f1</em>, <em>f2</em>)</code> -</p> -<p> - In this case only query term pairs searching in index fields <em>f1</em> and <em>f2</em> are considered. -</p> -<p> - For multi-value fields, setting <a href="../schemas/schemas.html#rank-element-gap">element-gap</a> for the field in the rank profile enables distance calculation between adjacent elements. -</p> - -<h2 id="nativeAttributeMatch">nativeAttributeMatch</h2> -<p> - The <em>nativeAttributeMatch</em> feature captures how well query terms match searched attribute fields, - and is calculated as follows: -</p> -<figure style="font-size: 1.3rem"> - <div> - <math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> - <semantics> - <mrow> - <mi mathvariant="italic">nativeAttributeMatch</mi> - <mo>=</mo> - <mfrac> - <mrow> - <munderover> - <mo>∑</mo> - <mi>i</mi> - <mi>n</mi> - </munderover> - <msub> - <mi mathvariant="italic">termWeight</mi> - <mi>i</mi> - </msub> - <mo>×</mo> - <msub> - <mi mathvariant="italic">attributeWeight</mi> - <mi>j</mi> - </msub> - <mo>×</mo> - <mi>sign</mi> - <mfenced close=")" open="("> - <msub> - <mi>w</mi> - <mrow> - <mi>i</mi> - <mi>j</mi> - </mrow> - </msub> - </mfenced> - <mo>×</mo> - <msub> - <mi mathvariant="italic">weightTable</mi> - <mi>j</mi> - </msub> - <mfenced close="]" open="["> - <mrow> - <mi>abs</mi> - <mfenced close=")" open="("> - <msub> - <mi>w</mi> - <mrow> - <mi>i</mi> - <mi>j</mi> - </mrow> - </msub> - </mfenced> - </mrow> - </mfenced> - </mrow> - <mrow> - <munderover> - <mo>∑</mo> - <mi>i</mi> - <mi>n</mi> - </munderover> - <msub> - <mi mathvariant="italic">termWeight</mi> - <mi>i</mi> - </msub> - <mo>×</mo> - <msub> - <mi mathvariant="italic">attributeWeight</mi> - <mi>j</mi> - </msub> - <mo>×</mo> - <mi>max</mi> - <mfenced close=")" open="("> - <mrow> - <msub> - <mi mathvariant="italic">weightTable</mi> - <mi>j</mi> - </msub> - </mrow> - </mfenced> - </mrow> - </mfrac> - </mrow> - <annotation encoding="SnuggleTeX">\[ nativeAttributeMatch = \frac{\sum_i^ntermWeight_i \times attributeWeight_j \times sign(w_{ij}) \times weightTable_j[abs(w_{ij})]}{\sum_i^ntermWeight_i \times attributeWeight_j \times max(weightTable_j)} \]</annotation> - </semantics> - </math> - </div> -</figure> -<p> - where <em>n</em> is the number of query terms searched in attribute fields, - <em>weightTable<sub>j</sub></em> is the boost table for attribute <em>j</em>, - <em>max(weightTable<sub>j</sub>)</em> is the max value in that table - (1 if table normalization is turned off), - <em>sign(w<sub>ij</sub>)</em> is the sign of <em>w<sub>ij</sub></em>. - <em>w<sub>ij</sub></em> is dependent on the attribute type: -</p> -<ul> - <li><strong>Weighted set</strong>: equals the weight associated with the key - (represented by query term <em>i</em>) in attribute <em>j</em>.</li> - <li><strong>Array</strong>: equals the number of occurrences of query - term <em>i</em> in attribute <em>j</em>.</li> - <li><strong>Single</strong>: equals 1.</li> -</ul> -<p> - The default behavior for <em>nativeAttributeMatch</em> is to consider all query terms - searching in all attribute fields when calculating the score. - The calculation can be limited to a specified set of attribute fields as follows: -</p> -<p style="text-align: center;"> - <code>nativeAttributeMatch(<em>a1</em>, <em>a2</em>)</code> -</p> -<p> - In this case only query terms searching in attribute fields <em>a1</em> and <em>a2</em> are considered. -</p> - - - -<h2 id="nativeRank">nativeRank</h2> -<p> - The <em>nativeRank</em> feature is just a linear combination of the three other features, - and is calculated as follows: -</p> -<figure style="font-size: 1.3rem"> - <div> - <math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> - <semantics> - <mrow> - <mi mathvariant="italic">nativeRank</mi> - <mo>=</mo> - <mfrac> - <mrow> - <mi>f</mi> - <mi>m</mi> - <mi>w</mi> - <mo>×</mo> - <mi mathvariant="italic">nativeFieldMatch</mi> - <mo>+</mo> - <mi>p</mi> - <mi>w</mi> - <mo>×</mo> - <mi mathvariant="italic">nativeProximity</mi> - <mo>+</mo> - <mi>a</mi> - <mi>m</mi> - <mi>w</mi> - <mo>×</mo> - <mi mathvariant="italic">nativeAttributeMatch</mi> - </mrow> - <mrow> - <mi>f</mi> - <mi>m</mi> - <mi>w</mi> - <mo>+</mo> - <mi>p</mi> - <mi>w</mi> - <mo>+</mo> - <mi>a</mi> - <mi>m</mi> - <mi>w</mi> - </mrow> - </mfrac> - </mrow> - <annotation encoding="SnuggleTeX">\[ nativeRank = \frac{fmw \times nativeFieldMatch + pw \times nativeProximity + amw \times nativeAttributeMatch}{fmw + pw + amw} \]</annotation> - </semantics> - </math> - </div> -</figure> -<p> - where <em>fmw</em> is the <em>fieldMatchWeight</em>, - <em>pw</em> is the <em>proximityWeight</em>, - and <em>amw</em> is - the <em>attributeMatchWeight</em>. -</p><p> - The default behavior when calculating the native rank score - is to consider all query terms searching in all defined index fields and attribute fields. - In many cases though only a subset of these fields are of interest in the rank score calculation. - You can set up <em>nativeRank</em> for a subset of fields - by specifying the field names in the parameter list as follows: -</p> -<pre> -first-phase { - expression: nativeRank(title,body,tags) -} -</pre> -<p> - In this case we have two index fields (<em>title</em> and <em>body</em>) - and one attribute field (<em>tags</em>), - and the <em>nativeRank</em> feature is calculated based on the - features <em>nativeFieldMatch(title,body)</em>, <em>nativeProximity(title,body)</em>, - and <em>nativeAttributeMatch(tags)</em>. - Note that the CPU cost of calculating the native rank score is also reduced when specifying a subset of the fields. -</p> - - - -<h2 id="variables">Variables</h2> -<p> - This is a list of the common variables used in the formulas above: -</p> - -<!--table-to-list--> -<table class="table" id="nativerank-variables-table"> -<thead> -<tr><th>Variable</th><th>Description</th></tr> -</thead><tbody> -<tr> - <td><em>attributeWeight<sub>j</sub></em> - </td><td> - The weight of attribute field <em>j</em>. See the - <a href="../schemas/schemas.html#weight">schema reference</a> - for how to set this weight. The default value is 100. - </td> -</tr><tr> - <td><em>connectedness<sub>ab</sub></em> - </td><td> - The connectedness between query terms <em>a</em> and <em>b</em>. - </td> -</tr><tr> - <td><em>fieldLength<sub>j</sub></em> - </td><td> - The length of field <em>j</em> in number of words. - </td> -</tr><tr> - <td><em>fieldWeight<sub>j</sub></em> - </td><td> - The weight of index field <em>j</em>. See - the <a href="../schemas/schemas.html#weight">schema reference</a> - for how to set this weight. - The default value is 100. - </td> -</tr><tr> - <td><em>termSignificance<sub>i</sub></em> - </td><td> - The significance of query term <em>i</em>. - </td> -</tr><tr> - <td><em>termWeight<sub>i</sub></em> - </td><td> - The weight of query term <em>i</em>. - </td> -</tr> -<tbody> -</table> - - - -<h2 id="configuration-properties">Configuration properties</h2> -<p> -This is a comprehensive list of all the configuration properties to all native rank features: -</p> -<!--table-to-list--> -<table class="table" id="native-rank-parameters-table"> -<thead> -<tr><th>Feature</th><th>Parameter</th><th>Default</th><th>Description</th></tr> -</thead><tbody> -<tr> - <td><code>nativeFieldMatch(<em>fieldName</em>)</code></td> - <td><code>averageFieldLength</code></td> - <td>The actual length of the field in the given document.</td> - <td>When set this replaces the true field length in the nativeFieldMatch formula for all documents.</td> -</tr><tr> -<tr> - <td><code>nativeFieldMatch</code></td> - <td><code>firstOccurrenceTable</code></td> - <td>expdecay(8000,12.50)</td> - <td>The default table used when calculating boost for the first occurrence in a field.</td> -</tr><tr> - <td><code>nativeFieldMatch(<em>fieldName</em>)</code></td> - <td><code>firstOccurrenceTable</code></td> - <td>The value of <code>firstOccurrenceTable</code></td> - <td>The table used when calculating boost for the first occurrence in the given field.</td> -</tr><tr> - <td><code>nativeFieldMatch</code></td> - <td><code>occurrenceCountTable</code></td> - <td>loggrowth(1500,4000,19)</td> - <td>The default table used when calculating boost for the number of occurrences in a field.</td> -</tr><tr> - <td><code>nativeFieldMatch(<em>fieldName</em>)</code></td> - <td><code>occurrenceCountTable</code></td> - <td>The value of <code>occurrenceCountTable</code></td> - <td>The table used when calculating boost for the number of occurrences in the given field.</td> -</tr><tr> - <td><code>nativeFieldMatch</code></td> - <td><code>firstOccurrenceImportance</code></td> - <td>0.5</td> - <td>The default importance value used for weighting the boosts for first occurrence and number of occurrences in a field. This value should be in the interval [0, 1].</td> -</tr><tr> - <td><code>nativeFieldMatch(<em>fieldName</em>)</code></td> - <td><code>firstOccurrenceImportance</code></td> - <td>The value of <code>firstOccurrenceImportance</code></td> - <td>The importance value used for the given field.</td> -</tr><tr> - <td><code>nativeProximity</code></td> - <td><code>proximityTable</code></td> - <td>expdecay(500,3)</td> - <td>The default table used when calculating forward proximity boost in a field.</td> -</tr><tr> - <td><code>nativeProximity(<em>fieldName</em>)</code></td> - <td><code>proximityTable</code></td> - <td>The value of <code>proximityTable</code></td> - <td>The table used when calculating forward proximity boost in the given field.</td> -</tr><tr> - <td><code>nativeProximity</code></td> - <td><code>reverseProximityTable</code></td> - <td>expdecay(400,3)</td> - <td>The default table used when calculating reverse proximity boost in a field.</td> -</tr><tr> - <td><code>nativeProximity(<em>fieldName</em>)</code></td> - <td><code>reverseProximityTable</code></td> - <td>The value of <code>reverseProximityTable</code></td> - <td>The table used when calculating reverse proximity boost in the given field.</td> -</tr><tr> - <td><code>nativeProximity</code></td> - <td><code>proximityImportance</code></td> - <td>0.5</td> - <td>The default importance value used for weighting the boosts for forward and reverse proximity in a field. - This value should be in the interval [0, 1].</td> -</tr><tr> - <td><code>nativeProximity(<em>fieldName</em>)</code></td> - <td><code>proximityImportance</code></td> - <td>The value of <code>proximityImportance</code></td> - <td>The importance value used for the given field.</td> -</tr><tr> - <td><code>nativeProximity</code></td> - <td><code>slidingWindowSize</code></td> - <td>4</td> - <td>The size of the sliding window used when generating term pairs. - </td> -</tr><tr><td colspan="4">{% include deprecated.html content="The elementGap rank property is deprecated and will be removed in Vespa 9."%}</td> -</tr><tr> - <td><code>nativeProximity</code></td> - <td><p id="elementgap"><code>elementGap.<em>fieldName</em></code></p></td> - <td>infinity</td> - <td><p>The gap between positions in adjacent elements in multi-value fields. Use the <a href="../schemas/schemas.html#rank-element-gap">element-gap</a> rank setting instead.</p></td> -</tr><tr> - <td><code>nativeAttributeMatch</code></td> - <td><code>weightTable</code></td> - <td>linear(1,0)</td> - <td>The default table used when calculating boost for matching in an attribute field.</td> -</tr><tr> - <td><code>nativeAttributeMatch</code></td> - <td><code>weightTable.<em>attributeName</em></code></td> - <td>The value of <code>weightTable</code></td> - <td>The table used when calculating boost for matching in the given attribute.</td> -</tr><tr> - <td><code>nativeRank</code></td> - <td><code>fieldMatchWeight</code></td> - <td>100.0</td> - <td>How much to weight the score from <em>nativeFieldMatch</em>.</td> -</tr><tr> - <td><code>nativeRank</code></td> - <td><code>proximityWeight</code></td> - <td>25.0</td> - <td>How much to weight the score from <em>nativeProximity</em>. - If table normalization is turned off the default value is 100.0.</td> -</tr><tr> - <td><code>nativeRank</code></td> - <td><code>attributeMatchWeight</code></td> - <td>100.0</td> - <td>How much to weight the score from <em>nativeAttributeMatch</em>.</td> -</tr><tr> - <td><code>nativeRank</code></td> - <td><code>useTableNormalization</code></td> - <td>true</td> - <td>Whether we should use table normalization for the native rank features. - Set this property to <em>false</em> to turn off table normalization</td> -</tr> -<tbody> -</table> -<p> - For example, to override the <em>occurrenceCountTable</em> - and <em>reverseProximityTable</em> for the index field <em>content</em>, - add the following to the rank profile in the sd file: -</p> -<pre> -rank-properties { - nativeFieldMatch(content).occurrenceCountTable: "linear(0,0)" - nativeProximity(content).reverseProximityTable: "linear(0,0)" -} -</pre> -<p> - See the <a href="../schemas/schemas.html#rank-properties">search definitions</a> reference - for more information on rank-properties. -</p> - - -<h3 id="boost-tables">Boost tables</h3> -<p> - The following boost tables are supported by the native rank features: -</p> -<table class="table"> -<thead> -<tr><th>Name</th><th>Function</th><th>Description</th></tr> -</thead><tbody> -<tr> - <td>expdecay(w,t)</td> - <td><code style="white-space:nowrap">w * exp(-x/t)</code></td> - <td>Represents an exponential decay function where - <em>w</em> is the weight controlling the amplitude and - <em>t</em> is the tune parameter controlling the slope.</td> -</tr><tr> - <td>loggrowth(w,t,s)</td> - <td><code style="white-space:nowrap">w * log(1 + (x/s)) + t</code></td> - <td>Represents a logarithmic growth function where - <em>w</em> is the weight controlling the amplitude, - <em>t</em> is the tune parameter controlling the offset, - and <em>s</em> is a scale parameter controlling the sensitivity to the variable <em>x</em></td> -</tr><tr> - <td>linear(w,t)</td> - <td><code style="white-space:nowrap">w * x + t</code></td> - <td>Represents a linear function where - <em>w</em> controls the slope and <em>t</em> controls the offset.</td> -</tr> -<tbody> -</table> -<p> - The parameters <em>w</em>, <em>t</em>, and <em>s</em> are floating point numbers, - the same as the content of the tables. - The default table size is 256 with x in the interval [0,255]. - You can override this default size by specifying an optional last parameter to the table name. - For instance, if you use <em>linear(1.5,0,512)</em> you get a table with size 512 - populated with the result of evaluating the function <em>1.5*x + 0</em> for all x in the interval [0,511]. -</p> - - -<h3 id="rank-types">Rank types</h3> -<p> - Four predefined rank types are supported by <em>nativeRank</em>: - <em>about</em> (default), <em>identity</em>, <em>tags</em>, and <em>empty</em>. - Each type is associated with a set of boost tables that are used by the native rank features. - See the <a href="rank-types.html">rank type</a> document for detailed information on these type. -</p><p> - When setting up the sd file, either use one of the predefined rank types for a field, - or explicitly specify the boost tables to use for that field as a set of rank-properties. - If you don't specify anything you get the boost tables associated with the <em>about</em> type. - The <em>about</em> boost tables for <em>nativeFieldMatch</em> and <em>nativeProximity</em> - are already optimized for textual match, - while the boost table for <em>nativeAttributeMatch</em> is data dependent - and must be optimized for each use case. -</p> - - - -<h2 id="nativeRank-limitations">nativeRank limitations</h2> -<p> - The nativeRank feature is a pure text match scoring feature. - In particular, it does not take the following concepts into account for documents that match a query: -</p> -<ul> - <li> - Static rank or any other relevancy contribution that is based on a numeric value. - Use the <em>attribute</em> feature in a ranking expression to get this concept into the final relevancy score. - </li><li> - Geographical location of a match correlated to a location associated with the query. - Use the <em>distance</em> or <em>closeness</em> feature in a ranking expression to take this into account. - </li><li> - The age of the matching documents. - Use the <em>freshness</em> feature in a ranking expression to take this into account. - </li> -</ul> diff --git a/en/reference/ranking/nativerank.mdx b/en/reference/ranking/nativerank.mdx new file mode 100644 index 0000000000..5eed7ed545 --- /dev/null +++ b/en/reference/ranking/nativerank.mdx @@ -0,0 +1,278 @@ +--- +title: "nativeRank Reference" +sidebarTitle: "nativeRank" +--- + +The _nativeRank_ feature produces a reasonable text ranking score which is computed at an acceptable performance, and is a good candidate for [first phase](/en/ranking/phased-ranking) ranking. The _nativeRank_ feature is a linear combination of the normalized scores computed by the features _nativeFieldMatch_, _nativeProximity_, and _nativeAttributeMatch_. All these features are described in detail below. See the [configuration properties](#configuration-properties) section for how to configure the features. + +## nativeFieldMatch + +The _nativeFieldMatch_ feature captures how well query terms match searched index fields by looking at the number of times a term occurs in a field and how early in the field it occurs. The significance and weight of the terms are also taken into account such that unusual terms give a higher rank contribution than common ones. + +The score for _nativeFieldMatch_ is calculated as follows: + +$$ +\text{nativeFieldMatch} = +\frac{ +\left( +\sum_{i=1}^{n} +\text{termSignificance}_i \times \text{termWeight}_i +\right) +\left( +\sum_{j=1}^{m} +\text{fieldWeight}_j +\left( +\text{firstOccImp}_j \times \text{firstOccBoost}_{ij} ++ +(1 - \text{firstOccImp}_j) \times \text{numOccBoost}_{ij} +\right) +\right) +}{ +\left( +\sum_{i=1}^{n} +\text{termSignificance}_i \times \text{termWeight}_i +\right) +\left( +\sum_{j=1}^{m} +\text{fieldWeight}_j \times \text{fmMaxTable}_j +\right) +} +$$ + +where _n_ is the number of query terms searched in index fields, _m_ is the number of fields searched by query term _i_, _firstOccImpj_ is the _firstOccurrenceImportance_ for field _j_, and _firstOccBoostij_, _numOccBoostij_ and _fmMaxTablej_ are given below. + +$$ +\text{firstOccBoost}_{ij} = \text{firstOccurrenceTable}_j\left[\frac{\text{firstOcc}_{ij} \times \text{tableSize}_j}{\max(6, \text{fieldLength}_j)}\right] +$$ + +where _firstOccurrenceTablej_ is the boost table configured for field _j_, typically an expdecay function (see the [boost tables](#boost-tables) section below), _firstOccij_ is the first occurrence of query term _i_ in field _j_, and _tableSizej_ is the size of the boost table. + +$$ +\text{numOccBoost}_{ij} = \text{occurrenceCountTable}_j\left[\frac{\text{numOccs}_{ij} \times \text{tableSize}_j}{\max(6, \text{fieldLength}_j)}\right] +$$ + +where _occurrenceCountTablej_ is the boost table configured for field _j_, typically a loggrowth function (see the [boost tables](#boost-tables) section below), _numOccsij_ is the number of occurrences of query term _i_ in field _j_, and _tableSizej_ is the size of the boost table. + +$$ +\text{fmMaxTable}_j = \text{firstOccImp}_j \times \max(\text{firstOccurrenceTable}_j) + (1 - \text{firstOccImp}_j) \times \max(\text{occurrenceCountTable}_j) +$$ + +where \(\max(\text{boostTable}_j)\) is the max value in that table. _fmMaxTablej_ is 1 if table normalization is turned off (see the property _nativeRank.useTableNormalization_ in the [configuration properties](#configuration-properties) section). + +The default behavior for _nativeFieldMatch_ is to consider all query terms searching in all index fields when calculating the score. The calculation can be limited to a specified set of index fields as follows: + +`nativeFieldMatch(f1, f2)` + +In this case only query terms searching in index fields _f1_ and _f2_ are considered. + +## nativeProximity + +The _nativeProximity_ feature captures how near the matched query terms occur in searched index fields by looking at the word distance between query terms in query term pairs. Two query terms that are close to each other should give a higher score than two terms that are far from each other. + +The score for _nativeProximity_ is calculated as follows: + +$$ +\text{nativeProximity} = +\frac{ +\left( +\sum_{j=1}^{m} +\text{fieldWeight}_j +\right) +\left( +\sum_{ab} +\text{termPairWeight}_{ab} +\left( +\text{proxImp}_j \times \text{proxTable}_j[\text{dist}_{ab} - 1] ++ +(1 - \text{proxImp}_j) \times \text{revProxTable}_j[\text{dist}_{ba} - 1] +\right) +\right) +}{ +\left( +\sum_{j=1}^{m} +\text{fieldWeight}_j +\right) +\left( +\sum_{ab} +\text{termPairWeight}_{ab} \times \text{pMaxTable}_j +\right) +} +$$ + +where _m_ is the number of index fields, _ab_ is a term pair searched for in field _j_, _proxImpj_ is the _proximityImportance_ for field _j_, _proxTablej_ is the forward proximity boost table for field _j_, _distab_ is the minimum distance between occurrences of query terms _a_ and _b_ in field _j_, (_a_ occurs before _b_), _revProxTablej_ is the reverse proximity boost table for field _j_, _distba_ is the minimum distance between occurrences of query terms _b_ and _a_ in field _j_ (_b_ occurs before _a_), and _termPairWeightab_ and _pMaxTablej_ are given below. + +For each field _j_ we consider all query terms searched in this field and generate a set of term pairs. The _slidingWindowSize_ parameter determines how many pairs that are generated. With a sliding window of size 3 over the terms _a b c d_, we first consider the terms _a b c_, then the terms _b c d_, and finally the terms _c d_. The following pairs are generated: _ab_, _ac_, _bc_, _bd_, and _cd_. + +$$ +\text{termPairWeight}_{ab} = \text{connectedness}_{ab} \times (\text{termSignificance}_a \times \text{termWeight}_a + \text{termSignificance}_b \times \text{termWeight}_b) +$$ + +$$ +\text{connectedness}_{ac} = \frac{\min(\text{connectedness}_{ab}, \text{connectedness}_{bc})}{\text{dist}_{ac}} +$$ + +where _distac_ is the distance between term _a_ and _c_ in the query. + +$$ +\text{pMaxTable}_j = \text{proxImp}_j \times \max(\text{proxTable}_j) + (1 - \text{proxImp}_j) \times \max(\text{revProxTable}_j) +$$ + +where \(\max(\text{boostTable}_j)\) is the max value in that table. _pMaxTablej_ is 1 if table normalization is turned off (see the property _nativeRank.useTableNormalization_ in the [configuration properties](#configuration-properties) section). + +The default behavior for _nativeProximity_ is to consider all index fields and all query terms pairs searching in these fields when calculating the score. The calculation can be limited to a specified set of index fields as follows: + +`nativeProximity(f1, f2)` + +In this case only query term pairs searching in index fields _f1_ and _f2_ are considered. + +For multi-value fields, setting [element-gap](/en/reference/schemas/schemas#rank-element-gap) for the field in the rank profile enables distance calculation between adjacent elements. + +## nativeAttributeMatch + +The _nativeAttributeMatch_ feature captures how well query terms match searched attribute fields, and is calculated as follows: + +$$ +\text{nativeAttributeMatch} = +\frac{ +\left( +\sum_{i=1}^{n} +\text{termWeight}_i \times \text{attributeWeight}_j \times \text{sign}(w_{ij}) \times \text{weightTable}_j[abs(w_{ij})] +\right) +}{ +\left( +\sum_{i=1}^{n} +\text{termWeight}_i \times \text{attributeWeight}_j \times \max(\text{weightTable}_j) +\right) +} +$$ + +where _n_ is the number of query terms searched in attribute fields, _weightTablej_ is the boost table for attribute _j_, \(\max(\text{weightTable}_j)\) is the max value in that table (1 if table normalization is turned off), \(\text{sign}(w_{ij})\) is the sign of \(w_{ij}\). \(w_{ij}\) is dependent on the attribute type: + +- **Weighted set**: equals the weight associated with the key (represented by query term _i_) in attribute _j_. +- **Array**: equals the number of occurrences of query term _i_ in attribute _j_. +- **Single**: equals 1. + +The default behavior for _nativeAttributeMatch_ is to consider all query terms searching in all attribute fields when calculating the score. The calculation can be limited to a specified set of attribute fields as follows: + +`nativeAttributeMatch(a1, a2)` + +In this case only query terms searching in attribute fields _a1_ and _a2_ are considered. + +## nativeRank + +The _nativeRank_ feature is just a linear combination of the three other features, and is calculated as follows: + +$$ +\text{nativeRank} = +\frac{ +\text{fmw} \times \text{nativeFieldMatch} + \text{pw} \times \text{nativeProximity} + \text{amw} \times \text{nativeAttributeMatch} +}{ +\text{fmw} + \text{pw} + \text{amw} +} +$$ + +where _fmw_ is the _fieldMatchWeight_, _pw_ is the _proximityWeight_, and _amw_ is the _attributeMatchWeight_. + +The default behavior when calculating the native rank score is to consider all query terms searching in all defined index fields and attribute fields. In many cases though only a subset of these fields are of interest in the rank score calculation. You can set up _nativeRank_ for a subset of fields by specifying the field names in the parameter list as follows: + +```yaml +first-phase { + expression: nativeRank(title,body,tags) +} +``` + +In this case we have two index fields (_title_ and _body_) and one attribute field (_tags_), and the _nativeRank_ feature is calculated based on the features _nativeFieldMatch(title,body)_, _nativeProximity(title,body)_, and _nativeAttributeMatch(tags)_. Note that the CPU cost of calculating the native rank score is also reduced when specifying a subset of the fields. + +## Variables + +This is a list of the common variables used in the formulas above: + +- _attributeWeightj_: The weight of attribute field _j_. See the [schema reference](/en/reference/schemas/schemas#weight) for how to set this weight. The default value is 100. +- _connectednessab_: The connectedness between query terms _a_ and _b_. +- _fieldLengthj_: The length of field _j_ in number of words. +- _fieldWeightj_: The weight of index field _j_. See the [schema reference](/en/reference/schemas/schemas#weight) for how to set this weight. The default value is 100. +- _termSignificancei_: The significance of query term _i_. +- _termWeighti_: The weight of query term _i_. + +## Configuration properties + +This is a comprehensive list of all the configuration properties to all native rank features: + +- `nativeFieldMatch(fieldName)` -> `averageFieldLength`: The actual length of the field in the given document. When set this replaces the true field length in the nativeFieldMatch formula for all documents. +- `nativeFieldMatch(fieldName)` -> `firstOccurrenceTable`: The table used when calculating boost for the first occurrence in the given field. +- `nativeFieldMatch(fieldName)` -> `occurrenceCountTable`: The table used when calculating boost for the number of occurrences in the given field. +- `nativeFieldMatch(fieldName)` -> `firstOccurrenceImportance`: The importance value used for the given field. +- `nativeProximity` -> `proximityTable`: The table used when calculating forward proximity boost in a field. +- `nativeProximity(fieldName)` -> `proximityTable`: The table used when calculating forward proximity boost in the given field. +- `nativeProximity` -> `reverseProximityTable`: The table used when calculating reverse proximity boost in a field. +- `nativeProximity(fieldName)` -> `reverseProximityTable`: The table used when calculating reverse proximity boost in the given field. +- `nativeProximity` -> `proximityImportance`: The importance value used for the given field. +- `nativeProximity` -> `slidingWindowSize`: The size of the sliding window used when generating term pairs. + <Warning> + **Deprecated:** The elementGap rank property is deprecated and will be removed in Vespa 9. + </Warning> +- nativeProximity -> `elementGap.fieldName`: The gap between positions in adjacent elements in multi-value fields. Use the [element-gap](/en/reference/schemas/schemas#rank-element-gap) rank setting instead. +- `nativeAttributeMatch` -> `weightTable`: The table used when calculating boost for matching in an attribute field. +- `nativeAttributeMatch` -> `weightTable.attributeName`: The table used when calculating boost for matching in the given attribute. +- `nativeRank` -> `fieldMatchWeight`: How much to weight the score from _nativeFieldMatch_. +- `nativeRank` -> `proximityWeight`: How much to weight the score from _nativeProximity_. If table normalization is turned off the default value is 100.0. +- `nativeRank` -> `attributeMatchWeight`: How much to weight the score from _nativeAttributeMatch_. +- `nativeRank` -> `useTableNormalization`: Whether we should use table normalization for the native rank features. Set this property to _false_ to turn off table normalization + +For example, to override the _occurrenceCountTable_ and _reverseProximityTable_ for the index field _content_, add the following to the rank profile in the sd file: + +```bash +rank-properties { + nativeFieldMatch(content).occurrenceCountTable: "linear(0,0)" + nativeProximity(content).reverseProximityTable: "linear(0,0)" +} +``` + +See the [search definitions](/en/reference/schemas/schemas#rank-properties) reference for more information on rank-properties. + +### Boost tables + +The following boost tables are supported by the native rank features: + +<table className="vespa-table"> + <thead> + <tr> + <th>Name</th> + <th>Function</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>expdecay(w,t)</td> + <td><code>{`w * exp(-x/t)`}</code></td> + <td>Represents an exponential decay function where _w_ is the weight controlling the amplitude and _t_ is the tune parameter controlling the slope.</td> + </tr> + <tr> + <td>loggrowth(w,t,s)</td> + <td><code>{`w * log(1 + (x/s)) + t`}</code></td> + <td>Represents a logarithmic growth function where _w_ is the weight controlling the amplitude, _t_ is the tune parameter controlling the offset, and _s_ is a scale parameter controlling the sensitivity to the variable _x_</td> + </tr> + <tr> + <td>linear(w,t)</td> + <td><code>{`w * x + t`}</code></td> + <td>Represents a linear function where _w_ controls the slope and _t_ controls the offset.</td> + </tr> + </tbody> +</table> + +The parameters _w_, _t_, and _s_ are floating point numbers, the same as the content of the tables. The default table size is 256 with x in the interval [0,255]. You can override this default size by specifying an optional last parameter to the table name. For instance, if you use _linear(1.5,0,512)_ you get a table with size 512 populated with the result of evaluating the function \(1.5 \times x + 0\) for all x in the interval [0,511]. + +### Rank types + +Four predefined rank types are supported by _nativeRank_: _about_ (default), _identity_, _tags_, and _empty_. Each type is associated with a set of boost tables that are used by the native rank features. See the [rank type](/en/reference/ranking/rank-types) document for detailed information on these type. + +When setting up the sd file, either use one of the predefined rank types for a field, or explicitly specify the boost tables to use for that field as a set of rank-properties. If you don't specify anything you get the boost tables associated with the _about_ type. The _about_ boost tables for _nativeFieldMatch_ and _nativeProximity_ are already optimized for textual match, while the boost table for _nativeAttributeMatch_ is data dependent and must be optimized for each use case. + +## nativeRank limitations + +The nativeRank feature is a pure text match scoring feature. In particular, it does not take the following concepts into account for documents that match a query: + +- Static rank or any other relevancy contribution that is based on a numeric value. Use the _attribute_ feature in a ranking expression to get this concept into the final relevancy score. +- Geographical location of a match correlated to a location associated with the query. Use the _distance_ or _closeness_ feature in a ranking expression to take this into account. +- The age of the matching documents. Use the _freshness_ feature in a ranking expression to take this into account. \ No newline at end of file diff --git a/en/reference/ranking/rank-feature-configuration.html b/en/reference/ranking/rank-feature-configuration.html deleted file mode 100644 index b8985842bc..0000000000 --- a/en/reference/ranking/rank-feature-configuration.html +++ /dev/null @@ -1,379 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Rank Feature Configuration" -redirect_from: -- /en/reference/rank-feature-configuration.html ---- - -<p> - For some <a href="rank-features.html">rank features</a>, - it is possible to set configuration variables for how the features are calculated. - For features that are per field or attribute, - the variables are set separately per field/attribute. -</p> - - - -<h2 id="properties">Properties</h2> -<p> - Rank Features configuration properties are set by adding the following to the rank profile: -</p> -<pre> -rank-properties { - <featurename>.<configuration-property>: <value> -} -</pre> -<p> - Where <em><featurename></em> is the name of a feature class (feature name up to the first dot), - <em><configuration-property></em> is a property from the list below, - appropriate for the feature, and <em><value></em> is either a number of a quoted string. Example: -</p> -<pre> -rank-profile my-profile inherits default { - rank-properties { - fieldMatch(title).maxAlternativeSegmentations: 10 - fieldMatch(title).maxOccurrences: 5 - bm25(title).k1: 1.5 - bm25(title).b: 0.85 - bm25(title).averageFieldLength: 200 - elementwise(bm25(content),x,float).k1: 1.4 - elementwise(bm25(content),x,float).b: 0.87 - elementwise(bm25(content),x,float).averageElementLength: 50 - elementSimilarity(tags).output.sumWeightSquared: "sum((0.35*p+0.15*o+0.30*q+0.20*f)*w^2)" - elementSimilarity(tags).output.avgWeightSquared: "avg((0.35*p+0.15*o+0.30*q+0.20*f)*w^2)" - elementSimilarity(tags).output.sumWeight: "sum(w)" - } -} -</pre> -<p> - For features missing from the list of properties below a rank property can be set with - another syntax, e.g. setting <em>averageFieldLength</em> for <em>my_field</em> for - the <em>nativeFieldMatch</em> feature can be done as: -</p> -<pre> - nativeFieldMatch.averageFieldLength.my_field: 512 -</pre> -<p> - Rank profiles are inherited like other content of rank profiles. -</p> - - - -<h2 id="List of properties">List of properties</h2> - -<p>An incomplete list of rank properties by the feature they apply to.</p> - -<table class="table"> - <thead> - <tr><th>Feature</th><th>Parameter</th><th>Default</th><th>Description</th></tr> - </thead><tbody> - -<tr><td>term</td> - <td>numTerms</td> - <td>5</td> - <td><p id="term">The number of terms for which this is included in the rank features dump in the summary</p></td></tr> - -<tr><td rowspan="3"><a href="rank-features.html#bm25">bm25(<em>fieldname</em>)</a></td> - <td>k1</td> - <td>1.2</td> - <td><p id="bm25">Used to limit how much a single query term can affect the score for a document.</p></td></tr> - -<tr> - <td>b</td> - <td>0.75</td> - <td>Used to control the effect of the field length compared to the average field length.</td></tr> - -<tr> - <td>averageFieldLength</td> - <td>Automatically calculated per field per content node for <a href="../applications/services/content.html#document">indexed search</a>, - 100 for <a href="../../performance/streaming-search.html">streaming search</a>.</td> - <td>Used to set an explicit value for the average field length (in number of words). - When using <a href="../../performance/streaming-search.html#differences-in-streaming-search">streaming search</a>, - no index structures are generated, and the average field length is not automatically calculated. - Instead, manually set an average field length for a more precise BM25 score. - </td> -</tr> - -<tr> - <td rowspan="3"> - <p id="elementwise-bm25"> - <a href="rank-features.html#elementwise-bm25">elementwise(bm25(<em>fieldname</em>,x,<em>celltype</em>))</a> - </p> - </td> - <td>k1</td> - <td>1.2</td> - <td> - <p> - Used to limit how much a single query term can affect the score for a document. - Note that <code style="font-weight: bold;">bm25(<em>fieldname</em>).k1</code> will be used as a fallback before the default. - </p> - </td> -</tr> - -<tr> - <td>b</td> - <td>0.75</td> - <td> - <p> - Used to control the effect of the element length compared to the average element length. - Note that <code style="font-weight: bold;">bm25(<em>fieldname</em>).b</code> will be used as a fallback before the default. - </p> - </td> -</tr> - -<tr> - <td>averageElementLength</td> - <td> - <p> - Automatically calculated per field element per content node for <a href="../applications/services/content.html#document">indexed search</a>, - 100 for <a href="../../performance/streaming-search.html">streaming search</a>. - </p> - </td> - <td> - <p> - Used to set an explicit value for the average element length (in number of words). - When using <a href="../../performance/streaming-search.html#differences-in-streaming-search">streaming search</a>, - no index structures are generated and the average element length is not automatically calculated. - Instead, manually set an average element length for a more precise elementwise bm25 score. - It should also be manually set for multi-node indexed search to get consistent scoring across the nodes. - Note that <code style="font-weight: bold;">bm25(<em>fieldname</em>).averageFieldLength</code> will be used as a fallback before the default. - </p> - </td> -</tr> - -<tr><td>nativeRank</td> - <td></td> - <td></td> - <td>See the <a href="nativerank.html#configuration-properties">nativeRank configuration</a> documentation</td></tr> - -<tr><td>nativeFieldMatch</td> - <td></td> - <td></td> - <td>See the <a href="nativerank.html#configuration-properties">nativeRank configuration</a> documentation</td></tr> - -<tr><td>nativeProximity</td> - <td></td> - <td></td> - <td>See the <a href="nativerank.html#configuration-properties">nativeRank configuration</a> documentation</td></tr> - -<tr><td rowspan="10">fieldMatch</td> - <td>proximityLimit - </td><td>10</td> - <td><p id="fieldMatch">The maximum allowed gap within a segment.</p></td></tr> - -<tr> - <td>proximityTable</td> - <td>1/(2^(i/2)/3) for i in 9..0 followed by 1/2^(i/2) for i in 0..10</td> - <td> - The proximity table deciding the importance of separations of various distances, - The table must have size proximityLimit*2+1, where the first half is for reverse direction distances. - The table must only contain values between 0 and 1, where 1 is "perfect" and 0 is "worst".</td></tr> - -<tr> - <td>maxAlternativeSegmentations</td> - <td>10000</td> - <td> - The maximum number of <em>alternative</em> segmentations allowed in addition to the first one found. - This will prefer to not consider iterations on segments that are far out in the field, - and which start late in the query.</td></tr> - -<tr> - <td>maxOccurrences</td> - <td>100</td> - <td> - The number of occurrences of each word is normalized against. - This should be set as the number above which additional occurrences of the term have no real significance.</td></tr> - -<tr> - <td>proximityCompletenessImportance</td> - <td>0.9</td> - <td> - A number between 0 and 1 that determines the importance of field completeness in relation to - query completeness in the <code>match</code> and <code>completeness</code> metrics.</td></tr> - -<tr> - <td>relatednessImportance</td> - <td>0.9</td> - <td> - The normalized importance of relatedness used in the <code>match</code> metric.</td></tr> - -<tr> - <td>earlinessImportance</td> - <td>0.05</td> - <td> - The importance of the match occurring early in the query, relative to segmentProximityImportance, - occurrenceImportance and proximityCompletenessImportance in the <code>match</code> metric.</td></tr> - -<tr> - <td>segmentProximityImportance</td> - <td>0.05</td> - <td> - The importance of multiple segments being close to each other, relative to earlinessImportance, - occurrenceImportance and proximityCompletenessImportance in the <code>match</code> metric.</td></tr> - -<tr> - <td>occurrenceImportance</td> - <td>0.05</td> - <td> - The importance of having many occurrences of the query terms, relative to earlinessImportance, - segmentProximityImportance and proximityCompletenessImportance in the <code>match</code> metric.</td></tr> - -<tr> - <td>fieldCompletenessImportance</td> - <td>0.05</td> - <td> - A number between 0 and 1 that determines the importance of field completeness in relation to - query completeness in the <code>match</code> and <code>completeness</code> metrics.</td></tr> - -<tr><td rowspan="2">fieldTermMatch</td> - <td>numTerms</td> - <td>5</td> - <td> - <p id="fieldTermMatch">The number of terms for which this is included in the rank features dump in the summary</p> - </td></tr> - -<tr> - <td>numTerms.<em>fieldName</em></td> - <td>5</td> - <td> - The number of terms for which this is included in the rank features dump - in the summary for the specified field. Also configurable using - <code style="font-weight: bold;">fieldTermMatch(<em>fieldName</em>).numTerms</code> as the property name. - </td></tr> - -<tr><td>elementCompleteness</td> - <td>fieldCompletenessImportance</td> - <td>0.5</td> - <td> - <p id="elementCompleteness"> - Higher values favor field completeness, lower values favor query completeness. - Adjusting this parameter will also affect which element is selected as the best. - </p> - </td></tr> - -<tr><td rowspan="2">elementSimilarity</td> - <td>output.default</td> - <td>"max( (0.35*p + 0.15*o + 0.30*q + 0.20*f) * w)"</td> - <td> - <p id="elementSimilarity"> - Describes how the default output should be calculated. The value must be on the - form <code>aggregator(expression)</code>. The expression is used to - combine the low-level similarity measures between the query and - individual elements in the field that matched the query. - The aggregator will be used to aggregate the output of the expression across matched elements. - The available aggregators are <code>max</code>, <code>avg</code> - and <code>sum</code>. The available expression operators - are <code>+</code>, <code>-</code>, <code>*</code>, <code>/</code> - and <code>^</code>. Parentheses may be used to override default - operator precedence. Note that you must quote the expression using <code>"expression"</code>. - Terminals can be numbers or any of the following symbols: - </p> -<table> -<tr><td><strong>p</strong></td><td>normalized <strong>proximity</strong> measure</td></tr> -<tr><td><strong>o</strong></td><td>normalized term <strong>ordering</strong> measure</td></tr> -<tr><td><strong>q</strong></td><td>normalized <strong>query</strong> coverage</td></tr> -<tr><td><strong>f</strong></td><td>normalized <strong>field</strong> coverage</td></tr> -<tr><td><strong>w</strong></td><td>element <strong>weight</strong></td></tr> -</table> -</td></tr> - -<tr> - <td>output.name</td> - <td>N/A</td> - <td>Define an additional feature output called <code>name</code>. - The value describes how the output should be calculated and has the same syntax - as the <code>default</code> output described above. - Example create a new output which can be accessed as <code>elementSimilarity(tags).sumW</code>: - <code>elementSimilarity(tags).output.sumW: "sum(w)"</code> - </td></tr> - -<tr><td rowspan="2">attributeMatch</td> - <td>fieldCompletenessImportance</td> - <td>0.05</td> - <td> - <p id="attributeMatch"> - A number between 0 and 1 that determines the importance of field completeness in relation to - query completeness in the <code>match</code> and <code>completeness</code> metrics. - </p> - </td></tr> - -<tr> - <td>maxWeight</td> - <td>256</td> - <td> - The maximal weight when calculating <code>attributeMatch(<em>name</em>).normalizedWeight</code>. - Weights higher than this will not have any effect on this feature.</td></tr> - -<tr><td rowspan="3">closeness</td> - <td>maxDistance</td> - <td>9013305.0</td> - <td> - <p id="closeness"> - The maximal distance when calculating <code>closeness(<em>name</em>)</code>. - Distances higher than this will not have any effect on this feature. - The default is about 1000 km (1 km is about 9013.305 microdegrees). - </p> - </td></tr> - -<tr> - <td>scaleDistance</td> - <td>45066.525</td> - <td> - Basic scale for distances when calculating <code>closeness(<em>name</em>).logscale</code>. - The default is about 5 km. - {% include deprecated.html content="use <code>halfResponse</code> instead"%} - </td></tr> - -<tr> - <td>halfResponse</td> - <td>593861.739</td> - <td> - The distance that should give an output of 0.5 when calculating - <code>closeness(<em>name</em>).logscale</code>. - The default is about 65.89 km (must be in the range [1, maxDistance/2>). - Use this parameter to fine-tune the distance range where half of the dynamics - of the logscale function will be used.</td></tr> - -<tr><td rowspan="2">freshness</td> - <td>maxAge</td> - <td>3*30*24*60*60</td> - <td> - <p id="freshness"> - The maximal age in seconds when calculating <code>freshness(<em>name</em>)</code>. - Ages older than this will not have any effect on this feature. - The default is about 3 months. - </p> - </td></tr> - -<tr> - <td>halfResponse</td> - <td>7*24*60*60</td> - <td> - The age in seconds that should give an output of 0.5 when calculating - <code>freshness(<em>name</em>).logscale</code>. - The default is 7 days (must be in the range [1, maxAge/2>). - Use this parameter to fine-tune the age range where half of the dynamics - of the logscale function will be used.</td></tr> - -<tr><td>random</td> - <td>seed</td> - <td>Current time in microseconds</td> - <td><p id="random">The random seed.</p></td></tr> - -<tr><td>randomNormal</td> - <td>seed</td> - <td>Current time in microseconds</td> - <td><p id="randomNormal">The random seed for randomNormal.</p></td></tr> - -<tr><td>foreach</td> - <td>maxTerms</td> - <td>16</td> - <td> - <p id="foreach"> - Specifies how many query term indices to iterate over ([0, <code>maxTerms</code>>) - when using dimension <code>terms</code>. - </p> - </td></tr> - -</tbody> -</table> diff --git a/en/reference/ranking/rank-feature-configuration.mdx b/en/reference/ranking/rank-feature-configuration.mdx new file mode 100644 index 0000000000..8ed242d546 --- /dev/null +++ b/en/reference/ranking/rank-feature-configuration.mdx @@ -0,0 +1,270 @@ +--- +title: "Rank Feature Configuration" +--- + +For some [rank features](/en/reference/ranking/rank-features), it is possible to set configuration variables for how the features are calculated. For features that are per field or attribute, the variables are set separately per field/attribute. + +## Properties + +Rank Features configuration properties are set by adding the following to the rank profile: + +```js +rank-properties { + <featurename>.<configuration-property>: <value> +} +``` + +Where _\<featurename\>_ is the name of a feature class (feature name up to the first dot), _\<configuration-property\>_ is a property from the list below, appropriate for the feature, and _\<value\>_ is either a number of a quoted string. Example: + +```js +rank-profile my-profile inherits default { + rank-properties { + fieldMatch(title).maxAlternativeSegmentations: 10 + fieldMatch(title).maxOccurrences: 5 + bm25(title).k1: 1.5 + bm25(title).b: 0.85 + bm25(title).averageFieldLength: 200 + elementwise(bm25(content),x,float).k1: 1.4 + elementwise(bm25(content),x,float).b: 0.87 + elementwise(bm25(content),x,float).averageElementLength: 50 + elementSimilarity(tags).output.sumWeightSquared: "sum((0.35*p+0.15*o+0.30*q+0.20*f)*w^2)" + elementSimilarity(tags).output.avgWeightSquared: "avg((0.35*p+0.15*o+0.30*q+0.20*f)*w^2)" + elementSimilarity(tags).output.sumWeight: "sum(w)" + } +} +``` + +For features missing from the list of properties below a rank property can be set with another syntax, e.g. setting _averageFieldLength_ for _my\_field_ for the _nativeFieldMatch_ feature can be done as: + +```bash +nativeFieldMatch.averageFieldLength.my_field: 512 +``` + +Rank profiles are inherited like other content of rank profiles. + +## List of properties + +An incomplete list of rank properties by the feature they apply to. + +<table className="vespa-table"> + <thead> + <tr> + <th>Feature</th> + <th>Parameter</th> + <th>Default</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>term</td> + <td>numTerms</td> + <td>5</td> + <td>The number of terms for which this is included in the rank features dump in the summary</td> + </tr> + <tr> + <td><a href="/en/reference/ranking/rank-features#bm25" aria-label="bm25(*fieldname*)">bm25(*fieldname*)</a></td> + <td>k1</td> + <td>1.2</td> + <td>Used to limit how much a single query term can affect the score for a document.</td> + </tr> + <tr> + <td></td> + <td>b</td> + <td>0.75</td> + <td>Used to control the effect of the field length compared to the average field length.</td> + </tr> + <tr> + <td></td> + <td>averageFieldLength</td> + <td>Automatically calculated per field per content node for <a href="/en/reference/applications/services/content#document" aria-label="indexed search">indexed search</a>, 100 for <a href="/en/performance/streaming-search" aria-label="streaming search">streaming search</a>.</td> + <td>Used to set an explicit value for the average field length (in number of words). When using <a href="/en/performance/streaming-search#differences-in-streaming-search" aria-label="streaming search">streaming search</a>, no index structures are generated, and the average field length is not automatically calculated. Instead, manually set an average field length for a more precise BM25 score.</td> + </tr> + <tr> + <td><a href="/en/reference/ranking/rank-features#elementwise-bm25" aria-label="elementwise(bm25(*fieldname*,x,*celltype*))">elementwise(bm25(*fieldname*,x,*celltype*))</a></td> + <td>k1</td> + <td>1.2</td> + <td>Used to limit how much a single query term can affect the score for a document. Note that <code>{`bm25(fieldname).k1`}</code> will be used as a fallback before the default.</td> + </tr> + <tr> + <td></td> + <td>b</td> + <td>0.75</td> + <td>Used to control the effect of the element length compared to the average element length. Note that <code>{`bm25(fieldname).b`}</code> will be used as a fallback before the default.</td> + </tr> + <tr> + <td></td> + <td>averageElementLength</td> + <td>Automatically calculated per field element per content node for <a href="/en/reference/applications/services/content#document" aria-label="indexed search">indexed search</a>, 100 for <a href="/en/performance/streaming-search" aria-label="streaming search">streaming search</a>.</td> + <td>Used to set an explicit value for the average element length (in number of words). When using <a href="/en/performance/streaming-search#differences-in-streaming-search" aria-label="streaming search">streaming search</a>, no index structures are generated and the average element length is not automatically calculated. Instead, manually set an average element length for a more precise elementwise BM25 score. It should also be manually set for multi-node indexed search to get consistent scoring across the nodes. Note that <code>{`bm25(fieldname).averageFieldLength`}</code> will be used as a fallback before the default.</td> + </tr> + <tr> + <td>nativeRank</td> + <td></td> + <td></td> + <td>See the <a href="/en/reference/ranking/nativerank#configuration-properties" aria-label="nativeRank configuration">nativeRank configuration</a> documentation</td> + </tr> + <tr> + <td>nativeFieldMatch</td> + <td></td> + <td></td> + <td>See the <a href="/en/reference/ranking/nativerank#configuration-properties" aria-label="nativeRank configuration">nativeRank configuration</a> documentation</td> + </tr> + <tr> + <td>nativeProximity</td> + <td></td> + <td></td> + <td>See the <a href="/en/reference/ranking/nativerank#configuration-properties" aria-label="nativeRank configuration">nativeRank configuration</a> documentation</td> + </tr> + <tr> + <td>fieldMatch</td> + <td>proximityLimit</td> + <td>10</td> + <td>The maximum allowed gap within a segment.</td> + </tr> + <tr> + <td></td> + <td>proximityTable</td> + <td>1/(2^(i/2)/3) for i in 9..0 followed by 1/2^(i/2) for i in 0..10</td> + <td>The proximity table deciding the importance of separations of various distances. The table must have size proximityLimit*2+1, where the first half is for reverse direction distances. The table must only contain values between 0 and 1, where 1 is "perfect" and 0 is "worst".</td> + </tr> + <tr> + <td></td> + <td>maxAlternativeSegmentations</td> + <td>10000</td> + <td>The maximum number of *alternative* segmentations allowed in addition to the first one found. This will prefer to not consider iterations on segments that are far out in the field, and which start late in the query.</td> + </tr> + <tr> + <td></td> + <td>maxOccurrences</td> + <td>100</td> + <td>The number of occurrences of each word is normalized against. This should be set as the number above which additional occurrences of the term have no real significance.</td> + </tr> + <tr> + <td></td> + <td>proximityCompletenessImportance</td> + <td>0.9</td> + <td>A number between 0 and 1 that determines the importance of field completeness in relation to query completeness in the <code>{`match`}</code> and <code>{`completeness`}</code> metrics.</td> + </tr> + <tr> + <td></td> + <td>relatednessImportance</td> + <td>0.9</td> + <td>The normalized importance of relatedness used in the <code>{`match`}</code> metric.</td> + </tr> + <tr> + <td></td> + <td>earlinessImportance</td> + <td>0.05</td> + <td>The importance of the match occurring early in the query, relative to segmentProximityImportance, occurrenceImportance and proximityCompletenessImportance in the <code>{`match`}</code> metric.</td> + </tr> + <tr> + <td></td> + <td>segmentProximityImportance</td> + <td>0.05</td> + <td>The importance of multiple segments being close to each other, relative to earlinessImportance, occurrenceImportance and proximityCompletenessImportance in the <code>{`match`}</code> metric.</td> + </tr> + <tr> + <td></td> + <td>occurrenceImportance</td> + <td>0.05</td> + <td>The importance of having many occurrences of the query terms, relative to earlinessImportance, segmentProximityImportance and proximityCompletenessImportance in the <code>{`match`}</code> metric.</td> + </tr> + <tr> + <td></td> + <td>fieldCompletenessImportance</td> + <td>0.05</td> + <td>A number between 0 and 1 that determines the importance of field completeness in relation to query completeness in the <code>{`match`}</code> and <code>{`completeness`}</code> metrics.</td> + </tr> + <tr> + <td>fieldTermMatch</td> + <td>numTerms</td> + <td>5</td> + <td>The number of terms for which this is included in the rank features dump in the summary</td> + </tr> + <tr> + <td></td> + <td>numTerms.*fieldName*</td> + <td>5</td> + <td>The number of terms for which this is included in the rank features dump in the summary for the specified field. Also configurable using <code>{`fieldTermMatch(fieldName).numTerms`}</code> as the property name.</td> + </tr> + <tr> + <td>elementCompleteness</td> + <td>fieldCompletenessImportance</td> + <td>0.5</td> + <td>Higher values favor field completeness, lower values favor query completeness. Adjusting this parameter will also affect which element is selected as the best.</td> + </tr> + <tr> + <td>elementSimilarity</td> + <td>output.default</td> + <td>"max( (0.35*p + 0.15*o + 0.30*q + 0.20*f) * w)"</td> + <td>Describes how the default output should be calculated. The value must be on the form <code>{`aggregator(expression)`}</code>. The expression is used to combine the low-level similarity measures between the query and individual elements in the field that matched the query. The aggregator will be used to aggregate the output of the expression across matched elements. The available aggregators are <code>{`max`}</code>, <code>{`avg`}</code>, and <code>{`sum`}</code>. The available expression operators are <code>{`+`}</code>, <code>{`-`}</code>, <code>{`*`}</code>, <code>{`/`}</code>, and <code>{`^`}</code>. Parentheses may be used to override default operator precedence. Note that you must quote the expression using <code>{`"expression"`}</code>. <br/><br/>Terminals can be numbers or any of the following symbols:<br/><br/> | Symbol | Meaning | <br/> | -------- | ---------------------------- | <br/> | **p** | normalized proximity measure | <br/> | **o** | normalized ordering measure | <br/> | **q** | normalized query coverage | <br/> | **f** | normalized field coverage | <br/> | **w** | element weight | </td> + </tr> + <tr> + <td></td> + <td>output.name</td> + <td>N/A</td> + <td>Define an additional feature output called <code>{`name`}</code>. The value describes how the output should be calculated and has the same syntax as the <code>{`default`}</code> output described above. Example create a new output which can be accessed as <code>{`elementSimilarity(tags).sumW`}</code>:<br/><code>{`elementSimilarity(tags).output.sumW: "sum(w)"`}</code></td> + </tr> + <tr> + <td>attributeMatch</td> + <td>fieldCompletenessImportance</td> + <td>0.05</td> + <td>A number between 0 and 1 that determines the importance of field completeness in relation to query completeness in the <code>{`match`}</code> and <code>{`completeness`}</code> metrics.</td> + </tr> + <tr> + <td></td> + <td>maxWeight</td> + <td>256</td> + <td>The maximal weight when calculating <code>{`attributeMatch(name).normalizedWeight`}</code>. Weights higher than this will not have any effect on this feature.</td> + </tr> + <tr> + <td>closeness</td> + <td>maxDistance</td> + <td>9013305.0</td> + <td>The maximal distance when calculating <code>{`closeness(name)`}</code>. Distances higher than this will not have any effect on this feature. The default is about 1000 km (1 km is about 9013.305 microdegrees).</td> + </tr> + <tr> + <td></td> + <td>scaleDistance</td> + <td>45066.525</td> + <td>Basic scale for distances when calculating <code>{`closeness(name).logscale`}</code>. The default is about 5 km.<br/>**Deprecated:** use <code>{`halfResponse`}</code> instead</td> + </tr> + <tr> + <td></td> + <td>halfResponse</td> + <td>593861.739</td> + <td>The distance that should give an output of 0.5 when calculating <code>{`closeness(name).logscale`}</code>. The default is about 65.89 km (must be in the range [1, maxDistance/2>). Use this parameter to fine-tune the distance range where half of the dynamics of the logscale function will be used.</td> + </tr> + <tr> + <td>freshness</td> + <td>maxAge</td> + <td>3*30*24*60*60</td> + <td>The maximal age in seconds when calculating <code>{`freshness(name)`}</code>. Ages older than this will not have any effect on this feature. The default is about 3 months.</td> + </tr> + <tr> + <td></td> + <td>halfResponse</td> + <td>7*24*60*60</td> + <td>The age in seconds that should give an output of 0.5 when calculating <code>{`freshness(name).logscale`}</code>. The default is 7 days (must be in the range [1, maxAge/2>). Use this parameter to fine-tune the age range where half of the dynamics of the logscale function will be used.</td> + </tr> + <tr> + <td>random</td> + <td>seed</td> + <td>Current time in microseconds</td> + <td>The random seed.</td> + </tr> + <tr> + <td>randomNormal</td> + <td>seed</td> + <td>Current time in microseconds</td> + <td>The random seed for randomNormal.</td> + </tr> + <tr> + <td>foreach</td> + <td>maxTerms</td> + <td></td> + <td></td> + </tr> + </tbody> +</table> \ No newline at end of file diff --git a/en/reference/ranking/rank-features.html b/en/reference/ranking/rank-features.html deleted file mode 100644 index f86975c60d..0000000000 --- a/en/reference/ranking/rank-features.html +++ /dev/null @@ -1,1886 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Rank Feature Reference" -redirect_from: -- /en/reference/rank-features.html ---- - -<p> -This is the list of the rank features in Vespa. -These features are available during document ranking for combination into a complete rank score by a -<a href="ranking-expressions.html">ranking expression</a>. -The features are a combination of coarse grained features suitable for handwritten expressions, -and finer grained features suitable for machine learning. -</p><p> -See also <a href="../../basics/ranking.html">the overview of the ranking framework</a>, and -<a href="rank-feature-configuration.html">rank feature configuration parameters</a>. -Notes: -</p> -<ul> - <li> - Types: All rank feature values are doubles or tensors. Integers are converted to exact whole value doubles. - String values are converted to exact whole value doubles using a hash function. - String literals in ranking expressions are converted using the same hash function, - to enable equality tests on string values. - </li> - <li> - Features which are <em>normalized</em> are between 0 and 1, where 0 is always the minimum and 1 the maximum. - Normalized features should normally be preferred because they are more easily combined by - <a href="ranking-expressions.html">ranking expressions</a> into a complete normalized score. - </li> - <li> - A query may override <em>any</em> rank feature value by submitting that value as a feature with the query. - </li> - <li> - Some features have parameters. It is always allowed to quote parameters with <em>"</em>. - Nested quotes are not allowed and must be escaped using <em>\</em>. - Parameters that can be parsed as feature names may be left unquoted. - Examples: <em>foo(bar(baz(5.5)))</em>, <em>foo("bar(\"baz(\\\"5.5\\\")\")")</em>, <em>foo("need quote")</em> - </li> -</ul> - - -<h2 id="feature-list">Feature list</h2> - -<!--table-to-list--> -<table class="table" id="rank-feature-table"> - - <tr><td colspan="3"></td></tr> -<tr><td colspan="3"><h3 id="query-features">Query features</h3></td></tr> -<tr class="trx"><th class="thx">Feature name</th><th class="thx">Default</th><th class="thx">Description</th></tr> - -<tr><td>query(<em>value</em>)</td> - <td>0</td> - <td> - <p id="query(value)"> - An application specific feature submitted with the query, - see <a href="../../ranking/ranking-expressions-features.html#using-query-variables">using the query feature</a>. - </p> - </td></tr> - -<tr><td>term(<em>n</em>).significance</td> - <td>0</td> - <td><p id="term(n).significance"> - A normalized number (between 0.0 and 1.0) describing the significance - of the term; used as a multiplier or weighting factor by many other text matching rank features. - </p><p> - This should ideally be set by a searcher in the container for global correctness - as each node will estimate the significance values from the local corpus. - Use the - <a href="https://javadoc.io/doc/com.yahoo.vespa/container-search/latest/com/yahoo/prelude/query/TaggableItem.html#setSignificance(double)"> - Java API for significance</a> or - <a href="../querying/yql.html#significance">YQL annotation for significance</a>. - </p><p> - As a fallback, a significance based on Robertson-Sparck-Jones term - weighting is used; it is logarithmic from 1.0 for rare terms down - to 0.5 for common terms (those occurring in every document seen). - </p><p> - Note that "rare" is defined as a frequency of 0.000001 or less. - This is the term document frequency (how many documents contain the - term out of all documents that can be observed), so you cannot get 1.0 - as the fallback until you actually have a large number of documents - (minimum 1 million) in the same search process. - </p><p> - See <a href="rank-feature-configuration.html#term">numTerms</a> config.</p> -</td></tr> - -<tr><td>term(<em>n</em>).weight</td> - <td>100</td> - <td><p id="term(n).weight">The importance of matching this query term given in the query</p></td></tr> - -<tr><td>term(<em>n</em>).connectedness</td> - <td>0.1</td> - <td> - <p id="term(n).connectedness"> - The normalized strength with which this term is connected to the previous term in the query. - Must be assigned to query terms in a <a href="../../applications/searchers.html">searcher</a> using the - <a href="https://javadoc.io/doc/com.yahoo.vespa/container-search/latest/com/yahoo/prelude/query/TaggableItem.html#setConnectivity(com.yahoo.prelude.query.Item,double)"> - Java API for connectivity</a> - or <a href="../querying/yql.html#connectivity">YQL annotation for connectivity</a>. - </p> - </td></tr> - -<tr><td>queryTermCount</td> - <td>0</td> - <td> - <p id="queryTermCount"> - The total number of terms in this query, including both user and synthetic terms in all fields. - </p> - </td></tr> - - - - -<tr><td colspan="3"></td></tr> -<tr><td colspan="3"><h3 id="document-features">Document features</h3></td></tr> -<tr class="trx"><th class="thx">Feature name</th><th class="thx">Default</th><th class="thx">Description</th></tr> - -<tr><td>fieldLength(<em>name</em>)</td> - <td>1000000</td> - <td> - <p id="fieldLength(name)"> - The number of terms in this field if one or more query term matched the field, - 1000000 if no query term matched the field. - </p> - </td></tr> - -<tr><td>averageFieldLength(<em>name</em>)</td> - <td>n/a</td> - <td> - <p id="averageFieldLength(name)"> - The average length, in number of terms, of the indexed field <em>name</em>, - as computed from the local content node index (in memory or on disk). - This is the same index statistic that <a href="../../ranking/bm25.html">BM25</a> uses by default for field - length normalization (the <em>avg_field_len</em> term), exposed here so it can be used for debugging - and in custom text ranking formulas such as <a href="https://github.com/vespa-engine/sample-apps/tree/master/examples/bayesian_bm25">Bayesian BM25</a>. - </p> - <p> - <em>name</em> must be a configured <a href="../schemas/schemas.html#indexing-index">index</a> field; it may be - single-value, array or weighted-set. Referencing an unknown field or a non-index field is a configuration - error. For multi-value fields the field length is the sum of the lengths over all elements, - consistent with <a href="#bm25">bm25</a>. - </p> - {% include note.html content="The value is a per-content-node statistic computed locally; it is - <em>not</em> cluster-wide and may differ between nodes. It also reflects the actual index contents and is - <em>not</em> affected by the - <a href='rank-feature-configuration.html#bm25'>bm25(field).averageFieldLength</a> rank-property override." %} - </td></tr> - -<tr><td>attribute(<em>name</em>)</td> - <td>null</td> - <td> - <p id="attribute(name)"> - The value of a <a href="../schemas/schemas.html#tensor">tensor</a> - or single value <em>numeric</em> attribute or null/NaN if not set. - Use <em>isNan()</em> to check if value is not defined. - Using undefined values in ranking expressions leads to undefined behavior. - </p> - </td></tr> - -<tr><td>attribute(<em>name</em>,<em>n</em>)</td> - <td>0</td> - <td> - <p id="attribute(name,n)"> - The value at index n (base 0) of a <em>numeric</em> array attribute with the given name. - Note that the index number must be explicit, it cannot be the output of an - <a href="../schemas/schemas.html#function-rank">expression function</a>. - The order of the items in an array attribute is the same as the order they have in the input feed. - If items are added using partial updates they are added to the end of the existing items list. - </p> - </td></tr> - -<tr><td>attribute(<em>name</em>,<em>key</em>).weight</td> - <td>0</td> - <td><p id="attribute(name,key).weight">The weight found at a given key in a weighted set attribute</p></td></tr> - -<tr><td>attribute(<em>name</em>,<em>key</em>).contains</td> - <td>0</td> - <td> - <p id="attribute(name,key).contains">1 if the given key is present in a weighted set attribute, 0 otherwise</p> - </td></tr> - -<tr><td>attribute(<em>name</em>).count</td> - <td>0</td> - <td><p id="attribute(name).count">The number of elements in the attribute with the given name.</p></td></tr> - -<tr><td>tensorFromWeightedSet(<em>source</em>,<em>dimension</em>)</td> - <td>empty tensor</td> - <td> - <p id="tensorFromWeightedSet(source,dimension)"> - Creates a <code>tensor<double></code> with one mapped dimension - from the given integer or string weighted set attribute. - The attribute is specified as the full feature name, <code>attribute(name)</code>. - The <em>dimension</em> parameter is optional. If omitted the dimension name will be the attribute name. - </p> - <p>Example: Given the weighted set:</p> -<pre> -{key1:0, key2:1, key3:2.5} -</pre> - <p><em>tensorFromWeightedSet(attribute(myField), dim)</em> produces:</p> -<pre> -tensor<double>(dim{}):{ {dim:key1}:0.0, {dim:key2}:1.0, {dim:key3}:2.5} } -</pre> - {% include note.html content="This creates a temporary tensor, - and has build cost and extra memory is touched. - Tensor evaluation is most effective when the cell types of all tensors are equal - - use <a href='ranking-expressions.html#cell_cast'>cell_cast</a> to enable optimizations. - Also, duplicating the field in the schema to a native tensor instead of creating from a set - can increase performance." %} - </td></tr> - -<tr><td>tensorFromLabels(<em>attribute</em>,<em>dimension</em>)</td> - <td>empty tensor</td> - <td> - <p id="tensorFromLabels(source,dimension)"> - Creates a <code>tensor<double></code> with one mapped dimension - from the given single value or array attribute. - The value(s) must be integers or strings. The attribute is specified as the full feature name, - <code>attribute(name)</code>. - The <em>dimension</em> parameter is optional. If omitted the dimension name will be the attribute name. - </p> - <p> - Example: Given an attribute field <code>myField</code> containing the array value: - </p> -<pre> -[v1, v2, v3] -</pre> - <p><em>tensorFromLabels(attribute(myField), dim)</em> produces:</p> -<pre> -tensor<double>(dim{}):{ {dim:v1}:1.0, {dim:v2}:1.0, {dim:v3}:1.0 } -</pre> - <p>See <em>tensorFromWeightedSet</em> for performance notes.</p> - </td></tr> - -<tr><td>tensorFromStructs(<em>attribute</em>,<em>key</em>,<em>value</em>,<em>type</em>)</td> - <td>empty tensor</td> - <td> - <p id="tensorFromStructs(attribute,key,value,type)"> - Creates a <code>tensor<type></code> with one mapped dimension - from the given <code>array<struct></code> attribute. - Keys are taken from the struct field <em>key</em> and values from the struct field <em>value</em>. - The resulting tensor will have one mapped dimension named after the <em>key</em> field. - The <em>type</em> parameter is required and must be <code>float</code> or <code>double</code>. - </p> - <p>Example: Given an <code>array<struct></code> attribute <code>items</code> with fields - <code>name</code> (string) and <code>price</code> (float):</p> -<pre> -tensorFromStructs(attribute(items), name, price, float) -</pre> -<pre> -tensor<float>(name{}):{ {name:apple}:1.5, {name:banana}:0.75, {name:cherry}:2.25 } -</pre> - - <p>Example: Integer keys and float values:</p> -<pre> -tensorFromStructs(attribute(ids), id, score, double) -</pre> -<pre> -tensor(id{}):{ {id:100}:10.5, {id:200}:20.75, {id:300}:30.25 } -</pre> - - <p> - The function takes at least four arguments; it's possible to use several <em>key</em> - arguments to get multiple mapped dimensions. If the <code>struct</code> above also - contained a <code>region</code> field one could use -<pre class="code"> -tensorFromStructs(attribute(items), name, region, price, float) -</pre> - to get a <code>tensor<float>(name{},region{})</code> making it possible to have -different prices of apples in different regions. - </p> - <p> - <em>Details:</em> - Empty or missing arrays yield an empty tensor of the requested type. - The first argument must be an <code>attribute(...)</code> source. - It's an application responsibility to ensure that the key (or combination of keys - in the multi-dimensional case) is unique. - </p> - <p>See <em>tensorFromWeightedSet</em> for performance notes.</p> - </td> -</tr> - -<tr><td>tensorFromLabelsWithOffset(<em>attribute</em>,<em>label-dimension</em>,<em>offset-dimension</em>)</td> - <td>empty tensor</td> - <td> - <p id="tensorFromLabelsWithOffset"> - Creates a <code>tensor<float></code> with one mapped dimension - with labels from the given array attribute, and another mapped dimension - where labels are constructed from the array index (offset) of that label. - The cell values of the tensor are always 1.0 (suitable for multiplication). - The attribute values must be integers or strings. The attribute is specified as the full feature name, - <code>attribute(name)</code>. - The <em>label-dimension</em> parameter is required, using the same as the attribute <em>name</em> is common. - The <em>offset-dimension</em> will get label value "0", "1" and so on. - This feature may be useful if there are several arrays populated in parallel, and the - array index of a label is needed in order to correlate with a different array. - </p> - <p> - Example: Given an attribute field <code>myField</code> containing the array value: - </p> -<pre> -[v1, v2, v3] -</pre> - <p><em>tensorFromLabelsWithOffset(attribute(myField), dim, off)</em> produces:</p> -<pre> -tensor<float>(dim{},off{}):{ {dim:v1,off:0}:1.0, {dim:v2,off:1}:1.0, {dim:v3,off:2}:1.0 } -</pre> - </td></tr> - -<tr><td colspan="3"></td></tr> -<tr><td colspan="3"><h3 id="field-match-features-normalized">Field match features - normalized</h3></td></tr> -<tr><td colspan="3"> - fieldMatch features provide a good measure of the degree - to which a query matches the text of a field, but are expensive to calculate and therefore - often only suitable for <a href="../../ranking/phased-ranking.html">second-phase</a> ranking expressions. - See the <a href="string-segment-match.html">string segment match</a> document - for details on the algorithm computing this rank-feature set. - - Note that even using a fine-grained sub features like fieldMatch(<em>name</em>).absoluteOccurrence - will have the same complexity and cost as using the general top level fieldMatch(<em>name</em>) feature. -</td></tr> -<tr class="trx"><th class="thx">Feature name</th><th class="thx">Default</th><th class="thx">Description</th></tr> - -<tr><td>fieldMatch(<em>name</em>)</td> - <td>0</td> - <td> - <p id="fieldMatch(name)"> - A normalized measure of the degree to which this query and field matched - (default, the long name of this is <code>match</code>). Use this if you do not - want to create your own combination function of more fine-grained fieldmatch features. - </p> - </td></tr> - -<tr><td>fieldMatch(<em>name</em>).proximity</td> - <td>0</td> - <td><p id="fieldMatch(name).proximity"> - Normalized proximity - a value which is close to 1 when matched terms are close - <em>inside each segment</em>, and close to zero when they are far apart inside segments. - Relatively more connected terms influence this value more. This is - absoluteProximity/average connectedness for the query terms for this field. - </p><p> - Note that if all the terms are far apart, - the proximity will be 1, but the number of segments will be high. - Proximity is only concerned with closeness within segments, - a total score must also take the number of segments into account.</p> - </td></tr> - -<tr><td>fieldMatch(<em>name</em>).completeness</td> - <td>0</td> - <td> - <p id="fieldMatch(name).completeness"> - The normalized total completeness, where field completeness is more important: - </p> - <p> - <code>queryCompleteness * - ( 1 - <a href="rank-feature-configuration.html#fieldMatch">fieldCompletenessImportance</a> ) - + <a href="rank-feature-configuration.html#fieldMatch">fieldCompletenessImportance</a> * fieldCompleteness</code> - </p> - </td></tr> - -<tr><td>fieldMatch(<em>name</em>).queryCompleteness</td> - <td>0</td> - <td><p id="fieldMatch(name).queryCompleteness">The normalized ratio of query tokens matched in the field:</p> - <code>matches/query terms searching this field</code></td></tr> - -<tr><td>fieldMatch(<em>name</em>).fieldCompleteness</td> - <td>0</td> - <td><p id="fieldMatch(name).fieldCompleteness">The normalized ratio of query tokens which was matched in the field:</p> - <code>matches/fieldLength</code></td></tr> - -<tr><td>fieldMatch(<em>name</em>).orderness</td> - <td>0</td> - <td> - <p id="fieldMatch(name).orderness"> - A normalized metric of how well the order of the terms agrees in the chosen segments: - </p> - <p><code>1-outOfOrder/pairs</code></p> - </td></tr> - -<tr><td>fieldMatch(<em>name</em>).relatedness</td> - <td>0</td> - <td> - <p id="fieldMatch(name).relatedness"> - A normalized measure of the degree to which different terms are related (occurring in the same segment): - </p> - <p><code>1-(segments-1)/(matches-1)</code></p></td></tr> - -<tr><td>fieldMatch(<em>name</em>).earliness</td> - <td>0</td> - <td> - <p id="fieldMatch(name).earliness"> - A normalized measure of how early the first segment occurs in this field. - </p> - </td></tr> - -<tr><td>fieldMatch(<em>name</em>).longestSequenceRatio</td> - <td>0</td> - <td> - <p id="fieldMatch(name).longestSequenceRatio">A normalized metric of the relative size of the longest sequence:</p> - <p><code>longestSequence/matches</code></p></td></tr> - -<tr><td>fieldMatch(<em>name</em>).segmentProximity</td> - <td>0</td> - <td> - <p id="fieldMatch(name).segmentProximity"> - A normalized metric of the closeness (inverse of spread) of segments in the field: - </p> - <p><code>1-segmentDistance/fieldLength</code></p> - </td></tr> - -<tr><td>fieldMatch(<em>name</em>).unweightedProximity</td> - <td>0</td> - <td> - <p id="fieldMatch(name).unweightedProximity"> - The normalized proximity of the matched terms, not taking term connectedness into account. - This number is close to 1 if all the matched terms are - following each other in sequence, and close to 0 if they are far from each other or out of order. - </p> - </td></tr> - -<tr><td>fieldMatch(<em>name</em>).absoluteProximity</td> - <td>0</td> - <td> - <p id="fieldMatch(name).absoluteProximity"> - Returns the normalized proximity of the matched terms, weighted by the connectedness of - the query terms. This number is 0.1 if all the matched terms are and have default or lower - connectedness, close to 1 if they are following in sequence and have a high connectedness, - and close to 0 if they are far from each other in the segments or out of order. - </p> - </td></tr> - -<tr><td>fieldMatch(<em>name</em>).occurrence</td> - <td>0</td> - <td><p id="fieldMatch(name).occurrence"> - Returns a normalized measure of the number of occurrences of the terms of the query. - This is 1 if there are many occurrences of the query terms <em>in absolute terms, - or relative to the total content of the field</em>, and 0 if there are none. - </p><p> - This is suitable for occurrence in fields containing regular text.</p> - </td></tr> - -<tr><td>fieldMatch(<em>name</em>).absoluteOccurrence</td> - <td>0</td> - <td><p id="fieldMatch(name).absoluteOccurrence"> - Returns a normalized measure of the number of occurrence of the terms of the query: - </p><p class="equation-container"><!-- depends on mathjax --> - $$\frac{\sum_{\text{all query terms}}(min(\text{number of occurrences of the term},maxOccurrences))}{(\text{query term count} × 100)}$$ - </p><p> - This is 1 if there are many occurrences of the query terms, and 0 if there are none. - </p><p> - This number is not relative to the field length, so it is suitable for uses - of occurrence to denote relative importance between matched terms - (i.e. fields containing keywords, not normal text).</p> - </td></tr> - -<tr><td>fieldMatch(<em>name</em>).weightedOccurrence</td> - <td>0</td> - <td> - <p id="fieldMatch(name).weightedOccurrence"> - Returns a normalized measure of the number of occurrence of the terms of the query, - weighted by term weight. - This number is close to 1 if there are many occurrences of highly weighted query terms, - in absolute terms, or relative to the total content of the field, and 0 if there are none. - </p> - </td></tr> - -<tr><td>fieldMatch(<em>name</em>).weightedAbsoluteOccurrence</td> - <td>0</td> - <td><p id="fieldMatch(name).weightedAbsoluteOccurrence"> - Returns a normalized measure of the number of occurrence of the terms of the query, - taking weights into account so that occurrences of higher weighted query terms - has more impact than lower weighted terms. - </p><p> - This is 1 if there are many occurrences of the highly weighted terms, and 0 if there are none. - </p><p> - This number is not relative to the field length, so it is suitable for uses - of occurrence to denote relative importance between matched terms - (i.e. fields containing keywords, not normal text).</p> - </td></tr> - -<tr><td>fieldMatch(<em>name</em>).significantOccurrence</td> - <td>0</td> - <td><p id="fieldMatch(name).significantOccurrence"> - Returns a normalized measure of the number of occurrence of the terms of the query <em>in absolute terms, - or relative to the total content of the field</em>, weighted by term significance. - </p><p> - This number is 1 if there are many occurrences of the highly significant terms, - and 0 if there are none.</p> - </td></tr> - - - - - -<tr><td colspan="3"></td></tr> -<tr><td colspan="3"><h3 id="field-match-features-normalized-and-relative-to-the-whole-query"> - Field match features - normalized and relative to the whole query</h3></td></tr> -<tr class="trx"><th class="thx">Feature name</th><th class="thx">Default</th><th class="thx">Description</th></tr> -<tr><td>fieldMatch(<em>name</em>).weight</td> - <td>0</td> - <td><p id="fieldMatch(name).weight"> - The normalized weight of this match relative to the whole query: - The sum of the weights of all <em>matched</em> terms/the sum of the weights of all <em>query</em> terms. - If all the query terms were matched, this is 1. - If no terms were matched, or these matches has weight zero this is 0. - </p><p> - As the sum of this number over all the terms of the query is always 1, sums over all fields of - normalized rank features for each field multiplied by this number - for the same field will produce a normalized number. - </p><p> - Note that this scales with the number of matched query terms in the field. - If you want a component which does not, divide by matches.</p> - </td></tr> - -<tr><td>fieldMatch(<em>name</em>).significance</td> - <td>0</td> - <td><p id="fieldMatch(name).significance"> - Returns the normalized term significance of the terms of this match relative to the - whole query: The sum of the significance of all <em>matched</em> terms/the sum of the significance of all - <em>query</em> terms. If all the query terms were matched, this is 1. If no terms were matched, or if the - significance of all the matched terms is zero, this number is zero. - </p><p> - This metric has the same properties as weight. - </p><p> - See the <a href="#term(n).significance">term(n).significance</a> feature for how the significance - for a single term is calculated.</p> - </td></tr> - -<tr><td>fieldMatch(<em>name</em>).importance</td> - <td>0</td> - <td> - <p id="fieldMatch(name).importance"> - Returns the average of significance and weight. This has the same properties as those metrics. - </p> - </td></tr> - - - - -<tr><td colspan="3"></td></tr> -<tr><td colspan="3"> - <h3 id="field-match-features-not-normalized">Field match features - not normalized</h3></td></tr> -<tr class="trx"><th class="thx">Feature name</th><th class="thx">Default</th><th class="thx">Description</th></tr> - -<tr><td>fieldMatch(<em>name</em>).segments</td> - <td>0</td> - <td> - <p id="fieldMatch(name).segments"> - The number of field text segments which are needed to match the query as completely as possible - </p> - </td></tr> - -<tr><td>fieldMatch(<em>name</em>).matches</td> - <td>0</td> - <td> - <p id="fieldMatch(name).matches"> - The total number of query terms which was matched in this field - </p> - </td></tr> - -<tr><td>fieldMatch(<em>name</em>).degradedMatches</td> - <td>0</td> - <td> - <p id="fieldMatch(name).degradedMatches"> - The number of degraded query terms which was matched in this field. - A degraded term is a term where no occurrence information is available during calculation. - The number of degraded matches is less than or equal to the total number of matches. - </p> - </td></tr> - -<tr><td>fieldMatch(<em>name</em>).outOfOrder</td> - <td>0</td> - <td> - <p id="fieldMatch(name).outOfOrder"> - The total number of out of order token sequences <em>within</em> matched field segments - </p> - </td></tr> - -<tr><td>fieldMatch(<em>name</em>).gaps</td> - <td>0</td> - <td> - <p id="fieldMatch(name).gaps"> - The total number of position jumps (backward or forward) within field segments - </p> - </td></tr> - -<tr><td>fieldMatch(<em>name</em>).gapLength</td> - <td>0</td> - <td><p id="fieldMatch(name).gapLength">The summed length of all gaps within segments</p></td></tr> - -<tr><td>fieldMatch(<em>name</em>).longestSequence</td> - <td>0</td> - <td> - <p id="fieldMatch(name).longestSequence"> - The size of the longest matched continuous, in-order sequence in the field - </p> - </td></tr> - -<tr><td>fieldMatch(<em>name</em>).head</td> - <td>0</td> - <td> - <p id="fieldMatch(name).head"> - The number of tokens in the field preceding the start of the first matched segment - </p> - </td></tr> - -<tr><td>fieldMatch(<em>name</em>).tail</td> - <td>0</td> - <td> - <p id="fieldMatch(name).tail"> - The number of tokens in the field following the end of the last matched segment - </p> - </td></tr> - -<tr><td>fieldMatch(<em>name</em>).segmentDistance</td> - <td>0</td> - <td> - <p id="fieldMatch(name).segmentDistance"> - The sum of the distance between all segments making up a match to the query, measured - as the sum of the number of token positions separating the - <em>start</em> of each <em>field</em> adjacent segment. - </p> - </td></tr> - - - - - -<tr><td colspan="3"></td></tr> -<tr><td colspan="3"><h3 id="query-and-field-similarity">Query and field similarity</h3></td></tr> -<tr><td colspan="3"> - Normalized feature set measuring the approximate <em>similarity</em> between a field and the query. - These features are suitable in cases where the query is as large as the field (i.e. is a document) - such that we are interested in the similarity between the query and the entire field. - They are cheap to compute even if the query is large. -</td></tr> -<tr class="trx"><th class="thx">Feature name</th><th class="thx">Default</th><th class="thx">Description</th></tr> - -<tr><td>textSimilarity(<em>name</em>)</td> - <td>0</td> - <td><p id="textSimilarity(name)">A weighted sum of the individual similarity measures.</p></td></tr> - -<tr><td>textSimilarity(<em>name</em>).proximity</td> - <td>0</td> - <td> - <p id="textSimilarity(name).proximity"> - A measure of how close together the query terms appear in the field. - </p> - </td></tr> - -<tr><td>textSimilarity(<em>name</em>).order</td> - <td>0</td> - <td> - <p id="textSimilarity(name).order"> - A measure of the order in which the query terms appear in the field compared to the query. - </p> - </td></tr> - -<tr><td>textSimilarity(<em>name</em>).queryCoverage</td> - <td>0</td> - <td> - <p id="textSimilarity(name).queryCoverage"> - A measure of how much of the query the field covers when a single term - from the field can only cover a single term in the query. - Query term weights are used during normalization. - </p> - </td></tr> - -<tr><td>textSimilarity(<em>name</em>).fieldCoverage</td> - <td>0</td> - <td> - <p id="textSimilarity(name).fieldCoverage"> - A measure of how much of the field the query covers when a single term from the query - can only cover a single term in the field. - </p> - </td></tr> - - - - - -<tr><td colspan="3"></td></tr> -<tr><td colspan="3"><h3 id="query-term-and-field-match-features">Query term and field match features</h3></td></tr> -<tr class="trx"><th class="thx">Feature name</th><th class="thx">Default</th><th class="thx">Description</th></tr> - -<tr><td>fieldTermMatch(<em>name</em>,<em>n</em>).firstPosition</td> - <td>1000000</td> - <td> - <p id="fieldTermMatch(name,n).firstPosition"> - The position of the first occurrence of this query term in this index field. - <a href="rank-feature-configuration.html#fieldTermMatch">numTerms</a> configuration - </p> - </td></tr> - -<tr><td>fieldTermMatch(<em>name</em>,<em>n</em>).occurrences</td> - <td>0</td> - <td> - <p id="fieldTermMatch(name,n).occurrences"> - The number of occurrences of this query term in this index field - </p> - </td></tr> - -<tr><td>matchCount(<em>name</em>)</td> - <td>0</td> - <td> - <p id="matchCount(name)"> - Returns number of times any term in the query matches this index/attribute field. - </p> - </td></tr> - -<tr><td>matches(<em>name</em>)</td> - <td>0</td> - <td> - <p id="matches(name)"> - Returns 1 if the index/attribute field with the given name is matched by the query. - </p> - </td></tr> - -<tr><td>matches(<em>name</em>,<em>n</em>)</td> - <td>0</td> - <td> - <p id="matches(name,n)"> - Returns 1 if the index/attribute field with the given name is matched by the query term with position <em>n</em>. - </p> - </td></tr> - -<tr><td>termDistance(<em>name</em>,<em>x</em>,<em>y</em>).forward</td> - <td>1000000</td> - <td> - <p id="termDistance(name,x,y).forward"> - The minimum distance between the occurrences of term <em>x</em> and term <em>y</em> in this index field. - Term <em>x</em> occurs before term <em>y</em>. - </p> - </td></tr> - -<tr><td>termDistance(<em>name</em>,<em>x</em>,<em>y</em>).forwardTermPosition</td> - <td>1000000</td> - <td> - <p id="termDistance(name,x,y).forwardTermPosition"> - The position of the occurrence of term <em>x</em> in this index field used for the forward distance. - </p> - </td></tr> - -<tr><td>termDistance(<em>name</em>,<em>x</em>,<em>y</em>).reverse</td> - <td>1000000</td> - <td> - <p id="termDistance(name,x,y).reverse"> - The minimum distance between the occurrences of term <em>y</em> and term <em>x</em> in this index field. - Term <em>y</em> occurs before term <em>x</em>. - </p> - </td></tr> - -<tr><td>termDistance(<em>name</em>,<em>x</em>,<em>y</em>).reverseTermPosition</td> - <td>1000000</td> - <td> - <p id="termDistance(name,x,y).reverseTermPosition"> - The position of the occurrence of term <em>y</em> in this index field used for the reverse distance. - </p> - </td></tr> - - - - - - - -<tr><td colspan="3"></td></tr> -<tr><td colspan="3"> - <h3 id="features-for-indexed-multivalue-string-fields">Features for indexed multivalue string fields</h3></td></tr> -<tr class="trx"><th class="thx">Feature name</th><th class="thx">Default</th><th class="thx">Description</th></tr> - -<tr><td>elementCompleteness(<em>name</em>).completeness</td> - <td>0</td> - <td> - <p id="elementCompleteness(name).completeness"> - A weighted combination of fieldCompleteness and queryCompleteness for the element in the field that produces the - highest value for this output after the elements weight is factored in. The weighting can be adjusted using - <a href="rank-feature-configuration.html#elementCompleteness">elementCompleteness(name).fieldCompletenessImportance</a>. - </p> - </td></tr> - -<tr><td>elementCompleteness(<em>name</em>).fieldCompleteness</td> - <td>0</td> - <td> - <p id="elementCompleteness(name).fieldCompleteness"> - The field completeness of the best matching element. This is calculated as: - </p> - <p><code>max( (number of query terms matched in the element) / - (element size), 1.0)</code>.</p> - </td></tr> - -<tr><td>elementCompleteness(<em>name</em>).queryCompleteness</td> - <td>0</td> - <td> - <p id="elementCompleteness(name).queryCompleteness"> - The query completeness of the best matching element. This is calculated as:</p> - <p> - <code>(sum of weight for query terms matched in the element) / - (sum of weight for query terms searching the field)</code>. - </p> - </td></tr> - -<tr><td>elementCompleteness(<em>name</em>).elementWeight</td> - <td>0</td> - <td><p id="elementCompleteness(name).elementWeight"> - The weight of the best matching element, starting from the default - i.e., negative weights will return 0. - </p></td></tr> - -<tr><td>elementSimilarity(<em>name</em>)</td> - <td>0</td> - <td> - <p id="elementSimilarity(name)"> - Aggregated similarity between the query and individual field elements. - The same sub-scores used by the <code>textSimilarity</code> feature - are calculated for each individual element in the field. - The final output is calculated as the maximum of the combined element - similarity measures (similarity measures are combined the same way as - the default output of the <code>textSimilarity</code> feature) - multiplied with the element weight which is 1 for arrays, - and the supplied weights for indexed weighted sets. - </p> - <p> - This is a flexible feature; - how sub-scores are combined for each element and how element scores are aggregated may be configured. - You may also add additional outputs if you want to capture multiple signals from a single field. - Use <a href="rank-feature-configuration.html#elementSimilarity">elementSimilarity</a> to customize this feature. - </p> - </td></tr> - - - - - - -<tr><td colspan="3"></td></tr> -<tr><td colspan="3"> - <h3 id="attribute-match-features-normalized">Attribute match features - normalized</h3></td></tr> -<tr class="trx"><th class="thx">Feature name</th><th class="thx">Default</th><th class="thx">Description</th></tr> - -<tr><td>attributeMatch(<em>name</em>)</td> - <td>0</td> - <td> - <p id="attributeMatch(name)"> - A normalized measure of the degree to which this query and field matched. This is currently the same as completeness. - Note that depending on what the attribute is used for, this may or may not be a suitable metric. - If the attribute is a weighted set representing counts of items (like tags), - <code>normalizedWeight</code> is probably a better metric. - </p> - </td></tr> - - <tr><td>attributeMatch(<em>name</em>).completeness</td> - <td>0</td> - <td> - <p id="attributeMatch(name).completeness"> - The normalized total completeness, where field completeness is more important: - </p><p> - <code>queryCompleteness * ( 1 - <a href="rank-feature-configuration.html#attributeMatch">fieldCompletenessImportance</a> + - <a href="rank-feature-configuration.html#attributeMatch">fieldCompletenessImportance</a> * fieldCompleteness )</code> - </p> - </td></tr> - -<tr><td>attributeMatch(<em>name</em>).queryCompleteness</td> - <td>0</td> - <td> - <p id="attributeMatch(name).queryCompleteness">The query completeness for this attribute:</p> - <p><code>matches/the number of query terms searching this attribute</code></p></td></tr> - -<tr><td>attributeMatch(<em>name</em>).fieldCompleteness</td> - <td>0</td> - <td> - <p id="attributeMatch(name).fieldCompleteness"> - The normalized ratio of query tokens which was matched in the field. - For arrays: <code>matches/array length</code> - For weighted sets: <code>sum of weight of matched terms/sum of weights of entire set</code>. - This is relatively expensive to calculate for large weighted sets. - </p> - </td></tr> - -<tr><td>attributeMatch(<em>name</em>).normalizedWeight</td> - <td>0</td> - <td> - <p id="attributeMatch(name).normalizedWeight"> - A number which is close to 1 if the attribute weights of most matches in a weighted set are high - (relative to <a href="rank-feature-configuration.html#attributeMatch">maxWeight</a>), 0 otherwise - </p> - </td></tr> - -<tr><td>attributeMatch(<em>name</em>).normalizedWeightedWeight</td> - <td>0</td> - <td> - <p id="attributeMatch(name).normalizedWeightedWeight"> - A number which is close to 1 if the attribute weights of most matches in a weighted set are high - (relative to <a href="rank-feature-configuration.html#attributeMatch">maxWeight</a>), - and where highly weighted query terms has more impact, 0 otherwise - </p> - </td></tr> - -<tr> - <td>closeness(<em>dimension</em>,<em>name</em>)</td> - <td>0</td> - <td><p id="closeness(dimension,name)"> - Used with the <a href="../querying/yql.html#nearestneighbor">nearestNeighbor</a> query operator. - A number which is close to 1 when a vector in the document tensor is close to the vector given in the query. - The indexed dimension representing a vector in document and query tensors must be identical. - </p> - <ul> - <li> - <p> - <em>dimension</em>: Specifies the dimension of <em>name</em>. - This must be either the string <code>field</code> or the string <code>label</code>. - </p> - <p> - When using <code>field</code>, the name given must be a field with a tensor attribute of appropriate type. - Often used when the document type has only one vector field, - see <a href="../../querying/nearest-neighbor-search#minimal-example">example</a>. - </p> - <p> - When using <code>label</code>, queries are assumed to contain a - <a href="../querying/yql.html#nearestneighbor">nearestNeighbor</a> query item - with a <a href="../querying/yql.html#label">label</a> that matches the given <em>name</em>. - This is useful when having multiple vector fields, where <code>closeness()</code> then maps - to the nearestNeighbor operator with the field configured. - <a href="../../querying/nearest-neighbor-search-guide#using-label">Example</a>. - </p> - </li> - <li> <em>name</em>: The value of the field name or label. - </li> - </ul> - <p> - {% include note.html content="<code>closeness()</code> is calculated <strong>only</strong> based on the vectors - matched with nearestNeighbor operator. - This means that the value of <code>closeness()</code> is not necessarily calculated based on the same vector - returned by <code>closest()</code> rank feature if nearestNeighbor search is approximate, as - <code>closest()</code> will be calculated based on <em>all</em> specified document vectors." %} - </p> - <p> - The output value is - $$ closeness(dimension,name) = \frac{1.0}{1.0 + distance(dimension,name)}$$ - </p> - <p> - When the tensor field stores multiple vectors per document, the minimum distance - between the vectors of a document and the query vector is used in the calculation above. - </p> - </td></tr> - -<tr><td>freshness(<em>name</em>)</td> - <td>0</td> - <td><p id="freshness(name)"> - A number which is close to 1 if the timestamp in attribute <em>name</em> is recent compared to the current time - compared to <a href="rank-feature-configuration.html#freshness">maxAge</a>: - </p><p> - <code>max( 1-age(name)/maxAge , 0 )</code> - </p><p> - Scales linearly with age, see <a href="#freshness">freshness plot</a>. - </td></tr> - -<tr><td>freshness(<em>name</em>).logscale</td> - <td>0</td> - <td><p id="freshness(name).logscale"> - A logarithmic-shaped freshness; also goes from 1 to 0, but looks like <a href="#freshness">freshness plot</a>. - The function is based on <code>-log(age(name) + scale)</code> and is calculated as: - </p><p class="equation-container"><!-- depends on mathjax --> - $$\frac{log(maxAge + scale) - log(age(name) + scale)}{log(maxAge + scale) - log(scale)}$$ - </p><p> - where scale is defined using - <a href="rank-feature-configuration.html#freshness"> halfResponse and maxAge</a>: - </p><p class="equation-container"><!-- depends on mathjax --> - $$\frac{-halfResponse^2}{2 × halfResponse - maxAge}$$ - </p><p> - When <code>age(name) == halfResponse</code> the function output is 0.5.</p> - </td></tr> - - - - - - -<tr><td colspan="3"></td></tr> -<tr><td colspan="3"><h3 id="attribute-match-features-normalized-and-relative-to-the-whole-query"> - Attribute match features - normalized and relative to the whole query</h3></td></tr> -<tr class="trx"><th class="thx">Feature name</th><th class="thx">Default</th><th class="thx">Description</th></tr> - -<tr><td>attributeMatch(<em>name</em>).weight</td> - <td>0</td> - <td> - <p id="attributeMatch(name).weight"> - This has the same semantics as fieldMatch(<em>name</em>).weight. - </p> - </td></tr> - -<tr><td>attributeMatch(<em>name</em>).significance</td> - <td>0</td> - <td> - <p id="attributeMatch(name).significance"> - This has the same semantics as fieldMatch(<em>name</em>).significance. - </p> - </td></tr> - -<tr><td>attributeMatch(<em>name</em>).importance</td> - <td>0</td> - <td> - <p id="attributeMatch(name).importance"> - Returns the average of significance and weight. This has the same properties as those metrics. - </p> - </td></tr> - - - - - - -<tr><td colspan="3"></td></tr> -<tr><td colspan="3"> - <h3 id="attribute-match-features-not-normalized">Attribute match features - not normalized</h3></td></tr> -<tr class="trx"><th class="thx">Feature name</th><th class="thx">Default</th><th class="thx">Description</th></tr> - -<tr><td>attributeMatch(<em>name</em>).matches</td> - <td>0</td> - <td><p id="attributeMatch(name).matches">The number of query terms which was matched in this attribute</p></td></tr> - -<tr><td>attributeMatch(<em>name</em>).totalWeight</td> - <td>0</td> - <td> - <p id="attributeMatch(name).totalWeight"> - The sum of the weights of the attribute keys matched in a weighted set attribute - </p> - </td></tr> - -<tr><td>attributeMatch(<em>name</em>).averageWeight</td> - <td>0</td> - <td><p id="attributeMatch(name).averageWeight">totalWeight/matches</p></td></tr> - -<tr><td>attributeMatch(<em>name</em>).maxWeight</td> - <td>0</td> - <td> - <p id="attributeMatch(name).maxWeight"> - The maximum weight of the attribute keys matched in a weighted set attribute - </p> - </td></tr> - -<tr><td>closest(<em>name</em>)</td> - <td>{}</td> - <td><p id="closest(name)"> - Used with the - <a href="../querying/yql.html#nearestneighbor">nearestNeighbor</a> - query operator and a tensor field attribute <em>name</em> storing multiple vectors per document. - This feature returns a tensor with one or more mapped dimensions and one point with a value of 1.0, - where the label of that point indicates which document vector was closest to the - query vector in the nearest neighbor search. - </p> - <p> - Given a tensor field with type <code>tensor<float>(m{},x[3])</code> - used with the <em>nearestNeighbor</em> operator, - an example output of this feature is: - </p> -<pre> - tensor<float>(m{}):{ 3: 1.0 } -</pre> - <p> - In this example, the document vector with label <em>3</em> in the mapped <em>m</em> dimension - was closest to the query vector. - </p> - </td></tr> - - -<tr><td>closest(<em>name</em>,<em>label</em>)</td> - <td>{}</td> - <td><p id="closest(name,label)"> - Used with the - <a href="../querying/yql.html#nearestneighbor">nearestNeighbor</a> - query operator tagged with a - <a href="../../ranking/multivalue-query-operators.html#raw-scores-and-query-item-labeling">label</a> <em>label</em> - and a tensor field attribute <em>name</em> storing multiple vectors per document. - </p> - <p> - See <a href="#closest(name)">closest(name)</a> for details. - </p> - </td></tr> - - -<tr><td>distance(<em>dimension</em>,<em>name</em>)</td> - <td>max double value</td> - <td><p id="distance(dimension,name)"> - Used with the - <a href="../querying/yql.html#nearestneighbor">nearestNeighbor</a> - query operator. - A number which is close to 0 when a point vector in the document is close - to a matching point vector in the query. - The document vectors and the query vector must be the same tensor - type, with one indexed dimension of size N, representing a point in an N-dimensional space. - <ul> - <li> - <p> - <em>dimension</em>: Specifies the dimension of <em>name</em>. - This must be either the string <code>field</code> or the string <code>label</code>. - </p> - <p> - When using <code>field</code>, the name given must be a field with a tensor attribute of appropriate type. - Often used when the document type has only one vector field, - see <a href="../../querying/nearest-neighbor-search#minimal-example">example</a>. - </p> - <p> - When using <code>label</code>, queries are assumed to contain a - <a href="../querying/yql.html#nearestneighbor">nearestNeighbor</a> query item - with a <a href="../querying/yql.html#label">label</a> that matches the given <em>name</em>. - This is useful when having multiple vector fields, where <code>distance()</code> then maps - to the nearestNeighbor operator with the field configured. - <a href="../../querying/nearest-neighbor-search-guide#using-label">Example</a>. - </p> - </li> - <li> <em>name</em>: The value of the field name or label. - </li> - </ul> - <p> - The output value depends on the - <a href="../schemas/schemas.html#distance-metric">distance metric</a> used. - The default is the Euclidean distance between the "n"-dimensional - query point "d" and the point "d" in the document tensor field: - $$ distance = \sqrt{\sum_{i=1}^n (q_i - d_i)^2} $$ - </p> - <p> - When the tensor field stores multiple vectors per document, the minimum distance - between the vectors of a document and the query vector is used in the calculation above. - </p> - </td></tr> - -<tr><td>age(<em>name</em>)</td> - <td>10B</td> - <td> - <p id="age(name)"> - The document age in seconds relative to the unit time value - stored in the attribute having this name - </p> - </td></tr> - - - - - - -<tr><td colspan="3"></td></tr> -<tr><td colspan="3"><h3 id="features-combining-multiple-fields-and-attributes"> - Features combining multiple fields and attributes</h3></td></tr> -<tr class="trx"><th class="thx">Feature name</th><th class="thx">Default</th><th class="thx">Description</th></tr> - -<tr><td>match</td> - <td>0</td> - <td> - <p id="match"> - A normalized average of the fieldMatch and attributeMatch scores of all the searched fields - and attributes, where the contribution of each field and attribute is weighted by its - <em>weight</em> setting. - </p> - </td></tr> - -<tr><td>match.totalWeight</td> - <td>0</td> - <td> - <p id="match.totalWeight"> - The sum of the weight settings of all the field and attributes searched by the query - </p> - </td></tr> - -<tr><td>match.weight.<em>name</em></td> - <td>100</td> - <td><p id="match.weight.name">The (schema) weight setting of a field or attribute</p></td></tr> - - - - - - -<tr><td colspan="3"></td></tr> -<tr><td colspan="3"> - <h3 id="rank-scores">Rank scores</h3></td></tr> -<tr class="trx"><th class="thx">Feature name</th><th class="thx">Default</th><th class="thx">Description</th></tr> - -<tr><td>bm25(field)</td> - <td>0</td> - <td> - <p id="bm25"> - Calculates the <a href="https://en.wikipedia.org/wiki/Okapi_BM25">Okapi BM25</a> - ranking function over the given <a href="../schemas/schemas.html#indexing-index">indexed string field</a>. - This feature is cheap to compute, about 3-4 times faster than nativeRank, - while still providing a good rank score quality wise. - This feature is a good candidate for usage in a first phase ranking function when ranking text documents. - Note that the field must be enabled to be used with the bm25 feature; - set the <em>enable-bm25</em> flag in the <a href="../schemas/schemas.html#index">index</a> - section of the field definition. - See the <a href="../../ranking/bm25.html">BM25 Reference</a> for more detailed information. - </p> - </td></tr> - -<tr> - <td>elementwise(bm25(field),dimension,cell_type)</td> - <td><code>tensor<cell_type>(dimension{}):{}</code></td> - <td> - <p id="elementwise-bm25"> - Calculates the <a href="https://en.wikipedia.org/wiki/Okapi_BM25">Okapi BM25</a> - ranking function over each element in the given <em>multi-valued</em> <a href="../schemas/schemas.html#indexing-index">indexed string field</a> and creates a tensor with a single mapped dimension containing the bm25 score for each matching element. - The element indexes (starting at 0) are used as dimension labels. - This feature is more expensive than <a href="#bm25">bm25</a>, - does not need the <em>enable-bm25</em> flag and can be tuned with - <a href="rank-feature-configuration.html#elementwise-bm25">rank properties</a>. - </p> - <p> - The <em>cell_type</em> parameter can be omitted (the default value is <code>double</code>) except when setting rank properties. - </p> - <p> - Example: If the <code>content</code> field is of type <code>array<string></code> - where query terms are found in elements 2 and 5 then the feature - <code>elementwise(bm25(content),x,float)</code> as a summary feature - might be shown as: - </p> -<pre> -"elementwise(bm25(content),x,float)": { - "type": "tensor<float>(x{})", - "cells": { - "2": 0.5112776, - "5": 0.1021805 - } -} -</pre> - </td> -</tr> - -<tr><td>nativeRank</td> - <td>0</td> - <td> - <p id="nativeRank"> - A reasonably good rank score which is computed cheaply by Vespa. - This value only is a good candidate first phase ranking function, - and is the default used in the default rank profile. - The value computed by this function may change between Vespa versions. - See the <a href="nativerank.html">native rank reference</a> for more information. - </p> - </td></tr> - -<tr><td>nativeRank(field,...)</td> - <td>0</td> - <td> - <p id="nativeRank(field,...)"> - Same as <em>nativeRank</em>, but only the given set of fields are used in the calculation. - </p> - </td></tr> - -<tr><td>nativeFieldMatch</td> - <td>0</td> - <td> - <p id="nativeFieldMatch"> - Captures how well query terms match in index fields. Used by <em>nativeRank</em>. - See the <a href="nativerank.html">native rank reference</a> for more information. - </p> - </td></tr> - -<tr><td>nativeFieldMatch(field,...)</td> - <td>0</td> - <td> - <p id="nativeFieldMatch(field,...)"> - Same as <em>nativeFieldMatch</em>, but only the given set of index fields are used in the calculation. - </p> - </td></tr> - -<tr><td>nativeProximity</td> - <td>0</td> - <td> - <p id="nativeProximity"> - Captures how near matched query terms occur in index fields. Used by <em>nativeRank</em>. - See the <a href="nativerank.html">native rank reference</a> for more information. - </p> - </td></tr> - -<tr><td>nativeProximity(field,...)</td> - <td>0</td> - <td> - <p id="nativeProximity(field,...)"> - Same as <em>nativeProximity</em>, but only the given set of index fields are used in the calculation. - </p> - </td></tr> - -<tr><td>nativeAttributeMatch</td> - <td>0</td> - <td> - <p id="nativeAttributeMatch"> - Captures how well query terms match in attribute fields. Used by <em>nativeRank</em>. - See the <a href="nativerank.html">native rank reference</a> for more information. - </p> - </td></tr> - -<tr><td>nativeAttributeMatch(field,...)</td> - <td>0</td> - <td> - <p id="nativeAttributeMatch(field,...)"> - Same as <em>nativeAttributeMatch</em>, but only the given set of attribute fields are used in the calculation. - </p> - </td></tr> - -<tr><td>nativeDotProduct(field)</td> - <td>0</td> - <td><p id="nativeDotProduct(field)"> - Calculates the sparse dot product between query term weights and match weights for the given field. - Example: A weighted set string field X: - </p> -<pre> -"X": { - "x": 10, - "y": 20, - "z": 30 -} -</pre> - <p> - For the query (x!2 OR y!4), the nativeDotProduct(X) feature - will have the value 100 (10*2+20*4) for that document. - </p> - {% include note.html content='<code>nativeDotProduct</code> and <code>nativeDotProduct(field)</code> - is less optimal for computing the dot product - - consider using <a href="#dotProduct(name,vector)">dotProduct(name,vector)</a>.'%} - </td></tr> - -<tr><td>nativeDotProduct</td> - <td>0</td> - <td><p id="nativeDotProduct"> - Calculates the sparse dot product between query term weights and match weights as above, but for all term/field combinations. - </td></tr> - -<tr><td>firstPhase</td> - <td>0</td> - <td> - <p id="firstPhase"> - The value of the rank score calculated in the first phase (unavailable in first phase ranking expressions) - </p> - </td></tr> - -<tr><td>secondPhase</td> - <td>0</td> - <td> - <p id="secondPhase"> - The value of the rank score calculated in the second phase (unavailable in first phase and second phase ranking expressions) - </p> - </td></tr> - -<tr><td>firstPhaseRank</td> - <td>max double value</td> - <td> - <p id="firstPhaseRank"> - The rank of the document after first phase within the content node when selecting which documents to rerank in second phase. The best document after first phase has rank 1, the second best 2, etc. The feature returns the default value for documents not selected for second phase ranking and for unsupported cases - (<a href="../../performance/streaming-search.html#differences-in-streaming-search">streaming search</a>, - <a href="../schemas/schemas.html#summary-features">summary features</a>, first phase expressions). Multiple documents can have the same <em>firstPhaseRank</em> value in multi-node configurations. - </p> - </td></tr> - -<tr><td>firstPhaseMax</td> - <td>-infinity</td> - <td> - <p id="firstPhaseMax"> - The maximum first-phase rank score among all hits matched on the content node. - Calculated locally on each content node, so may yield different results on different nodes. - </p> - </td></tr> - -<tr><td>relevanceScore</td> - <td>-</td> - <td> - <p id="relevancescore"> - The value of the rank score calculated either in the first or (when defined) in the second phase (unavailable in first phase and second phase ranking expressions) (since 8.559.30). - </p> - </td></tr> - -<tr><td colspan="3"></td></tr> -<tr><td colspan="3"><h3 id="global-features">Global features</h3></td></tr> -<tr class="trx"><th class="thx">Feature name</th><th class="thx">Default</th><th class="thx">Description</th></tr> - -<tr><td>globalSequence</td> - <td>n/a</td> - <td> - <p id="globalSequence"> - A global sequence number computed as (1 << 48) - (LocalDocumentId << 16 || <a href="../applications/services/content.html#node">distribution-key</a>). - This will give a global sequence to documents. This is a cheap way of having stable ordering of documents. Note the large range of this value. - Also note that if the system is not stable, e.g. if documents move around due to new nodes coming in, or nodes being removed, it will no longer - be stable as documents might be found in a different replica. If you need true global ordering we suggest assigning a unique numeric id - to your documents as an <a href="../schemas/schemas.html#attribute">attribute</a> field and use - the <a href="#attribute(name)">attribute(name)</a> feature. - </p> - </td></tr> - -<tr><td>now</td> - <td>n/a</td> - <td><p id="now">Time at which the query is executed in unix-time (seconds since epoch)</p></td></tr> - -<tr><td>random</td> - <td>n/a</td> - <td> - <p id="random"> - A pseudorandom number in the range [0,1> which is drawn once per document during rank evaluation. - By default, the current time in microseconds is used as a seed value. - Users can specify a seed value by setting - <a href="rank-feature-configuration.html#random">random.seed</a> in the rank profile. - If you need several independent random numbers the feature can be named like this: - <code>random(foo)</code>, <code>random(bar)</code>. - </p> - </td></tr> - -<tr><td>random.match</td> - <td>n/a</td> - <td> - <p id="random.match"> - A pseudorandom number in the range [0,1> that is stable for a given hit. - This means that a hit will always receive the same random score (on a single node). - If it is required that the scores be different between different queries, - specify a seed value dependent upon the query. By default, the seed value is 1024. - Users can specify a seed value by adding the query parameter - <a href="../api/query.html#ranking.properties">rankproperty.random.match.seed=<value></a>. - If you need several independent random numbers the feature can be named like this: - <code>random(foo).match</code>, <code>random(bar).match</code>. - </p> - </td></tr> - -<tr><td>randomNormal(<em>mean</em>,<em>stddev</em>)</td> - <td>0.0,1.0</td> - <td> - <p id="randomNormal(mean,stddev)"> - Same as <a href="#random">random</a>, except the random number is drawn from - the Gaussian distribution using the supplied mean and stddev parameters. - Can be called without parameters; default values are assumed. - Seed is set similarly as <em>random</em>. - If you need several independent random numbers with the same parameters, - the feature can be named like this: <code>randomNormal(0.0,1.0,foo)</code>, - <code>randomNormal(0.0,1.0,bar)</code>. - If the parameters to <em>randomNormal</em> are not the same, - you do not need to supply an additional name, - e.g. <code>randomNormal(0.0, 0.1)</code> and <code>randomNormal(0.0, 0.5)</code> - results in two independent values. - </p> - </td></tr> - -<tr><td>randomNormalStable(<em>mean</em>,<em>stddev</em>)</td> - <td>0.0,1.0</td> - <td> - <p id="randomNormalStable(mean,stddev)"> - Same as <a href="#randomNormal(mean,stddev)">randomNormal</a>, - except that the generated number is stable for a given hit, similar to - <a href="#random.match">random.match</a>. - </p> - </td></tr> - -<tr><td>constant(<em>name</em>)</td> - <td>n/a</td> - <td> - <p id="constant(name)">Returns the <a href="../schemas/schemas.html#constant">constant</a> tensor value.</p> - </td></tr> - - - - - - -<tr><td colspan="3"></td></tr> -<tr><td colspan="3"><h3 id="match-operator-scores">Match operator scores</h3></td></tr> -<tr><td colspan="3"> - See <a href="../../ranking/multivalue-query-operators.html#raw-scores-and-query-item-labeling"> - Raw scores and query item labeling</a> - </td></tr> -<tr class="trx"><th class="thx">Feature name</th><th class="thx">Default</th><th class="thx">Description</th></tr> - -<tr><td>rawScore(<em>field</em>)</td> - <td>0</td> - <td><p id="rawScore(field)">The sum of all raw scores produced by match operators for this field.</p></td></tr> - -<tr><td>itemRawScore(<em>label</em>)</td> - <td>0</td> - <td><p id="itemRawScore(label)">The raw score produced by the query item with the given label.</p></td></tr> - - - -<tr><td colspan="3"></td></tr> -<tr><td colspan="3"><h3 id="geo-search">Geo search</h3></td></tr> -<tr><td colspan="3"> - These features are for ranking on the distances between geographical coordinates, i.e. points on the surface of the earth defined by latitude/longitude pairs. See the main documentation for <a href="../../querying/geo-search.html"> - Geo Search</a>. - {% include note.html content='Some of these features have the same names as features used with the <a href="../querying/yql.html#nearestneighbor">nearestNeighbor</a> query operator. Take care not to get them mixed up!'%} - </td></tr> -<tr class="trx"><th class="thx">Feature name</th><th class="thx">Default</th><th class="thx">Description</th></tr> - - -<tr><td>closeness(<em>name</em>)</td> - <td>0</td> - <td><p id="closeness(name)"> - A number which is close to 1 if the position in attribute <em>name</em> is close to the query position compared - to <a href="rank-feature-configuration.html#closeness">maxDistance</a>: - </p><p> - <code>max(1-distance(name)/maxDistance , 0)</code> - </p><p> - Scales linearly with distance, see <a href="#closeness">closeness plot</a>.</p> - </td></tr> - -<tr><td>closeness(<em>name</em>).logscale</td> - <td>0</td> - <td><p id="closeness(name).logscale"> - A logarithmic-shaped closeness; like normal closeness it goes from 1 to 0, - but looks like <a href="#closeness">closeness plot</a>. - The function is a logarithmic fall-off based on - <code>log(distance + scale)</code> and is calculated as: - </p><p class="equation-container"><!-- depends on mathjax --> - $$closeness(name).logscale = \frac{log(maxDistance + scale) - log(distance(name) + scale))}{(log(maxDistance + scale) - log(scale))}$$ - </p><p> - where scale is defined using - <a href="rank-feature-configuration.html#closeness">halfResponse and maxDistance</a>: - </p><p class="equation-container"><!-- depends on mathjax --> - $$scale = \frac{halfResponse^2}{(maxDistance - 2 × halfResponse)}$$ - </p><p> - When <code>distance(name) == halfResponse</code> the function output is 0.5; - halfResponse should be less than <code>maxDistance/2</code> since that means - adding a certain distance when you are close matters more than adding - the same distance when you're already far away.</p> -</td></tr> - -<tr><td>distance(<em>name</em>)</td> - <td>6400M</td> - <td> - <p id="distance(name)"> - The Euclidean distance from the query position to the given position attribute in millionths of degrees (about 10 cm). - If there are multiple positions in the query, items that actually search in <em>name</em> is preferred. - Also: if multiple query items search in <em>name</em>, or <em>name</em> is an array of positions, or both, - the closest distance found is returned. - </p> - </td></tr> - -<tr><td>distance(<em>name</em>).km</td> - <td>711648.5</td> - <td> - <p id="distance(name).km"> - As above, but scaled, so it uses the kilometer as unit of distance, instead of "micro-degrees". - </p> - </td></tr> - -<tr><td>distance(<em>name</em>).index</td> - <td>-1</td> - <td> - <p id="distance(name).index"> - The array index of the closest position found. - Useful when <em>name</em> is of <code>array<position></code> type. - </p> - </td></tr> - -<tr><td>distance(<em>name</em>).latitude</td> - <td>90</td> - <td> - <p id="distance(name).latitude"> - The latitude (geographical north-south coordinate) of the closest position found. - In range from -90.0 (South Pole) to +90.0 (North Pole). - Useful when <em>name</em> is of <code>array<position></code> type. - </p> - </td></tr> - -<tr><td>distance(<em>name</em>).longitude</td> - <td>-180</td> - <td> - <p id="distance(name).longitude"> - The latitude (geographical east-west coordinate) of the closest position found. - In range from -180.0 (extreme west) to +180.0 (extreme east). - Useful when <em>name</em> is of <code>array<position></code> type. - </p> - </td></tr> - -<tr><td>distanceToPath(<em>name</em>).distance</td> - <td>6400M</td> - <td> - <p id="distanceToPath(name).distance"> - The Euclidean distance from a path through 2d space - given in the query to the given position attribute in - millionths of degrees. This is useful e.g. for finding the - closest locations to a given road. The query path is set in the - <a href="../api/query.html#ranking.properties">rankproperty.distanceToPath(<em>name</em>).path</a> - query parameter, using syntax <code>"(x1,y1,x2,y2,..)"</code> - also in millionth of degrees, see the - <a href="../../querying/geo-search.html#distance-to-path">distance to path</a> example. - The closest point along the path is referred to as the <em>intersection</em>. - </p> - {% include note.html content="For documents with multiple locations, - only the closest location is used for ranking purposes."%} - </td></tr> - -<tr><td>distanceToPath(<em>name</em>).traveled</td> - <td>1</td> - <td> - <p id="distanceToPath(name).traveled"> - The normalized distance along the query path traveled before intersection - (0.0 indicates start of path, 0.5 is middle, and 1.0 is end of path). - </p> - </td></tr> - -<tr><td>distanceToPath(<em>name</em>).product</td> - <td>0</td> - <td> - <p id="distanceToPath(name).product"> - The cross-product of the intersected path segment and the intersection-to-document vector. - Given that the document was found to lie closest to the path element <code>A->B</code>, - the intersected path segment vector is <code>[ B.x - A.x, B.y - A.y ]</code>. - Furthermore, given that the intersection of that path element - occurred at point <code>I</code> for document location <code>D</code>, - the intersection-to-document vector is <code>[ I.x - D.x, I.y - D.y]</code>. - This is useful e.g. for finding what side of a path a document exists - by looking at the sign of this value. - </p> - </td></tr> - - -<tr><td colspan="3"></td></tr> -<tr><td colspan="3"><h3 id="utility-features">Utility features</h3></td></tr> -<tr class="trx"><th class="thx">Feature name</th><th class="thx">Default</th><th class="thx">Description</th></tr> - -<tr> - <td>foreach(<em>dimension</em>, <em>variable</em>, <em>feature</em>, <em>condition</em>, <em>operation</em>)</td> - <td>n/a</td> - <td> - <p id="foreach(dimension,variable,feature,condition,operation)"> - <em>foreach</em> iterates over a set of feature output values and performs an operation on them. - Only the values where the condition evaluates to true are considered for the operation. - The result of this operation is returned. - </p> - <ul> - <li> <em>dimension</em>: Specifies what to iterate over. This can be: - <ul> - <li> <em>terms</em>: All query term indices, from 0 and up to - <a href="rank-feature-configuration.html#foreach">maxTerms</a>. - </li> <li> <em>fields</em>: All index field names. - </li> <li> <em>attributes</em>: All attribute field names. - </ul> - </li> <li> <em>variable</em>: The name of the variable 'storing' each of the items you are iterating over. - </li> <li> <em>feature</em>: The name of the feature you want to use the output value from. Use the <em>variable</em> as part of the feature name, and for each item you iterate over this <em>variable</em> is replaced with the actual item. - Note that the variable replacement is a simple string replace, so you should use a variable name - that is not in conflict with the feature name. - </li> <li> <em>condition</em>: The condition used on each feature output value to find out if the value should be considered by the operation. The condition can be: - <ul> - <li> <em>>a</em>: Use feature output if greater than number a. - </li> <li> <em><a</em>: Use feature output if less than number a. - </li> <li> <em>true</em>: Use all feature output values. - </ul> - </li> <li> <em>operation</em>: The operation you want to perform on the feature output values. This can be: - <ul> - <li> <em>sum</em>: Calculate the sum of the values. - </li> <li> <em>product</em>: Calculate the product of the values. - </li> <li> <em>average</em>: Calculate the average of the values. - </li> <li> <em>max</em>: Find the max of the values. - </li> <li> <em>min</em>: Find the min of the values. - </li> <li> <em>count</em>: Count the number of values. - </ul> - </ul> - <p> - Let's say you want to calculate the average score of the <em>fieldMatch</em> feature for all index fields, - but only consider the scores larger than 0. Then you can use the following setup of the <em>foreach</em> feature: - </p><p> - <code>foreach(fields,N,fieldMatch(N), ">0", average)</code>. - </p><p> - Note that when using the conditions <em>>a</em> and <em><a</em> - the arguments must be quoted. - </p><p> - You can also specify a ranking expression in the <em>foreach</em> feature by using the <em>rankingExpression</em> feature. - The <em>rankingExpression</em> feature takes the expression as the first and only parameter and - outputs the result of evaluating this expression. - Let's say you want to calculate the average score of the squared <em>fieldMatch</em> feature score for all index fields. - Then you can use the following setup of the <em>foreach</em> feature: - </p><p> - <code>foreach(fields, N, rankingExpression("fieldMatch(N)*fieldMatch(N)"), true, average)</code> - </p><p> - Note that you must quote the expression passed in to the <em>rankingExpression</em> feature. - </p> - </td></tr> - -<tr><td>dotProduct(<em>name</em>,<em>vector</em>)</td> - <td>0</td> - <td> - <p id="dotProduct(name,vector)"></p> - {% include note.html content='Most dot product use cases are better solved using - <a href="../../ranking/tensor-user-guide.html">tensors</a>.'%} - <p> - The sparse dot product of the vector represented by the given weighted set attribute and - the vector sent down with the query. - </p><p> - You can also do an ordinary full dotproduct by using arrays - instead of weighted sets. This will be a lot faster when you have full vectors in the document with - more than 5-10% non-zero values. You are also then not limited to integer weights. All the numeric - datatypes can be used with arrays, so you have full floating point support. - The 32 bit floating point type yields the fastest execution. - <ul> - <li> <em>name</em>: The name of the weighted set string/integer or array of numeric attribute. - <li> <em>vector</em>: The name of the vector sent down with the query. - </ul> - <p> - Each unique string/integer in the weighted set corresponds to a dimension - and the belonging weight is the vector component for that dimension. - The query vector is set in the - <a href="../api/query.html#ranking.properties">rankproperty.dotProduct.<em>vector</em></a> - query parameter, using syntax - <code>{d1:c1,d2:c2,…}</code> where <em>d1</em> and <em>d2</em> are dimensions matching the strings/integers in - the weighted set and <em>c1</em> and <em>c2</em> are the vector components (floating point numbers). - The number of dimensions in the weighted set and the query vector do not need to be the same. - When calculating the dot product we only use the dimensions present in both the weighted set and the - query vector. - </p><p> - When using an array the dimensions is a positive integer starting at 0. If the query is sparse all non given dimensions - are zero. That also goes for dimensions that outside of the array size in each document. - </p><p> - Assume a weighted set string attribute X with: -<pre> -"X": { - "x": 10, - "y": 20 -} -</pre> - <p> - for a particular document. - The result of using the feature dotProduct(X,Y) with the query vector - rankproperty.dotProduct.Y={x:2,y:4} will then be 100 (10*2+20*4) for this document. - </p><p> - Arrays can be passed down as <code>[w1 w2 w3 …]</code> or on sparse form <code>{d1:c1,d2:c2,…}</code> - as is already supported for weighted sets. - </p> - {% include note.html content='When the query vector ends up being the same as the query, - it is better to annotate the query terms with weights - (see <a href="../querying/simple-query-language.html#term-weight">term weight</a>) - and use the nativeDotProduct feature instead. - This will run more efficiently and improve the correlation between - results produced by the WAND operator and the final relevance score.'%} - {% include note.html content='When using the dotProduct feature, - <a href="../../content/attributes.html#fast-search">fast-search</a> is not needed, - unless also used for searching. - When using the dotProduct query operator, use fast-search.'%} - </td></tr> - -<tr> - <td>tokenInputIds(<em>length</em>, <em>input_1</em>, <em>input_2</em>, <em>...</em>)</td> - <td>n/a</td> - <td> - <p id="tokenInputIds(length, input_1, input_2, ...)"> - Convenience function for generating token sequence input to Transformer models. - Creates a tensor with dimensions <code>d0[1], d1[length]</code>, where - <code>d0</code> is the batch dimension and <code>d1</code> is the maximum length - of the token sequence. Assumes the inputs are zero-padded tensors representing - token sequences. The result is the token sequence: - </p> - - <p> - <code>CLS + input_1 + SEP + input_2 + SEP + ... + 0's</code> - </p> - - <ul> - <li><em>length</em>: The maximum length of the token sequence</li> - <li><em>input_N</em>: Where to retrieve input from. At least one is required.</li> - </ul> - - <p> - The inputs are typically retrieved from the query, document attributes or constants. - For instance, <code>tokenInputIds(128, query(my_input), attribute(my_field))</code> - where input types are: - </p> - - <ul> - <li><code>query(my_input): tensor(d0[32])</code></li> - <li><code>attribute(my_field): tensor(d0[128])</code></li> - </ul> - - <p> - will create a tensor of type <code>d0[1],d1[128]</code> consisting of the CLS token - <code>101</code>, the tokens from the query, the SEP token <code>102</code>, the - tokens from the document field, the SEP token <code>102</code>, and 0's for the - rest of the tensor. - </p> - </td> -</tr> - -<tr> - <td>customTokenInputIds(<em>start_sequence_id</em>, <em>sep_sequence_id</em><em>length</em>, <em>input_1</em>, <em>input_2</em>, <em>...</em>)</td> - <td>n/a</td> - <td> - <p id="customTokenInputIds(start_sequence_id, sep_sequence_id, length, input_1, input_2, ...)"> - Convenience function for generating token sequence input to Transformer models. - Creates a tensor with dimensions <code>d0[1], d1[length]</code>, where - <code>d0</code> is the batch dimension and <code>d1</code> is the maximum length - of the token sequence. Assumes the inputs are zero-padded tensors representing - token sequences. The result is the token sequence: - </p> - - <p> - <code>start_sequence_id + input_1 + sep_sequence_id + input_2 + sep_sequence_id + ... + 0's</code> - </p> - - <ul> - <li><em>start_sequence_id</em>The start sequence id, typically <em>1</em></li> - <li><em>sep_sequence_id</em>The separator sequence id, typically <em>2</em></li> - <li><em>length</em>: The maximum length of the token sequence</li> - <li><em>input_N</em>: Where to retrieve input from. At least one is required.</li> - </ul> - - <p> - The inputs are typically retrieved from the query, document attributes or constants. - For instance, <code>customTokenInputIds(1,2,128, query(my_input), attribute(my_field))</code> - where input types are: - </p> - <ul> - <li><code>query(my_input): tensor(d0[32])</code></li> - <li><code>attribute(my_field): tensor(d0[128])</code></li> - </ul> - </td> -</tr> - - -<tr> - <td>tokenTypeIds(<em>length</em>, <em>input_1</em>, <em>input_2</em>, <em>...</em>)</td> - <td>n/a</td> - <td> - <p id="tokenTypeIds(length, input_1, input_2, ...)"> - Convenience function for generating token sequence input to Transformer models. - Similar to the <code>tokenInputIds</code>, creates a tensor of type - <code>d0[1],d1[length]</code> which represents a mask with zeros for the - first input including CLS and SEP token, ones for the rest of the inputs - (up to and including the final SEP token), and 0's for the rest of the tensor. - </p> - </td> -</tr> - -<tr> - <td>tokenAttentionMask(<em>length</em>, <em>input_1</em>, <em>input_2</em>, <em>...</em>)</td> - <td>n/a</td> - <td> - <p id="tokenAttentionMask(length, input_1, input_2, ...)"> - Convenience function for generating token sequence input to Transformer models. - Similar to the <code>tokenInputIds</code>, creates a tensor of type - <code>d0[1],d1[length]</code> which represents a mask with ones for all - tokens that are set (CLS and SEP and all inputs), and zeros for the rest. - </p> - </td> -</tr> - -</table> - - - -<h2 id="graphs">Graphs for selected ranking functions</h2> - -<h3 id="closeness">closeness</h3> -<img src="/assets/img/relevance/closeness-logscale.png" alt="Closeness logscale plot"/> -<p> -The plot above shows the possible outputs from the closeness distance rank feature -using the default maxDistance of 1000 km. The <em>linear(x)</em> graph shows the default closeness -output while the other graphs are logscale output for various values of the scaleDistance -parameter: 9013.305 (1 km), 45066.525 (5 km - the default value), and 901330.5 (100 km). -These values correspond to the following values of the halfResponse parameter: -276154.903 (30.64 km), 593861.739 (65.89 km), and 2088044.581 (231.66 km). -</p> -<!-- gnuplot input (for image on previous line) - - maxd = 1000 - linear(x) = (x > maxd ? 0 : 1-x/maxd) - logscale(x, scale) = (x > maxd ? 0 : (( log(maxd+scale) - log(x+scale) ) / (log(maxd+scale) - log(scale)))) - logscale_100km(x) = logscale(x,100) - logscale_5km(x) = logscale(x,5) - logscale_1km(x) = logscale(x,1) - set terminal png picsize 1000 400 - set output "closeness-logscale.png" - plot [0:1100] [-0.1:1.1] logscale_5km(x), logscale_1km(x), logscale_100km(x), linear(x) - -gnuplot script --> - - -<h3 id="freshness">freshness</h3> -<img src="/assets/img/relevance/freshness-logscale.png" alt="Freshness logscale plot"/> -<p> -The plot above shows the possible outputs from the freshness rank feature -using the default maxAge of 7776000s (90 days). The <em>linear(x)</em> graph shows the default freshness -output while the other graphs are logscale output for various values of the halfResponse -parameter: 172800s (2 days), 604800s (7 days - the default value), 1209600s (14 days). -</p> -<!-- gnuplot input (for image on previous line) - -maxa = 90 -linear(x) = (x > maxa ? 0 : 1-x/maxa) -logscale(x, scale) = (x > maxa ? 0 : (( log(maxa+scale) - log(x+scale) ) / (log(maxa+scale) - log(scale)))) -# scale = -halfResponse*halfResponse/(2*halfResponse - maxa) -logscale_2d(x) = logscale(x,0.047) -logscale_7d(x) = logscale(x,0.64) -logscale_14d(x) = logscale(x,3.16) -set terminal png size 1000,400 -set output "freshness-logscale.png" -plot [0:90] [0:1] logscale_2d(x), logscale_7d(x), logscale_14d(x), linear(x) - -gnuplot script --> diff --git a/en/reference/ranking/rank-features.mdx b/en/reference/ranking/rank-features.mdx new file mode 100644 index 0000000000..5e7c1aa2d9 --- /dev/null +++ b/en/reference/ranking/rank-features.mdx @@ -0,0 +1,904 @@ +--- +title: "Rank Feature Reference" +sidebarTitle: "Rank features" +--- + +This is the list of the rank features in Vespa. These features are available during document ranking for combination into a complete rank score by a [ranking expression](/en/reference/ranking/ranking-expressions). The features are a combination of coarse grained features suitable for handwritten expressions, and finer grained features suitable for machine learning. + +See also [the overview of the ranking framework](/en/basics/ranking), and [rank feature configuration parameters](/en/reference/ranking/rank-feature-configuration). Notes: + +- Types: All rank feature values are doubles or tensors. Integers are converted to exact whole value doubles. String values are converted to exact whole value doubles using a hash function. String literals in ranking expressions are converted using the same hash function, to enable equality tests on string values. +- Features which are _normalized_ are between 0 and 1, where 0 is always the minimum and 1 the maximum. Normalized features should normally be preferred because they are more easily combined by [ranking expressions](/en/reference/ranking/ranking-expressions) into a complete normalized score. +- A query may override _any_ rank feature value by submitting that value as a feature with the query. +- Some features have parameters. It is always allowed to quote parameters with _"_. Nested quotes are not allowed and must be escaped using _\_. Parameters that can be parsed as feature names may be left unquoted. Examples: _foo(bar(baz(5.5)))_, _foo("bar(\"baz(\\\"5.5\\\")\")")_, _foo("need quote")_ + +## Feature list +### Query features + +- **query(value)**<br/> + Default: 0 + + An application specific feature submitted with the query, see [using the query feature](/en/ranking/ranking-expressions-features#using-query-variables). +- **term(n).significance**<br/> + Default: 0 + + A normalized number (between 0.0 and 1.0) describing the significance of the term; used as a multiplier or weighting factor by many other text matching rank features. + + This should ideally be set by a searcher in the container for global correctness as each node will estimate the significance values from the local corpus. Use the [Java API for significance](https://javadoc.io/doc/com.yahoo.vespa/container-search/latest/com/yahoo/prelude/query/TaggableItem.html#setSignificance\(double\)) or [YQL annotation for significance](/en/reference/querying/yql#significance). + + As a fallback, a significance based on Robertson-Sparck-Jones term weighting is used; it is logarithmic from 1.0 for rare terms down to 0.5 for common terms (those occurring in every document seen). + + Note that "rare" is defined as a frequency of 0.000001 or less. This is the term document frequency (how many documents contain the term out of all documents that can be observed), so you cannot get 1.0 as the fallback until you actually have a large number of documents (minimum 1 million) in the same search process. + See [numTerms](/en/reference/ranking/rank-feature-configuration#term) config. +- **term(n).weight**<br/> + Default: 100 + + The importance of matching this query term given in the query +- **term(n).connectedness**<br/> + Default: 0.1 + + The normalized strength with which this term is connected to the previous term in the query. Must be assigned to query terms in a [searcher](/en/applications/searchers) using the [Java API for connectivity](https://javadoc.io/doc/com.yahoo.vespa/container-search/latest/com/yahoo/prelude/query/TaggableItem.html#setConnectivity\(com.yahoo.prelude.query.Item,double\)) or [YQL annotation for connectivity](/en/reference/querying/yql#connectivity). +- **queryTermCount**<br/> + Default: 0 + + The total number of terms in this query, including both user and synthetic terms in all fields. + +### Document features + + - **fieldLength(name)**<br/> + Default: 1000000 + + The number of terms in this field if one or more query term matched the field, 1000000 if no query term matched the field. +- **attribute(name)**<br/> + + Default: null + + The value of a [tensor](/en/reference/schemas/schemas#tensor) or single value *numeric* attribute or null/NaN if not set. Use *isNan()* to check if value is not defined. Using undefined values in ranking expressions leads to undefined behavior. +- **attribute(name,n)**<br/> + Default: 0 + + The value at index n (base 0) of a *numeric* array attribute with the given name. Note that the index number must be explicit, it cannot be the output of an [expression function](/en/reference/schemas/schemas#function-rank). The order of the items in an array attribute is the same as the order they have in the input feed. If items are added using partial updates they are added to the end of the existing items list. +- **attribute(name,key).weight**<br/> + Default: 0 + + The weight found at a given key in a weighted set attribute +- **attribute(name,key).contains**<br/> + Default: 0 + + 1 if the given key is present in a weighted set attribute, 0 otherwise +- **attribute(name).count**<br/> + Default: 0 + + The number of elements in the attribute with the given name. +- **tensorFromWeightedSet(source,dimension)**<br/> + Default: empty tensor + + Creates a `tensor<double>` with one mapped dimension from the given integer or string weighted set attribute. The attribute is specified as the full feature name, `attribute(name)`. The *dimension* parameter is optional. If omitted the dimension name will be the attribute name. + Example: Given the weighted set: + + ```bash + {key1:0, key2:1, key3:2.5} + ``` + *tensorFromWeightedSet(attribute(myField), dim)* produces: + ```bash + tensor<double>(dim{}):{ {dim:key1}:0.0, {dim:key2}:1.0, {dim:key3}:2.5} } + ``` + <Info> + **Note:** This creates a temporary tensor, and has build cost and extra memory is touched. Tensor evaluation is most effective when the cell types of all tensors are equal - use [cell\_cast](/en/reference/ranking/ranking-expressions#cell_cast) to enable optimizations. Also, duplicating the field in the schema to a native tensor instead of creating from a set can increase performance. + </Info> + +- **tensorFromLabels(attribute,dimension)**<br/> + Default: empty tensor + + Creates a `tensor<double>` with one mapped dimension from the given single value or array attribute. The value(s) must be integers or strings. The attribute is specified as the full feature name, `attribute(name)`. The *dimension* parameter is optional. If omitted the dimension name will be the attribute name. + Example: Given an attribute field `myField` containing the array value: + + ```bash + [v1, v2, v3] + ``` + *tensorFromLabels(attribute(myField), dim)* produces: + ```bash + tensor<double>(dim{}):{ {dim:v1}:1.0, {dim:v2}:1.0, {dim:v3}:1.0} } + ``` + See *tensorFromWeightedSet* for performance notes. + +- **tensorFromStructs(attribute,key,value,type)**<br/> + Default: empty tensor + + Creates a `tensor<type>` with one mapped dimension from the given `array<struct>` attribute. Keys are taken from the struct field *key* and values from the struct field *value*. The resulting tensor will have one mapped dimension named after the *key* field. The *type* parameter is required and must be `float` or `double`. + + Example: Given an `array<struct>` attribute `items` with fields `name` (string) and `price` (float): + + ```bash + tensorFromStructs(attribute(items), name, price, float) + ``` + ```bash + tensor<float>(name{}):{ {name:apple}:1.5, {name:banana}:0.75, {name:cherry}:2.25 } + ``` + + Example: Integer keys and float values: + ```bash + tensorFromStructs(attribute(ids), id, score, double) + ``` + ```bash + tensor(id{}):{ {id:100}:10.5, {id:200}:20.75, {id:300}:30.25 } + ``` + The function takes at least four arguments; it's possible to use several *key* arguments to get multiple mapped dimensions. If the `struct` above also contained a `region` field one could use + + ```bash + tensorFromStructs(attribute(items), name, region, price, float) + ``` + to get a `tensor<float>(name{},region{})` making it possible to have different prices of apples in different regions. + + *Details:* Empty or missing arrays yield an empty tensor of the requested type. The first argument must be an `attribute(...)` source. It's an application responsibility to ensure that the key (or combination of keys in the multi-dimensional case) is unique. + + See *tensorFromWeightedSet* for performance notes. + +### Field match features - normalized + +fieldMatch features provide a good measure of the degree to which a query matches the text of a field, but are expensive to calculate and therefore often only suitable for [second-phase](/en/ranking/phased-ranking) ranking expressions. See the [string segment match](/en/reference/ranking/string-segment-match) document for details on the algorithm computing this rank-feature set. Note that even using a fine-grained sub features like fieldMatch(_name_).absoluteOccurrence will have the same complexity and cost as using the general top level fieldMatch(_name_) feature. + +- **fieldMatch(name)**<br/> + Default: 0 + + A normalized measure of the degree to which this query and field matched (default, the long name of this is `match`). Use this if you do not want to create your own combination function of more fine-grained fieldmatch features. + +- **fieldMatch(name).proximity**<br/> + Default: 0 + + Normalized proximity - a value which is close to 1 when matched terms are close *inside each segment*, and close to zero when they are far apart inside segments. Relatively more connected terms influence this value more. This is absoluteProximity/average connectedness for the query terms for this field. + + Note that if all the terms are far apart, the proximity will be 1, but the number of segments will be high. Proximity is only concerned with closeness within segments, a total score must also take the number of segments into account. + +- **fieldMatch(name).completeness**<br/> + Default: 0 + + The normalized total completeness, where field completeness is more important: + + queryCompleteness * ( 1 - [fieldCompletenessImportance](/en/reference/ranking/rank-feature-configuration#fieldMatch) ) + [fieldCompletenessImportance](/en/reference/ranking/rank-feature-configuration#fieldMatch) * fieldCompleteness +- **fieldMatch(name).queryCompleteness**<br/> + Default: 0 + + The normalized ratio of query tokens matched in the field: + + `matches/query terms searching this field` +- **fieldMatch(name).fieldCompleteness**<br/> + Default: 0 + + The normalized ratio of query tokens which was matched in the field: + + `matches/fieldLength` +- **fieldMatch(name).orderness**<br/> + Default: 0 + + A normalized metric of how well the order of the terms agrees in the chosen segments: + + `1-outOfOrder/pairs` +- **fieldMatch(name).relatedness**<br/> + Default: 0 + + A normalized measure of the degree to which different terms are related (occurring in the same segment): + + `1-(segments-1)/(matches-1)` +- **fieldMatch(name).earliness**<br/> + Default: 0 + + A normalized measure of how early the first segment occurs in this field. + +- **fieldMatch(name).longestSequenceRatio**<br/> + Default: 0 + + A normalized metric of the relative size of the longest sequence: + + `longestSequence/matches` + +- **fieldMatch(name).segmentProximity**<br/> + Default: 0 + + A normalized metric of the closeness (inverse of spread) of segments in the field: + + `1-segmentDistance/fieldLength` + +- **fieldMatch(name).unweightedProximity**<br/> + Default: 0 + + The normalized proximity of the matched terms, not taking term connectedness into account. This number is close to 1 if all the matched terms are following each other in sequence, and close to 0 if they are far from each other or out of order. + +- **fieldMatch(name).absoluteProximity**<br/> + Default: 0 + + Returns the normalized proximity of the matched terms, weighted by the connectedness of the query terms. This number is 0.1 if all the matched terms are and have default or lower connectedness, close to 1 if they are following in sequence and have a high connectedness, and close to 0 if they are far from each other in the segments or out of order. + +- **fieldMatch(name).occurrence**<br/> + Default: 0 + + Returns a normalized measure of the number of occurrences of the terms of the query. This is 1 if there are many occurrences of the query terms *in absolute terms, or relative to the total content of the field*, and 0 if there are none. + + This is suitable for occurrence in fields containing regular text. + +- **fieldMatch(name).absoluteOccurrence**<br/> + Default: 0 + + Returns a normalized measure of the number of occurrence of the terms of the query: + + $$ + \frac{\underset{\text{all query terms}}{\sum} \left(\right. m i n \left(\right. \text{number of occurrences of the term} , m a x O c c u r r e n c e s \left.\right) \left.\right)}{\left(\right. \text{query term count} \times 100 \left.\right)} + $$ + + This is 1 if there are many occurrences of the query terms, and 0 if there are none. + + This number is not relative to the field length, so it is suitable for uses of occurrence to denote relative importance between matched terms (i.e. fields containing keywords, not normal text). + +- **fieldMatch(name).weightedOccurrence**<br/> + Default: 0 + + Returns a normalized measure of the number of occurrence of the terms of the query, weighted by term weight. This number is close to 1 if there are many occurrences of highly weighted query terms, in absolute terms, or relative to the total content of the field, and 0 if there are none. + +- **fieldMatch(name).weightedAbsoluteOccurrence**<br/> + Default: 0 + + Returns a normalized measure of the number of occurrence of the terms of the query, taking weights into account so that occurrences of higher weighted query terms has more impact than lower weighted terms. + This is 1 if there are many occurrences of the highly weighted terms, and 0 if there are none. + + This number is not relative to the field length, so it is suitable for uses of occurrence to denote relative importance between matched terms (i.e. fields containing keywords, not normal text). + +- **fieldMatch(name).significantOccurrence**<br/> + Default: 0 + + Returns a normalized measure of the number of occurrence of the terms of the query *in absolute terms, or relative to the total content of the field*, weighted by term significance. + + This number is 1 if there are many occurrences of the highly significant terms, and 0 if there are none + +### Field match features - normalized and relative to the whole query +- **fieldMatch(name).weight**<br/> + Default: 0 + + The normalized weight of this match relative to the whole query: The sum of the weights of all *matched* terms/the sum of the weights of all *query* terms. If all the query terms were matched, this is 1. If no terms were matched, or these matches has weight zero this is 0. + + As the sum of this number over all the terms of the query is always 1, sums over all fields of normalized rank features for each field multiplied by this number for the same field will produce a normalized number. + + Note that this scales with the number of matched query terms in the field. If you want a component which does not, divide by matches. + +- **fieldMatch(name).significance**<br/> + Default: 0 + + Returns the normalized term significance of the terms of this match relative to the whole query: The sum of the significance of all *matched* terms/the sum of the significance of all *query* terms. If all the query terms were matched, this is 1. If no terms were matched, or if the significance of all the matched terms is zero, this number is zero. + + This metric has the same properties as weight. + + See the [term(n).significance](/en/reference/ranking/rank-features#term(n).significance) feature for how the significance for a single term is calculated. +- **fieldMatch(name).importance**<br/> + Default: 0 + + Returns the average of significance and weight. This has the same properties as those metrics. +### Field match features - not normalized + +- **fieldMatch(name).segments**<br/> + Default: 0 + + The number of field text segments which are needed to match the query as completely as possible +- **fieldMatch(name).matches**<br/> + Default: 0 + + The total number of query terms which was matched in this field +- **fieldMatch(name).degradedMatches**<br/> + Default: 0 + + The number of degraded query terms which was matched in this field. A degraded term is a term where no occurrence information is available during calculation. The number of degraded matches is less than or equal to the total number of matches. +- **fieldMatch(name).outOfOrder**<br/> + Default: 0 + + The total number of out of order token sequences *within* matched field segments +- **fieldMatch(name).gaps**<br/> + Default: 0 + + The total number of position jumps (backward or forward) within field segments +- **fieldMatch(name).gapLength**<br/> + Default: 0 + + The summed length of all gaps within segments +- **fieldMatch(name).longestSequence**<br/> + Default: 0 + + The size of the longest matched continuous, in-order sequence in the field +- **fieldMatch(name).head**<br/> + Default: 0 + + The number of tokens in the field preceding the start of the first matched segment +- **fieldMatch(name).tail**<br/> + Default: 0 + + The number of tokens in the field following the end of the last matched segment +- **fieldMatch(name).segmentDistance**<br/> + Default: 0 + + The sum of the distance between all segments making up a match to the query, measured as the sum of the number of token positions separating the *start* of each *field* adjacent segment. + +### Query and field similarity + - **fieldTermMatch(name,n).firstPosition**<br/> + Default: 1000000 + + The position of the first occurrence of this query term in this index field. [numTerms](/en/reference/ranking/rank-feature-configuration#fieldTermMatch) configuration +- **fieldTermMatch(name,n).occurrences**<br/> + Default: 0 + + The number of occurrences of this query term in this index field + +- **matchCount(name)**<br/> + Default: 0 + + Returns number of times any term in the query matches this index/attribute field. + +- **matches(name)**<br/> + Default: 0 + + Returns 1 if the index/attribute field with the given name is matched by the query. +- **matches(name,n)**<br/> + Default: 0 + + Returns 1 if the index/attribute field with the given name is matched by the query term with position *n*. +- **termDistance(name,x,y).forward**<br/> + Default: 1000000 + + The minimum distance between the occurrences of term *x* and term *y* in this index field. Term *x* occurs before term *y*. + +- **termDistance(name,x,y).forwardTermPosition**<br/> + Default: 1000000 + + The position of the occurrence of term *x* in this index field used for the forward distance. +- **termDistance(name,x,y).reverse**<br/> + Default: 1000000 + + The minimum distance between the occurrences of term *y* and term *x* in this index field. Term *y* occurs before term *x*. +- **termDistance(name,x,y).reverseTermPosition**<br/> + Default: 1000000 + + The position of the occurrence of term *y* in this index field used for the reverse distance. + +### Query term and field match features + +- **fieldTermMatch(name,n).firstPosition**<br/> + Default: 1000000 + + The position of the first occurrence of this query term in this index field. [numTerms](/en/reference/ranking/rank-feature-configuration#fieldTermMatch) configuration +- **fieldTermMatch(name,n).occurrences**<br/> + Default: 0 + + The number of occurrences of this query term in this index field +- **matchCount(name)**<br/> + Default: 0 + + Returns number of times any term in the query matches this index/attribute field. +- **matches(name)**<br/> + Default: 0 + + Returns 1 if the index/attribute field with the given name is matched by the query. +- **matches(name,n)**<br/> + Default: 0 + + Returns 1 if the index/attribute field with the given name is matched by the query term with position *n*. +- **termDistance(name,x,y).forward**<br/> + Default: 1000000 + + The minimum distance between the occurrences of term *x* and term *y* in this index field. Term *x* occurs before term *y*. +- **termDistance(name,x,y).forwardTermPosition**<br/> + Default: 1000000 + + The position of the occurrence of term *x* in this index field used for the forward distance. +- **termDistance(name,x,y).reverse**<br/> + Default: 1000000 + + The minimum distance between the occurrences of term *y* and term *x* in this index field. Term *y* occurs before term *x*. +- **termDistance(name,x,y).reverseTermPosition**<br/> + Default: 1000000 + + The position of the occurrence of term *y* in this index field used for the reverse distance + +### Features for indexed multivalue string fields +- **elementCompleteness(name).completeness**<br/> + Default: 0 + + A weighted combination of fieldCompleteness and queryCompleteness for the element in the field that produces the highest value for this output after the elements weight is factored in. The weighting can be adjusted using [elementCompleteness(name).fieldCompletenessImportance](/en/reference/ranking/rank-feature-configuration#elementCompleteness). +- **elementCompleteness(name).fieldCompleteness**<br/> + Default: 0 + + The field completeness of the best matching element. This is calculated as: + + `max( (number of query terms matched in the element) / (element size), 1.0)`. +- **elementCompleteness(name).queryCompleteness**<br/> + Default: 0 + + The query completeness of the best matching element. This is calculated as: + + `(sum of weight for query terms matched in the element) / (sum of weight for query terms searching the field)`. +- **elementCompleteness(name).elementWeight**<br/> + Default: 0 + + The weight of the best matching element, starting from the default - i.e., negative weights will return 0. +- **elementSimilarity(name)**<br/> + Default: 0 + + Aggregated similarity between the query and individual field elements. The same sub-scores used by the `textSimilarity` feature are calculated for each individual element in the field. The final output is calculated as the maximum of the combined element similarity measures (similarity measures are combined the same way as the default output of the `textSimilarity` feature) multiplied with the element weight which is 1 for arrays, and the supplied weights for indexed weighted sets. + + This is a flexible feature; how sub-scores are combined for each element and how element scores are aggregated may be configured. You may also add additional outputs if you want to capture multiple signals from a single field. Use [elementSimilarity](/en/reference/ranking/rank-feature-configuration#elementSimilarity) to customize this feature. +### Attribute match features - normalized + - **attributeMatch(name)**<br/> + Default: 0 + + A normalized measure of the degree to which this query and field matched. This is currently the same as completeness. Note that depending on what the attribute is used for, this may or may not be a suitable metric. If the attribute is a weighted set representing counts of items (like tags), `normalizedWeight` is probably a better metric. +- **attributeMatch(name).completeness**<br/> + Default: 0 + + The normalized total completeness, where field completeness is more important: + + queryCompleteness * ( 1 - [fieldCompletenessImportance](/en/reference/ranking/rank-feature-configuration#attributeMatch) + [fieldCompletenessImportance](/en/reference/ranking/rank-feature-configuration#attributeMatch) * fieldCompleteness )` +- **attributeMatch(name).queryCompleteness**<br/> + Default: 0 + + The query completeness for this attribute: + `matches/the number of query terms searching this attribute` +- **attributeMatch(name).fieldCompleteness**<br/> + Default: 0 + + The normalized ratio of query tokens which was matched in the field. For arrays: `matches/array length` For weighted sets: `sum of weight of matched terms/sum of weights of entire set`. This is relatively expensive to calculate for large weighted sets. +- **attributeMatch(name).normalizedWeight**<br/> + Default: 0 + + A number which is close to 1 if the attribute weights of most matches in a weighted set are high (relative to [maxWeight](/en/reference/ranking/rank-feature-configuration#attributeMatch)), 0 otherwise +- **attributeMatch(name).normalizedWeightedWeight**<br/> + Default: 0 + + A number which is close to 1 if the attribute weights of most matches in a weighted set are high (relative to [maxWeight](/en/reference/ranking/rank-feature-configuration#attributeMatch)), and where highly weighted query terms has more impact, 0 otherwise +- **closeness(dimension,name)**<br/> + Default: 0 + + Used with the [nearestNeighbor](/en/reference/querying/yql#nearestneighbor) query operator. A number which is close to 1 when a vector in the document tensor is close to the vector given in the query. The indexed dimension representing a vector in document and query tensors must be identical. + - *dimension*: Specifies the dimension of *name*. This must be either the string `field` or the string `label`. + When using `field`, the name given must be a field with a tensor attribute of appropriate type. Often used when the document type has only one vector field, see [example](/en/querying/nearest-neighbor-search#minimal-example). + When using `label`, queries are assumed to contain a [nearestNeighbor](/en/reference/querying/yql#nearestneighbor) query item with a [label](/en/reference/querying/yql#label) that matches the given *name*. This is useful when having multiple vector fields, where `closeness()` then maps to the nearestNeighbor operator with the field configured. [Example](/en/querying/nearest-neighbor-search-guide#using-label). + - *name*: The value of the field name or label. + + <Info> + **Note:** `closeness()` is calculated **only** based on the vectors matched with nearestNeighbor operator. This means that the value of `closeness()` is not necessarily calculated based on the same vector returned by `closest()` rank feature if nearestNeighbor search is approximate, as `closest()` will be calculated based on *all* specified document vectors. + </Info> + The output value is + $$ + closeness(dimension, name) = \frac{1.0}{1.0 + distance(dimension, name)} + $$ + When the tensor field stores multiple vectors per document, the minimum distance between the vectors of a document and the query vector is used in the calculation above. +- **freshness(name)**<br/> + Default: 0 + + A number which is close to 1 if the timestamp in attribute *name* is recent compared to the current time compared to [maxAge](/en/reference/ranking/rank-feature-configuration#freshness): + + `max( 1-age(name)/maxAge , 0 )` + + Scales linearly with age, see [freshness plot](#freshness). +- **freshness(name).logscale**<br/> + Default: 0 + + A logarithmic-shaped freshness; also goes from 1 to 0, but looks like [freshness plot](/en/reference/ranking/rank-features#freshness). The function is based on `-log(age(name) + scale)` and is calculated as: + + $$ + \frac{l o g \left(\right. m a x A g e + s c a l e \left.\right) - l o g \left(\right. a g e \left(\right. n a m e \left.\right) + s c a l e \left.\right)}{l o g \left(\right. m a x A g e + s c a l e \left.\right) - l o g \left(\right. s c a l e \left.\right)} + $$ + + where scale is defined using [halfResponse and maxAge](/en/reference/ranking/rank-feature-configuration#freshness): + + $$ + \frac{- h a l f R e s p o n s e^{2}}{2 \times h a l f R e s p o n s e - m a x A g e} + $$ + + When `age(name) == halfResponse` the function output is 0.5. +### Attribute match features - normalized and relative to the whole query + +- **attributeMatch(name).weight**<br/> + Default: 0 + + This has the same semantics as fieldMatch(*name*).weight. +- **attributeMatch(name).significance**<br/> + Default: 0 + + This has the same semantics as fieldMatch(*name*).significance. +- **attributeMatch(name).importance**<br/> + Default: 0 + + Returns the average of significance and weight. This has the same properties as those metrics. + +### Attribute match features - not normalized + +- **attributeMatch(name).matches**<br/> + Default: 0 + + The number of query terms which was matched in this attribute +- **attributeMatch(name).totalWeight**<br/> + Default: 0 + + The sum of the weights of the attribute keys matched in a weighted set attribute +- **attributeMatch(name).averageWeight**<br/> + Default: 0 + + totalWeight/matches +- **attributeMatch(name).maxWeight**<br/> + Default: 0 + + The maximum weight of the attribute keys matched in a weighted set attribute +- **closest(name)**<br/> + Default: {} + + Used with the [nearestNeighbor](/en/reference/querying/yql#nearestneighbor) query operator and a tensor field attribute *name* storing multiple vectors per document. This feature returns a tensor with one or more mapped dimensions and one point with a value of 1.0, where the label of that point indicates which document vector was closest to the query vector in the nearest neighbor search. + + Given a tensor field with type `tensor<float>(m{},x[3])` used with the *nearestNeighbor* operator, an example output of this feature is: + + ```bash + tensor<float>(m{}):{ 3: 1.0 } + ``` + In this example, the document vector with label *3* in the mapped *m* dimension was closest to the query vector. +- **closest(name,label)**<br/> + Default: {} + + Used with the [nearestNeighbor](/en/reference/querying/yql#nearestneighbor) query operator tagged with a [label](/en/ranking/multivalue-query-operators#raw-scores-and-query-item-labeling) *label* and a tensor field attribute *name* storing multiple vectors per document. + See [closest(name)](/en/reference/ranking/rank-features#closest(name)) for details. +- **distance(dimension,name)**<br/> + Default: max double value + Used with the [nearestNeighbor](/en/reference/querying/yql#nearestneighbor) query operator. A number which is close to 0 when a point vector in the document is close to a matching point vector in the query. The document vectors and the query vector must be the same tensor type, with one indexed dimension of size N, representing a point in an N-dimensional space. + - *dimension*: Specifies the dimension of *name*. This must be either the string `field` or the string `label`. + When using `field`, the name given must be a field with a tensor attribute of appropriate type. Often used when the document type has only one vector field, see [example](/en/querying/nearest-neighbor-search#minimal-example). + When using `label`, queries are assumed to contain a [nearestNeighbor](/en/reference/querying/yql#nearestneighbor) query item with a [label](/en/reference/querying/yql#label) that matches the given *name*. This is useful when having multiple vector fields, where `distance()` then maps to the nearestNeighbor operator with the field configured. [Example](/en/querying/nearest-neighbor-search-guide#using-label). + - *name*: The value of the field name or label. + + The output value depends on the [distance metric](/en/reference/schemas/schemas#distance-metric) used. The default is the Euclidean distance between the "n"-dimensional query point "d" and the point "d" in the document tensor field: + + $$ + d i s t a n c e = \sqrt{\sum_{i = 1}^{n} \left(\right. q_{i} - d_{i} \left.\right)^{2}} + $$ + + + When the tensor field stores multiple vectors per document, the minimum distance between the vectors of a document and the query vector is used in the calculation above. +- **age(name)**<br/> + Default: 10B + + The document age in seconds relative to the unit time value stored in the attribute having this name +### Features combining multiple fields and attributes +- **match**<br/> + Default: 0 + + A normalized average of the fieldMatch and attributeMatch scores of all the searched fields and attributes, where the contribution of each field and attribute is weighted by its *weight* setting. +- **match.totalWeight**<br/> + Default: 0 + + The sum of the weight settings of all the field and attributes searched by the query +- **match.weight.name**<br/> + Default: 100 + + The (schema) weight setting of a field or attribute + +### Rank scores + +- **bm25(field)**<br/> + Default: 0 + + Calculates the [Okapi BM25](https://en.wikipedia.org/wiki/Okapi_BM25) ranking function over the given [indexed string field](/en/reference/schemas/schemas#indexing-index). This feature is cheap to compute, about 3-4 times faster than nativeRank, while still providing a good rank score quality wise. This feature is a good candidate for usage in a first phase ranking function when ranking text documents. Note that the field must be enabled to be used with the bm25 feature; set the *enable-bm25* flag in the [index](/en/reference/schemas/schemas#index) section of the field definition. See the [BM25 Reference](/en/ranking/bm25) for more detailed information. +- **elementwise(bm25(field),dimension,cell\_type)**<br/> + Default: tensor(dimension{}):{} + Calculates the [Okapi BM25](https://en.wikipedia.org/wiki/Okapi_BM25) ranking function over each element in the given *multi-valued* [indexed string field](/en/reference/schemas/schemas#indexing-index) and creates a tensor with a single mapped dimension containing the bm25 score for each matching element. The element indexes (starting at 0) are used as dimension labels. This feature is more expensive than [bm25](#bm25), does not need the *enable-bm25* flag and can be tuned with [rank properties](/en/reference/ranking/rank-feature-configuration#elementwise-bm25). + + The *cell\_type* parameter can be omitted (the default value is `double`) except when setting rank properties. + + Example: If the `content` field is of type `array<string>` where query terms are found in elements 2 and 5 then the feature `elementwise(bm25(content),x,float)` as a summary feature might be shown as: + + ```bash + "elementwise(bm25(content),x,float)": { + "type": "tensor(x{})", + "cells": { + "2": 0.5112776, + "5": 0.1021805 + } + } + ``` +- **nativeRank**<br/> + Default: 0 + + A reasonably good rank score which is computed cheaply by Vespa. This value only is a good candidate first phase ranking function, and is the default used in the default rank profile. The value computed by this function may change between Vespa versions. See the [native rank reference](/en/reference/ranking/nativerank) for more information. +- **nativeRank(field,...)**<br/> + Default: 0 + + Same as *nativeRank*, but only the given set of fields are used in the calculation. +- **nativeFieldMatch**<br/> + Default: 0 + + Captures how well query terms match in index fields. Used by *nativeRank*. See the [native rank reference](/en/reference/ranking/nativerank) for more information. +- **nativeFieldMatch(field,...)**<br/> + Default: 0 + + Same as *nativeFieldMatch*, but only the given set of index fields are used in the calculation. +- **nativeProximity**<br/> + Default: 0 + + Captures how near matched query terms occur in index fields. Used by *nativeRank*. See the [native rank reference](/en/reference/ranking/nativerank) for more information. +- **nativeProximity(field,...)**<br/> + Default: 0 + + Same as *nativeProximity*, but only the given set of index fields are used in the calculation. +- **nativeAttributeMatch**<br/> + Default: 0 + + Captures how well query terms match in attribute fields. Used by *nativeRank*. See the [native rank reference](/en/reference/ranking/nativerank) for more information. +- **nativeAttributeMatch(field,...)**<br/> + Default: 0 + + Same as *nativeAttributeMatch*, but only the given set of attribute fields are used in the calculation. +- **nativeDotProduct(field)**<br/> + Default: 0 + + Calculates the sparse dot product between query term weights and match weights for the given field. Example: A weighted set string field X: + + ```bash + "X": { + "x": 10, + "y": 20, + "z": 30 + } + ``` + For the query (x!2 OR y!4), the nativeDotProduct(X) feature will have the value 100 (10\*2+20\*4) for that document. + <Info> + **Note:** `nativeDotProduct` and `nativeDotProduct(field)` is less optimal for computing the dot product - consider using [dotProduct(name,vector)](/en/reference/ranking/rank-features#dotProduct(name,vector)). + </Info> +- **nativeDotProduct**<br/> + Default: 0 + + Calculates the sparse dot product between query term weights and match weights as above, but for all term/field combinations. +- **firstPhase**<br/> + Default: 0 + + The value of the rank score calculated in the first phase (unavailable in first phase ranking expressions) +- **secondPhase**<br/> + Default: 0 + + The value of the rank score calculated in the second phase (unavailable in first phase and second phase ranking expressions) +- **firstPhaseRank**<br/> + Default: max double value + + The rank of the document after first phase within the content node when selecting which documents to rerank in second phase. The best document after first phase has rank 1, the second best 2, etc. The feature returns the default value for documents not selected for second phase ranking and for unsupported cases ([streaming search](/en/performance/streaming-search#differences-in-streaming-search), [summary features](/en/reference/schemas/schemas#summary-features), first phase expressions). Multiple documents can have the same *firstPhaseRank* value in multi-node configurations. +- **firstPhaseMax**<br/> + Default: -infinity + + The maximum first-phase rank score among all hits matched on the content node. Calculated locally on each content node, so may yield different results on different nodes. +- **relevanceScore**<br/> + Default: - + + The value of the rank score calculated either in the first or (when defined) in the second phase (unavailable in first phase and second phase ranking expressions) (since 8.559.30). +### Global features +- **globalSequence**<br/> + Default: n/a + + A global sequence number computed as (1 `<<` 48) - (LocalDocumentId `<<` 16 || [distribution-key](/en/reference/applications/services/content#node)). This will give a global sequence to documents. This is a cheap way of having stable ordering of documents. Note the large range of this value. Also note that if the system is not stable, e.g. if documents move around due to new nodes coming in, or nodes being removed, it will no longer be stable as documents might be found in a different replica. If you need true global ordering we suggest assigning a unique numeric id to your documents as an [attribute](/en/reference/schemas/schemas#attribute) field and use the [attribute(name)](/en/reference/ranking/rank-features#attribute(name)) feature. +- **now**<br/> + Default: n/a + + Time at which the query is executed in unix-time (seconds since epoch) +- **random**<br/> + Default: n/a + + A pseudorandom number in the range `[0,1>` which is drawn once per document during rank evaluation. By default, the current time in microseconds is used as a seed value. Users can specify a seed value by setting [random.seed](/en/reference/ranking/rank-feature-configuration#random) in the rank profile. If you need several independent random numbers the feature can be named like this: `random(foo)`, `random(bar)`. +- **random.match**<br/> + Default: n/a + + A pseudorandom number in the range `[0,1>` that is stable for a given hit. This means that a hit will always receive the same random score (on a single node). If it is required that the scores be different between different queries, specify a seed value dependent upon the query. By default, the seed value is 1024. Users can specify a seed value by adding the query parameter [rankproperty.random.match.seed=`<value>`](/en/reference/api/query#ranking.properties). If you need several independent random numbers the feature can be named like this: `random(foo).match`, `random(bar).match`. +- **randomNormal(mean,stddev)**<br/> + Default: 0.0,1.0 + + Same as [random](/en/reference/ranking/rank-features#random), except the random number is drawn from the Gaussian distribution using the supplied mean and stddev parameters. Can be called without parameters; default values are assumed. Seed is set similarly as *random*. If you need several independent random numbers with the same parameters, the feature can be named like this: `randomNormal(0.0,1.0,foo)`, `randomNormal(0.0,1.0,bar)`. If the parameters to *randomNormal* are not the same, you do not need to supply an additional name, e.g. `randomNormal(0.0, 0.1)` and `randomNormal(0.0, 0.5)` results in two independent values. +- **randomNormalStable(mean,stddev)**<br/> + Default: 0.0,1.0 + + Same as [randomNormal](/en/reference/ranking/rank-features#randomNormal(mean,stddev)), except that the generated number is stable for a given hit, similar to [random.match](/en/reference/ranking/rank-features#random.match). +- **constant(name)**<br/> + Default: n/a + + Returns the [constant](/en/reference/schemas/schemas#constant) tensor value. +### Match operator scores + +See [Raw scores and query item labeling](/en/ranking/multivalue-query-operators#raw-scores-and-query-item-labeling) + +- **rawScore(field)**<br/> + Default: 0 + + The sum of all raw scores produced by match operators for this field. +- **itemRawScore(label)**<br/> + Default: 0 + + The raw score produced by the query item with the given label. + +### Geo search +These features are for ranking on the distances between geographical coordinates, i.e. points on the surface of the earth defined by latitude/longitude pairs. See the main documentation for [Geo Search](/en/querying/geo-search). + +<Info> +**Note:** Some of these features have the same names as features used with the [nearestNeighbor](/en/reference/querying/yql#nearestneighbor) query operator. Take care not to get them mixed up! +</Info> + +- **closeness(name)**<br/> + Default: 0 + + A number which is close to 1 if the position in attribute *name* is close to the query position compared to [maxDistance](/en/reference/ranking/rank-feature-configuration#closeness): + + `max(1-distance(name)/maxDistance , 0)` + + Scales linearly with distance, see [closeness plot](/en/reference/ranking/rank-features#closeness). +- **closeness(name).logscale**<br/> + Default: 0 + + A logarithmic-shaped closeness; like normal closeness it goes from 1 to 0, but looks like [closeness plot](/en/reference/ranking/rank-features#closeness). The function is a logarithmic fall-off based on `log(distance + scale)` and is calculated as: + + $$ + c l o s e n e s s \left(\right. n a m e \left.\right) . l o g s c a l e = \frac{l o g \left(\right. m a x D i s t a n c e + s c a l e \left.\right) - l o g \left(\right. d i s t a n c e \left(\right. n a m e \left.\right) + s c a l e \left.\right) \left.\right)}{\left(\right. l o g \left(\right. m a x D i s t a n c e + s c a l e \left.\right) - l o g \left(\right. s c a l e \left.\right) \left.\right)} + $$ + + where scale is defined using [halfResponse and maxDistance](/en/reference/ranking/rank-feature-configuration#closeness): + + $$ + s c a l e = \frac{h a l f R e s p o n s e^{2}}{\left(\right. m a x D i s t a n c e - 2 \times h a l f R e s p o n s e \left.\right)} + $$ + + When `distance(name) == halfResponse` the function output is 0.5; halfResponse should be less than `maxDistance/2` since that means adding a certain distance when you are close matters more than adding the same distance when you're already far away. +- **distance(name)**<br/> + Default: 6400M + + The Euclidean distance from the query position to the given position attribute in millionths of degrees (about 10 cm). If there are multiple positions in the query, items that actually search in *name* is preferred. Also: if multiple query items search in *name*, or *name* is an array of positions, or both, the closest distance found is returned. +- **distance(name).km**<br/> + Default: 711648.5 + + As above, but scaled, so it uses the kilometer as unit of distance, instead of "micro-degrees". +- **distance(name).index**<br/> + Default: -1 + + The array index of the closest position found. Useful when *name* is of `array<position>` type. +- **distance(name).latitude**<br/> + Default: 90 + + The latitude (geographical north-south coordinate) of the closest position found. In range from -90.0 (South Pole) to +90.0 (North Pole). Useful when *name* is of `array<position>` type. +- **distance(name).longitude**<br/> + Default: -180 + + The latitude (geographical east-west coordinate) of the closest position found. In range from -180.0 (extreme west) to +180.0 (extreme east). Useful when *name* is of `array<position>` type. +- **distanceToPath(name).distance**<br/> + Default: 6400M + + The Euclidean distance from a path through 2d space given in the query to the given position attribute in millionths of degrees. This is useful e.g. for finding the closest locations to a given road. The query path is set in the [rankproperty.distanceToPath(*name*).path](/en/reference/api/query#ranking.properties) query parameter, using syntax `"(x1,y1,x2,y2,..)"` also in millionth of degrees, see the [distance to path](/en/querying/geo-search#distance-to-path) example. The closest point along the path is referred to as the *intersection*. + + <Info> + **Note:** For documents with multiple locations, only the closest location is used for ranking purposes. + </Info> +- **distanceToPath(name).traveled**<br/> + Default: 1 + + The normalized distance along the query path traveled before intersection (0.0 indicates start of path, 0.5 is middle, and 1.0 is end of path). +- **distanceToPath(name).product**<br/> + Default: 0 + + The cross-product of the intersected path segment and the intersection-to-document vector. Given that the document was found to lie closest to the path element `A->B`, the intersected path segment vector is `[ B.x - A.x, B.y - A.y ]`. Furthermore, given that the intersection of that path element occurred at point `I` for document location `D`, the intersection-to-document vector is `[ I.x - D.x, I.y - D.y]`. This is useful e.g. for finding what side of a path a document exists by looking at the sign of this value. +### Utility features +- **foreach(dimension, variable, feature, condition, operation)**<br/> + Default: n/a + + *foreach* iterates over a set of feature output values and performs an operation on them. Only the values where the condition evaluates to true are considered for the operation. The result of this operation is returned. + + - *dimension*: Specifies what to iterate over. This can be: + - *terms*: All query term indices, from 0 and up to [maxTerms](/en/reference/ranking/rank-feature-configuration#foreach). + - *fields*: All index field names. + - *attributes*: All attribute field names. + - *variable*: The name of the variable 'storing' each of the items you are iterating over. + - *feature*: The name of the feature you want to use the output value from. Use the *variable* as part of the feature name, and for each item you iterate over this *variable* is replaced with the actual item. Note that the variable replacement is a simple string replace, so you should use a variable name that is not in conflict with the feature name. + - *condition*: The condition used on each feature output value to find out if the value should be considered by the operation. The condition can be: + - *\>a*: Use feature output if greater than number a. + - *`<a`*: Use feature output if less than number a. + - *true*: Use all feature output values. + - *operation*: The operation you want to perform on the feature output values. This can be: + - *sum*: Calculate the sum of the values. + - *product*: Calculate the product of the values. + - *average*: Calculate the average of the values. + - *max*: Find the max of the values. + - *min*: Find the min of the values. + - *count*: Count the number of values. + + Let's say you want to calculate the average score of the *fieldMatch* feature for all index fields, but only consider the scores larger than 0. Then you can use the following setup of the *foreach* feature: + + `foreach(fields,N,fieldMatch(N), ">0", average)`. + + Note that when using the conditions *`>a`* and *`<a`* the arguments must be quoted. + + You can also specify a ranking expression in the *foreach* feature by using the *rankingExpression* feature. The *rankingExpression* feature takes the expression as the first and only parameter and outputs the result of evaluating this expression. Let's say you want to calculate the average score of the squared *fieldMatch* feature score for all index fields. Then you can use the following setup of the *foreach* feature: + `foreach(fields, N, rankingExpression("fieldMatch(N)*fieldMatch(N)"), true, average)` + + Note that you must quote the expression passed in to the *rankingExpression* feature. + +- **dotProduct(name,vector)**<br/> + Default: 0 + + <Info> + **Note:** Most dot product use cases are better solved using [tensors](/en/ranking/tensor-user-guide). + </Info> + The sparse dot product of the vector represented by the given weighted set attribute and the vector sent down with the query. + You can also do an ordinary full dotproduct by using arrays instead of weighted sets. This will be a lot faster when you have full vectors in the document with more than 5-10% non-zero values. You are also then not limited to integer weights. All the numeric datatypes can be used with arrays, so you have full floating point support. The 32 bit floating point type yields the fastest execution. + - *name*: The name of the weighted set string/integer or array of numeric attribute. + - *vector*: The name of the vector sent down with the query. + +Each unique string/integer in the weighted set corresponds to a dimension and the belonging weight is the vector component for that dimension. The query vector is set in the [rankproperty.dotProduct.*vector*](/en/reference/api/query#ranking.properties) query parameter, using syntax `{d1:c1,d2:c2,…}` where *d1* and *d2* are dimensions matching the strings/integers in the weighted set and *c1* and *c2* are the vector components (floating point numbers). The number of dimensions in the weighted set and the query vector do not need to be the same. When calculating the dot product we only use the dimensions present in both the weighted set and the query vector. + +When using an array the dimensions is a positive integer starting at 0. If the query is sparse all non given dimensions are zero. That also goes for dimensions that outside of the array size in each document. + +Assume a weighted set string attribute X with: + + ```bash + "X": { + "x": 10, + "y": 20 + } + ``` + +for a particular document. The result of using the feature dotProduct(X,Y) with the query vector rankproperty.dotProduct.Y=`{x:2,y:4}` will then be 100 (10\*2+20\*4) for this document. + +Arrays can be passed down as `[w1 w2 w3 …]` or on sparse form `{d1:c1,d2:c2,…}` as is already supported for weighted sets. + + <Info> + **Note:** When the query vector ends up being the same as the query, it is better to annotate the query terms with weights (see [term weight](/en/reference/querying/simple-query-language#term-weight)) and use the nativeDotProduct feature instead. This will run more efficiently and improve the correlation between results produced by the WAND operator and the final relevance score. + </Info> + <Info> + **Note:** When using the dotProduct feature, [fast-search](/en/content/attributes#fast-search) is not needed, unless also used for searching. When using the dotProduct query operator, use fast-search. + </Info> + +- **tokenInputIds(length, input\_1, input\_2, ...)**<br/> + Default: n/a + + Convenience function for generating token sequence input to Transformer models. Creates a tensor with dimensions `d0[1], d1[length]`, where `d0` is the batch dimension and `d1` is the maximum length of the token sequence. Assumes the inputs are zero-padded tensors representing token sequences. The result is the token sequence: + + `CLS + input_1 + SEP + input_2 + SEP + ... + 0's` + - *length*: The maximum length of the token sequence + - *input\_N*: Where to retrieve input from. At least one is required. + + The inputs are typically retrieved from the query, document attributes or constants. For instance, `tokenInputIds(128, query(my_input), attribute(my_field))` where input types are: + + - `query(my_input): tensor(d0[32])` + - `attribute(my_field): tensor(d0[128])` + + will create a tensor of type `d0[1],d1[128]` consisting of the CLS token `101`, the tokens from the query, the SEP token `102`, the tokens from the document field, the SEP token `102`, and 0's for the rest of the tensor. + +- **customTokenInputIds(start\_sequence\_id, sep\_sequence\_idlength, input\_1, input\_2, ...)**<br/> + Default: n/a + + Convenience function for generating token sequence input to Transformer models. Creates a tensor with dimensions `d0[1], d1[length]`, where `d0` is the batch dimension and `d1` is the maximum length of the token sequence. Assumes the inputs are zero-padded tensors representing token sequences. The result is the token sequence: + `start_sequence_id + input_1 + sep_sequence_id + input_2 + sep_sequence_id + ... + 0's` + - *start\_sequence\_id*The start sequence id, typically *1* + - *sep\_sequence\_id*The separator sequence id, typically *2* + - *length*: The maximum length of the token sequence + - *input\_N*: Where to retrieve input from. At least one is required. + +The inputs are typically retrieved from the query, document attributes or constants. For instance, `customTokenInputIds(1,2,128, query(my_input), attribute(my_field))` where input types are: + - `query(my_input): tensor(d0[32])` + - `attribute(my_field): tensor(d0[128])` +- **tokenTypeIds(length, input\_1, input\_2, ...)**<br/> + Default: n/a + + Convenience function for generating token sequence input to Transformer models. Similar to the `tokenInputIds`, creates a tensor of type `d0[1],d1[length]` which represents a mask with zeros for the first input including CLS and SEP token, ones for the rest of the inputs (up to and including the final SEP token), and 0's for the rest of the tensor. +- **tokenAttentionMask(length, input\_1, input\_2, ...)**<br/> + Default: n/a + + Convenience function for generating token sequence input to Transformer models. Similar to the `tokenInputIds`, creates a tensor of type `d0[1],d1[length]` which represents a mask with ones for all tokens that are set (CLS and SEP and all inputs), and zeros for the rest. + +## Graphs for selected ranking functions + +### closeness + +<Frame> + ![Closeness logscale plot](/assets/img/relevance/closeness-logscale.png) +</Frame> + +The plot above shows the possible outputs from the closeness distance rank feature using the default maxDistance of 1000 km. The _linear(x)_ graph shows the default closeness output while the other graphs are logscale output for various values of the scaleDistance parameter: 9013.305 (1 km), 45066.525 (5 km - the default value), and 901330.5 (100 km). These values correspond to the following values of the halfResponse parameter: 276154.903 (30.64 km), 593861.739 (65.89 km), and 2088044.581 (231.66 km). + +### freshness + +<Frame> + ![Freshness logscale plot](/assets/img/relevance/freshness-logscale.png) +</Frame> + +The plot above shows the possible outputs from the freshness rank feature using the default maxAge of 7776000s (90 days). The _linear(x)_ graph shows the default freshness output while the other graphs are logscale output for various values of the halfResponse parameter: 172800s (2 days), 604800s (7 days - the default value), 1209600s (14 days). \ No newline at end of file diff --git a/en/reference/ranking/rank-types.html b/en/reference/ranking/rank-types.html deleted file mode 100644 index 3eb1f419dd..0000000000 --- a/en/reference/ranking/rank-types.html +++ /dev/null @@ -1,306 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Rank Types" -redirect_from: -- /en/reference/rank-types.html ---- - -<p> - This document presents the <a href="../schemas/schemas.html#rank-type">rank-types</a> that are supported by nativeRank - and the main ranking features that are controlled through the use of them. - See the <a href="../../ranking/nativerank.html">native rank reference</a> - for detailed information on the nativeRank feature. - First, the generic mechanisms for ranking control through rank types are explained. - Then, each ranking type will be described, with details of how it uses the different ranking controls. -</p> - - -<h2 id="using-rank-types">Using rank-type </h2> -<p> - The <a href="../schemas/schemas.html#rank-type">rank-types</a> for a field is either controlled by the - <a href="../schemas/schemas.html#rank-profile">rank-profile</a> or per field. - Using it per rank-profile gives more flexible control: -</p> -<pre> -rank-profile product inherits default { - rank-type title: identity - rank-type body: about - first-phase { - expression: nativeRank - } -} -</pre> - - - -<h2 id="ranking-controls">Ranking controls</h2> -<p> - This section describes the different generic ranking controls that are influenced by the ranking type selection. -</p> - - -<h3 id="firstocc-boost">FirstOcc boost</h3> -<p> - The position of the first occurrence of a term in a document field is called the <code>FirstOcc</code>. - A FirstOcc boost table map a particular value <em><strong>x</strong></em> of FirstOcc to a rank contribution. - For FirstOcc equal to or above the size of the table (default 256), - the value for the last table element is used. -</p> -<p> - Equation 1. Example FirstOcc boost table function: -</p> -<p class="equation-container"><!-- depends on mathjax --> - $$firstocc(x) = expdecay(x) = {w} * {e}^{-\frac{x}{t}}$$ -</p> -<p> - where <em><strong>w</strong></em> is the weight (controls the amplitude) - and <em><strong>t</strong></em> is the tune parameter (controls the slope). - The curve shapes are illustrated in Figure 1-2. -</p> -<figure> - <img src="/assets/img/relevance/plot-firstocc-weight.png" alt="FirstOcc Plot weight" /> - <figcaption> - Figure 1. FirstOcc Plot weight: increasing w lifts amplitude - (x-axis is first occurrence position, and y-axis is firstocc boost) - </figcaption> -</figure> -<br/> -<figure> - <img src="/assets/img/relevance/plot-firstocc-tune.png" alt="FirstOcc Plot tune" /> - <figcaption> - Figure 2. FirstOcc Plot tune: Increasing t reduces exponential falloff - (x-axis is first occurrence position, and y-axis is firstocc boost) - </figcaption> -</figure> - - -<h3 id="numocc-boost">NumOcc boost</h3> -<p> - The number of occurrences of a term in a document field is called the <code>NumOcc</code>. - A NumOcc boost table map a given number of occurrences <em><strong>x</strong></em> - to a particular rank contribution. - For terms occurring more than or equal to the size of the table (default 256), - the value for the last table element is used. -</p> -<p> - Equation 2. Example NumOcc boost table function: -</p> -<p class="equation-container"><!-- depends on mathjax --> - $$numocc(x) = loggrowth(x) = {w} * log(1+\frac{x}{s}) + {t}$$ -</p> -<p> - where <em><strong>w</strong></em> is the weight (controls the amplitude) - and <em><strong>t</strong></em> is the tune parameter (controls the offset). - <em><strong>s</strong></em> is a scale parameter (controls the sensitivity to the numocc variable x). -</p> -<figure> - <img src="/assets/img/relevance/plot-numocc-weight.png" alt="NumOcc Plot weight" /> - <figcaption> - Figure 3. NumOcc Plot weight: increasing w scales log amplitude/shape - (x-axis is number of occurrences in the field and y-axis is numocc boost) - </figcaption> -</figure> -<br/> -<figure> - <img src="/assets/img/relevance/plot-numocc-tune.png" alt="NumOcc Plot tune" /> - <figcaption> - Figure 4. NumOcc Plot tune: increasing t increases boost by offset value - (x-axis is number of occurrences in the field and y-axis is numocc boost) - </figcaption> -</figure> - - -<h3 id="proximity-boost">Proximity boost</h3> -<p> - When a query with more than one query term is executed, - pairwise proximity is calculated between pairs of query terms. - For a given pair, the actual ranking value will be based on lookup in a proximity boost table. -</p><p> - For a given pair of query-terms <em><code>a</code></em> <em><code>b</code></em>, - the distance in the document field between the positions of the terms is calculated - such that if the terms occur in the same order in both the query and the document, - the distance will be positive. - If the order is reversed from the query to the document, the value will be negative. - The absolute value is in both cases the absolute difference in word positions in the document. -</p><p> - For proximity calculations in the forward direction, - the rank boost will be selected from the forward proximity table. - For reversed proximity, a similar reverse proximity table is used. -</p><p> - Typically, the rank score of the forward proximity table will be higher than the reverse proximity table, - giving an overall asymmetrical ranking function. -</p> -<p> - Equation 3. Example Proximity boost table function: -</p> -<p class="equation-container"><!-- depends on mathjax --> - $$prox(x) = expdecay(x) = {w} * {e}^{-\frac{x}{t}}$$ -</p> -<p> - where <em><strong>x</strong></em> is the absolute difference between - the query term distance and document term distance, - <em><strong>w</strong></em> is the weight (controls the amplitude) and - <em><strong>t</strong></em> is the tune parameter (controls the slope). - Refer to Figure 5 and 6: The same formula as in the FirstOcc examples are used, - but with different parameters. -</p> -<figure> - <img src="/assets/img/relevance/plot-proximity-weight.png" alt="Proximity Plot weight" /> - <figcaption> - Figure 5. Proximity Plot weight: w scales amplitude - (x-axis is proximity term pair distance and y-axis is proximity boost) - </figcaption> -</figure> -<br/> -<figure> - <img src="/assets/img/relevance/plot-proximity-tune.png" alt="Proximity Plot tune" /> - <figcaption> - Figure 6. Proximity Plot tune: Increasing t reduces curve fall-off - (x-axis is proximity term pair distance and y-axis is proximity boost) - </figcaption> -</figure> - - -<h3 id="weight_boost">Weight boost</h3> -<p> - The weightboost table is for calculating the rank boost contribution from attributes. - Equation 4 shows how attribute weight values are used for table look-ups in the weightboost table. -</p> -<p> - Equation 4. Example Weightboost boost table: -</p> -<p class="equation-container"><!-- depends on mathjax --> - $$weight(x) = sign(x) * weightboost[abs(x)]$$ -</p> -<p> - where <em><strong>sign(x)</strong></em> is the sign of <em><strong>x</strong></em>, - and <em><strong>abs(x)</strong></em> is the absolute value of <em><strong>x</strong></em>. - Hence, attributes can have negative rank contributions. - The argument <em><strong>x</strong></em> used as input in this boost table is dependent on the attribute type: -</p> -<ul> - <li><strong>Weighted set:</strong> <em><strong>x</strong></em> equals the attribute weight.</li> - <li><strong>Array:</strong> <em><strong>x</strong></em> - equals to the number of match occurrences in the attribute array.</li> - <li><strong>Single value:</strong> <em><strong>x</strong></em> equals 1.</li> -</ul> - - - -<h2 id="rank-types">Rank types</h2> -<p> - This section describes each individual ranking type, - and details how it uses the different generic ranking controls. -</p> - - -<h3 id="identity-rank-type">"identity" rank type</h3> -<p> - This ranking type is for fields that contain the identity of the document. - A title of a book, or product name of a product are examples of this. -</p><p> - The title will usually appear early in a document. - So the FirstOcc table starts high and descends sharply. -</p><p> - The title is usually not repeated, so the number of occurrences is not that important. - It starts out at a medium height, and ascends slowly. -</p><p> - Identity terms are well-connected, and fields are fairly small. - So the proximity table has a sharp and high peak, with the importance falling rapidly. -</p><p> - Supported by nativeRank using these tables: -</p> -<ul> - <li>nativeFieldMatch.firstOccurrenceTable: "expdecay(100,12.50)"</li> - <li>nativeFieldMatch.occurrenceCountTable: "loggrowth(1500,4000,19)"</li> - <li>nativeProximity.proximityTable: "expdecay(5000,3)"</li> - <li>nativeProximity.reverseProximityTable: "expdecay(3000,3)"</li> - <li>nativeAttributeMatch.weightTable: "linear(1,0)"</li> -</ul> - - -<h3 id="about-rank-type">"about" rank type</h3> -<p> - This is for fields that contain information directly relating to the document, describing what it is. - A description field of a catalog, or a feature list of a product specification are typical examples. -</p><p> - The order of occurrences is somewhat important, - as usually the most important features will be put up first. - The FirstOcc boost table starts out at a medium height, and falls fairly slowly. -</p><p> - The about-ness of the description is much related to how many times a descriptive term is used. - So the NumOcc boost table will start out fairly low, but climb high, - with the maximum gain (curvature point) around 5 occurrences. -</p><p> - Proximity is fairly important for about-ness. - The proximity boost will be somewhat similar to the <code>identity</code> type, - although it will be somewhat lower at the peak, and have a slightly shallower falloff. -</p><p> - <code>about</code> is <strong>the default ranking type</strong> - used when it is not explicitly specified which type to use. -</p><p> - Supported by nativeRank using these tables: -</p> -<ul> - <li>nativeFieldMatch.firstOccurrenceTable: "expdecay(8000,12.50)"</li> - <li>nativeFieldMatch.occurrenceCountTable: "loggrowth(1500,4000,19)"</li> - <li>nativeProximity.proximityTable: "expdecay(500,3)"</li> - <li>nativeProximity.reverseProximityTable: "expdecay(400,3)"</li> - <li>nativeAttributeMatch.weightTable: "linear(1,0)"</li> -</ul> - - -<!-- Shows the default tables used for classicRank -<p> -Figure 7 shows these curves together, -where the firstocc and numocc curves have been divided by the mean divtable value -to get a feeling of their average contribution -</p> - -<h4>Figure 7: Zoomed boost table plot of rank type "about" (firstocc -and numocc divided by mean divtable value)</h4> -<p><img src="/assets/img/relevance/ranktype-about.png" alt="Rank type -About Boost tables" /></p> ---> - -<!-- Tags begin --> -<h3 id="tags_rank_type">"tags" rank type</h3> -<p> - This is for search in attributes when they are used as tags, - for example name tags for images that may be updated frequently. - The tags rank type uses a logarithmic table to give more relative boost in the low range: - As tags are added they should have significant impact on rank score, - but as more and more tags are added, each new tag should contribute less. -</p><p> - The <code>tags</code> rank type is based on the <code>about</code> rank type, - i.e. only the weight boost table is different. - For the other rank types a 1-to-1 linear table is used, except for the empty rank type, - which has a table with zeros. -</p><p> - Supported by nativeRank using this table: -</p> -<ul> - <li>nativeAttributeMatch.weightTable: "loggrowth(38,50,1)"</li> -</ul> -<p> - The other tables are the same as the <code>about</code> rank type. -</p> - -<!-- Shows the default tables used for classicRank -<h4>Figure 8: Zoomed boost table plot of rank type "tags" (firstocc -and numocc divided by mean divtable value)</h4> -<p><img src="/assets/img/relevance/ranktype-tags.png" alt="Rank type Tags -boost tables" /></p> ---> - -<!-- Tags end --> - - -<h3 id="empty_rank_type">"empty" rank type</h3> -<p> - This is used for fields where you do not want matches to have - any impact on relevancy. Use this for instance when the field - contains keywords used to partition the dataset. -</p><p> - The empty rank type is also supported by nativeRank. -</p> diff --git a/en/reference/ranking/rank-types.mdx b/en/reference/ranking/rank-types.mdx new file mode 100644 index 0000000000..5b93f0c35e --- /dev/null +++ b/en/reference/ranking/rank-types.mdx @@ -0,0 +1,164 @@ +--- +title: "Rank Types" +--- + +This document presents the [rank-types](/en/reference/schemas/schemas#rank-type) that are supported by nativeRank and the main ranking features that are controlled through the use of them. See the [native rank reference](/en/reference/ranking/nativerank) for detailed information on the nativeRank feature. First, the generic mechanisms for ranking control through rank types are explained. Then, each ranking type will be described, with details of how it uses the different ranking controls. + +## Using rank-type + +The [rank-types](/en/reference/schemas/schemas#rank-type) for a field is either controlled by the [rank-profile](/en/reference/schemas/schemas#rank-profile) or per field. Using it per rank-profile gives more flexible control: + +```js +rank-profile product inherits default { + rank-type title: identity + rank-type body: about + first-phase { + expression: nativeRank + } +} +``` + +## Ranking controls + +This section describes the different generic ranking controls that are influenced by the ranking type selection. + +### FirstOcc boost + +The position of the first occurrence of a term in a document field is called the `FirstOcc`. A FirstOcc boost table map a particular value _**x**_ of FirstOcc to a rank contribution. For FirstOcc equal to or above the size of the table (default 256), the value for the last table element is used. + +Equation 1. Example FirstOcc boost table function: + +$$ +firstocc(x) = expdecay(x) = w \times e^{-\frac{x}{t}} +$$ + +where _**w**_ is the weight (controls the amplitude) and _**t**_ is the tune parameter (controls the slope). The curve shapes are illustrated in Figure 1-2. + +<Frame caption="Figure 1. FirstOcc Plot weight: increasing w lifts amplitude (x-axis is first occurrence position, and y-axis is firstocc boost)"> +![FirstOcc Plot weight](/assets/img/relevance/plot-firstocc-weight.png) +</Frame> +<br/> +<Frame caption="Figure 2. FirstOcc Plot tune: Increasing t reduces exponential falloff (x-axis is first occurrence position, and y-axis is firstocc boost)"> +![FirstOcc Plot tune](/assets/img/relevance/plot-firstocc-tune.png) +</Frame> +### NumOcc boost + +The number of occurrences of a term in a document field is called the `NumOcc`. A NumOcc boost table map a given number of occurrences _**x**_ to a particular rank contribution. For terms occurring more than or equal to the size of the table (default 256), the value for the last table element is used. + +Equation 2. Example NumOcc boost table function: + +$$ +numocc(x) = loggrowth(x) = w \times log(1+\frac{x}{s}) + t +$$ + +where _**w**_ is the weight (controls the amplitude) and _**t**_ is the tune parameter (controls the offset). _**s**_ is a scale parameter (controls the sensitivity to the numocc variable x). + +<Frame caption="Figure 3. NumOcc Plot weight: increasing w scales log amplitude/shape (x-axis is number of occurrences in the field and y-axis is numocc boost)"> +![NumOcc Plot weight](/assets/img/relevance/plot-numocc-weight.png) +</Frame> +<br/> +<Frame caption="Figure 4. NumOcc Plot tune: increasing t increases boost by offset value (x-axis is number of occurrences in the field and y-axis is numocc boost)"> +![NumOcc Plot tune](/assets/img/relevance/plot-numocc-tune.png) +</Frame> + +### Proximity boost + +When a query with more than one query term is executed, pairwise proximity is calculated between pairs of query terms. For a given pair, the actual ranking value will be based on lookup in a proximity boost table. + +For a given pair of query-terms _`a`_ _`b`_, the distance in the document field between the positions of the terms is calculated such that if the terms occur in the same order in both the query and the document, the distance will be positive. If the order is reversed from the query to the document, the value will be negative. The absolute value is in both cases the absolute difference in word positions in the document. + +For proximity calculations in the forward direction, the rank boost will be selected from the forward proximity table. For reversed proximity, a similar reverse proximity table is used. + +Typically, the rank score of the forward proximity table will be higher than the reverse proximity table, giving an overall asymmetrical ranking function. + +Equation 3. Example Proximity boost table function: + +$$ +prox(x) = expdecay(x) = w \times e^{-\frac{x}{t}} +$$ + +where _**x**_ is the absolute difference between the query term distance and document term distance, _**w**_ is the weight (controls the amplitude) and _**t**_ is the tune parameter (controls the slope). Refer to Figure 5 and 6: The same formula as in the FirstOcc examples are used, but with different parameters. + +<Frame caption="Figure 5. Proximity Plot weight: w scales amplitude (x-axis is proximity term pair distance and y-axis is proximity boost)"> +![Proximity Plot weight](/assets/img/relevance/plot-proximity-weight.png) +</Frame> +<br/> +<Frame caption="Figure 6. Proximity Plot tune: Increasing t reduces curve fall-off (x-axis is proximity term pair distance and y-axis is proximity boost)"> +![Proximity Plot tune](/assets/img/relevance/plot-proximity-tune.png) +</Frame> + +### Weight boost + +The weightboost table is for calculating the rank boost contribution from attributes. Equation 4 shows how attribute weight values are used for table look-ups in the weightboost table. + +Equation 4. Example Weightboost boost table: + +$$ +weight(x) = sign(x) \times weightboost[abs(x)] +$$ + +where \(\text{sign}(x)\) is the sign of \(x\), and \(\text{abs}(x)\) is the absolute value of \(x\). Hence, attributes can have negative rank contributions. The argument \(x\) used as input in this boost table is dependent on the attribute type: + +- **Weighted set:** \(x\) equals the attribute weight. +- **Array:** \(x\) equals to the number of match occurrences in the attribute array. +- **Single value:** \(x\) equals 1. + +## Rank types + +This section describes each individual ranking type, and details how it uses the different generic ranking controls. + +### "identity" rank type + +This ranking type is for fields that contain the identity of the document. A title of a book, or product name of a product are examples of this. + +The title will usually appear early in a document. So the FirstOcc table starts high and descends sharply. + +The title is usually not repeated, so the number of occurrences is not that important. It starts out at a medium height, and ascends slowly. + +Identity terms are well-connected, and fields are fairly small. So the proximity table has a sharp and high peak, with the importance falling rapidly. + +Supported by nativeRank using these tables: + +- nativeFieldMatch.firstOccurrenceTable: "expdecay(100,12.50)" +- nativeFieldMatch.occurrenceCountTable: "loggrowth(1500,4000,19)" +- nativeProximity.proximityTable: "expdecay(5000,3)" +- nativeProximity.reverseProximityTable: "expdecay(3000,3)" +- nativeAttributeMatch.weightTable: "linear(1,0)" + +### "about" rank type + +This is for fields that contain information directly relating to the document, describing what it is. A description field of a catalog, or a feature list of a product specification are typical examples. + +The order of occurrences is somewhat important, as usually the most important features will be put up first. The FirstOcc boost table starts out at a medium height, and falls fairly slowly. + +The about-ness of the description is much related to how many times a descriptive term is used. So the NumOcc boost table will start out fairly low, but climb high, with the maximum gain (curvature point) around 5 occurrences. + +Proximity is fairly important for about-ness. The proximity boost will be somewhat similar to the `identity` type, although it will be somewhat lower at the peak, and have a slightly shallower falloff. + +`about` is **the default ranking type** used when it is not explicitly specified which type to use. + +Supported by nativeRank using these tables: + +- nativeFieldMatch.firstOccurrenceTable: "expdecay(8000,12.50)" +- nativeFieldMatch.occurrenceCountTable: "loggrowth(1500,4000,19)" +- nativeProximity.proximityTable: "expdecay(500,3)" +- nativeProximity.reverseProximityTable: "expdecay(400,3)" +- nativeAttributeMatch.weightTable: "linear(1,0)" + +### "tags" rank type + +This is for search in attributes when they are used as tags, for example name tags for images that may be updated frequently. The tags rank type uses a logarithmic table to give more relative boost in the low range: As tags are added they should have significant impact on rank score, but as more and more tags are added, each new tag should contribute less. + +The `tags` rank type is based on the `about` rank type, i.e. only the weight boost table is different. For the other rank types a 1-to-1 linear table is used, except for the empty rank type, which has a table with zeros. + +Supported by nativeRank using this table: + +- nativeAttributeMatch.weightTable: "loggrowth(38,50,1)" + +The other tables are the same as the `about` rank type. + +### "empty" rank type + +This is used for fields where you do not want matches to have any impact on relevancy. Use this for instance when the field contains keywords used to partition the dataset. + +The empty rank type is also supported by nativeRank. diff --git a/en/reference/ranking/ranking-expressions.html b/en/reference/ranking/ranking-expressions.html deleted file mode 100644 index a7f71ea2ec..0000000000 --- a/en/reference/ranking/ranking-expressions.html +++ /dev/null @@ -1,883 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Ranking Expressions" -redirect_from: -- /en/reference/ranking-expressions.html ---- - -<p> - This is a complete reference to the <em>ranking expressions</em> - used to configure application specific ranking functions. - For examples and an overview of how to use ranking expressions, - see the <a href="../../basics/ranking.html">ranking overview</a>. -</p> -<p> - Ranking expressions are written in a simple language similar to ordinary functional notation. - The atoms in ranking expressions are <em>rank features</em> and <em>constants</em>. - These atoms can be combined by <em>arithmetic operations</em> and other - <em>built-in functions</em> over scalars and tensor. -</p> -<table class="table"> -<thead> -</thead><tbody> -<tr><th style="white-space:nowrap;">Rank Features</th> -<td> - <p id="rank-features"> - A rank feature is a named value calculated or looked up by vespa for - each query/document combination. See the - <a href="rank-features.html">rank feature reference</a> - for a list of all the rank features available to ranking expressions. - </p> -</td> -</tr> -<tr><th>Constants</th> - <td> - <p id="constants"> - A constant is either a floating point number, a boolean (true/false) or a quoted string. - Since ranking expressions can only work on scalars and tensors, strings and booleans are - immediately converted to scalars - true becomes 1.0, false 0.0 and a string its hash value. - This means that <b>strings can only be used for equality comparisons</b>, other purposes - such as parametrizing the key to slice out of a tensor will not work correctly. - </p> - </td> -</tr> -</tbody> -</table> - - - -<h2 id="arithmetic-operations">Arithmetic operations</h2> - -<p>Basic mathematical operations are expressed in in-fix notation:</p> - -<pre> -a + b * c -</pre> - -<p>Arithmetic operations work on any tensor in addition to scalars, and are a short form -of joining the tensors with the arithmetic operation used to join the cells. -For example <code>tensorA * tensorB</code> is the same as <code>join(tensorA, tensorB, f(a,b)(a * b))</code>.</p> - -<p>All arithmetic operators in order of decreasing precedence:</p> - -<table class="table"> -<thead> -<tr> -<th>Arithmetic operator</th> -<th>Description</th> -</tr> -</thead> -<tbody> -<tr><td>^</td> <td>Power</td></tr> -<tr><td>%</td> <td>Modulo</td></tr> -<tr><td>/</td> <td>Division</td></tr> -<tr><td>*</td> <td>Multiplication</td></tr> -<tr><td>-</td> <td>Subtraction</td></tr> -<tr><td>+</td> <td>Addition</td></tr> -<tr><td>&&</td> <td>And: 1 if both arguments are non-zero, 0 otherwise.</td></tr> -<tr><td>||</td> <td>Or: 1 if either argument is non-zero, 0 otherwise.</td></tr> -</tbody> -</table> - - - -<h2 id="mathematical-scalar-functions">Mathematical scalar functions</h2> -<table class="table"> - <thead> - <tr> - <th>Function</th> - <th>Description</th> - </tr> - </thead> - <tbody> - <tr><th>acos(<em>x</em>)</th> <td><p id="acos-x">Inverse cosine of <em>x</em></p></td></tr> - <tr><th>asin(<em>x</em>)</th> <td><p id="asin-x">Inverse sine of <em>x</em></p></td></tr> - <tr><th>atan(<em>x</em>)</th> <td><p id="atan-x">Inverse tangent of <em>x</em></p></td></tr> - <tr><th>atan2(<em>y</em>, <em>x</em>)</th> - <td><p id="atan2-x">Inverse tangent of <em>y / x</em>, - using signs of both arguments to determine correct quadrant.</p></td> - </tr> - <tr><th>bit(<em>x</em>, <em>y</em>)</th> - <td><p id="bit-x-y">Returns value of bit <em>y</em> in value <em>x</em> (for int8 values)</p></td> - </tr> - <tr><th>ceil(<em>x</em>)</th> <td><p id="ceil-x">Lowest integral value not less than <em>x</em></p></td></tr> - <tr><th>cos(<em>x</em>)</th> <td><p id="cos-x">Cosine of <em>x</em></p></td></tr> - <tr><th>cosh(<em>x</em>)</th> <td><p id="cosh-x">Hyperbolic cosine of <em>x</em></p></td></tr> - <tr><th>elu(<em>x</em>)</th> <td><p id="elu-x">The Exponential Linear Unit activation function for value <em>x</em></p></td></tr> - <tr><th>erf(<em>x</em>)</th> <td><p id="erf-x">The Gauss error function for value <em>x</em></p></td></tr> - <tr><th>exp(<em>x</em>)</th> <td><p id="exp-x">Base-e exponential function.</p></td></tr> - <tr><th>fabs(<em>x</em>)</th> <td><p id="fabs-x">Absolute value of (floating-point) number <em>x</em></p></td></tr> - <tr><th>floor(<em>x</em>)</th> <td><p id="floor-x">Largest integral value not greater than <em>x</em></p></td></tr> - <tr><th>fmod(<em>x</em>, <em>y</em>)</th> <td><p id="fmod-x">Remainder of <em>x / y</em></p></td></tr> - <tr><th>isNan(<em>x</em>)</th> <td><p id="isnan-x">Returns 1.0 if <em>x</em> is NaN, 0.0 otherwise</p></td></tr> - <tr><th>ldexp(<em>x</em>, <em>exp</em>)</th> <td><p id="ldexp-x">Multiply <em>x</em> by 2 to the power of <em>exp</em></p></td></tr> - <tr><th>log(<em>x</em>)</th> <td><p id="log-x">Base-e logarithm of <em>x</em></p></td></tr> - <tr><th>log10(<em>x</em>)</th> <td><p id="log10-x">Base-10 logarithm of <em>x</em></p></td></tr> - <tr><th>max(<em>x</em>, <em>y</em>)</th> <td><p id="max-x">Larger of <em>x</em> and <em>y</em></p></td></tr> - <tr><th>min(<em>x</em>, <em>y</em>)</th> <td><p id="min-x">Smaller of <em>x</em> and <em>y</em></p></td></tr> - <tr><th>pow(<em>x</em>, <em>y</em>)</th> <td><p id="pow-x">Return <em>x</em> raised to the power of <em>y</em></p></td></tr> - <tr><th>relu(<em>x</em>)</th> <td><p id="relu-x">The Rectified Linear Unit activation function for value <em>x</em></p></td></tr> - <tr><th>sigmoid(<em>x</em>)</th> <td><p id="sigmoid-x">The sigmoid (logistic) activation function for value <em>x</em></p></td></tr> - <tr><th>sin(<em>x</em>)</th> <td><p id="sin-xx">Sine of <em>x</em></p></td></tr> - <tr><th>sinh(<em>x</em>)</th> <td><p id="sinh-x">Hyperbolic sine of <em>x</em></p></td></tr> - <tr><th>sqrt(<em>x</em>)</th> <td><p id="sqrt-x">Square root of <em>x</em></p></td></tr> - <tr><th>tan(<em>x</em>)</th> <td><p id="tan-x">Tangent of <em>x</em></p></td></tr> - <tr><th>tanh(<em>x</em>)</th> <td><p id="tanh-x">Hyperbolic tangent of <em>x</em></p></td></tr> - <tr><th>hamming(<em>x</em>, <em>y</em>)</th> - <td><p id="hamming-x-y">Hamming (bit-wise) distance between <em>x</em> and <em>y</em> (considered as 8-bit integers).</p></td> - </tr> - </tbody> -</table> -<p><code>x</code> and <code>y</code> may be any ranking expression.</p> - - - -<h2 id="the-if-function">The if function</h2> -<p> -The <code>if</code> function chooses between two sub-expressions based on the truth value of a condition. -</p> -<pre> -if (expression1 <span class="pre-hilite">operator</span> expression2, trueExpression, falseExpression) -</pre> -<p> -If the condition given in the first argument is true, -the expression in argument 2 is used, otherwise argument 3. -The four expressions may be any ranking expression. -Conditional operators in ranking expression if functions: -</p> -<table class="table"> - <thead> - <tr> - <th>Boolean operator</th> - <th>Description</th> - </tr> - </thead> - <tbody> - <tr><td><=</td> <td>Less than or equal</td></tr> - <tr><td><</td> <td>Less than</td></tr> - <tr><td>==</td> <td>Equal</td></tr> - <tr><td>~=</td> <td>Approximately equal</td></tr> - <tr><td>>=</td> <td>Greater than or equal</td></tr> - <tr><td>></td> <td>Greater than</td></tr> - </tbody> -</table> -<p>The <code>in</code> membership operator uses a slightly modified if-syntax:</p> -<pre> -if (expression1 <span class="pre-hilite">in</span> [expression2, expression3, ..., expressionN], trueExpression, falseExpression) -</pre> -<p> -If expression1 is equal to either of expression2 through expressionN, -then trueExpression is used, otherwise falseExpression. -</p> - - - -<h2 id="the-switch-function">The switch function</h2> -{% include note.html content='Available from Vespa 8.626.55' %} -<p> -The <code>switch</code> function chooses between multiple sub-expressions based on matching a value. -It provides a more readable alternative to chained <code>if</code> statements when selecting from several options. -</p> -<pre> -switch (discriminant) { - case value1: result1, - case value2: result2, - ... - default: defaultResult -} -</pre> -<p> -The <code>discriminant</code> expression is compared for equality against each <code>case</code> value expression in order. -When a match is found, the corresponding result expression is evaluated and returned. -If no case matches, the <code>default</code> result is returned. -All expressions may be any ranking expression. -At least one <code>case</code> must be specified. -The <code>default</code> must be specified. -</p> - - - -<h2 id="the-foreach-function">The foreach function</h2> -<p> -The foreach function is not really part of the expression language but implemented as a -<a href="rank-features.html#foreach(dimension,variable,feature,condition,operation)">rank feature</a>. -</p> - - - -<h2 id="tensor-functions">Tensor functions</h2> -<p> -The following set of tensors functions are available to use in ranking expressions. -The functions are grouped in primitive functions and convenience -functions that can be implemented in terms of the primitive ones. - -</p> - - -<h3 id="primitive-functions">Primitive functions</h3> -<table class="table"> - <thead> - <tr> - <th>Function</th> - <th>Description</th> - </tr> - </thead> -<tbody> -<tr> - <th>map(<br/>  tensor,<br/>  f(x)(expr)<br/>)</th> - <td> - <p id="map"> - Returns a new tensor with the lambda function defined in <code>f(x)(expr)</code> applied to each cell. - </p><p> - Arguments: - </p> - <ul> - <li><code>tensor</code>: a tensor expression. For example <code>attribute(tensor_field)</code></li> - <li><code>f(x)(expr)</code>: a <a href="#lambda">lambda function</a> with one argument.</li> - </ul> - Returns a new tensor where the expression in the lambda function is - evaluated in each cell in <code>tensor</code>. Examples: -<pre> -map(t, f(x)(x*x)) -</pre> -<a href="https://docs.vespa.ai/playground/#N4KABGBEBmkFxgNrgmUrWQPYAd5QFNIAaFDSPBdDTAO30gEcBXAgJwE8AKAFwEoSZGpCIJIPArQDOWNgB5oAGywBDHgD4uAD0QAWALp84iAAzEAjMQBMxAMz7IQiAF8hz0hmrlcDIh+GUaE50DAC2KjgA+gRaKqE4in7BECJhEVws7Nz8xGDQ2nzaAHpWfALBrhiVYPogzkA"> - playground example map -</a> - </td> -</tr> -<tr> - <th>map_subspaces(<br/>  tensor,<br/>  f(x)(expr)<br/>)</th> - <td> - <p id="map-subspaces"> - Returns a new tensor with the lambda function defined in <code>f(x)(expr)</code> applied to each dense subspace. - </p><p> - Arguments: - </p> - <ul> - <li><code>tensor</code>: a tensor expression. For example <code>attribute(tensor_field)</code></li> - <li><code>f(x)(expr)</code>: a <a href="#lambda">lambda function</a> with one argument.</li> - </ul> - Returns a new tensor where the lambda function is evaluated for - each dense subspace in <code>tensor</code>. This is an advanced - feature that enables using dense <a href="#tensor">tensor - generator</a> expressions to transform mixed tensors. Example: -<pre> -map_subspaces(tensor(x{},y[3]):{a:[1,2,3]},f(d)(tensor(z[2])(d{y:(z)}+d{y:(z+1)}))) -</pre> - <a href="https://docs.vespa.ai/playground/#N4KABGBEBmkFxgNrgmUrWQPYAd5QFNIAaFDSPBdDTAO30gEcBXAgJwE8AKAFwEoSZGpCIJIPArQDOWNgB4AlrR4AOAHxcAHsAC+xDogBMAXT5xgAQziIAjAFZiAWhsA2YzshCIOoXqHVyXAYiUhooSjQvOgZmWhwLAGMAawB9ACMFHikUgHdMgAsUgFsLHBSpZjSpeISCKUEwiBEGErKKqpq6rhZ2bn5iaC4AEz5eSRl5JVUNA1dTLgyeYeAOOC4OAHoVPj0AdkcOAFJtvlPPMJ8MS7BjEB0gA"> - playground example for map_subspaces - </a> - </td> -</tr> -<tr> - <th>filter_subspaces(<br/>  tensor,<br/>  f(x)(expr)<br/>)</th> - <td> - <p id="filter-subspaces"> - Returns a new tensor containing only the subspaces for which the lambda function defined in <code>f(x)(expr)</code> returns true. - </p><p> - Arguments: - </p> - <ul> - <li><code>tensor</code>: a tensor expression. Must have at least one mapped dimension.</li> - <li><code>f(x)(expr)</code>: a <a href="#lambda">lambda function</a> with one argument.</li> - </ul> - Returns a new tensor containing only the subspaces for which the - lambda function defined in <code>f(x)(expr)</code> returns true. - Typically used to get rid of unneeded values in sparse - tensors. Example: -<pre> -filter_subspaces(tensor(x{}):{a:1,b:2,c:3,d:4},f(value)(value>2)) # tensor(x{}):{c:3,d:4} -</pre> - <a href="https://docs.vespa.ai/playground/#N4IgZiBcDaoPYAcogMYgDQiZUAXZABLgBYCmBAjgK4CGANgJa4CeBcYB9dBKxVAdgGsAziAC+Y9PGwhSGLFFD9kuAIzy5kELlL9hcAE4AeMHTg1cAPgAUvAYOBiAlDgAMkVQGZ0qyAHZ0ACZIAFZ0Tw8wgBZIT1d0EMhAsXFJaWQ0TGw8QgA5AgZhAgATZn4aAFsGNAkpEERkOSzFEGUtXI1kT1S6hq1MhRxtQjAGfmK2A2LSAzGAczYOFFI6OlFa9K0mwaUVQM7+lboAfUNpg2s1dAIKmgAPJx7N1Hls4a0bmkFyGk-hQQIYEMRDIBAARqRhLgCB0NvUZNs3m1tN1MJptIEjLC0vCMq8WvgPsUDIhOKsCMIqGDhAgaMsimASRUQeRbv8QRZOAZyGB6MI5HC+rJ8UNkbgogdwAw6DoDMdKdTafTLt5AdZhE51U5HoKZAM3oSQDw4BUwWMedLZaQJmyAQB3JjESYMOZjehEXT6AyA4Hcykyp64rYi3ZaXAhSXigBUalSAF0xEA"> - playground example for filter_subspaces - </a> - </td> -</tr> -<tr> - <th>reduce(<br/>  tensor,<br/>  aggregator,<br/>  dim1,<br/>  dim2,<br/>  ...<br/>)</th> - <td> - <p id="reduce"> - Returns a new tensor with the <code>aggregator</code> applied across dimensions dim1, dim2, etc. - If no dimensions are specified, reduce over all dimensions. - </p><p> - Arguments: - </p> - <ul> - <li><code>tensor</code>: a tensor expression.</li> - <li><code>aggregator</code>: the aggregator to use. See below.</li> - <li><code>dim1, dim2, ...</code>: the dimensions to reduce over. Optional.</li> - </ul> - <p> - Returns a new tensor with the aggregator applied across dimensions - <code>dim1</code>, <code>dim2</code>, etc. - If no dimensions are specified, reduce over all dimensions. - </p><p> - Available aggregators are: - </p> - <ul> - <li><code>avg</code>: arithmetic mean</li> - <li><code>count</code>: number of elements</li> - <li><code>max</code>: maximum value</li> - <li><code>median</code>: median value</li> - <li><code>min</code>: minimum value</li> - <li><code>prod</code>: product of all values</li> - <li><code>sum</code>: sum of all values</li> - </ul> - Examples: -<pre> -reduce(t, sum) # Sum all values in tensor -reduce(t, count, x) # Count number of cells along dimension x -</pre> -<a href="https://docs.vespa.ai/playground/#N4KABGBEBmkFxgNrgmUrWQPYAd5QFNIAaFDSPBdDTAO30gEcBXAgJwE8AKAFwEoSZGpCIJIPArQDOWNgB5oAGywBDHgD4uAD0QAWALp84iAAzEAjMQBMxAMz7IQiAF8hz0hmrlcDIh+GUaE50DGwEACbMAMYEAPpSzAC2sQRaKok4in7BECKhEdEEXCzs3PzEYAmJAsGuGHVg+iDOQA"> - playground example reduce -</a> - </td> -</tr> -<tr> - <th>join(<br/>  tensor1,<br/>  tensor2,<br/>  f(x,y)(expr)<br/>)</th> - <td> - <p id="join"> - Returns a new tensor constructed from the <em>natural join</em> between <code>tensor1</code> and <code>tensor2</code>, - with the resulting cells having the value as calculated from <code>f(x,y)(expr)</code>, where <code>x</code> - is the cell value from <code>tensor1</code> and <code>y</code> from <code>tensor2</code>. - </p><p> - Arguments: - </p> - <ul> - <li><code>tensor1</code>: a tensor expression.</li> - <li><code>tensor2</code>: a tensor expression.</li> - <li><code>f(x,y)(expr)</code>: a <a href="#lambda">lambda function</a> with two arguments.</li> - </ul> - <p> - Returns a new tensor constructed from the <em>natural join</em> - between <code>tensor1</code> and <code>tensor2</code>, - with the resulting cells having the value as calculated from <code>f(x,y)(expr)</code>, - where <code>x</code> is the cell value from <code>tensor1</code> and - <code>y</code> from <code>tensor2</code>. - </p><p> - Formally, the result of the <code>join</code> is a new tensor with dimensions - the union of dimension between <code>tensor1</code> and <code>tensor2</code>. - The cells are the set of all combinations of cells that have equal values - on their common dimensions. - </p><p> - Examples: - </p> -<pre>{% raw %} -join(t1, t2, f(x,y)(x * y)) -{% endraw %}</pre> -<a href="https://docs.vespa.ai/playground/#N4KABGBEBmkFxgNrgmUrWQPYAd5QFNIAaFDSPBdDTAO30gEcBXAgJwE8AKAFwEoSZGpCIJIPArQDOWNgB5oAGywBDHgD4uAD2ABfPnGDAtcAAy6EARgB0p4mhOWLYAEy3dkIRF1DdpDNTkuAxE-sKUaF50DGo8bACWAEbMErwCYTSEDBLSsgrKapo6fhx6BkYmdhxmzgDMtvbGZsTVTggALA0OcJYtNQgArF1Nva3OAGzunpk+GH5CgZjBYqFRFPiL5PRiAFZY8fQZwqJQewdcLOzc-PaxCcmpN2DQ2i182mAAVGAcfAJRs1QgIAuiBdEA"> - playground example join -</a> - </td> -</tr> -<tr> - <th>merge(<br/>  tensor1,<br/>  tensor2,<br/>  f(x,y)(expr)<br/>)</th> - <td> - <p id="merge"> - Returns a new tensor consisting of all cells from both the arguments, where the lambda function is used - to produce a single value in the cases where both arguments provide a value for a cell. - </p><p> - Arguments: - </p> - <ul> - <li><code>tensor1</code>: a tensor expression.</li> - <li><code>tensor2</code>: a tensor expression.</li> - <li><code>f(x,y)(expr)</code>: a <a href="#lambda">lambda function</a> with two arguments.</li> - </ul> - <p> - Returns a new tensor having all the cells of both arguments, - where the lambda is invoked to produce a single value only when both arguments have a value for the same cell. - </p><p> - The argument tensors must have the same type, and that will be the type of the resulting tensor. Example: - </p> -<pre>{% raw %} -merge(t1, t2, f(left,right)(right)) -{% endraw %}</pre> -<a href="https://docs.vespa.ai/playground/#N4KABGBEBmkFxgNrgmUrWQPYAd5QFNIAaFDSPBdDTAO30gEcBXAgJwE8AKAFwEoSZGpCIJIPArQDOWNlwDWBDsAC+xAB6IATAF0+cYAEM4iAIzFdxAEYmAzMQAsOlZCEQVQtUOrlcDIqQ0UJRobnQMhjw8bACWVswSvAKBQYQMEtKyCkqqGtp6BjaIAKzEAGw6xADGJgDsxAAczq5BHhheGD6YfmIBYRT4XeT0YgC27ADmBAD6BOqGozgANn2paWOTBFws7Nz8xGCR0XEJW-tg0Fwr0DzEsRMAFvxc9098AmFtqF86ICpAA"> - playground example merge -</a> - </td> -</tr> -<tr> - <th>tensor(<br/>  tensor-type-spec<br/>)(expr)</th> - <td> - <p id="tensor"> - Generates new tensors according to type specification and expression <code>expr</code>. - </p><p> - Arguments: - </p> - <ul> - <li><code>tensor-type-spec</code>: an <a href="tensor.html#tensor-type-spec">indexed tensor type specification.</a></li> - <li><code>(expression)</code>: a <a href="#lambda">lambda function</a> expressing how to generate the tensor.</li> - </ul> - <p> - Generates new tensors according to the type specification and expression <code>expr</code>. - The tensor type must be an indexed tensor (e.g. <code>tensor<float>(x[10])</code>). - The expression in <code>expr</code> will be evaluated for each cell. - The arguments in the expression is implicitly the names of the dimensions defined in the type spec. - </p><p> - Useful for creating transformation tensors. Examples: - </p> -<pre>{% raw %} -tensor<float>(x[3])(x) -{% endraw %}</pre> -<a href="https://docs.vespa.ai/playground/#N4KABGBEBmkFxgNrgmUrWQPYAd5QFNIAaFDSPBdDTAO30gHMDaCAnAQwBcCB9VgO4kyNSEQSQetAM5Y2ACgAeiAMwBdAJRKNkERAC+I-aQzVyuBkROjKaPXQbNWnHvwIDeAJmE1M4qFKybAA80AA2WNwAfEqInmrEAJ5xmkpgALzpYIk69oYYxiJmmBYSVvYU+MXk9BJcPr6EDIFysQAsmnCIAIwAdAAMxGCeA0Mqo2BtA2q6vvmohaYVpU3W5LbVDhJO7Nx8grzQbFgAtrwctFhcABbsvC1sDb5izSxB8tApWlzAinDQAGpuvpcnMjCg1CB9EA"> - playground generate examples -</a> - </td> -</tr> -<tr> - <th>rename(<br/>  tensor,<br/>  dim-to-rename,<br/>  new-names<br/>)</th> - <td> - <p id="rename"> - Renames one or more dimensions in the tensor. - </p><p> - Arguments: - </p> - <ul> - <li><code>tensor</code>: a tensor expression.</li> - <li><code>dim-to-rename</code>: a dimension, or list of dimensions, to rename.</li> - <li><code>new-names</code>: new names for the dimensions listed above.</li> - </ul> - Returns a new tensor with one or more dimension renamed. Examples: -<pre>{% raw %} -rename(t1,x,z) -{% endraw %}</pre> -<a href="https://docs.vespa.ai/playground/#N4KABGBEBmkFxgNrgmUrWQPYAd5QFNIAaFDSPBdDTAO30gBcSybIiEmDaBnLAJwAUAD2ABfYgE9xASjjBgwuAAYpKsQgCMAOlVolqyXE0awy3cX3G1y0+b2LrRk1t1jIrCGNYTW1crgMRKQ0UJRonnQM-NwAhgC2BAD6BMIJOAA2wZEQ7NFxiYKMxMLEAF4yHqHeGL4Y-piBnNmhFPgN5PScMbQJyanpWUmMAO5YLKG5HFA9fUXEIlIyCwCWxABWMpWRNai7ALogYkA"> - playground rename examples -</a> - </td> -</tr> -<tr> - <th>concat(<br/>  tensor1,<br/>  tensor2,<br/>  dim<br/>)</th> - <td> - <p id="concat"> - Concatenates two tensors along dimension <code>dim</code>. - </p><p> - Arguments: - </p> - <ul> - <li><code>tensor1</code>: a tensor expression.</li> - <li><code>tensor2</code>: a tensor expression.</li> - <li><code>dim</code>: the dimension to concatenate along.</li> - </ul> - Returns a new tensor with the two tensors <code>tensor1</code> and - <code>tensor2</code> concatenated along dimension <code>dim</code>. Examples: -<pre>{% raw %} -concat(t,t2,x) -{% endraw %}</pre> -<a href="https://docs.vespa.ai/playground/#N4KABGBEBmkFxgNrgmUrWQPYAd5QFNIAaFDSPBdDTAO30gBcSybIiEmDaBnLAJwAUAD0QBmALoBKOIgAsxAKzEAbBMisIAX1ZbSGauVwMi+tpTSa6DRgCYWNTByiNufIaNvTZY4nPVWOhh6rIaYxpymVhT4YeT0nADGWLSJAIbMZo7sDMmpGYKMxHbEwlIajkGoIQbREYQO5rFWEJAJUHnpjAD6WIwAFgT83QDuaQCejdnOkJ0FJUVlFTRV2igSIFpAA"> - playground concat examples -</a> - </td> -</tr> -<tr> - <th style="white-space:nowrap;">(tensor)partial-address</th> - <td> - <p id="slice"> - Slice - returns a new tensor containing the cells matching the partial address. - </p><p> - Arguments: - </p> - <ul> - <li><code>tensor</code>: a tensor expression.</li> - <li><code>partial-address</code>: Can be given in the form of a tensor address <code>{dimension:label,..}</code>, - or for tensors referenced directly and having a single mapped or indexed type respectively - as just a label in curly brackets <code>{label}</code> - or just an index in square brackets, <code>[index]</code>. - Index labels may be specified by a lambda expression enclosed in parentheses.</li> - </ul> - Returns a new tensor containing the cells matching the partial address. - A common special case is producing a single value by specifying a full address. - The type of the resulting tensor is the dimensions of the argument tensor not specified by the partial address. Examples: -<pre>{% raw %} -# a_tensor is of type tensor(key{},x[2]) -a_tensor{key:key1,x:1} -{% endraw %}</pre> -<a href="https://docs.vespa.ai/playground/#N4KABGBEBmkFxgNrgmUrWQPYAd5QFNIAaFDSPBdDTAO30gBcSybIiEmDaBnLAJwAUAD0QBmALoBKOIgAsxAKzEAbBMisIAX1ZbSGauVwMi+tpTSa6DHgBsAlgGNTViOwaNgwuAAYtGmjAdDD1WQ0xjThdAinxw8npOOycCAH0AJhZAtw4oT290-ytg1FCDK2wLdzNyC3jrTkZMmrZcrl4BQQBrAgBPYD1RdOk4YDAe3oBGWUmAOh9iMHT5iUWJ9NkxecW5FaCAmhLtFvrKkyzzONcoRKhk51SAWwBDHBwCABNU+1oPgmFPhcYm0msAJnAJpNiN5JkVAkcgicKpFCEDMHVrpBbpB7mkXm9Pt9fv9Cc1MSD0mC+hC+lDvIIxABadJSOGHXQoCQgLRAA"> - playground slice examples -</a> - </td> -</tr> -<tr> - <th style="white-space:nowrap;">tensor-literal-form</th> - <td> - <p id="literal"> - Returns a new tensor having the type and cell values given explicitly. - Each cell value may be supplied by a lambda which can access other features. - </p><p> - Returns a new tensor from the <a href="tensor.html#tensor-literal-form">literal form</a>, - where the type must be specified explicitly. - Each value may be supplied by a lambda, - which - in contrast to all other lambdas - - <em>may refer to features and expressions from the context</em>. Examples: - </p> -<pre>{% raw %} -# Declare an indexed tensor -tensor(x[2]):[1.0, 2.0] - -# Declare an mapped tensor -tensor(x{}):{x1:3, x2:4} -{% endraw %}</pre> - </td> -</tr> -<tr> - <th>cell_cast(<br/>  tensor,<br/>  cell_type<br/>)</th> - <td> - <p id="cell_cast"> - Returns a new tensor that is the same as the argument, except - that all cell values are converted to the given - <a href="tensor.html#tensor-type-spec">cell type</a>. - </p><p> - Arguments: - </p> - <ul> - <li><code>tensor</code>: a tensor expression.</li> - <li><code>cell_type</code>: wanted cell type.</li> - </ul> - Example, casting from <code>bfloat16</code> to <code>float</code>: -<pre>{% raw %} -# With a tensor t of the type tensor<bfloat16>(x[5])(x+1) -cell_cast(t, float) -{% endraw %}</pre> - </td> -</tr> -<tr> - <th>cell_order(<br/>  tensor,<br/>  order<br/>)</th> - <td> - <p id="cell-order"> - Returns a new tensor with the rank of the original cells based on the given order. - </p><p> - Arguments: - </p> - <ul> - <li><code>tensor</code>: a tensor expression.</li> - <li><code>order</code>: <code>max</code> or <code>min</code></li> - </ul> - Returns a new tensor with the rank of the original cells based on - the given order. With <code>max</code> the largest value gets - rank 0. With <code>min</code> the smallest value gets rank - 0. Examples: -<pre> -cell_order(tensor(x[3]):[2,3,1],max) # tensor(x[3]):[1,0,2] -cell_order(tensor(x[3]):[2,3,1],min) # tensor(x[3]):[1,2,0] -</pre> - <a href="https://docs.vespa.ai/playground/#N4IgZiBcDaoPYAcogMYgDQiZUAXZABLgBYCmBAjgK4CGANgJa4CeBcYB9dBKxVAdgGsAziAC+Y9PGwhSGLFFD9kuAIzy5kELlL9hcAE4AeMHTg1cAPgAUvAYOBiAlDgAMkVQGZ0qyAHZ0ACZIAFZ0Tw8wgBZIT1d0EMhAsXFJaWQ0TGw8QgA5AgZhAgATZn4aAFsGNAkpEERkOSzFEGUtXI1kT1S6hq1MhRxtQjAGfmK2A2LSAzGAczYOFFI6OlFa9K0mwaUVQM7+lboAfUNpg2s1dAIKmgAPJx7N1Hls4a0bmkFyGk-hQQIYEMRDIBAARqRhLgCB0NvUZNs3m1tN1MJptIEjLC0vCMq8WvgPsUDIhOKsCMIqGDhAgaMsimASRUQeRbv8QRZOAZyGB6MI5HC+rJ8UNkbgogdwAw6DoDMdKdTafTLt5AdZhE51U5HoKZAM3oSQDw4BUwWMedLZaQJmyAQB3JjESYMOZjehEXT6AyA4Hcykyp64rYi3ZaXAhSXigBUalSAF0xEA"> - playground example for cell_order - </a> - </td> -</tr> -</tbody> -</table> - - -<h3 id="lambda">Lambda functions in primitive functions</h3> -<p> -Some of the primitive functions accept lambda functions that are evaluated -and applied to a set of tensor cells. -The functions contain a single expression that have the same format and built-in functions as -<a href="ranking-expressions.html">general ranking expressions</a>. -However, the atoms are the arguments defined in the argument list of the lambda. -</p><p> -The expression cannot access variables or data structures outside the lambda, -i.e. they are not closures. -</p><p> -Examples: -</p> -<pre> -f(x)(log(x)) -f(x,y)(if(x < y, 0, 1)) -</pre> - - - -<h3 id="non-primitive-functions">Non-primitive functions</h3> -<p> -Non-primitive functions can be implemented by primitive functions, -but are not necessarily so for performance reasons. -Note that all the arithmetic operators, comparison operators, and -scalar operations can also be applied to tensors directly, -those are not repeated below here. -</p> -<table class="table"> - <thead> - <tr> - <th>Function</th> - <th>Description</th> - </tr> - </thead> - <tbody> - <tr> - <th>argmax(t, dim)</th> - <td> - <p id="argmax"><code>join(t, reduce(t, max, dim), f(x,y)(if (x == y, 1, 0)))</code></p> - <p> - Returns a tensor with cell(s) of the highest value(s) in the tensor set to 1. - The dimension argument follows the same format as reduce as multiple dimensions can be given and is optional. - </p> - </td> - </tr> - <tr> - <th>argmin(t, dim)</th> - <td> - <p id="argmin"><code>join(t, reduce(t, min, dim), f(x,y)(if (x == y, 1, 0)))</code></p> - <p> - Returns a tensor with cell(s) of the lowest value(s) in the tensor set to 1. - The dimension argument follows the same format as reduce as multiple dimensions can be given and is optional. - </p> - </td> - </tr> - <tr> - <th>avg(t, dim)</th> - <td> - <p id="avg-t"><code>reduce(t, avg, dim)</code></p> - <p> - Reduce the tensor with the <code>average</code> aggregator along dimension <code>dim</code>. - If the dimension argument is omitted, this reduces over all dimensions. - </p> - </td> - </tr> - <tr> - <th>count(t, dim)</th> - <td> - <p id="count-t"><code>reduce(t, count, dim)</code></p> - <p> - Reduce the tensor with the <code>count</code> aggregator along dimension <code>dim</code>. - If the dimension argument is omitted, this reduces over all dimensions. - </p> - - </td> - </tr> - <tr> - <th>cosine_similarity(t1, t2, dim)</th> - <td> - <p id="cosine-similarity-t"><code>reduce(t1*t2, sum, dim) / sqrt(reduce(t1*t1, sum, dim) * reduce(t2*t2, sum, dim))</code></p> - <p>The cosine similarity between the two vectors in the given dimension.</p> - </td> - </tr> - <tr> - <th>diag(n1, n2)</th> - <td> - <p id="diag-n1-n2"><code>tensor(i[n1],j[n2])(if (i==j, 1.0, 0.0)))</code></p> - <p>Returns a tensor with the diagonal set to 1.0.</p> - </td> - </tr> - <tr> - <th>elu(t)</th> - <td> - <p id="elu-t"><code>map(t, f(x)(if(x < 0, exp(x)-1, x)))</code></p> - <p><a href="https://arxiv.org/abs/1511.07289">Exponential linear unit</a>.</p> - </td> - </tr> - <tr> - <th>euclidean_distance(t1, t2, dim)</th> - <td> - <p id="euclidean-distance-t"><code>join(reduce(map(join(t1, t2, f(x,y)(x-y)), f(x)(x * x)), sum, dim), f(x)(sqrt(x)))</code></p> - <p>euclidean_distance: <code>sqrt(sum((t1-t2)^2, dim))</code>.</p> - </td> - </tr> - <tr> - <th>expand(t, dim)</th> - <td> - <p id="expand-t"><code>t * tensor(dim[1])(1)</code></p> - <p>Adds an indexed dimension with name <code>dim</code> to the tensor <code>t</code>.</p> - </td> - </tr> - <tr> - <th>hamming(t1, t2)</th> - <td> - <p id="hamming-t1-t2"><code>join(t1, t2, f(x,y)(hamming(x,y)))</code></p> - <p> - Join and return the Hamming distance between matching cells of <code>t1</code> and <code>t2</code>. - This function is mostly useful when the input contains vectors with binary data - and summing the hamming distance over the vector dimension, e.g.: - </p> - <table class="table"> - <thead></thead><tbody> - <tr> - <td>type of input <em>t1</em> →</td><td> - <code>tensor<int8>(dimone{},z[32])</code> - </td> - </tr><tr> - <td>type of input <em>t2</em> →</td><td> - <code>tensor<int8>(dimtwo{},z[32])</code> - </td> - </tr><tr> - <td>expression →</td><td> - <code>reduce(join(t1, t2, f(a,b)(hamming(a,b)), sum, z)</code> - </td> - </tr><tr> - <td>output type →</td><td> - <code>tensor<float>(dimone{},dimtwo{})</code> - </td> - </tr> - </tbody> - </table> - <p> - Note that the cell values are always treated as if they were both 8-bit integers in the range [-128,127], - and only then counting the number of bits that are different. - See also the corresponding <a href="../schemas/schemas.html#distance-metric">distance metric</a>. - Arguments can be scalars. - </p> - </td> - </tr> - <tr> - <th>l1_normalize(t, dim)</th> - <td> - <p id="l1-normalize-t"><code>join(t, reduce(t, sum, dim), f(x,y) (x / y))</code></p> - <p>L1 normalization: <code>t / sum(t, dim)</code>.</p> - </td> - </tr> - <tr> - <th>l2_normalize(t, dim)</th> - <td> - <p id="l2-normalize-t"><code>join(t, map(reduce(map(t, f(x)(x * x)), sum, dim), f(x)(sqrt(x))), f(x,y)(x / y))</code></p> - <p>L2 normalization: <code>t / sqrt(sum(t^2, dim)</code>.</p> - </td> - </tr> - <tr> - <th>matmul(t1, t2, dim)</th> - <td> - <p id="matmul-t1-t2"><code>reduce(join(t1, t2, f(x,y)(x * y)), sum, dim)</code></p> - <p> - Matrix multiplication of two tensors. - This is the product of the two tensors summed along a shared dimension. - </p> - </td> - </tr> - <tr> - <th>max(t, dim)</th> - <td> - <p id="max-t"><code>reduce(t, max, dim)</code></p> - <p>Reduce the tensor with the <code>max</code> aggregator along dimension <code>dim</code>.</p> - </td> - </tr> - <tr> - <th>median(t, dim)</th> - <td> - <p id="median-t"><code>reduce(t, median, dim)</code></p> - <p> - Reduce the tensor with the <code>median</code> aggregator along dimension <code>dim</code>. - If the dimension argument is omitted, this reduces over all dimensions. - </p> - </td> - </tr> - <tr> - <th>min(t, dim)</th> - <td> - <p id="min-t"><code>reduce(t, min, dim)</code></p> - <p>Reduce the tensor with the <code>min</code> aggregator along dimension <code>dim</code>.</p> - </td> - </tr> - <tr> - <th>prod(t, dim)</th> - <td> - <p id="prod-t"><code>reduce(t, prod, dim)</code></p> - <p> - Reduce the tensor with the <code>product</code> aggregator along dimension <code>dim</code>. - If the dimension argument is omitted, this reduces over all dimensions. - </p> - </td> - </tr> - <tr> - <th>random(n1, n2, ...)</th> - <td> - <p id="random-n1-n2"><code>tensor(i1[n1],i2[n2],...)(random(1.0))</code></p> - <p>Returns a tensor with random values between 0.0 and 1.0, uniform distribution.</p> - </td> - </tr> - <tr> - <th>range(n)</th> - <td> - <p id="range-t"><code>tensor(i[n])(i)</code></p> - <p>Returns a tensor with increasing values.</p> - </td> - </tr> - <tr> - <th>relu(t)</th> - <td> - <p id="relu-t"><code>map(t, f(x)(max(0,x)))</code></p> - <p>Rectified linear unit.</p> - </td> - </tr> - <tr> - <th>sigmoid(t)</th> - <td> - <p id="sigmoid-t"><code>map(t, f(x)(1.0 / (1.0 + exp(0.0-x))))</code></p> - <p>Returns the sigmoid of each element.</p> - </td> - </tr> - <tr> - <th>softmax(t, dim)</th> - <td> - <p id="softmax-t"><code>join(map(t, f(x)(exp(x))), reduce(map(t, f(x)(exp(x))), sum, dim), f(x,y)(x / y))</code></p> - <p>The softmax of the tensor, e.g. <code>e^x / sum(e^x)</code>.</p> - </td> - </tr> - <tr> - <th>sum(t, dim)</th> - <td> - <p id="sum-t"><code>reduce(t, sum, dim)</code></p> - <p> - Reduce the tensor with the <code>summation</code> aggregator along dimension <code>dim</code>. - If the dimension argument is omitted, this reduces over all dimensions. - </p> - </td> - </tr> - <tr> - <th>top(n, t)</th> - <td> - <p id="top-t"><code>t * filter_subspaces(cell_order(t, max) < n, f(s)(s))</code></p> - <p>top N function: Picks top N cells in a simple mapped tensor.</p> - </td> - </tr> - <tr> - <th>unpack_bits(t)</th> - <td> - <p id="unpack-bits">unpacks bits from int8 input to 8 times as many floats</p> - <p>The innermost indexed dimension will expand to have 8 times as many cells, - each with a float value of either 0.0 or 1.0 determined by one bit in - the 8-bit input value. Comparable to <code>numpy.unpackbits</code> - which gives the same basic functionality. - A minimal input such as <code>tensor<int8>(x[1]):[9]</code> - would give output <code>tensor<float>(x[8]):[0,0,0,0,1,0,0,1]</code> - (default bit-order is big-endian). - As a very complex example, an input with type <code>tensor<int8>(foo{},x[3],y[11],z{})</code> - will produce output with type <code>tensor<float>(foo{},x[3],y[88],z{})</code> - where "foo", "x" and "z" are unchanged, as "y" is the innermost indexed dimension. - </p> - </td> - </tr> - <tr> - <th>unpack_bits(t, cell_type)</th> - <td> - <p id="unpack-bits-cell-type">unpacks bits from int8 input to 8 times as many values</p> - <p>Same as above, but with optionally different cell_type (could be <code>double</code> - for example, if you will combine the output with other tensors using - double). - </p> - </td> - </tr> - <tr> - <th>unpack_bits(t, cell_type, endian)</th> - <td> - <p id="unpack-bits-cell-type-endian">unpacks bits from int8 input to 8 times as many values</p> - <p>Same as above, but also optionally different endian for the bits; must be - either <code>big</code> (default) or <code>little</code>. - </p> - </td> - </tr> - <tr> - <th>xw_plus_b(x, w, b, dim)</th> - <td> - <p id="xw-plus-b"><code>join(reduce(join(x, w, f(x,y)(x * y)), sum, dim), b, f(x,y)(x+y))</code></p> - <p> - Matrix multiplication of <code>x</code> (usually a vector) and <code>w</code> (weights), - with <code>b</code> added (bias). - A typical operation for activations in a neural network layer, - e.g. <code>sigmoid(xw_plus_b(x,w,b)))</code>. - </p> - </td> - </tr> - </tbody> -</table> diff --git a/en/reference/ranking/ranking-expressions.mdx b/en/reference/ranking/ranking-expressions.mdx new file mode 100644 index 0000000000..69d9f5e329 --- /dev/null +++ b/en/reference/ranking/ranking-expressions.mdx @@ -0,0 +1,492 @@ +--- +title: "Ranking Expressions" +--- + + +This is a complete reference to the _ranking expressions_ used to configure application specific ranking functions. For examples and an overview of how to use ranking expressions, see the [ranking overview](/en/basics/ranking). + +Ranking expressions are written in a simple language similar to ordinary functional notation. The atoms in ranking expressions are _rank features_ and _constants_. These atoms can be combined by _arithmetic operations_ and other _built-in functions_ over scalars and tensor. + +<table className="vespa-table"> + <tbody> + <tr> + <td>Rank Features</td> + <td>A rank feature is a named value calculated or looked up by vespa for each query/document combination. See the <a href="/en/reference/ranking/rank-features" aria-label="rank feature reference">rank feature reference</a> for a list of all the rank features available to ranking expressions.</td> + </tr> + <tr> + <td>Constants</td> + <td>A constant is either a floating point number, a boolean (true/false) or a quoted string. Since ranking expressions can only work on scalars and tensors, strings and booleans are immediately converted to scalars - true becomes 1.0, false 0.0 and a string its hash value. This means that **strings can only be used for equality comparisons**, other purposes such as parametrizing the key to slice out of a tensor will not work correctly.</td> + </tr> + </tbody> +</table> + +## Arithmetic operations + +Basic mathematical operations are expressed in in-fix notation: + +```bash +a + b * c +``` + +Arithmetic operations work on any tensor in addition to scalars, and are a short form of joining the tensors with the arithmetic operation used to join the cells. For example `tensorA * tensorB` is the same as `join(tensorA, tensorB, f(a,b)(a * b))`. + +All arithmetic operators in order of decreasing precedence: + +<table className="vespa-table"> + <thead> + <tr> + <th>Arithmetic operator</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>^</td> + <td>Power</td> + </tr> + <tr> + <td>%</td> + <td>Modulo</td> + </tr> + <tr> + <td>/</td> + <td>Division</td> + </tr> + <tr> + <td>*</td> + <td>Multiplication</td> + </tr> + <tr> + <td>-</td> + <td>Subtraction</td> + </tr> + <tr> + <td>+</td> + <td>Addition</td> + </tr> + <tr> + <td>&&</td> + <td>And: 1 if both arguments are non-zero, 0 otherwise.</td> + </tr> + <tr> + <td>||</td> + <td>Or: 1 if either argument is non-zero, 0 otherwise.</td> + </tr> + </tbody> +</table> + +## Mathematical scalar functions + +<table className="vespa-table"> + <thead> + <tr> + <th>Function</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>acos(*x*)</td> + <td>Inverse cosine of *x*</td> + </tr> + <tr> + <td>asin(*x*)</td> + <td>Inverse sine of *x*</td> + </tr> + <tr> + <td>atan(*x*)</td> + <td>Inverse tangent of *x*</td> + </tr> + <tr> + <td>atan2(*y*, *x*)</td> + <td>Inverse tangent of *y / x*, using signs of both arguments to determine correct quadrant.</td> + </tr> + <tr> + <td>bit(*x*, *y*)</td> + <td>Returns value of bit *y* in value *x* (for int8 values)</td> + </tr> + <tr> + <td>ceil(*x*)</td> + <td>Lowest integral value not less than *x*</td> + </tr> + <tr> + <td>cos(*x*)</td> + <td>Cosine of *x*</td> + </tr> + <tr> + <td>cosh(*x*)</td> + <td>Hyperbolic cosine of *x*</td> + </tr> + <tr> + <td>elu(*x*)</td> + <td>The Exponential Linear Unit activation function for value *x*</td> + </tr> + <tr> + <td>erf(*x*)</td> + <td>The Gauss error function for value *x*</td> + </tr> + <tr> + <td>exp(*x*)</td> + <td>Base-e exponential function.</td> + </tr> + <tr> + <td>fabs(*x*)</td> + <td>Absolute value of (floating-point) number *x*</td> + </tr> + <tr> + <td>floor(*x*)</td> + <td>Largest integral value not greater than *x*</td> + </tr> + <tr> + <td>fmod(*x*, *y*)</td> + <td>Remainder of *x / y*</td> + </tr> + <tr> + <td>isNan(*x*)</td> + <td>Returns 1.0 if *x* is NaN, 0.0 otherwise</td> + </tr> + <tr> + <td>ldexp(*x*, *exp*)</td> + <td>Multiply *x* by 2 to the power of *exp*</td> + </tr> + <tr> + <td>log(*x*)</td> + <td>Base-e logarithm of *x*</td> + </tr> + <tr> + <td>log10(*x*)</td> + <td>Base-10 logarithm of *x*</td> + </tr> + <tr> + <td>max(*x*, *y*)</td> + <td>Larger of *x* and *y*</td> + </tr> + <tr> + <td>min(*x*, *y*)</td> + <td>Smaller of *x* and *y*</td> + </tr> + <tr> + <td>pow(*x*, *y*)</td> + <td>Return *x* raised to the power of *y*</td> + </tr> + <tr> + <td>relu(*x*)</td> + <td>The Rectified Linear Unit activation function for value *x*</td> + </tr> + <tr> + <td>sigmoid(*x*)</td> + <td>The sigmoid (logistic) activation function for value *x*</td> + </tr> + <tr> + <td>sin(*x*)</td> + <td>Sine of *x*</td> + </tr> + <tr> + <td>sinh(*x*)</td> + <td>Hyperbolic sine of *x*</td> + </tr> + <tr> + <td>sqrt(*x*)</td> + <td>Square root of *x*</td> + </tr> + <tr> + <td>tan(*x*)</td> + <td>Tangent of *x*</td> + </tr> + <tr> + <td>tanh(*x*)</td> + <td>Hyperbolic tangent of *x*</td> + </tr> + <tr> + <td>hamming(*x*, *y*)</td> + <td>Hamming (bit-wise) distance between *x* and *y* (considered as 8-bit integers).</td> + </tr> + </tbody> +</table> + +`x` and `y` may be any ranking expression. + +## The if function + +The `if` function chooses between two sub-expressions based on the truth value of a condition. + +```bash +if (expression1operatorexpression2, trueExpression, falseExpression) +``` + +If the condition given in the first argument is true, the expression in argument 2 is used, otherwise argument 3. The four expressions may be any ranking expression. Conditional operators in ranking expression if functions: + +<table className="vespa-table"> + <thead> + <tr> + <th>Boolean operator</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td><=</td> + <td>Less than or equal</td> + </tr> + <tr> + <td><</td> + <td>Less than</td> + </tr> + <tr> + <td>==</td> + <td>Equal</td> + </tr> + <tr> + <td>~=</td> + <td>Approximately equal</td> + </tr> + <tr> + <td>>=</td> + <td>Greater than or equal</td> + </tr> + <tr> + <td>></td> + <td>Greater than</td> + </tr> + </tbody> +</table> + +The `in` membership operator uses a slightly modified if-syntax: + +```bash +if (expression1in[expression2, expression3, ..., expressionN], trueExpression, falseExpression) +``` + +If expression1 is equal to either of expression2 through expressionN, then trueExpression is used, otherwise falseExpression. + +## The switch function + +<Info> + **Note:** Available from Vespa 8.626.55 +</Info> + +The `switch` function chooses between multiple sub-expressions based on matching a value. It provides a more readable alternative to chained `if` statements when selecting from several options. + +```bash +switch (discriminant) { + case value1: result1, + case value2: result2, + ... + default: defaultResult +} +``` + +The `discriminant` expression is compared for equality against each `case` value expression in order. When a match is found, the corresponding result expression is evaluated and returned. If no case matches, the `default` result is returned. All expressions may be any ranking expression. At least one `case` must be specified. The `default` must be specified. + +## The foreach function + +The foreach function is not really part of the expression language but implemented as a [rank feature](/en/reference/ranking/rank-features#foreachdimensionvariablefeatureconditionoperation). + +## Tensor functions + +The following set of tensors functions are available to use in ranking expressions. The functions are grouped in primitive functions and convenience functions that can be implemented in terms of the primitive ones. + +### Primitive functions + +<table className="vespa-table"> + <thead> + <tr> + <th>Function</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>**map( tensor, f(x)(expr) )**</td> + <td>Returns a new tensor with the lambda function defined in <code>{`f(x)(expr)`}</code> applied to each cell. Arguments: <br/><br/> - <code>{`tensor`}</code>: a tensor expression. For example <code>{`attribute(tensor_field)`}</code> <br/> - <code>{`f(x)(expr)`}</code>: a <a href="#lambda-functions-in-primitive-functions" aria-label="lambda function">lambda function</a> with one argument. <br/><br/> Returns a new tensor where the expression in the lambda function is evaluated in each cell in <code>{`tensor`}</code>. <br/><br/> Examples: <code>{``}</code><code>{`bash map(t, f(x)(x*x)) `}</code><code>{``}</code> <br/><br/> <a href="https://docs.vespa.ai/playground/#N4KABGBEBmkFxgNrgmUrWQPYAd5QFNIAaFDSPBdDTAO30gEcBXAgJwE8AKAFwEoSZGpCIJIPArQDOWNgB5oAGywBDHgD4uAD0QAWALp84iAAzEAjMQBMxAMz7IQiAF8hz0hmrlcDIh+GUaE50DAC2KjgA+gRaKqE4in7BECJhEVws7Nz8xGDQ2nzaAHpWfALBrhiVYPogzkA" aria-label="playground example map">playground example map</a></td> + </tr> + <tr> + <td>**map_subspaces( tensor, f(x)(expr) )**</td> + <td>Returns a new tensor with the lambda function defined in <code>{`f(x)(expr)`}</code> applied to each dense subspace. <br/> Arguments: <br/><br/> - <code>{`tensor`}</code>: a tensor expression. For example <code>{`attribute(tensor_field)`}</code> <br/> - <code>{`f(x)(expr)`}</code>: a <a href="#lambda-functions-in-primitive-functions" aria-label="lambda function">lambda function</a> with one argument. Returns a new tensor where the lambda function is evaluated for each dense subspace in <code>{`tensor`}</code>. This is an advanced feature that enables using dense <a href="#tensor" aria-label="tensor generator">tensor generator</a> expressions to transform mixed tensors. <br/> Example: <br/> <code>{``}</code><code>{` map_subspaces(tensor(x{},y[3]):{a:[1,2,3]},f(d)(tensor(z[2])(d{y:(z)}+d{y:(z+1)})))`}</code><code>{``}</code> <br/> <a href="https://docs.vespa.ai/playground/#N4KABGBEBmkFxgNrgmUrWQPYAd5QFNIAaFDSPBdDTAO30gEcBXAgJwE8AKAFwEoSZGpCIJIPArQDOWNgB4AlrR4AOAHxcAHsAC+xDogBMAXT5xgAQziIAjAFZiAWhsA2YzshCIOoXqHVyXAYiUhooSjQvOgZmWhwLAGMAawB9ACMFHikUgHdMgAsUgFsLHBSpZjSpeISCKUEwiBEGErKKqpq6rhZ2bn5iaC4AEz5eSRl5JVUNA1dTLgyeYeAOOC4OAHoVPj0AdkcOAFJtvlPPMJ8MS7BjEB0gA" aria-label="playground example for map_subspaces">playground example for map_subspaces</a></td> + </tr> + <tr> + <td>**filter_subspaces( tensor, f(x)(expr) )**</td> + <td>Returns a new tensor containing only the subspaces for which the lambda function defined in <code>{`f(x)(expr)`}</code> returns true. <br/> Arguments: <br/> - <code>{`tensor`}</code>: a tensor expression. Must have at least one mapped dimension. <br/> - <code>{`f(x)(expr)`}</code>: a <a href="#lambda-functions-in-primitive-functions" aria-label="lambda function">lambda function</a> with one argument. <br/> Returns a new tensor containing only the subspaces for which the lambda function defined in <code>{`f(x)(expr)`}</code> returns true. Typically used to get rid of unneeded values in sparse tensors. Example: <code>{``}</code><code>{` filter_subspaces(tensor(x{}):{a:1,b:2,c:3,d:4},f(value)(value>2)) # tensor(x{}):{c:3,d:4} `}</code><code>{``}</code> <br/><br/> <a href="https://docs.vespa.ai/playground/#N4IgZiBcDaoPYAcogMYgDQiZUAXZABLgBYCmBAjgK4CGANgJa4CeBcYB9dBKxVAdgGsAziAC+Y9PGwhSGLFFD9kuAIzy5kELlL9hcAE4AeMHTg1cAPgAUvAYOBiAlDgAMkVQGZ0qyAHZ0ACZIAFZ0Tw8wgBZIT1d0EMhAsXFJaWQ0TGw8QgA5AgZhAgATZn4aAFsGNAkpEERkOSzFEGUtXI1kT1S6hq1MhRxtQjAGfmK2A2LSAzGAczYOFFI6OlFa9K0mwaUVQM7+lboAfUNpg2s1dAIKmgAPJx7N1Hls4a0bmkFyGk-hQQIYEMRDIBAARqRhLgCB0NvUZNs3m1tN1MJptIEjLC0vCMq8WvgPsUDIhOKsCMIqGDhAgaMsimASRUQeRbv8QRZOAZyGB6MI5HC+rJ8UNkbgogdwAw6DoDMdKdTafTLt5AdZhE51U5HoKZAM3oSQDw4BUwWMedLZaQJmyAQB3JjESYMOZjehEXT6AyA4Hcykyp64rYi3ZaXAhSXigBUalSAF0xEA" aria-label="playground example for filter_subspaces">playground example for filter_subspaces</a></td> + </tr> + <tr> + <td>**reduce( tensor, aggregator, dim1, dim2, ... )**</td> + <td>Returns a new tensor with the <code>{`aggregator`}</code> applied across dimensions dim1, dim2, etc. If no dimensions are specified, reduce over all dimensions. <br/> Arguments: <br/> - <code>{`tensor`}</code>: a tensor expression. <br/> - <code>{`aggregator`}</code>: the aggregator to use. See below. <br/> - <code>{`dim1, dim2, ...`}</code>: the dimensions to reduce over. Optional. <br/> Returns a new tensor with the aggregator applied across dimensions <code>{`dim1`}</code>, <code>{`dim2`}</code>, etc. If no dimensions are specified, reduce over all dimensions. <br/> Available aggregators are: <br/> - <code>{`avg`}</code>: arithmetic mean <br/> - <code>{`count`}</code>: number of elements <br/> - <code>{`max`}</code>: maximum value <br/> - <code>{`median`}</code>: median value <br/> - <code>{`min`}</code>: minimum value <br/> - <code>{`prod`}</code>: product of all values <br/> - <code>{`sum`}</code>: sum of all values <br/> Examples: <br/> <code>{``}</code><code>{` reduce(t, sum) # Sum all values in tensor reduce(t, count, x) # Count number of cells along dimension x `}</code><code>{``}</code> <br/> <a href="https://docs.vespa.ai/playground/#N4KABGBEBmkFxgNrgmUrWQPYAd5QFNIAaFDSPBdDTAO30gEcBXAgJwE8AKAFwEoSZGpCIJIPArQDOWNgB5oAGywBDHgD4uAD0QAWALp84iAAzEAjMQBMxAMz7IQiAF8hz0hmrlcDIh+GUaE50DGwEACbMAMYEAPpSzAC2sQRaKok4in7BECKhEdEEXCzs3PzEYAmJAsGuGHVg+iDOQA" aria-label="playground example reduce">playground example reduce</a></td> + </tr> + <tr> + <td>**join( tensor1, tensor2, f(x,y)(expr) )**</td> + <td>Returns a new tensor constructed from the *natural join* between <code>{`tensor1`}</code> and <code>{`tensor2`}</code>, with the resulting cells having the value as calculated from <code>{`f(x,y)(expr)`}</code>, where <code>{`x`}</code> is the cell value from <code>{`tensor1`}</code> and <code>{`y`}</code> from <code>{`tensor2`}</code>. <br/> Arguments: <br/> - <code>{`tensor1`}</code>: a tensor expression. <br/> - <code>{`tensor2`}</code>: a tensor expression. <br/> - <code>{`f(x,y)(expr)`}</code>: a <a href="#lambda-functions-in-primitive-functions" aria-label="lambda function">lambda function</a> with two arguments. Returns a new tensor constructed from the *natural join* between <code>{`tensor1`}</code> and <code>{`tensor2`}</code>, with the resulting cells having the value as calculated from <code>{`f(x,y)(expr)`}</code>, where <code>{`x`}</code> is the cell value from <code>{`tensor1`}</code> and <code>{`y`}</code> from <code>{`tensor2`}</code>. <br/> Formally, the result of the <code>{`join`}</code> is a new tensor with dimensions the union of dimension between <code>{`tensor1`}</code> and <code>{`tensor2`}</code>. The cells are the set of all combinations of cells that have equal values on their common dimensions. Examples: <br/> <code>{``}</code><code>{` join(t1, t2, f(x,y)(x * y)) `}</code><code>{``}</code> <br/> <a href="https://docs.vespa.ai/playground/#N4KABGBEBmkFxgNrgmUrWQPYAd5QFNIAaFDSPBdDTAO30gEcBXAgJwE8AKAFwEoSZGpCIJIPArQDOWNgB5oAGywBDHgD4uAD2ABfPnGDAtcAAy6EARgB0p4mhOWLYAEy3dkIRF1DdpDNTkuAxE-sKUaF50DGo8bACWAEbMErwCYTSEDBLSsgrKapo6fhx6BkYmdhxmzgDMtvbGZsTVTggALA0OcJYtNQgArF1Nva3OAGzunpk+GH5CgZjBYqFRFPiL5PRiAFZY8fQZwqJQewdcLOzc-PaxCcmpN2DQ2i182mAAVGAcfAJRs1QgIAuiBdEA" aria-label="playground example join">playground example join</a></td> + </tr> + <tr> + <td>**merge( tensor1, tensor2, f(x,y)(expr) )**</td> + <td>Returns a new tensor consisting of all cells from both the arguments, where the lambda function is used to produce a single value in the cases where both arguments provide a value for a cell. <br/> Arguments: <br/> - <code>{`tensor1`}</code>: a tensor expression. <br/> - <code>{`tensor2`}</code>: a tensor expression. <br/> - <code>{`f(x,y)(expr)`}</code>: a <a href="#lambda-functions-in-primitive-functions" aria-label="lambda function">lambda function</a> with two arguments. <br/> Returns a new tensor having all the cells of both arguments, where the lambda is invoked to produce a single value only when both arguments have a value for the same cell. <br/> The argument tensors must have the same type, and that will be the type of the resulting tensor. Example: <code>{``}</code><code>{` merge(t1, t2, f(left,right)(right)) `}</code><code>{``}</code> <br/> <a href="https://docs.vespa.ai/playground/#N4KABGBEBmkFxgNrgmUrWQPYAd5QFNIAaFDSPBdDTAO30gEcBXAgJwE8AKAFwEoSZGpCIJIPArQDOWNlwDWBDsAC+xAB6IATAF0+cYAEM4iAIzFdxAEYmAzMQAsOlZCEQVQtUOrlcDIqQ0UJRobnQMhjw8bACWVswSvAKBQYQMEtKyCkqqGtp6BjaIAKzEAGw6xADGJgDsxAAczq5BHhheGD6YfmIBYRT4XeT0YgC27ADmBAD6BOqGozgANn2paWOTBFws7Nz8xGCR0XEJW-tg0Fwr0DzEsRMAFvxc9098AmFtqF86ICpAA" aria-label="playground example merge">playground example merge</a></td> + </tr> + <tr> + <td>**tensor( tensor-type-spec )(expr)**</td> + <td>Generates new tensors according to type specification and expression <code>{`expr`}</code>. Arguments: - <code>{`tensor-type-spec`}</code>: an <a href="/en/reference/ranking/tensor#tensor-type-spec" aria-label="indexed tensor type specification.">indexed tensor type specification.</a> - <code>{`(expression)`}</code>: a <a href="#lambda-functions-in-primitive-functions" aria-label="lambda function">lambda function</a> expressing how to generate the tensor. Generates new tensors according to the type specification and expression <code>{`expr`}</code>. The tensor type must be an indexed tensor (e.g. <code>{`tensor<float>(x[10])`}</code>). The expression in <code>{`expr`}</code> will be evaluated for each cell. The arguments in the expression is implicitly the names of the dimensions defined in the type spec. <br/> Useful for creating transformation tensors. <br/> Examples: <br/> <code>{``}</code><code>{` tensor<float>(x[3])(x) `}</code><code>{``}</code> <br/> <a href="https://docs.vespa.ai/playground/#N4KABGBEBmkFxgNrgmUrWQPYAd5QFNIAaFDSPBdDTAO30gHMDaCAnAQwBcCB9VgO4kyNSEQSQetAM5Y2ACgAeiAMwBdAJRKNkERAC+I-aQzVyuBkROjKaPXQbNWnHvwIDeAJmE1M4qFKybAA80AA2WNwAfEqInmrEAJ5xmkpgALzpYIk69oYYxiJmmBYSVvYU+MXk9BJcPr6EDIFysQAsmnCIAIwAdAAMxGCeA0Mqo2BtA2q6vvmohaYVpU3W5LbVDhJO7Nx8grzQbFgAtrwctFhcABbsvC1sDb5izSxB8tApWlzAinDQAGpuvpcnMjCg1CB9EA" aria-label="playground generate examples">playground generate examples</a></td> + </tr> + <tr> + <td>**rename( tensor, dim-to-rename, new-names )**</td> + <td>Renames one or more dimensions in the tensor. <br/> Arguments: <br/> - <code>{`tensor`}</code>: a tensor expression. <br/> - <code>{`dim-to-rename`}</code>: a dimension, or list of dimensions, to rename. <br/> - <code>{`new-names`}</code>: new names for the dimensions listed above. Returns a new tensor with one or more dimension renamed. Examples: <br/> <code>{``}</code><code>{` rename(t1,x,z)`}</code><code>{``}</code> <br/> <a href="https://docs.vespa.ai/playground/#N4KABGBEBmkFxgNrgmUrWQPYAd5QFNIAaFDSPBdDTAO30gBcSybIiEmDaBnLAJwAUAD2ABfYgE9xASjjBgwuAAYpKsQgCMAOlVolqyXE0awy3cX3G1y0+b2LrRk1t1jIrCGNYTW1crgMRKQ0UJRonnQM-NwAhgC2BAD6BMIJOAA2wZEQ7NFxiYKMxMLEAF4yHqHeGL4Y-piBnNmhFPgN5PScMbQJyanpWUmMAO5YLKG5HFA9fUXEIlIyCwCWxABWMpWRNai7ALogYkA" aria-label="playground rename examples">playground rename examples</a></td> + </tr> + <tr> + <td>**concat( tensor1, tensor2, dim )**</td> + <td>Concatenates two tensors along dimension <code>{`dim`}</code>. <br/> Arguments: <br/> - <code>{`tensor1`}</code>: a tensor expression. <br/> - <code>{`tensor2`}</code>: a tensor expression. <br/> - <code>{`dim`}</code>: the dimension to concatenate along. <br/> Returns a new tensor with the two tensors <code>{`tensor1`}</code> and <code>{`tensor2`}</code> concatenated along dimension <code>{`dim`}</code>. <br/> Examples: <br/> <code>{``}</code><code>{` concat(t,t2,x) `}</code><code>{``}</code> <br/> <a href="https://docs.vespa.ai/playground/#N4KABGBEBmkFxgNrgmUrWQPYAd5QFNIAaFDSPBdDTAO30gBcSybIiEmDaBnLAJwAUAD0QBmALoBKOIgAsxAKzEAbBMisIAX1ZbSGauVwMi+tpTSa6DRgCYWNTByiNufIaNvTZY4nPVWOhh6rIaYxpymVhT4YeT0nADGWLSJAIbMZo7sDMmpGYKMxHbEwlIajkGoIQbREYQO5rFWEJAJUHnpjAD6WIwAFgT83QDuaQCejdnOkJ0FJUVlFTRV2igSIFpAA" aria-label="playground concat examples">playground concat examples</a></td> + </tr> + <tr> + <td>**(tensor)partial-address**</td> + <td>Slice - returns a new tensor containing the cells matching the partial address.<br/> Arguments: <br/> - <code>{`tensor`}</code>: a tensor expression. <br/> - <code>{`partial-address`}</code>: Can be given in the form of a tensor address <code>{`{dimension:label,..}`}</code>, or for tensors referenced directly and having a single mapped or indexed type respectively as just a label in curly brackets <code>{`{label}`}</code> or just an index in square brackets, <code>{`[index]`}</code>. Index labels may be specified by a lambda expression enclosed in parentheses. <br/> Returns a new tensor containing the cells matching the partial address. A common special case is producing a single value by specifying a full address. The type of the resulting tensor is the dimensions of the argument tensor not specified by the partial address.<br/> Examples: <br/> <code>{``}</code><code>{` # a_tensor is of type tensor(key{},x[2]) a_tensor{key:key1,x:1} `}</code><code>{``}</code> <br/> <a href="https://docs.vespa.ai/playground/#N4KABGBEBmkFxgNrgmUrWQPYAd5QFNIAaFDSPBdDTAO30gBcSybIiEmDaBnLAJwAUAD0QBmALoBKOIgAsxAKzEAbBMisIAX1ZbSGauVwMi+tpTSa6DHgBsAlgGNTViOwaNgwuAAYtGmjAdDD1WQ0xjThdAinxw8npOOycCAH0AJhZAtw4oT290-ytg1FCDK2wLdzNyC3jrTkZMmrZcrl4BQQBrAgBPYD1RdOk4YDAe3oBGWUmAOh9iMHT5iUWJ9NkxecW5FaCAmhLtFvrKkyzzONcoRKhk51SAWwBDHBwCABNU+1oPgmFPhcYm0msAJnAJpNiN5JkVAkcgicKpFCEDMHVrpBbpB7mkXm9Pt9fv9Cc1MSD0mC+hC+lDvIIxABadJSOGHXQoCQgLRAA" aria-label="playground slice examples">playground slice examples</a></td> + </tr> + <tr> + <td>**tensor-literal-form**</td> + <td>Returns a new tensor having the type and cell values given explicitly. Each cell value may be supplied by a lambda which can access other features. <br/> Returns a new tensor from the <a href="/en/reference/ranking/tensor#tensor-literal-form" aria-label="literal form">literal form</a>, where the type must be specified explicitly. Each value may be supplied by a lambda, which - in contrast to all other lambdas - *may refer to features and expressions from the context*. <br/> Examples: <code>{``}</code><code>{` # Declare an indexed tensor tensor(x[2]):[1.0, 2.0] # Declare an mapped tensor tensor(x{}):{x1:3, x2:4} `}</code><code>{``}</code></td> + </tr> + <tr> + <td>**cell_cast( tensor, cell_type )**</td> + <td>Returns a new tensor that is the same as the argument, except that all cell values are converted to the given <a href="/en/reference/ranking/tensor#tensor-type-spec" aria-label="cell type">cell type</a>. <br/> Arguments: <br/> - <code>{`tensor`}</code>: a tensor expression. <br/> - <code>{`cell_type`}</code>: wanted cell type. <br/> Example, casting from <code>{`bfloat16`}</code> to <code>{`float`}</code>: <br/> <code>{``}</code><code>{` # With a tensor t of the type tensor<bfloat16>(x[5])(x+1) cell_cast(t, float)`}</code><code>{``}</code></td> + </tr> + <tr> + <td>**cell_order( tensor, order )**</td> + <td>Returns a new tensor with the rank of the original cells based on the given order. <br/> Arguments: <br/> - <code>{`tensor`}</code>: a tensor expression. <br/> - <code>{`order`}</code>: <code>{`max`}</code> or <code>{`min`}</code> <br/> Returns a new tensor with the rank of the original cells based on the given order. With <code>{`max`}</code> the largest value gets rank 0. With <code>{`min`}</code> the smallest value gets rank 0. <br/> Examples: <br/> <code>{``}</code><code>{` cell_order(tensor(x[3]):[2,3,1],max) # tensor(x[3]):[1,0,2] cell_order(tensor(x[3]):[2,3,1],min) # tensor(x[3]):[1,2,0] `}</code><code>{``}</code> <br/> <a href="https://docs.vespa.ai/playground/#N4IgZiBcDaoPYAcogMYgDQiZUAXZABLgBYCmBAjgK4CGANgJa4CeBcYB9dBKxVAdgGsAziAC+Y9PGwhSGLFFD9kuAIzy5kELlL9hcAE4AeMHTg1cAPgAUvAYOBiAlDgAMkVQGZ0qyAHZ0ACZIAFZ0Tw8wgBZIT1d0EMhAsXFJaWQ0TGw8QgA5AgZhAgATZn4aAFsGNAkpEERkOSzFEGUtXI1kT1S6hq1MhRxtQjAGfmK2A2LSAzGAczYOFFI6OlFa9K0mwaUVQM7+lboAfUNpg2s1dAIKmgAPJx7N1Hls4a0bmkFyGk-hQQIYEMRDIBAARqRhLgCB0NvUZNs3m1tN1MJptIEjLC0vCMq8WvgPsUDIhOKsCMIqGDhAgaMsimASRUQeRbv8QRZOAZyGB6MI5HC+rJ8UNkbgogdwAw6DoDMdKdTafTLt5AdZhE51U5HoKZAM3oSQDw4BUwWMedLZaQJmyAQB3JjESYMOZjehEXT6AyA4Hcykyp64rYi3ZaXAhSXigBUalSAF0xEA" aria-label="playground example for cell_order">playground example for cell_order</a></td> + </tr> + </tbody> +</table> + +### Lambda functions in primitive functions + +Some of the primitive functions accept lambda functions that are evaluated and applied to a set of tensor cells. The functions contain a single expression that have the same format and built-in functions as [general ranking expressions](/en/reference/ranking/ranking-expressions). However, the atoms are the arguments defined in the argument list of the lambda. + +The expression cannot access variables or data structures outside the lambda, i.e. they are not closures. + +Examples: + +```bash +f(x)(log(x)) +f(x,y)(if(x < y, 0, 1)) +``` + +### Non-primitive functions + +Non-primitive functions can be implemented by primitive functions, but are not necessarily so for performance reasons. Note that all the arithmetic operators, comparison operators, and scalar operations can also be applied to tensors directly, those are not repeated below here. + +<table className="vespa-table"> + <thead> + <tr> + <th>Function</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>**argmax(t, dim)**</td> + <td><code>{`join(t, reduce(t, max, dim), f(x,y)(if (x == y, 1, 0)))`}</code> <br/> Returns a tensor with cell(s) of the highest value(s) in the tensor set to 1. The dimension argument follows the same format as reduce as multiple dimensions can be given and is optional.</td> + </tr> + <tr> + <td>**argmin(t, dim)**</td> + <td><code>{`join(t, reduce(t, min, dim), f(x,y)(if (x == y, 1, 0)))`}</code><br/> Returns a tensor with cell(s) of the lowest value(s) in the tensor set to 1. The dimension argument follows the same format as reduce as multiple dimensions can be given and is optional.</td> + </tr> + <tr> + <td>**avg(t, dim)**</td> + <td><code>{`reduce(t, avg, dim)`}</code> <br/> Reduce the tensor with the <code>{`average`}</code> aggregator along dimension <code>{`dim`}</code>. If the dimension argument is omitted, this reduces over all dimensions.</td> + </tr> + <tr> + <td>**count(t, dim)**</td> + <td><code>{`reduce(t, count, dim)`}</code> <br/> Reduce the tensor with the <code>{`count`}</code> aggregator along dimension <code>{`dim`}</code>. If the dimension argument is omitted, this reduces over all dimensions.</td> + </tr> + <tr> + <td>**cosine_similarity(t1, t2, dim)**</td> + <td><code>{`reduce(t1*t2, sum, dim) / sqrt(reduce(t1*t1, sum, dim) * reduce(t2*t2, sum, dim))`}</code> <br/> The cosine similarity between the two vectors in the given dimension.</td> + </tr> + <tr> + <td>**diag(n1, n2)**</td> + <td><code>{`tensor(i[n1],j[n2])(if (i==j, 1.0, 0.0)))`}</code> <br/> Returns a tensor with the diagonal set to 1.0.</td> + </tr> + <tr> + <td>**elu(t)**</td> + <td><code>{`map(t, f(x)(if(x < 0, exp(x)-1, x)))`}</code> <br/> <a href="https://arxiv.org/abs/1511.07289" aria-label="Exponential linear unit">Exponential linear unit</a>.</td> + </tr> + <tr> + <td>**euclidean_distance(t1, t2, dim)**</td> + <td><code>{`join(reduce(map(join(t1, t2, f(x,y)(x-y)), f(x)(x * x)), sum, dim), f(x)(sqrt(x)))`}</code> <br/> euclidean_distance: <code>{`sqrt(sum((t1-t2)^2, dim))`}</code>.</td> + </tr> + <tr> + <td>**expand(t, dim)**</td> + <td><code>{`t * tensor(dim[1])(1)`}</code> <br/> Adds an indexed dimension with name <code>{`dim`}</code> to the tensor <code>{`t`}</code>.</td> + </tr> + <tr> + <td>**hamming(t1, t2)**</td> + <td><code>{`join(t1, t2, f(x,y)(hamming(x,y)))`}</code> <br/> Join and return the Hamming distance between matching cells of <code>{`t1`}</code> and <code>{`t2`}</code>. This function is mostly useful when the input contains vectors with binary data and summing the hamming distance over the vector dimension, e.g.: <br/> | type of input *t1* → | <code>{`tensor<int8>(dimone{},z[32])`}</code> <br/> | type of input *t2* → | <code>{`tensor<int8>(dimtwo{},z[32])`}</code> <br/> | expression → <code>{`reduce(join(t1, t2, f(a,b)(hamming(a,b)), sum, z)`}</code> | | output type → <code>{`tensor<float>(dimone{},dimtwo{})`}</code> | <br/> Note that the cell values are always treated as if they were both 8-bit integers in the range [-128,127], and only then counting the number of bits that are different. See also the corresponding <a href="/en/reference/schemas/schemas#distance-metric" aria-label="distance metric">distance metric</a>. Arguments can be scalars.</td> + </tr> + <tr> + <td>**l1_normalize(t, dim)**</td> + <td><code>{`join(t, reduce(t, sum, dim), f(x,y) (x / y))`}</code> <br/> L1 normalization: <code>{`t / sum(t, dim)`}</code>.</td> + </tr> + <tr> + <td>**l2_normalize(t, dim)**</td> + <td><code>{`join(t, map(reduce(map(t, f(x)(x * x)), sum, dim), f(x)(sqrt(x))), f(x,y)(x / y))`}</code> <br/> L2 normalization: <code>{`t / sqrt(sum(t^2, dim)`}</code>.</td> + </tr> + <tr> + <td>**matmul(t1, t2, dim)**</td> + <td><code>{`reduce(join(t1, t2, f(x,y)(x * y)), sum, dim)`}</code> <br/> Matrix multiplication of two tensors. This is the product of the two tensors summed along a shared dimension.</td> + </tr> + <tr> + <td>**max(t, dim)**</td> + <td><code>{`reduce(t, max, dim)`}</code> <br/> Reduce the tensor with the <code>{`max`}</code> aggregator along dimension <code>{`dim`}</code>.</td> + </tr> + <tr> + <td>**median(t, dim)**</td> + <td><code>{`reduce(t, median, dim)`}</code> <br/> Reduce the tensor with the <code>{`median`}</code> aggregator along dimension <code>{`dim`}</code>. If the dimension argument is omitted, this reduces over all dimensions.</td> + </tr> + <tr> + <td>**min(t, dim)**</td> + <td><code>{`reduce(t, min, dim)`}</code> <br/> Reduce the tensor with the <code>{`min`}</code> aggregator along dimension <code>{`dim`}</code>.</td> + </tr> + <tr> + <td>**prod(t, dim)**</td> + <td><code>{`reduce(t, prod, dim)`}</code> <br/> Reduce the tensor with the <code>{`product`}</code> aggregator along dimension <code>{`dim`}</code>. If the dimension argument is omitted, this reduces over all dimensions.</td> + </tr> + <tr> + <td>**random(n1, n2, ...)**</td> + <td><code>{`tensor(i1[n1],i2[n2],...)(random(1.0))`}</code> <br/> Returns a tensor with random values between 0.0 and 1.0, uniform distribution.</td> + </tr> + <tr> + <td>**range(n)**</td> + <td><code>{`tensor(i[n])(i)`}</code> <br/> Returns a tensor with increasing values.</td> + </tr> + <tr> + <td>**relu(t)**</td> + <td><code>{`map(t, f(x)(max(0,x)))`}</code> <br/> Rectified linear unit.</td> + </tr> + <tr> + <td>**sigmoid(t)**</td> + <td><code>{`map(t, f(x)(1.0 / (1.0 + exp(0.0-x))))`}</code> <br/> Returns the sigmoid of each element.</td> + </tr> + <tr> + <td>**softmax(t, dim)**</td> + <td><code>{`join(map(t, f(x)(exp(x))), reduce(map(t, f(x)(exp(x))), sum, dim), f(x,y)(x / y))`}</code> <br/> The softmax of the tensor, e.g. <code>{`e^x / sum(e^x)`}</code>.</td> + </tr> + <tr> + <td>**sum(t, dim)**</td> + <td><code>{`reduce(t, sum, dim)`}</code> <br/> Reduce the tensor with the <code>{`summation`}</code> aggregator along dimension <code>{`dim`}</code>. If the dimension argument is omitted, this reduces over all dimensions.</td> + </tr> + <tr> + <td>**top(n, t)**</td> + <td><code>{`t * filter_subspaces(cell_order(t, max) < n, f(s)(s))`}</code> <br/> top N function: Picks top N cells in a simple mapped tensor.</td> + </tr> + <tr> + <td>**unpack_bits(t)**</td> + <td>unpacks bits from int8 input to 8 times as many floats The innermost indexed dimension will expand to have 8 times as many cells, each with a float value of either 0.0 or 1.0 determined by one bit in the 8-bit input value. Comparable to <code>{`numpy.unpackbits`}</code> which gives the same basic functionality. A minimal input such as <code>{`tensor<int8>(x[1]):[9]`}</code> would give output <code>{`tensor<float>(x[8]):[0,0,0,0,1,0,0,1]`}</code> (default bit-order is big-endian). As a very complex example, an input with type <code>{`tensor<int8>(foo{},x[3],y[11],z{})`}</code> will produce output with type <code>{`tensor<float>(foo{},x[3],y[88],z{})`}</code> where "foo", "x" and "z" are unchanged, as "y" is the innermost indexed dimension.</td> + </tr> + <tr> + <td>**unpack_bits(t, cell_type)**</td> + <td>unpacks bits from int8 input to 8 times as many values <br/> Same as above, but with optionally different cell_type (could be <code>{`double`}</code> for example, if you will combine the output with other tensors using double).</td> + </tr> + <tr> + <td>unpack_bits(t, cell_type, endian)</td> + <td>unpacks bits from int8 input to 8 times as many values <br/> Same as above, but also optionally different endian for the bits; must be either <code>{`big`}</code> (default) or <code>{`little`}</code>.</td> + </tr> + <tr> + <td>xw_plus_b(x, w, b, dim)</td> + <td><code>{`join(reduce(join(x, w, f(x,y)(x * y)), sum, dim), b, f(x,y)(x+y))`}</code> <br/> Matrix multiplication of <code>{`x`}</code> (usually a vector) and <code>{`w`}</code> (weights), with <code>{`b`}</code> added (bias). A typical operation for activations in a neural network layer, e.g. <code>{`sigmoid(xw_plus_b(x,w,b)))`}</code>.</td> + </tr> + </tbody> +</table> \ No newline at end of file diff --git a/en/reference/ranking/string-segment-match.html b/en/reference/ranking/string-segment-match.html deleted file mode 100644 index 3b2049f68f..0000000000 --- a/en/reference/ranking/string-segment-match.html +++ /dev/null @@ -1,270 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "String Segment Match" -redirect_from: -- /en/reference/string-segment-match.html ---- - -<p> -The <strong>string segment match</strong> algorithm computes a set of metrics - -the <strong>string segment match metrics</strong> - intended to capture all the -information about how well a <em>query</em> string matches a -<em>field</em> string, which is useful for document ranking in search, -from the limited information usually available during matching in search engines. -</p><p> -The algorithm works by locating <em>segments</em>, which are local -regions in the field which contain one or more adjacent terms of the -query. All segment start points in the field are explored, and the -ones which produce the best overall segmentation are -chosen. Informally, a segmentation is good if it contains few segments -with the query matches close together. Example: -</p> -<img src="/assets/img/relevance/segment-example.png" alt="String segment match example" /> -<p> -Here two segments are found to cover the query. An alternative second -segment is also found, but is discarded because it has inferior query -term proximity. The other lone "Bush" instance is never considered -because there is no segmentation causing "Bush" to be a segment start -token (i.e. there is no lone "George"). -</p><p> -A subset of the metrics are computed from the tokens <em>within</em> -located segments, while another subset of metrics characterizes the -number and placement of the segments themselves. This allows the -metrics to reflect the property of natural language that tokens which -are close are often part of the same meaning (typically as parts -of the same sentence) while somewhat more distant tokens are only weakly related. -</p><p> -Queries typically consists of multiple intended segments, where each -segment is continuous and in order, while the ordering between the -segments is of little significance (although the more important -segments tend to come first). -</p><p> -By matching the query in term order -to the best and fewest segments of the field, this algorithm makes use -of the available field data to discover the likely query segmentation -from the evidence. Explicit segmentation information can also be used -in the form of connectivity scores which influences the segment scores -and thus the chosen segments. -</p> - - - -<h2 id="source-information">Source information</h2> -<p> -The information used by this algorithm to calculate these metrics is -(the last three are optional): -</p> -<ul> - <li>The position of each occurrence of a <em>matched</em> term in the query and field - <li>The number of terms in the query and field - <li>A number per query term indicating the weight (importance) of each query term - <li>A number per query term indicating the relative frequency of the term - <li>A number per adjacent query term pair indicating the linguistic connectivity between the terms -</ul> - - - -<h2 id="algorithm">Algorithm</h2> -<p> -The algorithm locates segments from a given start query term as follows: -<pre> -i = the position of the first query term -j = the position of the first match of the query term at i -while (i < query.length) { - nextJ = the first location of query term i+1 at most <span class="pre-hilite">proximityLimit</span> steps to the right of j - if (nextJ not found) - nextJ = the first location query term i+1 at most <span class="pre-hilite">proximityLimit</span> steps to the left backwards of j - - if (nextJ is found) { // Find next token in this segment - i = i+1 - j = nextJ - } - else { - nextJ = the first location of query term i+1 at any location to the right forwards from j - if (nextJ not found) - nextJ = the first location of query term i+1 at any location to the left backwards from j - if (nextJ not found) { // Skip a non-existing query term - i = i+1 - } - else { // End of segment - return i,j as the segment end - } - } -} -</pre> -<p> -So a segment is a set of terms in the field which corresponds to an adjacent subsection of query -terms where the gap between any adjacent query term in the field is at most <code>proximityLimit</code> -forwards or backwards, and where query terms not present in the field are ignored. -</p><p> -Let's call the field term search order used above the <em>semantic distance</em> between two field position. -So, for example -</p> -<ul> - <li>the semantic distance between j and nextJ is n if nextJ is located n places after j and n<<code>proximityLimit</code></li> - <li>the semantic distance between j and nextJ is <code>proximityLimit</code>+n if nextJ is located n places to the <em>left</em> of j and n<<code>proximityLimit</code></li> -</ul> -<p> -The algorithm explores tokens and segments in the semantic distance space. -The algorithm works with any definition of semantic distance. -The algorithm will record for each segment start point: -</p> -<ul> - <li>metrics - The current best known metrics of the combined segments up to this point</li> - <li>previousJ - The end j of the previous segment in the field (if any)</li> - <li>i - the query term i which is the start of this segment</li> - <li>semanticDistanceExplored - the distance from previousJ explored so far</li> - <li>open - whether there are possibly more j's to find beyond semanticDistanceExplored</li> -</ul> -<p>With this, we can list the high level pseudocode of the algorithm:</p> -<pre> -currentSegment=a segment start point at starting at i=0 (the start of the query) -while (there are open segment start points) { - newSegment=find the next segment, at currentSegment.i with semanticDistance > currentsegment.semanticDistance - if (no newSegment) { - currentSegment.open = false - continue; - } - SegmentStartPoint existingStartPoint=find stored segment at start point newSegment.i+1 - if (no existingStartPoint) { - create and store a new (empty open) segment start point at newSegment.i+1 - } - else { - if (newSegment.score > existingStartPoint.score) { - existingStartPoint.metrics.score = newSegment.metrics.score - existingStartPoint.previousJ = newSegment.endJ - existingStartPoint.semanticDistanceExplored = newSegment.semanticDistance+1 - } - } - currentSegment=the next open start point (in the order they are found) -} -finalMetrics=metrics of segmentStartPoint at query.length -</pre> -<p> - The <code>metric.score</code> deciding which of two segmentation paths is best - is <code>absoluteProximity/segments^2</code>. - Any combination of metrics which can be calculated for a partial segmentation may be used. -</p><p> -Browse the -<a href="https://github.com/vespa-engine/vespa/tree/master/searchlib/src/main/java/com/yahoo/searchlib/ranking/features"> - code</a> for details. -</p> - - - -<h2 id="complexity">Complexity</h2> -<p> - The algorithm uses a linear programming technique to avoid recomputing earlier segments. - A constant amount of data is stored per possible segment start point. - Since there are at most as many start points as there are query terms, - the memory complexity is <code>O(query.length)</code>. - As the algorithm will try all possible segment starting points (up to a limit), - and there are at most one starting point per query term, - the time complexity is <code>O(query.length*total number of term occurrences)</code>. - The average time complexity is <code>O(average segment length/average number of term occurrences)</code>. -</p> - - - -<h2 id="metric-set">Metric set</h2> -<p> -The complete string segment match metrics set, computed by this algorithm, is: -</p> -<ul> -<li>match</li> -<li>proximity</li> -<li>completeness</li> -<li>queryCompleteness</li> -<li>fieldCompleteness</li> -<li>orderness</li> -<li>relatedness</li> -<li>earliness</li> -<li>longestSequenceRatio</li> -<li>segmentProximity</li> -<li>unweightedProximity</li> -<li>absoluteProximity</li> -<li>occurrence</li> -<li>absoluteOccurrence</li> -<li>weightedOccurrence</li> -<li>weightedAbsoluteOccurrence</li> -<li>significantOccurrence</li> -<li>weight</li> -<li>significance</li> -<li>importance</li> -<li>segments</li> -<li>matches</li> -<li>outOfOrder</li> -<li>gaps</li> -<li>gapLength</li> -<li>longestSequence</li> -<li>head</li> -<li>tail</li> -<li>segmentDistance</li> -</ul> -<p> -These are documented as the features prefixed by -<code>fieldMatch(<em>name</em>)</code>, see the -<a href="rank-features.html#field-match-features-normalized">rank features reference</a>. -</p><p> -The metric set contains both low level, un-normalized metrics corresponding directly to a concept in -the string segment match algorithm (e.g <code>segments</code>, <code>gaps</code>), normalized basic features -(e.g. <code>proximity</code>, <code>queryCompleteness</code>), normalized metrics combining lower level -metrics into some useful part of the truth (e.g. <code>completeness</code>, <code>orderness</code>) -as well as a metric combining most of the others into one normalized value (<code>match</code>). -Applications will choose the subset of the metrics which captures the properties they determine -is important, at the granularity which is convenient. -</p> - - - -<h2 id="configuration-parameters">Configuration parameters</h2> -<p> -The algorithm has the following configuration parameters, where the three first are fundamental -parameters of the algorithm, and the others are used to normalize or combine certain features. -Configure using <a href="rank-feature-configuration.html">rank feature configuration</a>: -</p> -<table class="table"> -<thead> -<tr><th>Parameter<th>Default<th>Description -</thead><tbody> -<tr><td><code>proximityLimit</code><td>10<td> -The maximum allowed gap within a segment.</td></tr> - -<tr><td><code>proximityTable</code><td>1/(2^(i/2)/3) for i in 9..0 followed by 1/2^(i/2) for i in 0..10<td> -The proximity table deciding the importance of separations of various distances, -The table must have size proximityLimit*2+1, where the first half is for reverse direction -distances. The table must only contain values between 0 and 1, where 1 is "perfect" and 0 is "worst".</td></tr> - -<tr><td><code>maxAlternativeSegmentations</code><td>10000<td> -The maximum number of <em>alternative</em> segmentations allowed in addition to the first one found. -This will prefer to not consider iterations on segments that are far out in the field, -and which starts late in the query.</td></tr> - -<tr><td><code>maxOccurrences</code><td>100<td> -The number of occurrences the number of occurrences of each word is normalized against. -This should be set as the number above which additional occurrences of the term has no real significance.</td></tr> - -<tr><td><code>proximityCompletenessImportance</code><td>0.9<td> -A number between 0 and 1 which determines the importance of field completeness in relation to -query completeness in the <code>match</code> and <code>completeness</code> metrics.</td></tr> - -<tr><td><code>relatednessImportance</code><td>0.9<td> -The normalized importance of relatedness used in the <code>match</code> metric.</td></tr> - -<tr><td><code>earlinessImportance</code><td>0.05<td> -The importance of the match occurring early in the query, relative to segmentProximityImportance, -occurrenceImportance and proximityCompletenessImportance in the <code>match</code> metric.</td></tr> - -<tr><td><code>segmentProximityImportance</code><td>0.05<td> -The importance of multiple segments being close to each other, relative to earlinessImportance, -occurrenceImportance and proximityCompletenessImportance in the <code>match</code> metric.</td></tr> - -<tr><td><code>occurrenceImportance</code><td>0.05<td> -The importance of having many occurrences of the query terms, relative to earlinessImportance, -segmentProximityImportance and proximityCompletenessImportance in the <code>match</code> metric.</td></tr> - -<tr><td><code>fieldCompletenessImportance</code><td>0.05<td> -A number between 0 and 1 which determines the importance of field completeness in relation to -query completeness in the <code>match</code> and <code>completeness</code> metrics.</td></tr> -</tbody> -</table> diff --git a/en/reference/ranking/string-segment-match.mdx b/en/reference/ranking/string-segment-match.mdx new file mode 100644 index 0000000000..63f938c8f9 --- /dev/null +++ b/en/reference/ranking/string-segment-match.mdx @@ -0,0 +1,212 @@ +--- +title: "String Segment Match" +--- + +The **string segment match** algorithm computes a set of metrics - the **string segment match metrics** - intended to capture all the information about how well a _query_ string matches a _field_ string, which is useful for document ranking in search, from the limited information usually available during matching in search engines. + +The algorithm works by locating _segments_, which are local regions in the field which contain one or more adjacent terms of the query. All segment start points in the field are explored, and the ones which produce the best overall segmentation are chosen. Informally, a segmentation is good if it contains few segments with the query matches close together. Example: + +<Frame> +![String segment match example](/assets/img/relevance/segment-example.png) +</Frame> + +Here two segments are found to cover the query. An alternative second segment is also found, but is discarded because it has inferior query term proximity. The other lone "Bush" instance is never considered because there is no segmentation causing "Bush" to be a segment start token (i.e. there is no lone "George"). + +A subset of the metrics are computed from the tokens _within_ located segments, while another subset of metrics characterizes the number and placement of the segments themselves. This allows the metrics to reflect the property of natural language that tokens which are close are often part of the same meaning (typically as parts of the same sentence) while somewhat more distant tokens are only weakly related. + +Queries typically consists of multiple intended segments, where each segment is continuous and in order, while the ordering between the segments is of little significance (although the more important segments tend to come first). + +By matching the query in term order to the best and fewest segments of the field, this algorithm makes use of the available field data to discover the likely query segmentation from the evidence. Explicit segmentation information can also be used in the form of connectivity scores which influences the segment scores and thus the chosen segments. + +## Source information + +The information used by this algorithm to calculate these metrics is (the last three are optional): + +- The position of each occurrence of a _matched_ term in the query and field +- The number of terms in the query and field +- A number per query term indicating the weight (importance) of each query term +- A number per query term indicating the relative frequency of the term +- A number per adjacent query term pair indicating the linguistic connectivity between the terms + +## Algorithm + +The algorithm locates segments from a given start query term as follows: + +```python expandable +i = the position of the first query term +j = the position of the first match of the query term at i +while (i < query.length) { + nextJ = the first location of query term i+1 at mostproximityLimitsteps to the right of j + if (nextJ not found) + nextJ = the first location query term i+1 at mostproximityLimitsteps to the left backwards of j + + if (nextJ is found) { // Find next token in this segment + i = i+1 + j = nextJ + } + else { + nextJ = the first location of query term i+1 at any location to the right forwards from j + if (nextJ not found) + nextJ = the first location of query term i+1 at any location to the left backwards from j + if (nextJ not found) { // Skip a non-existing query term + i = i+1 + } + else { // End of segment + return i,j as the segment end + } + } +} +``` + +So a segment is a set of terms in the field which corresponds to an adjacent subsection of query terms where the gap between any adjacent query term in the field is at most `proximityLimit` forwards or backwards, and where query terms not present in the field are ignored. + +Let's call the field term search order used above the _semantic distance_ between two field position. So, for example + +- the semantic distance between j and nextJ is n if nextJ is located n places after j and n\<`proximityLimit` +- the semantic distance between j and nextJ is `proximityLimit`+n if nextJ is located n places to the _left_ of j and n\<`proximityLimit` + +The algorithm explores tokens and segments in the semantic distance space. The algorithm works with any definition of semantic distance. The algorithm will record for each segment start point: + +- metrics - The current best known metrics of the combined segments up to this point +- previousJ - The end j of the previous segment in the field (if any) +- i - the query term i which is the start of this segment +- semanticDistanceExplored - the distance from previousJ explored so far +- open - whether there are possibly more j's to find beyond semanticDistanceExplored + +With this, we can list the high level pseudocode of the algorithm: + +```js expandable +currentSegment=a segment start point at starting at i=0 (the start of the query) +while (there are open segment start points) { + newSegment=find the next segment, at currentSegment.i with semanticDistance > currentsegment.semanticDistance + if (no newSegment) { + currentSegment.open = false + continue; + } + SegmentStartPoint existingStartPoint=find stored segment at start point newSegment.i+1 + if (no existingStartPoint) { + create and store a new (empty open) segment start point at newSegment.i+1 + } + else { + if (newSegment.score > existingStartPoint.score) { + existingStartPoint.metrics.score = newSegment.metrics.score + existingStartPoint.previousJ = newSegment.endJ + existingStartPoint.semanticDistanceExplored = newSegment.semanticDistance+1 + } + } + currentSegment=the next open start point (in the order they are found) +} +finalMetrics=metrics of segmentStartPoint at query.length +``` + +The `metric.score` deciding which of two segmentation paths is best is `absoluteProximity/segments^2`. Any combination of metrics which can be calculated for a partial segmentation may be used. + +Browse the [code](https://github.com/vespa-engine/vespa/tree/master/searchlib/src/main/java/com/yahoo/searchlib/ranking/features) for details. + +## Complexity + +The algorithm uses a linear programming technique to avoid recomputing earlier segments. A constant amount of data is stored per possible segment start point. Since there are at most as many start points as there are query terms, the memory complexity is `O(query.length)`. As the algorithm will try all possible segment starting points (up to a limit), and there are at most one starting point per query term, the time complexity is `O(query.length*total number of term occurrences)`. The average time complexity is `O(average segment length/average number of term occurrences)`. + +## Metric set + +The complete string segment match metrics set, computed by this algorithm, is: + +- match +- proximity +- completeness +- queryCompleteness +- fieldCompleteness +- orderness +- relatedness +- earliness +- longestSequenceRatio +- segmentProximity +- unweightedProximity +- absoluteProximity +- occurrence +- absoluteOccurrence +- weightedOccurrence +- weightedAbsoluteOccurrence +- significantOccurrence +- weight +- significance +- importance +- segments +- matches +- outOfOrder +- gaps +- gapLength +- longestSequence +- head +- tail +- segmentDistance + +These are documented as the features prefixed by `fieldMatch(name)`, see the [rank features reference](/en/reference/ranking/rank-feature-configuration). + +The metric set contains both low level, un-normalized metrics corresponding directly to a concept in the string segment match algorithm (e.g `segments`, `gaps`), normalized basic features (e.g. `proximity`, `queryCompleteness`), normalized metrics combining lower level metrics into some useful part of the truth (e.g. `completeness`, `orderness`) as well as a metric combining most of the others into one normalized value (`match`). Applications will choose the subset of the metrics which captures the properties they determine is important, at the granularity which is convenient. + +## Configuration parameters + +The algorithm has the following configuration parameters, where the three first are fundamental parameters of the algorithm, and the others are used to normalize or combine certain features. Configure using [rank feature configuration](/en/reference/ranking/rank-feature-configuration): + +<table className="vespa-table"> + <thead> + <tr> + <th>Parameter</th> + <th>Default</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td><code>{`proximityLimit`}</code></td> + <td>10</td> + <td>The maximum allowed gap within a segment.</td> + </tr> + <tr> + <td><code>{`proximityTable`}</code></td> + <td>1/(2^(i/2)/3) for i in 9..0 followed by 1/2^(i/2) for i in 0..10</td> + <td>The proximity table deciding the importance of separations of various distances, The table must have size proximityLimit*2+1, where the first half is for reverse direction distances. The table must only contain values between 0 and 1, where 1 is "perfect" and 0 is "worst".</td> + </tr> + <tr> + <td><code>{`maxAlternativeSegmentations`}</code></td> + <td>10000</td> + <td>The maximum number of _alternative_ segmentations allowed in addition to the first one found. This will prefer to not consider iterations on segments that are far out in the field, and which starts late in the query.</td> + </tr> + <tr> + <td><code>{`maxOccurrences`}</code></td> + <td>100</td> + <td>The number of occurrences the number of occurrences of each word is normalized against. This should be set as the number above which additional occurrences of the term has no real significance.</td> + </tr> + <tr> + <td><code>{`proximityCompletenessImportance`}</code></td> + <td>0.9</td> + <td>A number between 0 and 1 which determines the importance of field completeness in relation to query completeness in the <code>{`match`}</code> and <code>{`completeness`}</code> metrics.</td> + </tr> + <tr> + <td><code>{`relatednessImportance`}</code></td> + <td>0.9</td> + <td>The normalized importance of relatedness used in the <code>{`match`}</code> metric.</td> + </tr> + <tr> + <td><code>{`earlinessImportance`}</code></td> + <td>0.05</td> + <td>The importance of the match occurring early in the query, relative to segmentProximityImportance, occurrenceImportance and proximityCompletenessImportance in the <code>{`match`}</code> metric.</td> + </tr> + <tr> + <td><code>{`segmentProximityImportance`}</code></td> + <td>0.05</td> + <td>The importance of multiple segments being close to each other, relative to earlinessImportance, occurrenceImportance and proximityCompletenessImportance in the <code>{`match`}</code> metric.</td> + </tr> + <tr> + <td><code>{`occurrenceImportance`}</code></td> + <td>0.05</td> + <td>The importance of having many occurrences of the query terms, relative to earlinessImportance, segmentProximityImportance and proximityCompletenessImportance in the <code>{`match`}</code> metric.</td> + </tr> + <tr> + <td><code>{`fieldCompletenessImportance`}</code></td> + <td>0.05</td> + <td>A number between 0 and 1 which determines the importance of field completeness in relation to query completeness in the <code>{`match`}</code> and <code>{`completeness`}</code> metrics.</td> + </tr> + </tbody> +</table> \ No newline at end of file diff --git a/en/reference/ranking/tensor.html b/en/reference/ranking/tensor.html deleted file mode 100644 index 86f84117a2..0000000000 --- a/en/reference/ranking/tensor.html +++ /dev/null @@ -1,312 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Tensor Reference" -redirect_from: -- /en/reference/tensor.html ---- - -<p> - A tensor is a set of named <em>dimensions</em> defining its <em>order</em> - and a set of values located in the space of those dimensions: -</p> -<ul> - <li><em>Cell</em>: A value located in the dimension space. - Consists of a cell address and the value at that address.</li> - <li><em>Address</em>: A set of key-values - where each key is a <em>dimension</em> from the set of dimensions of the tensor, - and each value is a <em>label</em> (integer or string) determining the cell's location in that dimension.</li> -</ul> -<p> - The set of dimensions, cell values and cell address key-values can be of any size including zero. - A dimension can be either mapped or indexed. - Mapped dimensions use string identifiers as labels in the cell addresses (like a map), - while indexed dimensions use integers in the range <code>[0,N></code> (like an array), - where N is the size of the dimension. -</p> - - -<h2 id="tensor-type-spec">Tensor type spec</h2> -<p> - Contained in <a href="../schemas/schemas.html#constant">constant</a> - or <a href="../schemas/schemas.html#tensor">tensor field type</a>. - The dimensions of a tensor and the cell type defines its type. - A tensor type contains a list of dimensions on the format: -</p> -<pre> -tensor<value-type>(dimension-1,dimension-2,...,dimension-N) -</pre> -<p>The value-type is one of:</p> -<table class="table"> -<thead> -<tr> - <th>Type</th> - <th>Description</th> -</tr> -</thead> - <tbody> -<tr> - <td>float</td><td>32-bit IEEE 754 floating point</td> -</tr><tr> - <td>double</td><td>64-bit IEEE 754 floating point</td> -</tr><tr> - <td>int8</td><td>signed 8-bit integer - - see <a href="../../performance/feature-tuning.html#cell-value-types">performance considerations</a> - </td> -</tr><tr> - <td>bfloat16</td><td>first 16 bits of 32-bit IEEE 754 floating point - - see <a href="../../performance/feature-tuning.html#cell-value-types">performance considerations</a> - </td> -</tr> -</tbody> -</table> -<p>A dimension is specified as follows:</p> -<ul> - <li><code>dimension-name{}</code> - a mapped dimension</li> - <li><code>dimension-name[size]</code> - an indexed dimension</li> -</ul> -<p>The tensor type for a tensor<float> with two mapped dimensions <em>x</em> and <em>y</em> looks like:</p> -<pre> -tensor<float>(x{},y{}) -</pre> -<p>Example tensor with this type:</p> -<pre>{% raw %} -{{x:a,y:b}:10.0, {x:c,y:d}:20.1} -{% endraw %}</pre> -<p> - The tensor type for a tensor<float> with two indexed dimensions <em>x</em> and <em>y</em> - with sizes 3 and 2 respectively looks like: -</p> -<pre> -tensor<float>(x[3],y[2]) -</pre> -<p>Example tensor with this type (representing a matrix):</p> -<pre>{% raw %} -{{x:0,y:0}:1, {x:0,y:1}:2.1, - {x:1,y:0}:3, {x:1,y:1}:5, - {x:2,y:0}:7, {x:2,y:1}:11} -{% endraw %}</pre> -<p> - Note that the labels are indexes in the range <em>[0,dimension-size></em> -</p><p> - A tensor<double> with both mapped and indexed dimensions is <em>mixed</em>: -</p> -<pre> -tensor<double>(key{},x[2]) -</pre> -<p>Example:</p> -<pre>{% raw %} -{{key:a,x:0}:10, {key:b,x:0}:2.7, - {key:a,x:1}:5.3, {key:b,x:1}:-7 } -{% endraw %}</pre> - - - -<h2 id="tensor-literal-form">Tensor literal form</h2> -<p> - The tensor literal form is used in: -</p> -<ul> - <li>Tensors in queries, - see <a href="../../ranking/ranking-expressions-features.html#query-feature-types">defining query feature types</a> - and <a href="../../ranking/tensor-user-guide.html#querying-with-tensors">tensor user guide</a> - </li> - <li>Constant tensors in <a href="model-files.html">stateless model evaluation</a></li> - <li>Building tensors using the - <a href="https://javadoc.io/doc/com.yahoo.vespa/vespajlib/latest/com/yahoo/tensor/Tensor.html"> - Java Tensor API</a></li> -</ul> -<p> - The tensor literal form is <em>not</em> a JSON format. - When sent inside a JSON format (like when you POST a query), - it should be passed as a string. -</p> - -<h3 id="general-literal-form">General literal form</h3> -<p>The general literal form is verbose and explicit, - can represent any tensor and is as follows (EBNF):</p> -<pre> -literal tensor = ( tensor-type-spec ":" )? "{" cells "}" ; -cells = | cell , { "," cell } ; -cell = "{" address "}:" scalar ; -address = | element, { "," element } ; -element = dimension ":" label ; -dimension = integer | identifier ; -label = integer | identifier | 'string' | "string" ; -identifier = ["A"-"Z","a"-"z","0"-"9","_","@"](["A"-"Z","a"-"z","0"-"9","_","@","$"])* -</pre> -<p>For query inputs, the type should be declared as an input in the ranking profile, - so the type spec is usually skipped.</p> - -<h4 id="general-literal-form-examples">General literal form examples:</h4> -<p>An empty tensor:</p> -<pre> -{} -</pre> -<p>A single value tensor with a single mapped dimension <em>x</em>:</p> -<pre> -{ {x:foo}:5.0 } -</pre> -<p>A tensor with multiple values and mapped dimensions <em>x</em> and <em>y</em>:</p> -<pre> -{ {x:foo, y:bar}:5.0, {x:foo, y:baz}:7.0 } -</pre> -<p>A tensor where type is specified explicitly with a single indexed dimension <em>x</em> representing a vector:</p> -<pre> -tensor<float>(x[3]):{ {x:0}:3.0, {x:1}:5.0, {x:2}:7.0 } -</pre> -<p>A tensor with a type using the default value type (double) and quoted labels:</p> -<pre> -tensor(key{}):{ {key:'key.1'}:3.0, {key:'key 2'}:5.0, {key:"key's"}:7.0 } -</pre> - - -<h3 id="indexed-short-form">Indexed short form</h3> -<p> - Tensors where all dimensions are indexed can be written as numbers wrapped in square brackets in - <em>right dimension adjacent</em> order. If the type isn't declared already, this form requires - an explicit tensor type. Note: Dimensions should be alphabetically ordered. -</p> -<p> - Brackets must be nested according to the structure of the - type, where values in dimensions to the right are closer than - dimensions on the left. For backwards compatibility (not supported - in expressions), cell values may also be given in the same order - as a flat array. -</p> - -<h4 id="Indexed-short-form-examples">Indexed short form examples:</h4> -<p>A float 1d tensor in indexed form:</p> -<pre> -tensor<float>(x[3]):[3.0, 5.0, 7.0] -</pre> -<p> - A matrix in indexed form. Since the values for the right-most dimension (y) are adjacent, - the value 3 is here assigned to the cell {x:0,y:2}: -</p> -<pre> -tensor<float>(x[2],y[3]):[[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]] -</pre> -<p> - Deprecated: - Since inner brackets can be omitted, the above is equivalent to -</p> -<pre> -tensor<float>(x[2],y[3]):[1.0, 2.0, 3.0, 4.0, 5.0, 6.0] -</pre> - -<h3 id="indexed-hex-form">Binary hex format</h3> -<p> - Tensors representing binary data (indexed tensors with <code>int8</code> - cell value type) may be represented using a hexadecimal representation, - similar to the corresponding - <a href="../schemas/document-json-format.html#tensor-hex-dump">JSON tensor feed format</a>. - If the tensor type is declared as - <code>tensor<int8>(x[2],y[3])</code> input could be just -</p> -<pre> -0B22038405FF -</pre> -<p>which would be equivalent to:</p> -<pre> -[[11, 34, 3], [-124, 5, -1]] -</pre> - - -<h3 id="mapped-short-form">Mapped short form</h3> -<p> - Tensors with a single mapped dimension can be written by specifying - just the label in that implicit dimension instead of a full address map. - This form requires a type to be declared or explicitly specified. -</p> - -<h4 id="map-short-form-example">Map short form example:</h4> -<pre> -tensor<float>(key{}):{ key1:1.0, key2:2.0 } -</pre> - -<h3 id="mixed-short-form">Mixed short form</h3> -<p> - Tensors with a single mapped dimension and one or more indexed dimensions - can be written by specifying the mapped dimension in the map short form - and the values of each dense subspace on the indexed short form. - This form requires a type to be known (declared) or specified. -</p> - -<h4 id="mixed-short-form-example">Mixed short form example:</h4> -<p>A map of matrices:</p> -<pre> -tensor<float>(key{},x[2],y[3]):{ key1:[[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]], - key2:[[1.1, 2.1, 3.1], [4.1, 5.1, 6.1]] } -</pre> -<p>This may even be combined with a hexadecimal format for the dense subspace:</p> -<pre> -tensor<int8>(key{},x[5]):{ key1: 0102030405, key2: fffefdfcfb } -</pre> - -<p> - Tensors with a multiple mapped dimensions may use an extended - variant of the mixed short form, where labels are nested. - Again note that the type should be declared with dimensions - in alphabetic order, so the nesting will follow a consistent - ordering. -</p> -<pre> -tensor(category{},key{},x[2],y[3]):{ - cat1:{key1:[[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]], - key2:[[1.1, 2.1, 3.1], [4.1, 5.1, 6.1]]}, - cat2:{key1:[[7.3, 8.3, 9.3], [7.0, 8.0, 9.0]], - key3:[[7.5, 8.5, 9.5], [7.9, 8.9, 9.9]]} -} -</pre> -the equivalent fully-specified literal form would be -<pre>{% raw %} -tensor(category{},key{},x[2],y[3]):{{category:cat1, key:key1, x:0, y:0}: 1.0, - {category:cat1, key:key1, x:0, y:1}: 2.0, - {category:cat1, key:key1, x:0, y:2}: 3.0, - {category:cat1, key:key1, x:1, y:0}: 4.0, - {category:cat1, key:key1, x:1, y:1}: 5.0, - {category:cat1, key:key1, x:1, y:2}: 6.0, - {category:cat1, key:key2, x:0, y:0}: 1.1, - {category:cat1, key:key2, x:0, y:1}: 2.1, - {category:cat1, key:key2, x:0, y:2}: 3.1, - {category:cat1, key:key2, x:1, y:0}: 4.1, - {category:cat1, key:key2, x:1, y:1}: 5.1, - {category:cat1, key:key2, x:1, y:2}: 6.1, - {category:cat2, key:key1, x:0, y:0}: 7.3, - {category:cat2, key:key1, x:0, y:1}: 8.3, - {category:cat2, key:key1, x:0, y:2}: 9.3, - {category:cat2, key:key1, x:1, y:0}: 7.0, - {category:cat2, key:key1, x:1, y:1}: 8.0, - {category:cat2, key:key1, x:1, y:2}: 9.0, - {category:cat2, key:key3, x:0, y:0}: 7.5, - {category:cat2, key:key3, x:0, y:1}: 8.5, - {category:cat2, key:key3, x:0, y:2}: 9.5, - {category:cat2, key:key3, x:1, y:0}: 7.9, - {category:cat2, key:key3, x:1, y:1}: 8.9, - {category:cat2, key:key3, x:1, y:2}: 9.9} -{% endraw %}</pre> - -<h2 id="tensor-functions">Tensor functions</h2> -<p> - Tensor functions are listed in the <a href="ranking-expressions.html#tensor-functions">expressions</a> documentation. -</p> - -<h2 id="tensor-rank-features">Tensor rank features</h2> -<p> - The following rank features can be used to refer to or create tensors in ranking expressions. - The tensors can come from the document, the query or a constant in the application package: -</p> -<ul> - <li><a href="rank-features.html#attribute(name)">attribute(tensor_attribute)</a></li> - <li><a href="rank-features.html#query(value)">query(tensor_feature)</a></li> - <li><a href="rank-features.html#constant(name)">constant(tensor_constant)</a></li> - <li><a href="rank-features.html#tensorFromWeightedSet(source,dimension)"> - tensorFromWeightedSet(source, dimension)</a></li> - <li><a href="rank-features.html#tensorFromLabels(source,dimension)">tensorFromLabels(source, dimension)</a></li> - <li><a href="rank-features.html#tensorFromStructs(attribute,key,value,type)">tensorFromStructs(attribute,key,value,type)</a></li> -</ul> -<p>Use the following reference documentation on how use tensors in documents:</p> -<ul> - <li><a href="../schemas/schemas.html#tensor">Tensor field in schema</a></li> - <li><a href="../schemas/document-json-format.html">Document JSON Format</a></li> -</ul> diff --git a/en/reference/ranking/tensor.mdx b/en/reference/ranking/tensor.mdx new file mode 100644 index 0000000000..a7f4c940cf --- /dev/null +++ b/en/reference/ranking/tensor.mdx @@ -0,0 +1,280 @@ +--- +title: "Tensor Reference" +--- + + +A tensor is a set of named _dimensions_ defining its _order_ and a set of values located in the space of those dimensions: + +- _Cell_: A value located in the dimension space. Consists of a cell address and the value at that address. +- _Address_: A set of key-values where each key is a _dimension_ from the set of dimensions of the tensor, and each value is a _label_ (integer or string) determining the cell's location in that dimension. + +The set of dimensions, cell values and cell address key-values can be of any size including zero. A dimension can be either mapped or indexed. Mapped dimensions use string identifiers as labels in the cell addresses (like a map), while indexed dimensions use integers in the range `[0,N>` (like an array), where N is the size of the dimension. + +## Tensor type spec + +Contained in [constant](/en/reference/schemas/schemas#constant) or [tensor field type](/en/reference/schemas/schemas#tensor). The dimensions of a tensor and the cell type defines its type. A tensor type contains a list of dimensions on the format: + +```bash +tensor<value-type>(dimension-1,dimension-2,...,dimension-N) +``` + +The value-type is one of: + +<table className="vespa-table"> + <thead> + <tr> + <th>Type</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>float</td> + <td>32-bit IEEE 754 floating point</td> + </tr> + <tr> + <td>double</td> + <td>64-bit IEEE 754 floating point</td> + </tr> + <tr> + <td>int8</td> + <td>signed 8-bit integer - see <a href="/en/performance/feature-tuning#cell-value-types" aria-label="performance considerations">performance considerations</a></td> + </tr> + <tr> + <td>bfloat16</td> + <td>first 16 bits of 32-bit IEEE 754 floating point - see <a href="/en/performance/feature-tuning#cell-value-types" aria-label="performance considerations">performance considerations</a></td> + </tr> + </tbody> +</table> + +A dimension is specified as follows: + +- `dimension-name{}` - a mapped dimension +- `dimension-name[size]` - an indexed dimension + +The tensor type for a tensor\<float\> with two mapped dimensions _x_ and _y_ looks like: + +```bash +tensor<float>(x{},y{}) +``` + +Example tensor with this type: + +```bash +{{x:a,y:b}:10.0, {x:c,y:d}:20.1} +``` + +The tensor type for a tensor\<float\> with two indexed dimensions _x_ and _y_ with sizes 3 and 2 respectively looks like: + +```bash +tensor<float>(x[3],y[2]) +``` + +Example tensor with this type (representing a matrix): + +```bash +{{x:0,y:0}:1, {x:0,y:1}:2.1, + {x:1,y:0}:3, {x:1,y:1}:5, + {x:2,y:0}:7, {x:2,y:1}:11} +``` + +Note that the labels are indexes in the range _[0,dimension-size\>_ + +A tensor\<double\> with both mapped and indexed dimensions is _mixed_: + +```bash +tensor<double>(key{},x[2]) +``` + +Example: + +```bash +{{key:a,x:0}:10, {key:b,x:0}:2.7, + {key:a,x:1}:5.3, {key:b,x:1}:-7 } +``` + +## Tensor literal form + +The tensor literal form is used in: + +- Tensors in queries, see [defining query feature types](/en/ranking/ranking-expressions-features#query-feature-types) and [tensor user guide](/en/ranking/tensor-user-guide#querying-with-tensors) +- Constant tensors in [stateless model evaluation](/en/reference/ranking/model-files) +- Building tensors using the [Java Tensor API](https://javadoc.io/doc/com.yahoo.vespa/vespajlib/latest/com/yahoo/tensor/Tensor.html) + +The tensor literal form is _not_ a JSON format. When sent inside a JSON format (like when you POST a query), it should be passed as a string. + +### General literal form + +The general literal form is verbose and explicit, can represent any tensor and is as follows (EBNF): + +```bash +literal tensor = ( tensor-type-spec ":" )? "{" cells "}" ; +cells = | cell , { "," cell } ; +cell = "{" address "}:" scalar ; +address = | element, { "," element } ; +element = dimension ":" label ; +dimension = integer | identifier ; +label = integer | identifier | 'string' | "string" ; +identifier = ["A"-"Z","a"-"z","0"-"9","_","@"](["A"-"Z","a"-"z","0"-"9","_","@","$"])* +``` + +For query inputs, the type should be declared as an input in the ranking profile, so the type spec is usually skipped. + +#### General literal form examples: + +An empty tensor: + +```bash +{} +``` + +A single value tensor with a single mapped dimension _x_: + +```bash +{ {x:foo}:5.0 } +``` + +A tensor with multiple values and mapped dimensions _x_ and _y_: + +```bash +{ {x:foo, y:bar}:5.0, {x:foo, y:baz}:7.0 } +``` + +A tensor where type is specified explicitly with a single indexed dimension _x_ representing a vector: + +```bash +tensor<float>(x[3]):{ {x:0}:3.0, {x:1}:5.0, {x:2}:7.0 } +``` + +A tensor with a type using the default value type (double) and quoted labels: + +```bash +tensor(key{}):{ {key:'key.1'}:3.0, {key:'key 2'}:5.0, {key:"key's"}:7.0 } +``` + +### Indexed short form + +Tensors where all dimensions are indexed can be written as numbers wrapped in square brackets in _right dimension adjacent_ order. If the type isn't declared already, this form requires an explicit tensor type. Note: Dimensions should be alphabetically ordered. + +Brackets must be nested according to the structure of the type, where values in dimensions to the right are closer than dimensions on the left. For backwards compatibility (not supported in expressions), cell values may also be given in the same order as a flat array. + +#### Indexed short form examples: + +A float 1d tensor in indexed form: + +```bash +tensor<float>(x[3]):[3.0, 5.0, 7.0] +``` + +A matrix in indexed form. Since the values for the right-most dimension (y) are adjacent, the value 3 is here assigned to the cell \{x:0,y:2}: + +```bash +tensor<float>(x[2],y[3]):[[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]] +``` + +Deprecated: Since inner brackets can be omitted, the above is equivalent to + +```bash +tensor<float>(x[2],y[3]):[1.0, 2.0, 3.0, 4.0, 5.0, 6.0] +``` + +### Binary hex format + +Tensors representing binary data (indexed tensors with `int8` cell value type) may be represented using a hexadecimal representation, similar to the corresponding [JSON tensor feed format](/en/reference/schemas/document-json-format#tensor-hex-dump). If the tensor type is declared as `tensor<int8>(x[2],y[3])` input could be just + +```bash +0B22038405FF +``` + +which would be equivalent to: + +```bash +[[11, 34, 3], [-124, 5, -1]] +``` + +### Mapped short form + +Tensors with a single mapped dimension can be written by specifying just the label in that implicit dimension instead of a full address map. This form requires a type to be declared or explicitly specified. + +#### Map short form example: + +```bash +tensor<float>(key{}):{ key1:1.0, key2:2.0 } +``` + +### Mixed short form + +Tensors with a single mapped dimension and one or more indexed dimensions can be written by specifying the mapped dimension in the map short form and the values of each dense subspace on the indexed short form. This form requires a type to be known (declared) or specified. + +#### Mixed short form example: + +A map of matrices: + +```bash +tensor<float>(key{},x[2],y[3]):{ key1:[[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]], + key2:[[1.1, 2.1, 3.1], [4.1, 5.1, 6.1]] } +``` + +This may even be combined with a hexadecimal format for the dense subspace: + +```bash +tensor<int8>(key{},x[5]):{ key1: 0102030405, key2: fffefdfcfb } +``` + +Tensors with a multiple mapped dimensions may use an extended variant of the mixed short form, where labels are nested. Again note that the type should be declared with dimensions in alphabetic order, so the nesting will follow a consistent ordering. + +```bash +tensor(category{},key{},x[2],y[3]):{ + cat1:{key1:[[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]], + key2:[[1.1, 2.1, 3.1], [4.1, 5.1, 6.1]]}, + cat2:{key1:[[7.3, 8.3, 9.3], [7.0, 8.0, 9.0]], + key3:[[7.5, 8.5, 9.5], [7.9, 8.9, 9.9]]} +} +``` +the equivalent fully-specified literal form would be +```bash +tensor(category{},key{},x[2],y[3]):{{category:cat1, key:key1, x:0, y:0}: 1.0, + {category:cat1, key:key1, x:0, y:1}: 2.0, + {category:cat1, key:key1, x:0, y:2}: 3.0, + {category:cat1, key:key1, x:1, y:0}: 4.0, + {category:cat1, key:key1, x:1, y:1}: 5.0, + {category:cat1, key:key1, x:1, y:2}: 6.0, + {category:cat1, key:key2, x:0, y:0}: 1.1, + {category:cat1, key:key2, x:0, y:1}: 2.1, + {category:cat1, key:key2, x:0, y:2}: 3.1, + {category:cat1, key:key2, x:1, y:0}: 4.1, + {category:cat1, key:key2, x:1, y:1}: 5.1, + {category:cat1, key:key2, x:1, y:2}: 6.1, + {category:cat2, key:key1, x:0, y:0}: 7.3, + {category:cat2, key:key1, x:0, y:1}: 8.3, + {category:cat2, key:key1, x:0, y:2}: 9.3, + {category:cat2, key:key1, x:1, y:0}: 7.0, + {category:cat2, key:key1, x:1, y:1}: 8.0, + {category:cat2, key:key1, x:1, y:2}: 9.0, + {category:cat2, key:key3, x:0, y:0}: 7.5, + {category:cat2, key:key3, x:0, y:1}: 8.5, + {category:cat2, key:key3, x:0, y:2}: 9.5, + {category:cat2, key:key3, x:1, y:0}: 7.9, + {category:cat2, key:key3, x:1, y:1}: 8.9, + {category:cat2, key:key3, x:1, y:2}: 9.9} +``` + +## Tensor functions + +Tensor functions are listed in the [expressions](/en/reference/ranking/ranking-expressions#tensor-functions) documentation. + +## Tensor rank features + +The following rank features can be used to refer to or create tensors in ranking expressions. The tensors can come from the document, the query or a constant in the application package: + +- [attribute(tensor\_attribute)](/en/reference/ranking/rank-features#attribute(name)) +- [query(tensor\_feature)](/en/reference/ranking/rank-features#query(value)) +- [constant(tensor\_constant)](/en/reference/ranking/rank-features#constant(name)) +- [tensorFromWeightedSet(source, dimension)](/en/reference/ranking/rank-features#tensorFromWeightedSet(source,dimension)) +- [tensorFromLabels(source, dimension)](/en/reference/ranking/rank-features#tensorFromLabels(source,dimension)) +- [tensorFromStructs(attribute,key,value,type)](/en/reference/ranking/rank-features#tensorFromStructs(attribute,key,value,type)) + +Use the following reference documentation on how use tensors in documents: + +- [Tensor field in schema](/en/reference/schemas/schemas#tensor) +- [Document JSON Format](/en/reference/schemas/document-json-format) \ No newline at end of file diff --git a/en/reference/release-notes/vespa7.html b/en/reference/release-notes/vespa7.html deleted file mode 100644 index 5c2762b024..0000000000 --- a/en/reference/release-notes/vespa7.html +++ /dev/null @@ -1,469 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Vespa 7 Release Notes" -redirect_from: -- /en/vespa7-release-notes.html ---- - -<style> - th, td { - width: 50%; - } -</style> - -<p>These notes documents the changes between Vespa major versions 6 and 7. - As documented in <a href="../../learn/releases.html#versions">Vespa versions</a>, - major versions are used to mark breaking compatibility, - not to release significant new functionality (which instead happens on minor versions). - However, even on major versions, compatibility is broken only in two specific ways: <b>Changes to defaults</b> means that - applications may need to set some option explicitly to preserve earlier behavior, and <b>removal of deprecated functionality</b> - means that applications <i>which use functionality that has earlier been deprecated</i> need to change to keep working.</p> - -<p>Most deprecated functionality causes warning during compilation (Java API deprecations) or deployment - (application package deprecations), however with web service APIs there is no way to emit deprecation warnings, - and we have to rely on marking these as deprecated in the documentation.</p> - -<p>Given this, application owners need to do 3 tasks to be compatible with Vespa 7:</p> - - <ul> - <li>Review whether <a href="#changed-defaults">changes to defaults</a> requires additional settings in the application - (<b>note that this is likely</b> on changing from 6 to 7 due to the changes to tokenization and stemming). - <li>Make sure there are no deprecation warnings on compilation and deployment on Vespa 6. - <li>Review the list of removed web service APIs and API parameters and make sure these are not used by clients of - the application. - </ul> - -<p>As Vespa 7 does not introduce new functionality, it is as safe and mature as the versions of Vespa 6 preceding it. - Upon release of Vespa 7, no further releases will be made of Vespa 6 for any reason.</p> - - - -<h2 id="changes">Changes</h2> - -<p>The following sections lists the changes on moving from Vespa 6 to Vespa 7 which must be reviewed by applications.</p> - - -<h3 id="changed-defaults">Changed defaults</h3> - -<p>The following defaults have changed:</p> - -<table class="table"> - <thead> - <tr> - <th>Change</th><th>Configuration required to avoid change on Vespa 7</th> - </tr> - </thead> - <tbody> - - <tr> - <td><code>stemming: shortest</code> changed to <code>stemming: best</code></td> - <td>Add <a href="../schemas/schemas.html#stemming">stemming: shortest</a> - to the <code>schema</code> block of all schemas.</td> - </tr> - - <tr> - <td>Default linguistics component changed from SimpleLinguistics to OpenNlpLinguistics, - including language detection using Optimaize turned on by default.</td> - <td>Configure <code>com.yahoo.language.simple.SimpleLinguistics</code> as a component in services.xml as described in - <a href="../../linguistics/linguistics.html">linguistics in Vespa</a> - </td> - </tr> - - <tr> - <td>The default format accepted by the Java HTTP client - is changed from XML to <a href="../schemas/document-json-format.html">JSON</a></td> - <td>To keep using XML: - <ul> - <li><b>Java API</b>: When calling <code>FeedClientFactory.create(sessionParams, ...)</code>, pass a <code>SessionParams</code> - instance which has a <code>FeedParams</code> instance which have <code>dataFormat</code> set to - <code>FeedParams.DataFormat.XML_UTF8</code></li> - <li><b>Command line</b>: Pass the <code>--xmloutput</code> option.</li> - </ul> - </td> - </tr> - - <tr> - <td>Query timeout changed from 5000 ms to 500 ms. - <td>Set the <a href="../api/query.html#timeout">timeout</a> parameter explicitly in requests or query profiles. - </tr> - - <tr> - <td><a href="../api/query.html#ranking.softtimeout.enable">ranking.softtimeout.enable</a> changed to default true</td> - <td>Set to <code>false</code> in requests or a query profile.</td> - </tr> - - <tr> - <td>The default access log format is changed to <a href="../../operations/access-logging.html">JSON</a>.</td> - <td>To keep the old proprietary format, set accesslog type=vespa in services.xml - as described in <a href="../applications/services/container.html#accesslog">the accesslog reference</a>. - </tr> - - <tr> - <td>Default return format in vespa-visit and vespa-get is changed to JSON - <td>To get XML output specify the --xmloutput method - </tr> - - </tbody> -</table> - - -<h3 id="jdk-version">JDK version</h3> - -<p>Java components must be rebuilt with JDK 11 for the Vespa bundle-plugin to generate the -correct set of imported packages for your OSGi bundles.</p> - - -<h3 id="removed-java-apis">Removed Java APIs</h3> - -<p>Classes and methods that were marked as deprecated in Vespa 6 are removed. -If you get deprecation warnings for Vespa APIs when building your - application, they must be fixed before migrating to Vespa 7.</p> - - -<h3 id="container-runtime">Container Runtime Environment</h3> -<p> -The following maven artifacts are no longer provided runtime: -</p> - -<ul> - <li>commons-codec:commons-codec</li> - <li>org.apache.httpcomponents:httpclient</li> - <li>org.apache.httpcomponents:httpcore</li> -</ul> - -<p> -If you need any of these dependencies, they must be embedded in your bundle by adding them -in scope 'compile' in pom.xml. -</p> - - -<h3 id="removed-http-apis">Removed HTTP APIs</h3> - -<p>The following HTTP APIs are removed:</p> - -<table class="table"> - <thead> - <tr> - <th>Name</th><th>Replacement</th> - </tr> - </thead> - <tbody> - - <tr> - <td>Legacy HTTP apis for document feeding: - <ul> - <li>/feed</li> - <li>/remove</li> - <li>/removelocation</li> - <li>/get</li> - <li>/visit</li> - <li>/document</li> - </ul> - </td> - <td> - The <a href="../api/document-v1.html">/document/v1/</a> web service API, - or (for high throughput) the vespa-http-client. - </td> - </tr> - </tbody> -</table> - - - -<h3 id="removed-http-api-parameters">Removed HTTP API parameters</h3> - -<p>The following HTTP API parameters are removed</p> - -<table class="table"> - <thead> - <tr> - <th>Name</th><th>Replacement</th> - </tr> - </thead> - <tbody> - <tr> - <td>The <code>defidx</code> parameter in the search API</td> - <td>Use a custom searcher if this functionality is needed.</td> - </tr> - </tbody> -</table> - - -<h3 id="removed-command-line-tools">Removed command line tools</h3> - -<p>The following command line tools are removed:</p> - -<table class="table"> - <thead> - <tr> - <th>Name</th><th>Replacement</th> - </tr> - </thead> - <tbody> - <tr> - <td>Vespa spooler</td> - <td>Custom client using the Java HTTP client</td> - </tr> - </tbody> -</table> - - -<h3 id="removed-settings-from-schemas">Removed settings from schemas</h3> - -<p>The following settings are removed from <a href="../schemas/schemas.html">schemas</a>:</p> - -<table class="table"> - <thead> - <tr> - <th>Name</th><th>Replacement</th> - </tr> - </thead> - <tbody> - <tr> - <td>header</td> - <td>None. This setting doesn't have any effect</td> - </tr> - <tr> - <td>body</td> - <td>None. This setting doesn't have any effect</td> - </tr> - </tbody> -</table> - - -<h3 id="removed-constructs-from-services-xml">Removed constructs from services.xml</h3> - -<p>The following tags and attributes are removed from services.xml:</p> - -<table class="table"> - <thead> - <tr> - <th>Name</th><th>Replacement</th> - </tr> - </thead> - <tbody> - <tr> - <td>‘rotationScheme’ attribute in <container><accesslog></td> - <td>None, rotation scheme ‘date’ will always be used</td> - </tr> - <tr> - <td><container><filter> tag</td> - <td><container><http><filtering></td> - </tr> - </tbody> -</table> - - - -<h3 id="removed-metrics">Removed metrics</h3> - -<p>The following metrics are removed:</p> - -<table class="table"> - <thead> - <tr> - <th>Name</th><th>Replacement</th> - </tr> - </thead> - <tbody> - <tr> - <td>free/used/totalMemoryBytes</td> - <td>mem.heap.free/used/total</td> - </tr> - <tr> - <td>http.in.bytes</td> - <td>serverBytesReceived</td> - </tr> - <tr> - <td>http.out.bytes</td> - <td>serverBytesSent</td> - </tr> - <tr> - <td>http.requests</td> - <td>serverNumRequests</td> - </tr> - <tr> - <td>http.latency</td> - <td>serverTotalSuccessfulResponseLatency</td> - </tr> - <tr> - <td>http.out.firstbytetime</td> - <td>serverTimeToFirstByte</td> - </tr> - <tr> - <td>proc.uptime</td> - <td>serverStartedMillis</td> - </tr> - <tr> - <td>proton.*</td> - <td>content.proton.* (note that metrics might have different structure and names in new namespace)</td> - </tr> - <tr> - <td>vds.filestor.spi.*</td> - <td>vds.filestor.alldisks.allthreads.*</td> - </tr> - </tbody> -</table> - - -<h3 id="empty-fields">Empty fields</h3> - -Fields containing no value will not be included in responses on Vespa 7. - -<h3 id="uri-encoding">Allowed characters in request URIs</h3> - -Vespa 6 allowed some special characters in raw form in the query component of request URIs. -Vespa 7 requires these characters to be properly percent-encoded (RFC 2396). - -<table class="table"> - <thead> - <tr> - <th>Character</th><th>Percent-encoding</th> - </tr> - </thead> - <tbody> - <tr> - <td>\</td> - <td>%5C</td> - </tr> - <tr> - <td>^</td> - <td>%5E</td> - </tr> - <tr> - <td>`</td> - <td>%60</td> - </tr> - <tr> - <td>{</td> - <td>%7B</td> - </tr> - <tr> - <td>|</td> - <td>%7C</td> - </tr> - <tr> - <td>}</td> - <td>%7D</td> - </tr> - </tbody> -</table> - - -<h3 id="changes-to-the-default-json-result-format">Changes to the default JSON result format</h3> - -<p>The content of fields of type <b>position</b> in the default JSON query result format was rendered as XML -on Vespa 6 but is rendered as JSON.</p> - -<p>Specifically, the content of a position field was rendered as a string like</p> - -<code> -<position x="-121996000" y="37401000" latlong="N37.401000;W121.996000"/> -</code> - -<p>but is now instead rendered as a JSON map:</p> - -<pre><code> -{ - "y": 37401000, - "x": -121996000, - "latlong": "N37.401000;W121.996000" -} -</code></pre> - - -<h3 id="renamed-metrics">Renamed metrics</h3> - -<p>The following metrics are renamed:</p> - -<table class="table"> - <thead> - <tr> - <th>Old Name</th><th>New Name</th> - </tr> - </thead> - <tbody> - <tr><td>95p_query_latency</td><td>query_latency.95percentile</td></tr> - <tr><td>99p_query_latency</td><td>query_latency.99percentile</td></tr> - <tr><td>active_queries</td><td>active_queries.average</td></tr> - <tr><td>athenz-tenant-cert.expiry.seconds</td><td>athenz-tenant-cert.expiry.seconds.last</td></tr> - <tr><td>bytes</td><td>vds.datastored.alldisks.bytes.average</td></tr> - <tr><td>configserver.cacheChecksumElems</td><td>configserver.cacheChecksumElems.last</td></tr> - <tr><td>configserver.cacheConfigElems</td><td>configserver.cacheConfigElems.last</td></tr> - <tr><td>configserver.delayedResponses</td><td>configserver.delayedResponses.count</td></tr> - <tr><td>configserver.failedRequests</td><td>configserver.failedRequests.count</td></tr> - <tr><td>configserver.hosts</td><td>configserver.hosts.last</td></tr> - <tr><td>configserver.latency</td><td>configserver.latency.average</td></tr> - <tr><td>configserver.requests</td><td>configserver.requests.count</td></tr> - <tr><td>configserver.sessionChangeErrors</td><td>configserver.sessionChangeErrors.count</td></tr> - <tr><td>configserver.zkAvgLatency</td><td>configserver.zkAvgLatency.last</td></tr> - <tr><td>configserver.zkConnections</td><td>configserver.zkConnections.last</td></tr> - <tr><td>configserver.zkMaxLatency</td><td>configserver.zkMaxLatency.last</td></tr> - <tr><td>configserver.zkOutstandingRequests</td><td>configserver.zkOutstandingRequests.last</td></tr> - <tr><td>configserver.zkZNodes</td><td>configserver.zkZNodes.last</td></tr> - <tr><td>content.cluster-controller.cluster-state-change.count</td><td>cluster-controller.cluster-state-change.count</td></tr> - <tr><td>content.proton.memoryusage.max</td><td>content.proton.documentdb.memory_usage.allocated_bytes.max</td></tr> - <tr><td>content.proton.transport.docsum.latency.average</td><td>content.proton.docsum.latency.average</td></tr> - <tr><td>degraded_queries</td><td>degraded_queries.rate</td></tr> - <tr><td>deletefailed</td><td>vds.idealstate.delete_bucket.done_failed.rate</td></tr> - <tr><td>deleteok</td><td>vds.idealstate.delete_bucket.done_ok.rate</td></tr> - <tr><td>deletepending</td><td>vds.idealstate.delete_bucket.pending.average</td></tr> - <tr><td>diskqueuesize</td><td>vds.filestor.alldisks.queuesize.average</td></tr> - <tr><td>diskqueuewait</td><td>vds.filestor.alldisks.averagequeuewait.sum.average</td></tr> - <tr><td>diskusage</td><td>content.proton.documentdb.disk_usage.last</td></tr> - <tr><td>docs</td><td>vds.datastored.alldisks.docs.average</td></tr> - <tr><td>document_requests</td><td>content.proton.docsum.docs.rate</td></tr> - <tr><td>documents_active</td><td>content.proton.documentdb.documents.active.last</td></tr> - <tr><td>documents_inmemory</td><td>content.proton.documentdb.index.docs_in_memory.last</td></tr> - <tr><td>documents_processed</td><td>documents_processed.rate</td></tr> - <tr><td>documents_ready</td><td>content.proton.documentdb.documents.ready.last</td></tr> - <tr><td>documents_removed</td><td>content.proton.documentdb.documents.removed.last</td></tr> - <tr><td>documents_total</td><td>content.proton.documentdb.documents.total.last</td></tr> - <tr><td>empty_results</td><td>empty_results.rate</td></tr> - <tr><td>error.backend_communication_error</td><td>error.backend_communication_error.rate</td></tr> - <tr><td>error.backends_oos</td><td>error.backends_oos.rate</td></tr> - <tr><td>error.empty_document_summaries</td><td>error.empty_document_summaries.rate</td></tr> - <tr><td>error.internal_server_error</td><td>error.internal_server_error.rate</td></tr> - <tr><td>error.invalid_query_parameter</td><td>error.invalid_query_parameter.rate</td></tr> - <tr><td>error.invalid_query_transformation</td><td>error.invalid_query_transformation.rate</td></tr> - <tr><td>error.misconfigured_server</td><td>error.misconfigured_server.rate</td></tr> - <tr><td>error.plugin_failure</td><td>error.plugin_failure.rate</td></tr> - <tr><td>error.result_with_errors</td><td>error.result_with_errors.rate</td></tr> - <tr><td>error.timeout</td><td>error.timeout.rate</td></tr> - <tr><td>error.unhandled_exception</td><td>error.unhandled_exception.rate</td></tr> - <tr><td>error.unspecified</td><td>error.unspecified.rate</td></tr> - <tr><td>failed_queries</td><td>failed_queries.rate</td></tr> - <tr><td>handled.requests</td><td>handled.requests.count</td></tr> - <tr><td>hits_per_query</td><td>hits_per_query.average</td></tr> - <tr><td>joinfailed</td><td>vds.idealstate.join_bucket.done_failed.rate</td></tr> - <tr><td>joinok</td><td>vds.idealstate.join_bucket.done_ok.rate</td></tr> - <tr><td>joinpending</td><td>vds.idealstate.join_bucket.pending.average</td></tr> - <tr><td>logd.processed.lines</td><td>logd.processed.lines.count</td></tr> - <tr><td>max_query_latency</td><td>query_latency.max</td></tr> - <tr><td>mean_query_latency</td><td>query_latency.average</td></tr> - <tr><td>mergefailed</td><td>vds.idealstate.merge_bucket.done_failed.rate</td></tr> - <tr><td>mergeok</td><td>vds.idealstate.merge_bucket.done_ok.rate</td></tr> - <tr><td>mergepending</td><td>vds.idealstate.merge_bucket.pending.average</td></tr> - <tr><td>peak_qps</td><td>peak_qps.max</td></tr> - <tr><td>queries</td><td>queries.rate</td></tr> - <tr><td>query_latency</td><td>content.proton.transport.query.latency.average</td></tr> - <tr><td>query_requests</td><td>content.proton.transport.query.count.rate</td></tr> - <tr><td>search_connections</td><td>search_connections.average</td></tr> - <tr><td>sentinel.uptime</td><td>sentinel.uptime.last</td></tr> - <tr><td>slobrok.heartbeats.failed</td><td>slobrok.heartbeats.failed.count</td></tr> - <tr><td>splitfailed</td><td>vds.idealstate.split_bucket.done_failed.rate</td></tr> - <tr><td>splitok</td><td>vds.idealstate.split_bucket.done_ok.rate</td></tr> - <tr><td>splitpending</td><td>vds.idealstate.split_bucket.pending.average</td></tr> - <tr><td>totalhits_per_query</td><td>totalhits_per_query.average</td></tr> - <tr><td>visit</td><td>vds.visitor.allthreads.created.sum.rate</td></tr> - <tr><td>visitorlifetime</td><td>vds.visitor.allthreads.averagevisitorlifetime.sum.average</td></tr> - <tr><td>visitorqueuewait</td><td>vds.visitor.allthreads.averagequeuewait.sum.average</td></tr> - </tbody> -</table> - - -<h3 id="other-changes">Other changes</h3> - -<p>Vespa will not any longer implicitly load the "search" components" in containers which load the "document-api" components. -If your application depends on "search" functionality in a container specifying the <code><document-api></code> tag in services.xml, -make sure this container also specifies the <code><search></code> tag.</p> diff --git a/en/reference/release-notes/vespa7.mdx b/en/reference/release-notes/vespa7.mdx new file mode 100644 index 0000000000..696ec15782 --- /dev/null +++ b/en/reference/release-notes/vespa7.mdx @@ -0,0 +1,615 @@ +--- +title: "Vespa 7 Release Notes" +sidebarTitle: "Vespa 7" +--- + +These notes documents the changes between Vespa major versions 6 and 7. As documented in [Vespa versions](/en/learn/releases#versions), major versions are used to mark breaking compatibility, not to release significant new functionality (which instead happens on minor versions). However, even on major versions, compatibility is broken only in two specific ways: **Changes to defaults** means that applications may need to set some option explicitly to preserve earlier behavior, and **removal of deprecated functionality** means that applications *which use functionality that has earlier been deprecated* need to change to keep working. + +Most deprecated functionality causes warning during compilation (Java API deprecations) or deployment (application package deprecations), however with web service APIs there is no way to emit deprecation warnings, and we have to rely on marking these as deprecated in the documentation. + +Given this, application owners need to do 3 tasks to be compatible with Vespa 7: + +- Review whether [changes to defaults](#changed-defaults) requires additional settings in the application (**note that this is likely** on changing from 6 to 7 due to the changes to tokenization and stemming). +- Make sure there are no deprecation warnings on compilation and deployment on Vespa 6. +- Review the list of removed web service APIs and API parameters and make sure these are not used by clients of the application. + +As Vespa 7 does not introduce new functionality, it is as safe and mature as the versions of Vespa 6 preceding it. Upon release of Vespa 7, no further releases will be made of Vespa 6 for any reason. + +## Changes + +The following sections lists the changes on moving from Vespa 6 to Vespa 7 which must be reviewed by applications. + +### Changed defaults + +The following defaults have changed: + +<table className="vespa-table"> + <thead> + <tr> + <th>Change</th> + <th>Configuration required to avoid change on Vespa 7</th> + </tr> + </thead> + <tbody> + <tr> + <td><code>{`stemming: shortest`}</code> changed to <code>{`stemming: best`}</code></td> + <td>Add <a href="/en/reference/schemas/schemas#stemming" aria-label="stemming: shortest">stemming: shortest</a> to the <code>{`schema`}</code> block of all schemas.</td> + </tr> + <tr> + <td>Default linguistics component changed from SimpleLinguistics to OpenNlpLinguistics, including language detection using Optimaize turned on by default.</td> + <td>Configure <code>{`com.yahoo.language.simple.SimpleLinguistics`}</code> as a component in services.xml as described in <a href="/en/linguistics/linguistics" aria-label="linguistics in Vespa">linguistics in Vespa</a></td> + </tr> + <tr> + <td>The default format accepted by the Java HTTP client is changed from XML to <a href="/en/reference/schemas/document-json-format" aria-label="JSON">JSON</a></td> + <td>To keep using XML: <br/><br/>• **Java API**: When calling <code>{`FeedClientFactory.create(sessionParams, ...)`}</code>, pass a <code>{`SessionParams`}</code> instance which has a <code>{`FeedParams`}</code> instance which have <code>{`dataFormat`}</code> set to <code>{`FeedParams.DataFormat.XML_UTF8`}</code> <br/>• **Command line**: Pass the <code>{`--xmloutput`}</code> option.</td> + </tr> + <tr> + <td>Query timeout changed from 5000 ms to 500 ms.</td> + <td>Set the <a href="/en/reference/api/query#timeout" aria-label="timeout">timeout</a> parameter explicitly in requests or query profiles.</td> + </tr> + <tr> + <td><a href="/en/reference/api/query#ranking.softtimeout.enable" aria-label="ranking.softtimeout.enable">ranking.softtimeout.enable</a> changed to default true</td> + <td>Set to <code>{`false`}</code> in requests or a query profile.</td> + </tr> + <tr> + <td>The default access log format is changed to <a href="/en/operations/access-logging" aria-label="JSON">JSON</a>.</td> + <td>To keep the old proprietary format, set accesslog type=vespa in services.xml as described in <a href="/en/reference/applications/services/container#accesslog" aria-label="the accesslog reference">the accesslog reference</a>.</td> + </tr> + <tr> + <td>Default return format in vespa-visit and vespa-get is changed to JSON</td> + <td>To get XML output specify the --xmloutput method</td> + </tr> + </tbody> +</table> + +### JDK version + +Java components must be rebuilt with JDK 11 for the Vespa bundle-plugin to generate the correct set of imported packages for your OSGi bundles. + +### Removed Java APIs + +Classes and methods that were marked as deprecated in Vespa 6 are removed. If you get deprecation warnings for Vespa APIs when building your application, they must be fixed before migrating to Vespa 7. + +### Container Runtime Environment + +The following maven artifacts are no longer provided runtime: + +- commons-codec:commons-codec +- org.apache.httpcomponents:httpclient +- org.apache.httpcomponents:httpcore + +If you need any of these dependencies, they must be embedded in your bundle by adding them in scope 'compile' in pom.xml. + +### Removed HTTP APIs + +The following HTTP APIs are removed: + +<table className="vespa-table"> + <thead> + <tr> + <th>Name</th> + <th>Replacement</th> + </tr> + </thead> + <tbody> + <tr> + <td>Legacy HTTP apis for document feeding:<br/>• /feed<br/>• /remove<br/>• /removelocation<br/>• /get<br/>• /visit<br/>• /document</td> + <td>The <a href="/en/reference/api/document-v1" aria-label="/document/v1/">/document/v1/</a> web service API, or (for high throughput) the vespa-http-client.</td> + </tr> + </tbody> +</table> + +### Removed HTTP API parameters + +The following HTTP API parameters are removed + +<table className="vespa-table"> + <thead> + <tr> + <th>Name</th> + <th>Replacement</th> + </tr> + </thead> + <tbody> + <tr> + <td>The <code>{`defidx`}</code> parameter in the search API</td> + <td>Use a custom searcher if this functionality is needed.</td> + </tr> + </tbody> +</table> + +### Removed command line tools + +The following command line tools are removed: + +<table className="vespa-table"> + <thead> + <tr> + <th>Name</th> + <th>Replacement</th> + </tr> + </thead> + <tbody> + <tr> + <td>Vespa spooler</td> + <td>Custom client using the Java HTTP client</td> + </tr> + </tbody> +</table> + +### Removed settings from schemas + +The following settings are removed from [schemas](/en/reference/schemas/schemas): + +<table className="vespa-table"> + <thead> + <tr> + <th>Name</th> + <th>Replacement</th> + </tr> + </thead> + <tbody> + <tr> + <td>header</td> + <td>None. This setting doesn't have any effect</td> + </tr> + <tr> + <td>body</td> + <td>None. This setting doesn't have any effect</td> + </tr> + </tbody> +</table> + +### Removed constructs from services.xml + +The following tags and attributes are removed from services.xml: + +<table className="vespa-table"> + <thead> + <tr> + <th>Name</th> + <th>Replacement</th> + </tr> + </thead> + <tbody> + <tr> + <td>‘rotationScheme’ attribute in <code>{`<container><accesslog>`}</code></td> + <td>None, rotation scheme ‘date’ will always be used</td> + </tr> + <tr> + <td><code>{`<container><filter>`}</code> tag</td> + <td><code>{`<container><http><filtering>`}</code></td> + </tr> + </tbody> +</table> + +### Removed metrics + +The following metrics are removed: + +<table className="vespa-table"> + <thead> + <tr> + <th>Name</th> + <th>Replacement</th> + </tr> + </thead> + <tbody> + <tr> + <td>free/used/totalMemoryBytes</td> + <td>mem.heap.free/used/total</td> + </tr> + <tr> + <td>http.in.bytes</td> + <td>serverBytesReceived</td> + </tr> + <tr> + <td>http.out.bytes</td> + <td>serverBytesSent</td> + </tr> + <tr> + <td>http.requests</td> + <td>serverNumRequests</td> + </tr> + <tr> + <td>http.latency</td> + <td>serverTotalSuccessfulResponseLatency</td> + </tr> + <tr> + <td>http.out.firstbytetime</td> + <td>serverTimeToFirstByte</td> + </tr> + <tr> + <td>proc.uptime</td> + <td>serverStartedMillis</td> + </tr> + <tr> + <td>proton.*</td> + <td>content.proton.* (note that metrics might have different structure and names in new namespace)</td> + </tr> + <tr> + <td>vds.filestor.spi.*</td> + <td>vds.filestor.alldisks.allthreads.*</td> + </tr> + </tbody> +</table> + +### Empty fields + +Fields containing no value will not be included in responses on Vespa 7. + +### Allowed characters in request URIs + +Vespa 6 allowed some special characters in raw form in the query component of request URIs. Vespa 7 requires these characters to be properly percent-encoded (RFC 2396). + +<table className="vespa-table"> + <thead> + <tr> + <th>Character</th> + <th>Percent-encoding</th> + </tr> + </thead> + <tbody> + <tr> + <td><code>{`\\`}</code></td> + <td><code>{`%5C`}</code></td> + </tr> + <tr> + <td><code>{`^`}</code></td> + <td><code>{`%5E`}</code></td> + </tr> + <tr> + <td><code>{`\\`}</code></td> + <td><code>{`%60`}</code></td> + </tr> + <tr> + <td><code>{`{`}</code></td> + <td><code>{`%7B`}</code></td> + </tr> + <tr> + <td><code>{`|`}</code></td> + <td><code>{`%7C`}</code></td> + </tr> + <tr> + <td><code>{`}`}</code></td> + <td><code>{`%7D`}</code></td> + </tr> + </tbody> +</table> + +### Changes to the default JSON result format + +The content of fields of type **position** in the default JSON query result format was rendered as XML on Vespa 6 but is rendered as JSON. + +Specifically, the content of a position field was rendered as a string like + +` <position x="-121996000" y="37401000" latlong="N37.401000;W121.996000"/> ` + +but is now instead rendered as a JSON map: + +```json +{ + "y": 37401000, + "x": -121996000, + "latlong": "N37.401000;W121.996000" +} +``` + +### Renamed metrics + +The following metrics are renamed: + +<table className="vespa-table"> + <thead> + <tr> + <th>Old Name</th> + <th>New Name</th> + </tr> + </thead> + <tbody> + <tr> + <td>95p_query_latency</td> + <td>query_latency.95percentile</td> + </tr> + <tr> + <td>99p_query_latency</td> + <td>query_latency.99percentile</td> + </tr> + <tr> + <td>active_queries</td> + <td>active_queries.average</td> + </tr> + <tr> + <td>athenz-tenant-cert.expiry.seconds</td> + <td>athenz-tenant-cert.expiry.seconds.last</td> + </tr> + <tr> + <td>bytes</td> + <td>vds.datastored.alldisks.bytes.average</td> + </tr> + <tr> + <td>configserver.cacheChecksumElems</td> + <td>configserver.cacheChecksumElems.last</td> + </tr> + <tr> + <td>configserver.cacheConfigElems</td> + <td>configserver.cacheConfigElems.last</td> + </tr> + <tr> + <td>configserver.delayedResponses</td> + <td>configserver.delayedResponses.count</td> + </tr> + <tr> + <td>configserver.failedRequests</td> + <td>configserver.failedRequests.count</td> + </tr> + <tr> + <td>configserver.hosts</td> + <td>configserver.hosts.last</td> + </tr> + <tr> + <td>configserver.latency</td> + <td>configserver.latency.average</td> + </tr> + <tr> + <td>configserver.requests</td> + <td>configserver.requests.count</td> + </tr> + <tr> + <td>configserver.sessionChangeErrors</td> + <td>configserver.sessionChangeErrors.count</td> + </tr> + <tr> + <td>configserver.zkAvgLatency</td> + <td>configserver.zkAvgLatency.last</td> + </tr> + <tr> + <td>configserver.zkConnections</td> + <td>configserver.zkConnections.last</td> + </tr> + <tr> + <td>configserver.zkMaxLatency</td> + <td>configserver.zkMaxLatency.last</td> + </tr> + <tr> + <td>configserver.zkOutstandingRequests</td> + <td>configserver.zkOutstandingRequests.last</td> + </tr> + <tr> + <td>configserver.zkZNodes</td> + <td>configserver.zkZNodes.last</td> + </tr> + <tr> + <td>content.cluster-controller.cluster-state-change.count</td> + <td>cluster-controller.cluster-state-change.count</td> + </tr> + <tr> + <td>content.proton.memoryusage.max</td> + <td>content.proton.documentdb.memory_usage.allocated_bytes.max</td> + </tr> + <tr> + <td>content.proton.transport.docsum.latency.average</td> + <td>content.proton.docsum.latency.average</td> + </tr> + <tr> + <td>degraded_queries</td> + <td>degraded_queries.rate</td> + </tr> + <tr> + <td>deletefailed</td> + <td>vds.idealstate.delete_bucket.done_failed.rate</td> + </tr> + <tr> + <td>deleteok</td> + <td>vds.idealstate.delete_bucket.done_ok.rate</td> + </tr> + <tr> + <td>deletepending</td> + <td>vds.idealstate.delete_bucket.pending.average</td> + </tr> + <tr> + <td>diskqueuesize</td> + <td>vds.filestor.alldisks.queuesize.average</td> + </tr> + <tr> + <td>diskqueuewait</td> + <td>vds.filestor.alldisks.averagequeuewait.sum.average</td> + </tr> + <tr> + <td>diskusage</td> + <td>content.proton.documentdb.disk_usage.last</td> + </tr> + <tr> + <td>docs</td> + <td>vds.datastored.alldisks.docs.average</td> + </tr> + <tr> + <td>document_requests</td> + <td>content.proton.docsum.docs.rate</td> + </tr> + <tr> + <td>documents_active</td> + <td>content.proton.documentdb.documents.active.last</td> + </tr> + <tr> + <td>documents_inmemory</td> + <td>content.proton.documentdb.index.docs_in_memory.last</td> + </tr> + <tr> + <td>documents_processed</td> + <td>documents_processed.rate</td> + </tr> + <tr> + <td>documents_ready</td> + <td>content.proton.documentdb.documents.ready.last</td> + </tr> + <tr> + <td>documents_removed</td> + <td>content.proton.documentdb.documents.removed.last</td> + </tr> + <tr> + <td>documents_total</td> + <td>content.proton.documentdb.documents.total.last</td> + </tr> + <tr> + <td>empty_results</td> + <td>empty_results.rate</td> + </tr> + <tr> + <td>error.backend_communication_error</td> + <td>error.backend_communication_error.rate</td> + </tr> + <tr> + <td>error.backends_oos</td> + <td>error.backends_oos.rate</td> + </tr> + <tr> + <td>error.empty_document_summaries</td> + <td>error.empty_document_summaries.rate</td> + </tr> + <tr> + <td>error.internal_server_error</td> + <td>error.internal_server_error.rate</td> + </tr> + <tr> + <td>error.invalid_query_parameter</td> + <td>error.invalid_query_parameter.rate</td> + </tr> + <tr> + <td>error.invalid_query_transformation</td> + <td>error.invalid_query_transformation.rate</td> + </tr> + <tr> + <td>error.misconfigured_server</td> + <td>error.misconfigured_server.rate</td> + </tr> + <tr> + <td>error.plugin_failure</td> + <td>error.plugin_failure.rate</td> + </tr> + <tr> + <td>error.result_with_errors</td> + <td>error.result_with_errors.rate</td> + </tr> + <tr> + <td>error.timeout</td> + <td>error.timeout.rate</td> + </tr> + <tr> + <td>error.unhandled_exception</td> + <td>error.unhandled_exception.rate</td> + </tr> + <tr> + <td>error.unspecified</td> + <td>error.unspecified.rate</td> + </tr> + <tr> + <td>failed_queries</td> + <td>failed_queries.rate</td> + </tr> + <tr> + <td>handled.requests</td> + <td>handled.requests.count</td> + </tr> + <tr> + <td>hits_per_query</td> + <td>hits_per_query.average</td> + </tr> + <tr> + <td>joinfailed</td> + <td>vds.idealstate.join_bucket.done_failed.rate</td> + </tr> + <tr> + <td>joinok</td> + <td>vds.idealstate.join_bucket.done_ok.rate</td> + </tr> + <tr> + <td>joinpending</td> + <td>vds.idealstate.join_bucket.pending.average</td> + </tr> + <tr> + <td>logd.processed.lines</td> + <td>logd.processed.lines.count</td> + </tr> + <tr> + <td>max_query_latency</td> + <td>query_latency.max</td> + </tr> + <tr> + <td>mean_query_latency</td> + <td>query_latency.average</td> + </tr> + <tr> + <td>mergefailed</td> + <td>vds.idealstate.merge_bucket.done_failed.rate</td> + </tr> + <tr> + <td>mergeok</td> + <td>vds.idealstate.merge_bucket.done_ok.rate</td> + </tr> + <tr> + <td>mergepending</td> + <td>vds.idealstate.merge_bucket.pending.average</td> + </tr> + <tr> + <td>peak_qps</td> + <td>peak_qps.max</td> + </tr> + <tr> + <td>queries</td> + <td>queries.rate</td> + </tr> + <tr> + <td>query_latency</td> + <td>content.proton.transport.query.latency.average</td> + </tr> + <tr> + <td>query_requests</td> + <td>content.proton.transport.query.count.rate</td> + </tr> + <tr> + <td>search_connections</td> + <td>search_connections.average</td> + </tr> + <tr> + <td>sentinel.uptime</td> + <td>sentinel.uptime.last</td> + </tr> + <tr> + <td>slobrok.heartbeats.failed</td> + <td>slobrok.heartbeats.failed.count</td> + </tr> + <tr> + <td>splitfailed</td> + <td>vds.idealstate.split_bucket.done_failed.rate</td> + </tr> + <tr> + <td>splitok</td> + <td>vds.idealstate.split_bucket.done_ok.rate</td> + </tr> + <tr> + <td>splitpending</td> + <td>vds.idealstate.split_bucket.pending.average</td> + </tr> + <tr> + <td>totalhits_per_query</td> + <td>totalhits_per_query.average</td> + </tr> + <tr> + <td>visit</td> + <td>vds.visitor.allthreads.created.sum.rate</td> + </tr> + <tr> + <td>visitorlifetime</td> + <td>vds.visitor.allthreads.averagevisitorlifetime.sum.average</td> + </tr> + <tr> + <td>visitorqueuewait</td> + <td>vds.visitor.allthreads.averagequeuewait.sum.average</td> + </tr> + </tbody> +</table> + +### Other changes + +Vespa will not any longer implicitly load the "search" components" in containers which load the "document-api" components. If your application depends on "search" functionality in a container specifying the `<document-api>` tag in services.xml, make sure this container also specifies the `<search>` tag. \ No newline at end of file diff --git a/en/reference/release-notes/vespa8-geo-migration-guide.md b/en/reference/release-notes/vespa8-geo-migration-guide.mdx similarity index 58% rename from en/reference/release-notes/vespa8-geo-migration-guide.md rename to en/reference/release-notes/vespa8-geo-migration-guide.mdx index f935ad669a..d9a3ac0b2b 100644 --- a/en/reference/release-notes/vespa8-geo-migration-guide.md +++ b/en/reference/release-notes/vespa8-geo-migration-guide.mdx @@ -1,23 +1,19 @@ --- -# Copyright Vespa.ai. All rights reserved. title: "Position fields - Vespa 8 migration" -redirect_from: - - /en/vespa8-geo-migration-guide --- -Refer to [Vespa 8 release notes](vespa8.html) - -this is a guide on how to migrate from Vespa 7 to Vespa 8 when using position fields. -The guide is relevant for applications having a `position` field in a schema. +Refer to [Vespa 8 release notes](/en/reference/release-notes/vespa8) - this is a guide on how to migrate from Vespa 7 to Vespa 8 when using position fields. The guide is relevant for applications having a `position` field in a schema. For the rest of this document, we assume a schema containing: -``` + +```bash field myfield type position {...} ``` - ## Step 1: Upgrade to Vespa 8 in geo legacy mode -Add to _services.xml_, see [legacy-v7-json-rendering](../querying/default-result-format.html#geo-position-rendering), -add under the root `services` tag: + +Add to *services.xml*, see [legacy-v7-json-rendering](/en/reference/querying/default-result-format#geo-position-rendering), add under the root `services` tag: + ```xml <services> <legacy> @@ -25,11 +21,10 @@ add under the root `services` tag: </legacy> ``` +## Step 2: Result rendering +If the position field is only used as a filter, and not returned in result sets, skip this section. In Vespa 7, a position filed could be rendered as: -## Step 2: Result rendering -If the position field is only used as a filter, and not returned in result sets, skip this section. -In Vespa 7, a position filed could be rendered as: ```json "myfield.position": { "y": 63453700, @@ -37,69 +32,86 @@ In Vespa 7, a position filed could be rendered as: "latlong": "N63.453700;E10.460800" } ``` + and optionally: + ```json "myfield": { "y": 63453700, "x": 10460800 } ``` + With Vespa 8, the result format is changed to: + ```json "myfield": { "lat": 63.4537, "lng": 10.4608 } ``` -Note that this is also the Vespa 8 feeding format. -{% include important.html content="Change all code that parses query results to expect the new format. -This includes programs that parses the result JSON and [Searchers](../../applications/searchers.html)." %} +Note that this is also the Vespa 8 feeding format. + +<Warning> +**Important:** + +Change all code that parses query results to expect the new format. This includes programs that parses the result JSON and [Searchers](/en/applications/searchers). +</Warning> On Vespa 7, the `distance` rank feature is output as: -``` + +```bash "myfield.distance": 14352, ``` + On Vespa 8, use the summary feature instead: -``` + +```bash distance(myfield).km ``` - ## Step 3: Query API + Change from using the `pos.ll` / `pos.radius` / `pos.bb` / `pos.attribute` parameters, e.g.: -``` + +```bash pos.ll=63.4225N+10.3637E&pos.radius=5km ``` + to using [YQL](../../querying/query-language.html): -``` + +```bash where geoLocation(myfieldname, 63.5, 10.5, "5 km") ``` - ## Step 4: Feeding format and /document/v1/ API -The Vespa 7 feeding format can be used on Vespa 8, -it is however recommended changing to: +The Vespa 7 feeding format can be used on Vespa 8, it is however recommended changing to: + ```json "myfield": { "lat": 63.4537, "lng": 10.4608 } ``` + This is the same format as in query results. -The result format when using GET / VISIT in [document/v1/](../api/document-v1.html) is changed from: +The result format when using GET / VISIT in [document/v1/](/en/reference/api/document-v1) is changed from: + ```json "myfield": { "y": 63453700, "x": 10460800 } ``` + to: + ```json "myfield": { "lat": 63.4537, "lng": 10.4608 } -``` +``` \ No newline at end of file diff --git a/en/reference/release-notes/vespa8.html b/en/reference/release-notes/vespa8.html deleted file mode 100644 index 33e0bcf7c3..0000000000 --- a/en/reference/release-notes/vespa8.html +++ /dev/null @@ -1,1273 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Vespa 8 Release Notes" -redirect_from: -- /en/vespa8-release-notes.html ---- - -<p> - This document lists the changes between Vespa major versions 7 and 8. - As documented in <a href="../../learn/releases.html#versions">Vespa versions</a>, - new functionality in Vespa is introduced in minor versions, - while major versions are used to mark releases breaking compatibility. - As Vespa 8 does not introduce any new functionality, - it is as safe and mature as the versions of Vespa 7 preceding it. - No further releases will be made of Vespa 7, except - possible critical security fixes. -</p> - - - -<h2 id="compatibility-overview">Overview</h2> - -<p> - The compatibility breaking changes in Vespa 8 fall into these categories: -</p> -<ul> - <li><a href="#changed-defaults">Changes to default behaviour</a></li> - <li><a href="#application-package-changes">Application package structure and settings</a> - - deprecated settings and constructs in e.g. <em>schemas</em> and <em>services.xml</em> are removed.</li> - <li><a href="#java-api-changes">Java APIs</a> - - deprecated APIs are removed or revoked from Vespa's - <a href="https://javadoc.io/doc/com.yahoo.vespa/annotations/latest/com/yahoo/api/annotations/PublicApi.html">public API</a> surface.</li> - <li><a href="#container-runtime">Container runtime environment</a> - - incompatible changes to the Java build and runtime environments.</li> - <li><a href="#removed-http-api-parameters">HTTP API changes</a></li> - <li><a href="#removed-command-line-tools">Removed command line tools</a></li> - <li><a href="#removed-or-renamed-metrics">Removed or renamed metrics</a></li> - <li><a href="#document-selection-exact-type-matching">Changes to the document selection language</a></li> - <li><a href="#security">Security related changes</a></li> - <li><a href="#operating-system">Operating system support</a></li> - <li><a href="#other-changes">Other changes</a>, not covered by any of the above categories.</li> -</ul> - -<p> -To ensure their applications are compatible with Vespa 8, application owners must: -<ul> - <li>Review the list of <a href="#changed-defaults">changes to defaults</a> and add the necessary options - if you want to preserve behavior from Vespa 7.</li> - <li>Make sure there are no deprecation warnings when compiling against Vespa 7.</li> - <li>Review the <a href="#application-package-changes">application package changes</a> and make sure there - are no deployment warnings when deploying on Vespa 7.</li> - <li>Review the list of <a href="#removed-http-api-parameters">HTTP API changes</a> and update - any clients of the application.</li> - <li>Review the remaining sections of this document, and update the application and its environment accordingly.</li> -</ul> - -<p> - Usage of deprecated Java APIs produce warnings during compilation, while <em>deployment warnings</em> - are produced for application package deprecations and most changes to the container runtime environment. - In hosted Vespa or Vespa Cloud, deployment warnings are shown in the application's console view. - However, for other types of changes, there is no way to emit deprecation warnings, - so these are only described in this document and other Vespa documentation. -</p> - -<p> - The following sections lists all the changes from Vespa 7 to Vespa 8 in detail. -</p> - -<!-- ToDo: something on the xml/page presentation.format from query.html --> - -<h2 id="changed-defaults">Changed defaults</h2> - -<p>These changes may break clients, and impact both performance and user experience. -Applications that are in production and relies on these defaults should -make configuration changes to keep the existing behavior when upgrading to Vespa 8. -This can be done on Vespa 7, <i>before</i> upgrading - -using <a href="../../applications/testing.html#feature-switches-and-bucket-tests">bucket tests</a> can be useful.</p> - -<p> -The following defaults have changed: -</p> -<table class="table"> - <tr> - <th>Change</th> - <th>Configuration required to avoid change on Vespa 8</th> - </tr> - - <tr> - <td> - The default <a href="../querying/simple-query-language.html">simple query language</a> (for end users) - is changed from <code>all</code> to <a href="../../ranking/wand.html#weakand">weakAnd</a>. - {% include note.html content="This might increase recall, and increase latency significantly if document corpus is large." %} - </td> - <td> - Explicitly pass <a href="../api/query.html#model.type">model.type</a>=all in queries - or set this parameter in the relevant <a href="../../querying/query-profiles.html">query profiles</a>: - <code><field name="model.type">all</field></code>. - </td> - </tr> - - <tr> - <td> - The default grammar in <a href="../querying/yql.html#userinput">YQL userInput</a> - is changed from <code>all</code> to <a href="../../ranking/wand.html#weakand">weakAnd</a>. - {% include note.html content="This might increase recall, and increase latency significantly if document corpus is large." %} - </td> - <td> - Prefix <code>userInput</code> in YQL by <code>{grammar: "all"}</code>. - </td> - </tr> - - <tr> - <td> - The value of the services.xml - <a href="../querying/default-result-format.html#geo-position-rendering">legacy flag v7-geo-positions</a> - changes from true to false. See the <a href="vespa8-geo-migration-guide">Vespa 8 geo migration guide</a>. - </td> - <td> - Add to services.xml: - <code> - <legacy> <v7-geo-positions>true</v7-geo-positions> </legacy> - </code> - </td> - </tr> - - <tr> - <td> - Fields of type <code>map</code> - <a href="../querying/default-result-format.html#inconsistent-map-rendering">changes JSON rendering</a> - in search results. - </td> - <td> - Add overrides in your query profile(s) for the <code>renderer.json.jsonMaps</code> parameter. - </td> - </tr> - - <tr> - <td> - Fields of type <code>weightedset</code> - <a href="../querying/default-result-format.html#inconsistent-weightedset-rendering">changes JSON rendering</a> - in search results. - </td> - <td> - Add overrides in your query profile(s) for the <code>renderer.json.jsonWsets</code> parameter. - </td> - </tr> - - <tr> - <td> - Expressions used as summary features, are no longer rendered wrapped in - <code>rankingExpression()</code>. - </td> - <td> - Specify configuration in your rank profile as shown in - <a href="../querying/default-result-format.html#summary-features-wrapped-in-rankingexpression">this example</a>. - </td> - </tr> - - <tr> - <td> - Fields of type <code>raw</code> are now presented as a base64 - encoded string in summary, the same way as in json feed format. - Earlier, you needed to add <code>raw-as-base64-in-summary</code> - in your schema file to get this behavior. - </td> - <td> - If you have fields of type "raw" and you must have the old - summary behavior for them in search results, add the line - <br><code>raw-as-base64-in-summary : false</code><br> - in your schema definition. - </td> - </tr> - - <tr> - <td> - The default tensor format in responses has changed from 'long' to 'short': - Tensors in query results, document API responses, and stateless model evaluation - are rendered in the short form appropriate for their type - (if any), documented <a href="../schemas/document-json-format.html#tensor">here</a>. - </td> - <td> - <p><b>Queries</b>: Pass <a href="../api/query.html#presentation.format.tensors">presentation.format.tensors</a>=long - in queries, or set it parameter in the relevant <a href="../../querying/query-profiles.html">query profiles</a>.</p> - - <p><b>Document/v1</b>: Pass the parameter <code>format.tensors=long</code> in requests.</p> - - <p><b>Stateless model evaluation</b>: Pass the parameter <code>format.tensors=long</code> in requests.</p> - </td> - </tr> - - <tr> - <td> - The default fieldset when getting or visiting documents is now - <code>[document]</code> in all cases, meaning you only get those - fields that are declared in the "document" block of the schema - (generated fields are not included). - This was already the default for the <code>/document/v1</code> API - when fetching or visiting documents of a single known document type. - Now it is also the default when visiting at the root level, - for the command line tools <code>vespa-visit</code> and - <code>vespa-get</code>, and if you use the programmatic - <code>documentapi</code> from java to fetch documents. - </td> - <td> - In most cases there is no difference between <code>[all]</code> - and <code>[document]</code> fieldsets - so no action is needed. - If the old behavior is needed you can: - <ul> - <li> - For the command line tools, specify the fieldset as - <code>-l "[all]"</code> to include generated fields. - </li> - <li> - For <code>/document/v1</code> specify <code>[all]</code> - as the value for the <code>fieldSet</code> parameter. - </li> - <li> - If using <code>documentapi</code> from java, add the line - <br><code>params.setFieldSet("[all]");</code><br> - to modify your <code>VisitorParameters</code> value , or - <br><code>params = params.withFieldSet("[all]");</code><br> - to modify your <code>DocumentOperationParameters</code> value. - </li> - </ul> - If you run document processors to generate fields and want - those returned, it may be more useful to declare a fieldset - with just those fields you actually want as output instead. - </td> - </tr> - - <tr> - <td> - Vespa will now limit the number of groups and hits in - <a href="../../querying/grouping.html">grouping query results</a> when <code>max</code> is not specified - explicitly in grouping expressions. The default value is determined by - <a href="../api/query.html#grouping.defaultmaxgroups">grouping.defaultMaxGroups</a>/ - <a href="../api/query.html#grouping.defaultmaxhits">grouping.defaultMaxHits</a>. - The parameter <a href="../api/query.html#grouping.globalmaxgroups">grouping.globalMaxGroups</a> must now - be overridden in query profiles to allow grouping expressions that may return unbounded or large results. - </td> - <td> - <ul> - <li> - <a href="../api/query.html#grouping.defaultmaxgroups">grouping.defaultMaxGroups</a> - changed from <code>-1</code> to <code>10</code>. - </li> - <li> - <a href="../api/query.html#grouping.defaultmaxhits">grouping.defaultMaxHits</a> - changed from <code>-1</code> to <code>10</code>. - </li> - <li> - <a href="../api/query.html#grouping.globalmaxgroups">grouping.globalMaxGroups</a> - changed from <code>-1</code> to <code>10000</code>. - </li> - <li> - <a href="../api/query.html#grouping.defaultprecisionfactor">grouping.defaultPrecisionFactor</a> - changed from <code>1.0</code> to <code>2.0</code>. - </li> - </ul> - </td> - </tr> - - <tr> - <td> - Vespa <a href="../../operations/access-logging.html">access logs</a> are compressed with - <a href="https://github.com/facebook/zstd">zstd</a>. - </td> - <td> - Add a config override under <code><container></code> in <code>services.xml</code>: - <br><pre> - <config name="container.core.access-log"> - <fileHandler> - <compressionFormat>GZIP<compressionFormat> - </pre> - </td> - </tr> - -</table> - - - -<h2 id="application-package-changes">Application package changes</h2> - - -<h3 id="removed-settings-from-schemas">Removed settings from schemas</h3> -<p> - The following settings are removed from - <a href="../schemas/schemas.html">schema</a>: -</p> -<table class="table"> - <thead> - <tr> - <th>Name</th><th>Replacement</th> - </tr> - </thead> - <tbody> - <tr> - <td>attribute: huge</td> - <td>None. Setting <em>huge</em> on an attribute doesn't have any effect, - the code is rewritten to support it by default.</td> - </tr> - <tr> - <td><a href="../schemas/schemas.html#compression">compression</a></td> - <td> - <p id="compression"> - None. Document compression is not needed, as compression is always enabled. - </p> - </td> - </tr> - <tr> - <td>body (inside a field definition)</td> - <td> - <p id="body">None. Deprecated since before Vespa 7, had no effect in Vespa 7.</p> - </td> - </tr> - <tr> - <td>header (inside a field definition)</td> - <td><p id="header">None. Deprecated since before Vespa 7, had no effect in Vespa 7.</p></td> - </tr> - <tr> - <td>field type weightedset<float></td> - <td> - Because floating-point types are inherently imprecise they are badly - suited as keys in maps and sets. If you feel the need for such data - consider using something like: - <pre class="code"> - struct weightedfloat { - field value type float {} - field weight type int {} - } - field myfield type array<weightedfloat> { - ... - </pre> - </td> - </tr> - <tr> - <td>field type map<float,anything></td> - <td>Using "float" as the key in a map is no longer supported, see <code>weightedset<float></code> above.</td> - </tr> - <tr> - <td>field type weightedset<double></td> - <td>Using "double" as the key in a set is no longer supported, see <code>weightedset<float></code> above.</td> - </tr> - <tr> - <td>field type map<double,anything></td> - <td>Using "double" as the key in a map is no longer supported, see <code>weightedset<float></code> above.</td> - </tr> - <tr> - <td>field type weightedset<uri></td> - <td>Using complex types as the key in a set is no longer supported, see <code>weightedset<float></code> above.</td> - </tr> - <tr> - <td>field type map<uri,anything></td> - <td>Using complex types as the key in a map is no longer supported, see <code>weightedset<float></code> above.</td> - </tr> - <tr> - <td>Old syntax for array types like "string[]"</td> - <td>Write as <code>array<string></code> instead.</td> - </tr> - <tr> - <td>Rank functions must have different names in a rank-profile</td> - <td>Only the last of two functions with the same name would be used. - Remove or rename the first one.</td> - </tr> - <tr> - <td>Conflicting sorting settings are now rejected</td> - <td>Only keep the last of the conflicting settings.</td> - </tr> - <tr> - <td>A summary-field may only be added once in a document-summary block</td> - <td>Remove duplicates.</td> - </tr> - <tr> - <td>Schema and document should have the same name</td> - <td>Change name of the schema, so it is equal to the contained document.</td> - </tr> - </tbody> -</table> - - -<h3 id="tensorflow-import">TensorFlow import</h3> -<p> - Vespa 8 removes support for direct import of <a href="../../ranking/tensorflow">TensorFlow models</a>. - <a href="https://onnx.ai/">ONNX</a> is now the preferred ML model format, and works both for - <a href="../../ranking/onnx">ranking</a> and - <a href="../../ranking/stateless-model-evaluation.html">stateless model evaluation</a>. - ONNX contains tools to convert models from TensorFlow to ONNX, but Vespa will no longer provide this. -</p> - - -<h3 id="changed-semantics-in-services-xml">Changed semantics in services.xml</h3> -<p> - The following elements and attributes in services.xml have new semantics: -</p> -<table class="table"> - <thead> - <tr> - <th>Name</th><th>Description</th> - </tr> - </thead> - <tbody> - <tr> - <td><nodes><redundancy></td> - <td>It is now an error to configure a number of nodes (per group) that is - smaller than the configured redundancy. It used to generate an - application-level warning, with the redundancy implicitly reduced. - Remove any <nodes> override in the non-prod environments, as the - node count is automatically adjusted.</td> - </tr> - </tbody> -</table> - - -<h3 id="removed-constructs-from-services-xml">Removed constructs from services.xml</h3> -<p> - The following elements and attributes are removed from services.xml: -</p> -<table class="table"> - <thead> - <tr> - <th>Parent element</th><th>Removed construct</th><th>Description</th> - </tr> - </thead> - <tbody> - <tr> - <td rowspan="2"><admin></td> - <td><filedistribution></td> - <td>Configuring up/download rates is not supported</td> - </tr> - <tr> - <td><configserver></td> - <td>Use <a href="../applications/services/admin.html#configservers">configservers</a> element instead</td> - </tr> - <tr> - <td rowspan="2"><config></td> - <td><em>namespace</em> attribute</td> - <td>The namespace must be included in the <em>name</em> attribute.</td> - </tr> - <tr> - <td><myArray <em>operation="append"></em> syntax</td> - <td>Previously used to append items to config arrays. Use - <a href="../applications/config-files.html#configuring-arrays">item</a> instead.</td> - </tr> - <tr> - <td rowspan="4"><container></td> - <td><em>jetty</em> attribute</td> - <td>Removed, had no effect on Vespa 7.</td> - </tr> - <tr> - <td><nodes> jvm attributes</td> - <td>JVM attributes <em>jvmargs, allocated-memory, jvm-options, jvm-gc-options</em> - renamed and moved to <a href="../applications/services/container.html#jvm"> - JVM</a> subelement</td> - </tr> - <tr> - <td><client></td> - <td>Previously used for setting up client providers. - Use a <a href="../../applications/request-handlers.html">request handler</a> instead.</td> - </tr> - <tr> - <td><handler><clientBinding></td> - <td>Client bindings are no longer supported. - </tr> - <tr> - <td rowspan="5"><content></td> - <td><dispatch></td> - <td>Removed due to removal of <em>vespa-dispatch-bin</em>, - <a href="#vespa-dispatch-bin-process-is-removed">details.</a></td> - </tr> - <tr> - <td><tuning><dispatch><min-group-coverage></td> - <td>Use <a href="../applications/services/content.html#min-active-docs-coverage">min-active-docs-coverage</a> instead.</td> - </tr> - <tr> - <td><tuning><dispatch><use-local-node></td> - <td>Ignored, the local node will automatically be preferred when appropriate.</td> - </tr> - <tr> - <td><engine><proton><tuning><searchnode><flushstrategy><native><transactionlog><maxentries></td> - <td> - Use <a href="../applications/services/content.html#flushstrategy-native-transactionlog-maxsize">maxsize</a> instead. - Vespa 7 documentation: - The maximum number of entries in the <a href="../../content/proton.html#transaction-log">transaction log</a> - for a document type before running flush, default 1000000 (1 M). - </td> - </tr> - <tr> - <td><engine><proton><tuning><searchnode><summary><store><logstore><chunk><maxentries></td> - <td> - Use <a href="../applications/services/content.html#summary-store-logstore-chunk-maxsize">maxsize</a> instead. - Vespa 7 documentation: - Maximum number of documents in a chunk. - See <em>summary.log.chunk.maxentries</em>. - </td> - </tr> - <tr> - <td rowspan="3"><services> (root)</td> - <td><jdisc></td> - <td>Use <a href="../applications/services/container.html">container</a> instead. - </tr> - <tr> - <td><service></td> - <td>Running generic services is no longer supported.</td> - </tr> - <tr> - <td><clients></td> - <td>Client load types are deprecated and ignored.</td> - </tr> - </tbody> -</table> - - -<h3 id="application-folder"><em>application/</em> folder support removed</h3> -<p> - Application package content (<em>services.xml</em>, the <em>schemas/</em> - folder, etc.) is supposed to be put at the root level in the application - zip, such that the unzipped application package has <em>./services.xml</em>, - etc. -</p> -<p> - But it used to be that the application package content could be placed inside - an <em>application</em> directory. This support is removed on Vespa 8. -</p> - - -<h3 id="searchdefinitions-folder"><em>searchdefinitions/</em> folder is deprecated</h3> -<p> - Search definition schemas should now be placed in the <em>schemas/</em> folder. The old folder - will still work on Vespa 8, but causes a deprecation warning upon deployment. -</p> - - - -<h2 id="java-api-changes">Java API changes</h2> - -<h3 id="removed-java-packages">Removed Java packages</h3> - -<table class="table"> - <thead><tr><th>Package</th><th>Description</th></tr></thead> - <tbody> - <tr> - <td><em>com.yahoo.docproc.util</em></td> - <td>Removed</td> - </tr> - <tr> - <td><em>com.yahoo.jdisc.test</em></td> - <td>No longer <a href="https://javadoc.io/doc/com.yahoo.vespa/annotations/latest/com/yahoo/api/annotations/PublicApi.html">public API</a></td> - </tr> - <tr> - <td><em>com.yahoo.log.event</em></td> - <td>No longer <a href="https://javadoc.io/doc/com.yahoo.vespa/annotations/latest/com/yahoo/api/annotations/PublicApi.html">public API</a></td> - </tr> - <tr> - <td><em>com.yahoo.statistics</em></td> - <td>Removed</td> - </tr> - <tr> - <td><em>com.yahoo.vespa.curator</em></td> - <td>No longer <a href="https://javadoc.io/doc/com.yahoo.vespa/annotations/latest/com/yahoo/api/annotations/PublicApi.html">public API</a></td> - </tr> - <tr> - <td><em>com.yahoo.documentapi.messagebus.loadtypes</em></td> - <td>Load types are no longer supported. Use corresponding method overloads without <em>LoadType</em> or <em>LoadTypeSet</em> parameters instead.</td> - </tr> - </tbody> -</table> - - -<h3 id="removed-java-classes-and-methods">Removed Java Classes and methods</h3> - -<p> - Classes and methods that were marked as deprecated in Vespa 7 are removed. - If deprecation warnings are emitted for Vespa APIs when building the application, - these must be fixed before migrating to Vespa 8. The sections below contain only the - most notable changes. -</p> - -<p> - The following classes are no longer public API and have been moved to Vespa internal packages: -</p> -<table class="table"> - <thead><tr><th>Package</th><th>Class</th><th>Migration advice</th></tr></thead> - <tbody> - <tr> - <td><em>com.yahoo.config.subscription</em></td> - <td>All classes, except - <a href="https://javadoc.io/doc/com.yahoo.vespa/config/latest/com/yahoo/config/subscription/ConfigGetter.html"><em>ConfigGetter</em></a></td> - <td>Config should be <a href="../../applications/configuring-components.html#use-config-in-code">injected</a> - to your component class constructor. - </td> - </tr> - <tr> - <td rowspan="6"><em>com.yahoo.docproc</em></td> - <td><em>DocprocExecutor</em></td> - <td rowspan="2">For unit tests, follow the steps in the - <a href="https://github.com/vespa-engine/sample-apps/blob/master/examples/document-processing/src/test/java/ai/vespa/example/album/ProductTypeRefinerDocProcTest.java">document-processing</a> - sample app. If you need a <em>DocumentTypeManager</em> in production code, it can be directly - <a href="../../applications/dependency-injection.html">injected</a> to your component class constructor. - </td> - </tr> - <tr> - <td><em>DocprocService</em></td> - </tr> - <tr> - <td><em>DocumentOperationWrapper</em></td> - <td rowspan="4">No replacement - if needed, contact the Vespa team for advice.</td> - </tr> - <tr> - <td><em>HandledProcessingException</em></td> - </tr> - <tr> - <td><em>ProcessingEndpoint</em></td> - </tr> - <tr> - <td><em>TransientFailureException</em></td> - </tr> - </tbody> - <tr> - <td><em>com.yahoo.log</em></td> - <td><em>VespaFormatter</em></td> - <td>No replacement.</td> - </tr> -</table> - -<p> - The following methods are removed: -</p> -<table class="table"> - <thead><tr><th>Method</th><th>Migration advice</th></tr></thead> - <tbody> - <tr> - <td><em>com.yahoo.documentapi.DocumentAccess.createDefault()</em></td> - <td>Container components can have a <em>DocumentAccess</em> injected via their constructor. - For use outside the container, e.g. in a custom command line tool, use the new method - <em>createForNonContainer()</em>.</td> - </tr> - <tr> - <td><em>com.yahoo.log.LogSetup.getLogHandler()</em></td> - <td>No replacement.</td> - </tr> - </tbody> -</table> - - -<h3 id="changed-java-apis">Breaking changes to Java APIs</h3> -<p> - The Javadoc of the deprecated types/members should document the replacement API. - The below list is not exhaustive - some smaller and trivial changes are not listed. -</p> -<table class="table"> - <thead><tr><th>Type(s)</th><th>Description</th></tr></thead> - <tbody> - <tr> - <td><em>com.yahoo.processing</em></td> - <td>Removed use of Guava's <em>ListenableFuture</em> in type signatures. Replacement uses <em>CompletableFuture</em>.</td> - </tr> - <tr> - <td><em>com.yahoo.search.handler.HttpSearchResponse.waitableRender()</em></td> - <td>Removed use of Guava's <em>ListenableFuture</em> in type signature. The method is replaced with <em>asyncRender()</em>.</td> - </tr> - <tr> - <td><em>com.yahoo.jdisc.handler</em></td> - <td>Removed use of Guava's <em>ListenableFuture</em> in type signatures. Replacement uses <em>CompletableFuture</em></td> - </tr> - <tr> - <td><em>com.yahoo.searchlib.rankingexpression.rule</em></td> - <td>Removed use of Guava collection types in type signatures.</td> - </tr> - <tr> - <td><em>com.yahoo.search.rendering.JsonRenderer</em></td> - <td>Removed use of Jackson types from class signature.</td> - </tr> - <tr> - <td><em>com.yahoo.jdisc.Container</em></td> - <td>Removed use of Guice types from class signature.</td> - </tr> - <tr> - <td><em>com.yahoo.vdslib.VisitorStatistics</em></td> - <td>Removed all <em>set/getSecondPass</em>-related methods.</td> - </tr> - <tr> - <td><em>com.yahoo.documentapi</em></td> - <td> - Removed all methods taking in a <em>com.yahoo.documentapi.messagebus.DocumentProtocol.Priority</em> argument. - Explicit operation priorities are deprecated and should not be set by the client. - </td> - </tr> - </tbody> -</table> - - -<h3 id="built-in-xml-factories">Removed support for built-in XML factories</h3> -<p> - The Jdisc container has historically supported injection of built-in providers for the following XML factories: -</p> - -<ul> - <li><em>javax.xml.datatype.DatatypeFactory</em></li> - <li><em>javax.xml.parsers.DocumentBuilderFactory</em> and <em>SAXParserFactory</em></li> - <li><em>javax.xml.stream.XMLEventFactory</em>, <em>XMLInputFactory</em> and <em>XMLOutputFactory</em></li> - <li><em>javax.xml.transform.TransformerFactory</em></li> - <li><em>javax.xml.validation.SchemaFactory</em></li> - <li><em>javax.xml.xpath.XPathFactory</em></li> -</ul> - -<p> - These are now removed. Please check for more recent alternatives if you need this type of XML processing. -</p> - - -<h3 id="deprecated-java-apis">Deprecated Java APIs</h3> -<p> - A few redundant APIs have been deprecated because they have replacements that - provide the same, or better, functionality. We advise you switch to the - replacement to reduce future maintenance cost. -</p> - -<table class="table"> - <thead><tr><th>Type(s)</th><th>Replacement</th></tr></thead> - <tbody> - <tr> - <td><em>com.yahoo.container.jdisc.LoggingRequestHandler</em></td> - <td>Use <em>com.yahoo.container.jdisc.ThreadedHttpRequestHandler</em> instead.</td> - </tr> - <tr> - <td><em>com.yahoo.log.LogLevel</em></td> - <td>Use <em>java.util.logging.Level</em> instead.</td> - </tr> - </tbody> -</table> - - - - -<h2 id="container-runtime">Container Runtime Environment</h2> - - -<h3 id="jdk-version">JDK version</h3> -<p> - Vespa 8 upgrades the JDK version from 11 to 17. To ensure full compatibility, all container components - should be rebuilt with JDK 17 before being deployed on Vespa 8. -</p> - - -<h3 id="provided-maven-artifacts">Changes to provided maven artifacts</h3> -<p> - <a href="https://search.maven.org/artifact/com.google.guava/guava">Guava</a> has been upgraded from - version 20.0 to 27.1. If you are using APIs that have been removed from the library since version 20, your - code must be updated. In most cases, it should be trivial to find replacement APIs in Java's standard library. -</p> - -<p> -The following Maven artifacts are no longer provided runtime to user application plugins by the Jdisc container: -</p> -<table class="table"> - <thead><tr><th>Artifact</th><th>Notes</th></tr></thead> - <tbody> - <tr> - <td><a href="https://search.maven.org/artifact/com.fasterxml.jackson.jaxrs/jackson-jaxrs-base"><em>com.fasterxml.jackson.jaxrs:jackson-jaxrs-base</em></a> </td> - <td rowspan="2">JSON input/output handling for JAX-RS implementations, e.g. Jersey</td> - </tr> - <tr> - <td><a href="https://search.maven.org/artifact/com.fasterxml.jackson.jaxrs/jackson-jaxrs-json-provider"><em>com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider</em></a> </td> - </tr> - <tr> - <td><a href="https://search.maven.org/artifact/com.fasterxml.jackson.module/jackson-module-jaxb-annotations"><em>com.fasterxml.jackson.module:jackson-module-jaxb-annotations</em></a> </td> - <td>Jackson data binding with JAXB annotations.</td> - </tr> - <tr> - <td><a href="https://search.maven.org/artifact/com.google.code.findbugs/jsr305"><em>com.google.code.findbugs:jsr305</em></a></td> - <td>Annotations in package <em>javax.annotation[.*]</em>, e.g. <em>Nullable</em> and <em>Nonnnull</em>.</td> - </tr> - <tr> - <td><a href="https://search.maven.org/artifact/com.google.inject.extensions/guice-assistedinject"><em>com.google.inject.extensions:guice-assistedinject</em></a></td> - <td>Guice extensions.<br> For component injection see <a href="../../applications/dependency-injection.html#depending-on-another-component">Depending on another component</a></td> - </tr> - <tr> - <td><a href="https://search.maven.org/artifact/com.google.inject.extensions/guice-multibindings"><em>com.google.inject.extensions:guice-multibindings</em></a></td> - <td>Guice extensions.</td> - </tr> - <tr> - <td><a href="https://search.maven.org/artifact/javax.annotation/javax.annotation-api"><em>javax.annotation:javax.annotation-api</em></a></td> - <td>Annotations in package <em>javax.annotation[.*]</em>, e.g. <em>ManagedBean</em> and <em>Resource</em>.</td> - </tr> - <tr> - <td><a href="https://search.maven.org/artifact/javax.validation/validation-api"><em>javax.validation:validation-api</em></a></td> - <td>Javax bean validation, used by Jersey 2.</td> - </tr> - <tr> - <td><a href="https://search.maven.org/search?q=g:org.eclipse.jetty"><em>org.eclipse.jetty:*</em></a></td> - <td>The Eclipse Jetty Project.</td> - </tr> - <tr> - <td><a href="https://search.maven.org/artifact/org.apache.felix/org.apache.felix.framework"><em>org.apache.felix:org.apache.felix.framework</em></a></td> - <td>Felix OSGi framework.</td> - </tr> - <tr> - <td><em>org.apache.felix:org.apache.felix.log</em></td> - <td>Felix OSGi framework.</td> - </tr> - <tr> - <td><a href="https://search.maven.org/artifact/org.apache.felix/org.apache.felix.main"><em>org.apache.felix:org.apache.felix.main</em></a></td> - <td>Felix OSGi framework.</td> - </tr> - <tr> - <td><a href="https://search.maven.org/artifact/org.bouncycastle/bcpkix-jdk15on"><em>org.bouncycastle:bcpkix-jdk15on</em></a></td> - <td>Bouncy Castle crypto API.</td> - </tr> - <tr> - <td><a href="https://search.maven.org/artifact/org.bouncycastle/bcprov-jdk15on"><em>org.bouncycastle:bcprov-jdk15on</em></a></td> - <td>Bouncy Castle crypto provider.</td> - </tr> - <tr> - <td><em>org.glassfish.*:*</em></td> - <td>Jersey 2. All related artifacts are removed.</td> - </tr> - <tr> - <td><a href="https://search.maven.org/artifact/org.json/json"><em>org.json:json</em></a></td> - <td>See <a href="https://github.com/vespa-engine/vespa/issues/14762">vespa-engine/vespa#14762</a></td> - </tr> - <tr> - <td><a href="https://search.maven.org/artifact/org.javassist/javassist"><em>org.javassist:javassist</em></a></td> - <td>Bytecode manipulation, used by Jersey 2.</td> - </tr> - <tr> - <td><a href="https://search.maven.org/artifact/org.jvnet.mimepull/mimepull"><em>org.jvnet.mimepull:mimepull</em></a></td> - <td>MIME Streaming Extension, used by Jersey 2.</td> - </tr> - <tr> - <td><a href="https://search.maven.org/artifact/org.lz4/lz4-java"><em>org.lz4:lz4-java</em></a></td> - <td>Compression library.</td> - </tr> - </tbody> -</table> -<p> -Make sure your application OSGi bundle embeds the required artifacts from the above list. -An artifact can be embedded by adding it in scope <em>compile</em> to the <em>dependencies</em> section in pom.xml. -Typically, these artifacts have until now been used in scope <em>provided</em>. -Use <code>mvn dependency:tree</code> to check whether any of the listed artifacts are directly or transitively included -as dependencies. -</p> -<p> -As always, remove any dependencies that are not required by your project. Consult the Maven documentation on -<a href="https://maven.apache.org/guides/introduction/introduction-to-optional-and-excludes-dependencies.html#dependency-exclusions" data-proofer-ignore> -Dependency Exclusions</a> for how to remove a transitively included dependency. -</p> - -An example adding <em>org.json:json</em> as a compile scoped dependency: -<pre> -<dependencies> - ... - <dependency> - <groupId>org.json</groupId> - <artifactId>json</artifactId> - <version>20211205</version> - <scope>compile</scope> - </dependency> - ... -</dependencies> -</pre> - -<!-- -<h2 id="removed-http-apis">Removed HTTP APIs</h2> -<p> -The following HTTP APIs are removed: -</p> -<table width="100%" class="table"> - <thead> - <tr> - <th width="50%">Name</th><th>Replacement</th> - </tr> - </thead> - <tbody> - <tr> - <td>: - </td> - <td>. - </td> - </tr> - </tbody> -</table> ---> - - -<h2 id="removed-http-api-parameters">Removed HTTP API parameters</h2> -<p> -The following HTTP API parameters are removed from the <a href="../api/query.html">query API</a>: -</p> -<table class="table"> - <thead> - <tr> - <th>Standard API path</th><th>Parameter name</th><th>Replacement</th> - </tr> - </thead> - <tbody> - <tr> <td>/search/</td><td><em>pos.ll</em></td> <td>add a -<a href="../querying/yql.html#geolocation">geoLocation</a> -item to the query</td> </tr> - <tr> <td>/search/</td><td><em>pos.radius</em></td> <td>add a -<a href="../querying/yql.html#geolocation">geoLocation</a> -item to the query</td> </tr> - <tr> <td>/search/</td><td><em>pos.attribute</em></td> <td>add a - <a href="../querying/yql.html#geolocation">geoLocation</a> - item to the query</td> </tr> - <tr> <td>/search/</td><td><em>pos.bb</em></td> <td> - Support for restricting search by a bounding box, using the <code>pos.bb</code> - query parameter, has been removed - add a - <a href="../querying/yql.html#geolocation">geoLocation</a> - item to the query</td> </tr> - </tbody> -</table> - - - -<h2 id="removed-command-line-tools">Removed command line tools</h2> - -<h3 id="vespa-http-client">vespa-http-client</h3> -<p> - The <code>vespa-http-client</code> command line tool is removed on Vespa 8 and is replaced by the new - <a href="../../clients/vespa-feed-client.html">vespa-feed-client</a>. The new client uses - <a href="../../performance/http2.html">HTTP/2</a> and the <a href="../../writing/document-v1-api-guide.html">Document v1 API</a>. -</p> -<p> - The underlying rest API used by the vespa-http-client will still be available and supported on Vespa 8. - You can therefore continue to use an old client distributed with Vespa 7 to feed to a Vespa 8 installation. - Note that there will not be released any updates for vespa-http-client after the initial Vespa 8 release, - while fixes and security updates to the rest API implementation will continue as part of Vespa 8. - We strongly recommend that you migrate away from vespa-http-client in a timely manner. -</p> - - -<h2 id="removed-or-renamed-metrics">Removed or renamed metrics</h2> -<p> -The following metrics are renamed: -</p> -<table class="table"> - <thead> - <tr> - <th>Old Name</th><th>New name</th><th>Description</th> - </tr> - </thead> - <tbody> - <tr> - <td><em>vds.filestor.alldisks.*</em></td> - <td>vds.filestor.*</td> - <td><em>alldisks</em> has been removed from the metric name.</td> - </tr> - <tr> - <td><em>vds.visitor.*.sum.*</em></td> - <td>vds.visitor.*.*</td> - <td><em>sum</em> has been removed from the metric name.</td> - </tr> - <tr> - <td><em>vds.filestor.*.sum.*</em></td> - <td>vds.filestor.*.*</td> - <td><em>sum</em> has been removed from the metric name.</td> - </tr> - <tr> - <td><em>vds.distributor.*.sum.*</em></td> - <td>vds.distributor.*.*</td> - <td><em>sum</em> has been removed from the metric name.</td> - </tr> - </tbody> -</table> - -<p> -The following metrics are removed: -</p> -<table class="table"> - <thead> - <tr> - <th>Name</th><th>Description</th> - </tr> - </thead> - <tbody> - <tr> - <td><em>http.status.401.rate</em></td> - <td>Use <em>http.status.4xx.rate</em> with dimension <em>statusCode</em>==401</td> - </tr> - <tr> - <td><em>http.status.403.rate</em></td> - <td>Use <em>http.status.4xx.rate</em> with dimension <em>statusCode</em>==403</td> - </tr> - <tr> - <td><em>content.proton.documentdb.matching.query_collateral_time.*</em></td> - <td>Use <em>content.proton.documentdb.matching.query_setup_time.*</em> instead</td> - </tr> - <tr> - <td><em>content.proton.documentdb.matching.rank_profile.query_collateral_time.*</em></td> - <td>Use <em>content.proton.documentdb.matching.rank_profile.query_setup_time.*</em> instead</td> - </tr> - <tr> - <td><em>vds.visitor.allthreads.averagevisitorlifetime<s>.sum</s>.average</em></td> - <td>Use .sum/.count instead</td> - </tr> - <tr> - <td><em>vds.visitor.allthreads.averagequeuewait<s>.sum</s>.average</em></td> - <td>Use .sum/.count instead</td> - </tr> - <tr> - <td><em>vds.visitor.allthreads.queuesize<s>.sum</s>.average</em></td> - <td>Use .sum/.count instead</td> - </tr> - <tr> - <td><em>vds.visitor.allthreads.completed<s>.sum</s>.average</em></td> - <td>Use .sum/.count instead</td> - </tr> - <tr> - <td><em>vds.visitor.allthreads.averagemessagesendtime<s>.sum</s>.average</em></td> - <td>Use .sum/.count instead</td> - </tr> - <tr> - <td><em>vds.visitor.allthreads.averageprocessingtime<s>.sum</s>.average</em></td> - <td>Use .sum/.count instead</td> - </tr> - <tr> - <td><em>vds.filestor<s>.alldisks</s>.queuesize.average</em></td> - <td>Use .sum/.count instead</td> - </tr> - <tr> - <td><em>vds.filestor<s>.alldisks</s>.averagequeuewait.average</em></td> - <td>Use .sum/.count instead</td> - </tr> - <tr> - <td><em>vds.filestor<s>.alldisks</s>.allthreads.put<s>.sum</s>.latency.average</em></td> - <td>Use .sum/.count instead</td> - </tr> - <tr> - <td><em>vds.filestor<s>.alldisks</s>.allthreads.remove<s>.sum</s>.latency.average</em></td> - <td>Use .sum/.count instead</td> - </tr> - <tr> - <td><em>vds.filestor<s>.alldisks</s>.allthreads.get<s>.sum</s>.latency.average</em></td> - <td>Use .sum/.count instead</td> - </tr> - <tr> - <td><em>vds.filestor<s>.alldisks</s>.allthreads.update<s>.sum</s>.latency.average</em></td> - <td>Use .sum/.count instead</td> - </tr> - <tr> - <td><em>vds.filestor<s>.alldisks</s>.allthreads.createiterator<s>.sum</s>.latency.average</em></td> - <td>Use .sum/.count instead</td> - </tr> - <tr> - <td><em>vds.filestor<s>.alldisks</s>.allthreads.visit<s>.sum</s>.latency.average</em></td> - <td>Use .sum/.count instead</td> - </tr> - <tr> - <td><em>vds.filestor<s>.alldisks</s>.allthreads.remove_location<s>.sum</s>.latency.average</em></td> - <td>Use .sum/.count instead</td> - </tr> - <tr> - <td><em>vds.filestor<s>.alldisks</s>.allthreads.deletebuckets<s>.sum</s>.latency.average</em></td> - <td>Use .sum/.count instead</td> - </tr> - <tr> - <td><em>vds.distributor.puts<s>.sum</s>.latency.average</em></td> - <td>Use .sum/.count instead</td> - </tr> - <tr> - <td><em>vds.distributor.removes<s>.sum</s>.latency.average</em></td> - <td>Use .sum/.count instead</td> - </tr> - <tr> - <td><em>vds.distributor.updates<s>.sum</s>.latency.average</em></td> - <td>Use .sum/.count instead</td> - </tr> - <tr> - <td><em>vds.distributor.gets<s>.sum</s>.latency.average</em></td> - <td>Use .sum/.count instead</td> - </tr> - <tr> - <td><em>vds.distributor.visitor<s>.sum</s>.latency.average</em></td> - <td>Use .sum/.count instead</td> - </tr> - </tbody> -</table> - - - -<h2 id="document-selection-exact-type-matching">Exact matching of document types in selection language</h2> -<p> - The <a href="../writing/document-selector-language.html">document selection language</a> now - uses <em>exact</em> matching for document types rather than <em>inheritance</em> ("is-a") - matching. -</p> -<p> - Example with two minimal document schemas: -</p> -<ul> - <li><code>document my_doc_type {}</code></li> - <li><code>document my_extended_doc_type inherits my_doc_type {}</code></li> -</ul> -<p> - Previously, the selection expression <code>my_doc_type</code> would match both a document instance of - type <code>my_doc_type</code> <em>and</em> <code>my_extended_doc_type</code>. It will now - <em>only</em> match a document of type <code>my_doc_type</code>. -</p> - -<h2 id="security">Security</h2> -<h3 id="strict-mode">Strict mode enabled by default</h3> -<p> - <em>Strict mode</em> for request filtering in the jdisc container is enabled by default in Vespa 8. - See documentation on the <a href="../applications/services/http.html#filtering">strict-mode</a> - attribute in services.xml for details. -</p> - -<h3 id="request-headers-for-remote-in-access-log">Request headers controlling remote host/port in access log</h3> -<p> - The jdisc container will use the <em>X-Forwarded-For</em> and <em>X-Forwarded-Port</em> request headers to set the remote - host and port respectively in the access log. The following request headers will no longer be handled by default: -</p> -<ul> - <li>y-ra</li> - <li>yahooremoteip</li> - <li>client-ip</li> - <li>y-rp</li> -</ul> - -<h2 id="operating-system">Operating system support for Vespa artifacts</h2> -<h3 id="supported-rpm-os">RPMs</h3> -<p> - The supported OS for Vespa RPMs changes from <a href="https://www.centos.org/centos-linux/">CentOS Linux 7</a> to - <a href="https://www.centos.org/centos-stream/">CentOS Stream 8</a> for Vespa 8. RPMs will still be built - and distributed on <a href="https://copr.fedorainfracloud.org/coprs/g/vespa/vespa/">Fedora Copr</a>. If you install Vespa - RPMs you will have to upgrade your OS to <a href="https://www.centos.org/centos-stream/">CentOS Stream 8</a>. -</p> - -<h3 id="supported-container-os">OCI containers (Docker containers)</h3> -<p> - The base image used in our OCI containers changes from <a href="https://hub.docker.com/_/centos">docker.io/centos:7</a> - to <a href="https://quay.io/repository/centos/centos?tab=tags">quay.io/centos/centos:stream8</a> for Vespa 8. This means that - the container image is built and tested on systems running kernel version <em>4.18.0</em> (current kernel for CentOS Stream 8). - If you use Vespa's container image, you should upgrade the hosts running the containers to the same or a newer kernel version. -</p> - -<h2 id="other-changes">Other changes</h2> - -<h3 id="unknown-rank-profiles">Unknown rank profiles</h3> -<p> - Queries that specify a rank profile which does not exist in all the schemas being queried - will now fail instead of falling back to using the <code>default</code> profile. - Queries to multiple schemas must use a rank profile that exists in all of them, - which can be ensured by <a href="/en/schemas/inheritance-in-schemas.html">inheriting</a> a common schema. -</p> - -<h3 id="unknown-summary-classes">Unknown summary classes</h3> -<p> - Queries that specify a non-existent <a href="../../querying/document-summaries.html">summary class</a> - will now fail, instead of being rendered empty. - Queries to multiple schemas must use a summary class that exists in all of them, - which can be ensured by <a href="/en/schemas/inheritance-in-schemas.html">inheriting</a> a common schema. -</p> - -<h3 id="qrserver-service-name">The "qrserver" service name</h3> -<p> - Vespa containers are in general using "container" as their service name. However, a container cluster that has - declared neither <a href="../applications/services/docproc.html">document-processing</a> nor - <a href="../applications/services/container.html#document-api">document-api</a> used to be named "qrserver". - On Vespa 8 all container clusters uses the service name "container". This affects the output of all - metrics APIs, as well as the Vespa log output. -</p> - - -<h3 id="container-access-logs">Container access logs</h3> -<p> - The folder for container access logs has been moved from <code>$VESPA_HOME/logs/vespa/qrs/</code> to - <code>$VESPA_HOME/logs/vespa/access/</code>. -</p> -<p> - The default compression format has changed from gzip to zstd, see <a href="#changed-defaults">changed defaults</a>. -</p> - - -<h3 id="onnx-in-summary-features">ONNX output in summary features</h3> -<p> - When defining an ONNX model output in summary features, Vespa 8 ensures that the summary feature name is <code>onnx</code> - rather than <code>onnxModel</code> as in previous version. -</p> - - -<h3 id="rankfeatures">Changes in rankfeatures</h3> -<p> - Vespa can calculate and return all <a href="../api/query.html#ranking.listfeatures">rank-features</a> - in the <code>rankfeatures</code> summary field. Vespa 8 contains some changes to this list: -</p> -<ul> - <li><code>now</code> is removed</li> - <li><code>bm25(field)</code> is added</li> - <li><code>matches(field)</code> is added</li> -</ul> - - -<h3 id="storage-routing-policy">The "storage" message bus routing policy is removed</h3> -<p> - The "storage" routing policy was removed in early Vespa 7, and clients specifying it have been forwarded to - the content routing policy for backwards compatibility. The forwarding is removed on Vespa 8 and clients needs - be updated. -</p> -<p> - Replace all usages of the "storage" policy with "content", which behaves identically. -</p> - - -<h3 id="vespa-dispatch-bin-process-is-removed">vespa-dispatch-bin process is removed</h3> -<p> - The dispatch functionality is moved into the Vespa Container - and the <em>vespa-dispatch-bin</em> process is removed. - As this is not a public interface, - the default was switched to <strong>not</strong> using vespa-dispatch-bin in Vespa-7.109.10. - The process was removed in subsequent Vespa releases: -<table class="table"> - <tr> - <td><a href="../../querying/query-api.html">Dispatch</a></td> - <td>Content cluster</td> - <td>dynamically allocated in 19100 - 19899 range</td> - <td>$VESPA_HOME/sbin/vespa-dispatch-bin</td> - <td>Dispatcher, communicates between container and content nodes. - Can be multi-level in a hierarchy</td> - </tr> -</table> -<p> - Rolling upgrade note: A rolling upgrade over Vespa-7.109.10 should work with no extra steps. -</p> - -<h3 id="yql-format">YQL format</h3> -<ul> - <li> - When Vespa outputs an YQL statement, it will now not end the string by a semicolon. - Terminating statements with semicolon continue to be optional and legal input. - </li> - <li> - <a href="../querying/yql.html#annotations">Annotations</a> - are not enclosed in <code>[]</code> brackets (still valid input). - </li> - <li> - The annotation name is not quoted (still valid input). - </li> -</ul> -<p>Example Vespa 7 / Vespa 8:</p> -<pre> -where text contains ([{"distance": 5}]near("a", "b")); -where text contains ({distance: 5}near("a", "b")) -</pre> - -<h3 id="upgrade-procedure">Upgrade procedure</h3> -The <a href="/en/operations/self-managed/live-upgrade.html">upgrade procedure</a> has been simplified in Vespa 8 -(when upgrading from Vespa 7 to Vespa 8 or between Vespa 8 versions). -When upgrading from Vespa 7.x to Vespa 7.y replace item 3 -<a href="/en/operations/self-managed/live-upgrade.html#upgrade-config-server">Upgrade config servers</a> -in the <a href="/en/operations/self-managed/live-upgrade.html">upgrade procedure</a> with this procedure: -<ul> - <li> - When upgrading the config servers, the nodes of the application cannot receive config - until they are upgraded themselves. We need to set all of them - in standalone mode before continuing by running this command on each node: - <pre> - $ vespa-configproxy-cmd -m setmode memorycache - </pre> - Each node will automatically reattach itself when it is upgraded. - </li> - <li> - Install the new Vespa version on the config servers and - <a href="/en/operations/self-managed/admin-procedures.html#vespa-start-stop-restart">restart</a> them one by one. - Wait until it is up again, look in vespa log for - "Changing health status code from 'initializing' to 'up'" - or use <a href="/en/operations/self-managed/configuration-server.html#troubleshooting">health checks</a>. - </li> - <li> - Redeploy and activate the application: - <pre>$ <a href="/en/clients/vespa-cli.html#deployment">vespa deploy</a></pre> - </li> -</ul> diff --git a/en/reference/release-notes/vespa8.mdx b/en/reference/release-notes/vespa8.mdx new file mode 100644 index 0000000000..88b44d78bc --- /dev/null +++ b/en/reference/release-notes/vespa8.mdx @@ -0,0 +1,935 @@ +--- +title: "Vespa 8 Release Notes" +sidebarTitle: "Vespa 8" +--- + +This document lists the changes between Vespa major versions 7 and 8. As documented in [Vespa versions](/en/learn/releases#versions), new functionality in Vespa is introduced in minor versions, while major versions are used to mark releases breaking compatibility. As Vespa 8 does not introduce any new functionality, it is as safe and mature as the versions of Vespa 7 preceding it. No further releases will be made of Vespa 7, except possible critical security fixes. + +## Overview + +The compatibility breaking changes in Vespa 8 fall into these categories: + +- [Changes to default behaviour](#changed-defaults) +- [Application package structure and settings](#application-package-changes) - deprecated settings and constructs in e.g. *schemas* and *services.xml* are removed. +- [Java APIs](#java-api-changes) - deprecated APIs are removed or revoked from Vespa's [public API](https://javadoc.io/doc/com.yahoo.vespa/annotations/latest/com/yahoo/api/annotations/PublicApi.html) surface. +- [Container runtime environment](#container-runtime) - incompatible changes to the Java build and runtime environments. +- [HTTP API changes](#removed-http-api-parameters) +- [Removed command line tools](#removed-command-line-tools) +- [Removed or renamed metrics](#removed-or-renamed-metrics) +- [Changes to the document selection language](#document-selection-exact-type-matching) +- [Security related changes](#security) +- [Operating system support](#operating-system) +- [Other changes](#other-changes), not covered by any of the above categories. + +To ensure their applications are compatible with Vespa 8, application owners must: + +- Review the list of [changes to defaults](#changed-defaults) and add the necessary options if you want to preserve behavior from Vespa 7. +- Make sure there are no deprecation warnings when compiling against Vespa 7. +- Review the [application package changes](#application-package-changes) and make sure there are no deployment warnings when deploying on Vespa 7. +- Review the list of [HTTP API changes](#removed-http-api-parameters) and update any clients of the application. +- Review the remaining sections of this document, and update the application and its environment accordingly. + +Usage of deprecated Java APIs produce warnings during compilation, while *deployment warnings* are produced for application package deprecations and most changes to the container runtime environment. In hosted Vespa or Vespa Cloud, deployment warnings are shown in the application's console view. However, for other types of changes, there is no way to emit deprecation warnings, so these are only described in this document and other Vespa documentation. + +The following sections lists all the changes from Vespa 7 to Vespa 8 in detail. + +## Changed defaults + +These changes may break clients, and impact both performance and user experience. Applications that are in production and relies on these defaults should make configuration changes to keep the existing behavior when upgrading to Vespa 8. This can be done on Vespa 7, *before* upgrading - using [bucket tests](/en/applications/testing#feature-switches-and-bucket-tests) can be useful. + +The following defaults have changed: + +<table className="vespa-table"> + <thead> + <tr> + <th>Change</th> + <th>Configuration required to avoid change on Vespa 8</th> + </tr> + </thead> + <tbody> + <tr> + <td>The default <a href="/en/reference/querying/simple-query-language" aria-label="simple query language">simple query language</a> (for end users) is changed from <code>{`all`}</code> to <a href="/en/ranking/wand#weakand" aria-label="weakAnd">weakAnd</a>.<br/> <Note>**Note:**<br/><br/> This might increase recall, and increase latency significantly if document corpus is large.</Note></td> + <td>Explicitly pass <a href="/en/reference/api/query#model.type" aria-label="model.type">model.type</a>=all in queries or set this parameter in the relevant <a href="/en/querying/query-profiles" aria-label="query profiles">query profiles</a>: <code>{`<field name="model.type">all</field>`}</code>.</td> + </tr> + <tr> + <td>The default grammar in <a href="/en/reference/querying/yql#userinput" aria-label="YQL userInput">YQL userInput</a> is changed from <code>{`all`}</code> to <a href="/en/ranking/wand#weakand" aria-label="weakAnd">weakAnd</a>. <br/> <Note>**Note:**<br/><br/> This might increase recall, and increase latency significantly if document corpus is large.</Note></td> + <td>Prefix <code>{`userInput`}</code> in YQL by <code>{`{grammar: "all"}`}</code>.</td> + </tr> + <tr> + <td>The value of the services.xml <a href="/en/reference/querying/default-result-format#geo-position-rendering" aria-label="legacy flag v7-geo-positions">legacy flag v7-geo-positions</a> changes from true to false. See the <a href="/en/reference/release-notes/vespa8-geo-migration-guide" aria-label="Vespa 8 geo migration guide">Vespa 8 geo migration guide</a>.</td> + <td>Add to services.xml: <code>{` <legacy> <v7-geo-positions>true</v7-geo-positions> </legacy> `}</code></td> + </tr> + <tr> + <td>Fields of type <code>{`map`}</code> <a href="/en/reference/querying/default-result-format#inconsistent-map-rendering" aria-label="changes JSON rendering">changes JSON rendering</a> in search results.</td> + <td>Add overrides in your query profile(s) for the <code>{`renderer.json.jsonMaps`}</code> parameter.</td> + </tr> + <tr> + <td>Fields of type <code>{`weightedset`}</code> <a href="/en/reference/querying/default-result-format#inconsistent-weightedset-renderingg" aria-label="changes JSON rendering">changes JSON rendering</a> in search results.</td> + <td>Add overrides in your query profile(s) for the <code>{`renderer.json.jsonWsets`}</code> parameter.</td> + </tr> + <tr> + <td>Expressions used as summary features, are no longer rendered wrapped in <code>{`rankingExpression()`}</code>.</td> + <td>Specify configuration in your rank profile as shown in <a href="/en/reference/querying/default-result-format#summary-features-wrapped-in-rankingexpression" aria-label="this example">this example</a>.</td> + </tr> + <tr> + <td>Fields of type <code>{`raw`}</code> are now presented as a base64 encoded string in summary, the same way as in json feed format. Earlier, you needed to add <code>{`raw-as-base64-in-summary`}</code> in your schema file to get this behavior.</td> + <td>If you have fields of type "raw" and you must have the old summary behavior for them in search results, add the line <code>{`raw-as-base64-in-summary : false`}</code> in your schema definition.</td> + </tr> + <tr> + <td>The default tensor format in responses has changed from 'long' to 'short': Tensors in query results, document API responses, and stateless model evaluation are rendered in the short form appropriate for their type (if any), documented <a href="/en/reference/schemas/document-json-format#tensor" aria-label="here">here</a>.</td> + <td>**Queries**: Pass <a href="/en/reference/api/query#presentation.format.tensors" aria-label="presentation.format.tensors">presentation.format.tensors</a>=long in queries, or set it parameter in the relevant <a href="/en/querying/query-profiles" aria-label="query profiles">query profiles</a>. <br/><br/> **Document/v1**: Pass the parameter <code>{`format.tensors=long`}</code> in requests. <br/><br/> **Stateless model evaluation**: Pass the parameter <code>{`format.tensors=long`}</code> in requests.</td> + </tr> + <tr> + <td>The default fieldset when getting or visiting documents is now <code>{`[document]`}</code> in all cases, meaning you only get those fields that are declared in the "document" block of the schema (generated fields are not included). This was already the default for the <code>{`/document/v1`}</code> API when fetching or visiting documents of a single known document type. Now it is also the default when visiting at the root level, for the command line tools <code>{`vespa-visit`}</code> and <code>{`vespa-get`}</code>, and if you use the programmatic <code>{`documentapi`}</code> from java to fetch documents.</td> + <td>In most cases there is no difference between <code>{`[all]`}</code> and <code>{`[document]`}</code> fieldsets - so no action is needed. If the old behavior is needed you can:<br/><br/> • For the command line tools, specify the fieldset as <code>{`-l "[all]"`}</code> to include generated fields.<br/> • For <code>{`/document/v1`}</code> specify <code>{`[all]`}</code> as the value for the <code>{`fieldSet`}</code> parameter.<br/>• If using <code>{`documentapi`}</code> from java, add the line <code>{`params.setFieldSet("[all]");`}</code> to modify your <code>{`VisitorParameters`}</code> value , or <code>{`params = params.withFieldSet("[all]");`}</code> to modify your <code>{`DocumentOperationParameters`}</code> value. <br/><br/>If you run document processors to generate fields and want those returned, it may be more useful to declare a fieldset with just those fields you actually want as output instead.</td> + </tr> + <tr> + <td>Vespa will now limit the number of groups and hits in <a href="/en/querying/grouping" aria-label="grouping query results">grouping query results</a> when <code>{`max`}</code> is not specified explicitly in grouping expressions. The default value is determined by <a href="/en/reference/api/query#grouping.defaultmaxgroups" aria-label="grouping.defaultMaxGroups">grouping.defaultMaxGroups</a>/ <a href="/en/reference/api/query#grouping.defaultmaxhits" aria-label="grouping.defaultMaxHits">grouping.defaultMaxHits</a>. The parameter <a href="/en/reference/api/query#grouping.globalmaxgroups" aria-label="grouping.globalMaxGroups">grouping.globalMaxGroups</a> must now be overridden in query profiles to allow grouping expressions that may return unbounded or large results.</td> + <td>• <a href="/en/reference/api/query#grouping.defaultmaxgroups" aria-label="grouping.defaultMaxGroups">grouping.defaultMaxGroups</a> changed from <code>{`-1`}</code> to <code>{`10`}</code>. <br/>• <a href="/en/reference/api/query#grouping.defaultmaxhits" aria-label="grouping.defaultMaxHits">grouping.defaultMaxHits</a> changed from <code>{`-1`}</code> to <code>{`10`}</code>. <br/>• <a href="/en/reference/api/query#grouping.globalmaxgroups" aria-label="grouping.globalMaxGroups">grouping.globalMaxGroups</a> changed from <code>{`-1`}</code> to <code>{`10000`}</code>. <br/>• <a href="/en/reference/api/query#grouping.defaultprecisionfactor" aria-label="grouping.defaultPrecisionFactor">grouping.defaultPrecisionFactor</a> changed from <code>{`1.0`}</code> to <code>{`2.0`}</code>.</td> + </tr> + <tr> + <td>Vespa <a href="/en/operations/access-logging" aria-label="access logs">access logs</a> are compressed with <a href="https://github.com/facebook/zstd" aria-label="zstd">zstd</a>.</td> + <td>Add a config override under <code>{`<container>`}</code> in <code>{`services.xml`}</code>: <br/><br/> <code>{`<config name="container.core.access-log">`}</code><br/> <code>{`<fileHandler>`}</code> <br/><code>{`<compressionFormat>GZIP<compressionFormat>`}</code></td> + </tr> + </tbody> +</table> + +## Application package changes + +### Removed settings from schemas + +The following settings are removed from [schema](/en/reference/schemas/schemas): + +<table className="vespa-table"> + <thead> + <tr> + <th>Name</th> + <th>Replacement</th> + </tr> + </thead> + <tbody> + <tr> + <td>attribute: huge</td> + <td>None. Setting *huge* on an attribute doesn't have any effect, the code is rewritten to support it by default.</td> + </tr> + <tr> + <td><a href="/en/reference/schemas/schemas#compression" aria-label="compression">compression</a></td> + <td>None. Document compression is not needed, as compression is always enabled.</td> + </tr> + <tr> + <td>body (inside a field definition)</td> + <td>None. Deprecated since before Vespa 7, had no effect in Vespa 7.</td> + </tr> + <tr> + <td>header (inside a field definition)</td> + <td>None. Deprecated since before Vespa 7, had no effect in Vespa 7.</td> + </tr> + <tr> + <td>field type weightedset<code>{`<float>`}</code></td> + <td>Because floating-point types are inherently imprecise they are badly suited as keys in maps and sets. If you feel the need for such data consider using something like:<br/><br/> <code>{`struct weightedfloat {`}</code><br/> <code>{`field value type float {}`}</code><br/> <code>{`field weight type int {}`}</code><br/> <code>{`}`}</code> <br/> <code>{`field myfield type array<weightedfloat> {`}</code> <br/> <code>{`...`}</code></td> + </tr> + <tr> + <td>field type map<code>{`<float,anything>`}</code></td> + <td>Using "float" as the key in a map is no longer supported, see <code>{`weightedset<float>`}</code> above.</td> + </tr> + <tr> + <td>field type weightedset<code>{`<double>`}</code></td> + <td>Using "double" as the key in a set is no longer supported, see <code>{`weightedset<float>`}</code> above.</td> + </tr> + <tr> + <td>field type map<code>{`<double,anything>`}</code></td> + <td>Using "double" as the key in a map is no longer supported, see <code>{`weightedset<float>`}</code> above.</td> + </tr> + <tr> + <td>field type weightedset<code>{`<uri>`}</code></td> + <td>Using complex types as the key in a set is no longer supported, see <code>{`weightedset<float>`}</code> above.</td> + </tr> + <tr> + <td>field type map<code>{`<uri,anything>`}</code></td> + <td>Using complex types as the key in a map is no longer supported, see <code>{`weightedset<float>`}</code> above.</td> + </tr> + <tr> + <td>Old syntax for array types like "string[]"</td> + <td>Write as <code>{`array<string>`}</code> instead.</td> + </tr> + <tr> + <td>Rank functions must have different names in a rank-profile</td> + <td>Only the last of two functions with the same name would be used. Remove or rename the first one.</td> + </tr> + <tr> + <td>Conflicting sorting settings are now rejected</td> + <td>Only keep the last of the conflicting settings.</td> + </tr> + <tr> + <td>A summary-field may only be added once in a document-summary block</td> + <td>Remove duplicates.</td> + </tr> + <tr> + <td>Schema and document should have the same name</td> + <td>Change name of the schema, so it is equal to the contained document.</td> + </tr> + </tbody> +</table> + +### TensorFlow import + +Vespa 8 removes support for direct import of [TensorFlow models](/en/ranking/tensorflow). [ONNX](https://onnx.ai/) is now the preferred ML model format, and works both for [ranking](/en/ranking/onnx) and [stateless model evaluation](/en/ranking/stateless-model-evaluation). ONNX contains tools to convert models from TensorFlow to ONNX, but Vespa will no longer provide this. + +### Changed semantics in services.xml + +The following elements and attributes in services.xml have new semantics: + +<table className="vespa-table"> + <thead> + <tr> + <th>Name</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td><code>{`<nodes><redundancy>`}</code></td> + <td>It is now an error to configure a number of nodes (per group) that is smaller than the configured redundancy. It used to generate an application-level warning, with the redundancy implicitly reduced. Remove any <code>{`<nodes>`}</code> override in the non-prod environments, as the node count is automatically adjusted.</td> + </tr> + </tbody> +</table> + +### Removed constructs from services.xml + +The following elements and attributes are removed from services.xml: + +<table className="vespa-table"> + <thead> + <tr> + <th>Parent element</th> + <th>Removed construct</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td><code>{`<admin>`}</code></td> + <td><code>{`<filedistribution>`}</code></td> + <td>Configuring up/download rates is not supported</td> + </tr> + <tr> + <td></td> + <td><code>{`<configserver>`}</code></td> + <td>Use <a href="/en/reference/applications/services/admin#configservers" aria-label="`configservers`"><code>{`configservers`}</code></a> element instead</td> + </tr> + <tr> + <td><code>{`<config>`}</code></td> + <td>*namespace* attribute</td> + <td>The namespace must be included in the *name* attribute.</td> + </tr> + <tr> + <td></td> + <td><code>{`<myArray operation="append">`}</code> syntax</td> + <td>Previously used to append items to config arrays. Use <a href="/en/reference/applications/config-files#configuring-arrays" aria-label="`item`"><code>{`item`}</code></a> instead.</td> + </tr> + <tr> + <td><code>{`<container>`}</code></td> + <td>*jetty* attribute</td> + <td>Removed, had no effect on Vespa 7.</td> + </tr> + <tr> + <td></td> + <td><code>{`<nodes>`}</code> jvm attributes</td> + <td>JVM attributes *jvmargs, allocated-memory, jvm-options, jvm-gc-options* renamed and moved to <a href="/en/reference/applications/services/container#jvm" aria-label="`JVM`"><code>{`JVM`}</code></a> subelement</td> + </tr> + <tr> + <td></td> + <td><code>{`<client>`}</code></td> + <td>Previously used for setting up client providers. Use a <a href="/en/applications/request-handlers" aria-label="`request handler`"><code>{`request handler`}</code></a> instead.</td> + </tr> + <tr> + <td></td> + <td><code>{`<handler><clientBinding>`}</code></td> + <td>Client bindings are no longer supported.</td> + </tr> + <tr> + <td><code>{`<content>`}</code></td> + <td><code>{`<dispatch>`}</code></td> + <td>Removed due to removal of *vespa-dispatch-bin*, <a href="#vespa-dispatch-bin-process-is-removed" aria-label="details.">details.</a></td> + </tr> + <tr> + <td></td> + <td><code>{`<tuning><dispatch><min-group-coverage>`}</code></td> + <td>Use <a href="/en/reference/applications/services/content#min-active-docs-coverage" aria-label="`min-active-docs-coverage`"><code>{`min-active-docs-coverage`}</code></a> instead.</td> + </tr> + <tr> + <td></td> + <td><code>{`<tuning><dispatch><use-local-node>`}</code></td> + <td>Ignored, the local node will automatically be preferred when appropriate.</td> + </tr> + <tr> + <td></td> + <td><code>{`<engine><proton><tuning><searchnode><flushstrategy><native><transactionlog><maxentries>`}</code></td> + <td>Use <a href="/en/reference/applications/services/content#flushstrategy-native-transactionlog-maxsize" aria-label="`maxsize`"><code>{`maxsize`}</code></a> instead. Vespa 7 documentation: The maximum number of entries in the <a href="/en/content/proton#transaction-log" aria-label="`transaction log`"><code>{`transaction log`}</code></a> for a document type before running flush, default 1000000 (1 M).</td> + </tr> + <tr> + <td></td> + <td><code>{`<engine><proton><tuning><searchnode><summary><store><logstore><chunk><maxentries>`}</code></td> + <td>Use <a href="/en/reference/applications/services/content#summary-store-logstore-chunk-maxsize" aria-label="`maxsize`"><code>{`maxsize`}</code></a> instead. Vespa 7 documentation: Maximum number of documents in a chunk. See *summary.log.chunk.maxentries*.</td> + </tr> + <tr> + <td><code>{`<services>`}</code> (root)</td> + <td><code>{`<jdisc>`}</code></td> + <td>Use <a href="/en/reference/applications/services/container" aria-label="`container`"><code>{`container`}</code></a> instead.</td> + </tr> + <tr> + <td></td> + <td><code>{`<service>`}</code></td> + <td>Running generic services is no longer supported.</td> + </tr> + <tr> + <td></td> + <td><code>{`<clients>`}</code></td> + <td>Client load types are deprecated and ignored.</td> + </tr> + </tbody> +</table> + +### *application/* folder support removed + +Application package content (*services.xml*, the *schemas/* folder, etc.) is supposed to be put at the root level in the application zip, such that the unzipped application package has *./services.xml*, etc. + +But it used to be that the application package content could be placed inside an *application* directory. This support is removed on Vespa 8. + +### *searchdefinitions/* folder is deprecated + +Search definition schemas should now be placed in the *schemas/* folder. The old folder will still work on Vespa 8, but causes a deprecation warning upon deployment. + +## Java API changes + +### Removed Java packages + +<table className="vespa-table"> + <thead> + <tr> + <th>Package</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>*com.yahoo.docproc.util*</td> + <td>Removed</td> + </tr> + <tr> + <td>*com.yahoo.jdisc.test*</td> + <td>No longer <a href="https://javadoc.io/doc/com.yahoo.vespa/annotations/latest/com/yahoo/api/annotations/PublicApi.html" aria-label="public API">public API</a></td> + </tr> + <tr> + <td>*com.yahoo.log.event*</td> + <td>No longer <a href="https://javadoc.io/doc/com.yahoo.vespa/annotations/latest/com/yahoo/api/annotations/PublicApi.html" aria-label="public API">public API</a></td> + </tr> + <tr> + <td>*com.yahoo.statistics*</td> + <td>Removed</td> + </tr> + <tr> + <td>*com.yahoo.vespa.curator*</td> + <td>No longer <a href="https://javadoc.io/doc/com.yahoo.vespa/annotations/latest/com/yahoo/api/annotations/PublicApi.html" aria-label="public API">public API</a></td> + </tr> + <tr> + <td>*com.yahoo.documentapi.messagebus.loadtypes*</td> + <td>Load types are no longer supported. Use corresponding method overloads without *LoadType* or *LoadTypeSet* parameters instead.</td> + </tr> + </tbody> +</table> + +### Removed Java Classes and methods + +Classes and methods that were marked as deprecated in Vespa 7 are removed. If deprecation warnings are emitted for Vespa APIs when building the application, these must be fixed before migrating to Vespa 8. The sections below contain only the most notable changes. + +The following classes are no longer public API and have been moved to Vespa internal packages: + +<table className="vespa-table"> + <thead> + <tr> + <th>Package</th> + <th>Class</th> + <th>Migration advice</th> + </tr> + </thead> + <tbody> + <tr> + <td>*com.yahoo.config.subscription*</td> + <td>All classes, except <a href="https://javadoc.io/doc/com.yahoo.vespa/config/latest/com/yahoo/config/subscription/ConfigGetter.html" aria-label="`ConfigGetter`"><code>{`ConfigGetter`}</code></a></td> + <td>Config should be <a href="/en/applications/configuring-components#use-config-in-code" aria-label="`injected`"><code>{`injected`}</code></a> to your component class constructor.</td> + </tr> + <tr> + <td>*com.yahoo.docproc*</td> + <td>*DocprocExecutor*</td> + <td>For unit tests, follow the steps in the <a href="https://github.com/vespa-engine/sample-apps/blob/master/examples/document-processing/src/test/java/ai/vespa/example/album/ProductTypeRefinerDocProcTest.java" aria-label="`document-processing`"><code>{`document-processing`}</code></a> sample app. If you need a *DocumentTypeManager* in production code, it can be directly <a href="/en/applications/dependency-injection" aria-label="`injected`"><code>{`injected`}</code></a> to your component class constructor.</td> + </tr> + <tr> + <td></td> + <td>*DocprocService*</td> + <td>For unit tests, follow the steps in the <a href="https://github.com/vespa-engine/sample-apps/blob/master/examples/document-processing/src/test/java/ai/vespa/example/album/ProductTypeRefinerDocProcTest.java" aria-label="`document-processing`"><code>{`document-processing`}</code></a> sample app. If you need a *DocumentTypeManager* in production code, it can be directly <a href="/en/applications/dependency-injection" aria-label="`injected`"><code>{`injected`}</code></a> to your component class constructor.</td> + </tr> + <tr> + <td></td> + <td>*DocumentOperationWrapper*</td> + <td>No replacement - if needed, contact the Vespa team for advice.</td> + </tr> + <tr> + <td></td> + <td>*HandledProcessingException*</td> + <td></td> + </tr> + <tr> + <td></td> + <td>*ProcessingEndpoint*</td> + <td></td> + </tr> + <tr> + <td></td> + <td>*TransientFailureException*</td> + <td></td> + </tr> + <tr> + <td>*com.yahoo.log*</td> + <td>*VespaFormatter*</td> + <td>No replacement.</td> + </tr> + </tbody> +</table> + +The following methods are removed: + +<table className="vespa-table"> + <thead> + <tr> + <th>Method</th> + <th>Migration advice</th> + </tr> + </thead> + <tbody> + <tr> + <td>*com.yahoo.documentapi.DocumentAccess.createDefault()*</td> + <td>Container components can have a *DocumentAccess* injected via their constructor. For use outside the container, e.g. in a custom command line tool, use the new method *createForNonContainer()*.</td> + </tr> + <tr> + <td>*com.yahoo.log.LogSetup.getLogHandler()*</td> + <td>No replacement.</td> + </tr> + </tbody> +</table> + +### Breaking changes to Java APIs + +The Javadoc of the deprecated types/members should document the replacement API. The below list is not exhaustive - some smaller and trivial changes are not listed. + +<table className="vespa-table"> + <thead> + <tr> + <th>Type(s)</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>*com.yahoo.processing*</td> + <td>Removed use of Guava's *ListenableFuture* in type signatures. Replacement uses *CompletableFuture*.</td> + </tr> + <tr> + <td>*com.yahoo.search.handler.HttpSearchResponse.waitableRender()*</td> + <td>Removed use of Guava's *ListenableFuture* in type signature. The method is replaced with *asyncRender()*.</td> + </tr> + <tr> + <td>*com.yahoo.jdisc.handler*</td> + <td>Removed use of Guava's *ListenableFuture* in type signatures. Replacement uses *CompletableFuture*</td> + </tr> + <tr> + <td>*com.yahoo.searchlib.rankingexpression.rule*</td> + <td>Removed use of Guava collection types in type signatures.</td> + </tr> + <tr> + <td>*com.yahoo.search.rendering.JsonRenderer*</td> + <td>Removed use of Jackson types from class signature.</td> + </tr> + <tr> + <td>*com.yahoo.jdisc.Container*</td> + <td>Removed use of Guice types from class signature.</td> + </tr> + <tr> + <td>*com.yahoo.vdslib.VisitorStatistics*</td> + <td>Removed all *set/getSecondPass*-related methods.</td> + </tr> + <tr> + <td>*com.yahoo.documentapi*</td> + <td>Removed all methods taking in a *com.yahoo.documentapi.messagebus.DocumentProtocol.Priority* argument. Explicit operation priorities are deprecated and should not be set by the client.</td> + </tr> + </tbody> +</table> + +### Removed support for built-in XML factories + +The Jdisc container has historically supported injection of built-in providers for the following XML factories: + +- *javax.xml.datatype.DatatypeFactory* +- *javax.xml.parsers.DocumentBuilderFactory* and *SAXParserFactory* +- *javax.xml.stream.XMLEventFactory*, *XMLInputFactory* and *XMLOutputFactory* +- *javax.xml.transform.TransformerFactory* +- *javax.xml.validation.SchemaFactory* +- *javax.xml.xpath.XPathFactory* + +These are now removed. Please check for more recent alternatives if you need this type of XML processing. + +### Deprecated Java APIs + +A few redundant APIs have been deprecated because they have replacements that provide the same, or better, functionality. We advise you switch to the replacement to reduce future maintenance cost. + +<table className="vespa-table"> + <thead> + <tr> + <th>Type(s)</th> + <th>Replacement</th> + </tr> + </thead> + <tbody> + <tr> + <td>*com.yahoo.container.jdisc.LoggingRequestHandler*</td> + <td>Use *com.yahoo.container.jdisc.ThreadedHttpRequestHandler* instead.</td> + </tr> + <tr> + <td>*com.yahoo.log.LogLevel*</td> + <td>Use *java.util.logging.Level* instead.</td> + </tr> + </tbody> +</table> + +## Container Runtime Environment + +### JDK version + +Vespa 8 upgrades the JDK version from 11 to 17. To ensure full compatibility, all container components should be rebuilt with JDK 17 before being deployed on Vespa 8. + +### Changes to provided maven artifacts + +[Guava](https://search.maven.org/artifact/com.google.guava/guava) has been upgraded from version 20.0 to 27.1. If you are using APIs that have been removed from the library since version 20, your code must be updated. In most cases, it should be trivial to find replacement APIs in Java's standard library. + +The following Maven artifacts are no longer provided runtime to user application plugins by the Jdisc container: + +<table className="vespa-table"> + <thead> + <tr> + <th>Artifact</th> + <th>Notes</th> + </tr> + </thead> + <tbody> + <tr> + <td><a href="https://search.maven.org/artifact/com.fasterxml.jackson.jaxrs/jackson-jaxrs-base" aria-label="`*com.fasterxml.jackson.jaxrs:jackson-jaxrs-base*`"><code>{`*com.fasterxml.jackson.jaxrs:jackson-jaxrs-base*`}</code></a></td> + <td>JSON input/output handling for JAX-RS implementations, e.g. Jersey</td> + </tr> + <tr> + <td><a href="https://search.maven.org/artifact/com.fasterxml.jackson.jaxrs/jackson-jaxrs-json-provider" aria-label="`*com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider*`"><code>{`*com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider*`}</code></a></td> + <td>JSON input/output handling for JAX-RS implementations, e.g. Jersey</td> + </tr> + <tr> + <td><a href="https://search.maven.org/artifact/com.fasterxml.jackson.module/jackson-module-jaxb-annotations" aria-label="`*com.fasterxml.jackson.module:jackson-module-jaxb-annotations*`"><code>{`*com.fasterxml.jackson.module:jackson-module-jaxb-annotations*`}</code></a></td> + <td>Jackson data binding with JAXB annotations.</td> + </tr> + <tr> + <td><a href="https://search.maven.org/artifact/com.google.code.findbugs/jsr305" aria-label="`*com.google.code.findbugs:jsr305*`"><code>{`*com.google.code.findbugs:jsr305*`}</code></a></td> + <td>Annotations in package *javax.annotation[.*]*, e.g. *Nullable* and *Nonnnull*.</td> + </tr> + <tr> + <td><a href="https://search.maven.org/artifact/com.google.inject.extensions/guice-assistedinject" aria-label="`*com.google.inject.extensions:guice-assistedinject*`"><code>{`*com.google.inject.extensions:guice-assistedinject*`}</code></a></td> + <td>Guice extensions.<br />For component injection see <a href="/en/applications/dependency-injection#depending-on-another-component" aria-label="Depending on another component">Depending on another component</a></td> + </tr> + <tr> + <td><a href="https://search.maven.org/artifact/com.google.inject.extensions/guice-multibindings" aria-label="`*com.google.inject.extensions:guice-multibindings*`"><code>{`*com.google.inject.extensions:guice-multibindings*`}</code></a></td> + <td>Guice extensions.</td> + </tr> + <tr> + <td><a href="https://search.maven.org/artifact/javax.annotation/javax.annotation-api" aria-label="`*javax.annotation:javax.annotation-api*`"><code>{`*javax.annotation:javax.annotation-api*`}</code></a></td> + <td>Annotations in package *javax.annotation[.*]*, e.g. *ManagedBean* and *Resource*.</td> + </tr> + <tr> + <td><a href="https://search.maven.org/artifact/javax.validation/validation-api" aria-label="`*javax.validation:validation-api*`"><code>{`*javax.validation:validation-api*`}</code></a></td> + <td>Javax bean validation, used by Jersey 2.</td> + </tr> + <tr> + <td><a href="https://search.maven.org/search?q=g:org.eclipse.jetty" aria-label="`*org.eclipse.jetty:*`"><code>{`*org.eclipse.jetty:*`}</code></a></td> + <td>The Eclipse Jetty Project.</td> + </tr> + <tr> + <td><a href="https://search.maven.org/artifact/org.apache.felix/org.apache.felix.framework" aria-label="`*org.apache.felix:org.apache.felix.framework*`"><code>{`*org.apache.felix:org.apache.felix.framework*`}</code></a></td> + <td>Felix OSGi framework.</td> + </tr> + <tr> + <td>*org.apache.felix:org.apache.felix.log*</td> + <td>Felix OSGi framework.</td> + </tr> + <tr> + <td><a href="https://search.maven.org/artifact/org.apache.felix/org.apache.felix.main" aria-label="`*org.apache.felix:org.apache.felix.main*`"><code>{`*org.apache.felix:org.apache.felix.main*`}</code></a></td> + <td>Felix OSGi framework.</td> + </tr> + <tr> + <td><a href="https://search.maven.org/artifact/org.bouncycastle/bcpkix-jdk15on" aria-label="`*org.bouncycastle:bcpkix-jdk15on*`"><code>{`*org.bouncycastle:bcpkix-jdk15on*`}</code></a></td> + <td>Bouncy Castle crypto API.</td> + </tr> + <tr> + <td><a href="https://search.maven.org/artifact/org.bouncycastle/bcprov-jdk15on" aria-label="`*org.bouncycastle:bcprov-jdk15on*`"><code>{`*org.bouncycastle:bcprov-jdk15on*`}</code></a></td> + <td>Bouncy Castle crypto provider.</td> + </tr> + <tr> + <td>*org.glassfish.*:*</td> + <td>Jersey 2. All related artifacts are removed.</td> + </tr> + <tr> + <td><a href="https://search.maven.org/artifact/org.json/json" aria-label="`*org.json:json*`"><code>{`*org.json:json*`}</code></a></td> + <td>See <a href="https://github.com/vespa-engine/vespa/issues/14762" aria-label="vespa-engine/vespa#14762">vespa-engine/vespa#14762</a></td> + </tr> + <tr> + <td><a href="https://search.maven.org/artifact/org.javassist/javassist" aria-label="`*org.javassist:javassist*`"><code>{`*org.javassist:javassist*`}</code></a></td> + <td>Bytecode manipulation, used by Jersey 2.</td> + </tr> + <tr> + <td><a href="https://search.maven.org/artifact/org.jvnet.mimepull/mimepull" aria-label="`*org.jvnet.mimepull:mimepull*`"><code>{`*org.jvnet.mimepull:mimepull*`}</code></a></td> + <td>MIME Streaming Extension, used by Jersey 2.</td> + </tr> + <tr> + <td><a href="https://search.maven.org/artifact/org.lz4/lz4-java" aria-label="`*org.lz4:lz4-java*`"><code>{`*org.lz4:lz4-java*`}</code></a></td> + <td>Compression library.</td> + </tr> + </tbody> +</table> + +Make sure your application OSGi bundle embeds the required artifacts from the above list. An artifact can be embedded by adding it in scope *compile* to the *dependencies* section in pom.xml. Typically, these artifacts have until now been used in scope *provided*. Use `mvn dependency:tree` to check whether any of the listed artifacts are directly or transitively included as dependencies. + +As always, remove any dependencies that are not required by your project. Consult the Maven documentation on [Dependency Exclusions](https://maven.apache.org/guides/introduction/introduction-to-optional-and-excludes-dependencies.html#dependency-exclusions) for how to remove a transitively included dependency. + +An example adding *org.json:json* as a compile scoped dependency: + +```xml +<dependencies> + ... + <dependency> + <groupId>org.json</groupId> + <artifactId>json</artifactId> + <version>20211205</version> + <scope>compile</scope> + </dependency> + ... +</dependencies> +``` + +## Removed HTTP API parameters + +The following HTTP API parameters are removed from the [query API](/en/reference/api/query): + +<table className="vespa-table"> + <thead> + <tr> + <th>Standard API path</th> + <th>Parameter name</th> + <th>Replacement</th> + </tr> + </thead> + <tbody> + <tr> + <td>/search/</td> + <td>*pos.ll*</td> + <td>add a <a href="/en/reference/querying/yql#geolocation" aria-label="geoLocation">geoLocation</a> item to the query</td> + </tr> + <tr> + <td>/search/</td> + <td>*pos.radius*</td> + <td>add a <a href="/en/reference/querying/yql#geolocation" aria-label="geoLocation">geoLocation</a> item to the query</td> + </tr> + <tr> + <td>/search/</td> + <td>*pos.attribute*</td> + <td>add a <a href="/en/reference/querying/yql#geolocation" aria-label="geoLocation">geoLocation</a> item to the query</td> + </tr> + <tr> + <td>/search/</td> + <td>*pos.bb*</td> + <td>Support for restricting search by a bounding box, using the <code>{`pos.bb`}</code> query parameter, has been removed - add a <a href="/en/reference/querying/yql#geolocation" aria-label="geoLocation">geoLocation</a> item to the query</td> + </tr> + </tbody> +</table> + +## Removed command line tools + +### vespa-http-client + +The `vespa-http-client` command line tool is removed on Vespa 8 and is replaced by the new [vespa-feed-client](/en/clients/vespa-feed-client). The new client uses [HTTP/2](/en/performance/http2) and the [Document v1 API](/en/writing/document-v1-api-guide). + +The underlying rest API used by the vespa-http-client will still be available and supported on Vespa 8. You can therefore continue to use an old client distributed with Vespa 7 to feed to a Vespa 8 installation. Note that there will not be released any updates for vespa-http-client after the initial Vespa 8 release, while fixes and security updates to the rest API implementation will continue as part of Vespa 8. We strongly recommend that you migrate away from vespa-http-client in a timely manner. + +## Removed or renamed metrics + +The following metrics are renamed: + +<table className="vespa-table"> + <thead> + <tr> + <th>Old Name</th> + <th>New name</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>*vds.filestor.alldisks.**</td> + <td>vds.filestor.*</td> + <td>*alldisks* has been removed from the metric name.</td> + </tr> + <tr> + <td>*vds.visitor.*.sum.**</td> + <td>vds.visitor.*.*</td> + <td>*sum* has been removed from the metric name.</td> + </tr> + <tr> + <td>*vds.filestor.*.sum.**</td> + <td>vds.filestor.*.*</td> + <td>*sum* has been removed from the metric name.</td> + </tr> + <tr> + <td>*vds.distributor.*.sum.**</td> + <td>vds.distributor.*.*</td> + <td>*sum* has been removed from the metric name.</td> + </tr> + </tbody> +</table> + +The following metrics are removed: + +<table className="vespa-table"> + <thead> + <tr> + <th>Name</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>*http.status.401.rate*</td> + <td>Use *http.status.4xx.rate* with dimension *statusCode*==401</td> + </tr> + <tr> + <td>*http.status.403.rate*</td> + <td>Use *http.status.4xx.rate* with dimension *statusCode*==403</td> + </tr> + <tr> + <td>*content.proton.documentdb.matching.query_collateral_time.**</td> + <td>Use *content.proton.documentdb.matching.query_setup_time.** instead</td> + </tr> + <tr> + <td>*content.proton.documentdb.matching.rank_profile.query_collateral_time.**</td> + <td>Use *content.proton.documentdb.matching.rank_profile.query_setup_time.** instead</td> + </tr> + <tr> + <td>*vds.visitor.allthreads.averagevisitorlifetime~~.sum~~.average*</td> + <td>Use .sum/.count instead</td> + </tr> + <tr> + <td>*vds.visitor.allthreads.averagequeuewait~~.sum~~.average*</td> + <td>Use .sum/.count instead</td> + </tr> + <tr> + <td>*vds.visitor.allthreads.queuesize~~.sum~~.average*</td> + <td>Use .sum/.count instead</td> + </tr> + <tr> + <td>*vds.visitor.allthreads.completed~~.sum~~.average*</td> + <td>Use .sum/.count instead</td> + </tr> + <tr> + <td>*vds.visitor.allthreads.averagemessagesendtime~~.sum~~.average*</td> + <td>Use .sum/.count instead</td> + </tr> + <tr> + <td>*vds.visitor.allthreads.averageprocessingtime~~.sum~~.average*</td> + <td>Use .sum/.count instead</td> + </tr> + <tr> + <td>*vds.filestor~~.alldisks~~.queuesize.average*</td> + <td>Use .sum/.count instead</td> + </tr> + <tr> + <td>*vds.filestor~~.alldisks~~.averagequeuewait.average*</td> + <td>Use .sum/.count instead</td> + </tr> + <tr> + <td>*vds.filestor~~.alldisks~~.allthreads.put~~.sum~~.latency.average*</td> + <td>Use .sum/.count instead</td> + </tr> + <tr> + <td>*vds.filestor~~.alldisks~~.allthreads.remove~~.sum~~.latency.average*</td> + <td>Use .sum/.count instead</td> + </tr> + <tr> + <td>*vds.filestor~~.alldisks~~.allthreads.get~~.sum~~.latency.average*</td> + <td>Use .sum/.count instead</td> + </tr> + <tr> + <td>*vds.filestor~~.alldisks~~.allthreads.update~~.sum~~.latency.average*</td> + <td>Use .sum/.count instead</td> + </tr> + <tr> + <td>*vds.filestor~~.alldisks~~.allthreads.createiterator~~.sum~~.latency.average*</td> + <td>Use .sum/.count instead</td> + </tr> + <tr> + <td>*vds.filestor~~.alldisks~~.allthreads.visit~~.sum~~.latency.average*</td> + <td>Use .sum/.count instead</td> + </tr> + <tr> + <td>*vds.filestor~~.alldisks~~.allthreads.remove_location~~.sum~~.latency.average*</td> + <td>Use .sum/.count instead</td> + </tr> + <tr> + <td>*vds.filestor~~.alldisks~~.allthreads.deletebuckets~~.sum~~.latency.average*</td> + <td>Use .sum/.count instead</td> + </tr> + <tr> + <td>*vds.distributor.puts~~.sum~~.latency.average*</td> + <td>Use .sum/.count instead</td> + </tr> + <tr> + <td>*vds.distributor.removes~~.sum~~.latency.average*</td> + <td>Use .sum/.count instead</td> + </tr> + <tr> + <td>*vds.distributor.updates~~.sum~~.latency.average*</td> + <td>Use .sum/.count instead</td> + </tr> + <tr> + <td>*vds.distributor.gets~~.sum~~.latency.average*</td> + <td>Use .sum/.count instead</td> + </tr> + <tr> + <td>*vds.distributor.visitor~~.sum~~.latency.average*</td> + <td>Use .sum/.count instead</td> + </tr> + </tbody> +</table> + +## Exact matching of document types in selection language + +The [document selection language](/en/reference/writing/document-selector-language) now uses *exact* matching for document types rather than *inheritance* ("is-a") matching. + +Example with two minimal document schemas: + +- `document my_doc_type {}` +- `document my_extended_doc_type inherits my_doc_type {}` + +Previously, the selection expression `my_doc_type` would match both a document instance of type `my_doc_type` *and* `my_extended_doc_type`. It will now *only* match a document of type `my_doc_type`. + +## Security + +### Strict mode enabled by default + +*Strict mode* for request filtering in the jdisc container is enabled by default in Vespa 8. See documentation on the [strict-mode](/en/reference/applications/services/http#filtering) attribute in services.xml for details. + +### Request headers controlling remote host/port in access log + + +The jdisc container will use the *X-Forwarded-For* and *X-Forwarded-Port* request headers to set the remote host and port respectively in the access log. The following request headers will no longer be handled by default: + +- y-ra +- yahooremoteip +- client-ip +- y-rp + +## Operating system support for Vespa artifacts + +### RPMs + +The supported OS for Vespa RPMs changes from [CentOS Linux 7](https://www.centos.org/centos-linux/) to [CentOS Stream 8](https://www.centos.org/centos-stream/) for Vespa 8. RPMs will still be built and distributed on [Fedora Copr](https://copr.fedorainfracloud.org/coprs/g/vespa/vespa/). If you install Vespa RPMs you will have to upgrade your OS to [CentOS Stream 8](https://www.centos.org/centos-stream/). + +### OCI containers (Docker containers) + +The base image used in our OCI containers changes from [docker.io/centos:7](https://hub.docker.com/_/centos) to [quay.io/centos/centos:stream8](https://quay.io/repository/centos/centos?tab=tags) for Vespa 8. This means that the container image is built and tested on systems running kernel version *4.18.0* (current kernel for CentOS Stream 8). If you use Vespa's container image, you should upgrade the hosts running the containers to the same or a newer kernel version. + +## Other changes + +### Unknown rank profiles + +Queries that specify a rank profile which does not exist in all the schemas being queried will now fail instead of falling back to using the `default` profile. Queries to multiple schemas must use a rank profile that exists in all of them, which can be ensured by [inheriting](/en/schemas/inheritance-in-schemas) a common schema. + +### Unknown summary classes + +Queries that specify a non-existent [summary class](/en/querying/document-summaries) will now fail, instead of being rendered empty. Queries to multiple schemas must use a summary class that exists in all of them, which can be ensured by [inheriting](/en/schemas/inheritance-in-schemas) a common schema. + +### The "qrserver" service name + +Vespa containers are in general using "container" as their service name. However, a container cluster that has declared neither [document-processing](/en/reference/applications/services/docproc) nor [document-api](/en/reference/applications/services/container#document-api) used to be named "qrserver". On Vespa 8 all container clusters uses the service name "container". This affects the output of all metrics APIs, as well as the Vespa log output. + +### Container access logs + +The folder for container access logs has been moved from `$VESPA_HOME/logs/vespa/qrs/` to `$VESPA_HOME/logs/vespa/access/`. + +The default compression format has changed from gzip to zstd, see [changed defaults](#changed-defaults). + +### ONNX output in summary features + +When defining an ONNX model output in summary features, Vespa 8 ensures that the summary feature name is `onnx` rather than `onnxModel` as in previous version. + +### Changes in rankfeatures + +Vespa can calculate and return all [rank-features](/en/reference/api/query#ranking.listfeatures) in the `rankfeatures` summary field. Vespa 8 contains some changes to this list: + +- `now` is removed +- `bm25(field)` is added +- `matches(field)` is added + +### The "storage" message bus routing policy is removed + +The "storage" routing policy was removed in early Vespa 7, and clients specifying it have been forwarded to the content routing policy for backwards compatibility. The forwarding is removed on Vespa 8 and clients needs be updated. + +Replace all usages of the "storage" policy with "content", which behaves identically. + +### vespa-dispatch-bin process is removed + +The dispatch functionality is moved into the Vespa Container and the *vespa-dispatch-bin* process is removed. As this is not a public interface, the default was switched to **not** using vespa-dispatch-bin in Vespa-7.109.10. The process was removed in subsequent Vespa releases: + +<table className="vespa-table"> + <thead> + <tr> + <th>||||</th> + </tr> + </thead> + <tbody> + <tr> + <td><a href="/en/querying/query-api" aria-label="Dispatch">Dispatch</a> | Content cluster | dynamically allocated in 19100 - 19899 range | <code>{`$VESPA_HOME/sbin/vespa-dispatch-bin`}</code> | Dispatcher, communicates between container and content nodes. Can be multi-level in a hierarchy</td> + </tr> + </tbody> +</table> + + +Rolling upgrade note: A rolling upgrade over Vespa-7.109.10 should work with no extra steps. + +### YQL format + +- When Vespa outputs an YQL statement, it will now not end the string by a semicolon. Terminating statements with semicolon continue to be optional and legal input. +- [Annotations](/en/reference/querying/yql#annotations) are not enclosed in `[]` brackets (still valid input). +- The annotation name is not quoted (still valid input). + +Example Vespa 7 / Vespa 8: + +```js +where text contains ([{"distance": 5}]near("a", "b")); +where text contains ({distance: 5}near("a", "b")) +``` + +### Upgrade procedure + +The [upgrade procedure](/en/operations/self-managed/live-upgrade) has been simplified in Vespa 8 (when upgrading from Vespa 7 to Vespa 8 or between Vespa 8 versions). When upgrading from Vespa 7.x to Vespa 7.y replace item 3 [Upgrade config servers](/en/operations/self-managed/live-upgrade#upgrade-config-server) in the [upgrade procedure](/en/operations/self-managed/live-upgrade) with this procedure: +- When upgrading the config servers, the nodes of the application cannot receive config until they are upgraded themselves. We need to set all of them in standalone mode before continuing by running this command on each node: + + ```bash + $ vespa-configproxy-cmd -m setmode memorycache + ``` + Each node will automatically reattach itself when it is upgraded. +- Install the new Vespa version on the config servers and [restart](/en/operations/self-managed/admin-procedures#vespa-start-stop-restart) them one by one. Wait until it is up again, look in vespa log for "Changing health status code from 'initializing' to 'up'" or use [health checks](/en/operations/self-managed/configuration-server#troubleshooting). +- Redeploy and activate the application: + + `$` [`vespa deploy`](/en/clients/vespa-cli#deployment) \ No newline at end of file diff --git a/en/reference/release-notes/vespa9.html b/en/reference/release-notes/vespa9.html deleted file mode 100644 index b9f44a6dbc..0000000000 --- a/en/reference/release-notes/vespa9.html +++ /dev/null @@ -1,360 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Vespa 9 Release Notes" -redirect_from: -- /en/vespa9-release-notes.html ---- - -<p> - This document lists the changes between Vespa major versions 8 and 9. - As documented in <a href="https://vespa.ai/releases#versions">Vespa versions</a>, - new functionality in Vespa is introduced in minor versions, - while major versions are used to mark releases breaking compatibility. - As Vespa 9 does not introduce any new functionality, - it is as safe and mature as the versions of Vespa 8 preceding it. -<!-- No further releases will be made of Vespa 8, except - possible critical security fixes. --> -<p> -<p> - Note: This is work in progress, Vespa 9 is tentatively planned for - release in Q1 2026. -</p> - - - -<h2 id="compatibility-overview">Overview</h2> - -<p> - The compatibility breaking changes in Vespa 9 fall into these categories: -</p> -<ul> - <li><a href="#changed-defaults">Changes to default behaviour</a></li> - <li><a href="#application-package-changes">Application package structure and settings</a> - - deprecated settings and constructs in e.g. <em>schemas</em> and <em>services.xml</em> are removed.</li> - <li><a href="#java-api-changes">Java APIs</a> - - deprecated APIs are removed or revoked from Vespa's - <a href="https://javadoc.io/doc/com.yahoo.vespa/annotations/latest/com/yahoo/api/annotations/PublicApi.html">public API</a> surface.</li> - <li><a href="#container-runtime">Container runtime environment</a> - - incompatible changes to the Java build and runtime environments.</li> - <li><a href="#removed-http-api-parameters">HTTP API changes</a></li> - <li><a href="#removed-command-line-tools">Removed command line tools</a></li> - <li><a href="#removed-or-renamed-metrics">Removed or renamed metrics</a></li> - <li><a href="#security">Security related changes</a></li> - <li><a href="#operating-system">Operating system support</a></li> - <li><a href="#other-changes">Other changes</a>, not covered by any of the above categories.</li> -</ul> - -<p> -To ensure their applications are compatible with Vespa 9, application owners must: -<ul> - <li>Review the list of <a href="#changed-defaults">changes to defaults</a> and add the necessary options - if you want to preserve behavior from Vespa 8.</li> - <li>Make sure there are no deprecation warnings when compiling against Vespa 8.</li> - <li>Review the <a href="#application-package-changes">application package changes</a> and make sure there - are no deployment warnings when deploying on Vespa 8.</li> - <li>Review the list of <a href="#removed-http-api-parameters">HTTP API changes</a> and update - any clients of the application.</li> - <li>Review the remaining sections of this document, and update the application and its environment accordingly.</li> -</ul> - -<p> - Usage of deprecated Java APIs produce warnings during compilation, while <em>deployment warnings</em> - are produced for application package deprecations and most changes to the container runtime environment. - In hosted Vespa or Vespa Cloud, deployment warnings are shown in the application's console view. - However, for other types of changes, there is no way to emit deprecation warnings, - so these are only described in this document and other Vespa documentation. -</p> - -<p> - The following sections lists all the changes from Vespa 8 to Vespa 9 in detail. -</p> - -<h2 id="changed-defaults">Changed defaults</h2> - -<p>These changes may break clients, and impact both performance and user experience. -Applications that are in production and relies on these defaults should -make configuration changes to keep the existing behavior when upgrading to Vespa 8. -This can be done on Vespa 8, <i>before</i> upgrading - -using <a href="../../applications/testing.html#feature-switches-and-bucket-tests">bucket tests</a> can be useful.</p> - -<p> -The following defaults have changed: -</p> -<table class="table"> - <tr> - <th>Change</th> - <th>Configuration required to avoid change on Vespa 9</th> - </tr> - - -</table> - - - -<h2 id="application-package-changes">Application package changes</h2> - - -<h3 id="removed-settings-from-schemas">Removed settings from schemas</h3> -<p> - The following settings are removed from - <a href="../schemas/schemas.html">schema</a>: -</p> -<table class="table"> - <thead> - <tr> - <th>Name</th><th>Replacement</th> - </tr> - </thead> - <tbody> - </tbody> -</table> - - -<h3 id="changed-semantics-in-services-xml">Changed semantics in services.xml</h3> -<p> - The following elements and attributes in services.xml have new semantics: -</p> -<table class="table"> - <thead> - <tr> - <th>Name</th><th>Description</th> - </tr> - </thead> - <tbody> - </tbody> -</table> - - -<h3 id="removed-constructs-from-services-xml">Removed constructs from services.xml</h3> -<p> - The following elements and attributes are removed from services.xml: -</p> -<table class="table"> - <thead> - <tr> - <th>Parent element</th><th>Removed construct</th><th>Description</th> - </tr> - </thead> - <tbody> - </tbody> -</table> - - - -<h3 id="searchdefinitions-folder"><em>searchdefinitions/</em> folder support removed</h3> -<p> - Schemas should now be placed in the <em>schemas/</em> folder. -</p> - - -<h2 id="java-api-changes">Java API changes</h2> - -<h3 id="removed-java-packages">Removed Java packages</h3> - -<table class="table"> - <thead><tr><th>Package</th><th>Description</th></tr></thead> - <tbody> - </tbody> -</table> - - -<h3 id="removed-java-classes-and-methods">Removed Java Classes and methods</h3> - -<p> - Classes and methods that were marked as deprecated in Vespa 8 are removed. - If deprecation warnings are emitted for Vespa APIs when building the application, - these must be fixed before migrating to Vespa 9. The sections below contain only the - most notable changes. -</p> - -<p> - The following classes are no longer public API and have been moved to Vespa internal packages: -</p> -<table class="table"> - <thead><tr><th>Package</th><th>Class</th><th>Migration advice</th></tr></thead> - <tr> - <td>com.yahoo.search.predicate</td> - <td><em>PredicateIndex</em> + related classes</td> - <td> - The Predicate Search Java Library is removed (<em>com.yahoo.vespa:predicate-search</em>). - Use <a href="../../schemas/predicate-fields.html">predicate fields</a> in Vespa instead. - </td> - </tr> - <tbody> -</table> - -<p> - The following methods are removed: -</p> -<table class="table"> - <thead><tr><th>Method</th><th>Migration advice</th></tr></thead> - <tbody> - </tbody> -</table> - - -<h3 id="changed-java-apis">Breaking changes to Java APIs</h3> -<p> - The Javadoc of the deprecated types/members should document the replacement API. - The below list is not exhaustive - some smaller and trivial changes are not listed. -</p> -<table class="table"> - <thead><tr><th>Type(s)</th><th>Description</th></tr></thead> - <tbody> - </tbody> -</table> - - -<h3 id="deprecated-java-apis">Deprecated Java APIs</h3> -<p> - A few redundant APIs have been deprecated because they have replacements that - provide the same, or better, functionality. We advise you switch to the - replacement to reduce future maintenance cost. -</p> - -<table class="table"> - <thead><tr><th>Type(s)</th><th>Replacement</th></tr></thead> - <tbody> - </tbody> -</table> - - - - -<h2 id="container-runtime">Container Runtime Environment</h2> - - -<h3 id="jdk-version">JDK version</h3> -<p> - Vespa 9 upgrades the JDK version from 17 to 25. Java artifacts built against older JDK versions - will still be compatible with Vespa 9 (JDK 25). The opposite will not be possible - Vespa 8 - (JDK 21) is not compatible with newer JVM byte code. It's possible though to use the - <em>--release</em> option for <em>javac</em> to target an older JDK version. -</p> - - -<h3 id="provided-maven-artifacts">Changes to provided maven artifacts</h3> - -<p> -The following Maven artifacts are no longer provided runtime to user application plugins by the Jdisc container: -</p> -<table class="table"> - <thead><tr><th>Artifact</th><th>Notes</th></tr></thead> - <tbody> - </tbody> -</table> -<p> -Make sure your application OSGi bundle embeds the required artifacts from the above list. -An artifact can be embedded by adding it in scope <em>compile</em> to the <em>dependencies</em> section in pom.xml. -Typically, these artifacts have until now been used in scope <em>provided</em>. -Use <code>mvn dependency:tree</code> to check whether any of the listed artifacts are directly or transitively included -as dependencies. -</p> -<p> -As always, remove any dependencies that are not required by your project. Consult the Maven documentation on -<a href="https://maven.apache.org/guides/introduction/introduction-to-optional-and-excludes-dependencies.html#dependency-exclusions" data-proofer-ignore> -Dependency Exclusions</a> for how to remove a transitively included dependency. -</p> - -An example adding <em>org.json:json</em> as a compile scoped dependency: -<pre> -<dependencies> - ... - <dependency> - <groupId>org.json</groupId> - <artifactId>json</artifactId> - <version>20211205</version> - <scope>compile</scope> - </dependency> - ... -</dependencies> -</pre> - -<!-- -<h2 id="removed-http-apis">Removed HTTP APIs</h2> -<p> -The following HTTP APIs are removed: -</p> -<table width="100%" class="table"> - <thead> - <tr> - <th width="50%">Name</th><th>Replacement</th> - </tr> - </thead> - <tbody> - <tr> - <td>: - </td> - <td>. - </td> - </tr> - </tbody> -</table> ---> - - -<h2 id="removed-http-api-parameters">Removed HTTP API parameters</h2> -<p> -The following HTTP API parameters are removed from the <a href="../api/query.html">query API</a>: -</p> -<table class="table"> - <thead> - <tr> - <th>Standard API path</th><th>Parameter name</th><th>Replacement</th> - </tr> - </thead> - <tbody> - </tbody> -</table> - - - -<h2 id="removed-command-line-tools">Removed command line tools</h2> - - - -<h2 id="removed-or-renamed-metrics">Removed or renamed metrics</h2> -<p> -The following metrics are renamed: -</p> -<table class="table"> - <thead></thead> - <tbody></tbody> -</table> - -<p> -The following metrics are removed: -</p> -<table class="table"> - <thead></thead> - <tbody></tbody> -</table> - - -<h2 id="security">Security</h2> - -<h2 id="operating-system">Operating system support for Vespa artifacts</h2> - -<h3 id="supported-container-os">OCI containers (Docker containers)</h3> -<p> -</p> - -<h2 id="other-changes">Other changes</h2> - - - -<h3 id="rankfeatures">Changes in rankfeatures</h3> -<p> - Vespa can calculate and return all <a href="../api/query.html#ranking.listfeatures">rank-features</a> - in the <code>rankfeatures</code> summary field. Vespa 9 contains some changes to this list: -</p> -<ul> - -</ul> - - - -<h3 id="upgrade-procedure">Upgrade procedure</h3> -<p> -See <a href="/en/operations/self-managed/live-upgrade.html">upgrade procedure</a> for how to upgrade. -</p> diff --git a/en/reference/release-notes/vespa9.mdx b/en/reference/release-notes/vespa9.mdx new file mode 100644 index 0000000000..106607b010 --- /dev/null +++ b/en/reference/release-notes/vespa9.mdx @@ -0,0 +1,265 @@ +--- +title: "Vespa 9 Release Notes" +sidebarTitle: "Vespa 9 (upcoming)" +--- + +This document lists the changes between Vespa major versions 8 and 9. As documented in [Vespa versions](https://vespa.ai/releases#versions), new functionality in Vespa is introduced in minor versions, while major versions are used to mark releases breaking compatibility. As Vespa 9 does not introduce any new functionality, it is as safe and mature as the versions of Vespa 8 preceding it. + +Note: This is work in progress, Vespa 9 is tentatively planned for release in Q1 2026. + +## Overview + +The compatibility breaking changes in Vespa 9 fall into these categories: + +- [Changes to default behaviour](#changed-defaults) +- [Application package structure and settings](#application-package-changes) - deprecated settings and constructs in e.g. *schemas* and *services.xml* are removed. +- [Java APIs](#java-api-changes) - deprecated APIs are removed or revoked from Vespa's [public API](https://javadoc.io/doc/com.yahoo.vespa/annotations/latest/com/yahoo/api/annotations/PublicApi.html) surface. +- [Container runtime environment](#container-runtime) - incompatible changes to the Java build and runtime environments. +- [HTTP API changes](#removed-http-api-parameters) +- [Removed command line tools](#removed-command-line-tools) +- [Removed or renamed metrics](#removed-or-renamed-metrics) +- [Security related changes](#security) +- [Operating system support](#operating-system) +- [Other changes](#other-changes), not covered by any of the above categories. + +To ensure their applications are compatible with Vespa 9, application owners must: + +- Review the list of [changes to defaults](#changed-defaults) and add the necessary options if you want to preserve behavior from Vespa 8. +- Make sure there are no deprecation warnings when compiling against Vespa 8. +- Review the [application package changes](#application-package-changes) and make sure there are no deployment warnings when deploying on Vespa 8. +- Review the list of [HTTP API changes](#removed-http-api-parameters) and update any clients of the application. +- Review the remaining sections of this document, and update the application and its environment accordingly. + +Usage of deprecated Java APIs produce warnings during compilation, while *deployment warnings* are produced for application package deprecations and most changes to the container runtime environment. In hosted Vespa or Vespa Cloud, deployment warnings are shown in the application's console view. However, for other types of changes, there is no way to emit deprecation warnings, so these are only described in this document and other Vespa documentation. + +The following sections lists all the changes from Vespa 8 to Vespa 9 in detail. + +## Changed defaults + +These changes may break clients, and impact both performance and user experience. Applications that are in production and relies on these defaults should make configuration changes to keep the existing behavior when upgrading to Vespa 8. This can be done on Vespa 8, *before* upgrading - using [bucket tests](/en/applications/testing#feature-switches-and-bucket-tests) can be useful. + +The following defaults have changed: + +<table className="vespa-table"> + <thead> + <tr> + <th>Change</th> + <th>Configuration required to avoid change on Vespa 9</th> + </tr> + </thead> + <tbody> + </tbody> +</table> + +## Application package changes + +### Removed settings from schemas + +The following settings are removed from [schema](/en/reference/schemas/schemas): + +<table className="vespa-table"> + <thead> + <tr> + <th>Name</th> + <th>Replacement</th> + </tr> + </thead> + <tbody> + </tbody> +</table> + +### Changed semantics in services.xml + +The following elements and attributes in services.xml have new semantics: + +<table className="vespa-table"> + <thead> + <tr> + <th>Name</th> + <th>Description</th> + </tr> + </thead> + <tbody> + </tbody> +</table> + +### Removed constructs from services.xml + +The following elements and attributes are removed from services.xml: + +<table className="vespa-table"> + <thead> + <tr> + <th>Parent element</th> + <th>Removed construct</th> + <th>Description</th> + </tr> + </thead> + <tbody> + </tbody> +</table> + +### *searchdefinitions/* folder support removed + +Schemas should now be placed in the *schemas/* folder. + +## Java API changes + +### Removed Java packages + +<table className="vespa-table"> + <thead> + <tr> + <th>Package</th> + <th>Description</th> + </tr> + </thead> + <tbody> + </tbody> +</table> + +### Removed Java Classes and methods + +Classes and methods that were marked as deprecated in Vespa 8 are removed. If deprecation warnings are emitted for Vespa APIs when building the application, these must be fixed before migrating to Vespa 9. The sections below contain only the most notable changes. + +The following classes are no longer public API and have been moved to Vespa internal packages: + +<table className="vespa-table"> + <thead> + <tr> + <th>Package</th> + <th>Class</th> + <th>Migration advice</th> + </tr> + </thead> + <tbody> + <tr> + <td>com.yahoo.search.predicate</td> + <td>*PredicateIndex* + related classes</td> + <td>The Predicate Search Java Library is removed (*com.yahoo.vespa:predicate-search*). Use <a href="/en/schemas/predicate-fields." aria-label="predicate fields">predicate fields</a> in Vespa instead.</td> + </tr> + </tbody> +</table> + +The following methods are removed: + +<table className="vespa-table"> + <thead> + <tr> + <th>Method</th> + <th>Migration advice</th> + </tr> + </thead> + <tbody> + </tbody> +</table> + +### Breaking changes to Java APIs + +The Javadoc of the deprecated types/members should document the replacement API. The below list is not exhaustive - some smaller and trivial changes are not listed. + +<table className="vespa-table"> + <thead> + <tr> + <th>Type(s)</th> + <th>Description</th> + </tr> + </thead> + <tbody> + </tbody> +</table> + +### Deprecated Java APIs + +A few redundant APIs have been deprecated because they have replacements that provide the same, or better, functionality. We advise you switch to the replacement to reduce future maintenance cost. + +<table className="vespa-table"> + <thead> + <tr> + <th>Type(s)</th> + <th>Replacement</th> + </tr> + </thead> + <tbody> + </tbody> +</table> + +## Container Runtime Environment + +### JDK version + +Vespa 9 upgrades the JDK version from 17 to 25. Java artifacts built against older JDK versions will still be compatible with Vespa 9 (JDK 25). The opposite will not be possible - Vespa 8 (JDK 21) is not compatible with newer JVM byte code. It's possible though to use the *\--release* option for *javac* to target an older JDK version. + +### Changes to provided maven artifacts + +The following Maven artifacts are no longer provided runtime to user application plugins by the Jdisc container: + +<table className="vespa-table"> + <thead> + <tr> + <th>Artifact</th> + <th>Notes</th> + </tr> + </thead> + <tbody> + </tbody> +</table> + +Make sure your application OSGi bundle embeds the required artifacts from the above list. An artifact can be embedded by adding it in scope *compile* to the *dependencies* section in pom.xml. Typically, these artifacts have until now been used in scope *provided*. Use `mvn dependency:tree` to check whether any of the listed artifacts are directly or transitively included as dependencies. + +As always, remove any dependencies that are not required by your project. Consult the Maven documentation on [Dependency Exclusions](https://maven.apache.org/guides/introduction/introduction-to-optional-and-excludes-dependencies.html#dependency-exclusions) for how to remove a transitively included dependency. + +An example adding *org.json:json* as a compile scoped dependency: + +```xml +<dependencies> + ... + <dependency> + <groupId>org.json</groupId> + <artifactId>json</artifactId> + <version>20211205</version> + <scope>compile</scope> + </dependency> + ... +</dependencies> +``` + +## Removed HTTP API parameters + +The following HTTP API parameters are removed from the [query API](/en/reference/api/query): + +<table className="vespa-table"> + <thead> + <tr> + <th>Standard API path</th> + <th>Parameter name</th> + <th>Replacement</th> + </tr> + </thead> + <tbody> + </tbody> +</table> + +## Removed command line tools + +## Removed or renamed metrics + +The following metrics are renamed: + +The following metrics are removed: + +## Security + +## Operating system support for Vespa artifacts + +### OCI containers (Docker containers) + +## Other changes + +### Changes in rankfeatures + +Vespa can calculate and return all [rank-features](/en/reference/api/query#ranking.listfeatures) in the `rankfeatures` summary field. Vespa 9 contains some changes to this list: + +### Upgrade procedure + +See [upgrade procedure](/en/operations/self-managed/live-upgrade) for how to upgrade. \ No newline at end of file diff --git a/en/reference/schemas/document-field-path.html b/en/reference/schemas/document-field-path.html deleted file mode 100644 index f239f288a8..0000000000 --- a/en/reference/schemas/document-field-path.html +++ /dev/null @@ -1,123 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Document field path reference" -redirect_from: -- en/reference/document-field-path.html ---- - -<p> -The field path syntax is used several places in Vespa to traverse documents through -arrays, structs, maps and sets and generate a set of values matching the expression. Examples - -If the document contains the field <code>mymap</code>, and it has a -key <code>mykey</code>, the expression returns the value of the map for that key: -</p> -<pre> -mymap{mykey} -</pre> -<p>Returns the value in index 3 of the <code>myarray</code> field, if set:</p> -<pre> -myarray[3] -</pre> -<p> -Returns the value of the <code>value1</code> field in the struct -field <code>mystruct</code>, if set: -</p> -<pre> -mystruct.value1 -</pre> -<p> -If mystructarray is an array field containing structs, -returns the values of value1 for each of those structs: -</p> -<pre> -mystructarray.value1 -</pre> -<p> -The following syntax can be used for the different field types, -and can be combined recursively as required: -</p> - - - -<h2 id="maps-weighted-sets">Maps/weighted Sets</h2> -<table class="table"> - <thead></thead><tbody> - <tr> - <th style="width:250px"><mapfield>{<keyvalue>}</th> - <td> Retrieve the value of a specific key</td> - </tr><tr> - <th><mapfield>{$<variablename>}</th> - <td>Retrieve all values, setting the <a href="#variables">variable</a> - to the key value for each</td> - </tr><tr> - <th><mapfield>.key</th> - <td>Retrieve all key values</td> - </tr><tr> - <th><mapfield>.value</th> - <td>Retrieve all values</td> - </tr><tr> - <th><mapfield></th> - <td>Retrieve all keys</td> - </tr> - </tbody> -</table> -<p> -In the case of weighted sets, the value referenced above is the weight of the item. -</p> - - - -<h2 id="array">Array</h2> -<table class="table"> - <thead></thead><tbody> - <tr> - <th style="width:250px"><arrayfield>[<index>]</th> - <td>Retrieve the value in a specific index</td> - </tr><tr> - <th><arrayfield>[$<variablename>]</th> - <td>Retrieve all values in the array, setting the <a href="#variables">variable</a> - to the index of each</td> - </tr><tr> - <th><arrayfield></th> - <td>Retrieve all values in the array</td> - </tr> - </tbody> -</table> - - - -<h2 id="struct">Struct</h2> -<table class="table"> - <thead></thead><tbody> - <tr> - <th style="width:250px"><structfield>{.<subfield>}</th> - <td>Return the value of the struct field</td> - </tr><tr> - <th><structfield></th> - <td>Return the value of all subfields</td> - </tr> - </tbody> -</table> -<p> -Note that when specifying values of subscripts of maps, weighted sets and arrays, -only numbers and strings may be used. -</p> - - - -<h2 id="variables">Variables</h2> -<p> -It can be useful to reference several field paths using a common variable. -For instance, if you have an array of structs, -you may want to use document selection on fields within the same array index together. -This could be done by an expression like: -</p> -<pre> -mydoctype.mystructarray{$x}.field1=="foo" AND mydoctype.mystructarray{$x}.field2=="bar" -</pre> -<p> -Variables either have a <code>key</code> value (for maps and weighted sets), -or an <code>index</code> value (for arrays). -Variables cannot be used across such contexts -(that is, a map key cannot be used to index into an array). -</p> diff --git a/en/reference/schemas/document-field-path.mdx b/en/reference/schemas/document-field-path.mdx new file mode 100644 index 0000000000..385305a302 --- /dev/null +++ b/en/reference/schemas/document-field-path.mdx @@ -0,0 +1,124 @@ +--- +title: "Document field path reference" +--- + +The field path syntax is used several places in Vespa to traverse documents through arrays, structs, maps and sets and generate a set of values matching the expression. Examples - If the document contains the field `mymap`, and it has a key `mykey`, the expression returns the value of the map for that key: + +```bash +mymap{mykey} +``` + +Returns the value in index 3 of the `myarray` field, if set: + +```bash +myarray[3] +``` + +Returns the value of the `value1` field in the struct field `mystruct`, if set: + +```bash +mystruct.value1 +``` + +If mystructarray is an array field containing structs, returns the values of value1 for each of those structs: + +```bash +mystructarray.value1 +``` + +The following syntax can be used for the different field types, and can be combined recursively as required: + +## Maps/weighted Sets + +<table className="vespa-table"> + <thead> + <tr> + <th></th> + <th></th> + </tr> + </thead> + <tbody> + <tr> + <td><mapfield>{<keyvalue>}</td> + <td>Retrieve the value of a specific key</td> + </tr> + <tr> + <td><mapfield>{$<variablename>}</td> + <td>Retrieve all values, setting the <a href="#variables" aria-label="variable">variable</a> to the key value for each</td> + </tr> + <tr> + <td><mapfield>.key</td> + <td>Retrieve all key values</td> + </tr> + <tr> + <td><mapfield>.value</td> + <td>Retrieve all values</td> + </tr> + <tr> + <td><mapfield></td> + <td>Retrieve all keys</td> + </tr> + </tbody> +</table> + +In the case of weighted sets, the value referenced above is the weight of the item. + +## Array + +<table className="vespa-table"> + <thead> + <tr> + <th></th> + <th></th> + </tr> + </thead> + <tbody> + <tr> + <td><arrayfield>[<index>]</td> + <td>Retrieve the value in a specific index</td> + </tr> + <tr> + <td><arrayfield>[$<variablename>]</td> + <td>Retrieve all values in the array, setting the <a href="#variables" aria-label="variable">variable</a> to the index of each</td> + </tr> + <tr> + <td><arrayfield></td> + <td>Retrieve all values in the array</td> + </tr> + </tbody> +</table> + +## Struct + +<table className="vespa-table"> + <thead> + <tr> + <th></th> + <th></th> + </tr> + </thead> + <tbody> + <tr> + <td><structfield>{.<subfield>}</td> + <td>Return the value of the struct field</td> + </tr> + <tr> + <td><structfield></td> + <td>Return the value of all subfields</td> + </tr> + </tbody> +</table> + +<Info> +Note that when specifying values of subscripts of maps, weighted sets and arrays, only numbers and strings may be used. +</Info> + +## Variables + +It can be useful to reference several field paths using a common variable. For instance, if you have an array of structs, you may want to use document selection on fields within the same array index together. This could be done by an expression like: + +```bash +mydoctype.mystructarray{$x}.field1=="foo" AND mydoctype.mystructarray{$x}.field2=="bar" +``` + +Variables either have a `key` value (for maps and weighted sets), or an `index` value (for arrays). Variables cannot be used across such contexts (that is, a map key cannot be used to index into an array). \ No newline at end of file diff --git a/en/reference/schemas/document-json-format.html b/en/reference/schemas/document-json-format.html deleted file mode 100644 index 53c7caed1c..0000000000 --- a/en/reference/schemas/document-json-format.html +++ /dev/null @@ -1,1383 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Document JSON format reference" -redirect_from: -- en/reference/document-json-format.html ---- - -<p> -This document describes the JSON format used for sending document operations to Vespa. -Field types are defined in the -<a href="schemas.html#field">schema reference</a>. -This is a reference for: -</p> -<ul> - <li>JSON representation of <a href="#document-operations">document operations</a> (put, get, remove, update)</li> - <li>JSON representation of <a href="#field-types">field types</a> in Vespa documents</li> - <li>JSON representation of addressing fields for update, and <a href="#update-operations"> update operations</a></li> -</ul> -<p> -Also refer to <a href="../../linguistics/troubleshooting-encoding.html">encoding troubleshooting</a>. -</p> - -<pre class="pre-config"> -<a href="#document-operations">Document operations</a> - <a href="#put">Put</a> - <a href="#get">Get</a> - <a href="#remove">Remove</a> - <a href="#update">Update</a> - <a href="#test-and-set">Test and set</a> - <a href="#create">Create</a> -<a href="#field-types">Field types</a> - <a href="#string">string</a> - <a href="#int">int</a> - <a href="#long">long</a> - <a href="#bool">bool</a> - <a href="#byte">byte</a> - <a href="#float">float</a> - <a href="#double">double</a> - <a href="#position">position</a> - <a href="#predicate">predicate</a> - <a href="#raw">raw</a> - <a href="#uri">uri</a> - <a href="#array">array</a> - <a href="#weightedset">weightedset</a> - <a href="#tensor">Tensors</a> - <a href="#tensor-short-form-indexed">Indexed tensors short form</a> - <a href="#tensor-short-form-mapped">Short form for tensors with a single mapped dimension</a> - <a href="#tensor-short-form-mixed">Mixed tensors short form</a> - <a href="#tensor-hex-dump">Cell values as binary data (hex dump format)</a> - <a href="#tensor-verbose-form">Tensor verbose form</a> - <a href="#struct">struct</a> - <a href="#map">map</a> - <a href="#reference">reference</a> - <a href="#empty-fields">Empty fields</a> -<a href="#update-operations">Update operations</a> - <a href="#assign">assign</a> - <a href="#single-value-field">Single value field</a> - <a href="#tensor-field">Assign tensor</a> - <a href="#<struct-field">Assign struct field</a> - <a href="#replacing-all-fields-in-a-struct">Replacing entire struct</a> - <a href="#individual-struct-fields">Replace individual struct fields</a> - <a href="#assign-map-field">Assign map field</a> - <a href="#map-to-primitive-value">Map to primitive value</a> - <a href="#map-to-struct">Map to struct</a> - <a href="#array-field">Arrays</a> - <a href="#array-of-primitive-values">Array of primitive values</a> - <a href="#array-of-struct">Array of struct</a> - <a href="#weighted-set-field">Weighted set field</a> - <a href="#clearing-a-field">Clearing a field</a> - <a href="#add">add</a> - <a href="#add-array-elements">Add array elements</a> - <a href="#add-weighted-set">Add to weighted set</a> - <a href="#tensor-add">Add to tensor</a> - <a href="#composite-remove">Remove elements in composites</a> - <a href="#weighted-set-field-remove">Remove from weighted set field</a> - <a href="#map-field-remove">Remove from map field</a> - <a href="#tensor-remove">Remove from tensor</a> - <a href="#arithmetic">Arithmetic</a> - <a href="#match">match</a> - <a href="#tensor-modify">Tensor modify</a> - <a href="#fieldpath">Fieldpath</a> -</pre> - - -<h2 id="field-types">Field types</h2> -<p> - Unless otherwise noted, these formats are used both for returned - values in read operations, and as input in write operations (put - operations and field assign update operations). -</p> -<table class="table"> - <tr> - <th>string</th> - <td> - <p id="string"></p> -<pre>{% highlight json %} -"name": "Polly" -{% endhighlight %}</pre> - <p> - Feeding in an empty string ("") for a field will have the same effect as not - feeding a value for that field, and the field will not be rendered in the - document API and in document summaries. - </p> - </td> - </tr><tr> - <th>int</th> - <td> - <p id="int"></p> -<pre>{% highlight json %} -"age": 42 -{% endhighlight %}</pre> - </td> - </tr><tr> - <th>long</th> - <td> - <p id="long"></p> -<pre>{% highlight json %} -"current_time_ms": 1742837807000 -{% endhighlight %}</pre> - </td> - </tr><tr> - <th>bool</th> - <td> - <p id="bool"><em>true</em> or <em>false</em>:</p> -<pre>{% highlight json %} -"alive": false -{% endhighlight %}</pre> - </td> - </tr><tr> - <th>byte</th> - <td> - <p id="byte"></p> -<pre>{% highlight json %} -"tinynumber": 128 -{% endhighlight %}</pre> - </td> - </tr><tr> - <th>float</th> - <td> - <p id="float"></p> -<pre>{% highlight json %} -"weight": 123.4567 -{% endhighlight %}</pre> - </td> - </tr><tr> - <th>double</th> - <td> - <p id="double"></p> -<pre>{% highlight json %} -"weight": 123.4567 -{% endhighlight %}</pre> - </td> - </tr><tr> - <th>position</th> - <td> - <p id="position">A position is encoded as a lat/lng object:</p> -<pre>{% highlight json %} -"mypos": { - "lat": 37.4181488, - "lng": -122.0256157 -} -{% endhighlight %}</pre> - <p>See <a href="../../querying/geo-search.html">Geo Search</a> for details.</p> - </td> - </tr><tr> - <th>predicate</th> - <td> - <p id="predicate">A <a href="../../schemas/predicate-fields.html">predicate</a> is represented with a string:</p> -<pre>{% highlight json %} -"predicate_field": "gender in [Female] and age in [20..30] and pos in [1..4]" -{% endhighlight %}</pre> - </td> - </tr><tr> - <th>raw</th> - <td> - <p id="raw"> - The content of a <a href="schemas.html#raw">raw</a> field - is represented as a base64-encoded string: - </p> -<pre>{% highlight json %} -"raw_field": "VW5rbm93biBhcnRpc3QgZnJvbSB0aGUgbW9vbg==" -{% endhighlight %}</pre> - <p> - When used as <em>summary</em> field it will be rendered as a base64-encoded string. - </p> - </td> - </tr><tr> - <th>uri</th> - <td> - <p id="uri">A URI is a string:</p> -<pre>{% highlight json %} -"url": "https://www.yahoo.com/" -{% endhighlight %}</pre> - </td> - </tr><tr> - <th>array</th> - <td> - <p id="array">Arrays are represented as JSON arrays.</p> -<pre>{% highlight json %} -"int_array_field": [ - 123, - 456, - 789 -] - -"string_array_field": [ - "item 1", - "item 2", - "item 3" -] -{% endhighlight %}</pre> - - <p>An array of struct is represented as a JSON array of JSON objects matching the defined struct field:</p> -<pre>{% highlight json %} -"array_of_struct_field": [ - { "first_name": "Chris", "last_name": "Martin" }, - { "first_name": "James", "last_name": "Hetfield" }, - { "first_name": "Diana", "last_name": "Krall" } -] -{% endhighlight %}</pre> - <p> - Feeding in an empty array ([]) for a field will have the same effect as not - feeding a value for that field, and the field will not be rendered in the - document API and in document summaries. - </p> - </td> - </tr><tr> - <th>weightedset</th> - <td> - <p id="weightedset"> - Weighted sets are represented as maps where the value is the weight. - Note, even if the key is not a string as such, it will be represented as a string in the JSON format. - </p> -<pre>{% highlight json %} -"int_weighted_set": { - "123": 2, - "456": 78 -} - -"string_weighted_set": { - "item 1": 143, - "item 2": 6 -} -{% endhighlight %}</pre> - <p> - Feeding in an empty weightedset ({}) for a field will have the same effect as not - feeding a value for that field, and the field will not be rendered in the - document API and in document summaries. - </p> - </td> - </tr><tr> - <th id="tensor">tensor</th> - <td> - <p id="tensor-short-form-indexed"> - <strong>Indexed tensors short form:</strong> - An array where the values are ordered in the standard value order, - where indexes of dimensions to the right are incremented before indexes to the left, - where dimensions are ordered alphabetically - (such that, e.g. with a tensor with dimensions x,y the "y" values for each value of "x" are adjacent): - </p> - <pre>{% highlight json %} -"tensorfield": [ 2.0, 3.0, 5.0, 7.0 ] -{% endhighlight %}</pre> - <p> - The cells array can optionally be nested in an object under the key "values". - This is how tensor values are returned <a href="../api/document-v1.html#format.tensors">by default</a>, - along with another key "type" containing the tensor type. - </p> - - <p id="tensor-short-form-mapped"> - <strong>Short form for tensors with a single mapped dimension</strong>: - A map with the dimension key as key and the value as value. - </p> - <pre>{% highlight json %} -"tensorfield": { - "a": 2.0, - "b": 3.0 -} -{% endhighlight %}</pre> - <p> - The cells object can optionally be nested in an object under the key "cells". - This is how tensor values are returned <a href="../api/document-v1.html#format.tensors">by default</a>, - along with another key "type" containing the tensor type. - </p> - - <p id="tensor-short-form-mixed"> - <strong>Mixed tensors short form:</strong> - If the tensor has a single sparse dimension: A map where the key is the value of that dimension and the value - is a nested array containing the values of the dense subspace within that key.<br/> - If the tensor has multiple sparse dimensions: An array nested in a "blocks" element where the elements consist of a map with the keys - "address" and "values", where "address" is a map with the sparse dimensions and their values (as in cells), - and "values" is a nested array containing the values of the dense subspace within that address. - </p> - <p>Example - single sparse dimension:</p> - <pre>{% highlight json %} -"tensorfield": { - "x1":[2.0,3.0], - "x2":[4.0,5.0] -} -{% endhighlight %}</pre> - <p>Example - multiple sparse dimensions:</p> - <pre>{% highlight json %} -"tensorfield": { - "blocks": [ - {"address":{"x":"x1","y":"y2"},"values":[2.0,3.0]}, - {"address":{"x":"x2","y":"y2"},"values":[4.0,5.0]} - ] -} -{% endhighlight %}</pre> - <p> - This is how tensor values are returned <a href="../api/document-v1.html#format.tensors">by default</a>, - along with another key "type" containing the tensor type. - </p> - - <p id="tensor-hex-dump"> - <strong>Cell values as binary data</strong> - For dense and mixed tensors it's possible to fill the cell values directly from binary data - sent in as a string of hexadecimal digits. The simplest possible case is if you have a - vector with <code>int8</code> cell value type: - </p> - <pre>{% highlight json %} -"tensorfield": { - "values": "FF00118022FE" -} -{% endhighlight %}</pre> - <p>This can be used to represent the value <code>tensor<int8>(x[6]):[-1,0,17,-128,34,-2]</code>.</p> - <p> - For other cell types, it's possible to take the bits of the floating-point value, - interpreted directly as an unsigned integer of appropriate width (16, 32, or 64 bits) - and use the hex dump (respectively 4, 8, or 16 hex digits per cell) in a string. - For "float" cells (32-bit IEE754 floating-point) a simple snippet for converting - a cell could look like this: - </p> - <pre>{% highlight python %} -import struct -def float_to_hex(f: float): - return format(struct.unpack('=I', struct.pack('=f', f))[0], '08X') -{% endhighlight %}</pre> - <p> - As an advanced combination example, if you have a tensor with type - <code>tensor<float>(tag{},x[3])</code> - this input could be used, shown with corresponding output: - </p> - <pre>{% highlight json %} -"mixedtensor": { - "foo": "3DE38E393E638E393EAAAAAB", - "bar": "3EE38E393F0E38E43F2AAAAB", - "baz": "3F471C723F638E393F800000" -} -"mixedtensor":{ - "type":"tensor<float>(tag{},x[3])", - "blocks":{ - "foo":[0.1111111119389534,0.2222222238779068,0.3333333432674408], - "bar":[0.4444444477558136,0.5555555820465088,0.6666666865348816], - "baz":[0.7777777910232544,0.8888888955116272,1.0] - } -} -{% endhighlight %}</pre> - - <p id="tensor-verbose-form"> - <strong>Verbose:</strong> <a href="../../ranking/tensor-user-guide.html">Tensor</a> fields - may be represented as an array of cells: - </p> - <pre>{% highlight json %} -"tensorfield": [ - { "address": { "x": "a", "y": "0" }, "value": 2.0 }, - { "address": { "x": "a", "y": "1" }, "value": 3.0 }, - { "address": { "x": "b", "y": "0" }, "value": 4.0 }, - { "address": { "x": "b", "y": "1" }, "value": 5.0 } -] -{% endhighlight %}</pre> - <p> - This works for any tensor but is verbose, so shorter forms specific to various tensor types are also supported. - Use the shortest form applicable to your tensor type for the best possible performance. - </p> - <p> - The cells array can optionally be nested in an object under the key "cells". - This is how tensor values are returned <a href="../api/document-v1.html#format.tensors">by default</a>, - along with another key "type" containing the tensor type. - </p> - </td> - </tr> - - <tr> - <th>struct</th> - <td> - <p id="struct"></p> -<pre>{% highlight json %} -"mystruct": { - "intfield": 123, - "stringfield": "foo" -} -{% endhighlight %}</pre> - </td> - </tr><tr> - <th>map</th> - <td> - <p id="map"> - The JSON dictionary key must be a string, - even if the map key type in the schema is not a string: - </p> -<pre>{% highlight json %} -"int_to_string_map": { - "123": "foo", - "456": "bar", - "789": "foobar" -} -{% endhighlight %}</pre> - <p> - Feeding in an empty map ({}) for a field will have the same effect as not - feeding a value for that field, and the field will not be rendered in the - document API and in document summaries. - </p> - </td> - </tr><tr> - <th>reference</th> - <td> - <p id="reference">String with document ID referring to a <a href="../../schemas/parent-child.html">parent document</a>:</p> -<pre>{% highlight json %} -"artist_ref": "id:mynamespace:artists::artist-1" -{% endhighlight %}</pre> - </td> - </tr><tr> - </tr> -</table> - - -<h2 id="empty-fields">Empty fields</h2> -<p> - In general, fields that have not received a value during feeding will be ignored - when rendering the document. They are considered as empty fields. - However, certain field types have some values which causes them to be considered empty. - For instance, the empty string ("") is considered empty, as well as the empty array ([]). - See the above table for more information for each type. -</p> - - -<h2 id="document-operations">Document operations</h2> -<p> -Refer to <a href="../../writing/reads-and-writes.html">reads and writes</a> for details - alternatives: -</p> -<ul> - <li>Use the <a href="../../clients/vespa-cli.html#documents">Vespa CLI</a>.</li> - <li> - <a href="../api/document-v1.html">/document/v1/</a>: - This API accepts one operation per request, with the document ID encoded in the URL. - </li> - <li> - <a href="../../clients/vespa-feed-client.html">Vespa feed client</a>: - Java APIs / command line tool to feed document operations asynchronously to Vespa, over HTTP. - </li> -</ul> - -<h3 id="put">Put</h3> -<p> - The "put" payload has a "put" operation and - <a href="#field-types">"fields"</a> containing field values; - (<a href="../../writing/document-v1-api-guide.html#post">/document/v1/ example</a>): -</p> -<pre>{% highlight json %} -{ - "put": "id:mynamespace:music::123", - "fields": { - "title": "Best of Bob Dylan" - } -} -{% endhighlight %}</pre> - - -<h3 id="get">Get</h3> -<p> - "get" does not have a payload - the response has the same "field" object as in "put", - and also "id" and "pathId" fields - (<a href="../../writing/document-v1-api-guide.html#get">/document/v1/ example</a>): -</p> -<pre>{% highlight json %} -{ - "pathId": "/document/v1/mynamespace/music/docid/123", - "id": "id:mynamespace:music::123", - "fields": { - "title": "Best of Bob Dylan" - } -} -{% endhighlight %}</pre> - - -<h3 id="remove">Remove</h3> -<p> - The "remove" payload only has a "remove" operation - (<a href="../../writing/document-v1-api-guide.html#delete">/document/v1/ example</a>): -</p> -<pre>{% highlight json %} -{ - "remove": "id:mynamespace:music::123" -} -{% endhighlight %}</pre> - - -<h3 id="update">Update</h3> -<p> - The "update" payload has an "update" operation and "fields". - Note: Each field must contain an <a href="#update-operations">update operation</a>, - not just the field value directly; - (<a href="../../writing/document-v1-api-guide.html#put">/document/v1/ example</a>): -</p> -<pre>{% highlight json %} -{ - "update": "id:mynamespace:music::123", - "fields": { - "title": { - "assign": "The best of Bob Dylan" - } - } -} -{% endhighlight %}</pre> -<p> - Flags can be added to add a <a href="#test-and-set">test and set</a> - condition, or allow the update to <a href="#create">create</a> a new - document (a so-called "upsert" operation). -</p> - -<h4 id="test-and-set">Test and set</h4> -<p> -An optional <em>condition</em> can be added to operations to specify a <em>test and set</em> condition - -see <a href="../../writing/document-v1-api-guide.html#conditional-writes">conditional writes</a>. -The value of the <em>condition</em> is a <a href="../writing/document-selector-language.html">document selection</a>, -encoded as a string. -Example: Increment the <em>sales</em> field only if it is already equal to 999 - (<a href="../../writing/document-v1-api-guide.html#conditional-writes">/document/v1/ example</a>): -</p> -<pre>{% highlight json %} -{ - "update": "id:mynamespace:music::bob/BestOf", - "condition": "music.sales==999", - "fields": { - "sales": { - "increment": 1 - } - } -} -{% endhighlight %}</pre> -{% include note.html content="Use <em>documenttype.fieldname</em> in the condition, not only <em>fieldname</em>."%} -<p>If the condition is not met, a 412 response code is returned.</p> -<!-- ToDo: There is a discussion whether to change to not return error, - and instead return a <em>condition-not-met</em> in the response. --> - - -<h4 id="create">create (create if nonexistent)</h4> -<p> -<strong>Updates</strong> to nonexistent documents are supported using <em>create</em>; -(<a href="../../writing/document-v1-api-guide.html#create-if-nonexistent">/document/v1/ example</a>): -</p> -<pre>{% highlight json %} -{ - "update": "id:mynamespace:music::bob/BestOf", - "create": true, - "fields": { - "title": { - "assign": "The best of Bob Dylan" - } - } -} -{% endhighlight %}</pre> -<p> - Since Vespa 8.178, <em>create</em> can also be used together with conditional <strong>Put</strong> operations - (<a href="../../writing/document-v1-api-guide.html#conditional-updates-and-puts-with-create">/document/v1/ example</a> - - review notes there before using): -</p> -<pre>{% highlight json%} -{ - "put": "id:mynamespace:music::123", - "condition": "music.sales==999", - "create": true, - "fields": { - "title": "Best of Bob Dylan" - } -} -{% endhighlight %}</pre> - - -<h2 id="update-operations">Update operations</h2> -<p> - The update operations are: - <a href="#assign"> <code>assign</code></a>, - <a href="#add"> <code>add</code></a>, - <a href="#composite-remove"> <code>remove</code></a>, - <a href="#arithmetic"> arithmetics</a> - <!-- --> (<code>increment</code> - <!-- --> <code>decrement</code> - <!-- --> <code>multiply</code> - <!-- --> <code>divide</code>), - <a href="#match"> <code>match</code></a>, - <a href="#tensor-modify"> <code>modify</code></a> -</p> -<h2 id="assign">assign</h2> -<p> -<code>assign</code> is used to replace the value of a field (or an element of a collection) with a new value. -When assigning, one can generally use the same syntax and structure -as when feeding that field's value in a <code>put</code> operation. -</p> - - -<h3 id="single-value-field">Single value field</h3> -<pre> -field title type string { - indexing: summary -} -</pre> -<pre>{% highlight json %} -{ - "update": "id:mynamespace:music::example", - "fields": { - "title": { - "assign": "The best of Bob Dylan" - } - } -} -{% endhighlight %}</pre> - - -<h3 id="tensor-field">Tensor field</h3> -<pre> -field tensorfield type tensor(x{},y{}) { - indexing: attribute | summary -} -</pre> -<pre>{% highlight json %} -{ - "update": "id:mynamespace:tensordoctype::example", - "fields": { - "tensorfield": { - "assign": { - "cells": [ - { "address": { "x": "a", "y": "b" }, "value": 2.0 }, - { "address": { "x": "c", "y": "d" }, "value": 3.0 } - ] - } - } - } -} -{% endhighlight %}</pre> - -<p>This will fully replace the entire tensor stored in this field.</p> - - -<h3 id="<struct-field">Struct field</h3> - -<h4 id="replacing-all-fields-in-a-struct">Replacing all fields in a struct</h4> -<p> - A full struct is replaced by assigning an object of struct key/value pairs. -</p> -<pre> -struct person { - field first_name type string {} - field last_name type string {} -} -field contact type person { - indexing: summary -} -</pre> -<pre>{% highlight json %} -{ - "update": "id:mynamespace:workers::example", - "fields": { - "contact": { - "assign": { - "first_name": "Bob", - "last_name": "The Plumber" - } - } - } -} -{% endhighlight %}</pre> - -<h4 id="individual-struct-fields">Individual struct fields</h4> -<p> - Individual struct fields are updated using <a href="#fieldpath">field path</a> syntax. - Refer to the <a href="schemas.html#struct-name">reference</a> for restrictions using structs. -</p> -<pre>{% highlight json %} -{ - "update": "id:mynamespace:workers::example", - "fields": { - "contact.first_name": { - "assign": "Bob" - }, - "contact.last_name": { - "assign": "The Plumber" - } - } -} -{% endhighlight %}</pre> - - -<h3 id="assign-map-field">Map field</h3> -<p> - Individual map entries can be updated using <a href="document-field-path.html">field path</a> syntax. - The following declaration defines a <code>map</code> where the <code>key</code> is an Integer - and the value is a <code>person</code> struct. -</p> -<pre> -struct person { - field first_name type string {} - field last_name type string {} -} -field contact type map<int, person> { - indexing: summary -} -</pre> -<p>Example updating part of an entry in the <code>contact</code> map:</p> -<ul> - <li><code>contact</code> is the name of the map field to be updated</li> - <li><code>{0}</code> is the key that is going to be updated</li> - <li><code>first_name</code> is the struct field to be updated inside the <code>person</code> struct</li> -</ul> -<pre>{% highlight json %} -{ - "update": "id:mynamespace:workers::example", - "fields": { - "contact{0}.first_name": { - "assign": "John" - } - } -} -{% endhighlight %}</pre> -<p> -Assigning an element to a key in a map will insert the key/value mapping if it does not already exist, -or overwrite it with the new value if it does exist. -Refer to the <a href="schemas.html#map">reference</a> for restrictions using maps. -</p> - -<h4 id="map-to-primitive-value">Map to primitive value</h4> -<pre> -field my_food_scores type map<string, string> { - indexing: summary -} -</pre> -<pre>{% highlight json %} -{ - "update": "id:mynamespace:food::example", - "fields": { - "my_food_scores{Strawberries}": { - "assign": "Delicious!" - } - } -} -{% endhighlight %}</pre> - -<h4 id="map-to-struct">Map to struct</h4> -<pre> -struct contact_info { - field phone_number type string {} - field email type string {} -} -field contacts type map<string, contact_info> { - indexing: summary -} -</pre> -<pre>{% highlight json %} -{ - "update": "id:mynamespace:people::d_duck", - "fields": { - "contacts{\"Uncle Scrooge\"}": { - "assign": { - "phone_number": "555-123-4567", - "email": "number_one_dime_luvr1877@example.com" - } - } - } -} -{% endhighlight %}</pre> - - -<h3 id="array-field">Array field</h3> - -<h4 id="array-of-primitive-values">Array of primitive values</h4> -<pre> -field ingredients type array<string> { - indexing: summary -} -</pre> -<p>Assign full array:</p> -<pre>{% highlight json %} -{ - "update": "id:mynamespace:cakes:tasty_chocolate_cake", - "fields": { - "ingredients": { - "assign": [ "sugar", "butter", "vanilla", "flour" ] - } - } -} -{% endhighlight %}</pre> -<p>Assign existing elements in array:</p> -<pre>{% highlight json %} -{ - "update": "id:mynamespace:cakes:tasty_chocolate_cake", - "fields": { - "ingredients[3]": { - "assign": "2 cups of flour (editor's update: NOT asbestos!)" - } - } -} -{% endhighlight %}</pre> -<p>Note that the index element 3 needs to exist. Alternative using match:</p> -<pre>{% highlight json %} -{ - "update": "id:mynamespace:cakes:tasty_chocolate_cake", - "fields": { - "ingredients": { - "match": { - "element": 3, - "assign": "2 cups of flour (editor's update: NOT asbestos!)" - } - } - } -} -{% endhighlight %}</pre> -<p> - Individual array elements may be updated using <a href="document-field-path.html">field path</a> - or <a href="#match">match</a> syntax. -</p> - -<h4 id="array-of-struct">Array of struct</h4> -<p> -Refer to the reference for restrictions using -<a href="schemas.html#array">array of structs</a>. -</p> -<pre> -struct person { - field first_name type string {} - field last_name type string {} -} -field people type array<person> { - indexing: summary -} -</pre> -<pre>{% highlight json %} -{ - "update": "id:mynamespace:students:example", - "fields": { - "people[34]": { - "assign": { - "first_name": "Bobby", - "last_name": "Tables" - } - } - } -} -{% endhighlight %}</pre> -<p>Note that the element index needs to exist. Use <a href="#add-array-elements">add</a> to add a new element. -Alternative syntax using match:</p> -<pre>{% highlight json %} -{ - "update": "id:mynamespace:students:example", - "fields": { - "people": { - "match": { - "element": 34, - "assign": { - "first_name": "Bobby", - "last_name": "Tables" - } - } - } - } -} -{% endhighlight %}</pre> - - -<h3 id="weighted-set-field">Weighted set field</h3> -<p> -Adding new elements to a weighted set can be done using <a href="#add-weighted-set">add</a>, or -by assigning with <code>field{key}</code> syntax. Example of the latter: -</p> -<pre> -field int_weighted_set type weightedset<int> { - indexing: summary -} -field string_weighted_set type weightedset<string> { - indexing: summary -} -</pre> -<pre>{% highlight json %} -{ - "update":"id:mynamespace:weightedsetdoctype::example1", - "fields": { - "int_weighted_set{123}": { - "assign": 123 - }, - "int_weighted_set{456}": { - "assign": 100 - }, - "string_weighted_set{\"item 1\"}": { - "assign": 144 - }, - "string_weighted_set{\"item 2\"}": { - "assign": 7 - } - } -} -{% endhighlight %}</pre> -<p> -Note that using the <code>field{key}</code> syntax for weighted sets <em>may</em> be -less efficient than using <a href="#add-weighted-set">add</a>. -</p> - - -<h3 id="clearing-a-field">Clearing a field</h3> -<p>To clear a field, assign a <code>null</code> value to it.</p> -<pre>{% highlight json %} -{ - "update": "id:mynamespace:music::example", - "fields": { - "title": { - "assign": null - } - } -} -{% endhighlight %}</pre> - - - -<h2 id="add">add</h2> -<p> -<code>add</code> is used to add entries to arrays, weighted sets or to the mapped dimensions of tensors. -</p> - - -<h3 id="add-array-elements">Adding array elements</h3> -<p> -The added entries are appended to the end of the array in the order specified. -</p> -<pre> -field tracks type array<string> { - indexing: summary -} -</pre> -<pre>{% highlight json %} -{ - "update": "id:mynamespace:music::https://music.yahoo.com/bobdylan/BestOf", - "fields": { - "tracks": { - "add": [ - "Lay Lady Lay", - "Every Grain of Sand" - ] - } - } -} -{% endhighlight %}</pre> - - -<h3 id="add-weighted-set">Add weighted set entries</h3> -<p> -Add weighted set elements by using a JSON key/value syntax, -where the value is the weight of the element. -</p> -<p>Adding a key/weight mapping that already exists will overwrite the existing weight with the new one.</p> -<pre> -field int_weighted_set type weightedset<int> { - indexing: summary -} -field string_weighted_set type weightedset<string> { - indexing: summary -} -</pre> -<pre>{% highlight json %} -{ - "update":"id:mynamespace:weightedsetdoctype::example1", - "fields": { - "int_weighted_set": { - "add": { - "123": 123, - "456": 100 - } - }, - "string_weighted_set": { - "add": { - "item 1": 144, - "item 2": 7 - } - } - } -} -{% endhighlight %}</pre> - - -<h3 id="tensor-add">Add tensor cells</h3> -<p> -Add cells to mapped or mixed tensors. Invalid for tensors with only indexed -dimensions. Adding a cell that already exists will overwrite the cell value with the new value. -The address must be fully specified, but cells with bound indexed dimensions not specified -will receive the default value of <code>0.0</code>. -See system test -<a href="https://github.com/vespa-engine/system-test/tree/master/tests/search/tensor_feed/tensor_add_remove_update">tensor add update</a> -for more examples. -</p> -<pre> -field tensorfield type tensor(x{},y[3]) { - indexing: attribute | summary -} -</pre> -<pre>{% highlight json %} -{ - "update": "id:mynamespace:tensordoctype::example", - "fields": { - "tensorfield": { - "add": { - "cells": [ - { "address": { "x": "b", "y": "0" }, "value": 2.0 }, - { "address": { "x": "b", "y": "1" }, "value": 3.0 } - ] - } - } - } -} -{% endhighlight %}</pre> -<p>In this example, cell <code>{"x":"b","y":"2"}</code> will implicitly be set to 0.0.</p> - -<p>So if you started with the following tensor:</p> -<pre> -{ - {"x": "a", "y": "0"}: 0.2, - {"x": "a", "y": "1"}: 0.3, - {"x": "a", "y": "2"}: 0.5, -}</pre> - -<p>You now end up with this tensor after the above add operation was applied:</p> -<pre> -{ - {"x": "a", "y": "0"}: 0.2, - {"x": "a", "y": "1"}: 0.3, - {"x": "a", "y": "2"}: 0.5, - {"x": "b", "y": "0"}: 2.0, - {"x": "b", "y": "1"}: 3.0, - {"x": "b", "y": "2"}: 0.0, -}</pre> - -<p> - Prefer the <em>block short form</em> for mixed tensors instead. - This also avoids the problem where cells with indexed dimensions are not specified: -</p> -<pre>{% highlight json %} -{ - "update": "id:mynamespace:tensordoctype::example", - "fields": { - "tensorfield": { - "add": { - "blocks": [ - { "address": { "x": "b" }, "values": [2.0, 3.0, 5.0] } - ] - } - } - } -} -{% endhighlight %}</pre> - - - -<h2 id="composite-remove">remove</h2> -<p>Remove elements from arrays, weighted sets, maps and tensors with <code>remove</code>.</p> - -<h3 id="array-field-remove">Array field</h3> - -<p>Array fields support removal by value using "remove" and the array of values to remove.</p> - -<p>For example, given this field:</p> - -<pre> -field string_array type array<string> { - indexing: summary -} -</pre> - -<p>This update will remove all the array elements that are "hello" or "1":</p> - -<pre>{% highlight json %} -{ - "update":"id:mynamespace:weightedsetdoctype::example1", - "fields": { - "string_array": { - "remove": ["hello", 1] - } - } -} -{% endhighlight %}</pre> - - -<h3 id="weighted-set-field-remove">Weighted set field</h3> -<pre> -field string_weighted_set type weightedset<string> { - indexing: summary -} -</pre> -<pre>{% highlight json %} -{ - "update":"id:mynamespace:weightedsetdoctype::example1", - "fields": { - "string_weighted_set": { - "remove": { - "item 2": 0 - } - } - } -} -{% endhighlight %}</pre> - -<h3 id="map-field-remove">Map field</h3> -<pre> -field string_map type map<string, string> { - indexing: summary -} -</pre> -<pre>{% highlight json %} -{ - "update":"id:mynamespace:mapdoctype::example1", - "fields": { - "string_map{item 2}": { - "remove": 0 - } - } -} -{% endhighlight %}</pre> - -<h3 id="tensor-remove">Tensor field</h3> -<p> -Removes cells from mapped or mixed tensors. -Invalid for tensors with only indexed dimensions. -Only mapped dimensions should be specified for tensors with both -mapped and indexed dimensions, as all indexed cells the mapped -dimensions point to will be removed implicitly. -See system test -<a href="https://github.com/vespa-engine/system-test/tree/master/tests/search/tensor_feed/tensor_add_remove_update">tensor remove update</a> -for more examples. -</p> -<pre> -field tensorfield type tensor(x{},y[2]) { - indexing: attribute | summary -} -</pre> -<pre>{% highlight json %} -{ - "update": "id:mynamespace:tensordoctype::example", - "fields": { - "tensorfield": { - "remove": { - "addresses": [ - {"x": "b"}, - {"x": "c"} - ] - } - } - } -} -{% endhighlight %}</pre> -<p>In this example, cells <code>{x:b,y:0},{x:b,y:1},{x:c,y:0},{x:c,y:1}</code> will be removed.</p> -<p> -It is also supported to specify only a subset of the mapped dimensions in the addresses. -In that case, all cells that match the label values of the specified dimensions are removed. -In the given example, all cells having label <code>b</code> for dimension <code>x</code> are removed. -</p> -<pre> -field tensorfield type tensor(x{},y{},z[2]) { - indexing: attribute | summary -} -</pre> -<pre>{% highlight json %} -{ - "update": "id:mynamespace:tensordoctype::example", - "fields": { - "tensorfield": { - "remove": { - "addresses": [ - {"x": "b"} - ] - } - } - } -} -{% endhighlight %}</pre> - - - -<h2 id="arithmetic">Arithmetic</h2> -<p> -The four arithmetic operators <code>increment</code>, <code>decrement</code>, -<code>multiply</code> and <code>divide</code> are used to modify <em>single -value</em> numeric values without having to look up the current -value before applying the update. Example: -</p> -<pre> -field sales type int { - indexing: summary | attribute -} -</pre> -<pre>{% highlight json %} -{ - "update": "id:mynamespace:music::https://music.yahoo.com/bobdylan/BestOf", - "fields": { - "sales": { - "increment": 1 - } - } -} -{% endhighlight %}</pre> - - - -<h2 id="match">match</h2> -<p> -If an arithmetic operation is to be done for a specific key -in a <em>weighted set or array</em>, use the <code>match</code> operation: -</p> -<pre> -field track_popularity type weightedset<string> { - indexing: summary | attribute -} -</pre> -<pre>{% highlight json %} -{ - "update": "id:mynamespace:music::https://music.yahoo.com/bobdylan/BestOf", - "fields": { - "track_popularity": { - "match": { - "element": "Lay Lady Lay", - "increment": 1 - } - } - } -} -{% endhighlight %}</pre> -<p> -In other words, for the weighted set "track_popularity", -<code>match</code> the element "Lay Lady Lay", then <code>increment</code> its weight by 1. -See the <a href="schemas.html#weightedset-properties">weightedset properties</a> -reference for how to make incrementing a non-existing key trigger auto-create of the key. - -</p> -<p>If the updated field is an array, the <code>element</code> value would be a positive integer.</p> -{% include note.html content='Only <span style="text-decoration: underline">one</span> -element can be matched per operation.' %} - - - -<h2 id="tensor-modify">Modify tensors</h2> -<p> -Individual cells in tensors can be modified using the <code>modify</code> update. -The cells are modified according to the given operation: -</p> -<ul> - <li><code>replace</code> - replaces a single cell value</li> - <li><code>add</code> - adds a value to the existing cell value</li> - <li><code>multiply</code> - multiples a value with the existing cell value</li> -</ul> -<p> -The addresses of cells must be fully specified. If the cell does not exist, the update for that cell will be ignored. -Use <code>"create": true</code> (see example below) to create non-existing cells before the modify update is applied. -See system test -<a href="https://github.com/vespa-engine/system-test/tree/master/tests/search/tensor_feed/tensor_modify_update">tensor modify update</a> -for more examples. -</p> -<pre> -field tensorfield type tensor(x[3]) { - indexing: attribute | summary -} -</pre> -<pre>{% highlight json %} -{ - "update": "id:mynamespace:tensordoctype::example", - "fields": { - "tensorfield": { - "modify": { - "operation": "replace", - "addresses": [ - { "address": { "x": "1" }, "value": 7.0 }, - { "address": { "x": "2" }, "value": 8.0 } - ] - } - } - } -} -{% endhighlight %}</pre> -<p> -In this example, cell <code>{"x":"1"}</code> is replaced with value 7.0 and <code>{"x":"2"}</code> with value 8.0. -If operation <code>add</code> or <code>multiply</code> was used instead, -7.0 and 8.0 would be added or multiplied to the current values of cells <code>{"x":"1"}</code> and <code>{"x":"2"}</code>. -</p> - -<p>For tensors with a single mapped dimension the <em>cells short form</em> can also be used:</p> -<pre> -field tensorfield type tensor(x{}) { - indexing: attribute | summary -} -</pre> -<pre>{% highlight json %} -{ - "update": "id:mynamespace:tensordoctype::example", - "fields": { - "tensorfield": { - "modify": { - "operation": "add", - "create": true, - "cells": { - "b": 5.0, - "c": 6.0 - } - } - } - } -} -{% endhighlight %}</pre> -<p> -In this example, 5.0 is added to cell <code>{"x":"b"}</code> and 6.0 is added to cell <code>{"x":"c"}</code>. -With <code>"create": true</code> non-existing cells in the input tensor are created before applying the modify update. -The default cell value is 0.0 for <code>replace</code> and <code>add</code>, and 1.0 for <code>multiply</code>. -This means a non-existing cell ends up with the value specified in the operation. -</p> - -<p>For mixed tensors the <em>block short form</em> can also be used to modify entire dense subspaces:</p> -<pre> -field tensorfield type tensor(x{},y[3]) { - indexing: attribute | summary -} -</pre> -<pre>{% highlight json %} -{ - "update": "id:mynamespace:tensordoctype::example", - "fields": { - "tensorfield": { - "modify": { - "operation": "replace", - "blocks": { - "a": [1,2,3], - "b": [4,5,6] - } - } - } - } -} -{% endhighlight %}</pre> - - - -<h2 id="fieldpath">Fieldpath</h2> -<p> -Fieldpath is for accessing fields within composite structures - -for structures that are not part of index or attribute, -it is possible to access elements directly using fieldpaths. -This is done by adding more information to the field value. -For map structures, specify the key (see <a href="#assign">example</a>). -</p> -<pre> -mymap{mykey} -</pre> -<p> -and then do operation on the element which is keyed by "mykey". -Arrays can be accessed as well (see <a href="#assign">details</a>). -</p> -<pre> -myarray[3] -</pre> -<p> -And this is also true for structs (see <a href="#assign">details</a>). -<strong>Note:</strong> Struct updates do not work for -<a href="../applications/services/content.html#document">index</a> mode: -</p> -<pre> -mystruct.value1 -</pre> -<p> -This also works for nested structures, -e.g. a <code>map</code> of <code>map</code> to <code>array</code> of <code>struct</code>: -</p> -<pre>{% highlight json %} -{ - "update": "id:mynamespace:complexdoctype::foo", - "fields": { - "nested_structure{firstMapKey}{secondMapKey}[4].title": { - "assign": "Look at me, mom! I'm hiding deep in a nested type!" - } - } -} -{% endhighlight %}</pre> diff --git a/en/reference/schemas/document-json-format.mdx b/en/reference/schemas/document-json-format.mdx new file mode 100644 index 0000000000..212d5f6ffa --- /dev/null +++ b/en/reference/schemas/document-json-format.mdx @@ -0,0 +1,1031 @@ +--- +title: "Document JSON format reference" +sidebarTitle: "document JSON format" +--- + +This document describes the JSON format used for sending document operations to Vespa. Field types are defined in the [schema reference](/en/reference/schemas/schemas#field). This is a reference for: + +- JSON representation of [document operations](#document-operations) (put, get, remove, update) +- JSON representation of [field types](#field-types) in Vespa documents +- JSON representation of addressing fields for update, and [update operations](#update-operations) + +Also refer to [encoding troubleshooting](/en/linguistics/troubleshooting-encoding). + +```bash expandable +Document operations + Put + Get + Remove + Update + Test and set + Create +Field types + string + int + long + bool + byte + float + double + position + predicate + raw + uri + array + weightedset + Tensors + Indexed tensors short form + Short form for tensors with a single mapped dimension + Mixed tensors short form + Cell values as binary data (hex dump format) + Tensor verbose form + struct + map + reference + Empty fields +Update operations + assign + Single value field + Assign tensor + Assign struct field + Replacing entire struct + Replace individual struct fields + Assign map field + Map to primitive value + Map to struct + Arrays + Array of primitive values + Array of struct + Weighted set field + Clearing a field + add + Add array elements + Add to weighted set + Add to tensor + Remove elements in composites + Remove from weighted set field + Remove from map field + Remove from tensor + Arithmetic + match + Tensor modify + Fieldpath +``` + +## Field types + +Unless otherwise noted, these formats are used both for returned values in read operations, and as input in write operations (put operations and field assign update operations). + + +<table className="vespa-table"> + <thead> + <tr> + <th></th> + <th></th> + </tr> + </thead> + <tbody> + <tr> + <td>string</td> + <td><pre><code>{`"name": "Polly"`}</code></pre> Feeding in an empty string ("") for a field will have the same effect as not feeding a value for that field, and the field will not be rendered in the document API and in document summaries.</td> + </tr> + <tr> + <td>int</td> + <td><pre><code>{`"age": 42`}</code></pre></td> + </tr> + <tr> + <td>long</td> + <td><pre><code>{`"current_time_ms": 1742837807000`}</code></pre></td> + </tr> + <tr> + <td>bool</td> + <td>*true* or *false*: <pre><code>{`"alive": false`}</code></pre></td> + </tr> + <tr> + <td>byte</td> + <td><pre><code>{`"tinynumber": 128`}</code></pre></td> + </tr> + <tr> + <td>float</td> + <td><pre><code>{`"weight": 123.4567`}</code></pre></td> + </tr> + <tr> + <td>double</td> + <td><pre><code>{`"weight": 123.4567`}</code></pre></td> + </tr> + <tr> + <td>position</td> + <td>A position is encoded as a lat/lng object: <pre><code>{`"mypos": { "lat": 37.4181488, "lng": -122.0256157 }`}</code></pre> See <a href="/en/querying/geo-search" aria-label="Geo Search">Geo Search</a> for details.</td> + </tr> + <tr> + <td>predicate</td> + <td>A <a href="/en/reference/schemas/schemas#predicate" aria-label="predicate">predicate</a> is represented with a string: <pre><code>{`"predicate_field": "gender in [Female] and age in [20..30] and pos in [1..4]"`}</code></pre></td> + </tr> + <tr> + <td>raw</td> + <td>The content of a <a href="/en/reference/schemas/schemas#raw" aria-label="raw">raw</a> field is represented as a base64-encoded string: <pre><code>{`"raw_field": "VW5rbm93biBhcnRpc3QgZnJvbSB0aGUgbW9vbg=="`}</code></pre> When used as *summary* field it will be rendered as a base64-encoded string.</td> + </tr> + <tr> + <td>uri</td> + <td>A URI is a string: <pre><code>{`"url": "https://www.yahoo.com/"`}</code></pre></td> + </tr> + <tr> + <td>array</td> + <td>Arrays are represented as JSON arrays. <pre><code>{`"int_array_field": [ 123, 456, 789 ] "string_array_field": [ "item 1", "item 2", "item 3" ]`}</code></pre> An array of struct is represented as a JSON array of JSON objects matching the defined struct field: <pre><code>{`"array_of_struct_field": [ { "first_name": "Chris", "last_name": "Martin" }, { "first_name": "James", "last_name": "Hetfield" }, { "first_name": "Diana", "last_name": "Krall" } ]`}</code></pre> Feeding in an empty array ([]) for a field will have the same effect as not feeding a value for that field, and the field will not be rendered in the document API and in document summaries.</td> + </tr> + <tr> + <td>weightedset</td> + <td>Weighted sets are represented as maps where the value is the weight. Note, even if the key is not a string as such, it will be represented as a string in the JSON format. <pre><code>{`"int_weighted_set": { "123": 2, "456": 78 } "string_weighted_set": { "item 1": 143, "item 2": 6 }`}</code></pre> Feeding in an empty weightedset ({}) for a field will have the same effect as not feeding a value for that field, and the field will not be rendered in the document API and in document summaries.</td> + </tr> + <tr> + <td>tensor</td> + <td>**Indexed tensors short form:** An array where the values are ordered in the standard value order, where indexes of dimensions to the right are incremented before indexes to the left, where dimensions are ordered alphabetically (such that, e.g. with a tensor with dimensions x,y the "y" values for each value of "x" are adjacent): <pre><code>{`"tensorfield": [ 2.0, 3.0, 5.0, 7.0 ]`}</code></pre> The cells array can optionally be nested in an object under the key "values". This is how tensor values are returned <a href="/en/reference/api/document-v1#format.tensors" aria-label="by default">by default</a>, along with another key "type" containing the tensor type. <br/><br/> **Short form for tensors with a single mapped dimension**: A map with the dimension key as key and the value as value.<pre><code>{`"tensorfield": { "a": 2.0, "b": 3.0 }`}</code></pre> The cells object can optionally be nested in an object under the key "cells". This is how tensor values are returned <a href="/en/reference/api/document-v1#format.tensors" aria-label="by default">by default</a>, along with another key "type" containing the tensor type.<br/> **Mixed tensors short form:** If the tensor has a single sparse dimension: A map where the key is the value of that dimension and the value is a nested array containing the values of the dense subspace within that key. If the tensor has multiple sparse dimensions: An array nested in a "blocks" element where the elements consist of a map with the keys "address" and "values", where "address" is a map with the sparse dimensions and their values (as in cells), and "values" is a nested array containing the values of the dense subspace within that address.<br/><br/> Example - single sparse dimension: <br/><br/> <pre><code>{`"tensorfield": { "x1":[2.0,3.0], "x2":[4.0,5.0] }`}</code></pre> Example - multiple sparse dimensions: <pre><code>{`"tensorfield": { "blocks": [ {"address":{"x":"x1","y":"y2"},"values":[2.0,3.0]}, {"address":{"x":"x2","y":"y2"},"values":[4.0,5.0]} ] }`}</code></pre> This is how tensor values are returned <a href="/en/reference/api/document-v1#format.tensors" aria-label="by default">by default</a>, along with another key "type" containing the tensor type. <br/><br/> **Cell values as binary data** For dense and mixed tensors it's possible to fill the cell values directly from binary data sent in as a string of hexadecimal digits. The simplest possible case is if you have a vector with <code>{`int8`}</code> cell value type: <pre><code>{`"tensorfield": { "values": "FF00118022FE" }`}</code></pre> This can be used to represent the value <code>{`tensor<int8>(x[6]):[-1,0,17,-128,34,-2]`}</code>. <br/><br/> For other cell types, it's possible to take the bits of the floating-point value, interpreted directly as an unsigned integer of appropriate width (16, 32, or 64 bits) and use the hex dump (respectively 4, 8, or 16 hex digits per cell) in a string. For "float" cells (32-bit IEE754 floating-point) a simple snippet for converting a cell could look like this: <pre><code>{`import struct def float_to_hex(f: float): return format(struct.unpack('=I', struct.pack('=f', f))[0], '08X')`}</code></pre> As an advanced combination example, if you have a tensor with type <code>{`tensor<float>(tag{},x[3])`}</code> this input could be used, shown with corresponding output: <pre><code>{`"mixedtensor": { "foo": "3DE38E393E638E393EAAAAAB", "bar": "3EE38E393F0E38E43F2AAAAB", "baz": "3F471C723F638E393F800000" } "mixedtensor":{ "type":"tensor<float>(tag{},x[3])", "blocks":{ "foo":[0.1111111119389534,0.2222222238779068,0.3333333432674408], "bar":[0.4444444477558136,0.5555555820465088,0.6666666865348816], "baz":[0.7777777910232544,0.8888888955116272,1.0] } }`}</code></pre> **Verbose:** <a href="/en/ranking/tensor-user-guide" aria-label="Tensor">Tensor</a> fields may be represented as an array of cells: <pre><code>{`"tensorfield": [ { "address": { "x": "a", "y": "0" }, "value": 2.0 }, { "address": { "x": "a", "y": "1" }, "value": 3.0 }, { "address": { "x": "b", "y": "0" }, "value": 4.0 }, { "address": { "x": "b", "y": "1" }, "value": 5.0 } ]`}</code></pre> This works for any tensor but is verbose, so shorter forms specific to various tensor types are also supported. Use the shortest form applicable to your tensor type for the best possible performance. <br/> The cells array can optionally be nested in an object under the key "cells". This is how tensor values are returned <a href="/en/reference/api/document-v1#format.tensors" aria-label="by default">by default</a>, along with another key "type" containing the tensor type.</td> + </tr> + <tr> + <td>struct</td> + <td><pre><code>{`"mystruct": { "intfield": 123, "stringfield": "foo" }`}</code></pre></td> + </tr> + <tr> + <td>map</td> + <td>The JSON dictionary key must be a string, even if the map key type in the schema is not a string: <pre><code>{`"int_to_string_map": { "123": "foo", "456": "bar", "789": "foobar" }`}</code></pre> Feeding in an empty map ({}) for a field will have the same effect as not feeding a value for that field, and the field will not be rendered in the document API and in document summaries.</td> + </tr> + <tr> + <td>reference</td> + <td>String with document ID referring to a <a href="/en/schemas/parent-child" aria-label="parent document">parent document</a>: <pre><code>{`"artist_ref": "id:mynamespace:artists::artist-1"`}</code></pre></td> + </tr> + </tbody> +</table> + + +## Empty fields + +In general, fields that have not received a value during feeding will be ignored when rendering the document. They are considered as empty fields. However, certain field types have some values which causes them to be considered empty. For instance, the empty string ("") is considered empty, as well as the empty array ([]). See the above table for more information for each type. + +## Document operations + +Refer to [reads and writes](/en/writing/reads-and-writes) for details - alternatives: + +- Use the [Vespa CLI](/en/clients/vespa-cli#documents). +- [/document/v1/](/en/reference/api/document-v1): This API accepts one operation per request, with the document ID encoded in the URL. +- [Vespa feed client](/en/clients/vespa-feed-client): Java APIs / command line tool to feed document operations asynchronously to Vespa, over HTTP. + +### Put + +The "put" payload has a "put" operation and ["fields"](#field-types) containing field values; ([/document/v1/ example](/en/writing/document-v1-api-guide#post)): + +```json +{ + "put": "id:mynamespace:music::123", + "fields": { + "title": "Best of Bob Dylan" + } +} +``` + +### Get + +"get" does not have a payload - the response has the same "field" object as in "put", and also "id" and "pathId" fields ([/document/v1/ example](/en/writing/document-v1-api-guide#get)): + +```json +{ + "pathId": "/document/v1/mynamespace/music/docid/123", + "id": "id:mynamespace:music::123", + "fields": { + "title": "Best of Bob Dylan" + } +} +``` + +### Remove + +The "remove" payload only has a "remove" operation ([/document/v1/ example](/en/writing/document-v1-api-guide#delete)): + +```json +{ + "remove": "id:mynamespace:music::123" +} +``` + +### Update + +The "update" payload has an "update" operation and "fields". Note: Each field must contain an [update operation](#update-operations), not just the field value directly; ([/document/v1/ example](/en/writing/document-v1-api-guide#put)): + +```json +{ + "update": "id:mynamespace:music::123", + "fields": { + "title": { + "assign": "The best of Bob Dylan" + } + } +} +``` + +Flags can be added to add a [test and set](#test-and-set) condition, or allow the update to [create](#create) a new document (a so-called "upsert" operation). + +#### Test and set + +An optional _condition_ can be added to operations to specify a _test and set_ condition - see [conditional writes](/en/writing/document-v1-api-guide#conditional-writes). The value of the _condition_ is a [document selection](/en/reference/writing/document-selector-language), encoded as a string. Example: Increment the _sales_ field only if it is already equal to 999 ([/document/v1/ example](/en/writing/document-v1-api-guide#conditional-writes)): + +```json +{ + "update": "id:mynamespace:music::bob/BestOf", + "condition": "music.sales==999", + "fields": { + "sales": { + "increment": 1 + } + } +} +``` + +<Info> + **Note:** Use _documenttype.fieldname_ in the condition, not only _fieldname_. +</Info> + +If the condition is not met, a 412 response code is returned. + +#### create (create if nonexistent) + +**Updates** to nonexistent documents are supported using _create_; ([/document/v1/ example](/en/writing/document-v1-api-guide#create-if-nonexistent)): + +```json +{ + "update": "id:mynamespace:music::bob/BestOf", + "create": true, + "fields": { + "title": { + "assign": "The best of Bob Dylan" + } + } +} +``` + +Since Vespa 8.178, _create_ can also be used together with conditional **Put** operations ([/document/v1/ example](/en/writing/document-v1-api-guide#conditional-updates-and-puts-with-create) - review notes there before using): + +```json +{ + "put": "id:mynamespace:music::123", + "condition": "music.sales==999", + "create": true, + "fields": { + "title": "Best of Bob Dylan" + } +} +``` + +## Update operations + +The update operations are: [`assign`](#assign), [`add`](#add), [`remove`](#composite-remove), [arithmetics](#arithmetic) (`increment` `decrement` `multiply` `divide`), [`match`](#match), [`modify`](#tensor-modify) + +## assign + +`assign` is used to replace the value of a field (or an element of a collection) with a new value. When assigning, one can generally use the same syntax and structure as when feeding that field's value in a `put` operation. + +### Single value field + +```js +field title type string { + indexing: summary +} +``` + +```json +{ + "update": "id:mynamespace:music::example", + "fields": { + "title": { + "assign": "The best of Bob Dylan" + } + } +} +``` + +### Tensor field + +```js +field tensorfield type tensor(x{},y{}) { + indexing: attribute | summary +} +``` + +```json +{ + "update": "id:mynamespace:tensordoctype::example", + "fields": { + "tensorfield": { + "assign": { + "cells": [ + { "address": { "x": "a", "y": "b" }, "value": 2.0 }, + { "address": { "x": "c", "y": "d" }, "value": 3.0 } + ] + } + } + } +} +``` + +This will fully replace the entire tensor stored in this field. + +### Struct field + +#### Replacing all fields in a struct + +A full struct is replaced by assigning an object of struct key/value pairs. + +```js +struct person { + field first_name type string {} + field last_name type string {} +} +field contact type person { + indexing: summary +} +``` + +```json +{ + "update": "id:mynamespace:workers::example", + "fields": { + "contact": { + "assign": { + "first_name": "Bob", + "last_name": "The Plumber" + } + } + } +} +``` + +#### Individual struct fields + +Individual struct fields are updated using [field path](#fieldpath) syntax. Refer to the [reference](/en/reference/schemas/schemas#struct-name) for restrictions using structs. + +```json +{ + "update": "id:mynamespace:workers::example", + "fields": { + "contact.first_name": { + "assign": "Bob" + }, + "contact.last_name": { + "assign": "The Plumber" + } + } +} +``` + +### Map field + +Individual map entries can be updated using [field path](/en/reference/schemas/document-field-path) syntax. The following declaration defines a `map` where the `key` is an Integer and the value is a `person` struct. + +```js +struct person { + field first_name type string {} + field last_name type string {} +} +field contact type map<int, person> { + indexing: summary +} +``` + +Example updating part of an entry in the `contact` map: + +- `contact` is the name of the map field to be updated +- `{0}` is the key that is going to be updated +- `first_name` is the struct field to be updated inside the `person` struct + +```json +{ + "update": "id:mynamespace:workers::example", + "fields": { + "contact{0}.first_name": { + "assign": "John" + } + } +} +``` + +Assigning an element to a key in a map will insert the key/value mapping if it does not already exist, or overwrite it with the new value if it does exist. Refer to the [reference](/en/reference/schemas/schemas#map) for restrictions using maps. + +#### Map to primitive value + +```js +field my_food_scores type map<string, string> { + indexing: summary +} +``` + +```json +{ + "update": "id:mynamespace:food::example", + "fields": { + "my_food_scores{Strawberries}": { + "assign": "Delicious!" + } + } +} +``` + +#### Map to struct + +```js +struct contact_info { + field phone_number type string {} + field email type string {} +} +field contacts type map<string, contact_info> { + indexing: summary +} +``` + +```json +{ + "update": "id:mynamespace:people::d_duck", + "fields": { + "contacts{\"Uncle Scrooge\"}": { + "assign": { + "phone_number": "555-123-4567", + "email": "number_one_dime_luvr1877@example.com" + } + } + } +} +``` + +### Array field + +#### Array of primitive values + +```js +field ingredients type array<string> { + indexing: summary +} +``` + +Assign full array: + +```json +{ + "update": "id:mynamespace:cakes:tasty_chocolate_cake", + "fields": { + "ingredients": { + "assign": ["sugar", "butter", "vanilla", "flour"] + } + } +} +``` + +Assign existing elements in array: + +```json +{ + "update": "id:mynamespace:cakes:tasty_chocolate_cake", + "fields": { + "ingredients[3]": { + "assign": "2 cups of flour (editor's update: NOT asbestos!)" + } + } +} +``` + +Note that the index element 3 needs to exist. Alternative using match: + +```json +{ + "update": "id:mynamespace:cakes:tasty_chocolate_cake", + "fields": { + "ingredients": { + "match": { + "element": 3, + "assign": "2 cups of flour (editor's update: NOT asbestos!)" + } + } + } +} +``` + +Individual array elements may be updated using [field path](/en/reference/schemas/document-field-path) or [match](#match) syntax. + +#### Array of struct + +Refer to the reference for restrictions using [array of structs](/en/reference/schemas/schemas#array). + +```js +struct person { + field first_name type string {} + field last_name type string {} +} +field people type array<person> { + indexing: summary +} +``` + +```json +{ + "update": "id:mynamespace:students:example", + "fields": { + "people[34]": { + "assign": { + "first_name": "Bobby", + "last_name": "Tables" + } + } + } +} +``` + +Note that the element index needs to exist. Use [add](#add-array-elements) to add a new element. Alternative syntax using match: + +```json +{ + "update": "id:mynamespace:students:example", + "fields": { + "people": { + "match": { + "element": 34, + "assign": { + "first_name": "Bobby", + "last_name": "Tables" + } + } + } + } +} +``` + + +### Weighted set field + +Adding new elements to a weighted set can be done using [add](#add-weighted-set), or by assigning with `field{key}` syntax. Example of the latter: + +```js +field int_weighted_set type weightedset<int> { + indexing: summary +} +field string_weighted_set type weightedset<string> { + indexing: summary +} +``` + +```json +{ + "update":"id:mynamespace:weightedsetdoctype::example1", + "fields": { + "int_weighted_set{123}": { + "assign": 123 + }, + "int_weighted_set{456}": { + "assign": 100 + }, + "string_weighted_set{\"item 1\"}": { + "assign": 144 + }, + "string_weighted_set{\"item 2\"}": { + "assign": 7 + } + } +} +``` + +Note that using the `field{key}` syntax for weighted sets _may_ be less efficient than using [add](#add-weighted-set). + +### Clearing a field + +To clear a field, assign a `null` value to it. + +```json +{ + "update": "id:mynamespace:music::example", + "fields": { + "title": { + "assign": null + } + } +} +``` + +## add + +`add` is used to add entries to arrays, weighted sets or to the mapped dimensions of tensors. + +### Adding array elements + +The added entries are appended to the end of the array in the order specified. + +```js +field tracks type array<string> { + indexing: summary +} +``` + +```json +{ + "update": "id:mynamespace:music::https://music.yahoo.com/bobdylan/BestOf", + "fields": { + "tracks": { + "add": [ + "Lay Lady Lay", + "Every Grain of Sand" + ] + } + } +} +``` + +### Add weighted set entries + +Add weighted set elements by using a JSON key/value syntax, where the value is the weight of the element. + +Adding a key/weight mapping that already exists will overwrite the existing weight with the new one. + +```js +field int_weighted_set type weightedset<int> { + indexing: summary +} +field string_weighted_set type weightedset<string> { + indexing: summary +} +``` + +```json +{ + "update":"id:mynamespace:weightedsetdoctype::example1", + "fields": { + "int_weighted_set": { + "add": { + "123": 123, + "456": 100 + } + }, + "string_weighted_set": { + "add": { + "item 1": 144, + "item 2": 7 + } + } + } +} +``` + +### Add tensor cells + +Add cells to mapped or mixed tensors. Invalid for tensors with only indexed dimensions. Adding a cell that already exists will overwrite the cell value with the new value. The address must be fully specified, but cells with bound indexed dimensions not specified will receive the default value of `0.0`. See system test [tensor add update](https://github.com/vespa-engine/system-test/tree/master/tests/search/tensor_feed/tensor_add_remove_update) for more examples. + +```js +field tensorfield type tensor(x{},y[3]) { + indexing: attribute | summary +} +``` + +```json +{ + "update": "id:mynamespace:tensordoctype::example", + "fields": { + "tensorfield": { + "add": { + "cells": [ + { "address": { "x": "b", "y": "0" }, "value": 2.0 }, + { "address": { "x": "b", "y": "1" }, "value": 3.0 } + ] + } + } + } +} +``` + +In this example, cell `{"x":"b","y":"2"}` will implicitly be set to 0.0. + +So if you started with the following tensor: + +```json +{ + {"x": "a", "y": "0"}: 0.2, + {"x": "a", "y": "1"}: 0.3, + {"x": "a", "y": "2"}: 0.5, +} +``` + +You now end up with this tensor after the above add operation was applied: + +```json +{ + {"x": "a", "y": "0"}: 0.2, + {"x": "a", "y": "1"}: 0.3, + {"x": "a", "y": "2"}: 0.5, + {"x": "b", "y": "0"}: 2.0, + {"x": "b", "y": "1"}: 3.0, + {"x": "b", "y": "2"}: 0.0, +} +``` + +Prefer the _block short form_ for mixed tensors instead. This also avoids the problem where cells with indexed dimensions are not specified: + +```json +{ + "update": "id:mynamespace:tensordoctype::example", + "fields": { + "tensorfield": { + "add": { + "blocks": [ + { "address": { "x": "b" }, "values": [2.0, 3.0, 5.0] } + ] + } + } + } +} +``` + +## remove + +Remove elements from arrays, weighted sets, maps and tensors with `remove`. + +### Array field + +Array fields support removal by value using "remove" and the array of values to remove. + +For example, given this field: + +```js +field string_array type array<string> { + indexing: summary +} +``` + +This update will remove all the array elements that are "hello" or "1": + +```json +{ + "update":"id:mynamespace:weightedsetdoctype::example1", + "fields": { + "string_array": { + "remove": ["hello", 1] + } + } +} +``` + +### Weighted set field + +```js +field string_weighted_set type weightedset<string> { + indexing: summary +} +``` + +```json +{ + "update":"id:mynamespace:weightedsetdoctype::example1", + "fields": { + "string_weighted_set": { + "remove": { + "item 2": 0 + } + } + } +} +``` + +### Map field + +``` +field string_map type map<string, string> { + indexing: summary +} +``` + +```json +{ + "update":"id:mynamespace:mapdoctype::example1", + "fields": { + "string_map{item 2}": { + "remove": 0 + } + } +} +``` + +### Tensor field + +Removes cells from mapped or mixed tensors. Invalid for tensors with only indexed dimensions. Only mapped dimensions should be specified for tensors with both mapped and indexed dimensions, as all indexed cells the mapped dimensions point to will be removed implicitly. See system test [tensor remove update](https://github.com/vespa-engine/system-test/tree/master/tests/search/tensor_feed/tensor_add_remove_update) for more examples. + +```js +field tensorfield type tensor(x{},y[2]) { + indexing: attribute | summary +} +``` + +```json +{ + "update": "id:mynamespace:tensordoctype::example", + "fields": { + "tensorfield": { + "remove": { + "addresses": [ + {"x": "b"}, + {"x": "c"} + ] + } + } + } +} +``` + +In this example, cells `{x:b,y:0},{x:b,y:1},{x:c,y:0},{x:c,y:1}` will be removed. + +It is also supported to specify only a subset of the mapped dimensions in the addresses. In that case, all cells that match the label values of the specified dimensions are removed. In the given example, all cells having label `b` for dimension `x` are removed. + +```js +field tensorfield type tensor(x{},y{},z[2]) { + indexing: attribute | summary +} +``` + +```json +{ + "update": "id:mynamespace:tensordoctype::example", + "fields": { + "tensorfield": { + "remove": { + "addresses": [ + {"x": "b"} + ] + } + } + } +} +``` + +## Arithmetic + +The four arithmetic operators `increment`, `decrement`, `multiply` and `divide` are used to modify _single value_ numeric values without having to look up the current value before applying the update. Example: + +```js +field sales type int { + indexing: summary | attribute +} +``` + +```json +{ + "update": "id:mynamespace:music::https://music.yahoo.com/bobdylan/BestOf", + "fields": { + "sales": { + "increment": 1 + } + } +} +``` + +## match + +If an arithmetic operation is to be done for a specific key in a _weighted set or array_, use the `match` operation: + +```js +field track_popularity type weightedset<string> { + indexing: summary | attribute +} +``` + +```json +{ + "update": "id:mynamespace:music::https://music.yahoo.com/bobdylan/BestOf", + "fields": { + "track_popularity": { + "match": { + "element": "Lay Lady Lay", + "increment": 1 + } + } + } +} +``` + +In other words, for the weighted set "track\_popularity", `match` the element "Lay Lady Lay", then `increment` its weight by 1. See the [weightedset properties](/en/reference/schemas/schemas#weightedset-properties) reference for how to make incrementing a non-existing key trigger auto-create of the key. + +If the updated field is an array, the `element` value would be a positive integer. + +<Info> + **Note:** Only one element can be matched per operation. +</Info> + +## Modify tensors + +Individual cells in tensors can be modified using the `modify` update. The cells are modified according to the given operation: + +- `replace` - replaces a single cell value +- `add` - adds a value to the existing cell value +- `multiply` - multiples a value with the existing cell value + +The addresses of cells must be fully specified. If the cell does not exist, the update for that cell will be ignored. Use `"create": true` (see example below) to create non-existing cells before the modify update is applied. See system test [tensor modify update](https://github.com/vespa-engine/system-test/tree/master/tests/search/tensor_feed/tensor_modify_update) for more examples. + +```js +field tensorfield type tensor(x[3]) { + indexing: attribute | summary +} +``` + +```json +{ + "update": "id:mynamespace:tensordoctype::example", + "fields": { + "tensorfield": { + "modify": { + "operation": "replace", + "addresses": [ + { "address": { "x": "1" }, "value": 7.0 }, + { "address": { "x": "2" }, "value": 8.0 } + ] + } + } + } +} +``` + +In this example, cell `{"x":"1"}` is replaced with value 7.0 and `{"x":"2"}` with value 8.0. If operation `add` or `multiply` was used instead, 7.0 and 8.0 would be added or multiplied to the current values of cells `{"x":"1"}` and `{"x":"2"}`. + +For tensors with a single mapped dimension the _cells short form_ can also be used: + +```js +field tensorfield type tensor(x{}) { + indexing: attribute | summary +} +``` + +```json +{ + "update": "id:mynamespace:tensordoctype::example", + "fields": { + "tensorfield": { + "modify": { + "operation": "add", + "create": true, + "cells": { + "b": 5.0, + "c": 6.0 + } + } + } + } +} +``` + +In this example, 5.0 is added to cell `{"x":"b"}` and 6.0 is added to cell `{"x":"c"}`. With `"create": true` non-existing cells in the input tensor are created before applying the modify update. The default cell value is 0.0 for `replace` and `add`, and 1.0 for `multiply`. This means a non-existing cell ends up with the value specified in the operation. + +For mixed tensors the _block short form_ can also be used to modify entire dense subspaces: + +```js +field tensorfield type tensor(x{},y[3]) { + indexing: attribute | summary +} +``` + +```json +{ + "update": "id:mynamespace:tensordoctype::example", + "fields": { + "tensorfield": { + "modify": { + "operation": "replace", + "blocks": { + "a": [1,2,3], + "b": [4,5,6] + } + } + } + } +} +``` + +## Fieldpath + +Fieldpath is for accessing fields within composite structures - for structures that are not part of index or attribute, it is possible to access elements directly using fieldpaths. This is done by adding more information to the field value. For map structures, specify the key (see [example](#assign)). + +```js +mymap{mykey} +``` + +and then do operation on the element which is keyed by "mykey". Arrays can be accessed as well (see [details](#assign)). + +```js +myarray[3] +``` + +And this is also true for structs (see [details](#assign)). **Note:** Struct updates do not work for [index](/en/reference/applications/services/content#document) mode: + +```json +mystruct.value1 +``` + +This also works for nested structures, e.g. a `map` of `map` to `array` of `struct`: + +```json +{ + "update": "id:mynamespace:complexdoctype::foo", + "fields": { + "nested_structure{firstMapKey}{secondMapKey}[4].title": { + "assign": "Look at me, mom! I'm hiding deep in a nested type!" + } + } +} +``` \ No newline at end of file diff --git a/en/reference/schemas/schemas.html b/en/reference/schemas/schemas.html deleted file mode 100644 index ec514c5370..0000000000 --- a/en/reference/schemas/schemas.html +++ /dev/null @@ -1,5009 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Schema reference" -redirect_from: -- en/reference/schema-reference.html ---- - -<p> - This reference documents the syntax and content of schemas, document types, and fields. - This is a reference, see <a href="../../basics/schemas.html">schemas</a> for an overview and examples. -</p> - - - -<h2 id="syntax">Syntax</h2> -<p> -Throughout this document, a string in square brackets represents some argument. -The whole string, including the brackets, is replaced by a concrete string in a schema. -</p><p> -Constructs in schemas have a regular syntax. -Each element starts with the element <em>identifier</em>, -possibly followed by the <em>name</em> of this particular occurrence of the element, -possibly followed by a space-separated list of interleaved <em>attribute names</em> -and <em>attribute values</em>, possibly followed by the <em>element body</em>. -Thus, one will find elements of these varieties: -</p> -<p> -<code>[element-identifier] : [element-body]</code><br/> -<code>[element-identifier] [element-name] : [element-body]</code><br/> -<code>[element-identifier] [element-name] [attribute-name] [attribute-value]</code><br/> -<code>[element-identifier] [element-name] [attribute-name] [attribute-value] { - [element-body] -}</code><br/> -</p> -<p> - One-line element values start with a colon and end with a newline. -</p> -<p> -Multiline values (for fields supporting them) are any block of text enclosed in curly brackets. -</p> -<p> -Comments may be inserted anywhere and start with a hash (#). -</p> -<p> -Names are <em>identifiers</em>, they must match <code>["a"-"z","A"-"Z", "_"]["a"-"z","A"-"Z","0"-"9","_"]*</code>. -</p> -<p>A schema file is not sensitive to indentation.</p> - - - -<h2 id="elements">Elements</h2> -<p> - Elements and structure of a schema file: -</p> -<pre class="pre-config"> -<a href="#schema">schema</a> - <a href="#document">document</a> - <a href="#struct">struct</a> - <a href="#field">field</a> - <a href="#match">match</a> - <a href="#field">field</a><span style="display:none"> <!-- Keep field types here for the doc search direct display, hide for clarity --> - <a href="#array">array<type></a> - <a href="#bool">bool</a> - <a href="#byte">byte</a> - <a href="#double">double</a> - <a href="#float">float</a> - <a href="#int">int</a> - <a href="#long">long</a> - <a href="#map">map<key-type,value-type></a> - <a href="#position">position</a> - <a href="#predicate">predicate</a> - <a href="#raw">raw</a> - <a href="#reference">reference<document-type></a> - <a href="#string">string</a> - <a href="#struct-name">struct-name</a> - <a href="#tensor">tensor(dimension-1,...,dimension-N)</a> - <a href="#uri">uri</a> - <a href="#weightedset">weightedset<element-type></a> - <a href="#weightedset-properties">weightedset</a></span> - <a href="#alias">alias</a> - <a href="#attribute">attribute</a> - <a href="#distance-metric">distance-metric</a> - <a href="#bolding">bolding</a> - <a href="#dictionary">dictionary</a> - <a href="#id">id</a> - <a href="#index">index</a> - <a href="#index-hnsw">hnsw</a> - <a href="#indexing">indexing</a> - <a href="#linguistics">linguistics</a> - <a href="#match">match</a> - <a href="#normalizing">normalizing</a> - <a href="#query-command">query-command</a> - <a href="#rank">rank</a> - <a href="#rank-type">rank-type</a> - <a href="#sorting">sorting</a> - <a href="#stemming">stemming</a> - <a href="#struct-field">struct-field</a> - <a href="#indexing">indexing</a> - <a href="#match">match</a> - <a href="#query-command">query-command</a> - <a href="#struct-field">struct-field</a> - … - <a href="#summary">summary</a> - <a href="#summary-to">summary-to <b>DEPRECATED</b></a> - <a href="#summary">summary</a> - <a href="#summary-to">summary-to <b>DEPRECATED</b></a> - <a href="#weight">weight</a> - <a href="#compression">compression</a> - <a href="#index">index</a> - <a href="#field">field</a> - <a href="#fieldset">fieldset</a> - <a href="#rank-profile">rank-profile</a> - <a href="#diversity">diversity</a> - <a href="#diversity-attribute">attribute</a> - <a href="#diversity-min-groups">min-groups</a> - <a href="#match-phase">match-phase</a> - <a href="#match-phase-attribute">attribute</a> - <a href="#match-phase-order">order</a> - <a href="#match-phase-total-max-hits">total-max-hits</a> - <a href="#match-phase-max-hits">max-hits</a> - <a href="#firstphase-rank">first-phase</a> - <a href="#total-keep-rank-count">total-keep-rank-count</a> - <a href="#keep-rank-count">keep-rank-count</a> - <a href="#rank-score-drop-limit">rank-score-drop-limit</a> - <a href="#expression">expression</a> - <a href="#secondphase-rank">second-phase</a> - <a href="#expression">expression</a> - <a href="#secondphase-rank-score-drop-limit">rank-score-drop-limit</a> - <a href="#secondphase-total-rerank-count">total-rerank-count</a> - <a href="#secondphase-rerank-count">rerank-count</a> - <a href="#globalphase-rank">global-phase</a> - <a href="#expression">expression</a> - <a href="#globalphase-rerank-count">rerank-count</a> - <a href="#globalphase-rank-score-drop-limit">rank-score-drop-limit</a> - <a href="#function-rank">function [name] </a> - <a href="#inputs">inputs</a> - <a href="#constants">constants</a> - <a href="#onnx-model">onnx-model</a> - <a href="#significance">significance</a> - <a href="#rank-properties">rank-properties</a> - <a href="#match-features">match-features</a> - <a href="#mutate">mutate</a> - <a href="#on-match">on-match</a> - <a href="#on-first-phase">on-first-phase</a> - <a href="#on-second-phase">on-second-phase</a> - <a href="#on-summary">on-summary</a> - <a href="#summary-features">summary-features</a> - <a href="#rank-features">rank-features</a> - <a href="#ignore-default-rank-features">ignore-default-rank-features</a> - <a href="#num-threads-per-search">num-threads-per-search</a> - <a href="#num-search-partitions">num-search-partitions</a> - <a href="#min-hits-per-thread">min-hits-per-thread</a> - <a href="#termwise-limit">termwise-limit</a> - <a href="#post-filter-threshold">post-filter-threshold</a> - <a href="#approximate-threshold">approximate-threshold</a> - <a href="#filter-first-threshold">filter-first-threshold</a> - <a href="#filter-first-exploration">filter-first-exploration</a> - <a href="#exploration-slack">exploration-slack</a> - <a href="#target-hits-max-adjustment-factor">target-hits-max-adjustment-factor</a> - <a href="#filter-threshold">filter-threshold</a> - <a href="#rank">rank</a> - <a href="#rank-filter-threshold">filter-threshold</a> - <a href="#rank-element-gap">element-gap</a> - <a href="#rank-type">rank-type</a> - <a href="#weakand">weakand</a> - <a href="#weakand-stopword-limit">stopword-limit</a> - <a href="#weakand-adjust-target">adjust-target</a> - <a href="#weakand-allow-drop-all">allow-drop-all</a> - <a href="#rank-profile">rank-profile (inner)</a> - <a href="#constant">constant</a> - <a href="#onnx-model">onnx-model</a> - <a href="#stemming">stemming</a> - <a href="#documentid">documentid</a> - <a href="#document-summary">document-summary</a> - <a href="#summary">summary</a> - <a href="#import-field">import field</a> - <a href="#raw-as-base64-in-summary">raw-as-base64-in-summary</a> -</pre> - - - -<h2 id="schema">schema</h2> - -<p>The root element of schemas. -A schema describes a type of data and what we should compute over it. -A schema must be defined in a file named <code>[schema-name].sd</code>.</p> -<pre> -schema [name] inherits [name] { - [body] -} -</pre> - -<p>The <code>inherits</code> attribute is optional. -If a schema is inherited, this schema will include all the constructs of it as if they -were defined in this schema (except the parent document type). -The document type in this must declare that it inherits the document type of the parent schema. -</p> - -<p>The body is mandatory and may contain:</p> -<table class="table"> - <thead> - <tr><th>Name</th><th>Occurrence</th><th>Description</th></tr> - </thead><tbody> -<tr><td><a href="#document">document</a></td> - <td>One</td> - <td>A document type defined in this schema</td> -</tr> - -<tr><td><a href="#field">field</a></td> - <td style="white-space:nowrap;">Zero to many</td> - <td>A field not contained in the document. - Use <em>synthetic fields</em> (outside <a href="#document">document</a>) to derive new field values - to be placed in the indexing structure from document fields. - Find examples in <a href="../../operations/reindexing.html#use-cases">reindexing</a>.</td> -</tr> - -<tr><td><a href="#fieldset">fieldset</a></td> - <td>Zero to many</td> - <td>Group document fields together for searching</td> -</tr> - -<tr><td><a href="#rank-profile">rank-profile</a></td> - <td>Zero to many</td> - <td>A bundle of ranking functions and settings, selectable in a query.</td> -</tr> - -<tr><td><a href="#constant">constant</a></td> - <td>Zero to many</td> - <td>A constant tensor located in a file used for ranking</td> -</tr> - -<tr><td><a href="#onnx-model">onnx-model</a></td> - <td>Zero to many</td> - <td>An ONNX model located in the application package used for ranking</td> -</tr> - -<tr><td><a href="#stemming">stemming</a></td> - <td>Zero or one</td> - <td>The default stemming setting.</td> -</tr> - -<tr><td><a href="#raw-as-base64-in-summary">raw-as-base64-in-summary</a></td> - <td>Zero or one</td> - <td>Base64 encode raw fields in summary rather than using an escaped string. The default is true.</td> -</tr> - -<tr><td><a href="#documentid">documentid</a></td> - <td>Zero or one</td> - <td>Whether document IDs are stored on disk only or made an attribute.</td> -</tr> - -<tr><td style="white-space: nowrap"><a href="#document-summary">document-summary</a></td> - <td>Zero to many</td> - <td>An explicitly defined document summary</td> -</tr> - -<tr><td><a href="#import-field">import field</a></td> - <td>Zero to many</td> - <td>Import a field value from a global document</td> -</tr> -</tbody> -</table> - - - -<h2 id="document">document</h2> -<p> - Contained in <a href="#schema">schema</a> and describes a document type. - This can also be the root of the schema, if the document is not to be queried directly. -</p> -<pre> -document [name] inherits [name-list] { - [body] -} -</pre> -<p> - The document name is optional; it defaults to the containing <code>schema</code> - element's name. If there is no containing <code>schema</code> element, the document name is required. If the document with a name is defined inside a schema, the document name must match the <code>schema</code> element's name. The reference to <em>document type</em> in the documentation refers to the document name defined here. -</p> -<p> - The <code>inherits</code> attribute is optional - and has as value a comma-separated list of names of other document types. - A document type may inherit the fields of one or more other document types, - see <a href="/en/schemas/inheritance-in-schemas.html">document inheritance</a> for examples. - If no document types are explicitly inherited, - the document inherits the generic <code>document</code> type. -</p> - -<p>The body of a document type is optional and may contain:</p> -<table class="table"> - <thead> - <tr><th>Name</th><th>Occurrence</th><th>Description</th></tr> - </thead><tbody> -<tr><td><a href="#struct">struct</a></td> - <td>Zero to many</td> - <td>A struct type definition for this document.</td> -</tr> - -<tr><td><a href="#field">field</a></td> - <td>Zero to many</td> - <td>A field of this document.</td> -</tr> - -<tr><td><a href="#compression">compression</a></td> - <td>Zero to one</td> - <td>Specifies compression options for documents of this document type in storage.</td> - <!-- ToDo Check does this apply to proton? --> -</tr> -</tbody> -</table> - - - -<h2 id="struct">struct</h2> -<p> -Contained in <a href="#document">document</a>. -Defines a composite type. -A struct consists of zero or more fields that the user can access together as one. -The struct has to be defined before it is used as a type in a field specification. -</p> -<pre> -struct [name] { - [body] -} -</pre> -<p>The body of a struct is optional and may contain:</p> -<table class="table"> -<thead> -<tr><th>Name</th><th>Occurrence</th><th>Description</th></tr> -</thead><tbody> -<tr><td><a href="#field">field</a></td> - <td>Zero to many</td> - <td>A field of this struct.</td> -</tr> -</tbody> -</table> - - - -<h2 id="field">field</h2> -<p> -Contained in -<a href="#schema">schema</a>, -<a href="#document">document</a>, -or -<a href="#struct">struct</a>. -Defines a named value with a type and (optionally) how this field -should be stored, indexed, searched, presented, and how it should influence ranking. -</p> -<pre> -field [name] type [type-name] { - [body] -} -</pre> -Do not use names that are used for other purposes in the indexing language -or other places in the schema file. Reserved names are: -<ul> - <li>attribute</li> - <li>body</li> - <li>case</li> - <li>context</li> - <li>documentid</li> - <li>else</li> - <li>header</li> - <li>hit</li> - <li>host</li> - <li>if</li> - <li>index</li> - <li>position</li> - <li>reference</li> - <li>relevancy</li> - <li>sddocname</li> - <li>summary</li> - <li>switch</li> - <li>tokenize</li> -</ul> -<p> -Other names not to use include any words that start with a number or include special characters. -</p><p> -The <em>type</em> attribute is mandatory - supported types: -</p> -<table class="table"> - <thead> - <tr> - <th>Field type</th> - <th>Description</th> - </tr> - </thead> - <tbody> - - <tr><th>array<type></th> - <td> - <p id="array"> - For single-value (primitive) types, use array<type> to create an array field of the element type: - </p> - <table class="table"> - <thead></thead><tbody> - <tr> - <th style="width:100px">Index</th> - <td>Each element is indexed separately</td> - </tr><tr> - <th>Attribute</th> - <td>Added as an array attribute</td> - </tr><tr> - <th>Summary</th> - <td>Added as an array summary field</td> - </tr> - </tbody> - </table> - <p> - Also used to create an array field of the given <a href="#struct">struct type</a>. - The struct type must be defined separately. Example: - </p> -<pre> -struct person { - field first_name type string {} - field last_name type string {} -} - -field people type array<person> { - indexing: summary - summary: matched-elements-only - struct-field first_name { - indexing: attribute - attribute: fast-search - } -} -</pre> - <p> - The entire <em>people</em> field is part of the document summary. - The <a href="#struct-field">struct field</a> <em>first_name</em> is defined as an <em>attribute</em> for searching, - with <a href="../../content/attributes.html#fast-search">fast-search</a>. - A subset, or all, of the struct fields can be defined as attributes. - </p> - <p> - Use the <a href="../querying/yql.html#sameelement">sameElement</a> - operator to ensure matches in the same struct field instance. - </p> - <p> - Use <a href="#matched-elements-only">matched-elements-only</a> - to reduce the amount of data that is returned in the document summary. - </p> - {% include important.html content=" - <code>key</code> and <code>value</code> are reserved words in an array<struct>, - as these are used to implement <a href='#map'>map</a>. - Do not use these as struct-field names."%} - <p>Restrictions:</p> - <ul> - <li>Array of struct types does not support <a href="../../basics/ranking.html">ranking features</a> - and can only be used for matching and filtering.</li> - <li>All struct arrays can be fed, retrieved, and used in document summaries.</li> - <li>Some parts of struct arrays can be searched in <a href="../applications/services/content.html#document">indexed search mode</a>, - while all parts of struct arrays can be searched in <a href="../../performance/streaming-search.html">streaming search</a>. - See below for supported cases.</li> - </ul> - <table class="table"> - <thead></thead><tbody> - <tr> - <th style="width:100px">Index</th> - <td> - Only supported in <a href="../../performance/streaming-search.html#differences-in-streaming-search">streaming search</a>. - Set this on the top-level struct array field to make all parts searchable. - </td> - </tr><tr> - <th>Attribute</th> - <td> - Only supported for <a href="#struct-field">struct fields</a> - that have primitive types (bool, string, int, long, byte, float, double). - Any struct field must be defined as an attribute to be used for searching. - The struct type can still contain fields of non-primitive types, - as long as these are not defined as attributes. - </td> - </tr><tr> - <th>Summary</th> - <td>Added as an array summary field</td> - </tr> - </tbody> - </table> - </td></tr> - - <tr><th>bool</th> - <td><p id="bool">Use for boolean values.</p> -<pre> -field alive type bool { - indexing: summary | attribute -} -</pre> - <table class="table"> - <thead></thead><tbody> - <tr> - <th style="width:100px">Index</th> - <td>Not supported</td> - </tr><tr> - <th>Attribute</th> - <td>Added as a boolean</td> - </tr><tr> - <th>Summary</th> - <td>Added as a boolean value (<code>true</code> or <code>false</code>)</td> - </tr> - </tbody> - </table> - {% include important.html content=" - Defaults to <code>false</code> if not specified. - "%} - </td></tr> - - - <tr><th>byte</th> - <td><p id="byte">Use for single 8-bit numbers.</p> -<pre> -field smallnumber type byte { - indexing: summary | attribute -} -</pre> - <table class="table"> - <thead></thead><tbody> - <tr> - <th style="width:100px">Index</th> - <td>Not supported. An attribute will automatically be used instead</td> - </tr><tr> - <th>Attribute</th> - <td>Added as a byte which supports range searches</td> - </tr><tr> - <th>Summary</th> - <td>Added as a byte</td> - </tr> - </tbody> - </table> - </td></tr> - - <tr><th>double</th> - <td><p id="double">Use for high precision floating point numbers (64-bit IEEE 754 double).</p> -<pre> -field mydouble type double { - indexing: summary | attribute -} -</pre> - <table class="table"> - <thead></thead><tbody> - <tr> - <th style="width:100px">Index</th> - <td>Not supported. An attribute will automatically be used instead</td> - </tr><tr> - <th>Attribute</th> - <td>Added as a 64-bit IEEE 754 double which supports range searches</td> - </tr><tr> - <th>Summary</th> - <td>Added as a 64-bit IEEE 754 double</td> - </tr> - </tbody> - </table> - </td></tr> - - <tr><th>float</th> - <td><p id="float">Use for floating point numbers (32-bit IEEE 754 float).</p> -<pre> -field myfloat type float { - indexing: summary | attribute -} -</pre> - <table class="table"> - <thead></thead><tbody> - <tr> - <th style="width:100px">Index</th> - <td>Not supported. An attribute will automatically be used instead</td> - </tr><tr> - <th>Attribute</th> - <td>Added as a 32-bit IEEE 754 float which supports range searches</td> - </tr><tr> - <th>Summary</th> - <td>Added as a 32-bit IEEE 754 float</td> - </tr> - </tbody> - </table> - </td></tr> - - <tr><th>int</th> - <td><p id="int">Use for single 32-bit integers.</p> -<pre> -field release_year type int { - indexing: summary | attribute -} -</pre> - <table class="table"> - <thead></thead><tbody> - <tr> - <th style="width:100px">Index</th> - <td>Not supported. An attribute will automatically be used instead</td> - </tr><tr> - <th>Attribute</th> - <td>Becomes integer attributes, which supports range grouping and range searches</td> - </tr><tr> - <th>Summary</th> - <td>Added as a 32-bit integer</td> - </tr> - </tbody> - </table> - </td></tr> - - <tr><th>long</th> - <td><p id="long">Use for single 64-bit integers.</p> -<pre> -field bignumber type long { - indexing: summary | attribute -} -</pre> - <table class="table"> - <thead></thead><tbody> - <tr> - <th style="width:100px">Index</th> - <td>Not supported. An attribute will automatically be used instead</td> - </tr><tr> - <th>Attribute</th> - <td>Becomes a 64-bit integer attribute, which supports range grouping and range searches</td> - </tr><tr> - <th>Summary</th> - <td>Added as a 64-bit integer</td> - </tr> - </tbody> - </table> - </td></tr> - - <tr><th>map<key-type,value-type></th> - <td> - <p id="map"> - Use to create a map where each unique key is mapped to a single value. - Any primitive type can be used as <em>key-type</em> and any primitive type or Vespa struct type as <em>value-type</em>. - Example of a map of primitive types, - where the <em>key</em> and <em>value</em> fields are specified as <em>attributes</em>: - </p> -<pre> -field my_map type map<string, int> { - indexing: summary - struct-field key { indexing: attribute } - struct-field value { indexing: attribute } -} -</pre> - <p> - Note that a Vespa map entry is handled as a <em>struct</em> - with a <em>key</em> and <em>value</em> field - with <em>key-type</em> and <em>value-type</em> as types. - This explains the <em>struct-field</em> syntax above. - The full <em>my_map</em> field is configured into the document summary. - </p> - <p>A more complex example is a map of struct:</p> -<pre> -struct person { - field first_name type string {} - field last_name type string {} - field age type int {} -} - -field identities type map<string, person> { - indexing: summary - summary: matched-elements-only - struct-field key { - indexing: attribute - attribute: fast-search - } - struct-field value.first_name { - indexing: attribute - attribute: fast-search - } - struct-field value.last_name { - indexing: attribute - attribute: fast-search - } -} -</pre> - <p> - This example illustrates that the struct elements are configured individually - - there is no field configuration for <em>age</em> - - one can define a subset of the struct fields as attributes. - </p> - <p> - Here, <em>key</em>, <em>value.first_name</em> and <em>value.last_name</em> are defined as attributes. - This makes them available for searching and <a href="/en/querying/grouping.html#grouping-over-a-map-field">grouping</a>. - A common use case is requiring matches in the same map entry - (e.g., match both first and last name for the same person), - see the <a href="/en/reference/querying/yql.html#sameelement">sameElement</a> operator - for how to implement this. - Use <a href="#matched-elements-only">matched-elements-only</a> - to reduce the amount of data in the document summary. - </p> - <p> - <a href="/en/performance/feature-tuning.html#when-to-use-fast-search-for-attribute-fields">fast-search</a> - is used to make query access faster by creating an index structure for lookups. - </p> - <p> - As an alternative to a map, - an <a href="#array">array<struct></a> can contain the same element multiple times and maintains order. - </p> - <p>Restrictions:</p> - <ul> - <li>Map of struct or primitive types do not support <a href="../../basics/ranking.html">ranking features</a> - and can only be used for matching and filtering.</li> - <li>All map types can be fed, retrieved, and used in document summaries.</li> - <li>Some map types can be searched in <a href="../applications/services/content.html#document">indexed search mode</a>, - while all map types can be searched in <a href="../../performance/streaming-search.html">streaming search</a>. - See below for supported cases:</li> - </ul> - <table class="table"> - <thead></thead><tbody> - <tr> - <th style="width:100px">Index</th> - <td> - Only supported in <a href="../../performance/streaming-search.html#differences-in-streaming-search">streaming search</a>. - Set this on the top-level map field to make all struct fields in the map field searchable. - </td> - </tr><tr> - <th>Attribute</th> - <td> - Only supported for <a href="#struct-field">struct fields</a> where <em>value-type</em> is either a - primitive type (bool, string, int, long, byte, float, double) or a - <a href="#struct">struct type</a> with fields of primitive types. - Any struct field must be defined as an attribute to be used for searching. - The <em>value-type</em> struct can still contain fields of non-primitive types, - as long as these are not defined as attributes. - </td> - </tr><tr> - <th>Summary</th> - <td>Added as a map.</td> - </tr> - </tbody> - </table> - </td></tr> - - <tr><th>position</th> - <td> - <p id="position"> - Used to filter and/or rank documents by distance to a position in the query, - see <a href="../../querying/geo-search.html">Geo search</a>. - </p> -<pre> -field location type position { - indexing: attribute -} -</pre> - <table class="table"> - <thead></thead><tbody> - <tr> - <th style="width:100px">Index</th> - <td>Not supported</td> - </tr><tr> - <th>Attribute</th> - <td>Added as an interleaved 64-bit integer - (see <a href="https://en.wikipedia.org/wiki/Z-order_curve">Z-order curve</a>) - - queries are implemented by doing a set of range searches in the attribute. - This attribute has <a href="../../content/attributes.html#fast-search">fast-search</a> set implicitly</td> - </tr><tr> - <th>Summary</th> - <td>Refer to the <a href="document-json-format.html#position">reference</a></td> - </tr> - </tbody> - </table> - </td></tr> - - <tr><th>predicate</th> - <td> - <p id="predicate"> - Use to match queries to a set of boolean constraints. - See <a href="../../schemas/predicate-fields.html#queries">querying predicate fields.</a> - Predicate fields are not supported in - <a href="../../performance/streaming-search.html#differences-in-streaming-search">streaming search</a>. - </p> -<pre> -field predicate_field type predicate { - indexing: attribute - index { - arity: 2 # mandatory - lower-bound: 3 - upper-bound: 200 - dense-posting-list-threshold: 0.25 - } -} -</pre> - <table class="table"> - <thead></thead><tbody> - <tr> - <th style="width:100px">Index</th> - <td>Not supported</td> - </tr><tr> - <th>Attribute</th> - <td>Indexed in-memory in a variable-size binary format that is optimized for application during query evaluation</td> - </tr><tr> - <th>Summary</th> - <td>Added as-is</td> - </tr> - </tbody> - </table> - </td></tr> - - <tr><th>raw</th> - <td><p id="raw">Use for binary data</p> -<pre> -field rawfield type raw { - indexing: summary | attribute -} -</pre> - <table class="table"> - <thead></thead><tbody> - <tr> - <th style="width:100px">Index</th> - <td>Not supported</td> - </tr><tr> - <th>Attribute</th> - <td>Added as raw data. Not searchable.</td> - </tr><tr> - <th>Summary</th> - <td>Added as raw data. Outputted as a base64-encoded string. - See <a href="document-json-format.html#raw">JSON feed format</a> for details. - </td> - </tr> - </tbody> - </table> - </td></tr> - - <tr><th>reference<document-type></th> - <td> - <p id="reference"> - A <em>reference<document-type></em> field is a reference to an instance of a document-type - - i.e., a foreign key. - Reference fields are not supported in - <a href="../../performance/streaming-search.html#differences-in-streaming-search">streaming search</a>. - </p> -<pre> -field artist_ref type reference<artist> { - indexing: attribute -} -</pre> - The reference is the <a href="../../schemas/documents.html">document ID</a> of the document-type instance. - References are used to join documents in a <a href="../../schemas/parent-child.html">parent-child relationship</a>. - A reference can only be made to <a href="../applications/services/content.html#document">global</a> documents. - The following types of references are not supported: - <ul> - <li>Self-reference</li> - <li>Cyclic reference: If document type <em>foo</em> has a reference to <em>bar</em>, - then <em>bar</em> cannot have a reference to <em>foo</em></li> - </ul> - A reference attribute field can be searched using the document ID of the parent document-type instance as query term. - Note that this will be a linear scan as <a href="#attribute">fast-search</a> is not supported. - <table class="table"> - <thead></thead><tbody> - <tr> - <th style="width:100px">Index</th> - <td>Invalid - deployment will fail</td> - </tr><tr> - <th>Attribute</th> - <td> - As <a href="#string">string</a> - a reference must be an attribute. - Can be an empty string or point to a non-existent document. - Memory usage is about 33 bytes per parent document. - This is composed of 24 bytes used in a reference store, with a btree structure - on top of that which requires 5 bytes on average (depends on lid compaction). - In addition 4 bytes on average for a reference from child document to the parent document - (depends on lid compaction). In total about 33 bytes. - </td> - </tr><tr> - <th>Summary</th> - <td>As <a href="#string">string</a></td> - </tr> - </tbody> - </table> - </td></tr> - - <tr><th>string</th> - <td> - <p id="string"> - Use for a text field of any length. - String fields may only contain <em>text characters</em>, as defined by <code>isTextCharacter</code> in - <a href="https://github.com/vespa-engine/vespa/blob/master/vespajlib/src/main/java/com/yahoo/text/Text.java"> - com.yahoo.text.Text</a> - </p> -<pre> -field surname type string { - indexing: summary | index -} -</pre> - <table class="table"> - <thead></thead><tbody> - <tr> - <th style="width:100px">Index</th> - <td> - Refer to <a href="../../linguistics/linguistics.html">linguistics</a> - for details on normalization, tokenization and stemming. - </td> - </tr><tr> - <th>Attribute</th> - <td> - Added as-is. <a href="#match">match</a> exact or prefix is - supported types of searches in string attributes. Searches are however - case-insensitive. A query for <code>BritneY.spears</code> will match a - document containing <code>BrItNeY.SpEars</code> - </td> - </tr><tr> - <th>Summary</th> - <td>Added as-is</td> - </tr> - </tbody> - </table> - </td></tr> - - <tr><th>struct</th> - <td> - <p id="struct-name"> - Use to define a field with a struct datatype. - Create a <a href="#struct">struct type</a> inside the document definition and - declare the struct field in a document or struct using the struct type name as the field type: - </p> -<pre> -struct person { - field first_name type string {} - field last_name type string {} -} -field my_person type person { - indexing: summary -} -</pre> - Restrictions: - <ul> - <li>Struct fields can <strong>not</strong> be searched in indexed search mode - (but <a href="#array">array of struct</a> and - <a href="#map">map type</a> are searchable, with some restrictions).</li> - <li>Struct fields can be fed, retrieved, and used in document summaries.</li> - </ul> - <table class="table"> - <thead></thead><tbody> - <tr> - <th style="width:100px">Index</th> - <td> - Only supported in <a href="../../performance/streaming-search.html#differences-in-streaming-search">streaming search</a>. - Set this on the top-level field to make all parts searchable. - </td> - </tr><tr> - <th>Attribute</th> - <td>Not supported.</td> - </tr><tr> - <th>Summary</th> - <td>Added as a struct.</td> - </tr> - </tbody> - </table> - </td></tr> - - <tr><th>tensor(dimension-1,...,dimension-N)</th> - <td> - <p id="tensor"> - Use to create a tensor field with the given - <a href="../ranking/tensor.html#tensor-type-spec">tensor type spec</a> - that can be used for <a href="../../basics/ranking.html">ranking</a> and - <a href="../../querying/nearest-neighbor-search">nearest neighbor search</a>. - A tensor field is otherwise <span style="text-decoration: underline;">not</span> searchable. - </p><p> - See <a href="../ranking/tensor.html">tensor evaluation reference</a> for definition, - the <a href="../../ranking/tensor-user-guide.html">tensor user guide</a> and - the <a href="document-json-format.html#tensor">JSON feed format</a>. - </p> -<pre> -field tensorfield type tensor<float>(x{},y{}) { - indexing: attribute | summary -} - -field tensorfield type tensor<float>(x[2],y[2]) { - indexing: attribute | summary -} -</pre> - <table class="table"> - <thead></thead><tbody> - <tr> - <th style="width:100px">Index</th> - <td>Supported for tensor types with: - <ul> - <li>One indexed dimension - single vector per document</li> - <li>One or more mapped dimensions and one indexed dimension - multiple vectors per document</li> - </ul> - See <a href="../../querying/approximate-nn-hnsw">approximate nearest neighbor search</a>.</td> - </tr><tr> - <th>Attribute</th> - <td>Added as-is in an attribute to be used for ranking and nearest neighbor search.</td> - </tr><tr> - <th>Summary</th> - <td>Added as-is.</td> - </tr> - </tbody> - </table> - </td></tr> - - <tr><th>uri</th> - <td><p id="uri"> - {% include deprecated.html content="the uri type is deprecated, use string instead."%} - Use for URL type matching. - URI fields are not supported in - <a href="../../performance/streaming-search.html#differences-in-streaming-search">streaming search</a>. - </p> - <table class="table"> - <thead></thead><tbody> - <tr> - <th style="width:100px">Index</th> - <td> - <p> - The URL is split into its different components, which are indexed - separately. Note that only URLs can be indexed this way, not other URIs. - The different components are as defined by the HTTP standard: - Scheme, hostname, port, path, query, and fragment. Example: - <pre> -http://mysite.mydomain.com:8080/path/shop?d=hab&id=1804905709&cat=100#frag1 -</pre> - <table class="table"> - <thead></thead><tbody> - <tr> - <th>scheme</th><td>http</td> - </tr><tr> - <th>hostname</th><td>mysite.mydomain.com (indexed as "mysite", "mydomain" and "com")</td> - </tr><tr> - <th>port</th><td>8080 (note that port numbers 80 and 443 are not indexed, as they are the normal port numbers)</td> - </tr><tr> - <th>path</th><td>/path/shop (indexed as "path" and "shop")</td> - </tr><tr> - <th>query</th><td>d=hab&id=1804905709&cat=100 (indexed as "d", "hab", "id", "1804905709", "cat" and "100")</td> - </tr><tr> - <th>fragment</th><td>frag1</td> - </tr> - </tbody> - </table> - The syntax for searching these different components is: - <pre> -[field-name].[component-name]:term -</pre> - Example: In a URI field <code>sourceurl</code>, search for documents from slashdot: - <pre> -query=sourceurl.hostname:slashdot -</pre> - <p> - URL hostnames also support <em>anchored searching</em>, see - <a href="../querying/yql.html#uri">search in URL fields</a>. - </p><p> - It is not possible to index uri-typed fields into a common index, i.e., it has - to be indexed separately from other fields. If you need to combine URLs - with other fields, you could store it in a string-field instead, but then - you can not search in the different parts of the URL (scheme, hostname, - port, path, query, and fragment). - </p><p> - <strong>Aliasing</strong> also works differently for URL fields - you - are allowed to create aliases both to the index (as usual) and to the - components of it. Use -<pre> -alias [component]: [alias] -</pre> - to create an alias for a component. For example, given this field: - <pre> -field surl type uri { - indexing: summary | index - alias: url - alias hostname: site -} -</pre> - <p> - a search in "surl" and "url" will search in the entire url, - while "surl.hostname" or "site" will search the hostname. - </p> - </td> - </tr><tr> - <th>Attribute</th> - <td>Not allowed</td> - </tr><tr> - <th>Summary</th> - <td>Added as-is as a string</td> - </tr> - </tbody> - </table> - </td></tr> - - <tr><th>weightedset<element-type></th> - <td><p id="weightedset"> - Use to create a multivalue field of the element type, - where each element is assigned a signed 32-bit integer weight. - <pre> -field tag type weightedset<string> { - indexing: attribute | summary -} -</pre> - <p> - The element type can be one of the following primitive types: byte, int, long, and string. - </p> - <p> - To access a weighted set in ranking when - using <code>attribute</code>, see - <a href="../ranking/rank-features.html#attribute-match-features-not-normalized">attribute the match features</a>, - or convert the weighted set to a tensor using the tensorFromWeightedSet(field, dimensionName) feature. - </p> - <p> - To access a weighted set in ranking when - using <code>index</code>, see - <a href="../ranking/rank-features.html#features-for-indexed-multivalue-string-fields">ranking features - for indexed multivalued fields</a>. Note that when using <code>index</code> with weightedset, queries - are matching across elements in the set. - </p> - <p> - It is possible to specify that a new key should be created if it does not exist before the update, - and that it should be removed if the weight is set to zero - - see the <a href="#weightedset-properties">reference</a>. - </p> - <p> - The weightedset field does not support filtering on weight. - If you need that use the <a href="#map">map</a> type and - <a href="../querying/yql.html#sameelement">sameElement</a> query operator - - see <a href="../../querying/query-language.html#map">this example</a>. - </p> - <table class="table"> - <thead></thead><tbody> - <tr> - <th style="width:100px">Index</th> - <td> - Each token present in the field is indexed separately. - Information indexed includes element number, element weight, and a - list of token occurrence positions for each element in which the token is present - </td> - </tr><tr> - <th>Attribute</th> - <td>Added as a multivalue weighted attribute</td> - </tr><tr> - <th>Summary</th> - <td>Added as a multivalue summary field if this is an attribute</td> - </tr> - </tbody> - </table> - </td></tr> - </tbody> -</table> -<p> -The body of a field is optional for <a href="#schema">schema</a>, -<a href="#document">document</a> and -<a href="#struct">struct</a>. It may contain the following elements: -</p> -<table class="table"> - <thead> - <tr><th>Name</th><th>Occurrence</th><th>Description</th></tr> - </thead><tbody> -<tr><td>alias</td> - <td style="white-space: nowrap">Zero to many</td> - <td> - <p id="alias"> - Make an index or attribute available in queries under an additional name. - This has minimal performance impact and can safely be added to running applications. Example: - </p> -<pre> -field artist type string { - alias: artist_name -} -</pre> - <p> - <code>alias</code> works for primitive types, i.e, types you can set <code>indexing</code> on like <code>string</code>. - Composite types are not supported, the below will throw an error when deploying: - </p> -<pre> -document ppl { - struct person { - field first_name type string {} - field last_name type string {} - } - field p type array<person> { - struct-field first_name { - indexing: attribute - } - struct-field last_name { - indexing: attribute - } - alias: members - } -} -</pre> - </td> -</tr> - -<tr><td><a href="#attribute">attribute</a></td> - <td>Zero to many</td> - <td><p id="field-attribute">Specify an attribute setting.</p></td> -</tr> - -<tr><td><a href="#bolding">bolding</a></td> - <td>Zero to one</td> - <td><p id="field-bolding">Specifies whether the content of this field should be bolded. - Only supported for <a href="#indexing-index">index</a> fields of type string or array<string>.</p></td> -</tr> - -<tr><td><a href="#id">id</a></td> - <td>Zero to one</td> - <td><p id="field-id">Explicitly decide the numerical id of this field. Is normally not necessary, but can be used to save some disk space.</p></td> -</tr> - -<tr><td><a href="#index">index</a></td> - <td>Zero to many</td> - <td><p id="field-index">Specify a parameter of an index.</p></td> -</tr> - -<tr><td><a href="#indexing">indexing</a></td> - <td>Zero to one</td> - <td><p id="field-indexing">The indexing statements used to create index structure additions - from this field.</p></td> -</tr> - -<tr><td><a href="#match">match</a></td> - <td>Zero to one</td> - <td><p id="field-match">Set the matching type to use for this field.</p></td> -</tr> - -<tr><td><a href="#normalizing">normalizing</a></td> - <td>Zero or one</td> - <td><p id="field-normalizing">Specifies the kind of text normalizing to do on a string field.</p></td> -</tr> - -<tr><td style="white-space: nowrap"><a href="#query-command">query-command</a></td> - <td>Zero to many</td> - <td><p id="field-query-command">Specifies a command which can be received by a plugin searcher in the Search Container.</p></td> -</tr> - -<tr><td><a href="#rank">rank</a></td> - <td>Zero or one</td> - <td><p id="field-rank">Specify if the field is used for ranking.</p></td> -</tr> - -<tr><td><a href="#rank-type">rank-type</a></td> - <td>Zero to one</td> - <td><p id="field-rank-type">Selects the set of low-level rank settings to be used for this field - when using default <code>nativeRank</code>.</p></td> -</tr> - -<tr><td><a href="#sorting">sorting</a></td> - <td>Zero or one</td> - <td><p id="field-sorting">The sort specification for this field.</p></td> -</tr> - -<tr><td><a href="#stemming">stemming</a></td> - <td>Zero or one</td> - <td><p id="field-stemming">Specifies stemming options to use for this field.</p></td> -</tr> - -<tr><td><a href="#struct-field">struct-field</a></td> - <td>Zero to many</td> - <td><p id="field-struct-field">A subfield of a field of type struct. The struct must have been defined to - contain this subfield in the struct definition. If you want the subfield to - be handled differently from the rest of the struct, you may specify it within - the body of the struct-field.</p></td> -</tr> - -<tr><td><a href="#summary">summary</a></td> - <td>Zero to many</td> - <td><p id="field-summary">Sets a summary setting of this field, set to <code>dynamic</code> - to make a dynamic summary.</p></td> -</tr> - -<tr><td><a href="#summary-to">summary-to</a></td> - <td>Zero to one</td> - <td><p id="field-summary-to"> - {% include deprecated.html content="Use <a href='#document-summary'>document-summary</a> instead."%} - The list of document summary names this should be included in.</p></td> -</tr> - -<tr><td><a href="#weight">weight</a></td> - <td>Zero to one</td> - <td><p id="field-weight">The importance of a field when searching multiple fields and using <code>nativeRank</code>.</p></td> -</tr> - -<tr><td><a href="#weightedset-properties">weightedset</a></td> - <td>Zero to one</td> - <td><p id="field-weightedset-properties">Properties of a weightedset <a href="#weightedset">weightedset<element-type></a></p></td> -</tr> -</tbody> -</table> -<p> - Fields can not have default values. - See the <a href="../../schemas/documents.html#fields">document guide</a> for how to auto-set field values. -</p> -<p> - It is not possible to query for fields without value (i.e. query for NULL) - - see the <a href="../querying/yql.html">query language reference</a>. - Fields without value are not returned in <a href="../querying/default-result-format.html">query results</a>. -</p> -<p> -Fields can be declared outside the document block in the schema. -These fields are not part of the document type but behave like regular fields for queries. -Since they are not part of the document, they cannot be written directly, -but instead take their values from document fields, using the <code>input</code> expression: -<code>indexing: input my_document_field | embed | summary | index</code> -</p> -<p> -This is useful e.g., to index a field in multiple ways, -or to change the field value, something which is not allowed with document fields. -When the document field(s) used as input are updated, these fields are updated with them. -</p> - - - -<h2 id="struct-field">struct-field</h2> -<p> -Contained in <a href="#field">field</a> or -<a href="#struct-field">struct-field</a>. -Defines how this struct field (a subfield of a struct) should be stored, -indexed, searched, presented, and how it should influence ranking. -The field in which this struct field is contained must be of -type struct or a collection of type struct: -</p> -<pre> -struct-field [name] { - [body] -} -</pre> -<p>The body of a struct field is optional and may contain the following elements:</p> -<table class="table"> - <thead> - <tr><th>Name</th><th>Occurrence</th><th>Description</th></tr> - </thead> - <tbody> - <tr><td><a href="#indexing">indexing</a></td> - <td>Zero to one</td> - <td>The indexing statements used to create index structure additions from this field. - For indexed search only <code>attribute</code> is supported, which makes the struct - field a searchable in-memory attribute that can also be used for e.g. grouping and ranking. - For <a href="../../performance/streaming-search.html">streaming search</a> - <code>index</code> and <code>summary</code> are supported in addition. - </td> - </tr> - <tr><td><a href="#attribute">attribute</a></td> - <td style="white-space: nowrap">Zero to many</td> - <td>Specifies an attribute setting. For example <code>attribute:fast-search</code>.</td> - </tr> - <tr><td><a href="#rank">rank</a></td> - <td style="white-space: nowrap">Zero to one</td> - <td>Specifies <a href="#rank">rank</a> settings</td> - </tr> - <tr><td><a href="#match">match</a></td> - <td style="white-space: nowrap">Zero to one</td> - <td>Specifies <a href="#match">match</a> settings</td> - </tr> - </tbody> -</table> -<p> - If this struct field is of type struct (i.e., a nested struct), only <a href="#indexing">indexing:summary</a> - may be specified. See <a href="#array">array<type></a> for example use. -</p> - - - -<h2 id="fieldset">fieldset</h2> -<p> - Contained in <a href="#schema">schema</a>. -</p> - {% include note.html content="this is not related to the - <a href='../../schemas/documents.html#fieldsets'>Document fieldset</a>. - Also see the <a href='../../learn/faq#must-all-fields-in-a-fieldset-have-compatible-type-and-matching-settings'>FAQ</a> - for a discussion of what happens when using different types/match settings." %} -<p>A fieldset groups fields together for searching:</p> -<pre> -fieldset myfieldset { - fields: a,b,c -} -</pre> -<p> - Create a fieldset named <code>default</code> to be used as the default - (i.e., when not specified in the query): -</p> -<pre> -fieldset default { - fields: a,b,c -} -</pre> -<p>See <a href="../../querying/query-api.html#fieldset">example queries</a>.</p> -<p> - The fields in the fieldset should be as similar as possible in terms of indexing clause and <a href="#match">match mode</a>. - If they are not, test the application thoroughly. - Having different match modes for the fields in the fieldset generates a warning during application deployment. - If specific match settings for the fieldset are needed, such as <em>exact</em>, specify it using <em>match</em>: -</p> -<pre> -fieldset myfieldset { - fields: a,b,c - match { - exact - } -} -</pre> -<p> - Use <a href="#query-command">query-commands</a> in the field set to set search settings. Example: -</p> -<pre> -fieldset myfieldset { - fields: a,b,c - query-command:"exact @@" -} -</pre> -<p>Adding a fieldset will not create extra index structures in memory / on disk; it is just a mapping.</p> -<p> - Note that document frequency is aggregated across all member fields when matching a fieldset, - which affects <a href="../../ranking/bm25.html">BM25</a> and significance values, and can cause - <a href="../../ranking/wand.html">weakAnd</a> to prune matches for common terms like <em>"the"</em> - when they are frequent in one member field but rare in another. - See the <a href="../../learn/faq.html#why-can-common-words-like-the-hurt-recall-and-collapse-significance-across-a-fieldset">FAQ</a> - for details and the per-field workaround. -</p> - - -<h2 id="compression">compression</h2> - {% include deprecated.html content="see - <a href='../release-notes/vespa8.html#compression'>deprecations</a>."%} -<p> -Contained in <a href="#document">document</a>. -If a compression level is set within this element, -<strong>lz4</strong> compression is enabled for whole documents. -<pre> -compression { - [body] -} -</pre> -The body of a compression specification is optional and may contain: -<table class="table"> - <thead> - <tr><th>Name</th><th>Occurrence</th><th>Description</th></tr> - </thead><tbody> -<tr><td>type</td> - <td style="white-space: nowrap">Zero to one</td> - <td><p id="type"><strong>LZ4</strong> is the only valid compression method.</p></td> -</tr> -<tr><td>level</td> - <td>Zero to one</td> - <td><p id="level">Enable compression. LZ4 is linear and 9 means HC(high compression)</p></td> -</tr> -<tr><td>threshold</td> - <td>Zero to one</td> - <td> - <p id="threshold"> - A percentage (multiplied by 100) giving the maximum size that - compressed data can have to keep the compressed value. - If the resulting compressed data is higher than this, - the document will be stored uncompressed. The default value is 95. - </p> - </td> -</tr> -</tbody> -</table> - - - -<h2 id="rank-profile">rank-profile</h2> - -<p>Contained in <a href="#schema">schema</a> or equivalently in separate files in the -<a href="../applications/application-packages.html">application package</a>, named -<code>[profile-name].profile</code> in any directory below <code>schemas/[schema-name]/</code>. -A <a href="../../basics/ranking.html#rank-profiles">rank profile</a> is a named set of ranking expression functions and -settings which can be -<a href="../api/query.html#ranking.profile">selected in the query</a>).</p> - -<p>Whether defined inline in the schema or in a separate .profile file, the syntax of a rank profile is</p> - -<pre> -rank-profile [name] inherits [rank-profile1], [rank-profile2], ... { - [body] -} -</pre> - -<p>The <code>inherits</code> list is optional and may contain the name of other rank profiles -in this schema or one it inherits. -Elements not defined in this rank profile will then be inherited from those profiles. -Inheriting multiple profiles that define the same elements leads to an error at deployment.</p> - -<p>The body of a rank-profile may contain:</p> - -<table class="table"> -<thead> -<tr><th>Name</th><th>Occurrence</th><th>Description</th></tr> -</thead> -<tbody> -<tr><td><a href="#diversity">diversity</a></td> - <td>Zero or one</td> - <td>Specification of required diversity between the different phases.</td> -</tr> -<tr><td><a href="#strict">strict</a></td> - <td>Zero or one</td> - <td>true/false: Whether to use strict or loose type checking.</td> -</tr> -<tr><td><a href="#match-phase">match-phase</a></td> - <td>Zero or one</td> -<td>Ranking configuration to be used for hit limitation during matching.</td> -</tr> -<tr><td><a href="#firstphase-rank">first-phase</a></td> - <td>Zero or one</td> -<td>The ranking config to be used for first-phase ranking.</td> -</tr> -<tr><td><a href="#secondphase-rank">second-phase</a></td> - <td>Zero or one</td> -<td>The ranking config to be used for second-phase ranking.</td> -</tr> -<tr><td><a href="#globalphase-rank">global-phase</a></td> - <td>Zero or one</td> - <td>The ranking config to be used for global-phase ranking.</td> -</tr> -<tr><td><a href="#function-rank">function [name] </a></td> - <td>Zero or more</td> -<td>Defines a named function that can be referenced during ranking phase(s) and (if without arguments) -as part of match-and summary-features.</td> -</tr> -<tr><td><a href="#inputs">inputs</a></td> - <td style="white-space: nowrap">Zero or many</td> -<td>List of query features used in ranking expressions.</td> -</tr> -<tr><td><a href="#constants">constants</a></td> - <td>Zero or many</td> -<td>List of constant features available in ranking expressions.</td> -</tr> -<tr><td><a href="#mutate">mutate</a></td> - <td>Zero or many</td> -<td>Specification of mutations you can apply after different phases of a query.</td> -</tr> -<tr><td><a href="#onnx-model">onnx-model</a></td> - <td>Zero or many</td> - <td>An onnx model to make available in this profile.</td> -</tr> -<tr><td><a href="#significance">significance</a></td> - <td>Zero or one</td> - <td>To enable the use of significance models defined in the service.xml config.</td> -</tr> -<tr><td><a href="#rank-properties">rank-properties</a></td> - <td>Zero or one</td> -<td>List of any rank property key-values to be used by rank features.</td> -</tr> -<tr><td><a href="#match-features">match-features</a></td> - <td>Zero or more</td> -<td>The <a href="../ranking/rank-features.html">rank features</a> to be returned with each hit, computed in the <em>match</em> phase.</td> -</tr> -<tr><td><a href="#summary-features">summary-features</a></td> - <td>Zero or more</td> -<td>The <a href="../ranking/rank-features.html">rank features</a> to be returned with each hit, computed in the <em>fill</em> phase.</td> -</tr> -<tr><td><a href="#rank-features">rank-features</a></td> - <td>Zero or more</td> -<td>The <a href="../ranking/rank-features.html">rank features</a> to be dumped when using the query-argument - <a href="../api/query.html#ranking.listfeatures">rankfeatures</a>.</td> -</tr> -<tr><td>ignore-default-rank-features</td> - <td>Zero or one</td> - <td> - <p id="ignore-default-rank-features"> - Do not dump the default set of rank features, - only those explicitly specified with the <a href="#rank-features">rank-features</a> command. - </p> -</td> -</tr> -<tr><td>num-threads-per-search</td> - <td>Zero or one</td> -<td> - <p id="num-threads-per-search"> - Overrides the global - <a href="../applications/services/content.html#requestthreads-persearch">persearch</a> threads to a <strong>lower</strong> value. - </p> -</td> -</tr> -<tr><td>min-hits-per-thread</td> - <td>Zero or one</td> -<td> - <p id="min-hits-per-thread"> - After estimating the number of hits for a query prior to query evaluation, - this number is used to decide how many threads to use for the query. - </p> - <p> - <code>num_treads = min(<a href="#num-threads-per-search">num-threads-per-search</a>, - estimated_hits / min-hits-per-thread)</code> - </p> - <p> - The current default is 1. If you suspect the fixed cost per thread is too high, - increasing this number might be a good idea. - Especially if most of your queries are cheap, - but you have increased the <a href="#num-threads-per-search">num-threads-per-search</a> - in order to reduce latency for your costly queries covering a lot of documents. - The default might change, or the optimal value might be adaptive rendering overrides ignored or counterproductive. - </p> -</td> -</tr> -<tr><td>num-search-partitions</td> - <td>Zero or one</td> -<td> - <p id="num-search-partitions"> - The number of logical partitions in which the corpus is divided on a search node. - By default, this is the same as <a href="#num-threads-per-search">num-threads-per-search</a>. - A partition is the smallest unit a search thread will handle. - If you have a locality in time when searching and feeding documents, - you might want to split it into more, smaller partitions. - That way, you avoid that one costly partition leaves some threads idle while others are working hard. - </p> - <p> - If you have 8 threads per search, - you might have 10x as many partitions at 80 reducing max skew with a similar factor. - Note that a value of zero turns on adaptive partitioning which tries to solve this optimally. - {% include note.html content='If <code>num-search-partitions</code> is set to 0 (work sharing is enabled), - make sure <code>termwise-limit</code> is set to 1.0 (termwise evaluation is disabled). - This is to avoid redoing termwise evaluation when work is passed from one thread to another.' %} - </p> -</tr> -<tr><td>termwise-limit</td> - <td>Zero or one</td> -<td> - <p id="termwise-limit"> - If estimated number of hits > corpus * termwise-limit, - it will prune candidates with a CPU cache-friendly - <a href="../../performance/feature-tuning.html#hybrid-taat-daat">TAAT</a> - with the terms not needed for ranking, - prior to doing <a href="../../performance/feature-tuning.html#hybrid-taat-daat">DAAT</a>. - Current default is 1.0 which turns it off. - A value between 0.05 and 0.20 can be a good starting point. - This is particularly useful if you have many weak filters. - Note that this is a manual override. - The default might change, or the optimal value might be adaptive rendering overrides ignored or counterproductive. - </p> -</td> -</tr> -<tr><td>post-filter-threshold</td> - <td>Zero or one</td> -<td> - <p id="post-filter-threshold"> - Threshold value (in the range [0.0, 1.0]) deciding if a query with an approximate - <a href="../querying/yql.html#nearestneighbor">nearestNeighbor</a> - operator combined with filters is evaluated using post-filtering instead of the default filtering. - Post-filtering is chosen when the estimated filter hit ratio of the query is <em>larger</em> than this threshold. - The default value is 1.0, which disables post-filtering. - See - <a href="https://blog.vespa.ai/constrained-approximate-nearest-neighbor-search/#controlling-the-filtering-behavior-with-approximate-nearest-neighbor-search"> - Controlling the filtering behavior with approximate nearest neighbor search</a> for more details. - </p> - <p> - With post-filtering the <a href="../querying/yql.html#totaltargethits">totalTargetHits</a> value - used when searching the HNSW index is auto-adjusted in an effort to expose the node's share of <em>totalTargetHits</em> - hits to first-phase ranking after post-filtering has been applied. The following formula is used: - </p> -<pre> - adjustedTargetHits = min(targetHits / estimatedFilterHitRatio, targetHits * targetHitsMaxAdjustmentFactor). -</pre> - <p> - Use <a href="#target-hits-max-adjustment-factor">target-hits-max-adjustment-factor</a> to control - the upper bound of the adjusted <em>targetHits</em>. - </p> - <p> - This parameter has no effect in <a href="../../performance/streaming-search.html#differences-in-streaming-search">streaming search</a>. - </p> -</td> -</tr> -<tr><td>approximate-threshold</td> - <td>Zero or one</td> -<td> - <p id="approximate-threshold"> - Threshold value (in the range [0.0, 1.0]) deciding if a query with an approximate - <a href="../querying/yql.html#nearestneighbor">nearestNeighbor</a> - operator combined with filters is evaluated by searching the - <a href="schemas.html#index-hnsw">HNSW</a> - graph for approximate neighbors with filtering, - or performing an - <a href="../../querying/nearest-neighbor-search">exact nearest neighbor search</a> with pre-filtering. - The fallback to exact search is chosen when the estimated filter hit ratio of the query is <em>less</em> - than this threshold. - The default value is 0.02. - See - <a href="https://blog.vespa.ai/constrained-approximate-nearest-neighbor-search/#controlling-the-filtering-behavior-with-approximate-nearest-neighbor-search"> - Controlling the filtering behavior with approximate nearest neighbor search</a> for more details. - </p> - <p> - This parameter has no effect in <a href="../../performance/streaming-search.html#differences-in-streaming-search">streaming search</a>. - </p> -</td> -</tr> -<tr><td>filter-first-threshold</td> - <td>Zero or one</td> -<td> - <p id="filter-first-threshold"> - Threshold value (in the range [0.0, 1.0]) deciding if the filter is checked before computing a distance - (<em>filter-first heuristic</em>) while searching the - <a href="schemas.html#index-hnsw">HNSW</a> - graph for approximate neighbors with filtering. - This improves the response time at low hit ratios but causes a dip in recall. - The heuristic is used when the estimated filter hit ratio of the query is <em>less</em> - than this threshold. - The default value is 0.2. - </p> - <p> - This parameter has no effect in <a href="../../performance/streaming-search.html#differences-in-streaming-search">streaming search</a>. - </p> -</td> -</tr> -<tr><td>filter-first-exploration</td> - <td>Zero or one</td> -<td> - <p id="filter-first-exploration"> - Value (in the range [0.0, 1.0]) specifying how aggressively the - filter-first heuristic explores the graph when searching the - <a href="schemas.html#index-hnsw">HNSW</a> - graph for approximate neighbors with filtering. - A higher value means that the graph is explored more aggressively - and improves the recall at the cost of the response time. - The default value is 0.01. - </p> - <p> - This parameter has no effect in <a href="../../performance/streaming-search.html#differences-in-streaming-search">streaming search</a>. - </p> -</td> -</tr> -<tr><td>exploration-slack</td> - <td>Zero or one</td> -<td> - <p id="exploration-slack"> - Value (in the range [0.0, 1.0]) specifying slack to delay the termination - of the search of the - <a href="schemas.html#index-hnsw">HNSW</a> - graph for approximate neighbors with or without filtering. - A higher value means that more of the graph is explored - and improves the recall at the cost of the response time. - The default value is 0.0. - </p> - <p> - This parameter has no effect in <a href="../../performance/streaming-search.html#differences-in-streaming-search">streaming search</a>. - </p> -</td> -</tr> -<tr><td>target-hits-max-adjustment-factor</td> - <td>Zero or one</td> -<td> - <p id="target-hits-max-adjustment-factor"> - Value (in the range [1.0, inf]) used to control the auto-adjustment of - <a href="../querying/yql.html#totaltargethits">totalTargetHits</a> used when evaluating an approximate - <a href="../querying/yql.html#nearestneighbor">nearestNeighbor</a> - operator with post-filtering. - The default value is 20.0. - <p> - <p> - Setting this value to 1.0 disables auto-adjustment of <em>targetHits</em>. - See <a href="#post-filter-threshold">post-filter-threshold</a> for more details. - </p> - <p> - This parameter has no effect in <a href="../../performance/streaming-search.html#differences-in-streaming-search">streaming search</a>. - </p> -</td> -</tr> -<tr><td>filter-threshold</td> - <td>Zero or one</td> -<td> - <p id="filter-threshold"> - The threshold value (in the range [0.0, 1.0]) deciding when matching in <em>index</em> fields should be treated as filters. - This happens for query terms with <a href="../../learn/glossary.html#estimated-hit-ratio">estimated hit ratios</a> (in the - range [0.0, 1.0]) that are above the <em>filter-threshold</em>. - Use this to optimize query performance when searching large text <a href="../../basics/schemas.html#document-fields">index</a> fields, - by allowing a per query combination of <a href="#filter">rank: filter</a> and <a href="#normal">rank: normal</a> behavior. - This parameter can be overridden per <em>index</em> field, see <a href="#rank-filter-threshold">field-level filter-threshold</a> - for a more detailed description with tradeoffs. - </p> - <p> - In testing with various text datasets (e.g., Wikipedia), a <em>filter-threshold</em> setting of 0.05 has been shown to be a good starting point. - See <a href="../../performance/feature-tuning.html#tuning-query-performance-for-lexical-search">Tuning query performance for lexical search</a> - for more details. - </p> - <p> - This parameter has no effect in <a href="../../performance/streaming-search.html#differences-in-streaming-search">streaming search</a>. - </p> - <p> - Use the <a href="/en/reference/api/query.html#ranking.matching.filterThreshold">ranking.matching.filterThreshold</a> - query parameter to override this value. - </p> -</td> -</tr> -<tr><td><a href="#rank">rank</a></td> - <td>Zero or more</td> -<td>Specify rank settings of a field in this profile.</td> -</tr> -<tr><td><a href="#rank-type">rank-type</a></td> - <td>Zero or more</td> -<td>The rank-type of a field in this profile.</td> -</tr> -<tr><td><a href="#weakand">weakand</a></td> - <td>Zero or one</td> - <td> - <p> - Tunes the <a href="../../ranking/wand.html#weakand">weakAnd</a> algorithm to automatically - exclude terms and documents with expected low query significance based on - <a href="../../learn/glossary.html#document-frequency-normalized">document frequency</a> - statistics present in the document corpus. This makes matching faster at the cost of potentially reduced recall. - </p> - </td> -</tr> -<tr><td><a href="#rank-profile">rank-profile (inner)</a></td> - <td>Zero or more</td> - <td> - <p>An inner rank profile, useful for grouping related profiles, especially when defined in separate .profile files. - This behaves just like a top level rank profile, except that:</p> - <ul> - <li>The full name of the profile to use in queries will be <code>containing-profile-name.inner-profile-name</code>.</li> - <li>The profile must explicitly inherit the containing profile.</li> - </ul> - </td> -</tr> -</tbody> -</table> - - - -<h2 id="match-phase">match-phase</h2> - -<p>Contained in <a href="#rank-profile">rank-profile</a>. -The match-phase feature lets you increase performance by limiting hits -exposed to first-phase ranking to the highest (lowest) values of some attribute. -The performance gain may be substantial, especially with an expensive first-phase function. -The quality loss depends on how well the chosen attribute correlates with the -first-phase score.</p> - -<p>Documents that have no value of the chosen attribute will be taken as having the value 0.</p> - -<p> - See also <a href="../../performance/graceful-degradation.html#match-phase-degradation">graceful degradation</a> - and <a href="../../querying/result-diversity#match-phase-diversity">result diversity</a>. -</p> -<pre> -match-phase { - attribute: [numeric single value attribute] - order: [ascending | descending] - total-max-hits: [integer] -} -</pre> -<table class="table"> -<thead> - <tr><th>Name</th><th>Description</th></tr> -</thead><tbody> - <tr><td>attribute</td> - <td> - <p id="match-phase-attribute"> - The quality attribute that decides which documents are a match if the match phase - estimates that there will be more than the node's share if - <a href="#match-phase-total-max-hits">total-max-hits</a> hits. - The attribute must be single-value numeric with <a href="#attribute">fast-search</a> enabled. - It should correlate with the order which would be produced by a full query evaluation. - No default. - </p> - </td></tr> - <tr><td>order</td> - <td> - <p id="match-phase-order"> - Whether the attribute should be used in <code>descending</code> order (prefer documents with a high value) - or <code>ascending</code> order (prefer documents with a low value). - Usually, it is not necessary to specify this, - as the default value <code>descending</code> is by far the most common. - </p> - </td></tr> - <tr><td style="white-space: nowrap">total-max-hits</td> - <td> - <p id="match-phase-total-max-hits"> - The total max hits that should be produced in the match phase across all nodes - in the group evaluating the query. - This number should be large, and larger the worse the correlation between the - match-phase attribute and the first-phase function.</p> - </td></tr> - <tr><td style="white-space: nowrap">max-hits</td> - <td> - <p id="match-phase-max-hits"> - The max hits each content node should attempt to produce in the match phase. - Prefer using <a href="#match-phase-total-max-hits">total-max-hits</a> over this. - </td></tr> -</tbody> -</table> - - - - -<h2 id="strict">strict</h2> - -<p>Contained in <a href="#rank-profile">rank-profile</a>. -True or false. By default, Vespa uses loose type checking, where any query feature used but not defined in -a query profile type is assumed to be a float. -Set true to cause a deploy failure on missing query property type definitions instead.</p> - -<pre> -strict: true -</pre> - - - - -<h2 id="diversity">diversity</h2> -<p> -Contained in <a href="#rank-profile">rank-profile</a>. -Diversity is used to guarantee diversity in the different query phases. -If you have <a href="#match-phase">match-phase</a>, it will provide diverse results from match-phase to first-phase. -If you have <a href="#secondphase-rank">second-phase</a>, it will provide diverse results from first-phase to second-phase. -</p> -<p>Read more about this in <a href="../../querying/result-diversity">result diversity</a>.</p> -<p> -Specify the name of an attribute that will be used to provide diversity. -Result sets are guaranteed to get at least <a href="#diversity-min-groups">min-groups</a> -unique values from the <a href="#diversity-min-groups">diversity attribute</a> from this phase, -but no more than max-hits. -For <a href="#match-phase">match-phase</a> max-hits = the node's share of <a href="#match-phase-max-hits">match-phase total-max-hits</a>. -For <a href="#secondphase-rank">second-phase</a> max-hits = the node's share of <a href="#secondphase-total-rerank-count">total-rerank-count</a>. -A document is considered a candidate if: -<ul> - <li>The query has not yet reached the <em>max-hits</em> - number produced from this phase.</li> - <li>The query has not yet reached the max number of candidates in one group. - This is computed by the <em>max-hits</em> - of the phase divided by <a href="#diversity-min-groups">min-groups</a></li> -</ul> -<pre> -diversity { - attribute: [attribute name] - min-groups: [integer] -} -</pre> -<table class="table"> - <thead> - <tr><th>Name</th><th>Description</th></tr> - </thead><tbody> - <tr><td>attribute</td> - <td> - <p id="diversity-attribute"> - Which attribute to use when deciding diversity. - The attribute must be a single-valued numeric, string or <a href="#reference">reference</a> type. - </p> - </td></tr> - <tr><td style="white-space: nowrap">min-groups</td> - <td> - <p id="diversity-min-groups"> - Specifies the minimum number of groups returned from the phase. - Using this with <a href="#match-phase">match-phase</a> - often means one can reduce <a href="#match-phase-total-max-hits">total-max-hits</a>. - In <a href="#secondphase-rank">second-phase</a> - you might reduce <a href="#secondphase-total-rerank-count">total-rerank-count</a> and still get good and diverse results. - </p> - </td></tr> - </tbody> -</table> - - - -<h2 id="firstphase-rank">first-phase</h2> -<p> -Contained in <a href="#rank-profile">rank-profile</a>. -The config specifying the first phase of ranking. -See <a href="../../ranking/phased-ranking.html"> -phased ranking with Vespa</a>. - -This is the initial ranking performed on all matching documents; -you should therefore avoid doing computationally expensive relevancy calculations here. -By default, this will use the ranking feature <code>nativeRank</code>. -<pre> -first-phase { - [body] -} -</pre> -The body of a first-phase ranking statement consists of: -<table class="table"> -<thead> -<tr><th>Name</th><th>Description</th></tr> -</thead><tbody> -<tr><td><a href="#expression">expression</a></td> - <td> - <p>Specify the ranking expression to be used for the first phase of ranking - - see <a href="../ranking/ranking-expressions.html">ranking expressions</a>.</p> - </td> -</tr> -<tr><td>total-keep-rank-count</td> - <td> - <p id="total-keep-rank-count">How many documents to keep the first phase top rank values for in total - over the nodes evaluating the query. The default value is 10000 per node.</p> - </td> -</tr> -<tr><td>keep-rank-count</td> - <td> - <p id="keep-rank-count">How many documents to keep the first phase top rank values for per node. - Prefer <a href="#total-keep-rank-count">total-keep-rank-count</a> over this.</p> - </td> -</tr> -<tr><td style="white-space: nowrap">rank-score-drop-limit</td> - <td> - <p id="rank-score-drop-limit"> - Drop all hits with a first-phase rank score less than or equal to this floating-point number. - Use this to implement a rank cutoff. - Default is <code>-Double.MAX_VALUE</code> - </p> - </td> -</tr> -</tbody> -</table> - - - -<h2 id="expression">expression</h2> -<p> -Contained in <a href="#firstphase-rank">first-phase</a> or -<a href="#secondphase-rank">second-phase</a> or -<a href="#globalphase-rank">global-phase</a>. -Specify a <a href="../ranking/ranking-expressions.html">ranking expression</a>. -The expression can either be written directly or loaded from a file. -When writing it directly, the syntax is: -<pre> -expression: [ranking expression] -</pre> -or -<pre> -expression { - [ranking expression] - [ranking expression] - [ranking expression] -} -</pre> -<p> -The second format is primarily a convenience feature when using long expressions, enabling them -to be split over multiple lines. -</p><p> -Expressions can also be loaded from a separate file. This is useful when dealing with the long -expressions generated by e.g. MLR. The syntax is: -<pre> -expression: file:[path-to-expressionfile] -</pre> -<p> -The path is relative to the location of the schema definition file. -The file itself must end with <code>.expression</code>. This suffix is optional in the schema. -Therefore <code>expression: file:mlrranking.expression</code> and -<code>expression: file:mlrranking</code> are identical. -Both refer to a file called <code>mlrranking.expression</code> in the <em>schemas</em> directory. -</p> -{% include note.html content="Directories are not allowed in the path."%} - - - -<h2 id="rank-features">rank-features</h2> -<p> -Contained in <a href="#rank-profile">rank-profile</a>. -List of extra <a href="../ranking/rank-features.html">rank features</a> to be dumped -when using the query-argument <a href="../api/query.html#ranking.listfeatures">rankfeatures</a>. -<pre> -rank-features: [feature] [feature] -</pre> -or -<pre> -rank-features { - [feature] - [feature] -} -</pre> -<p> -Any number of ranking features can be listed on each line, separated by space. -</p> - - - -<h2 id="inputs">inputs</h2> - -<p>Contained in <a href="#rank-profile">rank-profile</a>. -List of inputs: Query features consumed by ranking expressions in this profile.</p> - -<p>Query features are set either as a <a href="../api/query.html#ranking.features">request property</a>, -or equivalently from a <a href="../../applications/searchers.html">Searcher</a>, by calling -<code>query.getRanking().getFeatures().put("query(myInput)", myValue)</code>.</p> - -<p>Query feature types can also be declared in -<a href="../../querying/query-profiles.html#query-profile-types">query profile types</a>, -but declaring inputs in the profile needing them is usually preferable.</p> - -<p>Inputs are inherited from inherited profiles.</p> - -<pre> -inputs { - name [type]? (: value)? -} -</pre> -<table class="table"> -<thead> -<tr> - <th>Name</th> - <th>Description</th> -</tr> -</thead> -<tbody> -<tr> - <td>name</td> - <td>The name of the inputs, written either the full feature name <code>query(myName)</code>, or just as <code>name</code>. -</tr> -<tr> - <td>type</td> - <td>The type of the constant, either <code>double</code> or a <a href="../ranking/tensor.html#tensor-type-spec">tensor type</a>. - If omitted, the type is double. -</tr> -<tr> - <td>value</td> - <td>An optional default module, used if this input is not set in the query. - A number, or a <a href="../ranking/tensor.html#tensor-literal-form">tensor on literal form</a>. - </td> -</tr> -</tbody> -</table> - -<p>Input examples:</p> - -<pre> -inputs { - myDouble: 0.5 - query(myOtherDouble) double - query(myArray) tensor(x[3]) - query(myMap) tensor(key{}):{key1: 1.0, key2: 2.0} -} -</pre> - - - -<h2 id="constants">constants</h2> - -<p>Contained in <a href="#rank-profile">rank-profile</a>. -List of constants available in ranking expressions, resolved and optimized at configuration time.</p> - -<p>Constants are inherited from inherited profiles, and from the schema itself.</p> - -<pre> -constants { - name [type]?: value|file:[path] -} -</pre> -<table class="table"> -<thead> -<tr> -<th>Name</th> -<th>Description</th> -</tr> -</thead> -<tbody> -<tr> -<td>name</td> -<td>The name of the constant, written either the full feature name <code>constant(myName)</code>, or just as <code>name</code>. -</tr> -<tr> -<td>type</td> -<td>The type of the constant, either <code>double</code> or a <a href="../ranking/tensor.html#tensor-type-spec">tensor type</a>. -If omitted, the type is double. -</tr> -<tr> -<td>value</td> -<td>A number, a <a href="../ranking/tensor.html#tensor-literal-form">tensor on literal form</a>, -or <code>file:</code> followed by a path from the application package root to a -file containing the constant. -The file must be stored in a valid <a href="../ranking/constant-tensor-json-format.html">tensor JSON Format</a> -and end with <code>.json</code>. The file may be lz4 compressed, in which case the ending must be -<code>.json.lz4</code>. -</td> -</tr> -</tbody> -</table> - -<p>Constant examples:</p> - -<pre> -constants { - myDouble: 0.5 - constant(myOtherDouble) double: 0.6 - constant(myArray) tensor(x[3]):[1, 2, 3] - constant(myMap) tensor(key{}]):{key1: 1.0, key2: 2.0} - constant(myLargeTensor) tensor(x[10000]): file:constants/myTensor.json.lz4 -} -</pre> - - - -<h2 id="rank-properties">rank-properties</h2> -<p> -Contained in <a href="#rank-profile">rank-profile</a>. -List of generic properties, in the form of key/value pairs to be used by ranking features. -<a href="../ranking/rank-feature-configuration.html">Examples</a>. -<pre> -rank-properties { - key: value -} -</pre> -<table class="table"> -<thead> -<tr><th>Name</th><th>Description</th></tr> -</thead><tbody> -<tr><td>key</td> -<td>Name of the property. -</tr> -<tr><td>value</td> -<td>A number or any string. Must be quoted if it contains spacing.</td> -</tr> -</tbody> -</table> - - - -<h2 id="function-rank">function (inline)? [name]</h2> -<p> -Contained in <a href="#rank-profile">rank-profile</a>. -Define a named function that can be referenced as a part of the ranking expression, or (if having no arguments) as a feature. -A function accepts any number of arguments. -<pre> -function [name]([arg1], [arg2], [arg3]) { - expression: … -} -</pre> -or -<pre> -function [name] ([arg1], [arg2], [arg3]) { - expression { - [ranking expression] - [ranking expression] - … -} -</pre> -Note that the parenthesis is required after the name. -A rank-profile example is shown below: -<pre> -rank-profile default inherits default { - function myfeature() { - expression: fieldMatch(title) + freshness(timestamp) - } - function otherfeature(foo) { - expression{ nativeRank(foo, body) } - } - - first-phase { - expression: myfeature * 10 - } - second-phase { - expression: otherfeature(title) * myfeature - } - summary-features: myfeature -} -</pre> -<p> -You can not include functions that accept arguments in summary features. -</p><p> -Adding the <code>inline</code> modifier will inline this function in the calling expression -if it also has no arguments. -This is faster for small and cheap functions (and more expensive for others). -</p> - - - -<h2 id="secondphase-rank">second-phase</h2> -<p> -Contained in <a href="#rank-profile">rank-profile</a>. -The config specifying the second phase of ranking. -See <a href="../../ranking/phased-ranking.html"> -phased ranking with Vespa</a>. - -This is the optional re-ranking phase performed on the top-ranking hits from the -<code>first-phase</code>, and where you should put any advanced relevancy calculations. -For example Machine Learned Ranking (MLR) models. - -By default, no second-phase ranking is performed. -<pre> -second-phase { - [body] -} -</pre> -The body of a secondphase-ranking statement consists of: -<table class="table"> -<thead> -<tr><th>Name</th><th>Description</th></tr> -</thead><tbody> -<tr><td><a href="#expression">expression</a></td> -<td><p>Specify the ranking expression to be used for the second phase of ranking. (for a description, - see the <a href="../ranking/ranking-expressions.html">ranking expression</a> documentation.</p> - <p id="secondphase-rescoring">Hits not reranked might be re-scored using a linear function to avoid a greater rank score than the worst reranked hit. This linear function will normally attempt to map the first phase rank score range of reranked hits to the reranked rank score range</p> -</td> -</tr> -<tr><td style="white-space: nowrap">rank-score-drop-limit</td> - <td> - <p id="secondphase-rank-score-drop-limit"> - When set, drop all hits with a second phase rank score (possibly a <a href="#secondphase-rescoring">re-scored</a> rank score) less than or equal to this floating point number. - Use this to implement a second-phase rank cutoff. - By default, this value is not set. This can also be <a href="../api/query.html#ranking.secondphase.rankscoredroplimit">set in the query</a>. - </p> - </td> -</tr> -<tr><td style="white-space: nowrap">total-rerank-count</td> - <td> - <p id="secondphase-total-rerank-count"> - Optional argument. Specifies the number of hits to be re-ranked in the second phase in total over the content - nodes that participate in evaluating a query (a <i>group</i>). - The default value is 100 per node. This can also be - <a href="../api/query.html#ranking.secondphase.totalrerankcount">set in the query</a>. - Hits not reranked might be <a href="#secondphase-rescoring">re-scored</a>. - </p> - </td> -</tr> -<tr><td style="white-space: nowrap">rerank-count</td> - <td> - <p id="secondphase-rerank-count"> - Optional argument. Specifies the number of hits to be re-ranked in the second phase on each content node. - This can also be <a href="../api/query.html#ranking.secondphase.rerankcount">set in the query</a>. - Prefer using <a href="#secondphase-total-rerank-count">total-rerank-count</a> over this. - </p> - </td> -</tr> -</tbody> -</table> - - - -<h2 id="globalphase-rank">global-phase</h2> -<p> -Contained in <a href="#rank-profile">rank-profile</a>. - -The config specifying the global phase of ranking. - -See <a href="../../ranking/phased-ranking.html"> phased ranking with Vespa</a>. - -This is an optional re-ranking phase performed on the top-ranking hits in -the stateless container after merging hits from all the content nodes. -The "top ranking" here means as scored by the first-phase ranking expression -or (if specified) second-phase ranking expression. - -Typically used for computing large ONNX models, which would be expensive to compute on all content nodes. - -By default, no global-phase ranking is performed. -<pre> -global-phase { - [body] -} -</pre> -The body of a global-phase ranking statement consists of: -<table class="table"> -<thead> -<tr><th>Name</th><th>Description</th></tr> -</thead><tbody> -<tr><td><a href="#expression">expression</a></td> -<td>Specify the ranking expression to be used for the global phase of ranking. (for a description, - see the <a href="../ranking/ranking-expressions.html">ranking expression</a> documentation.</td> -</tr> -<tr><td style="white-space: nowrap">rerank-count</td> -<td> - <p id="globalphase-rerank-count"> - Optional argument. Specifies the number of hits to be re-ranked in the global phase. - The default value is 100. - Note for complex setups: Applied to hits from one schema at a time, so if - a query searches in multiple schemas simultaneously, global-phase may run for 100 hits - per schema as default. - </p> -</td> -<tr><td style="white-space: nowrap">rank-score-drop-limit</td> - <td> - <p id="globalphase-rank-score-drop-limit"> - When set, drop all hits with a global phase rank score (possibly a <a href="#globalphase-rank">re-scored</a> rank score) less than or equal to this floating point number. - Use this to implement a global phase rank cutoff. - By default, this value is not set. This can also be <a href="../api/query.html#ranking.globalphase.rankscoredroplimit">set in the query</a>. - </p> - </td> -</tr> -</tbody> -</table> - - - -<h2 id="summary-features">summary-features</h2> -<p> - Contained in <a href="#rank-profile">rank-profile</a>. - List of <a href="../ranking/rank-features.html">rank features</a> to be included with each result hit, - in the <a href="../querying/default-result-format.html#summaryfeatures">summaryfeatures</a> field. - Also see <a href="../../ranking/ranking-expressions-features.html#accessing-feature-function-values-in-results"> - feature values in results</a>. -</p> -<p> - If not specified, the features are as specified in the parent profile (if any). - To inherit the features from the parent profile <em>and</em> specify additional features, - specify explicitly that the features should be inherited from the parent, as shown below. - Refer to <a href="/en/schemas/inheritance-in-schemas.html">schema inheritance</a> for examples. -</p> -<p> - The rank features specified here are computed in the - <a href="../../applications/searchers.html#multiphase-searching">fill phase</a> of multiphased queries. -</p> -{% include note.html content="Rank-features references in <em>summary-features</em> -are <strong>re-calculated</strong> during the <em>fill protocol phase</em> -for the hits which made it into the global top ranking hits (from all nodes). -See <a href='#match-features'>match-features</a> for an alternative." %} -<pre> -summary-features: [feature] [feature]… -</pre> -<p>or</p> -<pre> -summary-features [inherits parent-profile (, other-parent-profile)* ]? { - [feature] - [feature] -} -</pre> -<p> -Any number of rank features separated by space can be listed on each line. -{% include note.html content="Note that compound expressions must be wrapped in a <a href='https://docs.vespa.ai/en/ranking-expressions-features.html#accessing-feature-function-values-in-results'>function</a> to be accessible by match/summary-features. "%} -</p> - - -<h2 id="match-features">match-features</h2> -<p> - Contained in <a href="#rank-profile">rank-profile</a>. - List of <a href="../ranking/rank-features.html">rank features</a> to be included with each result hit, - in the <a href="../querying/default-result-format.html#matchfeatures">matchfeatures</a> field. - Also see <a href="../../ranking/ranking-expressions-features.html#accessing-feature-function-values-in-results"> - feature values in results</a>. -</p> -<p> - If not specified, the features are as specified in the parent profile (if any). - To inherit the features from the parent profile <em>and</em> specify additional features, - specify explicitly that the features should be inherited from the parent as shown below, - also see <a href="/en/schemas/inheritance-in-schemas.html">schema inheritance</a>. -</p> -<p>To disable match-features from parent rank profiles, use <code>match-features {}</code>.</p> -<p> - <em>match-features</em> is similar to <a href="#summary-features">summary-features</a>, - but the rank features specified here are computed in the <em>first protocol phase</em> of - <a href="../../applications/searchers.html#multiphase-searching">multiprotocol query execution</a>, - also called the <em>match</em> protocol phase. - - This gives a different performance trade-off, for details, see - <a href="../../ranking/ranking-expressions-features.html#accessing-feature-function-values-in-results"> - feature values in results</a>. -</p> -<pre> -match-features: [feature] [feature]… -</pre> -<p>or</p> -<pre> -match-features [inherits parent-profile (, other-parent-profile)* ]? { - [feature] - [feature] -} -</pre> -<p> -Any number of ranking features separated by space can be listed on each line. -{% include note.html content="Note that compound expressions must be wrapped in a <a href='https://docs.vespa.ai/en/ranking-expressions-features.html#accessing-feature-function-values-in-results'>function</a> to be accessible by match/summary-features. "%} -</p> - - - -<h2 id="mutate">mutate</h2> -<p> -Contained in <a href="#rank-profile">rank-profile</a>. -Specifies mutating operations you can do to each of the documents that make it through the 4 query phases, -<em>on-match</em>, <em>on-first-phase</em>, <em>on-second-phase</em> and <em>on-summary</em>. -</p> -<pre> -mutate { - [phase name] { [attribute name] [operation] [numeric_value] } -} -</pre> -The phases are: -<table class="table"> -<thead> -<tr><th>Name</th><th>Description</th></tr> -</thead><tbody> -<tr><td>on-match</td> - <td> - <p id="on-match">All documents that satisfy the query.</p> - </td> -</tr> -<tr><td>on-first-phase</td> - <td> - <p id="on-first-phase">All documents from <a href="#on-match">on-match</a>, - and is not dropped due the optional <a href="#rank-score-drop-limit">rank-score-drop-limit</a></p> - </td> -</tr> -<tr><td>on-second-phase</td> - <td> - <p id="on-second-phase">All documents from <a href="#on-first-phase">on-first-phase</a> - that makes it onto the <a href="#secondphase-rank">second-phase</a> heap.</p> - </td> -</tr> -<tr><td>on-summary</td> - <td> - <p id="on-summary">All documents where are a summary is requested.</p> - </td> -</tr> -</tbody> -</table> -<p> - The attribute must be a single value numeric attribute, enabled as <a href="#mutable">mutable</a>. - It must also be defined outside the <a href="#document">document</a> clause. -</p> -<table class="table"> -<thead> -<tr><th>Operation</th><th>Description</th></tr> -</thead><tbody> -<tr> - <td>=</td> - <td><p>Set the value of the attribute to the given value.</p></td> -</tr> -<tr> - <td>+=</td> - <td><p>Add the given value to the attribute</p></td> -</tr> -<tr> - <td>-=</td> - <td><p>Subtract the given value from the attribute</p></td> -</tr> -</tbody> -</table> -<p> - Find examples and use cases in <a href="../../ranking/phased-ranking.html#rank-phase-statistics">rank phase statistics</a>. -</p> - - -<h2 id="constant">constant</h2> - -<p><i>Prefer to define constants in the rank profiles that need them, -with rank profile inheritance to avoid repetition. -See <a href="#constants">constants</a>.</i> -</p> - -<p>Contained in <a href="#schema">schema</a>. -This defines a named constant tensor located in a file with a given type -that can be used in ranking expressions using the rank feature -<a href="../ranking/rank-features.html#constant(name)">constant(name)</a>: -<pre> -constant [name] { - [body] -} -</pre> -The body of a constant must contain: -<table class="table"> -<thead> -<tr><th>Name</th><th>Description</th><th>Occurrence</th></tr> -</thead><tbody> -<tr><td>file</td> -<td> - Path to the file containing this constant, relative to the application package root. - The file must be stored in a valid <a href="../ranking/constant-tensor-json-format.html">tensor JSON Format</a> - and end with <code>.json</code>. The file may be lz4 compressed, in which case the ending must be - <code>.json.lz4</code>. -</td> -<td>One</td> -</tr> -<tr><td>type</td> -<td>The type of the constant tensor, refer to - <a href="../ranking/tensor.html#tensor-type-spec">tensor-type-spec</a> for reference.</td> -<td>One</td> -</tr> -</tbody> -</table> -Constant tensor example: -<pre> -constant my_constant_tensor { - file: constants/my_constant_tensor_file.json - type: tensor<float>(x{},y{}) -} -</pre> -This example has a constant tensor with two mapped dimensions, <code>x</code> and <code>y</code>: -<pre> -{ - "cells": [ - { "address": { "x": "a", "y": "b"}, "value": 2.0 }, - { "address": { "x": "c", "y": "d"}, "value": 3.0 } - ] -} -</pre> -<p> -When an application with tensor constants is deployed, -the files are distributed to the content nodes -before the new configuration is used by the search nodes. -Incremental changes to constant tensors are not supported. -When changed, replace the old file with a new one and redeploy the application, -or create a new constant with a new name in a new file. -</p> - - -<h2 id="raw-as-base64-in-summary">raw-as-base64-in-summary</h2> -<p> -Contained in <a href="#schema">schema</a>. -Whether raw fields should be rendered as a base64 encoded string in summary, -the same way as in <a href="document-json-format.html#raw">json feed format</a>, -rather than an escaped string. This is by default true. -</p> - -<h2 id="onnx-model">onnx-model</h2> - -<p>Contained in <a href="#rank-profile">rank-profile</a> or <a href="#schema">schema</a>. -This defines a named ONNX model located in a file -that can be used in ranking expressions using the "onnx" rank feature.</p> - -<p>Prefer to define onnx models in the rank profiles using them. -Onnx models are inherited from parent profiles and from the schema.</p> - -<pre> -onnx-model [name] { - [body] -} -</pre> - -<p> -The body of an ONNX model must contain: -<table class="table"> - <thead> - <tr><th>Name</th><th>Occurrence</th><th>Description</th></tr> - </thead> - <tbody> - <tr> - <td>file</td> - <td>One</td> - <td> - Path to the location of the file containing the ONNX model. - The path is relative to the root of the application package containing this schema. - </td> - </tr> - <tr> - <td>input</td> - <td style="white-space: nowrap">Zero to many</td> - <td> - An input to the ONNX model. The ONNX name, as given in the model, as well as the source for the input, is specified. - </td> - </tr> - <tr> - <td>output</td> - <td>Zero to many</td> - <td> - An output of the ONNX model. The ONNX name, as given in the model, as well as the - name for use in Vespa, is specified. If no output is defined and is not - referred to from the rank feature, the first output defined in the model is used. - </td> - </tr> - <tr> - <td>gpu-device</td> - <td>Zero or one</td> - <td> - Set the GPU device number to use for computation, starting at 0, i.e. - if your GPU is <code>/dev/nvidia0</code> set this to 0. This must be an Nvidia CUDA-enabled GPU. Currently only - models used in <a href="#globalphase-rank">global-phase</a> can make use of GPU-acceleration. - </td> - </tr> - <tr> - <td>intraop-threads</td> - <td>Zero or one</td> - <td>The number of threads available for running operations with multithreaded implementations.</td> - </tr> - <tr> - <td>interop-threads</td> - <td>Zero or one</td> - <td> - The number of threads available for running multiple operations in parallel. - This is only applicable for <code>parallel</code> execution mode. - </td> - </tr> - <tr> - <td style="white-space: nowrap">execution-mode</td> - <td>Zero or one</td> - <td>Controls how the operators of a graph are executed, either <code>sequential</code> or <code>parallel</code>.</td> - </tr> - </tbody> -</table> -<p>For more details including examples, see <a href="../../ranking/onnx">ranking with ONNX models.</a></p> - -<h2 id="significance">significance</h2> - -<p> -Contained in <a href="#rank-profile">rank-profile</a>. -Configures a <a href="../../ranking/significance">significance model</a>. -</p> -<pre> -significance { - use-model: true -} -</pre> -<p>The body must contain:</p> -<table class="table"> - <thead> - <tr><th>name</th> - <th>occurrence</th> - <th>description</th></tr> - </thead> - <tbody> - <tr> - <td>use-model</td> - <td>One</td> - <td>Enable or disable the use of significance models specified in <a href="../applications/services/search.html#significance">service.xml</a>.</td> - </tr> - </tbody> -</table> -<p>For more details see <a href="../../ranking/significance">Significance Model.</a></p> - - - -<h2 id="document-summary">document-summary</h2> -<p> -Contained in <a href="#schema">schema</a>. -An explicitly defined document summary. By default, a document summary -named <code>default</code> is created. Using this element, other document -summaries containing a different set of fields can be created. -<pre> -document-summary [name] inherits [document-summary1], [document-summary2], ... { - [body] -} -</pre> -<p> - The <code>inherits</code> attribute is optional. - If defined, it contains the name of other document summaries in the same schema (or a parent) - which this summary should inherit the fields of. - Refer to <a href="../../schemas/inheritance-in-schemas.html">schema inheritance</a> for examples. -</p> -<p>The body of a document summary consists of:</p> -<table class="table"> - <thead> - <tr><th>Name</th><th>Occurrence</th><th>Description</th></tr> - </thead> - <tbody> - <tr> - <td>from-disk</td> - <td>Zero or one</td> - <td> - Mark this summary as accessing fields on disk. - This will silence the warnings that this summary reads from disk; - in the console for prod deployments, on the command line for manual deployments. - Read more in <a href="/en/querying/document-summaries.html#performance">Document Summaries</a> - on how to avoid disk access to speed up queries. - </td> - </tr> - <tr> - <td><a href="#summary">summary</a></td> - <td style="white-space: nowrap">Zero to many</td> - <td>A summary field in this document summary.</td> - </tr> - <tr> - <td>omit-summary-features</td> - <td>Zero or one</td> - <td> - Specifies that <a href="#summary-features">summary-features</a> should be omitted from this document summary. - Use this to reduce CPU cost in - <a href="../../applications/searchers.html#multiphase-searching">multiphase searching</a> - when using multiple document summaries to fill hits, - and only some of them need the summary features that are specified in the - <a href="#rank-profile">rank-profile</a>. - </td> - </tr> - </tbody> -</table> -<p> - Use the <a href="../api/query.html#presentation.summary">summary</a> - query parameter to choose a document summary in searches - or in <a href="/en/reference/querying/grouping-language.html#summary">grouping</a>. - See also <a href="../../querying/document-summaries.html">document summaries</a>. -</p> - - -<h2 id="documentid">documentid</h2> -<p>Available since {% include version.html version="8.691.19" %}.</p> -<p> -Contained in <a href="#schema">schema</a>. -Sets whether document IDs are stored on disk only or made an attribute -by also storing them in memory. -Changing this setting will only take effect on the next restart of the <code>searchnode</code> service. -Unlike other <a href="#changes-that-require-restart-but-not-re-feed">Changes that require restart but not re-feed</a>, -changing this setting is currently not being reported as needing a restart and the restart is currently not automated on Vespa Cloud. -<p> -Making the document IDs an attribute allows to return <a href="../../schemas/documents.html#docid-in-results">Document IDs in search results</a> -and to visit Document IDs without disk access, cf. -<a href="../../operations/data-management.html#export-documents">Export documents</a>. -</p> -<pre> -documentid: [setting] -</pre> -The settings are: -<table class="table"> -<thead> -<tr><th>Setting</th><th>Description</th></tr> -</thead><tbody> -<tr><td>from-disk</td><td>Store document IDs on disk only. This is the default setting.</td></tr> -<tr><td>attribute</td><td>Make the document IDs an attribute by also storing them in memory.</td></tr> -</tbody> -</table> - - -<h2 id="stemming">stemming</h2> -<p> -Contained in <a href="#field">field</a>, -<a href="#schema">schema</a> or -<a href="#index">index</a>. -Sets how to stem a field or an index, or how to stem by default. Typically used -with <a href="../../linguistics/linguistics-opennlp.html">OpenNLP linguistics</a>. -Read more on <a href="../../linguistics/linguistics-opennlp.html#stemming">stemming</a>. -<pre> -stemming: [stemming-type] -</pre> -The stemming types are: -<table class="table"> -<thead> -<tr><th>Type</th><th>Description</th></tr> -</thead><tbody> -<tr><td>none</td><td>No stemming: Keep words unchanged</td></tr> -<tr><td>best</td><td>Use the 'best' stem of each word according to some heuristic scoring. -This is the default setting</td></tr> -<tr><td>shortest</td><td>Use the shortest stem of each word</td></tr> -<tr><td>multiple</td><td>Use multiple stems. Retains all stems returned from the linguistics library</td></tr> -</tbody> -</table> -{% include note.html content="When combining multiple fields in a <a href='#fieldset'>fieldset</a>, -all fields should use the same stemming type."%} - - -<h2 id="normalizing">normalizing</h2> -<p>Contained in <a href="#field">field</a>. -Sets <a href="../../linguistics/linguistics-opennlp.html#normalization">normalizing</a> to be done on this field. -The default is to normalize. -<pre> -normalizing: [normalizing-type] -</pre> -<table class="table"> -<thead> -<tr><th>Type</th><th>Description</th></tr> -</thead><tbody> -<tr><td>none</td><td>No normalizing.</td></tr> -</tbody> -</table> - - - -<h2 id="dictionary">dictionary</h2> -<p> - Contained in <a href="#field">field</a>, - and specifies details of the dictionary used in the inverted index of the field. - Applies only to <a href="#attribute">attributes</a> annotated with <code>fast-search</code>. - You can specify either <code>btree</code> or <code>hash</code>, or both. - If both are specified, btree is used for range/prefix and hash for exact lookups. -</p> - -<h3>Dictionary Types</h3> -<p> - <strong>btree</strong> (Default): Provides good performance for exact, prefix, and range lookups. - Recommended for most use cases. Find more details in <a href="../../content/attributes.html#index-structures">attribute index structures</a>. -</p> - -<p> - <strong>hash</strong>: Optimized for fields with high cardinality (many unique values), - such as unique ID fields where each posting list contains only one item. -</p> - -<p> - {% include note.html content=' - When using <code>hash</code>, prefix searches for strings and range searches for numeric fields - will fall back to a full scan. This is primarily beneficial when you have many unique terms with - few occurrences each, where dictionary lookup costs would otherwise be significant. '%} -</p> - -<h3>Case Handling for String Fields</h3> -<p> - For string attributes, you can specify how character case is handled: -</p> -<ul> - <li><strong>uncased</strong> (Default): Case-insensitive - 'bear', 'Bear', and 'BEAR' are treated as identical</li> - <li><strong>cased</strong>: Case-sensitive - 'bear', 'Bear', and 'BEAR' are treated as different terms</li> -</ul> -<p> - This setting is automatically checked against the field's <a href="#match">match:casing</a> setting. -</p> - -<h3>Important Rules for String Fields with Dictionaries</h3> -<p> - <strong>For <code>btree</code> dictionaries</strong>: Both <code>cased</code> and <code>uncased</code> options are supported. -</p> -<p> - <strong>For <code>hash</code> dictionaries</strong>: Only <code>cased</code> is supported for string fields. When using <code>hash</code> dictionaries: -</p> -<ul> - <li>You <strong>must</strong> set <code>match: cased</code> on the field</li> - <li>You <strong>must</strong> include <code>cased</code> in the dictionary block</li> -</ul> - -<pre> -dictionary { - hash <!-- Specifies hash dictionary type --> - cased <!-- Required with hash for string fields --> -} -</pre> - -<h3>Example: Case-Sensitive Hash Dictionary</h3> -<pre> -field id_str type string { - indexing: summary | attribute - attribute: fast-search <!-- Enables dictionary functionality --> - match: cased <!-- Required for hash dictionaries with strings --> - rank: filter - dictionary { - hash <!-- Use hash dictionary type --> - cased <!-- Required for hash dictionaries with strings --> - } -} -</pre> - - - -<h2 id="attribute">attribute</h2> -<p>Contained in <a href="#field">field</a> or -<a href="#struct-field">struct-field</a>. -Specifies a property of an index structure attribute: -<pre> -attribute [attribute-name]: [property] -</pre> -or -<pre> -attribute [attribute-name] { - [property] - [property] - … -} -</pre> -Read the <a href="../../content/attributes.html">introduction to attributes</a>. -If the attribute name is specified, it will be used instead of the field name as the name of the attribute. -{% include deprecated.html content="Deprecated, use a field with the wanted name outside the document instead."%} -Actions required when <a href="#modifying-schemas">adding or modifying attributes</a>. Properties: -<table class="table"> -<thead> -<tr><th style="width:150px">Property</th><th>Description</th></tr> -</thead><tbody> -<tr><td>fast-search</td><td>Create a dictionary/index structure to speed up search in the attribute. - <a href="../../content/attributes.html#index-structures">Read more</a>.</td></tr> -<tr><td>fast-access</td><td> - If <a href="../applications/services/content.html#searchable-copies">searchable-copies</a> < - <a href="../applications/services/content.html#redundancy">redundancy</a>, - use <em>fast-access</em> to load the attribute in memory on all nodes with a document replica. - Use this for fast access when doing - <a href="../../writing/reads-and-writes.html">partial updates</a> and when used in a - <a href="../applications/services/content.html#documents">selection expression</a> for garbage collection. - If <a href="../applications/services/content.html#searchable-copies">searchable-copies</a> == - <a href="../applications/services/content.html#redundancy">redundancy</a> (default), this property is a no-op. - <a href="../../performance/sizing-feeding.html#redundancy-settings">Read more</a>. -</td></tr> -<tr><td>fast-rank</td><td> - Only supported for <a href="../../ranking/tensor-user-guide.html">tensor</a> field types with at least one mapped dimension. - Ensures that the per-document tensors are stored in-memory using a format that is more optimal for - <a href="../ranking/ranking-expressions.html">ranking expression</a> evaluation. This comes at the cost of using more memory. - Without this setting, these tensors are serialized in-memory, - which requires deserialization as part of ranking expression evaluation. - See <a href="../../performance/feature-tuning.html#tensor-ranking">tensor performance</a>. -</td></tr> -<tr><td>paged</td><td> - This can reduce the memory footprint by allowing paging the attribute data out of memory to disk. - Not supported for <a href="#tensor">tensor</a> with fast-rank and - <a href="#predicate">predicate</a> types. - See <a href="../../content/attributes.html#paged-attributes">paged attributes</a> for details. - Do not enable <em>paged</em> before fully understanding the consequences. -</td></tr> -<tr><td><a href="#sorting">sorting</a></td><td>The sort specification for this attribute.</td></tr> -<tr><td><a href="#distance-metric">distance-metric</a></td> -<td> - Specifies the distance metric to use with the <a href="../querying/yql.html#nearestneighbor">nearestNeighbor</a> - query operator. Only relevant for tensor attribute fields. -</td></tr> -<tr><td>mutable</td><td><p id="mutable">Marks the attribute as a special mutable attribute that can be updated by a - <a href="#mutate">mutate</a> operation during query evaluation.</p></td> -</tbody> -</table> -<p> -An attribute is <a href="../../querying/searching-multivalue-fields">multivalued</a> -if assigning it multiple values during indexing, -by using a multivalued field type like array or map, -or by using e.g. <a href="../writing/indexing-language.html#split">split</a> / -<a href="../writing/indexing-language.html#for_each">for_each</a> -or by letting multiple fields write their value to the attribute field. -</p><p> -Note that <a href="#normalizing">normalizing</a> and -<a href="../../linguistics/linguistics-opennlp.html#tokenization">tokenization</a> -is not supported for attribute fields. -</p><p> -Queries in attribute fields are not normalized, nor stemmed. -Use <a href="#index">index</a> on fields to enable. -Both <em>index</em> and <em>attribute</em> can be set on a field. -</p> - - - -<h2 id="sorting">sorting</h2> -<p> -Contained in <a href="#attribute">attribute</a> or -<a href="#field">field</a>. -Specifies how sorting should be done. -<pre> -sorting : [property] -</pre> -or -<pre> -sorting { - [property] - … -} -</pre> -<table class="table"> - <thead> - <tr><th>Property</th><th>Description</th></tr> - </thead><tbody> - <tr> - <td>order</td> - <td> - <code>ascending</code> (default) or <code>descending</code>. - Used unless overridden using <a href="../querying/yql.html#function">order by</a> in query. - </td> - </tr> - <tr> - <td>function</td> - <td> - <a href="../querying/yql.html#function">Sort function</a>: - <code>uca</code> (default), <code>lowercase</code> or <code>raw</code>. - Note that if no language or locale is specified in the query, the field, - or generally for the query, <code>lowercase</code> will be used instead of <code>uca</code>. - See <a href="../querying/yql.html#order-by">order by</a> for details. - </td> - </tr> - <tr> - <td>strength</td> - <td> - <a href="../querying/yql.html#strength">UCA sort strength</a>, default <code>primary</code> - - see <a href="../querying/yql.html#strength">strength</a> for values. - Values set in the query override the schema definition. - </td> - </tr> - <tr> - <td>locale</td> - <td> - <a href="../querying/yql.html#locale">UCA locale</a>, default none, - indicating that it is inferred from the query. - It should only be set here if the attribute is filled with data in one language only. - See <a href="../querying/yql.html#locale">locale</a> for details. - Values set in the query override the schema definition. - </td> - </tr> - </tbody> -</table> - - - -<h2 id="distance-metric">distance-metric</h2> -<p> - Specifies the distance metric to use with the <a href="../querying/yql.html#nearestneighbor">nearestNeighbor</a> - query operator to calculate the distance between document positions and the query position. - Only relevant for tensor attribute fields, where each tensor holds one or multiple vectors. -</p> -<p> - Which distance metric to use depends on the model used to produce the vectors; - it must match the distance metric used during representation learning (model learning). - If you are using an "off-the-shelf" model to vectorize your data, please ensure - that the distance metric matches the distance metric suggested for use with the model. - Different types of vectorization models use different types of distance metrics. -</p> -{% include important.html content="When changing the <code>distance-metric</code> or <code>max-links-per-node</code>, -the content nodes must be restarted to rebuild the HNSW index - see -<a href='#changes-that-require-restart-but-not-re-feed'>changes that require restart but not re-feed</a>"%} -<p> - The calculated distance will be used to - select the closest hits for <em>nearestNeighbor</em> query operator, but also to - build the <a href="../../querying/approximate-nn-hnsw">HNSW</a> index (if specified) and - to produce the <a href="../ranking/rank-features.html#distance(dimension,name)">distance</a> and - <a href="../ranking/rank-features.html#closeness(dimension,name)">closeness</a> ranking features. -</p> -<p> -<pre> -distance-metric: [metric] -</pre> -These are the available metrics; the expressions given for <em>distance</em> and <em>closeness</em> -assume a query vector <em>qv = [x0, x1, ...]</em> and an attribute vector <em>av = [y0, y1, ...]</em> -with same dimension of size <em>n</em> for all vectors. -<p> -<table class="table"> -<thead> - <tr><th>Metric</th><th>Description</th><th>distance</th><th>closeness</tr> -</thead> -<tbody> - <tr> - <td>euclidean</td> - <td>The normal <a href="#euclidean">euclidean</a> (aka L2) distance.</td> - <td> - <math xmlns="http://www.w3.org/1998/Math/MathML" display="inline"> - <mrow mathsize="120%"> - <mi>d</mi><mo>=</mo><msqrt> - <mrow> - <munder><mo>∑</mo><mi>n</mi></munder> - <msup> - <mrow> - <mo>(</mo> - <msub><mi>x</mi><mi>i</mi></msub> - <mo>-</mo> - <msub><mi>y</mi><mi>i</mi></msub> - <mo>)</mo> - </mrow> - <mn>2</mn> - </msup> - </mrow> - </msqrt> - </mrow> - </math> - with range: <code>[0,inf)</code></td> - <td> - <math xmlns="http://www.w3.org/1998/Math/MathML" display="inline"> - <mfrac><mn>1.0</mn><mrow><mn>1.0</mn><mo>+</mo><mi>d</mi></mrow></mfrac> - </math> - </td> - </tr> - <tr> - <td>angular</td> - <td>The <a href="#angular">angle</a> between <em>qv</em> and <em>av</em> vectors.</td> - <td> - <math xmlns="http://www.w3.org/1998/Math/MathML" display="inline"> - <mrow mathsize="120%"> - <mi>d</mi><mo>=</mo> - <msup><mo>cos</mo><mn>-1</mn></msup><mo>(</mo> - <mfrac> - <mrow> - <mover><mi>q</mi><mo>→</mo></mover> - <mo>⋅</mo> - <mover><mi>a</mi><mo>→</mo></mover> - </mrow> - <mrow> - <mo>|</mo> <mover><mi>q</mi><mo>→</mo></mover> <mo>|</mo> - <mo>⋅</mo> - <mo>|</mo> <mover><mi>a</mi><mo>→</mo></mover> <mo>|</mo> - </mrow> - </mfrac> - <mo>)</mo> - </mrow> - </math> - with range: <code>[0,pi]</code></td> - <td> - <math xmlns="http://www.w3.org/1998/Math/MathML" display="inline"> - <mfrac><mn>1.0</mn><mrow><mn>1.0</mn><mo>+</mo><mi>d</mi></mrow></mfrac> - </math> - </td> - </tr> - <tr> - <td>dotproduct</td> - <td>Used for <a href="#dotproduct">maximal inner product search</a>.</td> - <td> - <math xmlns="http://www.w3.org/1998/Math/MathML" display="inline"> - <mrow mathsize="120%"> - <mi>d</mi><mo>=</mo> - <mo>-</mo><mo>(</mo> - <mrow> - <mover><mi>q</mi><mo>→</mo></mover> - <mo>⋅</mo> - <mover><mi>a</mi><mo>→</mo></mover> - </mrow> - <mo>)</mo> - </mrow> - </math> - with range: <code>[-inf,+inf]</code></td> - <td> - <math xmlns="http://www.w3.org/1998/Math/MathML" display="inline"> - <mrow> - <mo>-</mo><mi>d</mi><mo>=</mo> - <mover><mi>q</mi><mo>→</mo></mover> - <mo>⋅</mo> - <mover><mi>a</mi><mo>→</mo></mover> - </mrow> - </math> - </td> - </tr> - <tr> - <td>prenormalized-angular</td> - <td>Assumes normalized vectors, see <a href="#prenormalized-angular">note</a> below.</td> - <td> - <math xmlns="http://www.w3.org/1998/Math/MathML" display="inline"> - <mrow mathsize="120%"> - <mi>d</mi><mo>=</mo> - <mn>1.0</mn><mo>-</mo><mo>(</mo> - <mfrac> - <mrow> - <mover><mi>q</mi><mo>→</mo></mover> - <mo>⋅</mo> - <mover><mi>a</mi><mo>→</mo></mover> - </mrow> - <msup> - <mrow> - <mo>|</mo> <mover><mi>q</mi><mo>→</mo></mover> <mo>|</mo> - </mrow> - <mn>2</mn> - </msup> - </mfrac> - <mo>)</mo> - </mrow> - </math> - with range: <code>[0,2]</code></td> - <td> - <math xmlns="http://www.w3.org/1998/Math/MathML" display="inline"> - <mfrac><mn>1.0</mn><mrow><mn>1.0</mn><mo>+</mo><mi>d</mi></mrow></mfrac> - </math> - </td> - </tr> - <tr> - <td>geodegrees</td> - <td>Assumes geographical coordinates, see <a href="#geodegrees">note</a> below.</td> - <td> - <math xmlns="http://www.w3.org/1998/Math/MathML" display="inline"> - <mrow mathsize="120%"> - <mi>d</mi><mo>=</mo> - </mrow> - </math> great-circle in km; range: <code>[0,20015]</code></td> - <td> - <math xmlns="http://www.w3.org/1998/Math/MathML" display="inline"> - <mfrac><mn>1.0</mn><mrow><mn>1.0</mn><mo>+</mo><mi>d</mi></mrow></mfrac> - </math> - </td> - </tr> - <tr> - <td>hamming</td> - <td>Only useful for binary tensors using <int8> precision, see <a href="#hamming">note</a> below.</td> - <td> - <math xmlns="http://www.w3.org/1998/Math/MathML" display="inline"> - <mrow mathsize="120%"> - <mi>d</mi><mo>=</mo> - <munder><mo>∑</mo><mi>n</mi></munder> - <mi>popcount</mi> - <mo>(</mo> - <msub><mi>x</mi><mi>i</mi></msub> - <mo>XOR</mo> - <msub><mi>y</mi><mi>i</mi></msub> - <mo>)</mo> - </mrow> - </math> - ; range: <code>[0,8*n]</code></td> - <td> - <math xmlns="http://www.w3.org/1998/Math/MathML" display="inline"> - <mfrac><mn>1.0</mn><mrow><mn>1.0</mn><mo>+</mo><mi>d</mi></mrow></mfrac> - </math> - </td> - </tr> -</tbody> -</table> - -<h3 id="euclidean">euclidean</h3> -<p> - The default metric is <a href="https://en.wikipedia.org/wiki/Euclidean_distance">Euclidean distance</a> - which is just the length of a line segment between the two points. - To compute the Euclidean distance directly in a ranking expression - instead of fetching one already computed in a nearestNeighbor query - operator, use the - <a href="../ranking/ranking-expressions.html#euclidean-distance-t"> - Euclidean_distance helper function</a>: -</p> -<pre> - function mydistance() { - expression: euclidean_distance(attribute(myembedding), query(myqueryvector), mydim)) - } -</pre> - - -<h3 id="angular">angular</h3> -<p> - The <em>angular</em> distance metric computes the <em>angle</em> between the vectors. - Its range is <code>[0,pi]</code>, which is the angular distance. - This is also known as ordering by - <a href="https://en.wikipedia.org/wiki/Cosine_similarity">cosine similarity</a> - where the score function is just the cosine of the angle. - To compute the angular distance directly in a ranking expression, use the - <a href="../ranking/ranking-expressions.html#cosine-similarity-t"> - cosine_similarity helper function</a>: -<pre> - function angle() { - expression: acos(cosine_similarity(attribute(myembedding), query(myqueryvector), mydim)) - } -</pre> - Conversely, the cosine similarity can be recovered from the - <a href="../ranking/rank-features.html#distance(dimension,name)">distance rank-feature</a> - when using a nearestNeighbor query operator: -<pre> - rank-profile cosine { - first-phase { - expression: cos(distance(field, myembedding)) - } - } -</pre> - If possible, it's slightly better for performance to normalize both query and document vectors to the same - L2 norm and use the <code>prenormalized-angular</code> metric instead; but note that returned - distance and closeness will be different. - -<h3 id="dotproduct">dotproduct</h3> -<p> - The <em>dotproduct</em> distance metric is used to <em>mathematically - transform</em> a "maximum inner product" search into a form where - it can be solved by nearest neighbor search, where the dotproduct - is used as a score directly (large positive dotproducts are - considered "nearby"). - Internally, an extra dimension is added (ensuring that all vectors - are normalized to the same length) and a distance similar to - <em>prenormalized-angular</em> is used to build the HNSW index. - For details, see - <a href="https://towardsdatascience.com/maximum-inner-product-search-using-nearest-neighbor-search-algorithms-c125d24777ef"> - this high level guide</a> based on - <a href="https://www.microsoft.com/en-us/research/wp-content/uploads/2016/02/XboxInnerProduct.pdf"> - section 3.1 Order Preserving Transformations in this paper</a>. -</p> -<p> - Note that the <em>distance</em> and <em>closeness</em> rank-features will not have the - usual semantic meanings when using the <em>dotproduct</em> distance metric. - In particular, <em>closeness</em> will just return the dot product - <math xmlns="http://www.w3.org/1998/Math/MathML" display="inline"> - <mrow> - <munder><mo>∑</mo><mi>n</mi></munder> - <mrow> - <mo>(</mo> - <msub><mi>x</mi><mi>i</mi></msub> - <mo>*</mo> - <msub><mi>y</mi><mi>i</mi></msub> - <mo>)</mo> - </mrow> - </mrow> - </math> - which may have any negative - or positive value, and <em>distance</em> is just the negative dot product. - If a normalized closeness in range <code>[0,1]</code> is needed, an appropriate - <a href="https://en.wikipedia.org/wiki/Sigmoid_function">sigmoid function</a> must be applied. - For example, if your attribute is named "foobar", and the maximum dotproduct seen is around 4000, the expression - <code>sigmoid(0.001*closeness(field,foobar))</code> - could be a possible choice. -</p> -<p> - The <em>dotproduct</em> distance metric is useful for some vectorization - models, including matrix factorization, that use "maximum inner - product" (MIP), with vectors that aren't normalized. - These models use both direction and magnitude. -</p> - -<h3 id="prenormalized-angular">prenormalized-angular</h3> -<p> - The <em>prenormalized-angular</em> distance metric - <strong>must only be used</strong> when <strong>both</strong> query and document vectors are normalized. - This metric was previously named "innerproduct" and required unit-length vectors. - The new version computes the length of the query vector once and - assumes all other vectors are of the same length. -</p> -<p> - Using <em>prenormalized-angular</em> with vectors that are not - normalized causes unpredictable nearest neighbor search, and is - observed to give bad results both for performance and quality. -</p> -<p> - The length, magnitude, or norm of a vector <em>x</em> is calculated as <code>length = sqrt(sum(pow(xi,2)))</code>. - The unit length normalized vector is then given by <code>[xi/length]</code>. - Zero vectors may not be used at all. -</p> -<p> - The Vespa <em>prenormalized-angular</em> computes the - <a href="https://en.wikipedia.org/wiki/Cosine_similarity">cosine similarity</a> - and uses <code>1.0 - cos(angle)</code> as the distance metric. - It gives exactly the same ordering as <code>angular</code> distance, - but with a distance in the range [0,2], since cosine similarity has range [1,-1], - so the end result is 0.0 for same direction vectors, - 1.0 for a right angle, and 2.0 for vectors with exactly opposite directions. - Getting the cosine score (or angle) is therefore easy: -</p> -<pre> - rank-profile cosine { - first-phase { - expression: 1.0 - distance(field, embedding) - } - function angle() { - expression: acos(1.0 - distance(field, embedding)) - } - } -</pre> - To compute the cosine similarity directly in a ranking expression - instead of fetching one already computed in a nearestNeighbor query - operator, use the - <a href="../ranking/ranking-expressions.html#cosine-similarity-t"> - cosine_similarity helper function</a>: -<pre> - function mysimilarity() { - expression: cosine_similarity(attribute(myembedding), query(myqueryvector), mydim)) - } -</pre> - - -<h3 id="geodegrees">geodegrees</h3> -<p> - The <em>geodegrees</em> distance metric is only valid for - geographical coordinates (two-dimensional vectors containing - latitude and longitude on Earth, in degrees). It computes the - great-circle distance (in kilometers) between two geographical - points using the - <a href="https://en.wikipedia.org/wiki/Haversine_formula">Haversine formula</a>. - See - <a href="https://github.com/vespa-engine/system-test/blob/master/tests/search/nearest_neighbor/geo.sd">geodegrees system test</a> - for an example. -</p> - -<h3 id="hamming">hamming</h3> -<p> - The <a href="https://en.wikipedia.org/wiki/Hamming_distance">Hamming distance</a> - metric counts the number of dimensions where the vectors have different coordinates. - This isn't useful for floating-point data since it means you only - get 1 bit of information from each floating-point number. - Instead, it should be used for binary data, where each bit is - considered a separate coordinate. Practically, this means you - should use the - <code>int8</code> <a href="../../performance/feature-tuning.html#cell-value-types">cell value type</a> - for your tensor, with the usual encoding from bit pattern to numerical value, for example: -</p> - <ul> - <li><code>00000000</code> → <code>0</code> (hex <code>00</code>)</li> - <li><code>00010001</code> → <code>17</code> (hex <code>11</code>)</li> - <li><code>00101010</code> → <code>42</code> (hex <code>2A</code>)</li> - <li><code>01111111</code> → <code>127</code> (hex <code>7F</code>)</li> - <li><code>10000000</code> → <code>-128</code> (hex <code>80</code>)</li> - <li><code>10000001</code> → <code>-127</code> (hex <code>81</code>)</li> - <li><code>11111110</code> → <code>-2</code> (hex <code>FE</code>)</li> - <li><code>11111111</code> → <code>-1</code> (hex <code>FF</code>)</li> - </ul> -<p> - Feeding data for this use case may be done with - <a href="document-json-format.html#tensor-hex-dump">"hex dump"</a> format - instead of numbers in range <code>[-128,127]</code> both to have a more natural format - for representing binary data, and to avoid the overhead of parsing a large JSON array of numbers. -</p> - - - -<h2 id="bolding">bolding</h2> -<p> -Contained in <a href="#field">field</a> or <a href="#summary">summary</a>. -Highlight matching query terms in the <a href="#summary">summary</a>: -<pre> -bolding: on -</pre> -<p> - The default is no bolding, set <code>bolding: on</code> to enable it. - Note that this command is overridden by <code>summary: dynamic</code>. - If both are specified, bolding will be ignored. - The difference between using bolding instead of <code>summary: dynamic</code> - is the latter will provide a dynamic abstract in addition to highlighting query terms, - while the first only highlights. - Bolding is only supported for <a href="#indexing-index">index</a> fields of type string or array<string>. -</p> -<p> - The default XML element used to highlight the search terms is <hi> - - to override, set <em>container.qr-searchers</em> configuration. Example using <code><strong></code>: -</p> -<pre>{% highlight xml %} -<container> - <search> - <config name="container.qr-searchers"> - <tag> - <bold> - <open><strong></open> - <close></strong></close> - </bold> - <separator>...</separator> - </tag> - </config> - </search> -</container> -{% endhighlight %}</pre> -<p> - Maximum field byte length for bolding is 64Mb - - field values larger than this will be represented as a snippet as in <code>summary: dynamic</code>. -</p> - - - -<h2 id="id">id</h2> -<p> -Contained in <a href="#field">field</a>. -Sets the numerical ID of this field. -All fields have a document-internal ID internally for transfer and storage. -IDs are usually determined programmatically as a 31-bit number. -Some storage and transfer space can be saved by instead explicitly setting IDs to a 7-bit number. -<!-- ToDo: check if this applies to proton --> -<pre> -id: [positive integer] -</pre> -<p>An ID must satisfy these requirements:</p> -<ul> -<li>Must be a positive integer</li> -<li>Must be less than 100 or larger than 127</li> -<li>Must be unique within the document and all documents this document inherits</li> -</ul> - - - -<h2 id="index">index</h2> -<p> -Contained in <a href="#field">field</a> or <a href="#schema">schema</a>. -Sets index parameters. -</p> -<p> - Content in <a href="#string">string</a>-fields with <em>index</em> is <a href="#normalizing">normalized</a> and - <a href="../../linguistics/linguistics-opennlp.html#tokenization">tokenized</a> by default. - The field can be single- or multivalued (e.g. <code>array<string></code>). -</p> -<p> - For <a href="#tensor">tensor</a>-typed fields, <em>index</em> creates an <a href="#index-hnsw">HNSW</a> index - for <a href="../../querying/nearest-neighbor-search-guide">Approximate Nearest Neighbor</a> queries, - with a default <a href="#euclidean">euclidean</a> distance metric. - The index is built after a <a href="#changes-that-require-restart-but-not-re-feed">content node restart</a> (automated on Vespa Cloud). -</p> -<p>Examples:</p> -<pre> -index [index-name]: [property] -</pre> -or -<pre> -index [index-name] { - [property] - [property] - … -} -</pre> - -{% include deprecated.html content=' -If <code>index-name</code> is specified, it will be used instead of the field name as the name of the index. -This use is deprecated, use a synthetic field with the wanted name outside the <code>document</code> block instead - -see an <a href="/en/writing/indexing.html#date-indexing">example</a>.'%} - -Parameters: -<table class="table"> - <thead> - <tr><th>Property</th><th>Occurrence</th><th>Description</th></tr> - </thead><tbody> -<tr><td><a href="#stemming">stemming</a></td> - <td>Zero to one</td> - <td>Set the stemming of this index. - Indexes without a stemming setting get their stemming setting from - the fields added to the index. Setting this explicitly is useful if - fields with conflicting stemming settings are added to this index.</td> -</tr> -<tr><td>arity</td> - <td>One (mandatory for predicate fields), else zero.</td> - <td>Set the - <a href="../../schemas/predicate-fields.html#index-size">arity value for a predicate field</a>. - The data type for the containing field must be <code>predicate</code>.</td> -</tr> -<tr><td>lower-bound</td> - <td>Zero to one</td> - <td>Set the - <a href="../../schemas/predicate-fields.html#upper-and-lower-bounds">lower bound value for a predicate field</a>. - The data type for the containing field must be <code>predicate</code>.</td> -</tr> -<tr><td>upper-bound</td> - <td>Zero to one</td> - <td>Set the - <a href="../../schemas/predicate-fields.html#upper-and-lower-bounds">upper bound value for predicate fields</a>. - The data type for the containing field must be <code>predicate</code>.</td> -</tr> -<tr><td style="white-space: nowrap">dense-posting-list-threshold</td> - <td>Zero to one</td> - <td>Set the - <a href="../../schemas/predicate-fields.html#dense-posting-list-threshold">dense posting list threshold value for predicate fields</a>. - The data type for the containing field must be <code>predicate</code>.</td> -</tr> -<tr><td>enable-bm25</td> - <td>Zero to one</td> - <td>Enable this index field to be used with the - <a href="../ranking/rank-features.html#bm25">bm25 rank feature</a>. - This creates posting lists for the - <a href="../../content/proton.html#index">indexes</a> - for this field that has interleaved features in the document ID streams. - This makes it fast to compute the <em>bm25</em> score. - See the <a href="/en/ranking/bm25.html">BM25 reference</a> for details and example use. - </td> -</tr> -<tr><td><a href="#index-hnsw">hnsw</a></td> - <td>Zero to one</td> - <td> - Specifies optional parameters for an HNSW index to enable faster, approximate nearest neighbor search. - Only supported for tensor attribute fields with tensor types with: - <ul> - <li>One indexed dimension - single vector per document</li> - <li>One or more mapped dimensions and one indexed dimension - multiple vectors per document</li> - </ul> - Used in combination with the - <a href="../querying/yql.html#nearestneighbor">nearestNeighbor</a> - query operator. - </td> -</tr> -</tbody> -</table> - - - -<h2 id="index-hnsw">hnsw</h2> -<p> -Contained in <a href="#index">index</a>. -Specifies optional parameters for an HNSW index to enable faster, approximate nearest neighbor search -using the <a href="../querying/yql.html#nearestneighbor">nearestNeighbor</a> query operator. - -{% include note.html content="Specifying the <code>index</code> keyword in the <a href='#indexing'>indexing</a> statement of a tensor creates an HNSW index with default settings, even if this block is not specified! "%} - -This implements a modified version of the -Hierarchical Navigable Small World (HNSW) graphs algorithm (<a href="https://arxiv.org/abs/1603.09320">paper</a>). -</p> -<p>Only supported for the following tensor attribute field types:</p> -<ul> - <li>Single vector per document: Tensor type with one indexed dimension. - Example: <code>tensor<float>(x[3])</code></li> - <li>Multiple vectors per document: Tensor type with one or more mapped dimensions and one indexed dimension. - Examples: <code>tensor<float>(m{},x[3])</code>, <code>tensor<float>(m{},n{},x[3])</code></li> -</ul> -<p> -HNSW indexes are not supported in -<a href="../../performance/streaming-search.html#differences-in-streaming-search">streaming search</a>. -</p> -<pre> -hnsw { - [parameter]: [value] - [parameter]: [value] - ... -} -</pre> -<p>The following parameters are used when building the index graph:</p> -<table class="table"> -<thead> - <tr><th>Parameter</th><th>Description</th></tr> -</thead> -<tbody> - <tr> - <td>max-links-per-node</td> - <td> - Specifies how many links per HNSW node to select when building the graph. The default value is 16. - In <a href="https://github.com/nmslib/hnswlib/blob/master/ALGO_PARAMS.md">HNSWlib</a> (implementation based on the paper) - this parameter is known as <em>M</em>. - </td> - </tr> - <tr> - <td>neighbors-to-explore-at-insert</td> - <td> - Specifies how many neighbors to explore when inserting a document in the HNSW graph. The default value is 200. - In HNSWlib, this parameter is known as <em>ef_construction</em>. - </td> - </tr> -</tbody> -</table> -<p> - The <a href="#distance-metric">distance metric</a> specified on the <em>attribute</em> - is used when building and searching the graph. Example: -<pre> -field text_embedding type tensor<float>(x[384]) { - indexing: summary | attribute | index - attribute { - distance-metric: prenormalized-angular - } - index { - hnsw { - max-links-per-node: 24 - neighbors-to-explore-at-insert: 200 - } - } -} -</pre> -<p> -See -<a href="../../querying/approximate-nn-hnsw">Approximate Nearest Neighbor Search using HNSW Index</a> -for examples of use, and see -<a href="https://blog.vespa.ai/approximate-nearest-neighbor-search-in-vespa-part-1/"> -Approximate Nearest Neighbor Search in Vespa - Part 1</a> blog post -for how the Vespa team selected HNSW as the baseline algorithm for extension and integration in Vespa. -</p> - - -<h2 id="indexing">indexing</h2> -<p> -Contained in <a href="#field">field</a> or -<a href="#struct-field">struct-field</a>. -One or more Indexing Language instructions used to produce index, attribute -and summary data from this field. Indexing instructions have pipeline -semantics similar to Unix shell commands. The value of the field -enters the pipeline during indexing, and the pipeline puts the value -into the desired index structures, possibly doing transformations and -pulling in other values along the way. -<pre> -indexing: [index-statement] -</pre> -or -<pre> -indexing { - [indexing-statement]; - [indexing-statement]; - … -} -</pre> -<p> -If the field containing this is defined outside the document, it -must start with an indexing statement which -outputs a value (either "input [fieldname]" to fetch a field value, -or a literal, e.g, "some-value" ). Fields in documents will use the value of the enclosing -field as input (input [fieldname]) if one isn't explicitly provided. -</p> -<p> -Specify the operations separated by the pipe (<code>|</code>) character. -For advanced processing needs, -use the <a href="../writing/indexing-language.html">indexing language</a>, -or write a <a href="../../applications/document-processors.html"> document processor</a>. -Supported expressions for fields are: -</p> -<table class="table"> -<thead> -<tr> - <th>expression</th> - <th>description</th> -</tr> -</thead><tbody> -<tr><td>attribute</td> -<td> -<p id="indexing-attribute"> - <a href="../../content/attributes.html">Attribute</a> is used to make a field available for sorting, - grouping, ranking and searching using <a href="#match">match</a> mode <code>word</code>. -</p> -</td></tr> -<tr><td>index</td> -<td> - <p id="indexing-index"> - Creates a searchable <a href="../../content/proton.html#index">index</a> for the values of this field - using <a href="#match">match</a> mode <code>text</code>. - By default, the index name will be the same as the name of the schema field. - Use a <a href="#fieldset">fieldset</a> to combine fields in the same set for searching. - </p> -</td></tr> -<tr><td>set_language</td> -<td> - Sets document language - <a href="../writing/indexing-language.html#set_language">details</a>. -</td></tr> -<tr><td>summary</td> -<td> - Includes the value of this field in a <a href="../writing/indexing-language.html#summary">summary</a> field. - Modify summary output by using <a href="#summary">summary:</a> (e.g., to generate dynamic teasers). -</td></tr> -</tbody> -</table> -<p> -When combining both <code>index</code> and <code>attribute</code> in the indexing statement for a field, -e.g <code>indexing: summary | attribute | index</code>, -the <a href="#match">match</a> mode becomes <code>text</code> for the field. -So searches in this field will not search the contents in the <a href="#attribute">attribute</a> but the index. -</p> -<p>Find examples and more details in the <a href="../../querying/text-matching.html">Text Matching</a> guide.</p> - - - -<h2 id="linguistics">linguistics</h2> -<p>Sets a linguistics 'profile' deciding how content and searches against this field should be linguistically processed. -The profile value is propagated to the <a href="../../linguistics/linguistics.html">linguistics component</a> -which can be configured to do profile-specific processing.</p> -<pre> -linguistics { - profile: myIndexAndSearchProfile -} -</pre> -or -<pre> -linguistics { - profile { - index: myIndexProfile - search: mySearchProfile - } -} -</pre> - - - -<h2 id="match">match</h2> -<p>Contained in <a href="#field">field</a>, <a href="#fieldset">fieldset</a> or -<a href="#struct-field">struct-field</a>. -Sets the matching method to use for this field to something other than the default token matching. -<pre> -match: [property] -</pre> -or -<pre> -match { - [property] - [property] - … -} -</pre> -<p> -Whether the match type is <code>text</code>, <code>word</code> or <code>exact</code>, -all term matching will be done after <a href="../../linguistics/linguistics-opennlp.html#normalization">normalization</a> -and locale-independent lowercasing (in that order). -</p><p> -Find examples and more details in the <a href="../../querying/text-matching.html">Text Matching</a> guide. -Also see search using <a href="../querying/yql.html#matches">regular expressions</a>. -</p> -<table class="table"> -<thead> -<tr><th>Property</th><th>Valid with</th><th>Description</th></tr> -</thead><tbody> -<tr><td>text</td> -<td>index</td> -<td><p> - The default for string fields with <code>index</code>. Can not be combined with exact matching. - The field is matched per <a href="../../linguistics/linguistics-opennlp.html#tokenization">token</a>. -</p></td> -</tr> - -<tr><td>exact</td> -<td>index, attribute</td> -<td><p id="exact"> - Can not be combined with <em>text</em> matching. - </p><p> - The field is matched <em>exactly</em>: - Strings containing any characters whatsoever will be indexed and matched as-is. - Lowercasing is still performed unless <code>match: cased</code> is also used. - In queries, the exact match string ends at the exact match terminator (below). - </p><p> - A field with <code>match: exact</code> is considered to be - a <a href="#filter">filter field</a>, just as if <code>rank: filter</code> was specified. - This is because there is only one word per field - (or per item in the case of multivalued types such as <code>array<string></code>), - so there is little ranking information. - Turn off the implicit <code>rank: filter</code> by adding <code>rank: normal</code>. -</p></td> -</tr> - -<tr><td style="white-space:nowrap;">exact-terminator</td> -<td>index, attribute</td> -<td><p id="exact-terminator"> - Only valid for <code>match: exact</code>. Default is <code>@@</code>. - Specify terminator in <a href="../api/query.html#model.querystring">query strings</a>. - If the query strings can contain <code>@@</code>, set a different terminator, - or use <code>match: word</code>, see below. Example - use: -<pre> -match { - exact - exact-terminator: "@%" -} -</pre> -<p> - on a field called <code>tag</code> to make query <code>tag:a b c!@%</code> - match documents with the string <em>a b c!</em> -</p><p> - Example using the default terminator: If <code>tag</code> is an exact match field, the query: -<pre> -someword AND (tag:!*!@@ OR tag:(kanoo)@@) -</pre> - matches documents with <code>someword</code> - and either <code>!*!</code> or <code>(kanoo)</code> as a tag. - Note that without the <code>@@</code> terminating the second tag string, - the second tag value would be <code>(kanoo))</code>. - </td> -</tr> - -<tr><td>word</td> -<td>index, attribute</td> -<td><p id="word"> - This is the default matching mode for <a href="../../content/attributes.html">string attributes</a>. - It cannot be combined with <em>text</em> matching. - </p><p> - Match word means that the entire content of the field is indexed as a single word. - </p><p> - Word matching is like exact matching, but with more advanced query parsing. - The query terms are heuristically parsed, taking into account some usual query syntax characters. - One can also use double quotes to include spaces, stars, or exclamation marks. - </p><p> - Example: If <code>artist</code> is a string attribute, the query: -<pre> -foo AND (artist:"'N Sync" OR artist:"*NSYNC" OR artist:A*teens OR artist:"Wham!") -</pre> - <p> - matches documents with <code>foo</code> and at least one of - <code>'N Sync</code> or <code>*NSYNC</code> or <code>A*teens</code> or <code>Wham!</code> - in the artist field - </p><p> - Note that without the quotes, the space in <code>'N Sync</code> would end that word - and would result in a search for just <code>'N</code>, - similarly the <code>!</code> would mean to increase the weight of a <code>Wham</code> term if not quoted. -</p></td> -</tr> - -<tr><td>prefix</td> -<td>attribute</td> -<td><p id="prefix"> - Has no effect, as <a href="../../content/attributes.html">attributes</a> - always support prefix searches. Prefix matching must be - <a href="../querying/yql.html#prefix">specified in the query</a>. - See also <a href="../querying/yql.html#matches">regular expressions</a>. -</p></td> -</tr> - -<tr><td>substring</td> - <td><a href="../../performance/streaming-search.html">Streaming mode</a> only</td> - <td><p id="substring"> - Set default match mode to <em>substring</em> for the field. - Only available in streaming search. - As the data structures in streaming search support substring searches, - one can always set substring matching in the query, - without setting the field to the substring default. - Also see <a href="../querying/yql.html#matches">regular expressions</a>. - </p></td> -</tr> - -<tr><td>suffix</td> - <td><a href="../../performance/streaming-search.html">Streaming mode</a> only</td> - <td><p id="suffix"> - Like substring above. - </p></td> -</tr> - -<tr><td>uncased</td> - <td>index, attribute</td> - <td><p id="uncased"> - Use case-insensitive matching (the default). - </p></td> -</tr> - -<tr><td>cased</td> -<td>index, attribute</td> -<td><p id="cased"> - Use case-sensitive matching. Usually only used together - with <code>match: exact</code> or <code>match: word</code> modes. - When using <code>match: text</code>, note that if you are using a custom - <a href="../../linguistics/linguistics-custom.html">linguistics implementation</a>, - this will only have effect for string index fields if that implementation produces cased tokens. -</p></td> -</tr> - -<tr><td>max-length</td> -<td>index</td> -<td><p id="max-length"> - Limit the length of the field that will be used for matching. - By default, only the first 1M characters are indexed. -</p><p> - When adjusting this limit, it might also be needed to adjust - <a href="#max-occurrences">max-occurrences</a>. -</p></td> -</tr> - -<tr><td>max-occurrences</td> -<td>index</td> -<td><p id="max-occurrences"> - Configure the maximum number of occurrences that will be indexed for - each unique token/term in the field for a given document. - If this limit is reached, consecutive occurrences - of the same token/term are ignored for that document. - The default value is 10000. -</p><p> - Adjusting this limit might be needed when using the - <a href="../querying/yql.html#phrase">phrase</a>, - <a href="../querying/yql.html#near">near</a>, or - <a href="../querying/yql.html#onear">onear</a> - query operators - to query documents with large field values (see <a href="#max-length">max-length</a>) - that contain more than 10000 occurrences of common tokens/terms. - When using these operators, it is only possible to match among the - first <em>max-occurrences</em> of a token/term in a document. -</p></td> -</tr> - -<tr><td>max-token-length</td> -<td>index</td> -<td><p id="max-token-length"> - Configure the max length of tokens that will be indexed for the field. - Longer tokens are silently ignored. - The unit is characters (cf. java.lang.String.length()). - The default value is 1000. -</p></td> -</tr> - -<tr><td>gram</td> -<td>index</td> -<td><p id="gram"> - This field is matched using n-grams. - For example, with the default gram size 2, the string "hi blue" is tokenized to "hi bl lu ue" - both in the index and in queries to the index. - </p><p> - N-gram matching is useful mainly as an alternative to - <a href="../../linguistics/linguistics-opennlp.html#tokenization">segmentation</a> in CJK languages. - Typically, it results in increased recall and lower precision. - However, as Vespa usually uses proximity in ranking, - the precision offset may not be of much importance. - Grams consume more resources than other matching methods - because both indexes and queries will have more terms, - and the terms contain repetition of the same letters. - On the other hand, CPU-intensive CJK segmentation is avoided. - </p><p> - It may also be used for substring matching in general. -</p></td> -</tr> - -<tr><td>gram-size</td> -<td>index</td> -<td><p id="gram-size"> - A positive, nonzero number, default 2. - Sets the gram size when gram matching is used. Example:</p> -<pre> -match { - gram - gram-size: 3 -} -</pre> -</td> -</tr> - -</tbody> -</table> - - - -<h2 id="rank">rank</h2> -<p>Contained in <a href="#field">field</a>, <a href="#struct-field">struct-field</a> or -<a href="#rank-profile">rank-profile</a>. -Set the kind of ranking calculations that will be done for the field. Even though the -actual ranking expressions decide the ranking, this setting tells Vespa which preparatory calculations -and which data structures are needed for the field. -<pre> -rank [field-name]: [ranking settings] -</pre> -or -<pre> -rank { - [ranking setting] -} -</pre> -The field name should only be specified when used inside a rank-profile. -The following ranking settings are supported in addition to the default: -<table class="table"> -<thead> -<tr><th>Ranking setting</th><th>Description</th></tr> -</thead><tbody> -<tr><td>filter</td><td> - <p id="filter"> - Indicates that matching in this field should use fast bit vector data structures only. - This saves CPU during matching, but only a few simple ranking features will be available for the field. - This setting is appropriate for fields typically used for filtering or simple boosting purposes, - like filtering or boosting on the language of the document. - </p> - <ul> - <li> - For <em>index</em> fields, this setting does <span style="text-decoration: underline;">not</span> change index formats - but helps choose the most compact representation when matching against the field. - </li> - <li> - For <em>attribute</em> fields with <em>fast-search</em> this setting builds additional - posting list representations (bit vectors) can significantly speed up query evaluation. - </li> - </ul> - <p> - See <a href="../../performance/feature-tuning.html#when-to-use-fast-search-for-attribute-fields">feature tuning</a> and - <a href="../../performance/practical-search-performance-guide.html">the practical search performance guide</a>. - </p> -</td></tr> -<tr><td>normal</td><td> - <p id="normal"> - The reverse of <code>filter</code>. - Matching in this field will use normal data structures and give normal match information for ranking. - Used to turn off implicit <code>rank: filter</code> when using <a href="#exact">match: exact</a>. - If both <code>filter</code> and <code>normal</code> are set somehow, - the effect is as if only <code>normal</code> was specified. - </p> -</td></tr> -</tbody> -</table> -<p> - Related: See the <a href="../querying/yql.html#filter">filter</a> query annotation - for how to annotate query terms as filters. -</p> - -<h3 id="rank-filter-threshold">filter-threshold</h3> -<p> - Contained in a <a href="#rank-profile">rank-profile</a>. - Used to optimize query performance when searching large text <a href="../../basics/schemas.html#document-fields">index</a> fields, - by allowing a per query combination of <a href="#filter">rank: filter</a> and <a href="#normal">rank: normal</a> behavior. - See <a href="#filter-threshold">profile-level filter-threshold</a> for how to use the same value for all <em>index</em> fields. -</p> -<pre> -rank [field-name] { - filter-threshold: 0.05 -} -</pre> -<table class="table"> - <thead> - <tr><th>Setting</th><th>Description</th></tr> - </thead> - <tbody> - <tr><td>filter-threshold</td><td> - <p> - Threshold value (in the range [0.0, 1.0]) deciding when matching in this <em>index</em> field should be treated as a filter. - This happens for query terms with <a href="../../learn/glossary.html#estimated-hit-ratio">estimated hit ratios</a> (in the - range [0.0, 1.0]) that are above the <em>filter-threshold</em>. - Then, fast bitvector data structures are used, similar to when the field is set to <a href="#filter">rank: filter</a>. - This saves CPU and Disk I/O during matching and typically results in faster query evaluation, - with the downside being that only a boolean signal is available for ranking (the document being a match or not). - <a href="../../ranking/bm25.html">BM25</a> handles this by assuming one occurrence of the query term in the document, - and the field length being equal to the average field length. - </p> - <p> - Use this to optimize query performance when searching large text <em>index</em> fields with e.g. - the <a href="../../ranking/wand.html#weakand">weakAnd</a> query operator and <a href="../../ranking/bm25.html">BM25</a> ranking. - Query terms that are common in the corpus (e.g., stopwords) are treated as filters with faster matching and simplified ranking, - while other query terms are handled as usual with full ranking. - </p> - <p> - In testing with various text datasets (e.g., Wikipedia), - a <em>filter-threshold</em> setting of 0.05 has been shown to be a good starting point. - <a href="../../performance/feature-tuning.html#posting-lists">Read more</a>. - </p> - <p> - This setting is only relevant for <a href="../../basics/schemas.html#document-fields">index</a> fields, - and cannot be used in combination with <a href="#filter">rank: filter</a>. - Has no effect in <a href="../../performance/streaming-search.html#differences-in-streaming-search">streaming search</a>. - </p> - </td></tr> - </tbody> -</table> - - -<h3 id="rank-element-gap">element-gap</h3> -<p> - Contained in a <a href="#rank-profile">rank-profile</a>. Used to specify the gap between positions in adjacent elements in multi-value fields. The default value is <code>infinity</code>. It should be specified as <code>infinity</code> or as a positive integer number. -</p> -<p> - Consider an <code>array<string></code> field with three elements <code>["a c", "x b x a x x x x x x b", "x x d"]</code>. - Normally, distance calculation is only performed within an element, thus the minimum forward distance between occurrences for terms <em>a</em> and <em>b</em> is 7. - By setting the <code>element-gap</code> for the field to 0, adjacent elements are considered and the minimum forward distance between occurrences for terms <em>a</em> and <em>b</em> becomes 3. - The minimum distance between occurrences for terms <em>c</em> and <em>d</em> is not calculated since they are not in adjacent elements. -</p> -<p> - The <code>element-gap</code> setting affects the <a href="/en/reference/ranking/rank-features.html#nativeProximity">nativeProximity</a> rank feature, - the <a href="/en/reference/querying/yql.html#near">near</a> query operator - and the <a href="/en/reference/querying/yql.html#onear">onear</a> query operator. -</p> -<pre> -rank [field-name] { - element-gap: 0 -} -</pre> -<p>No restart or reindexing is required when changing this setting, it is immediately effective.</p> - - - -<h2 id="query-command">query-command</h2> -<p>Contained in <a href="#fieldset">fieldset</a>, <a href="#field">field</a> or -<a href="#struct-field">struct-field</a>. -Specifies a function to be performed on query terms to the indexes of this field when searching. -The Search Container server has support for writing Vespa Searcher plugins that process these commands.</p> -<pre> -query-command: [an identifier or quoted string] -</pre> - -<p>If you write a plugin searcher that needs some index-specific -configuration parameter, that parameter can be set here.</p> - -<p>There is one built-in query-command available: <code>phrase-segmenting</code>. -If this is set, terms connected by non-word characters in user queries (such as "a.b") -will be parsed to a phrase item, instead of by default, an AND item where these terms have connectivity -set to 1.</p> - - -<h2 id="rank-type">rank-type</h2> -<p>Contained in <a -href="#field">field</a> or -<a href="#rank-profile">rank-profile</a>. -Selects the low-level rank settings to be used for this field when using <code>nativeRank</code>. -<pre> -rank-type [field-name]: [rank-type-name] -</pre> -The field name can be skipped inside fields. Defined rank types are: -<table class="table"> - <thead> - <tr><th>Type</th><th>Description</th></tr> - </thead><tbody> - <tr> - <td>identity</td> - <td> - Used for fields that contains only what this document - <em>is</em>, e.g., "Title". Complete identity hits will get a - high rank. - </td> - </tr><tr> - <td>about</td> - <td> - Some text which is (only) about this document, - e.g. "Description". About hits get high rank on partial - matches and higher for matches early in the text and repetitive matches. - This is the default rank type. - </td> - </tr><tr> - <td>tags</td> - <td> - Used for simple tag fields of type tag. The tags rank type uses a logarithmic table to give more relative boost in the low range: - As tags are added, they should have a significant impact on the rank score, but as more and more tags are added, each new tag should contribute less. - </td> - </tr><tr> - <td>empty</td> - <td> - Gives no relevancy effect on matches. Used for fields you just - want to treat as filters. - </td> - </tr> - </tbody> -</table> -<p> - For <code>nativeRank</code>, one can specify a rank type per field. - If the supported rank types do not meet requirements, - one can explicitly configure the native rank features using rank-properties. - See the <a href="../ranking/nativerank.html">native rank reference</a> for more information. -</p> - - -<h2 id="weakand">weakand</h2> -<p> - Contained in <a href="#rank-profile">rank-profile</a>. -</p> -<p> - Tunes the <a href="../../ranking/wand.html#weakand">weakAnd</a> algorithm to automatically - exclude terms and documents with expected low query significance based on document frequency - statistics present in the document corpus. This makes matching faster at the cost of potentially - reduced recall. -</p> -<pre> -weakand { - [body] -} -</pre> -<p> - Note that all document frequency calculations are done using <em>content node-local</em> document - statistics (i.e. <a href="../../ranking/significance#global-significance-model">global significance</a> - does not have an effect). This means results may differ across different content nodes and/or - content node groups. -</p> -<p> -The body of a <code>weakand</code> statement consists of: -</p> -<table class="table"> - <thead> - <tr> - <th>Property</th> - <th>Occurrence</th> - <th>Description</th> - </tr> - </thead> - <tbody> - <tr> - <td style="white-space: nowrap">stopword-limit</td> - <td>Zero to one</td> - <td> - <p id="weakand-stopword-limit"> - A number in the range [0, 1]. Represents the maximum - <a href="../../learn/glossary.html#document-frequency-normalized">normalized document frequency</a> a query term can have in the - corpus (i.e. the ratio of all documents where the term occurs at least once) before - it's considered a stopword and dropped entirely from being a part of the - <code>weakAnd</code> evaluation. This makes matching faster at the cost of - potentially producing more hits. Dropped terms are not exposed as part of ranking. - </p> - <p>Example:</p> - <pre>stopword-limit: 0.60</pre> - <p>This will drop all query terms that occur in at least 60% of the documents.</p> - <p> - Using <code>stopword-limit</code> is similar to explicitly removing stopwords - from the query up front, but has the benefit of dynamically adapting to the - actual document corpus and not having to know—or specify—a set of stopwords. - <a href="../../performance/feature-tuning.html#posting-lists">Read more</a>. - </p> - </td> - </tr> - <tr> - <td style="white-space: nowrap">adjust-target</td> - <td>Zero to one</td> - <td> - <p id="weakand-adjust-target"> - A number in the range [0, 1] representing - <a href="../../learn/glossary.html#document-frequency-normalized">normalized document frequency</a>. - Used to derive a per-query document score threshold, where documents scoring - lower than the threshold will not be considered as potential hits from the - <code>weakAnd</code> operator. The score threshold is selected to be equal to - that of the query term whose document frequency is <em>closest</em> to the - configured <code>adjust-target</code> value. - <p> - <p> - This can be used to efficiently <em>exclude</em> documents that only match terms that - occur very frequently in the document corpus. Such terms are likely to be stopwords - that have low semantic value for the query, and excluding documents only containing - them is likely to have only a minor impact on recall. - </p> - <p> - This makes overall matching faster by reducing the number of hits produced by - the <code>weakAnd</code> operator. - </p> - <p>Example:</p> - <pre>adjust-target: 0.01</pre> - <p> - This excludes documents that only have terms that occur in more than approximately 1% - of the document corpus. The actual threshold is query-specific and based on the query - term score whose document frequency is closest to 1%. - </p> - <p> - <code>adjust-target</code> can be used together with <a href="#weakand-stopword-limit">stopword-limit</a> - to efficiently prune both terms and documents with low significance when processing queries. - <a href="../../performance/feature-tuning.html#posting-lists">Read more</a>. - </p> - </td> - </tr> - <tr> - <td style="white-space: nowrap">allow-drop-all</td> - <td>Zero to one</td> - <td> - <p id="weakand-allow-drop-all"> - A boolean value. The default behavior of <code>weakAnd</code> is to always keep at least one term (the least common one) even though - it is considered a stopword. This is to avoid dropping all query terms in order to make sure that some hits are produced. - </p> - <p> - If set to <code>true</code>, the <code>weakAnd</code> operator - will allow removal of all query terms if they are all considered stopwords (i.e., if - <code>stopword-limit</code> is set and all query terms are above the limit). - <p> - This may be desired (and significantly improve query performance) if <code>weakAnd</code> is used together with another query operator, - e.g. the <a href="../../querying/nearest-neighbor-search#querying-using-nearestneighbor-query-operator">nearestNeighbor</a> operator. - </p> - <p> - Be aware that if this is set to <code>true</code> and all query terms are - considered stopwords, the <code>weakAnd</code> operator will not produce <em>any</em> - hits. And by extension, if <code>weakAnd</code> is used by itself, the query may return no hits. - </p> - <p>Example:</p> - <pre>allow-drop-all: true</pre> - <p> - This overrides the default behavior of <code>weakAnd</code> and allows all query terms to be dropped if they are all considered stopwords. - </p> - <p> - {% include important.html content=" - Defaults to <code>false</code> if not specified. - "%} - </p> - </td> - </tr> - </tbody> -</table> - - -<h2 id="summary-to">summary-to</h2> -<p> - {% include deprecated.html content="Use <a href='#document-summary'>document-summary</a> instead."%} - Contained in <a href="#field">field</a> or - <a href="#struct-field">struct-field</a>. - Specifies the name of the document summaries that should contain this field. -</p> -<pre> -summary-to: [summary-name], [summary-name], … -</pre> -<p> -Fields with summary will always be part of the default summary regardless of this setting. Use explicit <a href="#document-summary">document-summary</a> instead. -See also <a href="../../querying/document-summaries.html">document summaries</a>. -</p> - - -<h2 id="summary">summary</h2> -<p> -Contained in <a href="#field">field</a> or -<a href="#document-summary">document-summary</a> or -<a href="#struct-field">struct-field</a>. -Declares a summary field. -<pre> -summary: [property] -</pre> -or -<pre> -summary [name] { - [body] -} -</pre> -The summary <em>name</em> can be skipped if this is set inside a -field. The name will then be the same as the name of the source -field. -<em>full</em> summary is the default. Long field values (like document -content fields) should be made <em>dynamic</em>. -The body of a summary may contain: -<table class="table"> -<thead> -<tr><th>Name</th><th>Occurrence</th><th>Description</th></tr> -</thead><tbody> -<tr><td>full</td> - <td style="white-space:nowrap;">Zero to one</td> -<td>Returns the full field value in the summary (the default).</td> -</tr> -<tr><td>bolding: on</td> - <td>Zero to one</td> -<td>Specifies whether the content of this field should be <a href="#bolding">bolded</a>. -Only supported for <a href="#indexing-index">index</a> fields of type string or array<string>.</td> -</tr> -<tr><td>dynamic</td> - <td>Zero to one</td> -<td>Make the value returned in results from this summary field a <em>dynamic abstract</em> of the source - field by extracting fragments of text around matching query terms. Matching query terms will also be highlighted, in - similarity with the bolding feature. - This highlighting is not affected by the query-argument <span class="code">bolding</span>. - The default XML element used to highlight query terms is -<code><hi></code> - refer to <a href="#bolding">bolding</a> for how to configure. - <em>dynamic</em> is only supported for <a href="#indexing-index">index</a> fields of type string or array<string>. - For array<string> fields, a dynamic abstract is created per string item in the array. -</td> -</tr> -<tr> - <td>source</td> - <td>Zero to one</td> - <td> - <p id="source"> - Specifies the name of the field or fields from which the value of this summary field should be fetched. - If multiple fields are specified, the value will be taken from the first field if that has a value, - from the second if the first one is empty, and so on. - </p> -<pre> -source: [field-name], [field-name], … -</pre> - <p> - When this is not specified, the source field is assumed to be the field with the same name as the summary field. - </p> - <p> - Refer to <a href="#add-or-remove-an-existing-document-field-from-document-summary">attribute</a> - and <a href="#add-or-remove-a-new-non-attribute-document-field-from-document-summary">non-attribute</a> - fields for modifying a schema. - </p> - </td> -</tr> -<tr><td>to</td> - <td>Zero to one</td> - <td>Specifies the name of the document summaries that this should be included in. -<pre> -to: [document-summary-name], [document-summary-name], … -</pre> - This can only be specified in fields, not in the explicit document - summaries. When this is not specified, the field will go to the - <code>default</code> document summary.</td> -</tr> -<tr><td style="white-space: nowrap">matched-elements-only</td> - <td>Zero to one</td> - <td> - <p id="matched-elements-only"> - Specifies that only the matched elements in a searchable - <a href="#array">array of primitive</a>, - <a href="#weightedset">weightedset</a>, - <a href="#array">array of struct</a> or - <a href="#map">map type</a> field are returned as part of document summary. - For array of struct or map type fields, this is typically used in accordance with the - <a href="../querying/yql.html#sameelement">sameElement</a> operator, - but it can also be used when searching directly on a sub-struct field. - It is also supported when the field is <a href=#import-field>imported</a>. - </p> - <p>See <a href="#map">example use</a> and example schemas:</p> - <ul> - <li><a href="https://github.com/vespa-engine/system-test/blob/master/tests/search/matched_elements_only/indexed/test.sd"> - matched elements only</a></li> - <li><a href="https://github.com/vespa-engine/system-test/blob/master/tests/search/struct_and_map_types/attribute_fields/test.sd"> - array of struct and map type</a></li> - </ul> -</td> -</tr> -<tr><td style="white-space: nowrap">select-elements-by</td> - <td>Zero to one</td> - <td> - <p id="select-elements-by"> - Use a summary feature to control which elements in an - <a href="#array">array of primitive</a> or - <a href="#array">array of struct</a> field are returned as part of document summary. - </p> -<pre> -select-elements-by: <summary-feature-name> -</pre> - <p> - The summary feature used must be a tensor with a single mapped - dimension. An element will be returned if its id is a label along - the mapped dimension of this tensor. - </p> - <ul> - <li><a href="https://github.com/vespa-engine/system-test/blob/master/tests/search/chunk_selection/test.sd">schema example</a></li> - </ul> -</td> -</tr> -<tr><td style="white-space: nowrap">tokens</td> -<td>Zero to one</td> -<td> -<p id="tokens"> -Make the value returned in results from this summary field be an array -of the tokens indexed in the source field. Multiple tokens at the same -location are put into a nested array. -</p> -<p> -The source field must be specified, -and it must be an <a href="#indexing-index">index</a> or -<a href="#indexing-attribute">attribute</a> field of type string, -array<string> or weightedset<string>. If the source field -is of type weightedset<string> then the summary field is -rendered as if the source field was of type array<string>, -weights are not shown. -</p> - -<p> -This is mainly useful for <a href="../../querying/text-matching.html#tokens-example">linguistics transformations debugging</a>, to correlate query trace with the tokens indexed. -</p> -</td> -</tr> -</tbody> -</table> -<p> - Read more about <a href="../../querying/document-summaries.html">document summaries</a>. -</p> - - - -<h2 id="weight">weight</h2> -<p> -Contained in <a href="#field">field</a>. -The weight of a field - the default is 100. -The field weight is used when calculating the <a href="../../basics/ranking.html">rank scores</a>. -</p> -<pre> -weight: [positive integer] -</pre> - - - -<h2 id="weightedset-properties">weightedset</h2> -<p> -Contained in <a href="#field">field</a> of type weightedset. -Properties of a weighted set. -<pre> -weightedset: [property] -</pre> -or -<pre> -weightedset { - [property] - [property] - … -} -</pre> -<table class="table"> - <thead> - <tr><th>Property</th><th>Occurrence</th><th>Description</th></tr> - </thead><tbody> -<tr><td style="white-space: nowrap">create-if-nonexistent</td> - <td style="white-space:nowrap;">Zero to one</td> - <td>If the weight of a key is adjusted in a document using a partial update increment or decrement command, - but the key is currently not present, the command will be ignored by default. - Set this to make keys to be created in this case instead. - This is useful when the weight is used to represent the count of the key. -<pre> -field tag type weightedset<string> { - indexing: attribute | summary - weightedset { - create-if-nonexistent - remove-if-zero - } -} -</pre> - </td> -</tr> -<tr><td>remove-if-zero</td> - <td>Zero to one</td> - <td>This is the companion of <code>create-if-nonexistent</code> for the converse case: - By default, keys may have zero as weight. - With this turned on, keys whose weight is adjusted (or set) to zero will be removed.</td> -</tr> -</tbody> -</table> - - -<h2 id="import-field">import field</h2> -<p> -Contained in <a href="#schema">schema</a>. -Using a <a href="#reference">reference</a> to a document type, -import a field from that document type into this schema to be used for matching, ranking, grouping, and sorting. -Only attribute fields can be imported. -Importing fields are not supported in -<a href="../../performance/streaming-search.html#differences-in-streaming-search">streaming search</a>. -</p> -<p> -The imported field inherits all but the following properties from the parent field: -</p> -<ul> - <li><a href="#attribute">attribute: fast-access</a></li> -</ul> -<p> -Refer to <a href="../../schemas/parent-child.html">parent/child</a> for a complete example. -Note that the imported field is put <span style="text-decoration: underline">outside</span> the document type: -</p> -<pre> -schema myschema { - document myschema { - field parentschema_ref type reference<parentschema> { - indexing: attribute - } - } - import field parentschema_ref.name as parent_name {} -} -</pre> -<p> -Extra restrictions apply for some of the field types: -<table class="table"> - <thead> - <tr> - <th>Field type</th> - <th>Restriction</th> - </tr> - </thead> - <tbody> - <tr> - <td>array of struct</td> - <td>Can be imported if at least one of the struct fields has an attribute. - All struct fields with attributes must have primitive types. - Only the struct fields with attributes will be visible.</td> - </tr><tr> - <td>map of struct</td> - <td>Can be imported if the key field has an attribute, and - at least one of the struct fields has an attribute. - All struct fields with attributes must have primitive types. - Only the key field and the struct fields with attributes will be visible.</td> - </tr><tr> - <td>map</td> - <td>Can be imported if both key and value fields have primitive types and have attributes.</td> - </tr><tr> - <td>position</td> - <td>Can be imported if it has an attribute.</td> - </tr><tr> - <td style="white-space: nowrap">array of position</td> - <td>Can be imported if it has an attribute.</td> - </tr> - </tbody> -</table> -<p> -To use an imported field in summary, create an explicit -<a href="#document-summary">document summary</a> containing the field. -</p><p> -Imported fields can be used to expire documents, but <a href="../../schemas/documents.html#document-expiry">read this first</a>. -</p> - - - -<h2 id="document-and-search-field-types">Document and search field types</h2> -<p> -Note that it is possible to make a document field -of one type into one or more instances of another search field, by -declaring a field outside the document, which uses other fields as -input. For example, to create an integer attribute for a -string containing a comma-separated list of integers in the document, -do like this: -</p> -<pre> -schema example { - document example { - field yearlist type string { # Comma-separated years - } - } - - field year type array<int> { # Search field using the yearlist value - indexing: input yearlist | split "," | attribute - } -} -</pre> - - - -<h2 id="modifying-schemas">Modifying schemas</h2> -<p> -This section describes how a schema in a live application can be modified—categories: -<ol> - <li><a href="#valid-changes-without-restart-or-re-feed"> - Valid changes without restart or re-feed</a></li> - <li><a href="#changes-that-require-restart-but-not-re-feed"> - Changes that require restart but not re-feed</a></li> - <li><a href="#changes-that-require-reindexing"> - Changes that require reindexing</a></li> - <li><a href="#changes-that-require-re-feed"> - Changes that require re-feed</a></li> -</ol> -<p> -When running <code>vespa prepare</code> on a new application package, -the changes in the schema files are compared with the files in the current active package. -If some of the changes require restart or re-feed, the output from <code>vespa prepare</code> -specifies which actions are needed. -</p> -{% include important.html content=" -For changes that are not covered below, -and no output is returned from <code>vespa prepare</code>, -the impact is undefined and in no way guaranteed to allow a system to stay live until re-feeding. -Changes not related to the schema are discussed -in <a href='/en/operations/self-managed/admin-procedures.html'>admin procedures</a>."%} - - -<h3 id="valid-changes-without-restart-or-re-feed">Valid changes without restart or re-feed</h3> -<p> -Procedure: -<ol> - <li>Run <code>vespa prepare</code> on the changed application</li> - <li>Run <code>vespa activate</code>. The changes will take effect immediately</li> -</ol> -Changes: -<table class="table"> - <thead> - <tr><th>Change</th><th>Description</th></tr> - </thead> - <tbody> - <tr><th>Add a new document field</th> - <td> - Add a new document field as index, attribute, summary or any combination of these. - Existing documents will implicitly get the new field with no content. - Documents fed after the change can specify the new field. - If the field has existed with the same type earlier, - then old content <em>may or may not</em> reappear - </td></tr> - - <tr><th>Remove a document field</th> - <td> - Existing documents will no longer see the removed field, - but the field data is not completely removed from the search node - </td></tr> - - <tr><th>Add or remove an existing document field from document summary</th> - <td> - <p id="add-or-remove-an-existing-document-field-from-document-summary"> - Add an existing field to summary or any number of summary classes, - and remove an existing field from summary or any number of summary classes. Example: - </p> -<pre> - document-summary short-summary { - summary artist {} - } -</pre> - <p> - A change adding an <a href="../../content/attributes.html">attribute</a> field with a new name to a summary class - using <a href="#source">source</a> does not require restart or re-feed: - </p> -<pre> - field artist type string { - indexing: summary | attribute - } - - document-summary rename-summary { - summary <span class="pre-hilite">artist_name</span> { - <span class="pre-hilite">source: artist</span> - } - } -</pre> - <p> - Also see <a href="#add-or-remove-a-new-non-attribute-document-field-from-document-summary">non-attribute</a> - fields. - </p> - </td></tr> - - <tr><th>Remove the attribute aspect from a field that is also an index field</th> - <td> - This is the only scenario of changing the attribute aspect of a document field that is allowed without restart - </td></tr> - - <tr><th>Add, change or remove field sets</th> - <td> - Change <a href="#fieldset">fieldsets</a> used to group fields together for searching - </td></tr> - - <tr><th>Change the alias or sorting attribute settings for an attribute field</th> - <td><!-- ToDo --> - </td></tr> - - <tr><th>Add, change or remove rank profiles</th> - <td><!-- ToDo --> - </td></tr> - - <tr><th>Change document field weights</th> - <td><!-- ToDo --> - </td></tr> - - <tr><th>Add, change or remove field aliases</th> - <td><!-- ToDo --> - </td></tr> - - - <tr><th>Add, change or remove rank settings for a field</th> - <td> - Exception: Changing <code>rank: filter</code> on an attribute field in mode <em>index</em> requires restart. - See details in <a href="#changes-that-require-restart-but-not-re-feed">next section</a> - </td></tr> - - <tr><th>Add or remove a schema</th> - <td> - Removing a schema definition file will make <a href="../../content/proton.html">proton</a> - drop all documents of that type, subsequently releasing memory and disk. - </td></tr> - </tbody> -</table> - - -<h3 id="changes-that-require-restart-but-not-re-feed">Changes that require restart but not re-feed</h3> -<p> -Procedure: -<ol> - <li>Run <code>vespa prepare</code> on the changed application. - Output specifies which restart actions are needed - <li>Run <code>vespa activate</code></li> - <li>Restart <code>services</code> on the services specified in the <code>prepare</code> output -</ol> -Changes: -<table class="table"> - <thead> - <tr><th>Change</th><th>Description</th></tr> - </thead> - <tbody> - <tr><th>Change the attribute aspect of a document field</th> - <td> - Add or remove a field as attribute. - When adding, the attribute is populated based on the field value in stored documents during restart. - When removing, the field value in stored documents is updated based on the content in the attribute during restart. - </td></tr> - - <tr><th>Change the attribute settings for an attribute field</th> - <td> - Change the following attribute settings: <code>fast-search</code>, <code>fast-access</code>, <code>fast-rank</code>, <code>paged</code>. - </td></tr> - - <tr><th>Change the rank filter setting for an attribute field</th> - <td> - Add or remove <code>rank: filter</code> on an attribute field. - </td></tr> - - <tr><th>Change the hnsw index settings for a tensor attribute field</th> - <td> - Adding or removing the <a href="#index-hnsw">hnsw index</a> on a tensor attribute field, - or changing the <code>distance-metric</code> or <code>max-links-per-node</code> requires a restart to rebuild the index. - Changing <code>neighbors-to-explore-at-insert</code> requires a restart, but does not rebuild the index. - </td></tr> - - <tr><th>Change the distance metric for a tensor attribute field</th> - <td> - Change, add, or remove the <a href="#distance-metric">distance metric</a> on a tensor attribute field. - If no distance metric is specified, <em>euclidean</em> is used as the default. - </td></tr> - - </tbody> -</table> -<p> -Example: Given a content cluster <em>mycluster</em> with mode <em>index</em>: -</p> -<pre> -schema test { - document test { - field f1 type string { indexing: summary } - } -} -</pre> -Then add field <code>f1</code> as an attribute: -<pre> -schema test { - document test { - field f1 type string { indexing: attribute | summary } - } -} -</pre> -The following is output from <code>vespa prepare</code> - -which restart actions are needed: -<pre> -WARNING: Change(s) between active and new application that require restart: -In cluster 'mycluster' of type 'search': - Restart services of type 'searchnode' because: - 1) Document type 'test': Field 'f1' changed: add attribute aspect -</pre> - - -<h3 id="changes-that-require-reindexing">Changes that require reindexing</h3> -<p> -All the changes listed below require <a href="../../operations/reindexing.html">reindexing</a> -of all documents. Unlike re-feed, which requires an external source of data, reindexing is done -using documents stored in Vespa, and is automatic (once triggered). It can also run concurrently -with feed and serving, but until reindexing is complete, affected fields will be empty -or have potentially wrong annotations not matching the query processing. Procedure: -</p> -<ol> - <li>Run <code>vespa prepare</code> on the changed application. - Output specifies which reindexing actions are needed - <li>Run <code>vespa activate</code></li> - <li><a href="../api/deploy-v2.html#reindex">Enable reindexing</a> for the indicated document types and clusters</li> - <li>Run <code>vespa prepare</code> and <code>vespa activate</code> again to start reindexing process</li> -</ol> -{% include note.html content='On <a href="../../operations/reindexing-cloud.html">Vespa Cloud</a> -reindexing is enabled and started automatically on deployment, and is controlled from the console. The procedure above applies to self-managed deployments.' %} -<p>Changes:</p> -<table class="table"> - <thead> - <tr><th>Change</th><th>Description</th></tr> - </thead> - <tbody> - <tr><th>Change index aspect of a document field</th> - <td> - This changes the document processing pipeline before documents arrive in the backend. - Only documents fed after index aspect was added - will have annotations and be present in the reverse index. - Only documents fed after index aspect was removed - will avoid disk bloat due to unneeded annotations. - </td></tr> - - <tr><th>Switch stemming/normalizing on or off</th> - <td> - <p> - This changes the document processing pipeline before documents - arrive in the backend, and what annotations are made for an indexed field. - </p> - {% include important.html content="If not re-feeding after such a change, serving works, - but recall is undefined as the index has been produced using a different setting - than the one used when doing stemming/normalizing of the query terms."%} - </td></tr> - - <tr><th>Add, change, or remove match settings for a field</th> - <td> - <p> - Example: Adding <code>match: word</code> to a field. - </p><p> - This changes the document processing pipeline before documents - arrive in the backend, and what annotations are made for an indexed field. - </p> - {% include important.html content="If not reindexing after such a change, serving works, - but recall is undefined as the index has been produced using one match mode - while run-time is using a different match mode."%} - </td></tr> - - <tr> - <th>Add or remove a new non-attribute document field from document summary</th> - <td> - <p id="add-or-remove-a-new-non-attribute-document-field-from-document-summary"> - A change adding an <a href="/en/basics/schemas.html#document-fields">index or summary</a> field - (without <a href="/en/content/attributes.html">attribute</a>) - with a new name to a summary class using <a href="#source">source</a> requires re-index: - </p> -<pre> - field artist type string { - indexing: summary | index - } - - document-summary rename-summary { - summary <span class="pre-hilite">artist_name</span> { - <span class="pre-hilite">source: artist</span> - } - } -</pre> - <p> - Also see <a href="#add-or-remove-an-existing-document-field-from-document-summary">attribute</a> fields. - </p> - </td> - </tr> - - </tbody> -</table> -Example: Given a content cluster <em>mycluster</em> with mode <em>index</em>: -<pre> -schema test { - document test { - field f1 type string { indexing: summary } - } -} -</pre> -Then add field <code>f1</code> as an index: -<pre> -schema test { - document test { - field f1 type string { indexing: index | summary } - } -} -</pre> -The following is output from <code>vespa prepare</code> - -which reindex actions are needed: -<pre> -WARNING: Change(s) between active and new application that require re-index: -Reindex document type 'test' in cluster 'mycluster' because: - 1) Document type 'test': Field 'f1' changed: add index aspect, indexing script: '{ input f1 | summary f1; }' -> '{ input f1 | tokenize normalize stem:"SHORTEST" | index f1 | summary f1; }' -</pre> - - -<h3 id="changes-that-require-re-feed">Changes that require re-feed</h3> -<p> -All the changes listed below require re-feeding of all documents. -Unless a change is listed in the above sections, treat it as if it were listed here. -Until re-feed is complete, affected fields will be empty -or have potentially wrong annotations not matching the query processing. Procedure: -</p> -<ol> - <li>Run <code>vespa prepare</code> on the changed application. - Output specifies which re-feed actions are needed - <li>Stop feeding, wait until done</li> - <li>Run <code>vespa activate</code></li> - <li>Re-feed all documents</li> -</ol> -Changes: -<table class="table"> - <thead> - <tr><th>Change</th><th>Description</th></tr> - </thead> - <tbody> - <tr> - <th>Change a document field's data type or collection type</th> - <td> - <p> - Existing documents will no longer have any content for this field. - To populate the field, re-feed the existing documents using the new type for this field. - There will be no automatic conversion from old to new field type. - </p> - {% include important.html content="If not re-feeding after such a change, serving works, - but searching this field will not give any results"%} - </td></tr> - - <tr><th>Change a tensor attribute's tensor type</th> - <td><!-- ToDo --> - </td></tr> - - </tbody> -</table> -Example: Given a content cluster <em>mycluster</em> with mode <em>index</em>: -<pre> -schema test { - document test { - field f1 type string { indexing: summary } - } -} -</pre> -Then change field <code>f1</code> to hold an int: -<pre> -schema test { - document test { - field f1 type int { indexing: summary } - } -} -</pre> -The following is output from <code>vespa prepare</code> - -which re-feed actions are needed: -<pre> -WARNING: Change(s) between active and new application that require re-feed: -Re-feed document type 'test' in cluster 'mycluster' because: - 1) Document type 'test': Field 'f1' changed: data type: 'string' -> 'int' -</pre> diff --git a/en/reference/schemas/schemas.mdx b/en/reference/schemas/schemas.mdx new file mode 100644 index 0000000000..6e8f55ada4 --- /dev/null +++ b/en/reference/schemas/schemas.mdx @@ -0,0 +1,3690 @@ +--- +title: "Schema reference" +--- + +This reference documents the syntax and content of schemas, document types, and fields. This is a reference, see [schemas](/en/basics/schemas) for an overview and examples. + +## Syntax + +Throughout this document, a string in square brackets represents some argument. The whole string, including the brackets, is replaced by a concrete string in a schema. + +Constructs in schemas have a regular syntax. Each element starts with the element _identifier_, possibly followed by the _name_ of this particular occurrence of the element, possibly followed by a space-separated list of interleaved _attribute names_ and _attribute values_, possibly followed by the _element body_. Thus, one will find elements of these varieties: + +```bash +[element-identifier] : [element-body] +[element-identifier] [element-name] : [element-body] +[element-identifier] [element-name] [attribute-name] [attribute-value] +[element-identifier] [element-name] [attribute-name] [attribute-value] { + [element-body] +} +``` + +One-line element values start with a colon and end with a newline. + +Multiline values (for fields supporting them) are any block of text enclosed in curly brackets. + +Comments may be inserted anywhere and start with a hash (#). + +Names are _identifiers_, they must match `["a"-"z","A"-"Z", "_"]["a"-"z","A"-"Z","0"-"9","_"]*`. + +A schema file is not sensitive to indentation. + +## Elements + +Elements and structure of a schema file: + +```yaml expandable +schema + document + struct + field + match + field + alias + attribute + distance-metric + bolding + dictionary + id + index + hnsw + indexing + linguistics + match + normalizing + query-command + rank + rank-type + sorting + stemming + struct-field + indexing + match + query-command + struct-field + … + summary + summary-to DEPRECATED + summary + summary-to DEPRECATED + weight + compression + index + field + fieldset + rank-profile + diversity + attribute + min-groups + match-phase + attribute + order + total-max-hits + max-hits + first-phase + total-keep-rank-count + keep-rank-count + rank-score-drop-limit + expression + second-phase + expression + rank-score-drop-limit + total-rerank-count + rerank-count + global-phase + expression + rerank-count + rank-score-drop-limit + function [name] + inputs + constants + onnx-model + significance + rank-properties + match-features + mutate + on-match + on-first-phase + on-second-phase + on-summary + summary-features + rank-features + ignore-default-rank-features + num-threads-per-search + num-search-partitions + min-hits-per-thread + termwise-limit + post-filter-threshold + approximate-threshold + filter-first-threshold + filter-first-exploration + exploration-slack + target-hits-max-adjustment-factor + filter-threshold + rank + filter-threshold + element-gap + rank-type + weakand + stopword-limit + adjust-target + allow-drop-all + rank-profile (inner) + constant + onnx-model + stemming + documentid + document-summary + summary + import field + raw-as-base64-in-summary +schema +``` + +## schema + +The root element of schemas. A schema describes a type of data and what we should compute over it. A schema must be defined in a file named `[schema-name].sd`. + +```bash +schema [name] inherits [name] { + [body] +} +``` + +The `inherits` attribute is optional. If a schema is inherited, this schema will include all the constructs of it as if they were defined in this schema (except the parent document type). The document type in this must declare that it inherits the document type of the parent schema. + +The body is mandatory and may contain: + +<table className="vespa-table"> + <thead> + <tr> + <th>Name</th> + <th>Occurrence</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td><a href="#document" aria-label="document">document</a></td> + <td>One</td> + <td>A document type defined in this schema</td> + </tr> + <tr> + <td><a href="#field" aria-label="field">field</a></td> + <td>Zero to many</td> + <td>A field not contained in the document. Use _synthetic fields_ (outside <a href="#document" aria-label="document">document</a>) to derive new field values to be placed in the indexing structure from document fields. Find examples in <a href="/en/operations/reindexing#use-cases" aria-label="reindexing">reindexing</a>.</td> + </tr> + <tr> + <td><a href="#fieldset" aria-label="fieldset">fieldset</a></td> + <td>Zero to many</td> + <td>Group document fields together for searching</td> + </tr> + <tr> + <td><a href="#rank-profile" aria-label="rank-profile">rank-profile</a></td> + <td>Zero to many</td> + <td>A bundle of ranking functions and settings, selectable in a query.</td> + </tr> + <tr> + <td><a href="#constant" aria-label="constant">constant</a></td> + <td>Zero to many</td> + <td>A constant tensor located in a file used for ranking</td> + </tr> + <tr> + <td><a href="#onnx-model" aria-label="onnx-model">onnx-model</a></td> + <td>Zero to many</td> + <td>An ONNX model located in the application package used for ranking</td> + </tr> + <tr> + <td><a href="#stemming" aria-label="stemming">stemming</a></td> + <td>Zero or one</td> + <td>The default stemming setting.</td> + </tr> + <tr> + <td><a href="#raw-as-base64-in-summary" aria-label="raw-as-base64-in-summary">raw-as-base64-in-summary</a></td> + <td>Zero or one</td> + <td>Base64 encode raw fields in summary rather than using an escaped string. The default is true.</td> + </tr> + <tr> + <td><a href="#documentid" aria-label="documentid">documentid</a></td> + <td>Zero or one</td> + <td>Whether document IDs are stored on disk only or made an attribute.</td> + </tr> + <tr> + <td><a href="#document-summary" aria-label="document-summary">document-summary</a></td> + <td>Zero to many</td> + <td>An explicitly defined document summary</td> + </tr> + <tr> + <td><a href="#import-field" aria-label="import field">import field</a></td> + <td>Zero to many</td> + <td>Import a field value from a global document</td> + </tr> + </tbody> +</table> + +## document + +Contained in [schema](#schema) and describes a document type. This can also be the root of the schema, if the document is not to be queried directly. + +```bash +document [name] inherits [name-list] { + [body] +} +``` + +The document name is optional; it defaults to the containing `schema` element's name. If there is no containing `schema` element, the document name is required. If the document with a name is defined inside a schema, the document name must match the `schema` element's name. The reference to _document type_ in the documentation refers to the document name defined here. + +The `inherits` attribute is optional and has as value a comma-separated list of names of other document types. A document type may inherit the fields of one or more other document types, see [document inheritance](/en/schemas/inheritance-in-schemas) for examples. If no document types are explicitly inherited, the document inherits the generic `document` type. + +The body of a document type is optional and may contain: + +<table className="vespa-table"> + <thead> + <tr> + <th>Name</th> + <th>Occurrence</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td><a href="#struct" aria-label="struct">struct</a></td> + <td>Zero to many</td> + <td>A struct type definition for this document.</td> + </tr> + <tr> + <td><a href="#field" aria-label="field">field</a></td> + <td>Zero to many</td> + <td>A field of this document.</td> + </tr> + <tr> + <td><a href="#compression" aria-label="compression">compression</a></td> + <td>Zero to one</td> + <td>Specifies compression options for documents of this document type in storage.</td> + </tr> + </tbody> +</table> + +## struct + +Contained in [document](#document). Defines a composite type. A struct consists of zero or more fields that the user can access together as one. The struct has to be defined before it is used as a type in a field specification. + +```bash +struct [name] { + [body] +} +``` + +The body of a struct is optional and may contain: + +<table className="vespa-table"> + <thead> + <tr> + <th>Name</th> + <th>Occurrence</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td><a href="#field" aria-label="field">field</a></td> + <td>Zero to many</td> + <td>A field of this struct.</td> + </tr> + </tbody> +</table> + +## field + +Contained in [schema](#schema), [document](#document), or [struct](#struct). Defines a named value with a type and (optionally) how this field should be stored, indexed, searched, presented, and how it should influence ranking. + +```bash +field [name] type [type-name] { + [body] +} +``` +Do not use names that are used for other purposes in the indexing language or other places in the schema file. Reserved names are: +- attribute +- body +- case +- context +- documentid +- else +- header +- hit +- host +- if +- index +- position +- reference +- relevancy +- sddocname +- summary +- switch +- tokenize + +Other names not to use include any words that start with a number or include special characters. + +The _type_ attribute is mandatory - supported types: + +<table className="vespa-table"> + <thead> + <tr> + <th>Field type</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td colSpan={2}>array<type></td> + </tr> + </tbody> +</table> + +For single-value (primitive) types, use array\<type\> to create an array field of the element type: + +<table className="vespa-table"> + <tbody> + <tr> + <td>Index</td> + <td>Each element is indexed separately</td> + </tr> + <tr> + <td>Attribute</td> + <td>Added as an array attribute</td> + </tr> + <tr> + <td>Summary</td> + <td>Added as an array summary field</td> + </tr> + </tbody> +</table> + +Also used to create an array field of the given [struct type](#struct). The struct type must be defined separately. Example: + +```js +struct person { + field first_name type string {} + field last_name type string {} +} + +field people type array<person> { + indexing: summary + summary: matched-elements-only + struct-field first_name { + indexing: attribute + attribute: fast-search + } +} +``` + +The entire _people_ field is part of the document summary. The [struct field](#struct-field) _first\_name_ is defined as an _attribute_ for searching, with [fast-search](/en/content/attributes#fast-search). A subset, or all, of the struct fields can be defined as attributes. + +Use the [sameElement](/en/reference/querying/yql#sameelement) operator to ensure matches in the same struct field instance. + +Use [matched-elements-only](#matched-elements-only) to reduce the amount of data that is returned in the document summary. + +<Warning> + **Important:** + +`key` and `value` are reserved words in an array\<struct\>, as these are used to implement [map](#map). Do not use these as struct-field names. +</Warning> + +Restrictions: + +- Array of struct types does not support [ranking features](/en/basics/ranking) and can only be used for matching and filtering. +- All struct arrays can be fed, retrieved, and used in document summaries. +- Some parts of struct arrays can be searched in [indexed search mode](/en/reference/applications/services/content#document), while all parts of struct arrays can be searched in [streaming search](/en/performance/streaming-search). See below for supported cases. + +<table className="vespa-table"> + <tbody> + <tr> + <td>Index</td> + <td>Only supported in <a href="/en/performance/streaming-search#differences-in-streaming-search" aria-label="streaming search">streaming search</a>. Set this on the top-level struct array field to make all parts searchable.</td> + </tr> + <tr> + <td>Attribute</td> + <td>Only supported for <a href="#struct-field" aria-label="struct fields">struct fields</a> that have primitive types (bool, string, int, long, byte, float, double). Any struct field must be defined as an attribute to be used for searching. The struct type can still contain fields of non-primitive types, as long as these are not defined as attributes.</td> + </tr> + <tr> + <td>Summary</td> + <td>Added as an array summary field</td> + </tr> + </tbody> +</table> + + | +<table className="vespa-table"> + <tbody> + <tr> + <td>bool</td> + </tr> + </tbody> +</table> + +Use for boolean values. + +``` +field alive type bool { + indexing: summary | attribute +} +``` + +<table className="vespa-table"> + <tbody> + <tr> + <td>Index</td> + <td>Not supported</td> + </tr> + <tr> + <td>Attribute</td> + <td>Added as a boolean</td> + </tr> + <tr> + <td>Summary</td> + <td>Added as a boolean value (<code>{`true`}</code> or <code>{`false`}</code>)</td> + </tr> + </tbody> +</table> + +<Warning>**Important:** Defaults to `false` if not specified. </Warning> + | +<table className="vespa-table"> + <tbody> + <tr> + <td>byte</td> + </tr> + </tbody> +</table> + +Use for single 8-bit numbers. + +```js +field smallnumber type byte { + indexing: summary | attribute +} +``` + +<table className="vespa-table"> + <tbody> + <tr> + <td>Index</td> + <td>Not supported. An attribute will automatically be used instead</td> + </tr> + <tr> + <td>Attribute</td> + <td>Added as a byte which supports range searches</td> + </tr> + <tr> + <td>Summary</td> + <td>Added as a byte</td> + </tr> + </tbody> +</table> + + | +<table className="vespa-table"> + <tbody> + <tr> + <td>double</td> + </tr> + </tbody> +</table> + +Use for high precision floating point numbers (64-bit IEEE 754 double). + +``` +field mydouble type double { + indexing: summary | attribute +} +``` + +<table className="vespa-table"> + <tbody> + <tr> + <td>Index</td> + <td>Not supported. An attribute will automatically be used instead</td> + </tr> + <tr> + <td>Attribute</td> + <td>Added as a 64-bit IEEE 754 double which supports range searches</td> + </tr> + <tr> + <td>Summary</td> + <td>Added as a 64-bit IEEE 754 double</td> + </tr> + </tbody> +</table> + + | +<table className="vespa-table"> + <tbody> + <tr> + <td>float</td> + </tr> + </tbody> +</table> + +Use for floating point numbers (32-bit IEEE 754 float). + +``` +field myfloat type float { + indexing: summary | attribute +} +``` + +<table className="vespa-table"> + <tbody> + <tr> + <td>Index</td> + <td>Not supported. An attribute will automatically be used instead</td> + </tr> + <tr> + <td>Attribute</td> + <td>Added as a 32-bit IEEE 754 float which supports range searches</td> + </tr> + <tr> + <td>Summary</td> + <td>Added as a 32-bit IEEE 754 float</td> + </tr> + </tbody> +</table> + + | +<table className="vespa-table"> + <tbody> + <tr> + <td>int</td> + </tr> + </tbody> +</table> + +Use for single 32-bit integers. + +``` +field release_year type int { + indexing: summary | attribute +} +``` + +<table className="vespa-table"> + <tbody> + <tr> + <td>Index</td> + <td>Not supported. An attribute will automatically be used instead</td> + </tr> + <tr> + <td>Attribute</td> + <td>Becomes integer attributes, which supports range grouping and range searches</td> + </tr> + <tr> + <td>Summary</td> + <td>Added as a 32-bit integer</td> + </tr> + </tbody> +</table> + + | +<table className="vespa-table"> + <tbody> + <tr> + <td>long</td> + </tr> + </tbody> +</table> + +Use for single 64-bit integers. + +``` +field bignumber type long { + indexing: summary | attribute +} +``` + +<table className="vespa-table"> + <tbody> + <tr> + <td>Index</td> + <td>Not supported. An attribute will automatically be used instead</td> + </tr> + <tr> + <td>Attribute</td> + <td>Becomes a 64-bit integer attribute, which supports range grouping and range searches</td> + </tr> + <tr> + <td>Summary</td> + <td>Added as a 64-bit integer</td> + </tr> + </tbody> +</table> + + | +<table className="vespa-table"> + <tbody> + <tr> + <td>map<key-type,value-type></td> + </tr> + </tbody> +</table> + +Use to create a map where each unique key is mapped to a single value. Any primitive type can be used as _key-type_ and any primitive type or Vespa struct type as _value-type_. Example of a map of primitive types, where the _key_ and _value_ fields are specified as _attributes_: + +``` +field my_map type map<string, int> { + indexing: summary + struct-field key { indexing: attribute } + struct-field value { indexing: attribute } +} +``` + +Note that a Vespa map entry is handled as a _struct_ with a _key_ and _value_ field with _key-type_ and _value-type_ as types. This explains the _struct-field_ syntax above. The full _my\_map_ field is configured into the document summary. + +A more complex example is a map of struct: + +``` +struct person { + field first_name type string {} + field last_name type string {} + field age type int {} +} + +field identities type map<string, person> { + indexing: summary + summary: matched-elements-only + struct-field key { + indexing: attribute + attribute: fast-search + } + struct-field value.first_name { + indexing: attribute + attribute: fast-search + } + struct-field value.last_name { + indexing: attribute + attribute: fast-search + } +} +``` + +This example illustrates that the struct elements are configured individually - there is no field configuration for _age_ - one can define a subset of the struct fields as attributes. + +Here, _key_, _value.first\_name_ and _value.last\_name_ are defined as attributes. This makes them available for searching and [grouping](/en/querying/grouping#grouping-over-a-map-field). A common use case is requiring matches in the same map entry (e.g., match both first and last name for the same person), see the [sameElement](/en/reference/querying/yql#sameelement) operator for how to implement this. Use [matched-elements-only](#matched-elements-only) to reduce the amount of data in the document summary. + +[fast-search](/en/performance/feature-tuning#when-to-use-fast-search-for-attribute-fields) is used to make query access faster by creating an index structure for lookups. + +As an alternative to a map, an [array\<struct\>](#array) can contain the same element multiple times and maintains order. + +Restrictions: + +- Map of struct or primitive types do not support [ranking features](/en/basics/ranking) and can only be used for matching and filtering. +- All map types can be fed, retrieved, and used in document summaries. +- Some map types can be searched in [indexed search mode](/en/reference/applications/services/content#document), while all map types can be searched in [streaming search](/en/performance/streaming-search). See below for supported cases: + +<table className="vespa-table"> + <tbody> + <tr> + <td>Index</td> + <td>Only supported in <a href="/en/performance/streaming-search#differences-in-streaming-search" aria-label="streaming search">streaming search</a>. Set this on the top-level map field to make all struct fields in the map field searchable.</td> + </tr> + <tr> + <td>Attribute</td> + <td>Only supported for <a href="#struct-field" aria-label="struct fields">struct fields</a> where _value-type_ is either a primitive type (bool, string, int, long, byte, float, double) or a <a href="#struct" aria-label="struct type">struct type</a> with fields of primitive types. Any struct field must be defined as an attribute to be used for searching. The _value-type_ struct can still contain fields of non-primitive types, as long as these are not defined as attributes.</td> + </tr> + <tr> + <td>Summary</td> + <td>Added as a map.</td> + </tr> + </tbody> +</table> + + | +<table className="vespa-table"> + <tbody> + <tr> + <td>position</td> + </tr> + </tbody> +</table> + +Used to filter and/or rank documents by distance to a position in the query, see [Geo search](/en/querying/geo-search). + +``` +field location type position { + indexing: attribute +} +``` + +<table className="vespa-table"> + <tbody> + <tr> + <td>Index</td> + <td>Not supported</td> + </tr> + <tr> + <td>Attribute</td> + <td>Added as an interleaved 64-bit integer (see <a href="https://en.wikipedia.org/wiki/Z-order_curve" aria-label="Z-order curve">Z-order curve</a>) - queries are implemented by doing a set of range searches in the attribute. This attribute has <a href="/en/content/attributes#fast-search" aria-label="fast-search">fast-search</a> set implicitly</td> + </tr> + <tr> + <td>Summary</td> + <td>Refer to the <a href="/en/reference/schemas/document-json-format#position" aria-label="reference">reference</a></td> + </tr> + </tbody> +</table> + + | +<table className="vespa-table"> + <tbody> + <tr> + <td>predicate</td> + </tr> + </tbody> +</table> + +Use to match queries to a set of boolean constraints. See [querying predicate fields.](/en/schemas/predicate-fields.#queries) Predicate fields are not supported in [streaming search](/en/performance/streaming-search#differences-in-streaming-search). + +``` +field predicate_field type predicate { + indexing: attribute + index { + arity: 2 # mandatory + lower-bound: 3 + upper-bound: 200 + dense-posting-list-threshold: 0.25 + } +} +``` + +<table className="vespa-table"> + <tbody> + <tr> + <td>Index</td> + <td>Not supported</td> + </tr> + <tr> + <td>Attribute</td> + <td>Indexed in-memory in a variable-size binary format that is optimized for application during query evaluation</td> + </tr> + <tr> + <td>Summary</td> + <td>Added as-is</td> + </tr> + </tbody> +</table> + + | +<table className="vespa-table"> + <tbody> + <tr> + <td>raw</td> + </tr> + </tbody> +</table> + +Use for binary data + +``` +field rawfield type raw { + indexing: summary | attribute +} +``` + +<table className="vespa-table"> + <tbody> + <tr> + <td>Index</td> + <td>Not supported</td> + </tr> + <tr> + <td>Attribute</td> + <td>Added as raw data. Not searchable.</td> + </tr> + <tr> + <td>Summary</td> + <td>Added as raw data. Outputted as a base64-encoded string. See <a href="/en/reference/schemas/document-json-format#raw" aria-label="JSON feed format">JSON feed format</a> for details.</td> + </tr> + </tbody> +</table> + + | +<table className="vespa-table"> + <tbody> + <tr> + <td>reference<document-type></td> + </tr> + </tbody> +</table> + +A _reference\<document-type\>_ field is a reference to an instance of a document-type - i.e., a foreign key. Reference fields are not supported in [streaming search](/en/performance/streaming-search#differences-in-streaming-search). + +``` +field artist_ref type reference<artist> { + indexing: attribute +} +``` + The reference is the [document ID](/en/schemas/documents) of the document-type instance. References are used to join documents in a [parent-child relationship](/en/schemas/parent-child). A reference can only be made to [global](/en/reference/applications/services/content#document) documents. The following types of references are not supported: +- Self-reference +- Cyclic reference: If document type _foo_ has a reference to _bar_, then _bar_ cannot have a reference to _foo_ + + A reference attribute field can be searched using the document ID of the parent document-type instance as query term. Note that this will be a linear scan as [fast-search](#attribute) is not supported. + +<table className="vespa-table"> + <tbody> + <tr> + <td>Index</td> + <td>Invalid - deployment will fail</td> + </tr> + <tr> + <td>Attribute</td> + <td>As <a href="#string" aria-label="string">string</a> - a reference must be an attribute. Can be an empty string or point to a non-existent document. Memory usage is about 33 bytes per parent document. This is composed of 24 bytes used in a reference store, with a btree structure on top of that which requires 5 bytes on average (depends on lid compaction). In addition 4 bytes on average for a reference from child document to the parent document (depends on lid compaction). In total about 33 bytes.</td> + </tr> + <tr> + <td>Summary</td> + <td>As <a href="#string" aria-label="string">string</a></td> + </tr> + </tbody> +</table> + + | +<table className="vespa-table"> + <tbody> + <tr> + <td>string</td> + </tr> + </tbody> +</table> + +Use for a text field of any length. String fields may only contain _text characters_, as defined by `isTextCharacter` in [com.yahoo.text.Text](https://github.com/vespa-engine/vespa/blob/master/vespajlib/src/main/java/com/yahoo/text/Text.java) + +``` +field surname type string { + indexing: summary | index +} +``` + +<table className="vespa-table"> + <tbody> + <tr> + <td>Index</td> + <td>Refer to <a href="/en/linguistics/linguistics" aria-label="linguistics">linguistics</a> for details on normalization, tokenization and stemming.</td> + </tr> + <tr> + <td>Attribute</td> + <td>Added as-is. <a href="#match" aria-label="match">match</a> exact or prefix is supported types of searches in string attributes. Searches are however case-insensitive. A query for <code>{`BritneY.spears`}</code> will match a document containing <code>{`BrItNeY.SpEars`}</code></td> + </tr> + <tr> + <td>Summary</td> + <td>Added as-is</td> + </tr> + </tbody> +</table> + + | +<table className="vespa-table"> + <tbody> + <tr> + <td>struct</td> + </tr> + </tbody> +</table> + +Use to define a field with a struct datatype. Create a [struct type](#struct) inside the document definition and declare the struct field in a document or struct using the struct type name as the field type: + +``` +struct person { + field first_name type string {} + field last_name type string {} +} +field my_person type person { + indexing: summary +} +``` + Restrictions: +- Struct fields can **not** be searched in indexed search mode (but [array of struct](#array) and [map type](#map) are searchable, with some restrictions). +- Struct fields can be fed, retrieved, and used in document summaries. + +<table className="vespa-table"> + <tbody> + <tr> + <td>Index</td> + <td>Only supported in <a href="/en/performance/streaming-search#differences-in-streaming-search" aria-label="streaming search">streaming search</a>. Set this on the top-level field to make all parts searchable.</td> + </tr> + <tr> + <td>Attribute</td> + <td>Not supported.</td> + </tr> + <tr> + <td>Summary</td> + <td>Added as a struct.</td> + </tr> + </tbody> +</table> + + | +<table className="vespa-table"> + <tbody> + <tr> + <td>tensor(dimension-1,...,dimension-N)</td> + </tr> + </tbody> +</table> + +Use to create a tensor field with the given [tensor type spec](/en/reference/ranking/tensor#tensor-type-spec) that can be used for [ranking](/en/basics/ranking) and [nearest neighbor search](/en/querying/nearest-neighbor-search). A tensor field is otherwise not searchable. + +See [tensor evaluation reference](/en/reference/ranking/tensor) for definition, the [tensor user guide](/en/ranking/tensor-user-guide) and the [JSON feed format](/en/reference/schemas/document-json-format#tensor). + +``` +field tensorfield type tensor<float>(x{},y{}) { + indexing: attribute | summary +} + +field tensorfield type tensor<float>(x[2],y[2]) { + indexing: attribute | summary +} +``` + +<table className="vespa-table"> + <tbody> + <tr> + <td>Index</td> + <td>Supported for tensor types with:</td> + </tr> + </tbody> +</table> +- One indexed dimension - single vector per document +- One or more mapped dimensions and one indexed dimension - multiple vectors per document + + See [approximate nearest neighbor search](/en/querying/approximate-nn-hnsw). | +<table className="vespa-table"> + <tbody> + <tr> + <td>Attribute</td> + <td>Added as-is in an attribute to be used for ranking and nearest neighbor search.</td> + </tr> + <tr> + <td>Summary</td> + <td>Added as-is.</td> + </tr> + </tbody> +</table> + + | +<table className="vespa-table"> + <tbody> + <tr> + <td>uri</td> + </tr> + </tbody> +</table> + +Use for URL type matching. URI fields are not supported in [streaming search](/en/performance/streaming-search#differences-in-streaming-search). + +<table className="vespa-table"> + <tbody> + <tr> + <td>Index</td> + </tr> + </tbody> +</table> + +The URL is split into its different components, which are indexed separately. Note that only URLs can be indexed this way, not other URIs. The different components are as defined by the HTTP standard: Scheme, hostname, port, path, query, and fragment. Example: + +``` +http://mysite.mydomain.com:8080/path/shop?d=hab&id=1804905709&cat=100#frag1 +``` + +<table className="vespa-table"> + <tbody> + <tr> + <td>scheme</td> + <td>http</td> + </tr> + <tr> + <td>hostname</td> + <td>mysite.mydomain.com (indexed as "mysite", "mydomain" and "com")</td> + </tr> + <tr> + <td>port</td> + <td>8080 (note that port numbers 80 and 443 are not indexed, as they are the normal port numbers)</td> + </tr> + <tr> + <td>path</td> + <td>/path/shop (indexed as "path" and "shop")</td> + </tr> + <tr> + <td>query</td> + <td>d=hab&id=1804905709&cat=100 (indexed as "d", "hab", "id", "1804905709", "cat" and "100")</td> + </tr> + <tr> + <td>fragment</td> + <td>frag1</td> + </tr> + </tbody> +</table> + + The syntax for searching these different components is: +``` +[field-name].[component-name]:term +``` + Example: In a URI field `sourceurl`, search for documents from slashdot: +``` +query=sourceurl.hostname:slashdot +``` + +URL hostnames also support _anchored searching_, see [search in URL fields](/en/reference/querying/yql#uri). + +It is not possible to index uri-typed fields into a common index, i.e., it has to be indexed separately from other fields. If you need to combine URLs with other fields, you could store it in a string-field instead, but then you can not search in the different parts of the URL (scheme, hostname, port, path, query, and fragment). + +**Aliasing** also works differently for URL fields - you are allowed to create aliases both to the index (as usual) and to the components of it. Use + +``` +alias [component]: [alias] +``` + to create an alias for a component. For example, given this field: +``` +field surl type uri { + indexing: summary | index + alias: url + alias hostname: site +} +``` + +a search in "surl" and "url" will search in the entire url, while "surl.hostname" or "site" will search the hostname. + + | +<table className="vespa-table"> + <tbody> + <tr> + <td>Attribute</td> + <td>Not allowed</td> + </tr> + <tr> + <td>Summary</td> + <td>Added as-is as a string</td> + </tr> + </tbody> +</table> + + | +<table className="vespa-table"> + <tbody> + <tr> + <td>weightedset<element-type></td> + </tr> + </tbody> +</table> + +Use to create a multivalue field of the element type, where each element is assigned a signed 32-bit integer weight. + +``` +field tag type weightedset<string> { + indexing: attribute | summary +} +``` + +The element type can be one of the following primitive types: byte, int, long, and string. + +To access a weighted set in ranking when using `attribute`, see [attribute the match features](/en/reference/ranking/rank-features#attribute-match-features-not-normalized), or convert the weighted set to a tensor using the tensorFromWeightedSet(field, dimensionName) feature. + +To access a weighted set in ranking when using `index`, see [ranking features for indexed multivalued fields](/en/reference/ranking/rank-features#features-for-indexed-multivalue-string-fields). Note that when using `index` with weightedset, queries are matching across elements in the set. + +It is possible to specify that a new key should be created if it does not exist before the update, and that it should be removed if the weight is set to zero - see the [reference](#weightedset-properties). + +The weightedset field does not support filtering on weight. If you need that use the [map](#map) type and [sameElement](/en/reference/querying/yql#sameelement) query operator - see [this example](/en/querying/query-language#map). + +<table className="vespa-table"> + <tbody> + <tr> + <td>Index</td> + <td>Each token present in the field is indexed separately. Information indexed includes element number, element weight, and a list of token occurrence positions for each element in which the token is present</td> + </tr> + <tr> + <td>Attribute</td> + <td>Added as a multivalue weighted attribute</td> + </tr> + <tr> + <td>Summary</td> + <td>Added as a multivalue summary field if this is an attribute</td> + </tr> + </tbody> +</table> + + | + +The body of a field is optional for [schema](#schema), [document](#document) and [struct](#struct). It may contain the following elements: + +<table className="vespa-table"> + <thead> + <tr> + <th>Name</th> + <th>Occurrence</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>alias</td> + <td>Zero to many</td> + <td>Make an index or attribute available in queries under an additional name. This has minimal performance impact and can safely be added to running applications. Example: <br/><pre><code>{`field artist type string { alias: artist_name }`}</code></pre> <code>{`alias`}</code> works for primitive types, i.e, types you can set <code>{`indexing`}</code> on like <code>{`string`}</code>. Composite types are not supported, the below will throw an error when deploying: <br/><pre><code>{`document ppl { struct person { field first_name type string {} field last_name type string {} } field p type array<person> { struct-field first_name { indexing: attribute } struct-field last_name { indexing: attribute } alias: members } }`}</code></pre></td> + </tr> + <tr> + <td><a href="#attribute" aria-label="attribute">attribute</a></td> + <td>Zero to many</td> + <td>Specify an attribute setting.</td> + </tr> + <tr> + <td><a href="#bolding" aria-label="bolding">bolding</a></td> + <td>Zero to one</td> + <td>Specifies whether the content of this field should be bolded. Only supported for <a href="#indexing-index" aria-label="index">index</a> fields of type string or array<string>.</td> + </tr> + <tr> + <td><a href="#id" aria-label="id">id</a></td> + <td>Zero to one</td> + <td>Explicitly decide the numerical id of this field. Is normally not necessary, but can be used to save some disk space.</td> + </tr> + <tr> + <td><a href="#index" aria-label="index">index</a></td> + <td>Zero to many</td> + <td>Specify a parameter of an index.</td> + </tr> + <tr> + <td><a href="#indexing" aria-label="indexing">indexing</a></td> + <td>Zero to one</td> + <td>The indexing statements used to create index structure additions from this field.</td> + </tr> + <tr> + <td><a href="#match" aria-label="match">match</a></td> + <td>Zero to one</td> + <td>Set the matching type to use for this field.</td> + </tr> + <tr> + <td><a href="#normalizing" aria-label="normalizing">normalizing</a></td> + <td>Zero or one</td> + <td>Specifies the kind of text normalizing to do on a string field.</td> + </tr> + <tr> + <td><a href="#query-command" aria-label="query-command">query-command</a></td> + <td>Zero to many</td> + <td>Specifies a command which can be received by a plugin searcher in the Search Container.</td> + </tr> + <tr> + <td><a href="#rank" aria-label="rank">rank</a></td> + <td>Zero or one</td> + <td>Specify if the field is used for ranking.</td> + </tr> + <tr> + <td><a href="#rank-type" aria-label="rank-type">rank-type</a></td> + <td>Zero to one</td> + <td>Selects the set of low-level rank settings to be used for this field when using default <code>{`nativeRank`}</code>.</td> + </tr> + <tr> + <td><a href="#sorting" aria-label="sorting">sorting</a></td> + <td>Zero or one</td> + <td>The sort specification for this field.</td> + </tr> + <tr> + <td><a href="#stemming" aria-label="stemming">stemming</a></td> + <td>Zero or one</td> + <td>Specifies stemming options to use for this field.</td> + </tr> + <tr> + <td><a href="#struct-field" aria-label="struct-field">struct-field</a></td> + <td>Zero to many</td> + <td>A subfield of a field of type struct. The struct must have been defined to contain this subfield in the struct definition. If you want the subfield to be handled differently from the rest of the struct, you may specify it within the body of the struct-field.</td> + </tr> + <tr> + <td><a href="#summary" aria-label="summary">summary</a></td> + <td>Zero to many</td> + <td>Sets a summary setting of this field, set to <code>{`dynamic`}</code> to make a dynamic summary.</td> + </tr> + <tr> + <td><a href="#summary-to" aria-label="summary-to">summary-to</a></td> + <td>Zero to one</td> + <td><Warning>**Deprecated:** Use <a href="#document-summary" aria-label="document-summary">document-summary</a> instead.</Warning> The list of document summary names this should be included in.</td> + </tr> + <tr> + <td><a href="#weight" aria-label="weight">weight</a></td> + <td>Zero to one</td> + <td>The importance of a field when searching multiple fields and using <code>{`nativeRank`}</code>.</td> + </tr> + <tr> + <td><a href="#weightedset-properties" aria-label="weightedset">weightedset</a></td> + <td>Zero to one</td> + <td>Properties of a weightedset <a href="#weightedset" aria-label="weightedset<element-type>">weightedset<element-type></a></td> + </tr> + </tbody> +</table> + +Fields can not have default values. See the [document guide](/en/schemas/documents#fields) for how to auto-set field values. + +It is not possible to query for fields without value (i.e. query for NULL) - see the [query language reference](/en/reference/querying/yql). Fields without value are not returned in [query results](/en/reference/querying/default-result-format). + +Fields can be declared outside the document block in the schema. These fields are not part of the document type but behave like regular fields for queries. Since they are not part of the document, they cannot be written directly, but instead take their values from document fields, using the `input` expression: `indexing: input my_document_field | embed | summary | index` + +This is useful e.g., to index a field in multiple ways, or to change the field value, something which is not allowed with document fields. When the document field(s) used as input are updated, these fields are updated with them. + +## struct-field + +Contained in [field](#field) or [struct-field](#struct-field). Defines how this struct field (a subfield of a struct) should be stored, indexed, searched, presented, and how it should influence ranking. The field in which this struct field is contained must be of type struct or a collection of type struct: + +```bash +struct-field [name] { + [body] +} +``` + +The body of a struct field is optional and may contain the following elements: + +<table className="vespa-table"> + <thead> + <tr> + <th>Name</th> + <th>Occurrence</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td><a href="#indexing" aria-label="indexing">indexing</a></td> + <td>Zero to one</td> + <td>The indexing statements used to create index structure additions from this field. For indexed search only <code>{`attribute`}</code> is supported, which makes the struct field a searchable in-memory attribute that can also be used for e.g. grouping and ranking. For <a href="/en/performance/streaming-search" aria-label="streaming search">streaming search</a><code>{`index`}</code> and <code>{`summary`}</code> are supported in addition.</td> + </tr> + <tr> + <td><a href="#attribute" aria-label="attribute">attribute</a></td> + <td>Zero to many</td> + <td>Specifies an attribute setting. For example <code>{`attribute:fast-search`}</code>.</td> + </tr> + <tr> + <td><a href="#rank" aria-label="rank">rank</a></td> + <td>Zero to one</td> + <td>Specifies <a href="#rank" aria-label="rank">rank</a> settings</td> + </tr> + <tr> + <td><a href="#match" aria-label="match">match</a></td> + <td>Zero to one</td> + <td>Specifies <a href="#match" aria-label="match">match</a> settings</td> + </tr> + </tbody> +</table> + +If this struct field is of type struct (i.e., a nested struct), only [indexing:summary](#indexing) may be specified. See [array\<type\>](#array) for example use. + +## fieldset + +Contained in [schema](#schema). + +<Info> + **Note:** this is not related to the [Document fieldset](/en/schemas/documents#fieldsets). Also see the [FAQ](/en/learn/faq#must-all-fields-in-a-fieldset-have-compatible-type-and-matching-settings) for a discussion of what happens when using different types/match settings. +</Info> + +A fieldset groups fields together for searching: + +```js +fieldset myfieldset { + fields: a,b,c +} +``` + +Create a fieldset named `default` to be used as the default (i.e., when not specified in the query): + +```js +fieldset default { + fields: a,b,c +} +``` + +See [example queries](/en/querying/query-api#fieldset). + +The fields in the fieldset should be as similar as possible in terms of indexing clause and [match mode](#match). If they are not, test the application thoroughly. Having different match modes for the fields in the fieldset generates a warning during application deployment. If specific match settings for the fieldset are needed, such as _exact_, specify it using _match_: + +```js +fieldset myfieldset { + fields: a,b,c + match { + exact + } +} +``` + +Use [query-commands](#query-command) in the field set to set search settings. Example: + +```js +fieldset myfieldset { + fields: a,b,c + query-command:"exact @@" +} +``` + +Adding a fieldset will not create extra index structures in memory / on disk; it is just a mapping. + +Note that document frequency is aggregated across all member fields when matching a fieldset, which affects [BM25](/en/ranking/bm25) and significance values, and can cause [weakAnd](/en/ranking/wand) to prune matches for common terms like _"the"_ when they are frequent in one member field but rare in another. See the [FAQ](/en/learn/faq#why-can-common-words-like-the-hurt-recall-and-collapse-significance-across-a-fieldset) for details and the per-field workaround. + +## compression + +<Warning> + **Deprecated:** see [deprecations](/en/reference/release-notes/vespa8#compression). +</Warning> + +Contained in [document](#document). If a compression level is set within this element, **lz4** compression is enabled for whole documents. + +```js +compression { + [body] +} +``` +The body of a compression specification is optional and may contain: + +<table className="vespa-table"> + <thead> + <tr> + <th>Name</th> + <th>Occurrence</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>type</td> + <td>Zero to one</td> + <td>**LZ4** is the only valid compression method.</td> + </tr> + <tr> + <td>level</td> + <td>Zero to one</td> + <td>Enable compression. LZ4 is linear and 9 means HC(high compression).</td> + </tr> + </tbody> +</table> + +<table className="vespa-table"> + <tbody> + <tr> + <td>threshold</td> + <td>Zero to one</td> + <td>A percentage (multiplied by 100) giving the maximum size that compressed data can have to keep the compressed value. If the resulting compressed data is higher than this, the document will be stored uncompressed. The default value is 95.</td> + </tr> + </tbody> +</table> + + +## rank-profile + +Contained in [schema](#schema) or equivalently in separate files in the [application package](/en/reference/applications/application-packages), named `[profile-name].profile` in any directory below `schemas/[schema-name]/`. A [rank profile](/en/basics/ranking#rank-profiles) is a named set of ranking expression functions and settings which can be [selected in the query](/en/reference/api/query#ranking.profile). + +Whether defined inline in the schema or in a separate .profile file, the syntax of a rank profile is + +```bash +rank-profile [name] inherits [rank-profile1], [rank-profile2], ... { + [body] +} +``` + +The `inherits` list is optional and may contain the name of other rank profiles in this schema or one it inherits. Elements not defined in this rank profile will then be inherited from those profiles. Inheriting multiple profiles that define the same elements leads to an error at deployment. + +The body of a rank-profile may contain: + +<table className="vespa-table"> + <thead> + <tr> + <th>Name</th> + <th>Occurrence</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td><a href="#diversity" aria-label="diversity">diversity</a></td> + <td>Zero or one</td> + <td>Specification of required diversity between the different phases.</td> + </tr> + <tr> + <td><a href="#strict" aria-label="strict">strict</a></td> + <td>Zero or one</td> + <td>true/false: Whether to use strict or loose type checking.</td> + </tr> + <tr> + <td><a href="#match-phase" aria-label="match-phase">match-phase</a></td> + <td>Zero or one</td> + <td>Ranking configuration to be used for hit limitation during matching.</td> + </tr> + <tr> + <td><a href="#firstphase-rank" aria-label="first-phase">first-phase</a></td> + <td>Zero or one</td> + <td>The ranking config to be used for first-phase ranking.</td> + </tr> + <tr> + <td><a href="#secondphase-rank" aria-label="second-phase">second-phase</a></td> + <td>Zero or one</td> + <td>The ranking config to be used for second-phase ranking.</td> + </tr> + <tr> + <td><a href="#globalphase-rank" aria-label="global-phase">global-phase</a></td> + <td>Zero or one</td> + <td>The ranking config to be used for global-phase ranking.</td> + </tr> + <tr> + <td><a href="#function-rank" aria-label="function [name]">function [name]</a></td> + <td>Zero or more</td> + <td>Defines a named function that can be referenced during ranking phase(s) and (if without arguments) as part of match-and summary-features.</td> + </tr> + <tr> + <td><a href="#inputs" aria-label="inputs">inputs</a></td> + <td>Zero or many</td> + <td>List of query features used in ranking expressions.</td> + </tr> + <tr> + <td><a href="#constants" aria-label="constants">constants</a></td> + <td>Zero or many</td> + <td>List of constant features available in ranking expressions.</td> + </tr> + <tr> + <td><a href="#mutate" aria-label="mutate">mutate</a></td> + <td>Zero or many</td> + <td>Specification of mutations you can apply after different phases of a query.</td> + </tr> + <tr> + <td><a href="#onnx-model" aria-label="onnx-model">onnx-model</a></td> + <td>Zero or many</td> + <td>An onnx model to make available in this profile.</td> + </tr> + <tr> + <td><a href="#significance" aria-label="significance">significance</a></td> + <td>Zero or one</td> + <td>To enable the use of significance models defined in the service.xml config.</td> + </tr> + <tr> + <td><a href="#rank-properties" aria-label="rank-properties">rank-properties</a></td> + <td>Zero or one</td> + <td>List of any rank property key-values to be used by rank features.</td> + </tr> + <tr> + <td><a href="#match-features" aria-label="match-features">match-features</a></td> + <td>Zero or more</td> + <td>The <a href="/en/reference/ranking/rank-features" aria-label="rank features">rank features</a> to be returned with each hit, computed in the *match* phase.</td> + </tr> + <tr> + <td><a href="#summary-features" aria-label="summary-features">summary-features</a></td> + <td>Zero or more</td> + <td>The <a href="/en/reference/ranking/rank-features" aria-label="rank features">rank features</a> to be returned with each hit, computed in the *fill* phase.</td> + </tr> + <tr> + <td><a href="#rank-features" aria-label="rank-features">rank-features</a></td> + <td>Zero or more</td> + <td>The <a href="/en/reference/ranking/rank-features" aria-label="rank features">rank features</a> to be dumped when using the query-argument <a href="/en/reference/api/query#ranking.listfeatures" aria-label="rankfeatures">rankfeatures</a>.</td> + </tr> + <tr> + <td>ignore-default-rank-features</td> + <td>Zero or one</td> + <td>Do not dump the default set of rank features, only those explicitly specified with the <a href="#rank-features" aria-label="rank-features">rank-features</a> command.</td> + </tr> + <tr> + <td>num-threads-per-search</td> + <td>Zero or one</td> + <td>Overrides the global <a href="/en/reference/applications/services/content#requestthreads-persearch" aria-label="persearch">persearch</a> threads to a **lower** value.</td> + </tr> + <tr> + <td>min-hits-per-thread</td> + <td>Zero or one</td> + <td>After estimating the number of hits for a query prior to query evaluation, this number is used to decide how many threads to use for the query.<br/> <code>{`num_treads = min([num-threads-per-search](#num-threads-per-search), estimated_hits / min-hits-per-thread)`}</code> <br/>The current default is 1. If you suspect the fixed cost per thread is too high, increasing this number might be a good idea. Especially if most of your queries are cheap, but you have increased the <a href="#num-threads-per-search" aria-label="num-threads-per-search">num-threads-per-search</a> in order to reduce latency for your costly queries covering a lot of documents. The default might change, or the optimal value might be adaptive rendering overrides ignored or counterproductive.</td> + </tr> + <tr> + <td>num-search-partitions</td> + <td>Zero or one</td> + <td>The number of logical partitions in which the corpus is divided on a search node. By default, this is the same as <a href="#num-threads-per-search" aria-label="num-threads-per-search">num-threads-per-search</a>. A partition is the smallest unit a search thread will handle. If you have a locality in time when searching and feeding documents, you might want to split it into more, smaller partitions. That way, you avoid that one costly partition leaves some threads idle while others are working hard. <br/> If you have 8 threads per search, you might have 10x as many partitions at 80 reducing max skew with a similar factor. Note that a value of zero turns on adaptive partitioning which tries to solve this optimally. <Info> **Note:** If <code>{`num-search-partitions`}</code> is set to 0 (work sharing is enabled), make sure <code>{`termwise-limit`}</code> is set to 1.0 (termwise evaluation is disabled). This is to avoid redoing termwise evaluation when work is passed from one thread to another.</Info></td> + </tr> + <tr> + <td>termwise-limit</td> + <td>Zero or one</td> + <td>If estimated number of hits > corpus * termwise-limit, it will prune candidates with a CPU cache-friendly <a href="/en/performance/feature-tuning#hybrid-taat-daat" aria-label="TAAT">TAAT</a> with the terms not needed for ranking, prior to doing <a href="/en/performance/feature-tuning#hybrid-taat-daat" aria-label="DAAT">DAAT</a>. Current default is 1.0 which turns it off. A value between 0.05 and 0.20 can be a good starting point. This is particularly useful if you have many weak filters. Note that this is a manual override. The default might change, or the optimal value might be adaptive rendering overrides ignored or counterproductive.</td> + </tr> + <tr> + <td>post-filter-threshold</td> + <td>Zero or one</td> + <td>Threshold value (in the range [0.0, 1.0]) deciding if a query with an approximate <a href="/en/reference/querying/yql#nearestneighbor" aria-label="nearestNeighbor">nearestNeighbor</a> operator combined with filters is evaluated using post-filtering instead of the default filtering. Post-filtering is chosen when the estimated filter hit ratio of the query is *larger* than this threshold. The default value is 1.0, which disables post-filtering. See <a href="https://blog.vespa.ai/constrained-approximate-nearest-neighbor-search/#controlling-the-filtering-behavior-with-approximate-nearest-neighbor-search" aria-label="Controlling the filtering behavior with approximate nearest neighbor search">Controlling the filtering behavior with approximate nearest neighbor search</a> for more details. <br/><br/> With post-filtering the <a href="/en/reference/querying/yql#totaltargethits" aria-label="totalTargetHits">totalTargetHits</a> value used when searching the HNSW index is auto-adjusted in an effort to expose the node's share of *totalTargetHits* hits to first-phase ranking after post-filtering has been applied. The following formula is used: <pre><code>{`adjustedTargetHits = min(targetHits / estimatedFilterHitRatio, targetHits * targetHitsMaxAdjustmentFactor)`}</code></pre>. Use <a href="#target-hits-max-adjustment-factor" aria-label="target-hits-max-adjustment-factor">target-hits-max-adjustment-factor</a> to control the upper bound of the adjusted *targetHits*. This parameter has no effect in <a href="/en/performance/streaming-search#differences-in-streaming-search" aria-label="streaming search">streaming search</a>.</td> + </tr> + <tr> + <td>approximate-threshold</td> + <td>Zero or one</td> + <td>Threshold value (in the range [0.0, 1.0]) deciding if a query with an approximate <a href="/en/reference/querying/yql#nearestneighbor" aria-label="nearestNeighbor">nearestNeighbor</a> operator combined with filters is evaluated by searching the <a href="#index-hnsw" aria-label="HNSW">HNSW</a> graph for approximate neighbors with filtering, or performing an <a href="/en/querying/nearest-neighbor-search" aria-label="exact nearest neighbor search">exact nearest neighbor search</a> with pre-filtering. The fallback to exact search is chosen when the estimated filter hit ratio of the query is *less* than this threshold. The default value is 0.02. See <a href="https://blog.vespa.ai/constrained-approximate-nearest-neighbor-search/#controlling-the-filtering-behavior-with-approximate-nearest-neighbor-search" aria-label="Controlling the filtering behavior with approximate nearest neighbor search">Controlling the filtering behavior with approximate nearest neighbor search</a> for more details. This parameter has no effect in <a href="/en/performance/streaming-search#differences-in-streaming-search" aria-label="streaming search">streaming search</a>.</td> + </tr> + <tr> + <td>filter-first-threshold</td> + <td>Zero or one</td> + <td>Threshold value (in the range [0.0, 1.0]) deciding if the filter is checked before computing a distance (*filter-first heuristic*) while searching the <a href="#index-hnsw" aria-label="HNSW">HNSW</a> graph for approximate neighbors with filtering. This improves the response time at low hit ratios but causes a dip in recall. The heuristic is used when the estimated filter hit ratio of the query is *less* than this threshold. The default value is 0.2. This parameter has no effect in <a href="/en/performance/streaming-search#differences-in-streaming-search" aria-label="streaming search">streaming search</a>.</td> + </tr> + <tr> + <td>filter-first-exploration</td> + <td>Zero or one</td> + <td>Value (in the range [0.0, 1.0]) specifying how aggressively the filter-first heuristic explores the graph when searching the <a href="#index-hnsw" aria-label="HNSW">HNSW</a> graph for approximate neighbors with filtering. A higher value means that the graph is explored more aggressively and improves the recall at the cost of the response time. The default value is 0.01. This parameter has no effect in <a href="/en/performance/streaming-search#differences-in-streaming-search" aria-label="streaming search">streaming search</a>.</td> + </tr> + <tr> + <td>exploration-slack</td> + <td>Zero or one</td> + <td>Value (in the range [0.0, 1.0]) specifying slack to delay the termination of the search of the <a href="#index-hnsw" aria-label="HNSW">HNSW</a> graph for approximate neighbors with or without filtering. A higher value means that more of the graph is explored and improves the recall at the cost of the response time. The default value is 0.0. This parameter has no effect in <a href="/en/performance/streaming-search#differences-in-streaming-search" aria-label="streaming search">streaming search</a>.</td> + </tr> + <tr> + <td>target-hits-max-adjustment-factor</td> + <td>Zero or one</td> + <td>Value (in the range [1.0, inf]) used to control the auto-adjustment of <a href="/en/reference/querying/yql#totaltargethits" aria-label="totalTargetHits">totalTargetHits</a> used when evaluating an approximate <a href="/en/reference/querying/yql#nearestneighbor" aria-label="nearestNeighbor">nearestNeighbor</a> operator with post-filtering. The default value is 20.0. Setting this value to 1.0 disables auto-adjustment of *targetHits*. See <a href="#post-filter-threshold" aria-label="post-filter-threshold">post-filter-threshold</a> for more details. This parameter has no effect in <a href="/en/performance/streaming-search#differences-in-streaming-search" aria-label="streaming search">streaming search</a>.</td> + </tr> + <tr> + <td>filter-threshold</td> + <td>Zero or one</td> + <td>The threshold value (in the range [0.0, 1.0]) deciding when matching in *index* fields should be treated as filters. This happens for query terms with <a href="/en/learn/glossary#estimated-hit-ratio" aria-label="estimated hit ratios">estimated hit ratios</a> (in the range [0.0, 1.0]) that are above the *filter-threshold*. Use this to optimize query performance when searching large text <a href="/en/basics/schemas#document-fields" aria-label="index">index</a> fields, by allowing a per query combination of <a href="#filter" aria-label="rank: filter">rank: filter</a> and <a href="#normal" aria-label="rank: normal">rank: normal</a> behavior. This parameter can be overridden per *index* field, see <a href="#rank-filter-threshold" aria-label="field-level filter-threshold">field-level filter-threshold</a> for a more detailed description with tradeoffs. <br/><br/> In testing with various text datasets (e.g., Wikipedia), a *filter-threshold* setting of 0.05 has been shown to be a good starting point. See <a href="/en/performance/feature-tuning#tuning-query-performance-for-lexical-search" aria-label="Tuning query performance for lexical search">Tuning query performance for lexical search</a> for more details. This parameter has no effect in <a href="/en/performance/streaming-search#differences-in-streaming-search" aria-label="streaming search">streaming search</a>. Use the <a href="/en/reference/api/query#ranking.matching.filterThreshold" aria-label="ranking.matching.filterThreshold">ranking.matching.filterThreshold</a> query parameter to override this value.</td> + </tr> + <tr> + <td><a href="#rank" aria-label="rank">rank</a></td> + <td>Zero or more</td> + <td>Specify rank settings of a field in this profile.</td> + </tr> + <tr> + <td><a href="#rank-type" aria-label="rank-type">rank-type</a></td> + <td>Zero or more</td> + <td>The rank-type of a field in this profile.</td> + </tr> + <tr> + <td><a href="#weakand" aria-label="weakand">weakand</a></td> + <td>Zero or one</td> + <td>Tunes the <a href="/en/ranking/wand#weakand" aria-label="weakAnd">weakAnd</a> algorithm to automatically exclude terms and documents with expected low query significance based on <a href="/en/learn/glossary#document-frequency-normalized" aria-label="document frequency">document frequency</a> statistics present in the document corpus. This makes matching faster at the cost of potentially reduced recall.</td> + </tr> + <tr> + <td><a href="#rank-profile" aria-label="rank-profile (inner)">rank-profile (inner)</a></td> + <td>Zero or more</td> + <td>An inner rank profile, useful for grouping related profiles, especially when defined in separate .profile files. This behaves just like a top level rank profile, except that: <br/> - The full name of the profile to use in queries will be <code>{`containing-profile-name.inner-profile-name`}</code>. <br/> - The profile must explicitly inherit the containing profile.</td> + </tr> + </tbody> +</table> + +## match-phase + +Contained in [rank-profile](#rank-profile). The match-phase feature lets you increase performance by limiting hits exposed to first-phase ranking to the highest (lowest) values of some attribute. The performance gain may be substantial, especially with an expensive first-phase function. The quality loss depends on how well the chosen attribute correlates with the first-phase score. + +Documents that have no value of the chosen attribute will be taken as having the value 0. + +See also [graceful degradation](/en/performance/graceful-degradation#match-phase-degradation) and [result diversity](/en/querying/result-diversity#match-phase-diversity). + +```js +match-phase { + attribute: [numeric single value attribute] + order: [ascending | descending] + total-max-hits: [integer] +} +``` + +<table className="vespa-table"> + <thead> + <tr> + <th>Name</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>attribute</td> + <td>The quality attribute that decides which documents are a match if the match phase estimates that there will be more than the node's share if <a href="#match-phase-total-max-hits" aria-label="total-max-hits">total-max-hits</a> hits. The attribute must be single-value numeric with <a href="#attribute" aria-label="fast-search">fast-search</a> enabled. It should correlate with the order which would be produced by a full query evaluation. No default.</td> + </tr> + <tr> + <td>order</td> + <td>Whether the attribute should be used in <code>{`descending`}</code> order (prefer documents with a high value) or <code>{`ascending`}</code> order (prefer documents with a low value). Usually, it is not necessary to specify this, as the default value <code>{`descending`}</code> is by far the most common.</td> + </tr> + <tr> + <td>total-max-hits</td> + <td>The total max hits that should be produced in the match phase across all nodes in the group evaluating the query. This number should be large, and larger the worse the correlation between the match-phase attribute and the first-phase function.</td> + </tr> + <tr> + <td>max-hits</td> + <td>The max hits each content node should attempt to produce in the match phase. Prefer using <a href="#match-phase-total-max-hits" aria-label="total-max-hits">total-max-hits</a> over this.</td> + </tr> + </tbody> +</table> + +## strict + +Contained in [rank-profile](#rank-profile). True or false. By default, Vespa uses loose type checking, where any query feature used but not defined in a query profile type is assumed to be a float. Set true to cause a deploy failure on missing query property type definitions instead. + +```bash +strict: true +``` + +## diversity + +Contained in [rank-profile](#rank-profile). Diversity is used to guarantee diversity in the different query phases. If you have [match-phase](#match-phase), it will provide diverse results from match-phase to first-phase. If you have [second-phase](#secondphase-rank), it will provide diverse results from first-phase to second-phase. + +Read more about this in [result diversity](/en/querying/result-diversity). + +Specify the name of an attribute that will be used to provide diversity. Result sets are guaranteed to get at least [min-groups](#diversity-min-groups) unique values from the [diversity attribute](#diversity-min-groups) from this phase, but no more than max-hits. For [match-phase](#match-phase) max-hits = the node's share of [match-phase total-max-hits](#match-phase-max-hits). For [second-phase](#secondphase-rank) max-hits = the node's share of [total-rerank-count](#secondphase-total-rerank-count). A document is considered a candidate if: + +- The query has not yet reached the _max-hits_ number produced from this phase. +- The query has not yet reached the max number of candidates in one group. This is computed by the _max-hits_ of the phase divided by [min-groups](#diversity-min-groups) + +```js +diversity { + attribute: [attribute name] + min-groups: [integer] +} +``` + +<table className="vespa-table"> + <thead> + <tr> + <th>Name</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>attribute</td> + <td>Which attribute to use when deciding diversity. The attribute must be a single-valued numeric, string or <a href="#reference" aria-label="reference">reference</a> type.</td> + </tr> + <tr> + <td>min-groups</td> + <td>Specifies the minimum number of groups returned from the phase. Using this with <a href="#match-phase" aria-label="match-phase">match-phase</a> often means one can reduce <a href="#match-phase-total-max-hits" aria-label="total-max-hits">total-max-hits</a>. In <a href="#secondphase-rank" aria-label="second-phase">second-phase</a> you might reduce <a href="#secondphase-total-rerank-count" aria-label="total-rerank-count">total-rerank-count</a> and still get good and diverse results.</td> + </tr> + </tbody> +</table> + +## first-phase + +Contained in [rank-profile](#rank-profile). The config specifying the first phase of ranking. See [phased ranking with Vespa](/en/ranking/phased-ranking). This is the initial ranking performed on all matching documents; you should therefore avoid doing computationally expensive relevancy calculations here. By default, this will use the ranking feature `nativeRank`. + +```js +first-phase { + [body] +} +``` +The body of a first-phase ranking statement consists of: + +<table className="vespa-table"> + <thead> + <tr> + <th>Name</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td><a href="#expression" aria-label="expression">expression</a></td> + <td>Specify the ranking expression to be used for the first phase of ranking - see <a href="/en/reference/ranking/ranking-expressions" aria-label="ranking expressions">ranking expressions</a>.</td> + </tr> + <tr> + <td>total-keep-rank-count</td> + <td>How many documents to keep the first phase top rank values for in total over the nodes evaluating the query. The default value is 10000 per node.</td> + </tr> + <tr> + <td>keep-rank-count</td> + <td>How many documents to keep the first phase top rank values for per node. Prefer <a href="#total-keep-rank-count" aria-label="total-keep-rank-count">total-keep-rank-count</a> over this.</td> + </tr> + <tr> + <td>rank-score-drop-limit</td> + <td>Drop all hits with a first-phase rank score less than or equal to this floating-point number. Use this to implement a rank cutoff. Default is <code>{`-Double.MAX_VALUE`}</code>.</td> + </tr> + </tbody> +</table> + +## expression + +Contained in [first-phase](#firstphase-rank) or [second-phase](#secondphase-rank) or [global-phase](#globalphase-rank). Specify a [ranking expression](/en/reference/ranking/ranking-expressions). The expression can either be written directly or loaded from a file. When writing it directly, the syntax is: + +```bash +expression: [ranking expression] +``` +or +```js +expression { + [ranking expression] + [ranking expression] + [ranking expression] +} +``` + +The second format is primarily a convenience feature when using long expressions, enabling them to be split over multiple lines. + +Expressions can also be loaded from a separate file. This is useful when dealing with the long expressions generated by e.g. MLR. The syntax is: + +```js +expression: file:[path-to-expressionfile] +``` + +The path is relative to the location of the schema definition file. The file itself must end with `.expression`. This suffix is optional in the schema. Therefore `expression: file:mlrranking.expression` and `expression: file:mlrranking` are identical. Both refer to a file called `mlrranking.expression` in the _schemas_ directory. + +<Info> + **Note:** Directories are not allowed in the path. +</Info> + +## rank-features + +Contained in [rank-profile](#rank-profile). List of extra [rank features](/en/reference/ranking/rank-features) to be dumped when using the query-argument [rankfeatures](/en/reference/api/query#ranking.listfeatures). + +```bash +rank-features: [feature] [feature] +``` +or +```js +rank-features { + [feature] + [feature] +} +``` + +Any number of ranking features can be listed on each line, separated by space. + +## inputs + +Contained in [rank-profile](#rank-profile). List of inputs: Query features consumed by ranking expressions in this profile. + +Query features are set either as a [request property](/en/reference/api/query#ranking.features), or equivalently from a [Searcher](/en/applications/searchers), by calling `query.getRanking().getFeatures().put("query(myInput)", myValue)`. + +Query feature types can also be declared in [query profile types](/en/querying/query-profiles#query-profile-types), but declaring inputs in the profile needing them is usually preferable. + +Inputs are inherited from inherited profiles. + +```js +inputs { + name [type]? (: value)? +} +``` + +<table className="vespa-table"> + <thead> + <tr> + <th>Name</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>name</td> + <td>The name of the inputs, written either the full feature name <code>{`query(myName)`}</code>, or just as <code>{`name`}</code>.</td> + </tr> + <tr> + <td>type</td> + <td>The type of the constant, either <code>{`double`}</code> or a <a href="/en/reference/ranking/tensor#tensor-type-spec" aria-label="tensor type">tensor type</a>. If omitted, the type is double.</td> + </tr> + <tr> + <td>value</td> + <td>An optional default module, used if this input is not set in the query. A number, or a <a href="/en/reference/ranking/tensor#tensor-literal-form" aria-label="tensor on literal form">tensor on literal form</a>.</td> + </tr> + </tbody> +</table> + +Input examples: + +```bash +inputs { + myDouble: 0.5 + query(myOtherDouble) double + query(myArray) tensor(x[3]) + query(myMap) tensor(key{}):{key1: 1.0, key2: 2.0} +} +``` + +## constants + +Contained in [rank-profile](#rank-profile). List of constants available in ranking expressions, resolved and optimized at configuration time. + +Constants are inherited from inherited profiles, and from the schema itself. + +```js +constants { + name [type]?: value|file:[path] +} +``` + +<table className="vespa-table"> + <thead> + <tr> + <th>Name</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>name</td> + <td>The name of the constant, written either the full feature name <code>{`constant(myName)`}</code>, or just as <code>{`name`}</code>.</td> + </tr> + <tr> + <td>type</td> + <td>The type of the constant, either <code>{`double`}</code> or a <a href="/en/reference/ranking/tensor#tensor-type-spec" aria-label="tensor type">tensor type</a>. If omitted, the type is double.</td> + </tr> + <tr> + <td>value</td> + <td>A number, a <a href="/en/reference/ranking/tensor#tensor-literal-form" aria-label="tensor on literal form">tensor on literal form</a>, or <code>{`file:`}</code> followed by a path from the application package root to a file containing the constant. The file must be stored in a valid <a href="/en/reference/ranking/constant-tensor-json-format" aria-label="tensor JSON Format">tensor JSON Format</a> and end with <code>{`.json`}</code>. The file may be lz4 compressed, in which case the ending must be <code>{`.json.lz4`}</code>.</td> + </tr> + </tbody> +</table> + +Constant examples: + +```js +constants { + myDouble: 0.5 + constant(myOtherDouble) double: 0.6 + constant(myArray) tensor(x[3]):[1, 2, 3] + constant(myMap) tensor(key{}]):{key1: 1.0, key2: 2.0} + constant(myLargeTensor) tensor(x[10000]): file:constants/myTensor.json.lz4 +} +``` + +## rank-properties + +Contained in [rank-profile](#rank-profile). List of generic properties, in the form of key/value pairs to be used by ranking features. [Examples](/en/reference/ranking/rank-feature-configuration). + +```js +rank-properties { + key: value +} +``` + +<table className="vespa-table"> + <thead> + <tr> + <th>Name</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>key</td> + <td>Name of the property.</td> + </tr> + <tr> + <td>value</td> + <td>A number or any string. Must be quoted if it contains spacing.</td> + </tr> + </tbody> +</table> + +## function (inline)? [name] + +Contained in [rank-profile](#rank-profile). Define a named function that can be referenced as a part of the ranking expression, or (if having no arguments) as a feature. A function accepts any number of arguments. + +```js +function [name]([arg1], [arg2], [arg3]) { + expression: … +} +``` +or +```js +function [name] ([arg1], [arg2], [arg3]) { + expression { + [ranking expression] + [ranking expression] + … +} +``` +Note that the parenthesis is required after the name. A rank-profile example is shown below: +```js +rank-profile default inherits default { + function myfeature() { + expression: fieldMatch(title) + freshness(timestamp) + } + function otherfeature(foo) { + expression{ nativeRank(foo, body) } + } + + first-phase { + expression: myfeature * 10 + } + second-phase { + expression: otherfeature(title) * myfeature + } + summary-features: myfeature +} +``` + +You can not include functions that accept arguments in summary features. + +Adding the `inline` modifier will inline this function in the calling expression if it also has no arguments. This is faster for small and cheap functions (and more expensive for others). + +## second-phase + +Contained in [rank-profile](#rank-profile). The config specifying the second phase of ranking. See [phased ranking with Vespa](/en/ranking/phased-ranking). This is the optional re-ranking phase performed on the top-ranking hits from the `first-phase`, and where you should put any advanced relevancy calculations. For example Machine Learned Ranking (MLR) models. By default, no second-phase ranking is performed. + +```js +second-phase { + [body] +} +``` +The body of a secondphase-ranking statement consists of: + +<table className="vespa-table"> + <thead> + <tr> + <th>Name</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td><a href="#expression" aria-label="expression">expression</a></td> + <td>Specify the ranking expression to be used for the second phase of ranking. (for a description, see the <a href="/en/reference/ranking/ranking-expressions" aria-label="ranking expression">ranking expression</a> documentation. Hits not reranked might be re-scored using a linear function to avoid a greater rank score than the worst reranked hit. This linear function will normally attempt to map the first phase rank score range of reranked hits to the reranked rank score range</td> + </tr> + <tr> + <td>rank-score-drop-limit</td> + <td>When set, drop all hits with a second phase rank score (possibly a <a href="#secondphase-rescoring" aria-label="re-scored">re-scored</a> rank score) less than or equal to this floating point number. Use this to implement a second-phase rank cutoff. By default, this value is not set. This can also be <a href="/en/reference/api/query#ranking.secondphase.rankscoredroplimit" aria-label="set in the query">set in the query</a>.</td> + </tr> + <tr> + <td>total-rerank-count</td> + <td>Optional argument. Specifies the number of hits to be re-ranked in the second phase in total over the content nodes that participate in evaluating a query (a *group*). The default value is 100 per node. This can also be <a href="/en/reference/api/query#ranking.secondphase.totalrerankcount" aria-label="set in the query">set in the query</a>. Hits not reranked might be <a href="#secondphase-rescoring" aria-label="re-scored">re-scored</a>.</td> + </tr> + <tr> + <td>rerank-count</td> + <td>Optional argument. Specifies the number of hits to be re-ranked in the second phase on each content node. This can also be <a href="/en/reference/api/query#ranking.secondphase.rerankcount" aria-label="set in the query">set in the query</a>. Prefer using <a href="#secondphase-total-rerank-count" aria-label="total-rerank-count">total-rerank-count</a> over this.</td> + </tr> + </tbody> +</table> + +## global-phase + +Contained in [rank-profile](#rank-profile). The config specifying the global phase of ranking. See [phased ranking with Vespa](/en/ranking/phased-ranking). This is an optional re-ranking phase performed on the top-ranking hits in the stateless container after merging hits from all the content nodes. The "top ranking" here means as scored by the first-phase ranking expression or (if specified) second-phase ranking expression. Typically used for computing large ONNX models, which would be expensive to compute on all content nodes. By default, no global-phase ranking is performed. + +```js +global-phase { + [body] +} +``` +The body of a global-phase ranking statement consists of: + +<table className="vespa-table"> + <thead> + <tr> + <th>Name</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td><a href="#expression" aria-label="expression">expression</a></td> + <td>Specify the ranking expression to be used for the global phase of ranking. (for a description, see the <a href="/en/reference/ranking/ranking-expressions" aria-label="ranking expression">ranking expression</a> documentation.</td> + </tr> + <tr> + <td>rerank-count</td> + <td>Optional argument. Specifies the number of hits to be re-ranked in the global phase. The default value is 100. Note for complex setups: Applied to hits from one schema at a time, so if a query searches in multiple schemas simultaneously, global-phase may run for 100 hits per schema as default.</td> + </tr> + <tr> + <td>rank-score-drop-limit</td> + <td>When set, drop all hits with a global phase rank score (possibly a <a href="#globalphase-rank" aria-label="re-scored">re-scored</a> rank score) less than or equal to this floating point number. Use this to implement a global phase rank cutoff. By default, this value is not set. This can also be <a href="/en/reference/api/query#ranking.globalphase.rankscoredroplimit" aria-label="set in the query">set in the query</a>.</td> + </tr> + </tbody> +</table> + +## summary-features + +Contained in [rank-profile](#rank-profile). List of [rank features](/en/reference/ranking/rank-features) to be included with each result hit, in the [summaryfeatures](/en/reference/querying/default-result-format#summaryfeatures) field. Also see [feature values in results](/en/ranking/ranking-expressions-features#accessing-feature-function-values-in-results). + +If not specified, the features are as specified in the parent profile (if any). To inherit the features from the parent profile _and_ specify additional features, specify explicitly that the features should be inherited from the parent, as shown below. Refer to [schema inheritance](/en/schemas/inheritance-in-schemas) for examples. + +The rank features specified here are computed in the [fill phase](/en/applications/searchers#multiphase-searching) of multiphased queries. + +<Info> + **Note:** Rank-features references in _summary-features_ are **re-calculated** during the _fill protocol phase_ for the hits which made it into the global top ranking hits (from all nodes). See [match-features](#match-features) for an alternative. +</Info> + +```bash +summary-features: [feature] [feature]… +``` + +or + +```bash +summary-features [inherits parent-profile (, other-parent-profile)*]? { + [feature] + [feature] +} +``` + +Any number of rank features separated by space can be listed on each line. + +<Info> + **Note:** Note that compound expressions must be wrapped in a [function](/en/ranking/ranking-expressions-features#accessing-feature-function-values-in-results) to be accessible by match/summary-features. +</Info> + +## match-features + +Contained in [rank-profile](#rank-profile). List of [rank features](/en/reference/ranking/rank-features) to be included with each result hit, in the [matchfeatures](/en/reference/querying/default-result-format#matchfeatures) field. Also see [feature values in results](/en/ranking/ranking-expressions-features#accessing-feature-function-values-in-results). + +If not specified, the features are as specified in the parent profile (if any). To inherit the features from the parent profile _and_ specify additional features, specify explicitly that the features should be inherited from the parent as shown below, also see [schema inheritance](/en/schemas/inheritance-in-schemas). + +To disable match-features from parent rank profiles, use `match-features {}`. + +_match-features_ is similar to [summary-features](#summary-features), but the rank features specified here are computed in the _first protocol phase_ of [multiprotocol query execution](/en/applications/searchers#multiphase-searching), also called the _match_ protocol phase. This gives a different performance trade-off, for details, see [feature values in results](/en/ranking/ranking-expressions-features#accessing-feature-function-values-in-results). + +```bash +match-features: [feature] [feature]… +``` + +or + +```bash +match-features [inherits parent-profile (, other-parent-profile)*]? { + [feature] + [feature] +} +``` + +Any number of ranking features separated by space can be listed on each line. + +<Info> + **Note:** Note that compound expressions must be wrapped in a [function](/en/ranking/ranking-expressions-features#accessing-feature-function-values-in-results) to be accessible by match/summary-features. +</Info> + +## mutate + +Contained in [rank-profile](#rank-profile). Specifies mutating operations you can do to each of the documents that make it through the 4 query phases, _on-match_, _on-first-phase_, _on-second-phase_ and _on-summary_. + +```js +mutate { + [phase name] { [attribute name] [operation] [numeric_value] } +} +``` +The phases are: + +<table className="vespa-table"> + <thead> + <tr> + <th>Name</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>on-match</td> + <td>All documents that satisfy the query.</td> + </tr> + <tr> + <td>on-first-phase</td> + <td>All documents from <a href="#on-match" aria-label="on-match">on-match</a>, and is not dropped due the optional <a href="#rank-score-drop-limit" aria-label="rank-score-drop-limit">rank-score-drop-limit</a></td> + </tr> + <tr> + <td>on-second-phase</td> + <td>All documents from <a href="#on-first-phase" aria-label="on-first-phase">on-first-phase</a> that makes it onto the <a href="#secondphase-rank" aria-label="second-phase">second-phase</a> heap.</td> + </tr> + <tr> + <td>on-summary</td> + <td>All documents where are a summary is requested.</td> + </tr> + </tbody> +</table> + +The attribute must be a single value numeric attribute, enabled as [mutable](#mutable). It must also be defined outside the [document](#document) clause. + +<table className="vespa-table"> + <thead> + <tr> + <th>Operation</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>=</td> + <td>Set the value of the attribute to the given value.</td> + </tr> + <tr> + <td>+=</td> + <td>Add the given value to the attribute</td> + </tr> + <tr> + <td>-=</td> + <td>Subtract the given value from the attribute</td> + </tr> + </tbody> +</table> + + +Find examples and use cases in [rank phase statistics](/en/ranking/phased-ranking#rank-phase-statistics). + +## constant + +_Prefer to define constants in the rank profiles that need them, with rank profile inheritance to avoid repetition. See [constants](#constants)._ + +Contained in [schema](#schema). This defines a named constant tensor located in a file with a given type that can be used in ranking expressions using the rank feature [constant(name)](/en/reference/ranking/rank-features#constant(name)): + +```bash +constant [name] { + [body] +} +``` +The body of a constant must contain: + +<table className="vespa-table"> + <thead> + <tr> + <th>Name</th> + <th>Description</th> + <th>Occurrence</th> + </tr> + </thead> + <tbody> + <tr> + <td>file</td> + <td>Path to the file containing this constant, relative to the application package root. The file must be stored in a valid <a href="/en/reference/ranking/constant-tensor-json-format" aria-label="tensor JSON Format">tensor JSON Format</a> and end with <code>{`.json`}</code>. The file may be lz4 compressed, in which case the ending must be <code>{`.json.lz4`}</code>.</td> + <td>One</td> + </tr> + <tr> + <td>type</td> + <td>The type of the constant tensor, refer to <a href="/en/reference/ranking/tensor#tensor-type-spec" aria-label="tensor-type-spec">tensor-type-spec</a> for reference.</td> + <td>One</td> + </tr> + </tbody> +</table> + +Constant tensor example: + +```js +constant my_constant_tensor { + file: constants/my_constant_tensor_file.json + type: tensor<float>(x{},y{}) +} +``` +This example has a constant tensor with two mapped dimensions, `x` and `y`: +```json +{ + "cells": [ + { "address": { "x": "a", "y": "b"}, "value": 2.0 }, + { "address": { "x": "c", "y": "d"}, "value": 3.0 } + ] +} +``` + +When an application with tensor constants is deployed, the files are distributed to the content nodes before the new configuration is used by the search nodes. Incremental changes to constant tensors are not supported. When changed, replace the old file with a new one and redeploy the application, or create a new constant with a new name in a new file. + +## raw-as-base64-in-summary + +Contained in [schema](#schema). Whether raw fields should be rendered as a base64 encoded string in summary, the same way as in [json feed format](/en/reference/schemas/document-json-format#raw), rather than an escaped string. This is by default true. + +## onnx-model + +Contained in [rank-profile](#rank-profile) or [schema](#schema). This defines a named ONNX model located in a file that can be used in ranking expressions using the "onnx" rank feature. + +Prefer to define onnx models in the rank profiles using them. Onnx models are inherited from parent profiles and from the schema. + +```bash +onnx-model [name] { + [body] +} +``` + +The body of an ONNX model must contain: + +<table className="vespa-table"> + <thead> + <tr> + <th>Name</th> + <th>Occurrence</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>file</td> + <td>One</td> + <td>Path to the location of the file containing the ONNX model. The path is relative to the root of the application package containing this schema.</td> + </tr> + <tr> + <td>input</td> + <td>Zero to many</td> + <td>An input to the ONNX model. The ONNX name, as given in the model, as well as the source for the input, is specified.</td> + </tr> + <tr> + <td>output</td> + <td>Zero to many</td> + <td>An output of the ONNX model. The ONNX name, as given in the model, as well as the name for use in Vespa, is specified. If no output is defined and is not referred to from the rank feature, the first output defined in the model is used.</td> + </tr> + <tr> + <td>gpu-device</td> + <td>Zero or one</td> + <td>Set the GPU device number to use for computation, starting at 0, i.e. if your GPU is <code>{`/dev/nvidia0`}</code> set this to 0. This must be an Nvidia CUDA-enabled GPU. Currently only models used in <a href="#globalphase-rank" aria-label="global-phase">global-phase</a> can make use of GPU-acceleration.</td> + </tr> + <tr> + <td>intraop-threads</td> + <td>Zero or one</td> + <td>The number of threads available for running operations with multithreaded implementations.</td> + </tr> + <tr> + <td>interop-threads</td> + <td>Zero or one</td> + <td>The number of threads available for running multiple operations in parallel. This is only applicable for <code>{`parallel`}</code> execution mode.</td> + </tr> + <tr> + <td>execution-mode</td> + <td>Zero or one</td> + <td>Controls how the operators of a graph are executed, either <code>{`sequential`}</code> or <code>{`parallel`}</code>.</td> + </tr> + </tbody> +</table> + +For more details including examples, see [ranking with ONNX models.](/en/ranking/onnx) + +## significance + +Contained in [rank-profile](#rank-profile). Configures a [significance model](/en/ranking/significance). + +```js +significance { + use-model: true +} +``` + +The body must contain: + +<table className="vespa-table"> + <thead> + <tr> + <th>name</th> + <th>occurrence</th> + <th>description</th> + </tr> + </thead> + <tbody> + <tr> + <td>use-model</td> + <td>One</td> + <td>Enable or disable the use of significance models specified in <a href="/en/reference/applications/services/search#significance" aria-label="service.xml">service.xml</a>.</td> + </tr> + </tbody> +</table> + +For more details see [Significance Model.](/en/ranking/significance) + +## document-summary + +Contained in [schema](#schema). An explicitly defined document summary. By default, a document summary named `default` is created. Using this element, other document summaries containing a different set of fields can be created. + +```bash +document-summary [name] inherits [document-summary1], [document-summary2], ... { + [body] +} +``` + +The `inherits` attribute is optional. If defined, it contains the name of other document summaries in the same schema (or a parent) which this summary should inherit the fields of. Refer to [schema inheritance](/en/schemas/inheritance-in-schemas) for examples. + +The body of a document summary consists of: + +<table className="vespa-table"> + <thead> + <tr> + <th>Name</th> + <th>Occurrence</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>from-disk</td> + <td>Zero or one</td> + <td>Mark this summary as accessing fields on disk. This will silence the warnings that this summary reads from disk; in the console for prod deployments, on the command line for manual deployments. Read more in <a href="/en/querying/document-summaries#performance" aria-label="Document Summaries">Document Summaries</a> on how to avoid disk access to speed up queries.</td> + </tr> + <tr> + <td><a href="#summary" aria-label="summary">summary</a></td> + <td>Zero to many</td> + <td>A summary field in this document summary.</td> + </tr> + <tr> + <td>omit-summary-features</td> + <td>Zero or one</td> + <td>Specifies that <a href="#summary-features" aria-label="summary-features">summary-features</a> should be omitted from this document summary. Use this to reduce CPU cost in <a href="/en/applications/searchers#multiphase-searching" aria-label="multiphase searching">multiphase searching</a> when using multiple document summaries to fill hits, and only some of them need the summary features that are specified in the <a href="#rank-profile" aria-label="rank-profile">rank-profile</a>.</td> + </tr> + </tbody> +</table> + +Use the [summary](/en/reference/api/query#presentation.summary) query parameter to choose a document summary in searches or in [grouping](/en/reference/querying/grouping-language#summary). See also [document summaries](/en/querying/document-summaries). + +## documentid + +Available since `Vespa 8.691.19` . + +Contained in [schema](#schema). Sets whether document IDs are stored on disk only or made an attribute by also storing them in memory. Changing this setting will only take effect on the next restart of the `searchnode` service. + +Making the document IDs an attribute allows to return [Document IDs in search results](/en/schemas/documents#docid-in-results) and to visit Document IDs without disk access, cf. [Export documents](/en/operations/data-management#export-documents). + +```bash +documentid: [setting] +``` +The settings are: + +<table className="vespa-table"> + <thead> + <tr> + <th>Setting</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>from-disk</td> + <td>Store document IDs on disk only. This is the default setting.</td> + </tr> + <tr> + <td>attribute</td> + <td>Make the document IDs an attribute by also storing them in memory.</td> + </tr> + </tbody> +</table> + +## stemming + +Contained in [field](#field), [schema](#schema) or [index](#index). Sets how to stem a field or an index, or how to stem by default. Typically used with [OpenNLP linguistics](/en/linguistics/linguistics-opennlp). Read more on [stemming](/en/linguistics/linguistics-opennlp#stemming). + +```bash +stemming: [stemming-type] +``` +The stemming types are: + +<table className="vespa-table"> + <thead> + <tr> + <th>Type</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>none</td> + <td>No stemming: Keep words unchanged</td> + </tr> + <tr> + <td>best</td> + <td>Use the 'best' stem of each word according to some heuristic scoring. This is the default setting</td> + </tr> + <tr> + <td>shortest</td> + <td>Use the shortest stem of each word</td> + </tr> + <tr> + <td>multiple</td> + <td>Use multiple stems. Retains all stems returned from the linguistics library</td> + </tr> + </tbody> +</table> + +<Info> + **Note:** When combining multiple fields in a [fieldset](#fieldset), all fields should use the same stemming type. +</Info> + +## normalizing + +Contained in [field](#field). Sets [normalizing](/en/linguistics/linguistics-opennlp#normalization) to be done on this field. The default is to normalize. + +```js +normalizing: [normalizing-type] +``` + +<table className="vespa-table"> + <thead> + <tr> + <th>Type</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>none</td> + <td>No normalizing.</td> + </tr> + </tbody> +</table> + +## dictionary + +Contained in [field](#field), and specifies details of the dictionary used in the inverted index of the field. Applies only to [attributes](#attribute) annotated with `fast-search`. You can specify either `btree` or `hash`, or both. If both are specified, btree is used for range/prefix and hash for exact lookups. + +### Dictionary Types + +**btree** (Default): Provides good performance for exact, prefix, and range lookups. Recommended for most use cases. Find more details in [attribute index structures](/en/content/attributes#index-structures). + +**hash**: Optimized for fields with high cardinality (many unique values), such as unique ID fields where each posting list contains only one item. + +<Info> + **Note:** When using `hash`, prefix searches for strings and range searches for numeric fields will fall back to a full scan. This is primarily beneficial when you have many unique terms with few occurrences each, where dictionary lookup costs would otherwise be significant. +</Info> + +### Case Handling for String Fields + +For string attributes, you can specify how character case is handled: + +- **uncased** (Default): Case-insensitive - 'bear', 'Bear', and 'BEAR' are treated as identical +- **cased**: Case-sensitive - 'bear', 'Bear', and 'BEAR' are treated as different terms + +This setting is automatically checked against the field's [match:casing](#match) setting. + +### Important Rules for String Fields with Dictionaries + +**For `btree` dictionaries**: Both `cased` and `uncased` options are supported. + +**For `hash` dictionaries**: Only `cased` is supported for string fields. When using `hash` dictionaries: + + - You **must** set `match: cased` on the field + - You **must** include `cased` in the dictionary block + +```js +dictionary { + hashcased} +``` + +### Example: Case-Sensitive Hash Dictionary + +```js +field id_str type string { + indexing: summary | attribute + attribute: fast-searchmatch: casedrank: filter + dictionary { + hashcased} +} +``` + +## attribute + +Contained in [field](#field) or [struct-field](#struct-field). Specifies a property of an index structure attribute: + +```bash +attribute [attribute-name]: [property] +``` +or +```bash +attribute [attribute-name] { + [property] + [property] + … +} +``` +Read the [introduction to attributes](/en/content/attributes). If the attribute name is specified, it will be used instead of the field name as the name of the attribute. +<Warning> + **Deprecated:** Deprecated, use a field with the wanted name outside the document instead. +</Warning> +Actions required when [adding or modifying attributes](#modifying-schemas). Properties: + +<table className="vespa-table"> + <thead> + <tr> + <th>Property</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>fast-search</td> + <td>Create a dictionary/index structure to speed up search in the attribute. <a href="/en/content/attributes#index-structures" aria-label="Read more">Read more</a>.</td> + </tr> + <tr> + <td>fast-access</td> + <td>If <a href="/en/reference/applications/services/content#searchable-copies" aria-label="searchable-copies">searchable-copies</a> < <a href="/en/reference/applications/services/content#redundancy" aria-label="redundancy">redundancy</a>, use _fast-access_ to load the attribute in memory on all nodes with a document replica. Use this for fast access when doing <a href="/en/writing/reads-and-writes" aria-label="partial updates">partial updates</a> and when used in a <a href="/en/reference/applications/services/content#documents" aria-label="selection expression">selection expression</a> for garbage collection. If <a href="/en/reference/applications/services/content#searchable-copies" aria-label="searchable-copies">searchable-copies</a> == <a href="/en/reference/applications/services/content#redundancy" aria-label="redundancy">redundancy</a> (default), this property is a no-op. <a href="/en/performance/sizing-feeding#redundancy-settings" aria-label="Read more">Read more</a>.</td> + </tr> + <tr> + <td>fast-rank</td> + <td>Only supported for <a href="/en/ranking/tensor-user-guide" aria-label="tensor">tensor</a> field types with at least one mapped dimension. Ensures that the per-document tensors are stored in-memory using a format that is more optimal for <a href="/en/reference/ranking/ranking-expressions" aria-label="ranking expression">ranking expression</a> evaluation. This comes at the cost of using more memory. Without this setting, these tensors are serialized in-memory, which requires deserialization as part of ranking expression evaluation. See <a href="/en/performance/feature-tuning#tensor-ranking" aria-label="tensor performance">tensor performance</a>.</td> + </tr> + <tr> + <td>paged</td> + <td>This can reduce the memory footprint by allowing paging the attribute data out of memory to disk. Not supported for <a href="#tensor" aria-label="tensor">tensor</a> with fast-rank and <a href="#predicate" aria-label="predicate">predicate</a> types. See <a href="/en/content/attributes#paged-attributes" aria-label="paged attributes">paged attributes</a> for details. Do not enable _paged_ before fully understanding the consequences.</td> + </tr> + <tr> + <td><a href="#sorting" aria-label="sorting">sorting</a></td> + <td>The sort specification for this attribute.</td> + </tr> + <tr> + <td><a href="#distance-metric" aria-label="distance-metric">distance-metric</a></td> + <td>Specifies the distance metric to use with the <a href="/en/reference/querying/yql#nearestneighbor" aria-label="nearestNeighbor">nearestNeighbor</a> query operator. Only relevant for tensor attribute fields.</td> + </tr> + <tr> + <td>mutable</td> + <td>Marks the attribute as a special mutable attribute that can be updated by a <a href="#mutate" aria-label="mutate">mutate</a> operation during query evaluation.</td> + </tr> + </tbody> +</table> + +An attribute is [multivalued](/en/querying/searching-multivalue-fields) if assigning it multiple values during indexing, by using a multivalued field type like array or map, or by using e.g. [split](/en/reference/writing/indexing-language#split) / [for\_each](/en/reference/writing/indexing-language#for_each) or by letting multiple fields write their value to the attribute field. + +Note that [normalizing](#normalizing) and [tokenization](/en/linguistics/linguistics-opennlp#tokenization) is not supported for attribute fields. + +Queries in attribute fields are not normalized, nor stemmed. Use [index](#index) on fields to enable. Both _index_ and _attribute_ can be set on a field. + +## sorting + +Contained in [attribute](#attribute) or [field](#field). Specifies how sorting should be done. + +```bash +sorting : [property] +``` +or +```js +sorting { + [property] + … +} +``` + +<table className="vespa-table"> + <thead> + <tr> + <th>Property</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>order</td> + <td><code>{`ascending`}</code> (default) or <code>{`descending`}</code>. Used unless overridden using <a href="/en/reference/querying/yql#function" aria-label="order by">order by</a> in query.</td> + </tr> + <tr> + <td>function</td> + <td><a href="/en/reference/querying/yql#function" aria-label="Sort function">Sort function</a>: <code>{`uca`}</code> (default), <code>{`lowercase`}</code> or <code>{`raw`}</code>. Note that if no language or locale is specified in the query, the field, or generally for the query, <code>{`lowercase`}</code> will be used instead of <code>{`uca`}</code>. See <a href="/en/reference/querying/yql#order-by" aria-label="order by">order by</a> for details.</td> + </tr> + <tr> + <td>strength</td> + <td><a href="/en/reference/querying/yql#strength" aria-label="UCA sort strength">UCA sort strength</a>, default <code>{`primary`}</code> - see <a href="/en/reference/querying/yql#strength" aria-label="strength">strength</a> for values. Values set in the query override the schema definition.</td> + </tr> + <tr> + <td>locale</td> + <td><a href="/en/reference/querying/yql#locale" aria-label="UCA locale">UCA locale</a>, default none, indicating that it is inferred from the query. It should only be set here if the attribute is filled with data in one language only. See <a href="/en/reference/querying/yql#locale" aria-label="locale">locale</a> for details. Values set in the query override the schema definition.</td> + </tr> + </tbody> +</table> + +## distance-metric + +Specifies the distance metric to use with the [nearestNeighbor](/en/reference/querying/yql#nearestneighbor) query operator to calculate the distance between document positions and the query position. Only relevant for tensor attribute fields, where each tensor holds one or multiple vectors. + +Which distance metric to use depends on the model used to produce the vectors; it must match the distance metric used during representation learning (model learning). If you are using an "off-the-shelf" model to vectorize your data, please ensure that the distance metric matches the distance metric suggested for use with the model. Different types of vectorization models use different types of distance metrics. + +<Danger> + **Important:** When changing the `distance-metric` or `max-links-per-node`, the content nodes must be restarted to rebuild the HNSW index - see [changes that require restart but not re-feed](#changes-that-require-restart-but-not-re-feed) +</Danger> + +The calculated distance will be used to select the closest hits for _nearestNeighbor_ query operator, but also to build the [HNSW](/en/querying/approximate-nn-hnsw) index (if specified) and to produce the [distance](/en/reference/ranking/rank-features#distance(dimension,name)) and [closeness](/en/reference/ranking/rank-features#closeness(dimension,name)) ranking features. + +```bash +distance-metric: [metric] +``` +These are the available metrics; the expressions given for _distance_ and _closeness_ assume a query vector _qv = [x0, x1, ...]_ and an attribute vector _av = [y0, y1, ...]_ with same dimension of size _n_ for all vectors. + +<table className="vespa-table"> + <thead> + <tr> + <th>METRIC</th> + <th>DESCRIPTION</th> + <th>DISTANCE</th> + <th>CLOSENESS</th> + </tr> + </thead> + <tbody> + <tr> + <td>euclidean</td> + <td>The normal <a href="#euclidean" aria-label="euclidean">euclidean</a> (aka L2) distance.</td> + <td>$d = \sqrt{(x_1 - y_1)^2 + (x_2 - y_2)^2 + etc + (x_n - y_n)^2}$ <br/>range: $[0, \infty)$</td> + <td>$\frac{1}{1 + d}$</td> + </tr> + <tr> + <td>angular</td> + <td>The <a href="#angular" aria-label="angle">angle</a> between $q_v$ and $a_v$ vectors.</td> + <td>$d = \cos^{-1}((qa)/(q a))$ <br/>range: $[0, \pi]$</td> + <td>$\frac{1}{1 + d}$</td> + </tr> + <tr> + <td>dotproduct</td> + <td>Used for <a href="#dotproduct" aria-label="maximal inner product search">maximal inner product search</a>.</td> + <td>$d = -(\vec{q} \cdot \vec{a})$ <br/>range: $[-\infty, +\infty]$</td> + <td>$-d = \vec{q} \cdot \vec{a}$</td> + </tr> + <tr> + <td>prenormalized-angular</td> + <td>Assumes normalized vectors, see <a href="#prenormalized-angular" aria-label="note">note</a> below.</td> + <td>$d = 1.0 - ((qa)/(q a))$ <br/>range: $[0,2]$</td> + <td>$\frac{1}{1 + d}$</td> + </tr> + <tr> + <td>geodegrees</td> + <td>Assumes geographical coordinates, see <a href="#geodegrees" aria-label="note">note</a> below.</td> + <td>$d =$ great-circle (km) <br/>range: $[0, 20015]$</td> + <td>$\frac{1}{1 + d}$</td> + </tr> + <tr> + <td>hamming</td> + <td>Only useful for binary tensors using int8 precision, see <a href="#hamming" aria-label="note">note</a> below.</td> + <td>$d = \sum_{i=1}^{n} \mathrm{popcount}(x_i \oplus y_i)$ <br/>range: $[0, 8n]$</td> + <td>$\frac{1}{1 + d}$</td> + </tr> + </tbody> +</table> + +### euclidean + +The default metric is [Euclidean distance](https://en.wikipedia.org/wiki/Euclidean_distance) which is just the length of a line segment between the two points. To compute the Euclidean distance directly in a ranking expression instead of fetching one already computed in a nearestNeighbor query operator, use the [Euclidean\_distance helper function](/en/reference/ranking/ranking-expressions#euclidean-distance-t): + +```js +function mydistance() { + expression: euclidean_distance(attribute(myembedding), query(myqueryvector), mydim)) + } +``` + +### angular + +The _angular_ distance metric computes the _angle_ between the vectors. Its range is `[0,pi]`, which is the angular distance. This is also known as ordering by [cosine similarity](https://en.wikipedia.org/wiki/Cosine_similarity) where the score function is just the cosine of the angle. To compute the angular distance directly in a ranking expression, use the [cosine\_similarity helper function](/en/reference/ranking/ranking-expressions#cosine-similarity-t): + +```js +function angle() { + expression: acos(cosine_similarity(attribute(myembedding), query(myqueryvector), mydim)) + } +``` + Conversely, the cosine similarity can be recovered from the [distance rank-feature](/en/reference/ranking/rank-features#distance(dimension,name)) when using a nearestNeighbor query operator: + +```js +rank-profile cosine { + first-phase { + expression: cos(distance(field, myembedding)) + } + } +``` +If possible, it's slightly better for performance to normalize both query and document vectors to the same L2 norm and use the `prenormalized-angular` metric instead; but note that returned distance and closeness will be different. + +### dotproduct + +The _dotproduct_ distance metric is used to _mathematically transform_ a "maximum inner product" search into a form where it can be solved by nearest neighbor search, where the dotproduct is used as a score directly (large positive dotproducts are considered "nearby"). Internally, an extra dimension is added (ensuring that all vectors are normalized to the same length) and a distance similar to _prenormalized-angular_ is used to build the HNSW index. For details, see [this high level guide](https://towardsdatascience.com/maximum-inner-product-search-using-nearest-neighbor-search-algorithms-c125d24777ef) based on [section 3.1 Order Preserving Transformations in this paper](https://www.microsoft.com/en-us/research/wp-content/uploads/2016/02/XboxInnerProduct.pdf). + +Note that the _distance_ and _closeness_ rank-features will not have the usual semantic meanings when using the _dotproduct_ distance metric. In particular, _closeness_ will just return the dot product ∑n(xi\*yi) which may have any negative or positive value, and _distance_ is just the negative dot product. If a normalized closeness in range `[0,1]` is needed, an appropriate [sigmoid function](https://en.wikipedia.org/wiki/Sigmoid_function) must be applied. For example, if your attribute is named "foobar", and the maximum dotproduct seen is around 4000, the expression `sigmoid(0.001*closeness(field,foobar))` could be a possible choice. + +The _dotproduct_ distance metric is useful for some vectorization models, including matrix factorization, that use "maximum inner product" (MIP), with vectors that aren't normalized. These models use both direction and magnitude. + +### prenormalized-angular + +The _prenormalized-angular_ distance metric **must only be used** when **both** query and document vectors are normalized. This metric was previously named "innerproduct" and required unit-length vectors. The new version computes the length of the query vector once and assumes all other vectors are of the same length. + +Using _prenormalized-angular_ with vectors that are not normalized causes unpredictable nearest neighbor search, and is observed to give bad results both for performance and quality. + +The length, magnitude, or norm of a vector _x_ is calculated as `length = sqrt(sum(pow(xi,2)))`. The unit length normalized vector is then given by `[xi/length]`. Zero vectors may not be used at all. + +The Vespa _prenormalized-angular_ computes the [cosine similarity](https://en.wikipedia.org/wiki/Cosine_similarity) and uses `1.0 - cos(angle)` as the distance metric. It gives exactly the same ordering as `angular` distance, but with a distance in the range [0,2], since cosine similarity has range [1,-1], so the end result is 0.0 for same direction vectors, 1.0 for a right angle, and 2.0 for vectors with exactly opposite directions. Getting the cosine score (or angle) is therefore easy: + +```js +rank-profile cosine { + first-phase { + expression: 1.0 - distance(field, embedding) + } + function angle() { + expression: acos(1.0 - distance(field, embedding)) + } + } +``` + To compute the cosine similarity directly in a ranking expression instead of fetching one already computed in a nearestNeighbor query operator, use the [cosine\_similarity helper function](/en/reference/ranking/ranking-expressions#cosine-similarity-t): + +```js +function mysimilarity() { + expression: cosine_similarity(attribute(myembedding), query(myqueryvector), mydim)) + } +``` + +### geodegrees + +The _geodegrees_ distance metric is only valid for geographical coordinates (two-dimensional vectors containing latitude and longitude on Earth, in degrees). It computes the great-circle distance (in kilometers) between two geographical points using the [Haversine formula](https://en.wikipedia.org/wiki/Haversine_formula). See [geodegrees system test](https://github.com/vespa-engine/system-test/blob/master/tests/search/nearest_neighbor/geo.sd) for an example. + +### hamming + +The [Hamming distance](https://en.wikipedia.org/wiki/Hamming_distance) metric counts the number of dimensions where the vectors have different coordinates. This isn't useful for floating-point data since it means you only get 1 bit of information from each floating-point number. Instead, it should be used for binary data, where each bit is considered a separate coordinate. Practically, this means you should use the `int8` [cell value type](/en/performance/feature-tuning#cell-value-types) for your tensor, with the usual encoding from bit pattern to numerical value, for example: + +- `00000000` → `0` (hex `00`) +- `00010001` → `17` (hex `11`) +- `00101010` → `42` (hex `2A`) +- `01111111` → `127` (hex `7F`) +- `10000000` → `-128` (hex `80`) +- `10000001` → `-127` (hex `81`) +- `11111110` → `-2` (hex `FE`) +- `11111111` → `-1` (hex `FF`) + +Feeding data for this use case may be done with ["hex dump"](/en/reference/schemas/document-json-format#tensor-hex-dump) format instead of numbers in range `[-128,127]` both to have a more natural format for representing binary data, and to avoid the overhead of parsing a large JSON array of numbers. + +## bolding + +Contained in [field](#field) or [summary](#summary). Highlight matching query terms in the [summary](#summary): + +```bash +bolding: on +``` + +The default is no bolding, set `bolding: on` to enable it. Note that this command is overridden by `summary: dynamic`. If both are specified, bolding will be ignored. The difference between using bolding instead of `summary: dynamic` is the latter will provide a dynamic abstract in addition to highlighting query terms, while the first only highlights. Bolding is only supported for [index](#indexing-index) fields of type string or array\<string\>. + +The default XML element used to highlight the search terms is \<hi\> - to override, set _container.qr-searchers_ configuration. Example using `<strong>`: + +```xml +<container> + <search> + <config name="container.qr-searchers"> + <tag> + <bold> + <open><strong></open> + <close></strong></close> + </bold> + <separator>...</separator> + </tag> + </config> + </search> +</container> +``` + +Maximum field byte length for bolding is 64Mb - field values larger than this will be represented as a snippet as in `summary: dynamic`. + +## id + +Contained in [field](#field). Sets the numerical ID of this field. All fields have a document-internal ID internally for transfer and storage. IDs are usually determined programmatically as a 31-bit number. Some storage and transfer space can be saved by instead explicitly setting IDs to a 7-bit number. + +```js +id: [positive integer] +``` + +An ID must satisfy these requirements: + +- Must be a positive integer +- Must be less than 100 or larger than 127 +- Must be unique within the document and all documents this document inherits + +## index + +Contained in [field](#field) or [schema](#schema). Sets index parameters. + +Content in [string](#string)-fields with _index_ is [normalized](#normalizing) and [tokenized](/en/linguistics/linguistics-opennlp#tokenization) by default. The field can be single- or multivalued (e.g. `array<string>`). + +For [tensor](#tensor)-typed fields, _index_ creates an [HNSW](#index-hnsw) index for [Approximate Nearest Neighbor](/en/querying/nearest-neighbor-search-guide) queries, with a default [euclidean](#euclidean) distance metric. The index is built after a [content node restart](#changes-that-require-restart-but-not-re-feed) (automated on Vespa Cloud). + +Examples: + +```js +index [index-name]: [property] +``` +or +```js +index [index-name] { + [property] + [property] + … +} +``` + +<Warning> + **Deprecated:** If `index-name` is specified, it will be used instead of the field name as the name of the index. This use is deprecated, use a synthetic field with the wanted name outside the `document` block instead - see an [example](/en/writing/indexing#date-indexing). +</Warning> +Parameters: + +<table className="vespa-table"> + <thead> + <tr> + <th>Property</th> + <th>Occurrence</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td><a href="#stemming" aria-label="stemming">stemming</a></td> + <td>Zero to one</td> + <td>Set the stemming of this index. Indexes without a stemming setting get their stemming setting from the fields added to the index. Setting this explicitly is useful if fields with conflicting stemming settings are added to this index.</td> + </tr> + <tr> + <td>arity</td> + <td>One (mandatory for predicate fields), else zero.</td> + <td>Set the <a href="/en/schemas/predicate-fields#index-size" aria-label="arity value for a predicate field">arity value for a predicate field</a>. The data type for the containing field must be <code>{`predicate`}</code>.</td> + </tr> + <tr> + <td>lower-bound</td> + <td>Zero to one</td> + <td>Set the <a href="/en/schemas/predicate-fields#upper-and-lower-bounds" aria-label="lower bound value for a predicate field">lower bound value for a predicate field</a>. The data type for the containing field must be <code>{`predicate`}</code>.</td> + </tr> + <tr> + <td>upper-bound</td> + <td>Zero to one</td> + <td>Set the <a href="/en/schemas/predicate-fields#upper-and-lower-bounds" aria-label="upper bound value for predicate fields">upper bound value for predicate fields</a>. The data type for the containing field must be <code>{`predicate`}</code>.</td> + </tr> + <tr> + <td>dense-posting-list-threshold</td> + <td>Zero to one</td> + <td>Set the <a href="/en/schemas/predicate-fields#dense-posting-list-threshold" aria-label="dense posting list threshold value for predicate fields">dense posting list threshold value for predicate fields</a>. The data type for the containing field must be <code>{`predicate`}</code>.</td> + </tr> + <tr> + <td>enable-bm25</td> + <td>Zero to one</td> + <td>Enable this index field to be used with the <a href="/en/reference/ranking/rank-features#bm25" aria-label="bm25 rank feature">bm25 rank feature</a>. This creates posting lists for the <a href="/en/content/proton#index" aria-label="indexes">indexes</a> for this field that has interleaved features in the document ID streams. This makes it fast to compute the _bm25_ score. See the <a href="/en/ranking/bm25" aria-label="BM25 reference">BM25 reference</a> for details and example use.</td> + </tr> + <tr> + <td><a href="#index-hnsw" aria-label="hnsw">hnsw</a></td> + <td>Zero to one</td> + <td>Specifies optional parameters for an HNSW index to enable faster, approximate nearest neighbor search. Only supported for tensor attribute fields with tensor types with:</td> + </tr> + </tbody> +</table> +- One indexed dimension - single vector per document +- One or more mapped dimensions and one indexed dimension - multiple vectors per document + + Used in combination with the [nearestNeighbor](/en/reference/querying/yql#nearestneighbor) query operator. | + +## hnsw + +Contained in [index](#index). Specifies optional parameters for an HNSW index to enable faster, approximate nearest neighbor search using the [nearestNeighbor](/en/reference/querying/yql#nearestneighbor) query operator. + +<Info> + **Note:** Specifying the `index` keyword in the [indexing](#indexing) statement of a tensor creates an HNSW index with default settings, even if this block is not specified! +This implements a modified version of the Hierarchical Navigable Small World (HNSW) graphs algorithm ([paper](https://arxiv.org/abs/1603.09320)). +</Info> + +Only supported for the following tensor attribute field types: + +- Single vector per document: Tensor type with one indexed dimension. Example: `tensor<float>(x[3])` +- Multiple vectors per document: Tensor type with one or more mapped dimensions and one indexed dimension. Examples: `tensor<float>(m{},x[3])`, `tensor<float>(m{},n{},x[3])` + +HNSW indexes are not supported in [streaming search](/en/performance/streaming-search#differences-in-streaming-search). + +```js +hnsw { + [parameter]: [value] + [parameter]: [value] + ... +} +``` + +The following parameters are used when building the index graph: + +<table className="vespa-table"> + <thead> + <tr> + <th>Parameter</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>max-links-per-node</td> + <td>Specifies how many links per HNSW node to select when building the graph. The default value is 16. In <a href="https://github.com/nmslib/hnswlib/blob/master/ALGO_PARAMS.md" aria-label="HNSWlib">HNSWlib</a> (implementation based on the paper) this parameter is known as _M_.</td> + </tr> + <tr> + <td>neighbors-to-explore-at-insert</td> + <td>Specifies how many neighbors to explore when inserting a document in the HNSW graph. The default value is 200. In HNSWlib, this parameter is known as _ef_construction_.</td> + </tr> + </tbody> +</table> + +The [distance metric](#distance-metric) specified on the _attribute_ is used when building and searching the graph. Example: + +```js +field text_embedding type tensor<float>(x[384]) { + indexing: summary | attribute | index + attribute { + distance-metric: prenormalized-angular + } + index { + hnsw { + max-links-per-node: 24 + neighbors-to-explore-at-insert: 200 + } + } +} +``` + +See [Approximate Nearest Neighbor Search using HNSW Index](/en/querying/approximate-nn-hnsw) for examples of use, and see [Approximate Nearest Neighbor Search in Vespa - Part 1](https://blog.vespa.ai/approximate-nearest-neighbor-search-in-vespa-part-1/) blog post for how the Vespa team selected HNSW as the baseline algorithm for extension and integration in Vespa. + +## indexing + +Contained in [field](#field) or [struct-field](#struct-field). One or more Indexing Language instructions used to produce index, attribute and summary data from this field. Indexing instructions have pipeline semantics similar to Unix shell commands. The value of the field enters the pipeline during indexing, and the pipeline puts the value into the desired index structures, possibly doing transformations and pulling in other values along the way. + +```bash +indexing: [index-statement] +``` +or +```js +indexing { + [indexing-statement]; + [indexing-statement]; + … +} +``` + +If the field containing this is defined outside the document, it must start with an indexing statement which outputs a value (either "input [fieldname]" to fetch a field value, or a literal, e.g, "some-value" ). Fields in documents will use the value of the enclosing field as input (input [fieldname]) if one isn't explicitly provided. + +Specify the operations separated by the pipe (`|`) character. For advanced processing needs, use the [indexing language](/en/reference/writing/indexing-language), or write a [document processor](/en/applications/document-processors). Supported expressions for fields are: + +<table className="vespa-table"> + <thead> + <tr> + <th>expression</th> + <th>description</th> + </tr> + </thead> + <tbody> + <tr> + <td>attribute</td> + <td><a href="/en/content/attributes" aria-label="Attribute">Attribute</a> is used to make a field available for sorting, grouping, ranking and searching using <a href="#match" aria-label="match">match</a> mode <code>{`word`}</code>.</td> + </tr> + <tr> + <td>index</td> + <td>Creates a searchable <a href="/en/content/proton#index" aria-label="index">index</a> for the values of this field using <a href="#match" aria-label="match">match</a> mode <code>{`text`}</code>. By default, the index name will be the same as the name of the schema field. Use a <a href="#fieldset" aria-label="fieldset">fieldset</a> to combine fields in the same set for searching.</td> + </tr> + <tr> + <td>set_language</td> + <td>Sets document language - <a href="/en/reference/writing/indexing-language#set_language" aria-label="details">details</a>.</td> + </tr> + <tr> + <td>summary</td> + <td>Includes the value of this field in a <a href="/en/reference/writing/indexing-language#summary" aria-label="summary">summary</a> field. Modify summary output by using <a href="#summary" aria-label="summary:">summary:</a> (e.g., to generate dynamic teasers).</td> + </tr> + </tbody> +</table> + +When combining both `index` and `attribute` in the indexing statement for a field, e.g `indexing: summary | attribute | index`, the [match](#match) mode becomes `text` for the field. So searches in this field will not search the contents in the [attribute](#attribute) but the index. + +Find examples and more details in the [Text Matching](/en/querying/text-matching) guide. + +## linguistics + +Sets a linguistics 'profile' deciding how content and searches against this field should be linguistically processed. The profile value is propagated to the [linguistics component](/en/linguistics/linguistics) which can be configured to do profile-specific processing. + +```js +linguistics { + profile: myIndexAndSearchProfile +} +``` +or +```js +linguistics { + profile { + index: myIndexProfile + search: mySearchProfile + } +} +``` + +## match + +Contained in [field](#field), [fieldset](#fieldset) or [struct-field](#struct-field). Sets the matching method to use for this field to something other than the default token matching. + +```bash +match: [property] +``` +or +```js +match { + [property] + [property] + … +} +``` + +Whether the match type is `text`, `word` or `exact`, all term matching will be done after [normalization](/en/linguistics/linguistics-opennlp#normalization) and locale-independent lowercasing (in that order). + +Find examples and more details in the [Text Matching](/en/querying/text-matching) guide. Also see search using [regular expressions](/en/reference/querying/yql#matches). + +<table className="vespa-table"> + <thead> + <tr> + <th>Property</th> + <th>Valid with</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>text</td> + <td>index</td> + <td>The default for string fields with <code>{`index`}</code>. Can not be combined with exact matching. The field is matched per <a href="/en/linguistics/linguistics-opennlp#tokenization" aria-label="token">token</a>.</td> + </tr> + <tr> + <td>exact</td> + <td>index, attribute</td> + <td>Can not be combined with *text* matching. The field is matched *exactly*: Strings containing any characters whatsoever will be indexed and matched as-is. Lowercasing is still performed unless <code>{`match: cased`}</code> is also used. In queries, the exact match string ends at the exact match terminator (below). <br/><br/> A field with <code>{`match: exact`}</code> is considered to be a <a href="#filter" aria-label="filter field">filter field</a>, just as if <code>{`rank: filter`}</code> was specified. This is because there is only one word per field (or per item in the case of multivalued types such as <code>{`array<string>`}</code>), so there is little ranking information. Turn off the implicit <code>{`rank: filter`}</code> by adding <code>{`rank: normal`}</code>.</td> + </tr> + <tr> + <td>exact-terminator</td> + <td>index, attribute</td> + <td>Only valid for <code>{`match: exact`}</code>. Default is <code>{`@@`}</code>. Specify terminator in <a href="/en/reference/api/query#model.querystring" aria-label="query strings">query strings</a>. If the query strings can contain <code>{`@@`}</code>, set a different terminator, or use <code>{`match: word`}</code>, see below. Example - use: <pre><code>{`{ exact exact-terminator: "@%" }`}</code></pre>on a field called <code>{`tag`}</code> to make query <code>{`tag:a b c!@%`}</code> match documents with the string *a b c!* <br/><br/> Example using the default terminator: If <code>{`tag`}</code> is an exact match field, the query: <pre><code>{`someword AND (tag:!*!@@ OR tag:(kanoo)@@)`}</code></pre> matches documents with <code>{`someword`}</code> and either <code>{`!*!`}</code> or <code>{`(kanoo)`}</code> as a tag. Note that without the <code>{`@@`}</code> terminating the second tag string, the second tag value would be <code>{`(kanoo))`}</code>.</td> + </tr> + <tr> + <td>word</td> + <td>index, attribute</td> + <td>This is the default matching mode for <a href="/en/content/attributes" aria-label="string attributes">string attributes</a>. It cannot be combined with *text* matching. Match word means that the entire content of the field is indexed as a single word. Word matching is like exact matching, but with more advanced query parsing. The query terms are heuristically parsed, taking into account some usual query syntax characters. One can also use double quotes to include spaces, stars, or exclamation marks. Example: If <code>{`artist`}</code> is a string attribute, the query: foo AND (artist:"'N Sync" OR artist:"*NSYNC" OR artist:A*teens OR artist:"Wham!") matches documents with <code>{`foo`}</code> and at least one of <code>{`'N Sync`}</code> or <code>{`*NSYNC`}</code> or <code>{`A*teens`}</code> or <code>{`Wham!`}</code> in the artist field Note that without the quotes, the space in <code>{`'N Sync`}</code> would end that word and would result in a search for just <code>{`'N`}</code>, similarly the <code>{`!`}</code> would mean to increase the weight of a <code>{`Wham`}</code> term if not quoted.</td> + </tr> + <tr> + <td>prefix</td> + <td>attribute</td> + <td>Has no effect, as <a href="/en/content/attributes" aria-label="attributes">attributes</a> always support prefix searches. Prefix matching must be <a href="/en/reference/querying/yql#prefix" aria-label="specified in the query">specified in the query</a>. See also <a href="/en/reference/querying/yql#matches" aria-label="regular expressions">regular expressions</a>.</td> + </tr> + <tr> + <td>substring</td> + <td><a href="/en/performance/streaming-search#differences-in-streaming-search" aria-label="Streaming mode">Streaming mode</a> only</td> + <td>Set default match mode to *substring* for the field. Only available in streaming search. As the data structures in streaming search support substring searches, one can always set substring matching in the query, without setting the field to the substring default. Also see <a href="/en/reference/querying/yql#matches" aria-label="regular expressions">regular expressions</a>.</td> + </tr> + <tr> + <td>suffix</td> + <td><a href="/en/performance/streaming-search" aria-label="Streaming mode">Streaming mode</a> only</td> + <td>Like substring above.</td> + </tr> + <tr> + <td>uncased</td> + <td>index, attribute</td> + <td>Use case-insensitive matching (the default).</td> + </tr> + <tr> + <td>cased</td> + <td>index, attribute</td> + <td>Use case-sensitive matching. Usually only used together with <code>{`match: exact`}</code> or <code>{`match: word`}</code> modes. When using <code>{`match: text`}</code>, note that if you are using a custom <a href="/en/linguistics/linguistics-custom" aria-label="linguistics implementation">linguistics implementation</a>, this will only have effect for string index fields if that implementation produces cased tokens.</td> + </tr> + <tr> + <td>max-length</td> + <td>index</td> + <td>Limit the length of the field that will be used for matching. By default, only the first 1M characters are indexed. When adjusting this limit, it might also be needed to adjust <a href="#max-occurrences" aria-label="max-occurrences">max-occurrences</a>.</td> + </tr> + <tr> + <td>max-occurrences</td> + <td>index</td> + <td>Configure the maximum number of occurrences that will be indexed for each unique token/term in the field for a given document. If this limit is reached, consecutive occurrences of the same token/term are ignored for that document. The default value is 10000. <br/><br/>Adjusting this limit might be needed when using the <a href="/en/reference/querying/yql#phrase" aria-label="phrase">phrase</a>, <a href="/en/reference/querying/yql#near" aria-label="near">near</a>, or <a href="/en/reference/querying/yql#onear" aria-label="onear">onear</a> query operators to query documents with large field values (see <a href="#max-length" aria-label="max-length">max-length</a>) that contain more than 10000 occurrences of common tokens/terms. When using these operators, it is only possible to match among the first *max-occurrences* of a token/term in a document.</td> + </tr> + <tr> + <td>max-token-length</td> + <td>index</td> + <td>Configure the max length of tokens that will be indexed for the field. Longer tokens are silently ignored. The unit is characters (cf. java.lang.String.length()). The default value is 1000.</td> + </tr> + <tr> + <td>gram</td> + <td>index</td> + <td>This field is matched using n-grams. For example, with the default gram size 2, the string "hi blue" is tokenized to "hi bl lu ue" both in the index and in queries to the index. <br/><br/> N-gram matching is useful mainly as an alternative to <a href="/en/linguistics/linguistics-opennlp#tokenization" aria-label="segmentation">segmentation</a> in CJK languages. Typically, it results in increased recall and lower precision. However, as Vespa usually uses proximity in ranking, the precision offset may not be of much importance. Grams consume more resources than other matching methods because both indexes and queries will have more terms, and the terms contain repetition of the same letters. On the other hand, CPU-intensive CJK segmentation is avoided. It may also be used for substring matching in general.</td> + </tr> + <tr> + <td>gram-size</td> + <td>index</td> + <td>A positive, nonzero number, default 2. Sets the gram size when gram matching is used. Example: <pre><code>{`{ gram gram-size: 3 }`}</code></pre></td> + </tr> + </tbody> +</table> + +## rank + +Contained in [field](#field), [struct-field](#struct-field) or [rank-profile](#rank-profile). Set the kind of ranking calculations that will be done for the field. Even though the actual ranking expressions decide the ranking, this setting tells Vespa which preparatory calculations and which data structures are needed for the field. + +```bash +rank [field-name]: [ranking settings] +``` +or +```js +rank { + [ranking setting] +} +``` +The field name should only be specified when used inside a rank-profile. The following ranking settings are supported in addition to the default: + +<table className="vespa-table"> + <thead> + <tr> + <th>Ranking setting</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>filter</td> + <td>Indicates that matching in this field should use fast bit vector data structures only. This saves CPU during matching, but only a few simple ranking features will be available for the field. This setting is appropriate for fields typically used for filtering or simple boosting purposes, like filtering or boosting on the language of the document. <br/><br/> - For *index* fields, this setting does not change index formats but helps choose the most compact representation when matching against the field. <br/><br/> - For *attribute* fields with *fast-search* this setting builds additional posting list representations (bit vectors) can significantly speed up query evaluation. See <a href="/en/performance/feature-tuning#when-to-use-fast-search-for-attribute-fields" aria-label="feature tuning">feature tuning</a> and <a href="/en/performance/practical-search-performance-guide" aria-label="the practical search performance guide">the practical search performance guide</a>.</td> + </tr> + <tr> + <td>normal</td> + <td>The reverse of <code>{`filter`}</code>. Matching in this field will use normal data structures and give normal match information for ranking. Used to turn off implicit <code>{`rank: filter`}</code> when using <a href="#exact" aria-label="match: exact">match: exact</a>. If both <code>{`filter`}</code> and <code>{`normal`}</code> are set somehow, the effect is as if only <code>{`normal`}</code> was specified.</td> + </tr> + </tbody> +</table> + +Related: See the [filter](/en/reference/querying/yql#filter) query annotation for how to annotate query terms as filters. + +### filter-threshold + +Contained in a [rank-profile](#rank-profile). Used to optimize query performance when searching large text [index](/en/basics/schemas#document-fields) fields, by allowing a per query combination of [rank: filter](#filter) and [rank: normal](#normal) behavior. See [profile-level filter-threshold](#filter-threshold) for how to use the same value for all _index_ fields. + +```bash +rank [field-name] { + filter-threshold: 0.05 +} +``` + +<table className="vespa-table"> + <thead> + <tr> + <th>Setting</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>filter-threshold</td> + <td>Threshold value (in the range [0.0, 1.0]) deciding when matching in this _index_ field should be treated as a filter. This happens for query terms with <a href="/en/learn/glossary#estimated-hit-ratio" aria-label="estimated hit ratios">estimated hit ratios</a> (in the range [0.0, 1.0]) that are above the _filter-threshold_. Then, fast bitvector data structures are used, similar to when the field is set to <a href="#filter" aria-label="rank: filter">rank: filter</a>. This saves CPU and Disk I/O during matching and typically results in faster query evaluation, with the downside being that only a boolean signal is available for ranking (the document being a match or not). <a href="/en/ranking/bm25" aria-label="BM25">BM25</a> handles this by assuming one occurrence of the query term in the document, and the field length being equal to the average field length.<br/><br/>Use this to optimize query performance when searching large text _index_ fields with e.g. the <a href="/en/ranking/wand#weakand" aria-label="weakAnd">weakAnd</a> query operator and <a href="/en/ranking/bm25" aria-label="BM25">BM25</a> ranking. Query terms that are common in the corpus (e.g., stopwords) are treated as filters with faster matching and simplified ranking, while other query terms are handled as usual with full ranking.<br/><br/>In testing with various text datasets (e.g., Wikipedia), a _filter-threshold_ setting of 0.05 has been shown to be a good starting point. <a href="/en/performance/feature-tuning#posting-lists" aria-label="Read more">Read more</a>.<br/><br/>This setting is only relevant for <a href="/en/basics/schemas#document-fields" aria-label="index">index</a> fields, and cannot be used in combination with <a href="#filter" aria-label="rank: filter">rank: filter</a>. Has no effect in <a href="/en/performance/streaming-search#differences-in-streaming-search" aria-label="streaming search">streaming search</a>.</td> + </tr> + </tbody> +</table> + +### element-gap + +Contained in a [rank-profile](#rank-profile). Used to specify the gap between positions in adjacent elements in multi-value fields. The default value is `infinity`. It should be specified as `infinity` or as a positive integer number. + +Consider an `array<string>` field with three elements `["a c", "x b x a x x x x x x b", "x x d"]`. Normally, distance calculation is only performed within an element, thus the minimum forward distance between occurrences for terms _a_ and _b_ is 7. By setting the `element-gap` for the field to 0, adjacent elements are considered and the minimum forward distance between occurrences for terms _a_ and _b_ becomes 3. The minimum distance between occurrences for terms _c_ and _d_ is not calculated since they are not in adjacent elements. + +The `element-gap` setting affects the [nativeProximity](/en/reference/ranking/rank-features#nativeProximity) rank feature, the [near](/en/reference/querying/yql#near) query operator and the [onear](/en/reference/querying/yql#onear) query operator. + +```bash +rank [field-name] { + element-gap: 0 +} +``` + +No restart or reindexing is required when changing this setting, it is immediately effective. + +## query-command + +Contained in [fieldset](#fieldset), [field](#field) or [struct-field](#struct-field). Specifies a function to be performed on query terms to the indexes of this field when searching. The Search Container server has support for writing Vespa Searcher plugins that process these commands. + +```js +query-command: [an identifier or quoted string] +``` + +If you write a plugin searcher that needs some index-specific configuration parameter, that parameter can be set here. + +There is one built-in query-command available: `phrase-segmenting`. If this is set, terms connected by non-word characters in user queries (such as "a.b") will be parsed to a phrase item, instead of by default, an AND item where these terms have connectivity set to 1. + +## rank-type + +Contained in [field](#field) or [rank-profile](#rank-profile). Selects the low-level rank settings to be used for this field when using `nativeRank`. + +```bash +rank-type [field-name]: [rank-type-name] +``` +The field name can be skipped inside fields. Defined rank types are: + +<table className="vespa-table"> + <thead> + <tr> + <th>Type</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>identity</td> + <td>Used for fields that contains only what this document _is_, e.g., "Title". Complete identity hits will get a high rank.</td> + </tr> + <tr> + <td>about</td> + <td>Some text which is (only) about this document, e.g. "Description". About hits get high rank on partial matches and higher for matches early in the text and repetitive matches. This is the default rank type.</td> + </tr> + <tr> + <td>tags</td> + <td>Used for simple tag fields of type tag. The tags rank type uses a logarithmic table to give more relative boost in the low range: As tags are added, they should have a significant impact on the rank score, but as more and more tags are added, each new tag should contribute less.</td> + </tr> + <tr> + <td>empty</td> + <td>Gives no relevancy effect on matches. Used for fields you just want to treat as filters.</td> + </tr> + </tbody> +</table> + +For `nativeRank`, one can specify a rank type per field. If the supported rank types do not meet requirements, one can explicitly configure the native rank features using rank-properties. See the [native rank reference](/en/reference/ranking/nativerank) for more information. + +## weakand + +Contained in [rank-profile](#rank-profile). + +Tunes the [weakAnd](/en/ranking/wand#weakand) algorithm to automatically exclude terms and documents with expected low query significance based on document frequency statistics present in the document corpus. This makes matching faster at the cost of potentially reduced recall. + +```js +weakand { + [body] +} +``` + +Note that all document frequency calculations are done using _content node-local_ document statistics (i.e. [global significance](/en/ranking/significance#global-significance-model) does not have an effect). This means results may differ across different content nodes and/or content node groups. + +The body of a `weakand` statement consists of: + +<table className="vespa-table"> + <thead> + <tr> + <th>Property</th> + <th>Occurrence</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>stopword-limit</td> + <td>Zero to one</td> + <td>A number in the range [0, 1]. Represents the maximum <a href="/en/learn/glossary#document-frequency-normalized" aria-label="normalized document frequency">normalized document frequency</a> a query term can have in the corpus (i.e. the ratio of all documents where the term occurs at least once) before it's considered a stopword and dropped entirely from being a part of the <code>{`weakAnd`}</code> evaluation. This makes matching faster at the cost of potentially producing more hits. Dropped terms are not exposed as part of ranking. <br/> Example: <br/> stopword-limit: 0.60 This will drop all query terms that occur in at least 60% of the documents.<br/> Using <code>{`stopword-limit`}</code> is similar to explicitly removing stopwords from the query up front, but has the benefit of dynamically adapting to the actual document corpus and not having to know—or specify—a set of stopwords. <a href="/en/performance/feature-tuning#posting-lists" aria-label="Read more">Read more</a>.</td> + </tr> + <tr> + <td>adjust-target</td> + <td>Zero to one</td> + <td>A number in the range [0, 1] representing <a href="/en/learn/glossary#document-frequency-normalized" aria-label="normalized document frequency">normalized document frequency</a>. Used to derive a per-query document score threshold, where documents scoring lower than the threshold will not be considered as potential hits from the <code>{`weakAnd`}</code> operator. The score threshold is selected to be equal to that of the query term whose document frequency is *closest* to the configured <code>{`adjust-target`}</code> value. <br/> This can be used to efficiently *exclude* documents that only match terms that occur very frequently in the document corpus. Such terms are likely to be stopwords that have low semantic value for the query, and excluding documents only containing them is likely to have only a minor impact on recall. <br/> This makes overall matching faster by reducing the number of hits produced by the <code>{`weakAnd`}</code> operator. <br/> Example: <pre><code>{`adjust-target: 0.01`}</code></pre> This excludes documents that only have terms that occur in more than approximately 1% of the document corpus. The actual threshold is query-specific and based on the query term score whose document frequency is closest to 1%. <br/><code>{`adjust-target`}</code> can be used together with <a href="#weakand-stopword-limit" aria-label="stopword-limit">stopword-limit</a> to efficiently prune both terms and documents with low significance when processing queries. <a href="/en/performance/feature-tuning#posting-lists" aria-label="Read more">Read more</a>.</td> + </tr> + <tr> + <td>allow-drop-all</td> + <td>Zero to one</td> + <td>A boolean value. The default behavior of <code>{`weakAnd`}</code> is to always keep at least one term (the least common one) even though it is considered a stopword. This is to avoid dropping all query terms in order to make sure that some hits are produced.<br/> If set to <code>{`true`}</code>, the <code>{`weakAnd`}</code> operator will allow removal of all query terms if they are all considered stopwords (i.e., if <code>{`stopword-limit`}</code> is set and all query terms are above the limit).<br/> This may be desired (and significantly improve query performance) if <code>{`weakAnd`}</code> is used together with another query operator, e.g. the <a href="/en/querying/nearest-neighbor-search#querying-using-nearestneighbor-query-operator" aria-label="nearestNeighbor">nearestNeighbor</a> operator. <br/> Be aware that if this is set to <code>{`true`}</code> and all query terms are considered stopwords, the <code>{`weakAnd`}</code> operator will not produce *any* hits. And by extension, if <code>{`weakAnd`}</code> is used by itself, the query may return no hits. <br/> Example: <br/> <pre><code>{`allow-drop-all: true`}</code></pre> This overrides the default behavior of <code>{`weakAnd`}</code> and allows all query terms to be dropped if they are all considered stopwords. <Danger>**Important:** Defaults to <code>{`false`}</code> if not specified.</Danger></td> + </tr> + </tbody> +</table> + +## summary-to + +<Warning> + **Deprecated:** Use [document-summary](#document-summary) instead. +</Warning> + +Contained in [field](#field) or [struct-field](#struct-field). Specifies the name of the document summaries that should contain this field. +```bash +summary-to: [summary-name], [summary-name], … +``` + +Fields with summary will always be part of the default summary regardless of this setting. Use explicit [document-summary](#document-summary) instead. See also [document summaries](/en/querying/document-summaries). + +## summary + +Contained in [field](#field) or [document-summary](#document-summary) or [struct-field](#struct-field). Declares a summary field. + +```bash +summary: [property] +``` +or +```bash +summary [name] { + [body] +} +``` +The summary _name_ can be skipped if this is set inside a field. The name will then be the same as the name of the source field. _full_ summary is the default. Long field values (like document content fields) should be made _dynamic_. The body of a summary may contain: + +<table className="vespa-table"> + <thead> + <tr> + <th>Name</th> + <th>Occurrence</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>full</td> + <td>Zero to one</td> + <td>Returns the full field value in the summary (the default).</td> + </tr> + <tr> + <td>bolding: on</td> + <td>Zero to one</td> + <td>Specifies whether the content of this field should be <a href="#bolding" aria-label="bolded">bolded</a>. Only supported for <a href="#indexing-index" aria-label="index">index</a> fields of type string or array<string>.</td> + </tr> + <tr> + <td>dynamic</td> + <td>Zero to one</td> + <td>Make the value returned in results from this summary field a *dynamic abstract* of the source field by extracting fragments of text around matching query terms. Matching query terms will also be highlighted, in similarity with the bolding feature. This highlighting is not affected by the query-argument bolding. The default XML element used to highlight query terms is <code>{`<hi>`}</code> - refer to <a href="#bolding" aria-label="bolding">bolding</a> for how to configure. *dynamic* is only supported for <a href="#indexing-index" aria-label="index">index</a> fields of type string or array<string>. For array<string> fields, a dynamic abstract is created per string item in the array.</td> + </tr> + <tr> + <td>source</td> + <td>Zero to one</td> + <td>Specifies the name of the field or fields from which the value of this summary field should be fetched. If multiple fields are specified, the value will be taken from the first field if that has a value, from the second if the first one is empty, and so on. <pre><code>{`: [field-name], [field-name], …`}</code></pre> When this is not specified, the source field is assumed to be the field with the same name as the summary field. <br/><br/> Refer to <a href="#add-or-remove-an-existing-document-field-from-document-summary" aria-label="attribute">attribute</a> and <a href="#add-or-remove-a-new-non-attribute-document-field-from-document-summary" aria-label="non-attribute">non-attribute</a> fields for modifying a schema.</td> + </tr> + <tr> + <td>to</td> + <td>Zero to one</td> + <td>Specifies the name of the document summaries that this should be included in.<br/><br/> <pre><code>{`: [document-summary-name], [document-summary-name], …`}</code></pre> This can only be specified in fields, not in the explicit document summaries. When this is not specified, the field will go to the <code>{`default`}</code> document summary.</td> + </tr> + <tr> + <td>matched-elements-only</td> + <td>Zero to one</td> + <td>Specifies that only the matched elements in a searchable <a href="#array" aria-label="array of primitive">array of primitive</a>, <a href="#weightedset" aria-label="weightedset">weightedset</a>, <a href="#array" aria-label="array of struct">array of struct</a> or <a href="#map" aria-label="map type">map type</a> field are returned as part of document summary. For array of struct or map type fields, this is typically used in accordance with the <a href="/en/reference/querying/yql#sameelement" aria-label="sameElement">sameElement</a> operator, but it can also be used when searching directly on a sub-struct field. It is also supported when the field is <a href="#import-field" aria-label="imported">imported</a>.<br/><br/> See <a href="#map" aria-label="example use">example use</a> and example schemas: <br/><br/> - <a href="https://github.com/vespa-engine/system-test/blob/master/tests/search/matched_elements_only/indexed/test.sd" aria-label="matched elements only">matched elements only</a> <br/><br/> - <a href="https://github.com/vespa-engine/system-test/blob/master/tests/search/struct_and_map_types/attribute_fields/test.sd" aria-label="array of struct and map type">array of struct and map type</a></td> + </tr> + <tr> + <td>select-elements-by</td> + <td>Zero to one</td> + <td>Use a summary feature to control which elements in an <a href="#array" aria-label="array of primitive">array of primitive</a> or <a href="#array" aria-label="array of struct">array of struct</a> field are returned as part of document summary. <pre><code>{`select-elements-by: <summary-feature-name>`}</code></pre> The summary feature used must be a tensor with a single mapped dimension. An element will be returned if its id is a label along the mapped dimension of this tensor. <br/><br/> - <a href="https://github.com/vespa-engine/system-test/blob/master/tests/search/chunk_selection/test.sd" aria-label="schema example">schema example</a></td> + </tr> + <tr> + <td>tokens</td> + <td>Zero to one</td> + <td>Make the value returned in results from this summary field be an array of the tokens indexed in the source field. Multiple tokens at the same location are put into a nested array. The source field must be specified, and it must be an <a href="#indexing-index" aria-label="index">index</a> or <a href="#indexing-attribute" aria-label="attribute">attribute</a> field of type string, array<string> or weightedset<string>. If the source field is of type weightedset<string> then the summary field is rendered as if the source field was of type array<string>, weights are not shown. This is mainly useful for <a href="/en/querying/text-matching#tokens-example" aria-label="linguistics transformations debugging">linguistics transformations debugging</a>, to correlate query trace with the tokens indexed.</td> + </tr> + </tbody> +</table> + +Read more about [document summaries](/en/querying/document-summaries). + +## weight + +Contained in [field](#field). The weight of a field - the default is 100. The field weight is used when calculating the [rank scores](/en/basics/ranking). + +```js +weight: [positive integer] +``` + +## weightedset + +Contained in [field](#field) of type weightedset. Properties of a weighted set. + +```bash +weightedset: [property] +``` +or +```js +weightedset { + [property] + [property] + … +} +``` + +<table className="vespa-table"> + <thead> + <tr> + <th>Property</th> + <th>Occurrence</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>create-if-nonexistent</td> + <td>Zero to one</td> + <td>If the weight of a key is adjusted in a document using a partial update increment or decrement command, but the key is currently not present, the command will be ignored by default. Set this to make keys to be created in this case instead. This is useful when the weight is used to represent the count of the key. <br/><br/> <pre><code>{`field tag type weightedset<string> { indexing: attribute | summary weightedset { create-if-nonexistent remove-if-zero } }`}</code></pre></td> + </tr> + <tr> + <td>remove-if-zero</td> + <td>Zero to one</td> + <td>This is the companion of <code>{`create-if-nonexistent`}</code> for the converse case: By default, keys may have zero as weight. With this turned on, keys whose weight is adjusted (or set) to zero will be removed.</td> + </tr> + </tbody> +</table> + + +## import field + +Contained in [schema](#schema). Using a [reference](#reference) to a document type, import a field from that document type into this schema to be used for matching, ranking, grouping, and sorting. Only attribute fields can be imported. Importing fields are not supported in [streaming search](/en/performance/streaming-search#differences-in-streaming-search). + +The imported field inherits all but the following properties from the parent field: + +- [attribute: fast-access](#attribute) + +Refer to [parent/child](/en/schemas/parent-child) for a complete example. Note that the imported field is put outside the document type: + +```js +schema myschema { + document myschema { + field parentschema_ref type reference<parentschema> { + indexing: attribute + } + } + import field parentschema_ref.name as parent_name {} +} +``` + +Extra restrictions apply for some of the field types: + +<table className="vespa-table"> + <thead> + <tr> + <th>Field type</th> + <th>Restriction</th> + </tr> + </thead> + <tbody> + <tr> + <td>array of struct</td> + <td>Can be imported if at least one of the struct fields has an attribute. All struct fields with attributes must have primitive types. Only the struct fields with attributes will be visible.</td> + </tr> + <tr> + <td>map of struct</td> + <td>Can be imported if the key field has an attribute, and at least one of the struct fields has an attribute. All struct fields with attributes must have primitive types. Only the key field and the struct fields with attributes will be visible.</td> + </tr> + <tr> + <td>map</td> + <td>Can be imported if both key and value fields have primitive types and have attributes.</td> + </tr> + <tr> + <td>position</td> + <td>Can be imported if it has an attribute.</td> + </tr> + <tr> + <td>array of position</td> + <td>Can be imported if it has an attribute.</td> + </tr> + </tbody> +</table> + +To use an imported field in summary, create an explicit [document summary](#document-summary) containing the field. + +Imported fields can be used to expire documents, but [read this first](/en/schemas/documents#document-expiry). + +## Document and search field types + +Note that it is possible to make a document field of one type into one or more instances of another search field, by declaring a field outside the document, which uses other fields as input. For example, to create an integer attribute for a string containing a comma-separated list of integers in the document, do like this: + +```js +schema example { + document example { + field yearlist type string { # Comma-separated years + } + } + + field year type array<int> { # Search field using the yearlist value + indexing: input yearlist | split "," | attribute + } +} +``` + +## Modifying schemas + +This section describes how a schema in a live application can be modified—categories: + +1. [Valid changes without restart or re-feed](#valid-changes-without-restart-or-re-feed) +2. [Changes that require restart but not re-feed](#changes-that-require-restart-but-not-re-feed) +3. [Changes that require reindexing](#changes-that-require-reindexing) +4. [Changes that require re-feed](#changes-that-require-re-feed) + +When running `vespa prepare` on a new application package, the changes in the schema files are compared with the files in the current active package. If some of the changes require restart or re-feed, the output from `vespa prepare` specifies which actions are needed. + +<Danger> + **Important:** For changes that are not covered below, and no output is returned from `vespa prepare`, the impact is undefined and in no way guaranteed to allow a system to stay live until re-feeding. Changes not related to the schema are discussed in [admin procedures](/en/operations/self-managed/admin-procedures). +</Danger> + +### Valid changes without restart or re-feed + +Procedure: + +1. Run `vespa prepare` on the changed application +2. Run `vespa activate`. The changes will take effect immediately + +Changes: + +<table className="vespa-table"> + <thead> + <tr> + <th>Change</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>Add a new document field</td> + <td>Add a new document field as index, attribute, summary or any combination of these. Existing documents will implicitly get the new field with no content. Documents fed after the change can specify the new field. If the field has existed with the same type earlier, then old content *may or may not* reappear</td> + </tr> + <tr> + <td>Remove a document field</td> + <td>Existing documents will no longer see the removed field, but the field data is not completely removed from the search node</td> + </tr> + <tr> + <td>Add or remove an existing document field from document summary</td> + <td>Add an existing field to summary or any number of summary classes, and remove an existing field from summary or any number of summary classes. Example: <br/><br/> <pre><code>{`document-summary short-summary { summary artist {} }`}</code></pre> <br/><br/> A change adding an <a href="/en/content/attributes" aria-label="attribute">attribute</a> field with a new name to a summary class using <a href="#source" aria-label="source">source</a> does not require restart or re-feed: <br/><br/> <pre><code>{`field artist type string { indexing: summary | attribute } document-summary rename-summary { summary artist_name { source: artist } }`}</code></pre> <br/><br/> Also see <a href="#add-or-remove-a-new-non-attribute-document-field-from-document-summary" aria-label="non-attribute">non-attribute</a> fields.</td> + </tr> + <tr> + <td>Remove the attribute aspect from a field that is also an index field</td> + <td>This is the only scenario of changing the attribute aspect of a document field that is allowed without restart</td> + </tr> + <tr> + <td>Add, change or remove field sets</td> + <td>Change <a href="#fieldset" aria-label="fieldsets">fieldsets</a> used to group fields together for searching</td> + </tr> + <tr> + <td>Change the alias or sorting attribute settings for an attribute field</td> + <td></td> + </tr> + <tr> + <td>Add, change or remove rank profiles</td> + <td></td> + </tr> + <tr> + <td>Change document field weights</td> + <td></td> + </tr> + <tr> + <td>Add, change or remove field aliases</td> + <td></td> + </tr> + <tr> + <td>Add, change or remove rank settings for a field</td> + <td>Exception: Changing <code>{`rank: filter`}</code> on an attribute field in mode *index* requires restart. See details in <a href="#changes-that-require-restart-but-not-re-feed" aria-label="next section">next section</a></td> + </tr> + <tr> + <td>Add or remove a schema</td> + <td>Removing a schema definition file will make <a href="/en/content/proton" aria-label="proton">proton</a> drop all documents of that type, subsequently releasing memory and disk.</td> + </tr> + </tbody> +</table> + + +### Changes that require restart but not re-feed + +Procedure: + +1. Run `vespa prepare` on the changed application. Output specifies which restart actions are needed +2. Run `vespa activate` +3. Restart `services` on the services specified in the `prepare` output + +Changes: + +<table className="vespa-table"> + <thead> + <tr> + <th>Change</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>Change the attribute aspect of a document field</td> + <td>Add or remove a field as attribute. When adding, the attribute is populated based on the field value in stored documents during restart. When removing, the field value in stored documents is updated based on the content in the attribute during restart.</td> + </tr> + <tr> + <td>Change the attribute settings for an attribute field</td> + <td>Change the following attribute settings: <code>{`fast-search`}</code>, <code>{`fast-access`}</code>, <code>{`fast-rank`}</code>, <code>{`paged`}</code>.</td> + </tr> + <tr> + <td>Change the rank filter setting for an attribute field</td> + <td>Add or remove <code>{`rank: filter`}</code> on an attribute field.</td> + </tr> + <tr> + <td>Change the hnsw index settings for a tensor attribute field</td> + <td>Adding or removing the <a href="#index-hnsw" aria-label="hnsw index">hnsw index</a> on a tensor attribute field, or changing the <code>{`distance-metric`}</code> or <code>{`max-links-per-node`}</code> requires a restart to rebuild the index. Changing <code>{`neighbors-to-explore-at-insert`}</code> requires a restart, but does not rebuild the index.</td> + </tr> + <tr> + <td>Change the distance metric for a tensor attribute field</td> + <td>Change, add, or remove the <a href="#distance-metric" aria-label="distance metric">distance metric</a> on a tensor attribute field. If no distance metric is specified, _euclidean_ is used as the default.</td> + </tr> + </tbody> +</table> + +Example: Given a content cluster _mycluster_ with mode _index_: + +```js +schema test { + document test { + field f1 type string { indexing: summary } + } +} +``` +Then add field `f1` as an attribute: +```js +schema test { + document test { + field f1 type string { indexing: attribute | summary } + } +} +``` +The following is output from `vespa prepare` - which restart actions are needed: +```js +WARNING: Change(s) between active and new application that require restart: +In cluster 'mycluster' of type 'search': + Restart services of type 'searchnode' because: + 1) Document type 'test': Field 'f1' changed: add attribute aspect +``` + +### Changes that require reindexing + +All the changes listed below require [reindexing](/en/operations/reindexing) of all documents. Unlike re-feed, which requires an external source of data, reindexing is done using documents stored in Vespa, and is automatic (once triggered). It can also run concurrently with feed and serving, but until reindexing is complete, affected fields will be empty or have potentially wrong annotations not matching the query processing. Procedure: + +1. Run `vespa prepare` on the changed application. Output specifies which reindexing actions are needed +2. Run `vespa activate` +3. [Enable reindexing](/en/reference/api/deploy-v2#reindex) for the indicated document types and clusters +4. Run `vespa prepare` and `vespa activate` again to start reindexing process + +Changes: + +<table className="vespa-table"> + <thead> + <tr> + <th>Change</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>Change index aspect of a document field</td> + <td>This changes the document processing pipeline before documents arrive in the backend. Only documents fed after index aspect was added will have annotations and be present in the reverse index. Only documents fed after index aspect was removed will avoid disk bloat due to unneeded annotations.</td> + </tr> + <tr> + <td>Switch stemming/normalizing on or off</td> + <td>This changes the document processing pipeline before documents arrive in the backend, and what annotations are made for an indexed field. <Danger>**Important:** If not re-feeding after such a change, serving works, but recall is undefined as the index has been produced using a different setting than the one used when doing stemming/normalizing of the query terms.</Danger></td> + </tr> + <tr> + <td>Add, change, or remove match settings for a field</td> + <td>Example: Adding <code>{`match: word`}</code> to a field. This changes the document processing pipeline before documents arrive in the backend, and what annotations are made for an indexed field. <Danger>**Important:** If not reindexing after such a change, serving works, but recall is undefined as the index has been produced using one match mode while run-time is using a different match mode.</Danger></td> + </tr> + <tr> + <td>Add or remove a new non-attribute document field from document summary</td> + <td>A change adding an <a href="#document-fields" aria-label="index or summary">index or summary</a> field (without <a href="/en/content/attributes" aria-label="attribute">attribute</a>) with a new name to a summary class using <a href="#source" aria-label="source">source</a> requires re-index: <pre><code>{`artist type string { indexing: summary | index } document-summary rename-summary { summary artist_name { source: artist } }`}</code></pre> Also see <a href="#add-or-remove-an-existing-document-field-from-document-summary" aria-label="attribute">attribute</a> fields.</td> + </tr> + </tbody> +</table> + +Example: Given a content cluster *mycluster* with mode + +```js +schema test { + document test { + field f1 type string { indexing: summary } + } +} +``` + +Then add field `f1` as an index: + +```js +schema test { + document test { + field f1 type string { indexing: index | summary } + } +} +``` + +The following is output from `vespa prepare` - which reindex actions are needed: + +```js +WARNING: Change(s) between active and new application that require re-index: +Reindex document type 'test' in cluster 'mycluster' because: + 1) Document type 'test': Field 'f1' changed: add index aspect, indexing script: '{ input f1 | summary f1; }' -> '{ input f1 | tokenize normalize stem:"SHORTEST" | index f1 | summary f1; }' +``` + +### Changes that require re-feed + +All the changes listed below require re-feeding of all documents. Unless a change is listed in the above sections, treat it as if it were listed here. Until re-feed is complete, affected fields will be empty or have potentially wrong annotations not matching the query processing. Procedure: + +1. Run `vespa prepare` on the changed application. Output specifies which re-feed actions are needed +2. Stop feeding, wait until done +3. Run `vespa activate` +4. Re-feed all documents + +Changes: + +<table className="vespa-table"> + <thead> + <tr> + <th>Change</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>**Change a document field's data type or collection type**</td> + <td>Existing documents will no longer have any content for this field. To populate the field, re-feed the existing documents using the new type for this field. There will be no automatic conversion from old to new field type. <Danger>**Important:** If not re-feeding after such a change, serving works, but searching this field will not give any results.</Danger></td> + </tr> + <tr> + <td>Change a tensor attribute's tensor type</td> + <td></td> + </tr> + </tbody> +</table> + +Example: Given a content cluster *mycluster* with mode *index*: + +```js +schema test { + document test { + field f1 type string { indexing: summary } + } +} +``` +Then change field `f1` to hold an int: +```js +schema test { + document test { + field f1 type int { indexing: summary } + } +} +``` +The following is output from `vespa prepare` - which re-feed actions are needed: +```js +WARNING: Change(s) between active and new application that require re-feed: +Re-feed document type 'test' in cluster 'mycluster' because: + 1) Document type 'test': Field 'f1' changed: data type: 'string' -> 'int' +``` \ No newline at end of file diff --git a/en/reference/searchnode-metrics-reference.html b/en/reference/searchnode-metrics-reference.html deleted file mode 100644 index 44159dec97..0000000000 --- a/en/reference/searchnode-metrics-reference.html +++ /dev/null @@ -1,1282 +0,0 @@ ---- -# Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -title: "SearchNode Metrics" ---- - -<table class="table"> - <thead> - <tr><th>Name</th><th>Unit</th><th>Description</th></tr> - </thead> - <tbody> - <tr> - <td><p id="content_proton_config_generation">content.proton.config.generation</p></td> - <td>version</td> - <td>The oldest config generation used by this search node</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_documents_total">content.proton.documentdb.documents.total</p></td> - <td>document</td> - <td>The total number of documents in this documents db (ready + not-ready)</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_documents_ready">content.proton.documentdb.documents.ready</p></td> - <td>document</td> - <td>The number of ready documents in this document db</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_documents_active">content.proton.documentdb.documents.active</p></td> - <td>document</td> - <td>The number of active / searchable documents in this document db</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_documents_removed">content.proton.documentdb.documents.removed</p></td> - <td>document</td> - <td>The number of removed documents in this document db</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_index_docs_in_memory">content.proton.documentdb.index.docs_in_memory</p></td> - <td>document</td> - <td>Number of documents in memory index</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_disk_usage">content.proton.documentdb.disk_usage</p></td> - <td>byte</td> - <td>The total disk usage (in bytes) for this document db</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_memory_usage_allocated_bytes">content.proton.documentdb.memory_usage.allocated_bytes</p></td> - <td>byte</td> - <td>The number of allocated bytes</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_memory_usage_dead_bytes">content.proton.documentdb.memory_usage.dead_bytes</p></td> - <td>byte</td> - <td>The number of dead bytes (<= used_bytes)</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_memory_usage_onhold_bytes">content.proton.documentdb.memory_usage.onhold_bytes</p></td> - <td>byte</td> - <td>The number of bytes on hold</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_memory_usage_used_bytes">content.proton.documentdb.memory_usage.used_bytes</p></td> - <td>byte</td> - <td>The number of used bytes (<= allocated_bytes)</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_heart_beat_age">content.proton.documentdb.heart_beat_age</p></td> - <td>second</td> - <td>How long ago (in seconds) heart beat maintenance job was run</td> - </tr> - <tr> - <td><p id="content_proton_docsum_count">content.proton.docsum.count</p></td> - <td>request</td> - <td>Docsum requests handled</td> - </tr> - <tr> - <td><p id="content_proton_docsum_docs">content.proton.docsum.docs</p></td> - <td>document</td> - <td>Total docsums returned</td> - </tr> - <tr> - <td><p id="content_proton_docsum_latency">content.proton.docsum.latency</p></td> - <td>millisecond</td> - <td>Docsum request latency</td> - </tr> - <tr> - <td><p id="content_proton_search_protocol_query_latency">content.proton.search_protocol.query.latency</p></td> - <td>second</td> - <td>Query request latency (seconds)</td> - </tr> - <tr> - <td><p id="content_proton_search_protocol_query_request_size">content.proton.search_protocol.query.request_size</p></td> - <td>byte</td> - <td>Query request size (network bytes)</td> - </tr> - <tr> - <td><p id="content_proton_search_protocol_query_reply_size">content.proton.search_protocol.query.reply_size</p></td> - <td>byte</td> - <td>Query reply size (network bytes)</td> - </tr> - <tr> - <td><p id="content_proton_search_protocol_docsum_latency">content.proton.search_protocol.docsum.latency</p></td> - <td>second</td> - <td>Docsum request latency (seconds)</td> - </tr> - <tr> - <td><p id="content_proton_search_protocol_docsum_request_size">content.proton.search_protocol.docsum.request_size</p></td> - <td>byte</td> - <td>Docsum request size (network bytes)</td> - </tr> - <tr> - <td><p id="content_proton_search_protocol_docsum_reply_size">content.proton.search_protocol.docsum.reply_size</p></td> - <td>byte</td> - <td>Docsum reply size (network bytes)</td> - </tr> - <tr> - <td><p id="content_proton_search_protocol_docsum_requested_documents">content.proton.search_protocol.docsum.requested_documents</p></td> - <td>document</td> - <td>Total requested document summaries</td> - </tr> - <tr> - <td><p id="content_proton_executor_proton_queuesize">content.proton.executor.proton.queuesize</p></td> - <td>task</td> - <td>Size of executor proton task queue</td> - </tr> - <tr> - <td><p id="content_proton_executor_proton_accepted">content.proton.executor.proton.accepted</p></td> - <td>task</td> - <td>Number of executor proton accepted tasks</td> - </tr> - <tr> - <td><p id="content_proton_executor_proton_wakeups">content.proton.executor.proton.wakeups</p></td> - <td>wakeup</td> - <td>Number of times an executor proton worker thread has been woken up</td> - </tr> - <tr> - <td><p id="content_proton_executor_proton_utilization">content.proton.executor.proton.utilization</p></td> - <td>fraction</td> - <td>Ratio of time the executor proton worker threads has been active</td> - </tr> - <tr> - <td><p id="content_proton_executor_proton_rejected">content.proton.executor.proton.rejected</p></td> - <td>task</td> - <td>Number of rejected tasks</td> - </tr> - <tr> - <td><p id="content_proton_executor_flush_queuesize">content.proton.executor.flush.queuesize</p></td> - <td>task</td> - <td>Size of executor flush task queue</td> - </tr> - <tr> - <td><p id="content_proton_executor_flush_accepted">content.proton.executor.flush.accepted</p></td> - <td>task</td> - <td>Number of accepted executor flush tasks</td> - </tr> - <tr> - <td><p id="content_proton_executor_flush_wakeups">content.proton.executor.flush.wakeups</p></td> - <td>wakeup</td> - <td>Number of times an executor flush worker thread has been woken up</td> - </tr> - <tr> - <td><p id="content_proton_executor_flush_utilization">content.proton.executor.flush.utilization</p></td> - <td>fraction</td> - <td>Ratio of time the executor flush worker threads has been active</td> - </tr> - <tr> - <td><p id="content_proton_executor_flush_rejected">content.proton.executor.flush.rejected</p></td> - <td>task</td> - <td>Number of rejected tasks</td> - </tr> - <tr> - <td><p id="content_proton_executor_match_queuesize">content.proton.executor.match.queuesize</p></td> - <td>task</td> - <td>Size of executor match task queue</td> - </tr> - <tr> - <td><p id="content_proton_executor_match_accepted">content.proton.executor.match.accepted</p></td> - <td>task</td> - <td>Number of accepted executor match tasks</td> - </tr> - <tr> - <td><p id="content_proton_executor_match_wakeups">content.proton.executor.match.wakeups</p></td> - <td>wakeup</td> - <td>Number of times an executor match worker thread has been woken up</td> - </tr> - <tr> - <td><p id="content_proton_executor_match_utilization">content.proton.executor.match.utilization</p></td> - <td>fraction</td> - <td>Ratio of time the executor match worker threads has been active</td> - </tr> - <tr> - <td><p id="content_proton_executor_match_rejected">content.proton.executor.match.rejected</p></td> - <td>task</td> - <td>Number of rejected tasks</td> - </tr> - <tr> - <td><p id="content_proton_executor_docsum_queuesize">content.proton.executor.docsum.queuesize</p></td> - <td>task</td> - <td>Size of executor docsum task queue</td> - </tr> - <tr> - <td><p id="content_proton_executor_docsum_accepted">content.proton.executor.docsum.accepted</p></td> - <td>task</td> - <td>Number of executor accepted docsum tasks</td> - </tr> - <tr> - <td><p id="content_proton_executor_docsum_wakeups">content.proton.executor.docsum.wakeups</p></td> - <td>wakeup</td> - <td>Number of times an executor docsum worker thread has been woken up</td> - </tr> - <tr> - <td><p id="content_proton_executor_docsum_utilization">content.proton.executor.docsum.utilization</p></td> - <td>fraction</td> - <td>Ratio of time the executor docsum worker threads has been active</td> - </tr> - <tr> - <td><p id="content_proton_executor_docsum_rejected">content.proton.executor.docsum.rejected</p></td> - <td>task</td> - <td>Number of rejected tasks</td> - </tr> - <tr> - <td><p id="content_proton_executor_shared_queuesize">content.proton.executor.shared.queuesize</p></td> - <td>task</td> - <td>Size of executor shared task queue</td> - </tr> - <tr> - <td><p id="content_proton_executor_shared_accepted">content.proton.executor.shared.accepted</p></td> - <td>task</td> - <td>Number of executor shared accepted tasks</td> - </tr> - <tr> - <td><p id="content_proton_executor_shared_wakeups">content.proton.executor.shared.wakeups</p></td> - <td>wakeup</td> - <td>Number of times an executor shared worker thread has been woken up</td> - </tr> - <tr> - <td><p id="content_proton_executor_shared_utilization">content.proton.executor.shared.utilization</p></td> - <td>fraction</td> - <td>Ratio of time the executor shared worker threads has been active</td> - </tr> - <tr> - <td><p id="content_proton_executor_shared_rejected">content.proton.executor.shared.rejected</p></td> - <td>task</td> - <td>Number of rejected tasks</td> - </tr> - <tr> - <td><p id="content_proton_executor_warmup_queuesize">content.proton.executor.warmup.queuesize</p></td> - <td>task</td> - <td>Size of executor warmup task queue</td> - </tr> - <tr> - <td><p id="content_proton_executor_warmup_accepted">content.proton.executor.warmup.accepted</p></td> - <td>task</td> - <td>Number of accepted executor warmup tasks</td> - </tr> - <tr> - <td><p id="content_proton_executor_warmup_wakeups">content.proton.executor.warmup.wakeups</p></td> - <td>wakeup</td> - <td>Number of times a warmup executor worker thread has been woken up</td> - </tr> - <tr> - <td><p id="content_proton_executor_warmup_utilization">content.proton.executor.warmup.utilization</p></td> - <td>fraction</td> - <td>Ratio of time the executor warmup worker threads has been active</td> - </tr> - <tr> - <td><p id="content_proton_executor_warmup_rejected">content.proton.executor.warmup.rejected</p></td> - <td>task</td> - <td>Number of rejected tasks</td> - </tr> - <tr> - <td><p id="content_proton_executor_field_writer_queuesize">content.proton.executor.field_writer.queuesize</p></td> - <td>task</td> - <td>Size of executor field writer task queue</td> - </tr> - <tr> - <td><p id="content_proton_executor_field_writer_accepted">content.proton.executor.field_writer.accepted</p></td> - <td>task</td> - <td>Number of accepted executor field writer tasks</td> - </tr> - <tr> - <td><p id="content_proton_executor_field_writer_wakeups">content.proton.executor.field_writer.wakeups</p></td> - <td>wakeup</td> - <td>Number of times an executor field writer worker thread has been woken up</td> - </tr> - <tr> - <td><p id="content_proton_executor_field_writer_utilization">content.proton.executor.field_writer.utilization</p></td> - <td>fraction</td> - <td>Ratio of time the executor fieldwriter worker threads has been active</td> - </tr> - <tr> - <td><p id="content_proton_executor_field_writer_saturation">content.proton.executor.field_writer.saturation</p></td> - <td>fraction</td> - <td>Ratio indicating the max saturation of underlying worker threads. A higher saturation than utilization indicates a bottleneck in one of the worker threads.</td> - </tr> - <tr> - <td><p id="content_proton_executor_field_writer_rejected">content.proton.executor.field_writer.rejected</p></td> - <td>task</td> - <td>Number of rejected tasks</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_job_total">content.proton.documentdb.job.total</p></td> - <td>fraction</td> - <td>The job load average total of all job metrics</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_job_attribute_flush">content.proton.documentdb.job.attribute_flush</p></td> - <td>fraction</td> - <td>Flushing of attribute vector(s) to disk</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_job_memory_index_flush">content.proton.documentdb.job.memory_index_flush</p></td> - <td>fraction</td> - <td>Flushing of memory index to disk</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_job_disk_index_fusion">content.proton.documentdb.job.disk_index_fusion</p></td> - <td>fraction</td> - <td>Fusion of disk indexes</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_job_document_store_flush">content.proton.documentdb.job.document_store_flush</p></td> - <td>fraction</td> - <td>Flushing of document store to disk</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_job_document_store_compact">content.proton.documentdb.job.document_store_compact</p></td> - <td>fraction</td> - <td>Compaction of document store on disk</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_job_bucket_move">content.proton.documentdb.job.bucket_move</p></td> - <td>fraction</td> - <td>Moving of buckets between 'ready' and 'notready' sub databases</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_job_lid_space_compact">content.proton.documentdb.job.lid_space_compact</p></td> - <td>fraction</td> - <td>Compaction of lid space in document meta store and attribute vectors</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_job_removed_documents_prune">content.proton.documentdb.job.removed_documents_prune</p></td> - <td>fraction</td> - <td>Pruning of removed documents in 'removed' sub database</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_threading_service_master_queuesize">content.proton.documentdb.threading_service.master.queuesize</p></td> - <td>task</td> - <td>Size of threading service master task queue</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_threading_service_master_accepted">content.proton.documentdb.threading_service.master.accepted</p></td> - <td>task</td> - <td>Number of accepted threading service master tasks</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_threading_service_master_wakeups">content.proton.documentdb.threading_service.master.wakeups</p></td> - <td>wakeup</td> - <td>Number of times a threading service master worker thread has been woken up</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_threading_service_master_utilization">content.proton.documentdb.threading_service.master.utilization</p></td> - <td>fraction</td> - <td>Ratio of time the threading service master worker threads has been active</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_threading_service_master_rejected">content.proton.documentdb.threading_service.master.rejected</p></td> - <td>task</td> - <td>Number of rejected tasks</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_threading_service_index_queuesize">content.proton.documentdb.threading_service.index.queuesize</p></td> - <td>task</td> - <td>Size of threading service index task queue</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_threading_service_index_accepted">content.proton.documentdb.threading_service.index.accepted</p></td> - <td>task</td> - <td>Number of accepted threading service index tasks</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_threading_service_index_wakeups">content.proton.documentdb.threading_service.index.wakeups</p></td> - <td>wakeup</td> - <td>Number of times a threading service index worker thread has been woken up</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_threading_service_index_utilization">content.proton.documentdb.threading_service.index.utilization</p></td> - <td>fraction</td> - <td>Ratio of time the threading service index worker threads has been active</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_threading_service_index_rejected">content.proton.documentdb.threading_service.index.rejected</p></td> - <td>task</td> - <td>Number of rejected tasks</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_threading_service_summary_queuesize">content.proton.documentdb.threading_service.summary.queuesize</p></td> - <td>task</td> - <td>Size of threading service summary task queue</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_threading_service_summary_accepted">content.proton.documentdb.threading_service.summary.accepted</p></td> - <td>task</td> - <td>Number of accepted threading service summary tasks</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_threading_service_summary_wakeups">content.proton.documentdb.threading_service.summary.wakeups</p></td> - <td>wakeup</td> - <td>Number of times a threading service summary worker thread has been woken up</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_threading_service_summary_utilization">content.proton.documentdb.threading_service.summary.utilization</p></td> - <td>fraction</td> - <td>Ratio of time the threading service summary worker threads has been active</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_threading_service_summary_rejected">content.proton.documentdb.threading_service.summary.rejected</p></td> - <td>task</td> - <td>Number of rejected tasks</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_threading_service_attribute_field_writer_accepted">content.proton.documentdb.threading_service.attribute_field_writer.accepted</p></td> - <td>task</td> - <td>Number of accepted tasks</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_threading_service_attribute_field_writer_queuesize">content.proton.documentdb.threading_service.attribute_field_writer.queuesize</p></td> - <td>task</td> - <td>Size of task queue</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_threading_service_attribute_field_writer_rejected">content.proton.documentdb.threading_service.attribute_field_writer.rejected</p></td> - <td>task</td> - <td>Number of rejected tasks</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_threading_service_attribute_field_writer_utilization">content.proton.documentdb.threading_service.attribute_field_writer.utilization</p></td> - <td>fraction</td> - <td>Ratio of time the worker threads has been active</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_threading_service_attribute_field_writer_wakeups">content.proton.documentdb.threading_service.attribute_field_writer.wakeups</p></td> - <td>wakeup</td> - <td>Number of times a worker thread has been woken up</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_threading_service_index_field_inverter_accepted">content.proton.documentdb.threading_service.index_field_inverter.accepted</p></td> - <td>task</td> - <td>Number of accepted tasks</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_threading_service_index_field_inverter_queuesize">content.proton.documentdb.threading_service.index_field_inverter.queuesize</p></td> - <td>task</td> - <td>Size of task queue</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_threading_service_index_field_inverter_rejected">content.proton.documentdb.threading_service.index_field_inverter.rejected</p></td> - <td>task</td> - <td>Number of rejected tasks</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_threading_service_index_field_inverter_utilization">content.proton.documentdb.threading_service.index_field_inverter.utilization</p></td> - <td>fraction</td> - <td>Ratio of time the worker threads has been active</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_threading_service_index_field_inverter_wakeups">content.proton.documentdb.threading_service.index_field_inverter.wakeups</p></td> - <td>wakeup</td> - <td>Number of times a worker thread has been woken up</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_threading_service_index_field_writer_accepted">content.proton.documentdb.threading_service.index_field_writer.accepted</p></td> - <td>task</td> - <td>Number of accepted tasks</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_threading_service_index_field_writer_queuesize">content.proton.documentdb.threading_service.index_field_writer.queuesize</p></td> - <td>task</td> - <td>Size of task queue</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_threading_service_index_field_writer_rejected">content.proton.documentdb.threading_service.index_field_writer.rejected</p></td> - <td>task</td> - <td>Number of rejected tasks</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_threading_service_index_field_writer_utilization">content.proton.documentdb.threading_service.index_field_writer.utilization</p></td> - <td>fraction</td> - <td>Ratio of time the worker threads has been active</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_threading_service_index_field_writer_wakeups">content.proton.documentdb.threading_service.index_field_writer.wakeups</p></td> - <td>wakeup</td> - <td>Number of times a worker thread has been woken up</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_ready_lid_space_lid_bloat_factor">content.proton.documentdb.ready.lid_space.lid_bloat_factor</p></td> - <td>fraction</td> - <td>The bloat factor of this lid space, indicating the total amount of holes in the allocated lid space ((lid_limit - used_lids) / lid_limit)</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_ready_lid_space_lid_fragmentation_factor">content.proton.documentdb.ready.lid_space.lid_fragmentation_factor</p></td> - <td>fraction</td> - <td>The fragmentation factor of this lid space, indicating the amount of holes in the currently used part of the lid space ((highest_used_lid - used_lids) / highest_used_lid)</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_ready_lid_space_lid_limit">content.proton.documentdb.ready.lid_space.lid_limit</p></td> - <td>documentid</td> - <td>The size of the allocated lid space</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_ready_lid_space_highest_used_lid">content.proton.documentdb.ready.lid_space.highest_used_lid</p></td> - <td>documentid</td> - <td>The highest used lid</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_ready_lid_space_used_lids">content.proton.documentdb.ready.lid_space.used_lids</p></td> - <td>documentid</td> - <td>The number of lids used</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_ready_lid_space_lowest_free_lid">content.proton.documentdb.ready.lid_space.lowest_free_lid</p></td> - <td>documentid</td> - <td>The lowest free local document ID</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_notready_lid_space_lid_bloat_factor">content.proton.documentdb.notready.lid_space.lid_bloat_factor</p></td> - <td>fraction</td> - <td>The bloat factor of this lid space, indicating the total amount of holes in the allocated lid space ((lid_limit - used_lids) / lid_limit)</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_notready_lid_space_lid_fragmentation_factor">content.proton.documentdb.notready.lid_space.lid_fragmentation_factor</p></td> - <td>fraction</td> - <td>The fragmentation factor of this lid space, indicating the amount of holes in the currently used part of the lid space ((highest_used_lid - used_lids) / highest_used_lid)</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_notready_lid_space_lid_limit">content.proton.documentdb.notready.lid_space.lid_limit</p></td> - <td>documentid</td> - <td>The size of the allocated lid space</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_notready_lid_space_highest_used_lid">content.proton.documentdb.notready.lid_space.highest_used_lid</p></td> - <td>documentid</td> - <td>The highest used lid</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_notready_lid_space_used_lids">content.proton.documentdb.notready.lid_space.used_lids</p></td> - <td>documentid</td> - <td>The number of lids used</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_notready_lid_space_lowest_free_lid">content.proton.documentdb.notready.lid_space.lowest_free_lid</p></td> - <td>documentid</td> - <td>The lowest free local document ID</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_removed_lid_space_lid_bloat_factor">content.proton.documentdb.removed.lid_space.lid_bloat_factor</p></td> - <td>fraction</td> - <td>The bloat factor of this lid space, indicating the total amount of holes in the allocated lid space ((lid_limit - used_lids) / lid_limit)</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_removed_lid_space_lid_fragmentation_factor">content.proton.documentdb.removed.lid_space.lid_fragmentation_factor</p></td> - <td>fraction</td> - <td>The fragmentation factor of this lid space, indicating the amount of holes in the currently used part of the lid space ((highest_used_lid - used_lids) / highest_used_lid)</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_removed_lid_space_lid_limit">content.proton.documentdb.removed.lid_space.lid_limit</p></td> - <td>documentid</td> - <td>The size of the allocated lid space</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_removed_lid_space_highest_used_lid">content.proton.documentdb.removed.lid_space.highest_used_lid</p></td> - <td>documentid</td> - <td>The highest used lid</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_removed_lid_space_used_lids">content.proton.documentdb.removed.lid_space.used_lids</p></td> - <td>documentid</td> - <td>The number of lids used</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_removed_lid_space_lowest_free_lid">content.proton.documentdb.removed.lid_space.lowest_free_lid</p></td> - <td>documentid</td> - <td>The lowest free local document ID</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_bucket_move_buckets_pending">content.proton.documentdb.bucket_move.buckets_pending</p></td> - <td>bucket</td> - <td>The number of buckets left to move</td> - </tr> - <tr> - <td><p id="content_proton_resource_usage_disk">content.proton.resource_usage.disk</p></td> - <td>fraction</td> - <td>The relative amount of disk used by this content node (transient usage not included, value in the range [0, 1]). Same value as reported to the cluster controller</td> - </tr> - <tr> - <td><p id="content_proton_resource_usage_disk_usage_total">content.proton.resource_usage.disk_usage.total</p></td> - <td>fraction</td> - <td>The total relative amount of disk used by this content node (value in the range [0, 1])</td> - </tr> - <tr> - <td><p id="content_proton_resource_usage_disk_usage_total_utilization">content.proton.resource_usage.disk_usage.total_utilization</p></td> - <td>fraction</td> - <td>The relative amount of disk used compared to the content node disk resource limit</td> - </tr> - <tr> - <td><p id="content_proton_resource_usage_disk_usage_transient">content.proton.resource_usage.disk_usage.transient</p></td> - <td>fraction</td> - <td>The relative amount of transient disk used by this content node (value in the range [0, 1])</td> - </tr> - <tr> - <td><p id="content_proton_resource_usage_memory">content.proton.resource_usage.memory</p></td> - <td>fraction</td> - <td>The relative amount of memory used by this content node (transient usage not included, value in the range [0, 1]). Same value as reported to the cluster controller</td> - </tr> - <tr> - <td><p id="content_proton_resource_usage_memory_usage_total">content.proton.resource_usage.memory_usage.total</p></td> - <td>fraction</td> - <td>The total relative amount of memory used by this content node (value in the range [0, 1])</td> - </tr> - <tr> - <td><p id="content_proton_resource_usage_memory_usage_total_utilization">content.proton.resource_usage.memory_usage.total_utilization</p></td> - <td>fraction</td> - <td>The relative amount of memory used compared to the content node memory resource limit</td> - </tr> - <tr> - <td><p id="content_proton_resource_usage_memory_usage_transient">content.proton.resource_usage.memory_usage.transient</p></td> - <td>fraction</td> - <td>The relative amount of transient memory used by this content node (value in the range [0, 1])</td> - </tr> - <tr> - <td><p id="content_proton_resource_usage_memory_mappings">content.proton.resource_usage.memory_mappings</p></td> - <td>file</td> - <td>The number of memory mapped files</td> - </tr> - <tr> - <td><p id="content_proton_resource_usage_open_file_descriptors">content.proton.resource_usage.open_file_descriptors</p></td> - <td>file</td> - <td>The number of open files</td> - </tr> - <tr> - <td><p id="content_proton_resource_usage_feeding_blocked">content.proton.resource_usage.feeding_blocked</p></td> - <td>binary</td> - <td>Whether feeding is blocked due to resource limits being reached (value is either 0 or 1)</td> - </tr> - <tr> - <td><p id="content_proton_resource_usage_malloc_arena">content.proton.resource_usage.malloc_arena</p></td> - <td>byte</td> - <td>Size of malloc arena</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_attribute_resource_usage_address_space">content.proton.documentdb.attribute.resource_usage.address_space</p></td> - <td>fraction</td> - <td>The max relative address space used among components in all attribute vectors in this document db (value in the range [0, 1])</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_attribute_resource_usage_feeding_blocked">content.proton.documentdb.attribute.resource_usage.feeding_blocked</p></td> - <td>binary</td> - <td>Whether feeding is blocked due to attribute resource limits being reached (value is either 0 or 1)</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_attribute_memory_usage_allocated_bytes">content.proton.documentdb.attribute.memory_usage.allocated_bytes</p></td> - <td>byte</td> - <td>The number of allocated bytes</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_attribute_memory_usage_dead_bytes">content.proton.documentdb.attribute.memory_usage.dead_bytes</p></td> - <td>byte</td> - <td>The number of dead bytes (<= used_bytes)</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_attribute_memory_usage_onhold_bytes">content.proton.documentdb.attribute.memory_usage.onhold_bytes</p></td> - <td>byte</td> - <td>The number of bytes on hold</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_attribute_memory_usage_used_bytes">content.proton.documentdb.attribute.memory_usage.used_bytes</p></td> - <td>byte</td> - <td>The number of used bytes (<= allocated_bytes)</td> - </tr> - <tr> - <td><p id="content_proton_resource_usage_cpu_util_setup">content.proton.resource_usage.cpu_util.setup</p></td> - <td>fraction</td> - <td>cpu used by system init and (re-)configuration</td> - </tr> - <tr> - <td><p id="content_proton_resource_usage_cpu_util_read">content.proton.resource_usage.cpu_util.read</p></td> - <td>fraction</td> - <td>cpu used by reading data from the system</td> - </tr> - <tr> - <td><p id="content_proton_resource_usage_cpu_util_write">content.proton.resource_usage.cpu_util.write</p></td> - <td>fraction</td> - <td>cpu used by writing data to the system</td> - </tr> - <tr> - <td><p id="content_proton_resource_usage_cpu_util_compact">content.proton.resource_usage.cpu_util.compact</p></td> - <td>fraction</td> - <td>cpu used by internal data re-structuring</td> - </tr> - <tr> - <td><p id="content_proton_resource_usage_cpu_util_other">content.proton.resource_usage.cpu_util.other</p></td> - <td>fraction</td> - <td>cpu used by work not classified as a specific category</td> - </tr> - <tr> - <td><p id="content_proton_transactionlog_entries">content.proton.transactionlog.entries</p></td> - <td>record</td> - <td>The current number of entries in the transaction log</td> - </tr> - <tr> - <td><p id="content_proton_transactionlog_disk_usage">content.proton.transactionlog.disk_usage</p></td> - <td>byte</td> - <td>The disk usage (in bytes) of the transaction log</td> - </tr> - <tr> - <td><p id="content_proton_transactionlog_replay_time">content.proton.transactionlog.replay_time</p></td> - <td>second</td> - <td>The replay time (in seconds) of the transaction log during start-up</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_ready_document_store_disk_usage">content.proton.documentdb.ready.document_store.disk_usage</p></td> - <td>byte</td> - <td>Disk space usage in bytes</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_ready_document_store_disk_bloat">content.proton.documentdb.ready.document_store.disk_bloat</p></td> - <td>byte</td> - <td>Disk space bloat in bytes</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_ready_document_store_max_bucket_spread">content.proton.documentdb.ready.document_store.max_bucket_spread</p></td> - <td>fraction</td> - <td>Max bucket spread in underlying files (sum(unique buckets in each chunk)/unique buckets in file)</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_ready_document_store_memory_usage_allocated_bytes">content.proton.documentdb.ready.document_store.memory_usage.allocated_bytes</p></td> - <td>byte</td> - <td>The number of allocated bytes</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_ready_document_store_memory_usage_used_bytes">content.proton.documentdb.ready.document_store.memory_usage.used_bytes</p></td> - <td>byte</td> - <td>The number of used bytes (<= allocated_bytes)</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_ready_document_store_memory_usage_dead_bytes">content.proton.documentdb.ready.document_store.memory_usage.dead_bytes</p></td> - <td>byte</td> - <td>The number of dead bytes (<= used_bytes)</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_ready_document_store_memory_usage_onhold_bytes">content.proton.documentdb.ready.document_store.memory_usage.onhold_bytes</p></td> - <td>byte</td> - <td>The number of bytes on hold</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_notready_document_store_disk_usage">content.proton.documentdb.notready.document_store.disk_usage</p></td> - <td>byte</td> - <td>Disk space usage in bytes</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_notready_document_store_disk_bloat">content.proton.documentdb.notready.document_store.disk_bloat</p></td> - <td>byte</td> - <td>Disk space bloat in bytes</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_notready_document_store_max_bucket_spread">content.proton.documentdb.notready.document_store.max_bucket_spread</p></td> - <td>fraction</td> - <td>Max bucket spread in underlying files (sum(unique buckets in each chunk)/unique buckets in file)</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_notready_document_store_memory_usage_allocated_bytes">content.proton.documentdb.notready.document_store.memory_usage.allocated_bytes</p></td> - <td>byte</td> - <td>The number of allocated bytes</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_notready_document_store_memory_usage_used_bytes">content.proton.documentdb.notready.document_store.memory_usage.used_bytes</p></td> - <td>byte</td> - <td>The number of used bytes (<= allocated_bytes)</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_notready_document_store_memory_usage_dead_bytes">content.proton.documentdb.notready.document_store.memory_usage.dead_bytes</p></td> - <td>byte</td> - <td>The number of dead bytes (<= used_bytes)</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_notready_document_store_memory_usage_onhold_bytes">content.proton.documentdb.notready.document_store.memory_usage.onhold_bytes</p></td> - <td>byte</td> - <td>The number of bytes on hold</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_removed_document_store_disk_usage">content.proton.documentdb.removed.document_store.disk_usage</p></td> - <td>byte</td> - <td>Disk space usage in bytes</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_removed_document_store_disk_bloat">content.proton.documentdb.removed.document_store.disk_bloat</p></td> - <td>byte</td> - <td>Disk space bloat in bytes</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_removed_document_store_max_bucket_spread">content.proton.documentdb.removed.document_store.max_bucket_spread</p></td> - <td>fraction</td> - <td>Max bucket spread in underlying files (sum(unique buckets in each chunk)/unique buckets in file)</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_removed_document_store_memory_usage_allocated_bytes">content.proton.documentdb.removed.document_store.memory_usage.allocated_bytes</p></td> - <td>byte</td> - <td>The number of allocated bytes</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_removed_document_store_memory_usage_used_bytes">content.proton.documentdb.removed.document_store.memory_usage.used_bytes</p></td> - <td>byte</td> - <td>The number of used bytes (<= allocated_bytes)</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_removed_document_store_memory_usage_dead_bytes">content.proton.documentdb.removed.document_store.memory_usage.dead_bytes</p></td> - <td>byte</td> - <td>The number of dead bytes (<= used_bytes)</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_removed_document_store_memory_usage_onhold_bytes">content.proton.documentdb.removed.document_store.memory_usage.onhold_bytes</p></td> - <td>byte</td> - <td>The number of bytes on hold</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_ready_document_store_cache_elements">content.proton.documentdb.ready.document_store.cache.elements</p></td> - <td>item</td> - <td>Number of elements in the cache</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_ready_document_store_cache_memory_usage">content.proton.documentdb.ready.document_store.cache.memory_usage</p></td> - <td>byte</td> - <td>Memory usage of the cache (in bytes)</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_ready_document_store_cache_hit_rate">content.proton.documentdb.ready.document_store.cache.hit_rate</p></td> - <td>fraction</td> - <td>Rate of hits in the cache compared to number of lookups</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_ready_document_store_cache_lookups">content.proton.documentdb.ready.document_store.cache.lookups</p></td> - <td>operation</td> - <td>Number of lookups in the cache (hits + misses)</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_ready_document_store_cache_invalidations">content.proton.documentdb.ready.document_store.cache.invalidations</p></td> - <td>operation</td> - <td>Number of invalidations (erased elements) in the cache. </td> - </tr> - <tr> - <td><p id="content_proton_documentdb_notready_document_store_cache_elements">content.proton.documentdb.notready.document_store.cache.elements</p></td> - <td>item</td> - <td>Number of elements in the cache</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_notready_document_store_cache_memory_usage">content.proton.documentdb.notready.document_store.cache.memory_usage</p></td> - <td>byte</td> - <td>Memory usage of the cache (in bytes)</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_notready_document_store_cache_hit_rate">content.proton.documentdb.notready.document_store.cache.hit_rate</p></td> - <td>fraction</td> - <td>Rate of hits in the cache compared to number of lookups</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_notready_document_store_cache_lookups">content.proton.documentdb.notready.document_store.cache.lookups</p></td> - <td>operation</td> - <td>Number of lookups in the cache (hits + misses)</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_notready_document_store_cache_invalidations">content.proton.documentdb.notready.document_store.cache.invalidations</p></td> - <td>operation</td> - <td>Number of invalidations (erased elements) in the cache. </td> - </tr> - <tr> - <td><p id="content_proton_documentdb_removed_document_store_cache_elements">content.proton.documentdb.removed.document_store.cache.elements</p></td> - <td>item</td> - <td>Number of elements in the cache</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_removed_document_store_cache_hit_rate">content.proton.documentdb.removed.document_store.cache.hit_rate</p></td> - <td>fraction</td> - <td>Rate of hits in the cache compared to number of lookups</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_removed_document_store_cache_invalidations">content.proton.documentdb.removed.document_store.cache.invalidations</p></td> - <td>item</td> - <td>Number of invalidations (erased elements) in the cache. </td> - </tr> - <tr> - <td><p id="content_proton_documentdb_removed_document_store_cache_lookups">content.proton.documentdb.removed.document_store.cache.lookups</p></td> - <td>operation</td> - <td>Number of lookups in the cache (hits + misses)</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_removed_document_store_cache_memory_usage">content.proton.documentdb.removed.document_store.cache.memory_usage</p></td> - <td>byte</td> - <td>Memory usage of the cache (in bytes)</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_ready_attribute_memory_usage_allocated_bytes">content.proton.documentdb.ready.attribute.memory_usage.allocated_bytes</p></td> - <td>byte</td> - <td>The number of allocated bytes</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_ready_attribute_memory_usage_used_bytes">content.proton.documentdb.ready.attribute.memory_usage.used_bytes</p></td> - <td>byte</td> - <td>The number of used bytes (<= allocated_bytes)</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_ready_attribute_memory_usage_dead_bytes">content.proton.documentdb.ready.attribute.memory_usage.dead_bytes</p></td> - <td>byte</td> - <td>The number of dead bytes (<= used_bytes)</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_ready_attribute_memory_usage_onhold_bytes">content.proton.documentdb.ready.attribute.memory_usage.onhold_bytes</p></td> - <td>byte</td> - <td>The number of bytes on hold</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_ready_attribute_disk_usage">content.proton.documentdb.ready.attribute.disk_usage</p></td> - <td>byte</td> - <td>Disk space usage (in bytes) of the flushed snapshot of this attribute for this document type</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_notready_attribute_memory_usage_allocated_bytes">content.proton.documentdb.notready.attribute.memory_usage.allocated_bytes</p></td> - <td>byte</td> - <td>The number of allocated bytes</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_notready_attribute_memory_usage_used_bytes">content.proton.documentdb.notready.attribute.memory_usage.used_bytes</p></td> - <td>byte</td> - <td>The number of used bytes (<= allocated_bytes)</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_notready_attribute_memory_usage_dead_bytes">content.proton.documentdb.notready.attribute.memory_usage.dead_bytes</p></td> - <td>byte</td> - <td>The number of dead bytes (<= used_bytes)</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_notready_attribute_memory_usage_onhold_bytes">content.proton.documentdb.notready.attribute.memory_usage.onhold_bytes</p></td> - <td>byte</td> - <td>The number of bytes on hold</td> - </tr> - <tr> - <td><p id="content_proton_index_cache_postinglist_elements">content.proton.index.cache.postinglist.elements</p></td> - <td>item</td> - <td>Number of elements in the cache. Contains disk index posting list files across all document types</td> - </tr> - <tr> - <td><p id="content_proton_index_cache_postinglist_memory_usage">content.proton.index.cache.postinglist.memory_usage</p></td> - <td>byte</td> - <td>Memory usage of the cache (in bytes). Contains disk index posting list files across all document types</td> - </tr> - <tr> - <td><p id="content_proton_index_cache_postinglist_hit_rate">content.proton.index.cache.postinglist.hit_rate</p></td> - <td>fraction</td> - <td>Rate of hits in the cache compared to number of lookups. Contains disk index posting list files across all document types</td> - </tr> - <tr> - <td><p id="content_proton_index_cache_postinglist_lookups">content.proton.index.cache.postinglist.lookups</p></td> - <td>operation</td> - <td>Number of lookups in the cache (hits + misses). Contains disk index posting list files across all document types</td> - </tr> - <tr> - <td><p id="content_proton_index_cache_postinglist_invalidations">content.proton.index.cache.postinglist.invalidations</p></td> - <td>operation</td> - <td>Number of invalidations (erased elements) in the cache. Contains disk index posting list files across all document types</td> - </tr> - <tr> - <td><p id="content_proton_index_cache_bitvector_elements">content.proton.index.cache.bitvector.elements</p></td> - <td>item</td> - <td>Number of elements in the cache. Contains disk index bitvector files across all document types</td> - </tr> - <tr> - <td><p id="content_proton_index_cache_bitvector_memory_usage">content.proton.index.cache.bitvector.memory_usage</p></td> - <td>byte</td> - <td>Memory usage of the cache (in bytes). Contains disk index bitvector files across all document types</td> - </tr> - <tr> - <td><p id="content_proton_index_cache_bitvector_hit_rate">content.proton.index.cache.bitvector.hit_rate</p></td> - <td>fraction</td> - <td>Rate of hits in the cache compared to number of lookups. Contains disk index bitvector files across all document types</td> - </tr> - <tr> - <td><p id="content_proton_index_cache_bitvector_lookups">content.proton.index.cache.bitvector.lookups</p></td> - <td>operation</td> - <td>Number of lookups in the cache (hits + misses). Contains disk index bitvector files across all document types</td> - </tr> - <tr> - <td><p id="content_proton_index_cache_bitvector_invalidations">content.proton.index.cache.bitvector.invalidations</p></td> - <td>operation</td> - <td>Number of invalidations (erased elements) in the cache. Contains disk index bitvector files across all document types</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_index_memory_usage_allocated_bytes">content.proton.documentdb.index.memory_usage.allocated_bytes</p></td> - <td>byte</td> - <td>The number of allocated bytes for the memory index for this document type</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_index_memory_usage_used_bytes">content.proton.documentdb.index.memory_usage.used_bytes</p></td> - <td>byte</td> - <td>The number of used bytes (<= allocated_bytes) for the memory index for this document type</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_index_memory_usage_dead_bytes">content.proton.documentdb.index.memory_usage.dead_bytes</p></td> - <td>byte</td> - <td>The number of dead bytes (<= used_bytes) for the memory index for this document type</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_index_memory_usage_onhold_bytes">content.proton.documentdb.index.memory_usage.onhold_bytes</p></td> - <td>byte</td> - <td>The number of bytes on hold for the memory index for this document type</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_index_disk_usage">content.proton.documentdb.index.disk_usage</p></td> - <td>byte</td> - <td>Disk space usage (in bytes) of all disk indexes for this document type</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_index_indexes">content.proton.documentdb.index.indexes</p></td> - <td>item</td> - <td>Number of disk or memory indexes</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_index_io_search_read_bytes">content.proton.documentdb.index.io.search.read_bytes</p></td> - <td>byte</td> - <td>Bytes read from disk index posting list and bitvector files as part of search for this document type</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_index_io_search_cached_read_bytes">content.proton.documentdb.index.io.search.cached_read_bytes</p></td> - <td>byte</td> - <td>Bytes read from cached disk index posting list and bitvector files as part of search for this document type</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_ready_index_memory_usage_allocated_bytes">content.proton.documentdb.ready.index.memory_usage.allocated_bytes</p></td> - <td>byte</td> - <td>The number of allocated bytes for this index field in the memory index for this document type</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_ready_index_disk_usage">content.proton.documentdb.ready.index.disk_usage</p></td> - <td>byte</td> - <td>Disk space usage (in bytes) of this index field in all disk indexes for this document type</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_matching_queries">content.proton.documentdb.matching.queries</p></td> - <td>query</td> - <td>Number of queries executed</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_matching_soft_doomed_queries">content.proton.documentdb.matching.soft_doomed_queries</p></td> - <td>query</td> - <td>Number of queries hitting the soft timeout</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_matching_query_latency">content.proton.documentdb.matching.query_latency</p></td> - <td>second</td> - <td>Total average latency (sec) when matching and ranking a query</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_matching_query_setup_time">content.proton.documentdb.matching.query_setup_time</p></td> - <td>second</td> - <td>Average time (sec) spent setting up and tearing down queries</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_matching_docs_matched">content.proton.documentdb.matching.docs_matched</p></td> - <td>document</td> - <td>Number of documents matched</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_matching_docs_ranked">content.proton.documentdb.matching.docs_ranked</p></td> - <td>document</td> - <td>Number of documents ranked (first phase)</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_matching_docs_reranked">content.proton.documentdb.matching.docs_reranked</p></td> - <td>document</td> - <td>Number of documents re-ranked (second phase)</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_matching_exact_nns_distances_computed">content.proton.documentdb.matching.exact_nns_distances_computed</p></td> - <td>distance</td> - <td>Number of distances computed in exact nearest-neighbor search</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_matching_approximate_nns_distances_computed">content.proton.documentdb.matching.approximate_nns_distances_computed</p></td> - <td>distance</td> - <td>Number of distances computed in approximate nearest-neighbor search</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_matching_approximate_nns_nodes_visited">content.proton.documentdb.matching.approximate_nns_nodes_visited</p></td> - <td>graph_node</td> - <td>Number of nodes visited in approximate nearest-neighbor search</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_matching_rank_profile_queries">content.proton.documentdb.matching.rank_profile.queries</p></td> - <td>query</td> - <td>Number of queries executed</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_matching_rank_profile_soft_doomed_queries">content.proton.documentdb.matching.rank_profile.soft_doomed_queries</p></td> - <td>query</td> - <td>Number of queries hitting the soft timeout</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_matching_rank_profile_soft_doom_factor">content.proton.documentdb.matching.rank_profile.soft_doom_factor</p></td> - <td>fraction</td> - <td>Factor used to compute soft-timeout</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_matching_rank_profile_query_latency">content.proton.documentdb.matching.rank_profile.query_latency</p></td> - <td>second</td> - <td>Total average latency (sec) when matching and ranking a query</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_matching_rank_profile_query_setup_time">content.proton.documentdb.matching.rank_profile.query_setup_time</p></td> - <td>second</td> - <td>Average time (sec) spent setting up and tearing down queries</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_matching_rank_profile_grouping_time">content.proton.documentdb.matching.rank_profile.grouping_time</p></td> - <td>second</td> - <td>Average time (sec) spent on grouping</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_matching_rank_profile_rerank_time">content.proton.documentdb.matching.rank_profile.rerank_time</p></td> - <td>second</td> - <td>Average time (sec) spent on 2nd phase ranking</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_matching_rank_profile_docs_matched">content.proton.documentdb.matching.rank_profile.docs_matched</p></td> - <td>document</td> - <td>Number of documents matched</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_matching_rank_profile_docs_ranked">content.proton.documentdb.matching.rank_profile.docs_ranked</p></td> - <td>document</td> - <td>Number of documents ranked (first phase)</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_matching_rank_profile_docs_reranked">content.proton.documentdb.matching.rank_profile.docs_reranked</p></td> - <td>document</td> - <td>Number of documents re-ranked (second phase)</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_matching_rank_profile_exact_nns_distances_computed">content.proton.documentdb.matching.rank_profile.exact_nns_distances_computed</p></td> - <td>distance</td> - <td>Number of distances computed in exact nearest-neighbor search</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_matching_rank_profile_approximate_nns_distances_computed">content.proton.documentdb.matching.rank_profile.approximate_nns_distances_computed</p></td> - <td>distance</td> - <td>Number of distances computed in approximate nearest-neighbor search</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_matching_rank_profile_approximate_nns_nodes_visited">content.proton.documentdb.matching.rank_profile.approximate_nns_nodes_visited</p></td> - <td>graph_node</td> - <td>Number of nodes visited in approximate nearest-neighbor search</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_matching_rank_profile_limited_queries">content.proton.documentdb.matching.rank_profile.limited_queries</p></td> - <td>query</td> - <td>Number of queries limited in match phase</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_matching_rank_profile_docid_partition_active_time">content.proton.documentdb.matching.rank_profile.docid_partition.active_time</p></td> - <td>second</td> - <td>Time (sec) spent doing actual work</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_matching_rank_profile_docid_partition_docs_matched">content.proton.documentdb.matching.rank_profile.docid_partition.docs_matched</p></td> - <td>document</td> - <td>Number of documents matched</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_matching_rank_profile_docid_partition_docs_ranked">content.proton.documentdb.matching.rank_profile.docid_partition.docs_ranked</p></td> - <td>document</td> - <td>Number of documents ranked (first phase)</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_matching_rank_profile_docid_partition_docs_reranked">content.proton.documentdb.matching.rank_profile.docid_partition.docs_reranked</p></td> - <td>document</td> - <td>Number of documents re-ranked (second phase)</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_matching_rank_profile_docid_partition_wait_time">content.proton.documentdb.matching.rank_profile.docid_partition.wait_time</p></td> - <td>second</td> - <td>Time (sec) spent waiting for other external threads and resources</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_matching_rank_profile_match_time">content.proton.documentdb.matching.rank_profile.match_time</p></td> - <td>second</td> - <td>Average time (sec) for matching a query (1st phase)</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_feeding_commit_operations">content.proton.documentdb.feeding.commit.operations</p></td> - <td>operation</td> - <td>Number of operations included in a commit</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_feeding_commit_latency">content.proton.documentdb.feeding.commit.latency</p></td> - <td>second</td> - <td>Latency for commit in seconds</td> - </tr> - <tr> - <td><p id="content_proton_session_cache_grouping_num_cached">content.proton.session_cache.grouping.num_cached</p></td> - <td>session</td> - <td>Number of currently cached sessions</td> - </tr> - <tr> - <td><p id="content_proton_session_cache_grouping_num_dropped">content.proton.session_cache.grouping.num_dropped</p></td> - <td>session</td> - <td>Number of dropped cached sessions</td> - </tr> - <tr> - <td><p id="content_proton_session_cache_grouping_num_insert">content.proton.session_cache.grouping.num_insert</p></td> - <td>session</td> - <td>Number of inserted sessions</td> - </tr> - <tr> - <td><p id="content_proton_session_cache_grouping_num_pick">content.proton.session_cache.grouping.num_pick</p></td> - <td>session</td> - <td>Number if picked sessions</td> - </tr> - <tr> - <td><p id="content_proton_session_cache_grouping_num_timedout">content.proton.session_cache.grouping.num_timedout</p></td> - <td>session</td> - <td>Number of timed out sessions</td> - </tr> - <tr> - <td><p id="content_proton_session_cache_search_num_cached">content.proton.session_cache.search.num_cached</p></td> - <td>session</td> - <td>Number of currently cached sessions</td> - </tr> - <tr> - <td><p id="content_proton_session_cache_search_num_dropped">content.proton.session_cache.search.num_dropped</p></td> - <td>session</td> - <td>Number of dropped cached sessions</td> - </tr> - <tr> - <td><p id="content_proton_session_cache_search_num_insert">content.proton.session_cache.search.num_insert</p></td> - <td>session</td> - <td>Number of inserted sessions</td> - </tr> - <tr> - <td><p id="content_proton_session_cache_search_num_pick">content.proton.session_cache.search.num_pick</p></td> - <td>session</td> - <td>Number if picked sessions</td> - </tr> - <tr> - <td><p id="content_proton_session_cache_search_num_timedout">content.proton.session_cache.search.num_timedout</p></td> - <td>session</td> - <td>Number of timed out sessions</td> - </tr> - <tr> - <td><p id="metricmanager_periodichooklatency">metricmanager.periodichooklatency</p></td> - <td>millisecond</td> - <td>Time in ms used to update a single periodic hook</td> - </tr> - <tr> - <td><p id="metricmanager_resetlatency">metricmanager.resetlatency</p></td> - <td>millisecond</td> - <td>Time in ms used to reset all metrics.</td> - </tr> - <tr> - <td><p id="metricmanager_sleeptime">metricmanager.sleeptime</p></td> - <td>millisecond</td> - <td>Time in ms worker thread is sleeping</td> - </tr> - <tr> - <td><p id="metricmanager_snapshothooklatency">metricmanager.snapshothooklatency</p></td> - <td>millisecond</td> - <td>Time in ms used to update a single snapshot hook</td> - </tr> - <tr> - <td><p id="metricmanager_snapshotlatency">metricmanager.snapshotlatency</p></td> - <td>millisecond</td> - <td>Time in ms used to take a snapshot</td> - </tr> - </tbody> -</table> diff --git a/en/reference/security/mtls.md b/en/reference/security/mtls.md deleted file mode 100644 index e5c9f049cf..0000000000 --- a/en/reference/security/mtls.md +++ /dev/null @@ -1,250 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Mutually authenticated TLS (mTLS) reference" -redirect_from: - - en/reference/mtls.html ---- - -{% include note.html content="See [Securing a self-hosted Vespa application with mutually authenticated TLS (mTLS)](../../security/mtls) -for a practical guide." %} - -## Environment variables - -<table class="table table-striped"> - <tr><th>Name</th><th>Description</th></tr> - <tr> - <td>VESPA_TLS_CONFIG_FILE</td> - <td>Absolute path JSON configuration file with TLS configuration.</td> - </tr> - <tr> - <td>VESPA_TLS_INSECURE_MIXED_MODE</td> - <td markdown="span">Enables TLS mixed mode. See [TLS Mixed mode](#tls-mixed-mode) for possible values.</td> - </tr> -</table> - -### TLS mixed mode - -Possible TLS mixed mode settings for `VESPA_TLS_INSECURE_MIXED_MODE`: - -<table class="table table-striped"> - <tr><th>Name</th><th>Description</th></tr> - <tr> - <td>plaintext_client_mixed_server</td> - <td>Clients do not use TLS, servers accept both TLS and plaintext clients.</td> - </tr> - <tr> - <td>tls_client_mixed_server</td> - <td>Clients use TLS, servers accept both TLS and plaintext clients.</td> - </tr> - <tr> - <td>tls_client_tls_server</td> - <td>All clients and servers use TLS only.</td> - </tr> -</table> - -### Configuration file -The TLS configuration file contains a single top-level JSON object. - -#### Top-level elements -<table class="table table-striped"> - <tr><th>Name</th><th>Required</th><th>Description</th></tr> - <tr> - <td markdown="span">[files](#the-files-element)</td> - <td>Yes</td> - <td>JSON object containing file system paths crypto material.</td> - </tr> - <tr> - <td>authorized-peers</td> - <td>No</td> - <td markdown="span"> - JSON array of [authorized-peer](#the-authorized-peer-element) objects. Authorization engine is disabled if not specified. - See dedicated [section](#peer-authorization-rules) on how to create peer authorization rules.</td> - </tr> - <tr> - <td>accepted-ciphers</td> - <td>No</td> - <td markdown="span"> - JSON array of accepted TLS cipher suites. See [here](#cipher-suites) for cipher suites enabled by default. - You can only specify a _subset_ of the default cipher suites. - _This is an expert option_—use the default unless you have good reasons not to. - </td> - </tr> - <tr> - <td>accepted-protocols</td> - <td>No</td> - <td markdown="span"> - JSON array of accepted TLS protocol versions. See [here](#protocol-versions) for TLS versions enabled by default. - You can only specify a _subset_ of the default protocol versions. - _This is an expert option_—use the default unless you have good reasons not to. - </td> - </tr> - <tr> - <td>disable-hostname-validation</td> - <td>No</td> - <td markdown="span">Disables TLS/HTTPS hostname validation. Enabled by default (default value `false`).</td> - </tr> -</table> - -#### The *files* element -<table class="table table-striped"> - <tr><th>Name</th><th>Required</th><th>Description</th></tr> - <tr> - <td>private-key</td> - <td>Yes</td> - <td>Absolute path to file containing the private key in PKCS#8 PEM format.</td> - </tr> - <tr> - <td>certificate</td> - <td>Yes</td> - <td> - Absolute path to file containing X.509 certificate chain (including any intermediate certificates). - Certificates must be encoded in PEM format separated by newlines. - </td> - </tr> - <tr> - <td>ca-certificates</td> - <td>Yes</td> - <td> - Absolute path to file containing all trusted X.509 Certificate Authorities. - Certificates must be encoded in PEM format separated by newlines. - </td> - </tr> -</table> - -#### The *authorized-peer* element -<table class="table table-striped"> - <tr><th>Name</th><th>Required</th><th>Description</th></tr> - <tr> - <td>required-credentials</td> - <td>Yes</td> - <td markdown="span">A JSON array specifying each [credential requirement](#the-required-credential-element) for this particular rule.</td> - </tr> - <tr> - <td>name</td> - <td>Yes</td> - <td>Name of the rule.</td> - </tr> - <tr> - <td>description</td> - <td>No</td> - <td>Description of the rule.</td> - </tr> -</table> - -#### The *required-credential* element -<table class="table table-striped"> - <tr><th>Name</th><th>Required</th><th>Description</th></tr> - <tr> - <td>field</td> - <td>Yes</td> - <td markdown="span">Certificate field. Possible values: *CN*, *SAN_DNS*, *SAN_URI*.</td> - </tr> - <tr> - <td>must-match</td> - <td>Yes</td> - <td>String containing a "glob"-style pattern.</td> - </tr> -</table> - -#### Peer authorization rules -The `authorized-peers` member is an array of credential rule-set objects. For a peer to be considered authorized its certificate MUST match at least one rule set completely. - -Each rule set must contain a `required-credentials` array of credential matchers. For a certificate to match a rule set it MUST match all its credential matchers. - -A credential is matched by checking a pattern given in `must-match` against a specified certificate `field`. The following fields are currently supported: -* *CN* - the Common Name part of the certificate's Distinguished Name information. If multiple CN entries are present, the last one will be considered. -* *SAN_DNS* - a Subject Alternate Name with type DNS. A certificate may contain many SAN entries. If so, all entries are checked and the credential is considered a match if at least one entry matches. -* *SAN_URI* - a Subject Alternate Name with type URI. It is similar to *SAN_DNS* but with slightly different pattern matching semantics. - -For *CN* and *SAN_DNS* fields, the `must-match` pattern is a "glob"-style pattern with the following semantics: -* `*` matches 0-n non-dot characters within a single dot-separated hostname part. This is similar to the wildcards used by certificates for HTTPS hostname validation. Examples - * `*.baz` matches `bar.baz` but not `foo.bar.baz` or `foo.baz.bar`. - * `*.*.baz` matches `foo.bar.baz` but not `bar.baz`. - * `*-myservice` matches `foo-myservice` but not `bar.foo-myservice`. -* `?` matches exactly 1 non-dot character within a single dot-separated hostname part. Examples: - * `?.bar` matches `x.bar` but not `bar`, `.bar` or `yx.bar`. - * `?.?.baz` matches `x.y.baz` but not `x.baz` or `xx.yy.baz`. - -For *SAN_URI* fields, `must-match` is also a "glob"-style pattern, with some deviation compared to *CN*/*SAN_DNS*: -* The `*` wildcard matches 0-n non-slash characters. A `/` is used as separator between the host and path components, as well as separator between path segments. Examples: - * `vespa://myapp/content/*` matches `vespa://myapp/content/node1` but not `vespa://myapp/container/node1` or `vespa://myapp/content/node1/myservice`. - * `vespa://*/*/*` matches `vespa://myapp/content/node1` but not `vespa://myapp/content` or `vespa://myapp/content/node1/myservice`. -* `?` in a pattern has no special behaviour - it only matches the `?` literal. URIs use `?` as separator between the path and query components. - -The description field is optional and is useful for e.g. documenting why a particular ruleset is present. It has no semantic meaning to the authorization engine. - -Not providing the `authorized-peers` field means only certificate validity is used for authorization. -If the `authorized-peers` field is provided, it must contain at least one entry. - -#### Example - -```json -{ - "files": { - "ca-certificates": "/absolute/path/to/ca-certs.pem", - "certificates": "/absolute/path/to/host-certs.pem", - "private-key": "/absolute/path/to/private-key.pem", - "disable-hostname-validation": false - }, - "authorized-peers": [ - { - "required-credentials": [ - { "field": "CN", "must-match": "vespa-monitoring.example.com" }, - { "field": "SAN_DNS", "must-match": "*.us-east-*.monitor.example.com" } - ], - "description": "Backend monitoring service access", - "name": "monitoring" - }, - { - "required-credentials": [ - { "field": "SAN_DNS", "must-match": "*.mycluster.vespa.example.com" } - ], - "description": "Cluster-internal node P2P access", - "name": "cluster" - } - ] -} -``` -## TLS features supported by Vespa -Vespa is built with modern, high-performance cryptography libraries. For security reasons, -the Vespa TLS stack has some additional constraints that are always present: -* `TLSv1.2` is the oldest TLS version that can be negotiated. -* Only cipher suites supporting [forward secrecy](https://en.wikipedia.org/wiki/Forward_secrecy) can be negotiated - (i.e. cipher suites using ECDHE as part of their key exchange). -* Only modern, symmetric ciphers with - [AEAD](https://en.wikipedia.org/wiki/Authenticated_encryption#Authenticated_encryption_with_associated_data) - properties are supported. In practice this means [AES-GCM](https://en.wikipedia.org/wiki/AES-GCM) or - [ChaCha20-Poly1305](https://en.wikipedia.org/wiki/ChaCha20-Poly1305). - Supported cipher suites are listed [here](#cipher-suites). -* TLS compression is explicitly disabled to mitigate [CRIME](https://en.wikipedia.org/wiki/CRIME)/BREACH-style - compression oracle attacks. -* TLS renegotiation is explicitly disabled. -* TLS session resumption is explicitly disabled, as this opens up some potential vulnerabilities related to replay attacks. - Note that the Vespa application container edge does support session resumption, due to needing to support many frequent, - short-lived connections from proxies and clients. - -{% include note.html content="The above assumes you are using a Vespa version built with our `vespa_openssl` -package (which is the case for all Open-Source RPMs and Docker images). -If you are doing a custom build, it is highly recommended building against OpenSSL 1.1.1 or newer. -Older versions may have performance regressions or reduced crypto functionality. -OpenSSL versions prior to 1.0.1 are not supported." -%} - -### Default TLS protocol settings -Vespa will by default use the following TLS configuration (unless overridden by `accepted-ciphers` / `accepted-protocols`). - -#### Protocol versions -* `TLSv1.3` - _note:_ due to certain limitations in the current Java runtime, TLSv1.3 is only supported by the C++ backends for now. - We will revisit this in the near future to ensure Java defaults to TLSv1.3 as well. -* `TLSv1.2` - -#### Cipher suites -* `TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384` -* `TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384` -* `TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256` (JDK 12+) -* `TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256` -* `TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256` -* `TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256` -* `TLS_AES_128_GCM_SHA256` (TLSv1.3) -* `TLS_AES_256_GCM_SHA384` (TLSv1.3) -* `TLS_CHACHA20_POLY1305_SHA256` (TLSv1.3, JDK 12+) diff --git a/en/reference/security/mtls.mdx b/en/reference/security/mtls.mdx new file mode 100644 index 0000000000..c0c36f2326 --- /dev/null +++ b/en/reference/security/mtls.mdx @@ -0,0 +1,294 @@ +--- +# Copyright Vespa.ai. All rights reserved. +title: "Mutually authenticated TLS (mTLS) reference" +sidebarTitle: "Mtls" +--- + + +<Note> +**Note:** + +See [Securing a self-hosted Vespa application with mutually authenticated TLS (mTLS)](/en/security/mtls) for a practical guide. +</Note> + + +## Environment variables + + +<table className="vespa-table"> + <thead> + <tr> + <th>Name</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>VESPA_TLS_CONFIG_FILE</td> + <td>Absolute path JSON configuration file with TLS configuration.</td> + </tr> + <tr> + <td>VESPA_TLS_INSECURE_MIXED_MODE</td> + <td>Enables TLS mixed mode. See <a href="#tls-mixed-mode" aria-label="TLS Mixed mode">TLS Mixed mode</a> for possible values.</td> + </tr> + </tbody> +</table> + + +### TLS mixed mode + +Possible TLS mixed mode settings for `VESPA_TLS_INSECURE_MIXED_MODE`: + + +<table className="vespa-table"> + <thead> + <tr> + <th>Name</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>plaintext_client_mixed_server</td> + <td>Clients do not use TLS, servers accept both TLS and plaintext clients.</td> + </tr> + <tr> + <td>tls_client_mixed_server</td> + <td>Clients use TLS, servers accept both TLS and plaintext clients.</td> + </tr> + <tr> + <td>tls_client_tls_server</td> + <td>All clients and servers use TLS only.</td> + </tr> + </tbody> +</table> + + +### Configuration file + +The TLS configuration file contains a single top-level JSON object. + +#### Top-level elements + +<table className="vespa-table"> + <thead> + <tr> + <th>Name</th> + <th>Required</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td><a href="#the-files-element" aria-label="files">files</a></td> + <td>Yes</td> + <td>JSON object containing file system paths crypto material.</td> + </tr> + <tr> + <td>authorized-peers</td> + <td>No</td> + <td>JSON array of <a href="#the-authorized-peer-element" aria-label="authorized-peer">authorized-peer</a> objects. Authorization engine is disabled if not specified. See dedicated <a href="#peer-authorization-rules" aria-label="section">section</a> on how to create peer authorization rules.</td> + </tr> + <tr> + <td>accepted-ciphers</td> + <td>No</td> + <td>JSON array of accepted TLS cipher suites. See <a href="#cipher-suites" aria-label="here">here</a> for cipher suites enabled by default. You can only specify a *subset* of the default cipher suites. *This is an expert option*—use the default unless you have good reasons not to.</td> + </tr> + <tr> + <td>accepted-protocols</td> + <td>No</td> + <td>JSON array of accepted TLS protocol versions. See <a href="#protocol-versions" aria-label="here">here</a> for TLS versions enabled by default. You can only specify a *subset* of the default protocol versions. *This is an expert option*—use the default unless you have good reasons not to.</td> + </tr> + <tr> + <td>disable-hostname-validation</td> + <td>No</td> + <td>Disables TLS/HTTPS hostname validation. Enabled by default (default value false).</td> + </tr> + </tbody> +</table> + + + +#### The *files* element + +<table className="vespa-table"> + <thead> + <tr> + <th>Name</th> + <th>Required</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>private-key</td> + <td>Yes</td> + <td>Absolute path to file containing the private key in PKCS#8 PEM format.</td> + </tr> + <tr> + <td>certificate</td> + <td>Yes</td> + <td>Absolute path to file containing X.509 certificate chain (including any intermediate certificates). Certificates must be encoded in PEM format separated by newlines.</td> + </tr> + <tr> + <td>ca-certificates</td> + <td>Yes</td> + <td>Absolute path to file containing all trusted X.509 Certificate Authorities. Certificates must be encoded in PEM format separated by newlines.</td> + </tr> + </tbody> +</table> + + +#### The *authorized-peer* element + +<table className="vespa-table"> + <thead> + <tr> + <th>Name</th> + <th>Required</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>required-credentials</td> + <td>Yes</td> + <td>A JSON array specifying each <a href="#the-required-credential-element" aria-label="credential requirement">credential requirement</a> for this particular rule.</td> + </tr> + <tr> + <td>name</td> + <td>Yes</td> + <td>Name of the rule.</td> + </tr> + <tr> + <td>description</td> + <td>No</td> + <td>Description of the rule.</td> + </tr> + </tbody> +</table> + + +#### The *required-credential* element + +<table className="vespa-table"> + <thead> + <tr> + <th>Name</th> + <th>Required</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>field</td> + <td>Yes</td> + <td>Certificate field. Possible values: *CN*, *SAN_DNS*, *SAN_URI*.</td> + </tr> + <tr> + <td>must-match</td> + <td>Yes</td> + <td>String containing a "glob"-style pattern.</td> + </tr> + </tbody> +</table> + + + +#### Peer authorization rules + +The `authorized-peers` member is an array of credential rule-set objects. For a peer to be considered authorized its certificate MUST match at least one rule set completely. + +Each rule set must contain a `required-credentials` array of credential matchers. For a certificate to match a rule set it MUST match all its credential matchers. + +A credential is matched by checking a pattern given in `must-match` against a specified certificate `field`. The following fields are currently supported: +* *CN* - the Common Name part of the certificate's Distinguished Name information. If multiple CN entries are present, the last one will be considered. +* *SAN_DNS* - a Subject Alternate Name with type DNS. A certificate may contain many SAN entries. If so, all entries are checked and the credential is considered a match if at least one entry matches. +* *SAN_URI* - a Subject Alternate Name with type URI. It is similar to *SAN_DNS* but with slightly different pattern matching semantics. + +For *CN* and *SAN_DNS* fields, the `must-match` pattern is a "glob"-style pattern with the following semantics: +* `*` matches 0-n non-dot characters within a single dot-separated hostname part. This is similar to the wildcards used by certificates for HTTPS hostname validation. Examples + * `*.baz` matches `bar.baz` but not `foo.bar.baz` or `foo.baz.bar`. + * `*.*.baz` matches `foo.bar.baz` but not `bar.baz`. + * `*-myservice` matches `foo-myservice` but not `bar.foo-myservice`. +* `?` matches exactly 1 non-dot character within a single dot-separated hostname part. Examples: + * `?.bar` matches `x.bar` but not `bar`, `.bar` or `yx.bar`. + * `?.?.baz` matches `x.y.baz` but not `x.baz` or `xx.yy.baz`. + +For *SAN_URI* fields, `must-match` is also a "glob"-style pattern, with some deviation compared to *CN*/*SAN_DNS*: +* The `*` wildcard matches 0-n non-slash characters. A `/` is used as separator between the host and path components, as well as separator between path segments. Examples: + * `vespa://myapp/content/*` matches `vespa://myapp/content/node1` but not `vespa://myapp/container/node1` or `vespa://myapp/content/node1/myservice`. + * `vespa://*/*/*` matches `vespa://myapp/content/node1` but not `vespa://myapp/content` or `vespa://myapp/content/node1/myservice`. +* `?` in a pattern has no special behaviour - it only matches the `?` literal. URIs use `?` as separator between the path and query components. + +The description field is optional and is useful for e.g. documenting why a particular ruleset is present. It has no semantic meaning to the authorization engine. + +Not providing the `authorized-peers` field means only certificate validity is used for authorization. If the `authorized-peers` field is provided, it must contain at least one entry. + +#### Example + +```json expandable +{ + "files": { + "ca-certificates": "/absolute/path/to/ca-certs.pem", + "certificates": "/absolute/path/to/host-certs.pem", + "private-key": "/absolute/path/to/private-key.pem", + "disable-hostname-validation": false + }, + "authorized-peers": [ + { + "required-credentials": [ + { "field": "CN", "must-match": "vespa-monitoring.example.com" }, + { "field": "SAN_DNS", "must-match": "*.us-east-*.monitor.example.com" } + ], + "description": "Backend monitoring service access", + "name": "monitoring" + }, + { + "required-credentials": [ + { "field": "SAN_DNS", "must-match": "*.mycluster.vespa.example.com" } + ], + "description": "Cluster-internal node P2P access", + "name": "cluster" + } + ] +} +``` +## TLS features supported by Vespa + +Vespa is built with modern, high-performance cryptography libraries. For security reasons, the Vespa TLS stack has some additional constraints that are always present: +* `TLSv1.2` is the oldest TLS version that can be negotiated. +* Only cipher suites supporting [forward secrecy](https://en.wikipedia.org/wiki/Forward_secrecy) can be negotiated (i.e. cipher suites using ECDHE as part of their key exchange). +* Only modern, symmetric ciphers with [AEAD](https://en.wikipedia.org/wiki/Authenticated_encryption#Authenticated_encryption_with_associated_data) properties are supported. In practice this means [AES-GCM](https://en.wikipedia.org/wiki/AES-GCM) or [ChaCha20-Poly1305](https://en.wikipedia.org/wiki/ChaCha20-Poly1305). Supported cipher suites are listed [here](#cipher-suites). +* TLS compression is explicitly disabled to mitigate [CRIME](https://en.wikipedia.org/wiki/CRIME)/BREACH-style compression oracle attacks. +* TLS renegotiation is explicitly disabled. +* TLS session resumption is explicitly disabled, as this opens up some potential vulnerabilities related to replay attacks. Note that the Vespa application container edge does support session resumption, due to needing to support many frequent, short-lived connections from proxies and clients. + + +<Note> +**Note:** + +The above assumes you are using a Vespa version built with our `vespa_openssl` package (which is the case for all Open-Source RPMs and Docker images). If you are doing a custom build, it is highly recommended building against OpenSSL 1.1.1 or newer. Older versions may have performance regressions or reduced crypto functionality. OpenSSL versions prior to 1.0.1 are not supported. +</Note> + + +### Default TLS protocol settings + +Vespa will by default use the following TLS configuration (unless overridden by `accepted-ciphers` / `accepted-protocols`). + +#### Protocol versions + +* `TLSv1.3` - _note:_ due to certain limitations in the current Java runtime, TLSv1.3 is only supported by the C++ backends for now. We will revisit this in the near future to ensure Java defaults to TLSv1.3 as well. +* `TLSv1.2` + +#### Cipher suites + +* `TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384` +* `TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384` +* `TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256` (JDK 12+) +* `TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256` +* `TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256` +* `TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256` +* `TLS_AES_128_GCM_SHA256` (TLSv1.3) +* `TLS_AES_256_GCM_SHA384` (TLSv1.3) +* `TLS_CHACHA20_POLY1305_SHA256` (TLSv1.3, JDK 12+) diff --git a/en/reference/sentinel-metrics-reference.html b/en/reference/sentinel-metrics-reference.html deleted file mode 100644 index 0f2acd1aad..0000000000 --- a/en/reference/sentinel-metrics-reference.html +++ /dev/null @@ -1,32 +0,0 @@ ---- -# Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -title: "Sentinel Metrics" ---- - -<table class="table"> - <thead> - <tr><th>Name</th><th>Unit</th><th>Description</th></tr> - </thead> - <tbody> - <tr> - <td><p id="sentinel_restarts">sentinel.restarts</p></td> - <td>restart</td> - <td>Number of service restarts done by the sentinel</td> - </tr> - <tr> - <td><p id="sentinel_totalRestarts">sentinel.totalRestarts</p></td> - <td>restart</td> - <td>Total number of service restarts done by the sentinel since the sentinel was started</td> - </tr> - <tr> - <td><p id="sentinel_uptime">sentinel.uptime</p></td> - <td>second</td> - <td>Time the sentinel has been running</td> - </tr> - <tr> - <td><p id="sentinel_running">sentinel.running</p></td> - <td>instance</td> - <td>Number of services the sentinel has running currently</td> - </tr> - </tbody> -</table> diff --git a/en/reference/slobrok-metrics-reference.html b/en/reference/slobrok-metrics-reference.html deleted file mode 100644 index 832d97f0c3..0000000000 --- a/en/reference/slobrok-metrics-reference.html +++ /dev/null @@ -1,37 +0,0 @@ ---- -# Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -title: "Slobrok Metrics" ---- - -<table class="table"> - <thead> - <tr><th>Name</th><th>Unit</th><th>Description</th></tr> - </thead> - <tbody> - <tr> - <td><p id="slobrok_heartbeats_failed">slobrok.heartbeats.failed</p></td> - <td>request</td> - <td>Number of heartbeat requests failed</td> - </tr> - <tr> - <td><p id="slobrok_requests_register">slobrok.requests.register</p></td> - <td>request</td> - <td>Number of register requests received</td> - </tr> - <tr> - <td><p id="slobrok_requests_mirror">slobrok.requests.mirror</p></td> - <td>request</td> - <td>Number of mirroring requests received</td> - </tr> - <tr> - <td><p id="slobrok_requests_admin">slobrok.requests.admin</p></td> - <td>request</td> - <td>Number of administrative requests received</td> - </tr> - <tr> - <td><p id="slobrok_missing_consensus">slobrok.missing.consensus</p></td> - <td>second</td> - <td>Number of seconds without full consensus with all other brokers</td> - </tr> - </tbody> -</table> diff --git a/en/reference/storage-metrics-reference.html b/en/reference/storage-metrics-reference.html deleted file mode 100644 index 01c4bdac63..0000000000 --- a/en/reference/storage-metrics-reference.html +++ /dev/null @@ -1,1052 +0,0 @@ ---- -# Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -title: "Storage Metrics" ---- - -<table class="table"> - <thead> - <tr><th>Name</th><th>Unit</th><th>Description</th></tr> - </thead> - <tbody> - <tr> - <td><p id="vds_datastored_alldisks_buckets">vds.datastored.alldisks.buckets</p></td> - <td>bucket</td> - <td>Number of buckets managed</td> - </tr> - <tr> - <td><p id="vds_datastored_alldisks_docs">vds.datastored.alldisks.docs</p></td> - <td>document</td> - <td>Number of documents stored</td> - </tr> - <tr> - <td><p id="vds_datastored_alldisks_bytes">vds.datastored.alldisks.bytes</p></td> - <td>byte</td> - <td>Number of bytes stored</td> - </tr> - <tr> - <td><p id="vds_datastored_alldisks_activebuckets">vds.datastored.alldisks.activebuckets</p></td> - <td>bucket</td> - <td>Number of active buckets on the node</td> - </tr> - <tr> - <td><p id="vds_datastored_alldisks_readybuckets">vds.datastored.alldisks.readybuckets</p></td> - <td>bucket</td> - <td>Number of ready buckets on the node</td> - </tr> - <tr> - <td><p id="vds_visitor_allthreads_averagevisitorlifetime">vds.visitor.allthreads.averagevisitorlifetime</p></td> - <td>millisecond</td> - <td>Average lifetime of a visitor</td> - </tr> - <tr> - <td><p id="vds_visitor_allthreads_averagequeuewait">vds.visitor.allthreads.averagequeuewait</p></td> - <td>millisecond</td> - <td>Average time an operation spends in input queue.</td> - </tr> - <tr> - <td><p id="vds_visitor_allthreads_queuesize">vds.visitor.allthreads.queuesize</p></td> - <td>operation</td> - <td>Size of input message queue.</td> - </tr> - <tr> - <td><p id="vds_visitor_allthreads_completed">vds.visitor.allthreads.completed</p></td> - <td>operation</td> - <td>Number of visitors completed</td> - </tr> - <tr> - <td><p id="vds_visitor_allthreads_created">vds.visitor.allthreads.created</p></td> - <td>operation</td> - <td>Number of visitors created.</td> - </tr> - <tr> - <td><p id="vds_visitor_allthreads_failed">vds.visitor.allthreads.failed</p></td> - <td>operation</td> - <td>Number of visitors failed</td> - </tr> - <tr> - <td><p id="vds_visitor_allthreads_averagemessagesendtime">vds.visitor.allthreads.averagemessagesendtime</p></td> - <td>millisecond</td> - <td>Average time it takes for messages to be sent to their target (and be replied to)</td> - </tr> - <tr> - <td><p id="vds_visitor_allthreads_averageprocessingtime">vds.visitor.allthreads.averageprocessingtime</p></td> - <td>millisecond</td> - <td>Average time used to process visitor requests</td> - </tr> - <tr> - <td><p id="vds_visitor_allthreads_aborted">vds.visitor.allthreads.aborted</p></td> - <td>instance</td> - <td>Number of visitors aborted.</td> - </tr> - <tr> - <td><p id="vds_visitor_allthreads_averagevisitorcreationtime">vds.visitor.allthreads.averagevisitorcreationtime</p></td> - <td>millisecond</td> - <td>Average time spent creating a visitor instance</td> - </tr> - <tr> - <td><p id="vds_visitor_allthreads_destination_failure_replies">vds.visitor.allthreads.destination_failure_replies</p></td> - <td>instance</td> - <td>Number of failure replies received from the visitor destination</td> - </tr> - <tr> - <td><p id="vds_filestor_queuesize">vds.filestor.queuesize</p></td> - <td>operation</td> - <td>Size of input message queue.</td> - </tr> - <tr> - <td><p id="vds_filestor_averagequeuewait">vds.filestor.averagequeuewait</p></td> - <td>millisecond</td> - <td>Average time an operation spends in input queue.</td> - </tr> - <tr> - <td><p id="vds_filestor_active_operations_size">vds.filestor.active_operations.size</p></td> - <td>operation</td> - <td>Number of concurrent active operations</td> - </tr> - <tr> - <td><p id="vds_filestor_active_operations_latency">vds.filestor.active_operations.latency</p></td> - <td>millisecond</td> - <td>Latency (in ms) for completed operations</td> - </tr> - <tr> - <td><p id="vds_filestor_throttle_window_size">vds.filestor.throttle_window_size</p></td> - <td>operation</td> - <td>Current size of async operation throttler window size</td> - </tr> - <tr> - <td><p id="vds_filestor_throttle_waiting_threads">vds.filestor.throttle_waiting_threads</p></td> - <td>thread</td> - <td>Number of threads waiting to acquire a throttle token</td> - </tr> - <tr> - <td><p id="vds_filestor_throttle_active_tokens">vds.filestor.throttle_active_tokens</p></td> - <td>instance</td> - <td>Current number of active throttle tokens</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_mergemetadatareadlatency">vds.filestor.allthreads.mergemetadatareadlatency</p></td> - <td>millisecond</td> - <td>Time spent in a merge step to check metadata of current node to see what data it has.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_mergedatareadlatency">vds.filestor.allthreads.mergedatareadlatency</p></td> - <td>millisecond</td> - <td>Time spent in a merge step to read data other nodes need.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_mergedatawritelatency">vds.filestor.allthreads.mergedatawritelatency</p></td> - <td>millisecond</td> - <td>Time spent in a merge step to write data needed to current node.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_mergeavgdatareceivedneeded">vds.filestor.allthreads.mergeavgdatareceivedneeded</p></td> - <td>byte</td> - <td>Amount of data transferred from previous node in chain that we needed to apply locally.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_mergebuckets_count">vds.filestor.allthreads.mergebuckets.count</p></td> - <td>request</td> - <td>Number of requests processed.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_mergebuckets_failed">vds.filestor.allthreads.mergebuckets.failed</p></td> - <td>request</td> - <td>Number of failed requests.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_mergebuckets_latency">vds.filestor.allthreads.mergebuckets.latency</p></td> - <td>millisecond</td> - <td>Latency of successful requests.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_mergelatencytotal">vds.filestor.allthreads.mergelatencytotal</p></td> - <td>millisecond</td> - <td>Latency of total merge operation, from master node receives it, until merge is complete and master node replies.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_merge_put_latency">vds.filestor.allthreads.merge_put_latency</p></td> - <td>millisecond</td> - <td>Latency of individual puts that are part of merge operations</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_merge_remove_latency">vds.filestor.allthreads.merge_remove_latency</p></td> - <td>millisecond</td> - <td>Latency of individual removes that are part of merge operations</td> - </tr> - <tr> - <td><p id="vds_filestor_allstripes_throttled_rpc_direct_dispatches">vds.filestor.allstripes.throttled_rpc_direct_dispatches</p></td> - <td>instance</td> - <td>Number of times an RPC thread could not directly dispatch an async operation directly to Proton because it was disallowed by the throttle policy</td> - </tr> - <tr> - <td><p id="vds_filestor_allstripes_throttled_persistence_thread_polls">vds.filestor.allstripes.throttled_persistence_thread_polls</p></td> - <td>instance</td> - <td>Number of times a persistence thread could not immediately dispatch a queued async operation because it was disallowed by the throttle policy</td> - </tr> - <tr> - <td><p id="vds_filestor_allstripes_timeouts_waiting_for_throttle_token">vds.filestor.allstripes.timeouts_waiting_for_throttle_token</p></td> - <td>instance</td> - <td>Number of times a persistence thread timed out waiting for an available throttle policy token</td> - </tr> - <tr> - <td><p id="vds_filestor_allstripes_averagequeuewait">vds.filestor.allstripes.averagequeuewait</p></td> - <td>millisecond</td> - <td>Average time an operation spends in input queue.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_put_count">vds.filestor.allthreads.put.count</p></td> - <td>operation</td> - <td>Number of requests processed.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_put_failed">vds.filestor.allthreads.put.failed</p></td> - <td>operation</td> - <td>Number of failed requests.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_put_test_and_set_failed">vds.filestor.allthreads.put.test_and_set_failed</p></td> - <td>operation</td> - <td>Number of operations that were skipped due to a test-and-set condition not met</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_put_latency">vds.filestor.allthreads.put.latency</p></td> - <td>millisecond</td> - <td>Latency of successful requests.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_put_request_size">vds.filestor.allthreads.put.request_size</p></td> - <td>byte</td> - <td>Size of requests, in bytes</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_remove_count">vds.filestor.allthreads.remove.count</p></td> - <td>operation</td> - <td>Number of requests processed.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_remove_failed">vds.filestor.allthreads.remove.failed</p></td> - <td>operation</td> - <td>Number of failed requests.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_remove_test_and_set_failed">vds.filestor.allthreads.remove.test_and_set_failed</p></td> - <td>operation</td> - <td>Number of operations that were skipped due to a test-and-set condition not met</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_remove_latency">vds.filestor.allthreads.remove.latency</p></td> - <td>millisecond</td> - <td>Latency of successful requests.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_remove_request_size">vds.filestor.allthreads.remove.request_size</p></td> - <td>byte</td> - <td>Size of requests, in bytes</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_remove_not_found">vds.filestor.allthreads.remove.not_found</p></td> - <td>request</td> - <td>Number of requests that could not be completed due to source document not found.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_get_count">vds.filestor.allthreads.get.count</p></td> - <td>operation</td> - <td>Number of requests processed.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_get_failed">vds.filestor.allthreads.get.failed</p></td> - <td>operation</td> - <td>Number of failed requests.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_get_latency">vds.filestor.allthreads.get.latency</p></td> - <td>millisecond</td> - <td>Latency of successful requests.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_get_request_size">vds.filestor.allthreads.get.request_size</p></td> - <td>byte</td> - <td>Size of requests, in bytes</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_get_not_found">vds.filestor.allthreads.get.not_found</p></td> - <td>request</td> - <td>Number of requests that could not be completed due to source document not found.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_update_count">vds.filestor.allthreads.update.count</p></td> - <td>request</td> - <td>Number of requests processed.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_update_failed">vds.filestor.allthreads.update.failed</p></td> - <td>request</td> - <td>Number of failed requests.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_update_test_and_set_failed">vds.filestor.allthreads.update.test_and_set_failed</p></td> - <td>request</td> - <td>Number of requests that were skipped due to a test-and-set condition not met</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_update_latency">vds.filestor.allthreads.update.latency</p></td> - <td>millisecond</td> - <td>Latency of successful requests.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_update_request_size">vds.filestor.allthreads.update.request_size</p></td> - <td>byte</td> - <td>Size of requests, in bytes</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_update_latency_read">vds.filestor.allthreads.update.latency_read</p></td> - <td>millisecond</td> - <td>Latency of the source read in the request.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_update_not_found">vds.filestor.allthreads.update.not_found</p></td> - <td>request</td> - <td>Number of requests that could not be completed due to source document not found.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_createiterator_count">vds.filestor.allthreads.createiterator.count</p></td> - <td>request</td> - <td>Number of requests processed.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_createiterator_latency">vds.filestor.allthreads.createiterator.latency</p></td> - <td>millisecond</td> - <td>Latency of successful requests.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_createiterator_failed">vds.filestor.allthreads.createiterator.failed</p></td> - <td>request</td> - <td>Number of failed requests.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_visit_count">vds.filestor.allthreads.visit.count</p></td> - <td>request</td> - <td>Number of requests processed.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_visit_latency">vds.filestor.allthreads.visit.latency</p></td> - <td>millisecond</td> - <td>Latency of successful requests.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_visit_docs">vds.filestor.allthreads.visit.docs</p></td> - <td>document</td> - <td>Number of entries read per iterate call</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_visit_failed">vds.filestor.allthreads.visit.failed</p></td> - <td>request</td> - <td>Number of failed requests.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_remove_location_count">vds.filestor.allthreads.remove_location.count</p></td> - <td>request</td> - <td>Number of requests processed.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_remove_location_latency">vds.filestor.allthreads.remove_location.latency</p></td> - <td>millisecond</td> - <td>Latency of successful requests.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_remove_location_failed">vds.filestor.allthreads.remove_location.failed</p></td> - <td>request</td> - <td>Number of failed requests.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_splitbuckets_count">vds.filestor.allthreads.splitbuckets.count</p></td> - <td>request</td> - <td>Number of requests processed.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_splitbuckets_failed">vds.filestor.allthreads.splitbuckets.failed</p></td> - <td>request</td> - <td>Number of failed requests.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_splitbuckets_latency">vds.filestor.allthreads.splitbuckets.latency</p></td> - <td>request</td> - <td>Latency of successful requests.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_joinbuckets_count">vds.filestor.allthreads.joinbuckets.count</p></td> - <td>request</td> - <td>Number of requests processed.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_joinbuckets_failed">vds.filestor.allthreads.joinbuckets.failed</p></td> - <td>request</td> - <td>Number of failed requests.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_joinbuckets_latency">vds.filestor.allthreads.joinbuckets.latency</p></td> - <td>millisecond</td> - <td>Latency of successful requests.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_deletebuckets_count">vds.filestor.allthreads.deletebuckets.count</p></td> - <td>request</td> - <td>Number of requests processed.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_deletebuckets_failed">vds.filestor.allthreads.deletebuckets.failed</p></td> - <td>request</td> - <td>Number of failed requests.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_deletebuckets_latency">vds.filestor.allthreads.deletebuckets.latency</p></td> - <td>millisecond</td> - <td>Latency of successful requests.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_remove_by_gid_count">vds.filestor.allthreads.remove_by_gid.count</p></td> - <td>request</td> - <td>Number of requests processed.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_remove_by_gid_failed">vds.filestor.allthreads.remove_by_gid.failed</p></td> - <td>request</td> - <td>Number of failed requests.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_remove_by_gid_latency">vds.filestor.allthreads.remove_by_gid.latency</p></td> - <td>millisecond</td> - <td>Latency of successful requests.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_setbucketstates_count">vds.filestor.allthreads.setbucketstates.count</p></td> - <td>request</td> - <td>Number of requests processed.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_setbucketstates_failed">vds.filestor.allthreads.setbucketstates.failed</p></td> - <td>request</td> - <td>Number of failed requests.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_setbucketstates_latency">vds.filestor.allthreads.setbucketstates.latency</p></td> - <td>millisecond</td> - <td>Latency of successful requests.</td> - </tr> - <tr> - <td><p id="vds_mergethrottler_averagequeuewaitingtime">vds.mergethrottler.averagequeuewaitingtime</p></td> - <td>millisecond</td> - <td>Time merges spent in the throttler queue</td> - </tr> - <tr> - <td><p id="vds_mergethrottler_queuesize">vds.mergethrottler.queuesize</p></td> - <td>instance</td> - <td>Length of merge queue</td> - </tr> - <tr> - <td><p id="vds_mergethrottler_active_window_size">vds.mergethrottler.active_window_size</p></td> - <td>instance</td> - <td>Number of merges active within the pending window size</td> - </tr> - <tr> - <td><p id="vds_mergethrottler_estimated_merge_memory_usage">vds.mergethrottler.estimated_merge_memory_usage</p></td> - <td>byte</td> - <td>An estimated upper bound of the memory usage (in bytes) of the merges currently in the active window</td> - </tr> - <tr> - <td><p id="vds_mergethrottler_bounced_due_to_back_pressure">vds.mergethrottler.bounced_due_to_back_pressure</p></td> - <td>instance</td> - <td>Number of merges bounced due to resource exhaustion back-pressure</td> - </tr> - <tr> - <td><p id="vds_mergethrottler_locallyexecutedmerges_ok">vds.mergethrottler.locallyexecutedmerges.ok</p></td> - <td>instance</td> - <td>The number of successful merges for 'locallyexecutedmerges'</td> - </tr> - <tr> - <td><p id="vds_mergethrottler_locallyexecutedmerges_failures_aborted">vds.mergethrottler.locallyexecutedmerges.failures.aborted</p></td> - <td>operation</td> - <td>The number of merges that failed because the storage node was (most likely) shutting down</td> - </tr> - <tr> - <td><p id="vds_mergethrottler_locallyexecutedmerges_failures_bucketnotfound">vds.mergethrottler.locallyexecutedmerges.failures.bucketnotfound</p></td> - <td>operation</td> - <td>The number of operations that failed because the bucket did not exist</td> - </tr> - <tr> - <td><p id="vds_mergethrottler_locallyexecutedmerges_failures_busy">vds.mergethrottler.locallyexecutedmerges.failures.busy</p></td> - <td>operation</td> - <td>The number of merges that failed because the storage node was busy</td> - </tr> - <tr> - <td><p id="vds_mergethrottler_locallyexecutedmerges_failures_exists">vds.mergethrottler.locallyexecutedmerges.failures.exists</p></td> - <td>operation</td> - <td>The number of merges that were rejected due to a merge operation for their bucket already being processed</td> - </tr> - <tr> - <td><p id="vds_mergethrottler_locallyexecutedmerges_failures_notready">vds.mergethrottler.locallyexecutedmerges.failures.notready</p></td> - <td>operation</td> - <td>The number of merges discarded because distributor was not ready</td> - </tr> - <tr> - <td><p id="vds_mergethrottler_locallyexecutedmerges_failures_other">vds.mergethrottler.locallyexecutedmerges.failures.other</p></td> - <td>operation</td> - <td>The number of other failures</td> - </tr> - <tr> - <td><p id="vds_mergethrottler_locallyexecutedmerges_failures_rejected">vds.mergethrottler.locallyexecutedmerges.failures.rejected</p></td> - <td>operation</td> - <td>The number of merges that were rejected</td> - </tr> - <tr> - <td><p id="vds_mergethrottler_locallyexecutedmerges_failures_timeout">vds.mergethrottler.locallyexecutedmerges.failures.timeout</p></td> - <td>operation</td> - <td>The number of merges that failed because they timed out towards storage</td> - </tr> - <tr> - <td><p id="vds_mergethrottler_locallyexecutedmerges_failures_total">vds.mergethrottler.locallyexecutedmerges.failures.total</p></td> - <td>operation</td> - <td>Sum of all failures</td> - </tr> - <tr> - <td><p id="vds_mergethrottler_locallyexecutedmerges_failures_wrongdistribution">vds.mergethrottler.locallyexecutedmerges.failures.wrongdistribution</p></td> - <td>operation</td> - <td>The number of merges that were discarded (flushed) because they were initiated at an older cluster state than the current</td> - </tr> - <tr> - <td><p id="vds_mergethrottler_mergechains_ok">vds.mergethrottler.mergechains.ok</p></td> - <td>operation</td> - <td>The number of successful merges for 'mergechains'</td> - </tr> - <tr> - <td><p id="vds_mergethrottler_mergechains_failures_busy">vds.mergethrottler.mergechains.failures.busy</p></td> - <td>operation</td> - <td>The number of merges that failed because the storage node was busy</td> - </tr> - <tr> - <td><p id="vds_mergethrottler_mergechains_failures_total">vds.mergethrottler.mergechains.failures.total</p></td> - <td>operation</td> - <td>Sum of all failures</td> - </tr> - <tr> - <td><p id="vds_mergethrottler_mergechains_failures_exists">vds.mergethrottler.mergechains.failures.exists</p></td> - <td>operation</td> - <td>The number of merges that were rejected due to a merge operation for their bucket already being processed</td> - </tr> - <tr> - <td><p id="vds_mergethrottler_mergechains_failures_notready">vds.mergethrottler.mergechains.failures.notready</p></td> - <td>operation</td> - <td>The number of merges discarded because distributor was not ready</td> - </tr> - <tr> - <td><p id="vds_mergethrottler_mergechains_failures_other">vds.mergethrottler.mergechains.failures.other</p></td> - <td>operation</td> - <td>The number of other failures</td> - </tr> - <tr> - <td><p id="vds_mergethrottler_mergechains_failures_rejected">vds.mergethrottler.mergechains.failures.rejected</p></td> - <td>operation</td> - <td>The number of merges that were rejected</td> - </tr> - <tr> - <td><p id="vds_mergethrottler_mergechains_failures_timeout">vds.mergethrottler.mergechains.failures.timeout</p></td> - <td>operation</td> - <td>The number of merges that failed because they timed out towards storage</td> - </tr> - <tr> - <td><p id="vds_mergethrottler_mergechains_failures_wrongdistribution">vds.mergethrottler.mergechains.failures.wrongdistribution</p></td> - <td>operation</td> - <td>The number of merges that were discarded (flushed) because they were initiated at an older cluster state than the current</td> - </tr> - <tr> - <td><p id="vds_server_network_tls-handshakes-failed">vds.server.network.tls-handshakes-failed</p></td> - <td>operation</td> - <td>Number of client or server connection attempts that failed during TLS handshaking</td> - </tr> - <tr> - <td><p id="vds_server_network_peer-authorization-failures">vds.server.network.peer-authorization-failures</p></td> - <td>failure</td> - <td>Number of TLS connection attempts failed due to bad or missing peer certificate credentials</td> - </tr> - <tr> - <td><p id="vds_server_network_client_tls-connections-established">vds.server.network.client.tls-connections-established</p></td> - <td>connection</td> - <td>Number of secure mTLS connections established</td> - </tr> - <tr> - <td><p id="vds_server_network_server_tls-connections-established">vds.server.network.server.tls-connections-established</p></td> - <td>connection</td> - <td>Number of secure mTLS connections established</td> - </tr> - <tr> - <td><p id="vds_server_network_client_insecure-connections-established">vds.server.network.client.insecure-connections-established</p></td> - <td>connection</td> - <td>Number of insecure (plaintext) connections established</td> - </tr> - <tr> - <td><p id="vds_server_network_server_insecure-connections-established">vds.server.network.server.insecure-connections-established</p></td> - <td>connection</td> - <td>Number of insecure (plaintext) connections established</td> - </tr> - <tr> - <td><p id="vds_server_network_tls-connections-broken">vds.server.network.tls-connections-broken</p></td> - <td>connection</td> - <td>Number of TLS connections broken due to failures during frame encoding or decoding</td> - </tr> - <tr> - <td><p id="vds_server_network_failed-tls-config-reloads">vds.server.network.failed-tls-config-reloads</p></td> - <td>failure</td> - <td>Number of times background reloading of TLS config has failed</td> - </tr> - <tr> - <td><p id="vds_bouncer_unavailable_node_aborts">vds.bouncer.unavailable_node_aborts</p></td> - <td>operation</td> - <td>Number of operations that were aborted due to the node (or target bucket space) being unavailable</td> - </tr> - <tr> - <td><p id="vds_changedbucketownershiphandler_avg_abort_processing_time">vds.changedbucketownershiphandler.avg_abort_processing_time</p></td> - <td>millisecond</td> - <td>Average time spent aborting operations for changed buckets</td> - </tr> - <tr> - <td><p id="vds_changedbucketownershiphandler_external_load_ops_aborted">vds.changedbucketownershiphandler.external_load_ops_aborted</p></td> - <td>operation</td> - <td>Number of outdated external load operations aborted</td> - </tr> - <tr> - <td><p id="vds_changedbucketownershiphandler_ideal_state_ops_aborted">vds.changedbucketownershiphandler.ideal_state_ops_aborted</p></td> - <td>operation</td> - <td>Number of outdated ideal state operations aborted</td> - </tr> - <tr> - <td><p id="vds_communication_bucket_space_mapping_failures">vds.communication.bucket_space_mapping_failures</p></td> - <td>operation</td> - <td>Number of messages that could not be resolved to a known bucket space</td> - </tr> - <tr> - <td><p id="vds_communication_convertfailures">vds.communication.convertfailures</p></td> - <td>operation</td> - <td>Number of messages that failed to get converted to storage API messages</td> - </tr> - <tr> - <td><p id="vds_communication_exceptionmessageprocesstime">vds.communication.exceptionmessageprocesstime</p></td> - <td>millisecond</td> - <td>Time transport thread uses to process a single message that fails with an exception thrown into communication manager</td> - </tr> - <tr> - <td><p id="vds_communication_messageprocesstime">vds.communication.messageprocesstime</p></td> - <td>millisecond</td> - <td>Time transport thread uses to process a single message</td> - </tr> - <tr> - <td><p id="vds_communication_messagequeue">vds.communication.messagequeue</p></td> - <td>item</td> - <td>Size of input message queue.</td> - </tr> - <tr> - <td><p id="vds_communication_sendcommandlatency">vds.communication.sendcommandlatency</p></td> - <td>millisecond</td> - <td>Average ms used to send commands to MBUS</td> - </tr> - <tr> - <td><p id="vds_communication_sendreplylatency">vds.communication.sendreplylatency</p></td> - <td>millisecond</td> - <td>Average ms used to send replies to MBUS</td> - </tr> - <tr> - <td><p id="vds_communication_toolittlememory">vds.communication.toolittlememory</p></td> - <td>operation</td> - <td>Number of messages failed due to too little memory available</td> - </tr> - <tr> - <td><p id="vds_datastored_bucket_space_active_buckets">vds.datastored.bucket_space.active_buckets</p></td> - <td>bucket</td> - <td>Number of active buckets in the bucket space</td> - </tr> - <tr> - <td><p id="vds_datastored_bucket_space_bucket_db_memory_usage_allocated_bytes">vds.datastored.bucket_space.bucket_db.memory_usage.allocated_bytes</p></td> - <td>byte</td> - <td>The number of allocated bytes</td> - </tr> - <tr> - <td><p id="vds_datastored_bucket_space_bucket_db_memory_usage_dead_bytes">vds.datastored.bucket_space.bucket_db.memory_usage.dead_bytes</p></td> - <td>byte</td> - <td>The number of dead bytes (<= used_bytes)</td> - </tr> - <tr> - <td><p id="vds_datastored_bucket_space_bucket_db_memory_usage_onhold_bytes">vds.datastored.bucket_space.bucket_db.memory_usage.onhold_bytes</p></td> - <td>byte</td> - <td>The number of bytes on hold</td> - </tr> - <tr> - <td><p id="vds_datastored_bucket_space_bucket_db_memory_usage_used_bytes">vds.datastored.bucket_space.bucket_db.memory_usage.used_bytes</p></td> - <td>byte</td> - <td>The number of used bytes (<= allocated_bytes)</td> - </tr> - <tr> - <td><p id="vds_datastored_bucket_space_buckets_total">vds.datastored.bucket_space.buckets_total</p></td> - <td>bucket</td> - <td>Total number buckets present in the bucket space (ready + not ready)</td> - </tr> - <tr> - <td><p id="vds_datastored_bucket_space_entries">vds.datastored.bucket_space.entries</p></td> - <td>document</td> - <td>Number of entries (documents + tombstones) stored in the bucket space</td> - </tr> - <tr> - <td><p id="vds_datastored_bucket_space_bytes">vds.datastored.bucket_space.bytes</p></td> - <td>byte</td> - <td>Bytes stored across all documents in the bucket space</td> - </tr> - <tr> - <td><p id="vds_datastored_bucket_space_docs">vds.datastored.bucket_space.docs</p></td> - <td>document</td> - <td>Documents stored in the bucket space</td> - </tr> - <tr> - <td><p id="vds_datastored_bucket_space_ready_buckets">vds.datastored.bucket_space.ready_buckets</p></td> - <td>bucket</td> - <td>Number of ready buckets in the bucket space</td> - </tr> - <tr> - <td><p id="vds_datastored_fullbucketinfolatency">vds.datastored.fullbucketinfolatency</p></td> - <td>millisecond</td> - <td>Amount of time spent to process a full bucket info request</td> - </tr> - <tr> - <td><p id="vds_datastored_fullbucketinforeqsize">vds.datastored.fullbucketinforeqsize</p></td> - <td>node</td> - <td>Amount of distributors answered at once in full bucket info requests.</td> - </tr> - <tr> - <td><p id="vds_datastored_simplebucketinforeqsize">vds.datastored.simplebucketinforeqsize</p></td> - <td>bucket</td> - <td>Amount of buckets returned in simple bucket info requests</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_applybucketdiff_count">vds.filestor.allthreads.applybucketdiff.count</p></td> - <td>request</td> - <td>Number of requests processed.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_applybucketdiff_failed">vds.filestor.allthreads.applybucketdiff.failed</p></td> - <td>request</td> - <td>Number of failed requests.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_applybucketdiff_latency">vds.filestor.allthreads.applybucketdiff.latency</p></td> - <td>millisecond</td> - <td>Latency of successful requests.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_applybucketdiffreply">vds.filestor.allthreads.applybucketdiffreply</p></td> - <td>request</td> - <td>Number of applybucketdiff replies that have been processed.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_bucketfixed">vds.filestor.allthreads.bucketfixed</p></td> - <td>bucket</td> - <td>Number of times bucket has been fixed because of corruption</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_bucketverified_count">vds.filestor.allthreads.bucketverified.count</p></td> - <td>request</td> - <td>Number of requests processed.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_bucketverified_failed">vds.filestor.allthreads.bucketverified.failed</p></td> - <td>request</td> - <td>Number of failed requests.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_bucketverified_latency">vds.filestor.allthreads.bucketverified.latency</p></td> - <td>request</td> - <td>Latency of successful requests.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_bytesmerged">vds.filestor.allthreads.bytesmerged</p></td> - <td>byte</td> - <td>Total number of bytes merged into this node.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_createbuckets_count">vds.filestor.allthreads.createbuckets.count</p></td> - <td>request</td> - <td>Number of requests processed.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_createbuckets_failed">vds.filestor.allthreads.createbuckets.failed</p></td> - <td>request</td> - <td>Number of failed requests.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_createbuckets_latency">vds.filestor.allthreads.createbuckets.latency</p></td> - <td>request</td> - <td>Latency of successful requests.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_failedoperations">vds.filestor.allthreads.failedoperations</p></td> - <td>operation</td> - <td>Number of operations throwing exceptions.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_getbucketdiff_count">vds.filestor.allthreads.getbucketdiff.count</p></td> - <td>request</td> - <td>Number of requests processed.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_getbucketdiff_failed">vds.filestor.allthreads.getbucketdiff.failed</p></td> - <td>request</td> - <td>Number of failed requests.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_getbucketdiff_latency">vds.filestor.allthreads.getbucketdiff.latency</p></td> - <td>request</td> - <td>Latency of successful requests.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_getbucketdiffreply">vds.filestor.allthreads.getbucketdiffreply</p></td> - <td>request</td> - <td>Number of getbucketdiff replies that have been processed.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_internaljoin_count">vds.filestor.allthreads.internaljoin.count</p></td> - <td>request</td> - <td>Number of requests processed.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_internaljoin_failed">vds.filestor.allthreads.internaljoin.failed</p></td> - <td>request</td> - <td>Number of failed requests.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_internaljoin_latency">vds.filestor.allthreads.internaljoin.latency</p></td> - <td>millisecond</td> - <td>Latency of successful requests.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_movedbuckets_count">vds.filestor.allthreads.movedbuckets.count</p></td> - <td>request</td> - <td>Number of requests processed.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_movedbuckets_failed">vds.filestor.allthreads.movedbuckets.failed</p></td> - <td>request</td> - <td>Number of failed requests.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_movedbuckets_latency">vds.filestor.allthreads.movedbuckets.latency</p></td> - <td>millisecond</td> - <td>Latency of successful requests.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_operations">vds.filestor.allthreads.operations</p></td> - <td>operation</td> - <td>Number of operations processed.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_readbucketinfo_count">vds.filestor.allthreads.readbucketinfo.count</p></td> - <td>request</td> - <td>Number of requests processed.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_readbucketinfo_failed">vds.filestor.allthreads.readbucketinfo.failed</p></td> - <td>request</td> - <td>Number of failed requests.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_readbucketinfo_latency">vds.filestor.allthreads.readbucketinfo.latency</p></td> - <td>request</td> - <td>Latency of successful requests.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_readbucketlist_count">vds.filestor.allthreads.readbucketlist.count</p></td> - <td>request</td> - <td>Number of requests processed.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_readbucketlist_failed">vds.filestor.allthreads.readbucketlist.failed</p></td> - <td>request</td> - <td>Number of failed requests.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_readbucketlist_latency">vds.filestor.allthreads.readbucketlist.latency</p></td> - <td>millisecond</td> - <td>Latency of successful requests.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_recheckbucketinfo_count">vds.filestor.allthreads.recheckbucketinfo.count</p></td> - <td>request</td> - <td>Number of requests processed.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_recheckbucketinfo_failed">vds.filestor.allthreads.recheckbucketinfo.failed</p></td> - <td>request</td> - <td>Number of failed requests.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_recheckbucketinfo_latency">vds.filestor.allthreads.recheckbucketinfo.latency</p></td> - <td>millisecond</td> - <td>Latency of successful requests.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_revert_count">vds.filestor.allthreads.revert.count</p></td> - <td>request</td> - <td>Number of requests processed.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_revert_failed">vds.filestor.allthreads.revert.failed</p></td> - <td>request</td> - <td>Number of failed requests.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_revert_latency">vds.filestor.allthreads.revert.latency</p></td> - <td>millisecond</td> - <td>Latency of successful requests.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_revert_not_found">vds.filestor.allthreads.revert.not_found</p></td> - <td>request</td> - <td>Number of requests that could not be completed due to source document not found.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_stat_bucket_count">vds.filestor.allthreads.stat_bucket.count</p></td> - <td>request</td> - <td>Number of requests processed.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_stat_bucket_failed">vds.filestor.allthreads.stat_bucket.failed</p></td> - <td>request</td> - <td>Number of failed requests.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_stat_bucket_latency">vds.filestor.allthreads.stat_bucket.latency</p></td> - <td>request</td> - <td>Latency of successful requests.</td> - </tr> - <tr> - <td><p id="vds_filestor_bucket_db_init_latency">vds.filestor.bucket_db_init_latency</p></td> - <td>millisecond</td> - <td>Time taken (in ms) to initialize bucket databases with information from the persistence provider</td> - </tr> - <tr> - <td><p id="vds_filestor_directoryevents">vds.filestor.directoryevents</p></td> - <td>operation</td> - <td>Number of directory events received.</td> - </tr> - <tr> - <td><p id="vds_filestor_diskevents">vds.filestor.diskevents</p></td> - <td>operation</td> - <td>Number of disk events received.</td> - </tr> - <tr> - <td><p id="vds_filestor_partitionevents">vds.filestor.partitionevents</p></td> - <td>operation</td> - <td>Number of partition events received.</td> - </tr> - <tr> - <td><p id="vds_filestor_pendingmerge">vds.filestor.pendingmerge</p></td> - <td>bucket</td> - <td>Number of buckets currently being merged.</td> - </tr> - <tr> - <td><p id="vds_filestor_waitingforlockrate">vds.filestor.waitingforlockrate</p></td> - <td>operation</td> - <td>Amount of times a filestor thread has needed to wait for lock to take next message in queue.</td> - </tr> - <tr> - <td><p id="vds_mergethrottler_mergechains_failures_aborted">vds.mergethrottler.mergechains.failures.aborted</p></td> - <td>operation</td> - <td>The number of merges that failed because the storage node was (most likely) shutting down</td> - </tr> - <tr> - <td><p id="vds_mergethrottler_mergechains_failures_bucketnotfound">vds.mergethrottler.mergechains.failures.bucketnotfound</p></td> - <td>operation</td> - <td>The number of operations that failed because the bucket did not exist</td> - </tr> - <tr> - <td><p id="vds_server_memoryusage">vds.server.memoryusage</p></td> - <td>byte</td> - <td>Amount of memory used by the storage subsystem</td> - </tr> - <tr> - <td><p id="vds_server_memoryusage_visiting">vds.server.memoryusage_visiting</p></td> - <td>byte</td> - <td>Message use from visiting</td> - </tr> - <tr> - <td><p id="vds_server_message_memory_use_highpri">vds.server.message_memory_use.highpri</p></td> - <td>byte</td> - <td>Message use from high priority storage messages</td> - </tr> - <tr> - <td><p id="vds_server_message_memory_use_lowpri">vds.server.message_memory_use.lowpri</p></td> - <td>byte</td> - <td>Message use from low priority storage messages</td> - </tr> - <tr> - <td><p id="vds_server_message_memory_use_normalpri">vds.server.message_memory_use.normalpri</p></td> - <td>byte</td> - <td>Message use from normal priority storage messages</td> - </tr> - <tr> - <td><p id="vds_server_message_memory_use_total">vds.server.message_memory_use.total</p></td> - <td>byte</td> - <td>Message use from storage messages</td> - </tr> - <tr> - <td><p id="vds_server_message_memory_use_veryhighpri">vds.server.message_memory_use.veryhighpri</p></td> - <td>byte</td> - <td>Message use from very high priority storage messages</td> - </tr> - <tr> - <td><p id="vds_state_manager_invoke_state_listeners_latency">vds.state_manager.invoke_state_listeners_latency</p></td> - <td>millisecond</td> - <td>Time spent (in ms) propagating state changes to internal state listeners</td> - </tr> - <tr> - <td><p id="vds_visitor_cv_queueevictedwaittime">vds.visitor.cv_queueevictedwaittime</p></td> - <td>millisecond</td> - <td>Milliseconds waiting in create visitor queue, for visitors that was evicted from queue due to higher priority visitors coming</td> - </tr> - <tr> - <td><p id="vds_visitor_cv_queuefull">vds.visitor.cv_queuefull</p></td> - <td>operation</td> - <td>Number of create visitor messages failed as queue is full</td> - </tr> - <tr> - <td><p id="vds_visitor_cv_queuesize">vds.visitor.cv_queuesize</p></td> - <td>item</td> - <td>Size of create visitor queue</td> - </tr> - <tr> - <td><p id="vds_visitor_cv_queuetimeoutwaittime">vds.visitor.cv_queuetimeoutwaittime</p></td> - <td>millisecond</td> - <td>Milliseconds waiting in create visitor queue, for visitors that timed out while in the visitor queue</td> - </tr> - <tr> - <td><p id="vds_visitor_cv_queuewaittime">vds.visitor.cv_queuewaittime</p></td> - <td>millisecond</td> - <td>Milliseconds waiting in create visitor queue, for visitors that was added to visitor queue but scheduled later</td> - </tr> - <tr> - <td><p id="vds_visitor_cv_skipqueue">vds.visitor.cv_skipqueue</p></td> - <td>operation</td> - <td>Number of times we could skip queue as we had free visitor spots</td> - </tr> - <tr> - <td><p id="vds_server_network_rpc-capability-checks-failed">vds.server.network.rpc-capability-checks-failed</p></td> - <td>failure</td> - <td>Number of RPC operations that failed due to one or more missing capabilities</td> - </tr> - <tr> - <td><p id="vds_server_network_status-capability-checks-failed">vds.server.network.status-capability-checks-failed</p></td> - <td>failure</td> - <td>Number of status page operations that failed due to one or more missing capabilities</td> - </tr> - <tr> - <td><p id="vds_server_fnet_num-connections">vds.server.fnet.num-connections</p></td> - <td>connection</td> - <td>Total number of connection objects</td> - </tr> - </tbody> -</table> diff --git a/en/reference/vespa-set-metrics-reference.html b/en/reference/vespa-set-metrics-reference.html deleted file mode 100644 index 7418ed1a05..0000000000 --- a/en/reference/vespa-set-metrics-reference.html +++ /dev/null @@ -1,3132 +0,0 @@ ---- -# Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -# Note: This file is generated by -# https://github.com/vespa-engine/vespa/blob/master/metrics/src/main/java/ai/vespa/metrics/docs/MetricSetDocumentation.java -title: "Vespa Metric Set" ---- -<p> - This document provides reference documentation for the Vespa metric set, including suffixes present per metric. - If the suffix column contains "N/A" then the base name of the corresponding metric is used with no suffix. -</p> -<h2 id="clustercontroller-metrics">ClusterController Metrics</h2> -<table class="table"> - <thead> - <tr><th>Name</th><th>Unit</th><th>Suffixes</th><th>Description</th></tr> - </thead> - <tbody> - <tr> - <td><p id="cluster-controller_down_count">cluster-controller.down.count</p></td> - <td>node</td> - <td>last, max</td> - <td>Number of content nodes down</td> - </tr> - <tr> - <td><p id="cluster-controller_initializing_count">cluster-controller.initializing.count</p></td> - <td>node</td> - <td>last, max</td> - <td>Number of content nodes initializing</td> - </tr> - <tr> - <td><p id="cluster-controller_maintenance_count">cluster-controller.maintenance.count</p></td> - <td>node</td> - <td>last, max</td> - <td>Number of content nodes in maintenance</td> - </tr> - <tr> - <td><p id="cluster-controller_retired_count">cluster-controller.retired.count</p></td> - <td>node</td> - <td>last, max</td> - <td>Number of content nodes that are retired</td> - </tr> - <tr> - <td><p id="cluster-controller_stopping_count">cluster-controller.stopping.count</p></td> - <td>node</td> - <td>last</td> - <td>Number of content nodes currently stopping</td> - </tr> - <tr> - <td><p id="cluster-controller_up_count">cluster-controller.up.count</p></td> - <td>node</td> - <td>last, max</td> - <td>Number of content nodes up</td> - </tr> - <tr> - <td><p id="cluster-controller_nodes-not-converged">cluster-controller.nodes-not-converged</p></td> - <td>node</td> - <td>max</td> - <td>Number of nodes not converging to the latest cluster state version</td> - </tr> - <tr> - <td><p id="cluster-controller_stored-document-count">cluster-controller.stored-document-count</p></td> - <td>document</td> - <td>max</td> - <td>Total number of unique documents stored in the cluster</td> - </tr> - <tr> - <td><p id="cluster-controller_stored-document-bytes">cluster-controller.stored-document-bytes</p></td> - <td>byte</td> - <td>max</td> - <td>Combined byte size of all unique documents stored in the cluster (not including replication)</td> - </tr> - <tr> - <td><p id="cluster-controller_cluster-buckets-out-of-sync-ratio">cluster-controller.cluster-buckets-out-of-sync-ratio</p></td> - <td>fraction</td> - <td>max</td> - <td>Ratio of buckets in the cluster currently in need of syncing</td> - </tr> - <tr> - <td><p id="cluster-controller_busy-tick-time-ms">cluster-controller.busy-tick-time-ms</p></td> - <td>millisecond</td> - <td>count, last, max, sum</td> - <td>Time busy</td> - </tr> - <tr> - <td><p id="cluster-controller_idle-tick-time-ms">cluster-controller.idle-tick-time-ms</p></td> - <td>millisecond</td> - <td>count, last, max, sum</td> - <td>Time idle</td> - </tr> - <tr> - <td><p id="cluster-controller_work-ms">cluster-controller.work-ms</p></td> - <td>millisecond</td> - <td>count, last, sum</td> - <td>Time used for actual work</td> - </tr> - <tr> - <td><p id="cluster-controller_is-master">cluster-controller.is-master</p></td> - <td>binary</td> - <td>last, max</td> - <td>1 if this cluster controller is currently the master, or 0 if not</td> - </tr> - <tr> - <td><p id="cluster-controller_remote-task-queue_size">cluster-controller.remote-task-queue.size</p></td> - <td>operation</td> - <td>last</td> - <td>Number of remote tasks queued</td> - </tr> - <tr> - <td><p id="cluster-controller_resource_usage_nodes_above_limit">cluster-controller.resource_usage.nodes_above_limit</p></td> - <td>node</td> - <td>last, max</td> - <td>The number of content nodes above resource limit, blocking feed</td> - </tr> - <tr> - <td><p id="cluster-controller_resource_usage_max_memory_utilization">cluster-controller.resource_usage.max_memory_utilization</p></td> - <td>fraction</td> - <td>last, max</td> - <td>Current memory utilisation, for content node with the highest value</td> - </tr> - <tr> - <td><p id="cluster-controller_resource_usage_max_disk_utilization">cluster-controller.resource_usage.max_disk_utilization</p></td> - <td>fraction</td> - <td>last, max</td> - <td>Current disk space utilisation, for content node with the highest value</td> - </tr> - <tr> - <td><p id="cluster-controller_resource_usage_memory_limit">cluster-controller.resource_usage.memory_limit</p></td> - <td>fraction</td> - <td>last, max</td> - <td>Memory space limit as a fraction of available memory</td> - </tr> - <tr> - <td><p id="cluster-controller_resource_usage_disk_limit">cluster-controller.resource_usage.disk_limit</p></td> - <td>fraction</td> - <td>last, max</td> - <td>Disk space limit as a fraction of available disk space</td> - </tr> - <tr> - <td><p id="reindexing_progress">reindexing.progress</p></td> - <td>fraction</td> - <td>last, max</td> - <td>Re-indexing progress</td> - </tr> - </tbody> -</table> - -<h2 id="container-metrics">Container Metrics</h2> -<table class="table"> - <thead> - <tr><th>Name</th><th>Unit</th><th>Suffixes</th><th>Description</th></tr> - </thead> - <tbody> - <tr> - <td><p id="http_status_1xx">http.status.1xx</p></td> - <td>response</td> - <td>rate</td> - <td>Number of responses with a 1xx status</td> - </tr> - <tr> - <td><p id="http_status_2xx">http.status.2xx</p></td> - <td>response</td> - <td>rate</td> - <td>Number of responses with a 2xx status</td> - </tr> - <tr> - <td><p id="http_status_3xx">http.status.3xx</p></td> - <td>response</td> - <td>rate</td> - <td>Number of responses with a 3xx status</td> - </tr> - <tr> - <td><p id="http_status_4xx">http.status.4xx</p></td> - <td>response</td> - <td>rate</td> - <td>Number of responses with a 4xx status</td> - </tr> - <tr> - <td><p id="http_status_5xx">http.status.5xx</p></td> - <td>response</td> - <td>rate</td> - <td>Number of responses with a 5xx status</td> - </tr> - <tr> - <td><p id="application_generation">application_generation</p></td> - <td>version</td> - <td>N/A</td> - <td>The currently live application config generation (aka session id)</td> - </tr> - <tr> - <td><p id="jdisc_gc_count">jdisc.gc.count</p></td> - <td>operation</td> - <td>average, last, max</td> - <td>Number of JVM garbage collections done</td> - </tr> - <tr> - <td><p id="jdisc_gc_ms">jdisc.gc.ms</p></td> - <td>millisecond</td> - <td>average, last, max</td> - <td>Time spent in JVM garbage collection</td> - </tr> - <tr> - <td><p id="jdisc_jvm">jdisc.jvm</p></td> - <td>version</td> - <td>last</td> - <td>JVM runtime version</td> - </tr> - <tr> - <td><p id="jdisc_memory_mappings">jdisc.memory_mappings</p></td> - <td>operation</td> - <td>max</td> - <td>JDISC Memory mappings</td> - </tr> - <tr> - <td><p id="jdisc_open_file_descriptors">jdisc.open_file_descriptors</p></td> - <td>item</td> - <td>max</td> - <td>JDISC Open file descriptors</td> - </tr> - <tr> - <td><p id="jdisc_thread_pool_unhandled_exceptions">jdisc.thread_pool.unhandled_exceptions</p></td> - <td>thread</td> - <td>count, last, max, min, sum</td> - <td>Number of exceptions thrown by tasks</td> - </tr> - <tr> - <td><p id="jdisc_thread_pool_work_queue_capacity">jdisc.thread_pool.work_queue.capacity</p></td> - <td>thread</td> - <td>count, last, max, min, sum</td> - <td>Capacity of the task queue</td> - </tr> - <tr> - <td><p id="jdisc_thread_pool_work_queue_size">jdisc.thread_pool.work_queue.size</p></td> - <td>thread</td> - <td>count, last, max, min, sum</td> - <td>Size of the task queue</td> - </tr> - <tr> - <td><p id="jdisc_thread_pool_rejected_tasks">jdisc.thread_pool.rejected_tasks</p></td> - <td>thread</td> - <td>count, last, max, min, sum</td> - <td>Number of tasks rejected by the thread pool</td> - </tr> - <tr> - <td><p id="jdisc_thread_pool_size">jdisc.thread_pool.size</p></td> - <td>thread</td> - <td>count, last, max, min, sum</td> - <td>Size of the thread pool</td> - </tr> - <tr> - <td><p id="jdisc_thread_pool_max_allowed_size">jdisc.thread_pool.max_allowed_size</p></td> - <td>thread</td> - <td>count, last, max, min, sum</td> - <td>The maximum allowed number of threads in the pool</td> - </tr> - <tr> - <td><p id="jdisc_thread_pool_active_threads">jdisc.thread_pool.active_threads</p></td> - <td>thread</td> - <td>count, last, max, min, sum</td> - <td>Number of threads that are active</td> - </tr> - <tr> - <td><p id="jdisc_deactivated_containers_total">jdisc.deactivated_containers.total</p></td> - <td>item</td> - <td>last, sum</td> - <td>JDISC Deactivated container instances</td> - </tr> - <tr> - <td><p id="jdisc_deactivated_containers_with_retained_refs_last">jdisc.deactivated_containers.with_retained_refs.last</p></td> - <td>item</td> - <td>last</td> - <td>JDISC Deactivated container nodes with retained refs</td> - </tr> - <tr> - <td><p id="jdisc_application_failed_component_graphs">jdisc.application.failed_component_graphs</p></td> - <td>item</td> - <td>rate</td> - <td>JDISC Application failed component graphs</td> - </tr> - <tr> - <td><p id="jdisc_application_component_graph_creation_time_millis">jdisc.application.component_graph.creation_time_millis</p></td> - <td>millisecond</td> - <td>last</td> - <td>JDISC Application component graph creation time</td> - </tr> - <tr> - <td><p id="jdisc_application_component_graph_reconfigurations">jdisc.application.component_graph.reconfigurations</p></td> - <td>item</td> - <td>rate</td> - <td>JDISC Application component graph reconfigurations</td> - </tr> - <tr> - <td><p id="jdisc_singleton_is_active">jdisc.singleton.is_active</p></td> - <td>item</td> - <td>last, max, min</td> - <td>JDISC Singleton is active</td> - </tr> - <tr> - <td><p id="jdisc_singleton_activation_count">jdisc.singleton.activation.count</p></td> - <td>operation</td> - <td>last</td> - <td>JDISC Singleton activations</td> - </tr> - <tr> - <td><p id="jdisc_singleton_activation_failure_count">jdisc.singleton.activation.failure.count</p></td> - <td>operation</td> - <td>last</td> - <td>JDISC Singleton activation failures</td> - </tr> - <tr> - <td><p id="jdisc_singleton_activation_millis">jdisc.singleton.activation.millis</p></td> - <td>millisecond</td> - <td>last</td> - <td>JDISC Singleton activation time</td> - </tr> - <tr> - <td><p id="jdisc_singleton_deactivation_count">jdisc.singleton.deactivation.count</p></td> - <td>operation</td> - <td>last</td> - <td>JDISC Singleton deactivations</td> - </tr> - <tr> - <td><p id="jdisc_singleton_deactivation_failure_count">jdisc.singleton.deactivation.failure.count</p></td> - <td>operation</td> - <td>last</td> - <td>JDISC Singleton deactivation failures</td> - </tr> - <tr> - <td><p id="jdisc_singleton_deactivation_millis">jdisc.singleton.deactivation.millis</p></td> - <td>millisecond</td> - <td>last</td> - <td>JDISC Singleton deactivation time</td> - </tr> - <tr> - <td><p id="jdisc_http_ssl_handshake_failure_missing_client_cert">jdisc.http.ssl.handshake.failure.missing_client_cert</p></td> - <td>operation</td> - <td>rate</td> - <td>JDISC HTTP SSL Handshake failures due to missing client certificate</td> - </tr> - <tr> - <td><p id="jdisc_http_ssl_handshake_failure_expired_client_cert">jdisc.http.ssl.handshake.failure.expired_client_cert</p></td> - <td>operation</td> - <td>rate</td> - <td>JDISC HTTP SSL Handshake failures due to expired client certificate</td> - </tr> - <tr> - <td><p id="jdisc_http_ssl_handshake_failure_invalid_client_cert">jdisc.http.ssl.handshake.failure.invalid_client_cert</p></td> - <td>operation</td> - <td>rate</td> - <td>JDISC HTTP SSL Handshake failures due to invalid client certificate</td> - </tr> - <tr> - <td><p id="jdisc_http_ssl_handshake_failure_incompatible_protocols">jdisc.http.ssl.handshake.failure.incompatible_protocols</p></td> - <td>operation</td> - <td>rate</td> - <td>JDISC HTTP SSL Handshake failures due to incompatible protocols</td> - </tr> - <tr> - <td><p id="jdisc_http_ssl_handshake_failure_incompatible_chifers">jdisc.http.ssl.handshake.failure.incompatible_chifers</p></td> - <td>operation</td> - <td>rate</td> - <td>JDISC HTTP SSL Handshake failures due to incompatible chifers</td> - </tr> - <tr> - <td><p id="jdisc_http_ssl_handshake_failure_connection_closed">jdisc.http.ssl.handshake.failure.connection_closed</p></td> - <td>operation</td> - <td>rate</td> - <td>JDISC HTTP SSL Handshake failures due to connection closed</td> - </tr> - <tr> - <td><p id="jdisc_http_ssl_handshake_failure_unknown">jdisc.http.ssl.handshake.failure.unknown</p></td> - <td>operation</td> - <td>rate</td> - <td>JDISC HTTP SSL Handshake failures for unknown reason</td> - </tr> - <tr> - <td><p id="jdisc_http_request_prematurely_closed">jdisc.http.request.prematurely_closed</p></td> - <td>request</td> - <td>rate</td> - <td>HTTP requests prematurely closed</td> - </tr> - <tr> - <td><p id="jdisc_http_request_requests_per_connection">jdisc.http.request.requests_per_connection</p></td> - <td>request</td> - <td>average, count, max, min, sum</td> - <td>HTTP requests per connection</td> - </tr> - <tr> - <td><p id="jdisc_http_request_uri_length">jdisc.http.request.uri_length</p></td> - <td>byte</td> - <td>count, max, sum</td> - <td>HTTP URI length</td> - </tr> - <tr> - <td><p id="jdisc_http_request_content_size">jdisc.http.request.content_size</p></td> - <td>byte</td> - <td>count, max, sum</td> - <td>HTTP request content size</td> - </tr> - <tr> - <td><p id="jdisc_http_requests">jdisc.http.requests</p></td> - <td>request</td> - <td>count, rate</td> - <td>HTTP requests</td> - </tr> - <tr> - <td><p id="jdisc_http_filter_rule_blocked_requests">jdisc.http.filter.rule.blocked_requests</p></td> - <td>request</td> - <td>rate</td> - <td>Number of requests blocked by filter</td> - </tr> - <tr> - <td><p id="jdisc_http_filter_rule_allowed_requests">jdisc.http.filter.rule.allowed_requests</p></td> - <td>request</td> - <td>rate</td> - <td>Number of requests allowed by filter</td> - </tr> - <tr> - <td><p id="jdisc_http_filtering_request_handled">jdisc.http.filtering.request.handled</p></td> - <td>request</td> - <td>rate</td> - <td>Number of filtering requests handled</td> - </tr> - <tr> - <td><p id="jdisc_http_filtering_request_unhandled">jdisc.http.filtering.request.unhandled</p></td> - <td>request</td> - <td>rate</td> - <td>Number of filtering requests unhandled</td> - </tr> - <tr> - <td><p id="jdisc_http_filtering_response_handled">jdisc.http.filtering.response.handled</p></td> - <td>request</td> - <td>rate</td> - <td>Number of filtering responses handled</td> - </tr> - <tr> - <td><p id="jdisc_http_filtering_response_unhandled">jdisc.http.filtering.response.unhandled</p></td> - <td>request</td> - <td>rate</td> - <td>Number of filtering responses unhandled</td> - </tr> - <tr> - <td><p id="jdisc_http_handler_unhandled_exceptions">jdisc.http.handler.unhandled_exceptions</p></td> - <td>request</td> - <td>rate</td> - <td>Number of unhandled exceptions in handler</td> - </tr> - <tr> - <td><p id="jdisc_tls_capability_checks_succeeded">jdisc.tls.capability_checks.succeeded</p></td> - <td>operation</td> - <td>rate</td> - <td>Number of TLS capability checks succeeded</td> - </tr> - <tr> - <td><p id="jdisc_tls_capability_checks_failed">jdisc.tls.capability_checks.failed</p></td> - <td>operation</td> - <td>rate</td> - <td>Number of TLS capability checks failed</td> - </tr> - <tr> - <td><p id="jdisc_http_jetty_threadpool_thread_max">jdisc.http.jetty.threadpool.thread.max</p></td> - <td>thread</td> - <td>count, last, max, min, sum</td> - <td>Configured maximum number of threads</td> - </tr> - <tr> - <td><p id="jdisc_http_jetty_threadpool_thread_min">jdisc.http.jetty.threadpool.thread.min</p></td> - <td>thread</td> - <td>count, last, max, min, sum</td> - <td>Configured minimum number of threads</td> - </tr> - <tr> - <td><p id="jdisc_http_jetty_threadpool_thread_reserved">jdisc.http.jetty.threadpool.thread.reserved</p></td> - <td>thread</td> - <td>count, last, max, min, sum</td> - <td>Configured number of reserved threads or -1 for heuristic</td> - </tr> - <tr> - <td><p id="jdisc_http_jetty_threadpool_thread_busy">jdisc.http.jetty.threadpool.thread.busy</p></td> - <td>thread</td> - <td>count, last, max, min, sum</td> - <td>Number of threads executing internal and transient jobs</td> - </tr> - <tr> - <td><p id="jdisc_http_jetty_threadpool_thread_total">jdisc.http.jetty.threadpool.thread.total</p></td> - <td>thread</td> - <td>count, last, max, min, sum</td> - <td>Current number of threads</td> - </tr> - <tr> - <td><p id="jdisc_http_jetty_threadpool_queue_size">jdisc.http.jetty.threadpool.queue.size</p></td> - <td>thread</td> - <td>count, last, max, min, sum</td> - <td>Current size of the job queue</td> - </tr> - <tr> - <td><p id="jdisc_http_jetty_http_compliance_violation">jdisc.http.jetty.http_compliance.violation</p></td> - <td>failure</td> - <td>rate</td> - <td>Number of HTTP compliance violations</td> - </tr> - <tr> - <td><p id="serverNumOpenConnections">serverNumOpenConnections</p></td> - <td>connection</td> - <td>average, last, max</td> - <td>The number of currently open connections</td> - </tr> - <tr> - <td><p id="serverNumConnections">serverNumConnections</p></td> - <td>connection</td> - <td>average, last, max</td> - <td>The total number of connections opened</td> - </tr> - <tr> - <td><p id="serverBytesReceived">serverBytesReceived</p></td> - <td>byte</td> - <td>count, sum</td> - <td>The number of bytes received by the server</td> - </tr> - <tr> - <td><p id="serverBytesSent">serverBytesSent</p></td> - <td>byte</td> - <td>count, sum</td> - <td>The number of bytes sent from the server</td> - </tr> - <tr> - <td><p id="handled_requests">handled.requests</p></td> - <td>operation</td> - <td>count</td> - <td>The number of requests handled per metrics snapshot</td> - </tr> - <tr> - <td><p id="handled_latency">handled.latency</p></td> - <td>millisecond</td> - <td>count, max, sum</td> - <td>The time used for requests during this metrics snapshot</td> - </tr> - <tr> - <td><p id="httpapi_latency">httpapi_latency</p></td> - <td>millisecond</td> - <td>count, max, sum</td> - <td>Duration for requests to the HTTP document APIs</td> - </tr> - <tr> - <td><p id="httpapi_pending">httpapi_pending</p></td> - <td>operation</td> - <td>count, max, sum</td> - <td>Document operations pending execution</td> - </tr> - <tr> - <td><p id="httpapi_num_operations">httpapi_num_operations</p></td> - <td>operation</td> - <td>rate</td> - <td>Total number of document operations performed</td> - </tr> - <tr> - <td><p id="httpapi_num_updates">httpapi_num_updates</p></td> - <td>operation</td> - <td>rate</td> - <td>Document update operations performed</td> - </tr> - <tr> - <td><p id="httpapi_num_removes">httpapi_num_removes</p></td> - <td>operation</td> - <td>rate</td> - <td>Document remove operations performed</td> - </tr> - <tr> - <td><p id="httpapi_num_puts">httpapi_num_puts</p></td> - <td>operation</td> - <td>rate</td> - <td>Document put operations performed</td> - </tr> - <tr> - <td><p id="httpapi_succeeded">httpapi_succeeded</p></td> - <td>operation</td> - <td>rate</td> - <td>Document operations that succeeded</td> - </tr> - <tr> - <td><p id="httpapi_failed">httpapi_failed</p></td> - <td>operation</td> - <td>rate</td> - <td>Document operations that failed</td> - </tr> - <tr> - <td><p id="httpapi_parse_error">httpapi_parse_error</p></td> - <td>operation</td> - <td>rate</td> - <td>Document operations that failed due to document parse errors</td> - </tr> - <tr> - <td><p id="httpapi_condition_not_met">httpapi_condition_not_met</p></td> - <td>operation</td> - <td>rate</td> - <td>Document operations not applied due to condition not met</td> - </tr> - <tr> - <td><p id="httpapi_not_found">httpapi_not_found</p></td> - <td>operation</td> - <td>rate</td> - <td>Document operations not applied due to document not found</td> - </tr> - <tr> - <td><p id="httpapi_failed_unknown">httpapi_failed_unknown</p></td> - <td>operation</td> - <td>rate</td> - <td>Document operations failed by unknown cause</td> - </tr> - <tr> - <td><p id="httpapi_failed_timeout">httpapi_failed_timeout</p></td> - <td>operation</td> - <td>rate</td> - <td>Document operations failed by timeout</td> - </tr> - <tr> - <td><p id="httpapi_failed_insufficient_storage">httpapi_failed_insufficient_storage</p></td> - <td>operation</td> - <td>rate</td> - <td>Document operations failed by insufficient storage</td> - </tr> - <tr> - <td><p id="httpapi_queued_operations">httpapi_queued_operations</p></td> - <td>operation</td> - <td>last</td> - <td>Document operations queued for execution in /document/v1 API handler</td> - </tr> - <tr> - <td><p id="httpapi_queued_bytes">httpapi_queued_bytes</p></td> - <td>byte</td> - <td>last</td> - <td>Total operation bytes queued for execution in /document/v1 API handler</td> - </tr> - <tr> - <td><p id="httpapi_queued_age">httpapi_queued_age</p></td> - <td>second</td> - <td>last</td> - <td>Age in seconds of the oldest operation in the queue for /document/v1 API handler</td> - </tr> - <tr> - <td><p id="httpapi_mbus_window_size">httpapi_mbus_window_size</p></td> - <td>operation</td> - <td>last</td> - <td>The window size of Messagebus's dynamic throttle policy for /document/v1 API handler</td> - </tr> - <tr> - <td><p id="mem_heap_total">mem.heap.total</p></td> - <td>byte</td> - <td>average</td> - <td>Total available heap memory</td> - </tr> - <tr> - <td><p id="mem_heap_free">mem.heap.free</p></td> - <td>byte</td> - <td>average</td> - <td>Free heap memory</td> - </tr> - <tr> - <td><p id="mem_heap_used">mem.heap.used</p></td> - <td>byte</td> - <td>average, max</td> - <td>Currently used heap memory</td> - </tr> - <tr> - <td><p id="mem_direct_total">mem.direct.total</p></td> - <td>byte</td> - <td>average</td> - <td>Total available direct memory</td> - </tr> - <tr> - <td><p id="mem_direct_free">mem.direct.free</p></td> - <td>byte</td> - <td>average</td> - <td>Currently free direct memory</td> - </tr> - <tr> - <td><p id="mem_direct_used">mem.direct.used</p></td> - <td>byte</td> - <td>average, max</td> - <td>Direct memory currently used</td> - </tr> - <tr> - <td><p id="mem_direct_count">mem.direct.count</p></td> - <td>byte</td> - <td>max</td> - <td>Number of direct memory allocations</td> - </tr> - <tr> - <td><p id="mem_native_total">mem.native.total</p></td> - <td>byte</td> - <td>average</td> - <td>Total available native memory</td> - </tr> - <tr> - <td><p id="mem_native_free">mem.native.free</p></td> - <td>byte</td> - <td>average</td> - <td>Currently free native memory</td> - </tr> - <tr> - <td><p id="mem_native_used">mem.native.used</p></td> - <td>byte</td> - <td>average</td> - <td>Native memory currently used</td> - </tr> - <tr> - <td><p id="athenz-tenant-cert_expiry_seconds">athenz-tenant-cert.expiry.seconds</p></td> - <td>second</td> - <td>last, max, min</td> - <td>Time remaining until Athenz tenant certificate expires</td> - </tr> - <tr> - <td><p id="container-iam-role_expiry_seconds">container-iam-role.expiry.seconds</p></td> - <td>second</td> - <td>N/A</td> - <td>Time remaining until IAM role expires</td> - </tr> - <tr> - <td><p id="peak_qps">peak_qps</p></td> - <td>query_per_second</td> - <td>max</td> - <td>The highest number of qps for a second for this metrics snapshot</td> - </tr> - <tr> - <td><p id="search_connections">search_connections</p></td> - <td>connection</td> - <td>count, max, sum</td> - <td>Number of search connections</td> - </tr> - <tr> - <td><p id="feed_operations">feed.operations</p></td> - <td>operation</td> - <td>rate</td> - <td>Number of document feed operations</td> - </tr> - <tr> - <td><p id="feed_latency">feed.latency</p></td> - <td>millisecond</td> - <td>count, max, sum</td> - <td>Feed latency</td> - </tr> - <tr> - <td><p id="feed_http-requests">feed.http-requests</p></td> - <td>operation</td> - <td>count, rate</td> - <td>Feed HTTP requests</td> - </tr> - <tr> - <td><p id="queries">queries</p></td> - <td>operation</td> - <td>rate</td> - <td>Query volume</td> - </tr> - <tr> - <td><p id="query_container_latency">query_container_latency</p></td> - <td>millisecond</td> - <td>count, max, sum</td> - <td>The query execution time consumed in the container</td> - </tr> - <tr> - <td><p id="query_latency">query_latency</p></td> - <td>millisecond</td> - <td>count, max, sum</td> - <td>The overall query latency as seen by the container</td> - </tr> - <tr> - <td><p id="query_timeout">query_timeout</p></td> - <td>millisecond</td> - <td>count, max, min, sum</td> - <td>The amount of time allowed for query execution, from the client</td> - </tr> - <tr> - <td><p id="failed_queries">failed_queries</p></td> - <td>operation</td> - <td>rate</td> - <td>The number of failed queries</td> - </tr> - <tr> - <td><p id="degraded_queries">degraded_queries</p></td> - <td>operation</td> - <td>rate</td> - <td>The number of degraded queries, e.g. due to some content nodes not responding in time</td> - </tr> - <tr> - <td><p id="hits_per_query">hits_per_query</p></td> - <td>hit_per_query</td> - <td>count, max, sum</td> - <td>The number of hits returned</td> - </tr> - <tr> - <td><p id="query_hit_offset">query_hit_offset</p></td> - <td>hit</td> - <td>count, max, sum</td> - <td>The offset for hits returned</td> - </tr> - <tr> - <td><p id="documents_covered">documents_covered</p></td> - <td>document</td> - <td>count</td> - <td>The combined number of documents considered during query evaluation</td> - </tr> - <tr> - <td><p id="documents_total">documents_total</p></td> - <td>document</td> - <td>count</td> - <td>The number of documents to be evaluated if all requests had been fully executed</td> - </tr> - <tr> - <td><p id="documents_target_total">documents_target_total</p></td> - <td>document</td> - <td>count</td> - <td>The target number of total documents to be evaluated when all data is in sync</td> - </tr> - <tr> - <td><p id="jdisc_render_latency">jdisc.render.latency</p></td> - <td>nanosecond</td> - <td>average, count, last, max, min, sum</td> - <td>The time used by the container to render responses</td> - </tr> - <tr> - <td><p id="query_item_count">query_item_count</p></td> - <td>item</td> - <td>count, max, sum</td> - <td>The number of query items (terms, phrases, etc.)</td> - </tr> - <tr> - <td><p id="docproc_proctime">docproc.proctime</p></td> - <td>millisecond</td> - <td>count, max, sum</td> - <td>Time spent processing document</td> - </tr> - <tr> - <td><p id="docproc_documents">docproc.documents</p></td> - <td>document</td> - <td>count, max, min, sum</td> - <td>Number of processed documents</td> - </tr> - <tr> - <td><p id="totalhits_per_query">totalhits_per_query</p></td> - <td>hit_per_query</td> - <td>count, max, sum</td> - <td>The total number of documents found to match queries</td> - </tr> - <tr> - <td><p id="empty_results">empty_results</p></td> - <td>operation</td> - <td>rate</td> - <td>Number of queries matching no documents</td> - </tr> - <tr> - <td><p id="requestsOverQuota">requestsOverQuota</p></td> - <td>operation</td> - <td>count, rate</td> - <td>The number of requests rejected due to exceeding quota</td> - </tr> - <tr> - <td><p id="relevance_at_1">relevance.at_1</p></td> - <td>score</td> - <td>count, sum</td> - <td>The relevance of hit number 1</td> - </tr> - <tr> - <td><p id="relevance_at_3">relevance.at_3</p></td> - <td>score</td> - <td>count, sum</td> - <td>The relevance of hit number 3</td> - </tr> - <tr> - <td><p id="relevance_at_10">relevance.at_10</p></td> - <td>score</td> - <td>count, sum</td> - <td>The relevance of hit number 10</td> - </tr> - <tr> - <td><p id="error_timeout">error.timeout</p></td> - <td>operation</td> - <td>rate</td> - <td>Requests that timed out</td> - </tr> - <tr> - <td><p id="error_backends_oos">error.backends_oos</p></td> - <td>operation</td> - <td>rate</td> - <td>Requests that failed due to no available backends nodes</td> - </tr> - <tr> - <td><p id="error_plugin_failure">error.plugin_failure</p></td> - <td>operation</td> - <td>rate</td> - <td>Requests that failed due to plugin failure</td> - </tr> - <tr> - <td><p id="error_backend_communication_error">error.backend_communication_error</p></td> - <td>operation</td> - <td>rate</td> - <td>Requests that failed due to backend communication error</td> - </tr> - <tr> - <td><p id="error_empty_document_summaries">error.empty_document_summaries</p></td> - <td>operation</td> - <td>rate</td> - <td>Requests that failed due to missing document summaries</td> - </tr> - <tr> - <td><p id="error_invalid_query_parameter">error.invalid_query_parameter</p></td> - <td>operation</td> - <td>rate</td> - <td>Requests that failed due to invalid query parameters</td> - </tr> - <tr> - <td><p id="error_internal_server_error">error.internal_server_error</p></td> - <td>operation</td> - <td>rate</td> - <td>Requests that failed due to internal server error</td> - </tr> - <tr> - <td><p id="error_misconfigured_server">error.misconfigured_server</p></td> - <td>operation</td> - <td>rate</td> - <td>Requests that failed due to misconfigured server</td> - </tr> - <tr> - <td><p id="error_invalid_query_transformation">error.invalid_query_transformation</p></td> - <td>operation</td> - <td>rate</td> - <td>Requests that failed due to invalid query transformation</td> - </tr> - <tr> - <td><p id="error_results_with_errors">error.results_with_errors</p></td> - <td>operation</td> - <td>rate</td> - <td>The number of queries with error payload</td> - </tr> - <tr> - <td><p id="error_unspecified">error.unspecified</p></td> - <td>operation</td> - <td>rate</td> - <td>Requests that failed for an unspecified reason</td> - </tr> - <tr> - <td><p id="error_unhandled_exception">error.unhandled_exception</p></td> - <td>operation</td> - <td>rate</td> - <td>Requests that failed due to an unhandled exception</td> - </tr> - <tr> - <td><p id="serverRejectedRequests">serverRejectedRequests</p></td> - <td>operation</td> - <td>count, rate</td> - <td>Deprecated. Use jdisc.thread_pool.rejected_tasks instead.</td> - </tr> - <tr> - <td><p id="serverThreadPoolSize">serverThreadPoolSize</p></td> - <td>thread</td> - <td>last, max</td> - <td>Deprecated. Use jdisc.thread_pool.size instead.</td> - </tr> - <tr> - <td><p id="serverActiveThreads">serverActiveThreads</p></td> - <td>thread</td> - <td>count, last, max, min, sum</td> - <td>Deprecated. Use jdisc.thread_pool.active_threads instead.</td> - </tr> - <tr> - <td><p id="jrt_transport_tls-certificate-verification-failures">jrt.transport.tls-certificate-verification-failures</p></td> - <td>failure</td> - <td>N/A</td> - <td>TLS certificate verification failures</td> - </tr> - <tr> - <td><p id="jrt_transport_peer-authorization-failures">jrt.transport.peer-authorization-failures</p></td> - <td>failure</td> - <td>N/A</td> - <td>TLS peer authorization failures</td> - </tr> - <tr> - <td><p id="jrt_transport_server_tls-connections-established">jrt.transport.server.tls-connections-established</p></td> - <td>connection</td> - <td>N/A</td> - <td>TLS server connections established</td> - </tr> - <tr> - <td><p id="jrt_transport_client_tls-connections-established">jrt.transport.client.tls-connections-established</p></td> - <td>connection</td> - <td>N/A</td> - <td>TLS client connections established</td> - </tr> - <tr> - <td><p id="jrt_transport_server_unencrypted-connections-established">jrt.transport.server.unencrypted-connections-established</p></td> - <td>connection</td> - <td>N/A</td> - <td>Unencrypted server connections established</td> - </tr> - <tr> - <td><p id="jrt_transport_client_unencrypted-connections-established">jrt.transport.client.unencrypted-connections-established</p></td> - <td>connection</td> - <td>N/A</td> - <td>Unencrypted client connections established</td> - </tr> - <tr> - <td><p id="embedder_latency">embedder.latency</p></td> - <td>millisecond</td> - <td>count, max, sum</td> - <td>Time spent creating an embedding</td> - </tr> - <tr> - <td><p id="embedder_sequence_length">embedder.sequence_length</p></td> - <td>byte</td> - <td>count, max, sum</td> - <td>Size of sequence produced by tokenizer</td> - </tr> - </tbody> -</table> - -<h2 id="distributor-metrics">Distributor Metrics</h2> -<table class="table"> - <thead> - <tr><th>Name</th><th>Unit</th><th>Suffixes</th><th>Description</th></tr> - </thead> - <tbody> - <tr> - <td><p id="vds_idealstate_buckets_rechecking">vds.idealstate.buckets_rechecking</p></td> - <td>bucket</td> - <td>average</td> - <td>The number of buckets that we are rechecking for ideal state operations</td> - </tr> - <tr> - <td><p id="vds_idealstate_idealstate_diff">vds.idealstate.idealstate_diff</p></td> - <td>bucket</td> - <td>average</td> - <td>A number representing the current difference from the ideal state. This is a number that decreases steadily as the system is getting closer to the ideal state</td> - </tr> - <tr> - <td><p id="vds_idealstate_buckets_toofewcopies">vds.idealstate.buckets_toofewcopies</p></td> - <td>bucket</td> - <td>average</td> - <td>The number of buckets the distributor controls that have less than the desired redundancy</td> - </tr> - <tr> - <td><p id="vds_idealstate_buckets_toomanycopies">vds.idealstate.buckets_toomanycopies</p></td> - <td>bucket</td> - <td>average</td> - <td>The number of buckets the distributor controls that have more than the desired redundancy</td> - </tr> - <tr> - <td><p id="vds_idealstate_buckets">vds.idealstate.buckets</p></td> - <td>bucket</td> - <td>average</td> - <td>The number of buckets the distributor controls</td> - </tr> - <tr> - <td><p id="vds_idealstate_buckets_notrusted">vds.idealstate.buckets_notrusted</p></td> - <td>bucket</td> - <td>average</td> - <td>The number of buckets that have no trusted copies.</td> - </tr> - <tr> - <td><p id="vds_idealstate_bucket_replicas_moving_out">vds.idealstate.bucket_replicas_moving_out</p></td> - <td>bucket</td> - <td>average</td> - <td>Bucket replicas that should be moved out, e.g. retirement case or node added to cluster that has higher ideal state priority.</td> - </tr> - <tr> - <td><p id="vds_idealstate_bucket_replicas_copying_out">vds.idealstate.bucket_replicas_copying_out</p></td> - <td>bucket</td> - <td>average</td> - <td>Bucket replicas that should be copied out, e.g. node is in ideal state but might have to provide data other nodes in a merge</td> - </tr> - <tr> - <td><p id="vds_idealstate_bucket_replicas_copying_in">vds.idealstate.bucket_replicas_copying_in</p></td> - <td>bucket</td> - <td>average</td> - <td>Bucket replicas that should be copied in, e.g. node does not have a replica for a bucket that it is in ideal state for</td> - </tr> - <tr> - <td><p id="vds_idealstate_bucket_replicas_syncing">vds.idealstate.bucket_replicas_syncing</p></td> - <td>bucket</td> - <td>average</td> - <td>Bucket replicas that need syncing due to mismatching metadata</td> - </tr> - <tr> - <td><p id="vds_idealstate_max_observed_time_since_last_gc_sec">vds.idealstate.max_observed_time_since_last_gc_sec</p></td> - <td>second</td> - <td>average</td> - <td>Maximum time (in seconds) since GC was last successfully run for a bucket. Aggregated max value across all buckets on the distributor.</td> - </tr> - <tr> - <td><p id="vds_idealstate_delete_bucket_done_ok">vds.idealstate.delete_bucket.done_ok</p></td> - <td>operation</td> - <td>rate</td> - <td>The number of operations successfully performed</td> - </tr> - <tr> - <td><p id="vds_idealstate_delete_bucket_done_failed">vds.idealstate.delete_bucket.done_failed</p></td> - <td>operation</td> - <td>rate</td> - <td>The number of operations that failed</td> - </tr> - <tr> - <td><p id="vds_idealstate_delete_bucket_pending">vds.idealstate.delete_bucket.pending</p></td> - <td>operation</td> - <td>average</td> - <td>The number of operations pending</td> - </tr> - <tr> - <td><p id="vds_idealstate_merge_bucket_done_ok">vds.idealstate.merge_bucket.done_ok</p></td> - <td>operation</td> - <td>rate</td> - <td>The number of operations successfully performed</td> - </tr> - <tr> - <td><p id="vds_idealstate_merge_bucket_done_failed">vds.idealstate.merge_bucket.done_failed</p></td> - <td>operation</td> - <td>rate</td> - <td>The number of operations that failed</td> - </tr> - <tr> - <td><p id="vds_idealstate_merge_bucket_pending">vds.idealstate.merge_bucket.pending</p></td> - <td>operation</td> - <td>average</td> - <td>The number of operations pending</td> - </tr> - <tr> - <td><p id="vds_idealstate_merge_bucket_blocked">vds.idealstate.merge_bucket.blocked</p></td> - <td>operation</td> - <td>rate</td> - <td>The number of operations blocked by blocking operation starter</td> - </tr> - <tr> - <td><p id="vds_idealstate_merge_bucket_throttled">vds.idealstate.merge_bucket.throttled</p></td> - <td>operation</td> - <td>rate</td> - <td>The number of operations throttled by throttling operation starter</td> - </tr> - <tr> - <td><p id="vds_idealstate_merge_bucket_source_only_copy_changed">vds.idealstate.merge_bucket.source_only_copy_changed</p></td> - <td>operation</td> - <td>rate</td> - <td>The number of merge operations where source-only copy changed</td> - </tr> - <tr> - <td><p id="vds_idealstate_merge_bucket_source_only_copy_delete_blocked">vds.idealstate.merge_bucket.source_only_copy_delete_blocked</p></td> - <td>operation</td> - <td>rate</td> - <td>The number of merge operations where delete of unchanged source-only copies was blocked</td> - </tr> - <tr> - <td><p id="vds_idealstate_merge_bucket_source_only_copy_delete_failed">vds.idealstate.merge_bucket.source_only_copy_delete_failed</p></td> - <td>operation</td> - <td>rate</td> - <td>The number of merge operations where delete of unchanged source-only copies failed</td> - </tr> - <tr> - <td><p id="vds_idealstate_split_bucket_done_ok">vds.idealstate.split_bucket.done_ok</p></td> - <td>operation</td> - <td>rate</td> - <td>The number of operations successfully performed</td> - </tr> - <tr> - <td><p id="vds_idealstate_split_bucket_done_failed">vds.idealstate.split_bucket.done_failed</p></td> - <td>operation</td> - <td>rate</td> - <td>The number of operations that failed</td> - </tr> - <tr> - <td><p id="vds_idealstate_split_bucket_pending">vds.idealstate.split_bucket.pending</p></td> - <td>operation</td> - <td>average</td> - <td>The number of operations pending</td> - </tr> - <tr> - <td><p id="vds_idealstate_join_bucket_done_ok">vds.idealstate.join_bucket.done_ok</p></td> - <td>operation</td> - <td>rate</td> - <td>The number of operations successfully performed</td> - </tr> - <tr> - <td><p id="vds_idealstate_join_bucket_done_failed">vds.idealstate.join_bucket.done_failed</p></td> - <td>operation</td> - <td>rate</td> - <td>The number of operations that failed</td> - </tr> - <tr> - <td><p id="vds_idealstate_join_bucket_pending">vds.idealstate.join_bucket.pending</p></td> - <td>operation</td> - <td>average</td> - <td>The number of operations pending</td> - </tr> - <tr> - <td><p id="vds_idealstate_garbage_collection_done_ok">vds.idealstate.garbage_collection.done_ok</p></td> - <td>operation</td> - <td>rate</td> - <td>The number of operations successfully performed</td> - </tr> - <tr> - <td><p id="vds_idealstate_garbage_collection_done_failed">vds.idealstate.garbage_collection.done_failed</p></td> - <td>operation</td> - <td>rate</td> - <td>The number of operations that failed</td> - </tr> - <tr> - <td><p id="vds_idealstate_garbage_collection_pending">vds.idealstate.garbage_collection.pending</p></td> - <td>operation</td> - <td>average</td> - <td>The number of operations pending</td> - </tr> - <tr> - <td><p id="vds_idealstate_garbage_collection_documents_removed">vds.idealstate.garbage_collection.documents_removed</p></td> - <td>document</td> - <td>count, rate</td> - <td>Number of documents removed by GC operations</td> - </tr> - <tr> - <td><p id="vds_distributor_puts_latency">vds.distributor.puts.latency</p></td> - <td>millisecond</td> - <td>count, max, sum</td> - <td>The latency of put operations</td> - </tr> - <tr> - <td><p id="vds_distributor_puts_ok">vds.distributor.puts.ok</p></td> - <td>operation</td> - <td>rate</td> - <td>The number of successful put operations performed</td> - </tr> - <tr> - <td><p id="vds_distributor_puts_failures_total">vds.distributor.puts.failures.total</p></td> - <td>operation</td> - <td>rate</td> - <td>Sum of all failures</td> - </tr> - <tr> - <td><p id="vds_distributor_puts_failures_notfound">vds.distributor.puts.failures.notfound</p></td> - <td>operation</td> - <td>rate</td> - <td>The number of operations that failed because the document did not exist</td> - </tr> - <tr> - <td><p id="vds_distributor_puts_failures_test_and_set_failed">vds.distributor.puts.failures.test_and_set_failed</p></td> - <td>operation</td> - <td>rate</td> - <td>The number of mutating operations that failed because they specified a test-and-set condition that did not match the existing document</td> - </tr> - <tr> - <td><p id="vds_distributor_puts_failures_concurrent_mutations">vds.distributor.puts.failures.concurrent_mutations</p></td> - <td>operation</td> - <td>rate</td> - <td>The number of operations that were transiently failed due to a mutating operation already being in progress for its document ID</td> - </tr> - <tr> - <td><p id="vds_distributor_puts_failures_notconnected">vds.distributor.puts.failures.notconnected</p></td> - <td>operation</td> - <td>rate</td> - <td>The number of operations discarded because there were no available storage nodes to send to</td> - </tr> - <tr> - <td><p id="vds_distributor_puts_failures_notready">vds.distributor.puts.failures.notready</p></td> - <td>operation</td> - <td>rate</td> - <td>The number of operations discarded because distributor was not ready</td> - </tr> - <tr> - <td><p id="vds_distributor_puts_failures_wrongdistributor">vds.distributor.puts.failures.wrongdistributor</p></td> - <td>operation</td> - <td>rate</td> - <td>The number of operations discarded because they were sent to the wrong distributor</td> - </tr> - <tr> - <td><p id="vds_distributor_puts_failures_safe_time_not_reached">vds.distributor.puts.failures.safe_time_not_reached</p></td> - <td>operation</td> - <td>rate</td> - <td>The number of operations that were transiently failed due to them arriving before the safe time point for bucket ownership handovers has passed</td> - </tr> - <tr> - <td><p id="vds_distributor_puts_failures_storagefailure">vds.distributor.puts.failures.storagefailure</p></td> - <td>operation</td> - <td>rate</td> - <td>The number of operations that failed in storage</td> - </tr> - <tr> - <td><p id="vds_distributor_puts_failures_timeout">vds.distributor.puts.failures.timeout</p></td> - <td>operation</td> - <td>rate</td> - <td>The number of operations that failed because the operation timed out towards storage</td> - </tr> - <tr> - <td><p id="vds_distributor_puts_failures_busy">vds.distributor.puts.failures.busy</p></td> - <td>operation</td> - <td>rate</td> - <td>The number of messages from storage that failed because the storage node was busy</td> - </tr> - <tr> - <td><p id="vds_distributor_puts_failures_inconsistent_bucket">vds.distributor.puts.failures.inconsistent_bucket</p></td> - <td>operation</td> - <td>rate</td> - <td>The number of operations failed due to buckets being in an inconsistent state or not found</td> - </tr> - <tr> - <td><p id="vds_distributor_removes_latency">vds.distributor.removes.latency</p></td> - <td>millisecond</td> - <td>count, max, sum</td> - <td>The latency of remove operations</td> - </tr> - <tr> - <td><p id="vds_distributor_removes_ok">vds.distributor.removes.ok</p></td> - <td>operation</td> - <td>rate</td> - <td>The number of successful removes operations performed</td> - </tr> - <tr> - <td><p id="vds_distributor_removes_failures_total">vds.distributor.removes.failures.total</p></td> - <td>operation</td> - <td>rate</td> - <td>Sum of all failures</td> - </tr> - <tr> - <td><p id="vds_distributor_removes_failures_notfound">vds.distributor.removes.failures.notfound</p></td> - <td>operation</td> - <td>rate</td> - <td>The number of operations that failed because the document did not exist</td> - </tr> - <tr> - <td><p id="vds_distributor_removes_failures_test_and_set_failed">vds.distributor.removes.failures.test_and_set_failed</p></td> - <td>operation</td> - <td>rate</td> - <td>The number of mutating operations that failed because they specified a test-and-set condition that did not match the existing document</td> - </tr> - <tr> - <td><p id="vds_distributor_removes_failures_concurrent_mutations">vds.distributor.removes.failures.concurrent_mutations</p></td> - <td>operation</td> - <td>rate</td> - <td>The number of operations that were transiently failed due to a mutating operation already being in progress for its document ID</td> - </tr> - <tr> - <td><p id="vds_distributor_updates_latency">vds.distributor.updates.latency</p></td> - <td>millisecond</td> - <td>count, max, sum</td> - <td>The latency of update operations</td> - </tr> - <tr> - <td><p id="vds_distributor_updates_ok">vds.distributor.updates.ok</p></td> - <td>operation</td> - <td>rate</td> - <td>The number of successful updates operations performed</td> - </tr> - <tr> - <td><p id="vds_distributor_updates_failures_total">vds.distributor.updates.failures.total</p></td> - <td>operation</td> - <td>rate</td> - <td>Sum of all failures</td> - </tr> - <tr> - <td><p id="vds_distributor_updates_failures_notfound">vds.distributor.updates.failures.notfound</p></td> - <td>operation</td> - <td>rate</td> - <td>The number of operations that failed because the document did not exist</td> - </tr> - <tr> - <td><p id="vds_distributor_updates_failures_test_and_set_failed">vds.distributor.updates.failures.test_and_set_failed</p></td> - <td>operation</td> - <td>rate</td> - <td>The number of mutating operations that failed because they specified a test-and-set condition that did not match the existing document</td> - </tr> - <tr> - <td><p id="vds_distributor_updates_failures_concurrent_mutations">vds.distributor.updates.failures.concurrent_mutations</p></td> - <td>operation</td> - <td>rate</td> - <td>The number of operations that were transiently failed due to a mutating operation already being in progress for its document ID</td> - </tr> - <tr> - <td><p id="vds_distributor_updates_diverging_timestamp_updates">vds.distributor.updates.diverging_timestamp_updates</p></td> - <td>operation</td> - <td>rate</td> - <td>Number of updates that report they were performed against divergent version timestamps on different replicas</td> - </tr> - <tr> - <td><p id="vds_distributor_removelocations_ok">vds.distributor.removelocations.ok</p></td> - <td>operation</td> - <td>rate</td> - <td>The number of successful removelocations operations performed</td> - </tr> - <tr> - <td><p id="vds_distributor_removelocations_failures_total">vds.distributor.removelocations.failures.total</p></td> - <td>operation</td> - <td>rate</td> - <td>Sum of all failures</td> - </tr> - <tr> - <td><p id="vds_distributor_gets_latency">vds.distributor.gets.latency</p></td> - <td>millisecond</td> - <td>count, max, sum</td> - <td>The average latency of gets operations</td> - </tr> - <tr> - <td><p id="vds_distributor_gets_ok">vds.distributor.gets.ok</p></td> - <td>operation</td> - <td>rate</td> - <td>The number of successful gets operations performed</td> - </tr> - <tr> - <td><p id="vds_distributor_gets_failures_total">vds.distributor.gets.failures.total</p></td> - <td>operation</td> - <td>rate</td> - <td>Sum of all failures</td> - </tr> - <tr> - <td><p id="vds_distributor_gets_failures_notfound">vds.distributor.gets.failures.notfound</p></td> - <td>operation</td> - <td>rate</td> - <td>The number of operations that failed because the document did not exist</td> - </tr> - <tr> - <td><p id="vds_distributor_visitor_latency">vds.distributor.visitor.latency</p></td> - <td>millisecond</td> - <td>count, max, sum</td> - <td>The average latency of visitor operations</td> - </tr> - <tr> - <td><p id="vds_distributor_visitor_ok">vds.distributor.visitor.ok</p></td> - <td>operation</td> - <td>rate</td> - <td>The number of successful visitor operations performed</td> - </tr> - <tr> - <td><p id="vds_distributor_visitor_failures_total">vds.distributor.visitor.failures.total</p></td> - <td>operation</td> - <td>rate</td> - <td>Sum of all failures</td> - </tr> - <tr> - <td><p id="vds_distributor_visitor_failures_notready">vds.distributor.visitor.failures.notready</p></td> - <td>operation</td> - <td>rate</td> - <td>The number of operations discarded because distributor was not ready</td> - </tr> - <tr> - <td><p id="vds_distributor_visitor_failures_notconnected">vds.distributor.visitor.failures.notconnected</p></td> - <td>operation</td> - <td>rate</td> - <td>The number of operations discarded because there were no available storage nodes to send to</td> - </tr> - <tr> - <td><p id="vds_distributor_visitor_failures_wrongdistributor">vds.distributor.visitor.failures.wrongdistributor</p></td> - <td>operation</td> - <td>rate</td> - <td>The number of operations discarded because they were sent to the wrong distributor</td> - </tr> - <tr> - <td><p id="vds_distributor_visitor_failures_safe_time_not_reached">vds.distributor.visitor.failures.safe_time_not_reached</p></td> - <td>operation</td> - <td>rate</td> - <td>The number of operations that were transiently failed due to them arriving before the safe time point for bucket ownership handovers has passed</td> - </tr> - <tr> - <td><p id="vds_distributor_visitor_failures_storagefailure">vds.distributor.visitor.failures.storagefailure</p></td> - <td>operation</td> - <td>rate</td> - <td>The number of operations that failed in storage</td> - </tr> - <tr> - <td><p id="vds_distributor_visitor_failures_timeout">vds.distributor.visitor.failures.timeout</p></td> - <td>operation</td> - <td>rate</td> - <td>The number of operations that failed because the operation timed out towards storage</td> - </tr> - <tr> - <td><p id="vds_distributor_visitor_failures_busy">vds.distributor.visitor.failures.busy</p></td> - <td>operation</td> - <td>rate</td> - <td>The number of messages from storage that failed because the storage node was busy</td> - </tr> - <tr> - <td><p id="vds_distributor_visitor_failures_inconsistent_bucket">vds.distributor.visitor.failures.inconsistent_bucket</p></td> - <td>operation</td> - <td>rate</td> - <td>The number of operations failed due to buckets being in an inconsistent state or not found</td> - </tr> - <tr> - <td><p id="vds_distributor_visitor_failures_notfound">vds.distributor.visitor.failures.notfound</p></td> - <td>operation</td> - <td>rate</td> - <td>The number of operations that failed because the document did not exist</td> - </tr> - <tr> - <td><p id="vds_distributor_docsstored">vds.distributor.docsstored</p></td> - <td>document</td> - <td>average</td> - <td>Number of documents stored in all buckets controlled by this distributor</td> - </tr> - <tr> - <td><p id="vds_distributor_bytesstored">vds.distributor.bytesstored</p></td> - <td>byte</td> - <td>average</td> - <td>Number of bytes stored in all buckets controlled by this distributor</td> - </tr> - <tr> - <td><p id="vds_bouncer_clock_skew_aborts">vds.bouncer.clock_skew_aborts</p></td> - <td>operation</td> - <td>count</td> - <td>Number of client operations that were aborted due to clock skew between sender and receiver exceeding acceptable range</td> - </tr> - </tbody> -</table> - -<h2 id="logd-metrics">Logd Metrics</h2> -<table class="table"> - <thead> - <tr><th>Name</th><th>Unit</th><th>Suffixes</th><th>Description</th></tr> - </thead> - <tbody> - <tr> - <td><p id="logd_processed_lines">logd.processed.lines</p></td> - <td>item</td> - <td>count</td> - <td>Number of log lines processed</td> - </tr> - </tbody> -</table> - -<h2 id="nodeadmin-metrics">NodeAdmin Metrics</h2> -<table class="table"> - <thead> - <tr><th>Name</th><th>Unit</th><th>Suffixes</th><th>Description</th></tr> - </thead> - <tbody> - <tr> - <td><p id="endpoint_certificate_expiry_seconds">endpoint.certificate.expiry.seconds</p></td> - <td>second</td> - <td>N/A</td> - <td>Time until node endpoint certificate expires</td> - </tr> - <tr> - <td><p id="node-certificate_expiry_seconds">node-certificate.expiry.seconds</p></td> - <td>second</td> - <td>N/A</td> - <td>Time until node certificate expires</td> - </tr> - </tbody> -</table> - -<h2 id="searchnode-metrics">SearchNode Metrics</h2> -<table class="table"> - <thead> - <tr><th>Name</th><th>Unit</th><th>Suffixes</th><th>Description</th></tr> - </thead> - <tbody> - <tr> - <td><p id="content_proton_config_generation">content.proton.config.generation</p></td> - <td>version</td> - <td>last</td> - <td>The oldest config generation used by this search node</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_documents_total">content.proton.documentdb.documents.total</p></td> - <td>document</td> - <td>last, max</td> - <td>The total number of documents in this documents db (ready + not-ready)</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_documents_ready">content.proton.documentdb.documents.ready</p></td> - <td>document</td> - <td>last, max</td> - <td>The number of ready documents in this document db</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_documents_active">content.proton.documentdb.documents.active</p></td> - <td>document</td> - <td>last, max</td> - <td>The number of active / searchable documents in this document db</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_documents_removed">content.proton.documentdb.documents.removed</p></td> - <td>document</td> - <td>last, max</td> - <td>The number of removed documents in this document db</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_index_docs_in_memory">content.proton.documentdb.index.docs_in_memory</p></td> - <td>document</td> - <td>last, max</td> - <td>Number of documents in memory index</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_disk_usage">content.proton.documentdb.disk_usage</p></td> - <td>byte</td> - <td>last</td> - <td>The total disk usage (in bytes) for this document db</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_memory_usage_allocated_bytes">content.proton.documentdb.memory_usage.allocated_bytes</p></td> - <td>byte</td> - <td>max</td> - <td>The number of allocated bytes</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_heart_beat_age">content.proton.documentdb.heart_beat_age</p></td> - <td>second</td> - <td>last, min</td> - <td>How long ago (in seconds) heart beat maintenance job was run</td> - </tr> - <tr> - <td><p id="content_proton_docsum_docs">content.proton.docsum.docs</p></td> - <td>document</td> - <td>rate</td> - <td>Total docsums returned</td> - </tr> - <tr> - <td><p id="content_proton_docsum_latency">content.proton.docsum.latency</p></td> - <td>millisecond</td> - <td>count, max, sum</td> - <td>Docsum request latency</td> - </tr> - <tr> - <td><p id="content_proton_search_protocol_query_latency">content.proton.search_protocol.query.latency</p></td> - <td>second</td> - <td>count, max, sum</td> - <td>Query request latency (seconds)</td> - </tr> - <tr> - <td><p id="content_proton_search_protocol_query_request_size">content.proton.search_protocol.query.request_size</p></td> - <td>byte</td> - <td>count, max, sum</td> - <td>Query request size (network bytes)</td> - </tr> - <tr> - <td><p id="content_proton_search_protocol_query_reply_size">content.proton.search_protocol.query.reply_size</p></td> - <td>byte</td> - <td>count, max, sum</td> - <td>Query reply size (network bytes)</td> - </tr> - <tr> - <td><p id="content_proton_search_protocol_docsum_latency">content.proton.search_protocol.docsum.latency</p></td> - <td>second</td> - <td>average, count, max, sum</td> - <td>Docsum request latency (seconds)</td> - </tr> - <tr> - <td><p id="content_proton_search_protocol_docsum_request_size">content.proton.search_protocol.docsum.request_size</p></td> - <td>byte</td> - <td>count, max, sum</td> - <td>Docsum request size (network bytes)</td> - </tr> - <tr> - <td><p id="content_proton_search_protocol_docsum_reply_size">content.proton.search_protocol.docsum.reply_size</p></td> - <td>byte</td> - <td>count, max, sum</td> - <td>Docsum reply size (network bytes)</td> - </tr> - <tr> - <td><p id="content_proton_search_protocol_docsum_requested_documents">content.proton.search_protocol.docsum.requested_documents</p></td> - <td>document</td> - <td>count, max, sum</td> - <td>Total requested document summaries</td> - </tr> - <tr> - <td><p id="content_proton_executor_proton_queuesize">content.proton.executor.proton.queuesize</p></td> - <td>task</td> - <td>count, max, sum</td> - <td>Size of executor proton task queue</td> - </tr> - <tr> - <td><p id="content_proton_executor_proton_accepted">content.proton.executor.proton.accepted</p></td> - <td>task</td> - <td>rate</td> - <td>Number of executor proton accepted tasks</td> - </tr> - <tr> - <td><p id="content_proton_executor_proton_wakeups">content.proton.executor.proton.wakeups</p></td> - <td>wakeup</td> - <td>rate</td> - <td>Number of times an executor proton worker thread has been woken up</td> - </tr> - <tr> - <td><p id="content_proton_executor_proton_utilization">content.proton.executor.proton.utilization</p></td> - <td>fraction</td> - <td>count, max, sum</td> - <td>Ratio of time the executor proton worker threads has been active</td> - </tr> - <tr> - <td><p id="content_proton_executor_flush_queuesize">content.proton.executor.flush.queuesize</p></td> - <td>task</td> - <td>count, max, sum</td> - <td>Size of executor flush task queue</td> - </tr> - <tr> - <td><p id="content_proton_executor_flush_accepted">content.proton.executor.flush.accepted</p></td> - <td>task</td> - <td>rate</td> - <td>Number of accepted executor flush tasks</td> - </tr> - <tr> - <td><p id="content_proton_executor_flush_wakeups">content.proton.executor.flush.wakeups</p></td> - <td>wakeup</td> - <td>rate</td> - <td>Number of times an executor flush worker thread has been woken up</td> - </tr> - <tr> - <td><p id="content_proton_executor_flush_utilization">content.proton.executor.flush.utilization</p></td> - <td>fraction</td> - <td>count, max, sum</td> - <td>Ratio of time the executor flush worker threads has been active</td> - </tr> - <tr> - <td><p id="content_proton_executor_match_queuesize">content.proton.executor.match.queuesize</p></td> - <td>task</td> - <td>count, max, sum</td> - <td>Size of executor match task queue</td> - </tr> - <tr> - <td><p id="content_proton_executor_match_accepted">content.proton.executor.match.accepted</p></td> - <td>task</td> - <td>rate</td> - <td>Number of accepted executor match tasks</td> - </tr> - <tr> - <td><p id="content_proton_executor_match_wakeups">content.proton.executor.match.wakeups</p></td> - <td>wakeup</td> - <td>rate</td> - <td>Number of times an executor match worker thread has been woken up</td> - </tr> - <tr> - <td><p id="content_proton_executor_match_utilization">content.proton.executor.match.utilization</p></td> - <td>fraction</td> - <td>count, max, sum</td> - <td>Ratio of time the executor match worker threads has been active</td> - </tr> - <tr> - <td><p id="content_proton_executor_docsum_queuesize">content.proton.executor.docsum.queuesize</p></td> - <td>task</td> - <td>count, max, sum</td> - <td>Size of executor docsum task queue</td> - </tr> - <tr> - <td><p id="content_proton_executor_docsum_accepted">content.proton.executor.docsum.accepted</p></td> - <td>task</td> - <td>rate</td> - <td>Number of executor accepted docsum tasks</td> - </tr> - <tr> - <td><p id="content_proton_executor_docsum_wakeups">content.proton.executor.docsum.wakeups</p></td> - <td>wakeup</td> - <td>rate</td> - <td>Number of times an executor docsum worker thread has been woken up</td> - </tr> - <tr> - <td><p id="content_proton_executor_docsum_utilization">content.proton.executor.docsum.utilization</p></td> - <td>fraction</td> - <td>count, max, sum</td> - <td>Ratio of time the executor docsum worker threads has been active</td> - </tr> - <tr> - <td><p id="content_proton_executor_shared_queuesize">content.proton.executor.shared.queuesize</p></td> - <td>task</td> - <td>count, max, sum</td> - <td>Size of executor shared task queue</td> - </tr> - <tr> - <td><p id="content_proton_executor_shared_accepted">content.proton.executor.shared.accepted</p></td> - <td>task</td> - <td>rate</td> - <td>Number of executor shared accepted tasks</td> - </tr> - <tr> - <td><p id="content_proton_executor_shared_wakeups">content.proton.executor.shared.wakeups</p></td> - <td>wakeup</td> - <td>rate</td> - <td>Number of times an executor shared worker thread has been woken up</td> - </tr> - <tr> - <td><p id="content_proton_executor_shared_utilization">content.proton.executor.shared.utilization</p></td> - <td>fraction</td> - <td>count, max, sum</td> - <td>Ratio of time the executor shared worker threads has been active</td> - </tr> - <tr> - <td><p id="content_proton_executor_warmup_queuesize">content.proton.executor.warmup.queuesize</p></td> - <td>task</td> - <td>count, max, sum</td> - <td>Size of executor warmup task queue</td> - </tr> - <tr> - <td><p id="content_proton_executor_warmup_accepted">content.proton.executor.warmup.accepted</p></td> - <td>task</td> - <td>rate</td> - <td>Number of accepted executor warmup tasks</td> - </tr> - <tr> - <td><p id="content_proton_executor_warmup_wakeups">content.proton.executor.warmup.wakeups</p></td> - <td>wakeup</td> - <td>rate</td> - <td>Number of times a warmup executor worker thread has been woken up</td> - </tr> - <tr> - <td><p id="content_proton_executor_warmup_utilization">content.proton.executor.warmup.utilization</p></td> - <td>fraction</td> - <td>count, max, sum</td> - <td>Ratio of time the executor warmup worker threads has been active</td> - </tr> - <tr> - <td><p id="content_proton_executor_field_writer_queuesize">content.proton.executor.field_writer.queuesize</p></td> - <td>task</td> - <td>count, max, sum</td> - <td>Size of executor field writer task queue</td> - </tr> - <tr> - <td><p id="content_proton_executor_field_writer_accepted">content.proton.executor.field_writer.accepted</p></td> - <td>task</td> - <td>rate</td> - <td>Number of accepted executor field writer tasks</td> - </tr> - <tr> - <td><p id="content_proton_executor_field_writer_wakeups">content.proton.executor.field_writer.wakeups</p></td> - <td>wakeup</td> - <td>rate</td> - <td>Number of times an executor field writer worker thread has been woken up</td> - </tr> - <tr> - <td><p id="content_proton_executor_field_writer_utilization">content.proton.executor.field_writer.utilization</p></td> - <td>fraction</td> - <td>count, max, sum</td> - <td>Ratio of time the executor fieldwriter worker threads has been active</td> - </tr> - <tr> - <td><p id="content_proton_executor_field_writer_saturation">content.proton.executor.field_writer.saturation</p></td> - <td>fraction</td> - <td>count, max, sum</td> - <td>Ratio indicating the max saturation of underlying worker threads. A higher saturation than utilization indicates a bottleneck in one of the worker threads.</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_job_total">content.proton.documentdb.job.total</p></td> - <td>fraction</td> - <td>average</td> - <td>The job load average total of all job metrics</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_job_attribute_flush">content.proton.documentdb.job.attribute_flush</p></td> - <td>fraction</td> - <td>average</td> - <td>Flushing of attribute vector(s) to disk</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_job_memory_index_flush">content.proton.documentdb.job.memory_index_flush</p></td> - <td>fraction</td> - <td>average</td> - <td>Flushing of memory index to disk</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_job_disk_index_fusion">content.proton.documentdb.job.disk_index_fusion</p></td> - <td>fraction</td> - <td>average</td> - <td>Fusion of disk indexes</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_job_document_store_flush">content.proton.documentdb.job.document_store_flush</p></td> - <td>fraction</td> - <td>average</td> - <td>Flushing of document store to disk</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_job_document_store_compact">content.proton.documentdb.job.document_store_compact</p></td> - <td>fraction</td> - <td>average</td> - <td>Compaction of document store on disk</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_job_bucket_move">content.proton.documentdb.job.bucket_move</p></td> - <td>fraction</td> - <td>average</td> - <td>Moving of buckets between 'ready' and 'notready' sub databases</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_job_lid_space_compact">content.proton.documentdb.job.lid_space_compact</p></td> - <td>fraction</td> - <td>average</td> - <td>Compaction of lid space in document meta store and attribute vectors</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_job_removed_documents_prune">content.proton.documentdb.job.removed_documents_prune</p></td> - <td>fraction</td> - <td>average</td> - <td>Pruning of removed documents in 'removed' sub database</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_threading_service_master_queuesize">content.proton.documentdb.threading_service.master.queuesize</p></td> - <td>task</td> - <td>count, max, sum</td> - <td>Size of threading service master task queue</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_threading_service_master_accepted">content.proton.documentdb.threading_service.master.accepted</p></td> - <td>task</td> - <td>rate</td> - <td>Number of accepted threading service master tasks</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_threading_service_master_wakeups">content.proton.documentdb.threading_service.master.wakeups</p></td> - <td>wakeup</td> - <td>rate</td> - <td>Number of times a threading service master worker thread has been woken up</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_threading_service_master_utilization">content.proton.documentdb.threading_service.master.utilization</p></td> - <td>fraction</td> - <td>count, max, sum</td> - <td>Ratio of time the threading service master worker threads has been active</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_threading_service_index_queuesize">content.proton.documentdb.threading_service.index.queuesize</p></td> - <td>task</td> - <td>count, max, sum</td> - <td>Size of threading service index task queue</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_threading_service_index_accepted">content.proton.documentdb.threading_service.index.accepted</p></td> - <td>task</td> - <td>rate</td> - <td>Number of accepted threading service index tasks</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_threading_service_index_wakeups">content.proton.documentdb.threading_service.index.wakeups</p></td> - <td>wakeup</td> - <td>rate</td> - <td>Number of times a threading service index worker thread has been woken up</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_threading_service_index_utilization">content.proton.documentdb.threading_service.index.utilization</p></td> - <td>fraction</td> - <td>count, max, sum</td> - <td>Ratio of time the threading service index worker threads has been active</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_threading_service_summary_queuesize">content.proton.documentdb.threading_service.summary.queuesize</p></td> - <td>task</td> - <td>count, max, sum</td> - <td>Size of threading service summary task queue</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_threading_service_summary_accepted">content.proton.documentdb.threading_service.summary.accepted</p></td> - <td>task</td> - <td>rate</td> - <td>Number of accepted threading service summary tasks</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_threading_service_summary_wakeups">content.proton.documentdb.threading_service.summary.wakeups</p></td> - <td>wakeup</td> - <td>rate</td> - <td>Number of times a threading service summary worker thread has been woken up</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_threading_service_summary_utilization">content.proton.documentdb.threading_service.summary.utilization</p></td> - <td>fraction</td> - <td>count, max, sum</td> - <td>Ratio of time the threading service summary worker threads has been active</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_ready_lid_space_lid_bloat_factor">content.proton.documentdb.ready.lid_space.lid_bloat_factor</p></td> - <td>fraction</td> - <td>average</td> - <td>The bloat factor of this lid space, indicating the total amount of holes in the allocated lid space ((lid_limit - used_lids) / lid_limit)</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_ready_lid_space_lid_fragmentation_factor">content.proton.documentdb.ready.lid_space.lid_fragmentation_factor</p></td> - <td>fraction</td> - <td>average</td> - <td>The fragmentation factor of this lid space, indicating the amount of holes in the currently used part of the lid space ((highest_used_lid - used_lids) / highest_used_lid)</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_ready_lid_space_lid_limit">content.proton.documentdb.ready.lid_space.lid_limit</p></td> - <td>documentid</td> - <td>last, max</td> - <td>The size of the allocated lid space</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_ready_lid_space_highest_used_lid">content.proton.documentdb.ready.lid_space.highest_used_lid</p></td> - <td>documentid</td> - <td>last, max</td> - <td>The highest used lid</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_ready_lid_space_used_lids">content.proton.documentdb.ready.lid_space.used_lids</p></td> - <td>documentid</td> - <td>last, max</td> - <td>The number of lids used</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_notready_lid_space_lid_bloat_factor">content.proton.documentdb.notready.lid_space.lid_bloat_factor</p></td> - <td>fraction</td> - <td>average</td> - <td>The bloat factor of this lid space, indicating the total amount of holes in the allocated lid space ((lid_limit - used_lids) / lid_limit)</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_notready_lid_space_lid_fragmentation_factor">content.proton.documentdb.notready.lid_space.lid_fragmentation_factor</p></td> - <td>fraction</td> - <td>average</td> - <td>The fragmentation factor of this lid space, indicating the amount of holes in the currently used part of the lid space ((highest_used_lid - used_lids) / highest_used_lid)</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_notready_lid_space_lid_limit">content.proton.documentdb.notready.lid_space.lid_limit</p></td> - <td>documentid</td> - <td>last, max</td> - <td>The size of the allocated lid space</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_notready_lid_space_highest_used_lid">content.proton.documentdb.notready.lid_space.highest_used_lid</p></td> - <td>documentid</td> - <td>last, max</td> - <td>The highest used lid</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_notready_lid_space_used_lids">content.proton.documentdb.notready.lid_space.used_lids</p></td> - <td>documentid</td> - <td>last, max</td> - <td>The number of lids used</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_removed_lid_space_lid_bloat_factor">content.proton.documentdb.removed.lid_space.lid_bloat_factor</p></td> - <td>fraction</td> - <td>average</td> - <td>The bloat factor of this lid space, indicating the total amount of holes in the allocated lid space ((lid_limit - used_lids) / lid_limit)</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_removed_lid_space_lid_fragmentation_factor">content.proton.documentdb.removed.lid_space.lid_fragmentation_factor</p></td> - <td>fraction</td> - <td>average</td> - <td>The fragmentation factor of this lid space, indicating the amount of holes in the currently used part of the lid space ((highest_used_lid - used_lids) / highest_used_lid)</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_removed_lid_space_lid_limit">content.proton.documentdb.removed.lid_space.lid_limit</p></td> - <td>documentid</td> - <td>last, max</td> - <td>The size of the allocated lid space</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_removed_lid_space_highest_used_lid">content.proton.documentdb.removed.lid_space.highest_used_lid</p></td> - <td>documentid</td> - <td>last, max</td> - <td>The highest used lid</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_removed_lid_space_used_lids">content.proton.documentdb.removed.lid_space.used_lids</p></td> - <td>documentid</td> - <td>last, max</td> - <td>The number of lids used</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_bucket_move_buckets_pending">content.proton.documentdb.bucket_move.buckets_pending</p></td> - <td>bucket</td> - <td>last, max, sum</td> - <td>The number of buckets left to move</td> - </tr> - <tr> - <td><p id="content_proton_resource_usage_disk">content.proton.resource_usage.disk</p></td> - <td>fraction</td> - <td>average</td> - <td>The relative amount of disk used by this content node (transient usage not included, value in the range [0, 1]). Same value as reported to the cluster controller</td> - </tr> - <tr> - <td><p id="content_proton_resource_usage_disk_usage_total">content.proton.resource_usage.disk_usage.total</p></td> - <td>fraction</td> - <td>max</td> - <td>The total relative amount of disk used by this content node (value in the range [0, 1])</td> - </tr> - <tr> - <td><p id="content_proton_resource_usage_disk_usage_total_utilization">content.proton.resource_usage.disk_usage.total_utilization</p></td> - <td>fraction</td> - <td>max</td> - <td>The relative amount of disk used compared to the content node disk resource limit</td> - </tr> - <tr> - <td><p id="content_proton_resource_usage_disk_usage_transient">content.proton.resource_usage.disk_usage.transient</p></td> - <td>fraction</td> - <td>max</td> - <td>The relative amount of transient disk used by this content node (value in the range [0, 1])</td> - </tr> - <tr> - <td><p id="content_proton_resource_usage_memory">content.proton.resource_usage.memory</p></td> - <td>fraction</td> - <td>average</td> - <td>The relative amount of memory used by this content node (transient usage not included, value in the range [0, 1]). Same value as reported to the cluster controller</td> - </tr> - <tr> - <td><p id="content_proton_resource_usage_memory_usage_total">content.proton.resource_usage.memory_usage.total</p></td> - <td>fraction</td> - <td>max</td> - <td>The total relative amount of memory used by this content node (value in the range [0, 1])</td> - </tr> - <tr> - <td><p id="content_proton_resource_usage_memory_usage_total_utilization">content.proton.resource_usage.memory_usage.total_utilization</p></td> - <td>fraction</td> - <td>max</td> - <td>The relative amount of memory used compared to the content node memory resource limit</td> - </tr> - <tr> - <td><p id="content_proton_resource_usage_memory_usage_transient">content.proton.resource_usage.memory_usage.transient</p></td> - <td>fraction</td> - <td>max</td> - <td>The relative amount of transient memory used by this content node (value in the range [0, 1])</td> - </tr> - <tr> - <td><p id="content_proton_resource_usage_memory_mappings">content.proton.resource_usage.memory_mappings</p></td> - <td>file</td> - <td>max</td> - <td>The number of memory mapped files</td> - </tr> - <tr> - <td><p id="content_proton_resource_usage_open_file_descriptors">content.proton.resource_usage.open_file_descriptors</p></td> - <td>file</td> - <td>max</td> - <td>The number of open files</td> - </tr> - <tr> - <td><p id="content_proton_resource_usage_feeding_blocked">content.proton.resource_usage.feeding_blocked</p></td> - <td>binary</td> - <td>last, max</td> - <td>Whether feeding is blocked due to resource limits being reached (value is either 0 or 1)</td> - </tr> - <tr> - <td><p id="content_proton_resource_usage_malloc_arena">content.proton.resource_usage.malloc_arena</p></td> - <td>byte</td> - <td>max</td> - <td>Size of malloc arena</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_attribute_resource_usage_address_space">content.proton.documentdb.attribute.resource_usage.address_space</p></td> - <td>fraction</td> - <td>max</td> - <td>The max relative address space used among components in all attribute vectors in this document db (value in the range [0, 1])</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_attribute_resource_usage_feeding_blocked">content.proton.documentdb.attribute.resource_usage.feeding_blocked</p></td> - <td>binary</td> - <td>max</td> - <td>Whether feeding is blocked due to attribute resource limits being reached (value is either 0 or 1)</td> - </tr> - <tr> - <td><p id="content_proton_resource_usage_cpu_util_setup">content.proton.resource_usage.cpu_util.setup</p></td> - <td>fraction</td> - <td>count, max, sum</td> - <td>cpu used by system init and (re-)configuration</td> - </tr> - <tr> - <td><p id="content_proton_resource_usage_cpu_util_read">content.proton.resource_usage.cpu_util.read</p></td> - <td>fraction</td> - <td>count, max, sum</td> - <td>cpu used by reading data from the system</td> - </tr> - <tr> - <td><p id="content_proton_resource_usage_cpu_util_write">content.proton.resource_usage.cpu_util.write</p></td> - <td>fraction</td> - <td>count, max, sum</td> - <td>cpu used by writing data to the system</td> - </tr> - <tr> - <td><p id="content_proton_resource_usage_cpu_util_compact">content.proton.resource_usage.cpu_util.compact</p></td> - <td>fraction</td> - <td>count, max, sum</td> - <td>cpu used by internal data re-structuring</td> - </tr> - <tr> - <td><p id="content_proton_resource_usage_cpu_util_other">content.proton.resource_usage.cpu_util.other</p></td> - <td>fraction</td> - <td>count, max, sum</td> - <td>cpu used by work not classified as a specific category</td> - </tr> - <tr> - <td><p id="content_proton_transactionlog_entries">content.proton.transactionlog.entries</p></td> - <td>record</td> - <td>average</td> - <td>The current number of entries in the transaction log</td> - </tr> - <tr> - <td><p id="content_proton_transactionlog_disk_usage">content.proton.transactionlog.disk_usage</p></td> - <td>byte</td> - <td>average</td> - <td>The disk usage (in bytes) of the transaction log</td> - </tr> - <tr> - <td><p id="content_proton_transactionlog_replay_time">content.proton.transactionlog.replay_time</p></td> - <td>second</td> - <td>last, max</td> - <td>The replay time (in seconds) of the transaction log during start-up</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_ready_document_store_disk_usage">content.proton.documentdb.ready.document_store.disk_usage</p></td> - <td>byte</td> - <td>average</td> - <td>Disk space usage in bytes</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_ready_document_store_disk_bloat">content.proton.documentdb.ready.document_store.disk_bloat</p></td> - <td>byte</td> - <td>average</td> - <td>Disk space bloat in bytes</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_ready_document_store_max_bucket_spread">content.proton.documentdb.ready.document_store.max_bucket_spread</p></td> - <td>fraction</td> - <td>average</td> - <td>Max bucket spread in underlying files (sum(unique buckets in each chunk)/unique buckets in file)</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_ready_document_store_memory_usage_allocated_bytes">content.proton.documentdb.ready.document_store.memory_usage.allocated_bytes</p></td> - <td>byte</td> - <td>average</td> - <td>The number of allocated bytes</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_ready_document_store_memory_usage_used_bytes">content.proton.documentdb.ready.document_store.memory_usage.used_bytes</p></td> - <td>byte</td> - <td>average</td> - <td>The number of used bytes (<= allocated_bytes)</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_ready_document_store_memory_usage_onhold_bytes">content.proton.documentdb.ready.document_store.memory_usage.onhold_bytes</p></td> - <td>byte</td> - <td>average</td> - <td>The number of bytes on hold</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_notready_document_store_disk_usage">content.proton.documentdb.notready.document_store.disk_usage</p></td> - <td>byte</td> - <td>average</td> - <td>Disk space usage in bytes</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_notready_document_store_disk_bloat">content.proton.documentdb.notready.document_store.disk_bloat</p></td> - <td>byte</td> - <td>average</td> - <td>Disk space bloat in bytes</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_notready_document_store_max_bucket_spread">content.proton.documentdb.notready.document_store.max_bucket_spread</p></td> - <td>fraction</td> - <td>average</td> - <td>Max bucket spread in underlying files (sum(unique buckets in each chunk)/unique buckets in file)</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_notready_document_store_memory_usage_allocated_bytes">content.proton.documentdb.notready.document_store.memory_usage.allocated_bytes</p></td> - <td>byte</td> - <td>average</td> - <td>The number of allocated bytes</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_notready_document_store_memory_usage_used_bytes">content.proton.documentdb.notready.document_store.memory_usage.used_bytes</p></td> - <td>byte</td> - <td>average</td> - <td>The number of used bytes (<= allocated_bytes)</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_notready_document_store_memory_usage_dead_bytes">content.proton.documentdb.notready.document_store.memory_usage.dead_bytes</p></td> - <td>byte</td> - <td>average</td> - <td>The number of dead bytes (<= used_bytes)</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_notready_document_store_memory_usage_onhold_bytes">content.proton.documentdb.notready.document_store.memory_usage.onhold_bytes</p></td> - <td>byte</td> - <td>average</td> - <td>The number of bytes on hold</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_removed_document_store_disk_usage">content.proton.documentdb.removed.document_store.disk_usage</p></td> - <td>byte</td> - <td>average</td> - <td>Disk space usage in bytes</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_removed_document_store_disk_bloat">content.proton.documentdb.removed.document_store.disk_bloat</p></td> - <td>byte</td> - <td>average</td> - <td>Disk space bloat in bytes</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_removed_document_store_max_bucket_spread">content.proton.documentdb.removed.document_store.max_bucket_spread</p></td> - <td>fraction</td> - <td>average</td> - <td>Max bucket spread in underlying files (sum(unique buckets in each chunk)/unique buckets in file)</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_removed_document_store_memory_usage_allocated_bytes">content.proton.documentdb.removed.document_store.memory_usage.allocated_bytes</p></td> - <td>byte</td> - <td>average</td> - <td>The number of allocated bytes</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_removed_document_store_memory_usage_used_bytes">content.proton.documentdb.removed.document_store.memory_usage.used_bytes</p></td> - <td>byte</td> - <td>average</td> - <td>The number of used bytes (<= allocated_bytes)</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_removed_document_store_memory_usage_dead_bytes">content.proton.documentdb.removed.document_store.memory_usage.dead_bytes</p></td> - <td>byte</td> - <td>average</td> - <td>The number of dead bytes (<= used_bytes)</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_removed_document_store_memory_usage_onhold_bytes">content.proton.documentdb.removed.document_store.memory_usage.onhold_bytes</p></td> - <td>byte</td> - <td>average</td> - <td>The number of bytes on hold</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_ready_document_store_cache_memory_usage">content.proton.documentdb.ready.document_store.cache.memory_usage</p></td> - <td>byte</td> - <td>average</td> - <td>Memory usage of the cache (in bytes)</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_ready_document_store_cache_hit_rate">content.proton.documentdb.ready.document_store.cache.hit_rate</p></td> - <td>fraction</td> - <td>average</td> - <td>Rate of hits in the cache compared to number of lookups</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_ready_document_store_cache_lookups">content.proton.documentdb.ready.document_store.cache.lookups</p></td> - <td>operation</td> - <td>rate</td> - <td>Number of lookups in the cache (hits + misses)</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_ready_document_store_cache_invalidations">content.proton.documentdb.ready.document_store.cache.invalidations</p></td> - <td>operation</td> - <td>rate</td> - <td>Number of invalidations (erased elements) in the cache. </td> - </tr> - <tr> - <td><p id="content_proton_documentdb_notready_document_store_cache_memory_usage">content.proton.documentdb.notready.document_store.cache.memory_usage</p></td> - <td>byte</td> - <td>average</td> - <td>Memory usage of the cache (in bytes)</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_notready_document_store_cache_hit_rate">content.proton.documentdb.notready.document_store.cache.hit_rate</p></td> - <td>fraction</td> - <td>average</td> - <td>Rate of hits in the cache compared to number of lookups</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_notready_document_store_cache_lookups">content.proton.documentdb.notready.document_store.cache.lookups</p></td> - <td>operation</td> - <td>rate</td> - <td>Number of lookups in the cache (hits + misses)</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_notready_document_store_cache_invalidations">content.proton.documentdb.notready.document_store.cache.invalidations</p></td> - <td>operation</td> - <td>rate</td> - <td>Number of invalidations (erased elements) in the cache. </td> - </tr> - <tr> - <td><p id="content_proton_documentdb_ready_attribute_memory_usage_allocated_bytes">content.proton.documentdb.ready.attribute.memory_usage.allocated_bytes</p></td> - <td>byte</td> - <td>average</td> - <td>The number of allocated bytes</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_ready_attribute_memory_usage_used_bytes">content.proton.documentdb.ready.attribute.memory_usage.used_bytes</p></td> - <td>byte</td> - <td>average</td> - <td>The number of used bytes (<= allocated_bytes)</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_ready_attribute_memory_usage_dead_bytes">content.proton.documentdb.ready.attribute.memory_usage.dead_bytes</p></td> - <td>byte</td> - <td>average</td> - <td>The number of dead bytes (<= used_bytes)</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_ready_attribute_memory_usage_onhold_bytes">content.proton.documentdb.ready.attribute.memory_usage.onhold_bytes</p></td> - <td>byte</td> - <td>average</td> - <td>The number of bytes on hold</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_ready_attribute_disk_usage">content.proton.documentdb.ready.attribute.disk_usage</p></td> - <td>byte</td> - <td>average</td> - <td>Disk space usage (in bytes) of the flushed snapshot of this attribute for this document type</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_notready_attribute_memory_usage_allocated_bytes">content.proton.documentdb.notready.attribute.memory_usage.allocated_bytes</p></td> - <td>byte</td> - <td>average</td> - <td>The number of allocated bytes</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_notready_attribute_memory_usage_used_bytes">content.proton.documentdb.notready.attribute.memory_usage.used_bytes</p></td> - <td>byte</td> - <td>average</td> - <td>The number of used bytes (<= allocated_bytes)</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_notready_attribute_memory_usage_dead_bytes">content.proton.documentdb.notready.attribute.memory_usage.dead_bytes</p></td> - <td>byte</td> - <td>average</td> - <td>The number of dead bytes (<= used_bytes)</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_notready_attribute_memory_usage_onhold_bytes">content.proton.documentdb.notready.attribute.memory_usage.onhold_bytes</p></td> - <td>byte</td> - <td>average</td> - <td>The number of bytes on hold</td> - </tr> - <tr> - <td><p id="content_proton_index_cache_postinglist_memory_usage">content.proton.index.cache.postinglist.memory_usage</p></td> - <td>byte</td> - <td>average</td> - <td>Memory usage of the cache (in bytes). Contains disk index posting list files across all document types</td> - </tr> - <tr> - <td><p id="content_proton_index_cache_postinglist_hit_rate">content.proton.index.cache.postinglist.hit_rate</p></td> - <td>fraction</td> - <td>average</td> - <td>Rate of hits in the cache compared to number of lookups. Contains disk index posting list files across all document types</td> - </tr> - <tr> - <td><p id="content_proton_index_cache_postinglist_lookups">content.proton.index.cache.postinglist.lookups</p></td> - <td>operation</td> - <td>rate</td> - <td>Number of lookups in the cache (hits + misses). Contains disk index posting list files across all document types</td> - </tr> - <tr> - <td><p id="content_proton_index_cache_postinglist_invalidations">content.proton.index.cache.postinglist.invalidations</p></td> - <td>operation</td> - <td>rate</td> - <td>Number of invalidations (erased elements) in the cache. Contains disk index posting list files across all document types</td> - </tr> - <tr> - <td><p id="content_proton_index_cache_bitvector_memory_usage">content.proton.index.cache.bitvector.memory_usage</p></td> - <td>byte</td> - <td>average</td> - <td>Memory usage of the cache (in bytes). Contains disk index bitvector files across all document types</td> - </tr> - <tr> - <td><p id="content_proton_index_cache_bitvector_hit_rate">content.proton.index.cache.bitvector.hit_rate</p></td> - <td>fraction</td> - <td>average</td> - <td>Rate of hits in the cache compared to number of lookups. Contains disk index bitvector files across all document types</td> - </tr> - <tr> - <td><p id="content_proton_index_cache_bitvector_lookups">content.proton.index.cache.bitvector.lookups</p></td> - <td>operation</td> - <td>rate</td> - <td>Number of lookups in the cache (hits + misses). Contains disk index bitvector files across all document types</td> - </tr> - <tr> - <td><p id="content_proton_index_cache_bitvector_invalidations">content.proton.index.cache.bitvector.invalidations</p></td> - <td>operation</td> - <td>rate</td> - <td>Number of invalidations (erased elements) in the cache. Contains disk index bitvector files across all document types</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_index_memory_usage_allocated_bytes">content.proton.documentdb.index.memory_usage.allocated_bytes</p></td> - <td>byte</td> - <td>average</td> - <td>The number of allocated bytes for the memory index for this document type</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_index_memory_usage_used_bytes">content.proton.documentdb.index.memory_usage.used_bytes</p></td> - <td>byte</td> - <td>average</td> - <td>The number of used bytes (<= allocated_bytes) for the memory index for this document type</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_index_memory_usage_dead_bytes">content.proton.documentdb.index.memory_usage.dead_bytes</p></td> - <td>byte</td> - <td>average</td> - <td>The number of dead bytes (<= used_bytes) for the memory index for this document type</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_index_memory_usage_onhold_bytes">content.proton.documentdb.index.memory_usage.onhold_bytes</p></td> - <td>byte</td> - <td>average</td> - <td>The number of bytes on hold for the memory index for this document type</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_index_io_search_read_bytes">content.proton.documentdb.index.io.search.read_bytes</p></td> - <td>byte</td> - <td>count, sum</td> - <td>Bytes read from disk index posting list and bitvector files as part of search for this document type</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_index_io_search_cached_read_bytes">content.proton.documentdb.index.io.search.cached_read_bytes</p></td> - <td>byte</td> - <td>count, sum</td> - <td>Bytes read from cached disk index posting list and bitvector files as part of search for this document type</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_ready_index_disk_usage">content.proton.documentdb.ready.index.disk_usage</p></td> - <td>byte</td> - <td>average</td> - <td>Disk space usage (in bytes) of this index field in all disk indexes for this document type</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_matching_queries">content.proton.documentdb.matching.queries</p></td> - <td>query</td> - <td>rate</td> - <td>Number of queries executed</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_matching_soft_doomed_queries">content.proton.documentdb.matching.soft_doomed_queries</p></td> - <td>query</td> - <td>rate</td> - <td>Number of queries hitting the soft timeout</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_matching_query_latency">content.proton.documentdb.matching.query_latency</p></td> - <td>second</td> - <td>count, max, sum</td> - <td>Total average latency (sec) when matching and ranking a query</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_matching_query_setup_time">content.proton.documentdb.matching.query_setup_time</p></td> - <td>second</td> - <td>count, max, sum</td> - <td>Average time (sec) spent setting up and tearing down queries</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_matching_docs_matched">content.proton.documentdb.matching.docs_matched</p></td> - <td>document</td> - <td>count, rate</td> - <td>Number of documents matched</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_matching_rank_profile_queries">content.proton.documentdb.matching.rank_profile.queries</p></td> - <td>query</td> - <td>rate</td> - <td>Number of queries executed</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_matching_rank_profile_soft_doomed_queries">content.proton.documentdb.matching.rank_profile.soft_doomed_queries</p></td> - <td>query</td> - <td>rate</td> - <td>Number of queries hitting the soft timeout</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_matching_rank_profile_soft_doom_factor">content.proton.documentdb.matching.rank_profile.soft_doom_factor</p></td> - <td>fraction</td> - <td>count, max, min, sum</td> - <td>Factor used to compute soft-timeout</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_matching_rank_profile_query_latency">content.proton.documentdb.matching.rank_profile.query_latency</p></td> - <td>second</td> - <td>count, max, sum</td> - <td>Total average latency (sec) when matching and ranking a query</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_matching_rank_profile_query_setup_time">content.proton.documentdb.matching.rank_profile.query_setup_time</p></td> - <td>second</td> - <td>count, max, sum</td> - <td>Average time (sec) spent setting up and tearing down queries</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_matching_rank_profile_grouping_time">content.proton.documentdb.matching.rank_profile.grouping_time</p></td> - <td>second</td> - <td>count, max, sum</td> - <td>Average time (sec) spent on grouping</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_matching_rank_profile_rerank_time">content.proton.documentdb.matching.rank_profile.rerank_time</p></td> - <td>second</td> - <td>count, max, sum</td> - <td>Average time (sec) spent on 2nd phase ranking</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_matching_rank_profile_docs_matched">content.proton.documentdb.matching.rank_profile.docs_matched</p></td> - <td>document</td> - <td>count, rate</td> - <td>Number of documents matched</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_matching_rank_profile_limited_queries">content.proton.documentdb.matching.rank_profile.limited_queries</p></td> - <td>query</td> - <td>rate</td> - <td>Number of queries limited in match phase</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_feeding_commit_operations">content.proton.documentdb.feeding.commit.operations</p></td> - <td>operation</td> - <td>count, max, rate, sum</td> - <td>Number of operations included in a commit</td> - </tr> - <tr> - <td><p id="content_proton_documentdb_feeding_commit_latency">content.proton.documentdb.feeding.commit.latency</p></td> - <td>second</td> - <td>count, max, sum</td> - <td>Latency for commit in seconds</td> - </tr> - </tbody> -</table> - -<h2 id="sentinel-metrics">Sentinel Metrics</h2> -<table class="table"> - <thead> - <tr><th>Name</th><th>Unit</th><th>Suffixes</th><th>Description</th></tr> - </thead> - <tbody> - <tr> - <td><p id="sentinel_restarts">sentinel.restarts</p></td> - <td>restart</td> - <td>count</td> - <td>Number of service restarts done by the sentinel</td> - </tr> - <tr> - <td><p id="sentinel_totalRestarts">sentinel.totalRestarts</p></td> - <td>restart</td> - <td>last, max, sum</td> - <td>Total number of service restarts done by the sentinel since the sentinel was started</td> - </tr> - <tr> - <td><p id="sentinel_uptime">sentinel.uptime</p></td> - <td>second</td> - <td>last</td> - <td>Time the sentinel has been running</td> - </tr> - <tr> - <td><p id="sentinel_running">sentinel.running</p></td> - <td>instance</td> - <td>count, last</td> - <td>Number of services the sentinel has running currently</td> - </tr> - </tbody> -</table> - -<h2 id="slobrok-metrics">Slobrok Metrics</h2> -<table class="table"> - <thead> - <tr><th>Name</th><th>Unit</th><th>Suffixes</th><th>Description</th></tr> - </thead> - <tbody> - <tr> - <td><p id="slobrok_heartbeats_failed">slobrok.heartbeats.failed</p></td> - <td>request</td> - <td>count</td> - <td>Number of heartbeat requests failed</td> - </tr> - <tr> - <td><p id="slobrok_missing_consensus">slobrok.missing.consensus</p></td> - <td>second</td> - <td>count</td> - <td>Number of seconds without full consensus with all other brokers</td> - </tr> - </tbody> -</table> - -<h2 id="storage-metrics">Storage Metrics</h2> -<table class="table"> - <thead> - <tr><th>Name</th><th>Unit</th><th>Suffixes</th><th>Description</th></tr> - </thead> - <tbody> - <tr> - <td><p id="vds_datastored_alldisks_buckets">vds.datastored.alldisks.buckets</p></td> - <td>bucket</td> - <td>average</td> - <td>Number of buckets managed</td> - </tr> - <tr> - <td><p id="vds_datastored_alldisks_docs">vds.datastored.alldisks.docs</p></td> - <td>document</td> - <td>average</td> - <td>Number of documents stored</td> - </tr> - <tr> - <td><p id="vds_datastored_alldisks_bytes">vds.datastored.alldisks.bytes</p></td> - <td>byte</td> - <td>average</td> - <td>Number of bytes stored</td> - </tr> - <tr> - <td><p id="vds_visitor_allthreads_averagevisitorlifetime">vds.visitor.allthreads.averagevisitorlifetime</p></td> - <td>millisecond</td> - <td>count, max, sum</td> - <td>Average lifetime of a visitor</td> - </tr> - <tr> - <td><p id="vds_visitor_allthreads_averagequeuewait">vds.visitor.allthreads.averagequeuewait</p></td> - <td>millisecond</td> - <td>count, max, sum</td> - <td>Average time an operation spends in input queue.</td> - </tr> - <tr> - <td><p id="vds_visitor_allthreads_queuesize">vds.visitor.allthreads.queuesize</p></td> - <td>operation</td> - <td>count, max, sum</td> - <td>Size of input message queue.</td> - </tr> - <tr> - <td><p id="vds_visitor_allthreads_completed">vds.visitor.allthreads.completed</p></td> - <td>operation</td> - <td>rate</td> - <td>Number of visitors completed</td> - </tr> - <tr> - <td><p id="vds_visitor_allthreads_created">vds.visitor.allthreads.created</p></td> - <td>operation</td> - <td>rate</td> - <td>Number of visitors created.</td> - </tr> - <tr> - <td><p id="vds_visitor_allthreads_failed">vds.visitor.allthreads.failed</p></td> - <td>operation</td> - <td>rate</td> - <td>Number of visitors failed</td> - </tr> - <tr> - <td><p id="vds_visitor_allthreads_averagemessagesendtime">vds.visitor.allthreads.averagemessagesendtime</p></td> - <td>millisecond</td> - <td>count, max, sum</td> - <td>Average time it takes for messages to be sent to their target (and be replied to)</td> - </tr> - <tr> - <td><p id="vds_visitor_allthreads_averageprocessingtime">vds.visitor.allthreads.averageprocessingtime</p></td> - <td>millisecond</td> - <td>count, max, sum</td> - <td>Average time used to process visitor requests</td> - </tr> - <tr> - <td><p id="vds_filestor_queuesize">vds.filestor.queuesize</p></td> - <td>operation</td> - <td>count, max, sum</td> - <td>Size of input message queue.</td> - </tr> - <tr> - <td><p id="vds_filestor_averagequeuewait">vds.filestor.averagequeuewait</p></td> - <td>millisecond</td> - <td>count, max, sum</td> - <td>Average time an operation spends in input queue.</td> - </tr> - <tr> - <td><p id="vds_filestor_active_operations_size">vds.filestor.active_operations.size</p></td> - <td>operation</td> - <td>count, max, sum</td> - <td>Number of concurrent active operations</td> - </tr> - <tr> - <td><p id="vds_filestor_active_operations_latency">vds.filestor.active_operations.latency</p></td> - <td>millisecond</td> - <td>count, max, sum</td> - <td>Latency (in ms) for completed operations</td> - </tr> - <tr> - <td><p id="vds_filestor_throttle_window_size">vds.filestor.throttle_window_size</p></td> - <td>operation</td> - <td>count, max, sum</td> - <td>Current size of async operation throttler window size</td> - </tr> - <tr> - <td><p id="vds_filestor_throttle_waiting_threads">vds.filestor.throttle_waiting_threads</p></td> - <td>thread</td> - <td>count, max, sum</td> - <td>Number of threads waiting to acquire a throttle token</td> - </tr> - <tr> - <td><p id="vds_filestor_throttle_active_tokens">vds.filestor.throttle_active_tokens</p></td> - <td>instance</td> - <td>count, max, sum</td> - <td>Current number of active throttle tokens</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_mergemetadatareadlatency">vds.filestor.allthreads.mergemetadatareadlatency</p></td> - <td>millisecond</td> - <td>count, max, sum</td> - <td>Time spent in a merge step to check metadata of current node to see what data it has.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_mergedatareadlatency">vds.filestor.allthreads.mergedatareadlatency</p></td> - <td>millisecond</td> - <td>count, max, sum</td> - <td>Time spent in a merge step to read data other nodes need.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_mergedatawritelatency">vds.filestor.allthreads.mergedatawritelatency</p></td> - <td>millisecond</td> - <td>count, max, sum</td> - <td>Time spent in a merge step to write data needed to current node.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_merge_put_latency">vds.filestor.allthreads.merge_put_latency</p></td> - <td>millisecond</td> - <td>count, max, sum</td> - <td>Latency of individual puts that are part of merge operations</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_merge_remove_latency">vds.filestor.allthreads.merge_remove_latency</p></td> - <td>millisecond</td> - <td>count, max, sum</td> - <td>Latency of individual removes that are part of merge operations</td> - </tr> - <tr> - <td><p id="vds_filestor_allstripes_throttled_rpc_direct_dispatches">vds.filestor.allstripes.throttled_rpc_direct_dispatches</p></td> - <td>instance</td> - <td>rate</td> - <td>Number of times an RPC thread could not directly dispatch an async operation directly to Proton because it was disallowed by the throttle policy</td> - </tr> - <tr> - <td><p id="vds_filestor_allstripes_throttled_persistence_thread_polls">vds.filestor.allstripes.throttled_persistence_thread_polls</p></td> - <td>instance</td> - <td>rate</td> - <td>Number of times a persistence thread could not immediately dispatch a queued async operation because it was disallowed by the throttle policy</td> - </tr> - <tr> - <td><p id="vds_filestor_allstripes_timeouts_waiting_for_throttle_token">vds.filestor.allstripes.timeouts_waiting_for_throttle_token</p></td> - <td>instance</td> - <td>rate</td> - <td>Number of times a persistence thread timed out waiting for an available throttle policy token</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_put_count">vds.filestor.allthreads.put.count</p></td> - <td>operation</td> - <td>rate</td> - <td>Number of requests processed.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_put_failed">vds.filestor.allthreads.put.failed</p></td> - <td>operation</td> - <td>rate</td> - <td>Number of failed requests.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_put_test_and_set_failed">vds.filestor.allthreads.put.test_and_set_failed</p></td> - <td>operation</td> - <td>rate</td> - <td>Number of operations that were skipped due to a test-and-set condition not met</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_put_latency">vds.filestor.allthreads.put.latency</p></td> - <td>millisecond</td> - <td>count, max, sum</td> - <td>Latency of successful requests.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_put_request_size">vds.filestor.allthreads.put.request_size</p></td> - <td>byte</td> - <td>count, max, sum</td> - <td>Size of requests, in bytes</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_remove_count">vds.filestor.allthreads.remove.count</p></td> - <td>operation</td> - <td>rate</td> - <td>Number of requests processed.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_remove_failed">vds.filestor.allthreads.remove.failed</p></td> - <td>operation</td> - <td>rate</td> - <td>Number of failed requests.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_remove_test_and_set_failed">vds.filestor.allthreads.remove.test_and_set_failed</p></td> - <td>operation</td> - <td>rate</td> - <td>Number of operations that were skipped due to a test-and-set condition not met</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_remove_latency">vds.filestor.allthreads.remove.latency</p></td> - <td>millisecond</td> - <td>count, max, sum</td> - <td>Latency of successful requests.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_remove_request_size">vds.filestor.allthreads.remove.request_size</p></td> - <td>byte</td> - <td>count, max, sum</td> - <td>Size of requests, in bytes</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_get_count">vds.filestor.allthreads.get.count</p></td> - <td>operation</td> - <td>rate</td> - <td>Number of requests processed.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_get_failed">vds.filestor.allthreads.get.failed</p></td> - <td>operation</td> - <td>rate</td> - <td>Number of failed requests.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_get_latency">vds.filestor.allthreads.get.latency</p></td> - <td>millisecond</td> - <td>count, max, sum</td> - <td>Latency of successful requests.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_get_request_size">vds.filestor.allthreads.get.request_size</p></td> - <td>byte</td> - <td>count, max, sum</td> - <td>Size of requests, in bytes</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_update_count">vds.filestor.allthreads.update.count</p></td> - <td>request</td> - <td>rate</td> - <td>Number of requests processed.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_update_failed">vds.filestor.allthreads.update.failed</p></td> - <td>request</td> - <td>rate</td> - <td>Number of failed requests.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_update_test_and_set_failed">vds.filestor.allthreads.update.test_and_set_failed</p></td> - <td>request</td> - <td>rate</td> - <td>Number of requests that were skipped due to a test-and-set condition not met</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_update_latency">vds.filestor.allthreads.update.latency</p></td> - <td>millisecond</td> - <td>count, max, sum</td> - <td>Latency of successful requests.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_update_request_size">vds.filestor.allthreads.update.request_size</p></td> - <td>byte</td> - <td>count, max, sum</td> - <td>Size of requests, in bytes</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_createiterator_count">vds.filestor.allthreads.createiterator.count</p></td> - <td>request</td> - <td>rate</td> - <td>Number of requests processed.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_createiterator_latency">vds.filestor.allthreads.createiterator.latency</p></td> - <td>millisecond</td> - <td>count, max, sum</td> - <td>Latency of successful requests.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_visit_count">vds.filestor.allthreads.visit.count</p></td> - <td>request</td> - <td>rate</td> - <td>Number of requests processed.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_visit_latency">vds.filestor.allthreads.visit.latency</p></td> - <td>millisecond</td> - <td>count, max, sum</td> - <td>Latency of successful requests.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_remove_location_count">vds.filestor.allthreads.remove_location.count</p></td> - <td>request</td> - <td>rate</td> - <td>Number of requests processed.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_remove_location_latency">vds.filestor.allthreads.remove_location.latency</p></td> - <td>millisecond</td> - <td>count, max, sum</td> - <td>Latency of successful requests.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_splitbuckets_count">vds.filestor.allthreads.splitbuckets.count</p></td> - <td>request</td> - <td>rate</td> - <td>Number of requests processed.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_joinbuckets_count">vds.filestor.allthreads.joinbuckets.count</p></td> - <td>request</td> - <td>rate</td> - <td>Number of requests processed.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_deletebuckets_count">vds.filestor.allthreads.deletebuckets.count</p></td> - <td>request</td> - <td>rate</td> - <td>Number of requests processed.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_deletebuckets_failed">vds.filestor.allthreads.deletebuckets.failed</p></td> - <td>request</td> - <td>rate</td> - <td>Number of failed requests.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_deletebuckets_latency">vds.filestor.allthreads.deletebuckets.latency</p></td> - <td>millisecond</td> - <td>count, max, sum</td> - <td>Latency of successful requests.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_remove_by_gid_count">vds.filestor.allthreads.remove_by_gid.count</p></td> - <td>request</td> - <td>rate</td> - <td>Number of requests processed.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_remove_by_gid_failed">vds.filestor.allthreads.remove_by_gid.failed</p></td> - <td>request</td> - <td>rate</td> - <td>Number of failed requests.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_remove_by_gid_latency">vds.filestor.allthreads.remove_by_gid.latency</p></td> - <td>millisecond</td> - <td>count, max, sum</td> - <td>Latency of successful requests.</td> - </tr> - <tr> - <td><p id="vds_filestor_allthreads_setbucketstates_count">vds.filestor.allthreads.setbucketstates.count</p></td> - <td>request</td> - <td>rate</td> - <td>Number of requests processed.</td> - </tr> - <tr> - <td><p id="vds_mergethrottler_averagequeuewaitingtime">vds.mergethrottler.averagequeuewaitingtime</p></td> - <td>millisecond</td> - <td>count, max, sum</td> - <td>Time merges spent in the throttler queue</td> - </tr> - <tr> - <td><p id="vds_mergethrottler_queuesize">vds.mergethrottler.queuesize</p></td> - <td>instance</td> - <td>count, max, sum</td> - <td>Length of merge queue</td> - </tr> - <tr> - <td><p id="vds_mergethrottler_active_window_size">vds.mergethrottler.active_window_size</p></td> - <td>instance</td> - <td>count, max, sum</td> - <td>Number of merges active within the pending window size</td> - </tr> - <tr> - <td><p id="vds_mergethrottler_estimated_merge_memory_usage">vds.mergethrottler.estimated_merge_memory_usage</p></td> - <td>byte</td> - <td>count, max, sum</td> - <td>An estimated upper bound of the memory usage (in bytes) of the merges currently in the active window</td> - </tr> - <tr> - <td><p id="vds_mergethrottler_bounced_due_to_back_pressure">vds.mergethrottler.bounced_due_to_back_pressure</p></td> - <td>instance</td> - <td>rate</td> - <td>Number of merges bounced due to resource exhaustion back-pressure</td> - </tr> - <tr> - <td><p id="vds_mergethrottler_locallyexecutedmerges_ok">vds.mergethrottler.locallyexecutedmerges.ok</p></td> - <td>instance</td> - <td>rate</td> - <td>The number of successful merges for 'locallyexecutedmerges'</td> - </tr> - <tr> - <td><p id="vds_mergethrottler_mergechains_ok">vds.mergethrottler.mergechains.ok</p></td> - <td>operation</td> - <td>rate</td> - <td>The number of successful merges for 'mergechains'</td> - </tr> - <tr> - <td><p id="vds_mergethrottler_mergechains_failures_busy">vds.mergethrottler.mergechains.failures.busy</p></td> - <td>operation</td> - <td>rate</td> - <td>The number of merges that failed because the storage node was busy</td> - </tr> - <tr> - <td><p id="vds_mergethrottler_mergechains_failures_total">vds.mergethrottler.mergechains.failures.total</p></td> - <td>operation</td> - <td>rate</td> - <td>Sum of all failures</td> - </tr> - <tr> - <td><p id="vds_server_network_tls-handshakes-failed">vds.server.network.tls-handshakes-failed</p></td> - <td>operation</td> - <td>count</td> - <td>Number of client or server connection attempts that failed during TLS handshaking</td> - </tr> - <tr> - <td><p id="vds_server_network_peer-authorization-failures">vds.server.network.peer-authorization-failures</p></td> - <td>failure</td> - <td>count</td> - <td>Number of TLS connection attempts failed due to bad or missing peer certificate credentials</td> - </tr> - <tr> - <td><p id="vds_server_network_client_tls-connections-established">vds.server.network.client.tls-connections-established</p></td> - <td>connection</td> - <td>count</td> - <td>Number of secure mTLS connections established</td> - </tr> - <tr> - <td><p id="vds_server_network_server_tls-connections-established">vds.server.network.server.tls-connections-established</p></td> - <td>connection</td> - <td>count</td> - <td>Number of secure mTLS connections established</td> - </tr> - <tr> - <td><p id="vds_server_network_client_insecure-connections-established">vds.server.network.client.insecure-connections-established</p></td> - <td>connection</td> - <td>count</td> - <td>Number of insecure (plaintext) connections established</td> - </tr> - <tr> - <td><p id="vds_server_network_server_insecure-connections-established">vds.server.network.server.insecure-connections-established</p></td> - <td>connection</td> - <td>count</td> - <td>Number of insecure (plaintext) connections established</td> - </tr> - <tr> - <td><p id="vds_server_network_tls-connections-broken">vds.server.network.tls-connections-broken</p></td> - <td>connection</td> - <td>count</td> - <td>Number of TLS connections broken due to failures during frame encoding or decoding</td> - </tr> - <tr> - <td><p id="vds_server_network_failed-tls-config-reloads">vds.server.network.failed-tls-config-reloads</p></td> - <td>failure</td> - <td>count</td> - <td>Number of times background reloading of TLS config has failed</td> - </tr> - <tr> - <td><p id="vds_server_network_rpc-capability-checks-failed">vds.server.network.rpc-capability-checks-failed</p></td> - <td>failure</td> - <td>count</td> - <td>Number of RPC operations that failed due to one or more missing capabilities</td> - </tr> - <tr> - <td><p id="vds_server_network_status-capability-checks-failed">vds.server.network.status-capability-checks-failed</p></td> - <td>failure</td> - <td>count</td> - <td>Number of status page operations that failed due to one or more missing capabilities</td> - </tr> - <tr> - <td><p id="vds_server_fnet_num-connections">vds.server.fnet.num-connections</p></td> - <td>connection</td> - <td>count</td> - <td>Total number of connection objects</td> - </tr> - </tbody> -</table> diff --git a/en/reference/writing/document-selector-language.html b/en/reference/writing/document-selector-language.html deleted file mode 100644 index 56d5916e8b..0000000000 --- a/en/reference/writing/document-selector-language.html +++ /dev/null @@ -1,590 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Document selector language reference" -redirect_from: -- en/reference/document-select-language.html ---- - -<p> -This document describes the <em>document selector language</em>, used to -select a subset of documents when feeding, dumping and garbage collecting data. -It defines a text string format that can be parsed to build a parse tree, -which in turn can answer whether a given document is contained within the subset or not. -</p> - - - -<h2 id="examples">Examples</h2> -<p>Match all documents in the <code>music</code> schema:</p> -<p><code>music</code></p> -<p> - As applications can have multiple schemas, - match document type (schema) and then a specific value in the <code>artistname</code> field: -</p> -<p><code>music and music.artistname == "Coldplay"</code></p> -<p> - Below, the first condition states that the documents should be of type music, - and the author field must exist. - The second states that the field length must be set, and be less than or equal to 1000: -</p> -<p><code>music.author and music.length <= 1000</code></p> -<p> - The next expression selects all documents where either of the subexpressions are true. - The first one states that the author field should include the name John Doe, with anything in between or in front. - The <code>\n</code> escape is converted to a newline before the field comparison is done. - Thus requiring the field to end with Doe and a newline for a match to be true. - The second expression selects all books where no author is defined: -</p> -<p><code>book.author = "*John*Doe\n" or not book.author</code></p> -<p> - Here is an example of how parentheses are used to group expressions. - Also, a constant value false has been used. Note that the <code>(false or music.test)</code> - sub-expression could be exchanged with just <code>music.test</code> without altering the result of the selection. - The sub-expression within the <code>not</code> clause selects all documents - where the size field is above 1000 and the test field is defined. - The <code>not</code> clause inverts the selection, - thus selecting all documents with size less than or equal to 1000 or the test field undefined: -</p> -<p><code>not (music.length > 1000) and (false or music.test)</code></p> -<p>Other examples:</p> -<ul> - <li><code>music.version() == 3 and (music.givenname + " " + music.surname).lowercase() = "bruce spring*"</code></li> - <li><code>id.user.hash().abs() % 300 % 7 = 1</code></li> - <li><code>music.wavstream.hash() == music.checksum</code></li> - <li><code>music.size / music.length > 10</code></li> - <li><code>music.expire > now() - 7200</code></li> -</ul> - - - -<h2 id="case-sensitiveness">Case sensitiveness</h2> -<p> -The identifiers used in this language (<code>and or not true false null id -scheme namespace specific user group</code>) are not case-sensitive. -It is recommended to use lower cased identifiers for consistency with the documentation. -</p> - - - -<h2 id="branch-operators-precedence">Branch operators / precedence</h2> -<p> -The branch operators are used to combine other nodes in the parse tree -generated from the text format. The different branch nodes existing is -listed in the table below in order of precedence. -Operators listed in order of precedence: -</p> -<table class="table"> -<thead> - <tr><th>Operator</th><th>Description</th></tr> -</thead><tbody> -<tr> - <td>NOT</td> - <td>Unary prefix operator inverting the selection of the child node</td> - </tr><tr> - <td>AND</td> - <td>Binary infix operator, which is true if all its children are</td> - </tr><tr> - <td>OR</td> - <td>Binary infix operator, which is true if any of its children are</td> - </tr> -</tbody> -</table> -<p> - Use parentheses to define own precedence. - <code>a and b or c and d</code> is equivalent to <code>(a and b) or (c and d) -</code> since and has higher precedence than or. The expression - <code>a and (b or c) and d</code> is not - equivalent to the previous two, since parentheses have been used to force the - or-expression to be evaluated first. -</p><p> - Parentheses can also be used in value calculations. - Where modulo <code>%</code> has the highest precedence, - multiplication <code>*</code> and division <code>/</code> next, - addition <code>+</code> and subtractions <code>-</code> have lowest precedence. -</p> - - - -<h2 id="primitives">Primitives</h2> -<table class="table"> -<thead> - <tr><th>Primitive</th><th>Description</th></tr> -</thead><tbody> - <tr> - <td>Boolean constant</td> - <td>The boolean constants <code>true</code> and <code>false</code> can be - used to match all/nothing</td> - </tr> - <tr> - <td>Null constant</td> - <td>Referencing a field that is not present in a document returns a special <code>null</code> value. - The expression <code>music.title</code> is shorthand for <code>music.title != null</code>. - There are potentially subtle interactions with null values when used with comparisons, see - <a href="#comparisons-with-missing-fields-null-values">comparisons with missing fields (null values)</a>. - </td> - </tr> - <tr> - <td>Document type</td> - <td>A document type can be used as a primitive to select a given type of documents - - <a href="/en/writing/visiting.html#analyzing-field-values">example</a>.</td> - </tr> - <tr> - <td>Document field specification</td> - <td>A document field specification (<code>doctype.field</code>) can be used - as a primitive to select all documents that have field set - - a shorter form of <code>doctype.field != null</code></td> - </tr> - <tr> - <td>Comparison</td> - <td>The comparison is a primitive used to compare two values</td> - </tr> -</tbody> -</table> - - -<h2 id="comparison">Comparison</h2> -<p> - Comparisons operators compares two values using an operator. - All the operators are infix and take two arguments. -</p> -<table class="table"> -<thead> - <tr><th>Operator</th><th>Description</th></tr> -</thead><tbody> - <tr> - <td>></td> - <td> - This is true if the left argument is greater than the right one. - Operators using greater than or less than notations only makes sense where - both arguments are either numbers or strings. In case of strings, they are - ordered by their binary (byte-wise) representation, with the first character being the - most significant and the last character the least significant. If the argument - is of mixed type or one of the arguments are not a number or a string, the - comparison will be invalid and not match.</td> - </tr><tr> - <td><</td> - <td>Matches if left argument is less than the right one</td> - </tr><tr> - <td><=</td> - <td>Matches if the left argument is less than or equal to the right one</td> - </tr><tr> - <td>>=</td> - <td>Matches if the left argument is greater than or equal to the right one</td> - </tr><tr> - <td>==</td> - <td> - Matches if both arguments are exactly the same. - Both arguments must be of the same type for a match</td> - </tr><tr> - <td>!=</td> - <td>Matches if both arguments are not the same</td> - </tr><tr> - <td>=</td> - <td> - String matching using a glob pattern. Matches only if the pattern given - as the right argument matches the whole string given by the left argument. - Asterisk <code>*</code> can be used to match zero or more of any character. - Question mark <code>?</code> can be used to match any one character. - The pattern matching operators, regex <code>=~</code> and glob <code>=</code>, only makes sense - if both arguments are strings. The regex operator will never match anything else. - The glob operator will revert to the behaviour of <code>==</code> if both arguments are not strings.</td> - </tr><tr> - <td>=~</td> - <td> - String matching using a regular expression. - Matches if the regular expression given as the right argument - matches the string given as the left argument. Regex notation is like perl. - Use '^' to indicate start of value, '$' to indicate end of value</td> - </tr> -</tbody> -</table> - -<h3 id="comparisons-with-missing-fields-null-values">Comparisons with missing fields (null values)</h3> -<p> -The only comparison operators that are well-defined when one or both operands may be <code>null</code> -(i.e. field is not present) are <code>==</code> and <code>!=</code>. Using any other comparison operators -on a <code>null</code> value will yield a special <i>invalid</i> value. -</p> -<p> -Invalid values may "poison" any logical expression they are part of: -</p> -<ul> - <li><code>AND</code> returns invalid if none of its operands are false and at least one is invalid</li> - <li><code>OR</code> returns invalid if none of its operands are true and at least one is invalid</li> - <li><code>NOT</code> returns invalid if the operand is invalid</li> -</ul> -<p> -If an invalid value is propagated as the root result of a selection expression, the document is -not considered a match. This is usually the behavior you want; if a field does not exist, any selection -requiring it should not match either. However, in garbage collection, documents which results in an invalid -selection are <i>not</i> removed as that could be dangerous. -</p> -<p> -One example where this may have <em>unexpected</em> behavior: -</p> -<ol> - <li>You have many documents of type <code>foo</code> already fed into a cluster.</li> - <li>You add a new field <code>expires_at_time</code> to the document type and update a subset of the - documents that you wish to keep.</li> - <li>You add a garbage collection selection to the <code>foo</code> document declaration to only - keep non-expired documents: <code>foo.expires_at_time > now()</code></li> -</ol> -<p> - At this point, the old documents that <em>do not</em> contain an <code>expires_at_time</code> field will <em>not</em> - be removed, as the expression will evaluate to invalid instead of <code>false</code>. -</p> -<p> - To work around this issue, "short-circuiting" using a field presence check may be used: - <code>(foo.expires_at_time != null) and (foo.expires_at_time > now())</code>. -</p> - -<h2 id="null-behavior-with-imported-fields">Null behavior with imported fields</h2> -<p> - If your selection references imported fields, - <code>null</code> will be returned for any imported field when the selection is evaluated - in a context where the referenced document can't be retrieved. - For GC expressions this will happen in the client as part of the feed routing logic, - and it may also happen on backend nodes whose parent document set is incomplete (in case of node failures etc.). - It is therefore important that you have this in mind when writing GC selections using imported fields. -</p><p> - When you specify a selection criteria in a <code><document></code> tag, you're stating what - a document must satisfy in order to be fed into the content cluster and to be kept there. -</p><p> - As an example, imagine a document type <code>music_recording</code> with an imported field - <code>artist_is_cool</code> that points to a boolean field <code>is_cool</code> in a parent - <code>artist</code> document. - If you only want your cluster to retain recordings from artists that are certifiably cool, - you might be tempted to write a selection like the following: -</p> -<pre>{% highlight xml %} -<document type="music_recording" - mode="index" - selection="music_recording.artist_is_cool == true"> -{% endhighlight %}</pre> -<p> -<strong>This won't work as expected</strong>, because this expression is evaluated as part of the feeding pipeline to figure -out if a cluster should accept a given document. At that point in time, there is no access to the parent document. -Consequently, the field will return <code>null</code> and the document won't be routed to the cluster. -</p><p> -Instead, write your expressions to handle the case where the parent document <em>may not exist</em>: -</p> -<pre>{% highlight xml %} -<document type="music_recording" - mode="index" - selection="(music_recording.artist_is_cool == null) or (music_recording.artist_is_cool == true)"> -{% endhighlight %}</pre> -<p> -With this selection, we explicitly let a document be accepted into the cluster if its imported field -is <em>not</em> available. However, if it <em>is</em> available, we allow it to be used for GC. -</p> - -<h2 id="locale-character-sets">Locale / Character sets</h2> -<p> -The language currently does not support character sets other than ASCII. -Glob and regex matching of single characters are not guaranteed to match exactly one character, -but might match a part of a character represented by multiple byte values. -</p> - -<h2 id="values">Values</h2> -<p> -The comparison operator compares two values. A value can be any of the following: -</p> -<table class="table"> -<thead></thead><tbody> -<tr> - <th>Document field specification</th> - <td><p> - Syntax: <code><doctype>.<fieldpath></code> - </p><p> - Documents have a set of fields defined, depending on the document type. - The field name is the identifier used for the field. This expression returns - the value of the field, which can be an integer, a floating point number, - a string, an array, or a map of these types. - </p><p> - For multivalues, we support only the <em>equals</em> operator for comparison. - The semantics is that the array returned by the fieldvalue must <em>contain</em> at least - one element that matches the other side of the comparison. For maps, there must exist a - key matching the comparison. - </p><p> - The simplest use of the fieldpath is to specify a field, - but for complex types please refer to - <a href="../schemas/document-field-path.html">the field path syntax documentation</a>. - </p></td> -</tr><tr> - <th>Id</th> - <td><p> - Syntax: <code> id.[scheme|namespace|type|specific|user|group] </code> - </p><p> - Each document has a document ID, uniquely identifying that document within - a Vespa installation. The id operator returns the string identifier, or if - an optional argument is given, a part of the id. - </p> - <ul> - <li>scheme (id)</li> - <li>namespace (to separate different users' data)</li> - <li>type (specified in the id scheme)</li> - <li>specific (User specified part to distinguish documents within a namespace)</li> - <li>user (The number specified in document IDs using the n= modifier)</li> - <li>group (The string group specified in document IDs using the g= modifier)</li> - </ul> - </td> -</tr><tr> - <th>null</th> - <td><p> - The value null can be given to specify - nothingness. For instance, a field specification for - a document not containing the field will evaluate to - null, so the comparison 'music.artist == null' will - select all documents that don't have the artist - field set. 'id.user == null' will match all - documents that don't use the <code>n=</code> - <a href="../../schemas/documents.html#id-scheme">document ID scheme</a>. - </p><p> - Tensor fields can <em>only</em> be compared against null. It's not - possible to write a document selection that uses the <em>contents</em> - of tensor fields—only their presence can be checked. - </p></td> -</tr><tr> - <th>Number</th> - <td><p> - A value can be a number, either an integer or a floating point number. - Type of number is insignificant. You don't have to use the same type - of number on both sides of a comparison. For instance '3.0 < 4' will - match, and '3.0 == 3' will probably match (operator == is generally not - advised for floating point numbers due to rounding issues). - Numbers can be written in multiple ways - examples:</p> -<pre> - 1234 -234 +53 +534.34 543.34e4 -534E-3 0.2343e-8 -</pre> - </td> -</tr><tr> - <th>Strings</th> - <td><p> - A string value is given quoted with double quotes (i.e. "mystring"). - The string is interpreted as an ASCII string. that is, only ASCII values - 32 to 126 can be used unescaped, apart from the characters \ and " which - also needs to be escaped. Escape common special characters like:</p> - <table class="table"> - <thead><tr><th>Character</th><th>Escaped character</th></tr></thead> - <tbody> - <tr><td>Newline</td><td>\n</td></tr> - <tr><td>Carriage return</td><td>\r</td></tr> - <tr><td>Tab</td><td>\t</td></tr> - <tr><td>Form feed</td><td>\f</td></tr> - <tr><td>"</td><td>\"</td></tr> - <tr><td>Any other character</td> - <td>\x## (where ## is a two digit hexadecimal number specifying the ASCII - value.</td></tr> - </tbody> - </table> - </td> -</tr> -</tbody> -</table> - -<h3 id="value-arithmetics">Value arithmetics</h3> - -<p>You can do arithmetics on values. -The common arithmetics operators addition <code>+</code>, subtraction <code>-</code>, -multiplication <code>*</code>, division <code>/</code> and modulo <code>%</code> are supported. -</p> - -<h3 id="functions">Functions</h3> -<p> -Functions are called on something and returns a value that can be used in comparison expressions: -</p> -<table class="table"> -<thead></thead><tbody> - <tr> - <th>Value functions</th> - <td> - A value function takes a value, does something with it and returns a value which can be of any type. - <ul> - <li> - <em>abs() </em>Called on a numeric type, returns the absolute value of that numeric type. - That is -3 returns 3 and -4.3 returns 4.3. - </li><li> - <em>hash() </em>Calculates an MD5 hash of whatever value it is called on. The result is a - signed 64-bit integer. (Use abs() after if you want to only get positive hash values). - </li><li> - <em>lowercase() </em>Called on a string value to turn upper - case characters into lower case ones. - <strong>NOTE: </strong>This only works for the characters 'a' through 'z', no locale support. - </li> - </ul> - </td> - </tr><tr> - <th>Document type functions</th> - <td> - Some functions can take a document type instead of a value, and return a value based on the type. - <ul> - <li> - <em>version() </em>The <code>version()</code> function returns - the version number of a document type. - </li> - </ul> - </td> - </tr> -</tbody> -</table> - - -<h4 id="now-function">Now function</h4> -<p> -Document selection provides a <em>now()</em> function, which returns the current date timestamp. -Use this to filter documents by age, typically for <a href="../applications/services/content.html#documents">garbage collection</a>. -</p> - -<p><b>Example</b>: If you have a long field <em>inserttimestamp</em> in your <code>music</code> schema, -this expression will only match documents from the last two hours: -</p> - -<p> -<code>music.inserttimestamp > now() - 7200</code> -</p> - - -<h2 id="using-imported-fields-in-selections">Using imported fields in selections</h2> -<p> -When using <a href="../../schemas/parent-child.html">parent-child</a> you can refer to simple imported -fields (i.e. top-level primitive fields) in selections as if they were regular fields -in the child document type. Complex fields (collections, structures etc.) are not supported. -</p> -{% include important.html content="special care needs to be taken -when using document selections referencing imported fields, -especially if using these are part of garbage collection expressions. If an imported field -references a document that cannot be accessed at evaluation time, the imported field behaves -as if it had been a regular, non-present field in the child document. -In other words, it will return the special <code>null</code> value."%} -<p> -See <a href="#comparisons-with-missing-fields-null-values">comparisons with missing fields (null values)</a> -for a more detailed discussion of null-semantics and how to write selections that handle these -in a well-defined manner. In particular, read <a href="#null-behavior-with-imported-fields"> null -behavior with imported fields</a> if you're writing GC selections. -</p> - - -<h3 id="example">Example</h3> -<p> -The following is an example of a 3-level parent-child hierarchy. -</p> -<p>Grandparent schema:</p> -<pre> -schema grandparent { - document grandparent { - field a1 type int { - indexing: attribute | summary - } - } -} -</pre> -<p>Parent schema, with reference to grandparent:</p> -<pre> -schema parent { - document parent { - field a2 type int { - indexing: attribute | summary - } - field ref type reference<grandparent> { - indexing: attribute | summary - } - } - import field ref.a1 as a1 {} -} -</pre> -<p>Child schema, with reference to parent and (transitively) grandparent:</p> -<pre> -schema child { - document child { - field a3 type int { - indexing: attribute | summary - } - field ref type reference<parent> { - indexing: attribute | summary - } - } - import field ref.a1 as a1 {} - import field ref.a2 as a2 {} -} -</pre> -<p>Using these in document selection expressions is easy:</p> -<p>Find all child docs whose grandparents have an <code>a1</code> greater than 5:</p> -<p><code>child.a1 > 5</code></p> -<p>Find all child docs whose parents have an <code>a2</code> of 10 and grandparents have <code>a1</code> of 4:</p> -<p><code>child.a1 == 10 and child.a2 == 4</code></p> -<p>Find all child docs where the parent document cannot be found (or where the referenced field is not set in the parent):</p> -<p><code>child.a2 == null</code></p> -<p>Note that when visiting <code>child</code> documents we only ever access imported fields via the -<strong>child</strong> document type itself.</p> -<p>A much more complete list usage examples for the above document schemas and -reference relations can be found in the -<a href="https://github.com/vespa-engine/system-test/blob/master/tests/search/parent_child/imported_fields_in_selections.rb"> -imported fields in selections</a> system test. This test covers both the visiting and GC cases.</p> - - - -<h2 id="constraints">Constraints</h2> -<p> -Language identifiers restrict what can -be used as document type names. The following values are not valid document type names: -<em>true, false, and, or, not, id, null</em> -</p> - - - -<h2 id="grammar-EBNF-of-the-language">Grammar - EBNF of the language</h2> -<p> -To simplify, double casing of strings has not been included. -The identifiers "null", "true", "false" etc. can be written in any case, including mixed case. -</p> -<pre> -nil = "null" ; -bool = "true" | "false" ; -posdigit = '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' ; -digit = '0' | posdigit ; -hexdigit = digit | 'a' | 'b' | 'c' | 'd' | 'e' | 'f' - | 'A' | 'B' | 'C' | 'D' | 'E' | 'F' ; -integer = [ '-' | '+' ], posdigit, { digit } ; -float = [ '-' | '+' ], digit, { digit }, - [ '.' , { digit }, [ ('e' | 'E'), posdigit, { digit } ] ] ; -number = float | integer ; -stdchars = ? All ASCII chars except '\\', '"', 0 - 31 and 127 - 255 ? ; -alpha = ? ASCII characters in the range a-z and A-Z ? ; -alphanum = alpha | digit ; -space = ( ' ' | '\t' | '\f' | '\r' | '\n' ) ; -string = '"', { stdchars | ( '\\', ( 't' | 'n' | 'f' | 'r' | '"' ) ) - | ( "\\x", hexdigit, hexdigit ) }, '"' ; -doctype = alpha, { alphanum } ; -fieldname = { alphanum '{' |'}' | '[' | ']' '.' } ; -function = alpha, { alphanum } ; -idarg = "scheme" | "namespace" | "type" | "specific" | "user" | "group" ; -searchcolumnarg = integer ; -operator = ">=" | ">" | "==" | "=~" | "=" | "<=" | "<" | "!=" ; -idspec = "id", [ '.', idarg ] ; -searchcolumnspec = "searchcolumn", [ '.', searchcolumnarg ] ; -fieldspec = doctype, ( function | ('.', fieldname) ) ; -value = ( valuegroup | nil | number | string | idspec | searchcolumnspec | fieldspec ), - { function } ; -valuefuncmod = ( valuegroup | value ), '%', - ( valuefuncmod | valuegroup | value ) ; -valuefuncmul = ( valuefuncmod | valuegroup | value ), ( '*' | '/' ), - ( valuefuncmul | valuefuncmod | valuegroup | value ) ; -valuefuncadd = ( valuefuncmul | valuefuncmod | valuegroup | value ), - ( '+' | '-' ), - ( valuefuncadd | valuefuncmul | valuefuncmod | valuegroup - | value ) ; -valuegroup = '(', arithmvalue, ')' ; -arithmvalue = ( valuefuncadd | valuefuncmul | valuefuncmod | valuegroup - | value ) ; -comparison = arithmvalue, { space }, operator, { space }, - arithmvalue ; -leaf = bool | comparison | fieldspec | doctype ; -not = "not", { space }, ( group | leaf ) ; -and = ( not | group | leaf ), { space }, "and", { space }, - ( and | not | group | leaf ) ; -or = ( and | not | group | leaf ), { space }, "or", { space }, - ( or | and | not | group | leaf ) ; -group = '(', { space }, ( or | and | not | group | leaf ), - { space }, ')' ; -expression = ( or | and | not | group | leaf ) ; -</pre> diff --git a/en/reference/writing/document-selector-language.mdx b/en/reference/writing/document-selector-language.mdx new file mode 100644 index 0000000000..6b41a1eecb --- /dev/null +++ b/en/reference/writing/document-selector-language.mdx @@ -0,0 +1,410 @@ +--- +title: "Document selector language reference" +sidebarTitle: "document selector language" +--- + +This document describes the _document selector language_, used to select a subset of documents when feeding, dumping and garbage collecting data. It defines a text string format that can be parsed to build a parse tree, which in turn can answer whether a given document is contained within the subset or not. + +## Examples + +Match all documents in the `music` schema: + +`music` + +As applications can have multiple schemas, match document type (schema) and then a specific value in the `artistname` field: + +`music and music.artistname == "Coldplay"` + +Below, the first condition states that the documents should be of type music, and the author field must exist. The second states that the field length must be set, and be less than or equal to 1000: + +`music.author and music.length <= 1000` + +The next expression selects all documents where either of the subexpressions are true. The first one states that the author field should include the name John Doe, with anything in between or in front. The `\n` escape is converted to a newline before the field comparison is done. Thus requiring the field to end with Doe and a newline for a match to be true. The second expression selects all books where no author is defined: + +`book.author = "*John*Doe\n" or not book.author` + +Here is an example of how parentheses are used to group expressions. Also, a constant value false has been used. Note that the `(false or music.test)` sub-expression could be exchanged with just `music.test` without altering the result of the selection. The sub-expression within the `not` clause selects all documents where the size field is above 1000 and the test field is defined. The `not` clause inverts the selection, thus selecting all documents with size less than or equal to 1000 or the test field undefined: + +`not (music.length > 1000) and (false or music.test)` + +Other examples: + + - `music.version() == 3 and (music.givenname + " " + music.surname).lowercase() = "bruce spring*"` + - `id.user.hash().abs() % 300 % 7 = 1` + - `music.wavstream.hash() == music.checksum` + - `music.size / music.length > 10` + - `music.expire > now() - 7200` + +## Case sensitiveness + +The identifiers used in this language (`and or not true false null id scheme namespace specific user group`) are not case-sensitive. It is recommended to use lower cased identifiers for consistency with the documentation. + +## Branch operators / precedence + +The branch operators are used to combine other nodes in the parse tree generated from the text format. The different branch nodes existing is listed in the table below in order of precedence. Operators listed in order of precedence: + +<table className="vespa-table"> + <thead> + <tr> + <th>Operator</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>NOT</td> + <td>Unary prefix operator inverting the selection of the child node</td> + </tr> + <tr> + <td>AND</td> + <td>Binary infix operator, which is true if all its children are</td> + </tr> + <tr> + <td>OR</td> + <td>Binary infix operator, which is true if any of its children are</td> + </tr> + </tbody> +</table> + +Use parentheses to define own precedence. `a and b or c and d` is equivalent to `(a and b) or (c and d)` since and has higher precedence than or. The expression `a and (b or c) and d` is not equivalent to the previous two, since parentheses have been used to force the or-expression to be evaluated first. + +Parentheses can also be used in value calculations. Where modulo `%` has the highest precedence, multiplication `*` and division `/` next, addition `+` and subtractions `-` have lowest precedence. + +## Primitives + +<table className="vespa-table"> + <thead> + <tr> + <th>Primitive</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>Boolean constant</td> + <td>The boolean constants <code>{`true`}</code> and <code>{`false`}</code> can be used to match all/nothing</td> + </tr> + <tr> + <td>Null constant</td> + <td>Referencing a field that is not present in a document returns a special <code>{`null`}</code> value. The expression <code>{`music.title`}</code> is shorthand for <code>{`music.title != null`}</code>. There are potentially subtle interactions with null values when used with comparisons, see <a href="#comparisons-with-missing-fields-null-values" aria-label="comparisons with missing fields (null values)">comparisons with missing fields (null values)</a>.</td> + </tr> + <tr> + <td>Document type</td> + <td>A document type can be used as a primitive to select a given type of documents - <a href="/en/writing/visiting#analyzing-field-values" aria-label="example">example</a>.</td> + </tr> + <tr> + <td>Document field specification</td> + <td>A document field specification (<code>{`doctype.field`}</code>) can be used as a primitive to select all documents that have field set - a shorter form of <code>{`doctype.field != null`}</code></td> + </tr> + <tr> + <td>Comparison</td> + <td>The comparison is a primitive used to compare two values</td> + </tr> + </tbody> +</table> + +## Comparison + +Comparisons operators compares two values using an operator. All the operators are infix and take two arguments. + +<table className="vespa-table"> + <thead> + <tr> + <th>Operator</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>></td> + <td>This is true if the left argument is greater than the right one. Operators using greater than or less than notations only makes sense where both arguments are either numbers or strings. In case of strings, they are ordered by their binary (byte-wise) representation, with the first character being the most significant and the last character the least significant. If the argument is of mixed type or one of the arguments are not a number or a string, the comparison will be invalid and not match.</td> + </tr> + <tr> + <td><</td> + <td>Matches if left argument is less than the right one</td> + </tr> + <tr> + <td><=</td> + <td>Matches if the left argument is less than or equal to the right one</td> + </tr> + <tr> + <td>>=</td> + <td>Matches if the left argument is greater than or equal to the right one</td> + </tr> + <tr> + <td>==</td> + <td>Matches if both arguments are exactly the same. Both arguments must be of the same type for a match</td> + </tr> + <tr> + <td>!=</td> + <td>Matches if both arguments are not the same</td> + </tr> + <tr> + <td>=</td> + <td>String matching using a glob pattern. Matches only if the pattern given as the right argument matches the whole string given by the left argument. Asterisk <code>{`*`}</code> can be used to match zero or more of any character. Question mark <code>{`?`}</code> can be used to match any one character. The pattern matching operators, regex <code>{`=~`}</code> and glob <code>{`=`}</code>, only makes sense if both arguments are strings. The regex operator will never match anything else. The glob operator will revert to the behaviour of <code>{`==`}</code> if both arguments are not strings.</td> + </tr> + <tr> + <td>=~</td> + <td>String matching using a regular expression. Matches if the regular expression given as the right argument matches the string given as the left argument. Regex notation is like perl. Use '^' to indicate start of value, '$' to indicate end of value</td> + </tr> + </tbody> +</table> + +### Comparisons with missing fields (null values) + +The only comparison operators that are well-defined when one or both operands may be `null` (i.e. field is not present) are `==` and `!=`. Using any other comparison operators on a `null` value will yield a special _invalid_ value. + +Invalid values may "poison" any logical expression they are part of: + +- `AND` returns invalid if none of its operands are false and at least one is invalid +- `OR` returns invalid if none of its operands are true and at least one is invalid +- `NOT` returns invalid if the operand is invalid + +If an invalid value is propagated as the root result of a selection expression, the document is not considered a match. This is usually the behavior you want; if a field does not exist, any selection requiring it should not match either. However, in garbage collection, documents which results in an invalid selection are _not_ removed as that could be dangerous. + +One example where this may have _unexpected_ behavior: + +1. You have many documents of type `foo` already fed into a cluster. +2. You add a new field `expires_at_time` to the document type and update a subset of the documents that you wish to keep. +3. You add a garbage collection selection to the `foo` document declaration to only keep non-expired documents: `foo.expires_at_time > now()` + +At this point, the old documents that _do not_ contain an `expires_at_time` field will _not_ be removed, as the expression will evaluate to invalid instead of `false`. + +To work around this issue, "short-circuiting" using a field presence check may be used: `(foo.expires_at_time != null) and (foo.expires_at_time > now())`. + +## Null behavior with imported fields + +If your selection references imported fields, `null` will be returned for any imported field when the selection is evaluated in a context where the referenced document can't be retrieved. For GC expressions this will happen in the client as part of the feed routing logic, and it may also happen on backend nodes whose parent document set is incomplete (in case of node failures etc.). It is therefore important that you have this in mind when writing GC selections using imported fields. + +When you specify a selection criteria in a `<document>` tag, you're stating what a document must satisfy in order to be fed into the content cluster and to be kept there. + +As an example, imagine a document type `music_recording` with an imported field `artist_is_cool` that points to a boolean field `is_cool` in a parent `artist` document. If you only want your cluster to retain recordings from artists that are certifiably cool, you might be tempted to write a selection like the following: + +```js +<document type="music_recording" + mode="index" + selection="music_recording.artist_is_cool == true"> +``` + +**This won't work as expected**, because this expression is evaluated as part of the feeding pipeline to figure out if a cluster should accept a given document. At that point in time, there is no access to the parent document. Consequently, the field will return `null` and the document won't be routed to the cluster. + +Instead, write your expressions to handle the case where the parent document _may not exist_: + +```js +<document type="music_recording" + mode="index" + selection="(music_recording.artist_is_cool == null) or (music_recording.artist_is_cool == true)"> +``` + +With this selection, we explicitly let a document be accepted into the cluster if its imported field is _not_ available. However, if it _is_ available, we allow it to be used for GC. + +## Locale / Character sets + +The language currently does not support character sets other than ASCII. Glob and regex matching of single characters are not guaranteed to match exactly one character, but might match a part of a character represented by multiple byte values. + +## Values + +The comparison operator compares two values. A value can be any of the following: + +<table className="vespa-table"> + <thead> + <tr> + <th></th> + <th></th> + </tr> + </thead> + <tbody> + <tr> + <td>Document field specification</td> + <td>Syntax: <code>{`<doctype>.<fieldpath>`}</code> <br/> Documents have a set of fields defined, depending on the document type. The field name is the identifier used for the field. This expression returns the value of the field, which can be an integer, a floating point number, a string, an array, or a map of these types. <br/> For multivalues, we support only the *equals* operator for comparison. The semantics is that the array returned by the fieldvalue must *contain* at least one element that matches the other side of the comparison. For maps, there must exist a key matching the comparison. <br/> The simplest use of the fieldpath is to specify a field, but for complex types please refer to <a href="/en/reference/schemas/document-field-path" aria-label="the field path syntax documentation">the field path syntax documentation</a>.</td> + </tr> + <tr> + <td>Id</td> + <td>Syntax: <code>{` id.[scheme|namespace|type|specific|user|group] `}</code> <br/> Each document has a document ID, uniquely identifying that document within a Vespa installation. The id operator returns the string identifier, or if an optional argument is given, a part of the id. <br/> - scheme (id) <br/> - namespace (to separate different users' data) <br/> - type (specified in the id scheme) <br/> - specific (User specified part to distinguish documents within a namespace) <br/> - user (The number specified in document IDs using the n= modifier) <br/> - group (The string group specified in document IDs using the g= modifier)</td> + </tr> + <tr> + <td>null</td> + <td>The value null can be given to specify nothingness. For instance, a field specification for a document not containing the field will evaluate to null, so the comparison 'music.artist == null' will select all documents that don't have the artist field set. 'id.user == null' will match all documents that don't use the <code>{`n=`}</code> <a href="/en/schemas/documents#id-scheme" aria-label="document ID scheme">document ID scheme</a>. Tensor fields can *only* be compared against null. It's not possible to write a document selection that uses the *contents* of tensor fields—only their presence can be checked.</td> + </tr> + <tr> + <td>Number</td> + <td>A value can be a number, either an integer or a floating point number. Type of number is insignificant. You don't have to use the same type of number on both sides of a comparison. For instance '3.0 < 4' will match, and '3.0 == 3' will probably match (operator == is generally not advised for floating point numbers due to rounding issues). Numbers can be written in multiple ways - examples: <pre><code>{`1234 -234 +53 +534.34 543.34e4 -534E-3 0.2343e-8`}</code></pre></td> + </tr> + <tr> + <td>Strings</td> + <td>A string value is given quoted with double quotes (i.e. "mystring"). The string is interpreted as an ASCII string. Only ASCII values 32 to 126 can be used unescaped, except for the characters <code>{`\\`}</code> and <code>{`"`}</code> which must be escaped. <ul><li>Newline: <code>{`\\n`}</code></li><li>Carriage return: <code>{`\\r`}</code></li><li>Tab: <code>{`\\t`}</code></li><li>Form feed: <code>{`\\f`}</code></li><li>Quotation mark (<code>{`"`}</code>): <code>{`\\"`}</code></li><li>Any other character: <code>{`\\x##`}</code> (where <code>{`##`}</code> is a two-digit hexadecimal number specifying the ASCII value)</li></ul></td> + </tr> + </tbody> +</table> + +### Value arithmetics + +You can do arithmetics on values. The common arithmetics operators addition `+`, subtraction `-`, multiplication `*`, division `/` and modulo `%` are supported. + +### Functions + +Functions are called on something and returns a value that can be used in comparison expressions: + +<table className="vespa-table"> + <thead> + <tr> + <th></th> + <th></th> + </tr> + </thead> + <tbody> + <tr> + <td>Value functions</td> + <td>A value function takes a value, does something with it and returns a value which can be of any type. <br/> - *abs()* Called on a numeric type, returns the absolute value of that numeric type. That is -3 returns 3 and -4.3 returns 4.3. <br/> - *hash()* Calculates an MD5 hash of whatever value it is called on. The result is a signed 64-bit integer. (Use abs() after if you want to only get positive hash values). <br/> - *lowercase()* Called on a string value to turn upper case characters into lower case ones. <Note>**NOTE:** This only works for the characters 'a' through 'z', no locale support.</Note></td> + </tr> + <tr> + <td>Document type functions</td> + <td>Some functions can take a document type instead of a value, and return a value based on the type. <br/> - *version()* The <code>{`version()`}</code> function returns the version number of a document type.</td> + </tr> + </tbody> +</table> + +#### Now function + +Document selection provides a _now()_ function, which returns the current date timestamp. Use this to filter documents by age, typically for [garbage collection](/en/reference/applications/services/content#documents). + +**Example**: If you have a long field _inserttimestamp_ in your `music` schema, this expression will only match documents from the last two hours: + +`music.inserttimestamp > now() - 7200` + +## Using imported fields in selections + +When using [parent-child](/en/schemas/parent-child) you can refer to simple imported fields (i.e. top-level primitive fields) in selections as if they were regular fields in the child document type. Complex fields (collections, structures etc.) are not supported. + +<Danger> + **Important:** special care needs to be taken when using document selections referencing imported fields, especially if using these are part of garbage collection expressions. If an imported field references a document that cannot be accessed at evaluation time, the imported field behaves as if it had been a regular, non-present field in the child document. In other words, it will return the special `null` value. +</Danger> + +See [comparisons with missing fields (null values)](#comparisons-with-missing-fields-null-values) for a more detailed discussion of null-semantics and how to write selections that handle these in a well-defined manner. In particular, read [null behavior with imported fields](#null-behavior-with-imported-fields) if you're writing GC selections. + +### Example + +The following is an example of a 3-level parent-child hierarchy. + +Grandparent schema: + +```js +schema grandparent { + document grandparent { + field a1 type int { + indexing: attribute | summary + } + } +} +``` + +Parent schema, with reference to grandparent: + +```js +schema parent { + document parent { + field a2 type int { + indexing: attribute | summary + } + field ref type reference<grandparent> { + indexing: attribute | summary + } + } + import field ref.a1 as a1 {} +} +``` + +Child schema, with reference to parent and (transitively) grandparent: + +```js +schema child { + document child { + field a3 type int { + indexing: attribute | summary + } + field ref type reference<parent> { + indexing: attribute | summary + } + } + import field ref.a1 as a1 {} + import field ref.a2 as a2 {} +} +``` + +Using these in document selection expressions is easy: + +Find all child docs whose grandparents have an `a1` greater than 5: + +`child.a1 > 5` + +Find all child docs whose parents have an `a2` of 10 and grandparents have `a1` of 4: + +`child.a1 == 10 and child.a2 == 4` + +Find all child docs where the parent document cannot be found (or where the referenced field is not set in the parent): + +`child.a2 == null` + +Note that when visiting `child` documents we only ever access imported fields via the **child** document type itself. + +A much more complete list usage examples for the above document schemas and reference relations can be found in the [imported fields in selections](https://github.com/vespa-engine/system-test/blob/master/tests/search/parent_child/imported_fields_in_selections.rb) system test. This test covers both the visiting and GC cases. + +## Constraints + +Language identifiers restrict what can be used as document type names. The following values are not valid document type names: _true, false, and, or, not, id, null_ + +## Grammar - EBNF of the language + +To simplify, double casing of strings has not been included. The identifiers "null", "true", "false" etc. can be written in any case, including mixed case. + +```js expandable +nil = "null" ; +bool = "true" | "false" ; +posdigit = '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' ; +digit = '0' | posdigit ; +hexdigit = digit | 'a' | 'b' | 'c' | 'd' | 'e' | 'f' + | 'A' | 'B' | 'C' | 'D' | 'E' | 'F' ; +integer = ['-' | '+'], posdigit, { digit } ; +float = ['-' | '+'], digit, { digit }, + ['.' , { digit }, [ ('e' | 'E'), posdigit, { digit }] ] ; +number = float | integer ; +stdchars = ? All ASCII chars except '\\', '"', 0 - 31 and 127 - 255 ? ; +alpha = ? ASCII characters in the range a-z and A-Z ? ; +alphanum = alpha | digit ; +space = ( ' ' | '\t' | '\f' | '\r' | '\n' ) ; +string = '"', { stdchars | ( '\\', ( 't' | 'n' | 'f' | 'r' | '"' ) ) + | ( "\\x", hexdigit, hexdigit ) }, '"' ; +doctype = alpha, { alphanum } ; +fieldname = { alphanum '{' |'}' | '[' | ']' '.' } ; +function = alpha, { alphanum } ; +idarg = "scheme" | "namespace" | "type" | "specific" | "user" | "group" ; +searchcolumnarg = integer ; +operator = ">=" | ">" | "==" | "=~" | "=" | "<=" | "<" | "!=" ; +idspec = "id", ['.', idarg] ; +searchcolumnspec = "searchcolumn", ['.', searchcolumnarg] ; +fieldspec = doctype, ( function | ('.', fieldname) ) ; +value = ( valuegroup | nil | number | string | idspec | searchcolumnspec | fieldspec ), + { function } ; +valuefuncmod = ( valuegroup | value ), '%', + ( valuefuncmod | valuegroup | value ) ; +valuefuncmul = ( valuefuncmod | valuegroup | value ), ( '*' | '/' ), + ( valuefuncmul | valuefuncmod | valuegroup | value ) ; +valuefuncadd = ( valuefuncmul | valuefuncmod | valuegroup | value ), + ( '+' | '-' ), + ( valuefuncadd | valuefuncmul | valuefuncmod | valuegroup + | value ) ; +valuegroup = '(', arithmvalue, ')' ; +arithmvalue = ( valuefuncadd | valuefuncmul | valuefuncmod | valuegroup + | value ) ; +comparison = arithmvalue, { space }, operator, { space }, + arithmvalue ; +leaf = bool | comparison | fieldspec | doctype ; +not = "not", { space }, ( group | leaf ) ; +and = ( not | group | leaf ), { space }, "and", { space }, + ( and | not | group | leaf ) ; +or = ( and | not | group | leaf ), { space }, "or", { space }, + ( or | and | not | group | leaf ) ; +group = '(', { space }, ( or | and | not | group | leaf ), + { space }, ')' ; +expression = ( or | and | not | group | leaf ) ; +``` \ No newline at end of file diff --git a/en/reference/writing/indexing-language.html b/en/reference/writing/indexing-language.html deleted file mode 100644 index e832956d52..0000000000 --- a/en/reference/writing/indexing-language.html +++ /dev/null @@ -1,756 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Indexing language reference" -redirect_from: -- /en/reference/advanced-indexing-language.html -- /en/reference/indexing-language-reference.html ---- - -<p> -This reference documents the full Vespa <em>indexing language</em>. -If more complex processing of input data is required, implement a -<a href="../../applications/document-processors.html">document processor</a>. -</p><p> -The indexing language is analogous to UNIX pipes, in that statements -consists of expressions separated by the <i>pipe</i> symbol -where the output of each expression is the input of the next. -Statements are terminated by semicolon and are independent of each -other (except when using variables). -</p> -<p>Find examples in the <a href="/en/writing/indexing.html">indexing</a> guide.</p> - - - -<h2 id="indexing-script">Indexing script</h2> -<p> -An indexing script is a sequence of <a href="#indexing-statement">indexing -statements</a> separated by a semicolon (<code>;</code>). A script is -executed statement-by-statement, in order, one document at a time. -</p><p> -Vespa derives one indexing script per search cluster based on the search -definitions assigned to that cluster. As a document is fed to a search -cluster, it passes through the corresponding -<a href="../applications/services/content.html#document-processing">indexing cluster</a>, which runs the -document through its indexing script. -Note that this also happens whenever the document is -<a href="../../operations/reindexing.html">reindexed</a>, so expressions such as <a href="#now">now</a> must -be thought of as the time the document was (last) <em>indexed</em>, not when it was <em>fed</em>. -</p><p> -You can examine the indexing script generated for a specific search -cluster by retrieving the configuration of the indexing document processor. -</p> -<pre> -$ vespa-get-config -i search/cluster.<cluster-name> -n vespa.configdefinition.ilscripts -</pre> -<p> -The current <em>execution value</em> is set to <code>null</code> prior to -executing a statement. -</p> - - - -<h2 id="indexing-statement">Indexing statement</h2> -<p> -An indexing statement is a sequence of <a href="#indexing-expression">indexing -expressions</a> separated by a pipe (<code>|</code>). -A statement is executed expression-by-expression, in order. -</p><p> -Within a statement, the execution value is passed from one expression to the next. -</p><p> - The simplest of statements passes the value of an input field into an attribute: -</p> -<pre class="code"> -input year | attribute year; -</pre> -<p> -The above statement consists of 2 expressions; <code>input year</code> and -<code>attribute year</code>. The former sets the execution value to the -value of the "year" field of the input document. The latter writes the -current execution value into the attribute "year". -</p> - - - -<h2 id="indexing-expression">Indexing expression</h2> - - -<h3 id="primitives">Primitives</h3> - -<p> -A string, numeric literal and true/false can be used as an expression to explicitly - set the execution value. Examples: <code>"foo"</code>, <code>69</code>, <code>true</code>). -</p> - -<h3 id="outputs">Outputs</h3> -<p> -An output expression is an expression that writes the current execution -value to a document field. These expressions also double as the indicator -for the type of field to construct (i.e. attribute, index or summary). It -is important to note that you <u>can not</u> assign different values to -the same field in a single document (e.g. <code>attribute | lowercase | -index</code> is <strong>illegal</strong> and will not deploy). -</p> -<table class="table"> - <thead> - <tr> - <th>Expression</th> - <th>Description</th> - </tr> - </thead><tbody> - <tr> - <td><code>attribute</code></td> - <td> - Writes the execution value to the current field. During deployment, - this indicates that the field should be stored as an attribute. - </td> - </tr> - <tr> - <td><code>index</code></td> - <td> - Writes the execution value to the current field. During deployment, - this indicates that the field should be stored as an index field. - </td> - </tr> - <tr> - <td><code>summary</code></td> - <td> - Writes the execution value to the current field. During deployment, - this indicates that the field should be included in the document - summary. - </td> - </tr> - </tbody> -</table> - - -<h3 id="arithmetics">Arithmetics</h3> -<p> -Indexing statements can contain any combination of arithmetic operations, -as long as the operands are numeric values. In case you need to convert -from string to numeric, or convert from one numeric type to another, -use the applicable <a href="#converters">converter</a> expression. -The supported arithmetic operators are: -</p> -<table class="table"> - <thead> - <tr> - <th>Operator</th> - <th>Description</th> - </tr> - </thead><tbody> - <tr> - <td style="white-space: nowrap;"><code><lhs> + <rhs></code></td> - <td> - Sets the execution value to the result of adding of the execution - value of the <code>lhs</code> expression with that of - the <code>rhs</code> expression. - </td> - </tr> - <tr> - <td><code><lhs> - <rhs></code></td> - <td> - Sets the execution value to the result of subtracting of the execution - value of the <code>lhs</code> expression with that of - the <code>rhs</code> expression. - </td> - </tr> - <tr> - <td><code><lhs> * <rhs></code></td> - <td> - Sets the execution value to the result of multiplying of the execution - value of the <code>lhs</code> expression with that of - the <code>rhs</code> expression. - </td> - </tr> - <tr> - <td><code><lhs> / <rhs></code></td> - <td> - Sets the execution value to the result of dividing of the execution - value of the <code>lhs</code> expression with that of - the <code>rhs</code> expression. - </td> - </tr> - <tr> - <td><code><lhs> % <rhs></code></td> - <td> - Sets the execution value to the remainder of dividing the execution - value of the <code>lhs</code> expression with that of - the <code>rhs</code> expression. - </td> - </tr> - <tr> - <td><code><lhs> . <rhs></code></td> - <td> - Sets the execution value to the concatenation of the execution value - of the <code>lhs</code> expression with that of the <code>rhs</code> - expression. If <em>both</em> <code>lhs</code> and <code>rhs</code> are - collection types, this operator will append <code>rhs</code> - to <code>lhs</code> (if any operand is null, it is treated as an empty - collection). If not, this operator concatenates the string - representations of <code>lhs</code> and <code>rhs</code> (if any - operand is null, the result is null). - </td> - </tr> - </tbody> -</table> -<p id="parenthesis"> - You may use parenthesis to declare precedence of execution (e.g. <code>(1 - + 2) * 3</code>). This also works for more advanced array concatenation - statements such as <code>(input str_a | split ',') . (input str_b | split - ',') | index arr</code>. -</p> - - -<h3 id="converters">Converters</h3> - -<p>These expressions let you convert from one data type to another.</p> - -<table class="table"> - <thead> - <tr> - <th>Converter</th> - <th>Input</th> - <th>Output</th> - <th>Description</th> - </tr> - </thead><tbody> -<tr> - <td><code>binarize [threshold]</code></td> - <td>Any tensor</td> - <td>Any tensor</td> - <td> - <p id="binarize"> - Replaces all values in a tensor by 0 or 1. - This takes an optional argument specifying the threshold a value needs to be larger than to be - replaced by 1 instead of 0. The default threshold is 0. - This is useful to create a suitable input to <a href="#pack_bits">pack_bits</a>. - </p> - </td> -</tr> -<tr> - <td><code>embed [id] [args]</code></td> - <td>String</td> - <td>A tensor</td> - <td><p id="embed">Invokes an <a href="../../rag/embedding.html">embedder</a> to convert a text to one or more vector embeddings. - The type of the output tensor is what is required by the following expression (as supported by the specific embedder). - Arguments are given space separated, as in <code>embed colbert chunk</code>. - The first argument and can be omitted when only a single embedder is configured. - Any additional arguments are passed to the embedder implementation. - If the same chunk expression with the same input occurs multiple times in a schema, - its value will only be computed once. - </p> - </td> -</tr> -<tr> - <td><code>chunk id [args]</code></td> - <td>String</td> - <td>A tensor</td> - <td><p id="chunk">Invokes a which convert a string into an array of strings. - Arguments are given space separated, as in <code>chunk fixed-length 512</code>. - The id of the chunker to use is required and can be a chunker bundled with Vespa, - or any chunker component added in the services.xml, see the - <a href="../rag/chunking.html">chunking reference</a>. - Any additional arguments are passed to the chunker implementation. - If the same chunk expression with the same input occurs multiple times in a schema, - its value will only be computed once. - </p> - </td> -</tr> -<tr> - <td><code>hash</code></td> - <td>String</td> - <td>int or long</td> - <td><p id="hash">Converts the input to a hash value (using SipHash). - The hash will be int or long depending on the target field.</p></td> -</tr> -<tr> - <td><code>pack_bits</code></td> - <td>A tensor</td> - <td>A tensor</td> - <td> - <p id="pack_bits"> - Packs the values of a binary tensor into bytes with 1 bit per value in big-endian order. - The input tensor must have a single dense dimension. - It can have any value type and any number of sparse dimensions. - Values that are not 0 or 1 will be binarized with 0 as the threshold. - </p> - <p>The output tensor will have:</p> - <ul> - <li><code>int8</code> as the value type.</li> - <li>The dense dimension size divided by 8 (rounded upwards to integer).</li> - <li>The same sparse dimensions as before.</li> - </ul> - <p> - The resulting tensor can be unpacked during ranking using - <a href="../ranking/ranking-expressions.html#unpack-bits">unpack_bits</a>. - A tensor can be converted to binary form suitable as input to this by the - <a href="#binarize">binarize function</a>. - </p> - </td> - </tr> - <tr> - <td><code>to_array</code></td> - <td>Any</td> - <td>Array<inputType></td> - <td><p id="to_array">Converts the execution value to a single-element array.</p></td> - </tr> - <tr> - <td><code>to_byte</code></td> - <td>Any</td> - <td>Byte</td> - <td> - <p id="to_byte"> - Converts the execution value to a byte. This will throw a - NumberFormatException if the string representation of the execution - value does not contain a parseable number.</p> - </td> - </tr> - <tr> - <td><code>to_double</code></td> - <td>Any</td> - <td>Double</td> - <td> - <p id="to_double"> - Converts the execution value to a double. This will throw a - NumberFormatException if the string representation of the execution - value does not contain a parseable number. - </p> - </td> - </tr> - <tr> - <td><code>to_float</code></td> - <td>Any</td> - <td>Float</td> - <td> - <p id="to_float"> - Converts the execution value to a float. This will throw a - NumberFormatException if the string representation of the execution - value does not contain a parseable number.</p> - </td> - </tr> - <tr> - <td><code>to_int</code></td> - <td>Any</td> - <td>Integer</td> - <td> - <p id="to_int"> - Converts the execution value to an int. This will throw a - NumberFormatException if the string representation of the execution - value does not contain a parseable number.</p> - </td> - </tr> - <tr> - <td><code>to_long</code></td> - <td>Any</td> - <td>Long</td> - <td> - <p id="to_long"> - Converts the execution value to a long. This will throw a - NumberFormatException if the string representation of the execution - value does not contain a parseable number.</p> - </td> - </tr> - <tr> - <td><code>to_bool</code></td> - <td>Any</td> - <td>Bool</td> - <td> - <p id="to_bool"> - Converts the execution value to a boolean type. - If the input is a string it will become true if it is not empty. - If the input is a number it will become true if it is != 0.</p> - </td> - </tr> - <tr> - <td><code>to_pos</code></td> - <td>String</td> - <td>Position</td> - <td> - <p id="to_post"> - Converts the execution value to a position struct. The input format - must be either a) <code>[N|S]<val>;[E|W]<val></code>, or - b) <code>x;y</code>.</p> - </td> - </tr> - <tr> - <td><code>to_string</code></td> - <td>Any</td> - <td>String</td> - <td><p id="to_string">Converts the execution value to a string.</p></td> - </tr> - <tr> - <td><code>to_uri</code></td> - <td>String</td> - <td>Uri</td> - <td><p id="to_uri">Converts the execution value to a URI struct</p></td> - </tr> - <tr> - <td><code>to_wset</code></td> - <td>Any</td> - <td>WeightedSet<inputType></td> - <td> - <p id="to_wset"> - Converts the execution value to a single-element weighted set with - default weight.</p> - </td> - </tr> - <tr> - <td><code>to_epoch_second</code></td> - <td>String</td> - <td>Long</td> - <td> - <p id="to_epoch_second"> - Converts an ISO-8601 instant formatted String to Unix epoch (or Unix time or POSIX time or Unix timestamp) which - is the number of seconds elapsed since January 1, 1970, UTC. The converter uses - <a href="https://docs.oracle.com/en/java/javase/20/docs/api/java.base/java/time/Instant.html#parse(java.lang.CharSequence)"> - java.time.Instant.parse - </a> to parse the input string value. This will throw a DateTimeParseException if the input cannot be parsed. Examples: - </p> - <ul> - <li><code>2023-12-24T17:00:43.000Z</code> is converted to <code>1703437243L</code></li> - <li><code>2023-12-24T17:00:43Z</code> is converted to <code>1703437243L</code></li> - <li><code>2023-12-24T17:00:43.431Z</code> is converted to <code>1703437243L</code></li> - <li><code>2023-12-24T17:00:43.431+00:00</code> is converted to <code>1703437243L</code></li> - </ul> - </td> - </tr> - </tbody> -</table> - - -<h3 id="other-expressions">Other expressions</h3> -<p> -The following are the unclassified expressions available: -</p> -<table class="table"> - <thead> - <tr> - <th>Expression</th><th>Description</th> - </tr> - </thead><tbody> - <tr> - <td><code>_</code></td> - <td><p id="_"> - Returns the current execution value. - This is useful, e.g., to prepend some other value to the current execution value, - see <a href="/en/writing/indexing.html#execution-value-example">this example</a>. - </p></td> - </tr> - <tr> - <td><code>attribute <fieldName></code></td> - <td><p id="attribute">Writes the execution value to the named attribute field.</p></td> - </tr> - <tr> - <td><code>base64decode</code></td> - <td> - <p id="base64decode"> - If the execution value is a string, it is base-64 decoded to a long integer. - If it is not a string, the execution value is set to <code>Long.MIN_VALUE</code>. - </p> - </td> - </tr> - <tr> - <td><code>base64encode</code></td> - <td> - <p id="base64encode"> - If the execution value is a long integer, it is base-64 encoded to a string. - If it is not a long integer, the execution value is set to <code>null</code>. - </p> - </td> - </tr> - <tr> - <td><code>echo</code></td> - <td> - <p id="echo">Prints the execution value to standard output, for debug purposes.</p> - </td> - </tr> - <tr> - <td><code>flatten</code></td> - <td> - <p id="flatten"></p> - {% include deprecated.html content='Use <a href="/en/reference/schemas/schemas.html#tokens">tokens</a> - in the schema instead.' %} - </td> - </tr> - <tr> - <td><code>for_each { <script> }</code></td> - <td> - <p id="for_each"> - Executes the given indexing script for each element in the execution value. - Here, element refers to each element in a collection, or each field value in a struct. - </p> - </td> - </tr> - <tr> - <td><code>generate [id] [args]</code></td> - <td> - <p id="generate"> - Invokes a <a href="../../rag/document-enrichment">field generator</a> to generate a - field valued from an input string. - The argument is the id of the <code>FieldGenerator</code> component as described in - <a href="../../rag/document-enrichment">Document enrichment with LLMs</a>. - If the same generate expression with the same input occurs multiple times in a schema, - its value will only be computed once. - </p> - </td> - </tr> - <tr> - <td><code>get_field <fieldName></code></td> - <td> - <p id="get_field"> - Retrieves the value of the named field from the execution value - (which needs to be either a document or a struct), - and sets it as the new execution value. - </p> - </td> - </tr> - <tr> - <td><code>get_language</code></td> - <td> - <p id="get_language"> - Retrieves the code of the last assigned or detected language, ur "un" for "unknown" if no - language has been assigned or detected. Language is detected when a string is tokenized or embedded, - so this can be used to retrieve the language detected by a previous field executing one such operation, - e.g. by indexing. - </p> - </td> - </tr> - <tr> - <td><code>get_value <key></code></td> - <td> - <p id="get_value"> - Retrieves a value from an input Map. The key can be a number, identifier or quoted string. - If the given key does not have a value, the output is empty. - </p> - </td> - </tr> - <tr> - <td><code>get_var <varName></code></td> - <td> - <p id="get_var"> - Retrieves the value of the named variable from the execution context and sets it as the execution value. - Note that variables are scoped to the indexing script of the current field. - </p> - </td> - </tr> - <tr> - <td><code>hex_decode</code></td> - <td> - <p id="hex_decode"> - If the execution value is a string, it is parsed as a long integer in base-16. - If it is not a string, the execution value is set to <code>Long.MIN_VALUE</code>. - </p> - </td> - </tr> - <tr> - <td><code>hex_encode</code></td> - <td> - <p id="hex_encode"> - If the execution value is a long integer, - it is converted to a string representation of an unsigned integer in base-16. - If it is not a long integer, the execution value is set to <code>null</code>. - </p> - </td> - </tr> - <tr> - <td><code>hostname</code></td> - <td> - <p id="hostname">Sets the execution value to the name of the host computer.</p> - </td> - </tr> - <tr> - <td><code> - if (<left> <cmp> <right>) {<br/> -     <trueScript><br /> - }<br /> - [ else { <falseScript> } ] - </code></td> - <td> - <p id="if"> - Executes the <code>trueScript</code> if the conditional evaluates to true, - or the <code>falseScript</code> if it evaluates to false. - If either <code>left</code> or <code>right</code> is null, no expression is executed. - The value produced is the value returned from the chosen branch, and these must - produce values of compatible types (or none). - </p> - </td> - </tr> - <tr> - <td><code>index <fieldName></code></td> - <td><p id="index">Writes the execution value to the named index field.</p></td> - </tr> - <tr> - <td><code>input <fieldName></code></td> - <td> - <p id="input"> - Retrieves the value of the named field from the document and sets it as the execution value. - The field name may contain '.' characters to retrieve nested struct fields. - </p> - </td> - </tr> - <tr> - <td><code>join "<delim>"</code></td> - <td> - <p id="join"> - Creates a single string by concatenating the string representation of - each array element of the execution value. - This function is useful or indexing data from a <a href="../../querying/searching-multivalue-fields">multivalue</a> field - into a singlevalue field. - </p> - </td> - </tr> - <tr> - <td><code>lowercase</code></td> - <td><p id="lowercase">Lowercases all the strings in the execution value.</p></td> - </tr> - <tr> - <td><code>ngram <size></code></td> - <td> - <p id="ngram">Adds ngram annotations to all strings in the execution value.</p> - </td> - </tr> - <tr> - <td><code>normalize</code></td> - <td> - <p id="normalize"> - <a href="../../linguistics/linguistics-opennlp.html#normalization">normalize</a> the input data. - The corresponding query command for this function is <code>normalize</code>. - </p> - </td> - </tr> - <tr> - <td><code>now</code></td> - <td> - <p id="now"> - Outputs the current system clock time as a UNIX timestamp, - i.e. seconds since 0 hours, 0 minutes, 0 seconds, January 1, 1970, Coordinated Universal Time (Epoch). - </p> - </td> - </tr> - <tr> - <td><code>random [ <max> ]</code></td> - <td> - <p id="random"> - Returns a random integer value. - Lowest value is 0 and the highest value is determined either by the argument or, - if no argument is given, the execution value. - </p> - </td> - </tr> - <tr> - <td><code> - sub-expression1 || sub-expression2 || ... - </code></td> - <td> - <p id="choice"> - Returns the value of the first alternative sub-expression which returns a non-null value. - See <a href="/en/writing/indexing.html#choice-example">this example</a>. - </p> - </td> - </tr> - <tr> - <td><code> - select_input { <br /> - ( case <fieldName>: <statement>; )* <br /> - } - </code></td> - <td> - <p id="select_input"> - Performs the statement that corresponds to the <strong>first</strong> named - field that is not empty (see <a href="/en/writing/indexing.html#select-input-example">example</a>). - </p> - </td> - </tr> - <tr> - <td><code>set_language</code></td> - <td> - <p id="set_language"> - Sets the language of this document to the string representation of the execution value. - Parses the input value as an RFC 3066 language tag, - and sets that language for the current document. - This affects the behavior of the <a href="../../linguistics/linguistics-opennlp.html#tokenization">tokenizer</a>. - The recommended use is to have one field in the document containing the language code, - and that field should be the <strong>first</strong> field in the document, - as it will only affect the fields defined <strong>after</strong> it in the schema. - Read <a href="../../linguistics/linguistics.html#language-handling">linguistics</a> - for more information on how language settings are applied. - </p> - </td> - </tr> - <tr> - <td><code>set_var <varName></code></td> - <td> - <p id="set_var"> - Writes the execution value to the named variable. - Note that variables are scoped to the indexing script of the current field. - </p> - </td> - </tr> - <tr> - <td><code>substring <from> <to></code></td> - <td> - <p id="substring"> - Replaces all strings in the execution value by a substring of the respective value. - The arguments are inclusive-from and exclusive-to. - Both arguments are clamped during execution to avoid going out of bounds. - </p> - </td> - </tr> - <tr> - <td><code>split <regex></code></td> - <td> - <p id="split"> - Splits the string representation of the execution value into a string array using the given regex pattern. - This function is useful for creating <a href="../../querying/searching-multivalue-fields">multivalue</a> - fields such as an integer array out of a string of comma-separated numbers. - </p> - </td> - </tr> - <tr> - <td><code>summary <fieldName></code></td> - <td> - <p id="summary"> - Writes the execution value to the named summary field. During deployment, this indicates that the field should be included in the document summary. - </p> - </td> - </tr> - <tr> - <td><code> - switch { <br /> - ( case '<value>': <caseStatement>; )* <br /> - [ default: <defaultStatement>; ] <br /> - } - </code></td> - <td> - <p id="switch"> - Performs the statement of the case whose value matches the string - representation of the execution value (see <a href="/en/writing/indexing.html#switch-example">example</a>). - </p> - </td> - </tr> - <tr> - <td><code>tokenize [ normalize ] [ stem ]</code></td> - <td> - <p id="tokenize"> - Adds linguistic annotations to all strings in the execution value. - Read <a href="../../linguistics/linguistics.html">linguistics</a> for more information. - </p> - </td> - </tr> - <tr> - <td><code>trim</code></td> - <td> - <p id="trim">Removes leading and trailing whitespace from all strings in the execution value.</p> - </td> - </tr> - <tr> - <td><code>uri</code></td> - <td> - <p id="uri"> - Converts all strings in the execution value to a URI struct. - If a string could not be converted, it is removed. - </p> - </td> - </tr> - </tbody> -</table> diff --git a/en/reference/writing/indexing-language.mdx b/en/reference/writing/indexing-language.mdx new file mode 100644 index 0000000000..fc12fe3b7c --- /dev/null +++ b/en/reference/writing/indexing-language.mdx @@ -0,0 +1,389 @@ +--- +title: "Indexing language reference" +sidebarTitle: "indexing language" +--- + +This reference documents the full Vespa _indexing language_. If more complex processing of input data is required, implement a [document processor](/en/applications/document-processors). + +The indexing language is analogous to UNIX pipes, in that statements consists of expressions separated by the _pipe_ symbol where the output of each expression is the input of the next. Statements are terminated by semicolon and are independent of each other (except when using variables). + +Find examples in the [indexing](/en/writing/indexing) guide. + +## Indexing script + +An indexing script is a sequence of [indexing statements](#indexing-statement) separated by a semicolon (`;`). A script is executed statement-by-statement, in order, one document at a time. + +Vespa derives one indexing script per search cluster based on the search definitions assigned to that cluster. As a document is fed to a search cluster, it passes through the corresponding [indexing cluster](/en/reference/applications/services/content#document-processing), which runs the document through its indexing script. Note that this also happens whenever the document is [reindexed](/en/operations/reindexing), so expressions such as [now](#now) must be thought of as the time the document was (last) _indexed_, not when it was _fed_. + +You can examine the indexing script generated for a specific search cluster by retrieving the configuration of the indexing document processor. + +```sh +$ vespa-get-config -i search/cluster.<cluster-name> -n vespa.configdefinition.ilscripts +``` + +The current _execution value_ is set to `null` prior to executing a statement. + +## Indexing statement + +An indexing statement is a sequence of [indexing expressions](#indexing-expression) separated by a pipe (`|`). A statement is executed expression-by-expression, in order. + +Within a statement, the execution value is passed from one expression to the next. + +The simplest of statements passes the value of an input field into an attribute: + +```sh +input year | attribute year; +``` + +The above statement consists of 2 expressions; `input year` and `attribute year`. The former sets the execution value to the value of the "year" field of the input document. The latter writes the current execution value into the attribute "year". + +## Indexing expression + +### Primitives + +A string, numeric literal and true/false can be used as an expression to explicitly set the execution value. Examples: `"foo"`, `69`, `true`). + +### Outputs + +An output expression is an expression that writes the current execution value to a document field. These expressions also double as the indicator for the type of field to construct (i.e. attribute, index or summary). It is important to note that you can not assign different values to the same field in a single document (e.g. `attribute | lowercase | +index` is **illegal** and will not deploy). + +<table className="vespa-table"> + <thead> + <tr> + <th>Expression</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td><code>{`attribute`}</code></td> + <td>Writes the execution value to the current field. During deployment, this indicates that the field should be stored as an attribute.</td> + </tr> + <tr> + <td><code>{`index`}</code></td> + <td>Writes the execution value to the current field. During deployment, this indicates that the field should be stored as an index field.</td> + </tr> + <tr> + <td><code>{`summary`}</code></td> + <td>Writes the execution value to the current field. During deployment, this indicates that the field should be included in the document summary.</td> + </tr> + </tbody> +</table> + +### Arithmetics + +Indexing statements can contain any combination of arithmetic operations, as long as the operands are numeric values. In case you need to convert from string to numeric, or convert from one numeric type to another, use the applicable [converter](#converters) expression. The supported arithmetic operators are: + +<table className="vespa-table"> + <thead> + <tr> + <th>Operator</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td><code>{`<lhs> + <rhs>`}</code></td> + <td>Sets the execution value to the result of adding of the execution value of the <code>{`lhs`}</code> expression with that of the <code>{`rhs`}</code> expression.</td> + </tr> + <tr> + <td><code>{`<lhs> - <rhs>`}</code></td> + <td>Sets the execution value to the result of subtracting of the execution value of the <code>{`lhs`}</code> expression with that of the <code>{`rhs`}</code> expression.</td> + </tr> + <tr> + <td><code>{`<lhs> * <rhs>`}</code></td> + <td>Sets the execution value to the result of multiplying of the execution value of the <code>{`lhs`}</code> expression with that of the <code>{`rhs`}</code> expression.</td> + </tr> + <tr> + <td><code>{`<lhs> / <rhs>`}</code></td> + <td>Sets the execution value to the result of dividing of the execution value of the <code>{`lhs`}</code> expression with that of the <code>{`rhs`}</code> expression.</td> + </tr> + <tr> + <td><code>{`<lhs> % <rhs>`}</code></td> + <td>Sets the execution value to the remainder of dividing the execution value of the <code>{`lhs`}</code> expression with that of the <code>{`rhs`}</code> expression.</td> + </tr> + <tr> + <td><code>{`<lhs> . <rhs>`}</code></td> + <td>Sets the execution value to the concatenation of the execution value of the <code>{`lhs`}</code> expression with that of the <code>{`rhs`}</code> expression. If _both_ <code>{`lhs`}</code> and <code>{`rhs`}</code> are collection types, this operator will append <code>{`rhs`}</code> to <code>{`lhs`}</code> (if any operand is null, it is treated as an empty collection). If not, this operator concatenates the string representations of <code>{`lhs`}</code> and <code>{`rhs`}</code> (if any operand is null, the result is null).</td> + </tr> + </tbody> +</table> + +You may use parenthesis to declare precedence of execution (e.g. `(1 + + 2) * 3`). This also works for more advanced array concatenation statements such as `(input str_a | split ',') . (input str_b | split + ',') | index arr`. + +### Converters + +These expressions let you convert from one data type to another. + +<table className="vespa-table"> + <thead> + <tr> + <th>Converter</th> + <th>Input</th> + <th>Output</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td><code>{`binarize [threshold]`}</code></td> + <td>Any tensor</td> + <td>Any tensor</td> + <td>Replaces all values in a tensor by 0 or 1. This takes an optional argument specifying the threshold a value needs to be larger than to be replaced by 1 instead of 0. The default threshold is 0. This is useful to create a suitable input to <a href="#pack_bits" aria-label="pack_bits">pack_bits</a>.</td> + </tr> + <tr> + <td><code>{`embed [id] [args]`}</code></td> + <td>String</td> + <td>A tensor</td> + <td>Invokes an <a href="/en/reference/rag/embedding" aria-label="embedder">embedder</a> to convert a text to one or more vector embeddings. The type of the output tensor is what is required by the following expression (as supported by the specific embedder). Arguments are given space separated, as in <code>{`embed colbert chunk`}</code>. The first argument and can be omitted when only a single embedder is configured. Any additional arguments are passed to the embedder implementation. If the same chunk expression with the same input occurs multiple times in a schema, its value will only be computed once.</td> + </tr> + <tr> + <td><code>{`chunk id [args]`}</code></td> + <td>String</td> + <td>A tensor</td> + <td>Invokes a which convert a string into an array of strings. Arguments are given space separated, as in <code>{`chunk fixed-length 512`}</code>. The id of the chunker to use is required and can be a chunker bundled with Vespa, or any chunker component added in the services.xml, see the <a href="/en/reference/rag/chunking" aria-label="chunking reference">chunking reference</a>. Any additional arguments are passed to the chunker implementation. If the same chunk expression with the same input occurs multiple times in a schema, its value will only be computed once.</td> + </tr> + <tr> + <td><code>{`hash`}</code></td> + <td>String</td> + <td>int or long</td> + <td>Converts the input to a hash value (using SipHash). The hash will be int or long depending on the target field.</td> + </tr> + <tr> + <td><code>{`pack_bits`}</code></td> + <td>A tensor</td> + <td>A tensor</td> + <td>Packs the values of a binary tensor into bytes with 1 bit per value in big-endian order. The input tensor must have a single dense dimension. It can have any value type and any number of sparse dimensions. Values that are not 0 or 1 will be binarized with 0 as the threshold. <br/> The output tensor will have: <br/> - <code>{`int8`}</code> as the value type. <br/> - The dense dimension size divided by 8 (rounded upwards to integer). <br/> - The same sparse dimensions as before. <br/> The resulting tensor can be unpacked during ranking using <a href="/en/reference/ranking/ranking-expressions#unpack-bits" aria-label="unpack_bits">unpack_bits</a>. A tensor can be converted to binary form suitable as input to this by the <a href="#binarize" aria-label="binarize function">binarize function</a>.</td> + </tr> + <tr> + <td><code>{`to_array`}</code></td> + <td>Any</td> + <td>Array<inputType></td> + <td>Converts the execution value to a single-element array.</td> + </tr> + <tr> + <td><code>{`to_byte`}</code></td> + <td>Any</td> + <td>Byte</td> + <td>Converts the execution value to a byte. This will throw a NumberFormatException if the string representation of the execution value does not contain a parseable number.</td> + </tr> + <tr> + <td><code>{`to_double`}</code></td> + <td>Any</td> + <td>Double</td> + <td>Converts the execution value to a double. This will throw a NumberFormatException if the string representation of the execution value does not contain a parseable number.</td> + </tr> + <tr> + <td><code>{`to_float`}</code></td> + <td>Any</td> + <td>Float</td> + <td>Converts the execution value to a float. This will throw a NumberFormatException if the string representation of the execution value does not contain a parseable number.</td> + </tr> + <tr> + <td><code>{`to_int`}</code></td> + <td>Any</td> + <td>Integer</td> + <td>Converts the execution value to an int. This will throw a NumberFormatException if the string representation of the execution value does not contain a parseable number.</td> + </tr> + <tr> + <td><code>{`to_long`}</code></td> + <td>Any</td> + <td>Long</td> + <td>Converts the execution value to a long. This will throw a NumberFormatException if the string representation of the execution value does not contain a parseable number.</td> + </tr> + <tr> + <td><code>{`to_bool`}</code></td> + <td>Any</td> + <td>Bool</td> + <td>Converts the execution value to a boolean type. If the input is a string it will become true if it is not empty. If the input is a number it will become true if it is != 0.</td> + </tr> + <tr> + <td><code>{`to_pos`}</code></td> + <td>String</td> + <td>Position</td> + <td>Converts the execution value to a position struct. The input format must be either a) <code>{`[N|S]<val>;[E|W]<val>`}</code>, or b) <code>{`x;y`}</code>.</td> + </tr> + <tr> + <td><code>{`to_string`}</code></td> + <td>Any</td> + <td>String</td> + <td>Converts the execution value to a string.</td> + </tr> + <tr> + <td><code>{`to_uri`}</code></td> + <td>String</td> + <td>Uri</td> + <td>Converts the execution value to a URI struct</td> + </tr> + <tr> + <td><code>{`to_wset`}</code></td> + <td>Any</td> + <td>WeightedSet<inputType></td> + <td>Converts the execution value to a single-element weighted set with default weight.</td> + </tr> + <tr> + <td><code>{`to_epoch_second`}</code></td> + <td>String</td> + <td>Long</td> + <td>Converts an ISO-8601 instant formatted String to Unix epoch (or Unix time or POSIX time or Unix timestamp) which is the number of seconds elapsed since January 1, 1970, UTC. The converter uses <a href="https://docs.oracle.com/en/java/javase/20/docs/api/java.base/java/time/Instant.html#parse(java.lang.CharSequence)" aria-label="java.time.Instant.parse">java.time.Instant.parse</a> to parse the input string value. This will throw a DateTimeParseException if the input cannot be parsed. Examples: <br/> - <code>{`2023-12-24T17:00:43.000Z`}</code> is converted to <code>{`1703437243L`}</code> <br/> - <code>{`2023-12-24T17:00:43Z`}</code> is converted to <code>{`1703437243L`}</code> <br/> - <code>{`2023-12-24T17:00:43.431Z`}</code> is converted to <code>{`1703437243L`}</code> <br/> - <code>{`2023-12-24T17:00:43.431+00:00`}</code> is converted to <code>{`1703437243L`}</code></td> + </tr> + </tbody> +</table> + +### Other expressions + +The following are the unclassified expressions available: + +<table className="vespa-table"> + <thead> + <tr> + <th>Expression</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td><code>{`_`}</code></td> + <td>Returns the current execution value. This is useful, e.g., to prepend some other value to the current execution value, see <a href="/en/writing/indexing#execution-value-example" aria-label="this example">this example</a>.</td> + </tr> + <tr> + <td><code>{`attribute <fieldName>`}</code></td> + <td>Writes the execution value to the named attribute field.</td> + </tr> + <tr> + <td><code>{`base64decode`}</code></td> + <td>If the execution value is a string, it is base-64 decoded to a long integer. If it is not a string, the execution value is set to <code>{`Long.MIN_VALUE`}</code>.</td> + </tr> + <tr> + <td><code>{`base64encode`}</code></td> + <td>If the execution value is a long integer, it is base-64 encoded to a string. If it is not a long integer, the execution value is set to <code>{`null`}</code>.</td> + </tr> + <tr> + <td><code>{`echo`}</code></td> + <td>Prints the execution value to standard output, for debug purposes.</td> + </tr> + <tr> + <td><code>{`flatten`}</code></td> + <td><Warning>**Deprecated:** Use <a href="/en/reference/schemas/schemas#tokens" aria-label="tokens">tokens</a> in the schema instead.</Warning></td> + </tr> + <tr> + <td><code>{`for_each { <script> }`}</code></td> + <td>Executes the given indexing script for each element in the execution value. Here, element refers to each element in a collection, or each field value in a struct.</td> + </tr> + <tr> + <td><code>{`generate [id] [args]`}</code></td> + <td>Invokes a <a href="/en/rag/document-enrichment" aria-label="field generator">field generator</a> to generate a field valued from an input string. The argument is the id of the <code>{`FieldGenerator`}</code> component as described in <a href="/en/rag/document-enrichment" aria-label="Document enrichment with LLMs">Document enrichment with LLMs</a>. If the same generate expression with the same input occurs multiple times in a schema, its value will only be computed once.</td> + </tr> + <tr> + <td><code>{`get_field <fieldName>`}</code></td> + <td>Retrieves the value of the named field from the execution value (which needs to be either a document or a struct), and sets it as the new execution value.</td> + </tr> + <tr> + <td><code>{`get_language`}</code></td> + <td>Retrieves the code of the last assigned or detected language, ur "un" for "unknown" if no language has been assigned or detected. Language is detected when a string is tokenized or embedded, so this can be used to retrieve the language detected by a previous field executing one such operation, e.g. by indexing.</td> + </tr> + <tr> + <td><code>{`get_value <key>`}</code></td> + <td>Retrieves a value from an input Map. The key can be a number, identifier or quoted string. If the given key does not have a value, the output is empty.</td> + </tr> + <tr> + <td><code>{`get_var <varName>`}</code></td> + <td>Retrieves the value of the named variable from the execution context and sets it as the execution value. Note that variables are scoped to the indexing script of the current field.</td> + </tr> + <tr> + <td><code>{`hex_decode`}</code></td> + <td>If the execution value is a string, it is parsed as a long integer in base-16. If it is not a string, the execution value is set to <code>{`Long.MIN_VALUE`}</code>.</td> + </tr> + <tr> + <td><code>{`hex_encode`}</code></td> + <td>If the execution value is a long integer, it is converted to a string representation of an unsigned integer in base-16. If it is not a long integer, the execution value is set to <code>{`null`}</code>.</td> + </tr> + <tr> + <td><code>{`hostname`}</code></td> + <td>Sets the execution value to the name of the host computer.</td> + </tr> + <tr> + <td><code>{` if (<left> <cmp> <right>) {     <trueScript> } [ else { <falseScript> } ] `}</code></td> + <td>Executes the <code>{`trueScript`}</code> if the conditional evaluates to true, or the <code>{`falseScript`}</code> if it evaluates to false. If either <code>{`left`}</code> or <code>{`right`}</code> is null, no expression is executed. The value produced is the value returned from the chosen branch, and these must produce values of compatible types (or none).</td> + </tr> + <tr> + <td><code>{`index <fieldName>`}</code></td> + <td>Writes the execution value to the named index field.</td> + </tr> + <tr> + <td><code>{`input <fieldName>`}</code></td> + <td>Retrieves the value of the named field from the document and sets it as the execution value. The field name may contain '.' characters to retrieve nested struct fields.</td> + </tr> + <tr> + <td><code>{`join "<delim>"`}</code></td> + <td>Creates a single string by concatenating the string representation of each array element of the execution value. This function is useful or indexing data from a <a href="/en/querying/searching-multivalue-fields" aria-label="multivalue">multivalue</a> field into a singlevalue field.</td> + </tr> + <tr> + <td><code>{`lowercase`}</code></td> + <td>Lowercases all the strings in the execution value.</td> + </tr> + <tr> + <td><code>{`ngram <size>`}</code></td> + <td>Adds ngram annotations to all strings in the execution value.</td> + </tr> + <tr> + <td><code>{`normalize`}</code></td> + <td><a href="/en/linguistics/linguistics-opennlp#normalization" aria-label="normalize">normalize</a> the input data. The corresponding query command for this function is <code>{`normalize`}</code>.</td> + </tr> + <tr> + <td><code>{`now`}</code></td> + <td>Outputs the current system clock time as a UNIX timestamp, i.e. seconds since 0 hours, 0 minutes, 0 seconds, January 1, 1970, Coordinated Universal Time (Epoch).</td> + </tr> + <tr> + <td><code>{`random [ <max> ]`}</code></td> + <td>Returns a random integer value. Lowest value is 0 and the highest value is determined either by the argument or, if no argument is given, the execution value.</td> + </tr> + <tr> + <td><code>{` sub-expression1 || sub-expression2 || ... `}</code></td> + <td>Returns the value of the first alternative sub-expression which returns a non-null value. See <a href="/en/writing/indexing#choice-example" aria-label="this example">this example</a>.</td> + </tr> + <tr> + <td><code>{` select_input { ( case <fieldName>: <statement>; )* } `}</code></td> + <td>Performs the statement that corresponds to the **first** named field that is not empty (see <a href="/en/writing/indexing#select-input-example" aria-label="example">example</a>).</td> + </tr> + <tr> + <td><code>{`set_language`}</code></td> + <td>Sets the language of this document to the string representation of the execution value. Parses the input value as an RFC 3066 language tag, and sets that language for the current document. This affects the behavior of the <a href="/en/linguistics/linguistics-opennlp#tokenization" aria-label="tokenizer">tokenizer</a>. The recommended use is to have one field in the document containing the language code, and that field should be the **first** field in the document, as it will only affect the fields defined **after** it in the schema. Read <a href="/en/linguistics/linguistics#language-handling" aria-label="linguistics">linguistics</a> for more information on how language settings are applied.</td> + </tr> + <tr> + <td><code>{`set_var <varName>`}</code></td> + <td>Writes the execution value to the named variable. Note that variables are scoped to the indexing script of the current field.</td> + </tr> + <tr> + <td><code>{`substring <from> <to>`}</code></td> + <td>Replaces all strings in the execution value by a substring of the respective value. The arguments are inclusive-from and exclusive-to. Both arguments are clamped during execution to avoid going out of bounds.</td> + </tr> + <tr> + <td><code>{`split <regex>`}</code></td> + <td>Splits the string representation of the execution value into a string array using the given regex pattern. This function is useful for creating <a href="/en/querying/searching-multivalue-fields" aria-label="multivalue">multivalue</a> fields such as an integer array out of a string of comma-separated numbers.</td> + </tr> + <tr> + <td><code>{`summary <fieldName>`}</code></td> + <td>Writes the execution value to the named summary field. During deployment, this indicates that the field should be included in the document summary.</td> + </tr> + <tr> + <td><code>{` switch { ( case '<value>': <caseStatement>; )* [ default: <defaultStatement>; ] } `}</code></td> + <td>Performs the statement of the case whose value matches the string representation of the execution value (see <a href="/en/writing/indexing#switch-example" aria-label="example">example</a>).</td> + </tr> + <tr> + <td><code>{`tokenize [ normalize ] [ stem ]`}</code></td> + <td>Adds linguistic annotations to all strings in the execution value. Read <a href="/en/linguistics/linguistics" aria-label="linguistics">linguistics</a> for more information.</td> + </tr> + <tr> + <td><code>{`trim`}</code></td> + <td>Removes leading and trailing whitespace from all strings in the execution value.</td> + </tr> + <tr> + <td><code>{`uri`}</code></td> + <td>Converts all strings in the execution value to a URI struct. If a string could not be converted, it is removed.</td> + </tr> + </tbody> +</table> \ No newline at end of file diff --git a/en/schemas/concrete-documents.html b/en/schemas/concrete-documents.html deleted file mode 100644 index 2eec4cfbd3..0000000000 --- a/en/schemas/concrete-documents.html +++ /dev/null @@ -1,200 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Concrete documents" -redirect_from: -- /en/concrete-documents.html ---- - -<p> -In <a href="../applications/document-processors.html">document processing</a>, -<code>setFieldValue()</code> and <code>getFieldValue()</code> -is used to access fields in a <code>Document</code>. -The data for each of the fields in the document instance is wrapped in field values. -If the documents use structs, they are handled the same way. Example: -</p> -<pre> -book.setFieldValue("title", new StringFieldValue("Moby Dick")); -</pre> -<p> -Alternatively, use code generation to get a <em>concrete document type</em>, -a <code>Document</code> subclass that represents the exact document type -(defined for example in the file <code>book.sd</code>). -To generate, include it in the build, plugins section in <em>pom.xml</em>: -</p> -<pre> -<plugin> - <groupId>com.yahoo.vespa</groupId> - <artifactId>vespa-documentgen-plugin</artifactId> - <!-- Find latest version at <a href="https://search.maven.org/search?q=g:com.yahoo.vespa%20a:vespa-documentgen-plugin">search.maven.org/search?q=g:com.yahoo.vespa%20a:vespa-documentgen-plugin</a> --> - <version>{{site.variables.vespa_version}}</version> - <configuration> - <span class="pre-hilite"><schemasDirectory>etc/schemas</schemasDirectory></span> - </configuration> - <executions> - <execution> - <id>document-gen</id> - <goals> - <goal>document-gen</goal> - </goals> - </execution> - </executions> -</plugin> -</pre> -<p> -<code>schemasDirectory</code> contains the -<a href="../reference/schemas/schemas.html">schemas</a>. -Generated classes will be in <em>target/generated-sources</em>. -The document type <code>book</code> will be represented as the Java class <code>Book</code>, -and it will have native methods for data access, so the code example above becomes: -</p> -<pre> -book.setTitle("Moby Dick"); -</pre> -<table class="table"> -<thead> -<tr> - <th>Configuration</th> - <th>Description</th> -</tr> -</thead><tbody> -<tr> - <th>Java package</th> - <td> - <p id="java-package">Specify the Java package of the generated types by using the following configuration:</p> -<pre> -<configuration> - <packageName>com.yahoo.mypackage</packageName> -</pre> - </td> - -</tr><tr> - <th>User provided annotation types</th> - <td> - <p id="user-provided-annotation-types"> - To provide the Java implementation of a given annotation type, - yielding <em>behaviour of annotations</em> - (implementing additional interfaces may be one scenario): - </p> -<pre> -<configuration> - <schemasDirectory>etc/schemas</schemasDirectory> - <provided> - <annotation> - <type>NodeImpl</type> - <clazz>com.yahoo.vespa.document.NodeImpl</clazz> - </annotation> - <annotation> - <type>DocumentImpl</type> - <clazz>com.yahoo.vespa.document.DocumentImpl</clazz> - </annotation> - </provided> -</pre> - Here, the plugin will not generate a type for <code>NodeImpl</code> and <code>DocumentImpl</code>, - but the <code>ConcreteDocumentFactory</code> will support them, - so that code depending on this will work. - </td> - -</tr><tr> - <th>Abstract annotation types</th> - <td> - <p id="abstract-annotation-types">Make a generated annotation type abstract:</p> -<pre> -<configuration> - <abztract> - <annotation> - <type>myabstractannotationtype</type> - </annotation> - </abztract> -</pre> - </td> -</tr> -</tbody> -</table> - - - -<h2 id="inheritance">Inheritance</h2> -<p> - If input document types use single inheritance, the generated Java types will inherit accordingly. - However, if a document type inherits from more than one type - (example: <code>document myDoc inherits base1, base2</code>), - the Java type for <code>myDoc</code> will just inherit from <code>Document</code>, - since Java has single inheritance. - Refer to <a href="inheritance-in-schemas.html">schema inheritance</a> for examples. -</p> - - - -<h2 id="feeding">Feeding</h2> -<p> -Concrete types are often used in a docproc, used for feeding data into stateful clusters. -To make Vespa use the correct type during feeding and serialization, -include in <code><container></code> in <a href="../reference/applications/services/services.html ">services.xml</a>: -</p> -<pre> -<container id="default" version="1.0"> - <document <span class="pre-hilite">type="book"</span> - <span class="pre-hilite">bundle="the name in <artifactId> in your pom.xml"</span> - <span class="pre-hilite">class="com.yahoo.mypackage.Book"</span> /> -</pre> -<p> -Vespa will make the type <code>Book</code> and all other concrete -document, annotation and struct types from the bundle available to the docproc(s) in the container. -The specified bundle must be the <code>Bundle-SymbolicName</code>. -It will also use the given Java type when feeding through a docproc chain. -If the class is not in the specified bundle, -the container will emit an error message about not being able to load -<code>ConcreteDocumentFactory</code> as a component, and not start. -There is no need to <code>Export-Package</code> the concrete document types from the bundle, -a <code>package-info.java</code> is generated that does that. -</p> - - - -<h2 id="factory-and-copy-constructor">Factory and copy constructor</h2> -<p> -Along with the actual types, the Maven plugin will also generate a class <code>ConcreteDocumentFactory</code>, -which holds information about the actual concrete types present. -It can be used to initialize an object given the document type: -</p> -<pre> -Book b = (Book) ConcreteDocumentFactory.getDocument("book", new DocumentId("id:book:book::0")); -</pre> -<p> -This can be done for example during deserialization, when a document is created. -The concrete types also have copy constructors that can take a generic -<code>Document</code> object of the same type. The contents will be deep-copied: -</p> -<pre> -Document bookGeneric; -// … -Book book = new Book(bookGeneric, bookGeneric.getId()); -</pre> -<p> -All the accessor and mutator methods on <code>Document</code> will work as expected on concrete types. -Note that <code>getFieldValue()</code> will <em>generate</em> an -ad-hoc <code>FieldValue</code> <em>every time</em>, -since concrete types don't use them to store data. -<code>setFieldValue()</code> will pack the data into the native Java field of the type. -</p> - - - -<h2 id="document-processing">Document processing</h2> -<p> -In a document processor, cast the incoming document base into the -concrete document type before accessing it. Example: -</p> -<pre> -public class ConcreteDocDocProc extends DocumentProcessor { - public Progress process(Processing processing) { - DocumentPut put = (DocumentPut) processing.getDocumentOperations().get(0); - Book b = (Book) (put.getDocument()); - b.setTitle("The Title"); - return Progress.DONE; - } -} -</pre> -<p> -Concrete document types are not supported for document updates or removes. -</p> diff --git a/en/schemas/concrete-documents.mdx b/en/schemas/concrete-documents.mdx new file mode 100644 index 0000000000..cc4f2ee2eb --- /dev/null +++ b/en/schemas/concrete-documents.mdx @@ -0,0 +1,151 @@ +--- +title: "Concrete documents" +--- + +In [document processing](/en/applications/document-processors), `setFieldValue()` and `getFieldValue()` is used to access fields in a `Document`. The data for each of the fields in the document instance is wrapped in field values. If the documents use structs, they are handled the same way. Example: + + +```java +book.setFieldValue("title", new StringFieldValue("Moby Dick")); +``` + +Alternatively, use code generation to get a *concrete document type*, a `Document` subclass that represents the exact document type (defined for example in the file `book.sd`). To generate, include it in the build, plugins section in `pom.xml`: + +```xml +<plugin> + <groupId>com.yahoo.vespa</groupId> + <artifactId>vespa-documentgen-plugin</artifactId> + <!-- Find latest version at search.maven.org/search?q=g:com.yahoo.vespa%20a:vespa-documentgen-plugin --> + <version>8.689.26</version> + <configuration> + <schemasDirectory>etc/schemas</schemasDirectory> + </configuration> + <executions> + <execution> + <id>document-gen</id> + <goals> + <goal>document-gen</goal> + </goals> + </execution> + </executions> +</plugin> +``` + +`schemasDirectory` contains the [schemas](/en/reference/schemas/schemas). Generated classes will be in `target/generated-sources`. The document type `book` will be represented as the Java class `Book`, and it will have native methods for data access, so the code example above becomes: + +```java +book.setTitle("Moby Dick"); +``` + +<table> +<tr className='border-b border-gray-200'> + <th className='text-left'>Configuration</th> + <th className='text-left'>Description</th> +</tr> +<tr> + <td>**Java package**</td> + <td> + Specify the Java package of the generated types: + +```xml +<configuration> + <packageName>com.yahoo.mypackage</packageName> +</configuration> +``` + </td> +</tr> +<tr> + <td>**User provided annotation types**</td> + <td> + To provide the Java implementation of a given annotation type, yielding *behaviour of annotations* (implementing additional interfaces may be one scenario): + +```xml +<configuration> + <schemasDirectory>etc/schemas</schemasDirectory> + <provided> + <annotation> + <type>NodeImpl</type> + <clazz>com.yahoo.vespa.document.NodeImpl</clazz> + </annotation> + <annotation> + <type>DocumentImpl</type> + <clazz>com.yahoo.vespa.document.DocumentImpl</clazz> + </annotation> + </provided> +</configuration> +``` + +Here, the plugin will not generate a type for `NodeImpl` and `DocumentImpl`, but the `ConcreteDocumentFactory` will support them, so that code depending on this will work. + </td> +</tr> +<tr> + <td>**Abstract annotation types**</td> + <td> + Make a generated annotation type abstract: + +```xml +<configuration> + <abztract> + <annotation> + <type>myabstractannotationtype</type> + </annotation> + </abztract> +</configuration> +``` + </td> +</tr> +</table> + +## Inheritance + +If input document types use single inheritance, the generated Java types will inherit accordingly. However, if a document type inherits from more than one type (example: `document myDoc inherits base1, base2`), the Java type for `myDoc` will just inherit from `Document`, since Java has single inheritance. Refer to [schema inheritance](/en/schemas/inheritance-in-schemas) for examples. + +## Feeding + +Concrete types are often used in a docproc, used for feeding data into stateful clusters. To make Vespa use the correct type during feeding and serialization, include in `<container>` in [services.xml](/en/reference/applications/services/services): + +```xml highlight={2-4} +<container id="default" version="1.0"> + <document type="book" + bundle="the name in <artifactId> in your pom.xml" + class="com.yahoo.mypackage.Book"/> +``` + +Vespa will make the type `Book` and all other concrete document, annotation and struct types from the bundle available to the docproc(s) in the container. The specified bundle must be the `Bundle-SymbolicName`. It will also use the given Java type when feeding through a docproc chain. If the class is not in the specified bundle, the container will emit an error message about not being able to load `ConcreteDocumentFactory` as a component, and not start. There is no need to `Export-Package` the concrete document types from the bundle, a `package-info.java` is generated that does that. + +## Factory and copy constructor + +Along with the actual types, the Maven plugin will also generate a class `ConcreteDocumentFactory`, which holds information about the actual concrete types present. It can be used to initialize an object given the document type: + +```java +Book b = (Book) ConcreteDocumentFactory.getDocument("book", new DocumentId("id:book:book::0")); +``` + +This can be done for example during deserialization, when a document is created. The concrete types also have copy constructors that can take a generic `Document` object of the same type. The contents will be deep-copied: + +```java +Document bookGeneric; +// … +Book book = new Book(bookGeneric, bookGeneric.getId()); +``` + +All the accessor and mutator methods on `Document` will work as expected on concrete types. Note that `getFieldValue()` will *generate* an ad-hoc `FieldValue` *every time*, since concrete types don't use them to store data. `setFieldValue()` will pack the data into the native Java field of the type. + +## Document processing + +In a document processor, cast the incoming document base into the concrete document type before accessing it. + +Example: + +```java +public class ConcreteDocDocProc extends DocumentProcessor { + public Progress process(Processing processing) { + DocumentPut put = (DocumentPut) processing.getDocumentOperations().get(0); + Book b = (Book) (put.getDocument()); + b.setTitle("The Title"); + return Progress.DONE; + } +} +``` + +Concrete document types are not supported for document updates or removes. diff --git a/en/schemas/documents.html b/en/schemas/documents.html deleted file mode 100644 index 539403f9c5..0000000000 --- a/en/schemas/documents.html +++ /dev/null @@ -1,418 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Documents" -redirect_from: -- /en/documents.html ---- - -<p> - Vespa models data as <em>documents</em>. - A document has a string identifier, set by the application, unique across all documents. - A document is a set of <a href="../writing/document-api-guide.html">key-value pairs</a>. - A document has a schema (i.e. type), - defined in the <a href="../basics/schemas.html">schema</a>. -</p><p> - When configuring clusters, a <a href="../reference/applications/services/content.html#documents">documents</a> - element sets what document types a cluster is to store. - This configuration is used to configure the garbage collector if it is enabled. - Additionally, it is used to define default routes for documents sent into the application. - By default, a document will be sent to all clusters having the document type defined. - Refer to <a href="../writing/document-routing.html">routing</a> for details. -</p> - -<p> - Vespa uses the document ID to distribute documents to nodes. - From the document identifier, the content layer calculates a numeric location. - A bucket contains all the documents, - where a given amount of least-significant bits of the location are all equal. - This property is used to enable co-localized storage of documents - - read more in <a href="../content/buckets.html">buckets</a> and - <a href="../content/elasticity.html">content cluster elasticity</a>. -</p> - -<p> - Documents can be <a href="../reference/applications/services/content.html#document">global</a>, - see <a href="parent-child.html">parent/child</a>. -</p> - - - -<h2 id="document-ids">Document IDs</h2> -<p> - The document identifiers are URIs, represented by a string, - which must conform to a defined URI scheme for document identifiers. - The document identifier string may only contain <em>text characters</em>, - as defined by <code>isTextCharacter</code> in - <a href="https://github.com/vespa-engine/vespa/blob/master/vespajlib/src/main/java/com/yahoo/text/Text.java"> - com.yahoo.text.Text</a>. -</p> - - - - -<h3 id="id-scheme">id scheme</h3> -<p> - Vespa currently has only one defined scheme, the <em>id scheme</em>: - <code>id:<namespace>:<document-type>:<key/value-pair>:<user-specified></code> -</p> -{% include note.html content=' -An example mapping from ID to the URL used in <a href="../writing/document-v1-api-guide.html">/document/v1/</a> is from -<code>id:mynamespace:mydoctype::user-defined-id</code> to -<code>/document/v1/mynamespace/mydoctype/docid/user-defined-id</code>. -Find examples and tools in <a href="../writing/document-v1-api-guide.html#document-not-found">troubleshooting</a>.' %} -<p> - Find examples in the <a href="../writing/document-v1-api-guide.html">/document/v1/</a> guide. -</p> -<table class="table"> - <thead> - <tr> - <th>Part</th> - <th>Required</th> - <th>Description</th> - </tr> - </thead> - <tbody> - <tr><th>namespace</th><td>Yes</td><td>Not used by Vespa, see <a href="#namespace">below</a>.</td></tr> - <tr><th>document-type</th><td>Yes</td><td>Document type as defined in - <a href="../reference/applications/services/content.html#document">services.xml</a> and the - <a href="../reference/schemas/schemas.html">schema</a>.</td></tr> - <tr><th style="white-space:nowrap">key/value-pair</th><td>Optional</td><td> - Modifiers to the id scheme, used to configure document distribution to - <a href="../content/buckets.html#document-to-bucket-distribution">buckets</a>. - With no modifiers, the id scheme distributes all documents uniformly. - The key/value-pair field contains one of two possible key/value pairs; - <strong>n</strong> and <strong>g</strong> are mutually exclusive: - <table class="table"> - <thead></thead><tbody> - <tr><th>n=<em><number></em></th><td> - Number in the range [0,2^63-1] - only for testing of abnormal bucket distributions</td></tr> - <tr style="white-space: nowrap"><th>g=<em><groupname></em></th><td> - The <em>groupname</em> string is hashed and used to select the storage location</td></tr> - </tbody> - </table> - {% include important.html content='This is only useful for document types with - <a href="../reference/applications/services/content.html#document">mode=streaming or mode=store-only</a>. - Do not use modifiers for regular indexed document types.' %} - See <a href="../performance/streaming-search.html">streaming search</a>. Using modifiers - for regular indexed document will cause unpredictable feeding performance, in addition, - search dispatch does not have support to limit the search to modifiers/buckets. - </td></tr> - <tr><th>user-specified</th><td>Yes</td><td>A unique ID string.</td></tr> -</tbody> -</table> - - -<h3 id="docid-in-results">Document IDs in search results</h3> -<p> - The full document ID (as a string) will often contain redundant - information and be quite long; a typical value may look like - "id:mynamespace:mydoctype::user-specified-identifier", where only the - last part is useful outside Vespa. The document ID is therefore not - stored in memory by default and <strong>not always present</strong> in the - <a href="../reference/querying/default-result-format.html#id">search results</a>. -</p> -<p> - To enable storing the document IDs in memory, - set <a href="../reference/schemas/schemas.html#documentid"><code>documentid</code></a> - to <code>attribute</code> in the schema. - This is possible since {% include version.html version="8.691.19" %}. - To return the document IDs from memory in the search results, configure a - <a href="../querying/document-summaries.html">document summary</a> - like this: -</p> -<pre> -schema music { - documentid: attribute - document music { - field ... - } - document-summary empty-summary { - summary documentid { - source: documentid - } - } - ... -</pre> -<p> - Then, use <code>presentation.summary=empty-summary</code> in the query API. -</p> -<p> - With the default <code>from-disk</code> setting for - <a href="../reference/schemas/schemas.html#documentid"><code>documentid</code></a> - in the schema, the document ID is stored on disk only. - To return the value in the search results anyway, configure a - <a href="../querying/document-summaries.html">document summary</a> - like this: -</p> -<pre> -schema music { - document music { - field ... - } - document-summary empty-summary { - summary documentid { - source: documentid - } - from-disk - } - ... -</pre> -<p> - The <code>from-disk</code> setting mutes a warning for document-summary disk access; - use a higher query timeout when requesting many IDs like this. -</p> -<p> - A more memory-efficient, but also more complicated alternative is to - put your own unique identifier - (usually the "user-specified-identifier" above) in a document field, - typically named "myid" or "shortid" or similar: -</p> -<pre> -field shortid type string { - indexing: attribute | summary -} -</pre> -<p> - This enables using a - <a href="../querying/document-summaries.html">document summary</a> with only - in-memory fields while still getting the identifier you actually - care about. If the "user-specified-identifier" is just a simple - number you could even use "type int" for this field for minimal - memory overhead. -</p> - - -<h3 id="namespace">Namespace</h3> -<p> - The namespace in document IDs is useful when you have multiple - document collections that you want to be sure never end up with the - same document ID. It has no function in Vespa beyond this, and can - just be set to any short constant value like for example "doc". - Consider also letting synthetic documents used for - testing use namespace "test" so it's easy to detect and remove - them if they are present outside the test by mistake. -</p> -<p> - Example - if feeding -</p> -<ul> - <li>document A by <code>curl -X POST https:.../document/v1/first_namespace/my_doc_type/docid/shakespeare</code></li> - <li>document B by <code>curl -X POST https:.../document/v1/second_namespace/my_doc_type/docid/shakespeare</code></li> -</ul> -<p> - then those will be separate documents, both searchable, with different document IDs. - The document ID differs not in the user specified part (this is <code>shakespeare</code> for both documents), - but in the namespace part (<code>first_namespace</code> vs <code>second_namespace</code>). - The full document ID for document A is <code>id:first_namespace:my_doc_type::shakespeare</code>. -</p> -<p> - The namespace has no relation to other configuration elsewhere, like in <em>services.xml</em> or in schemas. - It is just like the user specified part of each document ID in that sense. - Namespace can not be used in queries, other than as part of the full document ID. - However, it can be used for <a href="../reference/writing/document-selector-language.html">document selection</a>, - where <code>id.namespace</code> can be accessed and compared to a given string, for instance. - An example use case is <a href="../writing/visiting.html">visiting</a> a subset of documents. -</p> - - - -<h2 id="fields">Fields</h2> -<p> - Documents can have fields, see the <a href="../reference/schemas/schemas.html#field">schema reference</a>. -</p> -<p> - A field can not be defined with a default value. - Use a <a href="../writing/indexing.html#choice-example">choice ('||') indexing statement or a - <a href="../applications/document-processors.html">document processor</a> to assign a default to - document put/update operations. -</p> - - - -<h2 id="fieldsets">Fieldsets</h2> - -<p> - Use <em>fieldset</em> to limit the fields that are returned from a read operation, - like <em>get</em> or <em>visit</em> - see <a href="../clients/vespa-cli.html#documents">examples</a>. - Vespa may return more fields than specified if this does not impact performance. -</p> -{% include note.html content='Document field sets is a different thing than -<a href="../reference/schemas/schemas.html#fieldset">searchable fieldsets</a>.' %} -<p> - There are two options for specifying a fieldset: -</p> -<ul> - <li>Built-in fieldset</li> - <li>Name of a document type, then a colon ":", followed by a comma-separated list of fields - (for example <code>music:artist,song</code> to fetch two fields declared in <code>music.sd</code>) - </li> -</ul> -<p>Built-in fieldsets:</p> -<table class="table"> - <thead> - <tr> - <th>Fieldset</th> - <th>Description</th> - </tr> - </thead> - <tbody> - <tr> - <th>[all]</th> - <td> - Returns all fields in the schema (generated fields included) and the document ID.</td> - </tr><tr> - <th>[document]</th> - <td> - Returns original fields in the document, including the document ID. - </td> - </tr><tr> - <th>[none]</th> - <td> - Returns no fields at all, not even the document ID. <em>Internal, do not use</em></td> - </tr><tr> - <th>[id]</th> - <td> - Returns only the document ID</td> - </tr><tr> - <th><document type>:[document]</th> - <td> - {% include deprecated.html content="Use <code>[document]</code>"%} - Same as <code>[document]</code> fieldset above: - Returns only the original document fields (generated fields not included) - together with the document ID. - </td> - </tr> - </tbody> -</table> -<p>If a built-in field set is not used, a list of fields can be specified. Syntax:</p> -<pre> -<document type>:field1,field2,… -</pre> -<p>Example:</p> -<pre> -music:title,artist -</pre> - - - -<h2 id="document-expiry">Document expiry</h2> -<p> - To auto-expire documents, use a <a href="../reference/applications/services/content.html#documents.selection"> - selection</a> with <a href="../reference/writing/indexing-language.html#now">now</a>. - Example, set time-to-live (TTL) for <em>music</em>-documents to one day, using a field called <em>timestamp</em>: -</p> -<pre>{% highlight xml %} -<documents garbage-collection="true"> - <document type="music" - mode="index" - selection="music.timestamp > now() - 86400" /> -</documents> -{% endhighlight %}</pre> -<p>Note: The <code>selection</code> expression says which documents to <em>keep</em>, not which ones to delete. - The <em>timestamp</em> field must have a value in seconds since EPOCH:</p> -<pre> -field timestamp type long { - indexing: attribute - attribute { - fast-access - } -} -</pre> -<p> - When <code>garbage-collection="true"</code>, Vespa iterates over the document space to purge expired documents. - Vespa will invoke the configured GC selection for each stored document once every - <a href="../reference/applications/services/content.html#documents.selection">garbage-collection-interval</a> seconds. - It is unspecified when a particular document will be processed within the configured interval. -</p> - {% include important.html content="This is a best-effort garbage collection feature to conserve CPU and space. - Use query filters if it is important to exclude documents based on a criterion."%} -<ul> - <li> - Using a <em>selection</em> with <em>now</em> can have side effects - when re-feeding or re-processing documents, as timestamps can be stale. - A common problem is feeding with too old timestamps, - resulting in no documents being indexed. - </li><li> - Normally, documents that are already expired at write time are not persisted. - When using <a href="../writing/document-v1-api-guide.html#create-if-nonexistent">create</a> - (Create if nonexistent), it is possible to create documents that are expired and will be removed in next cycle. - </li><li> - Deploying a configuration where the selection string selects no documents - will cause all documents to be garbage collected. - Use <a href="../writing/visiting.html">visit</a> to test the selection string. - Garbage collected documents are not to be expected to be recoverable. - </li><li> - The fields that are referenced in the selection expression should be attributes. - Also, either the fields should be set with <em>"fast-access"</em> or the number of - <a href="../reference/applications/services/content.html#searchable-copies"> - searchable copies</a> in the content cluster should be the same as - the <a href="../reference/applications/services/content.html#redundancy">redundancy</a>. - Otherwise, the document selection maintenance will be slow - and have a major performance impact on the system. - </li><li> - <a href="../reference/schemas/schemas.html#import-field">Imported fields</a> - can be used in the selection string to expire documents, but special care needs to be - taken when using these. - See <a href="../reference/writing/document-selector-language.html#using-imported-fields-in-selections"> - using imported fields in selections</a> for more information and restrictions. - </li><li> - Document garbage collection is a low priority background operation that runs continuously - unless preempted by higher priority operations. - If the cluster is too heavily loaded by client feed operations, there's a risk of starving - GC from running. To verify that garbage collection is not starved, check the - <a href="../operations/metrics.html">vds.idealstate.max_observed_time_since_last_gc_sec.average</a> - distributor metric. - If it significantly exceeds <code>garbage-collection-interval</code> it is an indication that GC is starved. - </li> -</ul> -<p> - To batch remove, set a selection that matches no documents, like <em>"not music"</em> -</p><p> - Use <a href="../writing/visiting.html">vespa visit</a> to test the selection. - Dump the IDs of all documents that would be <em>preserved</em>: -</p> -<pre>{% highlight sh %} -$ vespa visit --selection 'music.timestamp > now() - 86400' --field-set "music.timestamp" -{% endhighlight %}</pre> -<p> - Negate the expression by wrapping it in a <code>not</code> - to dump the IDs of all the documents that would be <em>removed</em> during GC: -</p> -<pre>{% highlight sh %} -$ vespa visit --selection 'not (music.timestamp > now() - 86400)' --field-set "music.timestamp" -{% endhighlight %}</pre> - - - -<h2 id="processing-documents">Processing documents</h2> -<p> - To process documents, use <a href="../applications/document-processors.html">Document processing</a>. - Examples are enriching documents (look up data from other sources), - transform content (like linguistic transformations, tokenization), - filter data and trigger other events based on the input data. -</p><p> - See the sample app <a href="https://github.com/vespa-engine/sample-apps/tree/master/examples/document-processing"> - album-recommendation-docproc</a> for use of Vespa APIs like: -</p> - <ul> - <li><a href="../writing/document-api-guide.html">Document API</a> - - work on documents and fields in documents, and create unit tests using the Application framework</li> - <li><a href="../applications/document-processors.html">Document Processing</a> - - chain independent processors with ordering constraints</li> - </ul> -<p> - The sample app <a href="https://github.com/vespa-cloud/vespa-documentation-search"> - vespa-documentation-search</a> has examples of processing PUTs or UPDATEs - (using <a href="../writing/document-v1-api-guide.html#create-if-nonexistent">create-if-nonexistent</a>) of documents in - <a href="https://github.com/vespa-cloud/vespa-documentation-search/blob/main/src/main/java/ai/vespa/cloud/docsearch/OutLinksDocumentProcessor.java"> - OutLinksDocumentProcessor</a>. - It is also in introduction to using <a href="../searching-multi-valued-fields">multivalued fields</a> - like arrays, maps and tensors. - Use the <a href="https://github.com/vespa-cloud/vespa-documentation-search/blob/main/src/test/java/ai/vespa/cloud/docsearch/VespaDocSystemTest.java"> - VespaDocSystemTest</a> to build code that feeds and tests an instance in the Vespa Developer Cloud / local Docker instance. -</p><p> - Both sample apps also use the Document API to GET/PUT/UPDATE other documents as part of processing, - using asynchronous <a href="https://github.com/vespa-engine/vespa/blob/master/documentapi/src/main/java/com/yahoo/documentapi/DocumentAccess.java"> - DocumentAccess</a>. - Use this as a starting point for applications that enrich data when writing. -</p> diff --git a/en/schemas/documents.mdx b/en/schemas/documents.mdx new file mode 100644 index 0000000000..39cfe3d27f --- /dev/null +++ b/en/schemas/documents.mdx @@ -0,0 +1,258 @@ +--- +title: "Documents" +--- + +Vespa models data as *documents*. A document has a string identifier, set by the application, unique across all documents. A document is a set of [key-value pairs](/en/writing/document-api-guide). A document has a schema (i.e. type), defined in the [schema](/en/basics/schemas). + +When configuring clusters, a [documents](/en/reference/applications/services/content#documents) element sets what document types a cluster is to store. This configuration is used to configure the garbage collector if it is enabled. Additionally, it is used to define default routes for documents sent into the application. By default, a document will be sent to all clusters having the document type defined. Refer to [routing](/en/writing/document-routing) for details. + +Vespa uses the document ID to distribute documents to nodes. From the document identifier, the content layer calculates a numeric location. A bucket contains all the documents, where a given amount of least-significant bits of the location are all equal. This property is used to enable co-localized storage of documents - read more in [buckets](/en/content/buckets) and [content cluster elasticity](/en/content/elasticity). + +Documents can be [global](/en/reference/applications/services/content#document), see [parent/child](/en/schemas/parent-child). + +## Document IDs + +The document identifiers are URIs, represented by a string, which must conform to a defined URI scheme for document identifiers. The document identifier string may only contain *text characters*, as defined by `isTextCharacter` in [com.yahoo.text.Text](https://github.com/vespa-engine/vespa/blob/master/vespajlib/src/main/java/com/yahoo/text/Text.java). + +### id scheme + +Vespa currently has only one defined scheme, the *id scheme*: `id:<namespace>:<document-type>:<key/value-pair>:<user-specified>` + +<Note> +**Note:** + +An example mapping from ID to the URL used in [/document/v1/](/en/writing/document-v1-api-guide) is from `id:mynamespace:mydoctype::user-defined-id` to `/document/v1/mynamespace/mydoctype/docid/user-defined-id`. Find examples and tools in [troubleshooting](/en/writing/document-v1-api-guide#document-not-found). +</Note> + +Find examples in the [/document/v1/](/en/writing/document-v1-api-guide) guide. + +<table className="vespa-table"> + <thead> + <tr> + <th>Part</th> + <th>Required</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>namespace</td> + <td>Yes</td> + <td>Not used by Vespa, see <a href="/en/schemas/documents#namespace" aria-label="below">below</a>.</td> + </tr> + <tr> + <td>document-type</td> + <td>Yes</td> + <td>Document type as defined in <a href="/en/reference/applications/services/content#document" aria-label="services.xml">services.xml</a> and the <a href="/en/reference/schemas/schemas" aria-label="schema">schema</a>.</td> + </tr> + <tr> + <td>key/value-pair</td> + <td>Optional</td> + <td>Modifiers to the id scheme, used to configure document distribution to <a href="/en/content/buckets#document-to-bucket-distribution" aria-label="buckets">buckets</a>. With no modifiers, the id scheme distributes all documents uniformly. The key/value-pair field contains one of two possible key/value pairs; **n** and **g** are mutually exclusive: <br/> <code>{`n=*<number>*`}</code><br/> Number in the range <code>{`[0,2^63-1]`}</code> - only for testing of abnormal bucket distributions <br/><br/> <code>{`g=<groupname>`}</code><br/> The *groupname* string is hashed and used to select the storage location<br/><br/><Warning> **Important:** <br/>This is only useful for document types with <a href="/en/reference/applications/services/content#document" aria-label="mode=streaming or mode=store-only">mode=streaming or mode=store-only</a>. Do not use modifiers for regular indexed document types.</Warning><br/><br/> See <a href="/en/performance/streaming-search" aria-label="streaming search">streaming search</a>. Using modifiers for regular indexed document will cause unpredictable feeding performance, in addition, search dispatch does not have support to limit the search to modifiers/buckets.</td> + </tr> + <tr> + <td>user-specified</td> + <td>Yes</td> + <td>A unique ID string.</td> + </tr> + </tbody> +</table> + + +### Document IDs in search results + +The full document ID (as a string) will often contain redundant information and be quite long; a typical value may look like "id:mynamespace:mydoctype::user-specified-identifier", where only the last part is useful outside Vespa. The document ID is therefore not stored in memory by default and **not always present** in the [search results](/en/reference/querying/default-result-format#id). + +To enable storing the document IDs in memory, set [`documentid`](/en/reference/schemas/schemas#documentid) to `attribute` in the schema. This is possible since Vespa 8.691.19 . To return the document IDs from memory in the search results, configure a [document summary](/en/querying/document-summaries) like this: + +```js +schema music { + documentid: attribute + document music { + field ... + } + document-summary empty-summary { + summary documentid { + source: documentid + } + } + ... +``` + +Then, use `presentation.summary=empty-summary` in the query API. + +With the default `from-disk` setting for [`documentid`](/en/reference/schemas/schemas#documentid) in the schema, the document ID is stored on disk only. To return the value in the search results anyway, configure a [document summary](/en/querying/document-summaries) like this: + +```js +schema music { + document music { + field ... + } + document-summary empty-summary { + summary documentid { + source: documentid + } + from-disk + } + ... +``` + +The `from-disk` setting mutes a warning for document-summary disk access; use a higher query timeout when requesting many IDs like this. + +A more memory-efficient, but also more complicated alternative is to put your own unique identifier (usually the "user-specified-identifier" above) in a document field, typically named "myid" or "shortid" or similar: + +```js +field shortid type string { + indexing: attribute | summary +} +``` + +This enables using a [document summary](/en/querying/document-summaries) with only in-memory fields while still getting the identifier you actually care about. If the "user-specified-identifier" is just a simple number you could even use "type int" for this field for minimal memory overhead. + +## Namespace + +The namespace in document IDs is useful when you have multiple document collections that you want to be sure never end up with the same document ID. It has no function in Vespa beyond this, and can just be set to any short constant value like for example "doc". Consider also letting synthetic documents used for testing use namespace "test" so it's easy to detect and remove them if they are present outside the test by mistake. + +Example - if feeding + +- document A by `curl -X POST https:.../document/v1/first_namespace/my_doc_type/docid/shakespeare` +- document B by `curl -X POST https:.../document/v1/second_namespace/my_doc_type/docid/shakespeare` + +then those will be separate documents, both searchable, with different document IDs. The document ID differs not in the user specified part (this is `shakespeare` for both documents), but in the namespace part (`first_namespace` vs `second_namespace`). The full document ID for document A is `id:first_namespace:my_doc_type::shakespeare`. + +The namespace has no relation to other configuration elsewhere, like in *services.xml* or in schemas. It is just like the user specified part of each document ID in that sense. Namespace can not be used in queries, other than as part of the full document ID. However, it can be used for [document selection](/en/reference/writing/document-selector-language), where `id.namespace` can be accessed and compared to a given string, for instance. An example use case is [visiting](/en/writing/visiting) a subset of documents. + +## Fields + +Documents can have fields, see the [schema reference](/en/reference/schemas/schemas#field). + +A field can not be defined with a default value. Use a [choice ('||') indexing statement or a](/en/writing/indexing#choice-example) [document processor](/en/applications/document-processors) to assign a default to document put/update operations. + +## Fieldsets + +Use *fieldset* to limit the fields that are returned from a read operation, like *get* or *visit* - see [examples](/en/clients/vespa-cli#documents). Vespa may return more fields than specified if this does not impact performance. + +<Note> +**Note:** + +Document field sets is a different thing than [searchable fieldsets](/en/reference/schemas/schemas#fieldset). +</Note> + +There are two options for specifying a fieldset: + +- Built-in fieldset +- Name of a document type, then a colon ":", followed by a comma-separated list of fields (for example `music:artist,song` to fetch two fields declared in `music.sd`) + +Built-in fieldsets: + +<table className="vespa-table"> + <thead> + <tr> + <th>Fieldset</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>[all]</td> + <td>Returns all fields in the schema (generated fields included) and the document ID.</td> + </tr> + <tr> + <td>[document]</td> + <td>Returns original fields in the document, including the document ID.</td> + </tr> + <tr> + <td>[none]</td> + <td>Returns no fields at all, not even the document ID. *Internal, do not use*</td> + </tr> + <tr> + <td>[id]</td> + <td>Returns only the document ID</td> + </tr> + <tr> + <td><code>{`<document type>:[document]`}</code></td> + <td><Danger>**Deprecated:**<br/> Use <code>{`[document]`}</code></Danger><br/> Same as <code>{`[document]`}</code> fieldset above: Returns only the original document fields (generated fields not included) together with the document ID.</td> + </tr> + </tbody> +</table> + +If a built-in field set is not used, a list of fields can be specified. Syntax: + +```bash +<document type>:field1,field2,… +``` + +Example: + +```bash +music:title,artist +``` + +## Document expiry + +To auto-expire documents, use a [selection](/en/reference/applications/services/content#documents.selection) with [now](en/reference/writing/indexing-language#now). Example, set time-to-live (TTL) for *music*\-documents to one day, using a field called *timestamp*: + +```xml +<documents garbage-collection="true"> + <document type="music" + mode="index" + selection="music.timestamp > now() - 86400" /> +</documents> +``` + +<Note> +**Note:** + +The `selection` expression says which documents to *keep*, not which ones to delete. The *timestamp* field must have a value in seconds since EPOCH: +</Note> + +```js +field timestamp type long { + indexing: attribute + attribute { + fast-access + } +} +``` + +When `garbage-collection="true"`, Vespa iterates over the document space to purge expired documents. Vespa will invoke the configured GC selection for each stored document once every [garbage-collection-interval](/en/reference/applications/services/content#documents.selection) seconds. It is unspecified when a particular document will be processed within the configured interval. + +<Warning> +**Important:** + +This is a best-effort garbage collection feature to conserve CPU and space. Use query filters if it is important to exclude documents based on a criterion. +</Warning> + +- Using a *selection* with *now* can have side effects when re-feeding or re-processing documents, as timestamps can be stale. A common problem is feeding with too old timestamps, resulting in no documents being indexed. +- Normally, documents that are already expired at write time are not persisted. When using [create](/en/writing/document-v1-api-guide#create-if-nonexistent) (Create if nonexistent), it is possible to create documents that are expired and will be removed in next cycle. +- Deploying a configuration where the selection string selects no documents will cause all documents to be garbage collected. Use [visit](/en/writing/visiting) to test the selection string. Garbage collected documents are not to be expected to be recoverable. +- The fields that are referenced in the selection expression should be attributes. Also, either the fields should be set with *"fast-access"* or the number of [searchable copies](/en/reference/applications/services/content#searchable-copies) in the content cluster should be the same as the [redundancy](/en/reference/applications/services/content#redundancy). Otherwise, the document selection maintenance will be slow and have a major performance impact on the system. +- [Imported fields](/en/reference/schemas/schemas#import-field) can be used in the selection string to expire documents, but special care needs to be taken when using these. See [using imported fields in selections](/en/reference/writing/document-selector-language#using-imported-fields-in-selections) for more information and restrictions. +- Document garbage collection is a low priority background operation that runs continuously unless preempted by higher priority operations. If the cluster is too heavily loaded by client feed operations, there's a risk of starving GC from running. To verify that garbage collection is not starved, check the [vds.idealstate.max\_observed\_time\_since\_last\_gc\_sec.average](/en/operations/metrics) distributor metric. If it significantly exceeds `garbage-collection-interval` it is an indication that GC is starved. + +To batch remove, set a selection that matches no documents, like *"not music"* + +Use [vespa visit](/en/writing/visiting) to test the selection. Dump the IDs of all documents that would be *preserved*: + +```sh +$ vespa visit --selection 'music.timestamp > now() - 86400' --field-set "music.timestamp" +``` + +Negate the expression by wrapping it in a `not` to dump the IDs of all the documents that would be *removed* during GC: + +```sh +$ vespa visit --selection 'not (music.timestamp > now() - 86400)' --field-set "music.timestamp" +``` + +## Processing documents + +To process documents, use [Document processing](/en/applications/document-processors). Examples are enriching documents (look up data from other sources), transform content (like linguistic transformations, tokenization), filter data and trigger other events based on the input data. + +See the sample app [album-recommendation-docproc](https://github.com/vespa-engine/sample-apps/tree/master/examples/document-processing) for use of Vespa APIs like: + +- [Document API](/en/writing/document-api-guide) - work on documents and fields in documents, and create unit tests using the Application framework +- [Document Processing](/en/applications/document-processors) - chain independent processors with ordering constraints + +The sample app [vespa-documentation-search](https://github.com/vespa-cloud/vespa-documentation-search) has examples of processing PUTs or UPDATEs (using [create-if-nonexistent](/en/writing/document-v1-api-guide#create-if-nonexistent)) of documents in [OutLinksDocumentProcessor](https://github.com/vespa-cloud/vespa-documentation-search/blob/main/src/main/java/ai/vespa/cloud/docsearch/OutLinksDocumentProcessor.java). It is also in introduction to using [multivalued fields](/en/querying/searching-multivalue-fields) like arrays, maps and tensors. Use the [VespaDocSystemTest](https://github.com/vespa-cloud/vespa-documentation-search/blob/main/src/test/java/ai/vespa/cloud/docsearch/VespaDocSystemTest.java) to build code that feeds and tests an instance in the Vespa Developer Cloud / local Docker instance. + +Both sample apps also use the Document API to GET/PUT/UPDATE other documents as part of processing, using asynchronous [DocumentAccess](https://github.com/vespa-engine/vespa/blob/master/documentapi/src/main/java/com/yahoo/documentapi/DocumentAccess.java). Use this as a starting point for applications that enrich data when writing. diff --git a/en/schemas/exposing-schema-information.html b/en/schemas/exposing-schema-information.mdx similarity index 53% rename from en/schemas/exposing-schema-information.html rename to en/schemas/exposing-schema-information.mdx index b71c4ba105..d3a773c4db 100644 --- a/en/schemas/exposing-schema-information.html +++ b/en/schemas/exposing-schema-information.mdx @@ -1,38 +1,26 @@ --- -# Copyright Vespa.ai. All rights reserved. title: "Exposing schema information" -redirect_from: -- /en/exposing-schema-information.html +description: "Some applications need to expose information about schemas to data plane clients. This document explains how to add an API for that to your application." --- -<p>Some applications need to expose information about schemas to data plane clients. -This document explains how to add an API for that to your application.</p> +You need to know two things: -<p>You need to know two things:</p> +- Your application can expose any custom API by implementing a [handler](/en/applications/request-handlers). +- Information about the deployed schemas are available in the component `com.yahoo.search.schema.SchemaInfo`. -<ul> - <li>Your application can expose any custom API by implementing a - <a href="../applications/request-handlers.html">handler</a>. - <li>Information about the deployed schemas are available in the component <em>com.yahoo.search.schema.SchemaInfo</em>. -</ul> +With this information, we can add an API exposing schemas information through the following steps. -<p>With this information, we can add an API exposing schemas information through the following steps.</p> +<Steps> +<Step title="Make sure your application package can contain Java components"> -<h2>1. Make sure your application package can contain Java components</h2> +Application packages containing Java components must follow Maven layout. If your application package root contains a `pom.xml` and `src/main` you're good, otherwise convert it to this layout by copying the pom.xml from [the album-recommendation.java](https://github.com/vespa-engine/sample-apps/tree/master/album-recommendation-java) sample app and moving the files to follow this layout before moving on. -<p>Application packages containing Java components must follow Maven layout. -If your application package root contains a <code>pom.xml</code> and <code>src/main</code> -you're good, otherwise convert it to this layout by copying the pom.xml from -<a href="https://github.com/vespa-engine/sample-apps/tree/master/album-recommendation-java">the album-recommendation.java</a> -sample app and moving the files to follow this layout before moving on. -</p> +</Step> +<Step title="Add a handler exposing schema info"> +Add the following handler (to a package of your choosing): -<h2>2. Add a handler exposing schema info</h2> - -<p>Add the following handler (to a package of your choosing):</p> - -<pre>{% highlight java %} +```java expandable package ai.vespa.example; import com.yahoo.container.jdisc.HttpRequest; @@ -99,27 +87,31 @@ <h2>2. Add a handler exposing schema info</h2> } } -{% endhighlight %}</pre> - -<h2>3. Add the new API handler to your container cluster</h2> +``` -<p>In your <code>services.xml</code> file, under <code><container></code>, add:</p> +</Step> +<Step title="Add the new API handler to your container cluster"> -<pre>{% highlight xml %} - <handler id="ai.vespa.example.SchemaInfoHandler" bundle="[your pom.xml artifactId]"> - <binding>http://*/schema/v1/*</binding> - </handler> -{% endhighlight %}</pre> +In your `services.xml` file, under `<container>`, add: -<h2>4. Deploy the modified application</h2> +```xml +<handler id="ai.vespa.example.SchemaInfoHandler" bundle="[your pom.xml artifactId]"> + <binding>http://*/schema/v1/*</binding> +</handler> +``` -<pre> +</Step> +<Step title="Deploy the modified application"> +```sh $ mvn install $ vespa deploy -</pre> +``` -<h2>5. Verify that it works</h2> - -<pre> +</Step> +<Step title="Verify that it works"> +```sh $ vespa curl "schema/v1/" -</pre> +``` + +</Step> +</Steps> diff --git a/en/schemas/inheritance-in-schemas.html b/en/schemas/inheritance-in-schemas.html deleted file mode 100644 index 45f44c384c..0000000000 --- a/en/schemas/inheritance-in-schemas.html +++ /dev/null @@ -1,274 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Inheritance in schemas" -redirect_from: -- /en/inheritance-in-schemas.html ---- - -<p> -Both document types and full schemas can be inherited to make it easy to -design a structured application package with little duplication. -Document type inheritance defines a type hierarchy which is also useful for applications that -<a href="../querying/federation.html">federate queries</a> -as queries can be written to the common supertype. -This guide covers the different elements in the schema that supports inheritance: -</p> -<ol> - <li>Schemas</li> - <li>Document types</li> - <li>Rank profiles</li> - <li>Document summaries</li> -</ol> -<img src="/assets/img/inheritance-overview.svg" width="600px" height="auto" - alt="Schema elements that support inheritance"/> - -{% include note.html content="Inheritance is not to be confused with <a href='parent-child.html'>parent/child</a>, -which is a feature to import field values at query time."%} - - -<h2 id="schema-inheritance">Schema inheritance</h2> -<p> -A schema that inherits another gets all the content of the parent schema -as if it was defined inside the inheriting schema. -A schema that inherits another must also (explicitly) inherit its document type: -</p> -<pre> -schema books <span class="pre-hilite">inherits items</span> { - document books <span class="pre-hilite">inherits items</span> { - field author type string { - indexing: summary | index - } - } -} -</pre> - - -<h2 id="document-type-inheritance">Document type inheritance</h2> -<p> -A document type can inherit another document type. This will include all fields, also fields declared outside the -document block in the schema, rank-profiles defined in the super-schema can then be inherited in the schema of this -document, see <a href="#rank-profile-inheritance">Rank profile inheritance</a> below. -</p> -<p> -Both schemas <em>music</em> and <em>books</em> have the <em>title</em> field through inheritance: -</p> -<code>my-app/schemas/items.sd</code>: -<pre> -document items { - field title type string { - indexing: summary | index - } -} -</pre> - -<code>my-app/schemas/books.sd</code>: -<pre> -schema books { - document books <span class="pre-hilite">inherits items</span> { - field author type string { - indexing: summary | index - } - } -} -</pre> - -<code>my-app/schemas/music.sd</code>: -<pre> -schema music { - document music <span class="pre-hilite">inherits items</span> { - field artist type string { - indexing: summary | index - } - } -} -</pre> -<p> -This is equivalent to: -</p> -<code>my-app/schemas/books.sd</code>: -<pre> -schema books { - document books { - field title type string { - indexing: summary | index - } - field author type string { - indexing: summary | index - } - } -} -</pre> - -<code>my-app/schemas/music.sd</code>: -<pre> -schema music { - document music { - field title type string { - indexing: summary | index - } - field artist type string { - indexing: summary | index - } - } -} -</pre> -<p> -Notes: -</p> -<ul> - <li>Multiple inheritance and multiple levels of inheritance is supported.</li> - <li>Inheriting a document type defined in another content cluster is allowed.</li> - <li>Overriding fields defined in supertypes is not allowed.</li> - <li><a href="../reference/schemas/schemas.html#import-field">Imported fields</a> - defined in supertypes are not inherited.</li> -</ul> - - -<h2 id="rank-profile-inheritance">Rank profile inheritance</h2> -<p> -Where fields define the document types, rank profiles define the computations over the documents. -Rank profiles can be inherited from rank-profiles defined in the same schema, or defined in another schema when this -document inherits the document defined in the schema where the rank profile is defined: -</p> - -<code>my-app/schemas/items.sd</code>: -<pre> -schema items { - document items { - field title type string { - indexing: summary | index - } - } - - rank-profile items_ranking_base { - function title_score() { - expression: fieldLength(title) - } - first-phase { - expression: title_score - } - summary-features { - title_score - } - } -} -</pre> - -<code>my-app/schemas/books.sd</code>: -<pre> -schema books { - document books <span class="pre-hilite">inherits items</span> { - field author type string { - indexing: summary | index - } - } - - rank-profile items_ranking <span class="pre-hilite">inherits items_ranking_base</span> {} - - rank-profile items_subschema_ranking <span class="pre-hilite">inherits items_ranking_base</span> { - first-phase { - expression: title_score + fieldMatch(author) - } - summary-features <span class="pre-hilite">inherits items_ranking_base</span> { - fieldMatch(author) - } - } -} -</pre> - -<code>my-app/schemas/music.sd</code>: -<pre> -schema music { - document music <span class="pre-hilite">inherits items</span> { - field artist type string { - indexing: summary | index - } - } - - rank-profile items_ranking <span class="pre-hilite">inherits items_ranking_base</span> {} - - rank-profile items_subschema_ranking <span class="pre-hilite">inherits items_ranking_base</span> { - first-phase { - expression: title_score + fieldMatch(artist) - } - summary-features <span class="pre-hilite">inherits items_ranking_base</span> { - fieldMatch(artist) - } - } -} -</pre> -<p> -<em>items_ranking</em> can be considered the "base" ranking. -Pro-tip: Set this as the <em>default</em> rank profile -by modifying the default <a href="../querying/query-profiles.html">query profile</a>: -</p> -<code>my-app/search/query-profiles/default.xml</code>: -<pre> -<query-profile id="default"> - <field name="ranking.profile">items_ranking</field> -</query-profile> -</pre> -<p> -Queries using <em>ranking.profile=default</em> will then use the first-phase ranking defined in <em>items.sd</em>. -</p> -<p> -Another way to inherit behavior is to override the first-phase ranking in the sub-schemas, -still using functions defined in the super-schema (e.g. <em>title_score</em>). -</p> - -<h3 id="summary-features">Summary features</h3> -<p> -<a href="../reference/schemas/schemas.html#summary-features">Summary-features</a> and -<a href="../reference/schemas/schemas.html#match-features">match-features</a> -are rank features computed during ranking, -to be included in <a href="../reference/querying/default-result-format.html">results</a>. -These features can be inherited from parent rank profiles - the -above example uses <code>inherits</code> to include scores from -features in super- and sub-schema - example result: -</p> -<pre>{% highlight json %} -"summaryfeatures": { - "fieldMatch(author)": 0, - "rankingExpression(title_score)": 4 -} -{% endhighlight %}</pre> -<p> -In the examples above, both <em>books</em> and <em>music</em> schemas implement rank profiles -with same names (e.g. <em>items_subschema_ranking</em>), -so they can be used in queries spanning both. -If a query's rank profile can not be found in a given schema, -Vespa's default rank profile <a href="../ranking/nativerank.html">nativerank</a> is used. -</p> -<p> -<a href="../reference/schemas/schemas.html#inputs">Inputs</a> to a rank profile are automatically inherited from -the parent rank profile. If a new inputs block is defined in a child rank profile, those inputs will be -added cumulatively to those defined in the parent. -</p> - - -<h2 id="document-summary-inheritance">Document summary inheritance</h2> -<p> -<a href="../querying/document-summaries.html">Document summaries</a> can inherit others defined in the same -or an inherited schema. -</p> -<code>my-app/schemas/books.sd</code>: -<pre> -schema books { - document books { - field title type string { - indexing: summary | index - } - field author type string { - indexing: summary | index - } - } - - document-summary items_summary_tiny { - summary title {} - } - - document-summary items_summary_full <span class="pre-hilite">inherits items_summary_tiny</span> { - summary author {} - } -} -</pre> diff --git a/en/schemas/inheritance-in-schemas.mdx b/en/schemas/inheritance-in-schemas.mdx new file mode 100644 index 0000000000..5915a2f039 --- /dev/null +++ b/en/schemas/inheritance-in-schemas.mdx @@ -0,0 +1,240 @@ +--- +title: "Inheritance in schemas" +description: "Both document types and full schemas can be inherited to make it easy to design a structured application package with little duplication. Document type inheritance defines a type hierarchy which is also useful for applications that [federate queries](/en/querying/federation) as queries can be written to the common supertype. This guide covers the different elements in the schema that supports inheritance:" +--- + +1. Schemas +2. Document types +3. Rank profiles +4. Document summaries + +<Frame>![Schema elements that support inheritance](/assets/img/inheritance-overview.svg)</Frame> + +<Note> +**Note:** + +Inheritance is not to be confused with [parent/child](/en/schemas/parent-child), which is a feature to import field values at query time. +</Note> + +## Schema inheritance + +A schema that inherits another gets all the content of the parent schema as if it was defined inside the inheriting schema. A schema that inherits another must also (explicitly) inherit its document type: + +```js +schema books inherits items { + document books inherits items { + field author type string { + indexing: summary | index + } + } +} +``` + +## Document type inheritance + +A document type can inherit another document type. This will include all fields, also fields declared outside the document block in the schema, rank-profiles defined in the super-schema can then be inherited in the schema of this document, see [Rank profile inheritance](#rank-profile-inheritance) below. + +Both schemas *music* and *books* have the *title* field through inheritance: + +`my-app/schemas/items.sd`: + +```js +document items { + field title type string { + indexing: summary | index + } +} +``` + +`my-app/schemas/books.sd`: + +```js +schema books { + document books inherits items { + field author type string { + indexing: summary | index + } + } +} +``` + +`my-app/schemas/music.sd`: + +```js +schema music { + document music inherits items { + field artist type string { + indexing: summary | index + } + } +} +``` + +This is equivalent to: + +`my-app/schemas/books.sd`: + +```js +schema books { + document books { + field title type string { + indexing: summary | index + } + field author type string { + indexing: summary | index + } + } +} +``` + +`my-app/schemas/music.sd`: + +```js +schema music { + document music { + field title type string { + indexing: summary | index + } + field artist type string { + indexing: summary | index + } + } +} +``` + +Notes: + +- Multiple inheritance and multiple levels of inheritance is supported. +- Inheriting a document type defined in another content cluster is allowed. +- Overriding fields defined in supertypes is not allowed. +- [Imported fields](/en/reference/schemas/schemas#import-field) defined in supertypes are not inherited. + +## Rank profile inheritance + +Where fields define the document types, rank profiles define the computations over the documents. Rank profiles can be inherited from rank-profiles defined in the same schema, or defined in another schema when this document inherits the document defined in the schema where the rank profile is defined: + +`my-app/schemas/items.sd`: + +```js +schema items { + document items { + field title type string { + indexing: summary | index + } + } + + rank-profile items_ranking_base { + function title_score() { + expression: fieldLength(title) + } + first-phase { + expression: title_score + } + summary-features { + title_score + } + } +} +``` + +`my-app/schemas/books.sd`: + +```js +schema books { + document books inherits items { + field author type string { + indexing: summary | index + } + } + + rank-profile items_ranking inherits items_ranking_base {} + + rank-profile items_subschema_ranking inherits items_ranking_base { + first-phase { + expression: title_score + fieldMatch(author) + } + summary-features inherits items_ranking_base { + fieldMatch(author) + } + } +} +``` + +`my-app/schemas/music.sd`: + +```js +schema music { + document music inherits items { + field artist type string { + indexing: summary | index + } + } + + rank-profile items_ranking inherits items_ranking_base {} + + rank-profile items_subschema_ranking inherits items_ranking_base { + first-phase { + expression: title_score + fieldMatch(artist) + } + summary-features inherits items_ranking_base { + fieldMatch(artist) + } + } +} +``` + +*items_ranking* can be considered the "base" ranking. Pro-tip: Set this as the *default* rank profile by modifying the default [query profile](/en/querying/query-profiles): + +`my-app/search/query-profiles/default.xml`: + +```xml +<query-profile id="default"> + <field name="ranking.profile">items_ranking</field> +</query-profile> +``` + +Queries using *ranking.profile=default* will then use the first-phase ranking defined in *items.sd*. + +Another way to inherit behavior is to override the first-phase ranking in the sub-schemas, still using functions defined in the super-schema (e.g. *title_score*). + +### Summary features + +[Summary-features](/en/reference/schemas/schemas#summary-features) and [match-features](/en/reference/schemas/schemas#match-features) are rank features computed during ranking, to be included in [results](/en/reference/querying/default-result-format). These features can be inherited from parent rank profiles - the above example uses `inherits` to include scores from features in super- and sub-schema - example result: + +```json +"summaryfeatures": { + "fieldMatch(author)": 0, + "rankingExpression(title_score)": 4 +} +``` + +In the examples above, both *books* and *music* schemas implement rank profiles with same names (e.g. *items_subschema_ranking*), so they can be used in queries spanning both. If a query's rank profile can not be found in a given schema, Vespa's default rank profile [nativerank](/en/ranking/nativerank) is used. + +[Inputs](/en/reference/schemas/schemas#inputs) to a rank profile are automatically inherited from the parent rank profile. If a new inputs block is defined in a child rank profile, those inputs will be added cumulatively to those defined in the parent. + +## Document summary inheritance + +[Document summaries](/en/querying/document-summaries) can inherit others defined in the same or an inherited schema. + +`my-app/schemas/books.sd`: + +```js +schema books { + document books { + field title type string { + indexing: summary | index + } + field author type string { + indexing: summary | index + } + } + + document-summary items_summary_tiny { + summary title {} + } + + document-summary items_summary_full inherits items_summary_tiny { + summary author {} + } +} +``` diff --git a/en/schemas/parent-child.html b/en/schemas/parent-child.html deleted file mode 100644 index 61d06ffaf8..0000000000 --- a/en/schemas/parent-child.html +++ /dev/null @@ -1,240 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Parent/Child" -redirect_from: -- /en/parent-child.html ---- - -<p> - Using <a href="../reference/schemas/schemas.html#reference">document references</a>, - documents can have parent/child relationships. - Use this to join data by <a href="../reference/schemas/schemas.html#import-field">importing</a> - fields from parent documents. Features: -</p> -<ul> - <li>simplify document operations - one write to update one value</li> - <li>no de-normalization needed - simplifies data updates and atomic update into all children</li> - <li>search child documents based on properties from parent documents</li> - <li>search parent documents only</li> - <li>use imported fields as part of <a href="../writing/visiting.html">visiting</a> and - <a href="documents.html#document-expiry">garbage collection</a> with - <a href="../reference/writing/document-selector-language.html#using-imported-fields-in-selections">document - selection</a> expressions</li> -</ul> -<p> - Parent/child relationships are not supported in - <a href="../performance/streaming-search.html#differences-in-streaming-search">streaming search</a>. -</p> -<p> - An alternative to parent documents is using arrays or maps of struct fields - this guide covers both. -</p><p> - Common use cases are applications with structured data like commerce (e.g. products with multiple sellers), - advertising (advertisers with campaigns with ads, that have budgets that need realtime updates). -</p><p> - High-level overview of documents, imported fields and array fields: -</p> -<img src="/assets/img/parent-child.svg" width="810px" height="auto" - alt="Parent/child and global documents" /> - - - -<h2 id="parent-documents">Parent documents</h2> -<p> - Model parent-child relationships by using - <a href="../reference/schemas/schemas.html#reference">references</a> - to <a href="../reference/applications/services/content.html#document">global documents</a>. - This is like foreign keys in a relational database. - Parents can have parents. - A document can have references to multiple parents - the parents can be of same or different types. -</p><p> - Using a <em>reference</em>, <a href="../content/attributes.html">attribute</a> fields can be - <a href="../reference/schemas/schemas.html#import-field">imported</a> - from parent types into the child's <a href="../basics/schemas.html">schema</a> - and used for matching, ranking, grouping and sorting. - A reference is a special attribute with the parent's - <a href="documents.html#document-ids">document ID</a> as value. - References are hence weak: -</p> -<ul> - <li>no cascade delete</li> - <li>a referenced document can be non-existent - imported fields do not have values in this case</li> -</ul> -<p> - When using parent-child relationships, - data does not have to be denormalized as fields from parents are imported into children. - Use this to update parent fields to limit number of updates if a field's value is shared between many documents. - This also limits the resources (memory / disk) required to store and handle documents on content nodes. -</p><p> - At cluster changes, global documents are merged to new nodes before regular documents. - For consistency, a content node is not serving queries before <span style="text-decoration: underline;">all</span> - global documents are synchronized - refer to <a href="../content/elasticity.html">content cluster elasticity</a>. -</p> -{% include important.html content=" -Cyclic or self <a href='../reference/schemas/schemas.html#reference'>references</a> are not allowed."%} -<p> - Performance notes: -</p> -<ul> - <li>As parent documents are global, - a write executes on <span style="text-decoration: underline;">all</span> content nodes - - see <a href="../performance/sizing-feeding.html#parent-child">examples</a></li> - <li>Node capacity will limit the number of parent documents - - there should normally be an order of magnitude fewer parent documents than child documents</li> - <li>Memory usage grows accordingly. A global document is otherwise equal to a regular document, - but each content node must be sized to hold all global documents plus its share of regular documents</li> - <li>Reference fields add a memory indirection and does not impact query performance much</li> - <li><a href="../performance/feature-tuning.html#parent-child-and-search-performance">Search performance notes</a></li> - <li><a href="../writing/partial-updates.html">Partial updates</a> to a reference field requires a read-modify-write to the - document store and limits throughput.</li> -</ul> - - - -<h2 id="multivalue-fields">Multivalue fields</h2> -<p> - A document can have <a href="../basics/schemas.html#document-fields">fields</a> that are arrays or maps of struct. - Structs and documents are similar - a set of field name/value pairs. - One-to-many mappings can therefore be implemented this way, as an alternative to using parent/child, - when each document has a set of properties that belongs to that document. -</p><p> - Refer to <a href="../searching-multi-valued-fields">multivalue fields</a> for more information. -</p> - - - -<h2 id="parent-or-multivalue">Parent or multivalue?</h2> -<p> - As a rule of thumb, model the items <em>searched for</em> as the document - example products for sale. - Shared properties, like vendor, can be model using a parent document, importing a vendor name field - - assuming a vendor has many products, and the vendor list is limited. - Use arrays or maps of structs for properties documents might have, like shoe size or screen resolution - - one can then have a struct field for property name and another for property value, - giving a flexible structure for products with an unlimited set of possible properties. -</p> - - - -<h2 id="parent-child-example">Parent/child example</h2> -<p>In services.xml:</p> -<pre>{% highlight xml %} -<content id="mycluster" version="1.0"> - <documents> - <document type="advertiser" mode="index" global="true" /> - <document type="campaign" mode="index" global="true" /> - <document type="salesperson" mode="index" global="true" /> - <document type="ad" mode="index" /> - </documents> -{% endhighlight %}</pre> -<p>Schemas and data:</p> -<pre> -schema advertiser { - document advertiser { - field name type string { - indexing : attribute - } - } -} -</pre> -<pre>{% highlight json %} -{ - "put": "id:test:advertiser::cool", - "fields": { - "name": "cool" - } -} -{% endhighlight %}</pre> -<pre> -schema campaign { - document campaign { - field advertiser_ref type <span class="pre-hilite">reference<advertiser></span> { - indexing: attribute - } - field budget type int { - indexing : attribute - } - } - <span class="pre-hilite">import field advertiser_ref.name as advertiser_name</span> {} -} -</pre> -<pre>{% highlight json %} -[{ - "put": "id:test:campaign::thebest", - "fields": { - "advertiser_ref": "id:test:advertiser::cool", - "budget": 20 - } -}, -{ - "put": "id:test:campaign::nextbest", - "fields": { - "advertiser_ref": "id:test:advertiser::cool", - "budget": 10 - } -}] -{% endhighlight %}</pre> -<pre> -schema salesperson { - document salesperson { - field name type string { - indexing: attribute - } - } -} -</pre> -<pre>{% highlight json %} -{ - "put": "id:test:salesperson::johndoe", - "fields": { - "name": "John Doe" - } -} -{% endhighlight %}</pre> -<pre> -schema ad { - document ad { - field campaign_ref type <span class="pre-hilite">reference<campaign></span> { - indexing: attribute - } - field other_campaign_ref type reference<campaign> { - indexing: attribute - } - field salesperson_ref type reference<salesperson> { - indexing: attribute - } - } - - <span class="pre-hilite">import field campaign_ref.budget as budget</span> {} - import field salesperson_ref.name as salesperson_name {} - import field campaign_ref.advertiser_name as advertiser_name {} - - document-summary my_summary { - summary budget {} - summary salesperson_name {} - summary advertiser_name {} - } -} -</pre> -<pre>{% highlight json %} -{ - "put": "id:test:ad::1", - "fields": { - "campaign_ref": "id:test:campaign::thebest", - "other_campaign_ref": "id:test:campaign::nextbest", - "salesperson_ref": "id:test:salesperson::johndoe" - } -} -{% endhighlight %}</pre> -<p> - Document type <em>ad</em> has two references to <em>campaign</em> - (via <em>campaign_ref</em> and <em>other_campaign_ref</em>) - and one reference to <em>salesperson</em> (via <em>salesperson_ref</em>). - The <em>budget</em> field from <em>campaign</em> is imported into the <em>ad</em> schema - (via <em>campaign_ref</em>) and given the name <em>budget</em>. - Similarly, the <em>name</em> of <em>salesperson</em> is imported as <em>salesperson_name</em>. -</p> -<p> - Document type <em>campaign</em> has a reference to <em>advertiser</em> - and imports the field <em>name</em> as <em>advertiser_name</em>. - This is also imported into <em>ad</em> via <em>campaign_ref</em> from its grandparent <em>advertiser</em>. - To use the imported fields in summary, define a document summary <em>my_summary</em> containing these fields. -</p> diff --git a/en/schemas/parent-child.mdx b/en/schemas/parent-child.mdx new file mode 100644 index 0000000000..738c4929d0 --- /dev/null +++ b/en/schemas/parent-child.mdx @@ -0,0 +1,187 @@ +--- +title: "Parent/Child" +sidebarTitle: "Parent-child relationships" +--- + +Using [document references](/en/reference/schemas/schemas#reference), documents can have parent/child relationships. Use this to join data by [importing](/en/reference/schemas/schemas#import-field) fields from parent documents. Features: + +- simplify document operations - one write to update one value +- no de-normalization needed - simplifies data updates and atomic update into all children +- search child documents based on properties from parent documents +- search parent documents only +- use imported fields as part of [visiting](/en/writing/visiting) and [garbage collection](/en/schemas/documents#document-expiry) with [document selection](/en/reference/writing/document-selector-language#using-imported-fields-in-selections) expressions + +Parent/child relationships are not supported in [streaming search](/en/performance/streaming-search#differences-in-streaming-search). + +An alternative to parent documents is using arrays or maps of struct fields - this guide covers both. + +Common use cases are applications with structured data like commerce (e.g. products with multiple sellers), advertising (advertisers with campaigns with ads, that have budgets that need realtime updates). + +High-level overview of documents, imported fields and array fields: + +<Frame>![Parent/child and global documents](/assets/img/parent-child.svg)</Frame> + +## Parent documents + +Model parent-child relationships by using [references](/en/reference/schemas/schemas#reference) to [global documents](/en/reference/applications/services/content#document). This is like foreign keys in a relational database. Parents can have parents. A document can have references to multiple parents - the parents can be of same or different types. + +Using a *reference*, [attribute](/en/content/attributes) fields can be [imported](/en/reference/schemas/schemas#import-field) from parent types into the child's [schema](/en/basics/schemas) and used for matching, ranking, grouping and sorting. A reference is a special attribute with the parent's [document ID](/en/schemas/documents#document-ids) as value. References are hence weak: + +- no cascade delete +- a referenced document can be non-existent - imported fields do not have values in this case + +When using parent-child relationships, data does not have to be denormalized as fields from parents are imported into children. Use this to update parent fields to limit number of updates if a field's value is shared between many documents. This also limits the resources (memory / disk) required to store and handle documents on content nodes. + +At cluster changes, global documents are merged to new nodes before regular documents. For consistency, a content node is not serving queries before all global documents are synchronized - refer to [content cluster elasticity](/en/content/elasticity). + +<Warning> +**Important:** + +Cyclic or self [references](/en/reference/schemas/schemas#reference) are not allowed. +</Warning> + +Performance notes: + +- As parent documents are global, a write executes on all content nodes - see [examples](/en/performance/sizing-feeding#parent-child) +- Node capacity will limit the number of parent documents - there should normally be an order of magnitude fewer parent documents than child documents +- Memory usage grows accordingly. A global document is otherwise equal to a regular document, but each content node must be sized to hold all global documents plus its share of regular documents +- Reference fields add a memory indirection and does not impact query performance much +- [Search performance notes](/en/performance/feature-tuning#parent-child-and-search-performance) +- [Partial updates](/en/writing/partial-updates) to a reference field requires a read-modify-write to the document store and limits throughput. + +## Multivalue fields + +A document can have [fields](/en/basics/schemas#document-fields) that are arrays or maps of struct. Structs and documents are similar - a set of field name/value pairs. One-to-many mappings can therefore be implemented this way, as an alternative to using parent/child, when each document has a set of properties that belongs to that document. + +Refer to [multivalue fields](/en/querying/searching-multivalue-fields) for more information. + +## Parent or multivalue? + +As a rule of thumb, model the items *searched for* as the document - example products for sale. Shared properties, like vendor, can be model using a parent document, importing a vendor name field - assuming a vendor has many products, and the vendor list is limited. Use arrays or maps of structs for properties documents might have, like shoe size or screen resolution - one can then have a struct field for property name and another for property value, giving a flexible structure for products with an unlimited set of possible properties. + +## Parent/child example + +In `services.xml`: + +```xml +<content id="mycluster" version="1.0"> + <documents> + <document type="advertiser" mode="index" global="true" /> + <document type="campaign" mode="index" global="true" /> + <document type="salesperson" mode="index" global="true" /> + <document type="ad" mode="index" /> + </documents> +</content> +``` + +Schemas and data: + +```js +schema advertiser { + document advertiser { + field name type string { + indexing : attribute + } + } +} +``` + +```json +{ + "put": "id:test:advertiser::cool", + "fields": { + "name": "cool" + } +} +``` + +```js +schema campaign { + document campaign { + field advertiser_ref type reference<advertiser> { + indexing: attribute + } + field budget type int { + indexing : attribute + } + } + import field advertiser_ref.name as advertiser_name {} +} +``` + +```json +[{ + "put": "id:test:campaign::thebest", + "fields": { + "advertiser_ref": "id:test:advertiser::cool", + "budget": 20 + } +}, +{ + "put": "id:test:campaign::nextbest", + "fields": { + "advertiser_ref": "id:test:advertiser::cool", + "budget": 10 + } +}] +``` + +```js +schema salesperson { + document salesperson { + field name type string { + indexing: attribute + } + } +} +``` + +```json +{ + "put": "id:test:salesperson::johndoe", + "fields": { + "name": "John Doe" + } +} +``` + +```js +schema ad { + document ad { + field campaign_ref type reference<campaign> { + indexing: attribute + } + field other_campaign_ref type reference<campaign> { + indexing: attribute + } + field salesperson_ref type reference<salesperson> { + indexing: attribute + } + } + + import field campaign_ref.budget as budget {} + import field salesperson_ref.name as salesperson_name {} + import field campaign_ref.advertiser_name as advertiser_name {} + + document-summary my_summary { + summary budget {} + summary salesperson_name {} + summary advertiser_name {} + } +} +``` + +```json +{ + "put": "id:test:ad::1", + "fields": { + "campaign_ref": "id:test:campaign::thebest", + "other_campaign_ref": "id:test:campaign::nextbest", + "salesperson_ref": "id:test:salesperson::johndoe" + } +} +``` + +Document type *ad* has two references to *campaign* (via *campaign_ref* and *other_campaign_ref*) and one reference to *salesperson* (via *salesperson_ref*). The *budget* field from *campaign* is imported into the *ad* schema (via *campaign_ref*) and given the name *budget*. Similarly, the *name* of *salesperson* is imported as *salesperson_name*. + +Document type *campaign* has a reference to *advertiser* and imports the field *name* as *advertiser_name*. This is also imported into *ad* via *campaign_ref* from its grandparent *advertiser*. To use the imported fields in summary, define a document summary *my_summary* containing these fields. diff --git a/en/schemas/predicate-fields.html b/en/schemas/predicate-fields.html deleted file mode 100644 index 22a320c4f3..0000000000 --- a/en/schemas/predicate-fields.html +++ /dev/null @@ -1,458 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Predicate Fields" -redirect_from: -- /en/predicate-fields.html ---- - -<p> -<em><a href="../reference/schemas/schemas.html#predicate">Predicate</a> fields</em> -provides a way to match queries to a set of <em>boolean constraints</em> in a document. -Example use cases: -</p> -<ul> - <li> - Boolean constraints in advertisements, specifying their target groups. - Query with a set of impressions, i.e., specific values for a given user, to find out which ads can be shown to this user. - </li> - <li> - Saved searches for e-commerce. - Users save searches in the form of predicates, and new items are matched to the saved searches. - </li> -</ul> -<p>Example document and query, with a <code>target</code> <a href="../reference/schemas/schemas.html#predicate">predicate field</a>:</p> -<pre>{% highlight json %} -{ - "put": "id:ns:user::12345", - "fields": { - "target": "gender in ['male'] and age in [30..40] and income in [200..50000]", - "name": "John Doe" - } -} -{% endhighlight %}</pre> -<pre> -select name from user where predicate(target, {"gender":"male"}, {"income":30000L}) -</pre> -<p> - Read more in the <a href="https://github.com/vespa-engine/sample-apps/blob/master/examples/predicate-fields/README.md">Predicate Search sample application</a>. -</p> -<p> - There are some trade-offs between index size and query performance when configuring predicate fields, - see <a href="#configuration">configuration</a>. -</p> -<p>Predicate fields are good for solving problems where practitioners have used Percolator Queries.</p> -{% include note.html content='Predicate fields are not supported in -<a href="../performance/streaming-search.html#differences-in-streaming-search">streaming search</a>.' %} - - - -<h2 id="boolean-constraints">Boolean Constraints</h2> -<p> -A boolean constraint (predicate) specifies a target area for queries to land in. -Its attributes may be simple true/false criteria, -subsets of sets to match, or ranges of values. -</p> - - -<h3 id="predicates">Predicates</h3> -<p> -A predicate is a specification of a boolean constraint in the form of a boolean expression. -For example, the predicate <code>gender in [Female] and age in [20..30] and pos in [1..4]</code> -can specify that an ad requires target users to be women between 20 and 30 years of age, -and that the ad must be placed in one of the top four positions. -</p> -<p>See <a href="#grammar">grammar</a> for details.</p> - - -<h3 id="attributes">Attributes</h3> -<p> -The variables in predicates are known as <em>attributes</em>. There are two types of attributes: -</p> -<ul> - <li><strong>Regular attributes</strong>. Regular attributes take string values. - Specify in the predicate that a regular attribute must have one value of multiple alternatives. - E.g. <code>hobby in [Music, Hiking]</code> evaluates true if hobby is assigned to either - <code>Music</code> or <code>Hiking</code> (or both).</li> - <li><strong>Range attributes</strong>. Range attributes take integer values - and may only be used in range expressions. - A range expression specifies either a lower bound, an upper bound or both: - <ol> - <li><code>age in [10..]</code> - age must be 10 or higher</li> - <li><code>age in [..10]</code> - age must be 10 or lower</li> - <li><code>age in [10..15]</code> - age must be between 10 and 15, inclusive</li> - </ol> - </li> -</ul> - - - -<h3 id="predicate-samples">Predicate Samples</h3> -<p> -The subset expression evaluates to true if the regular attribute is assigned -to any of the values listed in the brackets: -</p> -<pre>hobby in [Music, Hiking, Biking]</pre> -The range expression evaluates to true if the range attribute is in the specified range -(boundaries are inclusive): -<pre>age in [20..29]</pre> -It's also possible to specify only the lower or upper bound for a range expression: -<pre>age in [..29]</pre> -Use the <code>or</code> operator to create disjunctions: -<pre>age in [..29] or hobby in [Music, Biking]</pre> -Similarly, use the <code>and</code> operator to create conjunctions: -<pre>age in [20..29] and hobby in [Music]</pre> -Parenthesis can be used to create more complex predicates: -<pre>(age in [20..29] and gender in [Male]) or (age in [30..39] and gender in [Female])</pre> -The subset and range expression can be negated using the <code>not</code> operator: -<pre>age not in [20..29] and hobby not in [Music]</pre> -<pre>not age in [20..29] and not hobby in [Music]</pre> -The <code>not</code> operator can also be combined with parenthesis: -<pre>not (age in [20..29] or hobby in [Music])</pre> -Attributes and values containing non-alphanumeric letters must be surrounded with quotes: -<pre>"profile.gender" in ['Male', "Female"]</pre> -If a string surrounded with double-quotes contains a double-quote, escape it with backslash. -Same rule applies for single quotes in single-quoted strings. -Double quotes in single-quoted strings and single quotes in double-quoted string shall not be escaped. -<pre>"single'quote" in ["double\"quote", 'double"quote', 'single\'quote']</pre> -Set the predicate to the value <code>true</code> to make it always a match. -Setting the predicate to <code>false</code> will ensure that it's never a match. -<pre>true</pre> -<pre>false</pre> - - - - -<h2 id="queries">Queries</h2> -<p> -A boolean query represents a set of concrete values for attributes, -which may fall within the target area drawn up by one or more sets of boolean constraints. -Queries are specified by two lists of attributes with values. -One list holds regular attributes, each with one or more discrete values, -while the other list holds range attributes with a single value each. -</p> - - -<h3 id="search-using-yql">Search Using YQL+</h3> -<p> -Boolean queries are made using the <code>predicate</code> function of YQL+. -The predicate function takes three parameters: The predicate field, -a map of regular attribute key/value pairs, and a map of range attribute key/value pairs. -</p> -<pre>select * from sources * where predicate(predicate_field, {"gender":"Female", "gender":"Male", hobby":"Hiking"}, {"age":20L, "pos":2L})</pre> - -One can use empty maps when specifying attributes: -<pre>select * from sources * where predicate(predicate_field, {}, {"age":20L})</pre> - -When specifying multiple values for the same key, it is possible to use an array as the value: -<pre>select * from sources * where predicate(predicate_field,{"gender":["Female","Male"], "hobby":"Hiking"}, {"age":20L})</pre> - - - -<h3 id="subqueries">Subqueries</h3> -<p> -For efficiency reasons it is possible to specify multiple queries at -once. This is done by providing a bitmap with each term, where the -bitmap represents which (out of 64) subqueries the term is a part -of. A typical use case for this is when we want to find ads for -multiple positions on a page. Then the user profile information will -be part of every subquery while the ad placement varies. Remember that -all subqueries are used every time, which means that empty subqueries -also can get matches. -</p> - -<h4 id="specifying-subqueries-in-yql">Specifying Subqueries in YQL</h4> -<p> -Subqueries are specified as maps where the key is a string -representation of either a hex number or a list of bit numbers, and -the value is a map of attribute key/value pairs. The two queries below demonstrates the -two different methods of mapping attributes to subqueries. - -<pre>select * from sources * where predicate(predicate_field, {"0x3":{"gender":"Female"}, "0x1":{"hobby":["music","hiking"]}}, {"0x2":{"age":23L}})</pre> -<pre>select * from sources * where predicate(predicate_field, {"[0,1]":{"gender":"Female"}, "[0]":{"hobby":["music","hiking"]}}, {"[1]":{"age":23L}})</pre> - -The queries above is constructed from the following two queries: - -<pre> -select * from sources * where predicate(predicate_field, {"gender":"Female", "hobby":["music","hiking"]},{}) -select * from sources * where predicate(predicate_field, {"gender":"Female"}, {"age":23L}) -</pre> -<p> - Note that the subquery bit numbers use zero-based numbering, e.g. first subquery has index <code>0</code>. - Highest valid subquery has index <code>63</code>.<br/> - Any value <code>0x1</code>-<code>0xFFFFFFFFFFFFFFFF</code> is a valid subquery bitmap. -</p> - {% include note.html content="When no subquery mapping is specified, the attribute is applied to all subqueries."%} - -<h4 id="identifying-subqueries-in-results">Identifying Subqueries in Results</h4> -<p> -When using subqueries you need to add the <code>subqueries</code> -summary feature to your schema. For each hit, the subqueries -are reported in two different summary features, one for the lower 32 -bits, named <code>lsb</code>, and one for the upper 32 bits, -named <code>msb</code>. -</p><p> - See the <a href="https://github.com/vespa-engine/sample-apps/tree/master/examples/predicate-fields"> - predicate search example</a> for how to configure a custom <em>searcher</em>, <em>services.xml</em> - and the <em>schema</em> - required to retrieve the subquery bitmap of each hit. -</p> - -<h4 id="predicate-example">Predicate Example</h4> -<p> -A typical use case for the subquery feature is when we want to find ads for -multiple positions on a page. The user profile information will -be identical for every subquery while the ad placement varies. The following example uses 3 different attributes; -<code>age</code>, <code>gender</code> and <code>pos</code>. The 2 former attributes represents the user profile, -while the <code>pos</code> attribute determines the ad placement. -Assume the following 3 documents are indexed: -</p> -<pre> -[ - { - "fields" : { - "target" : "age in [20..30] and gender in [Female, Male] and pos in [1]" - }, - "put" : "id:test:ad::1" - }, - { - "fields" : { - "target" : "gender in [Male] and pos in [1, 2]" - }, - "put" : "id:test:ad::2" - }, - { - "fields" : { - "target" : "age in [20..] and gender in [Female, Male] and pos in [2]" - }, - "put" : "id:test:ad::3" - } -] -</pre> -<p> -Find all ads that target males at age 25 for ad placement 1 and 2. -To do that, create a query consisting of two subqueries, one for placement 1 and the other for placement 2: -</p> -<pre>select * from sources * where predicate(target, {"[0,1]":{"gender":"Male"}, "[0]":{"pos": "1"}, "[1]":{"pos": "2"}}, {"[0,1]":{"age":25L}})</pre> -Note that each subquery has a separate value for <code>pos</code>, while the <code>gender</code> and <code>age</code> values are common for both subqueries. -<p> -The query will return 3 hits, one for each document. Each document will have a <em>summary feature</em> with the subquery bitmap (64-bit). -This is assuming that the <code>SubqueriesSearcher</code> from the sample app is used. If not so, each document will have two summary features, -one for the lower 32-bit and one for the upper 32-bit of the subquery bitmap. -</p> -<ul> -<li>The document with id <code>id:test:ad::1</code> will have subquery bitmap of - <code>0x1</code>; the lowest bit set to 1 as the document is a hit for subquery #1.</li> -<li>The document with id <code>id:test:ad::2</code> is a hit for both subqueries and has the two lowest bits set to 1, - giving <code>0x3</code> as subquery bitmap.</li> -<li>Following the same principle, the subquery bitmap of <code>id:test:ad::3</code> is <code>0x2</code>.</li> -</ul> - - - -<h2 id="configuration">Configuration</h2> -<p> -Note: Using predicate fields is complex and tuning the configuration for performance requires -insight in the underlying algorithms. -</p><p> -A field of type predicate requires an index definition -with a mandatory parameter, <code>arity</code>, a value which trades index size -for query complexity. See <a href="#index-size">Index Size</a> for more -details. Fields of type predicate also accept three other optional parameters: -<code>lower-bound</code>, <code>upper-bound</code> and <code>dense-posting-list-threshold</code>. -These properties are helpful in optimizing query performance and index size. The two former parameters -sets the lower and upper bounds on values of range attributes. The latter value determines how the boolean index -is structured, trading index size for potentially better query performance. -</p><p> -To feed a predicate, put it in a field of type -<a href="../reference/schemas/schemas.html#predicate">predicate</a> as a string - -refer to the <a href="../reference/schemas/document-json-format.html#predicate">JSON reference</a>. -</p> - - -<h3 id="schema">Schema</h3> -<p> -The following schema example sets up an attribute predicate field -including the mandatory arity parameter. -</p> -<pre> -schema example { - document example { - - field predicate_field type predicate { - indexing: attribute - index { - arity: 2 # mandatory - lower-bound: 3 - upper-bound: 200 - dense-posting-list-threshold: 0.25 - } - } - - } - - # For subquery reporting: - rank-profile default { - summary-features: subqueries(predicate_field).lsb subqueries(predicate_field).msb - } -} -</pre> - -<h3 id="upper-and-lower-bounds">Upper and Lower Bounds</h3> -<p> -The <code>upper-bound</code> and <code>lower-bound</code> parameters -specify the range of values that the boolean expressions are expected -to operate on. Queries with values outside this range are -rejected. The index is optimized based on the bounds, so if the bounds -are changed, the index needs to be rebuilt. -</p> - - -<h3 id="dense-posting-list-threshold">Dense Posting List Threshold</h3> -<p> -The <code>dense-posting-list-threshold</code> parameter is a threshold that impacts how the -boolean index is structured in memory. The boolean index consists of several sparse data structures -(B-tree based posting lists). The largest posting lists are also stored in a dense vector based structure. -The dense posting lists are faster for searching, but may increase the overall index size significantly. -Only posting lists with relative size above the threshold are stored in the dense format -(for a corpus of 1mill documents and threshold=0.5, all posting lists of size >500k will be stored as vector). -The optimal value depends on corpus characteristics and will lay somewhere between 0.15 - 0.50. -A too low threshold will have large, negative impact on both query performance and index size, -while a too large threshold may slightly decrease the query performance. -</p><p> -The default value is 0.40. Valid range is (0, 1]. -</p> - - -<h3 id="index-size">Index Size</h3> -<p> -When using range attributes, the attributes are expanded to a set of -attributes for sub-ranges that together covers the entire range. The -granularity of the sub-ranges are controlled by the parameter -<code>arity</code>. A low arity will make smaller indexes, but -require more terms in the queries. Conversely, a high arity makes for -large indexes but fewer query terms. -</p><p> -Also impacting index size is the size of intervals that are accepted -in the boolean constraints. A typical case is intervals with infinite -endpoints, i.e. match every number greater than <em>x</em>. Using 2^63 -as infinity makes the intervals large, and impacts index size. A lower -max-value reduces the index size. The max-values can be easily -controlled with the <code>upper-bound</code> -and <code>lower-bound</code> parameters. -</p><p> -The <code>dense-posting-list-threshold</code> parameter has an inverse impact on the index size. -Increasing the threshold is beneficial if a smaller index size is preferred over query performance. -</p><p> -The following figure shows how the number of terms for a single -document grows with increasing arity and range limit. -</p> - - <script type="text/javascript" src="https://www.google.com/jsapi"></script> - <script type="text/javascript"> - google.load('visualization', '1', {packages: ['corechart']}); - </script> - <script type="text/javascript"> - function drawVisualization() { - // Create and populate the data table. - let data = google.visualization.arrayToDataTable([ - [ 'Bound (2^k)', 'Arity 2', 'Arity 4', 'Arity 8', 'Arity 16', 'Arity 32', 'Arity 64', 'Arity 128'], - [ 10 , 12, 18, 20, 30, 43, 27, 12 ], - [ 11 , 13, 19, 22, 34, 44, 43, 20 ], - [ 12 , 14, 21, 26, 42, 46, 75, 36 ], - [ 13 , 15, 22, 27, 43, 50, 76, 68 ], - [ 14 , 16, 24, 29, 45, 58, 78, 132 ], - [ 15 , 17, 25, 33, 49, 74, 82, 133 ], - [ 16 , 18, 27, 34, 57, 75, 90, 135 ], - [ 17 , 19, 28, 36, 58, 77, 106, 139 ], - [ 18 , 20, 30, 40, 60, 81, 138, 147 ], - [ 19 , 21, 31, 41, 64, 89, 139, 163 ], - [ 20 , 22, 33, 43, 72, 105, 141, 195 ], - [ 21 , 23, 34, 47, 73, 106, 145, 259 ], - [ 22 , 24, 36, 48, 75, 108, 153, 260 ], - [ 23 , 25, 37, 50, 79, 112, 169, 262 ], - [ 24 , 26, 39, 54, 87, 120, 201, 266 ], - [ 25 , 27, 40, 55, 88, 136, 202, 274 ], - [ 26 , 28, 42, 57, 90, 137, 204, 290 ], - [ 27 , 29, 43, 61, 94, 139, 208, 322 ], - [ 28 , 30, 45, 62, 102, 143, 216, 386 ], - [ 29 , 31, 46, 64, 103, 151, 232, 387 ], - [ 30 , 32, 48, 68, 105, 167, 264, 389 ], - [ 31 , 33, 49, 69, 109, 168, 265, 393 ], - [ 32 , 34, 51, 71, 117, 170, 267, 401 ], - [ 33 , 35, 52, 75, 118, 174, 271, 417 ], - [ 34 , 36, 54, 76, 120, 182, 279, 449 ], - [ 35 , 37, 55, 78, 124, 198, 295, 513 ], - [ 36 , 38, 57, 82, 132, 199, 327, 514 ], - [ 37 , 39, 58, 83, 133, 201, 328, 516 ], - [ 38 , 40, 60, 85, 135, 205, 330, 520 ], - [ 39 , 41, 61, 89, 139, 213, 334, 528 ], - [ 40 , 42, 63, 90, 147, 229, 342, 544 ], - [ 41 , 43, 64, 92, 148, 230, 358, 576 ], - [ 42 , 44, 66, 96, 150, 232, 390, 640 ], - [ 43 , 45, 67, 97, 154, 236, 391, 641 ], - [ 44 , 46, 69, 99, 162, 244, 393, 643 ], - [ 45 , 47, 70, 103, 163, 260, 397, 647 ], - [ 46 , 48, 72, 104, 165, 261, 405, 655 ], - [ 47 , 49, 73, 106, 169, 263, 421, 671 ], - [ 48 , 50, 75, 110, 177, 267, 453, 703 ], - [ 49 , 51, 76, 111, 178, 275, 454, 767 ], - [ 50 , 52, 78, 113, 180, 291, 456, 768 ], - [ 51 , 53, 79, 117, 184, 292, 460, 770 ], - [ 52 , 54, 81, 118, 192, 294, 468, 774 ], - [ 53 , 55, 82, 120, 193, 298, 484, 782 ], - [ 54 , 56, 84, 124, 195, 306, 516, 798 ], - [ 55 , 57, 85, 125, 199, 322, 517, 830 ], - [ 56 , 58, 87, 127, 207, 323, 519, 894 ], - [ 57 , 59, 88, 131, 208, 325, 523, 895 ], - [ 58 , 60, 90, 132, 210, 329, 531, 897 ], - [ 59 , 61, 91, 134, 214, 337, 547, 901 ], - [ 60 , 62, 93, 138, 222, 353, 579, 909 ], - [ 61 , 63, 94, 139, 223, 354, 580, 925 ], - [ 62 , 64, 96, 141, 225, 356, 582, 957 ], - [ 63 , 65, 97, 145, 229, 360, 586, 1021 ] - ]); - - // Create and draw the visualization. - new google.visualization.LineChart(document.getElementById('visualization')). - draw(data, {curveType: "none", - title: 'Index size vs. range limit and arity', - width: 700, height: 600, - vAxis: {title: 'Term count', maxValue: 10}, - hAxis: {title: 'Range limit, 2^k'}} - ); - } - - - google.setOnLoadCallback(drawVisualization); - </script> -<div id="visualization" style="position: relative; "><div style="position: relative; width: 700px; height: 600px; " dir="ltr"><div style="position: absolute; left: 0; top: 0; width: 100%; height: 100%; "><svg width="700" height="600" style="overflow: hidden; "><defs id="defs"><clipPath id="_ABSTRACT_RENDERER_ID_0"><rect x="134" y="115" width="433" height="371"></rect></clipPath></defs><rect x="0" y="0" width="700" height="600" stroke="none" stroke-width="0" fill="#ffffff"></rect><g><text text-anchor="start" x="134" y="89.9" font-family="Arial" font-size="14" font-weight="bold" stroke="none" stroke-width="0" fill="#000000">Index size vs. range limit and arity</text></g><g><rect x="581" y="115" width="105" height="152" stroke="none" stroke-width="0" fill-opacity="0" fill="#ffffff"></rect><g><rect x="581" y="115" width="105" height="14" stroke="none" stroke-width="0" fill-opacity="0" fill="#ffffff"></rect><g><text text-anchor="start" x="600" y="126.9" font-family="Arial" font-size="14" stroke="none" stroke-width="0" fill="#222222">Arity 2</text></g><rect x="581" y="115" width="14" height="14" stroke="none" stroke-width="0" fill="#3366cc"></rect></g><g><rect x="581" y="138" width="105" height="14" stroke="none" stroke-width="0" fill-opacity="0" fill="#ffffff"></rect><g><text text-anchor="start" x="600" y="149.9" font-family="Arial" font-size="14" stroke="none" stroke-width="0" fill="#222222">Arity 4</text></g><rect x="581" y="138" width="14" height="14" stroke="none" stroke-width="0" fill="#dc3912"></rect></g><g><rect x="581" y="161" width="105" height="14" stroke="none" stroke-width="0" fill-opacity="0" fill="#ffffff"></rect><g><text text-anchor="start" x="600" y="172.9" font-family="Arial" font-size="14" stroke="none" stroke-width="0" fill="#222222">Arity 8</text></g><rect x="581" y="161" width="14" height="14" stroke="none" stroke-width="0" fill="#ff9900"></rect></g><g><rect x="581" y="184" width="105" height="14" stroke="none" stroke-width="0" fill-opacity="0" fill="#ffffff"></rect><g><text text-anchor="start" x="600" y="195.9" font-family="Arial" font-size="14" stroke="none" stroke-width="0" fill="#222222">Arity 16</text></g><rect x="581" y="184" width="14" height="14" stroke="none" stroke-width="0" fill="#109618"></rect></g><g><rect x="581" y="207" width="105" height="14" stroke="none" stroke-width="0" fill-opacity="0" fill="#ffffff"></rect><g><text text-anchor="start" x="600" y="218.9" font-family="Arial" font-size="14" stroke="none" stroke-width="0" fill="#222222">Arity 32</text></g><rect x="581" y="207" width="14" height="14" stroke="none" stroke-width="0" fill="#990099"></rect></g><g><rect x="581" y="230" width="105" height="14" stroke="none" stroke-width="0" fill-opacity="0" fill="#ffffff"></rect><g><text text-anchor="start" x="600" y="241.9" font-family="Arial" font-size="14" stroke="none" stroke-width="0" fill="#222222">Arity 64</text></g><rect x="581" y="230" width="14" height="14" stroke="none" stroke-width="0" fill="#0099c6"></rect></g><g><rect x="581" y="253" width="105" height="14" stroke="none" stroke-width="0" fill-opacity="0" fill="#ffffff"></rect><g><text text-anchor="start" x="600" y="264.9" font-family="Arial" font-size="14" stroke="none" stroke-width="0" fill="#222222">Arity 128</text></g><rect x="581" y="253" width="14" height="14" stroke="none" stroke-width="0" fill="#dd4477"></rect></g></g><g><rect x="134" y="115" width="433" height="371" stroke="none" stroke-width="0" fill-opacity="0" fill="#ffffff"></rect><g clip-path="url(#_ABSTRACT_RENDERER_ID_0)"><g><rect x="216" y="115" width="1" height="371" stroke="none" stroke-width="0" fill="#cccccc"></rect><rect x="297" y="115" width="1" height="371" stroke="none" stroke-width="0" fill="#cccccc"></rect><rect x="379" y="115" width="1" height="371" stroke="none" stroke-width="0" fill="#cccccc"></rect><rect x="460" y="115" width="1" height="371" stroke="none" stroke-width="0" fill="#cccccc"></rect><rect x="542" y="115" width="1" height="371" stroke="none" stroke-width="0" fill="#cccccc"></rect><rect x="134" y="485" width="433" height="1" stroke="none" stroke-width="0" fill="#cccccc"></rect><rect x="134" y="393" width="433" height="1" stroke="none" stroke-width="0" fill="#cccccc"></rect><rect x="134" y="300" width="433" height="1" stroke="none" stroke-width="0" fill="#cccccc"></rect><rect x="134" y="208" width="433" height="1" stroke="none" stroke-width="0" fill="#cccccc"></rect><rect x="134" y="115" width="433" height="1" stroke="none" stroke-width="0" fill="#cccccc"></rect></g><g><rect x="134" y="115" width="1" height="371" stroke="none" stroke-width="0" fill="#333333"></rect><rect x="134" y="485" width="433" height="1" stroke="none" stroke-width="0" fill="#333333"></rect></g><g><path d="M134.5,481.8L142.6509433962264,481.4916666666667L150.80188679245282,481.18333333333334L158.95283018867923,480.875L167.10377358490567,480.56666666666666L175.25471698113208,480.2583333333333L183.4056603773585,479.95L191.5566037735849,479.64166666666665L199.70754716981133,479.3333333333333L207.85849056603774,479.025L216.00943396226415,478.71666666666664L224.16037735849056,478.40833333333336L232.31132075471697,478.1L240.46226415094338,477.7916666666667L248.6132075471698,477.48333333333335L256.7641509433962,477.175L264.91509433962267,476.8666666666667L273.066037735849,476.55833333333334L281.2169811320755,476.25L289.3679245283019,475.94166666666666L297.5188679245283,475.6333333333333L305.6698113207547,475.325L313.8207547169811,475.01666666666665L321.97169811320754,474.7083333333333L330.12264150943395,474.4L338.2735849056604,474.09166666666664L346.42452830188677,473.78333333333336L354.57547169811323,473.475L362.7264150943396,473.1666666666667L370.87735849056605,472.85833333333335L379.02830188679246,472.55L387.1792452830189,472.2416666666667L395.3301886792453,471.93333333333334L403.4811320754717,471.625L411.6320754716981,471.31666666666666L419.7830188679245,471.0083333333333L427.9339622641509,470.7L436.08490566037733,470.39166666666665L444.2358490566038,470.0833333333333L452.3867924528302,469.775L460.5377358490566,469.46666666666664L468.688679245283,469.1583333333333L476.83962264150944,468.85L484.99056603773585,468.5416666666667L493.14150943396226,468.23333333333335L501.29245283018867,467.925L509.4433962264151,467.6166666666667L517.5943396226414,467.30833333333334L525.7452830188679,467L533.8962264150944,466.69166666666666L542.0471698113208,466.3833333333333L550.1981132075472,466.075L558.3490566037735,465.76666666666665L566.5,465.4583333333333" stroke="#3366cc" stroke-width="2" fill-opacity="1" fill="none"></path><path d="M134.5,479.95L142.6509433962264,479.64166666666665L150.80188679245282,479.025L158.95283018867923,478.71666666666664L167.10377358490567,478.1L175.25471698113208,477.7916666666667L183.4056603773585,477.175L191.5566037735849,476.8666666666667L199.70754716981133,476.25L207.85849056603774,475.94166666666666L216.00943396226415,475.325L224.16037735849056,475.01666666666665L232.31132075471697,474.4L240.46226415094338,474.09166666666664L248.6132075471698,473.475L256.7641509433962,473.1666666666667L264.91509433962267,472.55L273.066037735849,472.2416666666667L281.2169811320755,471.625L289.3679245283019,471.31666666666666L297.5188679245283,470.7L305.6698113207547,470.39166666666665L313.8207547169811,469.775L321.97169811320754,469.46666666666664L330.12264150943395,468.85L338.2735849056604,468.5416666666667L346.42452830188677,467.925L354.57547169811323,467.6166666666667L362.7264150943396,467L370.87735849056605,466.69166666666666L379.02830188679246,466.075L387.1792452830189,465.76666666666665L395.3301886792453,465.15L403.4811320754717,464.84166666666664L411.6320754716981,464.225L419.7830188679245,463.9166666666667L427.9339622641509,463.3L436.08490566037733,462.9916666666667L444.2358490566038,462.375L452.3867924528302,462.06666666666666L460.5377358490566,461.45L468.688679245283,461.14166666666665L476.83962264150944,460.525L484.99056603773585,460.21666666666664L493.14150943396226,459.6L501.29245283018867,459.2916666666667L509.4433962264151,458.675L517.5943396226414,458.3666666666667L525.7452830188679,457.75L533.8962264150944,457.44166666666666L542.0471698113208,456.825L550.1981132075472,456.51666666666665L558.3490566037735,455.9L566.5,455.59166666666664" stroke="#dc3912" stroke-width="2" fill-opacity="1" fill="none"></path><path d="M134.5,479.3333333333333L142.6509433962264,478.71666666666664L150.80188679245282,477.48333333333335L158.95283018867923,477.175L167.10377358490567,476.55833333333334L175.25471698113208,475.325L183.4056603773585,475.01666666666665L191.5566037735849,474.4L199.70754716981133,473.1666666666667L207.85849056603774,472.85833333333335L216.00943396226415,472.2416666666667L224.16037735849056,471.0083333333333L232.31132075471697,470.7L240.46226415094338,470.0833333333333L248.6132075471698,468.85L256.7641509433962,468.5416666666667L264.91509433962267,467.925L273.066037735849,466.69166666666666L281.2169811320755,466.3833333333333L289.3679245283019,465.76666666666665L297.5188679245283,464.5333333333333L305.6698113207547,464.225L313.8207547169811,463.60833333333335L321.97169811320754,462.375L330.12264150943395,462.06666666666666L338.2735849056604,461.45L346.42452830188677,460.21666666666664L354.57547169811323,459.9083333333333L362.7264150943396,459.2916666666667L370.87735849056605,458.05833333333334L379.02830188679246,457.75L387.1792452830189,457.1333333333333L395.3301886792453,455.9L403.4811320754717,455.59166666666664L411.6320754716981,454.975L419.7830188679245,453.7416666666667L427.9339622641509,453.43333333333334L436.08490566037733,452.81666666666666L444.2358490566038,451.5833333333333L452.3867924528302,451.275L460.5377358490566,450.6583333333333L468.688679245283,449.425L476.83962264150944,449.1166666666667L484.99056603773585,448.5L493.14150943396226,447.26666666666665L501.29245283018867,446.9583333333333L509.4433962264151,446.34166666666664L517.5943396226414,445.10833333333335L525.7452830188679,444.8L533.8962264150944,444.18333333333334L542.0471698113208,442.95L550.1981132075472,442.64166666666665L558.3490566037735,442.025L566.5,440.7916666666667" stroke="#ff9900" stroke-width="2" fill-opacity="1" fill="none"></path><path d="M134.5,476.25L142.6509433962264,475.01666666666665L150.80188679245282,472.55L158.95283018867923,472.2416666666667L167.10377358490567,471.625L175.25471698113208,470.39166666666665L183.4056603773585,467.925L191.5566037735849,467.6166666666667L199.70754716981133,467L207.85849056603774,465.76666666666665L216.00943396226415,463.3L224.16037735849056,462.9916666666667L232.31132075471697,462.375L240.46226415094338,461.14166666666665L248.6132075471698,458.675L256.7641509433962,458.3666666666667L264.91509433962267,457.75L273.066037735849,456.51666666666665L281.2169811320755,454.05L289.3679245283019,453.7416666666667L297.5188679245283,453.125L305.6698113207547,451.89166666666665L313.8207547169811,449.425L321.97169811320754,449.1166666666667L330.12264150943395,448.5L338.2735849056604,447.26666666666665L346.42452830188677,444.8L354.57547169811323,444.4916666666667L362.7264150943396,443.875L370.87735849056605,442.64166666666665L379.02830188679246,440.175L387.1792452830189,439.8666666666667L395.3301886792453,439.25L403.4811320754717,438.01666666666665L411.6320754716981,435.55L419.7830188679245,435.2416666666667L427.9339622641509,434.625L436.08490566037733,433.39166666666665L444.2358490566038,430.925L452.3867924528302,430.6166666666667L460.5377358490566,430L468.688679245283,428.76666666666665L476.83962264150944,426.3L484.99056603773585,425.9916666666667L493.14150943396226,425.375L501.29245283018867,424.14166666666665L509.4433962264151,421.675L517.5943396226414,421.3666666666667L525.7452830188679,420.75L533.8962264150944,419.51666666666665L542.0471698113208,417.05L550.1981132075472,416.7416666666667L558.3490566037735,416.125L566.5,414.89166666666665" stroke="#109618" stroke-width="2" fill-opacity="1" fill="none"></path><path d="M134.5,472.2416666666667L142.6509433962264,471.93333333333334L150.80188679245282,471.31666666666666L158.95283018867923,470.0833333333333L167.10377358490567,467.6166666666667L175.25471698113208,462.68333333333334L183.4056603773585,462.375L191.5566037735849,461.7583333333333L199.70754716981133,460.525L207.85849056603774,458.05833333333334L216.00943396226415,453.125L224.16037735849056,452.81666666666666L232.31132075471697,452.2L240.46226415094338,450.9666666666667L248.6132075471698,448.5L256.7641509433962,443.56666666666666L264.91509433962267,443.2583333333333L273.066037735849,442.64166666666665L281.2169811320755,441.4083333333333L289.3679245283019,438.94166666666666L297.5188679245283,434.0083333333333L305.6698113207547,433.7L313.8207547169811,433.0833333333333L321.97169811320754,431.85L330.12264150943395,429.3833333333333L338.2735849056604,424.45L346.42452830188677,424.14166666666665L354.57547169811323,423.525L362.7264150943396,422.2916666666667L370.87735849056605,419.825L379.02830188679246,414.89166666666665L387.1792452830189,414.5833333333333L395.3301886792453,413.9666666666667L403.4811320754717,412.73333333333335L411.6320754716981,410.26666666666665L419.7830188679245,405.3333333333333L427.9339622641509,405.025L436.08490566037733,404.4083333333333L444.2358490566038,403.175L452.3867924528302,400.7083333333333L460.5377358490566,395.775L468.688679245283,395.4666666666667L476.83962264150944,394.85L484.99056603773585,393.6166666666667L493.14150943396226,391.15L501.29245283018867,386.2166666666667L509.4433962264151,385.9083333333333L517.5943396226414,385.29166666666663L525.7452830188679,384.05833333333334L533.8962264150944,381.5916666666667L542.0471698113208,376.6583333333333L550.1981132075472,376.35L558.3490566037735,375.73333333333335L566.5,374.5" stroke="#990099" stroke-width="2" fill-opacity="1" fill="none"></path><path d="M134.5,477.175L142.6509433962264,472.2416666666667L150.80188679245282,462.375L158.95283018867923,462.06666666666666L167.10377358490567,461.45L175.25471698113208,460.21666666666664L183.4056603773585,457.75L191.5566037735849,452.81666666666666L199.70754716981133,442.95L207.85849056603774,442.64166666666665L216.00943396226415,442.025L224.16037735849056,440.7916666666667L232.31132075471697,438.325L240.46226415094338,433.39166666666665L248.6132075471698,423.525L256.7641509433962,423.21666666666664L264.91509433962267,422.6L273.066037735849,421.3666666666667L281.2169811320755,418.9L289.3679245283019,413.9666666666667L297.5188679245283,404.1L305.6698113207547,403.79166666666663L313.8207547169811,403.175L321.97169811320754,401.94166666666666L330.12264150943395,399.475L338.2735849056604,394.54166666666663L346.42452830188677,384.675L354.57547169811323,384.3666666666667L362.7264150943396,383.75L370.87735849056605,382.51666666666665L379.02830188679246,380.05L387.1792452830189,375.1166666666667L395.3301886792453,365.25L403.4811320754717,364.94166666666666L411.6320754716981,364.325L419.7830188679245,363.09166666666664L427.9339622641509,360.625L436.08490566037733,355.69166666666666L444.2358490566038,345.825L452.3867924528302,345.51666666666665L460.5377358490566,344.9L468.688679245283,343.66666666666663L476.83962264150944,341.2L484.99056603773585,336.26666666666665L493.14150943396226,326.4L501.29245283018867,326.0916666666667L509.4433962264151,325.475L517.5943396226414,324.2416666666667L525.7452830188679,321.775L533.8962264150944,316.8416666666667L542.0471698113208,306.975L550.1981132075472,306.66666666666663L558.3490566037735,306.04999999999995L566.5,304.81666666666666" stroke="#0099c6" stroke-width="2" fill-opacity="1" fill="none"></path><path d="M134.5,481.8L142.6509433962264,479.3333333333333L150.80188679245282,474.4L158.95283018867923,464.5333333333333L167.10377358490567,444.8L175.25471698113208,444.4916666666667L183.4056603773585,443.875L191.5566037735849,442.64166666666665L199.70754716981133,440.175L207.85849056603774,435.2416666666667L216.00943396226415,425.375L224.16037735849056,405.64166666666665L232.31132075471697,405.3333333333333L240.46226415094338,404.7166666666667L248.6132075471698,403.48333333333335L256.7641509433962,401.01666666666665L264.91509433962267,396.0833333333333L273.066037735849,386.2166666666667L281.2169811320755,366.48333333333335L289.3679245283019,366.175L297.5188679245283,365.55833333333334L305.6698113207547,364.325L313.8207547169811,361.85833333333335L321.97169811320754,356.92499999999995L330.12264150943395,347.05833333333334L338.2735849056604,327.325L346.42452830188677,327.01666666666665L354.57547169811323,326.4L362.7264150943396,325.16666666666663L370.87735849056605,322.7L379.02830188679246,317.76666666666665L387.1792452830189,307.9L395.3301886792453,288.16666666666663L403.4811320754717,287.85833333333335L411.6320754716981,287.2416666666667L419.7830188679245,286.0083333333333L427.9339622641509,283.54166666666663L436.08490566037733,278.60833333333335L444.2358490566038,268.7416666666667L452.3867924528302,249.00833333333333L460.5377358490566,248.7L468.688679245283,248.08333333333331L476.83962264150944,246.85L484.99056603773585,244.38333333333333L493.14150943396226,239.45L501.29245283018867,229.58333333333331L509.4433962264151,209.84999999999997L517.5943396226414,209.54166666666663L525.7452830188679,208.925L533.8962264150944,207.69166666666666L542.0471698113208,205.22499999999997L550.1981132075472,200.29166666666663L558.3490566037735,190.425L566.5,170.69166666666666" stroke="#dd4477" stroke-width="2" fill-opacity="1" fill="none"></path></g></g><g></g><g><g><text text-anchor="middle" x="216.00943396226415" y="506.9" font-family="Arial" font-size="14" stroke="none" stroke-width="0" fill="#444444">20</text></g><g><text text-anchor="middle" x="297.5188679245283" y="506.9" font-family="Arial" font-size="14" stroke="none" stroke-width="0" fill="#444444">30</text></g><g><text text-anchor="middle" x="379.02830188679246" y="506.9" font-family="Arial" font-size="14" stroke="none" stroke-width="0" fill="#444444">40</text></g><g><text text-anchor="middle" x="460.5377358490566" y="506.9" font-family="Arial" font-size="14" stroke="none" stroke-width="0" fill="#444444">50</text></g><g><text text-anchor="middle" x="542.0471698113208" y="506.9" font-family="Arial" font-size="14" stroke="none" stroke-width="0" fill="#444444">60</text></g><g><text text-anchor="end" x="120" y="490.4" font-family="Arial" font-size="14" stroke="none" stroke-width="0" fill="#444444">0</text></g><g><text text-anchor="end" x="120" y="397.9" font-family="Arial" font-size="14" stroke="none" stroke-width="0" fill="#444444">300</text></g><g><text text-anchor="end" x="120" y="305.4" font-family="Arial" font-size="14" stroke="none" stroke-width="0" fill="#444444">600</text></g><g><text text-anchor="end" x="120" y="212.9" font-family="Arial" font-size="14" stroke="none" stroke-width="0" fill="#444444">900</text></g><g><text text-anchor="end" x="120" y="120.4" font-family="Arial" font-size="14" stroke="none" stroke-width="0" fill="#444444">1,200</text></g></g></g><g><g><text text-anchor="middle" x="350.5" y="559.4" font-family="Arial" font-size="14" font-style="italic" stroke="none" stroke-width="0" fill="#222222">Range limit, 2^k</text></g><g><text text-anchor="middle" x="46.9" y="300.5" font-family="Arial" font-size="14" font-style="italic" transform="rotate(-90 46.9 300.5)" stroke="none" stroke-width="0" fill="#222222">Term count</text></g></g><g></g></svg></div></div><div style="display: none; position: absolute; top: 610px; left: 710px; white-space: nowrap; font-family: Arial; font-size: 14px; font-weight: bold; ">259</div><div></div></div> - - -<h2 id="grammar">Grammar</h2> -<pre> -predicate = disjunction <EOF> ; -disjunction = conjunction [ 'or' disjunction ] ; -conjunction = ( leaf | [ 'not' ], '(', disjunction, ')' ) [ 'and' conjunction ] ; -leaf = value, [ 'not' ], 'in', ( value | multivalue | range ) - | 'true' - | 'false' ; - -value = alphanum { alphanum } | string ; -multivalue = '[' value, { ',', value } ']' ; -range = '[' [ integer ] '..' [ integer ] ']' ; - -alphanum = alpha | digit | '_'; -string = '\'', { stdchars_1 | escape_1 }, '\'' - | '"', { stdchars_2 | escape_2 }, '"' ; - -integer = [ '-' | '+' ], ( posdigit, { digit } | '0' ); - -alpha = ? ASCII characters in the range a-z and A-Z ? ; -digit = '0' | posdigit ; -posdigit = '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' ; -stdchars_1 = ? All unicode chars except '\\' and '\'' ? ; -stdchars_2 = ? All unicode chars except '\\' and '"' ? ; -escape_1 = '\\', ( '\\' | 't' | 'n' | 'f' | 'r' | '\'' | 'x', hexdigit, hexdigit ) -escape_2 = '\\', ( '\\' | 't' | 'n' | 'f' | 'r' | '"' | 'x', hexdigit, hexdigit ) -hexdigit = digit | 'a' | 'b' | 'c' | 'd' | 'e' | 'f' | 'A' | 'B' | 'C' | 'D' | 'E' | 'F' ; -</pre> diff --git a/en/schemas/predicate-fields.mdx b/en/schemas/predicate-fields.mdx new file mode 100644 index 0000000000..9a80dc1775 --- /dev/null +++ b/en/schemas/predicate-fields.mdx @@ -0,0 +1,329 @@ +--- +title: "Predicate Fields" +--- + +[Predicate](/en/reference/schemas/schemas#predicate) fields provides a way to match queries to a set of *boolean constraints* in a document. Example use cases: + +- Boolean constraints in advertisements, specifying their target groups. Query with a set of impressions, i.e., specific values for a given user, to find out which ads can be shown to this user. +- Saved searches for e-commerce. Users save searches in the form of predicates, and new items are matched to the saved searches. + +Example document and query, with a `target` [predicate field](/en/reference/schemas/schemas#predicate): + +```json +{ + "put": "id:ns:user::12345", + "fields": { + "target": "gender in ['male'] and age in [30..40] and income in [200..50000]", + "name": "John Doe" + } +} +``` + +```js +select name from user where predicate(target, {"gender":"male"}, {"income":30000L}) +``` + +Read more in the [Predicate Search sample application](https://github.com/vespa-engine/sample-apps/blob/master/examples/predicate-fields/README.md). + +There are some trade-offs between index size and query performance when configuring predicate fields, see [configuration](#configuration). + +Predicate fields are good for solving problems where practitioners have used Percolator Queries. + +<Note> +**Note:** + +Predicate fields are not supported in [streaming search](/en/performance/streaming-search#differences-in-streaming-search). +</Note> + +## Boolean Constraints + +A boolean constraint (predicate) specifies a target area for queries to land in. Its attributes may be simple true/false criteria, subsets of sets to match, or ranges of values. + +### Predicates + +A predicate is a specification of a boolean constraint in the form of a boolean expression. For example, the predicate `gender in [Female] and age in [20..30] and pos in [1..4]` can specify that an ad requires target users to be women between 20 and 30 years of age, and that the ad must be placed in one of the top four positions. + +See [grammar](#grammar) for details. + +### Attributes + +The variables in predicates are known as *attributes*. There are two types of attributes: + +- **Regular attributes**. Regular attributes take string values. Specify in the predicate that a regular attribute must have one value of multiple alternatives. E.g. `hobby in [Music, Hiking]` evaluates true if hobby is assigned to either `Music` or `Hiking` (or both). +- **Range attributes**. Range attributes take integer values and may only be used in range expressions. A range expression specifies either a lower bound, an upper bound or both: + 1. `age in [10..]` - age must be 10 or higher + 2. `age in [..10]` - age must be 10 or lower + 3. `age in [10..15]` - age must be between 10 and 15, inclusive + +### Predicate Samples + +The subset expression evaluates to true if the regular attribute is assigned to any of the values listed in the brackets: + +```js +hobby in [Music, Hiking, Biking] +``` + +The range expression evaluates to true if the range attribute is in the specified range (boundaries are inclusive): + +```js +age in [20..29] +``` + +It's also possible to specify only the lower or upper bound for a range expression: + +```js +age in [..29] +``` + +Use the `or` operator to create disjunctions: + +```js +age in [..29] or hobby in [Music, Biking] +``` + +Similarly, use the `and` operator to create conjunctions: + +```js +age in [20..29] and hobby in [Music] +``` + +Parenthesis can be used to create more complex predicates: + +```js +(age in [20..29] and gender in [Male]) or (age in [30..39] and gender in [Female]) +``` + +The subset and range expression can be negated using the `not` operator: + +```js +age not in [20..29] and hobby not in [Music] +``` + +```js +not age in [20..29] and not hobby in [Music] +``` + +The `not` operator can also be combined with parenthesis: + +```js +not (age in [20..29] or hobby in [Music]) +``` + +Attributes and values containing non-alphanumeric letters must be surrounded with quotes: + +```js +"profile.gender" in ['Male', "Female"] +``` + +If a string surrounded with double-quotes contains a double-quote, escape it with backslash. Same rule applies for single quotes in single-quoted strings. Double quotes in single-quoted strings and single quotes in double-quoted string shall not be escaped. + +```js +"single'quote" in ["double\"quote", 'double"quote', 'single\'quote'] +``` + +Set the predicate to the value true to make it always a match. Setting the predicate to false will ensure that it's never a match. + +```js +true +``` + +```js +false +``` + +## Queries + +A boolean query represents a set of concrete values for attributes, which may fall within the target area drawn up by one or more sets of boolean constraints. Queries are specified by two lists of attributes with values. One list holds regular attributes, each with one or more discrete values, while the other list holds range attributes with a single value each. + +### Search Using YQL+ + +Boolean queries are made using the `predicate` function of YQL+. The predicate function takes three parameters: The predicate field, a map of regular attribute key/value pairs, and a map of range attribute key/value pairs. + +```js +select * from sources * where predicate(predicate_field, {"gender":"Female", "gender":"Male", hobby:"Hiking"}, {"age":20L, "pos":2L}) +``` + +One can use empty maps when specifying attributes: + +```js +select * from sources * where predicate(predicate_field, {}, {"age":20L}) +``` + +When specifying multiple values for the same key, it is possible to use an array as the value: + +```js +select * from sources * where predicate(predicate_field,{"gender":["Female","Male"], "hobby":"Hiking"}, {"age":20L}) +``` + +### Subqueries + +For efficiency reasons it is possible to specify multiple queries at once. This is done by providing a bitmap with each term, where the bitmap represents which (out of 64) subqueries the term is a part of. A typical use case for this is when we want to find ads for multiple positions on a page. Then the user profile information will be part of every subquery while the ad placement varies. Remember that all subqueries are used every time, which means that empty subqueries also can get matches. + +#### Specifying Subqueries in YQL + +Subqueries are specified as maps where the key is a string representation of either a hex number or a list of bit numbers, and the value is a map of attribute key/value pairs. The two queries below demonstrates the two different methods of mapping attributes to subqueries. + +```js +select * from sources * where predicate(predicate_field, {"0x3":{"gender":"Female"}, "0x1":{"hobby":["music","hiking"]}}, {"0x2":{"age":23L}}) +``` + +```js +select * from sources * where predicate(predicate_field, {"[0,1]":{"gender":"Female"}, "[0]":{"hobby":["music","hiking"]}}, {"[1]":{"age":23L}}) +``` + +The queries above is constructed from the following two queries: + +```js +select * from sources * where predicate(predicate_field, {"gender":"Female", "hobby":["music","hiking"]},{}) +select * from sources * where predicate(predicate_field, {"gender":"Female"}, {"age":23L}) +``` + +Note that the subquery bit numbers use zero-based numbering, e.g. first subquery has index `0`. Highest valid subquery has index `63`. + +Any value `0x1`-`0xFFFFFFFFFFFFFFFF` is a valid subquery bitmap. + +<Note> +**Note:** + +When no subquery mapping is specified, the attribute is applied to all subqueries. +</Note> + +#### Identifying Subqueries in Results + +When using subqueries you need to add the `subqueries` summary feature to your schema. For each hit, the subqueries are reported in two different summary features, one for the lower 32 bits, named `lsb`, and one for the upper 32 bits, named `msb`. + +See the [predicate search example](https://github.com/vespa-engine/sample-apps/tree/master/examples/predicate-fields) for how to configure a custom *searcher*, *services.xml* and the *schema* required to retrieve the subquery bitmap of each hit. + +#### Predicate Example + +A typical use case for the subquery feature is when we want to find ads for multiple positions on a page. The user profile information will be identical for every subquery while the ad placement varies. The following example uses 3 different attributes; `age`, `gender` and `pos`. The 2 former attributes represents the user profile, while the `pos` attribute determines the ad placement. Assume the following 3 documents are indexed: + +```json +[ + { + "fields" : { + "target" : "age in [20..30] and gender in [Female, Male] and pos in [1]" + }, + "put" : "id:test:ad::1" + }, + { + "fields" : { + "target" : "gender in [Male] and pos in [1, 2]" + }, + "put" : "id:test:ad::2" + }, + { + "fields" : { + "target" : "age in [20..] and gender in [Female, Male] and pos in [2]" + }, + "put" : "id:test:ad::3" + } +] +``` + +Find all ads that target males at age 25 for ad placement 1 and 2. To do that, create a query consisting of two subqueries, one for placement 1 and the other for placement 2: + +```js +select * from sources * where predicate(target, {"[0,1]":{"gender":"Male"}, "[0]":{"pos": "1"}, "[1]":{"pos": "2"}}, {"[0,1]":{"age":25L}}) +``` + +Note that each subquery has a separate value for `pos`, while the `gender` and `age` values are common for both subqueries. + +The query will return 3 hits, one for each document. Each document will have a summary feature with the subquery bitmap (64-bit). This is assuming that the `SubqueriesSearcher` from the sample app is used. If not so, each document will have two summary features, one for the lower 32-bit and one for the upper 32-bit of the subquery bitmap. + +- The document with id `id:test:ad::1` will have subquery bitmap of `0x1`; the lowest bit set to 1 as the document is a hit for subquery #1. +- The document with id `id:test:ad::2` is a hit for both subqueries and has the two lowest bits set to 1, giving `0x3` as subquery bitmap. +- Following the same principle, the subquery bitmap of `id:test:ad::3` is `0x2`. + +## Configuration + +<Note> +**Note:** + +Using predicate fields is complex and tuning the configuration for performance requires insight in the underlying algorithms. +</Note> + +A field of type predicate requires an index definition with a mandatory parameter, `arity`, a value which trades index size for query complexity. See [Index Size](#index-size) for more details. Fields of type predicate also accept three other optional parameters: `lower-bound`, `upper-bound` and `dense-posting-list-threshold`. These properties are helpful in optimizing query performance and index size. The two former parameters sets the lower and upper bounds on values of range attributes. The latter value determines how the boolean index is structured, trading index size for potentially better query performance. + +To feed a predicate, put it in a field of type [predicate](/en/reference/schemas/schemas#predicate) as a string - refer to the [JSON reference](/en/reference/schemas/document-json-format#predicate). + +### Schema + +The following schema example sets up an attribute predicate field including the mandatory arity parameter. + +```js +schema example { + document example { + + field predicate_field type predicate { + indexing: attribute + index { + arity: 2 # mandatory + lower-bound: 3 + upper-bound: 200 + dense-posting-list-threshold: 0.25 + } + } + + } + + # For subquery reporting: + rank-profile default { + summary-features: subqueries(predicate_field).lsb subqueries(predicate_field).msb + } +} +``` + +### Upper and Lower Bounds + +The `upper-bound` and `lower-bound` parameters specify the range of values that the boolean expressions are expected to operate on. Queries with values outside this range are rejected. The index is optimized based on the bounds, so if the bounds are changed, the index needs to be rebuilt. + +### Dense Posting List Threshold + +The `dense-posting-list-threshold` parameter is a threshold that impacts how the boolean index is structured in memory. The boolean index consists of several sparse data structures (B-tree based posting lists). The largest posting lists are also stored in a dense vector based structure. The dense posting lists are faster for searching, but may increase the overall index size significantly. Only posting lists with relative size above the threshold are stored in the dense format (for a corpus of 1mill documents and threshold=0.5, all posting lists of size >500k will be stored as vector). The optimal value depends on corpus characteristics and will lay somewhere between 0.15 - 0.50. A too low threshold will have large, negative impact on both query performance and index size, while a too large threshold may slightly decrease the query performance. + +The default value is 0.40. Valid range is (0, 1]. + +### Index Size + +When using range attributes, the attributes are expanded to a set of attributes for sub-ranges that together covers the entire range. The granularity of the sub-ranges are controlled by the parameter `arity`. A low arity will make smaller indexes, but require more terms in the queries. Conversely, a high arity makes for large indexes but fewer query terms. + +Also impacting index size is the size of intervals that are accepted in the boolean constraints. A typical case is intervals with infinite endpoints, i.e. match every number greater than *x*. Using 2^63 as infinity makes the intervals large, and impacts index size. A lower max-value reduces the index size. The max-values can be easily controlled with the `upper-bound` and `lower-bound` parameters. + +The `dense-posting-list-threshold` parameter has an inverse impact on the index size. Increasing the threshold is beneficial if a smaller index size is preferred over query performance. + +The following figure shows how the number of terms for a single document grows with increasing arity and range limit: + +<Frame> +![](/assets/graph-image.png) +</Frame> + +## Grammar + +```js +predicate = disjunction <EOF> ; +disjunction = conjunction [ 'or' disjunction ] ; +conjunction = ( leaf | [ 'not' ], '(', disjunction, ')' ) [ 'and' conjunction ] ; +leaf = value, [ 'not' ], 'in', ( value | multivalue | range ) + | 'true' + | 'false' ; + +value = alphanum { alphanum } | string ; +multivalue = '[' value, { ',', value } ']' ; +range = '[' [ integer ] '..' [ integer ] ']' ; + +alphanum = alpha | digit | '_'; +string = '\'', { stdchars_1 | escape_1 }, '\'' + | '"', { stdchars_2 | escape_2 }, '"' ; + +integer = [ '-' | '+' ], ( posdigit, { digit } | '0' ); + +alpha = ? ASCII characters in the range a-z and A-Z ? ; +digit = '0' | posdigit ; +posdigit = '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' ; +stdchars_1 = ? All unicode chars except '\\' and '\'' ? ; +stdchars_2 = ? All unicode chars except '\\' and '"' ? ; +escape_1 = '\\', ( '\\' | 't' | 'n' | 'f' | 'r' | '\'' | 'x', hexdigit, hexdigit ) +escape_2 = '\\', ( '\\' | 't' | 'n' | 'f' | 'r' | '"' | 'x', hexdigit, hexdigit ) +hexdigit = digit | 'a' | 'b' | 'c' | 'd' | 'e' | 'f' | 'A' | 'B' | 'C' | 'D' | 'E' | 'F' ; +``` diff --git a/en/schemas/structs.html b/en/schemas/structs.html deleted file mode 100644 index 209683d6bc..0000000000 --- a/en/schemas/structs.html +++ /dev/null @@ -1,77 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Structs" -redirect_from: -- /en/structs.html ---- - -<p>This document explains how to use structs in Vespa documents.</p> - -<h2 id="structs">Structs</h2> - -<p>A <a href="../reference/schemas/schemas.html#struct">struct</a> -is contained in a document and groups one or more fields into -a composite type that can be accessed like a single field.</p> - -Example: -<pre> - struct email { - field sender type string {} - field recipient type string {} - field subject type string {} - field content type string {} - } - - field emails type array<email> {} -</pre> - -<p> -In this example the struct is part of an -<a href="../reference/schemas/schemas.html#array">array</a>. -A struct can also be used in a <a href="../reference/schemas/schemas.html#map">map</a>. -</p> - - -<h2 id="struct-fields">Struct fields</h2> - -<p> -A <a href="../reference/schemas/schemas.html#struct-field">struct-field</a> -defines how a given field in a struct should be indexed and searched. -</p> -<p> -Note that though a struct-field refers to a field in a struct, the struct-field itself is defined inside a field. -</p> - -<p> -Using the <em>email</em> struct defined previously (see <a href="../reference/schemas/schemas.html#struct">struct</a>), -we can define indexing for a specific field, like <em>content</em>: -</p> - -<pre> - field emails type array<email> { - indexing: summary - struct-field content { - indexing: attribute - attribute: fast-search - } - } -</pre> - -<p>The equivalent code (including the struct definition) in Pyvespa is as follows:</p> -<pre> - email_struct = Struct(name="email", fields=[ - Field(name="sender", type="string"), - Field(name="recipient", type="string"), - Field(name="subject", type="string"), - Field(name="content", type="string"), - ]) - emails_field = Field(name="emails", - type="array<email>", - indexing=["summary"], - struct_fields=[StructField(name="content", indexing=["attribute"], attribute=["fast-search"])] - ) - schema = Schema(name="schema", document=Document()) - schema.add_fields(emails_field) - schema.document.add_structs(email_struct) -</pre> - diff --git a/en/schemas/structs.mdx b/en/schemas/structs.mdx new file mode 100644 index 0000000000..7b9e6ea488 --- /dev/null +++ b/en/schemas/structs.mdx @@ -0,0 +1,60 @@ +--- +title: "Structs" +description: "This document explains how to use structs in Vespa documents." +--- + +## Structs + +A [struct](/en/reference/schemas/schemas#struct) is contained in a document and groups one or more fields into a composite type that can be accessed like a single field. + +Example: + +```js +struct email { + field sender type string {} + field recipient type string {} + field subject type string {} + field content type string {} +} + +field emails type array<email> {} +``` + +In this example the struct is part of an [array](/en/reference/schemas/schemas#array). A struct can also be used in a [map](/en/reference/schemas/schemas#map). + +## Struct fields + +A [struct-field](/en/reference/schemas/schemas#struct-field) defines how a given field in a struct should be indexed and searched. + +Note that though a struct-field refers to a field in a struct, the struct-field itself is defined inside a field. + +Using the *email* struct defined previously (see [struct](/en/reference/schemas/schemas#struct)), we can define indexing for a specific field, like *content*: + +```js +field emails type array<email> { + indexing: summary + struct-field content { + indexing: attribute + attribute: fast-search + } +} +``` + +The equivalent code (including the struct definition) in Pyvespa is as follows: + +```python +email_struct = Struct(name="email", fields=[ + Field(name="sender", type="string"), + Field(name="recipient", type="string"), + Field(name="subject", type="string"), + Field(name="content", type="string"), +]) +emails_field = Field(name="emails", + type="array<email>", + indexing=["summary"], + struct_fields=[StructField(name="content", indexing=["attribute"], attribute=["fast-search"])] +) +schema = Schema(name="schema", document=Document()) +schema.add_fields(emails_field) +schema.document.add_structs(email_struct) +``` diff --git a/en/security/cloudflare-workers.md b/en/security/cloudflare-workers.mdx similarity index 70% rename from en/security/cloudflare-workers.md rename to en/security/cloudflare-workers.mdx index a7c0acbb7c..8cefc79d61 100644 --- a/en/security/cloudflare-workers.md +++ b/en/security/cloudflare-workers.mdx @@ -1,56 +1,45 @@ --- -# Copyright Vespa.ai. All rights reserved. -title: Using Cloudflare Workers with Vespa Cloud -applies_to: cloud -redirect_from: - - /en/cloud/security/cloudflare-workers +title: "Using Cloudflare Workers with Vespa Cloud" +sidebarTitle: "Cloudflare Workers" --- - -This guide describes how you can access mutual TLS protected Vespa Cloud endpoints using -[Cloudflare Workers](https://workers.cloudflare.com/). +This guide describes how you can access mutual TLS protected Vespa Cloud endpoints using [Cloudflare Workers](https://workers.cloudflare.com/). ## Writing and reading from Vespa Cloud Endpoints -Vespa Cloud's endpoints are protected using mutual TLS. -This means the client must present a TLS certificate that the Vespa application trusts. The application -knows which certificate to trust because the certificate is included in the -Vespa application package. +Vespa Cloud's endpoints are protected using mutual TLS. This means the client must present a TLS certificate that the Vespa application trusts. The application knows which certificate to trust because the certificate is included in the Vespa application package. ### mTLS Configuration -Mutual TLS certificates can be created using the -[Vespa CLI](../clients/vespa-cli.html): +Mutual TLS certificates can be created using the [Vespa CLI](/en/clients/vespa-cli): For example, for tenant `samples` with application `vsearch` and instance `default`: -``` +```bash $ vespa auth cert --application samples.vsearch.default Success: Certificate written to security/clients.pem Success: Certificate written to $HOME/.vespa/samples.vsearch.default/data-plane-public-cert.pem Success: Private key written to $HOME/.vespa/samples.vsearch.default/data-plane-private-key.pem ``` -Refer to the [security guide](guide) for details. +Refer to the [security guide](/en/security/guide) for details. ### Creating a Cloudflare Worker to interact with mTLS Vespa Cloud endpoints -In March 2023, Cloudflare announced [Mutual TLS available for Workers](https://blog.cloudflare.com/mtls-workers/), -see also [Workers Runtime API mTLS](https://developers.cloudflare.com/workers/runtime-apis/mtls/). -Install wrangler and create a worker project. -Wrangler is the Cloudflare command line interface (CLI), refer to -[Workers:Get started guide](https://developers.cloudflare.com/workers/get-started/guide/). -Once configured and authenticated, one can upload the Vespa Cloud data plane certificates to Cloudflare. +In March 2023, Cloudflare announced [Mutual TLS available for Workers](https://blog.cloudflare.com/mtls-workers/), see also [Workers Runtime API mTLS](https://developers.cloudflare.com/workers/runtime-apis/mtls/). + +Install wrangler and create a worker project. Wrangler is the Cloudflare command line interface (CLI), refer to [Workers:Get started guide](https://developers.cloudflare.com/workers/get-started/guide/). Once configured and authenticated, one can upload the Vespa Cloud data plane certificates to Cloudflare. Upload Vespa Cloud mTLS certificates to Cloudflare: -``` +```bash $ npx wrangler mtls-certificate upload \ --cert $HOME/.vespa/samples.vsearch.default/data-plane-public-cert.pem \ --key $HOME/.vespa/samples.vsearch.default/data-plane-private-key.pem \ --name vector-search-dev ``` + The output will look something like this: -``` +```bash Uploading mTLS Certificate vector-search-dev... Success! Uploaded mTLS Certificate vector-search-dev ID: 63316464-1404-4462-baf7-9e9f81114d81 @@ -58,11 +47,9 @@ Issuer: CN=cloud.vespa.example Expires on 3/11/2033 ``` -Notice the `ID` in the output; This is the `certificate_id` of the uploaded mTLS certificate. -To use the certificate in the worker code, add an `mtls_certificates` variable to the `wrangler.toml` file -in the project to bind a name to the certificate id. In this case, bind to `VESPA_CERT`: +Notice the `ID` in the output; This is the `certificate_id` of the uploaded mTLS certificate. To use the certificate in the worker code, add an `mtls_certificates` variable to the `wrangler.toml` file in the project to bind a name to the certificate id. In this case, bind to `VESPA_CERT`: -``` +```toml mtls_certificates = [ { binding = "VESPA_CERT", certificate_id = "63316464-1404-4462-baf7-9e9f81114d81" } ] @@ -78,14 +65,13 @@ export default { } ``` -Notice that `env` is a variable passed by the Cloudflare worker infrastructure. +Notice that `env` is a variable passed by the Cloudflare worker infrastructure. ### Worker example -The following worker example forwards POST and GET HTTP requests to the `/search/` path -of the Vespa cloud endpoint. It rejects other paths or other HTTP methods. +The following worker example forwards POST and GET HTTP requests to the `/search/` path of the Vespa cloud endpoint. It rejects other paths or other HTTP methods. -```javascript +```js expandable /** * Simple Vespa proxy that forwards read (POST and GET) requests to the * /search/ endpoint @@ -124,33 +110,34 @@ export default { return await env.VESPA_CERT.fetch(new_request) }, }; - ``` + To deploy the above to the worldwide global edge network of Cloudflare, use: -``` +```bash $ npx wrangler publish ``` To start a local instance, use: -``` +```bash $ npx wrangler dev ``` Test using `curl`: -``` +```bash $ curl --json '{"yql": "select * from sources * where true"}' http://127.0.0.1:8787/search/ ``` After publishing to Cloudflare production: -``` +```bash $ curl --json '{"yql": "select * from sources * where true"}' https://your-worker-name.workers.dev/search/ ``` ## Data plane access control permissions + Vespa Cloud supports having multiple certificates to separate `read` and `write` access. This way, one can upload the read-only certificate to a Cloudflare worker to limit write access. -See [Data plane access control permissions](guide#permissions). +See [Data plane access control permissions](/en/security/guide#permissions). diff --git a/en/security/guide.md b/en/security/guide.mdx similarity index 54% rename from en/security/guide.md rename to en/security/guide.mdx index 8dcaebde2a..3b7d6b4191 100644 --- a/en/security/guide.md +++ b/en/security/guide.mdx @@ -1,25 +1,14 @@ --- -title: Security Guide -applies_to: cloud -redirect_from: -- /en/cloud/security/guide +title: "Security Guide" --- -Vespa Cloud has several security mechanisms it is important for developers to -understand. Vespa Cloud has two different interaction paths, _Data Plane_ and -_Control Plane_. Communication with the Vespa application goes through the _Data -Plane_, while the _Control Plane_ is used to manage Vespa tenants and -applications. - -The _Control Plane_ and the _Data Plane_ has different security mechanisms, -described in this guide. - +Vespa Cloud has several security mechanisms it is important for developers to understand. Vespa Cloud has two different interaction paths, *Data Plane* and *Control Plane*. Communication with the Vespa application goes through the *Data Plane*, while the *Control Plane* is used to manage Vespa tenants and applications. +The *Control Plane* and the *Data Plane* has different security mechanisms, described in this guide. ## SOC 2 -Vespa.ai has a SOC 2 attestation - read more in the [Trust Center](https://trust.vespa.ai/). - +Vespa.ai has a SOC 2 attestation - read more in the [Trust Center](https://trust.vespa.ai/). ## Data Plane @@ -27,79 +16,76 @@ Data plane requests are protected using mutual TLS, or optionally tokens. ### Configuring mTLS -Certificates can be created using the -[Vespa CLI](../clients/vespa-cli.html): +Certificates can be created using the [Vespa CLI](/en/clients/vespa-cli): -``` +```bash $ vespa auth cert --application <tenant>.<app>.<instance> ``` -``` +```bash $ vespa auth cert --application scoober.albums.default Success: Certificate written to security/clients.pem Success: Certificate written to $HOME/.vespa/scoober.albums.default/data-plane-public-cert.pem Success: Private key written to $HOME/.vespa/scoober.albums.default/data-plane-private-key.pem ``` -The certificates can be created regardless of the application existence in Vespa -Cloud. One can use this command to generate <code>security/clients.pem</code> -for an application package: +The certificates can be created regardless of the application existence in Vespa Cloud. One can use this command to generate `security/clients.pem` for an application package: -``` +```bash $ cp $HOME/.vespa/scoober.albums.default/data-plane-public-cert.pem security/clients.pem ``` Certificates can also be created using OpenSSL: -``` +```bash $ openssl req -x509 -sha256 -days 1825 -newkey rsa:2048 -keyout key.pem -out security/clients.pem ``` -The certificate is placed inside the application package in -[security/clients.pem](../reference/applications/application-packages.html). Make sure -`clients.pem` is placed correctly if the certificate is created with OpenSSL, -while the Vespa CLI will handle this automatically. - -`security/clients.pem` files can contain multiple PEM encoded certificates by -concatenating them. This allows you to have multiple clients with separate -private keys, making it possible to rotate to a new certificate without any -downtime. +The certificate is placed inside the application package in [security/clients.pem](/en/reference/applications/application-packages). Make sure `clients.pem` is placed correctly if the certificate is created with OpenSSL, while the Vespa CLI will handle this automatically. +`security/clients.pem` files can contain multiple PEM encoded certificates by concatenating them. This allows you to have multiple clients with separate private keys, making it possible to rotate to a new certificate without any downtime. ### Permissions -To support different permissions for clients, it is possible to limit the permissions of a client. Only `read` or `write` permissions are supported. +To support different permissions for clients, it is possible to limit the permissions of a client. Only `read` or `write` permissions are supported. #### Request mapping + The request actions are mapped from HTTP method. The default mapping rule is: -* GET → `read` -* PUT, POST, DELETE → `write` + +- GET → `read` +- PUT, POST, DELETE → `write` For `/search/` this is replaced by: -* GET, POST → `read` + +- GET, POST → `read` #### Example -Create 3 different certificates, for three different use cases: -* Serving - `read` -* Ingest - `write` -* Full access - `read, write` +Create 3 different certificates, for three different use cases: -``` +- Serving - `read` +- Ingest - `write` +- Full access - `read, write` + +```bash $ openssl req -x509 -sha256 -days 1825 -newkey rsa:2048 -keyout key.pem -out security/serve.pem $ openssl req -x509 -sha256 -days 1825 -newkey rsa:2048 -keyout key.pem -out security/ingest.pem $ openssl req -x509 -sha256 -days 1825 -newkey rsa:2048 -keyout key.pem -out security/full_access.pem ``` +<Note> Notes: -* Files must be placed in the <em>security</em> folder inside the application package -* Certificates must be unique -* Certificate chains are currently not supported -* Files must be written using PEM encoding + +- Files must be placed in the *security* folder inside the application package +- Certificates must be unique +- Certificate chains are currently not supported +- Files must be written using PEM encoding +</Note> Reference the certificate files from services xml using the `clients` element: -```xml +```xml expandable <container version='1.0'> ... <clients> @@ -116,11 +102,12 @@ Reference the certificate files from services xml using the `clients` element: ... </container> ``` + #### Custom request mapping The default mapping can be changed by overriding `requestHandlerSpec()`: -```java +```java expandable /** * Example overriding acl mapping of POST requests to read */ @@ -139,44 +126,71 @@ public class CustomAclHandler extends ThreadedHttpRequestHandler { } ``` - ### Configuring tokens -Application endpoints can also be configured with token based authentication. -Note that it is still required to define at least one client for mTLS. +Application endpoints can also be configured with token based authentication. Note that it is still required to define at least one client for mTLS. + +<Note> +**Note:** -{% include note.html content=' -Token authentication must be explicitly enabled when used in combination with -<a href="../operations/private-endpoints.html">Private Endpoints</a>. -'%} +Token authentication must be explicitly enabled when used in combination with [Private Endpoints](/en/operations/private-endpoints). +</Note> #### Creating tokens using the console -Tokens are identified by a name, and can contain multiple versions to easily support token rotation. +Tokens are identified by a name, and can contain multiple versions to easily support token rotation. To create a new token: -1. In the [console](https://console.vespa.ai) tenant view, open [**Account > Tokens**](https://console.vespa-cloud.com/link/tenant/account/tokens) -1. Click **Add token** -1. Enter a name you'll reference in the application later and click **Add**. Remember to copy the token value and store it securely. + +<Steps> +<Step> +In the [console](https://console.vespa.ai) tenant view, open [**Account > Tokens**](https://console.vespa-cloud.com/link/tenant/account/tokens) +</Step> +<Step> +Click **Add token** +</Step> +<Step> +Enter a name you'll reference in the application later and click **Add**. Remember to copy the token value and store it securely. +</Step> +</Steps> To add a new token *version*: -1. Find the existing token, click **Add version** -1. Select expiration and click **Add**. Copy the token value and store securely. + +<Steps> +<Step> +Find the existing token, click **Add version** +</Step> +<Step> +Select expiration and click **Add**. Copy the token value and store securely. +</Step> +</Steps> To revoke a version: -1. Find the existing token version, click **Revoke** + +<Steps> +<Step> +Find the existing token version, click **Revoke** +</Step> +</Steps> To manually rotate a token: -1. Add a new token *version* following the above steps -1. Revoke the old version when no clients use the old version + +<Steps> +<Step> +Add a new token *version* following the above steps +</Step> +<Step> +Revoke the old version when no clients use the old version +</Step> +</Steps> #### Application configuration with token endpoints -After creating a token, it must be configured in your application's services.xml by adding the -[clients](../reference/applications/services/container.html#clients) element to your container cluster(s). +After creating a token, it must be configured in your application's services.xml by adding the [clients](/en/reference/applications/services/container#clients) element to your container cluster(s). Here is an example with multiple container clusters and tokens (you may only have one): -```xml + +```xml expandable <container id="documentapi" version="1.0"> ... <clients> @@ -203,33 +217,30 @@ Here is an example with multiple container clusters and tokens (you may only hav </container> ``` - #### Security recommendations The cryptographic properties of token authentication vs mTLS are comparable. There are however a few key differences in how they are used: -* tokens are sent as a header with every request -* since they are part of the request they are also more easily leaked in log outputs or source code (e.g. curl commands). -It is therefore recommended to -* create tokens with a short expiry (keeping the default of 30 days). -* keep tokens in a secret provider, and remember to hide output. -* never commit secret tokens into source code repositories! +- tokens are sent as a header with every request +- since they are part of the request they are also more easily leaked in log outputs or source code (e.g. curl commands). +It is therefore recommended to +- create tokens with a short expiry (keeping the default of 30 days). +- keep tokens in a secret provider, and remember to hide output. +- never commit secret tokens into source code repositories! ### Use endpoints #### Using mTLS -Once the application is configured and deployed with a certificate in the -application package, requests can be sent to the application. Again, the Vespa -CLI can help to use the correct certificate. +Once the application is configured and deployed with a certificate in the application package, requests can be sent to the application. Again, the Vespa CLI can help to use the correct certificate. -``` +```bash $ vespa curl --application <tenant>.<app>.<instance> /ApplicationStatus ``` -``` +```bash $ curl --key $HOME/.vespa/scoober.albums.default/data-plane-private-key.pem \ --cert $HOME/.vespa/scoober.albums.default/data-plane-public-key.pem \ $ENDPOINT @@ -237,17 +248,15 @@ $ curl --key $HOME/.vespa/scoober.albums.default/data-plane-private-key.pem \ #### Using tokens -The token endpoint must be used when using tokens. -After deployment is complete, the token endpoint will be available in the token endpoint list (marked “Token”). -To use the token endpoint, the token should be sent as a bearer authorization header: +The token endpoint must be used when using tokens. After deployment is complete, the token endpoint will be available in the token endpoint list (marked “Token”). To use the token endpoint, the token should be sent as a bearer authorization header: -``` +```bash $ vespa query \ --header="Authorization: Bearer $TOKEN" \ 'yql=select * from music where album contains "head"' ``` -<!-- ToDo: Does this work with document api ? --> -``` + +```bash curl -H "Authorization: Bearer $TOKEN" $ENDPOINT ``` @@ -255,58 +264,50 @@ curl -H "Authorization: Bearer $TOKEN" $ENDPOINT In Vespa guides, curl is used in examples, like: -``` +```bash $ curl --cert ./data-plane-public-cert.pem --key ./data-plane-private-key.pem $ENDPOINT ``` -To use a browser, install key/cert pair into KeyChain Access (MacOS Sonoma), assuming Certificate Common Name is -"cloud.vespa.example" (as in the guides): +To use a browser, install key/cert pair into KeyChain Access (MacOS Sonoma), assuming Certificate Common Name is "cloud.vespa.example" (as in the guides): -<ol> - <li> - Install key/cert pair: -<pre> +<Steps> +<Step> +Install key/cert pair: + +```bash $ cat data-plane-public-cert.pem data-plane-private-key.pem > pkcs12.pem $ openssl pkcs12 -export -out pkcs12.p12 -in pkcs12.pem -</pre> - </li> - - <li> - New password will be requested, and it will be used in the next steps. - </li> - - <li> - In Keychain Access: With login keychain - <ul> - <li> Click "File" -> Import Items. </li> - <li> Choose pkcs12.p12 file created before and type the password. </li> - <li> Double-click the imported certificate, open "Trust" and set "When using this certificate" to "Always Trust". </li> - <li> Right-click and "New Certificate Preference...", then add the $ENDPOINT. </li> - </ul> - </li> - <li> - Open the same URL in Chrome, choose the example.com certificate and allow Chrome to read the private key. - </li> -</ol> +``` +</Step> +<Step> +New password will be requested, and it will be used in the next steps. +</Step> +<Step> +In Keychain Access: With login keychain + - Click "File" -> Import Items. + - Choose pkcs12.p12 file created before and type the password. + - Double-click the imported certificate, open "Trust" and set "When using this certificate" to "Always Trust". + - Right-click and "New Certificate Preference...", then add the $ENDPOINT. +</Step> +<Step> +Open the same URL in Chrome, choose the example.com certificate and allow Chrome to read the private key. +</Step> +</Steps> #### Using Postman -Many developers prefer interactive tools like -<a href="https://postman.com/">Postman</a>. The Vespa blog has an article on -<a href="https://blog.vespa.ai/interface-with-vespa-apis-using-postman/">how to -use Postman with Vespa</a>. +Many developers prefer interactive tools like [Postman](https://postman.com/). The Vespa blog has an article on [how to use Postman with Vespa](https://blog.vespa.ai/interface-with-vespa-apis-using-postman/). #### Using Cloudflare Workers -See [Using Cloudflare Workers with Vespa Cloud](cloudflare-workers). +See [Using Cloudflare Workers with Vespa Cloud](/en/security/cloudflare-workers). ### Different credentials per instance -To use different credentials per [instance](../learn/tenant-apps-instances.html), -use [services.xml variants](../operations/deployment-variants.html#services.xml-variants). -As an example, use this to have a separate MTLS keypair for production instances -(use the same pattern if using tokens.): -```xml + +To use different credentials per [instance](/en/learn/tenant-apps-instances), use [services.xml variants](/en/operations/deployment-variants#servicesxml-variants). As an example, use this to have a separate MTLS keypair for production instances (use the same pattern if using tokens.): + +```xml expandable <container id="default" version="1.0"> <clients> <client id="mtls" permissions="read" deploy:instance="default"> @@ -324,41 +325,29 @@ As an example, use this to have a separate MTLS keypair for production instances </container> ``` -Depending on the [instance](../operations/automated-deployments.html) deployed to, -a different keypair will be used for dataplane access. -Use the same mechanism to have a dedicated credential for the [dev](../operations/environments.html#dev) environment, -using `deploy:environment="dev"`. - - +Depending on the [instance](/en/operations/automated-deployments) deployed to, a different keypair will be used for dataplane access. Use the same mechanism to have a dedicated credential for the [dev](/en/operations/environments#dev) environment, using `deploy:environment="dev"`. ## Control Plane The control plane is used to manage the Vespa applications. -There are two different ways for access the Control Plane, using -`vespa auth login` to log in as a regular user and using Application Keys. -`vespa auth login` is intended for developers deploying manually to dev, while -Application Keys are intended for deploying applications to production, -typically by a continuous build tool. See more about these two methods below. +There are two different ways for access the Control Plane, using `vespa auth login` to log in as a regular user and using Application Keys. `vespa auth login` is intended for developers deploying manually to dev, while Application Keys are intended for deploying applications to production, typically by a continuous build tool. See more about these two methods below. ### Managing users Tenant administrators manage user access through the Vespa Console. -<img src="/assets/img/manage-users.png" alt="Vespa Console user management" - width="800px" height="auto" /> +<Frame>![Vespa Console user management](/assets/img/manage-users.png)</Frame> Users have two different privilege levels - **Admin:** Can administrate the tenants metadata and the users of the tenant. - **Developer:** Can administrate the applications deployed in the tenant. - ### User access to Control Plane -Outside using the Vespa Console, communicating with the Control Plane is easiest -with the [Vespa CLI](../clients/vespa-cli.html). +Outside using the Vespa Console, communicating with the Control Plane is easiest with the [Vespa CLI](/en/clients/vespa-cli). -``` +```bash $ vespa auth login Your Device Confirmation code is: ****-**** @@ -372,33 +361,25 @@ Waiting for login to complete in browser ... done Successfully logged in. ``` -After logging in with the Vespa CLI, the CLI can be used to deploy applications. -Users are logged in with the same privilege as the user described in the Vespa -Console. +After logging in with the Vespa CLI, the CLI can be used to deploy applications. Users are logged in with the same privilege as the user described in the Vespa Console. ### Application Key -If programmatic access to the Control Plane is needed, for example from a CI/CD -system like GitHub Actions, the Application Key can be used - -see example [deploy-vector-search.yaml](https://github.com/vespa-cloud/vector-search/blob/main/.github/workflows/deploy-vector-search.yaml). +If programmatic access to the Control Plane is needed, for example from a CI/CD system like GitHub Actions, the Application Key can be used - see example [deploy-vector-search.yaml](https://github.com/vespa-cloud/vector-search/blob/main/.github/workflows/deploy-vector-search.yaml). #### Configuration -The Application Key can be generated in the Console from the Deployment Screen. -The key is generated in the browser but the private key appears as a download in -the browser. The public key can be downloaded separately from Deployment Screen. -The private key is never persisted in Vespa Cloud, so it is important that the -private key is kept securely. If lost, the private key is unrecoverable. +The Application Key can be generated in the Console from the Deployment Screen. The key is generated in the browser but the private key appears as a download in the browser. The public key can be downloaded separately from Deployment Screen. The private key is never persisted in Vespa Cloud, so it is important that the private key is kept securely. If lost, the private key is unrecoverable. -<img src="/assets/img/application-key.png" alt="Vespa Console application key management"> +<Frame>![Vespa Console application key management](/assets/img/application-key.png)</Frame> The Application Key can also be generated using the Vespa CLI. -``` +```bash $ vespa auth api-key -a <tenant>.<app>.<instance> ``` -``` +```bash expandable $ vespa auth api-key -a scoober.albums.default Success: API private key written to $HOME/.vespa/scoober.api-key.pem @@ -418,18 +399,12 @@ and paste the entire public key including the BEGIN and END lines. #### Using the application key -The Application Key can be used from the Vespa CLI to run requests again the -Control Plane. Action like deploying applications to Vespa Cloud. +The Application Key can be used from the Vespa CLI to run requests again the Control Plane. Action like deploying applications to Vespa Cloud. -``` +```bash $ vespa deploy -z dev.aws-us-east-1c ``` - - ## Dataplane access -Vespa Cloud users on paid plans have access to Vespa Cloud Support. -For cases where the Vespa Team needs access to the application's data to provide -support, the Vespa support personnel can request access after an explicit approval -from the customer in the open support case. +Vespa Cloud users on paid plans have access to Vespa Cloud Support. For cases where the Vespa Team needs access to the application's data to provide support, the Vespa support personnel can request access after an explicit approval from the customer in the open support case. diff --git a/en/security/mtls.md b/en/security/mtls.md deleted file mode 100644 index 65e8d2f734..0000000000 --- a/en/security/mtls.md +++ /dev/null @@ -1,501 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Securing Vespa with mutually authenticated TLS (mTLS)" -applies_to: self-managed -redirect_from: -- /en/mtls.html -- /en/operations-selfhosted/mtls ---- - -{% include note.html content="This document is relevant for **self-hosted Vespa** only." %} - -[Transport Layer Security (TLS)](https://datatracker.ietf.org/doc/html/rfc5246) is a protocol that uses cryptography -to enable secure, tamper-proof communication over the network. -This document describes the TLS functionality in Vespa and how to configure it. -When properly configured, TLS ensures only trusted Vespa services can talk to each other. -See accompanying [reference](../reference/security/mtls) for details on configuration syntax. - -By default, all communication between self-hosted Vespa nodes is *unauthenticated* and *unencrypted*. -This means anyone with network access can read and write data and potentially execute commands on the system. -*Enabling TLS is therefore a fundamental part of a secure Vespa installation*. - -You should configure TLS even if you already have a firewall set up to prevent outside connections to your system. -TLS helps protect against the case even where an attacker has managed to get a foothold inside your private network. -Vespa will in some future version require TLS for all internal communication. -To ensure you are ready for this, secure your systems as soon as possible. - -Vespa offers two separate planes of TLS connectivity: -* **HTTP(S) application containers.** This is the edge of your cluster where search queries and feed requests are handled. - Authentication and authorization for this plane is handled separately from Vespa-internal traffic; this is covered - in [Configuring Http Servers and Filters](../applications/http-servers-and-filters.html#tls). - See also [Securing the application container](securing-your-vespa-installation.html#securing-the-application-container). -* **Vespa-internal communication.** This is all communication between processes running on the nodes in your cluster. - This includes clients connecting directly to the backends instead of going through the application container APIs. - Only mutually authenticated TLS (mTLS) may be configured for this traffic. - -This document only covers **Vespa-internal communication**. - -Enabling TLS in Vespa means that all internal endpoints are mTLS protected, even HTTP servers for status pages and metrics. -Be especially aware of this if you have custom solutions in place for collecting -and aggregating low level metrics or status pages from the Vespa backends. -Though the terms *TLS* and *mTLS* may be used interchangeably in this document, *TLS* implies *mTLS* for all Vespa-internal traffic. - -Refer to the [multinode-HA](https://github.com/vespa-engine/sample-apps/tree/master/examples/operations/multinode-HA) -example application for a working example. - - -## Prerequisites - -This section assumes you have some experience with generating and using certificates and private keys. -For an introduction, see [Appendix A: setting up with a self-signed Certificate Authority](#appendix-a-setting-up-with-a-self-signed-certificate-authority) -which gives step-by-step instructions on setting up certificates that can be used internally for a -single Vespa application. - -In order to enable TLS, some extra files must be present on every node in your Vespa application: -* A file containing the X.509 certificates of all trusted Certificate Authorities in PEM format. -* A file containing the X.509 certificate chain that will be used by Vespa processes on the node. This is in standard PEM format. - The host's own certificate should be the first certificate listed in the file, followed by intermediate certificates (if any), separated by newlines. -* A file containing the private key corresponding to the certificate in the above chain, in PKCS#8 PEM format. - Note that Vespa does not currently support encrypted private key files. -* A JSON configuration file telling Vespa which certificate/key files to use, and to provide further options for authorization. - See [Writing a TLS configuration file](#writing-a-tls-configuration-file) for how to write these. - -How certificate and key material is distributed to the nodes is outside the scope of this article. -See [dedicated section](#automatic-reloading-of-crypto-material) for Vespa's support of automatic and live reloading of TLS credentials. - -{% include warning.html content="You should never include public Certificate Authorities as part of the list of Certificate Authorities you trust. -Only use CAs that you (or your organization) directly control or trust. This is to minimize the risk of malicious actors -exploiting bugs or authentication flaws in public CAs to pose as your organization and acquire certificates -that would allow them to directly access your system. -" %} - -{% include warning.html content="Private keys must be kept secret and protected against unauthorized access. -Make sure only the user running the Vespa processes can read the private key file. -Key and certificate files should only be writable by administrator users." %} - - - -## Configuring Vespa TLS -On any node running Vespa software, TLS is controlled via a single environment variable. -This variable contains an absolute path pointing to a JSON configuration file: -```shell -VESPA_TLS_CONFIG_FILE=/absolute/path/to/my-tls-config.json -``` - -This environment variable must be set to a valid file path before any Vespa services are started on the node. -All nodes in your Vespa application must have a TLS config file pointing to the certificates that are trusted by the other nodes. - -See [Vespa environment variables](../operations/self-managed/files-processes-and-ports.html#environment-variables) -for information on configuring environment variables for Vespa. - -Setting `VESPA_TLS_CONFIG_FILE` automatically enables TLS for all Vespa processes on the node. -Vespa command-line tools will automatically pick up the required configuration and work transparently. - -{% include important.html content="If this variable is not set, Vespa starts up in insecure mode without any TLS! -" %} - - -### Writing a TLS configuration file -The simplest possible configuration file only needs to know the certificates to trust and the certificate/key pair that identifies the node itself. -Example: -```json -{ - "files": { - "ca-certificates": "/absolute/path/to/ca-certs.pem", - "certificates": "/absolute/path/to/host-certs.pem", - "private-key": "/absolute/path/to/private-key.pem" - } -} -``` - -Set the environment variable, for example by appending to -[conf/vespa/default-env.txt](../operations/self-managed/files-processes-and-ports.html#environment-variables): - -```shell -override VESPA_TLS_CONFIG_FILE /absolute/path/to/my-tls-config.json -``` - -All file paths must be absolute. If a Vespa process cannot load one or more files, it will fail to start up. - - -### Configuring TLS peer authorization rules -For many simpler deployments, a dedicated self-signed Certificate Authority will be used for the Vespa cluster alone. -In that case simply being in possession of a valid certificate is enough to be authorized to access the cluster nodes; -no one except the Vespa nodes is expected to have such a certificate. More complex deployments may instead use -a shared CA, e.g. a corporate CA issuing certificates to nodes across many services and departments. -In that case simply having a valid certificate is not sufficient to be used as an authorization mechanism. - -You can constrain which certificates may access the internal Vespa service by using *authorization rules*. -These are consulted as part of every TLS handshake and must pass before any connection can be established. - -Authorization rules are specified as part of the JSON configuration file using the top-level [`authorized-peers`](../reference/security/mtls#top-level-elements) member. - -#### Example - -Let's assume our Vespa cluster consists of many nodes, each with their own certificate signed by a shared Certificate Authority. -Each certificate contains a Subject Alternate Name (SAN) DNS name entry of the form -`<unique-node-id>.mycluster.vespa.example.com`, where *unique-node-id* is unique per cluster node. -These nodes will be running the actual Vespa services and must all be able to talk to each other. - -Let's also assume there is a monitoring service that requires low-level access to the services. -Certificates presented by nodes belonging to this service will always have a Common Name (CN) value of -`vespa-monitoring.example.com` and a DNS SAN entry of the form `<instance>.<region>.monitor.example.com`. -Any monitoring instance in any us-east region must be able to access our cluster, but no others. - -Our TLS config file implementing these rules may look like this: - -```json -{ - "files": { - "ca-certificates": "/absolute/path/to/ca-certs.pem", - "certificates": "/absolute/path/to/host-certs.pem", - "private-key": "/absolute/path/to/private-key.pem" - }, - "authorized-peers": [ - { - "required-credentials": [ - { "field": "CN", "must-match": "vespa-monitoring.example.com" }, - { "field": "SAN_DNS", "must-match": "*.us-east-*.monitor.example.com" } - ], - "description": "Backend monitoring service access" - }, - { - "required-credentials": [ - { "field": "SAN_DNS", "must-match": "*.mycluster.vespa.example.com" } - ], - "description": "Cluster-internal node P2P access" - } - ] -} -``` - -See the [reference documentation](../reference/security/mtls#peer-authorization-rules) for details on syntax and semantics. - - -### Automatic reloading of crypto material -Vespa performs periodic reloading of the specified TLS configuration file. -Currently, this happens every 60 minutes. This reloading happens live and does not impact service availability. -Both certificates, the private key and authorization rules are reloaded. -Vespa currently does not watch the configuration file for changes, so altering the config file -or any of its dependencies does not trigger a reload by itself. - -If live reloading fails, the old configuration continues to be used and a warning is emitted to the local Vespa log. - -Vespa does not currently lock files before reading them. -To avoid race conditions where files are reloaded by Vespa while they are being written, -consider splitting file refreshing into multiple phases: -1. Instead of overwriting existing key/cert files, write *new* files with different file names. -1. Create a temporary TLS config JSON file pointing to these files. -1. Atomically rename the new TLS config file to the name specified by `VESPA_TLS_CONFIG_FILE`. -1. Garbage-collect the old files at a later point (for example at the next refresh time). - - - -## Setting up TLS for a new Vespa application or upgrading with downtime -With no Vespa services running on any nodes, ensure the `VESPA_TLS_CONFIG_FILE` environment variable is set to -a valid configuration file path on every node, -and [is visible to any Vespa start scripts](../operations/self-managed/files-processes-and-ports.html#environment-variables). -Start Vespa services as you normally would. Check cluster health with -[vespa-get-cluster-state](../reference/operations/self-managed/tools.html#vespa-get-cluster-state) -and check [vespa-logfmt](../reference/operations/self-managed/tools.html#vespa-logfmt) for any TLS-related error messages -that indicate a misconfiguration (such as certificate rejections etc.)—see the Troubleshooting section. -The cluster should quickly converge to an available state. - -This is the simplest and fastest way to enable TLS, and is highly recommend if downtime is acceptable. - - - -## Upgrading an existing non-TLS Vespa application to TLS without downtime -If you already have a Vespa application serving live traffic that you don't want to take down completely in -order to enable TLS, it's possible to perform a gradual, rolling upgrade. Doing this requires insecure and -TLS connections to be used alongside each other for some time, moving more and more nodes onto TLS. -Finally, once all nodes are speaking only TLS, the support for insecure connections must be removed entirely. - -To achieve this, Vespa supports a feature called *insecure mixed mode*. -Enabling mixed mode lets all servers handle both TLS and insecure traffic at the same time. - -Mixed mode is controlled via the value set in environment variable `VESPA_TLS_INSECURE_MIXED_MODE`. - -TLS rollout happens in 3 phases: - -**Phase 1:** clients do not use TLS, servers accept both TLS and plaintext clients -1. Set `VESPA_TLS_INSECURE_MIXED_MODE=plaintext_client_mixed_server`. -1. Set `VESPA_TLS_CONFIG` file as documented in [Configuring Vespa TLS](#configuring-vespa-tls). -1. Rolling restart of all Vespa services to make mixed mode take effect. - -**Phase 2:** clients use TLS, servers accept both TLS and plaintext clients -1. Set `VESPA_TLS_INSECURE_MIXED_MODE=tls_client_mixed_server`. -1. Rolling restart of all Vespa services to make mixed mode take effect. - -**Phase 3:** all clients and servers use TLS only -1. Remove the `VESPA_TLS_INSECURE_MIXED_MODE` environment variable. -1. Rolling restart of all Vespa services to make enforced TLS take effect. - -{% include warning.html content="The insecure mixed mode environment variable MUST be removed from all nodes -(and all services subsequently restarted) before a cluster can be considered secure. -Even a single service left with insecure mixed mode enabled could be used by a determined attacker as a jumpgate into -other (believed secure) services." %} - - - -## Verify configuration of TLS -Successful configuration should be verified at runtime once TLS is enabled on all nodes. -The [openssl s_client](https://www.openssl.org/docs/man1.1.1/man1/openssl-s_client.html) tool is suitable for this. -Connect to a Vespa service, e.g a configserver on port 19071 or a container on port 8080, and verify that `openssl s_client` -successfully completes the TLS handshake. - -```shell -$ openssl s_client -connect <hostname>:<port> \ - -CAfile /absolute/path/to/ca-certs.pem \ - -key /absolute/path/to/private-key.pem \ - -cert /absolute/path/to/host-cert.pem -``` - -Further, you should verify that servers require clients to authenticate by omitting `-key`/`-cert` from above command. -The `s_client` tool should print an error during handshake and exit immediately. -```shell -$ openssl s_client -connect <hostname>:<port> \ - -CAfile /absolute/path/to/ca-certs.pem -``` - -## FAQ -* **Q: Should TLS be used even if I have a latency-sensitive real-time search application?** - * A: Yes. The Vespa cloud team has run many such applications in production for a long time and the overhead imposed by TLS is negligible. - Significant effort have been spent tuning Vespa's TLS integrations to keep overhead to a minimum. -* **Q: How much overhead does TLS impose in practice?** - * A: With modern CPUs, expect somewhere around 1-2% extra CPU usage for symmetric encryption (i.e. active connections). Connection handshakes have an expected extra latency of 2-4 ms of CPU time (network latency not included) due to more expensive cryptographic operations. Vespa performs handshake operations in separate threads to avoid stalling other network traffic. Vespa also uses long-lived connections internally to reduce the number of handshakes. - -## Troubleshooting - -### Certificate validation fails due to mismatching hostnames - -Vespa enables the [HTTPS endpoint identification algorithm](https://datatracker.ietf.org/doc/html/rfc2818#section-3) by default. -This extra verification can only be used if all certificates have their respective host's IP addresses and hostnames -in the Subject / Subject Alternative Names extensions. -[Disable hostname validation](../reference/security/mtls#top-level-elements) if this is not the case. - - -## Appendix A: setting up with a self-signed Certificate Authority - -Our goal is to create cryptographic keys and certificates that can be used by Vespa for secure mTLS -communication within a single Vespa installation. - -This requires the following steps, which we'll go through below: - -1. [Creating a root Certificate Authority](#creating-a-root-certificate-authority-ca). - This is only done once, regardless of how many Vespa hosts you want to secure. -1. [Creating a private key and Certificate Signing Request (CSR) for each Vespa host](#creating-a-private-key-and-certificate-for-a-vespa-host). -1. [Signing the CSR using the CA, creating a certificate for each Vespa host](#sign-host-certificate). - -We'll be using the [OpenSSL command-line tool](https://www.openssl.org/docs/man1.1.1/man1/) to -generate all our crypto keys and certificates. - -{% include note.html content="If you are setting up Vespa in an organization -that already has procedures for provisioning keys and certificates, -you should first reach out to the team responsible for this to make sure you're following best practices." %} - - -### Creating a root Certificate Authority (CA) - -When a server (or client) presents a certificate as part of proving its identity to us, we -must have a way to determine if this information is trustworthy. We do this by verifying -if the certificate is _cryptographically signed_ by a [Certificate Authority (CA)](https://en.wikipedia.org/wiki/Certificate_authority) -that we already know we can trust. It is possible that the certificate is in fact signed by a CA that we don't -directly trust, but that in turn is signed by a CA that we _do_ trust. These are known as -_intermediate_ Certificate Authorities and are part of what's known as the _certificate chain_. -There may be more than one intermediate CA in a chain. In our simple setup we will not be using -any intermediate CAs. - -At the top of the chain sits a _root_ Certificate Authority. Since we trust the root CA, we also -implicitly trust any intermediate CA it has signed and in turn any leaf certificates such an -intermediate CA has signed. - -A root Certificate Authority is special in that it has no CA above it to sign in. It is _self-signed_. - -To create our own root CA for our Vespa installation we'll first create its [_private key_](https://en.wikipedia.org/wiki/Public-key_cryptography). - -We have two choices of what kind of key to create; either based on [RSA](https://en.wikipedia.org/wiki/RSA_(cryptosystem)) -or [Elliptic Curve (EC)](https://en.wikipedia.org/wiki/Elliptic-curve_cryptography) -cryptography. EC keys are faster to process than RSA-based keys and take up less space, but older -OS versions or cryptographic libraries may not support these. -In the latter case, RSA keys offer the highest level of backwards compatibility. - -(Recommended) either create an Elliptic Curve private key: -```shell -$ openssl ecparam -name prime256v1 -genkey -noout -out root-ca.key -``` - -**OR:** create an RSA private key: -```shell -$ openssl genrsa -out root-ca.key 2048 -``` - -The root CA private key is stored in `root-ca.key`. This key is used to sign certificates and the -file MUST therefore be kept secret! If it is compromised, an attacker can create any number of -valid certificates that impersonate your Vespa hosts. - -We'll now create our CA X.509 certificate, self-signed with the private key. Substitute the information -given in `-subj` with whatever is appropriate for you; it's not really important for our -simple usage. - -```shell -$ openssl req -new -x509 -nodes \ - -key root-ca.key \ - -out root-ca.pem \ - -subj '/C=US/L=California/O=ACME/OU=ACME test root CA' \ - -sha256 \ - -days 3650 -``` - -Copy the resulting `root-ca.pem` file to your Vespa node(s) and point the `"ca-certificates"` -field in the TLS config file to its absolute file path on the node. - -With both the CA key and certificate, we have what we need to start signing certificates for -the hosts Vespa will be running on. - - -### Creating a private key and certificate for a Vespa host - -_Note: This section can be repeated for each Vespa host in your application. -See [Alternatives to having a unique certificate per individual host](#alternatives-to-having-a-unique-certificate-per-individual-host) -for (possibly less secure) options that do not require doing this step per host._ - -Just like our CA our host needs its own private cryptographic key. - -If we're using Elliptic Curve keys: -```shell -$ openssl ecparam -name prime256v1 -genkey -noout -out host.key -``` - -**OR:** if we're using RSA keys: -```shell -$ openssl genrsa -out host.key 2048 -``` - -As part of creating the certificate we'll first create a [Certificate Signing Request (CSR)](https://en.wikipedia.org/wiki/Certificate_signing_request). -Again, you can substitute the information in `-subj` with something more appropriate for you. -```shell -$ openssl req -new \ - -key host.key -out host.csr \ - -subj '/C=US/L=California/OU=ACME/O=My Vespa App' \ - -sha256 -``` - -#### Sign host certificate - -By default, Vespa runs with TLS hostname validation enabled, which requires the server's certificate -to contain a hostname matching what the client is connecting to. This is fundamental to the security -of protocols such as HTTP, but often sees less use with mTLS. Vespa supports it as an added layer of -security. Using certificates containing hostnames has the added benefit that you can run tools such -as `curl` against Vespa HTTPS status pages without having to explicitly disable certificate verification. - -Certificates can contain many entries known as ["Subject Alternate Names" (SANs)](https://en.wikipedia.org/wiki/Subject_Alternative_Name) -that list what DNS names and IP addresses the certificate is issued for. -We'll add a single such DNS SAN entry with the hostname of our node. We'll also use the opportunity -to add certain X.509 extensions to the certificate that specifies exactly what the certificate can -be used for. - -Below, substitute `myhost.example.com` with the hostname of your Vespa node. -```shell -$ cat > cert-exts.cnf << EOF -[host_cert_extensions] -basicConstraints = critical, CA:FALSE -keyUsage = critical, digitalSignature, keyAgreement, keyEncipherment -extendedKeyUsage = serverAuth, clientAuth -subjectKeyIdentifier = hash -authorityKeyIdentifier = keyid,issuer -subjectAltName = @host_sans -[host_sans] -DNS.1 = myhost.example.com -EOF -``` - -We can now use our existing CA key and certificate to sign the host's CSR, additionally -providing the above file of certificate extensions to OpenSSL. - -```shell -$ openssl x509 -req \ - -in host.csr \ - -CA root-ca.pem \ - -CAkey root-ca.key \ - -CAcreateserial \ - -out host.pem \ - -extfile cert-exts.cnf \ - -extensions host_cert_extensions \ - -days 3650 \ - -sha256 -``` - -This creates an X.509 certificate in PEM format for the host, valid for 3650 days from the -time of signing. - -We can inspect the certificate using the `openssl x509` command. Here's some example output -for a certificate using EC keys. Your output will look different since the serial -number, dates and key information etc. will differ. - -```shell -$ openssl x509 -in host.pem -text -noout -Certificate: - Data: - Version: 3 (0x2) - Serial Number: 13516182920561857512 (0xbb9320c1234a93e8) - Signature Algorithm: ecdsa-with-SHA256 - Issuer: C=US, L=California, O=ACME, OU=ACME test root CA - Validity - Not Before: Aug 19 13:09:37 2021 GMT - Not After : Aug 17 13:09:37 2031 GMT - Subject: C=US, L=California, OU=ACME, O=My Vespa App - Subject Public Key Info: - Public Key Algorithm: id-ecPublicKey - Public-Key: (256 bit) - pub: - 04:ed:01:0e:1e:c5:05:17:99:41:74:68:a0:c5:32: - 52:4f:45:d5:04:f8:a0:9c:35:26:ae:66:0c:e5:89: - 34:5c:21:09:b8:a9:ed:81:22:06:bb:d1:1c:9e:13: - 80:0a:9a:9e:0c:a0:78:ac:7c:c4:6f:1c:ec:e6:df: - c1:59:2d:71:8e - ASN1 OID: prime256v1 - NIST CURVE: P-256 - X509v3 extensions: - X509v3 Basic Constraints: critical - CA:FALSE - X509v3 Key Usage: critical - Digital Signature, Key Encipherment, Key Agreement - X509v3 Extended Key Usage: - TLS Web Server Authentication, TLS Web Client Authentication - X509v3 Subject Key Identifier: - 08:EF:C7:B4:95:36:64:EC:2A:2F:9F:5A:C3:EA:F0:98:2C:E5:78:EC - X509v3 Authority Key Identifier: - DirName:/C=US/L=California/O=ACME/OU=ACME test root CA - serial:94:77:40:20:69:50:87:45 - - X509v3 Subject Alternative Name: - DNS:myhost.example.com - Signature Algorithm: ecdsa-with-SHA256 - 30:45:02:21:00:91:58:bb:7f:47:75:60:c3:49:09:b3:d2:54: - ad:d2:47:58:1c:17:c7:5a:5f:f0:f4:9c:67:e9:6a:44:21:8e: - 08:02:20:23:9c:99:42:1b:91:29:26:f7:83:58:d1:09:65:38: - c1:18:e8:0d:55:3a:57:f6:e0:c6:5b:72:57:e4:d9:6a:d8 -``` - -Copy `host.key` and `host.pem` to your Vespa host and point the `"private-key"` and -`"certificates"` TLS config fields to their respective absolute paths. The CSR and -extension config files can be safely discarded. - -{% include warning.html content="Ensure that `host.key` is only readable by the Vespa user on your host(s)" %} - - -### Alternatives to having a unique certificate per individual host - -It's possible to avoid having to create a separate certificate per host in favor of a single certificate -shared between all hosts. - -* Hostname SANs do not have to be added (or match) if hostname validation is explicitly disabled in the - TLS config file. **Caveat:** this makes it impossible for clients to verify that they're talking to the host they expected. -* Many SAN entries can be added to the extension file, one per host. **Caveat:** new certificates must be generated - if new hosts are added to the Vespa application that aren't already in the SAN list. -* If all hosts share a common pattern (e.g. `foo.vespa.example.com` and `bar.vespa.example.com`) it's possible to use - a wildcard DNS SAN entry (`*.vespa.example.com`) instead of listing all hosts. - -However, for production deployments we recommend using a distinct certificate per host to help mitigate -the impact of a host being compromised. diff --git a/en/security/mtls.mdx b/en/security/mtls.mdx new file mode 100644 index 0000000000..7e29bbf4a1 --- /dev/null +++ b/en/security/mtls.mdx @@ -0,0 +1,456 @@ +--- +title: "Securing Vespa with mutually authenticated TLS (mTLS)" +sidebarTitle: "mTLS" +--- + +<Note> +**Note:** + +This document is relevant for **self-hosted Vespa** only. +</Note> + +[Transport Layer Security (TLS)](https://datatracker.ietf.org/doc/html/rfc5246) is a protocol that uses cryptography to enable secure, tamper-proof communication over the network. This document describes the TLS functionality in Vespa and how to configure it. When properly configured, TLS ensures only trusted Vespa services can talk to each other. See accompanying [reference](/en/reference/security/mtls) for details on configuration syntax. + +By default, all communication between self-hosted Vespa nodes is *unauthenticated* and *unencrypted*. This means anyone with network access can read and write data and potentially execute commands on the system. *Enabling TLS is therefore a fundamental part of a secure Vespa installation*. + +You should configure TLS even if you already have a firewall set up to prevent outside connections to your system. TLS helps protect against the case even where an attacker has managed to get a foothold inside your private network. Vespa will in some future version require TLS for all internal communication. To ensure you are ready for this, secure your systems as soon as possible. + +Vespa offers two separate planes of TLS connectivity: + +- **HTTP(S) application containers.** This is the edge of your cluster where search queries and feed requests are handled. Authentication and authorization for this plane is handled separately from Vespa-internal traffic; this is covered in [Configuring Http Servers and Filters](/en/applications/http-servers-and-filters#tls). See also [Securing the application container](/en/security/securing-your-vespa-installation#securing-the-application-container). +- **Vespa-internal communication.** This is all communication between processes running on the nodes in your cluster. This includes clients connecting directly to the backends instead of going through the application container APIs. Only mutually authenticated TLS (mTLS) may be configured for this traffic. + +This document only covers **Vespa-internal communication**. + +Enabling TLS in Vespa means that all internal endpoints are mTLS protected, even HTTP servers for status pages and metrics. Be especially aware of this if you have custom solutions in place for collecting and aggregating low level metrics or status pages from the Vespa backends. Though the terms *TLS* and *mTLS* may be used interchangeably in this document, *TLS* implies *mTLS* for all Vespa-internal traffic. + +Refer to the [multinode-HA](https://github.com/vespa-engine/sample-apps/tree/master/examples/operations/multinode-HA) example application for a working example. + +## Prerequisites + +This section assumes you have some experience with generating and using certificates and private keys. For an introduction, see [Appendix A: setting up with a self-signed Certificate Authority](#appendix-a-setting-up-with-a-self-signed-certificate-authority) which gives step-by-step instructions on setting up certificates that can be used internally for a single Vespa application. + +In order to enable TLS, some extra files must be present on every node in your Vespa application: + +- A file containing the X.509 certificates of all trusted Certificate Authorities in PEM format. +- A file containing the X.509 certificate chain that will be used by Vespa processes on the node. This is in standard PEM format. The host's own certificate should be the first certificate listed in the file, followed by intermediate certificates (if any), separated by newlines. +- A file containing the private key corresponding to the certificate in the above chain, in PKCS#8 PEM format. Note that Vespa does not currently support encrypted private key files. +- A JSON configuration file telling Vespa which certificate/key files to use, and to provide further options for authorization. See [Writing a TLS configuration file](#writing-a-tls-configuration-file) for how to write these. + +How certificate and key material is distributed to the nodes is outside the scope of this article. See [dedicated section](#automatic-reloading-of-crypto-material) for Vespa's support of automatic and live reloading of TLS credentials. + +<Warning> +**Warning:** + +You should never include public Certificate Authorities as part of the list of Certificate Authorities you trust. Only use CAs that you (or your organization) directly control or trust. This is to minimize the risk of malicious actors exploiting bugs or authentication flaws in public CAs to pose as your organization and acquire certificates that would allow them to directly access your system. +</Warning> + +<Warning> +**Warning:** + +Private keys must be kept secret and protected against unauthorized access. Make sure only the user running the Vespa processes can read the private key file. Key and certificate files should only be writable by administrator users. +</Warning> + +## Configuring Vespa TLS + +On any node running Vespa software, TLS is controlled via a single environment variable. This variable contains an absolute path pointing to a JSON configuration file: + +```bash +VESPA_TLS_CONFIG_FILE=/absolute/path/to/my-tls-config.json +``` + +This environment variable must be set to a valid file path before any Vespa services are started on the node. All nodes in your Vespa application must have a TLS config file pointing to the certificates that are trusted by the other nodes. + +See [Vespa environment variables](/en/operations/self-managed/files-processes-and-ports#environment-variables) for information on configuring environment variables for Vespa. + +Setting `VESPA_TLS_CONFIG_FILE` automatically enables TLS for all Vespa processes on the node. Vespa command-line tools will automatically pick up the required configuration and work transparently. + +<Warning> +**Important:** + +If this variable is not set, Vespa starts up in insecure mode without any TLS! +</Warning> + +### Writing a TLS configuration file + +The simplest possible configuration file only needs to know the certificates to trust and the certificate/key pair that identifies the node itself. Example: + +```json +{ + "files": { + "ca-certificates": "/absolute/path/to/ca-certs.pem", + "certificates": "/absolute/path/to/host-certs.pem", + "private-key": "/absolute/path/to/private-key.pem" + } +} +``` + +Set the environment variable, for example by appending to [conf/vespa/default-env.txt](/en/operations/self-managed/files-processes-and-ports#environment-variables): + +```bash +override VESPA_TLS_CONFIG_FILE /absolute/path/to/my-tls-config.json +``` + +All file paths must be absolute. If a Vespa process cannot load one or more files, it will fail to start up. + +### Configuring TLS peer authorization rules + +For many simpler deployments, a dedicated self-signed Certificate Authority will be used for the Vespa cluster alone. In that case simply being in possession of a valid certificate is enough to be authorized to access the cluster nodes; no one except the Vespa nodes is expected to have such a certificate. More complex deployments may instead use a shared CA, e.g. a corporate CA issuing certificates to nodes across many services and departments. In that case simply having a valid certificate is not sufficient to be used as an authorization mechanism. + +You can constrain which certificates may access the internal Vespa service by using *authorization rules*. These are consulted as part of every TLS handshake and must pass before any connection can be established. + +Authorization rules are specified as part of the JSON configuration file using the top-level [`authorized-peers`](/en/reference/security/mtls#top-level-elements) member. + +#### Example + +Let's assume our Vespa cluster consists of many nodes, each with their own certificate signed by a shared Certificate Authority. Each certificate contains a Subject Alternate Name (SAN) DNS name entry of the form `<unique-node-id>.mycluster.vespa.example.com`, where *unique-node-id* is unique per cluster node. These nodes will be running the actual Vespa services and must all be able to talk to each other. + +Let's also assume there is a monitoring service that requires low-level access to the services. Certificates presented by nodes belonging to this service will always have a Common Name (CN) value of `vespa-monitoring.example.com` and a DNS SAN entry of the form `<instance>.<region>.monitor.example.com`. Any monitoring instance in any us-east region must be able to access our cluster, but no others. + +Our TLS config file implementing these rules may look like this: + +```json +{ + "files": { + "ca-certificates": "/absolute/path/to/ca-certs.pem", + "certificates": "/absolute/path/to/host-certs.pem", + "private-key": "/absolute/path/to/private-key.pem" + }, + "authorized-peers": [ + { + "required-credentials": [ + { "field": "CN", "must-match": "vespa-monitoring.example.com" }, + { "field": "SAN_DNS", "must-match": "*.us-east-*.monitor.example.com" } + ], + "description": "Backend monitoring service access" + }, + { + "required-credentials": [ + { "field": "SAN_DNS", "must-match": "*.mycluster.vespa.example.com" } + ], + "description": "Cluster-internal node P2P access" + } + ] +} +``` + +See the [reference documentation](/en/reference/security/mtls#peer-authorization-rules) for details on syntax and semantics. + +### Automatic reloading of crypto material + +Vespa performs periodic reloading of the specified TLS configuration file. Currently, this happens every 60 minutes. This reloading happens live and does not impact service availability. Both certificates, the private key and authorization rules are reloaded. Vespa currently does not watch the configuration file for changes, so altering the config file or any of its dependencies does not trigger a reload by itself. + +If live reloading fails, the old configuration continues to be used and a warning is emitted to the local Vespa log. + +Vespa does not currently lock files before reading them. To avoid race conditions where files are reloaded by Vespa while they are being written, consider splitting file refreshing into multiple phases: + +<Steps> +<Step> +Instead of overwriting existing key/cert files, write *new* files with different file names. +</Step> +<Step> +Create a temporary TLS config JSON file pointing to these files. +</Step> +<Step> +Atomically rename the new TLS config file to the name specified by `VESPA_TLS_CONFIG_FILE`. +</Step> +<Step> +Garbage-collect the old files at a later point (for example at the next refresh time). +</Step> +</Steps> + +## Setting up TLS for a new Vespa application or upgrading with downtime + +With no Vespa services running on any nodes, ensure the `VESPA_TLS_CONFIG_FILE` environment variable is set to a valid configuration file path on every node, and [is visible to any Vespa start scripts](/en/operations/self-managed/files-processes-and-ports#environment-variables). Start Vespa services as you normally would. Check cluster health with [vespa-get-cluster-state](/en/reference/operations/self-managed/tools#vespa-get-cluster-state) and check [vespa-logfmt](/en/reference/operations/self-managed/tools#vespa-logfmt) for any TLS-related error messages that indicate a misconfiguration (such as certificate rejections etc.)—see the Troubleshooting section. The cluster should quickly converge to an available state. + +This is the simplest and fastest way to enable TLS, and is highly recommend if downtime is acceptable. + +## Upgrading an existing non-TLS Vespa application to TLS without downtime + +If you already have a Vespa application serving live traffic that you don't want to take down completely in order to enable TLS, it's possible to perform a gradual, rolling upgrade. Doing this requires insecure and TLS connections to be used alongside each other for some time, moving more and more nodes onto TLS. Finally, once all nodes are speaking only TLS, the support for insecure connections must be removed entirely. + +To achieve this, Vespa supports a feature called *insecure mixed mode*. Enabling mixed mode lets all servers handle both TLS and insecure traffic at the same time. + +Mixed mode is controlled via the value set in environment variable `VESPA_TLS_INSECURE_MIXED_MODE`. + +TLS rollout happens in 3 phases: + +**Phase 1:** clients do not use TLS, servers accept both TLS and plaintext clients + +<Steps> +<Step> +Set `VESPA_TLS_INSECURE_MIXED_MODE=plaintext_client_mixed_server`. +</Step> +<Step> +Set `VESPA_TLS_CONFIG` file as documented in [Configuring Vespa TLS](#configuring-vespa-tls). +</Step> +<Step> +Rolling restart of all Vespa services to make mixed mode take effect. +</Step> +</Steps> + +**Phase 2:** clients use TLS, servers accept both TLS and plaintext clients + +<Steps> +<Step> +Set `VESPA_TLS_INSECURE_MIXED_MODE=tls_client_mixed_server`. +</Step> +<Step> +Rolling restart of all Vespa services to make mixed mode take effect. +</Step> +</Steps> + +**Phase 3:** all clients and servers use TLS only + +<Steps> +<Step> +Remove the `VESPA_TLS_INSECURE_MIXED_MODE` environment variable. +</Step> +<Step> +Rolling restart of all Vespa services to make enforced TLS take effect. +</Step> +</Steps> + +<Warning> +**Warning:** + +The insecure mixed mode environment variable MUST be removed from all nodes (and all services subsequently restarted) before a cluster can be considered secure. Even a single service left with insecure mixed mode enabled could be used by a determined attacker as a jumpgate into other (believed secure) services. +</Warning> + +## Verify configuration of TLS + +Successful configuration should be verified at runtime once TLS is enabled on all nodes. The [openssl s_client](https://www.openssl.org/docs/man1.1.1/man1/openssl-s_client.html) tool is suitable for this. Connect to a Vespa service, e.g a configserver on port 19071 or a container on port 8080, and verify that `openssl s_client` successfully completes the TLS handshake. + +```bash +$ openssl s_client -connect <hostname>:<port> \ + -CAfile /absolute/path/to/ca-certs.pem \ + -key /absolute/path/to/private-key.pem \ + -cert /absolute/path/to/host-cert.pem +``` + +Further, you should verify that servers require clients to authenticate by omitting `-key`/`-cert` from above command. The `s_client` tool should print an error during handshake and exit immediately. + +```bash +$ openssl s_client -connect <hostname>:<port> \ + -CAfile /absolute/path/to/ca-certs.pem +``` + +## FAQ + +<AccordionGroup> +<Accordion title="Should TLS be used even if I have a latency-sensitive real-time search application?"> +Yes. The Vespa cloud team has run many such applications in production for a long time and the overhead imposed by TLS is negligible. Significant effort have been spent tuning Vespa's TLS integrations to keep overhead to a minimum. +</Accordion> +<Accordion title="How much overhead does TLS impose in practice?"> +With modern CPUs, expect somewhere around 1-2% extra CPU usage for symmetric encryption (i.e. active connections). Connection handshakes have an expected extra latency of 2-4 ms of CPU time (network latency not included) due to more expensive cryptographic operations. Vespa performs handshake operations in separate threads to avoid stalling other network traffic. Vespa also uses long-lived connections internally to reduce the number of handshakes. +</Accordion> +</AccordionGroup> + +## Troubleshooting + +### Certificate validation fails due to mismatching hostnames + +Vespa enables the [HTTPS endpoint identification algorithm](https://datatracker.ietf.org/doc/html/rfc2818#section-3) by default. This extra verification can only be used if all certificates have their respective host's IP addresses and hostnames in the Subject / Subject Alternative Names extensions. [Disable hostname validation](/en/reference/security/mtls#top-level-elements) if this is not the case. + +## Appendix A: setting up with a self-signed Certificate Authority + +Our goal is to create cryptographic keys and certificates that can be used by Vespa for secure mTLS communication within a single Vespa installation. + +This requires the following steps, which we'll go through below: + +<Steps> +<Step> +[Creating a root Certificate Authority](/en/security/mtls#creating-a-root-certificate-authority-ca). This is only done once, regardless of how many Vespa hosts you want to secure. +</Step> +<Step> +[Creating a private key and Certificate Signing Request (CSR) for each Vespa host](/en/security/mtls#creating-a-private-key-and-certificate-for-a-vespa-host). +</Step> +<Step> +[Signing the CSR using the CA, creating a certificate for each Vespa host](#sign-host-certificate). +</Step> +</Steps> + +We'll be using the [OpenSSL command-line tool](https://www.openssl.org/docs/man1.1.1/man1/) to generate all our crypto keys and certificates. + +<Note> +**Note:** + +If you are setting up Vespa in an organization that already has procedures for provisioning keys and certificates, you should first reach out to the team responsible for this to make sure you're following best practices. +</Note> + +### Creating a root Certificate Authority (CA) + + +When a server (or client) presents a certificate as part of proving its identity to us, we must have a way to determine if this information is trustworthy. We do this by verifying if the certificate is *cryptographically signed* by a [Certificate Authority (CA)](https://en.wikipedia.org/wiki/Certificate_authority) that we already know we can trust. It is possible that the certificate is in fact signed by a CA that we don't directly trust, but that in turn is signed by a CA that we *do* trust. These are known as *intermediate* Certificate Authorities and are part of what's known as the *certificate chain*. There may be more than one intermediate CA in a chain. In our simple setup we will not be using any intermediate CAs. + +At the top of the chain sits a *root* Certificate Authority. Since we trust the root CA, we also implicitly trust any intermediate CA it has signed and in turn any leaf certificates such an intermediate CA has signed. + +A root Certificate Authority is special in that it has no CA above it to sign in. It is *self-signed*. + +To create our own root CA for our Vespa installation we'll first create its [*private key*](https://en.wikipedia.org/wiki/Public-key_cryptography). + +We have two choices of what kind of key to create; either based on [RSA](https://en.wikipedia.org/wiki/RSA_\(cryptosystem\)) or [Elliptic Curve (EC)](https://en.wikipedia.org/wiki/Elliptic-curve_cryptography) cryptography. EC keys are faster to process than RSA-based keys and take up less space, but older OS versions or cryptographic libraries may not support these. In the latter case, RSA keys offer the highest level of backwards compatibility. + +(Recommended) either create an Elliptic Curve private key: + +```bash +$ openssl ecparam -name prime256v1 -genkey -noout -out root-ca.key +``` + +**OR:** create an RSA private key: + +```bash +$ openssl genrsa -out root-ca.key 2048 +``` + +The root CA private key is stored in `root-ca.key`. This key is used to sign certificates and the file MUST therefore be kept secret! If it is compromised, an attacker can create any number of valid certificates that impersonate your Vespa hosts. + +We'll now create our CA X.509 certificate, self-signed with the private key. Substitute the information given in `-subj` with whatever is appropriate for you; it's not really important for our simple usage. + +```bash +$ openssl req -new -x509 -nodes \ + -key root-ca.key \ + -out root-ca.pem \ + -subj '/C=US/L=California/O=ACME/OU=ACME test root CA' \ + -sha256 \ + -days 3650 +``` + +Copy the resulting `root-ca.pem` file to your Vespa node(s) and point the `"ca-certificates"` field in the TLS config file to its absolute file path on the node. + +With both the CA key and certificate, we have what we need to start signing certificates for the hosts Vespa will be running on. + +### Creating a private key and certificate for a Vespa host + +<Note> +***Note:*** + +*This section can be repeated for each Vespa host in your application. See [Alternatives to having a unique certificate per individual host](/en/security/mtls#alternatives-to-having-a-unique-certificate-per-individual-host) for (possibly less secure) options that do not require doing this step per host.* +</Note> + +Just like our CA our host needs its own private cryptographic key. + +If we're using Elliptic Curve keys: + +```bash +$ openssl ecparam -name prime256v1 -genkey -noout -out host.key +``` + +**OR:** if we're using RSA keys: + +```bash +$ openssl genrsa -out host.key 2048 +``` + +As part of creating the certificate we'll first create a [Certificate Signing Request (CSR)](https://en.wikipedia.org/wiki/Certificate_signing_request). Again, you can substitute the information in `-subj` with something more appropriate for you. + +```bash +$ openssl req -new \ + -key host.key -out host.csr \ + -subj '/C=US/L=California/OU=ACME/O=My Vespa App' \ + -sha256 +``` + +#### Sign host certificate + +By default, Vespa runs with TLS hostname validation enabled, which requires the server's certificate to contain a hostname matching what the client is connecting to. This is fundamental to the security of protocols such as HTTP, but often sees less use with mTLS. Vespa supports it as an added layer of security. Using certificates containing hostnames has the added benefit that you can run tools such as `curl` against Vespa HTTPS status pages without having to explicitly disable certificate verification. + +Certificates can contain many entries known as ["Subject Alternate Names" (SANs)](https://en.wikipedia.org/wiki/Subject_Alternative_Name) that list what DNS names and IP addresses the certificate is issued for. We'll add a single such DNS SAN entry with the hostname of our node. We'll also use the opportunity to add certain X.509 extensions to the certificate that specifies exactly what the certificate can be used for. + +Below, substitute `myhost.example.com` with the hostname of your Vespa node. + +```bash +$ cat > cert-exts.cnf << EOF +[host_cert_extensions] +basicConstraints = critical, CA:FALSE +keyUsage = critical, digitalSignature, keyAgreement, keyEncipherment +extendedKeyUsage = serverAuth, clientAuth +subjectKeyIdentifier = hash +authorityKeyIdentifier = keyid,issuer +subjectAltName = @host_sans +[host_sans] +DNS.1 = myhost.example.com +EOF +``` + +We can now use our existing CA key and certificate to sign the host's CSR, additionally providing the above file of certificate extensions to OpenSSL. + +```bash +$ openssl x509 -req \ + -in host.csr \ + -CA root-ca.pem \ + -CAkey root-ca.key \ + -CAcreateserial \ + -out host.pem \ + -extfile cert-exts.cnf \ + -extensions host_cert_extensions \ + -days 3650 \ + -sha256 +``` + +This creates an X.509 certificate in PEM format for the host, valid for 3650 days from the time of signing. + +We can inspect the certificate using the `openssl x509` command. Here's some example output for a certificate using EC keys. Your output will look different since the serial number, dates and key information etc. will differ. + +```bash expandable +$ openssl x509 -in host.pem -text -noout +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 13516182920561857512 (0xbb9320c1234a93e8) + Signature Algorithm: ecdsa-with-SHA256 + Issuer: C=US, L=California, O=ACME, OU=ACME test root CA + Validity + Not Before: Aug 19 13:09:37 2021 GMT + Not After : Aug 17 13:09:37 2031 GMT + Subject: C=US, L=California, OU=ACME, O=My Vespa App + Subject Public Key Info: + Public Key Algorithm: id-ecPublicKey + Public-Key: (256 bit) + pub: + 04:ed:01:0e:1e:c5:05:17:99:41:74:68:a0:c5:32: + 52:4f:45:d5:04:f8:a0:9c:35:26:ae:66:0c:e5:89: + 34:5c:21:09:b8:a9:ed:81:22:06:bb:d1:1c:9e:13: + 80:0a:9a:9e:0c:a0:78:ac:7c:c4:6f:1c:ec:e6:df: + c1:59:2d:71:8e + ASN1 OID: prime256v1 + NIST CURVE: P-256 + X509v3 extensions: + X509v3 Basic Constraints: critical + CA:FALSE + X509v3 Key Usage: critical + Digital Signature, Key Encipherment, Key Agreement + X509v3 Extended Key Usage: + TLS Web Server Authentication, TLS Web Client Authentication + X509v3 Subject Key Identifier: + 08:EF:C7:B4:95:36:64:EC:2A:2F:9F:5A:C3:EA:F0:98:2C:E5:78:EC + X509v3 Authority Key Identifier: + DirName:/C=US/L=California/O=ACME/OU=ACME test root CA + serial:94:77:40:20:69:50:87:45 + + X509v3 Subject Alternative Name: + DNS:myhost.example.com + Signature Algorithm: ecdsa-with-SHA256 + 30:45:02:21:00:91:58:bb:7f:47:75:60:c3:49:09:b3:d2:54: + ad:d2:47:58:1c:17:c7:5a:5f:f0:f4:9c:67:e9:6a:44:21:8e: + 08:02:20:23:9c:99:42:1b:91:29:26:f7:83:58:d1:09:65:38: + c1:18:e8:0d:55:3a:57:f6:e0:c6:5b:72:57:e4:d9:6a:d8 +``` + +Copy `host.key` and `host.pem` to your Vespa host and point the `"private-key"` and `"certificates"` TLS config fields to their respective absolute paths. The CSR and extension config files can be safely discarded. + +<Warning> +**Warning:** + +Ensure that `host.key` is only readable by the Vespa user on your host(s) +</Warning> + +### Alternatives to having a unique certificate per individual host + +It's possible to avoid having to create a separate certificate per host in favor of a single certificate shared between all hosts. + +- Hostname SANs do not have to be added (or match) if hostname validation is explicitly disabled in the TLS config file. **Caveat:** this makes it impossible for clients to verify that they're talking to the host they expected. +- Many SAN entries can be added to the extension file, one per host. **Caveat:** new certificates must be generated if new hosts are added to the Vespa application that aren't already in the SAN list. +- If all hosts share a common pattern (e.g. `foo.vespa.example.com` and `bar.vespa.example.com`) it's possible to use a wildcard DNS SAN entry (`*.vespa.example.com`) instead of listing all hosts. + +However, for production deployments we recommend using a distinct certificate per host to help mitigate the impact of a host being compromised. diff --git a/en/security/secret-store.md b/en/security/secret-store.md deleted file mode 100644 index 15cda1d15a..0000000000 --- a/en/security/secret-store.md +++ /dev/null @@ -1,187 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: Secret Store -applies_to: cloud -redirect_from: - - /en/cloud/security/secret-store ---- - -Vespa Cloud supports secure storage and management of secrets for use in your application. -A secret is a text-based value such as an API key, a token -or other private configuration value required by your application. - -By organizing secrets into vaults, setting application-specific access controls, -and integrating secrets cleanly into your application code, Vespa Cloud ensures that sensitive data -like API keys and tokens are kept safe and are easily updatable. - -This guide takes you through secret management for your tenant and how to use them in your application. - -Use the [Retrieval Augmented Generation (RAG) in Vespa](https://github.com/vespa-engine/sample-apps/tree/master/retrieval-augmented-generation#deploying-to-the-vespa-cloud-using-gpu) -sample application for a practical example getting started using the Secret Store. -This example uses the Secret Store to store an OpenAI API key. - - - -## Secret management -In the Vespa Cloud console, the "Account" section of your tenant contains a -"Secret store" tab. This is where you configure all secrets for your tenant. - - -### Vaults -Secrets are organized into vaults, where each vault can contain a number of -secrets. The vault also contains rules for which applications can use the -secrets in the vault. You can have any number of vaults. - -To create a new vault, click the "+ New vault" button. The vault name must -match the rule `[.a-zA-Z0-9_-]` meaning only alphanumeric characters and `.`, -`_`, and `-` are allowed. Spaces are not allowed. - -<img alt="Secret store overview" src="/assets/img/secret-store.png" /> - -After creation, you can delete the entire vault by clicking the red trash bin -button on the top right. - - -### Access scopes -Each vault has an "Access scopes" section that lists the access rules defined -for the vault. Every rule has a scope - either *application* or -*infrastructure*. Click "Edit access" to manage them; the editor has an -"Application scope" tab and an "Infrastructure scope" tab. - -#### Application scope -Application scope grants your applications read access to the secrets in the -vault, for use within your application code. In the "Application scope" tab, -grant each application access in the -[prod](../operations/environments#prod) (including test and staging) and/or -[dev](../operations/environments#dev) environment. - -Note that the application must have been created before you can grant it access. -Use the steps at [Retrieval Augmented Generation (RAG) in Vespa](https://github.com/vespa-engine/sample-apps/tree/master/retrieval-augmented-generation#deploying-to-the-vespa-cloud-using-gpu) -to create an application and grant access. - -#### Infrastructure scope -Infrastructure scope grants Vespa Cloud Enclave hosts read access to the secrets -in the vault, so the platform can use them on your behalf rather than your -application code. Access is granted to your tenant's Enclave cloud account. This -is required by features such as -[telemetry export](../operations/telemetry-export.html), where the collector -running on your hosts reads the authentication token for your telemetry backend. - -The "Infrastructure scope" tab is shown only for Vespa Cloud Enclave tenants. In -it, the cloud accounts that already have access are listed. To grant access, -select a cloud account from the dropdown. To revoke access, click "Remove" next -to the cloud account. - - -### Secrets -To add a new secret, click the "+ New secret" button. The same naming rules -apply for secrets. You can give any value to the secret. Note that once this is -saved the secret will never be visible again. You can update the secret to new -values, but never retrieve the actual value. Maximum length for a secret is 64K -characters. - -Each tenant has a limit of 15 secrets. - -<img alt="Creating new secret" src="/assets/img/secret-store-secret.png" /> - -After the secret has been created, you can update the secret to a new value or -delete it. - -Note that when a secret is updated, applications using it will start using this -new value within 60 seconds. - -Also note that your application will not deploy successfully if the application -requests a secret that for some reason is not available, by either not being -defined or does not have access to it. - - - -## Example: Using an OpenAI API key for RAG -Set up a RAG search chain that uses an OpenAI API key as secret: - -```xml -<services version="1.0" xmlns:deploy="vespa" xmlns:preprocess="properties"> - <container id="default" version="1.0"> - - <!-- configure the OpenAI API key secret --> - <secrets> - <apiKey vault="my-vault" name="openai-api-key" /> - </secrets> - - <!-- configure the OpenAI client to use the secret --> - <component id="openai" class="ai.vespa.llm.clients.OpenAI"> - <config name="ai.vespa.llm.clients.llm-client"> - <apiKeySecretRef>apiKey</apiKeySecretRef> - </config> - </component> - - <!-- configure a search chain to use the OpenAI client --> - <search> - <chain id="rag" inherits="vespa"> - <searcher id="ai.vespa.search.llm.RAGSearcher"> - <config name="ai.vespa.search.llm.llm-searcher"> - <providerId>openai</providerId> - </config> - </searcher> - </chain> - </search> - - </container> -</services> -``` -Try [Retrieval Augmented Generation (RAG) in Vespa](https://github.com/vespa-engine/sample-apps/tree/master/retrieval-augmented-generation#deploying-to-the-vespa-cloud-using-gpu) -for a practical example. - - - -## Using secrets -To use the secret in an application, add `secrets` to `services.xml`: - -```xml -<services version="1.0" xmlns:deploy="vespa" xmlns:preprocess="properties"> - <container id="default" version="1.0"> - - <secrets> - <myApiKey vault="my-vault" name="my-api-key" /> - </secrets> - - </container> -</services> -``` - -In this example, we refer to a secret named `my-api-key` in the vault -`my-vault` with the name `myApiKey` in the application. - -To access this secret in a custom component, inject the `Secrets` as a -constructor parameter in the component, like a Searcher: - -```java -import ai.vespa.secret.Secret; -import ai.vespa.secret.Secrets; -... - -public class MySearcher extends Searcher { - - private final Secret apiKeySecret; - - public MySearcher(Secrets secrets) { - apiKeySecret = secrets.get("myApiKey"); - } - - @Override - public Result search(Query query, Execution execution) { - String apiKey = apiKeySecret.current(); - // ... do something with the current value of secret ... - return execution.search(query); - } - -} -``` - -Typically, store the `Secret` in your class, and when you want to use the -secret value itself, you call `Secret.current();`. This ensures that you will -use the current secret value if it is updated. Note that it can take up to 60 -seconds for the current secret value to be updated for your container code. - -Ensure that you do not store the `current` value itself - -then the secret value will not be updated when the configuration is changed. diff --git a/en/security/secret-store.mdx b/en/security/secret-store.mdx new file mode 100644 index 0000000000..008911276e --- /dev/null +++ b/en/security/secret-store.mdx @@ -0,0 +1,129 @@ +--- +title: "Secret Store" +--- + +Vespa Cloud supports secure storage and management of secrets for use in your application. A secret is a text-based value such as an API key, a token or other private configuration value required by your application. + +By organizing secrets into vaults, setting application-specific access controls, and integrating secrets cleanly into your application code, Vespa Cloud ensures that sensitive data like API keys and tokens are kept safe and are easily updatable. + +This guide takes you through secret management for your tenant and how to use them in your application. + +Use the [Retrieval Augmented Generation (RAG) in Vespa](https://github.com/vespa-engine/sample-apps/tree/master/retrieval-augmented-generation#deploying-to-the-vespa-cloud-using-gpu) sample application for a practical example getting started using the Secret Store. This example uses the Secret Store to store an OpenAI API key. + +## Secret management + +In the Vespa Cloud console, the "Account" section of your tenant contains a "Secret store" tab. This is where you configure all secrets for your tenant. + +### Vaults + +Secrets are organized into vaults, where each vault can contain a number of secrets. The vault also contains rules for which applications can use the secrets in the vault. You can have any number of vaults. + +To create a new vault, click the "+ New vault" button. The vault name must match the rule `[.a-zA-Z0-9_-]` meaning only alphanumeric characters and `.`, `_`, and `-` are allowed. Spaces are not allowed. + +<Frame>![Secret store overview](/assets/img/secret-store.png)</Frame> + +After creation, you can delete the entire vault by clicking the red trash bin button on the top right. + +### Access control + +Each vault has an "Access control" section which determines which application has access to the secrets in the vault. For each application, you can set up which environment - [dev](/en/operations/environments#dev) or [prod](/en/operations/environments#prod) (including test and staging) - the application should have access within. + +Note that the application must have been created before you can set access control to it. Use the steps at [Retrieval Augmented Generation (RAG) in Vespa](https://github.com/vespa-engine/sample-apps/tree/master/retrieval-augmented-generation#deploying-to-the-vespa-cloud-using-gpu) to create an application and grant access. + +### Secrets + +To add a new secret, click the "+ New secret" button. The same naming rules apply for secrets. You can give any value to the secret. Note that once this is saved the secret will never be visible again. You can update the secret to a new value, but never retrieve the actual value. Maximum length for a secret is 64K characters. + +Each tenant has a limit of 15 secrets. + +<Frame>![Creating new secret](/assets/img/secret-store-secret.png)</Frame> + +After the secret has been created, you can update the secret to a new value or delete it. + +Note that when a secret is updated, applications using it will start using this new value within 60 seconds. + +Also note that your application will not deploy successfully if the application requests a secret that for some reason is not available, by either not being defined or does not have access to it. + +## Example: Using an OpenAI API key for RAG + +Set up a RAG search chain that uses an OpenAI API key as secret: + +```xml expandable +<services version="1.0" xmlns:deploy="vespa" xmlns:preprocess="properties"> + <container id="default" version="1.0"> + + {/* configure the OpenAI API key secret */} + <secrets> + <apiKey vault="my-vault" name="openai-api-key" /> + </secrets> + + {/* configure the OpenAI client to use the secret */} + <component id="openai" class="ai.vespa.llm.clients.OpenAI"> + <config name="ai.vespa.llm.clients.llm-client"> + <apiKeySecretRef>apiKey</apiKeySecretRef> + </config> + </component> + + {/* configure a search chain to use the OpenAI client */} + <search> + <chain id="rag" inherits="vespa"> + <searcher id="ai.vespa.search.llm.RAGSearcher"> + <config name="ai.vespa.search.llm.llm-searcher"> + <providerId>openai</providerId> + </config> + </searcher> + </chain> + </search> + + </container> +</services> +``` + +Try [Retrieval Augmented Generation (RAG) in Vespa](https://github.com/vespa-engine/sample-apps/tree/master/retrieval-augmented-generation#deploying-to-the-vespa-cloud-using-gpu) for a practical example. + +## Using secrets + +To use the secret in an application, add `secrets` to `services.xml`: + +```xml +<services version="1.0" xmlns:deploy="vespa" xmlns:preprocess="properties"> + <container id="default" version="1.0"> + + <secrets> + <myApiKey vault="my-vault" name="my-api-key" /> + </secrets> + + </container> +</services> +``` + +In this example, we refer to a secret named `my-api-key` in the vault `my-vault` with the name `myApiKey` in the application. + +To access this secret in a custom component, inject the `Secrets` as a constructor parameter in the component, like a Searcher: + +```java expandable +import ai.vespa.secret.Secret; +import ai.vespa.secret.Secrets; +... + +public class MySearcher extends Searcher { + + private final Secret apiKeySecret; + + public MySearcher(Secrets secrets) { + apiKeySecret = secrets.get("myApiKey"); + } + + @Override + public Result search(Query query, Execution execution) { + String apiKey = apiKeySecret.current(); + // ... do something with the current value of secret ... + return execution.search(query); + } + +} +``` + +Typically, store the `Secret` in your class, and when you want to use the secret value itself, you call `Secret.current();`. This ensures that you will use the current secret value if it is updated. Note that it can take up to 60 seconds for the current secret value to be updated for your container code. + +Ensure that you do not store the `current` value itself - then the secret value will not be updated when the configuration is changed. diff --git a/en/security/securing-your-vespa-installation.md b/en/security/securing-your-vespa-installation.md deleted file mode 100644 index 15074924e4..0000000000 --- a/en/security/securing-your-vespa-installation.md +++ /dev/null @@ -1,94 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Securing a Vespa Installation" -applies_to: self-managed -redirect_from: -- /en/securing-your-vespa-installation.html -- /en/operations-selfhosted/securing-your-vespa-installation ---- -{% include note.html content="This document is only relevant for **self-hosting Vespa** as Vespa Cloud does this for you." %} - -It is critical to understand the security requirements and limitations -of any networked system. Vespa is no exception. This document gives the most -important information related to security at the network and physical host levels. - -To keep the self-hosted Vespa installation safe, follow the guidelines outlined below: - -1. Isolate the Vespa hosts -2. Secure the application container with access control filters and TLS -3. Lockdown directory permissions -4. Securing Vespa with mutually authenticated TLS (mTLS) - - - -## Isolating the Vespa hosts - -**When running self-hosted Vespa instances, hosts running Vespa MUST NOT be directly exposed -to the public internet or to untrusted networks. Failure to ensure this may lead to data -exfiltration/infiltration or host compromise.** - -Vespa's internal protocols are not authenticated by default and are therefore not safe -in the face of untrusted network actors. -When running Vespa in your own organization, or on the public cloud in particular, this -is something you must take into account. - -Connections to _any_ hosts running Vespa services should _only_ be allowed from -a controlled set of trusted hosts. All Vespa hosts must be able to connect -to, and receive connections from, all other Vespa hosts that are part of the -same installation. For added security, consider limiting Vespa hosts to only -be able to talk to other Vespa hosts. If you are contacting external services -as part of [federation](../querying/federation.html) in the application container, your -container hosts must be able to connect to these services. - -This may be implemented by e.g. iptables, AWS Security Groups or similar technologies. - -The entry point into your Vespa installation is port 8080 on hosts running the -application container. This port is used for feed, document retrieval and search -queries. It should only be exposed to an untrusted network if you have properly -[secured the application container](#securing-the-application-container). It -should never be exposed directly to external traffic. All traffic to the containers -should be sent by your frontends or backends. - -Internode communication inside a Vespa installation is not encrypted by default -and will need to be configured explicitly as documented in -[Securing Vespa with mutually authenticated TLS](mtls). - - - -## Securing the application container - -By default, the container allows unauthenticated writes to, and reads from, the Vespa installation. -For a production deployment, this must be locked down. - -Connections to the HTTP containers may be [protected with TLS](../applications/http-servers-and-filters.html#tls). -Mutual TLS is supported and should be configured. - -Access to the container API endpoints can be controlled using -[request filters](../applications/http-servers-and-filters.html#set-up-filter-chains). -These filters can implement the required authentication and authorization logic -for your specific use case. - -If you do not set up TLS with restrictive filter logic, you should restrict the -container port in the same way as you would the rest of the Vespa hosts. - - -## Locking down directory permissions - -All Vespa processes run under the Linux user given by `$VESPA_USER` and store their -data under `$VESPA_HOME`. You should ensure the files and directories under -`$VESPA_HOME` are not accessible by other users if you store sensitive data in your application. - -Note also that private keys used by the container to set up TLS must be protected -to be readable by the container process only. - -Vespa does not have application-level support for encryption of on-disk document stores or -indexes. This would typically be implemented through file-system or storage-level (e.g. remote cloud storage) -encryption. - - -## Securing Vespa with mutually authenticated TLS (mTLS) - -Protect all internal endpoints and protocols in Vespa with mutually authenticated Transport Layer Security (mTLS). -See the [dedicated documentation](mtls) on how to get started, -and try the [multinode-HA](https://github.com/vespa-engine/sample-apps/tree/master/examples/operations/multinode-HA) -example. diff --git a/en/security/securing-your-vespa-installation.mdx b/en/security/securing-your-vespa-installation.mdx new file mode 100644 index 0000000000..62a1b95721 --- /dev/null +++ b/en/security/securing-your-vespa-installation.mdx @@ -0,0 +1,54 @@ +--- +title: "Securing a Vespa Installation" +--- + +<Note> +**Note:** + +This document is only relevant for **self-hosting Vespa** as Vespa Cloud does this for you. +</Note> + +It is critical to understand the security requirements and limitations of any networked system. Vespa is no exception. This document gives the most important information related to security at the network and physical host levels. + +To keep the self-hosted Vespa installation safe, follow the guidelines outlined below: +1. Isolate the Vespa hosts +2. Secure the application container with access control filters and TLS +3. Lockdown directory permissions +4. Securing Vespa with mutually authenticated TLS (mTLS) + + +## Isolating the Vespa hosts + +**When running self-hosted Vespa instances, hosts running Vespa MUST NOT be directly exposed to the public internet or to untrusted networks. Failure to ensure this may lead to data exfiltration/infiltration or host compromise.** + +Vespa's internal protocols are not authenticated by default and are therefore not safe in the face of untrusted network actors. When running Vespa in your own organization, or on the public cloud in particular, this is something you must take into account. + +Connections to *any* hosts running Vespa services should *only* be allowed from a controlled set of trusted hosts. All Vespa hosts must be able to connect to, and receive connections from, all other Vespa hosts that are part of the same installation. For added security, consider limiting Vespa hosts to only be able to talk to other Vespa hosts. If you are contacting external services as part of [federation](/en/querying/federation) in the application container, your container hosts must be able to connect to these services. + +This may be implemented by e.g. iptables, AWS Security Groups or similar technologies. + +The entry point into your Vespa installation is port 8080 on hosts running the application container. This port is used for feed, document retrieval and search queries. It should only be exposed to an untrusted network if you have properly [secured the application container](#securing-the-application-container). It should never be exposed directly to external traffic. All traffic to the containers should be sent by your frontends or backends. + +Internode communication inside a Vespa installation is not encrypted by default and will need to be configured explicitly as documented in [Securing Vespa with mutually authenticated TLS](/en/security/mtls). + +## Securing the application container + +By default, the container allows unauthenticated writes to, and reads from, the Vespa installation. For a production deployment, this must be locked down. + +Connections to the HTTP containers may be [protected with TLS](/en/applications/http-servers-and-filters#tls). Mutual TLS is supported and should be configured. + +Access to the container API endpoints can be controlled using [request filters](/en/applications/http-servers-and-filters#set-up-filter-chains). These filters can implement the required authentication and authorization logic for your specific use case. + +If you do not set up TLS with restrictive filter logic, you should restrict the container port in the same way as you would the rest of the Vespa hosts. + +## Locking down directory permissions + +All Vespa processes run under the Linux user given by `$VESPA_USER` and store their data under `$VESPA_HOME`. You should ensure the files and directories under `$VESPA_HOME` are not accessible by other users if you store sensitive data in your application. + +Note also that private keys used by the container to set up TLS must be protected to be readable by the container process only. + +Vespa does not have application-level support for encryption of on-disk document stores or indexes. This would typically be implemented through file-system or storage-level (e.g. remote cloud storage) encryption. + +## Securing Vespa with mutually authenticated TLS (mTLS) + +Protect all internal endpoints and protocols in Vespa with mutually authenticated Transport Layer Security (mTLS). See the [dedicated documentation](/en/security/mtls) on how to get started, and try the [multinode-HA](https://github.com/vespa-engine/sample-apps/tree/master/examples/operations/multinode-HA) example. diff --git a/en/security/security.md b/en/security/security.md deleted file mode 100644 index c2e98c37f8..0000000000 --- a/en/security/security.md +++ /dev/null @@ -1,15 +0,0 @@ ---- -title: Vespa Cloud Security -applies_to: cloud -redirect_from: -- /en/cloud/security/security ---- - -- [**Security Guide**](guide) is a practical guide to using the - different security features and getting started with them. -- [**Secret Store**](secret-store) is a guide on how to integrate - AWS Parameter Stores with Vespa Cloud. -- [**Cloudflare Workers**](cloudflare-workers) - describes how you can access mutal TLS protected Vespa Cloud endpoints using Cloudflare Workers. -- [**Whitepaper**](whitepaper) is an in-depth description of the - security architecture of Vespa Cloud. diff --git a/en/security/security.mdx b/en/security/security.mdx new file mode 100644 index 0000000000..0105f3df7a --- /dev/null +++ b/en/security/security.mdx @@ -0,0 +1,9 @@ +--- +title: "Vespa Cloud Security" +sidebarTitle: "Security overview" +--- + +- [**Security Guide**](/en/security/guide) is a practical guide to using the different security features and getting started with them. +- [**Secret Store**](/en/security/secret-store) is a guide on how to integrate AWS Parameter Stores with Vespa Cloud. +- [**Cloudflare Workers**](/en/security/cloudflare-workers) describes how you can access mutal TLS protected Vespa Cloud endpoints using Cloudflare Workers. +- [**Whitepaper**](/en/security/whitepaper) is an in-depth description of the security architecture of Vespa Cloud. diff --git a/en/security/whitepaper.md b/en/security/whitepaper.md deleted file mode 100644 index 13dd02f8a7..0000000000 --- a/en/security/whitepaper.md +++ /dev/null @@ -1,354 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: Vespa Cloud Security Whitepaper -applies_to: cloud -redirect_from: -- /en/cloud/security/whitepaper ---- - -<style> - td:nth-child(1) { - white-space: nowrap !important; - font-style: italic !important; - } - - tr { - margin-bottom: 0.5em !important; - } -</style> - -*Last updated: 2025-06-04* - -## Table of Contents - -- TOC -{:toc} - -## Introduction - -This document describes the Vespa Cloud service security features and -operational procedures. - -Vespa.ai has a SOC 2 attestation - read more in the [Trust Center](https://trust.vespa.ai/). - - - -## Concepts and architecture - -<!-- TODO: Make figure have split data plane? --> -<img alt="Vespa Cloud overall architecture diagram" style="max-height: 500px" src="/assets/img/overall-architecture.png"/> - -The Vespa Cloud consists of a _Control Plane_ and a _Date Plane_. Each have -their own web service APIs, respectively managing Vespa applications (Control), -and interacting with a deployed Vespa application (Data). - -The Control Plane manages deployment of applications in the zones they specify, -and lets tenant administrators manage their tenant information in Vespa Cloud. -The Control Plane is shared among all tenants in Vespa Cloud and is globally -synchronized. - -The Data Plane lets the tenants communicate with their deployed Vespa -applications. It supports queries, feeding, and any other type of requests the -tenant has configured and deployed in their application. The Data Plane is -isolated for each tenant, application, and (optionally) service. - -The Vespa Cloud is divided into _Zones_. A zone is a combination of an -_environment_ and a _region_ and have names like _prod.aws-us-east-1c_. Zones -are stand-alone and does not have critical dependencies on services outside the -zone. Tenants can implement service redundancy by specifying that applications -be deployed in multiple zones. - -A Zone is managed by a _Configuration Server_ cluster. These receive the -application packages from the _Control Plane_ on deployment and manages the -local deployment process in the zone, including provisioning the node resources -required to run the deployed application in the zone. Separately, it is -responsible for maintaining those resources - replacing failed nodes, upgrading -their OS and similar. - -Vespa applications run on _Nodes_ - a Linux container executed on a _Host_. The -_Host_ is the actual machine running the containers. Each Host has a management -process that receives instructions from the Configuration Server about what -containers should run on the Host. Once started, the containers ask the -Configuration Server cluster what Vespa services of what application they should -run. - -It is the individual Node that contains the customer data such as indexes and -document, and which receives the queries and feeding requests from the -customer's authenticated and authorized clients. Each Node is always dedicated -to a single Vespa application cluster. Hosts are shared by default, but -applications may specify that they require dedicated hosts to obtain an -additional level of security isolation. - -<!-- TODO: We need to define _deployment_ --> - -## Service deployment - -### Control plane authentication and authorization - -#### Control plane API access - -All API operations towards the Vespa Cloud control plane require authorization, -and no tenant or application information will be presented for unauthorized -access. A user can present a valid OAuth2 token which will be verified by the -API. If a OAuth2 token is not available the user can choose to use an API key -instead. The intended use for API keys is for service automation (e.g. CI/CD -workflows or GitHub actions), but they can also be used by developers. - -#### Roles and privileges - -Members of tenants in Vespa Cloud can be assigned to three different roles that -grant different privileges: - -- **Reader:** Can read tenant and application metadata. This is the minimal - privilege which is implicitly granted to all members of a tenant. -- **Developer:** Can create applications, deploy to dev and prod zones. - These are the privileges needed by members working on applications. -- **Administrator:** Can manage members of a tenant and tenant metadata, such as - tenant contact information and billing actions. - -All role memberships are stored in an external identity provider. - -#### Control plane audit logs -All operations against the control plane are persisted in an audit log capturing -_timestamp_, _client_, _principal_ (user), _HTTP method_, _resource_ accessed, -and _payload_ (for certain requests). As this data can potentially be sensitive, -it is available upon request from Vespa Cloud support. - -### Service isolation - -<img alt="image" width="100%" src="/assets/img/service-isolation.png" title="Service isolation"/> - -Nodes belonging to the same application are allowed to communicate with each -other while nodes of different applications are isolated on the network layer -and through authorization. - -Communication between Vespa services is encrypted and authenticated using mutual -TLS (mTLS). Identities and certificates are provided by infrastructure -components that can validate the configuration. - -#### Access control and service identity - -Each host and node has a unique cryptographic service identity. This identity is -required in all inter-service communication, including HTTPS and internal binary -RPC protocols. On the host, node, and configuration server level there are -authorization rules in place to ensure that only relevant services can -communicate with each other and retrieve resources from shared services, like -the configuration server. - -#### Node isolation - -The identity of the node is based on the tenant, application, and instance the -node is part of. The host and configuration server will together establish the -identity of the node. The configuration server tells the host which nodes it -should start, and the host requests a cryptographic identity for the nodes from -the identity provider. - -This node identity is used for all internal communication inside the -application. - -Nodes are implemented as Linux containers on the hosts. Each node runs in their -own container user namespaces, and each node has a dedicated IP address. - -#### Host isolation - -The lowest physical resource in the service architecture is a host. The -configuration server is responsible for provisioning hosts and will keep track -of known hosts, and reject any unknown hosts. Hosts only communicate directly -with the configuration server and cannot communicate with each other. - -#### Configuration isolation - -Both nodes and hosts will consume application configuration from the -configuration server. The configuration server will apply authorization rules -based on the host and node identity. Authorization rules are based on least -privilege. Hosts will only see which nodes to run, while the nodes are able to -access the application configuration. - -#### Network isolation - -All communication between services is protected through mTLS. mTLS authorization -is based on the identity mentioned above. In addition, network level isolation -is used to prevent any unauthorized network access between services. The network -rules are configured in the configuration server and applied by the host. -Changes to the topology are reflected within minutes. - -## Communication - -### Data plane - -All access to application endpoints are secured by mTLS and optionally token authentication. -Upon deployment, every application is provided a certificate with SAN DNS names -matching the endpoint names. This certificate will be automatically refreshed every -90 days. The application owner must provide a set of trusted Certificate Authorities -which will be used by all clients when accessing the endpoints using mTLS. - -### Federation - -It is possible for an application owner to federate calls to 3rd party services. -Either as scheduled jobs, or per request. To support this use case we provide -access to a credential storage in the customer's AWS account. - -## Data Storage - -### Encryption at Rest - -All customer data is encrypted at rest using the cloud provider's native encryption capabilities (AWS KMS or Google Cloud KMS). Encryption is performed with the following properties: - -* Cipher: A strong, industry-standard cipher such as AES-256 (or the provider's default strong cipher) -* Key Management: Customer-managed keys within the respective cloud provider's key management service (AWS KMS or Google Cloud KMS)   - -Access to the keys is strictly controlled and audited through IAM roles and policies employing least privilege. Key rotation is managed automatically by the cloud provider on a regular basis. - -### Data classification - -All data handled by Vespa Cloud is classified into two different classes which -has different policies associated with them. - -- **Internal data:** Information intended for internal consumption in Vespa - Cloud operations. This includes system level logs from services that do not - handle customer data. Internal data is readable by authenticated and - authorized members of the Vespa Cloud engineering team. - -- **Confidential data:** Confidential data is data that is sensitive to Vespa - Cloud or Vespa Cloud customers. Access to confidential data is subject to - stringent business need-to-know. Access to confidential data is regulated and - only granted to Vespa Cloud team members in a peer-approved, time-limited, and - audited manner. _All customer data is considered confidential._ - -### Asset types - -<table class="table is-striped"> - <thead> - <tr> - <th>Asset</th> - <th>Class</th> - <th>Description</th> - </tr> - </thead> - <tbody> - <tr> - <td>Control Plane data</td> - <td>Internal</td> - <td> - The Control Plane maintains a database to facilitate orchestration - of Vespa applications in multiple zones. This contains metadata - about tenants and applications in Vespa Cloud. - </td> - </tr> - <tr> - <td>Configuration Server data</td> - <td>Confidential</td> - <td> - The configuration server database contains the Vespa application - model as well as the orchestration database. Since the configuration - server is part of establishing node and host identities, the configuration server data - is considered confidential. - </td> - </tr> - <tr> - <td>Infrastructure logs</td> - <td>Internal</td> - <td> - Logs from infrastructure services like the configuration servers, - the control plane services, etc. are considered internal. - This includes logs from Control Plane, Configuration Servers, and Hosts. - </td> - </tr> - <tr> - <td>Application package</td> - <td>Internal</td> - <td> - The application.zip file uploaded to Vespa Cloud by the customer is - considered internal. The application package contains settings and - configuration that Vespa Cloud operations needs insight in to operate - the platform. - </td> - </tr> - <tr> - <td>Node logs</td> - <td>Confidential</td> - <td> - The logs inside the Node may contain data printed by the customer. - Because of this the logs are classified as confidential since Vespa - Cloud cannot guarantee they are free of confidential data. - This includes Data Plane access logs in addition to the node - Vespa logs. - </td> - </tr> - <tr> - <td>Core dumps / heap dumps</td> - <td>Confidential</td> - <td> - Occasionally core dumps and heap dumps are generated for running - services. These files may contain customer data and are considered - confidential. - </td> - </tr> - <tr> - <td>Node data</td> - <td>Confidential</td> - <td> - All data on the node itself is considered confidential. This data - includes the document data and the indexes of the application. - </td> - </tr> - </tbody> -</table> - -#### Logs - -All logs are stored on the nodes where they are generated, but also archived to -a remote object storage. All logs are kept for a maximum of 30 days. Access to -logs is based on the classifications described above. All logs are persisted in -the same geographic region as the Vespa application that generated them. - -Archived logs are encrypted at rest with keys automatically rotated at regular -intervals. - -Logs on the node are encrypted at rest with the same mechanism that encrypts -indexes and document databases. - -### Access management - -Access to confidential data is only granted on a case-by-case basis. Access is -reviewed, time-limited, and audited. No Vespa Cloud team member is allowed to -access any confidential data without review. - -## Security Measures - -Vespa Cloud employs a multi-layered approach to security, encompassing -vulnerability management, secure development practices, and proactive testing. -These include: - -## Security Testing - -Vespa Cloud proactively assesses its security posture through: - -* A vulnerability disclosure program, detailed at -https://vespa.ai/responsible-disclosure/, enabling security -researchers to responsibly report potential vulnerabilities. -* A yearly hybrid security pentest program, conducted in partnership with -Intigriti, to proactively identify and address vulnerabilities. - -### Secure Development - -Vespa Cloud follows a CI/CD process with mandatory code review for all commits. -Static analysis tools are employed to detect issues in source code and -third-party dependencies. In addition, the security team conducts regular -internal security reviews of code and infrastructure to identify and address -potential vulnerabilities throughout the development lifecycle. - -### Vulnerability Management - -Vespa is released up to 4 times a week, and we strive to keep all applications -and dependencies updated to the latest versions. Operating system upgrades are -rolled out every 90 days to address OS-level vulnerabilities. In case of a -severe security issue, fixes are applied and rolled out as quickly as possible. - -### Incident Response - -Any unexpected production issue, including security incidents, is handled -through our incident management process. Non-security incidents are announced -through our console. Security incidents are communicated directly to affected -customers. A post-mortem review process is initiated after every incident. In -the event of a potential security breach, a forensic investigation is conducted. diff --git a/en/security/whitepaper.mdx b/en/security/whitepaper.mdx new file mode 100644 index 0000000000..90af3c42cf --- /dev/null +++ b/en/security/whitepaper.mdx @@ -0,0 +1,194 @@ +--- +title: "Vespa Cloud Security Whitepaper" +sidebarTitle: "Security Whitepaper" +--- + +## Introduction + +This document describes the Vespa Cloud service security features and operational procedures. + +Vespa.ai has a SOC 2 attestation - read more in the [Trust Center](https://trust.vespa.ai/). + +## Concepts and architecture + +<Frame>![Vespa Cloud overall architecture diagram](/assets/img/overall-architecture.png)</Frame> + +The Vespa Cloud consists of a *Control Plane* and a *Date Plane*. Each have their own web service APIs, respectively managing Vespa applications (Control), and interacting with a deployed Vespa application (Data). + +The Control Plane manages deployment of applications in the zones they specify, and lets tenant administrators manage their tenant information in Vespa Cloud. The Control Plane is shared among all tenants in Vespa Cloud and is globally synchronized. + +The Data Plane lets the tenants communicate with their deployed Vespa applications. It supports queries, feeding, and any other type of requests the tenant has configured and deployed in their application. The Data Plane is isolated for each tenant, application, and (optionally) service. + +The Vespa Cloud is divided into *Zones*. A zone is a combination of an *environment* and a *region* and have names like *prod.aws-us-east-1c*. Zones are stand-alone and does not have critical dependencies on services outside the zone. Tenants can implement service redundancy by specifying that applications be deployed in multiple zones. + +A Zone is managed by a *Configuration Server* cluster. These receive the application packages from the *Control Plane* on deployment and manages the local deployment process in the zone, including provisioning the node resources required to run the deployed application in the zone. Separately, it is responsible for maintaining those resources - replacing failed nodes, upgrading their OS and similar. + +Vespa applications run on *Nodes* - a Linux container executed on a *Host*. The *Host* is the actual machine running the containers. Each Host has a management process that receives instructions from the Configuration Server about what containers should run on the Host. Once started, the containers ask the Configuration Server cluster what Vespa services of what application they should run. + +It is the individual Node that contains the customer data such as indexes and document, and which receives the queries and feeding requests from the customer's authenticated and authorized clients. Each Node is always dedicated to a single Vespa application cluster. Hosts are shared by default, but applications may specify that they require dedicated hosts to obtain an additional level of security isolation. + +## Service deployment + +### Control plane authentication and authorization + +#### Control plane API access + +All API operations towards the Vespa Cloud control plane require authorization, and no tenant or application information will be presented for unauthorized access. A user can present a valid OAuth2 token which will be verified by the API. If a OAuth2 token is not available the user can choose to use an API key instead. The intended use for API keys is for service automation (e.g. CI/CD workflows or GitHub actions), but they can also be used by developers. + +#### Roles and privileges + +Members of tenants in Vespa Cloud can be assigned to three different roles that grant different privileges: + +- **Reader:** Can read tenant and application metadata. This is the minimal privilege which is implicitly granted to all members of a tenant. +- **Developer:** Can create applications, deploy to dev and prod zones. These are the privileges needed by members working on applications. +- **Administrator:** Can manage members of a tenant and tenant metadata, such as tenant contact information and billing actions. + +All role memberships are stored in an external identity provider. + +#### Control plane audit logs + +All operations against the control plane are persisted in an audit log capturing *timestamp*, *client*, *principal* (user), *HTTP method*, *resource* accessed, and *payload* (for certain requests). As this data can potentially be sensitive, it is available upon request from Vespa Cloud support. + +### Service isolation + +<Frame>![image](/assets/img/service-isolation.png "Service isolation")</Frame> + +Nodes belonging to the same application are allowed to communicate with each other while nodes of different applications are isolated on the network layer and through authorization. + +Communication between Vespa services is encrypted and authenticated using mutual TLS (mTLS). Identities and certificates are provided by infrastructure components that can validate the configuration. + +#### Access control and service identity + +Each host and node has a unique cryptographic service identity. This identity is required in all inter-service communication, including HTTPS and internal binary RPC protocols. On the host, node, and configuration server level there are authorization rules in place to ensure that only relevant services can communicate with each other and retrieve resources from shared services, like the configuration server. + +#### Node isolation + +The identity of the node is based on the tenant, application, and instance the node is part of. The host and configuration server will together establish the identity of the node. The configuration server tells the host which nodes it should start, and the host requests a cryptographic identity for the nodes from the identity provider. + +This node identity is used for all internal communication inside the application. + +Nodes are implemented as Linux containers on the hosts. Each node runs in their own container user namespaces, and each node has a dedicated IP address. + +#### Host isolation + +The lowest physical resource in the service architecture is a host. The configuration server is responsible for provisioning hosts and will keep track of known hosts, and reject any unknown hosts. Hosts only communicate directly with the configuration server and cannot communicate with each other. + +#### Configuration isolation + +Both nodes and hosts will consume application configuration from the configuration server. The configuration server will apply authorization rules based on the host and node identity. Authorization rules are based on least privilege. Hosts will only see which nodes to run, while the nodes are able to access the application configuration. + +#### Network isolation + +All communication between services is protected through mTLS. mTLS authorization is based on the identity mentioned above. In addition, network level isolation is used to prevent any unauthorized network access between services. The network rules are configured in the configuration server and applied by the host. Changes to the topology are reflected within minutes. + +## Communication + +### Data plane + +All access to application endpoints are secured by mTLS and optionally token authentication. Upon deployment, every application is provided a certificate with SAN DNS names matching the endpoint names. This certificate will be automatically refreshed every 90 days. The application owner must provide a set of trusted Certificate Authorities which will be used by all clients when accessing the endpoints using mTLS. + +### Federation + +It is possible for an application owner to federate calls to 3rd party services. Either as scheduled jobs, or per request. To support this use case we provide access to a credential storage in the customer's AWS account. + +## Data Storage + +### Encryption at Rest + +All customer data is encrypted at rest using the cloud provider's native encryption capabilities (AWS KMS or Google Cloud KMS). Encryption is performed with the following properties: + +- Cipher: A strong, industry-standard cipher such as AES-256 (or the provider's default strong cipher) +- Key Management: Customer-managed keys within the respective cloud provider's key management service (AWS KMS or Google Cloud KMS) + +Access to the keys is strictly controlled and audited through IAM roles and policies employing least privilege. Key rotation is managed automatically by the cloud provider on a regular basis. + +### Data classification + +All data handled by Vespa Cloud is classified into two different classes which has different policies associated with them. + +- **Internal data:** Information intended for internal consumption in Vespa Cloud operations. This includes system level logs from services that do not handle customer data. Internal data is readable by authenticated and authorized members of the Vespa Cloud engineering team. +- **Confidential data:** Confidential data is data that is sensitive to Vespa Cloud or Vespa Cloud customers. Access to confidential data is subject to stringent business need-to-know. Access to confidential data is regulated and only granted to Vespa Cloud team members in a peer-approved, time-limited, and audited manner. *All customer data is considered confidential.* + +### Asset types + +<table className="vespa-table"> + <thead> + <tr> + <th>Asset</th> + <th>Class</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>Control Plane data</td> + <td>Internal</td> + <td>The Control Plane maintains a database to facilitate orchestration of Vespa applications in multiple zones. This contains metadata about tenants and applications in Vespa Cloud.</td> + </tr> + <tr> + <td>Configuration Server data</td> + <td>Confidential</td> + <td>The configuration server database contains the Vespa application model as well as the orchestration database. Since the configuration server is part of establishing node and host identities, the configuration server data is considered confidential.</td> + </tr> + <tr> + <td>Infrastructure logs</td> + <td>Internal</td> + <td>Logs from infrastructure services like the configuration servers, the control plane services, etc. are considered internal. This includes logs from Control Plane, Configuration Servers, and Hosts.</td> + </tr> + <tr> + <td>Application package</td> + <td>Internal</td> + <td>The application.zip file uploaded to Vespa Cloud by the customer is considered internal. The application package contains settings and configuration that Vespa Cloud operations needs insight in to operate the platform.</td> + </tr> + <tr> + <td>Node logs</td> + <td>Confidential</td> + <td>The logs inside the Node may contain data printed by the customer. Because of this the logs are classified as confidential since Vespa Cloud cannot guarantee they are free of confidential data. This includes Data Plane access logs in addition to the node Vespa logs.</td> + </tr> + <tr> + <td>Core dumps / heap dumps</td> + <td>Confidential</td> + <td>Occasionally core dumps and heap dumps are generated for running services. These files may contain customer data and are considered confidential.</td> + </tr> + <tr> + <td>Node data</td> + <td>Confidential</td> + <td>All data on the node itself is considered confidential. This data includes the document data and the indexes of the application.</td> + </tr> + </tbody> +</table> + +#### Logs + +All logs are stored on the nodes where they are generated, but also archived to a remote object storage. All logs are kept for a maximum of 30 days. Access to logs is based on the classifications described above. All logs are persisted in the same geographic region as the Vespa application that generated them. + +Archived logs are encrypted at rest with keys automatically rotated at regular intervals. + +Logs on the node are encrypted at rest with the same mechanism that encrypts indexes and document databases. + +### Access management + +Access to confidential data is only granted on a case-by-case basis. Access is reviewed, time-limited, and audited. No Vespa Cloud team member is allowed to access any confidential data without review. + +## Security Measures + +Vespa Cloud employs a multi-layered approach to security, encompassing vulnerability management, secure development practices, and proactive testing. These include: + +## Security Testing + +Vespa Cloud proactively assesses its security posture through: + +- A vulnerability disclosure program, detailed at https://vespa.ai/responsible-disclosure/, enabling security researchers to responsibly report potential vulnerabilities. +- A yearly hybrid security pentest program, conducted in partnership with Intigriti, to proactively identify and address vulnerabilities. + +### Secure Development + +Vespa Cloud follows a CI/CD process with mandatory code review for all commits. Static analysis tools are employed to detect issues in source code and third-party dependencies. In addition, the security team conducts regular internal security reviews of code and infrastructure to identify and address potential vulnerabilities throughout the development lifecycle. + +### Vulnerability Management + +Vespa is released up to 4 times a week, and we strive to keep all applications and dependencies updated to the latest versions. Operating system upgrades are rolled out every 90 days to address OS-level vulnerabilities. In case of a severe security issue, fixes are applied and rolled out as quickly as possible. + +### Incident Response + +Any unexpected production issue, including security incidents, is handled through our incident management process. Non-security incidents are announced through our console. Security incidents are communicated directly to affected customers. A post-mortem review process is initiated after every incident. In the event of a potential security breach, a forensic investigation is conducted. diff --git a/en/writing/batch-delete.html b/en/writing/batch-delete.html deleted file mode 100644 index c64f053fba..0000000000 --- a/en/writing/batch-delete.html +++ /dev/null @@ -1,204 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Batch delete" -redirect_from: -- /en/operations/batch-delete.html ---- - -<p> - Options for batch deleting documents: -</p> -<ol> - <li> - Use <a href="../clients/vespa-cli.html#documents">vespa feed</a>: -<pre> -$ vespa feed -t my-endpoint deletes.json -</pre> - </li> - <li> - Find documents using a query, delete, repeat. Pseudocode: -<pre> -while True; do - query and read document IDs, if empty exit - delete document IDs using <a href="../reference/api/document-v1.html#delete">/document/v1</a> - wait a sec # optional, add wait to reduce load while deleting -</pre> - </li> - <li> - Use a <a href="../schemas/documents.html#document-expiry">document selection</a> to expire documents. - This deletes all documents <em>not</em> matching the expression. - It is possible to use parent documents and imported fields for expiry of a document set. - The content node will iterate over the corpus and delete documents (that are later compacted out): -<pre>{% highlight xml %} -<documents garbage-collection="true"> - <document type="mytype" - mode="index" - selection="mytype.version > 4" /> -</documents> -{% endhighlight %}</pre> - </li> - <li> - Use <a href="../reference/api/document-v1.html#delete">/document/v1</a> to delete documents - identified by a <a href="../reference/writing/document-selector-language.html">document selection</a> - - example dropping all documents from the <em>my_doctype</em> schema. - The <em>cluster</em> value is the ID of the content cluster in <em>services.xml</em>, - e.g., <code><content id="my_cluster" version="1.0"></code>: -<pre> -$ curl -X DELETE \ - "$ENDPOINT/document/v1/my_namespace/my_doctype/docid?selection=true&cluster=my_cluster" -</pre> - </li> - <li> - <p id="drop-schema"> - It is possible to drop a schema, with all its content, by removing the mapping to the content cluster. - To understand what is happening, here is the status before the procedure: - </p> -<pre> -# ls $VESPA_HOME/var/db/vespa/search/cluster.music/n0/documents - -drwxr-xr-x 6 vespa vespa 4096 Oct 25 16:59 books -drwxr-xr-x 6 vespa vespa 4096 Oct 25 12:47 music -</pre> - <p>Remove the schema from configuration:</p> -<pre> -<documents> - <document type="music" mode="index" /> - <span class="pre-hilite"><!--document type="books" mode="index" /--></span> -</documents> -</pre> - <p> - It is not required to remove the schema file itself. - It is however required to add a <code>schema-removal</code> entry to - <a href="../reference/applications/validation-overrides.html">validation-overrides.xml</a>: - </p> -<pre> -<validation-overrides> - <allow until="2022-10-31">schema-removal</allow> -</validation-overrides> -</pre> - {% include note.html content='Use validation override name <code>content-type-removal</code> before Vespa 8.73' %} - <p> - Deploy the application package. - This will reconfigure the content node processes, - and the directory with the schema data is removed: - </p> -<pre> -# ls $VESPA_HOME/var/db/vespa/search/cluster.music/n0/documents - -drwxr-xr-x 6 vespa vespa 4096 Oct 25 12:47 music -</pre> - <p> - Add the mapping back and redeploy - the cluster now has a <code>books</code> schema with zero documents. - </p> -<pre> -# ls $VESPA_HOME/var/db/vespa/search/cluster.music/n0/documents - -drwxr-xr-x 6 vespa vespa 4096 Oct 25 17:06 books -drwxr-xr-x 6 vespa vespa 4096 Oct 25 12:47 music -</pre> - <p> - Use the <a href="../content/proton.html#custom-component-state-api">Custom Component State API</a> - to inspect document count per schema. - </p> - <p> - The procedure, deploying with and without the schema, is an efficient way to drop all documents. - After the procedure, it is good practice to remove <em>validation-overrides.xml</em> - or the <code>schema-removal</code> element inside, to avoid accidental data loss later. - The directory listing above is just for illustration. - </p> - </li> -</ol> - - - -<h2 id="example">Example</h2> -<p> - This is an end-to-end example on how to track number of documents, and delete a subset using a - <a href="../reference/writing/document-selector-language.html">selection string</a>. -</p> -<h3 id="feed-sample-documents">Feed sample documents</h3> -<p> - Feed a batch of documents, e.g. using the <a href="https://github.com/vespa-cloud/vector-search">vector-search</a> - sample application:</p> -<pre> -$ vespa feed <(python3 feed.py 100000 3) -</pre> -<p> - See number of documents for a node using the - <a href="../reference/operations/metrics/searchnode.html#content_proton_documentdb_documents_total"> - content.proton.documentdb.documents.total</a> metric (here 100,000): -</p> -<pre> -$ docker exec vespa curl -s http://localhost:19092/prometheus/v1/values | grep ^content.proton.documentdb.documents.total - - content_proton_documentdb_documents_total_max{metrictype="standard",instance="searchnode",documenttype="vector",clustername="vectors",vespa_service="vespa_searchnode",} 100000.0 1695383025000 - - content_proton_documentdb_documents_total_last{metrictype="standard",instance="searchnode",documenttype="vector",clustername="vectors",vespa_service="vespa_searchnode",} 100000.0 1695383025000 -</pre> -<p> - Using the metric above is useful while feeding this example. - Another alternative is <a href="visiting.html">visiting</a> all documents to print the ID: -</p> -<pre> -$ vespa visit --field-set "[id]" | wc -l - 100000 -</pre> -<p>At this point, there are 100,000 document in the index.</p> - - -<h3 id="define-selection">Define selection</h3> -<p> - Define the subset of documents to delete - e.g. by age or other criteria. - In this example, select random 1%. Do a test run: -</p> -<pre> -$ vespa visit --field-set "[id]" --selection 'id.hash().abs() % 100 == 0' | wc -l - 1016 -</pre> -<p> - Hence, the selection string <code>id.hash().abs() % 100 == 0</code> hits 1,016 documents. -</p> - - -<h3 id="delete-documents">Delete documents</h3> -<p> - Delete documents, see the number of documents deleted in the response: -</p> -<pre> -$ curl -X DELETE \ - "http://localhost:8080/document/v1/mynamespace/vector/docid?selection=id.hash%28%29.abs%28%29+%25+100+%3D%3D+0&cluster=vectors" - - { - "pathId":"/document/v1/mynamespace/vector/docid", - "documentCount":1016 - } -</pre> -<p> - In case of a large result set, a <code>continuation</code> token is returned in the response. - Loop until no more tokens are returned: -</p> -<pre> -$ ENDPOINT="http://localhost:8080" -$ URI="/document/v1/mynamespace/vector/docid?selection=id.hash%28%29.abs%28%29+%25+100+%3D%3D+0&cluster=vectors" -$ CONTINUATION="" -$ while true; do - RESPONSE=$(curl -s -X DELETE "${ENDPOINT}${URI}${CONTINUATION}") - echo "$RESPONSE" - TOKEN=$(echo "$RESPONSE" | jq -r '.continuation // empty') - if [ -z "$TOKEN" ]; then break; fi - CONTINUATION="&continuation=${TOKEN}" - done -</pre> - - -<h3 id="validate">Validate</h3> -<p>Check that all documents matching the selection criterion are deleted:</p> -<pre> -$ vespa visit --selection 'id.hash().abs() % 100 == 0' --field-set "[id]" | wc -l - 0 -</pre> -<p>List remaining documents:</p> -<pre> -$ vespa visit --field-set "[id]" | wc -l - 98984 -</pre> diff --git a/en/writing/batch-delete.mdx b/en/writing/batch-delete.mdx new file mode 100644 index 0000000000..0c53696354 --- /dev/null +++ b/en/writing/batch-delete.mdx @@ -0,0 +1,187 @@ +--- +title: "Batch delete" +--- + +Options for batch deleting documents: + +<Steps> +<Step> +Use [vespa feed](/en/clients/vespa-cli#documents): + +```sh +$ vespa feed -t my-endpoint deletes.json +``` + +</Step> +<Step> +Find documents using a query, delete, repeat. Pseudocode: + +```sh +while True; do + query and read document IDs, if empty exit + delete document IDs using [/document/v1](/en/reference/api/document-v1#delete) + wait a sec # optional, add wait to reduce load while deleting +``` +</Step> +<Step> +Use a [document selection](/en/schemas/documents#document-expiry) to expire documents. This deletes all documents *not* matching the expression. It is possible to use parent documents and imported fields for expiry of a document set. The content node will iterate over the corpus and delete documents (that are later compacted out): + +```xml +<documents garbage-collection="true"> + <document type="mytype" + mode="index" + selection="mytype.version > 4" /> +</documents> +``` +</Step> +<Step> +Use [/document/v1](/en/reference/api/document-v1#delete) to delete documents identified by a [document selection](/en/reference/writing/document-selector-language) — example dropping all documents from the *my_doctype* schema. The *cluster* value is the ID of the content cluster in *services.xml*, e.g., `<content id="my_cluster" version="1.0">`: + +```sh +$ curl -X DELETE \ + "$ENDPOINT/document/v1/my_namespace/my_doctype/docid?selection=true&cluster=my_cluster" +``` + +</Step> +<Step> +It is possible to drop a schema, with all its content, by removing the mapping to the content cluster. To understand what is happening, here is the status before the procedure: + +```sh +# ls $VESPA_HOME/var/db/vespa/search/cluster.music/n0/documents + +drwxr-xr-x 6 vespa vespa 4096 Oct 25 16:59 books +drwxr-xr-x 6 vespa vespa 4096 Oct 25 12:47 music +``` + +Remove the schema from configuration: + +```xml +<documents> + <document type="music" mode="index" /> + {/* document type="books" mode="index" / */} +</documents> +``` + +It is not required to remove the schema file itself. It is however required to add a `schema-removal` entry to [validation-overrides.xml](/en/reference/applications/validation-overrides): + +```xml +<validation-overrides> + <allow until="2022-10-31">schema-removal</allow> +</validation-overrides> +``` + +<Note> +**Note:** + +Use validation override name `content-type-removal` before Vespa 8.73. +</Note> + +Deploy the application package. This will reconfigure the content node processes, and the directory with the schema data is removed: + +```sh +# ls $VESPA_HOME/var/db/vespa/search/cluster.music/n0/documents + +drwxr-xr-x 6 vespa vespa 4096 Oct 25 12:47 music +``` + +Add the mapping back and redeploy — the cluster now has a `books` schema with zero documents. + +```sh +# ls $VESPA_HOME/var/db/vespa/search/cluster.music/n0/documents + +drwxr-xr-x 6 vespa vespa 4096 Oct 25 17:06 books +drwxr-xr-x 6 vespa vespa 4096 Oct 25 12:47 music +``` + +Use the [Custom Component State API](/en/content/proton#custom-component-state-api) to inspect document count per schema. + +The procedure, deploying with and without the schema, is an efficient way to drop all documents. After the procedure, it is good practice to remove *validation-overrides.xml* or the `schema-removal` element inside, to avoid accidental data loss later. The directory listing above is just for illustration. +</Step> +</Steps> + +## Example + +This is an end-to-end example on how to track number of documents, and delete a subset using a [selection string](/en/reference/writing/document-selector-language). + +### Feed sample documents + +Feed a batch of documents, e.g. using the [vector-search](https://github.com/vespa-cloud/vector-search) sample application: + +```sh +$ vespa feed <(python3 feed.py 100000 3) +``` + +See number of documents for a node using the [content.proton.documentdb.documents.total](/en/reference/operations/metrics/searchnode#content_proton_documentdb_documents_total) metric (here 100,000): + +```sh +$ docker exec vespa curl -s http://localhost:19092/prometheus/v1/values | grep ^content.proton.documentdb.documents.total + + content_proton_documentdb_documents_total_max{metrictype="standard",instance="searchnode",documenttype="vector",clustername="vectors",vespa_service="vespa_searchnode",} 100000.0 1695383025000 + + content_proton_documentdb_documents_total_last{metrictype="standard",instance="searchnode",documenttype="vector",clustername="vectors",vespa_service="vespa_searchnode",} 100000.0 1695383025000 +``` + +Using the metric above is useful while feeding this example. Another alternative is [visiting](/en/writing/visiting) all documents to print the ID: + +```sh +$ vespa visit --field-set "[id]" | wc -l + 100000 +``` + +At this point, there are 100,000 document in the index. + +### Define selection + +Define the subset of documents to delete — e.g. by age or other criteria. In this example, select random 1%. Do a test run: + +```sh +$ vespa visit --field-set "[id]" --selection 'id.hash().abs() % 100 == 0' | wc -l + 1016 +``` + +Hence, the selection string `id.hash().abs() % 100 == 0` hits 1,016 documents. + +### Delete documents + +Delete documents, see the number of documents deleted in the response: + +```sh +$ curl -X DELETE \ + "http://localhost:8080/document/v1/mynamespace/vector/docid?selection=id.hash%28%29.abs%28%29+%25+100+%3D%3D+0&cluster=vectors" + + { + "pathId":"/document/v1/mynamespace/vector/docid", + "documentCount":1016 + } +``` + +In case of a large result set, a `continuation` token is returned in the response. Loop until no more tokens are returned: + +```sh +$ ENDPOINT="http://localhost:8080" +$ URI="/document/v1/mynamespace/vector/docid?selection=id.hash%28%29.abs%28%29+%25+100+%3D%3D+0&cluster=vectors" +$ CONTINUATION="" +$ while true; do + RESPONSE=$(curl -s -X DELETE "${ENDPOINT}${URI}${CONTINUATION}") + echo "$RESPONSE" + TOKEN=$(echo "$RESPONSE" | jq -r '.continuation // empty') + if [ -z "$TOKEN" ]; then break; fi + CONTINUATION="&continuation=${TOKEN}" + done +``` + +### Validate + +Check that all documents matching the selection criterion are deleted: + +```sh +$ vespa visit --selection 'id.hash().abs() % 100 == 0' --field-set "[id]" | wc -l + 0 +``` + +List remaining documents: + +```sh +$ vespa visit --field-set "[id]" | wc -l + 98984 +``` diff --git a/en/writing/document-api-guide.html b/en/writing/document-api-guide.html deleted file mode 100644 index 4d14ff56bc..0000000000 --- a/en/writing/document-api-guide.html +++ /dev/null @@ -1,625 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Document API" -redirect_from: -- /en/document-api-guide.html ---- - -<p> - This is an introduction to how to build and compile Vespa clients using the Document API. - It can be used for feeding, updating and retrieving documents, - or removing documents from the repository. See also the - <a href="https://javadoc.io/doc/com.yahoo.vespa/documentapi">Java reference</a>. -</p> -<p> - Use the <a href="../operations/self-managed/files-processes-and-ports.html#environment-variables">VESPA_CONFIG_SOURCES</a> - environment variable to set config servers to interface with. -</p> -<p> - The most common use case is using the async API in a - <a href="../applications/document-processors.html">document processor</a> - from the sample apps: -</p> -<ul> - <li>Async GET in - <a href="https://github.com/vespa-engine/sample-apps/blob/master/examples/document-processing/src/main/java/ai/vespa/example/album/LyricsDocumentProcessor.java"> - LyricsDocumentProcessor.java</a></li> - <li>Async UPDATE in - <a href="https://github.com/vespa-engine/sample-apps/blob/master/use-case-shopping/src/main/java/ai/vespa/example/shopping/ReviewProcessor.java"> - ReviewProcessor.java</a></li> -</ul> - - - -<h2 id="documents">Documents</h2> -<p> -All data fed, indexed and searched in Vespa are instances of the <code>Document</code> class. -A <a href="../schemas/documents.html">document</a> is a composite object that consists of: -</p> -<ul> -<li><p>A <code>DocumentType</code> that defines the set of fields that - can exist in a document. A document can only have a single - <em>document type</em>, but document types can inherit the content of another. - All fields of an inherited type is available in all its descendants. - The document type is defined in the - <a href="../reference/schemas/schemas.html">schema</a>, - which is converted into a configuration file to be read by the - <code>DocumentManager</code>. - </p><p> - All registered document types are instantiated and stored within - the document manager. A reference to these objects can be - retrieved using the <code>getDocumentType()</code> method by - supplying the name and the version of the wanted document type. - </p><p> - <code>DocumentManager</code> initialization is done automatically - by the Document API by subscribing to the appropriate - configuration. - </p></li> -<li><p>A <code>DocumentId</code> which is a unique document identifier. - The document distribution uses the document identifier, - see the <a href="../content/buckets.html#distribution">reference</a> for details. - </p></li> -<li><p>A set of <code>(Field, FieldValue)</code> pairs, or - "fields" for short. The <code>Field</code> class has - methods for getting its name, data type and internal - identifier. The field object for a given field name can be - retrieved using the <code>getField(<fieldname>)</code> - method in the <code>DocumentType</code>. - </p></li> -</ul> -<p> -Use <a href="https://javadoc.io/doc/com.yahoo.vespa/documentapi/latest/com/yahoo/documentapi/DocumentAccess.html"> -DocumentAccess</a> javadoc. Sample app: -</p> -<pre> -<dependencies> - <dependency> - <groupId>com.yahoo.vespa</groupId> - <artifactId>documentapi</artifactId> - <version>{{site.variables.vespa_version}}</version> <!-- Find latest version at <a href="https://search.maven.org/search?q=g:com.yahoo.vespa%20a:documentapi">search.maven.org/search?q=g:com.yahoo.vespa%20a:documentapi</a> --> - </dependency> -<dependencies> -</pre> -<pre> -import com.yahoo.document.DataType; -import com.yahoo.document.Document; -import com.yahoo.document.DocumentId; -import com.yahoo.document.DocumentPut; -import com.yahoo.document.DocumentType; -import com.yahoo.document.DocumentUpdate; -import com.yahoo.document.datatypes.StringFieldValue; -import com.yahoo.document.datatypes.WeightedSet; -import com.yahoo.document.update.FieldUpdate; -import com.yahoo.documentapi.DocumentAccess; -import com.yahoo.documentapi.SyncParameters; -import com.yahoo.documentapi.SyncSession; - -public class DocClient { - - public static void main(String[] args) { - - // DocumentAccess is injectable in Vespa containers, but not in command line tools, etc. - DocumentAccess access = DocumentAccess.createForNonContainer(); - DocumentType type = access.getDocumentTypeManager().getDocumentType("music"); - DocumentId id = new DocumentId("id:namespace:music::0"); - Document docIn = new Document(type, id); - SyncSession session = access.createSyncSession(new SyncParameters.Builder().build()); - - // Put document with a1,1 - WeightedSet<StringFieldValue> wset = new WeightedSet<>(DataType.getWeightedSet(DataType.STRING)); - wset.put(new StringFieldValue("a1"), 1); - docIn.setFieldValue("aWeightedset", wset); - DocumentPut put = new DocumentPut(docIn); - System.out.println(docIn.toJson()); - session.put(put); - - // Update document with a1,10 and a2,20 - DocumentUpdate upd1 = new DocumentUpdate(type, id); - WeightedSet<StringFieldValue> wset1 = new WeightedSet<>(DataType.getWeightedSet(DataType.STRING)); - wset1.put(new StringFieldValue("a1"), 10); - wset1.put(new StringFieldValue("a2"), 20); - upd1.addFieldUpdate(FieldUpdate.createAddAll(type.getField("aWeightedset"), wset1)); - System.out.println(upd1.toString()); - session.update(upd1); - - Document docOut = session.get(id); - System.out.println("document get:" + docOut.toJson()); - - session.destroy(); - access.shutdown(); - } -} -</pre> -<p> -To test using the <a href="https://github.com/vespa-engine/sample-apps/tree/master/album-recommendation">sample apps</a>, -enable more ports for client to connect to config server and other processes on localhost - change docker command: -</p> -<pre> -$ docker run --detach --name vespa --hostname <span class="pre-hilite">localhost</span> --privileged \ - --volume $VESPA_SAMPLE_APPS:/vespa-sample-apps --publish 8080:8080 \ - <span class="pre-hilite">--publish 19070:19070 --publish 19071:19071 --publish 19090:19090 --publish 19099:19099 --publish 19101:19101 --publish 19112:19112</span> \ - vespaengine/vespa -</pre> - - - -<h2 id="fields">Fields</h2> -<p> -Examples: -</p> -<pre> -doc.setFieldValue("aByte", (byte)1); -doc.setFieldValue("aInt", (int)1); -doc.setFieldValue("aLong", (long)1); -doc.setFieldValue("aFloat", 1.0); -doc.setFieldValue("aDouble", 1.0); -doc.setFieldValue("aBool", new BoolFieldValue(true)); -doc.setFieldValue("aString", "Hello Field!"); - -doc.setFieldValue("unknownField", "Will not see me!"); - -Array<IntegerFieldValue> intArray = new Array<>(doc.getField("aArray").getDataType()); -intArray.add(new IntegerFieldValue(11)); -intArray.add(new IntegerFieldValue(12)); -doc.setFieldValue("aArray", intArray); - -Struct pos = PositionDataType.valueOf(1,2); - pos = PositionDataType.fromString("N0.000002;E0.000001"); // two ways to set same value -doc.setFieldValue("aPosition", pos); - -doc.setFieldValue("aPredicate", new PredicateFieldValue("aLong in [10..20]")); - -byte[] rawBytes = new byte[100]; -for (int i = 0; i < rawBytes.length; i++) { - rawBytes[i] = (byte)i; -} -doc.setFieldValue("aRaw", new Raw(ByteBuffer.wrap(rawBytes))); - -Tensor tensor = Tensor.Builder.of(TensorType.fromSpec("tensor<float>>(x[2],y[2])")). - cell().label("x", 0).label("y", 0).value(1.0). - cell().label("x", 0).label("y", 1).value(2.0). - cell().label("x", 1).label("y", 0).value(3.0). - cell().label("x", 1).label("y", 1).value(5.0).build(); -doc.setFieldValue("aTensor", new TensorFieldValue(tensor)); - -MapFieldValue<StringFieldValue, StringFieldValue> map = new MapFieldValue<>(new MapDataType(DataType.STRING, DataType.STRING)); -map.put(new StringFieldValue("key1"), new StringFieldValue("foo")); -map.put(new StringFieldValue("key2"), new StringFieldValue("bar")); -doc.setFieldValue("aMap", map); - -WeightedSet<StringFieldValue> wset = new WeightedSet<>(DataType.getWeightedSet(DataType.STRING)); -wset.put(new StringFieldValue("strval 1"), 5); -wset.put(new StringFieldValue("strval 2"), 10); -doc.setFieldValue("aWeightedset", wset); -</pre> - - - -<h2 id="document-updates">Document updates</h2> -<p> -A document update is a request to modify a document, see <a href="reads-and-writes.html">reads and writes</a>. -</p><p> -Primitive, and some multivalue fields (WeightedSet and Array<primitive>), are updated using a -<a href="https://javadoc.io/doc/com.yahoo.vespa/document/latest/com/yahoo/document/update/FieldUpdate.html"> -FieldUpdate</a>. -</p><p> -Complex, multivalue fields like Map and Array<struct> are updated using -<a href="https://javadoc.io/doc/com.yahoo.vespa/document/latest/com/yahoo/document/fieldpathupdate/AddFieldPathUpdate.html"> -AddFieldPathUpdate</a>, -<a href="https://javadoc.io/doc/com.yahoo.vespa/document/latest/com/yahoo/document/fieldpathupdate/AssignFieldPathUpdate.html"> -AssignFieldPathUpdate</a> and -<a href="https://javadoc.io/doc/com.yahoo.vespa/document/latest/com/yahoo/document/fieldpathupdate/RemoveFieldPathUpdate.html"> -RemoveFieldPathUpdate</a>. -<!-- ToDo: Rewrite --> -Field path updates are only supported on non-attribute -<a href="../reference/schemas/schemas.html#field">fields</a>, -<a href="../reference/schemas/schemas.html#index">index</a> fields, -or fields containing -<a href="../reference/schemas/schemas.html#struct-field">struct field</a> attributes. -If a field is both an index field and an attribute, then the document is updated in the document store, -the index is updated, but the attribute is not updated. -Thus, you can get old values in document summary requests and old values being used in ranking and grouping. -A <a href="../reference/schemas/document-field-path.html">field path</a> string identifies fields to update - example: -</p> -<pre> -upd.addFieldPathUpdate(new AssignFieldPathUpdate(type, "<span class="pre-hilite">myMap{key2}</span>", new StringFieldValue("abc"))); -</pre> -<p><em>FieldUpdate</em> examples:</p> <!-- ToDo: move this to a document processing sample app --> -<pre> -// Simple assignment -Field intField = type.getField("aInt"); -IntegerFieldValue intFieldValue = new IntegerFieldValue(2); -FieldUpdate assignUpdate = FieldUpdate.createAssign(intField, intFieldValue); -upd.addFieldUpdate(assignUpdate); - -// Arithmetic -FieldUpdate addUpdate = FieldUpdate.createIncrement(type.getField("aLong"), 3); -upd.addFieldUpdate(addUpdate); - -// Composite - add one array element -upd.addFieldUpdate(FieldUpdate.createAdd(type.getField("aArray"), - new IntegerFieldValue(13))); - -// Composite - add two array elements -upd.addFieldUpdate(FieldUpdate.createAddAll(type.getField("aArray"), - List.of(new IntegerFieldValue(14), new IntegerFieldValue(15)))); - -// Composite - add weightedset element -upd.addFieldUpdate(FieldUpdate.createAdd(type.getField("aWeightedset"), - new StringFieldValue("add_me"),101)); - -// Composite - add set to set -WeightedSet<StringFieldValue> wset = new WeightedSet<>(DataType.getWeightedSet(DataType.STRING)); -wset.put(new StringFieldValue("a1"), 3); -wset.put(new StringFieldValue("a2"), 4); -upd.addFieldUpdate(FieldUpdate.createAddAll(type.getField("aWeightedset"), wset)); - -// Composite - update array element -upd.addFieldUpdate(FieldUpdate.createMap(type.getField("aArray"), - new IntegerFieldValue(1), // array index - new AssignValueUpdate(new IntegerFieldValue(2)))); // value at index - -// Composite - increment weight -upd3.addFieldUpdate(FieldUpdate.createIncrement(type.getField("aWeightedset"), - new StringFieldValue("a1"), 1)); - -// Composite - add to set -upd.addFieldUpdate(FieldUpdate.createMap(type.getField("aWeightedset"), - new StringFieldValue("element1"), // value - new AssignValueUpdate(new IntegerFieldValue(30)))); -</pre> -<p><em>FieldPathUpdate</em> examples:</p> <!-- ToDo: move this to a document processing sample app --> -<pre> -// Add an element to a map -Array stringArray = new Array(DataType.getArray(DataType.STRING)); -stringArray.add(new StringFieldValue("my-val")); -AddFieldPathUpdate addElement = new AddFieldPathUpdate(type, "aMap{key1}", stringArray); -upd.addFieldPathUpdate(addElement); - -// Modify an element in a map -upd.addFieldPathUpdate(new AssignFieldPathUpdate(type, "aMap{key2}", new StringFieldValue("abc"))); -</pre> - - -<h3 id="update-reply-semantics">Update reply semantics</h3> -<p> -Sending in an update for which the system can not find a corresponding -document to update is <em>not</em> considered an error. -These are returned with a successful status code -(assuming that no actual error occurred during the update processing). Use -<a href="https://javadoc.io/doc/com.yahoo.vespa/documentapi/latest/com/yahoo/documentapi/UpdateResponse.html#wasFound()"> -UpdateDocumentReply.wasFound()</a> to check if the update was known to have been applied. -</p><p> -If the update returns with an error reply, the update <em>may or may not</em> have been applied, -depending on where in the platform stack the error occurred. -</p> - - - -<h2 id="document-access">Document Access</h2> -<p> -The starting point of for passing documents and updates to Vespa -is the <code>DocumentAccess</code> class. -This is a singleton (see <code>get()</code> method) session factory -(see <code>createXSession()</code> methods), -that provides three distinct access types: -</p> -<ul> - <li><strong>Synchronous random access</strong>: - provided by the class <code>SyncSession</code>. - Suitable for low-throughput proof-of-concept applications.</li> - <li><a href="#asyncsession"><strong>Asynchronous random access</strong></a>: - provided by the class <code>AsyncSession</code>. - It allows for document repository writes and random access with <strong>high throughput</strong>.</li> - <li><a href="#visitorsession"><strong>Visiting</strong></a>: - provided by the class <code>VisitorSession</code>. - Allows a set of documents to be accessed in order decided by the document repository, - which gives higher read throughput than random access.</li> -</ul> - - -<h3 id="asyncsession">AsyncSession</h3> -<p> -This class represents a session for asynchronous access to a document repository. -It is created by calling -<code>myDocumentAccess.createAsyncSession(myAsyncSessionParams)</code>, -and provides document repository writes and random access with high throughput. -The usage pattern for an asynchronous session is like: -</p> -<ol> -<li> - <code>put()</code>, <code>update()</code>, <code>get()</code> - or <code>remove()</code> is invoked on the session, - and it returns a synchronous <code>Result</code> object that indicates - whether the request was successful or not. - The <code>Result</code> object also contains a <em>request identifier</em>.</li> -<li> - The client polls the session for a <code>Response</code> through - its <code>getNext()</code> method. - Any operation accepted by an asynchronous session will produce - exactly one response within the configured timeout.</li> -<li> - Once a response is available, it is matched to the request by - inspecting the response's request identifier. - The response may also contain data, either a retrieved document or a failed document put - or update that needs to be handled.</li> -<li>Note that the client must process the response queue or your JVM will run into garbage collection issues, - as the underlying session keeps track of all responses - and unless they are consumed they will be kept alive and not be garbage collected.</li> -</ol> -<p>Example:</p> -<pre> -import com.yahoo.document.*; -import com.yahoo.documentapi.*; - -public class MyClient { - - // DocumentAccess is injectable in Vespa containers, but not in command line tools, etc. - private final DocumentAccess access = DocumentAccess.createForNonContainer(); - private final AsyncSession session = access.createAsyncSession(new AsyncParameters()); - private boolean abort = false; - private int numPending = 0; - - /** - * Implements application entry point. - * - * @param args Command line arguments. - */ - public static void main(String[] args) { - MyClient app = null; - try { - app = new MyClient(); - app.run(); - } catch (Exception e) { - e.printStackTrace(); - } finally { - if (app != null) { - app.shutdown(); - } - } - if (app == null || app.abort) { - System.exit(1); - } - } - - /** - * This is the main entry point of the client. This method will not return until all available documents - * have been fed and their responses have been returned, or something signaled an abort. - */ - public void run() { - System.out.println("client started"); - while (!abort) { - flushResponseQueue(); - - Document doc = getNextDocument(); - if (doc == null) { - System.out.println("no more documents to put"); - break; - } - System.out.println("sending doc " + doc); - - while (!abort) { - Result res = session.put(doc); - if (res.isSuccess()) { - System.out.println("put has request id " + res.getRequestId()); - ++numPending; - break; // step to next doc. - } else if (res.type() == Result.ResultType.TRANSIENT_ERROR) { - System.out.println("send queue full, waiting for some response"); - processNext(9999); - } else { - res.getError().printStackTrace(); - abort = true; // this is a fatal error - } - } - } - if (!abort) { - waitForPending(); - } - System.out.println("client stopped"); - } - - /** - * Shutdown the underlying api objects. - */ - public void shutdown() { - System.out.println("shutting down document api"); - session.destroy(); - access.shutdown(); - } - - /** - * Returns the next document to feed to Vespa. This method should only return null when the end of the - * document stream has been reached, as returning null terminates the client. This is the point at which - * your application logic should block if it knows more documents will eventually become available. - * - * @return The next document to put, or null to terminate. - */ - public Document getNextDocument() { - return null; // TODO: Implement at your discretion. - } - - /** - * Processes all immediately available responses. - */ - void flushResponseQueue() { - System.out.println("flushing response queue"); - while (processNext(0)) { - // empty - } - } - - /** - * Wait indefinitely for the responses of all sent operations to return. This method will only return - * early if the abort flag is set. - */ - void waitForPending() { - while (numPending != 0) { - if (abort) { - System.out.println("waiting aborted, " + numPending + " still pending"); - break; - } - System.out.println("waiting for " + numPending + " responses"); - processNext(9999); - } - } - - /** - * Retrieves and processes the next response available from the underlying asynchronous session. If no - * response becomes available within the given timeout, this method returns false. - * - * @param timeout The maximum number of seconds to wait for a response. - * @return True if a response was processed, false otherwise. - */ - boolean processNext(int timeout) { - Response res; - try { - res = session.getNext(timeout); - } catch (InterruptedException e) { - e.printStackTrace(); - abort = true; - return false; - } - if (res == null) { - return false; - } - System.out.println("got response for request id " + res.getRequestId()); - --numPending; - if (!res.isSuccess()) { - System.err.println(res.getTextMessage()); - abort = true; - return false; - } - return true; - } -} -</pre> - - -<h3 id="visitorsession">VisitorSession</h3> -<p> -This class represents a session for sequentially visiting documents with high throughput. -</p><p> -A visitor is started when creating the <code>VisitorSession</code> -through a call to <code>createVisitorSession</code>. -A visitor target, that is a receiver of visitor data, -can be created through a call to <code>createVisitorDestinationSession</code>. -The <code>VisitorSession</code> is a receiver of visitor data. -See <a href="visiting.html">visiting reference</a> for details. -The <code>VisitorSession</code>: -</p> -<ul> - <li>Controls the operation of the visiting process</li> - <li>Handles the data resulting from visiting data in the system</li> -</ul> -<p> -Those two different tasks may be set up to be handled by -a <code>VisitorControlHandler</code> and -a <code>VisitorDataHandler</code> respectively. -These handlers may be supplied to the <code>VisitorSession</code> in -the <code>VisitorParameters</code> object, -together with a set of other parameters for visiting. -Example: To increase performance, let more separate visitor destinations handle -visitor data, then specify the addresses to remote data handlers. -</p><p> -The default <code>VisitorDataHandler</code> used by -the <code>VisitorSession</code> returned from -<code>DocumentAccess</code> is <code>VisitorDataQueue</code> which -queues up incoming documents and implements a polling API. -The documents can be extracted by calls to the -session's <code>getNext()</code> methods and can be ack-ed by -the <code>ack()</code> method. -The default <code>VisitorControlHandler</code> can be accessed through the -session's <code>getProgress()</code>, -<code>isDone()</code>, and <code>waitUntilDone()</code> methods. -</p><p> -Implement custom <code>VisitorControlHandler</code> -and <code>VisitorDataHandler</code> by subclassing them and supplying -these to the <code>VisitorParameters</code> object. -</p><p> -The <code>VisitorParameters</code> object controls how and what data will be visited - -refer to the <a href="https://javadoc.io/doc/com.yahoo.vespa/documentapi/latest/com/yahoo/documentapi/VisitorParameters.html">javadoc</a>. -Configure the -<a href="../reference/writing/document-selector-language.html">document selection</a> string -to select what data to visit - the default is all data. -<!-- ToDo: This is probably not relevant anymore -Another important parameter is the "visitor library". -On the content side, all data read from a visitor is passed through a "visitor library", -referring to a dynamically linked library installed on all storage nodes (an .so file -located in <code>$VESPA_HOME/libexec/vespa/storage/</code>). This library -processes the data and sends all or part of it back to the client (or -generates other messages from the data). The default visitor library -is "dumpVisitor", which sends the data back as-is. In certain cases, -it can be interesting to visit only parts of documents. ---> -</p> - -<p> -You can specify what fields to return in a result by specifying a -<a href="https://javadoc.io/doc/com.yahoo.vespa/documentapi/latest/com/yahoo/documentapi/VisitorParameters.html">fieldSet</a> - -see <a href="../schemas/documents.html#fieldsets">document field sets</a>. Specifying only the fields you need may improve performance -a lot, especially if you can make do with only in-memory fields or if you have large fields you don't need returned. -</p> - -<p> -Example: -</p> <!-- ToDo: modify to JSON output --> -<pre> -import com.yahoo.document.Document; -import com.yahoo.document.DocumentId; -import com.yahoo.documentapi.DocumentAccess; -import com.yahoo.documentapi.DumpVisitorDataHandler; -import com.yahoo.documentapi.ProgressToken; -import com.yahoo.documentapi.VisitorControlHandler; -import com.yahoo.documentapi.VisitorParameters; -import com.yahoo.documentapi.VisitorSession; - -import java.util.concurrent.TimeoutException; - -public class MyClient { - - public static void main(String[] args) throws Exception { - VisitorParameters params = new VisitorParameters("true"); - params.setLocalDataHandler(new DumpVisitorDataHandler() { - - @Override - public void onDocument(Document doc, long timeStamp) { - System.out.print(doc.toXML("")); - } - - @Override - public void onRemove(DocumentId id) { - System.out.println("id=" + id); - } - }); - params.setControlHandler(new VisitorControlHandler() { - - @Override - public void onProgress(ProgressToken token) { - System.err.format("%.1f %% finished.\n", token.percentFinished()); - super.onProgress(token); - } - - @Override - public void onDone(CompletionCode code, String message) { - System.err.println("Completed visitation, code " + code + ": " + message); - super.onDone(code, message); - } - }); - params.setRoute(args.length > 0 ? args[0] : "[Storage:cluster=storage;clusterconfigid=storage]"); - params.setFieldSet(args.length > 1 ? args[1] : "[document]"); - - // DocumentAccess is injectable in Vespa containers, but not in command line tools, etc. - DocumentAccess access = DocumentAccess.createForNonContainer(); - VisitorSession session = access.createVisitorSession(params); - if (!session.waitUntilDone(0)) { - throw new TimeoutException(); - } - session.destroy(); - access.shutdown(); - } -} -</pre> -<p> -The first optional argument to this client is the <a href="document-routing.html">route</a> of the cluster to visit. -The second is the <a href="../schemas/documents.html#fieldsets">fieldset</a> set to retrieve. -</p> diff --git a/en/writing/document-api-guide.mdx b/en/writing/document-api-guide.mdx new file mode 100644 index 0000000000..4f02bdbac0 --- /dev/null +++ b/en/writing/document-api-guide.mdx @@ -0,0 +1,468 @@ +--- +title: "Document API" +--- + +This is an introduction to how to build and compile Vespa clients using the Document API. It can be used for feeding, updating and retrieving documents, or removing documents from the repository. See also the [Java reference](https://javadoc.io/doc/com.yahoo.vespa/documentapi). + +Use the [VESPA\_CONFIG\_SOURCES](/en/operations/self-managed/files-processes-and-ports#environment-variables) environment variable to set config servers to interface with. + +The most common use case is using the async API in a [document processor](/en/applications/document-processors) - from the sample apps: + +- Async GET in [LyricsDocumentProcessor.java](https://github.com/vespa-engine/sample-apps/blob/master/examples/document-processing/src/main/java/ai/vespa/example/album/LyricsDocumentProcessor.java) +- Async UPDATE in [ReviewProcessor.java](https://github.com/vespa-engine/sample-apps/blob/master/use-case-shopping/src/main/java/ai/vespa/example/shopping/ReviewProcessor.java) + +## Documents + +All data fed, indexed and searched in Vespa are instances of the `Document` class. A [document](/en/schemas/documents) is a composite object that consists of: + +- A `DocumentType` that defines the set of fields that can exist in a document. A document can only have a single *document type*, but document types can inherit the content of another. All fields of an inherited type is available in all its descendants. The document type is defined in the [schema](/en/reference/schemas/schemas), which is converted into a configuration file to be read by the `DocumentManager`. + All registered document types are instantiated and stored within the document manager. A reference to these objects can be retrieved using the `getDocumentType()` method by supplying the name and the version of the wanted document type. + `DocumentManager` initialization is done automatically by the Document API by subscribing to the appropriate configuration. +- A `DocumentId` which is a unique document identifier. The document distribution uses the document identifier, see the [reference](/en/content/buckets#distribution) for details. +- A set of `(Field, FieldValue)` pairs, or "fields" for short. The `Field` class has methods for getting its name, data type and internal identifier. The field object for a given field name can be retrieved using the `getField(<fieldname>)` method in the `DocumentType`. + +Use [DocumentAccess](https://javadoc.io/doc/com.yahoo.vespa/documentapi/latest/com/yahoo/documentapi/DocumentAccess.html) javadoc. Sample app: + +```js +<dependencies> + <dependency> + <groupId>com.yahoo.vespa</groupId> + <artifactId>documentapi</artifactId> + <version>8.689.26</version> {/* Find latest version at search.maven.org/search?q=g:com.yahoo.vespa%20a:documentapi */} + </dependency> +<dependencies> +``` + +```java expandable +import com.yahoo.document.DataType; +import com.yahoo.document.Document; +import com.yahoo.document.DocumentId; +import com.yahoo.document.DocumentPut; +import com.yahoo.document.DocumentType; +import com.yahoo.document.DocumentUpdate; +import com.yahoo.document.datatypes.StringFieldValue; +import com.yahoo.document.datatypes.WeightedSet; +import com.yahoo.document.update.FieldUpdate; +import com.yahoo.documentapi.DocumentAccess; +import com.yahoo.documentapi.SyncParameters; +import com.yahoo.documentapi.SyncSession; + +public class DocClient { + + public static void main(String[] args) { + + // DocumentAccess is injectable in Vespa containers, but not in command line tools, etc. + DocumentAccess access = DocumentAccess.createForNonContainer(); + DocumentType type = access.getDocumentTypeManager().getDocumentType("music"); + DocumentId id = new DocumentId("id:namespace:music::0"); + Document docIn = new Document(type, id); + SyncSession session = access.createSyncSession(new SyncParameters.Builder().build()); + + // Put document with a1,1 + WeightedSet<StringFieldValue> wset = new WeightedSet<>(DataType.getWeightedSet(DataType.STRING)); + wset.put(new StringFieldValue("a1"), 1); + docIn.setFieldValue("aWeightedset", wset); + DocumentPut put = new DocumentPut(docIn); + System.out.println(docIn.toJson()); + session.put(put); + + // Update document with a1,10 and a2,20 + DocumentUpdate upd1 = new DocumentUpdate(type, id); + WeightedSet<StringFieldValue> wset1 = new WeightedSet<>(DataType.getWeightedSet(DataType.STRING)); + wset1.put(new StringFieldValue("a1"), 10); + wset1.put(new StringFieldValue("a2"), 20); + upd1.addFieldUpdate(FieldUpdate.createAddAll(type.getField("aWeightedset"), wset1)); + System.out.println(upd1.toString()); + session.update(upd1); + + Document docOut = session.get(id); + System.out.println("document get:" + docOut.toJson()); + + session.destroy(); + access.shutdown(); + } +} +``` + +To test using the [sample apps](https://github.com/vespa-engine/sample-apps/tree/master/album-recommendation), enable more ports for client to connect to config server and other processes on localhost - change docker command: + +```js +$ docker run --detach --name vespa --hostname localhost --privileged \ + --volume $VESPA_SAMPLE_APPS:/vespa-sample-apps --publish 8080:8080 \ + --publish 19070:19070 --publish 19071:19071 --publish 19090:19090 --publish 19099:19099 --publish 19101:19101 --publish 19112:19112 \ + vespaengine/vespa +``` + + +## Fields + +Examples: + +```js expandable +doc.setFieldValue("aByte", (byte)1); +doc.setFieldValue("aInt", (int)1); +doc.setFieldValue("aLong", (long)1); +doc.setFieldValue("aFloat", 1.0); +doc.setFieldValue("aDouble", 1.0); +doc.setFieldValue("aBool", new BoolFieldValue(true)); +doc.setFieldValue("aString", "Hello Field!"); + +doc.setFieldValue("unknownField", "Will not see me!"); + +Array<IntegerFieldValue> intArray = new Array<>(doc.getField("aArray").getDataType()); +intArray.add(new IntegerFieldValue(11)); +intArray.add(new IntegerFieldValue(12)); +doc.setFieldValue("aArray", intArray); + +Struct pos = PositionDataType.valueOf(1,2); + pos = PositionDataType.fromString("N0.000002;E0.000001"); // two ways to set same value +doc.setFieldValue("aPosition", pos); + +doc.setFieldValue("aPredicate", new PredicateFieldValue("aLong in [10..20]")); + +byte[] rawBytes = new byte[100]; +for (int i = 0; i < rawBytes.length; i++) { + rawBytes[i] = (byte)i; +} +doc.setFieldValue("aRaw", new Raw(ByteBuffer.wrap(rawBytes))); + +Tensor tensor = Tensor.Builder.of(TensorType.fromSpec("tensor<float>>(x[2],y[2])")). + cell().label("x", 0).label("y", 0).value(1.0). + cell().label("x", 0).label("y", 1).value(2.0). + cell().label("x", 1).label("y", 0).value(3.0). + cell().label("x", 1).label("y", 1).value(5.0).build(); +doc.setFieldValue("aTensor", new TensorFieldValue(tensor)); + +MapFieldValue<StringFieldValue, StringFieldValue> map = new MapFieldValue<>(new MapDataType(DataType.STRING, DataType.STRING)); +map.put(new StringFieldValue("key1"), new StringFieldValue("foo")); +map.put(new StringFieldValue("key2"), new StringFieldValue("bar")); +doc.setFieldValue("aMap", map); + +WeightedSet<StringFieldValue> wset = new WeightedSet<>(DataType.getWeightedSet(DataType.STRING)); +wset.put(new StringFieldValue("strval 1"), 5); +wset.put(new StringFieldValue("strval 2"), 10); +doc.setFieldValue("aWeightedset", wset); +``` + +## Document updates + +A document update is a request to modify a document, see [reads and writes](/en/writing/reads-and-writes). + +Primitive, and some multivalue fields (WeightedSet and Array`<primitive>`), are updated using a [FieldUpdate](https://javadoc.io/doc/com.yahoo.vespa/document/latest/com/yahoo/document/update/FieldUpdate.html). + +Complex, multivalue fields like Map and Array`<struct>` are updated using [AddFieldPathUpdate](https://javadoc.io/doc/com.yahoo.vespa/document/latest/com/yahoo/document/fieldpathupdate/AddFieldPathUpdate.html), [AssignFieldPathUpdate](https://javadoc.io/doc/com.yahoo.vespa/document/latest/com/yahoo/document/fieldpathupdate/AssignFieldPathUpdate.html) and [RemoveFieldPathUpdate](https://javadoc.io/doc/com.yahoo.vespa/document/latest/com/yahoo/document/fieldpathupdate/RemoveFieldPathUpdate.html). Field path updates are only supported on non-attribute [fields](/en/reference/schemas/schemas#field), [index](/en/reference/schemas/schemas#index) fields, or fields containing [struct field](/en/reference/schemas/schemas#struct-field) attributes. If a field is both an index field and an attribute, then the document is updated in the document store, the index is updated, but the attribute is not updated. Thus, you can get old values in document summary requests and old values being used in ranking and grouping. A [field path](/en/reference/schemas/document-field-path) string identifies fields to update - example: + +```bash +upd.addFieldPathUpdate(new AssignFieldPathUpdate(type, "myMap{key2}", new StringFieldValue("abc"))); +``` + +*FieldUpdate* examples: + +```js expandable +// Simple assignment +Field intField = type.getField("aInt"); +IntegerFieldValue intFieldValue = new IntegerFieldValue(2); +FieldUpdate assignUpdate = FieldUpdate.createAssign(intField, intFieldValue); +upd.addFieldUpdate(assignUpdate); + +// Arithmetic +FieldUpdate addUpdate = FieldUpdate.createIncrement(type.getField("aLong"), 3); +upd.addFieldUpdate(addUpdate); + +// Composite - add one array element +upd.addFieldUpdate(FieldUpdate.createAdd(type.getField("aArray"), + new IntegerFieldValue(13))); + +// Composite - add two array elements +upd.addFieldUpdate(FieldUpdate.createAddAll(type.getField("aArray"), + List.of(new IntegerFieldValue(14), new IntegerFieldValue(15)))); + +// Composite - add weightedset element +upd.addFieldUpdate(FieldUpdate.createAdd(type.getField("aWeightedset"), + new StringFieldValue("add_me"),101)); + +// Composite - add set to set +WeightedSet<StringFieldValue> wset = new WeightedSet<>(DataType.getWeightedSet(DataType.STRING)); +wset.put(new StringFieldValue("a1"), 3); +wset.put(new StringFieldValue("a2"), 4); +upd.addFieldUpdate(FieldUpdate.createAddAll(type.getField("aWeightedset"), wset)); + +// Composite - update array element +upd.addFieldUpdate(FieldUpdate.createMap(type.getField("aArray"), + new IntegerFieldValue(1), // array index + new AssignValueUpdate(new IntegerFieldValue(2)))); // value at index + +// Composite - increment weight +upd3.addFieldUpdate(FieldUpdate.createIncrement(type.getField("aWeightedset"), + new StringFieldValue("a1"), 1)); + +// Composite - add to set +upd.addFieldUpdate(FieldUpdate.createMap(type.getField("aWeightedset"), + new StringFieldValue("element1"), // value + new AssignValueUpdate(new IntegerFieldValue(30)))); +``` + +*FieldPathUpdate* examples: + +```js +// Add an element to a map +Array stringArray = new Array(DataType.getArray(DataType.STRING)); +stringArray.add(new StringFieldValue("my-val")); +AddFieldPathUpdate addElement = new AddFieldPathUpdate(type, "aMap{key1}", stringArray); +upd.addFieldPathUpdate(addElement); + +// Modify an element in a map +upd.addFieldPathUpdate(new AssignFieldPathUpdate(type, "aMap{key2}", new StringFieldValue("abc"))); +```` + +### Update reply semantics + +Sending in an update for which the system can not find a corresponding document to update is *not* considered an error. These are returned with a successful status code (assuming that no actual error occurred during the update processing). Use [UpdateDocumentReply.wasFound()](https://javadoc.io/doc/com.yahoo.vespa/documentapi/latest/com/yahoo/documentapi/UpdateResponse.html#wasFound\(\)) to check if the update was known to have been applied. + +If the update returns with an error reply, the update *may or may not* have been applied, depending on where in the platform stack the error occurred. + +## Document Access + +The starting point of for passing documents and updates to Vespa is the `DocumentAccess` class. This is a singleton (see `get()` method) session factory (see `createXSession()` methods), that provides three distinct access types: + +- **Synchronous random access**: provided by the class `SyncSession`. Suitable for low-throughput proof-of-concept applications. +- [**Asynchronous random access**](#asyncsession): provided by the class `AsyncSession`. It allows for document repository writes and random access with **high throughput**. +- [**Visiting**](#visitorsession): provided by the class `VisitorSession`. Allows a set of documents to be accessed in order decided by the document repository, which gives higher read throughput than random access. + +### AsyncSession + +This class represents a session for asynchronous access to a document repository. It is created by calling `myDocumentAccess.createAsyncSession(myAsyncSessionParams)`, and provides document repository writes and random access with high throughput. The usage pattern for an asynchronous session is like: + +1. `put()`, `update()`, `get()` or `remove()` is invoked on the session, and it returns a synchronous `Result` object that indicates whether the request was successful or not. The `Result` object also contains a *request identifier*. +2. The client polls the session for a `Response` through its `getNext()` method. Any operation accepted by an asynchronous session will produce exactly one response within the configured timeout. +3. Once a response is available, it is matched to the request by inspecting the response's request identifier. The response may also contain data, either a retrieved document or a failed document put or update that needs to be handled. +4. Note that the client must process the response queue or your JVM will run into garbage collection issues, as the underlying session keeps track of all responses and unless they are consumed they will be kept alive and not be garbage collected. + +Example: + +```java expandable +import com.yahoo.document.*; +import com.yahoo.documentapi.*; + +public class MyClient { + + // DocumentAccess is injectable in Vespa containers, but not in command line tools, etc. + private final DocumentAccess access = DocumentAccess.createForNonContainer(); + private final AsyncSession session = access.createAsyncSession(new AsyncParameters()); + private boolean abort = false; + private int numPending = 0; + + /** + * Implements application entry point. + * + * @param args Command line arguments. + */ + public static void main(String[] args) { + MyClient app = null; + try { + app = new MyClient(); + app.run(); + } catch (Exception e) { + e.printStackTrace(); + } finally { + if (app != null) { + app.shutdown(); + } + } + if (app == null || app.abort) { + System.exit(1); + } + } + + /** + * This is the main entry point of the client. This method will not return until all available documents + * have been fed and their responses have been returned, or something signaled an abort. + */ + public void run() { + System.out.println("client started"); + while (!abort) { + flushResponseQueue(); + + Document doc = getNextDocument(); + if (doc == null) { + System.out.println("no more documents to put"); + break; + } + System.out.println("sending doc " + doc); + + while (!abort) { + Result res = session.put(doc); + if (res.isSuccess()) { + System.out.println("put has request id " + res.getRequestId()); + ++numPending; + break; // step to next doc. + } else if (res.type() == Result.ResultType.TRANSIENT_ERROR) { + System.out.println("send queue full, waiting for some response"); + processNext(9999); + } else { + res.getError().printStackTrace(); + abort = true; // this is a fatal error + } + } + } + if (!abort) { + waitForPending(); + } + System.out.println("client stopped"); + } + + /** + * Shutdown the underlying api objects. + */ + public void shutdown() { + System.out.println("shutting down document api"); + session.destroy(); + access.shutdown(); + } + + /** + * Returns the next document to feed to Vespa. This method should only return null when the end of the + * document stream has been reached, as returning null terminates the client. This is the point at which + * your application logic should block if it knows more documents will eventually become available. + * + * @return The next document to put, or null to terminate. + */ + public Document getNextDocument() { + return null; // TODO: Implement at your discretion. + } + + /** + * Processes all immediately available responses. + */ + void flushResponseQueue() { + System.out.println("flushing response queue"); + while (processNext(0)) { + // empty + } + } + + /** + * Wait indefinitely for the responses of all sent operations to return. This method will only return + * early if the abort flag is set. + */ + void waitForPending() { + while (numPending != 0) { + if (abort) { + System.out.println("waiting aborted, " + numPending + " still pending"); + break; + } + System.out.println("waiting for " + numPending + " responses"); + processNext(9999); + } + } + + /** + * Retrieves and processes the next response available from the underlying asynchronous session. If no + * response becomes available within the given timeout, this method returns false. + * + * @param timeout The maximum number of seconds to wait for a response. + * @return True if a response was processed, false otherwise. + */ + boolean processNext(int timeout) { + Response res; + try { + res = session.getNext(timeout); + } catch (InterruptedException e) { + e.printStackTrace(); + abort = true; + return false; + } + if (res == null) { + return false; + } + System.out.println("got response for request id " + res.getRequestId()); + --numPending; + if (!res.isSuccess()) { + System.err.println(res.getTextMessage()); + abort = true; + return false; + } + return true; + } +} +``` + +### VisitorSession + +This class represents a session for sequentially visiting documents with high throughput. + +A visitor is started when creating the `VisitorSession` through a call to `createVisitorSession`. A visitor target, that is a receiver of visitor data, can be created through a call to `createVisitorDestinationSession`. The `VisitorSession` is a receiver of visitor data. See [visiting reference](/en/writing/visiting) for details. The `VisitorSession`: + +- Controls the operation of the visiting process +- Handles the data resulting from visiting data in the system + +Those two different tasks may be set up to be handled by a `VisitorControlHandler` and a `VisitorDataHandler` respectively. These handlers may be supplied to the `VisitorSession` in the `VisitorParameters` object, together with a set of other parameters for visiting. Example: To increase performance, let more separate visitor destinations handle visitor data, then specify the addresses to remote data handlers. + +The default `VisitorDataHandler` used by the `VisitorSession` returned from `DocumentAccess` is `VisitorDataQueue` which queues up incoming documents and implements a polling API. The documents can be extracted by calls to the session's `getNext()` methods and can be ack-ed by the `ack()` method. The default `VisitorControlHandler` can be accessed through the session's `getProgress()`, `isDone()`, and `waitUntilDone()` methods. + +Implement custom `VisitorControlHandler` and `VisitorDataHandler` by subclassing them and supplying these to the `VisitorParameters` object. + +The `VisitorParameters` object controls how and what data will be visited - refer to the [javadoc](https://javadoc.io/doc/com.yahoo.vespa/documentapi/latest/com/yahoo/documentapi/VisitorParameters.html). Configure the [document selection](/en/reference/writing/document-selector-language) string to select what data to visit - the default is all data. + +You can specify what fields to return in a result by specifying a [fieldSet](https://javadoc.io/doc/com.yahoo.vespa/documentapi/latest/com/yahoo/documentapi/VisitorParameters.html) - see [document field sets](/en/schemas/documents#fieldsets). Specifying only the fields you need may improve performance a lot, especially if you can make do with only in-memory fields or if you have large fields you don't need returned. + +Example: + +```java expandable +import com.yahoo.document.Document; +import com.yahoo.document.DocumentId; +import com.yahoo.documentapi.DocumentAccess; +import com.yahoo.documentapi.DumpVisitorDataHandler; +import com.yahoo.documentapi.ProgressToken; +import com.yahoo.documentapi.VisitorControlHandler; +import com.yahoo.documentapi.VisitorParameters; +import com.yahoo.documentapi.VisitorSession; + +import java.util.concurrent.TimeoutException; + +public class MyClient { + + public static void main(String[] args) throws Exception { + VisitorParameters params = new VisitorParameters("true"); + params.setLocalDataHandler(new DumpVisitorDataHandler() { + + @Override + public void onDocument(Document doc, long timeStamp) { + System.out.print(doc.toXML("")); + } + + @Override + public void onRemove(DocumentId id) { + System.out.println("id=" + id); + } + }); + params.setControlHandler(new VisitorControlHandler() { + + @Override + public void onProgress(ProgressToken token) { + System.err.format("%.1f %% finished.\n", token.percentFinished()); + super.onProgress(token); + } + + @Override + public void onDone(CompletionCode code, String message) { + System.err.println("Completed visitation, code " + code + ": " + message); + super.onDone(code, message); + } + }); + params.setRoute(args.length > 0 ? args[0] : "[Storage:cluster=storage;clusterconfigid=storage]"); + params.setFieldSet(args.length > 1 ? args[1] : "[document]"); + + // DocumentAccess is injectable in Vespa containers, but not in command line tools, etc. + DocumentAccess access = DocumentAccess.createForNonContainer(); + VisitorSession session = access.createVisitorSession(params); + if (!session.waitUntilDone(0)) { + throw new TimeoutException(); + } + session.destroy(); + access.shutdown(); + } +} +``` + +The first optional argument to this client is the [route](/en/writing/document-routing) of the cluster to visit. The second is the [fieldset](/en/schemas/documents#fieldsets) set to retrieve. \ No newline at end of file diff --git a/en/writing/document-routing.html b/en/writing/document-routing.html deleted file mode 100644 index c5804d9e2d..0000000000 --- a/en/writing/document-routing.html +++ /dev/null @@ -1,1441 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Routing" -applies_to: self-managed -redirect_from: -- /en/routing.html -- /en/reference/services-routing.html -- /en/reference/routingpolicies.html -- /en/operations-selfhosted/routingpolicies.html -- /en/operations-selfhosted/routing.html ---- - -<p> -<em>Routing</em> is used to configure the paths that documents and updates written -to Vespa take through the system. Vespa will automatically set up a routing -configuration which is appropriate for most cases, so no explicit routing -configuration is necessary. However, explicit routing can be used in advanced use -cases such as sending different document streams to different document processing -clusters, or through multiple consecutive clusters etc. -</p> -<p>There are other, more in-depth, articles on routing:</p> -<ul> - <li> - Use <a href="../reference/operations/self-managed/tools.html#vespa-route">vespa-route</a> - to inspect routes and services of a Vespa application, like in the - <a href="#example-reconfigure-the-default-route">example</a> - </li><li> - <a href="#routing-policies-reference">Routing policies reference</a>. - See the <a href="#routing-policies">routing policies</a> note - for complex routes and default routing - </li> -</ul> -<p> -In Vespa, there is a transport layer and a programming interface that -are available to clients that wish to communicate with a Vespa application. -The transport layer is <em>Message Bus</em>. -<a href="document-api-guide.html">Document API</a> is -implemented on top of Message Bus. -Configuring the interface therefore exposes some features available in Message Bus. -Refer to the <a href="../reference/api/api.html">Vespa APIs and interfaces</a> -for clients using the <em>Document API</em>. -The atoms in Vespa routing are <em>routes</em> and <em>hops</em>. -</p> -<p> - <a href="https://github.com/vespa-engine/sample-apps/tree/master/examples/document-processing"> - document-processing</a> is an example of custom document processing, and useful for testing routing. -</p> - - - -<h2 id="a-route-is-a-sequence-of-hops">A route is a sequence of hops</h2> -<p> -The sequence of hosts, routers, bridges, gateways, and other devices -that network traffic takes, or could take, from its source to its -destination is what is classically termed a <em>route</em>. -As a verb, <em>to route</em> means to determine the link down which to send a packet, -that will minimize its total journey time according to some routing algorithm. -</p><p> -In Vespa, a route is simply a sequence of named hops. -Instead of leaving selection logic to a route, -the responsibility of resolving recipients is given to -the <a href="#a-hop-is-a-point-to-point-transmission">hops</a>' <a href="#selection-logic">selectors</a>. -A hop can do more or less whatever it wants -to change a message's journey through your application; -it can slightly alter itself by choosing among some predefined recipients, -it can change itself completely by either rewriting or looking up another hop, -or it can even modify the entire route from that branch onwards. -In effect, a route can end up branching at several points along its path, -resulting in complex routes. -As the figure suggests, Message Bus supports -both <a href="https://en.wikipedia.org/wiki/Unicast">unicasting</a> -and <a href="https://en.wikipedia.org/wiki/Multicast">multicasting</a> - -Message Bus allows for arbitrarily complex routes. -Each node in the above graph represents a Vespa service: -</p> -<img src="/assets/img/routing.svg" width="618px" height="auto" alt="Illustration of routes with hops"/> - - - -<h2 id="a-hop-is-a-point-to-point-transmission">A hop is a point-to-point transmission</h2> -<p> -In telecommunication, a <em>hop</em> is one step, from one router to the next, -on the path of a packet on an Internet Protocol network. -It is a direct host-to-host connection forming part of the route between -two hosts in a routed network such as the Internet. -In more general terms, a hop is a point-to-point transmission in a series -required to get a message from point A to point B. -</p><p> -With Message Bus the concept of hops was introduced as the smallest -steps of the transmission of a message. -A hop consists of a <em>name</em> that is used by the messaging clients to select it, -a list of <em>recipient</em> services that it may transmit to, -and a <em>selector</em> that is used to select among those recipients. -Unlike traditional hops, in Vespa a hop is a transmission from one sender to many recipients. -</p><p> -Well, the above is only partially true; -it is the easiest way to understand the hop concept. -In fact, a hop's recipient list is nothing more than a configured list of strings -that is made available to all <a href="#routing-policies">routing policies</a> -that are named in the selector string. -See <a href="#selection-logic">selection logic</a> below for details. -</p><p> -A hop's recipient is the service name of a Message Bus client that has -been registered in Vespa's service location broker (vespa-slobrok). -These names are well-defined once their derivation logic is understood; -they are "/"-separated sets of address-components whose values -are given by a service's role in the application. -An example of a recipient is: -<pre> -search/cluster.foo/*/feed-destination -</pre> -<p> -The marked components of the above recipient, <code>/search/cluster.foo/*</code>, -resolves to a host's symbolic name. -This is the name with which a Message Bus instance was configured. -The unmarked component, <code>feed-destination</code>, -is the local name of the running service that the hop transmits to, -i.e. the name of the <em>session</em> created on the running Message Bus instance. -</p><p> -The Active Configuration page in Vespa's administration interface -gives an insight into what symbolic names exist for any given -application by looking at its current configuration subscriptions. -All available Message Bus services use their <code>ConfigId</code> as their host's symbolic name. -See <a href="../reference/operations/self-managed/tools.html#vespa-route">vespa-route</a> for how to inspect this, -or use the <a href="../reference/api/config-v2.html">config API</a>. -</p><p> -A hop can be prefixed using the special character "?" to force it to behave as if its -<a href="#hop">ignore-result</a> attribute was configured to "true". -</p> - - -<h3 id="asterisk">Asterisk</h3> -<p> -A service identifier may include the special character "*" as an address component. -A recipient that contains this character -is a request for the network to choose <em>any one</em> service that matches it. -</p> - - - -<h2 id="routing-policies">Routing policies</h2> -<p> -A routing policy is a protocol-specific algorithm that chooses among a -list of candidate recipients for a single address component - -see <a href="#a-hop-is-a-point-to-point-transmission">hop description</a> above. -These policies are designed and implemented as key parts of a Message Bus protocol. -E.g. for the "Document" protocol these are what make up the routing behavior for document transmission. -Without policies, a hop would only be able to match verbatim to a recipient, -and thus the only advanced selection logic would be that of the <a href="#asterisk">asterisk</a>. -</p><p> -In addition to implementing a selection algorithm, -a routing policy must also implement a merging algorithm that combines the replies -returned from each selected recipient into a single sensible reply. -This is needed because a client does not necessarily know -whether a message has been sent to one or multiple recipients, -and <strong>Message Bus guarantees a single reply for every message</strong>. -</p><p> -More formally, a routing policy is an arbitrarily large (or small), -named, stand-alone piece of code registered with a Message Bus protocol. -As discussed <a href="#selection-logic">above</a>, -an instance of a policy is run both when resolving a route to recipients, -and when merging replies. -The policy is passed a <code>RoutingContext</code> object -that pretty much allows it to do whatever it pleases to the route and replies. -The same policy object and the same context object is used for both selection and merging. -</p><p> -Refer to the <a href="#routing-policies-reference">routing policy reference</a>. -</p> - - - -<h2 id="selection-logic">Selection logic</h2> -<p> -When Message Bus is about to route a message, at the last possible time, -it inspects the <strong>first</strong> hop of the message's route to resolve a set of recipients. -First, all of its <a href="#1-resolve-policy-directives">policies are resolved</a>. -Second, the output service name is matched to the routing table to see if it maps to another hop or route. -Finally, the message is <a href="#3-send-to-services">sent</a> to all chosen recipient services. -Because each policy can select multiple recipients, -this can give rise to an arbitrarily complex routing tree. -There are, of course, safeguards within Message Bus to prevent infinite recursions -due to circular dependencies or misconfiguration. -</p> -{% include note.html content="It <strong>is</strong> possible to develop a different protocol with other -policies to run in the application, -but since all of Vespa's component only support the \"Document\" protocol, -it makes little sense to do so."%} - - -<h3 id="1-resolve-policy-directives">1. Resolve Policy Directives</h3> -<p> -The logic run at this step is actually simple; -as long as the hop string contains a policy directive, -i.e. some arbitrary string enclosed in square brackets, -Message Bus will create and run an instance of that policy for the protocol of the message being routed. -<pre> -Name: storage/cluster.backup -Selector: storage/cluster.backup/distributor/[Distributor]/default -Recipients: - -</pre> -The above hop is probably the simplest hop you will encounter in Vespa; -it has a single policy directive contained in a string -that closely resembles service names discussed above, and it has no recipients. -When resolving this hop, -Message Bus creates an instance of the "DocumentRouteSelector" policy -and invokes its <code>select()</code> method. -The "Distributor" policy will replace its own directive -with a proper distributor identifier, -yielding a hop string that is now an unambiguous service identifier. -<pre> -Name: indexing -Selector: [DocumentRouteSelector] -Recipients: search/cluster.music - search/cluster.books -</pre> -This hop has a selector which is nothing more than a single policy directive, -"[DocumentRouteSelector]", and it has two configured recipients, -"search/cluster.music" and "search/cluster.books". -This policy expands the hop to zero, one or two <strong>new</strong> routes -by replacing its own directive with the content of the recipient routes. -Each of these routes may have one or more hops themselves. -In turn, these will be processed independently. -When replies are available from all chosen recipients, -the policy's <code>merge()</code> method is invoked, -and the resulting reply is passed upwards. -<pre> -Name: default -Selector: [AND:indexing storage/cluster.backup] -Recipients: - -</pre> -<p> -This hop has a selector but no recipients. -The reason for this is best explained in -the <a href="#routing-policies-reference">routing policies reference</a>, -but it serves as an example of a hop that has no configured recipients. -Notice how the policy directive contains a colon (":") -which denotes that the remainder of the directive is a parameter to the policy constructor. -This policy replaces the whole route of the message -with the set of routes named in the parameter string. -</p><p> -What routing policies are available depends on what protocol is currently running. -As of this version the only supported protocol is "Document". -This offers a set of routing policies discussed -<a href="#routing-policies-reference">below</a>. -</p> - - -<h3 id="2-resolve-hop-and-route-names">2. Resolve Hop- and Route names</h3> -<p> -As soon as all policy directives have been resolved, -Message Bus makes sure that the resulting string is, -in fact, a service name and not the name of another hop or route (in that order) -configured for the running protocol. The outcome is either: -</p> -<ol> - <li>The string is recognized as a hop name - The current hop - is replaced by the named one, and processing returns - to <a href="#1-resolve-policy-directives">step 1</a>.</li> - <li>The string is recognized as a route name - The current - route, including all the hops following this, is replaced by the named one. - Processing returns to <a href="#1-resolve-policy-directives">step 1</a>.</li> - <li>The string is accepted as a service name - This terminates - the current branch of the routing tree. If all branches are terminated, - processing proceeds to <a href="#3-send-to-services">step 3</a>.</li> -</ol> -<p> -Because hop names are checked before route names, -Message Bus also supports a "route:" prefix that forces the remainder of the string -to resolve to a configured route or fail. -</p> - - -<h3 id="3-send-to-services">3. Send to Services</h3> -<p> -When the route resolver reaches this point, -the first hop of the message being sent has been resolved to an arbitrarily complex routing tree. -Each leaf of this tree represents a service that is to receive the message, -unless some policy has already generated a reply for it. -No matter how many recipients are chosen, the message is serialized only once, -and the network transmission is able to share the same chunk of memory between all recipients. -</p><p> -As replies to the message arrive at the sender they are handed over to -the corresponding leaf nodes of the routing tree, -but merging will not commence until all leaf nodes are ready. -</p><p> -Route resolving happens just before network transmission, after all resending logic. -This means that if the route configuration changes while there are messages scheduled for resending, -these will adhere to the new routes. -</p><p> -If the resolution of a recipient passed through a hop that was configured -to <a href="#hop">ignore results</a>, -the network layer will reply immediately with a synthetic "OK". -</p> - - - -<h2 id="example-reconfigure-the-default-route">Example: Reconfigure the default route</h2> -<p> -Assume that the application requires both search and storage capabilities, -but that the default feed should only pass through to search. -An imaginary scenario for this would be a system where there -is a continuous feed being passed into Vespa with no filtering on spam. -You would like a minimal storage-only cluster that stores a URL blocklist -that can be used by a custom document processor to block incoming documents from offending sites. -</p><p> -Apart from the blocklist and the document processor, add the following: -<!-- ToDo: does the docproc name still work? --> -<pre> -<routing version="1.0"> - <routingtable protocol="document"> - <route name="default" hops="docproc/cluster.blocklist/*/chain.blocklist indexing" /> - </routingtable> -</routing> -</pre> -<p> -This overrides the default route to pass through any available -blocklisting document processor before being indexed. -If the document processor decides to block a message, -it must respond with an appropriate <em>ok</em> reply, -or your client software needs to accept whatever error reply you decide to return when blocking. -</p><p> -When feeding blocklist information to storage, -your application need only use the already available <code>storage</code> hop. -</p> -<p> - See <a href="https://github.com/vespa-engine/vespa/issues/13193">#13193</a> - for a discussion on using <em>default</em> as a name. -</p> - - -<h3 id="the-document-api">The Document API</h3> -<p> -With the current implementation of Document API running on Message bus, -the configuration of the API implies configuration of the latter. -Most clients will only ever route through this API. -To use the Document API, you need to instantiate a class -that implements the <code>DocumentAccess</code> interface. -At the time of writing only <code>MessageBusDocumentAccess</code> exists, -and it requires a parameter set for creation. -These parameters are contained in an instance of <code>MessageBusDocumentAccessParam</code> -that looks somewhat like the following: -<pre> -class MessageBusDocumentAccessParams { - String documentManagerConfigId; // The id to resolve to document manager config. - String oosServerPattern; // The service pattern to resolve to fleet controller - // services. - String appConfigId; // The id to resolve to application config. - String slobrokConfigId; // The id to resolve to slobrok config. - String routingConfigId; // The id to resolve to messagebus routing config. -<span class="pre-hilite"> - String routeName; // The name of the route to send to. - int traceLevel; // The trace level to use when sending. -</span> - class SourceSessionParams {<span class="pre-hilite"> - int maxPending; // Maximum number of pending messages. - int maxPendingSize; // Maximum size of pending messages. - double timeout; // Default timeout in seconds for messages - // that have no timeout set. - double requestTimeoutA; // Default request timeout in seconds, using - double requestTimeoutB; // the equation 'requestTimeout = a * retry + b'. - double retryDelay; // Number of seconds to wait before resending.</span> - }; -} -</pre> -<p> -The most obvious configuration parameter is <code>routeName</code>, -which informs the <code>MessageBusDocumentAccess</code> object the -name of the route to use when sending documents and updates. -The second parameter is <code>traceLevel</code>, -which allows a client to see exactly how the data was transmitted. -</p> -{% include note.html content="Tracing can be enabled on a level from 1-9, -where a higher number means more tracing. -Because the concept of tracing is not exposed by the Document API itself, -its data will simply be printed to standard output when a reply arrives for the sender. -This should therefore not be used in production, -but can be helpful when debugging."%} -<p> -Refer to the <a href="https://javadoc.io/doc/com.yahoo.vespa/documentapi">Document API JavaDoc</a>. -</p> - - - - -<h2 id="routing-services">Routing services</h2> -<p> - This is the reference documentation for all elements in - the <em>routing</em> section of <a href="../reference/applications/services/services.html">services.xml</a>. -</p> -<pre class="pre-config"> -<a href="#routing">routing [version]</a> - <a href="#routingtable">routingtable [protocol, verify]</a> - <a href="#route">route [name, hops]</a> - <a href="#hop">hop [name, selector, ignore-result]</a> - <a href="#recipient">recipient [session]</a> - <a href="#services">services [protocol]</a> - <a href="#service">service [name]</a> -</pre> - - - -<h2 id="routing">routing</h2> -<p> - Contained in <a href="../reference/applications/services/services.html#services">services</a>. - The container element for all configuration related to routing. -</p> -<table class="table"> - <thead> - <tr><th>Attribute</th><th>Required</th><th>Value</th><th>Default</th><th>Description</th></tr> - </thead><tbody> -<tr><th>version</th> - <td>required</td> - <td>number</td> - <td></td> - <td>Must be set to "1.0" in this Vespa-version</td></tr> -</tbody> -</table> -<p>Optional subelements:</p> -<ul> - <li><a href="#routingtable">routingtable</a></li> - <li><a href="#services">services</a></li> -</ul> -<p>Example:</p> -<pre> -<routing version="1.0"> - <routingtable protocol="document"> - <route name="route1" hops="hop1 hop2" /> - <route name="route2" hops="hop3 hop4 hop5" /> - <hop name="hop1" selector="docproc/cluster.foo/docproc/*/feed-processor"> - <recipient session="docproc/cluster.foo/docproc/*/feed-processor" /> - </hop> - <hop name="hop2" selector="search/cluster.bar/[SearchGroup]/[SearchRow]/[SearchColumn]/feed-destination"> - <recipient session="search/cluster.bar/g0/c0/r0/feed-destination" /> - <recipient session="search/cluster.bar/g0/c1/r0/feed-destination" /> - <recipient session="search/cluster.bar/g0/c0/r1/feed-destination" /> - <recipient session="search/cluster.bar/g0/c1/r1/feed-destination" /> - <recipient session="search/cluster.bar/g1/c0/r0/feed-destination" /> - <recipient session="search/cluster.bar/g1/c1/r0/feed-destination" /> - <recipient session="search/cluster.bar/g1/c0/r1/feed-destination" /> - <recipient session="search/cluster.bar/g1/c1/r1/feed-destination" /> - </hop> - </routingtable> - <services protocol="document"> - <service name="foo/bar" /> - </services> -</routing> -</pre> - - - -<h2 id="routingtable">routingtable</h2> -<p> - Contained in <a href="#routing">routing</a>. - Specifies a routing table for a specific protocol. -</p> -<table class="table"> - <thead> - <tr><th>Attribute</th><th>Required</th><th>Value</th><th>Default</th><th>Description</th></tr> - </thead><tbody> -<tr><th>protocol</th> - <td>required</td> - <td></td> - <td></td> - <td>Configure which protocol to use. - Only the protocol <em>document</em> is defined, - so if you define a routing table for an unsupported protocol, - the application will just log an INFO entry that contains the name of that protocol.</td></tr> -<tr><th>verify</th> - <td>optional</td> - <td>boolean</td> - <td></td> - <td>ToDo: document this</td></tr> -</tbody> -</table> -<p>Optional subelements:</p> -<ul> - <li><a href="#route">route</a></li> - <li><a href="#hop">hop</a></li> -</ul> -<p>Example:</p> -<pre> -<routing version="1.0"> - <routingtable protocol="document"> - <route name="route1" hops="hop1 hop2" /> - <hop name="hop1" selector="docproc/cluster.foo/docproc/*/feed-processor"> - <recipient session="docproc/cluster.foo/docproc/*/feed-processor" /> - </hop> - </routingtable> -</routing> -</pre> - - - -<h2 id="route">route</h2> -<p> - Contained in <a href="#routingtable">routingtable</a>. - Specifies a route for a message to its destination through a set of intermediate hops. - If at least one hop in a route does not exist, - the application will fail to start and issue an error that contains the name of that hop. -</p> -<table class="table"> - <thead> - <tr><th>Attribute</th><th>Required</th><th>Value</th><th>Default</th><th>Description</th></tr> - </thead><tbody> -<tr><th>name</th> - <td>required</td> - <td></td> - <td></td> - <td>Route name.</td></tr> -<tr><th>hops</th> - <td>required</td> - <td></td> - <td></td> - <td>A whitespace-separated list of hop names, where each name must be a valid hop.</td></tr> -</tbody> -</table> -<p>Subelements: none</p> -<p>Example:</p> -<pre> -<routing version="1.0"> - <routingtable protocol="document"> - <route name="route1" hops="hop1 hop2" /> - <route name="route2" hops="hop3 hop4 hop5" /> - </routingtable> -</routing> -</pre> - - - -<h2 id="hop">hop</h2> -<p> - Contained in <a href="#routingtable">routingtable</a>. - Specifies a single hop that can be used to construct one or more routes. - A hop must have a name that is unique within the routing table to which it belongs. - A hop contains a selector string and a list of recipient sessions. -</p> -<table class="table"> - <thead> - <tr><th>Attribute</th><th>Required</th><th>Value</th><th>Default</th><th>Description</th></tr> - </thead><tbody> -<tr><th>name</th> - <td>required</td> - <td></td> - <td></td> - <td>Hop name.</td></tr> -<tr><th>selector</th> - <td>required</td> - <td></td> - <td></td> - <td>Selector string.</td></tr> -<tr><th>ignore-result</th> - <td>optional</td> - <td></td> - <td></td> - <td>If set to <em>true</em>, specifies that the result of routing through that hop should be ignored.</td></tr> -</tbody> -</table> -<p>Optional subelements:</p> -<ul> - <li><a href="#recipient">recipient</a></li> -</ul> -<p>Example:</p> -<pre> -<routing version="1.0"> - <routingtable protocol="document"> - <hop name="hop1" selector="docproc/cluster.foo/docproc/*/feed-processor"> - <recipient session="docproc/cluster.foo/docproc/*/feed-processor" /> - </hop> - </routingtable> -</routing> -</pre> - - - -<h2 id="recipient">recipient</h2> -<p> - Contained in <a href="#hop">hop</a>. - Specifies a recipient session of a hop. -</p> -<table class="table"> - <thead> - <tr><th>Attribute</th><th>Required</th><th>Value</th><th>Default</th><th>Description</th></tr> - </thead><tbody> -<tr><th>session</th> - <td>required</td> - <td></td> - <td></td> - <td>This attribute must correspond to a running instance of a - service that can be routed to. - All session identifiers consist of a location part and a - name. A search node is always given a session name on the form - <em>search/cluster.name/g#/r#/c#/feed-destination</em>, - whereas a document processor service is always named - <em>docproc/cluster.name/docproc/#/feed-processor</em>.</td></tr> -</tbody> -</table> -<p>Subelements: none</p> -<p>Example:</p> -<pre> -<routing version="1.0"> - <routingtable protocol="document"> - <hop name="search/cluster.music" selector="search/cluster.music/[SearchGroup]/[SearchRow]/[SearchColumn]/feed-destination"> - <recipient session="search/cluster.music/g0/c0/r0/feed-destination" /> - <recipient session="search/cluster.music/g0/c0/r1/feed-destination" /> - <recipient session="search/cluster.music/g1/c0/r0/feed-destination" /> - <recipient session="search/cluster.music/g1/c0/r1/feed-destination" /> - </hop> - </routingtable> -</routing> -</pre> - - - -<h2 id="services">services</h2> -<p> - Contained in <a href="#routing">routing</a>. - Specifies a set of services available for a specific protocol. - At the moment the only supported protocol is <em>document</em>. - The services specified are used by the route verification step - to allow hops and routes to reference services known to exist, - but that can not be derived from <em>services.xml</em>. -</p> -<table class="table"> - <thead> - <tr><th>Attribute</th><th>Required</th><th>Value</th><th>Default</th><th>Description</th></tr> - </thead><tbody> -<tr><th>protocol</th> - <td>required</td> - <td></td> - <td></td> - <td>Configure which protocol to use. Only the protocol <em>document</em> is defined.</td></tr> -</tbody> -</table> -<p>Optional subelements:</p> -<ul> - <li><a href="#service">service</a></li> -</ul> -<p>Example:</p> -<pre> -<routing version="1.0"> - <services protocol="document"> - <service name="foo/bar" /> - </services> -</routing> -</pre> - - - -<h2 id="service">service</h2> -<p> - Contained in <a href="#services">services</a>. - Specifies a single known service that can not be derived from the <em>services.xml</em>. -</p> -<table class="table"> - <thead> - <tr><th>Attribute</th><th>Required</th><th>Value</th><th>Default</th><th>Description</th></tr> - </thead><tbody> -<tr><th>name</th> - <td>required</td> - <td></td> - <td></td> - <td>The name of the service.</td></tr> -</tbody> -</table> -<p>Subelements: none</p> -<p>Example:</p> -<pre> -<routing version="1.0"> - <services protocol="document"> - <service name="foo/bar" /> - </services> -</routing> -</pre> - - - - -<h2 id="routingpolicies-reference">Routingpolicies reference</h2> -<p> - This article contains detailed descriptions of the behaviour of all routing policies available in Vespa. -</p><p> - The <em>Document protocol</em> is currently the only Message Bus protocol supported by Vespa. - Furthermore, all routing policies that are part of this protocol - share a common code path for <a href="#merge">merging replies</a>. - The policies offered by the protocol are: -</p> -<ul> - <li><a href="#and">AND</a> - Selects all configured recipient hops.</li> - <li> - <a href="#documentrouteselector">DocumentRouteSelector</a> - Uses a <a - href="../reference/writing/document-selector-language.html">document selection string</a> to select compatible routes</li> - <li><a href="#content">Content</a> - Selects a content cluster distributor based on system state</li> - <li><a href="#messagetype">MessageType</a> - Selects a next hop based on message type</li> - <li><a href="#extern">Extern</a> - Selects a recipient by querying a remote Vespa application</li> - <li><a href="#localservice">LocalService</a> - Selects a recipient based on ip address</li> - <li><a href="#roundrobin">RoundRobin</a> - Selects one from the configured recipients in round-robin order</li> - <li><a href="#subsetservice">SubsetService</a> - Selects only among a subset of all matching services</li> - <li><a href="#loadbalancer">LoadBalancer</a> - A round-robin policy that chooses between the recipients - by generating a weight according to their performance</li> -</ul> - - -<h3 id="merge">Common Document <code>merge()</code> logic</h3> -<p> - The shared merge logic of most Document routing policies is an attempt to - do the "right" thing when merging multiple replies into one. - It works by first stepping through all replies, storing their content as either: -</p> -<ol> - <li>OK replies,</li> - <li>IGNORE replies, or</li> - <li>ERROR replies</li> -</ol> -<p> - If at least one ERROR reply is found, return a new reply that contains all the errors of the others. - If there is at least one OK reply, return the first OK reply, - but transfer all feed information from the others to this - (this is specific data for start- and end-of-feed messages). - Otherwise, return a new reply that contains all the IGNORE errors. Pseudocode: -</p> -<pre> -for each reply, do - if reply has no errors, do - store reply in OK list - else, do - if reply has only IGNORE errors - copy all errors from reply to IGNORE list - else, do - copy all errors from reply to ERROR list - -if ERROR list is not empty, do - return new reply with all errors -else, do - if OK list is not empty, do - return first reply with all feed answers - else, do - return new reply with all IGNORE errors -</pre> - - - -<h2 id="routing-policies-reference">Routing policies reference</h2> -<table class="table"> - <thead> - <tr> - <th>Policy</th> - <th>Description</th> - </tr> - </thead> - <tbody> - <tr> - <th>AND</th> - <td><p id="and"> - This is a mostly a convenience policy - that allows the user to fork a message's route to all configured recipients. - It is not message-type aware, and will simply always select all recipients. - Replies are merged according to the <a href="#merge">shared logic</a>. - </p><p> - The optional string parameter is parsed as a space-separated list of hops. - Configured recipients have precedence over parameter-given recipients, - although this is likely to be changed in the future. - </p></td> - </tr><tr> - <th>DocumentRouteSelector</th> - <td><p id="documentrouteselector"> <!-- ToDo this needs a rewrite --> - This policy is responsible for selecting among the policy's recipients - according to the subscription rules defined by a content cluster's <em>documents</em> element - in <a href="../reference/applications/services/services.html">services.xml</a>. - If the "selection" attribute is set in the "documents" element, - its value is processed as a <a href="../reference/writing/document-selector-language.html">document select</a> string, - and run on documents and document updates to determine routes. - If the "feedname" attribute is set, all feed commands are filtered through it. - </p><p> - The recipient list of this policy is required to map directly to route names. - E.g. if a recipient is "search/cluster.music", and a message is appropriate according to the selection criteria, - the message is routed to the "search/cluster.music" route. - If the route does not exist, this policy will reply with an error. - In short, <u>this policy selects one or more recipient routes based on document content and configured criteria</u>. - </p><p> - If more than one route is chosen, its replies are merged according to the <a href="#merge">shared logic</a>. - </p><p> - This policy does not support any parameters. - </p><p> - The configuration for this is "documentrouteselectorpolicy" available from config id "routing/documentapi". - </p> - {% include important.html content="Because GET messages do not contain any document on which to run the selection criteria, - this policy returns an IGNORED reply that the merging logic processes. - You can see this by attempting to retrieve a document from an application that does not have a content cluster."%} - </td> - </tr><tr> - <th>Content - <td><p id="content"> - This policy allows you to send a message to a content cluster. - The policy uses a system state retrieved from the cluster in question - in conjunction with slobrok information to pick the correct distributor for your message. - </p><p> - In short; <u>use this policy when communicating with document storage</u>. - </p><p> - This policy supports multiple parameters, up to one each of: - </p> - <table class="table"> - <thead></thead><tbody> - <tr> - <th>cluster</th> - <td>The name of the cluster you want to reach. Example: cluster=mycluster</td> - </tr><tr> - <th>config</th> - <td>A comma-separated list of config servers or proxies - you want to use to fetch configuration for the policy. - This can be used to communicate with other clusters than the one you're currently in. - Example: config=tcp/myadmin1:19070,tcp/myadmin2:19070</td> - </tr> - </tbody> - </table> - Separate each parameter with a semicolon. - </td> - </tr><tr> - <th>MessageType</th> - <td><p id="messagetype"> - This policy will select the next hop based on the type of the message. - You configure where all messages should go (defaultroute). - Then you configure what messages types should be overridden and sent to alternative routes. - It is currently only used internally by vespa when using the - <a href="../reference/applications/services/content.html#content">content</a> element. - </p></td> - </tr><tr> - <th>Extern</th> - <td><p id="extern"> - This policy implements the necessary logic to communicate with an external Vespa application - and resolve a single service pattern using that other application's slobrok servers. - Keep in mind that there might be some delay from the moment this policy is initially created - and when it receives the response to its service query, - so using this policy might cause a message to be resent a few times until it is resolved. - If you disable retries, this policy might cause all messages to fail for the first seconds. - </p><p> - This policy uses its parameter for both the address of the extern slobrok server to connect to, - and also the pattern to use for querying. - The parameter is required to be on the form <code><spec>;<service></code>, - where <code>spec</code> is a comma-separated list of slobrok connection specs - on the form "tcp/hostname:port", and <code>service</code> is a service running on the remote Vespa application. - </p> - {% include important.html content="The remote application needs to have a version of both message bus and the document api - that is binary compatible with the application sending from. - This <span style='text-decoration: underline'>can</span> be a problem even between patch releases, - so keep the application versions in sync when using this policy."%} - </td> - </tr><tr> - <th>LocalService</th> - <td><p id="localservice"> - This policy is used to select among all matching services, - but preferring those running on the same host as the current one. - The pattern used when querying for available services is the current one, - but replacing the policy directive with an asterisk. - E.g. the hop "docproc/cluster.default/[LocalService]/chain.default" would prefer local services - among all those that match the pattern "docproc/cluster.default/*/chain.default". - If there are multiple matching services that run locally, - this policy will do simple round-robin load balancing between them. - If no matching services run locally, - this policy simply returns the asterisk as a match to allow the underlying network logic - to do load balancing among all available. - </p><p> - This policy accepts an optional parameter which overrides the local hostname. - Use this if you wish the hop to prefer some specific host. - </p> - {% include important.html content="There is no additional logic to replace other policy directives with an asterisk, - meaning that if other policies directives are present in the hop string - <span style='text-decoration: underline'>after</span> \"[LocalService]\", - no services can possibly be matched."%} - </td> - </tr><tr> - <th>RoundRobin</th> - <td><p id="roundrobin"> - This policy is used to select among a configured set of recipients. - For each configured recipient, this policy determines what online services are matched, - and then selects one among all of those in round-robin order. - If none of the configured recipients match any available service, - this policy returns an error that indicates to the sender that it should retry later. - </p><p> - Because this policy only selects a single recipient, it contains no merging logic. - </p></td> - </tr><tr> - <th>SubsetService</th> - <td><p id="subsetservice"> - This policy is used to select among a subset of all matching services, - and is used to minimize number of connections in the system. - The pattern used when querying for available services is the current one, - but replacing the policy directive with an asterisk. - E.g. the hop "docproc/cluster.default/[SubsetService:3]/chain.default" - would select among a subset of all those that match the pattern "docproc/cluster.default/*/chain.default". - Given that the pattern returns a set of matches, - this policy stores a subset of these based on the hash-value of the running message bus' - connection string (this is unique for each instance). - If there are no matching services, - this policy returns the asterisk as a match to allow the underlying network logic to fail gracefully. - </p><p> - This policy parses its optional parameter as the size of the subset. - If none is given, the subset defaults to size 5. - </p> - {% include important.html content="There is no additional logic to replace other policy directives with an asterisk, - meaning that if other policies directives are present in the hop string - <span style='text-decoration: underline'>after</span> \"[SubsetService]\", - no services can possibly be matched."%} - </td> - </tr><tr> - <th>LoadBalancer</th> - <td><p id="loadbalancer"> - This policy is used to send to a stateless cluster such as docproc, - where any node can be chosen to process any message. - Messages are sent between the nodes in a round-robin fashion, - but each node is assigned a weight based on its performance. - The weights are calculated by measuring the number of times the node had a full input-queue - and returned a busy response. - Use this policy to send to docproc clusters that have nodes with different performance characteristics. - </p><p> - This policy supports multiple parameters, up to one each of: - </p> - <table class="table"> - <thead></thead><tbody> - <tr> - <th>cluster</th> - <td>The name of the cluster you want to reach. - Example: cluster=docproc/cluster.default (mandatory)</td> - </tr><tr> - <th>session</th> - <td>The destination session you want to reach. - In the case of docproc, the name of the docproc chain. - Example: session=chain.mychain (mandatory)</td> - </tr><tr> - <th>config</th> - <td>A comma-separated list of config servers or proxies - you want to use to fetch configuration for the policy. - This can be used to communicate with other clusters than the one you're currently in. - Example: config=tcp/myadmin1:19070,tcp/myadmin2:19070</td> - </tr> - </tbody> - </table> - Separate each parameter with a semicolon. - By default, this policy will use the current Vespa cluster for configuration. - </td> - </tr> - </tbody> -</table> - - -<h2 id="routing-for-indexing">Routing for indexing</h2> -<p> - A normal Vespa configuration has container and content cluster(s), - with one or more document types defined in <em>schemas</em>. - Routing document writes means routing documents to the <em>indexing</em> container cluster, - then the right <em>content</em> cluster. -</p> -<p> - The indexing cluster is a container cluster - - see <a href="#multiple-container-clusters">multiple container clusters</a> for variants. - Add the <a href="../reference/applications/services/container.html#document-api">document-api</a> - feed endpoint to this cluster. - The mapping from document type to content cluster is in - <a href="../reference/applications/services/content.html#document">document</a> in the content cluster. - From <a href="https://github.com/vespa-engine/sample-apps/blob/master/album-recommendation/app/services.xml"> - album-recommendation</a>: -</p> -<pre> -<services version="1.0"> - - <container id="<span class="pre-hilite">container</span>" version="1.0"> - <span class="pre-hilite"><document-api /></span> - <search /> - <nodes> - <node hostalias="node1" /> - </nodes> - </container> - - <content id="music" version="1.0"> - <redundancy>1</redundancy> - <documents> - <document <span class="pre-hilite">type="music"</span> mode="index" /> - </documents> - <nodes> - <node hostalias="node1" distribution-key="0" /> - </nodes> - </content> - -</services> -</pre> -<p> - Given this configuration, Vespa knows which is the container cluster used for indexing, - and which content cluster that stores the <em>music</em> document type. - Use <a href="../reference/operations/self-managed/tools.html#vespa-route">vespa-route</a> - to display routing generated from this configuration: -</p> -<pre> -$ vespa-route -There are 6 route(s): - 1. <span class="pre-hilite">default</span> - 2. default-get - 3. music - 4. music-direct - 5. music-index - 6. storage/cluster.music - -There are 2 hop(s): - 1. <span class="pre-hilite">container</span>/chain.indexing - 2. indexing -</pre> -<p> - Note the <em>default</em> route. This route is auto-generated by Vespa, - and is used when no other route is used when using <a href="../reference/api/document-v1.html">/document/v1</a>. - <em>default</em> points to <em>indexing</em>: -</p> -<pre> -$ vespa-route --route default -The route 'default' has 1 hop(s): - 1. indexing -</pre> -<pre> -$ vespa-route --hop indexing -The hop 'indexing' has selector: - [DocumentRouteSelector] -And 1 recipient(s): - 1. music -</pre> -<pre> -$ vespa-route --route music -The route 'music' has 1 hop(s): - 1. [MessageType:music] -</pre> -<p> - In short, the <em>default</em> route handles documents of type <em>music</em>. - Vespa will route to the container cluster with <em>document-api</em> - - note the <em>chain.indexing</em> above. - This is a set of built-in <em>document processors</em> that does the indexing (below). -</p><p> - Refer to the <a href="#appendix-trace">trace appendix</a> for routing details. -</p> - - - -<h2 id="chain-indexing">chain.indexing</h2> -<p> - This indexing chain is set up on the container once a content cluster has <code>mode="index"</code>. -</p><p> - The - <a href="https://github.com/vespa-engine/vespa/blob/master/docprocs/src/main/java/com/yahoo/docprocs/indexing/IndexingProcessor.java">IndexingProcessor</a> - annotates the document based on the <a href="../reference/writing/indexing-language.html">indexing script</a> - generated from the schema. Example: -</p> -<pre> -$ vespa-get-config -n vespa.configdefinition.ilscripts \ - -i container/docprocchains/chain/indexing/component/com.yahoo.docprocs.indexing.IndexingProcessor - -maxtermoccurrences 100 -fieldmatchmaxlength 1000000 -ilscript[0].doctype "music" -ilscript[0].docfield[0] "artist" -ilscript[0].docfield[1] "artistId" -ilscript[0].docfield[2] "title" -ilscript[0].docfield[3] "album" -ilscript[0].docfield[4] "duration" -ilscript[0].docfield[5] "year" -ilscript[0].docfield[6] "popularity" -ilscript[0].content[0] "clear_state | guard { input artist | tokenize normalize stem:"BEST" | summary artist | index artist; }" -ilscript[0].content[1] "clear_state | guard { input artistId | summary artistId | attribute artistId; }" -ilscript[0].content[2] "clear_state | guard { input title | tokenize normalize stem:"BEST" | summary title | index title; }" -ilscript[0].content[3] "clear_state | guard { input album | tokenize normalize stem:"BEST" | index album; }" -ilscript[0].content[4] "clear_state | guard { input duration | summary duration; }" -ilscript[0].content[5] "clear_state | guard { input year | summary year | attribute year; }" -ilscript[0].content[6] "clear_state | guard { input popularity | summary popularity | attribute popularity; }" -</pre> -<p> - Refer to <a href="../linguistics/linguistics.html">linguistics</a> for more details. -</p> -<p> - By default, the indexing chain is set up on the <em>first</em> container cluster in <em>services.xml</em>. - When having multiple container clusters, it is recommended to configure this explicitly, see - <a href="#multiple-container-clusters">multiple container clusters</a>. -</p> - - - -<h2 id="document-selection">Document selection</h2> -<p> - The <a href="../reference/applications/services/content.html#document">document</a> - can have a <a href="../reference/writing/document-selector-language.html">selection</a> string, - normally used to expire documents. - This is also evaluated during feeding, so documents that would immediately expire are dropped. - This is not an error, the document API will report 200 - but can be confusing. -</p><p> - The evaluation is done in the - <a href="https://github.com/vespa-engine/vespa/blob/master/documentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/DocumentRouteSelectorPolicy.java"> - DocumentRouteSelector</a> at the feeding endpoint - <em>before</em> any processing/indexing. - I.e. the document is evaluated using the selection string (drop it or not), - then where to route it, based on document type. -</p><p> - Example: the selection is configured to not match the document being fed: -</p> -<pre> -<content id="music" version="1.0"> - <redundancy>1</redundancy> - <documents> - <document type="music" mode="index" <span class="pre-hilite">selection='music.album == "thisstringwillnotmatch"'</span>/> -</pre> - -<pre> -$ vespa-feeder --trace 6 doc.json - -<trace> - [1564576570.693] Source session accepted a 4096 byte message. 1 message(s) now pending. - [1564576570.713] Sequencer sending message with sequence id '-1163801147'. - [1564576570.721] Recognized 'default' as route 'indexing'. - [1564576570.727] Recognized 'indexing' as HopBlueprint(selector = { '[DocumentRouteSelector]' }, recipients = { 'music' }, ignoreResult = false). - [1564576570.811] Running routing policy 'DocumentRouteSelector'. - [1564576570.822] Policy 'DocumentRouteSelector' assigned a reply to this branch. - [1564576570.828] Sequencer received reply with sequence id '-1163801147'. - [1564576570.828] Source session received reply. 0 message(s) now pending. -</trace> - -Messages sent to vespa (route default) : ----------------------------------------- -PutDocument: ok: 0 msgs/sec: 0.00 failed: 0 <span class="pre-hilite">ignored: 1</span> latency(min, max, avg): 9223372036854775807, -9223372036854775808, 0 -</pre> -<p> - Without the selection (i.e. everything matches): -</p> -<pre> -$ vespa-feeder --trace 6 doc.json - -<trace> - [1564576637.147] Source session accepted a 4096 byte message. 1 message(s) now pending. - [1564576637.168] Sequencer sending message with sequence id '-1163801147'. - [1564576637.176] Recognized 'default' as route 'indexing'. - [1564576637.180] Recognized 'indexing' as HopBlueprint(selector = { '[DocumentRouteSelector]' }, recipients = { 'music' }, ignoreResult = false). - [1564576637.256] Running routing policy 'DocumentRouteSelector'. - <span class="pre-hilite">[1564576637.268] Component '[MessageType:music]' selected by policy 'DocumentRouteSelector'.</span> - ... -</trace> - -Messages sent to vespa (route default) : ----------------------------------------- -PutDocument: ok: 1 msgs/sec: 1.05 failed: 0 <span class="pre-hilite">ignored: 0</span> latency(min, max, avg): 845, 845, 845 -</pre> -<p> - In the last case, in the <a href="https://github.com/vespa-engine/vespa/blob/master/documentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/DocumentRouteSelectorPolicy.java"> - DocumentRouteSelector</a> routing policy, - the document matched the selection string / there was no selection string, - and the document was forward to the nex hop in the route. -</p> - - - -<h2 id="document-processing">Document processing</h2> -<p> - Add custom processing of documents using <a href="../applications/document-processors.html">document processing</a>. - The normal use case is to add document processors in the default route, before indexing. Example: -</p> -<pre> -<services version="1.0"> - - <container id="container" version="1.0"> - <document-api /> - <search /> - <span class="pre-hilite"><document-processing></span> - <span class="pre-hilite"><chain id="default"></span> - <span class="pre-hilite"><documentprocessor</span> - <span class="pre-hilite">id="com.mydomain.example.Rot13DocumentProcessor"</span> - <span class="pre-hilite">bundle="album-recommendation-docproc" /></span> - <span class="pre-hilite"></chain></span> - <span class="pre-hilite"></document-processing></span> - <nodes> - <node hostalias="node1" /> - </nodes> - </container> - - <content id="music" version="1.0"> - <redundancy>1</redundancy> - <documents> - <document >type="music" mode="index" /> - </documents> - <nodes> - <node hostalias="node1" distribution-key="0" /> - </nodes> - </content> - -</services> -</pre> -<p> - Note that a new hop <em>default/chain.default</em> is added, - and the default route is changed to include this: -</p> -<pre> -$ vespa-route - -There are 6 route(s): - 1. default - 2. default-get - 3. music - 4. music-direct - 5. music-index - 6. storage/cluster.music - -There are 3 hop(s): - 1. default/chain.default - 2. default/chain.indexing - 3. indexing -</pre> -<pre> -$ vespa-route --route default - -The route 'default' has 2 hop(s): - 1. default/chain.default - 2. indexing -</pre> -<p> - Note that the document processing chain must be called <em>default</em> - to automatically be included in the default route. -</p> - - -<h3 id="inherit-indexing-chain">Inherit indexing chain</h3> -<p> - An alternative to the above is inheriting the indexing chain - use this when getting this error: -</p> -<pre> -Indexing cluster 'XX' specifies the chain 'default' as indexing chain. -As the 'default' chain is run by default, using it as the indexing chain will run it twice. -Use a different name for the indexing chain. -</pre> -<p> - Call the chain something else than <em>default</em>, and let it inherit <em>indexing</em>: -</p> -<pre> -<services version="1.0"> - - <container id="container" version="1.0"> - <document-api /> - <search /> - <span class="pre-hilite"><document-processing></span> - <span class="pre-hilite"><chain id="offer-processing" inherits="indexing"></span> - <span class="pre-hilite"><documentprocessor id="processor.OfferDocumentProcessor"/></span> - <span class="pre-hilite"></chain></span> - <span class="pre-hilite"></document-processing></span> - <nodes> - <node hostalias="node1" /> - </nodes> - </container> - - <content id="music" version="1.0"> - <redundancy>1</redundancy> - <span class="pre-hilite"><documents></span> - <span class="pre-hilite"><document type="offer" mode="index"/></span> - <span class="pre-hilite"><document-processing cluster="default" chain="offer-processing"/></span> - <span class="pre-hilite"></documents></span> - <nodes> - <node hostalias="node1" distribution-key="0" /> - </nodes> - </content> - -</services> -</pre> -<p>See <a href="https://github.com/vespa-engine/vespa/issues/13193">#13193</a> for details.</p> - - - -<h2 id="multiple-container-clusters">Multiple container clusters</h2> -<p> - Vespa can be configured to use more than one container cluster. - Use cases can be to separate search and document processing - or having different document processing clusters due to capacity constraints or dependencies. - Example with separate search and feeding/indexing container clusters: -</p> -<pre> -<services version="1.0"> - - <container id="container-search" version="1.0"> - <search /> - <nodes> - <node hostalias="node1" /> - </nodes> - </container> - - <container <span class="pre-hilite">id="container-indexing"</span> version="1.0"> - <http> - <server id="httpServer2" port="8081" /> - </http> - <span class="pre-hilite"><document-api /></span> - <span class="pre-hilite"><document-processing /></span> - <nodes> - <node hostalias="node1" /> - </nodes> - </container> - - <content id="music" version="1.0"> - <redundancy>1</redundancy> - <documents> - <document type="music" mode="index" /> - <span class="pre-hilite"><document-processing cluster="container-indexing" /></span> - </documents> - <nodes> - <node hostalias="node1" distribution-key="0" /> - </nodes> - </content> - -</services> -</pre> -<p>Notes:</p> -<ul> - <li> - The indexing route is explicit using - <a href="../reference/applications/services/content.html#document-processing">document-processing</a> - elements from the content to the container cluster - </li><li> - Set up <em>document-api</em> on the same cluster as indexing to avoid network hop - from feed endpoint to indexing processors -</li><li> - If no <em>document-processing</em> is configured, - it defaults to a container cluster named <em>default</em>. - When using multiple container clusters, - it is best practice to explicitly configure <em>document-processing</em>. -</li> -</ul> -<p> - Observe the <em>container-indexing/chain.indexing</em> hop, - and the indexing chain is set up on the <em>container-indexing</em> cluster: -</p> -<pre> -$ vespa-route - -There are 6 route(s): - 1. default - 2. default-get - 3. music - 4. music-direct - 5. music-index - 6. storage/cluster.music - -There are 2 hop(s): - 1. container-indexing/chain.indexing - 2. indexing -</pre> -<pre> -$ curl -s http://localhost:8081 | python -m json.tool | grep -C 3 chain.indexing - - { - "bundle": "container-disc:7.0.0", - "class": "com.yahoo.messagebus.jdisc.MbusClient", - "id": "chain.indexing@MbusClient", - "serverBindings": [] - }, - { --- - "class": "com.yahoo.docproc.jdisc.DocumentProcessingHandler", - "id": "com.yahoo.docproc.jdisc.DocumentProcessingHandler", - "serverBindings": [ - "mbus://*/chain.indexing" - ] - }, - { - -</pre> - - - -<h2 id="appendix-trace">Appendix: trace</h2> -<p> - Below is a trace example, no selection string: -</p> -<pre> -$ cat doc.json -[ -{ - "put": "id:mynamespace:music::123", - "fields": { - "album": "Bad", - "artist": "Michael Jackson", - "title": "Bad", - "year": 1987, - "duration": 247 - } -} -] - -$ vespa-feeder --trace 6 doc.json -<trace> - [1564571762.403] Source session accepted a 4096 byte message. 1 message(s) now pending. - [1564571762.420] Sequencer sending message with sequence id '-1163801147'. - [1564571762.426] Recognized 'default' as route 'indexing'. - [1564571762.429] Recognized 'indexing' as HopBlueprint(selector = { '[DocumentRouteSelector]' }, recipients = { 'music' }, ignoreResult = false). - [1564571762.489] Running routing policy 'DocumentRouteSelector'. - [1564571762.493] Component '[MessageType:music]' selected by policy 'DocumentRouteSelector'. - [1564571762.493] Resolving '[MessageType:music]'. - [1564571762.520] Running routing policy 'MessageType'. - [1564571762.520] Component 'music-index' selected by policy 'MessageType'. - [1564571762.520] Resolving 'music-index'. - [1564571762.520] Recognized 'music-index' as route 'container/chain.indexing [Content:cluster=music]'. - [1564571762.520] Recognized 'container/chain.indexing' as HopBlueprint(selector = { '[LoadBalancer:cluster=container;session=chain.indexing]' }, recipients = { }, ignoreResult = false). - [1564571762.526] Running routing policy 'LoadBalancer'. - [1564571762.538] Component 'tcp/vespa-container:19101/chain.indexing' selected by policy 'LoadBalancer'. - [1564571762.538] Resolving 'tcp/vespa-container:19101/chain.indexing [Content:cluster=music]'. - [1564571762.580] Sending message (version 7.83.27) from client to 'tcp/vespa-container:19101/chain.indexing' with 179.853 seconds timeout. - [1564571762.581] Message (type 100004) received at 'container/container.0' for session 'chain.indexing'. - [1564571762.581] Message received by MbusServer. - [1564571762.582] Request received by MbusClient. - [1564571762.582] Running routing policy 'Content'. - [1564571762.582] Selecting route - [1564571762.582] No cluster state cached. Sending to random distributor. - [1564571762.582] Too few nodes seen up in state. Sending totally random. - [1564571762.582] Component 'tcp/vespa-container:19114/default' selected by policy 'Content'. - [1564571762.582] Resolving 'tcp/vespa-container:19114/default'. - [1564571762.586] Sending message (version 7.83.27) from 'container/container.0' to 'tcp/vespa-container:19114/default' with 179.995 seconds timeout. - [1564571762.587181] Message (type 100004) received at 'storage/cluster.music/distributor/0' for session 'default'. - [1564571762.587245] music/distributor/0 CommunicationManager: Received message from message bus - [1564571762.587510] Communication manager: Sending Put(BucketId(0x2000000000000020), id:mynamespace:music::123, timestamp 1564571762000000, size 275) - [1564571762.587529] Communication manager: Passing message to source session - [1564571762.587547] Source session accepted a 1 byte message. 1 message(s) now pending. - [1564571762.587681] Sending message (version 7.83.27) from 'storage/cluster.music/distributor/0' to 'storage/cluster.music/storage/0/default' with 180.00 seconds timeout. - [1564571762.587960] Message (type 10) received at 'storage/cluster.music/storage/0' for session 'default'. - [1564571762.588052] music/storage/0 CommunicationManager: Received message from message bus - [1564571762.588263] PersistenceThread: Processing message in persistence layer - [1564571762.588953] Communication manager: Sending PutReply(id:mynamespace:music::123, BucketId(0x2000000000000020), timestamp 1564571762000000) - [1564571762.589023] Sending reply (version 7.83.27) from 'storage/cluster.music/storage/0'. - [1564571762.589332] Reply (type 11) received at 'storage/cluster.music/distributor/0'. - [1564571762.589448] Source session received reply. 0 message(s) now pending. - [1564571762.589459] music/distributor/0Communication manager: Received reply from message bus - [1564571762.589679] Communication manager: Sending PutReply(id:music:music::123, BucketId(0x0000000000000000), timestamp 1564571762000000) - [1564571762.589807] Sending reply (version 7.83.27) from 'storage/cluster.music/distributor/0'. - [1564571762.590] Reply (type 200004) received at 'container/container.0'. - [1564571762.590] Routing policy 'Content' merging replies. - [1564571762.590] Reply received by MbusClient. - [1564571762.590] Sending reply from MbusServer. - [1564571762.590] Sending reply (version 7.83.27) from 'container/container.0'. - [1564571762.612] Reply (type 200004) received at client. - [1564571762.613] Routing policy 'LoadBalancer' merging replies. - [1564571762.613] Routing policy 'MessageType' merging replies. - [1564571762.615] Routing policy 'DocumentRouteSelector' merging replies. - [1564571762.622] Sequencer received reply with sequence id '-1163801147'. - [1564571762.622] Source session received reply. 0 message(s) now pending. -</trace> - -Messages sent to vespa (route default) : ----------------------------------------- -PutDocument: ok: 1 msgs/sec: 3.30 failed: 0 ignored: 0 latency(min, max, avg): 225, 225, 225 -</pre> diff --git a/en/writing/document-routing.mdx b/en/writing/document-routing.mdx new file mode 100644 index 0000000000..69c4467c2d --- /dev/null +++ b/en/writing/document-routing.mdx @@ -0,0 +1,1055 @@ +--- +title: "Routing" +sidebarTitle: "Document routing" +--- + +*Routing* is used to configure the paths that documents and updates written to Vespa take through the system. Vespa will automatically set up a routing configuration which is appropriate for most cases, so no explicit routing configuration is necessary. However, explicit routing can be used in advanced use cases such as sending different document streams to different document processing clusters, or through multiple consecutive clusters etc. + +There are other, more in-depth, articles on routing: + +- Use [vespa-route](/en/reference/operations/self-managed/tools#vespa-route) to inspect routes and services of a Vespa application, like in the [example](#example-reconfigure-the-default-route) +- [Routing policies reference](#routing-policies-reference). See the [routing policies](#routing-policies) note for complex routes and default routing + +In Vespa, there is a transport layer and a programming interface that are available to clients that wish to communicate with a Vespa application. The transport layer is *Message Bus*. [Document API](/en/writing/document-api-guide) is implemented on top of Message Bus. Configuring the interface therefore exposes some features available in Message Bus. Refer to the [Vespa APIs and interfaces](/en/reference/api/api) for clients using the *Document API*. The atoms in Vespa routing are *routes* and *hops*. + +[document-processing](https://github.com/vespa-engine/sample-apps/tree/master/examples/document-processing) is an example of custom document processing, and useful for testing routing. + +## A route is a sequence of hops + +The sequence of hosts, routers, bridges, gateways, and other devices that network traffic takes, or could take, from its source to its destination is what is classically termed a *route*. As a verb, *to route* means to determine the link down which to send a packet, that will minimize its total journey time according to some routing algorithm. + +In Vespa, a route is simply a sequence of named hops. Instead of leaving selection logic to a route, the responsibility of resolving recipients is given to the [hops](#a-hop-is-a-point-to-point-transmission)' [selectors](#selection-logic). A hop can do more or less whatever it wants to change a message's journey through your application; it can slightly alter itself by choosing among some predefined recipients, it can change itself completely by either rewriting or looking up another hop, or it can even modify the entire route from that branch onwards. In effect, a route can end up branching at several points along its path, resulting in complex routes. As the figure suggests, Message Bus supports both [unicasting](https://en.wikipedia.org/wiki/Unicast) and [multicasting](https://en.wikipedia.org/wiki/Multicast) - Message Bus allows for arbitrarily complex routes. Each node in the above graph represents a Vespa service: + +<Frame> +![Illustration of routes with hops](/assets/img/routing.svg) +</Frame> + +## A hop is a point-to-point transmission + + +In telecommunication, a *hop* is one step, from one router to the next, on the path of a packet on an Internet Protocol network. It is a direct host-to-host connection forming part of the route between two hosts in a routed network such as the Internet. In more general terms, a hop is a point-to-point transmission in a series required to get a message from point A to point B. + +With Message Bus the concept of hops was introduced as the smallest steps of the transmission of a message. A hop consists of a *name* that is used by the messaging clients to select it, a list of *recipient* services that it may transmit to, and a *selector* that is used to select among those recipients. Unlike traditional hops, in Vespa a hop is a transmission from one sender to many recipients. + +Well, the above is only partially true; it is the easiest way to understand the hop concept. In fact, a hop's recipient list is nothing more than a configured list of strings that is made available to all [routing policies](#routing-policies) that are named in the selector string. See [selection logic](#selection-logic) below for details. + +A hop's recipient is the service name of a Message Bus client that has been registered in Vespa's service location broker (vespa-slobrok). These names are well-defined once their derivation logic is understood; they are "/"-separated sets of address-components whose values are given by a service's role in the application. An example of a recipient is: + +```text +search/cluster.foo/*/feed-destination +``` + +The marked components of the above recipient, `/search/cluster.foo/*`, resolves to a host's symbolic name. This is the name with which a Message Bus instance was configured. The unmarked component, `feed-destination`, is the local name of the running service that the hop transmits to, i.e. the name of the *session* created on the running Message Bus instance. + +The Active Configuration page in Vespa's administration interface gives an insight into what symbolic names exist for any given application by looking at its current configuration subscriptions. All available Message Bus services use their `ConfigId` as their host's symbolic name. See [vespa-route](/en/reference/operations/self-managed/tools#vespa-route) for how to inspect this, or use the [config API](/en/reference/api/config-v2). + +A hop can be prefixed using the special character "?" to force it to behave as if its [ignore-result](#hop) attribute was configured to "true". + +### Asterisk + +A service identifier may include the special character "\*" as an address component. A recipient that contains this character is a request for the network to choose *any one* service that matches it. + +## Routing policies + +A routing policy is a protocol-specific algorithm that chooses among a list of candidate recipients for a single address component - see [hop description](#a-hop-is-a-point-to-point-transmission) above. These policies are designed and implemented as key parts of a Message Bus protocol. E.g. for the "Document" protocol these are what make up the routing behavior for document transmission. Without policies, a hop would only be able to match verbatim to a recipient, and thus the only advanced selection logic would be that of the [asterisk](#asterisk). + +In addition to implementing a selection algorithm, a routing policy must also implement a merging algorithm that combines the replies returned from each selected recipient into a single sensible reply. This is needed because a client does not necessarily know whether a message has been sent to one or multiple recipients, and **Message Bus guarantees a single reply for every message**. + +More formally, a routing policy is an arbitrarily large (or small), named, stand-alone piece of code registered with a Message Bus protocol. As discussed [above](#selection-logic), an instance of a policy is run both when resolving a route to recipients, and when merging replies. The policy is passed a `RoutingContext` object that pretty much allows it to do whatever it pleases to the route and replies. The same policy object and the same context object is used for both selection and merging. + +Refer to the [routing policy reference](#routing-policies-reference). + +## Selection logic + +When Message Bus is about to route a message, at the last possible time, it inspects the **first** hop of the message's route to resolve a set of recipients. First, all of its [policies are resolved](#1-resolve-policy-directives). Second, the output service name is matched to the routing table to see if it maps to another hop or route. Finally, the message is [sent](#3-send-to-services) to all chosen recipient services. Because each policy can select multiple recipients, this can give rise to an arbitrarily complex routing tree. There are, of course, safeguards within Message Bus to prevent infinite recursions due to circular dependencies or misconfiguration. + +<Note> +**Note:** + +It **is** possible to develop a different protocol with other policies to run in the application, but since all of Vespa's component only support the "Document" protocol, it makes little sense to do so. +</Note> + +### 1. Resolve Policy Directives + +The logic run at this step is actually simple; as long as the hop string contains a policy directive, i.e. some arbitrary string enclosed in square brackets, Message Bus will create and run an instance of that policy for the protocol of the message being routed. + +```text +Name: storage/cluster.backup +Selector: storage/cluster.backup/distributor/[Distributor]/default +Recipients: - +``` + +The above hop is probably the simplest hop you will encounter in Vespa; it has a single policy directive contained in a string that closely resembles service names discussed above, and it has no recipients. When resolving this hop, Message Bus creates an instance of the "DocumentRouteSelector" policy and invokes its `select()` method. The "Distributor" policy will replace its own directive with a proper distributor identifier, yielding a hop string that is now an unambiguous service identifier. + +```text +Name: indexing +Selector: [DocumentRouteSelector] +Recipients: search/cluster.music + search/cluster.books +``` + +This hop has a selector which is nothing more than a single policy directive, "\[DocumentRouteSelector\]", and it has two configured recipients, "search/cluster.music" and "search/cluster.books". This policy expands the hop to zero, one or two **new** routes by replacing its own directive with the content of the recipient routes. Each of these routes may have one or more hops themselves. In turn, these will be processed independently. When replies are available from all chosen recipients, the policy's `merge()` method is invoked, and the resulting reply is passed upwards. + +```text +Name: default +Selector: [AND:indexing storage/cluster.backup] +Recipients: - +``` + +This hop has a selector but no recipients. The reason for this is best explained in the [routing policies reference](#routing-policies-reference), but it serves as an example of a hop that has no configured recipients. Notice how the policy directive contains a colon (":") which denotes that the remainder of the directive is a parameter to the policy constructor. This policy replaces the whole route of the message with the set of routes named in the parameter string. + +What routing policies are available depends on what protocol is currently running. As of this version the only supported protocol is "Document". This offers a set of routing policies discussed [below](#routing-policies-reference). + +### 2. Resolve Hop- and Route names + +As soon as all policy directives have been resolved, Message Bus makes sure that the resulting string is, in fact, a service name and not the name of another hop or route (in that order) configured for the running protocol. The outcome is either: + +1. The string is recognized as a hop name - The current hop is replaced by the named one, and processing returns to [step 1](#1-resolve-policy-directives). +2. The string is recognized as a route name - The current route, including all the hops following this, is replaced by the named one. Processing returns to [step 1](#1-resolve-policy-directives). +3. The string is accepted as a service name - This terminates the current branch of the routing tree. If all branches are terminated, processing proceeds to [step 3](#3-send-to-services). + +Because hop names are checked before route names, Message Bus also supports a "route:" prefix that forces the remainder of the string to resolve to a configured route or fail. + +### 3. Send to Services + +When the route resolver reaches this point, the first hop of the message being sent has been resolved to an arbitrarily complex routing tree. Each leaf of this tree represents a service that is to receive the message, unless some policy has already generated a reply for it. No matter how many recipients are chosen, the message is serialized only once, and the network transmission is able to share the same chunk of memory between all recipients. + +As replies to the message arrive at the sender they are handed over to the corresponding leaf nodes of the routing tree, but merging will not commence until all leaf nodes are ready. + +Route resolving happens just before network transmission, after all resending logic. This means that if the route configuration changes while there are messages scheduled for resending, these will adhere to the new routes. + +If the resolution of a recipient passed through a hop that was configured to [ignore results](#hop), the network layer will reply immediately with a synthetic "OK". + +## Example: Reconfigure the default route + +Assume that the application requires both search and storage capabilities, but that the default feed should only pass through to search. An imaginary scenario for this would be a system where there is a continuous feed being passed into Vespa with no filtering on spam. You would like a minimal storage-only cluster that stores a URL blocklist that can be used by a custom document processor to block incoming documents from offending sites. + +Apart from the blocklist and the document processor, add the following: + +```js +<routing version="1.0"> + <routingtable protocol="document"> + <route name="default" hops="docproc/cluster.blocklist/*/chain.blocklist indexing" /> + </routingtable> +</routing> +``` + +This overrides the default route to pass through any available blocklisting document processor before being indexed. If the document processor decides to block a message, it must respond with an appropriate *ok* reply, or your client software needs to accept whatever error reply you decide to return when blocking. + +When feeding blocklist information to storage, your application need only use the already available `storage` hop. + +See [#13193](https://github.com/vespa-engine/vespa/issues/13193) for a discussion on using *default* as a name. + +### The Document API + +With the current implementation of Document API running on Message bus, the configuration of the API implies configuration of the latter. Most clients will only ever route through this API. To use the Document API, you need to instantiate a class that implements the `DocumentAccess` interface. At the time of writing only `MessageBusDocumentAccess` exists, and it requires a parameter set for creation. These parameters are contained in an instance of `MessageBusDocumentAccessParam` that looks somewhat like the following: + +```js +class MessageBusDocumentAccessParams { + String documentManagerConfigId; // The id to resolve to document manager config. + String oosServerPattern; // The service pattern to resolve to fleet controller + // services. + String appConfigId; // The id to resolve to application config. + String slobrokConfigId; // The id to resolve to slobrok config. + String routingConfigId; // The id to resolve to messagebus routing config. + + String routeName; // The name of the route to send to. + int traceLevel; // The trace level to use when sending. + + class SourceSessionParams { + int maxPending; // Maximum number of pending messages. + int maxPendingSize; // Maximum size of pending messages. + double timeout; // Default timeout in seconds for messages + // that have no timeout set. + double requestTimeoutA; // Default request timeout in seconds, using + double requestTimeoutB; // the equation 'requestTimeout = a * retry + b'. + double retryDelay; // Number of seconds to wait before resending. + }; +} +``` + +The most obvious configuration parameter is `routeName`, which informs the `MessageBusDocumentAccess` object the name of the route to use when sending documents and updates. The second parameter is `traceLevel`, which allows a client to see exactly how the data was transmitted. + +<Note> +**Note:** + +Tracing can be enabled on a level from 1-9, where a higher number means more tracing. Because the concept of tracing is not exposed by the Document API itself, its data will simply be printed to standard output when a reply arrives for the sender. This should therefore not be used in production, but can be helpful when debugging. +</Note> + +Refer to the [Document API JavaDoc](https://javadoc.io/doc/com.yahoo.vespa/documentapi). + +## Routing servicesThis is the reference documentation for all elements in the *routing* section of [services.xml](/en/reference/applications/services/services). + +```text +routing [version] + routingtable [protocol, verify] + route [name, hops] + hop [name, selector, ignore-result] + recipient [session] + services [protocol] + service [name] +``` + +## routing + +Contained in [services](/en/reference/applications/services/services#services). The container element for all configuration related to routing. + +<table className="vespa-table"> + <thead> + <tr> + <th>Attribute</th> + <th>Required</th> + <th>Value</th> + <th>Default</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>version</td> + <td>required</td> + <td>number</td> + <td></td> + <td>Must be set to "1.0" in this Vespa-version</td> + </tr> + </tbody> +</table> + +Optional subelements: + +<CardGroup> + <Card title="routingtable" icon="code-branch" href="#routingtable" horizontal /> + <Card title="services" icon="server" href="#services" horizontal /> +</CardGroup> + +Example: + +```js +<routing version="1.0"> + <routingtable protocol="document"> + <route name="route1" hops="hop1 hop2" /> + <route name="route2" hops="hop3 hop4 hop5" /> + <hop name="hop1" selector="docproc/cluster.foo/docproc/*/feed-processor"> + <recipient session="docproc/cluster.foo/docproc/*/feed-processor" /> + </hop> + <hop name="hop2" selector="search/cluster.bar/[SearchGroup]/[SearchRow]/[SearchColumn]/feed-destination"> + <recipient session="search/cluster.bar/g0/c0/r0/feed-destination" /> + <recipient session="search/cluster.bar/g0/c1/r0/feed-destination" /> + <recipient session="search/cluster.bar/g0/c0/r1/feed-destination" /> + <recipient session="search/cluster.bar/g0/c1/r1/feed-destination" /> + <recipient session="search/cluster.bar/g1/c0/r0/feed-destination" /> + <recipient session="search/cluster.bar/g1/c1/r0/feed-destination" /> + <recipient session="search/cluster.bar/g1/c0/r1/feed-destination" /> + <recipient session="search/cluster.bar/g1/c1/r1/feed-destination" /> + </hop> + </routingtable> + <services protocol="document"> + <service name="foo/bar" /> + </services> +</routing> +``` + +## routingtable + +Contained in [routing](#routing). Specifies a routing table for a specific protocol. + +<table className="vespa-table"> + <thead> + <tr> + <th>Attribute</th> + <th>Required</th> + <th>Value</th> + <th>Default</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>protocol</td> + <td>required</td> + <td></td> + <td></td> + <td>Configure which protocol to use. Only the protocol *document* is defined, so if you define a routing table for an unsupported protocol, the application will just log an INFO entry that contains the name of that protocol.</td> + </tr> + <tr> + <td>verify</td> + <td>optional</td> + <td>boolean</td> + <td></td> + <td>ToDo: document this</td> + </tr> + </tbody> +</table> + +Optional subelements: + +<CardGroup> + <Card title="route" icon="code-branch" href="#route" horizontal /> + <Card title="hop" icon="arrow-right-circle" href="#hop" horizontal /> +</CardGroup> + +Example: + +```js +<routing version="1.0"> + <routingtable protocol="document"> + <route name="route1" hops="hop1 hop2" /> + <hop name="hop1" selector="docproc/cluster.foo/docproc/*/feed-processor"> + <recipient session="docproc/cluster.foo/docproc/*/feed-processor" /> + </hop> + </routingtable> +</routing> +``` + +## route + +Contained in [routingtable](#routingtable). Specifies a route for a message to its destination through a set of intermediate hops. If at least one hop in a route does not exist, the application will fail to start and issue an error that contains the name of that hop. + +<table className="vespa-table"> + <thead> + <tr> + <th>Attribute</th> + <th>Required</th> + <th>Value</th> + <th>Default</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>name</td> + <td>required</td> + <td></td> + <td></td> + <td>Route name.</td> + </tr> + <tr> + <td>hops</td> + <td>required</td> + <td></td> + <td></td> + <td>A whitespace-separated list of hop names, where each name must be a valid hop.</td> + </tr> + </tbody> +</table> + +Subelements: none + +Example: + +```js +<routing version="1.0"> + <routingtable protocol="document"> + <route name="route1" hops="hop1 hop2" /> + <route name="route2" hops="hop3 hop4 hop5" /> + </routingtable> +</routing> +``` + +## hop + +Contained in [routingtable](#routingtable). Specifies a single hop that can be used to construct one or more routes. A hop must have a name that is unique within the routing table to which it belongs. A hop contains a selector string and a list of recipient sessions. + +<table className="vespa-table"> + <thead> + <tr> + <th>Attribute</th> + <th>Required</th> + <th>Value</th> + <th>Default</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>name</td> + <td>required</td> + <td></td> + <td></td> + <td>Hop name.</td> + </tr> + <tr> + <td>selector</td> + <td>required</td> + <td></td> + <td></td> + <td>Selector string.</td> + </tr> + <tr> + <td>ignore-result</td> + <td>optional</td> + <td></td> + <td></td> + <td>If set to *true*, specifies that the result of routing through that hop should be ignored.</td> + </tr> + </tbody> +</table> + +Optional subelements: + +<CardGroup> + <Card title="recipient" icon="user" href="#recipient" horizontal /> +</CardGroup> + +Example: + +```js +<routing version="1.0"> + <routingtable protocol="document"> + <hop name="hop1" selector="docproc/cluster.foo/docproc/*/feed-processor"> + <recipient session="docproc/cluster.foo/docproc/*/feed-processor" /> + </hop> + </routingtable> +</routing> +``` + +## recipient + +Contained in [hop](#hop). Specifies a recipient session of a hop. + +<table className="vespa-table"> + <thead> + <tr> + <th>Attribute</th> + <th>Required</th> + <th>Value</th> + <th>Default</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>session</td> + <td>required</td> + <td></td> + <td></td> + <td>This attribute must correspond to a running instance of a service that can be routed to. All session identifiers consist of a location part and a name. A search node is always given a session name on the form *search/cluster.name/g#/r#/c#/feed-destination*, whereas a document processor service is always named *docproc/cluster.name/docproc/#/feed-processor*.</td> + </tr> + </tbody> +</table> + +Subelements: none + +Example: + +```js +<routing version="1.0"> + <routingtable protocol="document"> + <hop name="search/cluster.music" selector="search/cluster.music/[SearchGroup]/[SearchRow]/[SearchColumn]/feed-destination"> + <recipient session="search/cluster.music/g0/c0/r0/feed-destination" /> + <recipient session="search/cluster.music/g0/c0/r1/feed-destination" /> + <recipient session="search/cluster.music/g1/c0/r0/feed-destination" /> + <recipient session="search/cluster.music/g1/c0/r1/feed-destination" /> + </hop> + </routingtable> +</routing> +``` + +## services + +Contained in [routing](#routing). Specifies a set of services available for a specific protocol. At the moment the only supported protocol is *document*. The services specified are used by the route verification step to allow hops and routes to reference services known to exist, but that can not be derived from *services.xml*. + +<table className="vespa-table"> + <thead> + <tr> + <th>Attribute</th> + <th>Required</th> + <th>Value</th> + <th>Default</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>protocol</td> + <td>required</td> + <td></td> + <td></td> + <td>Configure which protocol to use. Only the protocol *document* is defined.</td> + </tr> + </tbody> +</table> + +Optional subelements: + +<CardGroup> + <Card title="service" icon="server" href="#service" horizontal /> +</CardGroup> + +Example: + +```js +<routing version="1.0"> + <services protocol="document"> + <service name="foo/bar" /> + </services> +</routing> +``` + +## service + +Contained in [services](#services). Specifies a single known service that can not be derived from the *services.xml*. + +<table className="vespa-table"> + <thead> + <tr> + <th>Attribute</th> + <th>Required</th> + <th>Value</th> + <th>Default</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>name</td> + <td>required</td> + <td></td> + <td></td> + <td>The name of the service.</td> + </tr> + </tbody> +</table> + +Subelements: none + +Example: + +```js +<routing version="1.0"> + <services protocol="document"> + <service name="foo/bar" /> + </services> +</routing> +``` + +## Routingpolicies reference + + +This article contains detailed descriptions of the behaviour of all routing policies available in Vespa. + +The *Document protocol* is currently the only Message Bus protocol supported by Vespa. Furthermore, all routing policies that are part of this protocol share a common code path for [merging replies](#merge). The policies offered by the protocol are: + +- [AND](#and) - Selects all configured recipient hops. +- [DocumentRouteSelector](#documentrouteselector) - Uses a [document selection string](/en/reference/writing/document-selector-language) to select compatible routes +- [Content](#content) - Selects a content cluster distributor based on system state +- [MessageType](#messagetype) - Selects a next hop based on message type +- [Extern](#extern) - Selects a recipient by querying a remote Vespa application +- [LocalService](#localservice) - Selects a recipient based on ip address +- [RoundRobin](#roundrobin) - Selects one from the configured recipients in round-robin order +- [SubsetService](#subsetservice) - Selects only among a subset of all matching services +- [LoadBalancer](#loadbalancer) - A round-robin policy that chooses between the recipients by generating a weight according to their performance + +### Common Document `merge()` logic + +The shared merge logic of most Document routing policies is an attempt to do the "right" thing when merging multiple replies into one. It works by first stepping through all replies, storing their content as either: + +1. OK replies, +2. IGNORE replies, or +3. ERROR replies + +If at least one ERROR reply is found, return a new reply that contains all the errors of the others. If there is at least one OK reply, return the first OK reply, but transfer all feed information from the others to this (this is specific data for start- and end-of-feed messages). Otherwise, return a new reply that contains all the IGNORE errors. Pseudocode: + +```text +for each reply, do + if reply has no errors, do + store reply in OK list + else, do + if reply has only IGNORE errors + copy all errors from reply to IGNORE list + else, do + copy all errors from reply to ERROR list + +if ERROR list is not empty, do + return new reply with all errors +else, do + if OK list is not empty, do + return first reply with all feed answers + else, do + return new reply with all IGNORE errors +``` + +## Routing policies reference + +<table className="vespa-table"> + <thead> + <tr> + <th>Policy</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>AND</td> + <td>This is a mostly a convenience policy that allows the user to fork a message's route to all configured recipients. It is not message-type aware, and will simply always select all recipients. Replies are merged according to the <a href="#merge" aria-label="shared logic">shared logic</a>. <br/><br/> The optional string parameter is parsed as a space-separated list of hops. Configured recipients have precedence over parameter-given recipients, although this is likely to be changed in the future.</td> + </tr> + <tr> + <td>DocumentRouteSelector</td> + <td>This policy is responsible for selecting among the policy's recipients according to the subscription rules defined by a content cluster's *documents* element in <a href="/en/reference/applications/services/services" aria-label="services.xml">services.xml</a>. If the "selection" attribute is set in the "documents" element, its value is processed as a <a href="/en/reference/writing/document-selector-language" aria-label="document select">document select</a> string, and run on documents and document updates to determine routes. If the "feedname" attribute is set, all feed commands are filtered through it. <br/><br/> The recipient list of this policy is required to map directly to route names. E.g. if a recipient is "search/cluster.music", and a message is appropriate according to the selection criteria, the message is routed to the "search/cluster.music" route. If the route does not exist, this policy will reply with an error. In short, this policy selects one or more recipient routes based on document content and configured criteria. <br/><br/> If more than one route is chosen, its replies are merged according to the <a href="#merge" aria-label="shared logic">shared logic</a>. <br/><br/> This policy does not support any parameters. <br/><br/> The configuration for this is "documentrouteselectorpolicy" available from config id "routing/documentapi". <br/><br/> <Warning> **Important:**<br/> Because GET messages do not contain any document on which to run the selection criteria, this policy returns an IGNORED reply that the merging logic processes. You can see this by attempting to retrieve a document from an application that does not have a content cluster.</Warning></td> + </tr> + <tr> + <td>Content</td> + <td>This policy allows you to send a message to a content cluster. The policy uses a system state retrieved from the cluster in question in conjunction with slobrok information to pick the correct distributor for your message. <br/><br/> In short; use this policy when communicating with document storage. <br/><br/> This policy supports multiple parameters, up to one each of:<br/><br/> **cluster**<br/>The name of the cluster you want to reach. Example: cluster=mycluster <br/><br/> **config**<br/> A comma-separated list of config servers or proxies you want to use to fetch configuration for the policy. This can be used to communicate with other clusters than the one you're currently in. Example: config=tcp/myadmin1:19070,tcp/myadmin2:19070 <br/><br/> Separate each parameter with a semicolon.</td> + </tr> + <tr> + <td>MessageType</td> + <td>This policy will select the next hop based on the type of the message. You configure where all messages should go (defaultroute). Then you configure what messages types should be overridden and sent to alternative routes. It is currently only used internally by vespa when using the <a href="/en/reference/applications/services/content#content" aria-label="content">content</a> element.</td> + </tr> + <tr> + <td>Extern</td> + <td>This policy implements the necessary logic to communicate with an external Vespa application and resolve a single service pattern using that other application's slobrok servers. Keep in mind that there might be some delay from the moment this policy is initially created and when it receives the response to its service query, so using this policy might cause a message to be resent a few times until it is resolved. If you disable retries, this policy might cause all messages to fail for the first seconds.<br/><br/> This policy uses its parameter for both the address of the extern slobrok server to connect to, and also the pattern to use for querying. The parameter is required to be on the form <code>{`<spec>;<service>`}</code>, where <code>{`spec`}</code> is a comma-separated list of slobrok connection specs on the form "tcp/hostname:port", and <code>{`service`}</code> is a service running on the remote Vespa application. <br/><br/> <Warning> **Important:**<br/> The remote application needs to have a version of both message bus and the document api that is binary compatible with the application sending from. This can be a problem even between patch releases, so keep the application versions in sync when using this policy.</Warning></td> + </tr> + <tr> + <td>LocalService</td> + <td>This policy is used to select among all matching services, but preferring those running on the same host as the current one. The pattern used when querying for available services is the current one, but replacing the policy directive with an asterisk. E.g. the hop "docproc/cluster.default/[LocalService]/chain.default" would prefer local services among all those that match the pattern "docproc/cluster.default/*/chain.default". If there are multiple matching services that run locally, this policy will do simple round-robin load balancing between them. If no matching services run locally, this policy simply returns the asterisk as a match to allow the underlying network logic to do load balancing among all available.<br/><br/> This policy accepts an optional parameter which overrides the local hostname. Use this if you wish the hop to prefer some specific host. <br/><br/> <Warning> **Important:**<br/> There is no additional logic to replace other policy directives with an asterisk, meaning that if other policies directives are present in the hop string after "[LocalService]", no services can possibly be matched.</Warning></td> + </tr> + <tr> + <td>RoundRobin</td> + <td>This policy is used to select among a configured set of recipients. For each configured recipient, this policy determines what online services are matched, and then selects one among all of those in round-robin order. If none of the configured recipients match any available service, this policy returns an error that indicates to the sender that it should retry later.<br/><br/> Because this policy only selects a single recipient, it contains no merging logic.</td> + </tr> + <tr> + <td>SubsetService</td> + <td>This policy is used to select among a subset of all matching services, and is used to minimize number of connections in the system. The pattern used when querying for available services is the current one, but replacing the policy directive with an asterisk. E.g. the hop "docproc/cluster.default/[SubsetService:3]/chain.default" would select among a subset of all those that match the pattern "docproc/cluster.default/*/chain.default". Given that the pattern returns a set of matches, this policy stores a subset of these based on the hash-value of the running message bus' connection string (this is unique for each instance). If there are no matching services, this policy returns the asterisk as a match to allow the underlying network logic to fail gracefully.<br/><br/> This policy parses its optional parameter as the size of the subset. If none is given, the subset defaults to size 5.<br/><br/> <Warning> **Important:** <br/>There is no additional logic to replace other policy directives with an asterisk, meaning that if other policies directives are present in the hop string after "[SubsetService]", no services can possibly be matched.</Warning></td> + </tr> + <tr> + <td>LoadBalancer</td> + <td>This policy is used to send to a stateless cluster such as docproc, where any node can be chosen to process any message. Messages are sent between the nodes in a round-robin fashion, but each node is assigned a weight based on its performance. The weights are calculated by measuring the number of times the node had a full input-queue and returned a busy response. Use this policy to send to docproc clusters that have nodes with different performance characteristics. <br/><br/> This policy supports multiple parameters, up to one each of: <br/><br/>**cluster**<br/> The name of the cluster you want to reach. Example: cluster=docproc/cluster.default (mandatory) <br/><br/>**session**<br/>The destination session you want to reach. In the case of docproc, the name of the docproc chain. Example: session=chain.mychain (mandatory) <br/><br/>**config**<br/> A comma-separated list of config servers or proxies you want to use to fetch configuration for the policy. This can be used to communicate with other clusters than the one you're currently in. Example: config=tcp/myadmin1:19070,tcp/myadmin2:19070 <br/><br/> Separate each parameter with a semicolon. By default, this policy will use the current Vespa cluster for configuration.</td> + </tr> + </tbody> +</table> + +## Routing for indexing + +A normal Vespa configuration has container and content cluster(s), with one or more document types defined in *schemas*. Routing document writes means routing documents to the *indexing* container cluster, then the right *content* cluster. + +The indexing cluster is a container cluster - see [multiple container clusters](#multiple-container-clusters) for variants. Add the [document-api](/en/reference/applications/services/container#document-api) feed endpoint to this cluster. The mapping from document type to content cluster is in [document](/en/reference/applications/services/content#document) in the content cluster. From [album-recommendation](https://github.com/vespa-engine/sample-apps/blob/master/album-recommendation/app/services.xml): + +```js +<services version="1.0"> + + <container id="container" version="1.0"> + <document-api /> + <search /> + <nodes> + <node hostalias="node1" /> + </nodes> + </container> + + <content id="music" version="1.0"> + <redundancy>1</redundancy> + <documents> + <document type="music" mode="index" /> + </documents> + <nodes> + <node hostalias="node1" distribution-key="0" /> + </nodes> + </content> + +</services> +``` + +Given this configuration, Vespa knows which is the container cluster used for indexing, and which content cluster that stores the *music* document type. Use [vespa-route](/en/reference/operations/self-managed/tools#vespa-route) to display routing generated from this configuration: + +```text +$ vespa-route +There are 6 route(s): + 1. default + 2. default-get + 3. music + 4. music-direct + 5. music-index + 6. storage/cluster.music + +There are 2 hop(s): + 1. container/chain.indexing + 2. indexing +``` + +Note the *default* route. This route is auto-generated by Vespa, and is used when no other route is used when using [/document/v1](/en/reference/api/document-v1). *default* points to *indexing*: + +```text +$ vespa-route --route default +The route 'default' has 1 hop(s): + 1. indexing +``` + +```text +$ vespa-route --hop indexing +The hop 'indexing' has selector: + [DocumentRouteSelector] +And 1 recipient(s): + 1. music +``` + +```text +$ vespa-route --route music +The route 'music' has 1 hop(s): + 1. [MessageType:music] +``` + +In short, the *default* route handles documents of type *music*. Vespa will route to the container cluster with *document-api* - note the *chain.indexing* above. This is a set of built-in *document processors* that does the indexing (below). + +Refer to the [trace appendix](#appendix-trace) for routing details. + +## chain.indexing + +This indexing chain is set up on the container once a content cluster has `mode="index"`. + +The [IndexingProcessor](https://github.com/vespa-engine/vespa/blob/master/docprocs/src/main/java/com/yahoo/docprocs/indexing/IndexingProcessor.java) annotates the document based on the [indexing script](/en/reference/writing/indexing-language) generated from the schema. Example: + +```js +$ vespa-get-config -n vespa.configdefinition.ilscripts \ + -i container/docprocchains/chain/indexing/component/com.yahoo.docprocs.indexing.IndexingProcessor + +maxtermoccurrences 100 +fieldmatchmaxlength 1000000 +ilscript[0].doctype "music" +ilscript[0].docfield[0] "artist" +ilscript[0].docfield[1] "artistId" +ilscript[0].docfield[2] "title" +ilscript[0].docfield[3] "album" +ilscript[0].docfield[4] "duration" +ilscript[0].docfield[5] "year" +ilscript[0].docfield[6] "popularity" +ilscript[0].content[0] "clear_state | guard { input artist | tokenize normalize stem:"BEST" | summary artist | index artist; }" +ilscript[0].content[1] "clear_state | guard { input artistId | summary artistId | attribute artistId; }" +ilscript[0].content[2] "clear_state | guard { input title | tokenize normalize stem:"BEST" | summary title | index title; }" +ilscript[0].content[3] "clear_state | guard { input album | tokenize normalize stem:"BEST" | index album; }" +ilscript[0].content[4] "clear_state | guard { input duration | summary duration; }" +ilscript[0].content[5] "clear_state | guard { input year | summary year | attribute year; }" +ilscript[0].content[6] "clear_state | guard { input popularity | summary popularity | attribute popularity; }" +``` + +Refer to [linguistics](/en/linguistics/linguistics) for more details. + +By default, the indexing chain is set up on the *first* container cluster in *services.xml*. When having multiple container clusters, it is recommended to configure this explicitly, see [multiple container clusters](#multiple-container-clusters). + +## Document selection + +The [document](/en/reference/applications/services/content#document) can have a [selection](/en/reference/writing/document-selector-language) string, normally used to expire documents. This is also evaluated during feeding, so documents that would immediately expire are dropped. This is not an error, the document API will report 200 - but can be confusing. + +The evaluation is done in the [DocumentRouteSelector](https://github.com/vespa-engine/vespa/blob/master/documentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/DocumentRouteSelectorPolicy.java) at the feeding endpoint - *before* any processing/indexing. I.e. the document is evaluated using the selection string (drop it or not), then where to route it, based on document type. + +Example: the selection is configured to not match the document being fed: + +```js +<content id="music" version="1.0"> + <redundancy>1</redundancy> + <documents> + <document type="music" mode="index" selection='music.album == "thisstringwillnotmatch"'/> +``` + +```js +$ vespa-feeder --trace 6 doc.json + +<trace> + [1564576570.693] Source session accepted a 4096 byte message. 1 message(s) now pending. + [1564576570.713] Sequencer sending message with sequence id '-1163801147'. + [1564576570.721] Recognized 'default' as route 'indexing'. + [1564576570.727] Recognized 'indexing' as HopBlueprint(selector = { '[DocumentRouteSelector]' }, recipients = { 'music' }, ignoreResult = false). + [1564576570.811] Running routing policy 'DocumentRouteSelector'. + [1564576570.822] Policy 'DocumentRouteSelector' assigned a reply to this branch. + [1564576570.828] Sequencer received reply with sequence id '-1163801147'. + [1564576570.828] Source session received reply. 0 message(s) now pending. +</trace> + +Messages sent to vespa (route default) : +---------------------------------------- +PutDocument: ok: 0 msgs/sec: 0.00 failed: 0 ignored: 1 latency(min, max, avg): 9223372036854775807, -9223372036854775808, 0 +``` + +Without the selection (i.e. everything matches): + +```js +$ vespa-feeder --trace 6 doc.json + +<trace> + [1564576637.147] Source session accepted a 4096 byte message. 1 message(s) now pending. + [1564576637.168] Sequencer sending message with sequence id '-1163801147'. + [1564576637.176] Recognized 'default' as route 'indexing'. + [1564576637.180] Recognized 'indexing' as HopBlueprint(selector = { '[DocumentRouteSelector]' }, recipients = { 'music' }, ignoreResult = false). + [1564576637.256] Running routing policy 'DocumentRouteSelector'. + [1564576637.268] Component '[MessageType:music]' selected by policy 'DocumentRouteSelector'. + ... +</trace> + +Messages sent to vespa (route default) : +---------------------------------------- +PutDocument: ok: 1 msgs/sec: 1.05 failed: 0 ignored: 0 latency(min, max, avg): 845, 845, 845 +``` + +In the last case, in the [DocumentRouteSelector](https://github.com/vespa-engine/vespa/blob/master/documentapi/src/main/java/com/yahoo/documentapi/messagebus/protocol/DocumentRouteSelectorPolicy.java) routing policy, the document matched the selection string / there was no selection string, and the document was forward to the nex hop in the route. + +## Document processing + +Add custom processing of documents using [document processing](/en/applications/document-processors). The normal use case is to add document processors in the default route, before indexing. Example: + +```js expandable +<services version="1.0"> + + <container id="container" version="1.0"> + <document-api /> + <search /> + <document-processing> + <chain id="default"> + <documentprocessor + id="com.mydomain.example.Rot13DocumentProcessor" + bundle="album-recommendation-docproc" /> + </chain> + </document-processing> + <nodes> + <node hostalias="node1" /> + </nodes> + </container> + + <content id="music" version="1.0"> + <redundancy>1</redundancy> + <documents> + <document >type="music" mode="index" /> + </documents> + <nodes> + <node hostalias="node1" distribution-key="0" /> + </nodes> + </content> + +</services> +``` + +Note that a new hop *default/chain.default* is added, and the default route is changed to include this: + + +```text +$ vespa-route + +There are 6 route(s): + 1. default + 2. default-get + 3. music + 4. music-direct + 5. music-index + 6. storage/cluster.music + +There are 3 hop(s): + 1. default/chain.default + 2. default/chain.indexing + 3. indexing +``` + +```text +$ vespa-route --route default + +The route 'default' has 2 hop(s): + 1. default/chain.default + 2. indexing +``` + + +Note that the document processing chain must be called *default* to automatically be included in the default route. + +### Inherit indexing chain + +An alternative to the above is inheriting the indexing chain - use this when getting this error: + +```text +Indexing cluster 'XX' specifies the chain 'default' as indexing chain. +As the 'default' chain is run by default, using it as the indexing chain will run it twice. +Use a different name for the indexing chain. +``` + +Call the chain something else than *default*, and let it inherit *indexing*: + +```js expandable +<services version="1.0"> + + <container id="container" version="1.0"> + <document-api /> + <search /> + <document-processing> + <chain id="offer-processing" inherits="indexing"> + <documentprocessor id="processor.OfferDocumentProcessor"/> + </chain> + </document-processing> + <nodes> + <node hostalias="node1" /> + </nodes> + </container> + + <content id="music" version="1.0"> + <redundancy>1</redundancy> + <documents> + <document type="offer" mode="index"/> + <document-processing cluster="default" chain="offer-processing"/> + </documents> + <nodes> + <node hostalias="node1" distribution-key="0" /> + </nodes> + </content> + +</services> +``` + +See [#13193](https://github.com/vespa-engine/vespa/issues/13193) for details. + +## Multiple container clusters + +Vespa can be configured to use more than one container cluster. Use cases can be to separate search and document processing or having different document processing clusters due to capacity constraints or dependencies. Example with separate search and feeding/indexing container clusters: + +```js expandable +<services version="1.0"> + + <container id="container-search" version="1.0"> + <search /> + <nodes> + <node hostalias="node1" /> + </nodes> + </container> + + <container id="container-indexing" version="1.0"> + <http> + <server id="httpServer2" port="8081" /> + </http> + <document-api /> + <document-processing /> + <nodes> + <node hostalias="node1" /> + </nodes> + </container> + + <content id="music" version="1.0"> + <redundancy>1</redundancy> + <documents> + <document type="music" mode="index" /> + <document-processing cluster="container-indexing" /> + </documents> + <nodes> + <node hostalias="node1" distribution-key="0" /> + </nodes> + </content> + +</services> +``` + +Notes: + +- The indexing route is explicit using [document-processing](/en/reference/applications/services/content#document-processing) elements from the content to the container cluster +- Set up *document-api* on the same cluster as indexing to avoid network hop from feed endpoint to indexing processors +- If no *document-processing* is configured, it defaults to a container cluster named *default*. When using multiple container clusters, it is best practice to explicitly configure *document-processing*. + +Observe the *container-indexing/chain.indexing* hop, and the indexing chain is set up on the *container-indexing* cluster: + +```text +$ vespa-route + +There are 6 route(s): + 1. default + 2. default-get + 3. music + 4. music-direct + 5. music-index + 6. storage/cluster.music + +There are 2 hop(s): + 1. container-indexing/chain.indexing + 2. indexing +``` + +```js +$ curl -s http://localhost:8081 | python -m json.tool | grep -C 3 chain.indexing + + { + "bundle": "container-disc:7.0.0", + "class": "com.yahoo.messagebus.jdisc.MbusClient", + "id": "chain.indexing@MbusClient", + "serverBindings": [] + }, + { +-- + "class": "com.yahoo.docproc.jdisc.DocumentProcessingHandler", + "id": "com.yahoo.docproc.jdisc.DocumentProcessingHandler", + "serverBindings": [ + "mbus://*/chain.indexing" + ] + }, + { +``` + +## Appendix: trace + +Below is a trace example, no selection string: + +```js expandable +$ cat doc.json +[ +{ + "put": "id:mynamespace:music::123", + "fields": { + "album": "Bad", + "artist": "Michael Jackson", + "title": "Bad", + "year": 1987, + "duration": 247 + } +} +] + +$ vespa-feeder --trace 6 doc.json +<trace> + [1564571762.403] Source session accepted a 4096 byte message. 1 message(s) now pending. + [1564571762.420] Sequencer sending message with sequence id '-1163801147'. + [1564571762.426] Recognized 'default' as route 'indexing'. + [1564571762.429] Recognized 'indexing' as HopBlueprint(selector = { '[DocumentRouteSelector]' }, recipients = { 'music' }, ignoreResult = false). + [1564571762.489] Running routing policy 'DocumentRouteSelector'. + [1564571762.493] Component '[MessageType:music]' selected by policy 'DocumentRouteSelector'. + [1564571762.493] Resolving '[MessageType:music]'. + [1564571762.520] Running routing policy 'MessageType'. + [1564571762.520] Component 'music-index' selected by policy 'MessageType'. + [1564571762.520] Resolving 'music-index'. + [1564571762.520] Recognized 'music-index' as route 'container/chain.indexing [Content:cluster=music]'. + [1564571762.520] Recognized 'container/chain.indexing' as HopBlueprint(selector = { '[LoadBalancer:cluster=container;session=chain.indexing]' }, recipients = { }, ignoreResult = false). + [1564571762.526] Running routing policy 'LoadBalancer'. + [1564571762.538] Component 'tcp/vespa-container:19101/chain.indexing' selected by policy 'LoadBalancer'. + [1564571762.538] Resolving 'tcp/vespa-container:19101/chain.indexing [Content:cluster=music]'. + [1564571762.580] Sending message (version 7.83.27) from client to 'tcp/vespa-container:19101/chain.indexing' with 179.853 seconds timeout. + [1564571762.581] Message (type 100004) received at 'container/container.0' for session 'chain.indexing'. + [1564571762.581] Message received by MbusServer. + [1564571762.582] Request received by MbusClient. + [1564571762.582] Running routing policy 'Content'. + [1564571762.582] Selecting route + [1564571762.582] No cluster state cached. Sending to random distributor. + [1564571762.582] Too few nodes seen up in state. Sending totally random. + [1564571762.582] Component 'tcp/vespa-container:19114/default' selected by policy 'Content'. + [1564571762.582] Resolving 'tcp/vespa-container:19114/default'. + [1564571762.586] Sending message (version 7.83.27) from 'container/container.0' to 'tcp/vespa-container:19114/default' with 179.995 seconds timeout. + [1564571762.587181] Message (type 100004) received at 'storage/cluster.music/distributor/0' for session 'default'. + [1564571762.587245] music/distributor/0 CommunicationManager: Received message from message bus + [1564571762.587510] Communication manager: Sending Put(BucketId(0x2000000000000020), id:mynamespace:music::123, timestamp 1564571762000000, size 275) + [1564571762.587529] Communication manager: Passing message to source session + [1564571762.587547] Source session accepted a 1 byte message. 1 message(s) now pending. + [1564571762.587681] Sending message (version 7.83.27) from 'storage/cluster.music/distributor/0' to 'storage/cluster.music/storage/0/default' with 180.00 seconds timeout. + [1564571762.587960] Message (type 10) received at 'storage/cluster.music/storage/0' for session 'default'. + [1564571762.588052] music/storage/0 CommunicationManager: Received message from message bus + [1564571762.588263] PersistenceThread: Processing message in persistence layer + [1564571762.588953] Communication manager: Sending PutReply(id:mynamespace:music::123, BucketId(0x2000000000000020), timestamp 1564571762000000) + [1564571762.589023] Sending reply (version 7.83.27) from 'storage/cluster.music/storage/0'. + [1564571762.589332] Reply (type 11) received at 'storage/cluster.music/distributor/0'. + [1564571762.589448] Source session received reply. 0 message(s) now pending. + [1564571762.589459] music/distributor/0Communication manager: Received reply from message bus + [1564571762.589679] Communication manager: Sending PutReply(id:music:music::123, BucketId(0x0000000000000000), timestamp 1564571762000000) + [1564571762.589807] Sending reply (version 7.83.27) from 'storage/cluster.music/distributor/0'. + [1564571762.590] Reply (type 200004) received at 'container/container.0'. + [1564571762.590] Routing policy 'Content' merging replies. + [1564571762.590] Reply received by MbusClient. + [1564571762.590] Sending reply from MbusServer. + [1564571762.590] Sending reply (version 7.83.27) from 'container/container.0'. + [1564571762.612] Reply (type 200004) received at client. + [1564571762.613] Routing policy 'LoadBalancer' merging replies. + [1564571762.613] Routing policy 'MessageType' merging replies. + [1564571762.615] Routing policy 'DocumentRouteSelector' merging replies. + [1564571762.622] Sequencer received reply with sequence id '-1163801147'. + [1564571762.622] Source session received reply. 0 message(s) now pending. +</trace> + +Messages sent to vespa (route default) : +---------------------------------------- +PutDocument: ok: 1 msgs/sec: 3.30 failed: 0 ignored: 0 latency(min, max, avg): 225, 225, 225 +``` diff --git a/en/writing/document-v1-api-guide.html b/en/writing/document-v1-api-guide.html deleted file mode 100644 index b68e474825..0000000000 --- a/en/writing/document-v1-api-guide.html +++ /dev/null @@ -1,626 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "/document/v1 API guide" -redirect_from: -- /en/document-v1-api-guide.html ---- - -<p>Use the <em>/document/v1/</em> API to read, write, update and delete documents.</p> -<p> - Refer to the <a href="../reference/api/document-v1.html">document/v1 API reference</a> for API details. - <a href="reads-and-writes.html">Reads and writes</a> has an overview of alternative tools and APIs - as well as the flow through the Vespa components when accessing documents. - See <a href="#getting-started">getting started</a> for how to work with the <em>/document/v1/ API</em>. -</p> -<p>Examples:</p> -<table class="table"> - <tr> - <th>GET</th> - <td> - <p id="get"></p> - <table class="table"> - <tr> - <th>Get</th> - <td> -<pre> -$ curl http://localhost:8080/document/v1/my_namespace/music/docid/love-id-here-to-stay -</pre> - </td> - </tr> - <tr> - <th>Visit</th> - <td> - <a href="visiting.html">Visit</a> all documents with given namespace and document type: -<pre> -$ curl http://localhost:8080/document/v1/namespace/music/docid -</pre> - Visit all documents using continuation: -<pre> -$ curl http://localhost:8080/document/v1/namespace/music/docid?continuation=AAAAEAAAAAAAAAM3AAAAAAAAAzYAAAAAAAEAAAAAAAFAAAAAAABswAAAAAAAAAAA -</pre> - Visit using a <em>selection</em>: -<pre> -$ curl http://localhost:8080/document/v1/namespace/music/docid?selection=music.genre=='blues' -</pre> - Visit documents across all <em>non-global</em> document types and namespaces stored - in content cluster <code>mycluster</code>: -<pre> -$ curl http://localhost:8080/document/v1/?cluster=mycluster -</pre> - Visit documents across all <em><a href="../reference/applications/services/content.html#document">global</a></em> - document types and namespaces stored in content cluster <code>mycluster</code>: -<pre> -$ curl http://localhost:8080/document/v1/?cluster=mycluster&bucketSpace=global -</pre> - Read about <a href="#visiting-throughput">visiting throughput</a> below. - </td> - </tr> - </table> - </td> - </tr><tr> - <th>POST</th> - <td> - <p id="post">Post data in the <a href="../reference/schemas/document-json-format.html">document JSON format</a>.</p> -<pre> -$ curl -X POST -H "Content-Type:application/json" --data ' - { - "fields": { - "artist": "Coldplay", - "album": "A Head Full of Dreams", - "year": 2015 - } - }' \ - http://localhost:8080/document/v1/mynamespace/music/docid/a-head-full-of-dreams -</pre> - </td> -</tr> - -<tr> - <th>PUT</th> - <td> - <p id="put">Do a <a href="partial-updates.html">partial update</a> for a document.</p> -<pre> -$ curl -X PUT -H "Content-Type:application/json" --data ' - { - "fields": { - "artist": { - "assign": "Warmplay" - } - } - }' \ - http://localhost:8080/document/v1/mynamespace/music/docid/a-head-full-of-dreams -</pre> - </td> -</tr><tr> - <th>DELETE</th> - <td> - <p id="delete">Delete a document by ID:</p> -<pre> -$ curl -X DELETE http://localhost:8080/document/v1/mynamespace/music/docid/a-head-full-of-dreams -</pre> - Delete all documents in the <code>music</code> schema: -<pre> -$ curl -X DELETE \ - "http://localhost:8080/document/v1/mynamespace/music/docid?selection=true&cluster=my_cluster" -</pre> - </td> -</tr> -</table> - - - -<h2 id="conditional-writes">Conditional writes</h2> -<p> - A <em>test-and-set</em> <a href="../reference/writing/document-selector-language.html">condition</a> - can be added to Put, Remove and Update operations. Example: -</p> -<pre> -$ curl -X PUT -H "Content-Type:application/json" --data ' - { - "condition": "music.artist==\"Warmplay\"", - "fields": { - "artist": { - "assign": "Coldplay" - } - } - }' \ - http://localhost:8080/document/v1/mynamespace/music/docid/a-head-full-of-dreams -</pre> -{% include important.html content="Use <em>documenttype.fieldname</em> (e.g. music.artist) in the condition, -not only <em>fieldname</em>." %} -<p> - If the condition is not met, a <em>412 Precondition Failed</em> is returned: -</p> -<pre>{% highlight json %} -{ - "pathId": "/document/v1/mynamespace/music/docid/a-head-full-of-dreams", - "id": "id:mynamespace:music::a-head-full-of-dreams", - "message": "[UNKNOWN(251013) @ tcp/vespa-container:19112/default]: ReturnCode(TEST_AND_SET_CONDITION_FAILED, Condition did not match document nodeIndex=0 bucket=20000000000000c4 ) " -} -{% endhighlight %}</pre> -<p> - Also see the <a href="../reference/schemas/document-json-format.html#test-and-set">condition reference</a>. -</p> - - - -<h2 id="create-if-nonexistent">Create if nonexistent</h2> -<p> -</p> -<h3 id="upserts">Upserts</h3> -<p> - Updates to nonexistent documents are supported using - <a href="../reference/schemas/document-json-format.html#create">create</a>. - This is often called an <em>upsert</em> — insert a document if it does not already exist, or update it if it exists. - An empty document is created on the content nodes, before the update is applied. - This simplifies client code in the case of multiple writers. Example: -</p> -<pre> -$ curl -X PUT -H "Content-Type:application/json" --data ' - { - "fields": { - "artist": { - "assign": "Coldplay" - } - } - }' \ - http://localhost:8080/document/v1/mynamespace/music/docid/a-head-full-of-thoughts?create=true -</pre> - - -<h3 id="conditional-updates-and-puts-with-create">Conditional updates and puts with create</h3> -<p> - Conditional updates and puts can be combined with <a href="../reference/schemas/document-json-format.html#create">create</a>. - This has the following semantics: -</p> -<ul> - <li>If the document already exists, the condition is evaluated against the most recent document version available. - The operation is applied if (and only if) the condition matches.</li> - <li>Otherwise (i.e. the document does not exist or the newest document version is a tombstone), - the condition is <em>ignored</em> and the operation is applied as if no condition was provided.</li> -</ul> -<p> - You can use conditional puts to handle out-of-order writes. Say you have a <code>version</code> field - in your source-of-truth system. You can use it to only write a document if it doesn't exist or if the - version is older than the one you're trying to write: -</p> -<pre> -$ curl -X POST -H "Content-Type:application/json" --data ' - { - "condition": "music.version < 42", - "create": true, - "fields": { - "version": 42, - "artist": "Coldplay" - } - }' \ - http://localhost:8080/document/v1/mynamespace/music/docid/a-head-full-of-thoughts -</pre> -<p> - Similarly, updates can depend on the version being older. Add <code>create=true</code> to make them upserts. -</p> -<pre> - $ curl -X PUT -H "Content-Type:application/json" --data ' - { - "condition": "music.version < 43", - "create": true, - "fields": { - "version": { - "assign": 43 - }, - "artist": { - "assign": "Warmplay" - } - } - }' \ - http://localhost:8080/document/v1/mynamespace/music/docid/a-head-full-of-thoughts -</pre> -<!-- ToDo: rewrite this / move to elasticity details ... - What does it mean that a document is "missing"? - When does this happen? - Is it likely to happen, so we need to care about this? - Where can I read more? --> -{% include warning.html content="If all existing replicas of a document are missing - when an operation with <code>\"create\": true</code> is executed, a new document will always be created. - This happens even if a condition has been given. - If the existing replicas become available later, - their version of the document will be overwritten by the newest update since it has a higher timestamp." %} - -{% include note.html content="See <a href='../schemas/documents.html#document-expiry'>document expiry</a> - for auto-created documents — it is possible to create documents that do not match the selection criterion." %} - -{% include note.html content="Specifying <em>create</em> for a Put operation <em>without</em> a - condition has no observable effect, as unconditional Put operations will always write - a new version of a document regardless of whether it existed already." %} - -<h2 id="data-dump">Data dump</h2> -<p> - To iterate over documents, use <a href="visiting.html">visiting</a> — sample output: -</p> -<pre>{% highlight json %} -{ - "pathId": "/document/v1/namespace/doc/docid", - "documents": [ - { - "id": "id:namespace:doc::id-1", - "fields": { - "title": "Document title 1", - } - } - ], - "continuation": "AAAAEAAAAAAAAAM3AAAAAAAAAzYAAAAAAAEAAAAAAAFAAAAAAABswAAAAAAAAAAA" -} -{% endhighlight %}</pre> -<p> - Note the <em>continuation</em> token — use this in the next request for more data. - Below is a sample script dumping all data using <a href="https://stedolan.github.io/jq/">jq</a> for JSON parsing. - It splits the corpus in 8 slices by default; - using a number of slices at least four times the number of container nodes is recommended for high throughput. - Timeout can be set lower for benchmarking. - (Each request has a maximum timeout of 60s to ensure progress is saved at regular intervals) -</p> -<pre>{% highlight sh %} -#!/bin bash -set -eo pipefail - -if [ $# -gt 2 ] -then - echo "Usage: $0 [number of slices, default 8] [timeout in seconds, default 31536000 (1 year)]" - exit 1 -fi - -endpoint="https://my.vespa.endpoint" -cluster="db" -selection="true" -slices="${1:-8}" -timeout="${2:-31516000}" -curlTimeout="$((timeout > 60 ? 60 : timeout))" -url="$endpoint/document/v1/?cluster=$cluster&selection=$selection&stream=true&timeout=$curlTimeout&concurrency=8&slices=$slices" -auth="--key my-key --cert my-cert -H 'Authorization: my-auth'" -curl="curl -sS $auth" -start=$(date '+%s') -doom=$((start + timeout)) - -## auth can be something like auth='--key data-plane-private-key.pem --cert data-plane-public-cert.pem' -curl="curl -sS $auth" - -function visit { - sliceId="$1" - documents=0 - continuation="" - while - printf -v filename "data-%03g-%012g.json.gz" $sliceId $documents - json="$(eval "$curl '$url&sliceId=$sliceId$continuation'" | tee >( gzip > $filename ) | jq '{ documentCount, continuation, message }')" - message="$(jq -re .message <<< $json)" && echo "Failed visit for sliceId $sliceId: $message" >&2 && exit 1 - documentCount="$(jq -re .documentCount <<< $json)" && ((documents += $documentCount)) - [ "$(date '+%s')" -lt "$doom" ] && token="$(jq -re .continuation <<< $json)" - do - echo "$documentCount documents retrieved from slice $sliceId; continuing at $token" - continuation="&continuation=$token" - done - time=$(($(date '+%s') - start)) - echo "$documents documents total retrieved in $time seconds ($((documents / time)) docs/s) from slice $sliceId" >&2 -} - -for ((sliceId = 0; sliceId < slices; sliceId++)) -do - visit $sliceId & -done -wait -{% endhighlight %}</pre> - - -<h3 id="visiting-throughput">Visiting throughput</h3> -<p> - Note that visit with selection is a linear scan over all the music documents - in the request examples at the start of this guide. - Each complete visit thus requires the selection expression to be evaluated for all documents. - Running concurrent visits with selections that match disjoint subsets of the document corpus - is therefore a poor way of increasing throughput, - as work is duplicated across each such visit. - Fortunately, the API offers other options for increasing throughput: -</p> -<ul> - <li> - Split the corpus into any number of smaller <a href="../reference/api/document-v1.html#slices">slices</a>, - each to be visited by a separate, independent series of HTTP requests. - This is by far the most effective setting to change, - as it allows visiting through all HTTP containers simultaneously, - and from any number of clients—either of which is - typically the bottleneck for visits through <em>/document/v1</em>. - A good value for this setting is at least a handful per container. - </li> - <li> - Increase backend <a href="../reference/api/document-v1.html#concurrency">concurrency</a> - so each visit HTTP response is promptly filled with documents. - When using this together with slicing (above), - take care to also stream the HTTP responses (below), - to avoid buffering too much data in the container layer. - When a high number of slices is specified, this setting may have no effect. - </li> - <li> - <a href="../reference/api/document-v1.html#stream">Stream</a> the HTTP responses. - This lets you receive data earlier, and more of it per request, reducing HTTP overhead. - It also minimizes memory usage due to buffering in the container, - allowing higher concurrency per container. - It is recommended to always use this, but the default is not to, due to backwards compatibility. - </li> -</ul> - - - -<h2 id="getting-started">Getting started</h2> -<p> - Pro-tip: It is easy to generate a <code>/document/v1</code> request by using the <a href="../clients/vespa-cli.html">Vespa CLI</a>, - with the <code>-v</code> option to output a generated <code>/document/v1</code> request - example: -</p> -<pre> -$ vespa document -v ext/A-Head-Full-of-Dreams.json - - <span class="pre-hilite">curl -X POST -H 'Content-Type: application/json'</span> - <span class="pre-hilite">--data-binary @ext/A-Head-Full-of-Dreams.json</span> - <span class="pre-hilite">http://127.0.0.1:8080/document/v1/mynamespace/music/docid/a-head-full-of-dreams</span> - - Success: put id:mynamespace:music::a-head-full-of-dreams -</pre> -<p> - See the <a href="../reference/schemas/document-json-format.html">document JSON format</a> for creating JSON payloads. -</p> -<p> - This is a quick guide into dumping random documents from a cluster to get started: -</p> -<ol> - <li> - To get documents from a cluster, - look up the content cluster name from the configuration, - like in the <a href="https://github.com/vespa-engine/sample-apps/blob/master/album-recommendation/app/services.xml"> - album-recommendation</a> example: <code><content id="music" version="1.0"></code>. - </li> - <li> - Use the cluster name to start dumping document IDs (skip <code>jq</code> for full json): - <pre> -$ curl -s 'http://localhost:8080/document/v1/?<span class="pre-hilite">cluster=music</span>&wantedDocumentCount=10&timeout=60s' | \ - jq -r .documents[].id -</pre> - <pre> -id:mynamespace:music::love-is-here-to-stay -id:mynamespace:music::a-head-full-of-dreams -id:mynamespace:music::hardwired-to-self-destruct -</pre> - <code>wantedDocumentCount</code> is useful to let the operation run longer to find documents, - to avoid an empty result. - This operation is a scan through the corpus, - and it is normal to get empty result and the <a href="#data-dump">continuation token</a>. - </li> - <li> - Look up the document with id <code>id:mynamespace:music::love-is-here-to-stay</code>: - <pre> -$ curl -s 'http://localhost:8080/document/v1/mynamespace/music/docid/love-is-here-to-stay' | jq . -</pre> - <pre>{% highlight json %} -{ - "pathId": "/document/v1/mynamespace/music/docid/love-is-here-to-stay", - "id": "id:mynamespace:music::love-is-here-to-stay", - "fields": { - "artist": "Diana Krall", - "year": 2018, - "category_scores": { - "type": "tensor<float>(cat{})", - "cells": { - "pop": 0.4000000059604645, - "rock": 0, - "jazz": 0.800000011920929 - } - }, - "album": "Love Is Here To Stay" - } -} -{% endhighlight %}</pre> - </li> - <li> - Read more about <a href="../schemas/documents.html">document IDs</a>. - </li> -</ol> - - -<h2 id="troubleshooting">Troubleshooting</h2> -<ul> - <li> <!-- ToDo: does not belong here, find a better place to put it ... --> - <p id="export-documents"> - When troubleshooting documents not found using the query API, - use <a href="../clients/vespa-cli.html#documents">vespa visit</a> to export the documents. - Then compare the <code>id</code> field with other user-defined <code>id</code> fields in the query. - </p> -<pre> -$ vespa visit -</pre> -<pre>{% highlight json %} -{ - "id": "id:mynamespace:music::when-we-all-fall-asleep-where-do-we-go", - "fields": { - "artist": "Billie Eilish", - "doc_id": 12345 - } -} -{% endhighlight %}</pre> - <p>Find more details on the components of the <a href="../schemas/documents.html#id-scheme">document ID</a>.</p> - </li> - <li> - <p id="document-not-found">Document not found responses look like:</p> -<pre> -$ curl http://127.0.0.1:8080/document/v1/mynamespace/music/docid/non-existing-doc -</pre> -<pre>{% highlight json %} -{ - "pathId": "/document/v1/mynamespace/music/docid/non-existing-doc", - "id": "id:mynamespace:music::non-existing-doc" -} -{% endhighlight %}</pre> - <p>This might look like an empty document, use <code>-v</code> for more output:</p> -<pre> -$ curl -v http://127.0.0.1:8080/document/v1/mynamespace/music/docid/non-existing-doc - -> GET /document/v1/mynamespace/music/docid/non-existing-doc HTTP/1.1 -> Host: 127.0.0.1:8080 -> User-Agent: curl/7.88.1 -> Accept: */* -> -< HTTP/1.1 404 Not Found -< Date: Fri, 26 May 2023 08:53:20 GMT -< Content-Type: application/json;charset=utf-8 -< Content-Length: 108 -</pre> -<pre>{% highlight json %} -{ - "pathId": "/document/v1/mynamespace/music/docid/non-existing-doc", - "id": "id:mynamespace:music::non-existing-doc" -} -{% endhighlight %}</pre> - <p> - Observe the <em>404 Not Found</em>. - Using the <a href="../clients/vespa-cli.html#documents">Vespa CLI</a> is great for troubleshooting - use - <code>-v</code> for verbose output, this prints an equivalent <code>curl</code> command: - </p> -<pre> -$ vespa document get -v id:mynamespace:music::non-existing-doc -curl -X GET http://127.0.0.1:8080/document/v1/mynamespace/music/docid/non-existing-doc -Error: Invalid document operation: 404 Not Found -</pre> -<pre>{% highlight json %} -{ - "pathId": "/document/v1/mynamespace/music/docid/non-existing-doc", - "id": "id:mynamespace:music::non-existing-doc" -} -{% endhighlight %}</pre> - </li> - <li> - <p id="query-result-id">Query results can have results like:</p> -<pre>{% highlight json %} -{ - "id": "index:mydoctype/3/399f8030300282ca93929939", - "relevance": 0, - "source": "test", - "fields": { - "sddocname": "testdoc", - "myfield": 12 - } -} -{% endhighlight %}</pre> - <p> - The <a href="../reference/querying/default-result-format.html#id">query result IDs</a> are not necessarily document IDs. - See <a href="../schemas/documents.html#docid-in-results">Document IDs in search results</a> - for how to ensure that document IDs are returned in search results. - </p> - </li> - <li> - <p id="delete-all-documents">Delete <em>all</em> documents in <em>music</em> schema, with security credentials:</p> -<pre> -$ curl -X DELETE \ - --cert data-plane-public-cert.pem --key data-plane-private-key.pem \ - "http://localhost:8080/document/v1/mynamespace/music/docid?selection=true&cluster=my_cluster" -</pre> - </li> - -</ul> - -<h2 id="request-size-limit">Request size limit</h2> -<p> -Starting from version 8.577.16, Vespa returns 413 (Content too large) as a response to POST and PUT requests that are above the request size limit. -To avoid this, automatically check document size and truncate or split large documents before feeding. -For optimal performance, it is recommended to keep the document size below 10 MB. -</p> - -<h2 id="backpressure">Backpressure</h2> -<p> - Vespa returns response code 429 (Too Many Requests) as a backpressure signal whenever client feed throughput exceeds system capacity. - Clients should implement retry strategies as described in the <a href="../cloud/http-best-practices.html">HTTP best practices</a> document. -</p> -<p> - Instead of implementing your own retry logic, consider using Vespa's feed clients which automatically handle retries and backpressure. - See the <a href="../clients/vespa-cli.html#documents">feed command</a> of the Vespa CLI and the <a href="../clients/vespa-feed-client.html">vespa-feed-client</a>. -</p> -<p> - The <code>/document/v1</code> API includes a configurable operation queue that by default is tuned to balance latency, throughput and memory. - Applications can adjust this balance by overriding the parameters defined in the - <a href="https://github.com/vespa-engine/vespa/blob/master/configdefinitions/src/vespa/document-operation-executor.def">document-operation-executor</a> - config definition. -</p> -<p> - To optimize for higher throughput at the cost of increased latency and higher memory usage on the container, - increase any of the <code>maxThrottled</code> (maximum queue capacity in number of operations), - <code>maxThrottledAge</code> (maximum time in queue in seconds), - and <code>maxThrottledBytes</code> (maximum memory usage in bytes) parameters. - This allows the container to buffer more operations during temporary spikes in load, - reducing the number of 429 responses while increasing request latency. - Make sure to increase operation and client timeouts to accommodate for the increased latency. -</p> -<p> - See the <a href="https://github.com/vespa-engine/vespa/blob/master/configdefinitions/src/vespa/document-operation-executor.def"> - config definition</a> for a detailed explanation of each parameter. -</p> -<p> - Set the values to <code>0</code> for the opposite effect, i.e. to optimize for latency. - Operations will be dispatched directly, - and failed out immediately if the number of pending operations exceeds the dynamic window size of the document processing pipeline. -</p> -<p> - <em>Example: overriding the default value of all 3 parameters to <code>0</code>.</em> -</p> -<pre> -<container id="feed" version="1.0"> - <document-api/> - - <config name="com.yahoo.document.restapi.document-operation-executor"> - <maxThrottled>0</maxThrottled> - <maxThrottledAge>0</maxThrottledAge> - <maxThrottledBytes>0</maxThrottledBytes> - </config> - -</container> -</pre> -<p> - The effective operation queue configuration is logged when the container starts up, see below example. -</p> -<pre> -INFO container Container.com.yahoo.document.restapi.resource.DocumentV1ApiHandler Operation queue: max-items=256, max-age=3000 ms, max-bytes=100 MB -</pre> -<p> - You can observe the state of the operation queue through the metrics - <code><a href="../reference/operations/metrics/container.html#httpapi_queued_operations">httpapi_queued_operations</a></code>, - <code><a href="../reference/operations/metrics/container.html#httpapi_queued_bytes">httpapi_queued_bytes</a></code> and - <code><a href="../reference/operations/metrics/container.html#httpapi_queued_age">httpapi_queued_age</a></code>. -</p> - -<h2 id="using-number-and-group-id-modifiers">Using number and group id modifiers</h2> -<p> - Do not use group or number modifiers with regular indexed mode document types. - These are special cases that only work as expected for document types - with <a href="../reference/applications/services/content.html#document">mode=streaming or mode=store-only</a>. - Examples: -</p> -<table class="table"> - <tr> - <th>Get</th> - <td> - Get a document in a group: -<pre> -$ curl http://localhost:8080/document/v1/mynamespace/music/number/23/some_key -</pre> -<pre> -$ curl http://localhost:8080/document/v1/mynamespace/music/group/mygroupname/some_key -</pre> - </td> - </tr> - <tr> - <th>Visit</th> - <td> - Visit all documents for a group: -<pre> -$ curl http://localhost:8080/document/v1/namespace/music/number/23/ -</pre> -<pre> -$ curl http://localhost:8080/document/v1/namespace/music/group/mygroupname/ -</pre> - </td> - </tr> -</table> diff --git a/en/writing/document-v1-api-guide.mdx b/en/writing/document-v1-api-guide.mdx new file mode 100644 index 0000000000..405d142bbf --- /dev/null +++ b/en/writing/document-v1-api-guide.mdx @@ -0,0 +1,463 @@ +--- +title: "/document/v1 API guide" +sidebarTitle: "/document/v1" +--- + +Use the */document/v1/* API to read, write, update and delete documents. + +Refer to the [document/v1 API reference](/en/reference/api/document-v1) for API details. [Reads and writes](/en/writing/reads-and-writes) has an overview of alternative tools and APIs as well as the flow through the Vespa components when accessing documents. See [getting started](#getting-started) for how to work with the */document/v1/ API*. + +<table className="vespa-table"> + <thead> + <tr> + <th></th> + <th></th> + </tr> + </thead> + <tbody> + <tr> + <td>GET</td> + <td>**Get**<br/><br/><code>{`$ curl http://localhost:8080/document/v1/my_namespace/music/docid/love-id-here-to-stay`}</code><br/><br/>**Visit** <br/><br/><a href="/en/writing/visiting" aria-label="Visit">Visit</a> all documents with given namespace and document type:<br/><br/><code>{`$ curl http://localhost:8080/document/v1/namespace/music/docid`}</code><br/><br/>Visit all documents using continuation:<br/><br/><code>{`$ curl http://localhost:8080/document/v1/namespace/music/docid?continuation=AAAAEAAAAAAAAAM3AAAAAAAAAzYAAAAAAAEAAAAAAAFAAAAAAABswAAAAAAAAAAA`}</code><br/><br/>Visit using a *selection*:<br/><br/><code>{`$ curl http://localhost:8080/document/v1/namespace/music/docid?selection=music.genre=='blues'`}</code><br/><br/>Visit documents across all *non-global* document types and namespaces in content cluster <code>{`mycluster`}</code>:<br/><br/><code>{`$ curl http://localhost:8080/document/v1/?cluster=mycluster`}</code><br/><br/>Visit documents across all *<a href="/en/reference/applications/services/content#document" aria-label="global">global</a>* document types and namespaces stored in content cluster <code>{`mycluster`}</code>:<br/><br/><code>{`$ curl http://localhost:8080/document/v1/?cluster=mycluster&bucketSpace=global`}</code><br/><br/>Read about <a href="#visiting-throughput" aria-label="visiting throughput">visiting throughput</a> below.</td> + </tr> + <tr> + <td>**POST**</td> + <td>Post data in the <a href="/en/reference/schemas/document-json-format" aria-label="document JSON format">document JSON format</a>:<br/><br/><code>{`$ curl -X POST -H "Content-Type:application/json" --data '`}</code><br/><code>{`{`}</code><br/><code>{`"fields": {`}</code><br/><code>{`"artist": "Coldplay",`}</code><br/><code>{`"album": "A Head Full of Dreams",`}</code><br/><code>{`"year": 2015`}</code><br/><code>{`}`}</code><br/><code>{`}' \\`}</code><br/><code>{`http://localhost:8080/document/v1/mynamespace/music/docid/a-head-full-of-dreams`}</code></td> + </tr> + <tr> + <td>**PUT**</td> + <td>Do a <a href="/en/writing/partial-updates" aria-label="partial update">partial update</a> for a document:<br/><br/><code>{`$ curl -X PUT -H "Content-Type:application/json" --data '`}</code><br/><code>{`{`}</code><br/><code>{`"fields":`}</code> <br/><code>{`{`}</code><br/><code>{`"artist":`}</code> <br/><code>{`{`}</code><br/><code>{`"assign": "Warmplay"`}</code><br/><code>{`}`}</code><br/><code>{`}`}</code><br/><code>{`}' \\`}</code><br/><code>{`http://localhost:8080/document/v1/mynamespace/music/docid/a-head-full-of-dreams`}</code></td> + </tr> + <tr> + <td>**DELETE**</td> + <td>Delete a document by ID:<br/><br/><code>{`$ curl -X DELETE http://localhost:8080/document/v1/mynamespace/music/docid/a-head-full-of-dreams`}</code><br/><br/>Delete all documents in the <code>{`music`}</code> schema:<br/><br/><code>{`$ curl -X DELETE \\`}</code><br/><code>{`"http://localhost:8080/document/v1/mynamespace/music/docid?selection=true&cluster=my_cluster"`}</code></td> + </tr> + </tbody> +</table> + +## Conditional writes + +A *test-and-set* [condition](/en/reference/writing/document-selector-language) can be added to Put, Remove and Update operations: + +Example: + +```sh +$ curl -X PUT -H "Content-Type:application/json" --data ' + { + "condition": "music.artist==\"Warmplay\"", + "fields": { + "artist": { + "assign": "Coldplay" + } + } + }' \ + http://localhost:8080/document/v1/mynamespace/music/docid/a-head-full-of-dreams +``` + +<Warning> +**Important:** + +*Use documenttype.fieldname (e.g. music.artist) in the condition, not only fieldname.* +</Warning> + +If the condition is not met, a 412 Precondition Failed is returned: + +```json +{ + "pathId": "/document/v1/mynamespace/music/docid/a-head-full-of-dreams", + "id": "id:mynamespace:music::a-head-full-of-dreams", + "message": "[UNKNOWN(251013) @ tcp/vespa-container:19112/default]: ReturnCode(TEST_AND_SET_CONDITION_FAILED, Condition did not match document nodeIndex=0 bucket=20000000000000c4 ) " +} +``` + +Also see the [condition reference](/en/reference/schemas/document-json-format#test-and-set). + +## Create if nonexistent + +### Upserts + +Updates to nonexistent documents are supported using [create](/en/reference/schemas/document-json-format#create). This is often called an *upsert* — insert a document if it does not already exist, or update it if it exists. An empty document is created on the content nodes, before the update is applied. This simplifies client code in the case of multiple writers. Example: + +```sh +$ curl -X PUT -H "Content-Type:application/json" --data ' + { + "fields": { + "artist": { + "assign": "Coldplay" + } + } + }' \ + http://localhost:8080/document/v1/mynamespace/music/docid/a-head-full-of-thoughts?create=true +``` + +### Conditional updates and puts with create + +Conditional updates and puts can be combined with [create](/en/reference/schemas/document-json-format#create). This has the following semantics: + +- If the document already exists, the condition is evaluated against the most recent document version available. The operation is applied if (and only if) the condition matches. +- Otherwise (i.e. the document does not exist or the newest document version is a tombstone), the condition is *ignored* and the operation is applied as if no condition was provided. + +You can use conditional puts to handle out-of-order writes. Say you have a `version` field in your source-of-truth system. You can use it to only write a document if it doesn't exist or if the version is older than the one you're trying to write: + + +```sh +$ curl -X POST -H "Content-Type:application/json" --data ' + { + "condition": "music.version < 42", + "create": true, + "fields": { + "version": 42, + "artist": "Coldplay" + } + }' \ + http://localhost:8080/document/v1/mynamespace/music/docid/a-head-full-of-thoughts +``` + +Similarly, updates can depend on the version being older. Add `create=true` to make them upserts. + +```sh + $ curl -X PUT -H "Content-Type:application/json" --data ' + { + "condition": "music.version < 43", + "create": true, + "fields": { + "version": { + "assign": 43 + }, + "artist": { + "assign": "Warmplay" + } + } + }' \ + http://localhost:8080/document/v1/mynamespace/music/docid/a-head-full-of-thoughts +``` + +<Danger> +**Warning:** + +If all existing replicas of a document are missing when an operation with `"create": true` is executed, a new document will always be created. This happens even if a condition has been given. If the existing replicas become available later, their version of the document will be overwritten by the newest update since it has a higher timestamp. +</Danger> + +<Note> +**Note:** + +See [document expiry](/en/schemas/documents#document-expiry) for auto-created documents — it is possible to create documents that do not match the selection criterion. +</Note> + +<Note> +**Note:** + +Specifying *create* for a Put operation *without* a condition has no observable effect, as unconditional Put operations will always write a new version of a document regardless of whether it existed already. +</Note> + +## Data dump + +To iterate over documents, use [visiting](/en/writing/visiting) — sample output: + +```json +{ + "pathId": "/document/v1/namespace/doc/docid", + "documents": [ + { + "id": "id:namespace:doc::id-1", + "fields": { + "title": "Document title 1" + } + } + ], + "continuation": "AAAAEAAAAAAAAAM3AAAAAAAAAzYAAAAAAAEAAAAAAAFAAAAAAABswAAAAAAAAAAA" +} +``` + +Note the *continuation* token — use this in the next request for more data. Below is a sample script dumping all data using [jq](https://stedolan.github.io/jq/) for JSON parsing. It splits the corpus in 8 slices by default; using a number of slices at least four times the number of container nodes is recommended for high throughput. Timeout can be set lower for benchmarking. (Each request has a maximum timeout of 60s to ensure progress is saved at regular intervals) + +```sh expandable +#!/bin bash +set -eo pipefail + +if [ $# -gt 2 ] +then + echo "Usage: $0 [number of slices, default 8] [timeout in seconds, default 31536000 (1 year)]" + exit 1 +fi + +endpoint="https://my.vespa.endpoint" +cluster="db" +selection="true" +slices="${1:-8}" +timeout="${2:-31516000}" +curlTimeout="$((timeout > 60 ? 60 : timeout))" +url="$endpoint/document/v1/?cluster=$cluster&selection=$selection&stream=true&timeout=$curlTimeout&concurrency=8&slices=$slices" +auth="--key my-key --cert my-cert -H 'Authorization: my-auth'" +curl="curl -sS $auth" +start=$(date '+%s') +doom=$((start + timeout)) + +## auth can be something like auth='--key data-plane-private-key.pem --cert data-plane-public-cert.pem' +curl="curl -sS $auth" + +function visit { + sliceId="$1" + documents=0 + continuation="" + while + printf -v filename "data-%03g-%012g.json.gz" $sliceId $documents + json="$(eval "$curl '$url&sliceId=$sliceId$continuation'" | tee >( gzip > $filename ) | jq '{ documentCount, continuation, message }')" + message="$(jq -re .message <<< $json)" && echo "Failed visit for sliceId $sliceId: $message" >&2 && exit 1 + documentCount="$(jq -re .documentCount <<< $json)" && ((documents += $documentCount)) + [ "$(date '+%s')" -lt "$doom" ] && token="$(jq -re .continuation <<< $json)" + do + echo "$documentCount documents retrieved from slice $sliceId; continuing at $token" + continuation="&continuation=$token" + done + time=$(($(date '+%s') - start)) + echo "$documents documents total retrieved in $time seconds ($((documents / time)) docs/s) from slice $sliceId" >&2 +} + +for ((sliceId = 0; sliceId < slices; sliceId++)) +do + visit $sliceId & +done +wait +``` + + +### Visiting throughput + +Note that visit with selection is a linear scan over all the music documents in the request examples at the start of this guide. Each complete visit thus requires the selection expression to be evaluated for all documents. Running concurrent visits with selections that match disjoint subsets of the document corpus is therefore a poor way of increasing throughput, as work is duplicated across each such visit. Fortunately, the API offers other options for increasing throughput: + +- Split the corpus into any number of smaller [slices](/en/reference/api/document-v1#slices), each to be visited by a separate, independent series of HTTP requests. This is by far the most effective setting to change, as it allows visiting through all HTTP containers simultaneously, and from any number of clients—either of which is typically the bottleneck for visits through */document/v1*. A good value for this setting is at least a handful per container. +- Increase backend [concurrency](/en/reference/api/document-v1#concurrency) so each visit HTTP response is promptly filled with documents. When using this together with slicing (above), take care to also stream the HTTP responses (below), to avoid buffering too much data in the container layer. When a high number of slices is specified, this setting may have no effect. +- [Stream](/en/reference/api/document-v1#stream) the HTTP responses. This lets you receive data earlier, and more of it per request, reducing HTTP overhead. It also minimizes memory usage due to buffering in the container, allowing higher concurrency per container. It is recommended to always use this, but the default is not to, due to backwards compatibility. + +## Getting started + +<Tip> +**Pro-tip:** + +It is easy to generate a `/document/v1` request by using the [Vespa CLI](/en/clients/vespa-cli), with the `-v` option to output a generated `/document/v1` request - example: +</Tip> + +```sh +$ vespa document -v ext/A-Head-Full-of-Dreams.json + + curl -X POST -H 'Content-Type: application/json' + --data-binary @ext/A-Head-Full-of-Dreams.json + http://127.0.0.1:8080/document/v1/mynamespace/music/docid/a-head-full-of-dreams + + Success: put id:mynamespace:music::a-head-full-of-dreams +``` + +See the [document JSON format](/en/reference/schemas/document-json-format) for creating JSON payloads. + +This is a quick guide into dumping random documents from a cluster to get started: + +<Steps> +<Step> +To get documents from a cluster, look up the content cluster name from the configuration, like in the [album-recommendation](https://github.com/vespa-engine/sample-apps/blob/master/album-recommendation/app/services.xml) example: `<content id="music" version="1.0">`. +</Step> +<Step> +Use the cluster name to start dumping document IDs (skip `jq` for full json): + +```bash +$ curl -s 'http://localhost:8080/document/v1/?cluster=music&wantedDocumentCount=10&timeout=60s' | \ + jq -r .documents[].id +``` + +```bash +id:mynamespace:music::love-is-here-to-stay +id:mynamespace:music::a-head-full-of-dreams +id:mynamespace:music::hardwired-to-self-destruct +``` + +`wantedDocumentCount` is useful to let the operation run longer to find documents, to avoid an empty result. This operation is a scan through the corpus, and it is normal to get empty result and the [continuation token](#data-dump). +</Step> +<Step> + +Look up the document with id `id:mynamespace:music::love-is-here-to-stay`: + +```bash +$ curl -s 'http://localhost:8080/document/v1/mynamespace/music/docid/love-is-here-to-stay' | jq . +``` + +```json +{ + "pathId": "/document/v1/mynamespace/music/docid/love-is-here-to-stay", + "id": "id:mynamespace:music::love-is-here-to-stay", + "fields": { + "artist": "Diana Krall", + "year": 2018, + "category_scores": { + "type": "tensor<float>(cat{})", + "cells": { + "pop": 0.4000000059604645, + "rock": 0, + "jazz": 0.800000011920929 + } + }, + "album": "Love Is Here To Stay" + } +} +``` + +</Step> +<Step> + +Read more about [document IDs](/en/schemas/documents). + +</Step> +</Steps> + +## Troubleshooting + +- When troubleshooting documents not found using the query API, use [vespa visit](/en/clients/vespa-cli#documents) to export the documents. Then compare the `id` field with other user-defined `id` fields in the query. + ```bash + $ vespa visit + ``` + + ```json + { + "id": "id:mynamespace:music::when-we-all-fall-asleep-where-do-we-go", + "fields": { + "artist": "Billie Eilish", + "doc_id": 12345 + } + } + ``` + Find more details on the components of the [document ID](/en/schemas/documents#id-scheme). +- Document not found responses look like: + ```bash + $ curl http://127.0.0.1:8080/document/v1/mynamespace/music/docid/non-existing-doc + ``` + + ```json + { + "pathId": "/document/v1/mynamespace/music/docid/non-existing-doc", + "id": "id:mynamespace:music::non-existing-doc" + } + ``` + + This might look like an empty document, use `-v` for more output: + + ```bash + $ curl -v http://127.0.0.1:8080/document/v1/mynamespace/music/docid/non-existing-doc + > GET /document/v1/mynamespace/music/docid/non-existing-doc HTTP/1.1 + > Host: 127.0.0.1:8080 + > User-Agent: curl/7.88.1 + > Accept: */* + > + < HTTP/1.1 404 Not Found + < Date: Fri, 26 May 2023 08:53:20 GMT + < Content-Type: application/json;charset=utf-8 + < Content-Length: 108 + ``` + + ```json + { + "pathId": "/document/v1/mynamespace/music/docid/non-existing-doc", + "id": "id:mynamespace:music::non-existing-doc" + } + ``` + + Observe the *404 Not Found*. Using the [Vespa CLI](/en/clients/vespa-cli#documents) is great for troubleshooting - use `-v` for verbose output, this prints an equivalent `curl` command: + + ```bash + $ vespa document get -v id:mynamespace:music::non-existing-doc + curl -X GET http://127.0.0.1:8080/document/v1/mynamespace/music/docid/non-existing-doc + ``` + + Error: Invalid document operation: 404 Not Found + + ```json + { + "pathId": "/document/v1/mynamespace/music/docid/non-existing-doc", + "id": "id:mynamespace:music::non-existing-doc" + } + ``` +- Query results can have results like: + + ```json + { + "id": "index:mydoctype/3/399f8030300282ca93929939", + "relevance": 0, + "source": "test", + "fields": { + "sddocname": "testdoc", + "myfield": 12 + } + } + ``` + + [Query result IDs](/en/reference/querying/default-result-format#id) are not the same as document IDs. Use a separate field for the document ID, if needed. + +- Delete *all* documents in *music* schema, with security credentials: + + ```bash + $ curl -X DELETE \\ + --cert data-plane-public-cert.pem --key data-plane-private-key.pem \\ + "http://localhost:8080/document/v1/mynamespace/music/docid?selection=true&cluster=my_cluster" + ``` + + +## Request size limit + +Starting from version 8.577.16, Vespa returns 413 (Content too large) as a response to POST and PUT requests that are above the request size limit. To avoid this, automatically check document size and truncate or split large documents before feeding. For optimal performance, it is recommended to keep the document size below 10 MB. + +## Backpressure + +Vespa returns response code 429 (Too Many Requests) as a backpressure signal whenever client feed throughput exceeds system capacity. Clients should implement retry strategies as described in the [HTTP best practices](/en/clients/http-best-practices) document. + +Instead of implementing your own retry logic, consider using Vespa's feed clients which automatically handle retries and backpressure. See the [feed command](/en/clients/vespa-cli#documents) of the Vespa CLI and the [vespa-feed-client](/en/clients/vespa-feed-client). + +The `/document/v1` API includes a configurable operation queue that by default is tuned to balance latency, throughput and memory. Applications can adjust this balance by overriding the parameters defined in the [document-operation-executor](https://github.com/vespa-engine/vespa/blob/master/configdefinitions/src/vespa/document-operation-executor.def) config definition. + +To optimize for higher throughput at the cost of increased latency and higher memory usage on the container, increase any of the `maxThrottled` (maximum queue capacity in number of operations), `maxThrottledAge` (maximum time in queue in seconds), and `maxThrottledBytes` (maximum memory usage in bytes) parameters. This allows the container to buffer more operations during temporary spikes in load, reducing the number of 429 responses while increasing request latency. Make sure to increase operation and client timeouts to accommodate for the increased latency. + +See the [config definition](https://github.com/vespa-engine/vespa/blob/master/configdefinitions/src/vespa/document-operation-executor.def) for a detailed explanation of each parameter. + +Set the values to `0` for the opposite effect, i.e. to optimize for latency. Operations will be dispatched directly, and failed out immediately if the number of pending operations exceeds the dynamic window size of the document processing pipeline. + +*Example: overriding the default value of all 3 parameters to `0`.* + +```js +<container id="feed" version="1.0"> + <document-api/> + + <config name="com.yahoo.document.restapi.document-operation-executor"> + <maxThrottled>0</maxThrottled> + <maxThrottledAge>0</maxThrottledAge> + <maxThrottledBytes>0</maxThrottledBytes> + </config> + +</container> +``` + +The effective operation queue configuration is logged when the container starts up, see below example. + +```text +INFO container Container.com.yahoo.document.restapi.resource.DocumentV1ApiHandler Operation queue: max-items=256, max-age=3000 ms, max-bytes=100 MB +``` + +You can observe the state of the operation queue through the metrics [`httpapi_queued_operations`](/en/reference/operations/metrics/container#httpapi_queued_operations), [`httpapi_queued_bytes`](/en/reference/operations/metrics/container#httpapi_queued_bytes) and [`httpapi_queued_age`](/en/reference/operations/metrics/container#httpapi_queued_age). + +## Using number and group id modifiers + +Do not use group or number modifiers with regular indexed mode document types. These are special cases that only work as expected for document types with [mode=streaming or mode=store-only](/en/reference/applications/services/content#document). Examples: + +<table className="vespa-table"> + <thead> + <tr> + <th></th> + <th></th> + </tr> + </thead> + <tbody> + <tr> + <td>**Get**</td> + <td>Get a document in a group:<br/><br/><code>{`$ curl http://localhost:8080/document/v1/mynamespace/music/number/23/some_key`}</code><br/><br/><code>{`$ curl http://localhost:8080/document/v1/mynamespace/music/group/mygroupname/some_key`}</code></td> + </tr> + <tr> + <td>**Visit**</td> + <td>Visit all documents for a group:<br/><br/><code>{`$ curl http://localhost:8080/document/v1/namespace/music/number/23/`}</code><br/><br/><code>{`$ curl http://localhost:8080/document/v1/namespace/music/group/mygroupname`}</code></td> + </tr> + </tbody> +</table> diff --git a/en/writing/feed-block.html b/en/writing/feed-block.html deleted file mode 100644 index 6c5ddc66ec..0000000000 --- a/en/writing/feed-block.html +++ /dev/null @@ -1,127 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Feed block" -redirect_from: -- /en/operations/feed-block.html ---- - -<p> - A content cluster blocks external write operations when at least one content node has reached the - <a href="../reference/applications/services/content.html#resource-limits">resource limit</a> - of disk or memory. This is done to avoid saturating resource usage on content nodes. - The <em>Cluster controller</em> monitors the resource usage of the content nodes - and decides whether to block feeding. - Transient resource usage (see details in the metrics below) is not included in the monitored usage. - This ensures that transient resource usage is covered by the resource headroom on the content nodes, - instead of leading to feed blocked due to natural fluctuations. -</p> -{% include note.html content="When running Vespa in a Docker image on a laptop, -one can easily get <code>[UNKNOWN(251009) @ tcp/vespa-host:19112/default]: -ReturnCode(NO_SPACE, External feed is blocked due to resource exhaustion: -in content cluster 'example': disk on node 0 [vespa-host] is 81.7% full (the configured limit is 80.0%, -effective limit lowered to 79.0% until feed unblocked)</code>. -Fix this by increasing allocated storage for the Docker daemon, clean up unused volumes -or remove unused Docker images." %} -<p>HTTP clients will see <em>507 Server Error: Insufficient Storage</em> when this happens.</p> -<p> - When feed is blocked, write operations are rejected by <em>Distributors</em>. - All Put operations and most Update operations are rejected. - These operations are still allowed: -</p> -<ul> - <li>Remove operations</li> - <li>Update <a href="../reference/schemas/document-json-format.html#assign">assign</a> operations to numeric single-value fields</li> -</ul> -<p> - To remedy, add nodes to the content cluster. - The data will <a href="../content/elasticity.html">auto-redistribute</a>, and feeding is unblocked when - all content nodes are below the limits. - For self-managed Vespa you can configure <a href="../reference/applications/services/content.html#resource-limits">resource-limits</a>, - although this is not recommended. Increasing them too much might lead to OOM and content nodes being unable to start. -</p> -{% include important.html content="Always <strong>add</strong> nodes, do not change node capacity - -this is in practise safer and quicker. -As most Vespa applications are set up on homogeneous nodes, changing node capacity can cause a full node set swap -and more data copying than just adding more nodes of the same kind. -Copying data will in itself stress nodes, adding one node is normally the smallest and safest change."%} -{% include note.html content="On <strong>Vespa Cloud</strong> the cluster controller is operated for you and the -resource limits are managed defaults (memory <code>0.8</code>), so the self-managed <code>resource-limits</code> tuning -above does not apply. Remedy a blocked feed by adding nodes — increase the cluster <code>nodes count</code> in -<em>services.xml</em> and redeploy; the data auto-redistributes and feeding unblocks once all content nodes are below -the limits. If <a href='../operations/autoscaling.html'>autoscaling</a> is enabled (node or resource <em>ranges</em> in -services.xml), Vespa Cloud adds nodes automatically when utilization is high, and its target utilization already -reserves headroom for a node being down or failing and for maintenance operations." %} -<p> - The <em>Cluster controller</em> compares each content node's disk and memory usage against the configured limits. - Feeding is blocked for the whole cluster as soon as a <strong>single</strong> available content node exceeds the limit - on a <strong>single</strong> resource — whichever of disk or memory crosses first. (A node that is <code>down</code> - or in <code>maintenance</code> does not count.) The block is lifted only once <em>all</em> content nodes are back below - the limits (in fact slightly below — see the hysteresis note below). -</p> -<p> - These <a href="../operations/metrics.html">metrics</a> are used to monitor resource usage and whether feeding is blocked. - <code>nodes_above_limit</code> is the authoritative "is feeding blocked" signal; the <code>max_*_utilization</code> metrics - are the best <em>leading</em> indicators of how close the cluster is to blocking; the per-node - <code>content.proton.resource_usage.*</code> metrics show the underlying usage that drives them: -</p> -<table class="table"> - <tr> - <th>cluster-controller.resource_usage.nodes_above_limit</th> - <td>The number of content nodes that are above one or more resource limits. - <strong>When above 0, feeding is blocked.</strong></td> - </tr> - <tr> - <th>cluster-controller.resource_usage.max_memory_utilization</th> - <td>The highest memory usage among all content nodes, relative to the memory limit (<code>usage / limit</code>). - <code>1.0</code> means a node has reached the limit and feeding blocks; e.g. <code>0.95</code> means the most-loaded - node is at 95% of the limit. This is the recommended signal to alert on <em>before</em> feeding blocks.</td> - </tr> - <tr> - <th>cluster-controller.resource_usage.max_disk_utilization</th> - <td>As above, for disk: the highest disk usage among all content nodes, relative to the disk limit (<code>usage / limit</code>).</td> - </tr> - <tr> - <th>content.proton.resource_usage.disk</th> - <td>A number between 0 and 1, indicating how much disk (of total available) is used on the content node. - Transient disk used during <a href="../content/proton.html#disk-index-fusion">disk index fusion</a> is not included.</td> - </tr> - <tr> - <th>content.proton.resource_usage.memory</th> - <td>A number between 0 and 1, indicating how much memory (of total available) is used on the content node. - Transient memory used by <a href="../content/proton.html#memory-index-flush">memory indexes</a> is not included.</td> - </tr> -</table> -<p> -When feeding is blocked, error messages are returned in write operation replies - example: -</p> -<pre> -ReturnCode(NO_SPACE, External feed is blocked due to resource exhaustion: - in content cluster 'example': memory on node 0 [my-vespa-node-0.example.com] is 82.0% full (the configured limit is 80.0%, effective limit lowered to 79.0% until feed unblocked)) -</pre> -<p> - Note that when feeding is blocked resource usage needs to decrease below another, lower limit before getting unblocked. This is to avoid - flip-flopping between blocking and unblocking feed when being near the limit. This lower limit is 1% lower than the configured limit. -</p> - -<p> - The address space used by data structures in attributes (<em>Multivalue Mapping</em>, <em>Enum Store</em>, and <em>Tensor Store</em>) can also go full and block feeding - - see <a href="../content/attributes.html#data-structures">attribute data structures</a> for details. - This will rarely happen. - The following metric is used to monitor address space usage: -</p> -<table class="table"> - <tr> - <th>content.proton.documentdb.attribute.resource_usage.address_space.max</th> - <td>A number between 0 and 1, indicating how much address space is used by the worst attribute data structure on the content node.</td> - </tr> -</table> -<p> - An error is returned when the address space limit (default value is 0.90) is exceeded: -</p> -<pre> -ReturnCode(NO_SPACE, External feed is blocked due to resource exhaustion: - in content cluster 'example': attribute-address-space:example.ready.a1.enum-store on node 0 [my-vespa-node-0.example.com] is 91.0% full (the configured limit is 90.0%)) -</pre> -<p> -To remedy, add nodes to the content cluster to distribute documents with attributes over more nodes. -</p> diff --git a/en/writing/feed-block.mdx b/en/writing/feed-block.mdx new file mode 100644 index 0000000000..dc2ffdfd7a --- /dev/null +++ b/en/writing/feed-block.mdx @@ -0,0 +1,87 @@ +--- +title: "Feed block" +--- + +A content cluster blocks external write operations when at least one content node has reached the [resource limit](/en/reference/applications/services/content#resource-limits) of disk or memory. This is done to avoid saturating resource usage on content nodes. The *Cluster controller* monitors the resource usage of the content nodes and decides whether to block feeding. Transient resource usage (see details in the metrics below) is not included in the monitored usage. This ensures that transient resource usage is covered by the resource headroom on the content nodes, instead of leading to feed blocked due to natural fluctuations. + +<Note> +**Note:** + +When running Vespa in a Docker image on a laptop, one can easily get `[UNKNOWN(251009) @ tcp/vespa-host:19112/default]: ReturnCode(NO_SPACE, External feed is blocked due to resource exhaustion: in content cluster 'example': disk on node 0 [vespa-host] is 81.7% full (the configured limit is 80.0%, effective limit lowered to 79.0% until feed unblocked)`. Fix this by increasing allocated storage for the Docker daemon, clean up unused volumes or remove unused Docker images. +</Note> + +HTTP clients will see *507 Server Error: Insufficient Storage* when this happens. + +When feed is blocked, write operations are rejected by *Distributors*. All Put operations and most Update operations are rejected. These operations are still allowed: + +- Remove operations +- Update [assign](/en/reference/schemas/document-json-format#assign) operations to numeric single-value fields + +To remedy, add nodes to the content cluster. The data will [auto-redistribute](/en/content/elasticity), and feeding is unblocked when all content nodes are below the limits. For self-managed Vespa you can configure [resource-limits](/en/reference/applications/services/content#resource-limits), although this is not recommended. Increasing them too much might lead to OOM and content nodes being unable to start. + +<Warning> +**Important:** + +Always **add** nodes, do not change node capacity - this is in practise safer and quicker. As most Vespa applications are set up on homogeneous nodes, changing node capacity can cause a full node set swap and more data copying than just adding more nodes of the same kind. Copying data will in itself stress nodes, adding one node is normally the smallest and safest change. +</Warning> + +These [metrics](/en/operations/metrics) are used to monitor resource usage and whether feeding is blocked: + +<table className="vespa-table"> + <thead> + <tr> + <th></th> + <th></th> + </tr> + </thead> + <tbody> + <tr> + <td>**cluster-controller.resource_usage.nodes_above_limit**</td> + <td>The number of content nodes that are above one or more resource limits. When above 0, feeding is blocked.</td> + </tr> + <tr> + <td>**content.proton.resource_usage.disk**</td> + <td>A number between 0 and 1, indicating how much disk (of total available) is used on the content node. Transient disk used during <a href="/en/content/proton#disk-index-fusion" aria-label="disk index fusion">disk index fusion</a> is not included.</td> + </tr> + <tr> + <td>**content.proton.resource_usage.memory**</td> + <td>A number between 0 and 1, indicating how much memory (of total available) is used on the content node. Transient memory used by <a href="/en/content/proton#memory-index-flush" aria-label="memory indexes">memory indexes</a> is not included.</td> + </tr> + </tbody> +</table> + +When feeding is blocked, error messages are returned in write operation replies - example: + +```text +ReturnCode(NO_SPACE, External feed is blocked due to resource exhaustion: + in content cluster 'example': memory on node 0 [my-vespa-node-0.example.com] is 82.0% full (the configured limit is 80.0%, effective limit lowered to 79.0% until feed unblocked)) +``` + +Note that when feeding is blocked resource usage needs to decrease below another, lower limit before getting unblocked. This is to avoid flip-flopping between blocking and unblocking feed when being near the limit. This lower limit is 1% lower than the configured limit. + +The address space used by data structures in attributes (*Multivalue Mapping*, *Enum Store*, and *Tensor Store*) can also go full and block feeding - see [attribute data structures](/en/content/attributes/#data-structures) for details. This will rarely happen. The following metric is used to monitor address space usage: + +<table className="vespa-table"> + <thead> + <tr> + <th></th> + <th></th> + </tr> + </thead> + <tbody> + <tr> + <td>**content.proton.documentdb.attribute.resource_usage.address_space.max**</td> + <td>A number between 0 and 1, indicating how much address space is used by the worst attribute data structure on the content node.</td> + </tr> + </tbody> +</table> + + +An error is returned when the address space limit (default value is 0.90) is exceeded: + +```text +ReturnCode(NO_SPACE, External feed is blocked due to resource exhaustion: + in content cluster 'example': attribute-address-space:example.ready.a1.enum-store on node 0 [my-vespa-node-0.example.com] is 91.0% full (the configured limit is 90.0%)) +``` + +To remedy, add nodes to the content cluster to distribute documents with attributes over more nodes. \ No newline at end of file diff --git a/en/writing/indexing-paged-vectors.html b/en/writing/indexing-paged-vectors.html deleted file mode 100644 index b58332eb64..0000000000 --- a/en/writing/indexing-paged-vectors.html +++ /dev/null @@ -1,84 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Indexing paged vectors" -redirect_from: -- /en/indexing-paged-vectors.html ---- - -<p>Most of the data of a vector (tensor) index is the vectors themselves. -The vector data must be accessed to calculate true distances both when querying the index and when adding vectors to it, -and due to the high dimensionality these accesses are effectively random. -While it is viable to <a href="../content/attributes.html#paged-attributes">page</a> -indexed vector attributes to disk for <i>queries</i> if somewhat higher latency can be tolerated, -it does not allow a large vector index to be built at reasonable speed: To create a high quality index, -each vector insert must make many distance calculations, which results in low write throughput -when the vectors in the index do not reside in RAM.</p> - -<p>To build vector indexes larger than available memory efficiently the procedure described here can be used. -This is suitable when:</p> -<ul> - <li>You want to build an index for vector retrieval (not just store the vectors for ranking/brute force NN), - with a vector data set that doesn't fit in memory across the content nodes you want to deploy for it.</li> - <li>The vector data in question is mostly write-once (frequent writes to other fields is fine), - and rescaling of the content cluster will not be necessary.</li> -</ul> - -<h2>Steps</h2> - -<ol> -<li><p>Declare the vector field(s) to be indexed as <code><a href="../content/attributes.html#paged-attributes">paged</a></code>.</p> - <pre> - schema docs { - document docs { - field myVectors type tensor<bfloat16>(chunk{}, x[384]) { - indexing: attribute | index - attribute: paged - } - } - } - </pre> -</li> -<li> - <p>Calculate how much data you can fit in memory:</p> - <p>Calculate your attribute raw data size (taking just the vector is close enough unless you have many other attribute fields),<br/> - multiply by the number of <a href="../reference/applications/services/content.html#searchable-copies">searchable-copies</a> you want,<br/> - multiply by 1.2 to add room for the index over the vectors,<br/> - divide by 0.65 to leave room for working memory,<br/> - multiply by your total number of documents.<br/></p> - <p>This gives you the total memory needed across all the nodes in your content cluster (or across one group if you have multiple).</p> - - <p><b>Example</b> with the type above with 1B documents and 10 chunks average per document:<br/> - 10 * 384*2 bytes * 2 * 1.2 / 0.65 * 1B = 14.178 Gb total cluster memory.</p> -</li> -<li> - <p>Create one document type per data subset which fits in memory under the calculation above.</p> - - <p><b>Example:</b> Suppose you want to create a vector index over four years worth of documents of type <code>docs</code> - and that you only want to allocate enough memory to fit 25% of the vector data across the cluster. - Create four subtypes of <code>docs</code>, one for each year: docs2021, docs2022, docs2023 and docs 2024, - in four different schema files. Each of these can inherit the parent type and otherwise be empty:</p> - - <pre> - schema docs2021 inherits docs { - document docs2021 inherits docs { - } - } - </pre> - - <p>You can of course also add time-period-specific fields and ranking here.</p> -</li> -<li>Add all the subtypes to the content cluster you want in services.xml: -<pre> - <content id="myClusterId" version="1.0"> - <documents> - <document type="docs2021" mode="index" /> - <document type="docs2022" mode="index" /> - ... - </documents> -</pre> -</li> -<li>Feed each of the types completely one by one, without applying queries at the same time. -<li>Once all the types are written, you can apply query traffic. Vespa will search across all the types by default, - but it is possible to restrict to a subset using the - <a href="../reference/api/query.html#model.restrict">restrict</a> query parameter.</li> -</ol> diff --git a/en/writing/indexing-paged-vectors.mdx b/en/writing/indexing-paged-vectors.mdx new file mode 100644 index 0000000000..aee5573261 --- /dev/null +++ b/en/writing/indexing-paged-vectors.mdx @@ -0,0 +1,74 @@ +--- +title: "Indexing paged vectors" +--- + +Most of the data of a vector (tensor) index is the vectors themselves. The vector data must be accessed to calculate true distances both when querying the index and when adding vectors to it, and due to the high dimensionality these accesses are effectively random. While it is viable to [page](/en/content/attributes/#paged-attributes) indexed vector attributes to disk for *queries* if somewhat higher latency can be tolerated, it does not allow a large vector index to be built at reasonable speed: To create a high quality index, each vector insert must make many distance calculations, which results in low write throughput when the vectors in the index do not reside in RAM. + +To build vector indexes larger than available memory efficiently the procedure described here can be used. This is suitable when: + +- You want to build an index for vector retrieval (not just store the vectors for ranking/brute force NN), with a vector data set that doesn't fit in memory across the content nodes you want to deploy for it. +- The vector data in question is mostly write-once (frequent writes to other fields is fine), and rescaling of the content cluster will not be necessary. + +## Steps + +<Steps> +<Step> +Declare the vector field(s) to be indexed as [paged](/en/content/attributes/#paged-attributes). + +```js + schema docs { + document docs { + field myVectors type tensor<bfloat16>(chunk{}, x[384]) { + indexing: attribute | index + attribute: paged + } + } + } +``` + +</Step> +<Step> +Calculate how much data you can fit in memory: + + Calculate your attribute raw data size (taking just the vector is close enough unless you have many other attribute fields),<br/> + multiply by the number of [searchable-copies](/en/reference/applications/services/content#searchable-copies) you want,<br/> + multiply by 1.2 to add room for the index over the vectors,<br/> + divide by 0.65 to leave room for working memory,<br/> + multiply by your total number of documents.<br/><br/> + This gives you the total memory needed across all the nodes in your content cluster (or across one group if you have multiple).<br/><br/> + **Example** with the type above with 1B documents and 10 chunks average per document:<br/> + 10 \* 384\*2 bytes \* 2 \* 1.2 / 0.65 \* 1B = 14.178 Gb total cluster memory. +</Step> +<Step> +Create one document type per data subset which fits in memory under the calculation above. + +**Example:** Suppose you want to create a vector index over four years worth of documents of type `docs` and that you only want to allocate enough memory to fit 25% of the vector data across the cluster. Create four subtypes of `docs`, one for each year: docs2021, docs2022, docs2023 and docs 2024, in four different schema files. Each of these can inherit the parent type and otherwise be empty: + +```js + schema docs2021 inherits docs { + document docs2021 inherits docs { + } + } +``` + +You can of course also add time-period-specific fields and ranking here. +</Step> +<Step> +Add all the subtypes to the content cluster you want in services.xml: + +```js + <content id="myClusterId" version="1.0"> + <documents> + <document type="docs2021" mode="index" /> + <document type="docs2022" mode="index" /> + ... + </documents> +``` +</Step> +<Step> +Feed each of the types completely one by one, without applying queries at the same time. +</Step> +<Step> +Once all the types are written, you can apply query traffic. Vespa will search across all the types by default, but it is possible to restrict to a subset using the [restrict](/en/reference/api/query#model.restrict) query parameter. +</Step> +</Steps> diff --git a/en/writing/indexing.html b/en/writing/indexing.html deleted file mode 100644 index 0d79600ba3..0000000000 --- a/en/writing/indexing.html +++ /dev/null @@ -1,178 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Indexing" -redirect_from: -- /en/indexing.html ---- - -<p> - Refer to the <a href="../learn/overview.html">overview</a>. - The primary index configuration is the <a href="../basics/schemas.html">schema</a>. -</p> -<p> - <a href="../reference/applications/services/services.html">services.xml</a> configures how indexing is distributed to the nodes, - see <a href="https://github.com/vespa-engine/sample-apps/blob/master/examples/operations/multinode-HA/services.xml">multinode-HA</a> - for a full example: -</p> -<pre>{% highlight xml %} -<container id="feed" version="1.0"> - <document-api /> - <document-processing /> - <nodes count="2" /> -</container> -{% endhighlight %}</pre> -<p> - It is important to configure both <code>document-api</code> and <code>document-processing</code> - to run the document processing on the same nodes as the document API endpoint, - to avoid network hops to other nodes (for better throughput). - Normally, one will run the indexing preprocessing on these nodes, too, - see the <a href="../reference/applications/services/content.html#document-processing">document-processing reference</a> - for a full example. -</p> - - - -<h2 id="date-indexing">Date indexing</h2> -<p> - Vespa does not have a "date" field type. - Best practise is using a <a href="../reference/schemas/schemas.html#long">long</a> field. - If the date is a string in the source data, - one can use <a href="../reference/writing/indexing-language.html#to_epoch_second">to_epoch_second</a> - to transform into a long-field: -</p> -<pre> -schema docs { - - document docs { - field date_string type string { - indexing: summary - } - } - - field date type long { - indexing: input date_string | to_epoch_second | attribute | summary - } - - field last_modified type long { - indexing: now | attribute | summary - } -} -</pre> -<p> - The synthetic <code>date</code> field can be used in queries and <a href="../querying/grouping.html">grouping</a>: -</p> -<pre>{% highlight json %} -"fields": { - "last_modified": 1695995429, - "date": 1703437243, - "date_string": "2023-12-24T17:00:43.000Z" -} -{% endhighlight %}</pre> -{% include note.html content='The <code>date</code> and <code>last_modified</code> fields above are placed -<span style="text-decoration: underline">outside</span> the <code>document</code> section, -as their content is generated from the document input. -Use <code>vespa visit --field-set "[all]"</code> to dump all fields' %} -<p>Note how <a href="../reference/writing/indexing-language.html#now">now</a> is used to get current time.</p> - - - - -<h2 id="execution-value-example">Execution value example</h2> - -<p>Accessing the execution value (the value passed into this expression) explicitly is useful when it is to be used - as part of an expression such as concatenation. In this example we have a document with a title and an array of sentences, and we - prepend each sentence by the document title (and a space), before converting it to a set of embedding vectors - (represented by a 2d mixed tensor).</p> - -<pre> -input mySentenceArray | for_each { input title . " " . _ } | embed | attribute my2dTensor | index my2dTensor -</pre> - - -<h2 id="choice-example">Choice (||) example</h2> - -<p>The choice expression is used to provide alternatives if an expression may return null.</p> -<pre> -(input myField1 || "") . " " . (input myField2 || "") | embed | attribute | index -</pre> -<p> - In this example two fields are concatenated, but if one of the fields is empty, the empty string is used instead. - If the empty string alternatives are not provided, no embedding will be produced if either input field is missing. -</p> - - -<h2 id="select-input-example">select_input example</h2> -<p> - The <code>select_input</code> expression is used to choose a statement to - execute based on which fields are non-empty in the input document: -</p> -<pre> -select_input { - CX: input CX | set_var CX; - CA: input CA . " " . input CB | set_var CX; -} -</pre> -<p> - This statement executes <code>input CX | set_var CX;</code> unless CX is empty. - If so, it will execute <code>input CA . " " . input CB | set_var CX;</code> unless CA is empty. -</p> - - - -<h2 id="switch-example">Switch example</h2> -<p> - The switch-expression behaves similarly to the switch-statement in other - programming languages. Each case in the switch-expression consists of a - string and a statement. The execution value is compared to each string, - and if there is a match, the corresponding statement is executed. An - optional default operation (designated by <code>default:</code>) can be - added to the end of the switch: -</p> -<pre> -input mt | switch { - case "audio": input fa | index; - case "video": input fv | index; - default: 0 | index; -}; -</pre> - - - -<h2 id="combine-values-in-indexing-statements">Indexing statements example</h2> -<p> - Using indexing statements, multiple document fields can be used to produce one index structure field. - For example, the index statement: -</p> -<pre> -input field1 . input field2 | attribute field2; -</pre> -<p> - combines <em>field1</em> and <em>field2</em> into the attribute named <em>field2</em>. - When partially updating documents which contains indexing statement which - combines multiple fields the following rules apply: -</p> -<ul> - <li>Only attributes where <em>all</em> the source values are available in - the source document update will be updated</li> - <li>The document update will fail when indexed (only) if <em>no</em> - attributes end up being updated when applying the rule above</li> -</ul> -<p> - Example: If a schema has the indexing statements -</p> -<pre> -input field1 | attribute field1; -input field1 . input field2 | attribute field2; -</pre> -<p>the following will happen for the different partial updates:</p> -<table class="table"> - <thead> - <tr><th>Partial update contains</th><th>Result</th></tr> - </thead> - <tbody> - <tr><td>field1</td><td>field1 is updated</td></tr> - <tr><td>field2</td><td>The update fails</td></tr> - <tr><td>field1 and field2</td><td>field1 and field2 are updated</td></tr> - </tbody> -</table> - diff --git a/en/writing/indexing.mdx b/en/writing/indexing.mdx new file mode 100644 index 0000000000..d5edf28912 --- /dev/null +++ b/en/writing/indexing.mdx @@ -0,0 +1,146 @@ +--- +title: "Indexing" +--- + +Refer to the [overview](/en/learn/overview). The primary index configuration is the [schema](/en/basics/schemas). + +[services.xml](/en/reference/applications/services/services) configures how indexing is distributed to the nodes, see [multinode-HA](https://github.com/vespa-engine/sample-apps/blob/master/examples/operations/multinode-HA/services.xml) for a full example: + +```xml +<container id="feed" version="1.0"> + <document-api /> + <document-processing /> + <nodes count="2" /> +</container> +``` + +It is important to configure both `document-api` and `document-processing` to run the document processing on the same nodes as the document API endpoint, to avoid network hops to other nodes (for better throughput). Normally, one will run the indexing preprocessing on these nodes, too, see the [document-processing reference](/en/reference/applications/services/content#document-processing) for a full example. + +## Date indexing + +Vespa does not have a "date" field type. Best practise is using a [long](/en/reference/schemas/schemas#long) field. If the date is a string in the source data, one can use [to_epoch_second](/en/reference/writing/indexing-language#to_epoch_second) to transform into a long-field: + +```js +schema docs { + + document docs { + field date_string type string { + indexing: summary + } + } + + field date type long { + indexing: input date_string | to_epoch_second | attribute | summary + } + + field last_modified type long { + indexing: now | attribute | summary + } +} +``` + +The synthetic `date` field can be used in queries and [grouping](/en/querying/grouping): + +```json +"fields": { + "last_modified": 1695995429, + "date": 1703437243, + "date_string": "2023-12-24T17:00:43.000Z" +} +``` + +<Note> +**Note:** + +The `date` and `last_modified` fields above are placed outside the `document` section, as their content is generated from the document input. Use `vespa visit --field-set "[all]"` to dump all fields. +</Note> + +Note how [now](/en/reference/writing/indexing-language#now) is used to get current time. + +## Execution value example + +Accessing the execution value (the value passed into this expression) explicitly is useful when it is to be used as part of an expression such as concatenation. In this example we have a document with a title and an array of sentences, and we prepend each sentence by the document title (and a space), before converting it to a set of embedding vectors (represented by a 2d mixed tensor). + +```js +input mySentenceArray | for_each { input title . " " . _ } | embed | attribute my2dTensor | index my2dTensor +``` + +## Choice (||) example + +The choice expression is used to provide alternatives if an expression may return null. + +```js +(input myField1 || "") . " " . (input myField2 || "") | embed | attribute | index +``` + +In this example two fields are concatenated, but if one of the fields is empty, the empty string is used instead. If the empty string alternatives are not provided, no embedding will be produced if either input field is missing. + +## select_input example + +The `select_input` expression is used to choose a statement to execute based on which fields are non-empty in the input document: + +```js +select_input { + CX: input CX | set_var CX; + CA: input CA . " " . input CB | set_var CX; +} +``` + +This statement executes `input CX | set_var CX;` unless CX is empty. If so, it will execute `input CA . " " . input CB | set_var CX;` unless CA is empty. + +## Switch example + +The switch-expression behaves similarly to the switch-statement in other programming languages. Each case in the switch-expression consists of a string and a statement. The execution value is compared to each string, and if there is a match, the corresponding statement is executed. An optional default operation (designated by `default:`) can be added to the end of the switch: + +```js +input mt | switch { + case "audio": input fa | index; + case "video": input fv | index; + default: 0 | index; +}; +``` + +## Indexing statements example + +Using indexing statements, multiple document fields can be used to produce one index structure field. For example, the index statement: + +```js +input field1 . input field2 | attribute field2; +``` + +combines *field1* and *field2* into the attribute named *field2*. When partially updating documents which contains indexing statement which combines multiple fields the following rules apply: + +- Only attributes where *all* the source values are available in the source document update will be updated +- The document update will fail when indexed (only) if *no* attributes end up being updated when applying the rule above + +Example: If a schema has the indexing statements + +```js +input field1 | attribute field1; +input field1 . input field2 | attribute field2; +``` + +the following will happen for the different partial updates: + +<table className="vespa-table"> + <thead> + <tr> + <th>Partial update contains</th> + <th>Result</th> + </tr> + </thead> + <tbody> + <tr> + <td>field1</td> + <td>field1 is updated</td> + </tr> + <tr> + <td>field2</td> + <td>The update fails</td> + </tr> + <tr> + <td>field1 and field2</td> + <td>field1 and field2 are updated</td> + </tr> + </tbody> +</table> diff --git a/en/writing/initial-batch-feed.html b/en/writing/initial-batch-feed.html deleted file mode 100644 index c23390e132..0000000000 --- a/en/writing/initial-batch-feed.html +++ /dev/null @@ -1,246 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: Initial batch feed -applies_to: cloud -redirect_from: -- /en/cloud/index-bootstrap.html -- /en/cloud/initial-batch-feed.html ---- - -<p> - One of the first things you do when creating a Vespa application is deploying it to Vespa Cloud and batch feeding all your data. - We assume you have made the first version of the schema file(s) and generated a set of Vespa feed files. - You will face some of these questions: -</p> -<ul> - <li>What is a good resource configuration for the content nodes holding the index?</li> - <li>What happens when I change the node resource configuration?</li> - <li>How many nodes are required to hold the full index?</li> - <li>Is using autoscaling a good idea when batch feeding?</li> - <li>How do I know what the feeding bottleneck is?</li> - <li>What is the difference between a container and content cluster?</li> -</ul> -<p> - Review the introduction section of the <a href="../learn/overview.html">Vespa overview</a> to understand - the different between <em>container</em> and <em>content</em> clusters before continuing. -</p> -<p> - The first thing to note is that changes to node resources (i.e., instance type) - are <a href="../performance/node-resources">automated</a>. - This makes it easy to just get started feeding, observe, change, and repeat. -</p> -<p> - The next thing is, changing node resources takes some time to apply, - as data and indexes are migrated to the node(s) with the new configuration - more details below. - This also means, it is easier to change this with little data and few nodes deployed, - like in the 1% step below. - Therefore, do an iteration or two to get the disk/memory balance right -</p> -<p> - Once the node resources configuration is about right, feed more data. - This is potentially time-consuming. - Make sure the bottleneck is the nodes in the content clusters (you should see at least one thread at 100%), - It is a good idea to make sure that the <em>container</em> cluster with the feed endpoint is not at 100% CPU, - add more nodes if so. - <a href="../performance/sizing-feeding.html#feed-testing">More details</a>. -</p> -<p> - Finally, we recommend in this phase to not use <a href="../operations/autoscaling.html">autoscaling</a>. - Autoscaling is great for incremental changes, in <em>this</em> case, we feed from zero to full. - Best practice is, once you have a rough idea based on the smaller feed batches, - allocate 50-100% more content node capacity than your estimate, and feed to full. - This will speed up the process with less wall-clock time and no delays due to - <a href="feed-block.html">blocked feeding</a>. - When the initial feed is done, it is easy to evaluate possible overcapacity and reduce node count. -</p> - -<h2 id="summary">Summary</h2> -<p> - When bootstrapping an index, one must consider node resource configuration and number of nodes. - The strategy is to iterate: -</p> -<img src="/assets/img/index-bootstrap.svg" width="465" alt="Growing a Vespa cluster in steps"/> -<ol> - <li>Feed smaller datasets</li> - <li>Evaluate</li> - <li>Deploy new node counts / node resource configuration</li> - <li>Wait for data migration to complete</li> - <li>Evaluate</li> -</ol> -<p> - While doing this, ensure the cluster is <strong>not more than 50% full</strong> - - this gives headroom to later increase/shrink the index - and change schema configuration easily using automatic reindexing. - It is easy to downscale resources after the bootstrap, - and it saves a lot of time keeping the clusters within limits - hence max 50%. -</p> - - - -<h2 id="preparations">Preparations</h2> -<p> - The content node resource configuration should not have ranges for index bootstrap, - as autoscaling will interfere with the evaluation in this step. - This is a good starting point, <strong>make sure there are no ranges like [2,3]</strong>: -</p> -<pre>{% highlight xml %} -<nodes count="2"> - <resources vcpu="2.0" memory="8Gb" disk="50Gb" /> -</nodes> -{% endhighlight %}</pre> -<p> - To evaluate how full the content cluster is, - use the "Metrics" view in the Vespa Cloud Console - or <a href="../operations/monitoring.html">metrics</a> from content nodes - example: -</p> -<pre> -$ curl \ - --cert data-plane-public-cert.pem \ - --key data-plane-private-key.pem \ - https://ab1f1234.b68a8765.z.vespa-app.cloud/prometheus/v1/values | \ - egrep 'disk.util|mem.util' | egrep 'clusterId="content/' -</pre> -<p>Once able to see the metrics above, you are ready to bootstrap the index.</p> - - - -<h2 id="bootstrap">Bootstrap</h2> -<table class="table"> - <thead> - <tr> - <th>Step</th><th>Description</th> - </tr> - </thead> - <tbody> - <tr> - <td><strong>1% feed</strong></td> - <td> - <p> - The purpose of this step is to feed a tiny chunk of the corpus to: - </p> - <ol> - <li>Estimate the memory and disk resource configuration.</li> - <li>Estimate the number of nodes required for the 10% step.</li> - </ol> - <p> - Feed a small data set, using <code>vespa feed</code> as in <a href="../basics/deploy-an-application.html">getting started</a>. - Observe the util metrics, stop no later than 50% memory/disk util. - The resource configuration should be modified so disk is in the 50-80% range of memory. - Example: if memory util is 50%, disk util should be 30-45%. - The reasoning is that memory is a more expensive component than disk, - better over-allocate on disk and just track memory usage. - </p> - <p> - Look at memory util. Say the 1% feed caused a 15% memory util - - this means that the 10% feed will take 150%, or 3X more than the 50% max. - There are two options, either increase memory/disk or add more nodes. - A good rule of thumb at this stage is that the final 100% feed could fit on 4 or more nodes, - and there is a 2-node minimum for redundancy. - The default configuration at the start of this document is quite small, - so a 3X at this stage means triple the disk and memory, - and add more nodes in later steps. - </p> - <p> - Deploy changes (if needed). - Whenever node count increases or resource configuration is modified, new nodes are added, - and data is migrated to new nodes. Example: growing from 2 to 3 nodes means each of the 2 current nodes - will migrate 33% of their data to the new node. - Read more in <a href="../content/elasticity.html">elasticity</a>. - </p> - <p> - It saves time to let the cluster finish data migration before feeding more data, - and it lets you observe metrics before next steps. - In this step it will be fast as the data volume is small, but nevertheless check the - "Content Buckets Out Of Sync (%), per Cluster" panel in the Vespa Cloud Console Metrics view, - or the <a href="../reference/operations/metrics/distributor.html#vds_idealstate_merge_bucket_pending"> - vds.idealstate.merge_bucket.pending.average</a> metric. - Wait for 0 for all nodes - this means data migration is completed: - </p> -<pre> -$ curl \ - --cert ~/.vespa/mytenant.myapp.default/data-plane-public-cert.pem \ - --key ~/.vespa/mytenant.myapp.default/data-plane-private-key.pem \ - https://ab1f1234.b68a8765.z.vespa-app.cloud/prometheus/v1/values?consumer=Vespa | \ - egrep 'vds_idealstate_merge_bucket_pending_average' -</pre> - <p> - At this point, you can validate that both memory and disk util is less than 5%, - so the 10% feed will fit. - </p> - </td> - </tr> - <tr> - <td><strong>10% feed</strong></td> - <td> - <p> - Feed the 10% corpus, still observing util metrics. - </p> - <p> - As the content cluster capacity is increased, - it is normal to eventually be CPU bound in the container or content cluster. - Grep for <code>cpu_util</code> in metrics (like in the example above) to evaluate. - </p> - <p> - A 10% feed is a great baseline for the full capacity requirements. - Fine tune the resource config and number of hosts as needed. - If you deploy changes, wait for the - <code>vds.idealstate.merge_bucket.pending.average</code> metric to go to zero again. - This now takes longer time as nodes are configured larger, - it normally completes within a few hours. - </p> - <p> - Again validate memory and disk util is less than 5% before the full feed. - </p> - </td> - </tr> - <tr> - <td style="white-space: nowrap;"><strong>100% feed</strong></td> - <td> - <p> - Feed the full data set, observing the metrics. - You should be able to estimate timing by extrapolation, this is linear at this scale. - At feed completion, observe the util metrics for the final fine-tuning. - </p> - <p> - A great exercise at this point is to add a node then reduce a node, and take the time to completion - (<code>vds.idealstate.merge_bucket.pending.average</code> to 0). - This is useful information when the application is in production, - as you know the time to add or shrink capacity in advance. - </p> - <p> - It can be a good idea to reduce node count to get the memory util closer to 70% at this step, - to optimize for cost. - However, do not spend too much time optimizing in this step, next step is normally - <a href="../performance/sizing-search.html">sizing for query load</a>. - This will again possibly alter resource configuration and node counts / topology, - but now you have a good grasp at how to easily bootstrap the index for these experiments. - </p> - </td> - </tr> - </tbody> -</table> - - - -<h2 id="troubleshooting">Troubleshooting</h2> -<p> - Make sure you are able to feed and access documents as the example in <a href="#preparations">preparations</a>. - Read <a href="../security/guide">security guide</a> for cert/key usage. -</p> -<p> - Feeding too much will cause a - <a href="feed-block.html">feed blocked</a> state. - Add a node to the full content cluster in services.xml, and wait for data migration to complete - - i.e. wait for the <code>vds.idealstate.merge_bucket.pending.average</code> metric to go to zero. - It is better to add a node than increasing node resources, as data migration is quicker. -</p> - - - -<h2 id="further-reading">Further reading</h2> -<ul> - <li><a href="reads-and-writes.html">Reads and Writes</a></li> - <li><a href="../performance/sizing-feeding.html">Vespa Feed Sizing Guide</a></li> - <li><a href="../performance/benchmarking-cloud.html">Vespa Cloud Benchmarking</a></li> - <li><a href="../operations/monitoring.html">Monitoring</a></li> -</ul> diff --git a/en/writing/initial-batch-feed.mdx b/en/writing/initial-batch-feed.mdx new file mode 100644 index 0000000000..cd0c95a53c --- /dev/null +++ b/en/writing/initial-batch-feed.mdx @@ -0,0 +1,99 @@ +--- +title: "Initial batch feed" +sidebarTitle: "Index bootstrap" +--- + +One of the first things you do when creating a Vespa application is deploying it to Vespa Cloud and batch feeding all your data. We assume you have made the first version of the schema file(s) and generated a set of Vespa feed files. You will face some of these questions: + +- What is a good resource configuration for the content nodes holding the index? +- What happens when I change the node resource configuration? +- How many nodes are required to hold the full index? +- Is using autoscaling a good idea when batch feeding? +- How do I know what the feeding bottleneck is? +- What is the difference between a container and content cluster? + +Review the introduction section of the [Vespa overview](/en/learn/overview) to understand the different between *container* and *content* clusters before continuing. + +The first thing to note is that changes to node resources (i.e., instance type) are [automated](/en/performance/node-resources). This makes it easy to just get started feeding, observe, change, and repeat. + +The next thing is, changing node resources takes some time to apply, as data and indexes are migrated to the node(s) with the new configuration — more details below. This also means, it is easier to change this with little data and few nodes deployed, like in the 1% step below. Therefore, do an iteration or two to get the disk/memory balance right. + +Once the node resources configuration is about right, feed more data. This is potentially time-consuming. Make sure the bottleneck is the nodes in the content clusters (you should see at least one thread at 100%), It is a good idea to make sure that the *container* cluster with the feed endpoint is not at 100% CPU, add more nodes if so. [More details](/en/performance/sizing-feeding#feed-testing). + +Finally, we recommend in this phase to not use [autoscaling](/en/operations/autoscaling). Autoscaling is great for incremental changes, in *this* case, we feed from zero to full. Best practice is, once you have a rough idea based on the smaller feed batches, allocate 50-100% more content node capacity than your estimate, and feed to full. This will speed up the process with less wall-clock time and no delays due to [blocked feeding](/en/writing/feed-block). When the initial feed is done, it is easy to evaluate possible overcapacity and reduce node count. + +## Summary + +When bootstrapping an index, one must consider node resource configuration and number of nodes. The strategy is to iterate: + +<Frame>![Growing a Vespa cluster in steps](/assets/img/index-bootstrap.svg)</Frame> + +1. Feed smaller datasets +2. Evaluate +3. Deploy new node counts / node resource configuration +4. Wait for data migration to complete +5. Evaluate + +While doing this, ensure the cluster is **not more than 50% full** — this gives headroom to later increase/shrink the index and change schema configuration easily using automatic reindexing. It is easy to downscale resources after the bootstrap, and it saves a lot of time keeping the clusters within limits — hence max 50%. + +## Preparations + +The content node resource configuration should not have ranges for index bootstrap, as autoscaling will interfere with the evaluation in this step. This is a good starting point, **make sure there are no ranges like [2,3]**: + +```xml +<nodes count="2"> + <resources vcpu="2.0" memory="8Gb" disk="50Gb" /> +</nodes> +``` + +To evaluate how full the content cluster is, use the "Metrics" view in the Vespa Cloud Console or [metrics](/en/operations/monitoring) from content nodes — example: + +```sh +$ curl \ + --cert data-plane-public-cert.pem \ + --key data-plane-private-key.pem \ + https://ab1f1234.b68a8765.z.vespa-app.cloud/prometheus/v1/values | \ + egrep 'disk.util|mem.util' | egrep 'clusterId="content/' +``` + +Once able to see the metrics above, you are ready to bootstrap the index. + +## Bootstrap + +<table className="vespa-table"> + <thead> + <tr> + <th>Step</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>**1% feed**</td> + <td>The purpose of this step is to feed a tiny chunk of the corpus to:<br/><br/>1. Estimate the memory and disk resource configuration.<br/>2. Estimate the number of nodes required for the 10% step.<br/>Feed a small data set, using <code>{`vespa feed`}</code> as in <a href="/en/basics/deploy-an-application" aria-label="getting started">getting started</a>. Observe the util metrics, stop no later than 50% memory/disk util. The resource configuration should be modified so disk is in the 50-80% range of memory. Example: if memory util is 50%, disk util should be 30-45%. The reasoning is that memory is a more expensive component than disk, better over-allocate on disk and just track memory usage.<br/><br/>Look at memory util. Say the 1% feed caused a 15% memory util - this means that the 10% feed will take 150%, or 3X more than the 50% max. There are two options, either increase memory/disk or add more nodes. A good rule of thumb at this stage is that the final 100% feed could fit on 4 or more nodes, and there is a 2-node minimum for redundancy. The default configuration at the start of this document is quite small, so a 3X at this stage means triple the disk and memory, and add more nodes in later steps.<br/><br/>Deploy changes (if needed). Whenever node count increases or resource configuration is modified, new nodes are added, and data is migrated to new nodes. Example: growing from 2 to 3 nodes means each of the 2 current nodes will migrate 33% of their data to the new node. Read more in <a href="/en/content/elasticity" aria-label="elasticity">elasticity</a>.<br/><br/>It saves time to let the cluster finish data migration before feeding more data, and it lets you observe metrics before next steps. In this step it will be fast as the data volume is small, but nevertheless check the "Content Buckets Out Of Sync (%), per Cluster" panel in the Vespa Cloud Console Metrics view, or the <a href="/en/reference/operations/metrics/distributor#vds_idealstate_merge_bucket_pending" aria-label="vds.idealstate.merge_bucket.pending.average">vds.idealstate.merge_bucket.pending.average</a> metric. Wait for 0 for all nodes - this means data migration is completed:<br/><br/><code>{`$ curl \\`}</code><br/><code>{`--cert ~/.vespa/mytenant.myapp.default/data-plane-public-cert.pem \\`}</code><br/><code>{`--key ~/.vespa/mytenant.myapp.default/data-plane-private-key.pem \\`}</code><br/><code>{`https://ab1f1234.b68a8765.z.vespa-app.cloud/prometheus/v1/values?consumer=Vespa \\`}</code><br/><code>{`egrep 'vds_idealstate_merge_bucket_pending_average'`}</code><br/><br/>At this point, you can validate that both memory and disk util is less than 5%, so the 10% feed will fit.</td> + </tr> + <tr> + <td>**10% feed**</td> + <td>Feed the 10% corpus, still observing util metrics.<br/><br/>As the content cluster capacity is increased, it is normal to eventually be CPU bound in the container or content cluster. Grep for <code>{`cpu_util`}</code> in metrics (like in the example above) to evaluate.<br/><br/>A 10% feed is a great baseline for the full capacity requirements. Fine tune the resource config and number of hosts as needed. If you deploy changes, wait for the <code>{`vds.idealstate.merge_bucket.pending.average`}</code> metric to go to zero again. This now takes longer time as nodes are configured larger, it normally completes within a few hours.<br/><br/>Again validate memory and disk util is less than 5% before the full feed.</td> + </tr> + <tr> + <td>**100% feed**</td> + <td>Feed the full data set, observing the metrics. You should be able to estimate timing by extrapolation, this is linear at this scale. At feed completion, observe the util metrics for the final fine-tuning.<br/><br/>A great exercise at this point is to add a node then reduce a node, and take the time to completion (<code>{`vds.idealstate.merge_bucket.pending.average`}</code> to 0). This is useful information when the application is in production, as you know the time to add or shrink capacity in advance.<br/><br/>It can be a good idea to reduce node count to get the memory util closer to 70% at this step, to optimize for cost. However, do not spend too much time optimizing in this step, next step is normally <a href="/en/performance/sizing-search" aria-label="sizing for query load">sizing for query load</a>. This will again possibly alter resource configuration and node counts / topology, but now you have a good grasp at how to easily bootstrap the index for these experiments.</td> + </tr> + </tbody> +</table> + +## Troubleshooting + +Make sure you are able to feed and access documents as the example in [preparations](#preparations). Read [security guide](/en/security/guide) for cert/key usage. + +Feeding too much will cause a [feed blocked](/en/writing/feed-block) state. Add a node to the full content cluster in services.xml, and wait for data migration to complete — i.e. wait for the `vds.idealstate.merge_bucket.pending.average` metric to go to zero. It is better to add a node than increasing node resources, as data migration is quicker. + +## Further reading + +<CardGroup> + <Card title="Reads and Writes" icon="database" href="/en/writing/reads-and-writes" horizontal /> + <Card title="Vespa Feed Sizing Guide" icon="bar-chart" href="/en/performance/sizing-feeding" horizontal /> + <Card title="Vespa Cloud Benchmarking" icon="volcano" href="/en/performance/benchmarking-cloud" horizontal /> + <Card title="Monitoring" icon="display" href="/en/operations/monitoring" horizontal /> +</CardGroup> \ No newline at end of file diff --git a/en/writing/partial-updates.html b/en/writing/partial-updates.html deleted file mode 100644 index 7980ef698b..0000000000 --- a/en/writing/partial-updates.html +++ /dev/null @@ -1,250 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Partial Updates" -redirect_from: -- /en/partial-updates.html ---- - -<p> - A partial update is an update to one or more fields in a document. - It also includes updating all index structures so the effect of the - partial update is immediately observable in queries. See - <a href="../reference/schemas/document-json-format.html#update">document update JSON format</a> - for full details on the various possible partial update operations. -</p> -<img src="/assets/img/attributes-update.svg" width="788px" height="auto" - alt="Attribute is an in-memory data structure" /> -<p> - In Vespa, all fields can be partially updated by default. - A field is index, attribute or summary or a combination of these, - and both index and attribute fields can be queried. -</p> - -<ul> - <li>For <a href="../content/proton.html#index">index</a> and summary fields, - an update means a read-modify-write to the - <a href="../content/proton.html#document-store">document store</a> - and limits throughput.</li> - <li>Most <a href="../content/attributes.html">attribute</a> fields do not - require the document store read-modify-write, increasing write - throughput by orders of magnitude. - The following attribute types require a read-modify-write to the document store: - <ul> - <li><a href="../reference/schemas/schemas.html#array">array of struct</a></li> - <li><a href="../reference/schemas/schemas.html#map">map of primitive and struct</a></li> - <li><a href="../reference/schemas/schemas.html#predicate">predicate</a></li> - <li><a href="../reference/schemas/schemas.html#reference">reference</a></li> - </ul> - </li> -</ul> - -{% include important.html content="For highest possible write throughput for field updates, -use attributes to write at memory speed." %} - -<table class="table"> - <thead> - <tr><th>Field Setting</th><th>Searchable</th><th>Fast searchable</th><th>Matching</th><th>Ranking</th><th>Display in results</th></tr> - </thead> - <tbody> - <tr> - <th>index</th><td>Y</td><td>Y</td><td>Text and Exact matching</td><td>Y</td><td>N</td> - </tr> - <tr> - <th>attribute</th><td>Y</td><td>Y with attribute:fast-search</td><td>Exact matching</td><td>Y</td><td>Y</td> - </tr> - <tr> - <th>summary</th><td>N</td><td>N</td><td>N</td><td>N</td><td>Y</td> - </tr> - </tbody> -</table> -<p> - Examples: -</p> -<table class="table"> - <tr> - <td> -<pre> -field user type string { -<span style="white-space: nowrap">    indexing: summary | index</span> -} -</pre> - </td> - <td>Summary + index field. - The field is stored in the document store, a partial update to the field will trigger read + write. - </td> - </tr> - <tr> - <td> -<pre> -field user type string { - indexing: attribute -} -</pre> - </td> - <td>Attribute only field. - The field is stored in the attribute (in-memory) and a partial update will update the document in-place - and will be visible for queries, ranking, grouping and sorting immediately. - </td> - </tr> -</table> - - - -<h2 id="use-cases">Use cases</h2> -<p> - Partial updates have many use cases. - <em>Functionally</em>, it enables updating a document without anything else than the ID, - simplifying logic in the upper levels of the serving stack. - <em>Performance-wise</em>, partial updates enables applications with a real-time update flow - in tens of thousands updates per second. Examples: -</p> -<table class="table"> - <thead> - <tr><th>Use case</th><th>Description</th></tr> - </thead> - <tr> - <th rowspan="2">Filtering</th> - <th>Inventory updates</th> - <td>Update product price and inventory count in real time. Do not show items out of stock.</td> - </tr> - <tr> - <th>Update relations</th> - <td>Add a "this person likes me" to the "likes me" - <a href="../reference/querying/yql.html#weightedset">set</a> - - display candidates based on sets of likes/dislikes/other relations. - </td> - </tr> - <tr> - <th>Ranking</th> - <th></th> - <td>Update click / views / non-clicks: Feed usage data to use in ranking - rank popular items higher.</td> - </tr> -</table> - - - -<h2 id="write-pipeline">Write pipeline</h2> -<p> - Refer to <a href="../content/proton.html">proton</a> for an overview of the write-pipeline - and the Transaction Log Server (TLS). -</p> -<table class="table"> - <thead> - <tr><th style="white-space:nowrap">Field Setting</th><th>Description</th></tr> - </thead> - <tbody> - <tr> - <th>index</th><td> - <p> - For all <a href="../reference/schemas/schemas.html#index">indexed fields</a>, - a memory index is used for the recent changes, implemented using B-trees. - This is periodically <a href="../content/proton.html#memory-index-flush">flushed</a> to a disk-based posting list index. - Disk-based indexes are subsequently <a href="../content/proton.html#disk-index-fusion">merged</a>. - </p> - <p> - Updating the in-memory B-trees is lock-free, implemented using copy-on-write semantics. - This gives high performance, with a predictable steady-state CPU/memory use. - The driver for this design is the requirement for a sustained, high change rate, - with stable, predictable read latencies and small temporary increases in CPU/memory. - This compared to index hierarchies, merging smaller real-time indices into larger, causing temporary hot-spots. - </p> - <p> - When updating an indexed field, the document is read from the - <a href="../content/proton.html#document-store">document store</a>, the field is updated, - and the full document is written back to the store. - At this point, the change is searchable, and an ACK is returned to the client. - Use <a href="../content/attributes.html">attributes</a> to avoid such document disk accesses - and increase performance for partial updates. - Find more details in <a href="../performance/sizing-feeding.html">feed performance</a>. - </p> - </td> - </tr> - <tr> - <th>attribute</th><td> - <p> - Attribute fields are in-memory fields, see <a href="../content/attributes.html">attributes</a>. - This makes updates inexpensive and fast. - Attribute data is periodically flushed, see <a href="../content/proton.html#attribute-flush">attribute-flush</a>. - Note that operations are persisted to the Transaction Log Service (TLS), - in the rare case of a power failure or unclean shutdown, - the operations are synced from the TLS. - </p> - <p> - - Note there is no transactional support for updates across fields. - To support high rate, there is no coordination between threads - example: - </p> -<pre>{% highlight json %} -{ - "update" : "id:namespace:doctype::1", - "fields" : { - "firstName" : { "assign" : "John" }, - "lastName" : { "assign" : "Smith" } - } -} -{% endhighlight %}</pre> - <p> - Above, the attributes <em>firstName</em> and <em>lastName</em> are updated in the same operation from the client, - whereas the update in the search core is non-transactional. - This is a throughput vs consistency tradeoff that enables the extreme update rates - without being a practical limitation for many applications. - More details in <a href="../content/attributes.html">attributes</a>. - </p> - <p> - Updating <a href="../searching-multi-valued-fields">multivalue</a> attributes (arrays, maps, sets, tensors) - means reading the current value, making the update and writing it back: - </p> - <ul> - <li><a href="../reference/schemas/schemas.html#array">Array of primitive types</a>, - <a href="../reference/schemas/schemas.html#weightedset">weightedsets</a> and - <a href="../reference/schemas/schemas.html#tensor">tensors</a> are in memory and therefore fast, - see <a href="../content/attributes.html#data-structures">attribute data structures</a> for performance considerations.</li> - <li>If the attribute field is an <a href="../reference/schemas/schemas.html#array">array of struct</a> or - <a href="../reference/schemas/schemas.html#map">map</a>, - values are written in the document store and update rates are hence lower - - refer to <a href="https://github.com/vespa-engine/vespa/issues/10892"> - #10892 updates of array of map/struct</a>.</li> - </ul> - <p> - Query execution time can be improved by adding an in-memory B-tree posting list structure using - <a href="../performance/feature-tuning.html#when-to-use-fast-search-for-attribute-fields">fast-search</a>. - This increases work when updating, - as both the value and the posting list is updated and hence decreases update throughput. - </p> - <p> - See <a href="../performance/sizing-feeding.html#attribute-store">sizing-feeding</a> - for how to ensure an attribute is in memory on all nodes with a replica (searchable-copies or fast-access). - </p> - </td> - </tr> - <tr> - <th>summary</th><td> - <p> - An update to the <a href="../content/proton.html#document-store">document store</a> - is read the current version, modify and write back a new blob. - Refer to <a href="../querying/document-summaries.html">document summaries</a>. - </p> - <p> - Attribute fields that are also in summary get their values from the memory structures, - not the document store. - Use <a href="../reference/schemas/schemas.html#document-summary">summary class</a> - with attributes only for applications with high write/query rates using memory only. - </p> - </td> - </tr> - </tbody> -</table> - - -<!-- ToDo: What if field is both indexed and attribute? --> - - - -<h2 id="further-reading">Further reading</h2> -<ul> - <li><a href="reads-and-writes.html">reads and writes</a> - functional overview of the Document API</li> - <li><a href="../performance/sizing-feeding.html">sizing-feeding</a> - troubleshooting</li> - <li><a href="../content/attributes.html">attributes</a> - to understand all aspects of attributes</li> - <li><a href="../content/proton.html">proton</a> - this should have the full write pipeline, go here for this</li> - <li><a href="../schemas/parent-child.html">parent-child</a> - how to use parent attributes for even higher update rates</li> -</ul> diff --git a/en/writing/partial-updates.mdx b/en/writing/partial-updates.mdx new file mode 100644 index 0000000000..a392c4ae90 --- /dev/null +++ b/en/writing/partial-updates.mdx @@ -0,0 +1,147 @@ +--- +title: "Partial Updates" +--- + +A partial update is an update to one or more fields in a document. It also includes updating all index structures so the effect of the partial update is immediately observable in queries. See [document update JSON format](/en/reference/schemas/document-json-format#update) for full details on the various possible partial update operations. + +<Frame> +![Attribute is an in-memory data structure](/assets/img/attributes-update.svg) +</Frame> + +In Vespa, all fields can be partially updated by default. A field is index, attribute or summary or a combination of these, and both index and attribute fields can be queried. + +- For [index](/en/content/proton#index) and summary fields, an update means a read-modify-write to the [document store](/en/content/proton#document-store) and limits throughput. +- Most [attribute](/en/content/attributes) fields do not require the document store read-modify-write, increasing write throughput by orders of magnitude. The following attribute types require a read-modify-write to the document store: + - [array of struct](/en/reference/schemas/schemas#array) + - [map of primitive and struct](/en/reference/schemas/schemas#map) + - [predicate](/en/reference/schemas/schemas#predicate) + - [reference](/en/reference/schemas/schemas#reference) + +<Warning> +**Important:** + +For highest possible write throughput for field updates, use attributes to write at memory speed. +</Warning> + +<table className="vespa-table"> + <thead> + <tr> + <th>Field Setting</th> + <th>Searchable</th> + <th>Fast searchable</th> + <th>Matching</th> + <th>Ranking</th> + <th>Display in results</th> + </tr> + </thead> + <tbody> + <tr> + <td>index</td> + <td>Y</td> + <td>Y</td> + <td>Text and Exact matching</td> + <td>Y</td> + <td>N</td> + </tr> + <tr> + <td>attribute</td> + <td>Y</td> + <td>Y with attribute:fast-search</td> + <td>Exact matching</td> + <td>Y</td> + <td>Y</td> + </tr> + <tr> + <td>summary</td> + <td>N</td> + <td>N</td> + <td>N</td> + <td>N</td> + <td>Y</td> + </tr> + </tbody> +</table> + +Examples: + +<table className="vespa-table"> + <thead> + <tr> + <th>Schema</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td><code>{`field user type string { indexing: summary `}</code>|<code>{` index }`}</code></td> + <td>Summary + index field. The field is stored in the document store, a partial update to the field will trigger read + write.</td> + </tr> + <tr> + <td><code>{`field user type string { indexing: attribute }`}</code></td> + <td>Attribute only field. The field is stored in the attribute (in-memory) and a partial update will update the document in-place and will be visible for queries, ranking, grouping and sorting immediately.</td> + </tr> + </tbody> +</table> + +## Use cases + +Partial updates have many use cases. *Functionally*, it enables updating a document without anything else than the ID, simplifying logic in the upper levels of the serving stack. *Performance-wise*, partial updates enables applications with a real-time update flow in tens of thousands updates per second. Examples: + +<table className="vespa-table"> + <thead> + <tr> + <th>Use case</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>**Filtering — Inventory updates**</td> + <td>Update product price and inventory count in real time. Do not show items out of stock.</td> + </tr> + <tr> + <td>**Filtering — Update relations**</td> + <td>Add a "this person likes me" to the "likes me" <a href="/en/reference/querying/yql#weightedset" aria-label="set">set</a> — display candidates based on sets of likes/dislikes/other relations.</td> + </tr> + <tr> + <td>**Ranking**</td> + <td>Update click / views / non-clicks: Feed usage data to use in ranking — rank popular items higher.</td> + </tr> + </tbody> +</table> + + +## Write pipeline + +Refer to [proton](/en/content/proton) for an overview of the write-pipeline and the Transaction Log Server (TLS). + +<table className="vespa-table"> + <thead> + <tr> + <th>Field Setting</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>index</td> + <td>For all <a href="/en/reference/schemas/schemas#index" aria-label="indexed fields">indexed fields</a>, a memory index is used for the recent changes, implemented using B-trees. This is periodically <a href="/en/content/proton#memory-index-flush" aria-label="flushed">flushed</a> to a disk-based posting list index. Disk-based indexes are subsequently <a href="/en/content/proton#disk-index-fusion" aria-label="merged">merged</a>. <br/><br/> Updating the in-memory B-trees is lock-free, implemented using copy-on-write semantics. This gives high performance, with a predictable steady-state CPU/memory use. The driver for this design is the requirement for a sustained, high change rate, with stable, predictable read latencies and small temporary increases in CPU/memory. This compared to index hierarchies, merging smaller real-time indices into larger, causing temporary hot-spots.<br/><br/> When updating an indexed field, the document is read from the <a href="/en/content/proton#document-store" aria-label="document store">document store</a>, the field is updated, and the full document is written back to the store. At this point, the change is searchable, and an ACK is returned to the client. Use <a href="/en/content/attributes/" aria-label="attributes">attributes</a> to avoid such document disk accesses and increase performance for partial updates. Find more details in <a href="/en/performance/sizing-feeding/" aria-label="feed performance">feed performance</a>.</td> + </tr> + <tr> + <td>attribute</td> + <td>Attribute fields are in-memory fields, see <a href="/en/content/attributes/" aria-label="attributes">attributes</a>. This makes updates inexpensive and fast. Attribute data is periodically flushed, see <a href="/en/content/proton#attribute-flush" aria-label="attribute-flush">attribute-flush</a>. Note that operations are persisted to the Transaction Log Service (TLS), in the rare case of a power failure or unclean shutdown, the operations are synced from the TLS.<br/><br/> Note there is no transactional support for updates across fields. To support high rate, there is no coordination between threads - example: <br/><br/> <code>{`{`}</code><br/> <code>{`"update" : "id:namespace:doctype::1",`}</code><br/> <code>{`"fields" : {`}</code> <br/><code>{`"firstName" : { "assign" : "John" },`}</code><br/> <code>{`"lastName" : { "assign" : "Smith" }`}</code><br/> <code>{`}`}</code><br/> <code>{`}`}</code><br/><br/> Above, the attributes *firstName* and *lastName* are updated in the same operation from the client, whereas the update in the search core is non-transactional. This is a throughput vs consistency tradeoff that enables the extreme update rates without being a practical limitation for many applications. More details in <a href="/en/content/attributes/" aria-label="attributes">attributes</a>.<br/><br/> Updating <a href="/en/querying/searching-multivalue-fields" aria-label="multivalue">multivalue</a> attributes (arrays, maps, sets, tensors) means reading the current value, making the update and writing it back:<br/><br/> • <a href="/en/reference/schemas/schemas#array" aria-label="Array of primitive types">Array of primitive types</a>, <a href="/en/reference/schemas/schemas#weightedset" aria-label="weightedsets">weightedsets</a> and <a href="/en/reference/schemas/schemas#tensor" aria-label="tensors">tensors</a> are in memory and therefore fast, see <a href="/en/content/attributes/#data-structures" aria-label="attribute data structures">attribute data structures</a> for performance considerations. <br/><br/> • If the attribute field is an <a href="/en/reference/schemas/schemas#array" aria-label="array of struct">array of struct</a> or <a href="/en/reference/schemas/schemas#map" aria-label="map">map</a>, values are written in the document store and update rates are hence lower - refer to <a href="https://github.com/vespa-engine/vespa/issues/10892" aria-label="#10892 updates of array of map/struct">#10892 updates of array of map/struct</a>. <br/><br/> Query execution time can be improved by adding an in-memory B-tree posting list structure using <a href="/en/performance/feature-tuning#when-to-use-fast-search-for-attribute-fields" aria-label="fast-search">fast-search</a>. This increases work when updating, as both the value and the posting list is updated and hence decreases update throughput.<br/><br/> See <a href="/en/performance/sizing-feeding/#attribute-store" aria-label="sizing-feeding">sizing-feeding</a> for how to ensure an attribute is in memory on all nodes with a replica (searchable-copies or fast-access).</td> + </tr> + <tr> + <td>summary</td> + <td>An update to the <a href="/en/content/proton#document-store" aria-label="document store">document store</a> is read the current version, modify and write back a new blob. Refer to <a href="/en/querying/document-summaries/" aria-label="document summaries">document summaries</a>.<br/><br/> Attribute fields that are also in summary get their values from the memory structures, not the document store. Use <a href="/en/reference/schemas/schemas#document-summary" aria-label="summary class">summary class</a> with attributes only for applications with high write/query rates using memory only.</td> + </tr> + </tbody> +</table> + +## Further reading + +- [reads and writes](/en/writing/reads-and-writes) — functional overview of the Document API +- [sizing-feeding](/en/performance/sizing-feeding) — troubleshooting +- [attributes](/en/content/attributes) — to understand all aspects of attributes +- [proton](/en/content/proton) — full write pipeline +- [parent-child](/en/schemas/parent-child) — how to use parent attributes for even higher update rates diff --git a/en/writing/reads-and-writes.html b/en/writing/reads-and-writes.html deleted file mode 100644 index d8ee8ce2e6..0000000000 --- a/en/writing/reads-and-writes.html +++ /dev/null @@ -1,256 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Reads and writes" -redirect_from: -- /en/reads-and-writes.html ---- - -<p> - This guide covers the aspects of accessing <a href="../schemas/documents.html">documents</a> in Vespa. - Documents are stored in <em>content</em> clusters. - Writes (PUT, UPDATE, DELETE) and reads (GET) pass through a <em>container</em> cluster. - Find a more detailed flow at the end of this article. -</p> -<img src="/assets/img/vespa-overview.svg" width="825px" height="auto" alt="Vespa Overview" /> -<p> - Highlights: -</p> -<ul> -<li> - Vespa's indexing structures are built for high-rate field updates. - Refer to the <a href="../performance/sizing-feeding.html">feed sizing guide</a> for write performance, - in particular <a href="partial-updates.html">partial updates</a> for partial updates. -</li> -<li> - Vespa supports <a href="../schemas/parent-child.html">parent/child</a> for de-normalized data. - This can be used to simplify the code to update application data, - as one write will update all children documents. -</li> -<li> - Applications can add custom feed <a href="../applications/document-processors.html">document processors</a> - and multiple container clusters - see <a href="indexing.html">indexing</a> for details. -</li> -<li> - Writes in Vespa are <em>consistent</em> in a stable cluster, but Vespa will prioritize availability over consistency when - there is a conflict. - See the <a href="../content/elasticity.html#consistency">elasticity</a> documentation - and the <a href="../content/consistency">Vespa consistency model</a>. - It is recommended to use the same client instance for updating a given document when possible - - for data consistency, but also - <a href="../performance/sizing-feeding.html#concurrent-mutations">performance</a> (see <em>concurrent mutations</em>). - Read more on write operation <a href="../content/content-nodes.html#ordering">ordering</a>. - For performance, group field updates to the same document into - <a href="../performance/sizing-feeding.html#client-roundtrips">one update operation</a>. -</li> -<li> - Applications can <a href="../schemas/documents.html#document-expiry">auto-expire documents</a>. - This feature also blocks PUTs to documents that are already expired - - see <a href="document-routing.html#document-selection">indexing</a> and - <a href="../reference/applications/services/content.html#documents">document selection</a>. - This is a common problem when feeding test data with timestamps, - and the writes a silently dropped. -</li> -</ul> -<p> - Also see <a href="../operations/self-managed/admin-procedures.html#troubleshooting">troubleshooting</a>. -</p> - - - -<h2 id="operations">Operations</h2> -<table class="table"> -<thead><tr><th>Operation</th><th>Description</th></tr></thead> -<tbody> -<tr> - <th>Get</th> - <td><p> - Get a document by ID. - </p></td> -</tr><tr> - <th>Put</th> - <td><p> - Write a document by ID - a document is overwritten if a document with the same document ID exists. - </p><p> - Puts can have <a href="document-v1-api-guide.html#conditional-writes">conditions</a> - for test-and-set use cases. Conditions can be combined with - <a href="document-v1-api-guide.html#create-if-nonexistent">create if nonexistent</a>, - which causes the condition to be ignored if the document does not already exist. - </p><p> - </p></td> -</tr><tr> - <th>Remove</th> - <td> - <p> - Remove a document by ID. - If the document to be removed is not found, it is not considered a failure. - Read more about <a href="../operations/self-managed/admin-procedures.html#data-retention-vs-size">data-retention</a>. - Also see <a href="batch-delete.html">batch deletes</a>. - </p><p> - Removes can have <a href="document-v1-api-guide.html#conditional-writes">conditions</a> - for test-and-set use cases. - </p><p> - A removed document is written as a tombstone, and later garbage collected - - see <a href="../reference/applications/services/content.html#removed-db-prune-age">removed-db / prune / age</a>. - Vespa does not retain, nor return, the document data of removed documents. - </p> - </td> -</tr><tr> - <th>Update</th> - <td><p> - Also referred to as <a href="partial-updates.html">partial updates</a>, - as it updates one or more fields of a document by ID - the - <a href="document-v1-api-guide.html#put">document v1 API</a> can be used to perform - <a href="../reference/schemas/document-json-format.html#update">updates in the JSON Document format</a>. - If the document to update is not found, it is not considered a failure. - </p><p> - Updates support <a href="document-v1-api-guide.html#create-if-nonexistent">create if nonexistent</a> (upsert). - </p><p> - Updates can have <a href="document-v1-api-guide.html#conditional-writes">conditions</a> - for test-and-set use cases. - </p><p> - All data structures (<a href="../content/attributes.html">attribute</a>, - <a href="../content/proton.html#index">index</a> and <a href="../querying/document-summaries.html">summary</a>) are updatable. - Note that only <em>assign</em> and <em>remove</em> are idempotent - - message re-sending can apply updates more than once. - Use <em>conditional writes</em> for stronger consistency. - </p> - <table class="table"> - <tr> - <td><strong>All field types</strong></td> - <td><ul> - <li><a href="../reference/schemas/document-json-format.html#assign">assign</a> (may also be used to clear fields)</li> - </ul></td> - </tr><tr> - <td><strong>Numeric field types</strong></td> - <td><ul> - <li><a href="../reference/schemas/document-json-format.html#arithmetic">increment</a>. - Also see <a href="../reference/schemas/schemas.html#weightedset">auto-generate weightedset keys</a></li> - <li><a href="../reference/schemas/document-json-format.html#arithmetic">decrement</a></li> - <li><a href="../reference/schemas/document-json-format.html#arithmetic">multiply</a></li> - <li><a href="../reference/schemas/document-json-format.html#arithmetic">divide</a></li> - </ul></td> - </tr><tr> - <td><strong>Composite types</strong></td> - <td><ul> - <li><a href="../reference/schemas/document-json-format.html#add">add</a> - For <em>array</em> and <em>weighted set</em>. - To put into a <em>map</em>, - see the <a href="../reference/schemas/document-json-format.html#assign">assign</a> section</li> - <li><a href="../reference/schemas/document-json-format.html#composite-remove">remove</a></li> - <li><a href="../reference/schemas/document-json-format.html#match">match</a> - Pick element from collection, then apply given operation to matched element</li> - <li><a href="../reference/schemas/document-json-format.html#fieldpath"> - accessing elements within a composite field using fieldpaths</a></li> - </ul></td> - </tr><tr> - <td><strong>Tensor types</strong></td> - <td><ul> - <li><a href="../reference/schemas/document-json-format.html#tensor-modify">modify</a> - Modify individual cells in a tensor - can replace, add or multiply cell values</li> - <li><a href="../reference/schemas/document-json-format.html#tensor-add">add</a> - Add cells to mapped or mixed tensors</li> - <li><a href="../reference/schemas/document-json-format.html#tensor-remove">remove</a> - Remove cells from mapped or mixed tensors</li> - </ul></td> - </tr> - </table> - </td> -</tr> -</tbody> -</table> - - - -<h2 id="api-and-utilities">API and utilities</h2> -<p> - Also see the <a href="../reference/schemas/document-json-format.html">JSON Document format</a>: -</p> -<table class="table"> - <thead><tr><th>API / util</th><th>Description</th></tr></thead> - <tbody> - <tr> - <td><a href="../clients/vespa-cli.html">Vespa CLI</a></td> - <td> - Command-line tool to - <code>get</code>, <code>put</code>, <code>remove</code>, <code>update</code>, <code>feed</code>, <code>visit</code>. - </td> - </tr> - <tr> - <td><a href="../reference/api/document-v1.html">/document/v1/</a></td> - <td>API for <code>get</code>, <code>put</code>, <code>remove</code>, <code>update</code>, <code>visit</code>.</td> - </tr> - <tr> - <td style="white-space: nowrap"><a href="document-api-guide.html">Java Document API</a></td> - <td> - Provides direct read-and write access to Vespa documents using Vespa's internal communication layer. - Use this when accessing documents from Java components in Vespa - such as <a href="../applications/searchers.html">searchers</a> and - <a href="../applications/document-processors.html">document processors</a>. - See the <a href="https://github.com/vespa-engine/vespa/blob/master/document/src/main/java/com/yahoo/document/Document.java"> - Document</a> class. - </td> - </tr> - <tr> - <td><a href="https://vespa-engine.github.io/pyvespa/reads-writes.html">pyvespa</a></td> - <td> - Python client library for reading and writing documents to Vespa. - Provides convenient methods for feeding, querying, and visiting documents. Expect less performance than Vespa CLI and vespa-feed-client for heavy batch feed operations. - </td> - </tr> - </tbody> -</table> -<p>Advanced / debugging tools:</p> -<ul> - <li> - <a href="../clients/vespa-feed-client.html">vespa-feed-client</a>: - Java library and command line client for feeding document operations - using <a href="../reference/api/document-v1.html">/document/v1/</a>. - </li> - <li> - <a href="../reference/operations/self-managed/tools.html#vespa-feeder">vespa-feeder</a> - is a utility for feeding over the <a href="document-routing.html">Message Bus</a>. - </li> - <li> - <a href="../reference/operations/self-managed/tools.html#vespa-get">vespa-get</a> gets single documents - over the <a href="document-routing.html">Message Bus</a>. - </li> - <li> - <a href="../reference/operations/self-managed/tools.html#vespa-visit">vespa-visit</a> gets multiple documents - over the <a href="document-routing.html">Message Bus</a>. - </li> -</ul> - - - -<h2 id="feed-flow">Feed flow</h2> -<p> - Use the <a href="../clients/vespa-cli.html">Vespa CLI</a>, <a href="../clients/vespa-feed-client.html">vespa-feed-client</a>, <a href="https://vespa-engine.github.io/pyvespa/reads-writes.html">pyvespa python client</a> - or <a href="../reference/api/document-v1.html">/document/v1/ API</a> to read and write documents: -</p> -<img src="/assets/img/elastic-feed-container.svg" width="345" height="auto" alt="Feed with feed client"/> -<p> - Alternatively, use <a href="../reference/operations/self-managed/tools.html#vespa-feeder">vespa-feeder</a> to feed files - or the <a href="document-api-guide.html">Java Document API</a>. -</p> -<img src="/assets/img/elastic-feed-vespafeeder.svg" width="345" height="auto" alt="Feed with vespafeeder"/> -<p> - <a href="document-routing.html#routing-for-indexing">Indexing</a> - and/or <a href="../applications/document-processors.html">document processing</a> - is a chain of processors that manipulate documents before they are stored. - Document processors can be user defined. - When using indexed search, the final step in the chain prepares documents for indexing. -</p> -<p> - The <a href="document-api-guide.html">Document API</a> forwards requests to distributors on content nodes. - For more information, read about <a href="../content/content-nodes.html">content nodes</a> - and the <a href="../content/proton.html">search core</a>. -</p> - - - -<h2 id="further-reading">Further reading</h2> -<ul> - <li><a href="visiting.html">Visiting</a></li> - <li><a href="document-v1-api-guide.html">/document/v1/ API guide</a></li> - <li><a href="../reference/api/document-v1.html">/document/v1/ API reference</a></li> -</ul> diff --git a/en/writing/reads-and-writes.mdx b/en/writing/reads-and-writes.mdx new file mode 100644 index 0000000000..afd5093941 --- /dev/null +++ b/en/writing/reads-and-writes.mdx @@ -0,0 +1,103 @@ +--- +title: "Reads and writes" +description: "This guide covers the aspects of accessing [documents](/en/schemas/documents) in Vespa. Documents are stored in content clusters. Writes (PUT, UPDATE, DELETE) and reads (GET) pass through a container cluster. Find a more detailed flow at the end of this article." +--- + +<Frame>![Vespa Overview](/assets/img/vespa-overview.svg)</Frame> + +Highlights: + +- Vespa's indexing structures are built for high-rate field updates. Refer to the [feed sizing guide](/en/performance/sizing-feeding) for write performance, in particular [partial updates](/en/writing/partial-updates) for partial updates. +- Vespa supports [parent/child](/en/schemas/parent-child) for de-normalized data. This can be used to simplify the code to update application data, as one write will update all children documents. +- Applications can add custom feed [document processors](/en/applications/document-processors) and multiple container clusters — see [indexing](/en/writing/indexing) for details. +- Writes in Vespa are *consistent* in a stable cluster, but Vespa will prioritize availability over consistency when there is a conflict. See the [elasticity](/en/content/elasticity#consistency) documentation and the [Vespa consistency model](/en/content/consistency). It is recommended to use the same client instance for updating a given document when possible — for data consistency, but also [performance](/en/performance/sizing-feeding#concurrent-mutations) (see *concurrent mutations*). Read more on write operation [ordering](/en/content/content-nodes#ordering). For performance, group field updates to the same document into [one update operation](/en/performance/sizing-feeding#client-roundtrips). +- Applications can [auto-expire documents](/en/schemas/documents#document-expiry). This feature also blocks PUTs to documents that are already expired — see [indexing](/en/writing/document-routing#document-selection) and [document selection](/en/reference/applications/services/content#documents). This is a common problem when feeding test data with timestamps, and the writes a silently dropped. + +Also see [troubleshooting](/en/operations/self-managed/admin-procedures#troubleshooting). + +## Operations + +<table className="vespa-table"> + <thead> + <tr> + <th>Operation</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>**Get**</td> + <td>Get a document by ID.</td> + </tr> + <tr> + <td>**Put**</td> + <td>Write a document by ID — a document is overwritten if a document with the same document ID exists. <br/><br/>Puts can have <a href="/en/writing/document-v1-api-guide#conditional-writes" aria-label="conditions">conditions</a> for test-and-set use cases. Conditions can be combined with <a href="/en/writing/document-v1-api-guide#create-if-nonexistent" aria-label="create if nonexistent">create if nonexistent</a>, which causes the condition to be ignored if the document does not already exist.</td> + </tr> + <tr> + <td>**Remove**</td> + <td>Remove a document by ID. If the document to be removed is not found, it is not considered a failure. Read more about <a href="/en/operations/self-managed/admin-procedures#data-retention-vs-size" aria-label="data-retention">data-retention</a>. Also see <a href="/en/writing/batch-delete" aria-label="batch deletes">batch deletes</a>.<br/><br/> Removes can have <a href="/en/writing/document-v1-api-guide#conditional-writes" aria-label="conditions">conditions</a> for test-and-set use cases.<br/><br/> A removed document is written as a tombstone, and later garbage collected — see <a href="/en/reference/applications/services/content#removed-db-prune-age" aria-label="removed-db / prune / age">removed-db / prune / age</a>. Vespa does not retain, nor return, the document data of removed documents.</td> + </tr> + <tr> + <td>**Update**</td> + <td>Also referred to as <a href="/en/writing/partial-updates" aria-label="partial updates">partial updates</a>, as it updates one or more fields of a document by ID — the <a href="/en/writing/document-v1-api-guide#put" aria-label="document v1 API">document v1 API</a> can be used to perform <a href="/en/reference/schemas/document-json-format#update" aria-label="updates in the JSON Document format">updates in the JSON Document format</a>. If the document to update is not found, it is not considered a failure. Updates support <a href="/en/writing/document-v1-api-guide#create-if-nonexistent" aria-label="create if nonexistent">create if nonexistent</a> (upsert). <br/><br/>Updates can have <a href="/en/writing/document-v1-api-guide#conditional-writes" aria-label="conditions">conditions</a> for test-and-set use cases. <br/><br/>All data structures (<a href="/en/content/attributes" aria-label="attribute">attribute</a>, <a href="/en/content/proton#index" aria-label="index">index</a> and <a href="/en/querying/document-summaries" aria-label="summary">summary</a>) are updatable. Note that only *assign* and *remove* are idempotent — message re-sending can apply updates more than once. Use *conditional writes* for stronger consistency. <br/><br/>**All field types** <br/><br/>• <a href="/en/reference/schemas/document-json-format#assign" aria-label="assign">assign</a> (may also be used to clear fields) <br/><br/> **Numeric field types** <br/><br/> • <a href="/en/reference/schemas/document-json-format#arithmetic" aria-label="increment">increment</a>. Also see <a href="/en/reference/schemas/schemas#weightedset" aria-label="auto-generate weightedset keys">auto-generate weightedset keys</a><br/><br/> • <a href="/en/reference/schemas/document-json-format#arithmetic" aria-label="decrement">decrement</a>,<br/><br/> • <a href="/en/reference/schemas/document-json-format#arithmetic" aria-label="multiply">multiply</a>, <br/><br/>• <a href="/en/reference/schemas/document-json-format#arithmetic" aria-label="divide">divide</a>.<br/><br/> **Composite types**<br/><br/> • <a href="/en/reference/schemas/document-json-format#add" aria-label="add">add</a> For *array* and *weighted set*. To put into a *map*, see the <a href="/en/reference/schemas/document-json-format#assign" aria-label="assign">assign</a> section, <br/><br/>• <a href="/en/reference/schemas/document-json-format#composite-remove" aria-label="remove">remove</a>,<br/><br/> • <a href="/en/reference/schemas/document-json-format#match" aria-label="match">match</a> Pick element from collection, then apply given operation to matched element,<br/><br/> • <a href="/en/reference/schemas/document-json-format#fieldpath" aria-label="accessing elements within a composite field using fieldpaths">accessing elements within a composite field using fieldpaths</a><br/><br/> **Tensor types** <br/><br/>• <a href="/en/reference/schemas/document-json-format#tensor-modify" aria-label="modify">modify</a> Modify individual cells in a tensor - can replace, add or multiply cell values,<br/><br/> • <a href="/en/reference/schemas/document-json-format#tensor-add" aria-label="add">add</a> Add cells to mapped or mixed tensors, <br/><br/>• <a href="/en/reference/schemas/document-json-format#tensor-remove" aria-label="remove">remove</a> Remove cells from mapped or mixed tensors</td> + </tr> + </tbody> +</table> + +## API and utilities + +Also see the [JSON Document format](/en/reference/schemas/document-json-format): + +<table className="vespa-table"> + <thead> + <tr> + <th>API / util</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td><a href="/en/clients/vespa-cli" aria-label="Vespa CLI">Vespa CLI</a></td> + <td>Command-line tool to <code>{`get`}</code>, <code>{`put`}</code>, <code>{`remove`}</code>, <code>{`update`}</code>, <code>{`feed`}</code>, <code>{`visit`}</code>.</td> + </tr> + <tr> + <td><a href="/en/reference/api/document-v1" aria-label="/document/v1/">/document/v1/</a></td> + <td>API for <code>{`get`}</code>, <code>{`put`}</code>, <code>{`remove`}</code>, <code>{`update`}</code>, <code>{`visit`}</code>.</td> + </tr> + <tr> + <td><a href="/en/writing/document-api-guide" aria-label="Java Document API">Java Document API</a></td> + <td>Provides direct read-and write access to Vespa documents using Vespa's internal communication layer. Use this when accessing documents from Java components in Vespa such as <a href="/en/applications/searchers" aria-label="searchers">searchers</a> and <a href="/en/applications/document-processors" aria-label="document processors">document processors</a>. See the <a href="https://github.com/vespa-engine/vespa/blob/master/document/src/main/java/com/yahoo/document/Document.java" aria-label="Document">Document</a> class.</td> + </tr> + <tr> + <td><a href="https://vespa-engine.github.io/pyvespa/reads-writes" aria-label="pyvespa">pyvespa</a></td> + <td>Python client library for reading and writing documents to Vespa. Provides convenient methods for feeding, querying, and visiting documents. Expect less performance than Vespa CLI and vespa-feed-client for heavy batch feed operations.</td> + </tr> + </tbody> +</table> + +Advanced / debugging tools: + +- [vespa-feed-client](/en/clients/vespa-feed-client): Java library and command line client for feeding document operations using [/document/v1/](/en/reference/api/document-v1). +- [vespa-feeder](/en/reference/operations/self-managed/tools#vespa-feeder) is a utility for feeding over the [Message Bus](/en/writing/document-routing). +- [vespa-get](/en/reference/operations/self-managed/tools#vespa-get) gets single documents over the [Message Bus](/en/writing/document-routing). +- [vespa-visit](/en/reference/operations/self-managed/tools#vespa-visit) gets multiple documents over the [Message Bus](/en/writing/document-routing). + +## Feed flow + +Use the [Vespa CLI](/en/clients/vespa-cli), [vespa-feed-client](/en/clients/vespa-feed-client), [pyvespa python client](https://vespa-engine.github.io/pyvespa/reads-writes) or [/document/v1/ API](/en/reference/api/document-v1) to read and write documents: + +<Frame>![Feed with feed client](/assets/img/elastic-feed-container.svg)</Frame> + +Alternatively, use [vespa-feeder](/en/reference/operations/self-managed/tools#vespa-feeder) to feed files or the [Java Document API](/en/writing/document-api-guide). + +<Frame>![Feed with vespafeeder](/assets/img/elastic-feed-vespafeeder.svg)</Frame> + +[Indexing](/en/writing/document-routing#routing-for-indexing) and/or [document processing](/en/applications/document-processors) is a chain of processors that manipulate documents before they are stored. Document processors can be user defined. When using indexed search, the final step in the chain prepares documents for indexing. + +The [Document API](/en/writing/document-api-guide) forwards requests to distributors on content nodes. For more information, read about [content nodes](/en/content/content-nodes) and the [search core](/en/content/proton). + +## Further reading + +- [Visiting](/en/writing/visiting) +- [/document/v1/ API guide](/en/writing/document-v1-api-guide) +- [/document/v1/ API reference](/en/reference/api/document-v1) diff --git a/en/writing/visiting.html b/en/writing/visiting.html deleted file mode 100644 index 2e537dbca3..0000000000 --- a/en/writing/visiting.html +++ /dev/null @@ -1,502 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Visiting" -redirect_from: -- /en/content/visiting.html -- /en/visiting.html ---- - -<p> - Visiting is a feature to efficiently get or process a set of documents, identified by a - <a href="../reference/writing/document-selector-language.html">document selection expression</a>. -</p> -<p> - Visiting is often used to back up or migrate applications, - <a href="https://cloud.vespa.ai/en/cloning-applications-and-data">cloning applications and data</a> - is a good guide for this. -</p> -<p> - Use the <a href="../clients/vespa-cli.html">Vespa CLI</a> to run visit - - example, using the <a href="../basics/deploy-an-application-local.html">quick start</a>: -</p> -<pre> -$ vespa visit -</pre> -<pre>{% highlight json %} -{ - "id": "id:mynamespace:music::love-is-here-to-stay", - "fields": { - "artist":"Diana Krall", - "year":2018, - "category_scores": { - "type": "tensor<float>(cat{})", - "cells": { - "pop": 0.4000000059604645, - "rock": 0.0, - "jazz": 0.800000011920929 - } - }, - "album": "Love Is Here To Stay" - } -} -... -{% endhighlight %}</pre> -<p> - Typically, the visit use cases are not time sensitive, like data reprocessing, - and document dump for backup and cluster clone - - <a href="../cloud/cloning-applications-and-data">cloning applications and data</a> - is a good read for more details. -</p> -<p> - Visiting does not have snapshot isolation—it returns the state of documents as they - were when iterated over. Iteration order is implementation-specific. - See <a href="../content/consistency#read-consistency">the consistency documentation</a> for more details. -</p> -{% include note.html content='Due to the bucket iteration, visiting is normally a high-latency iteration. - Even an empty content cluster is pre-partitioned into many data buckets to enable scaling. - Dumping just one document requires iteration over all data buckets, - unless <em>location</em>-specific selections are used. - To test visiting performance, use larger data sets; do not extrapolate from small. - Use the <a href="../querying/query-api.html">query API</a> or - <a href="../clients/vespa-cli.html#documents"><code>vespa document get</code></a> - for low latency operations on small result sets.' %} -<p> - See <a href="#request-handling">request handling</a> for details on how visiting works. - Also see the internal <a href="../reference/operations/self-managed/tools.html#vespa-visit">vespa-visit</a> tool. - For programmatic access, see the <a href="document-api-guide.html#visitorsession">visitor session API</a>. -</p> - -<h2 id="visiting-from-pyvespa">Visiting from pyvespa</h2> -<p> - The <a href="https://vespa-engine.github.io/pyvespa/reads-writes.html#visiting">pyvespa library</a> - provides a convenient Python interface for visiting documents. -</p> - -<h2 id="data-export">Data export</h2> -<p>Export data to stdout:</p> -<pre> -$ vespa visit -</pre> -<p> - To export a subset, <code>--slices 100 --slice-id 0</code> exports 1% of the corpus by efficiently iterating - over only 1/100th of the data space. - For a given number of <code>--slices</code>, - it's possible to visit the entire corpus (possibly in parallel) with non-overlapping output - by visiting with all <code>--slice-id</code> values from (and including) 0 up to (and excluding) <code>--slices</code>: -</p> -<pre> -$ vespa visit --slices 100 --slice-id 0 -</pre> -{% include note.html content='If the application has -<a href="../reference/applications/services/content.html#document">global document types</a>, -use <code>--bucket-space global</code> to visit these documents. -By default, visiting only iterates over non-global documents.' %} - -<h3 id="performance-note-feeding-data-exported-via-visiting">Performance note: feeding data exported via visiting</h3> -<p> -Vespa uses hashing to distribute documents pseudo-randomly across many -<a href="../content/buckets.html">buckets</a>. The operations in an incoming document stream are -expected to be distributed evenly across both the set of all buckets and the content nodes -storing them. This parallelizes the load and efficiently utilizes the hardware resources in the cluster. -</p> -<p> -Visiting iterates over the buckets in the cluster, returning all documents stored in -a bucket before moving on to the next bucket (often processing many buckets in parallel). -As a consequence there's a direct correlation between the internal document-to-bucket -distribution and the document output ordering of the visiting process. -</p> -<p> -If the output of visiting is fed directly back into a content cluster, this correlation -means that the stream of documents is no longer uniformly distributed across buckets and/or -content nodes. Prior to Vespa 8.349 this is likely to greatly reduce feeding performance due -to increased contention around backend bucket-level write locks and indexing threads. Vespa -8.349 and beyond contains optimizations that bring re-feeding performance much closer to -that of initial feeding. -</p> -<p> -A simple strategy to avoid this problem is to <em>shuffle</em> the visiting output prior to -re-feeding it. This removes any correlation between feed operations and the underlying -data distribution. -</p> - - -<h2 id="selection">Selection</h2> -<p> -To select (i.e. filter) which documents to visit, use a -<a href="../reference/writing/document-selector-language.html">document selection expression</a>. -See the <a href="../clients/vespa-cli.html#cheat-sheet">Vespa CLI cheat sheet</a> for more examples. -</p> -<pre> -$ vespa visit --selection "id = 'id:mynamespace:music::love-is-here-to-stay'" -$ vespa visit --selection "year = 2018" -</pre> - - - -<h2 id="fields">Fields</h2> -<p> - To select which fields to output, use a <a href="../schemas/documents.html#fieldsets">fieldset</a>. - See <a href="../clients/vespa-cli.html#documents">examples</a> - common use cases are using a comma-separated list of fields - or the <em>[document]</em> / <em>[all]</em> shorthand. -</p> -<pre> -$ vespa visit --field-set=[all] -$ vespa visit --field-set=music:id,year -</pre> - - -<h2 id="timestamp-ranges">Timestamp ranges</h2> -<p> - Both the <a href="../reference/api/document-v1.html">Document V1 API</a> - and the <a href="../clients/vespa-cli.html">Vespa CLI</a> have options for returning documents - last modified within a particular timestamp range. - Either—or both—of the <em>from</em> and <em>to</em> parts of the requested timestamp range - can be specified: -</p> -<ul> - <li>For Document V1, specify the range using the <code>fromTimestamp</code> and - <code>toTimestamp</code> HTTP request parameters (in microseconds). - <li>For <code>vespa visit</code>, specify the range using <code>--from</code> and - <code>--to</code> (in seconds).</li> -</ul> -<p> -Setting a timestamp range is only a <em>filter</em> on the document set that would otherwise -be returned by a visitor without a timestamp range. It does <em>not</em> imply snapshot isolation. -The returned set of documents may be affected by concurrent modifications to documents, as any -modification updates the document timestamp. -</p> - - - -<h2 id="reprocessing">Reprocessing</h2> -<p> -Reprocessing is used to solve these use cases: -</p> -<ul> - <li> - To change the document type used in ways that will not be - backward compatible, define a new type, and reprocess to change all - the existing documents. - </li><li> - Document identifiers can be changed to a new scheme. - </li><li> - Search documents can be reindexed after indexing steps have been changed. - </li> -</ul> -<p> -This example illustrates how one can identify a subset of the -documents in a content cluster, reprocess these, and write them back. -It is assumed that a Vespa cluster is set up, with data. -</p> - - -<h3 id="set-up-a-document-reprocessing-cluster">1. Set up a document reprocessing cluster</h3> -<p>This example document processor:</p> -<ul> - <li>deletes documents with an <em>artist</em> field whose value contains <em>Metallica</em></li> - <li>uppercases <em>title</em> field values of all other documents</li> -</ul> -<pre>{% highlight java %} -import com.yahoo.docproc.Arguments; -import com.yahoo.docproc.DocumentProcessor; -import com.yahoo.docproc.Processing; -import com.yahoo.docproc.documentstatus.DocumentStatus; -import com.yahoo.document.DocumentOperation; -import com.yahoo.document.DocumentPut; -import com.yahoo.document.Document; - -/** - * Example of using a document processor will modify and/or delete - * documents in the context of a reprocessing use case. - */ -public class ReProcessor extends DocumentProcessor { - private String deleteFieldName; - private String deleteRegex; - private String uppercaseFieldName; - - public ReProcessor() { - deleteFieldName = "artist"; - deleteRegex = ".*Metallica.*"; - uppercaseFieldName = "title"; - } - - public Progress process(Processing processing) { - Iterator<DocumentOperation> it = processing.getDocumentOperations().iterator(); - while (it.hasNext()) { - DocumentOperation op = it.next(); - if (op instanceof DocumentPut) { - Document doc = ((DocumentPut) op).getDocument(); - - // Delete the current document if it matches: - String deleteValue = (String) doc.getValue(deleteFieldName); - if (deleteValue != null) { - if (deleteValue.matches(deleteRegex)) { - it.remove(); - continue; - } - } - - // Uppercase the other field: - String uppercaseValue = doc.getValue(uppercaseFieldName).toString(); - if (uppercaseValue != null) { - doc.setValue(uppercaseFieldName, uppercaseValue.toUpperCase()); - } - } - } - return Progress.DONE; - } -} -{% endhighlight %}</pre> -<p> -To compile this processor, see the <a href="../applications/developer-guide.html">Developer Guide</a>. -For more information on document processing, refer -to <a href="../applications/document-processors.html">Document processor Development</a>. -After having changed the Vespa setup, reload config: -</p> -<pre> -$ vespa deploy music -</pre> -<p>Restart nodes as well to activate.</p> - - -<h3 id="select-documents">2. Select documents</h3> -<p> -Define a selection criteria for the documents to be reprocessed. -(To reprocess <em>all</em> documents, skip this). -For this example, assume all documents where the field <em>year</em> is greater than 1995. -The selection string <em>music.year > 1995</em> does this. -</p> - - -<h3 id="set-route">3. Set route</h3> -<p> -The visitor sends documents to a <a href="document-routing.html">Messagebus route</a> - examples: -</p> -<ul> -<li><strong>default</strong> - Documents are sent to the <em>default</em> - route.</li> -<li><strong>indexing</strong> - Documents are sent to <em>indexing</em>.</li> -<li><strong><clustername>/chain.<chainname></strong>: - Documents are sent to the document processing chain <em>chainname</em> running in - cluster <em>clustername</em>.</li> -</ul> -<p> -Assume you have a container cluster with id <em>reprocessing</em> -containing a docproc chain with id <em>reprocessing-chain</em>. -This example route sends documents from the content node, -into the document reprocessing chain, and ultimately, into indexing: -</p> -<pre> -reprocessing/chain.reprocessing-chain indexing -</pre> -<p>Details: <a href="document-routing.html">Message Bus Routing Guide</a>.</p> - - -<h3 id="reprocess">4. Reprocess</h3> -<p>Start reprocessing:</p> -<pre> -$ <a href="../reference/operations/self-managed/tools.html#vespa-visit">vespa-visit</a> -v --selection "music AND music.year > 1995" \ - --datahandler "reprocessing/chain.reprocessing-chain indexing" -</pre> -<p>The '-v' option emits progress information on standard error.</p> - - - -<h2 id="analyzing-field-values">Analyzing field values</h2> -<p> - Use <em>visit</em> to list ids of documents meeting criteria like empty fields - - example, find unset or empty "name" field: -</p> -<pre> -$ vespa visit \ - --pretty-json \ - --cluster default \ - --selection 'restaurant AND (restaurant.name == "" OR restaurant.name == null)' \ - --field-set "[id]" -</pre> -<p> - Note the first part of the selection string "restaurant AND ..." - - this is to ensure that the selection restricts to the <em>restaurant</em> document type (i.e. schema). -</p> -<p> - Also see count and list <a href="../querying/grouping.html#count-fields-with-nan">fields with NaN</a>. -</p> - - - -<h2 id="request-handling">Request handling</h2> -<p> - In short, <em>visit</em> iterates over all, or a set of, <a href="../content/buckets.html">buckets</a> - and sends documents to (a set of) targets. - The target is normally the visit client - (like <a href="../reference/operations/self-managed/tools.html#vespa-visit">vespa-visit</a>), - but can be set a set of targets that act like sinks for the documents - - see <a href="../reference/operations/self-managed/tools.html#vespa-visit-target">vespa-visit-target</a>. -</p> - - -<h3 id="client">Client</h3> -<p> -If the selection criteria managed to map the visitor to a specific set of buckets, -these will be used when sending distributor visit requests. -If not, the visit client will iterate the entire bucket space, -typically at the minimum split-level required to decide correct distributor. -</p><p> -The distributors will receive the requests and look at what buckets it has -that are contained by the requested bucket. -If more than one, the distributor will only start a limited number of bucket visitors at the same time. -Once it has processed the first ones, -it will reply to the visitor client with the last bucket processed. -</p><p> -As all buckets have a natural order, the client can use the returned bucket as a progress counter. -Thus, after a distributor request has returned, the client knows one of the following: -</p> -<ul> - <li>All buckets contained within the bucket sent have been visited</li> - <li>All buckets contained within the bucket sent, - up to this specific bucket in the order, have been visited</li> - <li>No buckets existed that was contained within the requested bucket</li> -</ul> -<p> -The client can decide whether to visit in strict order, -allowing only one bucket to be pending at a time, -or whether to start visiting many buckets at a time, allowing great throughput. -</p> - - -<h3 id="distributor">Distributor</h3> -<p> -The distributors receive visit requests from clients for a given bucket, -which may map to none, one or many buckets within the distributor. -It picks one or more of the first buckets in the order, -picks out one content node copy of each -and passes the request on to the content nodes. -</p><p> -Once all the content node requests have been responded to, -the distributor will reply to the client with the last bucket visited, -to be used as a progress counter. -</p><p> -Subsequent client requests to the distributor will have the progress counter set, -letting the distributor ignore all the buckets prior to that point in the ordering. -</p><p> -Bucket splitting and joining does not alter the ordering, -and does thus not affect visiting much as long as the buckets are consistently split. -If two buckets are joined, where the first one have already been visited, -a visit request has to be sent to the joined bucket. -The content layer use the progress counter to avoid revisiting documents already processed in the bucket. -</p><p> -If the distributor only starts one bucket visitor at a time, -it can ensure the visitor order is kept. -Starting multiple buckets at a time may improve throughput and decrease latency, -but progress tracking will be less fine-grained, -so a bit more documents may need to be revisited when continued after a failure. -</p> - - -<h3 id="content-node">Content node</h3> -<p> -The content node receives visit requests for single buckets for which they store documents. -It may receive many in parallel, but their execution is independent of each other. -</p><p> -The visitor layer in the content node picks up the visit requests. -There it is assigned a visitor thread, -and an instance of the processor type is created for that visitor. -The processor will set up an iterator in the backend -and send one or more requests for documents to the backend. -</p><p> -The document selection specified in the visitor client is sent through to the backend, -allowing it to filter out unwanted data at the lowest level possible. -</p><p> -Once documents are retrieved from the backend, back up to the visitor layer, -the visit processor will process the data. -</p><p> -The default is one iterator request is pending to the backend at any time. -By sending many small iterator requests, having several pending at a time, -the processing may occur in parallel with the document fetching. -</p> - - - -<h2 id="troubleshooting">Troubleshooting</h2> - - -<h3 id="not-exporting-all-documents">Not exporting all documents</h3> -<p> - Normally all documents share the same <a href="../content/buckets.html#bucket-space">bucket space</a>—documents - for multiple schemas are co-located. - When using <a href="../schemas/parent-child.html">parent/child</a>, global documents are stored in a separate bucket space. - Use the <a href="../reference/api/document-v1.html#bucketspace">bucket-space</a> parameter - to visit the <code>default</code> or <code>global</code> space. - This is a common problem when dumping all documents and dumped count is not the expected count. -</p> - - -<h3 id="visiting-performance-is-poor">Visiting performance is poor</h3> -<p> - When visiting, all content nodes may push data to the visitor <em>client</em> in parallel. - Therefore, the client is often the bottleneck. Slow data processing implicitly slows down - the entire visiting process. In particular, large floating point tensor fields are very - expensive to render as JSON. -</p> -<p> - To verify if client-side rendering is the bottleneck, - run <a href="../reference/operations/self-managed/tools.html#vespa-visit">vespa-visit</a> with - your usual selection criteria and field set, - but add the <code>--nullrender</code> argument (available from Vespa 8.134). - This receives and processes documents as usual, but completely skips rendering. -</p> -<p> - If you are redirecting the output of visiting to any custom business logic (such as running - <code>jq</code> on the stream of documents), make sure you are not accidentally buffering up - data internally—this goes for both input and output. To verify if processing the visitor output - is the bottleneck, run visiting with <code>stdout</code> redirected to <code>/dev/null</code> - and compare the time taken. -</p> -<p> - If the client is not the bottleneck, it is possible visiting performance is limited by disk - performance. Non-attribute fields are not memory backed and must be fetched from disk when - evaluating selections. This includes document IDs, which must always be returned for matching - documents. To see if any fields are particularly expensive to fetch or return, run visiting - with a selection and/or field set that does <em>not</em> include potentially expensive fields. -</p> - - -<h3 id="visitor-operations-are-hanging">Visitor operations are hanging</h3> -<p> - A visit operation might stall/hang if the content cluster is in an inconsistent - state—replicas are still merging between nodes. -</p> - - -<h3 id="handshake-failed">Handshake failed</h3> -<p>Running vespa visit via the <a href="document-v1-api-guide.html">/document/v1</a> API:</p> -<pre> -[HANDSHAKE_FAILED @ localhost]: An error occurred while resolving version of recipient(s) -[tcp/container0:37227/visitor-1-1682523227698 at tcp/container0:37227] from host 'content0' -</pre> -<p> - The visit client in this case is the Vespa Container node with the /document/v1 interface. - A visit is a relatively long-lived operation - - the client starts a visitor operation on each Content node, - that connects back to the client (here <code>tcp/container0:37227</code>) to send data. - This might sound a bit odd - why connect back? -</p> -<p> - The idea is that results of the visitor operation might be pushed to multiple destinations for increased throughput - - see <a href="#request-handling">request handling</a>. - This explains why it connects back on a random port, - and why one cannot see the port in <a href="../reference/operations/self-managed/tools.html#vespa-model-inspect">vespa-model-inspect</a> - it is temporary. -</p> -<p> - This also means, Vespa-nodes must be able to intercommunicate on all ports, - see <a href="../operations/self-managed/docker-containers.html">Docker containers</a>. -</p> -<p> - Check <a href="https://github.com/vespa-engine/sample-apps/tree/master/examples/operations/multinode-HA">multinode-HA</a> - for an example - a Docker network is used for all containers - also see "network" in - <a href="https://github.com/vespa-engine/sample-apps/blob/master/examples/operations/multinode-HA/docker-compose.yaml"> - docker-compose.yaml</a>. -</p> -<p> - Another source of this error can be an unresponsive container instance, e.g. during overload. -</p> diff --git a/en/writing/visiting.mdx b/en/writing/visiting.mdx new file mode 100644 index 0000000000..d2fa1f9b0c --- /dev/null +++ b/en/writing/visiting.mdx @@ -0,0 +1,312 @@ +--- +title: "Visiting" +--- + +Visiting is a feature to efficiently get or process a set of documents, identified by a [document selection expression](/en/reference/writing/document-selector-language). + +Visiting is often used to back up or migrate applications, [cloning applications and data](/en/operations/cloning) is a good guide for this. + +Use the [Vespa CLI](/en/clients/vespa-cli) to run visit — example, using the [quick start](/en/basics/deploy-an-application-local): + +```sh +$ vespa visit +``` + +```json +{ + "id": "id:mynamespace:music::love-is-here-to-stay", + "fields": { + "artist":"Diana Krall", + "year":2018, + "category_scores": { + "type": "tensor<float>(cat{})", + "cells": { + "pop": 0.4000000059604645, + "rock": 0.0, + "jazz": 0.800000011920929 + } + }, + "album": "Love Is Here To Stay" + } +} +``` + +Typically, the visit use cases are not time sensitive, like data reprocessing, and document dump for backup and cluster clone — [cloning applications and data](/en/operations/cloning) is a good read for more details. + +Visiting does not have snapshot isolation—it returns the state of documents as they were when iterated over. Iteration order is implementation-specific. See [the consistency documentation](/en/content/consistency#read-consistency) for more details. + +<Note> +**Note:** + +Due to the bucket iteration, visiting is normally a high-latency iteration. Even an empty content cluster is pre-partitioned into many data buckets to enable scaling. Dumping just one document requires iteration over all data buckets, unless *location*-specific selections are used. To test visiting performance, use larger data sets; do not extrapolate from small. Use the [query API](/en/querying/query-api) or [`vespa document get`](/en/clients/vespa-cli#documents) for low latency operations on small result sets. +</Note> + +See [request handling](#request-handling) for details on how visiting works. Also see the internal [vespa-visit](/en/reference/operations/self-managed/tools#vespa-visit) tool. For programmatic access, see the [visitor session API](/en/writing/document-api-guide#visitorsession). + +## Visiting from pyvespa + +The [pyvespa library](https://vespa-engine.github.io/pyvespa/reads-writes#visiting) provides a convenient Python interface for visiting documents. + +## Data export + +Export data to stdout: + +```sh +$ vespa visit +``` + +To export a subset, `--slices 100 --slice-id 0` exports 1% of the corpus by efficiently iterating over only 1/100th of the data space. For a given number of `--slices`, it's possible to visit the entire corpus (possibly in parallel) with non-overlapping output by visiting with all `--slice-id` values from (and including) 0 up to (and excluding) `--slices`: + +```sh +$ vespa visit --slices 100 --slice-id 0 +``` + +<Note> +**Note:** + +If the application has [global document types](/en/reference/applications/services/content#document), use `--bucket-space global` to visit these documents. By default, visiting only iterates over non-global documents. +</Note> + +### Performance note: feeding data exported via visiting + +Vespa uses hashing to distribute documents pseudo-randomly across many [buckets](/en/content/buckets). The operations in an incoming document stream are expected to be distributed evenly across both the set of all buckets and the content nodes storing them. This parallelizes the load and efficiently utilizes the hardware resources in the cluster. + +Visiting iterates over the buckets in the cluster, returning all documents stored in a bucket before moving on to the next bucket (often processing many buckets in parallel). As a consequence there's a direct correlation between the internal document-to-bucket distribution and the document output ordering of the visiting process. + +If the output of visiting is fed directly back into a content cluster, this correlation means that the stream of documents is no longer uniformly distributed across buckets and/or content nodes. Prior to Vespa 8.349 this is likely to greatly reduce feeding performance due to increased contention around backend bucket-level write locks and indexing threads. Vespa 8.349 and beyond contains optimizations that bring re-feeding performance much closer to that of initial feeding. + +A simple strategy to avoid this problem is to *shuffle* the visiting output prior to re-feeding it. This removes any correlation between feed operations and the underlying data distribution. + +## Selection + +To select (i.e. filter) which documents to visit, use a [document selection expression](/en/reference/writing/document-selector-language). See the [Vespa CLI cheat sheet](/en/clients/vespa-cli#cheat-sheet) for more examples. + + +```sh +$ vespa visit --selection "id = 'id:mynamespace:music::love-is-here-to-stay'" +$ vespa visit --selection "year = 2018" +``` + +## Fields + +To select which fields to output, use a [fieldset](/en/schemas/documents#fieldsets). See [examples](/en/clients/vespa-cli#documents) — common use cases are using a comma-separated list of fields or the *[document]* / *[all]* shorthand. + +```sh +$ vespa visit --field-set=[all] +$ vespa visit --field-set=music:id,year +``` + + +## Timestamp ranges + +Both the [Document V1 API](/en/reference/api/document-v1) and the [Vespa CLI](/en/clients/vespa-cli) have options for returning documents last modified within a particular timestamp range. Either—or both—of the *from* and *to* parts of the requested timestamp range can be specified: + +- For Document V1, specify the range using the `fromTimestamp` and `toTimestamp` HTTP request parameters (in microseconds). +- For `vespa visit`, specify the range using `--from` and `--to` (in seconds). + +Setting a timestamp range is only a *filter* on the document set that would otherwise be returned by a visitor without a timestamp range. It does *not* imply snapshot isolation. The returned set of documents may be affected by concurrent modifications to documents, as any modification updates the document timestamp. + +## Reprocessing + +Reprocessing is used to solve these use cases: + +- To change the document type used in ways that will not be backward compatible, define a new type, and reprocess to change all the existing documents. +- Document identifiers can be changed to a new scheme. +- Search documents can be reindexed after indexing steps have been changed. + +This example illustrates how one can identify a subset of the documents in a content cluster, reprocess these, and write them back. It is assumed that a Vespa cluster is set up, with data. + +### 1. Set up a document reprocessing cluster + +This example document processor: + +- deletes documents with an *artist* field whose value contains *Metallica* +- uppercases *title* field values of all other documents + +```js expandable +import com.yahoo.docproc.Arguments; +import com.yahoo.docproc.DocumentProcessor; +import com.yahoo.docproc.Processing; +import com.yahoo.docproc.documentstatus.DocumentStatus; +import com.yahoo.document.DocumentOperation; +import com.yahoo.document.DocumentPut; +import com.yahoo.document.Document; + +/** + * Example of using a document processor will modify and/or delete + * documents in the context of a reprocessing use case. + */ +public class ReProcessor extends DocumentProcessor { + private String deleteFieldName; + private String deleteRegex; + private String uppercaseFieldName; + + public ReProcessor() { + deleteFieldName = "artist"; + deleteRegex = ".*Metallica.*"; + uppercaseFieldName = "title"; + } + + public Progress process(Processing processing) { + Iterator<DocumentOperation> it = processing.getDocumentOperations().iterator(); + while (it.hasNext()) { + DocumentOperation op = it.next(); + if (op instanceof DocumentPut) { + Document doc = ((DocumentPut) op).getDocument(); + + // Delete the current document if it matches: + String deleteValue = (String) doc.getValue(deleteFieldName); + if (deleteValue != null) { + if (deleteValue.matches(deleteRegex)) { + it.remove(); + continue; + } + } + + // Uppercase the other field: + String uppercaseValue = doc.getValue(uppercaseFieldName).toString(); + if (uppercaseValue != null) { + doc.setValue(uppercaseFieldName, uppercaseValue.toUpperCase()); + } + } + } + return Progress.DONE; + } +} +``` + +To compile this processor, see the [Developer Guide](/en/applications/developer-guide). For more information on document processing, refer to [Document processor Development](/en/applications/document-processors). After having changed the Vespa setup, reload config: + +```bash +$ vespa deploy music +``` + +Restart nodes as well to activate. + +### 2. Select documents + +Define a selection criteria for the documents to be reprocessed. (To reprocess *all* documents, skip this). For this example, assume all documents where the field *year* is greater than 1995. The selection string *music.year > 1995* does this. + +### 3. Set route + +The visitor sends documents to a [Messagebus route](/en/writing/document-routing) - examples: + +- **default** - Documents are sent to the *default* route. +- **indexing** - Documents are sent to *indexing*. +- **`<clustername>/chain.<chainname>`**: Documents are sent to the document processing chain *chainname* running in cluster *clustername*. + +Assume you have a container cluster with id *reprocessing* containing a docproc chain with id *reprocessing-chain*. This example route sends documents from the content node, into the document reprocessing chain, and ultimately, into indexing: + +```bash +reprocessing/chain.reprocessing-chain indexing +``` + +Details: [Message Bus Routing Guide](/en/writing/document-routing). + +### 4. Reprocess + +Start reprocessing: + +```bash +$ [vespa-visit](/en/reference/operations/self-managed/tools.html#vespa-visit) -v --selection "music AND music.year > 1995" \ + --datahandler "reprocessing/chain.reprocessing-chain indexing" +``` + +The '-v' option emits progress information on standard error. + +## Analyzing field values + +Use *visit* to list ids of documents meeting criteria like empty fields - example, find unset or empty "name" field: + +```bash +$ vespa visit \ + --pretty-json \ + --cluster default \ + --selection 'restaurant AND (restaurant.name == "" OR restaurant.name == null)' \ + --field-set "[id]" +``` + +Note the first part of the selection string "restaurant AND ..." - this is to ensure that the selection restricts to the *restaurant* document type (i.e. schema). + +Also see count and list [fields with NaN](/en/querying/grouping#count-fields-with-nan). + +## Request handling + +In short, *visit* iterates over all, or a set of, [buckets](/en/content/buckets) and sends documents to (a set of) targets. The target is normally the visit client (like [vespa-visit](/en/reference/operations/self-managed/tools#vespa-visit)), but can be set a set of targets that act like sinks for the documents - see [vespa-visit-target](/en/reference/operations/self-managed/tools#vespa-visit-target). + +### Client + +If the selection criteria managed to map the visitor to a specific set of buckets, these will be used when sending distributor visit requests. If not, the visit client will iterate the entire bucket space, typically at the minimum split-level required to decide correct distributor. + +The distributors will receive the requests and look at what buckets it has that are contained by the requested bucket. If more than one, the distributor will only start a limited number of bucket visitors at the same time. Once it has processed the first ones, it will reply to the visitor client with the last bucket processed. + +As all buckets have a natural order, the client can use the returned bucket as a progress counter. Thus, after a distributor request has returned, the client knows one of the following: + +- All buckets contained within the bucket sent have been visited +- All buckets contained within the bucket sent, up to this specific bucket in the order, have been visited +- No buckets existed that was contained within the requested bucket + +The client can decide whether to visit in strict order, allowing only one bucket to be pending at a time, or whether to start visiting many buckets at a time, allowing great throughput. + +### Distributor + +The distributors receive visit requests from clients for a given bucket, which may map to none, one or many buckets within the distributor. It picks one or more of the first buckets in the order, picks out one content node copy of each and passes the request on to the content nodes. + +Once all the content node requests have been responded to, the distributor will reply to the client with the last bucket visited, to be used as a progress counter. + +Subsequent client requests to the distributor will have the progress counter set, letting the distributor ignore all the buckets prior to that point in the ordering. + +Bucket splitting and joining does not alter the ordering, and does thus not affect visiting much as long as the buckets are consistently split. If two buckets are joined, where the first one have already been visited, a visit request has to be sent to the joined bucket. The content layer use the progress counter to avoid revisiting documents already processed in the bucket. + +If the distributor only starts one bucket visitor at a time, it can ensure the visitor order is kept. Starting multiple buckets at a time may improve throughput and decrease latency, but progress tracking will be less fine-grained, so a bit more documents may need to be revisited when continued after a failure. + +### Content node + +The content node receives visit requests for single buckets for which they store documents. It may receive many in parallel, but their execution is independent of each other. + +The visitor layer in the content node picks up the visit requests. There it is assigned a visitor thread, and an instance of the processor type is created for that visitor. The processor will set up an iterator in the backend and send one or more requests for documents to the backend. + +The document selection specified in the visitor client is sent through to the backend, allowing it to filter out unwanted data at the lowest level possible. + +Once documents are retrieved from the backend, back up to the visitor layer, the visit processor will process the data. + +The default is one iterator request is pending to the backend at any time. By sending many small iterator requests, having several pending at a time, the processing may occur in parallel with the document fetching. + +## Troubleshooting + +### Not exporting all documents + +Normally all documents share the same [bucket space](/en/content/buckets#bucket-space)—documents for multiple schemas are co-located. When using [parent/child](/en/schemas/parent-child), global documents are stored in a separate bucket space. Use the [bucket-space](/en/reference/api/document-v1#bucketspace) parameter to visit the `default` or `global` space. This is a common problem when dumping all documents and dumped count is not the expected count. + +### Visiting performance is poor + +When visiting, all content nodes may push data to the visitor *client* in parallel. Therefore, the client is often the bottleneck. Slow data processing implicitly slows down the entire visiting process. In particular, large floating point tensor fields are very expensive to render as JSON. + +To verify if client-side rendering is the bottleneck, run [vespa-visit](/en/reference/operations/self-managed/tools#vespa-visit) with your usual selection criteria and field set, but add the `--nullrender` argument (available from Vespa 8.134). This receives and processes documents as usual, but completely skips rendering. + +If you are redirecting the output of visiting to any custom business logic (such as running `jq` on the stream of documents), make sure you are not accidentally buffering up data internally—this goes for both input and output. To verify if processing the visitor output is the bottleneck, run visiting with `stdout` redirected to `/dev/null` and compare the time taken. + +If the client is not the bottleneck, it is possible visiting performance is limited by disk performance. Non-attribute fields are not memory backed and must be fetched from disk when evaluating selections. This includes document IDs, which must always be returned for matching documents. To see if any fields are particularly expensive to fetch or return, run visiting with a selection and/or field set that does *not* include potentially expensive fields. + +### Visitor operations are hanging + +A visit operation might stall/hang if the content cluster is in an inconsistent state—replicas are still merging between nodes. + +### Handshake failed + +Running vespa visit via the [/document/v1](/en/reference/api/document-v1) API: + +```bash +[HANDSHAKE_FAILED @ localhost]: An error occurred while resolving version of recipient(s) +[tcp/container0:37227/visitor-1-1682523227698 at tcp/container0:37227] from host 'content0' +``` + +The visit client in this case is the Vespa Container node with the /document/v1 interface. A visit is a relatively long-lived operation - the client starts a visitor operation on each Content node, that connects back to the client (here `tcp/container0:37227`) to send data. This might sound a bit odd - why connect back? + +The idea is that results of the visitor operation might be pushed to multiple destinations for increased throughput - see [request handling](#request-handling). This explains why it connects back on a random port, and why one cannot see the port in [vespa-model-inspect](/en/reference/operations/self-managed/tools#vespa-model-inspect) - it is temporary. + +This also means, Vespa-nodes must be able to intercommunicate on all ports, see [Docker containers](/en/operations/self-managed/docker-containers). + +Check [multinode-HA](https://github.com/vespa-engine/sample-apps/tree/master/examples/operations/multinode-HA) for an example - a Docker network is used for all containers - also see "network" in [docker-compose.yaml](https://github.com/vespa-engine/sample-apps/blob/master/examples/operations/multinode-HA/docker-compose.yaml). + +Another source of this error can be an unresponsive container instance, e.g. during overload. \ No newline at end of file diff --git a/extract_redirects.py b/extract_redirects.py deleted file mode 100755 index 2a8211cdb2..0000000000 --- a/extract_redirects.py +++ /dev/null @@ -1,141 +0,0 @@ -#!/usr/bin/env python3 -# Copyright Vespa.ai. All rights reserved. -""" -Extract all Jekyll redirect_from entries and generate a redirects.yml file. - -This script scans the repository for Jekyll pages (HTML and Markdown files) that use -the jekyll-redirect-from plugin with 'redirect_from' in their front matter, and -generates a consolidated redirects.yml file with all redirects in the format: - /old-path: /new-path - -Usage: - python3 extract_redirects.py - -Output: - redirects.yml - Contains all redirect mappings sorted alphabetically by old path -""" - -import os -import re -from pathlib import Path - - -def extract_redirect_from(file_path): - """Extract redirect_from entries from YAML front matter.""" - with open(file_path, 'r', encoding='utf-8') as f: - content = f.read() - - # Match YAML front matter between --- delimiters - match = re.match(r'^---\s*\n(.*?\n)---\s*\n', content, re.DOTALL) - if not match: - return None - - front_matter = match.group(1) - - # Look for redirect_from entries - # Can be single line: redirect_from: /path - # Or multi-line list: - # redirect_from: - # - /path1 - # - /path2 - redirects = [] - - # Try to find redirect_from block - redirect_match = re.search(r'redirect_from:\s*\n((?:\s*-\s*.+\n)+)', front_matter) - if redirect_match: - # Multi-line format - redirect_lines = redirect_match.group(1) - for line in redirect_lines.strip().split('\n'): - path = line.strip().lstrip('-').strip() - if path: - redirects.append(path) - else: - # Try single line format - single_match = re.search(r'redirect_from:\s*(.+?)(?:\n|$)', front_matter) - if single_match: - path = single_match.group(1).strip() - if path: - redirects.append(path) - - return redirects if redirects else None - - -def find_redirects(root_dir='.'): - """Find all files with redirect_from in their front matter.""" - redirects = {} - - # Common extensions for Jekyll pages - extensions = ['.html', '.md', '.markdown'] - - # Directories to exclude from scanning - excluded_dirs = ['vendor/', 'node_modules/', '.git/'] - - for ext in extensions: - # Use glob to find all files with these extensions - for file_path in Path(root_dir).rglob(f'*{ext}'): - # Skip files in excluded directories - str_path = str(file_path) - if any(excluded in str_path for excluded in excluded_dirs): - continue - if any(part.startswith('_') or part.startswith('.') for part in file_path.parts): - if not str_path.startswith('_'): # Allow files in root starting with _ - continue - - try: - redirect_from_list = extract_redirect_from(file_path) - if redirect_from_list: - # Get the relative path from root - relative_path = file_path.relative_to(root_dir) - # Convert to URL path (use forward slashes) - new_path = str(relative_path).replace('\\', '/') - - # Store each old path -> new path mapping - for old_path in redirect_from_list: - # Ensure paths start with / - if not old_path.startswith('/'): - old_path = '/' + old_path - if not new_path.startswith('/'): - new_path = '/' + new_path - - redirects[old_path] = new_path - - except Exception as e: - print(f"Warning: Error processing {file_path}: {e}") - continue - - return redirects - - -def write_redirects_yaml(redirects, output_file='redirects.yml'): - """Write redirects to YAML file in the format /old-path: /new-path.""" - # Sort by old path for consistency - sorted_redirects = dict(sorted(redirects.items())) - - with open(output_file, 'w', encoding='utf-8') as f: - f.write("# Copyright Vespa.ai. All rights reserved.\n") - f.write("# Generated redirect mappings from Jekyll redirect_from plugin\n") - f.write("# Format: /old-path: /new-path\n\n") - - for old_path, new_path in sorted_redirects.items(): - # Quote paths if they contain special characters - old_quoted = f'"{old_path}"' if ':' in old_path or '#' in old_path else old_path - new_quoted = f'"{new_path}"' if ':' in new_path or '#' in new_path else new_path - f.write(f"{old_quoted}: {new_quoted}\n") - - -def main(): - """Main function.""" - print("Scanning for Jekyll redirect_from entries...") - redirects = find_redirects() - - print(f"Found {len(redirects)} redirects") - - if redirects: - write_redirects_yaml(redirects) - print(f"Redirects written to redirects.yml") - else: - print("No redirects found") - - -if __name__ == '__main__': - main() diff --git a/icons/favicon-32x32.png b/favicon.png similarity index 100% rename from icons/favicon-32x32.png rename to favicon.png diff --git a/feed-split.py b/feed-split.py deleted file mode 100755 index b68284ab00..0000000000 --- a/feed-split.py +++ /dev/null @@ -1,260 +0,0 @@ -#!/usr/bin/env python3 -# Copyright Vespa.ai. All rights reserved. -import copy -import json -import sys -from bs4 import BeautifulSoup -from markdownify import markdownify -import random -import re -from xml.sax.saxutils import escape -import tiktoken -import urllib.parse - -encoding = tiktoken.encoding_for_model("gpt-4o-mini") -note_pattern = re.compile(r"{%\s*include.*?%}", flags=re.DOTALL) -highlight_pattern = re.compile(r"{%\s*.*?\s%}", flags=re.DOTALL) - - -def what_language(el): - z = re.match(r"{%\s*highlight\s*(\w+)\s%}", el.text) - if z: - lang = z.group(1) - return lang - if el.text.find("curl") > 0: - return "bash" - if el.text.find("import com.yahoo") > 0: - return "java" - return "" - -def remove_jekyll(text): - text = note_pattern.sub("", text) - text = highlight_pattern.sub("", text) - return text - -def xml_fixup(text): - regex = r"{%\s*highlight xml\s*%}(.*?){%\s*endhighlight\s*%}" - matches = re.findall(regex, text, re.DOTALL) - for match in matches: - escaped_match = escape(match) - text = text.replace(match,escaped_match) - return text - - -def is_selfhosted_doc(doc): - path = doc['fields']['path'] - if "/en/operations-selfhosted" in path: - return True - return False - -def create_text_doc(doc, paragraph, paragraph_id, header): - id = doc['put'] - #id:open:doc::open/en/access-logging.html# - _,namespace,doc_type,_,id = id.split(":") - #print("n={},doc_type={},id={}".format(namespace,doc_type,id)) - - new_namespace = namespace + "-p" - id = "id:{}:{}::{}".format(new_namespace, "paragraph", id) - fields = doc['fields'] - n_tokens = len(encoding.encode(paragraph)) - new_doc = { - "put": id, - "fields": { - "title": fields['title'], - "path": fields['path'], - "doc_id": fields['path'], - "namespace": new_namespace, - "content": paragraph, - "content_tokens": n_tokens, - "base_uri": sys.argv[2], - "selfhosted": is_selfhosted_doc(doc) - } - } - - if header: - title = fields['title'] - new_title = title + " - " + header - new_doc["fields"]["title"] = new_title - - if paragraph_id is None: - paragraph_id = str(random.randint(0,1000)) - - new_doc['fields']['path'] = new_doc['fields']['path'] + \ - "#" + paragraph_id.replace("?", "") - new_doc['put'] = new_doc['put'] + "-" + urllib.parse.quote(paragraph_id) - - return new_doc - - -def split_text(soup): - split_tables(soup) - split_lists(soup) - md = markdownify(str(soup), heading_style='ATX', code_language_callback=what_language) - lines = md.split("\n") - header = "" - text = "" - id = "" - data = [] - for line in lines: - if line.startswith("#"): - if text: - data.append((id, header, text)) - text = "" - header = line.lstrip("#").replace("\\", "") - id = "-".join(header.replace(',', '').split()).lower() - else: - text = text + "\n" + line - - data.append((id, header, text)) #Flush any last data - return data - - -def remove_notext_tags(soup): - for remove_tag in soup.find_all(['style', 'script']): - remove_tag.decompose() - - -def split_lists(soup): - for list in soup.body.find_all(['ul', 'ol']): - for list_item in list.find_all('li'): - move_linkable_item_to_single_entity(soup, list_item) - - -def split_tables(soup): - top_level_tables = soup.body.find_all('table', recursive=False) # i.e., do not find rows in tables within tables - for table in top_level_tables: - tbody = table.find('tbody') # tbody is implicit and always there - for row in tbody.find_all('tr', recursive=False): - move_linkable_item_to_single_entity(soup, row) - - -def table_header_row(row): - thead = row.find_parent('table').find('thead') - if thead is not None: - return thead.find('tr') - return None - - -def max_cols(tbody): - max_cols = 0 - if tbody is None: - return max_cols - for row in tbody.find_all('tr'): - cols = len(row.find_all(['th', 'td'])) - if cols > max_cols: - max_cols = cols - return max_cols - - -def add_header_to_tables_if_missing(soup): - for table in soup.body.find_all('table'): - thead = table.find('thead') - if thead is None: - thead = soup.new_tag('thead') - table.insert(0, thead) - header_row = thead.find('tr') - if header_row is None: - tr = soup.new_tag('tr') - thead.append(tr) - for _ in range(max_cols(table.tbody)): - tr.append(soup.new_tag('th')) - - -def move_linkable_item_to_single_entity(soup, item): - id_elem = item.find('p', {'id': True}) - if id_elem is not None: - new_h4 = soup.new_tag('h4', id=id_elem['id']) - new_h4.string = id_elem['id'] - if item.name == 'tr': - new_container = soup.new_tag('table') - new_thead = soup.new_tag('thead') - new_tbody = soup.new_tag('tbody') - new_container.append(new_thead) - new_container.append(new_tbody) - header_row = table_header_row(item) - if header_row is not None: - new_thead.append(copy.copy(header_row)) - else: - dummy_tr = soup.new_tag('tr') - for _ in item.find_all(['th', 'td']): - dummy_tr.append(soup.new_tag('th')) - new_thead.append(dummy_tr) - new_tbody.append(item) - soup.append(new_h4) - soup.append(new_container) - else: - item.name = 'div' # change <li> to <div> for nicer display - soup.append(new_h4) - new_h4.insert_after(item) - - -def fix_pre_content(soup): - # Workaround: ensure no faux headings from pre content - for pre in soup.body.find_all('pre'): - pre_text = pre.get_text() - pre.string = re.sub(r'^#', ' #', pre_text, flags=re.MULTILINE) - - -def main(): - with open(sys.argv[1]) as fp: - random.seed(42) - docs = json.load(fp) - operations = [] - for doc in docs: - html_doc = doc['fields']['html'] - html_doc = xml_fixup(html_doc) - soup = BeautifulSoup(html_doc, 'html5lib') - remove_notext_tags(soup) - fix_pre_content(soup) - add_header_to_tables_if_missing(soup) - data = split_text(soup) - - for paragraph_id, header, paragraph in data: - - paragraph = paragraph.lstrip('\n').lstrip(" ") - paragraph = paragraph.rstrip('\n') - - paragraph = re.sub(r"\n{2,}", "\n\n", paragraph) - - paragraph = re.sub(r"\n*```", "\n```", paragraph) - paragraph = re.sub(r"```\n*", "```\n", paragraph) - - paragraph = paragraph.replace("```\njson","```json") - paragraph = paragraph.replace("```\nxml","```xml") - paragraph = paragraph.replace("```\nbash","```bash") - paragraph = paragraph.replace("```\nsh","```sh") - paragraph = paragraph.replace("```\nraw","```\n") - paragraph = paragraph.replace("```\njava","```java\n") - - # Necessary backslashes and quotes will be added when json-serialized. - paragraph = paragraph.replace("\\", "") - - paragraph = remove_jekyll(paragraph) - - if paragraph: - paragraph_doc = create_text_doc(doc, paragraph, paragraph_id, header) - operations.append(paragraph_doc) - - #Merge question expansion - questions_expansion = dict() - with open(sys.argv[3]) as fp: - for line in fp: - op = json.loads(line) - id = op['update'] - fields = op['fields'] - if "questions" in fields: - questions = fields['questions']['assign'] - questions_expansion[id] = questions - for op in operations: - id = op['put'] - if id in questions_expansion: - op['fields']['questions'] = questions_expansion[id] - else: - op['fields']['questions'] = [op['fields']['title']] - - with open("paragraph_index.json", "w") as fp: - json.dump(operations, fp) - - -if __name__ == "__main__": - main() diff --git a/feed_to_vespa.py b/feed_to_vespa.py deleted file mode 100755 index 75995bc7ed..0000000000 --- a/feed_to_vespa.py +++ /dev/null @@ -1,207 +0,0 @@ -#!/usr/bin/env python3 -# Copyright Vespa.ai. All rights reserved. - -import json -import os -import re -import subprocess -import sys -import yaml -import requests -from requests.adapters import HTTPAdapter, Retry -import urllib.parse - -def find(json, path, separator = "."): - if len(path) == 0: return json - head, _, rest = path.partition(separator) - return find(json[head], rest) if head in json else None - - -# extract <id> from form id:open:doc::<id> -def get_document_id(id): - return id[id.rfind(":")+1:] - - -def get_private_key_path(): - private_key_path = "data-plane-private-key.pem" - if not os.path.isfile(private_key_path): - private_key_raw = os.environ['DATA_PLANE_PRIVATE_KEY'] - private_key = private_key_raw.replace(" ", "\n") - with open(private_key_path, "w") as f: - f.write("-----BEGIN PRIVATE KEY-----\n" + private_key + "\n-----END PRIVATE KEY-----") - return private_key_path - - -def get_public_cert_path(): - public_cert_path = "data-plane-public-key.pem" - if not os.path.isfile(public_cert_path): - public_cert_raw = os.environ['DATA_PLANE_PUBLIC_KEY'] - public_cert = public_cert_raw.replace(" ", "\n") - with open(public_cert_path, "w") as f: - f.write("-----BEGIN CERTIFICATE-----\n" + public_cert + "\n-----END CERTIFICATE-----") - return public_cert_path - - -def vespa_get(endpoint, operation, options): - url = "{0}/{1}?{2}".format(endpoint, operation, "&".join(options)) - return session.get(url).json() - - -def vespa_delete(endpoint, operation, options): - url = "{0}/{1}?{2}".format(endpoint, operation, "&".join(options)) - return session.delete(url).json() - - -def vespa_visit(endpoint, namespace, doc_type, continuation = None): - options = [] - options.append("wantedDocumentCount=500") - options.append("timeout=60s") - if continuation is not None and len(continuation) > 0: - options.append("&continuation={0}".format(continuation)) - return vespa_get(endpoint, "document/v1/{0}/{1}/docid".format(namespace,doc_type), options) - - -def vespa_remove(endpoint, doc_ids, namespace, doc_type): - options = [] - for doc_id in doc_ids: - id = get_document_id(doc_id) - vespa_delete(endpoint, "document/v1/{0}/{1}/docid/{2}".format(namespace, doc_type, id), options) - - -def vespa_feed(endpoint, feed, namespace, doc_type): - if doc_type not in ["paragraph", "term", "doc"]: - raise ValueError(":error:Unknown vespa doc_type: {0}".format(doc_type)) - - splits = re.split(r'/|\.', endpoint) - app_string = splits[3] + '.' + splits[2] - print("Feeding to app: {0} , endpoint: {1}".format(app_string, endpoint)) - - process = subprocess.run(['vespa', 'feed', '-a', app_string, '-t', endpoint, feed], capture_output=True) - - # Print sderr if not empty - if process.stderr: - print("::group::VespaCLI-Error") - print("::error::Errors reported by VespaCLI:") - print(process.stderr.decode('utf-8')) - print("::endgroup::") - - if process.returncode != 0: - print("::error::Errors encountered while feeding Vespa application.") - sys.exit(process.returncode) - - return process.stdout.decode('utf-8') - - -def get_docs(index): - file = open(index, "r", encoding='utf-8') - return json.load(file) - - -def get_indexed_docids(endpoint, namespace, doc_type): - docids = set() - continuation = "" - while continuation is not None: - json = vespa_visit(endpoint, namespace, doc_type, continuation) - documents = find(json, "documents") - if documents is not None: - ids = [ find(document, "id") for document in documents ] - for id in ids: - # The document ID might contain chars that needs to be escaped for the delete/put operation to work - # also for comparison with what is in the feed - docid = get_document_id(id) # return the last part - encoded = urllib.parse.quote(docid) #escape - id = id.replace(docid, encoded) - docids.add(id) - continuation = find(json, "continuation") - return docids - - -def get_feed_docids(feed, namespace, doc_type): - with open(feed, "r", encoding='utf-8') as f: - feed_json = json.load(f) - if doc_type == "doc": - return set(["id:{0}:doc::".format(namespace) + find(doc, "fields.namespace") + find(doc, "fields.path") for doc in feed_json]) - elif doc_type == "term": - return set(["id:{0}:term::".format(namespace) + str(find(doc, "fields.hash")) for doc in feed_json]) - elif doc_type == "paragraph": - return set([doc['put'] for doc in feed_json]) - - -def print_header(msg): - print("") - print("*" * 80) - print("* {0}".format(msg)) - print("*" * 80) - - -def read_config(config_file): - with open(config_file, "r") as f: - return yaml.safe_load(f) - - -def update_endpoint(endpoint, config): - do_remove_index = config["search"]["do_index_removal_before_feed"] - do_feed = config["search"]["do_feed"] - namespace = config["search"]["namespace"] - doc_type = config["search"]["doc_type"] - - endpoint_url = endpoint["url"] - endpoint_url = endpoint_url[:-1] if endpoint_url.endswith("/") else endpoint_url - endpoint_indexes = endpoint["indexes"] - - print_header("Retrieving already indexed document IDs for endpoint {0}".format(endpoint_url)) - docids_in_index = get_indexed_docids(endpoint_url, namespace, doc_type) - print("{0} documents found.".format(len(docids_in_index))) - - if do_remove_index: - print_header("Removing all indexed documents in {0}".format(endpoint_url)) - vespa_remove(endpoint_url, docids_in_index, namespace, doc_type) - print("{0} documents removed.".format(len(docids_in_index))) - - if do_feed: - docids_in_feed = set() - print_header("Parsing feed file(s) for document IDs") - for index in endpoint_indexes: - assert os.path.exists(index) - docids_in_feed = docids_in_feed.union(get_feed_docids(index, namespace, doc_type)) - print("{0} documents found.".format(len(docids_in_feed))) - - if len(docids_in_feed) == 0: - return - - docids_to_remove = docids_in_index.difference(docids_in_feed) - if len(docids_to_remove) > 0: - print("::group::Removing indexed documents not in feed in {0}".format(endpoint_url)) - for id in docids_to_remove: - print("To Remove: {0}".format(id)) - vespa_remove(endpoint_url, docids_to_remove, namespace, doc_type) - print("{0} documents removed.".format(len(docids_to_remove))) - print("::endgroup::") - else: - print("No documents to be removed.") - - for index in endpoint_indexes: - print("::group::Feeding {0} to {1}".format(index, endpoint_url)) - print(vespa_feed(endpoint_url, index, namespace, doc_type)) - print("::endgroup::") - - print("{0} documents fed.".format(len(docids_in_feed))) - - -def main(): - configuration_file = sys.argv[1] - config = read_config(configuration_file) - global session - session = requests.Session() - retries = Retry(total=10, connect=10, - backoff_factor=0.8, - status_forcelist=[ 500, 503, 504, 429 ] - ) - session.mount('https://', HTTPAdapter(max_retries=retries)) - session.cert = (get_public_cert_path(), get_private_key_path()) - for endpoint in config["search"]["feed_endpoints"]: - update_endpoint(endpoint, config) - - -if __name__ == "__main__": - main() diff --git a/generate_suggestions_from_reference.py b/generate_suggestions_from_reference.py deleted file mode 100755 index 9bf3ad76d8..0000000000 --- a/generate_suggestions_from_reference.py +++ /dev/null @@ -1,56 +0,0 @@ -#!/usr/bin/env python3 -# Copyright Vespa.ai. All rights reserved. - -from bs4 import BeautifulSoup -import sys -import json -import mmh3 - -def extract_links_from_html(html_file): - with open(html_file, 'r') as file: - soup = BeautifulSoup(file, 'html.parser') - - links = [] - pre_tags = soup.find_all('pre', class_='pre-config') - for pre_tag in pre_tags: - a_tags = pre_tag.find_all('a') - for a_tag in a_tags: - link_text = a_tag.get_text() - link = a_tag['href'] - links.append((link_text, link)) - - return links - -# Example usage: -html_file = sys.argv[1] -url_prefix = sys.argv[2] -namespace = sys.argv[3] -part = html_file.split("/")[-1] -part = part.replace(".html","") -prefix = "/".join(html_file.split("/")[:-1]) - -result = extract_links_from_html(html_file) -for link_text, link in result: - if not link_text: - continue - if link_text.find("DEPRECATED") > 0: - continue - if link.startswith("#"): - url = url_prefix + html_file + link - else: - url = url_prefix + prefix + '/' + link - id = id = mmh3.hash(url) - term = { - 'put': 'id:term-reference:term::%i' % id, - 'fields': { - 'term': link_text, - 'namespace': namespace, - 'hash': id, - 'type': "reference:" + part, - 'url': url, - "corpus_count": 2, - "document_count": 2 - } - } - print(json.dumps(term)) - diff --git a/icons/android-chrome-192x192.png b/icons/android-chrome-192x192.png deleted file mode 100644 index 3dd89e5aeb4c858e38b52220957d76ae1647c8e7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1841 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 diff --git a/icons/android-chrome-384x384.png b/icons/android-chrome-384x384.png deleted file mode 100644 index 7094e2e232df03ca6f2049c9710d59accb3a9464..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 25683 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 diff --git a/icons/android-chrome-512x512.png b/icons/android-chrome-512x512.png deleted file mode 100644 index 33c339f6b13b0d40c36a5b4af2c59ffe78aa093d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4050 zcmb_eS5%W*7CwInmnxuGut5e(P+I6MkPt#I2|e@x0-{Kf9!h>IC@84dDN#WXEU4J1 z@rr_?H%L*g4Xi*!kP;XOBy)1#=3(9@D{Jlj?fvbRv(H&MLicc7CcjP|0I<y2iRuLa z2C5h!D+R@qtD8Haz{c3R+X8UANMSKt8e&|elb1UHtgQf`2LV`sCUgXVR005eCIFV_ z09cb)+UQAyiIVAVKK6S0cmpdvVwk;6l(T`kt|3w1wJ0+5)zPrmg<;(V0j;Q+yM^0{ z9brAk!rvALJvnIRVQFYXa61+4emW+s_m~mM!2Rq_CRE6V9_M3?D25&vcQN~p8&iyE zWxJXE$Bn7R^ol*k_9k@B9%g@$iGzvf<pdK)Q_m|2CQfEvRY}Y@MW!xhURRS$UCg~} zl1*JLylYd;+%0{sr<&2Md~T$f)2w{!)6G4JzPB?hysiE2WLo&x_%*OB{Yd`zvn&~8 z{|DJt0k-}RbF6|WjK&;dh#jLbm(j$wX4(g|u=NOd>j+1iNXLM-eR>3~K<<80j7#9- z{iImepeG0P47A9*-1H5%k>lKho*uM~_XvJ=h>}PT?l?@@>#1+F%`U|&_<5dPns><a zJbmNs)C`}H7x~mo-;l0+`)vP^mq!RD+77vl&{szYrrHkd0LOiSp?@7EnCUnk2oB{H z5X^O)4h1{sg&J7sIv)u$u+(!Y2zNOa;d)|+p*6w%bhMGJA+2;btvrr?Dc;1%)YRF` z)YaUpX0Ol9G~fCRb1!QPUy|RwEK7f~rDVoz#-kjoU^`+c)jHgPngQE5LP_?dvV85c z{OofW4rqYm{y@k5K~9H4oDPRLA7#1}FkK2^#c<c-5w69NZYQJMPDQzuL<hAWG$I=W zw(YZybo98ei*|8WKnrSYXB5!PCWcTMO}TVVyj75$RUpN)GSRDguZ6FTg}06GoeVQK z3-7uVb2`!YR=VHaOz&&S9_MyC<%K#O40b>R-Ot33cDmZ^aI#DFrl$J@cOIrB(QOlG zK~E2nce@4tc|a@{N68P)!o5TyTpg+1N^f|j5XhuByW7hQE2yuLLhQK?c>pNPJ5z0a zGT#0An4aZpkLgI-UxNEXjw+>wJ975&TRQbx1h;!L=CbDHh+|R99BdiLDoRMDbYlO# zU$sp?kJh1#`k$-2W*;={A7)bGPi9S=&&-X9ZmsYq#N>u`=aI%|_>%kbfBtl9kLIX* z{QT%>eTX`kyW}dv<f1*(GPMti$Jn-i<4Ti0GX-jx?{)*E)e+#kop#^ba&_d7#Me(x z^Mhz7eq+>3OPAiu>7mQd`dphJ`cf2_-)dg-wP|O$n3|j6;^TkHb7p%A7yj(sS_7UH zMv0RTt>iwO92;~dBf)8zemU>At@Hr?eYe?4kFkVsP2ung7jSCpUAD(QY~uHovpn5G zaI-Nmhj(oi6?j))yJgWrk!3C@3UsR(0C`zj)%wkNnuP9-uA4CLZ&n_9#}cfnzBqxr zJYU!Pxn!D%{#@6$rZg>AhLF&@kn7F+30CX)ge5G@Zy-<D^WUeU9Y}B+Bj=IcZ&UQy z`oH`KZ>VN3<0L)z67)Ad?geMc(yuH;Z~Pnsy3Pz%wle%P)^hJ$Oz<46Gs;$EKE1J~ z_E!H!^*`#T?)&nCQuEo!_2wldK~-aW6Y%@=C@`nmV^SAAoqn5bOH>#d&l>j~tchHd z9~u{q`-j!+ZkFea?vv@C8{JTW*~uCS7o;|JTmctyC-WCfH-4jmSG(1H!>$%I%W=Nn z7V(=>PU<5K#8$tY)A3WA(Nx-X|G~;Di?Ylo`hT}Mg<`vhpCkONlrzdm+y|f9R)_RV z74DDffwqis8Typ55I>X+a)gtMt}AZJbkD7wrL|9j`--3ED|UQP;$&+J@!wJaQCw2m zh5TMTg(%5%znG@|@o+u2a}QA*iQYwg5@Oh@!OJ*}hlj%o6OB;=tARm9GpV6@d}OI2 z6VD^9{Tcz9GDOce$Vd$=Y0sK<v;c^-hB0cL(=>-itGUc_wqe(8aCO`>>|DYJdFH}h zzHsWeH!}8p9{)919v!aGNz_7fg#Bl~Z%W6QbEm^h%~iRNO$EO5&rgFN8@&cgsyE0D z5qPA4FAl&FCSSb<vorhnF#CGazfd`oym#Gi-Y~z$)NhdG#J1=AHQZNPZ?>fqTo@0! zX+v=_etSrb+<R-H%`xY2iL4=dHLsl4%CVuk9E9cqp7IM7E)Ao|88s_X)I=E>h;haI zQUWF)yM{dN8j~w71DcnlRm37Igo;ss2Qo4zmcy;^1G@Sh;@{5T1P8M-b8(E6txDcp z2?9gs=a*z)@Je!5-LFq#9O`6_q-2W^0L{nP`kP;c8dw#%E$FgasoCN~Kyx3q{_}(3 zW-$evAYeuoMal?uzbuC~Jja&Bt)PHMpV*qH6F}k?Lyk*KU)bPc7*8NlMX1fn%<1(O zgJMlIe*%fiHJTG9U{xlG=*hwKNs$IZT~y*!cFl=lOe)9`cYhbMq1yr-|HR&m=64`7 zOOa+1>=oqpL2$ph|D&)7dQH(OQmRmdN2u^7WbdAvAVGgGc#${zTVh;P<iNh<a1mS3 z2Qs@!{GGfBk}A2~1qdQ52UAiqcoSmCcK{PQu|Byy^~6~`dhQT7BMN9_|7@CX7Q2Jx z(}+%vN254@Zi*#74>X@inWwd+iHU=3JeofWR_~tEu-ML<8-z8?(Wfm{VaeH9<w8~R z%Q%4lbT-#5)xA}Mb^`nwr?#rbt>S;EhUlrhstPZmtw`cz0tHe@uJf<s7)kv{;0Js2 zQn5j#!rv7j;u&VA$^@yEvm}R$-i{tV(8E)kOA41n(ZQbN$}rF0BCLv$D))m_Ptibt z|A+*wAj1y3_;;i=V8dH6HEn(ia!nqqT1iN|BDS7gF{5zv9HSjFUDdf`v`P)ar5c$2 zj5+%ETPI#y)?QDTUMCjZwK=WM_BtT};h^GGNx5iR|ERS&LOm%>h`crPTdIIPTLLtX zU^3DgXT}cP(i4qfRh(CF8}zFj+qOTJ<X7SPdspEr8u1dOZG&3-J1dL6`#Zxq8LyGC zwK~>wGqFb`=r-c^$cr^L%G>%LPW@E6xXh5w;<r?6`2eB4bn()c<k3qpTR%yh*y1vx zGwTPf&7&EPbW|26d=)=>;@0`HY&Ir;l{z`?%c>sL38UN4DX)afa%mRls=_I*cY`~r zO_jse^n@J}q8j=BxZ!A$@=KGu5~cz!Yu;ltcDP~fJ6J?ox_CaLbf#YB*e@EmO~G^) zz7>fE9rFq$5CdWVIEgm|r|KdBfk;Eo_#w<OrG&eC#gEnf1n)i$Y1vYp*>qj&VPW(f zoUk0(v8%SKgLtQNOcJJ#y1hyo-Ik?qDj8x*o;j2DOEg_LA&JANS?{WxDLj^-HV9kE z#1dHVWq+;ppV<U<vy?delYWzH<+zvCrMe;8RIrlV|2v$&X$9(Tjit|@y~0;rf%<-w zp=VCo^QC4rL0#iA+(w})>i$uhZqX2&(`+Qmy&R9J8MttKNsfE<rc{mE9s8VzvIwIu z3am<~ieh8xuhoI=dLwl<?*gz)&ozh-@l?5l`vEBtxV{!)ncjba7YnCrc>U@KqeL6M zQ*BmP)Q>=Y6WU|edyj++^jV-i)n4}{+BUT3ZLdU2i3e2|O>2w#<Pb)R2h<m8Xnkv8 zT>})MT}_nKwF6S3M%$e7VA$g<a-FSvTGIJ1jMGn8wU>un7r(a@gg27G>au0bCDquE z6(H`Y6hpYHU_mQH6BJwqZ(2`&lJr`E84=H|l>9<vavX72EL@#9%Z+5Q$u)_#V1PQl zHLjz*w#L-g%=ayv^=fS^1LIz7Uzdispx#OwJ6asJcxkpNX!6R0a<#Bcd>^Cc<9qnd zofrM2>pnbfc~EUFj^K+$cu=68I}|T?h{eqekxB<DfKKLb8?#wFuo@RXJ^@=fnl+I$ zBo@nYFJy%!M#CwGD$*apojdnnAxsJG)3Y0`y;YFc&@WEg#l{fEj=%p1ckt!jcnJ8U z2RG=-E`U<^0p1M7Ktb3XOr89xwf`{;Ij4{idR6@tI2b?w*He-h%X}d|V>LeviH~A4 zudtjkDvhH=|2aHR-2kUnd^nkv4tMd}r+_c;-6$UDSsx6|Cj%Esd*KD4bZmh8cG4oP zg+<2DEnf$<Xdqf;=9kNhK#7MQ{gxwe!7JBwYywsD8nqAp5!b4rIQ+A(YdPxm#8!}q z<@A|4wya0A`Zef02jC)jTk8)Q3pxDPvgFuVJpYv}`T5-?ISx6DT^m{mN}^x+C_|pn zZ0(#Ts9M|GpS&FHF;<36F6IcTSUMQ`J0stgRfyKFwb#p!f|8rFDQrw9uVf$pk}O&4 z=LDQ0Z*0w}KBMI*J#8vp;DW`KEJg2UZN{j)@R(t-F(bCKeR={aG~bwp@56}SkEt^L zJ{XI_F4mtf>bN%iFL3GQzYX}Q&}d|O5Sq2mZ@kCf17ULPJ=Qvmiab2D&TZbRFJLv- zXkn5-4~sKq^*IT2u(*|dB5e2bk-5PBD=H|xzsVLJ=y1(C8#ik?MAmm1sW$W=IJjNE z)yY~Gk(Gw8M1%MIo<}Stj7sgmI{1Jj`aLaHnVY!x<+eRODX>kKuWRhGt1juCrC`PX z`QP(D=^hJ7djCGNvT)hU^AK-Iv1g?02v3Q!jNBUq1u!HS8tEDs=o%4x42>)a@N1w= zAXpLzPc@7d|0`fm!j9PJ%>NzGKq(-@0DqPbBgHE`9hbN_Av!iL3YU_Z7=?>XOo;>_ zqx9>PqHL0%mevbe*GuQ`UO;M-f?lM8>Lyj!Q!AxZaa7d!$quPacYSbO1AU$CFWWb? f%XFbm92Y&nU$Z=AiehpamH^K79@Lu@=KlWx^up7) diff --git a/icons/apple-touch-icon.png b/icons/apple-touch-icon.png deleted file mode 100644 index 1338caea4b2841a26f2c41dc6362dc4f7d84929c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1580 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 diff --git a/icons/browserconfig.xml b/icons/browserconfig.xml deleted file mode 100644 index ace840ed31..0000000000 --- a/icons/browserconfig.xml +++ /dev/null @@ -1,9 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<browserconfig> - <msapplication> - <tile> - <square150x150logo src="/mstile-150x150.png"/> - <TileColor>#2e2f27</TileColor> - </tile> - </msapplication> -</browserconfig> diff --git a/icons/favicon-16x16.png b/icons/favicon-16x16.png deleted file mode 100644 index 90aed23b9f3052f3c429c337dcb244e9dfdbba1a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 835 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbKJOS+@4BLl<6e(pbstU$g(vPY0F z14ES>14Ba#1H&(%P{RubhEf9thF1v;3|2E37{m+a><YADU|?ho@Ck9%(^n@F>FTKg z5iZ5Lx@x*Qs=B(WKsCmWMiv3q=Kj_e{?_JxRt9F;7D2XQTdU*FcEz6Qj6T|G8Ek72 zXcNA@CgEy-!j-;+t9^+#Cd6InwhXnmNpQ7^ceROiac_)@ztE$nr)C*q7q+>|GR$5_ zQ`sigIdnt0sk>RkuG;vEJvMPJA#2ObyezDvo#HR|#$W1nZ;G~0_p;CQwoP(Nygnh} zT7Ss8GE)yTt8j;?L(MVA+hR_%2d^qI^)NRu)i(7sH?-FK|NsAl=%+fskP<Hm@(X57 zy!rQ53R}|K_o+8-|M~a#-<=N`cki*YCZ?VRDq&3Wc6SNm;(gf(<gk}``ns||=av&^ zvYvH)T?tTVtEY=&h{R>z^R7Zph61h^kKI*m+g*F-?%mY?(TaZ;a-Gw7&lkkm;I>*h z-fHhb4mIPS$s3z|Z3C7)I>g6k$;#WR9AqL^&XhOLW7#E*$*D#h(@u+b1$k<QrwG`D zt-ZFSYVnOJ3tex|&EwC1^VPdqfYnZ>_x5~)nddYZ&Ej>!U-RDLvpl#Vq^#Q0QbzJ% zfmQs4uVv4FxD?p-m4B%Typwl<>!0Ek`-%ezKmX6U&hEDQ|90km52Q<9C{HMfe`E>t zfNF_rL`h0wNvc(HQ7VvPFfuSQ)-^QLH8uz_GPW`>vNE;QHZZU<FbKRPaS%mAZhlH; zS|x4`3jGUeff~ZALLy3n63Z0|it^Jkb5a#bDhpB-G7CzQ85k<&JpRPPQ5dG7amxSn z8PBId49v>hddb|v%EI20MVN&ZTpCOcr!XsT4pBIL<I0I6XO74mVL#pAvA|1@;gz^x V$tNe%sX!|jJYD@<);T3K0RXU-9UTAw diff --git a/icons/favicon.ico b/icons/favicon.ico deleted file mode 100644 index f1838731dce3b206f29963e1acd7584018cb7d84..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15086 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 F{{dILDBS=6 diff --git a/icons/manifest.json b/icons/manifest.json deleted file mode 100644 index 5b2f361846..0000000000 --- a/icons/manifest.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "name": "", - "short_name": "", - "icons": [ - { - "src": "/icons/android-chrome-192x192.png", - "sizes": "192x192", - "type": "image/png" - }, - { - "src": "/icons/android-chrome-512x512.png", - "sizes": "512x512", - "type": "image/png" - } - ], - "theme_color": "#ffffff", - "background_color": "#ffffff", - "display": "standalone" -} diff --git a/icons/mstile-144x144.png b/icons/mstile-144x144.png deleted file mode 100644 index 9eacb6051d82f28dbad596b126595635eee7a7ce..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5313 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 diff --git a/icons/mstile-150x150.png b/icons/mstile-150x150.png deleted file mode 100644 index c69f8ffb31b37498097a850ca2761b36f8faa829..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1744 zcmeAS@N?(olHy`uVBq!ia0y~yVB`Z~4rZW;e?I4QAjOjI=<CS9u(6-}Pa-RjuaN8! z<jcTNrN+R}(89p*3n<j^f`OsbfPvvv0t1893<d`Af;qbaZGZ;t3GfMV1u96qK4HkA zr0WwPh=wk|ffOv)kl2GGxc>kDzs*rA6c~S#N`m}?8RVNLO`M#`qOffjhtiIn%DYwf zX&lfxtaD87guyA}v!>_GFIru(xn_UE>6XhK_j_Isd>;lpW(<3-yZYQO3vM1xvo{5= zEidO;U$wQn?r?J>oAYhAyN(|By&w7cUkZG}81(c4W5_d;uSRDKPwF4nUSCt!5Fxi; zZLf;r_AQ$?$!?I5UMux)jpRy+72?apmi}EVI*U`vCHEiDb&N^g?k<xbGT+|@<gk}` z`ns||=av&^W^_H^*3Q7dEa2(l7*cWT?Tza}A&w$$i3Ua;Dyb5eCb&(KIKa}u$m*$- zbk36fjHPv2$d>sxoVa^J;xE*e<!9IO7i9eleV%mg+zFt;I6*^Z%DGv#$9s2ve&*dj zbK{yBg=*D}UuMlb75QRa+sUi*l=bf?@_x1C;)=a?VP5jZq~)elo@;dNUc1oRz3@;W z?^R1Luh{Ds<}6-wr;_)mrPic(*)PrpMm}BGc1z}}sa;ma*}&PKf}At^R7>l!UYrTM z|6pa?DVbj1eOVc2Hg35Y&D&}@P4|1ab9|quw(Yfm>5G?MP3O(E3|spx$~nBxH2eC` zwF|Y~qpOwPxgD?iYiSj2mQ#{^`I+8VORM1MIku}SR)3SZYnwKEbNklJ^M)UJz56`( z*0#w6|Jq-Faq{E#Mn~s9&B@<+i+pa_rJOK)`h4s0`aapim$Gxq`evLcGBNjBzxC(W zZ%?g{dwei0duulFX~r$(z1N<7_g-*c#`!By-M4qfKtYKKaoL}r$6iVAJ6QK}tJ&{p z-xSMVRZFkm+9G2x@u|mEpfQ#|zE8ao-8U}}DBu3C(D?h*#|6v3M9o%q2dd3lwe)(F zjCI{1muZhvmVSxp4qkp9Xn)Ausq?b`Zd|LjNk&%mQuf+c=Ut~MSH@N4cv$MiOt~i& z)c48g<x0h;J4zy}GCeH4ZZBzXoATIe<Ck+2id5Elo#)YT4`1X_mFi&W<@WW;3^VsP zw@S_P?(9+Bd(n|E{ZZFF-v5?)_b0|l1oa(adf5mxsM^0O-ox_K=Oxcurzl&ySH*f* z7QOfG=hi<n)Hg@o7iqnuof_5Bt1KReV~p<mLRG-(~ved%Wk;JbpR-(q^YqhQDH} zLOm?w_Dqoz4e0x_`ue5WOs6L1U5`0j+V^Gl_DkJBUfuV&ssIPeOY+B-d}o=W{5P<g zce<rrt=#lD<K(F)ciEY<?SHX;qM?o5vs2+Ob#bOlY!W~CYnhh>TU=XyS@$VWUbVzE zq9i4;B-JXpC>2OC7#SED>lzyB8XJTd8Cw|uk)gJMft7*5QF-G(C>nC}Q!>*kackJ( z)a?M&5MC7$Q4*9`u24{vpO%@Es!&o{kgAYbP?F5RP%-E6CmxQ%Fb$1U{-@7)J`G}E zR_4}A<`z~K_MR-lEUe(tU~)KxS$T7a!s#1VP8>ONMCJ(l=?0GlUV03##05(}Ihjrc PTEXDy>gTe~DWM4fTLB_* diff --git a/icons/mstile-310x150.png b/icons/mstile-310x150.png deleted file mode 100644 index 0542b761ba4e7a0162fbb4da0e327eb45f2f92b6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5909 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 diff --git a/icons/mstile-310x310.png b/icons/mstile-310x310.png deleted file mode 100644 index 5fc7990af486df84cb2e21b7245676294bfcb199..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12065 zcmeHtXH=8h(=Wn7&k>b_AXq30NGCJ_sj(1xk%S^75S1n+^dcq1a_H^Qdy5q5N(lie z9;Jws5IUh9YUq((LX!LVzxS@Y?uYy3UF&{(!^(Q#Vb9Fovwt(QXZA!rG1j?!@#aM) zCZ@}Jx>`^sCT7sD@9*b<HyQfx=77g<P7jS9GBJHhU^{!x3jBTBP8Vv##1tgL#Ps?v zCZ<#1)$3IzCO=swruAn`OsW}7Ogx@B&8F(WKhD1}(9vRI{Q4<qElmdAT=3O1f?Qa> z#Km<^vBPvPjfqM6tDe?Fvsa|8nZO)-2eb^GQnsCk4mbL<%k-bz3T%{ttf`i1#&aeP zE$rLhzhkxEoqs<Y8DD_yTAp2%`Cah01D9$9&x6}8vEFrfVUQTY^Bk)qi{rD5s?yTW z>nAihxO8cG#W>8I7D~l_E;Rv@)yeh4v|K{qK%*cqq5uB;mo)HtAQ1$nd%1!G_mwHF z`ya^HG<XW?Qlj&i9yIta@4{Xb?8a;@|BxJW<2~8&kkMF=fe`*!<+UPmGDSw}=Bv#U z6H{dpq-;4jyyhN1{^U(NjEsCL+qnNP){556dzOcx2NL&wi@xi1?qYg1B2+)+N8^@+ z^*V~ba+C3;YYR-9iLec_BKolYXGn6jrWZww9iv)%zbX{&w*W;Er`uA4+50qHdNA#4 z$`FD;M(9<Zf<M>xdea~d$A>N0;?%fz%XeLIZf%Rz1_`I0L^6{hIEqcSYA8KTI=XOZ zPP?P2s;%1UfAM0|XVKveAqIpZ_XE!jKVD!q%-(OZL-IeKM9JwBhpK-u+X%-oR<;vb zHosyCmoxaU@;v)dT$3lStmJ{l6uok)KVdKNznVT%8`(-en3Hpwf19p8|8|2Sa~krd zOvvk8iP`CymrSG)+#`;Q5?ywhnJlQ}c0*;q&@Z9#jHZ(i1SgU&8S(*hygM|iGZ1wT z11ZAG3Z8bUh8jj0#7Q0I%>{JN`bg?Xwf(%X5#4s>%Tf}aDH&uFIKB5?p5dehOOxY~ z=2Q=In~?WjCF#6ao)HK=S%V<io_XsbUh5@W@J<RCtT4>&z4Wv$8QLQkO%R9Q1z*gu zvVciW`MbUBuX@>)c+lwAQON*bL7S$t8#@yx;CLy`tDI8SA}C{yPoaB1=Orl#7ch)Z z?~SE{G{1BJoc}1(x6=HRqfXSdJvk9?)>|8iUOAY9f+zv|!<GoOK(+HFsU57>8~53J z^R5FE=p9IL7>Z64&vcD`^|JSSjLy)z4Gd;2(I6&|T~-rNYacXPs@gWxxainkt1my= z&d`4WT9xAPiSU-5oiC5jyaV5Pmsr+f@ns2$XR=GGwhi($JFSk6OgCKiDyj9HbeP?2 zi=*~!fY){?r`ty*`wzYpnc|sH2KnIa1CBKXj!&ND35tAS23lIkv|=>R0i#CrGMW<W z1~Dr+d;R4vF~n=r;)(<fStx=-TrIRYUd1M?7sN*1ubOi8Uz_M|B?mK9P#0Lc`W6rc z#lOq}n>3@D3o+nOLP>&Mwz}p87>7ge+n(+tUats)9#rp#y=RY1eA6q~sR!Z3G;^lA z#H<`v<$y8&E=M4abJf@~nFdojSYQ5Kt-5V4r|t<ig%}W(uB4?`2EST8{0u@*>O|+U zJuz5d-c0*t3Lh?E?g<V^2Z10Wpi%DrEqK$16uuLD41Kv8OJ>$Zyv`jm?@fsM@v{cY z5}jsT>-Di^iH;I#Z=wpxd@fpTV!FzcvGacnL527b%&B_UD{8{av{ovnm3%a(;S|bX zV-KY^j&o^<W|RqoWZxAN1Q=hhOVIB57fJEK>V0!^Emsn&giI!W{v2#Q3f*+@kO|V4 zg`GTPRd{l`F79KuH+G~G9iSaSXDNYQO_z%KE;w}(s<WHqz!BWP_1SK|vQ3CRgdD&x z78Ea=?7PG+^=!R}Ag!jZbeXOWZIL{?^in(~7t&ju5O7U~<CF8pkk!+F5>qQQz53wM zd7^#7z#{GpEfa1;(%;cV1Y4B_Powh)FIDe&^%%j|HIY9Ax%)R5L@bKdz1g_15(hyl zulms&SQQ?ePX7@BnR^3{(xoJT#H>{xZeSe<)JVWIdn0Nnx7sjUq6vX0;$FC|sMj0* zwEtr_URr$;L7H}^_6teK4@*39rsmTrwNk(aP)+A%HxjHvW=$Ho(U%K(^biF)#T99^ z##ftX0@~aHQ6Nd{i|M27gqzA^*Q7tU*$?A(=cmRfdtX@cp9Z-ROV73HE--6|R6R9~ zzRNfm`#z*2^unLGccTV-HQi!Ozs?U7Xs2)0@wEx9EO3m6h&tA^;f}lC7wCu~3L=UK zd>Gvn3C5osI$Rp*Q4(0sG8tV#>uYjb*Ocp=QkA4L_(UlI{{*zb_Z7oz|Fz)Qf1M&| zU?~k&)=x!hvvWy@O$Jg$%VW~M2TG~l<XKaabPtah{;Gp3qNx9jDn^?05D*z+QEa|V zz9%~gG4vni9A0KbpG?EyT)g2bH?f+ae1lW1yDu}fDqnH1@ywKGdVdjHBjYPM3ikc< zzr)4qCv5SbRrDK;)HdRPvehg(J7Lm%)lI;ddihE!2EUDwqP-xNvd!o(Fx!ZsxBt@7 zv1n7TeFXsoNepM=>xW-f&l6AyWybc0ZrK`tvR}EB5<f{sDKlDDT`$jQ0aPM@%SsVT z^`I<c9aI%j8~N0>^4pUtj@`!VN$HN13VIbDeSZow_2hIdoYAydi>*25%LyziYX7MB zD9UOlAhFXKLU2$uv|Jdn@E(!~bPhI*5luL+FG<xePTxLApF2en$N5xqJ6M|^G@s(_ z?EI&dV9{x7L+)@!S7{Up58>+XT2T+Ymz3rv0Geys8SW0nuevhl8ZR*ON!ieATd|nK zZo!3VELnc0MAgf=v<jn@-P4nC+A?xi*>CFn`4`KZTWFm=H2HG7%xN%FE`SO?3H0_v zx(=ff*?CFIx&(3yMceb>SQZvFog?49<xpif+P2Y^qVu9>@WHY&9E?5-gCGT0wXX84 z%C7A$k|CJpdxEWsjz8y!WrhfmiYmqj6Ml$|w?ep_mDZ)cXtfuI*mSA;WXU5Q8tt|5 z9j`Z6lB%ugr9|WjL@Nm1_b=8#!o2<Vi*Ml{NE4M#2K6_y!6kAwQKm6zQZZueP7z&W zagvQR+Ae_nnF84HKfm+cwm~oD=<Zo+MY9QEYgSKu-<IhkL~7~wEgEnUQlN6L;Hkl6 zRo(D+j>#$a*{AzQ=c9$a-n=cAOb8A&TVeb%elBU4%cBhw!5XG0ToCS_iPz2cdVgGS zR|-ts6<$Vpvz5+=2_lYj{D-ipH(%jT^o2H|TD-8}`RF{8(#+_NVp_f3dJrzQb<ce< zqm6!Sig7sXt!qSlZd#>q8Y)MzaozqE;fR=j69i)t{=zy~$6AYZ2p_mPA1;}G%OKfp zXV}kDE9coHmhXoMhx7BEY<iV)qivoy>mi=qEWW05*qwBOxn>n|ThNh5Bi~plGphI1 zc)r$fk$vn2vP;9Uen}Kk#?@#~?blQdK4=&|f|8!xj$$)R{!biC$Ys!S#bW@VHdtez zjcG-vu0EV8AM_2hTOlb5B3<$#+BcFRMME)*g<BiLP6;;F`wro21)guj)1dZi<;!I4 zCSO;rmXE6^efJh!HUt&{>>Y#+<jFO#ni>z<v{&WIjoQI*w}&dThnGdCQ!FZ1Wlpoc znR!?IUh<?lXzd@<wa*fQw$Ob<bHDwxgiG@ie@pb>SXH)$U029wr}6ymy{8A)xi5^D zyTw|0z3@Lgm;(U$JM7^CGwfNw59k`zLCiXO_~V3P1!<)G4#L=bNY?QTMT3igFFPAs z_(fFc3Bumji-jA^8Kmd`>`Xa}Ej`jjJWKjdV3jB)K#CTl^9+=;UoH%(iaRTBT}K;^ zU_#Xs%oFaO^r(dH#r!=6nfpGpnR5$cy??|h663-vV%qF|B5vB_ZduPIIYld(<cW2X z)}v>HFJMpG{XLF4UfOk=)@x}MZJL^HGYGyXe;}n!gPD^d3(U#4(etY;FJApy$FcJR zOCFLGNy7X3!x?8N;y~MacPvD_Dz?(s%zs-s6t;iFE%IoAnKR{oMdM%~L_GRto${#? z9M$zg(7Lq-BtUZCK-ze3-WVRs$}A6zx4H|zy6mgdyl|Yrbte7xk3{^J`~UN7h@ypP zwUn$j?PGUDKsIw@nDKF=C2rzN{k7@|r?I*^%itcS=7bK`F4x60mPf=P(~_zxO#g(1 zo%c}Uv~bC!YPJ-nPs_Ykv}>h5L2_G-b+Od*T+r929vpH08znom>oGCqM~a<BH1*O` zZjSxyVQe;JJ&#srQ=(4+OG8tdz2#k^d*R?yL~MH%!*jpk`jqO4X~UN*7kQco#?bnu z;1z7Pg>)b}AP=#RSiJ-zje$MfMl<+0Cr(6^6DQ{zm(h!{MxwZ<9aC6&)5b6E03kH` zDI!zBo1Ansdq&5SAAQ=rm^5=su**g)R#T)xBQ$wm1j~ga`9Ez58m?FzU#3m9Kt}LN z7bYIgKT?)7udRQv15u{6cS^dyG9xAz-d5>^(5teq!wLrvPm^2Z)WB_E>DW5uc0o<~ zW9&rUslSObg`1eSmx*O<8WiFJ?Z16om)MpXGZ!#i1~K_-Z4@Bm?u1mx#pz5PJg?(& zB2Xz;z~aWUuUyXTm*iAFI~#iKdN}F93-Z2(<^5fo!6#1NDTr}z7XM`xL7Ili7VV=t zpCKu6nkCK;Q;y~a7G4_kMH}GSgl9iFIIAbEEEzWiGT&p>fERR&Ay&n$I%+$Y$#aZ} z823<HqR(>|qn-_h7HWv-?)AyFuSWYyEKE7~=D0g1s3Nm*X9@P#2i9G2#43auuXVb! za5Q_Ii52}ia=jHFS<>0L&DzpGmUNHnN`G{W2J3fSi|F3)JOL*+4VCSR&C#Ik_f?r? zS)zlw^N&nOJ5-6_-h~+V7Rul4C!|H-z?|&H+X<mjTN;EH11p2ft+|{$R8%xSZ=IML zzfZq)!iJd<eCrR$_L@v89Gae!4xIzH7O!T?s&tLkIn+<-zpu9G5%5|3IlM5MjpS^M z=e>WJJh2zijDjGauex32aePbHO(m$Wpp|Jq*CgAT3s!q%Lz@!r>)3e_|Hdtuvoz@$ z4hYV!%aBgC=QD2cTmmH=U1?Ltl~~!SMJVxCoX>*cW$lW%BESgSkN!K++3}ztP%P~6 zr$i*8eso&en|lfOiN2&Gl#l>xY!Pw45&HGM$sLC2Cg~B|OYQluh6>;xJ6rGQD<w5N z8XR3E2hD$5BuFFxfpb(fIalBk1Q`;t8hIRp676XIaTb+tNGlzY9^?*VQ-E4TTY}nF zOD?cK^_qDOA%qb}d3Y3W32IX&huliv$4;ikPn%E53{0GocT%?q@grT#(`n?P1yi?Y zzQDR`bq><0n6@Qsw&s(4t?LjQFXEZMNo;70SdEiGvK)ui+2Wy7w=*2c*<P`!97TTb z#1@<WHS7jR(p+$sd=Rf{In@{m7{*IY$D@H;@wjiKr(7N+e`v@HDu<kW6j;K?!{Hh_ zu`osq@hswToj2o0C&p*dH9GHiKWmzW$D-o9tT^X`$o=+|eBPR=Q_`{XiJaWzzT2B9 zQ~VcW@u5w#Xc+-tVjTEqkWTJ%!1Hz=L7CunRy%JB?G`kq;^T~*54rN5&IA{rF7kY+ zUF3luE_5z~1E2K-x6wo)1Sb*KmKg<oMzR|rgVd^(EL?&k^wvmR=v;#%Zz;#AqizoL zN()|>31eH*r>0PQ!Ro8nY>;G-h=Q#<XVX|?q;)IZ1YHP=q933A%<njPT-s4yZ)vF5 z82Ri47idrmw;0fO5+l>R)7FiAMY{tCT*<`dNt-ifV3W!Nvxn<=@8h31Tc?*-O)g&- z4P%RyfXk~lGL&QHs8x;J8p)YOld36lKsfe}5rc5g@di5PG|&`xHhjO_%TI@E$#Pue zd1->NFK&m25F11158_Mol5zB{hE<Q*G0fw>n1IXqe+t~vlsG^9z>CZp8}pU5t7yM6 zLWjFjW3`o0U%#5!_ghska6YJFB=jaED1_VcFl<5F1NqIYBHHuJb}54W31V|+DZzxc z7U+)UOOjzh730Z%H-lGG9>l96H&*&b+rXbs#K3;frJy9*{L1d8U0&ea4KL~;Ehf+0 z#AUVjYTSkm$$10gq$)cw9cqn$)8GHm4$!CrtNmxY*1_rA-jhIoUi4hg&Zmfc<o-y| z(5tCa9jz3h7>43*%d5RZ*kW6~39*#f@M;)ay=wp|seD%3OmG3lPBY~VrMTM7d~d-z zO$K)n#~Ac_%NF!f3cls+s~$S{PQfd>_Ty~m>;>m;Di;T4qD_{5oHyPzt^{scEj#>c z2Y1k3ygPC~__!Y5Ab(P$@Ee!OJoDCf132{Hkk12G3T+AOH&`e#@9y?z3lNp6sbUOB zk0P&eaKK;qhrG16YQfkCy2Xo!0Fs`B)_(l}4JAR_QAee)GwK-2;^17nCLLXYIY>a# zrLir3GVLGKl?Hf2(IJiAFNw+LOESK)YQrom?-{`TTZ}$~8=IyDM5_?o9#+&LETKTs z!H7|T&MkD6kcnjLE$w*s>Eo(6%kYDFX6d*j+&wPYhDB&J{xI?JLP98mbo}in7Hd*M ztzVt=HreFy0JV!$i?`pFfDkw{lmL|6Q7n=!tA0>6g(D{@PFY;(kK&o8mE&K0Q;60q zu1*@SxGNZQOERJ}TiZBh&UkO*(z#9;{(-`%m;U2c`5+RM%E}=mzTh#YDPtQ%khC)> zZeOk2conDtSL$ea<+0pxY__~C$5nl&PYVzxs2EaT8dNr!mQ^gR1~#{3qIqx3?u^W} zDY;KTr#F^;<I3S$jI=yn52^M$p*eX^_A6gAiz99m#GDPb-cFNHfzG|v=<S=Isu0l% z6czaa1lsIX>-&}3$cemzZim$Ljtdf^9YS)AtK#Qa_c%GdM;Z?aCUEQ_$NQXRnX+nz z!PcNYg7Guy70Cv}-+q%+vkKe3Z>4o6!pB9noc(i77lYXMeSM|S^G%SEHI+g~*pi^I zFH5lo=0fXvrMB&@VB4Va-Q&S`1nJX0wJzNh9Bl^2GvoL0EU;2Pm`8)J;N8=i^M2Z{ zTS#h{(M=*`gzLcp)zELTH#O2^8uKz_G4b8v%47Ff8uffk+oiCkV0+3O2=4P^rGME9 zV+NE&286ucxS(){H7C9<yhvErk_L1}*1ns(&7Z?*!Z_&J35&?L-cJ>KbSl6OKDr=d zebW4nOZtJrRh|jdW);m9x2(yZo5gj1bjoG?`=xXt`feK;05<>Cam_<Dxr!0$)+m2j zU+A;Rb=WrE$-YJ?CXa1!QW*5H_dc9DPI)#`U#!PSKSj^(eh=L#QZtFe%mh6lS~}{Z zefE+WTQ`o{K7|mz+sXie>H!%*f|g-@17@TCZu^uwnlywbaNUZiyFVWw6Q^9Iid|*9 zw-yd%l)%Q@7N$I`0`~JQ1cN@SvWUceWqq-0O&wRxuAoKx52bZ=mq?afMTY*A+u29v z^9FcO;>OiZI|tum0#{p|YEBzb2JwG@BVNn>WML6$YQXg49Gwkp80jCtTE>iw)ORd9 z(`sqm-QyY+tCC9Y;toO-+oJ`FG;*)J^hz<k^qPi10rhE4Z>+L8prQogM0>I0R6O6( zX5Hbjka%b3k%n~j+(%*ekgowRo;%}Ld&hB#%O^)58^85+ztkq3U-jK!Jw(Q23BCan z`Ucj}IO5q6u2buZ+nOfJC=Dct&iX?UjXVEhYo5WYr(aD-6X1beZvoN>9)R>SKuRnr zZNMbg-(BWw56VA4KFz*-`AX@1S^y!Tn5rq)7V`5*bysGu>z*r3ay;j3Sgnx~>`U}H z7xb<pEYmd$PNm!!5;X0Rd<{)r?2doCR^mtPN_JOMap&8t99L_0_~``>T+wjyfYvi5 z>&<_SHP>=wd;pux9EY1MbE(8p4L`ZVxn-JZ#XHrC1j*CdU(n5^eHp@pZqd5IPQv;Z zcmN$WiXk#GFS>6bF<=QrwI{>%cg?qaBJ|5UoGdg!d4AnHI}ZYl@1!48&!a>4_Jar7 z_VRI>I}RK~%kRRk^GZCQpI?t1qaSq7&@x-<?>2tvnIlt_E_c8%pVT$m>Egpdrr@2Q zd+os;<>R2)97l~{RrX7LQn~m%_7aPThF_)%9id>&0cW}G_#tPZsy$dc#DVsY*SBQ) z-IX-$l1*>Tr+JO*8Kb52dHQZky2<22CjTV7>^EOWSH?_!&&HUV3n{5?TLh|Abes@D z?);~bUjRH2@^1>FzXlchqn*Bk_E`Pgu1+D8ECJUx!k|8}+-VXxE)8cU`mm`#Xpi8$ z_#}F{QGmZj?dKc(!VW`DeB4Xyz~gMMLG*0gB>YN{o|b)bE$p?DE+RukwYdQ^a_#;o z)h+FV7U1J^f7?34CqYBJ4(K~6NPeCGcIli!hqIGI7Qi@KJrK0y!gsbeqq(1Di6b)P zym>lGa!_Sfq}n|gY>3qEWvm$SK@aXNY91VWs<EkF8r+z&klB7;LbLhshFyE8_^m+l zsSslgKU&J$tZ@(sM3+C^ugNL+I=eE?&ify9dtB$e>})c6=-tFi`Aq#A?@voQJ{)cn zpdMU~3p&}pVe30S)KD|~@soP++Fxz#Oq{}A&$Yj!4##}|FqpOL{Txo_xuvE#ER5<w zDOt7>PG#I?+^Q#4mch$hpNG><RcJ1cG3slj-5uk~>eb5)pS_3c7?k`fb<|wk@+2Yi z-^O##z6yi(+xTB@Ar*=`k8K2&a-D5WGOMN}ce19g#4KOn=S8B27*!{4K7Nv?{=+$* zvtNf~rv%5QN<Aq38$Y<`Er8*dMnw&{ZO!2cIg?68@S7&8TW52PW1z@)m}d1*CA^20 zRtV<j4mh+6?c{NC@UyMku=D7*XDxtWTv`<kUM$_Ps8-g1`G#vouT+25yDW4QH`lJ% znNQzj-N||9ICmH^^pL>D_;^nBZvn&okF)Mu`N774CMu(G9upRd9}v|Yx$oHWIYO0~ z_<pd923bCqzuMH<72jw?;fu?|Epgums^<VuAKxFcASGqxz-BdlhSNk$<RRNv&Z&lf z4dYi#wx*JEVEvl62xX%%3#ME{AUny50zA)ArrO7y9BkVYXe(2$(flJy=08<5pDWX6 zInYJB;UC-u!$2~k(!}-xHh^2V7d$!&Qgb$l(Q=5bq8;&yf^U?~l3!NQvJroaP%FWr zuLp`f3mds+N$u|Ne$8yD$bR5}vUN_|1n$kA6I^ncoDyL8`U)_nwBFj6w7)~n39>)? zkA@VHPZcyr+j>mQx+Y2O%PQuj(nbS31<64A#-~9dwFk-%g}O4jm`f)$A8#@H-V0;X z5IWi+6Nl1T+}`WJspODauWT&qs^>+Xpm$?*mEo77C_Q2&H}fhG<%1r%oW2>OH*L7z zY^ywhL78&3<{uroX5vOd9|25NmUFH?=!W74m+KE}j1AV0GCyx?lb{)>G7nk(RG@f$ z!B+vu(@~D~hY4ps<yL>I{ZLWdEY%}nUuSwx2968dDBe{`?DQo1ym2q`EDZKDb!CwB z;u{(;74>&rj#O60=yA?5wGQhJ<QY$gK0X!U(;+?xfwejAYgl#a=i9g*)ZbWO2@wxY zdUOvoOK}Yt8x<i`rp4rqIqTHi;28{1!xhakBR;qi#x|qp%2;P1=Q)2<S>~+^&38#( zOV9P8g%R`f_^cdke$Ao4vrw|3arK@HhLb!ZWKC_H5FHj#BcCF>0>{Cswfk|vr9-aq z{39L-N=SbGhuEN7fNNeM7x{}Dra|tiF`APFCG)Ra-INN_94zAk`PSr!+GmwW%l*KP zH=|3!cP>Xn=Q(fX-*nV_Q@iJZ2RS;$yqBSUSZ#VB3MjF7m;ai3DsJ->KZ?pYn^JC_ zA|XjbWwi?JO?T9ent8O!cB{Yp>%=|)w$?(Wr|W~W!4H9a<i8{Fa|hLOnb+^4dZazG zHWes2H<|O5hSG2VbZMr`L(a8()r_aHDS6k6f3yPHQw{s2J!I@iv4~3N*QeIhQjt#W z#U0m})E08Ib&2%wFBD2A3>MotV0I3I0cuRhzGZb}pWQH^AgY-^dQdp?gXARuoKW{q zPcXMi?qse4D5)BV8b|^(8klGvANj#eDhTvM7}))?)iJseXKG7*vFaZ`D%hg`W23&< z&6Znq>Fv4WRWWn-Lq5BbUnq+9*aExA58YQ)Eos0Ee05NT;}$MV0TTc#)!vsB3~Uv0 z<OMdetOxaH6|JJi$cB`xP#TW%joUoisE&s^c-g1IfisXHkY(9a_Q<T{Vq7FMUs@Z~ zC*$r}{h6JP8@YSSXxkg|m;?*{cg3Mg3r;nFH$(krFK^WD)nNQnBrvY?AcbH08GU;F z7nPn_78hIV45_`d`Bt;O|Lg&Vk0ulWG2;hTCp!vkaoJeo!}8?B6y?g?`nxiwYoEsG z?pz1)n{mps++-x?S&9Tw0vMVK3@t8kHmJAVw{Cwo=4`OHqcKcE_7A~or6d4L$meGf z64m-^Tn#G<xD8{Y;WQfo@?3ZsUt)S}y{^dam63a>p3j$)>uxK_pn3xJ%W1&zV?=%^ zEmI_TvC%zpzL2GLmZKG@odon6z`a9v##3Z3ck0I23(J`H058~bM5ui?kh-pz7<iEN zAud~aJEZkJn@|!U(Fwp4)Gtck<x`bo@F*_a@3tRuS{|jR(rZuBh1VZCWjT;-7*QWO z?j)MipX?)^$pZwoeqsNt0sxfeUibJ`uP;)6SbEFBqiMSh*b4S2=xIuK$(lb<LK}?d zksKm{U83t&&cP~tUWoJzeBS0WjpotjPT<nu`IUChh%2Ld7{zn)sGg=csL$_Jv^BZO zrg56irkdx2X|-1YfNAak<tt`c{@fW7*yF3phI{!1a<R0ct0#2(V~dZ=Ny@MmNL51! zs6J1{<gvu2cZ5~L%1&USxI>RroLRqG&Fk_>r(kD;ML|1fw!zQ9X5`WfkBu#Uj5Qj= zvxFs#qFJkGndeg@k9~>BuiewHX`H*BZx*zVX`j#1Z`y6vO`TzYXK4nSqNp+u0$OJ; zJSuwMeB;T@!%UZp>1SvQzV_$&qbBmBlsfmRj3X=M#w;HfR6YxL_jFiH$q99GV~8S| znA>!iWal9sBr>=q3?Qe{n-O7hA6?K+iInP9(yC81yk`XWM^E3kb;XE?IdE8BI_g(5 zBr+#IUU<Z=Tg)<x{-&6^``uYB)HOlqMvsvQNe^D`@oh;|=!EK)^qV;b)mwjel*>Fh z6{n?(5FutsN#@qAFBB_tYjCXIy~-2xz#{NpA<-Ou7Ie2m@x)Mu|42*O<lymx9Uvw9 zruqIv)2GXI3v7p&0wV&Ov8*Eb9bvPz3sj#&f$5(IX8cxZE@Sapu`;N#(6hix&IU$x zO`j+V0W<wCoegZ8&3w)d0jW!LT`&4NJ3qA_E6Uue|Ercq(NNCGxXg}jN|NxIYPwvv zXyZqMX4jhWJh+U8WpUj)K*AZmP#37%2*rm`(uE$&)7kKGN&o6FT^-l9n{8QhKh%cE z`Np=yv(QOe^C&!U>WH5XPEAa7IB+(o`1)+bzQ_?AtjP~2L4yfpO8=C#DUQ6DF#=A! zl;bTJK6WxZrMR)G>D38OOy2K(zX4Hg_6<+yUSHvlLxOgBUv%Udqy|+iU6q^|4kS#U z659zmiwV2B0q&$E<wiLuzFsB<uTqghusSJRv;@+J3~??{-|rV{j=N^zI5T(t8#gpt zmQfkgWAsHmcvfW<U~gI1ZW$d}1)S*_!UefNC{l)CFgeR0&Bk<J>sg5@UiLk&=luA< zEfCP@QG8FfZAvy+bNu*&x7J?$UGR+?Fw)S9rfa+ZCKu?v9)mZPUT1+>&7el-{n4kz zAK|DS`0t`9QnT?M#;dHx*Oi9r8l#&}xy+eqbWP(~s%XJu&x&3ht+z4MwTaA~i9l6y zD`0+n7REmts;8gYmR0W%UVrGJy^ypKVTBC(R(`D)1N$|vWME_!<G3e%U?V$N7s>d- zXH6aM!0GO6j|RAhCSqAt-*vDGE|~HFdB(S?2#|iNH!_qo^x~oW!J}D_%hp_MheNur zD>vGyve|yEigS)f)=C)kC&z|n=<bEn3yvD^YcS8uQJ*;c#I^T;Xpl%GwO7PxaSLdy z(sjTlMCM$b1!liPT7@fEMgE@eL;YnpAcKv8Q<W9Iai&kbuh~Bxl}w{ou0}DP2!o!G z3r%u%<J(mBu1Fpbth4iROB%2dbySaxl8a75I<Bb8YbdP;3N&@7dOTLw0k|72FiUSz z4;i$c`K>9?2Q#QrTYYi$B;o-7_Lq!d;Oc&1TR&y?<4RI7!y3rniNNi4ScInEy4rb1 zLeO(0hVhLfF&|fC;cNg{YI(P1>g_mz<zneFfAz(r+DJ+5^VBP_`UgjU;~&7G&Xh*X zK~^FPZc<TXHsw~WCqJ|XocA*CMD=P|S*ZAoc6OuNfu-01MFO0C>g9aZgMB*dUcx3N z|KR(!iE^-6lVh+Qw$>?l07uFaDUwc>G??URWWNlC5t(Q77MSfCUO*VO>>A4z-yXlK zaXTbE+A5Zgqam$}r4JH}$lUtL1reO-4T+si6*8KK;ea{D<^#1sU|zIE$#5MUx=CJM zz2+9M^x^kmy$zG!E0wF7_Z>C>BaxN6$deIy&5~eMANubGz`hrHQ{#rS8merAA#dYs zphdU_VlPT*b?(2Aj&21Ok^Fd`Ir&))Tib4VVj&7C#?DUIN*km1?;Fo2x$}x0<@yjJ z(yIZ__hktv>IzR%-+FA?+?Wj68^l#bG#bLa^M1bxC&@Rkc?TDG1`0zC$6Y~I{tzPP zIT2vaB9m>HlT@Gc7aBuK5d4{=J%Aq9cE2u9Ss`1zMx*U40SEWY1t>3Hlv1VsKo4)e zxfOv!X{f)bbT*(@6=%^53qjHdrIPQnO~BUX$9z{8x}sq?&kTE6(|$aJ^teUZE&QR| z1Qt{+Z@?(nJjI^{m(}ctrvhNB1{Mc1VNuO^ln}B~pODJkr}AnnCotgU7I^KvQ0HgM zBihpM5Vk&EphrW5ZFV>8$bAmy2XU}3lEEGW3h>oi*j?0}yiozhZ;nGue#@}^H87je zK92F6&7q49WT{c&BL_*CkZ>Ubfv0Hl3K&-Zr4P6zW|lgOkqPmhN;AS(iKiQIbOJV8 z@{Thd3F){|A)|6g2DpjEcd%akxTa8p>E2|ef_=tvR9s!yl2i8FyGo^n5<7K{)~3!7 z5VqUErL%O%9*?F9K5&sWRbNF?6&1CL?^tFO>#N%JN2D<<0agDp?~AxK3X&1e%C^GT z`I4h@r1Mw319{Df!%_=*5*W>0kk!Gesx8rv9#$N`gbH2qD}(lwyq`qmEpVpqQ}ykg zs#}*P;1_d#8!QwC4a(L2_}9VXK6)0b!#4>I1~BLZdz}J7$W1F#WFc&Wu>douM85`Q z-4zEgt6wv=y{M1a?yudfH0P0_&iV>mUj+fz`?^*RKX>bl^FP1LUp#f{Cqeh!N+ud; z-%_0<tzY}=A_|6E=hszy98=ph>m7z``GXTXsX_+is<u}re#2g}1<FA0h=J6ry-`ur zRPC~X(6=eFG0;F&=Gmll`oqJvb>Y|#hQ9r)Vp0$z^((-z7b_|QLr*$kezD>pgi_J> z+2)lb2V_<`3sbAnJhRQq`IGUS2EUE2-XB8W>O_L)VCHyx4(0UI)45l3(eEuydLhg~ zNncr;N3eA@(*gJtci!pRwVyrH6fQ2%Etow8;nPzqS#op{XomsVx&M=0t%>pMU1^t0 zxWsPQMHgxEJaIQW9k8Vp+HFg21H(EN2M3pxkp3%9<gNC(g~>hg9N?nLFKI^y)NE6G zpb$3MOhDz2SK8OgkKCqOPLjL>i#%GW8Gm&wM--cB+AUv5U;s@WEUOxTM(@BuY8ykX z+@SmRhb^oDT%O0M#D$p(%Ji8az{nh6^qxBhb3ko$pFD2UZ&EM86vT>p=~<oXnGM|< z3;Br`yeRVQd?Nlq0_kj00W6EzrkmtzIBlD0hN@q+3h~_`>9ju~G8<lqV(VktBebt4 zzspv(c~X|5LsOe`Uq~v(OCO{UJZ*ikMu^q~KU?`5|K-xh<vOQUyX~)mgggzWS(4tw zjIR9-u_>X$=bH2v-(+Bf0;Pq3)WiqwsfaI^%Dfi_1r&?r{xWI6TL&^JBzLenW>lr; zqi5f?a<_Rt8_0?1mX!j|Q7IRwdBrc!{c54OA(^_87jU3BrR1S5UHoI40JN=PzSsV+ zssL$jQz3-w4;GjeEG%xcoK+5!DW55cEOlh{B<eMB>cU~+{5POF3)D84I3z9h2{s24 zHSSZ*JE_4G%#%JF{7aO**Y<i$LTlk_**P;9p6RpnFUVc)%RDM?<p%0e0v0XakBNKd z{!4wT|4sD&TLWEW#)Sz5wdh{f<aj{VU0=vkU;F324ytzE4#0y+URGXCT25YCUe-)r zR#jF`RZ&4w7WhTBjKL-Le-WTO?42D0|K9`?*vb@uz%t0}sW0?-z->=&4@YM=hugk^ zo({L2J$>z%m|o>9?q2%c%R)@-he>ar-ZGSlMTiY#$HpndX^_mt!g*Wk^}TQQEJAf= qw|gf>zIXI>@OE72eXU!d4`N#9;qcvkq?r0kVR{f_t?Eb5-u@48U^O@Z diff --git a/icons/mstile-70x70.png b/icons/mstile-70x70.png deleted file mode 100644 index 4064cb227c4b430a5963d4d93c7ef0f1f3e64af1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3697 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 BxDWsU diff --git a/icons/safari-pinned-tab.svg b/icons/safari-pinned-tab.svg deleted file mode 100644 index 5eb01d6432..0000000000 --- a/icons/safari-pinned-tab.svg +++ /dev/null @@ -1,35 +0,0 @@ -<?xml version="1.0" standalone="no"?> -<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN" - "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd"> -<svg version="1.0" xmlns="http://www.w3.org/2000/svg" - width="912.000000pt" height="912.000000pt" viewBox="0 0 912.000000 912.000000" - preserveAspectRatio="xMidYMid meet"> -<metadata> -Created by potrace 1.14, written by Peter Selinger 2001-2017 -</metadata> -<g transform="translate(0.000000,912.000000) scale(0.100000,-0.100000)" -fill="#000000" stroke="none"> -<path d="M3517 7451 c-24 -25 -155 -154 -289 -286 -135 -132 -424 -416 -644 --632 -219 -215 -474 -465 -566 -556 -93 -90 -168 -166 -168 -169 0 -2 1209 -4 -2688 -4 l2687 0 5 -1218 5 -1217 382 378 c210 208 597 591 860 852 l478 474 1 -46 c0 26 3 52 7 58 4 5 4 13 0 17 -9 8 -10 146 -1 146 4 0 3 12 0 28 -4 15 -6 -41 -5 57 2 17 3 36 3 43 0 6 0 29 0 50 0 20 0 53 -1 72 0 19 0 50 0 68 1 17 1 -39 1 47 0 59 0 95 -1 125 0 19 0 50 0 68 1 17 1 39 1 47 0 59 0 95 -1 125 0 -19 0 50 0 68 1 33 1 37 1 100 0 86 -1 99 -3 130 -2 18 0 35 5 38 4 3 4 15 0 -27 -9 26 -8 69 2 86 4 6 3 11 -3 11 -6 0 -6 11 -1 30 5 17 6 30 2 30 -4 0 -7 -25 -7 55 0 30 4 55 8 55 5 0 5 5 1 11 -10 16 -11 58 -2 92 3 15 4 27 0 27 -4 -0 -7 25 -7 55 0 30 4 55 8 55 5 0 5 5 1 11 -10 16 -11 58 -2 92 3 15 4 27 0 -27 -4 0 -7 25 -7 55 0 30 4 55 8 55 5 0 5 5 1 11 -10 16 -11 58 -2 92 3 15 3 -27 -1 27 -8 0 -5 65 3 87 3 7 2 13 -2 13 -5 0 -7 20 -5 43 l3 44 -2699 0 --2699 0 -45 -46z"/> -<path d="M1503 5455 c-182 -181 -566 -562 -852 -846 l-521 -516 0 -1211 0 --1212 2696 0 2695 0 154 153 c84 83 467 462 849 841 383 379 696 692 696 696 -0 3 -1208 6 -2685 6 -1476 0 -2687 3 -2691 7 -4 4 -6 33 -4 65 1 31 2 66 2 77 --3 119 -2 166 3 175 3 5 1 10 -5 10 -6 0 -9 4 -6 9 5 7 7 84 5 161 0 8 0 44 1 -80 1 36 1 98 0 137 -1 40 1 77 5 83 3 5 1 10 -5 10 -6 0 -9 4 -6 9 5 7 7 84 5 -161 0 8 0 67 0 130 1 145 1 147 2 163 0 6 0 45 -1 84 -1 40 1 77 5 83 3 5 1 -10 -5 10 -6 0 -9 4 -6 9 5 7 7 84 5 161 0 8 0 67 0 130 1 63 0 122 0 130 0 8 -0 44 0 80 0 36 0 74 0 85 0 11 0 98 1 194 0 95 -1 174 -2 175 -2 0 -152 -147 --335 -329z"/> -</g> -</svg> diff --git a/icons/site.webmanifest b/icons/site.webmanifest deleted file mode 100644 index 3ccd337ada..0000000000 --- a/icons/site.webmanifest +++ /dev/null @@ -1,19 +0,0 @@ -{ - "name": "", - "short_name": "", - "icons": [ - { - "src": "/android-chrome-192x192.png", - "sizes": "192x192", - "type": "image/png" - }, - { - "src": "/android-chrome-512x512.png", - "sizes": "512x512", - "type": "image/png" - } - ], - "theme_color": "#2e2f27", - "background_color": "#2e2f27", - "display": "standalone" -} diff --git a/index.html b/index.html deleted file mode 100644 index 5d7f64c1b1..0000000000 --- a/index.html +++ /dev/null @@ -1,37 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -skipheading: true -index: false ---- - -<div class="row m-20"> - <div class="col"> - <h1>Vespa documentation</h1> - <p class="p-t-10">Welcome to the Vespa documentation site!</p> - <p class="p-t-10">To see what Vespa is about, visit the <a href="https://vespa.ai">Vespa home page</a>.</p> - </div> -</div> - -<div class="row m-20"> - <div class="col"> - <form action="https://search.vespa.ai/search.html" method="GET" id="search-form"> - <div class="input-group has-button"> - <input style="height:50px; font-size:1.6rem; padding:10px; border:1px solid #c0c0e0; border-radius: 8px;" - autofocus size="40" type="text" placeholder="Search documentation" id="searchinput" name="query" /> - </div> - </form> - </div> -</div> - -<div class="row m-20 p-t-10"> - <div class="col"> - <p><a href="en/getting-started.html">Getting started</a> - Create your first app</p> - <p><a href="sitemap.html">Table of contents</a> - Browse documentation</p> - <p><a href="en/faq.html">FAQ</a> - Frequently asked questions</p> - <p><a href="https://slack.vespa.ai">Vespa Slack</a> - chat with users and developers</p> - <p><a href="https://github.com/vespa-engine/vespa/issues">GitHub Issues</a> - browse and create issues</p> - <p><a href="https://stackoverflow.com/questions/tagged/vespa">Stack Overflow</a> - questions tagged vespa</p> - <p><a href="https://blog.vespa.ai/">Vespa Blog</a> - the Vespa tech blog</p> - <p><a href="https://x.com/vespaengine" data-proofer-ignore>vespaengine@</a> - Vespa on X</p> - </div> -</div> diff --git a/index.mdx b/index.mdx new file mode 100644 index 0000000000..851f7bb562 --- /dev/null +++ b/index.mdx @@ -0,0 +1,318 @@ +--- +title: "Vespa Documentation" +description: "Technical documentation for Vespa — the open-source big data serving engine for real-time search, recommendation, and AI applications" +mode: custom +--- + +{/* Hero Section */} + +<div className="relative overflow-hidden"> + <div className="max-w-8xl mx-auto px-4 lg:px-12 py-16 md:py-24"> + <div className="flex flex-col md:flex-row items-center gap-8 md:gap-12"> + {/* Left content - text aligned left */} + <div className="flex-1 text-left"> + <h1 className="text-4xl md:text-6xl font-bold tracking-tight mb-6 text-gray-900 dark:text-white"> + Get Started + <br /> + <span className="text-[#61D790]">With Vespa</span> + </h1> + <p className="text-lg md:text-xl text-gray-600 dark:text-gray-400 max-w-xl mb-6"> + Learn how to build, deploy, and operate applications on Vespa — the + open-source serving engine for search, recommendations, and AI at any + scale. + </p> + <div className="max-w-xl mb-10"> + <button + type="button" + data-search-trigger + onClick={() => { + document.body.dispatchEvent( + new KeyboardEvent("keydown", { + key: "k", + code: "KeyK", + metaKey: true, + bubbles: true, + cancelable: true, + }) + ); + }} + className="pointer-events-auto flex h-12 w-full cursor-pointer items-center justify-between gap-3 rounded-full border border-gray-200 bg-white px-5 text-left text-base text-[#525654] shadow-sm transition hover:border-gray-300 hover:text-gray-700 dark:border-[#43433C] dark:bg-[#2E2E26] dark:text-[#D0D4D2] dark:hover:border-[#5A5A51] dark:hover:text-white" + > + <span className="flex min-w-0 flex-1 items-center gap-3"> + <Icon icon="magnifying-glass" size={16} className="shrink-0 text-[#525654] dark:text-[#D0D4D2]" /> + <span className="min-w-0 truncate">Search the documentation</span> + </span> + <span className="flex-none text-sm font-semibold text-gray-500 dark:text-[#D0D4D2]">⌘K</span> + </button> + </div> + <div className="flex flex-col sm:flex-row gap-4 items-start sm:items-center"> + <a + href="/en/basics/deploy-an-application" + className="inline-flex items-center justify-center px-8 py-3 bg-[#61D790] text-black text-lg font-medium rounded-full hover:bg-[#4fc77a] transition-colors" + > + Quickstart Guide + </a> + <a + href="/en/learn/overview" + className="inline-flex items-center justify-center px-8 py-3 border border-gray-900 dark:border-gray-100 text-gray-900 dark:text-gray-100 text-lg font-medium rounded-full hover:bg-gray-100 dark:hover:bg-gray-800 transition-colors" + > + Platform Overview + </a> + </div> + </div> + {/* Right content - illustration */} + <div className="flex-1 flex justify-center md:justify-end"> + <img + src="/logo/hero-light.svg" + alt="Vespa workspace illustration" + noZoom + className="w-full max-w-md md:max-w-lg h-auto dark:hidden" + /> + <img + src="/logo/hero-dark.svg" + alt="Vespa workspace illustration" + noZoom + className="w-full max-w-md md:max-w-lg h-auto hidden dark:block" + /> + </div> + </div> + </div> +</div> + +{/* Getting Started - Arrow Links */} + +<div className="max-w-8xl mx-auto px-4 lg:px-12 py-12"> + <h2 className="text-2xl font-bold mb-2 text-gray-900 dark:text-white">Getting Started</h2> + <p className="text-gray-600 dark:text-gray-400 mb-8"> + Choose your deployment method and start building + </p> + + <CardGroup cols={3}> + <Card title="Deploy to Vespa Cloud" icon="cloud-arrow-up" href="/en/basics/deploy-an-application"> + Create an application package, configure services.xml, and deploy to managed infrastructure + </Card> + <Card title="Python Client (pyvespa)" icon="code" href="https://vespa-engine.github.io/pyvespa/getting-started-pyvespa-cloud.html"> + Define schemas, deploy applications, and query Vespa programmatically with Python + </Card> + <Card title="Self-Hosted (Docker)" icon="server" href="/en/basics/deploy-an-application-local.html"> + Run Vespa locally or on your own infrastructure using Docker containers + </Card> + </CardGroup> +</div> + +{/* Use Cases - Feature Grid */} + +<div className="max-w-8xl mx-auto px-4 lg:px-12 py-12"> + <h2 className="text-2xl font-bold mb-2 text-gray-900 dark:text-white">Common Use Cases</h2> + <p className="text-gray-600 dark:text-gray-400 mb-8"> + Reference architectures and implementation guides for typical applications + </p> + + <CardGroup cols={2}> + <Card title="Search Applications" icon="magnifying-glass" href="/en/learn/tutorials"> + Implement text search with BM25, vector search with HNSW, or hybrid approaches combining both methods. + </Card> + <Card title="Retrieval-Augmented Generation" icon="sparkles" href="/en/rag/rag"> + Configure document chunking, embedding generation, retrieval pipelines, and LLM integration for RAG. + </Card> + <Card title="Recommendation Systems" icon="heart" href="/en/learn/tutorials"> + Build content-based and collaborative filtering systems with real-time user signals and ML ranking. + </Card> + <Card title="E-commerce Search" icon="shopping-cart" href="/en/learn/tutorials/e-commerce"> + Configure product schemas, faceted search, inventory filtering, and personalized result ranking. + </Card> + </CardGroup> +</div> + +{/* Core Concepts - Multi-column Link List */} + +<div className="max-w-8xl mx-auto px-4 lg:px-12 py-12"> + <h2 className="text-2xl font-bold mb-2 text-gray-900 dark:text-white">Core Concepts</h2> + <p className="text-gray-600 dark:text-gray-400 mb-8"> + Understand the fundamental components of Vespa + </p> + + <div className="grid grid-cols-1 md:grid-cols-3 gap-8"> + <div> + <h3 className="flex items-center gap-2 font-semibold pb-3 mb-4 border-b border-gray-200 dark:border-gray-800 text-gray-900 dark:text-white"> + <Icon icon="book-open" size={18} className="text-gray-400" /> + Vespa Basics + </h3> + <ul className="space-y-2"> + <li><a href="/en/basics/applications" className="group flex items-center justify-between text-gray-600 dark:text-gray-400 hover:text-[#61D790] transition-colors">Applications<span className="opacity-0 group-hover:opacity-100 transition-opacity">→</span></a></li> + <li><a href="/en/basics/schemas" className="group flex items-center justify-between text-gray-600 dark:text-gray-400 hover:text-[#61D790] transition-colors">Schemas<span className="opacity-0 group-hover:opacity-100 transition-opacity">→</span></a></li> + <li><a href="/en/basics/writing" className="group flex items-center justify-between text-gray-600 dark:text-gray-400 hover:text-[#61D790] transition-colors">Writing Documents<span className="opacity-0 group-hover:opacity-100 transition-opacity">→</span></a></li> + <li><a href="/en/basics/deploy-an-application" className="group flex items-center justify-between text-gray-600 dark:text-gray-400 hover:text-[#61D790] transition-colors">Deployment<span className="opacity-0 group-hover:opacity-100 transition-opacity">→</span></a></li> + <li><a href="/en/learn/overview" className="group flex items-center justify-between text-gray-600 dark:text-gray-400 hover:text-[#61D790] transition-colors">Platform Overview<span className="opacity-0 group-hover:opacity-100 transition-opacity">→</span></a></li> + </ul> + </div> + + <div> + <h3 className="flex items-center gap-2 font-semibold pb-3 mb-4 border-b border-gray-200 dark:border-gray-800 text-gray-900 dark:text-white"> + <Icon icon="magnifying-glass" size={18} className="text-gray-400" /> + Querying & Search + </h3> + <ul className="space-y-2"> + <li><a href="/en/querying/query-api" className="group flex items-center justify-between text-gray-600 dark:text-gray-400 hover:text-[#61D790] transition-colors">Query API<span className="opacity-0 group-hover:opacity-100 transition-opacity">→</span></a></li> + <li><a href="/en/querying/query-language" className="group flex items-center justify-between text-gray-600 dark:text-gray-400 hover:text-[#61D790] transition-colors">YQL Query Language<span className="opacity-0 group-hover:opacity-100 transition-opacity">→</span></a></li> + <li><a href="/en/querying/nearest-neighbor-search" className="group flex items-center justify-between text-gray-600 dark:text-gray-400 hover:text-[#61D790] transition-colors">Nearest Neighbor Search<span className="opacity-0 group-hover:opacity-100 transition-opacity">→</span></a></li> + <li><a href="/en/querying/grouping" className="group flex items-center justify-between text-gray-600 dark:text-gray-400 hover:text-[#61D790] transition-colors">Grouping & Aggregation<span className="opacity-0 group-hover:opacity-100 transition-opacity">→</span></a></li> + <li><a href="/en/querying/text-matching" className="group flex items-center justify-between text-gray-600 dark:text-gray-400 hover:text-[#61D790] transition-colors">Text Matching<span className="opacity-0 group-hover:opacity-100 transition-opacity">→</span></a></li> + </ul> + </div> + + <div> + <h3 className="flex items-center gap-2 font-semibold pb-3 mb-4 border-b border-gray-200 dark:border-gray-800 text-gray-900 dark:text-white"> + <Icon icon="brain" size={18} className="text-gray-400" /> + Ranking & ML + </h3> + <ul className="space-y-2"> + <li><a href="/en/ranking/ranking-intro" className="group flex items-center justify-between text-gray-600 dark:text-gray-400 hover:text-[#61D790] transition-colors">Ranking Introduction<span className="opacity-0 group-hover:opacity-100 transition-opacity">→</span></a></li> + <li><a href="/en/ranking/onnx" className="group flex items-center justify-between text-gray-600 dark:text-gray-400 hover:text-[#61D790] transition-colors">ONNX Models<span className="opacity-0 group-hover:opacity-100 transition-opacity">→</span></a></li> + <li><a href="/en/rag/embedding" className="group flex items-center justify-between text-gray-600 dark:text-gray-400 hover:text-[#61D790] transition-colors">Embeddings<span className="opacity-0 group-hover:opacity-100 transition-opacity">→</span></a></li> + <li><a href="/en/ranking/phased-ranking" className="group flex items-center justify-between text-gray-600 dark:text-gray-400 hover:text-[#61D790] transition-colors">Phased Ranking<span className="opacity-0 group-hover:opacity-100 transition-opacity">→</span></a></li> + <li><a href="/en/ranking/tensor-user-guide" className="group flex items-center justify-between text-gray-600 dark:text-gray-400 hover:text-[#61D790] transition-colors">Tensor Guide<span className="opacity-0 group-hover:opacity-100 transition-opacity">→</span></a></li> + </ul> + </div> + + </div> +</div> + +{/* Tutorials - Horizontal Cards */} + +<div className="max-w-8xl mx-auto px-4 lg:px-12 py-12"> + <h2 className="text-2xl font-bold mb-2 text-gray-900 dark:text-white">Tutorials</h2> + <p className="text-gray-600 dark:text-gray-400 mb-8"> + Step-by-step walkthroughs for common implementation patterns + </p> + + <CardGroup cols={2}> + <Card title="Text Search" icon="file" href="/en/learn/tutorials/text-search"> + Configure schemas, indexing, and BM25 ranking for full-text search + </Card> + <Card title="Vector Search" icon="magnifying-glass" href="/en/querying/nearest-neighbor-search-guide"> + Set up HNSW indexes, configure distance metrics, and query embeddings + </Card> + <Card title="Hybrid Search" icon="bolt" href="/en/learn/tutorials/hybrid-search"> + Combine lexical and semantic search with reciprocal rank fusion + </Card> + <Card title="RAG Implementation" icon="sparkles" href="/en/learn/tutorials/rag-blueprint"> + End-to-end RAG setup with chunking, retrieval, and LLM integration + </Card> + </CardGroup> + + <div className="mt-6"> + <Card title="News Search & Recommendations (Multi-part Series)" icon="book-open" href="/en/learn/tutorials/news-1-deploy-an-application"> + Complete application covering deployment, feeding, querying, ranking, and recommendation algorithms + </Card> + </div> +</div> + +{/* Developer Tools - Badge Cards */} + +<div className="max-w-8xl mx-auto px-4 lg:px-12 py-12"> + <h2 className="text-2xl font-bold mb-2 text-gray-900 dark:text-white">Developer Tools & SDKs</h2> + <p className="text-gray-600 dark:text-gray-400 mb-8"> + Client libraries and command-line tools for interacting with Vespa + </p> + + <div className="grid grid-cols-1 md:grid-cols-2 gap-6"> + <a + href="/en/clients/vespa-cli" + className="group flex items-center justify-between p-4 rounded-lg border border-gray-200 dark:border-[#43433C] bg-white dark:bg-[#2E2E26] hover:border-[#61D790] transition-colors" + > + <div className="flex items-center gap-3"> + <Icon icon="terminal" size={20} className="text-gray-900 dark:text-white" /> + <span className="font-medium">Vespa CLI</span> + </div> + <span className="flex items-center gap-2 text-sm text-[#61D790] opacity-0 group-hover:opacity-100 transition-opacity"> + Get started + <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"> + <path d="M5 12h14"/> + <path d="m12 5 7 7-7 7"/> + </svg> + </span> + </a> + + <a + href="https://vespa-engine.github.io/pyvespa/" + className="group flex items-center justify-between p-4 rounded-lg border border-gray-200 dark:border-[#43433C] bg-white dark:bg-[#2E2E26] hover:border-[#61D790] transition-colors" + > + <div className="flex items-center gap-3"> + <Icon icon="python" size={20} className="text-gray-900 dark:text-white" /> + <span className="font-medium">pyvespa</span> + </div> + <span className="flex items-center gap-2 text-sm text-[#61D790] opacity-0 group-hover:opacity-100 transition-opacity"> + Get started + <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"> + <path d="M5 12h14"/> + <path d="m12 5 7 7-7 7"/> + </svg> + </span> + </a> + + <a + href="/en/clients/vespa-feed-client.html" + className="group flex items-center justify-between p-4 rounded-lg border border-gray-200 dark:border-[#43433C] bg-white dark:bg-[#2E2E26] hover:border-[#61D790] transition-colors" + > + <div className="flex items-center gap-3"> + <Icon icon="mug-hot" size={20} className="text-gray-900 dark:text-white" /> + <span className="font-medium">Java Feed Client</span> + </div> + <span className="flex items-center gap-2 text-sm text-[#61D790] opacity-0 group-hover:opacity-100 transition-opacity"> + Get started + <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"> + <path d="M5 12h14"/> + <path d="m12 5 7 7-7 7"/> + </svg> + </span> + </a> + + <a + href="https://github.com/vespa-engine/sample-apps" + className="group flex items-center justify-between p-4 rounded-lg border border-gray-200 dark:border-[#43433C] bg-white dark:bg-[#2E2E26] hover:border-[#61D790] transition-colors" + > + <div className="flex items-center gap-3"> + <Icon icon="folder-open" size={20} className="text-gray-900 dark:text-white" /> + <span className="font-medium">Sample Applications</span> + </div> + <span className="flex items-center gap-2 text-sm text-[#61D790] opacity-0 group-hover:opacity-100 transition-opacity"> + Browse + <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"> + <path d="M5 12h14"/> + <path d="m12 5 7 7-7 7"/> + </svg> + </span> + </a> + + </div> +</div> + +{/* CTA Banner */} + +<div className="max-w-8xl mx-auto px-4 lg:px-12 py-12 pb-20"> + <div className="flex flex-col md:flex-row md:items-center md:justify-between gap-6 p-8 rounded-lg border border-gray-200 dark:border-[#43433C] bg-white dark:bg-[#2E2E26]"> + <div> + <h2 className="text-xl font-bold mb-1 text-gray-900 dark:text-white"> + Need help? + </h2> + <p className="text-gray-600 dark:text-gray-400"> + Check the FAQ, ask questions on Stack Overflow, or join the community. + </p> + </div> + <div className="flex flex-wrap gap-3"> + <a + href="/en/learn/faq" + className="inline-flex items-center justify-center px-5 py-2.5 bg-[#61D790] text-black font-medium rounded-full hover:opacity-90 transition-opacity" + > + FAQ + </a> + <a + href="https://stackoverflow.com/questions/tagged/vespa" + className="inline-flex items-center justify-center px-5 py-2.5 border border-gray-900 dark:border-gray-100 text-gray-900 dark:text-gray-100 font-medium rounded-full hover:bg-gray-100 dark:hover:bg-gray-800 transition-colors" + > + Stack Overflow + </a> + </div> + </div> +</div> diff --git a/ja/features.html b/ja/features.html deleted file mode 100644 index 490e033d7c..0000000000 --- a/ja/features.html +++ /dev/null @@ -1,111 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Vespaの機能" ---- - -<h2>Vespaとは何か?</h2> - -<p>Vespaはリアルタイムで巨大なデータセットに対して計算を実行・配信するためのエンジンです。 -どのような量のデータでも書き込んで保存することができ、典型的には数十ミリ秒で完了するデータに対する膨大なクエリーを実行することができます。 -</p> - -<p>データを選択するために、クエリーには構造化されたフィルタと構造化されていないテキスト検索の両方を指定することができます。 -検索の関連度、レコメンデーション、ターゲティングやパーソナライズといった用途を実現するため、マッチしたすべてのデータはランキング関数(典型的には機械学習による)に従ってランキングされます。 -</p> - -<p>マッチしたすべてのドキュメントはグループやサブグループに分けることもでき、このときデータはグラフ、タグクラウド、ナビゲーション・ツール、結果の多様性といった機能を実現するため各グループに集約されます。 -</p> - -<p>クエリー、検索結果、書き込みを処理するアプリケーション固有の動作は、Javaコンポーネントをアプリケーション・パッケージに含めることで追加することができます。</p> - -<p>Vespaはリアルタイムです。データの断片とコアに対してクエリーを同時実行することで、さらなるクエリー量には同じデータのたくさんのコピー(グループ)に対してクエリーを同時実行することで、どのような量のデータでも一定の応答時間を維持するよう設計されています。数十ミリ秒以内にレスポンスを返却できるよう最適化されています。データの書き込みは数ミリ秒で参照できるようになり、各ノードで秒あたり数千から数万のレートで扱うことができます。 -</p> - -<p>Vespaを簡単にセットアップして運用できるようにするため多くの作業が費やされました。 -どのようなVespaアプリケーション(単一ノードのシステムから複数のデータセンターにまたがる数百ノードを稼働させるシステムまで)も<i>アプリケーション・パッケージ</i>と呼ばれる単一のアーティファクトで完全に設定されます。ノードやプロセス、コンポーネントの低レベルな設定はアプリケーション・パッケージで指定された特性をもとに行われます。 -</p> - -<p>Vespaはスケーラブルです。数百億のドキュメントを扱う数百ノードにもおよぶシステムは珍しいものではなく、単一ノードのシステムをセットアップして変更するのと大変さは変わりません。 - -すべてのシステム・コンポーネントは蓄積されたデータと同様に冗長性と自己修正のメカニズムを備えているため、ハードウェア故障は運用上の緊急事態ではなく、都合のよい時にキャパシティを再度追加することで対処できます。 -</p> - -<p>Vespaは自己修復のメカニズムを備えておりダイナミックです。マシンが失われたり新しいものが追加されると、データの配信と書き込みは継続しながら、各マシンに自動的に再配布されます。 - -変更されたアプリケーション・パッケージをデプロイすることで、配信を継続しながら設定情報とJavaコンポーネントを変更することが可能です - ダウンタイムはなく、再起動も必要ありません。 -</p> - - -<h2>機能</h2> - -このセクションではVespaの主な機能の概要を説明します。 -ドキュメンテーションの残りで詳しく説明します。 - -<h3>データと書き込み</h3> - -<ul> - <li>Vespaのドキュメントは追加、置換、変更(1つのフィールドあるいは任意のサブセット)、削除することができます。 - <li>書き込みは恒久的な状態になり、さらに(デフォルトでは)クエリーで参照できるようになるとクライアントに受信したことが通知されます。 - <li>書き込みリクエストはクエリーに対する配信を継続しながら、毎ノード毎秒あたり数千から数万のボリュームを維持して発行することができます。 - <li>データは設定可能な冗長レベルでレプリケーションされます。 - <li>ノードが追加、削除あるいは意図せず失われると、設定された冗長レベルのデータの均等分散が維持されます。 - <li>データの破損は、破損していないデータのレプリカから自動的に修復されます。 - <li>データはシンプルなHTTP APIか、(大容量向けに)小さなスタンドアロンのJavaクライアントで書き込むことができます。 - <li>一般的なプリミティブ型やコレクション、構造体、テンソルをドキュメントのデータ・スキーマのフィールドに指定することができます。 - <li>同時にいくつものデータ・スキーマを使うことができます。 - <li>ドキュメントは相互に参照することができ、参照されているドキュメントのフィールドはパフォーマンスのペナルティなしでクエリーで指定することができます。 - <li>カスタムのJavaコンポーネントを追加することで、書き込み処理をプロセスすることができます。 - <li>データはバッチ再処理用にシステムからストリーミングすることができます。 -</ul> - -<h3>クエリー</h3> - -<ul> - <li>クエリーは構造化されたフィルターと構造化されていない検索演算子の任意の組み合わせを含むことができます。 - <li>クエリーは大きなテンソルとベクトルを(例えばユーザーを表現するために)含むことができます。 - <li>クエリーで検索結果がどのようにランキングされて、またオーガナイズされるべきかを指定します(以下のセクションを参照してください)。 - <li>カスタムのJavaコンポーネントを追加することで、クエリーと検索結果をプロセスすることができます - カスタムのリクエスト・ハンドラで任意のHTTPリクエストをクエリーに変換することもできます。 - <li>クエリーの応答時間は典型的には数十ミリ秒以内で、ハードウェアを追加することで負荷やデータサイズに対応できます。 - <li>事前に設定されたドキュメントのグループ(例えば、ユーザーのドキュメント)に対してのみ<i>ストリーミング検索</i>モードを利用することができます。このモードでは各ノードで、短い応答時間を維持しながら数十億のドキュメントを保持して配信することができます。 -</ul> - -<h3>ランキング</h3> - -<ul> - <li>すべての結果は設定されたランキング関数でランキングされます。ランキング関数はクエリーで指定します。 - <li>ランキング関数にはスカラーまたはテンソル(多次元配列)の任意の数学関数を指定することができます。 - <li>スカラー関数にはビジネスロジックや決定木を表現するための "if" 関数が含まれます。 - <li>テンソル関数には深層ニューラルネットワークのようなもっとも進化した機械学習ランキング関数の表現が可能な原始関数と合成関数の強力なセットが含まれます。 - <li>期待の持てる候補のランキングにより多くのCPUを割り当てられるように、複数フェーズのランキングがサポートされています。 - <li>ドキュメントでの位置情報を用いたテキストのランキング特徴量の強力なセットがすぐに使えます。 - <li>その他にも2次元の距離や鮮度といったランキング特徴量があります。 -</ul> - -<h3>結果のオーガナイズとプレゼンテーション</h3> - -<ul> - <li>クエリーでの指定にしたがって、クエリーに対してマッチしたドキュメントをグルーピングしたり、集約したりすることができます。 - <li>同時実行される複数台のマシンにまたがる場合であっても、すべてのマッチしたドキュメントが含まれます。 - <li>マッチしたドキュメントはユニークな値や数値的なバケットでグルーピングすることができます。 - <li>任意のレベルのグループとサブグループがサポートされており、複数の並列グルーピングを1つのクエリーで指定することができます。 - <li>データは集約することができ(カウントする、平均をとるなど)、また各グループやサブグループ内で選択することができます。 - <li>ドキュメントからのいかなるデータ選択もクライアントに返却される最終的な検索結果に含めることができます。 - <li>マッチしたフィールドにおける検索エンジン・スタイルのキーワードのハイライトがサポートされています。 -</ul> - -<h2>設定と運用</h2> - -<ul> - <li>VespaはRPMまたはDockerイメージとしてインストールすることができます。それは個人のラップトップでも、所有しているデータセンターでも、AWSでも可能です。 - <li>Vespaのアプリケーションは独立した構築可能なアーティファクトで完全に記述されます: それは<i>アプリケーション・パッケージ</i>で、個々のマシンやプロセスを個別に設定する必要はありません。 - <li>システムは任意の数のノードを含む各タイプ(ステートレスとステートフル)からなる複数のクラスタで構成することができます。 - <li>どのようなサイズのシステムもアプリケーション・パッケージの2つの短い設定ファイルで記述することができます。 - <li>ドキュメント・スキーマ、Javaコンポーネント、ランキング関数/モデルもアプリケーションパッケージで設定されます。 - <li>アプリケーションによって意図されたシステムを実現するため、アプリケーション・パッケージが単一のユニットとしてVespaにデプロイされます。 - <li>アプリケーションのたいていの変更(Javaコンポーネントの変更を含む)は変更されたアプリケーション・パッケージをデプロイすることで適用することができます。システムは配信と書き込みを維持しながら変更処理を管理します。 - <li>たいていのドキュメント・スキーマの変更(フィールド型の変更を除く)はシステムが稼働した状態で適用することができます。 - <li>アプリケーション・パッケージの変更は稼働中のシステムに対する破壊的な変更を防ぐため、デプロイ時にバリデーションされます。 - <li>Vespaには単一障害点がなく、自動でフェイルしたノードを迂回します。 - <li>システムのログはリアルタイムで中央サーバーに収集されます。 - <li>すべてのノードから、サードパーティーのメトリクス/アラートシステムに選択されたメトリクスを送信することができます。 -</ul> diff --git a/ja/features.mdx b/ja/features.mdx new file mode 100644 index 0000000000..8800ed4774 --- /dev/null +++ b/ja/features.mdx @@ -0,0 +1,102 @@ +--- +title: "Vespaの機能" +--- + +## Vespaとは何か? + +Vespaはリアルタイムで巨大なデータセットに対して計算を実行・配信するためのエンジンです。 +どのような量のデータでも書き込んで保存することができ、典型的には数十ミリ秒で完了するデータに対する膨大なクエリーを実行することができます。 + + +データを選択するために、クエリーには構造化されたフィルタと構造化されていないテキスト検索の両方を指定することができます。 +検索の関連度、レコメンデーション、ターゲティングやパーソナライズといった用途を実現するため、マッチしたすべてのデータはランキング関数(典型的には機械学習による)に従ってランキングされます。 + + +マッチしたすべてのドキュメントはグループやサブグループに分けることもでき、このときデータはグラフ、タグクラウド、ナビゲーション・ツール、結果の多様性といった機能を実現するため各グループに集約されます。 + + +クエリー、検索結果、書き込みを処理するアプリケーション固有の動作は、Javaコンポーネントをアプリケーション・パッケージに含めることで追加することができます。 + +Vespaはリアルタイムです。データの断片とコアに対してクエリーを同時実行することで、さらなるクエリー量には同じデータのたくさんのコピー(グループ)に対してクエリーを同時実行することで、どのような量のデータでも一定の応答時間を維持するよう設計されています。数十ミリ秒以内にレスポンスを返却できるよう最適化されています。データの書き込みは数ミリ秒で参照できるようになり、各ノードで秒あたり数千から数万のレートで扱うことができます。 + + +Vespaを簡単にセットアップして運用できるようにするため多くの作業が費やされました。 +どのようなVespaアプリケーション(単一ノードのシステムから複数のデータセンターにまたがる数百ノードを稼働させるシステムまで)も<i>アプリケーション・パッケージ</i>と呼ばれる単一のアーティファクトで完全に設定されます。ノードやプロセス、コンポーネントの低レベルな設定はアプリケーション・パッケージで指定された特性をもとに行われます。 + + +Vespaはスケーラブルです。数百億のドキュメントを扱う数百ノードにもおよぶシステムは珍しいものではなく、単一ノードのシステムをセットアップして変更するのと大変さは変わりません。 + +すべてのシステム・コンポーネントは蓄積されたデータと同様に冗長性と自己修正のメカニズムを備えているため、ハードウェア故障は運用上の緊急事態ではなく、都合のよい時にキャパシティを再度追加することで対処できます。 + + +Vespaは自己修復のメカニズムを備えておりダイナミックです。マシンが失われたり新しいものが追加されると、データの配信と書き込みは継続しながら、各マシンに自動的に再配布されます。 + +変更されたアプリケーション・パッケージをデプロイすることで、配信を継続しながら設定情報とJavaコンポーネントを変更することが可能です - ダウンタイムはなく、再起動も必要ありません。 + + + +## 機能 + +このセクションではVespaの主な機能の概要を説明します。 +ドキュメンテーションの残りで詳しく説明します。 + +<h3>データと書き込み</h3> + + + - Vespaのドキュメントは追加、置換、変更(1つのフィールドあるいは任意のサブセット)、削除することができます。 + - 書き込みは恒久的な状態になり、さらに(デフォルトでは)クエリーで参照できるようになるとクライアントに受信したことが通知されます。 + - 書き込みリクエストはクエリーに対する配信を継続しながら、毎ノード毎秒あたり数千から数万のボリュームを維持して発行することができます。 + - データは設定可能な冗長レベルでレプリケーションされます。 + - ノードが追加、削除あるいは意図せず失われると、設定された冗長レベルのデータの均等分散が維持されます。 + - データの破損は、破損していないデータのレプリカから自動的に修復されます。 + - データはシンプルなHTTP APIか、(大容量向けに)小さなスタンドアロンのJavaクライアントで書き込むことができます。 + - 一般的なプリミティブ型やコレクション、構造体、テンソルをドキュメントのデータ・スキーマのフィールドに指定することができます。 + - 同時にいくつものデータ・スキーマを使うことができます。 + - ドキュメントは相互に参照することができ、参照されているドキュメントのフィールドはパフォーマンスのペナルティなしでクエリーで指定することができます。 + - カスタムのJavaコンポーネントを追加することで、書き込み処理をプロセスすることができます。 + - データはバッチ再処理用にシステムからストリーミングすることができます。 + +#### クエリー + + - クエリーは構造化されたフィルターと構造化されていない検索演算子の任意の組み合わせを含むことができます。 + - クエリーは大きなテンソルとベクトルを(例えばユーザーを表現するために)含むことができます。 + - クエリーで検索結果がどのようにランキングされて、またオーガナイズされるべきかを指定します(以下のセクションを参照してください)。 + - カスタムのJavaコンポーネントを追加することで、クエリーと検索結果をプロセスすることができます - カスタムのリクエスト・ハンドラで任意のHTTPリクエストをクエリーに変換することもできます。 + - クエリーの応答時間は典型的には数十ミリ秒以内で、ハードウェアを追加することで負荷やデータサイズに対応できます。 + - 事前に設定されたドキュメントのグループ(例えば、ユーザーのドキュメント)に対してのみ<i>ストリーミング検索</i>モードを利用することができます。このモードでは各ノードで、短い応答時間を維持しながら数十億のドキュメントを保持して配信することができます。 + + +#### ランキング + + - すべての結果は設定されたランキング関数でランキングされます。ランキング関数はクエリーで指定します。 + - ランキング関数にはスカラーまたはテンソル(多次元配列)の任意の数学関数を指定することができます。 + - スカラー関数にはビジネスロジックや決定木を表現するための "if" 関数が含まれます。 + - テンソル関数には深層ニューラルネットワークのようなもっとも進化した機械学習ランキング関数の表現が可能な原始関数と合成関数の強力なセットが含まれます。 + - 期待の持てる候補のランキングにより多くのCPUを割り当てられるように、複数フェーズのランキングがサポートされています。 + - ドキュメントでの位置情報を用いたテキストのランキング特徴量の強力なセットがすぐに使えます。 + - その他にも2次元の距離や鮮度といったランキング特徴量があります。 + +### 結果のオーガナイズとプレゼンテーション + + - クエリーでの指定にしたがって、クエリーに対してマッチしたドキュメントをグルーピングしたり、集約したりすることができます。 + - 同時実行される複数台のマシンにまたがる場合であっても、すべてのマッチしたドキュメントが含まれます。 + - マッチしたドキュメントはユニークな値や数値的なバケットでグルーピングすることができます。 + - 任意のレベルのグループとサブグループがサポートされており、複数の並列グルーピングを1つのクエリーで指定することができます。 + - データは集約することができ(カウントする、平均をとるなど)、また各グループやサブグループ内で選択することができます。 + - ドキュメントからのいかなるデータ選択もクライアントに返却される最終的な検索結果に含めることができます。 + - マッチしたフィールドにおける検索エンジン・スタイルのキーワードのハイライトがサポートされています。 + +## 設定と運用 + + - VespaはRPMまたはDockerイメージとしてインストールすることができます。それは個人のラップトップでも、所有しているデータセンターでも、AWSでも可能です。 + - Vespaのアプリケーションは独立した構築可能なアーティファクトで完全に記述されます: それは<i>アプリケーション・パッケージ</i>で、個々のマシンやプロセスを個別に設定する必要はありません。 + - システムは任意の数のノードを含む各タイプ(ステートレスとステートフル)からなる複数のクラスタで構成することができます。 + - どのようなサイズのシステムもアプリケーション・パッケージの2つの短い設定ファイルで記述することができます。 + - ドキュメント・スキーマ、Javaコンポーネント、ランキング関数/モデルもアプリケーションパッケージで設定されます。 + - アプリケーションによって意図されたシステムを実現するため、アプリケーション・パッケージが単一のユニットとしてVespaにデプロイされます。 + - アプリケーションのたいていの変更(Javaコンポーネントの変更を含む)は変更されたアプリケーション・パッケージをデプロイすることで適用することができます。システムは配信と書き込みを維持しながら変更処理を管理します。 + - たいていのドキュメント・スキーマの変更(フィールド型の変更を除く)はシステムが稼働した状態で適用することができます。 + - アプリケーション・パッケージの変更は稼働中のシステムに対する破壊的な変更を防ぐため、デプロイ時にバリデーションされます。 + - Vespaには単一障害点がなく、自動でフェイルしたノードを迂回します。 + - システムのログはリアルタイムで中央サーバーに収集されます。 + - すべてのノードから、サードパーティーのメトリクス/アラートシステムに選択されたメトリクスを送信することができます。 diff --git a/ja/introduction-to-documentation.html b/ja/introduction-to-documentation.mdx similarity index 69% rename from ja/introduction-to-documentation.html rename to ja/introduction-to-documentation.mdx index e09f9c5e4c..212cbda2ba 100644 --- a/ja/introduction-to-documentation.html +++ b/ja/introduction-to-documentation.mdx @@ -1,41 +1,37 @@ --- -# Copyright Vespa.ai. All rights reserved. title: "Vespaドキュメンテーションの紹介" --- -<p> このドキュメンテーションはVespaの利用者、あるいは潜在的な利用者(アプリケーションの所有者 / PM、エンジニアまたは運用者)のためのものです。 このドキュメンテーションはプロダクトの概念上の概要をカバーしており、それには背景的な理論や、プロダクトがなぜそのように開発されたかの説明が含まれています。 これは開発者が最初に使う機能の詳しい説明に続きます。 -</p><p> + Vespaプラグインの開発については、このドキュメンテーションは経験豊かなJava開発者を対象としています。 初心者は対象としておらず、一般的なプログラミング技術やプログラミング言語の基礎はカバーしていません。 -</p><p> + ドキュメンテーションの一部について読者はUnixライクなプラットフォームに精通しているべきで、これはVespaがLinux上で利用可能であるためです。 VespaのAPIは、Vespaの内部動作に関する深い知識がなくても使い始められるようになっています。 読者はエキスパートである必要は決してありませんが、Vespaの基本的性質をきちんと理解することで、テキストとサンプルをより簡単に理解することができるでしょう。 -</p><p> -もし誤りや綴りの間違い、コードの欠陥を発見したりドキュメンテーションを改善したい場合は、pull requestを送るか<a href="https://github.com/vespa-engine/vespa/issues">issueを作成</a>してください。 -</p><p> -<em>イタリック文字</em>は以下の用途に用いられます: -<ul> -<li>パス名、ファイル名、プログラム名、ホスト名、URL</li> -<li>新しい用語が定義されている箇所</li> -</ul> -<code>等幅文字</code>は以下の用途に用いられます: -<ul> -<li>プログラミング言語の要素、コードの例、キーワード、関数、クラス、インタフェース、メソッド、その他</li> -<li>コマンドとコマンドラインの出力</li> -</ul> + +もし誤りや綴りの間違い、コードの欠陥を発見したりドキュメンテーションを改善したい場合は、pull requestを送るか [issueを作成](https://github.com/vespa-engine/vespa/issues)してください。 + +**イタリック文字** は以下の用途に用いられます: + +- パス名、ファイル名、プログラム名、ホスト名、URL +- 新しい用語が定義されている箇所 + +`等幅文字` は以下の用途に用いられます: + +- プログラミング言語の要素、コードの例、キーワード、関数、クラス、インタフェース、メソッド、その他 +- コマンドとコマンドラインの出力 + 注釈とその他の重要な情報は以下のように示されます: -</p> -<p> -<strong>Note: </strong> -注意してほしい情報 -</p> -<p> + +<Note> +**Note: ** 注意してほしい情報 + コマンドラインで実行するコマンドは、以下のようにプロンプトの $ で始まり示されます: -<pre> +```bash $ export PATH=$VESPA_HOME/bin:$PATH -</pre> -</p> +``` +</Note> \ No newline at end of file diff --git a/ja/overview.html b/ja/overview.html deleted file mode 100644 index 29f5f16f76..0000000000 --- a/ja/overview.html +++ /dev/null @@ -1,136 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Vespaの概要" ---- - -<p> -Vespaはスケーラブルで低レイテンシな、ステートフルあるいはステートレスなバックエンドサービスを簡単に開発して稼働させることができるプラットフォームです。 -このドキュメントではプラットフォームの機能と主なコンポーネントの概要を説明します。 -</p> - - - -<h2 id="introduction">イントロダクション</h2> -<p> -Vespaを使うことで、レイテンシや信頼性を犠牲にせずに、大規模データや高負荷に耐えるバックエンドあるいはミドルウェアシステムを構築することができます。 -Vespaのインスタンスはいくつかの<em>ステートレスなJavaコンテナー・クラスター</em>と、データを保持する0個以上の<em>コンテント</em>・クラスターで構成されます。 -</p> -<img src="../assets/img/vespa-overview.svg" alt="Vespa Overview" /> -<p> -<a href="../en/jdisc/">ステートレスな<strong>コンテナー</strong>・クラスター</a>は、入力データと、リクエスト/クエリーとそのレスポンスの両方を処理するコンポーネントをホスティングします。 -これらのコンポーネントは(インデックス構築やクエリー実行の全ステージといった)プラットフォームに関する機能を提供するだけでなく、アプリケーションのミドルウェアのロジックも提供します。 - -アプリケーション開発者は、全ての機能を満たす単一のステートレスなクラスターとしてVespaシステムを設定することもできますし、タスクの種類に合わせて異なる複数のクラスターを設定することもできます。 - -そしてコンテナー・クラスターは -クエリーやデータ操作の命令を適切なコンテント・クラスターにパスします -— -アプリケーションが保持しないデータである場合には、そのデータを供給する外部サービスと連携することが可能です。 - -</p><p> -Vespaクラスターの <a href="../en/content/elasticity.html"><strong>コンテント</strong>・クラスター</a>は -データ(ドキュメント)を保持して、それらに対する参照、分散された選択/グルーピング/集約のクエリー処理に責任を持ちます。 -コンテント・クラスターはシンプルなkey-valueの配信システムとして機能させることもできますし、構造化・非構造化データに対する複雑な検索を実行したり、関連度のモデルに従って並べ替えて検索結果をグルーピングしたり集約処理を実行したりもできます。 -これらの操作が低レイテンシで機能するよう細心の注意が払われています。それは、結果データを事前に計算することなく、大規模なデータセットに対してエンドユーザー・アプリケーションが直接使えるようにするためです。 - -</p><p> -スケーラビリティを提供するために、コンテント・クラスターは設定された冗長性のレベルを維持するためにバックグラウンドで自動的にデータを再バランスします。到達できないノードに対するフェイル・オーバーも行なうため、柔軟であり、自動リカバリー機能を備えているといえるのです。 -</p><p> -コンテナー・クラスターでの中間処理の後、データはコンテント・クラスターに書き込まれます。 -書き込みは数ミリ秒の後に有効になり、与えられた時間内に成功するか失敗に関する情報を提供するかが保証されており、利用可能なリソースに合わせてスケールされます。 -書き込みはHTTPで直接送信することもできますし、Javaクライアントを使うこともできます — -<a href="../en/reference/api/api.html">APIドキュメンテーション</a>を参照してください。 -</p><p> -Vespaに蓄積されるドキュメントのインスタンスは設定された<a href="../en/basics/schemas.html">スキーマ</a>を持たなければなりません。 -システムにおける各コンテント・クラスターは同時に複数の型のドキュメントを扱うことができます; -アプリケーションは異なる型のデータを異なるコンテント・クラスターに割り当てることもできますし、同じコンテント・クラスターに複数のデータ型を割り当てることもできます。 -</p><p> -コンテナー・クラスターとコンテント・クラスターはVespaのすべてのエンドユーザーのトラフィックを処理しますが、3番目のタイプのクラスタがあります。それは<a href="../en/application-packages.html"><strong>admin</strong>とconfigクラスター</a>で、これは他のクラスターを管理してシステムの設定変更のリクエストを扱います。 -</p><p> -Vespaアプリケーションは<a href="../en/application-packages.html"><em>アプリケーション・パッケージ</em></a>で完全に記述されますが、それはシステムの一部として稼働するクラスターに関する宣言、コンテントのスキーマ、アプリケーションで必要なJavaコンポーネントやその他の設定、データ・ファイルなどを含むディレクトリです。 -アプリケーションの所有者はアプリケーション・パッケージを単一のadminクラスターに<em>デプロイする</em>ことで稼働させることができ、また同じ手順で稼働中のアプリケーションに変更を加えることができます。 -アプリケーション設定の管理に加えて、adminクラスターはシステムのすべてのノードからリアルタイムでログを収集します。 -ノードにVespaがインストールされて起動されると、それはシステム全体が単一のユニットとして扱えるようにadminシステムによって管理されるようになり、そしてアプリケーションの所有者はシステムのノードのローカルで管理タスクを実行する必要はありません。 -</p><p> -ドキュメントの残りでVespaが行なう機能の詳細について説明します。 -</p> - - - -<h2 id="vespa-operations">Vespaのオペレーション</h2> -<p> -Vespaは以下のオペレーションを受け付けます: -</p> -<ul> - <li>書き込み: ドキュメントの設置(追加と置き換え)と削除、それらのフィールドの更新。</li> - <li>IDによるドキュメント(または、そのサブセット)の参照。</li> - <li><a href="../en/querying/query-language.html"><em>選択</em></a>; マッチしたドキュメントは<a href="../en/reference/querying/sorting-language.html"><em>ソーティング</em></a>したり、<a href="../en/basics/ranking.html"><em>ランキング</em></a>したり、<a href="../en/querying/grouping.html"><em>グルーピング</em></a>することができます。 - 検索結果のランキングは<em><a href="../en/reference/ranking/ranking-expressions.html">ランキング式</a></em>に従って実行されます。シンプルな数学関数や複雑なビジネスロジック、機械学習の検索ランキングモデルを使うことができます。 - グルーピングは各グループがグループ内のデータの集約した値を含むことができるような階層的なグループの集合で、フィールドの値を使って行なわれます。 - - グルーピングは値を計算するために集約処理と組み合わせることができます。例えば: ナビゲーション補助、タグ・クラウド、グラフ、クラスタリング — すべては分散して処理され、大規模データセットで法外な計算コストになってしまうような、コンテナー・クラスターに全データを送り返すようなことはありません。</li> - <li>データのダンプ: <a href="../en/writing/visiting.html"><em>visit</em></a>オペレーションを使うことで、条件に一致するコンテントをストリーム出力することができます。これはバックグラウンドの再処理やバックアップといったことに使うことができます。</li> - <li><a href="../en/reference/applications/components.html">その他のカスタムのネットワーク・リクエスト</a> - はコンテナー・クラスターにデプロイされたアプリケーション・コンポーネントで処理することができます。</li> -</ul> -<p> -これらのオペレーションで開発者はリッチな機能のアプリケーションを構築することができます。 -それは選択やキーワード検索、オーガナイズやコンテントの処理が宣言的なクエリーで表現可能な蓄積されたコンテントで稼働するようなJavaのミドルウェア・ロジックとして記述されます。 -</p> - - - -<h2 id="the-stateless-container">ステートレス・コンテナー</h2> -<p> -<a href="../en/jdisc/">コンテナー・クラスター</a>は上記に掲載されたオペレーションと、それらの返却データの処理に従事するアプリケーション・コンポーネントをホスティングしています。 - -Vespaはコンポーネントのインフラとともに、すぐに使えるコンポーネント群を提供しています: -adminサーバまたはアプリケーション・パッケージからの設定の注入のサポートが追加された<a href="https://github.com/google/guice">Guice</a>の上に構築された依存関係の注入; -OSGiをベースとしたコンポーント・モデル; -メトリクスやロギングと同様にモジュール性のためハンドラのチェーンにコンポーネントを連結できる共有されたメカニズム。 -さらにコンテナーはリモート・リクエストを処理したり発行できるネットワーク・レイヤーを提供しています - -HTTPはすぐに使えますし、その他のプロトコル/トランスポートはコンポーネントとして透過的にプラグインできます。 -<p> -開発者はアプリケーション・パッケージを単に再デプロイするだけでコンポーネント群に変更を(もちろんその設定も)加えることができます - システムはその場でリクエスト処理に影響を与えずに、クラスターのノードに対するコピー、コンポーネントのロード/アンロードを管理します。 -</p> - - - -<h2 id="content_clusters">コンテント・クラスター</h2> -<p> -<a href="../en/content/elasticity.html">コンテント・クラスター</a> はデータを確実に保存して、検索と選択のためにデータの分散インデックスを維持します。クラスターがノードやディスクの喪失に対して自動で修復できるよう、データはアプリケーションで指定されたコピー数に応じて複数ノード間でレプリケーションされます。同じメカニズムを使って、クラスターは拡大したり縮小したりすることもできます。それはアプリケーション・パッケージで宣言された利用可能なノードの集合を単に変更するだけです。 -</p><p> -個々のドキュメントの参照はそのドキュメントを保持するノードに直接的にルーティングされ、クエリーは対象ドキュメントを保持する一部のノードに分散されます。 -複雑なクエリーはコンテナーとコンテント・ノードの間で複数ステップにまたがる分散アルゴリズムで処理されます; -これはVespaのデザインのゴールのひとつである低レイテンシーを実現するためのものです。 -</p> - - - -<h2 id="administration_and_developer_support">管理と開発者のサポート</h2> -<p> -<a href="../en/application-packages.html">単一のadmin・configクラスター</a>がシステムの他のクラスターを制御します。 -アプリケーション開発者が詳細を気にせずに希望するシステムの高レベルな宣言ができるよう、 -プロセスとコンポーネントの実体を含む個々のクラスターの低レベルな設定が導かれます。 - -アプリケーション・パッケージが再デプロイされるといつも、システムは設定の必要な変更を計算して、これらが分散されたコンポーネントにプッシュされます。 -効率のため、変更されたコンポーネントとデータ・ファイルはBitTorrentで配布されます。 -</p><p> -アプリケーション・パッケージはHTTP REST APIか<a href="/en/basics/applications.html#deploying-applications">コマンドライン・インタフェース</a>で<a href="../en/reference/api/deploy-v2.html">変更したり、再デプロイしたり</a>、<a href="../en/reference/api/config-v2.html">検証する</a>ことができます。 - -設定の変更を唯一で一貫性のあるものにするため、また単一障害点を持つことを避けるため、管理クラスターは<a href="https://zookeeper.apache.org/">ZooKeeper</a>の上で稼働します。 -</p><p> -数百ノードで構成される大規模システムも、すべてのサービスを稼働させている単一ノードも、アプリケーションパッケージは同じように見え、またデプロイも同じ方法で行われます。 -唯一の必要な変更はクラスターを構成するノードのリストです。 -コンテナー・クラスターはメソッド呼び出しでアプリケーション・パッケージを「デプロイ」することで、単一のJava VMの中で起動することもできます。 -これはIDEやユニット・テストの中でアプリケーションをテストするのに便利です。 -コンポーネントを含むアプリケーション・パッケージは、サンプル・アプリケーションを始めとして、Mavenを使ったIDEで<a href="../en/applications/developer-guide.html">開発する</a>ことができます。 -</p> - -<h2 id="summary">サマリー</h2> -<p> -Vespaを使うことで、低レベルの複雑さに開発者が悩むことなく、スケーラブルで高い基準に従う、リッチに機能して高い可用性のあるアプリケーションを構築することができます。 - -開発者は時間の経過とともに、システムをオフラインにすることなくアプリケーションを進化させ、また成長させることができます。 -そして、データを陳腐化させてパーソナライズできなくするような、複雑なデータやページの事前計算を避けることができます。なぜならそれは、同時かつ定期的に変化するユーザーのデータに対する複雑なクエリーをしばしば必要とするからです。 -</p> diff --git a/ja/overview.mdx b/ja/overview.mdx new file mode 100644 index 0000000000..80982c1f86 --- /dev/null +++ b/ja/overview.mdx @@ -0,0 +1,92 @@ +--- +title: "Vespaの概要" +--- + +Vespaはスケーラブルで低レイテンシな、ステートフルあるいはステートレスなバックエンドサービスを簡単に開発して稼働させることができるプラットフォームです。 +このドキュメントではプラットフォームの機能と主なコンポーネントの概要を説明します。 + + + +## イントロダクション + +Vespaを使うことで、レイテンシや信頼性を犠牲にせずに、大規模データや高負荷に耐えるバックエンドあるいはミドルウェアシステムを構築することができます。 +Vespaのインスタンスはいくつかの<em>ステートレスなJavaコンテナー・クラスター</em>と、データを保持する0個以上の<em>コンテント</em>・クラスターで構成されます。 + +<Frame> +![Vespa Overview](/assets/img/vespa-overview.svg) +</Frame> +[ステートレスな **コンテナー**・クラスター](/en/applications/containers)は、入力データと、リクエスト/クエリーとそのレスポンスの両方を処理するコンポーネントをホスティングします。 +これらのコンポーネントは(インデックス構築やクエリー実行の全ステージといった)プラットフォームに関する機能を提供するだけでなく、アプリケーションのミドルウェアのロジックも提供します。 + +アプリケーション開発者は、全ての機能を満たす単一のステートレスなクラスターとしてVespaシステムを設定することもできますし、タスクの種類に合わせて異なる複数のクラスターを設定することもできます。 + +そしてコンテナー・クラスターは +クエリーやデータ操作の命令を適切なコンテント・クラスターにパスします +— +アプリケーションが保持しないデータである場合には、そのデータを供給する外部サービスと連携することが可能です。 + +Vespaクラスターの [**コンテント**・クラスター](/en/content/elasticity)は +データ(ドキュメント)を保持して、それらに対する参照、分散された選択/グルーピング/集約のクエリー処理に責任を持ちます。 +コンテント・クラスターはシンプルなkey-valueの配信システムとして機能させることもできますし、構造化・非構造化データに対する複雑な検索を実行したり、関連度のモデルに従って並べ替えて検索結果をグルーピングしたり集約処理を実行したりもできます。 +これらの操作が低レイテンシで機能するよう細心の注意が払われています。それは、結果データを事前に計算することなく、大規模なデータセットに対してエンドユーザー・アプリケーションが直接使えるようにするためです。 + +スケーラビリティを提供するために、コンテント・クラスターは設定された冗長性のレベルを維持するためにバックグラウンドで自動的にデータを再バランスします。到達できないノードに対するフェイル・オーバーも行なうため、柔軟であり、自動リカバリー機能を備えているといえるのです。 + +コンテナー・クラスターでの中間処理の後、データはコンテント・クラスターに書き込まれます。 +書き込みは数ミリ秒の後に有効になり、与えられた時間内に成功するか失敗に関する情報を提供するかが保証されており、利用可能なリソースに合わせてスケールされます。 +書き込みはHTTPで直接送信することもできますし、Javaクライアントを使うこともできます — +[APIドキュメンテーション](/en/reference/api/api)を参照してください。 + +Vespaに蓄積されるドキュメントのインスタンスは設定された[スキーマ](/en/basics/schemas)を持たなければなりません。 +システムにおける各コンテント・クラスターは同時に複数の型のドキュメントを扱うことができます; +アプリケーションは異なる型のデータを異なるコンテント・クラスターに割り当てることもできますし、同じコンテント・クラスターに複数のデータ型を割り当てることもできます。 + +コンテナー・クラスターとコンテント・クラスターはVespaのすべてのエンドユーザーのトラフィックを処理しますが、3番目のタイプのクラスタがあります。それは<a href="/en/basics/applications"><strong>admin</strong>とconfigクラスター</a>で、これは他のクラスターを管理してシステムの設定変更のリクエストを扱います。 + +Vespaアプリケーションは<a href="/en/basics/applications"><em>アプリケーション・パッケージ</em></a>で完全に記述されますが、それはシステムの一部として稼働するクラスターに関する宣言、コンテントのスキーマ、アプリケーションで必要なJavaコンポーネントやその他の設定、データ・ファイルなどを含むディレクトリです。 +アプリケーションの所有者はアプリケーション・パッケージを単一のadminクラスターに<em>デプロイする</em>ことで稼働させることができ、また同じ手順で稼働中のアプリケーションに変更を加えることができます。 +アプリケーション設定の管理に加えて、adminクラスターはシステムのすべてのノードからリアルタイムでログを収集します。 +ノードにVespaがインストールされて起動されると、それはシステム全体が単一のユニットとして扱えるようにadminシステムによって管理されるようになり、そしてアプリケーションの所有者はシステムのノードのローカルで管理タスクを実行する必要はありません。 + +ドキュメントの残りでVespaが行なう機能の詳細について説明します。 + + + +## Vespaのオペレーション + +Vespaは以下のオペレーションを受け付けます: + +Vespaは以下のオペレーションを受け付けます: + +- 書き込み: ドキュメントの設置(追加と置き換え)と削除、それらのフィールドの更新。 +- IDによるドキュメント(または、そのサブセット)の参照。 +- [*選択*](/en/querying/query-language); マッチしたドキュメントは[*ソーティング*](/en/reference/querying/sorting-language)したり、[*ランキング*](/en/basics/ranking)したり、[*グルーピング*](/en/querying/grouping)することができます。 検索結果のランキングは*[ランキング式](/en/reference/ranking/ranking-expressions)*に従って実行されます。シンプルな数学関数や複雑なビジネスロジック、機械学習の検索ランキングモデルを使うことができます。 グルーピングは各グループがグループ内のデータの集約した値を含むことができるような階層的なグループの集合で、フィールドの値を使って行なわれます。 グルーピングは値を計算するために集約処理と組み合わせることができます。例えば: ナビゲーション補助、タグ・クラウド、グラフ、クラスタリング — すべては分散して処理され、大規模データセットで法外な計算コストになってしまうような、コンテナー・クラスターに全データを送り返すようなことはありません。 +- データのダンプ: [*visit*](/en/writing/visiting)オペレーションを使うことで、条件に一致するコンテントをストリーム出力することができます。これはバックグラウンドの再処理やバックアップといったことに使うことができます。 +- [その他のカスタムのネットワーク・リクエスト](/en/reference/applications/components) はコンテナー・クラスターにデプロイされたアプリケーション・コンポーネントで処理することができます。 + +これらのオペレーションで開発者はリッチな機能のアプリケーションを構築することができます。 それは選択やキーワード検索、オーガナイズやコンテントの処理が宣言的なクエリーで表現可能な蓄積されたコンテントで稼働するようなJavaのミドルウェア・ロジックとして記述されます。 + + +## ステートレス・コンテナー + +[コンテナー・クラスター](/en/applications/containers)は上記に掲載されたオペレーションと、それらの返却データの処理に従事するアプリケーション・コンポーネントをホスティングしています。 Vespaはコンポーネントのインフラとともに、すぐに使えるコンポーネント群を提供しています: adminサーバまたはアプリケーション・パッケージからの設定の注入のサポートが追加された[Guice](https://github.com/google/guice)の上に構築された依存関係の注入; OSGiをベースとしたコンポーント・モデル; メトリクスやロギングと同様にモジュール性のためハンドラのチェーンにコンポーネントを連結できる共有されたメカニズム。 さらにコンテナーはリモート・リクエストを処理したり発行できるネットワーク・レイヤーを提供しています - HTTPはすぐに使えますし、その他のプロトコル/トランスポートはコンポーネントとして透過的にプラグインできます。 + +開発者はアプリケーション・パッケージを単に再デプロイするだけでコンポーネント群に変更を(もちろんその設定も)加えることができます - システムはその場でリクエスト処理に影響を与えずに、クラスターのノードに対するコピー、コンポーネントのロード/アンロードを管理します。 + +## コンテント・クラスター + +[コンテント・クラスター](/en/content/elasticity) はデータを確実に保存して、検索と選択のためにデータの分散インデックスを維持します。クラスターがノードやディスクの喪失に対して自動で修復できるよう、データはアプリケーションで指定されたコピー数に応じて複数ノード間でレプリケーションされます。同じメカニズムを使って、クラスターは拡大したり縮小したりすることもできます。それはアプリケーション・パッケージで宣言された利用可能なノードの集合を単に変更するだけです。 + +個々のドキュメントの参照はそのドキュメントを保持するノードに直接的にルーティングされ、クエリーは対象ドキュメントを保持する一部のノードに分散されます。 複雑なクエリーはコンテナーとコンテント・ノードの間で複数ステップにまたがる分散アルゴリズムで処理されます; これはVespaのデザインのゴールのひとつである低レイテンシーを実現するためのものです。 + +## 管理と開発者のサポート + +[単一のadmin・configクラスター](/en/basics/applications)がシステムの他のクラスターを制御します。 アプリケーション開発者が詳細を気にせずに希望するシステムの高レベルな宣言ができるよう、 プロセスとコンポーネントの実体を含む個々のクラスターの低レベルな設定が導かれます。 アプリケーション・パッケージが再デプロイされるといつも、システムは設定の必要な変更を計算して、これらが分散されたコンポーネントにプッシュされます。 効率のため、変更されたコンポーネントとデータ・ファイルはBitTorrentで配布されます。 + +アプリケーション・パッケージはHTTP REST APIか[コマンドライン・インタフェース](/en/basics/applications#deploying-applications)で[変更したり、再デプロイしたり](/en/reference/api/deploy-v2)、[検証する](/en/reference/api/config-v2)ことができます。 設定の変更を唯一で一貫性のあるものにするため、また単一障害点を持つことを避けるため、管理クラスターは[ZooKeeper](https://zookeeper.apache.org/)の上で稼働します。 + +数百ノードで構成される大規模システムも、すべてのサービスを稼働させている単一ノードも、アプリケーションパッケージは同じように見え、またデプロイも同じ方法で行われます。 唯一の必要な変更はクラスターを構成するノードのリストです。 コンテナー・クラスターはメソッド呼び出しでアプリケーション・パッケージを「デプロイ」することで、単一のJava VMの中で起動することもできます。 これはIDEやユニット・テストの中でアプリケーションをテストするのに便利です。 コンポーネントを含むアプリケーション・パッケージは、サンプル・アプリケーションを始めとして、Mavenを使ったIDEで[開発する](/en/applications/developer-guide)ことができます。 + +## サマリー + +Vespaを使うことで、低レベルの複雑さに開発者が悩むことなく、スケーラブルで高い基準に従う、リッチに機能して高い可用性のあるアプリケーションを構築することができます。 開発者は時間の経過とともに、システムをオフラインにすることなくアプリケーションを進化させ、また成長させることができます。 そして、データを陳腐化させてパーソナライズできなくするような、複雑なデータやページの事前計算を避けることができます。なぜならそれは、同時かつ定期的に変化するユーザーのデータに対する複雑なクエリーをしばしば必要とするからです。 \ No newline at end of file diff --git a/ja/vespa-quick-start.html b/ja/vespa-quick-start.html deleted file mode 100644 index 861a186d37..0000000000 --- a/ja/vespa-quick-start.html +++ /dev/null @@ -1,125 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "Dockerを使ったVespaのクイック・スタート" ---- - -<p> -このガイドではDockerを使って1台のマシン上にVespaをインストールして起動する方法を説明します。 -</p> -<p><strong>必要条件</strong>:</p> -<ul> - <li><a href="https://docs.docker.com/engine/install/">Docker</a>がインストールされていること。</li> - <li><a href="https://git-scm.com/downloads">Git</a>がインストールされていること。</li> - <li>オペレーティング・システム: macOSまたはLinux</li> - <li>アーキテクチャ: x86_64</li> - <li>少なくとも2GBのメモリがコンテナのインスタンスに割り当てられていること。</li> -</ul> -<ol> -<li> - <strong><a href="https://github.com/vespa-engine/sample-apps">GitHub</a>からVespaのサンプル・アプリケーションをcloneする:</strong> -<pre data-test="exec"> -$ git clone https://github.com/vespa-engine/sample-apps.git -$ export VESPA_SAMPLE_APPS=`pwd`/sample-apps -</pre> -</li> - -<li> - <strong>VespaのDockerコンテナを起動する:</strong> -<pre data-test="exec"> -$ docker run --detach --name vespa --hostname vespa-container --privileged \ - --volume $VESPA_SAMPLE_APPS:/vespa-sample-apps --publish 8080:8080 vespaengine/vespa -</pre> -<p> - <code>volume</code>オプションで、事前にダウンロードしたソースコードにDockerコンテナ内の<code>/vespa-sample-apps</code>としてアクセスできるようになります。 - 検索やフィード用のインタフェースにアクセスできるように、Dockerコンテナの外に<code>8080</code>ポートを公開します。 - <code>vespa</code>の名前で同時に稼働できるDockerコンテナは1つまでです。必要あらば変更してください。 -</p> -<p> - 上記のコマンドの具体的なステップに興味がある場合は、<a href="https://github.com/vespa-engine/docker-image/blob/master/Dockerfile">Dockerfile</a> と<a href="https://github.com/vespa-engine/docker-image/blob/master/include/start-container.sh">起動スクリプト</a>を参照してください。 -</p> -</li> - -<li> - <strong>設定サーバが起動するのを待つ - 200 OKのレスポンスを待つ:</strong> -<pre data-test="exec" data-test-wait-for="200 OK"> -$ docker exec vespa bash -c 'curl -s --head http://localhost:19071/ApplicationStatus' -</pre> -</li> - -<li> - <strong>サンプル・アプリケーションをデプロイしてアクティベートする:</strong> -<pre data-test="exec"> -$ docker exec vespa bash -c 'vespa-deploy prepare /vespa-sample-apps/basic-search/src/main/application/ && \ - vespa-deploy activate' -</pre> - さらなるサンプル・アプリケーションは<a href="https://github.com/vespa-engine/sample-apps/tree/master">sample-apps</a>で見つけることができます。 - <a href="../en/application-packages.html">アプリケーション・パッケージ</a>のアプリケーションの項目を参照してください。 -</li> - -<li> - <strong>アプリケーションがアクティブであることを確認する - 200 OKのレスポンスを待つ:</strong> -<pre data-test="exec" data-test-wait-for="200 OK"> -$ curl -s --head http://localhost:8080/ApplicationStatus -</pre> -</li> - -<li> - <strong>ドキュメントをフィードする:</strong> -<pre data-test="exec"> -$ curl -s -H "Content-Type:application/json" --data-binary @${VESPA_SAMPLE_APPS}/basic-search/music-data-1.json \ - http://localhost:8080/document/v1/music/music/docid/1 | python -m json.tool -$ curl -s -H "Content-Type:application/json" --data-binary @${VESPA_SAMPLE_APPS}/basic-search/music-data-2.json \ - http://localhost:8080/document/v1/music/music/docid/2 | python -m json.tool -</pre> - この例では<a href="../en/reference/api/document-v1.html">ドキュメントAPI</a>を使っています。 - 大規模なデータを高速にフィードするには<a href="../en/clients/vespa-feed-client.html">Java Feeding API</a>を使ってください。 -</li> - -<li> - <strong>クエリーとドキュメント取得リクエストを実行する:</strong> -<pre data-test="exec" data-test-assert-contains="Michael Jackson"> -$ curl -s http://localhost:8080/search/?query=bad | python -m json.tool -</pre> -<pre data-test="exec" data-test-assert-contains="Eminem"> -$ curl -s http://localhost:8080/document/v1/music/music/docid/2 | python -m json.tool -</pre> - ブラウザで<a href="http://localhost:8080/search/?query=bad" data-proofer-ignore>localhost:8080/search/?query=bad</a>の結果を参照してください。 - 詳しくは<a href="../en/querying/query-api.html">Query API</a>を参照してください。 -</li> - -<li> - <p><strong>終わったらクリーンアップする</strong></p> - <p>必要なくなった稼働中のコンテナを停止する:</p> -<pre data-test="after"> -$ docker stop vespa -</pre> - <p>必要に応じて、停止したコンテナを完全に削除する:</p> -<pre data-test="after"> -$ docker rm vespa -</pre> -</li> - -</ol> - - - -<h2 id="next-steps">次のステップ</h2> -<ul> - <li> - このアプリケーションは完全に機能してプロダクションで使うことができますが、冗長性のために - <a href="../en/operations/self-managed/multinode-systems.html ">ノードを追加</a>した方がいいかもしれません。 - <li> - Vespaアプリケーションにあなた独自のJavaコンポーネントを追加するには、 - <a href="../en/applications/developer-guide.html">アプリケーションの開発</a> - を参照してください。 - <li> - <a href="../en/reference/api/api.html">Vespa API</a>はVespaのインタフェースの理解に役立つでしょう。 - <li> - <a href="https://github.com/vespa-engine/sample-apps/tree/master">サンプル・アプリケーション</a>を眺めてみましょう。 - <li> - <a href="../en/security/securing-your-vespa-installation">Vespaのインストールをセキュア</a>にします。 - <li> - AWSで稼働させるには、<a href="../en/operations/self-managed/multinode-systems.html#aws-ec2">AWS EC2での複数ノードのクイック・スタート</a>または - <a href="../en/operations/self-managed/multinode-systems.html#aws-ecs">AWS ECSでの複数ノードのクイック・スタート</a>を参照してください。 - </li> -</ul> diff --git a/ja/vespa-quick-start.mdx b/ja/vespa-quick-start.mdx new file mode 100644 index 0000000000..050e78e596 --- /dev/null +++ b/ja/vespa-quick-start.mdx @@ -0,0 +1,87 @@ +--- +# Copyright Vespa.ai. All rights reserved. +title: "Dockerを使ったVespaのクイック・スタート" +--- + +このガイドではDockerを使って1台のマシン上にVespaをインストールして起動する方法を説明します。 + +**必要条件**: + +- [Docker](https://docs.docker.com/engine/install/)がインストールされていること。 +- [Git](https://git-scm.com/downloads)がインストールされていること。 +- オペレーティング・システム: macOSまたはLinux +- アーキテクチャ: x86\_64 +- 少なくとも2GBのメモリがコンテナのインスタンスに割り当てられていること。 +1. **[GitHub](https://github.com/vespa-engine/sample-apps)からVespaのサンプル・アプリケーションをcloneする:** + +```bash +$ git clone https://github.com/vespa-engine/sample-apps.git +$ export VESPA\_SAMPLE\_APPS=\`pwd\`/sample-apps +``` + +2. **VespaのDockerコンテナを起動する:** + +```bash +$ docker run --detach --name vespa --hostname vespa-container --privileged \\ +--volume $VESPA\_SAMPLE\_APPS:/vespa-sample-apps --publish 8080:8080 vespaengine/vespa +``` + +`volume`オプションで、事前にダウンロードしたソースコードにDockerコンテナ内の`/vespa-sample-apps`としてアクセスできるようになります。 検索やフィード用のインタフェースにアクセスできるように、Dockerコンテナの外に`8080`ポートを公開します。 `vespa`の名前で同時に稼働できるDockerコンテナは1つまでです。必要あらば変更してください。 + 上記のコマンドの具体的なステップに興味がある場合は、[Dockerfile](https://github.com/vespa-engine/docker-image/blob/master/Dockerfile) と[起動スクリプト](https://github.com/vespa-engine/docker-image/blob/master/include/start-container.sh)を参照してください。 + +3. **設定サーバが起動するのを待つ - 200 OKのレスポンスを待つ:** + +```bash +$ docker exec vespa bash -c 'curl -s --head http://localhost:19071/ApplicationStatus' +``` +4. **サンプル・アプリケーションをデプロイしてアクティベートする:** + +```bash +$ docker exec vespa bash -c 'vespa-deploy prepare /vespa-sample-apps/basic-search/src/main/application/ && \\ + vespa-deploy activate' +``` + さらなるサンプル・アプリケーションは[sample-apps](https://github.com/vespa-engine/sample-apps/tree/master)で見つけることができます。 [アプリケーション・パッケージ](/en/basics/applications)のアプリケーションの項目を参照してください。 + +5. **アプリケーションがアクティブであることを確認する - 200 OKのレスポンスを待つ:** + +```bash +$ curl -s --head http://localhost:8080/ApplicationStatus +``` + +6. **ドキュメントをフィードする:**$ curl -s -H "Content-Type:application/json" --data-binary @ + +```bash +$ curl -s -H "Content-Type:application/json" --data-binary @${VESPA_SAMPLE_APPS}/basic-search/music-data-1.json \\ + http://localhost:8080/document/v1/music/music/docid/1 | python -m json.tool + $ curl -s -H "Content-Type:application/json" --data-binary @${VESPA\_SAMPLE\_APPS}/basic-search/music-data-2.json \\ + http://localhost:8080/document/v1/music/music/docid/2 | python -m json.tool +``` + この例では[ドキュメントAPI](/en/reference/api/document-v1)を使っています。 大規模なデータを高速にフィードするには[Java Feeding API](/en/clients/vespa-feed-client)を使ってください。 +7. **クエリーとドキュメント取得リクエストを実行する:** + +```bash +$ curl -s http://localhost:8080/search/?query=bad | python -m json.tool +``` + +```bash + $ curl -s http://localhost:8080/document/v1/music/music/docid/2 | python -m json.tool +``` + + ブラウザで[localhost:8080/search/?query=bad](http://localhost:8080/search/?query=bad)の結果を参照してください。 詳しくは[Query API](../en/querying/query-api.html)を参照してください。 +8. **終わったらクリーンアップする** + 必要なくなった稼働中のコンテナを停止する: +```bash + $ docker stop vespa +``` + 必要に応じて、停止したコンテナを完全に削除する: +```bash + $ docker rm vespa +``` + +## 次のステップ +- このアプリケーションは完全に機能してプロダクションで使うことができますが、冗長性のために [ノードを追加](/en/operations/self-managed/multinode-systems)した方がいいかもしれません。 +- Vespaアプリケーションにあなた独自のJavaコンポーネントを追加するには、 [アプリケーションの開発](/en/applications/developer-guide) を参照してください。 +- [Vespa API](/en/reference/api/api)はVespaのインタフェースの理解に役立つでしょう。 +- [サンプル・アプリケーション](https://github.com/vespa-engine/sample-apps/tree/master)を眺めてみましょう。 +- [Vespaのインストールをセキュア](/en/security/securing-your-vespa-installation)にします。 +- AWSで稼働させるには、[AWS EC2での複数ノードのクイック・スタート](/en/operations/self-managed/multinode-systems#aws-ec2)または [AWS ECSでの複数ノードのクイック・スタート](/en/operations/self-managed/multinode-systems#aws-ecs)を参照してください。 \ No newline at end of file diff --git a/js/handle_results.js b/js/handle_results.js deleted file mode 100644 index d5b37b6ce6..0000000000 --- a/js/handle_results.js +++ /dev/null @@ -1,121 +0,0 @@ - -const unescapeMap = Object.freeze({ - '&' : '&', - '<' : '<', - '>' : '>', - '"': '"', - ''' : "'", - '/': '/', - '`': '`', - '=': '=' -}); -const unescapeHtml = (string) => String(string) - .replace(/&|<|>|"|'|/|`|=/g, (s) => unescapeMap[s]); - - -const handleResults = (data, query) => { - const result = document.getElementById("result"); - result.innerHTML = ""; - - let searchParams = new URLSearchParams(); - - let a = document.getElementById("stackoverflow-query") - searchParams.set('q', '[vespa] ' + unescapeHtml(query)); - a.href = 'https://stackoverflow.com/search?' + searchParams.toString(); - a.innerHTML = "[vespa] " + query; - - a = document.getElementById("gh-issues-query") - searchParams.set('q', 'is:issue ' + unescapeHtml(query)); - a.href = 'https://github.com/vespa-engine/vespa/issues?' + searchParams.toString(); - a.innerHTML = "is:issue " + query; - - // Dirty hack from https://stackoverflow.com/questions/51541986/a-way-to-open-up-slack-search-ui-in-a-browser-from-a-url - a = document.getElementById("slack-query") - const slackQuery = "in:#troubleshooting in:#general " + unescapeHtml(query); - const slackJSON = JSON.stringify({ - d:encodeURIComponent(slackQuery), - r:encodeURIComponent(slackQuery)}); - a.href = 'https://app.slack.com/client/T01RFPFNAHX/search/search-' + btoa(slackJSON); - a.innerHTML = "in:#troubleshooting in:#general " + query; - - const hits = data; - if (hits && hits.length > 0) { - const unorderedList = document.createElement("ul"); - unorderedList.className = "search-result-list"; - result.appendChild(unorderedList); - - const baseURL = { - open: "http://docs.vespa.ai", - cloud: "https://cloud.vespa.ai", - blog: "https://blog.vespa.ai", - vespaai: "https://vespa.ai", - vespaapps: "https://github.com/vespa-engine/sample-apps/tree/master", - pyvespa: "https://vespa-engine.github.io/pyvespa", - }; - - const sourceName = { - open: "Documentation", - cloud: "Cloud", - blog: "Blog", - vespaai: "Vespa.ai", - vespaapps: "Vespa Sample Apps", - pyvespa: "pyvespa", - }; - - const highlightWeight = 10; - - - hits.forEach( - ({ - fields: { - namespace, - path, - content, - title, - summaryfeatures, - }, - }) => { - const modifiedURL = baseURL[namespace] + path; - - const modifiedContent = - typeof content == "undefined" - ? "" - : content - .replace(/<sep \/>/g, " ... ") - .replace(/<hi>/g, "<mark>") - .replace(/<\/hi>/g, "</mark>"); - - const modifiedTitle = - typeof title == "undefined" - ? "No title" - : title - .replace(/<hi>/g, "<mark>") - .replace(/<\/hi>/g, "</mark>"); - - const listItem = document.createElement("li"); - listItem.className = "search-result-item"; - - const header = document.createElement("h4"); - header.innerHTML = `<a href="${modifiedURL}">${modifiedTitle}</a>`; - - const paragraph = document.createElement("p"); - paragraph.innerHTML = modifiedContent; - const paragraphBreak = document.createElement("br"); - paragraph.appendChild(paragraphBreak); - const paragraphSmall = document.createElement("small"); - paragraphSmall.className = "search-result-link"; - paragraphSmall.innerHTML = `${sourceName[namespace]}: ${path}`; - paragraph.appendChild(paragraphSmall); - - listItem.appendChild(header); - listItem.appendChild(paragraph); - - unorderedList.appendChild(listItem); - } - ); - } else { - document.getElementById("hits").innerHTML = "No hits found!"; - } -}; - -export default handleResults; diff --git a/js/handle_search_suggestions.js b/js/handle_search_suggestions.js deleted file mode 100644 index ec3160e040..0000000000 --- a/js/handle_search_suggestions.js +++ /dev/null @@ -1,198 +0,0 @@ -// Function to create and manage search suggestions for a specific input -const createSearchSuggestionHandler = (input) => { - const inputParent = input.parentElement; - const searchForm = input.closest('.search-form'); - const dropdown = document.createElement("div"); - dropdown.className = "dropdown hide"; - inputParent.appendChild(dropdown); - input.setAttribute("autocomplete", "off"); - - const getNextSibling = (node) => { - let sibling = node.nextSibling; - // Skip separator elements - while (sibling && sibling.tagName === 'HR') { - sibling = sibling.nextSibling; - } - return sibling; - } - - const getPreviousSibling = (node) => { - let sibling = node.previousSibling; - // Skip separator elements - while (sibling && sibling.tagName === 'HR') { - sibling = sibling.previousSibling; - } - return sibling; - } - - const getValueFromElement = (element) => { - // For term suggestions, use the data-term attribute (without emoji) - if (element.hasAttribute('data-term')) { - return element.getAttribute('data-term'); - } - // For document suggestions, use the title (without emoji) - if (element.hasAttribute('data-path')) { - return element.innerHTML.replace('📃 ', ''); - } - return element.innerHTML; - } - - //arrow-key logic - let dropdownShow = false; - let firstArrow = false; - const selectedID = `selectedDropdownElement-${input.id}`; - let writtenInput = ""; - - const handleArrowKeys = (event) => { - if (dropdownShow){ - let newSelected; - let selected; - if (event.key === "ArrowDown" && event.target.value) { - event.preventDefault(); - if (firstArrow) { - writtenInput = input.value; - newSelected = dropdown.firstChild; - firstArrow = false; - } else { - selected = document.getElementById(selectedID); - selected.id = ""; - newSelected = getNextSibling(selected); - } - if (newSelected) { - newSelected.id = selectedID; - input.value = getValueFromElement(newSelected); - } else { - firstArrow = true; - input.value = writtenInput; - } - } else if (event.key === "ArrowUp" && event.target.value){ - event.preventDefault(); - if (firstArrow) { - writtenInput = input.value; - newSelected = dropdown.lastChild; - firstArrow = false; - } else { - selected = document.getElementById(selectedID); - selected.id = ""; - newSelected = getPreviousSibling(selected); - } - if (newSelected) { - newSelected.id = selectedID; - input.value = getValueFromElement(newSelected); - } else { - firstArrow = true; - input.value = writtenInput; - } - } else if (event.key === "Enter" && !firstArrow) { - // If a document suggestion is selected, navigate to it - selected = document.getElementById(selectedID); - if (selected && selected.hasAttribute('data-path')) { - event.preventDefault(); - window.location.href = selected.getAttribute('data-path'); - } - // Otherwise let the form submit normally for term suggestions - } - } - } - - const hideDropdown = () => { - dropdown.classList.remove("show"); - dropdown.classList.add("hide"); - dropdownShow = false; - }; - - const showDropdown = () => { - dropdown.classList.add("show"); - dropdown.classList.remove("hide"); - dropdownShow = true; - }; - - const handleSuggestClick = (e) => { - e.preventDefault(); - e.stopPropagation(); - input.value = e.target.getAttribute('data-term') || e.target.innerHTML; - const submitButton = searchForm.querySelector('.search-submit'); - if (submitButton) { - submitButton.click(); - } - }; - - const handleUnfocus = (e) => hideDropdown(); - - const handleDocClick = (e, path) => { - e.preventDefault(); - e.stopPropagation(); - window.location.href = path; - }; - - const handleSuggestionResults = (docData, termData) => { - firstArrow = true; - dropdown.innerHTML = ""; - - const hasDocResults = docData && docData.length > 0; - const hasTermResults = termData && termData.length > 0; - - if (hasDocResults || hasTermResults) { - // Add document results - if (hasDocResults) { - docData.forEach((child) => { - const p = document.createElement("p"); - p.className = "suggestion-doc"; - p.innerHTML = `📃 ${child.fields.title}`; - p.setAttribute("data-path", child.fields.path); - p.addEventListener("mousedown", (e) => handleDocClick(e, child.fields.path)); - dropdown.appendChild(p); - }); - } - - // Add separator if both types exist - if (hasDocResults && hasTermResults) { - const separator = document.createElement("hr"); - separator.className = "suggestion-separator"; - dropdown.appendChild(separator); - } - - // Add term results - if (hasTermResults) { - termData.forEach((child) => { - const p = document.createElement("p"); - p.className = "suggestion-term"; - p.innerHTML = `🔍 ${child.fields.term}`; - p.setAttribute("data-term", child.fields.term); - p.addEventListener("mousedown", handleSuggestClick); - dropdown.appendChild(p); - }); - } - - showDropdown(); - if (input.value.length <= 0) { hideDropdown();} - } else { - hideDropdown(); - } - }; - - return { - handleSuggestionResults, - handleUnfocus, - hideDropdown, - handleArrowKeys - }; -}; - -// Initialize handlers for all search inputs -const handlers = new Map(); - -document.addEventListener("DOMContentLoaded", () => { - const inputs = document.querySelectorAll('.searchinput'); - inputs.forEach(input => { - const handler = createSearchSuggestionHandler(input); - handlers.set(input.id, handler); - }); -}); - -// Export a function that finds the right handler for the current input -const getHandlerForInput = (inputId) => { - return handlers.get(inputId); -}; - -export { getHandlerForInput }; diff --git a/js/header_links.js b/js/header_links.js deleted file mode 100644 index 46264ad0ba..0000000000 --- a/js/header_links.js +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright Vespa.ai. All rights reserved. - -function generateHeaderLinks() { - for (const tag of ["h2", "h3", "h4"]) { - let headings = document.getElementsByTagName(tag); - for (const heading of headings) { - addLink(heading) - } - } -} - -function addLink(heading) { - if (heading.id === null || heading.id.trim() === "") {return;} // Consider auto-generate the id is empty - let i = document.createElement("i"); - i.classList.add("d-icon", "d-link"); - i.style.color = "#303030"; // same as heading - let a = document.createElement("a"); - a.setAttribute("href", "#" + heading.id); - a.classList.add("a-hidden"); - a.appendChild(i); - heading.appendChild(a); -} - -document.addEventListener("DOMContentLoaded", generateHeaderLinks); diff --git a/js/load-mathjax.js b/js/load-mathjax.js deleted file mode 100644 index 72a5ec4ec3..0000000000 --- a/js/load-mathjax.js +++ /dev/null @@ -1,27 +0,0 @@ - -window.MathJax = { - //chtml: { - // scale: 0.75, - // minScale: .5 - //}, - svg: { // http://docs.mathjax.org/en/latest/options/output/svg.html - minScale: .5 - }, - options: { - enableMenu: true, - menuOptions: { - settings: { - zoom: 'Click', // `Click' - zscale: '100%' // Zoom scaling factor - } - } - } -}; - -(function () { - let script = document.createElement('script'); - //script.src = 'https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js'; - script.src = 'https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-svg.js'; - script.async = true; - document.head.appendChild(script); -})(); diff --git a/js/pre_copy.js b/js/pre_copy.js deleted file mode 100644 index 03b310f477..0000000000 --- a/js/pre_copy.js +++ /dev/null @@ -1,33 +0,0 @@ - -function copyPreContent(button) { - // Assume a pre after the copy button - let elem = button; - button.classList.add("d-tooltip-top"); - button.setAttribute("data-tooltip", "copied!"); - setTimeout(() => button.classList.remove("d-tooltip-top"), 800); - setTimeout(() => button.removeAttribute("data-tooltip"), 800); - while (elem = elem.nextSibling) { - if ( elem.nodeType !== Node.TEXT_NODE) { - if (elem.nodeName === "PRE") { - copyToClipboard(removePrompt(elem.innerText).trim()); - } - } - } -} - -function removePrompt(text) { - return text.replace(/^\$\s*/gm, ""); -} - -function copyToClipboard(text) { - // thanks to https://stackoverflow.com/questions/400212/how-do-i-copy-to-the-clipboard-in-javascript - if (!navigator.clipboard) { - alert("Copy button not supported in this browser version") - return; - } - navigator.clipboard.writeText(text).then(function() { - console.log('Async: Copying to clipboard was successful!'); - }, function(err) { - console.error('Async: Could not copy text: ', err); - }); -} diff --git a/js/process_pre.js b/js/process_pre.js deleted file mode 100644 index 4f650e8f69..0000000000 --- a/js/process_pre.js +++ /dev/null @@ -1,21 +0,0 @@ - -function processFilePREs() { - const tags = document.getElementsByTagName("pre"); - - // copy elements, because the list above is mutated by the insert html below - let elems = []; - for (let i = 0; i < tags.length; i++) { - elems.push(tags[i]); - } - - for (let i = 0; i < elems.length; i++) { - let elem = elems[i]; - if (elem.getAttribute("data-test") === "file") { - let html = elem.innerHTML; - //elem.innerHTML = html.replace(/<!--\?/g, "<?").replace(/\?-->/g, "?>").replace(/</g, "<").replace(/>/g, ">"); - elem.insertAdjacentHTML("afterend", "<div class=\"filepath\">Paste the above into file " + elem.getAttribute("data-path") + "</div>"); - } - } -}; - -document.addEventListener("DOMContentLoaded", processFilePREs); diff --git a/js/query_examples.js b/js/query_examples.js deleted file mode 100644 index db65bf681b..0000000000 --- a/js/query_examples.js +++ /dev/null @@ -1,21 +0,0 @@ - -function generateQueryExamples () { - let docsearchQueries = document.getElementsByClassName("yql-x"); - for (let anchor of docsearchQueries) { - generateYQL(anchor, "https://api.search.vespa.ai/search/"); - } - let queryStringQueries = document.getElementsByClassName("querystring-x"); - for (let anchor of queryStringQueries) { - generateQuery(anchor, "https://api.search.vespa.ai/search/"); - } -} - -function generateYQL(anchor, endpoint) { - anchor.setAttribute("href", encodeURI(endpoint + "?yql=" + anchor.innerText)); -} - -function generateQuery(anchor, endpoint) { - anchor.setAttribute("href", encodeURI(endpoint + "?" + anchor.innerText)); -} - -document.addEventListener("DOMContentLoaded", generateQueryExamples); diff --git a/js/search.js b/js/search.js deleted file mode 100644 index 53cd29dfe9..0000000000 --- a/js/search.js +++ /dev/null @@ -1,121 +0,0 @@ -import handleResults from "./handle_results.js"; -import { getHandlerForInput } from "./handle_search_suggestions.js"; - -const escapeMap = Object.freeze({ - '&': '&', - '<': '<', - '>': '>', - '"': '"', - "'": ''', - '/': '/', - '`': '`', - '=': '=' -}); -// https://github.com/janl/mustache.js/blob/550d1da9e3f322649d04b4795f5356914f6fd7e8/mustache.js#L71 -const escapeHtml = (string) => String(string).replace(/[&<>"'`=\/]/g, (s) => escapeMap[s]); - - -// https://www.freecodecamp.org/news/javascript-debounce-example/ -const debounce = (func, timeout = 200) => { - let timer; - return (...args) => { - clearTimeout(timer); - timer = setTimeout(() => { - func.apply(this, args); - }, timeout); - }; -}; - -const encode = (params) => { - return new URLSearchParams(params) - .toString() - .replaceAll('+', '%20'); // CloudFront requires that spaces are encoded as %20 -}; - -// Get the active search input (mobile or desktop) -const getActiveSearchInput = () => { - const inputs = document.querySelectorAll('.searchinput'); - for (const input of inputs) { - // Check if input is visible - if (input.offsetParent !== null) { - return input; - } - } - // Fallback to first input if none are visible - return inputs[0]; -}; - -const handleQuery = (query, inputId) => { - const handler = getHandlerForInput(inputId); - - if (query.length > 0) { - const result = document.getElementById("result"); - - document.getElementById("hits").innerHTML = ""; - result.innerHTML = `Searching for '${escapeHtml(query)}' ...`; - fetch("https://api.search.vespa.ai/search/?" + encode({term: query})) - .then((res) => res.json()) - .then((res) => { const children = (res.root.children)? res.root.children : []; - if (handler) { - handler.handleSuggestionResults( - children.filter(child => child.fields.sddocname === "doc"), - children.filter(child => child.fields.sddocname === "term") - ); - } - handleResults(children.filter(child => child.fields.sddocname === "doc"), escapeHtml(query))}) - .catch(console.error); - } else { - document.getElementById("hits").innerHTML = ""; - result.innerHTML = ""; - if (handler) { - handler.hideDropdown(); - } - } -}; - -const handleLocationQuery = () => { - const params = new URLSearchParams(window.location.search); - - if (params.has("q")) { - const query = params.get("q"); - const activeInput = getActiveSearchInput(); - if (activeInput) { - activeInput.value = query; - } - result.innerHTML = `Searching for '${escapeHtml(query)}' ...`; - - const searchParams = { - yql: 'select * from doc where {grammar: "weakAnd"}userInput(@userinput)', - hits: 25, - ranking: 'documentation', - locale: 'en-US', - userinput: query, - }; - - fetch("https://api.search.vespa.ai/search/?" + encode(searchParams)) - .then((res) => res.json()) - .then((res) => handleResults(res.root.children, escapeHtml(query))) - } -}; - -document.addEventListener("DOMContentLoaded", () => { - handleLocationQuery(); - - // Attach event listeners to all search inputs - const inputs = document.querySelectorAll('.searchinput'); - inputs.forEach(input => { - input.addEventListener("input", (event) => { - debounce(handleQuery(event.target.value, event.target.id)); - }); - - input.addEventListener("focusout", (event) => { - const handler = getHandlerForInput(event.target.id); - if (handler) handler.handleUnfocus(event); - }); - - input.addEventListener("keydown", (event) => { - const handler = getHandlerForInput(event.target.id); - if (handler) handler.handleArrowKeys(event); - }); - }); -}); diff --git a/js/search_suggestions.js b/js/search_suggestions.js deleted file mode 100644 index 161970c984..0000000000 --- a/js/search_suggestions.js +++ /dev/null @@ -1,83 +0,0 @@ -import { getHandlerForInput } from "./handle_search_suggestions.js"; - -if (window.location.pathname != "/search.html"){ - -// https://www.freecodecamp.org/news/javascript-debounce-example/ -const debounce = (func, timeout = 200) => { - let timer; - return (...args) => { - clearTimeout(timer); - timer = setTimeout(() => { - func.apply(this, args); - }, timeout); - }; -}; - -const encode = (params) => { - return new URLSearchParams(params) - .toString() - .replaceAll('+', '%20'); // seems like CloudFront requires that spaces are encoded as %20 -}; - -const handleInput = (e) => { - const handler = getHandlerForInput(e.target.id); - if (!handler) return; - - if (e.target.value.length > 0) { - // Sanitize input for search (remove special characters) - const sanitizedInput = e.target.value.replaceAll(/[^a-zA-Z0-9 ]/g, ""); - - // First fetch: Document suggestions using YQL - const docSearchParams = { - yql: 'select * from doc where grams contains (@userinput)', - hits: 5, - ranking: 'weighted_doc_rank', - locale: 'en-US', - userinput: sanitizedInput, - }; - - const docFetch = fetch( - `https://api.search.vespa.ai/search/?${encode(docSearchParams)}` - ).then((res) => res.json()); - - // Second fetch: Term suggestions (prefix matching) - const termSearchParams = { - yql: `select * from sources term where default contains ([{"prefix":true}]"${sanitizedInput}")`, - ranking: 'term_rank', - hits: 5, - }; - - const termFetch = fetch( - `https://api.search.vespa.ai/search/?${encode(termSearchParams)}` - ).then((res) => res.json()); - - // Wait for both fetches - Promise.all([docFetch, termFetch]) - .then(([docRes, termRes]) => { - const docChildren = (docRes.root.children) ? docRes.root.children : []; - const termChildren = (termRes.root.children) ? termRes.root.children : []; - handler.handleSuggestionResults(docChildren, termChildren); - }) - .catch(console.error); - } else { - handler.hideDropdown(); - } -}; - -// Initialize all search inputs -document.addEventListener("DOMContentLoaded", () => { - const inputs = document.querySelectorAll('.searchinput'); - inputs.forEach(input => { - input.addEventListener("input", debounce(handleInput)); - input.addEventListener("focusout", (e) => { - const handler = getHandlerForInput(e.target.id); - if (handler) handler.handleUnfocus(e); - }); - input.addEventListener("keydown", (e) => { - const handler = getHandlerForInput(e.target.id); - if (handler) handler.handleArrowKeys(e); - }); - }); -}); - -} diff --git a/js/table-list-converter.js b/js/table-list-converter.js deleted file mode 100644 index 7883744eac..0000000000 --- a/js/table-list-converter.js +++ /dev/null @@ -1,118 +0,0 @@ -/* Table to list converter wit JQuery */ - -function scrollToAnchor(hash) { - if (hash) { - setTimeout(() => { - const $target = $(hash); - if ($target.length) { - $('html, body').scrollTop($target.offset().top); - } - }, 0); - } -} - -$(window).on('load', function() { - const anchor = window.location.hash; - let $table = $('#rank-feature-table'); - let $list = $('<ul id="feature-list"></ul>'); - - $table.find('tr').each(function() { - let $row = $(this); - - // Check if it's a section heading (h3 inside td) - if ($row.find('td[colspan="3"] h3').length > 0) { - let headingText = $row.find('h3').text().trim(); - let headingId = $row.find('h3').attr('id') || ''; - $list.append('<h3 style="list-style: none; margin-top: 1em;"' + (headingId ? ' id="' + headingId + '"' : '') + '>' + headingText + '</h3>'); - return; - } - - // Skip column headers - if ($row.hasClass('trx') || $row.find('th').length > 0) { - return; - } - - let $tds = $row.find('td'); - if ($tds.length >= 3) { - let featureName = $tds.eq(0).text().trim(); - let defaultValue = $tds.eq(1).text().trim(); - let description = $tds.eq(2).html().trim(); // Keep inner HTML - - let $li = $('<li style="margin-left: 1em"></li>'); - $li.append('<strong>' + featureName + '</strong>'); - $li.append('<div>Default: ' + defaultValue + '</div>'); - $li.append('<div> ' + description + '</div>'); - - $list.append($li); - } - else{ - $list.append('<div>' + $tds.html() + '</div>'); - } - }); - - $table.replaceWith($list); - scrollToAnchor(anchor); -}); - -/* Natve Ranking Tables */ -$(window).on('load', function() { - const anchor = window.location.hash; - let $table = $('#nativerank-variables-table'); - let $list = $('<ul id="variable-list"></ul>'); - - $table.find('tbody tr').each(function() { - let $row = $(this); - let $tds = $row.find('td'); - - if ($tds.length >= 2) { - let variable = $tds.eq(0).html().trim(); // Keep HTML (e.g., <em>, <sub>) - let description = $tds.eq(1).html().trim(); // Keep inner HTML - - let $li = $('<li style="margin-bottom: 1em;"></li>'); - $li.append('<strong>' + variable + '</strong>'); - $li.append('<div>' + description + '</div>'); - - $list.append($li); - } - }); - $table.replaceWith($list); - scrollToAnchor(anchor); - }); - - $(window).on('load', function() { - const anchor = window.location.hash; - let $table = $('#native-rank-parameters-table'); - let $list = $('<ul id="native-rank-parameters-list"></ul>'); - - $table.find('tbody tr').each(function() { - let $row = $(this); - let $tds = $row.find('td'); - - // Skip rows that span all columns (e.g., deprecated notice) - if ($tds.length === 1 && $tds.attr('colspan') === '4') { - let noticeHtml = $tds.html().trim(); - let $li = $('<li style="list-style: none; margin: 1em 0; color: red;"></li>'); - $li.html(noticeHtml); - $list.append($li); - return; - } - - if ($tds.length >= 4) { - let feature = $tds.eq(0).html().trim(); // Feature name (keep HTML) - let parameter = $tds.eq(1).html().trim(); // Parameter (keep HTML) - let defaultValue = $tds.eq(2).text().trim(); // Default (plain text) - let description = $tds.eq(3).html().trim(); // Description (keep HTML) - - let $li = $('<li style="margin-bottom: 1em;"></li>'); - $li.append('<strong>' + feature + ' → ' + parameter + '</strong>'); - $li.append('<div><em>Default:</em> ' + defaultValue + '</div>'); - $li.append('<div>' + description + '</div>'); - - $list.append($li); - } - }); - - $table.replaceWith($list); - scrollToAnchor(anchor); - }); - \ No newline at end of file diff --git a/llms-template.md b/llms-template.md deleted file mode 100644 index 84fb5f2e6c..0000000000 --- a/llms-template.md +++ /dev/null @@ -1,43 +0,0 @@ -# Vespa documentation - -> Vespa is a powerful and scalable engine for low-latency computation over large data sets. It is designed for real-time applications that require a combination of search, recommendation, and personalization. Vespa allows developers to build applications that can handle high volumes of queries and data writes while maintaining fast response times. - -**Key Features** - -Vespa offers a rich set of features for building sophisticated search and recommendation applications: - -* **Real-time Indexing and Search:** Vespa provides low-latency CRUD (Create, Read, Update, Delete) operations, making data searchable in milliseconds after being fed. -* **Approximate Nearest Neighbor (ANN) Search:** Vespa includes a highly efficient implementation of the HNSW algorithm for fast and accurate vector search, which can be combined with traditional filters. -* **Flexible Ranking and Relevance:** Ranking is a first-class citizen in Vespa. It supports complex, multi-phase ranking expressions and can integrate with machine-learned models (e.g., ONNX, TensorFlow, XGBoost, LightGBM) to deliver highly relevant results. -* **Scalability and Elasticity:** Vespa is designed to scale horizontally. Content clusters can be grown or shrunk on the fly without service interruptions, and data is automatically redistributed to maintain a balanced load. -* **Rich Data Modeling:** Vespa supports a variety of data types, including structured data, unstructured text, and tensors for vector embeddings. It also supports parent-child relationships to model complex data hierarchies. -* **Comprehensive Query Language:** Vespa's query language (YQL) allows for a combination of keyword search, structured filtering, and nearest neighbor search in a single query. -* **Component-Based Architecture:** Vespa's container clusters host custom Java components (Searchers, Document Processors) that allow for extensive customization of query and data processing pipelines. - -**Architecture** - -A Vespa application consists of two main types of clusters: - -* **Stateless Container Clusters:** These clusters handle incoming queries and data writes. They host application components that process requests and responses, perform query rewriting, and federate to backend services. -* **Stateful Content Clusters:** These clusters are responsible for storing, indexing, and searching data. They automatically manage data distribution and redundancy. - -Vespa's architecture is designed for high availability and fault tolerance. When a node fails, the system automatically re-routes traffic and re-distributes data to maintain service. - -**Application Package** - -A Vespa application is defined by an **application package**, which contains all the necessary configuration, schemas, components, and machine-learned models. This self-contained package allows for atomic deployments and ensures consistency between code and configuration. - -Key files in an application package include: - -* **`services.xml`**: Defines the services and clusters that make up the application, including their topology and resource allocation. -* **`schemas/*.sd`**: Defines the document types, their fields, and how they should be indexed and searched. Rank profiles are also defined within schemas. - -**APIs and Interfaces** - -Vespa provides a comprehensive set of APIs for interacting with the system: - -* **Document API (`/document/v1/`)**: A REST API for performing CRUD operations on documents. -* **Query API (`/search/`)**: A powerful API for querying data using YQL, with extensive options for ranking, grouping, and presentation. -* **Configuration and Deployment APIs**: REST APIs for deploying application packages and managing system configuration. - -This overview provides a glimpse into the capabilities of the Vespa Search engine. For more in-depth information, please refer to each of the documentation links below. \ No newline at end of file diff --git a/logo/dark.svg b/logo/dark.svg new file mode 100644 index 0000000000..0276516f59 --- /dev/null +++ b/logo/dark.svg @@ -0,0 +1,9 @@ +<svg width="211" height="48" viewBox="0 0 211 48" fill="none" xmlns="http://www.w3.org/2000/svg"> +<path d="M13.5876 32.2135H56.2925L42.7049 45.2039H0V26.6467L13.5876 13.6562V32.2135Z" fill="white"></path> +<path d="M69.8804 0.666016V19.2233L56.2928 32.213V13.6558H13.5879L27.1762 0.666016H69.8804Z" fill="white"></path> +<path d="M95.7499 38.7409L83.4688 7.16113H88.5697L98.5833 34.049L108.691 7.16113H113.698L101.417 38.7409H95.7485H95.7499Z" fill="white"></path> +<path d="M133.678 20.8978C132.765 19.2598 131.475 17.9441 129.805 16.9512C128.136 15.9583 126.137 15.4619 123.807 15.4619C121.477 15.4619 119.429 15.9649 117.665 16.9735C115.901 17.9821 114.525 19.381 113.534 21.1689C112.54 22.9588 112.045 25.0565 112.045 27.4621C112.045 29.8676 112.541 31.8684 113.534 33.6426C114.525 35.4168 115.901 36.8006 117.665 37.7928C119.429 38.7857 121.444 39.2821 123.712 39.2821C125.57 39.2821 127.214 38.9658 128.647 38.3345C130.079 37.7031 131.284 36.8386 132.261 35.7403C133.236 34.642 133.929 33.4022 134.34 32.0184H129.616C129.144 33.101 128.419 33.9589 127.444 34.5903C126.467 35.2216 125.222 35.5379 123.712 35.5379C122.452 35.5379 121.286 35.2439 120.216 34.6577C119.145 34.0716 118.287 33.1999 117.641 32.0413C117.084 31.0432 116.768 29.8467 116.692 28.4543H134.954C134.984 28.0338 135.008 27.65 135.024 27.3042C135.04 26.9584 135.047 26.6349 135.047 26.3343C135.047 24.3492 134.592 22.5384 133.678 20.8971V20.8978ZM116.746 25.2524C116.873 24.1803 117.171 23.2548 117.641 22.4788C118.287 21.4099 119.144 20.5991 120.216 20.0418C121.286 19.4851 122.468 19.2068 123.76 19.2068C125.585 19.2068 127.114 19.7412 128.342 20.8081C129.57 21.8769 130.231 23.357 130.325 25.2524H116.747H116.746Z" fill="white"></path> +<path d="M148.274 39.2821C146.258 39.2821 144.495 38.9665 142.984 38.3345C141.472 37.7031 140.275 36.8164 139.394 35.6729C138.512 34.53 137.977 33.1913 137.788 31.6575H142.606C142.763 32.3792 143.071 33.0414 143.527 33.6426C143.983 34.2445 144.613 34.7252 145.416 35.086C146.219 35.4469 147.171 35.6277 148.274 35.6277C149.313 35.6277 150.171 35.4849 150.848 35.1993C151.525 34.9138 152.021 34.5228 152.336 34.0264C152.651 33.5299 152.809 32.9968 152.809 32.4251C152.809 31.5835 152.596 30.9443 152.171 30.5075C151.746 30.0719 151.115 29.7255 150.282 29.4701C149.447 29.2147 148.447 28.9815 147.282 28.7706C146.179 28.5899 145.117 28.3423 144.094 28.0266C143.071 27.7109 142.157 27.3128 141.355 26.8314C140.552 26.3507 139.914 25.7416 139.441 25.0041C138.969 24.2674 138.733 23.3577 138.733 22.2744C138.733 20.9816 139.094 19.8158 139.819 18.7778C140.543 17.7404 141.574 16.9283 142.913 16.3415C144.251 15.7547 145.833 15.4619 147.66 15.4619C150.305 15.4619 152.431 16.0638 154.036 17.2662C155.642 18.4693 156.587 20.1688 156.871 22.3642H152.289C152.163 21.3418 151.69 20.5526 150.872 19.9959C150.053 19.4399 148.966 19.1616 147.613 19.1616C146.259 19.1616 145.219 19.4177 144.495 19.9285C143.77 20.44 143.408 21.1172 143.408 21.9588C143.408 22.5004 143.613 22.9817 144.022 23.4022C144.431 23.824 145.037 24.177 145.841 24.4625C146.644 24.7487 147.628 25.0114 148.793 25.2517C150.462 25.553 151.958 25.9282 153.28 26.3795C154.603 26.8307 155.657 27.4929 156.445 28.3646C157.232 29.2369 157.626 30.4845 157.626 32.1088C157.657 33.5227 157.288 34.7704 156.516 35.853C155.744 36.9355 154.658 37.7784 153.258 38.3797C151.856 38.9809 150.195 39.2821 148.274 39.2821H148.274Z" fill="white"></path> +<path d="M161.971 48.6662V16.0039H166.222L166.694 19.5228C167.198 18.8312 167.828 18.1769 168.584 17.5606C169.339 16.9443 170.244 16.4407 171.3 16.049C172.354 15.6587 173.591 15.4629 175.007 15.4629C177.274 15.4629 179.266 15.9894 180.983 17.0419C182.699 18.095 184.037 19.5162 184.998 21.3055C185.958 23.0954 186.438 25.1328 186.438 27.4185C186.438 29.7042 185.95 31.7423 184.974 33.5316C183.998 35.3215 182.652 36.7276 180.936 37.7499C179.219 38.7723 177.228 39.2838 174.961 39.2838C173.102 39.2838 171.466 38.9301 170.048 38.2235C168.632 37.5168 167.514 36.5318 166.695 35.2684V48.6675H161.971L161.971 48.6662ZM174.205 35.3575C175.653 35.3575 176.936 35.0268 178.054 34.3653C179.171 33.7038 180.054 32.7712 180.699 31.5681C181.344 30.3657 181.667 28.9668 181.667 27.3727C181.667 25.7786 181.344 24.3882 180.699 23.1995C180.053 22.0115 179.171 21.0789 178.054 20.4023C176.936 19.7258 175.653 19.3872 174.205 19.3872C172.756 19.3872 171.425 19.7258 170.308 20.4023C169.19 21.0789 168.316 22.0115 167.686 23.1995C167.056 24.3882 166.741 25.7786 166.741 27.3727C166.741 28.9668 167.056 30.3657 167.686 31.5681C168.316 32.7712 169.189 33.7038 170.308 34.3653C171.425 35.0274 172.724 35.3575 174.205 35.3575Z" fill="white"></path> +<path d="M198.625 39.2821C196.672 39.2821 195.051 38.9665 193.76 38.3345C192.468 37.7031 191.508 36.8537 190.878 35.7855C190.248 34.718 189.934 33.5679 189.934 32.3341C189.934 30.8303 190.343 29.5447 191.162 28.4766C191.981 27.409 193.146 26.5897 194.657 26.018C196.168 25.4469 197.979 25.1607 200.089 25.1607H206.277C206.277 23.8377 206.071 22.7401 205.663 21.8677C205.253 20.996 204.647 20.3417 203.845 19.9056C203.042 19.47 202.026 19.2513 200.798 19.2513C199.381 19.2513 198.169 19.5748 197.161 20.2212C196.153 20.8683 195.523 21.8225 195.272 23.0859H190.548C190.737 21.4918 191.311 20.1309 192.272 19.0031C193.233 17.8753 194.469 17.0029 195.98 16.3867C197.492 15.7704 199.098 15.4619 200.798 15.4619C203.033 15.4619 204.908 15.8385 206.419 16.5897C207.93 17.3415 209.072 18.4019 209.844 19.77C210.615 21.1388 211.001 22.7702 211.001 24.6649V38.7405H206.891L206.513 34.9059C206.166 35.5078 205.757 36.0789 205.285 36.6205C204.813 37.1621 204.253 37.6285 203.607 38.0188C202.961 38.4098 202.222 38.7182 201.388 38.9435C200.553 39.1688 199.632 39.2821 198.624 39.2821H198.625ZM199.522 35.6277C200.529 35.6277 201.443 35.4325 202.262 35.0409C203.081 34.6505 203.781 34.1161 204.364 33.4396C204.946 32.763 205.387 31.9961 205.686 31.1388C205.985 30.2815 206.15 29.402 206.182 28.4995V28.3639H200.561C199.207 28.3639 198.112 28.5217 197.278 28.8374C196.444 29.1531 195.837 29.5821 195.459 30.123C195.081 30.6647 194.893 31.296 194.893 32.0177C194.893 32.7394 195.074 33.4094 195.436 33.9353C195.798 34.4619 196.325 34.8758 197.019 35.1758C197.711 35.477 198.546 35.627 199.522 35.627L199.522 35.6277Z" fill="white"></path> +</svg> \ No newline at end of file diff --git a/logo/hero-dark.svg b/logo/hero-dark.svg new file mode 100644 index 0000000000..9072d9af19 --- /dev/null +++ b/logo/hero-dark.svg @@ -0,0 +1,29 @@ +<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" id="artwork" x="0px" y="0px" viewBox="0 0 1000 1000" style="enable-background:new 0 0 1000 1000; fill: white;" xml:space="preserve"> +<style type="text/css"> + .st0{fill:#61D790;} +</style> +<path class="st0" d="M939.2,944.4H726.9c-1.3,0-2.7,0-4,0c-7.1,0-14.1-2.2-19.9-6.3c-9.4-6.7-14.1-17.5-14.1-28.9v-308 c0-0.7,0-1.5,0-2.2c0-18.7,15.1-33.8,33.8-33.8c0,0,0,0,0,0c0.9,0,1.9,0,2.8,0h201.1c4.1,0,8.3,0,12.4,0 c17.3,0.1,32.4,13.4,33.8,30.9c0.3,3.2,0.1,6.4,0.1,9.5v297.3c0,2.6,0,5.1,0,7.7C972.8,929.2,957.8,944.2,939.2,944.4 c-2.5,0-2.5,4,0,3.9c20-0.2,36.5-15.9,37.7-35.9c0.2-2.9,0-5.9,0-8.8V605c0.1-4,0-8.1-0.5-12.1c-2.8-18.2-19.1-31.6-37.3-31.7 c-3.9,0-7.8,0-11.7,0H725.3c-1.2,0-2.4,0-3.6,0c-9.6,0.3-18.8,4.2-25.6,11c-7.7,7.7-11.1,17.8-11.1,28.5v308.7 c0,12.5,5.3,24,15.4,31.5c6.5,4.8,14.4,7.3,22.4,7.3c1.2,0,2.4,0,3.6,0h212.7C941.8,948.3,941.8,944.4,939.2,944.4z"/> +<path class="st0" d="M687,647.6h276.3c3.7,0,7.5,0.1,11.2,0c0.2,0,0.3,0,0.5,0c2.5,0,2.5-3.9,0-3.9H698.7c-3.7,0-7.5-0.1-11.2,0 c-0.2,0-0.3,0-0.5,0C684.4,643.6,684.4,647.6,687,647.6L687,647.6z"/> +<path class="st0" d="M687,756.7h276.3c3.7,0,7.5,0.1,11.2,0c0.2,0,0.3,0,0.5,0c2.5,0,2.5-3.9,0-3.9H698.7c-3.7,0-7.5-0.1-11.2,0 c-0.2,0-0.3,0-0.5,0C684.4,752.8,684.4,756.7,687,756.7L687,756.7z"/> +<path class="st0" d="M687,865.9h276.3c3.7,0,7.5,0.1,11.2,0c0.2,0,0.3,0,0.5,0c2.5,0,2.5-3.9,0-3.9H698.7c-3.7,0-7.5-0.1-11.2,0 c-0.2,0-0.3,0-0.5,0C684.4,862,684.4,865.9,687,865.9L687,865.9z"/> +<path d="M258.7,241.1c-7.3-29-14.1-58.2-20.2-87.4c-1.5-7-3.1-14.1-4.2-21.2c-1.1-6.8-1.5-13.9,0.4-20.7 c3.1-11.3,12.5-19.1,22.2-24.8c11.2-6.6,24-12.3,37.1-12.8c11.1-0.4,23.3,3.4,29.9,12.9c6.2,8.8,7.2,20.1,9.6,30.3 c1,5.3,2.8,10.4,5.3,15.2c1.3,2.4,2.9,4.7,4.8,6.7c1.8,2,4.1,4.1,3.4,7c-0.3,1.6-1,3.1-1.5,4.7c-0.4,1.8-0.8,3.6-0.9,5.4 c-0.3,3.4-0.2,6.9,0.5,10.3c1.2,6.6,4.7,12.3,8.2,17.9c3.3,5.4,6.9,11.1,7.2,17.6c0.3,5.3-2.2,10.6-7.4,12.5c-2,0.8-4.2,0.7-6.1-0.2 c-1-0.5-2.2-0.2-2.7,0.8c-0.2,0.3-0.2,0.6-0.2,0.9c-0.8,8.9-8.7,15.4-17.6,14.6c-4.2-0.4-8-2.4-10.7-5.5c-0.6-0.6-1.6-0.8-2.4-0.3 c-8.1,6.2-20.2,1.8-25.7-6c-7.5-10.8-6.3-24.8-5.2-37.2c0.7-7.2,1.4-14.5,0.6-21.8c-0.7-6.2-2.3-12.9-7-17.4c-4.1-4-11-5.8-16.1-2.5 c-4.5,2.9-5.9,8.1-6,13.1c-0.2,5.5,1.4,11,0.4,16.4c-0.7,4.2-2.8,9.7-7.9,9.4c-2.5-0.1-2.5,3.8,0,3.9c5.1,0.3,8.6-3.4,10.4-7.8 c1.9-4.7,1.8-9.5,1.3-14.4c-0.5-4.6-1.1-10.2,1.2-14.5c2.6-4.8,8.7-4.8,12.8-1.8c5,3.8,6.4,10.9,7,16.7c0.7,7.5-0.2,15.1-0.9,22.5 c-1.1,12.5-1.6,26.5,5.9,37.2c6.5,9.4,21.1,15,31.1,7.4l-2.4-0.3c7.4,8.3,20.1,9.1,28.4,1.7c3.9-3.5,6.3-8.3,6.7-13.5l-3,1.7 c5.1,2.7,11.1,0.8,15-3.1c4.6-4.6,5.3-11.3,3.8-17.4c-1.6-6.9-5.7-12.7-9.3-18.6c-3.6-6-6.1-12.3-6-19.4c0.1-3.6,0.7-7.1,1.9-10.4 c0.6-1.5,0.8-3.2,0.7-4.8c-0.2-1.6-0.9-3.2-1.9-4.5c-1.7-2.3-3.9-4.3-5.5-6.7c-1.5-2.3-2.7-4.7-3.6-7.3c-3.9-10.7-4.3-22.3-8.5-32.8 c-2-5.4-5.3-10.2-9.7-13.9c-4.8-3.9-10.5-6.6-16.5-7.8c-13.6-2.8-27.2,1.1-39.5,6.8c-11.1,5.1-22.4,11.7-29.6,21.9 c-3.8,5.5-6,11.9-6.5,18.6c-0.6,7.2,0.6,14.4,2,21.5c6,30.6,13,61,20.5,91.2c0.9,3.7,1.8,7.3,2.8,11 C255.5,244.6,259.3,243.5,258.7,241.1L258.7,241.1z"/> +<path d="M316.5,133.5c3,2.6,7.2,3.5,11,2.4c1-0.3,1.6-1.4,1.3-2.4c-0.3-1-1.4-1.6-2.4-1.4c-0.4,0.1-0.7,0.2-1.1,0.3 c-0.1,0-0.2,0-0.3,0c0.1,0,0.4,0,0.1,0c-0.2,0-0.4,0-0.6,0c-0.4,0-0.7,0-1.1,0c-0.2,0-0.4-0.1-0.6-0.1c-0.6,0-1.1,0.4-1.1,1 c0,0.6,0.4,1.1,1,1.1c0.6,0,1.1-0.4,1.1-1c0-0.5-0.4-1-0.9-1.1c-0.1,0-0.2,0-0.3-0.1c-0.4-0.1-0.7-0.2-1.1-0.3 c-0.2-0.1-0.4-0.1-0.5-0.2c-0.3-0.1,0.3,0.2,0.1,0c-0.1,0-0.2-0.1-0.3-0.1c-0.3-0.2-0.7-0.3-1-0.5c-0.2-0.1-0.3-0.2-0.5-0.3 c0,0-0.3-0.2-0.1-0.1c0.2,0.2-0.1-0.1-0.1-0.1c-0.8-0.8-2-0.8-2.8,0S315.8,132.7,316.5,133.5C316.5,133.5,316.5,133.5,316.5,133.5 L316.5,133.5z"/> +<path d="M319.6,159c1.7,5.2,6.4,8.9,11.8,9.4c1.1,0,2-0.9,2-2c0-1.1-0.9-1.9-2-2c-0.1,0-0.6,0,0,0c-0.1,0-0.3,0-0.4-0.1 c-0.2,0-0.5-0.1-0.7-0.2c-0.5-0.1-1-0.3-1.5-0.5c0.4,0.3,0.1,0-0.1,0c-0.1-0.1-0.3-0.1-0.4-0.2c-0.2-0.1-0.4-0.2-0.6-0.3 c-0.2-0.1-0.5-0.3-0.7-0.5c-0.1-0.1-0.2-0.1-0.3-0.2c-0.1,0-0.1-0.1-0.2-0.1c0.2,0.1,0.2,0.2,0.1,0.1c-0.4-0.3-0.8-0.7-1.2-1.1 c-0.2-0.2-0.4-0.4-0.5-0.6c-0.1-0.1-0.4-0.4,0-0.1c-0.1-0.1-0.1-0.2-0.2-0.3c-0.3-0.4-0.6-0.9-0.8-1.3c-0.1-0.1-0.1-0.2-0.2-0.3 c0-0.1-0.1-0.1-0.1-0.2c-0.1-0.1,0-0.1,0.1,0.1c-0.1-0.3-0.2-0.6-0.3-0.8c-0.3-1.1-1.4-1.7-2.4-1.4S319.3,157.9,319.6,159 C319.6,159,319.6,159,319.6,159L319.6,159z"/> +<path d="M218.8,114.3c1.3,4.5,1.7,9.3,2.6,13.9c0.4,2.3,1.4,4.1,3.7,5c2.3,0.9,4.4-0.1,6.5-0.9c10.8-3.9,21.7-7.9,32.5-11.8 c19.3-7,38.7-14.4,56.2-25.4c4.4-2.7,8.6-5.6,12.6-8.9c1.9-1.5,2.7-4.1,2-6.4c-0.7-2.7-1.7-5.3-2.8-7.9c-1-2.5-2.2-5.1-3.4-7.5 c-1.1-2.1-2.4-4.2-4.9-4.6c-2.2-0.4-4,0.8-5.9,1.7c-0.7,0.4-1.4,0.6-2.2,0.6c-1.2-0.2-2.4-0.7-3.4-1.3c-18.4-8.7-39.3-10.9-59.3-7.7 c-5,0.8-9.8,1.9-14.6,3.4c-4.6,1.4-9.6,2.9-13.5,5.8c-3.9,2.8-7,7.1-6.7,12.1c0.1,4.3,3.2,7.9,7.4,8.9c2,0.5,4,0.1,5.8-0.9 c0.9-0.6,1.2-1.8,0.7-2.7c-0.1-0.1-0.2-0.3-0.3-0.4c-3.3-2.9-6.6,1.5-8.1,4.1c-1.8,3.2-2.9,6.7-3.1,10.3c-0.2,3.6,0.5,7.3,1.8,10.7 c0.6,1.6,1.4,3.5,0.9,5.2c-0.3,1.2-1.1,2.3-2.4,2.2c-2.5-0.2-2.5,3.8,0,3.9c3.3,0.2,5.8-2.7,6.3-5.8c0.6-3.8-1.6-7.1-2.4-10.6 c-1-4.8-0.2-9.8,2.2-14c0.4-0.7,0.8-1.3,1.3-1.9c0.2-0.3,0.4-0.5,0.6-0.8c0.1-0.1,0.3-0.3,0.4-0.3c0.3-0.1,0.2-0.2-0.4-0.3l0.4-3.1 c-2.6,1.6-6.1-0.3-6.9-3c-1.2-4,1.5-8.1,4.7-10.3c3.6-2.5,8-3.9,12.2-5.2c4.5-1.4,9-2.5,13.6-3.3c18-3.2,36.6-1.4,53.7,5.4 c2.2,0.9,4.3,2,6.5,2.9c2,0.8,4.3,0.8,6.2-0.2c1.1-0.5,2.7-1.9,4-1.6c0.8,0.2,1.2,1,1.6,1.7c0.6,1.2,1.2,2.4,1.8,3.6 c1.1,2.3,2.1,4.7,2.9,7.1c0.4,1.1,0.8,2.2,1.1,3.3c0.3,0.9,0.9,2.2,0.4,3.2c-0.5,0.7-1.2,1.3-2,1.8c-1,0.7-2,1.5-3,2.2 c-1.9,1.4-3.8,2.7-5.8,4c-15.8,10.3-33.3,17.4-50.9,23.9c-9.6,3.6-19.2,7-28.8,10.5l-7.5,2.7l-3.5,1.3c-0.8,0.3-1.9,0.9-2.8,0.8 c-1.7-0.3-1.7-2.6-1.9-3.9c-0.4-2.2-0.7-4.5-1.1-6.7c-0.3-2-0.7-3.9-1.3-5.9C221.9,110.8,218.1,111.8,218.8,114.3L218.8,114.3z"/> +<path d="M327.4,263.8c7.4-11,22.9-12.4,34.5-8.1c14.6,5.5,25.6,18.5,35.4,30.1c10.9,13.1,20.8,27.1,29.5,41.7 c7.2,11.8,14.2,23.6,21.9,35c4.3,6.5,9.1,12.8,14.1,18.9c2.3,2.6,4.7,5.1,7.2,7.5c1.3,1.2,3,2.8,3.2,4.7c0.1,0.7-1.1,3.2-2.1,2.4 v2.8c8.2-7.2,20.1-8.6,30.4-11c11.3-2.6,22.6-5.6,33.7-9c10.9-3.4,21.7-7.2,32.3-11.5c5.4-2.2,10.8-4.5,16.2-7 c4.4-1.9,8.6-4.4,12.4-7.4c4-3.3,7.6-7,11.8-9.9c1.9-1.4,3.9-2.5,6.1-3.3c1.1-0.4,2.3-0.7,3.4-0.8c0.5,0,1,0.1,1.5,0.3 c0.2,0.1,0.3,0.2,0.5,0.3c0.1,0.1,0.2,0.1,0.3,0.2c0.3,0.4,0.5,0.1,0.4-0.9l2.4,3.1c7.6-5.9,19.1-9.5,28.5-6.2 c3.6,1.3,7.2,4.2,7,8.4c-0.2,2.9-2.4,7.1-5.9,6.5l0.9,3.3c5.9-6.5,16.3-9.2,22.9-2.2c2.9,2.8,3.8,7.1,2.4,10.9 c-1.5,3.8-5.2,6.1-9.3,5.9l1.7,3c2.5-3.5,7.3-4.3,11.3-3.2c4.6,1.2,7.8,5.1,9.6,9.2c2.2,4.9,3,10.3,2.1,15.6 c-1,4.8-4.3,9.6-9.5,10.3c-4.5,0.6-9.8-4.2-7.3-8.8l-3.6-1.5c-1.1,3.2-4.8,5.1-7.5,6.9c-3.1,2.1-6.1,4.1-9.2,6.1 c-6.2,4.2-12.5,8.3-18.8,12.4c-12.5,8.1-25.2,16-38.2,23.3c-25.8,14.6-52.9,27.2-81.6,34.9c-14.7,3.9-29.9,6.9-45.2,7 c-11.2,0.1-23.3-1.2-33.1-6.8c-7.3-4.2-14-9.5-20.5-14.9c-8-6.7-15.7-13.8-23.2-21.1c-13.3-12.7-26-26-38-40 c-0.9-1.1-1.8-2.1-2.6-3.2c-1.6-2-4.4,0.8-2.8,2.8c11,13.8,23.5,26.5,36.1,38.8c7.7,7.6,15.6,15,23.8,22.1 c6.9,6.1,14.2,11.7,21.8,16.8c2.8,1.9,5.8,3.5,9,4.8c6.1,2.5,12.9,3.5,19.4,4.2c15.7,1.5,31.7-0.9,47-4.3 c29.4-6.6,57.3-18.7,83.7-33c26.8-14.5,52.5-31.3,77.5-48.5c2.7-1.9,5.1-3.9,6.2-7.1c0.7-2.1-2.6-3.5-3.6-1.5 c-2.8,5.3,0.3,11.9,5.7,14.1c6,2.4,12.9-1.3,16.1-6.5c3.7-6.1,3.3-14.2,1.3-20.8c-1.8-6-5.6-11.8-11.6-14.3 c-6.1-2.5-13.6-1.1-17.6,4.4c-1,1.3,0.4,2.8,1.7,3c5.4,0.5,10.4-3,12.7-7.8c2.2-4.8,1.4-10.4-2-14.5c-7.7-9.8-21.8-7.4-29.5,1 c-0.9,1-0.7,3,0.9,3.3c8.1,1.4,13.2-8.7,9.8-15.4c-4.4-8.9-16.7-9.4-25.1-7.6c-5.9,1.3-11.4,3.9-16.1,7.6c-1.8,1.4,0.5,4.3,2.4,3.1 c1.5-0.9,2.1-2.8,1.2-4.3c-0.1-0.3-0.3-0.5-0.5-0.7c-1.5-1.6-3.6-2.4-5.8-2.2c-4.7,0.5-9,3-12.7,5.8c-4,3-7.5,6.6-11.4,9.7 c-4.4,3.6-9.4,5.7-14.6,8c-23.3,10.2-47.5,18.3-72.3,24.2c-6.8,1.7-13.8,3-20.5,4.8c-6.2,1.7-12.3,4.2-17.2,8.5 c-0.8,0.8-0.8,2,0,2.8c2.6,2.2,6.1,1.1,7.9-1.7c2-3.2,0.4-6.7-1.8-9.3c-2.3-2.6-4.9-4.8-7.2-7.4c-2.5-2.8-4.9-5.7-7.1-8.7 c-9.4-12.5-17.6-25.9-25.6-39.3c-9.1-15-18.6-29.8-29.6-43.5c-5.5-7-11.4-13.8-17.7-20.1c-5.8-5.8-12.3-11.3-19.8-14.9 c-14.2-6.9-33.7-6.1-43.3,8C322.6,263.9,326,265.8,327.4,263.8L327.4,263.8z"/> +<path class="st0" d="M25,948.3h66H172c15.3,0,29.6-6.6,37.1-20.6c4.4-8.2,4.6-16.8,4.6-25.8V695.7c0-8.9,0-17.3-5.7-25 c-7.6-10.3-21-12.3-32.5-15.7l-46.2-13.4c-15.3-4.4-29.9-9.4-42.6-19.5c-19.5-15.5-32.1-38.1-35.1-62.8c-0.9-8.2-0.6-16.5-0.6-24.8 V399.9c0-7.7-0.2-15.4,0.2-23.1c1.2-23.8,8.1-47,20.1-67.7c23.7-40.7,66.2-68.7,113.1-73.9c47.3-5.2,94.2,12.1,126.8,46.7 c27.3,29.1,40.6,67,40.6,106.6v128.2c0,7.8-0.7,16.1,0.7,23.8c3.6,19,20,31.5,38.7,32.8c14.2,0.9,28.4,1.8,42.6,2.7 c14.2,0.9,29.8,0,41.3,9.8c20,17,15.6,49.9-8.3,61c-7.6,3.5-16.1,3.5-24.2,4.1l-37.3,2.7l-98.9,7l-34.7,2.5 c-9,0.6-17.7,1.1-25.2,7.2c-8.8,7.2-10.6,17.2-10.6,27.8v208c0,5.5-0.5,11.2,0.3,16.6c1.5,10.2,8.2,18.8,17.7,22.8 c4.8,2,9.7,2.2,14.8,2.2h86.3c12.1,0,23.8,0.2,35.2-4.9c19.9-9.1,32.8-28.9,32.9-50.8c0.1-11.7,0-23.4,0-35.1V529.4 c0-9.9-4.8-19.2-15.6-20.7c-6.4-0.9-12.2-1.6-15.3-8.2c-2.4-5.4-1.8-11.7,1.8-16.4c2.9-3.7,7.2-5.2,11.7-5.3c10.3-0.1,20.6,0,30.9,0 H968c2.5,0,2.5-3.9,0-3.9H435.2c-9.8,0-19.7-0.2-29.6,0c-14.8,0.3-24,17.1-15.5,29.7c2.7,3.9,6.7,6.6,11.4,7.6 c2.2,0.5,4.6,0.2,6.8,0.7c3.6,0.8,6.7,3.1,8.7,6.2c1.6,2.8,2.4,6,2.2,9.2c0.1,15.5,0,30.9,0,46.4V852c0,13.3,0.1,26.6,0,39.9 c0,20.7-12.3,39.5-31.3,47.8c-11.4,4.9-23.4,4.2-35.5,4.2h-84.9c-15.2,0-27.4-10.1-27.5-26c0-6.7,0-13.4,0-20.1V716.4 c0-7.3,0-14.7,0-22c0-14.2,9.7-25.4,24.1-26.6c22.1-1.8,44.3-3.2,66.4-4.7l96.3-6.8l25.7-1.8c15.4-1.1,29.2-9.1,35.8-23.4 c9.2-19.9,0.4-44.4-19.6-53.6c-11.5-5.3-26.1-4.4-38.4-5.2c-13.3-0.9-26.7-1.6-40-2.6c-19.5-1.3-34.6-17.6-34.5-37.1 c-0.1-24.9,0-49.9,0-74.8c0-24.1,0.1-48.2,0-72.4c0.2-85.3-68.7-154.6-154-154.8c-78.3-0.2-144.4,58.3-153.7,136.1 c-1.2,9.7-1.1,19.4-1.1,29.2v90.2c0,20.9-0.1,41.8,0,62.7c0.3,44.2,30,81.9,72.1,94.2c24.1,7,48.5,13.4,72.5,21 c10.7,3.3,18,13.1,18.2,24.2c0,1.6,0,3.3,0,4.9v205.8c0,7.3,0.5,14.8-1.9,21.8c-4.1,11.7-14,20.5-26.1,23.1 c-3.6,0.6-7.2,0.8-10.8,0.7H87.2H25C22.5,944.4,22.5,948.3,25,948.3L25,948.3z"/> +<path d="M322.6,263.8c-1.7,1.8-1.6,4.7,0.3,6.4c0,0,0,0,0,0c2.1,2,5.3,1.6,7.8,0.9c2.6-0.7,5.1-2.2,6.1-4.8c0.9-2.5,0.6-5.3-0.8-7.6 c-1.5-2.3-3.5-4.4-5.7-6.1c-2.1-1.8-4.3-3.5-6.6-5c-4.5-3.1-9.2-5.8-14.2-8.2c-10-4.7-20.4-8.4-31-11.3c-5.3-1.5-10.7-2.9-16.1-4.3 c-6.3-1.7-12.6-3.1-19.1-4c-5.5-0.7-12.5-0.9-16.2,4.1c-2.7,3.7-2.6,9.9,1.8,12.2c2.2,1.2,4.2-2.2,2-3.4c-2.5-1.3-2-5-0.4-6.9 c2.7-3.2,8.1-2.7,11.8-2.3c10.2,1.2,20.3,4.3,30.2,7c17.9,4.9,36,10.7,51.1,21.9c2.1,1.5,4.1,3.2,6,4.9c1.7,1.5,3.5,3.4,3.7,5.8 c0.2,2.5-1.7,3.7-3.9,4.3c-1,0.3-2.4,0.7-3.5,0.3c-0.3-0.1-1-0.7-0.6-1C327.2,264.8,324.4,262,322.6,263.8L322.6,263.8z"/> +<path d="M362.8,406.5c-3.8-3.5-6.9-7.8-9-12.6c-1.4-3.5-1.5-8.5,2-11c4-3,9.5,0.1,12,3.5c1.6,2.2,2.9,4.7,3.8,7.3 c1.2,3,2.3,6.1,3.4,9.1l14,37.5l3.5,9.3c1.2,2.9,2.1,5.8,3,8.8c0.8,2.6,0.9,5.4,0.3,8.1c-0.8,2.5-2.4,4.7-4.5,6.3 c-9.9,7.9-23.6,11-36,9.6c-1.1,0-2,0.9-2,2c0,1.1,0.9,1.9,2,2c12.7,1.4,25.5-1.7,36.1-8.8c2.6-1.6,4.9-3.8,6.6-6.3 c1.6-2.8,2.3-6,2-9.1c-0.5-6.7-3.6-13.1-5.9-19.4l-15.5-41.5c-2.2-6-4-12.7-8.1-17.7c-3.5-4.3-9.6-7.2-15-5c-5.7,2.3-7.8,9.5-6,15.1 c1.9,5.9,6,11.4,10.5,15.6C361.9,411.1,364.7,408.3,362.8,406.5L362.8,406.5z"/> +<path d="M355.9,482.5c-2.3-0.4-3.9-1.5-4-3.9c0-2.1,0.9-4.2,2.5-5.6c2.5-2.3,6-3.3,9.3-4c3.9-0.8,7.8-1.4,11.7-1.7 c16.1-1.3,32.2,0.9,47.8,5c15.9,4.2,31.2,10.1,46.3,16.4c15.5,6.5,30.9,13.1,46.2,20c26.3,11.9,53.7,24.3,74.3,45.1 c10,10.1,17.6,22,24.3,34.6c7.2,13.9,13.5,28.2,18.9,42.9c11.5,30.7,20.9,62.1,30.4,93.4c5,16.3,10.1,32.4,15.6,48.5 c4.2,12.3,8.6,26,3.6,38.7c-3.6,9.2-11.3,16.4-19.5,21.4c-8.8,5.4-18.7,8.8-29,10.1c-4.8,0.6-9.7,0.6-14.5-0.1 c-5.9-0.9-11.3-3.6-15.6-7.7c-5.1-4.8-9-10.7-13-16.4c-4.5-6.4-8.9-13-13.1-19.7c-16.8-26.7-30.8-55-43.5-83.8 c-6.3-14.4-12.3-29-18.2-43.6c-5.8-14.5-12.4-30.1-10.8-46c0.1-0.9,0.2-1.9,0.4-2.8c0.3-1,0.5-1.9,0.6-3l-0.7-0.3 c-0.6-0.5-0.9-0.4-0.9,0.2c-0.2,0.3-0.4,0.7-0.5,1c-0.4,0.7-1,1.2-1.6,1.7c-3,2.1-7.4,1.4-10.8,1c-2.5-0.3-2.5,3.7,0,3.9 c3.9,0.4,7.9,0.7,11.5-0.9c2.7-1.2,5.8-4.4,5.6-7.6c-0.1-1.8-1.6-3.1-3.4-3c-0.8,0.1-1.6,0.4-2.2,1c-1.1,1.3-1.2,3.5-1.5,5.2 c-0.4,2.2-0.6,4.3-0.7,6.5c-0.5,16.8,6.7,32.9,12.8,48.2c12.5,31.1,25.8,62,41.6,91.6c7.9,14.8,16.4,29.2,25.7,43.2 c4.4,6.6,8.9,13.1,13.8,19.4c4.3,5.5,9.3,10.6,15.8,13.4c9.8,4.2,21.7,3.3,31.7,0.9c10.9-2.5,21.1-7.4,29.8-14.5 c7.9-6.5,14.3-15.5,15.8-25.8c1.1-7.3-0.1-14.8-2-21.9c-2.3-8.6-5.5-17-8.3-25.4c-11.2-34-20.8-68.6-32.3-102.5 c-5.3-15.6-10.8-31-17.3-46.2c-6.4-14.8-13.5-29.7-22.8-42.9c-8.3-11.6-18.4-21.7-29.9-30c-12.3-9-25.9-16-39.7-22.6 c-16.3-7.9-32.8-15.1-49.4-22.2c-16-6.8-32-13.7-48.5-19.1c-16.4-5.4-33.4-9.3-50.6-9.8c-8.4-0.2-16.7,0.5-24.9,2.1 c-5.9,1.2-12.6,3.8-14.5,10.2c-1.6,5.1,1,10.1,6.4,11C357.3,486.8,358.4,483,355.9,482.5L355.9,482.5z"/> +<path d="M612.1,845c9.9,16.5,19.9,33,30,49.4c2.5,4.1,5.1,8.3,7.7,12.4c2.1,3.4,4.1,7.2,7,10.1c3,3,7.2,4.4,11.4,3.8 c4.1-0.4,8.2-1.2,12.2-2.4c16.1-4.6,30.4-13.8,43.9-23.6c3.3-2.4,6.6-4.9,9.9-7.4c3.2-2.5,6.5-4.9,9-8.2c4.4-5.6,6.4-14.7,0.5-20 c-2.9-2.6-6.7-3.5-10.5-3.9c-4.6-0.6-9.2-1-13.8-1.4l-7.1-0.7c-2-0.3-4-0.3-6.1,0c-3.4,0.7-5.9,3.9-4.8,7.4c0.4,1.5,1.6,2.6,3.1,2.9 c1.3,0.2,3-0.9,2.4-2.4c-5.7-15.2-12.6-29.9-20.8-44c-1.3-2.2-4.7-0.2-3.4,2c8,13.7,14.8,28.1,20.4,43l2.4-2.4 c-0.3-0.1-0.3,0-0.4-0.3c-0.1-0.4-0.1-0.7,0-1.1c0.4-1.1,1.9-1.3,2.9-1.4c2,0,3.9,0.1,5.9,0.4l6.6,0.7c4.1,0.4,8.3,0.7,12.4,1.3 c3.1,0.4,6.5,1.1,8.6,3.6c1.9,2.2,2.1,5.4,1.4,8.2c-0.9,3.3-2.8,6.3-5.4,8.5c-2.7,2.4-5.6,4.7-8.5,6.8c-3,2.3-6.1,4.5-9.1,6.8 c-12.2,8.8-25.1,17-39.6,21.3c-3.6,1.1-7.3,1.9-11.1,2.4c-1.9,0.3-3.8,0.3-5.7-0.1c-1.6-0.5-3-1.4-4.1-2.6c-2.6-2.6-4.3-6.1-6.3-9.2 c-2.3-3.7-4.6-7.4-6.9-11.1c-9.2-14.9-18.3-29.9-27.3-44.9c-1.1-1.9-2.3-3.8-3.4-5.7C614.3,840.9,610.8,842.9,612.1,845L612.1,845z"/> +<path d="M492,624.2c-22.8-0.5-45.8-1.1-68.1-6c-22.1-4.9-44.7-14.5-58.8-32.9c-7.7-10-12.6-22.4-12.7-35.1c0-5.1,0.5-11,3.9-15.1 c1.5-1.9,3.8-3,6.3-3c2.9,0.1,5.5,1.8,7.6,3.7c4.9,4.3,8.7,10,12.3,15.3c3.6,5.4,6.9,11,9.9,16.7c5.9,11.7,10.4,24.1,13.2,36.9 c5.7,26.4,3.8,53.4-3.2,79.3c-7,25.9-18.4,50.3-29.6,74.6c-2.9,6.2-5.7,12.4-8.5,18.7c-2.6,5.9-4.7,11.9-6.8,18 c-4,11.9-8.3,24.7-17.6,33.6c-16.4,15.6-43.1,10.9-59.5-2.2c-8-6.2-13.5-15.1-15.5-25.1c-1-5.3-1-10.7-0.3-16c1-7.5,2.3-15,3.6-22.5 c2.7-15.2,5.7-30.4,9.1-45.5c2.9-13,5.9-25.8,11.5-37.9c2.4-5.4,5.4-10.5,8.9-15.2c1.5-2-1.9-4-3.4-2c-7.9,10.3-12.7,23-16.3,35.3 c-2,6.9-3.4,14-5,21.1c-1.7,7.8-3.4,15.7-4.9,23.6c-1.6,7.9-3,15.8-4.4,23.7c-1.3,7.4-2.8,15-3.4,22.5c-0.9,10.8,1.6,21.8,7.8,30.7 c6.4,9.2,16,15.7,26.4,19.6c10.6,4,22.7,5.1,33.5,1.5c26.6-9,30.3-40.3,40.2-62.6c11.3-25.5,23.9-50.4,32.9-76.8 c9.1-26.6,14-54.6,10.6-82.7c-3.3-27.1-13.6-53-29.9-74.9c-3.8-5.1-7.8-10.9-13.6-13.8c-5.4-2.7-11.5-1.6-15.3,3.2 c-3.6,4.6-4.5,10.6-4.6,16.3c-0.1,6.6,1,13.1,3.2,19.4c8.3,24.1,29.6,40,52.9,48.3c22.6,8.1,47,10.1,70.7,10.9 c5.6,0.2,11.1,0.3,16.7,0.4C494.5,628.2,494.5,624.2,492,624.2L492,624.2z"/> +<path d="M261.4,806c-3.4,10.1-6.9,20.2-10.5,30.2c-1.8,5-3.7,9.9-5.6,14.8c-1.9,4.9-4,9.8-5.8,14.8c-1.6,4.4-2.4,9-1.2,13.7 c1.2,4.2,3.5,8,6.6,11c3.2,3.2,7.2,5.6,11.1,7.9c4.1,2.4,8.4,4.5,12.8,6.3c13.3,5.6,27.5,8.5,41.8,10.2c5,0.7,10,0.9,15,0.8 c4.4-0.2,9.4-0.7,13-3.3c3.9-2.8,4.6-7.3,3.1-11.6c-1.6-4.3-3.9-8.3-6.8-11.9c-1.4-1.8-3-3.6-4.6-5.3c-1.4-1.6-2.9-3.8-5.1-4.3 c-1.8-0.4-3.5,0.8-3.9,2.6c-0.1,0.5-0.1,1,0,1.4c0.5,2,3.1,1.7,3.8,0c7.3-18.6,14.6-37.3,22-55.9c0.3-1-0.3-2.1-1.4-2.4 c-1-0.3-2.1,0.3-2.4,1.4l-22,55.9h3.8l0-0.1l-0.3,1c0.2-0.3-0.4,0.1-0.4,0c0,0,0.4,0.3,0.4,0.3c0.3,0.4,0.6,0.7,1,1 c0.7,0.8,1.4,1.6,2.2,2.4c1.4,1.6,2.8,3.2,4.1,4.9c2.6,3.1,4.6,6.7,6,10.6c0.3,0.9,0.5,1.8,0.5,2.7c0,0.2,0,0.4,0,0.6 c0,0.1,0,0.2,0,0.3c0-0.1,0.1-0.4,0,0c-0.1,0.4-0.2,0.8-0.3,1.2c0,0,0.1-0.2-0.1,0.2c-0.1,0.2-0.2,0.4-0.3,0.5 c0,0.1-0.3,0.5-0.2,0.3c0.1-0.2-0.2,0.2-0.3,0.3c-0.1,0.1-0.3,0.3-0.4,0.4c-0.1,0.1-0.6,0.5-0.2,0.2c-0.3,0.2-0.7,0.5-1,0.7 c-0.7,0.4-1.5,0.8-2.2,1c-1,0.3-1.9,0.5-2.9,0.7c-2.1,0.4-4.2,0.6-6.4,0.6c-4.6,0-9.2-0.2-13.8-0.8c-1.1-0.1-1.8-0.2-2.7-0.4 c-0.8-0.1-1.7-0.2-2.5-0.4c-2.3-0.4-4.7-0.7-7-1.1c-8.7-1.5-17.2-3.9-25.5-7.1c-4.1-1.6-8-3.4-11.8-5.5c-3.8-2-7.4-4.2-10.9-6.8 c-3.1-2.3-5.5-5.4-7.1-8.9c-1.5-3.8-1.6-8.1-0.3-12c1.4-4.5,3.4-8.9,5.1-13.3c1.8-4.6,3.6-9.3,5.3-13.9c3.4-9.1,6.7-18.3,9.8-27.6 c0.8-2.3,1.6-4.6,2.3-6.9C266,804.6,262.2,803.6,261.4,806L261.4,806z"/> +<path class="st0" d="M867.6,298.2H542.8c-4.3,0-8.8-0.4-13.1,0c-0.2,0-0.4,0-0.6,0l1.9,2.5l4.1-22.8l9.9-54.4l12-65.8l10.4-56.9 l3.4-18.8c0.5-2.9,1.5-5.9,1.6-8.9c0-0.1,0-0.3,0.1-0.4l-1.9,1.4h324.8c4.3,0,8.8,0.4,13.1,0c0.2,0,0.4,0,0.6,0l-1.9-2.5L903,94.4 l-9.9,54.4l-12,65.8l-10.4,56.9c-1.7,9.2-3.5,18.4-5,27.6c0,0.1,0,0.3-0.1,0.4c-0.5,2.5,3.3,3.5,3.8,1l4.1-22.8l9.9-54.4l12-65.8 l10.4-56.9c1.7-9.2,3.5-18.4,5-27.7c0-0.1,0-0.3,0.1-0.4c0.3-1.1-0.4-2.2-1.4-2.4c-0.1,0-0.3-0.1-0.5-0.1H584.3 c-4.4,0-8.8-0.1-13.1,0c-0.2,0-0.4,0-0.6,0c-0.9,0-1.7,0.6-1.9,1.4l-4.1,22.8l-9.9,54.4l-12,65.8l-10.4,56.9 c-1.7,9.2-3.5,18.4-5,27.6c0,0.1,0,0.3-0.1,0.4c-0.3,1.1,0.4,2.2,1.4,2.4c0.1,0,0.3,0.1,0.5,0.1h324.8c4.4,0,8.8,0.1,13.1,0 c0.2,0,0.4,0,0.6,0C870.1,302.1,870.1,298.2,867.6,298.2z"/> +<path class="st0" d="M892.8,71.7l-1.1,6.1l-3,16.6l-4.5,24.5l-5.4,29.9l-6,32.8l-6,33.1l-5.6,30.8l-4.8,26.1l-3.4,18.7 c-0.5,3-1.1,5.9-1.6,8.9c0,0.1,0,0.3-0.1,0.4c-0.3,1.1,0.4,2.2,1.4,2.4c0.1,0,0.3,0.1,0.5,0.1h14.3c0.9,0,1.7-0.6,1.9-1.4l1.1-6.1 l3-16.6l4.5-24.5l5.4-29.9l6-32.8l6-33.1l5.6-30.8l4.8-26.1l3.4-18.7c0.5-3,1.1-5.9,1.6-8.9c0-0.1,0-0.3,0.1-0.4 c0.3-1.1-0.4-2.2-1.4-2.4c-0.1,0-0.3-0.1-0.5-0.1h-14.3c-2.5,0-2.5,3.9,0,3.9h14.3l-1.9-2.5l-1.1,6.1l-3,16.6l-4.5,24.5l-5.4,29.9 l-6,32.8l-6,33.1l-5.6,30.8l-4.8,26.1l-3.4,18.7c-0.5,2.9-1.3,5.9-1.6,8.9c0,0.1,0,0.3-0.1,0.4l1.9-1.4h-14.3l1.9,2.5l1.1-6.1 l3-16.6l4.5-24.5l5.4-29.9l6-32.8l6-33.1l5.6-30.8l4.8-26.1l3.4-18.7c0.5-3,1.1-5.9,1.6-8.9c0-0.1,0-0.3,0.1-0.4 C897.1,70.2,893.3,69.2,892.8,71.7z"/> +<path class="st0" d="M769.1,474.8c-7.1,0.1-14.1-1.3-20.6-4.2c-6.5-3-11.8-8-15.2-14.3c-3.3-6.2-4.6-13.3-4.9-20.2 c-0.3-7.6,0.4-15.3,1.9-22.8c0.9-4.3,1.9-8.5,2.7-12.7c0.8-4.1,1.8-8.6,1.2-12.7c-0.4-3.6-2.3-6.9-5.2-9c-2.3-1.7-5.1-2.2-7.9-2.6 c-2.7-0.4-5.5-0.6-7.9-2c-2.2-1.2-4-3-5.2-5.3c-2.4-4.5-2.5-9.9-0.3-14.5c2.9-5.9,9.3-8.4,15.6-8.7c6.7-0.1,13.3,1.7,19.1,5.2 c12.5,7.8,19.8,21.7,19.2,36.4c-0.3,6.6-3.7,12-6.7,17.7c-2.8,5.3-5.7,11.4-4.2,17.5c0.9,3.5,3.7,6.2,4.5,9.7 c0.2,0.8,0.2,1.6-0.1,2.4c-0.1,0.3-0.5,1.2-0.9,1.2c-1-0.2,0.5-1.8,0.7-2.1c3.8-5.8,8.8-10.8,14.5-14.7c12.7-8.7,28-12.1,43.3-11.7 c8.7,0.3,17.4,1.5,25.8,3.6c4.2,1,8.4,2.1,12.5,3.4c3.4,1,6.8,2.3,10,3.9c3.1,1.6,5.8,3.8,7.9,6.4c1,1.3,3.5,0.2,3.4-1.4 c-0.3-3.8-3-6.2-6.2-7.9c-2.8-1.5-6.6-2.8-8.2-5.8c-1.3-2.4-0.9-5.7-0.8-8.3c0.1-3.2,0.3-6.4,0.6-9.6c0.3-3,0.8-6,1.4-9 c0.3-1.7,0.8-3.6,2.6-4.1c2.8-0.8,4.1,1.7,5.5,3.6c1.4,1.9,3.6,3.9,3.6,6.5c0,1.1,0.9,2,2,2c0.3,0,0.6-0.1,0.9-0.3 c6.1-3.1,12.4-5.9,19.1-7c6.7-1.1,13.5-0.4,20.1,0.8c1.1,0.3,2.2-0.4,2.4-1.5c0.1-0.3,0.1-0.7,0-1c-0.2-1.5,0-3,0.7-4.4 c0.6-1.7,1.6-3.2,2.8-4.5c1-1,1.9,0.7,2.5,1.5c1.1,1.4,2.1,2.9,3,4.5c3.5,6,5.7,12.7,6.3,19.6c1.1,13.3-3.8,26.3-13.3,35.7 c-5.1,5.1-11.4,8.9-18.4,11.1l2.2,2.9c0.8-1.2,1.7-2.3,2.8-3.2c0.1-0.1,0.8-0.5,0.7-0.4c0.1,0,0.6-0.2,0.4-0.1c-0.2,0,0.3,0,0.5,0 c0.3,0-0.1,0,0.3,0c0.2,0.1,0.5,0.1,0.7,0.2c0.5,0.2,0.9,0.3,1.4,0.5c1.4,0.6,2.8,1.3,4.1,1.9c6,2.7,12.4,4.5,18.7,6.2 c3.3,0.9,6.7,1.8,10,2.8c3,0.9,6,1.9,9.1,2.7c7.1,1.8,16.7,2.1,21.2,8.8c1,1.4,1.6,3,1.6,4.7c-0.1,0.8-0.4,1.1-1.2,1.5 c-1.4,0.7-3.8,0.6-5.5,1.4c-2.3,1.1-0.3,4.5,2,3.4c-0.5,0.3,0.3,0,0.5-0.1c0.5-0.1,1-0.2,1.5-0.2c1.1-0.2,2.3-0.5,3.3-1 c2.8-1.3,3.7-4,3.2-6.9c-1.7-8.8-11.1-12.3-18.8-13.9c-4.1-0.9-8.2-1.9-12.2-3.1c-3.4-1-6.9-2-10.4-2.9c-6.9-1.9-14.1-3.6-20.6-6.6 c-2.8-1.2-6-3.5-9.1-3.4c-3.5,0-5.7,3.2-7.6,5.8c-1,1.4,0.7,3.4,2.2,2.9c14.5-4.6,26.1-15.5,31.6-29.6c5.7-14.8,3.3-32.3-5.7-45.3 c-1.2-1.7-2.5-4-4.4-5c-2-1-4.4-0.5-5.8,1.3c-2.8,3.4-5.2,7.9-4.2,12.4l2.4-2.4c-7.2-1.3-14.5-2-21.8-0.9c-7.2,1.2-14,4.1-20.4,7.5 l3,1.7c0-2.9-1.5-5.1-3.2-7.3c-1.6-2.1-2.8-4.6-5.2-5.9c-2.5-1.4-5.6-1.2-8,0.5c-1.9,1.5-2.7,3.7-3.1,6c-1.3,7-2.1,14.1-2.1,21.2 c0,3,0,6.3,1.7,9c1.2,1.7,2.8,3.1,4.6,4.2c1.9,1.2,4,2.1,5.9,3.3c1.4,0.9,2.8,2,3,3.8l3.4-1.4c-4.9-6.2-12.5-9.2-19.8-11.4 c-8.9-2.8-18.1-4.9-27.4-6.2c-17-2.2-34.5-0.7-50,7.1c-6.8,3.4-12.9,8-17.9,13.6c-1.3,1.4-2.5,2.9-3.6,4.5c-1,1.4-2.4,3.1-2.9,4.8 c-0.6,2.3,0.9,4.7,3.2,5.3c1,0.2,2.1,0.1,3-0.4c4-2.3,3.1-7.9,1.4-11.3c-0.8-1.7-2.1-3.2-2.8-5c-0.5-1.4-0.8-2.9-0.7-4.3 c0.2-3.1,1.1-6.1,2.6-8.8c1.5-3.1,3.3-6,4.8-9.1c1.6-2.9,2.8-6.1,3.5-9.4c0.7-4.4,0.6-8.8-0.2-13.2c-1.3-7.9-4.8-15.2-9.9-21.3 c-9-10.9-24.8-18.1-38.9-13.9c-6.6,1.8-11.8,7.1-13.4,13.7c-1.4,5.8-0.1,12,3.5,16.8c1.9,2.4,4.4,4.2,7.3,5.2c2.8,1,5.9,1.1,8.9,1.7 c3,0.6,5.4,1.9,6.9,4.7c2,4.1,0.9,8.9,0.1,13.2c-1.6,8.9-4.1,17.6-4.8,26.7c-0.8,7.7-0.3,15.4,1.3,23c1.7,7.6,5.6,14.5,11.2,19.8 c5.8,5.1,12.8,8.5,20.4,9.9c3.8,0.7,7.6,1.1,11.4,1.1C771.6,478.7,771.6,474.8,769.1,474.8L769.1,474.8z"/> +<path class="st0" d="M780.6,428.6c10.2-6.7,22.9-8.7,34.7-5.5c5.7,1.6,11.5,4.4,14.5,9.6c2.7,4.8,3,10.6,0.9,15.7 c-2.5,5.7-7.5,9.9-13.5,11.5l2.2,0.9l-0.1-0.2l0.3,1l0-0.2l-0.3,1c-0.2,0.1-0.2,0.2-0.1,0.1c0.3,0,0.5,0,0.8-0.1 c0.6,0,1.2-0.1,1.7-0.1l3.7-0.3l6.9-0.6c2.3-0.2,4.6-0.4,6.9-0.6c2-0.3,4.1-0.2,6.2,0.2c3.1,1,5.7,3.2,7.1,6.2 c1.1,2.1,2.2,6.2-0.5,7.7c-2.2,1.2-0.2,4.6,2,3.4c3.2-1.7,3.9-5.6,3.3-8.9c-0.7-3.5-2.5-6.7-5.2-9c-2.9-2.5-6.7-3.8-10.6-3.6 c-4.7,0.2-9.5,0.8-14.2,1.1c-2.6,0.2-5.3,0.4-7.9,0.7c-2.2,0.2-4.8,1.7-3.5,4.3c0.5,0.8,1.4,1.1,2.2,0.9c6.2-1.7,11.5-5.6,14.8-11.1 c3.3-5.6,3.9-12.3,1.7-18.4c-4.9-13.3-21.8-17.4-34.5-16.3c-7.7,0.6-15.2,3.2-21.7,7.4C776.5,426.6,778.5,430,780.6,428.6 L780.6,428.6z"/> +<path class="st0" d="M872.5,404.4c4.2-0.3,8.5,0.6,12.2,2.6c0.9,0.5,2.1,0.2,2.7-0.8c0.5-0.9,0.2-2.1-0.7-2.6 c-4.3-2.4-9.3-3.5-14.2-3.2c-1.1,0-1.9,0.9-2,2C870.5,403.5,871.4,404.4,872.5,404.4z"/> +<path class="st0" d="M919.4,395.3c-3-0.4-6.1,0.3-8.7,1.9c-1.2,0.7-2.4,1.5-3.5,2.3c-1.3,0.8-2.4,1.9-3.1,3.2 c-0.5,0.9-0.2,2.1,0.7,2.7c0.9,0.5,2.1,0.2,2.7-0.7c0.4-0.8,1.1-1.5,1.8-1.9c1-0.7,2-1.4,3.1-2c0.5-0.3,1-0.6,1.6-0.8 c0.4-0.2,0.9-0.4,1.4-0.5c0.3-0.1,0.6-0.2,0.8-0.2c0.1,0,0.3-0.1,0.4-0.1c0.3-0.1-0.2,0,0.1,0c0.3,0,0.6,0,0.9,0c0.1,0,0.3,0,0.4,0 c0,0,0.5,0.1,0.2,0c1,0.3,2.1-0.3,2.4-1.4C921,396.7,920.4,395.6,919.4,395.3L919.4,395.3z"/> +<path class="st0" d="M899.5,411.6c-0.3,0.7-0.9,1.1-1.6,1.3c-1.1,0.2-2.2,0.2-3.3,0c-1-0.3-2.1,0.3-2.4,1.4c-0.3,1,0.3,2.1,1.4,2.4 l1.9,0.6c0.1,0,0.9,0.2,0.9,0.3l-0.3-0.2c-0.2-0.7-0.3-0.8-0.3-0.4c0,0.7,0.2,1.3,0.4,1.9c0.1,0.4,0.2,0.8,0.1,1.2 c-0.2,1.2-1.3,2-2.5,1.8c-0.2,0-0.4-0.1-0.5-0.2l-1.5,3.6c1.7,0.6,3.5,0.8,5.3,0.6c0.9-0.2,1.8-0.6,2.5-1.2c0.1,0,0.6-0.5,0.4-0.4 c0.3,0,0.7,0.1,1,0.1c3.6,0.4,7.7-0.7,9.3-4.1c0.5-0.9,0.2-2.1-0.7-2.7c-0.9-0.5-2.1-0.2-2.7,0.7c-0.5,1-1.3,1.7-2.3,2 c-1.4,0.3-2.9,0.3-4.4,0.1c-0.8-0.1-1.7,0.2-2.4,0.7c-0.4,0.4-0.9,0.7-1.3,1c-1.1,0.4-2.5-0.1-3.7-0.5c-2.1-0.6-3.6,2.6-1.5,3.6 c3,1.6,6.7,0.4,8.3-2.6c0.2-0.4,0.4-0.9,0.5-1.3c0.2-0.9,0.2-1.9,0-2.8c-0.1-0.4-0.2-0.8-0.4-1.3c-0.1-0.4-0.1-0.8-0.2-1.2 c-0.6-2.1-3.2-2.5-5-3l-1,3.8c1.8,0.4,3.7,0.4,5.6,0c1.6-0.4,2.9-1.5,3.7-3c0.5-0.9,0.2-2.1-0.7-2.7 C901.3,410.4,900.1,410.7,899.5,411.6L899.5,411.6z"/> +<path d="M475.8,390.4c-14.9,5.4-30.3,9-46,10.7c-7.6,0.8-15.4,1.6-23.1,1c-6.6-0.5-13.4-2.7-17.6-8.1c-9.8-12.8,7.4-25.5,16.7-31.7 c6.6-4.4,13.5-8.5,20.2-12.8c6.9-4.3,13.8-8.8,20.9-12.9c6.8-4.1,13.9-7.7,21.2-10.8c3.7-1.6,7.4-3,11.2-4.2c1.6-0.5,6.1-2,6.6,0.8 c0,0.3,0,0.6,0,0.9c-0.1,0.1-0.3,0.3-0.1,0.3l1.4,3.4c4-3.6,8.9-6,14.2-7.1c1.8-0.4,4.6-0.9,6,0.6c0.3,0.4,0.5,0.9,0.4,1.5 c0,0.2-0.1,0.5-0.2,0.7c-0.1,0.2-0.1,0.2-0.3,0.2l1.4,3.4c7.6-6.7,18.8-8,28.1-4.7c4.6,1.6,9.8,4.9,12,9.4c2.7,5.6-3.8,9.1-8.4,9.6 c-3.5,0.4-7.1,0-10.6,0.4c-2.8,0.3-5.6,1-8.2,2.1c-5.4,2.2-10.2,6-13.6,10.7c-3.4,4.8-5.5,11.9-0.7,16.6c1.9,1.9,4.7,2.7,7.3,2.1 c2.5-0.5,4.7-1.9,7-2.9c2-0.9,4.4-1.5,6.1,0.1c1.5,1.6,2.1,3.7,1.8,5.9c-0.8,6-8.1,8.3-13.1,9.6l2.4,1.4c-0.3-0.9,0.1-1.9,1-2.3 c2.2-1.2,0.2-4.6-2-3.4c-2.4,1.3-3.5,4.1-2.8,6.7c0.3,1,1.4,1.6,2.4,1.4c5.5-1.5,12.1-3.9,14.8-9.4c2.1-4.3,1.4-10.3-2.6-13.3 c-2.2-1.5-4.9-1.9-7.4-1.1c-2.6,0.7-4.9,2.3-7.5,3.2c-2.4,0.8-4.7,0.1-5.8-2.3c-1.3-2.7-0.3-5.7,1.1-8.2c3.1-5.3,7.9-9.3,13.6-11.4 c2.9-1.1,5.9-1.7,9-1.7c3.7-0.1,7.4,0.1,11-0.7c5.8-1.4,11.3-6.7,9.3-13.1c-1.7-5.4-7.2-9.6-12.2-11.9c-11.3-5.3-25.5-3.8-34.9,4.5 c-0.8,0.8-0.7,2.1,0.1,2.8c0.4,0.3,0.8,0.5,1.3,0.6c3.2-0.1,4.8-3.2,4.3-6.1c-0.5-3.4-3.9-5-7.1-5c-3.6-0.1-7.4,1.2-10.7,2.6 c-3.5,1.5-6.7,3.5-9.5,6.1c-1.2,1.1-0.3,3.6,1.4,3.4c3.2-0.4,4.6-3.3,3.9-6.3c-0.9-3.6-4.7-4.7-8-4.2c-2,0.4-3.9,0.9-5.8,1.7 c-2.1,0.7-4.1,1.5-6.2,2.3c-4,1.6-8,3.4-11.9,5.3c-7.9,3.9-15.4,8.5-22.8,13.1c-7.7,4.8-15.3,9.6-23,14.4 c-7.1,4.4-14.2,8.9-20.2,14.6c-5.4,5.1-10.3,12.1-9.2,19.9c1,6.8,6.5,12.4,12.6,15.1c7.7,3.3,16.4,3,24.6,2.5 c17.3-1,34.4-4.2,50.9-9.6c2.1-0.7,4.2-1.4,6.2-2.1C479.2,393.4,478.1,389.6,475.8,390.4L475.8,390.4z"/> +</svg> \ No newline at end of file diff --git a/logo/hero-light.svg b/logo/hero-light.svg new file mode 100644 index 0000000000..73e417c1f7 --- /dev/null +++ b/logo/hero-light.svg @@ -0,0 +1,29 @@ +<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" id="artwork" x="0px" y="0px" viewBox="0 0 1000 1000" style="enable-background:new 0 0 1000 1000;" xml:space="preserve"> +<style type="text/css"> + .st0{fill:#61D790;} +</style> +<path class="st0" d="M939.2,944.4H726.9c-1.3,0-2.7,0-4,0c-7.1,0-14.1-2.2-19.9-6.3c-9.4-6.7-14.1-17.5-14.1-28.9v-308 c0-0.7,0-1.5,0-2.2c0-18.7,15.1-33.8,33.8-33.8c0,0,0,0,0,0c0.9,0,1.9,0,2.8,0h201.1c4.1,0,8.3,0,12.4,0 c17.3,0.1,32.4,13.4,33.8,30.9c0.3,3.2,0.1,6.4,0.1,9.5v297.3c0,2.6,0,5.1,0,7.7C972.8,929.2,957.8,944.2,939.2,944.4 c-2.5,0-2.5,4,0,3.9c20-0.2,36.5-15.9,37.7-35.9c0.2-2.9,0-5.9,0-8.8V605c0.1-4,0-8.1-0.5-12.1c-2.8-18.2-19.1-31.6-37.3-31.7 c-3.9,0-7.8,0-11.7,0H725.3c-1.2,0-2.4,0-3.6,0c-9.6,0.3-18.8,4.2-25.6,11c-7.7,7.7-11.1,17.8-11.1,28.5v308.7 c0,12.5,5.3,24,15.4,31.5c6.5,4.8,14.4,7.3,22.4,7.3c1.2,0,2.4,0,3.6,0h212.7C941.8,948.3,941.8,944.4,939.2,944.4z"/> +<path class="st0" d="M687,647.6h276.3c3.7,0,7.5,0.1,11.2,0c0.2,0,0.3,0,0.5,0c2.5,0,2.5-3.9,0-3.9H698.7c-3.7,0-7.5-0.1-11.2,0 c-0.2,0-0.3,0-0.5,0C684.4,643.6,684.4,647.6,687,647.6L687,647.6z"/> +<path class="st0" d="M687,756.7h276.3c3.7,0,7.5,0.1,11.2,0c0.2,0,0.3,0,0.5,0c2.5,0,2.5-3.9,0-3.9H698.7c-3.7,0-7.5-0.1-11.2,0 c-0.2,0-0.3,0-0.5,0C684.4,752.8,684.4,756.7,687,756.7L687,756.7z"/> +<path class="st0" d="M687,865.9h276.3c3.7,0,7.5,0.1,11.2,0c0.2,0,0.3,0,0.5,0c2.5,0,2.5-3.9,0-3.9H698.7c-3.7,0-7.5-0.1-11.2,0 c-0.2,0-0.3,0-0.5,0C684.4,862,684.4,865.9,687,865.9L687,865.9z"/> +<path d="M258.7,241.1c-7.3-29-14.1-58.2-20.2-87.4c-1.5-7-3.1-14.1-4.2-21.2c-1.1-6.8-1.5-13.9,0.4-20.7 c3.1-11.3,12.5-19.1,22.2-24.8c11.2-6.6,24-12.3,37.1-12.8c11.1-0.4,23.3,3.4,29.9,12.9c6.2,8.8,7.2,20.1,9.6,30.3 c1,5.3,2.8,10.4,5.3,15.2c1.3,2.4,2.9,4.7,4.8,6.7c1.8,2,4.1,4.1,3.4,7c-0.3,1.6-1,3.1-1.5,4.7c-0.4,1.8-0.8,3.6-0.9,5.4 c-0.3,3.4-0.2,6.9,0.5,10.3c1.2,6.6,4.7,12.3,8.2,17.9c3.3,5.4,6.9,11.1,7.2,17.6c0.3,5.3-2.2,10.6-7.4,12.5c-2,0.8-4.2,0.7-6.1-0.2 c-1-0.5-2.2-0.2-2.7,0.8c-0.2,0.3-0.2,0.6-0.2,0.9c-0.8,8.9-8.7,15.4-17.6,14.6c-4.2-0.4-8-2.4-10.7-5.5c-0.6-0.6-1.6-0.8-2.4-0.3 c-8.1,6.2-20.2,1.8-25.7-6c-7.5-10.8-6.3-24.8-5.2-37.2c0.7-7.2,1.4-14.5,0.6-21.8c-0.7-6.2-2.3-12.9-7-17.4c-4.1-4-11-5.8-16.1-2.5 c-4.5,2.9-5.9,8.1-6,13.1c-0.2,5.5,1.4,11,0.4,16.4c-0.7,4.2-2.8,9.7-7.9,9.4c-2.5-0.1-2.5,3.8,0,3.9c5.1,0.3,8.6-3.4,10.4-7.8 c1.9-4.7,1.8-9.5,1.3-14.4c-0.5-4.6-1.1-10.2,1.2-14.5c2.6-4.8,8.7-4.8,12.8-1.8c5,3.8,6.4,10.9,7,16.7c0.7,7.5-0.2,15.1-0.9,22.5 c-1.1,12.5-1.6,26.5,5.9,37.2c6.5,9.4,21.1,15,31.1,7.4l-2.4-0.3c7.4,8.3,20.1,9.1,28.4,1.7c3.9-3.5,6.3-8.3,6.7-13.5l-3,1.7 c5.1,2.7,11.1,0.8,15-3.1c4.6-4.6,5.3-11.3,3.8-17.4c-1.6-6.9-5.7-12.7-9.3-18.6c-3.6-6-6.1-12.3-6-19.4c0.1-3.6,0.7-7.1,1.9-10.4 c0.6-1.5,0.8-3.2,0.7-4.8c-0.2-1.6-0.9-3.2-1.9-4.5c-1.7-2.3-3.9-4.3-5.5-6.7c-1.5-2.3-2.7-4.7-3.6-7.3c-3.9-10.7-4.3-22.3-8.5-32.8 c-2-5.4-5.3-10.2-9.7-13.9c-4.8-3.9-10.5-6.6-16.5-7.8c-13.6-2.8-27.2,1.1-39.5,6.8c-11.1,5.1-22.4,11.7-29.6,21.9 c-3.8,5.5-6,11.9-6.5,18.6c-0.6,7.2,0.6,14.4,2,21.5c6,30.6,13,61,20.5,91.2c0.9,3.7,1.8,7.3,2.8,11 C255.5,244.6,259.3,243.5,258.7,241.1L258.7,241.1z"/> +<path d="M316.5,133.5c3,2.6,7.2,3.5,11,2.4c1-0.3,1.6-1.4,1.3-2.4c-0.3-1-1.4-1.6-2.4-1.4c-0.4,0.1-0.7,0.2-1.1,0.3 c-0.1,0-0.2,0-0.3,0c0.1,0,0.4,0,0.1,0c-0.2,0-0.4,0-0.6,0c-0.4,0-0.7,0-1.1,0c-0.2,0-0.4-0.1-0.6-0.1c-0.6,0-1.1,0.4-1.1,1 c0,0.6,0.4,1.1,1,1.1c0.6,0,1.1-0.4,1.1-1c0-0.5-0.4-1-0.9-1.1c-0.1,0-0.2,0-0.3-0.1c-0.4-0.1-0.7-0.2-1.1-0.3 c-0.2-0.1-0.4-0.1-0.5-0.2c-0.3-0.1,0.3,0.2,0.1,0c-0.1,0-0.2-0.1-0.3-0.1c-0.3-0.2-0.7-0.3-1-0.5c-0.2-0.1-0.3-0.2-0.5-0.3 c0,0-0.3-0.2-0.1-0.1c0.2,0.2-0.1-0.1-0.1-0.1c-0.8-0.8-2-0.8-2.8,0S315.8,132.7,316.5,133.5C316.5,133.5,316.5,133.5,316.5,133.5 L316.5,133.5z"/> +<path d="M319.6,159c1.7,5.2,6.4,8.9,11.8,9.4c1.1,0,2-0.9,2-2c0-1.1-0.9-1.9-2-2c-0.1,0-0.6,0,0,0c-0.1,0-0.3,0-0.4-0.1 c-0.2,0-0.5-0.1-0.7-0.2c-0.5-0.1-1-0.3-1.5-0.5c0.4,0.3,0.1,0-0.1,0c-0.1-0.1-0.3-0.1-0.4-0.2c-0.2-0.1-0.4-0.2-0.6-0.3 c-0.2-0.1-0.5-0.3-0.7-0.5c-0.1-0.1-0.2-0.1-0.3-0.2c-0.1,0-0.1-0.1-0.2-0.1c0.2,0.1,0.2,0.2,0.1,0.1c-0.4-0.3-0.8-0.7-1.2-1.1 c-0.2-0.2-0.4-0.4-0.5-0.6c-0.1-0.1-0.4-0.4,0-0.1c-0.1-0.1-0.1-0.2-0.2-0.3c-0.3-0.4-0.6-0.9-0.8-1.3c-0.1-0.1-0.1-0.2-0.2-0.3 c0-0.1-0.1-0.1-0.1-0.2c-0.1-0.1,0-0.1,0.1,0.1c-0.1-0.3-0.2-0.6-0.3-0.8c-0.3-1.1-1.4-1.7-2.4-1.4S319.3,157.9,319.6,159 C319.6,159,319.6,159,319.6,159L319.6,159z"/> +<path d="M218.8,114.3c1.3,4.5,1.7,9.3,2.6,13.9c0.4,2.3,1.4,4.1,3.7,5c2.3,0.9,4.4-0.1,6.5-0.9c10.8-3.9,21.7-7.9,32.5-11.8 c19.3-7,38.7-14.4,56.2-25.4c4.4-2.7,8.6-5.6,12.6-8.9c1.9-1.5,2.7-4.1,2-6.4c-0.7-2.7-1.7-5.3-2.8-7.9c-1-2.5-2.2-5.1-3.4-7.5 c-1.1-2.1-2.4-4.2-4.9-4.6c-2.2-0.4-4,0.8-5.9,1.7c-0.7,0.4-1.4,0.6-2.2,0.6c-1.2-0.2-2.4-0.7-3.4-1.3c-18.4-8.7-39.3-10.9-59.3-7.7 c-5,0.8-9.8,1.9-14.6,3.4c-4.6,1.4-9.6,2.9-13.5,5.8c-3.9,2.8-7,7.1-6.7,12.1c0.1,4.3,3.2,7.9,7.4,8.9c2,0.5,4,0.1,5.8-0.9 c0.9-0.6,1.2-1.8,0.7-2.7c-0.1-0.1-0.2-0.3-0.3-0.4c-3.3-2.9-6.6,1.5-8.1,4.1c-1.8,3.2-2.9,6.7-3.1,10.3c-0.2,3.6,0.5,7.3,1.8,10.7 c0.6,1.6,1.4,3.5,0.9,5.2c-0.3,1.2-1.1,2.3-2.4,2.2c-2.5-0.2-2.5,3.8,0,3.9c3.3,0.2,5.8-2.7,6.3-5.8c0.6-3.8-1.6-7.1-2.4-10.6 c-1-4.8-0.2-9.8,2.2-14c0.4-0.7,0.8-1.3,1.3-1.9c0.2-0.3,0.4-0.5,0.6-0.8c0.1-0.1,0.3-0.3,0.4-0.3c0.3-0.1,0.2-0.2-0.4-0.3l0.4-3.1 c-2.6,1.6-6.1-0.3-6.9-3c-1.2-4,1.5-8.1,4.7-10.3c3.6-2.5,8-3.9,12.2-5.2c4.5-1.4,9-2.5,13.6-3.3c18-3.2,36.6-1.4,53.7,5.4 c2.2,0.9,4.3,2,6.5,2.9c2,0.8,4.3,0.8,6.2-0.2c1.1-0.5,2.7-1.9,4-1.6c0.8,0.2,1.2,1,1.6,1.7c0.6,1.2,1.2,2.4,1.8,3.6 c1.1,2.3,2.1,4.7,2.9,7.1c0.4,1.1,0.8,2.2,1.1,3.3c0.3,0.9,0.9,2.2,0.4,3.2c-0.5,0.7-1.2,1.3-2,1.8c-1,0.7-2,1.5-3,2.2 c-1.9,1.4-3.8,2.7-5.8,4c-15.8,10.3-33.3,17.4-50.9,23.9c-9.6,3.6-19.2,7-28.8,10.5l-7.5,2.7l-3.5,1.3c-0.8,0.3-1.9,0.9-2.8,0.8 c-1.7-0.3-1.7-2.6-1.9-3.9c-0.4-2.2-0.7-4.5-1.1-6.7c-0.3-2-0.7-3.9-1.3-5.9C221.9,110.8,218.1,111.8,218.8,114.3L218.8,114.3z"/> +<path d="M327.4,263.8c7.4-11,22.9-12.4,34.5-8.1c14.6,5.5,25.6,18.5,35.4,30.1c10.9,13.1,20.8,27.1,29.5,41.7 c7.2,11.8,14.2,23.6,21.9,35c4.3,6.5,9.1,12.8,14.1,18.9c2.3,2.6,4.7,5.1,7.2,7.5c1.3,1.2,3,2.8,3.2,4.7c0.1,0.7-1.1,3.2-2.1,2.4 v2.8c8.2-7.2,20.1-8.6,30.4-11c11.3-2.6,22.6-5.6,33.7-9c10.9-3.4,21.7-7.2,32.3-11.5c5.4-2.2,10.8-4.5,16.2-7 c4.4-1.9,8.6-4.4,12.4-7.4c4-3.3,7.6-7,11.8-9.9c1.9-1.4,3.9-2.5,6.1-3.3c1.1-0.4,2.3-0.7,3.4-0.8c0.5,0,1,0.1,1.5,0.3 c0.2,0.1,0.3,0.2,0.5,0.3c0.1,0.1,0.2,0.1,0.3,0.2c0.3,0.4,0.5,0.1,0.4-0.9l2.4,3.1c7.6-5.9,19.1-9.5,28.5-6.2 c3.6,1.3,7.2,4.2,7,8.4c-0.2,2.9-2.4,7.1-5.9,6.5l0.9,3.3c5.9-6.5,16.3-9.2,22.9-2.2c2.9,2.8,3.8,7.1,2.4,10.9 c-1.5,3.8-5.2,6.1-9.3,5.9l1.7,3c2.5-3.5,7.3-4.3,11.3-3.2c4.6,1.2,7.8,5.1,9.6,9.2c2.2,4.9,3,10.3,2.1,15.6 c-1,4.8-4.3,9.6-9.5,10.3c-4.5,0.6-9.8-4.2-7.3-8.8l-3.6-1.5c-1.1,3.2-4.8,5.1-7.5,6.9c-3.1,2.1-6.1,4.1-9.2,6.1 c-6.2,4.2-12.5,8.3-18.8,12.4c-12.5,8.1-25.2,16-38.2,23.3c-25.8,14.6-52.9,27.2-81.6,34.9c-14.7,3.9-29.9,6.9-45.2,7 c-11.2,0.1-23.3-1.2-33.1-6.8c-7.3-4.2-14-9.5-20.5-14.9c-8-6.7-15.7-13.8-23.2-21.1c-13.3-12.7-26-26-38-40 c-0.9-1.1-1.8-2.1-2.6-3.2c-1.6-2-4.4,0.8-2.8,2.8c11,13.8,23.5,26.5,36.1,38.8c7.7,7.6,15.6,15,23.8,22.1 c6.9,6.1,14.2,11.7,21.8,16.8c2.8,1.9,5.8,3.5,9,4.8c6.1,2.5,12.9,3.5,19.4,4.2c15.7,1.5,31.7-0.9,47-4.3 c29.4-6.6,57.3-18.7,83.7-33c26.8-14.5,52.5-31.3,77.5-48.5c2.7-1.9,5.1-3.9,6.2-7.1c0.7-2.1-2.6-3.5-3.6-1.5 c-2.8,5.3,0.3,11.9,5.7,14.1c6,2.4,12.9-1.3,16.1-6.5c3.7-6.1,3.3-14.2,1.3-20.8c-1.8-6-5.6-11.8-11.6-14.3 c-6.1-2.5-13.6-1.1-17.6,4.4c-1,1.3,0.4,2.8,1.7,3c5.4,0.5,10.4-3,12.7-7.8c2.2-4.8,1.4-10.4-2-14.5c-7.7-9.8-21.8-7.4-29.5,1 c-0.9,1-0.7,3,0.9,3.3c8.1,1.4,13.2-8.7,9.8-15.4c-4.4-8.9-16.7-9.4-25.1-7.6c-5.9,1.3-11.4,3.9-16.1,7.6c-1.8,1.4,0.5,4.3,2.4,3.1 c1.5-0.9,2.1-2.8,1.2-4.3c-0.1-0.3-0.3-0.5-0.5-0.7c-1.5-1.6-3.6-2.4-5.8-2.2c-4.7,0.5-9,3-12.7,5.8c-4,3-7.5,6.6-11.4,9.7 c-4.4,3.6-9.4,5.7-14.6,8c-23.3,10.2-47.5,18.3-72.3,24.2c-6.8,1.7-13.8,3-20.5,4.8c-6.2,1.7-12.3,4.2-17.2,8.5 c-0.8,0.8-0.8,2,0,2.8c2.6,2.2,6.1,1.1,7.9-1.7c2-3.2,0.4-6.7-1.8-9.3c-2.3-2.6-4.9-4.8-7.2-7.4c-2.5-2.8-4.9-5.7-7.1-8.7 c-9.4-12.5-17.6-25.9-25.6-39.3c-9.1-15-18.6-29.8-29.6-43.5c-5.5-7-11.4-13.8-17.7-20.1c-5.8-5.8-12.3-11.3-19.8-14.9 c-14.2-6.9-33.7-6.1-43.3,8C322.6,263.9,326,265.8,327.4,263.8L327.4,263.8z"/> +<path class="st0" d="M25,948.3h66H172c15.3,0,29.6-6.6,37.1-20.6c4.4-8.2,4.6-16.8,4.6-25.8V695.7c0-8.9,0-17.3-5.7-25 c-7.6-10.3-21-12.3-32.5-15.7l-46.2-13.4c-15.3-4.4-29.9-9.4-42.6-19.5c-19.5-15.5-32.1-38.1-35.1-62.8c-0.9-8.2-0.6-16.5-0.6-24.8 V399.9c0-7.7-0.2-15.4,0.2-23.1c1.2-23.8,8.1-47,20.1-67.7c23.7-40.7,66.2-68.7,113.1-73.9c47.3-5.2,94.2,12.1,126.8,46.7 c27.3,29.1,40.6,67,40.6,106.6v128.2c0,7.8-0.7,16.1,0.7,23.8c3.6,19,20,31.5,38.7,32.8c14.2,0.9,28.4,1.8,42.6,2.7 c14.2,0.9,29.8,0,41.3,9.8c20,17,15.6,49.9-8.3,61c-7.6,3.5-16.1,3.5-24.2,4.1l-37.3,2.7l-98.9,7l-34.7,2.5 c-9,0.6-17.7,1.1-25.2,7.2c-8.8,7.2-10.6,17.2-10.6,27.8v208c0,5.5-0.5,11.2,0.3,16.6c1.5,10.2,8.2,18.8,17.7,22.8 c4.8,2,9.7,2.2,14.8,2.2h86.3c12.1,0,23.8,0.2,35.2-4.9c19.9-9.1,32.8-28.9,32.9-50.8c0.1-11.7,0-23.4,0-35.1V529.4 c0-9.9-4.8-19.2-15.6-20.7c-6.4-0.9-12.2-1.6-15.3-8.2c-2.4-5.4-1.8-11.7,1.8-16.4c2.9-3.7,7.2-5.2,11.7-5.3c10.3-0.1,20.6,0,30.9,0 H968c2.5,0,2.5-3.9,0-3.9H435.2c-9.8,0-19.7-0.2-29.6,0c-14.8,0.3-24,17.1-15.5,29.7c2.7,3.9,6.7,6.6,11.4,7.6 c2.2,0.5,4.6,0.2,6.8,0.7c3.6,0.8,6.7,3.1,8.7,6.2c1.6,2.8,2.4,6,2.2,9.2c0.1,15.5,0,30.9,0,46.4V852c0,13.3,0.1,26.6,0,39.9 c0,20.7-12.3,39.5-31.3,47.8c-11.4,4.9-23.4,4.2-35.5,4.2h-84.9c-15.2,0-27.4-10.1-27.5-26c0-6.7,0-13.4,0-20.1V716.4 c0-7.3,0-14.7,0-22c0-14.2,9.7-25.4,24.1-26.6c22.1-1.8,44.3-3.2,66.4-4.7l96.3-6.8l25.7-1.8c15.4-1.1,29.2-9.1,35.8-23.4 c9.2-19.9,0.4-44.4-19.6-53.6c-11.5-5.3-26.1-4.4-38.4-5.2c-13.3-0.9-26.7-1.6-40-2.6c-19.5-1.3-34.6-17.6-34.5-37.1 c-0.1-24.9,0-49.9,0-74.8c0-24.1,0.1-48.2,0-72.4c0.2-85.3-68.7-154.6-154-154.8c-78.3-0.2-144.4,58.3-153.7,136.1 c-1.2,9.7-1.1,19.4-1.1,29.2v90.2c0,20.9-0.1,41.8,0,62.7c0.3,44.2,30,81.9,72.1,94.2c24.1,7,48.5,13.4,72.5,21 c10.7,3.3,18,13.1,18.2,24.2c0,1.6,0,3.3,0,4.9v205.8c0,7.3,0.5,14.8-1.9,21.8c-4.1,11.7-14,20.5-26.1,23.1 c-3.6,0.6-7.2,0.8-10.8,0.7H87.2H25C22.5,944.4,22.5,948.3,25,948.3L25,948.3z"/> +<path d="M322.6,263.8c-1.7,1.8-1.6,4.7,0.3,6.4c0,0,0,0,0,0c2.1,2,5.3,1.6,7.8,0.9c2.6-0.7,5.1-2.2,6.1-4.8c0.9-2.5,0.6-5.3-0.8-7.6 c-1.5-2.3-3.5-4.4-5.7-6.1c-2.1-1.8-4.3-3.5-6.6-5c-4.5-3.1-9.2-5.8-14.2-8.2c-10-4.7-20.4-8.4-31-11.3c-5.3-1.5-10.7-2.9-16.1-4.3 c-6.3-1.7-12.6-3.1-19.1-4c-5.5-0.7-12.5-0.9-16.2,4.1c-2.7,3.7-2.6,9.9,1.8,12.2c2.2,1.2,4.2-2.2,2-3.4c-2.5-1.3-2-5-0.4-6.9 c2.7-3.2,8.1-2.7,11.8-2.3c10.2,1.2,20.3,4.3,30.2,7c17.9,4.9,36,10.7,51.1,21.9c2.1,1.5,4.1,3.2,6,4.9c1.7,1.5,3.5,3.4,3.7,5.8 c0.2,2.5-1.7,3.7-3.9,4.3c-1,0.3-2.4,0.7-3.5,0.3c-0.3-0.1-1-0.7-0.6-1C327.2,264.8,324.4,262,322.6,263.8L322.6,263.8z"/> +<path d="M362.8,406.5c-3.8-3.5-6.9-7.8-9-12.6c-1.4-3.5-1.5-8.5,2-11c4-3,9.5,0.1,12,3.5c1.6,2.2,2.9,4.7,3.8,7.3 c1.2,3,2.3,6.1,3.4,9.1l14,37.5l3.5,9.3c1.2,2.9,2.1,5.8,3,8.8c0.8,2.6,0.9,5.4,0.3,8.1c-0.8,2.5-2.4,4.7-4.5,6.3 c-9.9,7.9-23.6,11-36,9.6c-1.1,0-2,0.9-2,2c0,1.1,0.9,1.9,2,2c12.7,1.4,25.5-1.7,36.1-8.8c2.6-1.6,4.9-3.8,6.6-6.3 c1.6-2.8,2.3-6,2-9.1c-0.5-6.7-3.6-13.1-5.9-19.4l-15.5-41.5c-2.2-6-4-12.7-8.1-17.7c-3.5-4.3-9.6-7.2-15-5c-5.7,2.3-7.8,9.5-6,15.1 c1.9,5.9,6,11.4,10.5,15.6C361.9,411.1,364.7,408.3,362.8,406.5L362.8,406.5z"/> +<path d="M355.9,482.5c-2.3-0.4-3.9-1.5-4-3.9c0-2.1,0.9-4.2,2.5-5.6c2.5-2.3,6-3.3,9.3-4c3.9-0.8,7.8-1.4,11.7-1.7 c16.1-1.3,32.2,0.9,47.8,5c15.9,4.2,31.2,10.1,46.3,16.4c15.5,6.5,30.9,13.1,46.2,20c26.3,11.9,53.7,24.3,74.3,45.1 c10,10.1,17.6,22,24.3,34.6c7.2,13.9,13.5,28.2,18.9,42.9c11.5,30.7,20.9,62.1,30.4,93.4c5,16.3,10.1,32.4,15.6,48.5 c4.2,12.3,8.6,26,3.6,38.7c-3.6,9.2-11.3,16.4-19.5,21.4c-8.8,5.4-18.7,8.8-29,10.1c-4.8,0.6-9.7,0.6-14.5-0.1 c-5.9-0.9-11.3-3.6-15.6-7.7c-5.1-4.8-9-10.7-13-16.4c-4.5-6.4-8.9-13-13.1-19.7c-16.8-26.7-30.8-55-43.5-83.8 c-6.3-14.4-12.3-29-18.2-43.6c-5.8-14.5-12.4-30.1-10.8-46c0.1-0.9,0.2-1.9,0.4-2.8c0.3-1,0.5-1.9,0.6-3l-0.7-0.3 c-0.6-0.5-0.9-0.4-0.9,0.2c-0.2,0.3-0.4,0.7-0.5,1c-0.4,0.7-1,1.2-1.6,1.7c-3,2.1-7.4,1.4-10.8,1c-2.5-0.3-2.5,3.7,0,3.9 c3.9,0.4,7.9,0.7,11.5-0.9c2.7-1.2,5.8-4.4,5.6-7.6c-0.1-1.8-1.6-3.1-3.4-3c-0.8,0.1-1.6,0.4-2.2,1c-1.1,1.3-1.2,3.5-1.5,5.2 c-0.4,2.2-0.6,4.3-0.7,6.5c-0.5,16.8,6.7,32.9,12.8,48.2c12.5,31.1,25.8,62,41.6,91.6c7.9,14.8,16.4,29.2,25.7,43.2 c4.4,6.6,8.9,13.1,13.8,19.4c4.3,5.5,9.3,10.6,15.8,13.4c9.8,4.2,21.7,3.3,31.7,0.9c10.9-2.5,21.1-7.4,29.8-14.5 c7.9-6.5,14.3-15.5,15.8-25.8c1.1-7.3-0.1-14.8-2-21.9c-2.3-8.6-5.5-17-8.3-25.4c-11.2-34-20.8-68.6-32.3-102.5 c-5.3-15.6-10.8-31-17.3-46.2c-6.4-14.8-13.5-29.7-22.8-42.9c-8.3-11.6-18.4-21.7-29.9-30c-12.3-9-25.9-16-39.7-22.6 c-16.3-7.9-32.8-15.1-49.4-22.2c-16-6.8-32-13.7-48.5-19.1c-16.4-5.4-33.4-9.3-50.6-9.8c-8.4-0.2-16.7,0.5-24.9,2.1 c-5.9,1.2-12.6,3.8-14.5,10.2c-1.6,5.1,1,10.1,6.4,11C357.3,486.8,358.4,483,355.9,482.5L355.9,482.5z"/> +<path d="M612.1,845c9.9,16.5,19.9,33,30,49.4c2.5,4.1,5.1,8.3,7.7,12.4c2.1,3.4,4.1,7.2,7,10.1c3,3,7.2,4.4,11.4,3.8 c4.1-0.4,8.2-1.2,12.2-2.4c16.1-4.6,30.4-13.8,43.9-23.6c3.3-2.4,6.6-4.9,9.9-7.4c3.2-2.5,6.5-4.9,9-8.2c4.4-5.6,6.4-14.7,0.5-20 c-2.9-2.6-6.7-3.5-10.5-3.9c-4.6-0.6-9.2-1-13.8-1.4l-7.1-0.7c-2-0.3-4-0.3-6.1,0c-3.4,0.7-5.9,3.9-4.8,7.4c0.4,1.5,1.6,2.6,3.1,2.9 c1.3,0.2,3-0.9,2.4-2.4c-5.7-15.2-12.6-29.9-20.8-44c-1.3-2.2-4.7-0.2-3.4,2c8,13.7,14.8,28.1,20.4,43l2.4-2.4 c-0.3-0.1-0.3,0-0.4-0.3c-0.1-0.4-0.1-0.7,0-1.1c0.4-1.1,1.9-1.3,2.9-1.4c2,0,3.9,0.1,5.9,0.4l6.6,0.7c4.1,0.4,8.3,0.7,12.4,1.3 c3.1,0.4,6.5,1.1,8.6,3.6c1.9,2.2,2.1,5.4,1.4,8.2c-0.9,3.3-2.8,6.3-5.4,8.5c-2.7,2.4-5.6,4.7-8.5,6.8c-3,2.3-6.1,4.5-9.1,6.8 c-12.2,8.8-25.1,17-39.6,21.3c-3.6,1.1-7.3,1.9-11.1,2.4c-1.9,0.3-3.8,0.3-5.7-0.1c-1.6-0.5-3-1.4-4.1-2.6c-2.6-2.6-4.3-6.1-6.3-9.2 c-2.3-3.7-4.6-7.4-6.9-11.1c-9.2-14.9-18.3-29.9-27.3-44.9c-1.1-1.9-2.3-3.8-3.4-5.7C614.3,840.9,610.8,842.9,612.1,845L612.1,845z"/> +<path d="M492,624.2c-22.8-0.5-45.8-1.1-68.1-6c-22.1-4.9-44.7-14.5-58.8-32.9c-7.7-10-12.6-22.4-12.7-35.1c0-5.1,0.5-11,3.9-15.1 c1.5-1.9,3.8-3,6.3-3c2.9,0.1,5.5,1.8,7.6,3.7c4.9,4.3,8.7,10,12.3,15.3c3.6,5.4,6.9,11,9.9,16.7c5.9,11.7,10.4,24.1,13.2,36.9 c5.7,26.4,3.8,53.4-3.2,79.3c-7,25.9-18.4,50.3-29.6,74.6c-2.9,6.2-5.7,12.4-8.5,18.7c-2.6,5.9-4.7,11.9-6.8,18 c-4,11.9-8.3,24.7-17.6,33.6c-16.4,15.6-43.1,10.9-59.5-2.2c-8-6.2-13.5-15.1-15.5-25.1c-1-5.3-1-10.7-0.3-16c1-7.5,2.3-15,3.6-22.5 c2.7-15.2,5.7-30.4,9.1-45.5c2.9-13,5.9-25.8,11.5-37.9c2.4-5.4,5.4-10.5,8.9-15.2c1.5-2-1.9-4-3.4-2c-7.9,10.3-12.7,23-16.3,35.3 c-2,6.9-3.4,14-5,21.1c-1.7,7.8-3.4,15.7-4.9,23.6c-1.6,7.9-3,15.8-4.4,23.7c-1.3,7.4-2.8,15-3.4,22.5c-0.9,10.8,1.6,21.8,7.8,30.7 c6.4,9.2,16,15.7,26.4,19.6c10.6,4,22.7,5.1,33.5,1.5c26.6-9,30.3-40.3,40.2-62.6c11.3-25.5,23.9-50.4,32.9-76.8 c9.1-26.6,14-54.6,10.6-82.7c-3.3-27.1-13.6-53-29.9-74.9c-3.8-5.1-7.8-10.9-13.6-13.8c-5.4-2.7-11.5-1.6-15.3,3.2 c-3.6,4.6-4.5,10.6-4.6,16.3c-0.1,6.6,1,13.1,3.2,19.4c8.3,24.1,29.6,40,52.9,48.3c22.6,8.1,47,10.1,70.7,10.9 c5.6,0.2,11.1,0.3,16.7,0.4C494.5,628.2,494.5,624.2,492,624.2L492,624.2z"/> +<path d="M261.4,806c-3.4,10.1-6.9,20.2-10.5,30.2c-1.8,5-3.7,9.9-5.6,14.8c-1.9,4.9-4,9.8-5.8,14.8c-1.6,4.4-2.4,9-1.2,13.7 c1.2,4.2,3.5,8,6.6,11c3.2,3.2,7.2,5.6,11.1,7.9c4.1,2.4,8.4,4.5,12.8,6.3c13.3,5.6,27.5,8.5,41.8,10.2c5,0.7,10,0.9,15,0.8 c4.4-0.2,9.4-0.7,13-3.3c3.9-2.8,4.6-7.3,3.1-11.6c-1.6-4.3-3.9-8.3-6.8-11.9c-1.4-1.8-3-3.6-4.6-5.3c-1.4-1.6-2.9-3.8-5.1-4.3 c-1.8-0.4-3.5,0.8-3.9,2.6c-0.1,0.5-0.1,1,0,1.4c0.5,2,3.1,1.7,3.8,0c7.3-18.6,14.6-37.3,22-55.9c0.3-1-0.3-2.1-1.4-2.4 c-1-0.3-2.1,0.3-2.4,1.4l-22,55.9h3.8l0-0.1l-0.3,1c0.2-0.3-0.4,0.1-0.4,0c0,0,0.4,0.3,0.4,0.3c0.3,0.4,0.6,0.7,1,1 c0.7,0.8,1.4,1.6,2.2,2.4c1.4,1.6,2.8,3.2,4.1,4.9c2.6,3.1,4.6,6.7,6,10.6c0.3,0.9,0.5,1.8,0.5,2.7c0,0.2,0,0.4,0,0.6 c0,0.1,0,0.2,0,0.3c0-0.1,0.1-0.4,0,0c-0.1,0.4-0.2,0.8-0.3,1.2c0,0,0.1-0.2-0.1,0.2c-0.1,0.2-0.2,0.4-0.3,0.5 c0,0.1-0.3,0.5-0.2,0.3c0.1-0.2-0.2,0.2-0.3,0.3c-0.1,0.1-0.3,0.3-0.4,0.4c-0.1,0.1-0.6,0.5-0.2,0.2c-0.3,0.2-0.7,0.5-1,0.7 c-0.7,0.4-1.5,0.8-2.2,1c-1,0.3-1.9,0.5-2.9,0.7c-2.1,0.4-4.2,0.6-6.4,0.6c-4.6,0-9.2-0.2-13.8-0.8c-1.1-0.1-1.8-0.2-2.7-0.4 c-0.8-0.1-1.7-0.2-2.5-0.4c-2.3-0.4-4.7-0.7-7-1.1c-8.7-1.5-17.2-3.9-25.5-7.1c-4.1-1.6-8-3.4-11.8-5.5c-3.8-2-7.4-4.2-10.9-6.8 c-3.1-2.3-5.5-5.4-7.1-8.9c-1.5-3.8-1.6-8.1-0.3-12c1.4-4.5,3.4-8.9,5.1-13.3c1.8-4.6,3.6-9.3,5.3-13.9c3.4-9.1,6.7-18.3,9.8-27.6 c0.8-2.3,1.6-4.6,2.3-6.9C266,804.6,262.2,803.6,261.4,806L261.4,806z"/> +<path class="st0" d="M867.6,298.2H542.8c-4.3,0-8.8-0.4-13.1,0c-0.2,0-0.4,0-0.6,0l1.9,2.5l4.1-22.8l9.9-54.4l12-65.8l10.4-56.9 l3.4-18.8c0.5-2.9,1.5-5.9,1.6-8.9c0-0.1,0-0.3,0.1-0.4l-1.9,1.4h324.8c4.3,0,8.8,0.4,13.1,0c0.2,0,0.4,0,0.6,0l-1.9-2.5L903,94.4 l-9.9,54.4l-12,65.8l-10.4,56.9c-1.7,9.2-3.5,18.4-5,27.6c0,0.1,0,0.3-0.1,0.4c-0.5,2.5,3.3,3.5,3.8,1l4.1-22.8l9.9-54.4l12-65.8 l10.4-56.9c1.7-9.2,3.5-18.4,5-27.7c0-0.1,0-0.3,0.1-0.4c0.3-1.1-0.4-2.2-1.4-2.4c-0.1,0-0.3-0.1-0.5-0.1H584.3 c-4.4,0-8.8-0.1-13.1,0c-0.2,0-0.4,0-0.6,0c-0.9,0-1.7,0.6-1.9,1.4l-4.1,22.8l-9.9,54.4l-12,65.8l-10.4,56.9 c-1.7,9.2-3.5,18.4-5,27.6c0,0.1,0,0.3-0.1,0.4c-0.3,1.1,0.4,2.2,1.4,2.4c0.1,0,0.3,0.1,0.5,0.1h324.8c4.4,0,8.8,0.1,13.1,0 c0.2,0,0.4,0,0.6,0C870.1,302.1,870.1,298.2,867.6,298.2z"/> +<path class="st0" d="M892.8,71.7l-1.1,6.1l-3,16.6l-4.5,24.5l-5.4,29.9l-6,32.8l-6,33.1l-5.6,30.8l-4.8,26.1l-3.4,18.7 c-0.5,3-1.1,5.9-1.6,8.9c0,0.1,0,0.3-0.1,0.4c-0.3,1.1,0.4,2.2,1.4,2.4c0.1,0,0.3,0.1,0.5,0.1h14.3c0.9,0,1.7-0.6,1.9-1.4l1.1-6.1 l3-16.6l4.5-24.5l5.4-29.9l6-32.8l6-33.1l5.6-30.8l4.8-26.1l3.4-18.7c0.5-3,1.1-5.9,1.6-8.9c0-0.1,0-0.3,0.1-0.4 c0.3-1.1-0.4-2.2-1.4-2.4c-0.1,0-0.3-0.1-0.5-0.1h-14.3c-2.5,0-2.5,3.9,0,3.9h14.3l-1.9-2.5l-1.1,6.1l-3,16.6l-4.5,24.5l-5.4,29.9 l-6,32.8l-6,33.1l-5.6,30.8l-4.8,26.1l-3.4,18.7c-0.5,2.9-1.3,5.9-1.6,8.9c0,0.1,0,0.3-0.1,0.4l1.9-1.4h-14.3l1.9,2.5l1.1-6.1 l3-16.6l4.5-24.5l5.4-29.9l6-32.8l6-33.1l5.6-30.8l4.8-26.1l3.4-18.7c0.5-3,1.1-5.9,1.6-8.9c0-0.1,0-0.3,0.1-0.4 C897.1,70.2,893.3,69.2,892.8,71.7z"/> +<path class="st0" d="M769.1,474.8c-7.1,0.1-14.1-1.3-20.6-4.2c-6.5-3-11.8-8-15.2-14.3c-3.3-6.2-4.6-13.3-4.9-20.2 c-0.3-7.6,0.4-15.3,1.9-22.8c0.9-4.3,1.9-8.5,2.7-12.7c0.8-4.1,1.8-8.6,1.2-12.7c-0.4-3.6-2.3-6.9-5.2-9c-2.3-1.7-5.1-2.2-7.9-2.6 c-2.7-0.4-5.5-0.6-7.9-2c-2.2-1.2-4-3-5.2-5.3c-2.4-4.5-2.5-9.9-0.3-14.5c2.9-5.9,9.3-8.4,15.6-8.7c6.7-0.1,13.3,1.7,19.1,5.2 c12.5,7.8,19.8,21.7,19.2,36.4c-0.3,6.6-3.7,12-6.7,17.7c-2.8,5.3-5.7,11.4-4.2,17.5c0.9,3.5,3.7,6.2,4.5,9.7 c0.2,0.8,0.2,1.6-0.1,2.4c-0.1,0.3-0.5,1.2-0.9,1.2c-1-0.2,0.5-1.8,0.7-2.1c3.8-5.8,8.8-10.8,14.5-14.7c12.7-8.7,28-12.1,43.3-11.7 c8.7,0.3,17.4,1.5,25.8,3.6c4.2,1,8.4,2.1,12.5,3.4c3.4,1,6.8,2.3,10,3.9c3.1,1.6,5.8,3.8,7.9,6.4c1,1.3,3.5,0.2,3.4-1.4 c-0.3-3.8-3-6.2-6.2-7.9c-2.8-1.5-6.6-2.8-8.2-5.8c-1.3-2.4-0.9-5.7-0.8-8.3c0.1-3.2,0.3-6.4,0.6-9.6c0.3-3,0.8-6,1.4-9 c0.3-1.7,0.8-3.6,2.6-4.1c2.8-0.8,4.1,1.7,5.5,3.6c1.4,1.9,3.6,3.9,3.6,6.5c0,1.1,0.9,2,2,2c0.3,0,0.6-0.1,0.9-0.3 c6.1-3.1,12.4-5.9,19.1-7c6.7-1.1,13.5-0.4,20.1,0.8c1.1,0.3,2.2-0.4,2.4-1.5c0.1-0.3,0.1-0.7,0-1c-0.2-1.5,0-3,0.7-4.4 c0.6-1.7,1.6-3.2,2.8-4.5c1-1,1.9,0.7,2.5,1.5c1.1,1.4,2.1,2.9,3,4.5c3.5,6,5.7,12.7,6.3,19.6c1.1,13.3-3.8,26.3-13.3,35.7 c-5.1,5.1-11.4,8.9-18.4,11.1l2.2,2.9c0.8-1.2,1.7-2.3,2.8-3.2c0.1-0.1,0.8-0.5,0.7-0.4c0.1,0,0.6-0.2,0.4-0.1c-0.2,0,0.3,0,0.5,0 c0.3,0-0.1,0,0.3,0c0.2,0.1,0.5,0.1,0.7,0.2c0.5,0.2,0.9,0.3,1.4,0.5c1.4,0.6,2.8,1.3,4.1,1.9c6,2.7,12.4,4.5,18.7,6.2 c3.3,0.9,6.7,1.8,10,2.8c3,0.9,6,1.9,9.1,2.7c7.1,1.8,16.7,2.1,21.2,8.8c1,1.4,1.6,3,1.6,4.7c-0.1,0.8-0.4,1.1-1.2,1.5 c-1.4,0.7-3.8,0.6-5.5,1.4c-2.3,1.1-0.3,4.5,2,3.4c-0.5,0.3,0.3,0,0.5-0.1c0.5-0.1,1-0.2,1.5-0.2c1.1-0.2,2.3-0.5,3.3-1 c2.8-1.3,3.7-4,3.2-6.9c-1.7-8.8-11.1-12.3-18.8-13.9c-4.1-0.9-8.2-1.9-12.2-3.1c-3.4-1-6.9-2-10.4-2.9c-6.9-1.9-14.1-3.6-20.6-6.6 c-2.8-1.2-6-3.5-9.1-3.4c-3.5,0-5.7,3.2-7.6,5.8c-1,1.4,0.7,3.4,2.2,2.9c14.5-4.6,26.1-15.5,31.6-29.6c5.7-14.8,3.3-32.3-5.7-45.3 c-1.2-1.7-2.5-4-4.4-5c-2-1-4.4-0.5-5.8,1.3c-2.8,3.4-5.2,7.9-4.2,12.4l2.4-2.4c-7.2-1.3-14.5-2-21.8-0.9c-7.2,1.2-14,4.1-20.4,7.5 l3,1.7c0-2.9-1.5-5.1-3.2-7.3c-1.6-2.1-2.8-4.6-5.2-5.9c-2.5-1.4-5.6-1.2-8,0.5c-1.9,1.5-2.7,3.7-3.1,6c-1.3,7-2.1,14.1-2.1,21.2 c0,3,0,6.3,1.7,9c1.2,1.7,2.8,3.1,4.6,4.2c1.9,1.2,4,2.1,5.9,3.3c1.4,0.9,2.8,2,3,3.8l3.4-1.4c-4.9-6.2-12.5-9.2-19.8-11.4 c-8.9-2.8-18.1-4.9-27.4-6.2c-17-2.2-34.5-0.7-50,7.1c-6.8,3.4-12.9,8-17.9,13.6c-1.3,1.4-2.5,2.9-3.6,4.5c-1,1.4-2.4,3.1-2.9,4.8 c-0.6,2.3,0.9,4.7,3.2,5.3c1,0.2,2.1,0.1,3-0.4c4-2.3,3.1-7.9,1.4-11.3c-0.8-1.7-2.1-3.2-2.8-5c-0.5-1.4-0.8-2.9-0.7-4.3 c0.2-3.1,1.1-6.1,2.6-8.8c1.5-3.1,3.3-6,4.8-9.1c1.6-2.9,2.8-6.1,3.5-9.4c0.7-4.4,0.6-8.8-0.2-13.2c-1.3-7.9-4.8-15.2-9.9-21.3 c-9-10.9-24.8-18.1-38.9-13.9c-6.6,1.8-11.8,7.1-13.4,13.7c-1.4,5.8-0.1,12,3.5,16.8c1.9,2.4,4.4,4.2,7.3,5.2c2.8,1,5.9,1.1,8.9,1.7 c3,0.6,5.4,1.9,6.9,4.7c2,4.1,0.9,8.9,0.1,13.2c-1.6,8.9-4.1,17.6-4.8,26.7c-0.8,7.7-0.3,15.4,1.3,23c1.7,7.6,5.6,14.5,11.2,19.8 c5.8,5.1,12.8,8.5,20.4,9.9c3.8,0.7,7.6,1.1,11.4,1.1C771.6,478.7,771.6,474.8,769.1,474.8L769.1,474.8z"/> +<path class="st0" d="M780.6,428.6c10.2-6.7,22.9-8.7,34.7-5.5c5.7,1.6,11.5,4.4,14.5,9.6c2.7,4.8,3,10.6,0.9,15.7 c-2.5,5.7-7.5,9.9-13.5,11.5l2.2,0.9l-0.1-0.2l0.3,1l0-0.2l-0.3,1c-0.2,0.1-0.2,0.2-0.1,0.1c0.3,0,0.5,0,0.8-0.1 c0.6,0,1.2-0.1,1.7-0.1l3.7-0.3l6.9-0.6c2.3-0.2,4.6-0.4,6.9-0.6c2-0.3,4.1-0.2,6.2,0.2c3.1,1,5.7,3.2,7.1,6.2 c1.1,2.1,2.2,6.2-0.5,7.7c-2.2,1.2-0.2,4.6,2,3.4c3.2-1.7,3.9-5.6,3.3-8.9c-0.7-3.5-2.5-6.7-5.2-9c-2.9-2.5-6.7-3.8-10.6-3.6 c-4.7,0.2-9.5,0.8-14.2,1.1c-2.6,0.2-5.3,0.4-7.9,0.7c-2.2,0.2-4.8,1.7-3.5,4.3c0.5,0.8,1.4,1.1,2.2,0.9c6.2-1.7,11.5-5.6,14.8-11.1 c3.3-5.6,3.9-12.3,1.7-18.4c-4.9-13.3-21.8-17.4-34.5-16.3c-7.7,0.6-15.2,3.2-21.7,7.4C776.5,426.6,778.5,430,780.6,428.6 L780.6,428.6z"/> +<path class="st0" d="M872.5,404.4c4.2-0.3,8.5,0.6,12.2,2.6c0.9,0.5,2.1,0.2,2.7-0.8c0.5-0.9,0.2-2.1-0.7-2.6 c-4.3-2.4-9.3-3.5-14.2-3.2c-1.1,0-1.9,0.9-2,2C870.5,403.5,871.4,404.4,872.5,404.4z"/> +<path class="st0" d="M919.4,395.3c-3-0.4-6.1,0.3-8.7,1.9c-1.2,0.7-2.4,1.5-3.5,2.3c-1.3,0.8-2.4,1.9-3.1,3.2 c-0.5,0.9-0.2,2.1,0.7,2.7c0.9,0.5,2.1,0.2,2.7-0.7c0.4-0.8,1.1-1.5,1.8-1.9c1-0.7,2-1.4,3.1-2c0.5-0.3,1-0.6,1.6-0.8 c0.4-0.2,0.9-0.4,1.4-0.5c0.3-0.1,0.6-0.2,0.8-0.2c0.1,0,0.3-0.1,0.4-0.1c0.3-0.1-0.2,0,0.1,0c0.3,0,0.6,0,0.9,0c0.1,0,0.3,0,0.4,0 c0,0,0.5,0.1,0.2,0c1,0.3,2.1-0.3,2.4-1.4C921,396.7,920.4,395.6,919.4,395.3L919.4,395.3z"/> +<path class="st0" d="M899.5,411.6c-0.3,0.7-0.9,1.1-1.6,1.3c-1.1,0.2-2.2,0.2-3.3,0c-1-0.3-2.1,0.3-2.4,1.4c-0.3,1,0.3,2.1,1.4,2.4 l1.9,0.6c0.1,0,0.9,0.2,0.9,0.3l-0.3-0.2c-0.2-0.7-0.3-0.8-0.3-0.4c0,0.7,0.2,1.3,0.4,1.9c0.1,0.4,0.2,0.8,0.1,1.2 c-0.2,1.2-1.3,2-2.5,1.8c-0.2,0-0.4-0.1-0.5-0.2l-1.5,3.6c1.7,0.6,3.5,0.8,5.3,0.6c0.9-0.2,1.8-0.6,2.5-1.2c0.1,0,0.6-0.5,0.4-0.4 c0.3,0,0.7,0.1,1,0.1c3.6,0.4,7.7-0.7,9.3-4.1c0.5-0.9,0.2-2.1-0.7-2.7c-0.9-0.5-2.1-0.2-2.7,0.7c-0.5,1-1.3,1.7-2.3,2 c-1.4,0.3-2.9,0.3-4.4,0.1c-0.8-0.1-1.7,0.2-2.4,0.7c-0.4,0.4-0.9,0.7-1.3,1c-1.1,0.4-2.5-0.1-3.7-0.5c-2.1-0.6-3.6,2.6-1.5,3.6 c3,1.6,6.7,0.4,8.3-2.6c0.2-0.4,0.4-0.9,0.5-1.3c0.2-0.9,0.2-1.9,0-2.8c-0.1-0.4-0.2-0.8-0.4-1.3c-0.1-0.4-0.1-0.8-0.2-1.2 c-0.6-2.1-3.2-2.5-5-3l-1,3.8c1.8,0.4,3.7,0.4,5.6,0c1.6-0.4,2.9-1.5,3.7-3c0.5-0.9,0.2-2.1-0.7-2.7 C901.3,410.4,900.1,410.7,899.5,411.6L899.5,411.6z"/> +<path d="M475.8,390.4c-14.9,5.4-30.3,9-46,10.7c-7.6,0.8-15.4,1.6-23.1,1c-6.6-0.5-13.4-2.7-17.6-8.1c-9.8-12.8,7.4-25.5,16.7-31.7 c6.6-4.4,13.5-8.5,20.2-12.8c6.9-4.3,13.8-8.8,20.9-12.9c6.8-4.1,13.9-7.7,21.2-10.8c3.7-1.6,7.4-3,11.2-4.2c1.6-0.5,6.1-2,6.6,0.8 c0,0.3,0,0.6,0,0.9c-0.1,0.1-0.3,0.3-0.1,0.3l1.4,3.4c4-3.6,8.9-6,14.2-7.1c1.8-0.4,4.6-0.9,6,0.6c0.3,0.4,0.5,0.9,0.4,1.5 c0,0.2-0.1,0.5-0.2,0.7c-0.1,0.2-0.1,0.2-0.3,0.2l1.4,3.4c7.6-6.7,18.8-8,28.1-4.7c4.6,1.6,9.8,4.9,12,9.4c2.7,5.6-3.8,9.1-8.4,9.6 c-3.5,0.4-7.1,0-10.6,0.4c-2.8,0.3-5.6,1-8.2,2.1c-5.4,2.2-10.2,6-13.6,10.7c-3.4,4.8-5.5,11.9-0.7,16.6c1.9,1.9,4.7,2.7,7.3,2.1 c2.5-0.5,4.7-1.9,7-2.9c2-0.9,4.4-1.5,6.1,0.1c1.5,1.6,2.1,3.7,1.8,5.9c-0.8,6-8.1,8.3-13.1,9.6l2.4,1.4c-0.3-0.9,0.1-1.9,1-2.3 c2.2-1.2,0.2-4.6-2-3.4c-2.4,1.3-3.5,4.1-2.8,6.7c0.3,1,1.4,1.6,2.4,1.4c5.5-1.5,12.1-3.9,14.8-9.4c2.1-4.3,1.4-10.3-2.6-13.3 c-2.2-1.5-4.9-1.9-7.4-1.1c-2.6,0.7-4.9,2.3-7.5,3.2c-2.4,0.8-4.7,0.1-5.8-2.3c-1.3-2.7-0.3-5.7,1.1-8.2c3.1-5.3,7.9-9.3,13.6-11.4 c2.9-1.1,5.9-1.7,9-1.7c3.7-0.1,7.4,0.1,11-0.7c5.8-1.4,11.3-6.7,9.3-13.1c-1.7-5.4-7.2-9.6-12.2-11.9c-11.3-5.3-25.5-3.8-34.9,4.5 c-0.8,0.8-0.7,2.1,0.1,2.8c0.4,0.3,0.8,0.5,1.3,0.6c3.2-0.1,4.8-3.2,4.3-6.1c-0.5-3.4-3.9-5-7.1-5c-3.6-0.1-7.4,1.2-10.7,2.6 c-3.5,1.5-6.7,3.5-9.5,6.1c-1.2,1.1-0.3,3.6,1.4,3.4c3.2-0.4,4.6-3.3,3.9-6.3c-0.9-3.6-4.7-4.7-8-4.2c-2,0.4-3.9,0.9-5.8,1.7 c-2.1,0.7-4.1,1.5-6.2,2.3c-4,1.6-8,3.4-11.9,5.3c-7.9,3.9-15.4,8.5-22.8,13.1c-7.7,4.8-15.3,9.6-23,14.4 c-7.1,4.4-14.2,8.9-20.2,14.6c-5.4,5.1-10.3,12.1-9.2,19.9c1,6.8,6.5,12.4,12.6,15.1c7.7,3.3,16.4,3,24.6,2.5 c17.3-1,34.4-4.2,50.9-9.6c2.1-0.7,4.2-1.4,6.2-2.1C479.2,393.4,478.1,389.6,475.8,390.4L475.8,390.4z"/> +</svg> \ No newline at end of file diff --git a/logo/light.svg b/logo/light.svg new file mode 100644 index 0000000000..12c67c1baf --- /dev/null +++ b/logo/light.svg @@ -0,0 +1,9 @@ +<svg width="211" height="48" viewBox="0 0 211 48" fill="none" xmlns="http://www.w3.org/2000/svg"> +<path d="M13.5876 32.2135H56.2925L42.7049 45.2039H0V26.6467L13.5876 13.6562V32.2135Z" fill="black"></path> +<path d="M69.8804 0.666016V19.2233L56.2928 32.213V13.6558H13.5879L27.1762 0.666016H69.8804Z" fill="black"></path> +<path d="M95.7499 38.7409L83.4688 7.16113H88.5697L98.5833 34.049L108.691 7.16113H113.698L101.417 38.7409H95.7485H95.7499Z" fill="black"></path> +<path d="M133.678 20.8978C132.765 19.2598 131.475 17.9441 129.805 16.9512C128.136 15.9583 126.137 15.4619 123.807 15.4619C121.477 15.4619 119.429 15.9649 117.665 16.9735C115.901 17.9821 114.525 19.381 113.534 21.1689C112.54 22.9588 112.045 25.0565 112.045 27.4621C112.045 29.8676 112.541 31.8684 113.534 33.6426C114.525 35.4168 115.901 36.8006 117.665 37.7928C119.429 38.7857 121.444 39.2821 123.712 39.2821C125.57 39.2821 127.214 38.9658 128.647 38.3345C130.079 37.7031 131.284 36.8386 132.261 35.7403C133.236 34.642 133.929 33.4022 134.34 32.0184H129.616C129.144 33.101 128.419 33.9589 127.444 34.5903C126.467 35.2216 125.222 35.5379 123.712 35.5379C122.452 35.5379 121.286 35.2439 120.216 34.6577C119.145 34.0716 118.287 33.1999 117.641 32.0413C117.084 31.0432 116.768 29.8467 116.692 28.4543H134.954C134.984 28.0338 135.008 27.65 135.024 27.3042C135.04 26.9584 135.047 26.6349 135.047 26.3343C135.047 24.3492 134.592 22.5384 133.678 20.8971V20.8978ZM116.746 25.2524C116.873 24.1803 117.171 23.2548 117.641 22.4788C118.287 21.4099 119.144 20.5991 120.216 20.0418C121.286 19.4851 122.468 19.2068 123.76 19.2068C125.585 19.2068 127.114 19.7412 128.342 20.8081C129.57 21.8769 130.231 23.357 130.325 25.2524H116.747H116.746Z" fill="black"></path> +<path d="M148.274 39.2821C146.258 39.2821 144.495 38.9665 142.984 38.3345C141.472 37.7031 140.275 36.8164 139.394 35.6729C138.512 34.53 137.977 33.1913 137.788 31.6575H142.606C142.763 32.3792 143.071 33.0414 143.527 33.6426C143.983 34.2445 144.613 34.7252 145.416 35.086C146.219 35.4469 147.171 35.6277 148.274 35.6277C149.313 35.6277 150.171 35.4849 150.848 35.1993C151.525 34.9138 152.021 34.5228 152.336 34.0264C152.651 33.5299 152.809 32.9968 152.809 32.4251C152.809 31.5835 152.596 30.9443 152.171 30.5075C151.746 30.0719 151.115 29.7255 150.282 29.4701C149.447 29.2147 148.447 28.9815 147.282 28.7706C146.179 28.5899 145.117 28.3423 144.094 28.0266C143.071 27.7109 142.157 27.3128 141.355 26.8314C140.552 26.3507 139.914 25.7416 139.441 25.0041C138.969 24.2674 138.733 23.3577 138.733 22.2744C138.733 20.9816 139.094 19.8158 139.819 18.7778C140.543 17.7404 141.574 16.9283 142.913 16.3415C144.251 15.7547 145.833 15.4619 147.66 15.4619C150.305 15.4619 152.431 16.0638 154.036 17.2662C155.642 18.4693 156.587 20.1688 156.871 22.3642H152.289C152.163 21.3418 151.69 20.5526 150.872 19.9959C150.053 19.4399 148.966 19.1616 147.613 19.1616C146.259 19.1616 145.219 19.4177 144.495 19.9285C143.77 20.44 143.408 21.1172 143.408 21.9588C143.408 22.5004 143.613 22.9817 144.022 23.4022C144.431 23.824 145.037 24.177 145.841 24.4625C146.644 24.7487 147.628 25.0114 148.793 25.2517C150.462 25.553 151.958 25.9282 153.28 26.3795C154.603 26.8307 155.657 27.4929 156.445 28.3646C157.232 29.2369 157.626 30.4845 157.626 32.1088C157.657 33.5227 157.288 34.7704 156.516 35.853C155.744 36.9355 154.658 37.7784 153.258 38.3797C151.856 38.9809 150.195 39.2821 148.274 39.2821H148.274Z" fill="black"></path> +<path d="M161.971 48.6662V16.0039H166.222L166.694 19.5228C167.198 18.8312 167.828 18.1769 168.584 17.5606C169.339 16.9443 170.244 16.4407 171.3 16.049C172.354 15.6587 173.591 15.4629 175.007 15.4629C177.274 15.4629 179.266 15.9894 180.983 17.0419C182.699 18.095 184.037 19.5162 184.998 21.3055C185.958 23.0954 186.438 25.1328 186.438 27.4185C186.438 29.7042 185.95 31.7423 184.974 33.5316C183.998 35.3215 182.652 36.7276 180.936 37.7499C179.219 38.7723 177.228 39.2838 174.961 39.2838C173.102 39.2838 171.466 38.9301 170.048 38.2235C168.632 37.5168 167.514 36.5318 166.695 35.2684V48.6675H161.971L161.971 48.6662ZM174.205 35.3575C175.653 35.3575 176.936 35.0268 178.054 34.3653C179.171 33.7038 180.054 32.7712 180.699 31.5681C181.344 30.3657 181.667 28.9668 181.667 27.3727C181.667 25.7786 181.344 24.3882 180.699 23.1995C180.053 22.0115 179.171 21.0789 178.054 20.4023C176.936 19.7258 175.653 19.3872 174.205 19.3872C172.756 19.3872 171.425 19.7258 170.308 20.4023C169.19 21.0789 168.316 22.0115 167.686 23.1995C167.056 24.3882 166.741 25.7786 166.741 27.3727C166.741 28.9668 167.056 30.3657 167.686 31.5681C168.316 32.7712 169.189 33.7038 170.308 34.3653C171.425 35.0274 172.724 35.3575 174.205 35.3575Z" fill="black"></path> +<path d="M198.625 39.2821C196.672 39.2821 195.051 38.9665 193.76 38.3345C192.468 37.7031 191.508 36.8537 190.878 35.7855C190.248 34.718 189.934 33.5679 189.934 32.3341C189.934 30.8303 190.343 29.5447 191.162 28.4766C191.981 27.409 193.146 26.5897 194.657 26.018C196.168 25.4469 197.979 25.1607 200.089 25.1607H206.277C206.277 23.8377 206.071 22.7401 205.663 21.8677C205.253 20.996 204.647 20.3417 203.845 19.9056C203.042 19.47 202.026 19.2513 200.798 19.2513C199.381 19.2513 198.169 19.5748 197.161 20.2212C196.153 20.8683 195.523 21.8225 195.272 23.0859H190.548C190.737 21.4918 191.311 20.1309 192.272 19.0031C193.233 17.8753 194.469 17.0029 195.98 16.3867C197.492 15.7704 199.098 15.4619 200.798 15.4619C203.033 15.4619 204.908 15.8385 206.419 16.5897C207.93 17.3415 209.072 18.4019 209.844 19.77C210.615 21.1388 211.001 22.7702 211.001 24.6649V38.7405H206.891L206.513 34.9059C206.166 35.5078 205.757 36.0789 205.285 36.6205C204.813 37.1621 204.253 37.6285 203.607 38.0188C202.961 38.4098 202.222 38.7182 201.388 38.9435C200.553 39.1688 199.632 39.2821 198.624 39.2821H198.625ZM199.522 35.6277C200.529 35.6277 201.443 35.4325 202.262 35.0409C203.081 34.6505 203.781 34.1161 204.364 33.4396C204.946 32.763 205.387 31.9961 205.686 31.1388C205.985 30.2815 206.15 29.402 206.182 28.4995V28.3639H200.561C199.207 28.3639 198.112 28.5217 197.278 28.8374C196.444 29.1531 195.837 29.5821 195.459 30.123C195.081 30.6647 194.893 31.296 194.893 32.0177C194.893 32.7394 195.074 33.4094 195.436 33.9353C195.798 34.4619 196.325 34.8758 197.019 35.1758C197.711 35.477 198.546 35.627 199.522 35.627L199.522 35.6277Z" fill="black"></path> +</svg> \ No newline at end of file diff --git a/playground/css/playground.css b/playground/css/playground.css deleted file mode 100644 index 270117bb7d..0000000000 --- a/playground/css/playground.css +++ /dev/null @@ -1,673 +0,0 @@ -/* Copyright Vespa.ai. All rights reserved. */ - -:root { - /* Light theme (default) */ - --bg-color: #fff; - --text-color: #333; - --header-bg: #61D790; - --header-shadow: #D0F3DE; - --header-text: #2E2F27; - --border-color: #bbb; - --light-border: #ddd; - --frame-header-bg: #f5f8fe; - --frame-bg: #fff; - --action-bg: #eee; - --tensor-label-color: #555; - --tensor-value-bg: #f2f4f6; - --tensor-value-border: #e5e7eb; - --table-header-bg: #f9fafb; - --table-cell-bg: #fff; - --tooltip-bg: #555; - --tooltip-text: #fff; - --notification-info-bg: #e3f2fd; - --notification-info-color: #0d47a1; - --notification-info-border: #2196f3; - --notification-warning-bg: #fff8e1; - --notification-warning-color: #ff6f00; - --notification-warning-border: #ffc107; - --notification-success-bg: #e8f5e9; - --notification-success-color: #2e7d32; - --notification-success-border: #4caf50; -} - -[data-theme="dark"] { - /* Dark theme */ - --bg-color: #1a1a1a; - --text-color: #e0e0e0; - --header-bg: #61D790; /* Preserved bright green from light theme */ - --header-shadow: #2c5e3f; /* Darker shadow for contrast */ - --header-text: #1a1a1a; /* Dark text for contrast with bright green */ - --border-color: #555; - --light-border: #444; - --frame-header-bg: #2a2a2a; - --frame-bg: #2a2a2a; - --tensor-value-bg: #333; - --tensor-value-border: #444; - --tensor-label-color: #ccc; - --table-header-bg: #333; - --table-cell-bg: #2a2a2a; - --tooltip-bg: #444; - --tooltip-text: #e0e0e0; - --action-bg: #333; - --notification-info-bg: #1a365d; - --notification-info-color: #90caf9; - --notification-info-border: #1976d2; - --notification-warning-bg: #4d3800; - --notification-warning-color: #ffcc80; - --notification-warning-border: #ff9800; - --notification-success-bg: #1b4d1b; - --notification-success-color: #a5d6a7; - --notification-success-border: #43a047; -} - -html, body { - font: 0.9rem "Roobert", "Arial"; /* ~14px base font */ - width: 100%; - margin: 0; - padding: 0; - color: var(--text-color); -} - -body { - padding-top: 65px; - background: var(--bg-color); - transition: background-color 0.3s ease, color 0.3s ease; -} - -/* Notification system */ -.notification { - position: fixed; - top: 70px; - left: 50%; - transform: translateX(-50%); - padding: 12px 20px; - border-radius: 4px; - box-shadow: 0 3px 6px rgba(0, 0, 0, 0.16); - z-index: 1000; - max-width: 80%; - text-align: center; - font-weight: 500; -} - -.notification.info { - background-color: var(--notification-info-bg); - color: var(--notification-info-color); - border-left: 4px solid var(--notification-info-border); -} - -.notification.warning { - background-color: var(--notification-warning-bg); - color: var(--notification-warning-color); - border-left: 4px solid var(--notification-warning-border); -} - -.notification.success { - background-color: var(--notification-success-bg); - color: var(--notification-success-color); - border-left: 4px solid var(--notification-success-border); -} - -/* Edit mode indicator */ -.edit-mode-active { - border: 2px solid #2196f3 !important; - box-shadow: 0 0 8px rgba(33, 150, 243, 0.5); -} - -#header { - display: grid; - width: 100%; - height: 55px; - position: fixed; - top: 0; - background: var(--header-bg); - box-shadow: 5px 5px 5px var(--header-shadow); - color: var(--header-text); - z-index: 1100; - transition: background-color 0.3s ease, color 0.3s ease; -} - -#header-global-commands { - display: flex; - align-items: center; - margin: auto; - width: 1000px; -} - -#header-global-commands .title { - font-size: 1.125rem; /* ~18px */ - font-weight: 700; -} - -.wrapper { - display: flex; - align-items: center; - justify-content: space-between; - width: 100%; -} - -.logo { - object-fit: cover; - width: auto; - height: 27px; - margin-right: 34px; -} - -.commands > a { - color: var(--header-text); - font-size: 1rem; /* ~16px */ - font-weight: 600; - text-decoration: none; - margin-left: 25px; -} - -#content { - background: var(--bg-color); - transition: background-color 0.3s ease; -} - -#footer { - margin-top: 50px; - padding-top: 25px; - padding-bottom: 40px; - text-align: center; - border-top: 1px solid var(--border-color); -} - -#setup-container { - width: 1000px; - margin: 0 auto; -} - -#setup-container > textarea { - border-radius: 10px; - border: 1px solid #bbb; -} - -#frame-label { - width: 10em; - text-align: left; -} - -.block { - display: inline-block; -} - -.right { - float: right; -} - -.frame { - border-radius: 10px; - border: 1px solid var(--border-color); - width: 1000px; - margin: 10px auto; - box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); -} - -.frame > .frame-header { - border-top-left-radius: 10px; - border-top-right-radius: 10px; - border-bottom: 1px solid var(--border-color); - margin: 0px; - background: var(--frame-header-bg); - text-align: left; - font-size: 0.8rem; /* ~13px */ - padding: 2px 10px; - height: 20px; - transition: background-color 0.3s ease; -} - -.frame > .frame-content { - border-bottom-left-radius: 10px; - border-bottom-right-radius: 10px; - padding: 10px 10px; - background: var(--frame-bg); - margin: 0px; - transition: background-color 0.3s ease; -} - -.selected { - border: 2px solid #61D790; - box-shadow: 0 6px 12px rgba(97, 215, 144, 0.5), 0 0 0 2px rgba(97, 215, 144, 0.2); - transform: translateY(-2px); - transition: all 0.2s ease; -} - -.error { - color: red; -} - -hr { - border-top: 1px solid #ccc; -} - -p { - margin: 0.5em 0em 0.5em 0em; -} - -input { - font: 0.9rem monospace; /* ~14px */ - background-color: var(--frame-bg); - color: var(--text-color); - border: 1px solid var(--border-color); - transition: background-color 0.3s ease, color 0.3s ease, border-color 0.3s ease; -} - -.header-text { - padding: 3px 0px 0px 0px; -} - -button { - background-color: var(--frame-bg); - border: 1px solid var(--border-color); - color: var(--text-color); - padding: 2px 10px; - margin: 2px; - border-radius: 4px; - cursor: pointer; - font-size: 0.8rem; /* ~13px */ - transition: background-color 0.3s ease, color 0.3s ease, border-color 0.3s ease; -} - -button:disabled { - color: var(--border-color); - border: 1px solid var(--light-border); - opacity: 0.6; - cursor: not-allowed; -} - -textarea { - font: 1em monospace; - white-space: pre; - overflow-wrap: normal; - overflow-x: scroll; - background-color: var(--frame-bg); - color: var(--text-color); - border: 1px solid var(--border-color); - transition: background-color 0.3s ease, color 0.3s ease, border-color 0.3s ease; -} - -table { - margin: 0px; -} - -th { - text-align: center; -} - -td { - padding: 3px 5px; - vertical-align: top; - text-align: left; -} - -td.header { - border-bottom: 1px solid #aaa; - font-weight: 600; -} - -td.label { - text-align: right; -} - -td.code { - font: 0.9rem monospace; /* ~14px */ - padding-top: 4px; - width: 800px; -} - -td.actions { - border: 1px solid var(--border-color); - background: var(--action-bg); - text-align: center; - border-radius: 10px; -} - -/* Modern table styling for tensor tables */ -.tensor-table { - border-collapse: collapse; - width: auto; - font-size: 0.875rem; /* 14px */ - margin: 0; -} - -.tensor-table th, .tensor-table td { - border: 1px solid var(--light-border); - /* padding: 6px 12px; */ - padding: 3px 10px; - text-align: left; -} - -.tensor-table th { - background-color: var(--table-header-bg); - font-weight: 600; - color: var(--text-color); -} - -.tensor-table td { - background-color: var(--table-cell-bg); -} - -td.data { - padding: 5px; - border: 1px solid black; - font-size: 0.8rem; /* ~13px */ -} - -.hidden { - display: none; -} - -#add_frames { - text-align: center; - padding-top: 34px; -} - -#add_frames > a { - margin: 0px 20px; - padding: 8px 16px; - color: var(--bg-color); - background-color: var(--header-bg); - font-size: 1rem; - font-weight: 600; - display: inline-flex; - align-items: center; - gap: 8px; - border-radius: 6px; - box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); - transition: all 0.2s ease; -} - -#add_frames > a:hover { - transform: translateY(-2px); - box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15); -} - -#setup-container > a { - margin: 0px 20px; - display: inline-flex; - align-items: center; - vertical-align: middle; - padding: 5px 10px; -} - -a { - color: var(--text-color); - font-weight: 600; - text-decoration: none; - transition: color 0.3s ease; -} - -a > svg { - position: static; - top: 6px; -} - -/* Fix alignment for dark/light mode toggle icon */ -#toggle-theme-cmd > svg { - vertical-align: middle; - position: relative; - top: -3px; -} - -a.header > svg { - position: static; - top: 0px; -} - -a.header { - margin-left: 4px; - position: relative; - display: inline-flex; - align-items: center; - color: var(--text-color); - transition: color 0.3s ease; -} - -a.header:hover { - color: var(--header-bg); -} - -/* Make save and cancel icons bigger and bolder */ -a.save-button, a.cancel-button { - color: var(--text-color); - transition: color 0.3s ease; -} - -a.save-button:hover, a.cancel-button:hover { - color: var(--header-bg); -} - -a.save-button svg, a.cancel-button svg { - width: 24px; - height: 24px; - stroke-width: 1.5; - margin-right: 6px; -} - -a.header-space { - margin-right: 30px; -} - -a.disabled { - opacity: 0.5; - cursor: not-allowed; - pointer-events: none; - color: var(--border-color) !important; -} - -/* Simplified tooltip styles */ -.tooltip { - position: relative; -} - -/* Special case for header tooltips */ -#header .tooltip .tooltip-text { - bottom: auto; - top: 150%; -} - -#header .tooltip .tooltip-text::after { - top: auto; - bottom: 100%; - border-color: transparent transparent var(--tooltip-bg) transparent; -} - -.tooltip .tooltip-text { - visibility: hidden; - background: var(--tooltip-bg); - color: var(--tooltip-text); - font-size: 0.8rem; /* ~13px */ - padding: 5px 10px; - border-radius: 4px; - position: absolute; - z-index: 1050; - bottom: 150%; - left: 50%; - transform: translateX(-50%); - opacity: 0; - white-space: nowrap; - pointer-events: none; - transition: opacity 0.3s; -} - -.tooltip .tooltip-text::after { - content: ""; - position: absolute; - top: 100%; - left: 50%; - margin-left: -6px; - border-width: 6px; - border-style: solid; - border-color: var(--tooltip-bg) transparent transparent transparent; -} - -.tooltip:hover .tooltip-text { - visibility: visible; - opacity: 1; - transition-delay: 0.3s; -} - -.debug-info-text { - font-size: 0.9em; - width: 800px; - border: 1px solid #ccc; -} - -/* Fix margins for markdown headings in comments */ -.frame-content h1, .frame-content h2, .frame-content h3, -.frame-content h4, .frame-content h5, .frame-content h6 { - margin-top: 0.5em; - margin-bottom: 0.5em; -} - -.frame-content h1:first-child, .frame-content h2:first-child, -.frame-content h3:first-child, .frame-content h4:first-child, -.frame-content h5:first-child, .frame-content h6:first-child { - margin-top: 0; -} - -/* Modern UI blocks for Expression, Type, and Value */ -.tensor-row { - display: flex; - align-items: baseline; - margin-bottom: 4px; - justify-content: flex-start; - flex-wrap: nowrap; - } - - .tensor-label { - font-weight: 500; - font-size: 0.8rem; /* ~13px */ - color: var(--tensor-label-color); - min-width: 80px; - margin-right: 8px; - text-align: right; - } - - .tensor-value { - font-family: monospace; - background-color: var(--tensor-value-bg); - padding: 4px 8px; - border-radius: 6px; - overflow-wrap: break-word; - word-wrap: break-word; - word-break: break-all; - white-space: pre-wrap; - display: inline-block; - border: 1px solid var(--tensor-value-border); - position: relative; - max-width: 100%; - transition: background-color 0.3s ease, border-color 0.3s ease; - } - - .tensor-value input { - font-family: monospace; - background-color: transparent; - border: none; - width: 100%; - padding: 0; - margin: 0; - white-space: pre-wrap; - word-break: break-word; - overflow-wrap: break-word; - } - - .tensor-value pre { - margin: 0; - white-space: pre; - overflow-x: auto; - } - - .tensor-table-container { - margin-top: 4px; - margin-left: 0; - padding: 0; - } - - .tensor-section { - margin-bottom: 10px; - } - - .tensor-section-title { - font-weight: 600; - font-size: 0.9rem; /* ~14px */ - margin-bottom: 12px; - color: var(--text-color); - padding-bottom: 8px; - border-bottom: 1px solid var(--light-border); - } - - - - .tensor-clipboard { - display: inline-flex; - align-items: center; - justify-content: center; - margin-left: 6px; - color: var(--tensor-label-color); - cursor: pointer; - transition: all 0.2s; - opacity: 0.5; - } - - .copy-button-align { - position: relative; - top: 5px; - } - - /* Icon is now always visible */ - .tensor-row:hover .tensor-clipboard { - opacity: 1; - } - - .tensor-clipboard:hover { - color: var(--header-bg); - opacity: 1; - } - - /* Value field with ellipsis */ - .tensor-value-single-line { - white-space: nowrap !important; - overflow: hidden !important; - text-overflow: ellipsis !important; - } - - /* Debug info styling */ - .debug-info-container { - margin-top: 16px; - } - - .debug-info-text { - width: 100%; - font-family: monospace; - font-size: 0.675rem; /* ~11px */ - padding: 8px; - border: 1px solid var(--light-border); - border-radius: 8px; - background-color: var(--tensor-value-bg); - transition: background-color 0.3s ease, border-color 0.3s ease; - } - - -#examples { - text-align: left; - border-bottom: 1px solid #aaa; -} - -#examples-content { - width: 1000px; - margin: 10px auto 20px auto; - text-align: left; -} - -select { - background-color: var(--frame-bg); - color: var(--text-color); - border: 1px solid var(--border-color); - transition: background-color 0.3s ease, color 0.3s ease, border-color 0.3s ease; -} - -#examples-select { - margin-top: 10px; -} diff --git a/playground/css/typography.css b/playground/css/typography.css deleted file mode 100644 index 8dab32ed57..0000000000 --- a/playground/css/typography.css +++ /dev/null @@ -1,30 +0,0 @@ -@font-face { - font-family: 'Roobert'; - src: url("/assets/fonts/Roobert-Medium.woff2") format("woff2"), url("/assets/fonts/Roobert-Medium.woff") format("woff"); - font-weight: 500; - font-style: normal; } -@font-face { - font-family: 'Roobert'; - src: url("/assets/fonts/Roobert-MediumItalic.woff2") format("woff2"), url("/assets/fonts/Roobert-MediumItalic.woff") format("woff"); - font-weight: 500; - font-style: italic; } -@font-face { - font-family: 'Roobert'; - src: url("/assets/fonts/Roobert-Regular.woff2") format("woff2"), url("/assets/fonts/Roobert-Regular.woff") format("woff"); - font-weight: 400; - font-style: normal; } -@font-face { - font-family: 'Roobert'; - src: url("/assets/fonts/Roobert-RegularItalic.woff2") format("woff2"), url("/assets/fonts/Roobert-RegularItalic.woff") format("woff"); - font-weight: 400; - font-style: italic; } -@font-face { - font-family: 'Roobert'; - src: url("/assets/fonts/Roobert-SemiBold.woff2") format("woff2"), url("/assets/fonts/Roobert-SemiBold.woff") format("woff"); - font-weight: 600; - font-style: normal; } -@font-face { - font-family: 'Roobert'; - src: url("/assets/fonts/Roobert-SemiBoldItalic.woff2") format("woff2"), url("/assets/fonts/Roobert-SemiBoldItalic.woff") format("woff"); - font-weight: 600; - font-style: italic; } diff --git a/playground/icons/favicon-16x16.png b/playground/icons/favicon-16x16.png deleted file mode 100644 index 90aed23b9f3052f3c429c337dcb244e9dfdbba1a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 835 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbKJOS+@4BLl<6e(pbstU$g(vPY0F z14ES>14Ba#1H&(%P{RubhEf9thF1v;3|2E37{m+a><YADU|?ho@Ck9%(^n@F>FTKg z5iZ5Lx@x*Qs=B(WKsCmWMiv3q=Kj_e{?_JxRt9F;7D2XQTdU*FcEz6Qj6T|G8Ek72 zXcNA@CgEy-!j-;+t9^+#Cd6InwhXnmNpQ7^ceROiac_)@ztE$nr)C*q7q+>|GR$5_ zQ`sigIdnt0sk>RkuG;vEJvMPJA#2ObyezDvo#HR|#$W1nZ;G~0_p;CQwoP(Nygnh} zT7Ss8GE)yTt8j;?L(MVA+hR_%2d^qI^)NRu)i(7sH?-FK|NsAl=%+fskP<Hm@(X57 zy!rQ53R}|K_o+8-|M~a#-<=N`cki*YCZ?VRDq&3Wc6SNm;(gf(<gk}``ns||=av&^ zvYvH)T?tTVtEY=&h{R>z^R7Zph61h^kKI*m+g*F-?%mY?(TaZ;a-Gw7&lkkm;I>*h z-fHhb4mIPS$s3z|Z3C7)I>g6k$;#WR9AqL^&XhOLW7#E*$*D#h(@u+b1$k<QrwG`D zt-ZFSYVnOJ3tex|&EwC1^VPdqfYnZ>_x5~)nddYZ&Ej>!U-RDLvpl#Vq^#Q0QbzJ% zfmQs4uVv4FxD?p-m4B%Typwl<>!0Ek`-%ezKmX6U&hEDQ|90km52Q<9C{HMfe`E>t zfNF_rL`h0wNvc(HQ7VvPFfuSQ)-^QLH8uz_GPW`>vNE;QHZZU<FbKRPaS%mAZhlH; zS|x4`3jGUeff~ZALLy3n63Z0|it^Jkb5a#bDhpB-G7CzQ85k<&JpRPPQ5dG7amxSn z8PBId49v>hddb|v%EI20MVN&ZTpCOcr!XsT4pBIL<I0I6XO74mVL#pAvA|1@;gz^x V$tNe%sX!|jJYD@<);T3K0RXU-9UTAw diff --git a/playground/icons/favicon-32x32.png b/playground/icons/favicon-32x32.png deleted file mode 100644 index 27cb33ef1652f59145b101097aa29ab9d6abeee0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1122 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE3?yBabR7dyEa{HEjtmSN`?>!lvVtU&J%W50 z7^>757#dm_7=8hT8eT9klo~KFyh>nTu$sZZAYL$MSD+0817m7{Plzi}fu6oPosq7d zny!wjuCAKCsg_lwqfNZ4v4fGmiI!()eEfy(_{+WVmwFv?d<{&sy}A<;uJk2dpWu+| zXJDr7)ti)XwJ+gXzk!9W&%~6(8xs<5PPC48c5jUGXo+zLqUPw3wWSW(J~~<|j(L9G zeL(g7j`{xXP0?}Zx@=-y49s=B`;+6&cRQDd=oxAlSm=09NJ+Xi$+<LG&p<u?LXUGv zkZV;~;*E(em7&gM!7f#yiPt8Ct}pkUp5Z$q!*6D$S6`B4ux--qNpTmt^$av(&vZH# z1h`a%IhO{-o$ZW0-5GtP#mH9Q+~3;N!(3Na_5c6>kJqVx0S2^MNswPK1A|KI)$c#9 zb*eM@Ui^0@=*8>c3z~hmfB(5V@$wCwcM&(48SmVibaLB|Wk4N_N#5=*WroQofpN^h zUgGKN%Kn^NPMn$1^?+MDC<;AY978Nl*G^6kKV%@{AnXvy)U?1tz|}YUyMuOAu4~|i zj%tNR_jVlrzn!<>7t^KRU+*nDY0c>*5xr)cT9EtZyAO{@D84D$UU6c7#U49xw;Ol8 zq#p4nq+}|q-Do=T%8=WGqte$l)4kxo%y}h&ON|TU<s?%j9&<1@8rT_Hvd*0MFj3)1 z`g50Ql@c7Ct*(JddsvtgGb6(oHXUJes!`p!D{Vs5^qCIP^~${bGyJq}*&AJT@W@#e zDR-{@evpdg_E^rAxv^8P`6#@9D?jD=0a>|~i$bJjp8I{8b)DI6byw8^#vfNQ8>^(| zh%UV)y5lpqlP#~_R^`~(`*)=7+_j&w-<)Y9$FeU!LMql(C^HC8^VVPTD)YlC1ILY1 zOl^4Y-Lq#{dY<P)319qu9{b7%opn#M@^0yWNlY={zh&-;#Jab;Cv3ib=DUl)x1Z9# z@2&V|7Tef%`$hM+*XOOpcz9c0=T;PG2pxXGZ*8&d4ui?|-M~0dEpd$~Nl7e8wMs5Z z1yT$~21drZhK9Pv1|deqRt7+1sBK_iWnger-uMrShTQy=%(P0}8n!rfI{-C=SA|5B z1SOU$6cpvBW#*(RlvEa^Dr6RvBr`Bn%z6BYhodk|L*tbH=`)^BgBX~Vx%HB{g_VW9 zCyOu(E4Vb698O_Y-W;NE`o@(LN6s9PIl_Lr!DE4!9>Xhf!IDo-rc;4dFnGH9xvX<a GXaWGJ?v~jA diff --git a/playground/index.html b/playground/index.html deleted file mode 100644 index 01b5b06b43..0000000000 --- a/playground/index.html +++ /dev/null @@ -1,176 +0,0 @@ -<!DOCTYPE html> -<!-- Copyright Vespa.ai. All rights reserved. --> -<html> - -<head> - <meta charset="utf-8"> - <title>Tensor playground - - - - - - - - - - - - - - - - - - - - -
    -
    - The tensor playground is a tool to get familiar with tensor algebra.
    - For more information see Tensor Examples, - Introduction to Vespa Tensors - and Advent of Tensors.
    - - -
    -
    - -
    - - - -
    -
    - - - -
    - - - - - - - - - diff --git a/playground/js/d3.min.js b/playground/js/d3.min.js deleted file mode 100644 index d9ff27858e..0000000000 --- a/playground/js/d3.min.js +++ /dev/null @@ -1,2 +0,0 @@ -// https://d3js.org v7.0.0 Copyright 2010-2021 Mike Bostock -!function(t,n){"object"==typeof exports&&"undefined"!=typeof module?n(exports):"function"==typeof define&&define.amd?define(["exports"],n):n((t="undefined"!=typeof globalThis?globalThis:t||self).d3=t.d3||{})}(this,(function(t){"use strict";function n(t,n){return null==t||null==n?NaN:tn?1:t>=n?0:NaN}function e(t){let e=t,r=t;function i(t,n,e,i){for(null==e&&(e=0),null==i&&(i=t.length);e>>1;r(t[o],n)<0?e=o+1:i=o}return e}return 1===t.length&&(e=(n,e)=>t(n)-e,r=function(t){return(e,r)=>n(t(e),r)}(t)),{left:i,center:function(t,n,r,o){null==r&&(r=0),null==o&&(o=t.length);const a=i(t,n,r,o-1);return a>r&&e(t[a-1],n)>-e(t[a],n)?a-1:a},right:function(t,n,e,i){for(null==e&&(e=0),null==i&&(i=t.length);e>>1;r(t[o],n)>0?i=o:e=o+1}return e}}}function r(t){return null===t?NaN:+t}const i=e(n),o=i.right,a=i.left,u=e(r).center;function c(t,n){let e=0;if(void 0===n)for(let n of t)null!=n&&(n=+n)>=n&&++e;else{let r=-1;for(let i of t)null!=(i=n(i,++r,t))&&(i=+i)>=i&&++e}return e}function f(t){return 0|t.length}function s(t){return!(t>0)}function l(t){return"object"!=typeof t||"length"in t?t:Array.from(t)}function h(t,n){let e,r=0,i=0,o=0;if(void 0===n)for(let n of t)null!=n&&(n=+n)>=n&&(e=n-i,i+=e/++r,o+=e*(n-i));else{let a=-1;for(let u of t)null!=(u=n(u,++a,t))&&(u=+u)>=u&&(e=u-i,i+=e/++r,o+=e*(u-i))}if(r>1)return o/(r-1)}function d(t,n){const e=h(t,n);return e?Math.sqrt(e):e}function p(t,n){let e,r;if(void 0===n)for(const n of t)null!=n&&(void 0===e?n>=n&&(e=r=n):(e>n&&(e=n),r=o&&(e=r=o):(e>o&&(e=o),r0){for(o=t[--i];i>0&&(n=o,e=t[--i],o=n+e,r=e-(o-n),!r););i>0&&(r<0&&t[i-1]<0||r>0&&t[i-1]>0)&&(e=2*r,n=o+e,e==n-o&&(o=n))}return o}}class y extends Map{constructor(t,n=x){if(super(),Object.defineProperties(this,{_intern:{value:new Map},_key:{value:n}}),null!=t)for(const[n,e]of t)this.set(n,e)}get(t){return super.get(_(this,t))}has(t){return super.has(_(this,t))}set(t,n){return super.set(b(this,t),n)}delete(t){return super.delete(m(this,t))}}class v extends Set{constructor(t,n=x){if(super(),Object.defineProperties(this,{_intern:{value:new Map},_key:{value:n}}),null!=t)for(const n of t)this.add(n)}has(t){return super.has(_(this,t))}add(t){return super.add(b(this,t))}delete(t){return super.delete(m(this,t))}}function _({_intern:t,_key:n},e){const r=n(e);return t.has(r)?t.get(r):e}function b({_intern:t,_key:n},e){const r=n(e);return t.has(r)?t.get(r):(t.set(r,e),e)}function m({_intern:t,_key:n},e){const r=n(e);return t.has(r)&&(e=t.get(e),t.delete(r)),e}function x(t){return null!==t&&"object"==typeof t?t.valueOf():t}function w(t){return t}function M(t,...n){return N(t,w,w,n)}function A(t,...n){return N(t,Array.from,w,n)}function T(t,n){for(let e=1,r=n.length;et.pop().map((([n,e])=>[...t,n,e]))));return t}function S(t,n,...e){return N(t,w,n,e)}function E(t,n,...e){return N(t,Array.from,n,e)}function k(t){if(1!==t.length)throw new Error("duplicate key");return t[0]}function N(t,n,e,r){return function t(i,o){if(o>=r.length)return e(i);const a=new y,u=r[o++];let c=-1;for(const t of i){const n=u(t,++c,i),e=a.get(n);e?e.push(t):a.set(n,[t])}for(const[n,e]of a)a.set(n,t(e,o));return n(a)}(t,0)}function C(t,n){return Array.from(n,(n=>t[n]))}function P(t,...e){if("function"!=typeof t[Symbol.iterator])throw new TypeError("values is not iterable");t=Array.from(t);let[r=n]=e;if(1===r.length||e.length>1){const i=Uint32Array.from(t,((t,n)=>n));return e.length>1?(e=e.map((n=>t.map(n))),i.sort(((t,r)=>{for(const i of e){const e=n(i[t],i[r]);if(e)return e}}))):(r=t.map(r),i.sort(((t,e)=>n(r[t],r[e])))),C(t,i)}return t.sort(r)}var z=Array.prototype.slice;function D(t){return function(){return t}}var q=Math.sqrt(50),R=Math.sqrt(10),F=Math.sqrt(2);function O(t,n,e){var r,i,o,a,u=-1;if(e=+e,(t=+t)===(n=+n)&&e>0)return[t];if((r=n0){let e=Math.round(t/a),r=Math.round(n/a);for(e*an&&--r,o=new Array(i=r-e+1);++un&&--r,o=new Array(i=r-e+1);++u=0?(o>=q?10:o>=R?5:o>=F?2:1)*Math.pow(10,i):-Math.pow(10,-i)/(o>=q?10:o>=R?5:o>=F?2:1)}function U(t,n,e){var r=Math.abs(n-t)/Math.max(0,e),i=Math.pow(10,Math.floor(Math.log(r)/Math.LN10)),o=r/i;return o>=q?i*=10:o>=R?i*=5:o>=F&&(i*=2),n0?(t=Math.floor(t/i)*i,n=Math.ceil(n/i)*i):i<0&&(t=Math.ceil(t*i)/i,n=Math.floor(n*i)/i),r=i}}function Y(t){return Math.ceil(Math.log(c(t))/Math.LN2)+1}function L(){var t=w,n=p,e=Y;function r(r){Array.isArray(r)||(r=Array.from(r));var i,a,u=r.length,c=new Array(u);for(i=0;i=l)if(t>=l&&n===p){const t=I(s,l,e);isFinite(t)&&(t>0?l=(Math.floor(l/t)+1)*t:t<0&&(l=(Math.ceil(l*-t)+1)/-t))}else h.pop()}for(var d=h.length;h[0]<=s;)h.shift(),--d;for(;h[d-1]>l;)h.pop(),--d;var g,y=new Array(d+1);for(i=0;i<=d;++i)(g=y[i]=[]).x0=i>0?h[i-1]:s,g.x1=i=n)&&(e=n);else{let r=-1;for(let i of t)null!=(i=n(i,++r,t))&&(e=i)&&(e=i)}return e}function H(t,n){let e;if(void 0===n)for(const n of t)null!=n&&(e>n||void 0===e&&n>=n)&&(e=n);else{let r=-1;for(let i of t)null!=(i=n(i,++r,t))&&(e>i||void 0===e&&i>=i)&&(e=i)}return e}function X(t,e,r=0,i=t.length-1,o=n){for(;i>r;){if(i-r>600){const n=i-r+1,a=e-r+1,u=Math.log(n),c=.5*Math.exp(2*u/3),f=.5*Math.sqrt(u*c*(n-c)/n)*(a-n/2<0?-1:1);X(t,e,Math.max(r,Math.floor(e-a*c/n+f)),Math.min(i,Math.floor(e+(n-a)*c/n+f)),o)}const n=t[e];let a=r,u=i;for(G(t,r,e),o(t[i],n)>0&&G(t,r,i);a0;)--u}0===o(t[r],n)?G(t,r,u):(++u,G(t,u,i)),u<=e&&(r=u+1),e<=u&&(i=u-1)}return t}function G(t,n,e){const r=t[n];t[n]=t[e],t[e]=r}function V(t,n,e){if(r=(t=Float64Array.from(function*(t,n){if(void 0===n)for(let n of t)null!=n&&(n=+n)>=n&&(yield n);else{let e=-1;for(let r of t)null!=(r=n(r,++e,t))&&(r=+r)>=r&&(yield r)}}(t,e))).length){if((n=+n)<=0||r<2)return H(t);if(n>=1)return j(t);var r,i=(r-1)*n,o=Math.floor(i),a=j(X(t,o).subarray(0,o+1));return a+(H(t.subarray(o+1))-a)*(i-o)}}function $(t,n,e=r){if(i=t.length){if((n=+n)<=0||i<2)return+e(t[0],0,t);if(n>=1)return+e(t[i-1],i-1,t);var i,o=(i-1)*n,a=Math.floor(o),u=+e(t[a],a,t);return u+(+e(t[a+1],a+1,t)-u)*(o-a)}}function W(t,n){let e,r=-1,i=-1;if(void 0===n)for(const n of t)++i,null!=n&&(e=n)&&(e=n,r=i);else for(let o of t)null!=(o=n(o,++i,t))&&(e=o)&&(e=o,r=i);return r}function Z(t){return Array.from(function*(t){for(const n of t)yield*n}(t))}function K(t,n){let e,r=-1,i=-1;if(void 0===n)for(const n of t)++i,null!=n&&(e>n||void 0===e&&n>=n)&&(e=n,r=i);else for(let o of t)null!=(o=n(o,++i,t))&&(e>o||void 0===e&&o>=o)&&(e=o,r=i);return r}function Q(t,n){return[t,n]}function J(t,n,e){t=+t,n=+n,e=(i=arguments.length)<2?(n=t,t=0,1):i<3?1:+e;for(var r=-1,i=0|Math.max(0,Math.ceil((n-t)/e)),o=new Array(i);++r+t(n)}function ht(t,n){return n=Math.max(0,t.bandwidth()-2*n)/2,t.round()&&(n=Math.round(n)),e=>+t(e)+n}function dt(){return!this.__axis}function pt(t,n){var e=[],r=null,i=null,o=6,a=6,u=3,c="undefined"!=typeof window&&window.devicePixelRatio>1?0:.5,f=1===t||4===t?-1:1,s=4===t||2===t?"x":"y",l=1===t||3===t?ft:st;function h(h){var d=null==r?n.ticks?n.ticks.apply(n,e):n.domain():r,p=null==i?n.tickFormat?n.tickFormat.apply(n,e):ut:i,g=Math.max(o,0)+u,y=n.range(),v=+y[0]+c,_=+y[y.length-1]+c,b=(n.bandwidth?ht:lt)(n.copy(),c),m=h.selection?h.selection():h,x=m.selectAll(".domain").data([null]),w=m.selectAll(".tick").data(d,n).order(),M=w.exit(),A=w.enter().append("g").attr("class","tick"),T=w.select("line"),S=w.select("text");x=x.merge(x.enter().insert("path",".tick").attr("class","domain").attr("stroke","currentColor")),w=w.merge(A),T=T.merge(A.append("line").attr("stroke","currentColor").attr(s+"2",f*o)),S=S.merge(A.append("text").attr("fill","currentColor").attr(s,f*g).attr("dy",1===t?"0em":3===t?"0.71em":"0.32em")),h!==m&&(x=x.transition(h),w=w.transition(h),T=T.transition(h),S=S.transition(h),M=M.transition(h).attr("opacity",ct).attr("transform",(function(t){return isFinite(t=b(t))?l(t+c):this.getAttribute("transform")})),A.attr("opacity",ct).attr("transform",(function(t){var n=this.parentNode.__axis;return l((n&&isFinite(n=n(t))?n:b(t))+c)}))),M.remove(),x.attr("d",4===t||2===t?a?"M"+f*a+","+v+"H"+c+"V"+_+"H"+f*a:"M"+c+","+v+"V"+_:a?"M"+v+","+f*a+"V"+c+"H"+_+"V"+f*a:"M"+v+","+c+"H"+_),w.attr("opacity",1).attr("transform",(function(t){return l(b(t)+c)})),T.attr(s+"2",f*o),S.attr(s,f*g).text(p),m.filter(dt).attr("fill","none").attr("font-size",10).attr("font-family","sans-serif").attr("text-anchor",2===t?"start":4===t?"end":"middle"),m.each((function(){this.__axis=b}))}return h.scale=function(t){return arguments.length?(n=t,h):n},h.ticks=function(){return e=Array.from(arguments),h},h.tickArguments=function(t){return arguments.length?(e=null==t?[]:Array.from(t),h):e.slice()},h.tickValues=function(t){return arguments.length?(r=null==t?null:Array.from(t),h):r&&r.slice()},h.tickFormat=function(t){return arguments.length?(i=t,h):i},h.tickSize=function(t){return arguments.length?(o=a=+t,h):o},h.tickSizeInner=function(t){return arguments.length?(o=+t,h):o},h.tickSizeOuter=function(t){return arguments.length?(a=+t,h):a},h.tickPadding=function(t){return arguments.length?(u=+t,h):u},h.offset=function(t){return arguments.length?(c=+t,h):c},h}var gt={value:()=>{}};function yt(){for(var t,n=0,e=arguments.length,r={};n=0&&(e=t.slice(r+1),t=t.slice(0,r)),t&&!n.hasOwnProperty(t))throw new Error("unknown type: "+t);return{type:t,name:e}}))}function bt(t,n){for(var e,r=0,i=t.length;r0)for(var e,r,i=new Array(e),o=0;o=0&&"xmlns"!==(n=t.slice(0,e))&&(t=t.slice(e+1)),wt.hasOwnProperty(n)?{space:wt[n],local:t}:t}function At(t){return function(){var n=this.ownerDocument,e=this.namespaceURI;return e===xt&&n.documentElement.namespaceURI===xt?n.createElement(t):n.createElementNS(e,t)}}function Tt(t){return function(){return this.ownerDocument.createElementNS(t.space,t.local)}}function St(t){var n=Mt(t);return(n.local?Tt:At)(n)}function Et(){}function kt(t){return null==t?Et:function(){return this.querySelector(t)}}function Nt(t){return null==t?[]:Array.isArray(t)?t:Array.from(t)}function Ct(){return[]}function Pt(t){return null==t?Ct:function(){return this.querySelectorAll(t)}}function zt(t){return function(){return this.matches(t)}}function Dt(t){return function(n){return n.matches(t)}}var qt=Array.prototype.find;function Rt(){return this.firstElementChild}var Ft=Array.prototype.filter;function Ot(){return Array.from(this.children)}function It(t){return new Array(t.length)}function Ut(t,n){this.ownerDocument=t.ownerDocument,this.namespaceURI=t.namespaceURI,this._next=null,this._parent=t,this.__data__=n}function Bt(t){return function(){return t}}function Yt(t,n,e,r,i,o){for(var a,u=0,c=n.length,f=o.length;un?1:t>=n?0:NaN}function Gt(t){return function(){this.removeAttribute(t)}}function Vt(t){return function(){this.removeAttributeNS(t.space,t.local)}}function $t(t,n){return function(){this.setAttribute(t,n)}}function Wt(t,n){return function(){this.setAttributeNS(t.space,t.local,n)}}function Zt(t,n){return function(){var e=n.apply(this,arguments);null==e?this.removeAttribute(t):this.setAttribute(t,e)}}function Kt(t,n){return function(){var e=n.apply(this,arguments);null==e?this.removeAttributeNS(t.space,t.local):this.setAttributeNS(t.space,t.local,e)}}function Qt(t){return t.ownerDocument&&t.ownerDocument.defaultView||t.document&&t||t.defaultView}function Jt(t){return function(){this.style.removeProperty(t)}}function tn(t,n,e){return function(){this.style.setProperty(t,n,e)}}function nn(t,n,e){return function(){var r=n.apply(this,arguments);null==r?this.style.removeProperty(t):this.style.setProperty(t,r,e)}}function en(t,n){return t.style.getPropertyValue(n)||Qt(t).getComputedStyle(t,null).getPropertyValue(n)}function rn(t){return function(){delete this[t]}}function on(t,n){return function(){this[t]=n}}function an(t,n){return function(){var e=n.apply(this,arguments);null==e?delete this[t]:this[t]=e}}function un(t){return t.trim().split(/^|\s+/)}function cn(t){return t.classList||new fn(t)}function fn(t){this._node=t,this._names=un(t.getAttribute("class")||"")}function sn(t,n){for(var e=cn(t),r=-1,i=n.length;++r=0&&(n=t.slice(e+1),t=t.slice(0,e)),{type:t,name:n}}))}function kn(t){return function(){var n=this.__on;if(n){for(var e,r=0,i=-1,o=n.length;r=0&&(this._names.splice(n,1),this._node.setAttribute("class",this._names.join(" ")))},contains:function(t){return this._names.indexOf(t)>=0}};var Dn=[null];function qn(t,n){this._groups=t,this._parents=n}function Rn(){return new qn([[document.documentElement]],Dn)}function Fn(t){return"string"==typeof t?new qn([[document.querySelector(t)]],[document.documentElement]):new qn([[t]],Dn)}qn.prototype=Rn.prototype={constructor:qn,select:function(t){"function"!=typeof t&&(t=kt(t));for(var n=this._groups,e=n.length,r=new Array(e),i=0;i=x&&(x=m+1);!(b=y[x])&&++x=0;)(r=i[o])&&(a&&4^r.compareDocumentPosition(a)&&a.parentNode.insertBefore(r,a),a=r);return this},sort:function(t){function n(n,e){return n&&e?t(n.__data__,e.__data__):!n-!e}t||(t=Xt);for(var e=this._groups,r=e.length,i=new Array(r),o=0;o1?this.each((null==n?Jt:"function"==typeof n?nn:tn)(t,n,null==e?"":e)):en(this.node(),t)},property:function(t,n){return arguments.length>1?this.each((null==n?rn:"function"==typeof n?an:on)(t,n)):this.node()[t]},classed:function(t,n){var e=un(t+"");if(arguments.length<2){for(var r=cn(this.node()),i=-1,o=e.length;++i()=>t;function Wn(t,{sourceEvent:n,subject:e,target:r,identifier:i,active:o,x:a,y:u,dx:c,dy:f,dispatch:s}){Object.defineProperties(this,{type:{value:t,enumerable:!0,configurable:!0},sourceEvent:{value:n,enumerable:!0,configurable:!0},subject:{value:e,enumerable:!0,configurable:!0},target:{value:r,enumerable:!0,configurable:!0},identifier:{value:i,enumerable:!0,configurable:!0},active:{value:o,enumerable:!0,configurable:!0},x:{value:a,enumerable:!0,configurable:!0},y:{value:u,enumerable:!0,configurable:!0},dx:{value:c,enumerable:!0,configurable:!0},dy:{value:f,enumerable:!0,configurable:!0},_:{value:s}})}function Zn(t){return!t.ctrlKey&&!t.button}function Kn(){return this.parentNode}function Qn(t,n){return null==n?{x:t.x,y:t.y}:n}function Jn(){return navigator.maxTouchPoints||"ontouchstart"in this}function te(t,n,e){t.prototype=n.prototype=e,e.constructor=t}function ne(t,n){var e=Object.create(t.prototype);for(var r in n)e[r]=n[r];return e}function ee(){}Wn.prototype.on=function(){var t=this._.on.apply(this._,arguments);return t===this._?this:t};var re=.7,ie=1/re,oe="\\s*([+-]?\\d+)\\s*",ae="\\s*([+-]?\\d*\\.?\\d+(?:[eE][+-]?\\d+)?)\\s*",ue="\\s*([+-]?\\d*\\.?\\d+(?:[eE][+-]?\\d+)?)%\\s*",ce=/^#([0-9a-f]{3,8})$/,fe=new RegExp("^rgb\\("+[oe,oe,oe]+"\\)$"),se=new RegExp("^rgb\\("+[ue,ue,ue]+"\\)$"),le=new RegExp("^rgba\\("+[oe,oe,oe,ae]+"\\)$"),he=new RegExp("^rgba\\("+[ue,ue,ue,ae]+"\\)$"),de=new RegExp("^hsl\\("+[ae,ue,ue]+"\\)$"),pe=new RegExp("^hsla\\("+[ae,ue,ue,ae]+"\\)$"),ge={aliceblue:15792383,antiquewhite:16444375,aqua:65535,aquamarine:8388564,azure:15794175,beige:16119260,bisque:16770244,black:0,blanchedalmond:16772045,blue:255,blueviolet:9055202,brown:10824234,burlywood:14596231,cadetblue:6266528,chartreuse:8388352,chocolate:13789470,coral:16744272,cornflowerblue:6591981,cornsilk:16775388,crimson:14423100,cyan:65535,darkblue:139,darkcyan:35723,darkgoldenrod:12092939,darkgray:11119017,darkgreen:25600,darkgrey:11119017,darkkhaki:12433259,darkmagenta:9109643,darkolivegreen:5597999,darkorange:16747520,darkorchid:10040012,darkred:9109504,darksalmon:15308410,darkseagreen:9419919,darkslateblue:4734347,darkslategray:3100495,darkslategrey:3100495,darkturquoise:52945,darkviolet:9699539,deeppink:16716947,deepskyblue:49151,dimgray:6908265,dimgrey:6908265,dodgerblue:2003199,firebrick:11674146,floralwhite:16775920,forestgreen:2263842,fuchsia:16711935,gainsboro:14474460,ghostwhite:16316671,gold:16766720,goldenrod:14329120,gray:8421504,green:32768,greenyellow:11403055,grey:8421504,honeydew:15794160,hotpink:16738740,indianred:13458524,indigo:4915330,ivory:16777200,khaki:15787660,lavender:15132410,lavenderblush:16773365,lawngreen:8190976,lemonchiffon:16775885,lightblue:11393254,lightcoral:15761536,lightcyan:14745599,lightgoldenrodyellow:16448210,lightgray:13882323,lightgreen:9498256,lightgrey:13882323,lightpink:16758465,lightsalmon:16752762,lightseagreen:2142890,lightskyblue:8900346,lightslategray:7833753,lightslategrey:7833753,lightsteelblue:11584734,lightyellow:16777184,lime:65280,limegreen:3329330,linen:16445670,magenta:16711935,maroon:8388608,mediumaquamarine:6737322,mediumblue:205,mediumorchid:12211667,mediumpurple:9662683,mediumseagreen:3978097,mediumslateblue:8087790,mediumspringgreen:64154,mediumturquoise:4772300,mediumvioletred:13047173,midnightblue:1644912,mintcream:16121850,mistyrose:16770273,moccasin:16770229,navajowhite:16768685,navy:128,oldlace:16643558,olive:8421376,olivedrab:7048739,orange:16753920,orangered:16729344,orchid:14315734,palegoldenrod:15657130,palegreen:10025880,paleturquoise:11529966,palevioletred:14381203,papayawhip:16773077,peachpuff:16767673,peru:13468991,pink:16761035,plum:14524637,powderblue:11591910,purple:8388736,rebeccapurple:6697881,red:16711680,rosybrown:12357519,royalblue:4286945,saddlebrown:9127187,salmon:16416882,sandybrown:16032864,seagreen:3050327,seashell:16774638,sienna:10506797,silver:12632256,skyblue:8900331,slateblue:6970061,slategray:7372944,slategrey:7372944,snow:16775930,springgreen:65407,steelblue:4620980,tan:13808780,teal:32896,thistle:14204888,tomato:16737095,turquoise:4251856,violet:15631086,wheat:16113331,white:16777215,whitesmoke:16119285,yellow:16776960,yellowgreen:10145074};function ye(){return this.rgb().formatHex()}function ve(){return this.rgb().formatRgb()}function _e(t){var n,e;return t=(t+"").trim().toLowerCase(),(n=ce.exec(t))?(e=n[1].length,n=parseInt(n[1],16),6===e?be(n):3===e?new Me(n>>8&15|n>>4&240,n>>4&15|240&n,(15&n)<<4|15&n,1):8===e?me(n>>24&255,n>>16&255,n>>8&255,(255&n)/255):4===e?me(n>>12&15|n>>8&240,n>>8&15|n>>4&240,n>>4&15|240&n,((15&n)<<4|15&n)/255):null):(n=fe.exec(t))?new Me(n[1],n[2],n[3],1):(n=se.exec(t))?new Me(255*n[1]/100,255*n[2]/100,255*n[3]/100,1):(n=le.exec(t))?me(n[1],n[2],n[3],n[4]):(n=he.exec(t))?me(255*n[1]/100,255*n[2]/100,255*n[3]/100,n[4]):(n=de.exec(t))?Ee(n[1],n[2]/100,n[3]/100,1):(n=pe.exec(t))?Ee(n[1],n[2]/100,n[3]/100,n[4]):ge.hasOwnProperty(t)?be(ge[t]):"transparent"===t?new Me(NaN,NaN,NaN,0):null}function be(t){return new Me(t>>16&255,t>>8&255,255&t,1)}function me(t,n,e,r){return r<=0&&(t=n=e=NaN),new Me(t,n,e,r)}function xe(t){return t instanceof ee||(t=_e(t)),t?new Me((t=t.rgb()).r,t.g,t.b,t.opacity):new Me}function we(t,n,e,r){return 1===arguments.length?xe(t):new Me(t,n,e,null==r?1:r)}function Me(t,n,e,r){this.r=+t,this.g=+n,this.b=+e,this.opacity=+r}function Ae(){return"#"+Se(this.r)+Se(this.g)+Se(this.b)}function Te(){var t=this.opacity;return(1===(t=isNaN(t)?1:Math.max(0,Math.min(1,t)))?"rgb(":"rgba(")+Math.max(0,Math.min(255,Math.round(this.r)||0))+", "+Math.max(0,Math.min(255,Math.round(this.g)||0))+", "+Math.max(0,Math.min(255,Math.round(this.b)||0))+(1===t?")":", "+t+")")}function Se(t){return((t=Math.max(0,Math.min(255,Math.round(t)||0)))<16?"0":"")+t.toString(16)}function Ee(t,n,e,r){return r<=0?t=n=e=NaN:e<=0||e>=1?t=n=NaN:n<=0&&(t=NaN),new Ce(t,n,e,r)}function ke(t){if(t instanceof Ce)return new Ce(t.h,t.s,t.l,t.opacity);if(t instanceof ee||(t=_e(t)),!t)return new Ce;if(t instanceof Ce)return t;var n=(t=t.rgb()).r/255,e=t.g/255,r=t.b/255,i=Math.min(n,e,r),o=Math.max(n,e,r),a=NaN,u=o-i,c=(o+i)/2;return u?(a=n===o?(e-r)/u+6*(e0&&c<1?0:a,new Ce(a,u,c,t.opacity)}function Ne(t,n,e,r){return 1===arguments.length?ke(t):new Ce(t,n,e,null==r?1:r)}function Ce(t,n,e,r){this.h=+t,this.s=+n,this.l=+e,this.opacity=+r}function Pe(t,n,e){return 255*(t<60?n+(e-n)*t/60:t<180?e:t<240?n+(e-n)*(240-t)/60:n)}te(ee,_e,{copy:function(t){return Object.assign(new this.constructor,this,t)},displayable:function(){return this.rgb().displayable()},hex:ye,formatHex:ye,formatHsl:function(){return ke(this).formatHsl()},formatRgb:ve,toString:ve}),te(Me,we,ne(ee,{brighter:function(t){return t=null==t?ie:Math.pow(ie,t),new Me(this.r*t,this.g*t,this.b*t,this.opacity)},darker:function(t){return t=null==t?re:Math.pow(re,t),new Me(this.r*t,this.g*t,this.b*t,this.opacity)},rgb:function(){return this},displayable:function(){return-.5<=this.r&&this.r<255.5&&-.5<=this.g&&this.g<255.5&&-.5<=this.b&&this.b<255.5&&0<=this.opacity&&this.opacity<=1},hex:Ae,formatHex:Ae,formatRgb:Te,toString:Te})),te(Ce,Ne,ne(ee,{brighter:function(t){return t=null==t?ie:Math.pow(ie,t),new Ce(this.h,this.s,this.l*t,this.opacity)},darker:function(t){return t=null==t?re:Math.pow(re,t),new Ce(this.h,this.s,this.l*t,this.opacity)},rgb:function(){var t=this.h%360+360*(this.h<0),n=isNaN(t)||isNaN(this.s)?0:this.s,e=this.l,r=e+(e<.5?e:1-e)*n,i=2*e-r;return new Me(Pe(t>=240?t-240:t+120,i,r),Pe(t,i,r),Pe(t<120?t+240:t-120,i,r),this.opacity)},displayable:function(){return(0<=this.s&&this.s<=1||isNaN(this.s))&&0<=this.l&&this.l<=1&&0<=this.opacity&&this.opacity<=1},formatHsl:function(){var t=this.opacity;return(1===(t=isNaN(t)?1:Math.max(0,Math.min(1,t)))?"hsl(":"hsla(")+(this.h||0)+", "+100*(this.s||0)+"%, "+100*(this.l||0)+"%"+(1===t?")":", "+t+")")}}));const ze=Math.PI/180,De=180/Math.PI,qe=.96422,Re=.82521,Fe=4/29,Oe=6/29,Ie=3*Oe*Oe;function Ue(t){if(t instanceof Ye)return new Ye(t.l,t.a,t.b,t.opacity);if(t instanceof $e)return We(t);t instanceof Me||(t=xe(t));var n,e,r=Xe(t.r),i=Xe(t.g),o=Xe(t.b),a=Le((.2225045*r+.7168786*i+.0606169*o)/1);return r===i&&i===o?n=e=a:(n=Le((.4360747*r+.3850649*i+.1430804*o)/qe),e=Le((.0139322*r+.0971045*i+.7141733*o)/Re)),new Ye(116*a-16,500*(n-a),200*(a-e),t.opacity)}function Be(t,n,e,r){return 1===arguments.length?Ue(t):new Ye(t,n,e,null==r?1:r)}function Ye(t,n,e,r){this.l=+t,this.a=+n,this.b=+e,this.opacity=+r}function Le(t){return t>.008856451679035631?Math.pow(t,1/3):t/Ie+Fe}function je(t){return t>Oe?t*t*t:Ie*(t-Fe)}function He(t){return 255*(t<=.0031308?12.92*t:1.055*Math.pow(t,1/2.4)-.055)}function Xe(t){return(t/=255)<=.04045?t/12.92:Math.pow((t+.055)/1.055,2.4)}function Ge(t){if(t instanceof $e)return new $e(t.h,t.c,t.l,t.opacity);if(t instanceof Ye||(t=Ue(t)),0===t.a&&0===t.b)return new $e(NaN,0=1?(e=1,n-1):Math.floor(e*n),i=t[r],o=t[r+1],a=r>0?t[r-1]:2*i-o,u=r()=>t;function lr(t,n){return function(e){return t+e*n}}function hr(t,n){var e=n-t;return e?lr(t,e>180||e<-180?e-360*Math.round(e/360):e):sr(isNaN(t)?n:t)}function dr(t){return 1==(t=+t)?pr:function(n,e){return e-n?function(t,n,e){return t=Math.pow(t,e),n=Math.pow(n,e)-t,e=1/e,function(r){return Math.pow(t+r*n,e)}}(n,e,t):sr(isNaN(n)?e:n)}}function pr(t,n){var e=n-t;return e?lr(t,e):sr(isNaN(t)?n:t)}var gr=function t(n){var e=dr(n);function r(t,n){var r=e((t=we(t)).r,(n=we(n)).r),i=e(t.g,n.g),o=e(t.b,n.b),a=pr(t.opacity,n.opacity);return function(n){return t.r=r(n),t.g=i(n),t.b=o(n),t.opacity=a(n),t+""}}return r.gamma=t,r}(1);function yr(t){return function(n){var e,r,i=n.length,o=new Array(i),a=new Array(i),u=new Array(i);for(e=0;eo&&(i=n.slice(o,i),u[a]?u[a]+=i:u[++a]=i),(e=e[0])===(r=r[0])?u[a]?u[a]+=r:u[++a]=r:(u[++a]=null,c.push({i:a,x:Mr(e,r)})),o=Sr.lastIndex;return o180?n+=360:n-t>180&&(t+=360),o.push({i:e.push(i(e)+"rotate(",null,r)-2,x:Mr(t,n)})):n&&e.push(i(e)+"rotate("+n+r)}(o.rotate,a.rotate,u,c),function(t,n,e,o){t!==n?o.push({i:e.push(i(e)+"skewX(",null,r)-2,x:Mr(t,n)}):n&&e.push(i(e)+"skewX("+n+r)}(o.skewX,a.skewX,u,c),function(t,n,e,r,o,a){if(t!==e||n!==r){var u=o.push(i(o)+"scale(",null,",",null,")");a.push({i:u-4,x:Mr(t,e)},{i:u-2,x:Mr(n,r)})}else 1===e&&1===r||o.push(i(o)+"scale("+e+","+r+")")}(o.scaleX,o.scaleY,a.scaleX,a.scaleY,u,c),o=a=null,function(t){for(var n,e=-1,r=c.length;++e=0&&n._call.call(void 0,t),n=n._next;--Kr}function si(){ni=(ti=ri.now())+ei,Kr=Qr=0;try{fi()}finally{Kr=0,function(){var t,n,e=Wr,r=1/0;for(;e;)e._call?(r>e._time&&(r=e._time),t=e,e=e._next):(n=e._next,e._next=null,e=t?t._next=n:Wr=n);Zr=t,hi(r)}(),ni=0}}function li(){var t=ri.now(),n=t-ti;n>1e3&&(ei-=n,ti=t)}function hi(t){Kr||(Qr&&(Qr=clearTimeout(Qr)),t-ni>24?(t<1/0&&(Qr=setTimeout(si,t-ri.now()-ei)),Jr&&(Jr=clearInterval(Jr))):(Jr||(ti=ri.now(),Jr=setInterval(li,1e3)),Kr=1,ii(si)))}function di(t,n,e){var r=new ui;return n=null==n?0:+n,r.restart((e=>{r.stop(),t(e+n)}),n,e),r}ui.prototype=ci.prototype={constructor:ui,restart:function(t,n,e){if("function"!=typeof t)throw new TypeError("callback is not a function");e=(null==e?oi():+e)+(null==n?0:+n),this._next||Zr===this||(Zr?Zr._next=this:Wr=this,Zr=this),this._call=t,this._time=e,hi()},stop:function(){this._call&&(this._call=null,this._time=1/0,hi())}};var pi=yt("start","end","cancel","interrupt"),gi=[];function yi(t,n,e,r,i,o){var a=t.__transition;if(a){if(e in a)return}else t.__transition={};!function(t,n,e){var r,i=t.__transition;function o(t){e.state=1,e.timer.restart(a,e.delay,e.time),e.delay<=t&&a(t-e.delay)}function a(o){var f,s,l,h;if(1!==e.state)return c();for(f in i)if((h=i[f]).name===e.name){if(3===h.state)return di(a);4===h.state?(h.state=6,h.timer.stop(),h.on.call("interrupt",t,t.__data__,h.index,h.group),delete i[f]):+f0)throw new Error("too late; already scheduled");return e}function _i(t,n){var e=bi(t,n);if(e.state>3)throw new Error("too late; already running");return e}function bi(t,n){var e=t.__transition;if(!e||!(e=e[n]))throw new Error("transition not found");return e}function mi(t,n){var e,r,i,o=t.__transition,a=!0;if(o){for(i in n=null==n?null:n+"",o)(e=o[i]).name===n?(r=e.state>2&&e.state<5,e.state=6,e.timer.stop(),e.on.call(r?"interrupt":"cancel",t,t.__data__,e.index,e.group),delete o[i]):a=!1;a&&delete t.__transition}}function xi(t,n){var e,r;return function(){var i=_i(this,t),o=i.tween;if(o!==e)for(var a=0,u=(r=e=o).length;a=0&&(t=t.slice(0,n)),!t||"start"===t}))}(n)?vi:_i;return function(){var a=o(this,t),u=a.on;u!==r&&(i=(r=u).copy()).on(n,e),a.on=i}}var Yi=Rn.prototype.constructor;function Li(t){return function(){this.style.removeProperty(t)}}function ji(t,n,e){return function(r){this.style.setProperty(t,n.call(this,r),e)}}function Hi(t,n,e){var r,i;function o(){var o=n.apply(this,arguments);return o!==i&&(r=(i=o)&&ji(t,o,e)),r}return o._value=n,o}function Xi(t){return function(n){this.textContent=t.call(this,n)}}function Gi(t){var n,e;function r(){var r=t.apply(this,arguments);return r!==e&&(n=(e=r)&&Xi(r)),n}return r._value=t,r}var Vi=0;function $i(t,n,e,r){this._groups=t,this._parents=n,this._name=e,this._id=r}function Wi(t){return Rn().transition(t)}function Zi(){return++Vi}var Ki=Rn.prototype;$i.prototype=Wi.prototype={constructor:$i,select:function(t){var n=this._name,e=this._id;"function"!=typeof t&&(t=kt(t));for(var r=this._groups,i=r.length,o=new Array(i),a=0;a()=>t;function To(t,{sourceEvent:n,target:e,selection:r,mode:i,dispatch:o}){Object.defineProperties(this,{type:{value:t,enumerable:!0,configurable:!0},sourceEvent:{value:n,enumerable:!0,configurable:!0},target:{value:e,enumerable:!0,configurable:!0},selection:{value:r,enumerable:!0,configurable:!0},mode:{value:i,enumerable:!0,configurable:!0},_:{value:o}})}function So(t){t.stopImmediatePropagation()}function Eo(t){t.preventDefault(),t.stopImmediatePropagation()}var ko={name:"drag"},No={name:"space"},Co={name:"handle"},Po={name:"center"};const{abs:zo,max:Do,min:qo}=Math;function Ro(t){return[+t[0],+t[1]]}function Fo(t){return[Ro(t[0]),Ro(t[1])]}var Oo={name:"x",handles:["w","e"].map(Xo),input:function(t,n){return null==t?null:[[+t[0],n[0][1]],[+t[1],n[1][1]]]},output:function(t){return t&&[t[0][0],t[1][0]]}},Io={name:"y",handles:["n","s"].map(Xo),input:function(t,n){return null==t?null:[[n[0][0],+t[0]],[n[1][0],+t[1]]]},output:function(t){return t&&[t[0][1],t[1][1]]}},Uo={name:"xy",handles:["n","w","e","s","nw","ne","sw","se"].map(Xo),input:function(t){return null==t?null:Fo(t)},output:function(t){return t}},Bo={overlay:"crosshair",selection:"move",n:"ns-resize",e:"ew-resize",s:"ns-resize",w:"ew-resize",nw:"nwse-resize",ne:"nesw-resize",se:"nwse-resize",sw:"nesw-resize"},Yo={e:"w",w:"e",nw:"ne",ne:"nw",se:"sw",sw:"se"},Lo={n:"s",s:"n",nw:"sw",ne:"se",se:"ne",sw:"nw"},jo={overlay:1,selection:1,n:null,e:1,s:null,w:-1,nw:-1,ne:1,se:1,sw:-1},Ho={overlay:1,selection:1,n:-1,e:null,s:1,w:null,nw:-1,ne:-1,se:1,sw:1};function Xo(t){return{type:t}}function Go(t){return!t.ctrlKey&&!t.button}function Vo(){var t=this.ownerSVGElement||this;return t.hasAttribute("viewBox")?[[(t=t.viewBox.baseVal).x,t.y],[t.x+t.width,t.y+t.height]]:[[0,0],[t.width.baseVal.value,t.height.baseVal.value]]}function $o(){return navigator.maxTouchPoints||"ontouchstart"in this}function Wo(t){for(;!t.__brush;)if(!(t=t.parentNode))return;return t.__brush}function Zo(t){return t[0][0]===t[1][0]||t[0][1]===t[1][1]}function Ko(t){var n,e=Vo,r=Go,i=$o,o=!0,a=yt("start","brush","end"),u=6;function c(n){var e=n.property("__brush",g).selectAll(".overlay").data([Xo("overlay")]);e.enter().append("rect").attr("class","overlay").attr("pointer-events","all").attr("cursor",Bo.overlay).merge(e).each((function(){var t=Wo(this).extent;Fn(this).attr("x",t[0][0]).attr("y",t[0][1]).attr("width",t[1][0]-t[0][0]).attr("height",t[1][1]-t[0][1])})),n.selectAll(".selection").data([Xo("selection")]).enter().append("rect").attr("class","selection").attr("cursor",Bo.selection).attr("fill","#777").attr("fill-opacity",.3).attr("stroke","#fff").attr("shape-rendering","crispEdges");var r=n.selectAll(".handle").data(t.handles,(function(t){return t.type}));r.exit().remove(),r.enter().append("rect").attr("class",(function(t){return"handle handle--"+t.type})).attr("cursor",(function(t){return Bo[t.type]})),n.each(f).attr("fill","none").attr("pointer-events","all").on("mousedown.brush",h).filter(i).on("touchstart.brush",h).on("touchmove.brush",d).on("touchend.brush touchcancel.brush",p).style("touch-action","none").style("-webkit-tap-highlight-color","rgba(0,0,0,0)")}function f(){var t=Fn(this),n=Wo(this).selection;n?(t.selectAll(".selection").style("display",null).attr("x",n[0][0]).attr("y",n[0][1]).attr("width",n[1][0]-n[0][0]).attr("height",n[1][1]-n[0][1]),t.selectAll(".handle").style("display",null).attr("x",(function(t){return"e"===t.type[t.type.length-1]?n[1][0]-u/2:n[0][0]-u/2})).attr("y",(function(t){return"s"===t.type[0]?n[1][1]-u/2:n[0][1]-u/2})).attr("width",(function(t){return"n"===t.type||"s"===t.type?n[1][0]-n[0][0]+u:u})).attr("height",(function(t){return"e"===t.type||"w"===t.type?n[1][1]-n[0][1]+u:u}))):t.selectAll(".selection,.handle").style("display","none").attr("x",null).attr("y",null).attr("width",null).attr("height",null)}function s(t,n,e){var r=t.__brush.emitter;return!r||e&&r.clean?new l(t,n,e):r}function l(t,n,e){this.that=t,this.args=n,this.state=t.__brush,this.active=0,this.clean=e}function h(e){if((!n||e.touches)&&r.apply(this,arguments)){var i,a,u,c,l,h,d,p,g,y,v,_=this,b=e.target.__data__.type,m="selection"===(o&&e.metaKey?b="overlay":b)?ko:o&&e.altKey?Po:Co,x=t===Io?null:jo[b],w=t===Oo?null:Ho[b],M=Wo(_),A=M.extent,T=M.selection,S=A[0][0],E=A[0][1],k=A[1][0],N=A[1][1],C=0,P=0,z=x&&w&&o&&e.shiftKey,D=Array.from(e.touches||[e],(t=>{const n=t.identifier;return(t=Yn(t,_)).point0=t.slice(),t.identifier=n,t}));mi(_);var q=s(_,arguments,!0).beforestart();if("overlay"===b){T&&(g=!0);const n=[D[0],D[1]||D[0]];M.selection=T=[[i=t===Io?S:qo(n[0][0],n[1][0]),u=t===Oo?E:qo(n[0][1],n[1][1])],[l=t===Io?k:Do(n[0][0],n[1][0]),d=t===Oo?N:Do(n[0][1],n[1][1])]],D.length>1&&U(e)}else i=T[0][0],u=T[0][1],l=T[1][0],d=T[1][1];a=i,c=u,h=l,p=d;var R=Fn(_).attr("pointer-events","none"),F=R.selectAll(".overlay").attr("cursor",Bo[b]);if(e.touches)q.moved=I,q.ended=B;else{var O=Fn(e.view).on("mousemove.brush",I,!0).on("mouseup.brush",B,!0);o&&O.on("keydown.brush",Y,!0).on("keyup.brush",L,!0),Gn(e.view)}f.call(_),q.start(e,m.name)}function I(t){for(const n of t.changedTouches||[t])for(const t of D)t.identifier===n.identifier&&(t.cur=Yn(n,_));if(z&&!y&&!v&&1===D.length){const t=D[0];zo(t.cur[0]-t[0])>zo(t.cur[1]-t[1])?v=!0:y=!0}for(const t of D)t.cur&&(t[0]=t.cur[0],t[1]=t.cur[1]);g=!0,Eo(t),U(t)}function U(t){const n=D[0],e=n.point0;var r;switch(C=n[0]-e[0],P=n[1]-e[1],m){case No:case ko:x&&(C=Do(S-i,qo(k-l,C)),a=i+C,h=l+C),w&&(P=Do(E-u,qo(N-d,P)),c=u+P,p=d+P);break;case Co:D[1]?(x&&(a=Do(S,qo(k,D[0][0])),h=Do(S,qo(k,D[1][0])),x=1),w&&(c=Do(E,qo(N,D[0][1])),p=Do(E,qo(N,D[1][1])),w=1)):(x<0?(C=Do(S-i,qo(k-i,C)),a=i+C,h=l):x>0&&(C=Do(S-l,qo(k-l,C)),a=i,h=l+C),w<0?(P=Do(E-u,qo(N-u,P)),c=u+P,p=d):w>0&&(P=Do(E-d,qo(N-d,P)),c=u,p=d+P));break;case Po:x&&(a=Do(S,qo(k,i-C*x)),h=Do(S,qo(k,l+C*x))),w&&(c=Do(E,qo(N,u-P*w)),p=Do(E,qo(N,d+P*w)))}h0&&(i=a-C),w<0?d=p-P:w>0&&(u=c-P),m=No,F.attr("cursor",Bo.selection),U(t));break;default:return}Eo(t)}function L(t){switch(t.keyCode){case 16:z&&(y=v=z=!1,U(t));break;case 18:m===Po&&(x<0?l=h:x>0&&(i=a),w<0?d=p:w>0&&(u=c),m=Co,U(t));break;case 32:m===No&&(t.altKey?(x&&(l=h-C*x,i=a+C*x),w&&(d=p-P*w,u=c+P*w),m=Po):(x<0?l=h:x>0&&(i=a),w<0?d=p:w>0&&(u=c),m=Co),F.attr("cursor",Bo[b]),U(t));break;default:return}Eo(t)}}function d(t){s(this,arguments).moved(t)}function p(t){s(this,arguments).ended(t)}function g(){var n=this.__brush||{selection:null};return n.extent=Fo(e.apply(this,arguments)),n.dim=t,n}return c.move=function(n,e,r){n.tween?n.on("start.brush",(function(t){s(this,arguments).beforestart().start(t)})).on("interrupt.brush end.brush",(function(t){s(this,arguments).end(t)})).tween("brush",(function(){var n=this,r=n.__brush,i=s(n,arguments),o=r.selection,a=t.input("function"==typeof e?e.apply(this,arguments):e,r.extent),u=kr(o,a);function c(t){r.selection=1===t&&null===a?null:u(t),f.call(n),i.brush()}return null!==o&&null!==a?c:c(1)})):n.each((function(){var n=this,i=arguments,o=n.__brush,a=t.input("function"==typeof e?e.apply(n,i):e,o.extent),u=s(n,i).beforestart();mi(n),o.selection=null===a?null:a,f.call(n),u.start(r).brush(r).end(r)}))},c.clear=function(t,n){c.move(t,null,n)},l.prototype={beforestart:function(){return 1==++this.active&&(this.state.emitter=this,this.starting=!0),this},start:function(t,n){return this.starting?(this.starting=!1,this.emit("start",t,n)):this.emit("brush",t),this},brush:function(t,n){return this.emit("brush",t,n),this},end:function(t,n){return 0==--this.active&&(delete this.state.emitter,this.emit("end",t,n)),this},emit:function(n,e,r){var i=Fn(this.that).datum();a.call(n,this.that,new To(n,{sourceEvent:e,target:c,selection:t.output(this.state.selection),mode:r,dispatch:a}),i)}},c.extent=function(t){return arguments.length?(e="function"==typeof t?t:Ao(Fo(t)),c):e},c.filter=function(t){return arguments.length?(r="function"==typeof t?t:Ao(!!t),c):r},c.touchable=function(t){return arguments.length?(i="function"==typeof t?t:Ao(!!t),c):i},c.handleSize=function(t){return arguments.length?(u=+t,c):u},c.keyModifiers=function(t){return arguments.length?(o=!!t,c):o},c.on=function(){var t=a.on.apply(a,arguments);return t===a?c:t},c}var Qo=Math.abs,Jo=Math.cos,ta=Math.sin,na=Math.PI,ea=na/2,ra=2*na,ia=Math.max,oa=1e-12;function aa(t,n){return Array.from({length:n-t},((n,e)=>t+e))}function ua(t){return function(n,e){return t(n.source.value+n.target.value,e.source.value+e.target.value)}}function ca(t,n){var e=0,r=null,i=null,o=null;function a(a){var u,c=a.length,f=new Array(c),s=aa(0,c),l=new Array(c*c),h=new Array(c),d=0;a=Float64Array.from({length:c*c},n?(t,n)=>a[n%c][n/c|0]:(t,n)=>a[n/c|0][n%c]);for(let n=0;nr(f[t],f[n])));for(const e of s){const r=n;if(t){const t=aa(1+~c,c).filter((t=>t<0?a[~t*c+e]:a[e*c+t]));i&&t.sort(((t,n)=>i(t<0?-a[~t*c+e]:a[e*c+t],n<0?-a[~n*c+e]:a[e*c+n])));for(const r of t)if(r<0){(l[~r*c+e]||(l[~r*c+e]={source:null,target:null})).target={index:e,startAngle:n,endAngle:n+=a[~r*c+e]*d,value:a[~r*c+e]}}else{(l[e*c+r]||(l[e*c+r]={source:null,target:null})).source={index:e,startAngle:n,endAngle:n+=a[e*c+r]*d,value:a[e*c+r]}}h[e]={index:e,startAngle:r,endAngle:n,value:f[e]}}else{const t=aa(0,c).filter((t=>a[e*c+t]||a[t*c+e]));i&&t.sort(((t,n)=>i(a[e*c+t],a[e*c+n])));for(const r of t){let t;if(ela)if(Math.abs(s*u-c*f)>la&&i){var h=e-o,d=r-a,p=u*u+c*c,g=h*h+d*d,y=Math.sqrt(p),v=Math.sqrt(l),_=i*Math.tan((fa-Math.acos((p+l-g)/(2*y*v)))/2),b=_/v,m=_/y;Math.abs(b-1)>la&&(this._+="L"+(t+b*f)+","+(n+b*s)),this._+="A"+i+","+i+",0,0,"+ +(s*h>f*d)+","+(this._x1=t+m*u)+","+(this._y1=n+m*c)}else this._+="L"+(this._x1=t)+","+(this._y1=n);else;},arc:function(t,n,e,r,i,o){t=+t,n=+n,o=!!o;var a=(e=+e)*Math.cos(r),u=e*Math.sin(r),c=t+a,f=n+u,s=1^o,l=o?r-i:i-r;if(e<0)throw new Error("negative radius: "+e);null===this._x1?this._+="M"+c+","+f:(Math.abs(this._x1-c)>la||Math.abs(this._y1-f)>la)&&(this._+="L"+c+","+f),e&&(l<0&&(l=l%sa+sa),l>ha?this._+="A"+e+","+e+",0,1,"+s+","+(t-a)+","+(n-u)+"A"+e+","+e+",0,1,"+s+","+(this._x1=c)+","+(this._y1=f):l>la&&(this._+="A"+e+","+e+",0,"+ +(l>=fa)+","+s+","+(this._x1=t+e*Math.cos(i))+","+(this._y1=n+e*Math.sin(i))))},rect:function(t,n,e,r){this._+="M"+(this._x0=this._x1=+t)+","+(this._y0=this._y1=+n)+"h"+ +e+"v"+ +r+"h"+-e+"Z"},toString:function(){return this._}};var ga=Array.prototype.slice;function ya(t){return function(){return t}}function va(t){return t.source}function _a(t){return t.target}function ba(t){return t.radius}function ma(t){return t.startAngle}function xa(t){return t.endAngle}function wa(){return 0}function Ma(){return 10}function Aa(t){var n=va,e=_a,r=ba,i=ba,o=ma,a=xa,u=wa,c=null;function f(){var f,s=n.apply(this,arguments),l=e.apply(this,arguments),h=u.apply(this,arguments)/2,d=ga.call(arguments),p=+r.apply(this,(d[0]=s,d)),g=o.apply(this,d)-ea,y=a.apply(this,d)-ea,v=+i.apply(this,(d[0]=l,d)),_=o.apply(this,d)-ea,b=a.apply(this,d)-ea;if(c||(c=f=pa()),h>oa&&(Qo(y-g)>2*h+oa?y>g?(g+=h,y-=h):(g-=h,y+=h):g=y=(g+y)/2,Qo(b-_)>2*h+oa?b>_?(_+=h,b-=h):(_-=h,b+=h):_=b=(_+b)/2),c.moveTo(p*Jo(g),p*ta(g)),c.arc(0,0,p,g,y),g!==_||y!==b)if(t){var m=+t.apply(this,arguments),x=v-m,w=(_+b)/2;c.quadraticCurveTo(0,0,x*Jo(_),x*ta(_)),c.lineTo(v*Jo(w),v*ta(w)),c.lineTo(x*Jo(b),x*ta(b))}else c.quadraticCurveTo(0,0,v*Jo(_),v*ta(_)),c.arc(0,0,v,_,b);if(c.quadraticCurveTo(0,0,p*Jo(g),p*ta(g)),c.closePath(),f)return c=null,f+""||null}return t&&(f.headRadius=function(n){return arguments.length?(t="function"==typeof n?n:ya(+n),f):t}),f.radius=function(t){return arguments.length?(r=i="function"==typeof t?t:ya(+t),f):r},f.sourceRadius=function(t){return arguments.length?(r="function"==typeof t?t:ya(+t),f):r},f.targetRadius=function(t){return arguments.length?(i="function"==typeof t?t:ya(+t),f):i},f.startAngle=function(t){return arguments.length?(o="function"==typeof t?t:ya(+t),f):o},f.endAngle=function(t){return arguments.length?(a="function"==typeof t?t:ya(+t),f):a},f.padAngle=function(t){return arguments.length?(u="function"==typeof t?t:ya(+t),f):u},f.source=function(t){return arguments.length?(n=t,f):n},f.target=function(t){return arguments.length?(e=t,f):e},f.context=function(t){return arguments.length?(c=null==t?null:t,f):c},f}var Ta=Array.prototype.slice;function Sa(t,n){return t-n}var Ea=t=>()=>t;function ka(t,n){for(var e,r=-1,i=n.length;++rr!=d>r&&e<(h-f)*(r-s)/(d-s)+f&&(i=-i)}return i}function Ca(t,n,e){var r,i,o,a;return function(t,n,e){return(n[0]-t[0])*(e[1]-t[1])==(e[0]-t[0])*(n[1]-t[1])}(t,n,e)&&(i=t[r=+(t[0]===n[0])],o=e[r],a=n[r],i<=o&&o<=a||a<=o&&o<=i)}function Pa(){}var za=[[],[[[1,1.5],[.5,1]]],[[[1.5,1],[1,1.5]]],[[[1.5,1],[.5,1]]],[[[1,.5],[1.5,1]]],[[[1,1.5],[.5,1]],[[1,.5],[1.5,1]]],[[[1,.5],[1,1.5]]],[[[1,.5],[.5,1]]],[[[.5,1],[1,.5]]],[[[1,1.5],[1,.5]]],[[[.5,1],[1,.5]],[[1.5,1],[1,1.5]]],[[[1.5,1],[1,.5]]],[[[.5,1],[1.5,1]]],[[[1,1.5],[1.5,1]]],[[[.5,1],[1,1.5]]],[]];function Da(){var t=1,n=1,e=Y,r=u;function i(t){var n=e(t);if(Array.isArray(n))n=n.slice().sort(Sa);else{const e=p(t),r=U(e[0],e[1],n);n=O(Math.floor(e[0]/r)*r,Math.floor(e[1]/r-1)*r,n)}return n.map((n=>o(t,n)))}function o(e,i){var o=[],u=[];return function(e,r,i){var o,u,c,f,s,l,h=new Array,d=new Array;o=u=-1,f=e[0]>=r,za[f<<1].forEach(p);for(;++o=r,za[c|f<<1].forEach(p);za[f<<0].forEach(p);for(;++u=r,s=e[u*t]>=r,za[f<<1|s<<2].forEach(p);++o=r,l=s,s=e[u*t+o+1]>=r,za[c|f<<1|s<<2|l<<3].forEach(p);za[f|s<<3].forEach(p)}o=-1,s=e[u*t]>=r,za[s<<2].forEach(p);for(;++o=r,za[s<<2|l<<3].forEach(p);function p(t){var n,e,r=[t[0][0]+o,t[0][1]+u],c=[t[1][0]+o,t[1][1]+u],f=a(r),s=a(c);(n=d[f])?(e=h[s])?(delete d[n.end],delete h[e.start],n===e?(n.ring.push(c),i(n.ring)):h[n.start]=d[e.end]={start:n.start,end:e.end,ring:n.ring.concat(e.ring)}):(delete d[n.end],n.ring.push(c),d[n.end=s]=n):(n=h[s])?(e=d[f])?(delete h[n.start],delete d[e.end],n===e?(n.ring.push(c),i(n.ring)):h[e.start]=d[n.end]={start:e.start,end:n.end,ring:e.ring.concat(n.ring)}):(delete h[n.start],n.ring.unshift(r),h[n.start=f]=n):h[f]=d[s]={start:f,end:s,ring:[r,c]}}za[s<<3].forEach(p)}(e,i,(function(t){r(t,e,i),function(t){for(var n=0,e=t.length,r=t[e-1][1]*t[0][0]-t[e-1][0]*t[0][1];++n0?o.push([t]):u.push(t)})),u.forEach((function(t){for(var n,e=0,r=o.length;e0&&a0&&u=0&&o>=0))throw new Error("invalid size");return t=r,n=o,i},i.thresholds=function(t){return arguments.length?(e="function"==typeof t?t:Array.isArray(t)?Ea(Ta.call(t)):Ea(t),i):e},i.smooth=function(t){return arguments.length?(r=t?u:Pa,i):r===u},i}function qa(t,n,e){for(var r=t.width,i=t.height,o=1+(e<<1),a=0;a=e&&(u>=o&&(c-=t.data[u-o+a*r]),n.data[u-e+a*r]=c/Math.min(u+1,r-1+o-u,o))}function Ra(t,n,e){for(var r=t.width,i=t.height,o=1+(e<<1),a=0;a=e&&(u>=o&&(c-=t.data[a+(u-o)*r]),n.data[a+(u-e)*r]=c/Math.min(u+1,i-1+o-u,o))}function Fa(t){return t[0]}function Oa(t){return t[1]}function Ia(){return 1}const Ua=134217729;function Ba(t,n,e,r,i){let o,a,u,c,f=n[0],s=r[0],l=0,h=0;s>f==s>-f?(o=f,f=n[++l]):(o=s,s=r[++h]);let d=0;if(lf==s>-f?(a=f+o,u=o-(a-f),f=n[++l]):(a=s+o,u=o-(a-s),s=r[++h]),o=a,0!==u&&(i[d++]=u);lf==s>-f?(a=o+f,c=a-o,u=o-(a-c)+(f-c),f=n[++l]):(a=o+s,c=a-o,u=o-(a-c)+(s-c),s=r[++h]),o=a,0!==u&&(i[d++]=u);for(;l0!=u>0)return c;const f=Math.abs(a+u);return Math.abs(c)>=33306690738754716e-32*f?c:-function(t,n,e,r,i,o,a){let u,c,f,s,l,h,d,p,g,y,v,_,b,m,x,w,M,A;const T=t-i,S=e-i,E=n-o,k=r-o;m=T*k,h=Ua*T,d=h-(h-T),p=T-d,h=Ua*k,g=h-(h-k),y=k-g,x=p*y-(m-d*g-p*g-d*y),w=E*S,h=Ua*E,d=h-(h-E),p=E-d,h=Ua*S,g=h-(h-S),y=S-g,M=p*y-(w-d*g-p*g-d*y),v=x-M,l=x-v,La[0]=x-(v+l)+(l-M),_=m+v,l=_-m,b=m-(_-l)+(v-l),v=b-w,l=b-v,La[1]=b-(v+l)+(l-w),A=_+v,l=A-_,La[2]=_-(A-l)+(v-l),La[3]=A;let N=function(t,n){let e=n[0];for(let r=1;r=C||-N>=C)return N;if(l=t-T,u=t-(T+l)+(l-i),l=e-S,f=e-(S+l)+(l-i),l=n-E,c=n-(E+l)+(l-o),l=r-k,s=r-(k+l)+(l-o),0===u&&0===c&&0===f&&0===s)return N;if(C=11093356479670487e-47*a+33306690738754706e-32*Math.abs(N),N+=T*s+k*u-(E*f+S*c),N>=C||-N>=C)return N;m=u*k,h=Ua*u,d=h-(h-u),p=u-d,h=Ua*k,g=h-(h-k),y=k-g,x=p*y-(m-d*g-p*g-d*y),w=c*S,h=Ua*c,d=h-(h-c),p=c-d,h=Ua*S,g=h-(h-S),y=S-g,M=p*y-(w-d*g-p*g-d*y),v=x-M,l=x-v,Ga[0]=x-(v+l)+(l-M),_=m+v,l=_-m,b=m-(_-l)+(v-l),v=b-w,l=b-v,Ga[1]=b-(v+l)+(l-w),A=_+v,l=A-_,Ga[2]=_-(A-l)+(v-l),Ga[3]=A;const P=Ba(4,La,4,Ga,ja);m=T*s,h=Ua*T,d=h-(h-T),p=T-d,h=Ua*s,g=h-(h-s),y=s-g,x=p*y-(m-d*g-p*g-d*y),w=E*f,h=Ua*E,d=h-(h-E),p=E-d,h=Ua*f,g=h-(h-f),y=f-g,M=p*y-(w-d*g-p*g-d*y),v=x-M,l=x-v,Ga[0]=x-(v+l)+(l-M),_=m+v,l=_-m,b=m-(_-l)+(v-l),v=b-w,l=b-v,Ga[1]=b-(v+l)+(l-w),A=_+v,l=A-_,Ga[2]=_-(A-l)+(v-l),Ga[3]=A;const z=Ba(P,ja,4,Ga,Ha);m=u*s,h=Ua*u,d=h-(h-u),p=u-d,h=Ua*s,g=h-(h-s),y=s-g,x=p*y-(m-d*g-p*g-d*y),w=c*f,h=Ua*c,d=h-(h-c),p=c-d,h=Ua*f,g=h-(h-f),y=f-g,M=p*y-(w-d*g-p*g-d*y),v=x-M,l=x-v,Ga[0]=x-(v+l)+(l-M),_=m+v,l=_-m,b=m-(_-l)+(v-l),v=b-w,l=b-v,Ga[1]=b-(v+l)+(l-w),A=_+v,l=A-_,Ga[2]=_-(A-l)+(v-l),Ga[3]=A;const D=Ba(z,Ha,4,Ga,Xa);return Xa[D-1]}(t,n,e,r,i,o,f)}const $a=Math.pow(2,-52),Wa=new Uint32Array(512);class Za{static from(t,n=eu,e=ru){const r=t.length,i=new Float64Array(2*r);for(let o=0;o>1;if(n>0&&"number"!=typeof t[0])throw new Error("Expected coords to contain numbers.");this.coords=t;const e=Math.max(2*n-5,0);this._triangles=new Uint32Array(3*e),this._halfedges=new Int32Array(3*e),this._hashSize=Math.ceil(Math.sqrt(n)),this._hullPrev=new Uint32Array(n),this._hullNext=new Uint32Array(n),this._hullTri=new Uint32Array(n),this._hullHash=new Int32Array(this._hashSize).fill(-1),this._ids=new Uint32Array(n),this._dists=new Float64Array(n),this.update()}update(){const{coords:t,_hullPrev:n,_hullNext:e,_hullTri:r,_hullHash:i}=this,o=t.length>>1;let a=1/0,u=1/0,c=-1/0,f=-1/0;for(let n=0;nc&&(c=e),r>f&&(f=r),this._ids[n]=n}const s=(a+c)/2,l=(u+f)/2;let h,d,p,g=1/0;for(let n=0;n0&&(d=n,g=e)}let _=t[2*d],b=t[2*d+1],m=1/0;for(let n=0;nr&&(n[e++]=i,r=this._dists[i])}return this.hull=n.subarray(0,e),this.triangles=new Uint32Array(0),void(this.halfedges=new Uint32Array(0))}if(Va(y,v,_,b,x,w)<0){const t=d,n=_,e=b;d=p,_=x,b=w,p=t,x=n,w=e}const M=function(t,n,e,r,i,o){const a=e-t,u=r-n,c=i-t,f=o-n,s=a*a+u*u,l=c*c+f*f,h=.5/(a*f-u*c);return{x:t+(f*s-u*l)*h,y:n+(a*l-c*s)*h}}(y,v,_,b,x,w);this._cx=M.x,this._cy=M.y;for(let n=0;n0&&Math.abs(f-o)<=$a&&Math.abs(s-a)<=$a)continue;if(o=f,a=s,c===h||c===d||c===p)continue;let l=0;for(let t=0,n=this._hashKey(f,s);t=0;)if(y=g,y===l){y=-1;break}if(-1===y)continue;let v=this._addTriangle(y,c,e[y],-1,-1,r[y]);r[c]=this._legalize(v+2),r[y]=v,A++;let _=e[y];for(;g=e[_],Va(f,s,t[2*_],t[2*_+1],t[2*g],t[2*g+1])<0;)v=this._addTriangle(_,c,g,r[c],-1,r[_]),r[c]=this._legalize(v+2),e[_]=_,A--,_=g;if(y===l)for(;g=n[y],Va(f,s,t[2*g],t[2*g+1],t[2*y],t[2*y+1])<0;)v=this._addTriangle(g,c,y,-1,r[y],r[g]),this._legalize(v+2),r[g]=v,e[y]=y,A--,y=g;this._hullStart=n[c]=y,e[y]=n[_]=c,e[c]=_,i[this._hashKey(f,s)]=c,i[this._hashKey(t[2*y],t[2*y+1])]=y}this.hull=new Uint32Array(A);for(let t=0,n=this._hullStart;t0?3-e:1+e)/4}(t-this._cx,n-this._cy)*this._hashSize)%this._hashSize}_legalize(t){const{_triangles:n,_halfedges:e,coords:r}=this;let i=0,o=0;for(;;){const a=e[t],u=t-t%3;if(o=u+(t+2)%3,-1===a){if(0===i)break;t=Wa[--i];continue}const c=a-a%3,f=u+(t+1)%3,s=c+(a+2)%3,l=n[o],h=n[t],d=n[f],p=n[s];if(Qa(r[2*l],r[2*l+1],r[2*h],r[2*h+1],r[2*d],r[2*d+1],r[2*p],r[2*p+1])){n[t]=p,n[a]=l;const r=e[s];if(-1===r){let n=this._hullStart;do{if(this._hullTri[n]===s){this._hullTri[n]=t;break}n=this._hullPrev[n]}while(n!==this._hullStart)}this._link(t,r),this._link(a,e[o]),this._link(o,s);const u=c+(a+1)%3;i=e&&n[t[a]]>o;)t[a+1]=t[a--];t[a+1]=r}else{let i=e+1,o=r;nu(t,e+r>>1,i),n[t[e]]>n[t[r]]&&nu(t,e,r),n[t[i]]>n[t[r]]&&nu(t,i,r),n[t[e]]>n[t[i]]&&nu(t,e,i);const a=t[i],u=n[a];for(;;){do{i++}while(n[t[i]]u);if(o=o-e?(tu(t,n,i,r),tu(t,n,e,o-1)):(tu(t,n,e,o-1),tu(t,n,i,r))}}function nu(t,n,e){const r=t[n];t[n]=t[e],t[e]=r}function eu(t){return t[0]}function ru(t){return t[1]}const iu=1e-6;class ou{constructor(){this._x0=this._y0=this._x1=this._y1=null,this._=""}moveTo(t,n){this._+=`M${this._x0=this._x1=+t},${this._y0=this._y1=+n}`}closePath(){null!==this._x1&&(this._x1=this._x0,this._y1=this._y0,this._+="Z")}lineTo(t,n){this._+=`L${this._x1=+t},${this._y1=+n}`}arc(t,n,e){const r=(t=+t)+(e=+e),i=n=+n;if(e<0)throw new Error("negative radius");null===this._x1?this._+=`M${r},${i}`:(Math.abs(this._x1-r)>iu||Math.abs(this._y1-i)>iu)&&(this._+="L"+r+","+i),e&&(this._+=`A${e},${e},0,1,1,${t-e},${n}A${e},${e},0,1,1,${this._x1=r},${this._y1=i}`)}rect(t,n,e,r){this._+=`M${this._x0=this._x1=+t},${this._y0=this._y1=+n}h${+e}v${+r}h${-e}Z`}value(){return this._||null}}class au{constructor(){this._=[]}moveTo(t,n){this._.push([t,n])}closePath(){this._.push(this._[0].slice())}lineTo(t,n){this._.push([t,n])}value(){return this._.length?this._:null}}class uu{constructor(t,[n,e,r,i]=[0,0,960,500]){if(!((r=+r)>=(n=+n)&&(i=+i)>=(e=+e)))throw new Error("invalid bounds");this.delaunay=t,this._circumcenters=new Float64Array(2*t.points.length),this.vectors=new Float64Array(2*t.points.length),this.xmax=r,this.xmin=n,this.ymax=i,this.ymin=e,this._init()}update(){return this.delaunay.update(),this._init(),this}_init(){const{delaunay:{points:t,hull:n,triangles:e},vectors:r}=this,i=this.circumcenters=this._circumcenters.subarray(0,e.length/3*2);for(let n,r,o=0,a=0,u=e.length;o1;)i-=2;for(let t=2;t4)for(let t=0;t0){if(n>=this.ymax)return null;(i=(this.ymax-n)/r)0){if(t>=this.xmax)return null;(i=(this.xmax-t)/e)this.xmax?2:0)|(nthis.ymax?8:0)}}const cu=2*Math.PI,fu=Math.pow;function su(t){return t[0]}function lu(t){return t[1]}function hu(t,n,e){return[t+Math.sin(t+n)*e,n+Math.cos(t-n)*e]}class du{static from(t,n=su,e=lu,r){return new du("length"in t?function(t,n,e,r){const i=t.length,o=new Float64Array(2*i);for(let a=0;a2&&function(t){const{triangles:n,coords:e}=t;for(let t=0;t1e-10)return!1}return!0}(t)){this.collinear=Int32Array.from({length:n.length/2},((t,n)=>n)).sort(((t,e)=>n[2*t]-n[2*e]||n[2*t+1]-n[2*e+1]));const t=this.collinear[0],e=this.collinear[this.collinear.length-1],r=[n[2*t],n[2*t+1],n[2*e],n[2*e+1]],i=1e-8*Math.hypot(r[3]-r[1],r[2]-r[0]);for(let t=0,e=n.length/2;t0&&(this.triangles=new Int32Array(3).fill(-1),this.halfedges=new Int32Array(3).fill(-1),this.triangles[0]=r[0],o[r[0]]=1,2===r.length&&(o[r[1]]=0,this.triangles[1]=r[1],this.triangles[2]=r[1]))}voronoi(t){return new uu(this,t)}*neighbors(t){const{inedges:n,hull:e,_hullIndex:r,halfedges:i,triangles:o,collinear:a}=this;if(a){const n=a.indexOf(t);return n>0&&(yield a[n-1]),void(n=0&&i!==e&&i!==r;)e=i;return i}_step(t,n,e){const{inedges:r,hull:i,_hullIndex:o,halfedges:a,triangles:u,points:c}=this;if(-1===r[t]||!c.length)return(t+1)%(c.length>>1);let f=t,s=fu(n-c[2*t],2)+fu(e-c[2*t+1],2);const l=r[t];let h=l;do{let r=u[h];const l=fu(n-c[2*r],2)+fu(e-c[2*r+1],2);if(l9999?"+"+_u(t,6):_u(t,4)}(t.getUTCFullYear())+"-"+_u(t.getUTCMonth()+1,2)+"-"+_u(t.getUTCDate(),2)+(i?"T"+_u(n,2)+":"+_u(e,2)+":"+_u(r,2)+"."+_u(i,3)+"Z":r?"T"+_u(n,2)+":"+_u(e,2)+":"+_u(r,2)+"Z":e||n?"T"+_u(n,2)+":"+_u(e,2)+"Z":"")}function mu(t){var n=new RegExp('["'+t+"\n\r]"),e=t.charCodeAt(0);function r(t,n){var r,i=[],o=t.length,a=0,u=0,c=o<=0,f=!1;function s(){if(c)return gu;if(f)return f=!1,pu;var n,r,i=a;if(34===t.charCodeAt(i)){for(;a++=o?c=!0:10===(r=t.charCodeAt(a++))?f=!0:13===r&&(f=!0,10===t.charCodeAt(a)&&++a),t.slice(i+1,n-1).replace(/""/g,'"')}for(;aYu(n,e).then((n=>(new DOMParser).parseFromString(n,t)))}var Vu=Gu("application/xml"),$u=Gu("text/html"),Wu=Gu("image/svg+xml");function Zu(t,n,e,r){if(isNaN(n)||isNaN(e))return t;var i,o,a,u,c,f,s,l,h,d=t._root,p={data:r},g=t._x0,y=t._y0,v=t._x1,_=t._y1;if(!d)return t._root=p,t;for(;d.length;)if((f=n>=(o=(g+v)/2))?g=o:v=o,(s=e>=(a=(y+_)/2))?y=a:_=a,i=d,!(d=d[l=s<<1|f]))return i[l]=p,t;if(u=+t._x.call(null,d.data),c=+t._y.call(null,d.data),n===u&&e===c)return p.next=d,i?i[l]=p:t._root=p,t;do{i=i?i[l]=new Array(4):t._root=new Array(4),(f=n>=(o=(g+v)/2))?g=o:v=o,(s=e>=(a=(y+_)/2))?y=a:_=a}while((l=s<<1|f)==(h=(c>=a)<<1|u>=o));return i[h]=d,i[l]=p,t}function Ku(t,n,e,r,i){this.node=t,this.x0=n,this.y0=e,this.x1=r,this.y1=i}function Qu(t){return t[0]}function Ju(t){return t[1]}function tc(t,n,e){var r=new nc(null==n?Qu:n,null==e?Ju:e,NaN,NaN,NaN,NaN);return null==t?r:r.addAll(t)}function nc(t,n,e,r,i,o){this._x=t,this._y=n,this._x0=e,this._y0=r,this._x1=i,this._y1=o,this._root=void 0}function ec(t){for(var n={data:t.data},e=n;t=t.next;)e=e.next={data:t.data};return n}var rc=tc.prototype=nc.prototype;function ic(t){return function(){return t}}function oc(t){return 1e-6*(t()-.5)}function ac(t){return t.x+t.vx}function uc(t){return t.y+t.vy}function cc(t){return t.index}function fc(t,n){var e=t.get(n);if(!e)throw new Error("node not found: "+n);return e}rc.copy=function(){var t,n,e=new nc(this._x,this._y,this._x0,this._y0,this._x1,this._y1),r=this._root;if(!r)return e;if(!r.length)return e._root=ec(r),e;for(t=[{source:r,target:e._root=new Array(4)}];r=t.pop();)for(var i=0;i<4;++i)(n=r.source[i])&&(n.length?t.push({source:n,target:r.target[i]=new Array(4)}):r.target[i]=ec(n));return e},rc.add=function(t){const n=+this._x.call(null,t),e=+this._y.call(null,t);return Zu(this.cover(n,e),n,e,t)},rc.addAll=function(t){var n,e,r,i,o=t.length,a=new Array(o),u=new Array(o),c=1/0,f=1/0,s=-1/0,l=-1/0;for(e=0;es&&(s=r),il&&(l=i));if(c>s||f>l)return this;for(this.cover(c,f).cover(s,l),e=0;et||t>=i||r>n||n>=o;)switch(u=(nh||(o=c.y0)>d||(a=c.x1)=v)<<1|t>=y)&&(c=p[p.length-1],p[p.length-1]=p[p.length-1-f],p[p.length-1-f]=c)}else{var _=t-+this._x.call(null,g.data),b=n-+this._y.call(null,g.data),m=_*_+b*b;if(m=(u=(p+y)/2))?p=u:y=u,(s=a>=(c=(g+v)/2))?g=c:v=c,n=d,!(d=d[l=s<<1|f]))return this;if(!d.length)break;(n[l+1&3]||n[l+2&3]||n[l+3&3])&&(e=n,h=l)}for(;d.data!==t;)if(r=d,!(d=d.next))return this;return(i=d.next)&&delete d.next,r?(i?r.next=i:delete r.next,this):n?(i?n[l]=i:delete n[l],(d=n[0]||n[1]||n[2]||n[3])&&d===(n[3]||n[2]||n[1]||n[0])&&!d.length&&(e?e[h]=d:this._root=d),this):(this._root=i,this)},rc.removeAll=function(t){for(var n=0,e=t.length;n1?r[0]+r.slice(2):r,+t.slice(e+1)]}function gc(t){return(t=pc(Math.abs(t)))?t[1]:NaN}var yc,vc=/^(?:(.)?([<>=^]))?([+\-( ])?([$#])?(0)?(\d+)?(,)?(\.\d+)?(~)?([a-z%])?$/i;function _c(t){if(!(n=vc.exec(t)))throw new Error("invalid format: "+t);var n;return new bc({fill:n[1],align:n[2],sign:n[3],symbol:n[4],zero:n[5],width:n[6],comma:n[7],precision:n[8]&&n[8].slice(1),trim:n[9],type:n[10]})}function bc(t){this.fill=void 0===t.fill?" ":t.fill+"",this.align=void 0===t.align?">":t.align+"",this.sign=void 0===t.sign?"-":t.sign+"",this.symbol=void 0===t.symbol?"":t.symbol+"",this.zero=!!t.zero,this.width=void 0===t.width?void 0:+t.width,this.comma=!!t.comma,this.precision=void 0===t.precision?void 0:+t.precision,this.trim=!!t.trim,this.type=void 0===t.type?"":t.type+""}function mc(t,n){var e=pc(t,n);if(!e)return t+"";var r=e[0],i=e[1];return i<0?"0."+new Array(-i).join("0")+r:r.length>i+1?r.slice(0,i+1)+"."+r.slice(i+1):r+new Array(i-r.length+2).join("0")}_c.prototype=bc.prototype,bc.prototype.toString=function(){return this.fill+this.align+this.sign+this.symbol+(this.zero?"0":"")+(void 0===this.width?"":Math.max(1,0|this.width))+(this.comma?",":"")+(void 0===this.precision?"":"."+Math.max(0,0|this.precision))+(this.trim?"~":"")+this.type};var xc={"%":(t,n)=>(100*t).toFixed(n),b:t=>Math.round(t).toString(2),c:t=>t+"",d:function(t){return Math.abs(t=Math.round(t))>=1e21?t.toLocaleString("en").replace(/,/g,""):t.toString(10)},e:(t,n)=>t.toExponential(n),f:(t,n)=>t.toFixed(n),g:(t,n)=>t.toPrecision(n),o:t=>Math.round(t).toString(8),p:(t,n)=>mc(100*t,n),r:mc,s:function(t,n){var e=pc(t,n);if(!e)return t+"";var r=e[0],i=e[1],o=i-(yc=3*Math.max(-8,Math.min(8,Math.floor(i/3))))+1,a=r.length;return o===a?r:o>a?r+new Array(o-a+1).join("0"):o>0?r.slice(0,o)+"."+r.slice(o):"0."+new Array(1-o).join("0")+pc(t,Math.max(0,n+o-1))[0]},X:t=>Math.round(t).toString(16).toUpperCase(),x:t=>Math.round(t).toString(16)};function wc(t){return t}var Mc,Ac=Array.prototype.map,Tc=["y","z","a","f","p","n","µ","m","","k","M","G","T","P","E","Z","Y"];function Sc(t){var n,e,r=void 0===t.grouping||void 0===t.thousands?wc:(n=Ac.call(t.grouping,Number),e=t.thousands+"",function(t,r){for(var i=t.length,o=[],a=0,u=n[0],c=0;i>0&&u>0&&(c+u+1>r&&(u=Math.max(1,r-c)),o.push(t.substring(i-=u,i+u)),!((c+=u+1)>r));)u=n[a=(a+1)%n.length];return o.reverse().join(e)}),i=void 0===t.currency?"":t.currency[0]+"",o=void 0===t.currency?"":t.currency[1]+"",a=void 0===t.decimal?".":t.decimal+"",u=void 0===t.numerals?wc:function(t){return function(n){return n.replace(/[0-9]/g,(function(n){return t[+n]}))}}(Ac.call(t.numerals,String)),c=void 0===t.percent?"%":t.percent+"",f=void 0===t.minus?"−":t.minus+"",s=void 0===t.nan?"NaN":t.nan+"";function l(t){var n=(t=_c(t)).fill,e=t.align,l=t.sign,h=t.symbol,d=t.zero,p=t.width,g=t.comma,y=t.precision,v=t.trim,_=t.type;"n"===_?(g=!0,_="g"):xc[_]||(void 0===y&&(y=12),v=!0,_="g"),(d||"0"===n&&"="===e)&&(d=!0,n="0",e="=");var b="$"===h?i:"#"===h&&/[boxX]/.test(_)?"0"+_.toLowerCase():"",m="$"===h?o:/[%p]/.test(_)?c:"",x=xc[_],w=/[defgprs%]/.test(_);function M(t){var i,o,c,h=b,M=m;if("c"===_)M=x(t)+M,t="";else{var A=(t=+t)<0||1/t<0;if(t=isNaN(t)?s:x(Math.abs(t),y),v&&(t=function(t){t:for(var n,e=t.length,r=1,i=-1;r0&&(i=0)}return i>0?t.slice(0,i)+t.slice(n+1):t}(t)),A&&0==+t&&"+"!==l&&(A=!1),h=(A?"("===l?l:f:"-"===l||"("===l?"":l)+h,M=("s"===_?Tc[8+yc/3]:"")+M+(A&&"("===l?")":""),w)for(i=-1,o=t.length;++i(c=t.charCodeAt(i))||c>57){M=(46===c?a+t.slice(i+1):t.slice(i))+M,t=t.slice(0,i);break}}g&&!d&&(t=r(t,1/0));var T=h.length+t.length+M.length,S=T>1)+h+t+M+S.slice(T);break;default:t=S+h+t+M}return u(t)}return y=void 0===y?6:/[gprs]/.test(_)?Math.max(1,Math.min(21,y)):Math.max(0,Math.min(20,y)),M.toString=function(){return t+""},M}return{format:l,formatPrefix:function(t,n){var e=l(((t=_c(t)).type="f",t)),r=3*Math.max(-8,Math.min(8,Math.floor(gc(n)/3))),i=Math.pow(10,-r),o=Tc[8+r/3];return function(t){return e(i*t)+o}}}}function Ec(n){return Mc=Sc(n),t.format=Mc.format,t.formatPrefix=Mc.formatPrefix,Mc}function kc(t){return Math.max(0,-gc(Math.abs(t)))}function Nc(t,n){return Math.max(0,3*Math.max(-8,Math.min(8,Math.floor(gc(n)/3)))-gc(Math.abs(t)))}function Cc(t,n){return t=Math.abs(t),n=Math.abs(n)-t,Math.max(0,gc(n)-gc(t))+1}t.format=void 0,t.formatPrefix=void 0,Ec({thousands:",",grouping:[3],currency:["$",""]});var Pc=1e-6,zc=1e-12,Dc=Math.PI,qc=Dc/2,Rc=Dc/4,Fc=2*Dc,Oc=180/Dc,Ic=Dc/180,Uc=Math.abs,Bc=Math.atan,Yc=Math.atan2,Lc=Math.cos,jc=Math.ceil,Hc=Math.exp,Xc=Math.hypot,Gc=Math.log,Vc=Math.pow,$c=Math.sin,Wc=Math.sign||function(t){return t>0?1:t<0?-1:0},Zc=Math.sqrt,Kc=Math.tan;function Qc(t){return t>1?0:t<-1?Dc:Math.acos(t)}function Jc(t){return t>1?qc:t<-1?-qc:Math.asin(t)}function tf(t){return(t=$c(t/2))*t}function nf(){}function ef(t,n){t&&of.hasOwnProperty(t.type)&&of[t.type](t,n)}var rf={Feature:function(t,n){ef(t.geometry,n)},FeatureCollection:function(t,n){for(var e=t.features,r=-1,i=e.length;++r=0?1:-1,i=r*e,o=Lc(n=(n*=Ic)/2+Rc),a=$c(n),u=df*a,c=hf*o+u*Lc(i),f=u*r*$c(i);Tf.add(Yc(f,c)),lf=t,hf=o,df=a}function zf(t){return[Yc(t[1],t[0]),Jc(t[2])]}function Df(t){var n=t[0],e=t[1],r=Lc(e);return[r*Lc(n),r*$c(n),$c(e)]}function qf(t,n){return t[0]*n[0]+t[1]*n[1]+t[2]*n[2]}function Rf(t,n){return[t[1]*n[2]-t[2]*n[1],t[2]*n[0]-t[0]*n[2],t[0]*n[1]-t[1]*n[0]]}function Ff(t,n){t[0]+=n[0],t[1]+=n[1],t[2]+=n[2]}function Of(t,n){return[t[0]*n,t[1]*n,t[2]*n]}function If(t){var n=Zc(t[0]*t[0]+t[1]*t[1]+t[2]*t[2]);t[0]/=n,t[1]/=n,t[2]/=n}var Uf,Bf,Yf,Lf,jf,Hf,Xf,Gf,Vf,$f,Wf,Zf,Kf,Qf,Jf,ts,ns={point:es,lineStart:is,lineEnd:os,polygonStart:function(){ns.point=as,ns.lineStart=us,ns.lineEnd=cs,wf=new g,Ef.polygonStart()},polygonEnd:function(){Ef.polygonEnd(),ns.point=es,ns.lineStart=is,ns.lineEnd=os,Tf<0?(pf=-(yf=180),gf=-(vf=90)):wf>Pc?vf=90:wf<-1e-6&&(gf=-90),Af[0]=pf,Af[1]=yf},sphere:function(){pf=-(yf=180),gf=-(vf=90)}};function es(t,n){Mf.push(Af=[pf=t,yf=t]),nvf&&(vf=n)}function rs(t,n){var e=Df([t*Ic,n*Ic]);if(xf){var r=Rf(xf,e),i=Rf([r[1],-r[0],0],r);If(i),i=zf(i);var o,a=t-_f,u=a>0?1:-1,c=i[0]*Oc*u,f=Uc(a)>180;f^(u*_fvf&&(vf=o):f^(u*_f<(c=(c+360)%360-180)&&cvf&&(vf=n)),f?t<_f?fs(pf,t)>fs(pf,yf)&&(yf=t):fs(t,yf)>fs(pf,yf)&&(pf=t):yf>=pf?(tyf&&(yf=t)):t>_f?fs(pf,t)>fs(pf,yf)&&(yf=t):fs(t,yf)>fs(pf,yf)&&(pf=t)}else Mf.push(Af=[pf=t,yf=t]);nvf&&(vf=n),xf=e,_f=t}function is(){ns.point=rs}function os(){Af[0]=pf,Af[1]=yf,ns.point=es,xf=null}function as(t,n){if(xf){var e=t-_f;wf.add(Uc(e)>180?e+(e>0?360:-360):e)}else bf=t,mf=n;Ef.point(t,n),rs(t,n)}function us(){Ef.lineStart()}function cs(){as(bf,mf),Ef.lineEnd(),Uc(wf)>Pc&&(pf=-(yf=180)),Af[0]=pf,Af[1]=yf,xf=null}function fs(t,n){return(n-=t)<0?n+360:n}function ss(t,n){return t[0]-n[0]}function ls(t,n){return t[0]<=t[1]?t[0]<=n&&n<=t[1]:nDc?t+Math.round(-t/Fc)*Fc:t,n]}function Ss(t,n,e){return(t%=Fc)?n||e?As(ks(t),Ns(n,e)):ks(t):n||e?Ns(n,e):Ts}function Es(t){return function(n,e){return[(n+=t)>Dc?n-Fc:n<-Dc?n+Fc:n,e]}}function ks(t){var n=Es(t);return n.invert=Es(-t),n}function Ns(t,n){var e=Lc(t),r=$c(t),i=Lc(n),o=$c(n);function a(t,n){var a=Lc(n),u=Lc(t)*a,c=$c(t)*a,f=$c(n),s=f*e+u*r;return[Yc(c*i-s*o,u*e-f*r),Jc(s*i+c*o)]}return a.invert=function(t,n){var a=Lc(n),u=Lc(t)*a,c=$c(t)*a,f=$c(n),s=f*i-c*o;return[Yc(c*i+f*o,u*e+s*r),Jc(s*e-u*r)]},a}function Cs(t){function n(n){return(n=t(n[0]*Ic,n[1]*Ic))[0]*=Oc,n[1]*=Oc,n}return t=Ss(t[0]*Ic,t[1]*Ic,t.length>2?t[2]*Ic:0),n.invert=function(n){return(n=t.invert(n[0]*Ic,n[1]*Ic))[0]*=Oc,n[1]*=Oc,n},n}function Ps(t,n,e,r,i,o){if(e){var a=Lc(n),u=$c(n),c=r*e;null==i?(i=n+r*Fc,o=n-c/2):(i=zs(a,i),o=zs(a,o),(r>0?io)&&(i+=r*Fc));for(var f,s=i;r>0?s>o:s1&&n.push(n.pop().concat(n.shift()))},result:function(){var e=n;return n=[],t=null,e}}}function qs(t,n){return Uc(t[0]-n[0])=0;--o)i.point((s=f[o])[0],s[1]);else r(h.x,h.p.x,-1,i);h=h.p}f=(h=h.o).z,d=!d}while(!h.v);i.lineEnd()}}}function Os(t){if(n=t.length){for(var n,e,r=0,i=t[0];++r=0?1:-1,E=S*T,k=E>Dc,N=v*M;if(c.add(Yc(N*S*$c(E),_*A+N*Lc(E))),a+=k?T+S*Fc:T,k^p>=e^x>=e){var C=Rf(Df(d),Df(m));If(C);var P=Rf(o,C);If(P);var z=(k^T>=0?-1:1)*Jc(P[2]);(r>z||r===z&&(C[0]||C[1]))&&(u+=k^T>=0?1:-1)}}return(a<-1e-6||a0){for(l||(i.polygonStart(),l=!0),i.lineStart(),t=0;t1&&2&c&&h.push(h.pop().concat(h.shift())),a.push(h.filter(Ys))}return h}}function Ys(t){return t.length>1}function Ls(t,n){return((t=t.x)[0]<0?t[1]-qc-Pc:qc-t[1])-((n=n.x)[0]<0?n[1]-qc-Pc:qc-n[1])}Ts.invert=Ts;var js=Bs((function(){return!0}),(function(t){var n,e=NaN,r=NaN,i=NaN;return{lineStart:function(){t.lineStart(),n=1},point:function(o,a){var u=o>0?Dc:-Dc,c=Uc(o-e);Uc(c-Dc)0?qc:-qc),t.point(i,r),t.lineEnd(),t.lineStart(),t.point(u,r),t.point(o,r),n=0):i!==u&&c>=Dc&&(Uc(e-i)Pc?Bc(($c(n)*(o=Lc(r))*$c(e)-$c(r)*(i=Lc(n))*$c(t))/(i*o*a)):(n+r)/2}(e,r,o,a),t.point(i,r),t.lineEnd(),t.lineStart(),t.point(u,r),n=0),t.point(e=o,r=a),i=u},lineEnd:function(){t.lineEnd(),e=r=NaN},clean:function(){return 2-n}}}),(function(t,n,e,r){var i;if(null==t)i=e*qc,r.point(-Dc,i),r.point(0,i),r.point(Dc,i),r.point(Dc,0),r.point(Dc,-i),r.point(0,-i),r.point(-Dc,-i),r.point(-Dc,0),r.point(-Dc,i);else if(Uc(t[0]-n[0])>Pc){var o=t[0]0,i=Uc(n)>Pc;function o(t,e){return Lc(t)*Lc(e)>n}function a(t,e,r){var i=[1,0,0],o=Rf(Df(t),Df(e)),a=qf(o,o),u=o[0],c=a-u*u;if(!c)return!r&&t;var f=n*a/c,s=-n*u/c,l=Rf(i,o),h=Of(i,f);Ff(h,Of(o,s));var d=l,p=qf(h,d),g=qf(d,d),y=p*p-g*(qf(h,h)-1);if(!(y<0)){var v=Zc(y),_=Of(d,(-p-v)/g);if(Ff(_,h),_=zf(_),!r)return _;var b,m=t[0],x=e[0],w=t[1],M=e[1];x0^_[1]<(Uc(_[0]-m)Dc^(m<=_[0]&&_[0]<=x)){var S=Of(d,(-p+v)/g);return Ff(S,h),[_,zf(S)]}}}function u(n,e){var i=r?t:Dc-t,o=0;return n<-i?o|=1:n>i&&(o|=2),e<-i?o|=4:e>i&&(o|=8),o}return Bs(o,(function(t){var n,e,c,f,s;return{lineStart:function(){f=c=!1,s=1},point:function(l,h){var d,p=[l,h],g=o(l,h),y=r?g?0:u(l,h):g?u(l+(l<0?Dc:-Dc),h):0;if(!n&&(f=c=g)&&t.lineStart(),g!==c&&(!(d=a(n,p))||qs(n,d)||qs(p,d))&&(p[2]=1),g!==c)s=0,g?(t.lineStart(),d=a(p,n),t.point(d[0],d[1])):(d=a(n,p),t.point(d[0],d[1],2),t.lineEnd()),n=d;else if(i&&n&&r^g){var v;y&e||!(v=a(p,n,!0))||(s=0,r?(t.lineStart(),t.point(v[0][0],v[0][1]),t.point(v[1][0],v[1][1]),t.lineEnd()):(t.point(v[1][0],v[1][1]),t.lineEnd(),t.lineStart(),t.point(v[0][0],v[0][1],3)))}!g||n&&qs(n,p)||t.point(p[0],p[1]),n=p,c=g,e=y},lineEnd:function(){c&&t.lineEnd(),n=null},clean:function(){return s|(f&&c)<<1}}}),(function(n,r,i,o){Ps(o,t,e,i,n,r)}),r?[0,-t]:[-Dc,t-Dc])}var Xs,Gs,Vs,$s,Ws=1e9,Zs=-Ws;function Ks(t,n,e,r){function i(i,o){return t<=i&&i<=e&&n<=o&&o<=r}function o(i,o,u,f){var s=0,l=0;if(null==i||(s=a(i,u))!==(l=a(o,u))||c(i,o)<0^u>0)do{f.point(0===s||3===s?t:e,s>1?r:n)}while((s=(s+u+4)%4)!==l);else f.point(o[0],o[1])}function a(r,i){return Uc(r[0]-t)0?0:3:Uc(r[0]-e)0?2:1:Uc(r[1]-n)0?1:0:i>0?3:2}function u(t,n){return c(t.x,n.x)}function c(t,n){var e=a(t,1),r=a(n,1);return e!==r?e-r:0===e?n[1]-t[1]:1===e?t[0]-n[0]:2===e?t[1]-n[1]:n[0]-t[0]}return function(a){var c,f,s,l,h,d,p,g,y,v,_,b=a,m=Ds(),x={point:w,lineStart:function(){x.point=M,f&&f.push(s=[]);v=!0,y=!1,p=g=NaN},lineEnd:function(){c&&(M(l,h),d&&y&&m.rejoin(),c.push(m.result()));x.point=w,y&&b.lineEnd()},polygonStart:function(){b=m,c=[],f=[],_=!0},polygonEnd:function(){var n=function(){for(var n=0,e=0,i=f.length;er&&(h-o)*(r-a)>(d-a)*(t-o)&&++n:d<=r&&(h-o)*(r-a)<(d-a)*(t-o)&&--n;return n}(),e=_&&n,i=(c=Z(c)).length;(e||i)&&(a.polygonStart(),e&&(a.lineStart(),o(null,null,1,a),a.lineEnd()),i&&Fs(c,u,n,o,a),a.polygonEnd());b=a,c=f=s=null}};function w(t,n){i(t,n)&&b.point(t,n)}function M(o,a){var u=i(o,a);if(f&&s.push([o,a]),v)l=o,h=a,d=u,v=!1,u&&(b.lineStart(),b.point(o,a));else if(u&&y)b.point(o,a);else{var c=[p=Math.max(Zs,Math.min(Ws,p)),g=Math.max(Zs,Math.min(Ws,g))],m=[o=Math.max(Zs,Math.min(Ws,o)),a=Math.max(Zs,Math.min(Ws,a))];!function(t,n,e,r,i,o){var a,u=t[0],c=t[1],f=0,s=1,l=n[0]-u,h=n[1]-c;if(a=e-u,l||!(a>0)){if(a/=l,l<0){if(a0){if(a>s)return;a>f&&(f=a)}if(a=i-u,l||!(a<0)){if(a/=l,l<0){if(a>s)return;a>f&&(f=a)}else if(l>0){if(a0)){if(a/=h,h<0){if(a0){if(a>s)return;a>f&&(f=a)}if(a=o-c,h||!(a<0)){if(a/=h,h<0){if(a>s)return;a>f&&(f=a)}else if(h>0){if(a0&&(t[0]=u+f*l,t[1]=c+f*h),s<1&&(n[0]=u+s*l,n[1]=c+s*h),!0}}}}}(c,m,t,n,e,r)?u&&(b.lineStart(),b.point(o,a),_=!1):(y||(b.lineStart(),b.point(c[0],c[1])),b.point(m[0],m[1]),u||b.lineEnd(),_=!1)}p=o,g=a,y=u}return x}}var Qs={sphere:nf,point:nf,lineStart:function(){Qs.point=tl,Qs.lineEnd=Js},lineEnd:nf,polygonStart:nf,polygonEnd:nf};function Js(){Qs.point=Qs.lineEnd=nf}function tl(t,n){Gs=t*=Ic,Vs=$c(n*=Ic),$s=Lc(n),Qs.point=nl}function nl(t,n){t*=Ic;var e=$c(n*=Ic),r=Lc(n),i=Uc(t-Gs),o=Lc(i),a=r*$c(i),u=$s*e-Vs*r*o,c=Vs*e+$s*r*o;Xs.add(Yc(Zc(a*a+u*u),c)),Gs=t,Vs=e,$s=r}function el(t){return Xs=new g,cf(t,Qs),+Xs}var rl=[null,null],il={type:"LineString",coordinates:rl};function ol(t,n){return rl[0]=t,rl[1]=n,el(il)}var al={Feature:function(t,n){return cl(t.geometry,n)},FeatureCollection:function(t,n){for(var e=t.features,r=-1,i=e.length;++r0&&(i=ol(t[o],t[o-1]))>0&&e<=i&&r<=i&&(e+r-i)*(1-Math.pow((e-r)/i,2))Pc})).map(c)).concat(J(jc(o/d)*d,i,d).filter((function(t){return Uc(t%g)>Pc})).map(f))}return v.lines=function(){return _().map((function(t){return{type:"LineString",coordinates:t}}))},v.outline=function(){return{type:"Polygon",coordinates:[s(r).concat(l(a).slice(1),s(e).reverse().slice(1),l(u).reverse().slice(1))]}},v.extent=function(t){return arguments.length?v.extentMajor(t).extentMinor(t):v.extentMinor()},v.extentMajor=function(t){return arguments.length?(r=+t[0][0],e=+t[1][0],u=+t[0][1],a=+t[1][1],r>e&&(t=r,r=e,e=t),u>a&&(t=u,u=a,a=t),v.precision(y)):[[r,u],[e,a]]},v.extentMinor=function(e){return arguments.length?(n=+e[0][0],t=+e[1][0],o=+e[0][1],i=+e[1][1],n>t&&(e=n,n=t,t=e),o>i&&(e=o,o=i,i=e),v.precision(y)):[[n,o],[t,i]]},v.step=function(t){return arguments.length?v.stepMajor(t).stepMinor(t):v.stepMinor()},v.stepMajor=function(t){return arguments.length?(p=+t[0],g=+t[1],v):[p,g]},v.stepMinor=function(t){return arguments.length?(h=+t[0],d=+t[1],v):[h,d]},v.precision=function(h){return arguments.length?(y=+h,c=pl(o,i,90),f=gl(n,t,y),s=pl(u,a,90),l=gl(r,e,y),v):y},v.extentMajor([[-180,-89.999999],[180,89.999999]]).extentMinor([[-180,-80.000001],[180,80.000001]])}var vl,_l,bl,ml,xl=t=>t,wl=new g,Ml=new g,Al={point:nf,lineStart:nf,lineEnd:nf,polygonStart:function(){Al.lineStart=Tl,Al.lineEnd=kl},polygonEnd:function(){Al.lineStart=Al.lineEnd=Al.point=nf,wl.add(Uc(Ml)),Ml=new g},result:function(){var t=wl/2;return wl=new g,t}};function Tl(){Al.point=Sl}function Sl(t,n){Al.point=El,vl=bl=t,_l=ml=n}function El(t,n){Ml.add(ml*t-bl*n),bl=t,ml=n}function kl(){El(vl,_l)}var Nl=1/0,Cl=Nl,Pl=-Nl,zl=Pl,Dl={point:function(t,n){tPl&&(Pl=t);nzl&&(zl=n)},lineStart:nf,lineEnd:nf,polygonStart:nf,polygonEnd:nf,result:function(){var t=[[Nl,Cl],[Pl,zl]];return Pl=zl=-(Cl=Nl=1/0),t}};var ql,Rl,Fl,Ol,Il=0,Ul=0,Bl=0,Yl=0,Ll=0,jl=0,Hl=0,Xl=0,Gl=0,Vl={point:$l,lineStart:Wl,lineEnd:Ql,polygonStart:function(){Vl.lineStart=Jl,Vl.lineEnd=th},polygonEnd:function(){Vl.point=$l,Vl.lineStart=Wl,Vl.lineEnd=Ql},result:function(){var t=Gl?[Hl/Gl,Xl/Gl]:jl?[Yl/jl,Ll/jl]:Bl?[Il/Bl,Ul/Bl]:[NaN,NaN];return Il=Ul=Bl=Yl=Ll=jl=Hl=Xl=Gl=0,t}};function $l(t,n){Il+=t,Ul+=n,++Bl}function Wl(){Vl.point=Zl}function Zl(t,n){Vl.point=Kl,$l(Fl=t,Ol=n)}function Kl(t,n){var e=t-Fl,r=n-Ol,i=Zc(e*e+r*r);Yl+=i*(Fl+t)/2,Ll+=i*(Ol+n)/2,jl+=i,$l(Fl=t,Ol=n)}function Ql(){Vl.point=$l}function Jl(){Vl.point=nh}function th(){eh(ql,Rl)}function nh(t,n){Vl.point=eh,$l(ql=Fl=t,Rl=Ol=n)}function eh(t,n){var e=t-Fl,r=n-Ol,i=Zc(e*e+r*r);Yl+=i*(Fl+t)/2,Ll+=i*(Ol+n)/2,jl+=i,Hl+=(i=Ol*t-Fl*n)*(Fl+t),Xl+=i*(Ol+n),Gl+=3*i,$l(Fl=t,Ol=n)}function rh(t){this._context=t}rh.prototype={_radius:4.5,pointRadius:function(t){return this._radius=t,this},polygonStart:function(){this._line=0},polygonEnd:function(){this._line=NaN},lineStart:function(){this._point=0},lineEnd:function(){0===this._line&&this._context.closePath(),this._point=NaN},point:function(t,n){switch(this._point){case 0:this._context.moveTo(t,n),this._point=1;break;case 1:this._context.lineTo(t,n);break;default:this._context.moveTo(t+this._radius,n),this._context.arc(t,n,this._radius,0,Fc)}},result:nf};var ih,oh,ah,uh,ch,fh=new g,sh={point:nf,lineStart:function(){sh.point=lh},lineEnd:function(){ih&&hh(oh,ah),sh.point=nf},polygonStart:function(){ih=!0},polygonEnd:function(){ih=null},result:function(){var t=+fh;return fh=new g,t}};function lh(t,n){sh.point=hh,oh=uh=t,ah=ch=n}function hh(t,n){uh-=t,ch-=n,fh.add(Zc(uh*uh+ch*ch)),uh=t,ch=n}function dh(){this._string=[]}function ph(t){return"m0,"+t+"a"+t+","+t+" 0 1,1 0,"+-2*t+"a"+t+","+t+" 0 1,1 0,"+2*t+"z"}function gh(t){return function(n){var e=new yh;for(var r in t)e[r]=t[r];return e.stream=n,e}}function yh(){}function vh(t,n,e){var r=t.clipExtent&&t.clipExtent();return t.scale(150).translate([0,0]),null!=r&&t.clipExtent(null),cf(e,t.stream(Dl)),n(Dl.result()),null!=r&&t.clipExtent(r),t}function _h(t,n,e){return vh(t,(function(e){var r=n[1][0]-n[0][0],i=n[1][1]-n[0][1],o=Math.min(r/(e[1][0]-e[0][0]),i/(e[1][1]-e[0][1])),a=+n[0][0]+(r-o*(e[1][0]+e[0][0]))/2,u=+n[0][1]+(i-o*(e[1][1]+e[0][1]))/2;t.scale(150*o).translate([a,u])}),e)}function bh(t,n,e){return _h(t,[[0,0],n],e)}function mh(t,n,e){return vh(t,(function(e){var r=+n,i=r/(e[1][0]-e[0][0]),o=(r-i*(e[1][0]+e[0][0]))/2,a=-i*e[0][1];t.scale(150*i).translate([o,a])}),e)}function xh(t,n,e){return vh(t,(function(e){var r=+n,i=r/(e[1][1]-e[0][1]),o=-i*e[0][0],a=(r-i*(e[1][1]+e[0][1]))/2;t.scale(150*i).translate([o,a])}),e)}dh.prototype={_radius:4.5,_circle:ph(4.5),pointRadius:function(t){return(t=+t)!==this._radius&&(this._radius=t,this._circle=null),this},polygonStart:function(){this._line=0},polygonEnd:function(){this._line=NaN},lineStart:function(){this._point=0},lineEnd:function(){0===this._line&&this._string.push("Z"),this._point=NaN},point:function(t,n){switch(this._point){case 0:this._string.push("M",t,",",n),this._point=1;break;case 1:this._string.push("L",t,",",n);break;default:null==this._circle&&(this._circle=ph(this._radius)),this._string.push("M",t,",",n,this._circle)}},result:function(){if(this._string.length){var t=this._string.join("");return this._string=[],t}return null}},yh.prototype={constructor:yh,point:function(t,n){this.stream.point(t,n)},sphere:function(){this.stream.sphere()},lineStart:function(){this.stream.lineStart()},lineEnd:function(){this.stream.lineEnd()},polygonStart:function(){this.stream.polygonStart()},polygonEnd:function(){this.stream.polygonEnd()}};var wh=Lc(30*Ic);function Mh(t,n){return+n?function(t,n){function e(r,i,o,a,u,c,f,s,l,h,d,p,g,y){var v=f-r,_=s-i,b=v*v+_*_;if(b>4*n&&g--){var m=a+h,x=u+d,w=c+p,M=Zc(m*m+x*x+w*w),A=Jc(w/=M),T=Uc(Uc(w)-1)n||Uc((v*N+_*C)/b-.5)>.3||a*h+u*d+c*p2?t[2]%360*Ic:0,N()):[y*Oc,v*Oc,_*Oc]},E.angle=function(t){return arguments.length?(b=t%360*Ic,N()):b*Oc},E.reflectX=function(t){return arguments.length?(m=t?-1:1,N()):m<0},E.reflectY=function(t){return arguments.length?(x=t?-1:1,N()):x<0},E.precision=function(t){return arguments.length?(a=Mh(u,S=t*t),C()):Zc(S)},E.fitExtent=function(t,n){return _h(E,t,n)},E.fitSize=function(t,n){return bh(E,t,n)},E.fitWidth=function(t,n){return mh(E,t,n)},E.fitHeight=function(t,n){return xh(E,t,n)},function(){return n=t.apply(this,arguments),E.invert=n.invert&&k,N()}}function kh(t){var n=0,e=Dc/3,r=Eh(t),i=r(n,e);return i.parallels=function(t){return arguments.length?r(n=t[0]*Ic,e=t[1]*Ic):[n*Oc,e*Oc]},i}function Nh(t,n){var e=$c(t),r=(e+$c(n))/2;if(Uc(r)0?n<-qc+Pc&&(n=-qc+Pc):n>qc-Pc&&(n=qc-Pc);var e=i/Vc(Ih(n),r);return[e*$c(r*t),i-e*Lc(r*t)]}return o.invert=function(t,n){var e=i-n,o=Wc(r)*Zc(t*t+e*e),a=Yc(t,Uc(e))*Wc(e);return e*r<0&&(a-=Dc*Wc(t)*Wc(e)),[a/r,2*Bc(Vc(i/o,1/r))-qc]},o}function Bh(t,n){return[t,n]}function Yh(t,n){var e=Lc(t),r=t===n?$c(t):(e-Lc(n))/(n-t),i=e/r+t;if(Uc(r)=0;)n+=e[r].value;else n=1;t.value=n}function rd(t,n){t instanceof Map?(t=[void 0,t],void 0===n&&(n=od)):void 0===n&&(n=id);for(var e,r,i,o,a,u=new cd(t),c=[u];e=c.pop();)if((i=n(e.data))&&(a=(i=Array.from(i)).length))for(e.children=i,o=a-1;o>=0;--o)c.push(r=i[o]=new cd(i[o])),r.parent=e,r.depth=e.depth+1;return u.eachBefore(ud)}function id(t){return t.children}function od(t){return Array.isArray(t)?t[1]:null}function ad(t){void 0!==t.data.value&&(t.value=t.data.value),t.data=t.data.data}function ud(t){var n=0;do{t.height=n}while((t=t.parent)&&t.height<++n)}function cd(t){this.data=t,this.depth=this.height=0,this.parent=null}function fd(t){for(var n,e,r=0,i=(t=function(t){for(var n,e,r=t.length;r;)e=Math.random()*r--|0,n=t[r],t[r]=t[e],t[e]=n;return t}(Array.from(t))).length,o=[];r0&&e*e>r*r+i*i}function dd(t,n){for(var e=0;e(a*=a)?(r=(f+a-i)/(2*f),o=Math.sqrt(Math.max(0,a/f-r*r)),e.x=t.x-r*u-o*c,e.y=t.y-r*c+o*u):(r=(f+i-a)/(2*f),o=Math.sqrt(Math.max(0,i/f-r*r)),e.x=n.x+r*u-o*c,e.y=n.y+r*c+o*u)):(e.x=n.x+e.r,e.y=n.y)}function _d(t,n){var e=t.r+n.r-1e-6,r=n.x-t.x,i=n.y-t.y;return e>0&&e*e>r*r+i*i}function bd(t){var n=t._,e=t.next._,r=n.r+e.r,i=(n.x*e.r+e.x*n.r)/r,o=(n.y*e.r+e.y*n.r)/r;return i*i+o*o}function md(t){this._=t,this.next=null,this.previous=null}function xd(t){if(!(i=(t=function(t){return"object"==typeof t&&"length"in t?t:Array.from(t)}(t)).length))return 0;var n,e,r,i,o,a,u,c,f,s,l;if((n=t[0]).x=0,n.y=0,!(i>1))return n.r;if(e=t[1],n.x=-e.r,e.x=n.r,e.y=0,!(i>2))return n.r+e.r;vd(e,n,r=t[2]),n=new md(n),e=new md(e),r=new md(r),n.next=r.previous=e,e.next=n.previous=r,r.next=e.previous=n;t:for(u=3;uPc&&--i>0);return[t/(.8707+(o=r*r)*(o*(o*o*o*(.003971-.001529*o)-.013791)-.131979)),r]},Zh.invert=Dh(Jc),Kh.invert=Dh((function(t){return 2*Bc(t)})),Qh.invert=function(t,n){return[-n,2*Bc(Hc(t))-qc]},cd.prototype=rd.prototype={constructor:cd,count:function(){return this.eachAfter(ed)},each:function(t,n){let e=-1;for(const r of this)t.call(n,r,++e,this);return this},eachAfter:function(t,n){for(var e,r,i,o=this,a=[o],u=[],c=-1;o=a.pop();)if(u.push(o),e=o.children)for(r=0,i=e.length;r=0;--r)o.push(e[r]);return this},find:function(t,n){let e=-1;for(const r of this)if(t.call(n,r,++e,this))return r},sum:function(t){return this.eachAfter((function(n){for(var e=+t(n.data)||0,r=n.children,i=r&&r.length;--i>=0;)e+=r[i].value;n.value=e}))},sort:function(t){return this.eachBefore((function(n){n.children&&n.children.sort(t)}))},path:function(t){for(var n=this,e=function(t,n){if(t===n)return t;var e=t.ancestors(),r=n.ancestors(),i=null;t=e.pop(),n=r.pop();for(;t===n;)i=t,t=e.pop(),n=r.pop();return i}(n,t),r=[n];n!==e;)n=n.parent,r.push(n);for(var i=r.length;t!==e;)r.splice(i,0,t),t=t.parent;return r},ancestors:function(){for(var t=this,n=[t];t=t.parent;)n.push(t);return n},descendants:function(){return Array.from(this)},leaves:function(){var t=[];return this.eachBefore((function(n){n.children||t.push(n)})),t},links:function(){var t=this,n=[];return t.each((function(e){e!==t&&n.push({source:e.parent,target:e})})),n},copy:function(){return rd(this).eachBefore(ad)},[Symbol.iterator]:function*(){var t,n,e,r,i=this,o=[i];do{for(t=o.reverse(),o=[];i=t.pop();)if(yield i,n=i.children)for(e=0,r=n.length;eh&&(h=u),y=s*s*g,(d=Math.max(h/y,y/l))>p){s-=u;break}p=d}v.push(a={value:s,dice:c1?n:1)},e}(jd);var Gd=function t(n){function e(t,e,r,i,o){if((a=t._squarify)&&a.ratio===n)for(var a,u,c,f,s,l=-1,h=a.length,d=t.value;++l1?n:1)},e}(jd);function Vd(t,n,e){return(n[0]-t[0])*(e[1]-t[1])-(n[1]-t[1])*(e[0]-t[0])}function $d(t,n){return t[0]-n[0]||t[1]-n[1]}function Wd(t){const n=t.length,e=[0,1];let r,i=2;for(r=2;r1&&Vd(t[e[i-2]],t[e[i-1]],t[r])<=0;)--i;e[i++]=r}return e.slice(0,i)}var Zd=Math.random,Kd=function t(n){function e(t,e){return t=null==t?0:+t,e=null==e?1:+e,1===arguments.length?(e=t,t=0):e-=t,function(){return n()*e+t}}return e.source=t,e}(Zd),Qd=function t(n){function e(t,e){return arguments.length<2&&(e=t,t=0),t=Math.floor(t),e=Math.floor(e)-t,function(){return Math.floor(n()*e+t)}}return e.source=t,e}(Zd),Jd=function t(n){function e(t,e){var r,i;return t=null==t?0:+t,e=null==e?1:+e,function(){var o;if(null!=r)o=r,r=null;else do{r=2*n()-1,o=2*n()-1,i=r*r+o*o}while(!i||i>1);return t+e*o*Math.sqrt(-2*Math.log(i)/i)}}return e.source=t,e}(Zd),tp=function t(n){var e=Jd.source(n);function r(){var t=e.apply(this,arguments);return function(){return Math.exp(t())}}return r.source=t,r}(Zd),np=function t(n){function e(t){return(t=+t)<=0?()=>0:function(){for(var e=0,r=t;r>1;--r)e+=n();return e+r*n()}}return e.source=t,e}(Zd),ep=function t(n){var e=np.source(n);function r(t){if(0==(t=+t))return n;var r=e(t);return function(){return r()/t}}return r.source=t,r}(Zd),rp=function t(n){function e(t){return function(){return-Math.log1p(-n())/t}}return e.source=t,e}(Zd),ip=function t(n){function e(t){if((t=+t)<0)throw new RangeError("invalid alpha");return t=1/-t,function(){return Math.pow(1-n(),t)}}return e.source=t,e}(Zd),op=function t(n){function e(t){if((t=+t)<0||t>1)throw new RangeError("invalid p");return function(){return Math.floor(n()+t)}}return e.source=t,e}(Zd),ap=function t(n){function e(t){if((t=+t)<0||t>1)throw new RangeError("invalid p");return 0===t?()=>1/0:1===t?()=>1:(t=Math.log1p(-t),function(){return 1+Math.floor(Math.log1p(-n())/t)})}return e.source=t,e}(Zd),up=function t(n){var e=Jd.source(n)();function r(t,r){if((t=+t)<0)throw new RangeError("invalid k");if(0===t)return()=>0;if(r=null==r?1:+r,1===t)return()=>-Math.log1p(-n())*r;var i=(t<1?t+1:t)-1/3,o=1/(3*Math.sqrt(i)),a=t<1?()=>Math.pow(n(),1/t):()=>1;return function(){do{do{var t=e(),u=1+o*t}while(u<=0);u*=u*u;var c=1-n()}while(c>=1-.0331*t*t*t*t&&Math.log(c)>=.5*t*t+i*(1-u+Math.log(u)));return i*u*a()*r}}return r.source=t,r}(Zd),cp=function t(n){var e=up.source(n);function r(t,n){var r=e(t),i=e(n);return function(){var t=r();return 0===t?0:t/(t+i())}}return r.source=t,r}(Zd),fp=function t(n){var e=ap.source(n),r=cp.source(n);function i(t,n){return t=+t,(n=+n)>=1?()=>t:n<=0?()=>0:function(){for(var i=0,o=t,a=n;o*a>16&&o*(1-a)>16;){var u=Math.floor((o+1)*a),c=r(u,o-u+1)();c<=a?(i+=u,o-=u,a=(a-c)/(1-c)):(o=u-1,a/=c)}for(var f=a<.5,s=e(f?a:1-a),l=s(),h=0;l<=o;++h)l+=s();return i+(f?h:o-h)}}return i.source=t,i}(Zd),sp=function t(n){function e(t,e,r){var i;return 0==(t=+t)?i=t=>-Math.log(t):(t=1/t,i=n=>Math.pow(n,t)),e=null==e?0:+e,r=null==r?1:+r,function(){return e+r*i(-Math.log1p(-n()))}}return e.source=t,e}(Zd),lp=function t(n){function e(t,e){return t=null==t?0:+t,e=null==e?1:+e,function(){return t+e*Math.tan(Math.PI*n())}}return e.source=t,e}(Zd),hp=function t(n){function e(t,e){return t=null==t?0:+t,e=null==e?1:+e,function(){var r=n();return t+e*Math.log(r/(1-r))}}return e.source=t,e}(Zd),dp=function t(n){var e=up.source(n),r=fp.source(n);function i(t){return function(){for(var i=0,o=t;o>16;){var a=Math.floor(.875*o),u=e(a)();if(u>o)return i+r(a-1,o/u)();i+=a,o-=u}for(var c=-Math.log1p(-n()),f=0;c<=o;++f)c-=Math.log1p(-n());return i+f}}return i.source=t,i}(Zd);const pp=1/4294967296;function gp(t,n){switch(arguments.length){case 0:break;case 1:this.range(t);break;default:this.range(n).domain(t)}return this}function yp(t,n){switch(arguments.length){case 0:break;case 1:"function"==typeof t?this.interpolator(t):this.range(t);break;default:this.domain(t),"function"==typeof n?this.interpolator(n):this.range(n)}return this}const vp=Symbol("implicit");function _p(){var t=new y,n=[],e=[],r=vp;function i(i){let o=t.get(i);if(void 0===o){if(r!==vp)return r;t.set(i,o=n.push(i)-1)}return e[o%e.length]}return i.domain=function(e){if(!arguments.length)return n.slice();n=[],t=new y;for(const r of e)t.has(r)||t.set(r,n.push(r)-1);return i},i.range=function(t){return arguments.length?(e=Array.from(t),i):e.slice()},i.unknown=function(t){return arguments.length?(r=t,i):r},i.copy=function(){return _p(n,e).unknown(r)},gp.apply(i,arguments),i}function bp(){var t,n,e=_p().unknown(void 0),r=e.domain,i=e.range,o=0,a=1,u=!1,c=0,f=0,s=.5;function l(){var e=r().length,l=an&&(e=t,t=n,n=e),function(e){return Math.max(t,Math.min(n,e))}}(a[0],a[t-1])),r=t>2?Sp:Tp,i=o=null,l}function l(n){return null==n||isNaN(n=+n)?e:(i||(i=r(a.map(t),u,c)))(t(f(n)))}return l.invert=function(e){return f(n((o||(o=r(u,a.map(t),Mr)))(e)))},l.domain=function(t){return arguments.length?(a=Array.from(t,xp),s()):a.slice()},l.range=function(t){return arguments.length?(u=Array.from(t),s()):u.slice()},l.rangeRound=function(t){return u=Array.from(t),c=Nr,s()},l.clamp=function(t){return arguments.length?(f=!!t||Mp,s()):f!==Mp},l.interpolate=function(t){return arguments.length?(c=t,s()):c},l.unknown=function(t){return arguments.length?(e=t,l):e},function(e,r){return t=e,n=r,s()}}function Np(){return kp()(Mp,Mp)}function Cp(n,e,r,i){var o,a=U(n,e,r);switch((i=_c(null==i?",f":i)).type){case"s":var u=Math.max(Math.abs(n),Math.abs(e));return null!=i.precision||isNaN(o=Nc(a,u))||(i.precision=o),t.formatPrefix(i,u);case"":case"e":case"g":case"p":case"r":null!=i.precision||isNaN(o=Cc(a,Math.max(Math.abs(n),Math.abs(e))))||(i.precision=o-("e"===i.type));break;case"f":case"%":null!=i.precision||isNaN(o=kc(a))||(i.precision=o-2*("%"===i.type))}return t.format(i)}function Pp(t){var n=t.domain;return t.ticks=function(t){var e=n();return O(e[0],e[e.length-1],null==t?10:t)},t.tickFormat=function(t,e){var r=n();return Cp(r[0],r[r.length-1],null==t?10:t,e)},t.nice=function(e){null==e&&(e=10);var r,i,o=n(),a=0,u=o.length-1,c=o[a],f=o[u],s=10;for(f0;){if((i=I(c,f,e))===r)return o[a]=c,o[u]=f,n(o);if(i>0)c=Math.floor(c/i)*i,f=Math.ceil(f/i)*i;else{if(!(i<0))break;c=Math.ceil(c*i)/i,f=Math.floor(f*i)/i}r=i}return t},t}function zp(t,n){var e,r=0,i=(t=t.slice()).length-1,o=t[r],a=t[i];return a0){for(;h<=d;++h)for(s=1,f=r(h);sc)break;g.push(l)}}else for(;h<=d;++h)for(s=a-1,f=r(h);s>=1;--s)if(!((l=f*s)c)break;g.push(l)}2*g.length0))return u;do{u.push(a=new Date(+e)),n(e,o),t(e)}while(a=n)for(;t(n),!e(n);)n.setTime(n-1)}),(function(t,r){if(t>=t)if(r<0)for(;++r<=0;)for(;n(t,-1),!e(t););else for(;--r>=0;)for(;n(t,1),!e(t););}))},e&&(i.count=function(n,r){return Zp.setTime(+n),Kp.setTime(+r),t(Zp),t(Kp),Math.floor(e(Zp,Kp))},i.every=function(t){return t=Math.floor(t),isFinite(t)&&t>0?t>1?i.filter(r?function(n){return r(n)%t==0}:function(n){return i.count(0,n)%t==0}):i:null}),i}var Jp=Qp((function(){}),(function(t,n){t.setTime(+t+n)}),(function(t,n){return n-t}));Jp.every=function(t){return t=Math.floor(t),isFinite(t)&&t>0?t>1?Qp((function(n){n.setTime(Math.floor(n/t)*t)}),(function(n,e){n.setTime(+n+e*t)}),(function(n,e){return(e-n)/t})):Jp:null};var tg=Jp.range;const ng=1e3,eg=6e4,rg=36e5,ig=864e5,og=6048e5,ag=2592e6,ug=31536e6;var cg=Qp((function(t){t.setTime(t-t.getMilliseconds())}),(function(t,n){t.setTime(+t+n*ng)}),(function(t,n){return(n-t)/ng}),(function(t){return t.getUTCSeconds()})),fg=cg.range,sg=Qp((function(t){t.setTime(t-t.getMilliseconds()-t.getSeconds()*ng)}),(function(t,n){t.setTime(+t+n*eg)}),(function(t,n){return(n-t)/eg}),(function(t){return t.getMinutes()})),lg=sg.range,hg=Qp((function(t){t.setTime(t-t.getMilliseconds()-t.getSeconds()*ng-t.getMinutes()*eg)}),(function(t,n){t.setTime(+t+n*rg)}),(function(t,n){return(n-t)/rg}),(function(t){return t.getHours()})),dg=hg.range,pg=Qp((t=>t.setHours(0,0,0,0)),((t,n)=>t.setDate(t.getDate()+n)),((t,n)=>(n-t-(n.getTimezoneOffset()-t.getTimezoneOffset())*eg)/ig),(t=>t.getDate()-1)),gg=pg.range;function yg(t){return Qp((function(n){n.setDate(n.getDate()-(n.getDay()+7-t)%7),n.setHours(0,0,0,0)}),(function(t,n){t.setDate(t.getDate()+7*n)}),(function(t,n){return(n-t-(n.getTimezoneOffset()-t.getTimezoneOffset())*eg)/og}))}var vg=yg(0),_g=yg(1),bg=yg(2),mg=yg(3),xg=yg(4),wg=yg(5),Mg=yg(6),Ag=vg.range,Tg=_g.range,Sg=bg.range,Eg=mg.range,kg=xg.range,Ng=wg.range,Cg=Mg.range,Pg=Qp((function(t){t.setDate(1),t.setHours(0,0,0,0)}),(function(t,n){t.setMonth(t.getMonth()+n)}),(function(t,n){return n.getMonth()-t.getMonth()+12*(n.getFullYear()-t.getFullYear())}),(function(t){return t.getMonth()})),zg=Pg.range,Dg=Qp((function(t){t.setMonth(0,1),t.setHours(0,0,0,0)}),(function(t,n){t.setFullYear(t.getFullYear()+n)}),(function(t,n){return n.getFullYear()-t.getFullYear()}),(function(t){return t.getFullYear()}));Dg.every=function(t){return isFinite(t=Math.floor(t))&&t>0?Qp((function(n){n.setFullYear(Math.floor(n.getFullYear()/t)*t),n.setMonth(0,1),n.setHours(0,0,0,0)}),(function(n,e){n.setFullYear(n.getFullYear()+e*t)})):null};var qg=Dg.range,Rg=Qp((function(t){t.setUTCSeconds(0,0)}),(function(t,n){t.setTime(+t+n*eg)}),(function(t,n){return(n-t)/eg}),(function(t){return t.getUTCMinutes()})),Fg=Rg.range,Og=Qp((function(t){t.setUTCMinutes(0,0,0)}),(function(t,n){t.setTime(+t+n*rg)}),(function(t,n){return(n-t)/rg}),(function(t){return t.getUTCHours()})),Ig=Og.range,Ug=Qp((function(t){t.setUTCHours(0,0,0,0)}),(function(t,n){t.setUTCDate(t.getUTCDate()+n)}),(function(t,n){return(n-t)/ig}),(function(t){return t.getUTCDate()-1})),Bg=Ug.range;function Yg(t){return Qp((function(n){n.setUTCDate(n.getUTCDate()-(n.getUTCDay()+7-t)%7),n.setUTCHours(0,0,0,0)}),(function(t,n){t.setUTCDate(t.getUTCDate()+7*n)}),(function(t,n){return(n-t)/og}))}var Lg=Yg(0),jg=Yg(1),Hg=Yg(2),Xg=Yg(3),Gg=Yg(4),Vg=Yg(5),$g=Yg(6),Wg=Lg.range,Zg=jg.range,Kg=Hg.range,Qg=Xg.range,Jg=Gg.range,ty=Vg.range,ny=$g.range,ey=Qp((function(t){t.setUTCDate(1),t.setUTCHours(0,0,0,0)}),(function(t,n){t.setUTCMonth(t.getUTCMonth()+n)}),(function(t,n){return n.getUTCMonth()-t.getUTCMonth()+12*(n.getUTCFullYear()-t.getUTCFullYear())}),(function(t){return t.getUTCMonth()})),ry=ey.range,iy=Qp((function(t){t.setUTCMonth(0,1),t.setUTCHours(0,0,0,0)}),(function(t,n){t.setUTCFullYear(t.getUTCFullYear()+n)}),(function(t,n){return n.getUTCFullYear()-t.getUTCFullYear()}),(function(t){return t.getUTCFullYear()}));iy.every=function(t){return isFinite(t=Math.floor(t))&&t>0?Qp((function(n){n.setUTCFullYear(Math.floor(n.getUTCFullYear()/t)*t),n.setUTCMonth(0,1),n.setUTCHours(0,0,0,0)}),(function(n,e){n.setUTCFullYear(n.getUTCFullYear()+e*t)})):null};var oy=iy.range;function ay(t,n,r,i,o,a){const u=[[cg,1,ng],[cg,5,5e3],[cg,15,15e3],[cg,30,3e4],[a,1,eg],[a,5,3e5],[a,15,9e5],[a,30,18e5],[o,1,rg],[o,3,108e5],[o,6,216e5],[o,12,432e5],[i,1,ig],[i,2,1728e5],[r,1,og],[n,1,ag],[n,3,7776e6],[t,1,ug]];function c(n,r,i){const o=Math.abs(r-n)/i,a=e((([,,t])=>t)).right(u,o);if(a===u.length)return t.every(U(n/ug,r/ug,i));if(0===a)return Jp.every(Math.max(U(n,r,i),1));const[c,f]=u[o/u[a-1][2]=12)]},q:function(t){return 1+~~(t.getMonth()/3)},Q:Pv,s:zv,S:Qy,u:Jy,U:tv,V:ev,w:rv,W:iv,x:null,X:null,y:ov,Y:uv,Z:fv,"%":Cv},m={a:function(t){return a[t.getUTCDay()]},A:function(t){return o[t.getUTCDay()]},b:function(t){return c[t.getUTCMonth()]},B:function(t){return u[t.getUTCMonth()]},c:null,d:sv,e:sv,f:gv,g:Sv,G:kv,H:lv,I:hv,j:dv,L:pv,m:yv,M:vv,p:function(t){return i[+(t.getUTCHours()>=12)]},q:function(t){return 1+~~(t.getUTCMonth()/3)},Q:Pv,s:zv,S:_v,u:bv,U:mv,V:wv,w:Mv,W:Av,x:null,X:null,y:Tv,Y:Ev,Z:Nv,"%":Cv},x={a:function(t,n,e){var r=d.exec(n.slice(e));return r?(t.w=p.get(r[0].toLowerCase()),e+r[0].length):-1},A:function(t,n,e){var r=l.exec(n.slice(e));return r?(t.w=h.get(r[0].toLowerCase()),e+r[0].length):-1},b:function(t,n,e){var r=v.exec(n.slice(e));return r?(t.m=_.get(r[0].toLowerCase()),e+r[0].length):-1},B:function(t,n,e){var r=g.exec(n.slice(e));return r?(t.m=y.get(r[0].toLowerCase()),e+r[0].length):-1},c:function(t,e,r){return A(t,n,e,r)},d:qy,e:qy,f:By,g:Cy,G:Ny,H:Fy,I:Fy,j:Ry,L:Uy,m:Dy,M:Oy,p:function(t,n,e){var r=f.exec(n.slice(e));return r?(t.p=s.get(r[0].toLowerCase()),e+r[0].length):-1},q:zy,Q:Ly,s:jy,S:Iy,u:Ty,U:Sy,V:Ey,w:Ay,W:ky,x:function(t,n,r){return A(t,e,n,r)},X:function(t,n,e){return A(t,r,n,e)},y:Cy,Y:Ny,Z:Py,"%":Yy};function w(t,n){return function(e){var r,i,o,a=[],u=-1,c=0,f=t.length;for(e instanceof Date||(e=new Date(+e));++u53)return null;"w"in o||(o.w=1),"Z"in o?(i=(r=hy(dy(o.y,0,1))).getUTCDay(),r=i>4||0===i?jg.ceil(r):jg(r),r=Ug.offset(r,7*(o.V-1)),o.y=r.getUTCFullYear(),o.m=r.getUTCMonth(),o.d=r.getUTCDate()+(o.w+6)%7):(i=(r=ly(dy(o.y,0,1))).getDay(),r=i>4||0===i?_g.ceil(r):_g(r),r=pg.offset(r,7*(o.V-1)),o.y=r.getFullYear(),o.m=r.getMonth(),o.d=r.getDate()+(o.w+6)%7)}else("W"in o||"U"in o)&&("w"in o||(o.w="u"in o?o.u%7:"W"in o?1:0),i="Z"in o?hy(dy(o.y,0,1)).getUTCDay():ly(dy(o.y,0,1)).getDay(),o.m=0,o.d="W"in o?(o.w+6)%7+7*o.W-(i+5)%7:o.w+7*o.U-(i+6)%7);return"Z"in o?(o.H+=o.Z/100|0,o.M+=o.Z%100,hy(o)):ly(o)}}function A(t,n,e,r){for(var i,o,a=0,u=n.length,c=e.length;a=c)return-1;if(37===(i=n.charCodeAt(a++))){if(i=n.charAt(a++),!(o=x[i in yy?n.charAt(a++):i])||(r=o(t,e,r))<0)return-1}else if(i!=e.charCodeAt(r++))return-1}return r}return b.x=w(e,b),b.X=w(r,b),b.c=w(n,b),m.x=w(e,m),m.X=w(r,m),m.c=w(n,m),{format:function(t){var n=w(t+="",b);return n.toString=function(){return t},n},parse:function(t){var n=M(t+="",!1);return n.toString=function(){return t},n},utcFormat:function(t){var n=w(t+="",m);return n.toString=function(){return t},n},utcParse:function(t){var n=M(t+="",!0);return n.toString=function(){return t},n}}}var gy,yy={"-":"",_:" ",0:"0"},vy=/^\s*\d+/,_y=/^%/,by=/[\\^$*+?|[\]().{}]/g;function my(t,n,e){var r=t<0?"-":"",i=(r?-t:t)+"",o=i.length;return r+(o[t.toLowerCase(),n])))}function Ay(t,n,e){var r=vy.exec(n.slice(e,e+1));return r?(t.w=+r[0],e+r[0].length):-1}function Ty(t,n,e){var r=vy.exec(n.slice(e,e+1));return r?(t.u=+r[0],e+r[0].length):-1}function Sy(t,n,e){var r=vy.exec(n.slice(e,e+2));return r?(t.U=+r[0],e+r[0].length):-1}function Ey(t,n,e){var r=vy.exec(n.slice(e,e+2));return r?(t.V=+r[0],e+r[0].length):-1}function ky(t,n,e){var r=vy.exec(n.slice(e,e+2));return r?(t.W=+r[0],e+r[0].length):-1}function Ny(t,n,e){var r=vy.exec(n.slice(e,e+4));return r?(t.y=+r[0],e+r[0].length):-1}function Cy(t,n,e){var r=vy.exec(n.slice(e,e+2));return r?(t.y=+r[0]+(+r[0]>68?1900:2e3),e+r[0].length):-1}function Py(t,n,e){var r=/^(Z)|([+-]\d\d)(?::?(\d\d))?/.exec(n.slice(e,e+6));return r?(t.Z=r[1]?0:-(r[2]+(r[3]||"00")),e+r[0].length):-1}function zy(t,n,e){var r=vy.exec(n.slice(e,e+1));return r?(t.q=3*r[0]-3,e+r[0].length):-1}function Dy(t,n,e){var r=vy.exec(n.slice(e,e+2));return r?(t.m=r[0]-1,e+r[0].length):-1}function qy(t,n,e){var r=vy.exec(n.slice(e,e+2));return r?(t.d=+r[0],e+r[0].length):-1}function Ry(t,n,e){var r=vy.exec(n.slice(e,e+3));return r?(t.m=0,t.d=+r[0],e+r[0].length):-1}function Fy(t,n,e){var r=vy.exec(n.slice(e,e+2));return r?(t.H=+r[0],e+r[0].length):-1}function Oy(t,n,e){var r=vy.exec(n.slice(e,e+2));return r?(t.M=+r[0],e+r[0].length):-1}function Iy(t,n,e){var r=vy.exec(n.slice(e,e+2));return r?(t.S=+r[0],e+r[0].length):-1}function Uy(t,n,e){var r=vy.exec(n.slice(e,e+3));return r?(t.L=+r[0],e+r[0].length):-1}function By(t,n,e){var r=vy.exec(n.slice(e,e+6));return r?(t.L=Math.floor(r[0]/1e3),e+r[0].length):-1}function Yy(t,n,e){var r=_y.exec(n.slice(e,e+1));return r?e+r[0].length:-1}function Ly(t,n,e){var r=vy.exec(n.slice(e));return r?(t.Q=+r[0],e+r[0].length):-1}function jy(t,n,e){var r=vy.exec(n.slice(e));return r?(t.s=+r[0],e+r[0].length):-1}function Hy(t,n){return my(t.getDate(),n,2)}function Xy(t,n){return my(t.getHours(),n,2)}function Gy(t,n){return my(t.getHours()%12||12,n,2)}function Vy(t,n){return my(1+pg.count(Dg(t),t),n,3)}function $y(t,n){return my(t.getMilliseconds(),n,3)}function Wy(t,n){return $y(t,n)+"000"}function Zy(t,n){return my(t.getMonth()+1,n,2)}function Ky(t,n){return my(t.getMinutes(),n,2)}function Qy(t,n){return my(t.getSeconds(),n,2)}function Jy(t){var n=t.getDay();return 0===n?7:n}function tv(t,n){return my(vg.count(Dg(t)-1,t),n,2)}function nv(t){var n=t.getDay();return n>=4||0===n?xg(t):xg.ceil(t)}function ev(t,n){return t=nv(t),my(xg.count(Dg(t),t)+(4===Dg(t).getDay()),n,2)}function rv(t){return t.getDay()}function iv(t,n){return my(_g.count(Dg(t)-1,t),n,2)}function ov(t,n){return my(t.getFullYear()%100,n,2)}function av(t,n){return my((t=nv(t)).getFullYear()%100,n,2)}function uv(t,n){return my(t.getFullYear()%1e4,n,4)}function cv(t,n){var e=t.getDay();return my((t=e>=4||0===e?xg(t):xg.ceil(t)).getFullYear()%1e4,n,4)}function fv(t){var n=t.getTimezoneOffset();return(n>0?"-":(n*=-1,"+"))+my(n/60|0,"0",2)+my(n%60,"0",2)}function sv(t,n){return my(t.getUTCDate(),n,2)}function lv(t,n){return my(t.getUTCHours(),n,2)}function hv(t,n){return my(t.getUTCHours()%12||12,n,2)}function dv(t,n){return my(1+Ug.count(iy(t),t),n,3)}function pv(t,n){return my(t.getUTCMilliseconds(),n,3)}function gv(t,n){return pv(t,n)+"000"}function yv(t,n){return my(t.getUTCMonth()+1,n,2)}function vv(t,n){return my(t.getUTCMinutes(),n,2)}function _v(t,n){return my(t.getUTCSeconds(),n,2)}function bv(t){var n=t.getUTCDay();return 0===n?7:n}function mv(t,n){return my(Lg.count(iy(t)-1,t),n,2)}function xv(t){var n=t.getUTCDay();return n>=4||0===n?Gg(t):Gg.ceil(t)}function wv(t,n){return t=xv(t),my(Gg.count(iy(t),t)+(4===iy(t).getUTCDay()),n,2)}function Mv(t){return t.getUTCDay()}function Av(t,n){return my(jg.count(iy(t)-1,t),n,2)}function Tv(t,n){return my(t.getUTCFullYear()%100,n,2)}function Sv(t,n){return my((t=xv(t)).getUTCFullYear()%100,n,2)}function Ev(t,n){return my(t.getUTCFullYear()%1e4,n,4)}function kv(t,n){var e=t.getUTCDay();return my((t=e>=4||0===e?Gg(t):Gg.ceil(t)).getUTCFullYear()%1e4,n,4)}function Nv(){return"+0000"}function Cv(){return"%"}function Pv(t){return+t}function zv(t){return Math.floor(+t/1e3)}function Dv(n){return gy=py(n),t.timeFormat=gy.format,t.timeParse=gy.parse,t.utcFormat=gy.utcFormat,t.utcParse=gy.utcParse,gy}t.timeFormat=void 0,t.timeParse=void 0,t.utcFormat=void 0,t.utcParse=void 0,Dv({dateTime:"%x, %X",date:"%-m/%-d/%Y",time:"%-I:%M:%S %p",periods:["AM","PM"],days:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],shortDays:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],months:["January","February","March","April","May","June","July","August","September","October","November","December"],shortMonths:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"]});var qv="%Y-%m-%dT%H:%M:%S.%LZ";var Rv=Date.prototype.toISOString?function(t){return t.toISOString()}:t.utcFormat(qv);var Fv=+new Date("2000-01-01T00:00:00.000Z")?function(t){var n=new Date(t);return isNaN(n)?null:n}:t.utcParse(qv);function Ov(t){return new Date(t)}function Iv(t){return t instanceof Date?+t:+new Date(+t)}function Uv(t,n,e,r,i,o,a,u,c,f){var s=Np(),l=s.invert,h=s.domain,d=f(".%L"),p=f(":%S"),g=f("%I:%M"),y=f("%I %p"),v=f("%a %d"),_=f("%b %d"),b=f("%B"),m=f("%Y");function x(t){return(c(t)vr(t[t.length-1]),r_=new Array(3).concat("d8b365f5f5f55ab4ac","a6611adfc27d80cdc1018571","a6611adfc27df5f5f580cdc1018571","8c510ad8b365f6e8c3c7eae55ab4ac01665e","8c510ad8b365f6e8c3f5f5f5c7eae55ab4ac01665e","8c510abf812ddfc27df6e8c3c7eae580cdc135978f01665e","8c510abf812ddfc27df6e8c3f5f5f5c7eae580cdc135978f01665e","5430058c510abf812ddfc27df6e8c3c7eae580cdc135978f01665e003c30","5430058c510abf812ddfc27df6e8c3f5f5f5c7eae580cdc135978f01665e003c30").map(Xv),i_=e_(r_),o_=new Array(3).concat("af8dc3f7f7f77fbf7b","7b3294c2a5cfa6dba0008837","7b3294c2a5cff7f7f7a6dba0008837","762a83af8dc3e7d4e8d9f0d37fbf7b1b7837","762a83af8dc3e7d4e8f7f7f7d9f0d37fbf7b1b7837","762a839970abc2a5cfe7d4e8d9f0d3a6dba05aae611b7837","762a839970abc2a5cfe7d4e8f7f7f7d9f0d3a6dba05aae611b7837","40004b762a839970abc2a5cfe7d4e8d9f0d3a6dba05aae611b783700441b","40004b762a839970abc2a5cfe7d4e8f7f7f7d9f0d3a6dba05aae611b783700441b").map(Xv),a_=e_(o_),u_=new Array(3).concat("e9a3c9f7f7f7a1d76a","d01c8bf1b6dab8e1864dac26","d01c8bf1b6daf7f7f7b8e1864dac26","c51b7de9a3c9fde0efe6f5d0a1d76a4d9221","c51b7de9a3c9fde0eff7f7f7e6f5d0a1d76a4d9221","c51b7dde77aef1b6dafde0efe6f5d0b8e1867fbc414d9221","c51b7dde77aef1b6dafde0eff7f7f7e6f5d0b8e1867fbc414d9221","8e0152c51b7dde77aef1b6dafde0efe6f5d0b8e1867fbc414d9221276419","8e0152c51b7dde77aef1b6dafde0eff7f7f7e6f5d0b8e1867fbc414d9221276419").map(Xv),c_=e_(u_),f_=new Array(3).concat("998ec3f7f7f7f1a340","5e3c99b2abd2fdb863e66101","5e3c99b2abd2f7f7f7fdb863e66101","542788998ec3d8daebfee0b6f1a340b35806","542788998ec3d8daebf7f7f7fee0b6f1a340b35806","5427888073acb2abd2d8daebfee0b6fdb863e08214b35806","5427888073acb2abd2d8daebf7f7f7fee0b6fdb863e08214b35806","2d004b5427888073acb2abd2d8daebfee0b6fdb863e08214b358067f3b08","2d004b5427888073acb2abd2d8daebf7f7f7fee0b6fdb863e08214b358067f3b08").map(Xv),s_=e_(f_),l_=new Array(3).concat("ef8a62f7f7f767a9cf","ca0020f4a58292c5de0571b0","ca0020f4a582f7f7f792c5de0571b0","b2182bef8a62fddbc7d1e5f067a9cf2166ac","b2182bef8a62fddbc7f7f7f7d1e5f067a9cf2166ac","b2182bd6604df4a582fddbc7d1e5f092c5de4393c32166ac","b2182bd6604df4a582fddbc7f7f7f7d1e5f092c5de4393c32166ac","67001fb2182bd6604df4a582fddbc7d1e5f092c5de4393c32166ac053061","67001fb2182bd6604df4a582fddbc7f7f7f7d1e5f092c5de4393c32166ac053061").map(Xv),h_=e_(l_),d_=new Array(3).concat("ef8a62ffffff999999","ca0020f4a582bababa404040","ca0020f4a582ffffffbababa404040","b2182bef8a62fddbc7e0e0e09999994d4d4d","b2182bef8a62fddbc7ffffffe0e0e09999994d4d4d","b2182bd6604df4a582fddbc7e0e0e0bababa8787874d4d4d","b2182bd6604df4a582fddbc7ffffffe0e0e0bababa8787874d4d4d","67001fb2182bd6604df4a582fddbc7e0e0e0bababa8787874d4d4d1a1a1a","67001fb2182bd6604df4a582fddbc7ffffffe0e0e0bababa8787874d4d4d1a1a1a").map(Xv),p_=e_(d_),g_=new Array(3).concat("fc8d59ffffbf91bfdb","d7191cfdae61abd9e92c7bb6","d7191cfdae61ffffbfabd9e92c7bb6","d73027fc8d59fee090e0f3f891bfdb4575b4","d73027fc8d59fee090ffffbfe0f3f891bfdb4575b4","d73027f46d43fdae61fee090e0f3f8abd9e974add14575b4","d73027f46d43fdae61fee090ffffbfe0f3f8abd9e974add14575b4","a50026d73027f46d43fdae61fee090e0f3f8abd9e974add14575b4313695","a50026d73027f46d43fdae61fee090ffffbfe0f3f8abd9e974add14575b4313695").map(Xv),y_=e_(g_),v_=new Array(3).concat("fc8d59ffffbf91cf60","d7191cfdae61a6d96a1a9641","d7191cfdae61ffffbfa6d96a1a9641","d73027fc8d59fee08bd9ef8b91cf601a9850","d73027fc8d59fee08bffffbfd9ef8b91cf601a9850","d73027f46d43fdae61fee08bd9ef8ba6d96a66bd631a9850","d73027f46d43fdae61fee08bffffbfd9ef8ba6d96a66bd631a9850","a50026d73027f46d43fdae61fee08bd9ef8ba6d96a66bd631a9850006837","a50026d73027f46d43fdae61fee08bffffbfd9ef8ba6d96a66bd631a9850006837").map(Xv),__=e_(v_),b_=new Array(3).concat("fc8d59ffffbf99d594","d7191cfdae61abdda42b83ba","d7191cfdae61ffffbfabdda42b83ba","d53e4ffc8d59fee08be6f59899d5943288bd","d53e4ffc8d59fee08bffffbfe6f59899d5943288bd","d53e4ff46d43fdae61fee08be6f598abdda466c2a53288bd","d53e4ff46d43fdae61fee08bffffbfe6f598abdda466c2a53288bd","9e0142d53e4ff46d43fdae61fee08be6f598abdda466c2a53288bd5e4fa2","9e0142d53e4ff46d43fdae61fee08bffffbfe6f598abdda466c2a53288bd5e4fa2").map(Xv),m_=e_(b_),x_=new Array(3).concat("e5f5f999d8c92ca25f","edf8fbb2e2e266c2a4238b45","edf8fbb2e2e266c2a42ca25f006d2c","edf8fbccece699d8c966c2a42ca25f006d2c","edf8fbccece699d8c966c2a441ae76238b45005824","f7fcfde5f5f9ccece699d8c966c2a441ae76238b45005824","f7fcfde5f5f9ccece699d8c966c2a441ae76238b45006d2c00441b").map(Xv),w_=e_(x_),M_=new Array(3).concat("e0ecf49ebcda8856a7","edf8fbb3cde38c96c688419d","edf8fbb3cde38c96c68856a7810f7c","edf8fbbfd3e69ebcda8c96c68856a7810f7c","edf8fbbfd3e69ebcda8c96c68c6bb188419d6e016b","f7fcfde0ecf4bfd3e69ebcda8c96c68c6bb188419d6e016b","f7fcfde0ecf4bfd3e69ebcda8c96c68c6bb188419d810f7c4d004b").map(Xv),A_=e_(M_),T_=new Array(3).concat("e0f3dba8ddb543a2ca","f0f9e8bae4bc7bccc42b8cbe","f0f9e8bae4bc7bccc443a2ca0868ac","f0f9e8ccebc5a8ddb57bccc443a2ca0868ac","f0f9e8ccebc5a8ddb57bccc44eb3d32b8cbe08589e","f7fcf0e0f3dbccebc5a8ddb57bccc44eb3d32b8cbe08589e","f7fcf0e0f3dbccebc5a8ddb57bccc44eb3d32b8cbe0868ac084081").map(Xv),S_=e_(T_),E_=new Array(3).concat("fee8c8fdbb84e34a33","fef0d9fdcc8afc8d59d7301f","fef0d9fdcc8afc8d59e34a33b30000","fef0d9fdd49efdbb84fc8d59e34a33b30000","fef0d9fdd49efdbb84fc8d59ef6548d7301f990000","fff7ecfee8c8fdd49efdbb84fc8d59ef6548d7301f990000","fff7ecfee8c8fdd49efdbb84fc8d59ef6548d7301fb300007f0000").map(Xv),k_=e_(E_),N_=new Array(3).concat("ece2f0a6bddb1c9099","f6eff7bdc9e167a9cf02818a","f6eff7bdc9e167a9cf1c9099016c59","f6eff7d0d1e6a6bddb67a9cf1c9099016c59","f6eff7d0d1e6a6bddb67a9cf3690c002818a016450","fff7fbece2f0d0d1e6a6bddb67a9cf3690c002818a016450","fff7fbece2f0d0d1e6a6bddb67a9cf3690c002818a016c59014636").map(Xv),C_=e_(N_),P_=new Array(3).concat("ece7f2a6bddb2b8cbe","f1eef6bdc9e174a9cf0570b0","f1eef6bdc9e174a9cf2b8cbe045a8d","f1eef6d0d1e6a6bddb74a9cf2b8cbe045a8d","f1eef6d0d1e6a6bddb74a9cf3690c00570b0034e7b","fff7fbece7f2d0d1e6a6bddb74a9cf3690c00570b0034e7b","fff7fbece7f2d0d1e6a6bddb74a9cf3690c00570b0045a8d023858").map(Xv),z_=e_(P_),D_=new Array(3).concat("e7e1efc994c7dd1c77","f1eef6d7b5d8df65b0ce1256","f1eef6d7b5d8df65b0dd1c77980043","f1eef6d4b9dac994c7df65b0dd1c77980043","f1eef6d4b9dac994c7df65b0e7298ace125691003f","f7f4f9e7e1efd4b9dac994c7df65b0e7298ace125691003f","f7f4f9e7e1efd4b9dac994c7df65b0e7298ace125698004367001f").map(Xv),q_=e_(D_),R_=new Array(3).concat("fde0ddfa9fb5c51b8a","feebe2fbb4b9f768a1ae017e","feebe2fbb4b9f768a1c51b8a7a0177","feebe2fcc5c0fa9fb5f768a1c51b8a7a0177","feebe2fcc5c0fa9fb5f768a1dd3497ae017e7a0177","fff7f3fde0ddfcc5c0fa9fb5f768a1dd3497ae017e7a0177","fff7f3fde0ddfcc5c0fa9fb5f768a1dd3497ae017e7a017749006a").map(Xv),F_=e_(R_),O_=new Array(3).concat("edf8b17fcdbb2c7fb8","ffffcca1dab441b6c4225ea8","ffffcca1dab441b6c42c7fb8253494","ffffccc7e9b47fcdbb41b6c42c7fb8253494","ffffccc7e9b47fcdbb41b6c41d91c0225ea80c2c84","ffffd9edf8b1c7e9b47fcdbb41b6c41d91c0225ea80c2c84","ffffd9edf8b1c7e9b47fcdbb41b6c41d91c0225ea8253494081d58").map(Xv),I_=e_(O_),U_=new Array(3).concat("f7fcb9addd8e31a354","ffffccc2e69978c679238443","ffffccc2e69978c67931a354006837","ffffccd9f0a3addd8e78c67931a354006837","ffffccd9f0a3addd8e78c67941ab5d238443005a32","ffffe5f7fcb9d9f0a3addd8e78c67941ab5d238443005a32","ffffe5f7fcb9d9f0a3addd8e78c67941ab5d238443006837004529").map(Xv),B_=e_(U_),Y_=new Array(3).concat("fff7bcfec44fd95f0e","ffffd4fed98efe9929cc4c02","ffffd4fed98efe9929d95f0e993404","ffffd4fee391fec44ffe9929d95f0e993404","ffffd4fee391fec44ffe9929ec7014cc4c028c2d04","ffffe5fff7bcfee391fec44ffe9929ec7014cc4c028c2d04","ffffe5fff7bcfee391fec44ffe9929ec7014cc4c02993404662506").map(Xv),L_=e_(Y_),j_=new Array(3).concat("ffeda0feb24cf03b20","ffffb2fecc5cfd8d3ce31a1c","ffffb2fecc5cfd8d3cf03b20bd0026","ffffb2fed976feb24cfd8d3cf03b20bd0026","ffffb2fed976feb24cfd8d3cfc4e2ae31a1cb10026","ffffccffeda0fed976feb24cfd8d3cfc4e2ae31a1cb10026","ffffccffeda0fed976feb24cfd8d3cfc4e2ae31a1cbd0026800026").map(Xv),H_=e_(j_),X_=new Array(3).concat("deebf79ecae13182bd","eff3ffbdd7e76baed62171b5","eff3ffbdd7e76baed63182bd08519c","eff3ffc6dbef9ecae16baed63182bd08519c","eff3ffc6dbef9ecae16baed64292c62171b5084594","f7fbffdeebf7c6dbef9ecae16baed64292c62171b5084594","f7fbffdeebf7c6dbef9ecae16baed64292c62171b508519c08306b").map(Xv),G_=e_(X_),V_=new Array(3).concat("e5f5e0a1d99b31a354","edf8e9bae4b374c476238b45","edf8e9bae4b374c47631a354006d2c","edf8e9c7e9c0a1d99b74c47631a354006d2c","edf8e9c7e9c0a1d99b74c47641ab5d238b45005a32","f7fcf5e5f5e0c7e9c0a1d99b74c47641ab5d238b45005a32","f7fcf5e5f5e0c7e9c0a1d99b74c47641ab5d238b45006d2c00441b").map(Xv),$_=e_(V_),W_=new Array(3).concat("f0f0f0bdbdbd636363","f7f7f7cccccc969696525252","f7f7f7cccccc969696636363252525","f7f7f7d9d9d9bdbdbd969696636363252525","f7f7f7d9d9d9bdbdbd969696737373525252252525","fffffff0f0f0d9d9d9bdbdbd969696737373525252252525","fffffff0f0f0d9d9d9bdbdbd969696737373525252252525000000").map(Xv),Z_=e_(W_),K_=new Array(3).concat("efedf5bcbddc756bb1","f2f0f7cbc9e29e9ac86a51a3","f2f0f7cbc9e29e9ac8756bb154278f","f2f0f7dadaebbcbddc9e9ac8756bb154278f","f2f0f7dadaebbcbddc9e9ac8807dba6a51a34a1486","fcfbfdefedf5dadaebbcbddc9e9ac8807dba6a51a34a1486","fcfbfdefedf5dadaebbcbddc9e9ac8807dba6a51a354278f3f007d").map(Xv),Q_=e_(K_),J_=new Array(3).concat("fee0d2fc9272de2d26","fee5d9fcae91fb6a4acb181d","fee5d9fcae91fb6a4ade2d26a50f15","fee5d9fcbba1fc9272fb6a4ade2d26a50f15","fee5d9fcbba1fc9272fb6a4aef3b2ccb181d99000d","fff5f0fee0d2fcbba1fc9272fb6a4aef3b2ccb181d99000d","fff5f0fee0d2fcbba1fc9272fb6a4aef3b2ccb181da50f1567000d").map(Xv),tb=e_(J_),nb=new Array(3).concat("fee6cefdae6be6550d","feeddefdbe85fd8d3cd94701","feeddefdbe85fd8d3ce6550da63603","feeddefdd0a2fdae6bfd8d3ce6550da63603","feeddefdd0a2fdae6bfd8d3cf16913d948018c2d04","fff5ebfee6cefdd0a2fdae6bfd8d3cf16913d948018c2d04","fff5ebfee6cefdd0a2fdae6bfd8d3cf16913d94801a636037f2704").map(Xv),eb=e_(nb);var rb=Vr(or(300,.5,0),or(-240,.5,1)),ib=Vr(or(-100,.75,.35),or(80,1.5,.8)),ob=Vr(or(260,.75,.35),or(80,1.5,.8)),ab=or();var ub=we(),cb=Math.PI/3,fb=2*Math.PI/3;function sb(t){var n=t.length;return function(e){return t[Math.max(0,Math.min(n-1,Math.floor(e*n)))]}}var lb=sb(Xv("44015444025645045745055946075a46085c460a5d460b5e470d60470e6147106347116447136548146748166848176948186a481a6c481b6d481c6e481d6f481f70482071482173482374482475482576482677482878482979472a7a472c7a472d7b472e7c472f7d46307e46327e46337f463480453581453781453882443983443a83443b84433d84433e85423f854240864241864142874144874045884046883f47883f48893e49893e4a893e4c8a3d4d8a3d4e8a3c4f8a3c508b3b518b3b528b3a538b3a548c39558c39568c38588c38598c375a8c375b8d365c8d365d8d355e8d355f8d34608d34618d33628d33638d32648e32658e31668e31678e31688e30698e306a8e2f6b8e2f6c8e2e6d8e2e6e8e2e6f8e2d708e2d718e2c718e2c728e2c738e2b748e2b758e2a768e2a778e2a788e29798e297a8e297b8e287c8e287d8e277e8e277f8e27808e26818e26828e26828e25838e25848e25858e24868e24878e23888e23898e238a8d228b8d228c8d228d8d218e8d218f8d21908d21918c20928c20928c20938c1f948c1f958b1f968b1f978b1f988b1f998a1f9a8a1e9b8a1e9c891e9d891f9e891f9f881fa0881fa1881fa1871fa28720a38620a48621a58521a68522a78522a88423a98324aa8325ab8225ac8226ad8127ad8128ae8029af7f2ab07f2cb17e2db27d2eb37c2fb47c31b57b32b67a34b67935b77937b87838b9773aba763bbb753dbc743fbc7340bd7242be7144bf7046c06f48c16e4ac16d4cc26c4ec36b50c46a52c56954c56856c66758c7655ac8645cc8635ec96260ca6063cb5f65cb5e67cc5c69cd5b6ccd5a6ece5870cf5773d05675d05477d1537ad1517cd2507fd34e81d34d84d44b86d54989d5488bd6468ed64590d74393d74195d84098d83e9bd93c9dd93ba0da39a2da37a5db36a8db34aadc32addc30b0dd2fb2dd2db5de2bb8de29bade28bddf26c0df25c2df23c5e021c8e020cae11fcde11dd0e11cd2e21bd5e21ad8e219dae319dde318dfe318e2e418e5e419e7e419eae51aece51befe51cf1e51df4e61ef6e620f8e621fbe723fde725")),hb=sb(Xv("00000401000501010601010802010902020b02020d03030f03031204041405041606051806051a07061c08071e0907200a08220b09240c09260d0a290e0b2b100b2d110c2f120d31130d34140e36150e38160f3b180f3d19103f1a10421c10441d11471e114920114b21114e22115024125325125527125829115a2a115c2c115f2d11612f116331116533106734106936106b38106c390f6e3b0f703d0f713f0f72400f74420f75440f764510774710784910784a10794c117a4e117b4f127b51127c52137c54137d56147d57157e59157e5a167e5c167f5d177f5f187f601880621980641a80651a80671b80681c816a1c816b1d816d1d816e1e81701f81721f817320817521817621817822817922827b23827c23827e24828025828125818326818426818627818827818928818b29818c29818e2a81902a81912b81932b80942c80962c80982d80992d809b2e7f9c2e7f9e2f7fa02f7fa1307ea3307ea5317ea6317da8327daa337dab337cad347cae347bb0357bb2357bb3367ab5367ab73779b83779ba3878bc3978bd3977bf3a77c03a76c23b75c43c75c53c74c73d73c83e73ca3e72cc3f71cd4071cf4070d0416fd2426fd3436ed5446dd6456cd8456cd9466bdb476adc4869de4968df4a68e04c67e24d66e34e65e44f64e55064e75263e85362e95462ea5661eb5760ec5860ed5a5fee5b5eef5d5ef05f5ef1605df2625df2645cf3655cf4675cf4695cf56b5cf66c5cf66e5cf7705cf7725cf8745cf8765cf9785df9795df97b5dfa7d5efa7f5efa815ffb835ffb8560fb8761fc8961fc8a62fc8c63fc8e64fc9065fd9266fd9467fd9668fd9869fd9a6afd9b6bfe9d6cfe9f6dfea16efea36ffea571fea772fea973feaa74feac76feae77feb078feb27afeb47bfeb67cfeb77efeb97ffebb81febd82febf84fec185fec287fec488fec68afec88cfeca8dfecc8ffecd90fecf92fed194fed395fed597fed799fed89afdda9cfddc9efddea0fde0a1fde2a3fde3a5fde5a7fde7a9fde9aafdebacfcecaefceeb0fcf0b2fcf2b4fcf4b6fcf6b8fcf7b9fcf9bbfcfbbdfcfdbf")),db=sb(Xv("00000401000501010601010802010a02020c02020e03021004031204031405041706041907051b08051d09061f0a07220b07240c08260d08290e092b10092d110a30120a32140b34150b37160b39180c3c190c3e1b0c411c0c431e0c451f0c48210c4a230c4c240c4f260c51280b53290b552b0b572d0b592f0a5b310a5c320a5e340a5f3609613809623909633b09643d09653e0966400a67420a68440a68450a69470b6a490b6a4a0c6b4c0c6b4d0d6c4f0d6c510e6c520e6d540f6d550f6d57106e59106e5a116e5c126e5d126e5f136e61136e62146e64156e65156e67166e69166e6a176e6c186e6d186e6f196e71196e721a6e741a6e751b6e771c6d781c6d7a1d6d7c1d6d7d1e6d7f1e6c801f6c82206c84206b85216b87216b88226a8a226a8c23698d23698f24699025689225689326679526679727669827669a28659b29649d29649f2a63a02a63a22b62a32c61a52c60a62d60a82e5fa92e5eab2f5ead305dae305cb0315bb1325ab3325ab43359b63458b73557b93556ba3655bc3754bd3853bf3952c03a51c13a50c33b4fc43c4ec63d4dc73e4cc83f4bca404acb4149cc4248ce4347cf4446d04545d24644d34743d44842d54a41d74b3fd84c3ed94d3dda4e3cdb503bdd513ade5238df5337e05536e15635e25734e35933e45a31e55c30e65d2fe75e2ee8602de9612bea632aeb6429eb6628ec6726ed6925ee6a24ef6c23ef6e21f06f20f1711ff1731df2741cf3761bf37819f47918f57b17f57d15f67e14f68013f78212f78410f8850ff8870ef8890cf98b0bf98c0af98e09fa9008fa9207fa9407fb9606fb9706fb9906fb9b06fb9d07fc9f07fca108fca309fca50afca60cfca80dfcaa0ffcac11fcae12fcb014fcb216fcb418fbb61afbb81dfbba1ffbbc21fbbe23fac026fac228fac42afac62df9c72ff9c932f9cb35f8cd37f8cf3af7d13df7d340f6d543f6d746f5d949f5db4cf4dd4ff4df53f4e156f3e35af3e55df2e661f2e865f2ea69f1ec6df1ed71f1ef75f1f179f2f27df2f482f3f586f3f68af4f88ef5f992f6fa96f8fb9af9fc9dfafda1fcffa4")),pb=sb(Xv("0d088710078813078916078a19068c1b068d1d068e20068f2206902406912605912805922a05932c05942e05952f059631059733059735049837049938049a3a049a3c049b3e049c3f049c41049d43039e44039e46039f48039f4903a04b03a14c02a14e02a25002a25102a35302a35502a45601a45801a45901a55b01a55c01a65e01a66001a66100a76300a76400a76600a76700a86900a86a00a86c00a86e00a86f00a87100a87201a87401a87501a87701a87801a87a02a87b02a87d03a87e03a88004a88104a78305a78405a78606a68707a68808a68a09a58b0aa58d0ba58e0ca48f0da4910ea3920fa39410a29511a19613a19814a099159f9a169f9c179e9d189d9e199da01a9ca11b9ba21d9aa31e9aa51f99a62098a72197a82296aa2395ab2494ac2694ad2793ae2892b02991b12a90b22b8fb32c8eb42e8db52f8cb6308bb7318ab83289ba3388bb3488bc3587bd3786be3885bf3984c03a83c13b82c23c81c33d80c43e7fc5407ec6417dc7427cc8437bc9447aca457acb4679cc4778cc4977cd4a76ce4b75cf4c74d04d73d14e72d24f71d35171d45270d5536fd5546ed6556dd7566cd8576bd9586ada5a6ada5b69db5c68dc5d67dd5e66de5f65de6164df6263e06363e16462e26561e26660e3685fe4695ee56a5de56b5de66c5ce76e5be76f5ae87059e97158e97257ea7457eb7556eb7655ec7754ed7953ed7a52ee7b51ef7c51ef7e50f07f4ff0804ef1814df1834cf2844bf3854bf3874af48849f48948f58b47f58c46f68d45f68f44f79044f79143f79342f89441f89540f9973ff9983ef99a3efa9b3dfa9c3cfa9e3bfb9f3afba139fba238fca338fca537fca636fca835fca934fdab33fdac33fdae32fdaf31fdb130fdb22ffdb42ffdb52efeb72dfeb82cfeba2cfebb2bfebd2afebe2afec029fdc229fdc328fdc527fdc627fdc827fdca26fdcb26fccd25fcce25fcd025fcd225fbd324fbd524fbd724fad824fada24f9dc24f9dd25f8df25f8e125f7e225f7e425f6e626f6e826f5e926f5eb27f4ed27f3ee27f3f027f2f227f1f426f1f525f0f724f0f921"));function gb(t){return function(){return t}}var yb=Math.abs,vb=Math.atan2,_b=Math.cos,bb=Math.max,mb=Math.min,xb=Math.sin,wb=Math.sqrt,Mb=1e-12,Ab=Math.PI,Tb=Ab/2,Sb=2*Ab;function Eb(t){return t>1?0:t<-1?Ab:Math.acos(t)}function kb(t){return t>=1?Tb:t<=-1?-Tb:Math.asin(t)}function Nb(t){return t.innerRadius}function Cb(t){return t.outerRadius}function Pb(t){return t.startAngle}function zb(t){return t.endAngle}function Db(t){return t&&t.padAngle}function qb(t,n,e,r,i,o,a,u){var c=e-t,f=r-n,s=a-i,l=u-o,h=l*c-s*f;if(!(h*hC*C+P*P&&(A=S,T=E),{cx:A,cy:T,x01:-s,y01:-l,x11:A*(i/x-1),y11:T*(i/x-1)}}var Fb=Array.prototype.slice;function Ob(t){return"object"==typeof t&&"length"in t?t:Array.from(t)}function Ib(t){this._context=t}function Ub(t){return new Ib(t)}function Bb(t){return t[0]}function Yb(t){return t[1]}function Lb(t,n){var e=gb(!0),r=null,i=Ub,o=null;function a(a){var u,c,f,s=(a=Ob(a)).length,l=!1;for(null==r&&(o=i(f=pa())),u=0;u<=s;++u)!(u=s;--l)u.point(y[l],v[l]);u.lineEnd(),u.areaEnd()}g&&(y[f]=+t(h,f,c),v[f]=+n(h,f,c),u.point(r?+r(h,f,c):y[f],e?+e(h,f,c):v[f]))}if(d)return u=null,d+""||null}function f(){return Lb().defined(i).curve(a).context(o)}return t="function"==typeof t?t:void 0===t?Bb:gb(+t),n="function"==typeof n?n:gb(void 0===n?0:+n),e="function"==typeof e?e:void 0===e?Yb:gb(+e),c.x=function(n){return arguments.length?(t="function"==typeof n?n:gb(+n),r=null,c):t},c.x0=function(n){return arguments.length?(t="function"==typeof n?n:gb(+n),c):t},c.x1=function(t){return arguments.length?(r=null==t?null:"function"==typeof t?t:gb(+t),c):r},c.y=function(t){return arguments.length?(n="function"==typeof t?t:gb(+t),e=null,c):n},c.y0=function(t){return arguments.length?(n="function"==typeof t?t:gb(+t),c):n},c.y1=function(t){return arguments.length?(e=null==t?null:"function"==typeof t?t:gb(+t),c):e},c.lineX0=c.lineY0=function(){return f().x(t).y(n)},c.lineY1=function(){return f().x(t).y(e)},c.lineX1=function(){return f().x(r).y(n)},c.defined=function(t){return arguments.length?(i="function"==typeof t?t:gb(!!t),c):i},c.curve=function(t){return arguments.length?(a=t,null!=o&&(u=a(o)),c):a},c.context=function(t){return arguments.length?(null==t?o=u=null:u=a(o=t),c):o},c}function Hb(t,n){return nt?1:n>=t?0:NaN}function Xb(t){return t}Ib.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._point=0},lineEnd:function(){(this._line||0!==this._line&&1===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(t,n){switch(t=+t,n=+n,this._point){case 0:this._point=1,this._line?this._context.lineTo(t,n):this._context.moveTo(t,n);break;case 1:this._point=2;default:this._context.lineTo(t,n)}}};var Gb=$b(Ub);function Vb(t){this._curve=t}function $b(t){function n(n){return new Vb(t(n))}return n._curve=t,n}function Wb(t){var n=t.curve;return t.angle=t.x,delete t.x,t.radius=t.y,delete t.y,t.curve=function(t){return arguments.length?n($b(t)):n()._curve},t}function Zb(){return Wb(Lb().curve(Gb))}function Kb(){var t=jb().curve(Gb),n=t.curve,e=t.lineX0,r=t.lineX1,i=t.lineY0,o=t.lineY1;return t.angle=t.x,delete t.x,t.startAngle=t.x0,delete t.x0,t.endAngle=t.x1,delete t.x1,t.radius=t.y,delete t.y,t.innerRadius=t.y0,delete t.y0,t.outerRadius=t.y1,delete t.y1,t.lineStartAngle=function(){return Wb(e())},delete t.lineX0,t.lineEndAngle=function(){return Wb(r())},delete t.lineX1,t.lineInnerRadius=function(){return Wb(i())},delete t.lineY0,t.lineOuterRadius=function(){return Wb(o())},delete t.lineY1,t.curve=function(t){return arguments.length?n($b(t)):n()._curve},t}function Qb(t,n){return[(n=+n)*Math.cos(t-=Math.PI/2),n*Math.sin(t)]}function Jb(t){return t.source}function tm(t){return t.target}function nm(t){var n=Jb,e=tm,r=Bb,i=Yb,o=null;function a(){var a,u=Fb.call(arguments),c=n.apply(this,u),f=e.apply(this,u);if(o||(o=a=pa()),t(o,+r.apply(this,(u[0]=c,u)),+i.apply(this,u),+r.apply(this,(u[0]=f,u)),+i.apply(this,u)),a)return o=null,a+""||null}return a.source=function(t){return arguments.length?(n=t,a):n},a.target=function(t){return arguments.length?(e=t,a):e},a.x=function(t){return arguments.length?(r="function"==typeof t?t:gb(+t),a):r},a.y=function(t){return arguments.length?(i="function"==typeof t?t:gb(+t),a):i},a.context=function(t){return arguments.length?(o=null==t?null:t,a):o},a}function em(t,n,e,r,i){t.moveTo(n,e),t.bezierCurveTo(n=(n+r)/2,e,n,i,r,i)}function rm(t,n,e,r,i){t.moveTo(n,e),t.bezierCurveTo(n,e=(e+i)/2,r,e,r,i)}function im(t,n,e,r,i){var o=Qb(n,e),a=Qb(n,e=(e+i)/2),u=Qb(r,e),c=Qb(r,i);t.moveTo(o[0],o[1]),t.bezierCurveTo(a[0],a[1],u[0],u[1],c[0],c[1])}Vb.prototype={areaStart:function(){this._curve.areaStart()},areaEnd:function(){this._curve.areaEnd()},lineStart:function(){this._curve.lineStart()},lineEnd:function(){this._curve.lineEnd()},point:function(t,n){this._curve.point(n*Math.sin(t),n*-Math.cos(t))}};var om={draw:function(t,n){var e=Math.sqrt(n/Ab);t.moveTo(e,0),t.arc(0,0,e,0,Sb)}},am={draw:function(t,n){var e=Math.sqrt(n/5)/2;t.moveTo(-3*e,-e),t.lineTo(-e,-e),t.lineTo(-e,-3*e),t.lineTo(e,-3*e),t.lineTo(e,-e),t.lineTo(3*e,-e),t.lineTo(3*e,e),t.lineTo(e,e),t.lineTo(e,3*e),t.lineTo(-e,3*e),t.lineTo(-e,e),t.lineTo(-3*e,e),t.closePath()}},um=Math.sqrt(1/3),cm=2*um,fm={draw:function(t,n){var e=Math.sqrt(n/cm),r=e*um;t.moveTo(0,-e),t.lineTo(r,0),t.lineTo(0,e),t.lineTo(-r,0),t.closePath()}},sm=Math.sin(Ab/10)/Math.sin(7*Ab/10),lm=Math.sin(Sb/10)*sm,hm=-Math.cos(Sb/10)*sm,dm={draw:function(t,n){var e=Math.sqrt(.8908130915292852*n),r=lm*e,i=hm*e;t.moveTo(0,-e),t.lineTo(r,i);for(var o=1;o<5;++o){var a=Sb*o/5,u=Math.cos(a),c=Math.sin(a);t.lineTo(c*e,-u*e),t.lineTo(u*r-c*i,c*r+u*i)}t.closePath()}},pm={draw:function(t,n){var e=Math.sqrt(n),r=-e/2;t.rect(r,r,e,e)}},gm=Math.sqrt(3),ym={draw:function(t,n){var e=-Math.sqrt(n/(3*gm));t.moveTo(0,2*e),t.lineTo(-gm*e,-e),t.lineTo(gm*e,-e),t.closePath()}},vm=-.5,_m=Math.sqrt(3)/2,bm=1/Math.sqrt(12),mm=3*(bm/2+1),xm={draw:function(t,n){var e=Math.sqrt(n/mm),r=e/2,i=e*bm,o=r,a=e*bm+e,u=-o,c=a;t.moveTo(r,i),t.lineTo(o,a),t.lineTo(u,c),t.lineTo(vm*r-_m*i,_m*r+vm*i),t.lineTo(vm*o-_m*a,_m*o+vm*a),t.lineTo(vm*u-_m*c,_m*u+vm*c),t.lineTo(vm*r+_m*i,vm*i-_m*r),t.lineTo(vm*o+_m*a,vm*a-_m*o),t.lineTo(vm*u+_m*c,vm*c-_m*u),t.closePath()}},wm=[om,am,fm,pm,dm,ym,xm];function Mm(){}function Am(t,n,e){t._context.bezierCurveTo((2*t._x0+t._x1)/3,(2*t._y0+t._y1)/3,(t._x0+2*t._x1)/3,(t._y0+2*t._y1)/3,(t._x0+4*t._x1+n)/6,(t._y0+4*t._y1+e)/6)}function Tm(t){this._context=t}function Sm(t){this._context=t}function Em(t){this._context=t}Tm.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._y0=this._y1=NaN,this._point=0},lineEnd:function(){switch(this._point){case 3:Am(this,this._x1,this._y1);case 2:this._context.lineTo(this._x1,this._y1)}(this._line||0!==this._line&&1===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(t,n){switch(t=+t,n=+n,this._point){case 0:this._point=1,this._line?this._context.lineTo(t,n):this._context.moveTo(t,n);break;case 1:this._point=2;break;case 2:this._point=3,this._context.lineTo((5*this._x0+this._x1)/6,(5*this._y0+this._y1)/6);default:Am(this,t,n)}this._x0=this._x1,this._x1=t,this._y0=this._y1,this._y1=n}},Sm.prototype={areaStart:Mm,areaEnd:Mm,lineStart:function(){this._x0=this._x1=this._x2=this._x3=this._x4=this._y0=this._y1=this._y2=this._y3=this._y4=NaN,this._point=0},lineEnd:function(){switch(this._point){case 1:this._context.moveTo(this._x2,this._y2),this._context.closePath();break;case 2:this._context.moveTo((this._x2+2*this._x3)/3,(this._y2+2*this._y3)/3),this._context.lineTo((this._x3+2*this._x2)/3,(this._y3+2*this._y2)/3),this._context.closePath();break;case 3:this.point(this._x2,this._y2),this.point(this._x3,this._y3),this.point(this._x4,this._y4)}},point:function(t,n){switch(t=+t,n=+n,this._point){case 0:this._point=1,this._x2=t,this._y2=n;break;case 1:this._point=2,this._x3=t,this._y3=n;break;case 2:this._point=3,this._x4=t,this._y4=n,this._context.moveTo((this._x0+4*this._x1+t)/6,(this._y0+4*this._y1+n)/6);break;default:Am(this,t,n)}this._x0=this._x1,this._x1=t,this._y0=this._y1,this._y1=n}},Em.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._y0=this._y1=NaN,this._point=0},lineEnd:function(){(this._line||0!==this._line&&3===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(t,n){switch(t=+t,n=+n,this._point){case 0:this._point=1;break;case 1:this._point=2;break;case 2:this._point=3;var e=(this._x0+4*this._x1+t)/6,r=(this._y0+4*this._y1+n)/6;this._line?this._context.lineTo(e,r):this._context.moveTo(e,r);break;case 3:this._point=4;default:Am(this,t,n)}this._x0=this._x1,this._x1=t,this._y0=this._y1,this._y1=n}};class km{constructor(t,n){this._context=t,this._x=n}areaStart(){this._line=0}areaEnd(){this._line=NaN}lineStart(){this._point=0}lineEnd(){(this._line||0!==this._line&&1===this._point)&&this._context.closePath(),this._line=1-this._line}point(t,n){switch(t=+t,n=+n,this._point){case 0:this._point=1,this._line?this._context.lineTo(t,n):this._context.moveTo(t,n);break;case 1:this._point=2;default:this._x?this._context.bezierCurveTo(this._x0=(this._x0+t)/2,this._y0,this._x0,n,t,n):this._context.bezierCurveTo(this._x0,this._y0=(this._y0+n)/2,t,this._y0,t,n)}this._x0=t,this._y0=n}}function Nm(t,n){this._basis=new Tm(t),this._beta=n}Nm.prototype={lineStart:function(){this._x=[],this._y=[],this._basis.lineStart()},lineEnd:function(){var t=this._x,n=this._y,e=t.length-1;if(e>0)for(var r,i=t[0],o=n[0],a=t[e]-i,u=n[e]-o,c=-1;++c<=e;)r=c/e,this._basis.point(this._beta*t[c]+(1-this._beta)*(i+r*a),this._beta*n[c]+(1-this._beta)*(o+r*u));this._x=this._y=null,this._basis.lineEnd()},point:function(t,n){this._x.push(+t),this._y.push(+n)}};var Cm=function t(n){function e(t){return 1===n?new Tm(t):new Nm(t,n)}return e.beta=function(n){return t(+n)},e}(.85);function Pm(t,n,e){t._context.bezierCurveTo(t._x1+t._k*(t._x2-t._x0),t._y1+t._k*(t._y2-t._y0),t._x2+t._k*(t._x1-n),t._y2+t._k*(t._y1-e),t._x2,t._y2)}function zm(t,n){this._context=t,this._k=(1-n)/6}zm.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._x2=this._y0=this._y1=this._y2=NaN,this._point=0},lineEnd:function(){switch(this._point){case 2:this._context.lineTo(this._x2,this._y2);break;case 3:Pm(this,this._x1,this._y1)}(this._line||0!==this._line&&1===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(t,n){switch(t=+t,n=+n,this._point){case 0:this._point=1,this._line?this._context.lineTo(t,n):this._context.moveTo(t,n);break;case 1:this._point=2,this._x1=t,this._y1=n;break;case 2:this._point=3;default:Pm(this,t,n)}this._x0=this._x1,this._x1=this._x2,this._x2=t,this._y0=this._y1,this._y1=this._y2,this._y2=n}};var Dm=function t(n){function e(t){return new zm(t,n)}return e.tension=function(n){return t(+n)},e}(0);function qm(t,n){this._context=t,this._k=(1-n)/6}qm.prototype={areaStart:Mm,areaEnd:Mm,lineStart:function(){this._x0=this._x1=this._x2=this._x3=this._x4=this._x5=this._y0=this._y1=this._y2=this._y3=this._y4=this._y5=NaN,this._point=0},lineEnd:function(){switch(this._point){case 1:this._context.moveTo(this._x3,this._y3),this._context.closePath();break;case 2:this._context.lineTo(this._x3,this._y3),this._context.closePath();break;case 3:this.point(this._x3,this._y3),this.point(this._x4,this._y4),this.point(this._x5,this._y5)}},point:function(t,n){switch(t=+t,n=+n,this._point){case 0:this._point=1,this._x3=t,this._y3=n;break;case 1:this._point=2,this._context.moveTo(this._x4=t,this._y4=n);break;case 2:this._point=3,this._x5=t,this._y5=n;break;default:Pm(this,t,n)}this._x0=this._x1,this._x1=this._x2,this._x2=t,this._y0=this._y1,this._y1=this._y2,this._y2=n}};var Rm=function t(n){function e(t){return new qm(t,n)}return e.tension=function(n){return t(+n)},e}(0);function Fm(t,n){this._context=t,this._k=(1-n)/6}Fm.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._x2=this._y0=this._y1=this._y2=NaN,this._point=0},lineEnd:function(){(this._line||0!==this._line&&3===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(t,n){switch(t=+t,n=+n,this._point){case 0:this._point=1;break;case 1:this._point=2;break;case 2:this._point=3,this._line?this._context.lineTo(this._x2,this._y2):this._context.moveTo(this._x2,this._y2);break;case 3:this._point=4;default:Pm(this,t,n)}this._x0=this._x1,this._x1=this._x2,this._x2=t,this._y0=this._y1,this._y1=this._y2,this._y2=n}};var Om=function t(n){function e(t){return new Fm(t,n)}return e.tension=function(n){return t(+n)},e}(0);function Im(t,n,e){var r=t._x1,i=t._y1,o=t._x2,a=t._y2;if(t._l01_a>Mb){var u=2*t._l01_2a+3*t._l01_a*t._l12_a+t._l12_2a,c=3*t._l01_a*(t._l01_a+t._l12_a);r=(r*u-t._x0*t._l12_2a+t._x2*t._l01_2a)/c,i=(i*u-t._y0*t._l12_2a+t._y2*t._l01_2a)/c}if(t._l23_a>Mb){var f=2*t._l23_2a+3*t._l23_a*t._l12_a+t._l12_2a,s=3*t._l23_a*(t._l23_a+t._l12_a);o=(o*f+t._x1*t._l23_2a-n*t._l12_2a)/s,a=(a*f+t._y1*t._l23_2a-e*t._l12_2a)/s}t._context.bezierCurveTo(r,i,o,a,t._x2,t._y2)}function Um(t,n){this._context=t,this._alpha=n}Um.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._x2=this._y0=this._y1=this._y2=NaN,this._l01_a=this._l12_a=this._l23_a=this._l01_2a=this._l12_2a=this._l23_2a=this._point=0},lineEnd:function(){switch(this._point){case 2:this._context.lineTo(this._x2,this._y2);break;case 3:this.point(this._x2,this._y2)}(this._line||0!==this._line&&1===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(t,n){if(t=+t,n=+n,this._point){var e=this._x2-t,r=this._y2-n;this._l23_a=Math.sqrt(this._l23_2a=Math.pow(e*e+r*r,this._alpha))}switch(this._point){case 0:this._point=1,this._line?this._context.lineTo(t,n):this._context.moveTo(t,n);break;case 1:this._point=2;break;case 2:this._point=3;default:Im(this,t,n)}this._l01_a=this._l12_a,this._l12_a=this._l23_a,this._l01_2a=this._l12_2a,this._l12_2a=this._l23_2a,this._x0=this._x1,this._x1=this._x2,this._x2=t,this._y0=this._y1,this._y1=this._y2,this._y2=n}};var Bm=function t(n){function e(t){return n?new Um(t,n):new zm(t,0)}return e.alpha=function(n){return t(+n)},e}(.5);function Ym(t,n){this._context=t,this._alpha=n}Ym.prototype={areaStart:Mm,areaEnd:Mm,lineStart:function(){this._x0=this._x1=this._x2=this._x3=this._x4=this._x5=this._y0=this._y1=this._y2=this._y3=this._y4=this._y5=NaN,this._l01_a=this._l12_a=this._l23_a=this._l01_2a=this._l12_2a=this._l23_2a=this._point=0},lineEnd:function(){switch(this._point){case 1:this._context.moveTo(this._x3,this._y3),this._context.closePath();break;case 2:this._context.lineTo(this._x3,this._y3),this._context.closePath();break;case 3:this.point(this._x3,this._y3),this.point(this._x4,this._y4),this.point(this._x5,this._y5)}},point:function(t,n){if(t=+t,n=+n,this._point){var e=this._x2-t,r=this._y2-n;this._l23_a=Math.sqrt(this._l23_2a=Math.pow(e*e+r*r,this._alpha))}switch(this._point){case 0:this._point=1,this._x3=t,this._y3=n;break;case 1:this._point=2,this._context.moveTo(this._x4=t,this._y4=n);break;case 2:this._point=3,this._x5=t,this._y5=n;break;default:Im(this,t,n)}this._l01_a=this._l12_a,this._l12_a=this._l23_a,this._l01_2a=this._l12_2a,this._l12_2a=this._l23_2a,this._x0=this._x1,this._x1=this._x2,this._x2=t,this._y0=this._y1,this._y1=this._y2,this._y2=n}};var Lm=function t(n){function e(t){return n?new Ym(t,n):new qm(t,0)}return e.alpha=function(n){return t(+n)},e}(.5);function jm(t,n){this._context=t,this._alpha=n}jm.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._x2=this._y0=this._y1=this._y2=NaN,this._l01_a=this._l12_a=this._l23_a=this._l01_2a=this._l12_2a=this._l23_2a=this._point=0},lineEnd:function(){(this._line||0!==this._line&&3===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(t,n){if(t=+t,n=+n,this._point){var e=this._x2-t,r=this._y2-n;this._l23_a=Math.sqrt(this._l23_2a=Math.pow(e*e+r*r,this._alpha))}switch(this._point){case 0:this._point=1;break;case 1:this._point=2;break;case 2:this._point=3,this._line?this._context.lineTo(this._x2,this._y2):this._context.moveTo(this._x2,this._y2);break;case 3:this._point=4;default:Im(this,t,n)}this._l01_a=this._l12_a,this._l12_a=this._l23_a,this._l01_2a=this._l12_2a,this._l12_2a=this._l23_2a,this._x0=this._x1,this._x1=this._x2,this._x2=t,this._y0=this._y1,this._y1=this._y2,this._y2=n}};var Hm=function t(n){function e(t){return n?new jm(t,n):new Fm(t,0)}return e.alpha=function(n){return t(+n)},e}(.5);function Xm(t){this._context=t}function Gm(t){return t<0?-1:1}function Vm(t,n,e){var r=t._x1-t._x0,i=n-t._x1,o=(t._y1-t._y0)/(r||i<0&&-0),a=(e-t._y1)/(i||r<0&&-0),u=(o*i+a*r)/(r+i);return(Gm(o)+Gm(a))*Math.min(Math.abs(o),Math.abs(a),.5*Math.abs(u))||0}function $m(t,n){var e=t._x1-t._x0;return e?(3*(t._y1-t._y0)/e-n)/2:n}function Wm(t,n,e){var r=t._x0,i=t._y0,o=t._x1,a=t._y1,u=(o-r)/3;t._context.bezierCurveTo(r+u,i+u*n,o-u,a-u*e,o,a)}function Zm(t){this._context=t}function Km(t){this._context=new Qm(t)}function Qm(t){this._context=t}function Jm(t){this._context=t}function tx(t){var n,e,r=t.length-1,i=new Array(r),o=new Array(r),a=new Array(r);for(i[0]=0,o[0]=2,a[0]=t[0]+2*t[1],n=1;n=0;--n)i[n]=(a[n]-i[n+1])/o[n];for(o[r-1]=(t[r]+i[r-1])/2,n=0;n1)for(var e,r,i,o=1,a=t[n[0]],u=a.length;o=0;)e[n]=n;return e}function ix(t,n){return t[n]}function ox(t){const n=[];return n.key=t,n}function ax(t){var n=t.map(ux);return rx(t).sort((function(t,e){return n[t]-n[e]}))}function ux(t){for(var n,e=-1,r=0,i=t.length,o=-1/0;++eo&&(o=n,r=e);return r}function cx(t){var n=t.map(fx);return rx(t).sort((function(t,e){return n[t]-n[e]}))}function fx(t){for(var n,e=0,r=-1,i=t.length;++r=0&&(this._t=1-this._t,this._line=1-this._line)},point:function(t,n){switch(t=+t,n=+n,this._point){case 0:this._point=1,this._line?this._context.lineTo(t,n):this._context.moveTo(t,n);break;case 1:this._point=2;default:if(this._t<=0)this._context.lineTo(this._x,n),this._context.lineTo(t,n);else{var e=this._x*(1-this._t)+t*this._t;this._context.lineTo(e,this._y),this._context.lineTo(e,n)}}this._x=t,this._y=n}};var sx=t=>()=>t;function lx(t,{sourceEvent:n,target:e,transform:r,dispatch:i}){Object.defineProperties(this,{type:{value:t,enumerable:!0,configurable:!0},sourceEvent:{value:n,enumerable:!0,configurable:!0},target:{value:e,enumerable:!0,configurable:!0},transform:{value:r,enumerable:!0,configurable:!0},_:{value:i}})}function hx(t,n,e){this.k=t,this.x=n,this.y=e}hx.prototype={constructor:hx,scale:function(t){return 1===t?this:new hx(this.k*t,this.x,this.y)},translate:function(t,n){return 0===t&0===n?this:new hx(this.k,this.x+this.k*t,this.y+this.k*n)},apply:function(t){return[t[0]*this.k+this.x,t[1]*this.k+this.y]},applyX:function(t){return t*this.k+this.x},applyY:function(t){return t*this.k+this.y},invert:function(t){return[(t[0]-this.x)/this.k,(t[1]-this.y)/this.k]},invertX:function(t){return(t-this.x)/this.k},invertY:function(t){return(t-this.y)/this.k},rescaleX:function(t){return t.copy().domain(t.range().map(this.invertX,this).map(t.invert,t))},rescaleY:function(t){return t.copy().domain(t.range().map(this.invertY,this).map(t.invert,t))},toString:function(){return"translate("+this.x+","+this.y+") scale("+this.k+")"}};var dx=new hx(1,0,0);function px(t){for(;!t.__zoom;)if(!(t=t.parentNode))return dx;return t.__zoom}function gx(t){t.stopImmediatePropagation()}function yx(t){t.preventDefault(),t.stopImmediatePropagation()}function vx(t){return!(t.ctrlKey&&"wheel"!==t.type||t.button)}function _x(){var t=this;return t instanceof SVGElement?(t=t.ownerSVGElement||t).hasAttribute("viewBox")?[[(t=t.viewBox.baseVal).x,t.y],[t.x+t.width,t.y+t.height]]:[[0,0],[t.width.baseVal.value,t.height.baseVal.value]]:[[0,0],[t.clientWidth,t.clientHeight]]}function bx(){return this.__zoom||dx}function mx(t){return-t.deltaY*(1===t.deltaMode?.05:t.deltaMode?1:.002)*(t.ctrlKey?10:1)}function xx(){return navigator.maxTouchPoints||"ontouchstart"in this}function wx(t,n,e){var r=t.invertX(n[0][0])-e[0][0],i=t.invertX(n[1][0])-e[1][0],o=t.invertY(n[0][1])-e[0][1],a=t.invertY(n[1][1])-e[1][1];return t.translate(i>r?(r+i)/2:Math.min(0,r)||Math.max(0,i),a>o?(o+a)/2:Math.min(0,o)||Math.max(0,a))}px.prototype=hx.prototype,t.Adder=g,t.Delaunay=du,t.FormatSpecifier=bc,t.InternMap=y,t.InternSet=v,t.Node=cd,t.Voronoi=uu,t.ZoomTransform=hx,t.active=function(t,n){var e,r,i=t.__transition;if(i)for(r in n=null==n?null:n+"",i)if((e=i[r]).state>1&&e.name===n)return new $i([[t]],Mo,n,+r);return null},t.arc=function(){var t=Nb,n=Cb,e=gb(0),r=null,i=Pb,o=zb,a=Db,u=null;function c(){var c,f,s=+t.apply(this,arguments),l=+n.apply(this,arguments),h=i.apply(this,arguments)-Tb,d=o.apply(this,arguments)-Tb,p=yb(d-h),g=d>h;if(u||(u=c=pa()),lMb)if(p>Sb-Mb)u.moveTo(l*_b(h),l*xb(h)),u.arc(0,0,l,h,d,!g),s>Mb&&(u.moveTo(s*_b(d),s*xb(d)),u.arc(0,0,s,d,h,g));else{var y,v,_=h,b=d,m=h,x=d,w=p,M=p,A=a.apply(this,arguments)/2,T=A>Mb&&(r?+r.apply(this,arguments):wb(s*s+l*l)),S=mb(yb(l-s)/2,+e.apply(this,arguments)),E=S,k=S;if(T>Mb){var N=kb(T/s*xb(A)),C=kb(T/l*xb(A));(w-=2*N)>Mb?(m+=N*=g?1:-1,x-=N):(w=0,m=x=(h+d)/2),(M-=2*C)>Mb?(_+=C*=g?1:-1,b-=C):(M=0,_=b=(h+d)/2)}var P=l*_b(_),z=l*xb(_),D=s*_b(x),q=s*xb(x);if(S>Mb){var R,F=l*_b(b),O=l*xb(b),I=s*_b(m),U=s*xb(m);if(pMb?k>Mb?(y=Rb(I,U,P,z,l,k,g),v=Rb(F,O,D,q,l,k,g),u.moveTo(y.cx+y.x01,y.cy+y.y01),kMb&&w>Mb?E>Mb?(y=Rb(D,q,F,O,s,-E,g),v=Rb(P,z,I,U,s,-E,g),u.lineTo(y.cx+y.x01,y.cy+y.y01),E>a,f=i+2*u>>a,s=Ea(20);function l(r){var i=new Float32Array(c*f),l=new Float32Array(c*f),d=Math.pow(2,-a);r.forEach((function(r,o,a){var s=(t(r,o,a)+u)*d,l=(n(r,o,a)+u)*d,h=+e(r,o,a);if(s>=0&&s=0&&l>a),Ra({width:c,height:f,data:l},{width:c,height:f,data:i},o>>a),qa({width:c,height:f,data:i},{width:c,height:f,data:l},o>>a),Ra({width:c,height:f,data:l},{width:c,height:f,data:i},o>>a),qa({width:c,height:f,data:i},{width:c,height:f,data:l},o>>a),Ra({width:c,height:f,data:l},{width:c,height:f,data:i},o>>a);var p=s(i);if(!Array.isArray(p)){var g=j(i);p=U(0,g,p),(p=J(0,Math.floor(g/p)*p,p)).shift()}return Da().thresholds(p).size([c,f])(i).map(h)}function h(t){return t.value*=Math.pow(2,-2*a),t.coordinates.forEach(d),t}function d(t){t.forEach(p)}function p(t){t.forEach(g)}function g(t){t[0]=t[0]*Math.pow(2,a)-u,t[1]=t[1]*Math.pow(2,a)-u}function y(){return c=r+2*(u=3*o)>>a,f=i+2*u>>a,l}return l.x=function(n){return arguments.length?(t="function"==typeof n?n:Ea(+n),l):t},l.y=function(t){return arguments.length?(n="function"==typeof t?t:Ea(+t),l):n},l.weight=function(t){return arguments.length?(e="function"==typeof t?t:Ea(+t),l):e},l.size=function(t){if(!arguments.length)return[r,i];var n=+t[0],e=+t[1];if(!(n>=0&&e>=0))throw new Error("invalid size");return r=n,i=e,y()},l.cellSize=function(t){if(!arguments.length)return 1<=1))throw new Error("invalid cell size");return a=Math.floor(Math.log(t)/Math.LN2),y()},l.thresholds=function(t){return arguments.length?(s="function"==typeof t?t:Array.isArray(t)?Ea(Ta.call(t)):Ea(t),l):s},l.bandwidth=function(t){if(!arguments.length)return Math.sqrt(o*(o+1));if(!((t=+t)>=0))throw new Error("invalid bandwidth");return o=Math.round((Math.sqrt(4*t*t+1)-1)/2),y()},l},t.contours=Da,t.count=c,t.create=function(t){return Fn(St(t).call(document.documentElement))},t.creator=St,t.cross=function(...t){const n="function"==typeof t[t.length-1]&&function(t){return n=>t(...n)}(t.pop()),e=(t=t.map(l)).map(f),r=t.length-1,i=new Array(r+1).fill(0),o=[];if(r<0||e.some(s))return o;for(;;){o.push(i.map(((n,e)=>t[e][n])));let a=r;for(;++i[a]===e[a];){if(0===a)return n?o.map(n):o;i[a--]=0}}},t.csv=ju,t.csvFormat=Au,t.csvFormatBody=Tu,t.csvFormatRow=Eu,t.csvFormatRows=Su,t.csvFormatValue=ku,t.csvParse=wu,t.csvParseRows=Mu,t.cubehelix=or,t.cumsum=function(t,n){var e=0,r=0;return Float64Array.from(t,void 0===n?t=>e+=+t||0:i=>e+=+n(i,r++,t)||0)},t.curveBasis=function(t){return new Tm(t)},t.curveBasisClosed=function(t){return new Sm(t)},t.curveBasisOpen=function(t){return new Em(t)},t.curveBumpX=function(t){return new km(t,!0)},t.curveBumpY=function(t){return new km(t,!1)},t.curveBundle=Cm,t.curveCardinal=Dm,t.curveCardinalClosed=Rm,t.curveCardinalOpen=Om,t.curveCatmullRom=Bm,t.curveCatmullRomClosed=Lm,t.curveCatmullRomOpen=Hm,t.curveLinear=Ub,t.curveLinearClosed=function(t){return new Xm(t)},t.curveMonotoneX=function(t){return new Zm(t)},t.curveMonotoneY=function(t){return new Km(t)},t.curveNatural=function(t){return new Jm(t)},t.curveStep=function(t){return new nx(t,.5)},t.curveStepAfter=function(t){return new nx(t,1)},t.curveStepBefore=function(t){return new nx(t,0)},t.descending=function(t,n){return null==t||null==n?NaN:nt?1:n>=t?0:NaN},t.deviation=d,t.difference=function(t,...n){t=new Set(t);for(const e of n)for(const n of e)t.delete(n);return t},t.disjoint=function(t,n){const e=n[Symbol.iterator](),r=new Set;for(const n of t){if(r.has(n))return!1;let t,i;for(;({value:t,done:i}=e.next())&&!i;){if(Object.is(n,t))return!1;r.add(t)}}return!0},t.dispatch=yt,t.drag=function(){var t,n,e,r,i=Zn,o=Kn,a=Qn,u=Jn,c={},f=yt("start","drag","end"),s=0,l=0;function h(t){t.on("mousedown.drag",d).filter(u).on("touchstart.drag",y).on("touchmove.drag",v,Ln).on("touchend.drag touchcancel.drag",_).style("touch-action","none").style("-webkit-tap-highlight-color","rgba(0,0,0,0)")}function d(a,u){if(!r&&i.call(this,a,u)){var c=b(this,o.call(this,a,u),a,u,"mouse");c&&(Fn(a.view).on("mousemove.drag",p,jn).on("mouseup.drag",g,jn),Gn(a.view),Hn(a),e=!1,t=a.clientX,n=a.clientY,c("start",a))}}function p(r){if(Xn(r),!e){var i=r.clientX-t,o=r.clientY-n;e=i*i+o*o>l}c.mouse("drag",r)}function g(t){Fn(t.view).on("mousemove.drag mouseup.drag",null),Vn(t.view,e),Xn(t),c.mouse("end",t)}function y(t,n){if(i.call(this,t,n)){var e,r,a=t.changedTouches,u=o.call(this,t,n),c=a.length;for(e=0;e+t,t.easePoly=eo,t.easePolyIn=to,t.easePolyInOut=eo,t.easePolyOut=no,t.easeQuad=Qi,t.easeQuadIn=function(t){return t*t},t.easeQuadInOut=Qi,t.easeQuadOut=function(t){return t*(2-t)},t.easeSin=oo,t.easeSinIn=function(t){return 1==+t?1:1-Math.cos(t*io)},t.easeSinInOut=oo,t.easeSinOut=function(t){return Math.sin(t*io)},t.every=function(t,n){if("function"!=typeof n)throw new TypeError("test is not a function");let e=-1;for(const r of t)if(!n(r,++e,t))return!1;return!0},t.extent=p,t.fcumsum=function(t,n){const e=new g;let r=-1;return Float64Array.from(t,void 0===n?t=>e.add(+t||0):i=>e.add(+n(i,++r,t)||0))},t.filter=function(t,n){if("function"!=typeof n)throw new TypeError("test is not a function");const e=[];let r=-1;for(const i of t)n(i,++r,t)&&e.push(i);return e},t.flatGroup=function(t,...n){return T(A(t,...n),n)},t.flatRollup=function(t,n,...e){return T(E(t,n,...e),e)},t.forceCenter=function(t,n){var e,r=1;function i(){var i,o,a=e.length,u=0,c=0;for(i=0;if+p||os+p||ac.index){var g=f-u.x-u.vx,y=s-u.y-u.vy,v=g*g+y*y;vt.r&&(t.r=t[n].r)}function c(){if(n){var r,i,o=n.length;for(e=new Array(o),r=0;r[u(t,n,r),t])));for(a=0,i=new Array(f);a=u)){(t.data!==n||t.next)&&(0===l&&(p+=(l=oc(e))*l),0===h&&(p+=(h=oc(e))*h),p(t=(1664525*t+1013904223)%sc)/sc}();function l(){h(),f.call("tick",n),e1?(null==e?u.delete(t):u.set(t,p(e)),n):u.get(t)},find:function(n,e,r){var i,o,a,u,c,f=0,s=t.length;for(null==r?r=1/0:r*=r,f=0;f1?(f.on(t,e),n):f.on(t)}}},t.forceX=function(t){var n,e,r,i=ic(.1);function o(t){for(var i,o=0,a=n.length;o=.12&&i<.234&&r>=-.425&&r<-.214?u:i>=.166&&i<.234&&r>=-.214&&r<-.115?c:a).invert(t)},s.stream=function(e){return t&&n===e?t:(r=[a.stream(n=e),u.stream(e),c.stream(e)],i=r.length,t={point:function(t,n){for(var e=-1;++efs(r[0],r[1])&&(r[1]=i[1]),fs(i[0],r[1])>fs(r[0],r[1])&&(r[0]=i[0])):o.push(r=i);for(a=-1/0,n=0,r=o[e=o.length-1];n<=e;r=i,++n)i=o[n],(u=fs(r[1],i[0]))>a&&(a=u,pf=i[0],yf=r[1])}return Mf=Af=null,pf===1/0||gf===1/0?[[NaN,NaN],[NaN,NaN]]:[[pf,gf],[yf,vf]]},t.geoCentroid=function(t){Uf=Bf=Yf=Lf=jf=Hf=Xf=Gf=0,Vf=new g,$f=new g,Wf=new g,cf(t,hs);var n=+Vf,e=+$f,r=+Wf,i=Xc(n,e,r);return i2?t[2]+90:90]):[(t=e())[0],t[1],t[2]-90]},e([0,0,90]).scale(159.155)},t.geoTransverseMercatorRaw=Qh,t.gray=function(t,n){return new Ye(t,0,0,null==n?1:n)},t.greatest=function(t,e=n){let r,i=!1;if(1===e.length){let o;for(const a of t){const t=e(a);(i?n(t,o)>0:0===n(t,t))&&(r=a,o=t,i=!0)}}else for(const n of t)(i?e(n,r)>0:0===e(n,n))&&(r=n,i=!0);return r},t.greatestIndex=function(t,e=n){if(1===e.length)return W(t,e);let r,i=-1,o=-1;for(const n of t)++o,(i<0?0===e(n,n):e(n,r)>0)&&(r=n,i=o);return i},t.group=M,t.groupSort=function(t,e,r){return(1===e.length?P(S(t,e,r),(([t,e],[r,i])=>n(e,i)||n(t,r))):P(M(t,r),(([t,r],[i,o])=>e(r,o)||n(t,i)))).map((([t])=>t))},t.groups=A,t.hcl=Ve,t.hierarchy=rd,t.histogram=L,t.hsl=Ne,t.html=$u,t.image=function(t,n){return new Promise((function(e,r){var i=new Image;for(var o in n)i[o]=n[o];i.onerror=r,i.onload=function(){e(i)},i.src=t}))},t.index=function(t,...n){return N(t,w,k,n)},t.indexes=function(t,...n){return N(t,Array.from,k,n)},t.interpolate=kr,t.interpolateArray=function(t,n){return(mr(n)?br:xr)(t,n)},t.interpolateBasis=cr,t.interpolateBasisClosed=fr,t.interpolateBlues=G_,t.interpolateBrBG=i_,t.interpolateBuGn=w_,t.interpolateBuPu=A_,t.interpolateCividis=function(t){return t=Math.max(0,Math.min(1,t)),"rgb("+Math.max(0,Math.min(255,Math.round(-4.54-t*(35.34-t*(2381.73-t*(6402.7-t*(7024.72-2710.57*t)))))))+", "+Math.max(0,Math.min(255,Math.round(32.49+t*(170.73+t*(52.82-t*(131.46-t*(176.58-67.37*t)))))))+", "+Math.max(0,Math.min(255,Math.round(81.24+t*(442.36-t*(2482.43-t*(6167.24-t*(6614.94-2475.67*t)))))))+")"},t.interpolateCool=ob,t.interpolateCubehelix=Gr,t.interpolateCubehelixDefault=rb,t.interpolateCubehelixLong=Vr,t.interpolateDate=wr,t.interpolateDiscrete=function(t){var n=t.length;return function(e){return t[Math.max(0,Math.min(n-1,Math.floor(e*n)))]}},t.interpolateGnBu=S_,t.interpolateGreens=$_,t.interpolateGreys=Z_,t.interpolateHcl=jr,t.interpolateHclLong=Hr,t.interpolateHsl=Br,t.interpolateHslLong=Yr,t.interpolateHue=function(t,n){var e=hr(+t,+n);return function(t){var n=e(t);return n-360*Math.floor(n/360)}},t.interpolateInferno=db,t.interpolateLab=function(t,n){var e=pr((t=Be(t)).l,(n=Be(n)).l),r=pr(t.a,n.a),i=pr(t.b,n.b),o=pr(t.opacity,n.opacity);return function(n){return t.l=e(n),t.a=r(n),t.b=i(n),t.opacity=o(n),t+""}},t.interpolateMagma=hb,t.interpolateNumber=Mr,t.interpolateNumberArray=br,t.interpolateObject=Ar,t.interpolateOrRd=k_,t.interpolateOranges=eb,t.interpolatePRGn=a_,t.interpolatePiYG=c_,t.interpolatePlasma=pb,t.interpolatePuBu=z_,t.interpolatePuBuGn=C_,t.interpolatePuOr=s_,t.interpolatePuRd=q_,t.interpolatePurples=Q_,t.interpolateRainbow=function(t){(t<0||t>1)&&(t-=Math.floor(t));var n=Math.abs(t-.5);return ab.h=360*t-100,ab.s=1.5-1.5*n,ab.l=.8-.9*n,ab+""},t.interpolateRdBu=h_,t.interpolateRdGy=p_,t.interpolateRdPu=F_,t.interpolateRdYlBu=y_,t.interpolateRdYlGn=__,t.interpolateReds=tb,t.interpolateRgb=gr,t.interpolateRgbBasis=vr,t.interpolateRgbBasisClosed=_r,t.interpolateRound=Nr,t.interpolateSinebow=function(t){var n;return t=(.5-t)*Math.PI,ub.r=255*(n=Math.sin(t))*n,ub.g=255*(n=Math.sin(t+cb))*n,ub.b=255*(n=Math.sin(t+fb))*n,ub+""},t.interpolateSpectral=m_,t.interpolateString=Er,t.interpolateTransformCss=Rr,t.interpolateTransformSvg=Fr,t.interpolateTurbo=function(t){return t=Math.max(0,Math.min(1,t)),"rgb("+Math.max(0,Math.min(255,Math.round(34.61+t*(1172.33-t*(10793.56-t*(33300.12-t*(38394.49-14825.05*t)))))))+", "+Math.max(0,Math.min(255,Math.round(23.31+t*(557.33+t*(1225.33-t*(3574.96-t*(1073.77+707.56*t)))))))+", "+Math.max(0,Math.min(255,Math.round(27.2+t*(3211.1-t*(15327.97-t*(27814-t*(22569.18-6838.66*t)))))))+")"},t.interpolateViridis=lb,t.interpolateWarm=ib,t.interpolateYlGn=B_,t.interpolateYlGnBu=I_,t.interpolateYlOrBr=L_,t.interpolateYlOrRd=H_,t.interpolateZoom=Ir,t.interrupt=mi,t.intersection=function(t,...n){t=new Set(t),n=n.map(ot);t:for(const e of t)for(const r of n)if(!r.has(e)){t.delete(e);continue t}return t},t.interval=function(t,n,e){var r=new ui,i=n;return null==n?(r.restart(t,n,e),r):(r._restart=r.restart,r.restart=function(t,n,e){n=+n,e=null==e?oi():+e,r._restart((function o(a){a+=i,r._restart(o,i+=n,e),t(a)}),n,e)},r.restart(t,n,e),r)},t.isoFormat=Rv,t.isoParse=Fv,t.json=function(t,n){return fetch(t,n).then(Xu)},t.lab=Be,t.lch=function(t,n,e,r){return 1===arguments.length?Ge(t):new $e(e,n,t,null==r?1:r)},t.least=function(t,e=n){let r,i=!1;if(1===e.length){let o;for(const a of t){const t=e(a);(i?n(t,o)<0:0===n(t,t))&&(r=a,o=t,i=!0)}}else for(const n of t)(i?e(n,r)<0:0===e(n,n))&&(r=n,i=!0);return r},t.leastIndex=tt,t.line=Lb,t.lineRadial=Zb,t.linkHorizontal=function(){return nm(em)},t.linkRadial=function(){var t=nm(im);return t.angle=t.x,delete t.x,t.radius=t.y,delete t.y,t},t.linkVertical=function(){return nm(rm)},t.local=In,t.map=function(t,n){if("function"!=typeof t[Symbol.iterator])throw new TypeError("values is not iterable");if("function"!=typeof n)throw new TypeError("mapper is not a function");return Array.from(t,((e,r)=>n(e,r,t)))},t.matcher=zt,t.max=j,t.maxIndex=W,t.mean=function(t,n){let e=0,r=0;if(void 0===n)for(let n of t)null!=n&&(n=+n)>=n&&(++e,r+=n);else{let i=-1;for(let o of t)null!=(o=n(o,++i,t))&&(o=+o)>=o&&(++e,r+=o)}if(e)return r/e},t.median=function(t,n){return V(t,.5,n)},t.merge=Z,t.min=H,t.minIndex=K,t.mode=function(t,n){const e=new y;if(void 0===n)for(let n of t)null!=n&&n>=n&&e.set(n,(e.get(n)||0)+1);else{let r=-1;for(let i of t)null!=(i=n(i,++r,t))&&i>=i&&e.set(i,(e.get(i)||0)+1)}let r,i=0;for(const[t,n]of e)n>i&&(i=n,r=t);return r},t.namespace=Mt,t.namespaces=wt,t.nice=B,t.now=oi,t.pack=function(){var t=null,n=1,e=1,r=Ad;function i(i){return i.x=n/2,i.y=e/2,t?i.eachBefore(Ed(t)).eachAfter(kd(r,.5)).eachBefore(Nd(1)):i.eachBefore(Ed(Sd)).eachAfter(kd(Ad,1)).eachAfter(kd(r,i.r/Math.min(n,e))).eachBefore(Nd(Math.min(n,e)/(2*i.r))),i}return i.radius=function(n){return arguments.length?(t=wd(n),i):t},i.size=function(t){return arguments.length?(n=+t[0],e=+t[1],i):[n,e]},i.padding=function(t){return arguments.length?(r="function"==typeof t?t:Td(+t),i):r},i},t.packEnclose=fd,t.packSiblings=function(t){return xd(t),t},t.pairs=function(t,n=Q){const e=[];let r,i=!1;for(const o of t)i&&e.push(n(r,o)),r=o,i=!0;return e},t.partition=function(){var t=1,n=1,e=0,r=!1;function i(i){var o=i.height+1;return i.x0=i.y0=e,i.x1=t,i.y1=n/o,i.eachBefore(function(t,n){return function(r){r.children&&Pd(r,r.x0,t*(r.depth+1)/n,r.x1,t*(r.depth+2)/n);var i=r.x0,o=r.y0,a=r.x1-e,u=r.y1-e;a0&&(d+=l);for(null!=n?p.sort((function(t,e){return n(g[t],g[e])})):null!=e&&p.sort((function(t,n){return e(a[t],a[n])})),u=0,f=d?(v-h*b)/d:0;u0?l*f:0)+b,g[c]={data:a[c],index:u,value:l,startAngle:y,endAngle:s,padAngle:_};return g}return a.value=function(n){return arguments.length?(t="function"==typeof n?n:gb(+n),a):t},a.sortValues=function(t){return arguments.length?(n=t,e=null,a):n},a.sort=function(t){return arguments.length?(e=t,n=null,a):e},a.startAngle=function(t){return arguments.length?(r="function"==typeof t?t:gb(+t),a):r},a.endAngle=function(t){return arguments.length?(i="function"==typeof t?t:gb(+t),a):i},a.padAngle=function(t){return arguments.length?(o="function"==typeof t?t:gb(+t),a):o},a},t.piecewise=$r,t.pointRadial=Qb,t.pointer=Yn,t.pointers=function(t,n){return t.target&&(t=Bn(t),void 0===n&&(n=t.currentTarget),t=t.touches||[t]),Array.from(t,(t=>Yn(t,n)))},t.polygonArea=function(t){for(var n,e=-1,r=t.length,i=t[r-1],o=0;++eu!=f>u&&a<(c-e)*(u-r)/(f-r)+e&&(s=!s),c=e,f=r;return s},t.polygonHull=function(t){if((e=t.length)<3)return null;var n,e,r=new Array(e),i=new Array(e);for(n=0;n=0;--n)f.push(t[r[o[n]][2]]);for(n=+u;n(n=1664525*n+1013904223|0,pp*(n>>>0))},t.randomLogNormal=tp,t.randomLogistic=hp,t.randomNormal=Jd,t.randomPareto=ip,t.randomPoisson=dp,t.randomUniform=Kd,t.randomWeibull=sp,t.range=J,t.reduce=function(t,n,e){if("function"!=typeof n)throw new TypeError("reducer is not a function");const r=t[Symbol.iterator]();let i,o,a=-1;if(arguments.length<3){if(({done:i,value:e}=r.next()),i)return;++a}for(;({done:i,value:o}=r.next()),!i;)e=n(e,o,++a,t);return e},t.reverse=function(t){if("function"!=typeof t[Symbol.iterator])throw new TypeError("values is not iterable");return Array.from(t).reverse()},t.rgb=we,t.ribbon=function(){return Aa()},t.ribbonArrow=function(){return Aa(Ma)},t.rollup=S,t.rollups=E,t.scaleBand=bp,t.scaleDiverging=function t(){var n=Pp(jv()(Mp));return n.copy=function(){return Yv(n,t())},yp.apply(n,arguments)},t.scaleDivergingLog=function t(){var n=Up(jv()).domain([.1,1,10]);return n.copy=function(){return Yv(n,t()).base(n.base())},yp.apply(n,arguments)},t.scaleDivergingPow=Hv,t.scaleDivergingSqrt=function(){return Hv.apply(null,arguments).exponent(.5)},t.scaleDivergingSymlog=function t(){var n=Lp(jv());return n.copy=function(){return Yv(n,t()).constant(n.constant())},yp.apply(n,arguments)},t.scaleIdentity=function t(n){var e;function r(t){return null==t||isNaN(t=+t)?e:t}return r.invert=r,r.domain=r.range=function(t){return arguments.length?(n=Array.from(t,xp),r):n.slice()},r.unknown=function(t){return arguments.length?(e=t,r):e},r.copy=function(){return t(n).unknown(e)},n=arguments.length?Array.from(n,xp):[0,1],Pp(r)},t.scaleImplicit=vp,t.scaleLinear=function t(){var n=Np();return n.copy=function(){return Ep(n,t())},gp.apply(n,arguments),Pp(n)},t.scaleLog=function t(){var n=Up(kp()).domain([1,10]);return n.copy=function(){return Ep(n,t()).base(n.base())},gp.apply(n,arguments),n},t.scaleOrdinal=_p,t.scalePoint=function(){return mp(bp.apply(null,arguments).paddingInner(1))},t.scalePow=Vp,t.scaleQuantile=function t(){var e,r=[],i=[],a=[];function u(){var t=0,n=Math.max(1,i.length);for(a=new Array(n-1);++t0?a[n-1]:r[0],n=i?[a[i-1],r]:[a[n-1],a[n]]},c.unknown=function(t){return arguments.length?(n=t,c):c},c.thresholds=function(){return a.slice()},c.copy=function(){return t().domain([e,r]).range(u).unknown(n)},gp.apply(Pp(c),arguments)},t.scaleRadial=function t(){var n,e=Np(),r=[0,1],i=!1;function o(t){var r=Wp(e(t));return isNaN(r)?n:i?Math.round(r):r}return o.invert=function(t){return e.invert($p(t))},o.domain=function(t){return arguments.length?(e.domain(t),o):e.domain()},o.range=function(t){return arguments.length?(e.range((r=Array.from(t,xp)).map($p)),o):r.slice()},o.rangeRound=function(t){return o.range(t).round(!0)},o.round=function(t){return arguments.length?(i=!!t,o):i},o.clamp=function(t){return arguments.length?(e.clamp(t),o):e.clamp()},o.unknown=function(t){return arguments.length?(n=t,o):n},o.copy=function(){return t(e.domain(),r).round(i).clamp(e.clamp()).unknown(n)},gp.apply(o,arguments),Pp(o)},t.scaleSequential=function t(){var n=Pp(Bv()(Mp));return n.copy=function(){return Yv(n,t())},yp.apply(n,arguments)},t.scaleSequentialLog=function t(){var n=Up(Bv()).domain([1,10]);return n.copy=function(){return Yv(n,t()).base(n.base())},yp.apply(n,arguments)},t.scaleSequentialPow=Lv,t.scaleSequentialQuantile=function t(){var e=[],r=Mp;function i(t){if(null!=t&&!isNaN(t=+t))return r((o(e,t,1)-1)/(e.length-1))}return i.domain=function(t){if(!arguments.length)return e.slice();e=[];for(let n of t)null==n||isNaN(n=+n)||e.push(n);return e.sort(n),i},i.interpolator=function(t){return arguments.length?(r=t,i):r},i.range=function(){return e.map(((t,n)=>r(n/(e.length-1))))},i.quantiles=function(t){return Array.from({length:t+1},((n,r)=>V(e,r/t)))},i.copy=function(){return t(r).domain(e)},yp.apply(i,arguments)},t.scaleSequentialSqrt=function(){return Lv.apply(null,arguments).exponent(.5)},t.scaleSequentialSymlog=function t(){var n=Lp(Bv());return n.copy=function(){return Yv(n,t()).constant(n.constant())},yp.apply(n,arguments)},t.scaleSqrt=function(){return Vp.apply(null,arguments).exponent(.5)},t.scaleSymlog=function t(){var n=Lp(kp());return n.copy=function(){return Ep(n,t()).constant(n.constant())},gp.apply(n,arguments)},t.scaleThreshold=function t(){var n,e=[.5],r=[0,1],i=1;function a(t){return null!=t&&t<=t?r[o(e,t,0,i)]:n}return a.domain=function(t){return arguments.length?(e=Array.from(t),i=Math.min(e.length,r.length-1),a):e.slice()},a.range=function(t){return arguments.length?(r=Array.from(t),i=Math.min(e.length,r.length-1),a):r.slice()},a.invertExtent=function(t){var n=r.indexOf(t);return[e[n-1],e[n]]},a.unknown=function(t){return arguments.length?(n=t,a):n},a.copy=function(){return t().domain(e).range(r).unknown(n)},gp.apply(a,arguments)},t.scaleTime=function(){return gp.apply(Uv(fy,sy,Dg,Pg,vg,pg,hg,sg,cg,t.timeFormat).domain([new Date(2e3,0,1),new Date(2e3,0,2)]),arguments)},t.scaleUtc=function(){return gp.apply(Uv(uy,cy,iy,ey,Lg,Ug,Og,Rg,cg,t.utcFormat).domain([Date.UTC(2e3,0,1),Date.UTC(2e3,0,2)]),arguments)},t.scan=function(t,n){const e=tt(t,n);return e<0?void 0:e},t.schemeAccent=Vv,t.schemeBlues=X_,t.schemeBrBG=r_,t.schemeBuGn=x_,t.schemeBuPu=M_,t.schemeCategory10=Gv,t.schemeDark2=$v,t.schemeGnBu=T_,t.schemeGreens=V_,t.schemeGreys=W_,t.schemeOrRd=E_,t.schemeOranges=nb,t.schemePRGn=o_,t.schemePaired=Wv,t.schemePastel1=Zv,t.schemePastel2=Kv,t.schemePiYG=u_,t.schemePuBu=P_,t.schemePuBuGn=N_,t.schemePuOr=f_,t.schemePuRd=D_,t.schemePurples=K_,t.schemeRdBu=l_,t.schemeRdGy=d_,t.schemeRdPu=R_,t.schemeRdYlBu=g_,t.schemeRdYlGn=v_,t.schemeReds=J_,t.schemeSet1=Qv,t.schemeSet2=Jv,t.schemeSet3=t_,t.schemeSpectral=b_,t.schemeTableau10=n_,t.schemeYlGn=U_,t.schemeYlGnBu=O_,t.schemeYlOrBr=Y_,t.schemeYlOrRd=j_,t.select=Fn,t.selectAll=function(t){return"string"==typeof t?new qn([document.querySelectorAll(t)],[document.documentElement]):new qn([Nt(t)],Dn)},t.selection=Rn,t.selector=kt,t.selectorAll=Pt,t.shuffle=nt,t.shuffler=et,t.some=function(t,n){if("function"!=typeof n)throw new TypeError("test is not a function");let e=-1;for(const r of t)if(n(r,++e,t))return!0;return!1},t.sort=P,t.stack=function(){var t=gb([]),n=rx,e=ex,r=ix;function i(i){var o,a,u=Array.from(t.apply(this,arguments),ox),c=u.length,f=-1;for(const t of i)for(o=0,++f;o0)for(var e,r,i,o,a,u,c=0,f=t[n[0]].length;c0?(r[0]=o,r[1]=o+=i):i<0?(r[1]=a,r[0]=a+=i):(r[0]=0,r[1]=i)},t.stackOffsetExpand=function(t,n){if((r=t.length)>0){for(var e,r,i,o=0,a=t[0].length;o0){for(var e,r=0,i=t[n[0]],o=i.length;r0&&(r=(e=t[n[0]]).length)>0){for(var e,r,i,o=0,a=1;a0)throw new Error("cycle");return o}return e.id=function(n){return arguments.length?(t=Md(n),e):t},e.parentId=function(t){return arguments.length?(n=Md(t),e):n},e},t.style=en,t.subset=function(t,n){return at(n,t)},t.sum=function(t,n){let e=0;if(void 0===n)for(let n of t)(n=+n)&&(e+=n);else{let r=-1;for(let i of t)(i=+n(i,++r,t))&&(e+=i)}return e},t.superset=at,t.svg=Wu,t.symbol=function(t,n){var e=null;function r(){var r;if(e||(e=r=pa()),t.apply(this,arguments).draw(e,+n.apply(this,arguments)),r)return e=null,r+""||null}return t="function"==typeof t?t:gb(t||om),n="function"==typeof n?n:gb(void 0===n?64:+n),r.type=function(n){return arguments.length?(t="function"==typeof n?n:gb(n),r):t},r.size=function(t){return arguments.length?(n="function"==typeof t?t:gb(+t),r):n},r.context=function(t){return arguments.length?(e=null==t?null:t,r):e},r},t.symbolCircle=om,t.symbolCross=am,t.symbolDiamond=fm,t.symbolSquare=pm,t.symbolStar=dm,t.symbolTriangle=ym,t.symbolWye=xm,t.symbols=wm,t.text=Yu,t.thresholdFreedmanDiaconis=function(t,n,e){return Math.ceil((e-n)/(2*(V(t,.75)-V(t,.25))*Math.pow(c(t),-1/3)))},t.thresholdScott=function(t,n,e){return Math.ceil((e-n)/(3.5*d(t)*Math.pow(c(t),-1/3)))},t.thresholdSturges=Y,t.tickFormat=Cp,t.tickIncrement=I,t.tickStep=U,t.ticks=O,t.timeDay=pg,t.timeDays=gg,t.timeFormatDefaultLocale=Dv,t.timeFormatLocale=py,t.timeFriday=wg,t.timeFridays=Ng,t.timeHour=hg,t.timeHours=dg,t.timeInterval=Qp,t.timeMillisecond=Jp,t.timeMilliseconds=tg,t.timeMinute=sg,t.timeMinutes=lg,t.timeMonday=_g,t.timeMondays=Tg,t.timeMonth=Pg,t.timeMonths=zg,t.timeSaturday=Mg,t.timeSaturdays=Cg,t.timeSecond=cg,t.timeSeconds=fg,t.timeSunday=vg,t.timeSundays=Ag,t.timeThursday=xg,t.timeThursdays=kg,t.timeTickInterval=sy,t.timeTicks=fy,t.timeTuesday=bg,t.timeTuesdays=Sg,t.timeWednesday=mg,t.timeWednesdays=Eg,t.timeWeek=vg,t.timeWeeks=Ag,t.timeYear=Dg,t.timeYears=qg,t.timeout=di,t.timer=ci,t.timerFlush=fi,t.transition=Wi,t.transpose=rt,t.tree=function(){var t=Fd,n=1,e=1,r=null;function i(i){var c=function(t){for(var n,e,r,i,o,a=new Yd(t,0),u=[a];n=u.pop();)if(r=n._.children)for(n.children=new Array(o=r.length),i=o-1;i>=0;--i)u.push(e=n.children[i]=new Yd(r[i],i)),e.parent=n;return(a.parent=new Yd(null,0)).children=[a],a}(i);if(c.eachAfter(o),c.parent.m=-c.z,c.eachBefore(a),r)i.eachBefore(u);else{var f=i,s=i,l=i;i.eachBefore((function(t){t.xs.x&&(s=t),t.depth>l.depth&&(l=t)}));var h=f===s?1:t(f,s)/2,d=h-f.x,p=n/(s.x+h+d),g=e/(l.depth||1);i.eachBefore((function(t){t.x=(t.x+d)*p,t.y=t.depth*g}))}return i}function o(n){var e=n.children,r=n.parent.children,i=n.i?r[n.i-1]:null;if(e){!function(t){for(var n,e=0,r=0,i=t.children,o=i.length;--o>=0;)(n=i[o]).z+=e,n.m+=e,e+=n.s+(r+=n.c)}(n);var o=(e[0].z+e[e.length-1].z)/2;i?(n.z=i.z+t(n._,i._),n.m=n.z-o):n.z=o}else i&&(n.z=i.z+t(n._,i._));n.parent.A=function(n,e,r){if(e){for(var i,o=n,a=n,u=e,c=o.parent.children[0],f=o.m,s=a.m,l=u.m,h=c.m;u=Id(u),o=Od(o),u&&o;)c=Od(c),(a=Id(a)).a=n,(i=u.z+l-o.z-f+t(u._,o._))>0&&(Ud(Bd(u,n,r),n,i),f+=i,s+=i),l+=u.m,f+=o.m,h+=c.m,s+=a.m;u&&!Id(a)&&(a.t=u,a.m+=l-s),o&&!Od(c)&&(c.t=o,c.m+=f-h,r=n)}return r}(n,i,n.parent.A||r[0])}function a(t){t._.x=t.z+t.parent.m,t.m+=t.parent.m}function u(t){t.x*=n,t.y=t.depth*e}return i.separation=function(n){return arguments.length?(t=n,i):t},i.size=function(t){return arguments.length?(r=!1,n=+t[0],e=+t[1],i):r?null:[n,e]},i.nodeSize=function(t){return arguments.length?(r=!0,n=+t[0],e=+t[1],i):r?[n,e]:null},i},t.treemap=function(){var t=Xd,n=!1,e=1,r=1,i=[0],o=Ad,a=Ad,u=Ad,c=Ad,f=Ad;function s(t){return t.x0=t.y0=0,t.x1=e,t.y1=r,t.eachBefore(l),i=[0],n&&t.eachBefore(Cd),t}function l(n){var e=i[n.depth],r=n.x0+e,s=n.y0+e,l=n.x1-e,h=n.y1-e;l=e-1){var s=u[n];return s.x0=i,s.y0=o,s.x1=a,void(s.y1=c)}var l=f[n],h=r/2+l,d=n+1,p=e-1;for(;d>>1;f[g]c-o){var _=r?(i*v+a*y)/r:a;t(n,d,y,i,o,_,c),t(d,e,v,_,o,a,c)}else{var b=r?(o*v+c*y)/r:c;t(n,d,y,i,o,a,b),t(d,e,v,i,b,a,c)}}(0,c,t.value,n,e,r,i)},t.treemapDice=Pd,t.treemapResquarify=Gd,t.treemapSlice=Ld,t.treemapSliceDice=function(t,n,e,r,i){(1&t.depth?Ld:Pd)(t,n,e,r,i)},t.treemapSquarify=Xd,t.tsv=Hu,t.tsvFormat=zu,t.tsvFormatBody=Du,t.tsvFormatRow=Ru,t.tsvFormatRows=qu,t.tsvFormatValue=Fu,t.tsvParse=Cu,t.tsvParseRows=Pu,t.union=function(...t){const n=new Set;for(const e of t)for(const t of e)n.add(t);return n},t.utcDay=Ug,t.utcDays=Bg,t.utcFriday=Vg,t.utcFridays=ty,t.utcHour=Og,t.utcHours=Ig,t.utcMillisecond=Jp,t.utcMilliseconds=tg,t.utcMinute=Rg,t.utcMinutes=Fg,t.utcMonday=jg,t.utcMondays=Zg,t.utcMonth=ey,t.utcMonths=ry,t.utcSaturday=$g,t.utcSaturdays=ny,t.utcSecond=cg,t.utcSeconds=fg,t.utcSunday=Lg,t.utcSundays=Wg,t.utcThursday=Gg,t.utcThursdays=Jg,t.utcTickInterval=cy,t.utcTicks=uy,t.utcTuesday=Hg,t.utcTuesdays=Kg,t.utcWednesday=Xg,t.utcWednesdays=Qg,t.utcWeek=Lg,t.utcWeeks=Wg,t.utcYear=iy,t.utcYears=oy,t.variance=h,t.version="7.0.0",t.window=Qt,t.xml=Vu,t.zip=function(){return rt(arguments)},t.zoom=function(){var t,n,e,r=vx,i=_x,o=wx,a=mx,u=xx,c=[0,1/0],f=[[-1/0,-1/0],[1/0,1/0]],s=250,l=Ir,h=yt("start","zoom","end"),d=500,p=0,g=10;function y(t){t.property("__zoom",bx).on("wheel.zoom",M,{passive:!1}).on("mousedown.zoom",A).on("dblclick.zoom",T).filter(u).on("touchstart.zoom",S).on("touchmove.zoom",E).on("touchend.zoom touchcancel.zoom",k).style("-webkit-tap-highlight-color","rgba(0,0,0,0)")}function v(t,n){return(n=Math.max(c[0],Math.min(c[1],n)))===t.k?t:new hx(n,t.x,t.y)}function _(t,n,e){var r=n[0]-e[0]*t.k,i=n[1]-e[1]*t.k;return r===t.x&&i===t.y?t:new hx(t.k,r,i)}function b(t){return[(+t[0][0]+ +t[1][0])/2,(+t[0][1]+ +t[1][1])/2]}function m(t,n,e,r){t.on("start.zoom",(function(){x(this,arguments).event(r).start()})).on("interrupt.zoom end.zoom",(function(){x(this,arguments).event(r).end()})).tween("zoom",(function(){var t=this,o=arguments,a=x(t,o).event(r),u=i.apply(t,o),c=null==e?b(u):"function"==typeof e?e.apply(t,o):e,f=Math.max(u[1][0]-u[0][0],u[1][1]-u[0][1]),s=t.__zoom,h="function"==typeof n?n.apply(t,o):n,d=l(s.invert(c).concat(f/s.k),h.invert(c).concat(f/h.k));return function(t){if(1===t)t=h;else{var n=d(t),e=f/n[2];t=new hx(e,c[0]-n[0]*e,c[1]-n[1]*e)}a.zoom(null,t)}}))}function x(t,n,e){return!e&&t.__zooming||new w(t,n)}function w(t,n){this.that=t,this.args=n,this.active=0,this.sourceEvent=null,this.extent=i.apply(t,n),this.taps=0}function M(t,...n){if(r.apply(this,arguments)){var e=x(this,n).event(t),i=this.__zoom,u=Math.max(c[0],Math.min(c[1],i.k*Math.pow(2,a.apply(this,arguments)))),s=Yn(t);if(e.wheel)e.mouse[0][0]===s[0]&&e.mouse[0][1]===s[1]||(e.mouse[1]=i.invert(e.mouse[0]=s)),clearTimeout(e.wheel);else{if(i.k===u)return;e.mouse=[s,i.invert(s)],mi(this),e.start()}yx(t),e.wheel=setTimeout(l,150),e.zoom("mouse",o(_(v(i,u),e.mouse[0],e.mouse[1]),e.extent,f))}function l(){e.wheel=null,e.end()}}function A(t,...n){if(!e&&r.apply(this,arguments)){var i=t.currentTarget,a=x(this,n,!0).event(t),u=Fn(t.view).on("mousemove.zoom",h,!0).on("mouseup.zoom",d,!0),c=Yn(t,i),s=t.clientX,l=t.clientY;Gn(t.view),gx(t),a.mouse=[c,this.__zoom.invert(c)],mi(this),a.start()}function h(t){if(yx(t),!a.moved){var n=t.clientX-s,e=t.clientY-l;a.moved=n*n+e*e>p}a.event(t).zoom("mouse",o(_(a.that.__zoom,a.mouse[0]=Yn(t,i),a.mouse[1]),a.extent,f))}function d(t){u.on("mousemove.zoom mouseup.zoom",null),Vn(t.view,a.moved),yx(t),a.event(t).end()}}function T(t,...n){if(r.apply(this,arguments)){var e=this.__zoom,a=Yn(t.changedTouches?t.changedTouches[0]:t,this),u=e.invert(a),c=e.k*(t.shiftKey?.5:2),l=o(_(v(e,c),a,u),i.apply(this,n),f);yx(t),s>0?Fn(this).transition().duration(s).call(m,l,a,t):Fn(this).call(y.transform,l,a,t)}}function S(e,...i){if(r.apply(this,arguments)){var o,a,u,c,f=e.touches,s=f.length,l=x(this,i,e.changedTouches.length===s).event(e);for(gx(e),a=0;ae;e++){var s=r.charCodeAt(e);n[2*e]=s>>>8,n[2*e+1]=s%256}return n},decompressFromUint8Array:function(o){if(null===o||void 0===o)return i.decompress(o);for(var n=new Array(o.length/2),e=0,t=n.length;t>e;e++)n[e]=256*o[2*e]+o[2*e+1];var s=[];return n.forEach(function(o){s.push(r(o))}),i.decompress(s.join(""))},compressToEncodedURIComponent:function(o){return null==o?"":i._compress(o,6,function(o){return e.charAt(o)})},decompressFromEncodedURIComponent:function(r){return null==r?"":""==r?null:(r=r.replace(/ /g,"+"),i._decompress(r.length,32,function(n){return o(e,r.charAt(n))}))},compress:function(o){return i._compress(o,16,function(o){return r(o)})},_compress:function(o,r,n){if(null==o)return"";var e,t,i,s={},p={},u="",c="",a="",l=2,f=3,h=2,d=[],m=0,v=0;for(i=0;ie;e++)m<<=1,v==r-1?(v=0,d.push(n(m)),m=0):v++;for(t=a.charCodeAt(0),e=0;8>e;e++)m=m<<1|1&t,v==r-1?(v=0,d.push(n(m)),m=0):v++,t>>=1}else{for(t=1,e=0;h>e;e++)m=m<<1|t,v==r-1?(v=0,d.push(n(m)),m=0):v++,t=0;for(t=a.charCodeAt(0),e=0;16>e;e++)m=m<<1|1&t,v==r-1?(v=0,d.push(n(m)),m=0):v++,t>>=1}l--,0==l&&(l=Math.pow(2,h),h++),delete p[a]}else for(t=s[a],e=0;h>e;e++)m=m<<1|1&t,v==r-1?(v=0,d.push(n(m)),m=0):v++,t>>=1;l--,0==l&&(l=Math.pow(2,h),h++),s[c]=f++,a=String(u)}if(""!==a){if(Object.prototype.hasOwnProperty.call(p,a)){if(a.charCodeAt(0)<256){for(e=0;h>e;e++)m<<=1,v==r-1?(v=0,d.push(n(m)),m=0):v++;for(t=a.charCodeAt(0),e=0;8>e;e++)m=m<<1|1&t,v==r-1?(v=0,d.push(n(m)),m=0):v++,t>>=1}else{for(t=1,e=0;h>e;e++)m=m<<1|t,v==r-1?(v=0,d.push(n(m)),m=0):v++,t=0;for(t=a.charCodeAt(0),e=0;16>e;e++)m=m<<1|1&t,v==r-1?(v=0,d.push(n(m)),m=0):v++,t>>=1}l--,0==l&&(l=Math.pow(2,h),h++),delete p[a]}else for(t=s[a],e=0;h>e;e++)m=m<<1|1&t,v==r-1?(v=0,d.push(n(m)),m=0):v++,t>>=1;l--,0==l&&(l=Math.pow(2,h),h++)}for(t=2,e=0;h>e;e++)m=m<<1|1&t,v==r-1?(v=0,d.push(n(m)),m=0):v++,t>>=1;for(;;){if(m<<=1,v==r-1){d.push(n(m));break}v++}return d.join("")},decompress:function(o){return null==o?"":""==o?null:i._decompress(o.length,32768,function(r){return o.charCodeAt(r)})},_decompress:function(o,n,e){var t,i,s,p,u,c,a,l,f=[],h=4,d=4,m=3,v="",w=[],A={val:e(0),position:n,index:1};for(i=0;3>i;i+=1)f[i]=i;for(p=0,c=Math.pow(2,2),a=1;a!=c;)u=A.val&A.position,A.position>>=1,0==A.position&&(A.position=n,A.val=e(A.index++)),p|=(u>0?1:0)*a,a<<=1;switch(t=p){case 0:for(p=0,c=Math.pow(2,8),a=1;a!=c;)u=A.val&A.position,A.position>>=1,0==A.position&&(A.position=n,A.val=e(A.index++)),p|=(u>0?1:0)*a,a<<=1;l=r(p);break;case 1:for(p=0,c=Math.pow(2,16),a=1;a!=c;)u=A.val&A.position,A.position>>=1,0==A.position&&(A.position=n,A.val=e(A.index++)),p|=(u>0?1:0)*a,a<<=1;l=r(p);break;case 2:return""}for(f[3]=l,s=l,w.push(l);;){if(A.index>o)return"";for(p=0,c=Math.pow(2,m),a=1;a!=c;)u=A.val&A.position,A.position>>=1,0==A.position&&(A.position=n,A.val=e(A.index++)),p|=(u>0?1:0)*a,a<<=1;switch(l=p){case 0:for(p=0,c=Math.pow(2,8),a=1;a!=c;)u=A.val&A.position,A.position>>=1,0==A.position&&(A.position=n,A.val=e(A.index++)),p|=(u>0?1:0)*a,a<<=1;f[d++]=r(p),l=d-1,h--;break;case 1:for(p=0,c=Math.pow(2,16),a=1;a!=c;)u=A.val&A.position,A.position>>=1,0==A.position&&(A.position=n,A.val=e(A.index++)),p|=(u>0?1:0)*a,a<<=1;f[d++]=r(p),l=d-1,h--;break;case 2:return w.join("")}if(0==h&&(h=Math.pow(2,m),m++),f[l])v=f[l];else{if(l!==d)return null;v=s+s.charAt(0)}w.push(v),f[d++]=s+v.charAt(0),h--,s=v,0==h&&(h=Math.pow(2,m),m++)}}};return i}();"function"==typeof define&&define.amd?define(function(){return LZString}):"undefined"!=typeof module&&null!=module&&(module.exports=LZString); \ No newline at end of file diff --git a/playground/js/playground.js b/playground/js/playground.js deleted file mode 100644 index 8c95dd16cd..0000000000 --- a/playground/js/playground.js +++ /dev/null @@ -1,1232 +0,0 @@ -// Copyright Vespa.ai. All rights reserved. - -contexts = { - VIEW: "view", - EDIT: "edit", - SETUP: "setup" -}; - -var show_setup = false; -var setup = { "f": [] }; -var results = { "f": [] }; -var variables = new Map(); -var selected = null; -var converter = new showdown.Converter(); -var context = contexts.VIEW; -var currentTheme = localStorage.getItem('theme') || null; // Will be set based on system preference - -/////////////////////////////////////////////////////////////////////////////// -// Notifications -/////////////////////////////////////////////////////////////////////////////// - -function show_notification(message, type = 'info', duration = 3000) { - // Remove any existing notifications - d3.selectAll('.notification').remove(); - - // Create notification element - var notification = d3.select('body') - .append('div') - .attr('class', 'notification ' + type) - .style('opacity', '0') - .html(message); - - // Fade in - notification - .transition() - .duration(300) - .style('opacity', '1'); - - // Automatically fade out after duration - setTimeout(function() { - notification - .transition() - .duration(300) - .style('opacity', '0') - .on('end', function() { - notification.remove(); - }); - }, duration); -} - -/////////////////////////////////////////////////////////////////////////////// -// Operations and UI -/////////////////////////////////////////////////////////////////////////////// - -var operations = { - - //////////////////////////////////////////////////////////// - // Comments - "c" : { - "params" : { - "t" : "" // text of comment - }, - "setup_ui" : function(param, element, header, content, frame_index) { - clear(header); - header.append("div").attr("class", "block header-text").html("Edit comment"); - add_setup_ui_buttons(header.append("div").attr("class", "block right")); - - clear(content); - add_table(content); - add_textarea_field(content, "Comment", "", 3, 100, param["t"], true); - add_save_cancel_field(content); - }, - "result_ui" : function(result, element, header, content, frame_index) { - clear(header); - header.append("div").attr("class", "block header-text").html("Comment"); - add_result_ui_buttons(header.append("div").attr("class", "block right"), frame_index); - - clear(content); - content.html(result.get("t")); - }, - "save" : function(param, element) { - if (element.select("textarea").node() != null) { - param["t"] = element.select("textarea").property("value").trim(); - } - }, - }, - - //////////////////////////////////////////////////////////// - // Expressions - "e" : { - "params" : { - "n" : "", // name - "e" : "" // expression - }, - "setup_ui" : function(param, element, header, content, frame_index) { - clear(header); - header.append("div").attr("class", "block header-text").html("Edit expression"); - add_setup_ui_buttons(header.append("div").attr("class", "block right")); - - clear(content); - add_table(content); - add_input_field(content, "Name", "expression_name", param["n"], true, "Only required if using in other expressions"); - add_textarea_field(content, "Expression", "expression_expression", 3, 65, param["e"], false); - add_save_cancel_field(content); - }, - "result_ui" : function(result, element, header, content, frame_index) { - header.html("Expression"); - clear(content); - if (result.size == 0) { - content.html("Not executed yet..."); - return; - } - - var headerLeft = "Expression"; - if (result.has("n") && result.get("n").length > 0) { - headerLeft = "" + result.get("n") + ""; - } - if (result.has("executing") && result.get("executing") == true) { - headerLeft += " (awaiting result)"; - } - - clear(header); - header.append("div").attr("class", "block header-text").html(headerLeft); - var button_area = header.append("div").attr("class", "block right"); - add_expression_result_ui_buttons(button_area, frame_index); - add_result_ui_buttons(button_area, frame_index); - - if (result.has("error")) { - content.html(""); - var table = content.append("table"); - if (result.has("e") && result.get("e").length > 0) { - var row = table.append("tr") - row.append("td").attr("class", "label").html("Expression"); - row.append("td").attr("class", "code").html(replace_html_code(result.get("e"))); - } - var row = table.append("tr") - row.append("td").attr("class", "label").html("Error"); - row.append("td").append("div").attr("class", "error").html(replace_html_code(result.get("error"))); - - } else { - var data = result.get("result"); - if (typeof data === "object") { - // Create a container for all tensor cards - var tensorContainer = content.append("div").attr("class", "tensor-section"); - - // Get the values we need - var value = data["value"]; - if (data["type"] !== null && data["type"].includes("tensor")) { - value = data["value"]["literal"]; - } - - // Expression row - if (result.has("e") && result.get("e").length > 0) { - var expressionRow = tensorContainer.append("div").attr("class", "tensor-row"); - expressionRow.append("div").attr("class", "tensor-label").html("Expression:"); - var expressionValue = expressionRow.append("div").attr("class", "tensor-value"); - expressionValue.html(replace_html_code(result.get("e"))); - - // Add copy icon to the right of the field - expressionRow.append("span") - .attr("class", "tensor-clipboard copy-button-align") - .html(icon_replicate()) - .on("click", function() { - copy_to_clipboard(result.get("e")); - event.stopPropagation(); - }); - } - - // Type row - if (result.has("type") && result.get("type").length > 0) { - var typeRow = tensorContainer.append("div").attr("class", "tensor-row"); - typeRow.append("div").attr("class", "tensor-label").html("Type:"); - var typeValue = typeRow.append("div").attr("class", "tensor-value"); - typeValue.html(replace_html_code(result.get("type"))); - } - - // Check if value should be shown based on the 'same' parameter in the response - const shouldShowValue = data["same"] !== true; - - if (shouldShowValue) { - // Value row - only show if different from expression - var valueRow = tensorContainer.append("div").attr("class", "tensor-row"); - valueRow.append("div").attr("class", "tensor-label").html("Value:"); - var valueContent = valueRow.append("div").attr("class", "tensor-value tensor-value-single-line"); - - // Add input with value - valueContent.append("pre") - .attr("style", "margin: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;") - .text(value); - - // Add copy icon to the right of the field - valueRow.append("span") - .attr("class", "tensor-clipboard copy-button-align") - .html(icon_replicate()) - .on("click", function(event) { - copy_to_clipboard(value); - event.stopPropagation(); - }); - } - - // Table row - with empty space for label to align with other rows - var tableRow = tensorContainer.append("div").attr("class", "tensor-row"); - tableRow.append("div").attr("class", "tensor-label").html(""); - var tableContent = tableRow.append("div").attr("style", "margin-top: 4px; width: 100%;"); - draw_table(tableContent, data); - - // Add execution trace section (previously steps) - const primitive = "Primitive representation:\n" + result.get("primitive"); - const steps = "Steps:\n" + JSON.stringify(JSON.parse(result.get("steps")), null, 2); - var debugContainer = content.append("div").attr("class", "debug-info-container").attr("id", "steps_" + frame_index); - - if (!result.has("show_details") || result.get("show_details") == false) { - debugContainer.attr("hidden", true); - } - - var traceHeader = debugContainer.append("div").attr("class", "tensor-row"); - traceHeader.append("div").attr("class", "tensor-label").html("Execution Trace:"); - debugContainer.append("textarea") - .attr("rows", steps.split("\n").length + 2) - .attr("class", "debug-info-text") - .attr("style", "width: 100%; margin-top: 4px;") - .text(primitive + "\n\n" + steps); - } else { - content.html(data); - } - } - - }, - "save" : function(param, element) { - if (element.select(".expression_name").node() != null) { - param["n"] = get_input_field_value(element, "expression_name"); - param["e"] = get_textarea_field_value(element, "expression_expression"); - } - }, - } -} - -function replace_html_code(str) { - return str.replace(//g, '>'); -} - -function has_error(response, result) { - result.delete("error"); - if (response == null) { - result.set("error", "Did not receive a response.") - return true; - } else if ("error" in response) { - result.set("error", response["error"]) - return true; - } - return false; -} - -function clear(root) { - root.html(""); -} - -function add_table(root) { - root.append("table"); -} - -function add_label_field(root, label) { - root = root.select("table"); - var field = root.append("tr"); - field.append("td") - .attr("colspan", "3") - .attr("class", "header") - .html(label); -} - -function add_input_field(root, label, classname, value, focus, helptext) { - root = root.select("table"); - var field = root.append("tr"); - field.append("td").html(label); - field.append("td") - .attr("class", classname) - .append("input") - .attr("value", value) - .attr("size", "50"); - field.append("td").html("" + (helptext == null ? "" : helptext) + ""); - if (focus) { - field.select("input").node().select(); - } -} - -function add_textarea_field(root, label, classname, rows, cols, value, focus, helptext) { - root = root.select("table"); - var field = root.append("tr"); - field.append("td").html(label); - field.append("td") - .attr("class", classname) - .append("textarea") - .attr("rows", rows) - .attr("cols", cols) - .text(value); - field.append("td").html("" + (helptext == null ? "" : helptext) + ""); - if (focus) { - field.select("textarea").node().select(); - } -} - -function add_save_cancel_field(root) { - var row = root.select("table").append("tr"); - row.append("td").attr("class", "label"); - var cell = row.append("td"); - - var saveButton = cell.append("a").attr("href", "#").attr("class","header tooltip save-button") - .html(icon_check() + " Save and execute (ctrl + enter)" + 'Save changes and execute expression') - .on("click", function(event) { execute_selected(); event.preventDefault(); }); - var cancelButton = cell.append("a").attr("href", "#").attr("class","header tooltip cancel-button") - .attr("style","margin-left: 80px").html(icon_exit() + " Cancel (escape)" + 'Cancel editing') - .on("click", function(event) { document.activeElement.blur(); exit_edit_selected(); event.preventDefault(); }); - - // Check if we're in an expression context by looking for the expression textarea - var expressionTextarea = root.select(".expression_expression textarea"); - if (!expressionTextarea.empty()) { - // disable save button if expression is empty (causes ugly errors from backend) - function updateButtonStates() { - var value = get_textarea_field_value(root, "expression_expression"); - var isEmpty = value === ""; - - saveButton.classed("disabled", isEmpty); - } - - // Initial check - updateButtonStates(); - - // Add event listener to check for changes - expressionTextarea.on("input", updateButtonStates); - } -} - -function add_setup_ui_buttons(root) { - root.append("a").attr("href", "#").attr("class","header tooltip").html(icon_cancel() + 'Cancel editing') - .on("click", function(event) { document.activeElement.blur(); exit_edit_selected(); event.preventDefault(); }); -} - -function addActionButton(root, iconFn, actionFn, frameIndex, actionName, tooltipText) { - root.append("a") - .attr("href", "#") - .attr("class", "header tooltip") - .html(iconFn() + `${tooltipText}`) - .on("click", function(event) { - // Only allow actions if not in edit mode - if (context !== contexts.EDIT) { - actionFn(frameIndex); - } else { - show_notification(`Cannot ${actionName} while in edit mode. Finish editing first.`, "warning"); - } - event.stopPropagation(); - event.preventDefault(); - }); -} - -function add_result_ui_buttons(root, frame_index) { - addActionButton(root, icon_edit, edit_frame, frame_index, "edit a different frame", "Edit this frame"); - addActionButton(root, icon_up, move_frame_up, frame_index, "move frames", "Move frame up"); - addActionButton(root, icon_down, move_frame_down, frame_index, "move frames", "Move frame down"); - addActionButton(root, icon_cross, remove_frame, frame_index, "remove frames", "Remove this frame"); -} - -function add_expression_result_ui_buttons(root, frame_index) { - root.append("a").attr("href", "#").attr("class","header header-space tooltip").html(icon_hierarchy() + 'Show execution trace') - .on("click", function(event) { show_details(frame_index); event.stopPropagation(); event.preventDefault(); }); -} - -function get_input_field_value(root, classname) { - return root.select("." + classname).select("input").property("value").trim(); -} - -function get_textarea_field_value(root, classname) { - return root.select("." + classname).select("textarea").property("value").trim(); -} - -function get_select_field_value(root, classname) { - return root.select("." + classname).select("select").property("value").trim(); -} - -function draw_table(element, variable) { - if (variable === null || typeof variable !== "object") { - return; - } - - var type = variable["type"]; - var columns = new Set(); - columns.add("__value__"); - - var data = [ { "__value__": variable["value"]} ] - if (type.includes("tensor")) { - data = variable["value"]["cells"].map(function(cell) { - var entry = new Object(); - var address = cell["address"]; - for (var dim in address) { - entry[dim] = address[dim]; - columns.add(dim); - } - entry["__value__"] = cell["value"]; - return entry; - }); - } - - columns = [...columns]; // sort "value" to back - columns.sort(function(a, b) { - var _a = a.toLowerCase(); // ignore upper and lowercase - var _b = b.toLowerCase(); // ignore upper and lowercase - if (_a.startsWith("__value__") && !_b.startsWith("__value__")) { - return 1; - } - if (_b.startsWith("__value__") && !_a.startsWith("__value__")) { - return -1; - } - if (_a < _b) { - return -1; - } - if (_a > _b) { - return 1; - } - return 0; - }); - - if (data.length > 25) { - empty_row = columns.reduce((accumulator,current)=>(accumulator[current]='',accumulator), {}) - empty_row.__value__ = "..."; - filtered_data = data.filter(function(d,i) { return i < 10; }); - filtered_data = filtered_data.concat([empty_row]); - data = filtered_data.concat(data.filter(function(d,i) { return i > data.length - 10 - 1; })); - } - table_html(element, data, columns); -} - -function table_html(element, data, columns) { - var table = element.append("table") - .attr("class", "tensor-table"), - thead = table.append("thead"), - tbody = table.append("tbody"); - - thead.append("tr") - .selectAll("th") - .data(columns) - .enter() - .append("th") - .text(function(column) { return column.startsWith("__value__") ? "value" : column; }); - - var rows = tbody.selectAll("tr") - .data(data) - .enter() - .append("tr"); - - var cells = rows.selectAll("td") - .data(function(row) { - return columns.map(function(column) { - return {column: column, value: row[column]}; - }); - }) - .enter() - .append("td") - .text(function(d) { return d.value; }); - - return table; -} - -// icons from https://systemuicons.com/ -function icon_edit() { - return ''; -} - -function icon_up() { - return ''; -} - -function icon_down() { - return ''; -} - -function icon_cross() { - return ''; -} - -function icon_cancel() { - return ''; -} - -function icon_replicate() { - return ''; -} - -function icon_check() { - return ''; -} - -function icon_exit() { - return ''; -} - -function icon_comment() { - return ''; -} - -function icon_code() { - return ''; -} - -function icon_hierarchy() { - return ''; -} - -function setup_commands() { - d3.select("#view-setup-cmd").on("click", function(event) { toggle_show_setup(); event.preventDefault(); }); - d3.select("#clear-cmd").on("click", function(event) { clear_all(); event.preventDefault(); }); - d3.select("#copy-url-cmd").on("click", function(event) { copy_to_clipboard(window.location); event.preventDefault(); }); - d3.select("#new-comment-cmd").html(icon_comment() + " " + d3.select("#new-comment-cmd").html()) - .on("click", function(event) { - select_frame_by_index(num_frames() - 1); - new_frame("c"); - event.preventDefault(); - }); - d3.select("#new-expression-cmd").html(icon_code() + " " + d3.select("#new-expression-cmd").html()) - .on("click", function(event) { - select_frame_by_index(num_frames() - 1); - new_frame("e"); - event.preventDefault(); - }); - d3.select("#apply-setup-cmd").html(icon_check() + " " + d3.select("#apply-setup-cmd").html()) - .on("click", function(event) { apply_setup(); event.preventDefault(); }); - d3.select("#close-setup-cmd").html(icon_exit() + " " + d3.select("#close-setup-cmd").html()) - .on("click", function(event) { toggle_show_setup(); event.preventDefault(); }); -} - -/////////////////////////////////////////////////////////////////////////////// -// Setup handling -/////////////////////////////////////////////////////////////////////////////// - -function load_setup() { - if (window.location.hash) { - var compressed = window.location.hash.substring(1); - var decompressed = LZString.decompressFromEncodedURIComponent(compressed); - setup = JSON.parse(decompressed); - d3.select("#setup-content").text(JSON.stringify(setup, null, 2)); - d3.select("#setup-input").attr("value", compressed); - } -} - -function apply_setup() { - var setup_string = d3.select("#setup-content").property("value"); - setup = JSON.parse(setup_string); - save_setup(); - clear_results(); - execute_all(); - toggle_show_setup(); -} - -function save_setup() { - var setup_string = JSON.stringify(setup, null, 2); - d3.select("#setup-content").text(setup_string); - var compressed = LZString.compressToEncodedURIComponent(setup_string); - window.location.hash = compressed; - d3.select("#setup-input").attr("value", compressed); -} - -function save_changes() { - d3.selectAll(".setup").each(function (d,i) { - var element = d3.select(this); - var op = d["op"]; - var param = d["p"]; - operations[op]["save"](param, element); - }); - save_setup(); -} - -function clear_results() { - results["f"] = []; - for (var i = 0; i < setup["f"].length; ++i) { - results["f"][i] = new Map(); - } -} - -function clear_all() { - setup = { "f": [] }; - save_setup(); - clear_results(); - clear_examples(); - update(); -} - -function toggle_show_setup() { - show_setup = !show_setup; - d3.select("#setup-container").classed("hidden", !show_setup); - d3.select("#frames").classed("hidden", show_setup); - d3.select("#add_frames").classed("hidden", show_setup); - if (show_setup) { - d3.select("#setup-content").node().focus(); - context = contexts.SETUP; - } else { - context = contexts.VIEW; - } -} - -function num_frames() { - return setup["f"].length; -} - -function swap(frame1, frame2) { - var setup_frame_1 = setup["f"][frame1]; - setup["f"][frame1] = setup["f"][frame2]; - setup["f"][frame2] = setup_frame_1; - - var result_frame_1 = results["f"][frame1]; - results["f"][frame1] = results["f"][frame2]; - results["f"][frame2] = result_frame_1; -} - -function remove(frame) { - setup["f"].splice(frame, 1); - results["f"].splice(frame, 1); -} - - -/////////////////////////////////////////////////////////////////////////////// -// UI handling -/////////////////////////////////////////////////////////////////////////////// - -function new_frame(operation) { - var default_params = JSON.stringify(operations[operation]["params"]); - setup["f"].push({ - "op" : operation, - "p" : JSON.parse(default_params) - }); - results["f"].push(new Map()); - - var insert_as_index = find_selected_frame_index() + 1; - var current_index = num_frames() - 1; - if (current_index > 0) { - while (current_index > insert_as_index) { - swap(current_index, current_index - 1); - current_index -= 1; - } - } - - save_setup(); - update(); - select_frame_by_index(insert_as_index); - document.activeElement.blur(); - edit_selected(); -} - -function update() { - var all_data = d3.zip(setup["f"], results["f"]); - - var rows = d3.select("#frames").selectAll(".frame").data(all_data); - rows.exit().remove(); - var frames = rows.enter() - .append("div") - .on("click", function() { - // Only allow frame selection when not in edit mode - if (context !== contexts.EDIT) { - select_frame(this); - } - }) - .attr("class", "frame"); - frames.append("div").attr("class", "frame-header").html("header"); - frames.append("div").attr("class", "frame-content").html("content"); - - d3.select("#frames").selectAll(".frame").data(all_data).each(function (d, i) { - var element = d3.select(this); - var op = d[0]["op"]; - var param = d[0]["p"]; - var result = d[1]; - - var header = element.select(".frame-header"); - var content = element.select(".frame-content"); - - operations[op]["result_ui"](result, element, header, content, i); - }); -} - -function remove_frame(frame_index) { - select_frame_by_index(frame_index); - remove_selected(); -} - -function remove_selected() { - var frame = find_selected_frame_index(); - remove(frame); - save_setup(); - update(); - select_frame_by_index(frame); -} - -function move_frame_up(frame_index) { - select_frame_by_index(frame_index); - move_selected_up(); -} - -function move_selected_up() { - var frame_index = find_selected_frame_index(); - if (frame_index == 0) { - return; - } - swap(frame_index, frame_index-1); - save_setup(); - update(); - select_frame_by_index(frame_index-1); -} - -function move_frame_down(frame_index) { - select_frame_by_index(frame_index); - move_selected_down(); -} - -function move_selected_down() { - var frame_index = find_selected_frame_index(); - if (frame_index == setup["f"].length - 1) { - return; - } - swap(frame_index, frame_index+1); - save_setup(); - update(); - select_frame_by_index(frame_index+1); -} - -// Check if an expression is empty (only contains whitespace) -function is_expression_empty() { - // Only check in edit mode and for expressions - if (context !== contexts.EDIT) { - return false; - } - - var frame = d3.select(selected); - if (!frame.empty()) { - var data = frame.data(); - var setup = data[0][0]; // because of zip in update - var op = setup["op"]; - - // Only check for expressions - if (op === "e") { - try { - var value = get_textarea_field_value(frame, "expression_expression"); - return value === ""; - } catch (e) { - // If there's an error (e.g., textarea not found), return false - return false; - } - } - } - return false; -} - -function execute_selected() { - // Don't execute if the expression is empty - if (is_expression_empty()) { - show_notification("Expression is empty. Please enter an expression first.", "warning"); - return; - } - - var frame_index = find_selected_frame_index(); - var frame = d3.select(selected); - var data = frame.data(); - var setup = data[0][0]; // because of zip in update - var op = setup["op"]; - var param = setup["p"]; - - operations[op]["save"](param, frame); - save_setup(); - exit_edit_selected(); - - // If frame is a comment, update directly. Else execute all - if (op == "c") { - var comment = replace_html_code(param["t"]); - results["f"][frame_index].set("t", converter.makeHtml(comment)); - update(); - } else { - execute_all(); - } -} - -function execute_all() { - var expressions = []; - for (var i=0; i < setup["f"].length; ++i) { - var op = setup["f"][i]["op"]; - var param = setup["f"][i]["p"]; - var result = results["f"][i]; - - if (op == "e") { - expressions.push({ "cell": i, "name": param["n"], "expr": param["e"], "verbose":true }); // later: add option - if ( ! result.has("result")) { - result.set("result", "Executing..."); - } - result.set("executing", true) - result.set("n", replace_html_code(param["n"])) - result.set("e", replace_html_code(param["e"])) - } else if (op == "c") { - var comment = replace_html_code(param["t"]); - result.set("t", converter.makeHtml(comment)); - } - } - update(); - - d3.text("https://api.search.vespa.ai/playground/eval", { - method: "POST", - body: "json=" + encodeURIComponent(JSON.stringify(expressions)), - headers: { "Content-Type": "application/x-www-form-urlencoded" } - }) - .then(function(response) { - response = JSON.parse(response); - - if ( ! Array.isArray(response) ) { - var error = "Unknown error"; - if ("error" in response) { - error = response["error"]; - } - for (var i=0; i < setup["f"].length; ++i) { - results["f"][i].set("executing", false); - results["f"][i].set("error", response["error"]); - } - update(); - return; - } - - for(var i=0; i < response.length; ++i) { - var cell = response[i]["cell"]; - var param = setup["f"][cell]["p"]; - var result = results["f"][cell]; - - result.set("n", replace_html_code(param["n"])); - result.set("e", param["e"]); - result.set("executing", false); - - if ( ! has_error(response[i], result) ) { - if (param["n"].length > 0) { - variables.set(param["n"], { - "name" : param["n"], - "type" : response[i]["type"], - "value": response[i]["type"].includes("tensor") ? response[i]["value"]["literal"] : response["value"] - }); - } - result.set("result", response[i]); - result.set("type", response[i]["type"]); - result.set("steps", response[i]["steps"]); - result.set("primitive", response[i]["primitive"]); - } - } - update(); - }); -} - -function find_selected_frame_index() { - var result = -1; - d3.select("#frames").selectAll(".frame") - .each(function (d, i) { - if (this.classList.contains("selected")) { - result = i; - } - }); - return result; -} - -function find_frame_index(frame) { - var result = null; - // If frame is a D3 selection, get the DOM element - var frameNode = frame.node ? frame.node() : frame; - - d3.select("#frames").selectAll(".frame") - .each(function (d, i) { - if (this == frameNode) { - result = i; - } - }); - return result; -} - -function is_element_entirely_visible(el) { - var rect = el.getBoundingClientRect(); - var height = window.innerHeight || doc.documentElement.clientHeight; - return !(rect.top < 50 || rect.bottom > height); -} - -function select_frame(frame) { - // Don't allow selecting a different frame while in edit mode - if (context === contexts.EDIT) { - show_notification("Cannot select a different frame while in edit mode. Finish editing first.", "warning"); - return; - } - - if (selected == frame) { - return; - } - - if (selected != null) { - selected.classList.remove("selected"); - } - selected = frame; - selected.classList.add("selected"); - if (!is_element_entirely_visible(selected)) { - selected.scrollIntoView(); - document.scrollingElement.scrollTop -= 60; - } - selected_frame_index = find_selected_frame_index(); -} - -function select_frame_by_index(i) { - // Don't allow selecting a different frame while in edit mode - if (context === contexts.EDIT) { - show_notification("Cannot select a different frame while in edit mode. Finish editing first.", "warning"); - return; - } - - if (i >= num_frames()) { - i = num_frames() - 1; - } - if (i < 0) { - i = 0; - } - d3.select("#frames").selectAll(".frame") - .each(function (datum, index) { - if (i == index) { - select_frame(this); - } - }); -} - -function edit_frame(frame_index) { - select_frame_by_index(frame_index); - edit_selected(); -} - -function edit_selected() { - if (context === contexts.EDIT) { - exit_edit_selected(); - return; - } - var frame = d3.select(selected); - var data = frame.data(); - var setup = data[0][0]; // because of zip in update - var result = data[0][1]; - - var op = setup["op"]; - var param = setup["p"]; - - var header = frame.select(".frame-header"); - var content = frame.select(".frame-content"); - - operations[op]["setup_ui"](param, frame, header, content, find_frame_index(frame)); - - // Add visual indicator for edit mode - frame.classed("edit-mode-active", true); - - context = contexts.EDIT; -} - -function show_details(frame_index) { - var result = results["f"][frame_index]; - var element = document.getElementById("steps_" + frame_index); - if (element != null) { - element.hidden = ! element.hidden; - result.set("show_details", result.has("show_details") ? ! result.get("show_details") : true); - } -} - -function exit_edit_selected() { - if (context !== contexts.EDIT) { - return; - } - - var frame = d3.select(selected); - var data = frame.data(); - var setup = data[0][0]; // because of zip in update - var result = data[0][1]; - - var op = setup["op"]; - var param = setup["p"]; - - // Check if this is a new frame that hasn't been executed yet - var isNewFrame = result.size === 0; - var isEmptyExpression = op === "e" && param["e"] === ""; - var isEmptyComment = op === "c" && param["t"] === ""; - - if (isNewFrame && (isEmptyExpression || isEmptyComment)) { - // Nothing was entered, remove the frame - var frameIndex = find_frame_index(frame); - remove(frameIndex); - save_setup(); - update(); - // Remove visual indicator for edit mode - frame.classed("edit-mode-active", false); - context = contexts.VIEW; - select_frame_by_index(frameIndex-1); - return; - } - - var header = frame.select(".frame-header"); - var content = frame.select(".frame-content"); - - operations[op]["result_ui"](result, frame, header, content, find_frame_index(frame)); - - // Remove visual indicator for edit mode - frame.classed("edit-mode-active", false); - - context = contexts.VIEW; -} - -function event_in_input(event) { - var tag_name = d3.select(event.target).node().tagName; - return (tag_name == 'INPUT' || tag_name == 'SELECT' || tag_name == 'TEXTAREA' || tag_name == 'BUTTON'); -} - -function event_in_frame(event) { - var node = d3.select(event.target).node(); - while (node != null) { - if (d3.select(node).attr("class") != null) { - if (d3.select(node).attr("class").includes("frame")) { - return true - } - } - node = node.parentElement; - } - return false; -} - -function copy_to_clipboard(text) { - var textarea = document.createElement("textarea"); - textarea.value = text; - document.body.appendChild(textarea); - textarea.select(); - document.execCommand('copy'); - document.body.removeChild(textarea); - - // Show notification when copied to clipboard - show_notification("Copied to clipboard", "success", 2000); -} - - - -function setup_keybinds() { - var previous_keydown = { "key" : null, "ts" : 0 }; - - key_binds = {} - key_binds[contexts.VIEW] = {} - key_binds[contexts.EDIT] = {} - key_binds[contexts.SETUP] = {} - - key_binds[contexts.VIEW]["up"] = - key_binds[contexts.VIEW]["k"] = function() { select_frame_by_index(find_selected_frame_index() - 1); }; - key_binds[contexts.VIEW]["down"] = - key_binds[contexts.VIEW]["j"] = function() { select_frame_by_index(find_selected_frame_index() + 1); }; - - key_binds[contexts.VIEW]["shift + up"] = - key_binds[contexts.VIEW]["shift + k"] = function() { move_selected_up(); }; - key_binds[contexts.VIEW]["shift + down"] = - key_binds[contexts.VIEW]["shift + j"] = function() { move_selected_down(); }; - - key_binds[contexts.VIEW]["x"] = - key_binds[contexts.VIEW]["d,d"] = function() { remove_selected(); }; - - key_binds[contexts.VIEW]["e"] = - key_binds[contexts.VIEW]["enter"] = function() { edit_selected(); }; - key_binds[contexts.VIEW]["ctrl + enter"] = function() { execute_selected(); }; - - key_binds[contexts.VIEW]["n,c"] = function() { new_frame("c"); }; - key_binds[contexts.VIEW]["n,e"] = function() { new_frame("e"); }; - key_binds[contexts.VIEW]["a"] = function() { new_frame(null); }; - - key_binds[contexts.VIEW]["esc"] = function() { document.activeElement.blur(); }; - key_binds[contexts.SETUP]["esc"] = function() { document.activeElement.blur(); }; - - key_binds[contexts.EDIT]["esc"] = function() { document.activeElement.blur(); exit_edit_selected(); }; - key_binds[contexts.EDIT]["ctrl + enter"] = function() { execute_selected(); }; - - - d3.select('body').on('keydown', function(event) { - var combo = []; - - if (event.shiftKey) combo.push("shift"); - if (event.ctrlKey) combo.push("ctrl"); - if (event.altKey) combo.push("alt"); - if (event.metaKey) combo.push("meta"); - - var key_code = event.keyCode; - - if (key_code == 8) combo.push("backspace"); - if (key_code == 13) combo.push("enter"); - if (key_code == 27) combo.push("esc"); - if (key_code == 32) combo.push("space"); - if (key_code == 46) combo.push("del"); - - if (key_code == 37) combo.push("left"); - if (key_code == 38) combo.push("up"); - if (key_code == 39) combo.push("right"); - if (key_code == 40) combo.push("down"); - - // a-z - if (key_code >= 64 && key_code < 91) combo.push(String.fromCharCode(key_code).toLowerCase()); - - var key = combo.join(" + "); - if (event_in_input(event) && !event_in_frame(event) && key !== "esc") { - return; - } - - // Check if combo combined with previous key is bound - if (Date.now() - previous_keydown["ts"] < 400) { - var two_key = previous_keydown["key"] + "," + key; - if (two_key in key_binds[context]) { - key_binds[context][two_key](); - event.preventDefault(); - previous_keydown = { "key":null, "ts": 0 }; // reset - return; - } - } - - if (key in key_binds[context]) { - key_binds[context][key](); - event.preventDefault(); - } - - previous_keydown = { "key":key, "ts": Date.now() }; - }); -} - -function setup_examples() { - d3.select("#examples-select").on("change", function (_) { go_to_example(); }); - - if (window.location.hash) { - var hash = window.location.hash.substring(1); - var select = d3.select("#examples-select"); - var option = select.select(`option[value="${hash}"]`); - - if (!option.empty()) { - select.property("value", hash); - } else { - select.property("value", ""); - } - } -} - -function go_to_example() { - var hash = d3.select("#examples-select").property("value"); - window.location.hash = hash; - window.location.reload(); -} - -function clear_examples() { - d3.select("#examples-select").property("value", ""); -} - -/////////////////////////////////////////////////////////////////////////////// -// Theme Toggle Functionality -/////////////////////////////////////////////////////////////////////////////// - -function setTheme(theme, saveToStorage = true) { - currentTheme = theme; - if (saveToStorage) { - localStorage.setItem('theme', theme); - } - document.documentElement.setAttribute('data-theme', theme); - - // Update the theme toggle icon - const themeToggle = document.getElementById('toggle-theme-cmd'); - if (themeToggle) { - if (theme === 'dark') { - themeToggle.innerHTML = ` - - - - - - - - - - - - Toggle light mode - `; - } else { - themeToggle.innerHTML = ` - - - - Toggle dark mode - `; - } - } -} - -function toggleTheme() { - const newTheme = currentTheme === 'light' ? 'dark' : 'light'; - setTheme(newTheme, true); // Save to storage when manually toggled - show_notification(`Switched to ${newTheme} mode`, 'info', 2000); -} - -function setupThemeToggle() { - const themeToggle = document.getElementById('toggle-theme-cmd'); - if (themeToggle) { - themeToggle.addEventListener('click', function(e) { - e.preventDefault(); - toggleTheme(); - }); - } - - // Check if theme is saved in localStorage - const savedTheme = localStorage.getItem('theme'); - const prefersDarkMode = window.matchMedia('(prefers-color-scheme: dark)').matches; - - if (savedTheme) { - // Use saved preference if it exists - currentTheme = savedTheme; - } else { - // Otherwise use system preference - currentTheme = prefersDarkMode ? 'dark' : 'light'; - } - - // Apply the theme without saving to localStorage if using system preference - setTheme(currentTheme, savedTheme !== null); - - // Listen for system preference changes - window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', e => { - // Only change if user hasn't set a preference - if (!localStorage.getItem('theme')) { - setTheme(e.matches ? 'dark' : 'light', false); // Don't save to storage when following system - } - }); -} - -function main() { - setup_commands(); - load_setup(); - clear_results(); - execute_all(); - select_frame_by_index(0); - setup_keybinds(); - setup_examples(); - setupThemeToggle(); -} diff --git a/playground/js/showdown.min.js b/playground/js/showdown.min.js deleted file mode 100644 index 7a8ffc8f0c..0000000000 --- a/playground/js/showdown.min.js +++ /dev/null @@ -1,5 +0,0 @@ -/*! showdown 02-06-2017 */ - -(function(){function a(a){"use strict";var b={omitExtraWLInCodeBlocks:{defaultValue:!1,describe:"Omit the default extra whiteline added to code blocks",type:"boolean"},noHeaderId:{defaultValue:!1,describe:"Turn on/off generated header id",type:"boolean"},prefixHeaderId:{defaultValue:!1,describe:"Specify a prefix to generated header ids",type:"string"},ghCompatibleHeaderId:{defaultValue:!1,describe:"Generate header ids compatible with github style (spaces are replaced with dashes, a bunch of non alphanumeric chars are removed)",type:"boolean"},headerLevelStart:{defaultValue:!1,describe:"The header blocks level start",type:"integer"},parseImgDimensions:{defaultValue:!1,describe:"Turn on/off image dimension parsing",type:"boolean"},simplifiedAutoLink:{defaultValue:!1,describe:"Turn on/off GFM autolink style",type:"boolean"},excludeTrailingPunctuationFromURLs:{defaultValue:!1,describe:"Excludes trailing punctuation from links generated with autoLinking",type:"boolean"},literalMidWordUnderscores:{defaultValue:!1,describe:"Parse midword underscores as literal underscores",type:"boolean"},literalMidWordAsterisks:{defaultValue:!1,describe:"Parse midword asterisks as literal asterisks",type:"boolean"},strikethrough:{defaultValue:!1,describe:"Turn on/off strikethrough support",type:"boolean"},tables:{defaultValue:!1,describe:"Turn on/off tables support",type:"boolean"},tablesHeaderId:{defaultValue:!1,describe:"Add an id to table headers",type:"boolean"},ghCodeBlocks:{defaultValue:!0,describe:"Turn on/off GFM fenced code blocks support",type:"boolean"},tasklists:{defaultValue:!1,describe:"Turn on/off GFM tasklist support",type:"boolean"},smoothLivePreview:{defaultValue:!1,describe:"Prevents weird effects in live previews due to incomplete input",type:"boolean"},smartIndentationFix:{defaultValue:!1,description:"Tries to smartly fix indentation in es6 strings",type:"boolean"},disableForced4SpacesIndentedSublists:{defaultValue:!1,description:"Disables the requirement of indenting nested sublists by 4 spaces",type:"boolean"},simpleLineBreaks:{defaultValue:!1,description:"Parses simple line breaks as
    (GFM Style)",type:"boolean"},requireSpaceBeforeHeadingText:{defaultValue:!1,description:"Makes adding a space between `#` and the header text mandatory (GFM Style)",type:"boolean"},ghMentions:{defaultValue:!1,description:"Enables github @mentions",type:"boolean"},ghMentionsLink:{defaultValue:"https://github.com/{u}",description:"Changes the link generated by @mentions. Only applies if ghMentions option is enabled.",type:"string"},encodeEmails:{defaultValue:!0,description:"Encode e-mail addresses through the use of Character Entities, transforming ASCII e-mail addresses into its equivalent decimal entities",type:"boolean"},openLinksInNewWindow:{defaultValue:!1,description:"Open all links in new windows",type:"boolean"}};if(!1===a)return JSON.parse(JSON.stringify(b));var c={};for(var d in b)b.hasOwnProperty(d)&&(c[d]=b[d].defaultValue);return c}function b(a,b){"use strict";var c=b?"Error in "+b+" extension->":"Error in unnamed extension",e={valid:!0,error:""};d.helper.isArray(a)||(a=[a]);for(var f=0;f-1,l=new RegExp(b+"|"+c,"g"+j.replace(/g/g,"")),m=new RegExp(b,j.replace(/g/g,"")),n=[];do{for(e=0;g=l.exec(a);)if(m.test(g[0]))e++||(f=l.lastIndex,h=f-g[0].length);else if(e&&!--e){i=g.index+g[0].length;var o={left:{start:h,end:f},match:{start:f,end:g.index},right:{start:g.index,end:i},wholeMatch:{start:h,end:i}};if(n.push(o),!k)return n}}while(e&&(l.lastIndex=f));return n};d.helper.matchRecursiveRegExp=function(a,b,c,d){"use strict";for(var e=j(a,b,c,d),f=[],g=0;g0){var l=[];0!==h[0].wholeMatch.start&&l.push(a.slice(0,h[0].wholeMatch.start));for(var m=0;m=0?e+(c||0):e},d.helper.splitAtIndex=function(a,b){"use strict";if(!d.helper.isString(a))throw"InvalidArgumentError: first parameter of showdown.helper.regexIndexOf function must be a string";return[a.substring(0,b),a.substring(b)]},d.helper.encodeEmailAddress=function(a){"use strict";var b=[function(a){return"&#"+a.charCodeAt(0)+";"},function(a){return"&#x"+a.charCodeAt(0).toString(16)+";"},function(a){return a}];return a=a.replace(/./g,function(a){if("@"===a)a=b[Math.floor(2*Math.random())](a);else{var c=Math.random();a=c>.9?b[2](a):c>.45?b[1](a):b[0](a)}return a})},"undefined"==typeof console&&(console={warn:function(a){"use strict";alert(a)},log:function(a){"use strict";alert(a)},error:function(a){"use strict";throw a}}),d.helper.regexes={asteriskAndDash:/([*_])/g},d.Converter=function(a){"use strict";function c(a,c){if(c=c||null,d.helper.isString(a)){if(a=d.helper.stdExtName(a),c=a,d.extensions[a])return console.warn("DEPRECATION WARNING: "+a+" is an old extension that uses a deprecated loading method.Please inform the developer that the extension should be updated!"),void e(d.extensions[a],a);if(d.helper.isUndefined(f[a]))throw Error('Extension "'+a+'" could not be loaded. It was either not found or is not a valid extension.');a=f[a]}"function"==typeof a&&(a=a()),d.helper.isArray(a)||(a=[a]);var g=b(a,c);if(!g.valid)throw Error(g.error);for(var h=0;h? ?(['"].*['"])?\)$/m)>-1)g="";else if(!g){if(f||(f=e.toLowerCase().replace(/ ?\n/g," ")),g="#"+f,d.helper.isUndefined(c.gUrls[f]))return a;g=c.gUrls[f],d.helper.isUndefined(c.gTitles[f])||(j=c.gTitles[f])}g=g.replace(d.helper.regexes.asteriskAndDash,d.helper.escapeCharactersCallback);var k='"};return a=a.replace(/\[((?:\[[^\]]*]|[^\[\]])*)] ?(?:\n *)?\[(.*?)]()()()()/g,e),a=a.replace(/\[((?:\[[^\]]*]|[^\[\]])*)]()[ \t]*\([ \t]?<([^>]*)>(?:[ \t]*((["'])([^"]*?)\5))?[ \t]?\)/g,e),a=a.replace(/\[((?:\[[^\]]*]|[^\[\]])*)]()[ \t]*\([ \t]??(?:[ \t]*((["'])([^"]*?)\5))?[ \t]?\)/g,e),a=a.replace(/\[([^\[\]]+)]()()()()()/g,e),b.ghMentions&&(a=a.replace(/(^|\s)(\\)?(@([a-z\d\-]+))(?=[.!?;,[\]()]|\s|$)/gim,function(a,c,e,f,g){if("\\"===e)return c+f;if(!d.helper.isString(b.ghMentionsLink))throw new Error("ghMentionsLink option must be a string");return c+''+f+""})),a=c.converter._dispatch("anchors.after",a,b,c)});var k=/\b(((https?|ftp|dict):\/\/|www\.)[^'">\s]+\.[^'">\s]+)()(?=\s|$)(?!["<>])/gi,l=/\b(((https?|ftp|dict):\/\/|www\.)[^'">\s]+\.[^'">\s]+?)([.!?,()\[\]]?)(?=\s|$)(?!["<>])/gi,m=/<(((https?|ftp|dict):\/\/|www\.)[^'">\s]+)()>/gi,n=/(^|\s)(?:mailto:)?([A-Za-z0-9!#$%&'*+-\/=?^_`{|}~.]+@[-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+)(?=$|\s)/gim,o=/<()(?:mailto:)?([-.\w]+@[-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+)>/gi,p=function(a){"use strict";return function(b,c,d,e,f){var g=c,h="",i="";return/^www\./i.test(c)&&(c=c.replace(/^www\./i,"http://www.")),a.excludeTrailingPunctuationFromURLs&&f&&(h=f),a.openLinksInNewWindow&&(i=' target="¨E95Eblank"'),'"+g+""+h}},q=function(a,b){"use strict";return function(c,e,f){var g="mailto:";return e=e||"",f=d.subParser("unescapeSpecialChars")(f,a,b),a.encodeEmails?(g=d.helper.encodeEmailAddress(g+f),f=d.helper.encodeEmailAddress(f)):g+=f,e+''+f+""}};d.subParser("autoLinks",function(a,b,c){"use strict";return a=c.converter._dispatch("autoLinks.before",a,b,c),a=a.replace(m,p(b)),a=a.replace(o,q(b,c)),a=c.converter._dispatch("autoLinks.after",a,b,c)}),d.subParser("simplifiedAutoLinks",function(a,b,c){"use strict";return b.simplifiedAutoLink?(a=c.converter._dispatch("simplifiedAutoLinks.before",a,b,c),a=b.excludeTrailingPunctuationFromURLs?a.replace(l,p(b)):a.replace(k,p(b)),a=a.replace(n,q(b,c)),a=c.converter._dispatch("simplifiedAutoLinks.after",a,b,c)):a}),d.subParser("blockGamut",function(a,b,c){"use strict";return a=c.converter._dispatch("blockGamut.before",a,b,c),a=d.subParser("blockQuotes")(a,b,c),a=d.subParser("headers")(a,b,c),a=d.subParser("horizontalRule")(a,b,c),a=d.subParser("lists")(a,b,c),a=d.subParser("codeBlocks")(a,b,c),a=d.subParser("tables")(a,b,c),a=d.subParser("hashHTMLBlocks")(a,b,c),a=d.subParser("paragraphs")(a,b,c),a=c.converter._dispatch("blockGamut.after",a,b,c)}),d.subParser("blockQuotes",function(a,b,c){"use strict";return a=c.converter._dispatch("blockQuotes.before",a,b,c),a=a.replace(/((^ {0,3}>[ \t]?.+\n(.+\n)*\n*)+)/gm,function(a,e){var f=e;return f=f.replace(/^[ \t]*>[ \t]?/gm,"¨0"),f=f.replace(/¨0/g,""),f=f.replace(/^[ \t]+$/gm,""),f=d.subParser("githubCodeBlocks")(f,b,c),f=d.subParser("blockGamut")(f,b,c),f=f.replace(/(^|\n)/g,"$1 "),f=f.replace(/(\s*
    [^\r]+?<\/pre>)/gm,function(a,b){var c=b;return c=c.replace(/^  /gm,"¨0"),c=c.replace(/¨0/g,"")}),d.subParser("hashBlock")("
    \n"+f+"\n
    ",b,c)}),a=c.converter._dispatch("blockQuotes.after",a,b,c)}),d.subParser("codeBlocks",function(a,b,c){"use strict";a=c.converter._dispatch("codeBlocks.before",a,b,c),a+="¨0";var e=/(?:\n\n|^)((?:(?:[ ]{4}|\t).*\n+)+)(\n*[ ]{0,3}[^ \t\n]|(?=¨0))/g;return a=a.replace(e,function(a,e,f){var g=e,h=f,i="\n";return g=d.subParser("outdent")(g,b,c),g=d.subParser("encodeCode")(g,b,c),g=d.subParser("detab")(g,b,c),g=g.replace(/^\n+/g,""),g=g.replace(/\n+$/g,""),b.omitExtraWLInCodeBlocks&&(i=""),g="
    "+g+i+"
    ",d.subParser("hashBlock")(g,b,c)+h}),a=a.replace(/¨0/,""),a=c.converter._dispatch("codeBlocks.after",a,b,c)}),d.subParser("codeSpans",function(a,b,c){"use strict";return a=c.converter._dispatch("codeSpans.before",a,b,c),void 0===a&&(a=""),a=a.replace(/(^|[^\\])(`+)([^\r]*?[^`])\2(?!`)/gm,function(a,e,f,g){var h=g;return h=h.replace(/^([ \t]*)/g,""),h=h.replace(/[ \t]*$/g,""),h=d.subParser("encodeCode")(h,b,c),e+""+h+""}),a=c.converter._dispatch("codeSpans.after",a,b,c)}),d.subParser("detab",function(a,b,c){"use strict";return a=c.converter._dispatch("detab.before",a,b,c),a=a.replace(/\t(?=\t)/g," "),a=a.replace(/\t/g,"¨A¨B"),a=a.replace(/¨B(.+?)¨A/g,function(a,b){for(var c=b,d=4-c.length%4,e=0;e/g,">"),a=c.converter._dispatch("encodeAmpsAndAngles.after",a,b,c)}),d.subParser("encodeBackslashEscapes",function(a,b,c){"use strict";return a=c.converter._dispatch("encodeBackslashEscapes.before",a,b,c),a=a.replace(/\\(\\)/g,d.helper.escapeCharactersCallback),a=a.replace(/\\([`*_{}\[\]()>#+.!~=|-])/g,d.helper.escapeCharactersCallback),a=c.converter._dispatch("encodeBackslashEscapes.after",a,b,c)}),d.subParser("encodeCode",function(a,b,c){"use strict";return a=c.converter._dispatch("encodeCode.before",a,b,c),a=a.replace(/&/g,"&").replace(//g,">").replace(/([*_{}\[\]\\=~-])/g,d.helper.escapeCharactersCallback),a=c.converter._dispatch("encodeCode.after",a,b,c)}),d.subParser("escapeSpecialCharsWithinTagAttributes",function(a,b,c){"use strict";a=c.converter._dispatch("escapeSpecialCharsWithinTagAttributes.before",a,b,c);var e=/(<[a-z\/!$]("[^"]*"|'[^']*'|[^'">])*>|)/gi;return a=a.replace(e,function(a){return a.replace(/(.)<\/?code>(?=.)/g,"$1`").replace(/([\\`*_~=|])/g,d.helper.escapeCharactersCallback)}),a=c.converter._dispatch("escapeSpecialCharsWithinTagAttributes.after",a,b,c)}),d.subParser("githubCodeBlocks",function(a,b,c){"use strict";return b.ghCodeBlocks?(a=c.converter._dispatch("githubCodeBlocks.before",a,b,c),a+="¨0",a=a.replace(/(?:^|\n)```(.*)\n([\s\S]*?)\n```/g,function(a,e,f){var g=b.omitExtraWLInCodeBlocks?"":"\n";return f=d.subParser("encodeCode")(f,b,c),f=d.subParser("detab")(f,b,c),f=f.replace(/^\n+/g,""),f=f.replace(/\n+$/g,""),f="
    "+f+g+"
    ",f=d.subParser("hashBlock")(f,b,c),"\n\n¨G"+(c.ghCodeBlocks.push({text:a,codeblock:f})-1)+"G\n\n"}),a=a.replace(/¨0/,""),c.converter._dispatch("githubCodeBlocks.after",a,b,c)):a}),d.subParser("hashBlock",function(a,b,c){"use strict";return a=c.converter._dispatch("hashBlock.before",a,b,c),a=a.replace(/(^\n+|\n+$)/g,""),a="\n\n¨K"+(c.gHtmlBlocks.push(a)-1)+"K\n\n",a=c.converter._dispatch("hashBlock.after",a,b,c)}),d.subParser("hashCodeTags",function(a,b,c){"use strict";a=c.converter._dispatch("hashCodeTags.before",a,b,c);var e=function(a,e,f,g){var h=f+d.subParser("encodeCode")(e,b,c)+g;return"¨C"+(c.gHtmlSpans.push(h)-1)+"C"};return a=d.helper.replaceRecursiveRegExp(a,e,"]*>","","gim"),a=c.converter._dispatch("hashCodeTags.after",a,b,c)}),d.subParser("hashElement",function(a,b,c){"use strict";return function(a,b){var d=b;return d=d.replace(/\n\n/g,"\n"),d=d.replace(/^\n/,""),d=d.replace(/\n+$/g,""),d="\n\n¨K"+(c.gHtmlBlocks.push(d)-1)+"K\n\n"}}),d.subParser("hashHTMLBlocks",function(a,b,c){"use strict";a=c.converter._dispatch("hashHTMLBlocks.before",a,b,c);for(var e=["pre","div","h1","h2","h3","h4","h5","h6","blockquote","table","dl","ol","ul","script","noscript","form","fieldset","iframe","math","style","section","header","footer","nav","article","aside","address","audio","canvas","figure","hgroup","output","video","p"],f=function(a,b,d,e){var f=a;return-1!==d.search(/\bmarkdown\b/)&&(f=d+c.converter.makeHtml(b)+e),"\n\n¨K"+(c.gHtmlBlocks.push(f)-1)+"K\n\n"},g=0;g]*>","im"),j="<"+e[g]+"\\b[^>]*>",k="";-1!==(h=d.helper.regexIndexOf(a,i));){var l=d.helper.splitAtIndex(a,h),m=d.helper.replaceRecursiveRegExp(l[1],f,j,k,"im");if(m===l[1])break;a=l[0].concat(m)}return a=a.replace(/(\n {0,3}(<(hr)\b([^<>])*?\/?>)[ \t]*(?=\n{2,}))/g,d.subParser("hashElement")(a,b,c)),a=d.helper.replaceRecursiveRegExp(a,function(a){return"\n\n¨K"+(c.gHtmlBlocks.push(a)-1)+"K\n\n"},"^ {0,3}\x3c!--","--\x3e","gm"),a=a.replace(/(?:\n\n)( {0,3}(?:<([?%])[^\r]*?\2>)[ \t]*(?=\n{2,}))/g,d.subParser("hashElement")(a,b,c)),a=c.converter._dispatch("hashHTMLBlocks.after",a,b,c)}),d.subParser("hashHTMLSpans",function(a,b,c){"use strict";function d(a){return"¨C"+(c.gHtmlSpans.push(a)-1)+"C"}return a=c.converter._dispatch("hashHTMLSpans.before",a,b,c),a=a.replace(/<[^>]+?\/>/gi,function(a){return d(a)}),a=a.replace(/<([^>]+?)>[\s\S]*?<\/\1>/g,function(a){return d(a)}),a=a.replace(/<([^>]+?)\s[^>]+?>[\s\S]*?<\/\1>/g,function(a){return d(a)}),a=a.replace(/<[^>]+?>/gi,function(a){return d(a)}),a=c.converter._dispatch("hashHTMLSpans.after",a,b,c)}),d.subParser("unhashHTMLSpans",function(a,b,c){"use strict";a=c.converter._dispatch("unhashHTMLSpans.before",a,b,c);for(var d=0;d]*>\\s*]*>","^ {0,3}\\s*
    ","gim"),a=c.converter._dispatch("hashPreCodeTags.after",a,b,c)}),d.subParser("headers",function(a,b,c){"use strict";function e(a){var e;if(b.customizedHeaderId){var f=a.match(/\{([^{]+?)}\s*$/);f&&f[1]&&(a=f[1])}return e=d.helper.isString(b.prefixHeaderId)?b.prefixHeaderId+a:!0===b.prefixHeaderId?"section "+a:a,e=g?e.replace(/ /g,"-").replace(/&/g,"").replace(/¨T/g,"").replace(/¨D/g,"").replace(/[&+$,\/:;=?@"#{}|^¨~\[\]`\\*)(%.!'<>]/g,"").toLowerCase():e.replace(/[^\w]/g,"").toLowerCase(),c.hashLinkCounts[e]?e=e+"-"+c.hashLinkCounts[e]++:c.hashLinkCounts[e]=1,e}a=c.converter._dispatch("headers.before",a,b,c);var f=isNaN(parseInt(b.headerLevelStart))?1:parseInt(b.headerLevelStart),g=b.ghCompatibleHeaderId,h=b.smoothLivePreview?/^(.+)[ \t]*\n={2,}[ \t]*\n+/gm:/^(.+)[ \t]*\n=+[ \t]*\n+/gm,i=b.smoothLivePreview?/^(.+)[ \t]*\n-{2,}[ \t]*\n+/gm:/^(.+)[ \t]*\n-+[ \t]*\n+/gm;a=a.replace(h,function(a,g){var h=d.subParser("spanGamut")(g,b,c),i=b.noHeaderId?"":' id="'+e(g)+'"',j=f,k=""+h+"";return d.subParser("hashBlock")(k,b,c)}),a=a.replace(i,function(a,g){var h=d.subParser("spanGamut")(g,b,c),i=b.noHeaderId?"":' id="'+e(g)+'"',j=f+1,k=""+h+"";return d.subParser("hashBlock")(k,b,c)});var j=b.requireSpaceBeforeHeadingText?/^(#{1,6})[ \t]+(.+?)[ \t]*#*\n+/gm:/^(#{1,6})[ \t]*(.+?)[ \t]*#*\n+/gm;return a=a.replace(j,function(a,g,h){var i=h;b.customizedHeaderId&&(i=h.replace(/\s?\{([^{]+?)}\s*$/,""));var j=d.subParser("spanGamut")(i,b,c),k=b.noHeaderId?"":' id="'+e(h)+'"',l=f-1+g.length,m=""+j+"";return d.subParser("hashBlock")(m,b,c)}),a=c.converter._dispatch("headers.after",a,b,c)}),d.subParser("horizontalRule",function(a,b,c){"use strict";a=c.converter._dispatch("horizontalRule.before",a,b,c);var e=d.subParser("hashBlock")("
    ",b,c);return a=a.replace(/^ {0,2}( ?-){3,}[ \t]*$/gm,e),a=a.replace(/^ {0,2}( ?\*){3,}[ \t]*$/gm,e),a=a.replace(/^ {0,2}( ?_){3,}[ \t]*$/gm,e),a=c.converter._dispatch("horizontalRule.after",a,b,c)}),d.subParser("images",function(a,b,c){"use strict";function e(a,b,e,f,g,h,i,j){var k=c.gUrls,l=c.gTitles,m=c.gDimensions;if(e=e.toLowerCase(),j||(j=""),a.search(/\(? ?(['"].*['"])?\)$/m)>-1)f="";else if(""===f||null===f){if(""!==e&&null!==e||(e=b.toLowerCase().replace(/ ?\n/g," ")),f="#"+e,d.helper.isUndefined(k[e]))return a;f=k[e],d.helper.isUndefined(l[e])||(j=l[e]),d.helper.isUndefined(m[e])||(g=m[e].width,h=m[e].height)}b=b.replace(/"/g,""").replace(d.helper.regexes.asteriskAndDash,d.helper.escapeCharactersCallback),f=f.replace(d.helper.regexes.asteriskAndDash,d.helper.escapeCharactersCallback);var n=''+b+'?(?: =([*\d]+[A-Za-z%]{0,4})x([*\d]+[A-Za-z%]{0,4}))?[ \t]*(?:(["'])([^"]*?)\6)?[ \t]?\)/g,g=/!\[([^\]]*?)][ \t]*()\([ \t]?<([^>]*)>(?: =([*\d]+[A-Za-z%]{0,4})x([*\d]+[A-Za-z%]{0,4}))?[ \t]*(?:(?:(["'])([^"]*?)\6))?[ \t]?\)/g,h=/!\[([^\]]*?)] ?(?:\n *)?\[(.*?)]()()()()()/g,i=/!\[([^\[\]]+)]()()()()()/g;return a=a.replace(h,e),a=a.replace(g,e),a=a.replace(f,e),a=a.replace(i,e),a=c.converter._dispatch("images.after",a,b,c)}),d.subParser("italicsAndBold",function(a,b,c){"use strict";function e(a,e,f){return b.simplifiedAutoLink&&(a=d.subParser("simplifiedAutoLinks")(a,b,c)),e+a+f}return a=c.converter._dispatch("italicsAndBold.before",a,b,c),b.literalMidWordUnderscores?(a=a.replace(/\b___(\S[\s\S]*)___\b/g,function(a,b){return e(b,"","")}),a=a.replace(/\b__(\S[\s\S]*)__\b/g,function(a,b){return e(b,"","")}),a=a.replace(/\b_(\S[\s\S]*?)_\b/g,function(a,b){return e(b,"","")})):(a=a.replace(/___(\S[\s\S]*?)___/g,function(a,b){return/\S$/.test(b)?e(b,"",""):a}),a=a.replace(/__(\S[\s\S]*?)__/g,function(a,b){return/\S$/.test(b)?e(b,"",""):a}),a=a.replace(/_([^\s_][\s\S]*?)_/g,function(a,b){return/\S$/.test(b)?e(b,"",""):a})),b.literalMidWordAsterisks?(a=a.trim().replace(/(?:^| +)\*{3}(\S[\s\S]*?)\*{3}(?: +|$)/g,function(a,b){return e(b," "," ")}),a=a.trim().replace(/(?:^| +)\*{2}(\S[\s\S]*?)\*{2}(?: +|$)/g,function(a,b){return e(b," "," ")}),a=a.trim().replace(/(?:^| +)\*{1}(\S[\s\S]*?)\*{1}(?: +|$)/g,function(a,b){return e(b," ",""+(" "===a.slice(-1)?" ":""))})):(a=a.replace(/\*\*\*(\S[\s\S]*?)\*\*\*/g,function(a,b){return/\S$/.test(b)?e(b,"",""):a}),a=a.replace(/\*\*(\S[\s\S]*?)\*\*/g,function(a,b){return/\S$/.test(b)?e(b,"",""):a}),a=a.replace(/\*([^\s*][\s\S]*?)\*/g,function(a,b){return/\S$/.test(b)?e(b,"",""):a})),a=c.converter._dispatch("italicsAndBold.after",a,b,c)}),d.subParser("lists",function(a,b,c){"use strict";function e(a,e){c.gListLevel++,a=a.replace(/\n{2,}$/,"\n"),a+="¨0";var f=/(\n)?(^ {0,3})([*+-]|\d+[.])[ \t]+((\[(x|X| )?])?[ \t]*[^\r]+?(\n{1,2}))(?=\n*(¨0| {0,3}([*+-]|\d+[.])[ \t]+))/gm,g=/\n[ \t]*\n(?!¨0)/.test(a);return b.disableForced4SpacesIndentedSublists&&(f=/(\n)?(^ {0,3})([*+-]|\d+[.])[ \t]+((\[(x|X| )?])?[ \t]*[^\r]+?(\n{1,2}))(?=\n*(¨0|\2([*+-]|\d+[.])[ \t]+))/gm),a=a.replace(f,function(a,e,f,h,i,j,k){k=k&&""!==k.trim();var l=d.subParser("outdent")(i,b,c),m="";return j&&b.tasklists&&(m=' class="task-list-item" style="list-style-type: none;"',l=l.replace(/^[ \t]*\[(x|X| )?]/m,function(){var a='-1?(l=d.subParser("githubCodeBlocks")(l,b,c),l=d.subParser("blockGamut")(l,b,c)):(l=d.subParser("lists")(l,b,c),l=l.replace(/\n$/,""),l=d.subParser("hashHTMLBlocks")(l,b,c),l=l.replace(/\n\n+/g,"\n\n"),l=l.replace(/\n\n/g,"¨B"),l=g?d.subParser("paragraphs")(l,b,c):d.subParser("spanGamut")(l,b,c),l=l.replace(/¨B/g,"\n\n")),l=l.replace("¨A",""),l=""+l+"\n"}),a=a.replace(/¨0/g,""),c.gListLevel--,e&&(a=a.replace(/\s+$/,"")),a}function f(a,c,d){var f=b.disableForced4SpacesIndentedSublists?/^ ?\d+\.[ \t]/gm:/^ {0,3}\d+\.[ \t]/gm,g=b.disableForced4SpacesIndentedSublists?/^ ?[*+-][ \t]/gm:/^ {0,3}[*+-][ \t]/gm,h="ul"===c?f:g,i="";return-1!==a.search(h)?function a(b){var j=b.search(h);-1!==j?(i+="\n<"+c+">\n"+e(b.slice(0,j),!!d)+"\n",c="ul"===c?"ol":"ul",h="ul"===c?f:g,a(b.slice(j))):i+="\n<"+c+">\n"+e(b,!!d)+"\n"}(a):i="\n<"+c+">\n"+e(a,!!d)+"\n",i} -return a=c.converter._dispatch("lists.before",a,b,c),a+="¨0",a=c.gListLevel?a.replace(/^(( {0,3}([*+-]|\d+[.])[ \t]+)[^\r]+?(¨0|\n{2,}(?=\S)(?![ \t]*(?:[*+-]|\d+[.])[ \t]+)))/gm,function(a,b,c){return f(b,c.search(/[*+-]/g)>-1?"ul":"ol",!0)}):a.replace(/(\n\n|^\n?)(( {0,3}([*+-]|\d+[.])[ \t]+)[^\r]+?(¨0|\n{2,}(?=\S)(?![ \t]*(?:[*+-]|\d+[.])[ \t]+)))/gm,function(a,b,c,d){return f(c,d.search(/[*+-]/g)>-1?"ul":"ol",!1)}),a=a.replace(/¨0/,""),a=c.converter._dispatch("lists.after",a,b,c)}),d.subParser("outdent",function(a,b,c){"use strict";return a=c.converter._dispatch("outdent.before",a,b,c),a=a.replace(/^(\t|[ ]{1,4})/gm,"¨0"),a=a.replace(/¨0/g,""),a=c.converter._dispatch("outdent.after",a,b,c)}),d.subParser("paragraphs",function(a,b,c){"use strict";a=c.converter._dispatch("paragraphs.before",a,b,c),a=a.replace(/^\n+/g,""),a=a.replace(/\n+$/g,"");for(var e=a.split(/\n{2,}/g),f=[],g=e.length,h=0;h=0?f.push(i):i.search(/\S/)>=0&&(i=d.subParser("spanGamut")(i,b,c),i=i.replace(/^([ \t]*)/g,"

    "),i+="

    ",f.push(i))}for(g=f.length,h=0;h]*>\s*]*>/.test(k)&&(l=!0)}f[h]=k}return a=f.join("\n"),a=a.replace(/^\n+/g,""),a=a.replace(/\n+$/g,""),c.converter._dispatch("paragraphs.after",a,b,c)}),d.subParser("runExtension",function(a,b,c,d){"use strict";if(a.filter)b=a.filter(b,d.converter,c);else if(a.regex){var e=a.regex;e instanceof RegExp||(e=new RegExp(e,"g")),b=b.replace(e,a.replace)}return b}),d.subParser("spanGamut",function(a,b,c){"use strict";return a=c.converter._dispatch("spanGamut.before",a,b,c),a=d.subParser("codeSpans")(a,b,c),a=d.subParser("escapeSpecialCharsWithinTagAttributes")(a,b,c),a=d.subParser("encodeBackslashEscapes")(a,b,c),a=d.subParser("images")(a,b,c),a=d.subParser("anchors")(a,b,c),a=d.subParser("autoLinks")(a,b,c),a=d.subParser("italicsAndBold")(a,b,c),a=d.subParser("strikethrough")(a,b,c),a=d.subParser("simplifiedAutoLinks")(a,b,c),a=d.subParser("hashHTMLSpans")(a,b,c),a=d.subParser("encodeAmpsAndAngles")(a,b,c),a=b.simpleLineBreaks?a.replace(/\n/g,"
    \n"):a.replace(/ +\n/g,"
    \n"),a=c.converter._dispatch("spanGamut.after",a,b,c)}),d.subParser("strikethrough",function(a,b,c){"use strict";function e(a){return b.simplifiedAutoLink&&(a=d.subParser("simplifiedAutoLinks")(a,b,c)),""+a+""}return b.strikethrough&&(a=c.converter._dispatch("strikethrough.before",a,b,c),a=a.replace(/(?:~){2}([\s\S]+?)(?:~){2}/g,function(a,b){return e(b)}),a=c.converter._dispatch("strikethrough.after",a,b,c)),a}),d.subParser("stripLinkDefinitions",function(a,b,c){"use strict";var e=/^ {0,3}\[(.+)]:[ \t]*\n?[ \t]*\s]+)>?(?: =([*\d]+[A-Za-z%]{0,4})x([*\d]+[A-Za-z%]{0,4}))?[ \t]*\n?[ \t]*(?:(\n*)["|'(](.+?)["|')][ \t]*)?(?:\n+|(?=¨0))/gm;return a+="¨0",a=a.replace(e,function(a,e,f,g,h,i,j){return e=e.toLowerCase(),c.gUrls[e]=d.subParser("encodeAmpsAndAngles")(f,b,c),i?i+j:(j&&(c.gTitles[e]=j.replace(/"|'/g,""")),b.parseImgDimensions&&g&&h&&(c.gDimensions[e]={width:g,height:h}),"")}),a=a.replace(/¨0/,"")}),d.subParser("tables",function(a,b,c){"use strict";function e(a){return/^:[ \t]*--*$/.test(a)?' style="text-align:left;"':/^--*[ \t]*:[ \t]*$/.test(a)?' style="text-align:right;"':/^:[ \t]*--*[ \t]*:$/.test(a)?' style="text-align:center;"':""}function f(a,e){var f="";return a=a.trim(),b.tableHeaderId&&(f=' id="'+a.replace(/ /g,"_").toLowerCase()+'"'),a=d.subParser("spanGamut")(a,b,c),""+a+"\n"}function g(a,e){return""+d.subParser("spanGamut")(a,b,c)+"\n"}function h(a,b){for(var c="\n\n\n",d=a.length,e=0;e\n\n\n",e=0;e\n";for(var f=0;f\n"}return c+="\n
    \n"}if(!b.tables)return a;var i=/^ {0,3}\|?.+\|.+\n[ \t]{0,3}\|?[ \t]*:?[ \t]*(?:-|=){2,}[ \t]*:?[ \t]*\|[ \t]*:?[ \t]*(?:-|=){2,}[\s\S]+?(?:\n\n|¨0)/gm;return a=c.converter._dispatch("tables.before",a,b,c),a=a.replace(/\\(\|)/g,d.helper.escapeCharactersCallback),a=a.replace(i,function(a){var b,c=a.split("\n");for(b=0;b return?", "What does /cluster/v2/// do?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/cluster-v2.html-node-state", "fields": {"questions": {"assign": ["What are the states of nodes?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/cluster-v2.html-types", "fields": {"questions": {"assign": ["What are unit states?", "What is the state-disk?", "What is the state-user?", "What is the state-generated?", "What is a group-spec?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/cluster-v2.html-request-parameters", "fields": {"questions": {"assign": ["What is the recursive parameter?", "How do I use recursive mode?", "What does recursive=1 do?", "What does recursive=2 do?", "What happens in recursive mode?", "How is the url-path element replaced?", "What is the spec below?", "How does recursive impact node requests?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/cluster-v2.html-response-format", "fields": {"questions": {"assign": ["What is the response format?", "What are the fields in response?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/clustercontroller-metrics-reference.html-", "fields": {"questions": {"assign": ["What is cluster-controller.down.count?", "What is cluster-controller.busy-tick-time-ms?", "What is cluster-controller.is-master?", "What is the unit of cluster-controller.resource_usage.memory_limit?", "What is the unit of the reindexing progress?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/component-reference.html-", "fields": {"questions": {"assign": ["What is a component?", "How components are configured?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/component-reference.html-component-types", "fields": {"questions": {"assign": ["What is a Binding?", "What is a Server binding?", "What is a Client binding?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/component-reference.html-component-configurations", "fields": {"questions": {"assign": ["What is the ProcessingHandler in Vespa?", "What is the role of SearchHandler in Vespa?", "How does a DocumentProcessingHandler work in Vespa?", "What is the recommended way to build a custom HTTP API in Vespa?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/component-reference.html-injectable-components", "fields": {"questions": {"assign": ["What is ConfigInstance used for?", "What is DocumentAccess?", "What is ModelsEvaluator used for?", "What is BertBaseEmbedder?", "What is the VipStatus used for?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/component-reference.html-component-versioning", "fields": {"questions": {"assign": ["What can be versioned in Vespa?", "What are artifact versions like?", "How are versions referred to?", "What does this document explain?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/component-reference.html-format", "fields": {"questions": {"assign": ["What is the format of a version?", "What is a qualifier?", "What is the file name format?", "What are the integers in a version?", "When is a dash used in a file name?", "How is a version appended to an id?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/component-reference.html-ordering", "fields": {"questions": {"assign": ["How are versions ordered?", "What is a lexical ordering?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/component-reference.html-referencing-a-versioned-component", "fields": {"questions": {"assign": ["What is component referencing?", "What is version specification?", "How are references resolved?", "What is the highest version without a qualifier?", "What is the rule for partially or full version specification?", "What are the versions with qualifiers matched by?", "How does a reference resolve to test qualified version?", "What kind references will not resolve?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/component-reference.html-merging-specifications-for-chained-components", "fields": {"questions": {"assign": ["What is the need for merging references?", "What are compatible version references?", "What are conflicting version references?"]}}} -{"update": "id:open-p:paragraph::open/en/concrete-documents.html-", "fields": {"questions": {"assign": ["What is setFieldValue() used for?", "What is getFieldValue() used for?", "How to generate a concrete document type?", "What is a Document subclass?"]}}} -{"update": "id:open-p:paragraph::open/en/concrete-documents.html-inheritance", "fields": {"questions": {"assign": ["What is single inheritance?", "What happens with multiple inheritance?", "What is the Java type for myDoc?", "Where can I find inheritance examples in the documentation?"]}}} -{"update": "id:open-p:paragraph::open/en/concrete-documents.html-feeding", "fields": {"questions": {"assign": ["What are concrete types used for?", "How to specify document type?"]}}} -{"update": "id:open-p:paragraph::open/en/concrete-documents.html-factory-and-copy-constructor", "fields": {"questions": {"assign": ["What is ConcreteDocumentFactory?", "How is ConcreteDocumentFactory initialized?", "What is the use of ConcreteDocumentFactory?", "What are copy constructors in Vespa?", "What happens when getFieldValue() is called on concrete types?", "How are accessor and mutator methods used on concrete types?", "What is the syntax for initializing an object with ConcreteDocumentFactory?", "How does setFieldValue() work on concrete types?"]}}} -{"update": "id:open-p:paragraph::open/en/concrete-documents.html-document-processing", "fields": {"questions": {"assign": ["What is a concrete document type?", "What does DocumentPut do?", "What is the Progress class?", "Are concrete document types supported for updates?", "What is an example of a concrete document type?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/config-files.html-", "fields": {"questions": {"assign": ["What is a config file?", "What is a configurable component?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/config-files.html-config-definition-files", "fields": {"questions": {"assign": ["What is a config definition?", "What is the file suffix for config definition?", "Where are Vespa's builtin *.def* files located?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/config-files.html-package", "fields": {"questions": {"assign": ["What is a package?", "Where should the package statement be placed?", "Why is it recommended to use a separate package for each bundle?", "Who should use separate package for each bundle?", "How to define a package?", "What characters can be used in a package name?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/config-files.html-parameter-names", "fields": {"questions": {"assign": ["What is a config definition file?", "What is the recommended parameter name style?", "What does a parameter definition have?", "What is the purpose of [range=[min,max]]?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/config-files.html-parameter-types", "fields": {"questions": {"assign": ["What is an enum?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/config-files.html-structs", "fields": {"questions": {"assign": ["What is a struct in Vespa?", "How do you declare a struct?", "What is the syntax for declaring a struct?", "What are the benefits of using a struct?", "What is the purpose of a struct?", "What can be included in a Vespa struct?", "How does Vespa handle structs?", "What is the basic syntax for declaring a Vespa struct?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/config-files.html-arrays", "fields": {"questions": {"assign": ["What is an array?", "How are arrays declared?", "What can arrays contain?", "Can arrays have default values?", "What is the structure of a complex array?", "What is not allowed in an array?", "What is the difference between an array with simple values and one with children?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/config-files.html-maps", "fields": {"questions": {"assign": ["How are maps declared in Vespa?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/config-files.html-generic-configuration-in-services.xml", "fields": {"questions": {"assign": ["What are four types of elements in services.xml?", "What is a dedicated config element?", "What is a generic config element?", "What is the priority of config settings in services.xml?", "What is the syntax for setting values for a config in services.xml?", "What happens if a value is not set for a parameter in services.xml?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/config-files.html-configuring-arrays", "fields": {"questions": {"assign": ["How to assign values to arrays?", "What is the `` element?"]}}} -{"update": "id:open-p:paragraph::open/en/config-introduction.html-", "fields": {"questions": {"assign": ["What is Vespa Configuration?", "What are the three parts of node configuration?", "What is the nature of the interface between delivery and assembly subsystems?", "What is configuration delivery?", "What is configuration assembly?", "How does Vespa Configuration manage node variations?", "What are the tool provided for package management?"]}}} -{"update": "id:open-p:paragraph::open/en/config-introduction.html-configuration-assembly", "fields": {"questions": {"assign": ["What is config assembly?", "What does config assembly involve?", "What is the role of Vespa operations in config assembly?"]}}} -{"update": "id:open-p:paragraph::open/en/config-introduction.html-configuration-delivery", "fields": {"questions": {"assign": ["What is configuration delivery?", "What are the goals of configuration delivery?", "What are the aspects of configuration delivery?"]}}} -{"update": "id:open-p:paragraph::open/en/config-introduction.html-configuration-definitions", "fields": {"questions": {"assign": ["What is a configuration?", "What is a type definition?", "What is a namespace?", "What is a nested configuration?", "What does a type definition define?", "What is the syntax for a configuration object?"]}}} -{"update": "id:open-p:paragraph::open/en/config-introduction.html-documentation-of-this-key", "fields": {"questions": {"assign": ["What is myProperty?"]}}} -{"update": "id:open-p:paragraph::open/en/config-introduction.html-component-view", "fields": {"questions": {"assign": ["How do components request configs?"]}}} -{"update": "id:open-p:paragraph::open/en/config-introduction.html-delivery-mechanism", "fields": {"questions": {"assign": ["What is the config delivery mechanism responsible for?", "What identifies a config subscription?", "What is a config proxy?", "How does the proxy resolve config requests?", "What is the failover implementation for config subscriptions?", "What is the benefit of not storing configs locally on nodes?", "What is the url config type used for?"]}}} -{"update": "id:open-p:paragraph::open/en/config-introduction.html-bootstrapping", "fields": {"questions": {"assign": ["What is config-sentinel process?", "What services are maintained by config-sentinel?", "What role does config-sentinel play?", "How does config-sentinel start services?"]}}} -{"update": "id:open-p:paragraph::open/en/config-introduction.html-system-upgrades", "fields": {"questions": {"assign": ["What happens during minor upgrades?", "What is a major upgrade?", "What is the procedure for major upgrades?"]}}} -{"update": "id:open-p:paragraph::open/en/config-introduction.html-notes", "fields": {"questions": {"assign": ["What are the assumptions for nodes using Vespa Configuration?", "What is the purpose of configuration server operations?", "Is reading this document necessary for using Vespa?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/config-proxy.html-", "fields": {"questions": {"assign": ["What is a config proxy?", "How does the config proxy work?", "What is the default mode of the config proxy?", "What is memorycache mode?", "How can I interface with the config proxy?", "What is vespa-configproxy-cmd?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/config-proxy.html-detaching-from-config-servers", "fields": {"questions": {"assign": ["What is vespa-configproxy-cmd?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/config-proxy.html-inspecting-config", "fields": {"questions": {"assign": ["What is the active config generation?", "What is the config definition name?", "What is the config id used?", "What tool can be used to find the relevant config information?", "How is config generation determined?", "What is vespa-get-config used for?", "When is a restart required for some config changes?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/config-rest-api-v2.html-", "fields": {"questions": {"assign": ["What is the Vespa REST API?", "What does the Config API do?", "What is the current API version?", "What is the API port number?", "How do I find config server hosts?", "When is the API available?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/config-rest-api-v2.html-the-application-id", "fields": {"questions": {"assign": ["What is an application id?", "What is a tuple?", "What is the default shorthand?", "What does the API provide?", "What is the 'full' form of the application id?", "What information is included in the full application id?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/config-rest-api-v2.html-get-/config/v2/tenant/mytenant/application/myapplication/", "fields": {"questions": {"assign": ["What is default recursive parameter?", "What is recursive set to true?", "What does the children array include?", "What does the configs array include?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/config-rest-api-v2.html-get-/config/v2/tenant/mytenant/application/myapplication/[namespace.name]/", "fields": {"questions": {"assign": ["What does the vespaclient.config.feeder manage?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/config-rest-api-v2.html-get-/config/v2/tenant/mytenant/application/myapplication/[namespace.name]/[config/id]", "fields": {"questions": {"assign": ["What does the config payload include?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/config-sentinel.html-", "fields": {"questions": {"assign": ["What is config sentinel?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/config-sentinel.html-user-interface", "fields": {"questions": {"assign": ["What does the config sentinel do?", "How can the config sentinel be used?", "What is vespa-sentinel-cmd?", "How do you list services running on a node?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/config-sentinel.html-cluster-startup", "fields": {"questions": {"assign": ["What is config sentinel?", "What is minOkPercent?", "What is maxBadCount?", "What is connectivity?", "What is the recommended value for maxBadCount?", "What is the recommended value for minOkPercent?", "What is the connectivity check details?"]}}} -{"update": "id:open-p:paragraph::open/en/contributing/configapi-dev-cpp.html-", "fields": {"questions": {"assign": ["What is the C++ cloud config API?", "What is a config definition file?", "How do I generate C++ code?", "What do I need to subscribe to using the API?"]}}} -{"update": "id:open-p:paragraph::open/en/contributing/configapi-dev-cpp.html-generating-config", "fields": {"questions": {"assign": ["What is config-defs/motd.def?", "What is make-config.pl?", "What is generated by make-config.pl?"]}}} -{"update": "id:open-p:paragraph::open/en/contributing/configapi-dev-cpp.html-subscribing-and-getting-config", "fields": {"questions": {"assign": ["What is a ConfigSubscriber?", "What is the config API?", "What does the subscribe method do?", "What happens if ConfigSubscriber is unable to subscribe?", "What headers must be included to use the API?"]}}} -{"update": "id:open-p:paragraph::open/en/contributing/configapi-dev-cpp.html-include-", "fields": {"questions": {"assign": ["What is the ConfigSubscriber?", "What is nextConfig()?", "What does handle->getConfig() return?", "Is ConfigSubscriber thread-safe?", "How long does nextConfig() wait?", "What is the format of subscribe()?", "What happens if nextConfig() times out?"]}}} -{"update": "id:open-p:paragraph::open/en/contributing/configapi-dev-cpp.html-selecting-config-source", "fields": {"questions": {"assign": ["What is the purpose of ConfigSubscriber?", "What is a ConfigContext used for?", "What are the different spec types?"]}}} -{"update": "id:open-p:paragraph::open/en/contributing/configapi-dev-cpp.html-unit-testing", "fields": {"questions": {"assign": ["What is a builder type?", "What is a ConfigSet?", "How to create a context?", "What is ConfigHandle?", "What is ConfigGetter?", "How to test the config?", "What does id1 represents?"]}}} -{"update": "id:open-p:paragraph::open/en/contributing/configapi-dev-cpp.html-printing-config", "fields": {"questions": {"assign": ["How do you print a config?", "What is ConfigWriter?", "What is a ConfigFormatter?", "What is FileConfigWriter?", "What is OstreamConfigWriter?", "What is FileConfigFormatter?", "What is JsonConfigFormatter?", "How to write a config to JSON file?"]}}} -{"update": "id:open-p:paragraph::open/en/contributing/configapi-dev-java.html-", "fields": {"questions": {"assign": ["What is a def file?", "How to generate config classes?", "Where are the generated classes saved?", "What is an example def-file used?", "What class is generated?", "What is a subtype of ConfigInstance?", "How to pull in config system?"]}}} -{"update": "id:open-p:paragraph::open/en/contributing/configapi-dev-java.html-subscribing-and-getting-config", "fields": {"questions": {"assign": ["What is a ConfigSubscriber?", "How to retrieve config in application?", "What does nextConfig() do?", "How to handle errors?", "What is the second parameter to subscribe()?", "How does isChanged() work?", "How to subscribe to multiple configs?"]}}} -{"update": "id:open-p:paragraph::open/en/contributing/configapi-dev-java.html-simplified-subscription", "fields": {"questions": {"assign": ["What is ConfigSubscriber.SingleSubscriber interface?", "How do you define configure() method?", "What does subscribe() method do?", "What happens if initial configuration fails?", "What is the disadvantage of using ConfigSubscriber.SingleSubscriber?", "What is the generic method to track config changes?", "What is MotdConfig?", "What is the purpose of MyConfigSubscriber class?"]}}} -{"update": "id:open-p:paragraph::open/en/contributing/configapi-dev-java.html-unit-testing-config", "fields": {"questions": {"assign": ["What is a ConfigSubscriber?", "What is a ConfigSource?", "What is a ConfigSet?", "What is a Builder?", "What is the reload() method for ConfigSubscriber used for?", "What is the difference between Builder and ConfigInstance?", "Where can I find a full example of ConfigSet Subscription?"]}}} -{"update": "id:open-p:paragraph::open/en/contributing/configapi-dev.html-", "fields": {"questions": {"assign": ["What is the config API?", "How do I use the C++ version of the config API?", "How do I use the Java version of the config API?", "Is reading this document necessary for using Vespa?", "What is the purpose of the Vespa container?", "What is configurable message?"]}}} -{"update": "id:open-p:paragraph::open/en/contributing/configapi-dev.html-creating-a-config-definition", "fields": {"questions": {"assign": ["What is config API?", "How to define a config?", "What is motd.def?"]}}} -{"update": "id:open-p:paragraph::open/en/contributing/configapi-dev.html-generating-source-code-and-accessing-config-in-code", "fields": {"questions": {"assign": ["How do I generate config code?", "What languages have API code?"]}}} -{"update": "id:open-p:paragraph::open/en/contributing/configapi-dev.html-config-id", "fields": {"questions": {"assign": ["What is a config id?", "What is a config producer instance?", "What is the config object model?", "How is the final config instance produced?", "What is VESPA_CONFIG_ID used for?", "Can multiple services have the same config id?", "What is the config sentinel?", "What is the config id for services that are not part of the model?"]}}} -{"update": "id:open-p:paragraph::open/en/contributing/configapi-dev.html-schema-compatibility-rules", "fields": {"questions": {"assign": ["What is a schema incompatibility?", "What happens when C is missing a config value that S has?", "What happens when C has an additional config value with a default value?", "What happens when C has an additional config value without a default value?", "What happens when C has the type of a config value changed?", "What is the system's behavior with compatible changes?", "What is the system's behavior with incompatible changes?", "Why should one prefer creating a new config name instead of removing a config value?"]}}} -{"update": "id:open-p:paragraph::open/en/contributing/configapi-dev.html-the-config-server-and-object-model", "fields": {"questions": {"assign": ["What is the server's object model?", "How do I create a model plugin?", "What is the purpose of config id mappings?", "Is the object model pluggable?", "What is Developing Cloud Config Model plugins about?"]}}} -{"update": "id:open-p:paragraph::open/en/contributing/configapi-dev.html-creating-a-deployable-application-package", "fields": {"questions": {"assign": ["What is the purpose of hosts.xml?"]}}} -{"update": "id:open-p:paragraph::open/en/contributing/configapi-dev.html-setting-up-a-running-system", "fields": {"questions": {"assign": ["What is the cloudconfig package?"]}}} -{"update": "id:open-p:paragraph::open/en/contributing/configapi-dev.html-advanced-usage-of-the-config-api", "fields": {"questions": {"assign": ["Why split config settings?", "What are independent static configs?", "What are dependent static configs?", "What are dependent dynamic configs?", "How do you cope with different scenarios?"]}}} -{"update": "id:open-p:paragraph::open/en/contributing/configapi-dev.html-independent-static-configs", "fields": {"questions": {"assign": ["What are independent configs?", "What is ConfigSubscriber?", "What is recommended for config management?", "How to retrieve configs using ConfigHandle?", "Is it recommended to use a separate ConfigSubscriber for each config?", "What is the retrieval part of the config check?", "Can the config fetch part be performed in a separate thread?"]}}} -{"update": "id:open-p:paragraph::open/en/contributing/configapi-dev.html-dependent-static-configs", "fields": {"questions": {"assign": ["What are dependent configs?", "How do you approach dependent configs?", "What is the recommended way to use dependent configs?", "What is generation in context of dependent configs?", "How do you ensure dependent and derived configs are of same generation?"]}}} -{"update": "id:open-p:paragraph::open/en/contributing/configapi-dev.html-dependent-dynamic-configs", "fields": {"questions": {"assign": ["What is ConfigRetriever?", "What are bootstrap configs?", "How to use ConfigRetriever?"]}}} -{"update": "id:open-p:paragraph::open/en/contributing/configapi-dev.html-advice-on-config-modelling", "fields": {"questions": {"assign": ["What is ConfigSubscriber?", "Why fetch all configs at once?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/configserver-metrics-reference.html-", "fields": {"questions": {"assign": ["What is configserver?", "What is configserver.requests?", "What is the unit of configserver.latency?", "What is configserver.activeSessions?", "What is configserver.rpcServerWorkQueueSize?", "What is configserver.zkMaxLatency?", "What is configserver.zkConnections?", "What is the purpose of configserver.cacheChecksumElems?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/configuration-server.html-", "fields": {"questions": {"assign": ["What are Vespa Configuration Servers?", "What is the role of config servers?", "How is Vespa configuration set up?", "What is Apache ZooKeeper used for?", "Where can I find practical examples of multi-configserver configuration?", "Can Vespa be configured without config servers?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/configuration-server.html-status-and-config-generation", "fields": {"questions": {"assign": ["How can I check the health of a config server?", "What is the URL to check config server health?", "What is a config server?", "How does the config server work?", "What is config generation 0?", "What is the URL to check config server config?", "Can I change the config server through application deployment?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/configuration-server.html-redundancy", "fields": {"questions": {"assign": ["What is the config proxy?", "How many nodes are needed for fault-tolerant system?", "What happens when the config server goes down?", "What is minimun number of nodes for fault-tolerant config?", "What is admin model reference?", "Where are config servers defined?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/configuration-server.html-start-sequence", "fields": {"questions": {"assign": ["How to start a Vespa application instance?", "How to start config servers?", "How to start Vespa nodes?", "How to verify config server health?", "What is the Vespa start sequence?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/configuration-server.html-scaling-up", "fields": {"questions": {"assign": ["How to add a config server node?", "What is ZooKeeper configuration?", "What is recommended when adding config server nodes?", "How to install Vespa on config server node?", "What files need to be updated when adding a config server node?", "How to prepare and activate Vespa deploy after adding config server node?", "How to start using the new config servers?", "Will the config servers automatically redistribute application data to new nodes?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/configuration-server.html-scaling-down", "fields": {"questions": {"assign": ["What is scaling down?", "How to remove config servers?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/configuration-server.html-replacing-nodes", "fields": {"questions": {"assign": ["What is the process to replace a node?", "Can you replace multiple nodes at once?", "How do you scale up with one config server?", "What happens if you replace all nodes at once?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/configuration-server.html-tools", "fields": {"questions": {"assign": ["What is vespa-configproxy-cmd?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/configuration-server.html-zookeeper", "fields": {"questions": {"assign": ["What is ZooKeeper?", "What does ZooKeeper do?", "Where is application data stored?", "What is vespa-zkcli?", "How to inspect ZooKeeper state?", "Where does ZooKeeper store data?", "What is the role of ZooKeeper in vespa-deploy?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/configuration-server.html-zookeeper-configuration", "fields": {"questions": {"assign": ["What is ZooKeeper cluster?", "Where is zookeeper.cfg written?", "What happens when VESPA_CONFIGSERVERS change?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/configuration-server.html-zookeeper-recovery", "fields": {"questions": {"assign": ["What is the recovery procedure?", "What causes data corruption?", "How to run cluster-controllers in standalone mode?", "What is vespa-stop-configserver?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/configuration-server.html-zookeeper-barrier-timeout", "fields": {"questions": {"assign": ["What causes timeout during deploy?", "How to work around config server timeout?", "What is VESPA_CONFIGSERVER_ZOOKEEPER_BARRIER_TIMEOUT set to?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/configuration-server.html-configuration", "fields": {"questions": {"assign": ["Which environment variable to use?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/configuration-server.html-system-requirements", "fields": {"questions": {"assign": ["What is the minimum JVM heap size?", "What is the maximum JVM heap size?", "Can the maximum heap size be changed?", "Is disk space required for the transaction log?", "What happens to the old items in the transaction log?", "How will a server with a lot of disk I/O affect performance?", "Is running on a server with a lot of disk I/O recommended?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/configuration-server.html-ports", "fields": {"questions": {"assign": ["How to change config server RPC port?", "Where to change HTTP port?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/configuration-server.html-troubleshooting", "fields": {"questions": {"assign": ["What is a config server?", "How to check if a config server is up?", "How to check consistency between config servers?", "What happens if a config server goes down?", "How to check file distribution status?", "What causes startup or deploy problems?", "What can cause Java IOException in ZooKeeper?"]}}} -{"update": "id:open-p:paragraph::open/en/configuring-components.html-", "fields": {"questions": {"assign": ["What does the application code interface with?", "How are code and config kept in sync?"]}}} -{"update": "id:open-p:paragraph::open/en/configuring-components.html-config-definition", "fields": {"questions": {"assign": ["Where to place config definition file?", "What is a config definition file?", "What is the syntax of a config definition file?"]}}} -{"update": "id:open-p:paragraph::open/en/configuring-components.html-generate-config-class", "fields": {"questions": {"assign": ["How to generate Java config classes?", "Where are config classes generated?", "What is the full name of the generated Java class?"]}}} -{"update": "id:open-p:paragraph::open/en/configuring-components.html-use-config-in-code", "fields": {"questions": {"assign": ["What is a generated config class?", "What is constructor injection?", "How do you override config values?"]}}} -{"update": "id:open-p:paragraph::open/en/configuring-components.html-unit-testing-configurable-components", "fields": {"questions": {"assign": ["What is a builder API?", "How do you create config objects for unit testing?", "What is a unit test?", "What is a MyComponentTest?", "What is a MyComponent?", "What are the components of a MyComponentConfig?"]}}} -{"update": "id:open-p:paragraph::open/en/configuring-components.html-adding-files-to-the-component-configuration", "fields": {"questions": {"assign": ["How can files be transferred?", "What is a provider component?", "What is the config def file?", "What is file distribution?", "What is URL downloading?", "How can machine-learned models be referenced?", "What are the config type options for files?"]}}} -{"update": "id:open-p:paragraph::open/en/content/consistency.html-", "fields": {"questions": {"assign": ["What is data redundancy?", "What is Vespa?", "What is eventual consistency?", "What are acceptable tradeoffs?", "Where is eventual consistency efficient?", "What does this document aim to do?"]}}} -{"update": "id:open-p:paragraph::open/en/content/consistency.html-vespa-and-cap", "fields": {"questions": {"assign": ["What is the CAP theorem?", "What is consistency in CAP?", "What is availability in CAP?", "Does Vespa offer linearizability?", "Does Vespa offer guaranteed availability?", "What kind of consistency does Vespa offer?", "What are the limitations of Vespa under CAP?"]}}} -{"update": "id:open-p:paragraph::open/en/content/consistency.html-write-durability-and-consistency", "fields": {"questions": {"assign": ["What happens when a client receives a successful write response?", "What is the default replication level for operations?", "How does the system handle sudden power loss?", "What is the consequence of large clock drifts?", "What is the recommended approach for conditional updates?", "Are multi-document transactions supported?", "How does the system detect and reconcile divergent replicas?"]}}} -{"update": "id:open-p:paragraph::open/en/content/consistency.html-read-consistency", "fields": {"questions": {"assign": ["What guarantees do read operations have?", "What are the limitations of consistent reads?", "How does Vespa handle partially updated documents?", "Is coverage automatically restored after node failures?", "What happens if replicas diverge during a Get?", "What is the difference in behavior between Document V1 and vespa-visit?", "What is expected behavior during visitor operations?", "Is snapshot isolation guaranteed during visitor operations?"]}}} -{"update": "id:open-p:paragraph::open/en/content/consistency.html-replica-reconciliation", "fields": {"questions": {"assign": ["What is reconciliation in Vespa?", "How does Vespa reconcile replicas?", "What happens during reconciliation?", "What happens to tombstones during reconciliation?", "How does Vespa handle tombstones?", "What are the risks of not removing indexes on partitioned nodes?", "Where can I find more information on data retention vs. size?"]}}} -{"update": "id:open-p:paragraph::open/en/jdisc/container-components.html-", "fields": {"questions": {"assign": ["What is a Container component?", "What is the base class for all components?", "What are the main component types in Vespa?", "How are search container components deployed?", "Where can I find a multi-bundle example?", "What is required to develop all types of Container components?"]}}} -{"update": "id:open-p:paragraph::open/en/jdisc/container-components.html-concurrency", "fields": {"questions": {"assign": ["What is concurrency?"]}}} -{"update": "id:open-p:paragraph::open/en/jdisc/container-components.html-resource-management", "fields": {"questions": {"assign": ["What is deconstruct method?", "How to properly release resources?", "What is SharedResource.java?", "How to debug component lifetime?", "What is executor in the example?", "What happens if deconstruct is not overridden?", "What are the debug options for tools?", "What is container profiling?"]}}} -{"update": "id:open-p:paragraph::open/en/jdisc/container-components.html-dependency-injection", "fields": {"questions": {"assign": ["What are constructor dependencies?", "What objects can be injected?", "How to annotate the constructor?"]}}} -{"update": "id:open-p:paragraph::open/en/jdisc/container-components.html-the-systeminfo-injectable-component", "fields": {"questions": {"assign": ["What does the environment component provide?", "What information does the component include?"]}}} -{"update": "id:open-p:paragraph::open/en/jdisc/container-components.html-deploying-a-component", "fields": {"questions": {"assign": ["When is a component created?", "Where can I find the Container Javadoc?", "What are the steps to deploy a component?"]}}} -{"update": "id:open-p:paragraph::open/en/jdisc/container-components.html-building-the-plugin-.jar", "fields": {"questions": {"assign": ["How to build plugin jar?", "What command builds plugin jar?", "Where can the plugin be found?"]}}} -{"update": "id:open-p:paragraph::open/en/jdisc/container-components.html-adding-the-plugin-to-the-vespa-application-package", "fields": {"questions": {"assign": ["What is a plugin jar file?", "What is the manifest file?"]}}} -{"update": "id:open-p:paragraph::open/en/jdisc/container-components.html-deploying-the-application-package", "fields": {"questions": {"assign": ["How is switching done?", "Can interdependent searcher changes be deployed?"]}}} -{"update": "id:open-p:paragraph::open/en/jdisc/container-components.html-jni-requires-restart", "fields": {"questions": {"assign": ["What is the best practice for loading JNI library?", "What happens if JNI library is initialized lazily?", "What is the warning when deploying JNI packages?", "What causes failures when using JNI library?", "Where should JNI components be put?", "How can bundle reloads be prevented?", "What is the issue with reloading bundles containing JNI packages?", "What is the solution to prevent failures when using the new bundle version with JNI?"]}}} -{"update": "id:open-p:paragraph::open/en/jdisc/container-components.html-monitoring-the-active-application", "fields": {"questions": {"assign": ["What information does the built-in handler output?", "What path does the handler answer to?", "What is JSONView?", "How do I view nice formatted output in a browser?"]}}} -{"update": "id:open-p:paragraph::open/en/jdisc/container-components.html-including-third-party-libraries", "fields": {"questions": {"assign": ["What are external dependencies?"]}}} -{"update": "id:open-p:paragraph::open/en/jdisc/container-components.html-exporting,-importing-and-including-packages-in-bundles", "fields": {"questions": {"assign": ["What is information hiding?"]}}} -{"update": "id:open-p:paragraph::open/en/jdisc/container-components.html-global-and-exported-packages", "fields": {"questions": {"assign": ["What are global packages?", "What are exported packages?", "Are backwards incompatible changes allowed on exported packages?", "Where can I find the list of global and exported packages?", "What is the supported API of the JDisc Container?", "What constitutes the exported packages?", "Are exported packages considered public APIs?", "What is included in the exported packages?"]}}} -{"update": "id:open-p:paragraph::open/en/jdisc/container-components.html-versions", "fields": {"questions": {"assign": ["What can be versioned?", "What is the syntax for versioning?", "How are versions matched?", "Where should version be specified?"]}}} -{"update": "id:open-p:paragraph::open/en/jdisc/container-components.html-container-start", "fields": {"questions": {"assign": ["What is `vespa-logfmt`?", "How do you know when the new application is active?"]}}} -{"update": "id:open-p:paragraph::open/en/jdisc/container-components.html-component-load", "fields": {"questions": {"assign": ["What are constructed components?", "How to debug construction failures?", "What is vespa-logctl?", "What is container id?", "How to find failed components?"]}}} -{"update": "id:open-p:paragraph::open/en/performance/container-http.html-", "fields": {"questions": {"assign": ["What is Gatling?", "How is Gatling used?", "What does Gatling provide?", "How do you get started with Gatling in Vespa?", "Is Vespa different from other HTTP services when using Gatling?", "What kind of flexibility and checking is required for container testing?", "Can Gatling be customized with plug-ins?", "What is the DSL in Gatling?"]}}} -{"update": "id:open-p:paragraph::open/en/performance/container-http.html-install-gatling", "fields": {"questions": {"assign": ["Where to find Gatling's documentation?", "How to get started with Gatling?", "What version of Gatling is this tutorial written for?", "Where can I download Gatling?"]}}} -{"update": "id:open-p:paragraph::open/en/performance/container-http.html-configure-the-first-test-with-a-query-log", "fields": {"questions": {"assign": ["What is Gatling?", "What is a feeder?", "What is httpProtocol?", "What does the scenario execute?", "What does the constantRate do?", "What is the purpose of the VespaTutorial?"]}}} -{"update": "id:open-p:paragraph::open/en/performance/container-http.html-running-a-benchmark", "fields": {"questions": {"assign": ["What is Gatling used for?", "How do you start a Gatling simulation?", "What is the output of a Gatling simulation?", "How can you run Gatling on a personal laptop?", "What are the accepted characters for simulation id?", "What does the Gatling simulation report include?"]}}} -{"update": "id:open-p:paragraph::open/en/performance/container-tuning.html-", "fields": {"questions": {"assign": ["What are generic config overrides?"]}}} -{"update": "id:open-p:paragraph::open/en/performance/container-tuning.html-container-worker-threads", "fields": {"questions": {"assign": ["What are thread pools?", "How are thread pools tuned?", "What is the default thread pool?", "Which components have dedicated thread pools?", "How does the container scale thread pools?", "What is the risk of tuning capacity upwards?", "How are extra threads created?", "Where can the effective thread pool configuration be observed?"]}}} -{"update": "id:open-p:paragraph::open/en/performance/container-tuning.html-jvm-heap-size", "fields": {"questions": {"assign": ["How to change JVM heap size?", "What is allocated-memory?"]}}} -{"update": "id:open-p:paragraph::open/en/performance/container-tuning.html-jvm-tuning", "fields": {"questions": {"assign": ["What are gc-options?", "What are options used for?", "How does G1GC Work?"]}}} -{"update": "id:open-p:paragraph::open/en/performance/container-tuning.html-config-server-and-config-proxy", "fields": {"questions": {"assign": ["What is used to bootstrap services?", "How do you set JVM parameters?", "Do you need to vespa-deploy prepare?"]}}} -{"update": "id:open-p:paragraph::open/en/performance/container-tuning.html-container-warmup", "fields": {"questions": {"assign": ["What causes first queries to a freshly started container to take a long time?", "What is lazy initialization?", "What is eager initialization?", "What are warmup queries?", "How to perform warmup queries at startup?", "What is the warmupQueries() method?", "How to subclass default Vespa's SearchHandler?", "What is metrics.ignore used for?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/container.html-", "fields": {"questions": {"assign": ["What is the Container service?", "What is meant by 'container' in Vespa?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/container.html-endpoints", "fields": {"questions": {"assign": ["What is a container service?", "What are the different container clusters?", "What is the difference between container services in album-recommendation and multinode-HA examples?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/container.html-inspecting-vespa-java-services-using-jconsole", "fields": {"questions": {"assign": ["What is JConsole?", "How do I start JConsole?", "Can JConsole attach to Vespa services?", "What determines which service to attach to?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/container.html-connecting-to-a-vespa-instance", "fields": {"questions": {"assign": ["How to attach JConsole?", "What is an SSH tunnel?", "Where can I find updated port information?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/container.html-examining-thread-states", "fields": {"questions": {"assign": ["What is JConsole?", "What are the thread types?", "Which threads are used for backend communications?"]}}} -{"update": "id:open-p:paragraph::open/en/content/content-nodes.html-", "fields": {"questions": {"assign": ["What does the distributor do?", "How are documents forwarded to content nodes?"]}}} -{"update": "id:open-p:paragraph::open/en/content/content-nodes.html-cluster-state", "fields": {"questions": {"assign": ["What are the three kinds of state?", "What is the cluster state version?", "What is the minimum time between cluster state changes?", "What is the minimum capacity for cluster state to be up?", "What happens if a cluster has many nodes down?", "What is a cluster state indicating the entire cluster is down?"]}}} -{"update": "id:open-p:paragraph::open/en/content/content-nodes.html-cluster-controller", "fields": {"questions": {"assign": ["What is the cluster controller?", "What is the main task of the cluster controller?", "What is the cluster state?", "How is the cluster state updated and distributed?"]}}} -{"update": "id:open-p:paragraph::open/en/content/content-nodes.html-master-election", "fields": {"questions": {"assign": ["What is master election?", "How do cluster controllers vote?", "What happens if cluster controller goes down?", "What is the recommended number of cluster controllers?"]}}} -{"update": "id:open-p:paragraph::open/en/content/content-nodes.html-distributor", "fields": {"questions": {"assign": ["What is the ideal state algorithm?", "How are buckets mapped to distributors?", "Do distributors persist the bucket database?", "Where is the bucket-to-content-node mapping kept?", "How are document operations mapped to content nodes?", "Do document API clients know the cluster state at startup?", "What happens if the document operation hit the wrong distributor?"]}}} -{"update": "id:open-p:paragraph::open/en/content/content-nodes.html-timestamps", "fields": {"questions": {"assign": ["What is a last modified time?", "How are timestamps unique?", "What is the timestamp unit?", "What is the use of timestamps?", "How are timestamps affected by migration?", "Do documents have new timestamps when reprocessed?"]}}} -{"update": "id:open-p:paragraph::open/en/content/content-nodes.html-ordering", "fields": {"questions": {"assign": ["How are multiple operations on the same document handled?"]}}} -{"update": "id:open-p:paragraph::open/en/content/content-nodes.html-maintenance-operations", "fields": {"questions": {"assign": ["What is the ideal state algorithm?", "What are bucket maintenance operations?", "How are bucket inconsistencies resolved?", "What is the priority of maintenance operations?", "How do distributors synchronize maintenance and user load?", "What happens if buckets are too small or large?"]}}} -{"update": "id:open-p:paragraph::open/en/content/content-nodes.html-restart", "fields": {"questions": {"assign": ["What happens when a distributor stops?", "What happens to requests for buckets owned by a restarting distributor?", "Will requests to buckets not owned by the restarting distributor be affected?", "What will happen to the latency of requests when a distributor stops?"]}}} -{"update": "id:open-p:paragraph::open/en/content/content-nodes.html-content-node", "fields": {"questions": {"assign": ["What is the content node?", "What runs on the content node?"]}}} -{"update": "id:open-p:paragraph::open/en/content/content-nodes.html-restart", "fields": {"questions": {"assign": ["What happens during a controlled restart?", "Are client requests affected by content node restarts?", "What state does the node transition through during a restart?", "What happens when a node is in maintenance state during a restart?", "What happens to pending requests during a content node restart?", "What does the distributor do during a content node restart?", "What information do content nodes gather when starting?", "What state is the service layer in during a content node restart?"]}}} -{"update": "id:open-p:paragraph::open/en/content/content-nodes.html-metrics", "fields": {"questions": {"assign": ["What is idealstate.merge_bucket.pending?", "What is the description of vds.idealstate.buckets?", "What is the description of vds.idealstate.buckets_notrusted?", "What is the description of vds.idealstate.join_bucket.pending?", "What is the description of vds.idealstate.set_bucket_state.pending?", "What is the description of vds.idealstate.split_bucket.pending?"]}}} -{"update": "id:open-p:paragraph::open/en/content/content-nodes.html-/cluster/v2-api-examples", "fields": {"questions": {"assign": ["What is the /cluster/v2 API?", "What does cluster/v2/music return?", "What is the format of /cluster/v2/music response?", "What does /cluster/v2/music/storage list?", "What is the result of /cluster/v2/music/storage/0 request?", "What is the result of /cluster/v2/music/?recursive=true?"]}}} -{"update": "id:open-p:paragraph::open/en/content/content-nodes.html-further-reading", "fields": {"questions": {"assign": ["What are Vespa administrative procedures?", "What is the Multinode testing and observability sample app?", "What is the purpose of Vespa administrative procedures?", "What is the purpose of the Multinode testing and observability sample app?", "What can I learn from the Multinode testing and observability sample app?", "Where can I find information on Vespa administrative procedures?"]}}} -{"update": "id:open-p:paragraph::open/en/contributing.html-", "fields": {"questions": {"assign": ["Can I contribute to Vespa?"]}}} -{"update": "id:open-p:paragraph::open/en/contributing.html-open-development", "fields": {"questions": {"assign": ["What is Github flow model?", "How often is the master branch released?", "What is the Screwdriver build?", "Are there acceptance and performance tests?"]}}} -{"update": "id:open-p:paragraph::open/en/contributing.html-pull-requests", "fields": {"questions": {"assign": ["Who reviews pull requests?", "Where can I find a reviewer?", "How to become an OWNER?", "What are the pull request requirements?", "What is the OWNERS file responsibility?"]}}} -{"update": "id:open-p:paragraph::open/en/contributing.html-versioning", "fields": {"questions": {"assign": ["What is semantic versioning in Vespa?", "What is the link to Vespa versions?", "What is the @PublicAPI annotation?"]}}} -{"update": "id:open-p:paragraph::open/en/contributing.html-issues", "fields": {"questions": {"assign": ["Where can issues be tracked on Vespa?"]}}} -{"update": "id:open-p:paragraph::open/en/contributing.html-community", "fields": {"questions": {"assign": ["Where can I join Vespa community?", "What is the Vespa community?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/default-result-format.html-", "fields": {"questions": {"assign": ["What is the default JSON format?", "What is the timing object?", "What is the root element?", "What is the format of the coverage object?", "What is trace in Vespa API?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/default-result-format.html-json-schema", "fields": {"questions": {"assign": ["What is the schema format?", "What is the coverage property?", "What is the purpose of the trace node?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/default-result-format.html-appendix:-legacy-vespa-7-json-rendering", "fields": {"questions": {"assign": ["What is fixed in Vespa 8?", "What are the search inconsistencies?", "How to configure document rendering?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/default-result-format.html-inconsistent-weightedset-rendering", "fields": {"questions": {"assign": ["What is the input representation of weightedset fields?", "How is a field with weightedset type represented in search results?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/default-result-format.html-inconsistent-map-rendering", "fields": {"questions": {"assign": ["What is the JSON input representation?", "What is the default JSON renderer for search results?", "What is the format used when rendering a document?", "What is the key/value pair format used in search results?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/default-result-format.html-geo-position-rendering", "fields": {"questions": {"assign": ["What are x and y used for when rendering positions?", "What is the difference between Vespa 7 and Vespa 8 position rendering?", "How do you switch back to the legacy Vespa 7 position rendering?", "What fields are removed in Vespa 8 position rendering?", "What is the 'lat' and 'lng' fields used for?", "What is the format used for Google places API fields?", "What changes have been made to synthetic summary fields in Vespa 8?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/default-result-format.html-summary-features-wrapped-in-\"rankingexpression\"", "fields": {"questions": {"assign": ["What is FeatureData class?", "What is the JSON output?", "How to get old behavior?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/default-set-metrics-reference.html-searchnode-metrics", "fields": {"questions": {"assign": ["What is the proton documentdb?", "What is the purpose of content.proton.search_protocol.docsum?", "What is the purpose of content.proton.documentdb.documents ?", "What is the purpose of content.proton.resource_usage?", "What is the meaning of content.proton.documentdb.matching.docs_matched?", "What is the meaning of content.proton.documentdb.matching.rank_profile.query_setup_time?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/deploy-rest-api-v2.html-", "fields": {"questions": {"assign": ["What is HTTP Deploy API?", "What is the current API version?", "What is a session-id used for?", "What tools can be used for deployment?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/deploy-rest-api-v2.html-post-/application/v2/tenant/default/session", "fields": {"questions": {"assign": ["What is the purpose of creating a session?", "What parameters are needed to create a new session?", "What is the response when a session is created?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/deploy-rest-api-v2.html-put-/application/v2/tenant/default/session/[[session-id](%23session-id)]/content/[[path](%23path)]", "fields": {"questions": {"assign": ["What happens when path ends in '/'?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/deploy-rest-api-v2.html-get-/application/v2/tenant/default/session/[[session-id](%23session-id)]/content/[[path](%23path)]", "fields": {"questions": {"assign": ["What is returned for a file in the content/ URL endpoint?", "What is returned for a directory in the content/ URL endpoint?", "What does the 'status' option of the 'return' parameter return?", "What happens when the 'status' option of the 'return' parameter is set for a directory in the content/ URL endpoint?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/deploy-rest-api-v2.html-delete-/application/v2/tenant/default/session/[[session-id](%23session-id)]/content/[[path](%23path)]", "fields": {"questions": {"assign": ["What does the DELETE method do?", "What does the server respond with after deleting a resource?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/deploy-rest-api-v2.html-put-/application/v2/tenant/default/session/[[session-id](%23session-id)]/prepared", "fields": {"questions": {"assign": ["What is session-id?", "What is configChangeActions?", "What is reindex?", "What is session preparation?", "What is the activate URL?", "What are the possible categories of actions?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/deploy-rest-api-v2.html-get-/application/v2/tenant/default/session/[[session-id](%23session-id)]/prepared", "fields": {"questions": {"assign": ["What is a prepared session?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/deploy-rest-api-v2.html-get-/application/v2/tenant/default/application/default", "fields": {"questions": {"assign": ["What does GET /application/v2/tenant/default/application/default do?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/deploy-rest-api-v2.html-get-/application/v2/tenant/default/application/default/environment/default/region/default/instance/default/reindexing", "fields": {"questions": {"assign": ["What does reindexing status return?", "What is the status of test_result?", "What is the progress of test_artifact?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/deploy-rest-api-v2.html-post-/application/v2/tenant/default/application/default/environment/default/region/default/instance/default/reindex", "fields": {"questions": {"assign": ["What is reindexing?", "How do you trigger reindexing?", "What are the parameters for reindexing?", "What does indexedOnly do?", "What is the default speed for reindexing?", "What is the response to reindexing?", "What is the request body for reindexing?", "What is 'message' in the response?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/deploy-rest-api-v2.html-get-/application/v2/tenant/default/application/default/environment/default/region/default/instance/default/content/[[path](%23path)]", "fields": {"questions": {"assign": ["What is the argument for getting content?", "What is the response type of 'getting content'?", "What is the endpoint for 'getting content'?", "How do you format the path to get content?", "Is 'getting content' only available for applications?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/deploy-rest-api-v2.html-get-/application/v2/host/[hostname]", "fields": {"questions": {"assign": ["What does GET /application/v2/host/ do?", "What hostname is used in the example?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/deploy-rest-api-v2.html-error-handling", "fields": {"questions": {"assign": ["What is an error response?", "What does the 400 error code mean?", "What is the error message for 401 error code?", "What are the possible error codes for Vespa?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/deploy-rest-api-v2.html-access-log", "fields": {"questions": {"assign": ["Where are requests logged?", "What is the access log path?", "What does the access log contain?", "What is the format of the access log?", "What is the IP field in the access log?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/deploy-rest-api-v2.html-create,-prepare-and-activate-an-application", "fields": {"questions": {"assign": ["How to create a session with an application package?", "What is the URL to activate the application?", "What is the URL to prepare the application?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/deploy-rest-api-v2.html-modify-the-application-package", "fields": {"questions": {"assign": ["How do we modify services.xml?", "What is session 1 and 2?", "What is the purpose of rpcport?", "How to make a session active?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/deploy-rest-api-v2.html-rollback", "fields": {"questions": {"assign": ["How to roll back application package?", "What is session-id?"]}}} -{"update": "id:open-p:paragraph::open/en/developer-guide.html-", "fields": {"questions": {"assign": ["Where can I find sample applications?"]}}} -{"update": "id:open-p:paragraph::open/en/developer-guide.html-developing-components", "fields": {"questions": {"assign": ["What is the development cycle?", "What is metal-names.def?"]}}} -{"update": "id:open-p:paragraph::open/en/developer-guide.html-debugging-components", "fields": {"questions": {"assign": ["How to debug in Vespa?", "How to set up remote debugging in Vespa?", "How to configure jvm options in Vespa?", "What is the default jvm option for remote debugging in Vespa?", "How to restart Vespa in a node that runs the container?"]}}} -{"update": "id:open-p:paragraph::open/en/developer-guide.html-plugins", "fields": {"questions": {"assign": ["What is the Vespa IntelliJ plugin?", "How can IntelliJ be simplified?"]}}} -{"update": "id:open-p:paragraph::open/en/developer-guide.html-miscellaneous", "fields": {"questions": {"assign": ["How do I delete a schema?", "What is the 'drop schema' command?"]}}} -{"update": "id:open-p:paragraph::open/en/jdisc/developing-request-handlers.html-", "fields": {"questions": {"assign": ["What is a custom request handler?", "When is a custom request handler necessary?", "What are examples of when to use a custom request handler?"]}}} -{"update": "id:open-p:paragraph::open/en/jdisc/developing-request-handlers.html-implementing-a-request-handler", "fields": {"questions": {"assign": ["What is a request handler?", "How should I implement a request handler?", "What is ThreadedHttpRequestHandler?", "What functionality is implemented by ThreadedHttpRequestHandler?", "What is the execution model for ThreadedHttpRequestHandler?", "What is the HTTP date header?", "What is logged by ThreadedHttpRequestHandler in case of errors or timeouts?"]}}} -{"update": "id:open-p:paragraph::open/en/jdisc/developing-request-handlers.html-example-request-handler-implementations", "fields": {"questions": {"assign": ["What is the SiteHandler?", "What is the DemoHandler?"]}}} -{"update": "id:open-p:paragraph::open/en/jdisc/developing-request-handlers.html-deploying-a-request-handler", "fields": {"questions": {"assign": ["What is a request handler?", "How to deploy a request handler?", "What is the handler element?", "What is the binding element?", "How to bind a request handler?", "What is the id attribute for handler?", "What is the bundle attribute for handler?", "What is the binding element used for?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/developing-server-providers.html-", "fields": {"questions": {"assign": ["What is ServerProvider?", "What requests are created by server providers?", "What is the purpose of a server provider?", "How to implement a custom server provider?", "How to respond back to the client?", "What is the difference between a server request and a client request?", "How to install a server provider in a container?"]}}} -{"update": "id:open-p:paragraph::open/en/developing-web-services.html-", "fields": {"questions": {"assign": ["What is a Fine-grained API?", "What is a Coarse-grained API?", "How does the container help with coarse-grained APIs?", "How can a specific chain of processors be invoked?", "What is the purpose of the generic mechanism allowing request handlers to be bound to a URI pattern?", "What are the types of web service APIs?"]}}} -{"update": "id:open-p:paragraph::open/en/developing-web-services.html-accessing-the-http-request", "fields": {"questions": {"assign": ["What is a HttpRequest?", "What are HTTP URL parameters?", "How can a Processor retrieve the entire HTTP request?"]}}} -{"update": "id:open-p:paragraph::open/en/developing-web-services.html-setting-the-http-status-and-http-headers", "fields": {"questions": {"assign": ["What sets return status in Processing?", "What is ResponseStatus in Processing?", "How is status determined in Processing?"]}}} -{"update": "id:open-p:paragraph::open/en/developing-web-services.html-setting-response-headers-from-processors", "fields": {"questions": {"assign": ["What are response headers?", "How are response headers added?", "What is ResponseHeaders?", "Can multiple ResponseHeaders be added?", "What data type is ResponseHeaders?", "Can request handlers manipulate headers?", "What is HttpRequest used for?"]}}} -{"update": "id:open-p:paragraph::open/en/developing-web-services.html-queries", "fields": {"questions": {"assign": ["What are extra bindings?", "How to add bindings?"]}}} -{"update": "id:open-p:paragraph::open/en/developing-web-services.html-invoking-vespa-queries-from-a-component", "fields": {"questions": {"assign": ["What is ExecutionFactory?", "What is Vespa query?", "What is the purpose of Execution in Vespa?", "What is ExecutionFactory API?", "How is ExecutionFactory injected?", "How to create new Execution in Vespa?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/docker-containers.html-", "fields": {"questions": {"assign": ["What is Vespa Docker?", "How to tune Vespa containers?", "What are the adaptions for Vespa Docker?", "Can Vespa Docker be used in production?", "How to use Vespa Docker on a laptop?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/docker-containers.html-mounting-persistent-volumes", "fields": {"questions": {"assign": ["How to run Vespa in Docker?", "What is the Vespa var path?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/docker-containers.html-start-vespa-container-with-vespa-user", "fields": {"questions": {"assign": ["What is vespa user?", "What is VESPA_VAR_STORAGE?", "What is VESPA_LOG_STORAGE?", "What is the purpose of chown command?", "How to start Vespa container?", "What is the purpose of start script?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/docker-containers.html-system-limits", "fields": {"questions": {"assign": ["What are system limits for Vespa?", "How should Docker be started for Vespa?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/docker-containers.html-transparent-huge-pages", "fields": {"questions": {"assign": ["What is THP?", "How does THP improve Vespa performance?", "What are the recommended THP settings for Vespa on Linux?", "How can I verify that THP is active?", "What is AnonHugePages used for in Vespa?", "What process in Vespa uses MADV_HUGEPAGE?", "Why is THP more important in virtualized environments?", "What was the query p99 latency improvement when using THP in Vespa?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/docker-containers.html-controlling-which-services-to-start", "fields": {"questions": {"assign": ["What is the purpose of start-container.sh script?", "Which services can be started inside the container?", "How do you start a configserver container?", "How do you start a services container?", "How do you start a container with both configserver and service?", "What is the VESPA_CONFIGSERVERS environment variable used for?", "What happens if VESPA_CONFIGSERVERS environment variable is not set?", "Which sample application can be used as a blueprint for setting up config servers and services?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/docker-containers.html-memory", "fields": {"questions": {"assign": ["What are the minimum memory requirements for Vespa Docker containers?", "How can I track memory usage of Vespa Docker container?", "What are the symptoms of errors due to insufficient memory during Vespa startup?", "What is a common problem due to lack of memory when running Vespa config servers?", "Where can container memory settings be found in Vespa services.xml?", "What is the significance of VESPA_CONFIGSERVER_JVMARGS in Vespa multinode-HA configuration?", "What should be done to ensure container memory settings match that of Docker container when running Vespa?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/docker-containers.html-network", "fields": {"questions": {"assign": ["What is an example of Docker network use?"]}}} -{"update": "id:open-p:paragraph::open/en/document-api-guide.html-", "fields": {"questions": {"assign": ["What is Vespa Document API?", "How to build and compile Vespa clients?", "What is the async API used for?", "What is the VESPA_CONFIG_SOURCES environment variable?"]}}} -{"update": "id:open-p:paragraph::open/en/document-api-guide.html-documents", "fields": {"questions": {"assign": ["What consists of a Document?", "What is a DocumentType?", "What is a DocumentId?", "What is a FieldUpdate?", "What is DocumentAccess?", "What is SyncSession?"]}}} -{"update": "id:open-p:paragraph::open/en/document-api-guide.html-fields", "fields": {"questions": {"assign": ["What is aByte?", "What is aDouble?", "What is aWeightedset?"]}}} -{"update": "id:open-p:paragraph::open/en/document-api-guide.html-document-updates", "fields": {"questions": {"assign": ["What is a document update?", "How are primitive fields updated?", "How are complex fields updated?", "What field types support field path updates?", "What are some examples of FieldUpdate?", "What are some examples of FieldPathUpdate?"]}}} -{"update": "id:open-p:paragraph::open/en/document-api-guide.html-document-access", "fields": {"questions": {"assign": ["What is DocumentAccess for?", "What is SyncSession?", "What is AsyncSession?", "What is VisitorSession?", "What is the purpose of DocumentAccess?", "What are the access types provided by DocumentAccess?", "What is the get() method for?", "What is the createXSession() method for?"]}}} -{"update": "id:open-p:paragraph::open/en/document-api-guide.html-asyncsession", "fields": {"questions": {"assign": ["What is an asynchronous session?", "What is the purpose of waitForPending()?"]}}} -{"update": "id:open-p:paragraph::open/en/document-api-guide.html-visitorsession", "fields": {"questions": {"assign": ["What is a VisitorSession?", "What is a visitor target?", "What is VisitorControlHandler?", "What is VisitorDataHandler?", "What is VisitorParameters?", "What is the default VisitorDataHandler?", "How to implement custom VisitorControlHandler?", "How to improve visiting performance?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/document-field-path.html-", "fields": {"questions": {"assign": ["What is field path syntax?", "How is field path syntax used?", "What is an example of field path syntax?", "What field types can use field path syntax?", "Can field path syntax be combined?", "How does field path syntax traverse documents?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/document-field-path.html-maps/weighted-sets", "fields": {"questions": {"assign": ["What is the syntax for retrieving a specific key in a mapfield?", "What does {$} retrieve?", "How do I retrieve all the values associated with a mapfield?", "What does .key retrieve?", "What does .value retrieve?", "What does retrieve?", "What is a weighted set?", "What is referred to as the value in a weighted set?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/document-field-path.html-array", "fields": {"questions": {"assign": ["What is the syntax for retrieving a value at a specific index in an array?", "How do you retrieve all values in an array?", "What is the syntax for retrieving all values in an array with variable names?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/document-field-path.html-struct", "fields": {"questions": {"assign": ["What does do?", "What subfields can be returned?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/document-json-format.html-", "fields": {"questions": {"assign": ["What does this document describe?", "What is the JSON format used?", "What are the JSON representations for document operations?", "What is the JSON representation for addressing fields?", "What is the troubleshooting guide for encoding?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/document-json-format.html-field-types", "fields": {"questions": {"assign": ["What is a raw field?", "How are weighted sets represented?", "How are arrays represented?", "What is a URI field?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/document-json-format.html-empty-fields", "fields": {"questions": {"assign": ["What is ignored during feeding?", "What is an empty field?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/document-json-format.html-document-operations", "fields": {"questions": {"assign": ["What are the two methods for document operations?", "What does /document/v1/ accept?", "What is the PUT request for Vespa HTTP/feed client?", "What is the PUT request for /document/v1/?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/document-json-format.html-test-and-set", "fields": {"questions": {"assign": ["What is a test and set condition?", "What is a condition?", "What is the response when the condition is not met?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/document-json-format.html-assign", "fields": {"questions": {"assign": ["What is the 'assign' command used for?", "Can 'assign' replace values in a collection?", "What is the syntax for 'assign'?", "Is the syntax for 'assign' the same as 'put'?", "How does 'assign' work with fields?", "How does 'assign' work with collections?", "Does 'assign' create new values or replace existing ones?", "Is 'assign' a fast operation?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/document-json-format.html-single-value-field", "fields": {"questions": {"assign": ["What is being assigned to title field?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/document-json-format.html-tensor-field", "fields": {"questions": {"assign": ["What is the value of cell at address (x=a,y=b)?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/document-json-format.html-replacing-all-fields-in-a-struct", "fields": {"questions": {"assign": ["What is a struct in Vespa?", "What is a person struct?", "What is 'contact' in this example?", "What does assigning a contact do?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/document-json-format.html-individual-struct-fields", "fields": {"questions": {"assign": ["What is the field path syntax?", "How to update individual struct fields?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/document-json-format.html-map-field", "fields": {"questions": {"assign": ["How to update map entries?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/document-json-format.html-map-to-primitive-value", "fields": {"questions": {"assign": ["What is my_food_scores?", "What is the content of my_food_score for Strawberries?", "What is the id for mynamespace:food::example?", "What is the value for my_food_scores{Strawberries}?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/document-json-format.html-map-to-struct", "fields": {"questions": {"assign": ["What is the attribute type of contacts?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/document-json-format.html-array-of-primitive-values", "fields": {"questions": {"assign": ["What is the field type of 'ingredients'?", "What is the indexing for 'ingredients'?", "How do you assign elements in arrays?", "What is the syntax for matching elements in arrays?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/document-json-format.html-array-of-struct", "fields": {"questions": {"assign": ["What is an array of structs?", "How do you update an element in an array?", "What is the alternative syntax for updating elements in an array?", "What is the 'people' field?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/document-json-format.html-weighted-set-field", "fields": {"questions": {"assign": ["What is a weighted set?", "How do you add to a weighted set?", "What is more efficient than using field{key} syntax for weighted sets?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/document-json-format.html-clearing-a-field", "fields": {"questions": {"assign": ["How to clear a field?", "What value clears a field?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/document-json-format.html-add", "fields": {"questions": {"assign": ["What can be added using add?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/document-json-format.html-adding-array-elements", "fields": {"questions": {"assign": ["What is the field type of 'tracks'?", "What is being added to 'tracks'?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/document-json-format.html-add-weighted-set-entries", "fields": {"questions": {"assign": ["What is weighted set?", "How to add weighted set elements?", "Can a key/weight mapping be overwritten?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/document-json-format.html-add-tensor-cells", "fields": {"questions": {"assign": ["What is the format for tensor fields?", "How do you add cells to tensors?", "What happens when you add a cell that already exists?", "What is the fully specified address required for adding cells?", "What is the block short form for mixed tensors?", "What is the default value for cells with bound indexed dimensions not specified?", "What is the URL for more examples of adding cells?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/document-json-format.html-weighted-set-field", "fields": {"questions": {"assign": ["What is field string_weighted_set?", "What is weightedset?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/document-json-format.html-map-field", "fields": {"questions": {"assign": ["What is the type of string_map?", "What is an example of string_map update?", "What is the purpose of 'remove' in string_map update?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/document-json-format.html-tensor-field", "fields": {"questions": {"assign": ["What is tensorfield?", "What is tensor cell removal?", "What is the format of tensorfield?", "How to remove cells from tensorfield?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/document-json-format.html-arithmetic", "fields": {"questions": {"assign": ["What are the four arithmetic operators used?", "How can you modify single value numeric values?", "What is the syntax to define int data type for sales field?", "What is the purpose of the 'increment' operator?", "What is a sample update using the increment operator?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/document-json-format.html-match", "fields": {"questions": {"assign": ["What is the match operation?", "What is a weighted set?", "How to increment a weightedset ?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/document-json-format.html-fieldpath", "fields": {"questions": {"assign": ["What is Fieldpath?", "How to access map elements?", "How to access array elements?", "What is the syntax to access struct elements?", "What structures can be accessed with Fieldpath?", "What does Fieldpath add to field values?", "Does Fieldpath work for index mode?", "Can nested structures be accessed with Fieldpath?"]}}} -{"update": "id:open-p:paragraph::open/en/document-processing.html-", "fields": {"questions": {"assign": ["What is document processing?", "What are document processors?", "What is the motivation for document processing?"]}}} -{"update": "id:open-p:paragraph::open/en/document-processing.html-deploying-a-document-processor", "fields": {"questions": {"assign": ["What is a document processor?", "How do you add a document processor?", "What is a general docproc chain?", "What is the default indexing chain?", "What is a route to run through a chain?", "What is the purpose of adding a document processor to a chain?", "What is the purpose of running a chain in indexing?"]}}} -{"update": "id:open-p:paragraph::open/en/document-processing.html-document-processors", "fields": {"questions": {"assign": ["What is a document processor?", "What is com.yahoo.docproc.DocumentProcessor?", "What is the process() method used for?", "What is Progress?", "What is Progress.DONE used for?", "What is Progress.LATER used for?"]}}} -{"update": "id:open-p:paragraph::open/en/document-processing.html-chains", "fields": {"questions": {"assign": ["What is a document processor chain?", "What is a call stack?", "What is the purpose of a document processor chain?", "How are document processor chains executed?"]}}} -{"update": "id:open-p:paragraph::open/en/document-processing.html-execution-model", "fields": {"questions": {"assign": ["How does the Document Processing Framework work?", "What is the priority queue used for?", "How are messages processed?", "Are there multiple instances of each document processor?", "Are instance variables safe in document processor classes?", "What is the constraint on document processor classes?", "What is the recommended resource management approach?", "How many worker threads execute document processors concurrently?"]}}} -{"update": "id:open-p:paragraph::open/en/document-processing.html-asynchronous-execution", "fields": {"questions": {"assign": ["What are the restrictions in executing a document processor?", "How can you fix a bottleneck when all worker threads block on an external resource?", "What is the pattern to use for a document processor doing high latency operations?"]}}} -{"update": "id:open-p:paragraph::open/en/document-processing.html-state", "fields": {"questions": {"assign": ["What is thread-safe?", "How to implement a multithread-safe document processor?", "What are the options for implementing multithread-safe document processor?"]}}} -{"update": "id:open-p:paragraph::open/en/document-processing.html-processing-context-variables", "fields": {"questions": {"assign": ["What is the map format of Processing variable?", "How can you pass information between document processors using Processing variable?", "What is Progress.LATER used for?", "How can you save the state of Processing using context variables?", "Is there any sample application that uses Processing variable?", "Where can I find more details about get/setVariable methods in Processing.java?"]}}} -{"update": "id:open-p:paragraph::open/en/document-processing.html-feed-ordering", "fields": {"questions": {"assign": ["What is the guarantee for same document operations?", "Is ordering guaranteed for different documents?", "What is feed endpoint?"]}}} -{"update": "id:open-p:paragraph::open/en/document-processing.html-document-processing-ordering", "fields": {"questions": {"assign": ["What are document operations?", "What rules apply to document processors?", "How does Vespa sequence operations?"]}}} -{"update": "id:open-p:paragraph::open/en/document-processing.html-(re)configuring-document-processing", "fields": {"questions": {"assign": ["What happens when changing chain ids?", "What is a document processor?", "When is a restart required?", "What happens when deploying a new document processor?"]}}} -{"update": "id:open-p:paragraph::open/en/document-processing.html-class-diagram", "fields": {"questions": {"assign": ["What is in the class diagram?", "What is a core class?", "What does the diagram show?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/document-select-language.html-", "fields": {"questions": {"assign": ["What is the document selector language?", "What is a subset of documents?", "What can be parsed to build a parse tree?", "What can the parse tree answer?", "What is contained within the subset?", "How is the document selector language used during feeding?", "How is the document selector language used during garbage collection?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/document-select-language.html-case-sensitiveness", "fields": {"questions": {"assign": ["What are case-sensitive identifiers?", "What are the recommended identifiers?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/document-select-language.html-branch-operators-/-precedence", "fields": {"questions": {"assign": ["What are branch operators?", "What is the order of precedence?", "How to use parentheses in Vespa queries?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/document-select-language.html-primitives", "fields": {"questions": {"assign": ["What is a Boolean constant?", "What is a null constant?", "What is a document field specification?", "What is a comparison?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/document-select-language.html-comparison", "fields": {"questions": {"assign": ["What are comparison operators?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/document-select-language.html-comparisons-with-missing-fields-(null-values)", "fields": {"questions": {"assign": ["What comparison operators work with null values?", "What happens when invalid values are propagated?", "How can short-circuiting be used to work around the issue?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/document-select-language.html-null-behavior-with-imported-fields", "fields": {"questions": {"assign": ["What is the issue with `null` values in imported fields?", "How can you handle `null` values for imported fields in GC selections?", "What is the purpose of selection criteria in a `` tag?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/document-select-language.html-locale-/-character-sets", "fields": {"questions": {"assign": ["What character sets do Vespa support?", "Does Vespa support regex matching?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/document-select-language.html-values", "fields": {"questions": {"assign": ["What is a fieldpath?", "What is null value?", "What is the syntax for numbers?", "What is the syntax for strings?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/document-select-language.html-value-arithmetics", "fields": {"questions": {"assign": ["What arithmetic operators are supported?", "What can you do with values?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/document-select-language.html-functions", "fields": {"questions": {"assign": ["What does abs() do?", "What is the lowercase() function?", "What is the version() function?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/document-select-language.html-now-function", "fields": {"questions": {"assign": ["What is the now() function?", "What is garbage collection in Vespa?", "How can you filter documents by age?", "What is the inserttimestamp field?", "Can you use now() for filtering documents?", "What is the time range for now() in garbage collection?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/document-select-language.html-using-imported-fields-in-selections", "fields": {"questions": {"assign": ["How to handle null values?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/document-select-language.html-example", "fields": {"questions": {"assign": ["What does a grandparent schema contain?", "What are the benefits of using a parent-child hierarchy?", "Where can I find more examples of using imported fields?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/document-select-language.html-constraints", "fields": {"questions": {"assign": ["What are invalid document type names?", "What is restricted in document type names?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/document-select-language.html-grammar---ebnf-of-the-language", "fields": {"questions": {"assign": ["What is excluded from double casing?", "What is a fieldspec?", "What is a leaf?"]}}} -{"update": "id:open-p:paragraph::open/en/document-summaries.html-", "fields": {"questions": {"assign": ["What is a document summary?", "What is a document summary class?", "What is the default summary class?", "How are summary classes defined?", "What is the use of document summary class?", "What is the parameter for defining summary class?", "What is the benefit of using document summary?", "What are dynamic snippets in document summaries?"]}}} -{"update": "id:open-p:paragraph::open/en/document-summaries.html-selecting-summary-fields-in-yql", "fields": {"questions": {"assign": ["What is a YQL statement?", "What is a summary class?", "How to filter fields using YQL?", "What is contained in a summary?", "How to select all fields?", "What is the syntax of a select statement?", "What is the meaning of fields in a document summary?"]}}} -{"update": "id:open-p:paragraph::open/en/document-summaries.html-summary-field-rename", "fields": {"questions": {"assign": ["What is a document summary?", "What is a summary class?", "How to define fields in a summary class?", "What is a non-attribute field?", "When do changes in summary class require re-indexing?", "What is the source in document summary?", "What is the artist_name field in document summary?"]}}} -{"update": "id:open-p:paragraph::open/en/document-summaries.html-dynamic-snippets", "fields": {"questions": {"assign": ["What is dynamic for summary fields?", "How does dynamic summary work?", "What is bolding?", "How to enable bolding?", "What is the response format for dynamic summaries?", "What is the syntax to enable dynamic summary?", "How to create document summaries?", "What are the benefits of using dynamic summaries?"]}}} -{"update": "id:open-p:paragraph::open/en/document-summaries.html-dynamic-snippet-configuration", "fields": {"questions": {"assign": ["How to configure dynamic snippets?", "Where to add juniperrc config?"]}}} -{"update": "id:open-p:paragraph::open/en/document-summaries.html-performance", "fields": {"questions": {"assign": ["What are memory-only operations?", "What is the optimal way to get high query throughput?", "How are debug fields with summary enabled affected?", "What is the document summary latency?", "What increases with more hits?", "What is the content node summary cache?"]}}} -{"update": "id:open-p:paragraph::open/en/document-v1-api-guide.html-", "fields": {"questions": {"assign": ["What is the document/v1 API?", "How to read a document?", "How to write a document?", "How to delete a document?"]}}} -{"update": "id:open-p:paragraph::open/en/document-v1-api-guide.html-conditional-writes", "fields": {"questions": {"assign": ["What is a test-and-set condition?", "Which operations support test-and-set?", "What is returned if the test-and-set condition is not met?", "Where can I find the condition reference?", "What is the format for the test-and-set condition?", "How do I add a test-and-set condition to Put, Remove, and Update operations?", "What is the pathId in the message response?", "What is the meaning of the 412 Precondition Failed error code?"]}}} -{"update": "id:open-p:paragraph::open/en/document-v1-api-guide.html-create-if-nonexistent", "fields": {"questions": {"assign": ["What is an upsert?", "How does 'create' simplify client code?", "What is the syntax for using 'create' in a curl command?", "What is a conditional write?", "How does 'create' work with a conditional write?"]}}} -{"update": "id:open-p:paragraph::open/en/document-v1-api-guide.html-data-dump", "fields": {"questions": {"assign": ["What is jq?", "How to dump all data?", "What is a continuation token?", "What is the recommended number of slices?"]}}} -{"update": "id:open-p:paragraph::open/en/document-v1-api-guide.html-!/bin-bash", "fields": {"questions": {"assign": ["What is the default number of slices?", "What is the 'curlTimeout' variable?"]}}} -{"update": "id:open-p:paragraph::open/en/document-v1-api-guide.html-auth-can-be-something-like-auth='--key-data-plane-private-key.pem---cert-data-plane-public-cert.pem'", "fields": {"questions": {"assign": ["What is the function 'visit' for?", "What does the parameter 'sliceId' represent?"]}}} -{"update": "id:open-p:paragraph::open/en/document-v1-api-guide.html-visiting-throughput", "fields": {"questions": {"assign": ["What is visit with selection?", "What are the options for increasing throughput?", "What is the most effective way to increase throughput?", "What are slices?", "What is backend concurrency?", "Why is it recommended to always stream HTTP responses?", "What is the default for streaming HTTP responses?"]}}} -{"update": "id:open-p:paragraph::open/en/document-v1-api-guide.html-getting-started", "fields": {"questions": {"assign": ["What is wantedDocumentCount used for?", "How do you look up a document by ID?"]}}} -{"update": "id:open-p:paragraph::open/en/document-v1-api-guide.html-troubleshooting", "fields": {"questions": {"assign": ["How to delete all documents in music schema?"]}}} -{"update": "id:open-p:paragraph::open/en/document-v1-api-guide.html-using-number-and-group-id-modifiers", "fields": {"questions": {"assign": ["What are group and number modifiers?", "What document types support group/number?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/document-v1-api-reference.html-", "fields": {"questions": {"assign": ["What is the /document/v1 API?", "What are the uses for the /document/v1 API?", "What are number and group modifiers?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/document-v1-api-reference.html-configuration", "fields": {"questions": {"assign": ["How to enable API in Vespa?", "Where to add document-api?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/document-v1-api-reference.html-http-requests", "fields": {"questions": {"assign": ["What is /document/v1 used for?", "How to retrieve a document by ID?", "How to delete a document by ID?", "How to delete documents based on a selection?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/document-v1-api-reference.html-request-parameters", "fields": {"questions": {"assign": ["What is continuation?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/document-v1-api-reference.html-request-body", "fields": {"questions": {"assign": ["What must requests include?", "What happens with ID in requests?", "What is dropped in requests?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/document-v1-api-reference.html-http-status-codes", "fields": {"questions": {"assign": ["What is the status code 200?", "What is the status code 400?", "What is the status code 404?", "What is the status code 412?", "What is the status code 429?", "What is the status code 500?", "What is the status code 502?", "What is the status code 503?", "What is the status code 504?", "What is the status code 507?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/document-v1-api-reference.html-http-response-headers", "fields": {"questions": {"assign": ["What is X-Vespa-Ignored-Fields?", "When is X-Vespa-Ignored-Fields set?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/document-v1-api-reference.html-response-format", "fields": {"questions": {"assign": ["What is the format of Vespa responses?", "What are the fields in Vespa responses?", "What is a continuation in Vespa responses?", "What is included in a GET request?", "What is the purpose of the message field in Vespa responses?"]}}} -{"update": "id:open-p:paragraph::open/en/documents.html-", "fields": {"questions": {"assign": ["What is a document in Vespa?", "What is a document ID?", "What is a document schema?", "What is the garbage collector?", "How does Vespa distribute documents?", "What are global documents?"]}}} -{"update": "id:open-p:paragraph::open/en/documents.html-document-ids", "fields": {"questions": {"assign": ["What are document identifiers?", "What is the URI scheme for identifiers?", "What text characters are allowed in identifiers?"]}}} -{"update": "id:open-p:paragraph::open/en/documents.html-id-scheme", "fields": {"questions": {"assign": ["What is the id scheme?", "What are the required id scheme parts?", "What is the key/value-pairs field used for?", "What are the possible modifiers for the id scheme?", "What is the user-specified field?", "Where can examples of the id scheme be found?", "What is the *id scheme*?", "What is the format of the id scheme?"]}}} -{"update": "id:open-p:paragraph::open/en/documents.html-namespace", "fields": {"questions": {"assign": ["How are document IDs different?", "What is a namespace?", "Can namespace be used in queries?", "How is namespace used for document selection?", "What is the full document ID for document A?", "What is the full document ID for document B?", "What is the difference in the document IDs of A and B?"]}}} -{"update": "id:open-p:paragraph::open/en/documents.html-fields", "fields": {"questions": {"assign": ["What are fields in documents?", "What is document put/update?", "Can fields have default values?", "How can a default value be assigned?", "What is assigned by document processor?"]}}} -{"update": "id:open-p:paragraph::open/en/documents.html-fieldsets", "fields": {"questions": {"assign": ["What is a fieldset?", "What are built-in fieldsets?", "How do I specify a fieldset?", "What does [all] fieldset do?", "What is [document] fieldset?", "What is [none] fieldset?", "What does [id] fieldset return?", "What does :[document] fieldset return?"]}}} -{"update": "id:open-p:paragraph::open/en/documents.html-document-expiry", "fields": {"questions": {"assign": ["How to auto-expire documents?", "How does Vespa iterate over the document space?", "How to use a selection with now?", "What are the side effects of using a selection with now?", "What happens when using create with expired documents?", "How to test a selection string using visit?", "What are the fields referenced in the selection expression?"]}}} -{"update": "id:open-p:paragraph::open/en/documents.html-processing-documents", "fields": {"questions": {"assign": ["What is Document Processing?", "What can Document Processing do?", "What is DocumentAccess?", "What are examples of processing PUTs or UPDATEs?", "What is OutLinksDocumentProcessor?", "What is the VespaDocSystemTest used for?"]}}} -{"update": "id:open-p:paragraph::open/en/elasticity.html-", "fields": {"questions": {"assign": ["What are Vespa clusters?", "How are documents managed?", "How to remove nodes from a content cluster?"]}}} -{"update": "id:open-p:paragraph::open/en/elasticity.html-adding-nodes", "fields": {"questions": {"assign": ["How to add or remove nodes?", "What happens when a new node is added?", "How are buckets moved?", "What is the node sequence used for?", "Can multiple nodes be added at once?", "What happens to buckets when a new node is added?"]}}} -{"update": "id:open-p:paragraph::open/en/elasticity.html-removing-nodes", "fields": {"questions": {"assign": ["What happens when a node fails?"]}}} -{"update": "id:open-p:paragraph::open/en/elasticity.html-grouped-distribution", "fields": {"questions": {"assign": ["What is a group?", "What is cluster upgrade?", "How can groups improve query throughput?", "What is topology in Vespa?", "How can redundancy be ensured using groups?", "How can group sizes be tuned?", "What are the benefits of using groups in content clusters?"]}}} -{"update": "id:open-p:paragraph::open/en/elasticity.html-topology-change", "fields": {"questions": {"assign": ["What is Vespa's elasticity feature?", "Can topology be changed without disruption?", "How does Vespa auto-redistribute documents?", "What is the recommended way to increase capacity?", "Can current nodes be moved to new groups?", "How should nodes be configured when starting from non-grouped?"]}}} -{"update": "id:open-p:paragraph::open/en/elasticity.html-buckets", "fields": {"questions": {"assign": ["What are buckets in Vespa?", "How are documents managed in Vespa?", "What happens when bucket replicas are out of sync?", "What are tombstones in Vespa?", "What triggers bucket split or join?", "Does Vespa index data in separate buckets?", "What is the role of buckets in content management?", "Where can I read more about buckets in Vespa?"]}}} -{"update": "id:open-p:paragraph::open/en/elasticity.html-ideal-state-distribution-algorithm", "fields": {"questions": {"assign": ["What is the ideal state distribution algorithm?", "What algorithm does ideal state distribution use?", "What is the CRUSH algorithm?", "How are nodes assigned to buckets?", "What is the bucket's ideal state?", "How does ideal state distribution limit document movement?", "What is the purpose of the random number generator in ideal state distribution?", "What is the redundancy value used in ideal state distribution?"]}}} -{"update": "id:open-p:paragraph::open/en/elasticity.html-consistency", "fields": {"questions": {"assign": ["How is consistency maintained?", "What is inconsistent bucket splitting?", "How are checksums calculated?", "What happens during inconsistent bucket splitting?", "What is the highest maintenance priority?", "What happens when all buckets are split or joined back?"]}}} -{"update": "id:open-p:paragraph::open/en/elasticity.html-further-reading", "fields": {"questions": {"assign": ["What is the 'ready' state?"]}}} -{"update": "id:open-p:paragraph::open/en/embedding.html-", "fields": {"questions": {"assign": ["What is embedding in Vespa?", "How are embeddings used for ML serving in Vespa?", "What is the purpose of mapping unstructured data to a vector space?", "How are vectors used as input in neural nets?"]}}} -{"update": "id:open-p:paragraph::open/en/embedding.html-provided-embedders", "fields": {"questions": {"assign": ["What are Vespa embedders?", "How does Vespa reduce latency?", "Why use Vespa embedder functionality?"]}}} -{"update": "id:open-p:paragraph::open/en/embedding.html-bertbase-embedder", "fields": {"questions": {"assign": ["What is an embedder?", "What is WordPiece?", "How to export a sentence-transformer model to ONNX format?", "What is the BertBase embedder?", "How to configure the BertBase embedder?", "What pooling strategy does BertBase embedder use by default?", "How to supply model files for the BertBase embedder?"]}}} -{"update": "id:open-p:paragraph::open/en/embedding.html-sentencepiece-embedder", "fields": {"questions": {"assign": ["What is SentencePiece?", "What does SentencePiece do?", "What languages does SentencePiece support?", "How is SentencePiece implemented?", "How can SentencePiece be used in Vespa?", "What is the configuration definition of SentencePiece?", "What is the SentencePiece embedder?", "Where can I find the configuration definition of SentencePiece?"]}}} -{"update": "id:open-p:paragraph::open/en/embedding.html-wordpiece-embedder", "fields": {"questions": {"assign": ["What is WordPiece?", "What is a native Java implementation?", "How do you use WordPiece in Vespa?", "What can you use the WordPiece tensor for?"]}}} -{"update": "id:open-p:paragraph::open/en/embedding.html-embedding-a-query-text", "fields": {"questions": {"assign": ["What is an embedder?", "How is an embedder used?", "How do you embed text?", "What is the format for embedding a text?", "What is the output of the embed function?", "Can multiple embedders be used?", "What is an example of using multiple embedders?", "What is the format for a Vespa query with embedding?"]}}} -{"update": "id:open-p:paragraph::open/en/embedding.html-embedding-a-document-field", "fields": {"questions": {"assign": ["How can I convert string fields into embedding vectors?", "What is the embed function?", "Can I use multiple input fields for embedding vectors?", "How are array indexes used for embeddings?", "How do I store produced vector embeddings?"]}}} -{"update": "id:open-p:paragraph::open/en/embedding.html-using-an-embedder-from-java", "fields": {"questions": {"assign": ["What is ComponentRegistry?", "How to use embedders in Java components?"]}}} -{"update": "id:open-p:paragraph::open/en/embedding.html-custom-embedders", "fields": {"questions": {"assign": ["What is com.yahoo.language.process.Embedder?", "How can I define a custom embedder?"]}}} -{"update": "id:open-p:paragraph::open/en/embedding.html-examples", "fields": {"questions": {"assign": ["What is commerce-product-ranking?", "Where can I find the embedders example?", "What is the sample application for multiple embedders?", "What do the sample applications demonstrate?"]}}} -{"update": "id:open-p:paragraph::open/en/embedding.html-troubleshooting-model-signature", "fields": {"questions": {"assign": ["What is a model signature?", "How to load ONNX models?", "How to export sentence-transformer model to ONNX format?", "What are common issues when loading ONNX models?", "How to analyze model signature using Vespa?", "What happens if the model contains incorrect input or output signatures?"]}}} -{"update": "id:open-p:paragraph::open/en/faq.html-does-vespa-support-a-flexible-ranking-score%3F", "fields": {"questions": {"assign": ["What is a feature vector?", "How can ranking data be supplied in queries?", "How are weights used in ranking?", "What is compared in Vespa's ranking process?"]}}} -{"update": "id:open-p:paragraph::open/en/faq.html-where-would-customer-specific-weightings-be-stored%3F", "fields": {"questions": {"assign": ["Can Vespa store customer data?", "How to store customer data?", "What is album-recommendation-docproc?", "Where can I find example code for looking up data in Vespa?"]}}} -{"update": "id:open-p:paragraph::open/en/faq.html-how-to-create-a-tensor-on-the-fly-in-the-ranking-expression%3F", "fields": {"questions": {"assign": ["How do you create a tensor?", "What are `tensorFrom` functions?", "Where can I find `tensorFrom` functions?", "Can you create a tensor from arrays?", "Can you create a tensor from weighted sets?"]}}} -{"update": "id:open-p:paragraph::open/en/faq.html-how-to-set-a-dynamic-(query-time)-ranking-drop-threshold%3F", "fields": {"questions": {"assign": ["How to use if statement in ranking expression?", "How to drop low scores?", "What is first-phase score?", "What is rank-score-drop-limit?", "What is query(threshold)?"]}}} -{"update": "id:open-p:paragraph::open/en/faq.html-does-vespa-support-early-termination-of-matching-and-ranking%3F", "fields": {"questions": {"assign": ["What is capped numeric range search?", "What are the requirements for using capped range queries?", "What does match-phase support?", "What are the drawbacks of using capped range queries?"]}}} -{"update": "id:open-p:paragraph::open/en/faq.html-what-limits-apply-to-json-document-size%3F", "fields": {"questions": {"assign": ["What is the memory limit for loading documents?", "What is the limit for huge documents?"]}}} -{"update": "id:open-p:paragraph::open/en/faq.html-does-a-whole-document-need-to-be-updated-and-re-indexed%3F", "fields": {"questions": {"assign": ["What are partial updates?"]}}} -{"update": "id:open-p:paragraph::open/en/faq.html-what-acid-guarantees-if-any-does-vespa-provide-for-single-writes-/-updates-/-deletes-vs-batch-operations-etc%3F", "fields": {"questions": {"assign": ["What is Vespa Consistency Model?", "Is Vespa transactional?", "What are ACID guarantees?", "Does Vespa have eventual consistency?", "What is the trade-off for high performance?", "Is eventual consistency configurable?"]}}} -{"update": "id:open-p:paragraph::open/en/faq.html-does-vespa-support-wildcard-fields%3F", "fields": {"questions": {"assign": ["What are wildcard fields?", "How can we store wildcard fields?", "What is the workaround for wildcard fields?", "Are wildcard fields supported in Vespa?", "How are map values stored?"]}}} -{"update": "id:open-p:paragraph::open/en/faq.html-how-to-auto-expire-documents-/-set-up-garbage-collection%3F", "fields": {"questions": {"assign": ["What does the selection criterion do?", "How can you set a selection criterion?", "What is a document expiry?", "How can you purge documents?", "What is the expression for purging documents?", "What is selected by the criterion?", "Where can you read more about document expiry?"]}}} -{"update": "id:open-p:paragraph::open/en/faq.html-how-to-increase-redundancy-and-track-data-migration-progress%3F", "fields": {"questions": {"assign": ["What is changing redundancy?", "Is changing redundancy a safe change?", "How long does it take to migrate?", "What should I look at to monitor progress of migration?", "How can I track progress of migration?", "Will nodes be affected during data sync?", "Will query coverage be affected during data sync?", "How do I monitor distance to ideal state?"]}}} -{"update": "id:open-p:paragraph::open/en/faq.html-how-does-namespace-relate-to-schema%3F", "fields": {"questions": {"assign": ["What is a namespace?", "Can namespace be searched using query API?", "What is the purpose of namespace?", "How is namespace used for document selection?", "Where can I find documentation about namespace?", "Can namespace be used for document visiting?"]}}} -{"update": "id:open-p:paragraph::open/en/faq.html-visiting-does-not-dump-all-documents,-and/or-hangs.", "fields": {"questions": {"assign": ["What are the causes?"]}}} -{"update": "id:open-p:paragraph::open/en/faq.html-is-hierarchical-facets-supported%3F", "fields": {"questions": {"assign": ["What are facets in Vespa?"]}}} -{"update": "id:open-p:paragraph::open/en/faq.html-is-filters-supported%3F", "fields": {"questions": {"assign": ["How to add filters in YQL?", "Can query terms be annotated?", "How to not highlight filter results?"]}}} -{"update": "id:open-p:paragraph::open/en/faq.html-how-to-query-for-similar-items%3F", "fields": {"questions": {"assign": ["How does ANN differ from full precision?", "How can filters limit the neighbor candidate set?"]}}} -{"update": "id:open-p:paragraph::open/en/faq.html-does-vespa-support-stop-word-removal%3F", "fields": {"questions": {"assign": ["What is stop-word?", "Does Vespa have stop-words?"]}}} -{"update": "id:open-p:paragraph::open/en/faq.html-how-to-extract-more-than-400-hits-/-query-and-get-all-documents%3F", "fields": {"questions": {"assign": ["What is the 'maxHits' parameter?", "What is the 'maxHits' limit?", "How to allow for more than 400 hits in a query?", "What happens if maxHits is increased beyond the configured limit?", "How can a query be made cheaper?"]}}} -{"update": "id:open-p:paragraph::open/en/faq.html-how-to-make-a-sub-query-to-get-data-to-enrich-the-query,-like-get-a-user-profile%3F", "fields": {"questions": {"assign": ["What is UserProfileSearcher?", "How to create new query?", "What is fill() method?"]}}} -{"update": "id:open-p:paragraph::open/en/faq.html-is-it-possible-to-query-vespa-using-a-list-of-document-ids%3F", "fields": {"questions": {"assign": ["What is multi-lookup set filtering?", "What is weightedset-example?", "How do you select multiple IDs with YQL?"]}}} -{"update": "id:open-p:paragraph::open/en/faq.html-how-to-query-documents-where-one-field-matches-any-values-in-a-list%3F-similar-to-using-sql-in-operator", "fields": {"questions": {"assign": ["What is multi-lookup set filtering?", "What is a weighted set?"]}}} -{"update": "id:open-p:paragraph::open/en/faq.html-how-to-count-hits-/-all-documents-without-returning-results%3F", "fields": {"questions": {"assign": ["What does the query 'select * from doc where true' do?", "How to count all documents in Vespa?", "What is the alternative to 'select * from doc where true' if we only need the count?", "How to make the query less expensive to run?", "What is hitcountestimate=true used for?"]}}} -{"update": "id:open-p:paragraph::open/en/faq.html-must-all-fields-in-a-fieldset-have-the-same-type%3F", "fields": {"questions": {"assign": ["What causes a deployment warning?", "What is a fieldset in Vespa?", "Why is tokenization important in Vespa?", "What is recall in Vespa?"]}}} -{"update": "id:open-p:paragraph::open/en/faq.html-how-is-the-query-timeout-computed%3F", "fields": {"questions": {"assign": ["Where can I find query timeout details?", "What is the Query API Guide?"]}}} -{"update": "id:open-p:paragraph::open/en/faq.html-how-does-backslash-escapes-work%3F", "fields": {"questions": {"assign": ["What is the purpose of backslash in YQL?", "How to query literal backslash in YQL?", "What happens if backslashes are not escaped in YQL?", "How does Vespa CLI deal with backslashes in YQL query?", "How to escape backslashes in YQL for Vespa CLI?", "Do log messages and JSON results escape backslashes in YQL?"]}}} -{"update": "id:open-p:paragraph::open/en/faq.html-is-it-possible-to-have-multiple-select-statements-in-a-single-call-(subqueries)%3F", "fields": {"questions": {"assign": ["What is the issue with using OR conditions?", "How can two select queries be run in parallel?", "What is an AsyncExecution in Vespa?", "How to split an incoming query?"]}}} -{"update": "id:open-p:paragraph::open/en/faq.html-is-it-possible-to-query-for-the-number-of-elements-in-an-array", "fields": {"questions": {"assign": ["Is efficient searching possible for arrays?"]}}} -{"update": "id:open-p:paragraph::open/en/faq.html-is-it-possible-to-query-for-fields-with-nan/no-value-set/null/none", "fields": {"questions": {"assign": ["Is there a way to query for not having a value?", "How does the visiting API support queries?"]}}} -{"update": "id:open-p:paragraph::open/en/faq.html-how-to-debug-document-processing-chain-configuration%3F", "fields": {"questions": {"assign": ["What is the configuration combination?", "What is feed troubleshooting?"]}}} -{"update": "id:open-p:paragraph::open/en/faq.html-i-feed-documents-with-no-error,-but-they-are-not-in-the-index", "fields": {"questions": {"assign": ["What happens to expired documents?"]}}} -{"update": "id:open-p:paragraph::open/en/faq.html-how-to-feed-many-files,-avoiding-429-error%3F", "fields": {"questions": {"assign": ["How can 429 response code occur?", "What is high throughput?"]}}} -{"update": "id:open-p:paragraph::open/en/faq.html-does-vespa-support-addition-of-flexible-nlp-processing-for-documents-and-search-queries%3F", "fields": {"questions": {"assign": ["What does Vespa support?", "What is NER?", "What is word sense disambiguation?", "What is intent detection?"]}}} -{"update": "id:open-p:paragraph::open/en/faq.html-does-vespa-support-customization-of-the-inverted-index%3F", "fields": {"questions": {"assign": ["What is a custom tokenizer?", "What can be controlled in Java code?", "What is the purpose of creating a new index?"]}}} -{"update": "id:open-p:paragraph::open/en/faq.html-does-vespa-provide-support-for-text-extraction%3F", "fields": {"questions": {"assign": ["Does Vespa provide text extraction?"]}}} -{"update": "id:open-p:paragraph::open/en/faq.html-how-to-do-text-search-in-an-imported-field%3F", "fields": {"questions": {"assign": ["What are imported fields?", "What are the limitations of imported fields match modes?", "What is the parent-child mode?", "Can parent-child mode be searched by parent column?"]}}} -{"update": "id:open-p:paragraph::open/en/faq.html-why-is-closeness-1-for-all-my-vectors%3F", "fields": {"questions": {"assign": ["What is distance-metric?", "What are normalized vectors?", "What is capped to 0?", "What is sbert.net?", "How to switch distance-metric?", "What is closeness?", "What causes negative distances?"]}}} -{"update": "id:open-p:paragraph::open/en/faq.html-is-python-plugins-supported-/-is-there-a-scripting-language%3F", "fields": {"questions": {"assign": ["What is a plugin?", "What is JVM?", "What is jython?", "What is painless?", "Does Vespa support painless?"]}}} -{"update": "id:open-p:paragraph::open/en/faq.html-how-can-i-batch-get-documents-by-ids-in-a-searcher", "fields": {"questions": {"assign": ["What is com.yahoo.documentapi.DocumentAccess?", "How to get documents by id?", "How to use DocumentAccess component?"]}}} -{"update": "id:open-p:paragraph::open/en/faq.html-what-is-the-latency-of-documents-being-ingested-vs-indexed-and-available-for-search%3F", "fields": {"questions": {"assign": ["What is Vespa's indexing core?", "What are Vespa's latency times?"]}}} -{"update": "id:open-p:paragraph::open/en/faq.html-is-there-a-batch-ingestion-mode,-what-limits-apply%3F", "fields": {"questions": {"assign": ["What is batch ingestion in Vespa?", "What are the core features of Vespa?", "What is serving elasticity in Vespa?", "What is the sub-second indexing latency in Vespa?", "What is the feed sizing guide?", "How can you tune Vespa to meet your requirements?", "What use-cases in production do high throughput updates to large parts of the document set?"]}}} -{"update": "id:open-p:paragraph::open/en/faq.html-can-the-index-support-up-to-512-gb-index-size-in-memory%3F", "fields": {"questions": {"assign": ["What is the content node implementation language?", "Is the content node memory constrained?"]}}} -{"update": "id:open-p:paragraph::open/en/faq.html-get-request-for-a-document-when-document-is-not-in-sync-in-all-the-replica-nodes%3F", "fields": {"questions": {"assign": ["What happens if replicas are in sync?", "How is the primary content node determined?", "How is the request sent to multiple nodes?", "What is replica metadata?", "What is bucket with 3 replicas?", "Why would the request not be sent to node B?", "What is the purpose of replicas?"]}}} -{"update": "id:open-p:paragraph::open/en/faq.html-how-to-keep-indexes-in-memory%3F", "fields": {"questions": {"assign": ["What is the difference between index and attribute?", "What is tokenized matching?", "What is the memory footprint during index switch?", "What are the limitations of populate option?", "How can we diagnose what is mapped using pmap?"]}}} -{"update": "id:open-p:paragraph::open/en/faq.html-is-memory-freed-when-deleting-documents%3F", "fields": {"questions": {"assign": ["How can I increase capacity on content nodes?", "What happens when I delete a document?", "What is a tombstone in Vespa?", "What is the purpose of feed block in Vespa?", "How are deleted documents represented in Vespa?", "What is removed-db-prune-age in Vespa?", "What are the factors affecting memory release in Vespa?"]}}} -{"update": "id:open-p:paragraph::open/en/faq.html-can-one-do-a-partial-deploy-to-the-config-server-/-update-the-schema-without-deploying-all-the-node-configs%3F", "fields": {"questions": {"assign": ["What is the deployment API?", "What does the web service API allow?", "How can you create an edit session?", "When is the web service API useful?", "How does the config server handle resend requests?", "What is deploy-rest-api-v2.html used for?"]}}} -{"update": "id:open-p:paragraph::open/en/faq.html-how-fast-can-nodes-be-added-and-removed-from-a-running-cluster%3F", "fields": {"questions": {"assign": ["What is elasticity in Vespa?", "How does Vespa ensure minimal serving impact during node changes?", "What does ideal data distribution mean?"]}}} -{"update": "id:open-p:paragraph::open/en/faq.html-should-vespa-api-search-calls-be-load-balanced-or-does-vespa-do-this-automatically%3F", "fields": {"questions": {"assign": ["What is a stateless Java container cluster?", "How can I load balance requests?", "What is Vespa Cloud?"]}}} -{"update": "id:open-p:paragraph::open/en/faq.html-supporting-index-partitions", "fields": {"questions": {"assign": ["Why is topology important for Vespa?"]}}} -{"update": "id:open-p:paragraph::open/en/faq.html-can-a-running-cluster-be-upgraded-with-zero-downtime%3F", "fields": {"questions": {"assign": ["Does Vespa Cloud do automated upgrades?", "How often are upgrades done in Vespa Cloud?", "What is the procedure for live upgrades?"]}}} -{"update": "id:open-p:paragraph::open/en/faq.html-can-vespa-be-deployed-multi-region%3F", "fields": {"questions": {"assign": ["What is Vespa Cloud?", "What is the Vespa Cloud global endpoint?", "Is there an auto-sync between zones in Vespa Cloud?", "How many zones can Vespa Cloud support?", "What is the reference for Vespa Cloud zones?"]}}} -{"update": "id:open-p:paragraph::open/en/faq.html-can-vespa-serve-an-offline-index%3F", "fields": {"questions": {"assign": ["What is building indexes offline?", "Why is it not recommended?"]}}} -{"update": "id:open-p:paragraph::open/en/faq.html-does-vespa-give-us-any-tool-to-browse-the-index-and-attribute-data%3F", "fields": {"questions": {"assign": ["How can documents be dumped?", "What is dumping-data?", "Where can I find a sample script for dumping data?"]}}} -{"update": "id:open-p:paragraph::open/en/faq.html-what-is-the-response-when-data-is-written-only-on-some-nodes-and-not-on-all-replica-nodes-(based-on-the-redundancy-count-of-the-content-cluster)%3F", "fields": {"questions": {"assign": ["What triggers a failure response?", "When is a failure response given?"]}}} -{"update": "id:open-p:paragraph::open/en/faq.html-when-the-doc-is-not-written-to-some-nodes,-will-the-document-become-available-due-to-replica-reconciliation%3F", "fields": {"questions": {"assign": ["Will it be available?"]}}} -{"update": "id:open-p:paragraph::open/en/faq.html-does-vespa-provide-soft-delete-functionality%3F", "fields": {"questions": {"assign": ["How to mark documents as deleted?", "What is the 'deleted' attribute?", "How does fast-search work with deleted documents?"]}}} -{"update": "id:open-p:paragraph::open/en/faq.html-can-we-configure-a-grace-period-for-bucket-distribution-so-that-buckets-are-not-redistributed-as-soon-as-a-node-goes-down%3F", "fields": {"questions": {"assign": ["What is transition-time?", "Where can transition-time be set?", "What does setting transition-time do?"]}}} -{"update": "id:open-p:paragraph::open/en/faq.html-what-is-the-recommended-redundant/searchable-copies-config-when-using-grouping-distribution%3F", "fields": {"questions": {"assign": ["What is grouped distribution?", "How does grouped distribution reduce latency?", "What is redundancy?", "What are searchable copies?", "What happens when redundancy and searchable-copies are set equal to the number of groups?"]}}} -{"update": "id:open-p:paragraph::open/en/faq.html-how-to-set-up-for-disaster-recovery-/-backup%3F", "fields": {"questions": {"assign": ["What is issue 17898 about?"]}}} -{"update": "id:open-p:paragraph::open/en/faq.html-how-to-check-vespa-version-for-a-running-instance%3F", "fields": {"questions": {"assign": ["How do I find Vespa version?"]}}} -{"update": "id:open-p:paragraph::open/en/faq.html-the-endpoint-does-not-come-up-after-deployment", "fields": {"questions": {"assign": ["How to check Vespa container errors?", "What is Vespa-logfmt?"]}}} -{"update": "id:open-p:paragraph::open/en/faq.html-starting-vespa-using-docker-on-m1-fails", "fields": {"questions": {"assign": ["What makes Docker run fail on M1?", "How to fix Docker on M1?"]}}} -{"update": "id:open-p:paragraph::open/en/faq.html-deployment-fails-/-nothing-is-listening-on-19071", "fields": {"questions": {"assign": ["What is ZooKeeper quorum?", "How to validate container memory?"]}}} -{"update": "id:open-p:paragraph::open/en/faq.html-startup-problems-in-multinode-kubernetes-cluster---readinessprobe-using-19071-fails", "fields": {"questions": {"assign": ["Why does the Config Server cluster fail to start?", "What is the role of ZooKeeper in Config Server cluster?", "How does the ZooKeeper cluster wait for hosts on the network?", "What is catch 22 in the context of Config Server cluster?", "Where can I find troubleshooting guidelines for Config Server cluster?", "What is sampleapp troubleshooting?"]}}} -{"update": "id:open-p:paragraph::open/en/performance/feature-tuning.html-", "fields": {"questions": {"assign": ["What is the main focus?", "What is container tuning?"]}}} -{"update": "id:open-p:paragraph::open/en/performance/feature-tuning.html-attribute-v.s-index", "fields": {"questions": {"assign": ["What does the attribute documentation summarize?", "What is the procedure for changing attribute to index and vice versa?"]}}} -{"update": "id:open-p:paragraph::open/en/performance/feature-tuning.html-when-to-use-fast-search-for-attribute-fields", "fields": {"questions": {"assign": ["What does fast-search enable?", "When to use fast-search?", "What is a hit count estimate?", "How does Vespa build query execution plan?", "What is the impact of using fast-search?", "How to change fast-search aspect of an attribute?", "What is the optimization for sorting on a single-value numeric attribute?"]}}} -{"update": "id:open-p:paragraph::open/en/performance/feature-tuning.html-indexing-uuids", "fields": {"questions": {"assign": ["What is the default match mode for string fields?", "What happens when Vespa indexes a UUID as tokens?", "How can you configure a field to treat UUID as a single token?"]}}} -{"update": "id:open-p:paragraph::open/en/performance/feature-tuning.html-parent-child-and-search-performance", "fields": {"questions": {"assign": ["What is the rank:filter setting?", "What does visibility-delay do?", "How can imported attribute fields be searched?", "What is the impact of rank:filter?", "What is the importance of visibility-delay?", "How does ranking impact posting list granularity?"]}}} -{"update": "id:open-p:paragraph::open/en/performance/feature-tuning.html-ranking-and-ml-model-inferences", "fields": {"questions": {"assign": ["What is the first-phase ranking function?", "Why use the second phase ranking step?", "What is the inner scoring function?", "How does WAND operator find top k documents?"]}}} -{"update": "id:open-p:paragraph::open/en/performance/feature-tuning.html-multi-lookup---set-filtering", "fields": {"questions": {"assign": ["What is set filter?"]}}} -{"update": "id:open-p:paragraph::open/en/performance/feature-tuning.html-document-summaries---hits", "fields": {"questions": {"assign": ["What is summary cache?", "What are explicit document summaries?", "What is the benefit of dedicated in-memory summaries?", "How are Vespa document summaries stored?", "What is the hits fetching performance guide?", "How does the summary store avoid disk reads?", "What is the purpose of summary chunk decompression?"]}}} -{"update": "id:open-p:paragraph::open/en/performance/feature-tuning.html-boolean,-numeric,-text-attribute", "fields": {"questions": {"assign": ["What is a boolean field?", "When to use string attributes?", "Why use numeric attributes?", "What is the rule of thumb for attribute field type selection?"]}}} -{"update": "id:open-p:paragraph::open/en/performance/feature-tuning.html-tensor-ranking", "fields": {"questions": {"assign": ["What is the ranking workload?", "Why is understanding computational cost important?", "How does tensor size affect query cost?"]}}} -{"update": "id:open-p:paragraph::open/en/performance/feature-tuning.html-memory", "fields": {"questions": {"assign": ["What is a dot product?", "What is tensor quantization?"]}}} -{"update": "id:open-p:paragraph::open/en/performance/feature-tuning.html-compute", "fields": {"questions": {"assign": ["What is fast-rank?", "How can fast-rank optimize ranking?", "What is the tradeoff of using fast-rank?", "What is the Tensor Playground?", "How can Tensor Playground help with optimizing ranking functions?", "How to avoid temporary objects when optimizing ranking with tensors?"]}}} -{"update": "id:open-p:paragraph::open/en/performance/feature-tuning.html-multiphase-ranking", "fields": {"questions": {"assign": ["What is multiphase ranking?", "What are the benefits of multiphase ranking?", "How does multiphase ranking save resources?", "What is the blog post series about?"]}}} -{"update": "id:open-p:paragraph::open/en/performance/feature-tuning.html-cell-value-types", "fields": {"questions": {"assign": ["What is the default tensor cell type?", "What are the precision and memory considerations of double cell type?", "What is the memory cost of a float vector with 1000 dimensions?", "When should bfloat16 be used?", "What is the computational overhead of using bfloat16 compared to 32-bit float?", "What is int8 used for in machine-learning?", "What is the range of int8 cell value type?"]}}} -{"update": "id:open-p:paragraph::open/en/performance/feature-tuning.html-inner/outer-products", "fields": {"questions": {"assign": ["What is an inner product?", "What is an outer product?", "What is DenseDotProductFunction?", "What is a DenseSimpleExpandFunction?", "What is Inject in Vespa tensor functions?"]}}} -{"update": "id:open-p:paragraph::open/en/performance/feature-tuning.html-mapped-lookups", "fields": {"questions": {"assign": ["What is mapped dimension?", "What is MappedLookup?", "What is a slice in Vespa?", "What is considered a mapped lookup?", "What is Tensor Playground?"]}}} -{"update": "id:open-p:paragraph::open/en/performance/feature-tuning.html-three-way-dot-product---mapped", "fields": {"questions": {"assign": ["What is three-way mapped dot product?"]}}} -{"update": "id:open-p:paragraph::open/en/performance/feature-tuning.html-three-way-dot-product---mixed", "fields": {"questions": {"assign": ["What is dot product?", "What is Mixed112DotProduct?"]}}} -{"update": "id:open-p:paragraph::open/en/features.html-what-is-vespa%3F", "fields": {"questions": {"assign": ["What is Vespa?", "What kind of queries can Vespa execute?", "What is the response time of Vespa?", "What happens in case of hardware failures?", "Is Vespa self-repairing and dynamic?"]}}} -{"update": "id:open-p:paragraph::open/en/features.html-features", "fields": {"questions": {"assign": ["What does this section provide?"]}}} -{"update": "id:open-p:paragraph::open/en/features.html-data-and-writes", "fields": {"questions": {"assign": ["What types of data can be used in document data schemas?", "How is data replicated in Vespa?", "What is the sustained volume of writes per node per second?", "How are write operations processed?", "How is data written in Vespa?", "Can data be streamed out of the system?", "What happens in case of data corruption?"]}}} -{"update": "id:open-p:paragraph::open/en/features.html-queries", "fields": {"questions": {"assign": ["What can queries contain?", "How fast are query response times?"]}}} -{"update": "id:open-p:paragraph::open/en/features.html-ranking-and-inference", "fields": {"questions": {"assign": ["What are scalar functions?", "What text ranking features are provided?", "What are other ranking features?"]}}} -{"update": "id:open-p:paragraph::open/en/features.html-organizing-data-and-presenting-results", "fields": {"questions": {"assign": ["How are matches aggregated?", "What is keyword highlighting?", "What kind of data can be aggregated?", "Can matches be grouped by numerical bucket?", "What is included in the final result?"]}}} -{"update": "id:open-p:paragraph::open/en/features.html-configuration-and-operations", "fields": {"questions": {"assign": ["Can an application package be changed while system is live?"]}}} -{"update": "id:open-p:paragraph::open/en/federation.html-", "fields": {"questions": {"assign": ["What is federation?", "What is a federated search service?", "What are the main tasks in creating a federation solution?", "What does the container aid with in federation?", "What are some use cases for federation?", "What is the com.yahoo.search.federation Javadoc?", "What is a pure federation platform?"]}}} -{"update": "id:open-p:paragraph::open/en/federation.html-configuring-providers", "fields": {"questions": {"assign": ["What is a provider?", "How to configure a provider?", "How to exclude searchers from a provider?"]}}} -{"update": "id:open-p:paragraph::open/en/federation.html-configuring-sources", "fields": {"questions": {"assign": ["What is a source in Vespa?", "What is a source search chain?", "How does a source inherit searchers?", "What is the purpose of a source?", "Is the provider modified by adding sources?", "How can Vespa reuse connections to data providers?", "How does Vespa modify a query for a source search chain?"]}}} -{"update": "id:open-p:paragraph::open/en/federation.html-multiple-providers-per-source", "fields": {"questions": {"assign": ["What is a source search chain?", "What is a leader source search chain?", "What is a participant source search chain?", "What is `idref` attribute used for?", "How are participants and leaders different?", "What is the default behaviour when federating to a source?", "How to specify a participant source instead of the leader?"]}}} -{"update": "id:open-p:paragraph::open/en/federation.html-built-in-federation", "fields": {"questions": {"assign": ["What are the built-in search chains?", "What is the name of the federation searcher?", "What is the default federation configuration?", "What providers can the federation searcher be configured to federate to?", "Are custom sources allowed for federation searchers?", "What is the flexibility of configuring a custom federation searcher?"]}}} -{"update": "id:open-p:paragraph::open/en/federation.html-inheriting-default-sources", "fields": {"questions": {"assign": ["What is a federation searcher?", "How do you inherit the default source set?"]}}} -{"update": "id:open-p:paragraph::open/en/federation.html-changing-content-cluster-chains", "fields": {"questions": {"assign": ["What is the purpose of modifying the search chain?", "How do you modify the search chain?", "What is a content cluster?", "What is the excludes attribute in provider?", "What is the cluster attribute in provider?"]}}} -{"update": "id:open-p:paragraph::open/en/federation.html-timeout-behavior", "fields": {"questions": {"assign": ["What is federation layer?", "What is a provider?", "What is requestTimeout?", "How do you limit provider search time?"]}}} -{"update": "id:open-p:paragraph::open/en/federation.html-non-essential-providers", "fields": {"questions": {"assign": ["What is the purpose of the ads provider?", "How can the ads provider be made optional?", "What happens if only optional providers are selected for the Federation searcher?"]}}} -{"update": "id:open-p:paragraph::open/en/federation.html-federation-options-inheritance", "fields": {"questions": {"assign": ["What are federation options?", "How do you override federation options?"]}}} -{"update": "id:open-p:paragraph::open/en/federation.html-selecting-search-chains-programmatically", "fields": {"questions": {"assign": ["What is a TargetSelector?", "How is target selector used?", "What is a FederationTarget?", "How do you modify target query?", "How do you modify target result?", "How to configure a federation searcher?"]}}} -{"update": "id:open-p:paragraph::open/en/federation.html-example:-setting-up-a-federated-service", "fields": {"questions": {"assign": ["What is a federation application?", "How is federation set up?", "What is a provider in federation?", "What is a FederationSearcher?", "How are results merged in federation?"]}}} -{"update": "id:open-p:paragraph::open/en/federation.html-selecting-sources", "fields": {"questions": {"assign": ["What is the best way to bring relevant data to a user?", "What is the main search chain?", "How should sources be selected during searching?", "What is a provider in Vespa?", "How can we reduce resource cost involved in searching?", "What is the purpose of an explicit main chain in Vespa?"]}}} -{"update": "id:open-p:paragraph::open/en/federation.html-rewriting-queries-to-individual-providers", "fields": {"questions": {"assign": ["What are provider searchers?", "What modifications may need to be made to a query?", "What is the purpose of the NewsCustomerIdSearcher class?", "How are source and provider search chains related?", "What is the difference between a source and a provider?"]}}} -{"update": "id:open-p:paragraph::open/en/federation.html-processing-results", "fields": {"questions": {"assign": ["What is the ResultBlender?", "What is the purpose of the ResultBlender?", "What are HitGroups?", "What is the purpose of SourceSelector?", "What is the page-result format?"]}}} -{"update": "id:open-p:paragraph::open/en/federation.html-unit-testing-the-result-processor", "fields": {"questions": {"assign": ["What is a mock backend?", "What is the purpose of ResultBlender?", "What is ResultBlenderTestCase?"]}}} -{"update": "id:open-p:paragraph::open/en/operations/feed-block.html-", "fields": {"questions": {"assign": ["Why are external write operations blocked?", "What causes feed blocking?", "What operations are rejected when feed is blocked?", "How to unblock feeding when resource limit is reached?", "What metrics are used to monitor resource usage?", "What is the error message returned when feeding is blocked due to resource exhaustion?", "What is attribute data structures and how does it affect feeding?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/files-processes-and-ports.html-", "fields": {"questions": {"assign": ["What are environment variables used?", "What processes run on Vespa nodes?", "What are the directories used in Vespa installation?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/files-processes-and-ports.html-directories", "fields": {"questions": {"assign": ["What is the $VESPA_HOME/bin directory for?", "What is stored in the $VESPA_HOME/conf/vespa directory?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/files-processes-and-ports.html-processes-and-ports", "fields": {"questions": {"assign": ["What is the port allocation for Vespa services?", "What is Proton process?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/files-processes-and-ports.html-system-limits", "fields": {"questions": {"assign": ["What are Vespa startup scripts?", "What is checked during startup?", "Where can I find the minimum values for startup?", "What services are checked during startup?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/files-processes-and-ports.html-core-dumps", "fields": {"questions": {"assign": ["What does chmod command do?", "How are core files written?", "What is the format of core files?", "How to create a core file?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/files-processes-and-ports.html-environment-variables", "fields": {"questions": {"assign": ["What are environment variables?", "What is VESPA_HOSTNAME?", "What is VESPA_WEB_SERVICE_PORT?", "What is VESPA_TLS_CONFIG_FILE?", "What is VESPA_CONFIGSERVER_JVMARGS?", "What is VESPA_LOG_LEVEL?"]}}} -{"update": "id:open-p:paragraph::open/en/geo-search.html-", "fields": {"questions": {"assign": ["What is a latitude?", "What is a longitude?", "How is location represented in JSON?", "How can position limit hits?", "What is the usual convention for latitude and longitude?"]}}} -{"update": "id:open-p:paragraph::open/en/geo-search.html-sample-schema-and-document", "fields": {"questions": {"assign": ["What is schema biz?", "What is document biz?", "What is the type of 'mainloc'?", "What is a possible business entry?"]}}} -{"update": "id:open-p:paragraph::open/en/geo-search.html-restrict", "fields": {"questions": {"assign": ["What is the YQL statement?", "What is the GeoLocationItem?", "How to add geo-restrictions?", "What is the curl command to restrict search using a point and distance?", "How to include a position for ranking only?", "What is a negative radius?", "How to match any position?"]}}} -{"update": "id:open-p:paragraph::open/en/geo-search.html-ranking-from-a-position-match", "fields": {"questions": {"assign": ["What is distance(mainloc).km?", "What is closeness(mainloc)?", "What are practical distances for ranking?", "What is the range of closeness(mainloc)?"]}}} -{"update": "id:open-p:paragraph::open/en/geo-search.html-useful-summary-features", "fields": {"questions": {"assign": ["How to get computed distance?", "What is the preferred way?"]}}} -{"update": "id:open-p:paragraph::open/en/geo-search.html-using-multiple-position-fields", "fields": {"questions": {"assign": ["What is the purpose of having several position attributes?", "How can you search for several position attributes?", "How can you combine several fields into a single search field?"]}}} -{"update": "id:open-p:paragraph::open/en/geo-search.html-example-with-airport-positions", "fields": {"questions": {"assign": ["What is the lat of JFK?", "Where is LAX located?", "What is GIG?", "What is the lng of NRT?"]}}} -{"update": "id:open-p:paragraph::open/en/geo-search.html-distance-to-path", "fields": {"questions": {"assign": ["What is DistanceToPath feature?", "What does DistanceToPath do?", "What is the purpose of DistanceToPath?", "How does DistanceToPath work?", "What is the nickname of DistanceToPath feature?", "What type of search corpus is required for DistanceToPath?", "What components are required for DistanceToPath to work?", "What is a rank-profile for DistanceToPath?"]}}} -{"update": "id:open-p:paragraph::open/en/geo-search.html-query-syntax", "fields": {"questions": {"assign": ["What is *DistanceToPath*?", "What are the syntax expectations when using *DistanceToPath*?", "What is the path for *DistanceToPath*?", "What is the format for supplying the path for *DistanceToPath*?"]}}} -{"update": "id:open-p:paragraph::open/en/geo-search.html-rank-profile", "fields": {"questions": {"assign": ["What is the purpose of the first component in the second rank-profile?", "How does the second component influence ranking in the second rank-profile?", "How does the second rank-profile improve over the first?"]}}} -{"update": "id:open-p:paragraph::open/en/geo-search.html-results", "fields": {"questions": {"assign": ["What is the feature's output scale?", "What is DistanceToPath feature?", "What is the normalized range for traveled?", "What does Vespa expect the parameters to be mapped by?", "What is Vespa's internal resolution?", "How do the parameters help retrieve the appropriate hits?"]}}} -{"update": "id:open-p:paragraph::open/en/getting-started-ranking.html-", "fields": {"questions": {"assign": ["What is ranking in Vespa?", "How is ranking defined?", "What is doc.sd?", "Where can I find schema documentation?", "What can be configured in doc.sd?"]}}} -{"update": "id:open-p:paragraph::open/en/getting-started-ranking.html-ranking-using-document-features-only", "fields": {"questions": {"assign": ["What is the inlinks attribute?", "What is the relevance score?"]}}} -{"update": "id:open-p:paragraph::open/en/getting-started-ranking.html-observing-values-used-in-ranking", "fields": {"questions": {"assign": ["What is the age_decay function?", "What is the num_inlinks function?", "What is the rank_score function?"]}}} -{"update": "id:open-p:paragraph::open/en/getting-started-ranking.html-ranking-with-query-features", "fields": {"questions": {"assign": ["What is document similarity?", "What is term_count field?", "How to find similar documents?", "What is term_count_similarity?", "How to use input.query()?", "How to transfer ranking features?"]}}} -{"update": "id:open-p:paragraph::open/en/getting-started-ranking.html-ranking-with-a-query-tensor", "fields": {"questions": {"assign": ["What is overlap in in-links?", "How are inlinks mapped?", "How is inlink_similarity defined?", "What is the problem of comparing two lists of links?", "How is the problem of comparing two lists of links transformed into a numerical problem?"]}}} -{"update": "id:open-p:paragraph::open/en/getting-started-ranking.html-retrieval-and-ranking", "fields": {"questions": {"assign": ["What is the purpose of splitting ranking into two phases?", "What is the purpose of num_inlinks() function?", "What is the meaning of 'keep-rank-count'?", "What is 'query(threshold)' function used for?", "How is rank-score-drop-limit used in Vespa?", "What is the significance of using an if statement to assign -1 in ranking expressions?"]}}} -{"update": "id:open-p:paragraph::open/en/getting-started-ranking.html-retrieval:-and,-or,-weakand", "fields": {"questions": {"assign": ["What is weakAnd in Vespa?", "How does weakAnd work?", "What is the relevance of weakAnd?", "What is the purpose of weakAnd?", "What is the difference between AND and weakAnd?", "What is the scoring function of weakAnd?"]}}} -{"update": "id:open-p:paragraph::open/en/getting-started-ranking.html-next-steps", "fields": {"questions": {"assign": ["What is reranking?", "Where can I read about reranking?"]}}} -{"update": "id:open-p:paragraph::open/en/getting-started.html-", "fields": {"questions": {"assign": ["How to use Vespa for ML Model Serving?"]}}} -{"update": "id:open-p:paragraph::open/en/getting-started.html-next-steps", "fields": {"questions": {"assign": ["How do I secure a Vespa installation?"]}}} -{"update": "id:open-p:paragraph::open/en/graceful-degradation.html-", "fields": {"questions": {"assign": ["What is coverage?"]}}} -{"update": "id:open-p:paragraph::open/en/graceful-degradation.html-detection", "fields": {"questions": {"assign": ["What is the degraded field?", "What is softtimeout?", "How is query coverage calculated?", "What is match-phase ranking degradation?", "What is non-ideal-state in Vespa.ai?", "How can coverage information be accessed programmatically?"]}}} -{"update": "id:open-p:paragraph::open/en/graceful-degradation.html-adaptive-node-timeout", "fields": {"questions": {"assign": ["What is adaptive coverage?", "What is minimum coverage?", "What is minimum wait setting?", "What is the max wait setting?", "What is the default Vespa timeout?"]}}} -{"update": "id:open-p:paragraph::open/en/graceful-degradation.html-match-phase-degradation", "fields": {"questions": {"assign": ["What is match-phase?", "How does match-phase work?", "What is max-hits?", "What is the purpose of match-phase?", "What is the effect of match-phase on aggregation?", "What is the content node metric for match-phase?", "Can match-phase be used with WAND operator?", "What does the graph in the document show?"]}}} -{"update": "id:open-p:paragraph::open/en/graceful-degradation.html-match-phase-tradeoffs", "fields": {"questions": {"assign": ["What is match-phase?", "How does match-phase affect latency?", "What is the impact of match-phase on filters?", "How does Vespa evaluate potential matches?", "Why can match-phase have higher latencies with filters?", "Why is total-hits reported differently with match-phase enabled?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/grouping-syntax.html-", "fields": {"questions": {"assign": ["What is the Vespa grouping guide?", "How does using a multivalued attribute affect performance?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/grouping-syntax.html-group", "fields": {"questions": {"assign": ["What is the 'group' clause?", "How do you group query results?", "What expressions can be used for grouping?", "Can groups contain subgroups?", "What are group lists and hit lists?", "How is the identity of a group held?", "How are range identities separated for bucket aggregation?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/grouping-syntax.html-multivalue-attributes", "fields": {"questions": {"assign": ["What is a multivalue attribute?", "What are the grouping options for maps?", "Can tensors be used in grouping?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/grouping-syntax.html-order-/-max", "fields": {"questions": {"assign": ["What is grouping order?", "How does multi-level grouping work?", "What is locale aware sorting?", "Which aggregates allow ordering?", "What is the default ordering?", "What is `max` and `order`?", "How does `order` affect group ordering?", "What aggregation functions does `order` affect?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/grouping-syntax.html-continuations", "fields": {"questions": {"assign": ["What are continuations?", "How are continuations used?", "What is pagination in Vespa?", "How do continuations enable pagination?", "What is the role of 'this' continuation in pagination?", "What are the types of continuations?", "How do I programmatically work with grouping in Vespa?", "Where can I find examples of pagination using continuations in Vespa?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/grouping-syntax.html-labels", "fields": {"questions": {"assign": ["What is the `each` keyword used for?", "How can a label be assigned?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/grouping-syntax.html-precision", "fields": {"questions": {"assign": ["What is precision() function?", "How does precision affect accuracy?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/grouping-syntax.html-query-parameters", "fields": {"questions": {"assign": ["What are relevant query parameters?", "What is groupingSessionCache used for?", "What is grouping.defaultMaxGroups?", "What is grouping.defaultMaxHits?", "What is grouping.globalMaxGroups?", "What is grouping.defaultPrecisionFactor?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/grouping-syntax.html-grouping-session-cache", "fields": {"questions": {"assign": ["What is the session cache?", "What does the session cache do?", "How does session cache work?", "Why use session cache for grouping?", "What is the drawback of multi-level grouping?", "How does session cache improve performance?", "What are intermediate grouping results?", "What is the purpose of session cache in Vespa?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/grouping-syntax.html-aggregators", "fields": {"questions": {"assign": ["What are grouping aggregators?", "What is HyperLogLog++?", "What does 'output(sum())' do?", "What is the result of 'max'myarray' on a multivalued field?", "What is the result type when all arguments are numeric?", "What is the 'summary' aggregator used for?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/grouping-syntax.html-expressions", "fields": {"questions": {"assign": ["What are arithmetic expressions?", "What is interpolatedlookup?", "What is zcurve.x?", "What is math.cosh?", "What is the usage of time expressions?", "What is the usage of tolong type conversion expression?", "What is relation between reversed and sort expression?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/grouping-syntax.html-grouping-language-grammar", "fields": {"questions": {"assign": ["What is a math value in grouping?"]}}} -{"update": "id:open-p:paragraph::open/en/grouping.html-", "fields": {"questions": {"assign": ["What is Vespa grouping language?", "What does a grouping statement do?", "What is the Grouping reference?", "What fields can be used in grouping?", "What is pagination?"]}}} -{"update": "id:open-p:paragraph::open/en/grouping.html-the-grouping-language-structure", "fields": {"questions": {"assign": ["What are the operations in grouping?", "How does Vespa execute grouping?", "What is a grouping specification?", "What is the output of a grouping?", "What is the structure of the computation?", "What is the cost of a grouping query?"]}}} -{"update": "id:open-p:paragraph::open/en/grouping.html-grouping-by-example", "fields": {"questions": {"assign": ["What item did Brown buy last?", "How much did Smith spend?", "Who purchased the oil pan?", "What did Jones buy on September 8th?"]}}} -{"update": "id:open-p:paragraph::open/en/grouping.html-basic-grouping", "fields": {"questions": {"assign": ["What query returns sum(price) grouped by customer?", "What query returns sum(price) grouped by date?"]}}} -{"update": "id:open-p:paragraph::open/en/grouping.html-ordering-and-limiting-groups", "fields": {"questions": {"assign": ["What is the purpose of ordering groups?", "How is the number of returned groups limited?", "What is the grouping.defaultMaxGroups query parameter?", "What is precision in grouping?", "What happens if precision is not specified?", "What is the defaultPrecisionFactor query parameter?", "In what scenario is precision not required?", "What is the exception to precision requirement?"]}}} -{"update": "id:open-p:paragraph::open/en/grouping.html-hits-per-group", "fields": {"questions": {"assign": ["How to limit the number of hits per group?", "What is 'grouping.defaultMaxHits'?", "Can defaultMaxHits be overridden?", "What happens when max is explicitly specified?", "How to retrieve all hits with max?", "What query parameter can specify an implicit limit?"]}}} -{"update": "id:open-p:paragraph::open/en/grouping.html-example", "fields": {"questions": {"assign": ["How to order hits inside group?", "What is pricerank?"]}}} -{"update": "id:open-p:paragraph::open/en/grouping.html-global-limit-for-grouping-queries", "fields": {"questions": {"assign": ["What is 'grouping.globalMaxGroups'?", "How does the query parameter restrict execution?", "What happens to queries exceeding the threshold?", "What is compared against the limit?", "What is the purpose of 'grouping.globalMaxGroups'?"]}}} -{"update": "id:open-p:paragraph::open/en/grouping.html-examples", "fields": {"questions": {"assign": ["What is the purpose of globalMaxGroups?", "What happens when globalMaxGroups is exceeded?"]}}} -{"update": "id:open-p:paragraph::open/en/grouping.html-recommended-settings", "fields": {"questions": {"assign": ["What is the recommended 'max' in groupings?", "What is globalMaxGroups?", "What should be done if defaultMaxGroups is too low?"]}}} -{"update": "id:open-p:paragraph::open/en/grouping.html-performance-and-correctness", "fields": {"questions": {"assign": ["What is the default tuning for grouping?", "What is the grouping session cache?", "How can correctness be improved for queries using order and max?", "What is the cost of multi-level grouping expressions?", "How can precision be increased when using max and order?"]}}} -{"update": "id:open-p:paragraph::open/en/grouping.html-nested-groups", "fields": {"questions": {"assign": ["How is drilling accomplished?"]}}} -{"update": "id:open-p:paragraph::open/en/grouping.html-range-grouping", "fields": {"questions": {"assign": ["How do I group on price range using Vespa?", "How do I configure group sizes individually?", "Can I use bucket expressions with strings?", "How do I use predefined to group with bucket expressions in Vespa?", "How do I group on tax in Vespa?"]}}} -{"update": "id:open-p:paragraph::open/en/grouping.html-pagination", "fields": {"questions": {"assign": ["What are continuation objects?", "What is the 'continuations' annotation?", "How to reproduce the same result?", "How to display the next page of customers?", "How to display the previous page of customers?"]}}} -{"update": "id:open-p:paragraph::open/en/grouping.html-expressions", "fields": {"questions": {"assign": ["What expressions can be used in group clauses?", "What is the format for using group clause in output operations?", "What is the rule for applying operators in an expression?", "What is the difference between sum(price) and price when using the group clause?", "What is arbitrary complex expressions in the context of grouping?"]}}} -{"update": "id:open-p:paragraph::open/en/grouping.html-search-container-api", "fields": {"questions": {"assign": ["What is the programmatic API?", "What is a grouping request?", "How does the API parallelize grouping?", "What is the purpose of AllOperation?", "What is CountAggregator?", "What is the API documentation?", "Where can the API documentation be found?"]}}} -{"update": "id:open-p:paragraph::open/en/grouping.html-topn-/-full-corpus", "fields": {"questions": {"assign": ["What is simple grouping?", "How to count documents using grouping?", "What is the syntax for parallel grouping?", "What is the advantage of lower accuracy?", "What does max(1000) do?"]}}} -{"update": "id:open-p:paragraph::open/en/grouping.html-selecting-groups", "fields": {"questions": {"assign": ["How do you select a group in Vespa", "What is the modulo 5 operation in Vespa", "How do you output a count in Vespa", "What does 'group' do in Vespa", "What is the formula for selecting a group in Vespa"]}}} -{"update": "id:open-p:paragraph::open/en/grouping.html-ordering-groups", "fields": {"questions": {"assign": ["What does the modulo operation do?", "How is group ordering determined?", "Can attributes be referenced in the order clause?", "What is the average relevance of groups?", "How to calculate cumulative group count?"]}}} -{"update": "id:open-p:paragraph::open/en/grouping.html-collecting-aggregates", "fields": {"questions": {"assign": ["What is simple grouping?", "What is count and max?", "What is XOR and MD5?"]}}} -{"update": "id:open-p:paragraph::open/en/grouping.html-grouping", "fields": {"questions": {"assign": ["What is single level grouping?", "What is two level grouping?", "What is three level grouping?", "What is deep grouping?"]}}} -{"update": "id:open-p:paragraph::open/en/grouping.html-time-and-date", "fields": {"questions": {"assign": ["What are time-functions?", "How to group by year/month/day/hour?", "What is the predefined aggregator?", "How to group today/yesterday/lastweek/lastmonth?"]}}} -{"update": "id:open-p:paragraph::open/en/grouping.html-counting-unique-groups", "fields": {"questions": {"assign": ["What is the `count` aggregator used for?", "How does HyperLogLog++ affect the count?", "How can the `max` clause limit the number of groups returned?", "What is the inner each clause for?", "How to output an estimate of the number of groups for an attribute?", "What is the count-distinct problem?", "Is the `count` aggregator an accurate count of all groups?", "What are the use cases for the count aggregator?"]}}} -{"update": "id:open-p:paragraph::open/en/grouping.html-impression-forecasting", "fields": {"questions": {"assign": ["What is impression logs?", "How is rank score mapped?", "What is the function to map rank score to the number of impressions?", "What is interpolatedlookup used for?", "How is the interpolatedlookup used?", "What is the result of the interpolatedlookup?", "How can the interpolatedlookup be used for forecasting?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/healthchecks.html-", "fields": {"questions": {"assign": ["What is VipStatusHandler?", "How does VipStatusHandler work?", "What is the default healthcheck path?", "What does the healthcheck respond with?", "How do you manage data sources?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/healthchecks.html-override-using-a-status-file", "fields": {"questions": {"assign": ["What is `container.core.vip-status`?", "How to make `VipStatusHandler` use a file?", "What is served on `/status.html`?", "What happens if the file doesn't exist?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/healthchecks.html-alternative-/-multiple-paths", "fields": {"questions": {"assign": ["What is VipStatusHandler?", "How to configure multiple handler instances?", "What does VipStatusHandler do?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/hosts.html-", "fields": {"questions": {"assign": ["What is hosts.xml?", "What is the purpose of hosts.xml?", "What elements are in hosts.xml?", "What is a host in hosts.xml?", "What is an alias in hosts.xml?", "What is the relationship between hosts.xml and services.xml?", "When is hosts.xml needed?", "What does hosts.xml do?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/hosts.html-host", "fields": {"questions": {"assign": ["How to use alias element?"]}}} -{"update": "id:open-p:paragraph::open/en/jdisc/http-api-tutorial.html-request-handler", "fields": {"questions": {"assign": ["What is a custom request handler?", "When is a custom handler required?", "What does a custom handler add?", "How does a custom handler route parameters?", "What is DemoHandler.java?", "How can a custom handler be implemented?", "What is the built-in processing handler?"]}}} -{"update": "id:open-p:paragraph::open/en/jdisc/http-api-tutorial.html-processors", "fields": {"questions": {"assign": ["What is the shared component used for?", "What happens to the incoming request?", "What is checked in the incoming request?"]}}} -{"update": "id:open-p:paragraph::open/en/jdisc/http-api-tutorial.html-annotatingprocessor", "fields": {"questions": {"assign": ["What is AnnotatingProcessor?", "Where is AnnotatingProcessor located?", "What does AnnotatingProcessor do?", "What is the file extension of AnnotatingProcessor?", "What programming language is used in AnnotatingProcessor?", "Which repository contains AnnotatingProcessor?", "What is the purpose of AnnotatingProcessor?", "How can AnnotatingProcessor be used in an application?"]}}} -{"update": "id:open-p:paragraph::open/en/jdisc/http-api-tutorial.html-dataprocessor", "fields": {"questions": {"assign": ["What is DataProcessor.java?", "How are processors composed?", "What are Processing steps?", "What do processors communicate through?", "What are the benefits of Processor structure?"]}}} -{"update": "id:open-p:paragraph::open/en/jdisc/http-api-tutorial.html-custom-configuration", "fields": {"questions": {"assign": ["What is the AnnotatingProcessor?", "What is in the demo.def file?"]}}} -{"update": "id:open-p:paragraph::open/en/jdisc/http-api-tutorial.html-renderer", "fields": {"questions": {"assign": ["What is the responsibility of the renderer?", "What is the purpose of init()?", "What is DemoRenderer.java?", "How many times is init() invoked per clone?", "What happens before render() is invoked?", "What does the renderer do?", "What is serialized by the renderer?"]}}} -{"update": "id:open-p:paragraph::open/en/jdisc/http-api-tutorial.html-shared-component", "fields": {"questions": {"assign": ["What does the custom component do?", "Why is the custom component important?", "How does the custom component work?", "Where can I find the code for the custom component?"]}}} -{"update": "id:open-p:paragraph::open/en/jdisc/http-api-tutorial.html-application", "fields": {"questions": {"assign": ["What is reviewed in services.xml?"]}}} -{"update": "id:open-p:paragraph::open/en/jdisc/http-server-and-filters.html-", "fields": {"questions": {"assign": ["How do I set up http servers?", "What does this document explain?", "Where can I find the Developer Guide?", "What should I do before proceeding?"]}}} -{"update": "id:open-p:paragraph::open/en/jdisc/http-server-and-filters.html-set-up-http-servers", "fields": {"questions": {"assign": ["How to accept http requests?", "How to verify new server?", "What does http section disable?", "What is port binding?", "Is binding to privileged ports supported?"]}}} -{"update": "id:open-p:paragraph::open/en/jdisc/http-server-and-filters.html-configure-the-http-server", "fields": {"questions": {"assign": ["How to modify server configuration?", "What is jdisc.http.connector?"]}}} -{"update": "id:open-p:paragraph::open/en/jdisc/http-server-and-filters.html-tls", "fields": {"questions": {"assign": ["How to configure TLS?", "What is ssl-provider?"]}}} -{"update": "id:open-p:paragraph::open/en/jdisc/http-server-and-filters.html-set-up-filter-chains", "fields": {"questions": {"assign": ["What are request filters?", "What do request filters do?", "What are response filters?", "What do response filters do?", "When do request filters run?", "When do response filters run?", "What tasks are filters used for?", "What is the purpose of filters?"]}}} -{"update": "id:open-p:paragraph::open/en/jdisc/http-server-and-filters.html-using-filter-chains", "fields": {"questions": {"assign": ["What are filter chains?", "How are filter chains set up?", "What is a filter binding?", "What happens if a request doesn't match any filter?", "Can a filter be used in multiple chains?", "What is the strict mode for filter chains?", "How do you bind a filter chain to a server?", "What is a response filter chain used for?"]}}} -{"update": "id:open-p:paragraph::open/en/jdisc/http-server-and-filters.html-excluding-filters-from-an-inherited-chain", "fields": {"questions": {"assign": ["What is a request filter chain?", "How do you add excludes to a chain element?", "What is the format for setting excludes?", "What is an example of using excludes in a chain element?"]}}} -{"update": "id:open-p:paragraph::open/en/jdisc/http-server-and-filters.html-creating-a-custom-filter", "fields": {"questions": {"assign": ["What is filter-bundle?", "What does TestRequestFilter do?", "What is HttpRequest?", "What is HttpResponse?"]}}} -{"update": "id:open-p:paragraph::open/en/performance/http2.html-", "fields": {"questions": {"assign": ["What is HTTP/2 performance?"]}}} -{"update": "id:open-p:paragraph::open/en/performance/http2.html-enabling-http/2-on-container", "fields": {"questions": {"assign": ["What is HTTP/2?", "When is HTTP/2 enabled?", "What is the recommended use of HTTP/2?"]}}} -{"update": "id:open-p:paragraph::open/en/performance/http2.html-http/2-with-tls", "fields": {"questions": {"assign": ["What is the TLS ALPN Extension?", "What is HTTP/2?", "What are the requirements for using HTTP/2 with TLS?"]}}} -{"update": "id:open-p:paragraph::open/en/performance/http2.html-http/2-without-tls", "fields": {"questions": {"assign": ["What are the supported mechanisms?", "How to upgrade to HTTP/2?", "What is HTTP/2 with prior knowledge?"]}}} -{"update": "id:open-p:paragraph::open/en/performance/http2.html-feeding-over-http/2", "fields": {"questions": {"assign": ["What is HTTP/2 multiplexing?", "What is the advantage of HTTP/2 over HTTP/1.1?", "What does vespa-feed-client provide?", "What is the implementation language of vespa-feed-client?"]}}} -{"update": "id:open-p:paragraph::open/en/performance/http2.html-client", "fields": {"questions": {"assign": ["What is the max concurrency of the v1 API?", "What are the reasons to use multiple TCP connections?", "How can a single container saturate the content layer?", "Why may a client have to use more connections than container nodes?", "What is the benefit of using multiple connections for utilizing several CPU cores?"]}}} -{"update": "id:open-p:paragraph::open/en/performance/http2.html-client-recommendations", "fields": {"questions": {"assign": ["What is vespa-feed-client used for?", "What benchmarking tool is recommended?", "Does vespa-fbench support HTTP/2?", "What are the 4 Java alternatives for feeding Vespa?"]}}} -{"update": "id:open-p:paragraph::open/en/performance/http2.html-testing", "fields": {"questions": {"assign": ["What is the curl command to use with HTTP/2 prior knowledge?", "Can the server perform a protocol upgrade with a request containing content?"]}}} -{"update": "id:open-p:paragraph::open/en/content/idealstate.html-", "fields": {"questions": {"assign": ["What is the distribution algorithm?", "How is the distribution algorithm used?", "What is tracked current state?", "What does the output of the distribution algorithm give?", "What is the ideal state algorithm?", "What is the input to the distribution algorithm?", "What are the desired qualities for the ideal state algorithm?", "What is the purpose of minimal reassignment in the ideal state algorithm?"]}}} -{"update": "id:open-p:paragraph::open/en/content/idealstate.html-computational-cost", "fields": {"questions": {"assign": ["What tasks require ideal location calculations?", "How can caching be avoided?", "Why is expensive computational cost problematic?"]}}} -{"update": "id:open-p:paragraph::open/en/content/idealstate.html-a-simple-example:-modulo", "fields": {"questions": {"assign": ["What is the modulo operation?", "Why is the modulo approach not scalable?"]}}} -{"update": "id:open-p:paragraph::open/en/content/idealstate.html-weighted-random-election", "fields": {"questions": {"assign": ["What is the content layer algorithm?", "What does the distribution key decide?", "What is the purpose of using pseudo-random numbers?", "What determines the randomness of migrating owners?", "What is the impact of altering the cluster state on bucket distribution?"]}}} -{"update": "id:open-p:paragraph::open/en/content/idealstate.html-weighting-nodes", "fields": {"questions": {"assign": ["What is the formula for node weights?", "What is node capacity?"]}}} -{"update": "id:open-p:paragraph::open/en/content/idealstate.html-distribution-skew", "fields": {"questions": {"assign": ["What increases skew in distribution?", "How to decrease skew?", "What is the impact of having less targets?", "What is the impact of using a more uniform pseudo-random function?", "What happens when the algorithm generates skew in the distribution?"]}}} -{"update": "id:open-p:paragraph::open/en/content/idealstate.html-distribution-waste", "fields": {"questions": {"assign": ["What is the waste metric?", "How is waste percentage calculated?", "What does distribution bit count indicate?", "How does redundancy affect bucket distribution?", "What is the formula for waste calculation?", "What is considered as full capacity for a cluster?"]}}} -{"update": "id:open-p:paragraph::open/en/content/idealstate.html-calculated-waste-from-various-cluster-sizes", "fields": {"questions": {"assign": ["What do different waste colors indicate?", "How is waste percentage calculated?"]}}} -{"update": "id:open-p:paragraph::open/en/content/idealstate.html-default-number-of-distribution-bits-used", "fields": {"questions": {"assign": ["What affects distribution in Vespa?", "What is waste in Vespa?", "What is a distribution profile?", "What is the impact of changing distribution bits?", "How often should distribution bit count be altered?", "How does waste impact computation?", "What is the computational cost of computing waste?"]}}} -{"update": "id:open-p:paragraph::open/en/content/idealstate.html-strict-mode-(default-for-memfile-persistence-provider)", "fields": {"questions": {"assign": ["What is strict mode?", "What is the maximum calculated waste?", "What is the minimum buckets for each node?"]}}} -{"update": "id:open-p:paragraph::open/en/content/idealstate.html-loose-mode-(default-for-search-provider)", "fields": {"questions": {"assign": ["What is loose mode?", "What is wasteful mode?", "What is the max calculated waste?"]}}} -{"update": "id:open-p:paragraph::open/en/performance/index.html-practical-search-performance-guide", "fields": {"questions": {"assign": ["What is the practical-search guide?", "What does the guide cover?", "What does the guide introduce?"]}}} -{"update": "id:open-p:paragraph::open/en/performance/index.html-sizing-and-capacity-planning", "fields": {"questions": {"assign": ["What is sizing and capacity planning?", "How do I size a Vespa search cluster?", "What is caching in Vespa?", "How do I enable caches in Vespa?", "What is the impact of attributes on memory footprint?", "How do I find attribute memory usage?", "What are Proton maintenance jobs?", "What is the impact of Proton maintenance jobs on resource usage?"]}}} -{"update": "id:open-p:paragraph::open/en/performance/index.html-benchmarking-and-tuning", "fields": {"questions": {"assign": ["Why is benchmarking important in Vespa?", "What tools are used for benchmarking?", "How to tune system aspects of Vespa?", "What is vespa-fbench?", "How to improve HTTP performance in Vespa?"]}}} -{"update": "id:open-p:paragraph::open/en/performance/index.html-profiling", "fields": {"questions": {"assign": ["How to do performance analysis?", "How to profile application?", "How to run Vespa with Valgrind?"]}}} -{"update": "id:open-p:paragraph::open/en/jdisc/index.html-", "fields": {"questions": {"assign": ["What is JDisc?", "What does JDisc do?", "What are the features of JDisc?", "What is the component model of JDisc?", "What are the standard components in JDisc?", "What is the chain composition mechanism in JDisc?"]}}} -{"update": "id:open-p:paragraph::open/en/jdisc/index.html-developing-components", "fields": {"questions": {"assign": ["What are the building blocks of Processing?"]}}} -{"update": "id:open-p:paragraph::open/en/jdisc/index.html-other-related-documents", "fields": {"questions": {"assign": ["What is a healthcheck?", "What is the Container's request processing lifecycle?"]}}} -{"update": "id:open-p:paragraph::open/en/tutorials/index.html-news-search-and-recommendation", "fields": {"questions": {"assign": ["What is the News Search Tutorial?", "What are the articles in the tutorial?"]}}} -{"update": "id:open-p:paragraph::open/en/tutorials/index.html-models-hot-swap", "fields": {"questions": {"assign": ["What is the Models hot swap tutorial?", "What does the Models hot swap tutorial cover?"]}}} -{"update": "id:open-p:paragraph::open/en/tutorials/index.html-text-search", "fields": {"questions": {"assign": ["What are the Text Search tutorials?", "What is Text Search ML?", "What is Text Search Semantic?"]}}} -{"update": "id:open-p:paragraph::open/en/indexing.html-", "fields": {"questions": {"assign": ["What is indexing?", "What is the default indexing configuration?", "How can custom document processing be added?"]}}} -{"update": "id:open-p:paragraph::open/en/indexing.html-routing", "fields": {"questions": {"assign": ["What is a routing document?", "What is the purpose of indexing cluster?", "What is the default route used for?"]}}} -{"update": "id:open-p:paragraph::open/en/indexing.html-chain.indexing", "fields": {"questions": {"assign": ["What is indexing chain?", "What does IndexingProcessor do?"]}}} -{"update": "id:open-p:paragraph::open/en/indexing.html-document-selection", "fields": {"questions": {"assign": ["What is a document selection string?", "When is a document dropped?", "Where is selection string evaluated?", "What is DocumentRouteSelector?"]}}} -{"update": "id:open-p:paragraph::open/en/indexing.html-document-processing", "fields": {"questions": {"assign": ["What is document processing?", "How do you add document processors?", "What is the format for adding a document processor?", "What is the purpose of a document processor?", "Does adding a document processor affect indexing?"]}}} -{"update": "id:open-p:paragraph::open/en/indexing.html-inherit-indexing-chain", "fields": {"questions": {"assign": ["How to fix indexing cluster", "What is the alternative to indexing chain 'default'?", "What is the recommended action when using the default chain?"]}}} -{"update": "id:open-p:paragraph::open/en/indexing.html-multiple-container-clusters", "fields": {"questions": {"assign": ["How to configure multiple container clusters?", "What are the use cases for multiple container clusters?", "How to avoid network hop when using multiple clusters?", "How to set up an indexing chain?", "What is the best practice for configuring document-processing?"]}}} -{"update": "id:open-p:paragraph::open/en/indexing.html-appendix:-trace", "fields": {"questions": {"assign": ["What is a trace example?"]}}} -{"update": "id:open-p:paragraph::open/en/jdisc/injecting-components.html-", "fields": {"questions": {"assign": ["What is dependency injection?", "What is circular dependency?", "What is auto-injection?", "What is the purpose of the jdisc-container?", "What are the three elements of dependency injection?", "What is the difference between auto-injection and Guice modules?", "What is the sample app for dependency injection?"]}}} -{"update": "id:open-p:paragraph::open/en/jdisc/injecting-components.html-depending-on-another-component", "fields": {"questions": {"assign": ["What is a consumer?", "What are MyComponent dependencies?", "How do you register MyDependency?"]}}} -{"update": "id:open-p:paragraph::open/en/jdisc/injecting-components.html-extending-components", "fields": {"questions": {"assign": ["What happens when injecting two components?", "How can the dependency injection issue be resolved?", "What is a ComponentRegistry?", "What method is used to look up entries in a ComponentRegistry?"]}}} -{"update": "id:open-p:paragraph::open/en/jdisc/injecting-components.html-specify-the-bundle", "fields": {"questions": {"assign": ["What causes class loading problems?", "How to remedy class loading problems?", "How to specify the jar file?"]}}} -{"update": "id:open-p:paragraph::open/en/jdisc/injecting-components.html-depending-on-all-components-of-a-specific-type", "fields": {"questions": {"assign": ["What is ComponentRegistry?", "What is the purpose of ComponentRegistry?", "What does ComponentRegistry do?", "How is ComponentRegistry used?", "What is the syntax for creating a new component using ComponentRegistry?", "What is the dependency of MyComponent?", "What is the class hierarchy for ComponentRegistry?"]}}} -{"update": "id:open-p:paragraph::open/en/jdisc/injecting-components.html-special-components", "fields": {"questions": {"assign": ["What is a Provider interface?", "What is a constructor dependency?", "What is the role of deconstruct()?", "What is a unique instance of dependency class?", "What are the situations when a component cannot be directly injected?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/inspecting-structured-data.html-", "fields": {"questions": {"assign": ["What is the Data Access API?", "What structured data can it access?", "How does the API access arrays?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/inspecting-structured-data.html-use-case:-accessing-array-attributes", "fields": {"questions": {"assign": ["What is the output of SimpleTestSearcher?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/inspecting-structured-data.html-use-case:-accessing-weighted-set-attributes", "fields": {"questions": {"assign": ["What is Data Access API?", "What is weighted set?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/inspecting-structured-data.html-unit-testing-with-structured-data", "fields": {"questions": {"assign": ["What is Slime?", "What is a structured data field?", "How do you create a struct using Slime?", "How do you create an array using Slime?", "Can Slime create nested arrays and objects?", "How do you convert a JSON string to a Slime structure?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/internal-config-files.html-", "fields": {"questions": {"assign": ["What does the reference describe?", "What file format does Vespa use?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/internal-config-files.html-file-format", "fields": {"questions": {"assign": ["What is a configuration file?", "What is the format of a configuration file?", "How do you set an integer in a configuration file?", "How do you set a string in a configuration file?", "Are strings always in quotation marks in a configuration file?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/internal-config-files.html-arrays", "fields": {"questions": {"assign": ["What is an array?", "How are arrays structured?", "How do you declare an array?", "What is the separator for child arrays?", "What is a table structure?", "What is a row in a table?", "What is a column in a table?", "How do you set values in an array?"]}}} -{"update": "id:open-p:paragraph::open/en/introduction-to-documentation.html-", "fields": {"questions": {"assign": ["Who is this documentation for?", "What is the purpose of italic font?", "What programming language is the documentation for?", "What is covered in the Vespa documentation?"]}}} -{"update": "id:open-p:paragraph::open/en/lightgbm.html-", "fields": {"questions": {"assign": ["What is LightGBM?", "How is LightGBM different from XGBoost?", "What is a defining feature of LightGBM?", "Does LightGBM support categorical features?", "Can Vespa import models trained with LightGBM?"]}}} -{"update": "id:open-p:paragraph::open/en/lightgbm.html-exporting-models-from-lightgbm", "fields": {"questions": {"assign": ["What is LightGBM's dump_model?", "What data is dumped by dump_model?", "How can a LightGBM model be trained for use in Vespa?", "What features does the dumped data include?"]}}} -{"update": "id:open-p:paragraph::open/en/lightgbm.html-create-random-training-set", "fields": {"questions": {"assign": ["What is pd.DataFrame()", "What is lgb.Dataset()"]}}} -{"update": "id:open-p:paragraph::open/en/lightgbm.html-train-the-model", "fields": {"questions": {"assign": ["What is the 'objective' parameter?"]}}} -{"update": "id:open-p:paragraph::open/en/lightgbm.html-save-the-model", "fields": {"questions": {"assign": ["What is lightgbm_model.json?", "Can lightgbm_model.json be imported into Vespa?"]}}} -{"update": "id:open-p:paragraph::open/en/lightgbm.html-importing-lightgbm-models", "fields": {"questions": {"assign": ["How do I import LightGBM model?", "Can the application package have multiple models?", "Is the model available for ranking?", "What is stateless model evaluation?", "How can I do stateless model evaluation?"]}}} -{"update": "id:open-p:paragraph::open/en/lightgbm.html-ranking-with-lightgbm-models", "fields": {"questions": {"assign": ["What is lightgbm?", "How does lightgbm work?", "What are the feature names?", "How to map lightgbm features?", "How to control the number of documents exposed?"]}}} -{"update": "id:open-p:paragraph::open/en/lightgbm.html-objective-functions", "fields": {"questions": {"assign": ["What objectives does Vespa support?"]}}} -{"update": "id:open-p:paragraph::open/en/lightgbm.html-using-categorical-features", "fields": {"questions": {"assign": ["What is LightGBM?", "Can LightGBM handle categorical features?", "How does Vespa handle categorical features?"]}}} -{"update": "id:open-p:paragraph::open/en/lightgbm.html-debugging-vespa-inference-score-versus-lightgbm-predict-score", "fields": {"questions": {"assign": ["What is lost when dumping LightGBM models?", "How to scrape features for training?", "Why is precision important for reading Vespa features?", "Why might matching features differ in a distributed setting?", "What is term(n).significance?", "Who sets term(n).significance?", "How can dumping features help debug differences in offline and online features?"]}}} -{"update": "id:open-p:paragraph::open/en/linguistics.html-", "fields": {"questions": {"assign": ["What is linguistic processing in Vespa?", "What fields are processed by linguistic processing?", "What is the default linguistics implementation used by Vespa?", "What is the goal of linguistic processing in Vespa?", "What are the operations in linguistic processing?", "What are the supported languages in OpenNLP?", "How can transforms per query term be enabled or disabled in the schema?"]}}} -{"update": "id:open-p:paragraph::open/en/linguistics.html-creating-a-custom-linguistics-implementation", "fields": {"questions": {"assign": ["What is com.yahoo.language.Linguistics?", "What is SimpleLinguistics?", "What linguistics module should be provided?", "How to configure SimpleLinguistics?", "What is the impact of updating the linguistics component of a live system?"]}}} -{"update": "id:open-p:paragraph::open/en/linguistics.html-language-handling", "fields": {"questions": {"assign": ["How does Vespa detect language?", "What affects document recall?", "Can Vespa detect languages automatically?"]}}} -{"update": "id:open-p:paragraph::open/en/linguistics.html-indexing-with-language", "fields": {"questions": {"assign": ["What is set_language used for?", "How does Vespa determine language?", "How can fixed languages be set?", "Can a document have multiple languages?", "What happens when a language is not set?", "What is the order of indexing scripts?", "How is language set per field?"]}}} -{"update": "id:open-p:paragraph::open/en/linguistics.html-field-language-detection", "fields": {"questions": {"assign": ["What is language detection?", "How is language detection performed?", "Is language detection per document or per field?", "What happens when a field has unknown language?", "What is query language detection?", "What is the default language for fields with little text?", "Where can I find more information about language detection?"]}}} -{"update": "id:open-p:paragraph::open/en/linguistics.html-querying-with-language", "fields": {"questions": {"assign": ["What is an indexed string field?", "What is symmetric tokenization?", "How does the query parser work?", "What controls the language state?", "Can multiple languages be in one index?", "How to store the language of a document?", "Why apply a filter at query-time?", "What does the language parameter affect?"]}}} -{"update": "id:open-p:paragraph::open/en/linguistics.html-query-language-detection", "fields": {"questions": {"assign": ["What is language parameter in Vespa?", "How does language detector process queries?", "What is the confidence cutoff for language detection?"]}}} -{"update": "id:open-p:paragraph::open/en/linguistics.html-multiple-languages", "fields": {"questions": {"assign": ["What are the approaches for retrieval?", "What is the benefit of equiv?", "What is Sentence-Bert?"]}}} -{"update": "id:open-p:paragraph::open/en/linguistics.html-tokenization", "fields": {"questions": {"assign": ["What is tokenization?", "What is CJK segmentation?", "What is N-gram matching?"]}}} -{"update": "id:open-p:paragraph::open/en/linguistics.html-normalization", "fields": {"questions": {"assign": ["What is text normalization?", "How does Vespa normalize text?", "What is the nfkc query term annotation?", "What is accentDrop annotation?", "What does normalization preserve?", "Which library does Vespa use for text normalization?", "What are the benefits of normalization?", "What kind of decorations are normalized by Vespa?"]}}} -{"update": "id:open-p:paragraph::open/en/linguistics.html-stemming", "fields": {"questions": {"assign": ["What is stemming?", "What is a stemmer?", "How does stemming work?", "What is symmetric stemming?", "What is the purpose of stemming?", "What is bolding?", "How does bolding work?", "What is the stem query term?"]}}} -{"update": "id:open-p:paragraph::open/en/linguistics.html-theory", "fields": {"questions": {"assign": ["What are equivalence classes in Vespa?", "What is the problem with low-level linguistics library in Vespa?", "How does multiple stemming help with recall?"]}}} -{"update": "id:open-p:paragraph::open/en/linguistics.html-configuration", "fields": {"questions": {"assign": ["What is stemming?", "How to disable stemming?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/live-upgrade.html-", "fields": {"questions": {"assign": ["How to upgrade Vespa?", "What is step 1 of the upgrade process?", "What are the metrics for evaluating if the next node can be stopped?", "What is the effect of upgrading config servers?", "What is the redundancy requirement for availability during upgrade?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/live-upgrade.html-troubleshooting", "fields": {"questions": {"assign": ["Where can I find troubleshooting?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/logd-metrics-reference.html-", "fields": {"questions": {"assign": ["What is logd.processed.lines?", "What does logd.processed.lines count?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/logs.html-", "fields": {"questions": {"assign": ["Where are Vespa logs stored?", "What is the common log module?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/logs.html-log-file-fields", "fields": {"questions": {"assign": ["What is vespa-logfmt?", "What is the log format used?", "What are the fields in each log message?", "What is log level?", "What is the description for error level?", "What is the message field in a log message?", "What is event log level used for?", "What is spam log level used for?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/logs.html-controlling-log-levels", "fields": {"questions": {"assign": ["What is vespa-logctl?", "How to change log levels?", "What is the default log level?", "Where are log control files stored?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/logs.html-log-events", "fields": {"questions": {"assign": ["What are event messages?", "How are events emitted?", "What is an event type?", "What is an event version?", "What is an event payload?", "What is a count event?", "What is a value event?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/logs.html-logd", "fields": {"questions": {"assign": ["What is logd?", "What is vespa.log?", "How is the log file rotated?", "When is the log file rotated?", "How are rotated logs removed?", "What happens if log file grows too large?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/logs.html-log-server", "fields": {"questions": {"assign": ["What does the Archiver plugin do?", "Where are log files located?", "What is the log file naming pattern?", "What happens when a log file exceeds 20 Mb?", "How are rotated log files compressed?", "When are archived log files deleted?", "How can events be forwarded?", "What should be added to services.xml for forwarding events?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/logs.html-access-log-file-content", "fields": {"questions": {"assign": ["Where are Container logs stored?", "What is access logging?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/logs.html-", "fields": {"questions": {"assign": ["What causes timing differences?", "Why are timing values different?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/logs.html-definition-of-processing-time-in-the-access-log", "fields": {"questions": {"assign": ["What is the access log processing time?", "When does processing time end for an asynchronous response?", "When does processing time end for a synchronous response?", "What is a completion handler?", "When is the end of processing time for a synchronous response?", "When is the end of processing time for an asynchronous response?", "What happens before the end of processing time for an asynchronous response?", "What happens before the end of processing time for a synchronous response?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/logs.html-definition-of-processing-time-in-the-vespa.log", "fields": {"questions": {"assign": ["What is StatisticsSearcher?", "What is mean_query_latency event?", "What is the interval duration?", "What is included in the interval?", "What is excluded from the interval?", "When does fill time get included?", "When does fill() get invoked?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/logs.html-timing-summary", "fields": {"questions": {"assign": ["What information does the access log include?", "What does query latency event cover?", "Where is StatisticsSearcher placed?", "Does access log include rendering logic?", "What is excluded from query latency event?", "What networks are excluded from access log?", "What is the purpose of StatisticsSearcher?", "Does query latency cover summary fetching?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/logs.html-zookeeper-log", "fields": {"questions": {"assign": ["What is the location of the ZooKeeper log?", "When should you monitor the ZooKeeper log?", "Where is the ZooKeeper log file stored?", "What information can be found in the ZooKeeper log?", "How do I debug Vespa configuration using the ZooKeeper log?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/low-level-request-handlers.html-", "fields": {"questions": {"assign": ["What are low-level request handler APIs?", "What is RequestHandler interface?", "What is the purpose of a request handler?", "How is a custom request handler implemented?", "What is the difference between RequestHandler and AbstractRequestHandler?", "What is the role of a ResponseHandler?", "What is the timeout for a request?", "What is the dispatching logic for a client request?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/low-level-request-handlers.html-handling-a-request", "fields": {"questions": {"assign": ["What is a request handler?", "What method does a request handler invoke?", "What is ThreadedRequestHandler?", "What is a ContentChannel?", "What must a request handler do?", "What does JDisc enforce?", "What is the purpose of passing context to a known thread pool?", "What should a request handler not do?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/low-level-request-handlers.html-reading-from-a-contentchannel", "fields": {"questions": {"assign": ["What is a ContentChannel?", "What does the ContentChannel interface provide?", "What is handover?", "What is required for a content channel to be thread-safe?", "What is CompletionHandler?", "What is the purpose of CompletionHandler?", "What is BufferedContentChannel?", "What is ReadableContentChannel?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/low-level-request-handlers.html-dispatching-a-response", "fields": {"questions": {"assign": ["What is a ResponseHandler?", "What is the purpose of ResponseHandler?", "What is the second argument of handleRequest method?", "How do you dispatch a response?", "What is the purpose of ContentChannel?", "What is the ResponseDispatch utility class?", "What happens if you don't call the response handler exactly once?", "What is required when using ContentChannel to write the response content?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/low-level-request-handlers.html-writing-to-a-contentchannel", "fields": {"questions": {"assign": ["What is ContentChannel?", "What is ByteBuffer handover?", "Are all ContentChannels thread-safe?", "What is a CompletionHandler?", "Is the CompletionHandler optional?", "What guarantees are there for the completion handler?", "Is it required to explicitly close all content channels?", "What happens if an exception is thrown in a call to write()?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/low-level-request-handlers.html-dispatching-a-client-request", "fields": {"questions": {"assign": ["How is child-request dispatched?", "What is setServerRequest used for?", "What is a ContentChannel?", "What is RequestDispatch?", "When to close the content channel?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/low-level-request-handlers.html-handling-a-client-response", "fields": {"questions": {"assign": ["What happens when a client provider responds?", "What is the response handler?", "What should you not do in the handleResponse method?", "What happens when the response is dispatched?", "What does a response handler need to do?", "How should the response payload be consumed?", "What is a ContentChannel?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/low-level-request-handlers.html-request-timeout", "fields": {"questions": {"assign": ["What is a request timeout?", "What happens when a timeout occurs?", "How is the timeout managed?", "What is the consequence of not calling the response handler from handleTimeout()?", "Does a request always have a timeout assigned?", "Is additional synchronization needed to prevent repeated calling of the response handler?", "What happens if an exception is thrown in handleTimeout()?", "Why is it important to call the response handler from within handleTimeout()?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/metrics-v1.html-", "fields": {"questions": {"assign": ["Where is the node metrics API?", "What port is the metrics proxy?", "What is the default URL for the metrics API?", "What is the metrics API used for?", "What is the overview of nodes, services and metrics APIs?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/metrics-v1.html-http-requests", "fields": {"questions": {"assign": ["Where can I find examples of monitoring metrics/v1?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/metrics-v1.html-request-parameters", "fields": {"questions": {"assign": ["What is the consumer parameter?", "What is the default metric set?", "What is the Vespa metric set?", "What are the built-in metrics?", "What is the type of the consumer parameter?", "What does setting an unknown consumer value return?", "How do I specify a response consumer?", "What is the description of the consumer parameter?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/metrics-v1.html-http-status-codes", "fields": {"questions": {"assign": ["What is a status code?", "What is code 200?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/metrics-v1.html-response-format", "fields": {"questions": {"assign": ["What is the response format?", "What does the metrics field contain?", "What is the status field?", "What is the possible value of the status field?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/metrics-v2.html-", "fields": {"questions": {"assign": ["What is application metrics API?", "What port does the metrics proxy use?", "What is the metrics API aggregation?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/metrics-v2.html-request-parameters", "fields": {"questions": {"assign": ["What is the 'consumer' parameter?", "Where can I find details for metrics/v1?", "What does the 'consumer' parameter specify?", "Which document describes the 'consumer' parameter?", "What is the response consumer for?", "What is the format of the 'consumer' parameter?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/metrics-v2.html-http-status-codes", "fields": {"questions": {"assign": ["What is a status code?", "What is a list of status codes?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/metrics-v2.html-response-format", "fields": {"questions": {"assign": ["What is the format of responses?", "What are the fields in responses?", "What is the nodes element?", "What is the hostname field?", "What is the role field?", "What is the service object?"]}}} -{"update": "id:open-p:paragraph::open/en/operations/metrics.html-", "fields": {"questions": {"assign": ["What are the metric endpoints?", "How do I get metric values?", "What is * /metrics/v2/values*?", "What is * /prometheus/v1/values*?", "What is the purpose of metrics-proxy?", "What is the purpose of using filter for */metrics/v2/values*?"]}}} -{"update": "id:open-p:paragraph::open/en/operations/metrics.html-aggregating-metrics", "fields": {"questions": {"assign": ["How do you aggregate max values?", "How to compute average values?", "What is the guideline for using last value suffix?", "What are the recommended aggregation for count suffix?", "What is the guideline for using average suffix?", "What is the purpose of Vespa metrics?"]}}} -{"update": "id:open-p:paragraph::open/en/operations/metrics.html-metric-sets", "fields": {"questions": {"assign": ["What are Node metrics?", "Where are node metrics listed?", "What is the metric type?", "What is the default metric-set?", "What is the Vespa metric-set?", "What is the consumer request parameter?", "What is the difference between /metrics/v2/values and /prometheus/v1/values?"]}}} -{"update": "id:open-p:paragraph::open/en/operations/metrics.html-metrics-names", "fields": {"questions": {"assign": ["What is the Vespa metric naming scheme?", "What are the typical metric suffixes?"]}}} -{"update": "id:open-p:paragraph::open/en/operations/metrics.html-available-metrics", "fields": {"questions": {"assign": ["What are Vespa metrics?", "Where can I find container metrics?"]}}} -{"update": "id:open-p:paragraph::open/en/operations/metrics.html-metrics-from-custom-components", "fields": {"questions": {"assign": ["How to add custom metrics?", "What are MetricReceiver and Gauge?", "Where is an example for adding custom metrics?"]}}} -{"update": "id:open-p:paragraph::open/en/operations/metrics.html-example-/-qa", "fields": {"questions": {"assign": ["What is MetricReceiver?", "Can MetricReceiver object change?", "How do we store MetricReceiver reference?", "What happens if we share Component references?", "How long is the object valid?", "What is a common place to store MetricReceiver?", "Can the object update to a new reference?", "What is the lifetime of the MetricReceiver object?"]}}} -{"update": "id:open-p:paragraph::open/en/operations/metrics.html-start-of-metric-example_hitcounts,-dimensions:-{-\"query_language\":-\"en\"-}", "fields": {"questions": {"assign": ["What is the formula for calculating 1/(1-Percentile)?"]}}} -{"update": "id:open-p:paragraph::open/en/operations/metrics.html-start-of-metric-query_latency,-dimensions:-{-\"chain\":-\"metalchain\"-}", "fields": {"questions": {"assign": ["What is the value for Percentile?", "What is the value for TotalCount?"]}}} -{"update": "id:open-p:paragraph::open/en/operations/metrics.html-start-of-metric-totalhits_per_query,-dimensions:-{-\"chain\":-\"metalchain\"-}", "fields": {"questions": {"assign": ["What is the value of 1/(1-Percentile) when Percentile=0?", "What is the value of 1/(1-Percentile) when Percentile=1?", "What is the TotalCount when Percentile=0?", "What is the TotalCount when Percentile=1?"]}}} -{"update": "id:open-p:paragraph::open/en/tutorials/models-hot-swap.html-", "fields": {"questions": {"assign": ["What is this tutorial about?", "What is hot swap?", "What are recommendation models?", "What previous tutorials did this series include?", "What is assumed for simplicity in this tutorial?"]}}} -{"update": "id:open-p:paragraph::open/en/tutorials/models-hot-swap.html-real-world-requirements", "fields": {"questions": {"assign": ["What are the additional requirements?", "What is hot swap?", "What is shared model-agnostic data?", "How should cleanup be handled?"]}}} -{"update": "id:open-p:paragraph::open/en/tutorials/models-hot-swap.html-hot-swap", "fields": {"questions": {"assign": ["What is the central requirement for hot swap?", "What is external versioning?", "What is internal versioning?", "How does Vespa implement hot swap?"]}}} -{"update": "id:open-p:paragraph::open/en/tutorials/models-hot-swap.html-supporting-multiple-models", "fields": {"questions": {"assign": ["How are recommendation models separated?", "What is a model code?", "Where is the model code stored?", "What is the purpose of the model and version fields?", "How should the feeding procedures of each model be separated?"]}}} -{"update": "id:open-p:paragraph::open/en/tutorials/models-hot-swap.html-managing-model-agnostic-data-using-parent-documents", "fields": {"questions": {"assign": ["What is Vespa's parent-child relations?", "What is the news_model schema?", "What is the purpose of the news_model schema?", "What data is model agnostic?", "What is the format of the tuples used to create prediction models?", "What is the advantage of processing news article data and event data separately?", "How is model-related fields extracted into a new schema?", "What fields are imported from the parent document to the child document in Vespa's parent-child relations?"]}}} -{"update": "id:open-p:paragraph::open/en/tutorials/models-hot-swap.html-garbage-collection", "fields": {"questions": {"assign": ["What is the garbage collection mechanism?", "How does Vespa filter out obsolete documents?", "What are the version fields?", "What is the purpose of the config_ref field?", "What is the relation between version and latest_version fields?"]}}} -{"update": "id:open-p:paragraph::open/en/tutorials/models-hot-swap.html-dealing-with-feeding-failures", "fields": {"questions": {"assign": ["What are read and write versions?", "What is `next_version` field?", "What is `version` field?", "Why use separate versions?", "What happens during unsuccessful feedings?", "When should the active set be modified?"]}}} -{"update": "id:open-p:paragraph::open/en/tutorials/models-hot-swap.html-dealing-with-multi-region-apps", "fields": {"questions": {"assign": ["What are the issues with feeding in multiple regions?", "How can the version and model mismatch be avoided?", "What is a per-region endpoint?", "How can we achieve stickiness in follow-up queries?"]}}} -{"update": "id:open-p:paragraph::open/en/tutorials/models-hot-swap.html-putting-it-all-together", "fields": {"questions": {"assign": ["What does hot-swap schema introduce?", "What does the hot-swap schema enable?", "Why is hot-swap schema important?", "What types are introduced by hot-swap schema?", "What fields are introduced by hot-swap schema?", "What relations are introduced by hot-swap schema?", "Does hot-swap schema increase application complexity?", "What kind of application benefits from hot-swap schema?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/monitoring.html-", "fields": {"questions": {"assign": ["What external systems does Vespa integrate with?", "What is the process metrics API?", "How does Vespa transfer metrics?", "What is the default port for metrics-proxy?", "What is the Prometheus format for metrics?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/monitoring.html-metrics-proxy", "fields": {"questions": {"assign": ["What is the metrics-proxy?", "What does the metrics-proxy do?", "What is the port for metrics-proxy?", "Which API serves the metric cache?", "What is the process for metrics-proxy?", "What starts the metrics-proxy?", "Is the metrics-proxy configurable?", "What is the internal API for the metric cache?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/monitoring.html-/state/v1/health", "fields": {"questions": {"assign": ["Where is the health status found?", "What is the URL for health status?", "What is the status message?", "What is the purpose of /state/v1/health?", "How is /state/v1/health used?", "What is returned by /state/v1/health?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/monitoring.html-/state/v1/metrics", "fields": {"questions": {"assign": ["Where are per-process metrics found?", "What are per-process metrics?", "What is the purpose of per-process metrics?", "What are the benefits of Vespa's approach to metrics?", "What is a snapshot in Vespa metrics?", "What are custom metrics in Vespa?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/monitoring.html-/metrics/v1/values", "fields": {"questions": {"assign": ["What products can be used for monitoring?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/monitoring.html-/metrics/v2/values", "fields": {"questions": {"assign": ["What is /metrics/v2/values?", "What metrics are exposed by the /metrics/v2/values API?", "How can Vespa metrics be pulled to Cloudwatch?", "What is the response format of the /metrics/v2/values API?", "What is the purpose of the /metrics/v2/values API?", "What is the 'nodes' list in the response of the /metrics/v2/values API?", "What are the metrics exposed for each service in the /metrics/v2/values API?", "What is the format of each metric in the /metrics/v2/values API?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/monitoring.html-/prometheus/v1/values", "fields": {"questions": {"assign": ["What does the node metrics API expose?", "How can Prometheus scrape node metrics?", "What is the format of the metrics exposed by the prometheus API?", "What is the prometheus/Grafana example for the node metrics API?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/monitoring.html-pulling-metrics-from-vespa", "fields": {"questions": {"assign": ["What is Vespa's metrics API?", "What format does Vespa expose metrics in?", "What is Vespa's polling frequency limit?", "How can metrics be pulled into CloudWatch?", "What is the recommended solution for pulling Vespa metrics into CloudWatch?", "What Datadog integration exists for monitoring Vespa?", "Where can Prometheus scrape Vespa metrics from?", "What example exists for Prometheus/Grafana monitoring of Vespa?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/monitoring.html-pushing-metrics-to-cloudwatch", "fields": {"questions": {"assign": ["What is the required permission for IAM user or role?", "What is a shared credentials file?", "How to configure Vespa for CloudWatch?", "What metrics are sent to the default Vespa set?", "What region is the CloudWatch namespace set to?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/monitoring.html-monitoring-with-grafana", "fields": {"questions": {"assign": ["How to set up monitoring?", "What is Grafana used for?", "What is Prometheus?", "What is time-series database?", "How to launch Grafana?", "How to build Random Data Feeder?", "How to run Random Data Feeder?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/monitoring.html-histograms", "fields": {"questions": {"assign": ["What are metric histograms?", "How to create metric histograms?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/mtls.html-", "fields": {"questions": {"assign": ["What is Transport Layer Security (TLS)?", "Why is enabling TLS important?", "What are the two separate planes of TLS connectivity in Vespa?", "What is mutually authenticated TLS (mTLS)?", "Does enabling TLS in Vespa protect status pages and metrics?", "What makes Vespa installations more secure?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/mtls.html-prerequisites", "fields": {"questions": {"assign": ["How do I enable TLS?", "What is a PEM format?", "What is a PKCS#8 format?", "How do I write a TLS configuration file?", "Does Vespa support encrypted private keys?", "Does Vespa support live reloading of TLS credentials?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/mtls.html-configuring-vespa-tls", "fields": {"questions": {"assign": ["What environment variable governs TLS?", "What does VESPA_TLS_CONFIG_FILE contain?", "What must VESPA_TLS_CONFIG_FILE point to?", "What happens after setting VESPA_TLS_CONFIG_FILE?", "How are TLS certificates configured in Vespa?", "What is the purpose of VESPA_TLS_CONFIG_FILE?", "What are the requirements for enabling TLS in Vespa?", "What do Vespa command-line tools do automatically after setting VESPA_TLS_CONFIG_FILE?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/mtls.html-writing-a-tls-configuration-file", "fields": {"questions": {"assign": ["What is a configuration file?", "What happens if a process cannot load files?", "What is TLS?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/mtls.html-configuring-tls-peer-authorization-rules", "fields": {"questions": {"assign": ["What is a self-signed Certificate Authority?", "What is an authorization rule?", "How are authorization rules specified?", "What is the purpose of authorization rules?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/mtls.html-example", "fields": {"questions": {"assign": ["What is required for monitoring service?", "What is a certificate authority?", "What is the syntax for authorized peers?", "What is the CN value for monitoring service?", "What is the purpose for authorized peers?", "What is the role of SAN DNS entries?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/mtls.html-automatic-reloading-of-crypto-material", "fields": {"questions": {"assign": ["What is a TLS configuration file?", "How often is reloading performed?", "What is the impact of reloading?", "What happens if live reloading fails?", "Does Vespa watch the configuration file?", "How can race conditions be avoided?", "What are the steps to split file refreshing?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/mtls.html-setting-up-tls-for-a-new-vespa-application-or-upgrading-with-downtime", "fields": {"questions": {"assign": ["What is VESPA_TLS_CONFIG_FILE?", "What is vespa-get-cluster-state?", "What is vespa-logfmt?", "What is the recommended way to enable TLS?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/mtls.html-upgrading-an-existing-non-tls-vespa-application-to-tls-without-downtime", "fields": {"questions": {"assign": ["What is mixed mode in Vespa?", "How many phases are there in the TLS rollout?", "How can you enable gradual TLS upgrade in Vespa?", "What is the environment variable used to control mixed mode in Vespa?", "What happens during phase 1 of the TLS rollout in Vespa?", "What happens during phase 2 of the TLS rollout in Vespa?", "What happens during phase 3 of the TLS rollout in Vespa?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/mtls.html-verify-configuration-of-tls", "fields": {"questions": {"assign": ["What is the tool to verify runtime configuration?", "What port should be used to connect to Vespa?", "How can server client authentication be verified?", "What is the command to use openssl s_client?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/mtls.html-faq", "fields": {"questions": {"assign": ["Is TLS recommended for real-time search?", "How much extra CPU usage does TLS impose?", "What is symmetric encryption?", "How much additional latency does a TLS handshake have?", "How does Vespa handle TLS handshakes?", "Does Vespa use long-lived connections to reduce handshakes?", "How has Vespa tuned its TLS integration?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/mtls.html-certificate-validation-fails-due-to-mismatching-hostnames", "fields": {"questions": {"assign": ["What is the HTTPS endpoint identification algorithm?", "How can I disable hostname validation?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/mtls.html-application-deployment-fails-with-`sec\\_error\\_bad\\_key`", "fields": {"questions": {"assign": ["What causes an error with vespa-deploy?", "Why does vespa-deploy fail on CentOS 7?", "What cryptographic keys can be used with vespa-deploy?", "What is the problem with elliptic curve keys?", "How to resolve vespa-deploy errors on CentOS 7?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/mtls.html-appendix-a:-setting-up-with-a-self-signed-certificate-authority", "fields": {"questions": {"assign": ["What is mTLS communication?", "What is a Certificate Authority?", "What are the steps for creating cryptographic keys?", "What tool is used to generate crypto keys?", "How many times is a root Certificate Authority created?", "What is a Certificate Signing Request?", "What is the purpose of signing a CSR using the CA?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/mtls.html-creating-a-root-certificate-authority-(ca)", "fields": {"questions": {"assign": ["What is a Certificate Authority?", "What is a root Certificate Authority?", "How is a certificate verified?", "What is a certificate chain?", "How is a root CA created?", "What is the difference between RSA and EC cryptography?", "What is a private key?", "How is a CA X.509 certificate created?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/mtls.html-creating-a-private-key-and-certificate-for-a-vespa-host", "fields": {"questions": {"assign": ["What is a Certificate Signing Request?", "How can I create a private cryptographic key?", "What is Elliptic Curve key?", "What is RSA key?", "What is a CSR?", "What is openssl command for creating certificate?", "What are the alternatives for unique certificates per individual host?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/mtls.html-sign-host-certificate", "fields": {"questions": {"assign": ["What is TLS hostname validation?", "What are Subject Alternative Names?", "What is basicConstraints?", "What is keyUsage?", "What is extendedKeyUsage?", "What is subjectKeyIdentifier?", "What is authorityKeyIdentifier?", "What is subjectAltName?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/mtls.html-alternatives-to-having-a-unique-certificate-per-individual-host", "fields": {"questions": {"assign": ["What is a hostname SAN?", "What is a wildcard DNS?", "Why should separate certificates be used for production deployments?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/mtls.html-environment-variables", "fields": {"questions": {"assign": ["What is the VESPA_TLS_CONFIG_FILE?", "What is the VESPA_TLS_INSECURE_MIXED_MODE?", "What does TLS mixed mode refer to?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/mtls.html-tls-mixed-mode", "fields": {"questions": {"assign": ["What are TLS mixed mode settings?", "What is plaintext_client_mixed_server?", "What is tls_client_mixed_server?", "What is tls_client_tls_server?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/mtls.html-configuration-file", "fields": {"questions": {"assign": ["What is a TLS configuration?", "What does the TLS configuration file contain?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/mtls.html-top-level-elements", "fields": {"questions": {"assign": ["What is the 'files' element?", "What is the 'disable-hostname-validation' option?", "What is the 'authorized-peers' option?", "What are TLS cipher suites?", "What are TLS protocol versions?", "Is 'disable-hostname-validation' enabled by default?", "What happens if 'authorized-peers' is not specified?", "What is the purpose of 'accepted-ciphers' option?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/mtls.html-the-*files*-element", "fields": {"questions": {"assign": ["What is the private key file format?", "What is the certificate chain format?", "What format should ca-certificates be in?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/mtls.html-the-*authorized-peer*-element", "fields": {"questions": {"assign": ["What is required-credentials?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/mtls.html-the-*required-credential*-element", "fields": {"questions": {"assign": ["Is the field parameter required?", "Is the must-match parameter required?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/mtls.html-peer-authorization-rules", "fields": {"questions": {"assign": ["What is authorized-peers?", "What is a credential rule-set?", "What is the must-match pattern?", "What happens when authorized-peers is not provided?", "What is required to be considered authorized?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/mtls.html-example", "fields": {"questions": {"assign": ["What information is stored in authorized-peers?", "What are required-credentials in authorized-peers?", "What is the description of the monitoring authorized-peer?", "What must match the SAN_DNS field in the monitoring authorized-peer?", "What is the name of the authorized-peer intended for cluster-internal node P2P access?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/mtls.html-tls-features-supported-by-vespa", "fields": {"questions": {"assign": ["What are the constraints of Vespa TLS stack?", "What cipher suites are supported?", "What is forward secrecy?", "What is AEAD and its properties?", "Why is TLS compression disabled?", "Why is TLS session resumption explicitly disabled for Vespa?", "Does the Vespa application container edge support session resumption?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/mtls.html-default-tls-protocol-settings", "fields": {"questions": {"assign": ["What is the default TLS configuration used by Vespa?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/mtls.html-protocol-versions", "fields": {"questions": {"assign": ["What is TLSv1.3?", "Is Java runtime limitation present?", "Which backends support TLSv1.3?", "What is the TLSv1.2 version?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/mtls.html-cipher-suites", "fields": {"questions": {"assign": ["What are the TLSv1.3 cipher suites?", "What are the JDK 12+ cipher suites?", "What is the ECDHE-RSA cipher suite?", "What is the AES-256-GCM cipher suite?", "What is the CHACHA20_POLY1305_SHA256 cipher suite?", "What are the ECDSA cipher suites?", "What is the AES-128-GCM cipher suite?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/multinode-systems.html-", "fields": {"questions": {"assign": ["What is a Vespa system?", "What is an admin cluster?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/multinode-systems.html-", "fields": {"questions": {"assign": ["How to create multinode system?", "What is an admin cluster?", "What are slobroks?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/multinode-systems.html-aws-ec2", "fields": {"questions": {"assign": ["What is the procedure for multinode setup?", "Where can I find the multinode-HA procedure?", "Can AWS EC2 be used for Vespa multinode setup?", "What is the recommended number of hosts for the multinode setup?", "Is AWS Auto Scaling compatible with Vespa?", "Where can I find the autoscaling Q/A?", "What is the purpose of multinode setup?", "What are the Vespa concepts used in the multinode setup?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/multinode-systems.html-node-setup", "fields": {"questions": {"assign": ["How do I provision nodes?", "What is the security group setup?", "What is the recommended AMI for Vespa?", "What is the format to find hostname for login?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/multinode-systems.html-config-server-cluster-setup", "fields": {"questions": {"assign": ["What is a config server cluster?", "How do you verify a successful start?", "What is the start sequence for config server?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/multinode-systems.html-configure-application", "fields": {"questions": {"assign": ["What needs editing in hosts.xml?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/multinode-systems.html-vespa-nodes-setup", "fields": {"questions": {"assign": ["How to validate Vespa installation?", "Which steps to follow for checking the health interfaces?", "What are the native Vespa ports?", "What is the sample application port?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/multinode-systems.html-terminate-instances", "fields": {"questions": {"assign": ["How do I terminate AWS instances?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/multinode-systems.html-aws-ec2-singlenode", "fields": {"questions": {"assign": ["What is a multinode install?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/multinode-systems.html-create-a-10-node-ecs-cluster", "fields": {"questions": {"assign": ["How can I create a cluster?", "How many instances can I create?", "What are the security group inbound rules?", "What is the status of registered container instances?", "What is the Cluster name?", "What should I do after clicking Create?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/multinode-systems.html-configure-ecs-instances", "fields": {"questions": {"assign": ["What are public hostnames used for?", "How do I find a hostname in Vespa?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/multinode-systems.html-start-the-config-server-task", "fields": {"questions": {"assign": ["How do I create a task definition?", "What is EC2?", "What is networkMode?", "What is family?", "How do I run a task?", "How do I validate the config servers?", "What is memoryReservation?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/multinode-systems.html-configure-application---ecs", "fields": {"questions": {"assign": ["What is multinode-HA?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/multinode-systems.html-start-the-services-tasks", "fields": {"questions": {"assign": ["What is a Task Definition?", "How to create a new Task Definition?", "What is EC2?", "What is the launch type?", "How to validate startup?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/multinode-systems.html-terminate-cluster", "fields": {"questions": {"assign": ["How do I delete an AWS cluster?", "What should I remember when using AWS?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/multinode-systems.html-log-collection", "fields": {"questions": {"assign": ["What is vespa-logfmt?", "What is collected in real time?", "How do I view log messages?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/multinode-systems.html-making-changes-to-live-systems", "fields": {"questions": {"assign": ["What is the admin cluster?", "How do you change the system safely?", "Can you deploy a changed application during live traffic?", "When must restarts be done?", "How to avoid disruption when restarting nodes?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/multinode-systems.html-autoscaling", "fields": {"questions": {"assign": ["What is AWS Auto Scaling?", "Can AWS Auto Scaling be used?", "How to manage stateless nodes?", "How to manage stateful nodes?", "Is autoscaling the stateful nodes safe?", "What is the recommendation on autoscaling?", "How to optimize resources using static node set?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/multinode-systems.html-next-steps", "fields": {"questions": {"assign": ["What is the multinode application?", "What is multinode-HA?"]}}} -{"update": "id:open-p:paragraph::open/en/multivalue-query-operators.html-", "fields": {"questions": {"assign": ["What is this article about?", "What is the ranking introduction?"]}}} -{"update": "id:open-p:paragraph::open/en/multivalue-query-operators.html-dotproduct-and-wand", "fields": {"questions": {"assign": ["What is wand?", "How does wand work?", "What problem does wand solve?", "What is dotProduct?", "How does dotProduct differ from wand?", "Which is more cost-efficient: wand or dotProduct?", "How to compare wand and dotProduct?", "Can wand and dotProduct be benchmarked?"]}}} -{"update": "id:open-p:paragraph::open/en/multivalue-query-operators.html-dotproduct-example", "fields": {"questions": {"assign": ["What is dotProduct?", "How is dotProduct used?", "What are raw scores?", "How are raw scores used?", "How to label dot products for ranking?"]}}} -{"update": "id:open-p:paragraph::open/en/multivalue-query-operators.html-weightedset-example", "fields": {"questions": {"assign": ["What is the weightedSet used for?", "How can we use weightedSet for filtering a search?", "What is the syntax for using a weightedSet in Vespa?", "What is the performance impact of using a weightedSet?", "What type of data models can be used as a filter for weightedSet?", "What are the limitations of using a string field with weightedSet?", "What are the steps to take to implement weightedSet in Vespa search?", "What is the alternative for using a big OR expression for filtering a search?"]}}} -{"update": "id:open-p:paragraph::open/en/multivalue-query-operators.html-raw-scores-and-query-item-labeling", "fields": {"questions": {"assign": ["What is the rawScore feature?", "How does itemRawScore work?", "What is query item labeling?", "What is the wand operator?", "How does Vespa handle raw scores?", "Can query items be labeled?", "What is the output from the matching pipeline?"]}}} -{"update": "id:open-p:paragraph::open/en/nativerank.html-", "fields": {"questions": {"assign": ["What is the *nativeRank* score?", "How is *nativeRank* computed?", "What are the three matching features?", "What is included in *nativeRank*?", "What is not included in *nativeRank*?", "How should *nativeRank* be used?"]}}} -{"update": "id:open-p:paragraph::open/en/nativerank.html-ranking-expressions-and-nativerank", "fields": {"questions": {"assign": ["What is a ranking expression?", "What is nativeRank feature?", "What is the final rank score?", "How are rank features calculated?", "What operators and functions are supported in ranking expressions?"]}}} -{"update": "id:open-p:paragraph::open/en/nativerank.html-using-nativerank", "fields": {"questions": {"assign": ["What is sourcequality?", "What is age(name)?", "How many main rank signals are there?", "What is the nativeRank feature?"]}}} -{"update": "id:open-p:paragraph::open/en/nativerank.html-tuning-nativerank", "fields": {"questions": {"assign": ["What is index field weight?", "How to tune rank-profile?", "What is nativeRank feature?", "What are tuning parameters of nativerank?", "What is the weight of title?", "What is the weight of body?", "What are the core statistics used for boosting tables?", "What are the pre-configured boosting tables for different type of text fields?"]}}} -{"update": "id:open-p:paragraph::open/en/nativerank.html-designing-our-own-blog-freshness-ranking-function", "fields": {"questions": {"assign": ["What is Vespa's freshness rank-feature?", "What is the default freshness rank-feature of Vespa?", "How can we design our own rank-feature in Vespa?", "What is the formula for the freshness function?", "What is the plot comparing freshness functions?", "How do we control freshness rank score in Vespa?", "What is the shape of the freshness function in the blog application?", "What is the equation for the exponential decreasing function in Vespa?"]}}} -{"update": "id:open-p:paragraph::open/en/nativerank.html-putting-our-features-together-into-a-ranking", "fields": {"questions": {"assign": ["How can we control component weight?", "What is the functionality of the freshness feature?", "What is the functionality of the quality feature?", "What is the functionality of the normalization function?", "How can we override signal weights?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/nativerank.html-", "fields": {"questions": {"assign": ["What is nativeRank feature?", "What is included in nativeRank?", "How is nativeRank computed?", "What are the nativeFieldMatch, nativeProximity, and nativeAttributeMatch features?", "What is the recommended use of nativeRank?", "Where can I configure nativeRank features?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/nativerank.html-nativefieldmatch", "fields": {"questions": {"assign": ["What is nativeFieldMatch?", "How is nativeFieldMatch score calculated?", "What is fmMaxTable?", "How is fmMaxTable calculated?", "What is firstOccBoost?", "What is occurrenceCountTable?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/nativerank.html-nativeproximity", "fields": {"questions": {"assign": ["What is nativeProximity?", "How is nativeProximity calculated?", "What are term pairs?", "What is connectedness?", "How can nativeProximity calculation be limited?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/nativerank.html-nativeattributematch", "fields": {"questions": {"assign": ["What is nativeAttributeMatch?", "How is nativeAttributeMatch calculated?", "How can nativeAttributeMatch be limited?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/nativerank.html-nativerank", "fields": {"questions": {"assign": ["What is nativeRank?", "How is nativeRank calculated?", "What are fieldMatchWeight, proximityWeight, and attributeMatchWeight?", "How can you configure nativeRank for a subset of fields?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/nativerank.html-variables", "fields": {"questions": {"assign": ["What is attributeWeightj?", "What is termSignificancei?", "What is fieldWeightj?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/nativerank.html-configuration-properties", "fields": {"questions": {"assign": ["What is the purpose of `nativeFieldMatch`?", "What is the default table used for `firstOccurrenceTable`?", "What is the default importance value for `firstOccurrenceImportance`?", "What is the size of the sliding window used for generating term pairs?", "What is the default weight table used for `nativeAttributeMatch`?", "What is the role of `fieldMatchWeight` in `nativeRank`?", "What is the role of `proximityWeight` in `nativeRank`?", "What is `useTableNormalization` in `nativeRank`?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/nativerank.html-boost-tables", "fields": {"questions": {"assign": ["What is the expdecay function?", "What is the loggrowth function?", "What is the linear function?", "What are the parameters of expdecay?", "What are the parameters of loggrowth?", "What are the parameters of linear?", "How to override default table size?", "How to populate a table with custom function?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/nativerank.html-rank-types", "fields": {"questions": {"assign": ["What are the predefined rank types?", "What is nativeRank?", "What are boost tables?", "How to specify boost tables?", "What happens if boost tables are not specified?", "What are the boost tables for about type?", "What is nativeFieldMatch?", "What is nativeAttributeMatch?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/nativerank.html-nativerank-limitations", "fields": {"questions": {"assign": ["What is nativeRank?", "What does nativeRank not take into account?", "What feature should be used for relevancy based on numeric values?", "What features should be used for relevance based on geographical location?", "What feature should be used for freshness of matching documents?"]}}} -{"update": "id:open-p:paragraph::open/en/nearest-neighbor-search-guide.html-", "fields": {"questions": {"assign": ["What is Vespa nearest neighbor search?", "How can Vespa combine nearest neighbor search with other query operators?", "What candidate retrievers can be used in multi-phase ranking funnel?", "What dataset is used for illustration?", "What does the guide cover?", "What is distanceThresholding?", "How can hybrid sparse and dense retrieval methods be used with Vespa?", "What does the guide provide step-by-step instructions on?"]}}} -{"update": "id:open-p:paragraph::open/en/nearest-neighbor-search-guide.html-installing-vespa-cli", "fields": {"questions": {"assign": ["What is Vespa-CLI?"]}}} -{"update": "id:open-p:paragraph::open/en/nearest-neighbor-search-guide.html-dataset", "fields": {"questions": {"assign": ["What is the Last.fm dataset?", "How can I download the dataset?", "What is the format of the dataset?", "What is the schema used for the dataset?", "What is the popularity of the track?", "How do I process the dataset?"]}}} -{"update": "id:open-p:paragraph::open/en/nearest-neighbor-search-guide.html-create-a-vespa-application-package", "fields": {"questions": {"assign": ["What is a Vespa application package?", "What files are needed for basic search application?", "What does a Vespa application package define?", "What is the purpose of track.sd?", "What are the directory names for configuration files?", "How is data processed during feeding and indexing?"]}}} -{"update": "id:open-p:paragraph::open/en/nearest-neighbor-search-guide.html-schema", "fields": {"questions": {"assign": ["What is a schema?", "What is the HNSW Index?", "What is the practical search performance guide?"]}}} -{"update": "id:open-p:paragraph::open/en/nearest-neighbor-search-guide.html-services-specification", "fields": {"questions": {"assign": ["What is the purpose of presentation.timing?", "How are fields rendered in JSON?", "How many nodes can be run per service?"]}}} -{"update": "id:open-p:paragraph::open/en/nearest-neighbor-search-guide.html-deploy-the-application-package", "fields": {"questions": {"assign": ["How do I deploy the application package?", "What is the Vespa quick start guide?"]}}} -{"update": "id:open-p:paragraph::open/en/nearest-neighbor-search-guide.html-index-the-dataset", "fields": {"questions": {"assign": ["What is feed.jsonl?"]}}} -{"update": "id:open-p:paragraph::open/en/nearest-neighbor-search-guide.html-free-text-search-using-vespa-weakand", "fields": {"questions": {"assign": ["What is type=weakAnd?", "What is the using wand with Vespa guide?"]}}} -{"update": "id:open-p:paragraph::open/en/nearest-neighbor-search-guide.html-maximum-inner-product-search-using-vespa-wand", "fields": {"questions": {"assign": ["What is the 'wand' query?", "What does 'wand' do?", "How does 'wand' work?", "What is the syntax for using 'wand'?", "What is a safe query operator?", "What is the 'rawScore' rank feature?", "What is the result format of the 'wand' query?", "Where can I find more information on 'wand' and 'weakAnd' query operators?"]}}} -{"update": "id:open-p:paragraph::open/en/nearest-neighbor-search-guide.html-exact-nearest-neighbor-search", "fields": {"questions": {"assign": ["How does Vespa perform exact search?"]}}} -{"update": "id:open-p:paragraph::open/en/nearest-neighbor-search-guide.html-approximate-nearest-neighbor-search", "fields": {"questions": {"assign": ["What is approximate nearest neighbor search?", "What is the difference between exact and approximate neighbor search?", "Can the accuracy loss of approximate search be quantified?", "What is 'approximate:true' for?", "How is latency affected by approximate neighbor search?", "What is the difference in memory usage between exact and approximate search?", "How can a developer find the balance between search performance and accuracy?"]}}} -{"update": "id:open-p:paragraph::open/en/nearest-neighbor-search-guide.html-combining-approximate-nearest-neighbor-search-with-query-filters", "fields": {"questions": {"assign": ["What is the 'rank: filter'?", "How to combine search queries?", "What is the most efficient posting list representation?", "What is pure filtering?", "How to define fields for filtering?"]}}} -{"update": "id:open-p:paragraph::open/en/nearest-neighbor-search-guide.html-strict-filters-and-distant-neighbors", "fields": {"questions": {"assign": ["What is distanceThreshold in nearestNeighbor query operator?", "How is the quality of the returned neighbors determined?", "What is the significance of adding distance rank-features to match-features?", "What is post-processing searcher and how does it help?", "What is the importance of supervised learning in calibrating distance threshold?", "What is the impact of strict filters on nearest neighbor search?"]}}} -{"update": "id:open-p:paragraph::open/en/nearest-neighbor-search-guide.html-hybrid-sparse-and-dense-retrieval-methods-with-vespa", "fields": {"questions": {"assign": ["What is hybrid retrieval?", "What is nearestNeighbor operator?", "How to combine efficient query operators?", "Can personalized hits be retrieved?", "How to compute euclidean distance?"]}}} -{"update": "id:open-p:paragraph::open/en/nearest-neighbor-search-guide.html-controlling-filter-behavior", "fields": {"questions": {"assign": ["What is pre-filtering strategy?", "What is post-filtering strategy?", "What is post-filter threshold?", "How to perform exact search?"]}}} -{"update": "id:open-p:paragraph::open/en/nearest-neighbor-search-guide.html-tear-down-the-container", "fields": {"questions": {"assign": ["What does docker rm do?"]}}} -{"update": "id:open-p:paragraph::open/en/nearest-neighbor-search.html-", "fields": {"questions": {"assign": ["What is nearest neighbor search?", "How is nearest neighbor search achieved in Vespa?", "What is the nearestNeighbor query operator?", "How can nearestNeighbor be combined with other operators?", "What are the benefits of combining vector search with traditional retrieval in Vespa?"]}}} -{"update": "id:open-p:paragraph::open/en/nearest-neighbor-search.html-vectors-in-vespa", "fields": {"questions": {"assign": ["What is a vector in Vespa?", "How are document vectors stored?", "What is the difference between dense and mixed tensor types?", "How are query vectors defined?", "What is the distance-metric for searching vectors?"]}}} -{"update": "id:open-p:paragraph::open/en/nearest-neighbor-search.html-using-vespa\u2019s-nearest-neighbor-search", "fields": {"questions": {"assign": ["What is approximate nearest neighbor search?", "What is text_embedding field?", "What is image_embeddings field?", "What is the distance-metric for attribute?", "What is prenormalized-angular?", "What is the relevance of approximate search to exact search?"]}}} -{"update": "id:open-p:paragraph::open/en/nearest-neighbor-search.html-distance-metrics-for-nearest-neighbor-search", "fields": {"questions": {"assign": ["What are the supported distance metrics?", "What is the 'euclidean' distance metric?", "What is the 'angular' distance metric?", "What is the 'prenormalized-angular' distance metric?", "What is the 'hamming' distance metric?", "What is the 'geodegrees' distance metric?"]}}} -{"update": "id:open-p:paragraph::open/en/nearest-neighbor-search.html-configure-rank-profiles-for-nearest-neighbor-search", "fields": {"questions": {"assign": ["What is a rank-profile?", "How is the nearestNeighbor score computed?", "Can a rank-profile have multiple phases?", "How is image similarity calculated?"]}}} -{"update": "id:open-p:paragraph::open/en/nearest-neighbor-search.html-indexing-product-data", "fields": {"questions": {"assign": ["How can one index product data using Vespa?", "What is the short form for mixed tensor?", "What is the short form for indexed tensor?", "What is the purpose of image_embeddings field?"]}}} -{"update": "id:open-p:paragraph::open/en/nearest-neighbor-search.html-querying-using-nearestneighbor-query-operator", "fields": {"questions": {"assign": ["What is the nearestNeighbor operator?", "What is targetHits used for?", "What is the constraint for nearestNeighbor query?", "Where can I specify the query in Vespa?"]}}} -{"update": "id:open-p:paragraph::open/en/nearest-neighbor-search.html-using-nearest-neighbor-from-a-searcher-component", "fields": {"questions": {"assign": ["What is RetrievalModelSearcher?"]}}} -{"update": "id:open-p:paragraph::open/en/nearest-neighbor-search.html-using-binary-embeddings-with-hamming-distance", "fields": {"questions": {"assign": ["What is hamming distance search?", "What is the use of num-threads-per-search?", "What are the cpu instructions implemented in hamming distance search?", "Where can I find a detailed introduction to using binary vectors with hamming distance?"]}}} -{"update": "id:open-p:paragraph::open/en/tutorials/news-1-getting-started.html-", "fields": {"questions": {"assign": ["What is the goal of this tutorial?", "What are the different parts of the tutorial?", "What is covered in the getting started part?", "What is the recommended entry point for this tutorial?", "What is covered in part 2 of the tutorial?", "What is covered in part 5 of the tutorial?"]}}} -{"update": "id:open-p:paragraph::open/en/tutorials/news-1-getting-started.html-installing-vespa-cli", "fields": {"questions": {"assign": ["What is Vespa-CLI?", "What operating systems is Vespa-CLI available for?", "How do I install Vespa-CLI?"]}}} -{"update": "id:open-p:paragraph::open/en/tutorials/news-1-getting-started.html-a-minimal-vespa-application", "fields": {"questions": {"assign": ["What is the companion sample application?", "How do I clone the sample application?", "Where is the news directory located?", "What is contained in the app-1-getting-started directory?"]}}} -{"update": "id:open-p:paragraph::open/en/tutorials/news-1-getting-started.html-starting-vespa", "fields": {"questions": {"assign": ["What happens when you run 'vespa deploy' command?", "Can you deploy a new version of the application without restarting services?"]}}} -{"update": "id:open-p:paragraph::open/en/tutorials/news-1-getting-started.html-feeding-to-vespa", "fields": {"questions": {"assign": ["What is feeding in Vespa?", "What is vespa-feed-client?", "What is the Vespa CLI used for?"]}}} -{"update": "id:open-p:paragraph::open/en/tutorials/news-1-getting-started.html-querying-vespa", "fields": {"questions": {"assign": ["How to search for a phrase in title?", "What is demonstrated in the next part of the tutorial?"]}}} -{"update": "id:open-p:paragraph::open/en/tutorials/news-1-getting-started.html-remove-documents", "fields": {"questions": {"assign": ["How to remove document from Vespa?"]}}} -{"update": "id:open-p:paragraph::open/en/tutorials/news-1-getting-started.html-stopping-and-starting-vespa", "fields": {"questions": {"assign": ["How to start Vespa services?", "How to stop Vespa services?"]}}} -{"update": "id:open-p:paragraph::open/en/tutorials/news-1-getting-started.html-conclusion", "fields": {"questions": {"assign": ["Is the application up and running?", "What is the next tutorial about?"]}}} -{"update": "id:open-p:paragraph::open/en/tutorials/news-2-basic-feeding-and-query.html-", "fields": {"questions": {"assign": ["What is the tutorial series about?", "What is the final state of this tutorial?", "What is app-2-feed-and-query?"]}}} -{"update": "id:open-p:paragraph::open/en/tutorials/news-2-basic-feeding-and-query.html-the-microsoft-news-dataset", "fields": {"questions": {"assign": ["What is the Microsoft News Dataset?", "What does the MIND dataset contain?", "What is included in the news article content?", "What are impressions in the MIND dataset?", "How many articles are in the MIND dataset?", "What is the size of the demo dataset?", "How to download data for the news sample app?", "What data is in the news.tsv file?"]}}} -{"update": "id:open-p:paragraph::open/en/tutorials/news-2-basic-feeding-and-query.html-application-packages", "fields": {"questions": {"assign": ["What is an application package?", "What is the hosts.xml file?", "What is needed for self-hosted multi-node deployments?"]}}} -{"update": "id:open-p:paragraph::open/en/tutorials/news-2-basic-feeding-and-query.html-services-specification", "fields": {"questions": {"assign": ["What does services.xml define?", "How is the content cluster capacity increased?"]}}} -{"update": "id:open-p:paragraph::open/en/tutorials/news-2-basic-feeding-and-query.html-schema", "fields": {"questions": {"assign": ["What is the index property?"]}}} -{"update": "id:open-p:paragraph::open/en/tutorials/news-2-basic-feeding-and-query.html-deploy-the-application-package", "fields": {"questions": {"assign": ["What files are necessary for deployment?", "What is the wait time for deployment?"]}}} -{"update": "id:open-p:paragraph::open/en/tutorials/news-2-basic-feeding-and-query.html-feeding-data", "fields": {"questions": {"assign": ["How do you convert MIND data for Vespa?", "What file contains MIND data for Vespa?", "How can you verify that specific documents are indexed?"]}}} -{"update": "id:open-p:paragraph::open/en/tutorials/news-2-basic-feeding-and-query.html-the-first-query", "fields": {"questions": {"assign": ["How can the query be executed using both GET and POST?", "What does the totalCount field contain?", "What is query tracing?", "Why should HTTP POST be used over GET in production?"]}}} -{"update": "id:open-p:paragraph::open/en/tutorials/news-2-basic-feeding-and-query.html-query-examples", "fields": {"questions": {"assign": ["What is type=phrase?", "How can you boost documents in a query?", "What is YQL userQuery()?"]}}} -{"update": "id:open-p:paragraph::open/en/tutorials/news-2-basic-feeding-and-query.html-conclusion", "fields": {"questions": {"assign": ["What will the next part of the tutorial cover?", "What is explored in the next part of the tutorial?"]}}} -{"update": "id:open-p:paragraph::open/en/tutorials/news-3-searching.html-what-is-an-attribute%3F", "fields": {"questions": {"assign": ["What is an attribute?", "What is fast-search?", "What is the news.sd schema?"]}}} -{"update": "id:open-p:paragraph::open/en/tutorials/news-3-searching.html-example-queries-using-attribute-field", "fields": {"questions": {"assign": ["What is a single-term query?", "What fields are searched in default fieldset?", "What fields are searched with =?", "How can you combine two terms in Vespa search?", "How do you query for documents created on a specific date?", "What happens when a field is not included in a fieldset?"]}}} -{"update": "id:open-p:paragraph::open/en/tutorials/news-3-searching.html-range-searches", "fields": {"questions": {"assign": ["What are range searches?", "What operators can be used for range searches?", "How do you search for documents before a certain date?", "How do you search for documents within a date range?"]}}} -{"update": "id:open-p:paragraph::open/en/tutorials/news-3-searching.html-sorting-on-attribute-fields", "fields": {"questions": {"assign": ["How to change hit order?", "How to specify sort order?"]}}} -{"update": "id:open-p:paragraph::open/en/tutorials/news-3-searching.html-query-time-result-grouping", "fields": {"questions": {"assign": ["What are some common use cases for grouping?", "How do you group documents by time and date?", "What is result diversification?", "How do you perform a grouping query in Vespa?", "What is the output of a grouping query in Vespa?", "What is the default sorting in a grouping query?"]}}} -{"update": "id:open-p:paragraph::open/en/tutorials/news-3-searching.html-matching---index-versus-attribute", "fields": {"questions": {"assign": ["What are the differences between index and attribute?"]}}} -{"update": "id:open-p:paragraph::open/en/tutorials/news-3-searching.html-matching", "fields": {"questions": {"assign": ["How does Vespa tokenize strings?", "What is stemming in linguistic processing?", "What are the search capabilities for exact phrase queries?", "What is the difference between 'match:text' and indexing:attributes?"]}}} -{"update": "id:open-p:paragraph::open/en/tutorials/news-3-searching.html-memory-usage", "fields": {"questions": {"assign": ["How does memory usage affect cost-effectiveness?", "What is the performance impact of storing large flavor types as attributes?"]}}} -{"update": "id:open-p:paragraph::open/en/tutorials/news-3-searching.html-when-to-use-attributes", "fields": {"questions": {"assign": ["What are the advantages of attributes?", "When to use attributes?", "What fields support higher update throughput?", "Which fields are used for sorting?", "What types of field must be defined with attribute?"]}}} -{"update": "id:open-p:paragraph::open/en/tutorials/news-3-searching.html-combining-index-and-attribute", "fields": {"questions": {"assign": ["What is the category field?", "What is attribute indexing?", "What is index matching?"]}}} -{"update": "id:open-p:paragraph::open/en/tutorials/news-3-searching.html-relevance-and-ranking", "fields": {"questions": {"assign": ["What is relevance?", "What is the default ranking function?", "How can ranking signals be added?", "What are the use cases for tweaking relevance?", "What is Learning to Rank?"]}}} -{"update": "id:open-p:paragraph::open/en/tutorials/news-3-searching.html-news-article-popularity-signal", "fields": {"questions": {"assign": ["What is the purpose of a popularity rank profile?", "What is a two-phase calculation?", "What is the function of the popularity method?", "What are the attributes available for an article in the news dataset?", "What is impressions?", "How can we use impressions in ranking?"]}}} -{"update": "id:open-p:paragraph::open/en/tutorials/news-3-searching.html-conclusion", "fields": {"questions": {"assign": ["What does this tutorial cover?", "How do you load data?", "What is the next tutorial about?"]}}} -{"update": "id:open-p:paragraph::open/en/tutorials/news-4-embeddings.html-", "fields": {"questions": {"assign": ["What is this tutorial about?", "What is the focus of recommendation systems?", "What is the collaborative filtering method?", "What is the content-based approach?", "How can user profiles improve recommendations?", "What is the difference between maximum inner product search and euclidean distance search?"]}}} -{"update": "id:open-p:paragraph::open/en/tutorials/news-4-embeddings.html-requirements", "fields": {"questions": {"assign": ["What machine learning tools are needed?", "What is Numpy used for?", "What is Scikit-learn used for?", "What is PyTorch used for?", "What is the HuggingFace Transformers library used for?", "What is the command to install necessary dependencies?"]}}} -{"update": "id:open-p:paragraph::open/en/tutorials/news-4-embeddings.html-the-mind-dataset", "fields": {"questions": {"assign": ["What is the MIND dataset?", "What is an impression?", "What is included in an impression?", "What is a skip?", "What is implicit feedback?", "How can users' interests be inferred?", "How are clicks and skips used?"]}}} -{"update": "id:open-p:paragraph::open/en/tutorials/news-4-embeddings.html-collaborative-filtering-in-recommendation-systems", "fields": {"questions": {"assign": ["What is matrix factorization?", "What is collaborative filtering?", "What is the 'cold start' problem?", "How is recommendation generated?", "What approach does Vespa use for recommendation?", "What is side information?", "How does Vespa handle the 'cold start' problem?"]}}} -{"update": "id:open-p:paragraph::open/en/tutorials/news-4-embeddings.html-generating-embeddings", "fields": {"questions": {"assign": ["What is Alternating Least Squares?", "What is PyTorch?", "What is `Embedding` class?", "What are user and news embeddings?", "What is the loss function used?", "What is the meaning of AUC?"]}}} -{"update": "id:open-p:paragraph::open/en/tutorials/news-4-embeddings.html-addressing-the-cold-start-problem", "fields": {"questions": {"assign": ["What is content-based recommendation?", "How are category embeddings generated?"]}}} -{"update": "id:open-p:paragraph::open/en/tutorials/news-4-embeddings.html-creating-bert-embeddings", "fields": {"questions": {"assign": ["What is the BERT model?", "What is the hidden dimension size?", "How do you create embeddings?", "What is the purpose of the sentence classifier?", "What is the output size of the embedding?", "What is the format of the generated embeddings file?"]}}} -{"update": "id:open-p:paragraph::open/en/tutorials/news-4-embeddings.html-training-the-model", "fields": {"questions": {"assign": ["What is ContentBasedModel?", "What is the purpose of BERT embeddings?", "What losses are being measured?", "What is the baseline model's AUC score?"]}}} -{"update": "id:open-p:paragraph::open/en/tutorials/news-4-embeddings.html-mapping-from-inner-product-search-to-euclidean-search", "fields": {"questions": {"assign": ["What is MIPS?", "What is Euclidean nearest neighbor search?", "What is the technique discussed in the paper?", "How can we map the MIPS problem to a Euclidean nearest neighbor search problem?", "What is the purpose of the convert_embeddings_to_vespa_format.py script?", "What is the output of the convert_embeddings_to_vespa_format.py script?", "What is discussed in the Nearest Neighbor Search page?", "What is the next step after creating a feed suitable for Vespa?"]}}} -{"update": "id:open-p:paragraph::open/en/tutorials/news-4-embeddings.html-conclusion", "fields": {"questions": {"assign": ["How are news items recommended?", "What are user and document embeddings?", "What happens in the next part of the tutorial?"]}}} -{"update": "id:open-p:paragraph::open/en/tutorials/news-5-recommendation.html-", "fields": {"questions": {"assign": ["What are the parts of the tutorial?", "What is covered in part 5?", "Where can I find the final state of the tutorial app?"]}}} -{"update": "id:open-p:paragraph::open/en/tutorials/news-5-recommendation.html-indexing-embeddings", "fields": {"questions": {"assign": ["What is the purpose of 'embedding' field?", "How to feed user and news embeddings?"]}}} -{"update": "id:open-p:paragraph::open/en/tutorials/news-5-recommendation.html-query-profiles-and-query-profile-types", "fields": {"questions": {"assign": ["What is a query profile type?", "How do you set up query profiles?", "What is the expected tensor type?", "How do you deploy query profile updates?"]}}} -{"update": "id:open-p:paragraph::open/en/tutorials/news-5-recommendation.html-testing-the-application", "fields": {"questions": {"assign": ["What is the nearestNeighbor search operator?", "What is user_search.py?", "What is evaluate.py used for?", "What is the output of evaluate.py?"]}}} -{"update": "id:open-p:paragraph::open/en/tutorials/news-5-recommendation.html-approximate-nearest-neighbor-search", "fields": {"questions": {"assign": ["What is approximate nearest neighbor?", "What is the trade-off for ANN?", "What is the gain in efficiency?", "What is unique about Vespa's ANN implementation?"]}}} -{"update": "id:open-p:paragraph::open/en/tutorials/news-5-recommendation.html-filtering", "fields": {"questions": {"assign": ["How can query filters be used?", "What is post-filtering?", "What is pre-filtering?", "What are the tradeoffs of pre- and post-filtering?", "How does Vespa handle strict filters?", "What is the efficiency of Vespa's fallback search?"]}}} -{"update": "id:open-p:paragraph::open/en/tutorials/news-5-recommendation.html-conclusion", "fields": {"questions": {"assign": ["What is the recommendation system?"]}}} -{"update": "id:open-p:paragraph::open/en/tutorials/news-6-recommendation-with-searchers.html-searchers-and-document-processors", "fields": {"questions": {"assign": ["What invokes Searchers?", "Can developers create their own Searchers?", "What is the purpose of the user_search.py script?"]}}} -{"update": "id:open-p:paragraph::open/en/tutorials/news-6-recommendation-with-searchers.html-adding-a-user-profile-searcher", "fields": {"questions": {"assign": ["What is the UserProfileSearcher?", "What does the UserProfileSearcher do?", "What does the UserProfileSearcher extend?", "What is NearestNeighborItem?", "Where is the user_id retrieved from?", "What happens when no user_id is given?"]}}} -{"update": "id:open-p:paragraph::open/en/tutorials/news-6-recommendation-with-searchers.html-adding-a-search-chain", "fields": {"questions": {"assign": ["How is Java code added to Vespa?"]}}} -{"update": "id:open-p:paragraph::open/en/tutorials/news-6-recommendation-with-searchers.html-testing", "fields": {"questions": {"assign": ["What command returns top news articles for user U33527?", "What searcher is invoked in 'user' chain?", "What should the `trace.level` be for searcher invocation?", "What is the default chain for recommended news search?"]}}} -{"update": "id:open-p:paragraph::open/en/tutorials/news-6-recommendation-with-searchers.html-document-processors", "fields": {"questions": {"assign": ["What is the architecture overview?", "What is the difference between Document Processors and Searchers?", "Can Vespa support custom Document Processors?", "Where can I find more information about custom Document Processors?", "What happens to a document in Vespa before storage and indexing?"]}}} -{"update": "id:open-p:paragraph::open/en/tutorials/news-6-recommendation-with-searchers.html-improving-recommendation-diversity", "fields": {"questions": {"assign": ["What is a blending Searcher?", "What is reciprocal rank fusion?"]}}} -{"update": "id:open-p:paragraph::open/en/tutorials/news-7-recommendation-with-parent-child.html-", "fields": {"questions": {"assign": ["What is category CTR?", "What is parent-child in Vespa?", "What is the final state of the tutorial?", "What is the efficient way to update CTR?", "What is the recommendation based on?"]}}} -{"update": "id:open-p:paragraph::open/en/tutorials/news-7-recommendation-with-parent-child.html-parent-child-relationships-in-vespa", "fields": {"questions": {"assign": ["What are parent-child relationships?", "How do parent-child relationships work?", "What is the benefit of parent-child relationships?", "What is UserProfileSearcher?", "Where can I find more information on parent-child relationships?", "What is global CTR value?"]}}} -{"update": "id:open-p:paragraph::open/en/tutorials/news-7-recommendation-with-parent-child.html-setting-up-a-global-category-ctr-document", "fields": {"questions": {"assign": ["What is category_ctr document type?", "What is category_ctr schema?", "What is tensor modify?", "What is sparse dimension?", "What are the limits of parent-child relationship?"]}}} -{"update": "id:open-p:paragraph::open/en/tutorials/news-7-recommendation-with-parent-child.html-importing-parent-values-in-child-documents", "fields": {"questions": {"assign": ["What is `category_ctr`?", "How do we use `category_ctr`?", "How does the import statement work?"]}}} -{"update": "id:open-p:paragraph::open/en/tutorials/news-7-recommendation-with-parent-child.html-tensor-expressions-in-ranking", "fields": {"questions": {"assign": ["What is a tensor expression?", "How do you calculate global CTR score?", "What is a sparse tensor?", "What is the conceptually \"inner join\" in tensor multiplication?"]}}} -{"update": "id:open-p:paragraph::open/en/tutorials/news-7-recommendation-with-parent-child.html-feeding-parent-and-child-updates", "fields": {"questions": {"assign": ["What is create_category_ctrs.py?", "What is the purpose of global_category_ctr.json?", "What is the purpose of news_category_ctr_update.json?", "Which file needs to be fed to Vespa first?", "What is the command to create feed files?"]}}} -{"update": "id:open-p:paragraph::open/en/tutorials/news-7-recommendation-with-parent-child.html-testing-the-application", "fields": {"questions": {"assign": ["What is the relevance score?", "What is the category_ctr function?", "What is the purpose of global parameters?", "How can additional features affect the user experience?"]}}} -{"update": "id:open-p:paragraph::open/en/tutorials/news-7-recommendation-with-parent-child.html-conclusion", "fields": {"questions": {"assign": ["What is a parent-child relationship in Vespa?", "What is tensor-based ranking in Vespa?", "What is the global CTR feature in Vespa?", "Where can I find a real-world use case of parent-child tensors in Vespa?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/node-setup.html-", "fields": {"questions": {"assign": ["What is Vespa composed of?", "What kind of hardware can Vespa run on?", "How does Vespa achieve flexibility?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/node-setup.html-node", "fields": {"questions": {"assign": ["What is a node?", "What is network connectivity?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/node-setup.html-memory-settings", "fields": {"questions": {"assign": ["What is the purpose of minimum memory settings?", "What is the recommended memory setting?", "How do you find optimal node configuration?", "What is covered in the performance guides?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/node-setup.html-hostname", "fields": {"questions": {"assign": ["How does a node identify itself?", "What is required for a node to get its configuration?", "What hostname is used for identifying a node?", "What is the requirement for the hostname configured in hosts.xml?", "What is recommended when using IP addresses without DNS names?", "What must a node know about its hostname?", "What command should usually be run to achieve agreement about a hostname?", "What environment variable can be used to set a different hostname value?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/node-setup.html-simple-single-node-development-environment", "fields": {"questions": {"assign": ["How to override VESPA_HOSTNAME?", "What is default-env.txt?", "How to avoid single-node setup hassle?", "What is the command to override VESPA_HOSTNAME?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/nodeadmin-metrics-reference.html-", "fields": {"questions": {"assign": ["What is endpoint certificate expiry?", "What unit does node certificate use?", "What does endpoint.certificate.expiry.seconds measure?"]}}} -{"update": "id:open-p:paragraph::open/en/onnx.html-", "fields": {"questions": {"assign": ["What is ONNX format?", "How to implement advanced ranking models in Vespa?"]}}} -{"update": "id:open-p:paragraph::open/en/onnx.html-importing-onnx-model-files", "fields": {"questions": {"assign": ["What is ONNX?", "How to add a model?", "How to deploy remote models?", "What is model-exporting notebook?", "What is model-deployment example?", "What is the structure for a model file?", "How to download models during deployment?"]}}} -{"update": "id:open-p:paragraph::open/en/onnx.html-ranking-with-onnx-models", "fields": {"questions": {"assign": ["What is onnx-model?", "How is my_onnx_model defined?", "What are inputs to onnx-model?", "What is the output of a model?", "Are scalars supported in input tensors?"]}}} -{"update": "id:open-p:paragraph::open/en/onnx.html-batch-dimensions", "fields": {"questions": {"assign": ["What does -1 denote?", "What is batch dimension?", "What is batch size?", "What is mini-batch?", "What is Vespa run-time evaluation?", "How does Vespa do inference?", "What should batch dimension be set to during run-time evaluation?", "Why set batch dimension to 1 during run-time evaluation?"]}}} -{"update": "id:open-p:paragraph::open/en/onnx.html-limitations-on-model-size-and-complexity", "fields": {"questions": {"assign": ["What is ONNX model evaluation?", "What are the limitations of evaluating ONNX models?", "How can deep models be made feasible?", "What are the restrictions of evaluating models?", "What is the assumption when evaluating ONNX models in Vespa?"]}}} -{"update": "id:open-p:paragraph::open/en/onnx.html-examples", "fields": {"questions": {"assign": ["What is the Transformers sample app?", "What is the purpose of the ONNX model in the Transformers app?", "What is the HuggingFace's Transformers library?", "What is the Question-Answering sample app?", "What are the two ONNX models used in the Question-Answering app?"]}}} -{"update": "id:open-p:paragraph::open/en/onnx.html-using-vespa-analyze-onnx-model", "fields": {"questions": {"assign": ["What is vespa-analyze-onnx-model?", "What can vespa-analyze-onnx-model find?", "What does vespa-analyze-onnx-model output?", "What is the format of the input/output tensors?", "What does the flowercategory field represent?", "What indexing options are used for the flowercategory field?"]}}} -{"update": "id:open-p:paragraph::open/en/overview.html-", "fields": {"questions": {"assign": ["How does Vespa enable low-latency computation?", "Can Vespa be customized?", "What is machine-learned model inference in Vespa?"]}}} -{"update": "id:open-p:paragraph::open/en/overview.html-vespa-operations", "fields": {"questions": {"assign": ["What operations does Vespa accept?", "What is the visit operation?"]}}} -{"update": "id:open-p:paragraph::open/en/overview.html-the-stateless-container", "fields": {"questions": {"assign": ["What does dependency injection mean?", "What does the container provide?", "What protocols/transports can be plugged in?", "How can developers make changes to components?", "What does the system take care of?", "What is the network layer for?"]}}} -{"update": "id:open-p:paragraph::open/en/overview.html-content-clusters", "fields": {"questions": {"assign": ["What are content clusters?", "What is the purpose of data replication?", "Can clusters grow or shrink while online?", "What is the design goal of Vespa?"]}}} -{"update": "id:open-p:paragraph::open/en/overview.html-administration-and-developer-support", "fields": {"questions": {"assign": ["What is the single administration and configuration cluster?", "What is the purpose of the administration cluster?"]}}} -{"update": "id:open-p:paragraph::open/en/overview.html-summary", "fields": {"questions": {"assign": ["How does Vespa handle evolving applications?", "What does Vespa allow developers to avoid?"]}}} -{"update": "id:open-p:paragraph::open/ja/overview.html-\u30b9\u30c6\u30fc\u30c8\u30ec\u30b9\u30fb\u30b3\u30f3\u30c6\u30ca\u30fc", "fields": {"questions": {"assign": ["What is \u30b3\u30f3\u30c6\u30ca\u30fc\u30fb\u30af\u30e9\u30b9\u30bf\u30fc?", "What is Guice?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/page-result-format.html-", "fields": {"questions": {"assign": ["What is the page result format?", "When is this format used?", "What is presentation.format?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/page-result-format.html-", "fields": {"questions": {"assign": ["What is the root tag?", "What is 'layout' attribute used for?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/page-result-format.html-
    ", "fields": {"questions": {"assign": ["What is a layout box?", "What attributes describe a layout box?", "What are the sub tags of a layout box?", "Can a layout box have nested sections?", "What is the 'region' attribute used for?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/page-result-format.html-", "fields": {"questions": {"assign": ["What is a renderer?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/page-result-format.html-", "fields": {"questions": {"assign": ["What is the 'url' attribute used for?", "What is 'inline content'?", "What is a 'subtag' in this document?", "What is the description for the 'parameter' subtag?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/page-result-format.html-", "fields": {"questions": {"assign": ["What is a result content item?", "What attributes does a hit have?", "What is a subtag in a hit?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/page-result-format.html-", "fields": {"questions": {"assign": ["What is a nested hit?", "What is a hit hierarchy?", "What is a group tag?", "What is a subtag?", "Can a hit have nested hits?", "What are the tags in a hit?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/page-result-format.html-example", "fields": {"questions": {"assign": ["What is a page renderer?", "What is a two-column renderer?", "What is left region?", "What is right region?", "What is articleBodyRenderer?", "What is newsImageRenderer?", "What is imageRenderer?", "What is htmlRenderer?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/page-templates-syntax.html-", "fields": {"questions": {"assign": ["What is the Page Template XML format?", "What is a page template?", "What is the structure of a page template?", "What are the elements of a page template?", "What is a page-element?", "What is a choice?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/page-templates-syntax.html-id", "fields": {"questions": {"assign": ["What is the format of an id?", "What is the major component?", "What is the minor component?", "What is the micro component?", "What is the qualifier component?", "How are omitted numeric components handled?", "How is a missing qualifier handled?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/page-templates-syntax.html-page", "fields": {"questions": {"assign": ["What is a page template?", "What is the root tag?", "What are the attributes of a page template?", "What is the purpose of a section in a page template?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/page-templates-syntax.html-section", "fields": {"questions": {"assign": ["What is a section?", "What is the default layout?", "What is the max number of items for a section?", "What is the min number of items for a section?", "What is the order for displaying items in a section?", "What is the source attribute for a section?", "What is a subsection?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/page-templates-syntax.html-source", "fields": {"questions": {"assign": ["What is a data source?", "What are the attributes of a data source?", "What is the default value for name in a data source?", "What is the default value for url in a data source?", "What is the purpose of url in a data source?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/page-templates-syntax.html-renderer", "fields": {"questions": {"assign": ["What is a renderer?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/page-templates-syntax.html-choice", "fields": {"questions": {"assign": ["What is an alternative tag?", "What is the default for method?", "What does the alternative tag enclose?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/page-templates-syntax.html-contained-tags", "fields": {"questions": {"assign": ["What is a page-element?", "What is an alternative?", "What is a mapping function?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/page-templates-syntax.html-map", "fields": {"questions": {"assign": ["What is a map?", "What is a placeholder?", "What is a page element?", "What is an item?", "What is the purpose of a map?", "How many placeholder ids can 'to' have?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/page-templates-syntax.html-include", "fields": {"questions": {"assign": ["What is included in the page element?", "What is the default of the idref attribute?", "What is the include tag?", "What is the mandatory attribute?"]}}} -{"update": "id:open-p:paragraph::open/en/page-templates.html-", "fields": {"questions": {"assign": ["What are page templates?", "What is a resolver?", "What is the page template language?", "What is found in the page template reference?"]}}} -{"update": "id:open-p:paragraph::open/en/page-templates.html-getting-started", "fields": {"questions": {"assign": ["What is a page template?", "Where should template XML files be placed?", "What is the PageTemplateSearcher?", "What is the purpose of a presentation layer?"]}}} -{"update": "id:open-p:paragraph::open/en/page-templates.html-introduction-to-page-templates", "fields": {"questions": {"assign": ["What is a page template?", "How to define sections in templates?", "What is a nested section?", "What is a source in a template?", "What is a choice in a template?", "How to specify renderers in templates?", "What parameters can a source or a renderer take?", "What is an alternative tag in a choice?"]}}} -{"update": "id:open-p:paragraph::open/en/page-templates.html-using-choice-resolvers", "fields": {"questions": {"assign": ["What are template choices?", "Why do we need a resolver?", "How do you create a resolver?", "Can demo resolvers be used?", "What is resolver deployment?"]}}} -{"update": "id:open-p:paragraph::open/en/page-templates.html-writing-a-resolver", "fields": {"questions": {"assign": ["What are resolvers?", "What is the Resolver API?", "What does a Resolver method do?", "What is a page template?", "What is Resolution?", "How does RandomResolver choose alternatives?", "What is the purpose of the RandomResolver?"]}}} -{"update": "id:open-p:paragraph::open/en/page-templates.html-deploying-a-resolver", "fields": {"questions": {"assign": ["How are resolvers packaged?", "How is page template searcher configured?"]}}} -{"update": "id:open-p:paragraph::open/en/page-templates.html-choosing-a-resolver", "fields": {"questions": {"assign": ["How is the resolver determined?", "What is the page.resolver property?", "What happens if page.resolver is not set?", "What is the random resolver?", "What is the deterministic resolver?", "What is the default resolver?", "How can the resolver be set programmatically?", "What is a resolver component?"]}}} -{"update": "id:open-p:paragraph::open/en/page-templates.html-examples", "fields": {"questions": {"assign": ["What is a blending search result page?", "What is a richer search result page?", "What is a page template mapping?"]}}} -{"update": "id:open-p:paragraph::open/en/parent-child.html-", "fields": {"questions": {"assign": ["What is parent/child relationship?", "What is the alternative to parent documents?", "What is the high-level overview of parent/child documents in Vespa?"]}}} -{"update": "id:open-p:paragraph::open/en/parent-child.html-parent-documents", "fields": {"questions": {"assign": ["What are parent-child relationships?", "How are fields imported in Vespa parent-child relationships?", "How does Vespa handle parent fields updates?", "What are the performance notes for Vespa parent-child relationships?"]}}} -{"update": "id:open-p:paragraph::open/en/parent-child.html-multivalue-fields", "fields": {"questions": {"assign": ["What is a multivalue field?"]}}} -{"update": "id:open-p:paragraph::open/en/parent-child.html-parent-or-multivalue%3F", "fields": {"questions": {"assign": ["How to model shared properties?", "How to model properties using arrays or maps of structs?"]}}} -{"update": "id:open-p:paragraph::open/en/partial-updates.html-", "fields": {"questions": {"assign": ["What is a partial update?", "What is the effect of partial update on throughput?", "How are summary and index fields updated?", "What is the effect of partial update on write throughput?"]}}} -{"update": "id:open-p:paragraph::open/en/partial-updates.html-use-cases", "fields": {"questions": {"assign": ["What are use cases for partial updates?", "How do partial updates improve performance?", "What is the purpose of partial updates?", "What is the difference between partial updates and full updates?"]}}} -{"update": "id:open-p:paragraph::open/en/partial-updates.html-write-pipeline", "fields": {"questions": {"assign": ["Does completing an update return a synchronous ACK?", "Are attributes and summary stores updated in memory or on disk?"]}}} -{"update": "id:open-p:paragraph::open/en/partial-updates.html-further-reading", "fields": {"questions": {"assign": ["How can parent attributes help update rates?"]}}} -{"update": "id:open-p:paragraph::open/en/phased-ranking.html-", "fields": {"questions": {"assign": ["What are rank phases?", "What is first-phase ranking?", "What is second-phase ranking?", "What is global-phase ranking?"]}}} -{"update": "id:open-p:paragraph::open/en/phased-ranking.html-simple-ranking-on-content-nodes", "fields": {"questions": {"assign": ["What is normal ranking?", "What is a first-phase expression?", "What is the cost of first-phase?", "How is the first-phase configured?", "What is evaluated in the first-phase?", "What is the complexity of first-phase?", "How does scalability affect first-phase?"]}}} -{"update": "id:open-p:paragraph::open/en/phased-ranking.html-two-phase-ranking-on-content-nodes", "fields": {"questions": {"assign": ["What is second-phase ranking?", "What is first-phase ranking?", "What is freshness ranking?", "What is the rerank-count set to?"]}}} -{"update": "id:open-p:paragraph::open/en/phased-ranking.html-stateless-re-ranking", "fields": {"questions": {"assign": ["What are stateless searchers?", "How can hits be re-ranked?", "What is ReRankingSearcher?"]}}} -{"update": "id:open-p:paragraph::open/en/phased-ranking.html-top-k-query-operators", "fields": {"questions": {"assign": ["What is Weak And/WAND?", "What is the internal operator ranking?", "How does Weak And/WAND work?", "What is the targeted number of hits for Weak And/WAND?", "What is the skipped set in Weak And/WAND?", "What is the effect of not fully evaluating all documents matching the query in Weak And/WAND?"]}}} -{"update": "id:open-p:paragraph::open/en/phased-ranking.html-choosing-phased-ranking-functions", "fields": {"questions": {"assign": ["What is a good ranking expression?", "Why should application ranking be a second phase function?", "What is the task for finding a first phase function?", "What is the goal of finding a first phase function?", "How can relevance be ensured without evaluating the real ranking function on all the hits?"]}}} -{"update": "id:open-p:paragraph::open/en/phased-ranking.html-rank-phase-statistics", "fields": {"questions": {"assign": ["What are mutable attributes?", "What is the common use case for mutable attributes?"]}}} -{"update": "id:open-p:paragraph::open/en/phased-ranking.html-usage", "fields": {"questions": {"assign": ["How to initialize mutable attribute?", "What is the purpose of searchPath?"]}}} -{"update": "id:open-p:paragraph::open/en/jdisc/pluggable-frameworks.html-", "fields": {"questions": {"assign": ["What are Service Provider Interfaces (SPI)?", "What is a pluggable architecture?", "How do libraries provide pluggable architectures?", "What is required to use libraries with SPI?", "Is manual setup needed for libraries with SPI?", "What kind of libraries provide pluggable architectures?", "How does SPI benefit applications?", "What is the role of SPI in libraries?"]}}} -{"update": "id:open-p:paragraph::open/en/jdisc/pluggable-frameworks.html-importing-the-missing-package", "fields": {"questions": {"assign": ["What does ClassNotFoundException mean?", "How to solve ClassNotFoundException?"]}}} -{"update": "id:open-p:paragraph::open/en/jdisc/pluggable-frameworks.html-exporting-the-missing-package-from-the-container", "fields": {"questions": {"assign": ["What is the missing package?", "How do we export packages?", "Where is the export_packages defined?", "How is missing package resolved?", "How to export missing package in Vespa?"]}}} -{"update": "id:open-p:paragraph::open/en/performance/practical-search-performance-guide.html-", "fields": {"questions": {"assign": ["What is the purpose of this guide?", "What dataset does this guide use?", "What query performance aspects are covered?", "What is multithreaded search?", "What is early termination for range search?", "What is advanced query tracing?"]}}} -{"update": "id:open-p:paragraph::open/en/performance/practical-search-performance-guide.html-installing-vespa-cli", "fields": {"questions": {"assign": ["What is Vespa-CLI?", "What is the official command-line client for Vespa.ai?", "Is Vespa-CLI available for Windows?", "What are the system requirements for Vespa-CLI installation?"]}}} -{"update": "id:open-p:paragraph::open/en/performance/practical-search-performance-guide.html-dataset", "fields": {"questions": {"assign": ["What is the Last.fm tracks dataset?", "What are the terms for using the dataset?", "How can the dataset be downloaded?", "What is the purpose of the python script?", "What is the purpose of the feed.jsonl file?"]}}} -{"update": "id:open-p:paragraph::open/en/performance/practical-search-performance-guide.html-create-a-vespa-application-package", "fields": {"questions": {"assign": ["What is a Vespa application package?", "What defines Vespa behavior?", "What are the minimum required files?", "What is track.sd?", "What directories are needed?", "How do the configuration files define data processing?"]}}} -{"update": "id:open-p:paragraph::open/en/performance/practical-search-performance-guide.html-schema", "fields": {"questions": {"assign": ["What is a Vespa schema?", "What is a track schema?", "What is the 'track_id' field for?"]}}} -{"update": "id:open-p:paragraph::open/en/performance/practical-search-performance-guide.html-services-specification", "fields": {"questions": {"assign": ["How can you override default query settings?", "What is the purpose of 'presentation.timing' field?", "How can you render 'weightedset' fields?"]}}} -{"update": "id:open-p:paragraph::open/en/performance/practical-search-performance-guide.html-deploy-the-application-package", "fields": {"questions": {"assign": ["How to deploy Vespa instance?", "What is the Vespa quick start guide?"]}}} -{"update": "id:open-p:paragraph::open/en/performance/practical-search-performance-guide.html-basic-text-search-query-performance", "fields": {"questions": {"assign": ["How to formulate query?"]}}} -{"update": "id:open-p:paragraph::open/en/performance/practical-search-performance-guide.html-hits-and-summaries", "fields": {"questions": {"assign": ["What factors determine summaryfetchtime?", "What can improve summaryfetchtime?", "What is the benefit of using a dedicated document-summary?", "What is the difference between attribute and non-attribute fields in a summary?", "What are summary-features?", "What is a summary fill?"]}}} -{"update": "id:open-p:paragraph::open/en/performance/practical-search-performance-guide.html-searching-attribute-fields", "fields": {"questions": {"assign": ["What is a weightedset field?", "How is a tags field matched?", "What is free text searching?", "What is the reason for attribute fields not having inverted index?", "How is match:text different from match:word?", "How is a posting list represented in Vespa?"]}}} -{"update": "id:open-p:paragraph::open/en/performance/practical-search-performance-guide.html-searching-attribute-fields-using-fast-search", "fields": {"questions": {"assign": ["What is `fast-search`?", "What fields use `fast-search`?", "How does `fast-search` affect memory usage?", "What are the benefits of using `fast-search`?", "How do you enable `fast-search`?", "What is the downside of enabling `fast-search`?"]}}} -{"update": "id:open-p:paragraph::open/en/performance/practical-search-performance-guide.html-multi-valued-query-operators", "fields": {"questions": {"assign": ["What is the dotProduct query operator?", "What is the wand query operator?", "What is rawScore?", "What is the difference between wand and dotProduct?", "What are multi-value query operators used for?"]}}} -{"update": "id:open-p:paragraph::open/en/performance/practical-search-performance-guide.html-tensor-computations", "fields": {"questions": {"assign": ["What is tensor computation?", "What is an example of tensor computation?", "How can tensor computations be used in Vespa?", "How can document tensors and query tensors be defined in Vespa?", "What is the purpose of the similar field in the track schema?", "How can documents be ranked using tensor computations in Vespa?"]}}} -{"update": "id:open-p:paragraph::open/en/performance/practical-search-performance-guide.html-multithreaded-search-and-ranking", "fields": {"questions": {"assign": ["What is multithreaded execution?", "How to enable multithreaded execution?", "What are the benefits of multithreaded search?", "What is requestthreads:persearch?", "How do you change global threads per search?", "What happens when you change threads per search?", "What is num-threads-per-search?"]}}} -{"update": "id:open-p:paragraph::open/en/performance/practical-search-performance-guide.html-advanced-range-search-with-hitlimit", "fields": {"questions": {"assign": ["What is `popularity`?", "What is the `range()` query operator?", "What is the `hitLimit` annotation?", "What use cases are suitable for using the `range()` operator?", "What is the practical use of the `range()` operator in auto-complete or search suggestions?"]}}} -{"update": "id:open-p:paragraph::open/en/performance/practical-search-performance-guide.html-match-phase-limit---early-termination", "fields": {"questions": {"assign": ["What is match-phase early termination?", "How does match-phase early termination work?", "What field attribute is required for match-phase early termination?", "How can match-phase limit be used with text search queries?", "What is the difference between match-phase early termination and range search with hitLimit?", "What result diversity constraint is supported by match-phase early termination?", "What is the recommended setting for low settings of maxHits in the rank-profile?"]}}} -{"update": "id:open-p:paragraph::open/en/performance/practical-search-performance-guide.html-advanced-query-tracing", "fields": {"questions": {"assign": ["What is query tracing?", "How to enable query tracing?", "What are the parameters to enable tracing?", "How is tracing implemented?", "What information is included in traces?", "How can traces help in feature tuning decisions?", "What is the Trace Visualizer?", "What is the response time for the first protocol phase?"]}}} -{"update": "id:open-p:paragraph::open/en/performance/practical-search-performance-guide.html-tear-down-the-container", "fields": {"questions": {"assign": ["What does the tutorial conclude?"]}}} -{"update": "id:open-p:paragraph::open/en/predicate-fields.html-", "fields": {"questions": {"assign": ["What are predicate fields?", "What is a boolean constraint?", "What is a typical use case for predicate fields?", "What is an impression?", "What trade-offs are there when configuring predicate fields?"]}}} -{"update": "id:open-p:paragraph::open/en/predicate-fields.html-boolean-constraints", "fields": {"questions": {"assign": ["What is a boolean constraint?", "What can be the attributes of a boolean constraint?", "What does a boolean constraint specify?", "What are the criteria for attributes in a boolean constraint?", "What is a range of values in a boolean constraint?", "What is a subset of sets in a boolean constraint?", "How do queries land in a boolean constraint?", "What is a predicate in Vespa.ai?"]}}} -{"update": "id:open-p:paragraph::open/en/predicate-fields.html-predicates", "fields": {"questions": {"assign": ["What is a predicate?", "What is a boolean constraint?", "What is the grammar for valid expressions?", "What is a leaf in a predicate?", "What is an example of a predicate?", "What is a disjunction in a predicate?", "What is a conjunction in a predicate?", "What is the correct syntax for a multivalue in a predicate?"]}}} -{"update": "id:open-p:paragraph::open/en/predicate-fields.html-attributes", "fields": {"questions": {"assign": ["What are regular attributes?", "What is a range attribute?", "What values do regular attributes take?", "What values do range attributes take?", "What is a range expression?", "What are the three types of range expressions?", "How do you use a regular attribute in a predicate?", "How do you use a range attribute in a predicate?"]}}} -{"update": "id:open-p:paragraph::open/en/predicate-fields.html-predicate-samples", "fields": {"questions": {"assign": ["What is a subset expression?", "How are non-alphanumeric letters used in Vespa predicates?"]}}} -{"update": "id:open-p:paragraph::open/en/predicate-fields.html-queries", "fields": {"questions": {"assign": ["What is a boolean query?", "What are boolean constraints?", "What is a regular attribute?", "What kind of values can regular attributes have?"]}}} -{"update": "id:open-p:paragraph::open/en/predicate-fields.html-search-using-yql+", "fields": {"questions": {"assign": ["How do you use the predicate function?", "What are the parameters of the predicate function?", "How do you specify empty maps in a boolean query?", "How do you specify multiple values for the same key in a boolean query?"]}}} -{"update": "id:open-p:paragraph::open/en/predicate-fields.html-subqueries", "fields": {"questions": {"assign": ["What is the bitmap for?", "Why use multiple queries at once?", "What is a typical use case?", "What happens with empty subqueries?"]}}} -{"update": "id:open-p:paragraph::open/en/predicate-fields.html-specifying-subqueries-in-yql", "fields": {"questions": {"assign": ["What is a subquery?", "How are subqueries specified?", "What is a valid subquery bitmap?", "What is the format of a subquery?", "How do you construct a query from subqueries?"]}}} -{"update": "id:open-p:paragraph::open/en/predicate-fields.html-identifying-subqueries-in-results", "fields": {"questions": {"assign": ["What are subqueries?", "What do you need to add to the schema when using subqueries?", "How are subqueries reported?", "What is the lower 32-bit summary feature?", "What is the upper 32-bit summary feature?", "Where can you find an example of configuring a custom searcher for subqueries?", "What is the purpose of the subquery bitmap?", "What is required to retrieve the subquery bitmap?"]}}} -{"update": "id:open-p:paragraph::open/en/predicate-fields.html-predicate-example", "fields": {"questions": {"assign": ["What is a subquery feature?", "How can subqueries be used?", "How is the subquery bitmap used?", "How many subqueries are used?", "What does the target field represent?"]}}} -{"update": "id:open-p:paragraph::open/en/predicate-fields.html-configuration", "fields": {"questions": {"assign": ["What is a predicate field?", "How does dense-posting-list-threshold help?", "What is the JSON format for predicates?", "What is the index definition for a predicate field?"]}}} -{"update": "id:open-p:paragraph::open/en/predicate-fields.html-schema", "fields": {"questions": {"assign": ["What is the mandatory parameter called?", "What are the lower and upper bounds?", "What is the dense-posting-list-threshold?", "How is the field used for subquery reporting?"]}}} -{"update": "id:open-p:paragraph::open/en/predicate-fields.html-upper-and-lower-bounds", "fields": {"questions": {"assign": ["What do upper-bound and lower-bound do?", "What happens if the bounds are changed?", "What type of queries are rejected?"]}}} -{"update": "id:open-p:paragraph::open/en/predicate-fields.html-dense-posting-list-threshold", "fields": {"questions": {"assign": ["What is dense-posting-list-threshold?", "What impacts the boolean index structure?", "What is the default value of dense-posting-list-threshold?", "What is the valid range for dense-posting-list-threshold?", "What are the advantages of using dense posting lists?", "What are the disadvantages of using dense posting lists?", "What happens if the dense-posting-list-threshold is too low?", "What happens if the dense-posting-list-threshold is too large?"]}}} -{"update": "id:open-p:paragraph::open/en/predicate-fields.html-index-size", "fields": {"questions": {"assign": ["What impacts index size?", "What is the impact of dense-posting-list-threshold?", "What is the visualization showing?", "What is the impact of lower arity on index size?", "How can the interval size be controlled?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/procedure-change-attribute-index.html-", "fields": {"questions": {"assign": ["What is reindexing?", "How to change between index and attribute?", "What happens when converting attribute to index?", "What is the best approach to change between index and attribute?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/procedure-change-attribute-index.html-field-configuration-at-start", "fields": {"questions": {"assign": ["What is 'field artist'?", "What type is 'field artist'?", "How is 'field artist' indexed?", "What are the indexing options for 'field artist'?", "Is 'field artist' an attribute?", "Is 'field artist' indexed as a summary?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/procedure-change-attribute-index.html-procedure", "fields": {"questions": {"assign": ["How to change artist field to attribute?", "How to validate queried data?", "How to add an index aspect?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/procedure-change-attribute-index.html-appendix", "fields": {"questions": {"assign": ["What is vespa-proton-cmd?", "How to inspect attribute data?", "How to inspect index data?", "What is triggerFlush?", "How to troubleshoot with vespa-proton-cmd?"]}}} -{"update": "id:open-p:paragraph::open/en/jdisc/processing.html-", "fields": {"questions": {"assign": ["What can you use Processing for?", "What is the recommended way to create low-latency request/response processing applications?", "What kind of call model do Processors use?", "What is an example of a Processor task?", "Can Processing be used independently of JDisc?"]}}} -{"update": "id:open-p:paragraph::open/en/jdisc/processing.html-using-processing", "fields": {"questions": {"assign": ["How do I add Vespa dependency?", "What is the groupId?", "What is the latest version of container?", "What is the scope?", "Where can I find the latest version of container?", "How do I start a deployable project?"]}}} -{"update": "id:open-p:paragraph::open/en/jdisc/processing.html-processors", "fields": {"questions": {"assign": ["What is a processor?", "What method does a processor implement?", "What is the structure of an ExampleProcessor class?", "What does the process() method return?", "What can the result data from a processor include?", "What packages need to be imported for creating a processor?"]}}} -{"update": "id:open-p:paragraph::open/en/jdisc/processing.html-chaining-processors", "fields": {"questions": {"assign": ["What are chains in Vespa?", "How to configure chains?", "What is Processors and Chains?", "How to define chains in services.xml?", "Can processors be used in many chains at the same time?", "What is the advantage of chain configuration?"]}}} -{"update": "id:open-p:paragraph::open/en/jdisc/processing.html-asynchronous-results", "fields": {"questions": {"assign": ["What is a partial response?", "What is a placeholder in Vespa?", "What is ListenableFuture used for?"]}}} -{"update": "id:open-p:paragraph::open/en/jdisc/processing.html-dependency-injection", "fields": {"questions": {"assign": ["What are processor dependencies?", "How are dependencies injected?", "What is the container runtime used?", "What is the recommended immutability?", "What is the Guice dependency framework used for?", "How should field values be treated in a processing class?", "What is the role of constructor arguments in processor dependencies?", "What is the purpose of declaring dependencies in processors?"]}}} -{"update": "id:open-p:paragraph::open/en/jdisc/processing.html-response-rendering", "fields": {"questions": {"assign": ["What is a Renderer?", "How are Renderers selected?"]}}} -{"update": "id:open-p:paragraph::open/en/jdisc/processing.html-subclassing-of-processing", "fields": {"questions": {"assign": ["What is the Processing framework?", "What is subclassing?", "What is the example of subclassing in Search domain?"]}}} -{"update": "id:open-p:paragraph::open/en/jdisc/processing.html-testing-processors-with-an-application", "fields": {"questions": {"assign": ["What is the ChainRegistry in Vespa?", "What is a ComponentSpecification in Vespa?"]}}} -{"update": "id:open-p:paragraph::open/en/jdisc/processing.html-selecting-a-non-default-processor-chain", "fields": {"questions": {"assign": ["What is a processor chain?", "What is the parameter for chain ID?", "What is the default chain ID?", "How do you select a non-default chain?", "What is the syntax for testing a chain from the command line?", "What does adding a processor chain enable?"]}}} -{"update": "id:open-p:paragraph::open/en/jdisc/processing.html-references", "fields": {"questions": {"assign": ["What is developing web services?", "What is com.yahoo.processing?", "What is Guava?"]}}} -{"update": "id:open-p:paragraph::open/en/jdisc/processing.html-common-tasks-with-processing", "fields": {"questions": {"assign": ["What is jDisc binding?", "How do I use Processing?", "Can Processing be used outside jDisc?", "What does Processing do?", "What are the explanations in this section?", "Is jDisc binding required for Processing?", "In what environments can Processing be invoked?"]}}} -{"update": "id:open-p:paragraph::open/en/jdisc/processing.html-accessing-the-http-request-from-processors", "fields": {"questions": {"assign": ["What is jDisc?", "What does httpRequest contain?", "How can httpRequest be accessed?"]}}} -{"update": "id:open-p:paragraph::open/en/jdisc/processing.html-setting-response-headers-from-processors", "fields": {"questions": {"assign": ["What are response headers?", "How to add response headers?", "What is ResponseHeaders?", "How does ResponseHeaderSetter work?"]}}} -{"update": "id:open-p:paragraph::open/en/jdisc/processing.html-example-processors", "fields": {"questions": {"assign": ["What is the AsyncDataProducer?", "What is the AsyncDataProcessingInitiator?", "What is the purpose of FutureResponse?", "What is the purpose of AsyncExecution?"]}}} -{"update": "id:open-p:paragraph::open/en/performance/profiling.html-", "fields": {"questions": {"assign": ["What are profiling guidelines?", "How to find a load to profile?", "What to do if other bottlenecks are found?", "What to do if the system is multithreaded?"]}}} -{"update": "id:open-p:paragraph::open/en/performance/profiling.html-cpu-profiling", "fields": {"questions": {"assign": ["What is vmstat used for?", "What does system column measure in vmstat output?", "What does iostat show?", "What should swap in/out be according to vmstat?", "What might a maxed out system have?", "What is the useless statistic in iostat output?"]}}} -{"update": "id:open-p:paragraph::open/en/performance/profiling.html-cpu-profiling-using-perf", "fields": {"questions": {"assign": ["How to get a performance profile snapshot?", "What is perf?", "How to view a performance profile report?", "Why is kernel debug info useful?"]}}} -{"update": "id:open-p:paragraph::open/en/performance/profiling.html-container-privileges", "fields": {"questions": {"assign": ["How to debug an unprivileged container?", "What is the command to execute perf?", "What is a privileged container?", "How to share pid namespace?", "What is the format of the docker ps command?"]}}} -{"update": "id:open-p:paragraph::open/en/performance/profiling.html-profiling-the-query-container", "fields": {"questions": {"assign": ["How to profile custom searchers?", "What does profiling identify?", "How to configure Container profiling?"]}}} -{"update": "id:open-p:paragraph::open/en/performance/profiling.html-install-yourkit-profiler-on-the-container", "fields": {"questions": {"assign": ["What is Yourkit?", "What does Yourkit do?", "What modes does Yourkit support?", "Why use both sampling and tracing in Yourkit?", "How do you install Yourkit?", "How do you modify services.xml?", "How do you re-deploy the application?", "How do you restart Vespa?"]}}} -{"update": "id:open-p:paragraph::open/en/performance/profiling.html-install-yourkit-ui-on-the-desktop", "fields": {"questions": {"assign": ["What is YourKit profiler?", "How to install YourKit profiler?", "What is the default port for YourKit agent?", "How to work around remote hosts with Vespa?", "What is the license server?"]}}} -{"update": "id:open-p:paragraph::open/en/performance/profiling.html-using-yourkit", "fields": {"questions": {"assign": ["What is YourKit?", "What is the Vespa benchmarking guide?", "What is the name of the remote application being profiled?", "How do I connect to the remote application for profiling?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/prometheus-v1.html-", "fields": {"questions": {"assign": ["What is the prometheus node metrics API?", "Where can the prometheus node metrics API be found?", "What does the prometheus node metrics API offer?", "What is the format of the prometheus node metrics API?", "What is Prometheus?", "What is included in the monitoring overview?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/prometheus-v1.html-http-requests", "fields": {"questions": {"assign": ["What is prometheus/v1?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/prometheus-v1.html-request-parameters", "fields": {"questions": {"assign": ["What is the consumer parameter?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/prometheus-v1.html-http-status-codes", "fields": {"questions": {"assign": ["What is the status code 200?", "What does status code 200 mean?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/prometheus-v1.html-response-format", "fields": {"questions": {"assign": ["What is Prometheus format?", "What is in /metrics/v1/values?"]}}} -{"update": "id:open-p:paragraph::open/en/proton.html-", "fields": {"questions": {"assign": ["What is Proton?", "What does Proton maintain?", "What are maintenance jobs?", "What is concurrency in Proton?", "What are the sub-databases in Proton?", "What is the structure of Proton's document databases?"]}}} -{"update": "id:open-p:paragraph::open/en/proton.html-bucket-management", "fields": {"questions": {"assign": ["What is the role of metadata in starting a node?", "What is an in-memory bucket database?", "How is bucket management implemented with Vespa?", "What is the persistence engine in Vespa?", "How does the distributor handle bucket metadata requests?", "What is meant by elasticity support in Vespa?", "What is the SPI in Vespa?"]}}} -{"update": "id:open-p:paragraph::open/en/proton.html-document-database", "fields": {"questions": {"assign": ["What is a document database?", "What is a FeedHandler?", "What is a transaction log?"]}}} -{"update": "id:open-p:paragraph::open/en/proton.html-sub-databases", "fields": {"questions": {"assign": ["What are the sub-databases?", "What is the document meta store?", "What is the index maintainer?", "What is the Ready sub-database for?", "What is the difference between active and not active copies?", "What is the purpose of the Removed sub-database?", "What happens when the system is resized?"]}}} -{"update": "id:open-p:paragraph::open/en/proton.html-transaction-log", "fields": {"questions": {"assign": ["What is a transaction log?", "How is transaction log persisted?", "What is the benefit of having a transaction log?", "What is proton?", "What is written to the transaction log?", "What is sequential IO?", "What is sync-transactionlog?", "What does proton do on shutdown?"]}}} -{"update": "id:open-p:paragraph::open/en/proton.html-index", "fields": {"questions": {"assign": ["What are index fields?", "What is a disk index?", "What is stored in a dictionary?", "What is `posocc`?", "What is a posting list?"]}}} -{"update": "id:open-p:paragraph::open/en/proton.html-document-store", "fields": {"questions": {"assign": ["What is the document store?", "How are files written in document store?", "What is the maximum size for a .dat file?", "What happens to old version of documents?", "How are files written using compression settings?"]}}} -{"update": "id:open-p:paragraph::open/en/proton.html-defragmentation", "fields": {"questions": {"assign": ["What does document store compaction do?", "How is document store compaction triggered?", "What metric tracks proton's compaction jobs?", "What is a normal pattern for max_bucket_spread?", "How can the correct compaction settings be set?", "How can the defragmentation status be observed?", "What is the purpose of defragmentation?"]}}} -{"update": "id:open-p:paragraph::open/en/proton.html-optimized-reads-using-chunks", "fields": {"questions": {"assign": ["What is the purpose of maxsize?", "What is the default chunk size?", "What is bucket-splitting?", "How are documents read in a visit operation?", "How is memory lookup optimized?", "What is the impact of increasing maxfilesize on compaction time?", "What is the impact of reducing maxsize on wasted IO bytes?", "What are the sizing units to consider when optimizing reads?"]}}} -{"update": "id:open-p:paragraph::open/en/proton.html-memory-usage", "fields": {"questions": {"assign": ["What is a LID?", "What is the .idx file?", "What is the memory usage?", "How is memory usage related to number of documents?", "What is the mapping in memory?"]}}} -{"update": "id:open-p:paragraph::open/en/proton.html-proton-maintenance-jobs", "fields": {"questions": {"assign": ["What is Proton?", "What are maintenance jobs?", "What is CPU used for?", "What is disk usage?", "What is the purpose of attribute flushing?", "What is document store compaction?", "What are available Proton metrics?"]}}} -{"update": "id:open-p:paragraph::open/en/proton.html-queries", "fields": {"questions": {"assign": ["What is the QR-server?", "How does queries move through Vespa?"]}}} -{"update": "id:open-p:paragraph::open/en/proton.html-custom-component-state-api", "fields": {"questions": {"assign": ["What is the custom component state API?", "Where can component status be found?", "What is the format of the custom component status?", "What is the purpose of `state/v1/custom/component`?", "What is the purpose of `documentdb`?", "What information does the state of `mydoctype` include?", "What is the structure of the `subdb`?", "What is the structure of the `documentdb`?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/query-api-reference.html-", "fields": {"questions": {"assign": ["What is the difference between full and abbreviated names?", "Are query profile types case-sensitive?", "What is the purpose of modeling parameters as query profile types?", "What Java objects can I get from Query to Searcher components?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/query-api-reference.html-parameters", "fields": {"questions": {"assign": ["What are the Native Execution Parameters?", "What is the Presentation?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/query-api-reference.html-query", "fields": {"questions": {"assign": ["What is the YQL query guide?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/query-api-reference.html-native-execution-parameters", "fields": {"questions": {"assign": ["What is the default value for 'hits' parameter?", "What is the maximum value of 'hits' parameter?", "What is the purpose of 'offset' parameter?", "What is the default value for 'queryProfile' parameter?", "What is the purpose of 'groupingSessionCache' parameter?", "What is the default value of 'searchChain' parameter?", "What is the default unit for 'timeout' parameter?", "What is the purpose of 'timeout' parameter?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/query-api-reference.html-query-model-parameters", "fields": {"questions": {"assign": ["What is model.defaultIndex?", "What is model.encoding used for?", "What is model.filter in Simple Query Language used for?", "What is model.locale used for?", "What is model.queryString used for?", "What is model.restrict used for?", "What is model.searchPath used for?", "What is model.sources used for?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/query-api-reference.html-ranking", "fields": {"questions": {"assign": ["What is ranking.location?", "What is ranking.profile for?", "What is ranking.listFeatures for?", "What is ranking.properties.*propertyname* for?", "What is ranking.softtimeout.* for?", "What is ranking.queryCache for?", "What is ranking.sorting for?", "What is ranking.rerankCount for?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/query-api-reference.html-ranking.matching", "fields": {"questions": {"assign": ["What is numThreadsPerSearch?", "What is minHitsPerThread?", "What is numSearchPartitions?", "What is termwiseLimit?", "What is postFilterThreshold?", "What is approximateThreshold?", "What are ranking settings for query matching?", "What do ranking.matching parameters do?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/query-api-reference.html-ranking.matchphase", "fields": {"questions": {"assign": ["What are match-phase settings?", "What is max-hits in match-phase?", "What is diversity attribute in match-phase?", "What is min-groups in match-phase?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/query-api-reference.html-dispatch", "fields": {"questions": {"assign": ["What is dispatch.topKProbability?", "What is the type of dispatch.topKProbability?", "What is the default value of dispatch.topKProbability?", "Where can I find details about dispatch.topKProbability?", "What is the purpose of dispatch.topKProbability?", "What is the alias for dispatch.topKProbability?", "What is the range of dispatch.topKProbability?", "What is the importance of dispatch.topKProbability in result sets?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/query-api-reference.html-presentation", "fields": {"questions": {"assign": ["What is presentation.bolding?", "What is presentation.format used for?", "What does presentation.timing control?", "What are the different values for presentation.format.tensors?", "What is the default value for presentation.timing?", "What is the default value for presentation.bolding?", "What is presentation.summary used for?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/query-api-reference.html-grouping-and-aggregation", "fields": {"questions": {"assign": ["What is the 'grouping' parameter?", "What is 'collapsing'?", "What is the default 'grouping' limit?", "What is the 'collapsesize' parameter for?", "What is the 'collapsefield' parameter used for?", "What is the 'defaultPrecisionFactor' parameter for?", "What is the 'grouping.globalMaxGroups' parameter for?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/query-api-reference.html-tracing", "fields": {"questions": {"assign": ["What is trace.level for?", "What is trace.explainLevel?", "What is trace.profileDepth?", "What is trace.timestamps?", "What is trace.query?", "What is the syntax of trace.level?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/query-api-reference.html-semantic-rules", "fields": {"questions": {"assign": ["What are semantic rules?", "What is the parameter rules.off used for?", "What is the default value of rules.off?", "What is the parameter rules.rulebase?", "What is the purpose of tracelevel.rules?", "What does tracelevel.rules control?", "What is the type of rules.off?", "What is the type of tracelevel.rules?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/query-api-reference.html-other", "fields": {"questions": {"assign": ["What is the 'recall' parameter?", "What is the 'user' parameter?", "What is the purpose of 'hitcountestimate'?", "Can metric collection be ignored for a query?", "What happens when weakAnd.replace is set to true?", "What is the 'wand.hits' parameter used for?", "What does 'sorting.degrading' optimize?", "What is the purpose of the 'noCache' parameter?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/query-api-reference.html-http-status-codes", "fields": {"questions": {"assign": ["What are the status code rules?", "What are WEB SERVICE ERROR CODES?", "What is returned if there are errors?", "What is returned for valid hits?", "What errors return 500?", "What errors return 200?", "What is INTERNAL_SERVER_ERROR mapped to?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/query-api-reference.html-select", "fields": {"questions": {"assign": ["What is a select query?", "What are the subparameters of select query?"]}}} -{"update": "id:open-p:paragraph::open/en/query-api.html-", "fields": {"questions": {"assign": ["What is the Vespa Query API?", "Where can I find the Query API reference?", "What is the data flow?"]}}} -{"update": "id:open-p:paragraph::open/en/query-api.html-input", "fields": {"questions": {"assign": ["What is the structure of input data?", "What is userInput?", "How can user input be expressed?", "What is the query feature called user_profile?", "What is query execution?"]}}} -{"update": "id:open-p:paragraph::open/en/query-api.html-input-examples", "fields": {"questions": {"assign": ["What is the userInput() function?", "What does weakAnd() do?", "How do you combine multiple query properties?", "What is a grammar-annotation?", "How does the linguistic library split input?"]}}} -{"update": "id:open-p:paragraph::open/en/query-api.html-query-profiles", "fields": {"questions": {"assign": ["How can query profiles be nested?", "What are query profiles used for?", "How can query strings be shortened?", "Can query profiles be versioned?", "What is query profile inheritance?"]}}} -{"update": "id:open-p:paragraph::open/en/query-api.html-geo-filter-and-ranking", "fields": {"questions": {"assign": ["What is geo search?", "What is DistanceToPath?"]}}} -{"update": "id:open-p:paragraph::open/en/query-api.html-ranking", "fields": {"questions": {"assign": ["What is rank_albums?", "What is text ranking?"]}}} -{"update": "id:open-p:paragraph::open/en/query-api.html-grouping", "fields": {"questions": {"assign": ["What is grouping in Vespa?", "How does grouping work in Vespa?", "What are the benefits of grouping?", "What fields can be used for grouping?", "How is the grouping expression formed?", "What happens when limit 0 is used with grouping?", "Are attributes required for grouping?"]}}} -{"update": "id:open-p:paragraph::open/en/query-api.html-results", "fields": {"questions": {"assign": ["What are returned in search results by default?", "What is a dynamic summary?", "How can summary improve visual relevance?", "What is the default output format?", "How can I write a custom renderer?"]}}} -{"update": "id:open-p:paragraph::open/en/query-api.html-query-execution", "fields": {"questions": {"assign": ["What are the phases of query execution?", "What is trace.explainlevel used for?", "How can you modify the query plan?", "What are the benefits of using 'ranked: false' query annotations?", "What is capped range search?", "How can you efficiently implement top-k selection for ranking a subset of the documents in the index?"]}}} -{"update": "id:open-p:paragraph::open/en/query-api.html-query-processing-and-dispatch", "fields": {"questions": {"assign": ["What is Query API?", "What is linguistic processing?"]}}} -{"update": "id:open-p:paragraph::open/en/query-api.html-http", "fields": {"questions": {"assign": ["What HTTP client does Vespa recommend?", "What are HTTP best practices?"]}}} -{"update": "id:open-p:paragraph::open/en/query-api.html-using-post", "fields": {"questions": {"assign": ["What is the Query API reference?"]}}} -{"update": "id:open-p:paragraph::open/en/query-api.html-jetty", "fields": {"questions": {"assign": ["What is Jetty?", "What is requestHeaderSize?", "What is URL encoding?", "Which RFC describes URL encoding?", "What is the Stack Overflow question about?"]}}} -{"update": "id:open-p:paragraph::open/en/query-api.html-timeout", "fields": {"questions": {"assign": ["What is the query timeout?", "Does the Query.getTimeLeft() check timeout?", "How does timeout apply to multiphase searching?", "Can we set different timeouts for different chains?", "Is the timeout set for the entire search chain?", "Can we specify timeout when waiting for responses?", "What is the cooperative timeout handling?"]}}} -{"update": "id:open-p:paragraph::open/en/query-api.html-error-handling", "fields": {"questions": {"assign": ["What is the root error element?", "What does a root error mean?", "What does the error code 8 mean?", "How to locate an attribute vector?", "What is the summary of the error?", "What is the source of the error?", "What is the message of the error?"]}}} -{"update": "id:open-p:paragraph::open/en/query-api.html-troubleshooting", "fields": {"questions": {"assign": ["What is the error message for a missing yql parameter?"]}}} -{"update": "id:open-p:paragraph::open/en/query-api.html-query-tracing", "fields": {"questions": {"assign": ["What is query tracing?", "How do you enable query tracing?", "What is Query.trace used for?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/query-language-reference.html-", "fields": {"questions": {"assign": ["What does Vespa accept?", "How does Vespa handle human input and structured queries?", "What does a query URL look like?", "What is YQL?", "How does YQL match documents?", "How should quote and backslash characters be escaped?", "Is a semicolon required at the end of YQL queries?", "From which version of Vespa do YQL queries not require a semicolon?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/query-language-reference.html-select", "fields": {"questions": {"assign": ["How do you select specific fields?", "What does Vespa hide in matching documents?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/query-language-reference.html-from-sources", "fields": {"questions": {"assign": ["What is the *sources* keyword for?", "How do you restrict the query to a single schema?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/query-language-reference.html-order-by", "fields": {"questions": {"assign": ["What is order by in Vespa?", "How to do primary ordering on rank score in Vespa?", "How does Vespa sort documents with the same primary ordering attribute?", "What are special sorting attributes in Vespa?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/query-language-reference.html-limit-/-offset", "fields": {"questions": {"assign": ["How to limit the number of hits?", "What are native execution parameters?", "What is maxOffset?", "What is queryProfile?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/query-language-reference.html-timeout", "fields": {"questions": {"assign": ["How to set query timeout?", "Can timeout be controlled natively?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/query-language-reference.html-parameter-substitution", "fields": {"questions": {"assign": ["Which query operators support parameter substitution?", "What is the purpose of parameter substitution in query generation?", "What is the parameter substitution syntax for the weightedSet operator?", "What are the advantages of using parameter substitution in query parsing?", "In what formats can the value string be passed when using the weightedSet operator?", "Which query operator supports parameter substitution for user input?", "What is the syntax for passing a user input parameter with parameter substitution?", "What is the benefit of using parameter substitution for user input?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/query-language-reference.html-annotations", "fields": {"questions": {"assign": ["What is the distance annotation used for?", "What is the exploreAdditionalHits annotation used for?", "What is the hitLimit annotation used for?", "What is the purpose of the hnsw.exploreAdditionalHits annotation?", "What is the function annotation used for?", "What are annotations in Vespa?", "What is the scoreThreshold annotation used for?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/query-language-reference.html-annotations-of-sub-expressions", "fields": {"questions": {"assign": ["What is the stem annotation?", "What is the scope of annotations?", "What happens when a term in a boolean query is annotated with 'stem: false'?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/query-language-reference.html-query-properties", "fields": {"questions": {"assign": ["What is YQL variable syntax?", "What is @animal?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/query-language-reference.html-yql-in-query-profiles", "fields": {"questions": {"assign": ["What is YQL used for?", "How do you add a default query profile?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/query-language-reference.html-query-rewriting-in-searchers", "fields": {"questions": {"assign": ["What is the recommended annotation for modifying textual YQL statement?", "What is the recommended annotation for modifying query tree?", "What is the name of the annotation used for modifying textual YQL statement?", "How can searchers modify the YQL statement?", "What should be annotated with @Before(ExternalYql)?", "What should be annotated with @After(ExternalYql)?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/query-language-reference.html-grouping", "fields": {"questions": {"assign": ["What is a grouping expression?"]}}} -{"update": "id:open-p:paragraph::open/en/query-language.html-command-line-queries", "fields": {"questions": {"assign": ["How to run a query?", "What is encoded YQL string?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/query-profile-reference.html-", "fields": {"questions": {"assign": ["What is a query profile?", "What is Query Profile Type?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/query-profile-reference.html-query-profiles", "fields": {"questions": {"assign": ["What is a query profile?", "What does a query profile define?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/query-profile-reference.html-id", "fields": {"questions": {"assign": ["What is the format for id?", "What is the default profile?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/query-profile-reference.html-description", "fields": {"questions": {"assign": ["What is the purpose?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/query-profile-reference.html-dimensions", "fields": {"questions": {"assign": ["What are nested query profiles?", "What is a profile variant?", "What are profile dimension names?", "How are profile variants triggered?", "What is a comma-separated list?", "What is the format for profile dimensions?", "How can profile variants be created?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/query-profile-reference.html-query-profile-types", "fields": {"questions": {"assign": ["What is a query profile type?", "What is the structure of query profile type?", "What are the optional attributes for query profile type?", "What is a query profile type field?", "What does optional tag mean?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/query-profile-reference.html-optional-`query-profile-type`-attributes", "fields": {"questions": {"assign": ["How are fields included in a query profile?", "What happens if a field is defined in multiple inherited profiles?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/query-profile-reference.html-`match`", "fields": {"questions": {"assign": ["What is ?", "How is query profile name matched?", "What happens if is not specified?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/query-profile-reference.html-`strict`", "fields": {"questions": {"assign": ["What happens when strict sub-profiles are added?", "What are the rules of using a strict top-level profile?", "Can non-primitive model objects be added to strict top-level profiles?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/query-profile-reference.html-`field`-type", "fields": {"questions": {"assign": ["What is the 'long' type?", "What is the 'double' type?", "What is the 'boolean' type?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/query-profile-reference.html-optional-`field`-definition-attributes", "fields": {"questions": {"assign": ["What is the 'mandatory' field?", "What is the 'overridable' field?", "What is the 'description' field?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/query-profile-reference.html-identifiers", "fields": {"questions": {"assign": ["What is an identifier pattern?", "What is the syntax for an identifier?", "What is the format for an identifier?", "What is the pattern for an identifier?", "What is the definition of an identifier?", "What is the format of an identifier?"]}}} -{"update": "id:open-p:paragraph::open/en/query-profiles.html-", "fields": {"questions": {"assign": ["What is a Query Profile?", "How can search request specify Query Profile?", "What benefits Query Profiles offer?", "What can Query Profiles differentiate based on?", "How to create Query Profiles?", "How to use Query Profiles?", "Where can I find the full Query Profile syntax reference?"]}}} -{"update": "id:open-p:paragraph::open/en/query-profiles.html-using-a-query-profile", "fields": {"questions": {"assign": ["What is a Query Profile?", "How to deploy a Query Profile?", "What is the format of a Query Profile?", "What is the directory for Query Profiles?", "What is the default Query Profile name?", "What happens if a Query Profile contains errors?", "How to specify a Query Profile in a request?", "What are query properties?"]}}} -{"update": "id:open-p:paragraph::open/en/query-profiles.html-overrides", "fields": {"questions": {"assign": ["What is MyProfile.xml?", "How does overridable work?", "What is the precedence of parameter values?", "How can individual query profile fields be overriden?", "What is the default behavior when same parameter is present in request and profile?", "What is an example of setting an overridable field?"]}}} -{"update": "id:open-p:paragraph::open/en/query-profiles.html-nested-structure", "fields": {"questions": {"assign": ["How are nested profiles referenced?", "How can variables be overridden?", "What is the structure of a nested profile?", "What is the syntax for referencing a subprofile in the request?", "How does a query profile reference affect search requests?"]}}} -{"update": "id:open-p:paragraph::open/en/query-profiles.html-inheritance", "fields": {"questions": {"assign": ["What is a query profile?", "How do you inherit a query profile?", "What is the syntax for inheriting multiple query profiles?", "What happens when a parameter is set in multiple inherited profiles?"]}}} -{"update": "id:open-p:paragraph::open/en/query-profiles.html-value-substitution", "fields": {"questions": {"assign": ["What are query profile values?", "Can query profiles contain multiple fields?", "What will the query profile return?"]}}} -{"update": "id:open-p:paragraph::open/en/query-profiles.html-global-resolution", "fields": {"questions": {"assign": ["What is query.properties()?", "What is a substitution in Vespa?", "How are substituted values looked up?", "What happens when a substitution returns null?"]}}} -{"update": "id:open-p:paragraph::open/en/query-profiles.html-local-substitution", "fields": {"questions": {"assign": ["What is local substitution used for?", "How to substitute a value?"]}}} -{"update": "id:open-p:paragraph::open/en/query-profiles.html-query-profile-variants", "fields": {"questions": {"assign": ["What are query profiles?", "What are variants in query profiles?", "How does a query profile vary?", "What is the purpose of a query profile?", "What does the 'for' attribute do in a query profile?", "How are virtual children created in a query profile?", "Can a query profile filter values according to incoming requests?"]}}} -{"update": "id:open-p:paragraph::open/en/query-profiles.html-variants-and-inheritance", "fields": {"questions": {"assign": ["What is variant resolution?", "How are variant dimensions inherited?", "What is variant specificity order?", "Can variants specify inherited profiles?", "What is the resolution precedence for variants?", "How are values resolved in variants?", "What are variant dimensions?", "What is the syntax for variant profile inheritance?"]}}} -{"update": "id:open-p:paragraph::open/en/query-profiles.html-query-profile-types", "fields": {"questions": {"assign": ["What is type checking in query profiles?", "How do you reference a query profile type?", "What is a query profile type?", "How can query profile types be useful?", "What is the syntax for defining query profile types?", "Where should query profile types be deployed?", "What are the built-in query profile types?", "What can be specified in a query profile type definition?"]}}} -{"update": "id:open-p:paragraph::open/en/query-profiles.html-path-matching", "fields": {"questions": {"assign": ["What is path matching?", "How does path matching work?", "What is ``?", "What are the benefits of path matching?", "When is path matching useful?", "How can path matching help with configuration?", "How is path matching different from exact matching?", "What happens if a profile name does not have any subpaths?"]}}} -{"update": "id:open-p:paragraph::open/en/query-profiles.html-versioning", "fields": {"questions": {"assign": ["What is a version string?", "How are version resolved?", "Can query profiles exist in multiple versions?", "How are version numbers specified?", "What is component versioning?", "What is the file naming convention for query profiles with versioning?", "What is the format for defining Query profile or type id with version?"]}}} -{"update": "id:open-p:paragraph::open/en/query-profiles.html-dump-tool", "fields": {"questions": {"assign": ["What is vespa-query-profile-dump-tool?", "How to dump resolved query profiles?"]}}} -{"update": "id:open-p:paragraph::open/en/query-rewriting.html-", "fields": {"questions": {"assign": ["What is query rewriting?", "What are query focusing techniques?", "What is a rewriting searcher?", "How can query enhancing improve search?", "What is a synonym?", "What is the Vespa query rewriting language?"]}}} -{"update": "id:open-p:paragraph::open/en/query-rewriting.html-equiv", "fields": {"questions": {"assign": ["What is EQUIV query operator used for?", "How does EQUIV differ from OR?", "When should EQUIV be used?", "How to use EQUIV in YQL?", "What is EquivSearcher?", "How to manipulate the query object?", "What are the limitations of EQUIV?", "How to add synonyms using EQUIV?"]}}} -{"update": "id:open-p:paragraph::open/en/query-rewriting.html-rules", "fields": {"questions": {"assign": ["What is a semantic rule?", "What is the syntax for semantic rules?", "What is a condition in a semantic rule?", "How can adding production rules be written?", "What does RANK do in a semantic rule?", "What is the purpose of grouping in semantic rules?", "What is the format for defining a condition in a semantic rule?"]}}} -{"update": "id:open-p:paragraph::open/en/query-rewriting.html-rule-bases", "fields": {"questions": {"assign": ["What is a rule base?", "What is the suffix for rule base files?", "What is contained in a rule base file?", "What is a semantic rule?", "What is a collection of rules?", "What is the file extension for rule bases?", "Where are rules and conditions stored?", "What is a rule base file used for?"]}}} -{"update": "id:open-p:paragraph::open/en/query-rewriting.html-replacements", "fields": {"questions": {"assign": ["What is the meaning of `colour`?", "What is the replacement for `audi`?"]}}} -{"update": "id:open-p:paragraph::open/en/query-rewriting.html-stopwords", "fields": {"questions": {"assign": ["What are stopwords?", "How do you replace stopwords?"]}}} -{"update": "id:open-p:paragraph::open/en/query-rewriting.html-configuration", "fields": {"questions": {"assign": ["How to name rule bases?", "How to make a rule base default?", "How to deactivate default rules?", "How to update rules safely?", "What happens if rule bases have errors?"]}}} -{"update": "id:open-p:paragraph::open/en/query-rewriting.html-using-multiple-rule-bases", "fields": {"questions": {"assign": ["What is a rule base?", "How to include rulebases?", "What is @include used for?", "What is @super?"]}}} -{"update": "id:open-p:paragraph::open/en/query-rewriting.html-category-becomes-laptop,-digital-camera,-camera,-palmtop,-phone", "fields": {"questions": {"assign": ["How can a default rule base be set?", "How can another rule base be used?"]}}} -{"update": "id:open-p:paragraph::open/en/query-rewriting.html-using-a-finite-state-automaton", "fields": {"questions": {"assign": ["What is a Finite state automaton?", "What is vespa-makefsa?", "How are automata used in rule bases?", "How to compile an automaton source file?", "What is the format of an automaton source file?", "How to use automata in a rule base?", "What is the purpose of using automata in a rule base?", "How to declare automata in a rule base?"]}}} -{"update": "id:open-p:paragraph::open/en/query-rewriting.html-query-phrasing", "fields": {"questions": {"assign": ["What is the PhrasingSearcher?", "How is the PhrasingSearcher configured?", "What is the purpose of vespa-makefsa?", "What does the PhrasingSearcher do?", "How should the list of phrases be sorted?", "What is the output of vespa-makefsa?", "What is the format of the phrase list file?", "What happens if the list of phrases contains non-ASCII characters?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/rank-feature-configuration.html-", "fields": {"questions": {"assign": ["What are rank features?", "How do you set configuration variables?", "What are per field features?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/rank-feature-configuration.html-variables", "fields": {"questions": {"assign": ["What is a feature class?", "What are configuration properties?", "What is the syntax for setting rank properties?", "What is a usage example of rank properties and features?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/rank-feature-configuration.html-configuration-properties", "fields": {"questions": {"assign": ["What is the default numTerms for term feature?", "What is the default b value for bm25 feature?", "What is the maximum number of alternative segmentations allowed?", "What is the maximal age in seconds used for freshness feature?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/rank-features.html-", "fields": {"questions": {"assign": ["What is the rank feature?", "What are the available rank features?", "What is the range of normalized features?", "Can a query override a rank feature value?", "Do some rank features have parameters?", "How are string literals converted?", "What is the overview of the ranking framework?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/rank-features.html-closeness", "fields": {"questions": {"assign": ["What is shown in the plot?", "What is closeness distance?", "What is the default maxDistance?", "What are possible scaleDistance values?", "What are the corresponding halfResponse parameters?", "How does scaleDistance affect the logscale output?", "What is the default value of scaleDistance?", "What is the default closeness output graph called?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/rank-features.html-freshness", "fields": {"questions": {"assign": ["What does the plot show?", "What is freshness rank?", "What is the default maxAge?", "What is the default halfResponse value?", "What are the different logscale outputs for halfResponse parameter?", "What is the output for halfResponse=172800s?", "What is the output for halfResponse=1209600s?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/rank-types.html-", "fields": {"questions": {"assign": ["What are the supported rank-types?", "How are ranking features controlled?", "What is the native rank reference?", "How are ranking types described?", "What are the generic mechanisms for ranking control?", "What are the different ranking controls used?", "Where can I find detailed information on nativeRank?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/rank-types.html-using-rank-type", "fields": {"questions": {"assign": ["What is rank-types?", "What is rank-profile?", "Can rank-types be controlled per field?", "What is the syntax for defining rank-types?", "What is 'expression' used for in rank-profile?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/rank-types.html-ranking-controls", "fields": {"questions": {"assign": ["What are generic ranking controls?", "What influences ranking type selection?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/rank-types.html-firstocc-boost", "fields": {"questions": {"assign": ["What is FirstOcc?", "What is a FirstOcc boost table?", "What does equation 1 represent?", "What is the weight parameter?", "What does Figure 1-2 illustrate?", "What does increasing w do in Figure 1?", "What does increasing t do in Figure 2?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/rank-types.html-numocc-boost", "fields": {"questions": {"assign": ["What is NumOcc?", "What is a NumOcc boost table?", "What does the NumOcc boost table function do?", "What is the equation for the NumOcc boost table function?", "What is the weight parameter?", "What is the tune parameter?", "What do the NumOcc Plot weight and NumOcc Plot tune graphs show?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/rank-types.html-proximity-boost", "fields": {"questions": {"assign": ["What is pairwise proximity?", "How is proximity calculated?", "What is the equation for proximity boost?", "What are proximity tables used for?", "What is the difference between forward and reverse proximity?", "What are the parameters for the proximity boost equation?", "What is the effect of weight in the proximity boost equation?", "What is the effect of tune in the proximity boost equation?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/rank-types.html-weight-boost", "fields": {"questions": {"assign": ["What is the weightboost table?", "What does equation 4 show?", "How is weight(x) calculated?", "What is the input for the boost table?", "Can attributes have negative rank contributions?", "How is x defined for weighted sets?", "How is x defined for arrays?", "How is x defined for single values?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/rank-types.html-rank-types", "fields": {"questions": {"assign": ["What is described in this section?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/rank-types.html-\"identity\"-rank-type", "fields": {"questions": {"assign": ["What is FirstOcc in Vespa ranking?", "How does nativeProximity.proximityTable work?", "What types of fields work well with identity term ranking?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/rank-types.html-\"about\"-rank-type", "fields": {"questions": {"assign": ["What is the default ranking type?", "What is the NumOcc boost table?", "What is the proximity boost?", "What is the supported ranking type?", "What is the FirstOcc boost table?", "What are typical examples of a description field?", "What is the about-ness of description related to?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/rank-types.html-\"tags\"-rank-type", "fields": {"questions": {"assign": ["What is the 'tags' rank type?", "How does the 'tags' rank type work?"]}}} -{"update": "id:open-p:paragraph::open/en/ranking-expressions-features.html-", "fields": {"questions": {"assign": ["What is the ranking introduction?", "What are ranking expressions?", "Where to find guides for rank features?"]}}} -{"update": "id:open-p:paragraph::open/en/ranking-expressions-features.html-ranking-expressions", "fields": {"questions": {"assign": ["What are ranking expressions?", "What operators do ranking expressions use?", "What is two-phase ranking?", "How can ranking expressions be created?", "What is the resource driver in ranking?", "What is the purpose of two-phase ranking?"]}}} -{"update": "id:open-p:paragraph::open/en/ranking-expressions-features.html-rank-features", "fields": {"questions": {"assign": ["What are rank features?", "How are rank features computed?", "What is the format of defining rank features?", "What is ignore-default-rank-features?", "How can rank features be manually added?", "What is the command for including default rank features?", "What is the use of recording rank feature values?"]}}} -{"update": "id:open-p:paragraph::open/en/ranking-expressions-features.html-normalization", "fields": {"questions": {"assign": ["What is the range of normalized features?", "Why prefer normalized features?", "How can normalized rank scores be implemented?", "What are un-normalized features?", "What is relevance based blending?", "What can be triggered if there are no good hits?"]}}} -{"update": "id:open-p:paragraph::open/en/ranking-expressions-features.html-configuration", "fields": {"questions": {"assign": ["How are feature configurations set?", "What are rank-properties?", "What is maxAlternativeSegmentations?", "What is maxOccurrences?", "What are the benefit of feature configuration tuning?"]}}} -{"update": "id:open-p:paragraph::open/en/ranking-expressions-features.html-feature-contribution-functions", "fields": {"questions": {"assign": ["What is the earliness feature?", "How are Vespa ranking features linear?"]}}} -{"update": "id:open-p:paragraph::open/en/ranking-expressions-features.html-dumping-rank-features-for-specific-documents", "fields": {"questions": {"assign": ["What is the purpose of adding a specific term to a query?", "What does the 'ranked' parameter do?", "How can you prevent a term from impacting rank features?", "Can you search for a document by its ID without affecting rank features?"]}}} -{"update": "id:open-p:paragraph::open/en/ranking-expressions-features.html-accessing-feature/function-values-in-results", "fields": {"questions": {"assign": ["What are summary-features?", "Why use match-features?", "What is tensor_join?", "What data type does featureData return?", "What is the difference between match-features and summary-features?", "What is the computation in join_sum()?"]}}} -{"update": "id:open-p:paragraph::open/en/ranking-expressions-features.html-the-\"if\"-function-and-string-equality-tests", "fields": {"questions": {"assign": ["What is the 'if' function used for in Vespa?", "How can 'if' functions be used for ranking in Vespa?", "What is string equality in ranking expressions?", "What is an example of using 'if' for ranking different types of documents?", "How is strict tiering implemented using 'if' functions?", "Can you give an example of sorting documents by multiple criteria?", "What is the benefit of using 'if' functions for ranking in Vespa?", "How do multiple schemas contribute to the use of 'if' functions in ranking?"]}}} -{"update": "id:open-p:paragraph::open/en/ranking-expressions-features.html-using-constants", "fields": {"questions": {"assign": ["What are ranking expressions referencing?", "What is a constants clause?", "How are constants useful in rank profiles?", "When should query variables be used instead of constants?", "What happens to constants during configuration time?", "Can constants be overridden in sub-profiles?", "What is the benefit of using constants in ranking expressions?", "What is the performance impact of using constants instead of query variables?"]}}} -{"update": "id:open-p:paragraph::open/en/ranking-expressions-features.html-using-query-variables", "fields": {"questions": {"assign": ["What are query features?", "What are ranking variables?", "How can query specify importance?", "How to override ranking variables?"]}}} -{"update": "id:open-p:paragraph::open/en/ranking-expressions-features.html-query-feature-types", "fields": {"questions": {"assign": ["What are query features?", "What is the default feature type?", "How to define feature type?", "What produces incorrect results?", "What is a RankFeatures instance?", "How to construct tensors programmatically?"]}}} -{"update": "id:open-p:paragraph::open/en/ranking-expressions-features.html-function-snippets", "fields": {"questions": {"assign": ["What is machine learned ranking?", "What is function space in MLR?", "How do we increase the space of functions in MLR?", "What are primitive features in Vespa?", "How can primitive features be used in MLR?", "What are expression functions in Vespa?", "How can functions with zero arguments be used in ranking?", "How do we define summary-features in Vespa?"]}}} -{"update": "id:open-p:paragraph::open/en/ranking-expressions-features.html-tracking-relevance-variations-over-time", "fields": {"questions": {"assign": ["What are Vespa relevance metrics?", "What are the relevance metrics called?", "Where can I find more information about Vespa relevance metrics?", "How can Vespa's relevance metrics change?", "What do Vespa's relevance metrics measure?"]}}} -{"update": "id:open-p:paragraph::open/en/ranking-expressions-features.html-examples", "fields": {"questions": {"assign": ["What is the formula to assign adult content?", "What is the match quality formula?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/ranking-expressions.html-", "fields": {"questions": {"assign": ["What are ranking expressions?", "What are constants in ranking expressions?", "What can be combined in ranking expressions?", "What is the language used in ranking expressions?", "How are strings used in ranking expressions?", "What is the ranking overview?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/ranking-expressions.html-arithmetic-operations", "fields": {"questions": {"assign": ["What is in-fix notation?", "What are arithmetic operators?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/ranking-expressions.html-mathematical-scalar-functions", "fields": {"questions": {"assign": ["What is the function acos?", "What is the function asin?", "What is the function atan?", "What is the function atan2?", "What is the function ceil?", "What is the function cos?", "What is the function cosh?", "What is the function elu?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/ranking-expressions.html-the-if-function", "fields": {"questions": {"assign": ["What is the 'if' function?", "How does the 'if' function work?", "What are the conditional operators in if functions?", "What is the membership operator in if functions?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/ranking-expressions.html-the-foreach-function", "fields": {"questions": {"assign": ["What is the foreach function?", "How is foreach implemented?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/ranking-expressions.html-tensor-functions", "fields": {"questions": {"assign": ["What are tensor functions?", "What is a primitive function?", "What are convenience functions?", "How are the functions grouped?", "Can ranking expressions use tensors?", "What is the purpose of convenience functions?", "What is the purpose of primitive functions?", "How are convenience functions implemented?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/ranking-expressions.html-with-tensors", "fields": {"questions": {"assign": ["What is t1?", "What is t2?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/ranking-expressions.html-with-tensors", "fields": {"questions": {"assign": ["What is t1 and t2?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/ranking-expressions.html-with-tensor", "fields": {"questions": {"assign": ["What is t1?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/ranking-expressions.html-with-tensors", "fields": {"questions": {"assign": ["What is stored in t1?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/ranking-expressions.html-with-tensors", "fields": {"questions": {"assign": ["What is t1?", "What is t2?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/ranking-expressions.html-you-get", "fields": {"questions": {"assign": ["What is concat() function?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/ranking-expressions.html-with-tensors", "fields": {"questions": {"assign": ["What is t1?", "What is t2?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/ranking-expressions.html-with-tensor", "fields": {"questions": {"assign": ["What does t1 hold?", "What values does t1 contain?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/ranking-expressions.html-with-tensors", "fields": {"questions": {"assign": ["What is the difference between 'float' and 'bfloat16' tensors?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/ranking-expressions.html-...-you-get", "fields": {"questions": {"assign": ["What is t2?", "What is cell_cast?", "What is bfloat16?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/ranking-expressions.html-lambda-functions-in-primitive-functions", "fields": {"questions": {"assign": ["What are lambda functions?", "What are the atoms in a lambda?", "Can expressions access variables outside the lambda?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/ranking-expressions.html-non-primitive-functions", "fields": {"questions": {"assign": ["What is l1_normalize?", "What is max(t, dim)?", "What is hamming(t1,t2)?", "What is random(n1, n2, ...)?", "What is xw_plus_b(x,w,b,dim)?"]}}} -{"update": "id:open-p:paragraph::open/en/ranking.html-", "fields": {"questions": {"assign": ["What is ranking in Vespa?", "Where are ranking expressions defined?"]}}} -{"update": "id:open-p:paragraph::open/en/ranking.html-two-phase-ranking", "fields": {"questions": {"assign": ["How many phases can a rank profile define?", "What is evaluated locally on content nodes?", "How is the second phase executed?", "What is rerank-count?", "What is the purpose of phased ranking?"]}}} -{"update": "id:open-p:paragraph::open/en/ranking.html-global-phase-ranking", "fields": {"questions": {"assign": ["What is the global-phase?", "What is second-phase?", "What is used by my_onnx_model?", "How is the global phase different?", "How is the global-phase expression executed?"]}}} -{"update": "id:open-p:paragraph::open/en/ranking.html-machine-learned-model-inference", "fields": {"questions": {"assign": ["What ML formats does Vespa support?", "How can I combine ML models in Vespa for ranking?", "What is a model ensemble in Vespa?", "What is the syntax for using a model ensemble in a rank-profile in Vespa?"]}}} -{"update": "id:open-p:paragraph::open/en/ranking.html-rank-profiles", "fields": {"questions": {"assign": ["What are rank profiles?", "What is the default text ranking profile?", "How are rank profiles selected for queries?", "What is inheritance in rank profiles?", "What is the 'default' rank profile?", "What is the 'unranked' rank profile?", "Where can rank profiles be defined?", "How many rank profiles can a schema have?"]}}} -{"update": "id:open-p:paragraph::open/en/ranking.html-evaluation", "fields": {"questions": {"assign": ["How are rank profiles evaluated?", "What is the 'foo' function?", "What is the 'bar' function?", "What is the purpose of 'foo' function?", "What arguments can be passed between functions?", "Can 'sum' be computed lazily in 'bar' function?"]}}} -{"update": "id:open-p:paragraph::open/en/performance/rate-limiting-searcher.html-", "fields": {"questions": {"assign": ["What is Vespa Rate Limiting Searcher?", "What HTTP code is returned for exceeded quota?", "What is the consequence of exceeding the quota?"]}}} -{"update": "id:open-p:paragraph::open/en/performance/rate-limiting-searcher.html-getting-started", "fields": {"questions": {"assign": ["What is the rate limiting searcher?", "How to enable rate limiting searcher?", "How to enable the rate limiting searcher on per-request basis?"]}}} -{"update": "id:open-p:paragraph::open/en/performance/rate-limiting-searcher.html-activate-with-query-parameters", "fields": {"questions": {"assign": ["What is rate.cost?", "What is rate.quota used for?", "How do you set rate.id?"]}}} -{"update": "id:open-p:paragraph::open/en/performance/rate-limiting-searcher.html-activate-with-query-profiles", "fields": {"questions": {"assign": ["How can I enable rate limiting?"]}}} -{"update": "id:open-p:paragraph::open/en/performance/rate-limiting-searcher.html-per-client-quotas", "fields": {"questions": {"assign": ["What is rate limiting?", "What is clientId?", "What is the syntax for assigning a quota based on clientId?", "How can we add a specific configuration for a given clientId?", "What is the default quota if no clientId is specified?"]}}} -{"update": "id:open-p:paragraph::open/en/performance/rate-limiting-searcher.html-metrics", "fields": {"questions": {"assign": ["What is the `requestsOverQuota` metric?", "What is rate.idDimension?", "What is rate.id?"]}}} -{"update": "id:open-p:paragraph::open/en/reads-and-writes.html-", "fields": {"questions": {"assign": ["How are writes and reads processed?", "What is Vespa's consistency model?"]}}} -{"update": "id:open-p:paragraph::open/en/reads-and-writes.html-operations", "fields": {"questions": {"assign": ["What is a Get operation?", "What is a Put operation?", "What is a Remove operation?", "What is an Update operation?", "Can updates have conditions?", "What are the composite types?"]}}} -{"update": "id:open-p:paragraph::open/en/reads-and-writes.html-api-and-utilities", "fields": {"questions": {"assign": ["What is Java Document API?", "What is the purpose of vespa-feeder?"]}}} -{"update": "id:open-p:paragraph::open/en/reads-and-writes.html-feed-flow", "fields": {"questions": {"assign": ["What is vespa-feeder?"]}}} -{"update": "id:open-p:paragraph::open/en/operations/reindexing.html-", "fields": {"questions": {"assign": ["What triggers Vespa to refeed data?", "What are changes in the indexing pipeline?", "How can reindexing be triggered?", "What is the reindex endpoint?", "What is the reindexing endpoint?", "What can cause changes in the indexing pipeline?", "What is indexing mode in a content cluster?"]}}} -{"update": "id:open-p:paragraph::open/en/operations/reindexing.html-when-to-reindex", "fields": {"questions": {"assign": ["What happens when there is a change in the indexing pipeline?", "How is a change in indexing pipeline discovered?", "What are reindex actions?", "When should you trigger reindexing?", "When does reindexing commence?", "What is the prepare endpoint?", "What is the reindex endpoint?"]}}} -{"update": "id:open-p:paragraph::open/en/operations/reindexing.html-reindexing-progress", "fields": {"questions": {"assign": ["What is reindexing?", "How does reindexing work?", "What happens during reindexing?", "What is the purpose of reindexing?", "Which component does reindexing?", "What is the progress endpoint for reindexing?", "How is write latency impacted during reindexing?", "What types of documents are reindexed?"]}}} -{"update": "id:open-p:paragraph::open/en/operations/reindexing.html-procedure", "fields": {"questions": {"assign": ["What is schema changes procedure?", "How to test reindexing feature?", "What are tools to validate data?"]}}} -{"update": "id:open-p:paragraph::open/en/operations/reindexing.html-use-cases", "fields": {"questions": {"assign": ["What is advanced indexing language?"]}}} -{"update": "id:open-p:paragraph::open/en/reranking-in-searcher.html-", "fields": {"questions": {"assign": ["What is a stateless searcher?", "What is multiphase searching?", "What is the matching query protocol phase?", "What is the fill query protocol phase?", "What is the life of a query in Vespa?"]}}} -{"update": "id:open-p:paragraph::open/en/reranking-in-searcher.html-a-minimal-vespa-application", "fields": {"questions": {"assign": ["What rank profile is used for re-ranking?", "What is normalizedByMax used for?"]}}} -{"update": "id:open-p:paragraph::open/en/reranking-in-searcher.html-starting-vespa", "fields": {"questions": {"assign": ["How to install vespa-cli?", "What does Maven project do?"]}}} -{"update": "id:open-p:paragraph::open/en/reranking-in-searcher.html-query-the-data", "fields": {"questions": {"assign": ["What is reranking searcher?"]}}} -{"update": "id:open-p:paragraph::open/en/reranking-in-searcher.html-teardown", "fields": {"questions": {"assign": ["What does docker rm do?"]}}} -{"update": "id:open-p:paragraph::open/en/result-rendering.html-", "fields": {"questions": {"assign": ["What is the SectionedRenderer class?", "What is the difference between SectionedRenderer and Renderer?", "How are renderers deployed and configured?", "What is the AsynchronousSectionedRenderer class?", "What is the contract for creating renderers?", "What is the purpose of renderers?", "Can renderers use and store state during rendering?", "What is the configuration file for enabling a renderer?"]}}} -{"update": "id:open-p:paragraph::open/en/result-rendering.html-renderer", "fields": {"questions": {"assign": ["What is a renderer in Vespa?", "How does a renderer work?", "What is the purpose of extending Renderer?", "What are the entities rendered in a renderer?", "Can you provide a simple example of a renderer?", "Can you provide a more complex example of a renderer?"]}}} -{"update": "id:open-p:paragraph::open/en/result-rendering.html-sectionedrenderer", "fields": {"questions": {"assign": ["What is a SectionedRenderer subclass?", "How to create a SectionedRenderer?", "What are the methods for entities?", "What callback methods are available?", "What is a compound entity?", "What is the result class?", "What is the call sequence for entities?", "What are the subclasses of HitGroup?"]}}} -{"update": "id:open-p:paragraph::open/en/result-rendering.html-json-example", "fields": {"questions": {"assign": ["What is the default JSON result format?", "What is MyRenderer?"]}}} -{"update": "id:open-p:paragraph::open/en/result-rendering.html-asynchronoussectionedrenderer", "fields": {"questions": {"assign": ["What is SectionedRenderer?", "What is getResponse() used for?", "Where can I find an example of DemoRenderer.java?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/routing.html-", "fields": {"questions": {"assign": ["What is routing used for?", "When is explicit routing needed?", "What are routing policies?", "What is Message Bus?", "What are the atoms in Vespa routing?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/routing.html-a-route-is-a-sequence-of-hops", "fields": {"questions": {"assign": ["What is a route in Vespa?", "What are hops and selectors?", "What is a hop?", "Can a route branch in Vespa?", "Does Vespa support multicasting?", "What does a node represent in Vespa?", "How does Message Bus support routing?", "How can a hop modify a message's journey?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/routing.html-a-hop-is-a-point-to-point-transmission", "fields": {"questions": {"assign": ["What is a hop?", "What is a recipient?", "What is a selector?", "What is a Message Bus?", "What is the purpose of Vespa's service location broker?", "What is the derivation logic for recipient service names?", "How can you inspect symbolic names in Vespa?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/routing.html-asterisk", "fields": {"questions": {"assign": ["What is a service identifier?", "What happens when a recipient contains *?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/routing.html-routing-policies", "fields": {"questions": {"assign": ["What is a routing policy?", "What does a routing policy do?", "What is the purpose of a routing policy?", "How is merging implemented in a routing policy?", "What is the 'RoutingContext' object?", "What is the message guarantee of Message Bus?", "What is the routing policy reference?", "What are the components of a Message Bus protocol?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/routing.html-selection-logic", "fields": {"questions": {"assign": ["What is Message Bus?", "How does Message Bus resolve recipients?", "What happens if there are circular dependencies in Message Bus?", "What is the purpose of policy directives in Message Bus?", "How does Message Bus send messages to recipients?", "What is the routing table in Message Bus?", "Does Message Bus support complex routing trees?", "What are the safeguards in Message Bus?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/routing.html-1.-resolve-policy-directives", "fields": {"questions": {"assign": ["What is the logic run?", "What is a policy directive?", "What is the hop string?", "What is a recipient in hop?", "What is the purpose of DocumentRouteSelector?", "What is merge() method?", "What is AND policy?", "What does the policies article explain?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/routing.html-2.-resolve-hop--and-route-names", "fields": {"questions": {"assign": ["What happens when a string is recognized as a hop name?", "What is the outcome if the string is recognized as a service name?", "What is the purpose of the 'route:' prefix in Message Bus?", "What is the order of checking for string recognition in Message Bus?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/routing.html-3.-send-to-services", "fields": {"questions": {"assign": ["What happens at this route resolver point?", "What does each leaf node represent?", "How is message transmission optimized?", "When does route resolving occur?", "What happens if route configuration changes?", "What is a hop that ignores results?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/routing.html-example:-reconfigure-the-default-route", "fields": {"questions": {"assign": ["How to block an incoming document?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/routing.html-the-document-api", "fields": {"questions": {"assign": ["What is Document API?", "What is MessageBusDocumentAccessParams?", "What is the purpose of routeName?", "What is the purpose of traceLevel?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/schema-reference.html-", "fields": {"questions": {"assign": ["What is the document type syntax?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/schema-reference.html-syntax", "fields": {"questions": {"assign": ["What does a string in square brackets represent?", "What are the constructs in schemas?", "What is the syntax for an element?", "What is a multiline value?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/schema-reference.html-schema", "fields": {"questions": {"assign": ["What is a schema?", "What is a constant?", "What is a stemming setting?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/schema-reference.html-document", "fields": {"questions": {"assign": ["What is document inheritance?", "What is compression?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/schema-reference.html-struct", "fields": {"questions": {"assign": ["What is a struct?", "What is a composite type?", "How do you define a struct?", "What is a field in a struct?", "How many fields can a struct have?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/schema-reference.html-field", "fields": {"questions": {"assign": ["What are some Vespa field types?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/schema-reference.html-struct-field", "fields": {"questions": {"assign": ["What is a struct-field?", "What is the body of a struct-field?", "What is indexing in struct fields?", "What is attribute in struct fields?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/schema-reference.html-fieldset", "fields": {"questions": {"assign": ["What is a fieldset?", "How do fieldsets work?", "What is the purpose of a fieldset?", "Can a fieldset have different match modes?", "What query returns all documents containing a fieldset with a specific value?", "Does creating a fieldset create extra index structures?", "How can specific match settings be applied to a fieldset?", "Can query-commands be used with a fieldset?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/schema-reference.html-compression", "fields": {"questions": {"assign": ["What is enabled by lz4 compression?", "What is the compression type?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/schema-reference.html-rank-profile", "fields": {"questions": {"assign": ["What is a rank profile?", "How to define rank profiles?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/schema-reference.html-match-phase", "fields": {"questions": {"assign": ["What is match-phase feature?", "What is the purpose of match-phase?", "How does match-phase increase performance?", "What is graceful degradation?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/schema-reference.html-strict", "fields": {"questions": {"assign": ["What is loose type checking?", "What is strict?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/schema-reference.html-diversity", "fields": {"questions": {"assign": ["What is diversity in Vespa?", "What is min-groups?", "What is the diversity attribute?", "What kind of attribute can be used for diversity in Vespa?", "How does diversity guarantee unique results?", "How is min-groups used to reduce max-hits in Vespa?", "What are the requirements for a document to be considered as a candidate for diversity in Vespa?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/schema-reference.html-first-phase", "fields": {"questions": {"assign": ["What is first-phase ranking?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/schema-reference.html-expression", "fields": {"questions": {"assign": ["What is a ranking expression?", "How do you load a ranking expression file?", "What is the syntax for writing a ranking expression?", "When is it useful to load expressions from a separate file?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/schema-reference.html-rank-features", "fields": {"questions": {"assign": ["How to list rank features?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/schema-reference.html-inputs", "fields": {"questions": {"assign": ["How can input types be declared?", "What are input examples?", "What is the inputs table for?", "How are default values specified for inputs?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/schema-reference.html-constants", "fields": {"questions": {"assign": ["What are constants in Vespa?", "How are constants defined?", "What types of constants can be defined?", "How are constants inherited in Vespa?", "What is the format for defining constant values?", "Can a constant value be stored in a file?", "How can a large tensor be defined as a constant?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/schema-reference.html-rank-properties", "fields": {"questions": {"assign": ["What are rank properties?", "What is a key/value pair?", "What is the description of the key in rank-properties?", "How are multiple properties specified in rank-properties?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/schema-reference.html-function-(inline)%3F-[name]", "fields": {"questions": {"assign": ["What is a named function?", "What is the inline modifier?", "What are function arguments?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/schema-reference.html-second-phase", "fields": {"questions": {"assign": ["What is second-phase ranking?", "What is the body of second-phase statement?", "What can be put in the second-phase?", "What is Machine Learned Ranking?", "How is second-phase ranking performed?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/schema-reference.html-global-phase", "fields": {"questions": {"assign": ["What is global-phase ranking?", "What is the purpose of global-phase ranking?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/schema-reference.html-summary-features", "fields": {"questions": {"assign": ["What is a summary features field?", "What are rank features?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/schema-reference.html-match-features", "fields": {"questions": {"assign": ["What is matchfeatures field?", "What is summary-features?", "What are the rank features?", "What is multiphase searching?", "What is schema inheritance?", "What is the format for match-features?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/schema-reference.html-mutate", "fields": {"questions": {"assign": ["What is mutate?", "What operations can be done in mutate?", "What are the phases in mutate?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/schema-reference.html-constant", "fields": {"questions": {"assign": ["What is constant tensor?", "How to define a constant?", "What is file in constant?", "What is type in constant?", "What is an example of constant tensor?", "What is the format of a constant tensor file?", "How are constant tensors distributed?", "Can incremental changes be made to constant tensors?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/schema-reference.html-raw-as-base64-in-summary", "fields": {"questions": {"assign": ["What is the raw field?", "What is base64 encoding?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/schema-reference.html-onnx-model", "fields": {"questions": {"assign": ["What is an ONNX model?", "How do you define an ONNX model in Vespa?", "What must the ONNX model body contain?", "How is the path to the ONNX model defined?", "What is the maximum number of inputs and outputs for an ONNX model?", "What is the purpose of intraop-threads?", "What is the execution-mode of an ONNX model?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/schema-reference.html-document-summary", "fields": {"questions": {"assign": ["What is a document summary?", "What is the purpose of document summaries?", "How many occurrences of summary can a document summary have?", "What is the use of the 'inherits' attribute in document summary?", "What is the body of a document summary consist of?", "What is the use of [from-disk] and [omit-summary-features] in document summary?", "What is the rank-profile in document summary used for?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/schema-reference.html-stemming", "fields": {"questions": {"assign": ["What is stemming?", "What is the default stemming type?", "What is 'none' stemming?", "What is 'best' stemming?", "What is 'shortest' stemming?", "What is 'multiple' stemming?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/schema-reference.html-normalizing", "fields": {"questions": {"assign": ["What is the default normalization?", "What is a normalizing-type?", "What field does normalizing occur on?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/schema-reference.html-alias", "fields": {"questions": {"assign": ["What is an alias in Vespa?", "How to make an alias?", "What can be aliased in Vespa?", "What is the syntax for making an alias?", "Are dots allowed in alias names?", "How does an alias work?", "Can an attribute be aliased?", "Can a field be aliased?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/schema-reference.html-dictionary", "fields": {"questions": {"assign": ["What is the difference between 'hash' and 'btree'?", "When is 'hash' preferred over 'btree'?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/schema-reference.html-attribute", "fields": {"questions": {"assign": ["What is fast-access?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/schema-reference.html-sorting", "fields": {"questions": {"assign": ["What is sorting in Vespa?", "What is the default sort function?", "What is the default sort order?", "What is UCA sort strength?", "What is a UCA locale?", "What is the default UCA locale?", "What is a sort property?", "Where is sorting specified?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/schema-reference.html-distance-metric", "fields": {"questions": {"assign": ["What are defining properties of metric space?", "What is Dense passage retrieval sample app?", "What is great-circle distance?", "What is Hamming distance metric?", "What is prenormalized-angular distance metric?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/schema-reference.html-bolding", "fields": {"questions": {"assign": ["What is bolding?", "What fields support bolding?", "What is the default XML element used for highlighting?", "What happens if both bolding and summary: dynamic are specified?", "What is the maximum byte length for bolding?", "What is summary: dynamic?", "What types of fields support summary: dynamic?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/schema-reference.html-id", "fields": {"questions": {"assign": ["What are the id requirements?", "What are the advantages of explicitly setting id's?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/schema-reference.html-index", "fields": {"questions": {"assign": ["What are index parameters?", "What is an alias in index?", "What is stemming setting?", "What is dense posting list threshold value?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/schema-reference.html-hnsw", "fields": {"questions": {"assign": ["What is an HNSW index?", "What query operator uses HNSW?", "What are the parameters of HNSW?", "What is the default value of max-links-per-node parameter?", "What is the default value of neighbors-to-explore-at-insert parameter?", "Where can I find examples of using HNSW in Vespa?", "Why did Vespa team select HNSW as the baseline algorithm?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/schema-reference.html-indexing", "fields": {"questions": {"assign": ["What is the indexing language?", "What does index statement do?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/schema-reference.html-match", "fields": {"questions": {"assign": ["What is match type \"text\"?", "What is match type \"word\"?", "What is match type \"exact\"?", "What is match type \"cased\"?", "What is match type \"uncased\"?", "What is match type \"max-length\"?", "What is match type \"gram\"?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/schema-reference.html-rank", "fields": {"questions": {"assign": ["What is 'filter' ranking setting?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/schema-reference.html-query-command", "fields": {"questions": {"assign": ["What is query-command?", "What is the built-in query-command available?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/schema-reference.html-rank-type", "fields": {"questions": {"assign": ["What is the 'rank-type'?", "What are defined rank types?", "What is the default rank type?", "What is the 'tags' rank type?", "What is the 'empty' rank type?", "How can you configure native rank features?", "Where can you find more information on native rank?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/schema-reference.html-summary-to", "fields": {"questions": {"assign": ["What is `summary-to`?", "What does `summary-to` specify?", "What is the default summary behavior for fields with summary?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/schema-reference.html-summary", "fields": {"questions": {"assign": ["What is a summary field?", "What is a dynamic summary?", "What is a full summary?", "What is 'bolding on' in summary?", "What is 'to' summary?", "What are the summary data types available?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/schema-reference.html-weight", "fields": {"questions": {"assign": ["What is field weight?", "What is the default field weight?", "How is field weight used?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/schema-reference.html-import-field", "fields": {"questions": {"assign": ["What is an imported field?", "What can be imported?", "What is a position field?", "Can imported fields be used to expire documents?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/schema-reference.html-field-types", "fields": {"questions": {"assign": ["What is a string field?", "What is the syntax for searching in the URL field?", "What is a reference field?", "Can a reference field be searched using fast-search?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/schema-reference.html-document-and-search-field-types", "fields": {"questions": {"assign": ["What is a document field?", "How to make a string attribute?", "What is the syntax for creating an integer attribute?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/schema-reference.html-modifying-schemas", "fields": {"questions": {"assign": ["How can a schema be modified?", "What are the categories for schema modification?", "What are the valid changes without restart or re-feed?", "What are the changes that require restart but not re-feed?", "What are the changes that require reindexing?", "What are the changes that require re-feed?", "What is compared when running vespa-deploy prepare?", "What does the output from vespa-deploy prepare specify?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/schema-reference.html-valid-changes-without-restart-or-re-feed", "fields": {"questions": {"assign": ["What is a field alias?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/schema-reference.html-changes-that-require-restart-but-not-re-feed", "fields": {"questions": {"assign": ["What are restart actions?", "What is a schema change?", "What does the output of vespa-deploy prepare specify?", "What are the changes that can be made to attribute settings?", "What is the procedure for changing attribute aspect of a document field?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/schema-reference.html-changes-that-require-reindexing", "fields": {"questions": {"assign": ["What is reindexing?", "When is reindexing triggered?", "What happens during reindexing?", "How does reindexing affect annotations?", "What changes need reindexing?", "How to trigger reindexing?", "Why does reindexing require disk space?", "What is the output of vespa-deploy prepare?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/schema-reference.html-changes-that-require-re-feed", "fields": {"questions": {"assign": ["What is re-feeding?", "What changes require re-feeding?", "How to re-feed documents?", "What is the output of vespa-deploy prepare?"]}}} -{"update": "id:open-p:paragraph::open/en/schemas.html-", "fields": {"questions": {"assign": ["What is a schema?", "Where are schemas stored?", "What is the format of a schema file?", "What is schema inheritance?", "What is a document type in a schema?", "What is the purpose of a schema?"]}}} -{"update": "id:open-p:paragraph::open/en/schemas.html-schema-concepts", "fields": {"questions": {"assign": ["Where can I find a complete schema reference?"]}}} -{"update": "id:open-p:paragraph::open/en/schemas.html-document", "fields": {"questions": {"assign": ["Is document id a field?"]}}} -{"update": "id:open-p:paragraph::open/en/schemas.html-field", "fields": {"questions": {"assign": ["What is a synthetic field?", "What is a multivalue field?", "Can multivalue fields be used in grouping?", "Can attributes in maps and arrays be used in ranking?", "How to filter based on number of elements?", "Is filtering based on number of elements more expensive than using a separate field?"]}}} -{"update": "id:open-p:paragraph::open/en/schemas.html-indexing", "fields": {"questions": {"assign": ["What are the important indexing modes?"]}}} -{"update": "id:open-p:paragraph::open/en/schemas.html-match", "fields": {"questions": {"assign": ["What is match mode?", "What is sameElement() used for?"]}}} -{"update": "id:open-p:paragraph::open/en/schemas.html-fieldset", "fields": {"questions": {"assign": ["What is a fieldset?", "What is the default fieldset?", "How to query a specific fieldset?"]}}} -{"update": "id:open-p:paragraph::open/en/schemas.html-rank-profile", "fields": {"questions": {"assign": ["What is a rank profile?", "What does a rank profile define?"]}}} -{"update": "id:open-p:paragraph::open/en/schemas.html-plugins", "fields": {"questions": {"assign": ["What is the Vespa IntelliJ plugin?", "How does the plugin simplify schema work?"]}}} -{"update": "id:open-p:paragraph::open/en/schemas.html-schema-modifications", "fields": {"questions": {"assign": ["How does Vespa handle schema modifications?", "What type of changes can be made to a schema without validation override?", "What is a validation override?", "How to add a validation override?", "What are schema modifications?"]}}} -{"update": "id:open-p:paragraph::open/en/schemas.html-content-cluster-mapping", "fields": {"questions": {"assign": ["What is a content cluster?", "How do applications map multiple schemas?", "How to limit a query to a subset of content clusters?", "What is the use of restrict and sources combined?", "Where can I find more details about content nodes and schemas?"]}}} -{"update": "id:open-p:paragraph::open/en/schemas.html-schema-inheritance", "fields": {"questions": {"assign": ["What is document type inheritance?", "What is schema inheritance?", "What are the elements that support inheritance?", "Why is type hierarchy useful for federated queries?"]}}} -{"update": "id:open-p:paragraph::open/en/schemas.html-document-type-inheritance", "fields": {"questions": {"assign": ["What is the purpose of the 'title' field in music and book schemas?", "What is the difference between schemas music and books?"]}}} -{"update": "id:open-p:paragraph::open/en/schemas.html-rank-profile-inheritance", "fields": {"questions": {"assign": ["What are rank profiles?", "How are rank profiles inherited?", "What is the default rank profile?"]}}} -{"update": "id:open-p:paragraph::open/en/schemas.html-document-summary-inheritance", "fields": {"questions": {"assign": ["What are document summaries?", "How do document summaries inherit?", "What is a document summary for 'items_summary_tiny'?", "What is a document summary for 'items_summary_full'?", "What is the 'title' field in 'items_summary_tiny' document summary?", "What is the 'author' field in 'items_summary_full' document summary?"]}}} -{"update": "id:open-p:paragraph::open/en/searcher-development.html-", "fields": {"questions": {"assign": ["How to develop Searcher components?"]}}} -{"update": "id:open-p:paragraph::open/en/searcher-development.html-searchers", "fields": {"questions": {"assign": ["What is the search method signature?", "What is a synchronous model?"]}}} -{"update": "id:open-p:paragraph::open/en/searcher-development.html-queries-and-results", "fields": {"questions": {"assign": ["What is a Result object?", "What does a Hit object contain?", "How are HitGroups organized?", "What is a boolean query tree?", "How can structured information be handled in a Searcher?"]}}} -{"update": "id:open-p:paragraph::open/en/searcher-development.html-search-chains", "fields": {"questions": {"assign": ["What are search chains?", "How are search chains executed?", "What is a searcher instance?", "Can a Searcher instance exist in multiple search chains?", "What is the purpose of asynchronous execution?", "How is the execution order of searchers determined?", "Where can I find more information about searchers and search chains?"]}}} -{"update": "id:open-p:paragraph::open/en/searcher-development.html-writing-a-searcher", "fields": {"questions": {"assign": ["What is SimpleSearcher?", "How is SimpleSearcher used?", "What is the function of the first line in SimpleSearcher?", "How does SimpleSearcher modify Result?", "When is a new instance of SimpleSearcher created?", "What API is available to searchers?"]}}} -{"update": "id:open-p:paragraph::open/en/searcher-development.html-testing-a-searcher", "fields": {"questions": {"assign": ["What is a search chain?", "What is a unit test?", "What is the SimpleSearcher?", "What is a mock searcher?", "What is Execution.Context?", "What is the Execution class?", "What is the Result class?", "What is the purpose of unit tests?"]}}} -{"update": "id:open-p:paragraph::open/en/searcher-development.html-deploying-a-searcher", "fields": {"questions": {"assign": ["How do you deploy a Vespa component?"]}}} -{"update": "id:open-p:paragraph::open/en/searcher-development.html-testing-a-searcher-with-an-application", "fields": {"questions": {"assign": ["How to test a searcher?"]}}} -{"update": "id:open-p:paragraph::open/en/searcher-development.html-passing-information-between-searchers", "fields": {"questions": {"assign": ["What is the query object?", "What is the general property store?", "What can be stored in the property store?", "What is the purpose of setTo?", "What is the purpose of getFrom?", "What is the SomeObject class?", "Why should objects enforce immutability?", "How should objects support downstream modifications?"]}}} -{"update": "id:open-p:paragraph::open/en/searcher-development.html-query-context", "fields": {"questions": {"assign": ["What is QueryContext?", "How is QueryContext accessed safely?", "What is a Query object?", "What is the purpose of QueryContext?", "What can be set in QueryContext?", "How are properties set in QueryContext?", "What is the caveats in QueryContext?", "How is QueryContext accessed from threads?"]}}} -{"update": "id:open-p:paragraph::open/en/searcher-development.html-parametrizing-searchers", "fields": {"questions": {"assign": ["How are parameters passed?"]}}} -{"update": "id:open-p:paragraph::open/en/searcher-development.html-execution-model", "fields": {"questions": {"assign": ["What happens when simultaneous requests arrive?", "What constraints are placed on searcher classes?"]}}} -{"update": "id:open-p:paragraph::open/en/searcher-development.html-keeping-state-in-searchers", "fields": {"questions": {"assign": ["What kind of variables are member variables?", "What is a multithread-safe searcher?", "What is the restriction for member variables?", "What are the options for implementing a multithread-safe searcher?", "What is the purpose of thread synchronization?"]}}} -{"update": "id:open-p:paragraph::open/en/searcher-development.html-multiphase-searching", "fields": {"questions": {"assign": ["What is multiphase searching?", "How does the search container support multiphase searching?", "What are the execution fill methods?", "What kind of searchers should call fill() method?", "What is the purpose of fill() method?", "What should federating searchers implement in multi-phase setup?", "What should backend searchers implement to support multi-phase searching?"]}}} -{"update": "id:open-p:paragraph::open/en/searcher-development.html-error-handling", "fields": {"questions": {"assign": ["How to signal an error?", "How to add error to hits?"]}}} -{"update": "id:open-p:paragraph::open/en/searcher-development.html-worditem", "fields": {"questions": {"assign": ["What is WordItem?", "How can I keep linguistic settings?", "What is the purpose of isFromQuery?"]}}} -{"update": "id:open-p:paragraph::open/en/searching-multi-valued-fields.html-", "fields": {"questions": {"assign": ["What field type does the guide use?", "Can the map field type be ranked?"]}}} -{"update": "id:open-p:paragraph::open/en/searching-multi-valued-fields.html-introduction", "fields": {"questions": {"assign": ["How to match a user query?"]}}} -{"update": "id:open-p:paragraph::open/en/searching-multi-valued-fields.html-matching", "fields": {"questions": {"assign": ["What text matching options exist?", "What are the options for string fields?"]}}} -{"update": "id:open-p:paragraph::open/en/searching-multi-valued-fields.html-exploration", "fields": {"questions": {"assign": ["What are multi-valued string fields?"]}}} -{"update": "id:open-p:paragraph::open/en/searching-multi-valued-fields.html-a-minimal-vespa-application", "fields": {"questions": {"assign": ["What is a weightedset field type?", "How can we search tags?", "What is the match setting for string fields?"]}}} -{"update": "id:open-p:paragraph::open/en/searching-multi-valued-fields.html-starting-vespa", "fields": {"questions": {"assign": ["How to deploy Vespa using vespa-cli?", "What is Homebrew?"]}}} -{"update": "id:open-p:paragraph::open/en/searching-multi-valued-fields.html-query-our-data", "fields": {"questions": {"assign": ["What is query type all?", "What is query type any?", "How does Vespa match set elements?"]}}} -{"update": "id:open-p:paragraph::open/en/searching-multi-valued-fields.html-ranking", "fields": {"questions": {"assign": ["What is the output of matchfeatures?", "What is the elementSimilarity feature?", "How does elementCompleteness work?", "What are the examples of features for indexed multivalue string fields?"]}}} -{"update": "id:open-p:paragraph::open/en/searching-multi-valued-fields.html-conclusion", "fields": {"questions": {"assign": ["What is the query retrieval strategy?", "How to change ranking of multi-valued fields?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/securing-your-vespa-installation.html-", "fields": {"questions": {"assign": ["What are the security requirements for Vespa?", "How to secure Vespa with TLS?", "Why is it important to isolate Vespa hosts?", "How to lockdown directory permissions for Vespa?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/securing-your-vespa-installation.html-isolating-the-vespa-hosts", "fields": {"questions": {"assign": ["What is the risk of exposing Vespa?", "How to secure Vespa?", "What protocol should be used to secure inter-node communication?", "What are the consequences of not securing Vespa?", "How to limit Vespa hosts connections?", "What technologies can be used to secure Vespa?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/securing-your-vespa-installation.html-securing-the-application-container", "fields": {"questions": {"assign": ["How do you protect HTTP container?", "When should you restrict container port?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/securing-your-vespa-installation.html-locking-down-directory-permissions", "fields": {"questions": {"assign": ["Under which user do Vespa processes run?", "What is stored under $VESPA_HOME?", "What is the recommended security practice for sensitive data stored in application?", "How should private keys be protected in Vespa?", "Does Vespa have application-level support for encryption?", "How can encryption of on-disk document stores be implemented in Vespa?", "How is encryption typically implemented in Vespa?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/securing-your-vespa-installation.html-securing-vespa-with-mutually-authenticated-tls-(mtls)", "fields": {"questions": {"assign": ["How can I protect endpoints with mTLS?", "Where can I find documentation for mTLS?", "Is mTLS mutually authenticated?", "What is the multinode-HA example?", "How do I get started with mTLS?", "What protocols does mTLS protect?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/select-reference.html-", "fields": {"questions": {"assign": ["What is the SELECT parameter?", "How is SELECT parameter used?", "Can SELECT and QUERY be used together?", "What is YQL equivalent to SELECT?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/select-reference.html-where", "fields": {"questions": {"assign": ["What is the syntax for where queries?", "What is the difference with YQL?", "How do you write equivalent queries in JSON?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/select-reference.html-formal-structure", "fields": {"questions": {"assign": ["What is the query-tree?", "How is the query-tree built?", "What is the Vespa query-tree?", "How is YQL parsed in Vespa?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/select-reference.html-grouping", "fields": {"questions": {"assign": ["What is a grouping statement?", "How is grouping represented as JSON?", "What is the structure of a JSON grouping statement?", "What is the format of a JSON grouping function?", "What are the types of arguments that can be used in grouping?", "How is bucketing used in a JSON grouping statement?", "What is the purpose of the 'all' keyword in a JSON grouping statement?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/select-reference.html-numeric-operators", "fields": {"questions": {"assign": ["What are the parameters accepted by the range query?", "How is the range query formatted in JSON?", "What does the greater-than or equal to (\u2265) operator do?", "What does the greater-than (>) operator do?", "What does the less-than (<) operator do?", "What does the less-than or equal to (\u2264) operator do?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/select-reference.html-and-not", "fields": {"questions": {"assign": ["What is 'and_not' in YQL?", "What does 'title contains' do in YQL?", "What is the formal structure of YQL?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/select-reference.html-phrase", "fields": {"questions": {"assign": ["What does 'phrase' mean in YQL?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/select-reference.html-near-and-ordered-near", "fields": {"questions": {"assign": ["What is 'onear'?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/select-reference.html-search-within-same-struct-element", "fields": {"questions": {"assign": ["What is sameElement?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/select-reference.html-term-equivalence", "fields": {"questions": {"assign": ["What is equiv() in YQL?", "What is fieldName in YQL?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/select-reference.html-dotproduct", "fields": {"questions": {"assign": ["What is dotProduct?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/select-reference.html-weightedset", "fields": {"questions": {"assign": ["What is a weightedSet?", "What is the format for weightedSet in YQL?", "What is the meaning of {'a':1, 'b':2} in weightedSet?", "How does YQL handle weightedSet queries?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/select-reference.html-weakand", "fields": {"questions": {"assign": ["What is weakAnd?", "What does 'scoreThreshold' mean?", "What is the purpose of 'targetHits'?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/select-reference.html-predicate", "fields": {"questions": {"assign": ["What is a predicate field?"]}}} -{"update": "id:open-p:paragraph::open/en/semantic-qa-retrieval.html-", "fields": {"questions": {"assign": ["What is the semantic-qa-retrieval sample application?", "What is the advantage of Vespa's hybrid retrieval?"]}}} -{"update": "id:open-p:paragraph::open/en/semantic-qa-retrieval.html-about-google's-universal-sentence-encoder", "fields": {"questions": {"assign": ["What is the Universal Sentence Encoder?", "What tasks can the Universal Sentence Encoder be used for?", "How does the Universal Sentence Encoder work?", "What is the Multilingual Universal Sentence Encoder for Question-Answer Retrieval?", "How can Vespa be used with the Universal Sentence Encoder?", "What is a dense tensor representation?", "What is dense Passage Retrieval?"]}}} -{"update": "id:open-p:paragraph::open/en/semantic-qa-retrieval.html-about-the-squad-dataset", "fields": {"questions": {"assign": ["What is SQuAD dataset?", "What is SQuAD-explorer?", "What is Stanford Question Answering Dataset?", "What is the purpose of SQuAD?", "What is the format of SQuAD?", "How many questions are in SQuAD v1.1?", "How many paragraphs are in SQuAD v1.1?", "How many sentences can SQuAD's paragraphs be segmented into?"]}}} -{"update": "id:open-p:paragraph::open/en/semantic-qa-retrieval.html-squad-data-modelling-with-vespa", "fields": {"questions": {"assign": ["What is the SQuaD dataset?", "What fields does the context document type have?", "What is the sentence document type?", "What distance metric does the sentence document type use?", "Where can I find details on the HNSW index settings and distance metric?"]}}} -{"update": "id:open-p:paragraph::open/en/semantic-qa-retrieval.html-converting-the-squad-json-to-vespa-json-feed-format", "fields": {"questions": {"assign": ["What is the SQuAD data?", "What is the Vespa json format?", "What is a sentence tokenizer?", "How many context documents are in the content db?"]}}} -{"update": "id:open-p:paragraph::open/en/semantic-qa-retrieval.html-sentence-and-paragraph-retrieval", "fields": {"questions": {"assign": ["What is the ReQA task about?", "How do we retrieve paragraphs?", "How do we represent paragraph level scores?"]}}} -{"update": "id:open-p:paragraph::open/en/semantic-qa-retrieval.html-", "fields": {"questions": {"assign": ["What is hybrid retrieval?", "What is dense retrieval?", "What is sparse retrieval?", "How do you combine dense and sparse retrieval?", "What is the linear combination in bm25-sentence-semantic-similarity profile?", "How does Vespa support hybrid retrieval?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/semantic-rules.html-", "fields": {"questions": {"assign": ["What is the semantic rule language?", "Where can I find a guide to using the language?", "What is query rewriting in relation to the language?", "Which API should I refer to when using semantic rules in queries?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/semantic-rules.html-rule-bases", "fields": {"questions": {"assign": ["What are semantic rules?", "What are rule bases?", "Where are rule bases placed?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/semantic-rules.html-basic-syntax", "fields": {"questions": {"assign": ["What is a rule base?", "What are the constructs in a rule base?", "What are statements in a rule base?", "What are production rules?", "What are named conditions?", "How are comments formed in a rule base?", "What is the syntax for terminating a statement?", "Can statements span multiple lines?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/semantic-rules.html-directives", "fields": {"questions": {"assign": ["What is a directive?", "What is the syntax for a statement in directive?", "What is the usage of @include?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/semantic-rules.html-production-rules", "fields": {"questions": {"assign": ["What is a production rule?", "What are the two rule kinds?", "What is the syntax of a production rule?", "What does the replacing operator do?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/semantic-rules.html-namespaces", "fields": {"questions": {"assign": ["What is a namespace?", "What is the default namespace?", "What is a positional namespace?", "How do you determine the namespace?", "What is the Query namespace?", "What is the Parameter namespace?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/semantic-rules.html-named-conditions", "fields": {"questions": {"assign": ["What is a named condition?", "How do you create a named condition?", "What is the format of a named condition?", "What is the purpose of a named condition?", "How can a named condition be used?", "What is the syntax for defining a named condition?", "What is the significance of assigning a name to a condition?", "How is a named condition referred in other conditions and rules?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/semantic-rules.html-conditions", "fields": {"questions": {"assign": ["What is a condition?", "What does a condition evaluate?", "What is a positional namespace?", "What is a comparison condition?", "What is a label in a condition?", "What does a condition return?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/semantic-rules.html-reference-name", "fields": {"questions": {"assign": ["What is a reference name?", "Why is reference name useful?", "How can a reference name be set?", "What happens if no reference name is given?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/semantic-rules.html-label", "fields": {"questions": {"assign": ["What is label in Vespa?", "How does a specified label affect term matching?", "What is the index of a label?", "How does not setting a label affect term matching?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/semantic-rules.html-condition", "fields": {"questions": {"assign": ["What are the supported kinds of conditions?", "What is a Term condition?", "What is a Reference condition?", "What is a Sequence condition?", "What is a Choice condition?", "What is a Group condition?", "What is a Comparison condition?", "What is a Literal condition?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/semantic-rules.html-comparison-condition-operators", "fields": {"questions": {"assign": ["What are the comparison operators?", "What does the operator =~ mean?", "What is the meaning of operator ` element?", "Can you have multiple Configuration Server instances?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/services-admin.html-logserver", "fields": {"questions": {"assign": ["What is the Log Server service?", "Where is the logserver placed?", "What optional values does the logserver have?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/services-admin.html-slobroks", "fields": {"questions": {"assign": ["What is a slobrok element?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/services-admin.html-slobrok", "fields": {"questions": {"assign": ["What is slobrok service?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/services-admin.html-monitoring", "fields": {"questions": {"assign": ["What is the default system name for monitoring service?", "What is the element tag for monitoring in Vespa?", "What attribute specifies the system name for monitoring service?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/services-admin.html-logging", "fields": {"questions": {"assign": ["What is the purpose of the logging package?", "How can you tune log levels?", "How do you enable debug logging?", "What levels can you modify?", "What is the default level?", "What happens to sub-packages when tuning levels?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/services-admin.html-metrics", "fields": {"questions": {"assign": ["What is the purpose of the 'consumer' element?", "What is the 'region' attribute used for in the 'cloudwatch' element?", "What is the 'namespace' attribute used for in the 'cloudwatch' element?", "What is the 'shared-credentials' element used for in the 'cloudwatch' element?", "What is the syntax for adding a custom metric to the 'consumer' element?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/services-admin.html-consumer", "fields": {"questions": {"assign": ["What is a metrics consumer?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/services-admin.html-metric-set", "fields": {"questions": {"assign": ["What is the default metric set?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/services-admin.html-metric", "fields": {"questions": {"assign": ["What is metric id?", "How to add metric to consumer?", "What is process metrics api?", "What is in the /state/v1/metrics endpoint?", "What is the format for custom metric ids?", "What metrics are included in the default set?", "What is the prometheus endpoint for custom metrics?", "What is a metric aggregate?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/services-admin.html-cloudwatch", "fields": {"questions": {"assign": ["What is CloudWatch?", "What metrics can be forwarded?", "What is the required region?", "What is the required namespace?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/services-admin.html-shared-credentials", "fields": {"questions": {"assign": ["What is the required attribute for CloudWatch authentication?", "What is the 'file' attribute in CloudWatch authentication?", "What is the default profile for CloudWatch authentication?", "What is the 'profile' attribute in CloudWatch authentication?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/services-container.html-", "fields": {"questions": {"assign": ["What is a container cluster?", "What is the purpose of the 'id' attribute in container element?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/services-container.html-handler", "fields": {"questions": {"assign": ["What is the handler element?", "What is a binding tag?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/services-container.html-server", "fields": {"questions": {"assign": ["What is the server element?", "What attributes does server element have?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/services-container.html-components", "fields": {"questions": {"assign": ["What are component elements?", "What is modular config?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/services-container.html-component", "fields": {"questions": {"assign": ["What is the purpose of the 'component' element?", "What is a 'nested component'?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/services-container.html-document-api", "fields": {"questions": {"assign": ["What is the default value for ?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/services-container.html-http-client-api", "fields": {"questions": {"assign": ["What is http-client-api?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/services-container.html-accesslog", "fields": {"questions": {"assign": ["What is the default accesslog type?", "How can access logging be disabled?", "What attribute is required when type is disabled?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/services-container.html-include", "fields": {"questions": {"assign": ["What does dir specify?", "What kind of files can be included?", "What is the outer tag restriction?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/services-container.html-nodes", "fields": {"questions": {"assign": ["What is the nodes element?", "What are node children?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/services-container.html-environment-variables", "fields": {"questions": {"assign": ["What are environment variables?", "How do you add children elements to nodes?", "When are environment variables available?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/services-container.html-jvm", "fields": {"questions": {"assign": ["What are JVM settings?", "What are gc-options?", "What is the default allocated-memory value?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/services-container.html-group", "fields": {"questions": {"assign": ["What is 'my-ckms'?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/services-container.html-zookeeper", "fields": {"questions": {"assign": ["What does the zookeeper element do?", "What can be configured with zookeeper?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/services-content.html-", "fields": {"questions": {"assign": ["What is 'coverage minimum' in search?", "What is the 'top-k-probability' in tuning?", "What is 'attribute-io-write' in engine?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/services-content.html-content", "fields": {"questions": {"assign": ["What is a content cluster?", "What is 'distributor-base-port' used for?", "What are the available subelements of a content cluster?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/services-content.html-documents", "fields": {"questions": {"assign": ["What is the content cluster for?", "What is the optional document-processing for?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/services-content.html-document-processing", "fields": {"questions": {"assign": ["What is document preprocessing?", "What is documentprocessor?", "What is before=indexingStart?", "What is after=indexingEnd?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/services-content.html-min-redundancy", "fields": {"questions": {"assign": ["What is 'minimum total data'?", "What is redundancy?", "When is 'minimum total data' used?", "How does 'minimum total data' ensure data copies?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/services-content.html-redundancy", "fields": {"questions": {"assign": ["What is redundancy?", "How does redundancy work in Vespa?", "Can redundancy be changed without node restart?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/services-content.html-group", "fields": {"questions": {"assign": ["What is a group in Vespa?", "What is group distribution key?", "What's the difference between Vespa Cloud and Open Source Vespa when using groups?", "What is the purpose of the 'name' attribute in a group?", "What is the maximum replica number when using groups in Vespa Cloud?", "Can a group contain both subgroups and nodes?", "What are the benefits of using grouped distribution in Vespa?", "Where can I find examples of Vespa deployments using groups?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/services-content.html-distribution-(in-group)", "fields": {"questions": {"assign": ["When is distribution mandatory?", "What is the partition specification?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/services-content.html-engine", "fields": {"questions": {"assign": ["What is the content engine?", "What are the allowed engines?", "What is the sub-element?", "How do you specify the content engine?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/services-content.html-proton", "fields": {"questions": {"assign": ["What is Proton engine?", "What are the optional sub-elements?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/services-content.html-searchable-copies", "fields": {"questions": {"assign": ["What is searchable-copies?", "When to consider fast-access?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/services-content.html-requestthreads", "fields": {"questions": {"assign": ["What is persearch?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/services-content.html-flushstrategy", "fields": {"questions": {"assign": ["What is flushstrategy?", "What is memory_limit-factor?", "What is diskbloatfactor?", "What is transactionlog.maxsize?", "What is the flushstrategy sub element?", "What is downscaled in flushstrategy?", "What is the maximum memory gain in flushstrategy?", "What is the max flush age in flushstrategy?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/services-content.html-initialize", "fields": {"questions": {"assign": ["What is searchnode?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/services-content.html-feeding", "fields": {"questions": {"assign": ["What is the default concurrency value in feeding?", "What is feeding.concurrency?", "How does 'concurrency' affect thread pools in feed operations?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/services-content.html-index", "fields": {"questions": {"assign": ["What is io write?", "What is io read?", "What is the purpose of warmup?", "How can warmup be controlled?", "What are the values for io search?", "What is the default for io read?", "What is the default for unpack?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/services-content.html-removed-db", "fields": {"questions": {"assign": ["What is the purpose of 'removed-db'?", "What is 'prune' in 'removed-db'?", "What is 'age' in 'prune'?", "What is the default value of 'age'?", "Why is 'age' set this way?", "What is 'interval' in 'prune'?", "What is the default value of 'interval'?", "Where can 'removed-db' be contained in?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/services-content.html-summary", "fields": {"questions": {"assign": ["What are the options for io write?", "What are the options for io read?", "What is summary store?", "What is summary cache?", "What is document logstore?", "What is the maximum size of a chunk in document logstore?", "What is the compression level of documents in document logstore?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/services-content.html-flush-on-shutdown", "fields": {"questions": {"assign": ["What is flushed before shutdown?", "How does flushing reduce replay time?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/services-content.html-sync-transactionlog", "fields": {"questions": {"assign": ["What is transactionlog?", "What are the advantages of syncing transactionlog to disk?", "What is the impact of using NAS?", "What is EBS?", "How much improvement was seen by turning off sync-transactionlog for EBS?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/services-content.html-search", "fields": {"questions": {"assign": ["What are sub-elements?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/services-content.html-query-timeout", "fields": {"questions": {"assign": ["What is the default query timeout?", "What is the max query timeout?", "What is the search interface?", "Where can I find the request parameter for query timeout?", "What is the unit of the max timeout value?", "What happens when the query timeout is exceeded?", "What is the purpose of the query timeout?", "What is the timeout in seconds for queries against search interface?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/services-content.html-visibility-delay", "fields": {"questions": {"assign": ["What is the TTL caching control?", "How does search affect TTL?", "What is the max TTL?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/services-content.html-coverage", "fields": {"questions": {"assign": ["What is search coverage configuration?", "What are the optional sub-elements?", "How does search coverage affect search performance?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/services-content.html-minimum", "fields": {"questions": {"assign": ["What is minimum search coverage?", "What is the default coverage value?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/services-content.html-min-wait-after-coverage-factor", "fields": {"questions": {"assign": ["What is coverage?", "What is minimum coverage?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/services-content.html-max-wait-after-coverage-factor", "fields": {"questions": {"assign": ["What is coverage?", "What is the default coverage factor?", "What happens after reaching minimum coverage?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/services-content.html-tuning", "fields": {"questions": {"assign": ["What is bucket-splitting?", "What are maintenance tuning parameters?", "What is dispatch tuning?", "What are the optional tuning parameters?", "What is distribution_type?", "What is persistence-threads?", "What are the tuning parameters for visitors?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/services-content.html-bucket-splitting", "fields": {"questions": {"assign": ["How are buckets split?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/services-content.html-min-node-ratio-per-group", "fields": {"questions": {"assign": ["What is min-node-ratio-per-group?", "What does the tuning parameter allow?", "How does Vespa handle failing nodes in a group?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/services-content.html-maintenance", "fields": {"questions": {"assign": ["What is bucket maintenance?", "How do you control bucket maintenance running time?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/services-content.html-merges", "fields": {"questions": {"assign": ["What are throttling parameters?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/services-content.html-persistence-threads", "fields": {"questions": {"assign": ["How many threads are used by default?", "What is a persistence thread?", "How are bucket operations executed?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/services-content.html-visitors", "fields": {"questions": {"assign": ["What is max-concurrent?", "What is thread-count?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/services-content.html-max-concurrent", "fields": {"questions": {"assign": ["What is the maximum active count?", "What is the default fixed component?", "What is the default variable component?", "What is the purpose of visitors?", "What is the role of priority?", "How is the maximum active calculated?", "What are the attribute values for visitors?", "What is the allowed number of visitors?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/services-content.html-resource-limits", "fields": {"questions": {"assign": ["What are resource limits?", "What is the default disk value?", "What happens when the disk limit is reached?", "What happens when the memory limit is reached?", "What should be considered when tuning limits?", "What is the risk of setting limits too high?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/services-content.html-dispatch", "fields": {"questions": {"assign": ["What is max-hits-per-partition?", "What is top-k-probability?", "What is dispatch-policy?", "What is min-active-docs-coverage?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/services-content.html-cluster-controller", "fields": {"questions": {"assign": ["What are tuning parameters?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/services-docproc.html-", "fields": {"questions": {"assign": ["What is numnodesperclient?", "What is maxqueuewait?", "What is maxqueuebytesize?", "What is containercorememory?", "What is documentexpansionfactor?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/services-docproc.html-document-processor-elements", "fields": {"questions": {"assign": ["What is a documentprocessor?", "What is a docproc chain?", "What is a document processor reference?", "What is an inner document processor?", "What is an outer document processor?", "What are the optional sub-elements of a documentprocessor element?", "What are the attributes of a documentprocessor element?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/services-docproc.html-documentprocessor", "fields": {"questions": {"assign": ["What is documentprocessor?", "What are documentprocessor attributes?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/services-docproc.html-docproc-chain-elements", "fields": {"questions": {"assign": ["What is a docproc chain?", "How are document processors ordered?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/services-docproc.html-chain", "fields": {"questions": {"assign": ["What is a chain reference?", "How does chain inherit document processors?", "What is a documentprocessor reference?", "What is a documentprocessor definition?", "What is a phase in chain processing?", "What is a config in documentprocessor chain?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/services-docproc.html-map", "fields": {"questions": {"assign": ["What is field name mapping?", "How does field name mapping work?", "What is the purpose of field name mapping?", "What does the field mapping example show?", "How is mapping specified in Vespa?", "What happens if you specify mappings at different levels?", "What is the syntax for field name mapping?", "What is the dotted notation used for in mapping?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/services-http.html-", "fields": {"questions": {"assign": ["What is the http block?", "What is the purpose of the http block?", "What is the `http` subelement of container?", "How can you configure http servers and filters with the http block?", "What happens when the http block is present?", "What are the elements that the http block takes?", "What is the purpose of the request-chain and response-chain elements?", "What is the purpose of the binding element inside request-/response-chain?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/services-http.html-server", "fields": {"questions": {"assign": ["What is a http server?", "How to configure a server?", "What is jdisc.http.connector.def?", "What are the attributes of jdisc.http.connector.def?", "What is the purpose of 'id' attribute in jdisc.http.connector.def?", "What is the purpose of 'port' attribute in jdisc.http.connector.def?", "What is the purpose of 'default-request-chain' attribute in jdisc.http.connector.def?", "What is the purpose of 'default-response-chain' attribute in jdisc.http.connector.def?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/services-http.html-ssl", "fields": {"questions": {"assign": ["What is PEM format?", "How to setup TSL?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/services-http.html-private-key-file", "fields": {"questions": {"assign": ["What is the format for private keys?", "What is the path for PEM private key?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/services-http.html-certificate-file", "fields": {"questions": {"assign": ["What is the format of the certificate file?", "What is the path of the certificate file?", "How to access PEM formatted certificates?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/services-http.html-ca-certificates-file", "fields": {"questions": {"assign": ["What is a PEM file?", "What is a CA certificate?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/services-http.html-client-authentication", "fields": {"questions": {"assign": ["What are supported authentication values?", "What does *need* authentication mean?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/services-http.html-protocols", "fields": {"questions": {"assign": ["What is TLS protocol?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/services-http.html-cipher-suites", "fields": {"questions": {"assign": ["What is a TLS cipher suite?", "How to enable TLS cipher suites?", "What is an example of a TLS cipher suite?", "What must be supported by JDK installation?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/services-http.html-ssl-provider", "fields": {"questions": {"assign": ["What is SslProvider?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/services-http.html-filtering", "fields": {"questions": {"assign": ["What is filtering?", "What is response-chain used for?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/services-http.html-binding", "fields": {"questions": {"assign": ["What is a URI pattern?", "How do you specify a URI pattern?", "What happens to requests matching a URI pattern?", "What happens to responses matching a URI pattern?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/services-http.html-filter", "fields": {"questions": {"assign": ["What are the supported filter types?", "How do you refer to a single filter?", "What is the purpose of defining a filter under `http`?", "What are the required sub-elements for a filter?", "What is the purpose of `Security[Request/Response]Filters`?", "What is the format for defining a filter in XML?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/services-http.html-provides", "fields": {"questions": {"assign": ["What is a dependency in filters?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/services-http.html-before", "fields": {"questions": {"assign": ["What is the purpose of `before` tags?", "What is the syntax for `before` tags?", "How are filters or phases defined?", "What is the tag used for `before`?", "What does the `before` tag do?", "Where is `before` tag used?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/services-http.html-after", "fields": {"questions": {"assign": ["What are 'after' tags used for?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/services-http.html-filter-config", "fields": {"questions": {"assign": ["What is FilterConfig?", "What filters can I configure?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/services-http.html-request-chain/response-chain", "fields": {"questions": {"assign": ["What is a chain?", "What are dependencies expressed by?", "What is a response-chain?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/services-http.html-inherits", "fields": {"questions": {"assign": ["What is inherited chain?", "What is exclude element?", "What does inherited element contain?", "What does exclude element contain?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/services-http.html-(inherited)-chain", "fields": {"questions": {"assign": ["What is the 'inherits' parameter?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/services-http.html-phase", "fields": {"questions": {"assign": ["What is a phase?", "What is a checkpoint?", "What is the purpose of a phase?", "What can depend on a phase?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/services-processing.html-", "fields": {"questions": {"assign": ["What is processing subelement?", "What is the syntax of processing?", "What is the semantics of processing?", "What is a pure processing application?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/services-processing.html-binding", "fields": {"questions": {"assign": ["What is the default binding for ProcessingHandler?", "Example of ProcessingHandler binding?", "Are multiple bindings allowed for ProcessingHandler?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/services-processing.html-processor", "fields": {"questions": {"assign": ["What is a single processor?", "What is idref used for?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/services-processing.html-renderer", "fields": {"questions": {"assign": ["What is a renderer?", "What does the renderer tag look like?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/services-processing.html-processor-(in-chain)", "fields": {"questions": {"assign": ["What is a processor chain?", "What is the idref attribute?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/services-processing.html-chain", "fields": {"questions": {"assign": ["What is a chain?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/services-processing.html-exclude", "fields": {"questions": {"assign": ["What is the purpose of exclude in inherits?", "How does exclude work in inherited chains?", "What is an example of exclude in inherited chains?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/services-processing.html-phase", "fields": {"questions": {"assign": ["What is a phase?", "What is a checkpoint?", "How to define a phase?", "What is the purpose of before and after?", "What is the ID of a phase?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/services-processing.html-before", "fields": {"questions": {"assign": ["What does the 'before' element do?", "How does the 'before' element affect component order?", "Can multiple 'before' elements be used?", "What is the purpose of defining a component 'before' another?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/services-processing.html-after", "fields": {"questions": {"assign": ["What is the purpose of 'after' elements?", "How can multiple 'after' elements be used?", "What is a phase/component?", "What can be defined using 'after' elements?", "How does 'after' define component order?", "What is the structure of a 'after' element?", "What is the relationship between 'after' elements and component order?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/services-search.html-binding", "fields": {"questions": {"assign": ["What is the default binding for SearchHandler?", "What is the URI for SearchHandler binding?", "Can you provide an example of a SearchHandler binding element?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/services-search.html-searcher", "fields": {"questions": {"assign": ["What is a searcher element?", "What is a searcher definition?", "What is a searcher reference?", "What are the attributes of a searcher definition?", "What is the difference between an inner and outer searcher?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/services-search.html-federation", "fields": {"questions": {"assign": ["What is a federation?", "What is a target-selector?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/services-search.html-target-selector", "fields": {"questions": {"assign": ["What is TargetSelector?", "What must a component inherit from to select search chains in Vespa?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/services-search.html-source-set", "fields": {"questions": {"assign": ["What is 'combinator'?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/services-search.html-source", "fields": {"questions": {"assign": ["What is source idref?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/services-search.html-federationoptions", "fields": {"questions": {"assign": ["What are federation options?", "What is the default option for optional?", "What is the purpose of requestTimeout?", "What happens when a federation option overrides another?", "How does a source reference affect federation options?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/services-search.html-renderer", "fields": {"questions": {"assign": ["What is a search result renderer?", "What is the required attribute for search result renderer?", "What is the optional attribute for search result renderer?", "What is the bundle attribute for search result renderer?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/services-search.html-chain", "fields": {"questions": {"assign": ["What is a search chain?", "How can searchers be ordered?", "What are search chains?", "What are sub-elements of a chain?", "Can search config be in separate files?", "What is a searcher definition?", "Can searcher elements have the same name?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/services-search.html-provider", "fields": {"questions": {"assign": ["What is a provider?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/services-search.html-local-provider", "fields": {"questions": {"assign": ["What are local providers?", "What is a local Vespa cluster?", "How do you define a local provider?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/services-search.html-http-provider", "fields": {"questions": {"assign": ["What is an http provider?", "What are http providers used for?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/services-search.html-source", "fields": {"questions": {"assign": ["What is a source search chain?", "What is a leader search chain?", "What is a participant search chain?", "How can a participant search chain be used?", "How does a source search chain behave?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/services-search.html-nodes", "fields": {"questions": {"assign": ["What are node elements?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/services-search.html-threadpool", "fields": {"questions": {"assign": ["How is the threadpool configured?", "What happens when the request queue is full?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/services-search.html-max-threads", "fields": {"questions": {"assign": ["What is the maximum thread count?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/services-search.html-min-threads", "fields": {"questions": {"assign": ["What is the thread pool?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/services-search.html-queue-size", "fields": {"questions": {"assign": ["What is request queue size?", "How to configure request queue size?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/services.html-", "fields": {"questions": {"assign": ["What is services.xml?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/services.html-services", "fields": {"questions": {"assign": ["What is the required attribute for services?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/services.html-generic-configuration-using-", "fields": {"questions": {"assign": ["What is a config element?", "What is an example of generic configuration?", "What types of elements accept config?", "How are config elements prioritized?", "What does a config element override?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/services.html-modular-configuration", "fields": {"questions": {"assign": ["What files are used for configuration?", "What are the supported services?", "Where can XML files be found?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/simple-query-language-reference.html-", "fields": {"questions": {"assign": ["What is the simple query language?", "What types are available in simple query language?", "What is the difference between tokenize and web types?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/simple-query-language-reference.html-simple-query-syntax", "fields": {"questions": {"assign": ["What is Expr?", "What is SimpleTerm?", "What is Prefix?", "What is CoreTerm?", "What is NumTerm?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/simple-query-language-reference.html-prefix-searching", "fields": {"questions": {"assign": ["What is prefix matching?", "What is a prefix search term?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/simple-query-language-reference.html-term-weight", "fields": {"questions": {"assign": ["What is a weight expression?", "What is a term weight?", "What is the purpose of term scoring?", "How can weight be added to important terms?", "What is the default weight value?", "What is the rank calculation?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/simple-query-language-reference.html-numerical-terms", "fields": {"questions": {"assign": ["How can we use number in chess kasparov query?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/simple-query-language-reference.html-advanced-range-search", "fields": {"questions": {"assign": ["What is capped range search?", "How does capped range search work?", "What is the syntax for capped range search?", "How many documents are fetched using capped range search?", "What is fast-search?", "Which attribute is used for range search?", "Why is capped range search efficient?", "What does a negative number do in capped range search?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/simple-query-language-reference.html-grouping-in-the-simple-query-language", "fields": {"questions": {"assign": ["How many levels of parentheses?", "What happens with extra parentheses?", "How can '+' be excluded?", "When to use '-' with parentheses?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/simple-query-language-reference.html-search-in-urls", "fields": {"questions": {"assign": ["What is a URL-field?", "How to create a URL-field?", "What happens with invalid URLs?", "How does substring matching work?", "Is hostname search anchored by default?", "How to anchor hostname search at the start?", "How to turn off anchoring at the end?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/simple-query-language-reference.html-field-path-syntax", "fields": {"questions": {"assign": ["What is the field path syntax?", "What is the use case for map data types?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/simple-query-language-reference.html-removing-syntax-characters-from-queries", "fields": {"questions": {"assign": ["Why remove characters from search terms?", "What is a safe replacement character?", "How to quote URLs?", "How to handle parentheses in URLs?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/simple-query-language-reference.html-examples", "fields": {"questions": {"assign": ["What is the simple query language syntax?", "How can phrases be searched?", "What is ignored in noise?", "What is the syntax to get documents with a specific word?", "How can phrases and words be excluded?"]}}} -{"update": "id:open-p:paragraph::open/en/performance/sizing-examples.html-", "fields": {"questions": {"assign": ["When to use flat or grouped data distribution?", "What do the examples illustrate?", "How to get started from a deployable multinode starting point?", "Can you provide an example of hosts.xml for testing deployments?"]}}} -{"update": "id:open-p:paragraph::open/en/performance/sizing-examples.html-flat-distribution", "fields": {"questions": {"assign": ["What is min-redundancy?"]}}} -{"update": "id:open-p:paragraph::open/en/performance/sizing-examples.html-grouped-distribution", "fields": {"questions": {"assign": ["What are the restrictions of grouped distribution?", "What is min-node-ratio-per-group?", "What happens when a node fails in grouped distribution?"]}}} -{"update": "id:open-p:paragraph::open/en/performance/sizing-examples.html-9-nodes,-3-groups-with-3-nodes-per-group", "fields": {"questions": {"assign": ["How many nodes are there?", "What are the groups?"]}}} -{"update": "id:open-p:paragraph::open/en/performance/sizing-examples.html-9-nodes,-9-groups-with-1-node-per-group", "fields": {"questions": {"assign": ["How many replicas are there?"]}}} -{"update": "id:open-p:paragraph::open/en/performance/sizing-examples.html-serving-availability-tuning", "fields": {"questions": {"assign": ["What are soft failing nodes?", "What is adaptive coverage timeout?", "What is a dispatch policy?", "How are slow soft failing nodes prevented?", "What is the impact of soft failing on grouped distributions?", "What is the adaptive dispatch policy?"]}}} -{"update": "id:open-p:paragraph::open/en/performance/sizing-examples.html-changing-group-configuration", "fields": {"questions": {"assign": ["How do you change group topology?", "Is service disruption a concern?", "What are the caveats of changing topology?", "Where can I find more information about topology changes?"]}}} -{"update": "id:open-p:paragraph::open/en/performance/sizing-feeding.html-", "fields": {"questions": {"assign": ["What is Vespa optimized for?", "How does Vespa support feed rates?", "What is the stateless container cluster?", "How does NAS attached storage impact feed performance?"]}}} -{"update": "id:open-p:paragraph::open/en/performance/sizing-feeding.html-content-node", "fields": {"questions": {"assign": ["What does the guide cover?", "What does index structure flushing involve?"]}}} -{"update": "id:open-p:paragraph::open/en/performance/sizing-feeding.html-index", "fields": {"questions": {"assign": ["What is PUT?", "What is a partial update?", "What is the Index Manager?", "What is the difference between PUT and partial updates?"]}}} -{"update": "id:open-p:paragraph::open/en/performance/sizing-feeding.html-attribute", "fields": {"questions": {"assign": ["What are attribute fields?", "What is ticker schema?"]}}} -{"update": "id:open-p:paragraph::open/en/performance/sizing-feeding.html-redundancy-settings", "fields": {"questions": {"assign": ["How to achieve memory-only updates?", "What is redundancy?", "What is fast-access?", "How to ensure all attributes are ready?"]}}} -{"update": "id:open-p:paragraph::open/en/performance/sizing-feeding.html-debugging-performance", "fields": {"questions": {"assign": ["How to debug update performance?", "What is SummaryAdapter::put?", "How to enable spam log level?", "What does 'SummaryAdapter::put' log message indicate?"]}}} -{"update": "id:open-p:paragraph::open/en/performance/sizing-feeding.html-multivalue-attribute", "fields": {"questions": {"assign": ["What are multivalued attributes?", "How do multivalued fields impact write performance?", "What is a weightedset attribute?", "What is the impact of using numeric types with a weightedset attribute?", "What is the cost of updating array/map of struct/map attributes?", "How does updating tensor cell values impact memory?", "What is the recommended approach for using numeric types with a weightedset attribute?"]}}} -{"update": "id:open-p:paragraph::open/en/performance/sizing-feeding.html-parent/child", "fields": {"questions": {"assign": ["What are parent documents?", "What simplifies logic in parent documents?", "How does write performance depend on cluster size and child documents?"]}}} -{"update": "id:open-p:paragraph::open/en/performance/sizing-feeding.html-conditional-updates", "fields": {"questions": {"assign": ["What is a conditional update?", "How should conditions be evaluated?"]}}} -{"update": "id:open-p:paragraph::open/en/performance/sizing-feeding.html-client-roundtrips", "fields": {"questions": {"assign": ["What is the difference between two fields assignments?", "How can updates be put in as few operations as possible?"]}}} -{"update": "id:open-p:paragraph::open/en/performance/sizing-feeding.html-feed-vs.-search", "fields": {"questions": {"assign": ["What is CPU allocation?", "How can you allocate CPU resources for feeding?", "What does a higher concurrency value mean?", "What is the relation between CPU allocation and feeding in a content node?"]}}} -{"update": "id:open-p:paragraph::open/en/performance/sizing-feeding.html-feed-testing", "fields": {"questions": {"assign": ["How do you test feeding capacity?", "What is steady state?", "What is feeder instance?", "How do you troubleshoot feed testing?", "What are the different scenarios for feed testing?"]}}} -{"update": "id:open-p:paragraph::open/en/performance/sizing-search.html-data-distribution-in-vespa---flat-versus-grouped", "fields": {"questions": {"assign": ["How is data distributed to nodes?", "What is Vespa's consistency model?"]}}} -{"update": "id:open-p:paragraph::open/en/performance/sizing-search.html-flat-content-distribution", "fields": {"questions": {"assign": ["What is flat content distribution?", "How does flat distribution work?", "What is ideal state distribution algorithm?", "What is Little's law?", "How to scale query throughput?", "What is meant by active part?"]}}} -{"update": "id:open-p:paragraph::open/en/performance/sizing-search.html-grouped-content-distribution", "fields": {"questions": {"assign": ["What is a grouped distribution?", "How is content distributed?", "What is dispatch-policy?"]}}} -{"update": "id:open-p:paragraph::open/en/performance/sizing-search.html-high-data-availability", "fields": {"questions": {"assign": ["What is high availability?"]}}} -{"update": "id:open-p:paragraph::open/en/performance/sizing-search.html-content-node-database", "fields": {"questions": {"assign": ["What are the sub-databases inside a Vespa content node?", "What is the purpose of the Ready and Not Ready databases?", "What is the purpose of the Removed database?", "When is having searchable-copies=1 most optimal?", "What happens when searchable-copies=2 and redundancy=2?"]}}} -{"update": "id:open-p:paragraph::open/en/performance/sizing-search.html-life-of-a-query-in-vespa", "fields": {"questions": {"assign": ["How does Vespa execute queries?", "What are the components involved in query execution?", "How does Vespa scale horizontally?"]}}} -{"update": "id:open-p:paragraph::open/en/performance/sizing-search.html-content-cluster-scalability-model", "fields": {"questions": {"assign": ["What is Amdahl's law?", "How is query work divided in Vespa?", "What is dynamic query work?", "How does Little's Law apply to Vespa?", "How does scaling of static and dynamic query work affect throughput?", "How does the number of nodes affect Vespa performance?"]}}} -{"update": "id:open-p:paragraph::open/en/performance/sizing-search.html-scaling-latency-in-a-content-group", "fields": {"questions": {"assign": ["What is a latency SLA and how is it specified?", "What is sub-linear latency scaling?", "What are the performance characteristics of different use cases?", "What query operators have sub-linear scaling properties and what are the examples?", "What is the relationship between overall latency and the number of documents indexed per node?", "What is the sweet spot when using Vespa to reduce serving latency?", "What model or ranking features breach the targeted latency SLA?"]}}} -{"update": "id:open-p:paragraph::open/en/performance/sizing-search.html-", "fields": {"questions": {"assign": ["How can latency be reduced?", "What is persearch?", "What is the impact of using more threads per search?", "How should optimal threads per search be evaluated?", "What is the relationship between threads per search and CPU utilization?", "What type of search benefits from using more threads?", "What is intra-node per thread document space partitioning?"]}}} -{"update": "id:open-p:paragraph::open/en/performance/sizing-search.html-thread-configuration", "fields": {"questions": {"assign": ["What is `persearch` and its limitation?", "What should `summary` be set to?", "What is the default value for `persearch`?", "What happens if `persearch` is increased too much?", "How does `persearch` affect query latency?", "What is the relationship between `persearch` and efficiency?"]}}} -{"update": "id:open-p:paragraph::open/en/performance/sizing-search.html-scaling-document-volume-per-node", "fields": {"questions": {"assign": ["What are node constraints?", "What is the targeted search latency SLA?", "How can cost be reduced when fitting more documents into a node?", "What is larger fan-out?", "When is using adaptive timeout recommended?", "What happens if resource limits are reached during feed operations?"]}}} -{"update": "id:open-p:paragraph::open/en/performance/sizing-search.html-disk-usage-sizing", "fields": {"questions": {"assign": ["What factors affect disk usage?", "How to determine disk usage?", "What is index fusion?", "What is redundancy impact?"]}}} -{"update": "id:open-p:paragraph::open/en/performance/sizing-search.html-memory-usage-sizing", "fields": {"questions": {"assign": ["How does memory usage increase?", "What affects memory usage per document?", "What is vespa-proton-bin?", "How is virtual memory usage reported?", "What is mmap?", "What are the metrics for capacity planning?"]}}} -{"update": "id:open-p:paragraph::open/en/performance/sizing-search.html-scaling-throughput", "fields": {"questions": {"assign": ["What is static query work?", "How to scale throughput?", "What is throughput threshold?", "What causes latency to spike up?", "What happens when saturated?"]}}} -{"update": "id:open-p:paragraph::open/en/performance/sizing-search.html-scaling-for-failures-and-headroom", "fields": {"questions": {"assign": ["What is serving availability tuning?", "What is graceful degradation?", "What is system utilization?", "What is sizing write versus read?", "How important is measuring behaviour?", "How to verify feeding concurrency?"]}}} -{"update": "id:open-p:paragraph::open/en/performance/sizing-search.html-metrics-for-vespa-sizing", "fields": {"questions": {"assign": ["What are Vespa Metrics for?", "How can I calculate theoretical speedup?", "Which metrics are used to find number of matches per query?", "What is the disk usage of documentdb and transaction log?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/slobrok.html-", "fields": {"questions": {"assign": ["What is Slobrok?", "What is the purpose of Slobrok?", "Where is Slobrok hosted?", "How many instances of Slobrok are needed?", "What is the Slobrok process?", "Does Slobrok impact other services?", "How do clients do lookups on Slobrok?", "What uses Slobrok to evaluate service availability?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/sorting.html-", "fields": {"questions": {"assign": ["What is a sorting specification?", "How do you specify a sorting expression?", "What is multilevel sorting?", "How do you sort in Vespa?", "What is a SortAttribute?", "What are the options for SortOrder?", "What is a Function in sorting?", "What are the sorting references in YQL Vespa?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/sorting.html-sort-order", "fields": {"questions": {"assign": ["What does + denote in sorting?", "What does - denote in sorting?", "How are numerical attributes sorted?", "How are strings sorted?", "How should + be encoded in query URLs?", "How should - be encoded in query URLs?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/sorting.html-default-sort-order", "fields": {"questions": {"assign": ["What is the default sort order?", "What is the default for `+`/`-`?", "What is the special builtin for descending sort?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/sorting.html-sort-attribute", "fields": {"questions": {"assign": ["What is the sorting attribute?", "What is the recommended rank profile when sorting?", "Why is unranked recommended for sorting?", "What is an attribute in Vespa?", "How does Vespa execute sorting?", "What is the indexing language fragment for an attribute?", "Why is using unranked important for execution speed when sorting?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/sorting.html-special-sorting-attributes", "fields": {"questions": {"assign": ["What are the 3 special attributes?", "How are special attributes useful in sorting?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/sorting.html-attribute-only", "fields": {"questions": {"assign": ["What can you sort on?", "What happens when sorting multivalued?", "What is match-phase enabled for?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/sorting.html-optimization-causing-incorrect-total-hit-count", "fields": {"questions": {"assign": ["What is fast-search on numeric attributes?", "How does Vespa optimize sorting results?", "How to disable sorting degrading?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/sorting.html-examples", "fields": {"questions": {"assign": ["What is uca in sorting?", "What is the syntax for sorting by surname?", "What does the syntax for sorting by surname with youngest ones first look like?", "How is sorting prioritized within equal yearofbirth groups?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/state-v1.html-", "fields": {"questions": {"assign": ["What is the /state/v1 API?", "What is vespa-model-inspect?", "What is the multinode-HA sample app?", "Where can I find examples of using /state/v1 API?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/state-v1.html-http-status-codes", "fields": {"questions": {"assign": ["What does code 200 mean?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/state-v1.html-response-format", "fields": {"questions": {"assign": ["What is the JSON response format?"]}}} -{"update": "id:open-p:paragraph::open/en/stateless-model-evaluation.html-", "fields": {"questions": {"assign": ["What machine-learned models does Vespa support?"]}}} -{"update": "id:open-p:paragraph::open/en/stateless-model-evaluation.html-the-model-evaluation-tag", "fields": {"questions": {"assign": ["How to enable REST API and Java API?", "Where should model-evaluation tag be added?", "What is the purpose of model-evaluation tag?", "What are inference session options for ONNX models?", "What are ONNX inference options?"]}}} -{"update": "id:open-p:paragraph::open/en/stateless-model-evaluation.html-model-inference-using-the-rest-api", "fields": {"questions": {"assign": ["What is the REST API path for model evaluation?", "How do you evaluate a model using the REST API?", "What are expected input parameters for model evaluation?", "What format should input parameters be in for model evaluation?", "Where can you find information about models in your application package?", "Can you deploy multiple models in your application package?", "What does specify when evaluating a model?", "What is the expected format for input parameters for evaluating a model?"]}}} -{"update": "id:open-p:paragraph::open/en/stateless-model-evaluation.html-model-inference-using-java", "fields": {"questions": {"assign": ["What is ModelsEvaluator?", "How do you bind inputs?", "What does Java interface offer?", "How do you evaluate a model?", "What is the model-inference sample app?", "What is FunctionEvaluator?"]}}} -{"update": "id:open-p:paragraph::open/en/stateless-model-evaluation.html-unit-testing-model-evaluation-in-java", "fields": {"questions": {"assign": ["What is ModelsEvaluatorTester?", "How to create a testable ModelsEvaluator?", "What does ModelsEvaluator object contain?"]}}} -{"update": "id:open-p:paragraph::open/en/stateless-model-evaluation.html-onnx-inference-options", "fields": {"questions": {"assign": ["What is ONNX?", "What is ONNX Runtime?", "How do you tune inference?", "What is ``?", "What is the purpose of intraop-threads?", "What is the purpose of gpu-device?", "What is execution-mode?", "What is the purpose of model-evaluation?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/stateless-model-reference.html-", "fields": {"questions": {"assign": ["What are *.model* files?", "Where are *.model* files located?", "What is stateless model evaluation?", "What is the suffix for model files?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/stateless-model-reference.html-constant-element", "fields": {"questions": {"assign": ["What is a constant in Vespa?", "What can be the type of a constant?", "What is the format of a tensor constant?", "Can a tensor constant be stored in a file?", "Can a stored tensor constant be compressed?", "What is the maximum size of a tensor constant in Vespa?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/stateless-model-reference.html-model-example", "fields": {"questions": {"assign": ["What is the directory tree?", "What inputs must be declared?", "What are the functions available in the model?", "What does foo1 expression do?"]}}} -{"update": "id:open-p:paragraph::open/en/streaming-search.html-", "fields": {"questions": {"assign": ["What is streaming search?", "How does streaming search work?", "When to use streaming search?", "What are the benefits of streaming search?", "What are the limitations of streaming search?", "Which features are not supported by streaming search?", "How does Vespa handle subsets in streaming search?", "What is the difference between indexed and streaming search?"]}}} -{"update": "id:open-p:paragraph::open/en/streaming-search.html-using-streaming-search", "fields": {"questions": {"assign": ["What is streaming search?", "What are the steps to use streaming search?", "Where can I find sample apps for streaming search?"]}}} -{"update": "id:open-p:paragraph::open/en/streaming-search.html-match", "fields": {"questions": {"assign": ["What is match configuration?", "How to do substring matching?", "How to search a field exactly?"]}}} -{"update": "id:open-p:paragraph::open/en/streaming-search.html-vespa-get-config--n-vespa.config.search.vsm.vsmfields--i-music/search/cluster.music.music-|-egrep-'name|arg1'", "fields": {"questions": {"assign": ["What is fieldspec?", "What is the value of arg1?"]}}} -{"update": "id:open-p:paragraph::open/en/streaming-search.html-disk-sizing", "fields": {"questions": {"assign": ["What is the disk sizing for streaming search?", "What determines the disk size for streaming search?", "How do you estimate the disk used for streaming search?", "What is the relationship between disk size and number of documents in streaming search?"]}}} -{"update": "id:open-p:paragraph::open/en/streaming-search.html-memory-sizing", "fields": {"questions": {"assign": ["What is the document meta store?", "How much memory does streaming search require?"]}}} -{"update": "id:open-p:paragraph::open/en/streaming-search.html-streaming-search-query-tuning", "fields": {"questions": {"assign": ["What is persistence-threads?"]}}} -{"update": "id:open-p:paragraph::open/en/streaming-search.html-summary-store:-direct-io-and-cache", "fields": {"questions": {"assign": ["What is direct IO?", "When should direct IO be used?", "What is the summary cache?", "How much memory is set aside for summary cache?"]}}} -{"update": "id:open-p:paragraph::open/en/streaming-search.html-searchable-copies", "fields": {"questions": {"assign": ["What are searchable and ready copies in Vespa?", "How can I disable replicas transfer during streaming search?", "What happens when redundancy = searchable copies?"]}}} -{"update": "id:open-p:paragraph::open/en/streaming-search.html-grouping", "fields": {"questions": {"assign": ["How does streaming search work?", "What is the difference in grouping in streaming and indexed search?", "What is the use of 'where(true)' in Vespa?"]}}} -{"update": "id:open-p:paragraph::open/en/streaming-search.html-docidnsspecific", "fields": {"questions": {"assign": ["What does docidnsspecific return?"]}}} -{"update": "id:open-p:paragraph::open/en/streaming-search.html-routing", "fields": {"questions": {"assign": ["What is the routing configuration for indexed search?", "What is the routing configuration for streaming search?", "What is the routing for the msuic index with indexed search?", "What is the routing for the msuic index with streaming search?"]}}} -{"update": "id:open-p:paragraph::open/en/streaming-search.html-linguistics", "fields": {"questions": {"assign": ["What is streaming search?", "What is not used in streaming search?", "What is stemming not applicable to?"]}}} -{"update": "id:open-p:paragraph::open/en/streaming-search.html-query-api-reference", "fields": {"questions": {"assign": ["What features apply to streaming?"]}}} -{"update": "id:open-p:paragraph::open/en/streaming-search.html-streaming.userid", "fields": {"questions": {"assign": ["What is the purpose of n= modifier?", "What is the default value for Alias?", "What is the purpose of streaming search?", "How does n= modifier help in grouping documents?", "What is the range of the integer datatype?", "What is the format of userid in streaming search?"]}}} -{"update": "id:open-p:paragraph::open/en/streaming-search.html-streaming.groupname", "fields": {"questions": {"assign": ["What is streaming search?", "What is the purpose of g= modifier?", "How is g= used?", "Can g= be used for string grouping?", "What kind of documents can streaming search stream through?", "What is the effect of using g= modifier on streaming search?"]}}} -{"update": "id:open-p:paragraph::open/en/streaming-search.html-streaming.selection", "fields": {"questions": {"assign": ["What is document selection?", "What is an advanced expression?", "Can Alias be used with document selection?", "What is the purpose of restricting streaming search?", "How does document selection work with streaming search?"]}}} -{"update": "id:open-p:paragraph::open/en/streaming-search.html-streaming.maxbucketspervisitor", "fields": {"questions": {"assign": ["How can ordering reduce visiting time?"]}}} -{"update": "id:open-p:paragraph::open/en/streaming-search.html-query-language-reference", "fields": {"questions": {"assign": ["What is substring matching?", "Is substring matching supported?"]}}} -{"update": "id:open-p:paragraph::open/en/streaming-search.html-visiting-the-document-store", "fields": {"questions": {"assign": ["What is bucket localization?", "How are document IDs generated for a bucket?", "How much resource does generating a set of document IDs for a bucket take?", "What is a property on a document's ID?"]}}} -{"update": "id:open-p:paragraph::open/en/streaming-search.html-schema", "fields": {"questions": {"assign": ["What is a valid streaming search?", "How do you create struct fields?", "How should struct fields be stored and indexed?"]}}} -{"update": "id:open-p:paragraph::open/en/streaming-search.html-notes", "fields": {"questions": {"assign": ["What is searchable-copies?", "Does streaming search use index structures?", "What rank feature is not supported in streaming search?", "What is the second phase ranking in streaming search?", "Are references and imported fields supported in streaming mode?", "What is PR #1829 about?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/string-segment-match.html-", "fields": {"questions": {"assign": ["What is the string segment match algorithm?", "What are string segment match metrics?", "What is the purpose of the string segment match algorithm?", "How does the string segment match algorithm work?", "What is the format of typical queries for the string segment match algorithm?", "How does the algorithm determine the best segmentation?", "What is the role of token proximity in the string segment match algorithm?", "What other information is used to improve the matching of queries in the string segment match algorithm?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/string-segment-match.html-source-information", "fields": {"questions": {"assign": ["How does this algorithm calculate metrics?", "What is the linguistic connectivity?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/string-segment-match.html-algorithm", "fields": {"questions": {"assign": ["What is the algorithm metrics?", "What is a segment?", "How is a segment start point stored?", "What is the score deciding the best segmentation path?", "What metrics are used to calculate the segment score?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/string-segment-match.html-complexity", "fields": {"questions": {"assign": ["What technique does the algorithm use?", "What is the memory complexity?", "What is the time complexity?", "What is the average time complexity?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/string-segment-match.html-metric-set", "fields": {"questions": {"assign": ["What are the string segment match metrics?", "How are metrics normalized?", "What is included in the metric set?", "What are the basic normalized features?", "What is the 'match' metric?", "How can applications choose the metrics?", "What are the low level, un-normalized metrics?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/string-segment-match.html-configuration-parameters", "fields": {"questions": {"assign": ["What is the proximity table?", "What is maxAlternativeSegmentations?", "What is maxOccurrences?", "What is proximityCompletenessImportance?", "What is relatednessImportance?", "What is earlinessImportance?", "What is segmentProximityImportance?", "What is occurrenceImportance?", "What is fieldCompletenessImportance?"]}}} -{"update": "id:open-p:paragraph::open/en/tensor-examples.html-", "fields": {"questions": {"assign": ["What is the tensor model in Vespa?", "What are sparse dimensions?", "Can tensors be used for more than neural nets?", "What does the Tensor Usage Examples page provide?", "Does Vespa support lambda computations?", "What kind of examples are provided in Tensor Usage Examples?"]}}} -{"update": "id:open-p:paragraph::open/en/tensor-examples.html-values-that-depend-on-the-current-time", "fields": {"questions": {"assign": ["How do tensors specify prices?", "What are the three tensors used?", "How can promotions with a given product price and time interval be included in documents?", "How can you retrieve the currently valid price of a promotion?", "What is the expression to retrieve the currently valid price?", "What will be returned if there is no matching interval?"]}}} -{"update": "id:open-p:paragraph::open/en/tensor-examples.html-adding-scalars-to-a-tensor", "fields": {"questions": {"assign": ["How can tensor be created?", "What is a dense vector?", "What is a scalar expression?", "What does tensor(x[2]):[bm25(title),attribute(popularity)] do?"]}}} -{"update": "id:open-p:paragraph::open/en/tensor-examples.html-dot-product-between-query-and-document-vectors", "fields": {"questions": {"assign": ["What is dot product?", "How is the dot product calculated?"]}}} -{"update": "id:open-p:paragraph::open/en/tensor-examples.html-logistic-regression-models-with-cross-features", "fields": {"questions": {"assign": ["How to use machine-learning?", "What are the base features?", "What is logistic regression?", "What is the weight tensor?", "What is cross feature tensor?"]}}} -{"update": "id:open-p:paragraph::open/en/tensor-examples.html-matrix-product-between-1d-vector-and-2d-matrix", "fields": {"questions": {"assign": ["What is a tensor product?", "What is the matrix product?", "What is a sparse tensor product?", "What is a query vector?"]}}} -{"update": "id:open-p:paragraph::open/en/tensor-examples.html-using-a-tensor-as-a-lookup-structure", "fields": {"questions": {"assign": ["What are tensors?", "How do you do a tensor lookup?", "What is a tensor attribute?", "What is the query syntax for tensors?", "What happens if the key does not match anything?", "What is a fallback expression for tensor lookups?", "Where can I find more details about tensor performance tuning?"]}}} -{"update": "id:open-p:paragraph::open/en/tensor-user-guide.html-", "fields": {"questions": {"assign": ["What is the tensor model?", "How does Vespa use tensors?", "How do I query with tensors?", "What are tensor concepts?", "Are there examples of tensor computations?", "How do I create tensors from document fields?"]}}} -{"update": "id:open-p:paragraph::open/en/tensor-user-guide.html-tensor-concepts", "fields": {"questions": {"assign": ["What is a tensor in Vespa?", "What is the rank of a tensor?", "What are mapped dimensions?", "What are indexed dimensions?", "What is a mixed tensor?", "What is a tensor type?", "What is a cell in a tensor?", "What is the value type of a tensor?"]}}} -{"update": "id:open-p:paragraph::open/en/tensor-user-guide.html-tensor-document-fields", "fields": {"questions": {"assign": ["What is a tensor attribute?"]}}} -{"update": "id:open-p:paragraph::open/en/tensor-user-guide.html-feeding-tensors", "fields": {"questions": {"assign": ["What are tensor field values?", "How can you add tensor cells?", "What is the JSON format for adding tensor fields?", "What is the tensor field in Vespa?", "What can you do with tensor cells?", "How can you create tensor values?", "What is the tensor Java API?", "How can you set tensor values in documents?"]}}} -{"update": "id:open-p:paragraph::open/en/tensor-user-guide.html-querying-with-tensors", "fields": {"questions": {"assign": ["What types of tensors can be indexed?"]}}} -{"update": "id:open-p:paragraph::open/en/tensor-user-guide.html-ranking-with-tensors", "fields": {"questions": {"assign": ["What are tensors used for?", "How to pass tensors in queries?", "What is a tensor expression?", "What is the Tensor class?", "How can tensors be accessed in ranking expressions?", "What is the full list of tensor functions?", "What is the syntax to define a tensor type in a rank profile?"]}}} -{"update": "id:open-p:paragraph::open/en/tensor-user-guide.html-creating-tensors-from-document-fields", "fields": {"questions": {"assign": ["How can single-valued attributes be converted to tensors?", "What is the tensor generate function?", "What is a mapped tensor?", "What is the document feature tensorFromLabels used for?"]}}} -{"update": "id:open-p:paragraph::open/en/tensor-user-guide.html-constant-tensors", "fields": {"questions": {"assign": ["What are constant tensors?", "Why are constant tensors used in ranking?", "How are constant tensors defined?", "What is the format of constant tensor files?", "Can constant tensor files be compressed?", "How are constant tensors used in ranking expressions?", "What is rank-profile use_constant_tensor?", "What is the expression in rank-profile use_constant_tensor?"]}}} -{"update": "id:open-p:paragraph::open/en/tensor-user-guide.html-tensors-with-strings", "fields": {"questions": {"assign": ["Can tensors have string values?", "How can you represent sets of strings in tensors?", "How do you perform set operations on strings using tensors?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/tensor.html-", "fields": {"questions": {"assign": ["What is a cell?", "What is an address?", "What is the difference between mapped and indexed dimensions?", "What is the range of indexed dimensions?", "What are the values located in the space of dimensions?", "What is a label in the context of tensor dimensions?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/tensor.html-tensor-type-spec", "fields": {"questions": {"assign": ["What is a tensor type?", "What is a mixed tensor?", "What is a tensor cell?", "What is the format of a tensor?", "What is the cell type of a tensor?", "What are the valid tensor value types?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/tensor.html-tensor-literal-form", "fields": {"questions": {"assign": ["What is the tensor literal form?", "Where is the tensor literal form used?", "How is the tensor literal form used?", "What is the Java Tensor API?", "Where can I find the Java Tensor API documentation?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/tensor.html-general-literal-form", "fields": {"questions": {"assign": ["What is the general literal form?", "What is EBNF?", "What is a cell in tensor?", "What is an address in tensor?", "What is a label in tensor?", "What is an identifier in tensor?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/tensor.html-general-literal-form-examples:", "fields": {"questions": {"assign": ["What is an empty tensor?", "How is tensor type specified?", "What is the default value type for a tensor?", "How are labels represented in a tensor?", "What is the syntax for creating a tensor with multiple values and dimensions?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/tensor.html-indexed-short-form", "fields": {"questions": {"assign": ["How should indexed tensors be written?", "What are the requirements for explicit tensor types?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/tensor.html-indexed-short-form-examples:", "fields": {"questions": {"assign": ["What is a float 1d tensor in indexed form?", "What is a matrix in indexed form?", "Can inner brackets be omitted in indexed form?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/tensor.html-binary-hex-format", "fields": {"questions": {"assign": ["What is the input format for binary data?", "What is the cell value type for binary tensors?", "Can fixed size binary data be input using a hexadecimal dump?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/tensor.html-mapped-short-form", "fields": {"questions": {"assign": ["What is a mapped dimension?", "What is required for a single mapped dimension?", "What is the advantage of a single mapped dimension?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/tensor.html-mixed-short-form", "fields": {"questions": {"assign": ["What is the map short form?", "What is the indexed short form?", "What does the dense subspace contain?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/tensor.html-tensor-functions", "fields": {"questions": {"assign": ["What are tensor functions?", "Where are tensor functions listed?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/tensor.html-tensor-rank-features", "fields": {"questions": {"assign": ["What are the rank features?", "What is a tensor attribute?", "What is a tensor constant?", "What is the Tensor field in schema?"]}}} -{"update": "id:open-p:paragraph::open/en/tensorflow.html-", "fields": {"questions": {"assign": ["What is the ONNX format?", "What is TensorFlow?", "What is tf2onnx library?", "Can you inspect expected input/output format?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/testing-java.html-", "fields": {"questions": {"assign": ["What is Vespa Testing reference?", "What do Vespa system tests verify?", "What is the basic HTTP test reference?", "How to run Vespa tests?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/testing-java.html-test-suites", "fields": {"questions": {"assign": ["What is the testing documentation?", "What are the test scenarios and categories?", "What is the system test framework?", "What are the JUnit test tags?", "How do you run tests with Maven?", "What is the sample application test suite?"]}}} -{"update": "id:open-p:paragraph::open/en/testing.html-", "fields": {"questions": {"assign": ["What is a system tests suite?", "What are the two formalizations?", "What is a staging test?", "What is a production verification test?", "What is the purpose of HTTP requests in testing?", "What is the purpose of staging tests?", "What is the purpose of production verification tests?", "What is A/B-testing?"]}}} -{"update": "id:open-p:paragraph::open/en/testing.html-system-tests", "fields": {"questions": {"assign": ["What are system tests?", "How to run system tests?", "When should system tests be updated?", "Why run system tests?", "What do system tests verify?", "What does a system test suite include?", "How to test a deployed Vespa application?", "What to verify in a system test?"]}}} -{"update": "id:open-p:paragraph::open/en/testing.html-running-system-tests", "fields": {"questions": {"assign": ["What tests can Vespa CLI run?", "How to set up test deployment?", "What is test-config.json file?"]}}} -{"update": "id:open-p:paragraph::open/en/testing.html-staging-tests", "fields": {"questions": {"assign": ["What is the goal of staging tests?", "What should staging tests ensure?", "How are staging tests different from system tests?", "What is controlled during staging tests?", "What dictates the workflow when deploying a breaking change?", "What are the steps for deploying a breaking change?"]}}} -{"update": "id:open-p:paragraph::open/en/testing.html-running-staging-tests", "fields": {"questions": {"assign": ["What are system tests?", "What is meant by staging setup?", "What is the purpose of staging test?"]}}} -{"update": "id:open-p:paragraph::open/en/testing.html-load-old-application-code,-deploy-it,-run-setup", "fields": {"questions": {"assign": ["What is the path for the 'tests/staging-setup' test?"]}}} -{"update": "id:open-p:paragraph::open/en/testing.html-make-changes-to-the-application,-deploy-it,-run-tests", "fields": {"questions": {"assign": ["How long does vespa deploy wait?", "What is tested in staging-test?"]}}} -{"update": "id:open-p:paragraph::open/en/testing.html-load-old-application-code,-deploy-it,-run-setup", "fields": {"questions": {"assign": ["How long does 'vespa deploy' wait?", "What is 'mvn test'?"]}}} -{"update": "id:open-p:paragraph::open/en/testing.html-make-changes-to-the-application,-deploy-it,-run-tests", "fields": {"questions": {"assign": ["What is the mvn command to test staging?", "What is the path to test-config.json?"]}}} -{"update": "id:open-p:paragraph::open/en/testing.html-feature-switches-and-bucket-tests", "fields": {"questions": {"assign": ["What is continuous deployment?", "What are feature switches?", "What is bucket testing?", "What is an incompatible change?", "Why use precise semantics?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/testing.html-", "fields": {"questions": {"assign": ["What are basic HTTP tests?", "How are basic HTTP tests written?", "What is the Java testing reference?", "What do Vespa application system tests verify?", "What are the HTTP interfaces used for?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/testing.html-test-suites", "fields": {"questions": {"assign": ["What is testing documentation?", "How many test scenarios are defined?", "What are the four test code categories?", "What is the file format for each test?", "How are categories defined for HTTP tests?", "What is the path to the verify-search-still-works.json file?", "What is the path to the metrics-test.json file?", "Where can I find an example test suite?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/testing.html-test-file-structure", "fields": {"questions": {"assign": ["What does each step of a test contain?", "What is the format of an HTTP request specification in a test?", "What is the format of an HTTP response specification in a test?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/testing.html-test-json-specification", "fields": {"questions": {"assign": ["What is a 'step' field?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/testing.html-json-matching", "fields": {"questions": {"assign": ["What data format should requests be?", "What is JSON verification?", "What is a JSON template?", "What is the precision for number matching?", "What is the rule for objects containing unlisted fields?", "What is the rule for matching arrays?", "What is the empty object used for?"]}}} -{"update": "id:open-p:paragraph::open/en/text-matching.html-", "fields": {"questions": {"assign": ["What is the ranking introduction?", "What are the different match modes?", "What is Semantic Retrieval for QA?", "What is the Linguistics guide?"]}}} -{"update": "id:open-p:paragraph::open/en/text-matching.html-matching", "fields": {"questions": {"assign": ["What are linguistic transformations?"]}}} -{"update": "id:open-p:paragraph::open/en/text-matching.html-query-trace", "fields": {"questions": {"assign": ["How does trace.level help testing?", "What is query tracing for?", "How can query parsing change?"]}}} -{"update": "id:open-p:paragraph::open/en/text-matching.html-prefix-match", "fields": {"questions": {"assign": ["What is prefix search?", "What fields can use prefix search?", "How can prefix search be enabled?", "What is the search-suggestions sample application?", "What design discussions are present in the search-suggestions README?"]}}} -{"update": "id:open-p:paragraph::open/en/text-matching.html-n-gram-indexing", "fields": {"questions": {"assign": ["What is gram-size used for?", "What is the benefit of indexing ngrams?", "How does ngram indexing work?", "What is the difference between gram and phrase?", "When is indexing using ngrams useful?", "What are the limitations of ngram indexing?"]}}} -{"update": "id:open-p:paragraph::open/en/text-matching.html-regular-expressions", "fields": {"questions": {"assign": ["What are regular expressions?", "Is regular expression optimization supported?", "How does Vespa handle regular expressions?", "Can regular expressions be used in attributes?"]}}} -{"update": "id:open-p:paragraph::open/en/ranking.html-text-ranking", "fields": {"questions": {"assign": ["What is the default ranking?", "What other ranking dimensions are there?"]}}} -{"update": "id:open-p:paragraph::open/en/ranking.html-weight,-significance-and-connectedness", "fields": {"questions": {"assign": ["What is weight in Vespa?", "What is the default weight?", "What is the significance feature?", "How is the significance calculated?", "What is connectedness in Vespa?", "How can connectedness be set?"]}}} -{"update": "id:open-p:paragraph::open/en/tutorials/text-search-ml.html-", "fields": {"questions": {"assign": ["What is the Text Search Tutorial?", "What is MS MARCO?", "What is ML?", "What are sanity-checks for datasets?"]}}} -{"update": "id:open-p:paragraph::open/en/tutorials/text-search-ml.html-collect-rank-feature-data-from-vespa", "fields": {"questions": {"assign": ["What is Vespa's rank feature set?", "How can rank features improve ranking?"]}}} -{"update": "id:open-p:paragraph::open/en/tutorials/text-search-ml.html-default-rank-features", "fields": {"questions": {"assign": ["What is ranking.listFeatures?", "How to access default ranking features?", "What is the default rank profile?", "What is returned by default?", "Can the default list of rank features change?", "Where can I check the current list of rank features?"]}}} -{"update": "id:open-p:paragraph::open/en/tutorials/text-search-ml.html-chose-and-process-specific-rank-features", "fields": {"questions": {"assign": ["What is the purpose of the 'collect_rank_features' profile?", "How do you disable the default ranking features?", "What are some advisable use cases for many ranking features?"]}}} -{"update": "id:open-p:paragraph::open/en/tutorials/text-search-ml.html-create-a-training-dataset", "fields": {"questions": {"assign": ["What is the MS MARCO dataset?", "What is collect_training_data.py?", "What are relevant_id pairs?", "What is the collect_rank_features rank-profile?", "What is the output folder?", "What does collect_rank_features request?"]}}} -{"update": "id:open-p:paragraph::open/en/tutorials/text-search-ml.html-data-collection-logic", "fields": {"questions": {"assign": ["What is collect_training_data.py?", "What is the goal of data collection?", "How is data collected?", "What is the format of collected data?", "What hits are returned by Vespa?", "What is the pseudo-code for data collection?"]}}} -{"update": "id:open-p:paragraph::open/en/tutorials/text-search-ml.html-get-relevant-hit", "fields": {"questions": {"assign": ["What parameter specifies the desired query string?", "What is the syntax to retrieve more than one document?", "How does the recall parameter work?", "What is the purpose of setting 'hits' to 1?", "What is the function call to get relevant hits?", "What parameters instruct Vespa to return id and rank-features?"]}}} -{"update": "id:open-p:paragraph::open/en/tutorials/text-search-ml.html-get-random-hits", "fields": {"questions": {"assign": ["What is the second Vespa request?", "What is the purpose of `get_random_hits` function?", "What is the `number_random_sample` parameter?", "What are the changes with respect to `get_relevant_hit`?", "What is the `second-phase` configuration parameter?", "What does using `random` as the second-phase ranking function ensure?", "How are the top documents returned by Vespa selected?"]}}} -{"update": "id:open-p:paragraph::open/en/tutorials/text-search-ml.html-annotated-data", "fields": {"questions": {"assign": ["What is the meaning of the 'relevant' column?", "What does 'relevant'=1 signify in the file?", "How is the Vespa result parsed and stored?", "What are the columns in the Vespa result parsing file?", "What is the meaning of 'bm25' and 'nativeRank' columns in the file?", "How is the value in the 'relevant' column determined?"]}}} -{"update": "id:open-p:paragraph::open/en/tutorials/text-search-ml.html-data-collection-sanity-check", "fields": {"questions": {"assign": ["What is data collection sanity-check?", "What is the baseline used?", "What is the linear model?", "What is the purpose of dataset validation?", "What led to investigating and catching the bug?"]}}} -{"update": "id:open-p:paragraph::open/en/tutorials/text-search-ml.html-beyond-pointwise-loss-functions", "fields": {"questions": {"assign": ["What is the MRR?", "What is TF-Ranking?", "What is a listwise loss function?", "What is a pointwise loss function?", "What is the difference between listwise and pointwise loss?", "What is the best loss function for ranking?", "What is the ranking function plot?"]}}} -{"update": "id:open-p:paragraph::open/en/tutorials/text-search-ml.html-next-steps", "fields": {"questions": {"assign": ["What is Machine Learned Ranking?", "What are popular ML libraries integrated with Vespa?"]}}} -{"update": "id:open-p:paragraph::open/en/tutorials/text-search-semantic.html-", "fields": {"questions": {"assign": ["What is semantic search?", "What is a pre-trained text embedding?", "What is the MS MARCO dataset?", "How effective is the weakAND algorithm?", "What are the limitations of pre-trained semantic vectors?", "What datasets are less biased towards term-matching signals?"]}}} -{"update": "id:open-p:paragraph::open/en/tutorials/text-search-semantic.html-a-note-on-the-data", "fields": {"questions": {"assign": ["What is the MS MARCO dataset?", "How many queries were randomly selected?", "How many documents were randomly selected?", "Are the relevant documents guaranteed to be in the corpus?", "Where is the entire dataset and code described?"]}}} -{"update": "id:open-p:paragraph::open/en/tutorials/text-search-semantic.html-a-note-on-metrics", "fields": {"questions": {"assign": ["What are the three metrics?", "What is `Recall @ 100`?", "What is `MRR @ 100`?", "What is the importance of `Matched docs` and `MRR @ 100`?", "What is the relationship between `Matched docs` and `Recall @ 100`?", "What is the purpose of a multi-phased ranking system?", "What is the assumption made about the 2nd phase in the experiments?"]}}} -{"update": "id:open-p:paragraph::open/en/tutorials/text-search-semantic.html-baselines", "fields": {"questions": {"assign": ["What is the purpose of using baselines?", "How many queries result in missing relevant documents with the 'AND' operator?", "What is the consequence of using the 'AND' operator?", "What is the recall rate for relevant documents with the 'OR' operator?'"]}}} -{"update": "id:open-p:paragraph::open/en/tutorials/text-search-semantic.html-pre-trained-vector-embeddings", "fields": {"questions": {"assign": ["Which pre-trained vectors were evaluated?", "Where can Universal sentence encoder be found?", "What is the goal of the experiment?"]}}} -{"update": "id:open-p:paragraph::open/en/tutorials/text-search-semantic.html-from-text-to-embeddings-methodology", "fields": {"questions": {"assign": ["What is sentence-transformers library?"]}}} -{"update": "id:open-p:paragraph::open/en/tutorials/text-search-semantic.html-approximate-nearest-neighbor-(ann)-operator", "fields": {"questions": {"assign": ["What is semantic search?", "What is ANN?", "What is the ANN operator?"]}}} -{"update": "id:open-p:paragraph::open/en/tutorials/text-search-semantic.html-schema", "fields": {"questions": {"assign": ["How can document embeddings be defined?", "How can you match documents based on distance between query and document tensors?"]}}} -{"update": "id:open-p:paragraph::open/en/tutorials/text-search-semantic.html-query", "fields": {"questions": {"assign": ["How to send query embeddings?"]}}} -{"update": "id:open-p:paragraph::open/en/tutorials/text-search-semantic.html-ann-operator", "fields": {"questions": {"assign": ["What is nearestNeighbor operator?", "How does nearestNeighbor operator work?", "What is the targetHits property?", "What is bert_title_body_all?"]}}} -{"update": "id:open-p:paragraph::open/en/tutorials/text-search-semantic.html-ann-results", "fields": {"questions": {"assign": ["What is the ANN operator?", "What is the relationship between matched documents and recall?", "What is the benefit of combining semantic search with term-matching?", "What features related to term-matching are available in Vespa?"]}}} -{"update": "id:open-p:paragraph::open/en/tutorials/text-search-semantic.html-weakand-operator-and-its-effectiveness", "fields": {"questions": {"assign": ["What is the weakAnd implementation?", "How does the weakAnd operator work?", "What are the two core text rank features used in the weakAnd implementation?", "What is the recall metric for the weakAnd operator?", "How many documents did the weakAnd operator match?", "What is the difference between the weakAnd and OR operators regarding matched documents?"]}}} -{"update": "id:open-p:paragraph::open/en/tutorials/text-search-semantic.html-ann-and-weakand:-little-improvement", "fields": {"questions": {"assign": ["What are WAND and ANN?", "What is the impact on Recall and MRR of adding pre-trained embedings to WAND?", "What is the effect of using dot-product in the 1st phase ranking?", "How can we address scaling issues when combining BM25 and embedding dot-products?", "What is the recommended approach to handle scaling issues when combining BM25 and embedding dot-products?", "What are the benefits of the linear-model approach to model appropriate match phase for training model weights?", "How did the relative weight associated with the BM25 scores change after implementing machine learning for normalization?"]}}} -{"update": "id:open-p:paragraph::open/en/tutorials/text-search-semantic.html-msmarco:-a-biased-dataset%3F", "fields": {"questions": {"assign": ["What is the MSMARCO dataset?", "What is the correlation between bm25 and embedding scores?", "What is the blue histogram showing?", "What is the red histogram showing?", "What does semantic vectors complement with?", "What could explain outstanding results with weakAND?", "What kind of environment is favorable for weakAND?"]}}} -{"update": "id:open-p:paragraph::open/en/tutorials/text-search-semantic.html-fine-tuning-sentence-embeddings:-advantages-and-disadvantages", "fields": {"questions": {"assign": ["What are pre-trained embeddings?", "What is overfitting in NLP?", "What is the MS MARCO dataset?", "What is the problem with leaderboards in NLP?", "What is the importance of less biased datasets?", "What are the challenges with big complex models?", "What are the considerations in fine-tuning embeddings?", "Why is it important to check if the improvement is worth the cost?"]}}} -{"update": "id:open-p:paragraph::open/en/tutorials/text-search.html-", "fields": {"questions": {"assign": ["How do you set up a text search application on Vespa?", "What is the goal of this tutorial?", "What future improvements will be added in other tutorials?"]}}} -{"update": "id:open-p:paragraph::open/en/tutorials/text-search.html-installing-vespa-cli", "fields": {"questions": {"assign": ["What is Vespa CLI?", "What is vespa-cli used for?", "How do I install Vespa CLI?", "How can I clone a Vespa application from GitHub?", "What does the sample-apps repository contain?", "What is the purpose of this tutorial?", "What will be covered in this tutorial?"]}}} -{"update": "id:open-p:paragraph::open/en/tutorials/text-search.html-dataset", "fields": {"questions": {"assign": ["What is MS MARCO?", "What is a dataset?", "What is included in the sample data?", "Where can I download the full MS MARCO dataset?", "How much disk space does the full MS MARCO dataset require?", "How do I convert the MS MARCO dataset to Vespa format?", "What is test-queries.tsv?"]}}} -{"update": "id:open-p:paragraph::open/en/tutorials/text-search.html-create-a-vespa-application-package", "fields": {"questions": {"assign": ["What is a Vespa application package?", "What files are required for basic text search?", "How do you create a new Vespa application?", "What is the purpose of `msmarco.sd` file?", "What is the directory structure for a Vespa application?", "What are the configuration files required for defining behavior of a Vespa system?", "What is the role of Java plugins in a Vespa application package?"]}}} -{"update": "id:open-p:paragraph::open/en/tutorials/text-search.html-schema", "fields": {"questions": {"assign": ["What is a schema configuration?", "What is a fieldset?", "What does the bm25 rank profile do?"]}}} -{"update": "id:open-p:paragraph::open/en/tutorials/text-search.html-services-specification", "fields": {"questions": {"assign": ["What does services.xml define?", "What is defined in the container cluster?", "What is the default port of endpoint?"]}}} -{"update": "id:open-p:paragraph::open/en/tutorials/text-search.html-deploy-the-application-package", "fields": {"questions": {"assign": ["What is RAM requirement?"]}}} -{"update": "id:open-p:paragraph::open/en/tutorials/text-search.html-feed-the-data", "fields": {"questions": {"assign": ["What is the Java feeding API?"]}}} -{"update": "id:open-p:paragraph::open/en/tutorials/text-search.html-run-a-test-query", "fields": {"questions": {"assign": ["What is YQL?", "What is simple query language?", "What is the default query type?", "What is the purpose of Vespa WeakAnd query operator?", "How does 'type=weakAnd' query work?"]}}} -{"update": "id:open-p:paragraph::open/en/tutorials/text-search.html-compare-and-evaluate-different-ranking-functions", "fields": {"questions": {"assign": ["What is bm25?", "What is MRR?", "What is evaluate.py?", "What is the output of evaluate.py?", "What is the difference between bm25 and nativeRank in MSMARCO?"]}}} -{"update": "id:open-p:paragraph::open/en/tutorials/text-search.html-next-steps", "fields": {"questions": {"assign": ["What is the probabilistic relevance framework?"]}}} -{"update": "id:open-p:paragraph::open/en/troubleshooting-encoding.html-", "fields": {"questions": {"assign": ["What is UTF-8?", "What is UTF-16?", "How is a code point stored in Java?", "What is a surrogate pair?", "How to access code point in Java?", "What are the limitations of UTF-16 encoding?", "What is the purpose of com.yahoo.text.Utf8 toolbox?", "What is the use of String.codePoints() method?"]}}} -{"update": "id:open-p:paragraph::open/en/troubleshooting-encoding.html-visual-pattern-matching-of-encoding-bugs", "fields": {"questions": {"assign": ["What is UTF-8?", "What is ISO-8859-1?", "What is Windows 1252?", "What is line noise in-between normal ASCII?", "How to debug encoding problems?", "Why should you never try to debug encoding problems with a web browser?"]}}} -{"update": "id:open-p:paragraph::open/en/troubleshooting-encoding.html-json", "fields": {"questions": {"assign": ["What is stripInvalidCharacters used for?", "What is proper JSON formatting?", "What are ASCII control characters?"]}}} -{"update": "id:open-p:paragraph::open/en/unit-testing.html-", "fields": {"questions": {"assign": ["What are automated deployments?", "How to implement system verification?", "What are production verification tests?"]}}} -{"update": "id:open-p:paragraph::open/en/unit-testing.html-unit-testing-using-application", "fields": {"questions": {"assign": ["What is the Application class?", "What is MetalSearcherTest.java?", "How do you disable the network layer?"]}}} -{"update": "id:open-p:paragraph::open/en/unit-testing.html-unit-testing-configurable-components", "fields": {"questions": {"assign": ["How to programmatically instantiate configurations?", "How can builders help with unit testing?"]}}} -{"update": "id:open-p:paragraph::open/en/unit-testing.html-configuration-schema", "fields": {"questions": {"assign": ["What is the DemoConfig class?", "What does toplevel define?", "What is simplenumber?", "What is in simplevaluearray?", "What is the coordinate structure?", "What is coordinate.x?", "What is coordinate.y?"]}}} -{"update": "id:open-p:paragraph::open/en/unit-testing.html-using-configuration-builders", "fields": {"questions": {"assign": ["What is the purpose of DemoConfig?", "What is the coordinate builder?", "How many builder classes are used?", "What is a nested class?", "What is the purpose of DemoTest?", "What is the simplename setter for?", "What is the simplenumber setter for?", "How are configuration arrays built?"]}}} -{"update": "id:open-p:paragraph::open/en/use-case-question-answering.html-", "fields": {"questions": {"assign": ["What is the open-domain QA use case?", "What is Dense Passage Retriever?", "How does the application retrieve passages?", "What is the BERT model used for?", "What is the Natural Questions dataset?", "What kind of questions can be asked with the deployed application?"]}}} -{"update": "id:open-p:paragraph::open/en/use-case-question-answering.html-highlighted-features", "fields": {"questions": {"assign": ["What is multi-threaded ranking?", "What is multi-phased ranking?", "What are Transformer models used for?"]}}} -{"update": "id:open-p:paragraph::open/en/use-case-shopping.html-", "fields": {"questions": {"assign": ["What is the e-commerce use case?", "What is the shopping sample app about?", "Where do I navigate to browse the application?", "What is the Amazon product data set?", "How do I convert data to Vespa format?", "How is the review rating of an item stored?", "What is the recommended way to handle reviews?"]}}} -{"update": "id:open-p:paragraph::open/en/using-wand-with-vespa.html-", "fields": {"questions": {"assign": ["What is the WAND algorithm?", "How does WAND work?", "What are the benefits of WAND?", "When should WAND be used?", "What are the differences between weakAnd and wand operators?", "Does wand operator integrate with linguistic processing?", "How can WAND be evaluated?"]}}} -{"update": "id:open-p:paragraph::open/en/using-wand-with-vespa.html-weakand", "fields": {"questions": {"assign": ["What is the weakAnd operator?", "What fields can weakAnd retrieve?", "What is the effect of tuning targetHits?", "What are some ranking features that correlate well with weakAnd scoring?", "What should you do to get the best hits possible with weakAnd?", "What can cause weakAnd queries to become slower?", "What is the query example expressed in YQL?"]}}} -{"update": "id:open-p:paragraph::open/en/using-wand-with-vespa.html-weakand-inner-scoring", "fields": {"questions": {"assign": ["What is the weakAnd query operator?", "What ranking features does weakAnd use?", "How is term significance calculated?", "What is the purpose of weakAnd.replace?", "How does weakAnd handle documents that can't compete with top hits?", "Does weakAnd use the number of times a term occurs in a document for inner scoring?", "Can term significance and weight be overridden in the query using annotations?", "Does weakAnd expose all evaluated hits or just the top k hits?"]}}} -{"update": "id:open-p:paragraph::open/en/using-wand-with-vespa.html-wand", "fields": {"questions": {"assign": ["What is the wand operator?", "What types are supported by wand?", "What is deep_bm25 ranking profile?", "What is a rawScore?", "How is bm25 calculated for wand hits?"]}}} -{"update": "id:open-p:paragraph::open/en/using-zookeeper.html-", "fields": {"questions": {"assign": ["What is ZooKeeper?", "What is a ZooKeeper ensemble?", "How does Vespa reconfigure ZooKeeper members?", "What is the optimal node limit for ZooKeeper clusters?", "What happens to application packages that violate the node count?", "What are the valid node counts for ZooKeeper clusters?", "Where can I find more information about node counts for ZooKeeper clusters?"]}}} -{"update": "id:open-p:paragraph::open/en/using-zookeeper.html-configuration", "fields": {"questions": {"assign": ["How do I enable ZooKeeper?", "What is zkfacade?"]}}} -{"update": "id:open-p:paragraph::open/en/using-zookeeper.html-code-example", "fields": {"questions": {"assign": ["What is VespaCurator?", "How to use VespaCurator in ZooKeeper?", "What is an example of using VespaCurator in a request handler?"]}}} -{"update": "id:open-p:paragraph::open/en/performance/valgrind.html-", "fields": {"questions": {"assign": ["What is Valgrind?", "How to run Vespa processes with valgrind?", "Why is Valgrind useful?"]}}} -{"update": "id:open-p:paragraph::open/en/performance/valgrind.html-valgrind-with-callgrind", "fields": {"questions": {"assign": ["What is valgrind?", "How do you install valgrind?", "What is the general use of valgrind?", "What is world writeable in Vespa?", "How do you use valgrind to show a detailed profiling graph?", "What should you do after a profiling session is completed?", "What tool should be installed to view valgrind results?", "How do you specify the path to code in kcachegrind?"]}}} -{"update": "id:open-p:paragraph::open/en/performance/valgrind.html-start-vespa-using-valgrind", "fields": {"questions": {"assign": ["How to start Vespa with valgrind?", "How to check for memory errors?", "Where are the Valgrind logs located?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/validation-overrides.html-", "fields": {"questions": {"assign": ["What is the purpose of validation-overrides.xml?", "What is the format for validation messages?", "What does a validation-id signify?", "What is the location of validation-overrides.xml?", "When can overriding validations be useful?", "What protections do validations provide?", "What types of validation can be overridden?", "What happens when a deployment fails validation?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/validation-overrides.html-structure", "fields": {"questions": {"assign": ["What is the validation-overrides tag?", "What does the example of the validation-overrides tag look like?", "What is cluster-size-reduction?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/validation-overrides.html-allow", "fields": {"questions": {"assign": ["What is an 'allow'-tag?", "What does an 'allow'-tag contain?", "What is the format of the 'until' attribute?", "What happens with 'allow'-tags with unknown ids?", "What happens with 'allow'-tags with dates in the past?", "Is the 'comment' attribute mandatory?"]}}} -{"update": "id:open-p:paragraph::open/en/vector-search.html-", "fields": {"questions": {"assign": ["What is vector search?", "How does vector search work?", "What is multi-modal search?"]}}} -{"update": "id:open-p:paragraph::open/en/vector-search.html-digital-representations", "fields": {"questions": {"assign": ["What is the synonym problem?", "What is the solution to the synonym problem?", "What is a vector representation?", "What is vector search?", "What is the digital domain?", "What is the multi-modal problem?", "How is vector search different from textual search?"]}}} -{"update": "id:open-p:paragraph::open/en/vector-search.html-how-vectors-are-created", "fields": {"questions": {"assign": ["What are vectors in Vespa?", "What is the cost/quality tradeoff?", "What is PCA in Vespa?", "How to select optimal tensor type?", "What is the role of vectors in search?", "What are the dimensions and types of a vector?"]}}} -{"update": "id:open-p:paragraph::open/en/vector-search.html-doing-a-vector-search", "fields": {"questions": {"assign": ["What is vector search?", "How does vector search work?", "Can vectors with different values for each dimension be similar?", "When is vector search useful?", "How do you calculate vector similarity?"]}}} -{"update": "id:open-p:paragraph::open/en/vector-search.html-approximate-for-speed", "fields": {"questions": {"assign": ["What is vector similarity?", "What is ANN search?", "What indexing is needed for ANNs?", "Does Vespa support vector updates?", "Does Vespa support all kinds of ANN indexing?", "What is the cost of inserts for ANNs?"]}}} -{"update": "id:open-p:paragraph::open/en/performance/vespa-benchmarking.html-", "fields": {"questions": {"assign": ["What is benchmarking in Vespa?", "What questions can benchmarking answer?", "When is benchmarking recommended?", "What are considerations before benchmarking?", "How can you disable soft timeout?", "How long should timeout be for benchmarking?", "How can you set timeout for benchmarking?", "Where can I find Vespa Cloud Benchmarking?"]}}} -{"update": "id:open-p:paragraph::open/en/performance/vespa-benchmarking.html-vespa-fbench", "fields": {"questions": {"assign": ["What is vespa-fbench?", "What are the vespa-fbench statistics?", "What is the purpose of vespa-fbench?", "What is the disclaimer for vespa-fbench?"]}}} -{"update": "id:open-p:paragraph::open/en/performance/vespa-benchmarking.html-prepare-queries", "fields": {"questions": {"assign": ["What is vespa-fbench?", "What is the format for HTTP POST requests?"]}}} -{"update": "id:open-p:paragraph::open/en/performance/vespa-benchmarking.html-post-processing", "fields": {"questions": {"assign": ["What is summarized in benchmarking?", "How do you measure Vespa latency?"]}}} -{"update": "id:open-p:paragraph::open/en/performance/vespa-benchmarking.html-benchmark", "fields": {"questions": {"assign": ["What is a latency baseline?", "What is requestthreads?", "How do you find capacity?", "What is the purpose of increasing number of clients?", "What is the recommended way to increase threads?"]}}} -{"update": "id:open-p:paragraph::open/en/performance/vespa-benchmarking.html-metrics", "fields": {"questions": {"assign": ["How to output all metrics?", "What is the output format of 'cpu.util' metric?", "What is the role of 'content/mysearchcluster/0/0'?", "How to use the 'jq' command?"]}}} -{"update": "id:open-p:paragraph::open/en/vespa-cli.html-", "fields": {"questions": {"assign": ["What is Vespa CLI?", "What can be done with Vespa CLI?", "How to install Vespa CLI?", "What platforms support Vespa CLI?", "Where can I find Vespa CLI commands documentation?"]}}} -{"update": "id:open-p:paragraph::open/en/vespa-cli.html-install---make-sure-to-upgrade-frequently-for-new-features", "fields": {"questions": {"assign": ["How to install vespa-cli?", "What is vespa-cli?"]}}} -{"update": "id:open-p:paragraph::open/en/vespa-cli.html-set-home-dir-to-a-writeable-directory---useful-in-some-container-contexts", "fields": {"questions": {"assign": ["What is VESPA_CLI_HOME?", "How to set VESPA_CLI_HOME?"]}}} -{"update": "id:open-p:paragraph::open/en/vespa-cli.html-get-help", "fields": {"questions": {"assign": ["How can I get help with vespa document put?"]}}} -{"update": "id:open-p:paragraph::open/en/vespa-cli.html-use-a-browser-to-log-into-vespa-cloud", "fields": {"questions": {"assign": ["What is vespa auth?", "How do I perform Vespa auth?"]}}} -{"update": "id:open-p:paragraph::open/en/vespa-cli.html-get-a-document", "fields": {"questions": {"assign": ["What is 'a-head-full-of-dreams'?"]}}} -{"update": "id:open-p:paragraph::open/en/vespa-cli.html-export-slice-0-of-10---approximately-10%-of-the-documents", "fields": {"questions": {"assign": ["What is slice-id?"]}}} -{"update": "id:open-p:paragraph::open/en/vespa-cli.html-export-documents-using-a-selection-string:", "fields": {"questions": {"assign": ["How do I use the 'doc.last_updated' field in 'vespa visit'?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/vespa-cmdline-tools.html-", "fields": {"questions": {"assign": ["What are Vespa command-line tools?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/vespa-cmdline-tools.html-vespa-analyze-onnx-model", "fields": {"questions": {"assign": ["What is vespa-analyze-onnx-model?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/vespa-cmdline-tools.html-vespa-attribute-inspect", "fields": {"questions": {"assign": ["What is vespa-attribute-inspect?", "What is the use of vespa-attribute-inspect?", "How to use vespa-attribute-inspect?", "What is the purpose of vespa-attribute-inspect?", "How does vespa-attribute-inspect help with troubleshooting?", "What is vespa-index-inspect?", "What is the related tool to vespa-attribute-inspect?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/vespa-cmdline-tools.html-vespa-configproxy-cmd", "fields": {"questions": {"assign": ["What is vespa-configproxy-cmd?", "What is config proxy?", "What does config proxy run?", "What is the 'sources' method?", "How to update the config proxy's upstream sources?", "What happens when vespa-configproxy-cmd is run without arguments?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/vespa-cmdline-tools.html-vespa-configserver-remove-state", "fields": {"questions": {"assign": ["What is vespa-configserver-remove-state?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/vespa-cmdline-tools.html-vespa-config-status", "fields": {"questions": {"assign": ["What is vespa-config-status?", "What does vespa-config-status output?", "How do you invoke vespa-config-status?", "What optional arguments does vespa-config-status have?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/vespa-cmdline-tools.html-vespa-deploy", "fields": {"questions": {"assign": ["What is the deploy API?", "What does vespa-deploy activate do?", "What are the possible commands for vespa-deploy?", "What are the options for vespa-deploy?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/vespa-cmdline-tools.html-vespa-destination", "fields": {"questions": {"assign": ["What is vespa-destination?", "What does vespa-destination output?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/vespa-cmdline-tools.html-vespa-fbench", "fields": {"questions": {"assign": ["What is vespa-fbench?", "What does vespa-fbench do?", "What is the synopsis for vespa-fbench?", "What is the default output for vespa-fbench?", "How can vespa-fbench be used?", "What options are available for vespa-fbench?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/vespa-cmdline-tools.html-vespa-fbench-filter-file", "fields": {"questions": {"assign": ["What is vespa-fbench-filter-file?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/vespa-cmdline-tools.html-vespa-fbench-geturl", "fields": {"questions": {"assign": ["What is vespa-fbench-geturl?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/vespa-cmdline-tools.html-vespa-feeder", "fields": {"questions": {"assign": ["What is vespa-feeder?", "What does ignored report?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/vespa-cmdline-tools.html-vespa-get", "fields": {"questions": {"assign": ["What is vespa-get?", "What does vespa-get retrieve?", "What are the options for vespa-get?", "What is the default field set for vespa-get?", "What is the default load type for vespa-get?", "What are the options for output format?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/vespa-cmdline-tools.html-vespa-get-cluster-state", "fields": {"questions": {"assign": ["What is vespa-get-cluster-state?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/vespa-cmdline-tools.html-vespa-get-config", "fields": {"questions": {"assign": ["What is vespa-get-config?", "What is the purpose of vespa-get-config?", "What is config proxy?", "How to connect to a config proxy?", "How to connect to a config server?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/vespa-cmdline-tools.html-vespa-get-node-state", "fields": {"questions": {"assign": ["What is vespa-get-node-state?", "What is Generated state?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/vespa-cmdline-tools.html-vespa-index-inspect", "fields": {"questions": {"assign": ["What is vespa-index-inspect?", "How can I inspect indexed data?", "What is the output of vespa-index-inspect?", "What are the options for vespa-index-inspect?", "How do I show posting lists with vespa-index-inspect?", "What is the use of showpostings and dumpwords in vespa-index-inspect?", "How do I analyze a specific field with vespa-index-inspect?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/vespa-cmdline-tools.html-vespa-jvm-dumper", "fields": {"questions": {"assign": ["What is vespa-jvm-dumper?", "How do I produce JVM debugging information?", "How do I invoke vespa-jvm-dumper?", "What are the required arguments for vespa-jvm-dumper?", "Where will the output data be stored after running vespa-jvm-dumper?", "What can I debug with vespa-jvm-dumper?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/vespa-cmdline-tools.html-vespa-logctl", "fields": {"questions": {"assign": ["What is vespa-logctl?", "What is logcontrol file?", "What is level-mods?", "What is component-specification?", "How to set log level?", "What is the synopsis to show log levels?", "What is the synopsis to set log levels?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/vespa-cmdline-tools.html-vespa-logfmt", "fields": {"questions": {"assign": ["How to filter messages by log level?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/vespa-cmdline-tools.html-vespa-makefsa", "fields": {"questions": {"assign": ["What is vespa-makefsa?", "What is an FSA file?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/vespa-cmdline-tools.html-vespa-model-inspect", "fields": {"questions": {"assign": ["What is vespa-model-inspect?", "What can be inspected with vespa-model-inspect?", "What are the available commands of vespa-model-inspect?", "What are the available options of vespa-model-inspect?", "How to show hosts using vespa-model-inspect?", "How to show all services in a cluster using vespa-model-inspect?", "How to show all services using a certain config id using vespa-model-inspect?", "How to show all indexes for instances of a service on a given host using vespa-model-inspect?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/vespa-cmdline-tools.html-vespa-print-default", "fields": {"questions": {"assign": ["What does the internal script do?", "What information does the internal script provide?", "Is the internal script for end-users?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/vespa-cmdline-tools.html-vespa-proton-cmd", "fields": {"questions": {"assign": ["What is vespa-proton-cmd used for?", "How do I use vespa-proton-cmd?", "What are the available commands for vespa-proton-cmd?", "How do I locate the search node ADMIN RPC port?", "What is the command to trigger flush?", "What is the command to get the current proton state and its components?", "What is the command to estimate the cost of transaction log replay and flushes data structures?", "What is the command to get the current proton state?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/vespa-cmdline-tools.html-vespa-query-profile-dump-tool", "fields": {"questions": {"assign": ["What are the options for vespa-query-profile-dump-tool?", "How do I specify a custom output directory for vespa-query-profile-dump-tool?", "What are dimensions in Vespa query profiles?", "How do I specify dimension values for vespa-query-profile-dump-tool?", "What is the default output directory for vespa-query-profile-dump-tool?", "What is the format of dimension keys for vespa-query-profile-dump-tool?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/vespa-cmdline-tools.html-vespa-remove-index", "fields": {"questions": {"assign": ["What is vespa-remove-index tool?", "What does vespa-remove-index do?", "How can I delete Vespa's index data?", "Is vespa-remove-index reversible?", "Can recovered lost Vespa's indexes?", "What are the options for vespa-remove-index?", "How do I remove index data for a specific cluster using vespa-remove-index?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/vespa-cmdline-tools.html-vespa-route", "fields": {"questions": {"assign": ["What is vespa-route?", "What does vespa-route inspect?", "What options does vespa-route have?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/vespa-cmdline-tools.html-vespa-sentinel-cmd", "fields": {"questions": {"assign": ["What is vespa-sentinel-cmd?", "What is connectivity command?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/vespa-cmdline-tools.html-vespa-set-node-state", "fields": {"questions": {"assign": ["What is 'vespa-set-node-state'?", "What does setting user state do?", "What are the options for node state?", "What is 'vespa-set-node-state' used for?", "What is forced into maintenance state?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/vespa-cmdline-tools.html-vespa-start-configserver", "fields": {"questions": {"assign": ["What is vespa-start-services?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/vespa-cmdline-tools.html-vespa-start-services", "fields": {"questions": {"assign": ["How do I start all services on a node?", "What is vespa-start-services?", "How do I debug a failed start?", "What is the Safe Cluster Startup mode?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/vespa-cmdline-tools.html-vespa-stat", "fields": {"questions": {"assign": ["What is vespa-stat?", "How does vespa-stat work?", "What are the stages of vespa-stat?", "What options does vespa-stat have?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/vespa-cmdline-tools.html-vespa-status-filedistribution", "fields": {"questions": {"assign": ["Where should vespa-status-filedistribution be run?", "What are the options for vespa-status-filedistribution?", "What information does vespa-status-filedistribution retrieve from the config server?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/vespa-cmdline-tools.html-vespa-stop-configserver", "fields": {"questions": {"assign": ["How do I stop a config server on a node?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/vespa-cmdline-tools.html-vespa-stop-services", "fields": {"questions": {"assign": ["How do I stop Vespa services on a node?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/vespa-cmdline-tools.html-vespa-summary-benchmark", "fields": {"questions": {"assign": ["What is rpc docsum interface?", "What is VespaSummaryBenchmark?", "What does VespaSummaryBenchmark test?", "What does VespaSummaryBenchmark benchmark?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/vespa-cmdline-tools.html-vespa-visit", "fields": {"questions": {"assign": ["What is vespa-visit?", "How does vespa-visit work?", "How to use vespa-visit to migrate documents?", "What is the purpose of remove-entries?", "How to include remove-entries in vespa-visit?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/vespa-cmdline-tools.html-copies-all-data-in-the-local-cluster,-routing-it-to-the-remote-*mycluster*", "fields": {"questions": {"assign": ["How to specify datahandler?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/vespa-cmdline-tools.html-limit-to-'music'-documents-only", "fields": {"questions": {"assign": ["What is the datahandler?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/vespa-cmdline-tools.html-limit-to-all-documents-for-user-'1234'", "fields": {"questions": {"assign": ["What is a dump visitor used for?", "What are the target types you can specify?", "What is a TCP socket?", "What options can you specify in vespa-visit command?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/vespa-cmdline-tools.html-vespa-visit-target", "fields": {"questions": {"assign": ["What is vespa-visit-target used for?"]}}} -{"update": "id:open-p:paragraph::open/en/vespa-feed-client.html-", "fields": {"questions": {"assign": ["What is JSONL?"]}}} -{"update": "id:open-p:paragraph::open/en/vespa-feed-client.html-java-library", "fields": {"questions": {"assign": ["Where can I find the Vespa Java library?", "What is Maven Central?"]}}} -{"update": "id:open-p:paragraph::open/en/vespa-feed-client.html-command-line-client", "fields": {"questions": {"assign": ["How to install vespa-clients package?", "Where to download vespa-feed-client zip?"]}}} -{"update": "id:open-p:paragraph::open/en/vespa-feed-client.html-enable-feed-endpoint-in-vespa", "fields": {"questions": {"assign": ["What is recommended for security?", "What is the purpose of ca-certificates-file?"]}}} -{"update": "id:open-p:paragraph::open/en/vespa-feed-client.html-using-the-client", "fields": {"questions": {"assign": ["Where can I find Javadoc for the programmatic API?", "What is the command for running a bandwidth test?"]}}} -{"update": "id:open-p:paragraph::open/en/vespa-feed-client.html-example-java", "fields": {"questions": {"assign": ["What is the Maven dependency for vespa-feed-client?", "Where can I find code examples for vespa-feed-client?", "What is the latest version of the vespa-feed-client dependency?"]}}} -{"update": "id:open-p:paragraph::open/en/vespa-feed-client.html-example-command-line", "fields": {"questions": {"assign": ["What is the Vespa feed client?", "What are the parameters for the Vespa feed client?", "What is the format for document operations in Vespa feed client?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/vespa-gpu-container.html-", "fields": {"questions": {"assign": ["What is the NVIDIA container toolkit installation guide?", "How does Vespa support using GPUs?", "What is special configuration required for GPU(s) inside container?", "Which container engines are supported by Vespa?", "How to make GPU(s) available inside container?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/vespa-gpu-container.html-configuration-steps", "fields": {"questions": {"assign": ["How to install Nvidia drivers?", "How to expose GPU to container?", "How to configure ONNX runtime?"]}}} -{"update": "id:open-p:paragraph::open/en/vespa-quick-start-java.html-", "fields": {"questions": {"assign": ["How to install Vespa with Docker?", "What does the guide show?"]}}} -{"update": "id:open-p:paragraph::open/en/vespa-quick-start-java.html-running-vespa-in-docker", "fields": {"questions": {"assign": ["How to start a Vespa Docker container?"]}}} -{"update": "id:open-p:paragraph::open/en/vespa-quick-start-java.html-next-steps", "fields": {"questions": {"assign": ["How to get started with Vespa?", "What is the link for getting started with Vespa?"]}}} -{"update": "id:open-p:paragraph::open/en/operations-selfhosted/using-kubernetes-with-vespa.html-", "fields": {"questions": {"assign": ["How to install Vespa on Kubernetes?", "What is VESPA_SAMPLE_APPS?", "What is the statefulset.yml?", "How to start port forwarding?"]}}} -{"update": "id:open-p:paragraph::open/en/vespa-quick-start.html-", "fields": {"questions": {"assign": ["How to install Vespa with Docker?", "What does the guide show?"]}}} -{"update": "id:open-p:paragraph::open/en/vespa-quick-start.html-running-vespa-in-docker", "fields": {"questions": {"assign": ["How to set local target?", "What is Vespa Docker container?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/vespa-set-metrics-reference.html-clustercontroller-metrics", "fields": {"questions": {"assign": ["What is cluster-controller.down.count?", "What is cluster-controller.up.count?", "What does cluster-controller.resource_usage.nodes_above_limit capture?", "What is cluster-controller.is-master?", "What does cluster-controller.remote-task-queue.size measure?", "What is reindexing.progress?", "What is cluster-controller.busy-tick-time-ms?", "What is cluster-controller.initializing.count?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/vespa-set-metrics-reference.html-configserver-metrics", "fields": {"questions": {"assign": ["What is configserver.requests?", "What is the unit of configserver.latency?", "What is configserver.zkConnections?", "What is configserver.zkAvgLatency?", "What is configserver.zkMaxLatency?", "What is configserver.cacheConfigElems?", "What is configserver.failedRequests?", "What is configserver.delayedResponses?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/vespa-set-metrics-reference.html-container-metrics", "fields": {"questions": {"assign": ["What is mem.heap.total?", "What is http.status.2xx?", "What is search_connections?", "What is error.backend_communication_error?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/vespa-set-metrics-reference.html-distributor-metrics", "fields": {"questions": {"assign": ["What is vds.idealstate.buckets_rechecking?", "What is the unit of vds.distributor.puts.latency?", "What is vds.distributor.gets.ok?", "What is vds.distributor.docsstored?", "What is vds.idealstate.max_observed_time_since_last_gc_sec?", "What is vds.distributor.puts.failures.total?", "What is vds.distributor.visitor.latency?", "What is vds.bouncer.clock_skew_aborts?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/vespa-set-metrics-reference.html-logd-metrics", "fields": {"questions": {"assign": ["What is logd.processed.lines?", "What does logd.processed.lines count?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/vespa-set-metrics-reference.html-nodeadmin-metrics", "fields": {"questions": {"assign": ["What does endpoint.certificate.expiry.seconds measure?", "What is node-certificate.expiry.seconds?", "What is the unit for endpoint.certificate.expiry.seconds?", "What is N/A in suffixes?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/vespa-set-metrics-reference.html-sentinel-metrics", "fields": {"questions": {"assign": ["What is sentinel.restarts in Vespa?", "What is sentinel.totalRestarts?", "What is sentinel.uptime?", "What is sentinel.running?", "What does sentinel.restarts count?", "What is sentinel.totalRestarts' unit?", "What is the purpose of 'suffixes' in Vespa metrics?"]}}} -{"update": "id:open-p:paragraph::open/en/reference/vespa-set-metrics-reference.html-slobrok-metrics", "fields": {"questions": {"assign": ["What is slobrok.heartbeats.failed?", "What unit is used for slobrok.heartbeats.failed?", "What does slobrok.heartbeats.failed count?", "What does slobrok.missing.consensus measure?", "What is the unit of slobrok.missing.consensus?", "What does slobrok.missing.consensus count?", "What is the definition of slobrok.heartbeats.failed?", "What is the definition of slobrok.missing.consensus?"]}}} -{"update": "id:open-p:paragraph::open/en/vespa7-release-notes.html-", "fields": {"questions": {"assign": ["What are the changes between Vespa 6 and 7?", "What is the purpose of major versions in Vespa?", "What is considered breaking compatibility?", "How many tasks are necessary for compatibility with Vespa 7?", "What is meant by removal of deprecated functionality?", "What should be done to be compatible with Vespa 7?", "How can deprecation warnings be emitted?", "What is the significance of changes to defaults in Vespa 7?"]}}} -{"update": "id:open-p:paragraph::open/en/vespa7-release-notes.html-changed-defaults", "fields": {"questions": {"assign": ["What are the changes in stemming?", "What is the new default linguistics component?", "What is the new default format for the Java HTTP client?", "What is ranking.softtimeout.enable status?", "What is the default return format for vespa-visit and vespa-get?"]}}} -{"update": "id:open-p:paragraph::open/en/vespa7-release-notes.html-jdk-version", "fields": {"questions": {"assign": ["Why must Java components be rebuilt?", "What is JDK 11?"]}}} -{"update": "id:open-p:paragraph::open/en/vespa7-release-notes.html-removed-java-api's", "fields": {"questions": {"assign": ["What is deprecated in Vespa 6?", "What happens if you get deprecation warnings?", "How to fix Vespa API deprecation?"]}}} -{"update": "id:open-p:paragraph::open/en/vespa7-release-notes.html-container-runtime-environment", "fields": {"questions": {"assign": ["Which artifacts are no longer provided?"]}}} -{"update": "id:open-p:paragraph::open/en/vespa7-release-notes.html-removed-http-api's", "fields": {"questions": {"assign": ["What HTTP APIs are removed?", "Which API replaced the legacy HTTP APIs?"]}}} -{"update": "id:open-p:paragraph::open/en/vespa7-release-notes.html-removed-http-api-parameters", "fields": {"questions": {"assign": ["What HTTP parameters were removed?", "What is the replacement for defidx parameter?"]}}} -{"update": "id:open-p:paragraph::open/en/vespa7-release-notes.html-removed-command-line-tools", "fields": {"questions": {"assign": ["What command line tools are removed?"]}}} -{"update": "id:open-p:paragraph::open/en/vespa7-release-notes.html-removed-settings-from-schemas", "fields": {"questions": {"assign": ["What settings are removed?", "What is header setting for?", "What is body setting for?"]}}} -{"update": "id:open-p:paragraph::open/en/vespa7-release-notes.html-removed-constructs-from-services.xml", "fields": {"questions": {"assign": ["What tags are removed?", "What attributes are removed?", "What is the replacement for ?"]}}} -{"update": "id:open-p:paragraph::open/en/vespa7-release-notes.html-removed-metrics", "fields": {"questions": {"assign": ["What are the metrics removed?", "What is the replacement for the free/used/totalMemoryBytes metric?", "What is the replacement for http.in.bytes?", "What is the replacement for http.requests?", "What is the replacement for http.latency?", "What is VDS filestor.spi?", "What are the metrics that have different structure and names in new namespace?"]}}} -{"update": "id:open-p:paragraph::open/en/vespa7-release-notes.html-empty-fields", "fields": {"questions": {"assign": ["What happens to empty fields?", "Which Vespa version excludes empty fields?"]}}} -{"update": "id:open-p:paragraph::open/en/vespa7-release-notes.html-allowed-characters-in-request-uris", "fields": {"questions": {"assign": ["What characters require percent-encoding in Vespa 7?", "What did Vespa 6 allow for special characters?"]}}} -{"update": "id:open-p:paragraph::open/en/vespa7-release-notes.html-changes-to-the-default-json-result-format", "fields": {"questions": {"assign": ["What is the position field?", "What is the default JSON query result format?", "What was the rendering format for position fields in Vespa 6?"]}}} -{"update": "id:open-p:paragraph::open/en/vespa7-release-notes.html-renamed-metrics", "fields": {"questions": {"assign": ["What are renamed Vespa metrics?", "What metric is related to error.backend_communication_error?", "What metric is related to content.proton.memoryusage.max?"]}}} -{"update": "id:open-p:paragraph::open/en/vespa8-geo-migration-guide.html-", "fields": {"questions": {"assign": ["What is Vespa 8 release notes?", "How to migrate from Vespa 7 to Vespa 8 with position fields?", "For which application is this guide relevant?"]}}} -{"update": "id:open-p:paragraph::open/en/vespa8-geo-migration-guide.html-step-1:-upgrade-to-vespa-8-in-geo-legacy-mode", "fields": {"questions": {"assign": ["What is legacy-v7-json-rendering?", "What is the root tag in services.xml?", "What is the geo-position-rendering in services.xml?"]}}} -{"update": "id:open-p:paragraph::open/en/vespa8-geo-migration-guide.html-step-2:-result-rendering", "fields": {"questions": {"assign": ["What is the Vespa 7 result format for position field?", "What is the Vespa 8 result format for position field?", "What is distance rank feature in Vespa 7?", "How to output distance rank feature in Vespa 8?", "What is the format for rendering position field in Vespa 7?", "What is the format for rendering position field in Vespa 8?"]}}} -{"update": "id:open-p:paragraph::open/en/vespa8-geo-migration-guide.html-step-3:-query-api", "fields": {"questions": {"assign": ["How do I use geoLocation in YQL?"]}}} -{"update": "id:open-p:paragraph::open/en/vespa8-geo-migration-guide.html-step-4:-feeding-format-and-/document/v1/-api", "fields": {"questions": {"assign": ["What is the recommended feeding format?", "What is the result format when using GET / VISIT?", "What are the changes to the feeding format in Vespa 8?", "What is the new format for myfield in Vespa 8?", "What is the format of the myfield attribute in query results?", "What are the differences between the Vespa 7 feeding format and the new format?", "How has the Vespa 8 format for the myfield attribute changed compared to Vespa 7?", "What is the expected impact of switching to the recommended format on Vespa 8?"]}}} -{"update": "id:open-p:paragraph::open/en/vespa8-release-notes.html-", "fields": {"questions": {"assign": ["What are the changes between Vespa 7 and 8?", "What marks a major Vespa release?", "Is Vespa 8 as safe as Vespa 7?", "Will there be further releases of Vespa 7?", "What is introduced in Vespa minor versions?"]}}} -{"update": "id:open-p:paragraph::open/en/vespa8-release-notes.html-overview", "fields": {"questions": {"assign": ["What are the compatibility breaking changes in Vespa 8?", "What are the categories of changes?", "What are the changes to default behaviour?", "What are the Java API changes?", "What are the HTTP API changes?", "What are the security related changes?"]}}} -{"update": "id:open-p:paragraph::open/en/vespa8-release-notes.html-changed-defaults", "fields": {"questions": {"assign": ["What changes may break clients?", "How to avoid breaking changes?", "What defaults have changed?", "What fields rendering has changed?", "What is default fieldset?"]}}} -{"update": "id:open-p:paragraph::open/en/vespa8-release-notes.html-removed-settings-from-schemas", "fields": {"questions": {"assign": ["What settings were removed from schema?", "What was the replacement for 'huge' attribute?", "Why was compression setting removed?", "What is the recommended alternative for weightedset field type?", "Why is using 'float' as key in a map or set no longer supported?", "How to write array types like 'string[]'?", "When do rank functions with the same name create conflict?", "Why should the schema and document have the same name?"]}}} -{"update": "id:open-p:paragraph::open/en/vespa8-release-notes.html-tensorflow-import", "fields": {"questions": {"assign": ["What ML model format is preferred?", "What does Vespa 8 remove support for?", "What works for ranking and evaluation?", "Does Vespa provide TensorFlow conversion?", "What tools does ONNX contain?", "How to convert models to ONNX?"]}}} -{"update": "id:open-p:paragraph::open/en/vespa8-release-notes.html-changed-semantics-in-services.xml", "fields": {"questions": {"assign": ["What is ?", "What happens if node count is lower than redundancy?"]}}} -{"update": "id:open-p:paragraph::open/en/vespa8-release-notes.html-removed-constructs-from-services.xml", "fields": {"questions": {"assign": ["What elements are removed?", "What is filedistribution?", "What is the maxentries recommended?"]}}} -{"update": "id:open-p:paragraph::open/en/vespa8-release-notes.html-*application/*-folder-support-removed", "fields": {"questions": {"assign": ["What folder should the schemas be in?"]}}} -{"update": "id:open-p:paragraph::open/en/vespa8-release-notes.html-*searchdefinitions/*-folder-is-deprecated", "fields": {"questions": {"assign": ["What is the new folder for search schemas?", "What causes a deprecation warning?"]}}} -{"update": "id:open-p:paragraph::open/en/vespa8-release-notes.html-removed-java-packages", "fields": {"questions": {"assign": ["What is com.yahoo.vespa.curator?", "What happened to com.yahoo.statistics?", "What is com.yahoo.documentapi.messagebus?"]}}} -{"update": "id:open-p:paragraph::open/en/vespa8-release-notes.html-removed-java-classes-and-methods", "fields": {"questions": {"assign": ["What should be fixed before migrating to Vespa 8?", "What classes are moved to Vespa internal packages?", "What packages contain classes moved to internal packages?", "What is the migration advice for ConfigGetter class?", "What is the migration advice for DocprocExecutor class?", "What is the migration advice for DocumentAccess class?", "What method was removed in Vespa?"]}}} -{"update": "id:open-p:paragraph::open/en/vespa8-release-notes.html-breaking-changes-to-java-apis", "fields": {"questions": {"assign": ["What is the replacement for ListenableFuture?", "What is the replacement for HttpSearchResponse.waitableRender()?", "What is the replacement for Guava?", "What methods were removed from VisitorStatistics?", "What should not be set by the client in documentapi?", "What types were removed from JsonRenderer?", "What collection types were removed in ranking expression rule?", "What should documentation of deprecated types include?"]}}} -{"update": "id:open-p:paragraph::open/en/vespa8-release-notes.html-removed-support-for-built-in-xml-factories", "fields": {"questions": {"assign": ["What XML factories were supported?", "What providers were injected historically?", "What alternatives are there now?", "What XML processing is no longer supported?"]}}} -{"update": "id:open-p:paragraph::open/en/vespa8-release-notes.html-deprecated-java-apis", "fields": {"questions": {"assign": ["Which APIs are being deprecated and why?", "What are the replacements for deprecated APIs?", "How can I reduce future maintenance cost?"]}}} -{"update": "id:open-p:paragraph::open/en/vespa8-release-notes.html-jdk-version", "fields": {"questions": {"assign": ["What is the JDK version upgrade?", "How do I ensure compatibility?"]}}} -{"update": "id:open-p:paragraph::open/en/vespa8-release-notes.html-changes-to-provided-maven-artifacts", "fields": {"questions": {"assign": ["What is Guava?", "Which APIs have been removed from Guava?", "What Maven artifacts are no longer provided?", "What is the scope to embed an artifact?", "How can I check for dependencies?", "How to remove a tansitively included dependency?"]}}} -{"update": "id:open-p:paragraph::open/en/vespa8-release-notes.html-removed-http-api-parameters", "fields": {"questions": {"assign": ["What HTTP API parameters are removed from query API?", "How do you replace pos.ll parameter?", "What is pos.attribute parameter?", "Why has support for pos.bb been removed?"]}}} -{"update": "id:open-p:paragraph::open/en/vespa8-release-notes.html-vespa-http-client", "fields": {"questions": {"assign": ["What is vespa-http-client?", "What replaces vespa-http-client?", "What API does vespa-feed-client use?", "Will the rest API used by vespa-http-client be available in Vespa 8?", "Can an old client be used to feed to a Vespa 8 installation?", "Will vespa-http-client receive updates in Vespa 8?", "Why should users migrate from vespa-http-client?", "What will be supported as part of Vespa 8?"]}}} -{"update": "id:open-p:paragraph::open/en/vespa8-release-notes.html-removed-or-renamed-metrics", "fields": {"questions": {"assign": ["What metrics are renamed?", "What metrics are removed?"]}}} -{"update": "id:open-p:paragraph::open/en/vespa8-release-notes.html-exact-matching-of-document-types-in-selection-language", "fields": {"questions": {"assign": ["What is the document selection language?", "What is exact matching?", "What is inheritance matching?"]}}} -{"update": "id:open-p:paragraph::open/en/vespa8-release-notes.html-strict-mode-enabled-by-default", "fields": {"questions": {"assign": ["What is strict mode?", "How is strict mode enabled by default?", "What is the strict-mode attribute?", "Where can I find documentation on strict-mode?", "What is the version that enables strict mode by default?", "How do I configure strict-mode in services.xml?"]}}} -{"update": "id:open-p:paragraph::open/en/vespa8-release-notes.html-request-headers-controlling-remote-host/port-in-access-log", "fields": {"questions": {"assign": ["What does X-Forwarded-For header do?", "What happens to the y-ra header?"]}}} -{"update": "id:open-p:paragraph::open/en/vespa8-release-notes.html-rpms", "fields": {"questions": {"assign": ["What OS replaces CentOS Linux 7?", "Where are Vespa RPMs built?", "What do I need to upgrade to Vespa RPMs?"]}}} -{"update": "id:open-p:paragraph::open/en/vespa8-release-notes.html-oci-containers-(docker-containers)", "fields": {"questions": {"assign": ["What is the base image used for Vespa containers?", "What is the name of the new base image?", "What kernel version is required for Vespa's container image?", "What is the reason for changing the base image?", "What should be done if using Vespa's container image?"]}}} -{"update": "id:open-p:paragraph::open/en/vespa8-release-notes.html-unknown-rank-profiles", "fields": {"questions": {"assign": ["What happens when rank profile doesn't exist?", "What is the requirement for using rank profile in multiple schemas?", "How to ensure a common rank profile in multiple schemas?"]}}} -{"update": "id:open-p:paragraph::open/en/vespa8-release-notes.html-unknown-summary-classes", "fields": {"questions": {"assign": ["What happens when summary class is non-existent?", "How to ensure summary class in multiple schemas?"]}}} -{"update": "id:open-p:paragraph::open/en/vespa8-release-notes.html-the-\"qrserver\"-service-name", "fields": {"questions": {"assign": ["What is qrserver?"]}}} -{"update": "id:open-p:paragraph::open/en/vespa8-release-notes.html-container-access-logs", "fields": {"questions": {"assign": ["Where are the container access logs?", "What is the new location for access logs?", "What is the default compression format for access logs?"]}}} -{"update": "id:open-p:paragraph::open/en/vespa8-release-notes.html-onnx-output-in-summary-features", "fields": {"questions": {"assign": ["What is ONNX?", "What is new in Vespa 8?", "What is a summary feature?", "What is the summary feature name?", "What was the previous summary feature name?", "How does Vespa 8 define ONNX model output?", "What is different in ONNX model output definition in Vespa 8?", "What is the feature name for ONNX model output in Vespa 8?"]}}} -{"update": "id:open-p:paragraph::open/en/vespa8-release-notes.html-changes-in-rankfeatures", "fields": {"questions": {"assign": ["What changes were made to rank-features in Vespa 8?"]}}} -{"update": "id:open-p:paragraph::open/en/vespa8-release-notes.html-the-\"storage\"-message-bus-routing-policy-is-removed", "fields": {"questions": {"assign": ["What is the 'storage' routing policy?", "What has happened to the 'storage' routing policy in Vespa 7?", "What will happen to clients using 'storage' routing policy in Vespa 8?", "What should clients replace 'storage' routing policy with?", "How does the 'content' routing policy behave?", "How can clients update their routing policy?", "When was the 'storage' routing policy removed?", "What is the difference between 'storage' and 'content' routing policy?"]}}} -{"update": "id:open-p:paragraph::open/en/vespa8-release-notes.html-vespa-dispatch-bin-process-is-removed", "fields": {"questions": {"assign": ["What is the dispatch functionality?", "Where was the dispatch functionality moved?", "What process was removed?", "What was the default setting at Vespa-7.109.10?", "What does dispatch do between container and content nodes?", "What is the purpose of the vespa-dispatch-bin process?", "What range are dynamically allocated for the content cluster?"]}}} -{"update": "id:open-p:paragraph::open/en/vespa8-release-notes.html-yql-format", "fields": {"questions": {"assign": ["What is the YQL statement output format?", "Are annotations enclosed in brackets?", "Is quoting annotation name necessary?", "Is semicolon termination optional?", "What is the new change in YQL output?", "What is a valid input for annotations?", "What is a near query in text matching?"]}}} -{"update": "id:open-p:paragraph::open/en/vespa8-release-notes.html-upgrade-procedure", "fields": {"questions": {"assign": ["What has been simplified in Vespa 8 during upgrades?", "How do you upgrade the config servers?", "What command is used to set all nodes in standalone mode?", "What must be done before upgrading config servers?", "What is the upgrade procedure when upgrading from Vespa 7.x to Vespa 7.y?", "How do you install the new Vespa version on config servers?", "What commands are used to redeploy and activate an application?"]}}} -{"update": "id:open-p:paragraph::open/en/visiting.html-", "fields": {"questions": {"assign": ["What is visiting in Vespa?", "How is visiting used?", "Are visits time sensitive?", "What is cloning applications and data?", "Does visiting have snapshot isolation?", "Where can I find information on how visiting works?"]}}} -{"update": "id:open-p:paragraph::open/en/visiting.html-data-export", "fields": {"questions": {"assign": ["How to export data?", "What is stdout?", "How to visit entire corpus?", "How to efficiently iterate data?"]}}} -{"update": "id:open-p:paragraph::open/en/visiting.html-selection", "fields": {"questions": {"assign": ["What is a document selection expression?", "Where can I find examples of document selection expressions?", "How do I select documents based on ID?", "What is the Vespa CLI cheat sheet?"]}}} -{"update": "id:open-p:paragraph::open/en/visiting.html-fields", "fields": {"questions": {"assign": ["How do you set a fieldset?", "What are common use cases for a fieldset?", "What does the shorthand [document] do?", "What does the shorthand [all] do?"]}}} -{"update": "id:open-p:paragraph::open/en/visiting.html-timestamp-ranges", "fields": {"questions": {"assign": ["How do you specify timestamp range for Document V1?", "How do you specify timestamp range for Vespa CLI?", "What does setting a timestamp range do?", "Does setting a timestamp range imply snapshot isolation?", "Can concurrent modifications affect the set of documents returned?", "What updates the document timestamp?"]}}} -{"update": "id:open-p:paragraph::open/en/visiting.html-reprocessing", "fields": {"questions": {"assign": ["What is reprocessing used for?", "How can document identifiers be changed?", "What can be reindexed with reprocessing?", "How is reprocessed data written back?"]}}} -{"update": "id:open-p:paragraph::open/en/visiting.html-2.-select-documents", "fields": {"questions": {"assign": ["What is reprocessing?", "How to select documents for reprocessing?", "What is selection criteria?"]}}} -{"update": "id:open-p:paragraph::open/en/visiting.html-3.-set-route", "fields": {"questions": {"assign": ["What is Messagebus route?", "How to send documents to default route?", "Where can I find more details about Message Bus Routing?"]}}} -{"update": "id:open-p:paragraph::open/en/visiting.html-4.-reprocess", "fields": {"questions": {"assign": ["What is reprocessing?"]}}} -{"update": "id:open-p:paragraph::open/en/visiting.html-notes", "fields": {"questions": {"assign": ["What adjustments must be made?", "How are documents sent over the network?", "Can one message contain more than one document type?"]}}} -{"update": "id:open-p:paragraph::open/en/visiting.html-request-handling", "fields": {"questions": {"assign": ["What are targets in visit command?", "What is vespa-visit?", "What is vespa-visit-target?"]}}} -{"update": "id:open-p:paragraph::open/en/visiting.html-client", "fields": {"questions": {"assign": ["What happens if the selection criteria maps to a specific set of buckets?", "What happens if the selection criteria does not map to a specific set of buckets?", "How does the client know if all buckets have been visited?", "What options does the client have for bucket visiting order?"]}}} -{"update": "id:open-p:paragraph::open/en/visiting.html-distributor", "fields": {"questions": {"assign": ["What are distributors?", "How does the distributor reply to requests?", "How does the progress counter work?"]}}} -{"update": "id:open-p:paragraph::open/en/visiting.html-content-node", "fields": {"questions": {"assign": ["What is a visitor layer?", "How does the default iterator work?"]}}} -{"update": "id:open-p:paragraph::open/en/visiting.html-not-exporting-all-documents", "fields": {"questions": {"assign": ["What is a bucket space?", "What are global documents?", "What is the bucket-space parameter?", "How are documents co-located?", "What happens when dumping all documents?", "What is dumped count?"]}}} -{"update": "id:open-p:paragraph::open/en/visiting.html-visiting-performance-is-poor", "fields": {"questions": {"assign": ["What causes slow visiting process?", "How to identify client-side bottleneck?", "How to identify if visiting processing is the bottleneck?", "What causes visiting performance issues?", "How are non-attribute fields fetched?", "What fields may cause visiting to be limited by disk performance?", "How can you optimize visiting performance?"]}}} -{"update": "id:open-p:paragraph::open/en/visiting.html-visitor-operations-are-hanging", "fields": {"questions": {"assign": ["What causes visit operation stall?", "What happens when replicas are still merging?"]}}} -{"update": "id:open-p:paragraph::open/en/visiting.html-handshake-failed", "fields": {"questions": {"assign": ["Why does Vespa visit connect back?", "What causes HANDSHAKE_FAILED error?", "How to fix HANDSHAKE_FAILED error?"]}}} -{"update": "id:open-p:paragraph::open/en/xgboost.html-", "fields": {"questions": {"assign": ["What is XGBoost?"]}}} -{"update": "id:open-p:paragraph::open/en/xgboost.html-exporting-models-from-xgboost", "fields": {"questions": {"assign": ["What is XGBoost?", "What is dump_format?", "What is dump_model?", "What is split_condition?", "What is fmap?", "What is nodeid?", "What is leaf?"]}}} -{"update": "id:open-p:paragraph::open/en/xgboost.html-!/usr/local/bin/python3", "fields": {"questions": {"assign": ["What is xgboost?", "What is dtrain in xgboost?", "What is param in xgboost?", "What is bst.train in xgboost?", "What is the dump_model function?", "What is the input format for training data?", "What is the 'ranking' objective?", "What is the xgboost training notebook?"]}}} -{"update": "id:open-p:paragraph::open/en/xgboost.html-feature-mappings-from-xgboost-to-vespa", "fields": {"questions": {"assign": ["What is XGBoost?", "How is XGBoost trained?", "What is feature mapping?", "What is the format of feature-map.txt?", "What does 'i' mean in feature mapping?", "What does 'q' mean in feature mapping?", "What does 'int' mean in feature mapping?", "What is the sample demo for binary classification?"]}}} -{"update": "id:open-p:paragraph::open/en/xgboost.html-importing-xgboost-models", "fields": {"questions": {"assign": ["How to import XGBoost model?", "Where to put XGBoost model for Vespa?", "What is the directory structure for models in Vespa?", "Can an application package have multiple models?", "How to deploy remote models in Vespa?"]}}} -{"update": "id:open-p:paragraph::open/en/xgboost.html-ranking-with-xgboost-models", "fields": {"questions": {"assign": ["What is the xgboost ranking feature?", "How is xgboost used in Vespa?", "What is the format of the Vespa xgboost schema?", "How is the Vespa ranking profile used?", "How do you control the number of documents exposed to the model?"]}}} -{"update": "id:open-p:paragraph::open/en/xgboost.html-xgboost-models", "fields": {"questions": {"assign": ["What are the Vespa objective types?", "How can XGBoost be used in Vespa?", "What is the sigmoid function used for?", "What is the XGBoost classifier?", "How to import the XGBoost model?", "What is the dump model format?", "What is the predict_proba function equivalent in Vespa?"]}}} -{"update": "id:open-p:paragraph::open/en/xgboost.html-debugging-vespa-inference-score-versus-xgboost-predict-score", "fields": {"questions": {"assign": ["What information is lost when dumping XGBoost models?", "How should features be scraped from Vespa for training?", "What precision should be used when reading Vespa features for training?", "Why might text matching features differ in a distributed setting?"]}}} -{"update": "id:open-p:paragraph::open/en/llms-local.html-valid-llm-models", "fields": {"questions": {"assign": ["What is GGUF", "What LLMs does Vespa support for local inference", "Does Vespa support Llama?", "Does vespa support Mistral?", "Does Vespa support Mixtral", "Vespa uses llama.cpp", "How to find supported LLM models with Vespa local inference mode"]}}} -{"update": "id:open-p:paragraph::open/en/llms-local.html-using-gpus", "fields": {"questions": {"assign": ["How to use GPU with LLM"]}}} -{"update": "id:open-p:paragraph::open/en/llms-local.html-local-llm-configuration", "fields": {"questions": {"assign": ["How to configure local LLM inference", "What impact has contextSize on inference performance"]}}} -{"update": "id:open-p:paragraph::open/en/llms-in-vespa.html", "fields": {"questions": {"assign": ["How to use LLMs", "Can you integrate with OpenAi LLMs", "Can you use Anthropic LLMs?"]}}} -{"update": "id:open-p:paragraph::open/en/llms-in-vespa.html-using-llms", "fields": {"questions": {"assign": ["How to use LLMs", "How to pass the LLM API key", "API KEY"]}}} -{"update": "id:open-p:paragraph::open/en/llms-in-vespa.html-streaming-with-server-sent-events", "fields": {"questions": {"assign": ["How to stream the LLM response tokens?", "What are Server-Sent Events?", "How to consume the server side event stream?"]}}} -{"update": "id:open-p:paragraph::open/en/llms-in-vespa.html-llm-parameters", "fields": {"questions": {"assign": ["How to use LLMs", "How to pass the LLM API key", "API KEY"]}}} \ No newline at end of file diff --git a/redirects.yml b/redirects.yml deleted file mode 100644 index fce3c4301d..0000000000 --- a/redirects.yml +++ /dev/null @@ -1,360 +0,0 @@ -# Copyright Vespa.ai. All rights reserved. -# Generated redirect mappings from Jekyll redirect_from plugin -# Format: /old-path: /new-path - -/cloud/node-resources: /en/performance/node-resources.md -/cloud/topology-and-resizing: /en/performance/topology-and-resizing.md -/en/access-logging.html: /en/operations/access-logging.html -/en/api.html: /en/reference/api/api.html -/en/application-packages.html: /en/basics/applications.html -/en/applications.html: /en/basics/applications.html -/en/approximate-nn-hnsw: /en/querying/approximate-nn-hnsw.md -/en/attributes.html: /en/content/attributes.html -/en/binarizing-vectors: /en/rag/binarizing-vectors.md -/en/build-install-vespa.html: /en/operations/self-managed/build-install.html -/en/bundle-plugin.html: /en/applications/bundles.html -/en/chained-components.html: /en/applications/chaining.html -/en/cloud/archive-guide: /en/operations/archive/archive-guide.md -/en/cloud/archive-guide-aws: /en/operations/archive/archive-guide-aws.md -/en/cloud/archive-guide-gcp: /en/operations/archive/archive-guide-gcp.md -/en/cloud/automated-deployments.html: /en/operations/automated-deployments.html -/en/cloud/autoscaling.html: /en/operations/autoscaling.html -/en/cloud/benchmarking.html: /en/performance/benchmarking-cloud.html -/en/cloud/cloning-applications-and-data.html: /en/operations/cloning.md -/en/cloud/data-management-and-backup: /en/operations/data-management.html -/en/cloud/deleting-applications: /en/operations/deleting-applications.md -/en/cloud/deployment-patterns: /en/operations/deployment-patterns.md -/en/cloud/enclave/archive: /en/operations/enclave/archive.md -/en/cloud/enclave/aws-architecture: /en/operations/enclave/aws-architecture.md -/en/cloud/enclave/aws-deploy-an-application: /en/operations/enclave/aws-getting-started.md -/en/cloud/enclave/aws-getting-started: /en/operations/enclave/aws-getting-started.md -/en/cloud/enclave/azure-architecture: /en/operations/enclave/azure-architecture.md -/en/cloud/enclave/azure-deploy-an-application: /en/operations/enclave/azure-getting-started.md -/en/cloud/enclave/azure-getting-started: /en/operations/enclave/azure-getting-started.md -/en/cloud/enclave/enclave: /en/operations/enclave/enclave.md -/en/cloud/enclave/gcp-architecture: /en/operations/enclave/gcp-architecture.md -/en/cloud/enclave/gcp-deploy-an-application: /en/operations/enclave/gcp-getting-started.md -/en/cloud/enclave/gcp-getting-started: /en/operations/enclave/gcp-getting-started.md -/en/cloud/enclave/operations: /en/operations/enclave/operations.md -/en/cloud/environments: /en/operations/environments.html -/en/cloud/getting-started-java.html: /en/basics/deploy-an-application-java.html -/en/cloud/getting-started-shell.html: /en/basics/deploy-an-application-shell.html -/en/cloud/getting-started.html: /en/basics/deploy-an-application.html -/en/cloud/http-best-practices: /en/clients/http-best-practices.md -/en/cloud/index-bootstrap.html: /en/writing/initial-batch-feed.html -/en/cloud/initial-batch-feed.html: /en/writing/initial-batch-feed.html -/en/cloud/memory-visualizer: /en/performance/memory-visualizer.md -/en/cloud/migrating-from-elastic-search-to-vespa.html: /en/learn/migrating-from-elastic-search.html -/en/cloud/migrating-to-cloud.html: /en/learn/migrating-to-cloud.md -/en/cloud/model-hub.html: /en/rag/model-hub.html -/en/cloud/monitoring.html: /en/operations/monitoring.html -/en/cloud/multi-currency-filtering.html: /en/modules/e-commerce/multi-currency-filtering.html -/en/cloud/node-resources.html: /en/performance/node-resources.md -/en/cloud/notifications: /en/operations/notifications.md -/en/cloud/private-endpoints.html: /en/operations/private-endpoints.html -/en/cloud/production-deployment.html: /en/operations/production-deployment.html -/en/cloud/routing.html: /en/operations/endpoint-routing.html -/en/cloud/security/cloudflare-workers: /en/security/cloudflare-workers.md -/en/cloud/security/guide: /en/security/guide.md -/en/cloud/security/secret-store: /en/security/secret-store.md -/en/cloud/security/security: /en/security/security.md -/en/cloud/security/whitepaper: /en/security/whitepaper.md -/en/cloud/tenant-apps-instances.html: /en/learn/tenant-apps-instances.html -/en/cloud/topology-and-resizing.html: /en/performance/topology-and-resizing.md -/en/cloud/zones.html: /en/operations/zones.html -/en/cloudconfig/application-packages.html: /en/basics/applications.html -/en/cloudconfig/config-introduction.html: /en/basics/applications.html -/en/cloudconfig/config-rest-api-v2.html: /en/reference/api/config-v2.html -/en/cloudconfig/configapi-dev-java.html: /en/applications/configapi-dev.html -/en/cloudconfig/configapi-dev.html: /en/applications/configapi-dev.html -/en/cloudconfig/configuration-server.html: /en/operations/self-managed/configuration-server.html -/en/cloudconfig/deploy-rest-api-v2.html: /en/reference/api/deploy-v2.html -/en/cloudconfig/tenant-rest-api.html: /en/reference/api/application-v2.html -/en/components/bundles.html: /en/applications/bundles.html -/en/components/chained-components.html: /en/applications/chaining.html -/en/concrete-documents.html: /en/schemas/concrete-documents.html -/en/config-introduction.html: /en/basics/applications.html -/en/config-sentinel.html: /en/operations/self-managed/config-sentinel.html -/en/config-system.html: /en/applications/config-system.html -/en/configuring-components.html: /en/applications/configuring-components.html -/en/content/api-state-rest-api.html: /en/reference/api/cluster-v2.html -/en/content/visiting.html: /en/writing/visiting.html -/en/contributing: /en/learn/contributing.md -/en/contributing/configapi-dev-java.html: /en/applications/configapi-dev.html -/en/contributing/configapi-dev.html: /en/applications/configapi-dev.html -/en/cpu-support.html: /en/operations/self-managed/cpu-support.html -/en/cross-encoders: /en/ranking/cross-encoders.md -/en/deploy-an-application-java.html: /en/basics/deploy-an-application-java.html -/en/deploy-an-application-local-java.html: /en/basics/deploy-an-application-local-java.html -/en/deploy-an-application-local.html: /en/basics/deploy-an-application-local.html -/en/deploy-an-application-shell.html: /en/basics/deploy-an-application-shell.html -/en/deploy-an-application.html: /en/basics/deploy-an-application.html -/en/deployment.html: /en/applications/deployment.html -/en/developer-guide.html: /en/applications/developer-guide.html -/en/developing-web-services.html: /en/applications/web-services.html -/en/docker-containers-in-production.html: /en/operations/self-managed/docker-containers.html -/en/document-api-guide.html: /en/writing/document-api-guide.html -/en/document-processing.html: /en/applications/document-processors.html -/en/document-summaries.html: /en/querying/document-summaries.html -/en/document-v1-api-guide.html: /en/writing/document-v1-api-guide.html -/en/documents.html: /en/schemas/documents.html -/en/elastic-vespa.html: /en/content/elasticity.html -/en/elasticity.html: /en/content/elasticity.html -/en/embedding.html: /en/rag/embedding.html -/en/exposing-schema-information.html: /en/schemas/exposing-schema-information.html -/en/faq: /en/learn/faq.md -/en/features.html: /en/learn/features.html -/en/federation.html: /en/querying/federation.html -/en/geo-search.html: /en/querying/geo-search.html -/en/getting-started-ranking.html: /en/ranking/ranking-intro.md -/en/getting-started.html: /en/learn/tutorials/index.html -/en/glossary.html: /en/learn/glossary.html -/en/graceful-degradation.html: /en/performance/graceful-degradation.html -/en/grouping.html: /en/querying/grouping.html -/en/ide-support.html: /en/applications/ide-support.html -/en/indexing-paged-vectors.html: /en/writing/indexing-paged-vectors.html -/en/indexing.html: /en/writing/indexing.html -/en/inheritance-in-schemas.html: /en/schemas/inheritance-in-schemas.html -/en/inspecting-java-services.html: /en/operations/self-managed/container.html -/en/introduction-to-documentation.html: /en/learn/about-documentation.html -/en/jdisc/bundle-troubleshooting.html: /en/applications/bundles.html -/en/jdisc/component-versioning.html: /en/reference/applications/components.html -/en/jdisc/container-components.html: /en/applications/components.html -/en/jdisc/developing-osgi-bundles.html: /en/applications/bundles.html -/en/jdisc/developing-request-handlers.html: /en/applications/request-handlers.html -/en/jdisc/http-api-tutorial.html: /en/learn/tutorials/http-api.html -/en/jdisc/http-server-and-filters.html: /en/applications/http-servers-and-filters.html -/en/jdisc/index.html: /en/applications/containers.html -/en/jdisc/injecting-components.html: /en/applications/dependency-injection.html -/en/jdisc/pluggable-frameworks.html: /en/applications/pluggable-frameworks.html -/en/jdisc/processing.html: /en/applications/processing.html -/en/lightgbm: /en/ranking/lightgbm.md -/en/linguistics.html: /en/linguistics/linguistics.html -/en/llm-help: /en/learn/llm-help.md -/en/llms-document-enrichment: /en/rag/document-enrichment.md -/en/llms-external: /en/rag/external-llms.md -/en/llms-in-vespa: /en/rag/llms-in-vespa.md -/en/llms-local: /en/rag/local-llms.md -/en/llms-rag: /en/rag/rag.md -/en/lucene-linguistics.html: /en/linguistics/lucene-linguistics.html -/en/monitoring-with-grafana-quick-start.html: /en/operations/self-managed/monitoring.html -/en/monitoring.html: /en/operations/self-managed/monitoring.html -/en/mtls.html: /en/security/mtls.md -/en/multinode-systems.html: /en/operations/self-managed/multinode-systems.html -/en/multivalue-query-operators.html: /en/ranking/multivalue-query-operators.html -/en/nativerank.html: /en/ranking/nativerank.html -/en/nearest-neighbor-search: /en/querying/nearest-neighbor-search.md -/en/nearest-neighbor-search-guide: /en/querying/nearest-neighbor-search-guide.md -/en/node-setup.html: /en/operations/self-managed/node-setup.html -/en/onnx: /en/ranking/onnx.md -/en/operations-selfhosted/admin-procedures.html: /en/operations/self-managed/admin-procedures.html -/en/operations-selfhosted/config-proxy.html: /en/operations/self-managed/config-proxy.html -/en/operations-selfhosted/config-sentinel.html: /en/operations/self-managed/config-sentinel.html -/en/operations-selfhosted/configuration-server.html: /en/operations/self-managed/configuration-server.html -/en/operations-selfhosted/container.html: /en/operations/self-managed/container.html -/en/operations-selfhosted/content-node-recovery.html: /en/operations/self-managed/content-node-recovery.html -/en/operations-selfhosted/cpu-support.html: /en/operations/self-managed/cpu-support.html -/en/operations-selfhosted/docker-containers.html: /en/operations/self-managed/docker-containers.html -/en/operations-selfhosted/files-processes-and-ports.html: /en/operations/self-managed/files-processes-and-ports.html -/en/operations-selfhosted/live-upgrade.html: /en/operations/self-managed/live-upgrade.html -/en/operations-selfhosted/metrics-proxy.html: /en/operations/self-managed/monitoring.html -/en/operations-selfhosted/monitoring.html: /en/operations/self-managed/monitoring.html -/en/operations-selfhosted/mtls: /en/security/mtls.md -/en/operations-selfhosted/multinode-systems.html: /en/operations/self-managed/multinode-systems.html -/en/operations-selfhosted/node-setup.html: /en/operations/self-managed/node-setup.html -/en/operations-selfhosted/procedure-change-attribute-index.html: /en/operations/self-managed/procedure-change-attribute-index.html -/en/operations-selfhosted/routing.html: /en/writing/document-routing.html -/en/operations-selfhosted/routingpolicies.html: /en/writing/document-routing.html -/en/operations-selfhosted/securing-your-vespa-installation: /en/security/securing-your-vespa-installation.md -/en/operations-selfhosted/slobrok.html: /en/operations/self-managed/slobrok.html -/en/operations-selfhosted/using-kubernetes-with-vespa.html: /en/operations/self-managed/using-kubernetes-with-vespa.html -/en/operations-selfhosted/vespa-cmdline-tools.html: /en/reference/operations/self-managed/tools.html -/en/operations-selfhosted/vespa-gpu-container.html: /en/operations/self-managed/vespa-gpu-container.html -/en/operations.html: /en/basics/operations.html -/en/operations/admin-procedures.html: /en/operations/self-managed/admin-procedures.html -/en/operations/batch-delete.html: /en/writing/batch-delete.html -/en/operations/config-proxy.html: /en/operations/self-managed/config-proxy.html -/en/operations/config-sentinel.html: /en/operations/self-managed/config-sentinel.html -/en/operations/configuration-server.html: /en/operations/self-managed/configuration-server.html -/en/operations/container.html: /en/operations/self-managed/container.html -/en/operations/content-node-recovery.html: /en/operations/self-managed/content-node-recovery.html -/en/operations/docker-containers.html: /en/operations/self-managed/docker-containers.html -/en/operations/feed-block.html: /en/writing/feed-block.html -/en/operations/live-upgrade.html: /en/operations/self-managed/live-upgrade.html -/en/operations/metrics-proxy.html: /en/operations/self-managed/monitoring.html -/en/operations/multinode-systems.html: /en/operations/self-managed/multinode-systems.html -/en/operations/node-setup.html: /en/operations/self-managed/node-setup.html -/en/operations/procedure-change-attribute-index.html: /en/operations/self-managed/procedure-change-attribute-index.html -/en/operations/tools.html: /en/reference/operations/tools.html -/en/operations/using-kubernetes-with-vespa.html: /en/operations/self-managed/using-kubernetes-with-vespa.html -/en/overview.html: /en/learn/overview.html -/en/page-templates.html: /en/querying/page-templates.html -/en/parent-child.html: /en/schemas/parent-child.html -/en/partial-updates.html: /en/writing/partial-updates.html -/en/performance/profiling-search-container.html: /en/performance/profiling.html -/en/performance/vespa-benchmarking.html: /en/performance/benchmarking.html -/en/phased-ranking.html: /en/ranking/phased-ranking.html -/en/predicate-fields.html: /en/schemas/predicate-fields.html -/en/proton.html: /en/content/proton.html -/en/query-api.html: /en/querying/query-api.html -/en/query-language.html: /en/querying/query-language.html -/en/query-profiles.html: /en/querying/query-profiles.html -/en/query-rewriting.html: /en/linguistics/query-rewriting.html -/en/querying.html: /en/basics/querying.html -/en/querying/result-rendering.html: /en/applications/result-renderers.html -/en/ranking-expressions-features.html: /en/ranking/ranking-expressions-features.html -/en/ranking.html: /en/basics/ranking.html -/en/ranking/constant-tensor-json-format.html: /en/reference/ranking/constant-tensor-json-format.html -/en/reads-and-writes.html: /en/writing/reads-and-writes.html -/en/reference/advanced-indexing-language.html: /en/reference/writing/indexing-language.html -/en/reference/application-packages-reference.html: /en/reference/applications/application-packages.html -/en/reference/application-v2-tenant.html: /en/reference/api/application-v2.html -/en/reference/bm25.html: /en/ranking/bm25.html -/en/reference/chunking-reference.html: /en/reference/rag/chunking.html -/en/reference/cluster-v2.html: /en/reference/api/cluster-v2.html -/en/reference/clustercontroller-metrics-reference.html: /en/reference/operations/metrics/clustercontroller.html -/en/reference/component-reference.html: /en/reference/applications/components.html -/en/reference/config-files.html: /en/reference/applications/config-files.html -/en/reference/config-proxy.html: /en/operations/self-managed/config-proxy.html -/en/reference/config-rest-api-v2.html: /en/reference/api/config-v2.html -/en/reference/configserver-metrics-reference.html: /en/reference/operations/metrics/configserver.html -/en/reference/container-metrics-reference.html: /en/reference/operations/metrics/container.html -/en/reference/default-result-format.html: /en/reference/querying/default-result-format.html -/en/reference/default-set-metrics-reference.html: /en/reference/operations/metrics/default-metric-set.html -/en/reference/deploy-rest-api-v2.html: /en/reference/api/deploy-v2.html -/en/reference/deployment-variants.html: /en/operations/deployment-variants.html -/en/reference/deployment.html: /en/reference/applications/deployment.html -/en/reference/distributor-metrics-reference.html: /en/reference/operations/metrics/distributor.html -/en/reference/document-field-path.html: /en/reference/schemas/document-field-path.html -/en/reference/document-json-format.html: /en/reference/schemas/document-json-format.html -/en/reference/document-select-language.html: /en/reference/writing/document-selector-language.html -/en/reference/document-v1-api-reference.html: /en/reference/api/document-v1.html -/en/reference/embedding-reference.html: /en/reference/rag/embedding.html -/en/reference/files-processes-and-ports.html: /en/operations/self-managed/files-processes-and-ports.html -/en/reference/grouping-syntax.html: /en/reference/querying/grouping-language.html -/en/reference/grouping.html: /en/reference/querying/grouping-language.html -/en/reference/healthchecks.html: /en/reference/operations/health-checks.html -/en/reference/hosts.html: /en/reference/applications/hosts.html -/en/reference/indexing-language-reference.html: /en/reference/writing/indexing-language.html -/en/reference/inspecting-structured-data.html: /en/applications/inspecting-structured-data.html -/en/reference/logd-metrics-reference.html: /en/reference/operations/metrics/logd.html -/en/reference/logs.html: /en/reference/operations/log-files.html -/en/reference/metrics-reference.html: /en/reference/operations/metrics/metrics.md -/en/reference/metrics-v1.html: /en/reference/api/metrics-v1.html -/en/reference/metrics-v2.html: /en/reference/api/metrics-v2.html -/en/reference/metrics.html: /en/operations/metrics.html -/en/reference/mtls.html: /en/reference/security/mtls.md -/en/reference/nativerank.html: /en/reference/ranking/nativerank.html -/en/reference/nodeadmin-metrics-reference.html: /en/reference/operations/metrics/nodeadmin.html -/en/reference/page-result-format.html: /en/reference/querying/page-result-format.html -/en/reference/page-templates-syntax.html: /en/reference/querying/page-templates.html -/en/reference/prometheus-v1.html: /en/reference/api/prometheus-v1.html -/en/reference/query-api-reference.html: /en/reference/api/query.html -/en/reference/query-language-reference.html: /en/reference/querying/yql.html -/en/reference/query-profiles.html: /en/reference/querying/query-profiles.html -/en/reference/rank-feature-configuration.html: /en/reference/ranking/rank-feature-configuration.html -/en/reference/rank-features.html: /en/reference/ranking/rank-features.html -/en/reference/rank-types.html: /en/reference/ranking/rank-types.html -/en/reference/ranking-expressions.html: /en/reference/ranking/ranking-expressions.html -/en/reference/routingpolicies.html: /en/writing/document-routing.html -/en/reference/schema-reference.html: /en/reference/schemas/schemas.html -/en/reference/search-api-reference.html: /en/reference/api/query.html -/en/reference/search-custom-state-api.html: /en/content/proton.html -/en/reference/searchnode-metrics-reference.html: /en/reference/operations/metrics/searchnode.html -/en/reference/select-reference: /en/reference/querying/json-query-language.md -/en/reference/semantic-rules.html: /en/reference/querying/semantic-rules.html -/en/reference/sentinel-metrics-reference.html: /en/reference/operations/metrics/sentinel.html -/en/reference/services-admin.html: /en/reference/applications/services/admin.html -/en/reference/services-container.html: /en/reference/applications/services/container.html -/en/reference/services-content.html: /en/reference/applications/services/content.html -/en/reference/services-docproc.html: /en/reference/applications/services/docproc.html -/en/reference/services-http.html: /en/reference/applications/services/http.html -/en/reference/services-processing.html: /en/reference/applications/services/processing.html -/en/reference/services-routing.html: /en/writing/document-routing.html -/en/reference/services-search.html: /en/reference/applications/services/search.html -/en/reference/services.html: /en/reference/applications/services/services.html -/en/reference/services/deployment.html: /en/reference/applications/deployment.html -/en/reference/services/services.html: /en/reference/applications/services/services.html -/en/reference/simple-query-language-reference.html: /en/reference/querying/simple-query-language.html -/en/reference/slobrok-metrics-reference.html: /en/reference/operations/metrics/slobrok.html -/en/reference/sorting.html: /en/reference/querying/sorting-language.html -/en/reference/state-config-inspect.html: /en/reference/api/state-v1.html -/en/reference/state-v1.html: /en/reference/api/state-v1.html -/en/reference/stateless-model-reference.html: /en/reference/ranking/model-files.html -/en/reference/storage-metrics-reference.html: /en/reference/operations/metrics/storage.html -/en/reference/string-segment-match.html: /en/reference/ranking/string-segment-match.html -/en/reference/tenant-rest-api.html: /en/reference/api/application-v2.html -/en/reference/tensor.html: /en/reference/ranking/tensor.html -/en/reference/testing-java.html: /en/reference/applications/testing-java.html -/en/reference/testing.html: /en/reference/applications/testing.html -/en/reference/unit-metrics-reference.html: /en/reference/operations/metrics/metric-units.html -/en/reference/validation-overrides.html: /en/reference/applications/validation-overrides.html -/en/reference/vespa-cmdline-tools.html: /en/reference/operations/self-managed/tools.html -/en/reference/vespa-set-metrics-reference.html: /en/reference/operations/metrics/vespa-metric-set.html -/en/reindexing.html: /en/operations/reindexing.html -/en/releases.html: /en/learn/releases.html -/en/reranking-in-searcher: /en/ranking/reranking-in-searcher.md -/en/result-diversity: /en/querying/result-diversity.md -/en/result-rendering.html: /en/applications/result-renderers.html -/en/routing.html: /en/writing/document-routing.html -/en/schema-inheritance.html: /en/basics/schemas.html -/en/schemas.html: /en/basics/schemas.html -/en/searcher-development.html: /en/applications/searchers.html -/en/searchers.html: /en/applications/searchers.html -/en/searching-multi-valued-fields.html: /en/querying/searching-multivalue-fields.md -/en/securing-your-vespa-installation.html: /en/security/securing-your-vespa-installation.md -/en/setting-vespa-variables.html: /en/operations/self-managed/files-processes-and-ports.html -/en/significance: /en/ranking/significance.md -/en/slobrok.html: /en/operations/self-managed/slobrok.html -/en/stateless-model-evaluation: /en/ranking/stateless-model-evaluation.html -/en/streaming-search.html: /en/performance/streaming-search.html -/en/structs.html: /en/schemas/structs.html -/en/tensor-examples.html: /en/ranking/tensor-examples.html -/en/tensor-user-guide.html: /en/ranking/tensor-user-guide.html -/en/tensorflow: /en/ranking/tensorflow.md -/en/testing.html: /en/applications/testing.html -/en/text-matching-ranking.html: /en/querying/text-matching.html -/en/text-matching.html: /en/querying/text-matching.html -/en/troubleshooting-encoding.html: /en/linguistics/troubleshooting-encoding.html -/en/tutorials/hybrid-search: /en/learn/tutorials/hybrid-search.md -/en/tutorials/index.html: /en/learn/tutorials/index.html -/en/tutorials/news-1-deploy-an-application: /en/learn/tutorials/news-1-deploy-an-application.md -/en/tutorials/news-1-getting-started.html: /en/learn/tutorials/news-1-deploy-an-application.md -/en/tutorials/news-2-basic-feeding-and-query: /en/learn/tutorials/news-2-basic-feeding-and-query.md -/en/tutorials/news-3-searching: /en/learn/tutorials/news-3-searching.md -/en/tutorials/news-4-embeddings: /en/learn/tutorials/news-4-embeddings.md -/en/tutorials/news-5-recommendation: /en/learn/tutorials/news-5-recommendation.md -/en/tutorials/news-6-recommendation-with-searchers: /en/learn/tutorials/news-6-recommendation-with-searchers.md -/en/tutorials/news-7-recommendation-with-parent-child: /en/learn/tutorials/news-7-recommendation-with-parent-child.md -/en/tutorials/rag-blueprint: /en/learn/tutorials/rag-blueprint.md -/en/tutorials/text-search: /en/learn/tutorials/text-search.md -/en/tutorials/text-search-ml: /en/learn/tutorials/text-search-ml.md -/en/unit-testing.html: /en/applications/unit-testing.html -/en/use-case-shopping: /en/learn/tutorials/e-commerce.md -/en/using-wand-with-vespa.html: /en/ranking/wand.html -/en/using-zookeeper: /en/applications/using-zookeeper.md -/en/vector-search.html: /en/querying/vector-search-intro.html -/en/vespa-cli-feed.html: /en/clients/vespa-cli.html -/en/vespa-cli.html: /en/clients/vespa-cli.html -/en/vespa-feed-client.html: /en/clients/vespa-feed-client.html -/en/vespa-gpu-container.html: /en/operations/self-managed/vespa-gpu-container.html -/en/vespa-quick-start-java.html: /en/basics/deploy-an-application-local-java.html -/en/vespa-quick-start-kubernetes.html: /en/operations/self-managed/using-kubernetes-with-vespa.html -/en/vespa-quick-start-multinode-aws-ecs.html: /en/operations/self-managed/multinode-systems.html -/en/vespa-quick-start-multinode-aws.html: /en/operations/self-managed/multinode-systems.html -/en/vespa-quick-start-vagrant.html: /en/learn/tutorials/index.html -/en/vespa-quick-start.html: /en/basics/deploy-an-application-local.html -/en/vespa7-release-notes.html: /en/reference/release-notes/vespa7.html -/en/vespa8-geo-migration-guide: /en/reference/release-notes/vespa8-geo-migration-guide.md -/en/vespa8-release-notes.html: /en/reference/release-notes/vespa8.html -/en/vespa9-release-notes.html: /en/reference/release-notes/vespa9.html -/en/visiting.html: /en/writing/visiting.html -/en/whats-more.html: /en/basics/whats-more.html -/en/working-with-chunks.html: /en/rag/working-with-chunks.html -/en/writing.html: /en/basics/writing.html -/en/xgboost: /en/ranking/xgboost.md -/en/operations/kubernetes/logging.html: /en/operations/kubernetes/configuration/logging.html -/en/operations/kubernetes/ingress.html: /en/operations/kubernetes/configuration/ingress.html -/en/operations/kubernetes/custom-overrides-podtemplate.html: /en/operations/kubernetes/configuration/custom-overrides-podtemplate.html -/en/operations/kubernetes/tls.html: /en/operations/kubernetes/configuration/tls.html -/en/operations/kubernetes/monitoring.html: /en/operations/kubernetes/operations/monitoring.html diff --git a/renovate.json b/renovate.json deleted file mode 100644 index fb82a32b69..0000000000 --- a/renovate.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "$schema": "https://docs.renovatebot.com/renovate-schema.json", - "extends": [ - "local>vespa-engine/renovate-config" - ], - "packageRules": [ - { - "matchPackageNames": ["storybook"], - "schedule": ["every weekend"] - } - ] -} diff --git a/robots.txt b/robots.txt deleted file mode 100644 index 34a6423371..0000000000 --- a/robots.txt +++ /dev/null @@ -1,11 +0,0 @@ -User-agent: atlassian-bot -Disallow: /assets/ -Disallow: /css/ -Disallow: /js/ -Disallow: /playground/ -Disallow: /icons/ - -User-agent: * -Allow: / - -Sitemap: https://docs.vespa.ai/sitemap.xml \ No newline at end of file diff --git a/search.html b/search.html deleted file mode 100644 index e4c976038a..0000000000 --- a/search.html +++ /dev/null @@ -1,40 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "" -index: false -meta_robots_noindex: true -skipheading: true ---- - - -
    - - -
    - - -
    - Search results from
    - Vespa Logo -
    -
    -
    -
    - - - - - - - - - - - - - -
    Search in Stack Overflow:
    Search in GitHub Issues:
    Search in the Vespa-Slack: 
    -
    - - - diff --git a/search_beta.html b/search_beta.html deleted file mode 100644 index 38aff7d033..0000000000 --- a/search_beta.html +++ /dev/null @@ -1,27 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -title: "" -index: false -meta_robots_noindex: true ---- - - -
    - - -
    - - -
    - Search results from
    - Vespa Logo -
    -
    - - - - - - diff --git a/sitemap.html b/sitemap.html deleted file mode 100644 index 258bd3384b..0000000000 --- a/sitemap.html +++ /dev/null @@ -1,49 +0,0 @@ ---- -# Copyright Vespa.ai. All rights reserved. -skipheading: true -index: false ---- -
    -
    -

    Table of contents

    -
    -
    - -
    -
    - {% for item in site.data.sidebar.docs %} -

    {{ item.title }}

    -
      - {% for child in item.documents %} -
    • - {% if child.sub %} - - {{ child.title }} -
        - {% for subchild in child.sub %} -
      • - {% if subchild.sub %} - - {{ subchild.title }} - - - {% else %} - {{ subchild.page }} - {% endif %} -
      • - {% endfor %} -
      - - {% else %} - {{ child.page }} - {% endif %} -
    • - {% endfor %} -
    - {% endfor %} -
    -
    \ No newline at end of file diff --git a/suggestions_index.json b/suggestions_index.json deleted file mode 100644 index 7d4c9e486f..0000000000 --- a/suggestions_index.json +++ /dev/null @@ -1,7935 +0,0 @@ -[ -{"put": "id:term:term::1711002406", "fields": {"term": "What is the Vespa access log format?", "namespace": "open-p", "hash": 1711002406, "terms": ["What is the Vespa access log format?", "is the Vespa access log format?", "the Vespa access log format?", "Vespa access log format?", "access log format?", "log format?", "format?"], "tokens": ["What", "is", "the", "Vespa", "access", "log", "format", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1999771757", "fields": {"term": "How can custom key/value pairs be added?", "namespace": "open-p", "hash": 1999771757, "terms": ["How can custom key/value pairs be added?", "can custom key/value pairs be added?", "custom key/value pairs be added?", "key/value pairs be added?", "pairs be added?", "be added?", "added?"], "tokens": ["How", "can", "custom", "key", "/", "value", "pairs", "be", "added", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::558304988", "fields": {"term": "What can be tracked using custom key/value pairs?", "namespace": "open-p", "hash": 558304988, "terms": ["What can be tracked using custom key/value pairs?", "can be tracked using custom key/value pairs?", "be tracked using custom key/value pairs?", "tracked using custom key/value pairs?", "using custom key/value pairs?", "custom key/value pairs?", "key/value pairs?", "pairs?"], "tokens": ["What", "can", "be", "tracked", "using", "custom", "key", "/", "value", "pairs", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2035807020", "fields": {"term": "What is Vespa access log?", "namespace": "open-p", "hash": -2035807020, "terms": ["What is Vespa access log?", "is Vespa access log?", "Vespa access log?", "access log?", "log?"], "tokens": ["What", "is", "Vespa", "access", "log", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1332502154", "fields": {"term": "What is logged in Vespa access log?", "namespace": "open-p", "hash": 1332502154, "terms": ["What is logged in Vespa access log?", "is logged in Vespa access log?", "logged in Vespa access log?", "in Vespa access log?", "Vespa access log?", "access log?", "log?"], "tokens": ["What", "is", "logged", "in", "Vespa", "access", "log", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-565771718", "fields": {"term": "What is the format of Vespa access log?", "namespace": "open-p", "hash": -565771718, "terms": ["What is the format of Vespa access log?", "is the format of Vespa access log?", "the format of Vespa access log?", "format of Vespa access log?", "of Vespa access log?", "Vespa access log?", "access log?", "log?"], "tokens": ["What", "is", "the", "format", "of", "Vespa", "access", "log", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::885237581", "fields": {"term": "What are the predefined fields in Vespa access log?", "namespace": "open-p", "hash": 885237581, "terms": ["What are the predefined fields in Vespa access log?", "are the predefined fields in Vespa access log?", "the predefined fields in Vespa access log?", "predefined fields in Vespa access log?", "fields in Vespa access log?", "in Vespa access log?", "Vespa access log?", "access log?", "log?"], "tokens": ["What", "are", "the", "predefined", "fields", "in", "Vespa", "access", "log", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-562782549", "fields": {"term": "What is the significance of search.totalhits field in Vespa access log?", "namespace": "open-p", "hash": -562782549, "terms": ["What is the significance of search.totalhits field in Vespa access log?", "is the significance of search.totalhits field in Vespa access log?", "the significance of search.totalhits field in Vespa access log?", "significance of search.totalhits field in Vespa access log?", "of search.totalhits field in Vespa access log?", "search.totalhits field in Vespa access log?", "field in Vespa access log?", "in Vespa access log?", "Vespa access log?", "access log?", "log?"], "tokens": ["What", "is", "the", "significance", "of", "search", "totalhits", "field", "in", "Vespa", "access", "log", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1342433542", "fields": {"term": "What is the significance of search.hits field in Vespa access log?", "namespace": "open-p", "hash": 1342433542, "terms": ["What is the significance of search.hits field in Vespa access log?", "is the significance of search.hits field in Vespa access log?", "the significance of search.hits field in Vespa access log?", "significance of search.hits field in Vespa access log?", "of search.hits field in Vespa access log?", "search.hits field in Vespa access log?", "field in Vespa access log?", "in Vespa access log?", "Vespa access log?", "access log?", "log?"], "tokens": ["What", "is", "the", "significance", "of", "search", "hits", "field", "in", "Vespa", "access", "log", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1544472437", "fields": {"term": "What is the significance of search.coverage field in Vespa access log?", "namespace": "open-p", "hash": -1544472437, "terms": ["What is the significance of search.coverage field in Vespa access log?", "is the significance of search.coverage field in Vespa access log?", "the significance of search.coverage field in Vespa access log?", "significance of search.coverage field in Vespa access log?", "of search.coverage field in Vespa access log?", "search.coverage field in Vespa access log?", "field in Vespa access log?", "in Vespa access log?", "Vespa access log?", "access log?", "log?"], "tokens": ["What", "is", "the", "significance", "of", "search", "coverage", "field", "in", "Vespa", "access", "log", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-583276191", "fields": {"term": "What is the structure of the search.coverage object in Vespa access log?", "namespace": "open-p", "hash": -583276191, "terms": ["What is the structure of the search.coverage object in Vespa access log?", "is the structure of the search.coverage object in Vespa access log?", "the structure of the search.coverage object in Vespa access log?", "structure of the search.coverage object in Vespa access log?", "of the search.coverage object in Vespa access log?", "the search.coverage object in Vespa access log?", "search.coverage object in Vespa access log?", "object in Vespa access log?", "in Vespa access log?", "Vespa access log?", "access log?", "log?"], "tokens": ["What", "is", "the", "structure", "of", "the", "search", "coverage", "object", "in", "Vespa", "access", "log", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1123886936", "fields": {"term": "What are remoteaddr and remoteport?", "namespace": "open-p", "hash": 1123886936, "terms": ["What are remoteaddr and remoteport?", "are remoteaddr and remoteport?", "remoteaddr and remoteport?", "and remoteport?", "remoteport?"], "tokens": ["What", "are", "remoteaddr", "and", "remoteport", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1716105070", "fields": {"term": "Which HTTP headers are logged for remoteaddr?", "namespace": "open-p", "hash": -1716105070, "terms": ["Which HTTP headers are logged for remoteaddr?", "HTTP headers are logged for remoteaddr?", "headers are logged for remoteaddr?", "are logged for remoteaddr?", "logged for remoteaddr?", "for remoteaddr?", "remoteaddr?"], "tokens": ["Which", "HTTP", "headers", "are", "logged", "for", "remoteaddr", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::9828397", "fields": {"term": "What is the precedence for multiple headers with remoteaddr?", "namespace": "open-p", "hash": 9828397, "terms": ["What is the precedence for multiple headers with remoteaddr?", "is the precedence for multiple headers with remoteaddr?", "the precedence for multiple headers with remoteaddr?", "precedence for multiple headers with remoteaddr?", "for multiple headers with remoteaddr?", "multiple headers with remoteaddr?", "headers with remoteaddr?", "with remoteaddr?", "remoteaddr?"], "tokens": ["What", "is", "the", "precedence", "for", "multiple", "headers", "with", "remoteaddr", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1806496789", "fields": {"term": "What is logged as peeraddress and peerport?", "namespace": "open-p", "hash": 1806496789, "terms": ["What is logged as peeraddress and peerport?", "is logged as peeraddress and peerport?", "logged as peeraddress and peerport?", "as peeraddress and peerport?", "peeraddress and peerport?", "and peerport?", "peerport?"], "tokens": ["What", "is", "logged", "as", "peeraddress", "and", "peerport", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1069500005", "fields": {"term": "What is access logging configuration?", "namespace": "open-p", "hash": 1069500005, "terms": ["What is access logging configuration?", "is access logging configuration?", "access logging configuration?", "logging configuration?", "configuration?"], "tokens": ["What", "is", "access", "logging", "configuration", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1490682352", "fields": {"term": "Where can I find accesslog in *services.xml*?", "namespace": "open-p", "hash": -1490682352, "terms": ["Where can I find accesslog in *services.xml*?", "can I find accesslog in *services.xml*?", "I find accesslog in *services.xml*?", "find accesslog in *services.xml*?", "accesslog in *services.xml*?", "in *services.xml*?", "*services.xml*?"], "tokens": ["Where", "can", "I", "find", "accesslog", "in", "*", "services", "xml", "*?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1954248281", "fields": {"term": "What does the accesslog element do?", "namespace": "open-p", "hash": 1954248281, "terms": ["What does the accesslog element do?", "does the accesslog element do?", "the accesslog element do?", "accesslog element do?", "element do?", "do?"], "tokens": ["What", "does", "the", "accesslog", "element", "do", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1467855705", "fields": {"term": "What is the file name pattern?", "namespace": "open-p", "hash": -1467855705, "terms": ["What is the file name pattern?", "is the file name pattern?", "the file name pattern?", "file name pattern?", "name pattern?", "pattern?"], "tokens": ["What", "is", "the", "file", "name", "pattern", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1037772254", "fields": {"term": "What is expanded in the file name pattern?", "namespace": "open-p", "hash": -1037772254, "terms": ["What is expanded in the file name pattern?", "is expanded in the file name pattern?", "expanded in the file name pattern?", "in the file name pattern?", "the file name pattern?", "file name pattern?", "name pattern?", "pattern?"], "tokens": ["What", "is", "expanded", "in", "the", "file", "name", "pattern", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1425317655", "fields": {"term": "What is the format for month?", "namespace": "open-p", "hash": 1425317655, "terms": ["What is the format for month?", "is the format for month?", "the format for month?", "format for month?", "for month?", "month?"], "tokens": ["What", "is", "the", "format", "for", "month", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1216295554", "fields": {"term": "What is Apache httpd?", "namespace": "open-p", "hash": -1216295554, "terms": ["What is Apache httpd?", "is Apache httpd?", "Apache httpd?", "httpd?"], "tokens": ["What", "is", "Apache", "httpd", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1152353530", "fields": {"term": "How can log rotation be configured?", "namespace": "open-p", "hash": -1152353530, "terms": ["How can log rotation be configured?", "can log rotation be configured?", "log rotation be configured?", "rotation be configured?", "be configured?", "configured?"], "tokens": ["How", "can", "log", "rotation", "be", "configured", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-567597650", "fields": {"term": "What are the two rotationScheme alternatives?", "namespace": "open-p", "hash": -567597650, "terms": ["What are the two rotationScheme alternatives?", "are the two rotationScheme alternatives?", "the two rotationScheme alternatives?", "two rotationScheme alternatives?", "rotationScheme alternatives?", "alternatives?"], "tokens": ["What", "are", "the", "two", "rotationScheme", "alternatives", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1324808062", "fields": {"term": "What is controlled by rotationInterval?", "namespace": "open-p", "hash": 1324808062, "terms": ["What is controlled by rotationInterval?", "is controlled by rotationInterval?", "controlled by rotationInterval?", "by rotationInterval?", "rotationInterval?"], "tokens": ["What", "is", "controlled", "by", "rotationInterval", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1205847728", "fields": {"term": "What is fileNamePattern used for?", "namespace": "open-p", "hash": -1205847728, "terms": ["What is fileNamePattern used for?", "is fileNamePattern used for?", "fileNamePattern used for?", "used for?", "for?"], "tokens": ["What", "is", "fileNamePattern", "used", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1840322790", "fields": {"term": "How is fileNamePattern used?", "namespace": "open-p", "hash": -1840322790, "terms": ["How is fileNamePattern used?", "is fileNamePattern used?", "fileNamePattern used?", "used?"], "tokens": ["How", "is", "fileNamePattern", "used", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1729070279", "fields": {"term": "What is the rotationScheme of accesslog?", "namespace": "open-p", "hash": -1729070279, "terms": ["What is the rotationScheme of accesslog?", "is the rotationScheme of accesslog?", "the rotationScheme of accesslog?", "rotationScheme of accesslog?", "of accesslog?", "accesslog?"], "tokens": ["What", "is", "the", "rotationScheme", "of", "accesslog", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1694981390", "fields": {"term": "In what directory are the access logs stored?", "namespace": "open-p", "hash": 1694981390, "terms": ["In what directory are the access logs stored?", "what directory are the access logs stored?", "directory are the access logs stored?", "are the access logs stored?", "the access logs stored?", "access logs stored?", "logs stored?", "stored?"], "tokens": ["In", "what", "directory", "are", "the", "access", "logs", "stored", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1478369590", "fields": {"term": "What is the file name of the active log file?", "namespace": "open-p", "hash": 1478369590, "terms": ["What is the file name of the active log file?", "is the file name of the active log file?", "the file name of the active log file?", "file name of the active log file?", "name of the active log file?", "of the active log file?", "the active log file?", "active log file?", "log file?", "file?"], "tokens": ["What", "is", "the", "file", "name", "of", "the", "active", "log", "file", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-323189989", "fields": {"term": "What happens to the active log file during rotation?", "namespace": "open-p", "hash": -323189989, "terms": ["What happens to the active log file during rotation?", "happens to the active log file during rotation?", "to the active log file during rotation?", "the active log file during rotation?", "active log file during rotation?", "log file during rotation?", "file during rotation?", "during rotation?", "rotation?"], "tokens": ["What", "happens", "to", "the", "active", "log", "file", "during", "rotation", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1427429223", "fields": {"term": "What is the format of the file name of access logs?", "namespace": "open-p", "hash": 1427429223, "terms": ["What is the format of the file name of access logs?", "is the format of the file name of access logs?", "the format of the file name of access logs?", "format of the file name of access logs?", "of the file name of access logs?", "the file name of access logs?", "file name of access logs?", "name of access logs?", "of access logs?", "access logs?", "logs?"], "tokens": ["What", "is", "the", "format", "of", "the", "file", "name", "of", "access", "logs", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::376694733", "fields": {"term": "What does the variable N represent in fileNamePattern.N?", "namespace": "open-p", "hash": 376694733, "terms": ["What does the variable N represent in fileNamePattern.N?", "does the variable N represent in fileNamePattern.N?", "the variable N represent in fileNamePattern.N?", "variable N represent in fileNamePattern.N?", "N represent in fileNamePattern.N?", "represent in fileNamePattern.N?", "in fileNamePattern.N?", "fileNamePattern.N?"], "tokens": ["What", "does", "the", "variable", "N", "represent", "in", "fileNamePattern", "N", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-974691377", "fields": {"term": "What is fileNamePattern?", "namespace": "open-p", "hash": -974691377, "terms": ["What is fileNamePattern?", "is fileNamePattern?", "fileNamePattern?"], "tokens": ["What", "is", "fileNamePattern", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1735466772", "fields": {"term": "What is symlinkName used for?", "namespace": "open-p", "hash": -1735466772, "terms": ["What is symlinkName used for?", "is symlinkName used for?", "symlinkName used for?", "used for?", "for?"], "tokens": ["What", "is", "symlinkName", "used", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-163081360", "fields": {"term": "How are log files renamed?", "namespace": "open-p", "hash": -163081360, "terms": ["How are log files renamed?", "are log files renamed?", "log files renamed?", "files renamed?", "renamed?"], "tokens": ["How", "are", "log", "files", "renamed", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1865900317", "fields": {"term": "What is accesslog?", "namespace": "open-p", "hash": 1865900317, "terms": ["What is accesslog?", "is accesslog?", "accesslog?"], "tokens": ["What", "is", "accesslog", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1057484724", "fields": {"term": "What is rotationScheme?", "namespace": "open-p", "hash": 1057484724, "terms": ["What is rotationScheme?", "is rotationScheme?", "rotationScheme?"], "tokens": ["What", "is", "rotationScheme", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1511983041", "fields": {"term": "What is the purpose of JsonAccessLog?", "namespace": "open-p", "hash": 1511983041, "terms": ["What is the purpose of JsonAccessLog?", "is the purpose of JsonAccessLog?", "the purpose of JsonAccessLog?", "purpose of JsonAccessLog?", "of JsonAccessLog?", "JsonAccessLog?"], "tokens": ["What", "is", "the", "purpose", "of", "JsonAccessLog", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1274791440", "fields": {"term": "How can a unique log file name be achieved?", "namespace": "open-p", "hash": 1274791440, "terms": ["How can a unique log file name be achieved?", "can a unique log file name be achieved?", "a unique log file name be achieved?", "unique log file name be achieved?", "log file name be achieved?", "file name be achieved?", "name be achieved?", "be achieved?", "achieved?"], "tokens": ["How", "can", "a", "unique", "log", "file", "name", "be", "achieved", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1333758337", "fields": {"term": "What controls time of rotation?", "namespace": "open-p", "hash": 1333758337, "terms": ["What controls time of rotation?", "controls time of rotation?", "time of rotation?", "of rotation?", "rotation?"], "tokens": ["What", "controls", "time", "of", "rotation", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::334444514", "fields": {"term": "What is rotationInterval?", "namespace": "open-p", "hash": 334444514, "terms": ["What is rotationInterval?", "is rotationInterval?", "rotationInterval?"], "tokens": ["What", "is", "rotationInterval", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1590546145", "fields": {"term": "What does rotationInterval specify?", "namespace": "open-p", "hash": 1590546145, "terms": ["What does rotationInterval specify?", "does rotationInterval specify?", "rotationInterval specify?", "specify?"], "tokens": ["What", "does", "rotationInterval", "specify", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1890880674", "fields": {"term": "What happens when rotationInterval ends with '...'?", "namespace": "open-p", "hash": 1890880674, "terms": ["What happens when rotationInterval ends with '...'?", "happens when rotationInterval ends with '...'?", "when rotationInterval ends with '...'?", "rotationInterval ends with '...'?", "ends with '...'?", "with '...'?", "'...'?"], "tokens": ["What", "happens", "when", "rotationInterval", "ends", "with", "'", "'?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1072676482", "fields": {"term": "What happens to access logs?", "namespace": "open-p", "hash": 1072676482, "terms": ["What happens to access logs?", "happens to access logs?", "to access logs?", "access logs?", "logs?"], "tokens": ["What", "happens", "to", "access", "logs", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1674780322", "fields": {"term": "Who is responsible for archiving?", "namespace": "open-p", "hash": -1674780322, "terms": ["Who is responsible for archiving?", "is responsible for archiving?", "responsible for archiving?", "for archiving?", "archiving?"], "tokens": ["Who", "is", "responsible", "for", "archiving", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-517786598", "fields": {"term": "How do you add key/value pair to access log from searcher?", "namespace": "open-p", "hash": -517786598, "terms": ["How do you add key/value pair to access log from searcher?", "do you add key/value pair to access log from searcher?", "you add key/value pair to access log from searcher?", "add key/value pair to access log from searcher?", "key/value pair to access log from searcher?", "pair to access log from searcher?", "to access log from searcher?", "access log from searcher?", "log from searcher?", "from searcher?", "searcher?"], "tokens": ["How", "do", "you", "add", "key", "/", "value", "pair", "to", "access", "log", "from", "searcher", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::443067052", "fields": {"term": "What happens when the same key is logged multiple times?", "namespace": "open-p", "hash": 443067052, "terms": ["What happens when the same key is logged multiple times?", "happens when the same key is logged multiple times?", "when the same key is logged multiple times?", "the same key is logged multiple times?", "same key is logged multiple times?", "key is logged multiple times?", "is logged multiple times?", "logged multiple times?", "multiple times?", "times?"], "tokens": ["What", "happens", "when", "the", "same", "key", "is", "logged", "multiple", "times", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2010515497", "fields": {"term": "What does logValue method do?", "namespace": "open-p", "hash": 2010515497, "terms": ["What does logValue method do?", "does logValue method do?", "logValue method do?", "method do?", "do?"], "tokens": ["What", "does", "logValue", "method", "do", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::999566638", "fields": {"term": "Where are key/value pairs added in the log?", "namespace": "open-p", "hash": 999566638, "terms": ["Where are key/value pairs added in the log?", "are key/value pairs added in the log?", "key/value pairs added in the log?", "pairs added in the log?", "added in the log?", "in the log?", "the log?", "log?"], "tokens": ["Where", "are", "key", "/", "value", "pairs", "added", "in", "the", "log", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1846905617", "fields": {"term": "What is included in the log as an array of strings?", "namespace": "open-p", "hash": -1846905617, "terms": ["What is included in the log as an array of strings?", "is included in the log as an array of strings?", "included in the log as an array of strings?", "in the log as an array of strings?", "the log as an array of strings?", "log as an array of strings?", "as an array of strings?", "an array of strings?", "array of strings?", "of strings?", "strings?"], "tokens": ["What", "is", "included", "in", "the", "log", "as", "an", "array", "of", "strings", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::373009104", "fields": {"term": "Can key/value pairs be added from any thread?", "namespace": "open-p", "hash": 373009104, "terms": ["Can key/value pairs be added from any thread?", "key/value pairs be added from any thread?", "pairs be added from any thread?", "be added from any thread?", "added from any thread?", "from any thread?", "any thread?", "thread?"], "tokens": ["Can", "key", "/", "value", "pairs", "be", "added", "from", "any", "thread", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1849596690", "fields": {"term": "What is the format of an example log line?", "namespace": "open-p", "hash": -1849596690, "terms": ["What is the format of an example log line?", "is the format of an example log line?", "the format of an example log line?", "format of an example log line?", "of an example log line?", "an example log line?", "example log line?", "log line?", "line?"], "tokens": ["What", "is", "the", "format", "of", "an", "example", "log", "line", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1137348679", "fields": {"term": "What is the purpose of the attributes object in the log?", "namespace": "open-p", "hash": 1137348679, "terms": ["What is the purpose of the attributes object in the log?", "is the purpose of the attributes object in the log?", "the purpose of the attributes object in the log?", "purpose of the attributes object in the log?", "of the attributes object in the log?", "the attributes object in the log?", "attributes object in the log?", "object in the log?", "in the log?", "the log?", "log?"], "tokens": ["What", "is", "the", "purpose", "of", "the", "attributes", "object", "in", "the", "log", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1793347461", "fields": {"term": "What is a connection log?", "namespace": "open-p", "hash": -1793347461, "terms": ["What is a connection log?", "is a connection log?", "a connection log?", "connection log?", "log?"], "tokens": ["What", "is", "a", "connection", "log", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2012687799", "fields": {"term": "What fields are in connection log?", "namespace": "open-p", "hash": 2012687799, "terms": ["What fields are in connection log?", "fields are in connection log?", "are in connection log?", "in connection log?", "connection log?", "log?"], "tokens": ["What", "fields", "are", "in", "connection", "log", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1823342585", "fields": {"term": "What is clientSubject?", "namespace": "open-p", "hash": 1823342585, "terms": ["What is clientSubject?", "is clientSubject?", "clientSubject?"], "tokens": ["What", "is", "clientSubject", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1875489285", "fields": {"term": "What is clientNotAfter?", "namespace": "open-p", "hash": 1875489285, "terms": ["What is clientNotAfter?", "is clientNotAfter?", "clientNotAfter?"], "tokens": ["What", "is", "clientNotAfter", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::772594981", "fields": {"term": "What is sessionId?", "namespace": "open-p", "hash": 772594981, "terms": ["What is sessionId?", "is sessionId?", "sessionId?"], "tokens": ["What", "is", "sessionId", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-419317412", "fields": {"term": "What is cipherSuite?", "namespace": "open-p", "hash": -419317412, "terms": ["What is cipherSuite?", "is cipherSuite?", "cipherSuite?"], "tokens": ["What", "is", "cipherSuite", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-743459725", "fields": {"term": "What is sniServerName?", "namespace": "open-p", "hash": -743459725, "terms": ["What is sniServerName?", "is sniServerName?", "sniServerName?"], "tokens": ["What", "is", "sniServerName", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1351830838", "fields": {"term": "What is the multinode-HA sample application?", "namespace": "open-p", "hash": 1351830838, "terms": ["What is the multinode-HA sample application?", "is the multinode-HA sample application?", "the multinode-HA sample application?", "multinode-HA sample application?", "sample application?", "application?"], "tokens": ["What", "is", "the", "multinode", "HA", "sample", "application", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1229576016", "fields": {"term": "Where can I find the multinode-HA sample application?", "namespace": "open-p", "hash": 1229576016, "terms": ["Where can I find the multinode-HA sample application?", "can I find the multinode-HA sample application?", "I find the multinode-HA sample application?", "find the multinode-HA sample application?", "the multinode-HA sample application?", "multinode-HA sample application?", "sample application?", "application?"], "tokens": ["Where", "can", "I", "find", "the", "multinode", "HA", "sample", "application", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::161953390", "fields": {"term": "What does the Multinode testing and observability sample app offer?", "namespace": "open-p", "hash": 161953390, "terms": ["What does the Multinode testing and observability sample app offer?", "does the Multinode testing and observability sample app offer?", "the Multinode testing and observability sample app offer?", "Multinode testing and observability sample app offer?", "testing and observability sample app offer?", "and observability sample app offer?", "observability sample app offer?", "sample app offer?", "app offer?", "offer?"], "tokens": ["What", "does", "the", "Multinode", "testing", "and", "observability", "sample", "app", "offer", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2129052469", "fields": {"term": "Where can I find the Multinode testing and observability sample app?", "namespace": "open-p", "hash": 2129052469, "terms": ["Where can I find the Multinode testing and observability sample app?", "can I find the Multinode testing and observability sample app?", "I find the Multinode testing and observability sample app?", "find the Multinode testing and observability sample app?", "the Multinode testing and observability sample app?", "Multinode testing and observability sample app?", "testing and observability sample app?", "and observability sample app?", "observability sample app?", "sample app?", "app?"], "tokens": ["Where", "can", "I", "find", "the", "Multinode", "testing", "and", "observability", "sample", "app", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1717644761", "fields": {"term": "How to restart Vespa?", "namespace": "open-p", "hash": -1717644761, "terms": ["How to restart Vespa?", "to restart Vespa?", "restart Vespa?", "Vespa?"], "tokens": ["How", "to", "restart", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-899118262", "fields": {"term": "What is vespa-start-services command?", "namespace": "open-p", "hash": -899118262, "terms": ["What is vespa-start-services command?", "is vespa-start-services command?", "vespa-start-services command?", "command?"], "tokens": ["What", "is", "vespa", "start", "services", "command", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2080556386", "fields": {"term": "What is vespa-stop-services command?", "namespace": "open-p", "hash": 2080556386, "terms": ["What is vespa-stop-services command?", "is vespa-stop-services command?", "vespa-stop-services command?", "command?"], "tokens": ["What", "is", "vespa", "stop", "services", "command", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1534866975", "fields": {"term": "What is vespa-start-configserver command?", "namespace": "open-p", "hash": 1534866975, "terms": ["What is vespa-start-configserver command?", "is vespa-start-configserver command?", "vespa-start-configserver command?", "command?"], "tokens": ["What", "is", "vespa", "start", "configserver", "command", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-974217589", "fields": {"term": "What is vespa-stop-configserver command?", "namespace": "open-p", "hash": -974217589, "terms": ["What is vespa-stop-configserver command?", "is vespa-stop-configserver command?", "vespa-stop-configserver command?", "command?"], "tokens": ["What", "is", "vespa", "stop", "configserver", "command", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::990700877", "fields": {"term": "What is vespa-sentinel-cmd?", "namespace": "open-p", "hash": 990700877, "terms": ["What is vespa-sentinel-cmd?", "is vespa-sentinel-cmd?", "vespa-sentinel-cmd?"], "tokens": ["What", "is", "vespa", "sentinel", "cmd", "?"], "corpus_count": 3, "document_count": 3}}, -{"put": "id:term:term::-1313052305", "fields": {"term": "What are examples of systemd/systemctl?", "namespace": "open-p", "hash": -1313052305, "terms": ["What are examples of systemd/systemctl?", "are examples of systemd/systemctl?", "examples of systemd/systemctl?", "of systemd/systemctl?", "systemd/systemctl?"], "tokens": ["What", "are", "examples", "of", "systemd", "/", "systemctl", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1519780905", "fields": {"term": "What is vespa-config-status?", "namespace": "open-p", "hash": 1519780905, "terms": ["What is vespa-config-status?", "is vespa-config-status?", "vespa-config-status?"], "tokens": ["What", "is", "vespa", "config", "status", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::-1485750302", "fields": {"term": "How to verify updated config?", "namespace": "open-p", "hash": -1485750302, "terms": ["How to verify updated config?", "to verify updated config?", "verify updated config?", "updated config?", "config?"], "tokens": ["How", "to", "verify", "updated", "config", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1171586447", "fields": {"term": "What is the cluster controller status page?", "namespace": "open-p", "hash": -1171586447, "terms": ["What is the cluster controller status page?", "is the cluster controller status page?", "the cluster controller status page?", "cluster controller status page?", "controller status page?", "status page?", "page?"], "tokens": ["What", "is", "the", "cluster", "controller", "status", "page", "?"], "corpus_count": 3, "document_count": 3}}, -{"put": "id:term:term::-1418616149", "fields": {"term": "How to track node status?", "namespace": "open-p", "hash": -1418616149, "terms": ["How to track node status?", "to track node status?", "track node status?", "node status?", "status?"], "tokens": ["How", "to", "track", "node", "status", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-173076126", "fields": {"term": "What are the different tools available for tracking load?", "namespace": "open-p", "hash": -173076126, "terms": ["What are the different tools available for tracking load?", "are the different tools available for tracking load?", "the different tools available for tracking load?", "different tools available for tracking load?", "tools available for tracking load?", "available for tracking load?", "for tracking load?", "tracking load?", "load?"], "tokens": ["What", "are", "the", "different", "tools", "available", "for", "tracking", "load", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1736894339", "fields": {"term": "What is feed block?", "namespace": "open-p", "hash": 1736894339, "terms": ["What is feed block?", "is feed block?", "feed block?", "block?"], "tokens": ["What", "is", "feed", "block", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-813572046", "fields": {"term": "How to recover from feed block?", "namespace": "open-p", "hash": -813572046, "terms": ["How to recover from feed block?", "to recover from feed block?", "recover from feed block?", "from feed block?", "feed block?", "block?"], "tokens": ["How", "to", "recover", "from", "feed", "block", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::420924374", "fields": {"term": "What is a PID file?", "namespace": "open-p", "hash": 420924374, "terms": ["What is a PID file?", "is a PID file?", "a PID file?", "PID file?", "file?"], "tokens": ["What", "is", "a", "PID", "file", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::507333064", "fields": {"term": "Where is the PID file located?", "namespace": "open-p", "hash": 507333064, "terms": ["Where is the PID file located?", "is the PID file located?", "the PID file located?", "PID file located?", "file located?", "located?"], "tokens": ["Where", "is", "the", "PID", "file", "located", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1618077255", "fields": {"term": "What is the purpose of a PID file?", "namespace": "open-p", "hash": -1618077255, "terms": ["What is the purpose of a PID file?", "is the purpose of a PID file?", "the purpose of a PID file?", "purpose of a PID file?", "of a PID file?", "a PID file?", "PID file?", "file?"], "tokens": ["What", "is", "the", "purpose", "of", "a", "PID", "file", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1920184213", "fields": {"term": "What are status pages?", "namespace": "open-p", "hash": 1920184213, "terms": ["What are status pages?", "are status pages?", "status pages?", "pages?"], "tokens": ["What", "are", "status", "pages", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1541920474", "fields": {"term": "How can I find the status page port?", "namespace": "open-p", "hash": -1541920474, "terms": ["How can I find the status page port?", "can I find the status page port?", "I find the status page port?", "find the status page port?", "the status page port?", "status page port?", "page port?", "port?"], "tokens": ["How", "can", "I", "find", "the", "status", "page", "port", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1295982646", "fields": {"term": "How do I connect to the status page?", "namespace": "open-p", "hash": 1295982646, "terms": ["How do I connect to the status page?", "do I connect to the status page?", "I connect to the status page?", "connect to the status page?", "to the status page?", "the status page?", "status page?", "page?"], "tokens": ["How", "do", "I", "connect", "to", "the", "status", "page", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1995284981", "fields": {"term": "What can the cluster controller status page show?", "namespace": "open-p", "hash": -1995284981, "terms": ["What can the cluster controller status page show?", "can the cluster controller status page show?", "the cluster controller status page show?", "cluster controller status page show?", "controller status page show?", "status page show?", "page show?", "show?"], "tokens": ["What", "can", "the", "cluster", "controller", "status", "page", "show", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1218537482", "fields": {"term": "How to modify a Vespa schema?", "namespace": "open-p", "hash": 1218537482, "terms": ["How to modify a Vespa schema?", "to modify a Vespa schema?", "modify a Vespa schema?", "a Vespa schema?", "Vespa schema?", "schema?"], "tokens": ["How", "to", "modify", "a", "Vespa", "schema", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::686903535", "fields": {"term": "What is procedure-change-attribute-index?", "namespace": "open-p", "hash": 686903535, "terms": ["What is procedure-change-attribute-index?", "is procedure-change-attribute-index?", "procedure-change-attribute-index?"], "tokens": ["What", "is", "procedure", "change", "attribute", "index", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1292181461", "fields": {"term": "Where to find schema modification documentation?", "namespace": "open-p", "hash": -1292181461, "terms": ["Where to find schema modification documentation?", "to find schema modification documentation?", "find schema modification documentation?", "schema modification documentation?", "modification documentation?", "documentation?"], "tokens": ["Where", "to", "find", "schema", "modification", "documentation", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::848934022", "fields": {"term": "What is the /cluster/v2 API used for?", "namespace": "open-p", "hash": 848934022, "terms": ["What is the /cluster/v2 API used for?", "is the /cluster/v2 API used for?", "the /cluster/v2 API used for?", "/cluster/v2 API used for?", "API used for?", "used for?", "for?"], "tokens": ["What", "is", "the", "/", "cluster", "/", "v2", "API", "used", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1638922146", "fields": {"term": "How is node state set?", "namespace": "open-p", "hash": 1638922146, "terms": ["How is node state set?", "is node state set?", "node state set?", "state set?", "set?"], "tokens": ["How", "is", "node", "state", "set", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1659462444", "fields": {"term": "Where is state data persisted?", "namespace": "open-p", "hash": 1659462444, "terms": ["Where is state data persisted?", "is state data persisted?", "state data persisted?", "data persisted?", "persisted?"], "tokens": ["Where", "is", "state", "data", "persisted", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1825615839", "fields": {"term": "Where should cluster controller be run?", "namespace": "open-p", "hash": 1825615839, "terms": ["Where should cluster controller be run?", "should cluster controller be run?", "cluster controller be run?", "controller be run?", "be run?", "run?"], "tokens": ["Where", "should", "cluster", "controller", "be", "run", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::177444981", "fields": {"term": "What can cause ownership transfer?", "namespace": "open-p", "hash": 177444981, "terms": ["What can cause ownership transfer?", "can cause ownership transfer?", "cause ownership transfer?", "ownership transfer?", "transfer?"], "tokens": ["What", "can", "cause", "ownership", "transfer", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1686522129", "fields": {"term": "How to configure cluster controller?", "namespace": "open-p", "hash": 1686522129, "terms": ["How to configure cluster controller?", "to configure cluster controller?", "configure cluster controller?", "cluster controller?", "controller?"], "tokens": ["How", "to", "configure", "cluster", "controller", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1189086391", "fields": {"term": "What is max_premature_crashes?", "namespace": "open-p", "hash": -1189086391, "terms": ["What is max_premature_crashes?", "is max_premature_crashes?", "max_premature_crashes?"], "tokens": ["What", "is", "max_premature_crashes", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1892798615", "fields": {"term": "How to detect a broken node?", "namespace": "open-p", "hash": 1892798615, "terms": ["How to detect a broken node?", "to detect a broken node?", "detect a broken node?", "a broken node?", "broken node?", "node?"], "tokens": ["How", "to", "detect", "a", "broken", "node", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-701295631", "fields": {"term": "What is a controlled shutdown?", "namespace": "open-p", "hash": -701295631, "terms": ["What is a controlled shutdown?", "is a controlled shutdown?", "a controlled shutdown?", "controlled shutdown?", "shutdown?"], "tokens": ["What", "is", "a", "controlled", "shutdown", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::790585923", "fields": {"term": "What is RateLimitingSearcher?", "namespace": "open-p", "hash": 790585923, "terms": ["What is RateLimitingSearcher?", "is RateLimitingSearcher?", "RateLimitingSearcher?"], "tokens": ["What", "is", "RateLimitingSearcher", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-498746208", "fields": {"term": "How to protect clients from abuse?", "namespace": "open-p", "hash": -498746208, "terms": ["How to protect clients from abuse?", "to protect clients from abuse?", "protect clients from abuse?", "clients from abuse?", "from abuse?", "abuse?"], "tokens": ["How", "to", "protect", "clients", "from", "abuse", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-973945264", "fields": {"term": "How to rate limit load?", "namespace": "open-p", "hash": -973945264, "terms": ["How to rate limit load?", "to rate limit load?", "rate limit load?", "limit load?", "load?"], "tokens": ["How", "to", "rate", "limit", "load", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1796235529", "fields": {"term": "What are distributor status pages?", "namespace": "open-p", "hash": -1796235529, "terms": ["What are distributor status pages?", "are distributor status pages?", "distributor status pages?", "status pages?", "pages?"], "tokens": ["What", "are", "distributor", "status", "pages", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-679474631", "fields": {"term": "What is the idealstate.merge_bucket.pending metric?", "namespace": "open-p", "hash": -679474631, "terms": ["What is the idealstate.merge_bucket.pending metric?", "is the idealstate.merge_bucket.pending metric?", "the idealstate.merge_bucket.pending metric?", "idealstate.merge_bucket.pending metric?", "metric?"], "tokens": ["What", "is", "the", "idealstate", "merge_bucket", "pending", "metric", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1945916403", "fields": {"term": "How to add/remove capacity from a Vespa cluster?", "namespace": "open-p", "hash": 1945916403, "terms": ["How to add/remove capacity from a Vespa cluster?", "to add/remove capacity from a Vespa cluster?", "add/remove capacity from a Vespa cluster?", "capacity from a Vespa cluster?", "from a Vespa cluster?", "a Vespa cluster?", "Vespa cluster?", "cluster?"], "tokens": ["How", "to", "add", "/", "remove", "capacity", "from", "a", "Vespa", "cluster", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2030712765", "fields": {"term": "How to change from a flat to grouped content cluster?", "namespace": "open-p", "hash": -2030712765, "terms": ["How to change from a flat to grouped content cluster?", "to change from a flat to grouped content cluster?", "change from a flat to grouped content cluster?", "from a flat to grouped content cluster?", "a flat to grouped content cluster?", "flat to grouped content cluster?", "to grouped content cluster?", "grouped content cluster?", "content cluster?", "cluster?"], "tokens": ["How", "to", "change", "from", "a", "flat", "to", "grouped", "content", "cluster", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-397443859", "fields": {"term": "What is idealstate.merge_bucket.pending?", "namespace": "open-p", "hash": -397443859, "terms": ["What is idealstate.merge_bucket.pending?", "is idealstate.merge_bucket.pending?", "idealstate.merge_bucket.pending?"], "tokens": ["What", "is", "idealstate", "merge_bucket", "pending", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::537682398", "fields": {"term": "What is maxpendingidealstateoperations?", "namespace": "open-p", "hash": 537682398, "terms": ["What is maxpendingidealstateoperations?", "is maxpendingidealstateoperations?", "maxpendingidealstateoperations?"], "tokens": ["What", "is", "maxpendingidealstateoperations", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::310189413", "fields": {"term": "How do you add a node to a cluster?", "namespace": "open-p", "hash": 310189413, "terms": ["How do you add a node to a cluster?", "do you add a node to a cluster?", "you add a node to a cluster?", "add a node to a cluster?", "a node to a cluster?", "node to a cluster?", "to a cluster?", "a cluster?", "cluster?"], "tokens": ["How", "do", "you", "add", "a", "node", "to", "a", "cluster", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2059811655", "fields": {"term": "What is the tradeoff when tuning merges?", "namespace": "open-p", "hash": -2059811655, "terms": ["What is the tradeoff when tuning merges?", "is the tradeoff when tuning merges?", "the tradeoff when tuning merges?", "tradeoff when tuning merges?", "when tuning merges?", "tuning merges?", "merges?"], "tokens": ["What", "is", "the", "tradeoff", "when", "tuning", "merges", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1755326031", "fields": {"term": "Can multiple Vespa services run?", "namespace": "open-p", "hash": 1755326031, "terms": ["Can multiple Vespa services run?", "multiple Vespa services run?", "Vespa services run?", "services run?", "run?"], "tokens": ["Can", "multiple", "Vespa", "services", "run", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1557284054", "fields": {"term": "What is the procedure to update Vespa services on a host?", "namespace": "open-p", "hash": 1557284054, "terms": ["What is the procedure to update Vespa services on a host?", "is the procedure to update Vespa services on a host?", "the procedure to update Vespa services on a host?", "procedure to update Vespa services on a host?", "to update Vespa services on a host?", "update Vespa services on a host?", "Vespa services on a host?", "services on a host?", "on a host?", "a host?", "host?"], "tokens": ["What", "is", "the", "procedure", "to", "update", "Vespa", "services", "on", "a", "host", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1567075455", "fields": {"term": "Why should Vespa be stopped before activating deployment on a node?", "namespace": "open-p", "hash": -1567075455, "terms": ["Why should Vespa be stopped before activating deployment on a node?", "should Vespa be stopped before activating deployment on a node?", "Vespa be stopped before activating deployment on a node?", "be stopped before activating deployment on a node?", "stopped before activating deployment on a node?", "before activating deployment on a node?", "activating deployment on a node?", "deployment on a node?", "on a node?", "a node?", "node?"], "tokens": ["Why", "should", "Vespa", "be", "stopped", "before", "activating", "deployment", "on", "a", "node", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1438145786", "fields": {"term": "Can processing chains be modified?", "namespace": "open-p", "hash": 1438145786, "terms": ["Can processing chains be modified?", "processing chains be modified?", "chains be modified?", "be modified?", "modified?"], "tokens": ["Can", "processing", "chains", "be", "modified", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::503508747", "fields": {"term": "Can Document processing be modified at runtime?", "namespace": "open-p", "hash": 503508747, "terms": ["Can Document processing be modified at runtime?", "Document processing be modified at runtime?", "processing be modified at runtime?", "be modified at runtime?", "modified at runtime?", "at runtime?", "runtime?"], "tokens": ["Can", "Document", "processing", "be", "modified", "at", "runtime", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1428336726", "fields": {"term": "What changes can be made at runtime?", "namespace": "open-p", "hash": 1428336726, "terms": ["What changes can be made at runtime?", "changes can be made at runtime?", "can be made at runtime?", "be made at runtime?", "made at runtime?", "at runtime?", "runtime?"], "tokens": ["What", "changes", "can", "be", "made", "at", "runtime", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::798771260", "fields": {"term": "Does all changes require a container restart?", "namespace": "open-p", "hash": 798771260, "terms": ["Does all changes require a container restart?", "all changes require a container restart?", "changes require a container restart?", "require a container restart?", "a container restart?", "container restart?", "restart?"], "tokens": ["Does", "all", "changes", "require", "a", "container", "restart", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1593208349", "fields": {"term": "How to allocate enough memory to docker?", "namespace": "open-p", "hash": 1593208349, "terms": ["How to allocate enough memory to docker?", "to allocate enough memory to docker?", "allocate enough memory to docker?", "enough memory to docker?", "memory to docker?", "to docker?", "docker?"], "tokens": ["How", "to", "allocate", "enough", "memory", "to", "docker", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-399242890", "fields": {"term": "What causes OOM symptoms?", "namespace": "open-p", "hash": -399242890, "terms": ["What causes OOM symptoms?", "causes OOM symptoms?", "OOM symptoms?", "symptoms?"], "tokens": ["What", "causes", "OOM", "symptoms", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1079339082", "fields": {"term": "What is tracelevel used for?", "namespace": "open-p", "hash": -1079339082, "terms": ["What is tracelevel used for?", "is tracelevel used for?", "tracelevel used for?", "used for?", "for?"], "tokens": ["What", "is", "tracelevel", "used", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::686834567", "fields": {"term": "How to pretty-print json?", "namespace": "open-p", "hash": 686834567, "terms": ["How to pretty-print json?", "to pretty-print json?", "pretty-print json?", "json?"], "tokens": ["How", "to", "pretty", "print", "json", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1029199722", "fields": {"term": "What is validate_and_sanitize_docstore?", "namespace": "open-p", "hash": -1029199722, "terms": ["What is validate_and_sanitize_docstore?", "is validate_and_sanitize_docstore?", "validate_and_sanitize_docstore?"], "tokens": ["What", "is", "validate_and_sanitize_docstore", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::209493381", "fields": {"term": "When should I use validate_and_sanitize_docstore?", "namespace": "open-p", "hash": 209493381, "terms": ["When should I use validate_and_sanitize_docstore?", "should I use validate_and_sanitize_docstore?", "I use validate_and_sanitize_docstore?", "use validate_and_sanitize_docstore?", "validate_and_sanitize_docstore?"], "tokens": ["When", "should", "I", "use", "validate_and_sanitize_docstore", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1997260274", "fields": {"term": "How does validate_and_sanitize_docstore mode work?", "namespace": "open-p", "hash": 1997260274, "terms": ["How does validate_and_sanitize_docstore mode work?", "does validate_and_sanitize_docstore mode work?", "validate_and_sanitize_docstore mode work?", "mode work?", "work?"], "tokens": ["How", "does", "validate_and_sanitize_docstore", "mode", "work", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-120426928", "fields": {"term": "What is the outcome of inconsistency data?", "namespace": "open-p", "hash": -120426928, "terms": ["What is the outcome of inconsistency data?", "is the outcome of inconsistency data?", "the outcome of inconsistency data?", "outcome of inconsistency data?", "of inconsistency data?", "inconsistency data?", "data?"], "tokens": ["What", "is", "the", "outcome", "of", "inconsistency", "data", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::649169103", "fields": {"term": "What is remove-entries?", "namespace": "open-p", "hash": 649169103, "terms": ["What is remove-entries?", "is remove-entries?", "remove-entries?"], "tokens": ["What", "is", "remove", "entries", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1619074174", "fields": {"term": "What is transition-time?", "namespace": "open-p", "hash": 1619074174, "terms": ["What is transition-time?", "is transition-time?", "transition-time?"], "tokens": ["What", "is", "transition", "time", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::-1409892928", "fields": {"term": "How many times can a node crash?", "namespace": "open-p", "hash": -1409892928, "terms": ["How many times can a node crash?", "many times can a node crash?", "times can a node crash?", "can a node crash?", "a node crash?", "node crash?", "crash?"], "tokens": ["How", "many", "times", "can", "a", "node", "crash", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::543237672", "fields": {"term": "What happens when a cluster loses nodes?", "namespace": "open-p", "hash": 543237672, "terms": ["What happens when a cluster loses nodes?", "happens when a cluster loses nodes?", "when a cluster loses nodes?", "a cluster loses nodes?", "cluster loses nodes?", "loses nodes?", "nodes?"], "tokens": ["What", "happens", "when", "a", "cluster", "loses", "nodes", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1199655271", "fields": {"term": "What is min-distributor-up-ratio?", "namespace": "open-p", "hash": -1199655271, "terms": ["What is min-distributor-up-ratio?", "is min-distributor-up-ratio?", "min-distributor-up-ratio?"], "tokens": ["What", "is", "min", "distributor", "up", "ratio", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::-273856676", "fields": {"term": "What is min-storage-up-ratio?", "namespace": "open-p", "hash": -273856676, "terms": ["What is min-storage-up-ratio?", "is min-storage-up-ratio?", "min-storage-up-ratio?"], "tokens": ["What", "is", "min", "storage", "up", "ratio", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::1441554591", "fields": {"term": "What is the stable state period?", "namespace": "open-p", "hash": 1441554591, "terms": ["What is the stable state period?", "is the stable state period?", "the stable state period?", "stable state period?", "state period?", "period?"], "tokens": ["What", "is", "the", "stable", "state", "period", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1738933449", "fields": {"term": "What is the Vespa indexing language?", "namespace": "open-p", "hash": 1738933449, "terms": ["What is the Vespa indexing language?", "is the Vespa indexing language?", "the Vespa indexing language?", "Vespa indexing language?", "indexing language?", "language?"], "tokens": ["What", "is", "the", "Vespa", "indexing", "language", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-912075440", "fields": {"term": "How is the Vespa indexing language structured?", "namespace": "open-p", "hash": -912075440, "terms": ["How is the Vespa indexing language structured?", "is the Vespa indexing language structured?", "the Vespa indexing language structured?", "Vespa indexing language structured?", "indexing language structured?", "language structured?", "structured?"], "tokens": ["How", "is", "the", "Vespa", "indexing", "language", "structured", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1591794585", "fields": {"term": "What is the purpose of the Vespa indexing language?", "namespace": "open-p", "hash": -1591794585, "terms": ["What is the purpose of the Vespa indexing language?", "is the purpose of the Vespa indexing language?", "the purpose of the Vespa indexing language?", "purpose of the Vespa indexing language?", "of the Vespa indexing language?", "the Vespa indexing language?", "Vespa indexing language?", "indexing language?", "language?"], "tokens": ["What", "is", "the", "purpose", "of", "the", "Vespa", "indexing", "language", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1342403339", "fields": {"term": "What is an indexing script?", "namespace": "open-p", "hash": -1342403339, "terms": ["What is an indexing script?", "is an indexing script?", "an indexing script?", "indexing script?", "script?"], "tokens": ["What", "is", "an", "indexing", "script", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1854103775", "fields": {"term": "How is an indexing script executed?", "namespace": "open-p", "hash": -1854103775, "terms": ["How is an indexing script executed?", "is an indexing script executed?", "an indexing script executed?", "indexing script executed?", "script executed?", "executed?"], "tokens": ["How", "is", "an", "indexing", "script", "executed", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1316563774", "fields": {"term": "What is an indexing cluster?", "namespace": "open-p", "hash": 1316563774, "terms": ["What is an indexing cluster?", "is an indexing cluster?", "an indexing cluster?", "indexing cluster?", "cluster?"], "tokens": ["What", "is", "an", "indexing", "cluster", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::819105857", "fields": {"term": "How can you examine the indexing script for a specific search cluster?", "namespace": "open-p", "hash": 819105857, "terms": ["How can you examine the indexing script for a specific search cluster?", "can you examine the indexing script for a specific search cluster?", "you examine the indexing script for a specific search cluster?", "examine the indexing script for a specific search cluster?", "the indexing script for a specific search cluster?", "indexing script for a specific search cluster?", "script for a specific search cluster?", "for a specific search cluster?", "a specific search cluster?", "specific search cluster?", "search cluster?", "cluster?"], "tokens": ["How", "can", "you", "examine", "the", "indexing", "script", "for", "a", "specific", "search", "cluster", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1478850577", "fields": {"term": "What is an indexing statement?", "namespace": "open-p", "hash": 1478850577, "terms": ["What is an indexing statement?", "is an indexing statement?", "an indexing statement?", "indexing statement?", "statement?"], "tokens": ["What", "is", "an", "indexing", "statement", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1857806790", "fields": {"term": "What is an indexing expression?", "namespace": "open-p", "hash": 1857806790, "terms": ["What is an indexing expression?", "is an indexing expression?", "an indexing expression?", "indexing expression?", "expression?"], "tokens": ["What", "is", "an", "indexing", "expression", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1206828429", "fields": {"term": "What is the pipe used for in an indexing statement?", "namespace": "open-p", "hash": -1206828429, "terms": ["What is the pipe used for in an indexing statement?", "is the pipe used for in an indexing statement?", "the pipe used for in an indexing statement?", "pipe used for in an indexing statement?", "used for in an indexing statement?", "for in an indexing statement?", "in an indexing statement?", "an indexing statement?", "indexing statement?", "statement?"], "tokens": ["What", "is", "the", "pipe", "used", "for", "in", "an", "indexing", "statement", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1839777958", "fields": {"term": "What happens during execution of an indexing statement?", "namespace": "open-p", "hash": -1839777958, "terms": ["What happens during execution of an indexing statement?", "happens during execution of an indexing statement?", "during execution of an indexing statement?", "execution of an indexing statement?", "of an indexing statement?", "an indexing statement?", "indexing statement?", "statement?"], "tokens": ["What", "happens", "during", "execution", "of", "an", "indexing", "statement", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1359481588", "fields": {"term": "How does a simple indexing statement work?", "namespace": "open-p", "hash": 1359481588, "terms": ["How does a simple indexing statement work?", "does a simple indexing statement work?", "a simple indexing statement work?", "simple indexing statement work?", "indexing statement work?", "statement work?", "work?"], "tokens": ["How", "does", "a", "simple", "indexing", "statement", "work", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1088911518", "fields": {"term": "What are the examples of expression literals?", "namespace": "open-p", "hash": -1088911518, "terms": ["What are the examples of expression literals?", "are the examples of expression literals?", "the examples of expression literals?", "examples of expression literals?", "of expression literals?", "expression literals?", "literals?"], "tokens": ["What", "are", "the", "examples", "of", "expression", "literals", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1881617028", "fields": {"term": "What is an expression literal?", "namespace": "open-p", "hash": 1881617028, "terms": ["What is an expression literal?", "is an expression literal?", "an expression literal?", "expression literal?", "literal?"], "tokens": ["What", "is", "an", "expression", "literal", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1662389310", "fields": {"term": "How can a string be an expression literal?", "namespace": "open-p", "hash": 1662389310, "terms": ["How can a string be an expression literal?", "can a string be an expression literal?", "a string be an expression literal?", "string be an expression literal?", "be an expression literal?", "an expression literal?", "expression literal?", "literal?"], "tokens": ["How", "can", "a", "string", "be", "an", "expression", "literal", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1332794559", "fields": {"term": "What is an output expression?", "namespace": "open-p", "hash": -1332794559, "terms": ["What is an output expression?", "is an output expression?", "an output expression?", "output expression?", "expression?"], "tokens": ["What", "is", "an", "output", "expression", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-739440202", "fields": {"term": "What is an index field?", "namespace": "open-p", "hash": -739440202, "terms": ["What is an index field?", "is an index field?", "an index field?", "index field?", "field?"], "tokens": ["What", "is", "an", "index", "field", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1577943979", "fields": {"term": "What can indexing statements contain?", "namespace": "open-p", "hash": 1577943979, "terms": ["What can indexing statements contain?", "can indexing statements contain?", "indexing statements contain?", "statements contain?", "contain?"], "tokens": ["What", "can", "indexing", "statements", "contain", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::274014233", "fields": {"term": "What arithmetic operators are supported?", "namespace": "open-p", "hash": 274014233, "terms": ["What arithmetic operators are supported?", "arithmetic operators are supported?", "operators are supported?", "are supported?", "supported?"], "tokens": ["What", "arithmetic", "operators", "are", "supported", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::-115814044", "fields": {"term": "What is the embed converter?", "namespace": "open-p", "hash": -115814044, "terms": ["What is the embed converter?", "is the embed converter?", "the embed converter?", "embed converter?", "converter?"], "tokens": ["What", "is", "the", "embed", "converter", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::187386134", "fields": {"term": "What does the hash converter do?", "namespace": "open-p", "hash": 187386134, "terms": ["What does the hash converter do?", "does the hash converter do?", "the hash converter do?", "hash converter do?", "converter do?", "do?"], "tokens": ["What", "does", "the", "hash", "converter", "do", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::399286082", "fields": {"term": "What is the input for to_pos?", "namespace": "open-p", "hash": 399286082, "terms": ["What is the input for to_pos?", "is the input for to_pos?", "the input for to_pos?", "input for to_pos?", "for to_pos?", "to_pos?"], "tokens": ["What", "is", "the", "input", "for", "to_pos", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-109650416", "fields": {"term": "What is the output of to_uri?", "namespace": "open-p", "hash": -109650416, "terms": ["What is the output of to_uri?", "is the output of to_uri?", "the output of to_uri?", "output of to_uri?", "of to_uri?", "to_uri?"], "tokens": ["What", "is", "the", "output", "of", "to_uri", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::858710534", "fields": {"term": "What is the to_int converter?", "namespace": "open-p", "hash": 858710534, "terms": ["What is the to_int converter?", "is the to_int converter?", "the to_int converter?", "to_int converter?", "converter?"], "tokens": ["What", "is", "the", "to_int", "converter", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-595584622", "fields": {"term": "What is the to_bool converter?", "namespace": "open-p", "hash": -595584622, "terms": ["What is the to_bool converter?", "is the to_bool converter?", "the to_bool converter?", "to_bool converter?", "converter?"], "tokens": ["What", "is", "the", "to_bool", "converter", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-639058956", "fields": {"term": "What is the to_float converter?", "namespace": "open-p", "hash": -639058956, "terms": ["What is the to_float converter?", "is the to_float converter?", "the to_float converter?", "to_float converter?", "converter?"], "tokens": ["What", "is", "the", "to_float", "converter", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-613668979", "fields": {"term": "What is a converter?", "namespace": "open-p", "hash": -613668979, "terms": ["What is a converter?", "is a converter?", "a converter?", "converter?"], "tokens": ["What", "is", "a", "converter", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-665196281", "fields": {"term": "What is ngram?", "namespace": "open-p", "hash": -665196281, "terms": ["What is ngram?", "is ngram?", "ngram?"], "tokens": ["What", "is", "ngram", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-440251497", "fields": {"term": "What is set_var?", "namespace": "open-p", "hash": -440251497, "terms": ["What is set_var?", "is set_var?", "set_var?"], "tokens": ["What", "is", "set_var", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::169342409", "fields": {"term": "What is execution value?", "namespace": "open-p", "hash": 169342409, "terms": ["What is execution value?", "is execution value?", "execution value?", "value?"], "tokens": ["What", "is", "execution", "value", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1764777675", "fields": {"term": "How to concatenate titles with sentences?", "namespace": "open-p", "hash": -1764777675, "terms": ["How to concatenate titles with sentences?", "to concatenate titles with sentences?", "concatenate titles with sentences?", "titles with sentences?", "with sentences?", "sentences?"], "tokens": ["How", "to", "concatenate", "titles", "with", "sentences", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-763332458", "fields": {"term": "What is the choice expression?", "namespace": "open-p", "hash": -763332458, "terms": ["What is the choice expression?", "is the choice expression?", "the choice expression?", "choice expression?", "expression?"], "tokens": ["What", "is", "the", "choice", "expression", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1049462843", "fields": {"term": "How does choice expression handle null values?", "namespace": "open-p", "hash": -1049462843, "terms": ["How does choice expression handle null values?", "does choice expression handle null values?", "choice expression handle null values?", "expression handle null values?", "handle null values?", "null values?", "values?"], "tokens": ["How", "does", "choice", "expression", "handle", "null", "values", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1878356909", "fields": {"term": "What happens if a field is empty when concatenating fields?", "namespace": "open-p", "hash": -1878356909, "terms": ["What happens if a field is empty when concatenating fields?", "happens if a field is empty when concatenating fields?", "if a field is empty when concatenating fields?", "a field is empty when concatenating fields?", "field is empty when concatenating fields?", "is empty when concatenating fields?", "empty when concatenating fields?", "when concatenating fields?", "concatenating fields?", "fields?"], "tokens": ["What", "happens", "if", "a", "field", "is", "empty", "when", "concatenating", "fields", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1685693295", "fields": {"term": "What is produced if either input field is missing?", "namespace": "open-p", "hash": 1685693295, "terms": ["What is produced if either input field is missing?", "is produced if either input field is missing?", "produced if either input field is missing?", "if either input field is missing?", "either input field is missing?", "input field is missing?", "field is missing?", "is missing?", "missing?"], "tokens": ["What", "is", "produced", "if", "either", "input", "field", "is", "missing", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-105502077", "fields": {"term": "What is the select_input expression?", "namespace": "open-p", "hash": -105502077, "terms": ["What is the select_input expression?", "is the select_input expression?", "the select_input expression?", "select_input expression?", "expression?"], "tokens": ["What", "is", "the", "select_input", "expression", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::832446144", "fields": {"term": "How does select_input choose a statement?", "namespace": "open-p", "hash": 832446144, "terms": ["How does select_input choose a statement?", "does select_input choose a statement?", "select_input choose a statement?", "choose a statement?", "a statement?", "statement?"], "tokens": ["How", "does", "select_input", "choose", "a", "statement", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-328905187", "fields": {"term": "What statement does select_input execute?", "namespace": "open-p", "hash": -328905187, "terms": ["What statement does select_input execute?", "statement does select_input execute?", "does select_input execute?", "select_input execute?", "execute?"], "tokens": ["What", "statement", "does", "select_input", "execute", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1359927881", "fields": {"term": "What happens if CX is empty?", "namespace": "open-p", "hash": 1359927881, "terms": ["What happens if CX is empty?", "happens if CX is empty?", "if CX is empty?", "CX is empty?", "is empty?", "empty?"], "tokens": ["What", "happens", "if", "CX", "is", "empty", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1102828954", "fields": {"term": "What is the syntax of select_input?", "namespace": "open-p", "hash": 1102828954, "terms": ["What is the syntax of select_input?", "is the syntax of select_input?", "the syntax of select_input?", "syntax of select_input?", "of select_input?", "select_input?"], "tokens": ["What", "is", "the", "syntax", "of", "select_input", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1368687371", "fields": {"term": "What are the variables used in select_input?", "namespace": "open-p", "hash": 1368687371, "terms": ["What are the variables used in select_input?", "are the variables used in select_input?", "the variables used in select_input?", "variables used in select_input?", "used in select_input?", "in select_input?", "select_input?"], "tokens": ["What", "are", "the", "variables", "used", "in", "select_input", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1754565222", "fields": {"term": "How does select_input handle non-empty fields?", "namespace": "open-p", "hash": 1754565222, "terms": ["How does select_input handle non-empty fields?", "does select_input handle non-empty fields?", "select_input handle non-empty fields?", "handle non-empty fields?", "non-empty fields?", "fields?"], "tokens": ["How", "does", "select_input", "handle", "non", "empty", "fields", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::310198", "fields": {"term": "What does the select_input expression do?", "namespace": "open-p", "hash": 310198, "terms": ["What does the select_input expression do?", "does the select_input expression do?", "the select_input expression do?", "select_input expression do?", "expression do?", "do?"], "tokens": ["What", "does", "the", "select_input", "expression", "do", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::332117039", "fields": {"term": "What is a switch-expression?", "namespace": "open-p", "hash": 332117039, "terms": ["What is a switch-expression?", "is a switch-expression?", "a switch-expression?", "switch-expression?"], "tokens": ["What", "is", "a", "switch", "expression", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1274295715", "fields": {"term": "How does switch-expression work?", "namespace": "open-p", "hash": 1274295715, "terms": ["How does switch-expression work?", "does switch-expression work?", "switch-expression work?", "work?"], "tokens": ["How", "does", "switch", "expression", "work", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1019458706", "fields": {"term": "What is a case in switch-expression?", "namespace": "open-p", "hash": 1019458706, "terms": ["What is a case in switch-expression?", "is a case in switch-expression?", "a case in switch-expression?", "case in switch-expression?", "in switch-expression?", "switch-expression?"], "tokens": ["What", "is", "a", "case", "in", "switch", "expression", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1028679769", "fields": {"term": "What is default operation in switch?", "namespace": "open-p", "hash": -1028679769, "terms": ["What is default operation in switch?", "is default operation in switch?", "default operation in switch?", "operation in switch?", "in switch?", "switch?"], "tokens": ["What", "is", "default", "operation", "in", "switch", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::515742447", "fields": {"term": "How to use switch-expression in Vespa?", "namespace": "open-p", "hash": 515742447, "terms": ["How to use switch-expression in Vespa?", "to use switch-expression in Vespa?", "use switch-expression in Vespa?", "switch-expression in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["How", "to", "use", "switch", "expression", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::304026618", "fields": {"term": "What are indexing statements?", "namespace": "open-p", "hash": 304026618, "terms": ["What are indexing statements?", "are indexing statements?", "indexing statements?", "statements?"], "tokens": ["What", "are", "indexing", "statements", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1843051365", "fields": {"term": "How are multiple fields combined?", "namespace": "open-p", "hash": -1843051365, "terms": ["How are multiple fields combined?", "are multiple fields combined?", "multiple fields combined?", "fields combined?", "combined?"], "tokens": ["How", "are", "multiple", "fields", "combined", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2056482048", "fields": {"term": "What fields are updated on partial update?", "namespace": "open-p", "hash": 2056482048, "terms": ["What fields are updated on partial update?", "fields are updated on partial update?", "are updated on partial update?", "updated on partial update?", "on partial update?", "partial update?", "update?"], "tokens": ["What", "fields", "are", "updated", "on", "partial", "update", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::183841790", "fields": {"term": "What happens if a partial update contains only one indexed field?", "namespace": "open-p", "hash": 183841790, "terms": ["What happens if a partial update contains only one indexed field?", "happens if a partial update contains only one indexed field?", "if a partial update contains only one indexed field?", "a partial update contains only one indexed field?", "partial update contains only one indexed field?", "update contains only one indexed field?", "contains only one indexed field?", "only one indexed field?", "one indexed field?", "indexed field?", "field?"], "tokens": ["What", "happens", "if", "a", "partial", "update", "contains", "only", "one", "indexed", "field", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1811013302", "fields": {"term": "What happens if a partial update contains multiple indexed fields?", "namespace": "open-p", "hash": -1811013302, "terms": ["What happens if a partial update contains multiple indexed fields?", "happens if a partial update contains multiple indexed fields?", "if a partial update contains multiple indexed fields?", "a partial update contains multiple indexed fields?", "partial update contains multiple indexed fields?", "update contains multiple indexed fields?", "contains multiple indexed fields?", "multiple indexed fields?", "indexed fields?", "fields?"], "tokens": ["What", "happens", "if", "a", "partial", "update", "contains", "multiple", "indexed", "fields", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-562659118", "fields": {"term": "What is the Annotations API?", "namespace": "open-p", "hash": -562659118, "terms": ["What is the Annotations API?", "is the Annotations API?", "the Annotations API?", "Annotations API?", "API?"], "tokens": ["What", "is", "the", "Annotations", "API", "?"], "corpus_count": 5, "document_count": 5}}, -{"put": "id:term:term::403133462", "fields": {"term": "What are the use cases for Annotations API?", "namespace": "open-p", "hash": 403133462, "terms": ["What are the use cases for Annotations API?", "are the use cases for Annotations API?", "the use cases for Annotations API?", "use cases for Annotations API?", "cases for Annotations API?", "for Annotations API?", "Annotations API?", "API?"], "tokens": ["What", "are", "the", "use", "cases", "for", "Annotations", "API", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1844837785", "fields": {"term": "How to use the Annotations API?", "namespace": "open-p", "hash": -1844837785, "terms": ["How to use the Annotations API?", "to use the Annotations API?", "use the Annotations API?", "the Annotations API?", "Annotations API?", "API?"], "tokens": ["How", "to", "use", "the", "Annotations", "API", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1358961014", "fields": {"term": "What are the Annotations API examples?", "namespace": "open-p", "hash": 1358961014, "terms": ["What are the Annotations API examples?", "are the Annotations API examples?", "the Annotations API examples?", "Annotations API examples?", "API examples?", "examples?"], "tokens": ["What", "are", "the", "Annotations", "API", "examples", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::539731889", "fields": {"term": "What is a span tree?", "namespace": "open-p", "hash": 539731889, "terms": ["What is a span tree?", "is a span tree?", "a span tree?", "span tree?", "tree?"], "tokens": ["What", "is", "a", "span", "tree", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::-1115433718", "fields": {"term": "What is an annotation?", "namespace": "open-p", "hash": -1115433718, "terms": ["What is an annotation?", "is an annotation?", "an annotation?", "annotation?"], "tokens": ["What", "is", "an", "annotation", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::800463759", "fields": {"term": "How do you create a span tree?", "namespace": "open-p", "hash": 800463759, "terms": ["How do you create a span tree?", "do you create a span tree?", "you create a span tree?", "create a span tree?", "a span tree?", "span tree?", "tree?"], "tokens": ["How", "do", "you", "create", "a", "span", "tree", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1781459616", "fields": {"term": "What are spans?", "namespace": "open-p", "hash": -1781459616, "terms": ["What are spans?", "are spans?", "spans?"], "tokens": ["What", "are", "spans", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::395940267", "fields": {"term": "How do you use the Annotations API?", "namespace": "open-p", "hash": 395940267, "terms": ["How do you use the Annotations API?", "do you use the Annotations API?", "you use the Annotations API?", "use the Annotations API?", "the Annotations API?", "Annotations API?", "API?"], "tokens": ["How", "do", "you", "use", "the", "Annotations", "API", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1309442900", "fields": {"term": "What is an AnnotationTypeRegistry?", "namespace": "open-p", "hash": -1309442900, "terms": ["What is an AnnotationTypeRegistry?", "is an AnnotationTypeRegistry?", "an AnnotationTypeRegistry?", "AnnotationTypeRegistry?"], "tokens": ["What", "is", "an", "AnnotationTypeRegistry", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1160980459", "fields": {"term": "What is the markup annotation type?", "namespace": "open-p", "hash": -1160980459, "terms": ["What is the markup annotation type?", "is the markup annotation type?", "the markup annotation type?", "markup annotation type?", "annotation type?", "type?"], "tokens": ["What", "is", "the", "markup", "annotation", "type", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::402647091", "fields": {"term": "What are SpanNodes?", "namespace": "open-p", "hash": 402647091, "terms": ["What are SpanNodes?", "are SpanNodes?", "SpanNodes?"], "tokens": ["What", "are", "SpanNodes", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1789607056", "fields": {"term": "What is a SpanList?", "namespace": "open-p", "hash": 1789607056, "terms": ["What is a SpanList?", "is a SpanList?", "a SpanList?", "SpanList?"], "tokens": ["What", "is", "a", "SpanList", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-68862763", "fields": {"term": "What is a SpanTree?", "namespace": "open-p", "hash": -68862763, "terms": ["What is a SpanTree?", "is a SpanTree?", "a SpanTree?", "SpanTree?"], "tokens": ["What", "is", "a", "SpanTree", "?"], "corpus_count": 3, "document_count": 3}}, -{"put": "id:term:term::1038320715", "fields": {"term": "How do you annotate?", "namespace": "open-p", "hash": 1038320715, "terms": ["How do you annotate?", "do you annotate?", "you annotate?", "annotate?"], "tokens": ["How", "do", "you", "annotate", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::541157104", "fields": {"term": "What is an annotation type?", "namespace": "open-p", "hash": 541157104, "terms": ["What is an annotation type?", "is an annotation type?", "an annotation type?", "annotation type?", "type?"], "tokens": ["What", "is", "an", "annotation", "type", "?"], "corpus_count": 3, "document_count": 3}}, -{"put": "id:term:term::-1019212860", "fields": {"term": "What is the Annotation API?", "namespace": "open-p", "hash": -1019212860, "terms": ["What is the Annotation API?", "is the Annotation API?", "the Annotation API?", "Annotation API?", "API?"], "tokens": ["What", "is", "the", "Annotation", "API", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1846564928", "fields": {"term": "What is a SpanNode?", "namespace": "open-p", "hash": 1846564928, "terms": ["What is a SpanNode?", "is a SpanNode?", "a SpanNode?", "SpanNode?"], "tokens": ["What", "is", "a", "SpanNode", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::1127879033", "fields": {"term": "How do you create an annotation?", "namespace": "open-p", "hash": 1127879033, "terms": ["How do you create an annotation?", "do you create an annotation?", "you create an annotation?", "create an annotation?", "an annotation?", "annotation?"], "tokens": ["How", "do", "you", "create", "an", "annotation", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1500030428", "fields": {"term": "What is the purpose of Struct position?", "namespace": "open-p", "hash": -1500030428, "terms": ["What is the purpose of Struct position?", "is the purpose of Struct position?", "the purpose of Struct position?", "purpose of Struct position?", "of Struct position?", "Struct position?", "position?"], "tokens": ["What", "is", "the", "purpose", "of", "Struct", "position", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::940485452", "fields": {"term": "What is the value of the position for San Francisco?", "namespace": "open-p", "hash": 940485452, "terms": ["What is the value of the position for San Francisco?", "is the value of the position for San Francisco?", "the value of the position for San Francisco?", "value of the position for San Francisco?", "of the position for San Francisco?", "the position for San Francisco?", "position for San Francisco?", "for San Francisco?", "San Francisco?", "Francisco?"], "tokens": ["What", "is", "the", "value", "of", "the", "position", "for", "San", "Francisco", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1992989695", "fields": {"term": "How is latitude and longitude added to an annotation?", "namespace": "open-p", "hash": 1992989695, "terms": ["How is latitude and longitude added to an annotation?", "is latitude and longitude added to an annotation?", "latitude and longitude added to an annotation?", "and longitude added to an annotation?", "longitude added to an annotation?", "added to an annotation?", "to an annotation?", "an annotation?", "annotation?"], "tokens": ["How", "is", "latitude", "and", "longitude", "added", "to", "an", "annotation", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::753601003", "fields": {"term": "What is the purpose of annotator?", "namespace": "open-p", "hash": 753601003, "terms": ["What is the purpose of annotator?", "is the purpose of annotator?", "the purpose of annotator?", "purpose of annotator?", "of annotator?", "annotator?"], "tokens": ["What", "is", "the", "purpose", "of", "annotator", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1017979503", "fields": {"term": "What is an AlternateSpanList used for?", "namespace": "open-p", "hash": 1017979503, "terms": ["What is an AlternateSpanList used for?", "is an AlternateSpanList used for?", "an AlternateSpanList used for?", "AlternateSpanList used for?", "used for?", "for?"], "tokens": ["What", "is", "an", "AlternateSpanList", "used", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1932324546", "fields": {"term": "What are annotations in Vespa?", "namespace": "open-p", "hash": -1932324546, "terms": ["What are annotations in Vespa?", "are annotations in Vespa?", "annotations in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["What", "are", "annotations", "in", "Vespa", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::389810743", "fields": {"term": "How can you annotate a document using Vespa?", "namespace": "open-p", "hash": 389810743, "terms": ["How can you annotate a document using Vespa?", "can you annotate a document using Vespa?", "you annotate a document using Vespa?", "annotate a document using Vespa?", "a document using Vespa?", "document using Vespa?", "using Vespa?", "Vespa?"], "tokens": ["How", "can", "you", "annotate", "a", "document", "using", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1046049832", "fields": {"term": "What is an AnnotationType in Vespa?", "namespace": "open-p", "hash": -1046049832, "terms": ["What is an AnnotationType in Vespa?", "is an AnnotationType in Vespa?", "an AnnotationType in Vespa?", "AnnotationType in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["What", "is", "an", "AnnotationType", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::457209988", "fields": {"term": "What is an Annotation reference in Vespa?", "namespace": "open-p", "hash": 457209988, "terms": ["What is an Annotation reference in Vespa?", "is an Annotation reference in Vespa?", "an Annotation reference in Vespa?", "Annotation reference in Vespa?", "reference in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["What", "is", "an", "Annotation", "reference", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::646662544", "fields": {"term": "What is the purpose of using annotation references in Vespa?", "namespace": "open-p", "hash": 646662544, "terms": ["What is the purpose of using annotation references in Vespa?", "is the purpose of using annotation references in Vespa?", "the purpose of using annotation references in Vespa?", "purpose of using annotation references in Vespa?", "of using annotation references in Vespa?", "using annotation references in Vespa?", "annotation references in Vespa?", "references in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["What", "is", "the", "purpose", "of", "using", "annotation", "references", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1646743349", "fields": {"term": "What is docproc framework?", "namespace": "open-p", "hash": -1646743349, "terms": ["What is docproc framework?", "is docproc framework?", "docproc framework?", "framework?"], "tokens": ["What", "is", "docproc", "framework", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1331384315", "fields": {"term": "How does Annotations API work?", "namespace": "open-p", "hash": -1331384315, "terms": ["How does Annotations API work?", "does Annotations API work?", "Annotations API work?", "API work?", "work?"], "tokens": ["How", "does", "Annotations", "API", "work", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1303450487", "fields": {"term": "How can one remove markup annotations?", "namespace": "open-p", "hash": 1303450487, "terms": ["How can one remove markup annotations?", "can one remove markup annotations?", "one remove markup annotations?", "remove markup annotations?", "markup annotations?", "annotations?"], "tokens": ["How", "can", "one", "remove", "markup", "annotations", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1846518225", "fields": {"term": "What is a ListIterator?", "namespace": "open-p", "hash": 1846518225, "terms": ["What is a ListIterator?", "is a ListIterator?", "a ListIterator?", "ListIterator?"], "tokens": ["What", "is", "a", "ListIterator", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1512504370", "fields": {"term": "What is inheritance in annotation types?", "namespace": "open-p", "hash": 1512504370, "terms": ["What is inheritance in annotation types?", "is inheritance in annotation types?", "inheritance in annotation types?", "in annotation types?", "annotation types?", "types?"], "tokens": ["What", "is", "inheritance", "in", "annotation", "types", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-211639633", "fields": {"term": "How can annotation types be extended?", "namespace": "open-p", "hash": -211639633, "terms": ["How can annotation types be extended?", "can annotation types be extended?", "annotation types be extended?", "types be extended?", "be extended?", "extended?"], "tokens": ["How", "can", "annotation", "types", "be", "extended", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1557481996", "fields": {"term": "What is the Tenant API?", "namespace": "open-p", "hash": -1557481996, "terms": ["What is the Tenant API?", "is the Tenant API?", "the Tenant API?", "Tenant API?", "API?"], "tokens": ["What", "is", "the", "Tenant", "API", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2123279326", "fields": {"term": "What does the Tenant API enable?", "namespace": "open-p", "hash": -2123279326, "terms": ["What does the Tenant API enable?", "does the Tenant API enable?", "the Tenant API enable?", "Tenant API enable?", "API enable?", "enable?"], "tokens": ["What", "does", "the", "Tenant", "API", "enable", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1257443151", "fields": {"term": "How do I configure multiple tenants?", "namespace": "open-p", "hash": -1257443151, "terms": ["How do I configure multiple tenants?", "do I configure multiple tenants?", "I configure multiple tenants?", "configure multiple tenants?", "multiple tenants?", "tenants?"], "tokens": ["How", "do", "I", "configure", "multiple", "tenants", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1383248414", "fields": {"term": "How can I set configuration using the Config API?", "namespace": "open-p", "hash": -1383248414, "terms": ["How can I set configuration using the Config API?", "can I set configuration using the Config API?", "I set configuration using the Config API?", "set configuration using the Config API?", "configuration using the Config API?", "using the Config API?", "the Config API?", "Config API?", "API?"], "tokens": ["How", "can", "I", "set", "configuration", "using", "the", "Config", "API", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1864754194", "fields": {"term": "What is Vespa Java Document API?", "namespace": "open-p", "hash": -1864754194, "terms": ["What is Vespa Java Document API?", "is Vespa Java Document API?", "Vespa Java Document API?", "Java Document API?", "Document API?", "API?"], "tokens": ["What", "is", "Vespa", "Java", "Document", "API", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::281591547", "fields": {"term": "What is the Feeding API?", "namespace": "open-p", "hash": 281591547, "terms": ["What is the Feeding API?", "is the Feeding API?", "the Feeding API?", "Feeding API?", "API?"], "tokens": ["What", "is", "the", "Feeding", "API", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1962881706", "fields": {"term": "What is the purpose of document/v1 REST API?", "namespace": "open-p", "hash": 1962881706, "terms": ["What is the purpose of document/v1 REST API?", "is the purpose of document/v1 REST API?", "the purpose of document/v1 REST API?", "purpose of document/v1 REST API?", "of document/v1 REST API?", "document/v1 REST API?", "REST API?", "API?"], "tokens": ["What", "is", "the", "purpose", "of", "document", "/", "v1", "REST", "API", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1197193148", "fields": {"term": "What kind of operations does document/v1 REST API support?", "namespace": "open-p", "hash": 1197193148, "terms": ["What kind of operations does document/v1 REST API support?", "kind of operations does document/v1 REST API support?", "of operations does document/v1 REST API support?", "operations does document/v1 REST API support?", "does document/v1 REST API support?", "document/v1 REST API support?", "REST API support?", "API support?", "support?"], "tokens": ["What", "kind", "of", "operations", "does", "document", "/", "v1", "REST", "API", "support", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1899394015", "fields": {"term": "What is the recommended feeding API?", "namespace": "open-p", "hash": -1899394015, "terms": ["What is the recommended feeding API?", "is the recommended feeding API?", "the recommended feeding API?", "recommended feeding API?", "feeding API?", "API?"], "tokens": ["What", "is", "the", "recommended", "feeding", "API", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1932788324", "fields": {"term": "What is the purpose of Reads and writes API?", "namespace": "open-p", "hash": 1932788324, "terms": ["What is the purpose of Reads and writes API?", "is the purpose of Reads and writes API?", "the purpose of Reads and writes API?", "purpose of Reads and writes API?", "of Reads and writes API?", "Reads and writes API?", "and writes API?", "writes API?", "API?"], "tokens": ["What", "is", "the", "purpose", "of", "Reads", "and", "writes", "API", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1000417125", "fields": {"term": "What is Query API?", "namespace": "open-p", "hash": -1000417125, "terms": ["What is Query API?", "is Query API?", "Query API?", "API?"], "tokens": ["What", "is", "Query", "API", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::-471819800", "fields": {"term": "What is Grouping API?", "namespace": "open-p", "hash": -471819800, "terms": ["What is Grouping API?", "is Grouping API?", "Grouping API?", "API?"], "tokens": ["What", "is", "Grouping", "API", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::143846668", "fields": {"term": "Where can I find Grouping API reference?", "namespace": "open-p", "hash": 143846668, "terms": ["Where can I find Grouping API reference?", "can I find Grouping API reference?", "I find Grouping API reference?", "find Grouping API reference?", "Grouping API reference?", "API reference?", "reference?"], "tokens": ["Where", "can", "I", "find", "Grouping", "API", "reference", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1767034071", "fields": {"term": "What is Vespa Processing?", "namespace": "open-p", "hash": 1767034071, "terms": ["What is Vespa Processing?", "is Vespa Processing?", "Vespa Processing?", "Processing?"], "tokens": ["What", "is", "Vespa", "Processing", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::554828124", "fields": {"term": "What is Feed processing?", "namespace": "open-p", "hash": 554828124, "terms": ["What is Feed processing?", "is Feed processing?", "Feed processing?", "processing?"], "tokens": ["What", "is", "Feed", "processing", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::784039459", "fields": {"term": "What is the Federation API?", "namespace": "open-p", "hash": 784039459, "terms": ["What is the Federation API?", "is the Federation API?", "the Federation API?", "Federation API?", "API?"], "tokens": ["What", "is", "the", "Federation", "API", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1794446289", "fields": {"term": "What is the Web Service API?", "namespace": "open-p", "hash": -1794446289, "terms": ["What is the Web Service API?", "is the Web Service API?", "the Web Service API?", "Web Service API?", "Service API?", "API?"], "tokens": ["What", "is", "the", "Web", "Service", "API", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-52759224", "fields": {"term": "How do I develop web services?", "namespace": "open-p", "hash": -52759224, "terms": ["How do I develop web services?", "do I develop web services?", "I develop web services?", "develop web services?", "web services?", "services?"], "tokens": ["How", "do", "I", "develop", "web", "services", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::168368061", "fields": {"term": "What are the APIs available for Vespa development?", "namespace": "open-p", "hash": 168368061, "terms": ["What are the APIs available for Vespa development?", "are the APIs available for Vespa development?", "the APIs available for Vespa development?", "APIs available for Vespa development?", "available for Vespa development?", "for Vespa development?", "Vespa development?", "development?"], "tokens": ["What", "are", "the", "APIs", "available", "for", "Vespa", "development", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1542772080", "fields": {"term": "What is the Custom Renderer API used for?", "namespace": "open-p", "hash": -1542772080, "terms": ["What is the Custom Renderer API used for?", "is the Custom Renderer API used for?", "the Custom Renderer API used for?", "Custom Renderer API used for?", "Renderer API used for?", "API used for?", "used for?", "for?"], "tokens": ["What", "is", "the", "Custom", "Renderer", "API", "used", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::144937013", "fields": {"term": "How do I use the Custom Renderer API?", "namespace": "open-p", "hash": 144937013, "terms": ["How do I use the Custom Renderer API?", "do I use the Custom Renderer API?", "I use the Custom Renderer API?", "use the Custom Renderer API?", "the Custom Renderer API?", "Custom Renderer API?", "Renderer API?", "API?"], "tokens": ["How", "do", "I", "use", "the", "Custom", "Renderer", "API", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1724209614", "fields": {"term": "What is the syntax for the Custom Renderer API?", "namespace": "open-p", "hash": 1724209614, "terms": ["What is the syntax for the Custom Renderer API?", "is the syntax for the Custom Renderer API?", "the syntax for the Custom Renderer API?", "syntax for the Custom Renderer API?", "for the Custom Renderer API?", "the Custom Renderer API?", "Custom Renderer API?", "Renderer API?", "API?"], "tokens": ["What", "is", "the", "syntax", "for", "the", "Custom", "Renderer", "API", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2108216632", "fields": {"term": "What are the benefits of using the Custom Renderer API?", "namespace": "open-p", "hash": -2108216632, "terms": ["What are the benefits of using the Custom Renderer API?", "are the benefits of using the Custom Renderer API?", "the benefits of using the Custom Renderer API?", "benefits of using the Custom Renderer API?", "of using the Custom Renderer API?", "using the Custom Renderer API?", "the Custom Renderer API?", "Custom Renderer API?", "Renderer API?", "API?"], "tokens": ["What", "are", "the", "benefits", "of", "using", "the", "Custom", "Renderer", "API", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::649351054", "fields": {"term": "Are there any limitations to the Custom Renderer API?", "namespace": "open-p", "hash": 649351054, "terms": ["Are there any limitations to the Custom Renderer API?", "there any limitations to the Custom Renderer API?", "any limitations to the Custom Renderer API?", "limitations to the Custom Renderer API?", "to the Custom Renderer API?", "the Custom Renderer API?", "Custom Renderer API?", "Renderer API?", "API?"], "tokens": ["Are", "there", "any", "limitations", "to", "the", "Custom", "Renderer", "API", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-375077780", "fields": {"term": "Which Vespa features can I customize with the Custom Renderer API?", "namespace": "open-p", "hash": -375077780, "terms": ["Which Vespa features can I customize with the Custom Renderer API?", "Vespa features can I customize with the Custom Renderer API?", "features can I customize with the Custom Renderer API?", "can I customize with the Custom Renderer API?", "I customize with the Custom Renderer API?", "customize with the Custom Renderer API?", "with the Custom Renderer API?", "the Custom Renderer API?", "Custom Renderer API?", "Renderer API?", "API?"], "tokens": ["Which", "Vespa", "features", "can", "I", "customize", "with", "the", "Custom", "Renderer", "API", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::130525711", "fields": {"term": "Can I use the Custom Renderer API in conjunction with existing Vespa features?", "namespace": "open-p", "hash": 130525711, "terms": ["Can I use the Custom Renderer API in conjunction with existing Vespa features?", "I use the Custom Renderer API in conjunction with existing Vespa features?", "use the Custom Renderer API in conjunction with existing Vespa features?", "the Custom Renderer API in conjunction with existing Vespa features?", "Custom Renderer API in conjunction with existing Vespa features?", "Renderer API in conjunction with existing Vespa features?", "API in conjunction with existing Vespa features?", "in conjunction with existing Vespa features?", "conjunction with existing Vespa features?", "with existing Vespa features?", "existing Vespa features?", "Vespa features?", "features?"], "tokens": ["Can", "I", "use", "the", "Custom", "Renderer", "API", "in", "conjunction", "with", "existing", "Vespa", "features", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2041777300", "fields": {"term": "How can I integrate the Custom Renderer API into my Vespa application?", "namespace": "open-p", "hash": -2041777300, "terms": ["How can I integrate the Custom Renderer API into my Vespa application?", "can I integrate the Custom Renderer API into my Vespa application?", "I integrate the Custom Renderer API into my Vespa application?", "integrate the Custom Renderer API into my Vespa application?", "the Custom Renderer API into my Vespa application?", "Custom Renderer API into my Vespa application?", "Renderer API into my Vespa application?", "API into my Vespa application?", "into my Vespa application?", "my Vespa application?", "Vespa application?", "application?"], "tokens": ["How", "can", "I", "integrate", "the", "Custom", "Renderer", "API", "into", "my", "Vespa", "application", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1661968924", "fields": {"term": "What are Health and Metric APIs?", "namespace": "open-p", "hash": 1661968924, "terms": ["What are Health and Metric APIs?", "are Health and Metric APIs?", "Health and Metric APIs?", "and Metric APIs?", "Metric APIs?", "APIs?"], "tokens": ["What", "are", "Health", "and", "Metric", "APIs", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-466736589", "fields": {"term": "What does the /cluster/v2 API do?", "namespace": "open-p", "hash": -466736589, "terms": ["What does the /cluster/v2 API do?", "does the /cluster/v2 API do?", "the /cluster/v2 API do?", "/cluster/v2 API do?", "API do?", "do?"], "tokens": ["What", "does", "the", "/", "cluster", "/", "v2", "API", "do", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::297083406", "fields": {"term": "What is an application package?", "namespace": "open-p", "hash": 297083406, "terms": ["What is an application package?", "is an application package?", "an application package?", "application package?", "package?"], "tokens": ["What", "is", "an", "application", "package", "?"], "corpus_count": 3, "document_count": 3}}, -{"put": "id:term:term::-1806637181", "fields": {"term": "What is in an application package?", "namespace": "open-p", "hash": -1806637181, "terms": ["What is in an application package?", "is in an application package?", "in an application package?", "an application package?", "application package?", "package?"], "tokens": ["What", "is", "in", "an", "application", "package", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1767413863", "fields": {"term": "How do you deploy an application package?", "namespace": "open-p", "hash": -1767413863, "terms": ["How do you deploy an application package?", "do you deploy an application package?", "you deploy an application package?", "deploy an application package?", "an application package?", "application package?", "package?"], "tokens": ["How", "do", "you", "deploy", "an", "application", "package", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1471138616", "fields": {"term": "What is the 'prepare' command for?", "namespace": "open-p", "hash": 1471138616, "terms": ["What is the 'prepare' command for?", "is the 'prepare' command for?", "the 'prepare' command for?", "'prepare' command for?", "command for?", "for?"], "tokens": ["What", "is", "the", "'", "prepare", "'", "command", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::686003376", "fields": {"term": "What does 'prepare' validate?", "namespace": "open-p", "hash": 686003376, "terms": ["What does 'prepare' validate?", "does 'prepare' validate?", "'prepare' validate?", "validate?"], "tokens": ["What", "does", "'", "prepare", "'", "validate", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::74702115", "fields": {"term": "What is the 'activate' command for?", "namespace": "open-p", "hash": 74702115, "terms": ["What is the 'activate' command for?", "is the 'activate' command for?", "the 'activate' command for?", "'activate' command for?", "command for?", "for?"], "tokens": ["What", "is", "the", "'", "activate", "'", "command", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1894658862", "fields": {"term": "How can the active application package be downloaded?", "namespace": "open-p", "hash": 1894658862, "terms": ["How can the active application package be downloaded?", "can the active application package be downloaded?", "the active application package be downloaded?", "active application package be downloaded?", "application package be downloaded?", "package be downloaded?", "be downloaded?", "downloaded?"], "tokens": ["How", "can", "the", "active", "application", "package", "be", "downloaded", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1584090237", "fields": {"term": "What are preprocess directives?", "namespace": "open-p", "hash": -1584090237, "terms": ["What are preprocess directives?", "are preprocess directives?", "preprocess directives?", "directives?"], "tokens": ["What", "are", "preprocess", "directives", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-761859301", "fields": {"term": "What does preprocess:properties do?", "namespace": "open-p", "hash": -761859301, "terms": ["What does preprocess:properties do?", "does preprocess:properties do?", "preprocess:properties do?", "do?"], "tokens": ["What", "does", "preprocess", ":", "properties", "do", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1108201766", "fields": {"term": "What does preprocess:include do?", "namespace": "open-p", "hash": 1108201766, "terms": ["What does preprocess:include do?", "does preprocess:include do?", "preprocess:include do?", "do?"], "tokens": ["What", "does", "preprocess", ":", "include", "do", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1020609571", "fields": {"term": "How is ${container.port} replaced?", "namespace": "open-p", "hash": -1020609571, "terms": ["How is ${container.port} replaced?", "is ${container.port} replaced?", "${container.port} replaced?", "replaced?"], "tokens": ["How", "is", "${", "container", "port", "}", "replaced", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1409624891", "fields": {"term": "Can preprocess directives be used in included files?", "namespace": "open-p", "hash": -1409624891, "terms": ["Can preprocess directives be used in included files?", "preprocess directives be used in included files?", "directives be used in included files?", "be used in included files?", "used in included files?", "in included files?", "included files?", "files?"], "tokens": ["Can", "preprocess", "directives", "be", "used", "in", "included", "files", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1996194156", "fields": {"term": "What is the sample content.xml?", "namespace": "open-p", "hash": 1996194156, "terms": ["What is the sample content.xml?", "is the sample content.xml?", "the sample content.xml?", "sample content.xml?", "content.xml?"], "tokens": ["What", "is", "the", "sample", "content", "xml", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::280533704", "fields": {"term": "What does an application package contain?", "namespace": "open-p", "hash": 280533704, "terms": ["What does an application package contain?", "does an application package contain?", "an application package contain?", "application package contain?", "package contain?", "contain?"], "tokens": ["What", "does", "an", "application", "package", "contain", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-257385374", "fields": {"term": "How can an application package be built?", "namespace": "open-p", "hash": -257385374, "terms": ["How can an application package be built?", "can an application package be built?", "an application package be built?", "application package be built?", "package be built?", "be built?", "built?"], "tokens": ["How", "can", "an", "application", "package", "be", "built", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1985111758", "fields": {"term": "Where are most application packages stored?", "namespace": "open-p", "hash": 1985111758, "terms": ["Where are most application packages stored?", "are most application packages stored?", "most application packages stored?", "application packages stored?", "packages stored?", "stored?"], "tokens": ["Where", "are", "most", "application", "packages", "stored", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1727234017", "fields": {"term": "How is code deployed?", "namespace": "open-p", "hash": -1727234017, "terms": ["How is code deployed?", "is code deployed?", "code deployed?", "deployed?"], "tokens": ["How", "is", "code", "deployed", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::364815492", "fields": {"term": "What are config definition files?", "namespace": "open-p", "hash": 364815492, "terms": ["What are config definition files?", "are config definition files?", "config definition files?", "definition files?", "files?"], "tokens": ["What", "are", "config", "definition", "files", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::183070802", "fields": {"term": "What happens on discrepancies?", "namespace": "open-p", "hash": 183070802, "terms": ["What happens on discrepancies?", "happens on discrepancies?", "on discrepancies?", "discrepancies?"], "tokens": ["What", "happens", "on", "discrepancies", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2076706421", "fields": {"term": "What is vespa-deploy?", "namespace": "open-p", "hash": -2076706421, "terms": ["What is vespa-deploy?", "is vespa-deploy?", "vespa-deploy?"], "tokens": ["What", "is", "vespa", "deploy", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::56588203", "fields": {"term": "What is validation-overrides.xml?", "namespace": "open-p", "hash": 56588203, "terms": ["What is validation-overrides.xml?", "is validation-overrides.xml?", "validation-overrides.xml?"], "tokens": ["What", "is", "validation", "overrides", "xml", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-46612279", "fields": {"term": "How do you deploy changes in Vespa?", "namespace": "open-p", "hash": -46612279, "terms": ["How do you deploy changes in Vespa?", "do you deploy changes in Vespa?", "you deploy changes in Vespa?", "deploy changes in Vespa?", "changes in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["How", "do", "you", "deploy", "changes", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1942742309", "fields": {"term": "What is validate and distribute to nodes?", "namespace": "open-p", "hash": 1942742309, "terms": ["What is validate and distribute to nodes?", "is validate and distribute to nodes?", "validate and distribute to nodes?", "and distribute to nodes?", "distribute to nodes?", "to nodes?", "nodes?"], "tokens": ["What", "is", "validate", "and", "distribute", "to", "nodes", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1495643397", "fields": {"term": "What is activation for?", "namespace": "open-p", "hash": -1495643397, "terms": ["What is activation for?", "is activation for?", "activation for?", "for?"], "tokens": ["What", "is", "activation", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::888177263", "fields": {"term": "What files are distributed during vespa-deploy prepare?", "namespace": "open-p", "hash": 888177263, "terms": ["What files are distributed during vespa-deploy prepare?", "files are distributed during vespa-deploy prepare?", "are distributed during vespa-deploy prepare?", "distributed during vespa-deploy prepare?", "during vespa-deploy prepare?", "vespa-deploy prepare?", "prepare?"], "tokens": ["What", "files", "are", "distributed", "during", "vespa", "deploy", "prepare", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1098466368", "fields": {"term": "How to check for files in components?", "namespace": "open-p", "hash": -1098466368, "terms": ["How to check for files in components?", "to check for files in components?", "check for files in components?", "for files in components?", "files in components?", "in components?", "components?"], "tokens": ["How", "to", "check", "for", "files", "in", "components", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::718022533", "fields": {"term": "What is vespa-status-filedistribution command?", "namespace": "open-p", "hash": 718022533, "terms": ["What is vespa-status-filedistribution command?", "is vespa-status-filedistribution command?", "vespa-status-filedistribution command?", "command?"], "tokens": ["What", "is", "vespa", "status", "filedistribution", "command", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-916000172", "fields": {"term": "What machine learned models are available?", "namespace": "open-p", "hash": -916000172, "terms": ["What machine learned models are available?", "machine learned models are available?", "learned models are available?", "models are available?", "are available?", "available?"], "tokens": ["What", "machine", "learned", "models", "are", "available", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::613622972", "fields": {"term": "Where are the machine learned models stored?", "namespace": "open-p", "hash": 613622972, "terms": ["Where are the machine learned models stored?", "are the machine learned models stored?", "the machine learned models stored?", "machine learned models stored?", "learned models stored?", "models stored?", "stored?"], "tokens": ["Where", "are", "the", "machine", "learned", "models", "stored", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2021817706", "fields": {"term": "What if my models are too large?", "namespace": "open-p", "hash": 2021817706, "terms": ["What if my models are too large?", "if my models are too large?", "my models are too large?", "models are too large?", "are too large?", "too large?", "large?"], "tokens": ["What", "if", "my", "models", "are", "too", "large", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-963774449", "fields": {"term": "Which directory are the models stored in?", "namespace": "open-p", "hash": -963774449, "terms": ["Which directory are the models stored in?", "directory are the models stored in?", "are the models stored in?", "the models stored in?", "models stored in?", "stored in?", "in?"], "tokens": ["Which", "directory", "are", "the", "models", "stored", "in", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::900080979", "fields": {"term": "How do I download models during package build?", "namespace": "open-p", "hash": 900080979, "terms": ["How do I download models during package build?", "do I download models during package build?", "I download models during package build?", "download models during package build?", "models during package build?", "during package build?", "package build?", "build?"], "tokens": ["How", "do", "I", "download", "models", "during", "package", "build", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1967932437", "fields": {"term": "Does the script unpack archives?", "namespace": "open-p", "hash": 1967932437, "terms": ["Does the script unpack archives?", "the script unpack archives?", "script unpack archives?", "unpack archives?", "archives?"], "tokens": ["Does", "the", "script", "unpack", "archives", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1763476726", "fields": {"term": "Does the script require authentication?", "namespace": "open-p", "hash": -1763476726, "terms": ["Does the script require authentication?", "the script require authentication?", "script require authentication?", "require authentication?", "authentication?"], "tokens": ["Does", "the", "script", "require", "authentication", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-905408237", "fields": {"term": "What is the purpose of pushd?", "namespace": "open-p", "hash": -905408237, "terms": ["What is the purpose of pushd?", "is the purpose of pushd?", "the purpose of pushd?", "purpose of pushd?", "of pushd?", "pushd?"], "tokens": ["What", "is", "the", "purpose", "of", "pushd", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1418987262", "fields": {"term": "What is the purpose of popd?", "namespace": "open-p", "hash": -1418987262, "terms": ["What is the purpose of popd?", "is the purpose of popd?", "the purpose of popd?", "purpose of popd?", "of popd?", "popd?"], "tokens": ["What", "is", "the", "purpose", "of", "popd", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1614103658", "fields": {"term": "What should be added to the script?", "namespace": "open-p", "hash": -1614103658, "terms": ["What should be added to the script?", "should be added to the script?", "be added to the script?", "added to the script?", "to the script?", "the script?", "script?"], "tokens": ["What", "should", "be", "added", "to", "the", "script", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1396113238", "fields": {"term": "What is services.xml?", "namespace": "open-p", "hash": -1396113238, "terms": ["What is services.xml?", "is services.xml?", "services.xml?"], "tokens": ["What", "is", "services", "xml", "?"], "corpus_count": 3, "document_count": 3}}, -{"put": "id:term:term::330540209", "fields": {"term": "What does services.xml specify?", "namespace": "open-p", "hash": 330540209, "terms": ["What does services.xml specify?", "does services.xml specify?", "services.xml specify?", "specify?"], "tokens": ["What", "does", "services", "xml", "specify", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-383367258", "fields": {"term": "What can be specified in services.xml?", "namespace": "open-p", "hash": -383367258, "terms": ["What can be specified in services.xml?", "can be specified in services.xml?", "be specified in services.xml?", "specified in services.xml?", "in services.xml?", "services.xml?"], "tokens": ["What", "can", "be", "specified", "in", "services", "xml", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::454022266", "fields": {"term": "What is a top-level element?", "namespace": "open-p", "hash": 454022266, "terms": ["What is a top-level element?", "is a top-level element?", "a top-level element?", "top-level element?", "element?"], "tokens": ["What", "is", "a", "top", "level", "element", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1550785024", "fields": {"term": "What is the services.xml reference?", "namespace": "open-p", "hash": 1550785024, "terms": ["What is the services.xml reference?", "is the services.xml reference?", "the services.xml reference?", "services.xml reference?", "reference?"], "tokens": ["What", "is", "the", "services", "xml", "reference", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-754025638", "fields": {"term": "Where can I find information on services.xml?", "namespace": "open-p", "hash": -754025638, "terms": ["Where can I find information on services.xml?", "can I find information on services.xml?", "I find information on services.xml?", "find information on services.xml?", "information on services.xml?", "on services.xml?", "services.xml?"], "tokens": ["Where", "can", "I", "find", "information", "on", "services", "xml", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1394093299", "fields": {"term": "What is configuredHandler in services.xml?", "namespace": "open-p", "hash": 1394093299, "terms": ["What is configuredHandler in services.xml?", "is configuredHandler in services.xml?", "configuredHandler in services.xml?", "in services.xml?", "services.xml?"], "tokens": ["What", "is", "configuredHandler", "in", "services", "xml", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1492715256", "fields": {"term": "What is vespatest.port in services.xml?", "namespace": "open-p", "hash": -1492715256, "terms": ["What is vespatest.port in services.xml?", "is vespatest.port in services.xml?", "vespatest.port in services.xml?", "in services.xml?", "services.xml?"], "tokens": ["What", "is", "vespatest", "port", "in", "services", "xml", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1179281056", "fields": {"term": "What is configuring components?", "namespace": "open-p", "hash": -1179281056, "terms": ["What is configuring components?", "is configuring components?", "configuring components?", "components?"], "tokens": ["What", "is", "configuring", "components", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::975451479", "fields": {"term": "What is the /application/v2/tenant API?", "namespace": "open-p", "hash": 975451479, "terms": ["What is the /application/v2/tenant API?", "is the /application/v2/tenant API?", "the /application/v2/tenant API?", "/application/v2/tenant API?", "API?"], "tokens": ["What", "is", "the", "/", "application", "/", "v2", "/", "tenant", "API", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-800390539", "fields": {"term": "What is the current API version?", "namespace": "open-p", "hash": -800390539, "terms": ["What is the current API version?", "is the current API version?", "the current API version?", "current API version?", "API version?", "version?"], "tokens": ["What", "is", "the", "current", "API", "version", "?"], "corpus_count": 3, "document_count": 3}}, -{"put": "id:term:term::1960878066", "fields": {"term": "How do I list tenants?", "namespace": "open-p", "hash": 1960878066, "terms": ["How do I list tenants?", "do I list tenants?", "I list tenants?", "list tenants?", "tenants?"], "tokens": ["How", "do", "I", "list", "tenants", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-325373026", "fields": {"term": "How do I create a new tenant?", "namespace": "open-p", "hash": -325373026, "terms": ["How do I create a new tenant?", "do I create a new tenant?", "I create a new tenant?", "create a new tenant?", "a new tenant?", "new tenant?", "tenant?"], "tokens": ["How", "do", "I", "create", "a", "new", "tenant", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-630113460", "fields": {"term": "How do I delete a tenant?", "namespace": "open-p", "hash": -630113460, "terms": ["How do I delete a tenant?", "do I delete a tenant?", "I delete a tenant?", "delete a tenant?", "a tenant?", "tenant?"], "tokens": ["How", "do", "I", "delete", "a", "tenant", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2105156699", "fields": {"term": "What is the response format for listing tenants?", "namespace": "open-p", "hash": -2105156699, "terms": ["What is the response format for listing tenants?", "is the response format for listing tenants?", "the response format for listing tenants?", "response format for listing tenants?", "format for listing tenants?", "for listing tenants?", "listing tenants?", "tenants?"], "tokens": ["What", "is", "the", "response", "format", "for", "listing", "tenants", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1357184660", "fields": {"term": "What is the response format for getting a tenant?", "namespace": "open-p", "hash": -1357184660, "terms": ["What is the response format for getting a tenant?", "is the response format for getting a tenant?", "the response format for getting a tenant?", "response format for getting a tenant?", "format for getting a tenant?", "for getting a tenant?", "getting a tenant?", "a tenant?", "tenant?"], "tokens": ["What", "is", "the", "response", "format", "for", "getting", "a", "tenant", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1985139081", "fields": {"term": "What is the response format for creating a tenant?", "namespace": "open-p", "hash": -1985139081, "terms": ["What is the response format for creating a tenant?", "is the response format for creating a tenant?", "the response format for creating a tenant?", "response format for creating a tenant?", "format for creating a tenant?", "for creating a tenant?", "creating a tenant?", "a tenant?", "tenant?"], "tokens": ["What", "is", "the", "response", "format", "for", "creating", "a", "tenant", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::546349105", "fields": {"term": "What is the response format for deleting a tenant?", "namespace": "open-p", "hash": 546349105, "terms": ["What is the response format for deleting a tenant?", "is the response format for deleting a tenant?", "the response format for deleting a tenant?", "response format for deleting a tenant?", "format for deleting a tenant?", "for deleting a tenant?", "deleting a tenant?", "a tenant?", "tenant?"], "tokens": ["What", "is", "the", "response", "format", "for", "deleting", "a", "tenant", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::824765653", "fields": {"term": "What is the list of status codes?", "namespace": "open-p", "hash": 824765653, "terms": ["What is the list of status codes?", "is the list of status codes?", "the list of status codes?", "list of status codes?", "of status codes?", "status codes?", "codes?"], "tokens": ["What", "is", "the", "list", "of", "status", "codes", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2024423807", "fields": {"term": "What is status code 400?", "namespace": "open-p", "hash": 2024423807, "terms": ["What is status code 400?", "is status code 400?", "status code 400?", "code 400?", "400?"], "tokens": ["What", "is", "status", "code", "400", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1263025003", "fields": {"term": "What is status code 404?", "namespace": "open-p", "hash": 1263025003, "terms": ["What is status code 404?", "is status code 404?", "status code 404?", "code 404?", "404?"], "tokens": ["What", "is", "status", "code", "404", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::892319118", "fields": {"term": "What is status code 405?", "namespace": "open-p", "hash": 892319118, "terms": ["What is status code 405?", "is status code 405?", "status code 405?", "code 405?", "405?"], "tokens": ["What", "is", "status", "code", "405", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::835787708", "fields": {"term": "What is status code 500?", "namespace": "open-p", "hash": 835787708, "terms": ["What is status code 500?", "is status code 500?", "status code 500?", "code 500?", "500?"], "tokens": ["What", "is", "status", "code", "500", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1126023148", "fields": {"term": "In what format are responses?", "namespace": "open-p", "hash": -1126023148, "terms": ["In what format are responses?", "what format are responses?", "format are responses?", "are responses?", "responses?"], "tokens": ["In", "what", "format", "are", "responses", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1092780020", "fields": {"term": "What is the message field?", "namespace": "open-p", "hash": -1092780020, "terms": ["What is the message field?", "is the message field?", "the message field?", "message field?", "field?"], "tokens": ["What", "is", "the", "message", "field", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-247121901", "fields": {"term": "What is the HNSW algorithm?", "namespace": "open-p", "hash": -247121901, "terms": ["What is the HNSW algorithm?", "is the HNSW algorithm?", "the HNSW algorithm?", "HNSW algorithm?", "algorithm?"], "tokens": ["What", "is", "the", "HNSW", "algorithm", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::-1304843933", "fields": {"term": "What is multi-vector indexing?", "namespace": "open-p", "hash": -1304843933, "terms": ["What is multi-vector indexing?", "is multi-vector indexing?", "multi-vector indexing?", "indexing?"], "tokens": ["What", "is", "multi", "vector", "indexing", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1709815823", "fields": {"term": "What is real-time indexing?", "namespace": "open-p", "hash": -1709815823, "terms": ["What is real-time indexing?", "is real-time indexing?", "real-time indexing?", "indexing?"], "tokens": ["What", "is", "real", "time", "indexing", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1986674800", "fields": {"term": "What is the mutable HNSW graph?", "namespace": "open-p", "hash": -1986674800, "terms": ["What is the mutable HNSW graph?", "is the mutable HNSW graph?", "the mutable HNSW graph?", "mutable HNSW graph?", "HNSW graph?", "graph?"], "tokens": ["What", "is", "the", "mutable", "HNSW", "graph", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-87672985", "fields": {"term": "What is multithreaded indexing?", "namespace": "open-p", "hash": -87672985, "terms": ["What is multithreaded indexing?", "is multithreaded indexing?", "multithreaded indexing?", "indexing?"], "tokens": ["What", "is", "multithreaded", "indexing", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-464484258", "fields": {"term": "What is nearest neighbor search?", "namespace": "open-p", "hash": -464484258, "terms": ["What is nearest neighbor search?", "is nearest neighbor search?", "nearest neighbor search?", "neighbor search?", "search?"], "tokens": ["What", "is", "nearest", "neighbor", "search", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::1469641369", "fields": {"term": "What is HNSW index?", "namespace": "open-p", "hash": 1469641369, "terms": ["What is HNSW index?", "is HNSW index?", "HNSW index?", "index?"], "tokens": ["What", "is", "HNSW", "index", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-949812102", "fields": {"term": "What affects recall accuracy?", "namespace": "open-p", "hash": -949812102, "terms": ["What affects recall accuracy?", "affects recall accuracy?", "recall accuracy?", "accuracy?"], "tokens": ["What", "affects", "recall", "accuracy", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::195004737", "fields": {"term": "What is the value of max-links-per-node for text_embedding?", "namespace": "open-p", "hash": 195004737, "terms": ["What is the value of max-links-per-node for text_embedding?", "is the value of max-links-per-node for text_embedding?", "the value of max-links-per-node for text_embedding?", "value of max-links-per-node for text_embedding?", "of max-links-per-node for text_embedding?", "max-links-per-node for text_embedding?", "for text_embedding?", "text_embedding?"], "tokens": ["What", "is", "the", "value", "of", "max", "links", "per", "node", "for", "text_embedding", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-462538961", "fields": {"term": "What is the value of neighbors-to-explore-at-insert for image_embeddings?", "namespace": "open-p", "hash": -462538961, "terms": ["What is the value of neighbors-to-explore-at-insert for image_embeddings?", "is the value of neighbors-to-explore-at-insert for image_embeddings?", "the value of neighbors-to-explore-at-insert for image_embeddings?", "value of neighbors-to-explore-at-insert for image_embeddings?", "of neighbors-to-explore-at-insert for image_embeddings?", "neighbors-to-explore-at-insert for image_embeddings?", "for image_embeddings?", "image_embeddings?"], "tokens": ["What", "is", "the", "value", "of", "neighbors", "to", "explore", "at", "insert", "for", "image_embeddings", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-724864971", "fields": {"term": "Where to find a detailed description of the tradeoffs in billion-scale vector search?", "namespace": "open-p", "hash": -724864971, "terms": ["Where to find a detailed description of the tradeoffs in billion-scale vector search?", "to find a detailed description of the tradeoffs in billion-scale vector search?", "find a detailed description of the tradeoffs in billion-scale vector search?", "a detailed description of the tradeoffs in billion-scale vector search?", "detailed description of the tradeoffs in billion-scale vector search?", "description of the tradeoffs in billion-scale vector search?", "of the tradeoffs in billion-scale vector search?", "the tradeoffs in billion-scale vector search?", "tradeoffs in billion-scale vector search?", "in billion-scale vector search?", "billion-scale vector search?", "vector search?", "search?"], "tokens": ["Where", "to", "find", "a", "detailed", "description", "of", "the", "tradeoffs", "in", "billion", "scale", "vector", "search", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::144122036", "fields": {"term": "What is HNSW?", "namespace": "open-p", "hash": 144122036, "terms": ["What is HNSW?", "is HNSW?", "HNSW?"], "tokens": ["What", "is", "HNSW", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::278706594", "fields": {"term": "What is max-links-per-node?", "namespace": "open-p", "hash": 278706594, "terms": ["What is max-links-per-node?", "is max-links-per-node?", "max-links-per-node?"], "tokens": ["What", "is", "max", "links", "per", "node", "?"], "corpus_count": 3, "document_count": 3}}, -{"put": "id:term:term::112486286", "fields": {"term": "What is neighbors-to-explore-at-insert?", "namespace": "open-p", "hash": 112486286, "terms": ["What is neighbors-to-explore-at-insert?", "is neighbors-to-explore-at-insert?", "neighbors-to-explore-at-insert?"], "tokens": ["What", "is", "neighbors", "to", "explore", "at", "insert", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1191390279", "fields": {"term": "How does max-links-per-node affect memory?", "namespace": "open-p", "hash": -1191390279, "terms": ["How does max-links-per-node affect memory?", "does max-links-per-node affect memory?", "max-links-per-node affect memory?", "affect memory?", "memory?"], "tokens": ["How", "does", "max", "links", "per", "node", "affect", "memory", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-483644665", "fields": {"term": "What does max-links-per-node do?", "namespace": "open-p", "hash": -483644665, "terms": ["What does max-links-per-node do?", "does max-links-per-node do?", "max-links-per-node do?", "do?"], "tokens": ["What", "does", "max", "links", "per", "node", "do", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1574614171", "fields": {"term": "What is recall accuracy?", "namespace": "open-p", "hash": 1574614171, "terms": ["What is recall accuracy?", "is recall accuracy?", "recall accuracy?", "accuracy?"], "tokens": ["What", "is", "recall", "accuracy", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::588949409", "fields": {"term": "How does neighbors-to-explore-at-insert affect accuracy?", "namespace": "open-p", "hash": 588949409, "terms": ["How does neighbors-to-explore-at-insert affect accuracy?", "does neighbors-to-explore-at-insert affect accuracy?", "neighbors-to-explore-at-insert affect accuracy?", "affect accuracy?", "accuracy?"], "tokens": ["How", "does", "neighbors", "to", "explore", "at", "insert", "affect", "accuracy", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1459239788", "fields": {"term": "What is the impact of higher max-links-per-node on indexing performance?", "namespace": "open-p", "hash": 1459239788, "terms": ["What is the impact of higher max-links-per-node on indexing performance?", "is the impact of higher max-links-per-node on indexing performance?", "the impact of higher max-links-per-node on indexing performance?", "impact of higher max-links-per-node on indexing performance?", "of higher max-links-per-node on indexing performance?", "higher max-links-per-node on indexing performance?", "max-links-per-node on indexing performance?", "on indexing performance?", "indexing performance?", "performance?"], "tokens": ["What", "is", "the", "impact", "of", "higher", "max", "links", "per", "node", "on", "indexing", "performance", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1382765923", "fields": {"term": "What is the impact of higher neighbors-to-explore-at-insert on memory usage?", "namespace": "open-p", "hash": 1382765923, "terms": ["What is the impact of higher neighbors-to-explore-at-insert on memory usage?", "is the impact of higher neighbors-to-explore-at-insert on memory usage?", "the impact of higher neighbors-to-explore-at-insert on memory usage?", "impact of higher neighbors-to-explore-at-insert on memory usage?", "of higher neighbors-to-explore-at-insert on memory usage?", "higher neighbors-to-explore-at-insert on memory usage?", "neighbors-to-explore-at-insert on memory usage?", "on memory usage?", "memory usage?", "usage?"], "tokens": ["What", "is", "the", "impact", "of", "higher", "neighbors", "to", "explore", "at", "insert", "on", "memory", "usage", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::632819879", "fields": {"term": "What is an HNSW index?", "namespace": "open-p", "hash": 632819879, "terms": ["What is an HNSW index?", "is an HNSW index?", "an HNSW index?", "HNSW index?", "index?"], "tokens": ["What", "is", "an", "HNSW", "index", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::-846231488", "fields": {"term": "What is the approximate query annotation?", "namespace": "open-p", "hash": -846231488, "terms": ["What is the approximate query annotation?", "is the approximate query annotation?", "the approximate query annotation?", "approximate query annotation?", "query annotation?", "annotation?"], "tokens": ["What", "is", "the", "approximate", "query", "annotation", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1836893989", "fields": {"term": "What is the hnsw.exploreAdditionalHits parameter?", "namespace": "open-p", "hash": 1836893989, "terms": ["What is the hnsw.exploreAdditionalHits parameter?", "is the hnsw.exploreAdditionalHits parameter?", "the hnsw.exploreAdditionalHits parameter?", "hnsw.exploreAdditionalHits parameter?", "parameter?"], "tokens": ["What", "is", "the", "hnsw", "exploreAdditionalHits", "parameter", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1476862299", "fields": {"term": "How does the approximate parameter affect search accuracy?", "namespace": "open-p", "hash": 1476862299, "terms": ["How does the approximate parameter affect search accuracy?", "does the approximate parameter affect search accuracy?", "the approximate parameter affect search accuracy?", "approximate parameter affect search accuracy?", "parameter affect search accuracy?", "affect search accuracy?", "search accuracy?", "accuracy?"], "tokens": ["How", "does", "the", "approximate", "parameter", "affect", "search", "accuracy", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1203458322", "fields": {"term": "How can accuracy quality be tuned with the hnsw.exploreAdditionalHits parameter?", "namespace": "open-p", "hash": 1203458322, "terms": ["How can accuracy quality be tuned with the hnsw.exploreAdditionalHits parameter?", "can accuracy quality be tuned with the hnsw.exploreAdditionalHits parameter?", "accuracy quality be tuned with the hnsw.exploreAdditionalHits parameter?", "quality be tuned with the hnsw.exploreAdditionalHits parameter?", "be tuned with the hnsw.exploreAdditionalHits parameter?", "tuned with the hnsw.exploreAdditionalHits parameter?", "with the hnsw.exploreAdditionalHits parameter?", "the hnsw.exploreAdditionalHits parameter?", "hnsw.exploreAdditionalHits parameter?", "parameter?"], "tokens": ["How", "can", "accuracy", "quality", "be", "tuned", "with", "the", "hnsw", "exploreAdditionalHits", "parameter", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-986593097", "fields": {"term": "What is the overlap@k metric?", "namespace": "open-p", "hash": -986593097, "terms": ["What is the overlap@k metric?", "is the overlap@k metric?", "the overlap@k metric?", "overlap@k metric?", "metric?"], "tokens": ["What", "is", "the", "overlap", "@", "k", "metric", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1639589836", "fields": {"term": "What is the purpose of the targetHits parameter?", "namespace": "open-p", "hash": -1639589836, "terms": ["What is the purpose of the targetHits parameter?", "is the purpose of the targetHits parameter?", "the purpose of the targetHits parameter?", "purpose of the targetHits parameter?", "of the targetHits parameter?", "the targetHits parameter?", "targetHits parameter?", "parameter?"], "tokens": ["What", "is", "the", "purpose", "of", "the", "targetHits", "parameter", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::210166278", "fields": {"term": "How can query filters be combined with nearestNeighbor?", "namespace": "open-p", "hash": 210166278, "terms": ["How can query filters be combined with nearestNeighbor?", "can query filters be combined with nearestNeighbor?", "query filters be combined with nearestNeighbor?", "filters be combined with nearestNeighbor?", "be combined with nearestNeighbor?", "combined with nearestNeighbor?", "with nearestNeighbor?", "nearestNeighbor?"], "tokens": ["How", "can", "query", "filters", "be", "combined", "with", "nearestNeighbor", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::876772070", "fields": {"term": "What is pre-filtering?", "namespace": "open-p", "hash": 876772070, "terms": ["What is pre-filtering?", "is pre-filtering?", "pre-filtering?"], "tokens": ["What", "is", "pre", "filtering", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::-1071987831", "fields": {"term": "What is post-filtering?", "namespace": "open-p", "hash": -1071987831, "terms": ["What is post-filtering?", "is post-filtering?", "post-filtering?"], "tokens": ["What", "is", "post", "filtering", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::1471165260", "fields": {"term": "How can the filtering behavior be controlled?", "namespace": "open-p", "hash": 1471165260, "terms": ["How can the filtering behavior be controlled?", "can the filtering behavior be controlled?", "the filtering behavior be controlled?", "filtering behavior be controlled?", "behavior be controlled?", "be controlled?", "controlled?"], "tokens": ["How", "can", "the", "filtering", "behavior", "be", "controlled", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1573293199", "fields": {"term": "Which query operators are not included in pre-filtering?", "namespace": "open-p", "hash": 1573293199, "terms": ["Which query operators are not included in pre-filtering?", "query operators are not included in pre-filtering?", "operators are not included in pre-filtering?", "are not included in pre-filtering?", "not included in pre-filtering?", "included in pre-filtering?", "in pre-filtering?", "pre-filtering?"], "tokens": ["Which", "query", "operators", "are", "not", "included", "in", "pre", "filtering", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1160127151", "fields": {"term": "Can pre-filtering be evaluated using multiple threads per query?", "namespace": "open-p", "hash": 1160127151, "terms": ["Can pre-filtering be evaluated using multiple threads per query?", "pre-filtering be evaluated using multiple threads per query?", "be evaluated using multiple threads per query?", "evaluated using multiple threads per query?", "using multiple threads per query?", "multiple threads per query?", "threads per query?", "per query?", "query?"], "tokens": ["Can", "pre", "filtering", "be", "evaluated", "using", "multiple", "threads", "per", "query", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-103638058", "fields": {"term": "Is multithreaded evaluation supported for post-filtering?", "namespace": "open-p", "hash": -103638058, "terms": ["Is multithreaded evaluation supported for post-filtering?", "multithreaded evaluation supported for post-filtering?", "evaluation supported for post-filtering?", "supported for post-filtering?", "for post-filtering?", "post-filtering?"], "tokens": ["Is", "multithreaded", "evaluation", "supported", "for", "post", "filtering", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1907375296", "fields": {"term": "What is targetHits?", "namespace": "open-p", "hash": -1907375296, "terms": ["What is targetHits?", "is targetHits?", "targetHits?"], "tokens": ["What", "is", "targetHits", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1262097694", "fields": {"term": "What is Pagination?", "namespace": "open-p", "hash": 1262097694, "terms": ["What is Pagination?", "is Pagination?", "Pagination?"], "tokens": ["What", "is", "Pagination", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1919571697", "fields": {"term": "Is the Total hit count accurate?", "namespace": "open-p", "hash": -1919571697, "terms": ["Is the Total hit count accurate?", "the Total hit count accurate?", "Total hit count accurate?", "hit count accurate?", "count accurate?", "accurate?"], "tokens": ["Is", "the", "Total", "hit", "count", "accurate", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1796382084", "fields": {"term": "How does nearest neighbor search work?", "namespace": "open-p", "hash": 1796382084, "terms": ["How does nearest neighbor search work?", "does nearest neighbor search work?", "nearest neighbor search work?", "neighbor search work?", "search work?", "work?"], "tokens": ["How", "does", "nearest", "neighbor", "search", "work", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1078870118", "fields": {"term": "Which cell value types are available?", "namespace": "open-p", "hash": 1078870118, "terms": ["Which cell value types are available?", "cell value types are available?", "value types are available?", "types are available?", "are available?", "available?"], "tokens": ["Which", "cell", "value", "types", "are", "available", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::798314072", "fields": {"term": "What is the HNSW graph data structure?", "namespace": "open-p", "hash": 798314072, "terms": ["What is the HNSW graph data structure?", "is the HNSW graph data structure?", "the HNSW graph data structure?", "HNSW graph data structure?", "graph data structure?", "data structure?", "structure?"], "tokens": ["What", "is", "the", "HNSW", "graph", "data", "structure", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-88945640", "fields": {"term": "How does lower tensor cell type reduce memory footprint?", "namespace": "open-p", "hash": -88945640, "terms": ["How does lower tensor cell type reduce memory footprint?", "does lower tensor cell type reduce memory footprint?", "lower tensor cell type reduce memory footprint?", "tensor cell type reduce memory footprint?", "cell type reduce memory footprint?", "type reduce memory footprint?", "reduce memory footprint?", "memory footprint?", "footprint?"], "tokens": ["How", "does", "lower", "tensor", "cell", "type", "reduce", "memory", "footprint", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1695292765", "fields": {"term": "What is the memory usage for bfloat16?", "namespace": "open-p", "hash": 1695292765, "terms": ["What is the memory usage for bfloat16?", "is the memory usage for bfloat16?", "the memory usage for bfloat16?", "memory usage for bfloat16?", "usage for bfloat16?", "for bfloat16?", "bfloat16?"], "tokens": ["What", "is", "the", "memory", "usage", "for", "bfloat16", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1730287645", "fields": {"term": "What is the standard double memory usage?", "namespace": "open-p", "hash": 1730287645, "terms": ["What is the standard double memory usage?", "is the standard double memory usage?", "the standard double memory usage?", "standard double memory usage?", "double memory usage?", "memory usage?", "usage?"], "tokens": ["What", "is", "the", "standard", "double", "memory", "usage", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1601864125", "fields": {"term": "What is the in-memory data structure used by Vespa tensor fields?", "namespace": "open-p", "hash": 1601864125, "terms": ["What is the in-memory data structure used by Vespa tensor fields?", "is the in-memory data structure used by Vespa tensor fields?", "the in-memory data structure used by Vespa tensor fields?", "in-memory data structure used by Vespa tensor fields?", "data structure used by Vespa tensor fields?", "structure used by Vespa tensor fields?", "used by Vespa tensor fields?", "by Vespa tensor fields?", "Vespa tensor fields?", "tensor fields?", "fields?"], "tokens": ["What", "is", "the", "in", "memory", "data", "structure", "used", "by", "Vespa", "tensor", "fields", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1271541716", "fields": {"term": "What is the primary memory resource usage for large vector datasets?", "namespace": "open-p", "hash": -1271541716, "terms": ["What is the primary memory resource usage for large vector datasets?", "is the primary memory resource usage for large vector datasets?", "the primary memory resource usage for large vector datasets?", "primary memory resource usage for large vector datasets?", "memory resource usage for large vector datasets?", "resource usage for large vector datasets?", "usage for large vector datasets?", "for large vector datasets?", "large vector datasets?", "vector datasets?", "datasets?"], "tokens": ["What", "is", "the", "primary", "memory", "resource", "usage", "for", "large", "vector", "datasets", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1088269611", "fields": {"term": "What is the search latency of HNSW?", "namespace": "open-p", "hash": -1088269611, "terms": ["What is the search latency of HNSW?", "is the search latency of HNSW?", "the search latency of HNSW?", "search latency of HNSW?", "latency of HNSW?", "of HNSW?", "HNSW?"], "tokens": ["What", "is", "the", "search", "latency", "of", "HNSW", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2054526019", "fields": {"term": "What is flat data distribution?", "namespace": "open-p", "hash": -2054526019, "terms": ["What is flat data distribution?", "is flat data distribution?", "flat data distribution?", "data distribution?", "distribution?"], "tokens": ["What", "is", "flat", "data", "distribution", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1364809513", "fields": {"term": "How does flat scaling help with document volume?", "namespace": "open-p", "hash": 1364809513, "terms": ["How does flat scaling help with document volume?", "does flat scaling help with document volume?", "flat scaling help with document volume?", "scaling help with document volume?", "help with document volume?", "with document volume?", "document volume?", "volume?"], "tokens": ["How", "does", "flat", "scaling", "help", "with", "document", "volume", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1006047944", "fields": {"term": "How should pure vector search applications scale document volume?", "namespace": "open-p", "hash": -1006047944, "terms": ["How should pure vector search applications scale document volume?", "should pure vector search applications scale document volume?", "pure vector search applications scale document volume?", "vector search applications scale document volume?", "search applications scale document volume?", "applications scale document volume?", "scale document volume?", "document volume?", "volume?"], "tokens": ["How", "should", "pure", "vector", "search", "applications", "scale", "document", "volume", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1899390416", "fields": {"term": "When is strongly sublinear search not true?", "namespace": "open-p", "hash": -1899390416, "terms": ["When is strongly sublinear search not true?", "is strongly sublinear search not true?", "strongly sublinear search not true?", "sublinear search not true?", "search not true?", "not true?", "true?"], "tokens": ["When", "is", "strongly", "sublinear", "search", "not", "true", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1349236743", "fields": {"term": "What is multi-phase ranking pipeline?", "namespace": "open-p", "hash": 1349236743, "terms": ["What is multi-phase ranking pipeline?", "is multi-phase ranking pipeline?", "multi-phase ranking pipeline?", "ranking pipeline?", "pipeline?"], "tokens": ["What", "is", "multi", "phase", "ranking", "pipeline", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-242406602", "fields": {"term": "How to change distance-metric?", "namespace": "open-p", "hash": -242406602, "terms": ["How to change distance-metric?", "to change distance-metric?", "change distance-metric?", "distance-metric?"], "tokens": ["How", "to", "change", "distance", "metric", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::539216269", "fields": {"term": "What requires restarting?", "namespace": "open-p", "hash": 539216269, "terms": ["What requires restarting?", "requires restarting?", "restarting?"], "tokens": ["What", "requires", "restarting", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1625633536", "fields": {"term": "Can neighbors-to-explore-at-insert be changed?", "namespace": "open-p", "hash": 1625633536, "terms": ["Can neighbors-to-explore-at-insert be changed?", "neighbors-to-explore-at-insert be changed?", "be changed?", "changed?"], "tokens": ["Can", "neighbors", "to", "explore", "at", "insert", "be", "changed", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::973275347", "fields": {"term": "When to re-feed vectors?", "namespace": "open-p", "hash": 973275347, "terms": ["When to re-feed vectors?", "to re-feed vectors?", "re-feed vectors?", "vectors?"], "tokens": ["When", "to", "re", "feed", "vectors", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-878149979", "fields": {"term": "How to change index construction params?", "namespace": "open-p", "hash": -878149979, "terms": ["How to change index construction params?", "to change index construction params?", "change index construction params?", "index construction params?", "construction params?", "params?"], "tokens": ["How", "to", "change", "index", "construction", "params", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-557109402", "fields": {"term": "When not to re-index vectors?", "namespace": "open-p", "hash": -557109402, "terms": ["When not to re-index vectors?", "not to re-index vectors?", "to re-index vectors?", "re-index vectors?", "vectors?"], "tokens": ["When", "not", "to", "re", "index", "vectors", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1079055169", "fields": {"term": "What is an attribute?", "namespace": "open-p", "hash": 1079055169, "terms": ["What is an attribute?", "is an attribute?", "an attribute?", "attribute?"], "tokens": ["What", "is", "an", "attribute", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-432298977", "fields": {"term": "What are attribute properties?", "namespace": "open-p", "hash": -432298977, "terms": ["What are attribute properties?", "are attribute properties?", "attribute properties?", "properties?"], "tokens": ["What", "are", "attribute", "properties", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::878669732", "fields": {"term": "What is 'fast-access'?", "namespace": "open-p", "hash": 878669732, "terms": ["What is 'fast-access'?", "is 'fast-access'?", "'fast-access'?"], "tokens": ["What", "is", "'", "fast", "access", "'?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-648798523", "fields": {"term": "What are some uses for attributes?", "namespace": "open-p", "hash": -648798523, "terms": ["What are some uses for attributes?", "are some uses for attributes?", "some uses for attributes?", "uses for attributes?", "for attributes?", "attributes?"], "tokens": ["What", "are", "some", "uses", "for", "attributes", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1125595291", "fields": {"term": "What data type stores unique strings?", "namespace": "open-p", "hash": 1125595291, "terms": ["What data type stores unique strings?", "data type stores unique strings?", "type stores unique strings?", "stores unique strings?", "unique strings?", "strings?"], "tokens": ["What", "data", "type", "stores", "unique", "strings", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1968853277", "fields": {"term": "How are array attributes stored?", "namespace": "open-p", "hash": -1968853277, "terms": ["How are array attributes stored?", "are array attributes stored?", "array attributes stored?", "attributes stored?", "stored?"], "tokens": ["How", "are", "array", "attributes", "stored", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-870862853", "fields": {"term": "What are the attribute types?", "namespace": "open-p", "hash": -870862853, "terms": ["What are the attribute types?", "are the attribute types?", "the attribute types?", "attribute types?", "types?"], "tokens": ["What", "are", "the", "attribute", "types", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1699377994", "fields": {"term": "What is the limit on the number of documents with the same number of values?", "namespace": "open-p", "hash": -1699377994, "terms": ["What is the limit on the number of documents with the same number of values?", "is the limit on the number of documents with the same number of values?", "the limit on the number of documents with the same number of values?", "limit on the number of documents with the same number of values?", "on the number of documents with the same number of values?", "the number of documents with the same number of values?", "number of documents with the same number of values?", "of documents with the same number of values?", "documents with the same number of values?", "with the same number of values?", "the same number of values?", "same number of values?", "number of values?", "of values?", "values?"], "tokens": ["What", "is", "the", "limit", "on", "the", "number", "of", "documents", "with", "the", "same", "number", "of", "values", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::954426437", "fields": {"term": "What is the limit on the number of unique values for string attributes with fast-search?", "namespace": "open-p", "hash": 954426437, "terms": ["What is the limit on the number of unique values for string attributes with fast-search?", "is the limit on the number of unique values for string attributes with fast-search?", "the limit on the number of unique values for string attributes with fast-search?", "limit on the number of unique values for string attributes with fast-search?", "on the number of unique values for string attributes with fast-search?", "the number of unique values for string attributes with fast-search?", "number of unique values for string attributes with fast-search?", "of unique values for string attributes with fast-search?", "unique values for string attributes with fast-search?", "values for string attributes with fast-search?", "for string attributes with fast-search?", "string attributes with fast-search?", "attributes with fast-search?", "with fast-search?", "fast-search?"], "tokens": ["What", "is", "the", "limit", "on", "the", "number", "of", "unique", "values", "for", "string", "attributes", "with", "fast", "search", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1897799222", "fields": {"term": "What is the Attribute Multivalue Mapping used for?", "namespace": "open-p", "hash": -1897799222, "terms": ["What is the Attribute Multivalue Mapping used for?", "is the Attribute Multivalue Mapping used for?", "the Attribute Multivalue Mapping used for?", "Attribute Multivalue Mapping used for?", "Multivalue Mapping used for?", "Mapping used for?", "used for?", "for?"], "tokens": ["What", "is", "the", "Attribute", "Multivalue", "Mapping", "used", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::606508591", "fields": {"term": "What is the Attribute Tensor Store used for?", "namespace": "open-p", "hash": 606508591, "terms": ["What is the Attribute Tensor Store used for?", "is the Attribute Tensor Store used for?", "the Attribute Tensor Store used for?", "Attribute Tensor Store used for?", "Tensor Store used for?", "Store used for?", "used for?", "for?"], "tokens": ["What", "is", "the", "Attribute", "Tensor", "Store", "used", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1687972642", "fields": {"term": "What is fast-search?", "namespace": "open-p", "hash": -1687972642, "terms": ["What is fast-search?", "is fast-search?", "fast-search?"], "tokens": ["What", "is", "fast", "search", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::-234950842", "fields": {"term": "What is a dictionary?", "namespace": "open-p", "hash": -234950842, "terms": ["What is a dictionary?", "is a dictionary?", "a dictionary?", "dictionary?"], "tokens": ["What", "is", "a", "dictionary", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1437966167", "fields": {"term": "What is an occurrence b-tree?", "namespace": "open-p", "hash": 1437966167, "terms": ["What is an occurrence b-tree?", "is an occurrence b-tree?", "an occurrence b-tree?", "occurrence b-tree?", "b-tree?"], "tokens": ["What", "is", "an", "occurrence", "b", "tree", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1587027546", "fields": {"term": "What is the default dictionary?", "namespace": "open-p", "hash": 1587027546, "terms": ["What is the default dictionary?", "is the default dictionary?", "the default dictionary?", "default dictionary?", "dictionary?"], "tokens": ["What", "is", "the", "default", "dictionary", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1484774722", "fields": {"term": "What are the implications of using fast-search?", "namespace": "open-p", "hash": 1484774722, "terms": ["What are the implications of using fast-search?", "are the implications of using fast-search?", "the implications of using fast-search?", "implications of using fast-search?", "of using fast-search?", "using fast-search?", "fast-search?"], "tokens": ["What", "are", "the", "implications", "of", "using", "fast", "search", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::840476987", "fields": {"term": "What is the value b-tree used for?", "namespace": "open-p", "hash": 840476987, "terms": ["What is the value b-tree used for?", "is the value b-tree used for?", "the value b-tree used for?", "value b-tree used for?", "b-tree used for?", "used for?", "for?"], "tokens": ["What", "is", "the", "value", "b", "tree", "used", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1259088683", "fields": {"term": "What are some factors that affect attribute memory usage?", "namespace": "open-p", "hash": -1259088683, "terms": ["What are some factors that affect attribute memory usage?", "are some factors that affect attribute memory usage?", "some factors that affect attribute memory usage?", "factors that affect attribute memory usage?", "that affect attribute memory usage?", "affect attribute memory usage?", "attribute memory usage?", "memory usage?", "usage?"], "tokens": ["What", "are", "some", "factors", "that", "affect", "attribute", "memory", "usage", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-823827790", "fields": {"term": "How does the memory footprint of string attributes compare to other attribute types?", "namespace": "open-p", "hash": -823827790, "terms": ["How does the memory footprint of string attributes compare to other attribute types?", "does the memory footprint of string attributes compare to other attribute types?", "the memory footprint of string attributes compare to other attribute types?", "memory footprint of string attributes compare to other attribute types?", "footprint of string attributes compare to other attribute types?", "of string attributes compare to other attribute types?", "string attributes compare to other attribute types?", "attributes compare to other attribute types?", "compare to other attribute types?", "to other attribute types?", "other attribute types?", "attribute types?", "types?"], "tokens": ["How", "does", "the", "memory", "footprint", "of", "string", "attributes", "compare", "to", "other", "attribute", "types", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1311966319", "fields": {"term": "How can increasing the average number of values per document affect memory footprint?", "namespace": "open-p", "hash": -1311966319, "terms": ["How can increasing the average number of values per document affect memory footprint?", "can increasing the average number of values per document affect memory footprint?", "increasing the average number of values per document affect memory footprint?", "the average number of values per document affect memory footprint?", "average number of values per document affect memory footprint?", "number of values per document affect memory footprint?", "of values per document affect memory footprint?", "values per document affect memory footprint?", "per document affect memory footprint?", "document affect memory footprint?", "affect memory footprint?", "memory footprint?", "footprint?"], "tokens": ["How", "can", "increasing", "the", "average", "number", "of", "values", "per", "document", "affect", "memory", "footprint", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-692625879", "fields": {"term": "What is the maximum footprint during initialization?", "namespace": "open-p", "hash": -692625879, "terms": ["What is the maximum footprint during initialization?", "is the maximum footprint during initialization?", "the maximum footprint during initialization?", "maximum footprint during initialization?", "footprint during initialization?", "during initialization?", "initialization?"], "tokens": ["What", "is", "the", "maximum", "footprint", "during", "initialization", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1999806612", "fields": {"term": "What is an example of a multivalue attribute?", "namespace": "open-p", "hash": 1999806612, "terms": ["What is an example of a multivalue attribute?", "is an example of a multivalue attribute?", "an example of a multivalue attribute?", "example of a multivalue attribute?", "of a multivalue attribute?", "a multivalue attribute?", "multivalue attribute?", "attribute?"], "tokens": ["What", "is", "an", "example", "of", "a", "multivalue", "attribute", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1253277923", "fields": {"term": "What is the proton /state/v1/ interface used for?", "namespace": "open-p", "hash": 1253277923, "terms": ["What is the proton /state/v1/ interface used for?", "is the proton /state/v1/ interface used for?", "the proton /state/v1/ interface used for?", "proton /state/v1/ interface used for?", "/state/v1/ interface used for?", "interface used for?", "used for?", "for?"], "tokens": ["What", "is", "the", "proton", "/", "state", "/", "v1", "/", "interface", "used", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1758779327", "fields": {"term": "How can the example attribute sizing spreadsheet be helpful?", "namespace": "open-p", "hash": -1758779327, "terms": ["How can the example attribute sizing spreadsheet be helpful?", "can the example attribute sizing spreadsheet be helpful?", "the example attribute sizing spreadsheet be helpful?", "example attribute sizing spreadsheet be helpful?", "attribute sizing spreadsheet be helpful?", "sizing spreadsheet be helpful?", "spreadsheet be helpful?", "be helpful?", "helpful?"], "tokens": ["How", "can", "the", "example", "attribute", "sizing", "spreadsheet", "be", "helpful", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1419906532", "fields": {"term": "What is the maximum number of documents per node supported by multivalue attributes?", "namespace": "open-p", "hash": -1419906532, "terms": ["What is the maximum number of documents per node supported by multivalue attributes?", "is the maximum number of documents per node supported by multivalue attributes?", "the maximum number of documents per node supported by multivalue attributes?", "maximum number of documents per node supported by multivalue attributes?", "number of documents per node supported by multivalue attributes?", "of documents per node supported by multivalue attributes?", "documents per node supported by multivalue attributes?", "per node supported by multivalue attributes?", "node supported by multivalue attributes?", "supported by multivalue attributes?", "by multivalue attributes?", "multivalue attributes?", "attributes?"], "tokens": ["What", "is", "the", "maximum", "number", "of", "documents", "per", "node", "supported", "by", "multivalue", "attributes", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-14793207", "fields": {"term": "What is attribute sizing?", "namespace": "open-p", "hash": -14793207, "terms": ["What is attribute sizing?", "is attribute sizing?", "attribute sizing?", "sizing?"], "tokens": ["What", "is", "attribute", "sizing", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1254042410", "fields": {"term": "Why is attribute sizing not exact?", "namespace": "open-p", "hash": -1254042410, "terms": ["Why is attribute sizing not exact?", "is attribute sizing not exact?", "attribute sizing not exact?", "sizing not exact?", "not exact?", "exact?"], "tokens": ["Why", "is", "attribute", "sizing", "not", "exact", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-620009195", "fields": {"term": "What is VW?", "namespace": "open-p", "hash": -620009195, "terms": ["What is VW?", "is VW?", "VW?"], "tokens": ["What", "is", "VW", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2139645937", "fields": {"term": "What is PW?", "namespace": "open-p", "hash": -2139645937, "terms": ["What is PW?", "is PW?", "PW?"], "tokens": ["What", "is", "PW", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1737821621", "fields": {"term": "What is the formula for multivalue mapping?", "namespace": "open-p", "hash": 1737821621, "terms": ["What is the formula for multivalue mapping?", "is the formula for multivalue mapping?", "the formula for multivalue mapping?", "formula for multivalue mapping?", "for multivalue mapping?", "multivalue mapping?", "mapping?"], "tokens": ["What", "is", "the", "formula", "for", "multivalue", "mapping", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1123582819", "fields": {"term": "What is the ROF factor for enum store?", "namespace": "open-p", "hash": 1123582819, "terms": ["What is the ROF factor for enum store?", "is the ROF factor for enum store?", "the ROF factor for enum store?", "ROF factor for enum store?", "factor for enum store?", "for enum store?", "enum store?", "store?"], "tokens": ["What", "is", "the", "ROF", "factor", "for", "enum", "store", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1886806892", "fields": {"term": "What is a posting list?", "namespace": "open-p", "hash": -1886806892, "terms": ["What is a posting list?", "is a posting list?", "a posting list?", "posting list?", "list?"], "tokens": ["What", "is", "a", "posting", "list", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::-450310904", "fields": {"term": "What is the ROF factor for posting lists?", "namespace": "open-p", "hash": -450310904, "terms": ["What is the ROF factor for posting lists?", "is the ROF factor for posting lists?", "the ROF factor for posting lists?", "ROF factor for posting lists?", "factor for posting lists?", "for posting lists?", "posting lists?", "lists?"], "tokens": ["What", "is", "the", "ROF", "factor", "for", "posting", "lists", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1051998659", "fields": {"term": "What is the FW factor used for?", "namespace": "open-p", "hash": 1051998659, "terms": ["What is the FW factor used for?", "is the FW factor used for?", "the FW factor used for?", "FW factor used for?", "factor used for?", "used for?", "for?"], "tokens": ["What", "is", "the", "FW", "factor", "used", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1411117270", "fields": {"term": "What is the MIW factor used for?", "namespace": "open-p", "hash": 1411117270, "terms": ["What is the MIW factor used for?", "is the MIW factor used for?", "the MIW factor used for?", "MIW factor used for?", "factor used for?", "used for?", "for?"], "tokens": ["What", "is", "the", "MIW", "factor", "used", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::636933750", "fields": {"term": "What attribute types use EIW?", "namespace": "open-p", "hash": 636933750, "terms": ["What attribute types use EIW?", "attribute types use EIW?", "types use EIW?", "use EIW?", "EIW?"], "tokens": ["What", "attribute", "types", "use", "EIW", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-345072252", "fields": {"term": "What is the formula for Numeric singlevalue plain?", "namespace": "open-p", "hash": -345072252, "terms": ["What is the formula for Numeric singlevalue plain?", "is the formula for Numeric singlevalue plain?", "the formula for Numeric singlevalue plain?", "formula for Numeric singlevalue plain?", "for Numeric singlevalue plain?", "Numeric singlevalue plain?", "singlevalue plain?", "plain?"], "tokens": ["What", "is", "the", "formula", "for", "Numeric", "singlevalue", "plain", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1646223559", "fields": {"term": "What are the components for Numeric multivalue value fast-search?", "namespace": "open-p", "hash": 1646223559, "terms": ["What are the components for Numeric multivalue value fast-search?", "are the components for Numeric multivalue value fast-search?", "the components for Numeric multivalue value fast-search?", "components for Numeric multivalue value fast-search?", "for Numeric multivalue value fast-search?", "Numeric multivalue value fast-search?", "multivalue value fast-search?", "value fast-search?", "fast-search?"], "tokens": ["What", "are", "the", "components", "for", "Numeric", "multivalue", "value", "fast", "search", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-236929678", "fields": {"term": "What is Numeric singlevalue fast-search?", "namespace": "open-p", "hash": -236929678, "terms": ["What is Numeric singlevalue fast-search?", "is Numeric singlevalue fast-search?", "Numeric singlevalue fast-search?", "singlevalue fast-search?", "fast-search?"], "tokens": ["What", "is", "Numeric", "singlevalue", "fast", "search", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-865408053", "fields": {"term": "What is the formula for Multivalue string plain?", "namespace": "open-p", "hash": -865408053, "terms": ["What is the formula for Multivalue string plain?", "is the formula for Multivalue string plain?", "the formula for Multivalue string plain?", "formula for Multivalue string plain?", "for Multivalue string plain?", "Multivalue string plain?", "string plain?", "plain?"], "tokens": ["What", "is", "the", "formula", "for", "Multivalue", "string", "plain", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1750889518", "fields": {"term": "What is the formula for Boolean singlevalue?", "namespace": "open-p", "hash": -1750889518, "terms": ["What is the formula for Boolean singlevalue?", "is the formula for Boolean singlevalue?", "the formula for Boolean singlevalue?", "formula for Boolean singlevalue?", "for Boolean singlevalue?", "Boolean singlevalue?", "singlevalue?"], "tokens": ["What", "is", "the", "formula", "for", "Boolean", "singlevalue", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1998970415", "fields": {"term": "What is the formula for Singlevalue string fast-search?", "namespace": "open-p", "hash": -1998970415, "terms": ["What is the formula for Singlevalue string fast-search?", "is the formula for Singlevalue string fast-search?", "the formula for Singlevalue string fast-search?", "formula for Singlevalue string fast-search?", "for Singlevalue string fast-search?", "Singlevalue string fast-search?", "string fast-search?", "fast-search?"], "tokens": ["What", "is", "the", "formula", "for", "Singlevalue", "string", "fast", "search", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1166005403", "fields": {"term": "What are the components for Numeric singlevalue fast-search?", "namespace": "open-p", "hash": -1166005403, "terms": ["What are the components for Numeric singlevalue fast-search?", "are the components for Numeric singlevalue fast-search?", "the components for Numeric singlevalue fast-search?", "components for Numeric singlevalue fast-search?", "for Numeric singlevalue fast-search?", "Numeric singlevalue fast-search?", "singlevalue fast-search?", "fast-search?"], "tokens": ["What", "are", "the", "components", "for", "Numeric", "singlevalue", "fast", "search", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-562869417", "fields": {"term": "What are the components for Multivalue string fast-search?", "namespace": "open-p", "hash": -562869417, "terms": ["What are the components for Multivalue string fast-search?", "are the components for Multivalue string fast-search?", "the components for Multivalue string fast-search?", "components for Multivalue string fast-search?", "for Multivalue string fast-search?", "Multivalue string fast-search?", "string fast-search?", "fast-search?"], "tokens": ["What", "are", "the", "components", "for", "Multivalue", "string", "fast", "search", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1596851129", "fields": {"term": "What is the paged attribute setting?", "namespace": "open-p", "hash": -1596851129, "terms": ["What is the paged attribute setting?", "is the paged attribute setting?", "the paged attribute setting?", "paged attribute setting?", "attribute setting?", "setting?"], "tokens": ["What", "is", "the", "paged", "attribute", "setting", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1555349599", "fields": {"term": "What types are not supported by paged setting?", "namespace": "open-p", "hash": -1555349599, "terms": ["What types are not supported by paged setting?", "types are not supported by paged setting?", "are not supported by paged setting?", "not supported by paged setting?", "supported by paged setting?", "by paged setting?", "paged setting?", "setting?"], "tokens": ["What", "types", "are", "not", "supported", "by", "paged", "setting", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1788953300", "fields": {"term": "When is paged attribute setting an alternative?", "namespace": "open-p", "hash": -1788953300, "terms": ["When is paged attribute setting an alternative?", "is paged attribute setting an alternative?", "paged attribute setting an alternative?", "attribute setting an alternative?", "setting an alternative?", "an alternative?", "alternative?"], "tokens": ["When", "is", "paged", "attribute", "setting", "an", "alternative", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1344423938", "fields": {"term": "What is the rerank-count?", "namespace": "open-p", "hash": -1344423938, "terms": ["What is the rerank-count?", "is the rerank-count?", "the rerank-count?", "rerank-count?"], "tokens": ["What", "is", "the", "rerank", "count", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1085103935", "fields": {"term": "What is the worst case disk access per query?", "namespace": "open-p", "hash": -1085103935, "terms": ["What is the worst case disk access per query?", "is the worst case disk access per query?", "the worst case disk access per query?", "worst case disk access per query?", "case disk access per query?", "disk access per query?", "access per query?", "per query?", "query?"], "tokens": ["What", "is", "the", "worst", "case", "disk", "access", "per", "query", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1165128402", "fields": {"term": "What is the attribute used for?", "namespace": "open-p", "hash": -1165128402, "terms": ["What is the attribute used for?", "is the attribute used for?", "the attribute used for?", "attribute used for?", "used for?", "for?"], "tokens": ["What", "is", "the", "attribute", "used", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1928763352", "fields": {"term": "What are paged attributes?", "namespace": "open-p", "hash": -1928763352, "terms": ["What are paged attributes?", "are paged attributes?", "paged attributes?", "attributes?"], "tokens": ["What", "are", "paged", "attributes", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1844975709", "fields": {"term": "What are the disadvantages of paged attributes?", "namespace": "open-p", "hash": -1844975709, "terms": ["What are the disadvantages of paged attributes?", "are the disadvantages of paged attributes?", "the disadvantages of paged attributes?", "disadvantages of paged attributes?", "of paged attributes?", "paged attributes?", "attributes?"], "tokens": ["What", "are", "the", "disadvantages", "of", "paged", "attributes", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-744768048", "fields": {"term": "How do paged attributes affect query latency?", "namespace": "open-p", "hash": -744768048, "terms": ["How do paged attributes affect query latency?", "do paged attributes affect query latency?", "paged attributes affect query latency?", "attributes affect query latency?", "affect query latency?", "query latency?", "latency?"], "tokens": ["How", "do", "paged", "attributes", "affect", "query", "latency", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::851209209", "fields": {"term": "How does memory usage change when using paged attributes?", "namespace": "open-p", "hash": 851209209, "terms": ["How does memory usage change when using paged attributes?", "does memory usage change when using paged attributes?", "memory usage change when using paged attributes?", "usage change when using paged attributes?", "change when using paged attributes?", "when using paged attributes?", "using paged attributes?", "paged attributes?", "attributes?"], "tokens": ["How", "does", "memory", "usage", "change", "when", "using", "paged", "attributes", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1036437005", "fields": {"term": "What is the recommended understanding before enabling paged attributes?", "namespace": "open-p", "hash": 1036437005, "terms": ["What is the recommended understanding before enabling paged attributes?", "is the recommended understanding before enabling paged attributes?", "the recommended understanding before enabling paged attributes?", "recommended understanding before enabling paged attributes?", "understanding before enabling paged attributes?", "before enabling paged attributes?", "enabling paged attributes?", "paged attributes?", "attributes?"], "tokens": ["What", "is", "the", "recommended", "understanding", "before", "enabling", "paged", "attributes", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1556164258", "fields": {"term": "How does using paged attributes affect disk usage?", "namespace": "open-p", "hash": 1556164258, "terms": ["How does using paged attributes affect disk usage?", "does using paged attributes affect disk usage?", "using paged attributes affect disk usage?", "paged attributes affect disk usage?", "attributes affect disk usage?", "affect disk usage?", "disk usage?", "usage?"], "tokens": ["How", "does", "using", "paged", "attributes", "affect", "disk", "usage", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1314446713", "fields": {"term": "What is the problem of changing the paged setting on a running system?", "namespace": "open-p", "hash": -1314446713, "terms": ["What is the problem of changing the paged setting on a running system?", "is the problem of changing the paged setting on a running system?", "the problem of changing the paged setting on a running system?", "problem of changing the paged setting on a running system?", "of changing the paged setting on a running system?", "changing the paged setting on a running system?", "the paged setting on a running system?", "paged setting on a running system?", "setting on a running system?", "on a running system?", "a running system?", "running system?", "system?"], "tokens": ["What", "is", "the", "problem", "of", "changing", "the", "paged", "setting", "on", "a", "running", "system", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1048664099", "fields": {"term": "How does using a paged attribute affect first-phase ranking?", "namespace": "open-p", "hash": 1048664099, "terms": ["How does using a paged attribute affect first-phase ranking?", "does using a paged attribute affect first-phase ranking?", "using a paged attribute affect first-phase ranking?", "a paged attribute affect first-phase ranking?", "paged attribute affect first-phase ranking?", "attribute affect first-phase ranking?", "affect first-phase ranking?", "first-phase ranking?", "ranking?"], "tokens": ["How", "does", "using", "a", "paged", "attribute", "affect", "first", "phase", "ranking", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::26449880", "fields": {"term": "What are mutable attributes?", "namespace": "open-p", "hash": 26449880, "terms": ["What are mutable attributes?", "are mutable attributes?", "mutable attributes?", "attributes?"], "tokens": ["What", "are", "mutable", "attributes", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::701618667", "fields": {"term": "What is document metadata?", "namespace": "open-p", "hash": 701618667, "terms": ["What is document metadata?", "is document metadata?", "document metadata?", "metadata?"], "tokens": ["What", "is", "document", "metadata", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::794108890", "fields": {"term": "How are attribute values mutated?", "namespace": "open-p", "hash": 794108890, "terms": ["How are attribute values mutated?", "are attribute values mutated?", "attribute values mutated?", "values mutated?", "mutated?"], "tokens": ["How", "are", "attribute", "values", "mutated", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-410274231", "fields": {"term": "What is the purpose of mutable attributes?", "namespace": "open-p", "hash": -410274231, "terms": ["What is the purpose of mutable attributes?", "is the purpose of mutable attributes?", "the purpose of mutable attributes?", "purpose of mutable attributes?", "of mutable attributes?", "mutable attributes?", "attributes?"], "tokens": ["What", "is", "the", "purpose", "of", "mutable", "attributes", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2040375520", "fields": {"term": "What is the relationship between mutable attributes and rank profiles?", "namespace": "open-p", "hash": 2040375520, "terms": ["What is the relationship between mutable attributes and rank profiles?", "is the relationship between mutable attributes and rank profiles?", "the relationship between mutable attributes and rank profiles?", "relationship between mutable attributes and rank profiles?", "between mutable attributes and rank profiles?", "mutable attributes and rank profiles?", "attributes and rank profiles?", "and rank profiles?", "rank profiles?", "profiles?"], "tokens": ["What", "is", "the", "relationship", "between", "mutable", "attributes", "and", "rank", "profiles", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1497460436", "fields": {"term": "What is document meta store?", "namespace": "open-p", "hash": 1497460436, "terms": ["What is document meta store?", "is document meta store?", "document meta store?", "meta store?", "store?"], "tokens": ["What", "is", "document", "meta", "store", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::203813570", "fields": {"term": "How does meta store scale?", "namespace": "open-p", "hash": 203813570, "terms": ["How does meta store scale?", "does meta store scale?", "meta store scale?", "store scale?", "scale?"], "tokens": ["How", "does", "meta", "store", "scale", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::884353628", "fields": {"term": "What is content.proton.documentdb.ready.attribute.memory_usage.allocated_bytes?", "namespace": "open-p", "hash": 884353628, "terms": ["What is content.proton.documentdb.ready.attribute.memory_usage.allocated_bytes?", "is content.proton.documentdb.ready.attribute.memory_usage.allocated_bytes?", "content.proton.documentdb.ready.attribute.memory_usage.allocated_bytes?"], "tokens": ["What", "is", "content", "proton", "documentdb", "ready", "attribute", "memory_usage", "allocated_bytes", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-733171893", "fields": {"term": "How is memory usage for small documents affected by meta store?", "namespace": "open-p", "hash": -733171893, "terms": ["How is memory usage for small documents affected by meta store?", "is memory usage for small documents affected by meta store?", "memory usage for small documents affected by meta store?", "usage for small documents affected by meta store?", "for small documents affected by meta store?", "small documents affected by meta store?", "documents affected by meta store?", "affected by meta store?", "by meta store?", "meta store?", "store?"], "tokens": ["How", "is", "memory", "usage", "for", "small", "documents", "affected", "by", "meta", "store", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1765026100", "fields": {"term": "How can the size of document meta store be found?", "namespace": "open-p", "hash": -1765026100, "terms": ["How can the size of document meta store be found?", "can the size of document meta store be found?", "the size of document meta store be found?", "size of document meta store be found?", "of document meta store be found?", "document meta store be found?", "meta store be found?", "store be found?", "be found?", "found?"], "tokens": ["How", "can", "the", "size", "of", "document", "meta", "store", "be", "found", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1898846474", "fields": {"term": "What is the memory usage for applications with small documents?", "namespace": "open-p", "hash": -1898846474, "terms": ["What is the memory usage for applications with small documents?", "is the memory usage for applications with small documents?", "the memory usage for applications with small documents?", "memory usage for applications with small documents?", "usage for applications with small documents?", "for applications with small documents?", "applications with small documents?", "with small documents?", "small documents?", "documents?"], "tokens": ["What", "is", "the", "memory", "usage", "for", "applications", "with", "small", "documents", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2030979433", "fields": {"term": "What is the node's ready documents size and in-memory usage?", "namespace": "open-p", "hash": -2030979433, "terms": ["What is the node's ready documents size and in-memory usage?", "is the node's ready documents size and in-memory usage?", "the node's ready documents size and in-memory usage?", "node's ready documents size and in-memory usage?", "ready documents size and in-memory usage?", "documents size and in-memory usage?", "size and in-memory usage?", "and in-memory usage?", "in-memory usage?", "usage?"], "tokens": ["What", "is", "the", "node", "'", "s", "ready", "documents", "size", "and", "in", "memory", "usage", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-232909289", "fields": {"term": "How to batch delete documents?", "namespace": "open-p", "hash": -232909289, "terms": ["How to batch delete documents?", "to batch delete documents?", "batch delete documents?", "delete documents?", "documents?"], "tokens": ["How", "to", "batch", "delete", "documents", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1513268759", "fields": {"term": "What is the syntax for batch deleting documents?", "namespace": "open-p", "hash": -1513268759, "terms": ["What is the syntax for batch deleting documents?", "is the syntax for batch deleting documents?", "the syntax for batch deleting documents?", "syntax for batch deleting documents?", "for batch deleting documents?", "batch deleting documents?", "deleting documents?", "documents?"], "tokens": ["What", "is", "the", "syntax", "for", "batch", "deleting", "documents", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1243213335", "fields": {"term": "What is the curl command for deleting documents?", "namespace": "open-p", "hash": 1243213335, "terms": ["What is the curl command for deleting documents?", "is the curl command for deleting documents?", "the curl command for deleting documents?", "curl command for deleting documents?", "command for deleting documents?", "for deleting documents?", "deleting documents?", "documents?"], "tokens": ["What", "is", "the", "curl", "command", "for", "deleting", "documents", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1459129970", "fields": {"term": "Can you delete a schema and its content?", "namespace": "open-p", "hash": 1459129970, "terms": ["Can you delete a schema and its content?", "you delete a schema and its content?", "delete a schema and its content?", "a schema and its content?", "schema and its content?", "and its content?", "its content?", "content?"], "tokens": ["Can", "you", "delete", "a", "schema", "and", "its", "content", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1362821866", "fields": {"term": "Who is the owner of music?", "namespace": "open-p", "hash": -1362821866, "terms": ["Who is the owner of music?", "is the owner of music?", "the owner of music?", "owner of music?", "of music?", "music?"], "tokens": ["Who", "is", "the", "owner", "of", "music", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1417339422", "fields": {"term": "What is in the music directory?", "namespace": "open-p", "hash": 1417339422, "terms": ["What is in the music directory?", "is in the music directory?", "in the music directory?", "the music directory?", "music directory?", "directory?"], "tokens": ["What", "is", "in", "the", "music", "directory", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-86841823", "fields": {"term": "What is the size of music directory?", "namespace": "open-p", "hash": -86841823, "terms": ["What is the size of music directory?", "is the size of music directory?", "the size of music directory?", "size of music directory?", "of music directory?", "music directory?", "directory?"], "tokens": ["What", "is", "the", "size", "of", "music", "directory", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-924278959", "fields": {"term": "What is the Custom Component State API used for?", "namespace": "open-p", "hash": -924278959, "terms": ["What is the Custom Component State API used for?", "is the Custom Component State API used for?", "the Custom Component State API used for?", "Custom Component State API used for?", "Component State API used for?", "State API used for?", "API used for?", "used for?", "for?"], "tokens": ["What", "is", "the", "Custom", "Component", "State", "API", "used", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-647814574", "fields": {"term": "What is the recommended practice after deploying without schema?", "namespace": "open-p", "hash": -647814574, "terms": ["What is the recommended practice after deploying without schema?", "is the recommended practice after deploying without schema?", "the recommended practice after deploying without schema?", "recommended practice after deploying without schema?", "practice after deploying without schema?", "after deploying without schema?", "deploying without schema?", "without schema?", "schema?"], "tokens": ["What", "is", "the", "recommended", "practice", "after", "deploying", "without", "schema", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-99334115", "fields": {"term": "How can I drop all documents efficiently?", "namespace": "open-p", "hash": -99334115, "terms": ["How can I drop all documents efficiently?", "can I drop all documents efficiently?", "I drop all documents efficiently?", "drop all documents efficiently?", "all documents efficiently?", "documents efficiently?", "efficiently?"], "tokens": ["How", "can", "I", "drop", "all", "documents", "efficiently", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1767073597", "fields": {"term": "Why should validation-overrides.xml be removed after deploying?", "namespace": "open-p", "hash": 1767073597, "terms": ["Why should validation-overrides.xml be removed after deploying?", "should validation-overrides.xml be removed after deploying?", "validation-overrides.xml be removed after deploying?", "be removed after deploying?", "removed after deploying?", "after deploying?", "deploying?"], "tokens": ["Why", "should", "validation", "overrides", "xml", "be", "removed", "after", "deploying", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1149362630", "fields": {"term": "Can I inspect document count per schema?", "namespace": "open-p", "hash": -1149362630, "terms": ["Can I inspect document count per schema?", "I inspect document count per schema?", "inspect document count per schema?", "document count per schema?", "count per schema?", "per schema?", "schema?"], "tokens": ["Can", "I", "inspect", "document", "count", "per", "schema", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1199127929", "fields": {"term": "What is the procedure for dropping all documents?", "namespace": "open-p", "hash": -1199127929, "terms": ["What is the procedure for dropping all documents?", "is the procedure for dropping all documents?", "the procedure for dropping all documents?", "procedure for dropping all documents?", "for dropping all documents?", "dropping all documents?", "all documents?", "documents?"], "tokens": ["What", "is", "the", "procedure", "for", "dropping", "all", "documents", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2051646015", "fields": {"term": "What is the schema-removal element used for?", "namespace": "open-p", "hash": -2051646015, "terms": ["What is the schema-removal element used for?", "is the schema-removal element used for?", "the schema-removal element used for?", "schema-removal element used for?", "element used for?", "used for?", "for?"], "tokens": ["What", "is", "the", "schema", "removal", "element", "used", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1617001764", "fields": {"term": "What is the bm25 rank feature?", "namespace": "open-p", "hash": -1617001764, "terms": ["What is the bm25 rank feature?", "is the bm25 rank feature?", "the bm25 rank feature?", "bm25 rank feature?", "rank feature?", "feature?"], "tokens": ["What", "is", "the", "bm25", "rank", "feature", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-836742850", "fields": {"term": "What is Okapi BM25?", "namespace": "open-p", "hash": -836742850, "terms": ["What is Okapi BM25?", "is Okapi BM25?", "Okapi BM25?", "BM25?"], "tokens": ["What", "is", "Okapi", "BM25", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::481671323", "fields": {"term": "What kinds of fields does bm25 operate on?", "namespace": "open-p", "hash": 481671323, "terms": ["What kinds of fields does bm25 operate on?", "kinds of fields does bm25 operate on?", "of fields does bm25 operate on?", "fields does bm25 operate on?", "does bm25 operate on?", "bm25 operate on?", "operate on?", "on?"], "tokens": ["What", "kinds", "of", "fields", "does", "bm25", "operate", "on", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1913125116", "fields": {"term": "How does bm25 compare to nativeRank?", "namespace": "open-p", "hash": -1913125116, "terms": ["How does bm25 compare to nativeRank?", "does bm25 compare to nativeRank?", "bm25 compare to nativeRank?", "compare to nativeRank?", "to nativeRank?", "nativeRank?"], "tokens": ["How", "does", "bm25", "compare", "to", "nativeRank", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-897663227", "fields": {"term": "When is bm25 a good candidate to use?", "namespace": "open-p", "hash": -897663227, "terms": ["When is bm25 a good candidate to use?", "is bm25 a good candidate to use?", "bm25 a good candidate to use?", "a good candidate to use?", "good candidate to use?", "candidate to use?", "to use?", "use?"], "tokens": ["When", "is", "bm25", "a", "good", "candidate", "to", "use", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-339474701", "fields": {"term": "What is bm25?", "namespace": "open-p", "hash": -339474701, "terms": ["What is bm25?", "is bm25?", "bm25?"], "tokens": ["What", "is", "bm25", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::-1358473768", "fields": {"term": "How is bm25 calculated?", "namespace": "open-p", "hash": -1358473768, "terms": ["How is bm25 calculated?", "is bm25 calculated?", "bm25 calculated?", "calculated?"], "tokens": ["How", "is", "bm25", "calculated", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-121612007", "fields": {"term": "What is IDF in bm25?", "namespace": "open-p", "hash": -121612007, "terms": ["What is IDF in bm25?", "is IDF in bm25?", "IDF in bm25?", "in bm25?", "bm25?"], "tokens": ["What", "is", "IDF", "in", "bm25", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1452338025", "fields": {"term": "What is the term frequency in bm25?", "namespace": "open-p", "hash": -1452338025, "terms": ["What is the term frequency in bm25?", "is the term frequency in bm25?", "the term frequency in bm25?", "term frequency in bm25?", "frequency in bm25?", "in bm25?", "bm25?"], "tokens": ["What", "is", "the", "term", "frequency", "in", "bm25", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1919335183", "fields": {"term": "What is field-length in bm25?", "namespace": "open-p", "hash": -1919335183, "terms": ["What is field-length in bm25?", "is field-length in bm25?", "field-length in bm25?", "in bm25?", "bm25?"], "tokens": ["What", "is", "field", "length", "in", "bm25", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::909717889", "fields": {"term": "What is k1 in bm25?", "namespace": "open-p", "hash": 909717889, "terms": ["What is k1 in bm25?", "is k1 in bm25?", "k1 in bm25?", "in bm25?", "bm25?"], "tokens": ["What", "is", "k1", "in", "bm25", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::912139762", "fields": {"term": "What is b in bm25?", "namespace": "open-p", "hash": 912139762, "terms": ["What is b in bm25?", "is b in bm25?", "b in bm25?", "in bm25?", "bm25?"], "tokens": ["What", "is", "b", "in", "bm25", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1953169547", "fields": {"term": "What is bm25 rank feature?", "namespace": "open-p", "hash": 1953169547, "terms": ["What is bm25 rank feature?", "is bm25 rank feature?", "bm25 rank feature?", "rank feature?", "feature?"], "tokens": ["What", "is", "bm25", "rank", "feature", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1707875591", "fields": {"term": "What is the enable-bm25 flag?", "namespace": "open-p", "hash": 1707875591, "terms": ["What is the enable-bm25 flag?", "is the enable-bm25 flag?", "the enable-bm25 flag?", "enable-bm25 flag?", "flag?"], "tokens": ["What", "is", "the", "enable", "bm25", "flag", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1956758258", "fields": {"term": "What are the extra steps?", "namespace": "open-p", "hash": 1956758258, "terms": ["What are the extra steps?", "are the extra steps?", "the extra steps?", "extra steps?", "steps?"], "tokens": ["What", "are", "the", "extra", "steps", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::631973705", "fields": {"term": "What is memory index flush?", "namespace": "open-p", "hash": 631973705, "terms": ["What is memory index flush?", "is memory index flush?", "memory index flush?", "index flush?", "flush?"], "tokens": ["What", "is", "memory", "index", "flush", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::179807502", "fields": {"term": "What is disk index fusion?", "namespace": "open-p", "hash": 179807502, "terms": ["What is disk index fusion?", "is disk index fusion?", "disk index fusion?", "index fusion?", "fusion?"], "tokens": ["What", "is", "disk", "index", "fusion", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1430570823", "fields": {"term": "When must enable-bm25 be set?", "namespace": "open-p", "hash": -1430570823, "terms": ["When must enable-bm25 be set?", "must enable-bm25 be set?", "enable-bm25 be set?", "be set?", "set?"], "tokens": ["When", "must", "enable", "bm25", "be", "set", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1346353565", "fields": {"term": "What is the predicate fields document?", "namespace": "open-p", "hash": -1346353565, "terms": ["What is the predicate fields document?", "is the predicate fields document?", "the predicate fields document?", "predicate fields document?", "fields document?", "document?"], "tokens": ["What", "is", "the", "predicate", "fields", "document", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1205406447", "fields": {"term": "What is the standalone Java library for searching predicate fields?", "namespace": "open-p", "hash": 1205406447, "terms": ["What is the standalone Java library for searching predicate fields?", "is the standalone Java library for searching predicate fields?", "the standalone Java library for searching predicate fields?", "standalone Java library for searching predicate fields?", "Java library for searching predicate fields?", "library for searching predicate fields?", "for searching predicate fields?", "searching predicate fields?", "predicate fields?", "fields?"], "tokens": ["What", "is", "the", "standalone", "Java", "library", "for", "searching", "predicate", "fields", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1128375709", "fields": {"term": "Where can I find the API documentation for predicate search?", "namespace": "open-p", "hash": -1128375709, "terms": ["Where can I find the API documentation for predicate search?", "can I find the API documentation for predicate search?", "I find the API documentation for predicate search?", "find the API documentation for predicate search?", "the API documentation for predicate search?", "API documentation for predicate search?", "documentation for predicate search?", "for predicate search?", "predicate search?", "search?"], "tokens": ["Where", "can", "I", "find", "the", "API", "documentation", "for", "predicate", "search", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1350902180", "fields": {"term": "How can I add a dependency for predicate-search?", "namespace": "open-p", "hash": 1350902180, "terms": ["How can I add a dependency for predicate-search?", "can I add a dependency for predicate-search?", "I add a dependency for predicate-search?", "add a dependency for predicate-search?", "a dependency for predicate-search?", "dependency for predicate-search?", "for predicate-search?", "predicate-search?"], "tokens": ["How", "can", "I", "add", "a", "dependency", "for", "predicate", "search", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::808159183", "fields": {"term": "What is the Java library?", "namespace": "open-p", "hash": 808159183, "terms": ["What is the Java library?", "is the Java library?", "the Java library?", "Java library?", "library?"], "tokens": ["What", "is", "the", "Java", "library", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1658229385", "fields": {"term": "How do you build an index?", "namespace": "open-p", "hash": -1658229385, "terms": ["How do you build an index?", "do you build an index?", "you build an index?", "build an index?", "an index?", "index?"], "tokens": ["How", "do", "you", "build", "an", "index", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::730408936", "fields": {"term": "What is PredicateIndexBuilder?", "namespace": "open-p", "hash": 730408936, "terms": ["What is PredicateIndexBuilder?", "is PredicateIndexBuilder?", "PredicateIndexBuilder?"], "tokens": ["What", "is", "PredicateIndexBuilder", "?"], "corpus_count": 3, "document_count": 3}}, -{"put": "id:term:term::-1056520509", "fields": {"term": "What is a PredicateIndexBuilder?", "namespace": "open-p", "hash": -1056520509, "terms": ["What is a PredicateIndexBuilder?", "is a PredicateIndexBuilder?", "a PredicateIndexBuilder?", "PredicateIndexBuilder?"], "tokens": ["What", "is", "a", "PredicateIndexBuilder", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1293511071", "fields": {"term": "How do I make the index more efficient?", "namespace": "open-p", "hash": 1293511071, "terms": ["How do I make the index more efficient?", "do I make the index more efficient?", "I make the index more efficient?", "make the index more efficient?", "the index more efficient?", "index more efficient?", "more efficient?", "efficient?"], "tokens": ["How", "do", "I", "make", "the", "index", "more", "efficient", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1269777799", "fields": {"term": "What are the upper and lower bounds?", "namespace": "open-p", "hash": 1269777799, "terms": ["What are the upper and lower bounds?", "are the upper and lower bounds?", "the upper and lower bounds?", "upper and lower bounds?", "and lower bounds?", "lower bounds?", "bounds?"], "tokens": ["What", "are", "the", "upper", "and", "lower", "bounds", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-666717008", "fields": {"term": "Can the bounds be changed?", "namespace": "open-p", "hash": -666717008, "terms": ["Can the bounds be changed?", "the bounds be changed?", "bounds be changed?", "be changed?", "changed?"], "tokens": ["Can", "the", "bounds", "be", "changed", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2052494659", "fields": {"term": "Where can I find more information on configuration parameters?", "namespace": "open-p", "hash": 2052494659, "terms": ["Where can I find more information on configuration parameters?", "can I find more information on configuration parameters?", "I find more information on configuration parameters?", "find more information on configuration parameters?", "more information on configuration parameters?", "information on configuration parameters?", "on configuration parameters?", "configuration parameters?", "parameters?"], "tokens": ["Where", "can", "I", "find", "more", "information", "on", "configuration", "parameters", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-863951556", "fields": {"term": "What is PredicateIndex?", "namespace": "open-p", "hash": -863951556, "terms": ["What is PredicateIndex?", "is PredicateIndex?", "PredicateIndex?"], "tokens": ["What", "is", "PredicateIndex", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::-853819767", "fields": {"term": "How to serialize PredicateIndex?", "namespace": "open-p", "hash": -853819767, "terms": ["How to serialize PredicateIndex?", "to serialize PredicateIndex?", "serialize PredicateIndex?", "PredicateIndex?"], "tokens": ["How", "to", "serialize", "PredicateIndex", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::932388086", "fields": {"term": "How to deserialize PredicateIndex?", "namespace": "open-p", "hash": 932388086, "terms": ["How to deserialize PredicateIndex?", "to deserialize PredicateIndex?", "deserialize PredicateIndex?", "PredicateIndex?"], "tokens": ["How", "to", "deserialize", "PredicateIndex", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-976657237", "fields": {"term": "What is the advantage of deserializing an index?", "namespace": "open-p", "hash": -976657237, "terms": ["What is the advantage of deserializing an index?", "is the advantage of deserializing an index?", "the advantage of deserializing an index?", "advantage of deserializing an index?", "of deserializing an index?", "deserializing an index?", "an index?", "index?"], "tokens": ["What", "is", "the", "advantage", "of", "deserializing", "an", "index", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2038149106", "fields": {"term": "How to create a new index?", "namespace": "open-p", "hash": 2038149106, "terms": ["How to create a new index?", "to create a new index?", "create a new index?", "a new index?", "new index?", "index?"], "tokens": ["How", "to", "create", "a", "new", "index", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1631080908", "fields": {"term": "What is the speed difference between deserializing and creating a new index?", "namespace": "open-p", "hash": -1631080908, "terms": ["What is the speed difference between deserializing and creating a new index?", "is the speed difference between deserializing and creating a new index?", "the speed difference between deserializing and creating a new index?", "speed difference between deserializing and creating a new index?", "difference between deserializing and creating a new index?", "between deserializing and creating a new index?", "deserializing and creating a new index?", "and creating a new index?", "creating a new index?", "a new index?", "new index?", "index?"], "tokens": ["What", "is", "the", "speed", "difference", "between", "deserializing", "and", "creating", "a", "new", "index", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1143974472", "fields": {"term": "What is the method of PredicateIndex?", "namespace": "open-p", "hash": 1143974472, "terms": ["What is the method of PredicateIndex?", "is the method of PredicateIndex?", "the method of PredicateIndex?", "method of PredicateIndex?", "of PredicateIndex?", "PredicateIndex?"], "tokens": ["What", "is", "the", "method", "of", "PredicateIndex", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::413969396", "fields": {"term": "What is the search method of the searcher?", "namespace": "open-p", "hash": 413969396, "terms": ["What is the search method of the searcher?", "is the search method of the searcher?", "the search method of the searcher?", "search method of the searcher?", "method of the searcher?", "of the searcher?", "the searcher?", "searcher?"], "tokens": ["What", "is", "the", "search", "method", "of", "the", "searcher", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1120238154", "fields": {"term": "Is the index thread-safe?", "namespace": "open-p", "hash": -1120238154, "terms": ["Is the index thread-safe?", "the index thread-safe?", "index thread-safe?", "thread-safe?"], "tokens": ["Is", "the", "index", "thread", "safe", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-455384971", "fields": {"term": "Why should we create separate searcher objects for each thread?", "namespace": "open-p", "hash": -455384971, "terms": ["Why should we create separate searcher objects for each thread?", "should we create separate searcher objects for each thread?", "we create separate searcher objects for each thread?", "create separate searcher objects for each thread?", "separate searcher objects for each thread?", "searcher objects for each thread?", "objects for each thread?", "for each thread?", "each thread?", "thread?"], "tokens": ["Why", "should", "we", "create", "separate", "searcher", "objects", "for", "each", "thread", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::200810425", "fields": {"term": "What is a PredicateQuery?", "namespace": "open-p", "hash": 200810425, "terms": ["What is a PredicateQuery?", "is a PredicateQuery?", "a PredicateQuery?", "PredicateQuery?"], "tokens": ["What", "is", "a", "PredicateQuery", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1366932772", "fields": {"term": "What are range features in a query?", "namespace": "open-p", "hash": 1366932772, "terms": ["What are range features in a query?", "are range features in a query?", "range features in a query?", "features in a query?", "in a query?", "a query?", "query?"], "tokens": ["What", "are", "range", "features", "in", "a", "query", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1904025736", "fields": {"term": "What is a sub-query bitmap?", "namespace": "open-p", "hash": -1904025736, "terms": ["What is a sub-query bitmap?", "is a sub-query bitmap?", "a sub-query bitmap?", "sub-query bitmap?", "bitmap?"], "tokens": ["What", "is", "a", "sub", "query", "bitmap", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::326478975", "fields": {"term": "What does search() method return?", "namespace": "open-p", "hash": 326478975, "terms": ["What does search() method return?", "does search() method return?", "search() method return?", "method return?", "return?"], "tokens": ["What", "does", "search", "()", "method", "return", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::452803379", "fields": {"term": "What is a Hit object?", "namespace": "open-p", "hash": 452803379, "terms": ["What is a Hit object?", "is a Hit object?", "a Hit object?", "Hit object?", "object?"], "tokens": ["What", "is", "a", "Hit", "object", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1099255150", "fields": {"term": "What does sub-query bitmap indicate?", "namespace": "open-p", "hash": 1099255150, "terms": ["What does sub-query bitmap indicate?", "does sub-query bitmap indicate?", "sub-query bitmap indicate?", "bitmap indicate?", "indicate?"], "tokens": ["What", "does", "sub", "query", "bitmap", "indicate", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2058484666", "fields": {"term": "What impacts Vespa performance?", "namespace": "open-p", "hash": -2058484666, "terms": ["What impacts Vespa performance?", "impacts Vespa performance?", "Vespa performance?", "performance?"], "tokens": ["What", "impacts", "Vespa", "performance", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1332399000", "fields": {"term": "What is posting list cache?", "namespace": "open-p", "hash": 1332399000, "terms": ["What is posting list cache?", "is posting list cache?", "posting list cache?", "list cache?", "cache?"], "tokens": ["What", "is", "posting", "list", "cache", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-250937209", "fields": {"term": "How do you build posting list cache?", "namespace": "open-p", "hash": -250937209, "terms": ["How do you build posting list cache?", "do you build posting list cache?", "you build posting list cache?", "build posting list cache?", "posting list cache?", "list cache?", "cache?"], "tokens": ["How", "do", "you", "build", "posting", "list", "cache", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-326581334", "fields": {"term": "Is cache enabled by default?", "namespace": "open-p", "hash": -326581334, "terms": ["Is cache enabled by default?", "cache enabled by default?", "enabled by default?", "by default?", "default?"], "tokens": ["Is", "cache", "enabled", "by", "default", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-616228431", "fields": {"term": "Is cache rebuild thread-safe?", "namespace": "open-p", "hash": -616228431, "terms": ["Is cache rebuild thread-safe?", "cache rebuild thread-safe?", "rebuild thread-safe?", "thread-safe?"], "tokens": ["Is", "cache", "rebuild", "thread", "safe", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1223344090", "fields": {"term": "What is the cache based on?", "namespace": "open-p", "hash": 1223344090, "terms": ["What is the cache based on?", "is the cache based on?", "the cache based on?", "cache based on?", "based on?", "on?"], "tokens": ["What", "is", "the", "cache", "based", "on", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1041052569", "fields": {"term": "What is PredicateQuery?", "namespace": "open-p", "hash": -1041052569, "terms": ["What is PredicateQuery?", "is PredicateQuery?", "PredicateQuery?"], "tokens": ["What", "is", "PredicateQuery", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::681482393", "fields": {"term": "What is hitStream?", "namespace": "open-p", "hash": 681482393, "terms": ["What is hitStream?", "is hitStream?", "hitStream?"], "tokens": ["What", "is", "hitStream", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1630084644", "fields": {"term": "What is deserializedIndex?", "namespace": "open-p", "hash": 1630084644, "terms": ["What is deserializedIndex?", "is deserializedIndex?", "deserializedIndex?"], "tokens": ["What", "is", "deserializedIndex", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-750434177", "fields": {"term": "What is baos?", "namespace": "open-p", "hash": -750434177, "terms": ["What is baos?", "is baos?", "baos?"], "tokens": ["What", "is", "baos", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1870730029", "fields": {"term": "What is a bucket?", "namespace": "open-p", "hash": -1870730029, "terms": ["What is a bucket?", "is a bucket?", "a bucket?", "bucket?"], "tokens": ["What", "is", "a", "bucket", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1913054547", "fields": {"term": "How are documents mapped to buckets?", "namespace": "open-p", "hash": -1913054547, "terms": ["How are documents mapped to buckets?", "are documents mapped to buckets?", "documents mapped to buckets?", "mapped to buckets?", "to buckets?", "buckets?"], "tokens": ["How", "are", "documents", "mapped", "to", "buckets", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::423048099", "fields": {"term": "How does the cluster maintain uniform distribution?", "namespace": "open-p", "hash": 423048099, "terms": ["How does the cluster maintain uniform distribution?", "does the cluster maintain uniform distribution?", "the cluster maintain uniform distribution?", "cluster maintain uniform distribution?", "maintain uniform distribution?", "uniform distribution?", "distribution?"], "tokens": ["How", "does", "the", "cluster", "maintain", "uniform", "distribution", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1929130296", "fields": {"term": "What is a bucket defined as?", "namespace": "open-p", "hash": -1929130296, "terms": ["What is a bucket defined as?", "is a bucket defined as?", "a bucket defined as?", "bucket defined as?", "defined as?", "as?"], "tokens": ["What", "is", "a", "bucket", "defined", "as", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::702121141", "fields": {"term": "How are buckets split?", "namespace": "open-p", "hash": 702121141, "terms": ["How are buckets split?", "are buckets split?", "buckets split?", "split?"], "tokens": ["How", "are", "buckets", "split", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::1819504920", "fields": {"term": "How are buckets joined?", "namespace": "open-p", "hash": 1819504920, "terms": ["How are buckets joined?", "are buckets joined?", "buckets joined?", "joined?"], "tokens": ["How", "are", "buckets", "joined", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-123008268", "fields": {"term": "What are the layers of distribution?", "namespace": "open-p", "hash": -123008268, "terms": ["What are the layers of distribution?", "are the layers of distribution?", "the layers of distribution?", "layers of distribution?", "of distribution?", "distribution?"], "tokens": ["What", "are", "the", "layers", "of", "distribution", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::965547631", "fields": {"term": "What are document identifier schemes?", "namespace": "open-p", "hash": 965547631, "terms": ["What are document identifier schemes?", "are document identifier schemes?", "document identifier schemes?", "identifier schemes?", "schemes?"], "tokens": ["What", "are", "document", "identifier", "schemes", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::354461427", "fields": {"term": "What is the purpose of document identifiers?", "namespace": "open-p", "hash": 354461427, "terms": ["What is the purpose of document identifiers?", "is the purpose of document identifiers?", "the purpose of document identifiers?", "purpose of document identifiers?", "of document identifiers?", "document identifiers?", "identifiers?"], "tokens": ["What", "is", "the", "purpose", "of", "document", "identifiers", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1334567634", "fields": {"term": "How are data co-located within buckets?", "namespace": "open-p", "hash": -1334567634, "terms": ["How are data co-located within buckets?", "are data co-located within buckets?", "data co-located within buckets?", "co-located within buckets?", "within buckets?", "buckets?"], "tokens": ["How", "are", "data", "co", "located", "within", "buckets", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1677827224", "fields": {"term": "What are the options for specifying groups or numeric values?", "namespace": "open-p", "hash": -1677827224, "terms": ["What are the options for specifying groups or numeric values?", "are the options for specifying groups or numeric values?", "the options for specifying groups or numeric values?", "options for specifying groups or numeric values?", "for specifying groups or numeric values?", "specifying groups or numeric values?", "groups or numeric values?", "or numeric values?", "numeric values?", "values?"], "tokens": ["What", "are", "the", "options", "for", "specifying", "groups", "or", "numeric", "values", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1557045176", "fields": {"term": "When should you use document identifiers?", "namespace": "open-p", "hash": 1557045176, "terms": ["When should you use document identifiers?", "should you use document identifiers?", "you use document identifiers?", "use document identifiers?", "document identifiers?", "identifiers?"], "tokens": ["When", "should", "you", "use", "document", "identifiers", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-928729258", "fields": {"term": "What is the use of LSB bits for document identifiers?", "namespace": "open-p", "hash": -928729258, "terms": ["What is the use of LSB bits for document identifiers?", "is the use of LSB bits for document identifiers?", "the use of LSB bits for document identifiers?", "use of LSB bits for document identifiers?", "of LSB bits for document identifiers?", "LSB bits for document identifiers?", "bits for document identifiers?", "for document identifiers?", "document identifiers?", "identifiers?"], "tokens": ["What", "is", "the", "use", "of", "LSB", "bits", "for", "document", "identifiers", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1254602180", "fields": {"term": "What is the relationship between document identifiers and location?", "namespace": "open-p", "hash": 1254602180, "terms": ["What is the relationship between document identifiers and location?", "is the relationship between document identifiers and location?", "the relationship between document identifiers and location?", "relationship between document identifiers and location?", "between document identifiers and location?", "document identifiers and location?", "identifiers and location?", "and location?", "location?"], "tokens": ["What", "is", "the", "relationship", "between", "document", "identifiers", "and", "location", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1149417252", "fields": {"term": "What is a distribution bit count?", "namespace": "open-p", "hash": -1149417252, "terms": ["What is a distribution bit count?", "is a distribution bit count?", "a distribution bit count?", "distribution bit count?", "bit count?", "count?"], "tokens": ["What", "is", "a", "distribution", "bit", "count", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1302952905", "fields": {"term": "How does altering distribution bit count affect buckets?", "namespace": "open-p", "hash": 1302952905, "terms": ["How does altering distribution bit count affect buckets?", "does altering distribution bit count affect buckets?", "altering distribution bit count affect buckets?", "distribution bit count affect buckets?", "bit count affect buckets?", "count affect buckets?", "affect buckets?", "buckets?"], "tokens": ["How", "does", "altering", "distribution", "bit", "count", "affect", "buckets", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::750872309", "fields": {"term": "What are buckets in Vespa?", "namespace": "open-p", "hash": 750872309, "terms": ["What are buckets in Vespa?", "are buckets in Vespa?", "buckets in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["What", "are", "buckets", "in", "Vespa", "?"], "corpus_count": 3, "document_count": 3}}, -{"put": "id:term:term::1149931292", "fields": {"term": "What is the ideal state algorithm?", "namespace": "open-p", "hash": 1149931292, "terms": ["What is the ideal state algorithm?", "is the ideal state algorithm?", "the ideal state algorithm?", "ideal state algorithm?", "state algorithm?", "algorithm?"], "tokens": ["What", "is", "the", "ideal", "state", "algorithm", "?"], "corpus_count": 5, "document_count": 5}}, -{"put": "id:term:term::-94272106", "fields": {"term": "How does bucket ownership change?", "namespace": "open-p", "hash": -94272106, "terms": ["How does bucket ownership change?", "does bucket ownership change?", "bucket ownership change?", "ownership change?", "change?"], "tokens": ["How", "does", "bucket", "ownership", "change", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-4145169", "fields": {"term": "What is bucket replica?", "namespace": "open-p", "hash": -4145169, "terms": ["What is bucket replica?", "is bucket replica?", "bucket replica?", "replica?"], "tokens": ["What", "is", "bucket", "replica", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-864495443", "fields": {"term": "What is the distribution bit count?", "namespace": "open-p", "hash": -864495443, "terms": ["What is the distribution bit count?", "is the distribution bit count?", "the distribution bit count?", "distribution bit count?", "bit count?", "count?"], "tokens": ["What", "is", "the", "distribution", "bit", "count", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1978842291", "fields": {"term": "What are maintenance operations?", "namespace": "open-p", "hash": 1978842291, "terms": ["What are maintenance operations?", "are maintenance operations?", "maintenance operations?", "operations?"], "tokens": ["What", "are", "maintenance", "operations", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1337911475", "fields": {"term": "What is split bucket?", "namespace": "open-p", "hash": 1337911475, "terms": ["What is split bucket?", "is split bucket?", "split bucket?", "bucket?"], "tokens": ["What", "is", "split", "bucket", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2109753892", "fields": {"term": "What is merge bucket?", "namespace": "open-p", "hash": 2109753892, "terms": ["What is merge bucket?", "is merge bucket?", "merge bucket?", "bucket?"], "tokens": ["What", "is", "merge", "bucket", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1383033270", "fields": {"term": "What is create bucket?", "namespace": "open-p", "hash": -1383033270, "terms": ["What is create bucket?", "is create bucket?", "create bucket?", "bucket?"], "tokens": ["What", "is", "create", "bucket", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::178620261", "fields": {"term": "What is the delete bucket?", "namespace": "open-p", "hash": 178620261, "terms": ["What is the delete bucket?", "is the delete bucket?", "the delete bucket?", "delete bucket?", "bucket?"], "tokens": ["What", "is", "the", "delete", "bucket", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::563235047", "fields": {"term": "What is bucket splitting?", "namespace": "open-p", "hash": 563235047, "terms": ["What is bucket splitting?", "is bucket splitting?", "bucket splitting?", "splitting?"], "tokens": ["What", "is", "bucket", "splitting", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::651584550", "fields": {"term": "Why would a distributor split buckets?", "namespace": "open-p", "hash": 651584550, "terms": ["Why would a distributor split buckets?", "would a distributor split buckets?", "a distributor split buckets?", "distributor split buckets?", "split buckets?", "buckets?"], "tokens": ["Why", "would", "a", "distributor", "split", "buckets", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::852013484", "fields": {"term": "What is the benefit of using small buckets?", "namespace": "open-p", "hash": 852013484, "terms": ["What is the benefit of using small buckets?", "is the benefit of using small buckets?", "the benefit of using small buckets?", "benefit of using small buckets?", "of using small buckets?", "using small buckets?", "small buckets?", "buckets?"], "tokens": ["What", "is", "the", "benefit", "of", "using", "small", "buckets", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::56086618", "fields": {"term": "What is the benefit of using large buckets?", "namespace": "open-p", "hash": 56086618, "terms": ["What is the benefit of using large buckets?", "is the benefit of using large buckets?", "the benefit of using large buckets?", "benefit of using large buckets?", "of using large buckets?", "using large buckets?", "large buckets?", "buckets?"], "tokens": ["What", "is", "the", "benefit", "of", "using", "large", "buckets", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-348236116", "fields": {"term": "What is a document identifier?", "namespace": "open-p", "hash": -348236116, "terms": ["What is a document identifier?", "is a document identifier?", "a document identifier?", "document identifier?", "identifier?"], "tokens": ["What", "is", "a", "document", "identifier", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::742547822", "fields": {"term": "How is location generated?", "namespace": "open-p", "hash": 742547822, "terms": ["How is location generated?", "is location generated?", "location generated?", "generated?"], "tokens": ["How", "is", "location", "generated", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2081271233", "fields": {"term": "What is a groupname?", "namespace": "open-p", "hash": -2081271233, "terms": ["What is a groupname?", "is a groupname?", "a groupname?", "groupname?"], "tokens": ["What", "is", "a", "groupname", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1813521088", "fields": {"term": "What is the MD5 checksum?", "namespace": "open-p", "hash": -1813521088, "terms": ["What is the MD5 checksum?", "is the MD5 checksum?", "the MD5 checksum?", "MD5 checksum?", "checksum?"], "tokens": ["What", "is", "the", "MD5", "checksum", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1446196671", "fields": {"term": "What are the use cases for co-locating documents?", "namespace": "open-p", "hash": -1446196671, "terms": ["What are the use cases for co-locating documents?", "are the use cases for co-locating documents?", "the use cases for co-locating documents?", "use cases for co-locating documents?", "cases for co-locating documents?", "for co-locating documents?", "co-locating documents?", "documents?"], "tokens": ["What", "are", "the", "use", "cases", "for", "co", "locating", "documents", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::641858559", "fields": {"term": "What happens when the 'n=' option is specified?", "namespace": "open-p", "hash": 641858559, "terms": ["What happens when the 'n=' option is specified?", "happens when the 'n=' option is specified?", "when the 'n=' option is specified?", "the 'n=' option is specified?", "'n=' option is specified?", "option is specified?", "is specified?", "specified?"], "tokens": ["What", "happens", "when", "the", "'", "n", "='", "option", "is", "specified", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1396498990", "fields": {"term": "How do distributors map locations to buckets?", "namespace": "open-p", "hash": 1396498990, "terms": ["How do distributors map locations to buckets?", "do distributors map locations to buckets?", "distributors map locations to buckets?", "map locations to buckets?", "locations to buckets?", "to buckets?", "buckets?"], "tokens": ["How", "do", "distributors", "map", "locations", "to", "buckets", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-89095698", "fields": {"term": "Do content nodes need to map documents to buckets?", "namespace": "open-p", "hash": -89095698, "terms": ["Do content nodes need to map documents to buckets?", "content nodes need to map documents to buckets?", "nodes need to map documents to buckets?", "need to map documents to buckets?", "to map documents to buckets?", "map documents to buckets?", "documents to buckets?", "to buckets?", "buckets?"], "tokens": ["Do", "content", "nodes", "need", "to", "map", "documents", "to", "buckets", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1884334699", "fields": {"term": "What is the limitation of document to location mapping?", "namespace": "open-p", "hash": -1884334699, "terms": ["What is the limitation of document to location mapping?", "is the limitation of document to location mapping?", "the limitation of document to location mapping?", "limitation of document to location mapping?", "of document to location mapping?", "document to location mapping?", "to location mapping?", "location mapping?", "mapping?"], "tokens": ["What", "is", "the", "limitation", "of", "document", "to", "location", "mapping", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::322312135", "fields": {"term": "What happens if the document to location mapping changes?", "namespace": "open-p", "hash": 322312135, "terms": ["What happens if the document to location mapping changes?", "happens if the document to location mapping changes?", "if the document to location mapping changes?", "the document to location mapping changes?", "document to location mapping changes?", "to location mapping changes?", "location mapping changes?", "mapping changes?", "changes?"], "tokens": ["What", "happens", "if", "the", "document", "to", "location", "mapping", "changes", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-918256527", "fields": {"term": "Can document identifier schemes be extended?", "namespace": "open-p", "hash": -918256527, "terms": ["Can document identifier schemes be extended?", "document identifier schemes be extended?", "identifier schemes be extended?", "schemes be extended?", "be extended?", "extended?"], "tokens": ["Can", "document", "identifier", "schemes", "be", "extended", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-685518674", "fields": {"term": "How are new document identifier schemes created?", "namespace": "open-p", "hash": -685518674, "terms": ["How are new document identifier schemes created?", "are new document identifier schemes created?", "new document identifier schemes created?", "document identifier schemes created?", "identifier schemes created?", "schemes created?", "created?"], "tokens": ["How", "are", "new", "document", "identifier", "schemes", "created", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2013743103", "fields": {"term": "What is the purpose of the 32 least significant bits in document identifier schemes?", "namespace": "open-p", "hash": 2013743103, "terms": ["What is the purpose of the 32 least significant bits in document identifier schemes?", "is the purpose of the 32 least significant bits in document identifier schemes?", "the purpose of the 32 least significant bits in document identifier schemes?", "purpose of the 32 least significant bits in document identifier schemes?", "of the 32 least significant bits in document identifier schemes?", "the 32 least significant bits in document identifier schemes?", "32 least significant bits in document identifier schemes?", "least significant bits in document identifier schemes?", "significant bits in document identifier schemes?", "bits in document identifier schemes?", "in document identifier schemes?", "document identifier schemes?", "identifier schemes?", "schemes?"], "tokens": ["What", "is", "the", "purpose", "of", "the", "32", "least", "significant", "bits", "in", "document", "identifier", "schemes", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-135035238", "fields": {"term": "How many bits are reserved for the MD5 checksum in document identifier schemes?", "namespace": "open-p", "hash": -135035238, "terms": ["How many bits are reserved for the MD5 checksum in document identifier schemes?", "many bits are reserved for the MD5 checksum in document identifier schemes?", "bits are reserved for the MD5 checksum in document identifier schemes?", "are reserved for the MD5 checksum in document identifier schemes?", "reserved for the MD5 checksum in document identifier schemes?", "for the MD5 checksum in document identifier schemes?", "the MD5 checksum in document identifier schemes?", "MD5 checksum in document identifier schemes?", "checksum in document identifier schemes?", "in document identifier schemes?", "document identifier schemes?", "identifier schemes?", "schemes?"], "tokens": ["How", "many", "bits", "are", "reserved", "for", "the", "MD5", "checksum", "in", "document", "identifier", "schemes", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1154437964", "fields": {"term": "What happens when a bucket is split?", "namespace": "open-p", "hash": 1154437964, "terms": ["What happens when a bucket is split?", "happens when a bucket is split?", "when a bucket is split?", "a bucket is split?", "bucket is split?", "is split?", "split?"], "tokens": ["What", "happens", "when", "a", "bucket", "is", "split", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-860147421", "fields": {"term": "Why does co-localization enable parallel processing?", "namespace": "open-p", "hash": -860147421, "terms": ["Why does co-localization enable parallel processing?", "does co-localization enable parallel processing?", "co-localization enable parallel processing?", "enable parallel processing?", "parallel processing?", "processing?"], "tokens": ["Why", "does", "co", "localization", "enable", "parallel", "processing", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-788658620", "fields": {"term": "What are buckets?", "namespace": "open-p", "hash": -788658620, "terms": ["What are buckets?", "are buckets?", "buckets?"], "tokens": ["What", "are", "buckets", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-250870162", "fields": {"term": "Where do buckets exist?", "namespace": "open-p", "hash": -250870162, "terms": ["Where do buckets exist?", "do buckets exist?", "buckets exist?", "exist?"], "tokens": ["Where", "do", "buckets", "exist", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1058906246", "fields": {"term": "What version of Java and Maven is needed to build Vespa artifacts?", "namespace": "open-p", "hash": 1058906246, "terms": ["What version of Java and Maven is needed to build Vespa artifacts?", "version of Java and Maven is needed to build Vespa artifacts?", "of Java and Maven is needed to build Vespa artifacts?", "Java and Maven is needed to build Vespa artifacts?", "and Maven is needed to build Vespa artifacts?", "Maven is needed to build Vespa artifacts?", "is needed to build Vespa artifacts?", "needed to build Vespa artifacts?", "to build Vespa artifacts?", "build Vespa artifacts?", "Vespa artifacts?", "artifacts?"], "tokens": ["What", "version", "of", "Java", "and", "Maven", "is", "needed", "to", "build", "Vespa", "artifacts", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1663289900", "fields": {"term": "How to build a Vespa application?", "namespace": "open-p", "hash": -1663289900, "terms": ["How to build a Vespa application?", "to build a Vespa application?", "build a Vespa application?", "a Vespa application?", "Vespa application?", "application?"], "tokens": ["How", "to", "build", "a", "Vespa", "application", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-842226071", "fields": {"term": "Where can the Vespa releases be found?", "namespace": "open-p", "hash": -842226071, "terms": ["Where can the Vespa releases be found?", "can the Vespa releases be found?", "the Vespa releases be found?", "Vespa releases be found?", "releases be found?", "be found?", "found?"], "tokens": ["Where", "can", "the", "Vespa", "releases", "be", "found", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::773566771", "fields": {"term": "What is vespaengine/vespa image?", "namespace": "open-p", "hash": 773566771, "terms": ["What is vespaengine/vespa image?", "is vespaengine/vespa image?", "vespaengine/vespa image?", "image?"], "tokens": ["What", "is", "vespaengine", "/", "vespa", "image", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1732487784", "fields": {"term": "What is vespa-build-centos-stream8?", "namespace": "open-p", "hash": -1732487784, "terms": ["What is vespa-build-centos-stream8?", "is vespa-build-centos-stream8?", "vespa-build-centos-stream8?"], "tokens": ["What", "is", "vespa", "build", "centos", "stream8", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::366486697", "fields": {"term": "What is vespa-dev-centos-stream8?", "namespace": "open-p", "hash": 366486697, "terms": ["What is vespa-dev-centos-stream8?", "is vespa-dev-centos-stream8?", "vespa-dev-centos-stream8?"], "tokens": ["What", "is", "vespa", "dev", "centos", "stream8", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-715534080", "fields": {"term": "How to install Vespa?", "namespace": "open-p", "hash": -715534080, "terms": ["How to install Vespa?", "to install Vespa?", "install Vespa?", "Vespa?"], "tokens": ["How", "to", "install", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1770709186", "fields": {"term": "What are the RPMs for Vespa?", "namespace": "open-p", "hash": 1770709186, "terms": ["What are the RPMs for Vespa?", "are the RPMs for Vespa?", "the RPMs for Vespa?", "RPMs for Vespa?", "for Vespa?", "Vespa?"], "tokens": ["What", "are", "the", "RPMs", "for", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::119266213", "fields": {"term": "What is vespa-malloc?", "namespace": "open-p", "hash": 119266213, "terms": ["What is vespa-malloc?", "is vespa-malloc?", "vespa-malloc?"], "tokens": ["What", "is", "vespa", "malloc", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::465187301", "fields": {"term": "What is OSGi?", "namespace": "open-p", "hash": 465187301, "terms": ["What is OSGi?", "is OSGi?", "OSGi?"], "tokens": ["What", "is", "OSGi", "?"], "corpus_count": 4, "document_count": 4}}, -{"put": "id:term:term::1957981564", "fields": {"term": "What are OSGi bundles?", "namespace": "open-p", "hash": 1957981564, "terms": ["What are OSGi bundles?", "are OSGi bundles?", "OSGi bundles?", "bundles?"], "tokens": ["What", "are", "OSGi", "bundles", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1406979837", "fields": {"term": "What is a bundle's public API?", "namespace": "open-p", "hash": 1406979837, "terms": ["What is a bundle's public API?", "is a bundle's public API?", "a bundle's public API?", "bundle's public API?", "public API?", "API?"], "tokens": ["What", "is", "a", "bundle", "'", "s", "public", "API", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1339106116", "fields": {"term": "How does OSGi enable modular development?", "namespace": "open-p", "hash": 1339106116, "terms": ["How does OSGi enable modular development?", "does OSGi enable modular development?", "OSGi enable modular development?", "enable modular development?", "modular development?", "development?"], "tokens": ["How", "does", "OSGi", "enable", "modular", "development", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1917729288", "fields": {"term": "What is the recommended reading for OSGi?", "namespace": "open-p", "hash": 1917729288, "terms": ["What is the recommended reading for OSGi?", "is the recommended reading for OSGi?", "the recommended reading for OSGi?", "recommended reading for OSGi?", "reading for OSGi?", "for OSGi?", "OSGi?"], "tokens": ["What", "is", "the", "recommended", "reading", "for", "OSGi", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-459723444", "fields": {"term": "What layers of OSGi does JDisc use?", "namespace": "open-p", "hash": -459723444, "terms": ["What layers of OSGi does JDisc use?", "layers of OSGi does JDisc use?", "of OSGi does JDisc use?", "OSGi does JDisc use?", "does JDisc use?", "JDisc use?", "use?"], "tokens": ["What", "layers", "of", "OSGi", "does", "JDisc", "use", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1728147010", "fields": {"term": "What is an OSGi bundle?", "namespace": "open-p", "hash": 1728147010, "terms": ["What is an OSGi bundle?", "is an OSGi bundle?", "an OSGi bundle?", "OSGi bundle?", "bundle?"], "tokens": ["What", "is", "an", "OSGi", "bundle", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::-1867696247", "fields": {"term": "What is a bundle manifest?", "namespace": "open-p", "hash": -1867696247, "terms": ["What is a bundle manifest?", "is a bundle manifest?", "a bundle manifest?", "bundle manifest?", "manifest?"], "tokens": ["What", "is", "a", "bundle", "manifest", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::124101940", "fields": {"term": "What is Bundle-SymbolicName?", "namespace": "open-p", "hash": 124101940, "terms": ["What is Bundle-SymbolicName?", "is Bundle-SymbolicName?", "Bundle-SymbolicName?"], "tokens": ["What", "is", "Bundle", "SymbolicName", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1562034608", "fields": {"term": "What is Bundle-Version?", "namespace": "open-p", "hash": -1562034608, "terms": ["What is Bundle-Version?", "is Bundle-Version?", "Bundle-Version?"], "tokens": ["What", "is", "Bundle", "Version", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::211306642", "fields": {"term": "What is Export-Package?", "namespace": "open-p", "hash": 211306642, "terms": ["What is Export-Package?", "is Export-Package?", "Export-Package?"], "tokens": ["What", "is", "Export", "Package", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1448052137", "fields": {"term": "What is Import-Package?", "namespace": "open-p", "hash": -1448052137, "terms": ["What is Import-Package?", "is Import-Package?", "Import-Package?"], "tokens": ["What", "is", "Import", "Package", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1136561486", "fields": {"term": "What does Bundle-Description contain?", "namespace": "open-p", "hash": 1136561486, "terms": ["What does Bundle-Description contain?", "does Bundle-Description contain?", "Bundle-Description contain?", "contain?"], "tokens": ["What", "does", "Bundle", "Description", "contain", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1793924125", "fields": {"term": "What are the headers in a bundle manifest?", "namespace": "open-p", "hash": 1793924125, "terms": ["What are the headers in a bundle manifest?", "are the headers in a bundle manifest?", "the headers in a bundle manifest?", "headers in a bundle manifest?", "in a bundle manifest?", "a bundle manifest?", "bundle manifest?", "manifest?"], "tokens": ["What", "are", "the", "headers", "in", "a", "bundle", "manifest", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-252235823", "fields": {"term": "How to package a Maven project?", "namespace": "open-p", "hash": -252235823, "terms": ["How to package a Maven project?", "to package a Maven project?", "package a Maven project?", "a Maven project?", "Maven project?", "project?"], "tokens": ["How", "to", "package", "a", "Maven", "project", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1047950835", "fields": {"term": "What is the container-plugin used for?", "namespace": "open-p", "hash": 1047950835, "terms": ["What is the container-plugin used for?", "is the container-plugin used for?", "the container-plugin used for?", "container-plugin used for?", "used for?", "for?"], "tokens": ["What", "is", "the", "container", "plugin", "used", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-277104977", "fields": {"term": "What is the bundle-plugin used for?", "namespace": "open-p", "hash": -277104977, "terms": ["What is the bundle-plugin used for?", "is the bundle-plugin used for?", "the bundle-plugin used for?", "bundle-plugin used for?", "used for?", "for?"], "tokens": ["What", "is", "the", "bundle", "plugin", "used", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::978897706", "fields": {"term": "How to debug ClassNotFoundException?", "namespace": "open-p", "hash": 978897706, "terms": ["How to debug ClassNotFoundException?", "to debug ClassNotFoundException?", "debug ClassNotFoundException?", "ClassNotFoundException?"], "tokens": ["How", "to", "debug", "ClassNotFoundException", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::544320265", "fields": {"term": "What is vespa-logfmt?", "namespace": "open-p", "hash": 544320265, "terms": ["What is vespa-logfmt?", "is vespa-logfmt?", "vespa-logfmt?"], "tokens": ["What", "is", "vespa", "logfmt", "?"], "corpus_count": 4, "document_count": 4}}, -{"put": "id:term:term::1647696850", "fields": {"term": "How to make libraries OSGi-compatible?", "namespace": "open-p", "hash": 1647696850, "terms": ["How to make libraries OSGi-compatible?", "to make libraries OSGi-compatible?", "make libraries OSGi-compatible?", "libraries OSGi-compatible?", "OSGi-compatible?"], "tokens": ["How", "to", "make", "libraries", "OSGi", "compatible", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1218331362", "fields": {"term": "What is the scope of problematic dependency?", "namespace": "open-p", "hash": -1218331362, "terms": ["What is the scope of problematic dependency?", "is the scope of problematic dependency?", "the scope of problematic dependency?", "scope of problematic dependency?", "of problematic dependency?", "problematic dependency?", "dependency?"], "tokens": ["What", "is", "the", "scope", "of", "problematic", "dependency", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-109750232", "fields": {"term": "What is the practical implication of the bundle plugin?", "namespace": "open-p", "hash": -109750232, "terms": ["What is the practical implication of the bundle plugin?", "is the practical implication of the bundle plugin?", "the practical implication of the bundle plugin?", "practical implication of the bundle plugin?", "implication of the bundle plugin?", "of the bundle plugin?", "the bundle plugin?", "bundle plugin?", "plugin?"], "tokens": ["What", "is", "the", "practical", "implication", "of", "the", "bundle", "plugin", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-673379094", "fields": {"term": "What is considered a self-contained JAR file?", "namespace": "open-p", "hash": -673379094, "terms": ["What is considered a self-contained JAR file?", "is considered a self-contained JAR file?", "considered a self-contained JAR file?", "a self-contained JAR file?", "self-contained JAR file?", "JAR file?", "file?"], "tokens": ["What", "is", "considered", "a", "self", "contained", "JAR", "file", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-8078853", "fields": {"term": "What is the `Bundle-ClassPath` instruction?", "namespace": "open-p", "hash": -8078853, "terms": ["What is the `Bundle-ClassPath` instruction?", "is the `Bundle-ClassPath` instruction?", "the `Bundle-ClassPath` instruction?", "`Bundle-ClassPath` instruction?", "instruction?"], "tokens": ["What", "is", "the", "`", "Bundle", "ClassPath", "`", "instruction", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1764628615", "fields": {"term": "What happens if a library depends on other installed files?", "namespace": "open-p", "hash": 1764628615, "terms": ["What happens if a library depends on other installed files?", "happens if a library depends on other installed files?", "if a library depends on other installed files?", "a library depends on other installed files?", "library depends on other installed files?", "depends on other installed files?", "on other installed files?", "other installed files?", "installed files?", "files?"], "tokens": ["What", "happens", "if", "a", "library", "depends", "on", "other", "installed", "files", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1939006358", "fields": {"term": "How does the `compile` scope affect the bundle plugin?", "namespace": "open-p", "hash": -1939006358, "terms": ["How does the `compile` scope affect the bundle plugin?", "does the `compile` scope affect the bundle plugin?", "the `compile` scope affect the bundle plugin?", "`compile` scope affect the bundle plugin?", "scope affect the bundle plugin?", "affect the bundle plugin?", "the bundle plugin?", "bundle plugin?", "plugin?"], "tokens": ["How", "does", "the", "`", "compile", "`", "scope", "affect", "the", "bundle", "plugin", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::520560294", "fields": {"term": "What are alternatives to native code?", "namespace": "open-p", "hash": 520560294, "terms": ["What are alternatives to native code?", "are alternatives to native code?", "alternatives to native code?", "to native code?", "native code?", "code?"], "tokens": ["What", "are", "alternatives", "to", "native", "code", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-941134649", "fields": {"term": "What are OSGi jars?", "namespace": "open-p", "hash": -941134649, "terms": ["What are OSGi jars?", "are OSGi jars?", "OSGi jars?", "jars?"], "tokens": ["What", "are", "OSGi", "jars", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-868928567", "fields": {"term": "Can OSGi jars contain .so files?", "namespace": "open-p", "hash": -868928567, "terms": ["Can OSGi jars contain .so files?", "OSGi jars contain .so files?", "jars contain .so files?", "contain .so files?", ".so files?", "files?"], "tokens": ["Can", "OSGi", "jars", "contain", "so", "files", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1624010947", "fields": {"term": "How are .so files loaded?", "namespace": "open-p", "hash": -1624010947, "terms": ["How are .so files loaded?", "are .so files loaded?", ".so files loaded?", "files loaded?", "loaded?"], "tokens": ["How", "are", "so", "files", "loaded", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-61589163", "fields": {"term": "Can multiple instances of .so files be loaded?", "namespace": "open-p", "hash": -61589163, "terms": ["Can multiple instances of .so files be loaded?", "multiple instances of .so files be loaded?", "instances of .so files be loaded?", "of .so files be loaded?", ".so files be loaded?", "files be loaded?", "be loaded?", "loaded?"], "tokens": ["Can", "multiple", "instances", "of", "so", "files", "be", "loaded", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-814602339", "fields": {"term": "Why is it not possible to hot swap a jar with .so files?", "namespace": "open-p", "hash": -814602339, "terms": ["Why is it not possible to hot swap a jar with .so files?", "is it not possible to hot swap a jar with .so files?", "it not possible to hot swap a jar with .so files?", "not possible to hot swap a jar with .so files?", "possible to hot swap a jar with .so files?", "to hot swap a jar with .so files?", "hot swap a jar with .so files?", "swap a jar with .so files?", "a jar with .so files?", "jar with .so files?", "with .so files?", ".so files?", "files?"], "tokens": ["Why", "is", "it", "not", "possible", "to", "hot", "swap", "a", "jar", "with", "so", "files", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1610709581", "fields": {"term": "What is the recommended way to package .so files with Java APIs?", "namespace": "open-p", "hash": -1610709581, "terms": ["What is the recommended way to package .so files with Java APIs?", "is the recommended way to package .so files with Java APIs?", "the recommended way to package .so files with Java APIs?", "recommended way to package .so files with Java APIs?", "way to package .so files with Java APIs?", "to package .so files with Java APIs?", "package .so files with Java APIs?", ".so files with Java APIs?", "files with Java APIs?", "with Java APIs?", "Java APIs?", "APIs?"], "tokens": ["What", "is", "the", "recommended", "way", "to", "package", "so", "files", "with", "Java", "APIs", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-347005981", "fields": {"term": "Why should .so files and Java APIs be packaged separately from other code?", "namespace": "open-p", "hash": -347005981, "terms": ["Why should .so files and Java APIs be packaged separately from other code?", "should .so files and Java APIs be packaged separately from other code?", ".so files and Java APIs be packaged separately from other code?", "files and Java APIs be packaged separately from other code?", "and Java APIs be packaged separately from other code?", "Java APIs be packaged separately from other code?", "APIs be packaged separately from other code?", "be packaged separately from other code?", "packaged separately from other code?", "separately from other code?", "from other code?", "other code?", "code?"], "tokens": ["Why", "should", "so", "files", "and", "Java", "APIs", "be", "packaged", "separately", "from", "other", "code", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2010171629", "fields": {"term": "How to apply new configuration when a jar with .so files is changed?", "namespace": "open-p", "hash": 2010171629, "terms": ["How to apply new configuration when a jar with .so files is changed?", "to apply new configuration when a jar with .so files is changed?", "apply new configuration when a jar with .so files is changed?", "new configuration when a jar with .so files is changed?", "configuration when a jar with .so files is changed?", "when a jar with .so files is changed?", "a jar with .so files is changed?", "jar with .so files is changed?", "with .so files is changed?", ".so files is changed?", "files is changed?", "is changed?", "changed?"], "tokens": ["How", "to", "apply", "new", "configuration", "when", "a", "jar", "with", "so", "files", "is", "changed", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-969045334", "fields": {"term": "What is a JNI dependency?", "namespace": "open-p", "hash": -969045334, "terms": ["What is a JNI dependency?", "is a JNI dependency?", "a JNI dependency?", "JNI dependency?", "dependency?"], "tokens": ["What", "is", "a", "JNI", "dependency", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::475916190", "fields": {"term": "How to export packages in Vespa?", "namespace": "open-p", "hash": 475916190, "terms": ["How to export packages in Vespa?", "to export packages in Vespa?", "export packages in Vespa?", "packages in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["How", "to", "export", "packages", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::357998807", "fields": {"term": "What is bundle-plugin in Vespa?", "namespace": "open-p", "hash": 357998807, "terms": ["What is bundle-plugin in Vespa?", "is bundle-plugin in Vespa?", "bundle-plugin in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["What", "is", "bundle", "plugin", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::37384535", "fields": {"term": "How to configure a bundle in Vespa?", "namespace": "open-p", "hash": 37384535, "terms": ["How to configure a bundle in Vespa?", "to configure a bundle in Vespa?", "configure a bundle in Vespa?", "a bundle in Vespa?", "bundle in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["How", "to", "configure", "a", "bundle", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1098624380", "fields": {"term": "What is bundle-plugin?", "namespace": "open-p", "hash": 1098624380, "terms": ["What is bundle-plugin?", "is bundle-plugin?", "bundle-plugin?"], "tokens": ["What", "is", "bundle", "plugin", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::580634160", "fields": {"term": "How is the bundle-plugin used?", "namespace": "open-p", "hash": 580634160, "terms": ["How is the bundle-plugin used?", "is the bundle-plugin used?", "the bundle-plugin used?", "bundle-plugin used?", "used?"], "tokens": ["How", "is", "the", "bundle", "plugin", "used", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-62824231", "fields": {"term": "What is the minimal Maven configuration?", "namespace": "open-p", "hash": -62824231, "terms": ["What is the minimal Maven configuration?", "is the minimal Maven configuration?", "the minimal Maven configuration?", "minimal Maven configuration?", "Maven configuration?", "configuration?"], "tokens": ["What", "is", "the", "minimal", "Maven", "configuration", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-779960646", "fields": {"term": "What is the purpose of the bundle-plugin?", "namespace": "open-p", "hash": -779960646, "terms": ["What is the purpose of the bundle-plugin?", "is the purpose of the bundle-plugin?", "the purpose of the bundle-plugin?", "purpose of the bundle-plugin?", "of the bundle-plugin?", "the bundle-plugin?", "bundle-plugin?"], "tokens": ["What", "is", "the", "purpose", "of", "the", "bundle", "plugin", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::657584301", "fields": {"term": "What is the pom.xml configuration for Vespa?", "namespace": "open-p", "hash": 657584301, "terms": ["What is the pom.xml configuration for Vespa?", "is the pom.xml configuration for Vespa?", "the pom.xml configuration for Vespa?", "pom.xml configuration for Vespa?", "configuration for Vespa?", "for Vespa?", "Vespa?"], "tokens": ["What", "is", "the", "pom", "xml", "configuration", "for", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-888248106", "fields": {"term": "What step does the bundle-plugin automate?", "namespace": "open-p", "hash": -888248106, "terms": ["What step does the bundle-plugin automate?", "step does the bundle-plugin automate?", "does the bundle-plugin automate?", "the bundle-plugin automate?", "bundle-plugin automate?", "automate?"], "tokens": ["What", "step", "does", "the", "bundle", "plugin", "automate", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::452911349", "fields": {"term": "How to include external dependencies?", "namespace": "open-p", "hash": 452911349, "terms": ["How to include external dependencies?", "to include external dependencies?", "include external dependencies?", "external dependencies?", "dependencies?"], "tokens": ["How", "to", "include", "external", "dependencies", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::136704887", "fields": {"term": "What is the difference between compile and provided scope?", "namespace": "open-p", "hash": 136704887, "terms": ["What is the difference between compile and provided scope?", "is the difference between compile and provided scope?", "the difference between compile and provided scope?", "difference between compile and provided scope?", "between compile and provided scope?", "compile and provided scope?", "and provided scope?", "provided scope?", "scope?"], "tokens": ["What", "is", "the", "difference", "between", "compile", "and", "provided", "scope", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::392498476", "fields": {"term": "What is the purpose of deploying external dependency?", "namespace": "open-p", "hash": 392498476, "terms": ["What is the purpose of deploying external dependency?", "is the purpose of deploying external dependency?", "the purpose of deploying external dependency?", "purpose of deploying external dependency?", "of deploying external dependency?", "deploying external dependency?", "external dependency?", "dependency?"], "tokens": ["What", "is", "the", "purpose", "of", "deploying", "external", "dependency", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-743641051", "fields": {"term": "What becomes available for use when an external dependency is included?", "namespace": "open-p", "hash": -743641051, "terms": ["What becomes available for use when an external dependency is included?", "becomes available for use when an external dependency is included?", "available for use when an external dependency is included?", "for use when an external dependency is included?", "use when an external dependency is included?", "when an external dependency is included?", "an external dependency is included?", "external dependency is included?", "dependency is included?", "is included?", "included?"], "tokens": ["What", "becomes", "available", "for", "use", "when", "an", "external", "dependency", "is", "included", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1776528757", "fields": {"term": "What should be done if external dependency is an OSGi bundle?", "namespace": "open-p", "hash": 1776528757, "terms": ["What should be done if external dependency is an OSGi bundle?", "should be done if external dependency is an OSGi bundle?", "be done if external dependency is an OSGi bundle?", "done if external dependency is an OSGi bundle?", "if external dependency is an OSGi bundle?", "external dependency is an OSGi bundle?", "dependency is an OSGi bundle?", "is an OSGi bundle?", "an OSGi bundle?", "OSGi bundle?", "bundle?"], "tokens": ["What", "should", "be", "done", "if", "external", "dependency", "is", "an", "OSGi", "bundle", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-801796765", "fields": {"term": "What is the purpose of setting scope to provided?", "namespace": "open-p", "hash": -801796765, "terms": ["What is the purpose of setting scope to provided?", "is the purpose of setting scope to provided?", "the purpose of setting scope to provided?", "purpose of setting scope to provided?", "of setting scope to provided?", "setting scope to provided?", "scope to provided?", "to provided?", "provided?"], "tokens": ["What", "is", "the", "purpose", "of", "setting", "scope", "to", "provided", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1901359419", "fields": {"term": "What packages are available when deploying an OSGi bundle?", "namespace": "open-p", "hash": 1901359419, "terms": ["What packages are available when deploying an OSGi bundle?", "packages are available when deploying an OSGi bundle?", "are available when deploying an OSGi bundle?", "available when deploying an OSGi bundle?", "when deploying an OSGi bundle?", "deploying an OSGi bundle?", "an OSGi bundle?", "OSGi bundle?", "bundle?"], "tokens": ["What", "packages", "are", "available", "when", "deploying", "an", "OSGi", "bundle", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::369858852", "fields": {"term": "How does OSGi feature work?", "namespace": "open-p", "hash": 369858852, "terms": ["How does OSGi feature work?", "does OSGi feature work?", "OSGi feature work?", "feature work?", "work?"], "tokens": ["How", "does", "OSGi", "feature", "work", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-158621871", "fields": {"term": "What is package exporting?", "namespace": "open-p", "hash": -158621871, "terms": ["What is package exporting?", "is package exporting?", "package exporting?", "exporting?"], "tokens": ["What", "is", "package", "exporting", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1970967086", "fields": {"term": "What is bundle importing?", "namespace": "open-p", "hash": -1970967086, "terms": ["What is bundle importing?", "is bundle importing?", "bundle importing?", "importing?"], "tokens": ["What", "is", "bundle", "importing", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2104102487", "fields": {"term": "How can packages be exported?", "namespace": "open-p", "hash": 2104102487, "terms": ["How can packages be exported?", "can packages be exported?", "packages be exported?", "be exported?", "exported?"], "tokens": ["How", "can", "packages", "be", "exported", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2045952455", "fields": {"term": "How are packages imported?", "namespace": "open-p", "hash": -2045952455, "terms": ["How are packages imported?", "are packages imported?", "packages imported?", "imported?"], "tokens": ["How", "are", "packages", "imported", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::362845741", "fields": {"term": "What is the version range for package import?", "namespace": "open-p", "hash": 362845741, "terms": ["What is the version range for package import?", "is the version range for package import?", "the version range for package import?", "version range for package import?", "range for package import?", "for package import?", "package import?", "import?"], "tokens": ["What", "is", "the", "version", "range", "for", "package", "import", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1094756091", "fields": {"term": "Where can learn more about OSGi?", "namespace": "open-p", "hash": 1094756091, "terms": ["Where can learn more about OSGi?", "can learn more about OSGi?", "learn more about OSGi?", "more about OSGi?", "about OSGi?", "OSGi?"], "tokens": ["Where", "can", "learn", "more", "about", "OSGi", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-81922642", "fields": {"term": "What is the bundle plugin?", "namespace": "open-p", "hash": -81922642, "terms": ["What is the bundle plugin?", "is the bundle plugin?", "the bundle plugin?", "bundle plugin?", "plugin?"], "tokens": ["What", "is", "the", "bundle", "plugin", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1795134871", "fields": {"term": "How to configure the bundle plugin?", "namespace": "open-p", "hash": -1795134871, "terms": ["How to configure the bundle plugin?", "to configure the bundle plugin?", "configure the bundle plugin?", "the bundle plugin?", "bundle plugin?", "plugin?"], "tokens": ["How", "to", "configure", "the", "bundle", "plugin", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1953364926", "fields": {"term": "What is the purpose of bundle classifier name?", "namespace": "open-p", "hash": -1953364926, "terms": ["What is the purpose of bundle classifier name?", "is the purpose of bundle classifier name?", "the purpose of bundle classifier name?", "purpose of bundle classifier name?", "of bundle classifier name?", "bundle classifier name?", "classifier name?", "name?"], "tokens": ["What", "is", "the", "purpose", "of", "bundle", "classifier", "name", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1824012660", "fields": {"term": "What is bundle symbolic name?", "namespace": "open-p", "hash": 1824012660, "terms": ["What is bundle symbolic name?", "is bundle symbolic name?", "bundle symbolic name?", "symbolic name?", "name?"], "tokens": ["What", "is", "bundle", "symbolic", "name", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::80999698", "fields": {"term": "How do I start an application with discApplicationClass?", "namespace": "open-p", "hash": 80999698, "terms": ["How do I start an application with discApplicationClass?", "do I start an application with discApplicationClass?", "I start an application with discApplicationClass?", "start an application with discApplicationClass?", "an application with discApplicationClass?", "application with discApplicationClass?", "with discApplicationClass?", "discApplicationClass?"], "tokens": ["How", "do", "I", "start", "an", "application", "with", "discApplicationClass", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::841025861", "fields": {"term": "What is the use of bundleActivator?", "namespace": "open-p", "hash": 841025861, "terms": ["What is the use of bundleActivator?", "is the use of bundleActivator?", "the use of bundleActivator?", "use of bundleActivator?", "of bundleActivator?", "bundleActivator?"], "tokens": ["What", "is", "the", "use", "of", "bundleActivator", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::318877421", "fields": {"term": "What is configGenVersion?", "namespace": "open-p", "hash": 318877421, "terms": ["What is configGenVersion?", "is configGenVersion?", "configGenVersion?"], "tokens": ["What", "is", "configGenVersion", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::701532995", "fields": {"term": "How to debug package import issues?", "namespace": "open-p", "hash": 701532995, "terms": ["How to debug package import issues?", "to debug package import issues?", "debug package import issues?", "package import issues?", "import issues?", "issues?"], "tokens": ["How", "to", "debug", "package", "import", "issues", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::526629185", "fields": {"term": "What are common bundle errors?", "namespace": "open-p", "hash": 526629185, "terms": ["What are common bundle errors?", "are common bundle errors?", "common bundle errors?", "bundle errors?", "errors?"], "tokens": ["What", "are", "common", "bundle", "errors", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-366382040", "fields": {"term": "How to troubleshoot bundle issues?", "namespace": "open-p", "hash": -366382040, "terms": ["How to troubleshoot bundle issues?", "to troubleshoot bundle issues?", "troubleshoot bundle issues?", "bundle issues?", "issues?"], "tokens": ["How", "to", "troubleshoot", "bundle", "issues", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1134041364", "fields": {"term": "What are logged bundles?", "namespace": "open-p", "hash": -1134041364, "terms": ["What are logged bundles?", "are logged bundles?", "logged bundles?", "bundles?"], "tokens": ["What", "are", "logged", "bundles", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-914664825", "fields": {"term": "How are uninstalled bundles logged?", "namespace": "open-p", "hash": -914664825, "terms": ["How are uninstalled bundles logged?", "are uninstalled bundles logged?", "uninstalled bundles logged?", "bundles logged?", "logged?"], "tokens": ["How", "are", "uninstalled", "bundles", "logged", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-348993434", "fields": {"term": "What is the format of logged bundle?", "namespace": "open-p", "hash": -348993434, "terms": ["What is the format of logged bundle?", "is the format of logged bundle?", "the format of logged bundle?", "format of logged bundle?", "of logged bundle?", "logged bundle?", "bundle?"], "tokens": ["What", "is", "the", "format", "of", "logged", "bundle", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-898466027", "fields": {"term": "What if there are no bundles to uninstall?", "namespace": "open-p", "hash": -898466027, "terms": ["What if there are no bundles to uninstall?", "if there are no bundles to uninstall?", "there are no bundles to uninstall?", "are no bundles to uninstall?", "no bundles to uninstall?", "bundles to uninstall?", "to uninstall?", "uninstall?"], "tokens": ["What", "if", "there", "are", "no", "bundles", "to", "uninstall", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1625119320", "fields": {"term": "What does Bundles to schedule for uninstall refer to?", "namespace": "open-p", "hash": -1625119320, "terms": ["What does Bundles to schedule for uninstall refer to?", "does Bundles to schedule for uninstall refer to?", "Bundles to schedule for uninstall refer to?", "to schedule for uninstall refer to?", "schedule for uninstall refer to?", "for uninstall refer to?", "uninstall refer to?", "refer to?", "to?"], "tokens": ["What", "does", "Bundles", "to", "schedule", "for", "uninstall", "refer", "to", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1303059576", "fields": {"term": "What is bundle in Vespa?", "namespace": "open-p", "hash": -1303059576, "terms": ["What is bundle in Vespa?", "is bundle in Vespa?", "bundle in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["What", "is", "bundle", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::47439662", "fields": {"term": "How is container component graph created?", "namespace": "open-p", "hash": 47439662, "terms": ["How is container component graph created?", "is container component graph created?", "container component graph created?", "component graph created?", "graph created?", "created?"], "tokens": ["How", "is", "container", "component", "graph", "created", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-798841109", "fields": {"term": "What is the cause of NoClassDefFoundError exception?", "namespace": "open-p", "hash": -798841109, "terms": ["What is the cause of NoClassDefFoundError exception?", "is the cause of NoClassDefFoundError exception?", "the cause of NoClassDefFoundError exception?", "cause of NoClassDefFoundError exception?", "of NoClassDefFoundError exception?", "NoClassDefFoundError exception?", "exception?"], "tokens": ["What", "is", "the", "cause", "of", "NoClassDefFoundError", "exception", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1764480152", "fields": {"term": "What is Import-Package statement?", "namespace": "open-p", "hash": 1764480152, "terms": ["What is Import-Package statement?", "is Import-Package statement?", "Import-Package statement?", "statement?"], "tokens": ["What", "is", "Import", "Package", "statement", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1250718363", "fields": {"term": "How to fix NoClassDefFoundError exception?", "namespace": "open-p", "hash": -1250718363, "terms": ["How to fix NoClassDefFoundError exception?", "to fix NoClassDefFoundError exception?", "fix NoClassDefFoundError exception?", "NoClassDefFoundError exception?", "exception?"], "tokens": ["How", "to", "fix", "NoClassDefFoundError", "exception", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::87491107", "fields": {"term": "How to add Import-Package statement?", "namespace": "open-p", "hash": 87491107, "terms": ["How to add Import-Package statement?", "to add Import-Package statement?", "add Import-Package statement?", "Import-Package statement?", "statement?"], "tokens": ["How", "to", "add", "Import", "Package", "statement", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1599698945", "fields": {"term": "What is a 'provided' scope dependency?", "namespace": "open-p", "hash": -1599698945, "terms": ["What is a 'provided' scope dependency?", "is a 'provided' scope dependency?", "a 'provided' scope dependency?", "'provided' scope dependency?", "scope dependency?", "dependency?"], "tokens": ["What", "is", "a", "'", "provided", "'", "scope", "dependency", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-9889122", "fields": {"term": "What are the reasons for ClassNotFoundException?", "namespace": "open-p", "hash": -9889122, "terms": ["What are the reasons for ClassNotFoundException?", "are the reasons for ClassNotFoundException?", "the reasons for ClassNotFoundException?", "reasons for ClassNotFoundException?", "for ClassNotFoundException?", "ClassNotFoundException?"], "tokens": ["What", "are", "the", "reasons", "for", "ClassNotFoundException", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1074413571", "fields": {"term": "How to export a package from another bundle?", "namespace": "open-p", "hash": 1074413571, "terms": ["How to export a package from another bundle?", "to export a package from another bundle?", "export a package from another bundle?", "a package from another bundle?", "package from another bundle?", "from another bundle?", "another bundle?", "bundle?"], "tokens": ["How", "to", "export", "a", "package", "from", "another", "bundle", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1373227454", "fields": {"term": "What is a pluggable framework?", "namespace": "open-p", "hash": -1373227454, "terms": ["What is a pluggable framework?", "is a pluggable framework?", "a pluggable framework?", "pluggable framework?", "framework?"], "tokens": ["What", "is", "a", "pluggable", "framework", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2116854375", "fields": {"term": "What is a container startup?", "namespace": "open-p", "hash": -2116854375, "terms": ["What is a container startup?", "is a container startup?", "a container startup?", "container startup?", "startup?"], "tokens": ["What", "is", "a", "container", "startup", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::511918544", "fields": {"term": "How to debug Vespa container?", "namespace": "open-p", "hash": 511918544, "terms": ["How to debug Vespa container?", "to debug Vespa container?", "debug Vespa container?", "Vespa container?", "container?"], "tokens": ["How", "to", "debug", "Vespa", "container", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::149744998", "fields": {"term": "What is vespa-logctl used for?", "namespace": "open-p", "hash": 149744998, "terms": ["What is vespa-logctl used for?", "is vespa-logctl used for?", "vespa-logctl used for?", "used for?", "for?"], "tokens": ["What", "is", "vespa", "logctl", "used", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::867210542", "fields": {"term": "How to restart Vespa container?", "namespace": "open-p", "hash": 867210542, "terms": ["How to restart Vespa container?", "to restart Vespa container?", "restart Vespa container?", "Vespa container?", "container?"], "tokens": ["How", "to", "restart", "Vespa", "container", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2141739985", "fields": {"term": "What is container service name?", "namespace": "open-p", "hash": 2141739985, "terms": ["What is container service name?", "is container service name?", "container service name?", "service name?", "name?"], "tokens": ["What", "is", "container", "service", "name", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-227521049", "fields": {"term": "How to set debug logging?", "namespace": "open-p", "hash": -227521049, "terms": ["How to set debug logging?", "to set debug logging?", "set debug logging?", "debug logging?", "logging?"], "tokens": ["How", "to", "set", "debug", "logging", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1313955366", "fields": {"term": "What is ComponentGraph debug log?", "namespace": "open-p", "hash": -1313955366, "terms": ["What is ComponentGraph debug log?", "is ComponentGraph debug log?", "ComponentGraph debug log?", "debug log?", "log?"], "tokens": ["What", "is", "ComponentGraph", "debug", "log", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::500132899", "fields": {"term": "What causes an unresolved constraint error?", "namespace": "open-p", "hash": 500132899, "terms": ["What causes an unresolved constraint error?", "causes an unresolved constraint error?", "an unresolved constraint error?", "unresolved constraint error?", "constraint error?", "error?"], "tokens": ["What", "causes", "an", "unresolved", "constraint", "error", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::914670010", "fields": {"term": "What is the symptom of an unresolved constraint error?", "namespace": "open-p", "hash": 914670010, "terms": ["What is the symptom of an unresolved constraint error?", "is the symptom of an unresolved constraint error?", "the symptom of an unresolved constraint error?", "symptom of an unresolved constraint error?", "of an unresolved constraint error?", "an unresolved constraint error?", "unresolved constraint error?", "constraint error?", "error?"], "tokens": ["What", "is", "the", "symptom", "of", "an", "unresolved", "constraint", "error", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2134550644", "fields": {"term": "What is a 'provided' dependency?", "namespace": "open-p", "hash": -2134550644, "terms": ["What is a 'provided' dependency?", "is a 'provided' dependency?", "a 'provided' dependency?", "'provided' dependency?", "dependency?"], "tokens": ["What", "is", "a", "'", "provided", "'", "dependency", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1183325924", "fields": {"term": "What are the options for making a missing dependency available at runtime?", "namespace": "open-p", "hash": -1183325924, "terms": ["What are the options for making a missing dependency available at runtime?", "are the options for making a missing dependency available at runtime?", "the options for making a missing dependency available at runtime?", "options for making a missing dependency available at runtime?", "for making a missing dependency available at runtime?", "making a missing dependency available at runtime?", "a missing dependency available at runtime?", "missing dependency available at runtime?", "dependency available at runtime?", "available at runtime?", "at runtime?", "runtime?"], "tokens": ["What", "are", "the", "options", "for", "making", "a", "missing", "dependency", "available", "at", "runtime", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1731480621", "fields": {"term": "What causes a java.lang.LinkageError?", "namespace": "open-p", "hash": 1731480621, "terms": ["What causes a java.lang.LinkageError?", "causes a java.lang.LinkageError?", "a java.lang.LinkageError?", "java.lang.LinkageError?"], "tokens": ["What", "causes", "a", "java", "lang", "LinkageError", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-739621965", "fields": {"term": "What is the importance of resolving classes correctly?", "namespace": "open-p", "hash": -739621965, "terms": ["What is the importance of resolving classes correctly?", "is the importance of resolving classes correctly?", "the importance of resolving classes correctly?", "importance of resolving classes correctly?", "of resolving classes correctly?", "resolving classes correctly?", "classes correctly?", "correctly?"], "tokens": ["What", "is", "the", "importance", "of", "resolving", "classes", "correctly", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1371726604", "fields": {"term": "What are the possible root causes of error messages?", "namespace": "open-p", "hash": -1371726604, "terms": ["What are the possible root causes of error messages?", "are the possible root causes of error messages?", "the possible root causes of error messages?", "possible root causes of error messages?", "root causes of error messages?", "causes of error messages?", "of error messages?", "error messages?", "messages?"], "tokens": ["What", "are", "the", "possible", "root", "causes", "of", "error", "messages", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::21203991", "fields": {"term": "What is the significance of resolving classes to the same `Class` object?", "namespace": "open-p", "hash": 21203991, "terms": ["What is the significance of resolving classes to the same `Class` object?", "is the significance of resolving classes to the same `Class` object?", "the significance of resolving classes to the same `Class` object?", "significance of resolving classes to the same `Class` object?", "of resolving classes to the same `Class` object?", "resolving classes to the same `Class` object?", "classes to the same `Class` object?", "to the same `Class` object?", "the same `Class` object?", "same `Class` object?", "`Class` object?", "object?"], "tokens": ["What", "is", "the", "significance", "of", "resolving", "classes", "to", "the", "same", "`", "Class", "`", "object", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-874174199", "fields": {"term": "Why should 'javax.xml' packages not be embedded?", "namespace": "open-p", "hash": -874174199, "terms": ["Why should 'javax.xml' packages not be embedded?", "should 'javax.xml' packages not be embedded?", "'javax.xml' packages not be embedded?", "packages not be embedded?", "not be embedded?", "be embedded?", "embedded?"], "tokens": ["Why", "should", "'", "javax", "xml", "'", "packages", "not", "be", "embedded", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-867994286", "fields": {"term": "What tool can extract the full component jar including embedded jars?", "namespace": "open-p", "hash": -867994286, "terms": ["What tool can extract the full component jar including embedded jars?", "tool can extract the full component jar including embedded jars?", "can extract the full component jar including embedded jars?", "extract the full component jar including embedded jars?", "the full component jar including embedded jars?", "full component jar including embedded jars?", "component jar including embedded jars?", "jar including embedded jars?", "including embedded jars?", "embedded jars?", "jars?"], "tokens": ["What", "tool", "can", "extract", "the", "full", "component", "jar", "including", "embedded", "jars", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-321688577", "fields": {"term": "How can you find out which libraries pull in a package?", "namespace": "open-p", "hash": -321688577, "terms": ["How can you find out which libraries pull in a package?", "can you find out which libraries pull in a package?", "you find out which libraries pull in a package?", "find out which libraries pull in a package?", "out which libraries pull in a package?", "which libraries pull in a package?", "libraries pull in a package?", "pull in a package?", "in a package?", "a package?", "package?"], "tokens": ["How", "can", "you", "find", "out", "which", "libraries", "pull", "in", "a", "package", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::120724078", "fields": {"term": "How can you exclude a package from a direct dependency in pom.xml?", "namespace": "open-p", "hash": 120724078, "terms": ["How can you exclude a package from a direct dependency in pom.xml?", "can you exclude a package from a direct dependency in pom.xml?", "you exclude a package from a direct dependency in pom.xml?", "exclude a package from a direct dependency in pom.xml?", "a package from a direct dependency in pom.xml?", "package from a direct dependency in pom.xml?", "from a direct dependency in pom.xml?", "a direct dependency in pom.xml?", "direct dependency in pom.xml?", "dependency in pom.xml?", "in pom.xml?", "pom.xml?"], "tokens": ["How", "can", "you", "exclude", "a", "package", "from", "a", "direct", "dependency", "in", "pom", "xml", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1579062568", "fields": {"term": "What is causing the java.lang.LinkageError?", "namespace": "open-p", "hash": 1579062568, "terms": ["What is causing the java.lang.LinkageError?", "is causing the java.lang.LinkageError?", "causing the java.lang.LinkageError?", "the java.lang.LinkageError?", "java.lang.LinkageError?"], "tokens": ["What", "is", "causing", "the", "java", "lang", "LinkageError", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1571765449", "fields": {"term": "How can I fix the issue with slf4j?", "namespace": "open-p", "hash": 1571765449, "terms": ["How can I fix the issue with slf4j?", "can I fix the issue with slf4j?", "I fix the issue with slf4j?", "fix the issue with slf4j?", "the issue with slf4j?", "issue with slf4j?", "with slf4j?", "slf4j?"], "tokens": ["How", "can", "I", "fix", "the", "issue", "with", "slf4j", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::962572816", "fields": {"term": "What is the mvn dependency:tree command?", "namespace": "open-p", "hash": 962572816, "terms": ["What is the mvn dependency:tree command?", "is the mvn dependency:tree command?", "the mvn dependency:tree command?", "mvn dependency:tree command?", "dependency:tree command?", "command?"], "tokens": ["What", "is", "the", "mvn", "dependency", ":", "tree", "command", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1607129894", "fields": {"term": "What is an exclusion in Maven dependencies?", "namespace": "open-p", "hash": -1607129894, "terms": ["What is an exclusion in Maven dependencies?", "is an exclusion in Maven dependencies?", "an exclusion in Maven dependencies?", "exclusion in Maven dependencies?", "in Maven dependencies?", "Maven dependencies?", "dependencies?"], "tokens": ["What", "is", "an", "exclusion", "in", "Maven", "dependencies", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1086152221", "fields": {"term": "What is the root cause of the issue with slf4j?", "namespace": "open-p", "hash": 1086152221, "terms": ["What is the root cause of the issue with slf4j?", "is the root cause of the issue with slf4j?", "the root cause of the issue with slf4j?", "root cause of the issue with slf4j?", "cause of the issue with slf4j?", "of the issue with slf4j?", "the issue with slf4j?", "issue with slf4j?", "with slf4j?", "slf4j?"], "tokens": ["What", "is", "the", "root", "cause", "of", "the", "issue", "with", "slf4j", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1814914906", "fields": {"term": "What are the different scopes of Maven dependencies?", "namespace": "open-p", "hash": -1814914906, "terms": ["What are the different scopes of Maven dependencies?", "are the different scopes of Maven dependencies?", "the different scopes of Maven dependencies?", "different scopes of Maven dependencies?", "scopes of Maven dependencies?", "of Maven dependencies?", "Maven dependencies?", "dependencies?"], "tokens": ["What", "are", "the", "different", "scopes", "of", "Maven", "dependencies", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1164458888", "fields": {"term": "What is the summary cache?", "namespace": "open-p", "hash": 1164458888, "terms": ["What is the summary cache?", "is the summary cache?", "the summary cache?", "summary cache?", "cache?"], "tokens": ["What", "is", "the", "summary", "cache", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1053477108", "fields": {"term": "How is the proton summary cache enabled?", "namespace": "open-p", "hash": -1053477108, "terms": ["How is the proton summary cache enabled?", "is the proton summary cache enabled?", "the proton summary cache enabled?", "proton summary cache enabled?", "summary cache enabled?", "cache enabled?", "enabled?"], "tokens": ["How", "is", "the", "proton", "summary", "cache", "enabled", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-34121241", "fields": {"term": "What does enabling the summary cache save?", "namespace": "open-p", "hash": -34121241, "terms": ["What does enabling the summary cache save?", "does enabling the summary cache save?", "enabling the summary cache save?", "the summary cache save?", "summary cache save?", "cache save?", "save?"], "tokens": ["What", "does", "enabling", "the", "summary", "cache", "save", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::896816235", "fields": {"term": "How much available memory does the summary cache use by default?", "namespace": "open-p", "hash": 896816235, "terms": ["How much available memory does the summary cache use by default?", "much available memory does the summary cache use by default?", "available memory does the summary cache use by default?", "memory does the summary cache use by default?", "does the summary cache use by default?", "the summary cache use by default?", "summary cache use by default?", "cache use by default?", "use by default?", "by default?", "default?"], "tokens": ["How", "much", "available", "memory", "does", "the", "summary", "cache", "use", "by", "default", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::154901981", "fields": {"term": "What is ranking.queryCache and groupingSessionCache?", "namespace": "open-p", "hash": 154901981, "terms": ["What is ranking.queryCache and groupingSessionCache?", "is ranking.queryCache and groupingSessionCache?", "ranking.queryCache and groupingSessionCache?", "and groupingSessionCache?", "groupingSessionCache?"], "tokens": ["What", "is", "ranking", "queryCache", "and", "groupingSessionCache", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::430406021", "fields": {"term": "What benefit do caches provide?", "namespace": "open-p", "hash": 430406021, "terms": ["What benefit do caches provide?", "benefit do caches provide?", "do caches provide?", "caches provide?", "provide?"], "tokens": ["What", "benefit", "do", "caches", "provide", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::931759535", "fields": {"term": "What queries benefit from caching?", "namespace": "open-p", "hash": 931759535, "terms": ["What queries benefit from caching?", "queries benefit from caching?", "benefit from caching?", "from caching?", "caching?"], "tokens": ["What", "queries", "benefit", "from", "caching", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-996754116", "fields": {"term": "What is saved by using the caches?", "namespace": "open-p", "hash": -996754116, "terms": ["What is saved by using the caches?", "is saved by using the caches?", "saved by using the caches?", "by using the caches?", "using the caches?", "the caches?", "caches?"], "tokens": ["What", "is", "saved", "by", "using", "the", "caches", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1193233201", "fields": {"term": "What is a chained component?", "namespace": "open-p", "hash": 1193233201, "terms": ["What is a chained component?", "is a chained component?", "a chained component?", "chained component?", "component?"], "tokens": ["What", "is", "a", "chained", "component", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::791972374", "fields": {"term": "What are named services?", "namespace": "open-p", "hash": 791972374, "terms": ["What are named services?", "are named services?", "named services?", "services?"], "tokens": ["What", "are", "named", "services", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1157174198", "fields": {"term": "What are dependencies in a chain?", "namespace": "open-p", "hash": 1157174198, "terms": ["What are dependencies in a chain?", "are dependencies in a chain?", "dependencies in a chain?", "in a chain?", "a chain?", "chain?"], "tokens": ["What", "are", "dependencies", "in", "a", "chain", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-279217170", "fields": {"term": "How are component dependencies defined?", "namespace": "open-p", "hash": -279217170, "terms": ["How are component dependencies defined?", "are component dependencies defined?", "component dependencies defined?", "dependencies defined?", "defined?"], "tokens": ["How", "are", "component", "dependencies", "defined", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1131871812", "fields": {"term": "What is the purpose of a chain?", "namespace": "open-p", "hash": -1131871812, "terms": ["What is the purpose of a chain?", "is the purpose of a chain?", "the purpose of a chain?", "purpose of a chain?", "of a chain?", "a chain?", "chain?"], "tokens": ["What", "is", "the", "purpose", "of", "a", "chain", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1288398339", "fields": {"term": "What determines execution order in Vespa?", "namespace": "open-p", "hash": -1288398339, "terms": ["What determines execution order in Vespa?", "determines execution order in Vespa?", "execution order in Vespa?", "order in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["What", "determines", "execution", "order", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::224816070", "fields": {"term": "How are ordering constraints specified?", "namespace": "open-p", "hash": 224816070, "terms": ["How are ordering constraints specified?", "are ordering constraints specified?", "ordering constraints specified?", "constraints specified?", "specified?"], "tokens": ["How", "are", "ordering", "constraints", "specified", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-639856186", "fields": {"term": "What is the syntax for specifying multiple functionalities?", "namespace": "open-p", "hash": -639856186, "terms": ["What is the syntax for specifying multiple functionalities?", "is the syntax for specifying multiple functionalities?", "the syntax for specifying multiple functionalities?", "syntax for specifying multiple functionalities?", "for specifying multiple functionalities?", "specifying multiple functionalities?", "multiple functionalities?", "functionalities?"], "tokens": ["What", "is", "the", "syntax", "for", "specifying", "multiple", "functionalities", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1038191889", "fields": {"term": "What is the difference between specifying dependencies in code vs configuration?", "namespace": "open-p", "hash": -1038191889, "terms": ["What is the difference between specifying dependencies in code vs configuration?", "is the difference between specifying dependencies in code vs configuration?", "the difference between specifying dependencies in code vs configuration?", "difference between specifying dependencies in code vs configuration?", "between specifying dependencies in code vs configuration?", "specifying dependencies in code vs configuration?", "dependencies in code vs configuration?", "in code vs configuration?", "code vs configuration?", "vs configuration?", "configuration?"], "tokens": ["What", "is", "the", "difference", "between", "specifying", "dependencies", "in", "code", "vs", "configuration", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1931019590", "fields": {"term": "What is chains inheritance?", "namespace": "open-p", "hash": 1931019590, "terms": ["What is chains inheritance?", "is chains inheritance?", "chains inheritance?", "inheritance?"], "tokens": ["What", "is", "chains", "inheritance", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-602679805", "fields": {"term": "What is the default search chain?", "namespace": "open-p", "hash": -602679805, "terms": ["What is the default search chain?", "is the default search chain?", "the default search chain?", "default search chain?", "search chain?", "chain?"], "tokens": ["What", "is", "the", "default", "search", "chain", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::1262970888", "fields": {"term": "What is the purpose of 'inherits' and 'excludes'", "namespace": "open-p", "hash": 1262970888, "terms": ["What is the purpose of 'inherits' and 'excludes'", "is the purpose of 'inherits' and 'excludes'", "the purpose of 'inherits' and 'excludes'", "purpose of 'inherits' and 'excludes'", "of 'inherits' and 'excludes'", "'inherits' and 'excludes'", "and 'excludes'", "'excludes'"], "tokens": ["What", "is", "the", "purpose", "of", "'", "inherits", "'", "and", "'", "excludes", "'"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-367904367", "fields": {"term": "What are the built-in search chains?", "namespace": "open-p", "hash": -367904367, "terms": ["What are the built-in search chains?", "are the built-in search chains?", "the built-in search chains?", "built-in search chains?", "search chains?", "chains?"], "tokens": ["What", "are", "the", "built", "in", "search", "chains", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::957633956", "fields": {"term": "What is 'native' search chain?", "namespace": "open-p", "hash": 957633956, "terms": ["What is 'native' search chain?", "is 'native' search chain?", "'native' search chain?", "search chain?", "chain?"], "tokens": ["What", "is", "'", "native", "'", "search", "chain", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1518436528", "fields": {"term": "What is 'vespa' search chain?", "namespace": "open-p", "hash": 1518436528, "terms": ["What is 'vespa' search chain?", "is 'vespa' search chain?", "'vespa' search chain?", "search chain?", "chain?"], "tokens": ["What", "is", "'", "vespa", "'", "search", "chain", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::400830210", "fields": {"term": "What is unit testing?", "namespace": "open-p", "hash": 400830210, "terms": ["What is unit testing?", "is unit testing?", "unit testing?", "testing?"], "tokens": ["What", "is", "unit", "testing", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::277762900", "fields": {"term": "What is com.yahoo.component.chain.Chain?", "namespace": "open-p", "hash": 277762900, "terms": ["What is com.yahoo.component.chain.Chain?", "is com.yahoo.component.chain.Chain?", "com.yahoo.component.chain.Chain?"], "tokens": ["What", "is", "com", "yahoo", "component", "chain", "Chain", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2038714637", "fields": {"term": "What is the constructor of Chain?", "namespace": "open-p", "hash": 2038714637, "terms": ["What is the constructor of Chain?", "is the constructor of Chain?", "the constructor of Chain?", "constructor of Chain?", "of Chain?", "Chain?"], "tokens": ["What", "is", "the", "constructor", "of", "Chain", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1997881367", "fields": {"term": "How to simulate basic workflow?", "namespace": "open-p", "hash": -1997881367, "terms": ["How to simulate basic workflow?", "to simulate basic workflow?", "simulate basic workflow?", "basic workflow?", "workflow?"], "tokens": ["How", "to", "simulate", "basic", "workflow", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1383132655", "fields": {"term": "Why define shared field names?", "namespace": "open-p", "hash": -1383132655, "terms": ["Why define shared field names?", "define shared field names?", "shared field names?", "field names?", "names?"], "tokens": ["Why", "define", "shared", "field", "names", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-818393643", "fields": {"term": "What is good practice for shared field names?", "namespace": "open-p", "hash": -818393643, "terms": ["What is good practice for shared field names?", "is good practice for shared field names?", "good practice for shared field names?", "practice for shared field names?", "for shared field names?", "shared field names?", "field names?", "names?"], "tokens": ["What", "is", "good", "practice", "for", "shared", "field", "names", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::482958150", "fields": {"term": "Where can I find an example of passing information between searchers?", "namespace": "open-p", "hash": 482958150, "terms": ["Where can I find an example of passing information between searchers?", "can I find an example of passing information between searchers?", "I find an example of passing information between searchers?", "find an example of passing information between searchers?", "an example of passing information between searchers?", "example of passing information between searchers?", "of passing information between searchers?", "passing information between searchers?", "information between searchers?", "between searchers?", "searchers?"], "tokens": ["Where", "can", "I", "find", "an", "example", "of", "passing", "information", "between", "searchers", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-64587973", "fields": {"term": "How can the search chain be selected?", "namespace": "open-p", "hash": -64587973, "terms": ["How can the search chain be selected?", "can the search chain be selected?", "the search chain be selected?", "search chain be selected?", "chain be selected?", "be selected?", "selected?"], "tokens": ["How", "can", "the", "search", "chain", "be", "selected", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1888064068", "fields": {"term": "What is the native chain?", "namespace": "open-p", "hash": -1888064068, "terms": ["What is the native chain?", "is the native chain?", "the native chain?", "native chain?", "chain?"], "tokens": ["What", "is", "the", "native", "chain", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1553758475", "fields": {"term": "What is the Cloud Config System?", "namespace": "open-p", "hash": -1553758475, "terms": ["What is the Cloud Config System?", "is the Cloud Config System?", "the Cloud Config System?", "Cloud Config System?", "Config System?", "System?"], "tokens": ["What", "is", "the", "Cloud", "Config", "System", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1156837209", "fields": {"term": "What is a CCS plugin?", "namespace": "open-p", "hash": 1156837209, "terms": ["What is a CCS plugin?", "is a CCS plugin?", "a CCS plugin?", "CCS plugin?", "plugin?"], "tokens": ["What", "is", "a", "CCS", "plugin", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1029691451", "fields": {"term": "Why create a CCS plugin?", "namespace": "open-p", "hash": -1029691451, "terms": ["Why create a CCS plugin?", "create a CCS plugin?", "a CCS plugin?", "CCS plugin?", "plugin?"], "tokens": ["Why", "create", "a", "CCS", "plugin", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1875172352", "fields": {"term": "What does a plugin allow users to do?", "namespace": "open-p", "hash": 1875172352, "terms": ["What does a plugin allow users to do?", "does a plugin allow users to do?", "a plugin allow users to do?", "plugin allow users to do?", "allow users to do?", "users to do?", "to do?", "do?"], "tokens": ["What", "does", "a", "plugin", "allow", "users", "to", "do", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::665938852", "fields": {"term": "What is a CCS model plugin?", "namespace": "open-p", "hash": 665938852, "terms": ["What is a CCS model plugin?", "is a CCS model plugin?", "a CCS model plugin?", "CCS model plugin?", "model plugin?", "plugin?"], "tokens": ["What", "is", "a", "CCS", "model", "plugin", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1141424564", "fields": {"term": "What is a config model?", "namespace": "open-p", "hash": -1141424564, "terms": ["What is a config model?", "is a config model?", "a config model?", "config model?", "model?"], "tokens": ["What", "is", "a", "config", "model", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::-966883224", "fields": {"term": "What is an application package in CCS?", "namespace": "open-p", "hash": -966883224, "terms": ["What is an application package in CCS?", "is an application package in CCS?", "an application package in CCS?", "application package in CCS?", "package in CCS?", "in CCS?", "CCS?"], "tokens": ["What", "is", "an", "application", "package", "in", "CCS", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1823640126", "fields": {"term": "How does CCS handle system configuration?", "namespace": "open-p", "hash": -1823640126, "terms": ["How does CCS handle system configuration?", "does CCS handle system configuration?", "CCS handle system configuration?", "handle system configuration?", "system configuration?", "configuration?"], "tokens": ["How", "does", "CCS", "handle", "system", "configuration", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::425675301", "fields": {"term": "What is the role of model plugins?", "namespace": "open-p", "hash": 425675301, "terms": ["What is the role of model plugins?", "is the role of model plugins?", "the role of model plugins?", "role of model plugins?", "of model plugins?", "model plugins?", "plugins?"], "tokens": ["What", "is", "the", "role", "of", "model", "plugins", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::854112496", "fields": {"term": "What are the benefits of using CCS?", "namespace": "open-p", "hash": 854112496, "terms": ["What are the benefits of using CCS?", "are the benefits of using CCS?", "the benefits of using CCS?", "benefits of using CCS?", "of using CCS?", "using CCS?", "CCS?"], "tokens": ["What", "are", "the", "benefits", "of", "using", "CCS", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1058436472", "fields": {"term": "What is the input to the config server in CCS?", "namespace": "open-p", "hash": -1058436472, "terms": ["What is the input to the config server in CCS?", "is the input to the config server in CCS?", "the input to the config server in CCS?", "input to the config server in CCS?", "to the config server in CCS?", "the config server in CCS?", "config server in CCS?", "server in CCS?", "in CCS?", "CCS?"], "tokens": ["What", "is", "the", "input", "to", "the", "config", "server", "in", "CCS", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::125425799", "fields": {"term": "What is the model plugins?", "namespace": "open-p", "hash": 125425799, "terms": ["What is the model plugins?", "is the model plugins?", "the model plugins?", "model plugins?", "plugins?"], "tokens": ["What", "is", "the", "model", "plugins", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1131198285", "fields": {"term": "What can model plugins do?", "namespace": "open-p", "hash": -1131198285, "terms": ["What can model plugins do?", "can model plugins do?", "model plugins do?", "plugins do?", "do?"], "tokens": ["What", "can", "model", "plugins", "do", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-191277726", "fields": {"term": "What are the use cases for model plugins?", "namespace": "open-p", "hash": -191277726, "terms": ["What are the use cases for model plugins?", "are the use cases for model plugins?", "the use cases for model plugins?", "use cases for model plugins?", "cases for model plugins?", "for model plugins?", "model plugins?", "plugins?"], "tokens": ["What", "are", "the", "use", "cases", "for", "model", "plugins", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::186794455", "fields": {"term": "What can the plugin allocate?", "namespace": "open-p", "hash": 186794455, "terms": ["What can the plugin allocate?", "can the plugin allocate?", "the plugin allocate?", "plugin allocate?", "allocate?"], "tokens": ["What", "can", "the", "plugin", "allocate", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1145982874", "fields": {"term": "What syntax is supported by the plugin?", "namespace": "open-p", "hash": -1145982874, "terms": ["What syntax is supported by the plugin?", "syntax is supported by the plugin?", "is supported by the plugin?", "supported by the plugin?", "by the plugin?", "the plugin?", "plugin?"], "tokens": ["What", "syntax", "is", "supported", "by", "the", "plugin", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1387060712", "fields": {"term": "What level of abstraction can the plugin provide?", "namespace": "open-p", "hash": -1387060712, "terms": ["What level of abstraction can the plugin provide?", "level of abstraction can the plugin provide?", "of abstraction can the plugin provide?", "abstraction can the plugin provide?", "can the plugin provide?", "the plugin provide?", "plugin provide?", "provide?"], "tokens": ["What", "level", "of", "abstraction", "can", "the", "plugin", "provide", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1688175196", "fields": {"term": "What benefits does the plugin provide for configuration files?", "namespace": "open-p", "hash": -1688175196, "terms": ["What benefits does the plugin provide for configuration files?", "benefits does the plugin provide for configuration files?", "does the plugin provide for configuration files?", "the plugin provide for configuration files?", "plugin provide for configuration files?", "provide for configuration files?", "for configuration files?", "configuration files?", "files?"], "tokens": ["What", "benefits", "does", "the", "plugin", "provide", "for", "configuration", "files", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::859651571", "fields": {"term": "What is CCS?", "namespace": "open-p", "hash": 859651571, "terms": ["What is CCS?", "is CCS?", "CCS?"], "tokens": ["What", "is", "CCS", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::1428810352", "fields": {"term": "What is a service location broker cluster?", "namespace": "open-p", "hash": 1428810352, "terms": ["What is a service location broker cluster?", "is a service location broker cluster?", "a service location broker cluster?", "service location broker cluster?", "location broker cluster?", "broker cluster?", "cluster?"], "tokens": ["What", "is", "a", "service", "location", "broker", "cluster", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1311976222", "fields": {"term": "What does a Vespa model plugin do?", "namespace": "open-p", "hash": -1311976222, "terms": ["What does a Vespa model plugin do?", "does a Vespa model plugin do?", "a Vespa model plugin do?", "Vespa model plugin do?", "model plugin do?", "plugin do?", "do?"], "tokens": ["What", "does", "a", "Vespa", "model", "plugin", "do", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::716207472", "fields": {"term": "What is an EchoServer?", "namespace": "open-p", "hash": 716207472, "terms": ["What is an EchoServer?", "is an EchoServer?", "an EchoServer?", "EchoServer?"], "tokens": ["What", "is", "an", "EchoServer", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::853799159", "fields": {"term": "How to build an Echo plugin?", "namespace": "open-p", "hash": 853799159, "terms": ["How to build an Echo plugin?", "to build an Echo plugin?", "build an Echo plugin?", "an Echo plugin?", "Echo plugin?", "plugin?"], "tokens": ["How", "to", "build", "an", "Echo", "plugin", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::760674697", "fields": {"term": "What is an EchoModel?", "namespace": "open-p", "hash": 760674697, "terms": ["What is an EchoModel?", "is an EchoModel?", "an EchoModel?", "EchoModel?"], "tokens": ["What", "is", "an", "EchoModel", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-518000591", "fields": {"term": "What is the purpose of EchoConfig?", "namespace": "open-p", "hash": -518000591, "terms": ["What is the purpose of EchoConfig?", "is the purpose of EchoConfig?", "the purpose of EchoConfig?", "purpose of EchoConfig?", "of EchoConfig?", "EchoConfig?"], "tokens": ["What", "is", "the", "purpose", "of", "EchoConfig", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1672185051", "fields": {"term": "What is the role of ConfigModelBuilder?", "namespace": "open-p", "hash": -1672185051, "terms": ["What is the role of ConfigModelBuilder?", "is the role of ConfigModelBuilder?", "the role of ConfigModelBuilder?", "role of ConfigModelBuilder?", "of ConfigModelBuilder?", "ConfigModelBuilder?"], "tokens": ["What", "is", "the", "role", "of", "ConfigModelBuilder", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1685366879", "fields": {"term": "What is the role of AbstractConfigProducer?", "namespace": "open-p", "hash": -1685366879, "terms": ["What is the role of AbstractConfigProducer?", "is the role of AbstractConfigProducer?", "the role of AbstractConfigProducer?", "role of AbstractConfigProducer?", "of AbstractConfigProducer?", "AbstractConfigProducer?"], "tokens": ["What", "is", "the", "role", "of", "AbstractConfigProducer", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-327757712", "fields": {"term": "What is code reuse?", "namespace": "open-p", "hash": -327757712, "terms": ["What is code reuse?", "is code reuse?", "code reuse?", "reuse?"], "tokens": ["What", "is", "code", "reuse", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1480070107", "fields": {"term": "How to use other models?", "namespace": "open-p", "hash": -1480070107, "terms": ["How to use other models?", "to use other models?", "use other models?", "other models?", "models?"], "tokens": ["How", "to", "use", "other", "models", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1876403301", "fields": {"term": "What is a constructor argument?", "namespace": "open-p", "hash": 1876403301, "terms": ["What is a constructor argument?", "is a constructor argument?", "a constructor argument?", "constructor argument?", "argument?"], "tokens": ["What", "is", "a", "constructor", "argument", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-75133089", "fields": {"term": "What is EchoProxyModel used for?", "namespace": "open-p", "hash": -75133089, "terms": ["What is EchoProxyModel used for?", "is EchoProxyModel used for?", "EchoProxyModel used for?", "used for?", "for?"], "tokens": ["What", "is", "EchoProxyModel", "used", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1911562261", "fields": {"term": "What is EchoModel?", "namespace": "open-p", "hash": 1911562261, "terms": ["What is EchoModel?", "is EchoModel?", "EchoModel?"], "tokens": ["What", "is", "EchoModel", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::299823591", "fields": {"term": "How do you create a unit test for Vespa plugins?", "namespace": "open-p", "hash": 299823591, "terms": ["How do you create a unit test for Vespa plugins?", "do you create a unit test for Vespa plugins?", "you create a unit test for Vespa plugins?", "create a unit test for Vespa plugins?", "a unit test for Vespa plugins?", "unit test for Vespa plugins?", "test for Vespa plugins?", "for Vespa plugins?", "Vespa plugins?", "plugins?"], "tokens": ["How", "do", "you", "create", "a", "unit", "test", "for", "Vespa", "plugins", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-148497768", "fields": {"term": "What is the ConfigModelTester class?", "namespace": "open-p", "hash": -148497768, "terms": ["What is the ConfigModelTester class?", "is the ConfigModelTester class?", "the ConfigModelTester class?", "ConfigModelTester class?", "class?"], "tokens": ["What", "is", "the", "ConfigModelTester", "class", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1110555965", "fields": {"term": "Which method do you call to build the entire model?", "namespace": "open-p", "hash": -1110555965, "terms": ["Which method do you call to build the entire model?", "method do you call to build the entire model?", "do you call to build the entire model?", "you call to build the entire model?", "call to build the entire model?", "to build the entire model?", "build the entire model?", "the entire model?", "entire model?", "model?"], "tokens": ["Which", "method", "do", "you", "call", "to", "build", "the", "entire", "model", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::904191278", "fields": {"term": "What is the TestRoot object used for?", "namespace": "open-p", "hash": 904191278, "terms": ["What is the TestRoot object used for?", "is the TestRoot object used for?", "the TestRoot object used for?", "TestRoot object used for?", "object used for?", "used for?", "for?"], "tokens": ["What", "is", "the", "TestRoot", "object", "used", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1616275735", "fields": {"term": "What is mock class?", "namespace": "open-p", "hash": -1616275735, "terms": ["What is mock class?", "is mock class?", "mock class?", "class?"], "tokens": ["What", "is", "mock", "class", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-156483368", "fields": {"term": "What is `addBuilder` method used for?", "namespace": "open-p", "hash": -156483368, "terms": ["What is `addBuilder` method used for?", "is `addBuilder` method used for?", "`addBuilder` method used for?", "method used for?", "used for?", "for?"], "tokens": ["What", "is", "`", "addBuilder", "`", "method", "used", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1631836460", "fields": {"term": "What is Vespa helper classes?", "namespace": "open-p", "hash": 1631836460, "terms": ["What is Vespa helper classes?", "is Vespa helper classes?", "Vespa helper classes?", "helper classes?", "classes?"], "tokens": ["What", "is", "Vespa", "helper", "classes", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-131281986", "fields": {"term": "What is EchoServerClusterBuilder used for?", "namespace": "open-p", "hash": -131281986, "terms": ["What is EchoServerClusterBuilder used for?", "is EchoServerClusterBuilder used for?", "EchoServerClusterBuilder used for?", "used for?", "for?"], "tokens": ["What", "is", "EchoServerClusterBuilder", "used", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-880428175", "fields": {"term": "What is EchoServer used for?", "namespace": "open-p", "hash": -880428175, "terms": ["What is EchoServer used for?", "is EchoServer used for?", "EchoServer used for?", "used for?", "for?"], "tokens": ["What", "is", "EchoServer", "used", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::163832604", "fields": {"term": "What is EchoModel used for?", "namespace": "open-p", "hash": 163832604, "terms": ["What is EchoModel used for?", "is EchoModel used for?", "EchoModel used for?", "used for?", "for?"], "tokens": ["What", "is", "EchoModel", "used", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-226486963", "fields": {"term": "How can config overrides be parsed?", "namespace": "open-p", "hash": -226486963, "terms": ["How can config overrides be parsed?", "can config overrides be parsed?", "config overrides be parsed?", "overrides be parsed?", "be parsed?", "parsed?"], "tokens": ["How", "can", "config", "overrides", "be", "parsed", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2134564428", "fields": {"term": "What is the syntax to run echoservers on multiple nodes?", "namespace": "open-p", "hash": 2134564428, "terms": ["What is the syntax to run echoservers on multiple nodes?", "is the syntax to run echoservers on multiple nodes?", "the syntax to run echoservers on multiple nodes?", "syntax to run echoservers on multiple nodes?", "to run echoservers on multiple nodes?", "run echoservers on multiple nodes?", "echoservers on multiple nodes?", "on multiple nodes?", "multiple nodes?", "nodes?"], "tokens": ["What", "is", "the", "syntax", "to", "run", "echoservers", "on", "multiple", "nodes", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1168664559", "fields": {"term": "Does using helper classes minimize extra code?", "namespace": "open-p", "hash": -1168664559, "terms": ["Does using helper classes minimize extra code?", "using helper classes minimize extra code?", "helper classes minimize extra code?", "classes minimize extra code?", "minimize extra code?", "extra code?", "code?"], "tokens": ["Does", "using", "helper", "classes", "minimize", "extra", "code", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-276096290", "fields": {"term": "What is a model plugin?", "namespace": "open-p", "hash": -276096290, "terms": ["What is a model plugin?", "is a model plugin?", "a model plugin?", "model plugin?", "plugin?"], "tokens": ["What", "is", "a", "model", "plugin", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1697150295", "fields": {"term": "Where is the default folder for model plugins located?", "namespace": "open-p", "hash": 1697150295, "terms": ["Where is the default folder for model plugins located?", "is the default folder for model plugins located?", "the default folder for model plugins located?", "default folder for model plugins located?", "folder for model plugins located?", "for model plugins located?", "model plugins located?", "plugins located?", "located?"], "tokens": ["Where", "is", "the", "default", "folder", "for", "model", "plugins", "located", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::358309607", "fields": {"term": "How do you load a model plugin?", "namespace": "open-p", "hash": 358309607, "terms": ["How do you load a model plugin?", "do you load a model plugin?", "you load a model plugin?", "load a model plugin?", "a model plugin?", "model plugin?", "plugin?"], "tokens": ["How", "do", "you", "load", "a", "model", "plugin", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::848827036", "fields": {"term": "What is the EchoModelBuilder?", "namespace": "open-p", "hash": 848827036, "terms": ["What is the EchoModelBuilder?", "is the EchoModelBuilder?", "the EchoModelBuilder?", "EchoModelBuilder?"], "tokens": ["What", "is", "the", "EchoModelBuilder", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1785931395", "fields": {"term": "Where do you add the component for EchoModelBuilder?", "namespace": "open-p", "hash": -1785931395, "terms": ["Where do you add the component for EchoModelBuilder?", "do you add the component for EchoModelBuilder?", "you add the component for EchoModelBuilder?", "add the component for EchoModelBuilder?", "the component for EchoModelBuilder?", "component for EchoModelBuilder?", "for EchoModelBuilder?", "EchoModelBuilder?"], "tokens": ["Where", "do", "you", "add", "the", "component", "for", "EchoModelBuilder", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::226780247", "fields": {"term": "What is the name of the configuration file for model plugins?", "namespace": "open-p", "hash": 226780247, "terms": ["What is the name of the configuration file for model plugins?", "is the name of the configuration file for model plugins?", "the name of the configuration file for model plugins?", "name of the configuration file for model plugins?", "of the configuration file for model plugins?", "the configuration file for model plugins?", "configuration file for model plugins?", "file for model plugins?", "for model plugins?", "model plugins?", "plugins?"], "tokens": ["What", "is", "the", "name", "of", "the", "configuration", "file", "for", "model", "plugins", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::395072851", "fields": {"term": "Where should the model plugin be installed?", "namespace": "open-p", "hash": 395072851, "terms": ["Where should the model plugin be installed?", "should the model plugin be installed?", "the model plugin be installed?", "model plugin be installed?", "plugin be installed?", "be installed?", "installed?"], "tokens": ["Where", "should", "the", "model", "plugin", "be", "installed", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::350203103", "fields": {"term": "What is the /cluster/v2 API?", "namespace": "open-p", "hash": 350203103, "terms": ["What is the /cluster/v2 API?", "is the /cluster/v2 API?", "the /cluster/v2 API?", "/cluster/v2 API?", "API?"], "tokens": ["What", "is", "the", "/", "cluster", "/", "v2", "API", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::19484600", "fields": {"term": "What is the URL to access /cluster/v2 API?", "namespace": "open-p", "hash": 19484600, "terms": ["What is the URL to access /cluster/v2 API?", "is the URL to access /cluster/v2 API?", "the URL to access /cluster/v2 API?", "URL to access /cluster/v2 API?", "to access /cluster/v2 API?", "access /cluster/v2 API?", "/cluster/v2 API?", "API?"], "tokens": ["What", "is", "the", "URL", "to", "access", "/", "cluster", "/", "v2", "API", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::32077102", "fields": {"term": "What is the master cluster controller?", "namespace": "open-p", "hash": 32077102, "terms": ["What is the master cluster controller?", "is the master cluster controller?", "the master cluster controller?", "master cluster controller?", "cluster controller?", "controller?"], "tokens": ["What", "is", "the", "master", "cluster", "controller", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-397061238", "fields": {"term": "How to find URL to access /cluster/v2 API?", "namespace": "open-p", "hash": -397061238, "terms": ["How to find URL to access /cluster/v2 API?", "to find URL to access /cluster/v2 API?", "find URL to access /cluster/v2 API?", "URL to access /cluster/v2 API?", "to access /cluster/v2 API?", "access /cluster/v2 API?", " /cluster/v2 API?", "/cluster/v2 API?", "API?"], "tokens": ["How", "to", "find", "URL", "to", "access", "/", "cluster", "/", "v2", "API", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::697095533", "fields": {"term": "What is vespa-model-inspect?", "namespace": "open-p", "hash": 697095533, "terms": ["What is vespa-model-inspect?", "is vespa-model-inspect?", "vespa-model-inspect?"], "tokens": ["What", "is", "vespa", "model", "inspect", "?"], "corpus_count": 3, "document_count": 3}}, -{"put": "id:term:term::-858792", "fields": {"term": "What is the purpose of vespa-model-inspect?", "namespace": "open-p", "hash": -858792, "terms": ["What is the purpose of vespa-model-inspect?", "is the purpose of vespa-model-inspect?", "the purpose of vespa-model-inspect?", "purpose of vespa-model-inspect?", "of vespa-model-inspect?", "vespa-model-inspect?"], "tokens": ["What", "is", "the", "purpose", "of", "vespa", "model", "inspect", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2027083728", "fields": {"term": "How do you retrieve the cluster controller information using vespa-model-inspect?", "namespace": "open-p", "hash": -2027083728, "terms": ["How do you retrieve the cluster controller information using vespa-model-inspect?", "do you retrieve the cluster controller information using vespa-model-inspect?", "you retrieve the cluster controller information using vespa-model-inspect?", "retrieve the cluster controller information using vespa-model-inspect?", "the cluster controller information using vespa-model-inspect?", "cluster controller information using vespa-model-inspect?", "controller information using vespa-model-inspect?", "information using vespa-model-inspect?", "using vespa-model-inspect?", "vespa-model-inspect?"], "tokens": ["How", "do", "you", "retrieve", "the", "cluster", "controller", "information", "using", "vespa", "model", "inspect", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1137584622", "fields": {"term": "Where can I find examples of API usage?", "namespace": "open-p", "hash": -1137584622, "terms": ["Where can I find examples of API usage?", "can I find examples of API usage?", "I find examples of API usage?", "find examples of API usage?", "examples of API usage?", "of API usage?", "API usage?", "usage?"], "tokens": ["Where", "can", "I", "find", "examples", "of", "API", "usage", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2126758074", "fields": {"term": "What is the purpose of /cluster/v2/?", "namespace": "open-p", "hash": -2126758074, "terms": ["What is the purpose of /cluster/v2/?", "is the purpose of /cluster/v2/?", "the purpose of /cluster/v2/?", "purpose of /cluster/v2/?", "of /cluster/v2/?", "/cluster/v2/?"], "tokens": ["What", "is", "the", "purpose", "of", "/", "cluster", "/", "v2", "/?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-461028604", "fields": {"term": "What HTTP request lists cluster and nodes?", "namespace": "open-p", "hash": -461028604, "terms": ["What HTTP request lists cluster and nodes?", "HTTP request lists cluster and nodes?", "request lists cluster and nodes?", "lists cluster and nodes?", "cluster and nodes?", "and nodes?", "nodes?"], "tokens": ["What", "HTTP", "request", "lists", "cluster", "and", "nodes", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-387682522", "fields": {"term": "What does /cluster/v2/ return?", "namespace": "open-p", "hash": -387682522, "terms": ["What does /cluster/v2/ return?", "does /cluster/v2/ return?", "/cluster/v2/ return?", "return?"], "tokens": ["What", "does", "/", "cluster", "/", "v2", "/<", "cluster", ">", "return", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1964536481", "fields": {"term": "What does /cluster/v2/// do?", "namespace": "open-p", "hash": -1964536481, "terms": ["What does /cluster/v2/// do?", "does /cluster/v2/// do?", "/cluster/v2/// do?", "do?"], "tokens": ["What", "does", "/", "cluster", "/", "v2", "/<", "cluster", ">/<", "service", "type", ">/<", "node", ">", "do", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1976843845", "fields": {"term": "What are the states of nodes?", "namespace": "open-p", "hash": -1976843845, "terms": ["What are the states of nodes?", "are the states of nodes?", "the states of nodes?", "states of nodes?", "of nodes?", "nodes?"], "tokens": ["What", "are", "the", "states", "of", "nodes", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1271597020", "fields": {"term": "What are unit states?", "namespace": "open-p", "hash": -1271597020, "terms": ["What are unit states?", "are unit states?", "unit states?", "states?"], "tokens": ["What", "are", "unit", "states", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2002760437", "fields": {"term": "What is the state-disk?", "namespace": "open-p", "hash": 2002760437, "terms": ["What is the state-disk?", "is the state-disk?", "the state-disk?", "state-disk?"], "tokens": ["What", "is", "the", "state", "disk", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1633172848", "fields": {"term": "What is the state-user?", "namespace": "open-p", "hash": 1633172848, "terms": ["What is the state-user?", "is the state-user?", "the state-user?", "state-user?"], "tokens": ["What", "is", "the", "state", "user", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2116515168", "fields": {"term": "What is the state-generated?", "namespace": "open-p", "hash": 2116515168, "terms": ["What is the state-generated?", "is the state-generated?", "the state-generated?", "state-generated?"], "tokens": ["What", "is", "the", "state", "generated", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1969632654", "fields": {"term": "What is a group-spec?", "namespace": "open-p", "hash": -1969632654, "terms": ["What is a group-spec?", "is a group-spec?", "a group-spec?", "group-spec?"], "tokens": ["What", "is", "a", "group", "spec", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1667824045", "fields": {"term": "What is the recursive parameter?", "namespace": "open-p", "hash": -1667824045, "terms": ["What is the recursive parameter?", "is the recursive parameter?", "the recursive parameter?", "recursive parameter?", "parameter?"], "tokens": ["What", "is", "the", "recursive", "parameter", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::209635079", "fields": {"term": "How do I use recursive mode?", "namespace": "open-p", "hash": 209635079, "terms": ["How do I use recursive mode?", "do I use recursive mode?", "I use recursive mode?", "use recursive mode?", "recursive mode?", "mode?"], "tokens": ["How", "do", "I", "use", "recursive", "mode", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-653499833", "fields": {"term": "What does recursive=1 do?", "namespace": "open-p", "hash": -653499833, "terms": ["What does recursive=1 do?", "does recursive=1 do?", "recursive=1 do?", "do?"], "tokens": ["What", "does", "recursive", "=", "1", "do", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::874149857", "fields": {"term": "What does recursive=2 do?", "namespace": "open-p", "hash": 874149857, "terms": ["What does recursive=2 do?", "does recursive=2 do?", "recursive=2 do?", "do?"], "tokens": ["What", "does", "recursive", "=", "2", "do", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-124273738", "fields": {"term": "What happens in recursive mode?", "namespace": "open-p", "hash": -124273738, "terms": ["What happens in recursive mode?", "happens in recursive mode?", "in recursive mode?", "recursive mode?", "mode?"], "tokens": ["What", "happens", "in", "recursive", "mode", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2065695910", "fields": {"term": "How is the url-path element replaced?", "namespace": "open-p", "hash": -2065695910, "terms": ["How is the url-path element replaced?", "is the url-path element replaced?", "the url-path element replaced?", "url-path element replaced?", "element replaced?", "replaced?"], "tokens": ["How", "is", "the", "url", "path", "element", "replaced", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1613762635", "fields": {"term": "What is the spec below?", "namespace": "open-p", "hash": -1613762635, "terms": ["What is the spec below?", "is the spec below?", "the spec below?", "spec below?", "below?"], "tokens": ["What", "is", "the", "spec", "below", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::182625084", "fields": {"term": "How does recursive impact node requests?", "namespace": "open-p", "hash": 182625084, "terms": ["How does recursive impact node requests?", "does recursive impact node requests?", "recursive impact node requests?", "impact node requests?", "node requests?", "requests?"], "tokens": ["How", "does", "recursive", "impact", "node", "requests", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-649481345", "fields": {"term": "What is the response format?", "namespace": "open-p", "hash": -649481345, "terms": ["What is the response format?", "is the response format?", "the response format?", "response format?", "format?"], "tokens": ["What", "is", "the", "response", "format", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::-167236509", "fields": {"term": "What are the fields in response?", "namespace": "open-p", "hash": -167236509, "terms": ["What are the fields in response?", "are the fields in response?", "the fields in response?", "fields in response?", "in response?", "response?"], "tokens": ["What", "are", "the", "fields", "in", "response", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::65739494", "fields": {"term": "What is cluster-controller.down.count?", "namespace": "open-p", "hash": 65739494, "terms": ["What is cluster-controller.down.count?", "is cluster-controller.down.count?", "cluster-controller.down.count?"], "tokens": ["What", "is", "cluster", "controller", "down", "count", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::-894663542", "fields": {"term": "What is cluster-controller.busy-tick-time-ms?", "namespace": "open-p", "hash": -894663542, "terms": ["What is cluster-controller.busy-tick-time-ms?", "is cluster-controller.busy-tick-time-ms?", "cluster-controller.busy-tick-time-ms?"], "tokens": ["What", "is", "cluster", "controller", "busy", "tick", "time", "ms", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::209220995", "fields": {"term": "What is cluster-controller.is-master?", "namespace": "open-p", "hash": 209220995, "terms": ["What is cluster-controller.is-master?", "is cluster-controller.is-master?", "cluster-controller.is-master?"], "tokens": ["What", "is", "cluster", "controller", "is", "master", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::236797641", "fields": {"term": "What is the unit of cluster-controller.resource_usage.memory_limit?", "namespace": "open-p", "hash": 236797641, "terms": ["What is the unit of cluster-controller.resource_usage.memory_limit?", "is the unit of cluster-controller.resource_usage.memory_limit?", "the unit of cluster-controller.resource_usage.memory_limit?", "unit of cluster-controller.resource_usage.memory_limit?", "of cluster-controller.resource_usage.memory_limit?", "cluster-controller.resource_usage.memory_limit?"], "tokens": ["What", "is", "the", "unit", "of", "cluster", "controller", "resource_usage", "memory_limit", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-614310641", "fields": {"term": "What is the unit of the reindexing progress?", "namespace": "open-p", "hash": -614310641, "terms": ["What is the unit of the reindexing progress?", "is the unit of the reindexing progress?", "the unit of the reindexing progress?", "unit of the reindexing progress?", "of the reindexing progress?", "the reindexing progress?", "reindexing progress?", "progress?"], "tokens": ["What", "is", "the", "unit", "of", "the", "reindexing", "progress", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-506181682", "fields": {"term": "What is a component?", "namespace": "open-p", "hash": -506181682, "terms": ["What is a component?", "is a component?", "a component?", "component?"], "tokens": ["What", "is", "a", "component", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2038979856", "fields": {"term": "How components are configured?", "namespace": "open-p", "hash": 2038979856, "terms": ["How components are configured?", "components are configured?", "are configured?", "configured?"], "tokens": ["How", "components", "are", "configured", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-832139952", "fields": {"term": "What is a Binding?", "namespace": "open-p", "hash": -832139952, "terms": ["What is a Binding?", "is a Binding?", "a Binding?", "Binding?"], "tokens": ["What", "is", "a", "Binding", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2036079542", "fields": {"term": "What is a Server binding?", "namespace": "open-p", "hash": -2036079542, "terms": ["What is a Server binding?", "is a Server binding?", "a Server binding?", "Server binding?", "binding?"], "tokens": ["What", "is", "a", "Server", "binding", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-858681544", "fields": {"term": "What is a Client binding?", "namespace": "open-p", "hash": -858681544, "terms": ["What is a Client binding?", "is a Client binding?", "a Client binding?", "Client binding?", "binding?"], "tokens": ["What", "is", "a", "Client", "binding", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-573481279", "fields": {"term": "What is the ProcessingHandler in Vespa?", "namespace": "open-p", "hash": -573481279, "terms": ["What is the ProcessingHandler in Vespa?", "is the ProcessingHandler in Vespa?", "the ProcessingHandler in Vespa?", "ProcessingHandler in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["What", "is", "the", "ProcessingHandler", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::415130676", "fields": {"term": "What is the role of SearchHandler in Vespa?", "namespace": "open-p", "hash": 415130676, "terms": ["What is the role of SearchHandler in Vespa?", "is the role of SearchHandler in Vespa?", "the role of SearchHandler in Vespa?", "role of SearchHandler in Vespa?", "of SearchHandler in Vespa?", "SearchHandler in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["What", "is", "the", "role", "of", "SearchHandler", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-501573383", "fields": {"term": "How does a DocumentProcessingHandler work in Vespa?", "namespace": "open-p", "hash": -501573383, "terms": ["How does a DocumentProcessingHandler work in Vespa?", "does a DocumentProcessingHandler work in Vespa?", "a DocumentProcessingHandler work in Vespa?", "DocumentProcessingHandler work in Vespa?", "work in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["How", "does", "a", "DocumentProcessingHandler", "work", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::722453238", "fields": {"term": "What is the recommended way to build a custom HTTP API in Vespa?", "namespace": "open-p", "hash": 722453238, "terms": ["What is the recommended way to build a custom HTTP API in Vespa?", "is the recommended way to build a custom HTTP API in Vespa?", "the recommended way to build a custom HTTP API in Vespa?", "recommended way to build a custom HTTP API in Vespa?", "way to build a custom HTTP API in Vespa?", "to build a custom HTTP API in Vespa?", "build a custom HTTP API in Vespa?", "a custom HTTP API in Vespa?", "custom HTTP API in Vespa?", "HTTP API in Vespa?", "API in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["What", "is", "the", "recommended", "way", "to", "build", "a", "custom", "HTTP", "API", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1403263673", "fields": {"term": "What is ConfigInstance used for?", "namespace": "open-p", "hash": 1403263673, "terms": ["What is ConfigInstance used for?", "is ConfigInstance used for?", "ConfigInstance used for?", "used for?", "for?"], "tokens": ["What", "is", "ConfigInstance", "used", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::950557223", "fields": {"term": "What is DocumentAccess?", "namespace": "open-p", "hash": 950557223, "terms": ["What is DocumentAccess?", "is DocumentAccess?", "DocumentAccess?"], "tokens": ["What", "is", "DocumentAccess", "?"], "corpus_count": 3, "document_count": 3}}, -{"put": "id:term:term::-1206452982", "fields": {"term": "What is ModelsEvaluator used for?", "namespace": "open-p", "hash": -1206452982, "terms": ["What is ModelsEvaluator used for?", "is ModelsEvaluator used for?", "ModelsEvaluator used for?", "used for?", "for?"], "tokens": ["What", "is", "ModelsEvaluator", "used", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::319077768", "fields": {"term": "What is BertBaseEmbedder?", "namespace": "open-p", "hash": 319077768, "terms": ["What is BertBaseEmbedder?", "is BertBaseEmbedder?", "BertBaseEmbedder?"], "tokens": ["What", "is", "BertBaseEmbedder", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1296463065", "fields": {"term": "What is the VipStatus used for?", "namespace": "open-p", "hash": 1296463065, "terms": ["What is the VipStatus used for?", "is the VipStatus used for?", "the VipStatus used for?", "VipStatus used for?", "used for?", "for?"], "tokens": ["What", "is", "the", "VipStatus", "used", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1670901759", "fields": {"term": "What can be versioned in Vespa?", "namespace": "open-p", "hash": -1670901759, "terms": ["What can be versioned in Vespa?", "can be versioned in Vespa?", "be versioned in Vespa?", "versioned in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["What", "can", "be", "versioned", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1870207775", "fields": {"term": "What are artifact versions like?", "namespace": "open-p", "hash": 1870207775, "terms": ["What are artifact versions like?", "are artifact versions like?", "artifact versions like?", "versions like?", "like?"], "tokens": ["What", "are", "artifact", "versions", "like", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::657550861", "fields": {"term": "How are versions referred to?", "namespace": "open-p", "hash": 657550861, "terms": ["How are versions referred to?", "are versions referred to?", "versions referred to?", "referred to?", "to?"], "tokens": ["How", "are", "versions", "referred", "to", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1880074962", "fields": {"term": "What does this document explain?", "namespace": "open-p", "hash": 1880074962, "terms": ["What does this document explain?", "does this document explain?", "this document explain?", "document explain?", "explain?"], "tokens": ["What", "does", "this", "document", "explain", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::747062243", "fields": {"term": "What is the format of a version?", "namespace": "open-p", "hash": 747062243, "terms": ["What is the format of a version?", "is the format of a version?", "the format of a version?", "format of a version?", "of a version?", "a version?", "version?"], "tokens": ["What", "is", "the", "format", "of", "a", "version", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2021593194", "fields": {"term": "What is a qualifier?", "namespace": "open-p", "hash": 2021593194, "terms": ["What is a qualifier?", "is a qualifier?", "a qualifier?", "qualifier?"], "tokens": ["What", "is", "a", "qualifier", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1829812287", "fields": {"term": "What is the file name format?", "namespace": "open-p", "hash": -1829812287, "terms": ["What is the file name format?", "is the file name format?", "the file name format?", "file name format?", "name format?", "format?"], "tokens": ["What", "is", "the", "file", "name", "format", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1642019646", "fields": {"term": "What are the integers in a version?", "namespace": "open-p", "hash": 1642019646, "terms": ["What are the integers in a version?", "are the integers in a version?", "the integers in a version?", "integers in a version?", "in a version?", "a version?", "version?"], "tokens": ["What", "are", "the", "integers", "in", "a", "version", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-868575643", "fields": {"term": "When is a dash used in a file name?", "namespace": "open-p", "hash": -868575643, "terms": ["When is a dash used in a file name?", "is a dash used in a file name?", "a dash used in a file name?", "dash used in a file name?", "used in a file name?", "in a file name?", "a file name?", "file name?", "name?"], "tokens": ["When", "is", "a", "dash", "used", "in", "a", "file", "name", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1844991272", "fields": {"term": "How is a version appended to an id?", "namespace": "open-p", "hash": -1844991272, "terms": ["How is a version appended to an id?", "is a version appended to an id?", "a version appended to an id?", "version appended to an id?", "appended to an id?", "to an id?", "an id?", "id?"], "tokens": ["How", "is", "a", "version", "appended", "to", "an", "id", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::122776309", "fields": {"term": "How are versions ordered?", "namespace": "open-p", "hash": 122776309, "terms": ["How are versions ordered?", "are versions ordered?", "versions ordered?", "ordered?"], "tokens": ["How", "are", "versions", "ordered", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-266420142", "fields": {"term": "What is a lexical ordering?", "namespace": "open-p", "hash": -266420142, "terms": ["What is a lexical ordering?", "is a lexical ordering?", "a lexical ordering?", "lexical ordering?", "ordering?"], "tokens": ["What", "is", "a", "lexical", "ordering", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1125455291", "fields": {"term": "What is component referencing?", "namespace": "open-p", "hash": -1125455291, "terms": ["What is component referencing?", "is component referencing?", "component referencing?", "referencing?"], "tokens": ["What", "is", "component", "referencing", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1703059509", "fields": {"term": "What is version specification?", "namespace": "open-p", "hash": 1703059509, "terms": ["What is version specification?", "is version specification?", "version specification?", "specification?"], "tokens": ["What", "is", "version", "specification", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1729599895", "fields": {"term": "How are references resolved?", "namespace": "open-p", "hash": 1729599895, "terms": ["How are references resolved?", "are references resolved?", "references resolved?", "resolved?"], "tokens": ["How", "are", "references", "resolved", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1714200657", "fields": {"term": "What is the highest version without a qualifier?", "namespace": "open-p", "hash": 1714200657, "terms": ["What is the highest version without a qualifier?", "is the highest version without a qualifier?", "the highest version without a qualifier?", "highest version without a qualifier?", "version without a qualifier?", "without a qualifier?", "a qualifier?", "qualifier?"], "tokens": ["What", "is", "the", "highest", "version", "without", "a", "qualifier", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2103048907", "fields": {"term": "What is the rule for partially or full version specification?", "namespace": "open-p", "hash": 2103048907, "terms": ["What is the rule for partially or full version specification?", "is the rule for partially or full version specification?", "the rule for partially or full version specification?", "rule for partially or full version specification?", "for partially or full version specification?", "partially or full version specification?", "or full version specification?", "full version specification?", "version specification?", "specification?"], "tokens": ["What", "is", "the", "rule", "for", "partially", "or", "full", "version", "specification", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1040277145", "fields": {"term": "What are the versions with qualifiers matched by?", "namespace": "open-p", "hash": -1040277145, "terms": ["What are the versions with qualifiers matched by?", "are the versions with qualifiers matched by?", "the versions with qualifiers matched by?", "versions with qualifiers matched by?", "with qualifiers matched by?", "qualifiers matched by?", "matched by?", "by?"], "tokens": ["What", "are", "the", "versions", "with", "qualifiers", "matched", "by", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1098537521", "fields": {"term": "How does a reference resolve to test qualified version?", "namespace": "open-p", "hash": -1098537521, "terms": ["How does a reference resolve to test qualified version?", "does a reference resolve to test qualified version?", "a reference resolve to test qualified version?", "reference resolve to test qualified version?", "resolve to test qualified version?", "to test qualified version?", "test qualified version?", "qualified version?", "version?"], "tokens": ["How", "does", "a", "reference", "resolve", "to", "test", "qualified", "version", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::121131127", "fields": {"term": "What kind references will not resolve?", "namespace": "open-p", "hash": 121131127, "terms": ["What kind references will not resolve?", "kind references will not resolve?", "references will not resolve?", "will not resolve?", "not resolve?", "resolve?"], "tokens": ["What", "kind", "references", "will", "not", "resolve", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1826995115", "fields": {"term": "What is the need for merging references?", "namespace": "open-p", "hash": -1826995115, "terms": ["What is the need for merging references?", "is the need for merging references?", "the need for merging references?", "need for merging references?", "for merging references?", "merging references?", "references?"], "tokens": ["What", "is", "the", "need", "for", "merging", "references", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-463968774", "fields": {"term": "What are compatible version references?", "namespace": "open-p", "hash": -463968774, "terms": ["What are compatible version references?", "are compatible version references?", "compatible version references?", "version references?", "references?"], "tokens": ["What", "are", "compatible", "version", "references", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1254718471", "fields": {"term": "What are conflicting version references?", "namespace": "open-p", "hash": -1254718471, "terms": ["What are conflicting version references?", "are conflicting version references?", "conflicting version references?", "version references?", "references?"], "tokens": ["What", "are", "conflicting", "version", "references", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1588038438", "fields": {"term": "What is setFieldValue() used for?", "namespace": "open-p", "hash": -1588038438, "terms": ["What is setFieldValue() used for?", "is setFieldValue() used for?", "setFieldValue() used for?", "used for?", "for?"], "tokens": ["What", "is", "setFieldValue", "()", "used", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-746969558", "fields": {"term": "What is getFieldValue() used for?", "namespace": "open-p", "hash": -746969558, "terms": ["What is getFieldValue() used for?", "is getFieldValue() used for?", "getFieldValue() used for?", "used for?", "for?"], "tokens": ["What", "is", "getFieldValue", "()", "used", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1516209583", "fields": {"term": "How to generate a concrete document type?", "namespace": "open-p", "hash": 1516209583, "terms": ["How to generate a concrete document type?", "to generate a concrete document type?", "generate a concrete document type?", "a concrete document type?", "concrete document type?", "document type?", "type?"], "tokens": ["How", "to", "generate", "a", "concrete", "document", "type", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-18302204", "fields": {"term": "What is a Document subclass?", "namespace": "open-p", "hash": -18302204, "terms": ["What is a Document subclass?", "is a Document subclass?", "a Document subclass?", "Document subclass?", "subclass?"], "tokens": ["What", "is", "a", "Document", "subclass", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2003027713", "fields": {"term": "What is single inheritance?", "namespace": "open-p", "hash": -2003027713, "terms": ["What is single inheritance?", "is single inheritance?", "single inheritance?", "inheritance?"], "tokens": ["What", "is", "single", "inheritance", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1874183722", "fields": {"term": "What happens with multiple inheritance?", "namespace": "open-p", "hash": -1874183722, "terms": ["What happens with multiple inheritance?", "happens with multiple inheritance?", "with multiple inheritance?", "multiple inheritance?", "inheritance?"], "tokens": ["What", "happens", "with", "multiple", "inheritance", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1618926919", "fields": {"term": "What is the Java type for myDoc?", "namespace": "open-p", "hash": 1618926919, "terms": ["What is the Java type for myDoc?", "is the Java type for myDoc?", "the Java type for myDoc?", "Java type for myDoc?", "type for myDoc?", "for myDoc?", "myDoc?"], "tokens": ["What", "is", "the", "Java", "type", "for", "myDoc", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::411843900", "fields": {"term": "Where can I find inheritance examples in the documentation?", "namespace": "open-p", "hash": 411843900, "terms": ["Where can I find inheritance examples in the documentation?", "can I find inheritance examples in the documentation?", "I find inheritance examples in the documentation?", "find inheritance examples in the documentation?", "inheritance examples in the documentation?", "examples in the documentation?", "in the documentation?", "the documentation?", "documentation?"], "tokens": ["Where", "can", "I", "find", "inheritance", "examples", "in", "the", "documentation", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::642772327", "fields": {"term": "What are concrete types used for?", "namespace": "open-p", "hash": 642772327, "terms": ["What are concrete types used for?", "are concrete types used for?", "concrete types used for?", "types used for?", "used for?", "for?"], "tokens": ["What", "are", "concrete", "types", "used", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1725164499", "fields": {"term": "How to specify document type?", "namespace": "open-p", "hash": 1725164499, "terms": ["How to specify document type?", "to specify document type?", "specify document type?", "document type?", "type?"], "tokens": ["How", "to", "specify", "document", "type", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1279463241", "fields": {"term": "What is ConcreteDocumentFactory?", "namespace": "open-p", "hash": -1279463241, "terms": ["What is ConcreteDocumentFactory?", "is ConcreteDocumentFactory?", "ConcreteDocumentFactory?"], "tokens": ["What", "is", "ConcreteDocumentFactory", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2057064301", "fields": {"term": "How is ConcreteDocumentFactory initialized?", "namespace": "open-p", "hash": 2057064301, "terms": ["How is ConcreteDocumentFactory initialized?", "is ConcreteDocumentFactory initialized?", "ConcreteDocumentFactory initialized?", "initialized?"], "tokens": ["How", "is", "ConcreteDocumentFactory", "initialized", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1821689707", "fields": {"term": "What is the use of ConcreteDocumentFactory?", "namespace": "open-p", "hash": -1821689707, "terms": ["What is the use of ConcreteDocumentFactory?", "is the use of ConcreteDocumentFactory?", "the use of ConcreteDocumentFactory?", "use of ConcreteDocumentFactory?", "of ConcreteDocumentFactory?", "ConcreteDocumentFactory?"], "tokens": ["What", "is", "the", "use", "of", "ConcreteDocumentFactory", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::472594591", "fields": {"term": "What are copy constructors in Vespa?", "namespace": "open-p", "hash": 472594591, "terms": ["What are copy constructors in Vespa?", "are copy constructors in Vespa?", "copy constructors in Vespa?", "constructors in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["What", "are", "copy", "constructors", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::493890076", "fields": {"term": "What happens when getFieldValue() is called on concrete types?", "namespace": "open-p", "hash": 493890076, "terms": ["What happens when getFieldValue() is called on concrete types?", "happens when getFieldValue() is called on concrete types?", "when getFieldValue() is called on concrete types?", "getFieldValue() is called on concrete types?", "is called on concrete types?", "called on concrete types?", "on concrete types?", "concrete types?", "types?"], "tokens": ["What", "happens", "when", "getFieldValue", "()", "is", "called", "on", "concrete", "types", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-381054166", "fields": {"term": "How are accessor and mutator methods used on concrete types?", "namespace": "open-p", "hash": -381054166, "terms": ["How are accessor and mutator methods used on concrete types?", "are accessor and mutator methods used on concrete types?", "accessor and mutator methods used on concrete types?", "and mutator methods used on concrete types?", "mutator methods used on concrete types?", "methods used on concrete types?", "used on concrete types?", "on concrete types?", "concrete types?", "types?"], "tokens": ["How", "are", "accessor", "and", "mutator", "methods", "used", "on", "concrete", "types", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-187290870", "fields": {"term": "What is the syntax for initializing an object with ConcreteDocumentFactory?", "namespace": "open-p", "hash": -187290870, "terms": ["What is the syntax for initializing an object with ConcreteDocumentFactory?", "is the syntax for initializing an object with ConcreteDocumentFactory?", "the syntax for initializing an object with ConcreteDocumentFactory?", "syntax for initializing an object with ConcreteDocumentFactory?", "for initializing an object with ConcreteDocumentFactory?", "initializing an object with ConcreteDocumentFactory?", "an object with ConcreteDocumentFactory?", "object with ConcreteDocumentFactory?", "with ConcreteDocumentFactory?", "ConcreteDocumentFactory?"], "tokens": ["What", "is", "the", "syntax", "for", "initializing", "an", "object", "with", "ConcreteDocumentFactory", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1774515300", "fields": {"term": "How does setFieldValue() work on concrete types?", "namespace": "open-p", "hash": -1774515300, "terms": ["How does setFieldValue() work on concrete types?", "does setFieldValue() work on concrete types?", "setFieldValue() work on concrete types?", "work on concrete types?", "on concrete types?", "concrete types?", "types?"], "tokens": ["How", "does", "setFieldValue", "()", "work", "on", "concrete", "types", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1118155289", "fields": {"term": "What is a document processor?", "namespace": "open-p", "hash": 1118155289, "terms": ["What is a document processor?", "is a document processor?", "a document processor?", "document processor?", "processor?"], "tokens": ["What", "is", "a", "document", "processor", "?"], "corpus_count": 3, "document_count": 3}}, -{"put": "id:term:term::-51896359", "fields": {"term": "What is a concrete document type?", "namespace": "open-p", "hash": -51896359, "terms": ["What is a concrete document type?", "is a concrete document type?", "a concrete document type?", "concrete document type?", "document type?", "type?"], "tokens": ["What", "is", "a", "concrete", "document", "type", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::644897240", "fields": {"term": "What does DocumentPut do?", "namespace": "open-p", "hash": 644897240, "terms": ["What does DocumentPut do?", "does DocumentPut do?", "DocumentPut do?", "do?"], "tokens": ["What", "does", "DocumentPut", "do", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-937572895", "fields": {"term": "What is the Progress class?", "namespace": "open-p", "hash": -937572895, "terms": ["What is the Progress class?", "is the Progress class?", "the Progress class?", "Progress class?", "class?"], "tokens": ["What", "is", "the", "Progress", "class", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-453986280", "fields": {"term": "Are concrete document types supported for updates?", "namespace": "open-p", "hash": -453986280, "terms": ["Are concrete document types supported for updates?", "concrete document types supported for updates?", "document types supported for updates?", "types supported for updates?", "supported for updates?", "for updates?", "updates?"], "tokens": ["Are", "concrete", "document", "types", "supported", "for", "updates", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::576843637", "fields": {"term": "What is an example of a concrete document type?", "namespace": "open-p", "hash": 576843637, "terms": ["What is an example of a concrete document type?", "is an example of a concrete document type?", "an example of a concrete document type?", "example of a concrete document type?", "of a concrete document type?", "a concrete document type?", "concrete document type?", "document type?", "type?"], "tokens": ["What", "is", "an", "example", "of", "a", "concrete", "document", "type", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1523098230", "fields": {"term": "What is a config file?", "namespace": "open-p", "hash": 1523098230, "terms": ["What is a config file?", "is a config file?", "a config file?", "config file?", "file?"], "tokens": ["What", "is", "a", "config", "file", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::71684022", "fields": {"term": "What is a configurable component?", "namespace": "open-p", "hash": 71684022, "terms": ["What is a configurable component?", "is a configurable component?", "a configurable component?", "configurable component?", "component?"], "tokens": ["What", "is", "a", "configurable", "component", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1404157501", "fields": {"term": "What is a config definition?", "namespace": "open-p", "hash": 1404157501, "terms": ["What is a config definition?", "is a config definition?", "a config definition?", "config definition?", "definition?"], "tokens": ["What", "is", "a", "config", "definition", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1138940706", "fields": {"term": "What is the file suffix for config definition?", "namespace": "open-p", "hash": -1138940706, "terms": ["What is the file suffix for config definition?", "is the file suffix for config definition?", "the file suffix for config definition?", "file suffix for config definition?", "suffix for config definition?", "for config definition?", "config definition?", "definition?"], "tokens": ["What", "is", "the", "file", "suffix", "for", "config", "definition", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1607170997", "fields": {"term": "Where are Vespa's builtin *.def* files located?", "namespace": "open-p", "hash": -1607170997, "terms": ["Where are Vespa's builtin *.def* files located?", "are Vespa's builtin *.def* files located?", "Vespa's builtin *.def* files located?", "builtin *.def* files located?", "*.def* files located?", "files located?", "located?"], "tokens": ["Where", "are", "Vespa", "'", "s", "builtin", "*", "def", "*", "files", "located", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-732442929", "fields": {"term": "What is a package?", "namespace": "open-p", "hash": -732442929, "terms": ["What is a package?", "is a package?", "a package?", "package?"], "tokens": ["What", "is", "a", "package", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1835552369", "fields": {"term": "Where should the package statement be placed?", "namespace": "open-p", "hash": 1835552369, "terms": ["Where should the package statement be placed?", "should the package statement be placed?", "the package statement be placed?", "package statement be placed?", "statement be placed?", "be placed?", "placed?"], "tokens": ["Where", "should", "the", "package", "statement", "be", "placed", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::204231371", "fields": {"term": "Why is it recommended to use a separate package for each bundle?", "namespace": "open-p", "hash": 204231371, "terms": ["Why is it recommended to use a separate package for each bundle?", "is it recommended to use a separate package for each bundle?", "it recommended to use a separate package for each bundle?", "recommended to use a separate package for each bundle?", "to use a separate package for each bundle?", "use a separate package for each bundle?", "a separate package for each bundle?", "separate package for each bundle?", "package for each bundle?", "for each bundle?", "each bundle?", "bundle?"], "tokens": ["Why", "is", "it", "recommended", "to", "use", "a", "separate", "package", "for", "each", "bundle", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::905303162", "fields": {"term": "Who should use separate package for each bundle?", "namespace": "open-p", "hash": 905303162, "terms": ["Who should use separate package for each bundle?", "should use separate package for each bundle?", "use separate package for each bundle?", "separate package for each bundle?", "package for each bundle?", "for each bundle?", "each bundle?", "bundle?"], "tokens": ["Who", "should", "use", "separate", "package", "for", "each", "bundle", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-805978109", "fields": {"term": "How to define a package?", "namespace": "open-p", "hash": -805978109, "terms": ["How to define a package?", "to define a package?", "define a package?", "a package?", "package?"], "tokens": ["How", "to", "define", "a", "package", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-142636449", "fields": {"term": "What characters can be used in a package name?", "namespace": "open-p", "hash": -142636449, "terms": ["What characters can be used in a package name?", "characters can be used in a package name?", "can be used in a package name?", "be used in a package name?", "used in a package name?", "in a package name?", "a package name?", "package name?", "name?"], "tokens": ["What", "characters", "can", "be", "used", "in", "a", "package", "name", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-448644177", "fields": {"term": "What is a config definition file?", "namespace": "open-p", "hash": -448644177, "terms": ["What is a config definition file?", "is a config definition file?", "a config definition file?", "config definition file?", "definition file?", "file?"], "tokens": ["What", "is", "a", "config", "definition", "file", "?"], "corpus_count": 3, "document_count": 3}}, -{"put": "id:term:term::-807504947", "fields": {"term": "What is the recommended parameter name style?", "namespace": "open-p", "hash": -807504947, "terms": ["What is the recommended parameter name style?", "is the recommended parameter name style?", "the recommended parameter name style?", "recommended parameter name style?", "parameter name style?", "name style?", "style?"], "tokens": ["What", "is", "the", "recommended", "parameter", "name", "style", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1966386444", "fields": {"term": "What does a parameter definition have?", "namespace": "open-p", "hash": -1966386444, "terms": ["What does a parameter definition have?", "does a parameter definition have?", "a parameter definition have?", "parameter definition have?", "definition have?", "have?"], "tokens": ["What", "does", "a", "parameter", "definition", "have", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-222229485", "fields": {"term": "What is the purpose of [range=[min,max]]?", "namespace": "open-p", "hash": -222229485, "terms": ["What is the purpose of [range=[min,max]]?", "is the purpose of [range=[min,max]]?", "the purpose of [range=[min,max]]?", "purpose of [range=[min,max]]?", "of [range=[min,max]]?", "[range=[min,max]]?"], "tokens": ["What", "is", "the", "purpose", "of", "[", "range", "=[", "min", "max", "]]?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-356912836", "fields": {"term": "What is a path?", "namespace": "open-p", "hash": -356912836, "terms": ["What is a path?", "is a path?", "a path?", "path?"], "tokens": ["What", "is", "a", "path", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::123643578", "fields": {"term": "What is an enum?", "namespace": "open-p", "hash": 123643578, "terms": ["What is an enum?", "is an enum?", "an enum?", "enum?"], "tokens": ["What", "is", "an", "enum", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::891356025", "fields": {"term": "What is a struct in Vespa?", "namespace": "open-p", "hash": 891356025, "terms": ["What is a struct in Vespa?", "is a struct in Vespa?", "a struct in Vespa?", "struct in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["What", "is", "a", "struct", "in", "Vespa", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::988854753", "fields": {"term": "How do you declare a struct?", "namespace": "open-p", "hash": 988854753, "terms": ["How do you declare a struct?", "do you declare a struct?", "you declare a struct?", "declare a struct?", "a struct?", "struct?"], "tokens": ["How", "do", "you", "declare", "a", "struct", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-974226489", "fields": {"term": "What is the syntax for declaring a struct?", "namespace": "open-p", "hash": -974226489, "terms": ["What is the syntax for declaring a struct?", "is the syntax for declaring a struct?", "the syntax for declaring a struct?", "syntax for declaring a struct?", "for declaring a struct?", "declaring a struct?", "a struct?", "struct?"], "tokens": ["What", "is", "the", "syntax", "for", "declaring", "a", "struct", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1901400911", "fields": {"term": "What are the benefits of using a struct?", "namespace": "open-p", "hash": -1901400911, "terms": ["What are the benefits of using a struct?", "are the benefits of using a struct?", "the benefits of using a struct?", "benefits of using a struct?", "of using a struct?", "using a struct?", "a struct?", "struct?"], "tokens": ["What", "are", "the", "benefits", "of", "using", "a", "struct", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-342116149", "fields": {"term": "What is the purpose of a struct?", "namespace": "open-p", "hash": -342116149, "terms": ["What is the purpose of a struct?", "is the purpose of a struct?", "the purpose of a struct?", "purpose of a struct?", "of a struct?", "a struct?", "struct?"], "tokens": ["What", "is", "the", "purpose", "of", "a", "struct", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1205845014", "fields": {"term": "What can be included in a Vespa struct?", "namespace": "open-p", "hash": 1205845014, "terms": ["What can be included in a Vespa struct?", "can be included in a Vespa struct?", "be included in a Vespa struct?", "included in a Vespa struct?", "in a Vespa struct?", "a Vespa struct?", "Vespa struct?", "struct?"], "tokens": ["What", "can", "be", "included", "in", "a", "Vespa", "struct", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1769974452", "fields": {"term": "How does Vespa handle structs?", "namespace": "open-p", "hash": -1769974452, "terms": ["How does Vespa handle structs?", "does Vespa handle structs?", "Vespa handle structs?", "handle structs?", "structs?"], "tokens": ["How", "does", "Vespa", "handle", "structs", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1098448774", "fields": {"term": "What is the basic syntax for declaring a Vespa struct?", "namespace": "open-p", "hash": 1098448774, "terms": ["What is the basic syntax for declaring a Vespa struct?", "is the basic syntax for declaring a Vespa struct?", "the basic syntax for declaring a Vespa struct?", "basic syntax for declaring a Vespa struct?", "syntax for declaring a Vespa struct?", "for declaring a Vespa struct?", "declaring a Vespa struct?", "a Vespa struct?", "Vespa struct?", "struct?"], "tokens": ["What", "is", "the", "basic", "syntax", "for", "declaring", "a", "Vespa", "struct", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1954895965", "fields": {"term": "What is an array?", "namespace": "open-p", "hash": 1954895965, "terms": ["What is an array?", "is an array?", "an array?", "array?"], "tokens": ["What", "is", "an", "array", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::-1496861545", "fields": {"term": "How are arrays declared?", "namespace": "open-p", "hash": -1496861545, "terms": ["How are arrays declared?", "are arrays declared?", "arrays declared?", "declared?"], "tokens": ["How", "are", "arrays", "declared", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::354560424", "fields": {"term": "What can arrays contain?", "namespace": "open-p", "hash": 354560424, "terms": ["What can arrays contain?", "can arrays contain?", "arrays contain?", "contain?"], "tokens": ["What", "can", "arrays", "contain", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1685449869", "fields": {"term": "Can arrays have default values?", "namespace": "open-p", "hash": 1685449869, "terms": ["Can arrays have default values?", "arrays have default values?", "have default values?", "default values?", "values?"], "tokens": ["Can", "arrays", "have", "default", "values", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1542710927", "fields": {"term": "What is the structure of a complex array?", "namespace": "open-p", "hash": -1542710927, "terms": ["What is the structure of a complex array?", "is the structure of a complex array?", "the structure of a complex array?", "structure of a complex array?", "of a complex array?", "a complex array?", "complex array?", "array?"], "tokens": ["What", "is", "the", "structure", "of", "a", "complex", "array", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1962334605", "fields": {"term": "What is not allowed in an array?", "namespace": "open-p", "hash": -1962334605, "terms": ["What is not allowed in an array?", "is not allowed in an array?", "not allowed in an array?", "allowed in an array?", "in an array?", "an array?", "array?"], "tokens": ["What", "is", "not", "allowed", "in", "an", "array", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1181508663", "fields": {"term": "What is the difference between an array with simple values and one with children?", "namespace": "open-p", "hash": -1181508663, "terms": ["What is the difference between an array with simple values and one with children?", "is the difference between an array with simple values and one with children?", "the difference between an array with simple values and one with children?", "difference between an array with simple values and one with children?", "between an array with simple values and one with children?", "an array with simple values and one with children?", "array with simple values and one with children?", "with simple values and one with children?", "simple values and one with children?", "values and one with children?", "and one with children?", "one with children?", "with children?", "children?"], "tokens": ["What", "is", "the", "difference", "between", "an", "array", "with", "simple", "values", "and", "one", "with", "children", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::415372666", "fields": {"term": "How are maps declared in Vespa?", "namespace": "open-p", "hash": 415372666, "terms": ["How are maps declared in Vespa?", "are maps declared in Vespa?", "maps declared in Vespa?", "declared in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["How", "are", "maps", "declared", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::804554167", "fields": {"term": "What are four types of elements in services.xml?", "namespace": "open-p", "hash": 804554167, "terms": ["What are four types of elements in services.xml?", "are four types of elements in services.xml?", "four types of elements in services.xml?", "types of elements in services.xml?", "of elements in services.xml?", "elements in services.xml?", "in services.xml?", "services.xml?"], "tokens": ["What", "are", "four", "types", "of", "elements", "in", "services", "xml", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1242648675", "fields": {"term": "What is the purpose of the config element?", "namespace": "open-p", "hash": 1242648675, "terms": ["What is the purpose of the config element?", "is the purpose of the config element?", "the purpose of the config element?", "purpose of the config element?", "of the config element?", "the config element?", "config element?", "element?"], "tokens": ["What", "is", "the", "purpose", "of", "the", "config", "element", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::645073847", "fields": {"term": "What is a dedicated config element?", "namespace": "open-p", "hash": 645073847, "terms": ["What is a dedicated config element?", "is a dedicated config element?", "a dedicated config element?", "dedicated config element?", "config element?", "element?"], "tokens": ["What", "is", "a", "dedicated", "config", "element", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::753724811", "fields": {"term": "What is a generic config element?", "namespace": "open-p", "hash": 753724811, "terms": ["What is a generic config element?", "is a generic config element?", "a generic config element?", "generic config element?", "config element?", "element?"], "tokens": ["What", "is", "a", "generic", "config", "element", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::358806719", "fields": {"term": "What is the priority of config settings in services.xml?", "namespace": "open-p", "hash": 358806719, "terms": ["What is the priority of config settings in services.xml?", "is the priority of config settings in services.xml?", "the priority of config settings in services.xml?", "priority of config settings in services.xml?", "of config settings in services.xml?", "config settings in services.xml?", "settings in services.xml?", "in services.xml?", "services.xml?"], "tokens": ["What", "is", "the", "priority", "of", "config", "settings", "in", "services", "xml", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::283263366", "fields": {"term": "What is the syntax for setting values for a config in services.xml?", "namespace": "open-p", "hash": 283263366, "terms": ["What is the syntax for setting values for a config in services.xml?", "is the syntax for setting values for a config in services.xml?", "the syntax for setting values for a config in services.xml?", "syntax for setting values for a config in services.xml?", "for setting values for a config in services.xml?", "setting values for a config in services.xml?", "values for a config in services.xml?", "for a config in services.xml?", "a config in services.xml?", "config in services.xml?", "in services.xml?", "services.xml?"], "tokens": ["What", "is", "the", "syntax", "for", "setting", "values", "for", "a", "config", "in", "services", "xml", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-405487735", "fields": {"term": "What happens if a value is not set for a parameter in services.xml?", "namespace": "open-p", "hash": -405487735, "terms": ["What happens if a value is not set for a parameter in services.xml?", "happens if a value is not set for a parameter in services.xml?", "if a value is not set for a parameter in services.xml?", "a value is not set for a parameter in services.xml?", "value is not set for a parameter in services.xml?", "is not set for a parameter in services.xml?", "not set for a parameter in services.xml?", "set for a parameter in services.xml?", "for a parameter in services.xml?", "a parameter in services.xml?", "parameter in services.xml?", "in services.xml?", "services.xml?"], "tokens": ["What", "happens", "if", "a", "value", "is", "not", "set", "for", "a", "parameter", "in", "services", "xml", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::240387517", "fields": {"term": "How to assign values to arrays?", "namespace": "open-p", "hash": 240387517, "terms": ["How to assign values to arrays?", "to assign values to arrays?", "assign values to arrays?", "values to arrays?", "to arrays?", "arrays?"], "tokens": ["How", "to", "assign", "values", "to", "arrays", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-331913736", "fields": {"term": "What is the `` element?", "namespace": "open-p", "hash": -331913736, "terms": ["What is the `` element?", "is the `` element?", "the `` element?", "`` element?", "element?"], "tokens": ["What", "is", "the", "`<", "item", ">`", "element", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::947010067", "fields": {"term": "What is Vespa Configuration?", "namespace": "open-p", "hash": 947010067, "terms": ["What is Vespa Configuration?", "is Vespa Configuration?", "Vespa Configuration?", "Configuration?"], "tokens": ["What", "is", "Vespa", "Configuration", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1879767459", "fields": {"term": "What are the three parts of node configuration?", "namespace": "open-p", "hash": -1879767459, "terms": ["What are the three parts of node configuration?", "are the three parts of node configuration?", "the three parts of node configuration?", "three parts of node configuration?", "parts of node configuration?", "of node configuration?", "node configuration?", "configuration?"], "tokens": ["What", "are", "the", "three", "parts", "of", "node", "configuration", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::454878801", "fields": {"term": "What is the nature of the interface between delivery and assembly subsystems?", "namespace": "open-p", "hash": 454878801, "terms": ["What is the nature of the interface between delivery and assembly subsystems?", "is the nature of the interface between delivery and assembly subsystems?", "the nature of the interface between delivery and assembly subsystems?", "nature of the interface between delivery and assembly subsystems?", "of the interface between delivery and assembly subsystems?", "the interface between delivery and assembly subsystems?", "interface between delivery and assembly subsystems?", "between delivery and assembly subsystems?", "delivery and assembly subsystems?", "and assembly subsystems?", "assembly subsystems?", "subsystems?"], "tokens": ["What", "is", "the", "nature", "of", "the", "interface", "between", "delivery", "and", "assembly", "subsystems", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1261883637", "fields": {"term": "What is configuration delivery?", "namespace": "open-p", "hash": 1261883637, "terms": ["What is configuration delivery?", "is configuration delivery?", "configuration delivery?", "delivery?"], "tokens": ["What", "is", "configuration", "delivery", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::1535114039", "fields": {"term": "What is configuration assembly?", "namespace": "open-p", "hash": 1535114039, "terms": ["What is configuration assembly?", "is configuration assembly?", "configuration assembly?", "assembly?"], "tokens": ["What", "is", "configuration", "assembly", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-388434932", "fields": {"term": "How does Vespa Configuration manage node variations?", "namespace": "open-p", "hash": -388434932, "terms": ["How does Vespa Configuration manage node variations?", "does Vespa Configuration manage node variations?", "Vespa Configuration manage node variations?", "Configuration manage node variations?", "manage node variations?", "node variations?", "variations?"], "tokens": ["How", "does", "Vespa", "Configuration", "manage", "node", "variations", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1705949189", "fields": {"term": "What are the tool provided for package management?", "namespace": "open-p", "hash": 1705949189, "terms": ["What are the tool provided for package management?", "are the tool provided for package management?", "the tool provided for package management?", "tool provided for package management?", "provided for package management?", "for package management?", "package management?", "management?"], "tokens": ["What", "are", "the", "tool", "provided", "for", "package", "management", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::67113453", "fields": {"term": "What is config assembly?", "namespace": "open-p", "hash": 67113453, "terms": ["What is config assembly?", "is config assembly?", "config assembly?", "assembly?"], "tokens": ["What", "is", "config", "assembly", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2089501722", "fields": {"term": "What does config assembly involve?", "namespace": "open-p", "hash": -2089501722, "terms": ["What does config assembly involve?", "does config assembly involve?", "config assembly involve?", "assembly involve?", "involve?"], "tokens": ["What", "does", "config", "assembly", "involve", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::451036323", "fields": {"term": "What is the role of Vespa operations in config assembly?", "namespace": "open-p", "hash": 451036323, "terms": ["What is the role of Vespa operations in config assembly?", "is the role of Vespa operations in config assembly?", "the role of Vespa operations in config assembly?", "role of Vespa operations in config assembly?", "of Vespa operations in config assembly?", "Vespa operations in config assembly?", "operations in config assembly?", "in config assembly?", "config assembly?", "assembly?"], "tokens": ["What", "is", "the", "role", "of", "Vespa", "operations", "in", "config", "assembly", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::163208826", "fields": {"term": "What are the goals of configuration delivery?", "namespace": "open-p", "hash": 163208826, "terms": ["What are the goals of configuration delivery?", "are the goals of configuration delivery?", "the goals of configuration delivery?", "goals of configuration delivery?", "of configuration delivery?", "configuration delivery?", "delivery?"], "tokens": ["What", "are", "the", "goals", "of", "configuration", "delivery", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1293167596", "fields": {"term": "What are the aspects of configuration delivery?", "namespace": "open-p", "hash": -1293167596, "terms": ["What are the aspects of configuration delivery?", "are the aspects of configuration delivery?", "the aspects of configuration delivery?", "aspects of configuration delivery?", "of configuration delivery?", "configuration delivery?", "delivery?"], "tokens": ["What", "are", "the", "aspects", "of", "configuration", "delivery", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1327138771", "fields": {"term": "What is a configuration?", "namespace": "open-p", "hash": 1327138771, "terms": ["What is a configuration?", "is a configuration?", "a configuration?", "configuration?"], "tokens": ["What", "is", "a", "configuration", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1650764588", "fields": {"term": "What is a type definition?", "namespace": "open-p", "hash": -1650764588, "terms": ["What is a type definition?", "is a type definition?", "a type definition?", "type definition?", "definition?"], "tokens": ["What", "is", "a", "type", "definition", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1075658764", "fields": {"term": "What is a namespace?", "namespace": "open-p", "hash": -1075658764, "terms": ["What is a namespace?", "is a namespace?", "a namespace?", "namespace?"], "tokens": ["What", "is", "a", "namespace", "?"], "corpus_count": 3, "document_count": 3}}, -{"put": "id:term:term::-1385298377", "fields": {"term": "What is a nested configuration?", "namespace": "open-p", "hash": -1385298377, "terms": ["What is a nested configuration?", "is a nested configuration?", "a nested configuration?", "nested configuration?", "configuration?"], "tokens": ["What", "is", "a", "nested", "configuration", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-704110369", "fields": {"term": "What does a type definition define?", "namespace": "open-p", "hash": -704110369, "terms": ["What does a type definition define?", "does a type definition define?", "a type definition define?", "type definition define?", "definition define?", "define?"], "tokens": ["What", "does", "a", "type", "definition", "define", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-506795541", "fields": {"term": "What is the syntax for a configuration object?", "namespace": "open-p", "hash": -506795541, "terms": ["What is the syntax for a configuration object?", "is the syntax for a configuration object?", "the syntax for a configuration object?", "syntax for a configuration object?", "for a configuration object?", "a configuration object?", "configuration object?", "object?"], "tokens": ["What", "is", "the", "syntax", "for", "a", "configuration", "object", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1728136206", "fields": {"term": "What is myProperty?", "namespace": "open-p", "hash": -1728136206, "terms": ["What is myProperty?", "is myProperty?", "myProperty?"], "tokens": ["What", "is", "myProperty", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1428677885", "fields": {"term": "How do components request configs?", "namespace": "open-p", "hash": -1428677885, "terms": ["How do components request configs?", "do components request configs?", "components request configs?", "request configs?", "configs?"], "tokens": ["How", "do", "components", "request", "configs", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1371964890", "fields": {"term": "What is the config delivery mechanism responsible for?", "namespace": "open-p", "hash": 1371964890, "terms": ["What is the config delivery mechanism responsible for?", "is the config delivery mechanism responsible for?", "the config delivery mechanism responsible for?", "config delivery mechanism responsible for?", "delivery mechanism responsible for?", "mechanism responsible for?", "responsible for?", "for?"], "tokens": ["What", "is", "the", "config", "delivery", "mechanism", "responsible", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1244081564", "fields": {"term": "What identifies a config subscription?", "namespace": "open-p", "hash": -1244081564, "terms": ["What identifies a config subscription?", "identifies a config subscription?", "a config subscription?", "config subscription?", "subscription?"], "tokens": ["What", "identifies", "a", "config", "subscription", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2016898639", "fields": {"term": "What is a config proxy?", "namespace": "open-p", "hash": 2016898639, "terms": ["What is a config proxy?", "is a config proxy?", "a config proxy?", "config proxy?", "proxy?"], "tokens": ["What", "is", "a", "config", "proxy", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::-117870116", "fields": {"term": "How does the proxy resolve config requests?", "namespace": "open-p", "hash": -117870116, "terms": ["How does the proxy resolve config requests?", "does the proxy resolve config requests?", "the proxy resolve config requests?", "proxy resolve config requests?", "resolve config requests?", "config requests?", "requests?"], "tokens": ["How", "does", "the", "proxy", "resolve", "config", "requests", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-211886585", "fields": {"term": "What is the failover implementation for config subscriptions?", "namespace": "open-p", "hash": -211886585, "terms": ["What is the failover implementation for config subscriptions?", "is the failover implementation for config subscriptions?", "the failover implementation for config subscriptions?", "failover implementation for config subscriptions?", "implementation for config subscriptions?", "for config subscriptions?", "config subscriptions?", "subscriptions?"], "tokens": ["What", "is", "the", "failover", "implementation", "for", "config", "subscriptions", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-948400839", "fields": {"term": "What is the benefit of not storing configs locally on nodes?", "namespace": "open-p", "hash": -948400839, "terms": ["What is the benefit of not storing configs locally on nodes?", "is the benefit of not storing configs locally on nodes?", "the benefit of not storing configs locally on nodes?", "benefit of not storing configs locally on nodes?", "of not storing configs locally on nodes?", "not storing configs locally on nodes?", "storing configs locally on nodes?", "configs locally on nodes?", "locally on nodes?", "on nodes?", "nodes?"], "tokens": ["What", "is", "the", "benefit", "of", "not", "storing", "configs", "locally", "on", "nodes", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1490257262", "fields": {"term": "What is the url config type used for?", "namespace": "open-p", "hash": -1490257262, "terms": ["What is the url config type used for?", "is the url config type used for?", "the url config type used for?", "url config type used for?", "config type used for?", "type used for?", "used for?", "for?"], "tokens": ["What", "is", "the", "url", "config", "type", "used", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1883455555", "fields": {"term": "What is config-sentinel process?", "namespace": "open-p", "hash": -1883455555, "terms": ["What is config-sentinel process?", "is config-sentinel process?", "config-sentinel process?", "process?"], "tokens": ["What", "is", "config", "sentinel", "process", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1588524594", "fields": {"term": "What services are maintained by config-sentinel?", "namespace": "open-p", "hash": 1588524594, "terms": ["What services are maintained by config-sentinel?", "services are maintained by config-sentinel?", "are maintained by config-sentinel?", "maintained by config-sentinel?", "by config-sentinel?", "config-sentinel?"], "tokens": ["What", "services", "are", "maintained", "by", "config", "sentinel", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-862121807", "fields": {"term": "What role does config-sentinel play?", "namespace": "open-p", "hash": -862121807, "terms": ["What role does config-sentinel play?", "role does config-sentinel play?", "does config-sentinel play?", "config-sentinel play?", "play?"], "tokens": ["What", "role", "does", "config", "sentinel", "play", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1299422876", "fields": {"term": "How does config-sentinel start services?", "namespace": "open-p", "hash": 1299422876, "terms": ["How does config-sentinel start services?", "does config-sentinel start services?", "config-sentinel start services?", "start services?", "services?"], "tokens": ["How", "does", "config", "sentinel", "start", "services", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1008190021", "fields": {"term": "What happens during minor upgrades?", "namespace": "open-p", "hash": 1008190021, "terms": ["What happens during minor upgrades?", "happens during minor upgrades?", "during minor upgrades?", "minor upgrades?", "upgrades?"], "tokens": ["What", "happens", "during", "minor", "upgrades", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::179193289", "fields": {"term": "What is a major upgrade?", "namespace": "open-p", "hash": 179193289, "terms": ["What is a major upgrade?", "is a major upgrade?", "a major upgrade?", "major upgrade?", "upgrade?"], "tokens": ["What", "is", "a", "major", "upgrade", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1129048130", "fields": {"term": "What is the procedure for major upgrades?", "namespace": "open-p", "hash": 1129048130, "terms": ["What is the procedure for major upgrades?", "is the procedure for major upgrades?", "the procedure for major upgrades?", "procedure for major upgrades?", "for major upgrades?", "major upgrades?", "upgrades?"], "tokens": ["What", "is", "the", "procedure", "for", "major", "upgrades", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1023317594", "fields": {"term": "What are the assumptions for nodes using Vespa Configuration?", "namespace": "open-p", "hash": -1023317594, "terms": ["What are the assumptions for nodes using Vespa Configuration?", "are the assumptions for nodes using Vespa Configuration?", "the assumptions for nodes using Vespa Configuration?", "assumptions for nodes using Vespa Configuration?", "for nodes using Vespa Configuration?", "nodes using Vespa Configuration?", "using Vespa Configuration?", "Vespa Configuration?", "Configuration?"], "tokens": ["What", "are", "the", "assumptions", "for", "nodes", "using", "Vespa", "Configuration", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-443098139", "fields": {"term": "What is the purpose of configuration server operations?", "namespace": "open-p", "hash": -443098139, "terms": ["What is the purpose of configuration server operations?", "is the purpose of configuration server operations?", "the purpose of configuration server operations?", "purpose of configuration server operations?", "of configuration server operations?", "configuration server operations?", "server operations?", "operations?"], "tokens": ["What", "is", "the", "purpose", "of", "configuration", "server", "operations", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::498131421", "fields": {"term": "Is reading this document necessary for using Vespa?", "namespace": "open-p", "hash": 498131421, "terms": ["Is reading this document necessary for using Vespa?", "reading this document necessary for using Vespa?", "this document necessary for using Vespa?", "document necessary for using Vespa?", "necessary for using Vespa?", "for using Vespa?", "using Vespa?", "Vespa?"], "tokens": ["Is", "reading", "this", "document", "necessary", "for", "using", "Vespa", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::-1201025094", "fields": {"term": "How does the config proxy work?", "namespace": "open-p", "hash": -1201025094, "terms": ["How does the config proxy work?", "does the config proxy work?", "the config proxy work?", "config proxy work?", "proxy work?", "work?"], "tokens": ["How", "does", "the", "config", "proxy", "work", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1300444098", "fields": {"term": "What is the default mode of the config proxy?", "namespace": "open-p", "hash": -1300444098, "terms": ["What is the default mode of the config proxy?", "is the default mode of the config proxy?", "the default mode of the config proxy?", "default mode of the config proxy?", "mode of the config proxy?", "of the config proxy?", "the config proxy?", "config proxy?", "proxy?"], "tokens": ["What", "is", "the", "default", "mode", "of", "the", "config", "proxy", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1943457798", "fields": {"term": "What is memorycache mode?", "namespace": "open-p", "hash": -1943457798, "terms": ["What is memorycache mode?", "is memorycache mode?", "memorycache mode?", "mode?"], "tokens": ["What", "is", "memorycache", "mode", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-158820803", "fields": {"term": "How can I interface with the config proxy?", "namespace": "open-p", "hash": -158820803, "terms": ["How can I interface with the config proxy?", "can I interface with the config proxy?", "I interface with the config proxy?", "interface with the config proxy?", "with the config proxy?", "the config proxy?", "config proxy?", "proxy?"], "tokens": ["How", "can", "I", "interface", "with", "the", "config", "proxy", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1474616354", "fields": {"term": "What is vespa-configproxy-cmd?", "namespace": "open-p", "hash": 1474616354, "terms": ["What is vespa-configproxy-cmd?", "is vespa-configproxy-cmd?", "vespa-configproxy-cmd?"], "tokens": ["What", "is", "vespa", "configproxy", "cmd", "?"], "corpus_count": 4, "document_count": 4}}, -{"put": "id:term:term::-535227537", "fields": {"term": "What is the active config generation?", "namespace": "open-p", "hash": -535227537, "terms": ["What is the active config generation?", "is the active config generation?", "the active config generation?", "active config generation?", "config generation?", "generation?"], "tokens": ["What", "is", "the", "active", "config", "generation", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-61779789", "fields": {"term": "What is the config definition name?", "namespace": "open-p", "hash": -61779789, "terms": ["What is the config definition name?", "is the config definition name?", "the config definition name?", "config definition name?", "definition name?", "name?"], "tokens": ["What", "is", "the", "config", "definition", "name", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1233204804", "fields": {"term": "What is the config id used?", "namespace": "open-p", "hash": -1233204804, "terms": ["What is the config id used?", "is the config id used?", "the config id used?", "config id used?", "id used?", "used?"], "tokens": ["What", "is", "the", "config", "id", "used", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1785760443", "fields": {"term": "What tool can be used to find the relevant config information?", "namespace": "open-p", "hash": -1785760443, "terms": ["What tool can be used to find the relevant config information?", "tool can be used to find the relevant config information?", "can be used to find the relevant config information?", "be used to find the relevant config information?", "used to find the relevant config information?", "to find the relevant config information?", "find the relevant config information?", "the relevant config information?", "relevant config information?", "config information?", "information?"], "tokens": ["What", "tool", "can", "be", "used", "to", "find", "the", "relevant", "config", "information", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1551690018", "fields": {"term": "How is config generation determined?", "namespace": "open-p", "hash": 1551690018, "terms": ["How is config generation determined?", "is config generation determined?", "config generation determined?", "generation determined?", "determined?"], "tokens": ["How", "is", "config", "generation", "determined", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2105373035", "fields": {"term": "What is vespa-get-config used for?", "namespace": "open-p", "hash": 2105373035, "terms": ["What is vespa-get-config used for?", "is vespa-get-config used for?", "vespa-get-config used for?", "used for?", "for?"], "tokens": ["What", "is", "vespa", "get", "config", "used", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::222906432", "fields": {"term": "When is a restart required for some config changes?", "namespace": "open-p", "hash": 222906432, "terms": ["When is a restart required for some config changes?", "is a restart required for some config changes?", "a restart required for some config changes?", "restart required for some config changes?", "required for some config changes?", "for some config changes?", "some config changes?", "config changes?", "changes?"], "tokens": ["When", "is", "a", "restart", "required", "for", "some", "config", "changes", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1051804711", "fields": {"term": "What is the Vespa REST API?", "namespace": "open-p", "hash": 1051804711, "terms": ["What is the Vespa REST API?", "is the Vespa REST API?", "the Vespa REST API?", "Vespa REST API?", "REST API?", "API?"], "tokens": ["What", "is", "the", "Vespa", "REST", "API", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1718706915", "fields": {"term": "What does the Config API do?", "namespace": "open-p", "hash": -1718706915, "terms": ["What does the Config API do?", "does the Config API do?", "the Config API do?", "Config API do?", "API do?", "do?"], "tokens": ["What", "does", "the", "Config", "API", "do", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1316678486", "fields": {"term": "What is the API port number?", "namespace": "open-p", "hash": -1316678486, "terms": ["What is the API port number?", "is the API port number?", "the API port number?", "API port number?", "port number?", "number?"], "tokens": ["What", "is", "the", "API", "port", "number", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1903464826", "fields": {"term": "How do I find config server hosts?", "namespace": "open-p", "hash": -1903464826, "terms": ["How do I find config server hosts?", "do I find config server hosts?", "I find config server hosts?", "find config server hosts?", "config server hosts?", "server hosts?", "hosts?"], "tokens": ["How", "do", "I", "find", "config", "server", "hosts", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1656152740", "fields": {"term": "When is the API available?", "namespace": "open-p", "hash": 1656152740, "terms": ["When is the API available?", "is the API available?", "the API available?", "API available?", "available?"], "tokens": ["When", "is", "the", "API", "available", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-689993588", "fields": {"term": "What is needed before using the API?", "namespace": "open-p", "hash": -689993588, "terms": ["What is needed before using the API?", "is needed before using the API?", "needed before using the API?", "before using the API?", "using the API?", "the API?", "API?"], "tokens": ["What", "is", "needed", "before", "using", "the", "API", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::514041090", "fields": {"term": "What is an application id?", "namespace": "open-p", "hash": 514041090, "terms": ["What is an application id?", "is an application id?", "an application id?", "application id?", "id?"], "tokens": ["What", "is", "an", "application", "id", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::805804965", "fields": {"term": "What is the short form?", "namespace": "open-p", "hash": 805804965, "terms": ["What is the short form?", "is the short form?", "the short form?", "short form?", "form?"], "tokens": ["What", "is", "the", "short", "form", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::222786534", "fields": {"term": "What is a tuple?", "namespace": "open-p", "hash": 222786534, "terms": ["What is a tuple?", "is a tuple?", "a tuple?", "tuple?"], "tokens": ["What", "is", "a", "tuple", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::363794364", "fields": {"term": "What is the default shorthand?", "namespace": "open-p", "hash": 363794364, "terms": ["What is the default shorthand?", "is the default shorthand?", "the default shorthand?", "default shorthand?", "shorthand?"], "tokens": ["What", "is", "the", "default", "shorthand", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::200785130", "fields": {"term": "What does the API provide?", "namespace": "open-p", "hash": 200785130, "terms": ["What does the API provide?", "does the API provide?", "the API provide?", "API provide?", "provide?"], "tokens": ["What", "does", "the", "API", "provide", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::874277766", "fields": {"term": "What is the 'full' form of the application id?", "namespace": "open-p", "hash": 874277766, "terms": ["What is the 'full' form of the application id?", "is the 'full' form of the application id?", "the 'full' form of the application id?", "'full' form of the application id?", "form of the application id?", "of the application id?", "the application id?", "application id?", "id?"], "tokens": ["What", "is", "the", "'", "full", "'", "form", "of", "the", "application", "id", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1117293759", "fields": {"term": "What information is included in the full application id?", "namespace": "open-p", "hash": 1117293759, "terms": ["What information is included in the full application id?", "information is included in the full application id?", "is included in the full application id?", "included in the full application id?", "in the full application id?", "the full application id?", "full application id?", "application id?", "id?"], "tokens": ["What", "information", "is", "included", "in", "the", "full", "application", "id", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-976524313", "fields": {"term": "What does the config payload include?", "namespace": "open-p", "hash": -976524313, "terms": ["What does the config payload include?", "does the config payload include?", "the config payload include?", "config payload include?", "payload include?", "include?"], "tokens": ["What", "does", "the", "config", "payload", "include", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1448122160", "fields": {"term": "What is config sentinel?", "namespace": "open-p", "hash": -1448122160, "terms": ["What is config sentinel?", "is config sentinel?", "config sentinel?", "sentinel?"], "tokens": ["What", "is", "config", "sentinel", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::1572485628", "fields": {"term": "What does the config sentinel do?", "namespace": "open-p", "hash": 1572485628, "terms": ["What does the config sentinel do?", "does the config sentinel do?", "the config sentinel do?", "config sentinel do?", "sentinel do?", "do?"], "tokens": ["What", "does", "the", "config", "sentinel", "do", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1674794846", "fields": {"term": "How can the config sentinel be used?", "namespace": "open-p", "hash": 1674794846, "terms": ["How can the config sentinel be used?", "can the config sentinel be used?", "the config sentinel be used?", "config sentinel be used?", "sentinel be used?", "be used?", "used?"], "tokens": ["How", "can", "the", "config", "sentinel", "be", "used", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1553640975", "fields": {"term": "How do you list services running on a node?", "namespace": "open-p", "hash": 1553640975, "terms": ["How do you list services running on a node?", "do you list services running on a node?", "you list services running on a node?", "list services running on a node?", "services running on a node?", "running on a node?", "on a node?", "a node?", "node?"], "tokens": ["How", "do", "you", "list", "services", "running", "on", "a", "node", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1787112355", "fields": {"term": "What is minOkPercent?", "namespace": "open-p", "hash": 1787112355, "terms": ["What is minOkPercent?", "is minOkPercent?", "minOkPercent?"], "tokens": ["What", "is", "minOkPercent", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-97794701", "fields": {"term": "What is maxBadCount?", "namespace": "open-p", "hash": -97794701, "terms": ["What is maxBadCount?", "is maxBadCount?", "maxBadCount?"], "tokens": ["What", "is", "maxBadCount", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2026949297", "fields": {"term": "What is connectivity?", "namespace": "open-p", "hash": -2026949297, "terms": ["What is connectivity?", "is connectivity?", "connectivity?"], "tokens": ["What", "is", "connectivity", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-717841738", "fields": {"term": "What is the recommended value for maxBadCount?", "namespace": "open-p", "hash": -717841738, "terms": ["What is the recommended value for maxBadCount?", "is the recommended value for maxBadCount?", "the recommended value for maxBadCount?", "recommended value for maxBadCount?", "value for maxBadCount?", "for maxBadCount?", "maxBadCount?"], "tokens": ["What", "is", "the", "recommended", "value", "for", "maxBadCount", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-523138889", "fields": {"term": "What is the recommended value for minOkPercent?", "namespace": "open-p", "hash": -523138889, "terms": ["What is the recommended value for minOkPercent?", "is the recommended value for minOkPercent?", "the recommended value for minOkPercent?", "recommended value for minOkPercent?", "value for minOkPercent?", "for minOkPercent?", "minOkPercent?"], "tokens": ["What", "is", "the", "recommended", "value", "for", "minOkPercent", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-360220681", "fields": {"term": "What is the connectivity check details?", "namespace": "open-p", "hash": -360220681, "terms": ["What is the connectivity check details?", "is the connectivity check details?", "the connectivity check details?", "connectivity check details?", "check details?", "details?"], "tokens": ["What", "is", "the", "connectivity", "check", "details", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1438481522", "fields": {"term": "What is the C++ cloud config API?", "namespace": "open-p", "hash": -1438481522, "terms": ["What is the C++ cloud config API?", "is the C++ cloud config API?", "the C++ cloud config API?", "C++ cloud config API?", "cloud config API?", "config API?", "API?"], "tokens": ["What", "is", "the", "C", "++", "cloud", "config", "API", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1512426902", "fields": {"term": "How do I generate C++ code?", "namespace": "open-p", "hash": -1512426902, "terms": ["How do I generate C++ code?", "do I generate C++ code?", "I generate C++ code?", "generate C++ code?", "C++ code?", "code?"], "tokens": ["How", "do", "I", "generate", "C", "++", "code", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-173563769", "fields": {"term": "What do I need to subscribe to using the API?", "namespace": "open-p", "hash": -173563769, "terms": ["What do I need to subscribe to using the API?", "do I need to subscribe to using the API?", "I need to subscribe to using the API?", "need to subscribe to using the API?", "to subscribe to using the API?", "subscribe to using the API?", "to using the API?", "using the API?", "the API?", "API?"], "tokens": ["What", "do", "I", "need", "to", "subscribe", "to", "using", "the", "API", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-463278291", "fields": {"term": "What is config-defs/motd.def?", "namespace": "open-p", "hash": -463278291, "terms": ["What is config-defs/motd.def?", "is config-defs/motd.def?", "config-defs/motd.def?"], "tokens": ["What", "is", "config", "defs", "/", "motd", "def", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1662168559", "fields": {"term": "What is make-config.pl?", "namespace": "open-p", "hash": 1662168559, "terms": ["What is make-config.pl?", "is make-config.pl?", "make-config.pl?"], "tokens": ["What", "is", "make", "config", "pl", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1300715725", "fields": {"term": "What is generated by make-config.pl?", "namespace": "open-p", "hash": 1300715725, "terms": ["What is generated by make-config.pl?", "is generated by make-config.pl?", "generated by make-config.pl?", "by make-config.pl?", "make-config.pl?"], "tokens": ["What", "is", "generated", "by", "make", "config", "pl", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-182858014", "fields": {"term": "What is a ConfigSubscriber?", "namespace": "open-p", "hash": -182858014, "terms": ["What is a ConfigSubscriber?", "is a ConfigSubscriber?", "a ConfigSubscriber?", "ConfigSubscriber?"], "tokens": ["What", "is", "a", "ConfigSubscriber", "?"], "corpus_count": 3, "document_count": 3}}, -{"put": "id:term:term::2057011297", "fields": {"term": "What is the config API?", "namespace": "open-p", "hash": 2057011297, "terms": ["What is the config API?", "is the config API?", "the config API?", "config API?", "API?"], "tokens": ["What", "is", "the", "config", "API", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::-503670726", "fields": {"term": "What does the subscribe method do?", "namespace": "open-p", "hash": -503670726, "terms": ["What does the subscribe method do?", "does the subscribe method do?", "the subscribe method do?", "subscribe method do?", "method do?", "do?"], "tokens": ["What", "does", "the", "subscribe", "method", "do", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1453431206", "fields": {"term": "What happens if ConfigSubscriber is unable to subscribe?", "namespace": "open-p", "hash": -1453431206, "terms": ["What happens if ConfigSubscriber is unable to subscribe?", "happens if ConfigSubscriber is unable to subscribe?", "if ConfigSubscriber is unable to subscribe?", "ConfigSubscriber is unable to subscribe?", "is unable to subscribe?", "unable to subscribe?", "to subscribe?", "subscribe?"], "tokens": ["What", "happens", "if", "ConfigSubscriber", "is", "unable", "to", "subscribe", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-130502334", "fields": {"term": "What headers must be included to use the API?", "namespace": "open-p", "hash": -130502334, "terms": ["What headers must be included to use the API?", "headers must be included to use the API?", "must be included to use the API?", "be included to use the API?", "included to use the API?", "to use the API?", "use the API?", "the API?", "API?"], "tokens": ["What", "headers", "must", "be", "included", "to", "use", "the", "API", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1975547281", "fields": {"term": "What is the ConfigSubscriber?", "namespace": "open-p", "hash": 1975547281, "terms": ["What is the ConfigSubscriber?", "is the ConfigSubscriber?", "the ConfigSubscriber?", "ConfigSubscriber?"], "tokens": ["What", "is", "the", "ConfigSubscriber", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::942805543", "fields": {"term": "What is nextConfig()?", "namespace": "open-p", "hash": 942805543, "terms": ["What is nextConfig()?", "is nextConfig()?", "nextConfig()?"], "tokens": ["What", "is", "nextConfig", "()?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1100409098", "fields": {"term": "What does handle->getConfig() return?", "namespace": "open-p", "hash": 1100409098, "terms": ["What does handle->getConfig() return?", "does handle->getConfig() return?", "handle->getConfig() return?", "return?"], "tokens": ["What", "does", "handle", ">", "getConfig", "()", "return", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-95813399", "fields": {"term": "Is ConfigSubscriber thread-safe?", "namespace": "open-p", "hash": -95813399, "terms": ["Is ConfigSubscriber thread-safe?", "ConfigSubscriber thread-safe?", "thread-safe?"], "tokens": ["Is", "ConfigSubscriber", "thread", "safe", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1204604659", "fields": {"term": "How long does nextConfig() wait?", "namespace": "open-p", "hash": 1204604659, "terms": ["How long does nextConfig() wait?", "long does nextConfig() wait?", "does nextConfig() wait?", "nextConfig() wait?", "wait?"], "tokens": ["How", "long", "does", "nextConfig", "()", "wait", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1286687381", "fields": {"term": "What is the format of subscribe()?", "namespace": "open-p", "hash": 1286687381, "terms": ["What is the format of subscribe()?", "is the format of subscribe()?", "the format of subscribe()?", "format of subscribe()?", "of subscribe()?", "subscribe()?"], "tokens": ["What", "is", "the", "format", "of", "subscribe", "<", "MotdConfig", ">()?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-334977445", "fields": {"term": "What happens if nextConfig() times out?", "namespace": "open-p", "hash": -334977445, "terms": ["What happens if nextConfig() times out?", "happens if nextConfig() times out?", "if nextConfig() times out?", "nextConfig() times out?", "times out?", "out?"], "tokens": ["What", "happens", "if", "nextConfig", "()", "times", "out", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::444695691", "fields": {"term": "What is the purpose of ConfigSubscriber?", "namespace": "open-p", "hash": 444695691, "terms": ["What is the purpose of ConfigSubscriber?", "is the purpose of ConfigSubscriber?", "the purpose of ConfigSubscriber?", "purpose of ConfigSubscriber?", "of ConfigSubscriber?", "ConfigSubscriber?"], "tokens": ["What", "is", "the", "purpose", "of", "ConfigSubscriber", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1361084870", "fields": {"term": "What is a ConfigContext used for?", "namespace": "open-p", "hash": -1361084870, "terms": ["What is a ConfigContext used for?", "is a ConfigContext used for?", "a ConfigContext used for?", "ConfigContext used for?", "used for?", "for?"], "tokens": ["What", "is", "a", "ConfigContext", "used", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::874692386", "fields": {"term": "What are the different spec types?", "namespace": "open-p", "hash": 874692386, "terms": ["What are the different spec types?", "are the different spec types?", "the different spec types?", "different spec types?", "spec types?", "types?"], "tokens": ["What", "are", "the", "different", "spec", "types", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1427084505", "fields": {"term": "What is a builder type?", "namespace": "open-p", "hash": 1427084505, "terms": ["What is a builder type?", "is a builder type?", "a builder type?", "builder type?", "type?"], "tokens": ["What", "is", "a", "builder", "type", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1617157680", "fields": {"term": "What is a ConfigSet?", "namespace": "open-p", "hash": -1617157680, "terms": ["What is a ConfigSet?", "is a ConfigSet?", "a ConfigSet?", "ConfigSet?"], "tokens": ["What", "is", "a", "ConfigSet", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::-380733007", "fields": {"term": "How to create a context?", "namespace": "open-p", "hash": -380733007, "terms": ["How to create a context?", "to create a context?", "create a context?", "a context?", "context?"], "tokens": ["How", "to", "create", "a", "context", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-323828741", "fields": {"term": "What is ConfigHandle?", "namespace": "open-p", "hash": -323828741, "terms": ["What is ConfigHandle?", "is ConfigHandle?", "ConfigHandle?"], "tokens": ["What", "is", "ConfigHandle", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::248724589", "fields": {"term": "What is ConfigGetter?", "namespace": "open-p", "hash": 248724589, "terms": ["What is ConfigGetter?", "is ConfigGetter?", "ConfigGetter?"], "tokens": ["What", "is", "ConfigGetter", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1687301790", "fields": {"term": "How to test the config?", "namespace": "open-p", "hash": 1687301790, "terms": ["How to test the config?", "to test the config?", "test the config?", "the config?", "config?"], "tokens": ["How", "to", "test", "the", "config", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1480733942", "fields": {"term": "What does id1 represents?", "namespace": "open-p", "hash": -1480733942, "terms": ["What does id1 represents?", "does id1 represents?", "id1 represents?", "represents?"], "tokens": ["What", "does", "id1", "represents", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1571753509", "fields": {"term": "How do you print a config?", "namespace": "open-p", "hash": -1571753509, "terms": ["How do you print a config?", "do you print a config?", "you print a config?", "print a config?", "a config?", "config?"], "tokens": ["How", "do", "you", "print", "a", "config", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-492974878", "fields": {"term": "What is ConfigWriter?", "namespace": "open-p", "hash": -492974878, "terms": ["What is ConfigWriter?", "is ConfigWriter?", "ConfigWriter?"], "tokens": ["What", "is", "ConfigWriter", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1754638192", "fields": {"term": "What is a ConfigFormatter?", "namespace": "open-p", "hash": -1754638192, "terms": ["What is a ConfigFormatter?", "is a ConfigFormatter?", "a ConfigFormatter?", "ConfigFormatter?"], "tokens": ["What", "is", "a", "ConfigFormatter", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-928761999", "fields": {"term": "What is FileConfigWriter?", "namespace": "open-p", "hash": -928761999, "terms": ["What is FileConfigWriter?", "is FileConfigWriter?", "FileConfigWriter?"], "tokens": ["What", "is", "FileConfigWriter", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1192904398", "fields": {"term": "What is OstreamConfigWriter?", "namespace": "open-p", "hash": 1192904398, "terms": ["What is OstreamConfigWriter?", "is OstreamConfigWriter?", "OstreamConfigWriter?"], "tokens": ["What", "is", "OstreamConfigWriter", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-456485320", "fields": {"term": "What is FileConfigFormatter?", "namespace": "open-p", "hash": -456485320, "terms": ["What is FileConfigFormatter?", "is FileConfigFormatter?", "FileConfigFormatter?"], "tokens": ["What", "is", "FileConfigFormatter", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-424737120", "fields": {"term": "What is JsonConfigFormatter?", "namespace": "open-p", "hash": -424737120, "terms": ["What is JsonConfigFormatter?", "is JsonConfigFormatter?", "JsonConfigFormatter?"], "tokens": ["What", "is", "JsonConfigFormatter", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1243669499", "fields": {"term": "How to write a config to JSON file?", "namespace": "open-p", "hash": -1243669499, "terms": ["How to write a config to JSON file?", "to write a config to JSON file?", "write a config to JSON file?", "a config to JSON file?", "config to JSON file?", "to JSON file?", "JSON file?", "file?"], "tokens": ["How", "to", "write", "a", "config", "to", "JSON", "file", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1162201454", "fields": {"term": "What is a def file?", "namespace": "open-p", "hash": -1162201454, "terms": ["What is a def file?", "is a def file?", "a def file?", "def file?", "file?"], "tokens": ["What", "is", "a", "def", "file", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-460767000", "fields": {"term": "How to generate config classes?", "namespace": "open-p", "hash": -460767000, "terms": ["How to generate config classes?", "to generate config classes?", "generate config classes?", "config classes?", "classes?"], "tokens": ["How", "to", "generate", "config", "classes", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::112566356", "fields": {"term": "Where are the generated classes saved?", "namespace": "open-p", "hash": 112566356, "terms": ["Where are the generated classes saved?", "are the generated classes saved?", "the generated classes saved?", "generated classes saved?", "classes saved?", "saved?"], "tokens": ["Where", "are", "the", "generated", "classes", "saved", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1637817245", "fields": {"term": "What is an example def-file used?", "namespace": "open-p", "hash": -1637817245, "terms": ["What is an example def-file used?", "is an example def-file used?", "an example def-file used?", "example def-file used?", "def-file used?", "used?"], "tokens": ["What", "is", "an", "example", "def", "file", "used", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2109207720", "fields": {"term": "What class is generated?", "namespace": "open-p", "hash": 2109207720, "terms": ["What class is generated?", "class is generated?", "is generated?", "generated?"], "tokens": ["What", "class", "is", "generated", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-986206056", "fields": {"term": "What is a subtype of ConfigInstance?", "namespace": "open-p", "hash": -986206056, "terms": ["What is a subtype of ConfigInstance?", "is a subtype of ConfigInstance?", "a subtype of ConfigInstance?", "subtype of ConfigInstance?", "of ConfigInstance?", "ConfigInstance?"], "tokens": ["What", "is", "a", "subtype", "of", "ConfigInstance", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-707980842", "fields": {"term": "How to pull in config system?", "namespace": "open-p", "hash": -707980842, "terms": ["How to pull in config system?", "to pull in config system?", "pull in config system?", "in config system?", "config system?", "system?"], "tokens": ["How", "to", "pull", "in", "config", "system", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::552510412", "fields": {"term": "How to retrieve config in application?", "namespace": "open-p", "hash": 552510412, "terms": ["How to retrieve config in application?", "to retrieve config in application?", "retrieve config in application?", "config in application?", "in application?", "application?"], "tokens": ["How", "to", "retrieve", "config", "in", "application", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1113290076", "fields": {"term": "What does nextConfig() do?", "namespace": "open-p", "hash": 1113290076, "terms": ["What does nextConfig() do?", "does nextConfig() do?", "nextConfig() do?", "do?"], "tokens": ["What", "does", "nextConfig", "()", "do", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-953201187", "fields": {"term": "How to handle errors?", "namespace": "open-p", "hash": -953201187, "terms": ["How to handle errors?", "to handle errors?", "handle errors?", "errors?"], "tokens": ["How", "to", "handle", "errors", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1702142634", "fields": {"term": "What is the second parameter to subscribe()?", "namespace": "open-p", "hash": 1702142634, "terms": ["What is the second parameter to subscribe()?", "is the second parameter to subscribe()?", "the second parameter to subscribe()?", "second parameter to subscribe()?", "parameter to subscribe()?", "to subscribe()?", "subscribe()?"], "tokens": ["What", "is", "the", "second", "parameter", "to", "subscribe", "()?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-395012094", "fields": {"term": "How does isChanged() work?", "namespace": "open-p", "hash": -395012094, "terms": ["How does isChanged() work?", "does isChanged() work?", "isChanged() work?", "work?"], "tokens": ["How", "does", "isChanged", "()", "work", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-294265695", "fields": {"term": "How to subscribe to multiple configs?", "namespace": "open-p", "hash": -294265695, "terms": ["How to subscribe to multiple configs?", "to subscribe to multiple configs?", "subscribe to multiple configs?", "to multiple configs?", "multiple configs?", "configs?"], "tokens": ["How", "to", "subscribe", "to", "multiple", "configs", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2050902220", "fields": {"term": "What is ConfigSubscriber.SingleSubscriber interface?", "namespace": "open-p", "hash": -2050902220, "terms": ["What is ConfigSubscriber.SingleSubscriber interface?", "is ConfigSubscriber.SingleSubscriber interface?", "ConfigSubscriber.SingleSubscriber interface?", "interface?"], "tokens": ["What", "is", "ConfigSubscriber", "SingleSubscriber", "interface", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-717821660", "fields": {"term": "How do you define configure() method?", "namespace": "open-p", "hash": -717821660, "terms": ["How do you define configure() method?", "do you define configure() method?", "you define configure() method?", "define configure() method?", "configure() method?", "method?"], "tokens": ["How", "do", "you", "define", "configure", "()", "method", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::581609464", "fields": {"term": "What does subscribe() method do?", "namespace": "open-p", "hash": 581609464, "terms": ["What does subscribe() method do?", "does subscribe() method do?", "subscribe() method do?", "method do?", "do?"], "tokens": ["What", "does", "subscribe", "()", "method", "do", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1251898627", "fields": {"term": "What happens if initial configuration fails?", "namespace": "open-p", "hash": 1251898627, "terms": ["What happens if initial configuration fails?", "happens if initial configuration fails?", "if initial configuration fails?", "initial configuration fails?", "configuration fails?", "fails?"], "tokens": ["What", "happens", "if", "initial", "configuration", "fails", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2147419260", "fields": {"term": "What is the disadvantage of using ConfigSubscriber.SingleSubscriber?", "namespace": "open-p", "hash": -2147419260, "terms": ["What is the disadvantage of using ConfigSubscriber.SingleSubscriber?", "is the disadvantage of using ConfigSubscriber.SingleSubscriber?", "the disadvantage of using ConfigSubscriber.SingleSubscriber?", "disadvantage of using ConfigSubscriber.SingleSubscriber?", "of using ConfigSubscriber.SingleSubscriber?", "using ConfigSubscriber.SingleSubscriber?", "ConfigSubscriber.SingleSubscriber?"], "tokens": ["What", "is", "the", "disadvantage", "of", "using", "ConfigSubscriber", "SingleSubscriber", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1846884030", "fields": {"term": "What is the generic method to track config changes?", "namespace": "open-p", "hash": -1846884030, "terms": ["What is the generic method to track config changes?", "is the generic method to track config changes?", "the generic method to track config changes?", "generic method to track config changes?", "method to track config changes?", "to track config changes?", "track config changes?", "config changes?", "changes?"], "tokens": ["What", "is", "the", "generic", "method", "to", "track", "config", "changes", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1298279947", "fields": {"term": "What is MotdConfig?", "namespace": "open-p", "hash": -1298279947, "terms": ["What is MotdConfig?", "is MotdConfig?", "MotdConfig?"], "tokens": ["What", "is", "MotdConfig", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2126761968", "fields": {"term": "What is the purpose of MyConfigSubscriber class?", "namespace": "open-p", "hash": 2126761968, "terms": ["What is the purpose of MyConfigSubscriber class?", "is the purpose of MyConfigSubscriber class?", "the purpose of MyConfigSubscriber class?", "purpose of MyConfigSubscriber class?", "of MyConfigSubscriber class?", "MyConfigSubscriber class?", "class?"], "tokens": ["What", "is", "the", "purpose", "of", "MyConfigSubscriber", "class", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1756397514", "fields": {"term": "What is a ConfigSource?", "namespace": "open-p", "hash": 1756397514, "terms": ["What is a ConfigSource?", "is a ConfigSource?", "a ConfigSource?", "ConfigSource?"], "tokens": ["What", "is", "a", "ConfigSource", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1988173607", "fields": {"term": "What is a Builder?", "namespace": "open-p", "hash": 1988173607, "terms": ["What is a Builder?", "is a Builder?", "a Builder?", "Builder?"], "tokens": ["What", "is", "a", "Builder", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::768172467", "fields": {"term": "What is the reload() method for ConfigSubscriber used for?", "namespace": "open-p", "hash": 768172467, "terms": ["What is the reload() method for ConfigSubscriber used for?", "is the reload() method for ConfigSubscriber used for?", "the reload() method for ConfigSubscriber used for?", "reload() method for ConfigSubscriber used for?", "method for ConfigSubscriber used for?", "for ConfigSubscriber used for?", "ConfigSubscriber used for?", "used for?", "for?"], "tokens": ["What", "is", "the", "reload", "()", "method", "for", "ConfigSubscriber", "used", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-782203502", "fields": {"term": "What is the difference between Builder and ConfigInstance?", "namespace": "open-p", "hash": -782203502, "terms": ["What is the difference between Builder and ConfigInstance?", "is the difference between Builder and ConfigInstance?", "the difference between Builder and ConfigInstance?", "difference between Builder and ConfigInstance?", "between Builder and ConfigInstance?", "Builder and ConfigInstance?", "and ConfigInstance?", "ConfigInstance?"], "tokens": ["What", "is", "the", "difference", "between", "Builder", "and", "ConfigInstance", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1764766142", "fields": {"term": "Where can I find a full example of ConfigSet Subscription?", "namespace": "open-p", "hash": 1764766142, "terms": ["Where can I find a full example of ConfigSet Subscription?", "can I find a full example of ConfigSet Subscription?", "I find a full example of ConfigSet Subscription?", "find a full example of ConfigSet Subscription?", "a full example of ConfigSet Subscription?", "full example of ConfigSet Subscription?", "example of ConfigSet Subscription?", "of ConfigSet Subscription?", "ConfigSet Subscription?", "Subscription?"], "tokens": ["Where", "can", "I", "find", "a", "full", "example", "of", "ConfigSet", "Subscription", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::171872755", "fields": {"term": "How do I use the C++ version of the config API?", "namespace": "open-p", "hash": 171872755, "terms": ["How do I use the C++ version of the config API?", "do I use the C++ version of the config API?", "I use the C++ version of the config API?", "use the C++ version of the config API?", "the C++ version of the config API?", "C++ version of the config API?", "version of the config API?", "of the config API?", "the config API?", "config API?", "API?"], "tokens": ["How", "do", "I", "use", "the", "C", "++", "version", "of", "the", "config", "API", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::967108451", "fields": {"term": "How do I use the Java version of the config API?", "namespace": "open-p", "hash": 967108451, "terms": ["How do I use the Java version of the config API?", "do I use the Java version of the config API?", "I use the Java version of the config API?", "use the Java version of the config API?", "the Java version of the config API?", "Java version of the config API?", "version of the config API?", "of the config API?", "the config API?", "config API?", "API?"], "tokens": ["How", "do", "I", "use", "the", "Java", "version", "of", "the", "config", "API", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::447461984", "fields": {"term": "What is the purpose of the Vespa container?", "namespace": "open-p", "hash": 447461984, "terms": ["What is the purpose of the Vespa container?", "is the purpose of the Vespa container?", "the purpose of the Vespa container?", "purpose of the Vespa container?", "of the Vespa container?", "the Vespa container?", "Vespa container?", "container?"], "tokens": ["What", "is", "the", "purpose", "of", "the", "Vespa", "container", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1607975475", "fields": {"term": "What is configurable message?", "namespace": "open-p", "hash": -1607975475, "terms": ["What is configurable message?", "is configurable message?", "configurable message?", "message?"], "tokens": ["What", "is", "configurable", "message", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1808896934", "fields": {"term": "What is config API?", "namespace": "open-p", "hash": -1808896934, "terms": ["What is config API?", "is config API?", "config API?", "API?"], "tokens": ["What", "is", "config", "API", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2043418619", "fields": {"term": "How to define a config?", "namespace": "open-p", "hash": -2043418619, "terms": ["How to define a config?", "to define a config?", "define a config?", "a config?", "config?"], "tokens": ["How", "to", "define", "a", "config", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-443544788", "fields": {"term": "What is motd.def?", "namespace": "open-p", "hash": -443544788, "terms": ["What is motd.def?", "is motd.def?", "motd.def?"], "tokens": ["What", "is", "motd", "def", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1361866052", "fields": {"term": "How do I generate config code?", "namespace": "open-p", "hash": -1361866052, "terms": ["How do I generate config code?", "do I generate config code?", "I generate config code?", "generate config code?", "config code?", "code?"], "tokens": ["How", "do", "I", "generate", "config", "code", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1032206391", "fields": {"term": "What languages have API code?", "namespace": "open-p", "hash": 1032206391, "terms": ["What languages have API code?", "languages have API code?", "have API code?", "API code?", "code?"], "tokens": ["What", "languages", "have", "API", "code", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2141195847", "fields": {"term": "What is a config id?", "namespace": "open-p", "hash": 2141195847, "terms": ["What is a config id?", "is a config id?", "a config id?", "config id?", "id?"], "tokens": ["What", "is", "a", "config", "id", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1844191565", "fields": {"term": "What is a config producer instance?", "namespace": "open-p", "hash": -1844191565, "terms": ["What is a config producer instance?", "is a config producer instance?", "a config producer instance?", "config producer instance?", "producer instance?", "instance?"], "tokens": ["What", "is", "a", "config", "producer", "instance", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1777031507", "fields": {"term": "What is the config object model?", "namespace": "open-p", "hash": 1777031507, "terms": ["What is the config object model?", "is the config object model?", "the config object model?", "config object model?", "object model?", "model?"], "tokens": ["What", "is", "the", "config", "object", "model", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-899548567", "fields": {"term": "How is the final config instance produced?", "namespace": "open-p", "hash": -899548567, "terms": ["How is the final config instance produced?", "is the final config instance produced?", "the final config instance produced?", "final config instance produced?", "config instance produced?", "instance produced?", "produced?"], "tokens": ["How", "is", "the", "final", "config", "instance", "produced", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::697685551", "fields": {"term": "What is VESPA_CONFIG_ID used for?", "namespace": "open-p", "hash": 697685551, "terms": ["What is VESPA_CONFIG_ID used for?", "is VESPA_CONFIG_ID used for?", "VESPA_CONFIG_ID used for?", "used for?", "for?"], "tokens": ["What", "is", "VESPA_CONFIG_ID", "used", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1875713584", "fields": {"term": "Can multiple services have the same config id?", "namespace": "open-p", "hash": -1875713584, "terms": ["Can multiple services have the same config id?", "multiple services have the same config id?", "services have the same config id?", "have the same config id?", "the same config id?", "same config id?", "config id?", "id?"], "tokens": ["Can", "multiple", "services", "have", "the", "same", "config", "id", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1210176140", "fields": {"term": "What is the config sentinel?", "namespace": "open-p", "hash": -1210176140, "terms": ["What is the config sentinel?", "is the config sentinel?", "the config sentinel?", "config sentinel?", "sentinel?"], "tokens": ["What", "is", "the", "config", "sentinel", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1861250085", "fields": {"term": "What is the config id for services that are not part of the model?", "namespace": "open-p", "hash": -1861250085, "terms": ["What is the config id for services that are not part of the model?", "is the config id for services that are not part of the model?", "the config id for services that are not part of the model?", "config id for services that are not part of the model?", "id for services that are not part of the model?", "for services that are not part of the model?", "services that are not part of the model?", "that are not part of the model?", "are not part of the model?", "not part of the model?", "part of the model?", "of the model?", "the model?", "model?"], "tokens": ["What", "is", "the", "config", "id", "for", "services", "that", "are", "not", "part", "of", "the", "model", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1043246933", "fields": {"term": "What is a schema incompatibility?", "namespace": "open-p", "hash": 1043246933, "terms": ["What is a schema incompatibility?", "is a schema incompatibility?", "a schema incompatibility?", "schema incompatibility?", "incompatibility?"], "tokens": ["What", "is", "a", "schema", "incompatibility", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-963939785", "fields": {"term": "What happens when C is missing a config value that S has?", "namespace": "open-p", "hash": -963939785, "terms": ["What happens when C is missing a config value that S has?", "happens when C is missing a config value that S has?", "when C is missing a config value that S has?", "C is missing a config value that S has?", "is missing a config value that S has?", "missing a config value that S has?", "a config value that S has?", "config value that S has?", "value that S has?", "that S has?", "S has?", "has?"], "tokens": ["What", "happens", "when", "C", "is", "missing", "a", "config", "value", "that", "S", "has", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1033684571", "fields": {"term": "What happens when C has an additional config value with a default value?", "namespace": "open-p", "hash": -1033684571, "terms": ["What happens when C has an additional config value with a default value?", "happens when C has an additional config value with a default value?", "when C has an additional config value with a default value?", "C has an additional config value with a default value?", "has an additional config value with a default value?", "an additional config value with a default value?", "additional config value with a default value?", "config value with a default value?", "value with a default value?", "with a default value?", "a default value?", "default value?", "value?"], "tokens": ["What", "happens", "when", "C", "has", "an", "additional", "config", "value", "with", "a", "default", "value", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1364285641", "fields": {"term": "What happens when C has an additional config value without a default value?", "namespace": "open-p", "hash": -1364285641, "terms": ["What happens when C has an additional config value without a default value?", "happens when C has an additional config value without a default value?", "when C has an additional config value without a default value?", "C has an additional config value without a default value?", "has an additional config value without a default value?", "an additional config value without a default value?", "additional config value without a default value?", "config value without a default value?", "value without a default value?", "without a default value?", "a default value?", "default value?", "value?"], "tokens": ["What", "happens", "when", "C", "has", "an", "additional", "config", "value", "without", "a", "default", "value", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1418221581", "fields": {"term": "What happens when C has the type of a config value changed?", "namespace": "open-p", "hash": 1418221581, "terms": ["What happens when C has the type of a config value changed?", "happens when C has the type of a config value changed?", "when C has the type of a config value changed?", "C has the type of a config value changed?", "has the type of a config value changed?", "the type of a config value changed?", "type of a config value changed?", "of a config value changed?", "a config value changed?", "config value changed?", "value changed?", "changed?"], "tokens": ["What", "happens", "when", "C", "has", "the", "type", "of", "a", "config", "value", "changed", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-278501084", "fields": {"term": "What is the system's behavior with compatible changes?", "namespace": "open-p", "hash": -278501084, "terms": ["What is the system's behavior with compatible changes?", "is the system's behavior with compatible changes?", "the system's behavior with compatible changes?", "system's behavior with compatible changes?", "behavior with compatible changes?", "with compatible changes?", "compatible changes?", "changes?"], "tokens": ["What", "is", "the", "system", "'", "s", "behavior", "with", "compatible", "changes", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1208692538", "fields": {"term": "What is the system's behavior with incompatible changes?", "namespace": "open-p", "hash": -1208692538, "terms": ["What is the system's behavior with incompatible changes?", "is the system's behavior with incompatible changes?", "the system's behavior with incompatible changes?", "system's behavior with incompatible changes?", "behavior with incompatible changes?", "with incompatible changes?", "incompatible changes?", "changes?"], "tokens": ["What", "is", "the", "system", "'", "s", "behavior", "with", "incompatible", "changes", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1086441646", "fields": {"term": "Why should one prefer creating a new config name instead of removing a config value?", "namespace": "open-p", "hash": 1086441646, "terms": ["Why should one prefer creating a new config name instead of removing a config value?", "should one prefer creating a new config name instead of removing a config value?", "one prefer creating a new config name instead of removing a config value?", "prefer creating a new config name instead of removing a config value?", "creating a new config name instead of removing a config value?", "a new config name instead of removing a config value?", "new config name instead of removing a config value?", "config name instead of removing a config value?", "name instead of removing a config value?", "instead of removing a config value?", "of removing a config value?", "removing a config value?", "a config value?", "config value?", "value?"], "tokens": ["Why", "should", "one", "prefer", "creating", "a", "new", "config", "name", "instead", "of", "removing", "a", "config", "value", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1904028247", "fields": {"term": "What is the server's object model?", "namespace": "open-p", "hash": 1904028247, "terms": ["What is the server's object model?", "is the server's object model?", "the server's object model?", "server's object model?", "object model?", "model?"], "tokens": ["What", "is", "the", "server", "'", "s", "object", "model", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1258750219", "fields": {"term": "How do I create a model plugin?", "namespace": "open-p", "hash": 1258750219, "terms": ["How do I create a model plugin?", "do I create a model plugin?", "I create a model plugin?", "create a model plugin?", "a model plugin?", "model plugin?", "plugin?"], "tokens": ["How", "do", "I", "create", "a", "model", "plugin", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1465188806", "fields": {"term": "What is the purpose of config id mappings?", "namespace": "open-p", "hash": -1465188806, "terms": ["What is the purpose of config id mappings?", "is the purpose of config id mappings?", "the purpose of config id mappings?", "purpose of config id mappings?", "of config id mappings?", "config id mappings?", "id mappings?", "mappings?"], "tokens": ["What", "is", "the", "purpose", "of", "config", "id", "mappings", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1393222023", "fields": {"term": "Is the object model pluggable?", "namespace": "open-p", "hash": 1393222023, "terms": ["Is the object model pluggable?", "the object model pluggable?", "object model pluggable?", "model pluggable?", "pluggable?"], "tokens": ["Is", "the", "object", "model", "pluggable", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-399952932", "fields": {"term": "What is Developing Cloud Config Model plugins about?", "namespace": "open-p", "hash": -399952932, "terms": ["What is Developing Cloud Config Model plugins about?", "is Developing Cloud Config Model plugins about?", "Developing Cloud Config Model plugins about?", "Cloud Config Model plugins about?", "Config Model plugins about?", "Model plugins about?", "plugins about?", "about?"], "tokens": ["What", "is", "Developing", "Cloud", "Config", "Model", "plugins", "about", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1681367937", "fields": {"term": "What is the purpose of hosts.xml?", "namespace": "open-p", "hash": 1681367937, "terms": ["What is the purpose of hosts.xml?", "is the purpose of hosts.xml?", "the purpose of hosts.xml?", "purpose of hosts.xml?", "of hosts.xml?", "hosts.xml?"], "tokens": ["What", "is", "the", "purpose", "of", "hosts", "xml", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::-1498379989", "fields": {"term": "What is the cloudconfig package?", "namespace": "open-p", "hash": -1498379989, "terms": ["What is the cloudconfig package?", "is the cloudconfig package?", "the cloudconfig package?", "cloudconfig package?", "package?"], "tokens": ["What", "is", "the", "cloudconfig", "package", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::461998370", "fields": {"term": "Why split config settings?", "namespace": "open-p", "hash": 461998370, "terms": ["Why split config settings?", "split config settings?", "config settings?", "settings?"], "tokens": ["Why", "split", "config", "settings", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-479333197", "fields": {"term": "What are independent static configs?", "namespace": "open-p", "hash": -479333197, "terms": ["What are independent static configs?", "are independent static configs?", "independent static configs?", "static configs?", "configs?"], "tokens": ["What", "are", "independent", "static", "configs", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1884647941", "fields": {"term": "What are dependent static configs?", "namespace": "open-p", "hash": 1884647941, "terms": ["What are dependent static configs?", "are dependent static configs?", "dependent static configs?", "static configs?", "configs?"], "tokens": ["What", "are", "dependent", "static", "configs", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::789506699", "fields": {"term": "What are dependent dynamic configs?", "namespace": "open-p", "hash": 789506699, "terms": ["What are dependent dynamic configs?", "are dependent dynamic configs?", "dependent dynamic configs?", "dynamic configs?", "configs?"], "tokens": ["What", "are", "dependent", "dynamic", "configs", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1794364221", "fields": {"term": "How do you cope with different scenarios?", "namespace": "open-p", "hash": 1794364221, "terms": ["How do you cope with different scenarios?", "do you cope with different scenarios?", "you cope with different scenarios?", "cope with different scenarios?", "with different scenarios?", "different scenarios?", "scenarios?"], "tokens": ["How", "do", "you", "cope", "with", "different", "scenarios", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1075422467", "fields": {"term": "What are independent configs?", "namespace": "open-p", "hash": 1075422467, "terms": ["What are independent configs?", "are independent configs?", "independent configs?", "configs?"], "tokens": ["What", "are", "independent", "configs", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-434641031", "fields": {"term": "What is ConfigSubscriber?", "namespace": "open-p", "hash": -434641031, "terms": ["What is ConfigSubscriber?", "is ConfigSubscriber?", "ConfigSubscriber?"], "tokens": ["What", "is", "ConfigSubscriber", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::1945332707", "fields": {"term": "What is recommended for config management?", "namespace": "open-p", "hash": 1945332707, "terms": ["What is recommended for config management?", "is recommended for config management?", "recommended for config management?", "for config management?", "config management?", "management?"], "tokens": ["What", "is", "recommended", "for", "config", "management", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::132766519", "fields": {"term": "How to retrieve configs using ConfigHandle?", "namespace": "open-p", "hash": 132766519, "terms": ["How to retrieve configs using ConfigHandle?", "to retrieve configs using ConfigHandle?", "retrieve configs using ConfigHandle?", "configs using ConfigHandle?", "using ConfigHandle?", "ConfigHandle?"], "tokens": ["How", "to", "retrieve", "configs", "using", "ConfigHandle", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1358808677", "fields": {"term": "Is it recommended to use a separate ConfigSubscriber for each config?", "namespace": "open-p", "hash": -1358808677, "terms": ["Is it recommended to use a separate ConfigSubscriber for each config?", "it recommended to use a separate ConfigSubscriber for each config?", "recommended to use a separate ConfigSubscriber for each config?", "to use a separate ConfigSubscriber for each config?", "use a separate ConfigSubscriber for each config?", "a separate ConfigSubscriber for each config?", "separate ConfigSubscriber for each config?", "ConfigSubscriber for each config?", "for each config?", "each config?", "config?"], "tokens": ["Is", "it", "recommended", "to", "use", "a", "separate", "ConfigSubscriber", "for", "each", "config", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-620756660", "fields": {"term": "What is the retrieval part of the config check?", "namespace": "open-p", "hash": -620756660, "terms": ["What is the retrieval part of the config check?", "is the retrieval part of the config check?", "the retrieval part of the config check?", "retrieval part of the config check?", "part of the config check?", "of the config check?", "the config check?", "config check?", "check?"], "tokens": ["What", "is", "the", "retrieval", "part", "of", "the", "config", "check", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-858472284", "fields": {"term": "Can the config fetch part be performed in a separate thread?", "namespace": "open-p", "hash": -858472284, "terms": ["Can the config fetch part be performed in a separate thread?", "the config fetch part be performed in a separate thread?", "config fetch part be performed in a separate thread?", "fetch part be performed in a separate thread?", "part be performed in a separate thread?", "be performed in a separate thread?", "performed in a separate thread?", "in a separate thread?", "a separate thread?", "separate thread?", "thread?"], "tokens": ["Can", "the", "config", "fetch", "part", "be", "performed", "in", "a", "separate", "thread", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-533502377", "fields": {"term": "What are dependent configs?", "namespace": "open-p", "hash": -533502377, "terms": ["What are dependent configs?", "are dependent configs?", "dependent configs?", "configs?"], "tokens": ["What", "are", "dependent", "configs", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1793878529", "fields": {"term": "How do you approach dependent configs?", "namespace": "open-p", "hash": 1793878529, "terms": ["How do you approach dependent configs?", "do you approach dependent configs?", "you approach dependent configs?", "approach dependent configs?", "dependent configs?", "configs?"], "tokens": ["How", "do", "you", "approach", "dependent", "configs", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1578622476", "fields": {"term": "What is the recommended way to use dependent configs?", "namespace": "open-p", "hash": -1578622476, "terms": ["What is the recommended way to use dependent configs?", "is the recommended way to use dependent configs?", "the recommended way to use dependent configs?", "recommended way to use dependent configs?", "way to use dependent configs?", "to use dependent configs?", "use dependent configs?", "dependent configs?", "configs?"], "tokens": ["What", "is", "the", "recommended", "way", "to", "use", "dependent", "configs", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1571848888", "fields": {"term": "What is generation in context of dependent configs?", "namespace": "open-p", "hash": -1571848888, "terms": ["What is generation in context of dependent configs?", "is generation in context of dependent configs?", "generation in context of dependent configs?", "in context of dependent configs?", "context of dependent configs?", "of dependent configs?", "dependent configs?", "configs?"], "tokens": ["What", "is", "generation", "in", "context", "of", "dependent", "configs", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1802814280", "fields": {"term": "How do you ensure dependent and derived configs are of same generation?", "namespace": "open-p", "hash": 1802814280, "terms": ["How do you ensure dependent and derived configs are of same generation?", "do you ensure dependent and derived configs are of same generation?", "you ensure dependent and derived configs are of same generation?", "ensure dependent and derived configs are of same generation?", "dependent and derived configs are of same generation?", "and derived configs are of same generation?", "derived configs are of same generation?", "configs are of same generation?", "are of same generation?", "of same generation?", "same generation?", "generation?"], "tokens": ["How", "do", "you", "ensure", "dependent", "and", "derived", "configs", "are", "of", "same", "generation", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1884187293", "fields": {"term": "What is ConfigRetriever?", "namespace": "open-p", "hash": -1884187293, "terms": ["What is ConfigRetriever?", "is ConfigRetriever?", "ConfigRetriever?"], "tokens": ["What", "is", "ConfigRetriever", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-866812097", "fields": {"term": "What are bootstrap configs?", "namespace": "open-p", "hash": -866812097, "terms": ["What are bootstrap configs?", "are bootstrap configs?", "bootstrap configs?", "configs?"], "tokens": ["What", "are", "bootstrap", "configs", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1972410079", "fields": {"term": "How to use ConfigRetriever?", "namespace": "open-p", "hash": 1972410079, "terms": ["How to use ConfigRetriever?", "to use ConfigRetriever?", "use ConfigRetriever?", "ConfigRetriever?"], "tokens": ["How", "to", "use", "ConfigRetriever", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1800142683", "fields": {"term": "Why fetch all configs at once?", "namespace": "open-p", "hash": 1800142683, "terms": ["Why fetch all configs at once?", "fetch all configs at once?", "all configs at once?", "configs at once?", "at once?", "once?"], "tokens": ["Why", "fetch", "all", "configs", "at", "once", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1845164897", "fields": {"term": "What is configserver?", "namespace": "open-p", "hash": 1845164897, "terms": ["What is configserver?", "is configserver?", "configserver?"], "tokens": ["What", "is", "configserver", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::752503750", "fields": {"term": "What is configserver.requests?", "namespace": "open-p", "hash": 752503750, "terms": ["What is configserver.requests?", "is configserver.requests?", "configserver.requests?"], "tokens": ["What", "is", "configserver", "requests", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::-1037957314", "fields": {"term": "What is the unit of configserver.latency?", "namespace": "open-p", "hash": -1037957314, "terms": ["What is the unit of configserver.latency?", "is the unit of configserver.latency?", "the unit of configserver.latency?", "unit of configserver.latency?", "of configserver.latency?", "configserver.latency?"], "tokens": ["What", "is", "the", "unit", "of", "configserver", "latency", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::-1120760620", "fields": {"term": "What is configserver.activeSessions?", "namespace": "open-p", "hash": -1120760620, "terms": ["What is configserver.activeSessions?", "is configserver.activeSessions?", "configserver.activeSessions?"], "tokens": ["What", "is", "configserver", "activeSessions", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-853336401", "fields": {"term": "What is configserver.rpcServerWorkQueueSize?", "namespace": "open-p", "hash": -853336401, "terms": ["What is configserver.rpcServerWorkQueueSize?", "is configserver.rpcServerWorkQueueSize?", "configserver.rpcServerWorkQueueSize?"], "tokens": ["What", "is", "configserver", "rpcServerWorkQueueSize", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1145703145", "fields": {"term": "What is configserver.zkMaxLatency?", "namespace": "open-p", "hash": -1145703145, "terms": ["What is configserver.zkMaxLatency?", "is configserver.zkMaxLatency?", "configserver.zkMaxLatency?"], "tokens": ["What", "is", "configserver", "zkMaxLatency", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::-2081524861", "fields": {"term": "What is configserver.zkConnections?", "namespace": "open-p", "hash": -2081524861, "terms": ["What is configserver.zkConnections?", "is configserver.zkConnections?", "configserver.zkConnections?"], "tokens": ["What", "is", "configserver", "zkConnections", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::221703888", "fields": {"term": "What is the purpose of configserver.cacheChecksumElems?", "namespace": "open-p", "hash": 221703888, "terms": ["What is the purpose of configserver.cacheChecksumElems?", "is the purpose of configserver.cacheChecksumElems?", "the purpose of configserver.cacheChecksumElems?", "purpose of configserver.cacheChecksumElems?", "of configserver.cacheChecksumElems?", "configserver.cacheChecksumElems?"], "tokens": ["What", "is", "the", "purpose", "of", "configserver", "cacheChecksumElems", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1552014259", "fields": {"term": "What are Vespa Configuration Servers?", "namespace": "open-p", "hash": -1552014259, "terms": ["What are Vespa Configuration Servers?", "are Vespa Configuration Servers?", "Vespa Configuration Servers?", "Configuration Servers?", "Servers?"], "tokens": ["What", "are", "Vespa", "Configuration", "Servers", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::94133360", "fields": {"term": "What is the role of config servers?", "namespace": "open-p", "hash": 94133360, "terms": ["What is the role of config servers?", "is the role of config servers?", "the role of config servers?", "role of config servers?", "of config servers?", "config servers?", "servers?"], "tokens": ["What", "is", "the", "role", "of", "config", "servers", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1087889125", "fields": {"term": "How is Vespa configuration set up?", "namespace": "open-p", "hash": 1087889125, "terms": ["How is Vespa configuration set up?", "is Vespa configuration set up?", "Vespa configuration set up?", "configuration set up?", "set up?", "up?"], "tokens": ["How", "is", "Vespa", "configuration", "set", "up", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::65770840", "fields": {"term": "What is Apache ZooKeeper used for?", "namespace": "open-p", "hash": 65770840, "terms": ["What is Apache ZooKeeper used for?", "is Apache ZooKeeper used for?", "Apache ZooKeeper used for?", "ZooKeeper used for?", "used for?", "for?"], "tokens": ["What", "is", "Apache", "ZooKeeper", "used", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2133763052", "fields": {"term": "Where can I find practical examples of multi-configserver configuration?", "namespace": "open-p", "hash": -2133763052, "terms": ["Where can I find practical examples of multi-configserver configuration?", "can I find practical examples of multi-configserver configuration?", "I find practical examples of multi-configserver configuration?", "find practical examples of multi-configserver configuration?", "practical examples of multi-configserver configuration?", "examples of multi-configserver configuration?", "of multi-configserver configuration?", "multi-configserver configuration?", "configuration?"], "tokens": ["Where", "can", "I", "find", "practical", "examples", "of", "multi", "configserver", "configuration", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-407069687", "fields": {"term": "Can Vespa be configured without config servers?", "namespace": "open-p", "hash": -407069687, "terms": ["Can Vespa be configured without config servers?", "Vespa be configured without config servers?", "be configured without config servers?", "configured without config servers?", "without config servers?", "config servers?", "servers?"], "tokens": ["Can", "Vespa", "be", "configured", "without", "config", "servers", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1694610364", "fields": {"term": "How can I check the health of a config server?", "namespace": "open-p", "hash": -1694610364, "terms": ["How can I check the health of a config server?", "can I check the health of a config server?", "I check the health of a config server?", "check the health of a config server?", "the health of a config server?", "health of a config server?", "of a config server?", "a config server?", "config server?", "server?"], "tokens": ["How", "can", "I", "check", "the", "health", "of", "a", "config", "server", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1046737277", "fields": {"term": "What is the URL to check config server health?", "namespace": "open-p", "hash": 1046737277, "terms": ["What is the URL to check config server health?", "is the URL to check config server health?", "the URL to check config server health?", "URL to check config server health?", "to check config server health?", "check config server health?", "config server health?", "server health?", "health?"], "tokens": ["What", "is", "the", "URL", "to", "check", "config", "server", "health", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::5327789", "fields": {"term": "What is a config server?", "namespace": "open-p", "hash": 5327789, "terms": ["What is a config server?", "is a config server?", "a config server?", "config server?", "server?"], "tokens": ["What", "is", "a", "config", "server", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::-856302266", "fields": {"term": "How does the config server work?", "namespace": "open-p", "hash": -856302266, "terms": ["How does the config server work?", "does the config server work?", "the config server work?", "config server work?", "server work?", "work?"], "tokens": ["How", "does", "the", "config", "server", "work", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2140921635", "fields": {"term": "What is config generation 0?", "namespace": "open-p", "hash": 2140921635, "terms": ["What is config generation 0?", "is config generation 0?", "config generation 0?", "generation 0?", "0?"], "tokens": ["What", "is", "config", "generation", "0", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::123362540", "fields": {"term": "What is the URL to check config server config?", "namespace": "open-p", "hash": 123362540, "terms": ["What is the URL to check config server config?", "is the URL to check config server config?", "the URL to check config server config?", "URL to check config server config?", "to check config server config?", "check config server config?", "config server config?", "server config?", "config?"], "tokens": ["What", "is", "the", "URL", "to", "check", "config", "server", "config", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-37395860", "fields": {"term": "Can I change the config server through application deployment?", "namespace": "open-p", "hash": -37395860, "terms": ["Can I change the config server through application deployment?", "I change the config server through application deployment?", "change the config server through application deployment?", "the config server through application deployment?", "config server through application deployment?", "server through application deployment?", "through application deployment?", "application deployment?", "deployment?"], "tokens": ["Can", "I", "change", "the", "config", "server", "through", "application", "deployment", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1069289763", "fields": {"term": "What is the config proxy?", "namespace": "open-p", "hash": -1069289763, "terms": ["What is the config proxy?", "is the config proxy?", "the config proxy?", "config proxy?", "proxy?"], "tokens": ["What", "is", "the", "config", "proxy", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::592333224", "fields": {"term": "How many nodes are needed for fault-tolerant system?", "namespace": "open-p", "hash": 592333224, "terms": ["How many nodes are needed for fault-tolerant system?", "many nodes are needed for fault-tolerant system?", "nodes are needed for fault-tolerant system?", "are needed for fault-tolerant system?", "needed for fault-tolerant system?", "for fault-tolerant system?", "fault-tolerant system?", "system?"], "tokens": ["How", "many", "nodes", "are", "needed", "for", "fault", "tolerant", "system", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::753110872", "fields": {"term": "What happens when the config server goes down?", "namespace": "open-p", "hash": 753110872, "terms": ["What happens when the config server goes down?", "happens when the config server goes down?", "when the config server goes down?", "the config server goes down?", "config server goes down?", "server goes down?", "goes down?", "down?"], "tokens": ["What", "happens", "when", "the", "config", "server", "goes", "down", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1241856429", "fields": {"term": "What is minimun number of nodes for fault-tolerant config?", "namespace": "open-p", "hash": -1241856429, "terms": ["What is minimun number of nodes for fault-tolerant config?", "is minimun number of nodes for fault-tolerant config?", "minimun number of nodes for fault-tolerant config?", "number of nodes for fault-tolerant config?", "of nodes for fault-tolerant config?", "nodes for fault-tolerant config?", "for fault-tolerant config?", "fault-tolerant config?", "config?"], "tokens": ["What", "is", "minimun", "number", "of", "nodes", "for", "fault", "tolerant", "config", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1871997157", "fields": {"term": "What is admin model reference?", "namespace": "open-p", "hash": -1871997157, "terms": ["What is admin model reference?", "is admin model reference?", "admin model reference?", "model reference?", "reference?"], "tokens": ["What", "is", "admin", "model", "reference", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::65677485", "fields": {"term": "Where are config servers defined?", "namespace": "open-p", "hash": 65677485, "terms": ["Where are config servers defined?", "are config servers defined?", "config servers defined?", "servers defined?", "defined?"], "tokens": ["Where", "are", "config", "servers", "defined", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1683619217", "fields": {"term": "How to start a Vespa application instance?", "namespace": "open-p", "hash": -1683619217, "terms": ["How to start a Vespa application instance?", "to start a Vespa application instance?", "start a Vespa application instance?", "a Vespa application instance?", "Vespa application instance?", "application instance?", "instance?"], "tokens": ["How", "to", "start", "a", "Vespa", "application", "instance", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::863327796", "fields": {"term": "How to start config servers?", "namespace": "open-p", "hash": 863327796, "terms": ["How to start config servers?", "to start config servers?", "start config servers?", "config servers?", "servers?"], "tokens": ["How", "to", "start", "config", "servers", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1293316885", "fields": {"term": "How to start Vespa nodes?", "namespace": "open-p", "hash": 1293316885, "terms": ["How to start Vespa nodes?", "to start Vespa nodes?", "start Vespa nodes?", "Vespa nodes?", "nodes?"], "tokens": ["How", "to", "start", "Vespa", "nodes", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1638802032", "fields": {"term": "How to verify config server health?", "namespace": "open-p", "hash": 1638802032, "terms": ["How to verify config server health?", "to verify config server health?", "verify config server health?", "config server health?", "server health?", "health?"], "tokens": ["How", "to", "verify", "config", "server", "health", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-818480266", "fields": {"term": "What is the Vespa start sequence?", "namespace": "open-p", "hash": -818480266, "terms": ["What is the Vespa start sequence?", "is the Vespa start sequence?", "the Vespa start sequence?", "Vespa start sequence?", "start sequence?", "sequence?"], "tokens": ["What", "is", "the", "Vespa", "start", "sequence", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-568103904", "fields": {"term": "How to add a config server node?", "namespace": "open-p", "hash": -568103904, "terms": ["How to add a config server node?", "to add a config server node?", "add a config server node?", "a config server node?", "config server node?", "server node?", "node?"], "tokens": ["How", "to", "add", "a", "config", "server", "node", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1803444046", "fields": {"term": "What is ZooKeeper configuration?", "namespace": "open-p", "hash": 1803444046, "terms": ["What is ZooKeeper configuration?", "is ZooKeeper configuration?", "ZooKeeper configuration?", "configuration?"], "tokens": ["What", "is", "ZooKeeper", "configuration", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2056310941", "fields": {"term": "What is recommended when adding config server nodes?", "namespace": "open-p", "hash": 2056310941, "terms": ["What is recommended when adding config server nodes?", "is recommended when adding config server nodes?", "recommended when adding config server nodes?", "when adding config server nodes?", "adding config server nodes?", "config server nodes?", "server nodes?", "nodes?"], "tokens": ["What", "is", "recommended", "when", "adding", "config", "server", "nodes", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::366289043", "fields": {"term": "How to install Vespa on config server node?", "namespace": "open-p", "hash": 366289043, "terms": ["How to install Vespa on config server node?", "to install Vespa on config server node?", "install Vespa on config server node?", "Vespa on config server node?", "on config server node?", "config server node?", "server node?", "node?"], "tokens": ["How", "to", "install", "Vespa", "on", "config", "server", "node", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1449841687", "fields": {"term": "What files need to be updated when adding a config server node?", "namespace": "open-p", "hash": -1449841687, "terms": ["What files need to be updated when adding a config server node?", "files need to be updated when adding a config server node?", "need to be updated when adding a config server node?", "to be updated when adding a config server node?", "be updated when adding a config server node?", "updated when adding a config server node?", "when adding a config server node?", "adding a config server node?", "a config server node?", "config server node?", "server node?", "node?"], "tokens": ["What", "files", "need", "to", "be", "updated", "when", "adding", "a", "config", "server", "node", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::9154691", "fields": {"term": "How to prepare and activate Vespa deploy after adding config server node?", "namespace": "open-p", "hash": 9154691, "terms": ["How to prepare and activate Vespa deploy after adding config server node?", "to prepare and activate Vespa deploy after adding config server node?", "prepare and activate Vespa deploy after adding config server node?", "and activate Vespa deploy after adding config server node?", "activate Vespa deploy after adding config server node?", "Vespa deploy after adding config server node?", "deploy after adding config server node?", "after adding config server node?", "adding config server node?", "config server node?", "server node?", "node?"], "tokens": ["How", "to", "prepare", "and", "activate", "Vespa", "deploy", "after", "adding", "config", "server", "node", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::414953211", "fields": {"term": "How to start using the new config servers?", "namespace": "open-p", "hash": 414953211, "terms": ["How to start using the new config servers?", "to start using the new config servers?", "start using the new config servers?", "using the new config servers?", "the new config servers?", "new config servers?", "config servers?", "servers?"], "tokens": ["How", "to", "start", "using", "the", "new", "config", "servers", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1538422290", "fields": {"term": "Will the config servers automatically redistribute application data to new nodes?", "namespace": "open-p", "hash": -1538422290, "terms": ["Will the config servers automatically redistribute application data to new nodes?", "the config servers automatically redistribute application data to new nodes?", "config servers automatically redistribute application data to new nodes?", "servers automatically redistribute application data to new nodes?", "automatically redistribute application data to new nodes?", "redistribute application data to new nodes?", "application data to new nodes?", "data to new nodes?", "to new nodes?", "new nodes?", "nodes?"], "tokens": ["Will", "the", "config", "servers", "automatically", "redistribute", "application", "data", "to", "new", "nodes", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-33765961", "fields": {"term": "What is scaling down?", "namespace": "open-p", "hash": -33765961, "terms": ["What is scaling down?", "is scaling down?", "scaling down?", "down?"], "tokens": ["What", "is", "scaling", "down", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1508374907", "fields": {"term": "How to remove config servers?", "namespace": "open-p", "hash": 1508374907, "terms": ["How to remove config servers?", "to remove config servers?", "remove config servers?", "config servers?", "servers?"], "tokens": ["How", "to", "remove", "config", "servers", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1641559452", "fields": {"term": "What is the process to replace a node?", "namespace": "open-p", "hash": -1641559452, "terms": ["What is the process to replace a node?", "is the process to replace a node?", "the process to replace a node?", "process to replace a node?", "to replace a node?", "replace a node?", "a node?", "node?"], "tokens": ["What", "is", "the", "process", "to", "replace", "a", "node", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::20384451", "fields": {"term": "Can you replace multiple nodes at once?", "namespace": "open-p", "hash": 20384451, "terms": ["Can you replace multiple nodes at once?", "you replace multiple nodes at once?", "replace multiple nodes at once?", "multiple nodes at once?", "nodes at once?", "at once?", "once?"], "tokens": ["Can", "you", "replace", "multiple", "nodes", "at", "once", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1074219475", "fields": {"term": "How do you scale up with one config server?", "namespace": "open-p", "hash": -1074219475, "terms": ["How do you scale up with one config server?", "do you scale up with one config server?", "you scale up with one config server?", "scale up with one config server?", "up with one config server?", "with one config server?", "one config server?", "config server?", "server?"], "tokens": ["How", "do", "you", "scale", "up", "with", "one", "config", "server", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1842812370", "fields": {"term": "What happens if you replace all nodes at once?", "namespace": "open-p", "hash": -1842812370, "terms": ["What happens if you replace all nodes at once?", "happens if you replace all nodes at once?", "if you replace all nodes at once?", "you replace all nodes at once?", "replace all nodes at once?", "all nodes at once?", "nodes at once?", "at once?", "once?"], "tokens": ["What", "happens", "if", "you", "replace", "all", "nodes", "at", "once", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-94422903", "fields": {"term": "What is ZooKeeper?", "namespace": "open-p", "hash": -94422903, "terms": ["What is ZooKeeper?", "is ZooKeeper?", "ZooKeeper?"], "tokens": ["What", "is", "ZooKeeper", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::1331000071", "fields": {"term": "What does ZooKeeper do?", "namespace": "open-p", "hash": 1331000071, "terms": ["What does ZooKeeper do?", "does ZooKeeper do?", "ZooKeeper do?", "do?"], "tokens": ["What", "does", "ZooKeeper", "do", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1061761041", "fields": {"term": "Where is application data stored?", "namespace": "open-p", "hash": 1061761041, "terms": ["Where is application data stored?", "is application data stored?", "application data stored?", "data stored?", "stored?"], "tokens": ["Where", "is", "application", "data", "stored", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::285528256", "fields": {"term": "What is vespa-zkcli?", "namespace": "open-p", "hash": 285528256, "terms": ["What is vespa-zkcli?", "is vespa-zkcli?", "vespa-zkcli?"], "tokens": ["What", "is", "vespa", "zkcli", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2068692558", "fields": {"term": "How to inspect ZooKeeper state?", "namespace": "open-p", "hash": -2068692558, "terms": ["How to inspect ZooKeeper state?", "to inspect ZooKeeper state?", "inspect ZooKeeper state?", "ZooKeeper state?", "state?"], "tokens": ["How", "to", "inspect", "ZooKeeper", "state", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1451161184", "fields": {"term": "Where does ZooKeeper store data?", "namespace": "open-p", "hash": -1451161184, "terms": ["Where does ZooKeeper store data?", "does ZooKeeper store data?", "ZooKeeper store data?", "store data?", "data?"], "tokens": ["Where", "does", "ZooKeeper", "store", "data", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-743473439", "fields": {"term": "What is the role of ZooKeeper in vespa-deploy?", "namespace": "open-p", "hash": -743473439, "terms": ["What is the role of ZooKeeper in vespa-deploy?", "is the role of ZooKeeper in vespa-deploy?", "the role of ZooKeeper in vespa-deploy?", "role of ZooKeeper in vespa-deploy?", "of ZooKeeper in vespa-deploy?", "ZooKeeper in vespa-deploy?", "in vespa-deploy?", "vespa-deploy?"], "tokens": ["What", "is", "the", "role", "of", "ZooKeeper", "in", "vespa", "deploy", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1136849816", "fields": {"term": "What is ZooKeeper cluster?", "namespace": "open-p", "hash": 1136849816, "terms": ["What is ZooKeeper cluster?", "is ZooKeeper cluster?", "ZooKeeper cluster?", "cluster?"], "tokens": ["What", "is", "ZooKeeper", "cluster", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-250931777", "fields": {"term": "Where is zookeeper.cfg written?", "namespace": "open-p", "hash": -250931777, "terms": ["Where is zookeeper.cfg written?", "is zookeeper.cfg written?", "zookeeper.cfg written?", "written?"], "tokens": ["Where", "is", "zookeeper", "cfg", "written", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-494893964", "fields": {"term": "What happens when VESPA_CONFIGSERVERS change?", "namespace": "open-p", "hash": -494893964, "terms": ["What happens when VESPA_CONFIGSERVERS change?", "happens when VESPA_CONFIGSERVERS change?", "when VESPA_CONFIGSERVERS change?", "VESPA_CONFIGSERVERS change?", "change?"], "tokens": ["What", "happens", "when", "VESPA_CONFIGSERVERS", "change", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-218389562", "fields": {"term": "What is the recovery procedure?", "namespace": "open-p", "hash": -218389562, "terms": ["What is the recovery procedure?", "is the recovery procedure?", "the recovery procedure?", "recovery procedure?", "procedure?"], "tokens": ["What", "is", "the", "recovery", "procedure", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::797367037", "fields": {"term": "What causes data corruption?", "namespace": "open-p", "hash": 797367037, "terms": ["What causes data corruption?", "causes data corruption?", "data corruption?", "corruption?"], "tokens": ["What", "causes", "data", "corruption", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::869518751", "fields": {"term": "How to run cluster-controllers in standalone mode?", "namespace": "open-p", "hash": 869518751, "terms": ["How to run cluster-controllers in standalone mode?", "to run cluster-controllers in standalone mode?", "run cluster-controllers in standalone mode?", "cluster-controllers in standalone mode?", "in standalone mode?", "standalone mode?", "mode?"], "tokens": ["How", "to", "run", "cluster", "controllers", "in", "standalone", "mode", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1268012175", "fields": {"term": "What is vespa-stop-configserver?", "namespace": "open-p", "hash": 1268012175, "terms": ["What is vespa-stop-configserver?", "is vespa-stop-configserver?", "vespa-stop-configserver?"], "tokens": ["What", "is", "vespa", "stop", "configserver", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-745270150", "fields": {"term": "What causes timeout during deploy?", "namespace": "open-p", "hash": -745270150, "terms": ["What causes timeout during deploy?", "causes timeout during deploy?", "timeout during deploy?", "during deploy?", "deploy?"], "tokens": ["What", "causes", "timeout", "during", "deploy", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-608074555", "fields": {"term": "How to work around config server timeout?", "namespace": "open-p", "hash": -608074555, "terms": ["How to work around config server timeout?", "to work around config server timeout?", "work around config server timeout?", "around config server timeout?", "config server timeout?", "server timeout?", "timeout?"], "tokens": ["How", "to", "work", "around", "config", "server", "timeout", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-115445085", "fields": {"term": "What is VESPA_CONFIGSERVER_ZOOKEEPER_BARRIER_TIMEOUT set to?", "namespace": "open-p", "hash": -115445085, "terms": ["What is VESPA_CONFIGSERVER_ZOOKEEPER_BARRIER_TIMEOUT set to?", "is VESPA_CONFIGSERVER_ZOOKEEPER_BARRIER_TIMEOUT set to?", "VESPA_CONFIGSERVER_ZOOKEEPER_BARRIER_TIMEOUT set to?", "set to?", "to?"], "tokens": ["What", "is", "VESPA_CONFIGSERVER_ZOOKEEPER_BARRIER_TIMEOUT", "set", "to", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-384054266", "fields": {"term": "How to access config from non-config node?", "namespace": "open-p", "hash": -384054266, "terms": ["How to access config from non-config node?", "to access config from non-config node?", "access config from non-config node?", "config from non-config node?", "from non-config node?", "non-config node?", "node?"], "tokens": ["How", "to", "access", "config", "from", "non", "config", "node", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1124303977", "fields": {"term": "Which environment variable to use?", "namespace": "open-p", "hash": 1124303977, "terms": ["Which environment variable to use?", "environment variable to use?", "variable to use?", "to use?", "use?"], "tokens": ["Which", "environment", "variable", "to", "use", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::478911767", "fields": {"term": "What is the minimum JVM heap size?", "namespace": "open-p", "hash": 478911767, "terms": ["What is the minimum JVM heap size?", "is the minimum JVM heap size?", "the minimum JVM heap size?", "minimum JVM heap size?", "JVM heap size?", "heap size?", "size?"], "tokens": ["What", "is", "the", "minimum", "JVM", "heap", "size", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1211774680", "fields": {"term": "What is the maximum JVM heap size?", "namespace": "open-p", "hash": -1211774680, "terms": ["What is the maximum JVM heap size?", "is the maximum JVM heap size?", "the maximum JVM heap size?", "maximum JVM heap size?", "JVM heap size?", "heap size?", "size?"], "tokens": ["What", "is", "the", "maximum", "JVM", "heap", "size", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::727411152", "fields": {"term": "Can the maximum heap size be changed?", "namespace": "open-p", "hash": 727411152, "terms": ["Can the maximum heap size be changed?", "the maximum heap size be changed?", "maximum heap size be changed?", "heap size be changed?", "size be changed?", "be changed?", "changed?"], "tokens": ["Can", "the", "maximum", "heap", "size", "be", "changed", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1258061952", "fields": {"term": "Is disk space required for the transaction log?", "namespace": "open-p", "hash": 1258061952, "terms": ["Is disk space required for the transaction log?", "disk space required for the transaction log?", "space required for the transaction log?", "required for the transaction log?", "for the transaction log?", "the transaction log?", "transaction log?", "log?"], "tokens": ["Is", "disk", "space", "required", "for", "the", "transaction", "log", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1898350486", "fields": {"term": "What happens to the old items in the transaction log?", "namespace": "open-p", "hash": 1898350486, "terms": ["What happens to the old items in the transaction log?", "happens to the old items in the transaction log?", "to the old items in the transaction log?", "the old items in the transaction log?", "old items in the transaction log?", "items in the transaction log?", "in the transaction log?", "the transaction log?", "transaction log?", "log?"], "tokens": ["What", "happens", "to", "the", "old", "items", "in", "the", "transaction", "log", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1477797038", "fields": {"term": "How will a server with a lot of disk I/O affect performance?", "namespace": "open-p", "hash": 1477797038, "terms": ["How will a server with a lot of disk I/O affect performance?", "will a server with a lot of disk I/O affect performance?", "a server with a lot of disk I/O affect performance?", "server with a lot of disk I/O affect performance?", "with a lot of disk I/O affect performance?", "a lot of disk I/O affect performance?", "lot of disk I/O affect performance?", "of disk I/O affect performance?", "disk I/O affect performance?", "I/O affect performance?", "affect performance?", "performance?"], "tokens": ["How", "will", "a", "server", "with", "a", "lot", "of", "disk", "I", "/", "O", "affect", "performance", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1833927681", "fields": {"term": "Is running on a server with a lot of disk I/O recommended?", "namespace": "open-p", "hash": -1833927681, "terms": ["Is running on a server with a lot of disk I/O recommended?", "running on a server with a lot of disk I/O recommended?", "on a server with a lot of disk I/O recommended?", "a server with a lot of disk I/O recommended?", "server with a lot of disk I/O recommended?", "with a lot of disk I/O recommended?", "a lot of disk I/O recommended?", "lot of disk I/O recommended?", "of disk I/O recommended?", "disk I/O recommended?", "I/O recommended?", "recommended?"], "tokens": ["Is", "running", "on", "a", "server", "with", "a", "lot", "of", "disk", "I", "/", "O", "recommended", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1596212032", "fields": {"term": "How to change config server RPC port?", "namespace": "open-p", "hash": -1596212032, "terms": ["How to change config server RPC port?", "to change config server RPC port?", "change config server RPC port?", "config server RPC port?", "server RPC port?", "RPC port?", "port?"], "tokens": ["How", "to", "change", "config", "server", "RPC", "port", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-794219626", "fields": {"term": "Where to change HTTP port?", "namespace": "open-p", "hash": -794219626, "terms": ["Where to change HTTP port?", "to change HTTP port?", "change HTTP port?", "HTTP port?", "port?"], "tokens": ["Where", "to", "change", "HTTP", "port", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-917395656", "fields": {"term": "How to check if a config server is up?", "namespace": "open-p", "hash": -917395656, "terms": ["How to check if a config server is up?", "to check if a config server is up?", "check if a config server is up?", "if a config server is up?", "a config server is up?", "config server is up?", "server is up?", "is up?", "up?"], "tokens": ["How", "to", "check", "if", "a", "config", "server", "is", "up", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-215328993", "fields": {"term": "How to check consistency between config servers?", "namespace": "open-p", "hash": -215328993, "terms": ["How to check consistency between config servers?", "to check consistency between config servers?", "check consistency between config servers?", "consistency between config servers?", "between config servers?", "config servers?", "servers?"], "tokens": ["How", "to", "check", "consistency", "between", "config", "servers", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::49531581", "fields": {"term": "What happens if a config server goes down?", "namespace": "open-p", "hash": 49531581, "terms": ["What happens if a config server goes down?", "happens if a config server goes down?", "if a config server goes down?", "a config server goes down?", "config server goes down?", "server goes down?", "goes down?", "down?"], "tokens": ["What", "happens", "if", "a", "config", "server", "goes", "down", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1741758786", "fields": {"term": "How to check file distribution status?", "namespace": "open-p", "hash": -1741758786, "terms": ["How to check file distribution status?", "to check file distribution status?", "check file distribution status?", "file distribution status?", "distribution status?", "status?"], "tokens": ["How", "to", "check", "file", "distribution", "status", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-356706774", "fields": {"term": "What causes startup or deploy problems?", "namespace": "open-p", "hash": -356706774, "terms": ["What causes startup or deploy problems?", "causes startup or deploy problems?", "startup or deploy problems?", "or deploy problems?", "deploy problems?", "problems?"], "tokens": ["What", "causes", "startup", "or", "deploy", "problems", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1097933078", "fields": {"term": "What can cause Java IOException in ZooKeeper?", "namespace": "open-p", "hash": -1097933078, "terms": ["What can cause Java IOException in ZooKeeper?", "can cause Java IOException in ZooKeeper?", "cause Java IOException in ZooKeeper?", "Java IOException in ZooKeeper?", "IOException in ZooKeeper?", "in ZooKeeper?", "ZooKeeper?"], "tokens": ["What", "can", "cause", "Java", "IOException", "in", "ZooKeeper", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2088156026", "fields": {"term": "What does the application code interface with?", "namespace": "open-p", "hash": 2088156026, "terms": ["What does the application code interface with?", "does the application code interface with?", "the application code interface with?", "application code interface with?", "code interface with?", "interface with?", "with?"], "tokens": ["What", "does", "the", "application", "code", "interface", "with", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::904058417", "fields": {"term": "How are code and config kept in sync?", "namespace": "open-p", "hash": 904058417, "terms": ["How are code and config kept in sync?", "are code and config kept in sync?", "code and config kept in sync?", "and config kept in sync?", "config kept in sync?", "kept in sync?", "in sync?", "sync?"], "tokens": ["How", "are", "code", "and", "config", "kept", "in", "sync", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1737697254", "fields": {"term": "Where to place config definition file?", "namespace": "open-p", "hash": 1737697254, "terms": ["Where to place config definition file?", "to place config definition file?", "place config definition file?", "config definition file?", "definition file?", "file?"], "tokens": ["Where", "to", "place", "config", "definition", "file", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-15298152", "fields": {"term": "What is the syntax of a config definition file?", "namespace": "open-p", "hash": -15298152, "terms": ["What is the syntax of a config definition file?", "is the syntax of a config definition file?", "the syntax of a config definition file?", "syntax of a config definition file?", "of a config definition file?", "a config definition file?", "config definition file?", "definition file?", "file?"], "tokens": ["What", "is", "the", "syntax", "of", "a", "config", "definition", "file", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1671244079", "fields": {"term": "How to generate Java config classes?", "namespace": "open-p", "hash": -1671244079, "terms": ["How to generate Java config classes?", "to generate Java config classes?", "generate Java config classes?", "Java config classes?", "config classes?", "classes?"], "tokens": ["How", "to", "generate", "Java", "config", "classes", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::309274991", "fields": {"term": "Where are config classes generated?", "namespace": "open-p", "hash": 309274991, "terms": ["Where are config classes generated?", "are config classes generated?", "config classes generated?", "classes generated?", "generated?"], "tokens": ["Where", "are", "config", "classes", "generated", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1237982162", "fields": {"term": "What is the full name of the generated Java class?", "namespace": "open-p", "hash": -1237982162, "terms": ["What is the full name of the generated Java class?", "is the full name of the generated Java class?", "the full name of the generated Java class?", "full name of the generated Java class?", "name of the generated Java class?", "of the generated Java class?", "the generated Java class?", "generated Java class?", "Java class?", "class?"], "tokens": ["What", "is", "the", "full", "name", "of", "the", "generated", "Java", "class", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1297914172", "fields": {"term": "What is a generated config class?", "namespace": "open-p", "hash": -1297914172, "terms": ["What is a generated config class?", "is a generated config class?", "a generated config class?", "generated config class?", "config class?", "class?"], "tokens": ["What", "is", "a", "generated", "config", "class", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1296877845", "fields": {"term": "What is constructor injection?", "namespace": "open-p", "hash": 1296877845, "terms": ["What is constructor injection?", "is constructor injection?", "constructor injection?", "injection?"], "tokens": ["What", "is", "constructor", "injection", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1610524215", "fields": {"term": "How do you override config values?", "namespace": "open-p", "hash": -1610524215, "terms": ["How do you override config values?", "do you override config values?", "you override config values?", "override config values?", "config values?", "values?"], "tokens": ["How", "do", "you", "override", "config", "values", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-839276728", "fields": {"term": "What is a builder API?", "namespace": "open-p", "hash": -839276728, "terms": ["What is a builder API?", "is a builder API?", "a builder API?", "builder API?", "API?"], "tokens": ["What", "is", "a", "builder", "API", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1984044452", "fields": {"term": "How do you create config objects for unit testing?", "namespace": "open-p", "hash": -1984044452, "terms": ["How do you create config objects for unit testing?", "do you create config objects for unit testing?", "you create config objects for unit testing?", "create config objects for unit testing?", "config objects for unit testing?", "objects for unit testing?", "for unit testing?", "unit testing?", "testing?"], "tokens": ["How", "do", "you", "create", "config", "objects", "for", "unit", "testing", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-289145867", "fields": {"term": "What is a unit test?", "namespace": "open-p", "hash": -289145867, "terms": ["What is a unit test?", "is a unit test?", "a unit test?", "unit test?", "test?"], "tokens": ["What", "is", "a", "unit", "test", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::-1981111537", "fields": {"term": "What is a MyComponentTest?", "namespace": "open-p", "hash": -1981111537, "terms": ["What is a MyComponentTest?", "is a MyComponentTest?", "a MyComponentTest?", "MyComponentTest?"], "tokens": ["What", "is", "a", "MyComponentTest", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-127324287", "fields": {"term": "What is a MyComponent?", "namespace": "open-p", "hash": -127324287, "terms": ["What is a MyComponent?", "is a MyComponent?", "a MyComponent?", "MyComponent?"], "tokens": ["What", "is", "a", "MyComponent", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1679475005", "fields": {"term": "What are the components of a MyComponentConfig?", "namespace": "open-p", "hash": -1679475005, "terms": ["What are the components of a MyComponentConfig?", "are the components of a MyComponentConfig?", "the components of a MyComponentConfig?", "components of a MyComponentConfig?", "of a MyComponentConfig?", "a MyComponentConfig?", "MyComponentConfig?"], "tokens": ["What", "are", "the", "components", "of", "a", "MyComponentConfig", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::988008945", "fields": {"term": "How can files be transferred?", "namespace": "open-p", "hash": 988008945, "terms": ["How can files be transferred?", "can files be transferred?", "files be transferred?", "be transferred?", "transferred?"], "tokens": ["How", "can", "files", "be", "transferred", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-756175118", "fields": {"term": "What is a provider component?", "namespace": "open-p", "hash": -756175118, "terms": ["What is a provider component?", "is a provider component?", "a provider component?", "provider component?", "component?"], "tokens": ["What", "is", "a", "provider", "component", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2058252539", "fields": {"term": "What is the config def file?", "namespace": "open-p", "hash": -2058252539, "terms": ["What is the config def file?", "is the config def file?", "the config def file?", "config def file?", "def file?", "file?"], "tokens": ["What", "is", "the", "config", "def", "file", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1847715848", "fields": {"term": "What is file distribution?", "namespace": "open-p", "hash": 1847715848, "terms": ["What is file distribution?", "is file distribution?", "file distribution?", "distribution?"], "tokens": ["What", "is", "file", "distribution", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-481691351", "fields": {"term": "What is URL downloading?", "namespace": "open-p", "hash": -481691351, "terms": ["What is URL downloading?", "is URL downloading?", "URL downloading?", "downloading?"], "tokens": ["What", "is", "URL", "downloading", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1932535747", "fields": {"term": "How can machine-learned models be referenced?", "namespace": "open-p", "hash": -1932535747, "terms": ["How can machine-learned models be referenced?", "can machine-learned models be referenced?", "machine-learned models be referenced?", "models be referenced?", "be referenced?", "referenced?"], "tokens": ["How", "can", "machine", "learned", "models", "be", "referenced", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-609391295", "fields": {"term": "What are the config type options for files?", "namespace": "open-p", "hash": -609391295, "terms": ["What are the config type options for files?", "are the config type options for files?", "the config type options for files?", "config type options for files?", "type options for files?", "options for files?", "for files?", "files?"], "tokens": ["What", "are", "the", "config", "type", "options", "for", "files", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1000486030", "fields": {"term": "What is data redundancy?", "namespace": "open-p", "hash": -1000486030, "terms": ["What is data redundancy?", "is data redundancy?", "data redundancy?", "redundancy?"], "tokens": ["What", "is", "data", "redundancy", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1701443811", "fields": {"term": "What is Vespa?", "namespace": "open-p", "hash": 1701443811, "terms": ["What is Vespa?", "is Vespa?", "Vespa?"], "tokens": ["What", "is", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::224362365", "fields": {"term": "What is eventual consistency?", "namespace": "open-p", "hash": 224362365, "terms": ["What is eventual consistency?", "is eventual consistency?", "eventual consistency?", "consistency?"], "tokens": ["What", "is", "eventual", "consistency", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::547127785", "fields": {"term": "What are acceptable tradeoffs?", "namespace": "open-p", "hash": 547127785, "terms": ["What are acceptable tradeoffs?", "are acceptable tradeoffs?", "acceptable tradeoffs?", "tradeoffs?"], "tokens": ["What", "are", "acceptable", "tradeoffs", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::45495199", "fields": {"term": "Where is eventual consistency efficient?", "namespace": "open-p", "hash": 45495199, "terms": ["Where is eventual consistency efficient?", "is eventual consistency efficient?", "eventual consistency efficient?", "consistency efficient?", "efficient?"], "tokens": ["Where", "is", "eventual", "consistency", "efficient", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1503002005", "fields": {"term": "What does this document aim to do?", "namespace": "open-p", "hash": 1503002005, "terms": ["What does this document aim to do?", "does this document aim to do?", "this document aim to do?", "document aim to do?", "aim to do?", "to do?", "do?"], "tokens": ["What", "does", "this", "document", "aim", "to", "do", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::468082461", "fields": {"term": "What is the CAP theorem?", "namespace": "open-p", "hash": 468082461, "terms": ["What is the CAP theorem?", "is the CAP theorem?", "the CAP theorem?", "CAP theorem?", "theorem?"], "tokens": ["What", "is", "the", "CAP", "theorem", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1946606786", "fields": {"term": "What is consistency in CAP?", "namespace": "open-p", "hash": 1946606786, "terms": ["What is consistency in CAP?", "is consistency in CAP?", "consistency in CAP?", "in CAP?", "CAP?"], "tokens": ["What", "is", "consistency", "in", "CAP", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1994903283", "fields": {"term": "What is availability in CAP?", "namespace": "open-p", "hash": 1994903283, "terms": ["What is availability in CAP?", "is availability in CAP?", "availability in CAP?", "in CAP?", "CAP?"], "tokens": ["What", "is", "availability", "in", "CAP", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::964655634", "fields": {"term": "Does Vespa offer linearizability?", "namespace": "open-p", "hash": 964655634, "terms": ["Does Vespa offer linearizability?", "Vespa offer linearizability?", "offer linearizability?", "linearizability?"], "tokens": ["Does", "Vespa", "offer", "linearizability", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1258183788", "fields": {"term": "Does Vespa offer guaranteed availability?", "namespace": "open-p", "hash": -1258183788, "terms": ["Does Vespa offer guaranteed availability?", "Vespa offer guaranteed availability?", "offer guaranteed availability?", "guaranteed availability?", "availability?"], "tokens": ["Does", "Vespa", "offer", "guaranteed", "availability", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-139972205", "fields": {"term": "What kind of consistency does Vespa offer?", "namespace": "open-p", "hash": -139972205, "terms": ["What kind of consistency does Vespa offer?", "kind of consistency does Vespa offer?", "of consistency does Vespa offer?", "consistency does Vespa offer?", "does Vespa offer?", "Vespa offer?", "offer?"], "tokens": ["What", "kind", "of", "consistency", "does", "Vespa", "offer", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-992110328", "fields": {"term": "What are the limitations of Vespa under CAP?", "namespace": "open-p", "hash": -992110328, "terms": ["What are the limitations of Vespa under CAP?", "are the limitations of Vespa under CAP?", "the limitations of Vespa under CAP?", "limitations of Vespa under CAP?", "of Vespa under CAP?", "Vespa under CAP?", "under CAP?", "CAP?"], "tokens": ["What", "are", "the", "limitations", "of", "Vespa", "under", "CAP", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1625611017", "fields": {"term": "What happens when a client receives a successful write response?", "namespace": "open-p", "hash": -1625611017, "terms": ["What happens when a client receives a successful write response?", "happens when a client receives a successful write response?", "when a client receives a successful write response?", "a client receives a successful write response?", "client receives a successful write response?", "receives a successful write response?", "a successful write response?", "successful write response?", "write response?", "response?"], "tokens": ["What", "happens", "when", "a", "client", "receives", "a", "successful", "write", "response", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::517996250", "fields": {"term": "What is the default replication level for operations?", "namespace": "open-p", "hash": 517996250, "terms": ["What is the default replication level for operations?", "is the default replication level for operations?", "the default replication level for operations?", "default replication level for operations?", "replication level for operations?", "level for operations?", "for operations?", "operations?"], "tokens": ["What", "is", "the", "default", "replication", "level", "for", "operations", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1233518462", "fields": {"term": "How does the system handle sudden power loss?", "namespace": "open-p", "hash": 1233518462, "terms": ["How does the system handle sudden power loss?", "does the system handle sudden power loss?", "the system handle sudden power loss?", "system handle sudden power loss?", "handle sudden power loss?", "sudden power loss?", "power loss?", "loss?"], "tokens": ["How", "does", "the", "system", "handle", "sudden", "power", "loss", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1209822194", "fields": {"term": "What is the consequence of large clock drifts?", "namespace": "open-p", "hash": 1209822194, "terms": ["What is the consequence of large clock drifts?", "is the consequence of large clock drifts?", "the consequence of large clock drifts?", "consequence of large clock drifts?", "of large clock drifts?", "large clock drifts?", "clock drifts?", "drifts?"], "tokens": ["What", "is", "the", "consequence", "of", "large", "clock", "drifts", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2085038005", "fields": {"term": "What is the recommended approach for conditional updates?", "namespace": "open-p", "hash": -2085038005, "terms": ["What is the recommended approach for conditional updates?", "is the recommended approach for conditional updates?", "the recommended approach for conditional updates?", "recommended approach for conditional updates?", "approach for conditional updates?", "for conditional updates?", "conditional updates?", "updates?"], "tokens": ["What", "is", "the", "recommended", "approach", "for", "conditional", "updates", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1885847196", "fields": {"term": "Are multi-document transactions supported?", "namespace": "open-p", "hash": -1885847196, "terms": ["Are multi-document transactions supported?", "multi-document transactions supported?", "transactions supported?", "supported?"], "tokens": ["Are", "multi", "document", "transactions", "supported", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-694503776", "fields": {"term": "How does the system detect and reconcile divergent replicas?", "namespace": "open-p", "hash": -694503776, "terms": ["How does the system detect and reconcile divergent replicas?", "does the system detect and reconcile divergent replicas?", "the system detect and reconcile divergent replicas?", "system detect and reconcile divergent replicas?", "detect and reconcile divergent replicas?", "and reconcile divergent replicas?", "reconcile divergent replicas?", "divergent replicas?", "replicas?"], "tokens": ["How", "does", "the", "system", "detect", "and", "reconcile", "divergent", "replicas", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2125915362", "fields": {"term": "What guarantees do read operations have?", "namespace": "open-p", "hash": -2125915362, "terms": ["What guarantees do read operations have?", "guarantees do read operations have?", "do read operations have?", "read operations have?", "operations have?", "have?"], "tokens": ["What", "guarantees", "do", "read", "operations", "have", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2139169897", "fields": {"term": "What are the limitations of consistent reads?", "namespace": "open-p", "hash": 2139169897, "terms": ["What are the limitations of consistent reads?", "are the limitations of consistent reads?", "the limitations of consistent reads?", "limitations of consistent reads?", "of consistent reads?", "consistent reads?", "reads?"], "tokens": ["What", "are", "the", "limitations", "of", "consistent", "reads", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::445967470", "fields": {"term": "How does Vespa handle partially updated documents?", "namespace": "open-p", "hash": 445967470, "terms": ["How does Vespa handle partially updated documents?", "does Vespa handle partially updated documents?", "Vespa handle partially updated documents?", "handle partially updated documents?", "partially updated documents?", "updated documents?", "documents?"], "tokens": ["How", "does", "Vespa", "handle", "partially", "updated", "documents", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1812295282", "fields": {"term": "Is coverage automatically restored after node failures?", "namespace": "open-p", "hash": -1812295282, "terms": ["Is coverage automatically restored after node failures?", "coverage automatically restored after node failures?", "automatically restored after node failures?", "restored after node failures?", "after node failures?", "node failures?", "failures?"], "tokens": ["Is", "coverage", "automatically", "restored", "after", "node", "failures", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::715731081", "fields": {"term": "What happens if replicas diverge during a Get?", "namespace": "open-p", "hash": 715731081, "terms": ["What happens if replicas diverge during a Get?", "happens if replicas diverge during a Get?", "if replicas diverge during a Get?", "replicas diverge during a Get?", "diverge during a Get?", "during a Get?", "a Get?", "Get?"], "tokens": ["What", "happens", "if", "replicas", "diverge", "during", "a", "Get", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-377954601", "fields": {"term": "What is the difference in behavior between Document V1 and vespa-visit?", "namespace": "open-p", "hash": -377954601, "terms": ["What is the difference in behavior between Document V1 and vespa-visit?", "is the difference in behavior between Document V1 and vespa-visit?", "the difference in behavior between Document V1 and vespa-visit?", "difference in behavior between Document V1 and vespa-visit?", "in behavior between Document V1 and vespa-visit?", "behavior between Document V1 and vespa-visit?", "between Document V1 and vespa-visit?", "Document V1 and vespa-visit?", "V1 and vespa-visit?", "and vespa-visit?", "vespa-visit?"], "tokens": ["What", "is", "the", "difference", "in", "behavior", "between", "Document", "V1", "and", "vespa", "visit", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1003105749", "fields": {"term": "What is expected behavior during visitor operations?", "namespace": "open-p", "hash": 1003105749, "terms": ["What is expected behavior during visitor operations?", "is expected behavior during visitor operations?", "expected behavior during visitor operations?", "behavior during visitor operations?", "during visitor operations?", "visitor operations?", "operations?"], "tokens": ["What", "is", "expected", "behavior", "during", "visitor", "operations", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-484378815", "fields": {"term": "Is snapshot isolation guaranteed during visitor operations?", "namespace": "open-p", "hash": -484378815, "terms": ["Is snapshot isolation guaranteed during visitor operations?", "snapshot isolation guaranteed during visitor operations?", "isolation guaranteed during visitor operations?", "guaranteed during visitor operations?", "during visitor operations?", "visitor operations?", "operations?"], "tokens": ["Is", "snapshot", "isolation", "guaranteed", "during", "visitor", "operations", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1025246737", "fields": {"term": "What is reconciliation in Vespa?", "namespace": "open-p", "hash": -1025246737, "terms": ["What is reconciliation in Vespa?", "is reconciliation in Vespa?", "reconciliation in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["What", "is", "reconciliation", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1143303623", "fields": {"term": "How does Vespa reconcile replicas?", "namespace": "open-p", "hash": 1143303623, "terms": ["How does Vespa reconcile replicas?", "does Vespa reconcile replicas?", "Vespa reconcile replicas?", "reconcile replicas?", "replicas?"], "tokens": ["How", "does", "Vespa", "reconcile", "replicas", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-835694976", "fields": {"term": "What happens during reconciliation?", "namespace": "open-p", "hash": -835694976, "terms": ["What happens during reconciliation?", "happens during reconciliation?", "during reconciliation?", "reconciliation?"], "tokens": ["What", "happens", "during", "reconciliation", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::797290293", "fields": {"term": "What happens to tombstones during reconciliation?", "namespace": "open-p", "hash": 797290293, "terms": ["What happens to tombstones during reconciliation?", "happens to tombstones during reconciliation?", "to tombstones during reconciliation?", "tombstones during reconciliation?", "during reconciliation?", "reconciliation?"], "tokens": ["What", "happens", "to", "tombstones", "during", "reconciliation", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1002191717", "fields": {"term": "How does Vespa handle tombstones?", "namespace": "open-p", "hash": -1002191717, "terms": ["How does Vespa handle tombstones?", "does Vespa handle tombstones?", "Vespa handle tombstones?", "handle tombstones?", "tombstones?"], "tokens": ["How", "does", "Vespa", "handle", "tombstones", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-434193332", "fields": {"term": "What are the risks of not removing indexes on partitioned nodes?", "namespace": "open-p", "hash": -434193332, "terms": ["What are the risks of not removing indexes on partitioned nodes?", "are the risks of not removing indexes on partitioned nodes?", "the risks of not removing indexes on partitioned nodes?", "risks of not removing indexes on partitioned nodes?", "of not removing indexes on partitioned nodes?", "not removing indexes on partitioned nodes?", "removing indexes on partitioned nodes?", "indexes on partitioned nodes?", "on partitioned nodes?", "partitioned nodes?", "nodes?"], "tokens": ["What", "are", "the", "risks", "of", "not", "removing", "indexes", "on", "partitioned", "nodes", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1482204429", "fields": {"term": "Where can I find more information on data retention vs. size?", "namespace": "open-p", "hash": -1482204429, "terms": ["Where can I find more information on data retention vs. size?", "can I find more information on data retention vs. size?", "I find more information on data retention vs. size?", "find more information on data retention vs. size?", "more information on data retention vs. size?", "information on data retention vs. size?", "on data retention vs. size?", "data retention vs. size?", "retention vs. size?", "vs. size?", "size?"], "tokens": ["Where", "can", "I", "find", "more", "information", "on", "data", "retention", "vs", "size", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::454179287", "fields": {"term": "What is a Container component?", "namespace": "open-p", "hash": 454179287, "terms": ["What is a Container component?", "is a Container component?", "a Container component?", "Container component?", "component?"], "tokens": ["What", "is", "a", "Container", "component", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-680949203", "fields": {"term": "What is the base class for all components?", "namespace": "open-p", "hash": -680949203, "terms": ["What is the base class for all components?", "is the base class for all components?", "the base class for all components?", "base class for all components?", "class for all components?", "for all components?", "all components?", "components?"], "tokens": ["What", "is", "the", "base", "class", "for", "all", "components", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1683274494", "fields": {"term": "What are the main component types in Vespa?", "namespace": "open-p", "hash": -1683274494, "terms": ["What are the main component types in Vespa?", "are the main component types in Vespa?", "the main component types in Vespa?", "main component types in Vespa?", "component types in Vespa?", "types in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["What", "are", "the", "main", "component", "types", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-17994534", "fields": {"term": "How are search container components deployed?", "namespace": "open-p", "hash": -17994534, "terms": ["How are search container components deployed?", "are search container components deployed?", "search container components deployed?", "container components deployed?", "components deployed?", "deployed?"], "tokens": ["How", "are", "search", "container", "components", "deployed", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1155899511", "fields": {"term": "Where can I find a multi-bundle example?", "namespace": "open-p", "hash": -1155899511, "terms": ["Where can I find a multi-bundle example?", "can I find a multi-bundle example?", "I find a multi-bundle example?", "find a multi-bundle example?", "a multi-bundle example?", "multi-bundle example?", "example?"], "tokens": ["Where", "can", "I", "find", "a", "multi", "bundle", "example", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1748541805", "fields": {"term": "What is required to develop all types of Container components?", "namespace": "open-p", "hash": -1748541805, "terms": ["What is required to develop all types of Container components?", "is required to develop all types of Container components?", "required to develop all types of Container components?", "to develop all types of Container components?", "develop all types of Container components?", "all types of Container components?", "types of Container components?", "of Container components?", "Container components?", "components?"], "tokens": ["What", "is", "required", "to", "develop", "all", "types", "of", "Container", "components", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1068615708", "fields": {"term": "What is concurrency?", "namespace": "open-p", "hash": -1068615708, "terms": ["What is concurrency?", "is concurrency?", "concurrency?"], "tokens": ["What", "is", "concurrency", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-212850590", "fields": {"term": "What is deconstruct method?", "namespace": "open-p", "hash": -212850590, "terms": ["What is deconstruct method?", "is deconstruct method?", "deconstruct method?", "method?"], "tokens": ["What", "is", "deconstruct", "method", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::933530173", "fields": {"term": "How to properly release resources?", "namespace": "open-p", "hash": 933530173, "terms": ["How to properly release resources?", "to properly release resources?", "properly release resources?", "release resources?", "resources?"], "tokens": ["How", "to", "properly", "release", "resources", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1500386082", "fields": {"term": "What is SharedResource.java?", "namespace": "open-p", "hash": -1500386082, "terms": ["What is SharedResource.java?", "is SharedResource.java?", "SharedResource.java?"], "tokens": ["What", "is", "SharedResource", "java", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2088513826", "fields": {"term": "How to debug component lifetime?", "namespace": "open-p", "hash": -2088513826, "terms": ["How to debug component lifetime?", "to debug component lifetime?", "debug component lifetime?", "component lifetime?", "lifetime?"], "tokens": ["How", "to", "debug", "component", "lifetime", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::736877322", "fields": {"term": "What is executor in the example?", "namespace": "open-p", "hash": 736877322, "terms": ["What is executor in the example?", "is executor in the example?", "executor in the example?", "in the example?", "the example?", "example?"], "tokens": ["What", "is", "executor", "in", "the", "example", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1053166853", "fields": {"term": "What happens if deconstruct is not overridden?", "namespace": "open-p", "hash": -1053166853, "terms": ["What happens if deconstruct is not overridden?", "happens if deconstruct is not overridden?", "if deconstruct is not overridden?", "deconstruct is not overridden?", "is not overridden?", "not overridden?", "overridden?"], "tokens": ["What", "happens", "if", "deconstruct", "is", "not", "overridden", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-993001306", "fields": {"term": "What are the debug options for tools?", "namespace": "open-p", "hash": -993001306, "terms": ["What are the debug options for tools?", "are the debug options for tools?", "the debug options for tools?", "debug options for tools?", "options for tools?", "for tools?", "tools?"], "tokens": ["What", "are", "the", "debug", "options", "for", "tools", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::971185572", "fields": {"term": "What is container profiling?", "namespace": "open-p", "hash": 971185572, "terms": ["What is container profiling?", "is container profiling?", "container profiling?", "profiling?"], "tokens": ["What", "is", "container", "profiling", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-789353652", "fields": {"term": "What are constructor dependencies?", "namespace": "open-p", "hash": -789353652, "terms": ["What are constructor dependencies?", "are constructor dependencies?", "constructor dependencies?", "dependencies?"], "tokens": ["What", "are", "constructor", "dependencies", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1641349168", "fields": {"term": "What objects can be injected?", "namespace": "open-p", "hash": 1641349168, "terms": ["What objects can be injected?", "objects can be injected?", "can be injected?", "be injected?", "injected?"], "tokens": ["What", "objects", "can", "be", "injected", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::276643384", "fields": {"term": "How to annotate the constructor?", "namespace": "open-p", "hash": 276643384, "terms": ["How to annotate the constructor?", "to annotate the constructor?", "annotate the constructor?", "the constructor?", "constructor?"], "tokens": ["How", "to", "annotate", "the", "constructor", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1755984888", "fields": {"term": "What does the environment component provide?", "namespace": "open-p", "hash": 1755984888, "terms": ["What does the environment component provide?", "does the environment component provide?", "the environment component provide?", "environment component provide?", "component provide?", "provide?"], "tokens": ["What", "does", "the", "environment", "component", "provide", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1494548630", "fields": {"term": "What information does the component include?", "namespace": "open-p", "hash": -1494548630, "terms": ["What information does the component include?", "information does the component include?", "does the component include?", "the component include?", "component include?", "include?"], "tokens": ["What", "information", "does", "the", "component", "include", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-285796709", "fields": {"term": "When is a component created?", "namespace": "open-p", "hash": -285796709, "terms": ["When is a component created?", "is a component created?", "a component created?", "component created?", "created?"], "tokens": ["When", "is", "a", "component", "created", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::585846362", "fields": {"term": "Where can I find the Container Javadoc?", "namespace": "open-p", "hash": 585846362, "terms": ["Where can I find the Container Javadoc?", "can I find the Container Javadoc?", "I find the Container Javadoc?", "find the Container Javadoc?", "the Container Javadoc?", "Container Javadoc?", "Javadoc?"], "tokens": ["Where", "can", "I", "find", "the", "Container", "Javadoc", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-923751503", "fields": {"term": "What are the steps to deploy a component?", "namespace": "open-p", "hash": -923751503, "terms": ["What are the steps to deploy a component?", "are the steps to deploy a component?", "the steps to deploy a component?", "steps to deploy a component?", "to deploy a component?", "deploy a component?", "a component?", "component?"], "tokens": ["What", "are", "the", "steps", "to", "deploy", "a", "component", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::496460351", "fields": {"term": "How to build plugin jar?", "namespace": "open-p", "hash": 496460351, "terms": ["How to build plugin jar?", "to build plugin jar?", "build plugin jar?", "plugin jar?", "jar?"], "tokens": ["How", "to", "build", "plugin", "jar", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1678572317", "fields": {"term": "What command builds plugin jar?", "namespace": "open-p", "hash": -1678572317, "terms": ["What command builds plugin jar?", "command builds plugin jar?", "builds plugin jar?", "plugin jar?", "jar?"], "tokens": ["What", "command", "builds", "plugin", "jar", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1261993413", "fields": {"term": "Where can the plugin be found?", "namespace": "open-p", "hash": 1261993413, "terms": ["Where can the plugin be found?", "can the plugin be found?", "the plugin be found?", "plugin be found?", "be found?", "found?"], "tokens": ["Where", "can", "the", "plugin", "be", "found", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-20269011", "fields": {"term": "What is a plugin jar file?", "namespace": "open-p", "hash": -20269011, "terms": ["What is a plugin jar file?", "is a plugin jar file?", "a plugin jar file?", "plugin jar file?", "jar file?", "file?"], "tokens": ["What", "is", "a", "plugin", "jar", "file", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-792048191", "fields": {"term": "What is the manifest file?", "namespace": "open-p", "hash": -792048191, "terms": ["What is the manifest file?", "is the manifest file?", "the manifest file?", "manifest file?", "file?"], "tokens": ["What", "is", "the", "manifest", "file", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-104111168", "fields": {"term": "How is switching done?", "namespace": "open-p", "hash": -104111168, "terms": ["How is switching done?", "is switching done?", "switching done?", "done?"], "tokens": ["How", "is", "switching", "done", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::29166957", "fields": {"term": "Can interdependent searcher changes be deployed?", "namespace": "open-p", "hash": 29166957, "terms": ["Can interdependent searcher changes be deployed?", "interdependent searcher changes be deployed?", "searcher changes be deployed?", "changes be deployed?", "be deployed?", "deployed?"], "tokens": ["Can", "interdependent", "searcher", "changes", "be", "deployed", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2031077655", "fields": {"term": "What is the best practice for loading JNI library?", "namespace": "open-p", "hash": 2031077655, "terms": ["What is the best practice for loading JNI library?", "is the best practice for loading JNI library?", "the best practice for loading JNI library?", "best practice for loading JNI library?", "practice for loading JNI library?", "for loading JNI library?", "loading JNI library?", "JNI library?", "library?"], "tokens": ["What", "is", "the", "best", "practice", "for", "loading", "JNI", "library", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2113623331", "fields": {"term": "What happens if JNI library is initialized lazily?", "namespace": "open-p", "hash": 2113623331, "terms": ["What happens if JNI library is initialized lazily?", "happens if JNI library is initialized lazily?", "if JNI library is initialized lazily?", "JNI library is initialized lazily?", "library is initialized lazily?", "is initialized lazily?", "initialized lazily?", "lazily?"], "tokens": ["What", "happens", "if", "JNI", "library", "is", "initialized", "lazily", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1297714794", "fields": {"term": "What is the warning when deploying JNI packages?", "namespace": "open-p", "hash": -1297714794, "terms": ["What is the warning when deploying JNI packages?", "is the warning when deploying JNI packages?", "the warning when deploying JNI packages?", "warning when deploying JNI packages?", "when deploying JNI packages?", "deploying JNI packages?", "JNI packages?", "packages?"], "tokens": ["What", "is", "the", "warning", "when", "deploying", "JNI", "packages", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1246126359", "fields": {"term": "What causes failures when using JNI library?", "namespace": "open-p", "hash": -1246126359, "terms": ["What causes failures when using JNI library?", "causes failures when using JNI library?", "failures when using JNI library?", "when using JNI library?", "using JNI library?", "JNI library?", "library?"], "tokens": ["What", "causes", "failures", "when", "using", "JNI", "library", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1846870070", "fields": {"term": "Where should JNI components be put?", "namespace": "open-p", "hash": 1846870070, "terms": ["Where should JNI components be put?", "should JNI components be put?", "JNI components be put?", "components be put?", "be put?", "put?"], "tokens": ["Where", "should", "JNI", "components", "be", "put", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::901692437", "fields": {"term": "How can bundle reloads be prevented?", "namespace": "open-p", "hash": 901692437, "terms": ["How can bundle reloads be prevented?", "can bundle reloads be prevented?", "bundle reloads be prevented?", "reloads be prevented?", "be prevented?", "prevented?"], "tokens": ["How", "can", "bundle", "reloads", "be", "prevented", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::558619381", "fields": {"term": "What is the issue with reloading bundles containing JNI packages?", "namespace": "open-p", "hash": 558619381, "terms": ["What is the issue with reloading bundles containing JNI packages?", "is the issue with reloading bundles containing JNI packages?", "the issue with reloading bundles containing JNI packages?", "issue with reloading bundles containing JNI packages?", "with reloading bundles containing JNI packages?", "reloading bundles containing JNI packages?", "bundles containing JNI packages?", "containing JNI packages?", "JNI packages?", "packages?"], "tokens": ["What", "is", "the", "issue", "with", "reloading", "bundles", "containing", "JNI", "packages", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-289368720", "fields": {"term": "What is the solution to prevent failures when using the new bundle version with JNI?", "namespace": "open-p", "hash": -289368720, "terms": ["What is the solution to prevent failures when using the new bundle version with JNI?", "is the solution to prevent failures when using the new bundle version with JNI?", "the solution to prevent failures when using the new bundle version with JNI?", "solution to prevent failures when using the new bundle version with JNI?", "to prevent failures when using the new bundle version with JNI?", "prevent failures when using the new bundle version with JNI?", "failures when using the new bundle version with JNI?", "when using the new bundle version with JNI?", "using the new bundle version with JNI?", "the new bundle version with JNI?", "new bundle version with JNI?", "bundle version with JNI?", "version with JNI?", "with JNI?", "JNI?"], "tokens": ["What", "is", "the", "solution", "to", "prevent", "failures", "when", "using", "the", "new", "bundle", "version", "with", "JNI", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2127727334", "fields": {"term": "What information does the built-in handler output?", "namespace": "open-p", "hash": 2127727334, "terms": ["What information does the built-in handler output?", "information does the built-in handler output?", "does the built-in handler output?", "the built-in handler output?", "built-in handler output?", "handler output?", "output?"], "tokens": ["What", "information", "does", "the", "built", "in", "handler", "output", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-839978360", "fields": {"term": "What path does the handler answer to?", "namespace": "open-p", "hash": -839978360, "terms": ["What path does the handler answer to?", "path does the handler answer to?", "does the handler answer to?", "the handler answer to?", "handler answer to?", "answer to?", "to?"], "tokens": ["What", "path", "does", "the", "handler", "answer", "to", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-820237700", "fields": {"term": "What is JSONView?", "namespace": "open-p", "hash": -820237700, "terms": ["What is JSONView?", "is JSONView?", "JSONView?"], "tokens": ["What", "is", "JSONView", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::178892561", "fields": {"term": "How do I view nice formatted output in a browser?", "namespace": "open-p", "hash": 178892561, "terms": ["How do I view nice formatted output in a browser?", "do I view nice formatted output in a browser?", "I view nice formatted output in a browser?", "view nice formatted output in a browser?", "nice formatted output in a browser?", "formatted output in a browser?", "output in a browser?", "in a browser?", "a browser?", "browser?"], "tokens": ["How", "do", "I", "view", "nice", "formatted", "output", "in", "a", "browser", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::145840342", "fields": {"term": "What are external dependencies?", "namespace": "open-p", "hash": 145840342, "terms": ["What are external dependencies?", "are external dependencies?", "external dependencies?", "dependencies?"], "tokens": ["What", "are", "external", "dependencies", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1025429132", "fields": {"term": "How can they be included?", "namespace": "open-p", "hash": -1025429132, "terms": ["How can they be included?", "can they be included?", "they be included?", "be included?", "included?"], "tokens": ["How", "can", "they", "be", "included", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-180520652", "fields": {"term": "What is information hiding?", "namespace": "open-p", "hash": -180520652, "terms": ["What is information hiding?", "is information hiding?", "information hiding?", "hiding?"], "tokens": ["What", "is", "information", "hiding", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1043366417", "fields": {"term": "What are global packages?", "namespace": "open-p", "hash": 1043366417, "terms": ["What are global packages?", "are global packages?", "global packages?", "packages?"], "tokens": ["What", "are", "global", "packages", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1802615739", "fields": {"term": "What are exported packages?", "namespace": "open-p", "hash": -1802615739, "terms": ["What are exported packages?", "are exported packages?", "exported packages?", "packages?"], "tokens": ["What", "are", "exported", "packages", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1402559473", "fields": {"term": "Are backwards incompatible changes allowed on exported packages?", "namespace": "open-p", "hash": -1402559473, "terms": ["Are backwards incompatible changes allowed on exported packages?", "backwards incompatible changes allowed on exported packages?", "incompatible changes allowed on exported packages?", "changes allowed on exported packages?", "allowed on exported packages?", "on exported packages?", "exported packages?", "packages?"], "tokens": ["Are", "backwards", "incompatible", "changes", "allowed", "on", "exported", "packages", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::374646941", "fields": {"term": "Where can I find the list of global and exported packages?", "namespace": "open-p", "hash": 374646941, "terms": ["Where can I find the list of global and exported packages?", "can I find the list of global and exported packages?", "I find the list of global and exported packages?", "find the list of global and exported packages?", "the list of global and exported packages?", "list of global and exported packages?", "of global and exported packages?", "global and exported packages?", "and exported packages?", "exported packages?", "packages?"], "tokens": ["Where", "can", "I", "find", "the", "list", "of", "global", "and", "exported", "packages", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-179542675", "fields": {"term": "What is the supported API of the JDisc Container?", "namespace": "open-p", "hash": -179542675, "terms": ["What is the supported API of the JDisc Container?", "is the supported API of the JDisc Container?", "the supported API of the JDisc Container?", "supported API of the JDisc Container?", "API of the JDisc Container?", "of the JDisc Container?", "the JDisc Container?", "JDisc Container?", "Container?"], "tokens": ["What", "is", "the", "supported", "API", "of", "the", "JDisc", "Container", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1051077204", "fields": {"term": "What constitutes the exported packages?", "namespace": "open-p", "hash": -1051077204, "terms": ["What constitutes the exported packages?", "constitutes the exported packages?", "the exported packages?", "exported packages?", "packages?"], "tokens": ["What", "constitutes", "the", "exported", "packages", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1767374622", "fields": {"term": "Are exported packages considered public APIs?", "namespace": "open-p", "hash": 1767374622, "terms": ["Are exported packages considered public APIs?", "exported packages considered public APIs?", "packages considered public APIs?", "considered public APIs?", "public APIs?", "APIs?"], "tokens": ["Are", "exported", "packages", "considered", "public", "APIs", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1656557018", "fields": {"term": "What is included in the exported packages?", "namespace": "open-p", "hash": 1656557018, "terms": ["What is included in the exported packages?", "is included in the exported packages?", "included in the exported packages?", "in the exported packages?", "the exported packages?", "exported packages?", "packages?"], "tokens": ["What", "is", "included", "in", "the", "exported", "packages", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::549414487", "fields": {"term": "What can be versioned?", "namespace": "open-p", "hash": 549414487, "terms": ["What can be versioned?", "can be versioned?", "be versioned?", "versioned?"], "tokens": ["What", "can", "be", "versioned", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1058920690", "fields": {"term": "What is the syntax for versioning?", "namespace": "open-p", "hash": -1058920690, "terms": ["What is the syntax for versioning?", "is the syntax for versioning?", "the syntax for versioning?", "syntax for versioning?", "for versioning?", "versioning?"], "tokens": ["What", "is", "the", "syntax", "for", "versioning", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::388416301", "fields": {"term": "How are versions matched?", "namespace": "open-p", "hash": 388416301, "terms": ["How are versions matched?", "are versions matched?", "versions matched?", "matched?"], "tokens": ["How", "are", "versions", "matched", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2007999588", "fields": {"term": "Where should version be specified?", "namespace": "open-p", "hash": 2007999588, "terms": ["Where should version be specified?", "should version be specified?", "version be specified?", "be specified?", "specified?"], "tokens": ["Where", "should", "version", "be", "specified", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1952154859", "fields": {"term": "What is `vespa-logfmt`?", "namespace": "open-p", "hash": -1952154859, "terms": ["What is `vespa-logfmt`?", "is `vespa-logfmt`?", "`vespa-logfmt`?"], "tokens": ["What", "is", "`", "vespa", "logfmt", "`?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2089740837", "fields": {"term": "How do you know when the new application is active?", "namespace": "open-p", "hash": 2089740837, "terms": ["How do you know when the new application is active?", "do you know when the new application is active?", "you know when the new application is active?", "know when the new application is active?", "when the new application is active?", "the new application is active?", "new application is active?", "application is active?", "is active?", "active?"], "tokens": ["How", "do", "you", "know", "when", "the", "new", "application", "is", "active", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::785531578", "fields": {"term": "What are constructed components?", "namespace": "open-p", "hash": 785531578, "terms": ["What are constructed components?", "are constructed components?", "constructed components?", "components?"], "tokens": ["What", "are", "constructed", "components", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1140780831", "fields": {"term": "How to debug construction failures?", "namespace": "open-p", "hash": -1140780831, "terms": ["How to debug construction failures?", "to debug construction failures?", "debug construction failures?", "construction failures?", "failures?"], "tokens": ["How", "to", "debug", "construction", "failures", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2115571436", "fields": {"term": "What is vespa-logctl?", "namespace": "open-p", "hash": 2115571436, "terms": ["What is vespa-logctl?", "is vespa-logctl?", "vespa-logctl?"], "tokens": ["What", "is", "vespa", "logctl", "?"], "corpus_count": 3, "document_count": 3}}, -{"put": "id:term:term::1238663708", "fields": {"term": "What is container id?", "namespace": "open-p", "hash": 1238663708, "terms": ["What is container id?", "is container id?", "container id?", "id?"], "tokens": ["What", "is", "container", "id", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::307715937", "fields": {"term": "How to find failed components?", "namespace": "open-p", "hash": 307715937, "terms": ["How to find failed components?", "to find failed components?", "find failed components?", "failed components?", "components?"], "tokens": ["How", "to", "find", "failed", "components", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1910373780", "fields": {"term": "What is Gatling?", "namespace": "open-p", "hash": 1910373780, "terms": ["What is Gatling?", "is Gatling?", "Gatling?"], "tokens": ["What", "is", "Gatling", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::-679738082", "fields": {"term": "How is Gatling used?", "namespace": "open-p", "hash": -679738082, "terms": ["How is Gatling used?", "is Gatling used?", "Gatling used?", "used?"], "tokens": ["How", "is", "Gatling", "used", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1679029114", "fields": {"term": "What does Gatling provide?", "namespace": "open-p", "hash": -1679029114, "terms": ["What does Gatling provide?", "does Gatling provide?", "Gatling provide?", "provide?"], "tokens": ["What", "does", "Gatling", "provide", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1645124295", "fields": {"term": "How do you get started with Gatling in Vespa?", "namespace": "open-p", "hash": 1645124295, "terms": ["How do you get started with Gatling in Vespa?", "do you get started with Gatling in Vespa?", "you get started with Gatling in Vespa?", "get started with Gatling in Vespa?", "started with Gatling in Vespa?", "with Gatling in Vespa?", "Gatling in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["How", "do", "you", "get", "started", "with", "Gatling", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1703153042", "fields": {"term": "Is Vespa different from other HTTP services when using Gatling?", "namespace": "open-p", "hash": 1703153042, "terms": ["Is Vespa different from other HTTP services when using Gatling?", "Vespa different from other HTTP services when using Gatling?", "different from other HTTP services when using Gatling?", "from other HTTP services when using Gatling?", "other HTTP services when using Gatling?", "HTTP services when using Gatling?", "services when using Gatling?", "when using Gatling?", "using Gatling?", "Gatling?"], "tokens": ["Is", "Vespa", "different", "from", "other", "HTTP", "services", "when", "using", "Gatling", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1532087114", "fields": {"term": "What kind of flexibility and checking is required for container testing?", "namespace": "open-p", "hash": -1532087114, "terms": ["What kind of flexibility and checking is required for container testing?", "kind of flexibility and checking is required for container testing?", "of flexibility and checking is required for container testing?", "flexibility and checking is required for container testing?", "and checking is required for container testing?", "checking is required for container testing?", "is required for container testing?", "required for container testing?", "for container testing?", "container testing?", "testing?"], "tokens": ["What", "kind", "of", "flexibility", "and", "checking", "is", "required", "for", "container", "testing", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1583010733", "fields": {"term": "Can Gatling be customized with plug-ins?", "namespace": "open-p", "hash": -1583010733, "terms": ["Can Gatling be customized with plug-ins?", "Gatling be customized with plug-ins?", "be customized with plug-ins?", "customized with plug-ins?", "with plug-ins?", "plug-ins?"], "tokens": ["Can", "Gatling", "be", "customized", "with", "plug", "ins", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1629643167", "fields": {"term": "What is the DSL in Gatling?", "namespace": "open-p", "hash": 1629643167, "terms": ["What is the DSL in Gatling?", "is the DSL in Gatling?", "the DSL in Gatling?", "DSL in Gatling?", "in Gatling?", "Gatling?"], "tokens": ["What", "is", "the", "DSL", "in", "Gatling", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1965303646", "fields": {"term": "Where to find Gatling's documentation?", "namespace": "open-p", "hash": -1965303646, "terms": ["Where to find Gatling's documentation?", "to find Gatling's documentation?", "find Gatling's documentation?", "Gatling's documentation?", "documentation?"], "tokens": ["Where", "to", "find", "Gatling", "'", "s", "documentation", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1950142975", "fields": {"term": "How to get started with Gatling?", "namespace": "open-p", "hash": 1950142975, "terms": ["How to get started with Gatling?", "to get started with Gatling?", "get started with Gatling?", "started with Gatling?", "with Gatling?", "Gatling?"], "tokens": ["How", "to", "get", "started", "with", "Gatling", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1581583447", "fields": {"term": "What version of Gatling is this tutorial written for?", "namespace": "open-p", "hash": -1581583447, "terms": ["What version of Gatling is this tutorial written for?", "version of Gatling is this tutorial written for?", "of Gatling is this tutorial written for?", "Gatling is this tutorial written for?", "is this tutorial written for?", "this tutorial written for?", "tutorial written for?", "written for?", "for?"], "tokens": ["What", "version", "of", "Gatling", "is", "this", "tutorial", "written", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::963990532", "fields": {"term": "Where can I download Gatling?", "namespace": "open-p", "hash": 963990532, "terms": ["Where can I download Gatling?", "can I download Gatling?", "I download Gatling?", "download Gatling?", "Gatling?"], "tokens": ["Where", "can", "I", "download", "Gatling", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::125088563", "fields": {"term": "What is a feeder?", "namespace": "open-p", "hash": 125088563, "terms": ["What is a feeder?", "is a feeder?", "a feeder?", "feeder?"], "tokens": ["What", "is", "a", "feeder", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1964766301", "fields": {"term": "What is httpProtocol?", "namespace": "open-p", "hash": 1964766301, "terms": ["What is httpProtocol?", "is httpProtocol?", "httpProtocol?"], "tokens": ["What", "is", "httpProtocol", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-876546006", "fields": {"term": "What does the scenario execute?", "namespace": "open-p", "hash": -876546006, "terms": ["What does the scenario execute?", "does the scenario execute?", "the scenario execute?", "scenario execute?", "execute?"], "tokens": ["What", "does", "the", "scenario", "execute", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-391892865", "fields": {"term": "What does the constantRate do?", "namespace": "open-p", "hash": -391892865, "terms": ["What does the constantRate do?", "does the constantRate do?", "the constantRate do?", "constantRate do?", "do?"], "tokens": ["What", "does", "the", "constantRate", "do", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1708801371", "fields": {"term": "What is the purpose of the VespaTutorial?", "namespace": "open-p", "hash": -1708801371, "terms": ["What is the purpose of the VespaTutorial?", "is the purpose of the VespaTutorial?", "the purpose of the VespaTutorial?", "purpose of the VespaTutorial?", "of the VespaTutorial?", "the VespaTutorial?", "VespaTutorial?"], "tokens": ["What", "is", "the", "purpose", "of", "the", "VespaTutorial", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::793064382", "fields": {"term": "What is Gatling used for?", "namespace": "open-p", "hash": 793064382, "terms": ["What is Gatling used for?", "is Gatling used for?", "Gatling used for?", "used for?", "for?"], "tokens": ["What", "is", "Gatling", "used", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2100595788", "fields": {"term": "How do you start a Gatling simulation?", "namespace": "open-p", "hash": 2100595788, "terms": ["How do you start a Gatling simulation?", "do you start a Gatling simulation?", "you start a Gatling simulation?", "start a Gatling simulation?", "a Gatling simulation?", "Gatling simulation?", "simulation?"], "tokens": ["How", "do", "you", "start", "a", "Gatling", "simulation", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1436968264", "fields": {"term": "What is the output of a Gatling simulation?", "namespace": "open-p", "hash": 1436968264, "terms": ["What is the output of a Gatling simulation?", "is the output of a Gatling simulation?", "the output of a Gatling simulation?", "output of a Gatling simulation?", "of a Gatling simulation?", "a Gatling simulation?", "Gatling simulation?", "simulation?"], "tokens": ["What", "is", "the", "output", "of", "a", "Gatling", "simulation", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1308088387", "fields": {"term": "How can you run Gatling on a personal laptop?", "namespace": "open-p", "hash": 1308088387, "terms": ["How can you run Gatling on a personal laptop?", "can you run Gatling on a personal laptop?", "you run Gatling on a personal laptop?", "run Gatling on a personal laptop?", "Gatling on a personal laptop?", "on a personal laptop?", "a personal laptop?", "personal laptop?", "laptop?"], "tokens": ["How", "can", "you", "run", "Gatling", "on", "a", "personal", "laptop", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::539352700", "fields": {"term": "What are the accepted characters for simulation id?", "namespace": "open-p", "hash": 539352700, "terms": ["What are the accepted characters for simulation id?", "are the accepted characters for simulation id?", "the accepted characters for simulation id?", "accepted characters for simulation id?", "characters for simulation id?", "for simulation id?", "simulation id?", "id?"], "tokens": ["What", "are", "the", "accepted", "characters", "for", "simulation", "id", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1758615169", "fields": {"term": "What does the Gatling simulation report include?", "namespace": "open-p", "hash": -1758615169, "terms": ["What does the Gatling simulation report include?", "does the Gatling simulation report include?", "the Gatling simulation report include?", "Gatling simulation report include?", "simulation report include?", "report include?", "include?"], "tokens": ["What", "does", "the", "Gatling", "simulation", "report", "include", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1802098211", "fields": {"term": "What are generic config overrides?", "namespace": "open-p", "hash": 1802098211, "terms": ["What are generic config overrides?", "are generic config overrides?", "generic config overrides?", "config overrides?", "overrides?"], "tokens": ["What", "are", "generic", "config", "overrides", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-992327129", "fields": {"term": "What are thread pools?", "namespace": "open-p", "hash": -992327129, "terms": ["What are thread pools?", "are thread pools?", "thread pools?", "pools?"], "tokens": ["What", "are", "thread", "pools", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::940122916", "fields": {"term": "How are thread pools tuned?", "namespace": "open-p", "hash": 940122916, "terms": ["How are thread pools tuned?", "are thread pools tuned?", "thread pools tuned?", "pools tuned?", "tuned?"], "tokens": ["How", "are", "thread", "pools", "tuned", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2008645540", "fields": {"term": "What is the default thread pool?", "namespace": "open-p", "hash": -2008645540, "terms": ["What is the default thread pool?", "is the default thread pool?", "the default thread pool?", "default thread pool?", "thread pool?", "pool?"], "tokens": ["What", "is", "the", "default", "thread", "pool", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-514276501", "fields": {"term": "Which components have dedicated thread pools?", "namespace": "open-p", "hash": -514276501, "terms": ["Which components have dedicated thread pools?", "components have dedicated thread pools?", "have dedicated thread pools?", "dedicated thread pools?", "thread pools?", "pools?"], "tokens": ["Which", "components", "have", "dedicated", "thread", "pools", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2106748192", "fields": {"term": "How does the container scale thread pools?", "namespace": "open-p", "hash": -2106748192, "terms": ["How does the container scale thread pools?", "does the container scale thread pools?", "the container scale thread pools?", "container scale thread pools?", "scale thread pools?", "thread pools?", "pools?"], "tokens": ["How", "does", "the", "container", "scale", "thread", "pools", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-92372590", "fields": {"term": "What is the risk of tuning capacity upwards?", "namespace": "open-p", "hash": -92372590, "terms": ["What is the risk of tuning capacity upwards?", "is the risk of tuning capacity upwards?", "the risk of tuning capacity upwards?", "risk of tuning capacity upwards?", "of tuning capacity upwards?", "tuning capacity upwards?", "capacity upwards?", "upwards?"], "tokens": ["What", "is", "the", "risk", "of", "tuning", "capacity", "upwards", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::622939571", "fields": {"term": "How are extra threads created?", "namespace": "open-p", "hash": 622939571, "terms": ["How are extra threads created?", "are extra threads created?", "extra threads created?", "threads created?", "created?"], "tokens": ["How", "are", "extra", "threads", "created", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1313617740", "fields": {"term": "Where can the effective thread pool configuration be observed?", "namespace": "open-p", "hash": 1313617740, "terms": ["Where can the effective thread pool configuration be observed?", "can the effective thread pool configuration be observed?", "the effective thread pool configuration be observed?", "effective thread pool configuration be observed?", "thread pool configuration be observed?", "pool configuration be observed?", "configuration be observed?", "be observed?", "observed?"], "tokens": ["Where", "can", "the", "effective", "thread", "pool", "configuration", "be", "observed", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-866203128", "fields": {"term": "How to change JVM heap size?", "namespace": "open-p", "hash": -866203128, "terms": ["How to change JVM heap size?", "to change JVM heap size?", "change JVM heap size?", "JVM heap size?", "heap size?", "size?"], "tokens": ["How", "to", "change", "JVM", "heap", "size", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1363459483", "fields": {"term": "What is allocated-memory?", "namespace": "open-p", "hash": -1363459483, "terms": ["What is allocated-memory?", "is allocated-memory?", "allocated-memory?"], "tokens": ["What", "is", "allocated", "memory", "?"], "corpus_count": 3, "document_count": 3}}, -{"put": "id:term:term::-1498669680", "fields": {"term": "What are gc-options?", "namespace": "open-p", "hash": -1498669680, "terms": ["What are gc-options?", "are gc-options?", "gc-options?"], "tokens": ["What", "are", "gc", "options", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::1090345986", "fields": {"term": "What are options used for?", "namespace": "open-p", "hash": 1090345986, "terms": ["What are options used for?", "are options used for?", "options used for?", "used for?", "for?"], "tokens": ["What", "are", "options", "used", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1071307475", "fields": {"term": "How does G1GC Work?", "namespace": "open-p", "hash": -1071307475, "terms": ["How does G1GC Work?", "does G1GC Work?", "G1GC Work?", "Work?"], "tokens": ["How", "does", "G1GC", "Work", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1630530677", "fields": {"term": "What is used to bootstrap services?", "namespace": "open-p", "hash": -1630530677, "terms": ["What is used to bootstrap services?", "is used to bootstrap services?", "used to bootstrap services?", "to bootstrap services?", "bootstrap services?", "services?"], "tokens": ["What", "is", "used", "to", "bootstrap", "services", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1993398889", "fields": {"term": "How do you set JVM parameters?", "namespace": "open-p", "hash": 1993398889, "terms": ["How do you set JVM parameters?", "do you set JVM parameters?", "you set JVM parameters?", "set JVM parameters?", "JVM parameters?", "parameters?"], "tokens": ["How", "do", "you", "set", "JVM", "parameters", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::626959337", "fields": {"term": "Do you need to vespa-deploy prepare?", "namespace": "open-p", "hash": 626959337, "terms": ["Do you need to vespa-deploy prepare?", "you need to vespa-deploy prepare?", "need to vespa-deploy prepare?", "to vespa-deploy prepare?", "vespa-deploy prepare?", "prepare?"], "tokens": ["Do", "you", "need", "to", "vespa", "deploy", "prepare", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-49188016", "fields": {"term": "What causes first queries to a freshly started container to take a long time?", "namespace": "open-p", "hash": -49188016, "terms": ["What causes first queries to a freshly started container to take a long time?", "causes first queries to a freshly started container to take a long time?", "first queries to a freshly started container to take a long time?", "queries to a freshly started container to take a long time?", "to a freshly started container to take a long time?", "a freshly started container to take a long time?", "freshly started container to take a long time?", "started container to take a long time?", "container to take a long time?", "to take a long time?", "take a long time?", "a long time?", "long time?", "time?"], "tokens": ["What", "causes", "first", "queries", "to", "a", "freshly", "started", "container", "to", "take", "a", "long", "time", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1693230957", "fields": {"term": "What is lazy initialization?", "namespace": "open-p", "hash": -1693230957, "terms": ["What is lazy initialization?", "is lazy initialization?", "lazy initialization?", "initialization?"], "tokens": ["What", "is", "lazy", "initialization", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1630294484", "fields": {"term": "What is eager initialization?", "namespace": "open-p", "hash": 1630294484, "terms": ["What is eager initialization?", "is eager initialization?", "eager initialization?", "initialization?"], "tokens": ["What", "is", "eager", "initialization", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1552444594", "fields": {"term": "What are warmup queries?", "namespace": "open-p", "hash": 1552444594, "terms": ["What are warmup queries?", "are warmup queries?", "warmup queries?", "queries?"], "tokens": ["What", "are", "warmup", "queries", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-513263268", "fields": {"term": "How to perform warmup queries at startup?", "namespace": "open-p", "hash": -513263268, "terms": ["How to perform warmup queries at startup?", "to perform warmup queries at startup?", "perform warmup queries at startup?", "warmup queries at startup?", "queries at startup?", "at startup?", "startup?"], "tokens": ["How", "to", "perform", "warmup", "queries", "at", "startup", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::815463303", "fields": {"term": "What is the warmupQueries() method?", "namespace": "open-p", "hash": 815463303, "terms": ["What is the warmupQueries() method?", "is the warmupQueries() method?", "the warmupQueries() method?", "warmupQueries() method?", "method?"], "tokens": ["What", "is", "the", "warmupQueries", "()", "method", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1066883183", "fields": {"term": "How to subclass default Vespa's SearchHandler?", "namespace": "open-p", "hash": -1066883183, "terms": ["How to subclass default Vespa's SearchHandler?", "to subclass default Vespa's SearchHandler?", "subclass default Vespa's SearchHandler?", "default Vespa's SearchHandler?", "Vespa's SearchHandler?", "SearchHandler?"], "tokens": ["How", "to", "subclass", "default", "Vespa", "'", "s", "SearchHandler", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2082442834", "fields": {"term": "What is metrics.ignore used for?", "namespace": "open-p", "hash": -2082442834, "terms": ["What is metrics.ignore used for?", "is metrics.ignore used for?", "metrics.ignore used for?", "used for?", "for?"], "tokens": ["What", "is", "metrics", "ignore", "used", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-882367939", "fields": {"term": "What is the Container service?", "namespace": "open-p", "hash": -882367939, "terms": ["What is the Container service?", "is the Container service?", "the Container service?", "Container service?", "service?"], "tokens": ["What", "is", "the", "Container", "service", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-330624941", "fields": {"term": "What is meant by 'container' in Vespa?", "namespace": "open-p", "hash": -330624941, "terms": ["What is meant by 'container' in Vespa?", "is meant by 'container' in Vespa?", "meant by 'container' in Vespa?", "by 'container' in Vespa?", "'container' in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["What", "is", "meant", "by", "'", "container", "'", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1392749221", "fields": {"term": "What is a container service?", "namespace": "open-p", "hash": 1392749221, "terms": ["What is a container service?", "is a container service?", "a container service?", "container service?", "service?"], "tokens": ["What", "is", "a", "container", "service", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1218657061", "fields": {"term": "What are the different container clusters?", "namespace": "open-p", "hash": 1218657061, "terms": ["What are the different container clusters?", "are the different container clusters?", "the different container clusters?", "different container clusters?", "container clusters?", "clusters?"], "tokens": ["What", "are", "the", "different", "container", "clusters", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-583329109", "fields": {"term": "What is the difference between container services in album-recommendation and multinode-HA examples?", "namespace": "open-p", "hash": -583329109, "terms": ["What is the difference between container services in album-recommendation and multinode-HA examples?", "is the difference between container services in album-recommendation and multinode-HA examples?", "the difference between container services in album-recommendation and multinode-HA examples?", "difference between container services in album-recommendation and multinode-HA examples?", "between container services in album-recommendation and multinode-HA examples?", "container services in album-recommendation and multinode-HA examples?", "services in album-recommendation and multinode-HA examples?", "in album-recommendation and multinode-HA examples?", "album-recommendation and multinode-HA examples?", "and multinode-HA examples?", "multinode-HA examples?", "examples?"], "tokens": ["What", "is", "the", "difference", "between", "container", "services", "in", "album", "recommendation", "and", "multinode", "HA", "examples", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-732990500", "fields": {"term": "What is Metrics in Vespa?", "namespace": "open-p", "hash": -732990500, "terms": ["What is Metrics in Vespa?", "is Metrics in Vespa?", "Metrics in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["What", "is", "Metrics", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1585736036", "fields": {"term": "Where can I find Metrics information?", "namespace": "open-p", "hash": 1585736036, "terms": ["Where can I find Metrics information?", "can I find Metrics information?", "I find Metrics information?", "find Metrics information?", "Metrics information?", "information?"], "tokens": ["Where", "can", "I", "find", "Metrics", "information", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-418514252", "fields": {"term": "What are Container Metrics in Vespa?", "namespace": "open-p", "hash": -418514252, "terms": ["What are Container Metrics in Vespa?", "are Container Metrics in Vespa?", "Container Metrics in Vespa?", "Metrics in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["What", "are", "Container", "Metrics", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-572096954", "fields": {"term": "What are server metrics?", "namespace": "open-p", "hash": -572096954, "terms": ["What are server metrics?", "are server metrics?", "server metrics?", "metrics?"], "tokens": ["What", "are", "server", "metrics", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1946826747", "fields": {"term": "What memory usage metrics exist?", "namespace": "open-p", "hash": -1946826747, "terms": ["What memory usage metrics exist?", "memory usage metrics exist?", "usage metrics exist?", "metrics exist?", "exist?"], "tokens": ["What", "memory", "usage", "metrics", "exist", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::37564283", "fields": {"term": "What does jdisc.gc count?", "namespace": "open-p", "hash": 37564283, "terms": ["What does jdisc.gc count?", "does jdisc.gc count?", "jdisc.gc count?", "count?"], "tokens": ["What", "does", "jdisc", "gc", "count", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::814634802", "fields": {"term": "What are `jdisc.thread_pool.*` metrics?", "namespace": "open-p", "hash": 814634802, "terms": ["What are `jdisc.thread_pool.*` metrics?", "are `jdisc.thread_pool.*` metrics?", "`jdisc.thread_pool.*` metrics?", "metrics?"], "tokens": ["What", "are", "`", "jdisc", "thread_pool", "*`", "metrics", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-367677243", "fields": {"term": "What is the dimension of `jdisc.thread_pool.*` metrics?", "namespace": "open-p", "hash": -367677243, "terms": ["What is the dimension of `jdisc.thread_pool.*` metrics?", "is the dimension of `jdisc.thread_pool.*` metrics?", "the dimension of `jdisc.thread_pool.*` metrics?", "dimension of `jdisc.thread_pool.*` metrics?", "of `jdisc.thread_pool.*` metrics?", "`jdisc.thread_pool.*` metrics?", "metrics?"], "tokens": ["What", "is", "the", "dimension", "of", "`", "jdisc", "thread_pool", "*`", "metrics", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-730722216", "fields": {"term": "What is `threadpool`?", "namespace": "open-p", "hash": -730722216, "terms": ["What is `threadpool`?", "is `threadpool`?", "`threadpool`?"], "tokens": ["What", "is", "`", "threadpool", "`?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1099769150", "fields": {"term": "What is the name of the default thread pool in Vespa container?", "namespace": "open-p", "hash": -1099769150, "terms": ["What is the name of the default thread pool in Vespa container?", "is the name of the default thread pool in Vespa container?", "the name of the default thread pool in Vespa container?", "name of the default thread pool in Vespa container?", "of the default thread pool in Vespa container?", "the default thread pool in Vespa container?", "default thread pool in Vespa container?", "thread pool in Vespa container?", "pool in Vespa container?", "in Vespa container?", "Vespa container?", "container?"], "tokens": ["What", "is", "the", "name", "of", "the", "default", "thread", "pool", "in", "Vespa", "container", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1932768339", "fields": {"term": "Where can I find details about tuning the container?", "namespace": "open-p", "hash": -1932768339, "terms": ["Where can I find details about tuning the container?", "can I find details about tuning the container?", "I find details about tuning the container?", "find details about tuning the container?", "details about tuning the container?", "about tuning the container?", "tuning the container?", "the container?", "container?"], "tokens": ["Where", "can", "I", "find", "details", "about", "tuning", "the", "container", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1190448396", "fields": {"term": "What is the purpose of container thread pools?", "namespace": "open-p", "hash": -1190448396, "terms": ["What is the purpose of container thread pools?", "is the purpose of container thread pools?", "the purpose of container thread pools?", "purpose of container thread pools?", "of container thread pools?", "container thread pools?", "thread pools?", "pools?"], "tokens": ["What", "is", "the", "purpose", "of", "container", "thread", "pools", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-619802042", "fields": {"term": "What is the format of the `jdisc.thread_pool.*` metrics?", "namespace": "open-p", "hash": -619802042, "terms": ["What is the format of the `jdisc.thread_pool.*` metrics?", "is the format of the `jdisc.thread_pool.*` metrics?", "the format of the `jdisc.thread_pool.*` metrics?", "format of the `jdisc.thread_pool.*` metrics?", "of the `jdisc.thread_pool.*` metrics?", "the `jdisc.thread_pool.*` metrics?", "`jdisc.thread_pool.*` metrics?", "metrics?"], "tokens": ["What", "is", "the", "format", "of", "the", "`", "jdisc", "thread_pool", "*`", "metrics", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1566948042", "fields": {"term": "What are some examples of `jdisc.thread_pool.*` metrics?", "namespace": "open-p", "hash": -1566948042, "terms": ["What are some examples of `jdisc.thread_pool.*` metrics?", "are some examples of `jdisc.thread_pool.*` metrics?", "some examples of `jdisc.thread_pool.*` metrics?", "examples of `jdisc.thread_pool.*` metrics?", "of `jdisc.thread_pool.*` metrics?", "`jdisc.thread_pool.*` metrics?", "metrics?"], "tokens": ["What", "are", "some", "examples", "of", "`", "jdisc", "thread_pool", "*`", "metrics", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2027376871", "fields": {"term": "What are HTTP metrics?", "namespace": "open-p", "hash": 2027376871, "terms": ["What are HTTP metrics?", "are HTTP metrics?", "HTTP metrics?", "metrics?"], "tokens": ["What", "are", "HTTP", "metrics", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::817498347", "fields": {"term": "What is TCP listen port?", "namespace": "open-p", "hash": 817498347, "terms": ["What is TCP listen port?", "is TCP listen port?", "TCP listen port?", "listen port?", "port?"], "tokens": ["What", "is", "TCP", "listen", "port", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2000329911", "fields": {"term": "What are jdisc.http metrics?", "namespace": "open-p", "hash": 2000329911, "terms": ["What are jdisc.http metrics?", "are jdisc.http metrics?", "jdisc.http metrics?", "metrics?"], "tokens": ["What", "are", "jdisc", "http", "metrics", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-157185274", "fields": {"term": "What is serverNumOpenConnections?", "namespace": "open-p", "hash": -157185274, "terms": ["What is serverNumOpenConnections?", "is serverNumOpenConnections?", "serverNumOpenConnections?"], "tokens": ["What", "is", "serverNumOpenConnections", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::396090241", "fields": {"term": "What is serverBytesReceived?", "namespace": "open-p", "hash": 396090241, "terms": ["What is serverBytesReceived?", "is serverBytesReceived?", "serverBytesReceived?"], "tokens": ["What", "is", "serverBytesReceived", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-226736058", "fields": {"term": "What metrics should I use for query latency?", "namespace": "open-p", "hash": -226736058, "terms": ["What metrics should I use for query latency?", "metrics should I use for query latency?", "should I use for query latency?", "I use for query latency?", "use for query latency?", "for query latency?", "query latency?", "latency?"], "tokens": ["What", "metrics", "should", "I", "use", "for", "query", "latency", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1033392100", "fields": {"term": "What are the relevant metrics for query handling?", "namespace": "open-p", "hash": 1033392100, "terms": ["What are the relevant metrics for query handling?", "are the relevant metrics for query handling?", "the relevant metrics for query handling?", "relevant metrics for query handling?", "metrics for query handling?", "for query handling?", "query handling?", "handling?"], "tokens": ["What", "are", "the", "relevant", "metrics", "for", "query", "handling", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1427559190", "fields": {"term": "What are feed.operations used for?", "namespace": "open-p", "hash": -1427559190, "terms": ["What are feed.operations used for?", "are feed.operations used for?", "feed.operations used for?", "used for?", "for?"], "tokens": ["What", "are", "feed", "operations", "used", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1638020787", "fields": {"term": "What are feed.latency metrics?", "namespace": "open-p", "hash": -1638020787, "terms": ["What are feed.latency metrics?", "are feed.latency metrics?", "feed.latency metrics?", "metrics?"], "tokens": ["What", "are", "feed", "latency", "metrics", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::412673282", "fields": {"term": "What is JConsole?", "namespace": "open-p", "hash": 412673282, "terms": ["What is JConsole?", "is JConsole?", "JConsole?"], "tokens": ["What", "is", "JConsole", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::-1062762142", "fields": {"term": "How do I start JConsole?", "namespace": "open-p", "hash": -1062762142, "terms": ["How do I start JConsole?", "do I start JConsole?", "I start JConsole?", "start JConsole?", "JConsole?"], "tokens": ["How", "do", "I", "start", "JConsole", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::843150106", "fields": {"term": "Can JConsole attach to Vespa services?", "namespace": "open-p", "hash": 843150106, "terms": ["Can JConsole attach to Vespa services?", "JConsole attach to Vespa services?", "attach to Vespa services?", "to Vespa services?", "Vespa services?", "services?"], "tokens": ["Can", "JConsole", "attach", "to", "Vespa", "services", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1906966630", "fields": {"term": "What determines which service to attach to?", "namespace": "open-p", "hash": 1906966630, "terms": ["What determines which service to attach to?", "determines which service to attach to?", "which service to attach to?", "service to attach to?", "to attach to?", "attach to?", "to?"], "tokens": ["What", "determines", "which", "service", "to", "attach", "to", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1743353145", "fields": {"term": "How to attach JConsole?", "namespace": "open-p", "hash": -1743353145, "terms": ["How to attach JConsole?", "to attach JConsole?", "attach JConsole?", "JConsole?"], "tokens": ["How", "to", "attach", "JConsole", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1893500779", "fields": {"term": "What is an SSH tunnel?", "namespace": "open-p", "hash": -1893500779, "terms": ["What is an SSH tunnel?", "is an SSH tunnel?", "an SSH tunnel?", "SSH tunnel?", "tunnel?"], "tokens": ["What", "is", "an", "SSH", "tunnel", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1926219134", "fields": {"term": "Where can I find updated port information?", "namespace": "open-p", "hash": -1926219134, "terms": ["Where can I find updated port information?", "can I find updated port information?", "I find updated port information?", "find updated port information?", "updated port information?", "port information?", "information?"], "tokens": ["Where", "can", "I", "find", "updated", "port", "information", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1598187829", "fields": {"term": "What are the thread types?", "namespace": "open-p", "hash": -1598187829, "terms": ["What are the thread types?", "are the thread types?", "the thread types?", "thread types?", "types?"], "tokens": ["What", "are", "the", "thread", "types", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-611451311", "fields": {"term": "Which threads are used for backend communications?", "namespace": "open-p", "hash": -611451311, "terms": ["Which threads are used for backend communications?", "threads are used for backend communications?", "are used for backend communications?", "used for backend communications?", "for backend communications?", "backend communications?", "communications?"], "tokens": ["Which", "threads", "are", "used", "for", "backend", "communications", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1532442578", "fields": {"term": "What does the distributor do?", "namespace": "open-p", "hash": 1532442578, "terms": ["What does the distributor do?", "does the distributor do?", "the distributor do?", "distributor do?", "do?"], "tokens": ["What", "does", "the", "distributor", "do", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-332397120", "fields": {"term": "How are documents forwarded to content nodes?", "namespace": "open-p", "hash": -332397120, "terms": ["How are documents forwarded to content nodes?", "are documents forwarded to content nodes?", "documents forwarded to content nodes?", "forwarded to content nodes?", "to content nodes?", "content nodes?", "nodes?"], "tokens": ["How", "are", "documents", "forwarded", "to", "content", "nodes", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1936199105", "fields": {"term": "What are the three kinds of state?", "namespace": "open-p", "hash": 1936199105, "terms": ["What are the three kinds of state?", "are the three kinds of state?", "the three kinds of state?", "three kinds of state?", "kinds of state?", "of state?", "state?"], "tokens": ["What", "are", "the", "three", "kinds", "of", "state", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1958351669", "fields": {"term": "What is the cluster state version?", "namespace": "open-p", "hash": -1958351669, "terms": ["What is the cluster state version?", "is the cluster state version?", "the cluster state version?", "cluster state version?", "state version?", "version?"], "tokens": ["What", "is", "the", "cluster", "state", "version", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::36659790", "fields": {"term": "What is the minimum time between cluster state changes?", "namespace": "open-p", "hash": 36659790, "terms": ["What is the minimum time between cluster state changes?", "is the minimum time between cluster state changes?", "the minimum time between cluster state changes?", "minimum time between cluster state changes?", "time between cluster state changes?", "between cluster state changes?", "cluster state changes?", "state changes?", "changes?"], "tokens": ["What", "is", "the", "minimum", "time", "between", "cluster", "state", "changes", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::426876979", "fields": {"term": "What is the minimum capacity for cluster state to be up?", "namespace": "open-p", "hash": 426876979, "terms": ["What is the minimum capacity for cluster state to be up?", "is the minimum capacity for cluster state to be up?", "the minimum capacity for cluster state to be up?", "minimum capacity for cluster state to be up?", "capacity for cluster state to be up?", "for cluster state to be up?", "cluster state to be up?", "state to be up?", "to be up?", "be up?", "up?"], "tokens": ["What", "is", "the", "minimum", "capacity", "for", "cluster", "state", "to", "be", "up", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::236561137", "fields": {"term": "What happens if a cluster has many nodes down?", "namespace": "open-p", "hash": 236561137, "terms": ["What happens if a cluster has many nodes down?", "happens if a cluster has many nodes down?", "if a cluster has many nodes down?", "a cluster has many nodes down?", "cluster has many nodes down?", "has many nodes down?", "many nodes down?", "nodes down?", "down?"], "tokens": ["What", "happens", "if", "a", "cluster", "has", "many", "nodes", "down", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1232892070", "fields": {"term": "What is a cluster state indicating the entire cluster is down?", "namespace": "open-p", "hash": 1232892070, "terms": ["What is a cluster state indicating the entire cluster is down?", "is a cluster state indicating the entire cluster is down?", "a cluster state indicating the entire cluster is down?", "cluster state indicating the entire cluster is down?", "state indicating the entire cluster is down?", "indicating the entire cluster is down?", "the entire cluster is down?", "entire cluster is down?", "cluster is down?", "is down?", "down?"], "tokens": ["What", "is", "a", "cluster", "state", "indicating", "the", "entire", "cluster", "is", "down", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::702324223", "fields": {"term": "What is the cluster controller?", "namespace": "open-p", "hash": 702324223, "terms": ["What is the cluster controller?", "is the cluster controller?", "the cluster controller?", "cluster controller?", "controller?"], "tokens": ["What", "is", "the", "cluster", "controller", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1149877988", "fields": {"term": "What is the main task of the cluster controller?", "namespace": "open-p", "hash": -1149877988, "terms": ["What is the main task of the cluster controller?", "is the main task of the cluster controller?", "the main task of the cluster controller?", "main task of the cluster controller?", "task of the cluster controller?", "of the cluster controller?", "the cluster controller?", "cluster controller?", "controller?"], "tokens": ["What", "is", "the", "main", "task", "of", "the", "cluster", "controller", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::530790557", "fields": {"term": "What is the cluster state?", "namespace": "open-p", "hash": 530790557, "terms": ["What is the cluster state?", "is the cluster state?", "the cluster state?", "cluster state?", "state?"], "tokens": ["What", "is", "the", "cluster", "state", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-621509102", "fields": {"term": "How is the cluster state updated and distributed?", "namespace": "open-p", "hash": -621509102, "terms": ["How is the cluster state updated and distributed?", "is the cluster state updated and distributed?", "the cluster state updated and distributed?", "cluster state updated and distributed?", "state updated and distributed?", "updated and distributed?", "and distributed?", "distributed?"], "tokens": ["How", "is", "the", "cluster", "state", "updated", "and", "distributed", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2069267844", "fields": {"term": "What is master election?", "namespace": "open-p", "hash": -2069267844, "terms": ["What is master election?", "is master election?", "master election?", "election?"], "tokens": ["What", "is", "master", "election", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1098137034", "fields": {"term": "How do cluster controllers vote?", "namespace": "open-p", "hash": 1098137034, "terms": ["How do cluster controllers vote?", "do cluster controllers vote?", "cluster controllers vote?", "controllers vote?", "vote?"], "tokens": ["How", "do", "cluster", "controllers", "vote", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-992078868", "fields": {"term": "What happens if cluster controller goes down?", "namespace": "open-p", "hash": -992078868, "terms": ["What happens if cluster controller goes down?", "happens if cluster controller goes down?", "if cluster controller goes down?", "cluster controller goes down?", "controller goes down?", "goes down?", "down?"], "tokens": ["What", "happens", "if", "cluster", "controller", "goes", "down", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::955561490", "fields": {"term": "What is the recommended number of cluster controllers?", "namespace": "open-p", "hash": 955561490, "terms": ["What is the recommended number of cluster controllers?", "is the recommended number of cluster controllers?", "the recommended number of cluster controllers?", "recommended number of cluster controllers?", "number of cluster controllers?", "of cluster controllers?", "cluster controllers?", "controllers?"], "tokens": ["What", "is", "the", "recommended", "number", "of", "cluster", "controllers", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::494591185", "fields": {"term": "How are buckets mapped to distributors?", "namespace": "open-p", "hash": 494591185, "terms": ["How are buckets mapped to distributors?", "are buckets mapped to distributors?", "buckets mapped to distributors?", "mapped to distributors?", "to distributors?", "distributors?"], "tokens": ["How", "are", "buckets", "mapped", "to", "distributors", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1372816334", "fields": {"term": "Do distributors persist the bucket database?", "namespace": "open-p", "hash": 1372816334, "terms": ["Do distributors persist the bucket database?", "distributors persist the bucket database?", "persist the bucket database?", "the bucket database?", "bucket database?", "database?"], "tokens": ["Do", "distributors", "persist", "the", "bucket", "database", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::485338367", "fields": {"term": "Where is the bucket-to-content-node mapping kept?", "namespace": "open-p", "hash": 485338367, "terms": ["Where is the bucket-to-content-node mapping kept?", "is the bucket-to-content-node mapping kept?", "the bucket-to-content-node mapping kept?", "bucket-to-content-node mapping kept?", "mapping kept?", "kept?"], "tokens": ["Where", "is", "the", "bucket", "to", "content", "node", "mapping", "kept", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::850978155", "fields": {"term": "How are document operations mapped to content nodes?", "namespace": "open-p", "hash": 850978155, "terms": ["How are document operations mapped to content nodes?", "are document operations mapped to content nodes?", "document operations mapped to content nodes?", "operations mapped to content nodes?", "mapped to content nodes?", "to content nodes?", "content nodes?", "nodes?"], "tokens": ["How", "are", "document", "operations", "mapped", "to", "content", "nodes", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1130556419", "fields": {"term": "Do document API clients know the cluster state at startup?", "namespace": "open-p", "hash": 1130556419, "terms": ["Do document API clients know the cluster state at startup?", "document API clients know the cluster state at startup?", "API clients know the cluster state at startup?", "clients know the cluster state at startup?", "know the cluster state at startup?", "the cluster state at startup?", "cluster state at startup?", "state at startup?", "at startup?", "startup?"], "tokens": ["Do", "document", "API", "clients", "know", "the", "cluster", "state", "at", "startup", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-680128624", "fields": {"term": "What happens if the document operation hit the wrong distributor?", "namespace": "open-p", "hash": -680128624, "terms": ["What happens if the document operation hit the wrong distributor?", "happens if the document operation hit the wrong distributor?", "if the document operation hit the wrong distributor?", "the document operation hit the wrong distributor?", "document operation hit the wrong distributor?", "operation hit the wrong distributor?", "hit the wrong distributor?", "the wrong distributor?", "wrong distributor?", "distributor?"], "tokens": ["What", "happens", "if", "the", "document", "operation", "hit", "the", "wrong", "distributor", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::428980273", "fields": {"term": "What is a last modified time?", "namespace": "open-p", "hash": 428980273, "terms": ["What is a last modified time?", "is a last modified time?", "a last modified time?", "last modified time?", "modified time?", "time?"], "tokens": ["What", "is", "a", "last", "modified", "time", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-812047769", "fields": {"term": "How are timestamps unique?", "namespace": "open-p", "hash": -812047769, "terms": ["How are timestamps unique?", "are timestamps unique?", "timestamps unique?", "unique?"], "tokens": ["How", "are", "timestamps", "unique", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2124305161", "fields": {"term": "What is the timestamp unit?", "namespace": "open-p", "hash": 2124305161, "terms": ["What is the timestamp unit?", "is the timestamp unit?", "the timestamp unit?", "timestamp unit?", "unit?"], "tokens": ["What", "is", "the", "timestamp", "unit", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-35560939", "fields": {"term": "What is the use of timestamps?", "namespace": "open-p", "hash": -35560939, "terms": ["What is the use of timestamps?", "is the use of timestamps?", "the use of timestamps?", "use of timestamps?", "of timestamps?", "timestamps?"], "tokens": ["What", "is", "the", "use", "of", "timestamps", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1319917656", "fields": {"term": "How are timestamps affected by migration?", "namespace": "open-p", "hash": 1319917656, "terms": ["How are timestamps affected by migration?", "are timestamps affected by migration?", "timestamps affected by migration?", "affected by migration?", "by migration?", "migration?"], "tokens": ["How", "are", "timestamps", "affected", "by", "migration", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1766828544", "fields": {"term": "Do documents have new timestamps when reprocessed?", "namespace": "open-p", "hash": 1766828544, "terms": ["Do documents have new timestamps when reprocessed?", "documents have new timestamps when reprocessed?", "have new timestamps when reprocessed?", "new timestamps when reprocessed?", "timestamps when reprocessed?", "when reprocessed?", "reprocessed?"], "tokens": ["Do", "documents", "have", "new", "timestamps", "when", "reprocessed", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-848780995", "fields": {"term": "How are multiple operations on the same document handled?", "namespace": "open-p", "hash": -848780995, "terms": ["How are multiple operations on the same document handled?", "are multiple operations on the same document handled?", "multiple operations on the same document handled?", "operations on the same document handled?", "on the same document handled?", "the same document handled?", "same document handled?", "document handled?", "handled?"], "tokens": ["How", "are", "multiple", "operations", "on", "the", "same", "document", "handled", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::427991287", "fields": {"term": "What are bucket maintenance operations?", "namespace": "open-p", "hash": 427991287, "terms": ["What are bucket maintenance operations?", "are bucket maintenance operations?", "bucket maintenance operations?", "maintenance operations?", "operations?"], "tokens": ["What", "are", "bucket", "maintenance", "operations", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1095673602", "fields": {"term": "How are bucket inconsistencies resolved?", "namespace": "open-p", "hash": -1095673602, "terms": ["How are bucket inconsistencies resolved?", "are bucket inconsistencies resolved?", "bucket inconsistencies resolved?", "inconsistencies resolved?", "resolved?"], "tokens": ["How", "are", "bucket", "inconsistencies", "resolved", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1168054049", "fields": {"term": "What is the priority of maintenance operations?", "namespace": "open-p", "hash": 1168054049, "terms": ["What is the priority of maintenance operations?", "is the priority of maintenance operations?", "the priority of maintenance operations?", "priority of maintenance operations?", "of maintenance operations?", "maintenance operations?", "operations?"], "tokens": ["What", "is", "the", "priority", "of", "maintenance", "operations", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::199699459", "fields": {"term": "How do distributors synchronize maintenance and user load?", "namespace": "open-p", "hash": 199699459, "terms": ["How do distributors synchronize maintenance and user load?", "do distributors synchronize maintenance and user load?", "distributors synchronize maintenance and user load?", "synchronize maintenance and user load?", "maintenance and user load?", "and user load?", "user load?", "load?"], "tokens": ["How", "do", "distributors", "synchronize", "maintenance", "and", "user", "load", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1391136005", "fields": {"term": "What happens if buckets are too small or large?", "namespace": "open-p", "hash": -1391136005, "terms": ["What happens if buckets are too small or large?", "happens if buckets are too small or large?", "if buckets are too small or large?", "buckets are too small or large?", "are too small or large?", "too small or large?", "small or large?", "or large?", "large?"], "tokens": ["What", "happens", "if", "buckets", "are", "too", "small", "or", "large", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-563429561", "fields": {"term": "What happens when a distributor stops?", "namespace": "open-p", "hash": -563429561, "terms": ["What happens when a distributor stops?", "happens when a distributor stops?", "when a distributor stops?", "a distributor stops?", "distributor stops?", "stops?"], "tokens": ["What", "happens", "when", "a", "distributor", "stops", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1426153853", "fields": {"term": "What happens to requests for buckets owned by a restarting distributor?", "namespace": "open-p", "hash": 1426153853, "terms": ["What happens to requests for buckets owned by a restarting distributor?", "happens to requests for buckets owned by a restarting distributor?", "to requests for buckets owned by a restarting distributor?", "requests for buckets owned by a restarting distributor?", "for buckets owned by a restarting distributor?", "buckets owned by a restarting distributor?", "owned by a restarting distributor?", "by a restarting distributor?", "a restarting distributor?", "restarting distributor?", "distributor?"], "tokens": ["What", "happens", "to", "requests", "for", "buckets", "owned", "by", "a", "restarting", "distributor", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-554615684", "fields": {"term": "Will requests to buckets not owned by the restarting distributor be affected?", "namespace": "open-p", "hash": -554615684, "terms": ["Will requests to buckets not owned by the restarting distributor be affected?", "requests to buckets not owned by the restarting distributor be affected?", "to buckets not owned by the restarting distributor be affected?", "buckets not owned by the restarting distributor be affected?", "not owned by the restarting distributor be affected?", "owned by the restarting distributor be affected?", "by the restarting distributor be affected?", "the restarting distributor be affected?", "restarting distributor be affected?", "distributor be affected?", "be affected?", "affected?"], "tokens": ["Will", "requests", "to", "buckets", "not", "owned", "by", "the", "restarting", "distributor", "be", "affected", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1786086919", "fields": {"term": "What will happen to the latency of requests when a distributor stops?", "namespace": "open-p", "hash": 1786086919, "terms": ["What will happen to the latency of requests when a distributor stops?", "will happen to the latency of requests when a distributor stops?", "happen to the latency of requests when a distributor stops?", "to the latency of requests when a distributor stops?", "the latency of requests when a distributor stops?", "latency of requests when a distributor stops?", "of requests when a distributor stops?", "requests when a distributor stops?", "when a distributor stops?", "a distributor stops?", "distributor stops?", "stops?"], "tokens": ["What", "will", "happen", "to", "the", "latency", "of", "requests", "when", "a", "distributor", "stops", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-229198494", "fields": {"term": "What is the content node?", "namespace": "open-p", "hash": -229198494, "terms": ["What is the content node?", "is the content node?", "the content node?", "content node?", "node?"], "tokens": ["What", "is", "the", "content", "node", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::902610400", "fields": {"term": "What runs on the content node?", "namespace": "open-p", "hash": 902610400, "terms": ["What runs on the content node?", "runs on the content node?", "on the content node?", "the content node?", "content node?", "node?"], "tokens": ["What", "runs", "on", "the", "content", "node", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1605663140", "fields": {"term": "What happens during a controlled restart?", "namespace": "open-p", "hash": 1605663140, "terms": ["What happens during a controlled restart?", "happens during a controlled restart?", "during a controlled restart?", "a controlled restart?", "controlled restart?", "restart?"], "tokens": ["What", "happens", "during", "a", "controlled", "restart", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1336831366", "fields": {"term": "Are client requests affected by content node restarts?", "namespace": "open-p", "hash": 1336831366, "terms": ["Are client requests affected by content node restarts?", "client requests affected by content node restarts?", "requests affected by content node restarts?", "affected by content node restarts?", "by content node restarts?", "content node restarts?", "node restarts?", "restarts?"], "tokens": ["Are", "client", "requests", "affected", "by", "content", "node", "restarts", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-33686960", "fields": {"term": "What state does the node transition through during a restart?", "namespace": "open-p", "hash": -33686960, "terms": ["What state does the node transition through during a restart?", "state does the node transition through during a restart?", "does the node transition through during a restart?", "the node transition through during a restart?", "node transition through during a restart?", "transition through during a restart?", "through during a restart?", "during a restart?", "a restart?", "restart?"], "tokens": ["What", "state", "does", "the", "node", "transition", "through", "during", "a", "restart", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-862524058", "fields": {"term": "What happens when a node is in maintenance state during a restart?", "namespace": "open-p", "hash": -862524058, "terms": ["What happens when a node is in maintenance state during a restart?", "happens when a node is in maintenance state during a restart?", "when a node is in maintenance state during a restart?", "a node is in maintenance state during a restart?", "node is in maintenance state during a restart?", "is in maintenance state during a restart?", "in maintenance state during a restart?", "maintenance state during a restart?", "state during a restart?", "during a restart?", "a restart?", "restart?"], "tokens": ["What", "happens", "when", "a", "node", "is", "in", "maintenance", "state", "during", "a", "restart", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1612415342", "fields": {"term": "What happens to pending requests during a content node restart?", "namespace": "open-p", "hash": -1612415342, "terms": ["What happens to pending requests during a content node restart?", "happens to pending requests during a content node restart?", "to pending requests during a content node restart?", "pending requests during a content node restart?", "requests during a content node restart?", "during a content node restart?", "a content node restart?", "content node restart?", "node restart?", "restart?"], "tokens": ["What", "happens", "to", "pending", "requests", "during", "a", "content", "node", "restart", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::790014553", "fields": {"term": "What does the distributor do during a content node restart?", "namespace": "open-p", "hash": 790014553, "terms": ["What does the distributor do during a content node restart?", "does the distributor do during a content node restart?", "the distributor do during a content node restart?", "distributor do during a content node restart?", "do during a content node restart?", "during a content node restart?", "a content node restart?", "content node restart?", "node restart?", "restart?"], "tokens": ["What", "does", "the", "distributor", "do", "during", "a", "content", "node", "restart", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1901947520", "fields": {"term": "What information do content nodes gather when starting?", "namespace": "open-p", "hash": 1901947520, "terms": ["What information do content nodes gather when starting?", "information do content nodes gather when starting?", "do content nodes gather when starting?", "content nodes gather when starting?", "nodes gather when starting?", "gather when starting?", "when starting?", "starting?"], "tokens": ["What", "information", "do", "content", "nodes", "gather", "when", "starting", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-532591046", "fields": {"term": "What state is the service layer in during a content node restart?", "namespace": "open-p", "hash": -532591046, "terms": ["What state is the service layer in during a content node restart?", "state is the service layer in during a content node restart?", "is the service layer in during a content node restart?", "the service layer in during a content node restart?", "service layer in during a content node restart?", "layer in during a content node restart?", "in during a content node restart?", "during a content node restart?", "a content node restart?", "content node restart?", "node restart?", "restart?"], "tokens": ["What", "state", "is", "the", "service", "layer", "in", "during", "a", "content", "node", "restart", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-890950650", "fields": {"term": "What is the description of vds.idealstate.buckets?", "namespace": "open-p", "hash": -890950650, "terms": ["What is the description of vds.idealstate.buckets?", "is the description of vds.idealstate.buckets?", "the description of vds.idealstate.buckets?", "description of vds.idealstate.buckets?", "of vds.idealstate.buckets?", "vds.idealstate.buckets?"], "tokens": ["What", "is", "the", "description", "of", "vds", "idealstate", "buckets", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1903562895", "fields": {"term": "What is the description of vds.idealstate.buckets_notrusted?", "namespace": "open-p", "hash": 1903562895, "terms": ["What is the description of vds.idealstate.buckets_notrusted?", "is the description of vds.idealstate.buckets_notrusted?", "the description of vds.idealstate.buckets_notrusted?", "description of vds.idealstate.buckets_notrusted?", "of vds.idealstate.buckets_notrusted?", "vds.idealstate.buckets_notrusted?"], "tokens": ["What", "is", "the", "description", "of", "vds", "idealstate", "buckets_notrusted", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2034332642", "fields": {"term": "What is the description of vds.idealstate.join_bucket.pending?", "namespace": "open-p", "hash": -2034332642, "terms": ["What is the description of vds.idealstate.join_bucket.pending?", "is the description of vds.idealstate.join_bucket.pending?", "the description of vds.idealstate.join_bucket.pending?", "description of vds.idealstate.join_bucket.pending?", "of vds.idealstate.join_bucket.pending?", "vds.idealstate.join_bucket.pending?"], "tokens": ["What", "is", "the", "description", "of", "vds", "idealstate", "join_bucket", "pending", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::607229994", "fields": {"term": "What is the description of vds.idealstate.set_bucket_state.pending?", "namespace": "open-p", "hash": 607229994, "terms": ["What is the description of vds.idealstate.set_bucket_state.pending?", "is the description of vds.idealstate.set_bucket_state.pending?", "the description of vds.idealstate.set_bucket_state.pending?", "description of vds.idealstate.set_bucket_state.pending?", "of vds.idealstate.set_bucket_state.pending?", "vds.idealstate.set_bucket_state.pending?"], "tokens": ["What", "is", "the", "description", "of", "vds", "idealstate", "set_bucket_state", "pending", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::329075833", "fields": {"term": "What is the description of vds.idealstate.split_bucket.pending?", "namespace": "open-p", "hash": 329075833, "terms": ["What is the description of vds.idealstate.split_bucket.pending?", "is the description of vds.idealstate.split_bucket.pending?", "the description of vds.idealstate.split_bucket.pending?", "description of vds.idealstate.split_bucket.pending?", "of vds.idealstate.split_bucket.pending?", "vds.idealstate.split_bucket.pending?"], "tokens": ["What", "is", "the", "description", "of", "vds", "idealstate", "split_bucket", "pending", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::364453018", "fields": {"term": "What does cluster/v2/music return?", "namespace": "open-p", "hash": 364453018, "terms": ["What does cluster/v2/music return?", "does cluster/v2/music return?", "cluster/v2/music return?", "return?"], "tokens": ["What", "does", "cluster", "/", "v2", "/", "music", "return", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1692755926", "fields": {"term": "What does /cluster/v2/music/storage list?", "namespace": "open-p", "hash": -1692755926, "terms": ["What does /cluster/v2/music/storage list?", "does /cluster/v2/music/storage list?", "/cluster/v2/music/storage list?", "list?"], "tokens": ["What", "does", "/", "cluster", "/", "v2", "/", "music", "/", "storage", "list", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-666941410", "fields": {"term": "What are Vespa administrative procedures?", "namespace": "open-p", "hash": -666941410, "terms": ["What are Vespa administrative procedures?", "are Vespa administrative procedures?", "Vespa administrative procedures?", "administrative procedures?", "procedures?"], "tokens": ["What", "are", "Vespa", "administrative", "procedures", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-872175262", "fields": {"term": "What is the Multinode testing and observability sample app?", "namespace": "open-p", "hash": -872175262, "terms": ["What is the Multinode testing and observability sample app?", "is the Multinode testing and observability sample app?", "the Multinode testing and observability sample app?", "Multinode testing and observability sample app?", "testing and observability sample app?", "and observability sample app?", "observability sample app?", "sample app?", "app?"], "tokens": ["What", "is", "the", "Multinode", "testing", "and", "observability", "sample", "app", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::585162851", "fields": {"term": "What is the purpose of Vespa administrative procedures?", "namespace": "open-p", "hash": 585162851, "terms": ["What is the purpose of Vespa administrative procedures?", "is the purpose of Vespa administrative procedures?", "the purpose of Vespa administrative procedures?", "purpose of Vespa administrative procedures?", "of Vespa administrative procedures?", "Vespa administrative procedures?", "administrative procedures?", "procedures?"], "tokens": ["What", "is", "the", "purpose", "of", "Vespa", "administrative", "procedures", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-872725590", "fields": {"term": "What is the purpose of the Multinode testing and observability sample app?", "namespace": "open-p", "hash": -872725590, "terms": ["What is the purpose of the Multinode testing and observability sample app?", "is the purpose of the Multinode testing and observability sample app?", "the purpose of the Multinode testing and observability sample app?", "purpose of the Multinode testing and observability sample app?", "of the Multinode testing and observability sample app?", "the Multinode testing and observability sample app?", "Multinode testing and observability sample app?", "testing and observability sample app?", "and observability sample app?", "observability sample app?", "sample app?", "app?"], "tokens": ["What", "is", "the", "purpose", "of", "the", "Multinode", "testing", "and", "observability", "sample", "app", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-87351903", "fields": {"term": "What can I learn from the Multinode testing and observability sample app?", "namespace": "open-p", "hash": -87351903, "terms": ["What can I learn from the Multinode testing and observability sample app?", "can I learn from the Multinode testing and observability sample app?", "I learn from the Multinode testing and observability sample app?", "learn from the Multinode testing and observability sample app?", "from the Multinode testing and observability sample app?", "the Multinode testing and observability sample app?", "Multinode testing and observability sample app?", "testing and observability sample app?", "and observability sample app?", "observability sample app?", "sample app?", "app?"], "tokens": ["What", "can", "I", "learn", "from", "the", "Multinode", "testing", "and", "observability", "sample", "app", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-472793971", "fields": {"term": "Where can I find information on Vespa administrative procedures?", "namespace": "open-p", "hash": -472793971, "terms": ["Where can I find information on Vespa administrative procedures?", "can I find information on Vespa administrative procedures?", "I find information on Vespa administrative procedures?", "find information on Vespa administrative procedures?", "information on Vespa administrative procedures?", "on Vespa administrative procedures?", "Vespa administrative procedures?", "administrative procedures?", "procedures?"], "tokens": ["Where", "can", "I", "find", "information", "on", "Vespa", "administrative", "procedures", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::94244011", "fields": {"term": "Can I contribute to Vespa?", "namespace": "open-p", "hash": 94244011, "terms": ["Can I contribute to Vespa?", "I contribute to Vespa?", "contribute to Vespa?", "to Vespa?", "Vespa?"], "tokens": ["Can", "I", "contribute", "to", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1058178808", "fields": {"term": "What is Github flow model?", "namespace": "open-p", "hash": -1058178808, "terms": ["What is Github flow model?", "is Github flow model?", "Github flow model?", "flow model?", "model?"], "tokens": ["What", "is", "Github", "flow", "model", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1626549424", "fields": {"term": "How often is the master branch released?", "namespace": "open-p", "hash": 1626549424, "terms": ["How often is the master branch released?", "often is the master branch released?", "is the master branch released?", "the master branch released?", "master branch released?", "branch released?", "released?"], "tokens": ["How", "often", "is", "the", "master", "branch", "released", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-601323695", "fields": {"term": "What is the Screwdriver build?", "namespace": "open-p", "hash": -601323695, "terms": ["What is the Screwdriver build?", "is the Screwdriver build?", "the Screwdriver build?", "Screwdriver build?", "build?"], "tokens": ["What", "is", "the", "Screwdriver", "build", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-807031224", "fields": {"term": "Are there acceptance and performance tests?", "namespace": "open-p", "hash": -807031224, "terms": ["Are there acceptance and performance tests?", "there acceptance and performance tests?", "acceptance and performance tests?", "and performance tests?", "performance tests?", "tests?"], "tokens": ["Are", "there", "acceptance", "and", "performance", "tests", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-10111387", "fields": {"term": "Who reviews pull requests?", "namespace": "open-p", "hash": -10111387, "terms": ["Who reviews pull requests?", "reviews pull requests?", "pull requests?", "requests?"], "tokens": ["Who", "reviews", "pull", "requests", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-16751876", "fields": {"term": "Where can I find a reviewer?", "namespace": "open-p", "hash": -16751876, "terms": ["Where can I find a reviewer?", "can I find a reviewer?", "I find a reviewer?", "find a reviewer?", "a reviewer?", "reviewer?"], "tokens": ["Where", "can", "I", "find", "a", "reviewer", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1859265685", "fields": {"term": "How to become an OWNER?", "namespace": "open-p", "hash": -1859265685, "terms": ["How to become an OWNER?", "to become an OWNER?", "become an OWNER?", "an OWNER?", "OWNER?"], "tokens": ["How", "to", "become", "an", "OWNER", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::131817623", "fields": {"term": "What are the pull request requirements?", "namespace": "open-p", "hash": 131817623, "terms": ["What are the pull request requirements?", "are the pull request requirements?", "the pull request requirements?", "pull request requirements?", "request requirements?", "requirements?"], "tokens": ["What", "are", "the", "pull", "request", "requirements", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-236535384", "fields": {"term": "What is the OWNERS file responsibility?", "namespace": "open-p", "hash": -236535384, "terms": ["What is the OWNERS file responsibility?", "is the OWNERS file responsibility?", "the OWNERS file responsibility?", "OWNERS file responsibility?", "file responsibility?", "responsibility?"], "tokens": ["What", "is", "the", "OWNERS", "file", "responsibility", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-332965290", "fields": {"term": "What is semantic versioning in Vespa?", "namespace": "open-p", "hash": -332965290, "terms": ["What is semantic versioning in Vespa?", "is semantic versioning in Vespa?", "semantic versioning in Vespa?", "versioning in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["What", "is", "semantic", "versioning", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-247470836", "fields": {"term": "What is the link to Vespa versions?", "namespace": "open-p", "hash": -247470836, "terms": ["What is the link to Vespa versions?", "is the link to Vespa versions?", "the link to Vespa versions?", "link to Vespa versions?", "to Vespa versions?", "Vespa versions?", "versions?"], "tokens": ["What", "is", "the", "link", "to", "Vespa", "versions", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1858833224", "fields": {"term": "What is the @PublicAPI annotation?", "namespace": "open-p", "hash": 1858833224, "terms": ["What is the @PublicAPI annotation?", "is the @PublicAPI annotation?", "the @PublicAPI annotation?", "@PublicAPI annotation?", "annotation?"], "tokens": ["What", "is", "the", "@", "PublicAPI", "annotation", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-606142739", "fields": {"term": "Where can issues be tracked on Vespa?", "namespace": "open-p", "hash": -606142739, "terms": ["Where can issues be tracked on Vespa?", "can issues be tracked on Vespa?", "issues be tracked on Vespa?", "be tracked on Vespa?", "tracked on Vespa?", "on Vespa?", "Vespa?"], "tokens": ["Where", "can", "issues", "be", "tracked", "on", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1872899800", "fields": {"term": "Where can I join Vespa community?", "namespace": "open-p", "hash": -1872899800, "terms": ["Where can I join Vespa community?", "can I join Vespa community?", "I join Vespa community?", "join Vespa community?", "Vespa community?", "community?"], "tokens": ["Where", "can", "I", "join", "Vespa", "community", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::691915824", "fields": {"term": "What is the Vespa community?", "namespace": "open-p", "hash": 691915824, "terms": ["What is the Vespa community?", "is the Vespa community?", "the Vespa community?", "Vespa community?", "community?"], "tokens": ["What", "is", "the", "Vespa", "community", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::563558782", "fields": {"term": "What is the default JSON format?", "namespace": "open-p", "hash": 563558782, "terms": ["What is the default JSON format?", "is the default JSON format?", "the default JSON format?", "default JSON format?", "JSON format?", "format?"], "tokens": ["What", "is", "the", "default", "JSON", "format", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1178632954", "fields": {"term": "What is the timing object?", "namespace": "open-p", "hash": 1178632954, "terms": ["What is the timing object?", "is the timing object?", "the timing object?", "timing object?", "object?"], "tokens": ["What", "is", "the", "timing", "object", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1302563935", "fields": {"term": "What is the root element?", "namespace": "open-p", "hash": -1302563935, "terms": ["What is the root element?", "is the root element?", "the root element?", "root element?", "element?"], "tokens": ["What", "is", "the", "root", "element", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1078900912", "fields": {"term": "What is the format of the coverage object?", "namespace": "open-p", "hash": -1078900912, "terms": ["What is the format of the coverage object?", "is the format of the coverage object?", "the format of the coverage object?", "format of the coverage object?", "of the coverage object?", "the coverage object?", "coverage object?", "object?"], "tokens": ["What", "is", "the", "format", "of", "the", "coverage", "object", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1669578114", "fields": {"term": "What is trace in Vespa API?", "namespace": "open-p", "hash": -1669578114, "terms": ["What is trace in Vespa API?", "is trace in Vespa API?", "trace in Vespa API?", "in Vespa API?", "Vespa API?", "API?"], "tokens": ["What", "is", "trace", "in", "Vespa", "API", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1127537503", "fields": {"term": "What is the schema format?", "namespace": "open-p", "hash": -1127537503, "terms": ["What is the schema format?", "is the schema format?", "the schema format?", "schema format?", "format?"], "tokens": ["What", "is", "the", "schema", "format", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1707789647", "fields": {"term": "What is the coverage property?", "namespace": "open-p", "hash": 1707789647, "terms": ["What is the coverage property?", "is the coverage property?", "the coverage property?", "coverage property?", "property?"], "tokens": ["What", "is", "the", "coverage", "property", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1237793855", "fields": {"term": "What is the purpose of the trace node?", "namespace": "open-p", "hash": 1237793855, "terms": ["What is the purpose of the trace node?", "is the purpose of the trace node?", "the purpose of the trace node?", "purpose of the trace node?", "of the trace node?", "the trace node?", "trace node?", "node?"], "tokens": ["What", "is", "the", "purpose", "of", "the", "trace", "node", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-765752854", "fields": {"term": "What is the input representation of weightedset fields?", "namespace": "open-p", "hash": -765752854, "terms": ["What is the input representation of weightedset fields?", "is the input representation of weightedset fields?", "the input representation of weightedset fields?", "input representation of weightedset fields?", "representation of weightedset fields?", "of weightedset fields?", "weightedset fields?", "fields?"], "tokens": ["What", "is", "the", "input", "representation", "of", "weightedset", "fields", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1494517686", "fields": {"term": "How is a field with weightedset type represented in search results?", "namespace": "open-p", "hash": -1494517686, "terms": ["How is a field with weightedset type represented in search results?", "is a field with weightedset type represented in search results?", "a field with weightedset type represented in search results?", "field with weightedset type represented in search results?", "with weightedset type represented in search results?", "weightedset type represented in search results?", "type represented in search results?", "represented in search results?", "in search results?", "search results?", "results?"], "tokens": ["How", "is", "a", "field", "with", "weightedset", "type", "represented", "in", "search", "results", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::922289501", "fields": {"term": "What is the JSON input representation?", "namespace": "open-p", "hash": 922289501, "terms": ["What is the JSON input representation?", "is the JSON input representation?", "the JSON input representation?", "JSON input representation?", "input representation?", "representation?"], "tokens": ["What", "is", "the", "JSON", "input", "representation", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1705669835", "fields": {"term": "What is the default JSON renderer for search results?", "namespace": "open-p", "hash": -1705669835, "terms": ["What is the default JSON renderer for search results?", "is the default JSON renderer for search results?", "the default JSON renderer for search results?", "default JSON renderer for search results?", "JSON renderer for search results?", "renderer for search results?", "for search results?", "search results?", "results?"], "tokens": ["What", "is", "the", "default", "JSON", "renderer", "for", "search", "results", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-509849847", "fields": {"term": "What is the format used when rendering a document?", "namespace": "open-p", "hash": -509849847, "terms": ["What is the format used when rendering a document?", "is the format used when rendering a document?", "the format used when rendering a document?", "format used when rendering a document?", "used when rendering a document?", "when rendering a document?", "rendering a document?", "a document?", "document?"], "tokens": ["What", "is", "the", "format", "used", "when", "rendering", "a", "document", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1940470625", "fields": {"term": "What is the key/value pair format used in search results?", "namespace": "open-p", "hash": -1940470625, "terms": ["What is the key/value pair format used in search results?", "is the key/value pair format used in search results?", "the key/value pair format used in search results?", "key/value pair format used in search results?", "pair format used in search results?", "format used in search results?", "used in search results?", "in search results?", "search results?", "results?"], "tokens": ["What", "is", "the", "key", "/", "value", "pair", "format", "used", "in", "search", "results", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::391451096", "fields": {"term": "What are x and y used for when rendering positions?", "namespace": "open-p", "hash": 391451096, "terms": ["What are x and y used for when rendering positions?", "are x and y used for when rendering positions?", "x and y used for when rendering positions?", "and y used for when rendering positions?", "y used for when rendering positions?", "used for when rendering positions?", "for when rendering positions?", "when rendering positions?", "rendering positions?", "positions?"], "tokens": ["What", "are", "x", "and", "y", "used", "for", "when", "rendering", "positions", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1425519361", "fields": {"term": "What is the difference between Vespa 7 and Vespa 8 position rendering?", "namespace": "open-p", "hash": -1425519361, "terms": ["What is the difference between Vespa 7 and Vespa 8 position rendering?", "is the difference between Vespa 7 and Vespa 8 position rendering?", "the difference between Vespa 7 and Vespa 8 position rendering?", "difference between Vespa 7 and Vespa 8 position rendering?", "between Vespa 7 and Vespa 8 position rendering?", "Vespa 7 and Vespa 8 position rendering?", "7 and Vespa 8 position rendering?", "and Vespa 8 position rendering?", "Vespa 8 position rendering?", "8 position rendering?", "position rendering?", "rendering?"], "tokens": ["What", "is", "the", "difference", "between", "Vespa", "7", "and", "Vespa", "8", "position", "rendering", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-794651103", "fields": {"term": "How do you switch back to the legacy Vespa 7 position rendering?", "namespace": "open-p", "hash": -794651103, "terms": ["How do you switch back to the legacy Vespa 7 position rendering?", "do you switch back to the legacy Vespa 7 position rendering?", "you switch back to the legacy Vespa 7 position rendering?", "switch back to the legacy Vespa 7 position rendering?", "back to the legacy Vespa 7 position rendering?", "to the legacy Vespa 7 position rendering?", "the legacy Vespa 7 position rendering?", "legacy Vespa 7 position rendering?", "Vespa 7 position rendering?", "7 position rendering?", "position rendering?", "rendering?"], "tokens": ["How", "do", "you", "switch", "back", "to", "the", "legacy", "Vespa", "7", "position", "rendering", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-620359922", "fields": {"term": "What fields are removed in Vespa 8 position rendering?", "namespace": "open-p", "hash": -620359922, "terms": ["What fields are removed in Vespa 8 position rendering?", "fields are removed in Vespa 8 position rendering?", "are removed in Vespa 8 position rendering?", "removed in Vespa 8 position rendering?", "in Vespa 8 position rendering?", "Vespa 8 position rendering?", "8 position rendering?", "position rendering?", "rendering?"], "tokens": ["What", "fields", "are", "removed", "in", "Vespa", "8", "position", "rendering", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1808279052", "fields": {"term": "What is the 'lat' and 'lng' fields used for?", "namespace": "open-p", "hash": -1808279052, "terms": ["What is the 'lat' and 'lng' fields used for?", "is the 'lat' and 'lng' fields used for?", "the 'lat' and 'lng' fields used for?", "'lat' and 'lng' fields used for?", "and 'lng' fields used for?", "'lng' fields used for?", "fields used for?", "used for?", "for?"], "tokens": ["What", "is", "the", "'", "lat", "'", "and", "'", "lng", "'", "fields", "used", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1007302552", "fields": {"term": "What is the format used for Google places API fields?", "namespace": "open-p", "hash": -1007302552, "terms": ["What is the format used for Google places API fields?", "is the format used for Google places API fields?", "the format used for Google places API fields?", "format used for Google places API fields?", "used for Google places API fields?", "for Google places API fields?", "Google places API fields?", "places API fields?", "API fields?", "fields?"], "tokens": ["What", "is", "the", "format", "used", "for", "Google", "places", "API", "fields", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::911058113", "fields": {"term": "What changes have been made to synthetic summary fields in Vespa 8?", "namespace": "open-p", "hash": 911058113, "terms": ["What changes have been made to synthetic summary fields in Vespa 8?", "changes have been made to synthetic summary fields in Vespa 8?", "have been made to synthetic summary fields in Vespa 8?", "been made to synthetic summary fields in Vespa 8?", "made to synthetic summary fields in Vespa 8?", "to synthetic summary fields in Vespa 8?", "synthetic summary fields in Vespa 8?", "summary fields in Vespa 8?", "fields in Vespa 8?", "in Vespa 8?", "Vespa 8?", "8?"], "tokens": ["What", "changes", "have", "been", "made", "to", "synthetic", "summary", "fields", "in", "Vespa", "8", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1460498654", "fields": {"term": "What is FeatureData class?", "namespace": "open-p", "hash": 1460498654, "terms": ["What is FeatureData class?", "is FeatureData class?", "FeatureData class?", "class?"], "tokens": ["What", "is", "FeatureData", "class", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1947538790", "fields": {"term": "What is the JSON output?", "namespace": "open-p", "hash": 1947538790, "terms": ["What is the JSON output?", "is the JSON output?", "the JSON output?", "JSON output?", "output?"], "tokens": ["What", "is", "the", "JSON", "output", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-355962374", "fields": {"term": "How to get old behavior?", "namespace": "open-p", "hash": -355962374, "terms": ["How to get old behavior?", "to get old behavior?", "get old behavior?", "old behavior?", "behavior?"], "tokens": ["How", "to", "get", "old", "behavior", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::730065900", "fields": {"term": "What is the proton documentdb?", "namespace": "open-p", "hash": 730065900, "terms": ["What is the proton documentdb?", "is the proton documentdb?", "the proton documentdb?", "proton documentdb?", "documentdb?"], "tokens": ["What", "is", "the", "proton", "documentdb", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1977517498", "fields": {"term": "What is the purpose of content.proton.search_protocol.docsum?", "namespace": "open-p", "hash": 1977517498, "terms": ["What is the purpose of content.proton.search_protocol.docsum?", "is the purpose of content.proton.search_protocol.docsum?", "the purpose of content.proton.search_protocol.docsum?", "purpose of content.proton.search_protocol.docsum?", "of content.proton.search_protocol.docsum?", "content.proton.search_protocol.docsum?"], "tokens": ["What", "is", "the", "purpose", "of", "content", "proton", "search_protocol", "docsum", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1889032212", "fields": {"term": "What is the purpose of content.proton.documentdb.documents ?", "namespace": "open-p", "hash": 1889032212, "terms": ["What is the purpose of content.proton.documentdb.documents ?", "is the purpose of content.proton.documentdb.documents ?", "the purpose of content.proton.documentdb.documents ?", "purpose of content.proton.documentdb.documents ?", "of content.proton.documentdb.documents ?", "content.proton.documentdb.documents ?", "?"], "tokens": ["What", "is", "the", "purpose", "of", "content", "proton", "documentdb", "documents", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1962373922", "fields": {"term": "What is the purpose of content.proton.resource_usage?", "namespace": "open-p", "hash": -1962373922, "terms": ["What is the purpose of content.proton.resource_usage?", "is the purpose of content.proton.resource_usage?", "the purpose of content.proton.resource_usage?", "purpose of content.proton.resource_usage?", "of content.proton.resource_usage?", "content.proton.resource_usage?"], "tokens": ["What", "is", "the", "purpose", "of", "content", "proton", "resource_usage", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-207445240", "fields": {"term": "What is the meaning of content.proton.documentdb.matching.docs_matched?", "namespace": "open-p", "hash": -207445240, "terms": ["What is the meaning of content.proton.documentdb.matching.docs_matched?", "is the meaning of content.proton.documentdb.matching.docs_matched?", "the meaning of content.proton.documentdb.matching.docs_matched?", "meaning of content.proton.documentdb.matching.docs_matched?", "of content.proton.documentdb.matching.docs_matched?", "content.proton.documentdb.matching.docs_matched?"], "tokens": ["What", "is", "the", "meaning", "of", "content", "proton", "documentdb", "matching", "docs_matched", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1319064830", "fields": {"term": "What is the meaning of content.proton.documentdb.matching.rank_profile.query_setup_time?", "namespace": "open-p", "hash": 1319064830, "terms": ["What is the meaning of content.proton.documentdb.matching.rank_profile.query_setup_time?", "is the meaning of content.proton.documentdb.matching.rank_profile.query_setup_time?", "the meaning of content.proton.documentdb.matching.rank_profile.query_setup_time?", "meaning of content.proton.documentdb.matching.rank_profile.query_setup_time?", "of content.proton.documentdb.matching.rank_profile.query_setup_time?", "content.proton.documentdb.matching.rank_profile.query_setup_time?"], "tokens": ["What", "is", "the", "meaning", "of", "content", "proton", "documentdb", "matching", "rank_profile", "query_setup_time", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::534349118", "fields": {"term": "What is HTTP Deploy API?", "namespace": "open-p", "hash": 534349118, "terms": ["What is HTTP Deploy API?", "is HTTP Deploy API?", "HTTP Deploy API?", "Deploy API?", "API?"], "tokens": ["What", "is", "HTTP", "Deploy", "API", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1019405984", "fields": {"term": "What is a session-id used for?", "namespace": "open-p", "hash": -1019405984, "terms": ["What is a session-id used for?", "is a session-id used for?", "a session-id used for?", "session-id used for?", "used for?", "for?"], "tokens": ["What", "is", "a", "session", "id", "used", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-417518957", "fields": {"term": "What is the API port?", "namespace": "open-p", "hash": -417518957, "terms": ["What is the API port?", "is the API port?", "the API port?", "API port?", "port?"], "tokens": ["What", "is", "the", "API", "port", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2042336072", "fields": {"term": "What is the format of an application file path?", "namespace": "open-p", "hash": 2042336072, "terms": ["What is the format of an application file path?", "is the format of an application file path?", "the format of an application file path?", "format of an application file path?", "of an application file path?", "an application file path?", "application file path?", "file path?", "path?"], "tokens": ["What", "is", "the", "format", "of", "an", "application", "file", "path", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::903183241", "fields": {"term": "What tools can be used for deployment?", "namespace": "open-p", "hash": 903183241, "terms": ["What tools can be used for deployment?", "tools can be used for deployment?", "can be used for deployment?", "be used for deployment?", "used for deployment?", "for deployment?", "deployment?"], "tokens": ["What", "tools", "can", "be", "used", "for", "deployment", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1827220905", "fields": {"term": "What is the purpose of creating a session?", "namespace": "open-p", "hash": -1827220905, "terms": ["What is the purpose of creating a session?", "is the purpose of creating a session?", "the purpose of creating a session?", "purpose of creating a session?", "of creating a session?", "creating a session?", "a session?", "session?"], "tokens": ["What", "is", "the", "purpose", "of", "creating", "a", "session", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2073818543", "fields": {"term": "What parameters are needed to create a new session?", "namespace": "open-p", "hash": -2073818543, "terms": ["What parameters are needed to create a new session?", "parameters are needed to create a new session?", "are needed to create a new session?", "needed to create a new session?", "to create a new session?", "create a new session?", "a new session?", "new session?", "session?"], "tokens": ["What", "parameters", "are", "needed", "to", "create", "a", "new", "session", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::114242171", "fields": {"term": "What is the response when a session is created?", "namespace": "open-p", "hash": 114242171, "terms": ["What is the response when a session is created?", "is the response when a session is created?", "the response when a session is created?", "response when a session is created?", "when a session is created?", "a session is created?", "session is created?", "is created?", "created?"], "tokens": ["What", "is", "the", "response", "when", "a", "session", "is", "created", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1040615096", "fields": {"term": "What does GET /application/v2/tenant/default/application/default do?", "namespace": "open-p", "hash": 1040615096, "terms": ["What does GET /application/v2/tenant/default/application/default do?", "does GET /application/v2/tenant/default/application/default do?", "GET /application/v2/tenant/default/application/default do?", "/application/v2/tenant/default/application/default do?", "do?"], "tokens": ["What", "does", "GET", "/", "application", "/", "v2", "/", "tenant", "/", "default", "/", "application", "/", "default", "do", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1554691397", "fields": {"term": "What does reindexing status return?", "namespace": "open-p", "hash": 1554691397, "terms": ["What does reindexing status return?", "does reindexing status return?", "reindexing status return?", "status return?", "return?"], "tokens": ["What", "does", "reindexing", "status", "return", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1757616593", "fields": {"term": "What is the status of test_result?", "namespace": "open-p", "hash": -1757616593, "terms": ["What is the status of test_result?", "is the status of test_result?", "the status of test_result?", "status of test_result?", "of test_result?", "test_result?"], "tokens": ["What", "is", "the", "status", "of", "test_result", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1175196863", "fields": {"term": "What is the progress of test_artifact?", "namespace": "open-p", "hash": 1175196863, "terms": ["What is the progress of test_artifact?", "is the progress of test_artifact?", "the progress of test_artifact?", "progress of test_artifact?", "of test_artifact?", "test_artifact?"], "tokens": ["What", "is", "the", "progress", "of", "test_artifact", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1631852709", "fields": {"term": "What is reindexing?", "namespace": "open-p", "hash": -1631852709, "terms": ["What is reindexing?", "is reindexing?", "reindexing?"], "tokens": ["What", "is", "reindexing", "?"], "corpus_count": 4, "document_count": 4}}, -{"put": "id:term:term::-2073658004", "fields": {"term": "How do you trigger reindexing?", "namespace": "open-p", "hash": -2073658004, "terms": ["How do you trigger reindexing?", "do you trigger reindexing?", "you trigger reindexing?", "trigger reindexing?", "reindexing?"], "tokens": ["How", "do", "you", "trigger", "reindexing", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1456808817", "fields": {"term": "What are the parameters for reindexing?", "namespace": "open-p", "hash": 1456808817, "terms": ["What are the parameters for reindexing?", "are the parameters for reindexing?", "the parameters for reindexing?", "parameters for reindexing?", "for reindexing?", "reindexing?"], "tokens": ["What", "are", "the", "parameters", "for", "reindexing", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1799010138", "fields": {"term": "What does indexedOnly do?", "namespace": "open-p", "hash": 1799010138, "terms": ["What does indexedOnly do?", "does indexedOnly do?", "indexedOnly do?", "do?"], "tokens": ["What", "does", "indexedOnly", "do", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2107758862", "fields": {"term": "What is the default speed for reindexing?", "namespace": "open-p", "hash": -2107758862, "terms": ["What is the default speed for reindexing?", "is the default speed for reindexing?", "the default speed for reindexing?", "default speed for reindexing?", "speed for reindexing?", "for reindexing?", "reindexing?"], "tokens": ["What", "is", "the", "default", "speed", "for", "reindexing", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1552752177", "fields": {"term": "What is the response to reindexing?", "namespace": "open-p", "hash": 1552752177, "terms": ["What is the response to reindexing?", "is the response to reindexing?", "the response to reindexing?", "response to reindexing?", "to reindexing?", "reindexing?"], "tokens": ["What", "is", "the", "response", "to", "reindexing", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-602287521", "fields": {"term": "What is the request body for reindexing?", "namespace": "open-p", "hash": -602287521, "terms": ["What is the request body for reindexing?", "is the request body for reindexing?", "the request body for reindexing?", "request body for reindexing?", "body for reindexing?", "for reindexing?", "reindexing?"], "tokens": ["What", "is", "the", "request", "body", "for", "reindexing", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-341340398", "fields": {"term": "What is 'message' in the response?", "namespace": "open-p", "hash": -341340398, "terms": ["What is 'message' in the response?", "is 'message' in the response?", "'message' in the response?", "in the response?", "the response?", "response?"], "tokens": ["What", "is", "'", "message", "'", "in", "the", "response", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::297307814", "fields": {"term": "What does the response message say?", "namespace": "open-p", "hash": 297307814, "terms": ["What does the response message say?", "does the response message say?", "the response message say?", "response message say?", "message say?", "say?"], "tokens": ["What", "does", "the", "response", "message", "say", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1884455519", "fields": {"term": "What does GET /application/v2/host/ do?", "namespace": "open-p", "hash": 1884455519, "terms": ["What does GET /application/v2/host/ do?", "does GET /application/v2/host/ do?", "GET /application/v2/host/ do?", "/application/v2/host/ do?", "do?"], "tokens": ["What", "does", "GET", "/", "application", "/", "v2", "/", "host", "/", "do", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::651415849", "fields": {"term": "What hostname is used in the example?", "namespace": "open-p", "hash": 651415849, "terms": ["What hostname is used in the example?", "hostname is used in the example?", "is used in the example?", "used in the example?", "in the example?", "the example?", "example?"], "tokens": ["What", "hostname", "is", "used", "in", "the", "example", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::384438042", "fields": {"term": "What is an error response?", "namespace": "open-p", "hash": 384438042, "terms": ["What is an error response?", "is an error response?", "an error response?", "error response?", "response?"], "tokens": ["What", "is", "an", "error", "response", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-418528438", "fields": {"term": "What does the 400 error code mean?", "namespace": "open-p", "hash": -418528438, "terms": ["What does the 400 error code mean?", "does the 400 error code mean?", "the 400 error code mean?", "400 error code mean?", "error code mean?", "code mean?", "mean?"], "tokens": ["What", "does", "the", "400", "error", "code", "mean", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1628297423", "fields": {"term": "What is the error message for 401 error code?", "namespace": "open-p", "hash": 1628297423, "terms": ["What is the error message for 401 error code?", "is the error message for 401 error code?", "the error message for 401 error code?", "error message for 401 error code?", "message for 401 error code?", "for 401 error code?", "401 error code?", "error code?", "code?"], "tokens": ["What", "is", "the", "error", "message", "for", "401", "error", "code", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-905531332", "fields": {"term": "What are the possible error codes for Vespa?", "namespace": "open-p", "hash": -905531332, "terms": ["What are the possible error codes for Vespa?", "are the possible error codes for Vespa?", "the possible error codes for Vespa?", "possible error codes for Vespa?", "error codes for Vespa?", "codes for Vespa?", "for Vespa?", "Vespa?"], "tokens": ["What", "are", "the", "possible", "error", "codes", "for", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::214143656", "fields": {"term": "Where are requests logged?", "namespace": "open-p", "hash": 214143656, "terms": ["Where are requests logged?", "are requests logged?", "requests logged?", "logged?"], "tokens": ["Where", "are", "requests", "logged", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-610339707", "fields": {"term": "What is the access log path?", "namespace": "open-p", "hash": -610339707, "terms": ["What is the access log path?", "is the access log path?", "the access log path?", "access log path?", "log path?", "path?"], "tokens": ["What", "is", "the", "access", "log", "path", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::463033347", "fields": {"term": "What does the access log contain?", "namespace": "open-p", "hash": 463033347, "terms": ["What does the access log contain?", "does the access log contain?", "the access log contain?", "access log contain?", "log contain?", "contain?"], "tokens": ["What", "does", "the", "access", "log", "contain", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1552216247", "fields": {"term": "What is the format of the access log?", "namespace": "open-p", "hash": 1552216247, "terms": ["What is the format of the access log?", "is the format of the access log?", "the format of the access log?", "format of the access log?", "of the access log?", "the access log?", "access log?", "log?"], "tokens": ["What", "is", "the", "format", "of", "the", "access", "log", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::875928206", "fields": {"term": "What is the IP field in the access log?", "namespace": "open-p", "hash": 875928206, "terms": ["What is the IP field in the access log?", "is the IP field in the access log?", "the IP field in the access log?", "IP field in the access log?", "field in the access log?", "in the access log?", "the access log?", "access log?", "log?"], "tokens": ["What", "is", "the", "IP", "field", "in", "the", "access", "log", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1294826035", "fields": {"term": "How to create a session with an application package?", "namespace": "open-p", "hash": -1294826035, "terms": ["How to create a session with an application package?", "to create a session with an application package?", "create a session with an application package?", "a session with an application package?", "session with an application package?", "with an application package?", "an application package?", "application package?", "package?"], "tokens": ["How", "to", "create", "a", "session", "with", "an", "application", "package", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1286671868", "fields": {"term": "What is the URL to activate the application?", "namespace": "open-p", "hash": 1286671868, "terms": ["What is the URL to activate the application?", "is the URL to activate the application?", "the URL to activate the application?", "URL to activate the application?", "to activate the application?", "activate the application?", "the application?", "application?"], "tokens": ["What", "is", "the", "URL", "to", "activate", "the", "application", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1666277458", "fields": {"term": "What is the URL to prepare the application?", "namespace": "open-p", "hash": 1666277458, "terms": ["What is the URL to prepare the application?", "is the URL to prepare the application?", "the URL to prepare the application?", "URL to prepare the application?", "to prepare the application?", "prepare the application?", "the application?", "application?"], "tokens": ["What", "is", "the", "URL", "to", "prepare", "the", "application", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1185288280", "fields": {"term": "How do we modify services.xml?", "namespace": "open-p", "hash": -1185288280, "terms": ["How do we modify services.xml?", "do we modify services.xml?", "we modify services.xml?", "modify services.xml?", "services.xml?"], "tokens": ["How", "do", "we", "modify", "services", "xml", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-86217280", "fields": {"term": "What is session 1 and 2?", "namespace": "open-p", "hash": -86217280, "terms": ["What is session 1 and 2?", "is session 1 and 2?", "session 1 and 2?", "1 and 2?", "and 2?", "2?"], "tokens": ["What", "is", "session", "1", "and", "2", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-847127386", "fields": {"term": "What is the purpose of rpcport?", "namespace": "open-p", "hash": -847127386, "terms": ["What is the purpose of rpcport?", "is the purpose of rpcport?", "the purpose of rpcport?", "purpose of rpcport?", "of rpcport?", "rpcport?"], "tokens": ["What", "is", "the", "purpose", "of", "rpcport", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1410249361", "fields": {"term": "How to make a session active?", "namespace": "open-p", "hash": -1410249361, "terms": ["How to make a session active?", "to make a session active?", "make a session active?", "a session active?", "session active?", "active?"], "tokens": ["How", "to", "make", "a", "session", "active", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2068209971", "fields": {"term": "How to roll back application package?", "namespace": "open-p", "hash": 2068209971, "terms": ["How to roll back application package?", "to roll back application package?", "roll back application package?", "back application package?", "application package?", "package?"], "tokens": ["How", "to", "roll", "back", "application", "package", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2023925125", "fields": {"term": "What is session-id?", "namespace": "open-p", "hash": 2023925125, "terms": ["What is session-id?", "is session-id?", "session-id?"], "tokens": ["What", "is", "session", "id", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1392147660", "fields": {"term": "Where can I find sample applications?", "namespace": "open-p", "hash": -1392147660, "terms": ["Where can I find sample applications?", "can I find sample applications?", "I find sample applications?", "find sample applications?", "sample applications?", "applications?"], "tokens": ["Where", "can", "I", "find", "sample", "applications", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1985482887", "fields": {"term": "What is the development cycle?", "namespace": "open-p", "hash": 1985482887, "terms": ["What is the development cycle?", "is the development cycle?", "the development cycle?", "development cycle?", "cycle?"], "tokens": ["What", "is", "the", "development", "cycle", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1686293680", "fields": {"term": "What is metal-names.def?", "namespace": "open-p", "hash": -1686293680, "terms": ["What is metal-names.def?", "is metal-names.def?", "metal-names.def?"], "tokens": ["What", "is", "metal", "names", "def", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2031013631", "fields": {"term": "How to debug in Vespa?", "namespace": "open-p", "hash": 2031013631, "terms": ["How to debug in Vespa?", "to debug in Vespa?", "debug in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["How", "to", "debug", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-96637392", "fields": {"term": "How to set up remote debugging in Vespa?", "namespace": "open-p", "hash": -96637392, "terms": ["How to set up remote debugging in Vespa?", "to set up remote debugging in Vespa?", "set up remote debugging in Vespa?", "up remote debugging in Vespa?", "remote debugging in Vespa?", "debugging in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["How", "to", "set", "up", "remote", "debugging", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::447997174", "fields": {"term": "How to configure jvm options in Vespa?", "namespace": "open-p", "hash": 447997174, "terms": ["How to configure jvm options in Vespa?", "to configure jvm options in Vespa?", "configure jvm options in Vespa?", "jvm options in Vespa?", "options in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["How", "to", "configure", "jvm", "options", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-681720961", "fields": {"term": "What is the default jvm option for remote debugging in Vespa?", "namespace": "open-p", "hash": -681720961, "terms": ["What is the default jvm option for remote debugging in Vespa?", "is the default jvm option for remote debugging in Vespa?", "the default jvm option for remote debugging in Vespa?", "default jvm option for remote debugging in Vespa?", "jvm option for remote debugging in Vespa?", "option for remote debugging in Vespa?", "for remote debugging in Vespa?", "remote debugging in Vespa?", "debugging in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["What", "is", "the", "default", "jvm", "option", "for", "remote", "debugging", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::274296746", "fields": {"term": "How to restart Vespa in a node that runs the container?", "namespace": "open-p", "hash": 274296746, "terms": ["How to restart Vespa in a node that runs the container?", "to restart Vespa in a node that runs the container?", "restart Vespa in a node that runs the container?", "Vespa in a node that runs the container?", "in a node that runs the container?", "a node that runs the container?", "node that runs the container?", "that runs the container?", "runs the container?", "the container?", "container?"], "tokens": ["How", "to", "restart", "Vespa", "in", "a", "node", "that", "runs", "the", "container", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::928294163", "fields": {"term": "What is the Vespa IntelliJ plugin?", "namespace": "open-p", "hash": 928294163, "terms": ["What is the Vespa IntelliJ plugin?", "is the Vespa IntelliJ plugin?", "the Vespa IntelliJ plugin?", "Vespa IntelliJ plugin?", "IntelliJ plugin?", "plugin?"], "tokens": ["What", "is", "the", "Vespa", "IntelliJ", "plugin", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::-1572595484", "fields": {"term": "How can IntelliJ be simplified?", "namespace": "open-p", "hash": -1572595484, "terms": ["How can IntelliJ be simplified?", "can IntelliJ be simplified?", "IntelliJ be simplified?", "be simplified?", "simplified?"], "tokens": ["How", "can", "IntelliJ", "be", "simplified", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::919899461", "fields": {"term": "How do I delete a schema?", "namespace": "open-p", "hash": 919899461, "terms": ["How do I delete a schema?", "do I delete a schema?", "I delete a schema?", "delete a schema?", "a schema?", "schema?"], "tokens": ["How", "do", "I", "delete", "a", "schema", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-178130588", "fields": {"term": "What is the 'drop schema' command?", "namespace": "open-p", "hash": -178130588, "terms": ["What is the 'drop schema' command?", "is the 'drop schema' command?", "the 'drop schema' command?", "'drop schema' command?", "schema' command?", "command?"], "tokens": ["What", "is", "the", "'", "drop", "schema", "'", "command", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::232630654", "fields": {"term": "What is a custom request handler?", "namespace": "open-p", "hash": 232630654, "terms": ["What is a custom request handler?", "is a custom request handler?", "a custom request handler?", "custom request handler?", "request handler?", "handler?"], "tokens": ["What", "is", "a", "custom", "request", "handler", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::2047247025", "fields": {"term": "When is a custom request handler necessary?", "namespace": "open-p", "hash": 2047247025, "terms": ["When is a custom request handler necessary?", "is a custom request handler necessary?", "a custom request handler necessary?", "custom request handler necessary?", "request handler necessary?", "handler necessary?", "necessary?"], "tokens": ["When", "is", "a", "custom", "request", "handler", "necessary", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1686976103", "fields": {"term": "What are examples of when to use a custom request handler?", "namespace": "open-p", "hash": 1686976103, "terms": ["What are examples of when to use a custom request handler?", "are examples of when to use a custom request handler?", "examples of when to use a custom request handler?", "of when to use a custom request handler?", "when to use a custom request handler?", "to use a custom request handler?", "use a custom request handler?", "a custom request handler?", "custom request handler?", "request handler?", "handler?"], "tokens": ["What", "are", "examples", "of", "when", "to", "use", "a", "custom", "request", "handler", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1642895513", "fields": {"term": "What is a request handler?", "namespace": "open-p", "hash": 1642895513, "terms": ["What is a request handler?", "is a request handler?", "a request handler?", "request handler?", "handler?"], "tokens": ["What", "is", "a", "request", "handler", "?"], "corpus_count": 3, "document_count": 3}}, -{"put": "id:term:term::-1208008236", "fields": {"term": "How should I implement a request handler?", "namespace": "open-p", "hash": -1208008236, "terms": ["How should I implement a request handler?", "should I implement a request handler?", "I implement a request handler?", "implement a request handler?", "a request handler?", "request handler?", "handler?"], "tokens": ["How", "should", "I", "implement", "a", "request", "handler", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1323122664", "fields": {"term": "What is ThreadedHttpRequestHandler?", "namespace": "open-p", "hash": -1323122664, "terms": ["What is ThreadedHttpRequestHandler?", "is ThreadedHttpRequestHandler?", "ThreadedHttpRequestHandler?"], "tokens": ["What", "is", "ThreadedHttpRequestHandler", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-788852437", "fields": {"term": "What functionality is implemented by ThreadedHttpRequestHandler?", "namespace": "open-p", "hash": -788852437, "terms": ["What functionality is implemented by ThreadedHttpRequestHandler?", "functionality is implemented by ThreadedHttpRequestHandler?", "is implemented by ThreadedHttpRequestHandler?", "implemented by ThreadedHttpRequestHandler?", "by ThreadedHttpRequestHandler?", "ThreadedHttpRequestHandler?"], "tokens": ["What", "functionality", "is", "implemented", "by", "ThreadedHttpRequestHandler", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2100590454", "fields": {"term": "What is the execution model for ThreadedHttpRequestHandler?", "namespace": "open-p", "hash": 2100590454, "terms": ["What is the execution model for ThreadedHttpRequestHandler?", "is the execution model for ThreadedHttpRequestHandler?", "the execution model for ThreadedHttpRequestHandler?", "execution model for ThreadedHttpRequestHandler?", "model for ThreadedHttpRequestHandler?", "for ThreadedHttpRequestHandler?", "ThreadedHttpRequestHandler?"], "tokens": ["What", "is", "the", "execution", "model", "for", "ThreadedHttpRequestHandler", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::396622240", "fields": {"term": "What is the HTTP date header?", "namespace": "open-p", "hash": 396622240, "terms": ["What is the HTTP date header?", "is the HTTP date header?", "the HTTP date header?", "HTTP date header?", "date header?", "header?"], "tokens": ["What", "is", "the", "HTTP", "date", "header", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1158440640", "fields": {"term": "What is logged by ThreadedHttpRequestHandler in case of errors or timeouts?", "namespace": "open-p", "hash": 1158440640, "terms": ["What is logged by ThreadedHttpRequestHandler in case of errors or timeouts?", "is logged by ThreadedHttpRequestHandler in case of errors or timeouts?", "logged by ThreadedHttpRequestHandler in case of errors or timeouts?", "by ThreadedHttpRequestHandler in case of errors or timeouts?", "ThreadedHttpRequestHandler in case of errors or timeouts?", "in case of errors or timeouts?", "case of errors or timeouts?", "of errors or timeouts?", "errors or timeouts?", "or timeouts?", "timeouts?"], "tokens": ["What", "is", "logged", "by", "ThreadedHttpRequestHandler", "in", "case", "of", "errors", "or", "timeouts", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2144181349", "fields": {"term": "What is the SiteHandler?", "namespace": "open-p", "hash": -2144181349, "terms": ["What is the SiteHandler?", "is the SiteHandler?", "the SiteHandler?", "SiteHandler?"], "tokens": ["What", "is", "the", "SiteHandler", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::190963131", "fields": {"term": "What is the DemoHandler?", "namespace": "open-p", "hash": 190963131, "terms": ["What is the DemoHandler?", "is the DemoHandler?", "the DemoHandler?", "DemoHandler?"], "tokens": ["What", "is", "the", "DemoHandler", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::749450197", "fields": {"term": "How to deploy a request handler?", "namespace": "open-p", "hash": 749450197, "terms": ["How to deploy a request handler?", "to deploy a request handler?", "deploy a request handler?", "a request handler?", "request handler?", "handler?"], "tokens": ["How", "to", "deploy", "a", "request", "handler", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1045518980", "fields": {"term": "What is the handler element?", "namespace": "open-p", "hash": 1045518980, "terms": ["What is the handler element?", "is the handler element?", "the handler element?", "handler element?", "element?"], "tokens": ["What", "is", "the", "handler", "element", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::403790319", "fields": {"term": "What is the binding element?", "namespace": "open-p", "hash": 403790319, "terms": ["What is the binding element?", "is the binding element?", "the binding element?", "binding element?", "element?"], "tokens": ["What", "is", "the", "binding", "element", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2066831065", "fields": {"term": "How to bind a request handler?", "namespace": "open-p", "hash": 2066831065, "terms": ["How to bind a request handler?", "to bind a request handler?", "bind a request handler?", "a request handler?", "request handler?", "handler?"], "tokens": ["How", "to", "bind", "a", "request", "handler", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1551709302", "fields": {"term": "What is the id attribute for handler?", "namespace": "open-p", "hash": -1551709302, "terms": ["What is the id attribute for handler?", "is the id attribute for handler?", "the id attribute for handler?", "id attribute for handler?", "attribute for handler?", "for handler?", "handler?"], "tokens": ["What", "is", "the", "id", "attribute", "for", "handler", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1270856424", "fields": {"term": "What is the bundle attribute for handler?", "namespace": "open-p", "hash": 1270856424, "terms": ["What is the bundle attribute for handler?", "is the bundle attribute for handler?", "the bundle attribute for handler?", "bundle attribute for handler?", "attribute for handler?", "for handler?", "handler?"], "tokens": ["What", "is", "the", "bundle", "attribute", "for", "handler", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1003876888", "fields": {"term": "What is the binding element used for?", "namespace": "open-p", "hash": 1003876888, "terms": ["What is the binding element used for?", "is the binding element used for?", "the binding element used for?", "binding element used for?", "element used for?", "used for?", "for?"], "tokens": ["What", "is", "the", "binding", "element", "used", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1754862295", "fields": {"term": "What is ServerProvider?", "namespace": "open-p", "hash": -1754862295, "terms": ["What is ServerProvider?", "is ServerProvider?", "ServerProvider?"], "tokens": ["What", "is", "ServerProvider", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::390758390", "fields": {"term": "What requests are created by server providers?", "namespace": "open-p", "hash": 390758390, "terms": ["What requests are created by server providers?", "requests are created by server providers?", "are created by server providers?", "created by server providers?", "by server providers?", "server providers?", "providers?"], "tokens": ["What", "requests", "are", "created", "by", "server", "providers", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-102461986", "fields": {"term": "What is the purpose of a server provider?", "namespace": "open-p", "hash": -102461986, "terms": ["What is the purpose of a server provider?", "is the purpose of a server provider?", "the purpose of a server provider?", "purpose of a server provider?", "of a server provider?", "a server provider?", "server provider?", "provider?"], "tokens": ["What", "is", "the", "purpose", "of", "a", "server", "provider", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-83147508", "fields": {"term": "How to implement a custom server provider?", "namespace": "open-p", "hash": -83147508, "terms": ["How to implement a custom server provider?", "to implement a custom server provider?", "implement a custom server provider?", "a custom server provider?", "custom server provider?", "server provider?", "provider?"], "tokens": ["How", "to", "implement", "a", "custom", "server", "provider", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2026948372", "fields": {"term": "How to respond back to the client?", "namespace": "open-p", "hash": -2026948372, "terms": ["How to respond back to the client?", "to respond back to the client?", "respond back to the client?", "back to the client?", "to the client?", "the client?", "client?"], "tokens": ["How", "to", "respond", "back", "to", "the", "client", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::406460640", "fields": {"term": "What is the difference between a server request and a client request?", "namespace": "open-p", "hash": 406460640, "terms": ["What is the difference between a server request and a client request?", "is the difference between a server request and a client request?", "the difference between a server request and a client request?", "difference between a server request and a client request?", "between a server request and a client request?", "a server request and a client request?", "server request and a client request?", "request and a client request?", "and a client request?", "a client request?", "client request?", "request?"], "tokens": ["What", "is", "the", "difference", "between", "a", "server", "request", "and", "a", "client", "request", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1138106531", "fields": {"term": "How to install a server provider in a container?", "namespace": "open-p", "hash": -1138106531, "terms": ["How to install a server provider in a container?", "to install a server provider in a container?", "install a server provider in a container?", "a server provider in a container?", "server provider in a container?", "provider in a container?", "in a container?", "a container?", "container?"], "tokens": ["How", "to", "install", "a", "server", "provider", "in", "a", "container", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1239038135", "fields": {"term": "What is a Fine-grained API?", "namespace": "open-p", "hash": 1239038135, "terms": ["What is a Fine-grained API?", "is a Fine-grained API?", "a Fine-grained API?", "Fine-grained API?", "API?"], "tokens": ["What", "is", "a", "Fine", "grained", "API", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1749333279", "fields": {"term": "What is a Coarse-grained API?", "namespace": "open-p", "hash": -1749333279, "terms": ["What is a Coarse-grained API?", "is a Coarse-grained API?", "a Coarse-grained API?", "Coarse-grained API?", "API?"], "tokens": ["What", "is", "a", "Coarse", "grained", "API", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2141489424", "fields": {"term": "How does the container help with coarse-grained APIs?", "namespace": "open-p", "hash": 2141489424, "terms": ["How does the container help with coarse-grained APIs?", "does the container help with coarse-grained APIs?", "the container help with coarse-grained APIs?", "container help with coarse-grained APIs?", "help with coarse-grained APIs?", "with coarse-grained APIs?", "coarse-grained APIs?", "APIs?"], "tokens": ["How", "does", "the", "container", "help", "with", "coarse", "grained", "APIs", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::941410922", "fields": {"term": "How can a specific chain of processors be invoked?", "namespace": "open-p", "hash": 941410922, "terms": ["How can a specific chain of processors be invoked?", "can a specific chain of processors be invoked?", "a specific chain of processors be invoked?", "specific chain of processors be invoked?", "chain of processors be invoked?", "of processors be invoked?", "processors be invoked?", "be invoked?", "invoked?"], "tokens": ["How", "can", "a", "specific", "chain", "of", "processors", "be", "invoked", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-382925843", "fields": {"term": "What is the purpose of the generic mechanism allowing request handlers to be bound to a URI pattern?", "namespace": "open-p", "hash": -382925843, "terms": ["What is the purpose of the generic mechanism allowing request handlers to be bound to a URI pattern?", "is the purpose of the generic mechanism allowing request handlers to be bound to a URI pattern?", "the purpose of the generic mechanism allowing request handlers to be bound to a URI pattern?", "purpose of the generic mechanism allowing request handlers to be bound to a URI pattern?", "of the generic mechanism allowing request handlers to be bound to a URI pattern?", "the generic mechanism allowing request handlers to be bound to a URI pattern?", "generic mechanism allowing request handlers to be bound to a URI pattern?", "mechanism allowing request handlers to be bound to a URI pattern?", "allowing request handlers to be bound to a URI pattern?", "request handlers to be bound to a URI pattern?", "handlers to be bound to a URI pattern?", "to be bound to a URI pattern?", "be bound to a URI pattern?", "bound to a URI pattern?", "to a URI pattern?", "a URI pattern?", "URI pattern?", "pattern?"], "tokens": ["What", "is", "the", "purpose", "of", "the", "generic", "mechanism", "allowing", "request", "handlers", "to", "be", "bound", "to", "a", "URI", "pattern", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2095760638", "fields": {"term": "What are the types of web service APIs?", "namespace": "open-p", "hash": 2095760638, "terms": ["What are the types of web service APIs?", "are the types of web service APIs?", "the types of web service APIs?", "types of web service APIs?", "of web service APIs?", "web service APIs?", "service APIs?", "APIs?"], "tokens": ["What", "are", "the", "types", "of", "web", "service", "APIs", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1315008542", "fields": {"term": "What is a HttpRequest?", "namespace": "open-p", "hash": -1315008542, "terms": ["What is a HttpRequest?", "is a HttpRequest?", "a HttpRequest?", "HttpRequest?"], "tokens": ["What", "is", "a", "HttpRequest", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1072331773", "fields": {"term": "What are HTTP URL parameters?", "namespace": "open-p", "hash": -1072331773, "terms": ["What are HTTP URL parameters?", "are HTTP URL parameters?", "HTTP URL parameters?", "URL parameters?", "parameters?"], "tokens": ["What", "are", "HTTP", "URL", "parameters", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-265399305", "fields": {"term": "How can a Processor retrieve the entire HTTP request?", "namespace": "open-p", "hash": -265399305, "terms": ["How can a Processor retrieve the entire HTTP request?", "can a Processor retrieve the entire HTTP request?", "a Processor retrieve the entire HTTP request?", "Processor retrieve the entire HTTP request?", "retrieve the entire HTTP request?", "the entire HTTP request?", "entire HTTP request?", "HTTP request?", "request?"], "tokens": ["How", "can", "a", "Processor", "retrieve", "the", "entire", "HTTP", "request", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1541309898", "fields": {"term": "What sets return status in Processing?", "namespace": "open-p", "hash": 1541309898, "terms": ["What sets return status in Processing?", "sets return status in Processing?", "return status in Processing?", "status in Processing?", "in Processing?", "Processing?"], "tokens": ["What", "sets", "return", "status", "in", "Processing", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1730853717", "fields": {"term": "What is ResponseStatus in Processing?", "namespace": "open-p", "hash": -1730853717, "terms": ["What is ResponseStatus in Processing?", "is ResponseStatus in Processing?", "ResponseStatus in Processing?", "in Processing?", "Processing?"], "tokens": ["What", "is", "ResponseStatus", "in", "Processing", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1443787957", "fields": {"term": "How is status determined in Processing?", "namespace": "open-p", "hash": 1443787957, "terms": ["How is status determined in Processing?", "is status determined in Processing?", "status determined in Processing?", "determined in Processing?", "in Processing?", "Processing?"], "tokens": ["How", "is", "status", "determined", "in", "Processing", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-904852917", "fields": {"term": "What are response headers?", "namespace": "open-p", "hash": -904852917, "terms": ["What are response headers?", "are response headers?", "response headers?", "headers?"], "tokens": ["What", "are", "response", "headers", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::1509890729", "fields": {"term": "How are response headers added?", "namespace": "open-p", "hash": 1509890729, "terms": ["How are response headers added?", "are response headers added?", "response headers added?", "headers added?", "added?"], "tokens": ["How", "are", "response", "headers", "added", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2081410821", "fields": {"term": "What is ResponseHeaders?", "namespace": "open-p", "hash": 2081410821, "terms": ["What is ResponseHeaders?", "is ResponseHeaders?", "ResponseHeaders?"], "tokens": ["What", "is", "ResponseHeaders", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::-1833950817", "fields": {"term": "Can multiple ResponseHeaders be added?", "namespace": "open-p", "hash": -1833950817, "terms": ["Can multiple ResponseHeaders be added?", "multiple ResponseHeaders be added?", "ResponseHeaders be added?", "be added?", "added?"], "tokens": ["Can", "multiple", "ResponseHeaders", "be", "added", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1844868277", "fields": {"term": "What data type is ResponseHeaders?", "namespace": "open-p", "hash": 1844868277, "terms": ["What data type is ResponseHeaders?", "data type is ResponseHeaders?", "type is ResponseHeaders?", "is ResponseHeaders?", "ResponseHeaders?"], "tokens": ["What", "data", "type", "is", "ResponseHeaders", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::262136852", "fields": {"term": "Can request handlers manipulate headers?", "namespace": "open-p", "hash": 262136852, "terms": ["Can request handlers manipulate headers?", "request handlers manipulate headers?", "handlers manipulate headers?", "manipulate headers?", "headers?"], "tokens": ["Can", "request", "handlers", "manipulate", "headers", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::736595280", "fields": {"term": "What is HttpRequest used for?", "namespace": "open-p", "hash": 736595280, "terms": ["What is HttpRequest used for?", "is HttpRequest used for?", "HttpRequest used for?", "used for?", "for?"], "tokens": ["What", "is", "HttpRequest", "used", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1996311709", "fields": {"term": "What are extra bindings?", "namespace": "open-p", "hash": -1996311709, "terms": ["What are extra bindings?", "are extra bindings?", "extra bindings?", "bindings?"], "tokens": ["What", "are", "extra", "bindings", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-416077665", "fields": {"term": "How to add bindings?", "namespace": "open-p", "hash": -416077665, "terms": ["How to add bindings?", "to add bindings?", "add bindings?", "bindings?"], "tokens": ["How", "to", "add", "bindings", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1094224685", "fields": {"term": "What is ExecutionFactory?", "namespace": "open-p", "hash": -1094224685, "terms": ["What is ExecutionFactory?", "is ExecutionFactory?", "ExecutionFactory?"], "tokens": ["What", "is", "ExecutionFactory", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1720917399", "fields": {"term": "What is Vespa query?", "namespace": "open-p", "hash": -1720917399, "terms": ["What is Vespa query?", "is Vespa query?", "Vespa query?", "query?"], "tokens": ["What", "is", "Vespa", "query", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-300827129", "fields": {"term": "What is the purpose of Execution in Vespa?", "namespace": "open-p", "hash": -300827129, "terms": ["What is the purpose of Execution in Vespa?", "is the purpose of Execution in Vespa?", "the purpose of Execution in Vespa?", "purpose of Execution in Vespa?", "of Execution in Vespa?", "Execution in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["What", "is", "the", "purpose", "of", "Execution", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::395169551", "fields": {"term": "What is ExecutionFactory API?", "namespace": "open-p", "hash": 395169551, "terms": ["What is ExecutionFactory API?", "is ExecutionFactory API?", "ExecutionFactory API?", "API?"], "tokens": ["What", "is", "ExecutionFactory", "API", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1526334906", "fields": {"term": "How is ExecutionFactory injected?", "namespace": "open-p", "hash": 1526334906, "terms": ["How is ExecutionFactory injected?", "is ExecutionFactory injected?", "ExecutionFactory injected?", "injected?"], "tokens": ["How", "is", "ExecutionFactory", "injected", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::724549385", "fields": {"term": "How to create new Execution in Vespa?", "namespace": "open-p", "hash": 724549385, "terms": ["How to create new Execution in Vespa?", "to create new Execution in Vespa?", "create new Execution in Vespa?", "new Execution in Vespa?", "Execution in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["How", "to", "create", "new", "Execution", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::816364131", "fields": {"term": "What is Vespa Docker?", "namespace": "open-p", "hash": 816364131, "terms": ["What is Vespa Docker?", "is Vespa Docker?", "Vespa Docker?", "Docker?"], "tokens": ["What", "is", "Vespa", "Docker", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1285130416", "fields": {"term": "How to tune Vespa containers?", "namespace": "open-p", "hash": 1285130416, "terms": ["How to tune Vespa containers?", "to tune Vespa containers?", "tune Vespa containers?", "Vespa containers?", "containers?"], "tokens": ["How", "to", "tune", "Vespa", "containers", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1320388184", "fields": {"term": "What are the adaptions for Vespa Docker?", "namespace": "open-p", "hash": 1320388184, "terms": ["What are the adaptions for Vespa Docker?", "are the adaptions for Vespa Docker?", "the adaptions for Vespa Docker?", "adaptions for Vespa Docker?", "for Vespa Docker?", "Vespa Docker?", "Docker?"], "tokens": ["What", "are", "the", "adaptions", "for", "Vespa", "Docker", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1672776488", "fields": {"term": "Can Vespa Docker be used in production?", "namespace": "open-p", "hash": -1672776488, "terms": ["Can Vespa Docker be used in production?", "Vespa Docker be used in production?", "Docker be used in production?", "be used in production?", "used in production?", "in production?", "production?"], "tokens": ["Can", "Vespa", "Docker", "be", "used", "in", "production", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::121062836", "fields": {"term": "How to use Vespa Docker on a laptop?", "namespace": "open-p", "hash": 121062836, "terms": ["How to use Vespa Docker on a laptop?", "to use Vespa Docker on a laptop?", "use Vespa Docker on a laptop?", "Vespa Docker on a laptop?", "Docker on a laptop?", "on a laptop?", "a laptop?", "laptop?"], "tokens": ["How", "to", "use", "Vespa", "Docker", "on", "a", "laptop", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1744468179", "fields": {"term": "How to run Vespa in Docker?", "namespace": "open-p", "hash": 1744468179, "terms": ["How to run Vespa in Docker?", "to run Vespa in Docker?", "run Vespa in Docker?", "Vespa in Docker?", "in Docker?", "Docker?"], "tokens": ["How", "to", "run", "Vespa", "in", "Docker", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1912669696", "fields": {"term": "What is the Vespa var path?", "namespace": "open-p", "hash": 1912669696, "terms": ["What is the Vespa var path?", "is the Vespa var path?", "the Vespa var path?", "Vespa var path?", "var path?", "path?"], "tokens": ["What", "is", "the", "Vespa", "var", "path", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::687899304", "fields": {"term": "What is vespa user?", "namespace": "open-p", "hash": 687899304, "terms": ["What is vespa user?", "is vespa user?", "vespa user?", "user?"], "tokens": ["What", "is", "vespa", "user", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1071441031", "fields": {"term": "What is VESPA_VAR_STORAGE?", "namespace": "open-p", "hash": -1071441031, "terms": ["What is VESPA_VAR_STORAGE?", "is VESPA_VAR_STORAGE?", "VESPA_VAR_STORAGE?"], "tokens": ["What", "is", "VESPA_VAR_STORAGE", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1164191060", "fields": {"term": "What is VESPA_LOG_STORAGE?", "namespace": "open-p", "hash": 1164191060, "terms": ["What is VESPA_LOG_STORAGE?", "is VESPA_LOG_STORAGE?", "VESPA_LOG_STORAGE?"], "tokens": ["What", "is", "VESPA_LOG_STORAGE", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::284507166", "fields": {"term": "What is the purpose of chown command?", "namespace": "open-p", "hash": 284507166, "terms": ["What is the purpose of chown command?", "is the purpose of chown command?", "the purpose of chown command?", "purpose of chown command?", "of chown command?", "chown command?", "command?"], "tokens": ["What", "is", "the", "purpose", "of", "chown", "command", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-192835562", "fields": {"term": "How to start Vespa container?", "namespace": "open-p", "hash": -192835562, "terms": ["How to start Vespa container?", "to start Vespa container?", "start Vespa container?", "Vespa container?", "container?"], "tokens": ["How", "to", "start", "Vespa", "container", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1770478279", "fields": {"term": "What is the purpose of start script?", "namespace": "open-p", "hash": -1770478279, "terms": ["What is the purpose of start script?", "is the purpose of start script?", "the purpose of start script?", "purpose of start script?", "of start script?", "start script?", "script?"], "tokens": ["What", "is", "the", "purpose", "of", "start", "script", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-521126997", "fields": {"term": "What are system limits for Vespa?", "namespace": "open-p", "hash": -521126997, "terms": ["What are system limits for Vespa?", "are system limits for Vespa?", "system limits for Vespa?", "limits for Vespa?", "for Vespa?", "Vespa?"], "tokens": ["What", "are", "system", "limits", "for", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1414386894", "fields": {"term": "How should Docker be started for Vespa?", "namespace": "open-p", "hash": -1414386894, "terms": ["How should Docker be started for Vespa?", "should Docker be started for Vespa?", "Docker be started for Vespa?", "be started for Vespa?", "started for Vespa?", "for Vespa?", "Vespa?"], "tokens": ["How", "should", "Docker", "be", "started", "for", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1867772466", "fields": {"term": "What is THP?", "namespace": "open-p", "hash": -1867772466, "terms": ["What is THP?", "is THP?", "THP?"], "tokens": ["What", "is", "THP", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1323544959", "fields": {"term": "How does THP improve Vespa performance?", "namespace": "open-p", "hash": 1323544959, "terms": ["How does THP improve Vespa performance?", "does THP improve Vespa performance?", "THP improve Vespa performance?", "improve Vespa performance?", "Vespa performance?", "performance?"], "tokens": ["How", "does", "THP", "improve", "Vespa", "performance", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-826061203", "fields": {"term": "What are the recommended THP settings for Vespa on Linux?", "namespace": "open-p", "hash": -826061203, "terms": ["What are the recommended THP settings for Vespa on Linux?", "are the recommended THP settings for Vespa on Linux?", "the recommended THP settings for Vespa on Linux?", "recommended THP settings for Vespa on Linux?", "THP settings for Vespa on Linux?", "settings for Vespa on Linux?", "for Vespa on Linux?", "Vespa on Linux?", "on Linux?", "Linux?"], "tokens": ["What", "are", "the", "recommended", "THP", "settings", "for", "Vespa", "on", "Linux", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::997221964", "fields": {"term": "How can I verify that THP is active?", "namespace": "open-p", "hash": 997221964, "terms": ["How can I verify that THP is active?", "can I verify that THP is active?", "I verify that THP is active?", "verify that THP is active?", "that THP is active?", "THP is active?", "is active?", "active?"], "tokens": ["How", "can", "I", "verify", "that", "THP", "is", "active", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::916587238", "fields": {"term": "What is AnonHugePages used for in Vespa?", "namespace": "open-p", "hash": 916587238, "terms": ["What is AnonHugePages used for in Vespa?", "is AnonHugePages used for in Vespa?", "AnonHugePages used for in Vespa?", "used for in Vespa?", "for in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["What", "is", "AnonHugePages", "used", "for", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1588870007", "fields": {"term": "What process in Vespa uses MADV_HUGEPAGE?", "namespace": "open-p", "hash": -1588870007, "terms": ["What process in Vespa uses MADV_HUGEPAGE?", "process in Vespa uses MADV_HUGEPAGE?", "in Vespa uses MADV_HUGEPAGE?", "Vespa uses MADV_HUGEPAGE?", "uses MADV_HUGEPAGE?", "MADV_HUGEPAGE?"], "tokens": ["What", "process", "in", "Vespa", "uses", "MADV_HUGEPAGE", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-464375998", "fields": {"term": "Why is THP more important in virtualized environments?", "namespace": "open-p", "hash": -464375998, "terms": ["Why is THP more important in virtualized environments?", "is THP more important in virtualized environments?", "THP more important in virtualized environments?", "more important in virtualized environments?", "important in virtualized environments?", "in virtualized environments?", "virtualized environments?", "environments?"], "tokens": ["Why", "is", "THP", "more", "important", "in", "virtualized", "environments", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1095978791", "fields": {"term": "What was the query p99 latency improvement when using THP in Vespa?", "namespace": "open-p", "hash": -1095978791, "terms": ["What was the query p99 latency improvement when using THP in Vespa?", "was the query p99 latency improvement when using THP in Vespa?", "the query p99 latency improvement when using THP in Vespa?", "query p99 latency improvement when using THP in Vespa?", "p99 latency improvement when using THP in Vespa?", "latency improvement when using THP in Vespa?", "improvement when using THP in Vespa?", "when using THP in Vespa?", "using THP in Vespa?", "THP in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["What", "was", "the", "query", "p99", "latency", "improvement", "when", "using", "THP", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-884808628", "fields": {"term": "What is the purpose of start-container.sh script?", "namespace": "open-p", "hash": -884808628, "terms": ["What is the purpose of start-container.sh script?", "is the purpose of start-container.sh script?", "the purpose of start-container.sh script?", "purpose of start-container.sh script?", "of start-container.sh script?", "start-container.sh script?", "script?"], "tokens": ["What", "is", "the", "purpose", "of", "start", "container", "sh", "script", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1101763694", "fields": {"term": "Which services can be started inside the container?", "namespace": "open-p", "hash": 1101763694, "terms": ["Which services can be started inside the container?", "services can be started inside the container?", "can be started inside the container?", "be started inside the container?", "started inside the container?", "inside the container?", "the container?", "container?"], "tokens": ["Which", "services", "can", "be", "started", "inside", "the", "container", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::690926009", "fields": {"term": "How do you start a configserver container?", "namespace": "open-p", "hash": 690926009, "terms": ["How do you start a configserver container?", "do you start a configserver container?", "you start a configserver container?", "start a configserver container?", "a configserver container?", "configserver container?", "container?"], "tokens": ["How", "do", "you", "start", "a", "configserver", "container", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::452800457", "fields": {"term": "How do you start a services container?", "namespace": "open-p", "hash": 452800457, "terms": ["How do you start a services container?", "do you start a services container?", "you start a services container?", "start a services container?", "a services container?", "services container?", "container?"], "tokens": ["How", "do", "you", "start", "a", "services", "container", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::204709092", "fields": {"term": "How do you start a container with both configserver and service?", "namespace": "open-p", "hash": 204709092, "terms": ["How do you start a container with both configserver and service?", "do you start a container with both configserver and service?", "you start a container with both configserver and service?", "start a container with both configserver and service?", "a container with both configserver and service?", "container with both configserver and service?", "with both configserver and service?", "both configserver and service?", "configserver and service?", "and service?", "service?"], "tokens": ["How", "do", "you", "start", "a", "container", "with", "both", "configserver", "and", "service", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2077251932", "fields": {"term": "What is the VESPA_CONFIGSERVERS environment variable used for?", "namespace": "open-p", "hash": -2077251932, "terms": ["What is the VESPA_CONFIGSERVERS environment variable used for?", "is the VESPA_CONFIGSERVERS environment variable used for?", "the VESPA_CONFIGSERVERS environment variable used for?", "VESPA_CONFIGSERVERS environment variable used for?", "environment variable used for?", "variable used for?", "used for?", "for?"], "tokens": ["What", "is", "the", "VESPA_CONFIGSERVERS", "environment", "variable", "used", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-23038975", "fields": {"term": "What happens if VESPA_CONFIGSERVERS environment variable is not set?", "namespace": "open-p", "hash": -23038975, "terms": ["What happens if VESPA_CONFIGSERVERS environment variable is not set?", "happens if VESPA_CONFIGSERVERS environment variable is not set?", "if VESPA_CONFIGSERVERS environment variable is not set?", "VESPA_CONFIGSERVERS environment variable is not set?", "environment variable is not set?", "variable is not set?", "is not set?", "not set?", "set?"], "tokens": ["What", "happens", "if", "VESPA_CONFIGSERVERS", "environment", "variable", "is", "not", "set", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1090914605", "fields": {"term": "Which sample application can be used as a blueprint for setting up config servers and services?", "namespace": "open-p", "hash": 1090914605, "terms": ["Which sample application can be used as a blueprint for setting up config servers and services?", "sample application can be used as a blueprint for setting up config servers and services?", "application can be used as a blueprint for setting up config servers and services?", "can be used as a blueprint for setting up config servers and services?", "be used as a blueprint for setting up config servers and services?", "used as a blueprint for setting up config servers and services?", "as a blueprint for setting up config servers and services?", "a blueprint for setting up config servers and services?", "blueprint for setting up config servers and services?", "for setting up config servers and services?", "setting up config servers and services?", "up config servers and services?", "config servers and services?", "servers and services?", "and services?", "services?"], "tokens": ["Which", "sample", "application", "can", "be", "used", "as", "a", "blueprint", "for", "setting", "up", "config", "servers", "and", "services", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1606829398", "fields": {"term": "What are the minimum memory requirements for Vespa Docker containers?", "namespace": "open-p", "hash": -1606829398, "terms": ["What are the minimum memory requirements for Vespa Docker containers?", "are the minimum memory requirements for Vespa Docker containers?", "the minimum memory requirements for Vespa Docker containers?", "minimum memory requirements for Vespa Docker containers?", "memory requirements for Vespa Docker containers?", "requirements for Vespa Docker containers?", "for Vespa Docker containers?", "Vespa Docker containers?", "Docker containers?", "containers?"], "tokens": ["What", "are", "the", "minimum", "memory", "requirements", "for", "Vespa", "Docker", "containers", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1878533608", "fields": {"term": "How can I track memory usage of Vespa Docker container?", "namespace": "open-p", "hash": 1878533608, "terms": ["How can I track memory usage of Vespa Docker container?", "can I track memory usage of Vespa Docker container?", "I track memory usage of Vespa Docker container?", "track memory usage of Vespa Docker container?", "memory usage of Vespa Docker container?", "usage of Vespa Docker container?", "of Vespa Docker container?", "Vespa Docker container?", "Docker container?", "container?"], "tokens": ["How", "can", "I", "track", "memory", "usage", "of", "Vespa", "Docker", "container", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1542886564", "fields": {"term": "What are the symptoms of errors due to insufficient memory during Vespa startup?", "namespace": "open-p", "hash": 1542886564, "terms": ["What are the symptoms of errors due to insufficient memory during Vespa startup?", "are the symptoms of errors due to insufficient memory during Vespa startup?", "the symptoms of errors due to insufficient memory during Vespa startup?", "symptoms of errors due to insufficient memory during Vespa startup?", "of errors due to insufficient memory during Vespa startup?", "errors due to insufficient memory during Vespa startup?", "due to insufficient memory during Vespa startup?", "to insufficient memory during Vespa startup?", "insufficient memory during Vespa startup?", "memory during Vespa startup?", "during Vespa startup?", "Vespa startup?", "startup?"], "tokens": ["What", "are", "the", "symptoms", "of", "errors", "due", "to", "insufficient", "memory", "during", "Vespa", "startup", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1416256643", "fields": {"term": "What is a common problem due to lack of memory when running Vespa config servers?", "namespace": "open-p", "hash": -1416256643, "terms": ["What is a common problem due to lack of memory when running Vespa config servers?", "is a common problem due to lack of memory when running Vespa config servers?", "a common problem due to lack of memory when running Vespa config servers?", "common problem due to lack of memory when running Vespa config servers?", "problem due to lack of memory when running Vespa config servers?", "due to lack of memory when running Vespa config servers?", "to lack of memory when running Vespa config servers?", "lack of memory when running Vespa config servers?", "of memory when running Vespa config servers?", "memory when running Vespa config servers?", "when running Vespa config servers?", "running Vespa config servers?", "Vespa config servers?", "config servers?", "servers?"], "tokens": ["What", "is", "a", "common", "problem", "due", "to", "lack", "of", "memory", "when", "running", "Vespa", "config", "servers", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1470797217", "fields": {"term": "Where can container memory settings be found in Vespa services.xml?", "namespace": "open-p", "hash": -1470797217, "terms": ["Where can container memory settings be found in Vespa services.xml?", "can container memory settings be found in Vespa services.xml?", "container memory settings be found in Vespa services.xml?", "memory settings be found in Vespa services.xml?", "settings be found in Vespa services.xml?", "be found in Vespa services.xml?", "found in Vespa services.xml?", "in Vespa services.xml?", "Vespa services.xml?", "services.xml?"], "tokens": ["Where", "can", "container", "memory", "settings", "be", "found", "in", "Vespa", "services", "xml", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1460777025", "fields": {"term": "What is the significance of VESPA_CONFIGSERVER_JVMARGS in Vespa multinode-HA configuration?", "namespace": "open-p", "hash": -1460777025, "terms": ["What is the significance of VESPA_CONFIGSERVER_JVMARGS in Vespa multinode-HA configuration?", "is the significance of VESPA_CONFIGSERVER_JVMARGS in Vespa multinode-HA configuration?", "the significance of VESPA_CONFIGSERVER_JVMARGS in Vespa multinode-HA configuration?", "significance of VESPA_CONFIGSERVER_JVMARGS in Vespa multinode-HA configuration?", "of VESPA_CONFIGSERVER_JVMARGS in Vespa multinode-HA configuration?", "VESPA_CONFIGSERVER_JVMARGS in Vespa multinode-HA configuration?", "in Vespa multinode-HA configuration?", "Vespa multinode-HA configuration?", "multinode-HA configuration?", "configuration?"], "tokens": ["What", "is", "the", "significance", "of", "VESPA_CONFIGSERVER_JVMARGS", "in", "Vespa", "multinode", "HA", "configuration", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::907046035", "fields": {"term": "What should be done to ensure container memory settings match that of Docker container when running Vespa?", "namespace": "open-p", "hash": 907046035, "terms": ["What should be done to ensure container memory settings match that of Docker container when running Vespa?", "should be done to ensure container memory settings match that of Docker container when running Vespa?", "be done to ensure container memory settings match that of Docker container when running Vespa?", "done to ensure container memory settings match that of Docker container when running Vespa?", "to ensure container memory settings match that of Docker container when running Vespa?", "ensure container memory settings match that of Docker container when running Vespa?", "container memory settings match that of Docker container when running Vespa?", "memory settings match that of Docker container when running Vespa?", "settings match that of Docker container when running Vespa?", "match that of Docker container when running Vespa?", "that of Docker container when running Vespa?", "of Docker container when running Vespa?", "Docker container when running Vespa?", "container when running Vespa?", "when running Vespa?", "running Vespa?", "Vespa?"], "tokens": ["What", "should", "be", "done", "to", "ensure", "container", "memory", "settings", "match", "that", "of", "Docker", "container", "when", "running", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-534655414", "fields": {"term": "What is an example of Docker network use?", "namespace": "open-p", "hash": -534655414, "terms": ["What is an example of Docker network use?", "is an example of Docker network use?", "an example of Docker network use?", "example of Docker network use?", "of Docker network use?", "Docker network use?", "network use?", "use?"], "tokens": ["What", "is", "an", "example", "of", "Docker", "network", "use", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::611527803", "fields": {"term": "What is Vespa Document API?", "namespace": "open-p", "hash": 611527803, "terms": ["What is Vespa Document API?", "is Vespa Document API?", "Vespa Document API?", "Document API?", "API?"], "tokens": ["What", "is", "Vespa", "Document", "API", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1124784434", "fields": {"term": "How to build and compile Vespa clients?", "namespace": "open-p", "hash": -1124784434, "terms": ["How to build and compile Vespa clients?", "to build and compile Vespa clients?", "build and compile Vespa clients?", "and compile Vespa clients?", "compile Vespa clients?", "Vespa clients?", "clients?"], "tokens": ["How", "to", "build", "and", "compile", "Vespa", "clients", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-512774422", "fields": {"term": "What is the async API used for?", "namespace": "open-p", "hash": -512774422, "terms": ["What is the async API used for?", "is the async API used for?", "the async API used for?", "async API used for?", "API used for?", "used for?", "for?"], "tokens": ["What", "is", "the", "async", "API", "used", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2023296256", "fields": {"term": "What is the VESPA_CONFIG_SOURCES environment variable?", "namespace": "open-p", "hash": -2023296256, "terms": ["What is the VESPA_CONFIG_SOURCES environment variable?", "is the VESPA_CONFIG_SOURCES environment variable?", "the VESPA_CONFIG_SOURCES environment variable?", "VESPA_CONFIG_SOURCES environment variable?", "environment variable?", "variable?"], "tokens": ["What", "is", "the", "VESPA_CONFIG_SOURCES", "environment", "variable", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1888531147", "fields": {"term": "What consists of a Document?", "namespace": "open-p", "hash": -1888531147, "terms": ["What consists of a Document?", "consists of a Document?", "of a Document?", "a Document?", "Document?"], "tokens": ["What", "consists", "of", "a", "Document", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-250144174", "fields": {"term": "What is a DocumentType?", "namespace": "open-p", "hash": -250144174, "terms": ["What is a DocumentType?", "is a DocumentType?", "a DocumentType?", "DocumentType?"], "tokens": ["What", "is", "a", "DocumentType", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1281559273", "fields": {"term": "What is a DocumentId?", "namespace": "open-p", "hash": -1281559273, "terms": ["What is a DocumentId?", "is a DocumentId?", "a DocumentId?", "DocumentId?"], "tokens": ["What", "is", "a", "DocumentId", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::335355838", "fields": {"term": "What is a FieldUpdate?", "namespace": "open-p", "hash": 335355838, "terms": ["What is a FieldUpdate?", "is a FieldUpdate?", "a FieldUpdate?", "FieldUpdate?"], "tokens": ["What", "is", "a", "FieldUpdate", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1740195220", "fields": {"term": "What is SyncSession?", "namespace": "open-p", "hash": -1740195220, "terms": ["What is SyncSession?", "is SyncSession?", "SyncSession?"], "tokens": ["What", "is", "SyncSession", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::1492449388", "fields": {"term": "What is aByte?", "namespace": "open-p", "hash": 1492449388, "terms": ["What is aByte?", "is aByte?", "aByte?"], "tokens": ["What", "is", "aByte", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::9578448", "fields": {"term": "What is aDouble?", "namespace": "open-p", "hash": 9578448, "terms": ["What is aDouble?", "is aDouble?", "aDouble?"], "tokens": ["What", "is", "aDouble", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-809500818", "fields": {"term": "What is aWeightedset?", "namespace": "open-p", "hash": -809500818, "terms": ["What is aWeightedset?", "is aWeightedset?", "aWeightedset?"], "tokens": ["What", "is", "aWeightedset", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-574082745", "fields": {"term": "What is a document update?", "namespace": "open-p", "hash": -574082745, "terms": ["What is a document update?", "is a document update?", "a document update?", "document update?", "update?"], "tokens": ["What", "is", "a", "document", "update", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1057883752", "fields": {"term": "How are primitive fields updated?", "namespace": "open-p", "hash": 1057883752, "terms": ["How are primitive fields updated?", "are primitive fields updated?", "primitive fields updated?", "fields updated?", "updated?"], "tokens": ["How", "are", "primitive", "fields", "updated", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::802367648", "fields": {"term": "How are complex fields updated?", "namespace": "open-p", "hash": 802367648, "terms": ["How are complex fields updated?", "are complex fields updated?", "complex fields updated?", "fields updated?", "updated?"], "tokens": ["How", "are", "complex", "fields", "updated", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::896763420", "fields": {"term": "What field types support field path updates?", "namespace": "open-p", "hash": 896763420, "terms": ["What field types support field path updates?", "field types support field path updates?", "types support field path updates?", "support field path updates?", "field path updates?", "path updates?", "updates?"], "tokens": ["What", "field", "types", "support", "field", "path", "updates", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::868549064", "fields": {"term": "What are some examples of FieldUpdate?", "namespace": "open-p", "hash": 868549064, "terms": ["What are some examples of FieldUpdate?", "are some examples of FieldUpdate?", "some examples of FieldUpdate?", "examples of FieldUpdate?", "of FieldUpdate?", "FieldUpdate?"], "tokens": ["What", "are", "some", "examples", "of", "FieldUpdate", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1746385515", "fields": {"term": "What are some examples of FieldPathUpdate?", "namespace": "open-p", "hash": 1746385515, "terms": ["What are some examples of FieldPathUpdate?", "are some examples of FieldPathUpdate?", "some examples of FieldPathUpdate?", "examples of FieldPathUpdate?", "of FieldPathUpdate?", "FieldPathUpdate?"], "tokens": ["What", "are", "some", "examples", "of", "FieldPathUpdate", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1554244369", "fields": {"term": "What is DocumentAccess for?", "namespace": "open-p", "hash": 1554244369, "terms": ["What is DocumentAccess for?", "is DocumentAccess for?", "DocumentAccess for?", "for?"], "tokens": ["What", "is", "DocumentAccess", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1957545288", "fields": {"term": "What is AsyncSession?", "namespace": "open-p", "hash": 1957545288, "terms": ["What is AsyncSession?", "is AsyncSession?", "AsyncSession?"], "tokens": ["What", "is", "AsyncSession", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-638722371", "fields": {"term": "What is VisitorSession?", "namespace": "open-p", "hash": -638722371, "terms": ["What is VisitorSession?", "is VisitorSession?", "VisitorSession?"], "tokens": ["What", "is", "VisitorSession", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1485576605", "fields": {"term": "What is the purpose of DocumentAccess?", "namespace": "open-p", "hash": -1485576605, "terms": ["What is the purpose of DocumentAccess?", "is the purpose of DocumentAccess?", "the purpose of DocumentAccess?", "purpose of DocumentAccess?", "of DocumentAccess?", "DocumentAccess?"], "tokens": ["What", "is", "the", "purpose", "of", "DocumentAccess", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1805803349", "fields": {"term": "What are the access types provided by DocumentAccess?", "namespace": "open-p", "hash": -1805803349, "terms": ["What are the access types provided by DocumentAccess?", "are the access types provided by DocumentAccess?", "the access types provided by DocumentAccess?", "access types provided by DocumentAccess?", "types provided by DocumentAccess?", "provided by DocumentAccess?", "by DocumentAccess?", "DocumentAccess?"], "tokens": ["What", "are", "the", "access", "types", "provided", "by", "DocumentAccess", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::52392238", "fields": {"term": "What is the get() method for?", "namespace": "open-p", "hash": 52392238, "terms": ["What is the get() method for?", "is the get() method for?", "the get() method for?", "get() method for?", "method for?", "for?"], "tokens": ["What", "is", "the", "get", "()", "method", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2091753702", "fields": {"term": "What is the createXSession() method for?", "namespace": "open-p", "hash": -2091753702, "terms": ["What is the createXSession() method for?", "is the createXSession() method for?", "the createXSession() method for?", "createXSession() method for?", "method for?", "for?"], "tokens": ["What", "is", "the", "createXSession", "()", "method", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-734960982", "fields": {"term": "What is an asynchronous session?", "namespace": "open-p", "hash": -734960982, "terms": ["What is an asynchronous session?", "is an asynchronous session?", "an asynchronous session?", "asynchronous session?", "session?"], "tokens": ["What", "is", "an", "asynchronous", "session", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-181415941", "fields": {"term": "What is the purpose of waitForPending()?", "namespace": "open-p", "hash": -181415941, "terms": ["What is the purpose of waitForPending()?", "is the purpose of waitForPending()?", "the purpose of waitForPending()?", "purpose of waitForPending()?", "of waitForPending()?", "waitForPending()?"], "tokens": ["What", "is", "the", "purpose", "of", "waitForPending", "()?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-820252086", "fields": {"term": "What is a VisitorSession?", "namespace": "open-p", "hash": -820252086, "terms": ["What is a VisitorSession?", "is a VisitorSession?", "a VisitorSession?", "VisitorSession?"], "tokens": ["What", "is", "a", "VisitorSession", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-26579926", "fields": {"term": "What is a visitor target?", "namespace": "open-p", "hash": -26579926, "terms": ["What is a visitor target?", "is a visitor target?", "a visitor target?", "visitor target?", "target?"], "tokens": ["What", "is", "a", "visitor", "target", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1066871655", "fields": {"term": "What is VisitorControlHandler?", "namespace": "open-p", "hash": 1066871655, "terms": ["What is VisitorControlHandler?", "is VisitorControlHandler?", "VisitorControlHandler?"], "tokens": ["What", "is", "VisitorControlHandler", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-410363252", "fields": {"term": "What is VisitorDataHandler?", "namespace": "open-p", "hash": -410363252, "terms": ["What is VisitorDataHandler?", "is VisitorDataHandler?", "VisitorDataHandler?"], "tokens": ["What", "is", "VisitorDataHandler", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1642639314", "fields": {"term": "What is VisitorParameters?", "namespace": "open-p", "hash": 1642639314, "terms": ["What is VisitorParameters?", "is VisitorParameters?", "VisitorParameters?"], "tokens": ["What", "is", "VisitorParameters", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1312443656", "fields": {"term": "What is the default VisitorDataHandler?", "namespace": "open-p", "hash": -1312443656, "terms": ["What is the default VisitorDataHandler?", "is the default VisitorDataHandler?", "the default VisitorDataHandler?", "default VisitorDataHandler?", "VisitorDataHandler?"], "tokens": ["What", "is", "the", "default", "VisitorDataHandler", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::860951115", "fields": {"term": "How to implement custom VisitorControlHandler?", "namespace": "open-p", "hash": 860951115, "terms": ["How to implement custom VisitorControlHandler?", "to implement custom VisitorControlHandler?", "implement custom VisitorControlHandler?", "custom VisitorControlHandler?", "VisitorControlHandler?"], "tokens": ["How", "to", "implement", "custom", "VisitorControlHandler", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1932265648", "fields": {"term": "How to improve visiting performance?", "namespace": "open-p", "hash": 1932265648, "terms": ["How to improve visiting performance?", "to improve visiting performance?", "improve visiting performance?", "visiting performance?", "performance?"], "tokens": ["How", "to", "improve", "visiting", "performance", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1828557083", "fields": {"term": "What is field path syntax?", "namespace": "open-p", "hash": 1828557083, "terms": ["What is field path syntax?", "is field path syntax?", "field path syntax?", "path syntax?", "syntax?"], "tokens": ["What", "is", "field", "path", "syntax", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::317240296", "fields": {"term": "How is field path syntax used?", "namespace": "open-p", "hash": 317240296, "terms": ["How is field path syntax used?", "is field path syntax used?", "field path syntax used?", "path syntax used?", "syntax used?", "used?"], "tokens": ["How", "is", "field", "path", "syntax", "used", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::441681375", "fields": {"term": "What is an example of field path syntax?", "namespace": "open-p", "hash": 441681375, "terms": ["What is an example of field path syntax?", "is an example of field path syntax?", "an example of field path syntax?", "example of field path syntax?", "of field path syntax?", "field path syntax?", "path syntax?", "syntax?"], "tokens": ["What", "is", "an", "example", "of", "field", "path", "syntax", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1021111615", "fields": {"term": "What field types can use field path syntax?", "namespace": "open-p", "hash": 1021111615, "terms": ["What field types can use field path syntax?", "field types can use field path syntax?", "types can use field path syntax?", "can use field path syntax?", "use field path syntax?", "field path syntax?", "path syntax?", "syntax?"], "tokens": ["What", "field", "types", "can", "use", "field", "path", "syntax", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1964178248", "fields": {"term": "Can field path syntax be combined?", "namespace": "open-p", "hash": -1964178248, "terms": ["Can field path syntax be combined?", "field path syntax be combined?", "path syntax be combined?", "syntax be combined?", "be combined?", "combined?"], "tokens": ["Can", "field", "path", "syntax", "be", "combined", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1843599254", "fields": {"term": "How does field path syntax traverse documents?", "namespace": "open-p", "hash": 1843599254, "terms": ["How does field path syntax traverse documents?", "does field path syntax traverse documents?", "field path syntax traverse documents?", "path syntax traverse documents?", "syntax traverse documents?", "traverse documents?", "documents?"], "tokens": ["How", "does", "field", "path", "syntax", "traverse", "documents", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1403957881", "fields": {"term": "What is the syntax for retrieving a specific key in a mapfield?", "namespace": "open-p", "hash": -1403957881, "terms": ["What is the syntax for retrieving a specific key in a mapfield?", "is the syntax for retrieving a specific key in a mapfield?", "the syntax for retrieving a specific key in a mapfield?", "syntax for retrieving a specific key in a mapfield?", "for retrieving a specific key in a mapfield?", "retrieving a specific key in a mapfield?", "a specific key in a mapfield?", "specific key in a mapfield?", "key in a mapfield?", "in a mapfield?", "a mapfield?", "mapfield?"], "tokens": ["What", "is", "the", "syntax", "for", "retrieving", "a", "specific", "key", "in", "a", "mapfield", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1429273488", "fields": {"term": "What does {$} retrieve?", "namespace": "open-p", "hash": 1429273488, "terms": ["What does {$} retrieve?", "does {$} retrieve?", "{$} retrieve?", "retrieve?"], "tokens": ["What", "does", "<", "mapfield", ">{$<", "variablename", ">}", "retrieve", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2129307399", "fields": {"term": "How do I retrieve all the values associated with a mapfield?", "namespace": "open-p", "hash": 2129307399, "terms": ["How do I retrieve all the values associated with a mapfield?", "do I retrieve all the values associated with a mapfield?", "I retrieve all the values associated with a mapfield?", "retrieve all the values associated with a mapfield?", "all the values associated with a mapfield?", "the values associated with a mapfield?", "values associated with a mapfield?", "associated with a mapfield?", "with a mapfield?", "a mapfield?", "mapfield?"], "tokens": ["How", "do", "I", "retrieve", "all", "the", "values", "associated", "with", "a", "mapfield", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-651209312", "fields": {"term": "What does .key retrieve?", "namespace": "open-p", "hash": -651209312, "terms": ["What does .key retrieve?", "does .key retrieve?", ".key retrieve?", "retrieve?"], "tokens": ["What", "does", "<", "mapfield", ">", "key", "retrieve", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::948615307", "fields": {"term": "What does .value retrieve?", "namespace": "open-p", "hash": 948615307, "terms": ["What does .value retrieve?", "does .value retrieve?", ".value retrieve?", "retrieve?"], "tokens": ["What", "does", "<", "mapfield", ">", "value", "retrieve", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::66431739", "fields": {"term": "What does retrieve?", "namespace": "open-p", "hash": 66431739, "terms": ["What does retrieve?", "does retrieve?", " retrieve?", "retrieve?"], "tokens": ["What", "does", "<", "mapfield", ">", "retrieve", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::998936237", "fields": {"term": "What is a weighted set?", "namespace": "open-p", "hash": 998936237, "terms": ["What is a weighted set?", "is a weighted set?", "a weighted set?", "weighted set?", "set?"], "tokens": ["What", "is", "a", "weighted", "set", "?"], "corpus_count": 3, "document_count": 3}}, -{"put": "id:term:term::138027913", "fields": {"term": "What is referred to as the value in a weighted set?", "namespace": "open-p", "hash": 138027913, "terms": ["What is referred to as the value in a weighted set?", "is referred to as the value in a weighted set?", "referred to as the value in a weighted set?", "to as the value in a weighted set?", "as the value in a weighted set?", "the value in a weighted set?", "value in a weighted set?", "in a weighted set?", "a weighted set?", "weighted set?", "set?"], "tokens": ["What", "is", "referred", "to", "as", "the", "value", "in", "a", "weighted", "set", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::520222586", "fields": {"term": "What is the syntax for retrieving a value at a specific index in an array?", "namespace": "open-p", "hash": 520222586, "terms": ["What is the syntax for retrieving a value at a specific index in an array?", "is the syntax for retrieving a value at a specific index in an array?", "the syntax for retrieving a value at a specific index in an array?", "syntax for retrieving a value at a specific index in an array?", "for retrieving a value at a specific index in an array?", "retrieving a value at a specific index in an array?", "a value at a specific index in an array?", "value at a specific index in an array?", "at a specific index in an array?", "a specific index in an array?", "specific index in an array?", "index in an array?", "in an array?", "an array?", "array?"], "tokens": ["What", "is", "the", "syntax", "for", "retrieving", "a", "value", "at", "a", "specific", "index", "in", "an", "array", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::530082355", "fields": {"term": "How do you retrieve all values in an array?", "namespace": "open-p", "hash": 530082355, "terms": ["How do you retrieve all values in an array?", "do you retrieve all values in an array?", "you retrieve all values in an array?", "retrieve all values in an array?", "all values in an array?", "values in an array?", "in an array?", "an array?", "array?"], "tokens": ["How", "do", "you", "retrieve", "all", "values", "in", "an", "array", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2083055997", "fields": {"term": "What is the syntax for retrieving all values in an array with variable names?", "namespace": "open-p", "hash": 2083055997, "terms": ["What is the syntax for retrieving all values in an array with variable names?", "is the syntax for retrieving all values in an array with variable names?", "the syntax for retrieving all values in an array with variable names?", "syntax for retrieving all values in an array with variable names?", "for retrieving all values in an array with variable names?", "retrieving all values in an array with variable names?", "all values in an array with variable names?", "values in an array with variable names?", "in an array with variable names?", "an array with variable names?", "array with variable names?", "with variable names?", "variable names?", "names?"], "tokens": ["What", "is", "the", "syntax", "for", "retrieving", "all", "values", "in", "an", "array", "with", "variable", "names", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-906202852", "fields": {"term": "What does do?", "namespace": "open-p", "hash": -906202852, "terms": ["What does do?", "does do?", " do?", "do?"], "tokens": ["What", "does", "<", "structfield", ">", "do", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2145820301", "fields": {"term": "What subfields can be returned?", "namespace": "open-p", "hash": -2145820301, "terms": ["What subfields can be returned?", "subfields can be returned?", "can be returned?", "be returned?", "returned?"], "tokens": ["What", "subfields", "can", "be", "returned", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1017470315", "fields": {"term": "What does this document describe?", "namespace": "open-p", "hash": -1017470315, "terms": ["What does this document describe?", "does this document describe?", "this document describe?", "document describe?", "describe?"], "tokens": ["What", "does", "this", "document", "describe", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::321747959", "fields": {"term": "What is the JSON format used?", "namespace": "open-p", "hash": 321747959, "terms": ["What is the JSON format used?", "is the JSON format used?", "the JSON format used?", "JSON format used?", "format used?", "used?"], "tokens": ["What", "is", "the", "JSON", "format", "used", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-429535521", "fields": {"term": "What are the JSON representations for document operations?", "namespace": "open-p", "hash": -429535521, "terms": ["What are the JSON representations for document operations?", "are the JSON representations for document operations?", "the JSON representations for document operations?", "JSON representations for document operations?", "representations for document operations?", "for document operations?", "document operations?", "operations?"], "tokens": ["What", "are", "the", "JSON", "representations", "for", "document", "operations", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-124474515", "fields": {"term": "What is the JSON representation for addressing fields?", "namespace": "open-p", "hash": -124474515, "terms": ["What is the JSON representation for addressing fields?", "is the JSON representation for addressing fields?", "the JSON representation for addressing fields?", "JSON representation for addressing fields?", "representation for addressing fields?", "for addressing fields?", "addressing fields?", "fields?"], "tokens": ["What", "is", "the", "JSON", "representation", "for", "addressing", "fields", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-25289978", "fields": {"term": "What is the troubleshooting guide for encoding?", "namespace": "open-p", "hash": -25289978, "terms": ["What is the troubleshooting guide for encoding?", "is the troubleshooting guide for encoding?", "the troubleshooting guide for encoding?", "troubleshooting guide for encoding?", "guide for encoding?", "for encoding?", "encoding?"], "tokens": ["What", "is", "the", "troubleshooting", "guide", "for", "encoding", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1754487019", "fields": {"term": "What is a raw field?", "namespace": "open-p", "hash": -1754487019, "terms": ["What is a raw field?", "is a raw field?", "a raw field?", "raw field?", "field?"], "tokens": ["What", "is", "a", "raw", "field", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1575925941", "fields": {"term": "How are weighted sets represented?", "namespace": "open-p", "hash": -1575925941, "terms": ["How are weighted sets represented?", "are weighted sets represented?", "weighted sets represented?", "sets represented?", "represented?"], "tokens": ["How", "are", "weighted", "sets", "represented", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1563900576", "fields": {"term": "How are arrays represented?", "namespace": "open-p", "hash": 1563900576, "terms": ["How are arrays represented?", "are arrays represented?", "arrays represented?", "represented?"], "tokens": ["How", "are", "arrays", "represented", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-865659422", "fields": {"term": "What is a URI field?", "namespace": "open-p", "hash": -865659422, "terms": ["What is a URI field?", "is a URI field?", "a URI field?", "URI field?", "field?"], "tokens": ["What", "is", "a", "URI", "field", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1889702247", "fields": {"term": "What is ignored during feeding?", "namespace": "open-p", "hash": 1889702247, "terms": ["What is ignored during feeding?", "is ignored during feeding?", "ignored during feeding?", "during feeding?", "feeding?"], "tokens": ["What", "is", "ignored", "during", "feeding", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-384796759", "fields": {"term": "What is an empty field?", "namespace": "open-p", "hash": -384796759, "terms": ["What is an empty field?", "is an empty field?", "an empty field?", "empty field?", "field?"], "tokens": ["What", "is", "an", "empty", "field", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-51242724", "fields": {"term": "What are the two methods for document operations?", "namespace": "open-p", "hash": -51242724, "terms": ["What are the two methods for document operations?", "are the two methods for document operations?", "the two methods for document operations?", "two methods for document operations?", "methods for document operations?", "for document operations?", "document operations?", "operations?"], "tokens": ["What", "are", "the", "two", "methods", "for", "document", "operations", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-265824839", "fields": {"term": "What does /document/v1/ accept?", "namespace": "open-p", "hash": -265824839, "terms": ["What does /document/v1/ accept?", "does /document/v1/ accept?", "/document/v1/ accept?", "accept?"], "tokens": ["What", "does", "/", "document", "/", "v1", "/", "accept", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1168862739", "fields": {"term": "What is the PUT request for Vespa HTTP/feed client?", "namespace": "open-p", "hash": 1168862739, "terms": ["What is the PUT request for Vespa HTTP/feed client?", "is the PUT request for Vespa HTTP/feed client?", "the PUT request for Vespa HTTP/feed client?", "PUT request for Vespa HTTP/feed client?", "request for Vespa HTTP/feed client?", "for Vespa HTTP/feed client?", "Vespa HTTP/feed client?", "HTTP/feed client?", "client?"], "tokens": ["What", "is", "the", "PUT", "request", "for", "Vespa", "HTTP", "/", "feed", "client", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2100211387", "fields": {"term": "What is the PUT request for /document/v1/?", "namespace": "open-p", "hash": 2100211387, "terms": ["What is the PUT request for /document/v1/?", "is the PUT request for /document/v1/?", "the PUT request for /document/v1/?", "PUT request for /document/v1/?", "request for /document/v1/?", "for /document/v1/?", "/document/v1/?"], "tokens": ["What", "is", "the", "PUT", "request", "for", "/", "document", "/", "v1", "/?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2102833096", "fields": {"term": "What is a test and set condition?", "namespace": "open-p", "hash": -2102833096, "terms": ["What is a test and set condition?", "is a test and set condition?", "a test and set condition?", "test and set condition?", "and set condition?", "set condition?", "condition?"], "tokens": ["What", "is", "a", "test", "and", "set", "condition", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-209917896", "fields": {"term": "What is a condition?", "namespace": "open-p", "hash": -209917896, "terms": ["What is a condition?", "is a condition?", "a condition?", "condition?"], "tokens": ["What", "is", "a", "condition", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::337445877", "fields": {"term": "What is the response when the condition is not met?", "namespace": "open-p", "hash": 337445877, "terms": ["What is the response when the condition is not met?", "is the response when the condition is not met?", "the response when the condition is not met?", "response when the condition is not met?", "when the condition is not met?", "the condition is not met?", "condition is not met?", "is not met?", "not met?", "met?"], "tokens": ["What", "is", "the", "response", "when", "the", "condition", "is", "not", "met", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1076859399", "fields": {"term": "What is the 'assign' command used for?", "namespace": "open-p", "hash": 1076859399, "terms": ["What is the 'assign' command used for?", "is the 'assign' command used for?", "the 'assign' command used for?", "'assign' command used for?", "command used for?", "used for?", "for?"], "tokens": ["What", "is", "the", "'", "assign", "'", "command", "used", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-50966813", "fields": {"term": "Can 'assign' replace values in a collection?", "namespace": "open-p", "hash": -50966813, "terms": ["Can 'assign' replace values in a collection?", "'assign' replace values in a collection?", "replace values in a collection?", "values in a collection?", "in a collection?", "a collection?", "collection?"], "tokens": ["Can", "'", "assign", "'", "replace", "values", "in", "a", "collection", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1158671598", "fields": {"term": "What is the syntax for 'assign'?", "namespace": "open-p", "hash": -1158671598, "terms": ["What is the syntax for 'assign'?", "is the syntax for 'assign'?", "the syntax for 'assign'?", "syntax for 'assign'?", "for 'assign'?", "'assign'?"], "tokens": ["What", "is", "the", "syntax", "for", "'", "assign", "'?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1700146513", "fields": {"term": "Is the syntax for 'assign' the same as 'put'?", "namespace": "open-p", "hash": 1700146513, "terms": ["Is the syntax for 'assign' the same as 'put'?", "the syntax for 'assign' the same as 'put'?", "syntax for 'assign' the same as 'put'?", "for 'assign' the same as 'put'?", "'assign' the same as 'put'?", "the same as 'put'?", "same as 'put'?", "as 'put'?", "'put'?"], "tokens": ["Is", "the", "syntax", "for", "'", "assign", "'", "the", "same", "as", "'", "put", "'?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-157677712", "fields": {"term": "How does 'assign' work with fields?", "namespace": "open-p", "hash": -157677712, "terms": ["How does 'assign' work with fields?", "does 'assign' work with fields?", "'assign' work with fields?", "work with fields?", "with fields?", "fields?"], "tokens": ["How", "does", "'", "assign", "'", "work", "with", "fields", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1925084279", "fields": {"term": "How does 'assign' work with collections?", "namespace": "open-p", "hash": -1925084279, "terms": ["How does 'assign' work with collections?", "does 'assign' work with collections?", "'assign' work with collections?", "work with collections?", "with collections?", "collections?"], "tokens": ["How", "does", "'", "assign", "'", "work", "with", "collections", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2052777122", "fields": {"term": "Does 'assign' create new values or replace existing ones?", "namespace": "open-p", "hash": 2052777122, "terms": ["Does 'assign' create new values or replace existing ones?", "'assign' create new values or replace existing ones?", "create new values or replace existing ones?", "new values or replace existing ones?", "values or replace existing ones?", "or replace existing ones?", "replace existing ones?", "existing ones?", "ones?"], "tokens": ["Does", "'", "assign", "'", "create", "new", "values", "or", "replace", "existing", "ones", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1285182692", "fields": {"term": "Is 'assign' a fast operation?", "namespace": "open-p", "hash": 1285182692, "terms": ["Is 'assign' a fast operation?", "'assign' a fast operation?", "a fast operation?", "fast operation?", "operation?"], "tokens": ["Is", "'", "assign", "'", "a", "fast", "operation", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-185715377", "fields": {"term": "What is being assigned to title field?", "namespace": "open-p", "hash": -185715377, "terms": ["What is being assigned to title field?", "is being assigned to title field?", "being assigned to title field?", "assigned to title field?", "to title field?", "title field?", "field?"], "tokens": ["What", "is", "being", "assigned", "to", "title", "field", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-920949271", "fields": {"term": "What is the value of cell at address (x=a,y=b)?", "namespace": "open-p", "hash": -920949271, "terms": ["What is the value of cell at address (x=a,y=b)?", "is the value of cell at address (x=a,y=b)?", "the value of cell at address (x=a,y=b)?", "value of cell at address (x=a,y=b)?", "of cell at address (x=a,y=b)?", "cell at address (x=a,y=b)?", "at address (x=a,y=b)?", "address (x=a,y=b)?", "(x=a,y=b)?"], "tokens": ["What", "is", "the", "value", "of", "cell", "at", "address", "(", "x", "=", "a", "y", "=", "b", ")?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2012210272", "fields": {"term": "What is a person struct?", "namespace": "open-p", "hash": -2012210272, "terms": ["What is a person struct?", "is a person struct?", "a person struct?", "person struct?", "struct?"], "tokens": ["What", "is", "a", "person", "struct", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1302184741", "fields": {"term": "What is 'contact' in this example?", "namespace": "open-p", "hash": -1302184741, "terms": ["What is 'contact' in this example?", "is 'contact' in this example?", "'contact' in this example?", "in this example?", "this example?", "example?"], "tokens": ["What", "is", "'", "contact", "'", "in", "this", "example", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1082366234", "fields": {"term": "What does assigning a contact do?", "namespace": "open-p", "hash": -1082366234, "terms": ["What does assigning a contact do?", "does assigning a contact do?", "assigning a contact do?", "a contact do?", "contact do?", "do?"], "tokens": ["What", "does", "assigning", "a", "contact", "do", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::181132950", "fields": {"term": "What is the field path syntax?", "namespace": "open-p", "hash": 181132950, "terms": ["What is the field path syntax?", "is the field path syntax?", "the field path syntax?", "field path syntax?", "path syntax?", "syntax?"], "tokens": ["What", "is", "the", "field", "path", "syntax", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::1795206214", "fields": {"term": "How to update individual struct fields?", "namespace": "open-p", "hash": 1795206214, "terms": ["How to update individual struct fields?", "to update individual struct fields?", "update individual struct fields?", "individual struct fields?", "struct fields?", "fields?"], "tokens": ["How", "to", "update", "individual", "struct", "fields", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1230217960", "fields": {"term": "How to update map entries?", "namespace": "open-p", "hash": -1230217960, "terms": ["How to update map entries?", "to update map entries?", "update map entries?", "map entries?", "entries?"], "tokens": ["How", "to", "update", "map", "entries", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::5946829", "fields": {"term": "What is my_food_scores?", "namespace": "open-p", "hash": 5946829, "terms": ["What is my_food_scores?", "is my_food_scores?", "my_food_scores?"], "tokens": ["What", "is", "my_food_scores", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1202881530", "fields": {"term": "What is the content of my_food_score for Strawberries?", "namespace": "open-p", "hash": 1202881530, "terms": ["What is the content of my_food_score for Strawberries?", "is the content of my_food_score for Strawberries?", "the content of my_food_score for Strawberries?", "content of my_food_score for Strawberries?", "of my_food_score for Strawberries?", "my_food_score for Strawberries?", "for Strawberries?", "Strawberries?"], "tokens": ["What", "is", "the", "content", "of", "my_food_score", "for", "Strawberries", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1591872180", "fields": {"term": "What is the id for mynamespace:food::example?", "namespace": "open-p", "hash": 1591872180, "terms": ["What is the id for mynamespace:food::example?", "is the id for mynamespace:food::example?", "the id for mynamespace:food::example?", "id for mynamespace:food::example?", "for mynamespace:food::example?", "mynamespace:food::example?"], "tokens": ["What", "is", "the", "id", "for", "mynamespace", ":", "food", "::", "example", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1424810074", "fields": {"term": "What is the value for my_food_scores{Strawberries}?", "namespace": "open-p", "hash": 1424810074, "terms": ["What is the value for my_food_scores{Strawberries}?", "is the value for my_food_scores{Strawberries}?", "the value for my_food_scores{Strawberries}?", "value for my_food_scores{Strawberries}?", "for my_food_scores{Strawberries}?", "my_food_scores{Strawberries}?"], "tokens": ["What", "is", "the", "value", "for", "my_food_scores", "{", "Strawberries", "}?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-48760682", "fields": {"term": "What is the attribute type of contacts?", "namespace": "open-p", "hash": -48760682, "terms": ["What is the attribute type of contacts?", "is the attribute type of contacts?", "the attribute type of contacts?", "attribute type of contacts?", "type of contacts?", "of contacts?", "contacts?"], "tokens": ["What", "is", "the", "attribute", "type", "of", "contacts", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::517488257", "fields": {"term": "What is the field type of 'ingredients'?", "namespace": "open-p", "hash": 517488257, "terms": ["What is the field type of 'ingredients'?", "is the field type of 'ingredients'?", "the field type of 'ingredients'?", "field type of 'ingredients'?", "type of 'ingredients'?", "of 'ingredients'?", "'ingredients'?"], "tokens": ["What", "is", "the", "field", "type", "of", "'", "ingredients", "'?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1139295295", "fields": {"term": "What is the indexing for 'ingredients'?", "namespace": "open-p", "hash": 1139295295, "terms": ["What is the indexing for 'ingredients'?", "is the indexing for 'ingredients'?", "the indexing for 'ingredients'?", "indexing for 'ingredients'?", "for 'ingredients'?", "'ingredients'?"], "tokens": ["What", "is", "the", "indexing", "for", "'", "ingredients", "'?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1026583292", "fields": {"term": "How do you assign elements in arrays?", "namespace": "open-p", "hash": 1026583292, "terms": ["How do you assign elements in arrays?", "do you assign elements in arrays?", "you assign elements in arrays?", "assign elements in arrays?", "elements in arrays?", "in arrays?", "arrays?"], "tokens": ["How", "do", "you", "assign", "elements", "in", "arrays", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1013195472", "fields": {"term": "What is the syntax for matching elements in arrays?", "namespace": "open-p", "hash": -1013195472, "terms": ["What is the syntax for matching elements in arrays?", "is the syntax for matching elements in arrays?", "the syntax for matching elements in arrays?", "syntax for matching elements in arrays?", "for matching elements in arrays?", "matching elements in arrays?", "elements in arrays?", "in arrays?", "arrays?"], "tokens": ["What", "is", "the", "syntax", "for", "matching", "elements", "in", "arrays", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-859072313", "fields": {"term": "What is an array of structs?", "namespace": "open-p", "hash": -859072313, "terms": ["What is an array of structs?", "is an array of structs?", "an array of structs?", "array of structs?", "of structs?", "structs?"], "tokens": ["What", "is", "an", "array", "of", "structs", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-120357308", "fields": {"term": "How do you update an element in an array?", "namespace": "open-p", "hash": -120357308, "terms": ["How do you update an element in an array?", "do you update an element in an array?", "you update an element in an array?", "update an element in an array?", "an element in an array?", "element in an array?", "in an array?", "an array?", "array?"], "tokens": ["How", "do", "you", "update", "an", "element", "in", "an", "array", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::620511638", "fields": {"term": "What is the alternative syntax for updating elements in an array?", "namespace": "open-p", "hash": 620511638, "terms": ["What is the alternative syntax for updating elements in an array?", "is the alternative syntax for updating elements in an array?", "the alternative syntax for updating elements in an array?", "alternative syntax for updating elements in an array?", "syntax for updating elements in an array?", "for updating elements in an array?", "updating elements in an array?", "elements in an array?", "in an array?", "an array?", "array?"], "tokens": ["What", "is", "the", "alternative", "syntax", "for", "updating", "elements", "in", "an", "array", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1279157735", "fields": {"term": "What is the 'people' field?", "namespace": "open-p", "hash": 1279157735, "terms": ["What is the 'people' field?", "is the 'people' field?", "the 'people' field?", "'people' field?", "field?"], "tokens": ["What", "is", "the", "'", "people", "'", "field", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1623882098", "fields": {"term": "How do you add to a weighted set?", "namespace": "open-p", "hash": 1623882098, "terms": ["How do you add to a weighted set?", "do you add to a weighted set?", "you add to a weighted set?", "add to a weighted set?", "to a weighted set?", "a weighted set?", "weighted set?", "set?"], "tokens": ["How", "do", "you", "add", "to", "a", "weighted", "set", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-427098536", "fields": {"term": "What is more efficient than using field{key} syntax for weighted sets?", "namespace": "open-p", "hash": -427098536, "terms": ["What is more efficient than using field{key} syntax for weighted sets?", "is more efficient than using field{key} syntax for weighted sets?", "more efficient than using field{key} syntax for weighted sets?", "efficient than using field{key} syntax for weighted sets?", "than using field{key} syntax for weighted sets?", "using field{key} syntax for weighted sets?", "field{key} syntax for weighted sets?", "syntax for weighted sets?", "for weighted sets?", "weighted sets?", "sets?"], "tokens": ["What", "is", "more", "efficient", "than", "using", "field", "{", "key", "}", "syntax", "for", "weighted", "sets", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-428944288", "fields": {"term": "How to clear a field?", "namespace": "open-p", "hash": -428944288, "terms": ["How to clear a field?", "to clear a field?", "clear a field?", "a field?", "field?"], "tokens": ["How", "to", "clear", "a", "field", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::1587014377", "fields": {"term": "What value clears a field?", "namespace": "open-p", "hash": 1587014377, "terms": ["What value clears a field?", "value clears a field?", "clears a field?", "a field?", "field?"], "tokens": ["What", "value", "clears", "a", "field", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-531621921", "fields": {"term": "What can be added using add?", "namespace": "open-p", "hash": -531621921, "terms": ["What can be added using add?", "can be added using add?", "be added using add?", "added using add?", "using add?", "add?"], "tokens": ["What", "can", "be", "added", "using", "add", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1579984805", "fields": {"term": "What is the field type of 'tracks'?", "namespace": "open-p", "hash": -1579984805, "terms": ["What is the field type of 'tracks'?", "is the field type of 'tracks'?", "the field type of 'tracks'?", "field type of 'tracks'?", "type of 'tracks'?", "of 'tracks'?", "'tracks'?"], "tokens": ["What", "is", "the", "field", "type", "of", "'", "tracks", "'?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2121710473", "fields": {"term": "What is being added to 'tracks'?", "namespace": "open-p", "hash": -2121710473, "terms": ["What is being added to 'tracks'?", "is being added to 'tracks'?", "being added to 'tracks'?", "added to 'tracks'?", "to 'tracks'?", "'tracks'?"], "tokens": ["What", "is", "being", "added", "to", "'", "tracks", "'?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1300415771", "fields": {"term": "What is weighted set?", "namespace": "open-p", "hash": 1300415771, "terms": ["What is weighted set?", "is weighted set?", "weighted set?", "set?"], "tokens": ["What", "is", "weighted", "set", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1837987150", "fields": {"term": "How to add weighted set elements?", "namespace": "open-p", "hash": -1837987150, "terms": ["How to add weighted set elements?", "to add weighted set elements?", "add weighted set elements?", "weighted set elements?", "set elements?", "elements?"], "tokens": ["How", "to", "add", "weighted", "set", "elements", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-582492364", "fields": {"term": "Can a key/weight mapping be overwritten?", "namespace": "open-p", "hash": -582492364, "terms": ["Can a key/weight mapping be overwritten?", "a key/weight mapping be overwritten?", "key/weight mapping be overwritten?", "mapping be overwritten?", "be overwritten?", "overwritten?"], "tokens": ["Can", "a", "key", "/", "weight", "mapping", "be", "overwritten", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1666214502", "fields": {"term": "What is the format for tensor fields?", "namespace": "open-p", "hash": 1666214502, "terms": ["What is the format for tensor fields?", "is the format for tensor fields?", "the format for tensor fields?", "format for tensor fields?", "for tensor fields?", "tensor fields?", "fields?"], "tokens": ["What", "is", "the", "format", "for", "tensor", "fields", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-153767195", "fields": {"term": "How do you add cells to tensors?", "namespace": "open-p", "hash": -153767195, "terms": ["How do you add cells to tensors?", "do you add cells to tensors?", "you add cells to tensors?", "add cells to tensors?", "cells to tensors?", "to tensors?", "tensors?"], "tokens": ["How", "do", "you", "add", "cells", "to", "tensors", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::796943967", "fields": {"term": "What happens when you add a cell that already exists?", "namespace": "open-p", "hash": 796943967, "terms": ["What happens when you add a cell that already exists?", "happens when you add a cell that already exists?", "when you add a cell that already exists?", "you add a cell that already exists?", "add a cell that already exists?", "a cell that already exists?", "cell that already exists?", "that already exists?", "already exists?", "exists?"], "tokens": ["What", "happens", "when", "you", "add", "a", "cell", "that", "already", "exists", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1392862124", "fields": {"term": "What is the fully specified address required for adding cells?", "namespace": "open-p", "hash": -1392862124, "terms": ["What is the fully specified address required for adding cells?", "is the fully specified address required for adding cells?", "the fully specified address required for adding cells?", "fully specified address required for adding cells?", "specified address required for adding cells?", "address required for adding cells?", "required for adding cells?", "for adding cells?", "adding cells?", "cells?"], "tokens": ["What", "is", "the", "fully", "specified", "address", "required", "for", "adding", "cells", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-611235348", "fields": {"term": "What is the block short form for mixed tensors?", "namespace": "open-p", "hash": -611235348, "terms": ["What is the block short form for mixed tensors?", "is the block short form for mixed tensors?", "the block short form for mixed tensors?", "block short form for mixed tensors?", "short form for mixed tensors?", "form for mixed tensors?", "for mixed tensors?", "mixed tensors?", "tensors?"], "tokens": ["What", "is", "the", "block", "short", "form", "for", "mixed", "tensors", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2087579669", "fields": {"term": "What is the default value for cells with bound indexed dimensions not specified?", "namespace": "open-p", "hash": -2087579669, "terms": ["What is the default value for cells with bound indexed dimensions not specified?", "is the default value for cells with bound indexed dimensions not specified?", "the default value for cells with bound indexed dimensions not specified?", "default value for cells with bound indexed dimensions not specified?", "value for cells with bound indexed dimensions not specified?", "for cells with bound indexed dimensions not specified?", "cells with bound indexed dimensions not specified?", "with bound indexed dimensions not specified?", "bound indexed dimensions not specified?", "indexed dimensions not specified?", "dimensions not specified?", "not specified?", "specified?"], "tokens": ["What", "is", "the", "default", "value", "for", "cells", "with", "bound", "indexed", "dimensions", "not", "specified", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1105073793", "fields": {"term": "What is the URL for more examples of adding cells?", "namespace": "open-p", "hash": -1105073793, "terms": ["What is the URL for more examples of adding cells?", "is the URL for more examples of adding cells?", "the URL for more examples of adding cells?", "URL for more examples of adding cells?", "for more examples of adding cells?", "more examples of adding cells?", "examples of adding cells?", "of adding cells?", "adding cells?", "cells?"], "tokens": ["What", "is", "the", "URL", "for", "more", "examples", "of", "adding", "cells", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1548630237", "fields": {"term": "What is field string_weighted_set?", "namespace": "open-p", "hash": -1548630237, "terms": ["What is field string_weighted_set?", "is field string_weighted_set?", "field string_weighted_set?", "string_weighted_set?"], "tokens": ["What", "is", "field", "string_weighted_set", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-887248059", "fields": {"term": "What is weightedset?", "namespace": "open-p", "hash": -887248059, "terms": ["What is weightedset?", "is weightedset?", "weightedset?"], "tokens": ["What", "is", "weightedset", "<", "string", ">?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1272251210", "fields": {"term": "What is tensorfield?", "namespace": "open-p", "hash": -1272251210, "terms": ["What is tensorfield?", "is tensorfield?", "tensorfield?"], "tokens": ["What", "is", "tensorfield", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-629648187", "fields": {"term": "What is tensor cell removal?", "namespace": "open-p", "hash": -629648187, "terms": ["What is tensor cell removal?", "is tensor cell removal?", "tensor cell removal?", "cell removal?", "removal?"], "tokens": ["What", "is", "tensor", "cell", "removal", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1994014137", "fields": {"term": "What is the format of tensorfield?", "namespace": "open-p", "hash": -1994014137, "terms": ["What is the format of tensorfield?", "is the format of tensorfield?", "the format of tensorfield?", "format of tensorfield?", "of tensorfield?", "tensorfield?"], "tokens": ["What", "is", "the", "format", "of", "tensorfield", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1740031945", "fields": {"term": "How to remove cells from tensorfield?", "namespace": "open-p", "hash": 1740031945, "terms": ["How to remove cells from tensorfield?", "to remove cells from tensorfield?", "remove cells from tensorfield?", "cells from tensorfield?", "from tensorfield?", "tensorfield?"], "tokens": ["How", "to", "remove", "cells", "from", "tensorfield", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-954502462", "fields": {"term": "What are the four arithmetic operators used?", "namespace": "open-p", "hash": -954502462, "terms": ["What are the four arithmetic operators used?", "are the four arithmetic operators used?", "the four arithmetic operators used?", "four arithmetic operators used?", "arithmetic operators used?", "operators used?", "used?"], "tokens": ["What", "are", "the", "four", "arithmetic", "operators", "used", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1795392894", "fields": {"term": "How can you modify single value numeric values?", "namespace": "open-p", "hash": -1795392894, "terms": ["How can you modify single value numeric values?", "can you modify single value numeric values?", "you modify single value numeric values?", "modify single value numeric values?", "single value numeric values?", "value numeric values?", "numeric values?", "values?"], "tokens": ["How", "can", "you", "modify", "single", "value", "numeric", "values", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-615011570", "fields": {"term": "What is the syntax to define int data type for sales field?", "namespace": "open-p", "hash": -615011570, "terms": ["What is the syntax to define int data type for sales field?", "is the syntax to define int data type for sales field?", "the syntax to define int data type for sales field?", "syntax to define int data type for sales field?", "to define int data type for sales field?", "define int data type for sales field?", "int data type for sales field?", "data type for sales field?", "type for sales field?", "for sales field?", "sales field?", "field?"], "tokens": ["What", "is", "the", "syntax", "to", "define", "int", "data", "type", "for", "sales", "field", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::490295786", "fields": {"term": "What is the purpose of the 'increment' operator?", "namespace": "open-p", "hash": 490295786, "terms": ["What is the purpose of the 'increment' operator?", "is the purpose of the 'increment' operator?", "the purpose of the 'increment' operator?", "purpose of the 'increment' operator?", "of the 'increment' operator?", "the 'increment' operator?", "'increment' operator?", "operator?"], "tokens": ["What", "is", "the", "purpose", "of", "the", "'", "increment", "'", "operator", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::615256374", "fields": {"term": "What is a sample update using the increment operator?", "namespace": "open-p", "hash": 615256374, "terms": ["What is a sample update using the increment operator?", "is a sample update using the increment operator?", "a sample update using the increment operator?", "sample update using the increment operator?", "update using the increment operator?", "using the increment operator?", "the increment operator?", "increment operator?", "operator?"], "tokens": ["What", "is", "a", "sample", "update", "using", "the", "increment", "operator", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1514994555", "fields": {"term": "What is the match operation?", "namespace": "open-p", "hash": 1514994555, "terms": ["What is the match operation?", "is the match operation?", "the match operation?", "match operation?", "operation?"], "tokens": ["What", "is", "the", "match", "operation", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::28657213", "fields": {"term": "How to increment a weightedset ?", "namespace": "open-p", "hash": 28657213, "terms": ["How to increment a weightedset ?", "to increment a weightedset ?", "increment a weightedset ?", "a weightedset ?", "weightedset ?", "?"], "tokens": ["How", "to", "increment", "a", "weightedset", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-896617504", "fields": {"term": "What is Fieldpath?", "namespace": "open-p", "hash": -896617504, "terms": ["What is Fieldpath?", "is Fieldpath?", "Fieldpath?"], "tokens": ["What", "is", "Fieldpath", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1137103321", "fields": {"term": "How to access map elements?", "namespace": "open-p", "hash": -1137103321, "terms": ["How to access map elements?", "to access map elements?", "access map elements?", "map elements?", "elements?"], "tokens": ["How", "to", "access", "map", "elements", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1417319836", "fields": {"term": "How to access array elements?", "namespace": "open-p", "hash": 1417319836, "terms": ["How to access array elements?", "to access array elements?", "access array elements?", "array elements?", "elements?"], "tokens": ["How", "to", "access", "array", "elements", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1981578565", "fields": {"term": "What is the syntax to access struct elements?", "namespace": "open-p", "hash": 1981578565, "terms": ["What is the syntax to access struct elements?", "is the syntax to access struct elements?", "the syntax to access struct elements?", "syntax to access struct elements?", "to access struct elements?", "access struct elements?", "struct elements?", "elements?"], "tokens": ["What", "is", "the", "syntax", "to", "access", "struct", "elements", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-787867267", "fields": {"term": "What structures can be accessed with Fieldpath?", "namespace": "open-p", "hash": -787867267, "terms": ["What structures can be accessed with Fieldpath?", "structures can be accessed with Fieldpath?", "can be accessed with Fieldpath?", "be accessed with Fieldpath?", "accessed with Fieldpath?", "with Fieldpath?", "Fieldpath?"], "tokens": ["What", "structures", "can", "be", "accessed", "with", "Fieldpath", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1757863503", "fields": {"term": "What does Fieldpath add to field values?", "namespace": "open-p", "hash": -1757863503, "terms": ["What does Fieldpath add to field values?", "does Fieldpath add to field values?", "Fieldpath add to field values?", "add to field values?", "to field values?", "field values?", "values?"], "tokens": ["What", "does", "Fieldpath", "add", "to", "field", "values", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::692582831", "fields": {"term": "Does Fieldpath work for index mode?", "namespace": "open-p", "hash": 692582831, "terms": ["Does Fieldpath work for index mode?", "Fieldpath work for index mode?", "work for index mode?", "for index mode?", "index mode?", "mode?"], "tokens": ["Does", "Fieldpath", "work", "for", "index", "mode", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-728317584", "fields": {"term": "Can nested structures be accessed with Fieldpath?", "namespace": "open-p", "hash": -728317584, "terms": ["Can nested structures be accessed with Fieldpath?", "nested structures be accessed with Fieldpath?", "structures be accessed with Fieldpath?", "be accessed with Fieldpath?", "accessed with Fieldpath?", "with Fieldpath?", "Fieldpath?"], "tokens": ["Can", "nested", "structures", "be", "accessed", "with", "Fieldpath", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::72920367", "fields": {"term": "What is document processing?", "namespace": "open-p", "hash": 72920367, "terms": ["What is document processing?", "is document processing?", "document processing?", "processing?"], "tokens": ["What", "is", "document", "processing", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::-1028063277", "fields": {"term": "What are document processors?", "namespace": "open-p", "hash": -1028063277, "terms": ["What are document processors?", "are document processors?", "document processors?", "processors?"], "tokens": ["What", "are", "document", "processors", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1901955352", "fields": {"term": "What is the motivation for document processing?", "namespace": "open-p", "hash": -1901955352, "terms": ["What is the motivation for document processing?", "is the motivation for document processing?", "the motivation for document processing?", "motivation for document processing?", "for document processing?", "document processing?", "processing?"], "tokens": ["What", "is", "the", "motivation", "for", "document", "processing", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1188801437", "fields": {"term": "How do you add a document processor?", "namespace": "open-p", "hash": 1188801437, "terms": ["How do you add a document processor?", "do you add a document processor?", "you add a document processor?", "add a document processor?", "a document processor?", "document processor?", "processor?"], "tokens": ["How", "do", "you", "add", "a", "document", "processor", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-373740517", "fields": {"term": "What is a general docproc chain?", "namespace": "open-p", "hash": -373740517, "terms": ["What is a general docproc chain?", "is a general docproc chain?", "a general docproc chain?", "general docproc chain?", "docproc chain?", "chain?"], "tokens": ["What", "is", "a", "general", "docproc", "chain", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1507804295", "fields": {"term": "What is the default indexing chain?", "namespace": "open-p", "hash": 1507804295, "terms": ["What is the default indexing chain?", "is the default indexing chain?", "the default indexing chain?", "default indexing chain?", "indexing chain?", "chain?"], "tokens": ["What", "is", "the", "default", "indexing", "chain", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-620493970", "fields": {"term": "What is a route to run through a chain?", "namespace": "open-p", "hash": -620493970, "terms": ["What is a route to run through a chain?", "is a route to run through a chain?", "a route to run through a chain?", "route to run through a chain?", "to run through a chain?", "run through a chain?", "through a chain?", "a chain?", "chain?"], "tokens": ["What", "is", "a", "route", "to", "run", "through", "a", "chain", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::339974518", "fields": {"term": "What is the purpose of adding a document processor to a chain?", "namespace": "open-p", "hash": 339974518, "terms": ["What is the purpose of adding a document processor to a chain?", "is the purpose of adding a document processor to a chain?", "the purpose of adding a document processor to a chain?", "purpose of adding a document processor to a chain?", "of adding a document processor to a chain?", "adding a document processor to a chain?", "a document processor to a chain?", "document processor to a chain?", "processor to a chain?", "to a chain?", "a chain?", "chain?"], "tokens": ["What", "is", "the", "purpose", "of", "adding", "a", "document", "processor", "to", "a", "chain", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-843106936", "fields": {"term": "What is the purpose of running a chain in indexing?", "namespace": "open-p", "hash": -843106936, "terms": ["What is the purpose of running a chain in indexing?", "is the purpose of running a chain in indexing?", "the purpose of running a chain in indexing?", "purpose of running a chain in indexing?", "of running a chain in indexing?", "running a chain in indexing?", "a chain in indexing?", "chain in indexing?", "in indexing?", "indexing?"], "tokens": ["What", "is", "the", "purpose", "of", "running", "a", "chain", "in", "indexing", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::944672030", "fields": {"term": "What is com.yahoo.docproc.DocumentProcessor?", "namespace": "open-p", "hash": 944672030, "terms": ["What is com.yahoo.docproc.DocumentProcessor?", "is com.yahoo.docproc.DocumentProcessor?", "com.yahoo.docproc.DocumentProcessor?"], "tokens": ["What", "is", "com", "yahoo", "docproc", "DocumentProcessor", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1174311213", "fields": {"term": "What is the process() method used for?", "namespace": "open-p", "hash": 1174311213, "terms": ["What is the process() method used for?", "is the process() method used for?", "the process() method used for?", "process() method used for?", "method used for?", "used for?", "for?"], "tokens": ["What", "is", "the", "process", "()", "method", "used", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1144683803", "fields": {"term": "What is Progress?", "namespace": "open-p", "hash": 1144683803, "terms": ["What is Progress?", "is Progress?", "Progress?"], "tokens": ["What", "is", "Progress", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1730296563", "fields": {"term": "What is Progress.DONE used for?", "namespace": "open-p", "hash": -1730296563, "terms": ["What is Progress.DONE used for?", "is Progress.DONE used for?", "Progress.DONE used for?", "used for?", "for?"], "tokens": ["What", "is", "Progress", "DONE", "used", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1501252232", "fields": {"term": "What is Progress.LATER used for?", "namespace": "open-p", "hash": 1501252232, "terms": ["What is Progress.LATER used for?", "is Progress.LATER used for?", "Progress.LATER used for?", "used for?", "for?"], "tokens": ["What", "is", "Progress", "LATER", "used", "for", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::-758601530", "fields": {"term": "What is a document processor chain?", "namespace": "open-p", "hash": -758601530, "terms": ["What is a document processor chain?", "is a document processor chain?", "a document processor chain?", "document processor chain?", "processor chain?", "chain?"], "tokens": ["What", "is", "a", "document", "processor", "chain", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::458818273", "fields": {"term": "What is a call stack?", "namespace": "open-p", "hash": 458818273, "terms": ["What is a call stack?", "is a call stack?", "a call stack?", "call stack?", "stack?"], "tokens": ["What", "is", "a", "call", "stack", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::679049338", "fields": {"term": "What is the purpose of a document processor chain?", "namespace": "open-p", "hash": 679049338, "terms": ["What is the purpose of a document processor chain?", "is the purpose of a document processor chain?", "the purpose of a document processor chain?", "purpose of a document processor chain?", "of a document processor chain?", "a document processor chain?", "document processor chain?", "processor chain?", "chain?"], "tokens": ["What", "is", "the", "purpose", "of", "a", "document", "processor", "chain", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::914952278", "fields": {"term": "How are document processor chains executed?", "namespace": "open-p", "hash": 914952278, "terms": ["How are document processor chains executed?", "are document processor chains executed?", "document processor chains executed?", "processor chains executed?", "chains executed?", "executed?"], "tokens": ["How", "are", "document", "processor", "chains", "executed", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1313671", "fields": {"term": "How does the Document Processing Framework work?", "namespace": "open-p", "hash": -1313671, "terms": ["How does the Document Processing Framework work?", "does the Document Processing Framework work?", "the Document Processing Framework work?", "Document Processing Framework work?", "Processing Framework work?", "Framework work?", "work?"], "tokens": ["How", "does", "the", "Document", "Processing", "Framework", "work", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1391165147", "fields": {"term": "What is the priority queue used for?", "namespace": "open-p", "hash": 1391165147, "terms": ["What is the priority queue used for?", "is the priority queue used for?", "the priority queue used for?", "priority queue used for?", "queue used for?", "used for?", "for?"], "tokens": ["What", "is", "the", "priority", "queue", "used", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::608022852", "fields": {"term": "How are messages processed?", "namespace": "open-p", "hash": 608022852, "terms": ["How are messages processed?", "are messages processed?", "messages processed?", "processed?"], "tokens": ["How", "are", "messages", "processed", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1091389820", "fields": {"term": "Are there multiple instances of each document processor?", "namespace": "open-p", "hash": -1091389820, "terms": ["Are there multiple instances of each document processor?", "there multiple instances of each document processor?", "multiple instances of each document processor?", "instances of each document processor?", "of each document processor?", "each document processor?", "document processor?", "processor?"], "tokens": ["Are", "there", "multiple", "instances", "of", "each", "document", "processor", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-268153999", "fields": {"term": "Are instance variables safe in document processor classes?", "namespace": "open-p", "hash": -268153999, "terms": ["Are instance variables safe in document processor classes?", "instance variables safe in document processor classes?", "variables safe in document processor classes?", "safe in document processor classes?", "in document processor classes?", "document processor classes?", "processor classes?", "classes?"], "tokens": ["Are", "instance", "variables", "safe", "in", "document", "processor", "classes", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1791726236", "fields": {"term": "What is the constraint on document processor classes?", "namespace": "open-p", "hash": 1791726236, "terms": ["What is the constraint on document processor classes?", "is the constraint on document processor classes?", "the constraint on document processor classes?", "constraint on document processor classes?", "on document processor classes?", "document processor classes?", "processor classes?", "classes?"], "tokens": ["What", "is", "the", "constraint", "on", "document", "processor", "classes", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1485154059", "fields": {"term": "What is the recommended resource management approach?", "namespace": "open-p", "hash": 1485154059, "terms": ["What is the recommended resource management approach?", "is the recommended resource management approach?", "the recommended resource management approach?", "recommended resource management approach?", "resource management approach?", "management approach?", "approach?"], "tokens": ["What", "is", "the", "recommended", "resource", "management", "approach", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::497764356", "fields": {"term": "How many worker threads execute document processors concurrently?", "namespace": "open-p", "hash": 497764356, "terms": ["How many worker threads execute document processors concurrently?", "many worker threads execute document processors concurrently?", "worker threads execute document processors concurrently?", "threads execute document processors concurrently?", "execute document processors concurrently?", "document processors concurrently?", "processors concurrently?", "concurrently?"], "tokens": ["How", "many", "worker", "threads", "execute", "document", "processors", "concurrently", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2097671104", "fields": {"term": "What are the restrictions in executing a document processor?", "namespace": "open-p", "hash": -2097671104, "terms": ["What are the restrictions in executing a document processor?", "are the restrictions in executing a document processor?", "the restrictions in executing a document processor?", "restrictions in executing a document processor?", "in executing a document processor?", "executing a document processor?", "a document processor?", "document processor?", "processor?"], "tokens": ["What", "are", "the", "restrictions", "in", "executing", "a", "document", "processor", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::448965067", "fields": {"term": "How can you fix a bottleneck when all worker threads block on an external resource?", "namespace": "open-p", "hash": 448965067, "terms": ["How can you fix a bottleneck when all worker threads block on an external resource?", "can you fix a bottleneck when all worker threads block on an external resource?", "you fix a bottleneck when all worker threads block on an external resource?", "fix a bottleneck when all worker threads block on an external resource?", "a bottleneck when all worker threads block on an external resource?", "bottleneck when all worker threads block on an external resource?", "when all worker threads block on an external resource?", "all worker threads block on an external resource?", "worker threads block on an external resource?", "threads block on an external resource?", "block on an external resource?", "on an external resource?", "an external resource?", "external resource?", "resource?"], "tokens": ["How", "can", "you", "fix", "a", "bottleneck", "when", "all", "worker", "threads", "block", "on", "an", "external", "resource", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1745692089", "fields": {"term": "What is the pattern to use for a document processor doing high latency operations?", "namespace": "open-p", "hash": -1745692089, "terms": ["What is the pattern to use for a document processor doing high latency operations?", "is the pattern to use for a document processor doing high latency operations?", "the pattern to use for a document processor doing high latency operations?", "pattern to use for a document processor doing high latency operations?", "to use for a document processor doing high latency operations?", "use for a document processor doing high latency operations?", "for a document processor doing high latency operations?", "a document processor doing high latency operations?", "document processor doing high latency operations?", "processor doing high latency operations?", "doing high latency operations?", "high latency operations?", "latency operations?", "operations?"], "tokens": ["What", "is", "the", "pattern", "to", "use", "for", "a", "document", "processor", "doing", "high", "latency", "operations", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2067239480", "fields": {"term": "What is thread-safe?", "namespace": "open-p", "hash": 2067239480, "terms": ["What is thread-safe?", "is thread-safe?", "thread-safe?"], "tokens": ["What", "is", "thread", "safe", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-426709895", "fields": {"term": "How to implement a multithread-safe document processor?", "namespace": "open-p", "hash": -426709895, "terms": ["How to implement a multithread-safe document processor?", "to implement a multithread-safe document processor?", "implement a multithread-safe document processor?", "a multithread-safe document processor?", "multithread-safe document processor?", "document processor?", "processor?"], "tokens": ["How", "to", "implement", "a", "multithread", "safe", "document", "processor", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1367670834", "fields": {"term": "What are the options for implementing multithread-safe document processor?", "namespace": "open-p", "hash": -1367670834, "terms": ["What are the options for implementing multithread-safe document processor?", "are the options for implementing multithread-safe document processor?", "the options for implementing multithread-safe document processor?", "options for implementing multithread-safe document processor?", "for implementing multithread-safe document processor?", "implementing multithread-safe document processor?", "multithread-safe document processor?", "document processor?", "processor?"], "tokens": ["What", "are", "the", "options", "for", "implementing", "multithread", "safe", "document", "processor", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::132325128", "fields": {"term": "What is the map format of Processing variable?", "namespace": "open-p", "hash": 132325128, "terms": ["What is the map format of Processing variable?", "is the map format of Processing variable?", "the map format of Processing variable?", "map format of Processing variable?", "format of Processing variable?", "of Processing variable?", "Processing variable?", "variable?"], "tokens": ["What", "is", "the", "map", "format", "of", "Processing", "variable", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1732367498", "fields": {"term": "How can you pass information between document processors using Processing variable?", "namespace": "open-p", "hash": 1732367498, "terms": ["How can you pass information between document processors using Processing variable?", "can you pass information between document processors using Processing variable?", "you pass information between document processors using Processing variable?", "pass information between document processors using Processing variable?", "information between document processors using Processing variable?", "between document processors using Processing variable?", "document processors using Processing variable?", "processors using Processing variable?", "using Processing variable?", "Processing variable?", "variable?"], "tokens": ["How", "can", "you", "pass", "information", "between", "document", "processors", "using", "Processing", "variable", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-462993531", "fields": {"term": "How can you save the state of Processing using context variables?", "namespace": "open-p", "hash": -462993531, "terms": ["How can you save the state of Processing using context variables?", "can you save the state of Processing using context variables?", "you save the state of Processing using context variables?", "save the state of Processing using context variables?", "the state of Processing using context variables?", "state of Processing using context variables?", "of Processing using context variables?", "Processing using context variables?", "using context variables?", "context variables?", "variables?"], "tokens": ["How", "can", "you", "save", "the", "state", "of", "Processing", "using", "context", "variables", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1976295706", "fields": {"term": "Is there any sample application that uses Processing variable?", "namespace": "open-p", "hash": 1976295706, "terms": ["Is there any sample application that uses Processing variable?", "there any sample application that uses Processing variable?", "any sample application that uses Processing variable?", "sample application that uses Processing variable?", "application that uses Processing variable?", "that uses Processing variable?", "uses Processing variable?", "Processing variable?", "variable?"], "tokens": ["Is", "there", "any", "sample", "application", "that", "uses", "Processing", "variable", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1491422042", "fields": {"term": "Where can I find more details about get/setVariable methods in Processing.java?", "namespace": "open-p", "hash": 1491422042, "terms": ["Where can I find more details about get/setVariable methods in Processing.java?", "can I find more details about get/setVariable methods in Processing.java?", "I find more details about get/setVariable methods in Processing.java?", "find more details about get/setVariable methods in Processing.java?", "more details about get/setVariable methods in Processing.java?", "details about get/setVariable methods in Processing.java?", "about get/setVariable methods in Processing.java?", "get/setVariable methods in Processing.java?", "methods in Processing.java?", "in Processing.java?", "Processing.java?"], "tokens": ["Where", "can", "I", "find", "more", "details", "about", "get", "/", "setVariable", "methods", "in", "Processing", "java", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::311406697", "fields": {"term": "What is the guarantee for same document operations?", "namespace": "open-p", "hash": 311406697, "terms": ["What is the guarantee for same document operations?", "is the guarantee for same document operations?", "the guarantee for same document operations?", "guarantee for same document operations?", "for same document operations?", "same document operations?", "document operations?", "operations?"], "tokens": ["What", "is", "the", "guarantee", "for", "same", "document", "operations", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::960923523", "fields": {"term": "Is ordering guaranteed for different documents?", "namespace": "open-p", "hash": 960923523, "terms": ["Is ordering guaranteed for different documents?", "ordering guaranteed for different documents?", "guaranteed for different documents?", "for different documents?", "different documents?", "documents?"], "tokens": ["Is", "ordering", "guaranteed", "for", "different", "documents", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1054439931", "fields": {"term": "What is feed endpoint?", "namespace": "open-p", "hash": -1054439931, "terms": ["What is feed endpoint?", "is feed endpoint?", "feed endpoint?", "endpoint?"], "tokens": ["What", "is", "feed", "endpoint", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1836078566", "fields": {"term": "What are document operations?", "namespace": "open-p", "hash": 1836078566, "terms": ["What are document operations?", "are document operations?", "document operations?", "operations?"], "tokens": ["What", "are", "document", "operations", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1758381605", "fields": {"term": "What rules apply to document processors?", "namespace": "open-p", "hash": -1758381605, "terms": ["What rules apply to document processors?", "rules apply to document processors?", "apply to document processors?", "to document processors?", "document processors?", "processors?"], "tokens": ["What", "rules", "apply", "to", "document", "processors", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1009415924", "fields": {"term": "How does Vespa sequence operations?", "namespace": "open-p", "hash": -1009415924, "terms": ["How does Vespa sequence operations?", "does Vespa sequence operations?", "Vespa sequence operations?", "sequence operations?", "operations?"], "tokens": ["How", "does", "Vespa", "sequence", "operations", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1021295470", "fields": {"term": "What happens when changing chain ids?", "namespace": "open-p", "hash": -1021295470, "terms": ["What happens when changing chain ids?", "happens when changing chain ids?", "when changing chain ids?", "changing chain ids?", "chain ids?", "ids?"], "tokens": ["What", "happens", "when", "changing", "chain", "ids", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1099302247", "fields": {"term": "What is 'vespa-deploy activate'?", "namespace": "open-p", "hash": -1099302247, "terms": ["What is 'vespa-deploy activate'?", "is 'vespa-deploy activate'?", "'vespa-deploy activate'?", "activate'?"], "tokens": ["What", "is", "'", "vespa", "deploy", "activate", "'?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1324717706", "fields": {"term": "When is a restart required?", "namespace": "open-p", "hash": -1324717706, "terms": ["When is a restart required?", "is a restart required?", "a restart required?", "restart required?", "required?"], "tokens": ["When", "is", "a", "restart", "required", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::339924178", "fields": {"term": "What happens when deploying a new document processor?", "namespace": "open-p", "hash": 339924178, "terms": ["What happens when deploying a new document processor?", "happens when deploying a new document processor?", "when deploying a new document processor?", "deploying a new document processor?", "a new document processor?", "new document processor?", "document processor?", "processor?"], "tokens": ["What", "happens", "when", "deploying", "a", "new", "document", "processor", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1985022326", "fields": {"term": "What is in the class diagram?", "namespace": "open-p", "hash": -1985022326, "terms": ["What is in the class diagram?", "is in the class diagram?", "in the class diagram?", "the class diagram?", "class diagram?", "diagram?"], "tokens": ["What", "is", "in", "the", "class", "diagram", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1747362766", "fields": {"term": "What is a core class?", "namespace": "open-p", "hash": -1747362766, "terms": ["What is a core class?", "is a core class?", "a core class?", "core class?", "class?"], "tokens": ["What", "is", "a", "core", "class", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1482891724", "fields": {"term": "What does the diagram show?", "namespace": "open-p", "hash": 1482891724, "terms": ["What does the diagram show?", "does the diagram show?", "the diagram show?", "diagram show?", "show?"], "tokens": ["What", "does", "the", "diagram", "show", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::409179138", "fields": {"term": "What is the document selector language?", "namespace": "open-p", "hash": 409179138, "terms": ["What is the document selector language?", "is the document selector language?", "the document selector language?", "document selector language?", "selector language?", "language?"], "tokens": ["What", "is", "the", "document", "selector", "language", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1289791814", "fields": {"term": "What is a subset of documents?", "namespace": "open-p", "hash": -1289791814, "terms": ["What is a subset of documents?", "is a subset of documents?", "a subset of documents?", "subset of documents?", "of documents?", "documents?"], "tokens": ["What", "is", "a", "subset", "of", "documents", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1488028499", "fields": {"term": "What can be parsed to build a parse tree?", "namespace": "open-p", "hash": -1488028499, "terms": ["What can be parsed to build a parse tree?", "can be parsed to build a parse tree?", "be parsed to build a parse tree?", "parsed to build a parse tree?", "to build a parse tree?", "build a parse tree?", "a parse tree?", "parse tree?", "tree?"], "tokens": ["What", "can", "be", "parsed", "to", "build", "a", "parse", "tree", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1710304231", "fields": {"term": "What can the parse tree answer?", "namespace": "open-p", "hash": -1710304231, "terms": ["What can the parse tree answer?", "can the parse tree answer?", "the parse tree answer?", "parse tree answer?", "tree answer?", "answer?"], "tokens": ["What", "can", "the", "parse", "tree", "answer", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-955965110", "fields": {"term": "What is contained within the subset?", "namespace": "open-p", "hash": -955965110, "terms": ["What is contained within the subset?", "is contained within the subset?", "contained within the subset?", "within the subset?", "the subset?", "subset?"], "tokens": ["What", "is", "contained", "within", "the", "subset", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-683070253", "fields": {"term": "How is the document selector language used during feeding?", "namespace": "open-p", "hash": -683070253, "terms": ["How is the document selector language used during feeding?", "is the document selector language used during feeding?", "the document selector language used during feeding?", "document selector language used during feeding?", "selector language used during feeding?", "language used during feeding?", "used during feeding?", "during feeding?", "feeding?"], "tokens": ["How", "is", "the", "document", "selector", "language", "used", "during", "feeding", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1944094882", "fields": {"term": "How is the document selector language used during garbage collection?", "namespace": "open-p", "hash": 1944094882, "terms": ["How is the document selector language used during garbage collection?", "is the document selector language used during garbage collection?", "the document selector language used during garbage collection?", "document selector language used during garbage collection?", "selector language used during garbage collection?", "language used during garbage collection?", "used during garbage collection?", "during garbage collection?", "garbage collection?", "collection?"], "tokens": ["How", "is", "the", "document", "selector", "language", "used", "during", "garbage", "collection", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1075317443", "fields": {"term": "What are case-sensitive identifiers?", "namespace": "open-p", "hash": 1075317443, "terms": ["What are case-sensitive identifiers?", "are case-sensitive identifiers?", "case-sensitive identifiers?", "identifiers?"], "tokens": ["What", "are", "case", "sensitive", "identifiers", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1871354328", "fields": {"term": "What are the recommended identifiers?", "namespace": "open-p", "hash": -1871354328, "terms": ["What are the recommended identifiers?", "are the recommended identifiers?", "the recommended identifiers?", "recommended identifiers?", "identifiers?"], "tokens": ["What", "are", "the", "recommended", "identifiers", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1084932922", "fields": {"term": "What are branch operators?", "namespace": "open-p", "hash": 1084932922, "terms": ["What are branch operators?", "are branch operators?", "branch operators?", "operators?"], "tokens": ["What", "are", "branch", "operators", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1623142882", "fields": {"term": "What is the order of precedence?", "namespace": "open-p", "hash": -1623142882, "terms": ["What is the order of precedence?", "is the order of precedence?", "the order of precedence?", "order of precedence?", "of precedence?", "precedence?"], "tokens": ["What", "is", "the", "order", "of", "precedence", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1027337096", "fields": {"term": "What is a Boolean constant?", "namespace": "open-p", "hash": -1027337096, "terms": ["What is a Boolean constant?", "is a Boolean constant?", "a Boolean constant?", "Boolean constant?", "constant?"], "tokens": ["What", "is", "a", "Boolean", "constant", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1565114693", "fields": {"term": "What is a null constant?", "namespace": "open-p", "hash": -1565114693, "terms": ["What is a null constant?", "is a null constant?", "a null constant?", "null constant?", "constant?"], "tokens": ["What", "is", "a", "null", "constant", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1610394703", "fields": {"term": "What is a document field specification?", "namespace": "open-p", "hash": -1610394703, "terms": ["What is a document field specification?", "is a document field specification?", "a document field specification?", "document field specification?", "field specification?", "specification?"], "tokens": ["What", "is", "a", "document", "field", "specification", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-32657399", "fields": {"term": "What is a comparison?", "namespace": "open-p", "hash": -32657399, "terms": ["What is a comparison?", "is a comparison?", "a comparison?", "comparison?"], "tokens": ["What", "is", "a", "comparison", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1450799193", "fields": {"term": "What are comparison operators?", "namespace": "open-p", "hash": -1450799193, "terms": ["What are comparison operators?", "are comparison operators?", "comparison operators?", "operators?"], "tokens": ["What", "are", "comparison", "operators", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1547941323", "fields": {"term": "What comparison operators work with null values?", "namespace": "open-p", "hash": 1547941323, "terms": ["What comparison operators work with null values?", "comparison operators work with null values?", "operators work with null values?", "work with null values?", "with null values?", "null values?", "values?"], "tokens": ["What", "comparison", "operators", "work", "with", "null", "values", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1904488683", "fields": {"term": "What happens when invalid values are propagated?", "namespace": "open-p", "hash": -1904488683, "terms": ["What happens when invalid values are propagated?", "happens when invalid values are propagated?", "when invalid values are propagated?", "invalid values are propagated?", "values are propagated?", "are propagated?", "propagated?"], "tokens": ["What", "happens", "when", "invalid", "values", "are", "propagated", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::178117308", "fields": {"term": "How can short-circuiting be used to work around the issue?", "namespace": "open-p", "hash": 178117308, "terms": ["How can short-circuiting be used to work around the issue?", "can short-circuiting be used to work around the issue?", "short-circuiting be used to work around the issue?", "be used to work around the issue?", "used to work around the issue?", "to work around the issue?", "work around the issue?", "around the issue?", "the issue?", "issue?"], "tokens": ["How", "can", "short", "circuiting", "be", "used", "to", "work", "around", "the", "issue", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1927389413", "fields": {"term": "What is the issue with `null` values in imported fields?", "namespace": "open-p", "hash": 1927389413, "terms": ["What is the issue with `null` values in imported fields?", "is the issue with `null` values in imported fields?", "the issue with `null` values in imported fields?", "issue with `null` values in imported fields?", "with `null` values in imported fields?", "`null` values in imported fields?", "values in imported fields?", "in imported fields?", "imported fields?", "fields?"], "tokens": ["What", "is", "the", "issue", "with", "`", "null", "`", "values", "in", "imported", "fields", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1358848834", "fields": {"term": "How can you handle `null` values for imported fields in GC selections?", "namespace": "open-p", "hash": 1358848834, "terms": ["How can you handle `null` values for imported fields in GC selections?", "can you handle `null` values for imported fields in GC selections?", "you handle `null` values for imported fields in GC selections?", "handle `null` values for imported fields in GC selections?", "`null` values for imported fields in GC selections?", "values for imported fields in GC selections?", "for imported fields in GC selections?", "imported fields in GC selections?", "fields in GC selections?", "in GC selections?", "GC selections?", "selections?"], "tokens": ["How", "can", "you", "handle", "`", "null", "`", "values", "for", "imported", "fields", "in", "GC", "selections", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-618522629", "fields": {"term": "What is the purpose of selection criteria in a `` tag?", "namespace": "open-p", "hash": -618522629, "terms": ["What is the purpose of selection criteria in a `` tag?", "is the purpose of selection criteria in a `` tag?", "the purpose of selection criteria in a `` tag?", "purpose of selection criteria in a `` tag?", "of selection criteria in a `` tag?", "selection criteria in a `` tag?", "criteria in a `` tag?", "in a `` tag?", "a `` tag?", "`` tag?", "tag?"], "tokens": ["What", "is", "the", "purpose", "of", "selection", "criteria", "in", "a", "`<", "document", ">`", "tag", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1212452777", "fields": {"term": "What character sets do Vespa support?", "namespace": "open-p", "hash": 1212452777, "terms": ["What character sets do Vespa support?", "character sets do Vespa support?", "sets do Vespa support?", "do Vespa support?", "Vespa support?", "support?"], "tokens": ["What", "character", "sets", "do", "Vespa", "support", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1290804792", "fields": {"term": "Does Vespa support regex matching?", "namespace": "open-p", "hash": 1290804792, "terms": ["Does Vespa support regex matching?", "Vespa support regex matching?", "support regex matching?", "regex matching?", "matching?"], "tokens": ["Does", "Vespa", "support", "regex", "matching", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1634143746", "fields": {"term": "What is a fieldpath?", "namespace": "open-p", "hash": -1634143746, "terms": ["What is a fieldpath?", "is a fieldpath?", "a fieldpath?", "fieldpath?"], "tokens": ["What", "is", "a", "fieldpath", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1529809124", "fields": {"term": "What is null value?", "namespace": "open-p", "hash": 1529809124, "terms": ["What is null value?", "is null value?", "null value?", "value?"], "tokens": ["What", "is", "null", "value", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-797410219", "fields": {"term": "What is the syntax for numbers?", "namespace": "open-p", "hash": -797410219, "terms": ["What is the syntax for numbers?", "is the syntax for numbers?", "the syntax for numbers?", "syntax for numbers?", "for numbers?", "numbers?"], "tokens": ["What", "is", "the", "syntax", "for", "numbers", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-202743141", "fields": {"term": "What can you do with values?", "namespace": "open-p", "hash": -202743141, "terms": ["What can you do with values?", "can you do with values?", "you do with values?", "do with values?", "with values?", "values?"], "tokens": ["What", "can", "you", "do", "with", "values", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-437656247", "fields": {"term": "What does abs() do?", "namespace": "open-p", "hash": -437656247, "terms": ["What does abs() do?", "does abs() do?", "abs() do?", "do?"], "tokens": ["What", "does", "abs", "()", "do", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1744463010", "fields": {"term": "What is the lowercase() function?", "namespace": "open-p", "hash": 1744463010, "terms": ["What is the lowercase() function?", "is the lowercase() function?", "the lowercase() function?", "lowercase() function?", "function?"], "tokens": ["What", "is", "the", "lowercase", "()", "function", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2101790834", "fields": {"term": "What is the version() function?", "namespace": "open-p", "hash": -2101790834, "terms": ["What is the version() function?", "is the version() function?", "the version() function?", "version() function?", "function?"], "tokens": ["What", "is", "the", "version", "()", "function", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1389600489", "fields": {"term": "What is the now() function?", "namespace": "open-p", "hash": -1389600489, "terms": ["What is the now() function?", "is the now() function?", "the now() function?", "now() function?", "function?"], "tokens": ["What", "is", "the", "now", "()", "function", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::875076982", "fields": {"term": "What is garbage collection in Vespa?", "namespace": "open-p", "hash": 875076982, "terms": ["What is garbage collection in Vespa?", "is garbage collection in Vespa?", "garbage collection in Vespa?", "collection in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["What", "is", "garbage", "collection", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1332032163", "fields": {"term": "How can you filter documents by age?", "namespace": "open-p", "hash": -1332032163, "terms": ["How can you filter documents by age?", "can you filter documents by age?", "you filter documents by age?", "filter documents by age?", "documents by age?", "by age?", "age?"], "tokens": ["How", "can", "you", "filter", "documents", "by", "age", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-370222047", "fields": {"term": "What is the inserttimestamp field?", "namespace": "open-p", "hash": -370222047, "terms": ["What is the inserttimestamp field?", "is the inserttimestamp field?", "the inserttimestamp field?", "inserttimestamp field?", "field?"], "tokens": ["What", "is", "the", "inserttimestamp", "field", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1980487493", "fields": {"term": "Can you use now() for filtering documents?", "namespace": "open-p", "hash": -1980487493, "terms": ["Can you use now() for filtering documents?", "you use now() for filtering documents?", "use now() for filtering documents?", "now() for filtering documents?", "for filtering documents?", "filtering documents?", "documents?"], "tokens": ["Can", "you", "use", "now", "()", "for", "filtering", "documents", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::720189970", "fields": {"term": "What is the time range for now() in garbage collection?", "namespace": "open-p", "hash": 720189970, "terms": ["What is the time range for now() in garbage collection?", "is the time range for now() in garbage collection?", "the time range for now() in garbage collection?", "time range for now() in garbage collection?", "range for now() in garbage collection?", "for now() in garbage collection?", "now() in garbage collection?", "in garbage collection?", "garbage collection?", "collection?"], "tokens": ["What", "is", "the", "time", "range", "for", "now", "()", "in", "garbage", "collection", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::92377599", "fields": {"term": "How to handle null values?", "namespace": "open-p", "hash": 92377599, "terms": ["How to handle null values?", "to handle null values?", "handle null values?", "null values?", "values?"], "tokens": ["How", "to", "handle", "null", "values", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::446969867", "fields": {"term": "What does a grandparent schema contain?", "namespace": "open-p", "hash": 446969867, "terms": ["What does a grandparent schema contain?", "does a grandparent schema contain?", "a grandparent schema contain?", "grandparent schema contain?", "schema contain?", "contain?"], "tokens": ["What", "does", "a", "grandparent", "schema", "contain", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::690530206", "fields": {"term": "What are the benefits of using a parent-child hierarchy?", "namespace": "open-p", "hash": 690530206, "terms": ["What are the benefits of using a parent-child hierarchy?", "are the benefits of using a parent-child hierarchy?", "the benefits of using a parent-child hierarchy?", "benefits of using a parent-child hierarchy?", "of using a parent-child hierarchy?", "using a parent-child hierarchy?", "a parent-child hierarchy?", "parent-child hierarchy?", "hierarchy?"], "tokens": ["What", "are", "the", "benefits", "of", "using", "a", "parent", "child", "hierarchy", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-943601836", "fields": {"term": "Where can I find more examples of using imported fields?", "namespace": "open-p", "hash": -943601836, "terms": ["Where can I find more examples of using imported fields?", "can I find more examples of using imported fields?", "I find more examples of using imported fields?", "find more examples of using imported fields?", "more examples of using imported fields?", "examples of using imported fields?", "of using imported fields?", "using imported fields?", "imported fields?", "fields?"], "tokens": ["Where", "can", "I", "find", "more", "examples", "of", "using", "imported", "fields", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2101055184", "fields": {"term": "What does Vespa's parent-child hierarchy enable?", "namespace": "open-p", "hash": -2101055184, "terms": ["What does Vespa's parent-child hierarchy enable?", "does Vespa's parent-child hierarchy enable?", "Vespa's parent-child hierarchy enable?", "parent-child hierarchy enable?", "hierarchy enable?", "enable?"], "tokens": ["What", "does", "Vespa", "'", "s", "parent", "child", "hierarchy", "enable", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-752436118", "fields": {"term": "What are invalid document type names?", "namespace": "open-p", "hash": -752436118, "terms": ["What are invalid document type names?", "are invalid document type names?", "invalid document type names?", "document type names?", "type names?", "names?"], "tokens": ["What", "are", "invalid", "document", "type", "names", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::416738432", "fields": {"term": "What is restricted in document type names?", "namespace": "open-p", "hash": 416738432, "terms": ["What is restricted in document type names?", "is restricted in document type names?", "restricted in document type names?", "in document type names?", "document type names?", "type names?", "names?"], "tokens": ["What", "is", "restricted", "in", "document", "type", "names", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1807498086", "fields": {"term": "What is excluded from double casing?", "namespace": "open-p", "hash": -1807498086, "terms": ["What is excluded from double casing?", "is excluded from double casing?", "excluded from double casing?", "from double casing?", "double casing?", "casing?"], "tokens": ["What", "is", "excluded", "from", "double", "casing", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-443284054", "fields": {"term": "What is a fieldspec?", "namespace": "open-p", "hash": -443284054, "terms": ["What is a fieldspec?", "is a fieldspec?", "a fieldspec?", "fieldspec?"], "tokens": ["What", "is", "a", "fieldspec", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-984544660", "fields": {"term": "What is a leaf?", "namespace": "open-p", "hash": -984544660, "terms": ["What is a leaf?", "is a leaf?", "a leaf?", "leaf?"], "tokens": ["What", "is", "a", "leaf", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-787855731", "fields": {"term": "What is comparison?", "namespace": "open-p", "hash": -787855731, "terms": ["What is comparison?", "is comparison?", "comparison?"], "tokens": ["What", "is", "comparison", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1634732865", "fields": {"term": "What is a document summary?", "namespace": "open-p", "hash": 1634732865, "terms": ["What is a document summary?", "is a document summary?", "a document summary?", "document summary?", "summary?"], "tokens": ["What", "is", "a", "document", "summary", "?"], "corpus_count": 4, "document_count": 4}}, -{"put": "id:term:term::-1747826929", "fields": {"term": "What is a document summary class?", "namespace": "open-p", "hash": -1747826929, "terms": ["What is a document summary class?", "is a document summary class?", "a document summary class?", "document summary class?", "summary class?", "class?"], "tokens": ["What", "is", "a", "document", "summary", "class", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1157969216", "fields": {"term": "What is the default summary class?", "namespace": "open-p", "hash": 1157969216, "terms": ["What is the default summary class?", "is the default summary class?", "the default summary class?", "default summary class?", "summary class?", "class?"], "tokens": ["What", "is", "the", "default", "summary", "class", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-420829188", "fields": {"term": "How are summary classes defined?", "namespace": "open-p", "hash": -420829188, "terms": ["How are summary classes defined?", "are summary classes defined?", "summary classes defined?", "classes defined?", "defined?"], "tokens": ["How", "are", "summary", "classes", "defined", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1929073476", "fields": {"term": "What is the use of document summary class?", "namespace": "open-p", "hash": -1929073476, "terms": ["What is the use of document summary class?", "is the use of document summary class?", "the use of document summary class?", "use of document summary class?", "of document summary class?", "document summary class?", "summary class?", "class?"], "tokens": ["What", "is", "the", "use", "of", "document", "summary", "class", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1520886357", "fields": {"term": "What is the parameter for defining summary class?", "namespace": "open-p", "hash": 1520886357, "terms": ["What is the parameter for defining summary class?", "is the parameter for defining summary class?", "the parameter for defining summary class?", "parameter for defining summary class?", "for defining summary class?", "defining summary class?", "summary class?", "class?"], "tokens": ["What", "is", "the", "parameter", "for", "defining", "summary", "class", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1295101893", "fields": {"term": "What is the benefit of using document summary?", "namespace": "open-p", "hash": -1295101893, "terms": ["What is the benefit of using document summary?", "is the benefit of using document summary?", "the benefit of using document summary?", "benefit of using document summary?", "of using document summary?", "using document summary?", "document summary?", "summary?"], "tokens": ["What", "is", "the", "benefit", "of", "using", "document", "summary", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1040331944", "fields": {"term": "What are dynamic snippets in document summaries?", "namespace": "open-p", "hash": 1040331944, "terms": ["What are dynamic snippets in document summaries?", "are dynamic snippets in document summaries?", "dynamic snippets in document summaries?", "snippets in document summaries?", "in document summaries?", "document summaries?", "summaries?"], "tokens": ["What", "are", "dynamic", "snippets", "in", "document", "summaries", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1078985020", "fields": {"term": "What is a YQL statement?", "namespace": "open-p", "hash": -1078985020, "terms": ["What is a YQL statement?", "is a YQL statement?", "a YQL statement?", "YQL statement?", "statement?"], "tokens": ["What", "is", "a", "YQL", "statement", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1300822137", "fields": {"term": "What is a summary class?", "namespace": "open-p", "hash": 1300822137, "terms": ["What is a summary class?", "is a summary class?", "a summary class?", "summary class?", "class?"], "tokens": ["What", "is", "a", "summary", "class", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::-659372346", "fields": {"term": "How to filter fields using YQL?", "namespace": "open-p", "hash": -659372346, "terms": ["How to filter fields using YQL?", "to filter fields using YQL?", "filter fields using YQL?", "fields using YQL?", "using YQL?", "YQL?"], "tokens": ["How", "to", "filter", "fields", "using", "YQL", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1462519510", "fields": {"term": "What is contained in a summary?", "namespace": "open-p", "hash": 1462519510, "terms": ["What is contained in a summary?", "is contained in a summary?", "contained in a summary?", "in a summary?", "a summary?", "summary?"], "tokens": ["What", "is", "contained", "in", "a", "summary", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1003578969", "fields": {"term": "How to select all fields?", "namespace": "open-p", "hash": 1003578969, "terms": ["How to select all fields?", "to select all fields?", "select all fields?", "all fields?", "fields?"], "tokens": ["How", "to", "select", "all", "fields", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1161367792", "fields": {"term": "What is the syntax of a select statement?", "namespace": "open-p", "hash": -1161367792, "terms": ["What is the syntax of a select statement?", "is the syntax of a select statement?", "the syntax of a select statement?", "syntax of a select statement?", "of a select statement?", "a select statement?", "select statement?", "statement?"], "tokens": ["What", "is", "the", "syntax", "of", "a", "select", "statement", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1271442174", "fields": {"term": "What is the meaning of fields in a document summary?", "namespace": "open-p", "hash": 1271442174, "terms": ["What is the meaning of fields in a document summary?", "is the meaning of fields in a document summary?", "the meaning of fields in a document summary?", "meaning of fields in a document summary?", "of fields in a document summary?", "fields in a document summary?", "in a document summary?", "a document summary?", "document summary?", "summary?"], "tokens": ["What", "is", "the", "meaning", "of", "fields", "in", "a", "document", "summary", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-532561005", "fields": {"term": "How to define fields in a summary class?", "namespace": "open-p", "hash": -532561005, "terms": ["How to define fields in a summary class?", "to define fields in a summary class?", "define fields in a summary class?", "fields in a summary class?", "in a summary class?", "a summary class?", "summary class?", "class?"], "tokens": ["How", "to", "define", "fields", "in", "a", "summary", "class", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-945284554", "fields": {"term": "What is a non-attribute field?", "namespace": "open-p", "hash": -945284554, "terms": ["What is a non-attribute field?", "is a non-attribute field?", "a non-attribute field?", "non-attribute field?", "field?"], "tokens": ["What", "is", "a", "non", "attribute", "field", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2031608840", "fields": {"term": "When do changes in summary class require re-indexing?", "namespace": "open-p", "hash": 2031608840, "terms": ["When do changes in summary class require re-indexing?", "do changes in summary class require re-indexing?", "changes in summary class require re-indexing?", "in summary class require re-indexing?", "summary class require re-indexing?", "class require re-indexing?", "require re-indexing?", "re-indexing?"], "tokens": ["When", "do", "changes", "in", "summary", "class", "require", "re", "indexing", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-732342", "fields": {"term": "What is the source in document summary?", "namespace": "open-p", "hash": -732342, "terms": ["What is the source in document summary?", "is the source in document summary?", "the source in document summary?", "source in document summary?", "in document summary?", "document summary?", "summary?"], "tokens": ["What", "is", "the", "source", "in", "document", "summary", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-367443819", "fields": {"term": "What is the artist_name field in document summary?", "namespace": "open-p", "hash": -367443819, "terms": ["What is the artist_name field in document summary?", "is the artist_name field in document summary?", "the artist_name field in document summary?", "artist_name field in document summary?", "field in document summary?", "in document summary?", "document summary?", "summary?"], "tokens": ["What", "is", "the", "artist_name", "field", "in", "document", "summary", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::116176093", "fields": {"term": "What is dynamic for summary fields?", "namespace": "open-p", "hash": 116176093, "terms": ["What is dynamic for summary fields?", "is dynamic for summary fields?", "dynamic for summary fields?", "for summary fields?", "summary fields?", "fields?"], "tokens": ["What", "is", "dynamic", "for", "summary", "fields", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1045265677", "fields": {"term": "How does dynamic summary work?", "namespace": "open-p", "hash": -1045265677, "terms": ["How does dynamic summary work?", "does dynamic summary work?", "dynamic summary work?", "summary work?", "work?"], "tokens": ["How", "does", "dynamic", "summary", "work", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::16986819", "fields": {"term": "What is bolding?", "namespace": "open-p", "hash": 16986819, "terms": ["What is bolding?", "is bolding?", "bolding?"], "tokens": ["What", "is", "bolding", "?"], "corpus_count": 3, "document_count": 3}}, -{"put": "id:term:term::1086476097", "fields": {"term": "How to enable bolding?", "namespace": "open-p", "hash": 1086476097, "terms": ["How to enable bolding?", "to enable bolding?", "enable bolding?", "bolding?"], "tokens": ["How", "to", "enable", "bolding", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1000012171", "fields": {"term": "What is the response format for dynamic summaries?", "namespace": "open-p", "hash": 1000012171, "terms": ["What is the response format for dynamic summaries?", "is the response format for dynamic summaries?", "the response format for dynamic summaries?", "response format for dynamic summaries?", "format for dynamic summaries?", "for dynamic summaries?", "dynamic summaries?", "summaries?"], "tokens": ["What", "is", "the", "response", "format", "for", "dynamic", "summaries", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1442393999", "fields": {"term": "What is the syntax to enable dynamic summary?", "namespace": "open-p", "hash": 1442393999, "terms": ["What is the syntax to enable dynamic summary?", "is the syntax to enable dynamic summary?", "the syntax to enable dynamic summary?", "syntax to enable dynamic summary?", "to enable dynamic summary?", "enable dynamic summary?", "dynamic summary?", "summary?"], "tokens": ["What", "is", "the", "syntax", "to", "enable", "dynamic", "summary", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1071892245", "fields": {"term": "How to create document summaries?", "namespace": "open-p", "hash": -1071892245, "terms": ["How to create document summaries?", "to create document summaries?", "create document summaries?", "document summaries?", "summaries?"], "tokens": ["How", "to", "create", "document", "summaries", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-624027067", "fields": {"term": "What are the benefits of using dynamic summaries?", "namespace": "open-p", "hash": -624027067, "terms": ["What are the benefits of using dynamic summaries?", "are the benefits of using dynamic summaries?", "the benefits of using dynamic summaries?", "benefits of using dynamic summaries?", "of using dynamic summaries?", "using dynamic summaries?", "dynamic summaries?", "summaries?"], "tokens": ["What", "are", "the", "benefits", "of", "using", "dynamic", "summaries", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1925441674", "fields": {"term": "How to configure dynamic snippets?", "namespace": "open-p", "hash": -1925441674, "terms": ["How to configure dynamic snippets?", "to configure dynamic snippets?", "configure dynamic snippets?", "dynamic snippets?", "snippets?"], "tokens": ["How", "to", "configure", "dynamic", "snippets", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1878288316", "fields": {"term": "Where to add juniperrc config?", "namespace": "open-p", "hash": 1878288316, "terms": ["Where to add juniperrc config?", "to add juniperrc config?", "add juniperrc config?", "juniperrc config?", "config?"], "tokens": ["Where", "to", "add", "juniperrc", "config", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::3629030", "fields": {"term": "What are memory-only operations?", "namespace": "open-p", "hash": 3629030, "terms": ["What are memory-only operations?", "are memory-only operations?", "memory-only operations?", "operations?"], "tokens": ["What", "are", "memory", "only", "operations", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2059705614", "fields": {"term": "What is the optimal way to get high query throughput?", "namespace": "open-p", "hash": -2059705614, "terms": ["What is the optimal way to get high query throughput?", "is the optimal way to get high query throughput?", "the optimal way to get high query throughput?", "optimal way to get high query throughput?", "way to get high query throughput?", "to get high query throughput?", "get high query throughput?", "high query throughput?", "query throughput?", "throughput?"], "tokens": ["What", "is", "the", "optimal", "way", "to", "get", "high", "query", "throughput", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::284867518", "fields": {"term": "How are debug fields with summary enabled affected?", "namespace": "open-p", "hash": 284867518, "terms": ["How are debug fields with summary enabled affected?", "are debug fields with summary enabled affected?", "debug fields with summary enabled affected?", "fields with summary enabled affected?", "with summary enabled affected?", "summary enabled affected?", "enabled affected?", "affected?"], "tokens": ["How", "are", "debug", "fields", "with", "summary", "enabled", "affected", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1219419568", "fields": {"term": "What is the document summary latency?", "namespace": "open-p", "hash": -1219419568, "terms": ["What is the document summary latency?", "is the document summary latency?", "the document summary latency?", "document summary latency?", "summary latency?", "latency?"], "tokens": ["What", "is", "the", "document", "summary", "latency", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1909317558", "fields": {"term": "What increases with more hits?", "namespace": "open-p", "hash": 1909317558, "terms": ["What increases with more hits?", "increases with more hits?", "with more hits?", "more hits?", "hits?"], "tokens": ["What", "increases", "with", "more", "hits", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1301508134", "fields": {"term": "What is the content node summary cache?", "namespace": "open-p", "hash": 1301508134, "terms": ["What is the content node summary cache?", "is the content node summary cache?", "the content node summary cache?", "content node summary cache?", "node summary cache?", "summary cache?", "cache?"], "tokens": ["What", "is", "the", "content", "node", "summary", "cache", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-909424098", "fields": {"term": "What is the document/v1 API?", "namespace": "open-p", "hash": -909424098, "terms": ["What is the document/v1 API?", "is the document/v1 API?", "the document/v1 API?", "document/v1 API?", "API?"], "tokens": ["What", "is", "the", "document", "/", "v1", "API", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2140061444", "fields": {"term": "How to read a document?", "namespace": "open-p", "hash": 2140061444, "terms": ["How to read a document?", "to read a document?", "read a document?", "a document?", "document?"], "tokens": ["How", "to", "read", "a", "document", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1326332373", "fields": {"term": "How to write a document?", "namespace": "open-p", "hash": -1326332373, "terms": ["How to write a document?", "to write a document?", "write a document?", "a document?", "document?"], "tokens": ["How", "to", "write", "a", "document", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1103544774", "fields": {"term": "How to delete a document?", "namespace": "open-p", "hash": 1103544774, "terms": ["How to delete a document?", "to delete a document?", "delete a document?", "a document?", "document?"], "tokens": ["How", "to", "delete", "a", "document", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-47036640", "fields": {"term": "What is a test-and-set condition?", "namespace": "open-p", "hash": -47036640, "terms": ["What is a test-and-set condition?", "is a test-and-set condition?", "a test-and-set condition?", "test-and-set condition?", "condition?"], "tokens": ["What", "is", "a", "test", "and", "set", "condition", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-842384030", "fields": {"term": "What is returned if the test-and-set condition is not met?", "namespace": "open-p", "hash": -842384030, "terms": ["What is returned if the test-and-set condition is not met?", "is returned if the test-and-set condition is not met?", "returned if the test-and-set condition is not met?", "if the test-and-set condition is not met?", "the test-and-set condition is not met?", "test-and-set condition is not met?", "condition is not met?", "is not met?", "not met?", "met?"], "tokens": ["What", "is", "returned", "if", "the", "test", "and", "set", "condition", "is", "not", "met", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::85770613", "fields": {"term": "Where can I find the condition reference?", "namespace": "open-p", "hash": 85770613, "terms": ["Where can I find the condition reference?", "can I find the condition reference?", "I find the condition reference?", "find the condition reference?", "the condition reference?", "condition reference?", "reference?"], "tokens": ["Where", "can", "I", "find", "the", "condition", "reference", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1036391252", "fields": {"term": "What is the format for the test-and-set condition?", "namespace": "open-p", "hash": -1036391252, "terms": ["What is the format for the test-and-set condition?", "is the format for the test-and-set condition?", "the format for the test-and-set condition?", "format for the test-and-set condition?", "for the test-and-set condition?", "the test-and-set condition?", "test-and-set condition?", "condition?"], "tokens": ["What", "is", "the", "format", "for", "the", "test", "and", "set", "condition", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-769227647", "fields": {"term": "How do I add a test-and-set condition to Put, Remove, and Update operations?", "namespace": "open-p", "hash": -769227647, "terms": ["How do I add a test-and-set condition to Put, Remove, and Update operations?", "do I add a test-and-set condition to Put, Remove, and Update operations?", "I add a test-and-set condition to Put, Remove, and Update operations?", "add a test-and-set condition to Put, Remove, and Update operations?", "a test-and-set condition to Put, Remove, and Update operations?", "test-and-set condition to Put, Remove, and Update operations?", "condition to Put, Remove, and Update operations?", "to Put, Remove, and Update operations?", "Put, Remove, and Update operations?", "Remove, and Update operations?", "and Update operations?", "Update operations?", "operations?"], "tokens": ["How", "do", "I", "add", "a", "test", "and", "set", "condition", "to", "Put", "Remove", "and", "Update", "operations", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1876363913", "fields": {"term": "What is the pathId in the message response?", "namespace": "open-p", "hash": -1876363913, "terms": ["What is the pathId in the message response?", "is the pathId in the message response?", "the pathId in the message response?", "pathId in the message response?", "in the message response?", "the message response?", "message response?", "response?"], "tokens": ["What", "is", "the", "pathId", "in", "the", "message", "response", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1485063781", "fields": {"term": "What is the meaning of the 412 Precondition Failed error code?", "namespace": "open-p", "hash": 1485063781, "terms": ["What is the meaning of the 412 Precondition Failed error code?", "is the meaning of the 412 Precondition Failed error code?", "the meaning of the 412 Precondition Failed error code?", "meaning of the 412 Precondition Failed error code?", "of the 412 Precondition Failed error code?", "the 412 Precondition Failed error code?", "412 Precondition Failed error code?", "Precondition Failed error code?", "Failed error code?", "error code?", "code?"], "tokens": ["What", "is", "the", "meaning", "of", "the", "412", "Precondition", "Failed", "error", "code", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::520614476", "fields": {"term": "What is an upsert?", "namespace": "open-p", "hash": 520614476, "terms": ["What is an upsert?", "is an upsert?", "an upsert?", "upsert?"], "tokens": ["What", "is", "an", "upsert", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-194177377", "fields": {"term": "How does 'create' simplify client code?", "namespace": "open-p", "hash": -194177377, "terms": ["How does 'create' simplify client code?", "does 'create' simplify client code?", "'create' simplify client code?", "simplify client code?", "client code?", "code?"], "tokens": ["How", "does", "'", "create", "'", "simplify", "client", "code", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1818095600", "fields": {"term": "What is the syntax for using 'create' in a curl command?", "namespace": "open-p", "hash": -1818095600, "terms": ["What is the syntax for using 'create' in a curl command?", "is the syntax for using 'create' in a curl command?", "the syntax for using 'create' in a curl command?", "syntax for using 'create' in a curl command?", "for using 'create' in a curl command?", "using 'create' in a curl command?", "'create' in a curl command?", "in a curl command?", "a curl command?", "curl command?", "command?"], "tokens": ["What", "is", "the", "syntax", "for", "using", "'", "create", "'", "in", "a", "curl", "command", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1473545858", "fields": {"term": "What is a conditional write?", "namespace": "open-p", "hash": 1473545858, "terms": ["What is a conditional write?", "is a conditional write?", "a conditional write?", "conditional write?", "write?"], "tokens": ["What", "is", "a", "conditional", "write", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-934566753", "fields": {"term": "How does 'create' work with a conditional write?", "namespace": "open-p", "hash": -934566753, "terms": ["How does 'create' work with a conditional write?", "does 'create' work with a conditional write?", "'create' work with a conditional write?", "work with a conditional write?", "with a conditional write?", "a conditional write?", "conditional write?", "write?"], "tokens": ["How", "does", "'", "create", "'", "work", "with", "a", "conditional", "write", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1576496390", "fields": {"term": "What is jq?", "namespace": "open-p", "hash": 1576496390, "terms": ["What is jq?", "is jq?", "jq?"], "tokens": ["What", "is", "jq", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-914911330", "fields": {"term": "How to dump all data?", "namespace": "open-p", "hash": -914911330, "terms": ["How to dump all data?", "to dump all data?", "dump all data?", "all data?", "data?"], "tokens": ["How", "to", "dump", "all", "data", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1204755386", "fields": {"term": "What is a continuation token?", "namespace": "open-p", "hash": 1204755386, "terms": ["What is a continuation token?", "is a continuation token?", "a continuation token?", "continuation token?", "token?"], "tokens": ["What", "is", "a", "continuation", "token", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-552501318", "fields": {"term": "What is the recommended number of slices?", "namespace": "open-p", "hash": -552501318, "terms": ["What is the recommended number of slices?", "is the recommended number of slices?", "the recommended number of slices?", "recommended number of slices?", "number of slices?", "of slices?", "slices?"], "tokens": ["What", "is", "the", "recommended", "number", "of", "slices", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-675331333", "fields": {"term": "What is the default number of slices?", "namespace": "open-p", "hash": -675331333, "terms": ["What is the default number of slices?", "is the default number of slices?", "the default number of slices?", "default number of slices?", "number of slices?", "of slices?", "slices?"], "tokens": ["What", "is", "the", "default", "number", "of", "slices", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1395299024", "fields": {"term": "What is the 'curlTimeout' variable?", "namespace": "open-p", "hash": -1395299024, "terms": ["What is the 'curlTimeout' variable?", "is the 'curlTimeout' variable?", "the 'curlTimeout' variable?", "'curlTimeout' variable?", "variable?"], "tokens": ["What", "is", "the", "'", "curlTimeout", "'", "variable", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1479154570", "fields": {"term": "What is the function 'visit' for?", "namespace": "open-p", "hash": -1479154570, "terms": ["What is the function 'visit' for?", "is the function 'visit' for?", "the function 'visit' for?", "function 'visit' for?", "'visit' for?", "for?"], "tokens": ["What", "is", "the", "function", "'", "visit", "'", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1225394913", "fields": {"term": "What does the parameter 'sliceId' represent?", "namespace": "open-p", "hash": -1225394913, "terms": ["What does the parameter 'sliceId' represent?", "does the parameter 'sliceId' represent?", "the parameter 'sliceId' represent?", "parameter 'sliceId' represent?", "'sliceId' represent?", "represent?"], "tokens": ["What", "does", "the", "parameter", "'", "sliceId", "'", "represent", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::190844176", "fields": {"term": "What is visit with selection?", "namespace": "open-p", "hash": 190844176, "terms": ["What is visit with selection?", "is visit with selection?", "visit with selection?", "with selection?", "selection?"], "tokens": ["What", "is", "visit", "with", "selection", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-575325163", "fields": {"term": "What are the options for increasing throughput?", "namespace": "open-p", "hash": -575325163, "terms": ["What are the options for increasing throughput?", "are the options for increasing throughput?", "the options for increasing throughput?", "options for increasing throughput?", "for increasing throughput?", "increasing throughput?", "throughput?"], "tokens": ["What", "are", "the", "options", "for", "increasing", "throughput", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::330981499", "fields": {"term": "What is the most effective way to increase throughput?", "namespace": "open-p", "hash": 330981499, "terms": ["What is the most effective way to increase throughput?", "is the most effective way to increase throughput?", "the most effective way to increase throughput?", "most effective way to increase throughput?", "effective way to increase throughput?", "way to increase throughput?", "to increase throughput?", "increase throughput?", "throughput?"], "tokens": ["What", "is", "the", "most", "effective", "way", "to", "increase", "throughput", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::622689170", "fields": {"term": "What are slices?", "namespace": "open-p", "hash": 622689170, "terms": ["What are slices?", "are slices?", "slices?"], "tokens": ["What", "are", "slices", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-126819458", "fields": {"term": "What is backend concurrency?", "namespace": "open-p", "hash": -126819458, "terms": ["What is backend concurrency?", "is backend concurrency?", "backend concurrency?", "concurrency?"], "tokens": ["What", "is", "backend", "concurrency", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1164430557", "fields": {"term": "Why is it recommended to always stream HTTP responses?", "namespace": "open-p", "hash": -1164430557, "terms": ["Why is it recommended to always stream HTTP responses?", "is it recommended to always stream HTTP responses?", "it recommended to always stream HTTP responses?", "recommended to always stream HTTP responses?", "to always stream HTTP responses?", "always stream HTTP responses?", "stream HTTP responses?", "HTTP responses?", "responses?"], "tokens": ["Why", "is", "it", "recommended", "to", "always", "stream", "HTTP", "responses", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1919650348", "fields": {"term": "What is wantedDocumentCount used for?", "namespace": "open-p", "hash": -1919650348, "terms": ["What is wantedDocumentCount used for?", "is wantedDocumentCount used for?", "wantedDocumentCount used for?", "used for?", "for?"], "tokens": ["What", "is", "wantedDocumentCount", "used", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::850634383", "fields": {"term": "How do you look up a document by ID?", "namespace": "open-p", "hash": 850634383, "terms": ["How do you look up a document by ID?", "do you look up a document by ID?", "you look up a document by ID?", "look up a document by ID?", "up a document by ID?", "a document by ID?", "document by ID?", "by ID?", "ID?"], "tokens": ["How", "do", "you", "look", "up", "a", "document", "by", "ID", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::629968274", "fields": {"term": "How to delete all documents in music schema?", "namespace": "open-p", "hash": 629968274, "terms": ["How to delete all documents in music schema?", "to delete all documents in music schema?", "delete all documents in music schema?", "all documents in music schema?", "documents in music schema?", "in music schema?", "music schema?", "schema?"], "tokens": ["How", "to", "delete", "all", "documents", "in", "music", "schema", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::749964011", "fields": {"term": "What are group and number modifiers?", "namespace": "open-p", "hash": 749964011, "terms": ["What are group and number modifiers?", "are group and number modifiers?", "group and number modifiers?", "and number modifiers?", "number modifiers?", "modifiers?"], "tokens": ["What", "are", "group", "and", "number", "modifiers", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::357350576", "fields": {"term": "What document types support group/number?", "namespace": "open-p", "hash": 357350576, "terms": ["What document types support group/number?", "document types support group/number?", "types support group/number?", "support group/number?", "group/number?"], "tokens": ["What", "document", "types", "support", "group", "/", "number", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1131334678", "fields": {"term": "What is the /document/v1 API?", "namespace": "open-p", "hash": 1131334678, "terms": ["What is the /document/v1 API?", "is the /document/v1 API?", "the /document/v1 API?", "/document/v1 API?", "API?"], "tokens": ["What", "is", "the", "/", "document", "/", "v1", "API", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-71282510", "fields": {"term": "What are the uses for the /document/v1 API?", "namespace": "open-p", "hash": -71282510, "terms": ["What are the uses for the /document/v1 API?", "are the uses for the /document/v1 API?", "the uses for the /document/v1 API?", "uses for the /document/v1 API?", "for the /document/v1 API?", "the /document/v1 API?", "/document/v1 API?", "API?"], "tokens": ["What", "are", "the", "uses", "for", "the", "/", "document", "/", "v1", "API", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1575963713", "fields": {"term": "What are number and group modifiers?", "namespace": "open-p", "hash": -1575963713, "terms": ["What are number and group modifiers?", "are number and group modifiers?", "number and group modifiers?", "and group modifiers?", "group modifiers?", "modifiers?"], "tokens": ["What", "are", "number", "and", "group", "modifiers", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-532618330", "fields": {"term": "How to enable API in Vespa?", "namespace": "open-p", "hash": -532618330, "terms": ["How to enable API in Vespa?", "to enable API in Vespa?", "enable API in Vespa?", "API in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["How", "to", "enable", "API", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1006072451", "fields": {"term": "Where to add document-api?", "namespace": "open-p", "hash": -1006072451, "terms": ["Where to add document-api?", "to add document-api?", "add document-api?", "document-api?"], "tokens": ["Where", "to", "add", "document", "api", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1398925902", "fields": {"term": "What is /document/v1 used for?", "namespace": "open-p", "hash": -1398925902, "terms": ["What is /document/v1 used for?", "is /document/v1 used for?", "/document/v1 used for?", "used for?", "for?"], "tokens": ["What", "is", "/", "document", "/", "v1", "used", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-472107549", "fields": {"term": "How to retrieve a document by ID?", "namespace": "open-p", "hash": -472107549, "terms": ["How to retrieve a document by ID?", "to retrieve a document by ID?", "retrieve a document by ID?", "a document by ID?", "document by ID?", "by ID?", "ID?"], "tokens": ["How", "to", "retrieve", "a", "document", "by", "ID", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1064945671", "fields": {"term": "How to delete a document by ID?", "namespace": "open-p", "hash": 1064945671, "terms": ["How to delete a document by ID?", "to delete a document by ID?", "delete a document by ID?", "a document by ID?", "document by ID?", "by ID?", "ID?"], "tokens": ["How", "to", "delete", "a", "document", "by", "ID", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2104873163", "fields": {"term": "How to delete documents based on a selection?", "namespace": "open-p", "hash": 2104873163, "terms": ["How to delete documents based on a selection?", "to delete documents based on a selection?", "delete documents based on a selection?", "documents based on a selection?", "based on a selection?", "on a selection?", "a selection?", "selection?"], "tokens": ["How", "to", "delete", "documents", "based", "on", "a", "selection", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1397701613", "fields": {"term": "What is continuation?", "namespace": "open-p", "hash": -1397701613, "terms": ["What is continuation?", "is continuation?", "continuation?"], "tokens": ["What", "is", "continuation", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1770661381", "fields": {"term": "What must requests include?", "namespace": "open-p", "hash": -1770661381, "terms": ["What must requests include?", "must requests include?", "requests include?", "include?"], "tokens": ["What", "must", "requests", "include", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1778593661", "fields": {"term": "What happens with ID in requests?", "namespace": "open-p", "hash": 1778593661, "terms": ["What happens with ID in requests?", "happens with ID in requests?", "with ID in requests?", "ID in requests?", "in requests?", "requests?"], "tokens": ["What", "happens", "with", "ID", "in", "requests", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1896344729", "fields": {"term": "What is dropped in requests?", "namespace": "open-p", "hash": 1896344729, "terms": ["What is dropped in requests?", "is dropped in requests?", "dropped in requests?", "in requests?", "requests?"], "tokens": ["What", "is", "dropped", "in", "requests", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-603844738", "fields": {"term": "What is the status code 200?", "namespace": "open-p", "hash": -603844738, "terms": ["What is the status code 200?", "is the status code 200?", "the status code 200?", "status code 200?", "code 200?", "200?"], "tokens": ["What", "is", "the", "status", "code", "200", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::-1759269915", "fields": {"term": "What is the status code 400?", "namespace": "open-p", "hash": -1759269915, "terms": ["What is the status code 400?", "is the status code 400?", "the status code 400?", "status code 400?", "code 400?", "400?"], "tokens": ["What", "is", "the", "status", "code", "400", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-44693592", "fields": {"term": "What is the status code 404?", "namespace": "open-p", "hash": -44693592, "terms": ["What is the status code 404?", "is the status code 404?", "the status code 404?", "status code 404?", "code 404?", "404?"], "tokens": ["What", "is", "the", "status", "code", "404", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::406646050", "fields": {"term": "What is the status code 412?", "namespace": "open-p", "hash": 406646050, "terms": ["What is the status code 412?", "is the status code 412?", "the status code 412?", "status code 412?", "code 412?", "412?"], "tokens": ["What", "is", "the", "status", "code", "412", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::611713652", "fields": {"term": "What is the status code 429?", "namespace": "open-p", "hash": 611713652, "terms": ["What is the status code 429?", "is the status code 429?", "the status code 429?", "status code 429?", "code 429?", "429?"], "tokens": ["What", "is", "the", "status", "code", "429", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::761916875", "fields": {"term": "What is the status code 500?", "namespace": "open-p", "hash": 761916875, "terms": ["What is the status code 500?", "is the status code 500?", "the status code 500?", "status code 500?", "code 500?", "500?"], "tokens": ["What", "is", "the", "status", "code", "500", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1761171976", "fields": {"term": "What is the status code 502?", "namespace": "open-p", "hash": 1761171976, "terms": ["What is the status code 502?", "is the status code 502?", "the status code 502?", "status code 502?", "code 502?", "502?"], "tokens": ["What", "is", "the", "status", "code", "502", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1683256634", "fields": {"term": "What is the status code 503?", "namespace": "open-p", "hash": 1683256634, "terms": ["What is the status code 503?", "is the status code 503?", "the status code 503?", "status code 503?", "code 503?", "503?"], "tokens": ["What", "is", "the", "status", "code", "503", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::666794177", "fields": {"term": "What is the status code 504?", "namespace": "open-p", "hash": 666794177, "terms": ["What is the status code 504?", "is the status code 504?", "the status code 504?", "status code 504?", "code 504?", "504?"], "tokens": ["What", "is", "the", "status", "code", "504", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1760404177", "fields": {"term": "What is the status code 507?", "namespace": "open-p", "hash": 1760404177, "terms": ["What is the status code 507?", "is the status code 507?", "the status code 507?", "status code 507?", "code 507?", "507?"], "tokens": ["What", "is", "the", "status", "code", "507", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-378181705", "fields": {"term": "What is X-Vespa-Ignored-Fields?", "namespace": "open-p", "hash": -378181705, "terms": ["What is X-Vespa-Ignored-Fields?", "is X-Vespa-Ignored-Fields?", "X-Vespa-Ignored-Fields?"], "tokens": ["What", "is", "X", "Vespa", "Ignored", "Fields", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::646753142", "fields": {"term": "When is X-Vespa-Ignored-Fields set?", "namespace": "open-p", "hash": 646753142, "terms": ["When is X-Vespa-Ignored-Fields set?", "is X-Vespa-Ignored-Fields set?", "X-Vespa-Ignored-Fields set?", "set?"], "tokens": ["When", "is", "X", "Vespa", "Ignored", "Fields", "set", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::149693270", "fields": {"term": "What is the format of Vespa responses?", "namespace": "open-p", "hash": 149693270, "terms": ["What is the format of Vespa responses?", "is the format of Vespa responses?", "the format of Vespa responses?", "format of Vespa responses?", "of Vespa responses?", "Vespa responses?", "responses?"], "tokens": ["What", "is", "the", "format", "of", "Vespa", "responses", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-763218627", "fields": {"term": "What are the fields in Vespa responses?", "namespace": "open-p", "hash": -763218627, "terms": ["What are the fields in Vespa responses?", "are the fields in Vespa responses?", "the fields in Vespa responses?", "fields in Vespa responses?", "in Vespa responses?", "Vespa responses?", "responses?"], "tokens": ["What", "are", "the", "fields", "in", "Vespa", "responses", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::293712557", "fields": {"term": "What is a continuation in Vespa responses?", "namespace": "open-p", "hash": 293712557, "terms": ["What is a continuation in Vespa responses?", "is a continuation in Vespa responses?", "a continuation in Vespa responses?", "continuation in Vespa responses?", "in Vespa responses?", "Vespa responses?", "responses?"], "tokens": ["What", "is", "a", "continuation", "in", "Vespa", "responses", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1958232560", "fields": {"term": "What is included in a GET request?", "namespace": "open-p", "hash": -1958232560, "terms": ["What is included in a GET request?", "is included in a GET request?", "included in a GET request?", "in a GET request?", "a GET request?", "GET request?", "request?"], "tokens": ["What", "is", "included", "in", "a", "GET", "request", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::704772472", "fields": {"term": "What is the purpose of the message field in Vespa responses?", "namespace": "open-p", "hash": 704772472, "terms": ["What is the purpose of the message field in Vespa responses?", "is the purpose of the message field in Vespa responses?", "the purpose of the message field in Vespa responses?", "purpose of the message field in Vespa responses?", "of the message field in Vespa responses?", "the message field in Vespa responses?", "message field in Vespa responses?", "field in Vespa responses?", "in Vespa responses?", "Vespa responses?", "responses?"], "tokens": ["What", "is", "the", "purpose", "of", "the", "message", "field", "in", "Vespa", "responses", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1262184454", "fields": {"term": "What is a document in Vespa?", "namespace": "open-p", "hash": -1262184454, "terms": ["What is a document in Vespa?", "is a document in Vespa?", "a document in Vespa?", "document in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["What", "is", "a", "document", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::153296668", "fields": {"term": "What is a document ID?", "namespace": "open-p", "hash": 153296668, "terms": ["What is a document ID?", "is a document ID?", "a document ID?", "document ID?", "ID?"], "tokens": ["What", "is", "a", "document", "ID", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::-2012859789", "fields": {"term": "What is a document schema?", "namespace": "open-p", "hash": -2012859789, "terms": ["What is a document schema?", "is a document schema?", "a document schema?", "document schema?", "schema?"], "tokens": ["What", "is", "a", "document", "schema", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2102885934", "fields": {"term": "What is the garbage collector?", "namespace": "open-p", "hash": -2102885934, "terms": ["What is the garbage collector?", "is the garbage collector?", "the garbage collector?", "garbage collector?", "collector?"], "tokens": ["What", "is", "the", "garbage", "collector", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::988908486", "fields": {"term": "How does Vespa distribute documents?", "namespace": "open-p", "hash": 988908486, "terms": ["How does Vespa distribute documents?", "does Vespa distribute documents?", "Vespa distribute documents?", "distribute documents?", "documents?"], "tokens": ["How", "does", "Vespa", "distribute", "documents", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::885008106", "fields": {"term": "What are global documents?", "namespace": "open-p", "hash": 885008106, "terms": ["What are global documents?", "are global documents?", "global documents?", "documents?"], "tokens": ["What", "are", "global", "documents", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::-549509101", "fields": {"term": "What are document identifiers?", "namespace": "open-p", "hash": -549509101, "terms": ["What are document identifiers?", "are document identifiers?", "document identifiers?", "identifiers?"], "tokens": ["What", "are", "document", "identifiers", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1563353961", "fields": {"term": "What is the URI scheme for identifiers?", "namespace": "open-p", "hash": -1563353961, "terms": ["What is the URI scheme for identifiers?", "is the URI scheme for identifiers?", "the URI scheme for identifiers?", "URI scheme for identifiers?", "scheme for identifiers?", "for identifiers?", "identifiers?"], "tokens": ["What", "is", "the", "URI", "scheme", "for", "identifiers", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1802844644", "fields": {"term": "What text characters are allowed in identifiers?", "namespace": "open-p", "hash": 1802844644, "terms": ["What text characters are allowed in identifiers?", "text characters are allowed in identifiers?", "characters are allowed in identifiers?", "are allowed in identifiers?", "allowed in identifiers?", "in identifiers?", "identifiers?"], "tokens": ["What", "text", "characters", "are", "allowed", "in", "identifiers", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-445969690", "fields": {"term": "What is the id scheme?", "namespace": "open-p", "hash": -445969690, "terms": ["What is the id scheme?", "is the id scheme?", "the id scheme?", "id scheme?", "scheme?"], "tokens": ["What", "is", "the", "id", "scheme", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1907164483", "fields": {"term": "What are the required id scheme parts?", "namespace": "open-p", "hash": -1907164483, "terms": ["What are the required id scheme parts?", "are the required id scheme parts?", "the required id scheme parts?", "required id scheme parts?", "id scheme parts?", "scheme parts?", "parts?"], "tokens": ["What", "are", "the", "required", "id", "scheme", "parts", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::387105163", "fields": {"term": "What is the key/value-pairs field used for?", "namespace": "open-p", "hash": 387105163, "terms": ["What is the key/value-pairs field used for?", "is the key/value-pairs field used for?", "the key/value-pairs field used for?", "key/value-pairs field used for?", "field used for?", "used for?", "for?"], "tokens": ["What", "is", "the", "key", "/", "value", "pairs", "field", "used", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::638315079", "fields": {"term": "What are the possible modifiers for the id scheme?", "namespace": "open-p", "hash": 638315079, "terms": ["What are the possible modifiers for the id scheme?", "are the possible modifiers for the id scheme?", "the possible modifiers for the id scheme?", "possible modifiers for the id scheme?", "modifiers for the id scheme?", "for the id scheme?", "the id scheme?", "id scheme?", "scheme?"], "tokens": ["What", "are", "the", "possible", "modifiers", "for", "the", "id", "scheme", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-735133545", "fields": {"term": "What is the user-specified field?", "namespace": "open-p", "hash": -735133545, "terms": ["What is the user-specified field?", "is the user-specified field?", "the user-specified field?", "user-specified field?", "field?"], "tokens": ["What", "is", "the", "user", "specified", "field", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1415915659", "fields": {"term": "Where can examples of the id scheme be found?", "namespace": "open-p", "hash": 1415915659, "terms": ["Where can examples of the id scheme be found?", "can examples of the id scheme be found?", "examples of the id scheme be found?", "of the id scheme be found?", "the id scheme be found?", "id scheme be found?", "scheme be found?", "be found?", "found?"], "tokens": ["Where", "can", "examples", "of", "the", "id", "scheme", "be", "found", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-820672362", "fields": {"term": "What is the *id scheme*?", "namespace": "open-p", "hash": -820672362, "terms": ["What is the *id scheme*?", "is the *id scheme*?", "the *id scheme*?", "*id scheme*?", "scheme*?"], "tokens": ["What", "is", "the", "*", "id", "scheme", "*?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1106125621", "fields": {"term": "What is the format of the id scheme?", "namespace": "open-p", "hash": -1106125621, "terms": ["What is the format of the id scheme?", "is the format of the id scheme?", "the format of the id scheme?", "format of the id scheme?", "of the id scheme?", "the id scheme?", "id scheme?", "scheme?"], "tokens": ["What", "is", "the", "format", "of", "the", "id", "scheme", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1196681605", "fields": {"term": "How are document IDs different?", "namespace": "open-p", "hash": -1196681605, "terms": ["How are document IDs different?", "are document IDs different?", "document IDs different?", "IDs different?", "different?"], "tokens": ["How", "are", "document", "IDs", "different", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::401373576", "fields": {"term": "Can namespace be used in queries?", "namespace": "open-p", "hash": 401373576, "terms": ["Can namespace be used in queries?", "namespace be used in queries?", "be used in queries?", "used in queries?", "in queries?", "queries?"], "tokens": ["Can", "namespace", "be", "used", "in", "queries", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1608273449", "fields": {"term": "How is namespace used for document selection?", "namespace": "open-p", "hash": -1608273449, "terms": ["How is namespace used for document selection?", "is namespace used for document selection?", "namespace used for document selection?", "used for document selection?", "for document selection?", "document selection?", "selection?"], "tokens": ["How", "is", "namespace", "used", "for", "document", "selection", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1554390989", "fields": {"term": "What is the full document ID for document A?", "namespace": "open-p", "hash": 1554390989, "terms": ["What is the full document ID for document A?", "is the full document ID for document A?", "the full document ID for document A?", "full document ID for document A?", "document ID for document A?", "ID for document A?", "for document A?", "document A?", "A?"], "tokens": ["What", "is", "the", "full", "document", "ID", "for", "document", "A", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2043794239", "fields": {"term": "What is the full document ID for document B?", "namespace": "open-p", "hash": -2043794239, "terms": ["What is the full document ID for document B?", "is the full document ID for document B?", "the full document ID for document B?", "full document ID for document B?", "document ID for document B?", "ID for document B?", "for document B?", "document B?", "B?"], "tokens": ["What", "is", "the", "full", "document", "ID", "for", "document", "B", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1227460672", "fields": {"term": "What is the difference in the document IDs of A and B?", "namespace": "open-p", "hash": 1227460672, "terms": ["What is the difference in the document IDs of A and B?", "is the difference in the document IDs of A and B?", "the difference in the document IDs of A and B?", "difference in the document IDs of A and B?", "in the document IDs of A and B?", "the document IDs of A and B?", "document IDs of A and B?", "IDs of A and B?", "of A and B?", "A and B?", "and B?", "B?"], "tokens": ["What", "is", "the", "difference", "in", "the", "document", "IDs", "of", "A", "and", "B", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-444055292", "fields": {"term": "What are fields in documents?", "namespace": "open-p", "hash": -444055292, "terms": ["What are fields in documents?", "are fields in documents?", "fields in documents?", "in documents?", "documents?"], "tokens": ["What", "are", "fields", "in", "documents", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1096415183", "fields": {"term": "What is document put/update?", "namespace": "open-p", "hash": -1096415183, "terms": ["What is document put/update?", "is document put/update?", "document put/update?", "put/update?"], "tokens": ["What", "is", "document", "put", "/", "update", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::802055048", "fields": {"term": "Can fields have default values?", "namespace": "open-p", "hash": 802055048, "terms": ["Can fields have default values?", "fields have default values?", "have default values?", "default values?", "values?"], "tokens": ["Can", "fields", "have", "default", "values", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-97786947", "fields": {"term": "How can a default value be assigned?", "namespace": "open-p", "hash": -97786947, "terms": ["How can a default value be assigned?", "can a default value be assigned?", "a default value be assigned?", "default value be assigned?", "value be assigned?", "be assigned?", "assigned?"], "tokens": ["How", "can", "a", "default", "value", "be", "assigned", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-760424120", "fields": {"term": "What is assigned by document processor?", "namespace": "open-p", "hash": -760424120, "terms": ["What is assigned by document processor?", "is assigned by document processor?", "assigned by document processor?", "by document processor?", "document processor?", "processor?"], "tokens": ["What", "is", "assigned", "by", "document", "processor", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-851784268", "fields": {"term": "What is a fieldset?", "namespace": "open-p", "hash": -851784268, "terms": ["What is a fieldset?", "is a fieldset?", "a fieldset?", "fieldset?"], "tokens": ["What", "is", "a", "fieldset", "?"], "corpus_count": 5, "document_count": 5}}, -{"put": "id:term:term::-590591196", "fields": {"term": "What are built-in fieldsets?", "namespace": "open-p", "hash": -590591196, "terms": ["What are built-in fieldsets?", "are built-in fieldsets?", "built-in fieldsets?", "fieldsets?"], "tokens": ["What", "are", "built", "in", "fieldsets", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1017651835", "fields": {"term": "How do I specify a fieldset?", "namespace": "open-p", "hash": 1017651835, "terms": ["How do I specify a fieldset?", "do I specify a fieldset?", "I specify a fieldset?", "specify a fieldset?", "a fieldset?", "fieldset?"], "tokens": ["How", "do", "I", "specify", "a", "fieldset", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1914868107", "fields": {"term": "What does [all] fieldset do?", "namespace": "open-p", "hash": 1914868107, "terms": ["What does [all] fieldset do?", "does [all] fieldset do?", "[all] fieldset do?", "fieldset do?", "do?"], "tokens": ["What", "does", "[", "all", "]", "fieldset", "do", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::566588604", "fields": {"term": "What is [document] fieldset?", "namespace": "open-p", "hash": 566588604, "terms": ["What is [document] fieldset?", "is [document] fieldset?", "[document] fieldset?", "fieldset?"], "tokens": ["What", "is", "[", "document", "]", "fieldset", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1604030858", "fields": {"term": "What is [none] fieldset?", "namespace": "open-p", "hash": 1604030858, "terms": ["What is [none] fieldset?", "is [none] fieldset?", "[none] fieldset?", "fieldset?"], "tokens": ["What", "is", "[", "none", "]", "fieldset", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1805539781", "fields": {"term": "What does [id] fieldset return?", "namespace": "open-p", "hash": -1805539781, "terms": ["What does [id] fieldset return?", "does [id] fieldset return?", "[id] fieldset return?", "fieldset return?", "return?"], "tokens": ["What", "does", "[", "id", "]", "fieldset", "return", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2024833902", "fields": {"term": "What does :[document] fieldset return?", "namespace": "open-p", "hash": 2024833902, "terms": ["What does :[document] fieldset return?", "does :[document] fieldset return?", ":[document] fieldset return?", "type>:[document] fieldset return?", "fieldset return?", "return?"], "tokens": ["What", "does", "<", "document", "type", ">:[", "document", "]", "fieldset", "return", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-423645047", "fields": {"term": "How to auto-expire documents?", "namespace": "open-p", "hash": -423645047, "terms": ["How to auto-expire documents?", "to auto-expire documents?", "auto-expire documents?", "documents?"], "tokens": ["How", "to", "auto", "expire", "documents", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::854608371", "fields": {"term": "How does Vespa iterate over the document space?", "namespace": "open-p", "hash": 854608371, "terms": ["How does Vespa iterate over the document space?", "does Vespa iterate over the document space?", "Vespa iterate over the document space?", "iterate over the document space?", "over the document space?", "the document space?", "document space?", "space?"], "tokens": ["How", "does", "Vespa", "iterate", "over", "the", "document", "space", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2080282764", "fields": {"term": "How to use a selection with now?", "namespace": "open-p", "hash": 2080282764, "terms": ["How to use a selection with now?", "to use a selection with now?", "use a selection with now?", "a selection with now?", "selection with now?", "with now?", "now?"], "tokens": ["How", "to", "use", "a", "selection", "with", "now", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1652247665", "fields": {"term": "What are the side effects of using a selection with now?", "namespace": "open-p", "hash": 1652247665, "terms": ["What are the side effects of using a selection with now?", "are the side effects of using a selection with now?", "the side effects of using a selection with now?", "side effects of using a selection with now?", "effects of using a selection with now?", "of using a selection with now?", "using a selection with now?", "a selection with now?", "selection with now?", "with now?", "now?"], "tokens": ["What", "are", "the", "side", "effects", "of", "using", "a", "selection", "with", "now", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::376216472", "fields": {"term": "What happens when using create with expired documents?", "namespace": "open-p", "hash": 376216472, "terms": ["What happens when using create with expired documents?", "happens when using create with expired documents?", "when using create with expired documents?", "using create with expired documents?", "create with expired documents?", "with expired documents?", "expired documents?", "documents?"], "tokens": ["What", "happens", "when", "using", "create", "with", "expired", "documents", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::525622811", "fields": {"term": "How to test a selection string using visit?", "namespace": "open-p", "hash": 525622811, "terms": ["How to test a selection string using visit?", "to test a selection string using visit?", "test a selection string using visit?", "a selection string using visit?", "selection string using visit?", "string using visit?", "using visit?", "visit?"], "tokens": ["How", "to", "test", "a", "selection", "string", "using", "visit", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::209900572", "fields": {"term": "What are the fields referenced in the selection expression?", "namespace": "open-p", "hash": 209900572, "terms": ["What are the fields referenced in the selection expression?", "are the fields referenced in the selection expression?", "the fields referenced in the selection expression?", "fields referenced in the selection expression?", "referenced in the selection expression?", "in the selection expression?", "the selection expression?", "selection expression?", "expression?"], "tokens": ["What", "are", "the", "fields", "referenced", "in", "the", "selection", "expression", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1839373765", "fields": {"term": "What can Document Processing do?", "namespace": "open-p", "hash": -1839373765, "terms": ["What can Document Processing do?", "can Document Processing do?", "Document Processing do?", "Processing do?", "do?"], "tokens": ["What", "can", "Document", "Processing", "do", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1595033219", "fields": {"term": "What are examples of processing PUTs or UPDATEs?", "namespace": "open-p", "hash": 1595033219, "terms": ["What are examples of processing PUTs or UPDATEs?", "are examples of processing PUTs or UPDATEs?", "examples of processing PUTs or UPDATEs?", "of processing PUTs or UPDATEs?", "processing PUTs or UPDATEs?", "PUTs or UPDATEs?", "or UPDATEs?", "UPDATEs?"], "tokens": ["What", "are", "examples", "of", "processing", "PUTs", "or", "UPDATEs", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2013038289", "fields": {"term": "What is OutLinksDocumentProcessor?", "namespace": "open-p", "hash": -2013038289, "terms": ["What is OutLinksDocumentProcessor?", "is OutLinksDocumentProcessor?", "OutLinksDocumentProcessor?"], "tokens": ["What", "is", "OutLinksDocumentProcessor", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1307578246", "fields": {"term": "What is the VespaDocSystemTest used for?", "namespace": "open-p", "hash": -1307578246, "terms": ["What is the VespaDocSystemTest used for?", "is the VespaDocSystemTest used for?", "the VespaDocSystemTest used for?", "VespaDocSystemTest used for?", "used for?", "for?"], "tokens": ["What", "is", "the", "VespaDocSystemTest", "used", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::553981875", "fields": {"term": "What are Vespa clusters?", "namespace": "open-p", "hash": 553981875, "terms": ["What are Vespa clusters?", "are Vespa clusters?", "Vespa clusters?", "clusters?"], "tokens": ["What", "are", "Vespa", "clusters", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1594926486", "fields": {"term": "How are documents managed?", "namespace": "open-p", "hash": 1594926486, "terms": ["How are documents managed?", "are documents managed?", "documents managed?", "managed?"], "tokens": ["How", "are", "documents", "managed", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-277728490", "fields": {"term": "How to remove nodes from a content cluster?", "namespace": "open-p", "hash": -277728490, "terms": ["How to remove nodes from a content cluster?", "to remove nodes from a content cluster?", "remove nodes from a content cluster?", "nodes from a content cluster?", "from a content cluster?", "a content cluster?", "content cluster?", "cluster?"], "tokens": ["How", "to", "remove", "nodes", "from", "a", "content", "cluster", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1838881752", "fields": {"term": "How to add or remove nodes?", "namespace": "open-p", "hash": -1838881752, "terms": ["How to add or remove nodes?", "to add or remove nodes?", "add or remove nodes?", "or remove nodes?", "remove nodes?", "nodes?"], "tokens": ["How", "to", "add", "or", "remove", "nodes", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1880244610", "fields": {"term": "What happens when a new node is added?", "namespace": "open-p", "hash": 1880244610, "terms": ["What happens when a new node is added?", "happens when a new node is added?", "when a new node is added?", "a new node is added?", "new node is added?", "node is added?", "is added?", "added?"], "tokens": ["What", "happens", "when", "a", "new", "node", "is", "added", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::107424472", "fields": {"term": "How are buckets moved?", "namespace": "open-p", "hash": 107424472, "terms": ["How are buckets moved?", "are buckets moved?", "buckets moved?", "moved?"], "tokens": ["How", "are", "buckets", "moved", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::256341917", "fields": {"term": "What is the node sequence used for?", "namespace": "open-p", "hash": 256341917, "terms": ["What is the node sequence used for?", "is the node sequence used for?", "the node sequence used for?", "node sequence used for?", "sequence used for?", "used for?", "for?"], "tokens": ["What", "is", "the", "node", "sequence", "used", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-52791932", "fields": {"term": "Can multiple nodes be added at once?", "namespace": "open-p", "hash": -52791932, "terms": ["Can multiple nodes be added at once?", "multiple nodes be added at once?", "nodes be added at once?", "be added at once?", "added at once?", "at once?", "once?"], "tokens": ["Can", "multiple", "nodes", "be", "added", "at", "once", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1346467265", "fields": {"term": "What happens to buckets when a new node is added?", "namespace": "open-p", "hash": -1346467265, "terms": ["What happens to buckets when a new node is added?", "happens to buckets when a new node is added?", "to buckets when a new node is added?", "buckets when a new node is added?", "when a new node is added?", "a new node is added?", "new node is added?", "node is added?", "is added?", "added?"], "tokens": ["What", "happens", "to", "buckets", "when", "a", "new", "node", "is", "added", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1489686531", "fields": {"term": "What happens when a node fails?", "namespace": "open-p", "hash": 1489686531, "terms": ["What happens when a node fails?", "happens when a node fails?", "when a node fails?", "a node fails?", "node fails?", "fails?"], "tokens": ["What", "happens", "when", "a", "node", "fails", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-741679913", "fields": {"term": "What is a group?", "namespace": "open-p", "hash": -741679913, "terms": ["What is a group?", "is a group?", "a group?", "group?"], "tokens": ["What", "is", "a", "group", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::7745295", "fields": {"term": "What is cluster upgrade?", "namespace": "open-p", "hash": 7745295, "terms": ["What is cluster upgrade?", "is cluster upgrade?", "cluster upgrade?", "upgrade?"], "tokens": ["What", "is", "cluster", "upgrade", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::13600608", "fields": {"term": "How can groups improve query throughput?", "namespace": "open-p", "hash": 13600608, "terms": ["How can groups improve query throughput?", "can groups improve query throughput?", "groups improve query throughput?", "improve query throughput?", "query throughput?", "throughput?"], "tokens": ["How", "can", "groups", "improve", "query", "throughput", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1643261729", "fields": {"term": "What is topology in Vespa?", "namespace": "open-p", "hash": 1643261729, "terms": ["What is topology in Vespa?", "is topology in Vespa?", "topology in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["What", "is", "topology", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1471618333", "fields": {"term": "How can redundancy be ensured using groups?", "namespace": "open-p", "hash": 1471618333, "terms": ["How can redundancy be ensured using groups?", "can redundancy be ensured using groups?", "redundancy be ensured using groups?", "be ensured using groups?", "ensured using groups?", "using groups?", "groups?"], "tokens": ["How", "can", "redundancy", "be", "ensured", "using", "groups", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1806964792", "fields": {"term": "How can group sizes be tuned?", "namespace": "open-p", "hash": -1806964792, "terms": ["How can group sizes be tuned?", "can group sizes be tuned?", "group sizes be tuned?", "sizes be tuned?", "be tuned?", "tuned?"], "tokens": ["How", "can", "group", "sizes", "be", "tuned", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-531117658", "fields": {"term": "What are the benefits of using groups in content clusters?", "namespace": "open-p", "hash": -531117658, "terms": ["What are the benefits of using groups in content clusters?", "are the benefits of using groups in content clusters?", "the benefits of using groups in content clusters?", "benefits of using groups in content clusters?", "of using groups in content clusters?", "using groups in content clusters?", "groups in content clusters?", "in content clusters?", "content clusters?", "clusters?"], "tokens": ["What", "are", "the", "benefits", "of", "using", "groups", "in", "content", "clusters", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1922117360", "fields": {"term": "What is Vespa's elasticity feature?", "namespace": "open-p", "hash": -1922117360, "terms": ["What is Vespa's elasticity feature?", "is Vespa's elasticity feature?", "Vespa's elasticity feature?", "elasticity feature?", "feature?"], "tokens": ["What", "is", "Vespa", "'", "s", "elasticity", "feature", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::109324449", "fields": {"term": "Can topology be changed without disruption?", "namespace": "open-p", "hash": 109324449, "terms": ["Can topology be changed without disruption?", "topology be changed without disruption?", "be changed without disruption?", "changed without disruption?", "without disruption?", "disruption?"], "tokens": ["Can", "topology", "be", "changed", "without", "disruption", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1795753860", "fields": {"term": "How does Vespa auto-redistribute documents?", "namespace": "open-p", "hash": 1795753860, "terms": ["How does Vespa auto-redistribute documents?", "does Vespa auto-redistribute documents?", "Vespa auto-redistribute documents?", "auto-redistribute documents?", "documents?"], "tokens": ["How", "does", "Vespa", "auto", "redistribute", "documents", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2079835351", "fields": {"term": "What is the recommended way to increase capacity?", "namespace": "open-p", "hash": -2079835351, "terms": ["What is the recommended way to increase capacity?", "is the recommended way to increase capacity?", "the recommended way to increase capacity?", "recommended way to increase capacity?", "way to increase capacity?", "to increase capacity?", "increase capacity?", "capacity?"], "tokens": ["What", "is", "the", "recommended", "way", "to", "increase", "capacity", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1289913703", "fields": {"term": "Can current nodes be moved to new groups?", "namespace": "open-p", "hash": -1289913703, "terms": ["Can current nodes be moved to new groups?", "current nodes be moved to new groups?", "nodes be moved to new groups?", "be moved to new groups?", "moved to new groups?", "to new groups?", "new groups?", "groups?"], "tokens": ["Can", "current", "nodes", "be", "moved", "to", "new", "groups", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-927980901", "fields": {"term": "How should nodes be configured when starting from non-grouped?", "namespace": "open-p", "hash": -927980901, "terms": ["How should nodes be configured when starting from non-grouped?", "should nodes be configured when starting from non-grouped?", "nodes be configured when starting from non-grouped?", "be configured when starting from non-grouped?", "configured when starting from non-grouped?", "when starting from non-grouped?", "starting from non-grouped?", "from non-grouped?", "non-grouped?"], "tokens": ["How", "should", "nodes", "be", "configured", "when", "starting", "from", "non", "grouped", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-508252227", "fields": {"term": "How are documents managed in Vespa?", "namespace": "open-p", "hash": -508252227, "terms": ["How are documents managed in Vespa?", "are documents managed in Vespa?", "documents managed in Vespa?", "managed in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["How", "are", "documents", "managed", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2007182364", "fields": {"term": "What happens when bucket replicas are out of sync?", "namespace": "open-p", "hash": -2007182364, "terms": ["What happens when bucket replicas are out of sync?", "happens when bucket replicas are out of sync?", "when bucket replicas are out of sync?", "bucket replicas are out of sync?", "replicas are out of sync?", "are out of sync?", "out of sync?", "of sync?", "sync?"], "tokens": ["What", "happens", "when", "bucket", "replicas", "are", "out", "of", "sync", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::652262825", "fields": {"term": "What are tombstones in Vespa?", "namespace": "open-p", "hash": 652262825, "terms": ["What are tombstones in Vespa?", "are tombstones in Vespa?", "tombstones in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["What", "are", "tombstones", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-496397325", "fields": {"term": "What triggers bucket split or join?", "namespace": "open-p", "hash": -496397325, "terms": ["What triggers bucket split or join?", "triggers bucket split or join?", "bucket split or join?", "split or join?", "or join?", "join?"], "tokens": ["What", "triggers", "bucket", "split", "or", "join", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1026721934", "fields": {"term": "Does Vespa index data in separate buckets?", "namespace": "open-p", "hash": 1026721934, "terms": ["Does Vespa index data in separate buckets?", "Vespa index data in separate buckets?", "index data in separate buckets?", "data in separate buckets?", "in separate buckets?", "separate buckets?", "buckets?"], "tokens": ["Does", "Vespa", "index", "data", "in", "separate", "buckets", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1689983294", "fields": {"term": "What is the role of buckets in content management?", "namespace": "open-p", "hash": -1689983294, "terms": ["What is the role of buckets in content management?", "is the role of buckets in content management?", "the role of buckets in content management?", "role of buckets in content management?", "of buckets in content management?", "buckets in content management?", "in content management?", "content management?", "management?"], "tokens": ["What", "is", "the", "role", "of", "buckets", "in", "content", "management", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-449762194", "fields": {"term": "Where can I read more about buckets in Vespa?", "namespace": "open-p", "hash": -449762194, "terms": ["Where can I read more about buckets in Vespa?", "can I read more about buckets in Vespa?", "I read more about buckets in Vespa?", "read more about buckets in Vespa?", "more about buckets in Vespa?", "about buckets in Vespa?", "buckets in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["Where", "can", "I", "read", "more", "about", "buckets", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-790412288", "fields": {"term": "What is the ideal state distribution algorithm?", "namespace": "open-p", "hash": -790412288, "terms": ["What is the ideal state distribution algorithm?", "is the ideal state distribution algorithm?", "the ideal state distribution algorithm?", "ideal state distribution algorithm?", "state distribution algorithm?", "distribution algorithm?", "algorithm?"], "tokens": ["What", "is", "the", "ideal", "state", "distribution", "algorithm", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1150560076", "fields": {"term": "What algorithm does ideal state distribution use?", "namespace": "open-p", "hash": -1150560076, "terms": ["What algorithm does ideal state distribution use?", "algorithm does ideal state distribution use?", "does ideal state distribution use?", "ideal state distribution use?", "state distribution use?", "distribution use?", "use?"], "tokens": ["What", "algorithm", "does", "ideal", "state", "distribution", "use", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::915675349", "fields": {"term": "What is the CRUSH algorithm?", "namespace": "open-p", "hash": 915675349, "terms": ["What is the CRUSH algorithm?", "is the CRUSH algorithm?", "the CRUSH algorithm?", "CRUSH algorithm?", "algorithm?"], "tokens": ["What", "is", "the", "CRUSH", "algorithm", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1469885151", "fields": {"term": "How are nodes assigned to buckets?", "namespace": "open-p", "hash": -1469885151, "terms": ["How are nodes assigned to buckets?", "are nodes assigned to buckets?", "nodes assigned to buckets?", "assigned to buckets?", "to buckets?", "buckets?"], "tokens": ["How", "are", "nodes", "assigned", "to", "buckets", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1608322673", "fields": {"term": "What is the bucket's ideal state?", "namespace": "open-p", "hash": -1608322673, "terms": ["What is the bucket's ideal state?", "is the bucket's ideal state?", "the bucket's ideal state?", "bucket's ideal state?", "ideal state?", "state?"], "tokens": ["What", "is", "the", "bucket", "'", "s", "ideal", "state", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1582807445", "fields": {"term": "How does ideal state distribution limit document movement?", "namespace": "open-p", "hash": 1582807445, "terms": ["How does ideal state distribution limit document movement?", "does ideal state distribution limit document movement?", "ideal state distribution limit document movement?", "state distribution limit document movement?", "distribution limit document movement?", "limit document movement?", "document movement?", "movement?"], "tokens": ["How", "does", "ideal", "state", "distribution", "limit", "document", "movement", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-451173583", "fields": {"term": "What is the purpose of the random number generator in ideal state distribution?", "namespace": "open-p", "hash": -451173583, "terms": ["What is the purpose of the random number generator in ideal state distribution?", "is the purpose of the random number generator in ideal state distribution?", "the purpose of the random number generator in ideal state distribution?", "purpose of the random number generator in ideal state distribution?", "of the random number generator in ideal state distribution?", "the random number generator in ideal state distribution?", "random number generator in ideal state distribution?", "number generator in ideal state distribution?", "generator in ideal state distribution?", "in ideal state distribution?", "ideal state distribution?", "state distribution?", "distribution?"], "tokens": ["What", "is", "the", "purpose", "of", "the", "random", "number", "generator", "in", "ideal", "state", "distribution", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1917567805", "fields": {"term": "What is the redundancy value used in ideal state distribution?", "namespace": "open-p", "hash": 1917567805, "terms": ["What is the redundancy value used in ideal state distribution?", "is the redundancy value used in ideal state distribution?", "the redundancy value used in ideal state distribution?", "redundancy value used in ideal state distribution?", "value used in ideal state distribution?", "used in ideal state distribution?", "in ideal state distribution?", "ideal state distribution?", "state distribution?", "distribution?"], "tokens": ["What", "is", "the", "redundancy", "value", "used", "in", "ideal", "state", "distribution", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1230541995", "fields": {"term": "How is consistency maintained?", "namespace": "open-p", "hash": 1230541995, "terms": ["How is consistency maintained?", "is consistency maintained?", "consistency maintained?", "maintained?"], "tokens": ["How", "is", "consistency", "maintained", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::819985254", "fields": {"term": "What is inconsistent bucket splitting?", "namespace": "open-p", "hash": 819985254, "terms": ["What is inconsistent bucket splitting?", "is inconsistent bucket splitting?", "inconsistent bucket splitting?", "bucket splitting?", "splitting?"], "tokens": ["What", "is", "inconsistent", "bucket", "splitting", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2110840260", "fields": {"term": "How are checksums calculated?", "namespace": "open-p", "hash": -2110840260, "terms": ["How are checksums calculated?", "are checksums calculated?", "checksums calculated?", "calculated?"], "tokens": ["How", "are", "checksums", "calculated", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1992117903", "fields": {"term": "What happens during inconsistent bucket splitting?", "namespace": "open-p", "hash": -1992117903, "terms": ["What happens during inconsistent bucket splitting?", "happens during inconsistent bucket splitting?", "during inconsistent bucket splitting?", "inconsistent bucket splitting?", "bucket splitting?", "splitting?"], "tokens": ["What", "happens", "during", "inconsistent", "bucket", "splitting", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1879588900", "fields": {"term": "What is the highest maintenance priority?", "namespace": "open-p", "hash": 1879588900, "terms": ["What is the highest maintenance priority?", "is the highest maintenance priority?", "the highest maintenance priority?", "highest maintenance priority?", "maintenance priority?", "priority?"], "tokens": ["What", "is", "the", "highest", "maintenance", "priority", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1044817390", "fields": {"term": "What happens when all buckets are split or joined back?", "namespace": "open-p", "hash": -1044817390, "terms": ["What happens when all buckets are split or joined back?", "happens when all buckets are split or joined back?", "when all buckets are split or joined back?", "all buckets are split or joined back?", "buckets are split or joined back?", "are split or joined back?", "split or joined back?", "or joined back?", "joined back?", "back?"], "tokens": ["What", "happens", "when", "all", "buckets", "are", "split", "or", "joined", "back", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1134874010", "fields": {"term": "What is the 'ready' state?", "namespace": "open-p", "hash": 1134874010, "terms": ["What is the 'ready' state?", "is the 'ready' state?", "the 'ready' state?", "'ready' state?", "state?"], "tokens": ["What", "is", "the", "'", "ready", "'", "state", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-676254193", "fields": {"term": "What is embedding in Vespa?", "namespace": "open-p", "hash": -676254193, "terms": ["What is embedding in Vespa?", "is embedding in Vespa?", "embedding in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["What", "is", "embedding", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-671096542", "fields": {"term": "How are embeddings used for ML serving in Vespa?", "namespace": "open-p", "hash": -671096542, "terms": ["How are embeddings used for ML serving in Vespa?", "are embeddings used for ML serving in Vespa?", "embeddings used for ML serving in Vespa?", "used for ML serving in Vespa?", "for ML serving in Vespa?", "ML serving in Vespa?", "serving in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["How", "are", "embeddings", "used", "for", "ML", "serving", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-368405409", "fields": {"term": "What is the purpose of mapping unstructured data to a vector space?", "namespace": "open-p", "hash": -368405409, "terms": ["What is the purpose of mapping unstructured data to a vector space?", "is the purpose of mapping unstructured data to a vector space?", "the purpose of mapping unstructured data to a vector space?", "purpose of mapping unstructured data to a vector space?", "of mapping unstructured data to a vector space?", "mapping unstructured data to a vector space?", "unstructured data to a vector space?", "data to a vector space?", "to a vector space?", "a vector space?", "vector space?", "space?"], "tokens": ["What", "is", "the", "purpose", "of", "mapping", "unstructured", "data", "to", "a", "vector", "space", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1327408222", "fields": {"term": "How are vectors used as input in neural nets?", "namespace": "open-p", "hash": -1327408222, "terms": ["How are vectors used as input in neural nets?", "are vectors used as input in neural nets?", "vectors used as input in neural nets?", "used as input in neural nets?", "as input in neural nets?", "input in neural nets?", "in neural nets?", "neural nets?", "nets?"], "tokens": ["How", "are", "vectors", "used", "as", "input", "in", "neural", "nets", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-35319540", "fields": {"term": "What are Vespa embedders?", "namespace": "open-p", "hash": -35319540, "terms": ["What are Vespa embedders?", "are Vespa embedders?", "Vespa embedders?", "embedders?"], "tokens": ["What", "are", "Vespa", "embedders", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1265166238", "fields": {"term": "How does Vespa reduce latency?", "namespace": "open-p", "hash": -1265166238, "terms": ["How does Vespa reduce latency?", "does Vespa reduce latency?", "Vespa reduce latency?", "reduce latency?", "latency?"], "tokens": ["How", "does", "Vespa", "reduce", "latency", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1964567142", "fields": {"term": "Why use Vespa embedder functionality?", "namespace": "open-p", "hash": -1964567142, "terms": ["Why use Vespa embedder functionality?", "use Vespa embedder functionality?", "Vespa embedder functionality?", "embedder functionality?", "functionality?"], "tokens": ["Why", "use", "Vespa", "embedder", "functionality", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1634045104", "fields": {"term": "What is an embedder?", "namespace": "open-p", "hash": -1634045104, "terms": ["What is an embedder?", "is an embedder?", "an embedder?", "embedder?"], "tokens": ["What", "is", "an", "embedder", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::-1669566771", "fields": {"term": "What is WordPiece?", "namespace": "open-p", "hash": -1669566771, "terms": ["What is WordPiece?", "is WordPiece?", "WordPiece?"], "tokens": ["What", "is", "WordPiece", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::1927269882", "fields": {"term": "How to export a sentence-transformer model to ONNX format?", "namespace": "open-p", "hash": 1927269882, "terms": ["How to export a sentence-transformer model to ONNX format?", "to export a sentence-transformer model to ONNX format?", "export a sentence-transformer model to ONNX format?", "a sentence-transformer model to ONNX format?", "sentence-transformer model to ONNX format?", "model to ONNX format?", "to ONNX format?", "ONNX format?", "format?"], "tokens": ["How", "to", "export", "a", "sentence", "transformer", "model", "to", "ONNX", "format", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-560936067", "fields": {"term": "What is the BertBase embedder?", "namespace": "open-p", "hash": -560936067, "terms": ["What is the BertBase embedder?", "is the BertBase embedder?", "the BertBase embedder?", "BertBase embedder?", "embedder?"], "tokens": ["What", "is", "the", "BertBase", "embedder", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1091801187", "fields": {"term": "How to configure the BertBase embedder?", "namespace": "open-p", "hash": -1091801187, "terms": ["How to configure the BertBase embedder?", "to configure the BertBase embedder?", "configure the BertBase embedder?", "the BertBase embedder?", "BertBase embedder?", "embedder?"], "tokens": ["How", "to", "configure", "the", "BertBase", "embedder", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1752417461", "fields": {"term": "What pooling strategy does BertBase embedder use by default?", "namespace": "open-p", "hash": 1752417461, "terms": ["What pooling strategy does BertBase embedder use by default?", "pooling strategy does BertBase embedder use by default?", "strategy does BertBase embedder use by default?", "does BertBase embedder use by default?", "BertBase embedder use by default?", "embedder use by default?", "use by default?", "by default?", "default?"], "tokens": ["What", "pooling", "strategy", "does", "BertBase", "embedder", "use", "by", "default", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1949977933", "fields": {"term": "How to supply model files for the BertBase embedder?", "namespace": "open-p", "hash": -1949977933, "terms": ["How to supply model files for the BertBase embedder?", "to supply model files for the BertBase embedder?", "supply model files for the BertBase embedder?", "model files for the BertBase embedder?", "files for the BertBase embedder?", "for the BertBase embedder?", "the BertBase embedder?", "BertBase embedder?", "embedder?"], "tokens": ["How", "to", "supply", "model", "files", "for", "the", "BertBase", "embedder", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1475001221", "fields": {"term": "What is SentencePiece?", "namespace": "open-p", "hash": -1475001221, "terms": ["What is SentencePiece?", "is SentencePiece?", "SentencePiece?"], "tokens": ["What", "is", "SentencePiece", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1363259811", "fields": {"term": "What does SentencePiece do?", "namespace": "open-p", "hash": -1363259811, "terms": ["What does SentencePiece do?", "does SentencePiece do?", "SentencePiece do?", "do?"], "tokens": ["What", "does", "SentencePiece", "do", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1501692891", "fields": {"term": "What languages does SentencePiece support?", "namespace": "open-p", "hash": 1501692891, "terms": ["What languages does SentencePiece support?", "languages does SentencePiece support?", "does SentencePiece support?", "SentencePiece support?", "support?"], "tokens": ["What", "languages", "does", "SentencePiece", "support", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::607133288", "fields": {"term": "How is SentencePiece implemented?", "namespace": "open-p", "hash": 607133288, "terms": ["How is SentencePiece implemented?", "is SentencePiece implemented?", "SentencePiece implemented?", "implemented?"], "tokens": ["How", "is", "SentencePiece", "implemented", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1623313209", "fields": {"term": "How can SentencePiece be used in Vespa?", "namespace": "open-p", "hash": 1623313209, "terms": ["How can SentencePiece be used in Vespa?", "can SentencePiece be used in Vespa?", "SentencePiece be used in Vespa?", "be used in Vespa?", "used in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["How", "can", "SentencePiece", "be", "used", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-960904244", "fields": {"term": "What is the configuration definition of SentencePiece?", "namespace": "open-p", "hash": -960904244, "terms": ["What is the configuration definition of SentencePiece?", "is the configuration definition of SentencePiece?", "the configuration definition of SentencePiece?", "configuration definition of SentencePiece?", "definition of SentencePiece?", "of SentencePiece?", "SentencePiece?"], "tokens": ["What", "is", "the", "configuration", "definition", "of", "SentencePiece", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1866250574", "fields": {"term": "What is the SentencePiece embedder?", "namespace": "open-p", "hash": -1866250574, "terms": ["What is the SentencePiece embedder?", "is the SentencePiece embedder?", "the SentencePiece embedder?", "SentencePiece embedder?", "embedder?"], "tokens": ["What", "is", "the", "SentencePiece", "embedder", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1493366710", "fields": {"term": "Where can I find the configuration definition of SentencePiece?", "namespace": "open-p", "hash": -1493366710, "terms": ["Where can I find the configuration definition of SentencePiece?", "can I find the configuration definition of SentencePiece?", "I find the configuration definition of SentencePiece?", "find the configuration definition of SentencePiece?", "the configuration definition of SentencePiece?", "configuration definition of SentencePiece?", "definition of SentencePiece?", "of SentencePiece?", "SentencePiece?"], "tokens": ["Where", "can", "I", "find", "the", "configuration", "definition", "of", "SentencePiece", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::163423696", "fields": {"term": "How do you use WordPiece in Vespa?", "namespace": "open-p", "hash": 163423696, "terms": ["How do you use WordPiece in Vespa?", "do you use WordPiece in Vespa?", "you use WordPiece in Vespa?", "use WordPiece in Vespa?", "WordPiece in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["How", "do", "you", "use", "WordPiece", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1336265638", "fields": {"term": "What can you use the WordPiece tensor for?", "namespace": "open-p", "hash": 1336265638, "terms": ["What can you use the WordPiece tensor for?", "can you use the WordPiece tensor for?", "you use the WordPiece tensor for?", "use the WordPiece tensor for?", "the WordPiece tensor for?", "WordPiece tensor for?", "tensor for?", "for?"], "tokens": ["What", "can", "you", "use", "the", "WordPiece", "tensor", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-922118035", "fields": {"term": "How is an embedder used?", "namespace": "open-p", "hash": -922118035, "terms": ["How is an embedder used?", "is an embedder used?", "an embedder used?", "embedder used?", "used?"], "tokens": ["How", "is", "an", "embedder", "used", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::301264963", "fields": {"term": "How do you embed text?", "namespace": "open-p", "hash": 301264963, "terms": ["How do you embed text?", "do you embed text?", "you embed text?", "embed text?", "text?"], "tokens": ["How", "do", "you", "embed", "text", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1827074895", "fields": {"term": "What is the format for embedding a text?", "namespace": "open-p", "hash": -1827074895, "terms": ["What is the format for embedding a text?", "is the format for embedding a text?", "the format for embedding a text?", "format for embedding a text?", "for embedding a text?", "embedding a text?", "a text?", "text?"], "tokens": ["What", "is", "the", "format", "for", "embedding", "a", "text", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-918808270", "fields": {"term": "What is the output of the embed function?", "namespace": "open-p", "hash": -918808270, "terms": ["What is the output of the embed function?", "is the output of the embed function?", "the output of the embed function?", "output of the embed function?", "of the embed function?", "the embed function?", "embed function?", "function?"], "tokens": ["What", "is", "the", "output", "of", "the", "embed", "function", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1198638543", "fields": {"term": "Can multiple embedders be used?", "namespace": "open-p", "hash": 1198638543, "terms": ["Can multiple embedders be used?", "multiple embedders be used?", "embedders be used?", "be used?", "used?"], "tokens": ["Can", "multiple", "embedders", "be", "used", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::275590001", "fields": {"term": "What is an example of using multiple embedders?", "namespace": "open-p", "hash": 275590001, "terms": ["What is an example of using multiple embedders?", "is an example of using multiple embedders?", "an example of using multiple embedders?", "example of using multiple embedders?", "of using multiple embedders?", "using multiple embedders?", "multiple embedders?", "embedders?"], "tokens": ["What", "is", "an", "example", "of", "using", "multiple", "embedders", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1985041281", "fields": {"term": "What is the format for a Vespa query with embedding?", "namespace": "open-p", "hash": 1985041281, "terms": ["What is the format for a Vespa query with embedding?", "is the format for a Vespa query with embedding?", "the format for a Vespa query with embedding?", "format for a Vespa query with embedding?", "for a Vespa query with embedding?", "a Vespa query with embedding?", "Vespa query with embedding?", "query with embedding?", "with embedding?", "embedding?"], "tokens": ["What", "is", "the", "format", "for", "a", "Vespa", "query", "with", "embedding", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1181795976", "fields": {"term": "How can I convert string fields into embedding vectors?", "namespace": "open-p", "hash": 1181795976, "terms": ["How can I convert string fields into embedding vectors?", "can I convert string fields into embedding vectors?", "I convert string fields into embedding vectors?", "convert string fields into embedding vectors?", "string fields into embedding vectors?", "fields into embedding vectors?", "into embedding vectors?", "embedding vectors?", "vectors?"], "tokens": ["How", "can", "I", "convert", "string", "fields", "into", "embedding", "vectors", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::118082565", "fields": {"term": "What is the embed function?", "namespace": "open-p", "hash": 118082565, "terms": ["What is the embed function?", "is the embed function?", "the embed function?", "embed function?", "function?"], "tokens": ["What", "is", "the", "embed", "function", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::700167403", "fields": {"term": "Can I use multiple input fields for embedding vectors?", "namespace": "open-p", "hash": 700167403, "terms": ["Can I use multiple input fields for embedding vectors?", "I use multiple input fields for embedding vectors?", "use multiple input fields for embedding vectors?", "multiple input fields for embedding vectors?", "input fields for embedding vectors?", "fields for embedding vectors?", "for embedding vectors?", "embedding vectors?", "vectors?"], "tokens": ["Can", "I", "use", "multiple", "input", "fields", "for", "embedding", "vectors", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1698488743", "fields": {"term": "How are array indexes used for embeddings?", "namespace": "open-p", "hash": 1698488743, "terms": ["How are array indexes used for embeddings?", "are array indexes used for embeddings?", "array indexes used for embeddings?", "indexes used for embeddings?", "used for embeddings?", "for embeddings?", "embeddings?"], "tokens": ["How", "are", "array", "indexes", "used", "for", "embeddings", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2032931407", "fields": {"term": "How do I store produced vector embeddings?", "namespace": "open-p", "hash": 2032931407, "terms": ["How do I store produced vector embeddings?", "do I store produced vector embeddings?", "I store produced vector embeddings?", "store produced vector embeddings?", "produced vector embeddings?", "vector embeddings?", "embeddings?"], "tokens": ["How", "do", "I", "store", "produced", "vector", "embeddings", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::554455536", "fields": {"term": "What is ComponentRegistry?", "namespace": "open-p", "hash": 554455536, "terms": ["What is ComponentRegistry?", "is ComponentRegistry?", "ComponentRegistry?"], "tokens": ["What", "is", "ComponentRegistry", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::1831755088", "fields": {"term": "How to use embedders in Java components?", "namespace": "open-p", "hash": 1831755088, "terms": ["How to use embedders in Java components?", "to use embedders in Java components?", "use embedders in Java components?", "embedders in Java components?", "in Java components?", "Java components?", "components?"], "tokens": ["How", "to", "use", "embedders", "in", "Java", "components", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::436791898", "fields": {"term": "What is com.yahoo.language.process.Embedder?", "namespace": "open-p", "hash": 436791898, "terms": ["What is com.yahoo.language.process.Embedder?", "is com.yahoo.language.process.Embedder?", "com.yahoo.language.process.Embedder?"], "tokens": ["What", "is", "com", "yahoo", "language", "process", "Embedder", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1529530162", "fields": {"term": "How can I define a custom embedder?", "namespace": "open-p", "hash": -1529530162, "terms": ["How can I define a custom embedder?", "can I define a custom embedder?", "I define a custom embedder?", "define a custom embedder?", "a custom embedder?", "custom embedder?", "embedder?"], "tokens": ["How", "can", "I", "define", "a", "custom", "embedder", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-616985015", "fields": {"term": "What is simple-semantic-search?", "namespace": "open-p", "hash": -616985015, "terms": ["What is simple-semantic-search?", "is simple-semantic-search?", "simple-semantic-search?"], "tokens": ["What", "is", "simple", "semantic", "search", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::1498186566", "fields": {"term": "What is commerce-product-ranking?", "namespace": "open-p", "hash": 1498186566, "terms": ["What is commerce-product-ranking?", "is commerce-product-ranking?", "commerce-product-ranking?"], "tokens": ["What", "is", "commerce", "product", "ranking", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1580147374", "fields": {"term": "What is embedding?", "namespace": "open-p", "hash": 1580147374, "terms": ["What is embedding?", "is embedding?", "embedding?"], "tokens": ["What", "is", "embedding", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1562474932", "fields": {"term": "Where can I find the embedders example?", "namespace": "open-p", "hash": 1562474932, "terms": ["Where can I find the embedders example?", "can I find the embedders example?", "I find the embedders example?", "find the embedders example?", "the embedders example?", "embedders example?", "example?"], "tokens": ["Where", "can", "I", "find", "the", "embedders", "example", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1037657548", "fields": {"term": "What is the sample application for multiple embedders?", "namespace": "open-p", "hash": -1037657548, "terms": ["What is the sample application for multiple embedders?", "is the sample application for multiple embedders?", "the sample application for multiple embedders?", "sample application for multiple embedders?", "application for multiple embedders?", "for multiple embedders?", "multiple embedders?", "embedders?"], "tokens": ["What", "is", "the", "sample", "application", "for", "multiple", "embedders", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::965962109", "fields": {"term": "What do the sample applications demonstrate?", "namespace": "open-p", "hash": 965962109, "terms": ["What do the sample applications demonstrate?", "do the sample applications demonstrate?", "the sample applications demonstrate?", "sample applications demonstrate?", "applications demonstrate?", "demonstrate?"], "tokens": ["What", "do", "the", "sample", "applications", "demonstrate", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1575704189", "fields": {"term": "What is a model signature?", "namespace": "open-p", "hash": 1575704189, "terms": ["What is a model signature?", "is a model signature?", "a model signature?", "model signature?", "signature?"], "tokens": ["What", "is", "a", "model", "signature", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-263458728", "fields": {"term": "How to load ONNX models?", "namespace": "open-p", "hash": -263458728, "terms": ["How to load ONNX models?", "to load ONNX models?", "load ONNX models?", "ONNX models?", "models?"], "tokens": ["How", "to", "load", "ONNX", "models", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::24304669", "fields": {"term": "How to export sentence-transformer model to ONNX format?", "namespace": "open-p", "hash": 24304669, "terms": ["How to export sentence-transformer model to ONNX format?", "to export sentence-transformer model to ONNX format?", "export sentence-transformer model to ONNX format?", "sentence-transformer model to ONNX format?", "model to ONNX format?", "to ONNX format?", "ONNX format?", "format?"], "tokens": ["How", "to", "export", "sentence", "transformer", "model", "to", "ONNX", "format", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-20037851", "fields": {"term": "What are common issues when loading ONNX models?", "namespace": "open-p", "hash": -20037851, "terms": ["What are common issues when loading ONNX models?", "are common issues when loading ONNX models?", "common issues when loading ONNX models?", "issues when loading ONNX models?", "when loading ONNX models?", "loading ONNX models?", "ONNX models?", "models?"], "tokens": ["What", "are", "common", "issues", "when", "loading", "ONNX", "models", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1808474795", "fields": {"term": "How to analyze model signature using Vespa?", "namespace": "open-p", "hash": 1808474795, "terms": ["How to analyze model signature using Vespa?", "to analyze model signature using Vespa?", "analyze model signature using Vespa?", "model signature using Vespa?", "signature using Vespa?", "using Vespa?", "Vespa?"], "tokens": ["How", "to", "analyze", "model", "signature", "using", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-878493624", "fields": {"term": "What happens if the model contains incorrect input or output signatures?", "namespace": "open-p", "hash": -878493624, "terms": ["What happens if the model contains incorrect input or output signatures?", "happens if the model contains incorrect input or output signatures?", "if the model contains incorrect input or output signatures?", "the model contains incorrect input or output signatures?", "model contains incorrect input or output signatures?", "contains incorrect input or output signatures?", "incorrect input or output signatures?", "input or output signatures?", "or output signatures?", "output signatures?", "signatures?"], "tokens": ["What", "happens", "if", "the", "model", "contains", "incorrect", "input", "or", "output", "signatures", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::843718015", "fields": {"term": "What are the causes?", "namespace": "open-p", "hash": 843718015, "terms": ["What are the causes?", "are the causes?", "the causes?", "causes?"], "tokens": ["What", "are", "the", "causes", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-191575307", "fields": {"term": "What is a sub-query?", "namespace": "open-p", "hash": -191575307, "terms": ["What is a sub-query?", "is a sub-query?", "a sub-query?", "sub-query?"], "tokens": ["What", "is", "a", "sub", "query", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1430487675", "fields": {"term": "Is efficient searching possible for arrays?", "namespace": "open-p", "hash": 1430487675, "terms": ["Is efficient searching possible for arrays?", "efficient searching possible for arrays?", "searching possible for arrays?", "possible for arrays?", "for arrays?", "arrays?"], "tokens": ["Is", "efficient", "searching", "possible", "for", "arrays", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1863985035", "fields": {"term": "Is there a way to query for not having a value?", "namespace": "open-p", "hash": 1863985035, "terms": ["Is there a way to query for not having a value?", "there a way to query for not having a value?", "a way to query for not having a value?", "way to query for not having a value?", "to query for not having a value?", "query for not having a value?", "for not having a value?", "not having a value?", "having a value?", "a value?", "value?"], "tokens": ["Is", "there", "a", "way", "to", "query", "for", "not", "having", "a", "value", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-352483310", "fields": {"term": "How does the visiting API support queries?", "namespace": "open-p", "hash": -352483310, "terms": ["How does the visiting API support queries?", "does the visiting API support queries?", "the visiting API support queries?", "visiting API support queries?", "API support queries?", "support queries?", "queries?"], "tokens": ["How", "does", "the", "visiting", "API", "support", "queries", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1942976", "fields": {"term": "What happens to expired documents?", "namespace": "open-p", "hash": -1942976, "terms": ["What happens to expired documents?", "happens to expired documents?", "to expired documents?", "expired documents?", "documents?"], "tokens": ["What", "happens", "to", "expired", "documents", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1627198624", "fields": {"term": "What is com.yahoo.documentapi.DocumentAccess?", "namespace": "open-p", "hash": -1627198624, "terms": ["What is com.yahoo.documentapi.DocumentAccess?", "is com.yahoo.documentapi.DocumentAccess?", "com.yahoo.documentapi.DocumentAccess?"], "tokens": ["What", "is", "com", "yahoo", "documentapi", "DocumentAccess", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1041297345", "fields": {"term": "How to get documents by id?", "namespace": "open-p", "hash": -1041297345, "terms": ["How to get documents by id?", "to get documents by id?", "get documents by id?", "documents by id?", "by id?", "id?"], "tokens": ["How", "to", "get", "documents", "by", "id", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1484460712", "fields": {"term": "How to use DocumentAccess component?", "namespace": "open-p", "hash": 1484460712, "terms": ["How to use DocumentAccess component?", "to use DocumentAccess component?", "use DocumentAccess component?", "DocumentAccess component?", "component?"], "tokens": ["How", "to", "use", "DocumentAccess", "component", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::686442803", "fields": {"term": "Why is topology important for Vespa?", "namespace": "open-p", "hash": 686442803, "terms": ["Why is topology important for Vespa?", "is topology important for Vespa?", "topology important for Vespa?", "important for Vespa?", "for Vespa?", "Vespa?"], "tokens": ["Why", "is", "topology", "important", "for", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1606864960", "fields": {"term": "How to check Vespa container errors?", "namespace": "open-p", "hash": 1606864960, "terms": ["How to check Vespa container errors?", "to check Vespa container errors?", "check Vespa container errors?", "Vespa container errors?", "container errors?", "errors?"], "tokens": ["How", "to", "check", "Vespa", "container", "errors", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-411834317", "fields": {"term": "What makes Docker run fail on M1?", "namespace": "open-p", "hash": -411834317, "terms": ["What makes Docker run fail on M1?", "makes Docker run fail on M1?", "Docker run fail on M1?", "run fail on M1?", "fail on M1?", "on M1?", "M1?"], "tokens": ["What", "makes", "Docker", "run", "fail", "on", "M1", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-255838130", "fields": {"term": "How to fix Docker on M1?", "namespace": "open-p", "hash": -255838130, "terms": ["How to fix Docker on M1?", "to fix Docker on M1?", "fix Docker on M1?", "Docker on M1?", "on M1?", "M1?"], "tokens": ["How", "to", "fix", "Docker", "on", "M1", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1047720447", "fields": {"term": "What is ZooKeeper quorum?", "namespace": "open-p", "hash": -1047720447, "terms": ["What is ZooKeeper quorum?", "is ZooKeeper quorum?", "ZooKeeper quorum?", "quorum?"], "tokens": ["What", "is", "ZooKeeper", "quorum", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::356636538", "fields": {"term": "How to validate container memory?", "namespace": "open-p", "hash": 356636538, "terms": ["How to validate container memory?", "to validate container memory?", "validate container memory?", "container memory?", "memory?"], "tokens": ["How", "to", "validate", "container", "memory", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::239795329", "fields": {"term": "Why does the Config Server cluster fail to start?", "namespace": "open-p", "hash": 239795329, "terms": ["Why does the Config Server cluster fail to start?", "does the Config Server cluster fail to start?", "the Config Server cluster fail to start?", "Config Server cluster fail to start?", "Server cluster fail to start?", "cluster fail to start?", "fail to start?", "to start?", "start?"], "tokens": ["Why", "does", "the", "Config", "Server", "cluster", "fail", "to", "start", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1664271980", "fields": {"term": "What is the role of ZooKeeper in Config Server cluster?", "namespace": "open-p", "hash": 1664271980, "terms": ["What is the role of ZooKeeper in Config Server cluster?", "is the role of ZooKeeper in Config Server cluster?", "the role of ZooKeeper in Config Server cluster?", "role of ZooKeeper in Config Server cluster?", "of ZooKeeper in Config Server cluster?", "ZooKeeper in Config Server cluster?", "in Config Server cluster?", "Config Server cluster?", "Server cluster?", "cluster?"], "tokens": ["What", "is", "the", "role", "of", "ZooKeeper", "in", "Config", "Server", "cluster", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1152867888", "fields": {"term": "How does the ZooKeeper cluster wait for hosts on the network?", "namespace": "open-p", "hash": 1152867888, "terms": ["How does the ZooKeeper cluster wait for hosts on the network?", "does the ZooKeeper cluster wait for hosts on the network?", "the ZooKeeper cluster wait for hosts on the network?", "ZooKeeper cluster wait for hosts on the network?", "cluster wait for hosts on the network?", "wait for hosts on the network?", "for hosts on the network?", "hosts on the network?", "on the network?", "the network?", "network?"], "tokens": ["How", "does", "the", "ZooKeeper", "cluster", "wait", "for", "hosts", "on", "the", "network", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1676843982", "fields": {"term": "What is catch 22 in the context of Config Server cluster?", "namespace": "open-p", "hash": -1676843982, "terms": ["What is catch 22 in the context of Config Server cluster?", "is catch 22 in the context of Config Server cluster?", "catch 22 in the context of Config Server cluster?", "22 in the context of Config Server cluster?", "in the context of Config Server cluster?", "the context of Config Server cluster?", "context of Config Server cluster?", "of Config Server cluster?", "Config Server cluster?", "Server cluster?", "cluster?"], "tokens": ["What", "is", "catch", "22", "in", "the", "context", "of", "Config", "Server", "cluster", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2107464888", "fields": {"term": "Where can I find troubleshooting guidelines for Config Server cluster?", "namespace": "open-p", "hash": -2107464888, "terms": ["Where can I find troubleshooting guidelines for Config Server cluster?", "can I find troubleshooting guidelines for Config Server cluster?", "I find troubleshooting guidelines for Config Server cluster?", "find troubleshooting guidelines for Config Server cluster?", "troubleshooting guidelines for Config Server cluster?", "guidelines for Config Server cluster?", "for Config Server cluster?", "Config Server cluster?", "Server cluster?", "cluster?"], "tokens": ["Where", "can", "I", "find", "troubleshooting", "guidelines", "for", "Config", "Server", "cluster", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::987022303", "fields": {"term": "What is sampleapp troubleshooting?", "namespace": "open-p", "hash": 987022303, "terms": ["What is sampleapp troubleshooting?", "is sampleapp troubleshooting?", "sampleapp troubleshooting?", "troubleshooting?"], "tokens": ["What", "is", "sampleapp", "troubleshooting", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::368327154", "fields": {"term": "What is the main focus?", "namespace": "open-p", "hash": 368327154, "terms": ["What is the main focus?", "is the main focus?", "the main focus?", "main focus?", "focus?"], "tokens": ["What", "is", "the", "main", "focus", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1471413728", "fields": {"term": "What is container tuning?", "namespace": "open-p", "hash": 1471413728, "terms": ["What is container tuning?", "is container tuning?", "container tuning?", "tuning?"], "tokens": ["What", "is", "container", "tuning", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1366529845", "fields": {"term": "What does the attribute documentation summarize?", "namespace": "open-p", "hash": -1366529845, "terms": ["What does the attribute documentation summarize?", "does the attribute documentation summarize?", "the attribute documentation summarize?", "attribute documentation summarize?", "documentation summarize?", "summarize?"], "tokens": ["What", "does", "the", "attribute", "documentation", "summarize", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1688884808", "fields": {"term": "What is the procedure for changing attribute to index and vice versa?", "namespace": "open-p", "hash": -1688884808, "terms": ["What is the procedure for changing attribute to index and vice versa?", "is the procedure for changing attribute to index and vice versa?", "the procedure for changing attribute to index and vice versa?", "procedure for changing attribute to index and vice versa?", "for changing attribute to index and vice versa?", "changing attribute to index and vice versa?", "attribute to index and vice versa?", "to index and vice versa?", "index and vice versa?", "and vice versa?", "vice versa?", "versa?"], "tokens": ["What", "is", "the", "procedure", "for", "changing", "attribute", "to", "index", "and", "vice", "versa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-614435065", "fields": {"term": "What does fast-search enable?", "namespace": "open-p", "hash": -614435065, "terms": ["What does fast-search enable?", "does fast-search enable?", "fast-search enable?", "enable?"], "tokens": ["What", "does", "fast", "search", "enable", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1160323304", "fields": {"term": "When to use fast-search?", "namespace": "open-p", "hash": 1160323304, "terms": ["When to use fast-search?", "to use fast-search?", "use fast-search?", "fast-search?"], "tokens": ["When", "to", "use", "fast", "search", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1061614511", "fields": {"term": "What is a hit count estimate?", "namespace": "open-p", "hash": 1061614511, "terms": ["What is a hit count estimate?", "is a hit count estimate?", "a hit count estimate?", "hit count estimate?", "count estimate?", "estimate?"], "tokens": ["What", "is", "a", "hit", "count", "estimate", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::441909274", "fields": {"term": "How does Vespa build query execution plan?", "namespace": "open-p", "hash": 441909274, "terms": ["How does Vespa build query execution plan?", "does Vespa build query execution plan?", "Vespa build query execution plan?", "build query execution plan?", "query execution plan?", "execution plan?", "plan?"], "tokens": ["How", "does", "Vespa", "build", "query", "execution", "plan", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1823956867", "fields": {"term": "What is the impact of using fast-search?", "namespace": "open-p", "hash": 1823956867, "terms": ["What is the impact of using fast-search?", "is the impact of using fast-search?", "the impact of using fast-search?", "impact of using fast-search?", "of using fast-search?", "using fast-search?", "fast-search?"], "tokens": ["What", "is", "the", "impact", "of", "using", "fast", "search", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1016487216", "fields": {"term": "How to change fast-search aspect of an attribute?", "namespace": "open-p", "hash": -1016487216, "terms": ["How to change fast-search aspect of an attribute?", "to change fast-search aspect of an attribute?", "change fast-search aspect of an attribute?", "fast-search aspect of an attribute?", "aspect of an attribute?", "of an attribute?", "an attribute?", "attribute?"], "tokens": ["How", "to", "change", "fast", "search", "aspect", "of", "an", "attribute", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1723588072", "fields": {"term": "What is the optimization for sorting on a single-value numeric attribute?", "namespace": "open-p", "hash": 1723588072, "terms": ["What is the optimization for sorting on a single-value numeric attribute?", "is the optimization for sorting on a single-value numeric attribute?", "the optimization for sorting on a single-value numeric attribute?", "optimization for sorting on a single-value numeric attribute?", "for sorting on a single-value numeric attribute?", "sorting on a single-value numeric attribute?", "on a single-value numeric attribute?", "a single-value numeric attribute?", "single-value numeric attribute?", "numeric attribute?", "attribute?"], "tokens": ["What", "is", "the", "optimization", "for", "sorting", "on", "a", "single", "value", "numeric", "attribute", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-636730397", "fields": {"term": "What is the default match mode for string fields?", "namespace": "open-p", "hash": -636730397, "terms": ["What is the default match mode for string fields?", "is the default match mode for string fields?", "the default match mode for string fields?", "default match mode for string fields?", "match mode for string fields?", "mode for string fields?", "for string fields?", "string fields?", "fields?"], "tokens": ["What", "is", "the", "default", "match", "mode", "for", "string", "fields", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::615556040", "fields": {"term": "What happens when Vespa indexes a UUID as tokens?", "namespace": "open-p", "hash": 615556040, "terms": ["What happens when Vespa indexes a UUID as tokens?", "happens when Vespa indexes a UUID as tokens?", "when Vespa indexes a UUID as tokens?", "Vespa indexes a UUID as tokens?", "indexes a UUID as tokens?", "a UUID as tokens?", "UUID as tokens?", "as tokens?", "tokens?"], "tokens": ["What", "happens", "when", "Vespa", "indexes", "a", "UUID", "as", "tokens", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1709335759", "fields": {"term": "How can you configure a field to treat UUID as a single token?", "namespace": "open-p", "hash": -1709335759, "terms": ["How can you configure a field to treat UUID as a single token?", "can you configure a field to treat UUID as a single token?", "you configure a field to treat UUID as a single token?", "configure a field to treat UUID as a single token?", "a field to treat UUID as a single token?", "field to treat UUID as a single token?", "to treat UUID as a single token?", "treat UUID as a single token?", "UUID as a single token?", "as a single token?", "a single token?", "single token?", "token?"], "tokens": ["How", "can", "you", "configure", "a", "field", "to", "treat", "UUID", "as", "a", "single", "token", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::221949683", "fields": {"term": "What is the rank:filter setting?", "namespace": "open-p", "hash": 221949683, "terms": ["What is the rank:filter setting?", "is the rank:filter setting?", "the rank:filter setting?", "rank:filter setting?", "setting?"], "tokens": ["What", "is", "the", "rank", ":", "filter", "setting", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2105421", "fields": {"term": "What does visibility-delay do?", "namespace": "open-p", "hash": -2105421, "terms": ["What does visibility-delay do?", "does visibility-delay do?", "visibility-delay do?", "do?"], "tokens": ["What", "does", "visibility", "delay", "do", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1236253277", "fields": {"term": "How can imported attribute fields be searched?", "namespace": "open-p", "hash": 1236253277, "terms": ["How can imported attribute fields be searched?", "can imported attribute fields be searched?", "imported attribute fields be searched?", "attribute fields be searched?", "fields be searched?", "be searched?", "searched?"], "tokens": ["How", "can", "imported", "attribute", "fields", "be", "searched", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1971710544", "fields": {"term": "What is the impact of rank:filter?", "namespace": "open-p", "hash": -1971710544, "terms": ["What is the impact of rank:filter?", "is the impact of rank:filter?", "the impact of rank:filter?", "impact of rank:filter?", "of rank:filter?", "rank:filter?"], "tokens": ["What", "is", "the", "impact", "of", "rank", ":", "filter", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::454669283", "fields": {"term": "What is the importance of visibility-delay?", "namespace": "open-p", "hash": 454669283, "terms": ["What is the importance of visibility-delay?", "is the importance of visibility-delay?", "the importance of visibility-delay?", "importance of visibility-delay?", "of visibility-delay?", "visibility-delay?"], "tokens": ["What", "is", "the", "importance", "of", "visibility", "delay", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::785512266", "fields": {"term": "How does ranking impact posting list granularity?", "namespace": "open-p", "hash": 785512266, "terms": ["How does ranking impact posting list granularity?", "does ranking impact posting list granularity?", "ranking impact posting list granularity?", "impact posting list granularity?", "posting list granularity?", "list granularity?", "granularity?"], "tokens": ["How", "does", "ranking", "impact", "posting", "list", "granularity", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1149672037", "fields": {"term": "What is the first-phase ranking function?", "namespace": "open-p", "hash": 1149672037, "terms": ["What is the first-phase ranking function?", "is the first-phase ranking function?", "the first-phase ranking function?", "first-phase ranking function?", "ranking function?", "function?"], "tokens": ["What", "is", "the", "first", "phase", "ranking", "function", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2115701461", "fields": {"term": "Why use the second phase ranking step?", "namespace": "open-p", "hash": 2115701461, "terms": ["Why use the second phase ranking step?", "use the second phase ranking step?", "the second phase ranking step?", "second phase ranking step?", "phase ranking step?", "ranking step?", "step?"], "tokens": ["Why", "use", "the", "second", "phase", "ranking", "step", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1622607759", "fields": {"term": "What is the inner scoring function?", "namespace": "open-p", "hash": -1622607759, "terms": ["What is the inner scoring function?", "is the inner scoring function?", "the inner scoring function?", "inner scoring function?", "scoring function?", "function?"], "tokens": ["What", "is", "the", "inner", "scoring", "function", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1211797751", "fields": {"term": "How does WAND operator find top k documents?", "namespace": "open-p", "hash": 1211797751, "terms": ["How does WAND operator find top k documents?", "does WAND operator find top k documents?", "WAND operator find top k documents?", "operator find top k documents?", "find top k documents?", "top k documents?", "k documents?", "documents?"], "tokens": ["How", "does", "WAND", "operator", "find", "top", "k", "documents", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-73833308", "fields": {"term": "What is set filter?", "namespace": "open-p", "hash": -73833308, "terms": ["What is set filter?", "is set filter?", "set filter?", "filter?"], "tokens": ["What", "is", "set", "filter", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1887923465", "fields": {"term": "What is summary cache?", "namespace": "open-p", "hash": 1887923465, "terms": ["What is summary cache?", "is summary cache?", "summary cache?", "cache?"], "tokens": ["What", "is", "summary", "cache", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::-1659953542", "fields": {"term": "What are explicit document summaries?", "namespace": "open-p", "hash": -1659953542, "terms": ["What are explicit document summaries?", "are explicit document summaries?", "explicit document summaries?", "document summaries?", "summaries?"], "tokens": ["What", "are", "explicit", "document", "summaries", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::795465675", "fields": {"term": "What is the benefit of dedicated in-memory summaries?", "namespace": "open-p", "hash": 795465675, "terms": ["What is the benefit of dedicated in-memory summaries?", "is the benefit of dedicated in-memory summaries?", "the benefit of dedicated in-memory summaries?", "benefit of dedicated in-memory summaries?", "of dedicated in-memory summaries?", "dedicated in-memory summaries?", "in-memory summaries?", "summaries?"], "tokens": ["What", "is", "the", "benefit", "of", "dedicated", "in", "memory", "summaries", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1427113677", "fields": {"term": "How are Vespa document summaries stored?", "namespace": "open-p", "hash": 1427113677, "terms": ["How are Vespa document summaries stored?", "are Vespa document summaries stored?", "Vespa document summaries stored?", "document summaries stored?", "summaries stored?", "stored?"], "tokens": ["How", "are", "Vespa", "document", "summaries", "stored", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-278790758", "fields": {"term": "What is the hits fetching performance guide?", "namespace": "open-p", "hash": -278790758, "terms": ["What is the hits fetching performance guide?", "is the hits fetching performance guide?", "the hits fetching performance guide?", "hits fetching performance guide?", "fetching performance guide?", "performance guide?", "guide?"], "tokens": ["What", "is", "the", "hits", "fetching", "performance", "guide", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::383852650", "fields": {"term": "How does the summary store avoid disk reads?", "namespace": "open-p", "hash": 383852650, "terms": ["How does the summary store avoid disk reads?", "does the summary store avoid disk reads?", "the summary store avoid disk reads?", "summary store avoid disk reads?", "store avoid disk reads?", "avoid disk reads?", "disk reads?", "reads?"], "tokens": ["How", "does", "the", "summary", "store", "avoid", "disk", "reads", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::150680759", "fields": {"term": "What is the purpose of summary chunk decompression?", "namespace": "open-p", "hash": 150680759, "terms": ["What is the purpose of summary chunk decompression?", "is the purpose of summary chunk decompression?", "the purpose of summary chunk decompression?", "purpose of summary chunk decompression?", "of summary chunk decompression?", "summary chunk decompression?", "chunk decompression?", "decompression?"], "tokens": ["What", "is", "the", "purpose", "of", "summary", "chunk", "decompression", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::768076713", "fields": {"term": "What is a boolean field?", "namespace": "open-p", "hash": 768076713, "terms": ["What is a boolean field?", "is a boolean field?", "a boolean field?", "boolean field?", "field?"], "tokens": ["What", "is", "a", "boolean", "field", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::38028024", "fields": {"term": "When to use string attributes?", "namespace": "open-p", "hash": 38028024, "terms": ["When to use string attributes?", "to use string attributes?", "use string attributes?", "string attributes?", "attributes?"], "tokens": ["When", "to", "use", "string", "attributes", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-460260666", "fields": {"term": "Why use numeric attributes?", "namespace": "open-p", "hash": -460260666, "terms": ["Why use numeric attributes?", "use numeric attributes?", "numeric attributes?", "attributes?"], "tokens": ["Why", "use", "numeric", "attributes", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1002871610", "fields": {"term": "What is the rule of thumb for attribute field type selection?", "namespace": "open-p", "hash": 1002871610, "terms": ["What is the rule of thumb for attribute field type selection?", "is the rule of thumb for attribute field type selection?", "the rule of thumb for attribute field type selection?", "rule of thumb for attribute field type selection?", "of thumb for attribute field type selection?", "thumb for attribute field type selection?", "for attribute field type selection?", "attribute field type selection?", "field type selection?", "type selection?", "selection?"], "tokens": ["What", "is", "the", "rule", "of", "thumb", "for", "attribute", "field", "type", "selection", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1342531181", "fields": {"term": "What is the ranking workload?", "namespace": "open-p", "hash": 1342531181, "terms": ["What is the ranking workload?", "is the ranking workload?", "the ranking workload?", "ranking workload?", "workload?"], "tokens": ["What", "is", "the", "ranking", "workload", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-629726124", "fields": {"term": "Why is understanding computational cost important?", "namespace": "open-p", "hash": -629726124, "terms": ["Why is understanding computational cost important?", "is understanding computational cost important?", "understanding computational cost important?", "computational cost important?", "cost important?", "important?"], "tokens": ["Why", "is", "understanding", "computational", "cost", "important", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1025391756", "fields": {"term": "How does tensor size affect query cost?", "namespace": "open-p", "hash": -1025391756, "terms": ["How does tensor size affect query cost?", "does tensor size affect query cost?", "tensor size affect query cost?", "size affect query cost?", "affect query cost?", "query cost?", "cost?"], "tokens": ["How", "does", "tensor", "size", "affect", "query", "cost", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1866125930", "fields": {"term": "What is a dot product?", "namespace": "open-p", "hash": 1866125930, "terms": ["What is a dot product?", "is a dot product?", "a dot product?", "dot product?", "product?"], "tokens": ["What", "is", "a", "dot", "product", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1880696215", "fields": {"term": "What is tensor quantization?", "namespace": "open-p", "hash": 1880696215, "terms": ["What is tensor quantization?", "is tensor quantization?", "tensor quantization?", "quantization?"], "tokens": ["What", "is", "tensor", "quantization", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1530589284", "fields": {"term": "What is fast-rank?", "namespace": "open-p", "hash": -1530589284, "terms": ["What is fast-rank?", "is fast-rank?", "fast-rank?"], "tokens": ["What", "is", "fast", "rank", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1468076824", "fields": {"term": "How can fast-rank optimize ranking?", "namespace": "open-p", "hash": -1468076824, "terms": ["How can fast-rank optimize ranking?", "can fast-rank optimize ranking?", "fast-rank optimize ranking?", "optimize ranking?", "ranking?"], "tokens": ["How", "can", "fast", "rank", "optimize", "ranking", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1262919753", "fields": {"term": "What is the tradeoff of using fast-rank?", "namespace": "open-p", "hash": 1262919753, "terms": ["What is the tradeoff of using fast-rank?", "is the tradeoff of using fast-rank?", "the tradeoff of using fast-rank?", "tradeoff of using fast-rank?", "of using fast-rank?", "using fast-rank?", "fast-rank?"], "tokens": ["What", "is", "the", "tradeoff", "of", "using", "fast", "rank", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1970774310", "fields": {"term": "What is the Tensor Playground?", "namespace": "open-p", "hash": 1970774310, "terms": ["What is the Tensor Playground?", "is the Tensor Playground?", "the Tensor Playground?", "Tensor Playground?", "Playground?"], "tokens": ["What", "is", "the", "Tensor", "Playground", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1340483088", "fields": {"term": "How can Tensor Playground help with optimizing ranking functions?", "namespace": "open-p", "hash": -1340483088, "terms": ["How can Tensor Playground help with optimizing ranking functions?", "can Tensor Playground help with optimizing ranking functions?", "Tensor Playground help with optimizing ranking functions?", "Playground help with optimizing ranking functions?", "help with optimizing ranking functions?", "with optimizing ranking functions?", "optimizing ranking functions?", "ranking functions?", "functions?"], "tokens": ["How", "can", "Tensor", "Playground", "help", "with", "optimizing", "ranking", "functions", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1155673711", "fields": {"term": "How to avoid temporary objects when optimizing ranking with tensors?", "namespace": "open-p", "hash": -1155673711, "terms": ["How to avoid temporary objects when optimizing ranking with tensors?", "to avoid temporary objects when optimizing ranking with tensors?", "avoid temporary objects when optimizing ranking with tensors?", "temporary objects when optimizing ranking with tensors?", "objects when optimizing ranking with tensors?", "when optimizing ranking with tensors?", "optimizing ranking with tensors?", "ranking with tensors?", "with tensors?", "tensors?"], "tokens": ["How", "to", "avoid", "temporary", "objects", "when", "optimizing", "ranking", "with", "tensors", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::265748896", "fields": {"term": "What is multiphase ranking?", "namespace": "open-p", "hash": 265748896, "terms": ["What is multiphase ranking?", "is multiphase ranking?", "multiphase ranking?", "ranking?"], "tokens": ["What", "is", "multiphase", "ranking", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2147355367", "fields": {"term": "What are the benefits of multiphase ranking?", "namespace": "open-p", "hash": 2147355367, "terms": ["What are the benefits of multiphase ranking?", "are the benefits of multiphase ranking?", "the benefits of multiphase ranking?", "benefits of multiphase ranking?", "of multiphase ranking?", "multiphase ranking?", "ranking?"], "tokens": ["What", "are", "the", "benefits", "of", "multiphase", "ranking", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::843874307", "fields": {"term": "How does multiphase ranking save resources?", "namespace": "open-p", "hash": 843874307, "terms": ["How does multiphase ranking save resources?", "does multiphase ranking save resources?", "multiphase ranking save resources?", "ranking save resources?", "save resources?", "resources?"], "tokens": ["How", "does", "multiphase", "ranking", "save", "resources", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-520657532", "fields": {"term": "What is the blog post series about?", "namespace": "open-p", "hash": -520657532, "terms": ["What is the blog post series about?", "is the blog post series about?", "the blog post series about?", "blog post series about?", "post series about?", "series about?", "about?"], "tokens": ["What", "is", "the", "blog", "post", "series", "about", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1609955617", "fields": {"term": "What is the default tensor cell type?", "namespace": "open-p", "hash": 1609955617, "terms": ["What is the default tensor cell type?", "is the default tensor cell type?", "the default tensor cell type?", "default tensor cell type?", "tensor cell type?", "cell type?", "type?"], "tokens": ["What", "is", "the", "default", "tensor", "cell", "type", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1885672301", "fields": {"term": "What are the precision and memory considerations of double cell type?", "namespace": "open-p", "hash": 1885672301, "terms": ["What are the precision and memory considerations of double cell type?", "are the precision and memory considerations of double cell type?", "the precision and memory considerations of double cell type?", "precision and memory considerations of double cell type?", "and memory considerations of double cell type?", "memory considerations of double cell type?", "considerations of double cell type?", "of double cell type?", "double cell type?", "cell type?", "type?"], "tokens": ["What", "are", "the", "precision", "and", "memory", "considerations", "of", "double", "cell", "type", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1603346147", "fields": {"term": "What is the memory cost of a float vector with 1000 dimensions?", "namespace": "open-p", "hash": 1603346147, "terms": ["What is the memory cost of a float vector with 1000 dimensions?", "is the memory cost of a float vector with 1000 dimensions?", "the memory cost of a float vector with 1000 dimensions?", "memory cost of a float vector with 1000 dimensions?", "cost of a float vector with 1000 dimensions?", "of a float vector with 1000 dimensions?", "a float vector with 1000 dimensions?", "float vector with 1000 dimensions?", "vector with 1000 dimensions?", "with 1000 dimensions?", "1000 dimensions?", "dimensions?"], "tokens": ["What", "is", "the", "memory", "cost", "of", "a", "float", "vector", "with", "1000", "dimensions", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1915716016", "fields": {"term": "When should bfloat16 be used?", "namespace": "open-p", "hash": 1915716016, "terms": ["When should bfloat16 be used?", "should bfloat16 be used?", "bfloat16 be used?", "be used?", "used?"], "tokens": ["When", "should", "bfloat16", "be", "used", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1498435202", "fields": {"term": "What is the computational overhead of using bfloat16 compared to 32-bit float?", "namespace": "open-p", "hash": 1498435202, "terms": ["What is the computational overhead of using bfloat16 compared to 32-bit float?", "is the computational overhead of using bfloat16 compared to 32-bit float?", "the computational overhead of using bfloat16 compared to 32-bit float?", "computational overhead of using bfloat16 compared to 32-bit float?", "overhead of using bfloat16 compared to 32-bit float?", "of using bfloat16 compared to 32-bit float?", "using bfloat16 compared to 32-bit float?", "bfloat16 compared to 32-bit float?", "compared to 32-bit float?", "to 32-bit float?", "32-bit float?", "float?"], "tokens": ["What", "is", "the", "computational", "overhead", "of", "using", "bfloat16", "compared", "to", "32", "bit", "float", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1292433112", "fields": {"term": "What is int8 used for in machine-learning?", "namespace": "open-p", "hash": 1292433112, "terms": ["What is int8 used for in machine-learning?", "is int8 used for in machine-learning?", "int8 used for in machine-learning?", "used for in machine-learning?", "for in machine-learning?", "in machine-learning?", "machine-learning?"], "tokens": ["What", "is", "int8", "used", "for", "in", "machine", "learning", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1525536070", "fields": {"term": "What is the range of int8 cell value type?", "namespace": "open-p", "hash": -1525536070, "terms": ["What is the range of int8 cell value type?", "is the range of int8 cell value type?", "the range of int8 cell value type?", "range of int8 cell value type?", "of int8 cell value type?", "int8 cell value type?", "cell value type?", "value type?", "type?"], "tokens": ["What", "is", "the", "range", "of", "int8", "cell", "value", "type", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1116405768", "fields": {"term": "What is an inner product?", "namespace": "open-p", "hash": 1116405768, "terms": ["What is an inner product?", "is an inner product?", "an inner product?", "inner product?", "product?"], "tokens": ["What", "is", "an", "inner", "product", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1027432142", "fields": {"term": "What is an outer product?", "namespace": "open-p", "hash": 1027432142, "terms": ["What is an outer product?", "is an outer product?", "an outer product?", "outer product?", "product?"], "tokens": ["What", "is", "an", "outer", "product", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-275558648", "fields": {"term": "What is DenseDotProductFunction?", "namespace": "open-p", "hash": -275558648, "terms": ["What is DenseDotProductFunction?", "is DenseDotProductFunction?", "DenseDotProductFunction?"], "tokens": ["What", "is", "DenseDotProductFunction", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1625857896", "fields": {"term": "What is a DenseSimpleExpandFunction?", "namespace": "open-p", "hash": -1625857896, "terms": ["What is a DenseSimpleExpandFunction?", "is a DenseSimpleExpandFunction?", "a DenseSimpleExpandFunction?", "DenseSimpleExpandFunction?"], "tokens": ["What", "is", "a", "DenseSimpleExpandFunction", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::896517161", "fields": {"term": "What is Inject in Vespa tensor functions?", "namespace": "open-p", "hash": 896517161, "terms": ["What is Inject in Vespa tensor functions?", "is Inject in Vespa tensor functions?", "Inject in Vespa tensor functions?", "in Vespa tensor functions?", "Vespa tensor functions?", "tensor functions?", "functions?"], "tokens": ["What", "is", "Inject", "in", "Vespa", "tensor", "functions", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1612124822", "fields": {"term": "What is mapped dimension?", "namespace": "open-p", "hash": 1612124822, "terms": ["What is mapped dimension?", "is mapped dimension?", "mapped dimension?", "dimension?"], "tokens": ["What", "is", "mapped", "dimension", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-940563490", "fields": {"term": "What is MappedLookup?", "namespace": "open-p", "hash": -940563490, "terms": ["What is MappedLookup?", "is MappedLookup?", "MappedLookup?"], "tokens": ["What", "is", "MappedLookup", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1246917581", "fields": {"term": "What is a slice in Vespa?", "namespace": "open-p", "hash": -1246917581, "terms": ["What is a slice in Vespa?", "is a slice in Vespa?", "a slice in Vespa?", "slice in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["What", "is", "a", "slice", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1935620946", "fields": {"term": "What is considered a mapped lookup?", "namespace": "open-p", "hash": -1935620946, "terms": ["What is considered a mapped lookup?", "is considered a mapped lookup?", "considered a mapped lookup?", "a mapped lookup?", "mapped lookup?", "lookup?"], "tokens": ["What", "is", "considered", "a", "mapped", "lookup", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1159148050", "fields": {"term": "What is Tensor Playground?", "namespace": "open-p", "hash": -1159148050, "terms": ["What is Tensor Playground?", "is Tensor Playground?", "Tensor Playground?", "Playground?"], "tokens": ["What", "is", "Tensor", "Playground", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1859504788", "fields": {"term": "What is three-way mapped dot product?", "namespace": "open-p", "hash": -1859504788, "terms": ["What is three-way mapped dot product?", "is three-way mapped dot product?", "three-way mapped dot product?", "mapped dot product?", "dot product?", "product?"], "tokens": ["What", "is", "three", "way", "mapped", "dot", "product", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::88868818", "fields": {"term": "What is dot product?", "namespace": "open-p", "hash": 88868818, "terms": ["What is dot product?", "is dot product?", "dot product?", "product?"], "tokens": ["What", "is", "dot", "product", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::631449638", "fields": {"term": "What is Mixed112DotProduct?", "namespace": "open-p", "hash": 631449638, "terms": ["What is Mixed112DotProduct?", "is Mixed112DotProduct?", "Mixed112DotProduct?"], "tokens": ["What", "is", "Mixed112DotProduct", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1966226298", "fields": {"term": "What does this section provide?", "namespace": "open-p", "hash": -1966226298, "terms": ["What does this section provide?", "does this section provide?", "this section provide?", "section provide?", "provide?"], "tokens": ["What", "does", "this", "section", "provide", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::440526086", "fields": {"term": "What types of data can be used in document data schemas?", "namespace": "open-p", "hash": 440526086, "terms": ["What types of data can be used in document data schemas?", "types of data can be used in document data schemas?", "of data can be used in document data schemas?", "data can be used in document data schemas?", "can be used in document data schemas?", "be used in document data schemas?", "used in document data schemas?", "in document data schemas?", "document data schemas?", "data schemas?", "schemas?"], "tokens": ["What", "types", "of", "data", "can", "be", "used", "in", "document", "data", "schemas", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1600519990", "fields": {"term": "How is data replicated in Vespa?", "namespace": "open-p", "hash": 1600519990, "terms": ["How is data replicated in Vespa?", "is data replicated in Vespa?", "data replicated in Vespa?", "replicated in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["How", "is", "data", "replicated", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1328153748", "fields": {"term": "What is the sustained volume of writes per node per second?", "namespace": "open-p", "hash": 1328153748, "terms": ["What is the sustained volume of writes per node per second?", "is the sustained volume of writes per node per second?", "the sustained volume of writes per node per second?", "sustained volume of writes per node per second?", "volume of writes per node per second?", "of writes per node per second?", "writes per node per second?", "per node per second?", "node per second?", "per second?", "second?"], "tokens": ["What", "is", "the", "sustained", "volume", "of", "writes", "per", "node", "per", "second", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1056061872", "fields": {"term": "How are write operations processed?", "namespace": "open-p", "hash": 1056061872, "terms": ["How are write operations processed?", "are write operations processed?", "write operations processed?", "operations processed?", "processed?"], "tokens": ["How", "are", "write", "operations", "processed", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-159219332", "fields": {"term": "How is data written in Vespa?", "namespace": "open-p", "hash": -159219332, "terms": ["How is data written in Vespa?", "is data written in Vespa?", "data written in Vespa?", "written in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["How", "is", "data", "written", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-522326551", "fields": {"term": "Can data be streamed out of the system?", "namespace": "open-p", "hash": -522326551, "terms": ["Can data be streamed out of the system?", "data be streamed out of the system?", "be streamed out of the system?", "streamed out of the system?", "out of the system?", "of the system?", "the system?", "system?"], "tokens": ["Can", "data", "be", "streamed", "out", "of", "the", "system", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::904526472", "fields": {"term": "What happens in case of data corruption?", "namespace": "open-p", "hash": 904526472, "terms": ["What happens in case of data corruption?", "happens in case of data corruption?", "in case of data corruption?", "case of data corruption?", "of data corruption?", "data corruption?", "corruption?"], "tokens": ["What", "happens", "in", "case", "of", "data", "corruption", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-117721181", "fields": {"term": "What can queries contain?", "namespace": "open-p", "hash": -117721181, "terms": ["What can queries contain?", "can queries contain?", "queries contain?", "contain?"], "tokens": ["What", "can", "queries", "contain", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-792744321", "fields": {"term": "How fast are query response times?", "namespace": "open-p", "hash": -792744321, "terms": ["How fast are query response times?", "fast are query response times?", "are query response times?", "query response times?", "response times?", "times?"], "tokens": ["How", "fast", "are", "query", "response", "times", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-829666490", "fields": {"term": "What are scalar functions?", "namespace": "open-p", "hash": -829666490, "terms": ["What are scalar functions?", "are scalar functions?", "scalar functions?", "functions?"], "tokens": ["What", "are", "scalar", "functions", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1305074960", "fields": {"term": "What text ranking features are provided?", "namespace": "open-p", "hash": 1305074960, "terms": ["What text ranking features are provided?", "text ranking features are provided?", "ranking features are provided?", "features are provided?", "are provided?", "provided?"], "tokens": ["What", "text", "ranking", "features", "are", "provided", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1638984024", "fields": {"term": "What are other ranking features?", "namespace": "open-p", "hash": 1638984024, "terms": ["What are other ranking features?", "are other ranking features?", "other ranking features?", "ranking features?", "features?"], "tokens": ["What", "are", "other", "ranking", "features", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2082533430", "fields": {"term": "How are matches aggregated?", "namespace": "open-p", "hash": -2082533430, "terms": ["How are matches aggregated?", "are matches aggregated?", "matches aggregated?", "aggregated?"], "tokens": ["How", "are", "matches", "aggregated", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-263489630", "fields": {"term": "What is keyword highlighting?", "namespace": "open-p", "hash": -263489630, "terms": ["What is keyword highlighting?", "is keyword highlighting?", "keyword highlighting?", "highlighting?"], "tokens": ["What", "is", "keyword", "highlighting", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::997156015", "fields": {"term": "What kind of data can be aggregated?", "namespace": "open-p", "hash": 997156015, "terms": ["What kind of data can be aggregated?", "kind of data can be aggregated?", "of data can be aggregated?", "data can be aggregated?", "can be aggregated?", "be aggregated?", "aggregated?"], "tokens": ["What", "kind", "of", "data", "can", "be", "aggregated", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1521172524", "fields": {"term": "Can matches be grouped by numerical bucket?", "namespace": "open-p", "hash": -1521172524, "terms": ["Can matches be grouped by numerical bucket?", "matches be grouped by numerical bucket?", "be grouped by numerical bucket?", "grouped by numerical bucket?", "by numerical bucket?", "numerical bucket?", "bucket?"], "tokens": ["Can", "matches", "be", "grouped", "by", "numerical", "bucket", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2116407660", "fields": {"term": "What is included in the final result?", "namespace": "open-p", "hash": 2116407660, "terms": ["What is included in the final result?", "is included in the final result?", "included in the final result?", "in the final result?", "the final result?", "final result?", "result?"], "tokens": ["What", "is", "included", "in", "the", "final", "result", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1357295862", "fields": {"term": "Can an application package be changed while system is live?", "namespace": "open-p", "hash": -1357295862, "terms": ["Can an application package be changed while system is live?", "an application package be changed while system is live?", "application package be changed while system is live?", "package be changed while system is live?", "be changed while system is live?", "changed while system is live?", "while system is live?", "system is live?", "is live?", "live?"], "tokens": ["Can", "an", "application", "package", "be", "changed", "while", "system", "is", "live", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1732948823", "fields": {"term": "What is federation?", "namespace": "open-p", "hash": 1732948823, "terms": ["What is federation?", "is federation?", "federation?"], "tokens": ["What", "is", "federation", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1669853769", "fields": {"term": "What is a federated search service?", "namespace": "open-p", "hash": 1669853769, "terms": ["What is a federated search service?", "is a federated search service?", "a federated search service?", "federated search service?", "search service?", "service?"], "tokens": ["What", "is", "a", "federated", "search", "service", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1797811723", "fields": {"term": "What are the main tasks in creating a federation solution?", "namespace": "open-p", "hash": -1797811723, "terms": ["What are the main tasks in creating a federation solution?", "are the main tasks in creating a federation solution?", "the main tasks in creating a federation solution?", "main tasks in creating a federation solution?", "tasks in creating a federation solution?", "in creating a federation solution?", "creating a federation solution?", "a federation solution?", "federation solution?", "solution?"], "tokens": ["What", "are", "the", "main", "tasks", "in", "creating", "a", "federation", "solution", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1695900996", "fields": {"term": "What does the container aid with in federation?", "namespace": "open-p", "hash": -1695900996, "terms": ["What does the container aid with in federation?", "does the container aid with in federation?", "the container aid with in federation?", "container aid with in federation?", "aid with in federation?", "with in federation?", "in federation?", "federation?"], "tokens": ["What", "does", "the", "container", "aid", "with", "in", "federation", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1804959173", "fields": {"term": "What are some use cases for federation?", "namespace": "open-p", "hash": 1804959173, "terms": ["What are some use cases for federation?", "are some use cases for federation?", "some use cases for federation?", "use cases for federation?", "cases for federation?", "for federation?", "federation?"], "tokens": ["What", "are", "some", "use", "cases", "for", "federation", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::232244911", "fields": {"term": "What is the com.yahoo.search.federation Javadoc?", "namespace": "open-p", "hash": 232244911, "terms": ["What is the com.yahoo.search.federation Javadoc?", "is the com.yahoo.search.federation Javadoc?", "the com.yahoo.search.federation Javadoc?", "com.yahoo.search.federation Javadoc?", "Javadoc?"], "tokens": ["What", "is", "the", "com", "yahoo", "search", "federation", "Javadoc", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-303205128", "fields": {"term": "What is a pure federation platform?", "namespace": "open-p", "hash": -303205128, "terms": ["What is a pure federation platform?", "is a pure federation platform?", "a pure federation platform?", "pure federation platform?", "federation platform?", "platform?"], "tokens": ["What", "is", "a", "pure", "federation", "platform", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2031435493", "fields": {"term": "What is a provider?", "namespace": "open-p", "hash": -2031435493, "terms": ["What is a provider?", "is a provider?", "a provider?", "provider?"], "tokens": ["What", "is", "a", "provider", "?"], "corpus_count": 3, "document_count": 3}}, -{"put": "id:term:term::941348537", "fields": {"term": "How to configure a provider?", "namespace": "open-p", "hash": 941348537, "terms": ["How to configure a provider?", "to configure a provider?", "configure a provider?", "a provider?", "provider?"], "tokens": ["How", "to", "configure", "a", "provider", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1195309414", "fields": {"term": "How to exclude searchers from a provider?", "namespace": "open-p", "hash": -1195309414, "terms": ["How to exclude searchers from a provider?", "to exclude searchers from a provider?", "exclude searchers from a provider?", "searchers from a provider?", "from a provider?", "a provider?", "provider?"], "tokens": ["How", "to", "exclude", "searchers", "from", "a", "provider", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1886094699", "fields": {"term": "What is a source in Vespa?", "namespace": "open-p", "hash": 1886094699, "terms": ["What is a source in Vespa?", "is a source in Vespa?", "a source in Vespa?", "source in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["What", "is", "a", "source", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1657321098", "fields": {"term": "What is a source search chain?", "namespace": "open-p", "hash": -1657321098, "terms": ["What is a source search chain?", "is a source search chain?", "a source search chain?", "source search chain?", "search chain?", "chain?"], "tokens": ["What", "is", "a", "source", "search", "chain", "?"], "corpus_count": 3, "document_count": 3}}, -{"put": "id:term:term::424922857", "fields": {"term": "How does a source inherit searchers?", "namespace": "open-p", "hash": 424922857, "terms": ["How does a source inherit searchers?", "does a source inherit searchers?", "a source inherit searchers?", "source inherit searchers?", "inherit searchers?", "searchers?"], "tokens": ["How", "does", "a", "source", "inherit", "searchers", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1052253843", "fields": {"term": "What is the purpose of a source?", "namespace": "open-p", "hash": 1052253843, "terms": ["What is the purpose of a source?", "is the purpose of a source?", "the purpose of a source?", "purpose of a source?", "of a source?", "a source?", "source?"], "tokens": ["What", "is", "the", "purpose", "of", "a", "source", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1223751577", "fields": {"term": "Is the provider modified by adding sources?", "namespace": "open-p", "hash": 1223751577, "terms": ["Is the provider modified by adding sources?", "the provider modified by adding sources?", "provider modified by adding sources?", "modified by adding sources?", "by adding sources?", "adding sources?", "sources?"], "tokens": ["Is", "the", "provider", "modified", "by", "adding", "sources", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::291193006", "fields": {"term": "How can Vespa reuse connections to data providers?", "namespace": "open-p", "hash": 291193006, "terms": ["How can Vespa reuse connections to data providers?", "can Vespa reuse connections to data providers?", "Vespa reuse connections to data providers?", "reuse connections to data providers?", "connections to data providers?", "to data providers?", "data providers?", "providers?"], "tokens": ["How", "can", "Vespa", "reuse", "connections", "to", "data", "providers", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-819726624", "fields": {"term": "How does Vespa modify a query for a source search chain?", "namespace": "open-p", "hash": -819726624, "terms": ["How does Vespa modify a query for a source search chain?", "does Vespa modify a query for a source search chain?", "Vespa modify a query for a source search chain?", "modify a query for a source search chain?", "a query for a source search chain?", "query for a source search chain?", "for a source search chain?", "a source search chain?", "source search chain?", "search chain?", "chain?"], "tokens": ["How", "does", "Vespa", "modify", "a", "query", "for", "a", "source", "search", "chain", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1042242405", "fields": {"term": "What is a leader source search chain?", "namespace": "open-p", "hash": 1042242405, "terms": ["What is a leader source search chain?", "is a leader source search chain?", "a leader source search chain?", "leader source search chain?", "source search chain?", "search chain?", "chain?"], "tokens": ["What", "is", "a", "leader", "source", "search", "chain", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-422983872", "fields": {"term": "What is a participant source search chain?", "namespace": "open-p", "hash": -422983872, "terms": ["What is a participant source search chain?", "is a participant source search chain?", "a participant source search chain?", "participant source search chain?", "source search chain?", "search chain?", "chain?"], "tokens": ["What", "is", "a", "participant", "source", "search", "chain", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1743081202", "fields": {"term": "What is `idref` attribute used for?", "namespace": "open-p", "hash": -1743081202, "terms": ["What is `idref` attribute used for?", "is `idref` attribute used for?", "`idref` attribute used for?", "attribute used for?", "used for?", "for?"], "tokens": ["What", "is", "`", "idref", "`", "attribute", "used", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1714635917", "fields": {"term": "How are participants and leaders different?", "namespace": "open-p", "hash": 1714635917, "terms": ["How are participants and leaders different?", "are participants and leaders different?", "participants and leaders different?", "and leaders different?", "leaders different?", "different?"], "tokens": ["How", "are", "participants", "and", "leaders", "different", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-767505862", "fields": {"term": "What is the default behaviour when federating to a source?", "namespace": "open-p", "hash": -767505862, "terms": ["What is the default behaviour when federating to a source?", "is the default behaviour when federating to a source?", "the default behaviour when federating to a source?", "default behaviour when federating to a source?", "behaviour when federating to a source?", "when federating to a source?", "federating to a source?", "to a source?", "a source?", "source?"], "tokens": ["What", "is", "the", "default", "behaviour", "when", "federating", "to", "a", "source", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1052799884", "fields": {"term": "How to specify a participant source instead of the leader?", "namespace": "open-p", "hash": -1052799884, "terms": ["How to specify a participant source instead of the leader?", "to specify a participant source instead of the leader?", "specify a participant source instead of the leader?", "a participant source instead of the leader?", "participant source instead of the leader?", "source instead of the leader?", "instead of the leader?", "of the leader?", "the leader?", "leader?"], "tokens": ["How", "to", "specify", "a", "participant", "source", "instead", "of", "the", "leader", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-137804045", "fields": {"term": "What is the name of the federation searcher?", "namespace": "open-p", "hash": -137804045, "terms": ["What is the name of the federation searcher?", "is the name of the federation searcher?", "the name of the federation searcher?", "name of the federation searcher?", "of the federation searcher?", "the federation searcher?", "federation searcher?", "searcher?"], "tokens": ["What", "is", "the", "name", "of", "the", "federation", "searcher", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1398334017", "fields": {"term": "What providers can the federation searcher be configured to federate to?", "namespace": "open-p", "hash": 1398334017, "terms": ["What providers can the federation searcher be configured to federate to?", "providers can the federation searcher be configured to federate to?", "can the federation searcher be configured to federate to?", "the federation searcher be configured to federate to?", "federation searcher be configured to federate to?", "searcher be configured to federate to?", "be configured to federate to?", "configured to federate to?", "to federate to?", "federate to?", "to?"], "tokens": ["What", "providers", "can", "the", "federation", "searcher", "be", "configured", "to", "federate", "to", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1661478678", "fields": {"term": "Are custom sources allowed for federation searchers?", "namespace": "open-p", "hash": -1661478678, "terms": ["Are custom sources allowed for federation searchers?", "custom sources allowed for federation searchers?", "sources allowed for federation searchers?", "allowed for federation searchers?", "for federation searchers?", "federation searchers?", "searchers?"], "tokens": ["Are", "custom", "sources", "allowed", "for", "federation", "searchers", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1441760685", "fields": {"term": "What is the flexibility of configuring a custom federation searcher?", "namespace": "open-p", "hash": -1441760685, "terms": ["What is the flexibility of configuring a custom federation searcher?", "is the flexibility of configuring a custom federation searcher?", "the flexibility of configuring a custom federation searcher?", "flexibility of configuring a custom federation searcher?", "of configuring a custom federation searcher?", "configuring a custom federation searcher?", "a custom federation searcher?", "custom federation searcher?", "federation searcher?", "searcher?"], "tokens": ["What", "is", "the", "flexibility", "of", "configuring", "a", "custom", "federation", "searcher", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1024661993", "fields": {"term": "What is a federation searcher?", "namespace": "open-p", "hash": 1024661993, "terms": ["What is a federation searcher?", "is a federation searcher?", "a federation searcher?", "federation searcher?", "searcher?"], "tokens": ["What", "is", "a", "federation", "searcher", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1486064862", "fields": {"term": "How do you inherit the default source set?", "namespace": "open-p", "hash": -1486064862, "terms": ["How do you inherit the default source set?", "do you inherit the default source set?", "you inherit the default source set?", "inherit the default source set?", "the default source set?", "default source set?", "source set?", "set?"], "tokens": ["How", "do", "you", "inherit", "the", "default", "source", "set", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1923094854", "fields": {"term": "What is the purpose of modifying the search chain?", "namespace": "open-p", "hash": -1923094854, "terms": ["What is the purpose of modifying the search chain?", "is the purpose of modifying the search chain?", "the purpose of modifying the search chain?", "purpose of modifying the search chain?", "of modifying the search chain?", "modifying the search chain?", "the search chain?", "search chain?", "chain?"], "tokens": ["What", "is", "the", "purpose", "of", "modifying", "the", "search", "chain", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-355603845", "fields": {"term": "How do you modify the search chain?", "namespace": "open-p", "hash": -355603845, "terms": ["How do you modify the search chain?", "do you modify the search chain?", "you modify the search chain?", "modify the search chain?", "the search chain?", "search chain?", "chain?"], "tokens": ["How", "do", "you", "modify", "the", "search", "chain", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1733950264", "fields": {"term": "What is a content cluster?", "namespace": "open-p", "hash": 1733950264, "terms": ["What is a content cluster?", "is a content cluster?", "a content cluster?", "content cluster?", "cluster?"], "tokens": ["What", "is", "a", "content", "cluster", "?"], "corpus_count": 3, "document_count": 3}}, -{"put": "id:term:term::318165891", "fields": {"term": "What is the excludes attribute in provider?", "namespace": "open-p", "hash": 318165891, "terms": ["What is the excludes attribute in provider?", "is the excludes attribute in provider?", "the excludes attribute in provider?", "excludes attribute in provider?", "attribute in provider?", "in provider?", "provider?"], "tokens": ["What", "is", "the", "excludes", "attribute", "in", "provider", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1460145098", "fields": {"term": "What is the cluster attribute in provider?", "namespace": "open-p", "hash": 1460145098, "terms": ["What is the cluster attribute in provider?", "is the cluster attribute in provider?", "the cluster attribute in provider?", "cluster attribute in provider?", "attribute in provider?", "in provider?", "provider?"], "tokens": ["What", "is", "the", "cluster", "attribute", "in", "provider", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1498605207", "fields": {"term": "What is federation layer?", "namespace": "open-p", "hash": -1498605207, "terms": ["What is federation layer?", "is federation layer?", "federation layer?", "layer?"], "tokens": ["What", "is", "federation", "layer", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::936477551", "fields": {"term": "What is requestTimeout?", "namespace": "open-p", "hash": 936477551, "terms": ["What is requestTimeout?", "is requestTimeout?", "requestTimeout?"], "tokens": ["What", "is", "requestTimeout", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1936644044", "fields": {"term": "How do you limit provider search time?", "namespace": "open-p", "hash": -1936644044, "terms": ["How do you limit provider search time?", "do you limit provider search time?", "you limit provider search time?", "limit provider search time?", "provider search time?", "search time?", "time?"], "tokens": ["How", "do", "you", "limit", "provider", "search", "time", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1467852256", "fields": {"term": "What is the purpose of the ads provider?", "namespace": "open-p", "hash": 1467852256, "terms": ["What is the purpose of the ads provider?", "is the purpose of the ads provider?", "the purpose of the ads provider?", "purpose of the ads provider?", "of the ads provider?", "the ads provider?", "ads provider?", "provider?"], "tokens": ["What", "is", "the", "purpose", "of", "the", "ads", "provider", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1108952662", "fields": {"term": "How can the ads provider be made optional?", "namespace": "open-p", "hash": -1108952662, "terms": ["How can the ads provider be made optional?", "can the ads provider be made optional?", "the ads provider be made optional?", "ads provider be made optional?", "provider be made optional?", "be made optional?", "made optional?", "optional?"], "tokens": ["How", "can", "the", "ads", "provider", "be", "made", "optional", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-147861646", "fields": {"term": "What happens if only optional providers are selected for the Federation searcher?", "namespace": "open-p", "hash": -147861646, "terms": ["What happens if only optional providers are selected for the Federation searcher?", "happens if only optional providers are selected for the Federation searcher?", "if only optional providers are selected for the Federation searcher?", "only optional providers are selected for the Federation searcher?", "optional providers are selected for the Federation searcher?", "providers are selected for the Federation searcher?", "are selected for the Federation searcher?", "selected for the Federation searcher?", "for the Federation searcher?", "the Federation searcher?", "Federation searcher?", "searcher?"], "tokens": ["What", "happens", "if", "only", "optional", "providers", "are", "selected", "for", "the", "Federation", "searcher", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::838508643", "fields": {"term": "What are federation options?", "namespace": "open-p", "hash": 838508643, "terms": ["What are federation options?", "are federation options?", "federation options?", "options?"], "tokens": ["What", "are", "federation", "options", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::227579833", "fields": {"term": "How do you override federation options?", "namespace": "open-p", "hash": 227579833, "terms": ["How do you override federation options?", "do you override federation options?", "you override federation options?", "override federation options?", "federation options?", "options?"], "tokens": ["How", "do", "you", "override", "federation", "options", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1645054774", "fields": {"term": "What is a TargetSelector?", "namespace": "open-p", "hash": 1645054774, "terms": ["What is a TargetSelector?", "is a TargetSelector?", "a TargetSelector?", "TargetSelector?"], "tokens": ["What", "is", "a", "TargetSelector", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::997193881", "fields": {"term": "How is target selector used?", "namespace": "open-p", "hash": 997193881, "terms": ["How is target selector used?", "is target selector used?", "target selector used?", "selector used?", "used?"], "tokens": ["How", "is", "target", "selector", "used", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1404759068", "fields": {"term": "What is a FederationTarget?", "namespace": "open-p", "hash": -1404759068, "terms": ["What is a FederationTarget?", "is a FederationTarget?", "a FederationTarget?", "FederationTarget?"], "tokens": ["What", "is", "a", "FederationTarget", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1062181029", "fields": {"term": "How do you modify target query?", "namespace": "open-p", "hash": 1062181029, "terms": ["How do you modify target query?", "do you modify target query?", "you modify target query?", "modify target query?", "target query?", "query?"], "tokens": ["How", "do", "you", "modify", "target", "query", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1579947171", "fields": {"term": "How do you modify target result?", "namespace": "open-p", "hash": 1579947171, "terms": ["How do you modify target result?", "do you modify target result?", "you modify target result?", "modify target result?", "target result?", "result?"], "tokens": ["How", "do", "you", "modify", "target", "result", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-925599251", "fields": {"term": "How to configure a federation searcher?", "namespace": "open-p", "hash": -925599251, "terms": ["How to configure a federation searcher?", "to configure a federation searcher?", "configure a federation searcher?", "a federation searcher?", "federation searcher?", "searcher?"], "tokens": ["How", "to", "configure", "a", "federation", "searcher", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-44809237", "fields": {"term": "What is the best way to bring relevant data to a user?", "namespace": "open-p", "hash": -44809237, "terms": ["What is the best way to bring relevant data to a user?", "is the best way to bring relevant data to a user?", "the best way to bring relevant data to a user?", "best way to bring relevant data to a user?", "way to bring relevant data to a user?", "to bring relevant data to a user?", "bring relevant data to a user?", "relevant data to a user?", "data to a user?", "to a user?", "a user?", "user?"], "tokens": ["What", "is", "the", "best", "way", "to", "bring", "relevant", "data", "to", "a", "user", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1970742908", "fields": {"term": "What is the main search chain?", "namespace": "open-p", "hash": -1970742908, "terms": ["What is the main search chain?", "is the main search chain?", "the main search chain?", "main search chain?", "search chain?", "chain?"], "tokens": ["What", "is", "the", "main", "search", "chain", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1161676054", "fields": {"term": "How should sources be selected during searching?", "namespace": "open-p", "hash": 1161676054, "terms": ["How should sources be selected during searching?", "should sources be selected during searching?", "sources be selected during searching?", "be selected during searching?", "selected during searching?", "during searching?", "searching?"], "tokens": ["How", "should", "sources", "be", "selected", "during", "searching", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-49921136", "fields": {"term": "What is a provider in Vespa?", "namespace": "open-p", "hash": -49921136, "terms": ["What is a provider in Vespa?", "is a provider in Vespa?", "a provider in Vespa?", "provider in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["What", "is", "a", "provider", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1256113533", "fields": {"term": "How can we reduce resource cost involved in searching?", "namespace": "open-p", "hash": -1256113533, "terms": ["How can we reduce resource cost involved in searching?", "can we reduce resource cost involved in searching?", "we reduce resource cost involved in searching?", "reduce resource cost involved in searching?", "resource cost involved in searching?", "cost involved in searching?", "involved in searching?", "in searching?", "searching?"], "tokens": ["How", "can", "we", "reduce", "resource", "cost", "involved", "in", "searching", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-304276141", "fields": {"term": "What is the purpose of an explicit main chain in Vespa?", "namespace": "open-p", "hash": -304276141, "terms": ["What is the purpose of an explicit main chain in Vespa?", "is the purpose of an explicit main chain in Vespa?", "the purpose of an explicit main chain in Vespa?", "purpose of an explicit main chain in Vespa?", "of an explicit main chain in Vespa?", "an explicit main chain in Vespa?", "explicit main chain in Vespa?", "main chain in Vespa?", "chain in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["What", "is", "the", "purpose", "of", "an", "explicit", "main", "chain", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1802861424", "fields": {"term": "What are provider searchers?", "namespace": "open-p", "hash": -1802861424, "terms": ["What are provider searchers?", "are provider searchers?", "provider searchers?", "searchers?"], "tokens": ["What", "are", "provider", "searchers", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1782119510", "fields": {"term": "What modifications may need to be made to a query?", "namespace": "open-p", "hash": -1782119510, "terms": ["What modifications may need to be made to a query?", "modifications may need to be made to a query?", "may need to be made to a query?", "need to be made to a query?", "to be made to a query?", "be made to a query?", "made to a query?", "to a query?", "a query?", "query?"], "tokens": ["What", "modifications", "may", "need", "to", "be", "made", "to", "a", "query", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1462236545", "fields": {"term": "What is the purpose of the NewsCustomerIdSearcher class?", "namespace": "open-p", "hash": 1462236545, "terms": ["What is the purpose of the NewsCustomerIdSearcher class?", "is the purpose of the NewsCustomerIdSearcher class?", "the purpose of the NewsCustomerIdSearcher class?", "purpose of the NewsCustomerIdSearcher class?", "of the NewsCustomerIdSearcher class?", "the NewsCustomerIdSearcher class?", "NewsCustomerIdSearcher class?", "class?"], "tokens": ["What", "is", "the", "purpose", "of", "the", "NewsCustomerIdSearcher", "class", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1579689442", "fields": {"term": "How are source and provider search chains related?", "namespace": "open-p", "hash": -1579689442, "terms": ["How are source and provider search chains related?", "are source and provider search chains related?", "source and provider search chains related?", "and provider search chains related?", "provider search chains related?", "search chains related?", "chains related?", "related?"], "tokens": ["How", "are", "source", "and", "provider", "search", "chains", "related", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::518618162", "fields": {"term": "What is the difference between a source and a provider?", "namespace": "open-p", "hash": 518618162, "terms": ["What is the difference between a source and a provider?", "is the difference between a source and a provider?", "the difference between a source and a provider?", "difference between a source and a provider?", "between a source and a provider?", "a source and a provider?", "source and a provider?", "and a provider?", "a provider?", "provider?"], "tokens": ["What", "is", "the", "difference", "between", "a", "source", "and", "a", "provider", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2117668523", "fields": {"term": "What is the ResultBlender?", "namespace": "open-p", "hash": 2117668523, "terms": ["What is the ResultBlender?", "is the ResultBlender?", "the ResultBlender?", "ResultBlender?"], "tokens": ["What", "is", "the", "ResultBlender", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1712248617", "fields": {"term": "What is the purpose of the ResultBlender?", "namespace": "open-p", "hash": -1712248617, "terms": ["What is the purpose of the ResultBlender?", "is the purpose of the ResultBlender?", "the purpose of the ResultBlender?", "purpose of the ResultBlender?", "of the ResultBlender?", "the ResultBlender?", "ResultBlender?"], "tokens": ["What", "is", "the", "purpose", "of", "the", "ResultBlender", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::57323521", "fields": {"term": "What are HitGroups?", "namespace": "open-p", "hash": 57323521, "terms": ["What are HitGroups?", "are HitGroups?", "HitGroups?"], "tokens": ["What", "are", "HitGroups", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1671815976", "fields": {"term": "What is the purpose of SourceSelector?", "namespace": "open-p", "hash": -1671815976, "terms": ["What is the purpose of SourceSelector?", "is the purpose of SourceSelector?", "the purpose of SourceSelector?", "purpose of SourceSelector?", "of SourceSelector?", "SourceSelector?"], "tokens": ["What", "is", "the", "purpose", "of", "SourceSelector", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-390236832", "fields": {"term": "What is the page-result format?", "namespace": "open-p", "hash": -390236832, "terms": ["What is the page-result format?", "is the page-result format?", "the page-result format?", "page-result format?", "format?"], "tokens": ["What", "is", "the", "page", "result", "format", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1230653711", "fields": {"term": "What is a mock backend?", "namespace": "open-p", "hash": -1230653711, "terms": ["What is a mock backend?", "is a mock backend?", "a mock backend?", "mock backend?", "backend?"], "tokens": ["What", "is", "a", "mock", "backend", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::23458585", "fields": {"term": "What is the purpose of ResultBlender?", "namespace": "open-p", "hash": 23458585, "terms": ["What is the purpose of ResultBlender?", "is the purpose of ResultBlender?", "the purpose of ResultBlender?", "purpose of ResultBlender?", "of ResultBlender?", "ResultBlender?"], "tokens": ["What", "is", "the", "purpose", "of", "ResultBlender", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::478682628", "fields": {"term": "What is ResultBlenderTestCase?", "namespace": "open-p", "hash": 478682628, "terms": ["What is ResultBlenderTestCase?", "is ResultBlenderTestCase?", "ResultBlenderTestCase?"], "tokens": ["What", "is", "ResultBlenderTestCase", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1465833018", "fields": {"term": "How to pass arguments to services?", "namespace": "open-p", "hash": 1465833018, "terms": ["How to pass arguments to services?", "to pass arguments to services?", "pass arguments to services?", "arguments to services?", "to services?", "services?"], "tokens": ["How", "to", "pass", "arguments", "to", "services", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::456304242", "fields": {"term": "What is HTTPSearcher utility?", "namespace": "open-p", "hash": 456304242, "terms": ["What is HTTPSearcher utility?", "is HTTPSearcher utility?", "HTTPSearcher utility?", "utility?"], "tokens": ["What", "is", "HTTPSearcher", "utility", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-775507982", "fields": {"term": "What is the name scheme?", "namespace": "open-p", "hash": -775507982, "terms": ["What is the name scheme?", "is the name scheme?", "the name scheme?", "name scheme?", "scheme?"], "tokens": ["What", "is", "the", "name", "scheme", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::726161123", "fields": {"term": "What is the 'source' property?", "namespace": "open-p", "hash": 726161123, "terms": ["What is the 'source' property?", "is the 'source' property?", "the 'source' property?", "'source' property?", "property?"], "tokens": ["What", "is", "the", "'", "source", "'", "property", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::394506551", "fields": {"term": "What is the 'provider' property?", "namespace": "open-p", "hash": 394506551, "terms": ["What is the 'provider' property?", "is the 'provider' property?", "the 'provider' property?", "'provider' property?", "property?"], "tokens": ["What", "is", "the", "'", "provider", "'", "property", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1152041133", "fields": {"term": "What is the 'service' property?", "namespace": "open-p", "hash": -1152041133, "terms": ["What is the 'service' property?", "is the 'service' property?", "the 'service' property?", "'service' property?", "property?"], "tokens": ["What", "is", "the", "'", "service", "'", "property", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2097064544", "fields": {"term": "What is the HTTPSearcher utility used for?", "namespace": "open-p", "hash": -2097064544, "terms": ["What is the HTTPSearcher utility used for?", "is the HTTPSearcher utility used for?", "the HTTPSearcher utility used for?", "HTTPSearcher utility used for?", "utility used for?", "used for?", "for?"], "tokens": ["What", "is", "the", "HTTPSearcher", "utility", "used", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1925189023", "fields": {"term": "Why are external write operations blocked?", "namespace": "open-p", "hash": 1925189023, "terms": ["Why are external write operations blocked?", "are external write operations blocked?", "external write operations blocked?", "write operations blocked?", "operations blocked?", "blocked?"], "tokens": ["Why", "are", "external", "write", "operations", "blocked", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1737595581", "fields": {"term": "What causes feed blocking?", "namespace": "open-p", "hash": -1737595581, "terms": ["What causes feed blocking?", "causes feed blocking?", "feed blocking?", "blocking?"], "tokens": ["What", "causes", "feed", "blocking", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-589502786", "fields": {"term": "What operations are rejected when feed is blocked?", "namespace": "open-p", "hash": -589502786, "terms": ["What operations are rejected when feed is blocked?", "operations are rejected when feed is blocked?", "are rejected when feed is blocked?", "rejected when feed is blocked?", "when feed is blocked?", "feed is blocked?", "is blocked?", "blocked?"], "tokens": ["What", "operations", "are", "rejected", "when", "feed", "is", "blocked", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1973988948", "fields": {"term": "How to unblock feeding when resource limit is reached?", "namespace": "open-p", "hash": -1973988948, "terms": ["How to unblock feeding when resource limit is reached?", "to unblock feeding when resource limit is reached?", "unblock feeding when resource limit is reached?", "feeding when resource limit is reached?", "when resource limit is reached?", "resource limit is reached?", "limit is reached?", "is reached?", "reached?"], "tokens": ["How", "to", "unblock", "feeding", "when", "resource", "limit", "is", "reached", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::40033448", "fields": {"term": "What metrics are used to monitor resource usage?", "namespace": "open-p", "hash": 40033448, "terms": ["What metrics are used to monitor resource usage?", "metrics are used to monitor resource usage?", "are used to monitor resource usage?", "used to monitor resource usage?", "to monitor resource usage?", "monitor resource usage?", "resource usage?", "usage?"], "tokens": ["What", "metrics", "are", "used", "to", "monitor", "resource", "usage", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-837771589", "fields": {"term": "What is the error message returned when feeding is blocked due to resource exhaustion?", "namespace": "open-p", "hash": -837771589, "terms": ["What is the error message returned when feeding is blocked due to resource exhaustion?", "is the error message returned when feeding is blocked due to resource exhaustion?", "the error message returned when feeding is blocked due to resource exhaustion?", "error message returned when feeding is blocked due to resource exhaustion?", "message returned when feeding is blocked due to resource exhaustion?", "returned when feeding is blocked due to resource exhaustion?", "when feeding is blocked due to resource exhaustion?", "feeding is blocked due to resource exhaustion?", "is blocked due to resource exhaustion?", "blocked due to resource exhaustion?", "due to resource exhaustion?", "to resource exhaustion?", "resource exhaustion?", "exhaustion?"], "tokens": ["What", "is", "the", "error", "message", "returned", "when", "feeding", "is", "blocked", "due", "to", "resource", "exhaustion", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-120520176", "fields": {"term": "What is attribute data structures and how does it affect feeding?", "namespace": "open-p", "hash": -120520176, "terms": ["What is attribute data structures and how does it affect feeding?", "is attribute data structures and how does it affect feeding?", "attribute data structures and how does it affect feeding?", "data structures and how does it affect feeding?", "structures and how does it affect feeding?", "and how does it affect feeding?", "how does it affect feeding?", "does it affect feeding?", "it affect feeding?", "affect feeding?", "feeding?"], "tokens": ["What", "is", "attribute", "data", "structures", "and", "how", "does", "it", "affect", "feeding", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1235804567", "fields": {"term": "What are environment variables used?", "namespace": "open-p", "hash": -1235804567, "terms": ["What are environment variables used?", "are environment variables used?", "environment variables used?", "variables used?", "used?"], "tokens": ["What", "are", "environment", "variables", "used", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1960615122", "fields": {"term": "What processes run on Vespa nodes?", "namespace": "open-p", "hash": -1960615122, "terms": ["What processes run on Vespa nodes?", "processes run on Vespa nodes?", "run on Vespa nodes?", "on Vespa nodes?", "Vespa nodes?", "nodes?"], "tokens": ["What", "processes", "run", "on", "Vespa", "nodes", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-19428806", "fields": {"term": "What are the directories used in Vespa installation?", "namespace": "open-p", "hash": -19428806, "terms": ["What are the directories used in Vespa installation?", "are the directories used in Vespa installation?", "the directories used in Vespa installation?", "directories used in Vespa installation?", "used in Vespa installation?", "in Vespa installation?", "Vespa installation?", "installation?"], "tokens": ["What", "are", "the", "directories", "used", "in", "Vespa", "installation", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-257437046", "fields": {"term": "What is the $VESPA_HOME/bin directory for?", "namespace": "open-p", "hash": -257437046, "terms": ["What is the $VESPA_HOME/bin directory for?", "is the $VESPA_HOME/bin directory for?", "the $VESPA_HOME/bin directory for?", "$VESPA_HOME/bin directory for?", "directory for?", "for?"], "tokens": ["What", "is", "the", "$", "VESPA_HOME", "/", "bin", "directory", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1675792486", "fields": {"term": "What is stored in the $VESPA_HOME/conf/vespa directory?", "namespace": "open-p", "hash": -1675792486, "terms": ["What is stored in the $VESPA_HOME/conf/vespa directory?", "is stored in the $VESPA_HOME/conf/vespa directory?", "stored in the $VESPA_HOME/conf/vespa directory?", "in the $VESPA_HOME/conf/vespa directory?", "the $VESPA_HOME/conf/vespa directory?", "$VESPA_HOME/conf/vespa directory?", "directory?"], "tokens": ["What", "is", "stored", "in", "the", "$", "VESPA_HOME", "/", "conf", "/", "vespa", "directory", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1307614355", "fields": {"term": "What is the port allocation for Vespa services?", "namespace": "open-p", "hash": -1307614355, "terms": ["What is the port allocation for Vespa services?", "is the port allocation for Vespa services?", "the port allocation for Vespa services?", "port allocation for Vespa services?", "allocation for Vespa services?", "for Vespa services?", "Vespa services?", "services?"], "tokens": ["What", "is", "the", "port", "allocation", "for", "Vespa", "services", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1836092417", "fields": {"term": "What is Proton process?", "namespace": "open-p", "hash": -1836092417, "terms": ["What is Proton process?", "is Proton process?", "Proton process?", "process?"], "tokens": ["What", "is", "Proton", "process", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1601936474", "fields": {"term": "What are Vespa startup scripts?", "namespace": "open-p", "hash": -1601936474, "terms": ["What are Vespa startup scripts?", "are Vespa startup scripts?", "Vespa startup scripts?", "startup scripts?", "scripts?"], "tokens": ["What", "are", "Vespa", "startup", "scripts", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1667864081", "fields": {"term": "Where can I find the minimum values for startup?", "namespace": "open-p", "hash": -1667864081, "terms": ["Where can I find the minimum values for startup?", "can I find the minimum values for startup?", "I find the minimum values for startup?", "find the minimum values for startup?", "the minimum values for startup?", "minimum values for startup?", "values for startup?", "for startup?", "startup?"], "tokens": ["Where", "can", "I", "find", "the", "minimum", "values", "for", "startup", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-609798379", "fields": {"term": "What services are checked during startup?", "namespace": "open-p", "hash": -609798379, "terms": ["What services are checked during startup?", "services are checked during startup?", "are checked during startup?", "checked during startup?", "during startup?", "startup?"], "tokens": ["What", "services", "are", "checked", "during", "startup", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1429605934", "fields": {"term": "What does chmod command do?", "namespace": "open-p", "hash": 1429605934, "terms": ["What does chmod command do?", "does chmod command do?", "chmod command do?", "command do?", "do?"], "tokens": ["What", "does", "chmod", "command", "do", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1123480702", "fields": {"term": "How are core files written?", "namespace": "open-p", "hash": -1123480702, "terms": ["How are core files written?", "are core files written?", "core files written?", "files written?", "written?"], "tokens": ["How", "are", "core", "files", "written", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::341033563", "fields": {"term": "What is the format of core files?", "namespace": "open-p", "hash": 341033563, "terms": ["What is the format of core files?", "is the format of core files?", "the format of core files?", "format of core files?", "of core files?", "core files?", "files?"], "tokens": ["What", "is", "the", "format", "of", "core", "files", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1307958689", "fields": {"term": "How to create a core file?", "namespace": "open-p", "hash": 1307958689, "terms": ["How to create a core file?", "to create a core file?", "create a core file?", "a core file?", "core file?", "file?"], "tokens": ["How", "to", "create", "a", "core", "file", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::51472990", "fields": {"term": "What are environment variables?", "namespace": "open-p", "hash": 51472990, "terms": ["What are environment variables?", "are environment variables?", "environment variables?", "variables?"], "tokens": ["What", "are", "environment", "variables", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::-1257516404", "fields": {"term": "What is VESPA_HOSTNAME?", "namespace": "open-p", "hash": -1257516404, "terms": ["What is VESPA_HOSTNAME?", "is VESPA_HOSTNAME?", "VESPA_HOSTNAME?"], "tokens": ["What", "is", "VESPA_HOSTNAME", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1502995288", "fields": {"term": "What is VESPA_WEB_SERVICE_PORT?", "namespace": "open-p", "hash": 1502995288, "terms": ["What is VESPA_WEB_SERVICE_PORT?", "is VESPA_WEB_SERVICE_PORT?", "VESPA_WEB_SERVICE_PORT?"], "tokens": ["What", "is", "VESPA_WEB_SERVICE_PORT", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::202785100", "fields": {"term": "What is VESPA_TLS_CONFIG_FILE?", "namespace": "open-p", "hash": 202785100, "terms": ["What is VESPA_TLS_CONFIG_FILE?", "is VESPA_TLS_CONFIG_FILE?", "VESPA_TLS_CONFIG_FILE?"], "tokens": ["What", "is", "VESPA_TLS_CONFIG_FILE", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::1839788876", "fields": {"term": "What is VESPA_CONFIGSERVER_JVMARGS?", "namespace": "open-p", "hash": 1839788876, "terms": ["What is VESPA_CONFIGSERVER_JVMARGS?", "is VESPA_CONFIGSERVER_JVMARGS?", "VESPA_CONFIGSERVER_JVMARGS?"], "tokens": ["What", "is", "VESPA_CONFIGSERVER_JVMARGS", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::398350422", "fields": {"term": "What is VESPA_LOG_LEVEL?", "namespace": "open-p", "hash": 398350422, "terms": ["What is VESPA_LOG_LEVEL?", "is VESPA_LOG_LEVEL?", "VESPA_LOG_LEVEL?"], "tokens": ["What", "is", "VESPA_LOG_LEVEL", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-695670483", "fields": {"term": "What is a latitude?", "namespace": "open-p", "hash": -695670483, "terms": ["What is a latitude?", "is a latitude?", "a latitude?", "latitude?"], "tokens": ["What", "is", "a", "latitude", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1186183782", "fields": {"term": "What is a longitude?", "namespace": "open-p", "hash": 1186183782, "terms": ["What is a longitude?", "is a longitude?", "a longitude?", "longitude?"], "tokens": ["What", "is", "a", "longitude", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2082239298", "fields": {"term": "How is location represented in JSON?", "namespace": "open-p", "hash": 2082239298, "terms": ["How is location represented in JSON?", "is location represented in JSON?", "location represented in JSON?", "represented in JSON?", "in JSON?", "JSON?"], "tokens": ["How", "is", "location", "represented", "in", "JSON", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2134595491", "fields": {"term": "How can position limit hits?", "namespace": "open-p", "hash": 2134595491, "terms": ["How can position limit hits?", "can position limit hits?", "position limit hits?", "limit hits?", "hits?"], "tokens": ["How", "can", "position", "limit", "hits", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1083085263", "fields": {"term": "What is the usual convention for latitude and longitude?", "namespace": "open-p", "hash": -1083085263, "terms": ["What is the usual convention for latitude and longitude?", "is the usual convention for latitude and longitude?", "the usual convention for latitude and longitude?", "usual convention for latitude and longitude?", "convention for latitude and longitude?", "for latitude and longitude?", "latitude and longitude?", "and longitude?", "longitude?"], "tokens": ["What", "is", "the", "usual", "convention", "for", "latitude", "and", "longitude", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::758412666", "fields": {"term": "What is schema biz?", "namespace": "open-p", "hash": 758412666, "terms": ["What is schema biz?", "is schema biz?", "schema biz?", "biz?"], "tokens": ["What", "is", "schema", "biz", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::705281238", "fields": {"term": "What is document biz?", "namespace": "open-p", "hash": 705281238, "terms": ["What is document biz?", "is document biz?", "document biz?", "biz?"], "tokens": ["What", "is", "document", "biz", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::398666195", "fields": {"term": "What is the type of 'mainloc'?", "namespace": "open-p", "hash": 398666195, "terms": ["What is the type of 'mainloc'?", "is the type of 'mainloc'?", "the type of 'mainloc'?", "type of 'mainloc'?", "of 'mainloc'?", "'mainloc'?"], "tokens": ["What", "is", "the", "type", "of", "'", "mainloc", "'?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1493335893", "fields": {"term": "What is a possible business entry?", "namespace": "open-p", "hash": -1493335893, "terms": ["What is a possible business entry?", "is a possible business entry?", "a possible business entry?", "possible business entry?", "business entry?", "entry?"], "tokens": ["What", "is", "a", "possible", "business", "entry", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1661324805", "fields": {"term": "What is the YQL statement?", "namespace": "open-p", "hash": 1661324805, "terms": ["What is the YQL statement?", "is the YQL statement?", "the YQL statement?", "YQL statement?", "statement?"], "tokens": ["What", "is", "the", "YQL", "statement", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::95336078", "fields": {"term": "What is the GeoLocationItem?", "namespace": "open-p", "hash": 95336078, "terms": ["What is the GeoLocationItem?", "is the GeoLocationItem?", "the GeoLocationItem?", "GeoLocationItem?"], "tokens": ["What", "is", "the", "GeoLocationItem", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1608813022", "fields": {"term": "How to add geo-restrictions?", "namespace": "open-p", "hash": -1608813022, "terms": ["How to add geo-restrictions?", "to add geo-restrictions?", "add geo-restrictions?", "geo-restrictions?"], "tokens": ["How", "to", "add", "geo", "restrictions", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1423200464", "fields": {"term": "What is the curl command to restrict search using a point and distance?", "namespace": "open-p", "hash": -1423200464, "terms": ["What is the curl command to restrict search using a point and distance?", "is the curl command to restrict search using a point and distance?", "the curl command to restrict search using a point and distance?", "curl command to restrict search using a point and distance?", "command to restrict search using a point and distance?", "to restrict search using a point and distance?", "restrict search using a point and distance?", "search using a point and distance?", "using a point and distance?", "a point and distance?", "point and distance?", "and distance?", "distance?"], "tokens": ["What", "is", "the", "curl", "command", "to", "restrict", "search", "using", "a", "point", "and", "distance", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1085242446", "fields": {"term": "How to include a position for ranking only?", "namespace": "open-p", "hash": -1085242446, "terms": ["How to include a position for ranking only?", "to include a position for ranking only?", "include a position for ranking only?", "a position for ranking only?", "position for ranking only?", "for ranking only?", "ranking only?", "only?"], "tokens": ["How", "to", "include", "a", "position", "for", "ranking", "only", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1702944264", "fields": {"term": "What is a negative radius?", "namespace": "open-p", "hash": 1702944264, "terms": ["What is a negative radius?", "is a negative radius?", "a negative radius?", "negative radius?", "radius?"], "tokens": ["What", "is", "a", "negative", "radius", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-647710666", "fields": {"term": "How to match any position?", "namespace": "open-p", "hash": -647710666, "terms": ["How to match any position?", "to match any position?", "match any position?", "any position?", "position?"], "tokens": ["How", "to", "match", "any", "position", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2131307478", "fields": {"term": "What is distance(mainloc).km?", "namespace": "open-p", "hash": 2131307478, "terms": ["What is distance(mainloc).km?", "is distance(mainloc).km?", "distance(mainloc).km?"], "tokens": ["What", "is", "distance", "(", "mainloc", ")", "km", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1103370067", "fields": {"term": "What is closeness(mainloc)?", "namespace": "open-p", "hash": -1103370067, "terms": ["What is closeness(mainloc)?", "is closeness(mainloc)?", "closeness(mainloc)?"], "tokens": ["What", "is", "closeness", "(", "mainloc", ")?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-383462964", "fields": {"term": "What are practical distances for ranking?", "namespace": "open-p", "hash": -383462964, "terms": ["What are practical distances for ranking?", "are practical distances for ranking?", "practical distances for ranking?", "distances for ranking?", "for ranking?", "ranking?"], "tokens": ["What", "are", "practical", "distances", "for", "ranking", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1400943733", "fields": {"term": "What is the range of closeness(mainloc)?", "namespace": "open-p", "hash": -1400943733, "terms": ["What is the range of closeness(mainloc)?", "is the range of closeness(mainloc)?", "the range of closeness(mainloc)?", "range of closeness(mainloc)?", "of closeness(mainloc)?", "closeness(mainloc)?"], "tokens": ["What", "is", "the", "range", "of", "closeness", "(", "mainloc", ")?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::778743814", "fields": {"term": "How to get computed distance?", "namespace": "open-p", "hash": 778743814, "terms": ["How to get computed distance?", "to get computed distance?", "get computed distance?", "computed distance?", "distance?"], "tokens": ["How", "to", "get", "computed", "distance", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1312823412", "fields": {"term": "What is the preferred way?", "namespace": "open-p", "hash": 1312823412, "terms": ["What is the preferred way?", "is the preferred way?", "the preferred way?", "preferred way?", "way?"], "tokens": ["What", "is", "the", "preferred", "way", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1686770959", "fields": {"term": "What is the purpose of having several position attributes?", "namespace": "open-p", "hash": -1686770959, "terms": ["What is the purpose of having several position attributes?", "is the purpose of having several position attributes?", "the purpose of having several position attributes?", "purpose of having several position attributes?", "of having several position attributes?", "having several position attributes?", "several position attributes?", "position attributes?", "attributes?"], "tokens": ["What", "is", "the", "purpose", "of", "having", "several", "position", "attributes", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1897337545", "fields": {"term": "How can you search for several position attributes?", "namespace": "open-p", "hash": 1897337545, "terms": ["How can you search for several position attributes?", "can you search for several position attributes?", "you search for several position attributes?", "search for several position attributes?", "for several position attributes?", "several position attributes?", "position attributes?", "attributes?"], "tokens": ["How", "can", "you", "search", "for", "several", "position", "attributes", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::359341944", "fields": {"term": "How can you combine several fields into a single search field?", "namespace": "open-p", "hash": 359341944, "terms": ["How can you combine several fields into a single search field?", "can you combine several fields into a single search field?", "you combine several fields into a single search field?", "combine several fields into a single search field?", "several fields into a single search field?", "fields into a single search field?", "into a single search field?", "a single search field?", "single search field?", "search field?", "field?"], "tokens": ["How", "can", "you", "combine", "several", "fields", "into", "a", "single", "search", "field", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1399642642", "fields": {"term": "What is the lat of JFK?", "namespace": "open-p", "hash": 1399642642, "terms": ["What is the lat of JFK?", "is the lat of JFK?", "the lat of JFK?", "lat of JFK?", "of JFK?", "JFK?"], "tokens": ["What", "is", "the", "lat", "of", "JFK", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1644125871", "fields": {"term": "Where is LAX located?", "namespace": "open-p", "hash": -1644125871, "terms": ["Where is LAX located?", "is LAX located?", "LAX located?", "located?"], "tokens": ["Where", "is", "LAX", "located", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2097938252", "fields": {"term": "What is GIG?", "namespace": "open-p", "hash": 2097938252, "terms": ["What is GIG?", "is GIG?", "GIG?"], "tokens": ["What", "is", "GIG", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1766187933", "fields": {"term": "What is the lng of NRT?", "namespace": "open-p", "hash": -1766187933, "terms": ["What is the lng of NRT?", "is the lng of NRT?", "the lng of NRT?", "lng of NRT?", "of NRT?", "NRT?"], "tokens": ["What", "is", "the", "lng", "of", "NRT", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1180118513", "fields": {"term": "What is DistanceToPath feature?", "namespace": "open-p", "hash": 1180118513, "terms": ["What is DistanceToPath feature?", "is DistanceToPath feature?", "DistanceToPath feature?", "feature?"], "tokens": ["What", "is", "DistanceToPath", "feature", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::-584827997", "fields": {"term": "What does DistanceToPath do?", "namespace": "open-p", "hash": -584827997, "terms": ["What does DistanceToPath do?", "does DistanceToPath do?", "DistanceToPath do?", "do?"], "tokens": ["What", "does", "DistanceToPath", "do", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2008747760", "fields": {"term": "What is the purpose of DistanceToPath?", "namespace": "open-p", "hash": -2008747760, "terms": ["What is the purpose of DistanceToPath?", "is the purpose of DistanceToPath?", "the purpose of DistanceToPath?", "purpose of DistanceToPath?", "of DistanceToPath?", "DistanceToPath?"], "tokens": ["What", "is", "the", "purpose", "of", "DistanceToPath", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1577124757", "fields": {"term": "How does DistanceToPath work?", "namespace": "open-p", "hash": 1577124757, "terms": ["How does DistanceToPath work?", "does DistanceToPath work?", "DistanceToPath work?", "work?"], "tokens": ["How", "does", "DistanceToPath", "work", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2109920619", "fields": {"term": "What is the nickname of DistanceToPath feature?", "namespace": "open-p", "hash": -2109920619, "terms": ["What is the nickname of DistanceToPath feature?", "is the nickname of DistanceToPath feature?", "the nickname of DistanceToPath feature?", "nickname of DistanceToPath feature?", "of DistanceToPath feature?", "DistanceToPath feature?", "feature?"], "tokens": ["What", "is", "the", "nickname", "of", "DistanceToPath", "feature", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1765766793", "fields": {"term": "What type of search corpus is required for DistanceToPath?", "namespace": "open-p", "hash": -1765766793, "terms": ["What type of search corpus is required for DistanceToPath?", "type of search corpus is required for DistanceToPath?", "of search corpus is required for DistanceToPath?", "search corpus is required for DistanceToPath?", "corpus is required for DistanceToPath?", "is required for DistanceToPath?", "required for DistanceToPath?", "for DistanceToPath?", "DistanceToPath?"], "tokens": ["What", "type", "of", "search", "corpus", "is", "required", "for", "DistanceToPath", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::292944826", "fields": {"term": "What components are required for DistanceToPath to work?", "namespace": "open-p", "hash": 292944826, "terms": ["What components are required for DistanceToPath to work?", "components are required for DistanceToPath to work?", "are required for DistanceToPath to work?", "required for DistanceToPath to work?", "for DistanceToPath to work?", "DistanceToPath to work?", "to work?", "work?"], "tokens": ["What", "components", "are", "required", "for", "DistanceToPath", "to", "work", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1941972178", "fields": {"term": "What is a rank-profile for DistanceToPath?", "namespace": "open-p", "hash": -1941972178, "terms": ["What is a rank-profile for DistanceToPath?", "is a rank-profile for DistanceToPath?", "a rank-profile for DistanceToPath?", "rank-profile for DistanceToPath?", "for DistanceToPath?", "DistanceToPath?"], "tokens": ["What", "is", "a", "rank", "profile", "for", "DistanceToPath", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1434611409", "fields": {"term": "What is *DistanceToPath*?", "namespace": "open-p", "hash": 1434611409, "terms": ["What is *DistanceToPath*?", "is *DistanceToPath*?", "*DistanceToPath*?"], "tokens": ["What", "is", "*", "DistanceToPath", "*?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1108689206", "fields": {"term": "What are the syntax expectations when using *DistanceToPath*?", "namespace": "open-p", "hash": -1108689206, "terms": ["What are the syntax expectations when using *DistanceToPath*?", "are the syntax expectations when using *DistanceToPath*?", "the syntax expectations when using *DistanceToPath*?", "syntax expectations when using *DistanceToPath*?", "expectations when using *DistanceToPath*?", "when using *DistanceToPath*?", "using *DistanceToPath*?", "*DistanceToPath*?"], "tokens": ["What", "are", "the", "syntax", "expectations", "when", "using", "*", "DistanceToPath", "*?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1910200127", "fields": {"term": "What is the path for *DistanceToPath*?", "namespace": "open-p", "hash": 1910200127, "terms": ["What is the path for *DistanceToPath*?", "is the path for *DistanceToPath*?", "the path for *DistanceToPath*?", "path for *DistanceToPath*?", "for *DistanceToPath*?", "*DistanceToPath*?"], "tokens": ["What", "is", "the", "path", "for", "*", "DistanceToPath", "*?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2073311515", "fields": {"term": "What is the format for supplying the path for *DistanceToPath*?", "namespace": "open-p", "hash": 2073311515, "terms": ["What is the format for supplying the path for *DistanceToPath*?", "is the format for supplying the path for *DistanceToPath*?", "the format for supplying the path for *DistanceToPath*?", "format for supplying the path for *DistanceToPath*?", "for supplying the path for *DistanceToPath*?", "supplying the path for *DistanceToPath*?", "the path for *DistanceToPath*?", "path for *DistanceToPath*?", "for *DistanceToPath*?", "*DistanceToPath*?"], "tokens": ["What", "is", "the", "format", "for", "supplying", "the", "path", "for", "*", "DistanceToPath", "*?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1510331725", "fields": {"term": "What is the purpose of the first component in the second rank-profile?", "namespace": "open-p", "hash": -1510331725, "terms": ["What is the purpose of the first component in the second rank-profile?", "is the purpose of the first component in the second rank-profile?", "the purpose of the first component in the second rank-profile?", "purpose of the first component in the second rank-profile?", "of the first component in the second rank-profile?", "the first component in the second rank-profile?", "first component in the second rank-profile?", "component in the second rank-profile?", "in the second rank-profile?", "the second rank-profile?", "second rank-profile?", "rank-profile?"], "tokens": ["What", "is", "the", "purpose", "of", "the", "first", "component", "in", "the", "second", "rank", "profile", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-432417230", "fields": {"term": "How does the second component influence ranking in the second rank-profile?", "namespace": "open-p", "hash": -432417230, "terms": ["How does the second component influence ranking in the second rank-profile?", "does the second component influence ranking in the second rank-profile?", "the second component influence ranking in the second rank-profile?", "second component influence ranking in the second rank-profile?", "component influence ranking in the second rank-profile?", "influence ranking in the second rank-profile?", "ranking in the second rank-profile?", "in the second rank-profile?", "the second rank-profile?", "second rank-profile?", "rank-profile?"], "tokens": ["How", "does", "the", "second", "component", "influence", "ranking", "in", "the", "second", "rank", "profile", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-342921923", "fields": {"term": "How does the second rank-profile improve over the first?", "namespace": "open-p", "hash": -342921923, "terms": ["How does the second rank-profile improve over the first?", "does the second rank-profile improve over the first?", "the second rank-profile improve over the first?", "second rank-profile improve over the first?", "rank-profile improve over the first?", "improve over the first?", "over the first?", "the first?", "first?"], "tokens": ["How", "does", "the", "second", "rank", "profile", "improve", "over", "the", "first", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-218024927", "fields": {"term": "What is the feature's output scale?", "namespace": "open-p", "hash": -218024927, "terms": ["What is the feature's output scale?", "is the feature's output scale?", "the feature's output scale?", "feature's output scale?", "output scale?", "scale?"], "tokens": ["What", "is", "the", "feature", "'", "s", "output", "scale", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-699056042", "fields": {"term": "What is the normalized range for traveled?", "namespace": "open-p", "hash": -699056042, "terms": ["What is the normalized range for traveled?", "is the normalized range for traveled?", "the normalized range for traveled?", "normalized range for traveled?", "range for traveled?", "for traveled?", "traveled?"], "tokens": ["What", "is", "the", "normalized", "range", "for", "traveled", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2017905417", "fields": {"term": "What does Vespa expect the parameters to be mapped by?", "namespace": "open-p", "hash": -2017905417, "terms": ["What does Vespa expect the parameters to be mapped by?", "does Vespa expect the parameters to be mapped by?", "Vespa expect the parameters to be mapped by?", "expect the parameters to be mapped by?", "the parameters to be mapped by?", "parameters to be mapped by?", "to be mapped by?", "be mapped by?", "mapped by?", "by?"], "tokens": ["What", "does", "Vespa", "expect", "the", "parameters", "to", "be", "mapped", "by", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-650558703", "fields": {"term": "What is Vespa's internal resolution?", "namespace": "open-p", "hash": -650558703, "terms": ["What is Vespa's internal resolution?", "is Vespa's internal resolution?", "Vespa's internal resolution?", "internal resolution?", "resolution?"], "tokens": ["What", "is", "Vespa", "'", "s", "internal", "resolution", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::908551086", "fields": {"term": "How do the parameters help retrieve the appropriate hits?", "namespace": "open-p", "hash": 908551086, "terms": ["How do the parameters help retrieve the appropriate hits?", "do the parameters help retrieve the appropriate hits?", "the parameters help retrieve the appropriate hits?", "parameters help retrieve the appropriate hits?", "help retrieve the appropriate hits?", "retrieve the appropriate hits?", "the appropriate hits?", "appropriate hits?", "hits?"], "tokens": ["How", "do", "the", "parameters", "help", "retrieve", "the", "appropriate", "hits", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1564926696", "fields": {"term": "What is ranking in Vespa?", "namespace": "open-p", "hash": 1564926696, "terms": ["What is ranking in Vespa?", "is ranking in Vespa?", "ranking in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["What", "is", "ranking", "in", "Vespa", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::-764736984", "fields": {"term": "How is ranking defined?", "namespace": "open-p", "hash": -764736984, "terms": ["How is ranking defined?", "is ranking defined?", "ranking defined?", "defined?"], "tokens": ["How", "is", "ranking", "defined", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::411232716", "fields": {"term": "What is doc.sd?", "namespace": "open-p", "hash": 411232716, "terms": ["What is doc.sd?", "is doc.sd?", "doc.sd?"], "tokens": ["What", "is", "doc", "sd", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::66515875", "fields": {"term": "Where can I find schema documentation?", "namespace": "open-p", "hash": 66515875, "terms": ["Where can I find schema documentation?", "can I find schema documentation?", "I find schema documentation?", "find schema documentation?", "schema documentation?", "documentation?"], "tokens": ["Where", "can", "I", "find", "schema", "documentation", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1260833381", "fields": {"term": "What can be configured in doc.sd?", "namespace": "open-p", "hash": -1260833381, "terms": ["What can be configured in doc.sd?", "can be configured in doc.sd?", "be configured in doc.sd?", "configured in doc.sd?", "in doc.sd?", "doc.sd?"], "tokens": ["What", "can", "be", "configured", "in", "doc", "sd", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1619576962", "fields": {"term": "What is the inlinks attribute?", "namespace": "open-p", "hash": 1619576962, "terms": ["What is the inlinks attribute?", "is the inlinks attribute?", "the inlinks attribute?", "inlinks attribute?", "attribute?"], "tokens": ["What", "is", "the", "inlinks", "attribute", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1208839682", "fields": {"term": "What is the relevance score?", "namespace": "open-p", "hash": 1208839682, "terms": ["What is the relevance score?", "is the relevance score?", "the relevance score?", "relevance score?", "score?"], "tokens": ["What", "is", "the", "relevance", "score", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::-1720668981", "fields": {"term": "What is the age_decay function?", "namespace": "open-p", "hash": -1720668981, "terms": ["What is the age_decay function?", "is the age_decay function?", "the age_decay function?", "age_decay function?", "function?"], "tokens": ["What", "is", "the", "age_decay", "function", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2004907516", "fields": {"term": "What is the num_inlinks function?", "namespace": "open-p", "hash": -2004907516, "terms": ["What is the num_inlinks function?", "is the num_inlinks function?", "the num_inlinks function?", "num_inlinks function?", "function?"], "tokens": ["What", "is", "the", "num_inlinks", "function", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::968386695", "fields": {"term": "What is the rank_score function?", "namespace": "open-p", "hash": 968386695, "terms": ["What is the rank_score function?", "is the rank_score function?", "the rank_score function?", "rank_score function?", "function?"], "tokens": ["What", "is", "the", "rank_score", "function", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::639556158", "fields": {"term": "What is document similarity?", "namespace": "open-p", "hash": 639556158, "terms": ["What is document similarity?", "is document similarity?", "document similarity?", "similarity?"], "tokens": ["What", "is", "document", "similarity", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1128673524", "fields": {"term": "What is term_count field?", "namespace": "open-p", "hash": 1128673524, "terms": ["What is term_count field?", "is term_count field?", "term_count field?", "field?"], "tokens": ["What", "is", "term_count", "field", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2062713738", "fields": {"term": "How to find similar documents?", "namespace": "open-p", "hash": 2062713738, "terms": ["How to find similar documents?", "to find similar documents?", "find similar documents?", "similar documents?", "documents?"], "tokens": ["How", "to", "find", "similar", "documents", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1130577029", "fields": {"term": "What is term_count_similarity?", "namespace": "open-p", "hash": 1130577029, "terms": ["What is term_count_similarity?", "is term_count_similarity?", "term_count_similarity?"], "tokens": ["What", "is", "term_count_similarity", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::796647087", "fields": {"term": "How to use input.query()?", "namespace": "open-p", "hash": 796647087, "terms": ["How to use input.query()?", "to use input.query()?", "use input.query()?", "input.query()?"], "tokens": ["How", "to", "use", "input", "query", "()?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-310095041", "fields": {"term": "How to transfer ranking features?", "namespace": "open-p", "hash": -310095041, "terms": ["How to transfer ranking features?", "to transfer ranking features?", "transfer ranking features?", "ranking features?", "features?"], "tokens": ["How", "to", "transfer", "ranking", "features", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-809162859", "fields": {"term": "What is overlap in in-links?", "namespace": "open-p", "hash": -809162859, "terms": ["What is overlap in in-links?", "is overlap in in-links?", "overlap in in-links?", "in in-links?", "in-links?"], "tokens": ["What", "is", "overlap", "in", "in", "links", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::652689856", "fields": {"term": "How are inlinks mapped?", "namespace": "open-p", "hash": 652689856, "terms": ["How are inlinks mapped?", "are inlinks mapped?", "inlinks mapped?", "mapped?"], "tokens": ["How", "are", "inlinks", "mapped", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1673244404", "fields": {"term": "How is inlink_similarity defined?", "namespace": "open-p", "hash": -1673244404, "terms": ["How is inlink_similarity defined?", "is inlink_similarity defined?", "inlink_similarity defined?", "defined?"], "tokens": ["How", "is", "inlink_similarity", "defined", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::805546240", "fields": {"term": "What is the problem of comparing two lists of links?", "namespace": "open-p", "hash": 805546240, "terms": ["What is the problem of comparing two lists of links?", "is the problem of comparing two lists of links?", "the problem of comparing two lists of links?", "problem of comparing two lists of links?", "of comparing two lists of links?", "comparing two lists of links?", "two lists of links?", "lists of links?", "of links?", "links?"], "tokens": ["What", "is", "the", "problem", "of", "comparing", "two", "lists", "of", "links", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1944139194", "fields": {"term": "How is the problem of comparing two lists of links transformed into a numerical problem?", "namespace": "open-p", "hash": 1944139194, "terms": ["How is the problem of comparing two lists of links transformed into a numerical problem?", "is the problem of comparing two lists of links transformed into a numerical problem?", "the problem of comparing two lists of links transformed into a numerical problem?", "problem of comparing two lists of links transformed into a numerical problem?", "of comparing two lists of links transformed into a numerical problem?", "comparing two lists of links transformed into a numerical problem?", "two lists of links transformed into a numerical problem?", "lists of links transformed into a numerical problem?", "of links transformed into a numerical problem?", "links transformed into a numerical problem?", "transformed into a numerical problem?", "into a numerical problem?", "a numerical problem?", "numerical problem?", "problem?"], "tokens": ["How", "is", "the", "problem", "of", "comparing", "two", "lists", "of", "links", "transformed", "into", "a", "numerical", "problem", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1928382946", "fields": {"term": "What is the purpose of splitting ranking into two phases?", "namespace": "open-p", "hash": 1928382946, "terms": ["What is the purpose of splitting ranking into two phases?", "is the purpose of splitting ranking into two phases?", "the purpose of splitting ranking into two phases?", "purpose of splitting ranking into two phases?", "of splitting ranking into two phases?", "splitting ranking into two phases?", "ranking into two phases?", "into two phases?", "two phases?", "phases?"], "tokens": ["What", "is", "the", "purpose", "of", "splitting", "ranking", "into", "two", "phases", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-761075860", "fields": {"term": "What is the purpose of num_inlinks() function?", "namespace": "open-p", "hash": -761075860, "terms": ["What is the purpose of num_inlinks() function?", "is the purpose of num_inlinks() function?", "the purpose of num_inlinks() function?", "purpose of num_inlinks() function?", "of num_inlinks() function?", "num_inlinks() function?", "function?"], "tokens": ["What", "is", "the", "purpose", "of", "num_inlinks", "()", "function", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1740259886", "fields": {"term": "What is the meaning of 'keep-rank-count'?", "namespace": "open-p", "hash": -1740259886, "terms": ["What is the meaning of 'keep-rank-count'?", "is the meaning of 'keep-rank-count'?", "the meaning of 'keep-rank-count'?", "meaning of 'keep-rank-count'?", "of 'keep-rank-count'?", "'keep-rank-count'?"], "tokens": ["What", "is", "the", "meaning", "of", "'", "keep", "rank", "count", "'?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2107042091", "fields": {"term": "What is 'query(threshold)' function used for?", "namespace": "open-p", "hash": -2107042091, "terms": ["What is 'query(threshold)' function used for?", "is 'query(threshold)' function used for?", "'query(threshold)' function used for?", "function used for?", "used for?", "for?"], "tokens": ["What", "is", "'", "query", "(", "threshold", ")'", "function", "used", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-605678223", "fields": {"term": "How is rank-score-drop-limit used in Vespa?", "namespace": "open-p", "hash": -605678223, "terms": ["How is rank-score-drop-limit used in Vespa?", "is rank-score-drop-limit used in Vespa?", "rank-score-drop-limit used in Vespa?", "used in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["How", "is", "rank", "score", "drop", "limit", "used", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1754253614", "fields": {"term": "What is the significance of using an if statement to assign -1 in ranking expressions?", "namespace": "open-p", "hash": 1754253614, "terms": ["What is the significance of using an if statement to assign -1 in ranking expressions?", "is the significance of using an if statement to assign -1 in ranking expressions?", "the significance of using an if statement to assign -1 in ranking expressions?", "significance of using an if statement to assign -1 in ranking expressions?", "of using an if statement to assign -1 in ranking expressions?", "using an if statement to assign -1 in ranking expressions?", "an if statement to assign -1 in ranking expressions?", "if statement to assign -1 in ranking expressions?", "statement to assign -1 in ranking expressions?", "to assign -1 in ranking expressions?", "assign -1 in ranking expressions?", "-1 in ranking expressions?", "in ranking expressions?", "ranking expressions?", "expressions?"], "tokens": ["What", "is", "the", "significance", "of", "using", "an", "if", "statement", "to", "assign", "1", "in", "ranking", "expressions", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::135048693", "fields": {"term": "What is reranking?", "namespace": "open-p", "hash": 135048693, "terms": ["What is reranking?", "is reranking?", "reranking?"], "tokens": ["What", "is", "reranking", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::164802120", "fields": {"term": "Where can I read about reranking?", "namespace": "open-p", "hash": 164802120, "terms": ["Where can I read about reranking?", "can I read about reranking?", "I read about reranking?", "read about reranking?", "about reranking?", "reranking?"], "tokens": ["Where", "can", "I", "read", "about", "reranking", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1947691220", "fields": {"term": "How to use Vespa for ML Model Serving?", "namespace": "open-p", "hash": 1947691220, "terms": ["How to use Vespa for ML Model Serving?", "to use Vespa for ML Model Serving?", "use Vespa for ML Model Serving?", "Vespa for ML Model Serving?", "for ML Model Serving?", "ML Model Serving?", "Model Serving?", "Serving?"], "tokens": ["How", "to", "use", "Vespa", "for", "ML", "Model", "Serving", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-372198458", "fields": {"term": "How do I secure a Vespa installation?", "namespace": "open-p", "hash": -372198458, "terms": ["How do I secure a Vespa installation?", "do I secure a Vespa installation?", "I secure a Vespa installation?", "secure a Vespa installation?", "a Vespa installation?", "Vespa installation?", "installation?"], "tokens": ["How", "do", "I", "secure", "a", "Vespa", "installation", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1852894722", "fields": {"term": "What is coverage?", "namespace": "open-p", "hash": 1852894722, "terms": ["What is coverage?", "is coverage?", "coverage?"], "tokens": ["What", "is", "coverage", "?"], "corpus_count": 3, "document_count": 3}}, -{"put": "id:term:term::51504561", "fields": {"term": "What is the degraded field?", "namespace": "open-p", "hash": 51504561, "terms": ["What is the degraded field?", "is the degraded field?", "the degraded field?", "degraded field?", "field?"], "tokens": ["What", "is", "the", "degraded", "field", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1282732957", "fields": {"term": "What is softtimeout?", "namespace": "open-p", "hash": 1282732957, "terms": ["What is softtimeout?", "is softtimeout?", "softtimeout?"], "tokens": ["What", "is", "softtimeout", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2097683784", "fields": {"term": "How is query coverage calculated?", "namespace": "open-p", "hash": -2097683784, "terms": ["How is query coverage calculated?", "is query coverage calculated?", "query coverage calculated?", "coverage calculated?", "calculated?"], "tokens": ["How", "is", "query", "coverage", "calculated", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1584311086", "fields": {"term": "What is match-phase ranking degradation?", "namespace": "open-p", "hash": 1584311086, "terms": ["What is match-phase ranking degradation?", "is match-phase ranking degradation?", "match-phase ranking degradation?", "ranking degradation?", "degradation?"], "tokens": ["What", "is", "match", "phase", "ranking", "degradation", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::690746523", "fields": {"term": "What is non-ideal-state in Vespa.ai?", "namespace": "open-p", "hash": 690746523, "terms": ["What is non-ideal-state in Vespa.ai?", "is non-ideal-state in Vespa.ai?", "non-ideal-state in Vespa.ai?", "in Vespa.ai?", "Vespa.ai?"], "tokens": ["What", "is", "non", "ideal", "state", "in", "Vespa", "ai", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::497409136", "fields": {"term": "How can coverage information be accessed programmatically?", "namespace": "open-p", "hash": 497409136, "terms": ["How can coverage information be accessed programmatically?", "can coverage information be accessed programmatically?", "coverage information be accessed programmatically?", "information be accessed programmatically?", "be accessed programmatically?", "accessed programmatically?", "programmatically?"], "tokens": ["How", "can", "coverage", "information", "be", "accessed", "programmatically", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-549820064", "fields": {"term": "What is adaptive coverage?", "namespace": "open-p", "hash": -549820064, "terms": ["What is adaptive coverage?", "is adaptive coverage?", "adaptive coverage?", "coverage?"], "tokens": ["What", "is", "adaptive", "coverage", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::47084397", "fields": {"term": "What is minimum coverage?", "namespace": "open-p", "hash": 47084397, "terms": ["What is minimum coverage?", "is minimum coverage?", "minimum coverage?", "coverage?"], "tokens": ["What", "is", "minimum", "coverage", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::-1091999094", "fields": {"term": "What is minimum wait setting?", "namespace": "open-p", "hash": -1091999094, "terms": ["What is minimum wait setting?", "is minimum wait setting?", "minimum wait setting?", "wait setting?", "setting?"], "tokens": ["What", "is", "minimum", "wait", "setting", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-859621623", "fields": {"term": "What is the max wait setting?", "namespace": "open-p", "hash": -859621623, "terms": ["What is the max wait setting?", "is the max wait setting?", "the max wait setting?", "max wait setting?", "wait setting?", "setting?"], "tokens": ["What", "is", "the", "max", "wait", "setting", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::995983495", "fields": {"term": "What is the default Vespa timeout?", "namespace": "open-p", "hash": 995983495, "terms": ["What is the default Vespa timeout?", "is the default Vespa timeout?", "the default Vespa timeout?", "default Vespa timeout?", "Vespa timeout?", "timeout?"], "tokens": ["What", "is", "the", "default", "Vespa", "timeout", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::544241178", "fields": {"term": "What is match-phase?", "namespace": "open-p", "hash": 544241178, "terms": ["What is match-phase?", "is match-phase?", "match-phase?"], "tokens": ["What", "is", "match", "phase", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::1417620796", "fields": {"term": "How does match-phase work?", "namespace": "open-p", "hash": 1417620796, "terms": ["How does match-phase work?", "does match-phase work?", "match-phase work?", "work?"], "tokens": ["How", "does", "match", "phase", "work", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::858652637", "fields": {"term": "What is max-hits?", "namespace": "open-p", "hash": 858652637, "terms": ["What is max-hits?", "is max-hits?", "max-hits?"], "tokens": ["What", "is", "max", "hits", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1358718746", "fields": {"term": "What is the purpose of match-phase?", "namespace": "open-p", "hash": 1358718746, "terms": ["What is the purpose of match-phase?", "is the purpose of match-phase?", "the purpose of match-phase?", "purpose of match-phase?", "of match-phase?", "match-phase?"], "tokens": ["What", "is", "the", "purpose", "of", "match", "phase", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::-1602770829", "fields": {"term": "What is the effect of match-phase on aggregation?", "namespace": "open-p", "hash": -1602770829, "terms": ["What is the effect of match-phase on aggregation?", "is the effect of match-phase on aggregation?", "the effect of match-phase on aggregation?", "effect of match-phase on aggregation?", "of match-phase on aggregation?", "match-phase on aggregation?", "on aggregation?", "aggregation?"], "tokens": ["What", "is", "the", "effect", "of", "match", "phase", "on", "aggregation", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1770413188", "fields": {"term": "What is the content node metric for match-phase?", "namespace": "open-p", "hash": -1770413188, "terms": ["What is the content node metric for match-phase?", "is the content node metric for match-phase?", "the content node metric for match-phase?", "content node metric for match-phase?", "node metric for match-phase?", "metric for match-phase?", "for match-phase?", "match-phase?"], "tokens": ["What", "is", "the", "content", "node", "metric", "for", "match", "phase", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1962953326", "fields": {"term": "Can match-phase be used with WAND operator?", "namespace": "open-p", "hash": -1962953326, "terms": ["Can match-phase be used with WAND operator?", "match-phase be used with WAND operator?", "be used with WAND operator?", "used with WAND operator?", "with WAND operator?", "WAND operator?", "operator?"], "tokens": ["Can", "match", "phase", "be", "used", "with", "WAND", "operator", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1362423560", "fields": {"term": "What does the graph in the document show?", "namespace": "open-p", "hash": -1362423560, "terms": ["What does the graph in the document show?", "does the graph in the document show?", "the graph in the document show?", "graph in the document show?", "in the document show?", "the document show?", "document show?", "show?"], "tokens": ["What", "does", "the", "graph", "in", "the", "document", "show", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1489790489", "fields": {"term": "How does match-phase affect latency?", "namespace": "open-p", "hash": 1489790489, "terms": ["How does match-phase affect latency?", "does match-phase affect latency?", "match-phase affect latency?", "affect latency?", "latency?"], "tokens": ["How", "does", "match", "phase", "affect", "latency", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1697394223", "fields": {"term": "What is the impact of match-phase on filters?", "namespace": "open-p", "hash": -1697394223, "terms": ["What is the impact of match-phase on filters?", "is the impact of match-phase on filters?", "the impact of match-phase on filters?", "impact of match-phase on filters?", "of match-phase on filters?", "match-phase on filters?", "on filters?", "filters?"], "tokens": ["What", "is", "the", "impact", "of", "match", "phase", "on", "filters", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1347432090", "fields": {"term": "How does Vespa evaluate potential matches?", "namespace": "open-p", "hash": 1347432090, "terms": ["How does Vespa evaluate potential matches?", "does Vespa evaluate potential matches?", "Vespa evaluate potential matches?", "evaluate potential matches?", "potential matches?", "matches?"], "tokens": ["How", "does", "Vespa", "evaluate", "potential", "matches", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-452871925", "fields": {"term": "Why can match-phase have higher latencies with filters?", "namespace": "open-p", "hash": -452871925, "terms": ["Why can match-phase have higher latencies with filters?", "can match-phase have higher latencies with filters?", "match-phase have higher latencies with filters?", "have higher latencies with filters?", "higher latencies with filters?", "latencies with filters?", "with filters?", "filters?"], "tokens": ["Why", "can", "match", "phase", "have", "higher", "latencies", "with", "filters", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1896426140", "fields": {"term": "Why is total-hits reported differently with match-phase enabled?", "namespace": "open-p", "hash": -1896426140, "terms": ["Why is total-hits reported differently with match-phase enabled?", "is total-hits reported differently with match-phase enabled?", "total-hits reported differently with match-phase enabled?", "reported differently with match-phase enabled?", "differently with match-phase enabled?", "with match-phase enabled?", "match-phase enabled?", "enabled?"], "tokens": ["Why", "is", "total", "hits", "reported", "differently", "with", "match", "phase", "enabled", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2028396124", "fields": {"term": "What is the Vespa grouping guide?", "namespace": "open-p", "hash": 2028396124, "terms": ["What is the Vespa grouping guide?", "is the Vespa grouping guide?", "the Vespa grouping guide?", "Vespa grouping guide?", "grouping guide?", "guide?"], "tokens": ["What", "is", "the", "Vespa", "grouping", "guide", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-323745133", "fields": {"term": "How does using a multivalued attribute affect performance?", "namespace": "open-p", "hash": -323745133, "terms": ["How does using a multivalued attribute affect performance?", "does using a multivalued attribute affect performance?", "using a multivalued attribute affect performance?", "a multivalued attribute affect performance?", "multivalued attribute affect performance?", "attribute affect performance?", "affect performance?", "performance?"], "tokens": ["How", "does", "using", "a", "multivalued", "attribute", "affect", "performance", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1828914805", "fields": {"term": "What is the 'group' clause?", "namespace": "open-p", "hash": -1828914805, "terms": ["What is the 'group' clause?", "is the 'group' clause?", "the 'group' clause?", "'group' clause?", "clause?"], "tokens": ["What", "is", "the", "'", "group", "'", "clause", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::622647423", "fields": {"term": "How do you group query results?", "namespace": "open-p", "hash": 622647423, "terms": ["How do you group query results?", "do you group query results?", "you group query results?", "group query results?", "query results?", "results?"], "tokens": ["How", "do", "you", "group", "query", "results", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1943523411", "fields": {"term": "What expressions can be used for grouping?", "namespace": "open-p", "hash": 1943523411, "terms": ["What expressions can be used for grouping?", "expressions can be used for grouping?", "can be used for grouping?", "be used for grouping?", "used for grouping?", "for grouping?", "grouping?"], "tokens": ["What", "expressions", "can", "be", "used", "for", "grouping", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1436141711", "fields": {"term": "Can groups contain subgroups?", "namespace": "open-p", "hash": -1436141711, "terms": ["Can groups contain subgroups?", "groups contain subgroups?", "contain subgroups?", "subgroups?"], "tokens": ["Can", "groups", "contain", "subgroups", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-626481804", "fields": {"term": "What are group lists and hit lists?", "namespace": "open-p", "hash": -626481804, "terms": ["What are group lists and hit lists?", "are group lists and hit lists?", "group lists and hit lists?", "lists and hit lists?", "and hit lists?", "hit lists?", "lists?"], "tokens": ["What", "are", "group", "lists", "and", "hit", "lists", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1843344322", "fields": {"term": "How is the identity of a group held?", "namespace": "open-p", "hash": -1843344322, "terms": ["How is the identity of a group held?", "is the identity of a group held?", "the identity of a group held?", "identity of a group held?", "of a group held?", "a group held?", "group held?", "held?"], "tokens": ["How", "is", "the", "identity", "of", "a", "group", "held", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1866108406", "fields": {"term": "How are range identities separated for bucket aggregation?", "namespace": "open-p", "hash": -1866108406, "terms": ["How are range identities separated for bucket aggregation?", "are range identities separated for bucket aggregation?", "range identities separated for bucket aggregation?", "identities separated for bucket aggregation?", "separated for bucket aggregation?", "for bucket aggregation?", "bucket aggregation?", "aggregation?"], "tokens": ["How", "are", "range", "identities", "separated", "for", "bucket", "aggregation", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1035280201", "fields": {"term": "What is a multivalue attribute?", "namespace": "open-p", "hash": 1035280201, "terms": ["What is a multivalue attribute?", "is a multivalue attribute?", "a multivalue attribute?", "multivalue attribute?", "attribute?"], "tokens": ["What", "is", "a", "multivalue", "attribute", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::35599201", "fields": {"term": "What are the grouping options for maps?", "namespace": "open-p", "hash": 35599201, "terms": ["What are the grouping options for maps?", "are the grouping options for maps?", "the grouping options for maps?", "grouping options for maps?", "options for maps?", "for maps?", "maps?"], "tokens": ["What", "are", "the", "grouping", "options", "for", "maps", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1717320879", "fields": {"term": "Can tensors be used in grouping?", "namespace": "open-p", "hash": 1717320879, "terms": ["Can tensors be used in grouping?", "tensors be used in grouping?", "be used in grouping?", "used in grouping?", "in grouping?", "grouping?"], "tokens": ["Can", "tensors", "be", "used", "in", "grouping", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1444756971", "fields": {"term": "What is grouping order?", "namespace": "open-p", "hash": 1444756971, "terms": ["What is grouping order?", "is grouping order?", "grouping order?", "order?"], "tokens": ["What", "is", "grouping", "order", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1358665810", "fields": {"term": "How does multi-level grouping work?", "namespace": "open-p", "hash": 1358665810, "terms": ["How does multi-level grouping work?", "does multi-level grouping work?", "multi-level grouping work?", "grouping work?", "work?"], "tokens": ["How", "does", "multi", "level", "grouping", "work", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1508437531", "fields": {"term": "What is locale aware sorting?", "namespace": "open-p", "hash": 1508437531, "terms": ["What is locale aware sorting?", "is locale aware sorting?", "locale aware sorting?", "aware sorting?", "sorting?"], "tokens": ["What", "is", "locale", "aware", "sorting", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-735545343", "fields": {"term": "Which aggregates allow ordering?", "namespace": "open-p", "hash": -735545343, "terms": ["Which aggregates allow ordering?", "aggregates allow ordering?", "allow ordering?", "ordering?"], "tokens": ["Which", "aggregates", "allow", "ordering", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1311131746", "fields": {"term": "What is the default ordering?", "namespace": "open-p", "hash": -1311131746, "terms": ["What is the default ordering?", "is the default ordering?", "the default ordering?", "default ordering?", "ordering?"], "tokens": ["What", "is", "the", "default", "ordering", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::930034585", "fields": {"term": "What is `max` and `order`?", "namespace": "open-p", "hash": 930034585, "terms": ["What is `max` and `order`?", "is `max` and `order`?", "`max` and `order`?", "and `order`?", "`order`?"], "tokens": ["What", "is", "`", "max", "`", "and", "`", "order", "`?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1287561742", "fields": {"term": "How does `order` affect group ordering?", "namespace": "open-p", "hash": 1287561742, "terms": ["How does `order` affect group ordering?", "does `order` affect group ordering?", "`order` affect group ordering?", "affect group ordering?", "group ordering?", "ordering?"], "tokens": ["How", "does", "`", "order", "`", "affect", "group", "ordering", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-245677472", "fields": {"term": "What aggregation functions does `order` affect?", "namespace": "open-p", "hash": -245677472, "terms": ["What aggregation functions does `order` affect?", "aggregation functions does `order` affect?", "functions does `order` affect?", "does `order` affect?", "`order` affect?", "affect?"], "tokens": ["What", "aggregation", "functions", "does", "`", "order", "`", "affect", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1530889032", "fields": {"term": "What are continuations?", "namespace": "open-p", "hash": 1530889032, "terms": ["What are continuations?", "are continuations?", "continuations?"], "tokens": ["What", "are", "continuations", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-68457032", "fields": {"term": "How are continuations used?", "namespace": "open-p", "hash": -68457032, "terms": ["How are continuations used?", "are continuations used?", "continuations used?", "used?"], "tokens": ["How", "are", "continuations", "used", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-911700410", "fields": {"term": "What is pagination in Vespa?", "namespace": "open-p", "hash": -911700410, "terms": ["What is pagination in Vespa?", "is pagination in Vespa?", "pagination in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["What", "is", "pagination", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::841667206", "fields": {"term": "How do continuations enable pagination?", "namespace": "open-p", "hash": 841667206, "terms": ["How do continuations enable pagination?", "do continuations enable pagination?", "continuations enable pagination?", "enable pagination?", "pagination?"], "tokens": ["How", "do", "continuations", "enable", "pagination", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::985995513", "fields": {"term": "What is the role of 'this' continuation in pagination?", "namespace": "open-p", "hash": 985995513, "terms": ["What is the role of 'this' continuation in pagination?", "is the role of 'this' continuation in pagination?", "the role of 'this' continuation in pagination?", "role of 'this' continuation in pagination?", "of 'this' continuation in pagination?", "'this' continuation in pagination?", "continuation in pagination?", "in pagination?", "pagination?"], "tokens": ["What", "is", "the", "role", "of", "'", "this", "'", "continuation", "in", "pagination", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2076920005", "fields": {"term": "What are the types of continuations?", "namespace": "open-p", "hash": 2076920005, "terms": ["What are the types of continuations?", "are the types of continuations?", "the types of continuations?", "types of continuations?", "of continuations?", "continuations?"], "tokens": ["What", "are", "the", "types", "of", "continuations", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-767227903", "fields": {"term": "How do I programmatically work with grouping in Vespa?", "namespace": "open-p", "hash": -767227903, "terms": ["How do I programmatically work with grouping in Vespa?", "do I programmatically work with grouping in Vespa?", "I programmatically work with grouping in Vespa?", "programmatically work with grouping in Vespa?", "work with grouping in Vespa?", "with grouping in Vespa?", "grouping in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["How", "do", "I", "programmatically", "work", "with", "grouping", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1719713148", "fields": {"term": "Where can I find examples of pagination using continuations in Vespa?", "namespace": "open-p", "hash": 1719713148, "terms": ["Where can I find examples of pagination using continuations in Vespa?", "can I find examples of pagination using continuations in Vespa?", "I find examples of pagination using continuations in Vespa?", "find examples of pagination using continuations in Vespa?", "examples of pagination using continuations in Vespa?", "of pagination using continuations in Vespa?", "pagination using continuations in Vespa?", "using continuations in Vespa?", "continuations in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["Where", "can", "I", "find", "examples", "of", "pagination", "using", "continuations", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-315213376", "fields": {"term": "What is the `each` keyword used for?", "namespace": "open-p", "hash": -315213376, "terms": ["What is the `each` keyword used for?", "is the `each` keyword used for?", "the `each` keyword used for?", "`each` keyword used for?", "keyword used for?", "used for?", "for?"], "tokens": ["What", "is", "the", "`", "each", "`", "keyword", "used", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1618018887", "fields": {"term": "How can a label be assigned?", "namespace": "open-p", "hash": 1618018887, "terms": ["How can a label be assigned?", "can a label be assigned?", "a label be assigned?", "label be assigned?", "be assigned?", "assigned?"], "tokens": ["How", "can", "a", "label", "be", "assigned", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1244957417", "fields": {"term": "What is precision() function?", "namespace": "open-p", "hash": 1244957417, "terms": ["What is precision() function?", "is precision() function?", "precision() function?", "function?"], "tokens": ["What", "is", "precision", "()", "function", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-830936526", "fields": {"term": "How does precision affect accuracy?", "namespace": "open-p", "hash": -830936526, "terms": ["How does precision affect accuracy?", "does precision affect accuracy?", "precision affect accuracy?", "affect accuracy?", "accuracy?"], "tokens": ["How", "does", "precision", "affect", "accuracy", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1555656466", "fields": {"term": "What are relevant query parameters?", "namespace": "open-p", "hash": -1555656466, "terms": ["What are relevant query parameters?", "are relevant query parameters?", "relevant query parameters?", "query parameters?", "parameters?"], "tokens": ["What", "are", "relevant", "query", "parameters", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::342696339", "fields": {"term": "What is groupingSessionCache used for?", "namespace": "open-p", "hash": 342696339, "terms": ["What is groupingSessionCache used for?", "is groupingSessionCache used for?", "groupingSessionCache used for?", "used for?", "for?"], "tokens": ["What", "is", "groupingSessionCache", "used", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-703021998", "fields": {"term": "What is grouping.defaultMaxGroups?", "namespace": "open-p", "hash": -703021998, "terms": ["What is grouping.defaultMaxGroups?", "is grouping.defaultMaxGroups?", "grouping.defaultMaxGroups?"], "tokens": ["What", "is", "grouping", "defaultMaxGroups", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-459495155", "fields": {"term": "What is grouping.defaultMaxHits?", "namespace": "open-p", "hash": -459495155, "terms": ["What is grouping.defaultMaxHits?", "is grouping.defaultMaxHits?", "grouping.defaultMaxHits?"], "tokens": ["What", "is", "grouping", "defaultMaxHits", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1367144597", "fields": {"term": "What is grouping.globalMaxGroups?", "namespace": "open-p", "hash": 1367144597, "terms": ["What is grouping.globalMaxGroups?", "is grouping.globalMaxGroups?", "grouping.globalMaxGroups?"], "tokens": ["What", "is", "grouping", "globalMaxGroups", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::140790402", "fields": {"term": "What is grouping.defaultPrecisionFactor?", "namespace": "open-p", "hash": 140790402, "terms": ["What is grouping.defaultPrecisionFactor?", "is grouping.defaultPrecisionFactor?", "grouping.defaultPrecisionFactor?"], "tokens": ["What", "is", "grouping", "defaultPrecisionFactor", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-711177411", "fields": {"term": "What is the session cache?", "namespace": "open-p", "hash": -711177411, "terms": ["What is the session cache?", "is the session cache?", "the session cache?", "session cache?", "cache?"], "tokens": ["What", "is", "the", "session", "cache", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1913970285", "fields": {"term": "What does the session cache do?", "namespace": "open-p", "hash": -1913970285, "terms": ["What does the session cache do?", "does the session cache do?", "the session cache do?", "session cache do?", "cache do?", "do?"], "tokens": ["What", "does", "the", "session", "cache", "do", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1222408320", "fields": {"term": "How does session cache work?", "namespace": "open-p", "hash": -1222408320, "terms": ["How does session cache work?", "does session cache work?", "session cache work?", "cache work?", "work?"], "tokens": ["How", "does", "session", "cache", "work", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1119158553", "fields": {"term": "Why use session cache for grouping?", "namespace": "open-p", "hash": 1119158553, "terms": ["Why use session cache for grouping?", "use session cache for grouping?", "session cache for grouping?", "cache for grouping?", "for grouping?", "grouping?"], "tokens": ["Why", "use", "session", "cache", "for", "grouping", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1261581708", "fields": {"term": "What is the drawback of multi-level grouping?", "namespace": "open-p", "hash": -1261581708, "terms": ["What is the drawback of multi-level grouping?", "is the drawback of multi-level grouping?", "the drawback of multi-level grouping?", "drawback of multi-level grouping?", "of multi-level grouping?", "multi-level grouping?", "grouping?"], "tokens": ["What", "is", "the", "drawback", "of", "multi", "level", "grouping", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::713433500", "fields": {"term": "How does session cache improve performance?", "namespace": "open-p", "hash": 713433500, "terms": ["How does session cache improve performance?", "does session cache improve performance?", "session cache improve performance?", "cache improve performance?", "improve performance?", "performance?"], "tokens": ["How", "does", "session", "cache", "improve", "performance", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1211480840", "fields": {"term": "What are intermediate grouping results?", "namespace": "open-p", "hash": -1211480840, "terms": ["What are intermediate grouping results?", "are intermediate grouping results?", "intermediate grouping results?", "grouping results?", "results?"], "tokens": ["What", "are", "intermediate", "grouping", "results", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2041287991", "fields": {"term": "What is the purpose of session cache in Vespa?", "namespace": "open-p", "hash": -2041287991, "terms": ["What is the purpose of session cache in Vespa?", "is the purpose of session cache in Vespa?", "the purpose of session cache in Vespa?", "purpose of session cache in Vespa?", "of session cache in Vespa?", "session cache in Vespa?", "cache in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["What", "is", "the", "purpose", "of", "session", "cache", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-198697784", "fields": {"term": "What are grouping aggregators?", "namespace": "open-p", "hash": -198697784, "terms": ["What are grouping aggregators?", "are grouping aggregators?", "grouping aggregators?", "aggregators?"], "tokens": ["What", "are", "grouping", "aggregators", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1142813519", "fields": {"term": "What is HyperLogLog++?", "namespace": "open-p", "hash": -1142813519, "terms": ["What is HyperLogLog++?", "is HyperLogLog++?", "HyperLogLog++?"], "tokens": ["What", "is", "HyperLogLog", "++?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::781009969", "fields": {"term": "What does 'output(sum())' do?", "namespace": "open-p", "hash": 781009969, "terms": ["What does 'output(sum())' do?", "does 'output(sum())' do?", "'output(sum())' do?", "do?"], "tokens": ["What", "does", "'", "output", "(", "sum", "())'", "do", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1748681723", "fields": {"term": "What is the result of 'max'myarray' on a multivalued field?", "namespace": "open-p", "hash": -1748681723, "terms": ["What is the result of 'max'myarray' on a multivalued field?", "is the result of 'max'myarray' on a multivalued field?", "the result of 'max'myarray' on a multivalued field?", "result of 'max'myarray' on a multivalued field?", "of 'max'myarray' on a multivalued field?", "'max'myarray' on a multivalued field?", "on a multivalued field?", "a multivalued field?", "multivalued field?", "field?"], "tokens": ["What", "is", "the", "result", "of", "'", "max", "'", "myarray", "'", "on", "a", "multivalued", "field", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-282583937", "fields": {"term": "What is the result type when all arguments are numeric?", "namespace": "open-p", "hash": -282583937, "terms": ["What is the result type when all arguments are numeric?", "is the result type when all arguments are numeric?", "the result type when all arguments are numeric?", "result type when all arguments are numeric?", "type when all arguments are numeric?", "when all arguments are numeric?", "all arguments are numeric?", "arguments are numeric?", "are numeric?", "numeric?"], "tokens": ["What", "is", "the", "result", "type", "when", "all", "arguments", "are", "numeric", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-156104906", "fields": {"term": "What is the 'summary' aggregator used for?", "namespace": "open-p", "hash": -156104906, "terms": ["What is the 'summary' aggregator used for?", "is the 'summary' aggregator used for?", "the 'summary' aggregator used for?", "'summary' aggregator used for?", "aggregator used for?", "used for?", "for?"], "tokens": ["What", "is", "the", "'", "summary", "'", "aggregator", "used", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::598065550", "fields": {"term": "What are arithmetic expressions?", "namespace": "open-p", "hash": 598065550, "terms": ["What are arithmetic expressions?", "are arithmetic expressions?", "arithmetic expressions?", "expressions?"], "tokens": ["What", "are", "arithmetic", "expressions", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::254791948", "fields": {"term": "What is interpolatedlookup?", "namespace": "open-p", "hash": 254791948, "terms": ["What is interpolatedlookup?", "is interpolatedlookup?", "interpolatedlookup?"], "tokens": ["What", "is", "interpolatedlookup", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::784293168", "fields": {"term": "What is zcurve.x?", "namespace": "open-p", "hash": 784293168, "terms": ["What is zcurve.x?", "is zcurve.x?", "zcurve.x?"], "tokens": ["What", "is", "zcurve", "x", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1114151984", "fields": {"term": "What is math.cosh?", "namespace": "open-p", "hash": 1114151984, "terms": ["What is math.cosh?", "is math.cosh?", "math.cosh?"], "tokens": ["What", "is", "math", "cosh", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-51089071", "fields": {"term": "What is the usage of time expressions?", "namespace": "open-p", "hash": -51089071, "terms": ["What is the usage of time expressions?", "is the usage of time expressions?", "the usage of time expressions?", "usage of time expressions?", "of time expressions?", "time expressions?", "expressions?"], "tokens": ["What", "is", "the", "usage", "of", "time", "expressions", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1876152927", "fields": {"term": "What is the usage of tolong type conversion expression?", "namespace": "open-p", "hash": 1876152927, "terms": ["What is the usage of tolong type conversion expression?", "is the usage of tolong type conversion expression?", "the usage of tolong type conversion expression?", "usage of tolong type conversion expression?", "of tolong type conversion expression?", "tolong type conversion expression?", "type conversion expression?", "conversion expression?", "expression?"], "tokens": ["What", "is", "the", "usage", "of", "tolong", "type", "conversion", "expression", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1568832849", "fields": {"term": "What is relation between reversed and sort expression?", "namespace": "open-p", "hash": 1568832849, "terms": ["What is relation between reversed and sort expression?", "is relation between reversed and sort expression?", "relation between reversed and sort expression?", "between reversed and sort expression?", "reversed and sort expression?", "and sort expression?", "sort expression?", "expression?"], "tokens": ["What", "is", "relation", "between", "reversed", "and", "sort", "expression", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-12430872", "fields": {"term": "What is a math value?", "namespace": "open-p", "hash": -12430872, "terms": ["What is a math value?", "is a math value?", "a math value?", "math value?", "value?"], "tokens": ["What", "is", "a", "math", "value", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-575636413", "fields": {"term": "What is Vespa grouping language?", "namespace": "open-p", "hash": -575636413, "terms": ["What is Vespa grouping language?", "is Vespa grouping language?", "Vespa grouping language?", "grouping language?", "language?"], "tokens": ["What", "is", "Vespa", "grouping", "language", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-507677458", "fields": {"term": "What does a grouping statement do?", "namespace": "open-p", "hash": -507677458, "terms": ["What does a grouping statement do?", "does a grouping statement do?", "a grouping statement do?", "grouping statement do?", "statement do?", "do?"], "tokens": ["What", "does", "a", "grouping", "statement", "do", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1814296898", "fields": {"term": "What is the Grouping reference?", "namespace": "open-p", "hash": 1814296898, "terms": ["What is the Grouping reference?", "is the Grouping reference?", "the Grouping reference?", "Grouping reference?", "reference?"], "tokens": ["What", "is", "the", "Grouping", "reference", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::531511276", "fields": {"term": "What fields can be used in grouping?", "namespace": "open-p", "hash": 531511276, "terms": ["What fields can be used in grouping?", "fields can be used in grouping?", "can be used in grouping?", "be used in grouping?", "used in grouping?", "in grouping?", "grouping?"], "tokens": ["What", "fields", "can", "be", "used", "in", "grouping", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1633068283", "fields": {"term": "What are the operations in grouping?", "namespace": "open-p", "hash": 1633068283, "terms": ["What are the operations in grouping?", "are the operations in grouping?", "the operations in grouping?", "operations in grouping?", "in grouping?", "grouping?"], "tokens": ["What", "are", "the", "operations", "in", "grouping", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1070849395", "fields": {"term": "How does Vespa execute grouping?", "namespace": "open-p", "hash": -1070849395, "terms": ["How does Vespa execute grouping?", "does Vespa execute grouping?", "Vespa execute grouping?", "execute grouping?", "grouping?"], "tokens": ["How", "does", "Vespa", "execute", "grouping", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::790048786", "fields": {"term": "What is a grouping specification?", "namespace": "open-p", "hash": 790048786, "terms": ["What is a grouping specification?", "is a grouping specification?", "a grouping specification?", "grouping specification?", "specification?"], "tokens": ["What", "is", "a", "grouping", "specification", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::22964927", "fields": {"term": "What is the output of a grouping?", "namespace": "open-p", "hash": 22964927, "terms": ["What is the output of a grouping?", "is the output of a grouping?", "the output of a grouping?", "output of a grouping?", "of a grouping?", "a grouping?", "grouping?"], "tokens": ["What", "is", "the", "output", "of", "a", "grouping", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1560394177", "fields": {"term": "What is the structure of the computation?", "namespace": "open-p", "hash": -1560394177, "terms": ["What is the structure of the computation?", "is the structure of the computation?", "the structure of the computation?", "structure of the computation?", "of the computation?", "the computation?", "computation?"], "tokens": ["What", "is", "the", "structure", "of", "the", "computation", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-360559063", "fields": {"term": "What is the cost of a grouping query?", "namespace": "open-p", "hash": -360559063, "terms": ["What is the cost of a grouping query?", "is the cost of a grouping query?", "the cost of a grouping query?", "cost of a grouping query?", "of a grouping query?", "a grouping query?", "grouping query?", "query?"], "tokens": ["What", "is", "the", "cost", "of", "a", "grouping", "query", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::748526565", "fields": {"term": "What item did Brown buy last?", "namespace": "open-p", "hash": 748526565, "terms": ["What item did Brown buy last?", "item did Brown buy last?", "did Brown buy last?", "Brown buy last?", "buy last?", "last?"], "tokens": ["What", "item", "did", "Brown", "buy", "last", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::944386316", "fields": {"term": "How much did Smith spend?", "namespace": "open-p", "hash": 944386316, "terms": ["How much did Smith spend?", "much did Smith spend?", "did Smith spend?", "Smith spend?", "spend?"], "tokens": ["How", "much", "did", "Smith", "spend", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::966429894", "fields": {"term": "Who purchased the oil pan?", "namespace": "open-p", "hash": 966429894, "terms": ["Who purchased the oil pan?", "purchased the oil pan?", "the oil pan?", "oil pan?", "pan?"], "tokens": ["Who", "purchased", "the", "oil", "pan", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-425224831", "fields": {"term": "What did Jones buy on September 8th?", "namespace": "open-p", "hash": -425224831, "terms": ["What did Jones buy on September 8th?", "did Jones buy on September 8th?", "Jones buy on September 8th?", "buy on September 8th?", "on September 8th?", "September 8th?", "8th?"], "tokens": ["What", "did", "Jones", "buy", "on", "September", "8th", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1355256153", "fields": {"term": "What query returns sum(price) grouped by customer?", "namespace": "open-p", "hash": -1355256153, "terms": ["What query returns sum(price) grouped by customer?", "query returns sum(price) grouped by customer?", "returns sum(price) grouped by customer?", "sum(price) grouped by customer?", "grouped by customer?", "by customer?", "customer?"], "tokens": ["What", "query", "returns", "sum", "(", "price", ")", "grouped", "by", "customer", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-116730365", "fields": {"term": "What query returns sum(price) grouped by date?", "namespace": "open-p", "hash": -116730365, "terms": ["What query returns sum(price) grouped by date?", "query returns sum(price) grouped by date?", "returns sum(price) grouped by date?", "sum(price) grouped by date?", "grouped by date?", "by date?", "date?"], "tokens": ["What", "query", "returns", "sum", "(", "price", ")", "grouped", "by", "date", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::281768007", "fields": {"term": "What is the purpose of ordering groups?", "namespace": "open-p", "hash": 281768007, "terms": ["What is the purpose of ordering groups?", "is the purpose of ordering groups?", "the purpose of ordering groups?", "purpose of ordering groups?", "of ordering groups?", "ordering groups?", "groups?"], "tokens": ["What", "is", "the", "purpose", "of", "ordering", "groups", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::719423401", "fields": {"term": "How is the number of returned groups limited?", "namespace": "open-p", "hash": 719423401, "terms": ["How is the number of returned groups limited?", "is the number of returned groups limited?", "the number of returned groups limited?", "number of returned groups limited?", "of returned groups limited?", "returned groups limited?", "groups limited?", "limited?"], "tokens": ["How", "is", "the", "number", "of", "returned", "groups", "limited", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-702204938", "fields": {"term": "What is the grouping.defaultMaxGroups query parameter?", "namespace": "open-p", "hash": -702204938, "terms": ["What is the grouping.defaultMaxGroups query parameter?", "is the grouping.defaultMaxGroups query parameter?", "the grouping.defaultMaxGroups query parameter?", "grouping.defaultMaxGroups query parameter?", "query parameter?", "parameter?"], "tokens": ["What", "is", "the", "grouping", "defaultMaxGroups", "query", "parameter", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::361407889", "fields": {"term": "What is precision in grouping?", "namespace": "open-p", "hash": 361407889, "terms": ["What is precision in grouping?", "is precision in grouping?", "precision in grouping?", "in grouping?", "grouping?"], "tokens": ["What", "is", "precision", "in", "grouping", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::384699888", "fields": {"term": "What happens if precision is not specified?", "namespace": "open-p", "hash": 384699888, "terms": ["What happens if precision is not specified?", "happens if precision is not specified?", "if precision is not specified?", "precision is not specified?", "is not specified?", "not specified?", "specified?"], "tokens": ["What", "happens", "if", "precision", "is", "not", "specified", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-613321834", "fields": {"term": "What is the defaultPrecisionFactor query parameter?", "namespace": "open-p", "hash": -613321834, "terms": ["What is the defaultPrecisionFactor query parameter?", "is the defaultPrecisionFactor query parameter?", "the defaultPrecisionFactor query parameter?", "defaultPrecisionFactor query parameter?", "query parameter?", "parameter?"], "tokens": ["What", "is", "the", "defaultPrecisionFactor", "query", "parameter", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::405022399", "fields": {"term": "In what scenario is precision not required?", "namespace": "open-p", "hash": 405022399, "terms": ["In what scenario is precision not required?", "what scenario is precision not required?", "scenario is precision not required?", "is precision not required?", "precision not required?", "not required?", "required?"], "tokens": ["In", "what", "scenario", "is", "precision", "not", "required", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1511945172", "fields": {"term": "What is the exception to precision requirement?", "namespace": "open-p", "hash": -1511945172, "terms": ["What is the exception to precision requirement?", "is the exception to precision requirement?", "the exception to precision requirement?", "exception to precision requirement?", "to precision requirement?", "precision requirement?", "requirement?"], "tokens": ["What", "is", "the", "exception", "to", "precision", "requirement", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::431917123", "fields": {"term": "How to limit the number of hits per group?", "namespace": "open-p", "hash": 431917123, "terms": ["How to limit the number of hits per group?", "to limit the number of hits per group?", "limit the number of hits per group?", "the number of hits per group?", "number of hits per group?", "of hits per group?", "hits per group?", "per group?", "group?"], "tokens": ["How", "to", "limit", "the", "number", "of", "hits", "per", "group", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1705183938", "fields": {"term": "What is 'grouping.defaultMaxHits'?", "namespace": "open-p", "hash": 1705183938, "terms": ["What is 'grouping.defaultMaxHits'?", "is 'grouping.defaultMaxHits'?", "'grouping.defaultMaxHits'?"], "tokens": ["What", "is", "'", "grouping", "defaultMaxHits", "'?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1201015067", "fields": {"term": "Can defaultMaxHits be overridden?", "namespace": "open-p", "hash": 1201015067, "terms": ["Can defaultMaxHits be overridden?", "defaultMaxHits be overridden?", "be overridden?", "overridden?"], "tokens": ["Can", "defaultMaxHits", "be", "overridden", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1342851607", "fields": {"term": "What happens when max is explicitly specified?", "namespace": "open-p", "hash": -1342851607, "terms": ["What happens when max is explicitly specified?", "happens when max is explicitly specified?", "when max is explicitly specified?", "max is explicitly specified?", "is explicitly specified?", "explicitly specified?", "specified?"], "tokens": ["What", "happens", "when", "max", "is", "explicitly", "specified", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-807195314", "fields": {"term": "How to retrieve all hits with max?", "namespace": "open-p", "hash": -807195314, "terms": ["How to retrieve all hits with max?", "to retrieve all hits with max?", "retrieve all hits with max?", "all hits with max?", "hits with max?", "with max?", "max?"], "tokens": ["How", "to", "retrieve", "all", "hits", "with", "max", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1826039130", "fields": {"term": "What query parameter can specify an implicit limit?", "namespace": "open-p", "hash": -1826039130, "terms": ["What query parameter can specify an implicit limit?", "query parameter can specify an implicit limit?", "parameter can specify an implicit limit?", "can specify an implicit limit?", "specify an implicit limit?", "an implicit limit?", "implicit limit?", "limit?"], "tokens": ["What", "query", "parameter", "can", "specify", "an", "implicit", "limit", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::353223839", "fields": {"term": "How to order hits inside group?", "namespace": "open-p", "hash": 353223839, "terms": ["How to order hits inside group?", "to order hits inside group?", "order hits inside group?", "hits inside group?", "inside group?", "group?"], "tokens": ["How", "to", "order", "hits", "inside", "group", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-555045417", "fields": {"term": "What is pricerank?", "namespace": "open-p", "hash": -555045417, "terms": ["What is pricerank?", "is pricerank?", "pricerank?"], "tokens": ["What", "is", "pricerank", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-944562666", "fields": {"term": "What is 'grouping.globalMaxGroups'?", "namespace": "open-p", "hash": -944562666, "terms": ["What is 'grouping.globalMaxGroups'?", "is 'grouping.globalMaxGroups'?", "'grouping.globalMaxGroups'?"], "tokens": ["What", "is", "'", "grouping", "globalMaxGroups", "'?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1206653895", "fields": {"term": "How does the query parameter restrict execution?", "namespace": "open-p", "hash": -1206653895, "terms": ["How does the query parameter restrict execution?", "does the query parameter restrict execution?", "the query parameter restrict execution?", "query parameter restrict execution?", "parameter restrict execution?", "restrict execution?", "execution?"], "tokens": ["How", "does", "the", "query", "parameter", "restrict", "execution", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1533601559", "fields": {"term": "What happens to queries exceeding the threshold?", "namespace": "open-p", "hash": -1533601559, "terms": ["What happens to queries exceeding the threshold?", "happens to queries exceeding the threshold?", "to queries exceeding the threshold?", "queries exceeding the threshold?", "exceeding the threshold?", "the threshold?", "threshold?"], "tokens": ["What", "happens", "to", "queries", "exceeding", "the", "threshold", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::323489124", "fields": {"term": "What is compared against the limit?", "namespace": "open-p", "hash": 323489124, "terms": ["What is compared against the limit?", "is compared against the limit?", "compared against the limit?", "against the limit?", "the limit?", "limit?"], "tokens": ["What", "is", "compared", "against", "the", "limit", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-669630264", "fields": {"term": "What is the purpose of 'grouping.globalMaxGroups'?", "namespace": "open-p", "hash": -669630264, "terms": ["What is the purpose of 'grouping.globalMaxGroups'?", "is the purpose of 'grouping.globalMaxGroups'?", "the purpose of 'grouping.globalMaxGroups'?", "purpose of 'grouping.globalMaxGroups'?", "of 'grouping.globalMaxGroups'?", "'grouping.globalMaxGroups'?"], "tokens": ["What", "is", "the", "purpose", "of", "'", "grouping", "globalMaxGroups", "'?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::807272049", "fields": {"term": "What is the purpose of globalMaxGroups?", "namespace": "open-p", "hash": 807272049, "terms": ["What is the purpose of globalMaxGroups?", "is the purpose of globalMaxGroups?", "the purpose of globalMaxGroups?", "purpose of globalMaxGroups?", "of globalMaxGroups?", "globalMaxGroups?"], "tokens": ["What", "is", "the", "purpose", "of", "globalMaxGroups", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2064036039", "fields": {"term": "What happens when globalMaxGroups is exceeded?", "namespace": "open-p", "hash": -2064036039, "terms": ["What happens when globalMaxGroups is exceeded?", "happens when globalMaxGroups is exceeded?", "when globalMaxGroups is exceeded?", "globalMaxGroups is exceeded?", "is exceeded?", "exceeded?"], "tokens": ["What", "happens", "when", "globalMaxGroups", "is", "exceeded", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1090189823", "fields": {"term": "What is the recommended 'max' in groupings?", "namespace": "open-p", "hash": 1090189823, "terms": ["What is the recommended 'max' in groupings?", "is the recommended 'max' in groupings?", "the recommended 'max' in groupings?", "recommended 'max' in groupings?", "'max' in groupings?", "in groupings?", "groupings?"], "tokens": ["What", "is", "the", "recommended", "'", "max", "'", "in", "groupings", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2087078150", "fields": {"term": "What is globalMaxGroups?", "namespace": "open-p", "hash": 2087078150, "terms": ["What is globalMaxGroups?", "is globalMaxGroups?", "globalMaxGroups?"], "tokens": ["What", "is", "globalMaxGroups", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1971489894", "fields": {"term": "What should be done if defaultMaxGroups is too low?", "namespace": "open-p", "hash": 1971489894, "terms": ["What should be done if defaultMaxGroups is too low?", "should be done if defaultMaxGroups is too low?", "be done if defaultMaxGroups is too low?", "done if defaultMaxGroups is too low?", "if defaultMaxGroups is too low?", "defaultMaxGroups is too low?", "is too low?", "too low?", "low?"], "tokens": ["What", "should", "be", "done", "if", "defaultMaxGroups", "is", "too", "low", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-347542697", "fields": {"term": "What is the default tuning for grouping?", "namespace": "open-p", "hash": -347542697, "terms": ["What is the default tuning for grouping?", "is the default tuning for grouping?", "the default tuning for grouping?", "default tuning for grouping?", "tuning for grouping?", "for grouping?", "grouping?"], "tokens": ["What", "is", "the", "default", "tuning", "for", "grouping", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1944218008", "fields": {"term": "What is the grouping session cache?", "namespace": "open-p", "hash": -1944218008, "terms": ["What is the grouping session cache?", "is the grouping session cache?", "the grouping session cache?", "grouping session cache?", "session cache?", "cache?"], "tokens": ["What", "is", "the", "grouping", "session", "cache", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1675482549", "fields": {"term": "How can correctness be improved for queries using order and max?", "namespace": "open-p", "hash": 1675482549, "terms": ["How can correctness be improved for queries using order and max?", "can correctness be improved for queries using order and max?", "correctness be improved for queries using order and max?", "be improved for queries using order and max?", "improved for queries using order and max?", "for queries using order and max?", "queries using order and max?", "using order and max?", "order and max?", "and max?", "max?"], "tokens": ["How", "can", "correctness", "be", "improved", "for", "queries", "using", "order", "and", "max", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1015429874", "fields": {"term": "What is the cost of multi-level grouping expressions?", "namespace": "open-p", "hash": -1015429874, "terms": ["What is the cost of multi-level grouping expressions?", "is the cost of multi-level grouping expressions?", "the cost of multi-level grouping expressions?", "cost of multi-level grouping expressions?", "of multi-level grouping expressions?", "multi-level grouping expressions?", "grouping expressions?", "expressions?"], "tokens": ["What", "is", "the", "cost", "of", "multi", "level", "grouping", "expressions", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::580862864", "fields": {"term": "How can precision be increased when using max and order?", "namespace": "open-p", "hash": 580862864, "terms": ["How can precision be increased when using max and order?", "can precision be increased when using max and order?", "precision be increased when using max and order?", "be increased when using max and order?", "increased when using max and order?", "when using max and order?", "using max and order?", "max and order?", "and order?", "order?"], "tokens": ["How", "can", "precision", "be", "increased", "when", "using", "max", "and", "order", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-562406061", "fields": {"term": "How is drilling accomplished?", "namespace": "open-p", "hash": -562406061, "terms": ["How is drilling accomplished?", "is drilling accomplished?", "drilling accomplished?", "accomplished?"], "tokens": ["How", "is", "drilling", "accomplished", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1811485284", "fields": {"term": "How do I group on price range using Vespa?", "namespace": "open-p", "hash": 1811485284, "terms": ["How do I group on price range using Vespa?", "do I group on price range using Vespa?", "I group on price range using Vespa?", "group on price range using Vespa?", "on price range using Vespa?", "price range using Vespa?", "range using Vespa?", "using Vespa?", "Vespa?"], "tokens": ["How", "do", "I", "group", "on", "price", "range", "using", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-969833166", "fields": {"term": "How do I configure group sizes individually?", "namespace": "open-p", "hash": -969833166, "terms": ["How do I configure group sizes individually?", "do I configure group sizes individually?", "I configure group sizes individually?", "configure group sizes individually?", "group sizes individually?", "sizes individually?", "individually?"], "tokens": ["How", "do", "I", "configure", "group", "sizes", "individually", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2002661857", "fields": {"term": "Can I use bucket expressions with strings?", "namespace": "open-p", "hash": -2002661857, "terms": ["Can I use bucket expressions with strings?", "I use bucket expressions with strings?", "use bucket expressions with strings?", "bucket expressions with strings?", "expressions with strings?", "with strings?", "strings?"], "tokens": ["Can", "I", "use", "bucket", "expressions", "with", "strings", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1859936193", "fields": {"term": "How do I use predefined to group with bucket expressions in Vespa?", "namespace": "open-p", "hash": -1859936193, "terms": ["How do I use predefined to group with bucket expressions in Vespa?", "do I use predefined to group with bucket expressions in Vespa?", "I use predefined to group with bucket expressions in Vespa?", "use predefined to group with bucket expressions in Vespa?", "predefined to group with bucket expressions in Vespa?", "to group with bucket expressions in Vespa?", "group with bucket expressions in Vespa?", "with bucket expressions in Vespa?", "bucket expressions in Vespa?", "expressions in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["How", "do", "I", "use", "predefined", "to", "group", "with", "bucket", "expressions", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::105304498", "fields": {"term": "How do I group on tax in Vespa?", "namespace": "open-p", "hash": 105304498, "terms": ["How do I group on tax in Vespa?", "do I group on tax in Vespa?", "I group on tax in Vespa?", "group on tax in Vespa?", "on tax in Vespa?", "tax in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["How", "do", "I", "group", "on", "tax", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1408175950", "fields": {"term": "What are continuation objects?", "namespace": "open-p", "hash": -1408175950, "terms": ["What are continuation objects?", "are continuation objects?", "continuation objects?", "objects?"], "tokens": ["What", "are", "continuation", "objects", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1774169545", "fields": {"term": "What is the 'continuations' annotation?", "namespace": "open-p", "hash": -1774169545, "terms": ["What is the 'continuations' annotation?", "is the 'continuations' annotation?", "the 'continuations' annotation?", "'continuations' annotation?", "annotation?"], "tokens": ["What", "is", "the", "'", "continuations", "'", "annotation", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1288730753", "fields": {"term": "How to reproduce the same result?", "namespace": "open-p", "hash": -1288730753, "terms": ["How to reproduce the same result?", "to reproduce the same result?", "reproduce the same result?", "the same result?", "same result?", "result?"], "tokens": ["How", "to", "reproduce", "the", "same", "result", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1481306841", "fields": {"term": "How to display the next page of customers?", "namespace": "open-p", "hash": 1481306841, "terms": ["How to display the next page of customers?", "to display the next page of customers?", "display the next page of customers?", "the next page of customers?", "next page of customers?", "page of customers?", "of customers?", "customers?"], "tokens": ["How", "to", "display", "the", "next", "page", "of", "customers", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::460221197", "fields": {"term": "How to display the previous page of customers?", "namespace": "open-p", "hash": 460221197, "terms": ["How to display the previous page of customers?", "to display the previous page of customers?", "display the previous page of customers?", "the previous page of customers?", "previous page of customers?", "page of customers?", "of customers?", "customers?"], "tokens": ["How", "to", "display", "the", "previous", "page", "of", "customers", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-610298490", "fields": {"term": "What expressions can be used in group clauses?", "namespace": "open-p", "hash": -610298490, "terms": ["What expressions can be used in group clauses?", "expressions can be used in group clauses?", "can be used in group clauses?", "be used in group clauses?", "used in group clauses?", "in group clauses?", "group clauses?", "clauses?"], "tokens": ["What", "expressions", "can", "be", "used", "in", "group", "clauses", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-419494099", "fields": {"term": "What is the format for using group clause in output operations?", "namespace": "open-p", "hash": -419494099, "terms": ["What is the format for using group clause in output operations?", "is the format for using group clause in output operations?", "the format for using group clause in output operations?", "format for using group clause in output operations?", "for using group clause in output operations?", "using group clause in output operations?", "group clause in output operations?", "clause in output operations?", "in output operations?", "output operations?", "operations?"], "tokens": ["What", "is", "the", "format", "for", "using", "group", "clause", "in", "output", "operations", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-19990370", "fields": {"term": "What is the rule for applying operators in an expression?", "namespace": "open-p", "hash": -19990370, "terms": ["What is the rule for applying operators in an expression?", "is the rule for applying operators in an expression?", "the rule for applying operators in an expression?", "rule for applying operators in an expression?", "for applying operators in an expression?", "applying operators in an expression?", "operators in an expression?", "in an expression?", "an expression?", "expression?"], "tokens": ["What", "is", "the", "rule", "for", "applying", "operators", "in", "an", "expression", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::612937052", "fields": {"term": "What is the difference between sum(price) and price when using the group clause?", "namespace": "open-p", "hash": 612937052, "terms": ["What is the difference between sum(price) and price when using the group clause?", "is the difference between sum(price) and price when using the group clause?", "the difference between sum(price) and price when using the group clause?", "difference between sum(price) and price when using the group clause?", "between sum(price) and price when using the group clause?", "sum(price) and price when using the group clause?", "and price when using the group clause?", "price when using the group clause?", "when using the group clause?", "using the group clause?", "the group clause?", "group clause?", "clause?"], "tokens": ["What", "is", "the", "difference", "between", "sum", "(", "price", ")", "and", "price", "when", "using", "the", "group", "clause", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1344592965", "fields": {"term": "What is arbitrary complex expressions in the context of grouping?", "namespace": "open-p", "hash": -1344592965, "terms": ["What is arbitrary complex expressions in the context of grouping?", "is arbitrary complex expressions in the context of grouping?", "arbitrary complex expressions in the context of grouping?", "complex expressions in the context of grouping?", "expressions in the context of grouping?", "in the context of grouping?", "the context of grouping?", "context of grouping?", "of grouping?", "grouping?"], "tokens": ["What", "is", "arbitrary", "complex", "expressions", "in", "the", "context", "of", "grouping", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1464313706", "fields": {"term": "What is the programmatic API?", "namespace": "open-p", "hash": 1464313706, "terms": ["What is the programmatic API?", "is the programmatic API?", "the programmatic API?", "programmatic API?", "API?"], "tokens": ["What", "is", "the", "programmatic", "API", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2072231184", "fields": {"term": "What is a grouping request?", "namespace": "open-p", "hash": -2072231184, "terms": ["What is a grouping request?", "is a grouping request?", "a grouping request?", "grouping request?", "request?"], "tokens": ["What", "is", "a", "grouping", "request", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1187314466", "fields": {"term": "How does the API parallelize grouping?", "namespace": "open-p", "hash": -1187314466, "terms": ["How does the API parallelize grouping?", "does the API parallelize grouping?", "the API parallelize grouping?", "API parallelize grouping?", "parallelize grouping?", "grouping?"], "tokens": ["How", "does", "the", "API", "parallelize", "grouping", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-130398680", "fields": {"term": "What is the purpose of AllOperation?", "namespace": "open-p", "hash": -130398680, "terms": ["What is the purpose of AllOperation?", "is the purpose of AllOperation?", "the purpose of AllOperation?", "purpose of AllOperation?", "of AllOperation?", "AllOperation?"], "tokens": ["What", "is", "the", "purpose", "of", "AllOperation", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1908593105", "fields": {"term": "What is CountAggregator?", "namespace": "open-p", "hash": -1908593105, "terms": ["What is CountAggregator?", "is CountAggregator?", "CountAggregator?"], "tokens": ["What", "is", "CountAggregator", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::305021168", "fields": {"term": "What is the API documentation?", "namespace": "open-p", "hash": 305021168, "terms": ["What is the API documentation?", "is the API documentation?", "the API documentation?", "API documentation?", "documentation?"], "tokens": ["What", "is", "the", "API", "documentation", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1586027255", "fields": {"term": "Where can the API documentation be found?", "namespace": "open-p", "hash": 1586027255, "terms": ["Where can the API documentation be found?", "can the API documentation be found?", "the API documentation be found?", "API documentation be found?", "documentation be found?", "be found?", "found?"], "tokens": ["Where", "can", "the", "API", "documentation", "be", "found", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-783629263", "fields": {"term": "What is simple grouping?", "namespace": "open-p", "hash": -783629263, "terms": ["What is simple grouping?", "is simple grouping?", "simple grouping?", "grouping?"], "tokens": ["What", "is", "simple", "grouping", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::1353958559", "fields": {"term": "How to count documents using grouping?", "namespace": "open-p", "hash": 1353958559, "terms": ["How to count documents using grouping?", "to count documents using grouping?", "count documents using grouping?", "documents using grouping?", "using grouping?", "grouping?"], "tokens": ["How", "to", "count", "documents", "using", "grouping", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2037611128", "fields": {"term": "What is the syntax for parallel grouping?", "namespace": "open-p", "hash": 2037611128, "terms": ["What is the syntax for parallel grouping?", "is the syntax for parallel grouping?", "the syntax for parallel grouping?", "syntax for parallel grouping?", "for parallel grouping?", "parallel grouping?", "grouping?"], "tokens": ["What", "is", "the", "syntax", "for", "parallel", "grouping", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-104106608", "fields": {"term": "What is the advantage of lower accuracy?", "namespace": "open-p", "hash": -104106608, "terms": ["What is the advantage of lower accuracy?", "is the advantage of lower accuracy?", "the advantage of lower accuracy?", "advantage of lower accuracy?", "of lower accuracy?", "lower accuracy?", "accuracy?"], "tokens": ["What", "is", "the", "advantage", "of", "lower", "accuracy", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1175038651", "fields": {"term": "What does max(1000) do?", "namespace": "open-p", "hash": 1175038651, "terms": ["What does max(1000) do?", "does max(1000) do?", "max(1000) do?", "do?"], "tokens": ["What", "does", "max", "(", "1000", ")", "do", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-584523027", "fields": {"term": "How do you select a group in Vespa", "namespace": "open-p", "hash": -584523027, "terms": ["How do you select a group in Vespa", "do you select a group in Vespa", "you select a group in Vespa", "select a group in Vespa", "a group in Vespa", "group in Vespa", "in Vespa", "Vespa"], "tokens": ["How", "do", "you", "select", "a", "group", "in", "Vespa"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-226500442", "fields": {"term": "What is the modulo 5 operation in Vespa", "namespace": "open-p", "hash": -226500442, "terms": ["What is the modulo 5 operation in Vespa", "is the modulo 5 operation in Vespa", "the modulo 5 operation in Vespa", "modulo 5 operation in Vespa", "5 operation in Vespa", "operation in Vespa", "in Vespa", "Vespa"], "tokens": ["What", "is", "the", "modulo", "5", "operation", "in", "Vespa"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-629875784", "fields": {"term": "How do you output a count in Vespa", "namespace": "open-p", "hash": -629875784, "terms": ["How do you output a count in Vespa", "do you output a count in Vespa", "you output a count in Vespa", "output a count in Vespa", "a count in Vespa", "count in Vespa", "in Vespa", "Vespa"], "tokens": ["How", "do", "you", "output", "a", "count", "in", "Vespa"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1240948880", "fields": {"term": "What does 'group' do in Vespa", "namespace": "open-p", "hash": 1240948880, "terms": ["What does 'group' do in Vespa", "does 'group' do in Vespa", "'group' do in Vespa", "do in Vespa", "in Vespa", "Vespa"], "tokens": ["What", "does", "'", "group", "'", "do", "in", "Vespa"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::663840882", "fields": {"term": "What is the formula for selecting a group in Vespa", "namespace": "open-p", "hash": 663840882, "terms": ["What is the formula for selecting a group in Vespa", "is the formula for selecting a group in Vespa", "the formula for selecting a group in Vespa", "formula for selecting a group in Vespa", "for selecting a group in Vespa", "selecting a group in Vespa", "a group in Vespa", "group in Vespa", "in Vespa", "Vespa"], "tokens": ["What", "is", "the", "formula", "for", "selecting", "a", "group", "in", "Vespa"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1106294249", "fields": {"term": "What does the modulo operation do?", "namespace": "open-p", "hash": -1106294249, "terms": ["What does the modulo operation do?", "does the modulo operation do?", "the modulo operation do?", "modulo operation do?", "operation do?", "do?"], "tokens": ["What", "does", "the", "modulo", "operation", "do", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::101560926", "fields": {"term": "How is group ordering determined?", "namespace": "open-p", "hash": 101560926, "terms": ["How is group ordering determined?", "is group ordering determined?", "group ordering determined?", "ordering determined?", "determined?"], "tokens": ["How", "is", "group", "ordering", "determined", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::659921314", "fields": {"term": "Can attributes be referenced in the order clause?", "namespace": "open-p", "hash": 659921314, "terms": ["Can attributes be referenced in the order clause?", "attributes be referenced in the order clause?", "be referenced in the order clause?", "referenced in the order clause?", "in the order clause?", "the order clause?", "order clause?", "clause?"], "tokens": ["Can", "attributes", "be", "referenced", "in", "the", "order", "clause", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1850435004", "fields": {"term": "What is the average relevance of groups?", "namespace": "open-p", "hash": -1850435004, "terms": ["What is the average relevance of groups?", "is the average relevance of groups?", "the average relevance of groups?", "average relevance of groups?", "relevance of groups?", "of groups?", "groups?"], "tokens": ["What", "is", "the", "average", "relevance", "of", "groups", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2115007050", "fields": {"term": "How to calculate cumulative group count?", "namespace": "open-p", "hash": 2115007050, "terms": ["How to calculate cumulative group count?", "to calculate cumulative group count?", "calculate cumulative group count?", "cumulative group count?", "group count?", "count?"], "tokens": ["How", "to", "calculate", "cumulative", "group", "count", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::359436457", "fields": {"term": "What is count and max?", "namespace": "open-p", "hash": 359436457, "terms": ["What is count and max?", "is count and max?", "count and max?", "and max?", "max?"], "tokens": ["What", "is", "count", "and", "max", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1593645501", "fields": {"term": "What is XOR and MD5?", "namespace": "open-p", "hash": -1593645501, "terms": ["What is XOR and MD5?", "is XOR and MD5?", "XOR and MD5?", "and MD5?", "MD5?"], "tokens": ["What", "is", "XOR", "and", "MD5", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::10312190", "fields": {"term": "What is single level grouping?", "namespace": "open-p", "hash": 10312190, "terms": ["What is single level grouping?", "is single level grouping?", "single level grouping?", "level grouping?", "grouping?"], "tokens": ["What", "is", "single", "level", "grouping", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-464973573", "fields": {"term": "What is two level grouping?", "namespace": "open-p", "hash": -464973573, "terms": ["What is two level grouping?", "is two level grouping?", "two level grouping?", "level grouping?", "grouping?"], "tokens": ["What", "is", "two", "level", "grouping", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-596811421", "fields": {"term": "What is three level grouping?", "namespace": "open-p", "hash": -596811421, "terms": ["What is three level grouping?", "is three level grouping?", "three level grouping?", "level grouping?", "grouping?"], "tokens": ["What", "is", "three", "level", "grouping", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1323736728", "fields": {"term": "What is deep grouping?", "namespace": "open-p", "hash": 1323736728, "terms": ["What is deep grouping?", "is deep grouping?", "deep grouping?", "grouping?"], "tokens": ["What", "is", "deep", "grouping", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1832930616", "fields": {"term": "What are time-functions?", "namespace": "open-p", "hash": 1832930616, "terms": ["What are time-functions?", "are time-functions?", "time-functions?"], "tokens": ["What", "are", "time", "functions", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1891175371", "fields": {"term": "How to group by year/month/day/hour?", "namespace": "open-p", "hash": -1891175371, "terms": ["How to group by year/month/day/hour?", "to group by year/month/day/hour?", "group by year/month/day/hour?", "by year/month/day/hour?", "year/month/day/hour?"], "tokens": ["How", "to", "group", "by", "year", "/", "month", "/", "day", "/", "hour", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1489841262", "fields": {"term": "What is the predefined aggregator?", "namespace": "open-p", "hash": 1489841262, "terms": ["What is the predefined aggregator?", "is the predefined aggregator?", "the predefined aggregator?", "predefined aggregator?", "aggregator?"], "tokens": ["What", "is", "the", "predefined", "aggregator", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-865263879", "fields": {"term": "How to group today/yesterday/lastweek/lastmonth?", "namespace": "open-p", "hash": -865263879, "terms": ["How to group today/yesterday/lastweek/lastmonth?", "to group today/yesterday/lastweek/lastmonth?", "group today/yesterday/lastweek/lastmonth?", "today/yesterday/lastweek/lastmonth?"], "tokens": ["How", "to", "group", "today", "/", "yesterday", "/", "lastweek", "/", "lastmonth", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-286983066", "fields": {"term": "What is the `count` aggregator used for?", "namespace": "open-p", "hash": -286983066, "terms": ["What is the `count` aggregator used for?", "is the `count` aggregator used for?", "the `count` aggregator used for?", "`count` aggregator used for?", "aggregator used for?", "used for?", "for?"], "tokens": ["What", "is", "the", "`", "count", "`", "aggregator", "used", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::81519514", "fields": {"term": "How does HyperLogLog++ affect the count?", "namespace": "open-p", "hash": 81519514, "terms": ["How does HyperLogLog++ affect the count?", "does HyperLogLog++ affect the count?", "HyperLogLog++ affect the count?", "affect the count?", "the count?", "count?"], "tokens": ["How", "does", "HyperLogLog", "++", "affect", "the", "count", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1209288497", "fields": {"term": "How can the `max` clause limit the number of groups returned?", "namespace": "open-p", "hash": -1209288497, "terms": ["How can the `max` clause limit the number of groups returned?", "can the `max` clause limit the number of groups returned?", "the `max` clause limit the number of groups returned?", "`max` clause limit the number of groups returned?", "clause limit the number of groups returned?", "limit the number of groups returned?", "the number of groups returned?", "number of groups returned?", "of groups returned?", "groups returned?", "returned?"], "tokens": ["How", "can", "the", "`", "max", "`", "clause", "limit", "the", "number", "of", "groups", "returned", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1593796800", "fields": {"term": "What is the inner each clause for?", "namespace": "open-p", "hash": -1593796800, "terms": ["What is the inner each clause for?", "is the inner each clause for?", "the inner each clause for?", "inner each clause for?", "each clause for?", "clause for?", "for?"], "tokens": ["What", "is", "the", "inner", "each", "clause", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1526454369", "fields": {"term": "How to output an estimate of the number of groups for an attribute?", "namespace": "open-p", "hash": -1526454369, "terms": ["How to output an estimate of the number of groups for an attribute?", "to output an estimate of the number of groups for an attribute?", "output an estimate of the number of groups for an attribute?", "an estimate of the number of groups for an attribute?", "estimate of the number of groups for an attribute?", "of the number of groups for an attribute?", "the number of groups for an attribute?", "number of groups for an attribute?", "of groups for an attribute?", "groups for an attribute?", "for an attribute?", "an attribute?", "attribute?"], "tokens": ["How", "to", "output", "an", "estimate", "of", "the", "number", "of", "groups", "for", "an", "attribute", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::120788596", "fields": {"term": "What is the count-distinct problem?", "namespace": "open-p", "hash": 120788596, "terms": ["What is the count-distinct problem?", "is the count-distinct problem?", "the count-distinct problem?", "count-distinct problem?", "problem?"], "tokens": ["What", "is", "the", "count", "distinct", "problem", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1177596053", "fields": {"term": "Is the `count` aggregator an accurate count of all groups?", "namespace": "open-p", "hash": 1177596053, "terms": ["Is the `count` aggregator an accurate count of all groups?", "the `count` aggregator an accurate count of all groups?", "`count` aggregator an accurate count of all groups?", "aggregator an accurate count of all groups?", "an accurate count of all groups?", "accurate count of all groups?", "count of all groups?", "of all groups?", "all groups?", "groups?"], "tokens": ["Is", "the", "`", "count", "`", "aggregator", "an", "accurate", "count", "of", "all", "groups", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1710165557", "fields": {"term": "What are the use cases for the count aggregator?", "namespace": "open-p", "hash": -1710165557, "terms": ["What are the use cases for the count aggregator?", "are the use cases for the count aggregator?", "the use cases for the count aggregator?", "use cases for the count aggregator?", "cases for the count aggregator?", "for the count aggregator?", "the count aggregator?", "count aggregator?", "aggregator?"], "tokens": ["What", "are", "the", "use", "cases", "for", "the", "count", "aggregator", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::744405311", "fields": {"term": "What is impression logs?", "namespace": "open-p", "hash": 744405311, "terms": ["What is impression logs?", "is impression logs?", "impression logs?", "logs?"], "tokens": ["What", "is", "impression", "logs", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::226736257", "fields": {"term": "How is rank score mapped?", "namespace": "open-p", "hash": 226736257, "terms": ["How is rank score mapped?", "is rank score mapped?", "rank score mapped?", "score mapped?", "mapped?"], "tokens": ["How", "is", "rank", "score", "mapped", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::884168538", "fields": {"term": "What is the function to map rank score to the number of impressions?", "namespace": "open-p", "hash": 884168538, "terms": ["What is the function to map rank score to the number of impressions?", "is the function to map rank score to the number of impressions?", "the function to map rank score to the number of impressions?", "function to map rank score to the number of impressions?", "to map rank score to the number of impressions?", "map rank score to the number of impressions?", "rank score to the number of impressions?", "score to the number of impressions?", "to the number of impressions?", "the number of impressions?", "number of impressions?", "of impressions?", "impressions?"], "tokens": ["What", "is", "the", "function", "to", "map", "rank", "score", "to", "the", "number", "of", "impressions", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-235286713", "fields": {"term": "What is interpolatedlookup used for?", "namespace": "open-p", "hash": -235286713, "terms": ["What is interpolatedlookup used for?", "is interpolatedlookup used for?", "interpolatedlookup used for?", "used for?", "for?"], "tokens": ["What", "is", "interpolatedlookup", "used", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::587344807", "fields": {"term": "How is the interpolatedlookup used?", "namespace": "open-p", "hash": 587344807, "terms": ["How is the interpolatedlookup used?", "is the interpolatedlookup used?", "the interpolatedlookup used?", "interpolatedlookup used?", "used?"], "tokens": ["How", "is", "the", "interpolatedlookup", "used", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-93825913", "fields": {"term": "What is the result of the interpolatedlookup?", "namespace": "open-p", "hash": -93825913, "terms": ["What is the result of the interpolatedlookup?", "is the result of the interpolatedlookup?", "the result of the interpolatedlookup?", "result of the interpolatedlookup?", "of the interpolatedlookup?", "the interpolatedlookup?", "interpolatedlookup?"], "tokens": ["What", "is", "the", "result", "of", "the", "interpolatedlookup", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1768904920", "fields": {"term": "How can the interpolatedlookup be used for forecasting?", "namespace": "open-p", "hash": 1768904920, "terms": ["How can the interpolatedlookup be used for forecasting?", "can the interpolatedlookup be used for forecasting?", "the interpolatedlookup be used for forecasting?", "interpolatedlookup be used for forecasting?", "be used for forecasting?", "used for forecasting?", "for forecasting?", "forecasting?"], "tokens": ["How", "can", "the", "interpolatedlookup", "be", "used", "for", "forecasting", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1000083204", "fields": {"term": "What is VipStatusHandler?", "namespace": "open-p", "hash": -1000083204, "terms": ["What is VipStatusHandler?", "is VipStatusHandler?", "VipStatusHandler?"], "tokens": ["What", "is", "VipStatusHandler", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::614435761", "fields": {"term": "How does VipStatusHandler work?", "namespace": "open-p", "hash": 614435761, "terms": ["How does VipStatusHandler work?", "does VipStatusHandler work?", "VipStatusHandler work?", "work?"], "tokens": ["How", "does", "VipStatusHandler", "work", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-446191902", "fields": {"term": "What is the default healthcheck path?", "namespace": "open-p", "hash": -446191902, "terms": ["What is the default healthcheck path?", "is the default healthcheck path?", "the default healthcheck path?", "default healthcheck path?", "healthcheck path?", "path?"], "tokens": ["What", "is", "the", "default", "healthcheck", "path", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-878353173", "fields": {"term": "What does the healthcheck respond with?", "namespace": "open-p", "hash": -878353173, "terms": ["What does the healthcheck respond with?", "does the healthcheck respond with?", "the healthcheck respond with?", "healthcheck respond with?", "respond with?", "with?"], "tokens": ["What", "does", "the", "healthcheck", "respond", "with", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1839621473", "fields": {"term": "How do you manage data sources?", "namespace": "open-p", "hash": -1839621473, "terms": ["How do you manage data sources?", "do you manage data sources?", "you manage data sources?", "manage data sources?", "data sources?", "sources?"], "tokens": ["How", "do", "you", "manage", "data", "sources", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1002731759", "fields": {"term": "What is `container.core.vip-status`?", "namespace": "open-p", "hash": 1002731759, "terms": ["What is `container.core.vip-status`?", "is `container.core.vip-status`?", "`container.core.vip-status`?"], "tokens": ["What", "is", "`", "container", "core", "vip", "status", "`?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1668195207", "fields": {"term": "How to make `VipStatusHandler` use a file?", "namespace": "open-p", "hash": -1668195207, "terms": ["How to make `VipStatusHandler` use a file?", "to make `VipStatusHandler` use a file?", "make `VipStatusHandler` use a file?", "`VipStatusHandler` use a file?", "use a file?", "a file?", "file?"], "tokens": ["How", "to", "make", "`", "VipStatusHandler", "`", "use", "a", "file", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1144973605", "fields": {"term": "What is served on `/status.html`?", "namespace": "open-p", "hash": -1144973605, "terms": ["What is served on `/status.html`?", "is served on `/status.html`?", "served on `/status.html`?", "on `/status.html`?", "`/status.html`?"], "tokens": ["What", "is", "served", "on", "`/", "status", "html", "`?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1924257572", "fields": {"term": "What happens if the file doesn't exist?", "namespace": "open-p", "hash": 1924257572, "terms": ["What happens if the file doesn't exist?", "happens if the file doesn't exist?", "if the file doesn't exist?", "the file doesn't exist?", "file doesn't exist?", "doesn't exist?", "exist?"], "tokens": ["What", "happens", "if", "the", "file", "doesn", "'", "t", "exist", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::497334259", "fields": {"term": "How to configure multiple handler instances?", "namespace": "open-p", "hash": 497334259, "terms": ["How to configure multiple handler instances?", "to configure multiple handler instances?", "configure multiple handler instances?", "multiple handler instances?", "handler instances?", "instances?"], "tokens": ["How", "to", "configure", "multiple", "handler", "instances", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-413895553", "fields": {"term": "What is hosts.xml?", "namespace": "open-p", "hash": -413895553, "terms": ["What is hosts.xml?", "is hosts.xml?", "hosts.xml?"], "tokens": ["What", "is", "hosts", "xml", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2116790458", "fields": {"term": "What elements are in hosts.xml?", "namespace": "open-p", "hash": -2116790458, "terms": ["What elements are in hosts.xml?", "elements are in hosts.xml?", "are in hosts.xml?", "in hosts.xml?", "hosts.xml?"], "tokens": ["What", "elements", "are", "in", "hosts", "xml", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2064841798", "fields": {"term": "What is a host in hosts.xml?", "namespace": "open-p", "hash": -2064841798, "terms": ["What is a host in hosts.xml?", "is a host in hosts.xml?", "a host in hosts.xml?", "host in hosts.xml?", "in hosts.xml?", "hosts.xml?"], "tokens": ["What", "is", "a", "host", "in", "hosts", "xml", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1832479721", "fields": {"term": "What is an alias in hosts.xml?", "namespace": "open-p", "hash": -1832479721, "terms": ["What is an alias in hosts.xml?", "is an alias in hosts.xml?", "an alias in hosts.xml?", "alias in hosts.xml?", "in hosts.xml?", "hosts.xml?"], "tokens": ["What", "is", "an", "alias", "in", "hosts", "xml", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1788120397", "fields": {"term": "What is the relationship between hosts.xml and services.xml?", "namespace": "open-p", "hash": -1788120397, "terms": ["What is the relationship between hosts.xml and services.xml?", "is the relationship between hosts.xml and services.xml?", "the relationship between hosts.xml and services.xml?", "relationship between hosts.xml and services.xml?", "between hosts.xml and services.xml?", "hosts.xml and services.xml?", "and services.xml?", "services.xml?"], "tokens": ["What", "is", "the", "relationship", "between", "hosts", "xml", "and", "services", "xml", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-403795614", "fields": {"term": "When is hosts.xml needed?", "namespace": "open-p", "hash": -403795614, "terms": ["When is hosts.xml needed?", "is hosts.xml needed?", "hosts.xml needed?", "needed?"], "tokens": ["When", "is", "hosts", "xml", "needed", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::195735708", "fields": {"term": "What does hosts.xml do?", "namespace": "open-p", "hash": 195735708, "terms": ["What does hosts.xml do?", "does hosts.xml do?", "hosts.xml do?", "do?"], "tokens": ["What", "does", "hosts", "xml", "do", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2108339579", "fields": {"term": "How to use alias element?", "namespace": "open-p", "hash": -2108339579, "terms": ["How to use alias element?", "to use alias element?", "use alias element?", "alias element?", "element?"], "tokens": ["How", "to", "use", "alias", "element", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1683989838", "fields": {"term": "When is a custom handler required?", "namespace": "open-p", "hash": 1683989838, "terms": ["When is a custom handler required?", "is a custom handler required?", "a custom handler required?", "custom handler required?", "handler required?", "required?"], "tokens": ["When", "is", "a", "custom", "handler", "required", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1865598158", "fields": {"term": "What does a custom handler add?", "namespace": "open-p", "hash": -1865598158, "terms": ["What does a custom handler add?", "does a custom handler add?", "a custom handler add?", "custom handler add?", "handler add?", "add?"], "tokens": ["What", "does", "a", "custom", "handler", "add", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1682615545", "fields": {"term": "How does a custom handler route parameters?", "namespace": "open-p", "hash": 1682615545, "terms": ["How does a custom handler route parameters?", "does a custom handler route parameters?", "a custom handler route parameters?", "custom handler route parameters?", "handler route parameters?", "route parameters?", "parameters?"], "tokens": ["How", "does", "a", "custom", "handler", "route", "parameters", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1290176717", "fields": {"term": "What is DemoHandler.java?", "namespace": "open-p", "hash": -1290176717, "terms": ["What is DemoHandler.java?", "is DemoHandler.java?", "DemoHandler.java?"], "tokens": ["What", "is", "DemoHandler", "java", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::510542329", "fields": {"term": "How can a custom handler be implemented?", "namespace": "open-p", "hash": 510542329, "terms": ["How can a custom handler be implemented?", "can a custom handler be implemented?", "a custom handler be implemented?", "custom handler be implemented?", "handler be implemented?", "be implemented?", "implemented?"], "tokens": ["How", "can", "a", "custom", "handler", "be", "implemented", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::466252448", "fields": {"term": "What is the built-in processing handler?", "namespace": "open-p", "hash": 466252448, "terms": ["What is the built-in processing handler?", "is the built-in processing handler?", "the built-in processing handler?", "built-in processing handler?", "processing handler?", "handler?"], "tokens": ["What", "is", "the", "built", "in", "processing", "handler", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::688125955", "fields": {"term": "What is the shared component used for?", "namespace": "open-p", "hash": 688125955, "terms": ["What is the shared component used for?", "is the shared component used for?", "the shared component used for?", "shared component used for?", "component used for?", "used for?", "for?"], "tokens": ["What", "is", "the", "shared", "component", "used", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1607728941", "fields": {"term": "What happens to the incoming request?", "namespace": "open-p", "hash": 1607728941, "terms": ["What happens to the incoming request?", "happens to the incoming request?", "to the incoming request?", "the incoming request?", "incoming request?", "request?"], "tokens": ["What", "happens", "to", "the", "incoming", "request", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::932003248", "fields": {"term": "What is checked in the incoming request?", "namespace": "open-p", "hash": 932003248, "terms": ["What is checked in the incoming request?", "is checked in the incoming request?", "checked in the incoming request?", "in the incoming request?", "the incoming request?", "incoming request?", "request?"], "tokens": ["What", "is", "checked", "in", "the", "incoming", "request", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1431998914", "fields": {"term": "What is AnnotatingProcessor?", "namespace": "open-p", "hash": 1431998914, "terms": ["What is AnnotatingProcessor?", "is AnnotatingProcessor?", "AnnotatingProcessor?"], "tokens": ["What", "is", "AnnotatingProcessor", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1141573533", "fields": {"term": "Where is AnnotatingProcessor located?", "namespace": "open-p", "hash": -1141573533, "terms": ["Where is AnnotatingProcessor located?", "is AnnotatingProcessor located?", "AnnotatingProcessor located?", "located?"], "tokens": ["Where", "is", "AnnotatingProcessor", "located", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-827998803", "fields": {"term": "What does AnnotatingProcessor do?", "namespace": "open-p", "hash": -827998803, "terms": ["What does AnnotatingProcessor do?", "does AnnotatingProcessor do?", "AnnotatingProcessor do?", "do?"], "tokens": ["What", "does", "AnnotatingProcessor", "do", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2023484264", "fields": {"term": "What is the file extension of AnnotatingProcessor?", "namespace": "open-p", "hash": -2023484264, "terms": ["What is the file extension of AnnotatingProcessor?", "is the file extension of AnnotatingProcessor?", "the file extension of AnnotatingProcessor?", "file extension of AnnotatingProcessor?", "extension of AnnotatingProcessor?", "of AnnotatingProcessor?", "AnnotatingProcessor?"], "tokens": ["What", "is", "the", "file", "extension", "of", "AnnotatingProcessor", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1787878203", "fields": {"term": "What programming language is used in AnnotatingProcessor?", "namespace": "open-p", "hash": 1787878203, "terms": ["What programming language is used in AnnotatingProcessor?", "programming language is used in AnnotatingProcessor?", "language is used in AnnotatingProcessor?", "is used in AnnotatingProcessor?", "used in AnnotatingProcessor?", "in AnnotatingProcessor?", "AnnotatingProcessor?"], "tokens": ["What", "programming", "language", "is", "used", "in", "AnnotatingProcessor", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::736714456", "fields": {"term": "Which repository contains AnnotatingProcessor?", "namespace": "open-p", "hash": 736714456, "terms": ["Which repository contains AnnotatingProcessor?", "repository contains AnnotatingProcessor?", "contains AnnotatingProcessor?", "AnnotatingProcessor?"], "tokens": ["Which", "repository", "contains", "AnnotatingProcessor", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-178630087", "fields": {"term": "What is the purpose of AnnotatingProcessor?", "namespace": "open-p", "hash": -178630087, "terms": ["What is the purpose of AnnotatingProcessor?", "is the purpose of AnnotatingProcessor?", "the purpose of AnnotatingProcessor?", "purpose of AnnotatingProcessor?", "of AnnotatingProcessor?", "AnnotatingProcessor?"], "tokens": ["What", "is", "the", "purpose", "of", "AnnotatingProcessor", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::995649161", "fields": {"term": "How can AnnotatingProcessor be used in an application?", "namespace": "open-p", "hash": 995649161, "terms": ["How can AnnotatingProcessor be used in an application?", "can AnnotatingProcessor be used in an application?", "AnnotatingProcessor be used in an application?", "be used in an application?", "used in an application?", "in an application?", "an application?", "application?"], "tokens": ["How", "can", "AnnotatingProcessor", "be", "used", "in", "an", "application", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::421480676", "fields": {"term": "What is DataProcessor.java?", "namespace": "open-p", "hash": 421480676, "terms": ["What is DataProcessor.java?", "is DataProcessor.java?", "DataProcessor.java?"], "tokens": ["What", "is", "DataProcessor", "java", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::321238166", "fields": {"term": "How are processors composed?", "namespace": "open-p", "hash": 321238166, "terms": ["How are processors composed?", "are processors composed?", "processors composed?", "composed?"], "tokens": ["How", "are", "processors", "composed", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1331551186", "fields": {"term": "What are Processing steps?", "namespace": "open-p", "hash": -1331551186, "terms": ["What are Processing steps?", "are Processing steps?", "Processing steps?", "steps?"], "tokens": ["What", "are", "Processing", "steps", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::264370117", "fields": {"term": "What do processors communicate through?", "namespace": "open-p", "hash": 264370117, "terms": ["What do processors communicate through?", "do processors communicate through?", "processors communicate through?", "communicate through?", "through?"], "tokens": ["What", "do", "processors", "communicate", "through", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-527950765", "fields": {"term": "What are the benefits of Processor structure?", "namespace": "open-p", "hash": -527950765, "terms": ["What are the benefits of Processor structure?", "are the benefits of Processor structure?", "the benefits of Processor structure?", "benefits of Processor structure?", "of Processor structure?", "Processor structure?", "structure?"], "tokens": ["What", "are", "the", "benefits", "of", "Processor", "structure", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-630541150", "fields": {"term": "What is the AnnotatingProcessor?", "namespace": "open-p", "hash": -630541150, "terms": ["What is the AnnotatingProcessor?", "is the AnnotatingProcessor?", "the AnnotatingProcessor?", "AnnotatingProcessor?"], "tokens": ["What", "is", "the", "AnnotatingProcessor", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-346487500", "fields": {"term": "What is in the demo.def file?", "namespace": "open-p", "hash": -346487500, "terms": ["What is in the demo.def file?", "is in the demo.def file?", "in the demo.def file?", "the demo.def file?", "demo.def file?", "file?"], "tokens": ["What", "is", "in", "the", "demo", "def", "file", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1697773451", "fields": {"term": "What is the responsibility of the renderer?", "namespace": "open-p", "hash": 1697773451, "terms": ["What is the responsibility of the renderer?", "is the responsibility of the renderer?", "the responsibility of the renderer?", "responsibility of the renderer?", "of the renderer?", "the renderer?", "renderer?"], "tokens": ["What", "is", "the", "responsibility", "of", "the", "renderer", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2082467118", "fields": {"term": "What is the purpose of init()?", "namespace": "open-p", "hash": 2082467118, "terms": ["What is the purpose of init()?", "is the purpose of init()?", "the purpose of init()?", "purpose of init()?", "of init()?", "init()?"], "tokens": ["What", "is", "the", "purpose", "of", "init", "()?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1538097558", "fields": {"term": "What is DemoRenderer.java?", "namespace": "open-p", "hash": -1538097558, "terms": ["What is DemoRenderer.java?", "is DemoRenderer.java?", "DemoRenderer.java?"], "tokens": ["What", "is", "DemoRenderer", "java", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1851104771", "fields": {"term": "How many times is init() invoked per clone?", "namespace": "open-p", "hash": 1851104771, "terms": ["How many times is init() invoked per clone?", "many times is init() invoked per clone?", "times is init() invoked per clone?", "is init() invoked per clone?", "init() invoked per clone?", "invoked per clone?", "per clone?", "clone?"], "tokens": ["How", "many", "times", "is", "init", "()", "invoked", "per", "clone", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-938140291", "fields": {"term": "What happens before render() is invoked?", "namespace": "open-p", "hash": -938140291, "terms": ["What happens before render() is invoked?", "happens before render() is invoked?", "before render() is invoked?", "render() is invoked?", "is invoked?", "invoked?"], "tokens": ["What", "happens", "before", "render", "()", "is", "invoked", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1938532826", "fields": {"term": "What does the renderer do?", "namespace": "open-p", "hash": -1938532826, "terms": ["What does the renderer do?", "does the renderer do?", "the renderer do?", "renderer do?", "do?"], "tokens": ["What", "does", "the", "renderer", "do", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-952516970", "fields": {"term": "What is serialized by the renderer?", "namespace": "open-p", "hash": -952516970, "terms": ["What is serialized by the renderer?", "is serialized by the renderer?", "serialized by the renderer?", "by the renderer?", "the renderer?", "renderer?"], "tokens": ["What", "is", "serialized", "by", "the", "renderer", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::513944088", "fields": {"term": "What does the custom component do?", "namespace": "open-p", "hash": 513944088, "terms": ["What does the custom component do?", "does the custom component do?", "the custom component do?", "custom component do?", "component do?", "do?"], "tokens": ["What", "does", "the", "custom", "component", "do", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1167392402", "fields": {"term": "Why is the custom component important?", "namespace": "open-p", "hash": -1167392402, "terms": ["Why is the custom component important?", "is the custom component important?", "the custom component important?", "custom component important?", "component important?", "important?"], "tokens": ["Why", "is", "the", "custom", "component", "important", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1361278712", "fields": {"term": "How does the custom component work?", "namespace": "open-p", "hash": -1361278712, "terms": ["How does the custom component work?", "does the custom component work?", "the custom component work?", "custom component work?", "component work?", "work?"], "tokens": ["How", "does", "the", "custom", "component", "work", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1836971314", "fields": {"term": "Where can I find the code for the custom component?", "namespace": "open-p", "hash": -1836971314, "terms": ["Where can I find the code for the custom component?", "can I find the code for the custom component?", "I find the code for the custom component?", "find the code for the custom component?", "the code for the custom component?", "code for the custom component?", "for the custom component?", "the custom component?", "custom component?", "component?"], "tokens": ["Where", "can", "I", "find", "the", "code", "for", "the", "custom", "component", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1683457364", "fields": {"term": "What is reviewed in services.xml?", "namespace": "open-p", "hash": 1683457364, "terms": ["What is reviewed in services.xml?", "is reviewed in services.xml?", "reviewed in services.xml?", "in services.xml?", "services.xml?"], "tokens": ["What", "is", "reviewed", "in", "services", "xml", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-607980539", "fields": {"term": "How do I set up http servers?", "namespace": "open-p", "hash": -607980539, "terms": ["How do I set up http servers?", "do I set up http servers?", "I set up http servers?", "set up http servers?", "up http servers?", "http servers?", "servers?"], "tokens": ["How", "do", "I", "set", "up", "http", "servers", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-257360004", "fields": {"term": "Where can I find the Developer Guide?", "namespace": "open-p", "hash": -257360004, "terms": ["Where can I find the Developer Guide?", "can I find the Developer Guide?", "I find the Developer Guide?", "find the Developer Guide?", "the Developer Guide?", "Developer Guide?", "Guide?"], "tokens": ["Where", "can", "I", "find", "the", "Developer", "Guide", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1653915640", "fields": {"term": "What should I do before proceeding?", "namespace": "open-p", "hash": -1653915640, "terms": ["What should I do before proceeding?", "should I do before proceeding?", "I do before proceeding?", "do before proceeding?", "before proceeding?", "proceeding?"], "tokens": ["What", "should", "I", "do", "before", "proceeding", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::881350417", "fields": {"term": "How to accept http requests?", "namespace": "open-p", "hash": 881350417, "terms": ["How to accept http requests?", "to accept http requests?", "accept http requests?", "http requests?", "requests?"], "tokens": ["How", "to", "accept", "http", "requests", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::307343118", "fields": {"term": "How to verify new server?", "namespace": "open-p", "hash": 307343118, "terms": ["How to verify new server?", "to verify new server?", "verify new server?", "new server?", "server?"], "tokens": ["How", "to", "verify", "new", "server", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::818836603", "fields": {"term": "What does http section disable?", "namespace": "open-p", "hash": 818836603, "terms": ["What does http section disable?", "does http section disable?", "http section disable?", "section disable?", "disable?"], "tokens": ["What", "does", "http", "section", "disable", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-864764204", "fields": {"term": "What is port binding?", "namespace": "open-p", "hash": -864764204, "terms": ["What is port binding?", "is port binding?", "port binding?", "binding?"], "tokens": ["What", "is", "port", "binding", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-191439951", "fields": {"term": "Is binding to privileged ports supported?", "namespace": "open-p", "hash": -191439951, "terms": ["Is binding to privileged ports supported?", "binding to privileged ports supported?", "to privileged ports supported?", "privileged ports supported?", "ports supported?", "supported?"], "tokens": ["Is", "binding", "to", "privileged", "ports", "supported", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-767483661", "fields": {"term": "How to modify server configuration?", "namespace": "open-p", "hash": -767483661, "terms": ["How to modify server configuration?", "to modify server configuration?", "modify server configuration?", "server configuration?", "configuration?"], "tokens": ["How", "to", "modify", "server", "configuration", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2129486167", "fields": {"term": "What is jdisc.http.connector?", "namespace": "open-p", "hash": 2129486167, "terms": ["What is jdisc.http.connector?", "is jdisc.http.connector?", "jdisc.http.connector?"], "tokens": ["What", "is", "jdisc", "http", "connector", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::864252184", "fields": {"term": "How to configure TLS?", "namespace": "open-p", "hash": 864252184, "terms": ["How to configure TLS?", "to configure TLS?", "configure TLS?", "TLS?"], "tokens": ["How", "to", "configure", "TLS", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-726752460", "fields": {"term": "What is ssl-provider?", "namespace": "open-p", "hash": -726752460, "terms": ["What is ssl-provider?", "is ssl-provider?", "ssl-provider?"], "tokens": ["What", "is", "ssl", "provider", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1099857343", "fields": {"term": "What are request filters?", "namespace": "open-p", "hash": 1099857343, "terms": ["What are request filters?", "are request filters?", "request filters?", "filters?"], "tokens": ["What", "are", "request", "filters", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-54455675", "fields": {"term": "What do request filters do?", "namespace": "open-p", "hash": -54455675, "terms": ["What do request filters do?", "do request filters do?", "request filters do?", "filters do?", "do?"], "tokens": ["What", "do", "request", "filters", "do", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1198888906", "fields": {"term": "What are response filters?", "namespace": "open-p", "hash": 1198888906, "terms": ["What are response filters?", "are response filters?", "response filters?", "filters?"], "tokens": ["What", "are", "response", "filters", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1263212626", "fields": {"term": "What do response filters do?", "namespace": "open-p", "hash": 1263212626, "terms": ["What do response filters do?", "do response filters do?", "response filters do?", "filters do?", "do?"], "tokens": ["What", "do", "response", "filters", "do", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-405689843", "fields": {"term": "When do request filters run?", "namespace": "open-p", "hash": -405689843, "terms": ["When do request filters run?", "do request filters run?", "request filters run?", "filters run?", "run?"], "tokens": ["When", "do", "request", "filters", "run", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1816321614", "fields": {"term": "When do response filters run?", "namespace": "open-p", "hash": 1816321614, "terms": ["When do response filters run?", "do response filters run?", "response filters run?", "filters run?", "run?"], "tokens": ["When", "do", "response", "filters", "run", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1377305582", "fields": {"term": "What tasks are filters used for?", "namespace": "open-p", "hash": -1377305582, "terms": ["What tasks are filters used for?", "tasks are filters used for?", "are filters used for?", "filters used for?", "used for?", "for?"], "tokens": ["What", "tasks", "are", "filters", "used", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-135182758", "fields": {"term": "What is the purpose of filters?", "namespace": "open-p", "hash": -135182758, "terms": ["What is the purpose of filters?", "is the purpose of filters?", "the purpose of filters?", "purpose of filters?", "of filters?", "filters?"], "tokens": ["What", "is", "the", "purpose", "of", "filters", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1242770646", "fields": {"term": "What are filter chains?", "namespace": "open-p", "hash": 1242770646, "terms": ["What are filter chains?", "are filter chains?", "filter chains?", "chains?"], "tokens": ["What", "are", "filter", "chains", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1646660044", "fields": {"term": "How are filter chains set up?", "namespace": "open-p", "hash": -1646660044, "terms": ["How are filter chains set up?", "are filter chains set up?", "filter chains set up?", "chains set up?", "set up?", "up?"], "tokens": ["How", "are", "filter", "chains", "set", "up", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2091275528", "fields": {"term": "What is a filter binding?", "namespace": "open-p", "hash": 2091275528, "terms": ["What is a filter binding?", "is a filter binding?", "a filter binding?", "filter binding?", "binding?"], "tokens": ["What", "is", "a", "filter", "binding", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1848243063", "fields": {"term": "What happens if a request doesn't match any filter?", "namespace": "open-p", "hash": 1848243063, "terms": ["What happens if a request doesn't match any filter?", "happens if a request doesn't match any filter?", "if a request doesn't match any filter?", "a request doesn't match any filter?", "request doesn't match any filter?", "doesn't match any filter?", "match any filter?", "any filter?", "filter?"], "tokens": ["What", "happens", "if", "a", "request", "doesn", "'", "t", "match", "any", "filter", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-47810708", "fields": {"term": "Can a filter be used in multiple chains?", "namespace": "open-p", "hash": -47810708, "terms": ["Can a filter be used in multiple chains?", "a filter be used in multiple chains?", "filter be used in multiple chains?", "be used in multiple chains?", "used in multiple chains?", "in multiple chains?", "multiple chains?", "chains?"], "tokens": ["Can", "a", "filter", "be", "used", "in", "multiple", "chains", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-123351294", "fields": {"term": "What is the strict mode for filter chains?", "namespace": "open-p", "hash": -123351294, "terms": ["What is the strict mode for filter chains?", "is the strict mode for filter chains?", "the strict mode for filter chains?", "strict mode for filter chains?", "mode for filter chains?", "for filter chains?", "filter chains?", "chains?"], "tokens": ["What", "is", "the", "strict", "mode", "for", "filter", "chains", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1003598373", "fields": {"term": "How do you bind a filter chain to a server?", "namespace": "open-p", "hash": -1003598373, "terms": ["How do you bind a filter chain to a server?", "do you bind a filter chain to a server?", "you bind a filter chain to a server?", "bind a filter chain to a server?", "a filter chain to a server?", "filter chain to a server?", "chain to a server?", "to a server?", "a server?", "server?"], "tokens": ["How", "do", "you", "bind", "a", "filter", "chain", "to", "a", "server", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1054239258", "fields": {"term": "What is a response filter chain used for?", "namespace": "open-p", "hash": 1054239258, "terms": ["What is a response filter chain used for?", "is a response filter chain used for?", "a response filter chain used for?", "response filter chain used for?", "filter chain used for?", "chain used for?", "used for?", "for?"], "tokens": ["What", "is", "a", "response", "filter", "chain", "used", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::329056789", "fields": {"term": "What is a request filter chain?", "namespace": "open-p", "hash": 329056789, "terms": ["What is a request filter chain?", "is a request filter chain?", "a request filter chain?", "request filter chain?", "filter chain?", "chain?"], "tokens": ["What", "is", "a", "request", "filter", "chain", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1815203795", "fields": {"term": "How do you add excludes to a chain element?", "namespace": "open-p", "hash": 1815203795, "terms": ["How do you add excludes to a chain element?", "do you add excludes to a chain element?", "you add excludes to a chain element?", "add excludes to a chain element?", "excludes to a chain element?", "to a chain element?", "a chain element?", "chain element?", "element?"], "tokens": ["How", "do", "you", "add", "excludes", "to", "a", "chain", "element", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::687967593", "fields": {"term": "What is the format for setting excludes?", "namespace": "open-p", "hash": 687967593, "terms": ["What is the format for setting excludes?", "is the format for setting excludes?", "the format for setting excludes?", "format for setting excludes?", "for setting excludes?", "setting excludes?", "excludes?"], "tokens": ["What", "is", "the", "format", "for", "setting", "excludes", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::354988766", "fields": {"term": "What is an example of using excludes in a chain element?", "namespace": "open-p", "hash": 354988766, "terms": ["What is an example of using excludes in a chain element?", "is an example of using excludes in a chain element?", "an example of using excludes in a chain element?", "example of using excludes in a chain element?", "of using excludes in a chain element?", "using excludes in a chain element?", "excludes in a chain element?", "in a chain element?", "a chain element?", "chain element?", "element?"], "tokens": ["What", "is", "an", "example", "of", "using", "excludes", "in", "a", "chain", "element", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1349300525", "fields": {"term": "What is filter-bundle?", "namespace": "open-p", "hash": -1349300525, "terms": ["What is filter-bundle?", "is filter-bundle?", "filter-bundle?"], "tokens": ["What", "is", "filter", "bundle", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1518761123", "fields": {"term": "What does TestRequestFilter do?", "namespace": "open-p", "hash": -1518761123, "terms": ["What does TestRequestFilter do?", "does TestRequestFilter do?", "TestRequestFilter do?", "do?"], "tokens": ["What", "does", "TestRequestFilter", "do", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1574923290", "fields": {"term": "What is HttpRequest?", "namespace": "open-p", "hash": -1574923290, "terms": ["What is HttpRequest?", "is HttpRequest?", "HttpRequest?"], "tokens": ["What", "is", "HttpRequest", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-550299832", "fields": {"term": "What is HttpResponse?", "namespace": "open-p", "hash": -550299832, "terms": ["What is HttpResponse?", "is HttpResponse?", "HttpResponse?"], "tokens": ["What", "is", "HttpResponse", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1243948471", "fields": {"term": "What is HTTP/2 performance?", "namespace": "open-p", "hash": 1243948471, "terms": ["What is HTTP/2 performance?", "is HTTP/2 performance?", "HTTP/2 performance?", "performance?"], "tokens": ["What", "is", "HTTP", "/", "2", "performance", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1086436789", "fields": {"term": "What is HTTP/2?", "namespace": "open-p", "hash": 1086436789, "terms": ["What is HTTP/2?", "is HTTP/2?", "HTTP/2?"], "tokens": ["What", "is", "HTTP", "/", "2", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-133360159", "fields": {"term": "When is HTTP/2 enabled?", "namespace": "open-p", "hash": -133360159, "terms": ["When is HTTP/2 enabled?", "is HTTP/2 enabled?", "HTTP/2 enabled?", "enabled?"], "tokens": ["When", "is", "HTTP", "/", "2", "enabled", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1962185782", "fields": {"term": "What is the TLS ALPN Extension?", "namespace": "open-p", "hash": 1962185782, "terms": ["What is the TLS ALPN Extension?", "is the TLS ALPN Extension?", "the TLS ALPN Extension?", "TLS ALPN Extension?", "ALPN Extension?", "Extension?"], "tokens": ["What", "is", "the", "TLS", "ALPN", "Extension", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1990915476", "fields": {"term": "What are the requirements for using HTTP/2 with TLS?", "namespace": "open-p", "hash": 1990915476, "terms": ["What are the requirements for using HTTP/2 with TLS?", "are the requirements for using HTTP/2 with TLS?", "the requirements for using HTTP/2 with TLS?", "requirements for using HTTP/2 with TLS?", "for using HTTP/2 with TLS?", "using HTTP/2 with TLS?", "HTTP/2 with TLS?", "with TLS?", "TLS?"], "tokens": ["What", "are", "the", "requirements", "for", "using", "HTTP", "/", "2", "with", "TLS", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-900661114", "fields": {"term": "What are the supported mechanisms?", "namespace": "open-p", "hash": -900661114, "terms": ["What are the supported mechanisms?", "are the supported mechanisms?", "the supported mechanisms?", "supported mechanisms?", "mechanisms?"], "tokens": ["What", "are", "the", "supported", "mechanisms", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-733224249", "fields": {"term": "How to upgrade to HTTP/2?", "namespace": "open-p", "hash": -733224249, "terms": ["How to upgrade to HTTP/2?", "to upgrade to HTTP/2?", "upgrade to HTTP/2?", "to HTTP/2?", "HTTP/2?"], "tokens": ["How", "to", "upgrade", "to", "HTTP", "/", "2", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1419728847", "fields": {"term": "What is HTTP/2 with prior knowledge?", "namespace": "open-p", "hash": -1419728847, "terms": ["What is HTTP/2 with prior knowledge?", "is HTTP/2 with prior knowledge?", "HTTP/2 with prior knowledge?", "with prior knowledge?", "prior knowledge?", "knowledge?"], "tokens": ["What", "is", "HTTP", "/", "2", "with", "prior", "knowledge", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::213766300", "fields": {"term": "What is HTTP/2 multiplexing?", "namespace": "open-p", "hash": 213766300, "terms": ["What is HTTP/2 multiplexing?", "is HTTP/2 multiplexing?", "HTTP/2 multiplexing?", "multiplexing?"], "tokens": ["What", "is", "HTTP", "/", "2", "multiplexing", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1103659167", "fields": {"term": "What is the advantage of HTTP/2 over HTTP/1.1?", "namespace": "open-p", "hash": 1103659167, "terms": ["What is the advantage of HTTP/2 over HTTP/1.1?", "is the advantage of HTTP/2 over HTTP/1.1?", "the advantage of HTTP/2 over HTTP/1.1?", "advantage of HTTP/2 over HTTP/1.1?", "of HTTP/2 over HTTP/1.1?", "HTTP/2 over HTTP/1.1?", "over HTTP/1.1?", "HTTP/1.1?"], "tokens": ["What", "is", "the", "advantage", "of", "HTTP", "/", "2", "over", "HTTP", "/", "1", "1", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1087899075", "fields": {"term": "What does vespa-feed-client provide?", "namespace": "open-p", "hash": 1087899075, "terms": ["What does vespa-feed-client provide?", "does vespa-feed-client provide?", "vespa-feed-client provide?", "provide?"], "tokens": ["What", "does", "vespa", "feed", "client", "provide", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::137133130", "fields": {"term": "What is the implementation language of vespa-feed-client?", "namespace": "open-p", "hash": 137133130, "terms": ["What is the implementation language of vespa-feed-client?", "is the implementation language of vespa-feed-client?", "the implementation language of vespa-feed-client?", "implementation language of vespa-feed-client?", "language of vespa-feed-client?", "of vespa-feed-client?", "vespa-feed-client?"], "tokens": ["What", "is", "the", "implementation", "language", "of", "vespa", "feed", "client", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::161285764", "fields": {"term": "What is the max concurrency of the v1 API?", "namespace": "open-p", "hash": 161285764, "terms": ["What is the max concurrency of the v1 API?", "is the max concurrency of the v1 API?", "the max concurrency of the v1 API?", "max concurrency of the v1 API?", "concurrency of the v1 API?", "of the v1 API?", "the v1 API?", "v1 API?", "API?"], "tokens": ["What", "is", "the", "max", "concurrency", "of", "the", "v1", "API", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::315315744", "fields": {"term": "What are the reasons to use multiple TCP connections?", "namespace": "open-p", "hash": 315315744, "terms": ["What are the reasons to use multiple TCP connections?", "are the reasons to use multiple TCP connections?", "the reasons to use multiple TCP connections?", "reasons to use multiple TCP connections?", "to use multiple TCP connections?", "use multiple TCP connections?", "multiple TCP connections?", "TCP connections?", "connections?"], "tokens": ["What", "are", "the", "reasons", "to", "use", "multiple", "TCP", "connections", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1753283568", "fields": {"term": "How can a single container saturate the content layer?", "namespace": "open-p", "hash": 1753283568, "terms": ["How can a single container saturate the content layer?", "can a single container saturate the content layer?", "a single container saturate the content layer?", "single container saturate the content layer?", "container saturate the content layer?", "saturate the content layer?", "the content layer?", "content layer?", "layer?"], "tokens": ["How", "can", "a", "single", "container", "saturate", "the", "content", "layer", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::578702694", "fields": {"term": "Why may a client have to use more connections than container nodes?", "namespace": "open-p", "hash": 578702694, "terms": ["Why may a client have to use more connections than container nodes?", "may a client have to use more connections than container nodes?", "a client have to use more connections than container nodes?", "client have to use more connections than container nodes?", "have to use more connections than container nodes?", "to use more connections than container nodes?", "use more connections than container nodes?", "more connections than container nodes?", "connections than container nodes?", "than container nodes?", "container nodes?", "nodes?"], "tokens": ["Why", "may", "a", "client", "have", "to", "use", "more", "connections", "than", "container", "nodes", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::466945610", "fields": {"term": "What is the benefit of using multiple connections for utilizing several CPU cores?", "namespace": "open-p", "hash": 466945610, "terms": ["What is the benefit of using multiple connections for utilizing several CPU cores?", "is the benefit of using multiple connections for utilizing several CPU cores?", "the benefit of using multiple connections for utilizing several CPU cores?", "benefit of using multiple connections for utilizing several CPU cores?", "of using multiple connections for utilizing several CPU cores?", "using multiple connections for utilizing several CPU cores?", "multiple connections for utilizing several CPU cores?", "connections for utilizing several CPU cores?", "for utilizing several CPU cores?", "utilizing several CPU cores?", "several CPU cores?", "CPU cores?", "cores?"], "tokens": ["What", "is", "the", "benefit", "of", "using", "multiple", "connections", "for", "utilizing", "several", "CPU", "cores", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::753517193", "fields": {"term": "What is vespa-feed-client used for?", "namespace": "open-p", "hash": 753517193, "terms": ["What is vespa-feed-client used for?", "is vespa-feed-client used for?", "vespa-feed-client used for?", "used for?", "for?"], "tokens": ["What", "is", "vespa", "feed", "client", "used", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1370894920", "fields": {"term": "What benchmarking tool is recommended?", "namespace": "open-p", "hash": 1370894920, "terms": ["What benchmarking tool is recommended?", "benchmarking tool is recommended?", "tool is recommended?", "is recommended?", "recommended?"], "tokens": ["What", "benchmarking", "tool", "is", "recommended", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::89621392", "fields": {"term": "Does vespa-fbench support HTTP/2?", "namespace": "open-p", "hash": 89621392, "terms": ["Does vespa-fbench support HTTP/2?", "vespa-fbench support HTTP/2?", "support HTTP/2?", "HTTP/2?"], "tokens": ["Does", "vespa", "fbench", "support", "HTTP", "/", "2", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-38188109", "fields": {"term": "What are the 4 Java alternatives for feeding Vespa?", "namespace": "open-p", "hash": -38188109, "terms": ["What are the 4 Java alternatives for feeding Vespa?", "are the 4 Java alternatives for feeding Vespa?", "the 4 Java alternatives for feeding Vespa?", "4 Java alternatives for feeding Vespa?", "Java alternatives for feeding Vespa?", "alternatives for feeding Vespa?", "for feeding Vespa?", "feeding Vespa?", "Vespa?"], "tokens": ["What", "are", "the", "4", "Java", "alternatives", "for", "feeding", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1667124597", "fields": {"term": "What is the curl command to use with HTTP/2 prior knowledge?", "namespace": "open-p", "hash": -1667124597, "terms": ["What is the curl command to use with HTTP/2 prior knowledge?", "is the curl command to use with HTTP/2 prior knowledge?", "the curl command to use with HTTP/2 prior knowledge?", "curl command to use with HTTP/2 prior knowledge?", "command to use with HTTP/2 prior knowledge?", "to use with HTTP/2 prior knowledge?", "use with HTTP/2 prior knowledge?", "with HTTP/2 prior knowledge?", "HTTP/2 prior knowledge?", "prior knowledge?", "knowledge?"], "tokens": ["What", "is", "the", "curl", "command", "to", "use", "with", "HTTP", "/", "2", "prior", "knowledge", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::338465823", "fields": {"term": "Can the server perform a protocol upgrade with a request containing content?", "namespace": "open-p", "hash": 338465823, "terms": ["Can the server perform a protocol upgrade with a request containing content?", "the server perform a protocol upgrade with a request containing content?", "server perform a protocol upgrade with a request containing content?", "perform a protocol upgrade with a request containing content?", "a protocol upgrade with a request containing content?", "protocol upgrade with a request containing content?", "upgrade with a request containing content?", "with a request containing content?", "a request containing content?", "request containing content?", "containing content?", "content?"], "tokens": ["Can", "the", "server", "perform", "a", "protocol", "upgrade", "with", "a", "request", "containing", "content", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-794969347", "fields": {"term": "What is the distribution algorithm?", "namespace": "open-p", "hash": -794969347, "terms": ["What is the distribution algorithm?", "is the distribution algorithm?", "the distribution algorithm?", "distribution algorithm?", "algorithm?"], "tokens": ["What", "is", "the", "distribution", "algorithm", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2021008506", "fields": {"term": "How is the distribution algorithm used?", "namespace": "open-p", "hash": -2021008506, "terms": ["How is the distribution algorithm used?", "is the distribution algorithm used?", "the distribution algorithm used?", "distribution algorithm used?", "algorithm used?", "used?"], "tokens": ["How", "is", "the", "distribution", "algorithm", "used", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1926347149", "fields": {"term": "What is tracked current state?", "namespace": "open-p", "hash": 1926347149, "terms": ["What is tracked current state?", "is tracked current state?", "tracked current state?", "current state?", "state?"], "tokens": ["What", "is", "tracked", "current", "state", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1362659047", "fields": {"term": "What does the output of the distribution algorithm give?", "namespace": "open-p", "hash": -1362659047, "terms": ["What does the output of the distribution algorithm give?", "does the output of the distribution algorithm give?", "the output of the distribution algorithm give?", "output of the distribution algorithm give?", "of the distribution algorithm give?", "the distribution algorithm give?", "distribution algorithm give?", "algorithm give?", "give?"], "tokens": ["What", "does", "the", "output", "of", "the", "distribution", "algorithm", "give", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1558614022", "fields": {"term": "What is the input to the distribution algorithm?", "namespace": "open-p", "hash": 1558614022, "terms": ["What is the input to the distribution algorithm?", "is the input to the distribution algorithm?", "the input to the distribution algorithm?", "input to the distribution algorithm?", "to the distribution algorithm?", "the distribution algorithm?", "distribution algorithm?", "algorithm?"], "tokens": ["What", "is", "the", "input", "to", "the", "distribution", "algorithm", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1282985183", "fields": {"term": "What are the desired qualities for the ideal state algorithm?", "namespace": "open-p", "hash": -1282985183, "terms": ["What are the desired qualities for the ideal state algorithm?", "are the desired qualities for the ideal state algorithm?", "the desired qualities for the ideal state algorithm?", "desired qualities for the ideal state algorithm?", "qualities for the ideal state algorithm?", "for the ideal state algorithm?", "the ideal state algorithm?", "ideal state algorithm?", "state algorithm?", "algorithm?"], "tokens": ["What", "are", "the", "desired", "qualities", "for", "the", "ideal", "state", "algorithm", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1316042650", "fields": {"term": "What is the purpose of minimal reassignment in the ideal state algorithm?", "namespace": "open-p", "hash": -1316042650, "terms": ["What is the purpose of minimal reassignment in the ideal state algorithm?", "is the purpose of minimal reassignment in the ideal state algorithm?", "the purpose of minimal reassignment in the ideal state algorithm?", "purpose of minimal reassignment in the ideal state algorithm?", "of minimal reassignment in the ideal state algorithm?", "minimal reassignment in the ideal state algorithm?", "reassignment in the ideal state algorithm?", "in the ideal state algorithm?", "the ideal state algorithm?", "ideal state algorithm?", "state algorithm?", "algorithm?"], "tokens": ["What", "is", "the", "purpose", "of", "minimal", "reassignment", "in", "the", "ideal", "state", "algorithm", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1956009341", "fields": {"term": "What tasks require ideal location calculations?", "namespace": "open-p", "hash": -1956009341, "terms": ["What tasks require ideal location calculations?", "tasks require ideal location calculations?", "require ideal location calculations?", "ideal location calculations?", "location calculations?", "calculations?"], "tokens": ["What", "tasks", "require", "ideal", "location", "calculations", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::767494746", "fields": {"term": "How can caching be avoided?", "namespace": "open-p", "hash": 767494746, "terms": ["How can caching be avoided?", "can caching be avoided?", "caching be avoided?", "be avoided?", "avoided?"], "tokens": ["How", "can", "caching", "be", "avoided", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-836611305", "fields": {"term": "Why is expensive computational cost problematic?", "namespace": "open-p", "hash": -836611305, "terms": ["Why is expensive computational cost problematic?", "is expensive computational cost problematic?", "expensive computational cost problematic?", "computational cost problematic?", "cost problematic?", "problematic?"], "tokens": ["Why", "is", "expensive", "computational", "cost", "problematic", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-24064405", "fields": {"term": "What is the content layer algorithm?", "namespace": "open-p", "hash": -24064405, "terms": ["What is the content layer algorithm?", "is the content layer algorithm?", "the content layer algorithm?", "content layer algorithm?", "layer algorithm?", "algorithm?"], "tokens": ["What", "is", "the", "content", "layer", "algorithm", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-22924642", "fields": {"term": "What does the distribution key decide?", "namespace": "open-p", "hash": -22924642, "terms": ["What does the distribution key decide?", "does the distribution key decide?", "the distribution key decide?", "distribution key decide?", "key decide?", "decide?"], "tokens": ["What", "does", "the", "distribution", "key", "decide", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::525157375", "fields": {"term": "What is the purpose of using pseudo-random numbers?", "namespace": "open-p", "hash": 525157375, "terms": ["What is the purpose of using pseudo-random numbers?", "is the purpose of using pseudo-random numbers?", "the purpose of using pseudo-random numbers?", "purpose of using pseudo-random numbers?", "of using pseudo-random numbers?", "using pseudo-random numbers?", "pseudo-random numbers?", "numbers?"], "tokens": ["What", "is", "the", "purpose", "of", "using", "pseudo", "random", "numbers", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1518629395", "fields": {"term": "What determines the randomness of migrating owners?", "namespace": "open-p", "hash": 1518629395, "terms": ["What determines the randomness of migrating owners?", "determines the randomness of migrating owners?", "the randomness of migrating owners?", "randomness of migrating owners?", "of migrating owners?", "migrating owners?", "owners?"], "tokens": ["What", "determines", "the", "randomness", "of", "migrating", "owners", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1668740556", "fields": {"term": "What is the impact of altering the cluster state on bucket distribution?", "namespace": "open-p", "hash": -1668740556, "terms": ["What is the impact of altering the cluster state on bucket distribution?", "is the impact of altering the cluster state on bucket distribution?", "the impact of altering the cluster state on bucket distribution?", "impact of altering the cluster state on bucket distribution?", "of altering the cluster state on bucket distribution?", "altering the cluster state on bucket distribution?", "the cluster state on bucket distribution?", "cluster state on bucket distribution?", "state on bucket distribution?", "on bucket distribution?", "bucket distribution?", "distribution?"], "tokens": ["What", "is", "the", "impact", "of", "altering", "the", "cluster", "state", "on", "bucket", "distribution", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-426522839", "fields": {"term": "What is the formula for node weights?", "namespace": "open-p", "hash": -426522839, "terms": ["What is the formula for node weights?", "is the formula for node weights?", "the formula for node weights?", "formula for node weights?", "for node weights?", "node weights?", "weights?"], "tokens": ["What", "is", "the", "formula", "for", "node", "weights", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::21753551", "fields": {"term": "What is node capacity?", "namespace": "open-p", "hash": 21753551, "terms": ["What is node capacity?", "is node capacity?", "node capacity?", "capacity?"], "tokens": ["What", "is", "node", "capacity", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-655778916", "fields": {"term": "What increases skew in distribution?", "namespace": "open-p", "hash": -655778916, "terms": ["What increases skew in distribution?", "increases skew in distribution?", "skew in distribution?", "in distribution?", "distribution?"], "tokens": ["What", "increases", "skew", "in", "distribution", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2131492232", "fields": {"term": "How to decrease skew?", "namespace": "open-p", "hash": 2131492232, "terms": ["How to decrease skew?", "to decrease skew?", "decrease skew?", "skew?"], "tokens": ["How", "to", "decrease", "skew", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1322641171", "fields": {"term": "What is the impact of having less targets?", "namespace": "open-p", "hash": 1322641171, "terms": ["What is the impact of having less targets?", "is the impact of having less targets?", "the impact of having less targets?", "impact of having less targets?", "of having less targets?", "having less targets?", "less targets?", "targets?"], "tokens": ["What", "is", "the", "impact", "of", "having", "less", "targets", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1679848790", "fields": {"term": "What is the impact of using a more uniform pseudo-random function?", "namespace": "open-p", "hash": -1679848790, "terms": ["What is the impact of using a more uniform pseudo-random function?", "is the impact of using a more uniform pseudo-random function?", "the impact of using a more uniform pseudo-random function?", "impact of using a more uniform pseudo-random function?", "of using a more uniform pseudo-random function?", "using a more uniform pseudo-random function?", "a more uniform pseudo-random function?", "more uniform pseudo-random function?", "uniform pseudo-random function?", "pseudo-random function?", "function?"], "tokens": ["What", "is", "the", "impact", "of", "using", "a", "more", "uniform", "pseudo", "random", "function", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1793509904", "fields": {"term": "What happens when the algorithm generates skew in the distribution?", "namespace": "open-p", "hash": 1793509904, "terms": ["What happens when the algorithm generates skew in the distribution?", "happens when the algorithm generates skew in the distribution?", "when the algorithm generates skew in the distribution?", "the algorithm generates skew in the distribution?", "algorithm generates skew in the distribution?", "generates skew in the distribution?", "skew in the distribution?", "in the distribution?", "the distribution?", "distribution?"], "tokens": ["What", "happens", "when", "the", "algorithm", "generates", "skew", "in", "the", "distribution", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-35522172", "fields": {"term": "What is the waste metric?", "namespace": "open-p", "hash": -35522172, "terms": ["What is the waste metric?", "is the waste metric?", "the waste metric?", "waste metric?", "metric?"], "tokens": ["What", "is", "the", "waste", "metric", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1702900367", "fields": {"term": "How is waste percentage calculated?", "namespace": "open-p", "hash": -1702900367, "terms": ["How is waste percentage calculated?", "is waste percentage calculated?", "waste percentage calculated?", "percentage calculated?", "calculated?"], "tokens": ["How", "is", "waste", "percentage", "calculated", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::1844023315", "fields": {"term": "What does distribution bit count indicate?", "namespace": "open-p", "hash": 1844023315, "terms": ["What does distribution bit count indicate?", "does distribution bit count indicate?", "distribution bit count indicate?", "bit count indicate?", "count indicate?", "indicate?"], "tokens": ["What", "does", "distribution", "bit", "count", "indicate", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-284422348", "fields": {"term": "How does redundancy affect bucket distribution?", "namespace": "open-p", "hash": -284422348, "terms": ["How does redundancy affect bucket distribution?", "does redundancy affect bucket distribution?", "redundancy affect bucket distribution?", "affect bucket distribution?", "bucket distribution?", "distribution?"], "tokens": ["How", "does", "redundancy", "affect", "bucket", "distribution", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-216961509", "fields": {"term": "What is the formula for waste calculation?", "namespace": "open-p", "hash": -216961509, "terms": ["What is the formula for waste calculation?", "is the formula for waste calculation?", "the formula for waste calculation?", "formula for waste calculation?", "for waste calculation?", "waste calculation?", "calculation?"], "tokens": ["What", "is", "the", "formula", "for", "waste", "calculation", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1012471079", "fields": {"term": "What is considered as full capacity for a cluster?", "namespace": "open-p", "hash": 1012471079, "terms": ["What is considered as full capacity for a cluster?", "is considered as full capacity for a cluster?", "considered as full capacity for a cluster?", "as full capacity for a cluster?", "full capacity for a cluster?", "capacity for a cluster?", "for a cluster?", "a cluster?", "cluster?"], "tokens": ["What", "is", "considered", "as", "full", "capacity", "for", "a", "cluster", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-235735637", "fields": {"term": "What do different waste colors indicate?", "namespace": "open-p", "hash": -235735637, "terms": ["What do different waste colors indicate?", "do different waste colors indicate?", "different waste colors indicate?", "waste colors indicate?", "colors indicate?", "indicate?"], "tokens": ["What", "do", "different", "waste", "colors", "indicate", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::954440968", "fields": {"term": "What affects distribution in Vespa?", "namespace": "open-p", "hash": 954440968, "terms": ["What affects distribution in Vespa?", "affects distribution in Vespa?", "distribution in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["What", "affects", "distribution", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1519470510", "fields": {"term": "What is waste in Vespa?", "namespace": "open-p", "hash": 1519470510, "terms": ["What is waste in Vespa?", "is waste in Vespa?", "waste in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["What", "is", "waste", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1283100160", "fields": {"term": "What is a distribution profile?", "namespace": "open-p", "hash": -1283100160, "terms": ["What is a distribution profile?", "is a distribution profile?", "a distribution profile?", "distribution profile?", "profile?"], "tokens": ["What", "is", "a", "distribution", "profile", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1915356696", "fields": {"term": "What is the impact of changing distribution bits?", "namespace": "open-p", "hash": 1915356696, "terms": ["What is the impact of changing distribution bits?", "is the impact of changing distribution bits?", "the impact of changing distribution bits?", "impact of changing distribution bits?", "of changing distribution bits?", "changing distribution bits?", "distribution bits?", "bits?"], "tokens": ["What", "is", "the", "impact", "of", "changing", "distribution", "bits", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-792656135", "fields": {"term": "How often should distribution bit count be altered?", "namespace": "open-p", "hash": -792656135, "terms": ["How often should distribution bit count be altered?", "often should distribution bit count be altered?", "should distribution bit count be altered?", "distribution bit count be altered?", "bit count be altered?", "count be altered?", "be altered?", "altered?"], "tokens": ["How", "often", "should", "distribution", "bit", "count", "be", "altered", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1753059354", "fields": {"term": "How does waste impact computation?", "namespace": "open-p", "hash": -1753059354, "terms": ["How does waste impact computation?", "does waste impact computation?", "waste impact computation?", "impact computation?", "computation?"], "tokens": ["How", "does", "waste", "impact", "computation", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1241523232", "fields": {"term": "What is the computational cost of computing waste?", "namespace": "open-p", "hash": 1241523232, "terms": ["What is the computational cost of computing waste?", "is the computational cost of computing waste?", "the computational cost of computing waste?", "computational cost of computing waste?", "cost of computing waste?", "of computing waste?", "computing waste?", "waste?"], "tokens": ["What", "is", "the", "computational", "cost", "of", "computing", "waste", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2010694400", "fields": {"term": "What is strict mode?", "namespace": "open-p", "hash": -2010694400, "terms": ["What is strict mode?", "is strict mode?", "strict mode?", "mode?"], "tokens": ["What", "is", "strict", "mode", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::226718183", "fields": {"term": "What is the maximum calculated waste?", "namespace": "open-p", "hash": 226718183, "terms": ["What is the maximum calculated waste?", "is the maximum calculated waste?", "the maximum calculated waste?", "maximum calculated waste?", "calculated waste?", "waste?"], "tokens": ["What", "is", "the", "maximum", "calculated", "waste", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::617022500", "fields": {"term": "What is the minimum buckets for each node?", "namespace": "open-p", "hash": 617022500, "terms": ["What is the minimum buckets for each node?", "is the minimum buckets for each node?", "the minimum buckets for each node?", "minimum buckets for each node?", "buckets for each node?", "for each node?", "each node?", "node?"], "tokens": ["What", "is", "the", "minimum", "buckets", "for", "each", "node", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::11687945", "fields": {"term": "What is loose mode?", "namespace": "open-p", "hash": 11687945, "terms": ["What is loose mode?", "is loose mode?", "loose mode?", "mode?"], "tokens": ["What", "is", "loose", "mode", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1889613361", "fields": {"term": "What is wasteful mode?", "namespace": "open-p", "hash": -1889613361, "terms": ["What is wasteful mode?", "is wasteful mode?", "wasteful mode?", "mode?"], "tokens": ["What", "is", "wasteful", "mode", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-918085935", "fields": {"term": "What is the max calculated waste?", "namespace": "open-p", "hash": -918085935, "terms": ["What is the max calculated waste?", "is the max calculated waste?", "the max calculated waste?", "max calculated waste?", "calculated waste?", "waste?"], "tokens": ["What", "is", "the", "max", "calculated", "waste", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2076673888", "fields": {"term": "What is the practical-search guide?", "namespace": "open-p", "hash": -2076673888, "terms": ["What is the practical-search guide?", "is the practical-search guide?", "the practical-search guide?", "practical-search guide?", "guide?"], "tokens": ["What", "is", "the", "practical", "search", "guide", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-310872409", "fields": {"term": "What does the guide cover?", "namespace": "open-p", "hash": -310872409, "terms": ["What does the guide cover?", "does the guide cover?", "the guide cover?", "guide cover?", "cover?"], "tokens": ["What", "does", "the", "guide", "cover", "?"], "corpus_count": 3, "document_count": 3}}, -{"put": "id:term:term::60082747", "fields": {"term": "What does the guide introduce?", "namespace": "open-p", "hash": 60082747, "terms": ["What does the guide introduce?", "does the guide introduce?", "the guide introduce?", "guide introduce?", "introduce?"], "tokens": ["What", "does", "the", "guide", "introduce", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::953074561", "fields": {"term": "What is sizing and capacity planning?", "namespace": "open-p", "hash": 953074561, "terms": ["What is sizing and capacity planning?", "is sizing and capacity planning?", "sizing and capacity planning?", "and capacity planning?", "capacity planning?", "planning?"], "tokens": ["What", "is", "sizing", "and", "capacity", "planning", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::516019201", "fields": {"term": "How do I size a Vespa search cluster?", "namespace": "open-p", "hash": 516019201, "terms": ["How do I size a Vespa search cluster?", "do I size a Vespa search cluster?", "I size a Vespa search cluster?", "size a Vespa search cluster?", "a Vespa search cluster?", "Vespa search cluster?", "search cluster?", "cluster?"], "tokens": ["How", "do", "I", "size", "a", "Vespa", "search", "cluster", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1274315025", "fields": {"term": "What is caching in Vespa?", "namespace": "open-p", "hash": -1274315025, "terms": ["What is caching in Vespa?", "is caching in Vespa?", "caching in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["What", "is", "caching", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1718261972", "fields": {"term": "How do I enable caches in Vespa?", "namespace": "open-p", "hash": -1718261972, "terms": ["How do I enable caches in Vespa?", "do I enable caches in Vespa?", "I enable caches in Vespa?", "enable caches in Vespa?", "caches in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["How", "do", "I", "enable", "caches", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-756445220", "fields": {"term": "What is the impact of attributes on memory footprint?", "namespace": "open-p", "hash": -756445220, "terms": ["What is the impact of attributes on memory footprint?", "is the impact of attributes on memory footprint?", "the impact of attributes on memory footprint?", "impact of attributes on memory footprint?", "of attributes on memory footprint?", "attributes on memory footprint?", "on memory footprint?", "memory footprint?", "footprint?"], "tokens": ["What", "is", "the", "impact", "of", "attributes", "on", "memory", "footprint", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::430115222", "fields": {"term": "How do I find attribute memory usage?", "namespace": "open-p", "hash": 430115222, "terms": ["How do I find attribute memory usage?", "do I find attribute memory usage?", "I find attribute memory usage?", "find attribute memory usage?", "attribute memory usage?", "memory usage?", "usage?"], "tokens": ["How", "do", "I", "find", "attribute", "memory", "usage", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-292326252", "fields": {"term": "What are Proton maintenance jobs?", "namespace": "open-p", "hash": -292326252, "terms": ["What are Proton maintenance jobs?", "are Proton maintenance jobs?", "Proton maintenance jobs?", "maintenance jobs?", "jobs?"], "tokens": ["What", "are", "Proton", "maintenance", "jobs", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1299410160", "fields": {"term": "What is the impact of Proton maintenance jobs on resource usage?", "namespace": "open-p", "hash": -1299410160, "terms": ["What is the impact of Proton maintenance jobs on resource usage?", "is the impact of Proton maintenance jobs on resource usage?", "the impact of Proton maintenance jobs on resource usage?", "impact of Proton maintenance jobs on resource usage?", "of Proton maintenance jobs on resource usage?", "Proton maintenance jobs on resource usage?", "maintenance jobs on resource usage?", "jobs on resource usage?", "on resource usage?", "resource usage?", "usage?"], "tokens": ["What", "is", "the", "impact", "of", "Proton", "maintenance", "jobs", "on", "resource", "usage", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1817819892", "fields": {"term": "Why is benchmarking important in Vespa?", "namespace": "open-p", "hash": -1817819892, "terms": ["Why is benchmarking important in Vespa?", "is benchmarking important in Vespa?", "benchmarking important in Vespa?", "important in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["Why", "is", "benchmarking", "important", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::697091564", "fields": {"term": "What tools are used for benchmarking?", "namespace": "open-p", "hash": 697091564, "terms": ["What tools are used for benchmarking?", "tools are used for benchmarking?", "are used for benchmarking?", "used for benchmarking?", "for benchmarking?", "benchmarking?"], "tokens": ["What", "tools", "are", "used", "for", "benchmarking", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::686526141", "fields": {"term": "How to tune system aspects of Vespa?", "namespace": "open-p", "hash": 686526141, "terms": ["How to tune system aspects of Vespa?", "to tune system aspects of Vespa?", "tune system aspects of Vespa?", "system aspects of Vespa?", "aspects of Vespa?", "of Vespa?", "Vespa?"], "tokens": ["How", "to", "tune", "system", "aspects", "of", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-245721181", "fields": {"term": "What is vespa-fbench?", "namespace": "open-p", "hash": -245721181, "terms": ["What is vespa-fbench?", "is vespa-fbench?", "vespa-fbench?"], "tokens": ["What", "is", "vespa", "fbench", "?"], "corpus_count": 4, "document_count": 4}}, -{"put": "id:term:term::-175850337", "fields": {"term": "How to improve HTTP performance in Vespa?", "namespace": "open-p", "hash": -175850337, "terms": ["How to improve HTTP performance in Vespa?", "to improve HTTP performance in Vespa?", "improve HTTP performance in Vespa?", "HTTP performance in Vespa?", "performance in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["How", "to", "improve", "HTTP", "performance", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::553649537", "fields": {"term": "How to do performance analysis?", "namespace": "open-p", "hash": 553649537, "terms": ["How to do performance analysis?", "to do performance analysis?", "do performance analysis?", "performance analysis?", "analysis?"], "tokens": ["How", "to", "do", "performance", "analysis", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2124739433", "fields": {"term": "How to profile application?", "namespace": "open-p", "hash": -2124739433, "terms": ["How to profile application?", "to profile application?", "profile application?", "application?"], "tokens": ["How", "to", "profile", "application", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::409543262", "fields": {"term": "How to run Vespa with Valgrind?", "namespace": "open-p", "hash": 409543262, "terms": ["How to run Vespa with Valgrind?", "to run Vespa with Valgrind?", "run Vespa with Valgrind?", "Vespa with Valgrind?", "with Valgrind?", "Valgrind?"], "tokens": ["How", "to", "run", "Vespa", "with", "Valgrind", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1128076478", "fields": {"term": "What is JDisc?", "namespace": "open-p", "hash": -1128076478, "terms": ["What is JDisc?", "is JDisc?", "JDisc?"], "tokens": ["What", "is", "JDisc", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::181295953", "fields": {"term": "What does JDisc do?", "namespace": "open-p", "hash": 181295953, "terms": ["What does JDisc do?", "does JDisc do?", "JDisc do?", "do?"], "tokens": ["What", "does", "JDisc", "do", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::912209610", "fields": {"term": "What are the features of JDisc?", "namespace": "open-p", "hash": 912209610, "terms": ["What are the features of JDisc?", "are the features of JDisc?", "the features of JDisc?", "features of JDisc?", "of JDisc?", "JDisc?"], "tokens": ["What", "are", "the", "features", "of", "JDisc", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1091575512", "fields": {"term": "What is the component model of JDisc?", "namespace": "open-p", "hash": -1091575512, "terms": ["What is the component model of JDisc?", "is the component model of JDisc?", "the component model of JDisc?", "component model of JDisc?", "model of JDisc?", "of JDisc?", "JDisc?"], "tokens": ["What", "is", "the", "component", "model", "of", "JDisc", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::930526892", "fields": {"term": "What are the standard components in JDisc?", "namespace": "open-p", "hash": 930526892, "terms": ["What are the standard components in JDisc?", "are the standard components in JDisc?", "the standard components in JDisc?", "standard components in JDisc?", "components in JDisc?", "in JDisc?", "JDisc?"], "tokens": ["What", "are", "the", "standard", "components", "in", "JDisc", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1124879069", "fields": {"term": "What is the chain composition mechanism in JDisc?", "namespace": "open-p", "hash": 1124879069, "terms": ["What is the chain composition mechanism in JDisc?", "is the chain composition mechanism in JDisc?", "the chain composition mechanism in JDisc?", "chain composition mechanism in JDisc?", "composition mechanism in JDisc?", "mechanism in JDisc?", "in JDisc?", "JDisc?"], "tokens": ["What", "is", "the", "chain", "composition", "mechanism", "in", "JDisc", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1594564200", "fields": {"term": "What are the building blocks of Processing?", "namespace": "open-p", "hash": -1594564200, "terms": ["What are the building blocks of Processing?", "are the building blocks of Processing?", "the building blocks of Processing?", "building blocks of Processing?", "blocks of Processing?", "of Processing?", "Processing?"], "tokens": ["What", "are", "the", "building", "blocks", "of", "Processing", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1495965784", "fields": {"term": "What is a healthcheck?", "namespace": "open-p", "hash": 1495965784, "terms": ["What is a healthcheck?", "is a healthcheck?", "a healthcheck?", "healthcheck?"], "tokens": ["What", "is", "a", "healthcheck", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::85060794", "fields": {"term": "What is the Container's request processing lifecycle?", "namespace": "open-p", "hash": 85060794, "terms": ["What is the Container's request processing lifecycle?", "is the Container's request processing lifecycle?", "the Container's request processing lifecycle?", "Container's request processing lifecycle?", "request processing lifecycle?", "processing lifecycle?", "lifecycle?"], "tokens": ["What", "is", "the", "Container", "'", "s", "request", "processing", "lifecycle", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::812528549", "fields": {"term": "What is the News Search Tutorial?", "namespace": "open-p", "hash": 812528549, "terms": ["What is the News Search Tutorial?", "is the News Search Tutorial?", "the News Search Tutorial?", "News Search Tutorial?", "Search Tutorial?", "Tutorial?"], "tokens": ["What", "is", "the", "News", "Search", "Tutorial", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1533119219", "fields": {"term": "What are the articles in the tutorial?", "namespace": "open-p", "hash": 1533119219, "terms": ["What are the articles in the tutorial?", "are the articles in the tutorial?", "the articles in the tutorial?", "articles in the tutorial?", "in the tutorial?", "the tutorial?", "tutorial?"], "tokens": ["What", "are", "the", "articles", "in", "the", "tutorial", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1785469841", "fields": {"term": "What is the Models hot swap tutorial?", "namespace": "open-p", "hash": -1785469841, "terms": ["What is the Models hot swap tutorial?", "is the Models hot swap tutorial?", "the Models hot swap tutorial?", "Models hot swap tutorial?", "hot swap tutorial?", "swap tutorial?", "tutorial?"], "tokens": ["What", "is", "the", "Models", "hot", "swap", "tutorial", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-212499133", "fields": {"term": "What does the Models hot swap tutorial cover?", "namespace": "open-p", "hash": -212499133, "terms": ["What does the Models hot swap tutorial cover?", "does the Models hot swap tutorial cover?", "the Models hot swap tutorial cover?", "Models hot swap tutorial cover?", "hot swap tutorial cover?", "swap tutorial cover?", "tutorial cover?", "cover?"], "tokens": ["What", "does", "the", "Models", "hot", "swap", "tutorial", "cover", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1634198700", "fields": {"term": "What are the Text Search tutorials?", "namespace": "open-p", "hash": -1634198700, "terms": ["What are the Text Search tutorials?", "are the Text Search tutorials?", "the Text Search tutorials?", "Text Search tutorials?", "Search tutorials?", "tutorials?"], "tokens": ["What", "are", "the", "Text", "Search", "tutorials", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-881777178", "fields": {"term": "What is Text Search ML?", "namespace": "open-p", "hash": -881777178, "terms": ["What is Text Search ML?", "is Text Search ML?", "Text Search ML?", "Search ML?", "ML?"], "tokens": ["What", "is", "Text", "Search", "ML", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1099019506", "fields": {"term": "What is Text Search Semantic?", "namespace": "open-p", "hash": -1099019506, "terms": ["What is Text Search Semantic?", "is Text Search Semantic?", "Text Search Semantic?", "Search Semantic?", "Semantic?"], "tokens": ["What", "is", "Text", "Search", "Semantic", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-953209368", "fields": {"term": "What is indexing?", "namespace": "open-p", "hash": -953209368, "terms": ["What is indexing?", "is indexing?", "indexing?"], "tokens": ["What", "is", "indexing", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1778983620", "fields": {"term": "What is the default indexing configuration?", "namespace": "open-p", "hash": -1778983620, "terms": ["What is the default indexing configuration?", "is the default indexing configuration?", "the default indexing configuration?", "default indexing configuration?", "indexing configuration?", "configuration?"], "tokens": ["What", "is", "the", "default", "indexing", "configuration", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-473693585", "fields": {"term": "How can custom document processing be added?", "namespace": "open-p", "hash": -473693585, "terms": ["How can custom document processing be added?", "can custom document processing be added?", "custom document processing be added?", "document processing be added?", "processing be added?", "be added?", "added?"], "tokens": ["How", "can", "custom", "document", "processing", "be", "added", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1541461314", "fields": {"term": "What is a routing document?", "namespace": "open-p", "hash": 1541461314, "terms": ["What is a routing document?", "is a routing document?", "a routing document?", "routing document?", "document?"], "tokens": ["What", "is", "a", "routing", "document", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2076523114", "fields": {"term": "What is the purpose of indexing cluster?", "namespace": "open-p", "hash": 2076523114, "terms": ["What is the purpose of indexing cluster?", "is the purpose of indexing cluster?", "the purpose of indexing cluster?", "purpose of indexing cluster?", "of indexing cluster?", "indexing cluster?", "cluster?"], "tokens": ["What", "is", "the", "purpose", "of", "indexing", "cluster", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1606273049", "fields": {"term": "What is the default route used for?", "namespace": "open-p", "hash": -1606273049, "terms": ["What is the default route used for?", "is the default route used for?", "the default route used for?", "default route used for?", "route used for?", "used for?", "for?"], "tokens": ["What", "is", "the", "default", "route", "used", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2047349236", "fields": {"term": "What is indexing chain?", "namespace": "open-p", "hash": -2047349236, "terms": ["What is indexing chain?", "is indexing chain?", "indexing chain?", "chain?"], "tokens": ["What", "is", "indexing", "chain", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::245471862", "fields": {"term": "What is a document selection string?", "namespace": "open-p", "hash": 245471862, "terms": ["What is a document selection string?", "is a document selection string?", "a document selection string?", "document selection string?", "selection string?", "string?"], "tokens": ["What", "is", "a", "document", "selection", "string", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1181878562", "fields": {"term": "When is a document dropped?", "namespace": "open-p", "hash": -1181878562, "terms": ["When is a document dropped?", "is a document dropped?", "a document dropped?", "document dropped?", "dropped?"], "tokens": ["When", "is", "a", "document", "dropped", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2078975467", "fields": {"term": "Where is selection string evaluated?", "namespace": "open-p", "hash": 2078975467, "terms": ["Where is selection string evaluated?", "is selection string evaluated?", "selection string evaluated?", "string evaluated?", "evaluated?"], "tokens": ["Where", "is", "selection", "string", "evaluated", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::570686728", "fields": {"term": "What is DocumentRouteSelector?", "namespace": "open-p", "hash": 570686728, "terms": ["What is DocumentRouteSelector?", "is DocumentRouteSelector?", "DocumentRouteSelector?"], "tokens": ["What", "is", "DocumentRouteSelector", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-465867237", "fields": {"term": "How do you add document processors?", "namespace": "open-p", "hash": -465867237, "terms": ["How do you add document processors?", "do you add document processors?", "you add document processors?", "add document processors?", "document processors?", "processors?"], "tokens": ["How", "do", "you", "add", "document", "processors", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::125981320", "fields": {"term": "What is the format for adding a document processor?", "namespace": "open-p", "hash": 125981320, "terms": ["What is the format for adding a document processor?", "is the format for adding a document processor?", "the format for adding a document processor?", "format for adding a document processor?", "for adding a document processor?", "adding a document processor?", "a document processor?", "document processor?", "processor?"], "tokens": ["What", "is", "the", "format", "for", "adding", "a", "document", "processor", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-665479506", "fields": {"term": "What is the purpose of a document processor?", "namespace": "open-p", "hash": -665479506, "terms": ["What is the purpose of a document processor?", "is the purpose of a document processor?", "the purpose of a document processor?", "purpose of a document processor?", "of a document processor?", "a document processor?", "document processor?", "processor?"], "tokens": ["What", "is", "the", "purpose", "of", "a", "document", "processor", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1687188821", "fields": {"term": "Does adding a document processor affect indexing?", "namespace": "open-p", "hash": -1687188821, "terms": ["Does adding a document processor affect indexing?", "adding a document processor affect indexing?", "a document processor affect indexing?", "document processor affect indexing?", "processor affect indexing?", "affect indexing?", "indexing?"], "tokens": ["Does", "adding", "a", "document", "processor", "affect", "indexing", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1707573594", "fields": {"term": "How to fix indexing cluster", "namespace": "open-p", "hash": 1707573594, "terms": ["How to fix indexing cluster", "to fix indexing cluster", "fix indexing cluster", "indexing cluster", "cluster"], "tokens": ["How", "to", "fix", "indexing", "cluster"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::683443827", "fields": {"term": "What is the alternative to indexing chain 'default'?", "namespace": "open-p", "hash": 683443827, "terms": ["What is the alternative to indexing chain 'default'?", "is the alternative to indexing chain 'default'?", "the alternative to indexing chain 'default'?", "alternative to indexing chain 'default'?", "to indexing chain 'default'?", "indexing chain 'default'?", "chain 'default'?", "'default'?"], "tokens": ["What", "is", "the", "alternative", "to", "indexing", "chain", "'", "default", "'?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1673379955", "fields": {"term": "What is the recommended action when using the default chain?", "namespace": "open-p", "hash": -1673379955, "terms": ["What is the recommended action when using the default chain?", "is the recommended action when using the default chain?", "the recommended action when using the default chain?", "recommended action when using the default chain?", "action when using the default chain?", "when using the default chain?", "using the default chain?", "the default chain?", "default chain?", "chain?"], "tokens": ["What", "is", "the", "recommended", "action", "when", "using", "the", "default", "chain", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-36428882", "fields": {"term": "How to configure multiple container clusters?", "namespace": "open-p", "hash": -36428882, "terms": ["How to configure multiple container clusters?", "to configure multiple container clusters?", "configure multiple container clusters?", "multiple container clusters?", "container clusters?", "clusters?"], "tokens": ["How", "to", "configure", "multiple", "container", "clusters", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::226146686", "fields": {"term": "What are the use cases for multiple container clusters?", "namespace": "open-p", "hash": 226146686, "terms": ["What are the use cases for multiple container clusters?", "are the use cases for multiple container clusters?", "the use cases for multiple container clusters?", "use cases for multiple container clusters?", "cases for multiple container clusters?", "for multiple container clusters?", "multiple container clusters?", "container clusters?", "clusters?"], "tokens": ["What", "are", "the", "use", "cases", "for", "multiple", "container", "clusters", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::897752983", "fields": {"term": "How to avoid network hop when using multiple clusters?", "namespace": "open-p", "hash": 897752983, "terms": ["How to avoid network hop when using multiple clusters?", "to avoid network hop when using multiple clusters?", "avoid network hop when using multiple clusters?", "network hop when using multiple clusters?", "hop when using multiple clusters?", "when using multiple clusters?", "using multiple clusters?", "multiple clusters?", "clusters?"], "tokens": ["How", "to", "avoid", "network", "hop", "when", "using", "multiple", "clusters", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-456067426", "fields": {"term": "How to set up an indexing chain?", "namespace": "open-p", "hash": -456067426, "terms": ["How to set up an indexing chain?", "to set up an indexing chain?", "set up an indexing chain?", "up an indexing chain?", "an indexing chain?", "indexing chain?", "chain?"], "tokens": ["How", "to", "set", "up", "an", "indexing", "chain", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::676886439", "fields": {"term": "What is the best practice for configuring document-processing?", "namespace": "open-p", "hash": 676886439, "terms": ["What is the best practice for configuring document-processing?", "is the best practice for configuring document-processing?", "the best practice for configuring document-processing?", "best practice for configuring document-processing?", "practice for configuring document-processing?", "for configuring document-processing?", "configuring document-processing?", "document-processing?"], "tokens": ["What", "is", "the", "best", "practice", "for", "configuring", "document", "processing", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1952352101", "fields": {"term": "What is dependency injection?", "namespace": "open-p", "hash": 1952352101, "terms": ["What is dependency injection?", "is dependency injection?", "dependency injection?", "injection?"], "tokens": ["What", "is", "dependency", "injection", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::869894183", "fields": {"term": "What is circular dependency?", "namespace": "open-p", "hash": 869894183, "terms": ["What is circular dependency?", "is circular dependency?", "circular dependency?", "dependency?"], "tokens": ["What", "is", "circular", "dependency", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::808547567", "fields": {"term": "What is auto-injection?", "namespace": "open-p", "hash": 808547567, "terms": ["What is auto-injection?", "is auto-injection?", "auto-injection?"], "tokens": ["What", "is", "auto", "injection", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1601728106", "fields": {"term": "What is the purpose of the jdisc-container?", "namespace": "open-p", "hash": 1601728106, "terms": ["What is the purpose of the jdisc-container?", "is the purpose of the jdisc-container?", "the purpose of the jdisc-container?", "purpose of the jdisc-container?", "of the jdisc-container?", "the jdisc-container?", "jdisc-container?"], "tokens": ["What", "is", "the", "purpose", "of", "the", "jdisc", "container", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1862977917", "fields": {"term": "What are the three elements of dependency injection?", "namespace": "open-p", "hash": -1862977917, "terms": ["What are the three elements of dependency injection?", "are the three elements of dependency injection?", "the three elements of dependency injection?", "three elements of dependency injection?", "elements of dependency injection?", "of dependency injection?", "dependency injection?", "injection?"], "tokens": ["What", "are", "the", "three", "elements", "of", "dependency", "injection", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1493376544", "fields": {"term": "What is the difference between auto-injection and Guice modules?", "namespace": "open-p", "hash": 1493376544, "terms": ["What is the difference between auto-injection and Guice modules?", "is the difference between auto-injection and Guice modules?", "the difference between auto-injection and Guice modules?", "difference between auto-injection and Guice modules?", "between auto-injection and Guice modules?", "auto-injection and Guice modules?", "and Guice modules?", "Guice modules?", "modules?"], "tokens": ["What", "is", "the", "difference", "between", "auto", "injection", "and", "Guice", "modules", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-999899808", "fields": {"term": "What is the sample app for dependency injection?", "namespace": "open-p", "hash": -999899808, "terms": ["What is the sample app for dependency injection?", "is the sample app for dependency injection?", "the sample app for dependency injection?", "sample app for dependency injection?", "app for dependency injection?", "for dependency injection?", "dependency injection?", "injection?"], "tokens": ["What", "is", "the", "sample", "app", "for", "dependency", "injection", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1160842976", "fields": {"term": "What is a consumer?", "namespace": "open-p", "hash": 1160842976, "terms": ["What is a consumer?", "is a consumer?", "a consumer?", "consumer?"], "tokens": ["What", "is", "a", "consumer", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1487096727", "fields": {"term": "What are MyComponent dependencies?", "namespace": "open-p", "hash": -1487096727, "terms": ["What are MyComponent dependencies?", "are MyComponent dependencies?", "MyComponent dependencies?", "dependencies?"], "tokens": ["What", "are", "MyComponent", "dependencies", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-63419303", "fields": {"term": "How do you register MyDependency?", "namespace": "open-p", "hash": -63419303, "terms": ["How do you register MyDependency?", "do you register MyDependency?", "you register MyDependency?", "register MyDependency?", "MyDependency?"], "tokens": ["How", "do", "you", "register", "MyDependency", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::486902364", "fields": {"term": "What happens when injecting two components?", "namespace": "open-p", "hash": 486902364, "terms": ["What happens when injecting two components?", "happens when injecting two components?", "when injecting two components?", "injecting two components?", "two components?", "components?"], "tokens": ["What", "happens", "when", "injecting", "two", "components", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1405031590", "fields": {"term": "How can the dependency injection issue be resolved?", "namespace": "open-p", "hash": -1405031590, "terms": ["How can the dependency injection issue be resolved?", "can the dependency injection issue be resolved?", "the dependency injection issue be resolved?", "dependency injection issue be resolved?", "injection issue be resolved?", "issue be resolved?", "be resolved?", "resolved?"], "tokens": ["How", "can", "the", "dependency", "injection", "issue", "be", "resolved", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2006490511", "fields": {"term": "What is a ComponentRegistry?", "namespace": "open-p", "hash": 2006490511, "terms": ["What is a ComponentRegistry?", "is a ComponentRegistry?", "a ComponentRegistry?", "ComponentRegistry?"], "tokens": ["What", "is", "a", "ComponentRegistry", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1075274036", "fields": {"term": "What method is used to look up entries in a ComponentRegistry?", "namespace": "open-p", "hash": 1075274036, "terms": ["What method is used to look up entries in a ComponentRegistry?", "method is used to look up entries in a ComponentRegistry?", "is used to look up entries in a ComponentRegistry?", "used to look up entries in a ComponentRegistry?", "to look up entries in a ComponentRegistry?", "look up entries in a ComponentRegistry?", "up entries in a ComponentRegistry?", "entries in a ComponentRegistry?", "in a ComponentRegistry?", "a ComponentRegistry?", "ComponentRegistry?"], "tokens": ["What", "method", "is", "used", "to", "look", "up", "entries", "in", "a", "ComponentRegistry", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-688649150", "fields": {"term": "What causes class loading problems?", "namespace": "open-p", "hash": -688649150, "terms": ["What causes class loading problems?", "causes class loading problems?", "class loading problems?", "loading problems?", "problems?"], "tokens": ["What", "causes", "class", "loading", "problems", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::642389080", "fields": {"term": "How to remedy class loading problems?", "namespace": "open-p", "hash": 642389080, "terms": ["How to remedy class loading problems?", "to remedy class loading problems?", "remedy class loading problems?", "class loading problems?", "loading problems?", "problems?"], "tokens": ["How", "to", "remedy", "class", "loading", "problems", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2119009714", "fields": {"term": "How to specify the jar file?", "namespace": "open-p", "hash": -2119009714, "terms": ["How to specify the jar file?", "to specify the jar file?", "specify the jar file?", "the jar file?", "jar file?", "file?"], "tokens": ["How", "to", "specify", "the", "jar", "file", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-587553686", "fields": {"term": "What is the purpose of ComponentRegistry?", "namespace": "open-p", "hash": -587553686, "terms": ["What is the purpose of ComponentRegistry?", "is the purpose of ComponentRegistry?", "the purpose of ComponentRegistry?", "purpose of ComponentRegistry?", "of ComponentRegistry?", "ComponentRegistry?"], "tokens": ["What", "is", "the", "purpose", "of", "ComponentRegistry", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1792555900", "fields": {"term": "What does ComponentRegistry do?", "namespace": "open-p", "hash": -1792555900, "terms": ["What does ComponentRegistry do?", "does ComponentRegistry do?", "ComponentRegistry do?", "do?"], "tokens": ["What", "does", "ComponentRegistry", "do", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::997014047", "fields": {"term": "How is ComponentRegistry used?", "namespace": "open-p", "hash": 997014047, "terms": ["How is ComponentRegistry used?", "is ComponentRegistry used?", "ComponentRegistry used?", "used?"], "tokens": ["How", "is", "ComponentRegistry", "used", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::272883557", "fields": {"term": "What is the syntax for creating a new component using ComponentRegistry?", "namespace": "open-p", "hash": 272883557, "terms": ["What is the syntax for creating a new component using ComponentRegistry?", "is the syntax for creating a new component using ComponentRegistry?", "the syntax for creating a new component using ComponentRegistry?", "syntax for creating a new component using ComponentRegistry?", "for creating a new component using ComponentRegistry?", "creating a new component using ComponentRegistry?", "a new component using ComponentRegistry?", "new component using ComponentRegistry?", "component using ComponentRegistry?", "using ComponentRegistry?", "ComponentRegistry?"], "tokens": ["What", "is", "the", "syntax", "for", "creating", "a", "new", "component", "using", "ComponentRegistry", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::755683339", "fields": {"term": "What is the dependency of MyComponent?", "namespace": "open-p", "hash": 755683339, "terms": ["What is the dependency of MyComponent?", "is the dependency of MyComponent?", "the dependency of MyComponent?", "dependency of MyComponent?", "of MyComponent?", "MyComponent?"], "tokens": ["What", "is", "the", "dependency", "of", "MyComponent", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1106097988", "fields": {"term": "What is the class hierarchy for ComponentRegistry?", "namespace": "open-p", "hash": -1106097988, "terms": ["What is the class hierarchy for ComponentRegistry?", "is the class hierarchy for ComponentRegistry?", "the class hierarchy for ComponentRegistry?", "class hierarchy for ComponentRegistry?", "hierarchy for ComponentRegistry?", "for ComponentRegistry?", "ComponentRegistry?"], "tokens": ["What", "is", "the", "class", "hierarchy", "for", "ComponentRegistry", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1729538602", "fields": {"term": "What is a Provider interface?", "namespace": "open-p", "hash": -1729538602, "terms": ["What is a Provider interface?", "is a Provider interface?", "a Provider interface?", "Provider interface?", "interface?"], "tokens": ["What", "is", "a", "Provider", "interface", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1391906745", "fields": {"term": "What is a constructor dependency?", "namespace": "open-p", "hash": -1391906745, "terms": ["What is a constructor dependency?", "is a constructor dependency?", "a constructor dependency?", "constructor dependency?", "dependency?"], "tokens": ["What", "is", "a", "constructor", "dependency", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-997030940", "fields": {"term": "What is the role of deconstruct()?", "namespace": "open-p", "hash": -997030940, "terms": ["What is the role of deconstruct()?", "is the role of deconstruct()?", "the role of deconstruct()?", "role of deconstruct()?", "of deconstruct()?", "deconstruct()?"], "tokens": ["What", "is", "the", "role", "of", "deconstruct", "()?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-151030960", "fields": {"term": "What is a unique instance of dependency class?", "namespace": "open-p", "hash": -151030960, "terms": ["What is a unique instance of dependency class?", "is a unique instance of dependency class?", "a unique instance of dependency class?", "unique instance of dependency class?", "instance of dependency class?", "of dependency class?", "dependency class?", "class?"], "tokens": ["What", "is", "a", "unique", "instance", "of", "dependency", "class", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1490183639", "fields": {"term": "What are the situations when a component cannot be directly injected?", "namespace": "open-p", "hash": -1490183639, "terms": ["What are the situations when a component cannot be directly injected?", "are the situations when a component cannot be directly injected?", "the situations when a component cannot be directly injected?", "situations when a component cannot be directly injected?", "when a component cannot be directly injected?", "a component cannot be directly injected?", "component cannot be directly injected?", "cannot be directly injected?", "be directly injected?", "directly injected?", "injected?"], "tokens": ["What", "are", "the", "situations", "when", "a", "component", "cannot", "be", "directly", "injected", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::64537534", "fields": {"term": "What is the Data Access API?", "namespace": "open-p", "hash": 64537534, "terms": ["What is the Data Access API?", "is the Data Access API?", "the Data Access API?", "Data Access API?", "Access API?", "API?"], "tokens": ["What", "is", "the", "Data", "Access", "API", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::177102443", "fields": {"term": "What structured data can it access?", "namespace": "open-p", "hash": 177102443, "terms": ["What structured data can it access?", "structured data can it access?", "data can it access?", "can it access?", "it access?", "access?"], "tokens": ["What", "structured", "data", "can", "it", "access", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1637768751", "fields": {"term": "How does the API access arrays?", "namespace": "open-p", "hash": -1637768751, "terms": ["How does the API access arrays?", "does the API access arrays?", "the API access arrays?", "API access arrays?", "access arrays?", "arrays?"], "tokens": ["How", "does", "the", "API", "access", "arrays", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-969155312", "fields": {"term": "What is Slime?", "namespace": "open-p", "hash": -969155312, "terms": ["What is Slime?", "is Slime?", "Slime?"], "tokens": ["What", "is", "Slime", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1528204597", "fields": {"term": "What is a structured data field?", "namespace": "open-p", "hash": 1528204597, "terms": ["What is a structured data field?", "is a structured data field?", "a structured data field?", "structured data field?", "data field?", "field?"], "tokens": ["What", "is", "a", "structured", "data", "field", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1972254932", "fields": {"term": "How do you create a struct using Slime?", "namespace": "open-p", "hash": -1972254932, "terms": ["How do you create a struct using Slime?", "do you create a struct using Slime?", "you create a struct using Slime?", "create a struct using Slime?", "a struct using Slime?", "struct using Slime?", "using Slime?", "Slime?"], "tokens": ["How", "do", "you", "create", "a", "struct", "using", "Slime", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::68011292", "fields": {"term": "How do you create an array using Slime?", "namespace": "open-p", "hash": 68011292, "terms": ["How do you create an array using Slime?", "do you create an array using Slime?", "you create an array using Slime?", "create an array using Slime?", "an array using Slime?", "array using Slime?", "using Slime?", "Slime?"], "tokens": ["How", "do", "you", "create", "an", "array", "using", "Slime", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1291437600", "fields": {"term": "Can Slime create nested arrays and objects?", "namespace": "open-p", "hash": -1291437600, "terms": ["Can Slime create nested arrays and objects?", "Slime create nested arrays and objects?", "create nested arrays and objects?", "nested arrays and objects?", "arrays and objects?", "and objects?", "objects?"], "tokens": ["Can", "Slime", "create", "nested", "arrays", "and", "objects", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1973430314", "fields": {"term": "How do you convert a JSON string to a Slime structure?", "namespace": "open-p", "hash": -1973430314, "terms": ["How do you convert a JSON string to a Slime structure?", "do you convert a JSON string to a Slime structure?", "you convert a JSON string to a Slime structure?", "convert a JSON string to a Slime structure?", "a JSON string to a Slime structure?", "JSON string to a Slime structure?", "string to a Slime structure?", "to a Slime structure?", "a Slime structure?", "Slime structure?", "structure?"], "tokens": ["How", "do", "you", "convert", "a", "JSON", "string", "to", "a", "Slime", "structure", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2080745363", "fields": {"term": "What does the reference describe?", "namespace": "open-p", "hash": 2080745363, "terms": ["What does the reference describe?", "does the reference describe?", "the reference describe?", "reference describe?", "describe?"], "tokens": ["What", "does", "the", "reference", "describe", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1977690570", "fields": {"term": "What file format does Vespa use?", "namespace": "open-p", "hash": -1977690570, "terms": ["What file format does Vespa use?", "file format does Vespa use?", "format does Vespa use?", "does Vespa use?", "Vespa use?", "use?"], "tokens": ["What", "file", "format", "does", "Vespa", "use", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1111372537", "fields": {"term": "What is a configuration file?", "namespace": "open-p", "hash": -1111372537, "terms": ["What is a configuration file?", "is a configuration file?", "a configuration file?", "configuration file?", "file?"], "tokens": ["What", "is", "a", "configuration", "file", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::2046250299", "fields": {"term": "What is the format of a configuration file?", "namespace": "open-p", "hash": 2046250299, "terms": ["What is the format of a configuration file?", "is the format of a configuration file?", "the format of a configuration file?", "format of a configuration file?", "of a configuration file?", "a configuration file?", "configuration file?", "file?"], "tokens": ["What", "is", "the", "format", "of", "a", "configuration", "file", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2064515429", "fields": {"term": "How do you set an integer in a configuration file?", "namespace": "open-p", "hash": 2064515429, "terms": ["How do you set an integer in a configuration file?", "do you set an integer in a configuration file?", "you set an integer in a configuration file?", "set an integer in a configuration file?", "an integer in a configuration file?", "integer in a configuration file?", "in a configuration file?", "a configuration file?", "configuration file?", "file?"], "tokens": ["How", "do", "you", "set", "an", "integer", "in", "a", "configuration", "file", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1416709894", "fields": {"term": "How do you set a string in a configuration file?", "namespace": "open-p", "hash": -1416709894, "terms": ["How do you set a string in a configuration file?", "do you set a string in a configuration file?", "you set a string in a configuration file?", "set a string in a configuration file?", "a string in a configuration file?", "string in a configuration file?", "in a configuration file?", "a configuration file?", "configuration file?", "file?"], "tokens": ["How", "do", "you", "set", "a", "string", "in", "a", "configuration", "file", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::270038867", "fields": {"term": "Are strings always in quotation marks in a configuration file?", "namespace": "open-p", "hash": 270038867, "terms": ["Are strings always in quotation marks in a configuration file?", "strings always in quotation marks in a configuration file?", "always in quotation marks in a configuration file?", "in quotation marks in a configuration file?", "quotation marks in a configuration file?", "marks in a configuration file?", "in a configuration file?", "a configuration file?", "configuration file?", "file?"], "tokens": ["Are", "strings", "always", "in", "quotation", "marks", "in", "a", "configuration", "file", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::112769835", "fields": {"term": "How are arrays structured?", "namespace": "open-p", "hash": 112769835, "terms": ["How are arrays structured?", "are arrays structured?", "arrays structured?", "structured?"], "tokens": ["How", "are", "arrays", "structured", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::189332089", "fields": {"term": "How do you declare an array?", "namespace": "open-p", "hash": 189332089, "terms": ["How do you declare an array?", "do you declare an array?", "you declare an array?", "declare an array?", "an array?", "array?"], "tokens": ["How", "do", "you", "declare", "an", "array", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::338828514", "fields": {"term": "What is the separator for child arrays?", "namespace": "open-p", "hash": 338828514, "terms": ["What is the separator for child arrays?", "is the separator for child arrays?", "the separator for child arrays?", "separator for child arrays?", "for child arrays?", "child arrays?", "arrays?"], "tokens": ["What", "is", "the", "separator", "for", "child", "arrays", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-432468503", "fields": {"term": "What is a table structure?", "namespace": "open-p", "hash": -432468503, "terms": ["What is a table structure?", "is a table structure?", "a table structure?", "table structure?", "structure?"], "tokens": ["What", "is", "a", "table", "structure", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2127781220", "fields": {"term": "What is a row in a table?", "namespace": "open-p", "hash": 2127781220, "terms": ["What is a row in a table?", "is a row in a table?", "a row in a table?", "row in a table?", "in a table?", "a table?", "table?"], "tokens": ["What", "is", "a", "row", "in", "a", "table", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-396581543", "fields": {"term": "What is a column in a table?", "namespace": "open-p", "hash": -396581543, "terms": ["What is a column in a table?", "is a column in a table?", "a column in a table?", "column in a table?", "in a table?", "a table?", "table?"], "tokens": ["What", "is", "a", "column", "in", "a", "table", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-578580357", "fields": {"term": "How do you set values in an array?", "namespace": "open-p", "hash": -578580357, "terms": ["How do you set values in an array?", "do you set values in an array?", "you set values in an array?", "set values in an array?", "values in an array?", "in an array?", "an array?", "array?"], "tokens": ["How", "do", "you", "set", "values", "in", "an", "array", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::779318358", "fields": {"term": "Who is this documentation for?", "namespace": "open-p", "hash": 779318358, "terms": ["Who is this documentation for?", "is this documentation for?", "this documentation for?", "documentation for?", "for?"], "tokens": ["Who", "is", "this", "documentation", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1278820640", "fields": {"term": "What is the purpose of italic font?", "namespace": "open-p", "hash": -1278820640, "terms": ["What is the purpose of italic font?", "is the purpose of italic font?", "the purpose of italic font?", "purpose of italic font?", "of italic font?", "italic font?", "font?"], "tokens": ["What", "is", "the", "purpose", "of", "italic", "font", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1450710701", "fields": {"term": "What programming language is the documentation for?", "namespace": "open-p", "hash": -1450710701, "terms": ["What programming language is the documentation for?", "programming language is the documentation for?", "language is the documentation for?", "is the documentation for?", "the documentation for?", "documentation for?", "for?"], "tokens": ["What", "programming", "language", "is", "the", "documentation", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1064566807", "fields": {"term": "What is covered in the Vespa documentation?", "namespace": "open-p", "hash": 1064566807, "terms": ["What is covered in the Vespa documentation?", "is covered in the Vespa documentation?", "covered in the Vespa documentation?", "in the Vespa documentation?", "the Vespa documentation?", "Vespa documentation?", "documentation?"], "tokens": ["What", "is", "covered", "in", "the", "Vespa", "documentation", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1582816689", "fields": {"term": "What is LightGBM?", "namespace": "open-p", "hash": 1582816689, "terms": ["What is LightGBM?", "is LightGBM?", "LightGBM?"], "tokens": ["What", "is", "LightGBM", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::781897905", "fields": {"term": "How is LightGBM different from XGBoost?", "namespace": "open-p", "hash": 781897905, "terms": ["How is LightGBM different from XGBoost?", "is LightGBM different from XGBoost?", "LightGBM different from XGBoost?", "different from XGBoost?", "from XGBoost?", "XGBoost?"], "tokens": ["How", "is", "LightGBM", "different", "from", "XGBoost", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2071381802", "fields": {"term": "What is a defining feature of LightGBM?", "namespace": "open-p", "hash": -2071381802, "terms": ["What is a defining feature of LightGBM?", "is a defining feature of LightGBM?", "a defining feature of LightGBM?", "defining feature of LightGBM?", "feature of LightGBM?", "of LightGBM?", "LightGBM?"], "tokens": ["What", "is", "a", "defining", "feature", "of", "LightGBM", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-443638620", "fields": {"term": "Does LightGBM support categorical features?", "namespace": "open-p", "hash": -443638620, "terms": ["Does LightGBM support categorical features?", "LightGBM support categorical features?", "support categorical features?", "categorical features?", "features?"], "tokens": ["Does", "LightGBM", "support", "categorical", "features", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2021266293", "fields": {"term": "Can Vespa import models trained with LightGBM?", "namespace": "open-p", "hash": -2021266293, "terms": ["Can Vespa import models trained with LightGBM?", "Vespa import models trained with LightGBM?", "import models trained with LightGBM?", "models trained with LightGBM?", "trained with LightGBM?", "with LightGBM?", "LightGBM?"], "tokens": ["Can", "Vespa", "import", "models", "trained", "with", "LightGBM", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1515251362", "fields": {"term": "What is LightGBM's dump_model?", "namespace": "open-p", "hash": -1515251362, "terms": ["What is LightGBM's dump_model?", "is LightGBM's dump_model?", "LightGBM's dump_model?", "dump_model?"], "tokens": ["What", "is", "LightGBM", "'", "s", "dump_model", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1187613481", "fields": {"term": "What data is dumped by dump_model?", "namespace": "open-p", "hash": -1187613481, "terms": ["What data is dumped by dump_model?", "data is dumped by dump_model?", "is dumped by dump_model?", "dumped by dump_model?", "by dump_model?", "dump_model?"], "tokens": ["What", "data", "is", "dumped", "by", "dump_model", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::297538559", "fields": {"term": "How can a LightGBM model be trained for use in Vespa?", "namespace": "open-p", "hash": 297538559, "terms": ["How can a LightGBM model be trained for use in Vespa?", "can a LightGBM model be trained for use in Vespa?", "a LightGBM model be trained for use in Vespa?", "LightGBM model be trained for use in Vespa?", "model be trained for use in Vespa?", "be trained for use in Vespa?", "trained for use in Vespa?", "for use in Vespa?", "use in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["How", "can", "a", "LightGBM", "model", "be", "trained", "for", "use", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1355038199", "fields": {"term": "What is pd.DataFrame()", "namespace": "open-p", "hash": -1355038199, "terms": ["What is pd.DataFrame()", "is pd.DataFrame()", "pd.DataFrame()"], "tokens": ["What", "is", "pd", "DataFrame", "()"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1941349305", "fields": {"term": "What is lgb.Dataset()", "namespace": "open-p", "hash": 1941349305, "terms": ["What is lgb.Dataset()", "is lgb.Dataset()", "lgb.Dataset()"], "tokens": ["What", "is", "lgb", "Dataset", "()"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1458639239", "fields": {"term": "What is the 'objective' parameter?", "namespace": "open-p", "hash": -1458639239, "terms": ["What is the 'objective' parameter?", "is the 'objective' parameter?", "the 'objective' parameter?", "'objective' parameter?", "parameter?"], "tokens": ["What", "is", "the", "'", "objective", "'", "parameter", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::821398690", "fields": {"term": "What is lightgbm_model.json?", "namespace": "open-p", "hash": 821398690, "terms": ["What is lightgbm_model.json?", "is lightgbm_model.json?", "lightgbm_model.json?"], "tokens": ["What", "is", "lightgbm_model", "json", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1765966177", "fields": {"term": "Can lightgbm_model.json be imported into Vespa?", "namespace": "open-p", "hash": -1765966177, "terms": ["Can lightgbm_model.json be imported into Vespa?", "lightgbm_model.json be imported into Vespa?", "be imported into Vespa?", "imported into Vespa?", "into Vespa?", "Vespa?"], "tokens": ["Can", "lightgbm_model", "json", "be", "imported", "into", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1143929905", "fields": {"term": "How do I import LightGBM model?", "namespace": "open-p", "hash": -1143929905, "terms": ["How do I import LightGBM model?", "do I import LightGBM model?", "I import LightGBM model?", "import LightGBM model?", "LightGBM model?", "model?"], "tokens": ["How", "do", "I", "import", "LightGBM", "model", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-639457905", "fields": {"term": "Can the application package have multiple models?", "namespace": "open-p", "hash": -639457905, "terms": ["Can the application package have multiple models?", "the application package have multiple models?", "application package have multiple models?", "package have multiple models?", "have multiple models?", "multiple models?", "models?"], "tokens": ["Can", "the", "application", "package", "have", "multiple", "models", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1243968379", "fields": {"term": "Is the model available for ranking?", "namespace": "open-p", "hash": 1243968379, "terms": ["Is the model available for ranking?", "the model available for ranking?", "model available for ranking?", "available for ranking?", "for ranking?", "ranking?"], "tokens": ["Is", "the", "model", "available", "for", "ranking", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::370155100", "fields": {"term": "What is stateless model evaluation?", "namespace": "open-p", "hash": 370155100, "terms": ["What is stateless model evaluation?", "is stateless model evaluation?", "stateless model evaluation?", "model evaluation?", "evaluation?"], "tokens": ["What", "is", "stateless", "model", "evaluation", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::451731221", "fields": {"term": "How can I do stateless model evaluation?", "namespace": "open-p", "hash": 451731221, "terms": ["How can I do stateless model evaluation?", "can I do stateless model evaluation?", "I do stateless model evaluation?", "do stateless model evaluation?", "stateless model evaluation?", "model evaluation?", "evaluation?"], "tokens": ["How", "can", "I", "do", "stateless", "model", "evaluation", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1368103373", "fields": {"term": "How does lightgbm work?", "namespace": "open-p", "hash": 1368103373, "terms": ["How does lightgbm work?", "does lightgbm work?", "lightgbm work?", "work?"], "tokens": ["How", "does", "lightgbm", "work", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1911090858", "fields": {"term": "What are the feature names?", "namespace": "open-p", "hash": -1911090858, "terms": ["What are the feature names?", "are the feature names?", "the feature names?", "feature names?", "names?"], "tokens": ["What", "are", "the", "feature", "names", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-676148457", "fields": {"term": "How to map lightgbm features?", "namespace": "open-p", "hash": -676148457, "terms": ["How to map lightgbm features?", "to map lightgbm features?", "map lightgbm features?", "lightgbm features?", "features?"], "tokens": ["How", "to", "map", "lightgbm", "features", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1835836189", "fields": {"term": "How to control the number of documents exposed?", "namespace": "open-p", "hash": -1835836189, "terms": ["How to control the number of documents exposed?", "to control the number of documents exposed?", "control the number of documents exposed?", "the number of documents exposed?", "number of documents exposed?", "of documents exposed?", "documents exposed?", "exposed?"], "tokens": ["How", "to", "control", "the", "number", "of", "documents", "exposed", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1855346732", "fields": {"term": "What objectives does Vespa support?", "namespace": "open-p", "hash": -1855346732, "terms": ["What objectives does Vespa support?", "objectives does Vespa support?", "does Vespa support?", "Vespa support?", "support?"], "tokens": ["What", "objectives", "does", "Vespa", "support", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-545673233", "fields": {"term": "Can LightGBM handle categorical features?", "namespace": "open-p", "hash": -545673233, "terms": ["Can LightGBM handle categorical features?", "LightGBM handle categorical features?", "handle categorical features?", "categorical features?", "features?"], "tokens": ["Can", "LightGBM", "handle", "categorical", "features", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2124024892", "fields": {"term": "How does Vespa handle categorical features?", "namespace": "open-p", "hash": -2124024892, "terms": ["How does Vespa handle categorical features?", "does Vespa handle categorical features?", "Vespa handle categorical features?", "handle categorical features?", "categorical features?", "features?"], "tokens": ["How", "does", "Vespa", "handle", "categorical", "features", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::705961950", "fields": {"term": "What is lost when dumping LightGBM models?", "namespace": "open-p", "hash": 705961950, "terms": ["What is lost when dumping LightGBM models?", "is lost when dumping LightGBM models?", "lost when dumping LightGBM models?", "when dumping LightGBM models?", "dumping LightGBM models?", "LightGBM models?", "models?"], "tokens": ["What", "is", "lost", "when", "dumping", "LightGBM", "models", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-546142322", "fields": {"term": "How to scrape features for training?", "namespace": "open-p", "hash": -546142322, "terms": ["How to scrape features for training?", "to scrape features for training?", "scrape features for training?", "features for training?", "for training?", "training?"], "tokens": ["How", "to", "scrape", "features", "for", "training", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1034797563", "fields": {"term": "Why is precision important for reading Vespa features?", "namespace": "open-p", "hash": -1034797563, "terms": ["Why is precision important for reading Vespa features?", "is precision important for reading Vespa features?", "precision important for reading Vespa features?", "important for reading Vespa features?", "for reading Vespa features?", "reading Vespa features?", "Vespa features?", "features?"], "tokens": ["Why", "is", "precision", "important", "for", "reading", "Vespa", "features", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2087729692", "fields": {"term": "Why might matching features differ in a distributed setting?", "namespace": "open-p", "hash": 2087729692, "terms": ["Why might matching features differ in a distributed setting?", "might matching features differ in a distributed setting?", "matching features differ in a distributed setting?", "features differ in a distributed setting?", "differ in a distributed setting?", "in a distributed setting?", "a distributed setting?", "distributed setting?", "setting?"], "tokens": ["Why", "might", "matching", "features", "differ", "in", "a", "distributed", "setting", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1234606365", "fields": {"term": "What is term(n).significance?", "namespace": "open-p", "hash": 1234606365, "terms": ["What is term(n).significance?", "is term(n).significance?", "term(n).significance?"], "tokens": ["What", "is", "term", "(", "n", ")", "significance", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::569803547", "fields": {"term": "Who sets term(n).significance?", "namespace": "open-p", "hash": 569803547, "terms": ["Who sets term(n).significance?", "sets term(n).significance?", "term(n).significance?"], "tokens": ["Who", "sets", "term", "(", "n", ")", "significance", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1603695587", "fields": {"term": "How can dumping features help debug differences in offline and online features?", "namespace": "open-p", "hash": -1603695587, "terms": ["How can dumping features help debug differences in offline and online features?", "can dumping features help debug differences in offline and online features?", "dumping features help debug differences in offline and online features?", "features help debug differences in offline and online features?", "help debug differences in offline and online features?", "debug differences in offline and online features?", "differences in offline and online features?", "in offline and online features?", "offline and online features?", "and online features?", "online features?", "features?"], "tokens": ["How", "can", "dumping", "features", "help", "debug", "differences", "in", "offline", "and", "online", "features", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1140062298", "fields": {"term": "What is linguistic processing in Vespa?", "namespace": "open-p", "hash": -1140062298, "terms": ["What is linguistic processing in Vespa?", "is linguistic processing in Vespa?", "linguistic processing in Vespa?", "processing in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["What", "is", "linguistic", "processing", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::6459394", "fields": {"term": "What fields are processed by linguistic processing?", "namespace": "open-p", "hash": 6459394, "terms": ["What fields are processed by linguistic processing?", "fields are processed by linguistic processing?", "are processed by linguistic processing?", "processed by linguistic processing?", "by linguistic processing?", "linguistic processing?", "processing?"], "tokens": ["What", "fields", "are", "processed", "by", "linguistic", "processing", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1065793764", "fields": {"term": "What is the default linguistics implementation used by Vespa?", "namespace": "open-p", "hash": -1065793764, "terms": ["What is the default linguistics implementation used by Vespa?", "is the default linguistics implementation used by Vespa?", "the default linguistics implementation used by Vespa?", "default linguistics implementation used by Vespa?", "linguistics implementation used by Vespa?", "implementation used by Vespa?", "used by Vespa?", "by Vespa?", "Vespa?"], "tokens": ["What", "is", "the", "default", "linguistics", "implementation", "used", "by", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1059374874", "fields": {"term": "What is the goal of linguistic processing in Vespa?", "namespace": "open-p", "hash": -1059374874, "terms": ["What is the goal of linguistic processing in Vespa?", "is the goal of linguistic processing in Vespa?", "the goal of linguistic processing in Vespa?", "goal of linguistic processing in Vespa?", "of linguistic processing in Vespa?", "linguistic processing in Vespa?", "processing in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["What", "is", "the", "goal", "of", "linguistic", "processing", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1567128182", "fields": {"term": "What are the operations in linguistic processing?", "namespace": "open-p", "hash": -1567128182, "terms": ["What are the operations in linguistic processing?", "are the operations in linguistic processing?", "the operations in linguistic processing?", "operations in linguistic processing?", "in linguistic processing?", "linguistic processing?", "processing?"], "tokens": ["What", "are", "the", "operations", "in", "linguistic", "processing", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1725402374", "fields": {"term": "What are the supported languages in OpenNLP?", "namespace": "open-p", "hash": -1725402374, "terms": ["What are the supported languages in OpenNLP?", "are the supported languages in OpenNLP?", "the supported languages in OpenNLP?", "supported languages in OpenNLP?", "languages in OpenNLP?", "in OpenNLP?", "OpenNLP?"], "tokens": ["What", "are", "the", "supported", "languages", "in", "OpenNLP", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-409773152", "fields": {"term": "How can transforms per query term be enabled or disabled in the schema?", "namespace": "open-p", "hash": -409773152, "terms": ["How can transforms per query term be enabled or disabled in the schema?", "can transforms per query term be enabled or disabled in the schema?", "transforms per query term be enabled or disabled in the schema?", "per query term be enabled or disabled in the schema?", "query term be enabled or disabled in the schema?", "term be enabled or disabled in the schema?", "be enabled or disabled in the schema?", "enabled or disabled in the schema?", "or disabled in the schema?", "disabled in the schema?", "in the schema?", "the schema?", "schema?"], "tokens": ["How", "can", "transforms", "per", "query", "term", "be", "enabled", "or", "disabled", "in", "the", "schema", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1914735284", "fields": {"term": "What is com.yahoo.language.Linguistics?", "namespace": "open-p", "hash": -1914735284, "terms": ["What is com.yahoo.language.Linguistics?", "is com.yahoo.language.Linguistics?", "com.yahoo.language.Linguistics?"], "tokens": ["What", "is", "com", "yahoo", "language", "Linguistics", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1919017817", "fields": {"term": "What is SimpleLinguistics?", "namespace": "open-p", "hash": 1919017817, "terms": ["What is SimpleLinguistics?", "is SimpleLinguistics?", "SimpleLinguistics?"], "tokens": ["What", "is", "SimpleLinguistics", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1718242815", "fields": {"term": "What linguistics module should be provided?", "namespace": "open-p", "hash": 1718242815, "terms": ["What linguistics module should be provided?", "linguistics module should be provided?", "module should be provided?", "should be provided?", "be provided?", "provided?"], "tokens": ["What", "linguistics", "module", "should", "be", "provided", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-595140677", "fields": {"term": "How to configure SimpleLinguistics?", "namespace": "open-p", "hash": -595140677, "terms": ["How to configure SimpleLinguistics?", "to configure SimpleLinguistics?", "configure SimpleLinguistics?", "SimpleLinguistics?"], "tokens": ["How", "to", "configure", "SimpleLinguistics", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2043267812", "fields": {"term": "What is the impact of updating the linguistics component of a live system?", "namespace": "open-p", "hash": 2043267812, "terms": ["What is the impact of updating the linguistics component of a live system?", "is the impact of updating the linguistics component of a live system?", "the impact of updating the linguistics component of a live system?", "impact of updating the linguistics component of a live system?", "of updating the linguistics component of a live system?", "updating the linguistics component of a live system?", "the linguistics component of a live system?", "linguistics component of a live system?", "component of a live system?", "of a live system?", "a live system?", "live system?", "system?"], "tokens": ["What", "is", "the", "impact", "of", "updating", "the", "linguistics", "component", "of", "a", "live", "system", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1007147776", "fields": {"term": "How does Vespa detect language?", "namespace": "open-p", "hash": -1007147776, "terms": ["How does Vespa detect language?", "does Vespa detect language?", "Vespa detect language?", "detect language?", "language?"], "tokens": ["How", "does", "Vespa", "detect", "language", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1019627367", "fields": {"term": "What affects document recall?", "namespace": "open-p", "hash": -1019627367, "terms": ["What affects document recall?", "affects document recall?", "document recall?", "recall?"], "tokens": ["What", "affects", "document", "recall", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::144657280", "fields": {"term": "Can Vespa detect languages automatically?", "namespace": "open-p", "hash": 144657280, "terms": ["Can Vespa detect languages automatically?", "Vespa detect languages automatically?", "detect languages automatically?", "languages automatically?", "automatically?"], "tokens": ["Can", "Vespa", "detect", "languages", "automatically", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1324807369", "fields": {"term": "What is set_language used for?", "namespace": "open-p", "hash": 1324807369, "terms": ["What is set_language used for?", "is set_language used for?", "set_language used for?", "used for?", "for?"], "tokens": ["What", "is", "set_language", "used", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-988324518", "fields": {"term": "How does Vespa determine language?", "namespace": "open-p", "hash": -988324518, "terms": ["How does Vespa determine language?", "does Vespa determine language?", "Vespa determine language?", "determine language?", "language?"], "tokens": ["How", "does", "Vespa", "determine", "language", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1334103299", "fields": {"term": "How can fixed languages be set?", "namespace": "open-p", "hash": 1334103299, "terms": ["How can fixed languages be set?", "can fixed languages be set?", "fixed languages be set?", "languages be set?", "be set?", "set?"], "tokens": ["How", "can", "fixed", "languages", "be", "set", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1967239515", "fields": {"term": "Can a document have multiple languages?", "namespace": "open-p", "hash": -1967239515, "terms": ["Can a document have multiple languages?", "a document have multiple languages?", "document have multiple languages?", "have multiple languages?", "multiple languages?", "languages?"], "tokens": ["Can", "a", "document", "have", "multiple", "languages", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::312234091", "fields": {"term": "What happens when a language is not set?", "namespace": "open-p", "hash": 312234091, "terms": ["What happens when a language is not set?", "happens when a language is not set?", "when a language is not set?", "a language is not set?", "language is not set?", "is not set?", "not set?", "set?"], "tokens": ["What", "happens", "when", "a", "language", "is", "not", "set", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1307591705", "fields": {"term": "What is the order of indexing scripts?", "namespace": "open-p", "hash": -1307591705, "terms": ["What is the order of indexing scripts?", "is the order of indexing scripts?", "the order of indexing scripts?", "order of indexing scripts?", "of indexing scripts?", "indexing scripts?", "scripts?"], "tokens": ["What", "is", "the", "order", "of", "indexing", "scripts", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1250482773", "fields": {"term": "How is language set per field?", "namespace": "open-p", "hash": 1250482773, "terms": ["How is language set per field?", "is language set per field?", "language set per field?", "set per field?", "per field?", "field?"], "tokens": ["How", "is", "language", "set", "per", "field", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-90753620", "fields": {"term": "What is language detection?", "namespace": "open-p", "hash": -90753620, "terms": ["What is language detection?", "is language detection?", "language detection?", "detection?"], "tokens": ["What", "is", "language", "detection", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::748929485", "fields": {"term": "How is language detection performed?", "namespace": "open-p", "hash": 748929485, "terms": ["How is language detection performed?", "is language detection performed?", "language detection performed?", "detection performed?", "performed?"], "tokens": ["How", "is", "language", "detection", "performed", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1388022754", "fields": {"term": "Is language detection per document or per field?", "namespace": "open-p", "hash": -1388022754, "terms": ["Is language detection per document or per field?", "language detection per document or per field?", "detection per document or per field?", "per document or per field?", "document or per field?", "or per field?", "per field?", "field?"], "tokens": ["Is", "language", "detection", "per", "document", "or", "per", "field", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2018900322", "fields": {"term": "What happens when a field has unknown language?", "namespace": "open-p", "hash": -2018900322, "terms": ["What happens when a field has unknown language?", "happens when a field has unknown language?", "when a field has unknown language?", "a field has unknown language?", "field has unknown language?", "has unknown language?", "unknown language?", "language?"], "tokens": ["What", "happens", "when", "a", "field", "has", "unknown", "language", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::980442726", "fields": {"term": "What is query language detection?", "namespace": "open-p", "hash": 980442726, "terms": ["What is query language detection?", "is query language detection?", "query language detection?", "language detection?", "detection?"], "tokens": ["What", "is", "query", "language", "detection", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::836349792", "fields": {"term": "What is the default language for fields with little text?", "namespace": "open-p", "hash": 836349792, "terms": ["What is the default language for fields with little text?", "is the default language for fields with little text?", "the default language for fields with little text?", "default language for fields with little text?", "language for fields with little text?", "for fields with little text?", "fields with little text?", "with little text?", "little text?", "text?"], "tokens": ["What", "is", "the", "default", "language", "for", "fields", "with", "little", "text", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1240981413", "fields": {"term": "Where can I find more information about language detection?", "namespace": "open-p", "hash": 1240981413, "terms": ["Where can I find more information about language detection?", "can I find more information about language detection?", "I find more information about language detection?", "find more information about language detection?", "more information about language detection?", "information about language detection?", "about language detection?", "language detection?", "detection?"], "tokens": ["Where", "can", "I", "find", "more", "information", "about", "language", "detection", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2087254908", "fields": {"term": "What is an indexed string field?", "namespace": "open-p", "hash": -2087254908, "terms": ["What is an indexed string field?", "is an indexed string field?", "an indexed string field?", "indexed string field?", "string field?", "field?"], "tokens": ["What", "is", "an", "indexed", "string", "field", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-796327852", "fields": {"term": "What is symmetric tokenization?", "namespace": "open-p", "hash": -796327852, "terms": ["What is symmetric tokenization?", "is symmetric tokenization?", "symmetric tokenization?", "tokenization?"], "tokens": ["What", "is", "symmetric", "tokenization", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1101441319", "fields": {"term": "How does the query parser work?", "namespace": "open-p", "hash": -1101441319, "terms": ["How does the query parser work?", "does the query parser work?", "the query parser work?", "query parser work?", "parser work?", "work?"], "tokens": ["How", "does", "the", "query", "parser", "work", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::974559655", "fields": {"term": "What controls the language state?", "namespace": "open-p", "hash": 974559655, "terms": ["What controls the language state?", "controls the language state?", "the language state?", "language state?", "state?"], "tokens": ["What", "controls", "the", "language", "state", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::802503812", "fields": {"term": "Can multiple languages be in one index?", "namespace": "open-p", "hash": 802503812, "terms": ["Can multiple languages be in one index?", "multiple languages be in one index?", "languages be in one index?", "be in one index?", "in one index?", "one index?", "index?"], "tokens": ["Can", "multiple", "languages", "be", "in", "one", "index", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-333138197", "fields": {"term": "How to store the language of a document?", "namespace": "open-p", "hash": -333138197, "terms": ["How to store the language of a document?", "to store the language of a document?", "store the language of a document?", "the language of a document?", "language of a document?", "of a document?", "a document?", "document?"], "tokens": ["How", "to", "store", "the", "language", "of", "a", "document", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-621507056", "fields": {"term": "Why apply a filter at query-time?", "namespace": "open-p", "hash": -621507056, "terms": ["Why apply a filter at query-time?", "apply a filter at query-time?", "a filter at query-time?", "filter at query-time?", "at query-time?", "query-time?"], "tokens": ["Why", "apply", "a", "filter", "at", "query", "time", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-141581786", "fields": {"term": "What does the language parameter affect?", "namespace": "open-p", "hash": -141581786, "terms": ["What does the language parameter affect?", "does the language parameter affect?", "the language parameter affect?", "language parameter affect?", "parameter affect?", "affect?"], "tokens": ["What", "does", "the", "language", "parameter", "affect", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::198077798", "fields": {"term": "What is language parameter in Vespa?", "namespace": "open-p", "hash": 198077798, "terms": ["What is language parameter in Vespa?", "is language parameter in Vespa?", "language parameter in Vespa?", "parameter in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["What", "is", "language", "parameter", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1798128127", "fields": {"term": "How does language detector process queries?", "namespace": "open-p", "hash": -1798128127, "terms": ["How does language detector process queries?", "does language detector process queries?", "language detector process queries?", "detector process queries?", "process queries?", "queries?"], "tokens": ["How", "does", "language", "detector", "process", "queries", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2062958166", "fields": {"term": "What is the confidence cutoff for language detection?", "namespace": "open-p", "hash": 2062958166, "terms": ["What is the confidence cutoff for language detection?", "is the confidence cutoff for language detection?", "the confidence cutoff for language detection?", "confidence cutoff for language detection?", "cutoff for language detection?", "for language detection?", "language detection?", "detection?"], "tokens": ["What", "is", "the", "confidence", "cutoff", "for", "language", "detection", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1748026203", "fields": {"term": "What are the approaches for retrieval?", "namespace": "open-p", "hash": 1748026203, "terms": ["What are the approaches for retrieval?", "are the approaches for retrieval?", "the approaches for retrieval?", "approaches for retrieval?", "for retrieval?", "retrieval?"], "tokens": ["What", "are", "the", "approaches", "for", "retrieval", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-10978867", "fields": {"term": "What is the benefit of equiv?", "namespace": "open-p", "hash": -10978867, "terms": ["What is the benefit of equiv?", "is the benefit of equiv?", "the benefit of equiv?", "benefit of equiv?", "of equiv?", "equiv?"], "tokens": ["What", "is", "the", "benefit", "of", "equiv", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-956846288", "fields": {"term": "What is Sentence-Bert?", "namespace": "open-p", "hash": -956846288, "terms": ["What is Sentence-Bert?", "is Sentence-Bert?", "Sentence-Bert?"], "tokens": ["What", "is", "Sentence", "Bert", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1183846506", "fields": {"term": "What is tokenization?", "namespace": "open-p", "hash": -1183846506, "terms": ["What is tokenization?", "is tokenization?", "tokenization?"], "tokens": ["What", "is", "tokenization", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::667397115", "fields": {"term": "What is CJK segmentation?", "namespace": "open-p", "hash": 667397115, "terms": ["What is CJK segmentation?", "is CJK segmentation?", "CJK segmentation?", "segmentation?"], "tokens": ["What", "is", "CJK", "segmentation", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::741602042", "fields": {"term": "What is indexing-rewrite?", "namespace": "open-p", "hash": 741602042, "terms": ["What is indexing-rewrite?", "is indexing-rewrite?", "indexing-rewrite?"], "tokens": ["What", "is", "indexing", "rewrite", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::-1256280191", "fields": {"term": "What is N-gram matching?", "namespace": "open-p", "hash": -1256280191, "terms": ["What is N-gram matching?", "is N-gram matching?", "N-gram matching?", "matching?"], "tokens": ["What", "is", "N", "gram", "matching", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::951639467", "fields": {"term": "What is text normalization?", "namespace": "open-p", "hash": 951639467, "terms": ["What is text normalization?", "is text normalization?", "text normalization?", "normalization?"], "tokens": ["What", "is", "text", "normalization", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::699272445", "fields": {"term": "How does Vespa normalize text?", "namespace": "open-p", "hash": 699272445, "terms": ["How does Vespa normalize text?", "does Vespa normalize text?", "Vespa normalize text?", "normalize text?", "text?"], "tokens": ["How", "does", "Vespa", "normalize", "text", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1628116986", "fields": {"term": "What is the nfkc query term annotation?", "namespace": "open-p", "hash": -1628116986, "terms": ["What is the nfkc query term annotation?", "is the nfkc query term annotation?", "the nfkc query term annotation?", "nfkc query term annotation?", "query term annotation?", "term annotation?", "annotation?"], "tokens": ["What", "is", "the", "nfkc", "query", "term", "annotation", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-182202906", "fields": {"term": "What is accentDrop annotation?", "namespace": "open-p", "hash": -182202906, "terms": ["What is accentDrop annotation?", "is accentDrop annotation?", "accentDrop annotation?", "annotation?"], "tokens": ["What", "is", "accentDrop", "annotation", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1990363620", "fields": {"term": "What does normalization preserve?", "namespace": "open-p", "hash": -1990363620, "terms": ["What does normalization preserve?", "does normalization preserve?", "normalization preserve?", "preserve?"], "tokens": ["What", "does", "normalization", "preserve", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1303662647", "fields": {"term": "Which library does Vespa use for text normalization?", "namespace": "open-p", "hash": -1303662647, "terms": ["Which library does Vespa use for text normalization?", "library does Vespa use for text normalization?", "does Vespa use for text normalization?", "Vespa use for text normalization?", "use for text normalization?", "for text normalization?", "text normalization?", "normalization?"], "tokens": ["Which", "library", "does", "Vespa", "use", "for", "text", "normalization", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::79566329", "fields": {"term": "What are the benefits of normalization?", "namespace": "open-p", "hash": 79566329, "terms": ["What are the benefits of normalization?", "are the benefits of normalization?", "the benefits of normalization?", "benefits of normalization?", "of normalization?", "normalization?"], "tokens": ["What", "are", "the", "benefits", "of", "normalization", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::871969994", "fields": {"term": "What kind of decorations are normalized by Vespa?", "namespace": "open-p", "hash": 871969994, "terms": ["What kind of decorations are normalized by Vespa?", "kind of decorations are normalized by Vespa?", "of decorations are normalized by Vespa?", "decorations are normalized by Vespa?", "are normalized by Vespa?", "normalized by Vespa?", "by Vespa?", "Vespa?"], "tokens": ["What", "kind", "of", "decorations", "are", "normalized", "by", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1430984321", "fields": {"term": "What is stemming?", "namespace": "open-p", "hash": -1430984321, "terms": ["What is stemming?", "is stemming?", "stemming?"], "tokens": ["What", "is", "stemming", "?"], "corpus_count": 3, "document_count": 3}}, -{"put": "id:term:term::-356676258", "fields": {"term": "What is a stemmer?", "namespace": "open-p", "hash": -356676258, "terms": ["What is a stemmer?", "is a stemmer?", "a stemmer?", "stemmer?"], "tokens": ["What", "is", "a", "stemmer", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-395732290", "fields": {"term": "How does stemming work?", "namespace": "open-p", "hash": -395732290, "terms": ["How does stemming work?", "does stemming work?", "stemming work?", "work?"], "tokens": ["How", "does", "stemming", "work", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-969110844", "fields": {"term": "What is symmetric stemming?", "namespace": "open-p", "hash": -969110844, "terms": ["What is symmetric stemming?", "is symmetric stemming?", "symmetric stemming?", "stemming?"], "tokens": ["What", "is", "symmetric", "stemming", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1881233394", "fields": {"term": "What is the purpose of stemming?", "namespace": "open-p", "hash": -1881233394, "terms": ["What is the purpose of stemming?", "is the purpose of stemming?", "the purpose of stemming?", "purpose of stemming?", "of stemming?", "stemming?"], "tokens": ["What", "is", "the", "purpose", "of", "stemming", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-461261018", "fields": {"term": "How does bolding work?", "namespace": "open-p", "hash": -461261018, "terms": ["How does bolding work?", "does bolding work?", "bolding work?", "work?"], "tokens": ["How", "does", "bolding", "work", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::925254228", "fields": {"term": "What is the stem query term?", "namespace": "open-p", "hash": 925254228, "terms": ["What is the stem query term?", "is the stem query term?", "the stem query term?", "stem query term?", "query term?", "term?"], "tokens": ["What", "is", "the", "stem", "query", "term", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-700780589", "fields": {"term": "What are equivalence classes in Vespa?", "namespace": "open-p", "hash": -700780589, "terms": ["What are equivalence classes in Vespa?", "are equivalence classes in Vespa?", "equivalence classes in Vespa?", "classes in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["What", "are", "equivalence", "classes", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::644389503", "fields": {"term": "What is the problem with low-level linguistics library in Vespa?", "namespace": "open-p", "hash": 644389503, "terms": ["What is the problem with low-level linguistics library in Vespa?", "is the problem with low-level linguistics library in Vespa?", "the problem with low-level linguistics library in Vespa?", "problem with low-level linguistics library in Vespa?", "with low-level linguistics library in Vespa?", "low-level linguistics library in Vespa?", "linguistics library in Vespa?", "library in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["What", "is", "the", "problem", "with", "low", "level", "linguistics", "library", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::59339382", "fields": {"term": "How does multiple stemming help with recall?", "namespace": "open-p", "hash": 59339382, "terms": ["How does multiple stemming help with recall?", "does multiple stemming help with recall?", "multiple stemming help with recall?", "stemming help with recall?", "help with recall?", "with recall?", "recall?"], "tokens": ["How", "does", "multiple", "stemming", "help", "with", "recall", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1955071358", "fields": {"term": "How to disable stemming?", "namespace": "open-p", "hash": -1955071358, "terms": ["How to disable stemming?", "to disable stemming?", "disable stemming?", "stemming?"], "tokens": ["How", "to", "disable", "stemming", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1472142403", "fields": {"term": "How to upgrade Vespa?", "namespace": "open-p", "hash": 1472142403, "terms": ["How to upgrade Vespa?", "to upgrade Vespa?", "upgrade Vespa?", "Vespa?"], "tokens": ["How", "to", "upgrade", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2094172251", "fields": {"term": "What is step 1 of the upgrade process?", "namespace": "open-p", "hash": 2094172251, "terms": ["What is step 1 of the upgrade process?", "is step 1 of the upgrade process?", "step 1 of the upgrade process?", "1 of the upgrade process?", "of the upgrade process?", "the upgrade process?", "upgrade process?", "process?"], "tokens": ["What", "is", "step", "1", "of", "the", "upgrade", "process", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1370910969", "fields": {"term": "What are the metrics for evaluating if the next node can be stopped?", "namespace": "open-p", "hash": -1370910969, "terms": ["What are the metrics for evaluating if the next node can be stopped?", "are the metrics for evaluating if the next node can be stopped?", "the metrics for evaluating if the next node can be stopped?", "metrics for evaluating if the next node can be stopped?", "for evaluating if the next node can be stopped?", "evaluating if the next node can be stopped?", "if the next node can be stopped?", "the next node can be stopped?", "next node can be stopped?", "node can be stopped?", "can be stopped?", "be stopped?", "stopped?"], "tokens": ["What", "are", "the", "metrics", "for", "evaluating", "if", "the", "next", "node", "can", "be", "stopped", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::141716201", "fields": {"term": "What is the effect of upgrading config servers?", "namespace": "open-p", "hash": 141716201, "terms": ["What is the effect of upgrading config servers?", "is the effect of upgrading config servers?", "the effect of upgrading config servers?", "effect of upgrading config servers?", "of upgrading config servers?", "upgrading config servers?", "config servers?", "servers?"], "tokens": ["What", "is", "the", "effect", "of", "upgrading", "config", "servers", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-781503558", "fields": {"term": "What is the redundancy requirement for availability during upgrade?", "namespace": "open-p", "hash": -781503558, "terms": ["What is the redundancy requirement for availability during upgrade?", "is the redundancy requirement for availability during upgrade?", "the redundancy requirement for availability during upgrade?", "redundancy requirement for availability during upgrade?", "requirement for availability during upgrade?", "for availability during upgrade?", "availability during upgrade?", "during upgrade?", "upgrade?"], "tokens": ["What", "is", "the", "redundancy", "requirement", "for", "availability", "during", "upgrade", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1038356453", "fields": {"term": "Where can I find troubleshooting?", "namespace": "open-p", "hash": 1038356453, "terms": ["Where can I find troubleshooting?", "can I find troubleshooting?", "I find troubleshooting?", "find troubleshooting?", "troubleshooting?"], "tokens": ["Where", "can", "I", "find", "troubleshooting", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1387572062", "fields": {"term": "What is logd.processed.lines?", "namespace": "open-p", "hash": 1387572062, "terms": ["What is logd.processed.lines?", "is logd.processed.lines?", "logd.processed.lines?"], "tokens": ["What", "is", "logd", "processed", "lines", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::2034784926", "fields": {"term": "What does logd.processed.lines count?", "namespace": "open-p", "hash": 2034784926, "terms": ["What does logd.processed.lines count?", "does logd.processed.lines count?", "logd.processed.lines count?", "count?"], "tokens": ["What", "does", "logd", "processed", "lines", "count", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::460983426", "fields": {"term": "Where are Vespa logs stored?", "namespace": "open-p", "hash": 460983426, "terms": ["Where are Vespa logs stored?", "are Vespa logs stored?", "Vespa logs stored?", "logs stored?", "stored?"], "tokens": ["Where", "are", "Vespa", "logs", "stored", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1463686832", "fields": {"term": "What is the common log module?", "namespace": "open-p", "hash": -1463686832, "terms": ["What is the common log module?", "is the common log module?", "the common log module?", "common log module?", "log module?", "module?"], "tokens": ["What", "is", "the", "common", "log", "module", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1688921755", "fields": {"term": "What is the log format used?", "namespace": "open-p", "hash": -1688921755, "terms": ["What is the log format used?", "is the log format used?", "the log format used?", "log format used?", "format used?", "used?"], "tokens": ["What", "is", "the", "log", "format", "used", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1973338251", "fields": {"term": "What are the fields in each log message?", "namespace": "open-p", "hash": -1973338251, "terms": ["What are the fields in each log message?", "are the fields in each log message?", "the fields in each log message?", "fields in each log message?", "in each log message?", "each log message?", "log message?", "message?"], "tokens": ["What", "are", "the", "fields", "in", "each", "log", "message", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-203078025", "fields": {"term": "What is log level?", "namespace": "open-p", "hash": -203078025, "terms": ["What is log level?", "is log level?", "log level?", "level?"], "tokens": ["What", "is", "log", "level", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-717441626", "fields": {"term": "What is the description for error level?", "namespace": "open-p", "hash": -717441626, "terms": ["What is the description for error level?", "is the description for error level?", "the description for error level?", "description for error level?", "for error level?", "error level?", "level?"], "tokens": ["What", "is", "the", "description", "for", "error", "level", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-387271775", "fields": {"term": "What is the message field in a log message?", "namespace": "open-p", "hash": -387271775, "terms": ["What is the message field in a log message?", "is the message field in a log message?", "the message field in a log message?", "message field in a log message?", "field in a log message?", "in a log message?", "a log message?", "log message?", "message?"], "tokens": ["What", "is", "the", "message", "field", "in", "a", "log", "message", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-100686258", "fields": {"term": "What is event log level used for?", "namespace": "open-p", "hash": -100686258, "terms": ["What is event log level used for?", "is event log level used for?", "event log level used for?", "log level used for?", "level used for?", "used for?", "for?"], "tokens": ["What", "is", "event", "log", "level", "used", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-308127924", "fields": {"term": "What is spam log level used for?", "namespace": "open-p", "hash": -308127924, "terms": ["What is spam log level used for?", "is spam log level used for?", "spam log level used for?", "log level used for?", "level used for?", "used for?", "for?"], "tokens": ["What", "is", "spam", "log", "level", "used", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1787061492", "fields": {"term": "How to change log levels?", "namespace": "open-p", "hash": 1787061492, "terms": ["How to change log levels?", "to change log levels?", "change log levels?", "log levels?", "levels?"], "tokens": ["How", "to", "change", "log", "levels", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::104316546", "fields": {"term": "What is the default log level?", "namespace": "open-p", "hash": 104316546, "terms": ["What is the default log level?", "is the default log level?", "the default log level?", "default log level?", "log level?", "level?"], "tokens": ["What", "is", "the", "default", "log", "level", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-824688237", "fields": {"term": "Where are log control files stored?", "namespace": "open-p", "hash": -824688237, "terms": ["Where are log control files stored?", "are log control files stored?", "log control files stored?", "control files stored?", "files stored?", "stored?"], "tokens": ["Where", "are", "log", "control", "files", "stored", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1301449259", "fields": {"term": "What are event messages?", "namespace": "open-p", "hash": 1301449259, "terms": ["What are event messages?", "are event messages?", "event messages?", "messages?"], "tokens": ["What", "are", "event", "messages", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2009737132", "fields": {"term": "How are events emitted?", "namespace": "open-p", "hash": -2009737132, "terms": ["How are events emitted?", "are events emitted?", "events emitted?", "emitted?"], "tokens": ["How", "are", "events", "emitted", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-969311218", "fields": {"term": "What is an event type?", "namespace": "open-p", "hash": -969311218, "terms": ["What is an event type?", "is an event type?", "an event type?", "event type?", "type?"], "tokens": ["What", "is", "an", "event", "type", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::869097555", "fields": {"term": "What is an event version?", "namespace": "open-p", "hash": 869097555, "terms": ["What is an event version?", "is an event version?", "an event version?", "event version?", "version?"], "tokens": ["What", "is", "an", "event", "version", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::404363766", "fields": {"term": "What is an event payload?", "namespace": "open-p", "hash": 404363766, "terms": ["What is an event payload?", "is an event payload?", "an event payload?", "event payload?", "payload?"], "tokens": ["What", "is", "an", "event", "payload", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-743969497", "fields": {"term": "What is a count event?", "namespace": "open-p", "hash": -743969497, "terms": ["What is a count event?", "is a count event?", "a count event?", "count event?", "event?"], "tokens": ["What", "is", "a", "count", "event", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2049153025", "fields": {"term": "What is a value event?", "namespace": "open-p", "hash": -2049153025, "terms": ["What is a value event?", "is a value event?", "a value event?", "value event?", "event?"], "tokens": ["What", "is", "a", "value", "event", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::460272475", "fields": {"term": "What is logd?", "namespace": "open-p", "hash": 460272475, "terms": ["What is logd?", "is logd?", "logd?"], "tokens": ["What", "is", "logd", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2088282289", "fields": {"term": "What is vespa.log?", "namespace": "open-p", "hash": 2088282289, "terms": ["What is vespa.log?", "is vespa.log?", "vespa.log?"], "tokens": ["What", "is", "vespa", "log", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::839180190", "fields": {"term": "How is the log file rotated?", "namespace": "open-p", "hash": 839180190, "terms": ["How is the log file rotated?", "is the log file rotated?", "the log file rotated?", "log file rotated?", "file rotated?", "rotated?"], "tokens": ["How", "is", "the", "log", "file", "rotated", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1086508249", "fields": {"term": "When is the log file rotated?", "namespace": "open-p", "hash": 1086508249, "terms": ["When is the log file rotated?", "is the log file rotated?", "the log file rotated?", "log file rotated?", "file rotated?", "rotated?"], "tokens": ["When", "is", "the", "log", "file", "rotated", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1315159704", "fields": {"term": "How are rotated logs removed?", "namespace": "open-p", "hash": 1315159704, "terms": ["How are rotated logs removed?", "are rotated logs removed?", "rotated logs removed?", "logs removed?", "removed?"], "tokens": ["How", "are", "rotated", "logs", "removed", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::887135642", "fields": {"term": "What happens if log file grows too large?", "namespace": "open-p", "hash": 887135642, "terms": ["What happens if log file grows too large?", "happens if log file grows too large?", "if log file grows too large?", "log file grows too large?", "file grows too large?", "grows too large?", "too large?", "large?"], "tokens": ["What", "happens", "if", "log", "file", "grows", "too", "large", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-768634990", "fields": {"term": "What does the Archiver plugin do?", "namespace": "open-p", "hash": -768634990, "terms": ["What does the Archiver plugin do?", "does the Archiver plugin do?", "the Archiver plugin do?", "Archiver plugin do?", "plugin do?", "do?"], "tokens": ["What", "does", "the", "Archiver", "plugin", "do", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::144870018", "fields": {"term": "Where are log files located?", "namespace": "open-p", "hash": 144870018, "terms": ["Where are log files located?", "are log files located?", "log files located?", "files located?", "located?"], "tokens": ["Where", "are", "log", "files", "located", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::201871328", "fields": {"term": "What is the log file naming pattern?", "namespace": "open-p", "hash": 201871328, "terms": ["What is the log file naming pattern?", "is the log file naming pattern?", "the log file naming pattern?", "log file naming pattern?", "file naming pattern?", "naming pattern?", "pattern?"], "tokens": ["What", "is", "the", "log", "file", "naming", "pattern", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-957435191", "fields": {"term": "What happens when a log file exceeds 20 Mb?", "namespace": "open-p", "hash": -957435191, "terms": ["What happens when a log file exceeds 20 Mb?", "happens when a log file exceeds 20 Mb?", "when a log file exceeds 20 Mb?", "a log file exceeds 20 Mb?", "log file exceeds 20 Mb?", "file exceeds 20 Mb?", "exceeds 20 Mb?", "20 Mb?", "Mb?"], "tokens": ["What", "happens", "when", "a", "log", "file", "exceeds", "20", "Mb", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1876148406", "fields": {"term": "How are rotated log files compressed?", "namespace": "open-p", "hash": -1876148406, "terms": ["How are rotated log files compressed?", "are rotated log files compressed?", "rotated log files compressed?", "log files compressed?", "files compressed?", "compressed?"], "tokens": ["How", "are", "rotated", "log", "files", "compressed", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2125693694", "fields": {"term": "When are archived log files deleted?", "namespace": "open-p", "hash": 2125693694, "terms": ["When are archived log files deleted?", "are archived log files deleted?", "archived log files deleted?", "log files deleted?", "files deleted?", "deleted?"], "tokens": ["When", "are", "archived", "log", "files", "deleted", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::335106626", "fields": {"term": "How can events be forwarded?", "namespace": "open-p", "hash": 335106626, "terms": ["How can events be forwarded?", "can events be forwarded?", "events be forwarded?", "be forwarded?", "forwarded?"], "tokens": ["How", "can", "events", "be", "forwarded", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1612564272", "fields": {"term": "What should be added to services.xml for forwarding events?", "namespace": "open-p", "hash": 1612564272, "terms": ["What should be added to services.xml for forwarding events?", "should be added to services.xml for forwarding events?", "be added to services.xml for forwarding events?", "added to services.xml for forwarding events?", "to services.xml for forwarding events?", "services.xml for forwarding events?", "for forwarding events?", "forwarding events?", "events?"], "tokens": ["What", "should", "be", "added", "to", "services", "xml", "for", "forwarding", "events", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1112870041", "fields": {"term": "Where are Container logs stored?", "namespace": "open-p", "hash": -1112870041, "terms": ["Where are Container logs stored?", "are Container logs stored?", "Container logs stored?", "logs stored?", "stored?"], "tokens": ["Where", "are", "Container", "logs", "stored", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1140683387", "fields": {"term": "What is access logging?", "namespace": "open-p", "hash": 1140683387, "terms": ["What is access logging?", "is access logging?", "access logging?", "logging?"], "tokens": ["What", "is", "access", "logging", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::254979187", "fields": {"term": "What causes timing differences?", "namespace": "open-p", "hash": 254979187, "terms": ["What causes timing differences?", "causes timing differences?", "timing differences?", "differences?"], "tokens": ["What", "causes", "timing", "differences", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1086831163", "fields": {"term": "Why are timing values different?", "namespace": "open-p", "hash": 1086831163, "terms": ["Why are timing values different?", "are timing values different?", "timing values different?", "values different?", "different?"], "tokens": ["Why", "are", "timing", "values", "different", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::482903521", "fields": {"term": "What is the access log processing time?", "namespace": "open-p", "hash": 482903521, "terms": ["What is the access log processing time?", "is the access log processing time?", "the access log processing time?", "access log processing time?", "log processing time?", "processing time?", "time?"], "tokens": ["What", "is", "the", "access", "log", "processing", "time", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::802670777", "fields": {"term": "When does processing time end for an asynchronous response?", "namespace": "open-p", "hash": 802670777, "terms": ["When does processing time end for an asynchronous response?", "does processing time end for an asynchronous response?", "processing time end for an asynchronous response?", "time end for an asynchronous response?", "end for an asynchronous response?", "for an asynchronous response?", "an asynchronous response?", "asynchronous response?", "response?"], "tokens": ["When", "does", "processing", "time", "end", "for", "an", "asynchronous", "response", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::172247200", "fields": {"term": "When does processing time end for a synchronous response?", "namespace": "open-p", "hash": 172247200, "terms": ["When does processing time end for a synchronous response?", "does processing time end for a synchronous response?", "processing time end for a synchronous response?", "time end for a synchronous response?", "end for a synchronous response?", "for a synchronous response?", "a synchronous response?", "synchronous response?", "response?"], "tokens": ["When", "does", "processing", "time", "end", "for", "a", "synchronous", "response", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::199596273", "fields": {"term": "What is a completion handler?", "namespace": "open-p", "hash": 199596273, "terms": ["What is a completion handler?", "is a completion handler?", "a completion handler?", "completion handler?", "handler?"], "tokens": ["What", "is", "a", "completion", "handler", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1000436219", "fields": {"term": "When is the end of processing time for a synchronous response?", "namespace": "open-p", "hash": -1000436219, "terms": ["When is the end of processing time for a synchronous response?", "is the end of processing time for a synchronous response?", "the end of processing time for a synchronous response?", "end of processing time for a synchronous response?", "of processing time for a synchronous response?", "processing time for a synchronous response?", "time for a synchronous response?", "for a synchronous response?", "a synchronous response?", "synchronous response?", "response?"], "tokens": ["When", "is", "the", "end", "of", "processing", "time", "for", "a", "synchronous", "response", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::611567935", "fields": {"term": "When is the end of processing time for an asynchronous response?", "namespace": "open-p", "hash": 611567935, "terms": ["When is the end of processing time for an asynchronous response?", "is the end of processing time for an asynchronous response?", "the end of processing time for an asynchronous response?", "end of processing time for an asynchronous response?", "of processing time for an asynchronous response?", "processing time for an asynchronous response?", "time for an asynchronous response?", "for an asynchronous response?", "an asynchronous response?", "asynchronous response?", "response?"], "tokens": ["When", "is", "the", "end", "of", "processing", "time", "for", "an", "asynchronous", "response", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1875382712", "fields": {"term": "What happens before the end of processing time for an asynchronous response?", "namespace": "open-p", "hash": -1875382712, "terms": ["What happens before the end of processing time for an asynchronous response?", "happens before the end of processing time for an asynchronous response?", "before the end of processing time for an asynchronous response?", "the end of processing time for an asynchronous response?", "end of processing time for an asynchronous response?", "of processing time for an asynchronous response?", "processing time for an asynchronous response?", "time for an asynchronous response?", "for an asynchronous response?", "an asynchronous response?", "asynchronous response?", "response?"], "tokens": ["What", "happens", "before", "the", "end", "of", "processing", "time", "for", "an", "asynchronous", "response", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1053968531", "fields": {"term": "What happens before the end of processing time for a synchronous response?", "namespace": "open-p", "hash": 1053968531, "terms": ["What happens before the end of processing time for a synchronous response?", "happens before the end of processing time for a synchronous response?", "before the end of processing time for a synchronous response?", "the end of processing time for a synchronous response?", "end of processing time for a synchronous response?", "of processing time for a synchronous response?", "processing time for a synchronous response?", "time for a synchronous response?", "for a synchronous response?", "a synchronous response?", "synchronous response?", "response?"], "tokens": ["What", "happens", "before", "the", "end", "of", "processing", "time", "for", "a", "synchronous", "response", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::287579156", "fields": {"term": "What is StatisticsSearcher?", "namespace": "open-p", "hash": 287579156, "terms": ["What is StatisticsSearcher?", "is StatisticsSearcher?", "StatisticsSearcher?"], "tokens": ["What", "is", "StatisticsSearcher", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1051343315", "fields": {"term": "What is mean_query_latency event?", "namespace": "open-p", "hash": -1051343315, "terms": ["What is mean_query_latency event?", "is mean_query_latency event?", "mean_query_latency event?", "event?"], "tokens": ["What", "is", "mean_query_latency", "event", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1269748188", "fields": {"term": "What is the interval duration?", "namespace": "open-p", "hash": -1269748188, "terms": ["What is the interval duration?", "is the interval duration?", "the interval duration?", "interval duration?", "duration?"], "tokens": ["What", "is", "the", "interval", "duration", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::423087152", "fields": {"term": "What is included in the interval?", "namespace": "open-p", "hash": 423087152, "terms": ["What is included in the interval?", "is included in the interval?", "included in the interval?", "in the interval?", "the interval?", "interval?"], "tokens": ["What", "is", "included", "in", "the", "interval", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::330742520", "fields": {"term": "What is excluded from the interval?", "namespace": "open-p", "hash": 330742520, "terms": ["What is excluded from the interval?", "is excluded from the interval?", "excluded from the interval?", "from the interval?", "the interval?", "interval?"], "tokens": ["What", "is", "excluded", "from", "the", "interval", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1122317248", "fields": {"term": "When does fill time get included?", "namespace": "open-p", "hash": 1122317248, "terms": ["When does fill time get included?", "does fill time get included?", "fill time get included?", "time get included?", "get included?", "included?"], "tokens": ["When", "does", "fill", "time", "get", "included", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1422998355", "fields": {"term": "When does fill() get invoked?", "namespace": "open-p", "hash": -1422998355, "terms": ["When does fill() get invoked?", "does fill() get invoked?", "fill() get invoked?", "get invoked?", "invoked?"], "tokens": ["When", "does", "fill", "()", "get", "invoked", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::386994445", "fields": {"term": "What information does the access log include?", "namespace": "open-p", "hash": 386994445, "terms": ["What information does the access log include?", "information does the access log include?", "does the access log include?", "the access log include?", "access log include?", "log include?", "include?"], "tokens": ["What", "information", "does", "the", "access", "log", "include", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-818872152", "fields": {"term": "What does query latency event cover?", "namespace": "open-p", "hash": -818872152, "terms": ["What does query latency event cover?", "does query latency event cover?", "query latency event cover?", "latency event cover?", "event cover?", "cover?"], "tokens": ["What", "does", "query", "latency", "event", "cover", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1534353129", "fields": {"term": "Where is StatisticsSearcher placed?", "namespace": "open-p", "hash": -1534353129, "terms": ["Where is StatisticsSearcher placed?", "is StatisticsSearcher placed?", "StatisticsSearcher placed?", "placed?"], "tokens": ["Where", "is", "StatisticsSearcher", "placed", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::728452878", "fields": {"term": "Does access log include rendering logic?", "namespace": "open-p", "hash": 728452878, "terms": ["Does access log include rendering logic?", "access log include rendering logic?", "log include rendering logic?", "include rendering logic?", "rendering logic?", "logic?"], "tokens": ["Does", "access", "log", "include", "rendering", "logic", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::408219645", "fields": {"term": "What is excluded from query latency event?", "namespace": "open-p", "hash": 408219645, "terms": ["What is excluded from query latency event?", "is excluded from query latency event?", "excluded from query latency event?", "from query latency event?", "query latency event?", "latency event?", "event?"], "tokens": ["What", "is", "excluded", "from", "query", "latency", "event", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1099577900", "fields": {"term": "What networks are excluded from access log?", "namespace": "open-p", "hash": -1099577900, "terms": ["What networks are excluded from access log?", "networks are excluded from access log?", "are excluded from access log?", "excluded from access log?", "from access log?", "access log?", "log?"], "tokens": ["What", "networks", "are", "excluded", "from", "access", "log", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2097921388", "fields": {"term": "What is the purpose of StatisticsSearcher?", "namespace": "open-p", "hash": 2097921388, "terms": ["What is the purpose of StatisticsSearcher?", "is the purpose of StatisticsSearcher?", "the purpose of StatisticsSearcher?", "purpose of StatisticsSearcher?", "of StatisticsSearcher?", "StatisticsSearcher?"], "tokens": ["What", "is", "the", "purpose", "of", "StatisticsSearcher", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1396465419", "fields": {"term": "Does query latency cover summary fetching?", "namespace": "open-p", "hash": 1396465419, "terms": ["Does query latency cover summary fetching?", "query latency cover summary fetching?", "latency cover summary fetching?", "cover summary fetching?", "summary fetching?", "fetching?"], "tokens": ["Does", "query", "latency", "cover", "summary", "fetching", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1195154818", "fields": {"term": "What is the location of the ZooKeeper log?", "namespace": "open-p", "hash": 1195154818, "terms": ["What is the location of the ZooKeeper log?", "is the location of the ZooKeeper log?", "the location of the ZooKeeper log?", "location of the ZooKeeper log?", "of the ZooKeeper log?", "the ZooKeeper log?", "ZooKeeper log?", "log?"], "tokens": ["What", "is", "the", "location", "of", "the", "ZooKeeper", "log", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1094555761", "fields": {"term": "When should you monitor the ZooKeeper log?", "namespace": "open-p", "hash": 1094555761, "terms": ["When should you monitor the ZooKeeper log?", "should you monitor the ZooKeeper log?", "you monitor the ZooKeeper log?", "monitor the ZooKeeper log?", "the ZooKeeper log?", "ZooKeeper log?", "log?"], "tokens": ["When", "should", "you", "monitor", "the", "ZooKeeper", "log", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-875963566", "fields": {"term": "Where is the ZooKeeper log file stored?", "namespace": "open-p", "hash": -875963566, "terms": ["Where is the ZooKeeper log file stored?", "is the ZooKeeper log file stored?", "the ZooKeeper log file stored?", "ZooKeeper log file stored?", "log file stored?", "file stored?", "stored?"], "tokens": ["Where", "is", "the", "ZooKeeper", "log", "file", "stored", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::861972127", "fields": {"term": "What information can be found in the ZooKeeper log?", "namespace": "open-p", "hash": 861972127, "terms": ["What information can be found in the ZooKeeper log?", "information can be found in the ZooKeeper log?", "can be found in the ZooKeeper log?", "be found in the ZooKeeper log?", "found in the ZooKeeper log?", "in the ZooKeeper log?", "the ZooKeeper log?", "ZooKeeper log?", "log?"], "tokens": ["What", "information", "can", "be", "found", "in", "the", "ZooKeeper", "log", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-720694501", "fields": {"term": "How do I debug Vespa configuration using the ZooKeeper log?", "namespace": "open-p", "hash": -720694501, "terms": ["How do I debug Vespa configuration using the ZooKeeper log?", "do I debug Vespa configuration using the ZooKeeper log?", "I debug Vespa configuration using the ZooKeeper log?", "debug Vespa configuration using the ZooKeeper log?", "Vespa configuration using the ZooKeeper log?", "configuration using the ZooKeeper log?", "using the ZooKeeper log?", "the ZooKeeper log?", "ZooKeeper log?", "log?"], "tokens": ["How", "do", "I", "debug", "Vespa", "configuration", "using", "the", "ZooKeeper", "log", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1755549428", "fields": {"term": "What are low-level request handler APIs?", "namespace": "open-p", "hash": -1755549428, "terms": ["What are low-level request handler APIs?", "are low-level request handler APIs?", "low-level request handler APIs?", "request handler APIs?", "handler APIs?", "APIs?"], "tokens": ["What", "are", "low", "level", "request", "handler", "APIs", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::345936767", "fields": {"term": "What is RequestHandler interface?", "namespace": "open-p", "hash": 345936767, "terms": ["What is RequestHandler interface?", "is RequestHandler interface?", "RequestHandler interface?", "interface?"], "tokens": ["What", "is", "RequestHandler", "interface", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2041552420", "fields": {"term": "What is the purpose of a request handler?", "namespace": "open-p", "hash": -2041552420, "terms": ["What is the purpose of a request handler?", "is the purpose of a request handler?", "the purpose of a request handler?", "purpose of a request handler?", "of a request handler?", "a request handler?", "request handler?", "handler?"], "tokens": ["What", "is", "the", "purpose", "of", "a", "request", "handler", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::614163607", "fields": {"term": "How is a custom request handler implemented?", "namespace": "open-p", "hash": 614163607, "terms": ["How is a custom request handler implemented?", "is a custom request handler implemented?", "a custom request handler implemented?", "custom request handler implemented?", "request handler implemented?", "handler implemented?", "implemented?"], "tokens": ["How", "is", "a", "custom", "request", "handler", "implemented", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2093466679", "fields": {"term": "What is the difference between RequestHandler and AbstractRequestHandler?", "namespace": "open-p", "hash": 2093466679, "terms": ["What is the difference between RequestHandler and AbstractRequestHandler?", "is the difference between RequestHandler and AbstractRequestHandler?", "the difference between RequestHandler and AbstractRequestHandler?", "difference between RequestHandler and AbstractRequestHandler?", "between RequestHandler and AbstractRequestHandler?", "RequestHandler and AbstractRequestHandler?", "and AbstractRequestHandler?", "AbstractRequestHandler?"], "tokens": ["What", "is", "the", "difference", "between", "RequestHandler", "and", "AbstractRequestHandler", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::174001188", "fields": {"term": "What is the role of a ResponseHandler?", "namespace": "open-p", "hash": 174001188, "terms": ["What is the role of a ResponseHandler?", "is the role of a ResponseHandler?", "the role of a ResponseHandler?", "role of a ResponseHandler?", "of a ResponseHandler?", "a ResponseHandler?", "ResponseHandler?"], "tokens": ["What", "is", "the", "role", "of", "a", "ResponseHandler", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-234755425", "fields": {"term": "What is the timeout for a request?", "namespace": "open-p", "hash": -234755425, "terms": ["What is the timeout for a request?", "is the timeout for a request?", "the timeout for a request?", "timeout for a request?", "for a request?", "a request?", "request?"], "tokens": ["What", "is", "the", "timeout", "for", "a", "request", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1977281600", "fields": {"term": "What is the dispatching logic for a client request?", "namespace": "open-p", "hash": -1977281600, "terms": ["What is the dispatching logic for a client request?", "is the dispatching logic for a client request?", "the dispatching logic for a client request?", "dispatching logic for a client request?", "logic for a client request?", "for a client request?", "a client request?", "client request?", "request?"], "tokens": ["What", "is", "the", "dispatching", "logic", "for", "a", "client", "request", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1231328786", "fields": {"term": "What method does a request handler invoke?", "namespace": "open-p", "hash": -1231328786, "terms": ["What method does a request handler invoke?", "method does a request handler invoke?", "does a request handler invoke?", "a request handler invoke?", "request handler invoke?", "handler invoke?", "invoke?"], "tokens": ["What", "method", "does", "a", "request", "handler", "invoke", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2087076781", "fields": {"term": "What is ThreadedRequestHandler?", "namespace": "open-p", "hash": 2087076781, "terms": ["What is ThreadedRequestHandler?", "is ThreadedRequestHandler?", "ThreadedRequestHandler?"], "tokens": ["What", "is", "ThreadedRequestHandler", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1210016217", "fields": {"term": "What is a ContentChannel?", "namespace": "open-p", "hash": -1210016217, "terms": ["What is a ContentChannel?", "is a ContentChannel?", "a ContentChannel?", "ContentChannel?"], "tokens": ["What", "is", "a", "ContentChannel", "?"], "corpus_count": 4, "document_count": 4}}, -{"put": "id:term:term::-811198162", "fields": {"term": "What must a request handler do?", "namespace": "open-p", "hash": -811198162, "terms": ["What must a request handler do?", "must a request handler do?", "a request handler do?", "request handler do?", "handler do?", "do?"], "tokens": ["What", "must", "a", "request", "handler", "do", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1748314344", "fields": {"term": "What does JDisc enforce?", "namespace": "open-p", "hash": -1748314344, "terms": ["What does JDisc enforce?", "does JDisc enforce?", "JDisc enforce?", "enforce?"], "tokens": ["What", "does", "JDisc", "enforce", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1186569113", "fields": {"term": "What is the purpose of passing context to a known thread pool?", "namespace": "open-p", "hash": 1186569113, "terms": ["What is the purpose of passing context to a known thread pool?", "is the purpose of passing context to a known thread pool?", "the purpose of passing context to a known thread pool?", "purpose of passing context to a known thread pool?", "of passing context to a known thread pool?", "passing context to a known thread pool?", "context to a known thread pool?", "to a known thread pool?", "a known thread pool?", "known thread pool?", "thread pool?", "pool?"], "tokens": ["What", "is", "the", "purpose", "of", "passing", "context", "to", "a", "known", "thread", "pool", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::176802140", "fields": {"term": "What should a request handler not do?", "namespace": "open-p", "hash": 176802140, "terms": ["What should a request handler not do?", "should a request handler not do?", "a request handler not do?", "request handler not do?", "handler not do?", "not do?", "do?"], "tokens": ["What", "should", "a", "request", "handler", "not", "do", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1477047910", "fields": {"term": "What does the ContentChannel interface provide?", "namespace": "open-p", "hash": 1477047910, "terms": ["What does the ContentChannel interface provide?", "does the ContentChannel interface provide?", "the ContentChannel interface provide?", "ContentChannel interface provide?", "interface provide?", "provide?"], "tokens": ["What", "does", "the", "ContentChannel", "interface", "provide", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1425436626", "fields": {"term": "What is handover?", "namespace": "open-p", "hash": -1425436626, "terms": ["What is handover?", "is handover?", "handover?"], "tokens": ["What", "is", "handover", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1717418646", "fields": {"term": "What is required for a content channel to be thread-safe?", "namespace": "open-p", "hash": 1717418646, "terms": ["What is required for a content channel to be thread-safe?", "is required for a content channel to be thread-safe?", "required for a content channel to be thread-safe?", "for a content channel to be thread-safe?", "a content channel to be thread-safe?", "content channel to be thread-safe?", "channel to be thread-safe?", "to be thread-safe?", "be thread-safe?", "thread-safe?"], "tokens": ["What", "is", "required", "for", "a", "content", "channel", "to", "be", "thread", "safe", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::578325567", "fields": {"term": "What is CompletionHandler?", "namespace": "open-p", "hash": 578325567, "terms": ["What is CompletionHandler?", "is CompletionHandler?", "CompletionHandler?"], "tokens": ["What", "is", "CompletionHandler", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1918796570", "fields": {"term": "What is the purpose of CompletionHandler?", "namespace": "open-p", "hash": 1918796570, "terms": ["What is the purpose of CompletionHandler?", "is the purpose of CompletionHandler?", "the purpose of CompletionHandler?", "purpose of CompletionHandler?", "of CompletionHandler?", "CompletionHandler?"], "tokens": ["What", "is", "the", "purpose", "of", "CompletionHandler", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::763079838", "fields": {"term": "What is BufferedContentChannel?", "namespace": "open-p", "hash": 763079838, "terms": ["What is BufferedContentChannel?", "is BufferedContentChannel?", "BufferedContentChannel?"], "tokens": ["What", "is", "BufferedContentChannel", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::883414987", "fields": {"term": "What is ReadableContentChannel?", "namespace": "open-p", "hash": 883414987, "terms": ["What is ReadableContentChannel?", "is ReadableContentChannel?", "ReadableContentChannel?"], "tokens": ["What", "is", "ReadableContentChannel", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-45449521", "fields": {"term": "What is a ResponseHandler?", "namespace": "open-p", "hash": -45449521, "terms": ["What is a ResponseHandler?", "is a ResponseHandler?", "a ResponseHandler?", "ResponseHandler?"], "tokens": ["What", "is", "a", "ResponseHandler", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1395131296", "fields": {"term": "What is the purpose of ResponseHandler?", "namespace": "open-p", "hash": -1395131296, "terms": ["What is the purpose of ResponseHandler?", "is the purpose of ResponseHandler?", "the purpose of ResponseHandler?", "purpose of ResponseHandler?", "of ResponseHandler?", "ResponseHandler?"], "tokens": ["What", "is", "the", "purpose", "of", "ResponseHandler", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1521235817", "fields": {"term": "What is the second argument of handleRequest method?", "namespace": "open-p", "hash": 1521235817, "terms": ["What is the second argument of handleRequest method?", "is the second argument of handleRequest method?", "the second argument of handleRequest method?", "second argument of handleRequest method?", "argument of handleRequest method?", "of handleRequest method?", "handleRequest method?", "method?"], "tokens": ["What", "is", "the", "second", "argument", "of", "handleRequest", "method", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::731347917", "fields": {"term": "How do you dispatch a response?", "namespace": "open-p", "hash": 731347917, "terms": ["How do you dispatch a response?", "do you dispatch a response?", "you dispatch a response?", "dispatch a response?", "a response?", "response?"], "tokens": ["How", "do", "you", "dispatch", "a", "response", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1965550894", "fields": {"term": "What is the purpose of ContentChannel?", "namespace": "open-p", "hash": 1965550894, "terms": ["What is the purpose of ContentChannel?", "is the purpose of ContentChannel?", "the purpose of ContentChannel?", "purpose of ContentChannel?", "of ContentChannel?", "ContentChannel?"], "tokens": ["What", "is", "the", "purpose", "of", "ContentChannel", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-367067488", "fields": {"term": "What is the ResponseDispatch utility class?", "namespace": "open-p", "hash": -367067488, "terms": ["What is the ResponseDispatch utility class?", "is the ResponseDispatch utility class?", "the ResponseDispatch utility class?", "ResponseDispatch utility class?", "utility class?", "class?"], "tokens": ["What", "is", "the", "ResponseDispatch", "utility", "class", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::627918424", "fields": {"term": "What happens if you don't call the response handler exactly once?", "namespace": "open-p", "hash": 627918424, "terms": ["What happens if you don't call the response handler exactly once?", "happens if you don't call the response handler exactly once?", "if you don't call the response handler exactly once?", "you don't call the response handler exactly once?", "don't call the response handler exactly once?", "call the response handler exactly once?", "the response handler exactly once?", "response handler exactly once?", "handler exactly once?", "exactly once?", "once?"], "tokens": ["What", "happens", "if", "you", "don", "'", "t", "call", "the", "response", "handler", "exactly", "once", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1270411894", "fields": {"term": "What is required when using ContentChannel to write the response content?", "namespace": "open-p", "hash": 1270411894, "terms": ["What is required when using ContentChannel to write the response content?", "is required when using ContentChannel to write the response content?", "required when using ContentChannel to write the response content?", "when using ContentChannel to write the response content?", "using ContentChannel to write the response content?", "ContentChannel to write the response content?", "to write the response content?", "write the response content?", "the response content?", "response content?", "content?"], "tokens": ["What", "is", "required", "when", "using", "ContentChannel", "to", "write", "the", "response", "content", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1316073747", "fields": {"term": "What is ContentChannel?", "namespace": "open-p", "hash": 1316073747, "terms": ["What is ContentChannel?", "is ContentChannel?", "ContentChannel?"], "tokens": ["What", "is", "ContentChannel", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1639518793", "fields": {"term": "What is ByteBuffer handover?", "namespace": "open-p", "hash": -1639518793, "terms": ["What is ByteBuffer handover?", "is ByteBuffer handover?", "ByteBuffer handover?", "handover?"], "tokens": ["What", "is", "ByteBuffer", "handover", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1737941896", "fields": {"term": "Are all ContentChannels thread-safe?", "namespace": "open-p", "hash": -1737941896, "terms": ["Are all ContentChannels thread-safe?", "all ContentChannels thread-safe?", "ContentChannels thread-safe?", "thread-safe?"], "tokens": ["Are", "all", "ContentChannels", "thread", "safe", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1573139366", "fields": {"term": "What is a CompletionHandler?", "namespace": "open-p", "hash": -1573139366, "terms": ["What is a CompletionHandler?", "is a CompletionHandler?", "a CompletionHandler?", "CompletionHandler?"], "tokens": ["What", "is", "a", "CompletionHandler", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-341785623", "fields": {"term": "Is the CompletionHandler optional?", "namespace": "open-p", "hash": -341785623, "terms": ["Is the CompletionHandler optional?", "the CompletionHandler optional?", "CompletionHandler optional?", "optional?"], "tokens": ["Is", "the", "CompletionHandler", "optional", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::501730631", "fields": {"term": "What guarantees are there for the completion handler?", "namespace": "open-p", "hash": 501730631, "terms": ["What guarantees are there for the completion handler?", "guarantees are there for the completion handler?", "are there for the completion handler?", "there for the completion handler?", "for the completion handler?", "the completion handler?", "completion handler?", "handler?"], "tokens": ["What", "guarantees", "are", "there", "for", "the", "completion", "handler", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1003081985", "fields": {"term": "Is it required to explicitly close all content channels?", "namespace": "open-p", "hash": 1003081985, "terms": ["Is it required to explicitly close all content channels?", "it required to explicitly close all content channels?", "required to explicitly close all content channels?", "to explicitly close all content channels?", "explicitly close all content channels?", "close all content channels?", "all content channels?", "content channels?", "channels?"], "tokens": ["Is", "it", "required", "to", "explicitly", "close", "all", "content", "channels", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::264927063", "fields": {"term": "What happens if an exception is thrown in a call to write()?", "namespace": "open-p", "hash": 264927063, "terms": ["What happens if an exception is thrown in a call to write()?", "happens if an exception is thrown in a call to write()?", "if an exception is thrown in a call to write()?", "an exception is thrown in a call to write()?", "exception is thrown in a call to write()?", "is thrown in a call to write()?", "thrown in a call to write()?", "in a call to write()?", "a call to write()?", "call to write()?", "to write()?", "write()?"], "tokens": ["What", "happens", "if", "an", "exception", "is", "thrown", "in", "a", "call", "to", "write", "()?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1751877599", "fields": {"term": "How is child-request dispatched?", "namespace": "open-p", "hash": 1751877599, "terms": ["How is child-request dispatched?", "is child-request dispatched?", "child-request dispatched?", "dispatched?"], "tokens": ["How", "is", "child", "request", "dispatched", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1918225130", "fields": {"term": "What is setServerRequest used for?", "namespace": "open-p", "hash": -1918225130, "terms": ["What is setServerRequest used for?", "is setServerRequest used for?", "setServerRequest used for?", "used for?", "for?"], "tokens": ["What", "is", "setServerRequest", "used", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1027881645", "fields": {"term": "What is RequestDispatch?", "namespace": "open-p", "hash": -1027881645, "terms": ["What is RequestDispatch?", "is RequestDispatch?", "RequestDispatch?"], "tokens": ["What", "is", "RequestDispatch", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2046366852", "fields": {"term": "When to close the content channel?", "namespace": "open-p", "hash": -2046366852, "terms": ["When to close the content channel?", "to close the content channel?", "close the content channel?", "the content channel?", "content channel?", "channel?"], "tokens": ["When", "to", "close", "the", "content", "channel", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1480754067", "fields": {"term": "What happens when a client provider responds?", "namespace": "open-p", "hash": -1480754067, "terms": ["What happens when a client provider responds?", "happens when a client provider responds?", "when a client provider responds?", "a client provider responds?", "client provider responds?", "provider responds?", "responds?"], "tokens": ["What", "happens", "when", "a", "client", "provider", "responds", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1543383364", "fields": {"term": "What is the response handler?", "namespace": "open-p", "hash": 1543383364, "terms": ["What is the response handler?", "is the response handler?", "the response handler?", "response handler?", "handler?"], "tokens": ["What", "is", "the", "response", "handler", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-979096202", "fields": {"term": "What should you not do in the handleResponse method?", "namespace": "open-p", "hash": -979096202, "terms": ["What should you not do in the handleResponse method?", "should you not do in the handleResponse method?", "you not do in the handleResponse method?", "not do in the handleResponse method?", "do in the handleResponse method?", "in the handleResponse method?", "the handleResponse method?", "handleResponse method?", "method?"], "tokens": ["What", "should", "you", "not", "do", "in", "the", "handleResponse", "method", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1954436309", "fields": {"term": "What happens when the response is dispatched?", "namespace": "open-p", "hash": 1954436309, "terms": ["What happens when the response is dispatched?", "happens when the response is dispatched?", "when the response is dispatched?", "the response is dispatched?", "response is dispatched?", "is dispatched?", "dispatched?"], "tokens": ["What", "happens", "when", "the", "response", "is", "dispatched", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1711528422", "fields": {"term": "What does a response handler need to do?", "namespace": "open-p", "hash": 1711528422, "terms": ["What does a response handler need to do?", "does a response handler need to do?", "a response handler need to do?", "response handler need to do?", "handler need to do?", "need to do?", "to do?", "do?"], "tokens": ["What", "does", "a", "response", "handler", "need", "to", "do", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::957153405", "fields": {"term": "How should the response payload be consumed?", "namespace": "open-p", "hash": 957153405, "terms": ["How should the response payload be consumed?", "should the response payload be consumed?", "the response payload be consumed?", "response payload be consumed?", "payload be consumed?", "be consumed?", "consumed?"], "tokens": ["How", "should", "the", "response", "payload", "be", "consumed", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1339667560", "fields": {"term": "What is a request timeout?", "namespace": "open-p", "hash": -1339667560, "terms": ["What is a request timeout?", "is a request timeout?", "a request timeout?", "request timeout?", "timeout?"], "tokens": ["What", "is", "a", "request", "timeout", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-633147403", "fields": {"term": "What happens when a timeout occurs?", "namespace": "open-p", "hash": -633147403, "terms": ["What happens when a timeout occurs?", "happens when a timeout occurs?", "when a timeout occurs?", "a timeout occurs?", "timeout occurs?", "occurs?"], "tokens": ["What", "happens", "when", "a", "timeout", "occurs", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1879547980", "fields": {"term": "How is the timeout managed?", "namespace": "open-p", "hash": 1879547980, "terms": ["How is the timeout managed?", "is the timeout managed?", "the timeout managed?", "timeout managed?", "managed?"], "tokens": ["How", "is", "the", "timeout", "managed", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1610745265", "fields": {"term": "What is the consequence of not calling the response handler from handleTimeout()?", "namespace": "open-p", "hash": -1610745265, "terms": ["What is the consequence of not calling the response handler from handleTimeout()?", "is the consequence of not calling the response handler from handleTimeout()?", "the consequence of not calling the response handler from handleTimeout()?", "consequence of not calling the response handler from handleTimeout()?", "of not calling the response handler from handleTimeout()?", "not calling the response handler from handleTimeout()?", "calling the response handler from handleTimeout()?", "the response handler from handleTimeout()?", "response handler from handleTimeout()?", "handler from handleTimeout()?", "from handleTimeout()?", "handleTimeout()?"], "tokens": ["What", "is", "the", "consequence", "of", "not", "calling", "the", "response", "handler", "from", "handleTimeout", "()?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1263603586", "fields": {"term": "Does a request always have a timeout assigned?", "namespace": "open-p", "hash": -1263603586, "terms": ["Does a request always have a timeout assigned?", "a request always have a timeout assigned?", "request always have a timeout assigned?", "always have a timeout assigned?", "have a timeout assigned?", "a timeout assigned?", "timeout assigned?", "assigned?"], "tokens": ["Does", "a", "request", "always", "have", "a", "timeout", "assigned", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1668845252", "fields": {"term": "Is additional synchronization needed to prevent repeated calling of the response handler?", "namespace": "open-p", "hash": 1668845252, "terms": ["Is additional synchronization needed to prevent repeated calling of the response handler?", "additional synchronization needed to prevent repeated calling of the response handler?", "synchronization needed to prevent repeated calling of the response handler?", "needed to prevent repeated calling of the response handler?", "to prevent repeated calling of the response handler?", "prevent repeated calling of the response handler?", "repeated calling of the response handler?", "calling of the response handler?", "of the response handler?", "the response handler?", "response handler?", "handler?"], "tokens": ["Is", "additional", "synchronization", "needed", "to", "prevent", "repeated", "calling", "of", "the", "response", "handler", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::924701842", "fields": {"term": "What happens if an exception is thrown in handleTimeout()?", "namespace": "open-p", "hash": 924701842, "terms": ["What happens if an exception is thrown in handleTimeout()?", "happens if an exception is thrown in handleTimeout()?", "if an exception is thrown in handleTimeout()?", "an exception is thrown in handleTimeout()?", "exception is thrown in handleTimeout()?", "is thrown in handleTimeout()?", "thrown in handleTimeout()?", "in handleTimeout()?", "handleTimeout()?"], "tokens": ["What", "happens", "if", "an", "exception", "is", "thrown", "in", "handleTimeout", "()?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1406782444", "fields": {"term": "Why is it important to call the response handler from within handleTimeout()?", "namespace": "open-p", "hash": -1406782444, "terms": ["Why is it important to call the response handler from within handleTimeout()?", "is it important to call the response handler from within handleTimeout()?", "it important to call the response handler from within handleTimeout()?", "important to call the response handler from within handleTimeout()?", "to call the response handler from within handleTimeout()?", "call the response handler from within handleTimeout()?", "the response handler from within handleTimeout()?", "response handler from within handleTimeout()?", "handler from within handleTimeout()?", "from within handleTimeout()?", "within handleTimeout()?", "handleTimeout()?"], "tokens": ["Why", "is", "it", "important", "to", "call", "the", "response", "handler", "from", "within", "handleTimeout", "()?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1383296122", "fields": {"term": "What is the metrics-proxy?", "namespace": "open-p", "hash": 1383296122, "terms": ["What is the metrics-proxy?", "is the metrics-proxy?", "the metrics-proxy?", "metrics-proxy?"], "tokens": ["What", "is", "the", "metrics", "proxy", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1678805932", "fields": {"term": "What is the port for metrics-proxy?", "namespace": "open-p", "hash": 1678805932, "terms": ["What is the port for metrics-proxy?", "is the port for metrics-proxy?", "the port for metrics-proxy?", "port for metrics-proxy?", "for metrics-proxy?", "metrics-proxy?"], "tokens": ["What", "is", "the", "port", "for", "metrics", "proxy", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::618331516", "fields": {"term": "Which API serves the metric cache?", "namespace": "open-p", "hash": 618331516, "terms": ["Which API serves the metric cache?", "API serves the metric cache?", "serves the metric cache?", "the metric cache?", "metric cache?", "cache?"], "tokens": ["Which", "API", "serves", "the", "metric", "cache", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1478183919", "fields": {"term": "What is the process for metrics-proxy?", "namespace": "open-p", "hash": 1478183919, "terms": ["What is the process for metrics-proxy?", "is the process for metrics-proxy?", "the process for metrics-proxy?", "process for metrics-proxy?", "for metrics-proxy?", "metrics-proxy?"], "tokens": ["What", "is", "the", "process", "for", "metrics", "proxy", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-965390503", "fields": {"term": "What starts the metrics-proxy?", "namespace": "open-p", "hash": -965390503, "terms": ["What starts the metrics-proxy?", "starts the metrics-proxy?", "the metrics-proxy?", "metrics-proxy?"], "tokens": ["What", "starts", "the", "metrics", "proxy", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1873931287", "fields": {"term": "Is the metrics-proxy configurable?", "namespace": "open-p", "hash": -1873931287, "terms": ["Is the metrics-proxy configurable?", "the metrics-proxy configurable?", "metrics-proxy configurable?", "configurable?"], "tokens": ["Is", "the", "metrics", "proxy", "configurable", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1044387896", "fields": {"term": "What is the internal API for the metric cache?", "namespace": "open-p", "hash": -1044387896, "terms": ["What is the internal API for the metric cache?", "is the internal API for the metric cache?", "the internal API for the metric cache?", "internal API for the metric cache?", "API for the metric cache?", "for the metric cache?", "the metric cache?", "metric cache?", "cache?"], "tokens": ["What", "is", "the", "internal", "API", "for", "the", "metric", "cache", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1159988992", "fields": {"term": "Where is the node metrics API?", "namespace": "open-p", "hash": -1159988992, "terms": ["Where is the node metrics API?", "is the node metrics API?", "the node metrics API?", "node metrics API?", "metrics API?", "API?"], "tokens": ["Where", "is", "the", "node", "metrics", "API", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1213241467", "fields": {"term": "What port is the metrics proxy?", "namespace": "open-p", "hash": -1213241467, "terms": ["What port is the metrics proxy?", "port is the metrics proxy?", "is the metrics proxy?", "the metrics proxy?", "metrics proxy?", "proxy?"], "tokens": ["What", "port", "is", "the", "metrics", "proxy", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-243352335", "fields": {"term": "What is the default URL for the metrics API?", "namespace": "open-p", "hash": -243352335, "terms": ["What is the default URL for the metrics API?", "is the default URL for the metrics API?", "the default URL for the metrics API?", "default URL for the metrics API?", "URL for the metrics API?", "for the metrics API?", "the metrics API?", "metrics API?", "API?"], "tokens": ["What", "is", "the", "default", "URL", "for", "the", "metrics", "API", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1184545968", "fields": {"term": "What is the metrics API used for?", "namespace": "open-p", "hash": 1184545968, "terms": ["What is the metrics API used for?", "is the metrics API used for?", "the metrics API used for?", "metrics API used for?", "API used for?", "used for?", "for?"], "tokens": ["What", "is", "the", "metrics", "API", "used", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-359535654", "fields": {"term": "What is the overview of nodes, services and metrics APIs?", "namespace": "open-p", "hash": -359535654, "terms": ["What is the overview of nodes, services and metrics APIs?", "is the overview of nodes, services and metrics APIs?", "the overview of nodes, services and metrics APIs?", "overview of nodes, services and metrics APIs?", "of nodes, services and metrics APIs?", "nodes, services and metrics APIs?", "services and metrics APIs?", "and metrics APIs?", "metrics APIs?", "APIs?"], "tokens": ["What", "is", "the", "overview", "of", "nodes", "services", "and", "metrics", "APIs", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-560270098", "fields": {"term": "Where can I find examples of monitoring metrics/v1?", "namespace": "open-p", "hash": -560270098, "terms": ["Where can I find examples of monitoring metrics/v1?", "can I find examples of monitoring metrics/v1?", "I find examples of monitoring metrics/v1?", "find examples of monitoring metrics/v1?", "examples of monitoring metrics/v1?", "of monitoring metrics/v1?", "monitoring metrics/v1?", "metrics/v1?"], "tokens": ["Where", "can", "I", "find", "examples", "of", "monitoring", "metrics", "/", "v1", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1585434860", "fields": {"term": "What is the consumer parameter?", "namespace": "open-p", "hash": 1585434860, "terms": ["What is the consumer parameter?", "is the consumer parameter?", "the consumer parameter?", "consumer parameter?", "parameter?"], "tokens": ["What", "is", "the", "consumer", "parameter", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::1434761058", "fields": {"term": "What is the default metric set?", "namespace": "open-p", "hash": 1434761058, "terms": ["What is the default metric set?", "is the default metric set?", "the default metric set?", "default metric set?", "metric set?", "set?"], "tokens": ["What", "is", "the", "default", "metric", "set", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::2076572686", "fields": {"term": "What is the Vespa metric set?", "namespace": "open-p", "hash": 2076572686, "terms": ["What is the Vespa metric set?", "is the Vespa metric set?", "the Vespa metric set?", "Vespa metric set?", "metric set?", "set?"], "tokens": ["What", "is", "the", "Vespa", "metric", "set", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::821377424", "fields": {"term": "What are the built-in metrics?", "namespace": "open-p", "hash": 821377424, "terms": ["What are the built-in metrics?", "are the built-in metrics?", "the built-in metrics?", "built-in metrics?", "metrics?"], "tokens": ["What", "are", "the", "built", "in", "metrics", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::818064550", "fields": {"term": "What is the type of the consumer parameter?", "namespace": "open-p", "hash": 818064550, "terms": ["What is the type of the consumer parameter?", "is the type of the consumer parameter?", "the type of the consumer parameter?", "type of the consumer parameter?", "of the consumer parameter?", "the consumer parameter?", "consumer parameter?", "parameter?"], "tokens": ["What", "is", "the", "type", "of", "the", "consumer", "parameter", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1574202098", "fields": {"term": "What does setting an unknown consumer value return?", "namespace": "open-p", "hash": -1574202098, "terms": ["What does setting an unknown consumer value return?", "does setting an unknown consumer value return?", "setting an unknown consumer value return?", "an unknown consumer value return?", "unknown consumer value return?", "consumer value return?", "value return?", "return?"], "tokens": ["What", "does", "setting", "an", "unknown", "consumer", "value", "return", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1734147835", "fields": {"term": "How do I specify a response consumer?", "namespace": "open-p", "hash": -1734147835, "terms": ["How do I specify a response consumer?", "do I specify a response consumer?", "I specify a response consumer?", "specify a response consumer?", "a response consumer?", "response consumer?", "consumer?"], "tokens": ["How", "do", "I", "specify", "a", "response", "consumer", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::200725880", "fields": {"term": "What is the description of the consumer parameter?", "namespace": "open-p", "hash": 200725880, "terms": ["What is the description of the consumer parameter?", "is the description of the consumer parameter?", "the description of the consumer parameter?", "description of the consumer parameter?", "of the consumer parameter?", "the consumer parameter?", "consumer parameter?", "parameter?"], "tokens": ["What", "is", "the", "description", "of", "the", "consumer", "parameter", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::868056921", "fields": {"term": "What is a status code?", "namespace": "open-p", "hash": 868056921, "terms": ["What is a status code?", "is a status code?", "a status code?", "status code?", "code?"], "tokens": ["What", "is", "a", "status", "code", "?"], "corpus_count": 3, "document_count": 3}}, -{"put": "id:term:term::-1410771700", "fields": {"term": "What is code 200?", "namespace": "open-p", "hash": -1410771700, "terms": ["What is code 200?", "is code 200?", "code 200?", "200?"], "tokens": ["What", "is", "code", "200", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1751812386", "fields": {"term": "What does the metrics field contain?", "namespace": "open-p", "hash": 1751812386, "terms": ["What does the metrics field contain?", "does the metrics field contain?", "the metrics field contain?", "metrics field contain?", "field contain?", "contain?"], "tokens": ["What", "does", "the", "metrics", "field", "contain", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1012369750", "fields": {"term": "What is the status field?", "namespace": "open-p", "hash": 1012369750, "terms": ["What is the status field?", "is the status field?", "the status field?", "status field?", "field?"], "tokens": ["What", "is", "the", "status", "field", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-671325299", "fields": {"term": "What is the possible value of the status field?", "namespace": "open-p", "hash": -671325299, "terms": ["What is the possible value of the status field?", "is the possible value of the status field?", "the possible value of the status field?", "possible value of the status field?", "value of the status field?", "of the status field?", "the status field?", "status field?", "field?"], "tokens": ["What", "is", "the", "possible", "value", "of", "the", "status", "field", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-311853545", "fields": {"term": "What is application metrics API?", "namespace": "open-p", "hash": -311853545, "terms": ["What is application metrics API?", "is application metrics API?", "application metrics API?", "metrics API?", "API?"], "tokens": ["What", "is", "application", "metrics", "API", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-811436050", "fields": {"term": "What port does the metrics proxy use?", "namespace": "open-p", "hash": -811436050, "terms": ["What port does the metrics proxy use?", "port does the metrics proxy use?", "does the metrics proxy use?", "the metrics proxy use?", "metrics proxy use?", "proxy use?", "use?"], "tokens": ["What", "port", "does", "the", "metrics", "proxy", "use", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::317644763", "fields": {"term": "What is the metrics API aggregation?", "namespace": "open-p", "hash": 317644763, "terms": ["What is the metrics API aggregation?", "is the metrics API aggregation?", "the metrics API aggregation?", "metrics API aggregation?", "API aggregation?", "aggregation?"], "tokens": ["What", "is", "the", "metrics", "API", "aggregation", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::967074541", "fields": {"term": "What is the 'consumer' parameter?", "namespace": "open-p", "hash": 967074541, "terms": ["What is the 'consumer' parameter?", "is the 'consumer' parameter?", "the 'consumer' parameter?", "'consumer' parameter?", "parameter?"], "tokens": ["What", "is", "the", "'", "consumer", "'", "parameter", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1269478165", "fields": {"term": "Where can I find details for metrics/v1?", "namespace": "open-p", "hash": -1269478165, "terms": ["Where can I find details for metrics/v1?", "can I find details for metrics/v1?", "I find details for metrics/v1?", "find details for metrics/v1?", "details for metrics/v1?", "for metrics/v1?", "metrics/v1?"], "tokens": ["Where", "can", "I", "find", "details", "for", "metrics", "/", "v1", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-255879162", "fields": {"term": "What does the 'consumer' parameter specify?", "namespace": "open-p", "hash": -255879162, "terms": ["What does the 'consumer' parameter specify?", "does the 'consumer' parameter specify?", "the 'consumer' parameter specify?", "'consumer' parameter specify?", "parameter specify?", "specify?"], "tokens": ["What", "does", "the", "'", "consumer", "'", "parameter", "specify", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1003043672", "fields": {"term": "Which document describes the 'consumer' parameter?", "namespace": "open-p", "hash": -1003043672, "terms": ["Which document describes the 'consumer' parameter?", "document describes the 'consumer' parameter?", "describes the 'consumer' parameter?", "the 'consumer' parameter?", "'consumer' parameter?", "parameter?"], "tokens": ["Which", "document", "describes", "the", "'", "consumer", "'", "parameter", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1689789967", "fields": {"term": "What is the response consumer for?", "namespace": "open-p", "hash": 1689789967, "terms": ["What is the response consumer for?", "is the response consumer for?", "the response consumer for?", "response consumer for?", "consumer for?", "for?"], "tokens": ["What", "is", "the", "response", "consumer", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-549169743", "fields": {"term": "What is the format of the 'consumer' parameter?", "namespace": "open-p", "hash": -549169743, "terms": ["What is the format of the 'consumer' parameter?", "is the format of the 'consumer' parameter?", "the format of the 'consumer' parameter?", "format of the 'consumer' parameter?", "of the 'consumer' parameter?", "the 'consumer' parameter?", "'consumer' parameter?", "parameter?"], "tokens": ["What", "is", "the", "format", "of", "the", "'", "consumer", "'", "parameter", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1504601256", "fields": {"term": "What does 200 mean?", "namespace": "open-p", "hash": -1504601256, "terms": ["What does 200 mean?", "does 200 mean?", "200 mean?", "mean?"], "tokens": ["What", "does", "200", "mean", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::947589046", "fields": {"term": "What is a list of status codes?", "namespace": "open-p", "hash": 947589046, "terms": ["What is a list of status codes?", "is a list of status codes?", "a list of status codes?", "list of status codes?", "of status codes?", "status codes?", "codes?"], "tokens": ["What", "is", "a", "list", "of", "status", "codes", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1777599212", "fields": {"term": "What is the format of responses?", "namespace": "open-p", "hash": 1777599212, "terms": ["What is the format of responses?", "is the format of responses?", "the format of responses?", "format of responses?", "of responses?", "responses?"], "tokens": ["What", "is", "the", "format", "of", "responses", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-575916580", "fields": {"term": "What are the fields in responses?", "namespace": "open-p", "hash": -575916580, "terms": ["What are the fields in responses?", "are the fields in responses?", "the fields in responses?", "fields in responses?", "in responses?", "responses?"], "tokens": ["What", "are", "the", "fields", "in", "responses", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-355396586", "fields": {"term": "What is the nodes element?", "namespace": "open-p", "hash": -355396586, "terms": ["What is the nodes element?", "is the nodes element?", "the nodes element?", "nodes element?", "element?"], "tokens": ["What", "is", "the", "nodes", "element", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::-149015128", "fields": {"term": "What is the hostname field?", "namespace": "open-p", "hash": -149015128, "terms": ["What is the hostname field?", "is the hostname field?", "the hostname field?", "hostname field?", "field?"], "tokens": ["What", "is", "the", "hostname", "field", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1008979484", "fields": {"term": "What is the role field?", "namespace": "open-p", "hash": 1008979484, "terms": ["What is the role field?", "is the role field?", "the role field?", "role field?", "field?"], "tokens": ["What", "is", "the", "role", "field", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-55956310", "fields": {"term": "What is the service object?", "namespace": "open-p", "hash": -55956310, "terms": ["What is the service object?", "is the service object?", "the service object?", "service object?", "object?"], "tokens": ["What", "is", "the", "service", "object", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2047417674", "fields": {"term": "What are the metric endpoints?", "namespace": "open-p", "hash": -2047417674, "terms": ["What are the metric endpoints?", "are the metric endpoints?", "the metric endpoints?", "metric endpoints?", "endpoints?"], "tokens": ["What", "are", "the", "metric", "endpoints", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-56886076", "fields": {"term": "How do I get metric values?", "namespace": "open-p", "hash": -56886076, "terms": ["How do I get metric values?", "do I get metric values?", "I get metric values?", "get metric values?", "metric values?", "values?"], "tokens": ["How", "do", "I", "get", "metric", "values", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1041065416", "fields": {"term": "What is * /metrics/v2/values*?", "namespace": "open-p", "hash": 1041065416, "terms": ["What is * /metrics/v2/values*?", "is * /metrics/v2/values*?", "* /metrics/v2/values*?", "/metrics/v2/values*?"], "tokens": ["What", "is", "*", "/", "metrics", "/", "v2", "/", "values", "*?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1525398822", "fields": {"term": "What is * /prometheus/v1/values*?", "namespace": "open-p", "hash": 1525398822, "terms": ["What is * /prometheus/v1/values*?", "is * /prometheus/v1/values*?", "* /prometheus/v1/values*?", "/prometheus/v1/values*?"], "tokens": ["What", "is", "*", "/", "prometheus", "/", "v1", "/", "values", "*?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-639125945", "fields": {"term": "What is the purpose of metrics-proxy?", "namespace": "open-p", "hash": -639125945, "terms": ["What is the purpose of metrics-proxy?", "is the purpose of metrics-proxy?", "the purpose of metrics-proxy?", "purpose of metrics-proxy?", "of metrics-proxy?", "metrics-proxy?"], "tokens": ["What", "is", "the", "purpose", "of", "metrics", "proxy", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1260686027", "fields": {"term": "What is the purpose of using filter for */metrics/v2/values*?", "namespace": "open-p", "hash": 1260686027, "terms": ["What is the purpose of using filter for */metrics/v2/values*?", "is the purpose of using filter for */metrics/v2/values*?", "the purpose of using filter for */metrics/v2/values*?", "purpose of using filter for */metrics/v2/values*?", "of using filter for */metrics/v2/values*?", "using filter for */metrics/v2/values*?", "filter for */metrics/v2/values*?", "for */metrics/v2/values*?", "*/metrics/v2/values*?"], "tokens": ["What", "is", "the", "purpose", "of", "using", "filter", "for", "*/", "metrics", "/", "v2", "/", "values", "*?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::38003304", "fields": {"term": "How do you aggregate max values?", "namespace": "open-p", "hash": 38003304, "terms": ["How do you aggregate max values?", "do you aggregate max values?", "you aggregate max values?", "aggregate max values?", "max values?", "values?"], "tokens": ["How", "do", "you", "aggregate", "max", "values", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::365355402", "fields": {"term": "How to compute average values?", "namespace": "open-p", "hash": 365355402, "terms": ["How to compute average values?", "to compute average values?", "compute average values?", "average values?", "values?"], "tokens": ["How", "to", "compute", "average", "values", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-419243239", "fields": {"term": "What is the guideline for using last value suffix?", "namespace": "open-p", "hash": -419243239, "terms": ["What is the guideline for using last value suffix?", "is the guideline for using last value suffix?", "the guideline for using last value suffix?", "guideline for using last value suffix?", "for using last value suffix?", "using last value suffix?", "last value suffix?", "value suffix?", "suffix?"], "tokens": ["What", "is", "the", "guideline", "for", "using", "last", "value", "suffix", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1831440795", "fields": {"term": "What are the recommended aggregation for count suffix?", "namespace": "open-p", "hash": -1831440795, "terms": ["What are the recommended aggregation for count suffix?", "are the recommended aggregation for count suffix?", "the recommended aggregation for count suffix?", "recommended aggregation for count suffix?", "aggregation for count suffix?", "for count suffix?", "count suffix?", "suffix?"], "tokens": ["What", "are", "the", "recommended", "aggregation", "for", "count", "suffix", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1627198871", "fields": {"term": "What is the guideline for using average suffix?", "namespace": "open-p", "hash": 1627198871, "terms": ["What is the guideline for using average suffix?", "is the guideline for using average suffix?", "the guideline for using average suffix?", "guideline for using average suffix?", "for using average suffix?", "using average suffix?", "average suffix?", "suffix?"], "tokens": ["What", "is", "the", "guideline", "for", "using", "average", "suffix", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1000059987", "fields": {"term": "What is the purpose of Vespa metrics?", "namespace": "open-p", "hash": -1000059987, "terms": ["What is the purpose of Vespa metrics?", "is the purpose of Vespa metrics?", "the purpose of Vespa metrics?", "purpose of Vespa metrics?", "of Vespa metrics?", "Vespa metrics?", "metrics?"], "tokens": ["What", "is", "the", "purpose", "of", "Vespa", "metrics", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1568309666", "fields": {"term": "What are Node metrics?", "namespace": "open-p", "hash": -1568309666, "terms": ["What are Node metrics?", "are Node metrics?", "Node metrics?", "metrics?"], "tokens": ["What", "are", "Node", "metrics", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2053733657", "fields": {"term": "Where are node metrics listed?", "namespace": "open-p", "hash": 2053733657, "terms": ["Where are node metrics listed?", "are node metrics listed?", "node metrics listed?", "metrics listed?", "listed?"], "tokens": ["Where", "are", "node", "metrics", "listed", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-761723100", "fields": {"term": "What is the metric type?", "namespace": "open-p", "hash": -761723100, "terms": ["What is the metric type?", "is the metric type?", "the metric type?", "metric type?", "type?"], "tokens": ["What", "is", "the", "metric", "type", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1316517321", "fields": {"term": "What is the default metric-set?", "namespace": "open-p", "hash": 1316517321, "terms": ["What is the default metric-set?", "is the default metric-set?", "the default metric-set?", "default metric-set?", "metric-set?"], "tokens": ["What", "is", "the", "default", "metric", "set", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1205001575", "fields": {"term": "What is the Vespa metric-set?", "namespace": "open-p", "hash": -1205001575, "terms": ["What is the Vespa metric-set?", "is the Vespa metric-set?", "the Vespa metric-set?", "Vespa metric-set?", "metric-set?"], "tokens": ["What", "is", "the", "Vespa", "metric", "set", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1812436138", "fields": {"term": "What is the consumer request parameter?", "namespace": "open-p", "hash": 1812436138, "terms": ["What is the consumer request parameter?", "is the consumer request parameter?", "the consumer request parameter?", "consumer request parameter?", "request parameter?", "parameter?"], "tokens": ["What", "is", "the", "consumer", "request", "parameter", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::362381841", "fields": {"term": "What is the difference between /metrics/v2/values and /prometheus/v1/values?", "namespace": "open-p", "hash": 362381841, "terms": ["What is the difference between /metrics/v2/values and /prometheus/v1/values?", "is the difference between /metrics/v2/values and /prometheus/v1/values?", "the difference between /metrics/v2/values and /prometheus/v1/values?", "difference between /metrics/v2/values and /prometheus/v1/values?", "between /metrics/v2/values and /prometheus/v1/values?", "/metrics/v2/values and /prometheus/v1/values?", "and /prometheus/v1/values?", "/prometheus/v1/values?"], "tokens": ["What", "is", "the", "difference", "between", "/", "metrics", "/", "v2", "/", "values", "and", "/", "prometheus", "/", "v1", "/", "values", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1772330949", "fields": {"term": "What is the Vespa metric naming scheme?", "namespace": "open-p", "hash": -1772330949, "terms": ["What is the Vespa metric naming scheme?", "is the Vespa metric naming scheme?", "the Vespa metric naming scheme?", "Vespa metric naming scheme?", "metric naming scheme?", "naming scheme?", "scheme?"], "tokens": ["What", "is", "the", "Vespa", "metric", "naming", "scheme", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1595765191", "fields": {"term": "What are the typical metric suffixes?", "namespace": "open-p", "hash": -1595765191, "terms": ["What are the typical metric suffixes?", "are the typical metric suffixes?", "the typical metric suffixes?", "typical metric suffixes?", "metric suffixes?", "suffixes?"], "tokens": ["What", "are", "the", "typical", "metric", "suffixes", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1539478969", "fields": {"term": "What are Vespa metrics?", "namespace": "open-p", "hash": -1539478969, "terms": ["What are Vespa metrics?", "are Vespa metrics?", "Vespa metrics?", "metrics?"], "tokens": ["What", "are", "Vespa", "metrics", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1792271134", "fields": {"term": "Where can I find container metrics?", "namespace": "open-p", "hash": 1792271134, "terms": ["Where can I find container metrics?", "can I find container metrics?", "I find container metrics?", "find container metrics?", "container metrics?", "metrics?"], "tokens": ["Where", "can", "I", "find", "container", "metrics", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1407186092", "fields": {"term": "How to add custom metrics?", "namespace": "open-p", "hash": -1407186092, "terms": ["How to add custom metrics?", "to add custom metrics?", "add custom metrics?", "custom metrics?", "metrics?"], "tokens": ["How", "to", "add", "custom", "metrics", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-379008263", "fields": {"term": "What are MetricReceiver and Gauge?", "namespace": "open-p", "hash": -379008263, "terms": ["What are MetricReceiver and Gauge?", "are MetricReceiver and Gauge?", "MetricReceiver and Gauge?", "and Gauge?", "Gauge?"], "tokens": ["What", "are", "MetricReceiver", "and", "Gauge", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1951544156", "fields": {"term": "Where is an example for adding custom metrics?", "namespace": "open-p", "hash": 1951544156, "terms": ["Where is an example for adding custom metrics?", "is an example for adding custom metrics?", "an example for adding custom metrics?", "example for adding custom metrics?", "for adding custom metrics?", "adding custom metrics?", "custom metrics?", "metrics?"], "tokens": ["Where", "is", "an", "example", "for", "adding", "custom", "metrics", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-121041014", "fields": {"term": "What is MetricReceiver?", "namespace": "open-p", "hash": -121041014, "terms": ["What is MetricReceiver?", "is MetricReceiver?", "MetricReceiver?"], "tokens": ["What", "is", "MetricReceiver", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1127888032", "fields": {"term": "Can MetricReceiver object change?", "namespace": "open-p", "hash": -1127888032, "terms": ["Can MetricReceiver object change?", "MetricReceiver object change?", "object change?", "change?"], "tokens": ["Can", "MetricReceiver", "object", "change", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1231075978", "fields": {"term": "How do we store MetricReceiver reference?", "namespace": "open-p", "hash": -1231075978, "terms": ["How do we store MetricReceiver reference?", "do we store MetricReceiver reference?", "we store MetricReceiver reference?", "store MetricReceiver reference?", "MetricReceiver reference?", "reference?"], "tokens": ["How", "do", "we", "store", "MetricReceiver", "reference", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1088461500", "fields": {"term": "What happens if we share Component references?", "namespace": "open-p", "hash": 1088461500, "terms": ["What happens if we share Component references?", "happens if we share Component references?", "if we share Component references?", "we share Component references?", "share Component references?", "Component references?", "references?"], "tokens": ["What", "happens", "if", "we", "share", "Component", "references", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1295046983", "fields": {"term": "How long is the object valid?", "namespace": "open-p", "hash": -1295046983, "terms": ["How long is the object valid?", "long is the object valid?", "is the object valid?", "the object valid?", "object valid?", "valid?"], "tokens": ["How", "long", "is", "the", "object", "valid", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1496023132", "fields": {"term": "What is a common place to store MetricReceiver?", "namespace": "open-p", "hash": -1496023132, "terms": ["What is a common place to store MetricReceiver?", "is a common place to store MetricReceiver?", "a common place to store MetricReceiver?", "common place to store MetricReceiver?", "place to store MetricReceiver?", "to store MetricReceiver?", "store MetricReceiver?", "MetricReceiver?"], "tokens": ["What", "is", "a", "common", "place", "to", "store", "MetricReceiver", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1038435946", "fields": {"term": "Can the object update to a new reference?", "namespace": "open-p", "hash": 1038435946, "terms": ["Can the object update to a new reference?", "the object update to a new reference?", "object update to a new reference?", "update to a new reference?", "to a new reference?", "a new reference?", "new reference?", "reference?"], "tokens": ["Can", "the", "object", "update", "to", "a", "new", "reference", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::85472201", "fields": {"term": "What is the lifetime of the MetricReceiver object?", "namespace": "open-p", "hash": 85472201, "terms": ["What is the lifetime of the MetricReceiver object?", "is the lifetime of the MetricReceiver object?", "the lifetime of the MetricReceiver object?", "lifetime of the MetricReceiver object?", "of the MetricReceiver object?", "the MetricReceiver object?", "MetricReceiver object?", "object?"], "tokens": ["What", "is", "the", "lifetime", "of", "the", "MetricReceiver", "object", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1856407136", "fields": {"term": "What are metric histograms?", "namespace": "open-p", "hash": -1856407136, "terms": ["What are metric histograms?", "are metric histograms?", "metric histograms?", "histograms?"], "tokens": ["What", "are", "metric", "histograms", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1583697081", "fields": {"term": "How to create metric histograms?", "namespace": "open-p", "hash": -1583697081, "terms": ["How to create metric histograms?", "to create metric histograms?", "create metric histograms?", "metric histograms?", "histograms?"], "tokens": ["How", "to", "create", "metric", "histograms", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-206149662", "fields": {"term": "What is this tutorial about?", "namespace": "open-p", "hash": -206149662, "terms": ["What is this tutorial about?", "is this tutorial about?", "this tutorial about?", "tutorial about?", "about?"], "tokens": ["What", "is", "this", "tutorial", "about", "?"], "corpus_count": 3, "document_count": 3}}, -{"put": "id:term:term::1807888431", "fields": {"term": "What is hot swap?", "namespace": "open-p", "hash": 1807888431, "terms": ["What is hot swap?", "is hot swap?", "hot swap?", "swap?"], "tokens": ["What", "is", "hot", "swap", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::-1301403424", "fields": {"term": "What are recommendation models?", "namespace": "open-p", "hash": -1301403424, "terms": ["What are recommendation models?", "are recommendation models?", "recommendation models?", "models?"], "tokens": ["What", "are", "recommendation", "models", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::785381361", "fields": {"term": "What previous tutorials did this series include?", "namespace": "open-p", "hash": 785381361, "terms": ["What previous tutorials did this series include?", "previous tutorials did this series include?", "tutorials did this series include?", "did this series include?", "this series include?", "series include?", "include?"], "tokens": ["What", "previous", "tutorials", "did", "this", "series", "include", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::468658575", "fields": {"term": "What is assumed for simplicity in this tutorial?", "namespace": "open-p", "hash": 468658575, "terms": ["What is assumed for simplicity in this tutorial?", "is assumed for simplicity in this tutorial?", "assumed for simplicity in this tutorial?", "for simplicity in this tutorial?", "simplicity in this tutorial?", "in this tutorial?", "this tutorial?", "tutorial?"], "tokens": ["What", "is", "assumed", "for", "simplicity", "in", "this", "tutorial", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1643227882", "fields": {"term": "What are the additional requirements?", "namespace": "open-p", "hash": -1643227882, "terms": ["What are the additional requirements?", "are the additional requirements?", "the additional requirements?", "additional requirements?", "requirements?"], "tokens": ["What", "are", "the", "additional", "requirements", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::269615685", "fields": {"term": "What is shared model-agnostic data?", "namespace": "open-p", "hash": 269615685, "terms": ["What is shared model-agnostic data?", "is shared model-agnostic data?", "shared model-agnostic data?", "model-agnostic data?", "data?"], "tokens": ["What", "is", "shared", "model", "agnostic", "data", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1876659863", "fields": {"term": "How should cleanup be handled?", "namespace": "open-p", "hash": -1876659863, "terms": ["How should cleanup be handled?", "should cleanup be handled?", "cleanup be handled?", "be handled?", "handled?"], "tokens": ["How", "should", "cleanup", "be", "handled", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-577925159", "fields": {"term": "What is the central requirement for hot swap?", "namespace": "open-p", "hash": -577925159, "terms": ["What is the central requirement for hot swap?", "is the central requirement for hot swap?", "the central requirement for hot swap?", "central requirement for hot swap?", "requirement for hot swap?", "for hot swap?", "hot swap?", "swap?"], "tokens": ["What", "is", "the", "central", "requirement", "for", "hot", "swap", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1664788980", "fields": {"term": "What is external versioning?", "namespace": "open-p", "hash": 1664788980, "terms": ["What is external versioning?", "is external versioning?", "external versioning?", "versioning?"], "tokens": ["What", "is", "external", "versioning", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1777413853", "fields": {"term": "What is internal versioning?", "namespace": "open-p", "hash": 1777413853, "terms": ["What is internal versioning?", "is internal versioning?", "internal versioning?", "versioning?"], "tokens": ["What", "is", "internal", "versioning", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-234346807", "fields": {"term": "How does Vespa implement hot swap?", "namespace": "open-p", "hash": -234346807, "terms": ["How does Vespa implement hot swap?", "does Vespa implement hot swap?", "Vespa implement hot swap?", "implement hot swap?", "hot swap?", "swap?"], "tokens": ["How", "does", "Vespa", "implement", "hot", "swap", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1120790466", "fields": {"term": "How are recommendation models separated?", "namespace": "open-p", "hash": 1120790466, "terms": ["How are recommendation models separated?", "are recommendation models separated?", "recommendation models separated?", "models separated?", "separated?"], "tokens": ["How", "are", "recommendation", "models", "separated", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-258800420", "fields": {"term": "What is a model code?", "namespace": "open-p", "hash": -258800420, "terms": ["What is a model code?", "is a model code?", "a model code?", "model code?", "code?"], "tokens": ["What", "is", "a", "model", "code", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-972769355", "fields": {"term": "Where is the model code stored?", "namespace": "open-p", "hash": -972769355, "terms": ["Where is the model code stored?", "is the model code stored?", "the model code stored?", "model code stored?", "code stored?", "stored?"], "tokens": ["Where", "is", "the", "model", "code", "stored", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-990298177", "fields": {"term": "What is the purpose of the model and version fields?", "namespace": "open-p", "hash": -990298177, "terms": ["What is the purpose of the model and version fields?", "is the purpose of the model and version fields?", "the purpose of the model and version fields?", "purpose of the model and version fields?", "of the model and version fields?", "the model and version fields?", "model and version fields?", "and version fields?", "version fields?", "fields?"], "tokens": ["What", "is", "the", "purpose", "of", "the", "model", "and", "version", "fields", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::665702599", "fields": {"term": "How should the feeding procedures of each model be separated?", "namespace": "open-p", "hash": 665702599, "terms": ["How should the feeding procedures of each model be separated?", "should the feeding procedures of each model be separated?", "the feeding procedures of each model be separated?", "feeding procedures of each model be separated?", "procedures of each model be separated?", "of each model be separated?", "each model be separated?", "model be separated?", "be separated?", "separated?"], "tokens": ["How", "should", "the", "feeding", "procedures", "of", "each", "model", "be", "separated", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1975633139", "fields": {"term": "What is Vespa's parent-child relations?", "namespace": "open-p", "hash": -1975633139, "terms": ["What is Vespa's parent-child relations?", "is Vespa's parent-child relations?", "Vespa's parent-child relations?", "parent-child relations?", "relations?"], "tokens": ["What", "is", "Vespa", "'", "s", "parent", "child", "relations", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-466404968", "fields": {"term": "What is the news_model schema?", "namespace": "open-p", "hash": -466404968, "terms": ["What is the news_model schema?", "is the news_model schema?", "the news_model schema?", "news_model schema?", "schema?"], "tokens": ["What", "is", "the", "news_model", "schema", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::21493033", "fields": {"term": "What is the purpose of the news_model schema?", "namespace": "open-p", "hash": 21493033, "terms": ["What is the purpose of the news_model schema?", "is the purpose of the news_model schema?", "the purpose of the news_model schema?", "purpose of the news_model schema?", "of the news_model schema?", "the news_model schema?", "news_model schema?", "schema?"], "tokens": ["What", "is", "the", "purpose", "of", "the", "news_model", "schema", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-53646085", "fields": {"term": "What data is model agnostic?", "namespace": "open-p", "hash": -53646085, "terms": ["What data is model agnostic?", "data is model agnostic?", "is model agnostic?", "model agnostic?", "agnostic?"], "tokens": ["What", "data", "is", "model", "agnostic", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1048862775", "fields": {"term": "What is the format of the tuples used to create prediction models?", "namespace": "open-p", "hash": 1048862775, "terms": ["What is the format of the tuples used to create prediction models?", "is the format of the tuples used to create prediction models?", "the format of the tuples used to create prediction models?", "format of the tuples used to create prediction models?", "of the tuples used to create prediction models?", "the tuples used to create prediction models?", "tuples used to create prediction models?", "used to create prediction models?", "to create prediction models?", "create prediction models?", "prediction models?", "models?"], "tokens": ["What", "is", "the", "format", "of", "the", "tuples", "used", "to", "create", "prediction", "models", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-488239065", "fields": {"term": "What is the advantage of processing news article data and event data separately?", "namespace": "open-p", "hash": -488239065, "terms": ["What is the advantage of processing news article data and event data separately?", "is the advantage of processing news article data and event data separately?", "the advantage of processing news article data and event data separately?", "advantage of processing news article data and event data separately?", "of processing news article data and event data separately?", "processing news article data and event data separately?", "news article data and event data separately?", "article data and event data separately?", "data and event data separately?", "and event data separately?", "event data separately?", "data separately?", "separately?"], "tokens": ["What", "is", "the", "advantage", "of", "processing", "news", "article", "data", "and", "event", "data", "separately", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1037191108", "fields": {"term": "How is model-related fields extracted into a new schema?", "namespace": "open-p", "hash": -1037191108, "terms": ["How is model-related fields extracted into a new schema?", "is model-related fields extracted into a new schema?", "model-related fields extracted into a new schema?", "fields extracted into a new schema?", "extracted into a new schema?", "into a new schema?", "a new schema?", "new schema?", "schema?"], "tokens": ["How", "is", "model", "related", "fields", "extracted", "into", "a", "new", "schema", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::582465047", "fields": {"term": "What fields are imported from the parent document to the child document in Vespa's parent-child relations?", "namespace": "open-p", "hash": 582465047, "terms": ["What fields are imported from the parent document to the child document in Vespa's parent-child relations?", "fields are imported from the parent document to the child document in Vespa's parent-child relations?", "are imported from the parent document to the child document in Vespa's parent-child relations?", "imported from the parent document to the child document in Vespa's parent-child relations?", "from the parent document to the child document in Vespa's parent-child relations?", "the parent document to the child document in Vespa's parent-child relations?", "parent document to the child document in Vespa's parent-child relations?", "document to the child document in Vespa's parent-child relations?", "to the child document in Vespa's parent-child relations?", "the child document in Vespa's parent-child relations?", "child document in Vespa's parent-child relations?", "document in Vespa's parent-child relations?", "in Vespa's parent-child relations?", "Vespa's parent-child relations?", "parent-child relations?", "relations?"], "tokens": ["What", "fields", "are", "imported", "from", "the", "parent", "document", "to", "the", "child", "document", "in", "Vespa", "'", "s", "parent", "child", "relations", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1426795711", "fields": {"term": "What is the garbage collection mechanism?", "namespace": "open-p", "hash": 1426795711, "terms": ["What is the garbage collection mechanism?", "is the garbage collection mechanism?", "the garbage collection mechanism?", "garbage collection mechanism?", "collection mechanism?", "mechanism?"], "tokens": ["What", "is", "the", "garbage", "collection", "mechanism", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1765499495", "fields": {"term": "How does Vespa filter out obsolete documents?", "namespace": "open-p", "hash": -1765499495, "terms": ["How does Vespa filter out obsolete documents?", "does Vespa filter out obsolete documents?", "Vespa filter out obsolete documents?", "filter out obsolete documents?", "out obsolete documents?", "obsolete documents?", "documents?"], "tokens": ["How", "does", "Vespa", "filter", "out", "obsolete", "documents", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-581403052", "fields": {"term": "What are the version fields?", "namespace": "open-p", "hash": -581403052, "terms": ["What are the version fields?", "are the version fields?", "the version fields?", "version fields?", "fields?"], "tokens": ["What", "are", "the", "version", "fields", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::139626832", "fields": {"term": "What is the purpose of the config_ref field?", "namespace": "open-p", "hash": 139626832, "terms": ["What is the purpose of the config_ref field?", "is the purpose of the config_ref field?", "the purpose of the config_ref field?", "purpose of the config_ref field?", "of the config_ref field?", "the config_ref field?", "config_ref field?", "field?"], "tokens": ["What", "is", "the", "purpose", "of", "the", "config_ref", "field", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1787558091", "fields": {"term": "What is the relation between version and latest_version fields?", "namespace": "open-p", "hash": -1787558091, "terms": ["What is the relation between version and latest_version fields?", "is the relation between version and latest_version fields?", "the relation between version and latest_version fields?", "relation between version and latest_version fields?", "between version and latest_version fields?", "version and latest_version fields?", "and latest_version fields?", "latest_version fields?", "fields?"], "tokens": ["What", "is", "the", "relation", "between", "version", "and", "latest_version", "fields", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1869017354", "fields": {"term": "What are read and write versions?", "namespace": "open-p", "hash": -1869017354, "terms": ["What are read and write versions?", "are read and write versions?", "read and write versions?", "and write versions?", "write versions?", "versions?"], "tokens": ["What", "are", "read", "and", "write", "versions", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::929970048", "fields": {"term": "What is `next_version` field?", "namespace": "open-p", "hash": 929970048, "terms": ["What is `next_version` field?", "is `next_version` field?", "`next_version` field?", "field?"], "tokens": ["What", "is", "`", "next_version", "`", "field", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::211589028", "fields": {"term": "What is `version` field?", "namespace": "open-p", "hash": 211589028, "terms": ["What is `version` field?", "is `version` field?", "`version` field?", "field?"], "tokens": ["What", "is", "`", "version", "`", "field", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1045484808", "fields": {"term": "Why use separate versions?", "namespace": "open-p", "hash": -1045484808, "terms": ["Why use separate versions?", "use separate versions?", "separate versions?", "versions?"], "tokens": ["Why", "use", "separate", "versions", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1779739375", "fields": {"term": "What happens during unsuccessful feedings?", "namespace": "open-p", "hash": -1779739375, "terms": ["What happens during unsuccessful feedings?", "happens during unsuccessful feedings?", "during unsuccessful feedings?", "unsuccessful feedings?", "feedings?"], "tokens": ["What", "happens", "during", "unsuccessful", "feedings", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1778941551", "fields": {"term": "When should the active set be modified?", "namespace": "open-p", "hash": -1778941551, "terms": ["When should the active set be modified?", "should the active set be modified?", "the active set be modified?", "active set be modified?", "set be modified?", "be modified?", "modified?"], "tokens": ["When", "should", "the", "active", "set", "be", "modified", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-733477793", "fields": {"term": "What are the issues with feeding in multiple regions?", "namespace": "open-p", "hash": -733477793, "terms": ["What are the issues with feeding in multiple regions?", "are the issues with feeding in multiple regions?", "the issues with feeding in multiple regions?", "issues with feeding in multiple regions?", "with feeding in multiple regions?", "feeding in multiple regions?", "in multiple regions?", "multiple regions?", "regions?"], "tokens": ["What", "are", "the", "issues", "with", "feeding", "in", "multiple", "regions", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1995807058", "fields": {"term": "How can the version and model mismatch be avoided?", "namespace": "open-p", "hash": 1995807058, "terms": ["How can the version and model mismatch be avoided?", "can the version and model mismatch be avoided?", "the version and model mismatch be avoided?", "version and model mismatch be avoided?", "and model mismatch be avoided?", "model mismatch be avoided?", "mismatch be avoided?", "be avoided?", "avoided?"], "tokens": ["How", "can", "the", "version", "and", "model", "mismatch", "be", "avoided", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1576528030", "fields": {"term": "What is a per-region endpoint?", "namespace": "open-p", "hash": 1576528030, "terms": ["What is a per-region endpoint?", "is a per-region endpoint?", "a per-region endpoint?", "per-region endpoint?", "endpoint?"], "tokens": ["What", "is", "a", "per", "region", "endpoint", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-694320017", "fields": {"term": "How can we achieve stickiness in follow-up queries?", "namespace": "open-p", "hash": -694320017, "terms": ["How can we achieve stickiness in follow-up queries?", "can we achieve stickiness in follow-up queries?", "we achieve stickiness in follow-up queries?", "achieve stickiness in follow-up queries?", "stickiness in follow-up queries?", "in follow-up queries?", "follow-up queries?", "queries?"], "tokens": ["How", "can", "we", "achieve", "stickiness", "in", "follow", "up", "queries", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-584458141", "fields": {"term": "What does hot-swap schema introduce?", "namespace": "open-p", "hash": -584458141, "terms": ["What does hot-swap schema introduce?", "does hot-swap schema introduce?", "hot-swap schema introduce?", "schema introduce?", "introduce?"], "tokens": ["What", "does", "hot", "swap", "schema", "introduce", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::234695918", "fields": {"term": "What does the hot-swap schema enable?", "namespace": "open-p", "hash": 234695918, "terms": ["What does the hot-swap schema enable?", "does the hot-swap schema enable?", "the hot-swap schema enable?", "hot-swap schema enable?", "schema enable?", "enable?"], "tokens": ["What", "does", "the", "hot", "swap", "schema", "enable", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::696812894", "fields": {"term": "Why is hot-swap schema important?", "namespace": "open-p", "hash": 696812894, "terms": ["Why is hot-swap schema important?", "is hot-swap schema important?", "hot-swap schema important?", "schema important?", "important?"], "tokens": ["Why", "is", "hot", "swap", "schema", "important", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1772927990", "fields": {"term": "What types are introduced by hot-swap schema?", "namespace": "open-p", "hash": -1772927990, "terms": ["What types are introduced by hot-swap schema?", "types are introduced by hot-swap schema?", "are introduced by hot-swap schema?", "introduced by hot-swap schema?", "by hot-swap schema?", "hot-swap schema?", "schema?"], "tokens": ["What", "types", "are", "introduced", "by", "hot", "swap", "schema", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1526874526", "fields": {"term": "What fields are introduced by hot-swap schema?", "namespace": "open-p", "hash": -1526874526, "terms": ["What fields are introduced by hot-swap schema?", "fields are introduced by hot-swap schema?", "are introduced by hot-swap schema?", "introduced by hot-swap schema?", "by hot-swap schema?", "hot-swap schema?", "schema?"], "tokens": ["What", "fields", "are", "introduced", "by", "hot", "swap", "schema", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2030799804", "fields": {"term": "What relations are introduced by hot-swap schema?", "namespace": "open-p", "hash": 2030799804, "terms": ["What relations are introduced by hot-swap schema?", "relations are introduced by hot-swap schema?", "are introduced by hot-swap schema?", "introduced by hot-swap schema?", "by hot-swap schema?", "hot-swap schema?", "schema?"], "tokens": ["What", "relations", "are", "introduced", "by", "hot", "swap", "schema", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-105958151", "fields": {"term": "Does hot-swap schema increase application complexity?", "namespace": "open-p", "hash": -105958151, "terms": ["Does hot-swap schema increase application complexity?", "hot-swap schema increase application complexity?", "schema increase application complexity?", "increase application complexity?", "application complexity?", "complexity?"], "tokens": ["Does", "hot", "swap", "schema", "increase", "application", "complexity", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::487582645", "fields": {"term": "What kind of application benefits from hot-swap schema?", "namespace": "open-p", "hash": 487582645, "terms": ["What kind of application benefits from hot-swap schema?", "kind of application benefits from hot-swap schema?", "of application benefits from hot-swap schema?", "application benefits from hot-swap schema?", "benefits from hot-swap schema?", "from hot-swap schema?", "hot-swap schema?", "schema?"], "tokens": ["What", "kind", "of", "application", "benefits", "from", "hot", "swap", "schema", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-671055833", "fields": {"term": "What external systems does Vespa integrate with?", "namespace": "open-p", "hash": -671055833, "terms": ["What external systems does Vespa integrate with?", "external systems does Vespa integrate with?", "systems does Vespa integrate with?", "does Vespa integrate with?", "Vespa integrate with?", "integrate with?", "with?"], "tokens": ["What", "external", "systems", "does", "Vespa", "integrate", "with", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1669070893", "fields": {"term": "What is the process metrics API?", "namespace": "open-p", "hash": 1669070893, "terms": ["What is the process metrics API?", "is the process metrics API?", "the process metrics API?", "process metrics API?", "metrics API?", "API?"], "tokens": ["What", "is", "the", "process", "metrics", "API", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1450042822", "fields": {"term": "How does Vespa transfer metrics?", "namespace": "open-p", "hash": 1450042822, "terms": ["How does Vespa transfer metrics?", "does Vespa transfer metrics?", "Vespa transfer metrics?", "transfer metrics?", "metrics?"], "tokens": ["How", "does", "Vespa", "transfer", "metrics", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-429861815", "fields": {"term": "What is the default port for metrics-proxy?", "namespace": "open-p", "hash": -429861815, "terms": ["What is the default port for metrics-proxy?", "is the default port for metrics-proxy?", "the default port for metrics-proxy?", "default port for metrics-proxy?", "port for metrics-proxy?", "for metrics-proxy?", "metrics-proxy?"], "tokens": ["What", "is", "the", "default", "port", "for", "metrics", "proxy", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-665942826", "fields": {"term": "What is the Prometheus format for metrics?", "namespace": "open-p", "hash": -665942826, "terms": ["What is the Prometheus format for metrics?", "is the Prometheus format for metrics?", "the Prometheus format for metrics?", "Prometheus format for metrics?", "format for metrics?", "for metrics?", "metrics?"], "tokens": ["What", "is", "the", "Prometheus", "format", "for", "metrics", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::130123965", "fields": {"term": "Where is the health status found?", "namespace": "open-p", "hash": 130123965, "terms": ["Where is the health status found?", "is the health status found?", "the health status found?", "health status found?", "status found?", "found?"], "tokens": ["Where", "is", "the", "health", "status", "found", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1315029434", "fields": {"term": "What is the URL for health status?", "namespace": "open-p", "hash": -1315029434, "terms": ["What is the URL for health status?", "is the URL for health status?", "the URL for health status?", "URL for health status?", "for health status?", "health status?", "status?"], "tokens": ["What", "is", "the", "URL", "for", "health", "status", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::831713738", "fields": {"term": "What is the status message?", "namespace": "open-p", "hash": 831713738, "terms": ["What is the status message?", "is the status message?", "the status message?", "status message?", "message?"], "tokens": ["What", "is", "the", "status", "message", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1466473995", "fields": {"term": "What is the purpose of /state/v1/health?", "namespace": "open-p", "hash": -1466473995, "terms": ["What is the purpose of /state/v1/health?", "is the purpose of /state/v1/health?", "the purpose of /state/v1/health?", "purpose of /state/v1/health?", "of /state/v1/health?", "/state/v1/health?"], "tokens": ["What", "is", "the", "purpose", "of", "/", "state", "/", "v1", "/", "health", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1716334217", "fields": {"term": "How is /state/v1/health used?", "namespace": "open-p", "hash": -1716334217, "terms": ["How is /state/v1/health used?", "is /state/v1/health used?", "/state/v1/health used?", "used?"], "tokens": ["How", "is", "/", "state", "/", "v1", "/", "health", "used", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::112765261", "fields": {"term": "Where are per-process metrics found?", "namespace": "open-p", "hash": 112765261, "terms": ["Where are per-process metrics found?", "are per-process metrics found?", "per-process metrics found?", "metrics found?", "found?"], "tokens": ["Where", "are", "per", "process", "metrics", "found", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-471742060", "fields": {"term": "What are per-process metrics?", "namespace": "open-p", "hash": -471742060, "terms": ["What are per-process metrics?", "are per-process metrics?", "per-process metrics?", "metrics?"], "tokens": ["What", "are", "per", "process", "metrics", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1529170867", "fields": {"term": "What is the purpose of per-process metrics?", "namespace": "open-p", "hash": -1529170867, "terms": ["What is the purpose of per-process metrics?", "is the purpose of per-process metrics?", "the purpose of per-process metrics?", "purpose of per-process metrics?", "of per-process metrics?", "per-process metrics?", "metrics?"], "tokens": ["What", "is", "the", "purpose", "of", "per", "process", "metrics", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::648354165", "fields": {"term": "What are the benefits of Vespa's approach to metrics?", "namespace": "open-p", "hash": 648354165, "terms": ["What are the benefits of Vespa's approach to metrics?", "are the benefits of Vespa's approach to metrics?", "the benefits of Vespa's approach to metrics?", "benefits of Vespa's approach to metrics?", "of Vespa's approach to metrics?", "Vespa's approach to metrics?", "approach to metrics?", "to metrics?", "metrics?"], "tokens": ["What", "are", "the", "benefits", "of", "Vespa", "'", "s", "approach", "to", "metrics", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::118770202", "fields": {"term": "What is the structure of metrics in Vespa?", "namespace": "open-p", "hash": 118770202, "terms": ["What is the structure of metrics in Vespa?", "is the structure of metrics in Vespa?", "the structure of metrics in Vespa?", "structure of metrics in Vespa?", "of metrics in Vespa?", "metrics in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["What", "is", "the", "structure", "of", "metrics", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2019442643", "fields": {"term": "What is a snapshot in Vespa metrics?", "namespace": "open-p", "hash": -2019442643, "terms": ["What is a snapshot in Vespa metrics?", "is a snapshot in Vespa metrics?", "a snapshot in Vespa metrics?", "snapshot in Vespa metrics?", "in Vespa metrics?", "Vespa metrics?", "metrics?"], "tokens": ["What", "is", "a", "snapshot", "in", "Vespa", "metrics", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-985904580", "fields": {"term": "What are custom metrics in Vespa?", "namespace": "open-p", "hash": -985904580, "terms": ["What are custom metrics in Vespa?", "are custom metrics in Vespa?", "custom metrics in Vespa?", "metrics in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["What", "are", "custom", "metrics", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-482151099", "fields": {"term": "What products can be used for monitoring?", "namespace": "open-p", "hash": -482151099, "terms": ["What products can be used for monitoring?", "products can be used for monitoring?", "can be used for monitoring?", "be used for monitoring?", "used for monitoring?", "for monitoring?", "monitoring?"], "tokens": ["What", "products", "can", "be", "used", "for", "monitoring", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-538166433", "fields": {"term": "What is /metrics/v2/values?", "namespace": "open-p", "hash": -538166433, "terms": ["What is /metrics/v2/values?", "is /metrics/v2/values?", "/metrics/v2/values?"], "tokens": ["What", "is", "/", "metrics", "/", "v2", "/", "values", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1378288895", "fields": {"term": "What metrics are exposed by the /metrics/v2/values API?", "namespace": "open-p", "hash": 1378288895, "terms": ["What metrics are exposed by the /metrics/v2/values API?", "metrics are exposed by the /metrics/v2/values API?", "are exposed by the /metrics/v2/values API?", "exposed by the /metrics/v2/values API?", "by the /metrics/v2/values API?", "the /metrics/v2/values API?", "/metrics/v2/values API?", "API?"], "tokens": ["What", "metrics", "are", "exposed", "by", "the", "/", "metrics", "/", "v2", "/", "values", "API", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::84470571", "fields": {"term": "How can Vespa metrics be pulled to Cloudwatch?", "namespace": "open-p", "hash": 84470571, "terms": ["How can Vespa metrics be pulled to Cloudwatch?", "can Vespa metrics be pulled to Cloudwatch?", "Vespa metrics be pulled to Cloudwatch?", "metrics be pulled to Cloudwatch?", "be pulled to Cloudwatch?", "pulled to Cloudwatch?", "to Cloudwatch?", "Cloudwatch?"], "tokens": ["How", "can", "Vespa", "metrics", "be", "pulled", "to", "Cloudwatch", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1280818556", "fields": {"term": "What is the response format of the /metrics/v2/values API?", "namespace": "open-p", "hash": -1280818556, "terms": ["What is the response format of the /metrics/v2/values API?", "is the response format of the /metrics/v2/values API?", "the response format of the /metrics/v2/values API?", "response format of the /metrics/v2/values API?", "format of the /metrics/v2/values API?", "of the /metrics/v2/values API?", "the /metrics/v2/values API?", "/metrics/v2/values API?", "API?"], "tokens": ["What", "is", "the", "response", "format", "of", "the", "/", "metrics", "/", "v2", "/", "values", "API", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-965465853", "fields": {"term": "What is the purpose of the /metrics/v2/values API?", "namespace": "open-p", "hash": -965465853, "terms": ["What is the purpose of the /metrics/v2/values API?", "is the purpose of the /metrics/v2/values API?", "the purpose of the /metrics/v2/values API?", "purpose of the /metrics/v2/values API?", "of the /metrics/v2/values API?", "the /metrics/v2/values API?", "/metrics/v2/values API?", "API?"], "tokens": ["What", "is", "the", "purpose", "of", "the", "/", "metrics", "/", "v2", "/", "values", "API", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-183174101", "fields": {"term": "What is the 'nodes' list in the response of the /metrics/v2/values API?", "namespace": "open-p", "hash": -183174101, "terms": ["What is the 'nodes' list in the response of the /metrics/v2/values API?", "is the 'nodes' list in the response of the /metrics/v2/values API?", "the 'nodes' list in the response of the /metrics/v2/values API?", "'nodes' list in the response of the /metrics/v2/values API?", "list in the response of the /metrics/v2/values API?", "in the response of the /metrics/v2/values API?", "the response of the /metrics/v2/values API?", "response of the /metrics/v2/values API?", "of the /metrics/v2/values API?", "the /metrics/v2/values API?", "/metrics/v2/values API?", "API?"], "tokens": ["What", "is", "the", "'", "nodes", "'", "list", "in", "the", "response", "of", "the", "/", "metrics", "/", "v2", "/", "values", "API", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::675431328", "fields": {"term": "What are the metrics exposed for each service in the /metrics/v2/values API?", "namespace": "open-p", "hash": 675431328, "terms": ["What are the metrics exposed for each service in the /metrics/v2/values API?", "are the metrics exposed for each service in the /metrics/v2/values API?", "the metrics exposed for each service in the /metrics/v2/values API?", "metrics exposed for each service in the /metrics/v2/values API?", "exposed for each service in the /metrics/v2/values API?", "for each service in the /metrics/v2/values API?", "each service in the /metrics/v2/values API?", "service in the /metrics/v2/values API?", "in the /metrics/v2/values API?", "the /metrics/v2/values API?", "/metrics/v2/values API?", "API?"], "tokens": ["What", "are", "the", "metrics", "exposed", "for", "each", "service", "in", "the", "/", "metrics", "/", "v2", "/", "values", "API", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1947763811", "fields": {"term": "What is the format of each metric in the /metrics/v2/values API?", "namespace": "open-p", "hash": 1947763811, "terms": ["What is the format of each metric in the /metrics/v2/values API?", "is the format of each metric in the /metrics/v2/values API?", "the format of each metric in the /metrics/v2/values API?", "format of each metric in the /metrics/v2/values API?", "of each metric in the /metrics/v2/values API?", "each metric in the /metrics/v2/values API?", "metric in the /metrics/v2/values API?", "in the /metrics/v2/values API?", "the /metrics/v2/values API?", "/metrics/v2/values API?", "API?"], "tokens": ["What", "is", "the", "format", "of", "each", "metric", "in", "the", "/", "metrics", "/", "v2", "/", "values", "API", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::591649780", "fields": {"term": "What does the node metrics API expose?", "namespace": "open-p", "hash": 591649780, "terms": ["What does the node metrics API expose?", "does the node metrics API expose?", "the node metrics API expose?", "node metrics API expose?", "metrics API expose?", "API expose?", "expose?"], "tokens": ["What", "does", "the", "node", "metrics", "API", "expose", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-248491622", "fields": {"term": "How can Prometheus scrape node metrics?", "namespace": "open-p", "hash": -248491622, "terms": ["How can Prometheus scrape node metrics?", "can Prometheus scrape node metrics?", "Prometheus scrape node metrics?", "scrape node metrics?", "node metrics?", "metrics?"], "tokens": ["How", "can", "Prometheus", "scrape", "node", "metrics", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1090754483", "fields": {"term": "What is the format of the metrics exposed by the prometheus API?", "namespace": "open-p", "hash": 1090754483, "terms": ["What is the format of the metrics exposed by the prometheus API?", "is the format of the metrics exposed by the prometheus API?", "the format of the metrics exposed by the prometheus API?", "format of the metrics exposed by the prometheus API?", "of the metrics exposed by the prometheus API?", "the metrics exposed by the prometheus API?", "metrics exposed by the prometheus API?", "exposed by the prometheus API?", "by the prometheus API?", "the prometheus API?", "prometheus API?", "API?"], "tokens": ["What", "is", "the", "format", "of", "the", "metrics", "exposed", "by", "the", "prometheus", "API", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1348338789", "fields": {"term": "What is the prometheus/Grafana example for the node metrics API?", "namespace": "open-p", "hash": -1348338789, "terms": ["What is the prometheus/Grafana example for the node metrics API?", "is the prometheus/Grafana example for the node metrics API?", "the prometheus/Grafana example for the node metrics API?", "prometheus/Grafana example for the node metrics API?", "example for the node metrics API?", "for the node metrics API?", "the node metrics API?", "node metrics API?", "metrics API?", "API?"], "tokens": ["What", "is", "the", "prometheus", "/", "Grafana", "example", "for", "the", "node", "metrics", "API", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-479787060", "fields": {"term": "What is Vespa's metrics API?", "namespace": "open-p", "hash": -479787060, "terms": ["What is Vespa's metrics API?", "is Vespa's metrics API?", "Vespa's metrics API?", "metrics API?", "API?"], "tokens": ["What", "is", "Vespa", "'", "s", "metrics", "API", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1487699870", "fields": {"term": "What format does Vespa expose metrics in?", "namespace": "open-p", "hash": 1487699870, "terms": ["What format does Vespa expose metrics in?", "format does Vespa expose metrics in?", "does Vespa expose metrics in?", "Vespa expose metrics in?", "expose metrics in?", "metrics in?", "in?"], "tokens": ["What", "format", "does", "Vespa", "expose", "metrics", "in", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-493875606", "fields": {"term": "What is Vespa's polling frequency limit?", "namespace": "open-p", "hash": -493875606, "terms": ["What is Vespa's polling frequency limit?", "is Vespa's polling frequency limit?", "Vespa's polling frequency limit?", "polling frequency limit?", "frequency limit?", "limit?"], "tokens": ["What", "is", "Vespa", "'", "s", "polling", "frequency", "limit", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::401621906", "fields": {"term": "How can metrics be pulled into CloudWatch?", "namespace": "open-p", "hash": 401621906, "terms": ["How can metrics be pulled into CloudWatch?", "can metrics be pulled into CloudWatch?", "metrics be pulled into CloudWatch?", "be pulled into CloudWatch?", "pulled into CloudWatch?", "into CloudWatch?", "CloudWatch?"], "tokens": ["How", "can", "metrics", "be", "pulled", "into", "CloudWatch", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1026121428", "fields": {"term": "What is the recommended solution for pulling Vespa metrics into CloudWatch?", "namespace": "open-p", "hash": 1026121428, "terms": ["What is the recommended solution for pulling Vespa metrics into CloudWatch?", "is the recommended solution for pulling Vespa metrics into CloudWatch?", "the recommended solution for pulling Vespa metrics into CloudWatch?", "recommended solution for pulling Vespa metrics into CloudWatch?", "solution for pulling Vespa metrics into CloudWatch?", "for pulling Vespa metrics into CloudWatch?", "pulling Vespa metrics into CloudWatch?", "Vespa metrics into CloudWatch?", "metrics into CloudWatch?", "into CloudWatch?", "CloudWatch?"], "tokens": ["What", "is", "the", "recommended", "solution", "for", "pulling", "Vespa", "metrics", "into", "CloudWatch", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1298586396", "fields": {"term": "What Datadog integration exists for monitoring Vespa?", "namespace": "open-p", "hash": 1298586396, "terms": ["What Datadog integration exists for monitoring Vespa?", "Datadog integration exists for monitoring Vespa?", "integration exists for monitoring Vespa?", "exists for monitoring Vespa?", "for monitoring Vespa?", "monitoring Vespa?", "Vespa?"], "tokens": ["What", "Datadog", "integration", "exists", "for", "monitoring", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-707759355", "fields": {"term": "Where can Prometheus scrape Vespa metrics from?", "namespace": "open-p", "hash": -707759355, "terms": ["Where can Prometheus scrape Vespa metrics from?", "can Prometheus scrape Vespa metrics from?", "Prometheus scrape Vespa metrics from?", "scrape Vespa metrics from?", "Vespa metrics from?", "metrics from?", "from?"], "tokens": ["Where", "can", "Prometheus", "scrape", "Vespa", "metrics", "from", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-701853215", "fields": {"term": "What example exists for Prometheus/Grafana monitoring of Vespa?", "namespace": "open-p", "hash": -701853215, "terms": ["What example exists for Prometheus/Grafana monitoring of Vespa?", "example exists for Prometheus/Grafana monitoring of Vespa?", "exists for Prometheus/Grafana monitoring of Vespa?", "for Prometheus/Grafana monitoring of Vespa?", "Prometheus/Grafana monitoring of Vespa?", "monitoring of Vespa?", "of Vespa?", "Vespa?"], "tokens": ["What", "example", "exists", "for", "Prometheus", "/", "Grafana", "monitoring", "of", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1850710659", "fields": {"term": "What is the required permission for IAM user or role?", "namespace": "open-p", "hash": 1850710659, "terms": ["What is the required permission for IAM user or role?", "is the required permission for IAM user or role?", "the required permission for IAM user or role?", "required permission for IAM user or role?", "permission for IAM user or role?", "for IAM user or role?", "IAM user or role?", "user or role?", "or role?", "role?"], "tokens": ["What", "is", "the", "required", "permission", "for", "IAM", "user", "or", "role", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1093342646", "fields": {"term": "What is a shared credentials file?", "namespace": "open-p", "hash": -1093342646, "terms": ["What is a shared credentials file?", "is a shared credentials file?", "a shared credentials file?", "shared credentials file?", "credentials file?", "file?"], "tokens": ["What", "is", "a", "shared", "credentials", "file", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::744579876", "fields": {"term": "How to configure Vespa for CloudWatch?", "namespace": "open-p", "hash": 744579876, "terms": ["How to configure Vespa for CloudWatch?", "to configure Vespa for CloudWatch?", "configure Vespa for CloudWatch?", "Vespa for CloudWatch?", "for CloudWatch?", "CloudWatch?"], "tokens": ["How", "to", "configure", "Vespa", "for", "CloudWatch", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2140781916", "fields": {"term": "What metrics are sent to the default Vespa set?", "namespace": "open-p", "hash": 2140781916, "terms": ["What metrics are sent to the default Vespa set?", "metrics are sent to the default Vespa set?", "are sent to the default Vespa set?", "sent to the default Vespa set?", "to the default Vespa set?", "the default Vespa set?", "default Vespa set?", "Vespa set?", "set?"], "tokens": ["What", "metrics", "are", "sent", "to", "the", "default", "Vespa", "set", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-52252243", "fields": {"term": "What region is the CloudWatch namespace set to?", "namespace": "open-p", "hash": -52252243, "terms": ["What region is the CloudWatch namespace set to?", "region is the CloudWatch namespace set to?", "is the CloudWatch namespace set to?", "the CloudWatch namespace set to?", "CloudWatch namespace set to?", "namespace set to?", "set to?", "to?"], "tokens": ["What", "region", "is", "the", "CloudWatch", "namespace", "set", "to", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1308150512", "fields": {"term": "How to set up monitoring?", "namespace": "open-p", "hash": 1308150512, "terms": ["How to set up monitoring?", "to set up monitoring?", "set up monitoring?", "up monitoring?", "monitoring?"], "tokens": ["How", "to", "set", "up", "monitoring", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-963123878", "fields": {"term": "What is Grafana used for?", "namespace": "open-p", "hash": -963123878, "terms": ["What is Grafana used for?", "is Grafana used for?", "Grafana used for?", "used for?", "for?"], "tokens": ["What", "is", "Grafana", "used", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::980287592", "fields": {"term": "What is Prometheus?", "namespace": "open-p", "hash": 980287592, "terms": ["What is Prometheus?", "is Prometheus?", "Prometheus?"], "tokens": ["What", "is", "Prometheus", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::770822877", "fields": {"term": "What is time-series database?", "namespace": "open-p", "hash": 770822877, "terms": ["What is time-series database?", "is time-series database?", "time-series database?", "database?"], "tokens": ["What", "is", "time", "series", "database", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::248223775", "fields": {"term": "How to launch Grafana?", "namespace": "open-p", "hash": 248223775, "terms": ["How to launch Grafana?", "to launch Grafana?", "launch Grafana?", "Grafana?"], "tokens": ["How", "to", "launch", "Grafana", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-135243321", "fields": {"term": "How to build Random Data Feeder?", "namespace": "open-p", "hash": -135243321, "terms": ["How to build Random Data Feeder?", "to build Random Data Feeder?", "build Random Data Feeder?", "Random Data Feeder?", "Data Feeder?", "Feeder?"], "tokens": ["How", "to", "build", "Random", "Data", "Feeder", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::474766844", "fields": {"term": "How to run Random Data Feeder?", "namespace": "open-p", "hash": 474766844, "terms": ["How to run Random Data Feeder?", "to run Random Data Feeder?", "run Random Data Feeder?", "Random Data Feeder?", "Data Feeder?", "Feeder?"], "tokens": ["How", "to", "run", "Random", "Data", "Feeder", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::452200525", "fields": {"term": "What is Transport Layer Security (TLS)?", "namespace": "open-p", "hash": 452200525, "terms": ["What is Transport Layer Security (TLS)?", "is Transport Layer Security (TLS)?", "Transport Layer Security (TLS)?", "Layer Security (TLS)?", "Security (TLS)?", "(TLS)?"], "tokens": ["What", "is", "Transport", "Layer", "Security", "(", "TLS", ")?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1028224244", "fields": {"term": "Why is enabling TLS important?", "namespace": "open-p", "hash": 1028224244, "terms": ["Why is enabling TLS important?", "is enabling TLS important?", "enabling TLS important?", "TLS important?", "important?"], "tokens": ["Why", "is", "enabling", "TLS", "important", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1432529931", "fields": {"term": "What are the two separate planes of TLS connectivity in Vespa?", "namespace": "open-p", "hash": -1432529931, "terms": ["What are the two separate planes of TLS connectivity in Vespa?", "are the two separate planes of TLS connectivity in Vespa?", "the two separate planes of TLS connectivity in Vespa?", "two separate planes of TLS connectivity in Vespa?", "separate planes of TLS connectivity in Vespa?", "planes of TLS connectivity in Vespa?", "of TLS connectivity in Vespa?", "TLS connectivity in Vespa?", "connectivity in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["What", "are", "the", "two", "separate", "planes", "of", "TLS", "connectivity", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::805395040", "fields": {"term": "What is mutually authenticated TLS (mTLS)?", "namespace": "open-p", "hash": 805395040, "terms": ["What is mutually authenticated TLS (mTLS)?", "is mutually authenticated TLS (mTLS)?", "mutually authenticated TLS (mTLS)?", "authenticated TLS (mTLS)?", "TLS (mTLS)?", "(mTLS)?"], "tokens": ["What", "is", "mutually", "authenticated", "TLS", "(", "mTLS", ")?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2134414529", "fields": {"term": "Does enabling TLS in Vespa protect status pages and metrics?", "namespace": "open-p", "hash": -2134414529, "terms": ["Does enabling TLS in Vespa protect status pages and metrics?", "enabling TLS in Vespa protect status pages and metrics?", "TLS in Vespa protect status pages and metrics?", "in Vespa protect status pages and metrics?", "Vespa protect status pages and metrics?", "protect status pages and metrics?", "status pages and metrics?", "pages and metrics?", "and metrics?", "metrics?"], "tokens": ["Does", "enabling", "TLS", "in", "Vespa", "protect", "status", "pages", "and", "metrics", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1649452952", "fields": {"term": "What makes Vespa installations more secure?", "namespace": "open-p", "hash": -1649452952, "terms": ["What makes Vespa installations more secure?", "makes Vespa installations more secure?", "Vespa installations more secure?", "installations more secure?", "more secure?", "secure?"], "tokens": ["What", "makes", "Vespa", "installations", "more", "secure", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::938210783", "fields": {"term": "How do I enable TLS?", "namespace": "open-p", "hash": 938210783, "terms": ["How do I enable TLS?", "do I enable TLS?", "I enable TLS?", "enable TLS?", "TLS?"], "tokens": ["How", "do", "I", "enable", "TLS", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-253967055", "fields": {"term": "What is a PEM format?", "namespace": "open-p", "hash": -253967055, "terms": ["What is a PEM format?", "is a PEM format?", "a PEM format?", "PEM format?", "format?"], "tokens": ["What", "is", "a", "PEM", "format", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-111413810", "fields": {"term": "What is a PKCS#8 format?", "namespace": "open-p", "hash": -111413810, "terms": ["What is a PKCS#8 format?", "is a PKCS#8 format?", "a PKCS#8 format?", "PKCS#8 format?", "format?"], "tokens": ["What", "is", "a", "PKCS", "#", "8", "format", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1770081345", "fields": {"term": "How do I write a TLS configuration file?", "namespace": "open-p", "hash": 1770081345, "terms": ["How do I write a TLS configuration file?", "do I write a TLS configuration file?", "I write a TLS configuration file?", "write a TLS configuration file?", "a TLS configuration file?", "TLS configuration file?", "configuration file?", "file?"], "tokens": ["How", "do", "I", "write", "a", "TLS", "configuration", "file", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1454049020", "fields": {"term": "Does Vespa support encrypted private keys?", "namespace": "open-p", "hash": 1454049020, "terms": ["Does Vespa support encrypted private keys?", "Vespa support encrypted private keys?", "support encrypted private keys?", "encrypted private keys?", "private keys?", "keys?"], "tokens": ["Does", "Vespa", "support", "encrypted", "private", "keys", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1607299503", "fields": {"term": "Does Vespa support live reloading of TLS credentials?", "namespace": "open-p", "hash": -1607299503, "terms": ["Does Vespa support live reloading of TLS credentials?", "Vespa support live reloading of TLS credentials?", "support live reloading of TLS credentials?", "live reloading of TLS credentials?", "reloading of TLS credentials?", "of TLS credentials?", "TLS credentials?", "credentials?"], "tokens": ["Does", "Vespa", "support", "live", "reloading", "of", "TLS", "credentials", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-945515878", "fields": {"term": "What environment variable governs TLS?", "namespace": "open-p", "hash": -945515878, "terms": ["What environment variable governs TLS?", "environment variable governs TLS?", "variable governs TLS?", "governs TLS?", "TLS?"], "tokens": ["What", "environment", "variable", "governs", "TLS", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1339488167", "fields": {"term": "What does VESPA_TLS_CONFIG_FILE contain?", "namespace": "open-p", "hash": -1339488167, "terms": ["What does VESPA_TLS_CONFIG_FILE contain?", "does VESPA_TLS_CONFIG_FILE contain?", "VESPA_TLS_CONFIG_FILE contain?", "contain?"], "tokens": ["What", "does", "VESPA_TLS_CONFIG_FILE", "contain", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1639430512", "fields": {"term": "What must VESPA_TLS_CONFIG_FILE point to?", "namespace": "open-p", "hash": -1639430512, "terms": ["What must VESPA_TLS_CONFIG_FILE point to?", "must VESPA_TLS_CONFIG_FILE point to?", "VESPA_TLS_CONFIG_FILE point to?", "point to?", "to?"], "tokens": ["What", "must", "VESPA_TLS_CONFIG_FILE", "point", "to", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::234362026", "fields": {"term": "What happens after setting VESPA_TLS_CONFIG_FILE?", "namespace": "open-p", "hash": 234362026, "terms": ["What happens after setting VESPA_TLS_CONFIG_FILE?", "happens after setting VESPA_TLS_CONFIG_FILE?", "after setting VESPA_TLS_CONFIG_FILE?", "setting VESPA_TLS_CONFIG_FILE?", "VESPA_TLS_CONFIG_FILE?"], "tokens": ["What", "happens", "after", "setting", "VESPA_TLS_CONFIG_FILE", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-172074085", "fields": {"term": "How are TLS certificates configured in Vespa?", "namespace": "open-p", "hash": -172074085, "terms": ["How are TLS certificates configured in Vespa?", "are TLS certificates configured in Vespa?", "TLS certificates configured in Vespa?", "certificates configured in Vespa?", "configured in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["How", "are", "TLS", "certificates", "configured", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-228115962", "fields": {"term": "What is the purpose of VESPA_TLS_CONFIG_FILE?", "namespace": "open-p", "hash": -228115962, "terms": ["What is the purpose of VESPA_TLS_CONFIG_FILE?", "is the purpose of VESPA_TLS_CONFIG_FILE?", "the purpose of VESPA_TLS_CONFIG_FILE?", "purpose of VESPA_TLS_CONFIG_FILE?", "of VESPA_TLS_CONFIG_FILE?", "VESPA_TLS_CONFIG_FILE?"], "tokens": ["What", "is", "the", "purpose", "of", "VESPA_TLS_CONFIG_FILE", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-384115199", "fields": {"term": "What are the requirements for enabling TLS in Vespa?", "namespace": "open-p", "hash": -384115199, "terms": ["What are the requirements for enabling TLS in Vespa?", "are the requirements for enabling TLS in Vespa?", "the requirements for enabling TLS in Vespa?", "requirements for enabling TLS in Vespa?", "for enabling TLS in Vespa?", "enabling TLS in Vespa?", "TLS in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["What", "are", "the", "requirements", "for", "enabling", "TLS", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-575537974", "fields": {"term": "What do Vespa command-line tools do automatically after setting VESPA_TLS_CONFIG_FILE?", "namespace": "open-p", "hash": -575537974, "terms": ["What do Vespa command-line tools do automatically after setting VESPA_TLS_CONFIG_FILE?", "do Vespa command-line tools do automatically after setting VESPA_TLS_CONFIG_FILE?", "Vespa command-line tools do automatically after setting VESPA_TLS_CONFIG_FILE?", "command-line tools do automatically after setting VESPA_TLS_CONFIG_FILE?", "tools do automatically after setting VESPA_TLS_CONFIG_FILE?", "do automatically after setting VESPA_TLS_CONFIG_FILE?", "automatically after setting VESPA_TLS_CONFIG_FILE?", "after setting VESPA_TLS_CONFIG_FILE?", "setting VESPA_TLS_CONFIG_FILE?", "VESPA_TLS_CONFIG_FILE?"], "tokens": ["What", "do", "Vespa", "command", "line", "tools", "do", "automatically", "after", "setting", "VESPA_TLS_CONFIG_FILE", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1661063441", "fields": {"term": "What happens if a process cannot load files?", "namespace": "open-p", "hash": -1661063441, "terms": ["What happens if a process cannot load files?", "happens if a process cannot load files?", "if a process cannot load files?", "a process cannot load files?", "process cannot load files?", "cannot load files?", "load files?", "files?"], "tokens": ["What", "happens", "if", "a", "process", "cannot", "load", "files", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-242116236", "fields": {"term": "What is TLS?", "namespace": "open-p", "hash": -242116236, "terms": ["What is TLS?", "is TLS?", "TLS?"], "tokens": ["What", "is", "TLS", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1367377050", "fields": {"term": "What is a self-signed Certificate Authority?", "namespace": "open-p", "hash": 1367377050, "terms": ["What is a self-signed Certificate Authority?", "is a self-signed Certificate Authority?", "a self-signed Certificate Authority?", "self-signed Certificate Authority?", "Certificate Authority?", "Authority?"], "tokens": ["What", "is", "a", "self", "signed", "Certificate", "Authority", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1510467904", "fields": {"term": "What is an authorization rule?", "namespace": "open-p", "hash": -1510467904, "terms": ["What is an authorization rule?", "is an authorization rule?", "an authorization rule?", "authorization rule?", "rule?"], "tokens": ["What", "is", "an", "authorization", "rule", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1169208176", "fields": {"term": "How are authorization rules specified?", "namespace": "open-p", "hash": 1169208176, "terms": ["How are authorization rules specified?", "are authorization rules specified?", "authorization rules specified?", "rules specified?", "specified?"], "tokens": ["How", "are", "authorization", "rules", "specified", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::269679076", "fields": {"term": "What is the purpose of authorization rules?", "namespace": "open-p", "hash": 269679076, "terms": ["What is the purpose of authorization rules?", "is the purpose of authorization rules?", "the purpose of authorization rules?", "purpose of authorization rules?", "of authorization rules?", "authorization rules?", "rules?"], "tokens": ["What", "is", "the", "purpose", "of", "authorization", "rules", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1775815646", "fields": {"term": "What is required for monitoring service?", "namespace": "open-p", "hash": 1775815646, "terms": ["What is required for monitoring service?", "is required for monitoring service?", "required for monitoring service?", "for monitoring service?", "monitoring service?", "service?"], "tokens": ["What", "is", "required", "for", "monitoring", "service", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1331144964", "fields": {"term": "What is a certificate authority?", "namespace": "open-p", "hash": -1331144964, "terms": ["What is a certificate authority?", "is a certificate authority?", "a certificate authority?", "certificate authority?", "authority?"], "tokens": ["What", "is", "a", "certificate", "authority", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1894633201", "fields": {"term": "What is the syntax for authorized peers?", "namespace": "open-p", "hash": -1894633201, "terms": ["What is the syntax for authorized peers?", "is the syntax for authorized peers?", "the syntax for authorized peers?", "syntax for authorized peers?", "for authorized peers?", "authorized peers?", "peers?"], "tokens": ["What", "is", "the", "syntax", "for", "authorized", "peers", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2128045460", "fields": {"term": "What is the CN value for monitoring service?", "namespace": "open-p", "hash": -2128045460, "terms": ["What is the CN value for monitoring service?", "is the CN value for monitoring service?", "the CN value for monitoring service?", "CN value for monitoring service?", "value for monitoring service?", "for monitoring service?", "monitoring service?", "service?"], "tokens": ["What", "is", "the", "CN", "value", "for", "monitoring", "service", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-403657124", "fields": {"term": "What is the purpose for authorized peers?", "namespace": "open-p", "hash": -403657124, "terms": ["What is the purpose for authorized peers?", "is the purpose for authorized peers?", "the purpose for authorized peers?", "purpose for authorized peers?", "for authorized peers?", "authorized peers?", "peers?"], "tokens": ["What", "is", "the", "purpose", "for", "authorized", "peers", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::890401261", "fields": {"term": "What is the role of SAN DNS entries?", "namespace": "open-p", "hash": 890401261, "terms": ["What is the role of SAN DNS entries?", "is the role of SAN DNS entries?", "the role of SAN DNS entries?", "role of SAN DNS entries?", "of SAN DNS entries?", "SAN DNS entries?", "DNS entries?", "entries?"], "tokens": ["What", "is", "the", "role", "of", "SAN", "DNS", "entries", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1302083565", "fields": {"term": "What is a TLS configuration file?", "namespace": "open-p", "hash": 1302083565, "terms": ["What is a TLS configuration file?", "is a TLS configuration file?", "a TLS configuration file?", "TLS configuration file?", "configuration file?", "file?"], "tokens": ["What", "is", "a", "TLS", "configuration", "file", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2062402424", "fields": {"term": "How often is reloading performed?", "namespace": "open-p", "hash": 2062402424, "terms": ["How often is reloading performed?", "often is reloading performed?", "is reloading performed?", "reloading performed?", "performed?"], "tokens": ["How", "often", "is", "reloading", "performed", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1855062114", "fields": {"term": "What is the impact of reloading?", "namespace": "open-p", "hash": -1855062114, "terms": ["What is the impact of reloading?", "is the impact of reloading?", "the impact of reloading?", "impact of reloading?", "of reloading?", "reloading?"], "tokens": ["What", "is", "the", "impact", "of", "reloading", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1260814419", "fields": {"term": "What happens if live reloading fails?", "namespace": "open-p", "hash": -1260814419, "terms": ["What happens if live reloading fails?", "happens if live reloading fails?", "if live reloading fails?", "live reloading fails?", "reloading fails?", "fails?"], "tokens": ["What", "happens", "if", "live", "reloading", "fails", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::641799260", "fields": {"term": "Does Vespa watch the configuration file?", "namespace": "open-p", "hash": 641799260, "terms": ["Does Vespa watch the configuration file?", "Vespa watch the configuration file?", "watch the configuration file?", "the configuration file?", "configuration file?", "file?"], "tokens": ["Does", "Vespa", "watch", "the", "configuration", "file", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-713033018", "fields": {"term": "How can race conditions be avoided?", "namespace": "open-p", "hash": -713033018, "terms": ["How can race conditions be avoided?", "can race conditions be avoided?", "race conditions be avoided?", "conditions be avoided?", "be avoided?", "avoided?"], "tokens": ["How", "can", "race", "conditions", "be", "avoided", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1303977357", "fields": {"term": "What are the steps to split file refreshing?", "namespace": "open-p", "hash": 1303977357, "terms": ["What are the steps to split file refreshing?", "are the steps to split file refreshing?", "the steps to split file refreshing?", "steps to split file refreshing?", "to split file refreshing?", "split file refreshing?", "file refreshing?", "refreshing?"], "tokens": ["What", "are", "the", "steps", "to", "split", "file", "refreshing", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1364420220", "fields": {"term": "What is vespa-get-cluster-state?", "namespace": "open-p", "hash": 1364420220, "terms": ["What is vespa-get-cluster-state?", "is vespa-get-cluster-state?", "vespa-get-cluster-state?"], "tokens": ["What", "is", "vespa", "get", "cluster", "state", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::-172481044", "fields": {"term": "What is the recommended way to enable TLS?", "namespace": "open-p", "hash": -172481044, "terms": ["What is the recommended way to enable TLS?", "is the recommended way to enable TLS?", "the recommended way to enable TLS?", "recommended way to enable TLS?", "way to enable TLS?", "to enable TLS?", "enable TLS?", "TLS?"], "tokens": ["What", "is", "the", "recommended", "way", "to", "enable", "TLS", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1807647916", "fields": {"term": "What is mixed mode in Vespa?", "namespace": "open-p", "hash": -1807647916, "terms": ["What is mixed mode in Vespa?", "is mixed mode in Vespa?", "mixed mode in Vespa?", "mode in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["What", "is", "mixed", "mode", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::811265772", "fields": {"term": "How many phases are there in the TLS rollout?", "namespace": "open-p", "hash": 811265772, "terms": ["How many phases are there in the TLS rollout?", "many phases are there in the TLS rollout?", "phases are there in the TLS rollout?", "are there in the TLS rollout?", "there in the TLS rollout?", "in the TLS rollout?", "the TLS rollout?", "TLS rollout?", "rollout?"], "tokens": ["How", "many", "phases", "are", "there", "in", "the", "TLS", "rollout", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1926637931", "fields": {"term": "How can you enable gradual TLS upgrade in Vespa?", "namespace": "open-p", "hash": -1926637931, "terms": ["How can you enable gradual TLS upgrade in Vespa?", "can you enable gradual TLS upgrade in Vespa?", "you enable gradual TLS upgrade in Vespa?", "enable gradual TLS upgrade in Vespa?", "gradual TLS upgrade in Vespa?", "TLS upgrade in Vespa?", "upgrade in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["How", "can", "you", "enable", "gradual", "TLS", "upgrade", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::826856308", "fields": {"term": "What is the environment variable used to control mixed mode in Vespa?", "namespace": "open-p", "hash": 826856308, "terms": ["What is the environment variable used to control mixed mode in Vespa?", "is the environment variable used to control mixed mode in Vespa?", "the environment variable used to control mixed mode in Vespa?", "environment variable used to control mixed mode in Vespa?", "variable used to control mixed mode in Vespa?", "used to control mixed mode in Vespa?", "to control mixed mode in Vespa?", "control mixed mode in Vespa?", "mixed mode in Vespa?", "mode in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["What", "is", "the", "environment", "variable", "used", "to", "control", "mixed", "mode", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1590010511", "fields": {"term": "What happens during phase 1 of the TLS rollout in Vespa?", "namespace": "open-p", "hash": -1590010511, "terms": ["What happens during phase 1 of the TLS rollout in Vespa?", "happens during phase 1 of the TLS rollout in Vespa?", "during phase 1 of the TLS rollout in Vespa?", "phase 1 of the TLS rollout in Vespa?", "1 of the TLS rollout in Vespa?", "of the TLS rollout in Vespa?", "the TLS rollout in Vespa?", "TLS rollout in Vespa?", "rollout in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["What", "happens", "during", "phase", "1", "of", "the", "TLS", "rollout", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::851077213", "fields": {"term": "What happens during phase 2 of the TLS rollout in Vespa?", "namespace": "open-p", "hash": 851077213, "terms": ["What happens during phase 2 of the TLS rollout in Vespa?", "happens during phase 2 of the TLS rollout in Vespa?", "during phase 2 of the TLS rollout in Vespa?", "phase 2 of the TLS rollout in Vespa?", "2 of the TLS rollout in Vespa?", "of the TLS rollout in Vespa?", "the TLS rollout in Vespa?", "TLS rollout in Vespa?", "rollout in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["What", "happens", "during", "phase", "2", "of", "the", "TLS", "rollout", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::534341883", "fields": {"term": "What happens during phase 3 of the TLS rollout in Vespa?", "namespace": "open-p", "hash": 534341883, "terms": ["What happens during phase 3 of the TLS rollout in Vespa?", "happens during phase 3 of the TLS rollout in Vespa?", "during phase 3 of the TLS rollout in Vespa?", "phase 3 of the TLS rollout in Vespa?", "3 of the TLS rollout in Vespa?", "of the TLS rollout in Vespa?", "the TLS rollout in Vespa?", "TLS rollout in Vespa?", "rollout in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["What", "happens", "during", "phase", "3", "of", "the", "TLS", "rollout", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-165472969", "fields": {"term": "What is the tool to verify runtime configuration?", "namespace": "open-p", "hash": -165472969, "terms": ["What is the tool to verify runtime configuration?", "is the tool to verify runtime configuration?", "the tool to verify runtime configuration?", "tool to verify runtime configuration?", "to verify runtime configuration?", "verify runtime configuration?", "runtime configuration?", "configuration?"], "tokens": ["What", "is", "the", "tool", "to", "verify", "runtime", "configuration", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1389330957", "fields": {"term": "What port should be used to connect to Vespa?", "namespace": "open-p", "hash": -1389330957, "terms": ["What port should be used to connect to Vespa?", "port should be used to connect to Vespa?", "should be used to connect to Vespa?", "be used to connect to Vespa?", "used to connect to Vespa?", "to connect to Vespa?", "connect to Vespa?", "to Vespa?", "Vespa?"], "tokens": ["What", "port", "should", "be", "used", "to", "connect", "to", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::454816157", "fields": {"term": "How can server client authentication be verified?", "namespace": "open-p", "hash": 454816157, "terms": ["How can server client authentication be verified?", "can server client authentication be verified?", "server client authentication be verified?", "client authentication be verified?", "authentication be verified?", "be verified?", "verified?"], "tokens": ["How", "can", "server", "client", "authentication", "be", "verified", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1524193374", "fields": {"term": "What is the command to use openssl s_client?", "namespace": "open-p", "hash": 1524193374, "terms": ["What is the command to use openssl s_client?", "is the command to use openssl s_client?", "the command to use openssl s_client?", "command to use openssl s_client?", "to use openssl s_client?", "use openssl s_client?", "openssl s_client?", "s_client?"], "tokens": ["What", "is", "the", "command", "to", "use", "openssl", "s_client", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1768085060", "fields": {"term": "Is TLS recommended for real-time search?", "namespace": "open-p", "hash": -1768085060, "terms": ["Is TLS recommended for real-time search?", "TLS recommended for real-time search?", "recommended for real-time search?", "for real-time search?", "real-time search?", "search?"], "tokens": ["Is", "TLS", "recommended", "for", "real", "time", "search", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1044117027", "fields": {"term": "How much extra CPU usage does TLS impose?", "namespace": "open-p", "hash": -1044117027, "terms": ["How much extra CPU usage does TLS impose?", "much extra CPU usage does TLS impose?", "extra CPU usage does TLS impose?", "CPU usage does TLS impose?", "usage does TLS impose?", "does TLS impose?", "TLS impose?", "impose?"], "tokens": ["How", "much", "extra", "CPU", "usage", "does", "TLS", "impose", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2019003908", "fields": {"term": "What is symmetric encryption?", "namespace": "open-p", "hash": 2019003908, "terms": ["What is symmetric encryption?", "is symmetric encryption?", "symmetric encryption?", "encryption?"], "tokens": ["What", "is", "symmetric", "encryption", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1053440784", "fields": {"term": "How much additional latency does a TLS handshake have?", "namespace": "open-p", "hash": 1053440784, "terms": ["How much additional latency does a TLS handshake have?", "much additional latency does a TLS handshake have?", "additional latency does a TLS handshake have?", "latency does a TLS handshake have?", "does a TLS handshake have?", "a TLS handshake have?", "TLS handshake have?", "handshake have?", "have?"], "tokens": ["How", "much", "additional", "latency", "does", "a", "TLS", "handshake", "have", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-653360435", "fields": {"term": "How does Vespa handle TLS handshakes?", "namespace": "open-p", "hash": -653360435, "terms": ["How does Vespa handle TLS handshakes?", "does Vespa handle TLS handshakes?", "Vespa handle TLS handshakes?", "handle TLS handshakes?", "TLS handshakes?", "handshakes?"], "tokens": ["How", "does", "Vespa", "handle", "TLS", "handshakes", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1785417038", "fields": {"term": "Does Vespa use long-lived connections to reduce handshakes?", "namespace": "open-p", "hash": 1785417038, "terms": ["Does Vespa use long-lived connections to reduce handshakes?", "Vespa use long-lived connections to reduce handshakes?", "use long-lived connections to reduce handshakes?", "long-lived connections to reduce handshakes?", "connections to reduce handshakes?", "to reduce handshakes?", "reduce handshakes?", "handshakes?"], "tokens": ["Does", "Vespa", "use", "long", "lived", "connections", "to", "reduce", "handshakes", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-566082240", "fields": {"term": "How has Vespa tuned its TLS integration?", "namespace": "open-p", "hash": -566082240, "terms": ["How has Vespa tuned its TLS integration?", "has Vespa tuned its TLS integration?", "Vespa tuned its TLS integration?", "tuned its TLS integration?", "its TLS integration?", "TLS integration?", "integration?"], "tokens": ["How", "has", "Vespa", "tuned", "its", "TLS", "integration", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1922170802", "fields": {"term": "What is the HTTPS endpoint identification algorithm?", "namespace": "open-p", "hash": -1922170802, "terms": ["What is the HTTPS endpoint identification algorithm?", "is the HTTPS endpoint identification algorithm?", "the HTTPS endpoint identification algorithm?", "HTTPS endpoint identification algorithm?", "endpoint identification algorithm?", "identification algorithm?", "algorithm?"], "tokens": ["What", "is", "the", "HTTPS", "endpoint", "identification", "algorithm", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1860336985", "fields": {"term": "How can I disable hostname validation?", "namespace": "open-p", "hash": -1860336985, "terms": ["How can I disable hostname validation?", "can I disable hostname validation?", "I disable hostname validation?", "disable hostname validation?", "hostname validation?", "validation?"], "tokens": ["How", "can", "I", "disable", "hostname", "validation", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-85468607", "fields": {"term": "What causes an error with vespa-deploy?", "namespace": "open-p", "hash": -85468607, "terms": ["What causes an error with vespa-deploy?", "causes an error with vespa-deploy?", "an error with vespa-deploy?", "error with vespa-deploy?", "with vespa-deploy?", "vespa-deploy?"], "tokens": ["What", "causes", "an", "error", "with", "vespa", "deploy", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::907006024", "fields": {"term": "Why does vespa-deploy fail on CentOS 7?", "namespace": "open-p", "hash": 907006024, "terms": ["Why does vespa-deploy fail on CentOS 7?", "does vespa-deploy fail on CentOS 7?", "vespa-deploy fail on CentOS 7?", "fail on CentOS 7?", "on CentOS 7?", "CentOS 7?", "7?"], "tokens": ["Why", "does", "vespa", "deploy", "fail", "on", "CentOS", "7", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-383866419", "fields": {"term": "What cryptographic keys can be used with vespa-deploy?", "namespace": "open-p", "hash": -383866419, "terms": ["What cryptographic keys can be used with vespa-deploy?", "cryptographic keys can be used with vespa-deploy?", "keys can be used with vespa-deploy?", "can be used with vespa-deploy?", "be used with vespa-deploy?", "used with vespa-deploy?", "with vespa-deploy?", "vespa-deploy?"], "tokens": ["What", "cryptographic", "keys", "can", "be", "used", "with", "vespa", "deploy", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1391903300", "fields": {"term": "What is the problem with elliptic curve keys?", "namespace": "open-p", "hash": 1391903300, "terms": ["What is the problem with elliptic curve keys?", "is the problem with elliptic curve keys?", "the problem with elliptic curve keys?", "problem with elliptic curve keys?", "with elliptic curve keys?", "elliptic curve keys?", "curve keys?", "keys?"], "tokens": ["What", "is", "the", "problem", "with", "elliptic", "curve", "keys", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1476029204", "fields": {"term": "How to resolve vespa-deploy errors on CentOS 7?", "namespace": "open-p", "hash": 1476029204, "terms": ["How to resolve vespa-deploy errors on CentOS 7?", "to resolve vespa-deploy errors on CentOS 7?", "resolve vespa-deploy errors on CentOS 7?", "vespa-deploy errors on CentOS 7?", "errors on CentOS 7?", "on CentOS 7?", "CentOS 7?", "7?"], "tokens": ["How", "to", "resolve", "vespa", "deploy", "errors", "on", "CentOS", "7", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::153465964", "fields": {"term": "What is a root Certificate Authority?", "namespace": "open-p", "hash": 153465964, "terms": ["What is a root Certificate Authority?", "is a root Certificate Authority?", "a root Certificate Authority?", "root Certificate Authority?", "Certificate Authority?", "Authority?"], "tokens": ["What", "is", "a", "root", "Certificate", "Authority", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::956908925", "fields": {"term": "How is a certificate verified?", "namespace": "open-p", "hash": 956908925, "terms": ["How is a certificate verified?", "is a certificate verified?", "a certificate verified?", "certificate verified?", "verified?"], "tokens": ["How", "is", "a", "certificate", "verified", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1869177511", "fields": {"term": "What is a certificate chain?", "namespace": "open-p", "hash": -1869177511, "terms": ["What is a certificate chain?", "is a certificate chain?", "a certificate chain?", "certificate chain?", "chain?"], "tokens": ["What", "is", "a", "certificate", "chain", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1880472397", "fields": {"term": "How is a root CA created?", "namespace": "open-p", "hash": -1880472397, "terms": ["How is a root CA created?", "is a root CA created?", "a root CA created?", "root CA created?", "CA created?", "created?"], "tokens": ["How", "is", "a", "root", "CA", "created", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::509203266", "fields": {"term": "What is the difference between RSA and EC cryptography?", "namespace": "open-p", "hash": 509203266, "terms": ["What is the difference between RSA and EC cryptography?", "is the difference between RSA and EC cryptography?", "the difference between RSA and EC cryptography?", "difference between RSA and EC cryptography?", "between RSA and EC cryptography?", "RSA and EC cryptography?", "and EC cryptography?", "EC cryptography?", "cryptography?"], "tokens": ["What", "is", "the", "difference", "between", "RSA", "and", "EC", "cryptography", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::727592494", "fields": {"term": "What is a private key?", "namespace": "open-p", "hash": 727592494, "terms": ["What is a private key?", "is a private key?", "a private key?", "private key?", "key?"], "tokens": ["What", "is", "a", "private", "key", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2088573766", "fields": {"term": "How is a CA X.509 certificate created?", "namespace": "open-p", "hash": -2088573766, "terms": ["How is a CA X.509 certificate created?", "is a CA X.509 certificate created?", "a CA X.509 certificate created?", "CA X.509 certificate created?", "X.509 certificate created?", "certificate created?", "created?"], "tokens": ["How", "is", "a", "CA", "X", "509", "certificate", "created", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::259397430", "fields": {"term": "What is a Certificate Signing Request?", "namespace": "open-p", "hash": 259397430, "terms": ["What is a Certificate Signing Request?", "is a Certificate Signing Request?", "a Certificate Signing Request?", "Certificate Signing Request?", "Signing Request?", "Request?"], "tokens": ["What", "is", "a", "Certificate", "Signing", "Request", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1627143730", "fields": {"term": "How can I create a private cryptographic key?", "namespace": "open-p", "hash": -1627143730, "terms": ["How can I create a private cryptographic key?", "can I create a private cryptographic key?", "I create a private cryptographic key?", "create a private cryptographic key?", "a private cryptographic key?", "private cryptographic key?", "cryptographic key?", "key?"], "tokens": ["How", "can", "I", "create", "a", "private", "cryptographic", "key", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1181221829", "fields": {"term": "What is Elliptic Curve key?", "namespace": "open-p", "hash": 1181221829, "terms": ["What is Elliptic Curve key?", "is Elliptic Curve key?", "Elliptic Curve key?", "Curve key?", "key?"], "tokens": ["What", "is", "Elliptic", "Curve", "key", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1273184178", "fields": {"term": "What is RSA key?", "namespace": "open-p", "hash": 1273184178, "terms": ["What is RSA key?", "is RSA key?", "RSA key?", "key?"], "tokens": ["What", "is", "RSA", "key", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-452555509", "fields": {"term": "What is a CSR?", "namespace": "open-p", "hash": -452555509, "terms": ["What is a CSR?", "is a CSR?", "a CSR?", "CSR?"], "tokens": ["What", "is", "a", "CSR", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1288914934", "fields": {"term": "What is openssl command for creating certificate?", "namespace": "open-p", "hash": -1288914934, "terms": ["What is openssl command for creating certificate?", "is openssl command for creating certificate?", "openssl command for creating certificate?", "command for creating certificate?", "for creating certificate?", "creating certificate?", "certificate?"], "tokens": ["What", "is", "openssl", "command", "for", "creating", "certificate", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1240925343", "fields": {"term": "What are the alternatives for unique certificates per individual host?", "namespace": "open-p", "hash": -1240925343, "terms": ["What are the alternatives for unique certificates per individual host?", "are the alternatives for unique certificates per individual host?", "the alternatives for unique certificates per individual host?", "alternatives for unique certificates per individual host?", "for unique certificates per individual host?", "unique certificates per individual host?", "certificates per individual host?", "per individual host?", "individual host?", "host?"], "tokens": ["What", "are", "the", "alternatives", "for", "unique", "certificates", "per", "individual", "host", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::30901127", "fields": {"term": "What is TLS hostname validation?", "namespace": "open-p", "hash": 30901127, "terms": ["What is TLS hostname validation?", "is TLS hostname validation?", "TLS hostname validation?", "hostname validation?", "validation?"], "tokens": ["What", "is", "TLS", "hostname", "validation", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1084627995", "fields": {"term": "What are Subject Alternative Names?", "namespace": "open-p", "hash": -1084627995, "terms": ["What are Subject Alternative Names?", "are Subject Alternative Names?", "Subject Alternative Names?", "Alternative Names?", "Names?"], "tokens": ["What", "are", "Subject", "Alternative", "Names", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1421823191", "fields": {"term": "What is basicConstraints?", "namespace": "open-p", "hash": 1421823191, "terms": ["What is basicConstraints?", "is basicConstraints?", "basicConstraints?"], "tokens": ["What", "is", "basicConstraints", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2081083417", "fields": {"term": "What is keyUsage?", "namespace": "open-p", "hash": 2081083417, "terms": ["What is keyUsage?", "is keyUsage?", "keyUsage?"], "tokens": ["What", "is", "keyUsage", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1626276657", "fields": {"term": "What is extendedKeyUsage?", "namespace": "open-p", "hash": 1626276657, "terms": ["What is extendedKeyUsage?", "is extendedKeyUsage?", "extendedKeyUsage?"], "tokens": ["What", "is", "extendedKeyUsage", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-127688529", "fields": {"term": "What is subjectKeyIdentifier?", "namespace": "open-p", "hash": -127688529, "terms": ["What is subjectKeyIdentifier?", "is subjectKeyIdentifier?", "subjectKeyIdentifier?"], "tokens": ["What", "is", "subjectKeyIdentifier", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::401960966", "fields": {"term": "What is authorityKeyIdentifier?", "namespace": "open-p", "hash": 401960966, "terms": ["What is authorityKeyIdentifier?", "is authorityKeyIdentifier?", "authorityKeyIdentifier?"], "tokens": ["What", "is", "authorityKeyIdentifier", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1996896007", "fields": {"term": "What is subjectAltName?", "namespace": "open-p", "hash": -1996896007, "terms": ["What is subjectAltName?", "is subjectAltName?", "subjectAltName?"], "tokens": ["What", "is", "subjectAltName", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-729453459", "fields": {"term": "What is a hostname SAN?", "namespace": "open-p", "hash": -729453459, "terms": ["What is a hostname SAN?", "is a hostname SAN?", "a hostname SAN?", "hostname SAN?", "SAN?"], "tokens": ["What", "is", "a", "hostname", "SAN", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-247022005", "fields": {"term": "What is a wildcard DNS?", "namespace": "open-p", "hash": -247022005, "terms": ["What is a wildcard DNS?", "is a wildcard DNS?", "a wildcard DNS?", "wildcard DNS?", "DNS?"], "tokens": ["What", "is", "a", "wildcard", "DNS", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1508463422", "fields": {"term": "Why should separate certificates be used for production deployments?", "namespace": "open-p", "hash": 1508463422, "terms": ["Why should separate certificates be used for production deployments?", "should separate certificates be used for production deployments?", "separate certificates be used for production deployments?", "certificates be used for production deployments?", "be used for production deployments?", "used for production deployments?", "for production deployments?", "production deployments?", "deployments?"], "tokens": ["Why", "should", "separate", "certificates", "be", "used", "for", "production", "deployments", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1812258674", "fields": {"term": "What is the VESPA_TLS_CONFIG_FILE?", "namespace": "open-p", "hash": -1812258674, "terms": ["What is the VESPA_TLS_CONFIG_FILE?", "is the VESPA_TLS_CONFIG_FILE?", "the VESPA_TLS_CONFIG_FILE?", "VESPA_TLS_CONFIG_FILE?"], "tokens": ["What", "is", "the", "VESPA_TLS_CONFIG_FILE", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::315599079", "fields": {"term": "What is the VESPA_TLS_INSECURE_MIXED_MODE?", "namespace": "open-p", "hash": 315599079, "terms": ["What is the VESPA_TLS_INSECURE_MIXED_MODE?", "is the VESPA_TLS_INSECURE_MIXED_MODE?", "the VESPA_TLS_INSECURE_MIXED_MODE?", "VESPA_TLS_INSECURE_MIXED_MODE?"], "tokens": ["What", "is", "the", "VESPA_TLS_INSECURE_MIXED_MODE", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-750932482", "fields": {"term": "What does TLS mixed mode refer to?", "namespace": "open-p", "hash": -750932482, "terms": ["What does TLS mixed mode refer to?", "does TLS mixed mode refer to?", "TLS mixed mode refer to?", "mixed mode refer to?", "mode refer to?", "refer to?", "to?"], "tokens": ["What", "does", "TLS", "mixed", "mode", "refer", "to", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1651585011", "fields": {"term": "What are TLS mixed mode settings?", "namespace": "open-p", "hash": -1651585011, "terms": ["What are TLS mixed mode settings?", "are TLS mixed mode settings?", "TLS mixed mode settings?", "mixed mode settings?", "mode settings?", "settings?"], "tokens": ["What", "are", "TLS", "mixed", "mode", "settings", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::268943445", "fields": {"term": "What is plaintext_client_mixed_server?", "namespace": "open-p", "hash": 268943445, "terms": ["What is plaintext_client_mixed_server?", "is plaintext_client_mixed_server?", "plaintext_client_mixed_server?"], "tokens": ["What", "is", "plaintext_client_mixed_server", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-529562379", "fields": {"term": "What is tls_client_mixed_server?", "namespace": "open-p", "hash": -529562379, "terms": ["What is tls_client_mixed_server?", "is tls_client_mixed_server?", "tls_client_mixed_server?"], "tokens": ["What", "is", "tls_client_mixed_server", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-276140842", "fields": {"term": "What is tls_client_tls_server?", "namespace": "open-p", "hash": -276140842, "terms": ["What is tls_client_tls_server?", "is tls_client_tls_server?", "tls_client_tls_server?"], "tokens": ["What", "is", "tls_client_tls_server", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-627809670", "fields": {"term": "What is a TLS configuration?", "namespace": "open-p", "hash": -627809670, "terms": ["What is a TLS configuration?", "is a TLS configuration?", "a TLS configuration?", "TLS configuration?", "configuration?"], "tokens": ["What", "is", "a", "TLS", "configuration", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1895927667", "fields": {"term": "What does the TLS configuration file contain?", "namespace": "open-p", "hash": 1895927667, "terms": ["What does the TLS configuration file contain?", "does the TLS configuration file contain?", "the TLS configuration file contain?", "TLS configuration file contain?", "configuration file contain?", "file contain?", "contain?"], "tokens": ["What", "does", "the", "TLS", "configuration", "file", "contain", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1145021132", "fields": {"term": "What is the 'files' element?", "namespace": "open-p", "hash": 1145021132, "terms": ["What is the 'files' element?", "is the 'files' element?", "the 'files' element?", "'files' element?", "element?"], "tokens": ["What", "is", "the", "'", "files", "'", "element", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1122636417", "fields": {"term": "What is the 'disable-hostname-validation' option?", "namespace": "open-p", "hash": -1122636417, "terms": ["What is the 'disable-hostname-validation' option?", "is the 'disable-hostname-validation' option?", "the 'disable-hostname-validation' option?", "'disable-hostname-validation' option?", "option?"], "tokens": ["What", "is", "the", "'", "disable", "hostname", "validation", "'", "option", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1317058402", "fields": {"term": "What is the 'authorized-peers' option?", "namespace": "open-p", "hash": 1317058402, "terms": ["What is the 'authorized-peers' option?", "is the 'authorized-peers' option?", "the 'authorized-peers' option?", "'authorized-peers' option?", "option?"], "tokens": ["What", "is", "the", "'", "authorized", "peers", "'", "option", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2083332971", "fields": {"term": "What are TLS cipher suites?", "namespace": "open-p", "hash": -2083332971, "terms": ["What are TLS cipher suites?", "are TLS cipher suites?", "TLS cipher suites?", "cipher suites?", "suites?"], "tokens": ["What", "are", "TLS", "cipher", "suites", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1988351546", "fields": {"term": "What are TLS protocol versions?", "namespace": "open-p", "hash": 1988351546, "terms": ["What are TLS protocol versions?", "are TLS protocol versions?", "TLS protocol versions?", "protocol versions?", "versions?"], "tokens": ["What", "are", "TLS", "protocol", "versions", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1977119979", "fields": {"term": "Is 'disable-hostname-validation' enabled by default?", "namespace": "open-p", "hash": 1977119979, "terms": ["Is 'disable-hostname-validation' enabled by default?", "'disable-hostname-validation' enabled by default?", "enabled by default?", "by default?", "default?"], "tokens": ["Is", "'", "disable", "hostname", "validation", "'", "enabled", "by", "default", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1566257622", "fields": {"term": "What happens if 'authorized-peers' is not specified?", "namespace": "open-p", "hash": 1566257622, "terms": ["What happens if 'authorized-peers' is not specified?", "happens if 'authorized-peers' is not specified?", "if 'authorized-peers' is not specified?", "'authorized-peers' is not specified?", "is not specified?", "not specified?", "specified?"], "tokens": ["What", "happens", "if", "'", "authorized", "peers", "'", "is", "not", "specified", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-702527181", "fields": {"term": "What is the purpose of 'accepted-ciphers' option?", "namespace": "open-p", "hash": -702527181, "terms": ["What is the purpose of 'accepted-ciphers' option?", "is the purpose of 'accepted-ciphers' option?", "the purpose of 'accepted-ciphers' option?", "purpose of 'accepted-ciphers' option?", "of 'accepted-ciphers' option?", "'accepted-ciphers' option?", "option?"], "tokens": ["What", "is", "the", "purpose", "of", "'", "accepted", "ciphers", "'", "option", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1035360755", "fields": {"term": "What is the private key file format?", "namespace": "open-p", "hash": 1035360755, "terms": ["What is the private key file format?", "is the private key file format?", "the private key file format?", "private key file format?", "key file format?", "file format?", "format?"], "tokens": ["What", "is", "the", "private", "key", "file", "format", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::861296797", "fields": {"term": "What is the certificate chain format?", "namespace": "open-p", "hash": 861296797, "terms": ["What is the certificate chain format?", "is the certificate chain format?", "the certificate chain format?", "certificate chain format?", "chain format?", "format?"], "tokens": ["What", "is", "the", "certificate", "chain", "format", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::519811138", "fields": {"term": "What format should ca-certificates be in?", "namespace": "open-p", "hash": 519811138, "terms": ["What format should ca-certificates be in?", "format should ca-certificates be in?", "should ca-certificates be in?", "ca-certificates be in?", "be in?", "in?"], "tokens": ["What", "format", "should", "ca", "certificates", "be", "in", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::273589559", "fields": {"term": "What is required-credentials?", "namespace": "open-p", "hash": 273589559, "terms": ["What is required-credentials?", "is required-credentials?", "required-credentials?"], "tokens": ["What", "is", "required", "credentials", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-940033656", "fields": {"term": "Is the field parameter required?", "namespace": "open-p", "hash": -940033656, "terms": ["Is the field parameter required?", "the field parameter required?", "field parameter required?", "parameter required?", "required?"], "tokens": ["Is", "the", "field", "parameter", "required", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1803871321", "fields": {"term": "Is the must-match parameter required?", "namespace": "open-p", "hash": -1803871321, "terms": ["Is the must-match parameter required?", "the must-match parameter required?", "must-match parameter required?", "parameter required?", "required?"], "tokens": ["Is", "the", "must", "match", "parameter", "required", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-538888737", "fields": {"term": "What is authorized-peers?", "namespace": "open-p", "hash": -538888737, "terms": ["What is authorized-peers?", "is authorized-peers?", "authorized-peers?"], "tokens": ["What", "is", "authorized", "peers", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::629906391", "fields": {"term": "What is a credential rule-set?", "namespace": "open-p", "hash": 629906391, "terms": ["What is a credential rule-set?", "is a credential rule-set?", "a credential rule-set?", "credential rule-set?", "rule-set?"], "tokens": ["What", "is", "a", "credential", "rule", "set", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-370355112", "fields": {"term": "What is the must-match pattern?", "namespace": "open-p", "hash": -370355112, "terms": ["What is the must-match pattern?", "is the must-match pattern?", "the must-match pattern?", "must-match pattern?", "pattern?"], "tokens": ["What", "is", "the", "must", "match", "pattern", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-287898783", "fields": {"term": "What happens when authorized-peers is not provided?", "namespace": "open-p", "hash": -287898783, "terms": ["What happens when authorized-peers is not provided?", "happens when authorized-peers is not provided?", "when authorized-peers is not provided?", "authorized-peers is not provided?", "is not provided?", "not provided?", "provided?"], "tokens": ["What", "happens", "when", "authorized", "peers", "is", "not", "provided", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-543768900", "fields": {"term": "What is required to be considered authorized?", "namespace": "open-p", "hash": -543768900, "terms": ["What is required to be considered authorized?", "is required to be considered authorized?", "required to be considered authorized?", "to be considered authorized?", "be considered authorized?", "considered authorized?", "authorized?"], "tokens": ["What", "is", "required", "to", "be", "considered", "authorized", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1673653172", "fields": {"term": "What information is stored in authorized-peers?", "namespace": "open-p", "hash": -1673653172, "terms": ["What information is stored in authorized-peers?", "information is stored in authorized-peers?", "is stored in authorized-peers?", "stored in authorized-peers?", "in authorized-peers?", "authorized-peers?"], "tokens": ["What", "information", "is", "stored", "in", "authorized", "peers", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2007811260", "fields": {"term": "What are required-credentials in authorized-peers?", "namespace": "open-p", "hash": -2007811260, "terms": ["What are required-credentials in authorized-peers?", "are required-credentials in authorized-peers?", "required-credentials in authorized-peers?", "in authorized-peers?", "authorized-peers?"], "tokens": ["What", "are", "required", "credentials", "in", "authorized", "peers", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::838545240", "fields": {"term": "What is the description of the monitoring authorized-peer?", "namespace": "open-p", "hash": 838545240, "terms": ["What is the description of the monitoring authorized-peer?", "is the description of the monitoring authorized-peer?", "the description of the monitoring authorized-peer?", "description of the monitoring authorized-peer?", "of the monitoring authorized-peer?", "the monitoring authorized-peer?", "monitoring authorized-peer?", "authorized-peer?"], "tokens": ["What", "is", "the", "description", "of", "the", "monitoring", "authorized", "peer", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1071779574", "fields": {"term": "What must match the SAN_DNS field in the monitoring authorized-peer?", "namespace": "open-p", "hash": -1071779574, "terms": ["What must match the SAN_DNS field in the monitoring authorized-peer?", "must match the SAN_DNS field in the monitoring authorized-peer?", "match the SAN_DNS field in the monitoring authorized-peer?", "the SAN_DNS field in the monitoring authorized-peer?", "SAN_DNS field in the monitoring authorized-peer?", "field in the monitoring authorized-peer?", "in the monitoring authorized-peer?", "the monitoring authorized-peer?", "monitoring authorized-peer?", "authorized-peer?"], "tokens": ["What", "must", "match", "the", "SAN_DNS", "field", "in", "the", "monitoring", "authorized", "peer", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1816007936", "fields": {"term": "What is the name of the authorized-peer intended for cluster-internal node P2P access?", "namespace": "open-p", "hash": 1816007936, "terms": ["What is the name of the authorized-peer intended for cluster-internal node P2P access?", "is the name of the authorized-peer intended for cluster-internal node P2P access?", "the name of the authorized-peer intended for cluster-internal node P2P access?", "name of the authorized-peer intended for cluster-internal node P2P access?", "of the authorized-peer intended for cluster-internal node P2P access?", "the authorized-peer intended for cluster-internal node P2P access?", "authorized-peer intended for cluster-internal node P2P access?", "intended for cluster-internal node P2P access?", "for cluster-internal node P2P access?", "cluster-internal node P2P access?", "node P2P access?", "P2P access?", "access?"], "tokens": ["What", "is", "the", "name", "of", "the", "authorized", "peer", "intended", "for", "cluster", "internal", "node", "P2P", "access", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1697645776", "fields": {"term": "What are the constraints of Vespa TLS stack?", "namespace": "open-p", "hash": -1697645776, "terms": ["What are the constraints of Vespa TLS stack?", "are the constraints of Vespa TLS stack?", "the constraints of Vespa TLS stack?", "constraints of Vespa TLS stack?", "of Vespa TLS stack?", "Vespa TLS stack?", "TLS stack?", "stack?"], "tokens": ["What", "are", "the", "constraints", "of", "Vespa", "TLS", "stack", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-29964836", "fields": {"term": "What cipher suites are supported?", "namespace": "open-p", "hash": -29964836, "terms": ["What cipher suites are supported?", "cipher suites are supported?", "suites are supported?", "are supported?", "supported?"], "tokens": ["What", "cipher", "suites", "are", "supported", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1783147815", "fields": {"term": "What is forward secrecy?", "namespace": "open-p", "hash": 1783147815, "terms": ["What is forward secrecy?", "is forward secrecy?", "forward secrecy?", "secrecy?"], "tokens": ["What", "is", "forward", "secrecy", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-663196385", "fields": {"term": "What is AEAD and its properties?", "namespace": "open-p", "hash": -663196385, "terms": ["What is AEAD and its properties?", "is AEAD and its properties?", "AEAD and its properties?", "and its properties?", "its properties?", "properties?"], "tokens": ["What", "is", "AEAD", "and", "its", "properties", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2137317049", "fields": {"term": "Why is TLS compression disabled?", "namespace": "open-p", "hash": -2137317049, "terms": ["Why is TLS compression disabled?", "is TLS compression disabled?", "TLS compression disabled?", "compression disabled?", "disabled?"], "tokens": ["Why", "is", "TLS", "compression", "disabled", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::516133322", "fields": {"term": "Why is TLS session resumption explicitly disabled for Vespa?", "namespace": "open-p", "hash": 516133322, "terms": ["Why is TLS session resumption explicitly disabled for Vespa?", "is TLS session resumption explicitly disabled for Vespa?", "TLS session resumption explicitly disabled for Vespa?", "session resumption explicitly disabled for Vespa?", "resumption explicitly disabled for Vespa?", "explicitly disabled for Vespa?", "disabled for Vespa?", "for Vespa?", "Vespa?"], "tokens": ["Why", "is", "TLS", "session", "resumption", "explicitly", "disabled", "for", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1375172089", "fields": {"term": "Does the Vespa application container edge support session resumption?", "namespace": "open-p", "hash": -1375172089, "terms": ["Does the Vespa application container edge support session resumption?", "the Vespa application container edge support session resumption?", "Vespa application container edge support session resumption?", "application container edge support session resumption?", "container edge support session resumption?", "edge support session resumption?", "support session resumption?", "session resumption?", "resumption?"], "tokens": ["Does", "the", "Vespa", "application", "container", "edge", "support", "session", "resumption", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-688055244", "fields": {"term": "What is the default TLS configuration used by Vespa?", "namespace": "open-p", "hash": -688055244, "terms": ["What is the default TLS configuration used by Vespa?", "is the default TLS configuration used by Vespa?", "the default TLS configuration used by Vespa?", "default TLS configuration used by Vespa?", "TLS configuration used by Vespa?", "configuration used by Vespa?", "used by Vespa?", "by Vespa?", "Vespa?"], "tokens": ["What", "is", "the", "default", "TLS", "configuration", "used", "by", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::233970669", "fields": {"term": "What is TLSv1.3?", "namespace": "open-p", "hash": 233970669, "terms": ["What is TLSv1.3?", "is TLSv1.3?", "TLSv1.3?"], "tokens": ["What", "is", "TLSv1", "3", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-915704960", "fields": {"term": "Is Java runtime limitation present?", "namespace": "open-p", "hash": -915704960, "terms": ["Is Java runtime limitation present?", "Java runtime limitation present?", "runtime limitation present?", "limitation present?", "present?"], "tokens": ["Is", "Java", "runtime", "limitation", "present", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::756471010", "fields": {"term": "Which backends support TLSv1.3?", "namespace": "open-p", "hash": 756471010, "terms": ["Which backends support TLSv1.3?", "backends support TLSv1.3?", "support TLSv1.3?", "TLSv1.3?"], "tokens": ["Which", "backends", "support", "TLSv1", "3", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1428578351", "fields": {"term": "What is the TLSv1.2 version?", "namespace": "open-p", "hash": 1428578351, "terms": ["What is the TLSv1.2 version?", "is the TLSv1.2 version?", "the TLSv1.2 version?", "TLSv1.2 version?", "version?"], "tokens": ["What", "is", "the", "TLSv1", "2", "version", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1290549664", "fields": {"term": "What are the TLSv1.3 cipher suites?", "namespace": "open-p", "hash": -1290549664, "terms": ["What are the TLSv1.3 cipher suites?", "are the TLSv1.3 cipher suites?", "the TLSv1.3 cipher suites?", "TLSv1.3 cipher suites?", "cipher suites?", "suites?"], "tokens": ["What", "are", "the", "TLSv1", "3", "cipher", "suites", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-124294995", "fields": {"term": "What are the JDK 12+ cipher suites?", "namespace": "open-p", "hash": -124294995, "terms": ["What are the JDK 12+ cipher suites?", "are the JDK 12+ cipher suites?", "the JDK 12+ cipher suites?", "JDK 12+ cipher suites?", "12+ cipher suites?", "cipher suites?", "suites?"], "tokens": ["What", "are", "the", "JDK", "12", "+", "cipher", "suites", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::987311013", "fields": {"term": "What is the ECDHE-RSA cipher suite?", "namespace": "open-p", "hash": 987311013, "terms": ["What is the ECDHE-RSA cipher suite?", "is the ECDHE-RSA cipher suite?", "the ECDHE-RSA cipher suite?", "ECDHE-RSA cipher suite?", "cipher suite?", "suite?"], "tokens": ["What", "is", "the", "ECDHE", "RSA", "cipher", "suite", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1483457640", "fields": {"term": "What is the AES-256-GCM cipher suite?", "namespace": "open-p", "hash": -1483457640, "terms": ["What is the AES-256-GCM cipher suite?", "is the AES-256-GCM cipher suite?", "the AES-256-GCM cipher suite?", "AES-256-GCM cipher suite?", "cipher suite?", "suite?"], "tokens": ["What", "is", "the", "AES", "256", "GCM", "cipher", "suite", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1701736381", "fields": {"term": "What is the CHACHA20_POLY1305_SHA256 cipher suite?", "namespace": "open-p", "hash": -1701736381, "terms": ["What is the CHACHA20_POLY1305_SHA256 cipher suite?", "is the CHACHA20_POLY1305_SHA256 cipher suite?", "the CHACHA20_POLY1305_SHA256 cipher suite?", "CHACHA20_POLY1305_SHA256 cipher suite?", "cipher suite?", "suite?"], "tokens": ["What", "is", "the", "CHACHA20_POLY1305_SHA256", "cipher", "suite", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-843041629", "fields": {"term": "What are the ECDSA cipher suites?", "namespace": "open-p", "hash": -843041629, "terms": ["What are the ECDSA cipher suites?", "are the ECDSA cipher suites?", "the ECDSA cipher suites?", "ECDSA cipher suites?", "cipher suites?", "suites?"], "tokens": ["What", "are", "the", "ECDSA", "cipher", "suites", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1023914000", "fields": {"term": "What is the AES-128-GCM cipher suite?", "namespace": "open-p", "hash": 1023914000, "terms": ["What is the AES-128-GCM cipher suite?", "is the AES-128-GCM cipher suite?", "the AES-128-GCM cipher suite?", "AES-128-GCM cipher suite?", "cipher suite?", "suite?"], "tokens": ["What", "is", "the", "AES", "128", "GCM", "cipher", "suite", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1063449012", "fields": {"term": "What is a Vespa system?", "namespace": "open-p", "hash": -1063449012, "terms": ["What is a Vespa system?", "is a Vespa system?", "a Vespa system?", "Vespa system?", "system?"], "tokens": ["What", "is", "a", "Vespa", "system", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1666283000", "fields": {"term": "What is an admin cluster?", "namespace": "open-p", "hash": 1666283000, "terms": ["What is an admin cluster?", "is an admin cluster?", "an admin cluster?", "admin cluster?", "cluster?"], "tokens": ["What", "is", "an", "admin", "cluster", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::-629223237", "fields": {"term": "How to create multinode system?", "namespace": "open-p", "hash": -629223237, "terms": ["How to create multinode system?", "to create multinode system?", "create multinode system?", "multinode system?", "system?"], "tokens": ["How", "to", "create", "multinode", "system", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2088461816", "fields": {"term": "What are slobroks?", "namespace": "open-p", "hash": -2088461816, "terms": ["What are slobroks?", "are slobroks?", "slobroks?"], "tokens": ["What", "are", "slobroks", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1552481055", "fields": {"term": "What is the procedure for multinode setup?", "namespace": "open-p", "hash": -1552481055, "terms": ["What is the procedure for multinode setup?", "is the procedure for multinode setup?", "the procedure for multinode setup?", "procedure for multinode setup?", "for multinode setup?", "multinode setup?", "setup?"], "tokens": ["What", "is", "the", "procedure", "for", "multinode", "setup", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::107176127", "fields": {"term": "Where can I find the multinode-HA procedure?", "namespace": "open-p", "hash": 107176127, "terms": ["Where can I find the multinode-HA procedure?", "can I find the multinode-HA procedure?", "I find the multinode-HA procedure?", "find the multinode-HA procedure?", "the multinode-HA procedure?", "multinode-HA procedure?", "procedure?"], "tokens": ["Where", "can", "I", "find", "the", "multinode", "HA", "procedure", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1377391871", "fields": {"term": "Can AWS EC2 be used for Vespa multinode setup?", "namespace": "open-p", "hash": -1377391871, "terms": ["Can AWS EC2 be used for Vespa multinode setup?", "AWS EC2 be used for Vespa multinode setup?", "EC2 be used for Vespa multinode setup?", "be used for Vespa multinode setup?", "used for Vespa multinode setup?", "for Vespa multinode setup?", "Vespa multinode setup?", "multinode setup?", "setup?"], "tokens": ["Can", "AWS", "EC2", "be", "used", "for", "Vespa", "multinode", "setup", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1024035252", "fields": {"term": "What is the recommended number of hosts for the multinode setup?", "namespace": "open-p", "hash": -1024035252, "terms": ["What is the recommended number of hosts for the multinode setup?", "is the recommended number of hosts for the multinode setup?", "the recommended number of hosts for the multinode setup?", "recommended number of hosts for the multinode setup?", "number of hosts for the multinode setup?", "of hosts for the multinode setup?", "hosts for the multinode setup?", "for the multinode setup?", "the multinode setup?", "multinode setup?", "setup?"], "tokens": ["What", "is", "the", "recommended", "number", "of", "hosts", "for", "the", "multinode", "setup", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-980923602", "fields": {"term": "Is AWS Auto Scaling compatible with Vespa?", "namespace": "open-p", "hash": -980923602, "terms": ["Is AWS Auto Scaling compatible with Vespa?", "AWS Auto Scaling compatible with Vespa?", "Auto Scaling compatible with Vespa?", "Scaling compatible with Vespa?", "compatible with Vespa?", "with Vespa?", "Vespa?"], "tokens": ["Is", "AWS", "Auto", "Scaling", "compatible", "with", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1456457330", "fields": {"term": "Where can I find the autoscaling Q/A?", "namespace": "open-p", "hash": 1456457330, "terms": ["Where can I find the autoscaling Q/A?", "can I find the autoscaling Q/A?", "I find the autoscaling Q/A?", "find the autoscaling Q/A?", "the autoscaling Q/A?", "autoscaling Q/A?", "Q/A?"], "tokens": ["Where", "can", "I", "find", "the", "autoscaling", "Q", "/", "A", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1505058500", "fields": {"term": "What is the purpose of multinode setup?", "namespace": "open-p", "hash": 1505058500, "terms": ["What is the purpose of multinode setup?", "is the purpose of multinode setup?", "the purpose of multinode setup?", "purpose of multinode setup?", "of multinode setup?", "multinode setup?", "setup?"], "tokens": ["What", "is", "the", "purpose", "of", "multinode", "setup", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::317903126", "fields": {"term": "What are the Vespa concepts used in the multinode setup?", "namespace": "open-p", "hash": 317903126, "terms": ["What are the Vespa concepts used in the multinode setup?", "are the Vespa concepts used in the multinode setup?", "the Vespa concepts used in the multinode setup?", "Vespa concepts used in the multinode setup?", "concepts used in the multinode setup?", "used in the multinode setup?", "in the multinode setup?", "the multinode setup?", "multinode setup?", "setup?"], "tokens": ["What", "are", "the", "Vespa", "concepts", "used", "in", "the", "multinode", "setup", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1362177146", "fields": {"term": "How do I provision nodes?", "namespace": "open-p", "hash": 1362177146, "terms": ["How do I provision nodes?", "do I provision nodes?", "I provision nodes?", "provision nodes?", "nodes?"], "tokens": ["How", "do", "I", "provision", "nodes", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1364838205", "fields": {"term": "What is the security group setup?", "namespace": "open-p", "hash": 1364838205, "terms": ["What is the security group setup?", "is the security group setup?", "the security group setup?", "security group setup?", "group setup?", "setup?"], "tokens": ["What", "is", "the", "security", "group", "setup", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-84803289", "fields": {"term": "What is the recommended AMI for Vespa?", "namespace": "open-p", "hash": -84803289, "terms": ["What is the recommended AMI for Vespa?", "is the recommended AMI for Vespa?", "the recommended AMI for Vespa?", "recommended AMI for Vespa?", "AMI for Vespa?", "for Vespa?", "Vespa?"], "tokens": ["What", "is", "the", "recommended", "AMI", "for", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1106720531", "fields": {"term": "What is the format to find hostname for login?", "namespace": "open-p", "hash": 1106720531, "terms": ["What is the format to find hostname for login?", "is the format to find hostname for login?", "the format to find hostname for login?", "format to find hostname for login?", "to find hostname for login?", "find hostname for login?", "hostname for login?", "for login?", "login?"], "tokens": ["What", "is", "the", "format", "to", "find", "hostname", "for", "login", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1349035790", "fields": {"term": "What is a config server cluster?", "namespace": "open-p", "hash": 1349035790, "terms": ["What is a config server cluster?", "is a config server cluster?", "a config server cluster?", "config server cluster?", "server cluster?", "cluster?"], "tokens": ["What", "is", "a", "config", "server", "cluster", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1646931577", "fields": {"term": "How do you verify a successful start?", "namespace": "open-p", "hash": -1646931577, "terms": ["How do you verify a successful start?", "do you verify a successful start?", "you verify a successful start?", "verify a successful start?", "a successful start?", "successful start?", "start?"], "tokens": ["How", "do", "you", "verify", "a", "successful", "start", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1688424156", "fields": {"term": "What is the start sequence for config server?", "namespace": "open-p", "hash": -1688424156, "terms": ["What is the start sequence for config server?", "is the start sequence for config server?", "the start sequence for config server?", "start sequence for config server?", "sequence for config server?", "for config server?", "config server?", "server?"], "tokens": ["What", "is", "the", "start", "sequence", "for", "config", "server", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::618336191", "fields": {"term": "What needs editing in hosts.xml?", "namespace": "open-p", "hash": 618336191, "terms": ["What needs editing in hosts.xml?", "needs editing in hosts.xml?", "editing in hosts.xml?", "in hosts.xml?", "hosts.xml?"], "tokens": ["What", "needs", "editing", "in", "hosts", "xml", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2104130814", "fields": {"term": "How to validate Vespa installation?", "namespace": "open-p", "hash": 2104130814, "terms": ["How to validate Vespa installation?", "to validate Vespa installation?", "validate Vespa installation?", "Vespa installation?", "installation?"], "tokens": ["How", "to", "validate", "Vespa", "installation", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::611788925", "fields": {"term": "Which steps to follow for checking the health interfaces?", "namespace": "open-p", "hash": 611788925, "terms": ["Which steps to follow for checking the health interfaces?", "steps to follow for checking the health interfaces?", "to follow for checking the health interfaces?", "follow for checking the health interfaces?", "for checking the health interfaces?", "checking the health interfaces?", "the health interfaces?", "health interfaces?", "interfaces?"], "tokens": ["Which", "steps", "to", "follow", "for", "checking", "the", "health", "interfaces", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1553993169", "fields": {"term": "What are the native Vespa ports?", "namespace": "open-p", "hash": -1553993169, "terms": ["What are the native Vespa ports?", "are the native Vespa ports?", "the native Vespa ports?", "native Vespa ports?", "Vespa ports?", "ports?"], "tokens": ["What", "are", "the", "native", "Vespa", "ports", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1757797729", "fields": {"term": "What is the sample application port?", "namespace": "open-p", "hash": -1757797729, "terms": ["What is the sample application port?", "is the sample application port?", "the sample application port?", "sample application port?", "application port?", "port?"], "tokens": ["What", "is", "the", "sample", "application", "port", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1853530387", "fields": {"term": "How do I terminate AWS instances?", "namespace": "open-p", "hash": 1853530387, "terms": ["How do I terminate AWS instances?", "do I terminate AWS instances?", "I terminate AWS instances?", "terminate AWS instances?", "AWS instances?", "instances?"], "tokens": ["How", "do", "I", "terminate", "AWS", "instances", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-217067618", "fields": {"term": "What is a multinode install?", "namespace": "open-p", "hash": -217067618, "terms": ["What is a multinode install?", "is a multinode install?", "a multinode install?", "multinode install?", "install?"], "tokens": ["What", "is", "a", "multinode", "install", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-384250424", "fields": {"term": "How can I create a cluster?", "namespace": "open-p", "hash": -384250424, "terms": ["How can I create a cluster?", "can I create a cluster?", "I create a cluster?", "create a cluster?", "a cluster?", "cluster?"], "tokens": ["How", "can", "I", "create", "a", "cluster", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::527443271", "fields": {"term": "How many instances can I create?", "namespace": "open-p", "hash": 527443271, "terms": ["How many instances can I create?", "many instances can I create?", "instances can I create?", "can I create?", "I create?", "create?"], "tokens": ["How", "many", "instances", "can", "I", "create", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-946943748", "fields": {"term": "What are the security group inbound rules?", "namespace": "open-p", "hash": -946943748, "terms": ["What are the security group inbound rules?", "are the security group inbound rules?", "the security group inbound rules?", "security group inbound rules?", "group inbound rules?", "inbound rules?", "rules?"], "tokens": ["What", "are", "the", "security", "group", "inbound", "rules", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-733970992", "fields": {"term": "What is the status of registered container instances?", "namespace": "open-p", "hash": -733970992, "terms": ["What is the status of registered container instances?", "is the status of registered container instances?", "the status of registered container instances?", "status of registered container instances?", "of registered container instances?", "registered container instances?", "container instances?", "instances?"], "tokens": ["What", "is", "the", "status", "of", "registered", "container", "instances", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1210039102", "fields": {"term": "What is the Cluster name?", "namespace": "open-p", "hash": -1210039102, "terms": ["What is the Cluster name?", "is the Cluster name?", "the Cluster name?", "Cluster name?", "name?"], "tokens": ["What", "is", "the", "Cluster", "name", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1063201861", "fields": {"term": "What should I do after clicking Create?", "namespace": "open-p", "hash": 1063201861, "terms": ["What should I do after clicking Create?", "should I do after clicking Create?", "I do after clicking Create?", "do after clicking Create?", "after clicking Create?", "clicking Create?", "Create?"], "tokens": ["What", "should", "I", "do", "after", "clicking", "Create", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1292882049", "fields": {"term": "What are public hostnames used for?", "namespace": "open-p", "hash": 1292882049, "terms": ["What are public hostnames used for?", "are public hostnames used for?", "public hostnames used for?", "hostnames used for?", "used for?", "for?"], "tokens": ["What", "are", "public", "hostnames", "used", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1533024525", "fields": {"term": "How do I find a hostname in Vespa?", "namespace": "open-p", "hash": -1533024525, "terms": ["How do I find a hostname in Vespa?", "do I find a hostname in Vespa?", "I find a hostname in Vespa?", "find a hostname in Vespa?", "a hostname in Vespa?", "hostname in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["How", "do", "I", "find", "a", "hostname", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-583423945", "fields": {"term": "How do I create a task definition?", "namespace": "open-p", "hash": -583423945, "terms": ["How do I create a task definition?", "do I create a task definition?", "I create a task definition?", "create a task definition?", "a task definition?", "task definition?", "definition?"], "tokens": ["How", "do", "I", "create", "a", "task", "definition", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1576810874", "fields": {"term": "What is EC2?", "namespace": "open-p", "hash": -1576810874, "terms": ["What is EC2?", "is EC2?", "EC2?"], "tokens": ["What", "is", "EC2", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::-1414042618", "fields": {"term": "What is networkMode?", "namespace": "open-p", "hash": -1414042618, "terms": ["What is networkMode?", "is networkMode?", "networkMode?"], "tokens": ["What", "is", "networkMode", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1668374690", "fields": {"term": "What is family?", "namespace": "open-p", "hash": -1668374690, "terms": ["What is family?", "is family?", "family?"], "tokens": ["What", "is", "family", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1413937033", "fields": {"term": "How do I run a task?", "namespace": "open-p", "hash": 1413937033, "terms": ["How do I run a task?", "do I run a task?", "I run a task?", "run a task?", "a task?", "task?"], "tokens": ["How", "do", "I", "run", "a", "task", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::247447573", "fields": {"term": "How do I validate the config servers?", "namespace": "open-p", "hash": 247447573, "terms": ["How do I validate the config servers?", "do I validate the config servers?", "I validate the config servers?", "validate the config servers?", "the config servers?", "config servers?", "servers?"], "tokens": ["How", "do", "I", "validate", "the", "config", "servers", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-468305185", "fields": {"term": "What is memoryReservation?", "namespace": "open-p", "hash": -468305185, "terms": ["What is memoryReservation?", "is memoryReservation?", "memoryReservation?"], "tokens": ["What", "is", "memoryReservation", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1629267518", "fields": {"term": "What is multinode-HA?", "namespace": "open-p", "hash": 1629267518, "terms": ["What is multinode-HA?", "is multinode-HA?", "multinode-HA?"], "tokens": ["What", "is", "multinode", "HA", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::-1845969731", "fields": {"term": "What is a Task Definition?", "namespace": "open-p", "hash": -1845969731, "terms": ["What is a Task Definition?", "is a Task Definition?", "a Task Definition?", "Task Definition?", "Definition?"], "tokens": ["What", "is", "a", "Task", "Definition", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::171332473", "fields": {"term": "How to create a new Task Definition?", "namespace": "open-p", "hash": 171332473, "terms": ["How to create a new Task Definition?", "to create a new Task Definition?", "create a new Task Definition?", "a new Task Definition?", "new Task Definition?", "Task Definition?", "Definition?"], "tokens": ["How", "to", "create", "a", "new", "Task", "Definition", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-957506435", "fields": {"term": "What is the launch type?", "namespace": "open-p", "hash": -957506435, "terms": ["What is the launch type?", "is the launch type?", "the launch type?", "launch type?", "type?"], "tokens": ["What", "is", "the", "launch", "type", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1044461769", "fields": {"term": "How to validate startup?", "namespace": "open-p", "hash": -1044461769, "terms": ["How to validate startup?", "to validate startup?", "validate startup?", "startup?"], "tokens": ["How", "to", "validate", "startup", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2094111789", "fields": {"term": "How do I delete an AWS cluster?", "namespace": "open-p", "hash": -2094111789, "terms": ["How do I delete an AWS cluster?", "do I delete an AWS cluster?", "I delete an AWS cluster?", "delete an AWS cluster?", "an AWS cluster?", "AWS cluster?", "cluster?"], "tokens": ["How", "do", "I", "delete", "an", "AWS", "cluster", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::719786431", "fields": {"term": "What should I remember when using AWS?", "namespace": "open-p", "hash": 719786431, "terms": ["What should I remember when using AWS?", "should I remember when using AWS?", "I remember when using AWS?", "remember when using AWS?", "when using AWS?", "using AWS?", "AWS?"], "tokens": ["What", "should", "I", "remember", "when", "using", "AWS", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1023236202", "fields": {"term": "What is collected in real time?", "namespace": "open-p", "hash": -1023236202, "terms": ["What is collected in real time?", "is collected in real time?", "collected in real time?", "in real time?", "real time?", "time?"], "tokens": ["What", "is", "collected", "in", "real", "time", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1052072539", "fields": {"term": "How do I view log messages?", "namespace": "open-p", "hash": 1052072539, "terms": ["How do I view log messages?", "do I view log messages?", "I view log messages?", "view log messages?", "log messages?", "messages?"], "tokens": ["How", "do", "I", "view", "log", "messages", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1163446275", "fields": {"term": "What is the admin cluster?", "namespace": "open-p", "hash": -1163446275, "terms": ["What is the admin cluster?", "is the admin cluster?", "the admin cluster?", "admin cluster?", "cluster?"], "tokens": ["What", "is", "the", "admin", "cluster", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1876938789", "fields": {"term": "How do you change the system safely?", "namespace": "open-p", "hash": -1876938789, "terms": ["How do you change the system safely?", "do you change the system safely?", "you change the system safely?", "change the system safely?", "the system safely?", "system safely?", "safely?"], "tokens": ["How", "do", "you", "change", "the", "system", "safely", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1575640548", "fields": {"term": "Can you deploy a changed application during live traffic?", "namespace": "open-p", "hash": 1575640548, "terms": ["Can you deploy a changed application during live traffic?", "you deploy a changed application during live traffic?", "deploy a changed application during live traffic?", "a changed application during live traffic?", "changed application during live traffic?", "application during live traffic?", "during live traffic?", "live traffic?", "traffic?"], "tokens": ["Can", "you", "deploy", "a", "changed", "application", "during", "live", "traffic", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-960269510", "fields": {"term": "When must restarts be done?", "namespace": "open-p", "hash": -960269510, "terms": ["When must restarts be done?", "must restarts be done?", "restarts be done?", "be done?", "done?"], "tokens": ["When", "must", "restarts", "be", "done", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-413369850", "fields": {"term": "How to avoid disruption when restarting nodes?", "namespace": "open-p", "hash": -413369850, "terms": ["How to avoid disruption when restarting nodes?", "to avoid disruption when restarting nodes?", "avoid disruption when restarting nodes?", "disruption when restarting nodes?", "when restarting nodes?", "restarting nodes?", "nodes?"], "tokens": ["How", "to", "avoid", "disruption", "when", "restarting", "nodes", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::101253852", "fields": {"term": "How can schemas be deployed?", "namespace": "open-p", "hash": 101253852, "terms": ["How can schemas be deployed?", "can schemas be deployed?", "schemas be deployed?", "be deployed?", "deployed?"], "tokens": ["How", "can", "schemas", "be", "deployed", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::569647817", "fields": {"term": "What is the optimal schema mapping?", "namespace": "open-p", "hash": 569647817, "terms": ["What is the optimal schema mapping?", "is the optimal schema mapping?", "the optimal schema mapping?", "optimal schema mapping?", "schema mapping?", "mapping?"], "tokens": ["What", "is", "the", "optimal", "schema", "mapping", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::798408434", "fields": {"term": "How can load be distributed over hosts?", "namespace": "open-p", "hash": 798408434, "terms": ["How can load be distributed over hosts?", "can load be distributed over hosts?", "load be distributed over hosts?", "be distributed over hosts?", "distributed over hosts?", "over hosts?", "hosts?"], "tokens": ["How", "can", "load", "be", "distributed", "over", "hosts", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-31103355", "fields": {"term": "How can interference between proton processes be reduced?", "namespace": "open-p", "hash": -31103355, "terms": ["How can interference between proton processes be reduced?", "can interference between proton processes be reduced?", "interference between proton processes be reduced?", "between proton processes be reduced?", "proton processes be reduced?", "processes be reduced?", "be reduced?", "reduced?"], "tokens": ["How", "can", "interference", "between", "proton", "processes", "be", "reduced", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-882439089", "fields": {"term": "What is AWS Auto Scaling?", "namespace": "open-p", "hash": -882439089, "terms": ["What is AWS Auto Scaling?", "is AWS Auto Scaling?", "AWS Auto Scaling?", "Auto Scaling?", "Scaling?"], "tokens": ["What", "is", "AWS", "Auto", "Scaling", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2053897768", "fields": {"term": "Can AWS Auto Scaling be used?", "namespace": "open-p", "hash": 2053897768, "terms": ["Can AWS Auto Scaling be used?", "AWS Auto Scaling be used?", "Auto Scaling be used?", "Scaling be used?", "be used?", "used?"], "tokens": ["Can", "AWS", "Auto", "Scaling", "be", "used", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-299239734", "fields": {"term": "How to manage stateless nodes?", "namespace": "open-p", "hash": -299239734, "terms": ["How to manage stateless nodes?", "to manage stateless nodes?", "manage stateless nodes?", "stateless nodes?", "nodes?"], "tokens": ["How", "to", "manage", "stateless", "nodes", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1962159876", "fields": {"term": "How to manage stateful nodes?", "namespace": "open-p", "hash": -1962159876, "terms": ["How to manage stateful nodes?", "to manage stateful nodes?", "manage stateful nodes?", "stateful nodes?", "nodes?"], "tokens": ["How", "to", "manage", "stateful", "nodes", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1400246535", "fields": {"term": "Is autoscaling the stateful nodes safe?", "namespace": "open-p", "hash": 1400246535, "terms": ["Is autoscaling the stateful nodes safe?", "autoscaling the stateful nodes safe?", "the stateful nodes safe?", "stateful nodes safe?", "nodes safe?", "safe?"], "tokens": ["Is", "autoscaling", "the", "stateful", "nodes", "safe", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1519986572", "fields": {"term": "What is the recommendation on autoscaling?", "namespace": "open-p", "hash": -1519986572, "terms": ["What is the recommendation on autoscaling?", "is the recommendation on autoscaling?", "the recommendation on autoscaling?", "recommendation on autoscaling?", "on autoscaling?", "autoscaling?"], "tokens": ["What", "is", "the", "recommendation", "on", "autoscaling", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-970963453", "fields": {"term": "How to optimize resources using static node set?", "namespace": "open-p", "hash": -970963453, "terms": ["How to optimize resources using static node set?", "to optimize resources using static node set?", "optimize resources using static node set?", "resources using static node set?", "using static node set?", "static node set?", "node set?", "set?"], "tokens": ["How", "to", "optimize", "resources", "using", "static", "node", "set", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-655222858", "fields": {"term": "What is the multinode application?", "namespace": "open-p", "hash": -655222858, "terms": ["What is the multinode application?", "is the multinode application?", "the multinode application?", "multinode application?", "application?"], "tokens": ["What", "is", "the", "multinode", "application", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::487934280", "fields": {"term": "What is this article about?", "namespace": "open-p", "hash": 487934280, "terms": ["What is this article about?", "is this article about?", "this article about?", "article about?", "about?"], "tokens": ["What", "is", "this", "article", "about", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-737553847", "fields": {"term": "What is the ranking introduction?", "namespace": "open-p", "hash": -737553847, "terms": ["What is the ranking introduction?", "is the ranking introduction?", "the ranking introduction?", "ranking introduction?", "introduction?"], "tokens": ["What", "is", "the", "ranking", "introduction", "?"], "corpus_count": 3, "document_count": 3}}, -{"put": "id:term:term::-122834472", "fields": {"term": "What is wand?", "namespace": "open-p", "hash": -122834472, "terms": ["What is wand?", "is wand?", "wand?"], "tokens": ["What", "is", "wand", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2058338981", "fields": {"term": "How does wand work?", "namespace": "open-p", "hash": 2058338981, "terms": ["How does wand work?", "does wand work?", "wand work?", "work?"], "tokens": ["How", "does", "wand", "work", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1758445887", "fields": {"term": "What problem does wand solve?", "namespace": "open-p", "hash": -1758445887, "terms": ["What problem does wand solve?", "problem does wand solve?", "does wand solve?", "wand solve?", "solve?"], "tokens": ["What", "problem", "does", "wand", "solve", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::974030746", "fields": {"term": "What is dotProduct?", "namespace": "open-p", "hash": 974030746, "terms": ["What is dotProduct?", "is dotProduct?", "dotProduct?"], "tokens": ["What", "is", "dotProduct", "?"], "corpus_count": 3, "document_count": 3}}, -{"put": "id:term:term::1140673992", "fields": {"term": "How does dotProduct differ from wand?", "namespace": "open-p", "hash": 1140673992, "terms": ["How does dotProduct differ from wand?", "does dotProduct differ from wand?", "dotProduct differ from wand?", "differ from wand?", "from wand?", "wand?"], "tokens": ["How", "does", "dotProduct", "differ", "from", "wand", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-712263496", "fields": {"term": "Which is more cost-efficient: wand or dotProduct?", "namespace": "open-p", "hash": -712263496, "terms": ["Which is more cost-efficient: wand or dotProduct?", "is more cost-efficient: wand or dotProduct?", "more cost-efficient: wand or dotProduct?", "cost-efficient: wand or dotProduct?", "wand or dotProduct?", "or dotProduct?", "dotProduct?"], "tokens": ["Which", "is", "more", "cost", "efficient", ":", "wand", "or", "dotProduct", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-235280022", "fields": {"term": "How to compare wand and dotProduct?", "namespace": "open-p", "hash": -235280022, "terms": ["How to compare wand and dotProduct?", "to compare wand and dotProduct?", "compare wand and dotProduct?", "wand and dotProduct?", "and dotProduct?", "dotProduct?"], "tokens": ["How", "to", "compare", "wand", "and", "dotProduct", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-104186299", "fields": {"term": "Can wand and dotProduct be benchmarked?", "namespace": "open-p", "hash": -104186299, "terms": ["Can wand and dotProduct be benchmarked?", "wand and dotProduct be benchmarked?", "and dotProduct be benchmarked?", "dotProduct be benchmarked?", "be benchmarked?", "benchmarked?"], "tokens": ["Can", "wand", "and", "dotProduct", "be", "benchmarked", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-352095697", "fields": {"term": "How is dotProduct used?", "namespace": "open-p", "hash": -352095697, "terms": ["How is dotProduct used?", "is dotProduct used?", "dotProduct used?", "used?"], "tokens": ["How", "is", "dotProduct", "used", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::388538613", "fields": {"term": "What are raw scores?", "namespace": "open-p", "hash": 388538613, "terms": ["What are raw scores?", "are raw scores?", "raw scores?", "scores?"], "tokens": ["What", "are", "raw", "scores", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1137591952", "fields": {"term": "How are raw scores used?", "namespace": "open-p", "hash": -1137591952, "terms": ["How are raw scores used?", "are raw scores used?", "raw scores used?", "scores used?", "used?"], "tokens": ["How", "are", "raw", "scores", "used", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-84444701", "fields": {"term": "How to label dot products for ranking?", "namespace": "open-p", "hash": -84444701, "terms": ["How to label dot products for ranking?", "to label dot products for ranking?", "label dot products for ranking?", "dot products for ranking?", "products for ranking?", "for ranking?", "ranking?"], "tokens": ["How", "to", "label", "dot", "products", "for", "ranking", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2080563618", "fields": {"term": "What is the weightedSet used for?", "namespace": "open-p", "hash": 2080563618, "terms": ["What is the weightedSet used for?", "is the weightedSet used for?", "the weightedSet used for?", "weightedSet used for?", "used for?", "for?"], "tokens": ["What", "is", "the", "weightedSet", "used", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::78410328", "fields": {"term": "How can we use weightedSet for filtering a search?", "namespace": "open-p", "hash": 78410328, "terms": ["How can we use weightedSet for filtering a search?", "can we use weightedSet for filtering a search?", "we use weightedSet for filtering a search?", "use weightedSet for filtering a search?", "weightedSet for filtering a search?", "for filtering a search?", "filtering a search?", "a search?", "search?"], "tokens": ["How", "can", "we", "use", "weightedSet", "for", "filtering", "a", "search", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1078967917", "fields": {"term": "What is the syntax for using a weightedSet in Vespa?", "namespace": "open-p", "hash": -1078967917, "terms": ["What is the syntax for using a weightedSet in Vespa?", "is the syntax for using a weightedSet in Vespa?", "the syntax for using a weightedSet in Vespa?", "syntax for using a weightedSet in Vespa?", "for using a weightedSet in Vespa?", "using a weightedSet in Vespa?", "a weightedSet in Vespa?", "weightedSet in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["What", "is", "the", "syntax", "for", "using", "a", "weightedSet", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::432507018", "fields": {"term": "What is the performance impact of using a weightedSet?", "namespace": "open-p", "hash": 432507018, "terms": ["What is the performance impact of using a weightedSet?", "is the performance impact of using a weightedSet?", "the performance impact of using a weightedSet?", "performance impact of using a weightedSet?", "impact of using a weightedSet?", "of using a weightedSet?", "using a weightedSet?", "a weightedSet?", "weightedSet?"], "tokens": ["What", "is", "the", "performance", "impact", "of", "using", "a", "weightedSet", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-134544747", "fields": {"term": "What type of data models can be used as a filter for weightedSet?", "namespace": "open-p", "hash": -134544747, "terms": ["What type of data models can be used as a filter for weightedSet?", "type of data models can be used as a filter for weightedSet?", "of data models can be used as a filter for weightedSet?", "data models can be used as a filter for weightedSet?", "models can be used as a filter for weightedSet?", "can be used as a filter for weightedSet?", "be used as a filter for weightedSet?", "used as a filter for weightedSet?", "as a filter for weightedSet?", "a filter for weightedSet?", "filter for weightedSet?", "for weightedSet?", "weightedSet?"], "tokens": ["What", "type", "of", "data", "models", "can", "be", "used", "as", "a", "filter", "for", "weightedSet", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-970483825", "fields": {"term": "What are the limitations of using a string field with weightedSet?", "namespace": "open-p", "hash": -970483825, "terms": ["What are the limitations of using a string field with weightedSet?", "are the limitations of using a string field with weightedSet?", "the limitations of using a string field with weightedSet?", "limitations of using a string field with weightedSet?", "of using a string field with weightedSet?", "using a string field with weightedSet?", "a string field with weightedSet?", "string field with weightedSet?", "field with weightedSet?", "with weightedSet?", "weightedSet?"], "tokens": ["What", "are", "the", "limitations", "of", "using", "a", "string", "field", "with", "weightedSet", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1254527640", "fields": {"term": "What are the steps to take to implement weightedSet in Vespa search?", "namespace": "open-p", "hash": 1254527640, "terms": ["What are the steps to take to implement weightedSet in Vespa search?", "are the steps to take to implement weightedSet in Vespa search?", "the steps to take to implement weightedSet in Vespa search?", "steps to take to implement weightedSet in Vespa search?", "to take to implement weightedSet in Vespa search?", "take to implement weightedSet in Vespa search?", "to implement weightedSet in Vespa search?", "implement weightedSet in Vespa search?", "weightedSet in Vespa search?", "in Vespa search?", "Vespa search?", "search?"], "tokens": ["What", "are", "the", "steps", "to", "take", "to", "implement", "weightedSet", "in", "Vespa", "search", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-437534059", "fields": {"term": "What is the alternative for using a big OR expression for filtering a search?", "namespace": "open-p", "hash": -437534059, "terms": ["What is the alternative for using a big OR expression for filtering a search?", "is the alternative for using a big OR expression for filtering a search?", "the alternative for using a big OR expression for filtering a search?", "alternative for using a big OR expression for filtering a search?", "for using a big OR expression for filtering a search?", "using a big OR expression for filtering a search?", "a big OR expression for filtering a search?", "big OR expression for filtering a search?", "OR expression for filtering a search?", "expression for filtering a search?", "for filtering a search?", "filtering a search?", "a search?", "search?"], "tokens": ["What", "is", "the", "alternative", "for", "using", "a", "big", "OR", "expression", "for", "filtering", "a", "search", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1203652047", "fields": {"term": "What is the rawScore feature?", "namespace": "open-p", "hash": 1203652047, "terms": ["What is the rawScore feature?", "is the rawScore feature?", "the rawScore feature?", "rawScore feature?", "feature?"], "tokens": ["What", "is", "the", "rawScore", "feature", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-597304609", "fields": {"term": "How does itemRawScore work?", "namespace": "open-p", "hash": -597304609, "terms": ["How does itemRawScore work?", "does itemRawScore work?", "itemRawScore work?", "work?"], "tokens": ["How", "does", "itemRawScore", "work", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-712017024", "fields": {"term": "What is query item labeling?", "namespace": "open-p", "hash": -712017024, "terms": ["What is query item labeling?", "is query item labeling?", "query item labeling?", "item labeling?", "labeling?"], "tokens": ["What", "is", "query", "item", "labeling", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-500398559", "fields": {"term": "What is the wand operator?", "namespace": "open-p", "hash": -500398559, "terms": ["What is the wand operator?", "is the wand operator?", "the wand operator?", "wand operator?", "operator?"], "tokens": ["What", "is", "the", "wand", "operator", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::-1588785990", "fields": {"term": "How does Vespa handle raw scores?", "namespace": "open-p", "hash": -1588785990, "terms": ["How does Vespa handle raw scores?", "does Vespa handle raw scores?", "Vespa handle raw scores?", "handle raw scores?", "raw scores?", "scores?"], "tokens": ["How", "does", "Vespa", "handle", "raw", "scores", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-353315158", "fields": {"term": "Can query items be labeled?", "namespace": "open-p", "hash": -353315158, "terms": ["Can query items be labeled?", "query items be labeled?", "items be labeled?", "be labeled?", "labeled?"], "tokens": ["Can", "query", "items", "be", "labeled", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1905008838", "fields": {"term": "What is the output from the matching pipeline?", "namespace": "open-p", "hash": 1905008838, "terms": ["What is the output from the matching pipeline?", "is the output from the matching pipeline?", "the output from the matching pipeline?", "output from the matching pipeline?", "from the matching pipeline?", "the matching pipeline?", "matching pipeline?", "pipeline?"], "tokens": ["What", "is", "the", "output", "from", "the", "matching", "pipeline", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2032052416", "fields": {"term": "What is the *nativeRank* score?", "namespace": "open-p", "hash": -2032052416, "terms": ["What is the *nativeRank* score?", "is the *nativeRank* score?", "the *nativeRank* score?", "*nativeRank* score?", "score?"], "tokens": ["What", "is", "the", "*", "nativeRank", "*", "score", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-436191016", "fields": {"term": "How is *nativeRank* computed?", "namespace": "open-p", "hash": -436191016, "terms": ["How is *nativeRank* computed?", "is *nativeRank* computed?", "*nativeRank* computed?", "computed?"], "tokens": ["How", "is", "*", "nativeRank", "*", "computed", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-951452425", "fields": {"term": "What are the three matching features?", "namespace": "open-p", "hash": -951452425, "terms": ["What are the three matching features?", "are the three matching features?", "the three matching features?", "three matching features?", "matching features?", "features?"], "tokens": ["What", "are", "the", "three", "matching", "features", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-589220001", "fields": {"term": "What is included in *nativeRank*?", "namespace": "open-p", "hash": -589220001, "terms": ["What is included in *nativeRank*?", "is included in *nativeRank*?", "included in *nativeRank*?", "in *nativeRank*?", "*nativeRank*?"], "tokens": ["What", "is", "included", "in", "*", "nativeRank", "*?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::50820894", "fields": {"term": "What is not included in *nativeRank*?", "namespace": "open-p", "hash": 50820894, "terms": ["What is not included in *nativeRank*?", "is not included in *nativeRank*?", "not included in *nativeRank*?", "included in *nativeRank*?", "in *nativeRank*?", "*nativeRank*?"], "tokens": ["What", "is", "not", "included", "in", "*", "nativeRank", "*?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1110002899", "fields": {"term": "How should *nativeRank* be used?", "namespace": "open-p", "hash": -1110002899, "terms": ["How should *nativeRank* be used?", "should *nativeRank* be used?", "*nativeRank* be used?", "be used?", "used?"], "tokens": ["How", "should", "*", "nativeRank", "*", "be", "used", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1093008366", "fields": {"term": "What is a ranking expression?", "namespace": "open-p", "hash": -1093008366, "terms": ["What is a ranking expression?", "is a ranking expression?", "a ranking expression?", "ranking expression?", "expression?"], "tokens": ["What", "is", "a", "ranking", "expression", "?"], "corpus_count": 3, "document_count": 3}}, -{"put": "id:term:term::1493607153", "fields": {"term": "What is nativeRank feature?", "namespace": "open-p", "hash": 1493607153, "terms": ["What is nativeRank feature?", "is nativeRank feature?", "nativeRank feature?", "feature?"], "tokens": ["What", "is", "nativeRank", "feature", "?"], "corpus_count": 3, "document_count": 3}}, -{"put": "id:term:term::1746511586", "fields": {"term": "What is the final rank score?", "namespace": "open-p", "hash": 1746511586, "terms": ["What is the final rank score?", "is the final rank score?", "the final rank score?", "final rank score?", "rank score?", "score?"], "tokens": ["What", "is", "the", "final", "rank", "score", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1608516593", "fields": {"term": "How are rank features calculated?", "namespace": "open-p", "hash": 1608516593, "terms": ["How are rank features calculated?", "are rank features calculated?", "rank features calculated?", "features calculated?", "calculated?"], "tokens": ["How", "are", "rank", "features", "calculated", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1411014980", "fields": {"term": "What operators and functions are supported in ranking expressions?", "namespace": "open-p", "hash": 1411014980, "terms": ["What operators and functions are supported in ranking expressions?", "operators and functions are supported in ranking expressions?", "and functions are supported in ranking expressions?", "functions are supported in ranking expressions?", "are supported in ranking expressions?", "supported in ranking expressions?", "in ranking expressions?", "ranking expressions?", "expressions?"], "tokens": ["What", "operators", "and", "functions", "are", "supported", "in", "ranking", "expressions", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1736617118", "fields": {"term": "What is sourcequality?", "namespace": "open-p", "hash": 1736617118, "terms": ["What is sourcequality?", "is sourcequality?", "sourcequality?"], "tokens": ["What", "is", "sourcequality", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1458975686", "fields": {"term": "What is age(name)?", "namespace": "open-p", "hash": -1458975686, "terms": ["What is age(name)?", "is age(name)?", "age(name)?"], "tokens": ["What", "is", "age", "(", "name", ")?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1162906872", "fields": {"term": "How many main rank signals are there?", "namespace": "open-p", "hash": 1162906872, "terms": ["How many main rank signals are there?", "many main rank signals are there?", "main rank signals are there?", "rank signals are there?", "signals are there?", "are there?", "there?"], "tokens": ["How", "many", "main", "rank", "signals", "are", "there", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-849118950", "fields": {"term": "What is the nativeRank feature?", "namespace": "open-p", "hash": -849118950, "terms": ["What is the nativeRank feature?", "is the nativeRank feature?", "the nativeRank feature?", "nativeRank feature?", "feature?"], "tokens": ["What", "is", "the", "nativeRank", "feature", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::92943630", "fields": {"term": "What is index field weight?", "namespace": "open-p", "hash": 92943630, "terms": ["What is index field weight?", "is index field weight?", "index field weight?", "field weight?", "weight?"], "tokens": ["What", "is", "index", "field", "weight", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::731464352", "fields": {"term": "How to tune rank-profile?", "namespace": "open-p", "hash": 731464352, "terms": ["How to tune rank-profile?", "to tune rank-profile?", "tune rank-profile?", "rank-profile?"], "tokens": ["How", "to", "tune", "rank", "profile", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1882123417", "fields": {"term": "What are tuning parameters of nativerank?", "namespace": "open-p", "hash": 1882123417, "terms": ["What are tuning parameters of nativerank?", "are tuning parameters of nativerank?", "tuning parameters of nativerank?", "parameters of nativerank?", "of nativerank?", "nativerank?"], "tokens": ["What", "are", "tuning", "parameters", "of", "nativerank", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2138142077", "fields": {"term": "What is the weight of title?", "namespace": "open-p", "hash": -2138142077, "terms": ["What is the weight of title?", "is the weight of title?", "the weight of title?", "weight of title?", "of title?", "title?"], "tokens": ["What", "is", "the", "weight", "of", "title", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::952277995", "fields": {"term": "What is the weight of body?", "namespace": "open-p", "hash": 952277995, "terms": ["What is the weight of body?", "is the weight of body?", "the weight of body?", "weight of body?", "of body?", "body?"], "tokens": ["What", "is", "the", "weight", "of", "body", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-417930067", "fields": {"term": "What are the core statistics used for boosting tables?", "namespace": "open-p", "hash": -417930067, "terms": ["What are the core statistics used for boosting tables?", "are the core statistics used for boosting tables?", "the core statistics used for boosting tables?", "core statistics used for boosting tables?", "statistics used for boosting tables?", "used for boosting tables?", "for boosting tables?", "boosting tables?", "tables?"], "tokens": ["What", "are", "the", "core", "statistics", "used", "for", "boosting", "tables", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1459144838", "fields": {"term": "What are the pre-configured boosting tables for different type of text fields?", "namespace": "open-p", "hash": -1459144838, "terms": ["What are the pre-configured boosting tables for different type of text fields?", "are the pre-configured boosting tables for different type of text fields?", "the pre-configured boosting tables for different type of text fields?", "pre-configured boosting tables for different type of text fields?", "boosting tables for different type of text fields?", "tables for different type of text fields?", "for different type of text fields?", "different type of text fields?", "type of text fields?", "of text fields?", "text fields?", "fields?"], "tokens": ["What", "are", "the", "pre", "configured", "boosting", "tables", "for", "different", "type", "of", "text", "fields", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2052455296", "fields": {"term": "What is Vespa's freshness rank-feature?", "namespace": "open-p", "hash": 2052455296, "terms": ["What is Vespa's freshness rank-feature?", "is Vespa's freshness rank-feature?", "Vespa's freshness rank-feature?", "freshness rank-feature?", "rank-feature?"], "tokens": ["What", "is", "Vespa", "'", "s", "freshness", "rank", "feature", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1802828767", "fields": {"term": "What is the default freshness rank-feature of Vespa?", "namespace": "open-p", "hash": -1802828767, "terms": ["What is the default freshness rank-feature of Vespa?", "is the default freshness rank-feature of Vespa?", "the default freshness rank-feature of Vespa?", "default freshness rank-feature of Vespa?", "freshness rank-feature of Vespa?", "rank-feature of Vespa?", "of Vespa?", "Vespa?"], "tokens": ["What", "is", "the", "default", "freshness", "rank", "feature", "of", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::768814421", "fields": {"term": "How can we design our own rank-feature in Vespa?", "namespace": "open-p", "hash": 768814421, "terms": ["How can we design our own rank-feature in Vespa?", "can we design our own rank-feature in Vespa?", "we design our own rank-feature in Vespa?", "design our own rank-feature in Vespa?", "our own rank-feature in Vespa?", "own rank-feature in Vespa?", "rank-feature in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["How", "can", "we", "design", "our", "own", "rank", "feature", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-699514633", "fields": {"term": "What is the formula for the freshness function?", "namespace": "open-p", "hash": -699514633, "terms": ["What is the formula for the freshness function?", "is the formula for the freshness function?", "the formula for the freshness function?", "formula for the freshness function?", "for the freshness function?", "the freshness function?", "freshness function?", "function?"], "tokens": ["What", "is", "the", "formula", "for", "the", "freshness", "function", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2135568960", "fields": {"term": "What is the plot comparing freshness functions?", "namespace": "open-p", "hash": 2135568960, "terms": ["What is the plot comparing freshness functions?", "is the plot comparing freshness functions?", "the plot comparing freshness functions?", "plot comparing freshness functions?", "comparing freshness functions?", "freshness functions?", "functions?"], "tokens": ["What", "is", "the", "plot", "comparing", "freshness", "functions", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1401458125", "fields": {"term": "How do we control freshness rank score in Vespa?", "namespace": "open-p", "hash": 1401458125, "terms": ["How do we control freshness rank score in Vespa?", "do we control freshness rank score in Vespa?", "we control freshness rank score in Vespa?", "control freshness rank score in Vespa?", "freshness rank score in Vespa?", "rank score in Vespa?", "score in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["How", "do", "we", "control", "freshness", "rank", "score", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::378435309", "fields": {"term": "What is the shape of the freshness function in the blog application?", "namespace": "open-p", "hash": 378435309, "terms": ["What is the shape of the freshness function in the blog application?", "is the shape of the freshness function in the blog application?", "the shape of the freshness function in the blog application?", "shape of the freshness function in the blog application?", "of the freshness function in the blog application?", "the freshness function in the blog application?", "freshness function in the blog application?", "function in the blog application?", "in the blog application?", "the blog application?", "blog application?", "application?"], "tokens": ["What", "is", "the", "shape", "of", "the", "freshness", "function", "in", "the", "blog", "application", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::509802285", "fields": {"term": "What is the equation for the exponential decreasing function in Vespa?", "namespace": "open-p", "hash": 509802285, "terms": ["What is the equation for the exponential decreasing function in Vespa?", "is the equation for the exponential decreasing function in Vespa?", "the equation for the exponential decreasing function in Vespa?", "equation for the exponential decreasing function in Vespa?", "for the exponential decreasing function in Vespa?", "the exponential decreasing function in Vespa?", "exponential decreasing function in Vespa?", "decreasing function in Vespa?", "function in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["What", "is", "the", "equation", "for", "the", "exponential", "decreasing", "function", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::264309030", "fields": {"term": "How can we control component weight?", "namespace": "open-p", "hash": 264309030, "terms": ["How can we control component weight?", "can we control component weight?", "we control component weight?", "control component weight?", "component weight?", "weight?"], "tokens": ["How", "can", "we", "control", "component", "weight", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-802035376", "fields": {"term": "What is the functionality of the freshness feature?", "namespace": "open-p", "hash": -802035376, "terms": ["What is the functionality of the freshness feature?", "is the functionality of the freshness feature?", "the functionality of the freshness feature?", "functionality of the freshness feature?", "of the freshness feature?", "the freshness feature?", "freshness feature?", "feature?"], "tokens": ["What", "is", "the", "functionality", "of", "the", "freshness", "feature", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::113878384", "fields": {"term": "What is the functionality of the quality feature?", "namespace": "open-p", "hash": 113878384, "terms": ["What is the functionality of the quality feature?", "is the functionality of the quality feature?", "the functionality of the quality feature?", "functionality of the quality feature?", "of the quality feature?", "the quality feature?", "quality feature?", "feature?"], "tokens": ["What", "is", "the", "functionality", "of", "the", "quality", "feature", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-991800863", "fields": {"term": "What is the functionality of the normalization function?", "namespace": "open-p", "hash": -991800863, "terms": ["What is the functionality of the normalization function?", "is the functionality of the normalization function?", "the functionality of the normalization function?", "functionality of the normalization function?", "of the normalization function?", "the normalization function?", "normalization function?", "function?"], "tokens": ["What", "is", "the", "functionality", "of", "the", "normalization", "function", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1699635806", "fields": {"term": "How can we override signal weights?", "namespace": "open-p", "hash": 1699635806, "terms": ["How can we override signal weights?", "can we override signal weights?", "we override signal weights?", "override signal weights?", "signal weights?", "weights?"], "tokens": ["How", "can", "we", "override", "signal", "weights", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1302764402", "fields": {"term": "What is included in nativeRank?", "namespace": "open-p", "hash": -1302764402, "terms": ["What is included in nativeRank?", "is included in nativeRank?", "included in nativeRank?", "in nativeRank?", "nativeRank?"], "tokens": ["What", "is", "included", "in", "nativeRank", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::635892659", "fields": {"term": "How is nativeRank computed?", "namespace": "open-p", "hash": 635892659, "terms": ["How is nativeRank computed?", "is nativeRank computed?", "nativeRank computed?", "computed?"], "tokens": ["How", "is", "nativeRank", "computed", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-953304341", "fields": {"term": "What are the nativeFieldMatch, nativeProximity, and nativeAttributeMatch features?", "namespace": "open-p", "hash": -953304341, "terms": ["What are the nativeFieldMatch, nativeProximity, and nativeAttributeMatch features?", "are the nativeFieldMatch, nativeProximity, and nativeAttributeMatch features?", "the nativeFieldMatch, nativeProximity, and nativeAttributeMatch features?", "nativeFieldMatch, nativeProximity, and nativeAttributeMatch features?", "nativeProximity, and nativeAttributeMatch features?", "and nativeAttributeMatch features?", "nativeAttributeMatch features?", "features?"], "tokens": ["What", "are", "the", "nativeFieldMatch", "nativeProximity", "and", "nativeAttributeMatch", "features", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::618461535", "fields": {"term": "What is the recommended use of nativeRank?", "namespace": "open-p", "hash": 618461535, "terms": ["What is the recommended use of nativeRank?", "is the recommended use of nativeRank?", "the recommended use of nativeRank?", "recommended use of nativeRank?", "use of nativeRank?", "of nativeRank?", "nativeRank?"], "tokens": ["What", "is", "the", "recommended", "use", "of", "nativeRank", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::615281189", "fields": {"term": "Where can I configure nativeRank features?", "namespace": "open-p", "hash": 615281189, "terms": ["Where can I configure nativeRank features?", "can I configure nativeRank features?", "I configure nativeRank features?", "configure nativeRank features?", "nativeRank features?", "features?"], "tokens": ["Where", "can", "I", "configure", "nativeRank", "features", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1855737879", "fields": {"term": "What is nativeFieldMatch?", "namespace": "open-p", "hash": 1855737879, "terms": ["What is nativeFieldMatch?", "is nativeFieldMatch?", "nativeFieldMatch?"], "tokens": ["What", "is", "nativeFieldMatch", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::558220583", "fields": {"term": "How is nativeFieldMatch score calculated?", "namespace": "open-p", "hash": 558220583, "terms": ["How is nativeFieldMatch score calculated?", "is nativeFieldMatch score calculated?", "nativeFieldMatch score calculated?", "score calculated?", "calculated?"], "tokens": ["How", "is", "nativeFieldMatch", "score", "calculated", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1326083417", "fields": {"term": "What is fmMaxTable?", "namespace": "open-p", "hash": 1326083417, "terms": ["What is fmMaxTable?", "is fmMaxTable?", "fmMaxTable?"], "tokens": ["What", "is", "fmMaxTable", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::516898639", "fields": {"term": "How is fmMaxTable calculated?", "namespace": "open-p", "hash": 516898639, "terms": ["How is fmMaxTable calculated?", "is fmMaxTable calculated?", "fmMaxTable calculated?", "calculated?"], "tokens": ["How", "is", "fmMaxTable", "calculated", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::812038329", "fields": {"term": "What is firstOccBoost?", "namespace": "open-p", "hash": 812038329, "terms": ["What is firstOccBoost?", "is firstOccBoost?", "firstOccBoost?"], "tokens": ["What", "is", "firstOccBoost", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1328662967", "fields": {"term": "What is occurrenceCountTable?", "namespace": "open-p", "hash": 1328662967, "terms": ["What is occurrenceCountTable?", "is occurrenceCountTable?", "occurrenceCountTable?"], "tokens": ["What", "is", "occurrenceCountTable", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1934698378", "fields": {"term": "What is nativeProximity?", "namespace": "open-p", "hash": -1934698378, "terms": ["What is nativeProximity?", "is nativeProximity?", "nativeProximity?"], "tokens": ["What", "is", "nativeProximity", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-950290340", "fields": {"term": "How is nativeProximity calculated?", "namespace": "open-p", "hash": -950290340, "terms": ["How is nativeProximity calculated?", "is nativeProximity calculated?", "nativeProximity calculated?", "calculated?"], "tokens": ["How", "is", "nativeProximity", "calculated", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1330055649", "fields": {"term": "What are term pairs?", "namespace": "open-p", "hash": -1330055649, "terms": ["What are term pairs?", "are term pairs?", "term pairs?", "pairs?"], "tokens": ["What", "are", "term", "pairs", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2032372665", "fields": {"term": "What is connectedness?", "namespace": "open-p", "hash": -2032372665, "terms": ["What is connectedness?", "is connectedness?", "connectedness?"], "tokens": ["What", "is", "connectedness", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-437894181", "fields": {"term": "How can nativeProximity calculation be limited?", "namespace": "open-p", "hash": -437894181, "terms": ["How can nativeProximity calculation be limited?", "can nativeProximity calculation be limited?", "nativeProximity calculation be limited?", "calculation be limited?", "be limited?", "limited?"], "tokens": ["How", "can", "nativeProximity", "calculation", "be", "limited", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1062572405", "fields": {"term": "What is nativeAttributeMatch?", "namespace": "open-p", "hash": 1062572405, "terms": ["What is nativeAttributeMatch?", "is nativeAttributeMatch?", "nativeAttributeMatch?"], "tokens": ["What", "is", "nativeAttributeMatch", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::-1105408858", "fields": {"term": "How is nativeAttributeMatch calculated?", "namespace": "open-p", "hash": -1105408858, "terms": ["How is nativeAttributeMatch calculated?", "is nativeAttributeMatch calculated?", "nativeAttributeMatch calculated?", "calculated?"], "tokens": ["How", "is", "nativeAttributeMatch", "calculated", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1145708764", "fields": {"term": "How can nativeAttributeMatch be limited?", "namespace": "open-p", "hash": 1145708764, "terms": ["How can nativeAttributeMatch be limited?", "can nativeAttributeMatch be limited?", "nativeAttributeMatch be limited?", "be limited?", "limited?"], "tokens": ["How", "can", "nativeAttributeMatch", "be", "limited", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1274979788", "fields": {"term": "What is nativeRank?", "namespace": "open-p", "hash": -1274979788, "terms": ["What is nativeRank?", "is nativeRank?", "nativeRank?"], "tokens": ["What", "is", "nativeRank", "?"], "corpus_count": 3, "document_count": 3}}, -{"put": "id:term:term::-721998427", "fields": {"term": "How is nativeRank calculated?", "namespace": "open-p", "hash": -721998427, "terms": ["How is nativeRank calculated?", "is nativeRank calculated?", "nativeRank calculated?", "calculated?"], "tokens": ["How", "is", "nativeRank", "calculated", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1816016968", "fields": {"term": "What are fieldMatchWeight, proximityWeight, and attributeMatchWeight?", "namespace": "open-p", "hash": -1816016968, "terms": ["What are fieldMatchWeight, proximityWeight, and attributeMatchWeight?", "are fieldMatchWeight, proximityWeight, and attributeMatchWeight?", "fieldMatchWeight, proximityWeight, and attributeMatchWeight?", "proximityWeight, and attributeMatchWeight?", "and attributeMatchWeight?", "attributeMatchWeight?"], "tokens": ["What", "are", "fieldMatchWeight", "proximityWeight", "and", "attributeMatchWeight", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-400582610", "fields": {"term": "How can you configure nativeRank for a subset of fields?", "namespace": "open-p", "hash": -400582610, "terms": ["How can you configure nativeRank for a subset of fields?", "can you configure nativeRank for a subset of fields?", "you configure nativeRank for a subset of fields?", "configure nativeRank for a subset of fields?", "nativeRank for a subset of fields?", "for a subset of fields?", "a subset of fields?", "subset of fields?", "of fields?", "fields?"], "tokens": ["How", "can", "you", "configure", "nativeRank", "for", "a", "subset", "of", "fields", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-94735321", "fields": {"term": "What is attributeWeightj?", "namespace": "open-p", "hash": -94735321, "terms": ["What is attributeWeightj?", "is attributeWeightj?", "attributeWeightj?"], "tokens": ["What", "is", "attributeWeightj", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-113542420", "fields": {"term": "What is termSignificancei?", "namespace": "open-p", "hash": -113542420, "terms": ["What is termSignificancei?", "is termSignificancei?", "termSignificancei?"], "tokens": ["What", "is", "termSignificancei", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-439498228", "fields": {"term": "What is fieldWeightj?", "namespace": "open-p", "hash": -439498228, "terms": ["What is fieldWeightj?", "is fieldWeightj?", "fieldWeightj?"], "tokens": ["What", "is", "fieldWeightj", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1292923853", "fields": {"term": "What is the purpose of `nativeFieldMatch`?", "namespace": "open-p", "hash": 1292923853, "terms": ["What is the purpose of `nativeFieldMatch`?", "is the purpose of `nativeFieldMatch`?", "the purpose of `nativeFieldMatch`?", "purpose of `nativeFieldMatch`?", "of `nativeFieldMatch`?", "`nativeFieldMatch`?"], "tokens": ["What", "is", "the", "purpose", "of", "`", "nativeFieldMatch", "`?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::505193715", "fields": {"term": "What is the default table used for `firstOccurrenceTable`?", "namespace": "open-p", "hash": 505193715, "terms": ["What is the default table used for `firstOccurrenceTable`?", "is the default table used for `firstOccurrenceTable`?", "the default table used for `firstOccurrenceTable`?", "default table used for `firstOccurrenceTable`?", "table used for `firstOccurrenceTable`?", "used for `firstOccurrenceTable`?", "for `firstOccurrenceTable`?", "`firstOccurrenceTable`?"], "tokens": ["What", "is", "the", "default", "table", "used", "for", "`", "firstOccurrenceTable", "`?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1041508686", "fields": {"term": "What is the default importance value for `firstOccurrenceImportance`?", "namespace": "open-p", "hash": -1041508686, "terms": ["What is the default importance value for `firstOccurrenceImportance`?", "is the default importance value for `firstOccurrenceImportance`?", "the default importance value for `firstOccurrenceImportance`?", "default importance value for `firstOccurrenceImportance`?", "importance value for `firstOccurrenceImportance`?", "value for `firstOccurrenceImportance`?", "for `firstOccurrenceImportance`?", "`firstOccurrenceImportance`?"], "tokens": ["What", "is", "the", "default", "importance", "value", "for", "`", "firstOccurrenceImportance", "`?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-164254281", "fields": {"term": "What is the size of the sliding window used for generating term pairs?", "namespace": "open-p", "hash": -164254281, "terms": ["What is the size of the sliding window used for generating term pairs?", "is the size of the sliding window used for generating term pairs?", "the size of the sliding window used for generating term pairs?", "size of the sliding window used for generating term pairs?", "of the sliding window used for generating term pairs?", "the sliding window used for generating term pairs?", "sliding window used for generating term pairs?", "window used for generating term pairs?", "used for generating term pairs?", "for generating term pairs?", "generating term pairs?", "term pairs?", "pairs?"], "tokens": ["What", "is", "the", "size", "of", "the", "sliding", "window", "used", "for", "generating", "term", "pairs", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::292211502", "fields": {"term": "What is the default weight table used for `nativeAttributeMatch`?", "namespace": "open-p", "hash": 292211502, "terms": ["What is the default weight table used for `nativeAttributeMatch`?", "is the default weight table used for `nativeAttributeMatch`?", "the default weight table used for `nativeAttributeMatch`?", "default weight table used for `nativeAttributeMatch`?", "weight table used for `nativeAttributeMatch`?", "table used for `nativeAttributeMatch`?", "used for `nativeAttributeMatch`?", "for `nativeAttributeMatch`?", "`nativeAttributeMatch`?"], "tokens": ["What", "is", "the", "default", "weight", "table", "used", "for", "`", "nativeAttributeMatch", "`?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1769962299", "fields": {"term": "What is the role of `fieldMatchWeight` in `nativeRank`?", "namespace": "open-p", "hash": -1769962299, "terms": ["What is the role of `fieldMatchWeight` in `nativeRank`?", "is the role of `fieldMatchWeight` in `nativeRank`?", "the role of `fieldMatchWeight` in `nativeRank`?", "role of `fieldMatchWeight` in `nativeRank`?", "of `fieldMatchWeight` in `nativeRank`?", "`fieldMatchWeight` in `nativeRank`?", "in `nativeRank`?", "`nativeRank`?"], "tokens": ["What", "is", "the", "role", "of", "`", "fieldMatchWeight", "`", "in", "`", "nativeRank", "`?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-634706776", "fields": {"term": "What is the role of `proximityWeight` in `nativeRank`?", "namespace": "open-p", "hash": -634706776, "terms": ["What is the role of `proximityWeight` in `nativeRank`?", "is the role of `proximityWeight` in `nativeRank`?", "the role of `proximityWeight` in `nativeRank`?", "role of `proximityWeight` in `nativeRank`?", "of `proximityWeight` in `nativeRank`?", "`proximityWeight` in `nativeRank`?", "in `nativeRank`?", "`nativeRank`?"], "tokens": ["What", "is", "the", "role", "of", "`", "proximityWeight", "`", "in", "`", "nativeRank", "`?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1703894388", "fields": {"term": "What is `useTableNormalization` in `nativeRank`?", "namespace": "open-p", "hash": -1703894388, "terms": ["What is `useTableNormalization` in `nativeRank`?", "is `useTableNormalization` in `nativeRank`?", "`useTableNormalization` in `nativeRank`?", "in `nativeRank`?", "`nativeRank`?"], "tokens": ["What", "is", "`", "useTableNormalization", "`", "in", "`", "nativeRank", "`?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1269808197", "fields": {"term": "What is the expdecay function?", "namespace": "open-p", "hash": 1269808197, "terms": ["What is the expdecay function?", "is the expdecay function?", "the expdecay function?", "expdecay function?", "function?"], "tokens": ["What", "is", "the", "expdecay", "function", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::885371432", "fields": {"term": "What is the loggrowth function?", "namespace": "open-p", "hash": 885371432, "terms": ["What is the loggrowth function?", "is the loggrowth function?", "the loggrowth function?", "loggrowth function?", "function?"], "tokens": ["What", "is", "the", "loggrowth", "function", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1945464208", "fields": {"term": "What is the linear function?", "namespace": "open-p", "hash": -1945464208, "terms": ["What is the linear function?", "is the linear function?", "the linear function?", "linear function?", "function?"], "tokens": ["What", "is", "the", "linear", "function", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1523266857", "fields": {"term": "What are the parameters of expdecay?", "namespace": "open-p", "hash": 1523266857, "terms": ["What are the parameters of expdecay?", "are the parameters of expdecay?", "the parameters of expdecay?", "parameters of expdecay?", "of expdecay?", "expdecay?"], "tokens": ["What", "are", "the", "parameters", "of", "expdecay", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-951955759", "fields": {"term": "What are the parameters of loggrowth?", "namespace": "open-p", "hash": -951955759, "terms": ["What are the parameters of loggrowth?", "are the parameters of loggrowth?", "the parameters of loggrowth?", "parameters of loggrowth?", "of loggrowth?", "loggrowth?"], "tokens": ["What", "are", "the", "parameters", "of", "loggrowth", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1110997281", "fields": {"term": "What are the parameters of linear?", "namespace": "open-p", "hash": 1110997281, "terms": ["What are the parameters of linear?", "are the parameters of linear?", "the parameters of linear?", "parameters of linear?", "of linear?", "linear?"], "tokens": ["What", "are", "the", "parameters", "of", "linear", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1999715314", "fields": {"term": "How to override default table size?", "namespace": "open-p", "hash": 1999715314, "terms": ["How to override default table size?", "to override default table size?", "override default table size?", "default table size?", "table size?", "size?"], "tokens": ["How", "to", "override", "default", "table", "size", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::109118441", "fields": {"term": "How to populate a table with custom function?", "namespace": "open-p", "hash": 109118441, "terms": ["How to populate a table with custom function?", "to populate a table with custom function?", "populate a table with custom function?", "a table with custom function?", "table with custom function?", "with custom function?", "custom function?", "function?"], "tokens": ["How", "to", "populate", "a", "table", "with", "custom", "function", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1704090180", "fields": {"term": "What are the predefined rank types?", "namespace": "open-p", "hash": 1704090180, "terms": ["What are the predefined rank types?", "are the predefined rank types?", "the predefined rank types?", "predefined rank types?", "rank types?", "types?"], "tokens": ["What", "are", "the", "predefined", "rank", "types", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1292941032", "fields": {"term": "What are boost tables?", "namespace": "open-p", "hash": -1292941032, "terms": ["What are boost tables?", "are boost tables?", "boost tables?", "tables?"], "tokens": ["What", "are", "boost", "tables", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1732201901", "fields": {"term": "How to specify boost tables?", "namespace": "open-p", "hash": 1732201901, "terms": ["How to specify boost tables?", "to specify boost tables?", "specify boost tables?", "boost tables?", "tables?"], "tokens": ["How", "to", "specify", "boost", "tables", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1454225981", "fields": {"term": "What happens if boost tables are not specified?", "namespace": "open-p", "hash": -1454225981, "terms": ["What happens if boost tables are not specified?", "happens if boost tables are not specified?", "if boost tables are not specified?", "boost tables are not specified?", "tables are not specified?", "are not specified?", "not specified?", "specified?"], "tokens": ["What", "happens", "if", "boost", "tables", "are", "not", "specified", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::431782418", "fields": {"term": "What are the boost tables for about type?", "namespace": "open-p", "hash": 431782418, "terms": ["What are the boost tables for about type?", "are the boost tables for about type?", "the boost tables for about type?", "boost tables for about type?", "tables for about type?", "for about type?", "about type?", "type?"], "tokens": ["What", "are", "the", "boost", "tables", "for", "about", "type", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-514551141", "fields": {"term": "What does nativeRank not take into account?", "namespace": "open-p", "hash": -514551141, "terms": ["What does nativeRank not take into account?", "does nativeRank not take into account?", "nativeRank not take into account?", "not take into account?", "take into account?", "into account?", "account?"], "tokens": ["What", "does", "nativeRank", "not", "take", "into", "account", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1470323626", "fields": {"term": "What feature should be used for relevancy based on numeric values?", "namespace": "open-p", "hash": -1470323626, "terms": ["What feature should be used for relevancy based on numeric values?", "feature should be used for relevancy based on numeric values?", "should be used for relevancy based on numeric values?", "be used for relevancy based on numeric values?", "used for relevancy based on numeric values?", "for relevancy based on numeric values?", "relevancy based on numeric values?", "based on numeric values?", "on numeric values?", "numeric values?", "values?"], "tokens": ["What", "feature", "should", "be", "used", "for", "relevancy", "based", "on", "numeric", "values", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2021198901", "fields": {"term": "What features should be used for relevance based on geographical location?", "namespace": "open-p", "hash": -2021198901, "terms": ["What features should be used for relevance based on geographical location?", "features should be used for relevance based on geographical location?", "should be used for relevance based on geographical location?", "be used for relevance based on geographical location?", "used for relevance based on geographical location?", "for relevance based on geographical location?", "relevance based on geographical location?", "based on geographical location?", "on geographical location?", "geographical location?", "location?"], "tokens": ["What", "features", "should", "be", "used", "for", "relevance", "based", "on", "geographical", "location", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-532657262", "fields": {"term": "What feature should be used for freshness of matching documents?", "namespace": "open-p", "hash": -532657262, "terms": ["What feature should be used for freshness of matching documents?", "feature should be used for freshness of matching documents?", "should be used for freshness of matching documents?", "be used for freshness of matching documents?", "used for freshness of matching documents?", "for freshness of matching documents?", "freshness of matching documents?", "of matching documents?", "matching documents?", "documents?"], "tokens": ["What", "feature", "should", "be", "used", "for", "freshness", "of", "matching", "documents", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1820388887", "fields": {"term": "What is Vespa nearest neighbor search?", "namespace": "open-p", "hash": 1820388887, "terms": ["What is Vespa nearest neighbor search?", "is Vespa nearest neighbor search?", "Vespa nearest neighbor search?", "nearest neighbor search?", "neighbor search?", "search?"], "tokens": ["What", "is", "Vespa", "nearest", "neighbor", "search", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-513697092", "fields": {"term": "How can Vespa combine nearest neighbor search with other query operators?", "namespace": "open-p", "hash": -513697092, "terms": ["How can Vespa combine nearest neighbor search with other query operators?", "can Vespa combine nearest neighbor search with other query operators?", "Vespa combine nearest neighbor search with other query operators?", "combine nearest neighbor search with other query operators?", "nearest neighbor search with other query operators?", "neighbor search with other query operators?", "search with other query operators?", "with other query operators?", "other query operators?", "query operators?", "operators?"], "tokens": ["How", "can", "Vespa", "combine", "nearest", "neighbor", "search", "with", "other", "query", "operators", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-967147722", "fields": {"term": "What candidate retrievers can be used in multi-phase ranking funnel?", "namespace": "open-p", "hash": -967147722, "terms": ["What candidate retrievers can be used in multi-phase ranking funnel?", "candidate retrievers can be used in multi-phase ranking funnel?", "retrievers can be used in multi-phase ranking funnel?", "can be used in multi-phase ranking funnel?", "be used in multi-phase ranking funnel?", "used in multi-phase ranking funnel?", "in multi-phase ranking funnel?", "multi-phase ranking funnel?", "ranking funnel?", "funnel?"], "tokens": ["What", "candidate", "retrievers", "can", "be", "used", "in", "multi", "phase", "ranking", "funnel", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2018481411", "fields": {"term": "What dataset is used for illustration?", "namespace": "open-p", "hash": 2018481411, "terms": ["What dataset is used for illustration?", "dataset is used for illustration?", "is used for illustration?", "used for illustration?", "for illustration?", "illustration?"], "tokens": ["What", "dataset", "is", "used", "for", "illustration", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1079499662", "fields": {"term": "What is distanceThresholding?", "namespace": "open-p", "hash": -1079499662, "terms": ["What is distanceThresholding?", "is distanceThresholding?", "distanceThresholding?"], "tokens": ["What", "is", "distanceThresholding", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-166353611", "fields": {"term": "How can hybrid sparse and dense retrieval methods be used with Vespa?", "namespace": "open-p", "hash": -166353611, "terms": ["How can hybrid sparse and dense retrieval methods be used with Vespa?", "can hybrid sparse and dense retrieval methods be used with Vespa?", "hybrid sparse and dense retrieval methods be used with Vespa?", "sparse and dense retrieval methods be used with Vespa?", "and dense retrieval methods be used with Vespa?", "dense retrieval methods be used with Vespa?", "retrieval methods be used with Vespa?", "methods be used with Vespa?", "be used with Vespa?", "used with Vespa?", "with Vespa?", "Vespa?"], "tokens": ["How", "can", "hybrid", "sparse", "and", "dense", "retrieval", "methods", "be", "used", "with", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1368077844", "fields": {"term": "What does the guide provide step-by-step instructions on?", "namespace": "open-p", "hash": 1368077844, "terms": ["What does the guide provide step-by-step instructions on?", "does the guide provide step-by-step instructions on?", "the guide provide step-by-step instructions on?", "guide provide step-by-step instructions on?", "provide step-by-step instructions on?", "step-by-step instructions on?", "instructions on?", "on?"], "tokens": ["What", "does", "the", "guide", "provide", "step", "by", "step", "instructions", "on", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-809143718", "fields": {"term": "What is Vespa-CLI?", "namespace": "open-p", "hash": -809143718, "terms": ["What is Vespa-CLI?", "is Vespa-CLI?", "Vespa-CLI?"], "tokens": ["What", "is", "Vespa", "CLI", "?"], "corpus_count": 3, "document_count": 3}}, -{"put": "id:term:term::600173231", "fields": {"term": "What is the Last.fm dataset?", "namespace": "open-p", "hash": 600173231, "terms": ["What is the Last.fm dataset?", "is the Last.fm dataset?", "the Last.fm dataset?", "Last.fm dataset?", "dataset?"], "tokens": ["What", "is", "the", "Last", "fm", "dataset", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::646348895", "fields": {"term": "How can I download the dataset?", "namespace": "open-p", "hash": 646348895, "terms": ["How can I download the dataset?", "can I download the dataset?", "I download the dataset?", "download the dataset?", "the dataset?", "dataset?"], "tokens": ["How", "can", "I", "download", "the", "dataset", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-950561469", "fields": {"term": "What is the format of the dataset?", "namespace": "open-p", "hash": -950561469, "terms": ["What is the format of the dataset?", "is the format of the dataset?", "the format of the dataset?", "format of the dataset?", "of the dataset?", "the dataset?", "dataset?"], "tokens": ["What", "is", "the", "format", "of", "the", "dataset", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1345178519", "fields": {"term": "What is the schema used for the dataset?", "namespace": "open-p", "hash": 1345178519, "terms": ["What is the schema used for the dataset?", "is the schema used for the dataset?", "the schema used for the dataset?", "schema used for the dataset?", "used for the dataset?", "for the dataset?", "the dataset?", "dataset?"], "tokens": ["What", "is", "the", "schema", "used", "for", "the", "dataset", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1792304456", "fields": {"term": "What is the popularity of the track?", "namespace": "open-p", "hash": 1792304456, "terms": ["What is the popularity of the track?", "is the popularity of the track?", "the popularity of the track?", "popularity of the track?", "of the track?", "the track?", "track?"], "tokens": ["What", "is", "the", "popularity", "of", "the", "track", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::341820145", "fields": {"term": "How do I process the dataset?", "namespace": "open-p", "hash": 341820145, "terms": ["How do I process the dataset?", "do I process the dataset?", "I process the dataset?", "process the dataset?", "the dataset?", "dataset?"], "tokens": ["How", "do", "I", "process", "the", "dataset", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::784753516", "fields": {"term": "What is a Vespa application package?", "namespace": "open-p", "hash": 784753516, "terms": ["What is a Vespa application package?", "is a Vespa application package?", "a Vespa application package?", "Vespa application package?", "application package?", "package?"], "tokens": ["What", "is", "a", "Vespa", "application", "package", "?"], "corpus_count": 3, "document_count": 3}}, -{"put": "id:term:term::-1139257004", "fields": {"term": "What files are needed for basic search application?", "namespace": "open-p", "hash": -1139257004, "terms": ["What files are needed for basic search application?", "files are needed for basic search application?", "are needed for basic search application?", "needed for basic search application?", "for basic search application?", "basic search application?", "search application?", "application?"], "tokens": ["What", "files", "are", "needed", "for", "basic", "search", "application", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1121504780", "fields": {"term": "What does a Vespa application package define?", "namespace": "open-p", "hash": 1121504780, "terms": ["What does a Vespa application package define?", "does a Vespa application package define?", "a Vespa application package define?", "Vespa application package define?", "application package define?", "package define?", "define?"], "tokens": ["What", "does", "a", "Vespa", "application", "package", "define", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::101273568", "fields": {"term": "What is the purpose of track.sd?", "namespace": "open-p", "hash": 101273568, "terms": ["What is the purpose of track.sd?", "is the purpose of track.sd?", "the purpose of track.sd?", "purpose of track.sd?", "of track.sd?", "track.sd?"], "tokens": ["What", "is", "the", "purpose", "of", "track", "sd", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2058863664", "fields": {"term": "What are the directory names for configuration files?", "namespace": "open-p", "hash": -2058863664, "terms": ["What are the directory names for configuration files?", "are the directory names for configuration files?", "the directory names for configuration files?", "directory names for configuration files?", "names for configuration files?", "for configuration files?", "configuration files?", "files?"], "tokens": ["What", "are", "the", "directory", "names", "for", "configuration", "files", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1894938036", "fields": {"term": "How is data processed during feeding and indexing?", "namespace": "open-p", "hash": 1894938036, "terms": ["How is data processed during feeding and indexing?", "is data processed during feeding and indexing?", "data processed during feeding and indexing?", "processed during feeding and indexing?", "during feeding and indexing?", "feeding and indexing?", "and indexing?", "indexing?"], "tokens": ["How", "is", "data", "processed", "during", "feeding", "and", "indexing", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1450536881", "fields": {"term": "What is a schema?", "namespace": "open-p", "hash": 1450536881, "terms": ["What is a schema?", "is a schema?", "a schema?", "schema?"], "tokens": ["What", "is", "a", "schema", "?"], "corpus_count": 3, "document_count": 3}}, -{"put": "id:term:term::474702756", "fields": {"term": "What is the HNSW Index?", "namespace": "open-p", "hash": 474702756, "terms": ["What is the HNSW Index?", "is the HNSW Index?", "the HNSW Index?", "HNSW Index?", "Index?"], "tokens": ["What", "is", "the", "HNSW", "Index", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1265626363", "fields": {"term": "What is the practical search performance guide?", "namespace": "open-p", "hash": -1265626363, "terms": ["What is the practical search performance guide?", "is the practical search performance guide?", "the practical search performance guide?", "practical search performance guide?", "search performance guide?", "performance guide?", "guide?"], "tokens": ["What", "is", "the", "practical", "search", "performance", "guide", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-350288035", "fields": {"term": "What is the purpose of presentation.timing?", "namespace": "open-p", "hash": -350288035, "terms": ["What is the purpose of presentation.timing?", "is the purpose of presentation.timing?", "the purpose of presentation.timing?", "purpose of presentation.timing?", "of presentation.timing?", "presentation.timing?"], "tokens": ["What", "is", "the", "purpose", "of", "presentation", "timing", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1403778250", "fields": {"term": "How are fields rendered in JSON?", "namespace": "open-p", "hash": -1403778250, "terms": ["How are fields rendered in JSON?", "are fields rendered in JSON?", "fields rendered in JSON?", "rendered in JSON?", "in JSON?", "JSON?"], "tokens": ["How", "are", "fields", "rendered", "in", "JSON", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1106290305", "fields": {"term": "How many nodes can be run per service?", "namespace": "open-p", "hash": -1106290305, "terms": ["How many nodes can be run per service?", "many nodes can be run per service?", "nodes can be run per service?", "can be run per service?", "be run per service?", "run per service?", "per service?", "service?"], "tokens": ["How", "many", "nodes", "can", "be", "run", "per", "service", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::238393647", "fields": {"term": "How do I deploy the application package?", "namespace": "open-p", "hash": 238393647, "terms": ["How do I deploy the application package?", "do I deploy the application package?", "I deploy the application package?", "deploy the application package?", "the application package?", "application package?", "package?"], "tokens": ["How", "do", "I", "deploy", "the", "application", "package", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1717793748", "fields": {"term": "What is the Vespa quick start guide?", "namespace": "open-p", "hash": 1717793748, "terms": ["What is the Vespa quick start guide?", "is the Vespa quick start guide?", "the Vespa quick start guide?", "Vespa quick start guide?", "quick start guide?", "start guide?", "guide?"], "tokens": ["What", "is", "the", "Vespa", "quick", "start", "guide", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::1417142905", "fields": {"term": "What is feed.jsonl?", "namespace": "open-p", "hash": 1417142905, "terms": ["What is feed.jsonl?", "is feed.jsonl?", "feed.jsonl?"], "tokens": ["What", "is", "feed", "jsonl", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-684030279", "fields": {"term": "What is type=weakAnd?", "namespace": "open-p", "hash": -684030279, "terms": ["What is type=weakAnd?", "is type=weakAnd?", "type=weakAnd?"], "tokens": ["What", "is", "type", "=", "weakAnd", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1170889015", "fields": {"term": "What is the using wand with Vespa guide?", "namespace": "open-p", "hash": -1170889015, "terms": ["What is the using wand with Vespa guide?", "is the using wand with Vespa guide?", "the using wand with Vespa guide?", "using wand with Vespa guide?", "wand with Vespa guide?", "with Vespa guide?", "Vespa guide?", "guide?"], "tokens": ["What", "is", "the", "using", "wand", "with", "Vespa", "guide", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1244250459", "fields": {"term": "What is the 'wand' query?", "namespace": "open-p", "hash": -1244250459, "terms": ["What is the 'wand' query?", "is the 'wand' query?", "the 'wand' query?", "'wand' query?", "query?"], "tokens": ["What", "is", "the", "'", "wand", "'", "query", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1266776389", "fields": {"term": "What does 'wand' do?", "namespace": "open-p", "hash": -1266776389, "terms": ["What does 'wand' do?", "does 'wand' do?", "'wand' do?", "do?"], "tokens": ["What", "does", "'", "wand", "'", "do", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-207232269", "fields": {"term": "How does 'wand' work?", "namespace": "open-p", "hash": -207232269, "terms": ["How does 'wand' work?", "does 'wand' work?", "'wand' work?", "work?"], "tokens": ["How", "does", "'", "wand", "'", "work", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1357128009", "fields": {"term": "What is the syntax for using 'wand'?", "namespace": "open-p", "hash": 1357128009, "terms": ["What is the syntax for using 'wand'?", "is the syntax for using 'wand'?", "the syntax for using 'wand'?", "syntax for using 'wand'?", "for using 'wand'?", "using 'wand'?", "'wand'?"], "tokens": ["What", "is", "the", "syntax", "for", "using", "'", "wand", "'?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1285877985", "fields": {"term": "What is a safe query operator?", "namespace": "open-p", "hash": 1285877985, "terms": ["What is a safe query operator?", "is a safe query operator?", "a safe query operator?", "safe query operator?", "query operator?", "operator?"], "tokens": ["What", "is", "a", "safe", "query", "operator", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1243484971", "fields": {"term": "What is the 'rawScore' rank feature?", "namespace": "open-p", "hash": 1243484971, "terms": ["What is the 'rawScore' rank feature?", "is the 'rawScore' rank feature?", "the 'rawScore' rank feature?", "'rawScore' rank feature?", "rank feature?", "feature?"], "tokens": ["What", "is", "the", "'", "rawScore", "'", "rank", "feature", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1255657130", "fields": {"term": "What is the result format of the 'wand' query?", "namespace": "open-p", "hash": -1255657130, "terms": ["What is the result format of the 'wand' query?", "is the result format of the 'wand' query?", "the result format of the 'wand' query?", "result format of the 'wand' query?", "format of the 'wand' query?", "of the 'wand' query?", "the 'wand' query?", "'wand' query?", "query?"], "tokens": ["What", "is", "the", "result", "format", "of", "the", "'", "wand", "'", "query", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1889425938", "fields": {"term": "Where can I find more information on 'wand' and 'weakAnd' query operators?", "namespace": "open-p", "hash": -1889425938, "terms": ["Where can I find more information on 'wand' and 'weakAnd' query operators?", "can I find more information on 'wand' and 'weakAnd' query operators?", "I find more information on 'wand' and 'weakAnd' query operators?", "find more information on 'wand' and 'weakAnd' query operators?", "more information on 'wand' and 'weakAnd' query operators?", "information on 'wand' and 'weakAnd' query operators?", "on 'wand' and 'weakAnd' query operators?", "'wand' and 'weakAnd' query operators?", "and 'weakAnd' query operators?", "'weakAnd' query operators?", "query operators?", "operators?"], "tokens": ["Where", "can", "I", "find", "more", "information", "on", "'", "wand", "'", "and", "'", "weakAnd", "'", "query", "operators", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::884083940", "fields": {"term": "How does Vespa perform exact search?", "namespace": "open-p", "hash": 884083940, "terms": ["How does Vespa perform exact search?", "does Vespa perform exact search?", "Vespa perform exact search?", "perform exact search?", "exact search?", "search?"], "tokens": ["How", "does", "Vespa", "perform", "exact", "search", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1263445745", "fields": {"term": "What is approximate nearest neighbor search?", "namespace": "open-p", "hash": -1263445745, "terms": ["What is approximate nearest neighbor search?", "is approximate nearest neighbor search?", "approximate nearest neighbor search?", "nearest neighbor search?", "neighbor search?", "search?"], "tokens": ["What", "is", "approximate", "nearest", "neighbor", "search", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::-1264447805", "fields": {"term": "What is the difference between exact and approximate neighbor search?", "namespace": "open-p", "hash": -1264447805, "terms": ["What is the difference between exact and approximate neighbor search?", "is the difference between exact and approximate neighbor search?", "the difference between exact and approximate neighbor search?", "difference between exact and approximate neighbor search?", "between exact and approximate neighbor search?", "exact and approximate neighbor search?", "and approximate neighbor search?", "approximate neighbor search?", "neighbor search?", "search?"], "tokens": ["What", "is", "the", "difference", "between", "exact", "and", "approximate", "neighbor", "search", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-979037150", "fields": {"term": "Can the accuracy loss of approximate search be quantified?", "namespace": "open-p", "hash": -979037150, "terms": ["Can the accuracy loss of approximate search be quantified?", "the accuracy loss of approximate search be quantified?", "accuracy loss of approximate search be quantified?", "loss of approximate search be quantified?", "of approximate search be quantified?", "approximate search be quantified?", "search be quantified?", "be quantified?", "quantified?"], "tokens": ["Can", "the", "accuracy", "loss", "of", "approximate", "search", "be", "quantified", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::198141849", "fields": {"term": "What is 'approximate:true' for?", "namespace": "open-p", "hash": 198141849, "terms": ["What is 'approximate:true' for?", "is 'approximate:true' for?", "'approximate:true' for?", "for?"], "tokens": ["What", "is", "'", "approximate", ":", "true", "'", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-336341687", "fields": {"term": "How is latency affected by approximate neighbor search?", "namespace": "open-p", "hash": -336341687, "terms": ["How is latency affected by approximate neighbor search?", "is latency affected by approximate neighbor search?", "latency affected by approximate neighbor search?", "affected by approximate neighbor search?", "by approximate neighbor search?", "approximate neighbor search?", "neighbor search?", "search?"], "tokens": ["How", "is", "latency", "affected", "by", "approximate", "neighbor", "search", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::547066998", "fields": {"term": "What is the difference in memory usage between exact and approximate search?", "namespace": "open-p", "hash": 547066998, "terms": ["What is the difference in memory usage between exact and approximate search?", "is the difference in memory usage between exact and approximate search?", "the difference in memory usage between exact and approximate search?", "difference in memory usage between exact and approximate search?", "in memory usage between exact and approximate search?", "memory usage between exact and approximate search?", "usage between exact and approximate search?", "between exact and approximate search?", "exact and approximate search?", "and approximate search?", "approximate search?", "search?"], "tokens": ["What", "is", "the", "difference", "in", "memory", "usage", "between", "exact", "and", "approximate", "search", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1377069656", "fields": {"term": "How can a developer find the balance between search performance and accuracy?", "namespace": "open-p", "hash": -1377069656, "terms": ["How can a developer find the balance between search performance and accuracy?", "can a developer find the balance between search performance and accuracy?", "a developer find the balance between search performance and accuracy?", "developer find the balance between search performance and accuracy?", "find the balance between search performance and accuracy?", "the balance between search performance and accuracy?", "balance between search performance and accuracy?", "between search performance and accuracy?", "search performance and accuracy?", "performance and accuracy?", "and accuracy?", "accuracy?"], "tokens": ["How", "can", "a", "developer", "find", "the", "balance", "between", "search", "performance", "and", "accuracy", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::710841318", "fields": {"term": "What is the 'rank: filter'?", "namespace": "open-p", "hash": 710841318, "terms": ["What is the 'rank: filter'?", "is the 'rank: filter'?", "the 'rank: filter'?", "'rank: filter'?", "filter'?"], "tokens": ["What", "is", "the", "'", "rank", ":", "filter", "'?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1354466019", "fields": {"term": "How to combine search queries?", "namespace": "open-p", "hash": -1354466019, "terms": ["How to combine search queries?", "to combine search queries?", "combine search queries?", "search queries?", "queries?"], "tokens": ["How", "to", "combine", "search", "queries", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1432186292", "fields": {"term": "What is the most efficient posting list representation?", "namespace": "open-p", "hash": -1432186292, "terms": ["What is the most efficient posting list representation?", "is the most efficient posting list representation?", "the most efficient posting list representation?", "most efficient posting list representation?", "efficient posting list representation?", "posting list representation?", "list representation?", "representation?"], "tokens": ["What", "is", "the", "most", "efficient", "posting", "list", "representation", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1341590445", "fields": {"term": "What is pure filtering?", "namespace": "open-p", "hash": -1341590445, "terms": ["What is pure filtering?", "is pure filtering?", "pure filtering?", "filtering?"], "tokens": ["What", "is", "pure", "filtering", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1337318435", "fields": {"term": "How to define fields for filtering?", "namespace": "open-p", "hash": -1337318435, "terms": ["How to define fields for filtering?", "to define fields for filtering?", "define fields for filtering?", "fields for filtering?", "for filtering?", "filtering?"], "tokens": ["How", "to", "define", "fields", "for", "filtering", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-468204309", "fields": {"term": "What is distanceThreshold in nearestNeighbor query operator?", "namespace": "open-p", "hash": -468204309, "terms": ["What is distanceThreshold in nearestNeighbor query operator?", "is distanceThreshold in nearestNeighbor query operator?", "distanceThreshold in nearestNeighbor query operator?", "in nearestNeighbor query operator?", "nearestNeighbor query operator?", "query operator?", "operator?"], "tokens": ["What", "is", "distanceThreshold", "in", "nearestNeighbor", "query", "operator", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1238099545", "fields": {"term": "How is the quality of the returned neighbors determined?", "namespace": "open-p", "hash": -1238099545, "terms": ["How is the quality of the returned neighbors determined?", "is the quality of the returned neighbors determined?", "the quality of the returned neighbors determined?", "quality of the returned neighbors determined?", "of the returned neighbors determined?", "the returned neighbors determined?", "returned neighbors determined?", "neighbors determined?", "determined?"], "tokens": ["How", "is", "the", "quality", "of", "the", "returned", "neighbors", "determined", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-396512487", "fields": {"term": "What is the significance of adding distance rank-features to match-features?", "namespace": "open-p", "hash": -396512487, "terms": ["What is the significance of adding distance rank-features to match-features?", "is the significance of adding distance rank-features to match-features?", "the significance of adding distance rank-features to match-features?", "significance of adding distance rank-features to match-features?", "of adding distance rank-features to match-features?", "adding distance rank-features to match-features?", "distance rank-features to match-features?", "rank-features to match-features?", "to match-features?", "match-features?"], "tokens": ["What", "is", "the", "significance", "of", "adding", "distance", "rank", "features", "to", "match", "features", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-720658026", "fields": {"term": "What is post-processing searcher and how does it help?", "namespace": "open-p", "hash": -720658026, "terms": ["What is post-processing searcher and how does it help?", "is post-processing searcher and how does it help?", "post-processing searcher and how does it help?", "searcher and how does it help?", "and how does it help?", "how does it help?", "does it help?", "it help?", "help?"], "tokens": ["What", "is", "post", "processing", "searcher", "and", "how", "does", "it", "help", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-257047576", "fields": {"term": "What is the importance of supervised learning in calibrating distance threshold?", "namespace": "open-p", "hash": -257047576, "terms": ["What is the importance of supervised learning in calibrating distance threshold?", "is the importance of supervised learning in calibrating distance threshold?", "the importance of supervised learning in calibrating distance threshold?", "importance of supervised learning in calibrating distance threshold?", "of supervised learning in calibrating distance threshold?", "supervised learning in calibrating distance threshold?", "learning in calibrating distance threshold?", "in calibrating distance threshold?", "calibrating distance threshold?", "distance threshold?", "threshold?"], "tokens": ["What", "is", "the", "importance", "of", "supervised", "learning", "in", "calibrating", "distance", "threshold", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1070946839", "fields": {"term": "What is the impact of strict filters on nearest neighbor search?", "namespace": "open-p", "hash": -1070946839, "terms": ["What is the impact of strict filters on nearest neighbor search?", "is the impact of strict filters on nearest neighbor search?", "the impact of strict filters on nearest neighbor search?", "impact of strict filters on nearest neighbor search?", "of strict filters on nearest neighbor search?", "strict filters on nearest neighbor search?", "filters on nearest neighbor search?", "on nearest neighbor search?", "nearest neighbor search?", "neighbor search?", "search?"], "tokens": ["What", "is", "the", "impact", "of", "strict", "filters", "on", "nearest", "neighbor", "search", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::484575246", "fields": {"term": "What is hybrid retrieval?", "namespace": "open-p", "hash": 484575246, "terms": ["What is hybrid retrieval?", "is hybrid retrieval?", "hybrid retrieval?", "retrieval?"], "tokens": ["What", "is", "hybrid", "retrieval", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::-1046218640", "fields": {"term": "What is nearestNeighbor operator?", "namespace": "open-p", "hash": -1046218640, "terms": ["What is nearestNeighbor operator?", "is nearestNeighbor operator?", "nearestNeighbor operator?", "operator?"], "tokens": ["What", "is", "nearestNeighbor", "operator", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::-1678025440", "fields": {"term": "How to combine efficient query operators?", "namespace": "open-p", "hash": -1678025440, "terms": ["How to combine efficient query operators?", "to combine efficient query operators?", "combine efficient query operators?", "efficient query operators?", "query operators?", "operators?"], "tokens": ["How", "to", "combine", "efficient", "query", "operators", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-320601547", "fields": {"term": "Can personalized hits be retrieved?", "namespace": "open-p", "hash": -320601547, "terms": ["Can personalized hits be retrieved?", "personalized hits be retrieved?", "hits be retrieved?", "be retrieved?", "retrieved?"], "tokens": ["Can", "personalized", "hits", "be", "retrieved", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::159217594", "fields": {"term": "How to compute euclidean distance?", "namespace": "open-p", "hash": 159217594, "terms": ["How to compute euclidean distance?", "to compute euclidean distance?", "compute euclidean distance?", "euclidean distance?", "distance?"], "tokens": ["How", "to", "compute", "euclidean", "distance", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1548320019", "fields": {"term": "What is pre-filtering strategy?", "namespace": "open-p", "hash": -1548320019, "terms": ["What is pre-filtering strategy?", "is pre-filtering strategy?", "pre-filtering strategy?", "strategy?"], "tokens": ["What", "is", "pre", "filtering", "strategy", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1649535677", "fields": {"term": "What is post-filtering strategy?", "namespace": "open-p", "hash": -1649535677, "terms": ["What is post-filtering strategy?", "is post-filtering strategy?", "post-filtering strategy?", "strategy?"], "tokens": ["What", "is", "post", "filtering", "strategy", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::921588406", "fields": {"term": "What is post-filter threshold?", "namespace": "open-p", "hash": 921588406, "terms": ["What is post-filter threshold?", "is post-filter threshold?", "post-filter threshold?", "threshold?"], "tokens": ["What", "is", "post", "filter", "threshold", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-495398074", "fields": {"term": "How to perform exact search?", "namespace": "open-p", "hash": -495398074, "terms": ["How to perform exact search?", "to perform exact search?", "perform exact search?", "exact search?", "search?"], "tokens": ["How", "to", "perform", "exact", "search", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1785670759", "fields": {"term": "What does docker rm do?", "namespace": "open-p", "hash": 1785670759, "terms": ["What does docker rm do?", "does docker rm do?", "docker rm do?", "rm do?", "do?"], "tokens": ["What", "does", "docker", "rm", "do", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::1557281030", "fields": {"term": "How is nearest neighbor search achieved in Vespa?", "namespace": "open-p", "hash": 1557281030, "terms": ["How is nearest neighbor search achieved in Vespa?", "is nearest neighbor search achieved in Vespa?", "nearest neighbor search achieved in Vespa?", "neighbor search achieved in Vespa?", "search achieved in Vespa?", "achieved in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["How", "is", "nearest", "neighbor", "search", "achieved", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-618048232", "fields": {"term": "What is the nearestNeighbor query operator?", "namespace": "open-p", "hash": -618048232, "terms": ["What is the nearestNeighbor query operator?", "is the nearestNeighbor query operator?", "the nearestNeighbor query operator?", "nearestNeighbor query operator?", "query operator?", "operator?"], "tokens": ["What", "is", "the", "nearestNeighbor", "query", "operator", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-988185829", "fields": {"term": "How can nearestNeighbor be combined with other operators?", "namespace": "open-p", "hash": -988185829, "terms": ["How can nearestNeighbor be combined with other operators?", "can nearestNeighbor be combined with other operators?", "nearestNeighbor be combined with other operators?", "be combined with other operators?", "combined with other operators?", "with other operators?", "other operators?", "operators?"], "tokens": ["How", "can", "nearestNeighbor", "be", "combined", "with", "other", "operators", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::564395943", "fields": {"term": "What are the benefits of combining vector search with traditional retrieval in Vespa?", "namespace": "open-p", "hash": 564395943, "terms": ["What are the benefits of combining vector search with traditional retrieval in Vespa?", "are the benefits of combining vector search with traditional retrieval in Vespa?", "the benefits of combining vector search with traditional retrieval in Vespa?", "benefits of combining vector search with traditional retrieval in Vespa?", "of combining vector search with traditional retrieval in Vespa?", "combining vector search with traditional retrieval in Vespa?", "vector search with traditional retrieval in Vespa?", "search with traditional retrieval in Vespa?", "with traditional retrieval in Vespa?", "traditional retrieval in Vespa?", "retrieval in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["What", "are", "the", "benefits", "of", "combining", "vector", "search", "with", "traditional", "retrieval", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-52391280", "fields": {"term": "What is a vector in Vespa?", "namespace": "open-p", "hash": -52391280, "terms": ["What is a vector in Vespa?", "is a vector in Vespa?", "a vector in Vespa?", "vector in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["What", "is", "a", "vector", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-155473795", "fields": {"term": "How are document vectors stored?", "namespace": "open-p", "hash": -155473795, "terms": ["How are document vectors stored?", "are document vectors stored?", "document vectors stored?", "vectors stored?", "stored?"], "tokens": ["How", "are", "document", "vectors", "stored", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-242379594", "fields": {"term": "What is the difference between dense and mixed tensor types?", "namespace": "open-p", "hash": -242379594, "terms": ["What is the difference between dense and mixed tensor types?", "is the difference between dense and mixed tensor types?", "the difference between dense and mixed tensor types?", "difference between dense and mixed tensor types?", "between dense and mixed tensor types?", "dense and mixed tensor types?", "and mixed tensor types?", "mixed tensor types?", "tensor types?", "types?"], "tokens": ["What", "is", "the", "difference", "between", "dense", "and", "mixed", "tensor", "types", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2006878715", "fields": {"term": "How are query vectors defined?", "namespace": "open-p", "hash": 2006878715, "terms": ["How are query vectors defined?", "are query vectors defined?", "query vectors defined?", "vectors defined?", "defined?"], "tokens": ["How", "are", "query", "vectors", "defined", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-671973262", "fields": {"term": "What is the distance-metric for searching vectors?", "namespace": "open-p", "hash": -671973262, "terms": ["What is the distance-metric for searching vectors?", "is the distance-metric for searching vectors?", "the distance-metric for searching vectors?", "distance-metric for searching vectors?", "for searching vectors?", "searching vectors?", "vectors?"], "tokens": ["What", "is", "the", "distance", "metric", "for", "searching", "vectors", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2071953366", "fields": {"term": "What is text_embedding field?", "namespace": "open-p", "hash": 2071953366, "terms": ["What is text_embedding field?", "is text_embedding field?", "text_embedding field?", "field?"], "tokens": ["What", "is", "text_embedding", "field", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-599001136", "fields": {"term": "What is image_embeddings field?", "namespace": "open-p", "hash": -599001136, "terms": ["What is image_embeddings field?", "is image_embeddings field?", "image_embeddings field?", "field?"], "tokens": ["What", "is", "image_embeddings", "field", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-466040630", "fields": {"term": "What is the distance-metric for attribute?", "namespace": "open-p", "hash": -466040630, "terms": ["What is the distance-metric for attribute?", "is the distance-metric for attribute?", "the distance-metric for attribute?", "distance-metric for attribute?", "for attribute?", "attribute?"], "tokens": ["What", "is", "the", "distance", "metric", "for", "attribute", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-148121664", "fields": {"term": "What is prenormalized-angular?", "namespace": "open-p", "hash": -148121664, "terms": ["What is prenormalized-angular?", "is prenormalized-angular?", "prenormalized-angular?"], "tokens": ["What", "is", "prenormalized", "angular", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-635807644", "fields": {"term": "What is the relevance of approximate search to exact search?", "namespace": "open-p", "hash": -635807644, "terms": ["What is the relevance of approximate search to exact search?", "is the relevance of approximate search to exact search?", "the relevance of approximate search to exact search?", "relevance of approximate search to exact search?", "of approximate search to exact search?", "approximate search to exact search?", "search to exact search?", "to exact search?", "exact search?", "search?"], "tokens": ["What", "is", "the", "relevance", "of", "approximate", "search", "to", "exact", "search", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2077777344", "fields": {"term": "What are the supported distance metrics?", "namespace": "open-p", "hash": 2077777344, "terms": ["What are the supported distance metrics?", "are the supported distance metrics?", "the supported distance metrics?", "supported distance metrics?", "distance metrics?", "metrics?"], "tokens": ["What", "are", "the", "supported", "distance", "metrics", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-806402052", "fields": {"term": "What is the 'euclidean' distance metric?", "namespace": "open-p", "hash": -806402052, "terms": ["What is the 'euclidean' distance metric?", "is the 'euclidean' distance metric?", "the 'euclidean' distance metric?", "'euclidean' distance metric?", "distance metric?", "metric?"], "tokens": ["What", "is", "the", "'", "euclidean", "'", "distance", "metric", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1034508823", "fields": {"term": "What is the 'angular' distance metric?", "namespace": "open-p", "hash": 1034508823, "terms": ["What is the 'angular' distance metric?", "is the 'angular' distance metric?", "the 'angular' distance metric?", "'angular' distance metric?", "distance metric?", "metric?"], "tokens": ["What", "is", "the", "'", "angular", "'", "distance", "metric", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1936303039", "fields": {"term": "What is the 'prenormalized-angular' distance metric?", "namespace": "open-p", "hash": -1936303039, "terms": ["What is the 'prenormalized-angular' distance metric?", "is the 'prenormalized-angular' distance metric?", "the 'prenormalized-angular' distance metric?", "'prenormalized-angular' distance metric?", "distance metric?", "metric?"], "tokens": ["What", "is", "the", "'", "prenormalized", "angular", "'", "distance", "metric", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1251058097", "fields": {"term": "What is the 'hamming' distance metric?", "namespace": "open-p", "hash": 1251058097, "terms": ["What is the 'hamming' distance metric?", "is the 'hamming' distance metric?", "the 'hamming' distance metric?", "'hamming' distance metric?", "distance metric?", "metric?"], "tokens": ["What", "is", "the", "'", "hamming", "'", "distance", "metric", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::479031541", "fields": {"term": "What is the 'geodegrees' distance metric?", "namespace": "open-p", "hash": 479031541, "terms": ["What is the 'geodegrees' distance metric?", "is the 'geodegrees' distance metric?", "the 'geodegrees' distance metric?", "'geodegrees' distance metric?", "distance metric?", "metric?"], "tokens": ["What", "is", "the", "'", "geodegrees", "'", "distance", "metric", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-996862285", "fields": {"term": "What is a rank-profile?", "namespace": "open-p", "hash": -996862285, "terms": ["What is a rank-profile?", "is a rank-profile?", "a rank-profile?", "rank-profile?"], "tokens": ["What", "is", "a", "rank", "profile", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::895379308", "fields": {"term": "How is the nearestNeighbor score computed?", "namespace": "open-p", "hash": 895379308, "terms": ["How is the nearestNeighbor score computed?", "is the nearestNeighbor score computed?", "the nearestNeighbor score computed?", "nearestNeighbor score computed?", "score computed?", "computed?"], "tokens": ["How", "is", "the", "nearestNeighbor", "score", "computed", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2142236292", "fields": {"term": "Can a rank-profile have multiple phases?", "namespace": "open-p", "hash": -2142236292, "terms": ["Can a rank-profile have multiple phases?", "a rank-profile have multiple phases?", "rank-profile have multiple phases?", "have multiple phases?", "multiple phases?", "phases?"], "tokens": ["Can", "a", "rank", "profile", "have", "multiple", "phases", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1430711350", "fields": {"term": "How is image similarity calculated?", "namespace": "open-p", "hash": 1430711350, "terms": ["How is image similarity calculated?", "is image similarity calculated?", "image similarity calculated?", "similarity calculated?", "calculated?"], "tokens": ["How", "is", "image", "similarity", "calculated", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::159335434", "fields": {"term": "How can one index product data using Vespa?", "namespace": "open-p", "hash": 159335434, "terms": ["How can one index product data using Vespa?", "can one index product data using Vespa?", "one index product data using Vespa?", "index product data using Vespa?", "product data using Vespa?", "data using Vespa?", "using Vespa?", "Vespa?"], "tokens": ["How", "can", "one", "index", "product", "data", "using", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-586443323", "fields": {"term": "What is the short form for mixed tensor?", "namespace": "open-p", "hash": -586443323, "terms": ["What is the short form for mixed tensor?", "is the short form for mixed tensor?", "the short form for mixed tensor?", "short form for mixed tensor?", "form for mixed tensor?", "for mixed tensor?", "mixed tensor?", "tensor?"], "tokens": ["What", "is", "the", "short", "form", "for", "mixed", "tensor", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2015024168", "fields": {"term": "What is the short form for indexed tensor?", "namespace": "open-p", "hash": -2015024168, "terms": ["What is the short form for indexed tensor?", "is the short form for indexed tensor?", "the short form for indexed tensor?", "short form for indexed tensor?", "form for indexed tensor?", "for indexed tensor?", "indexed tensor?", "tensor?"], "tokens": ["What", "is", "the", "short", "form", "for", "indexed", "tensor", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::47539553", "fields": {"term": "What is the purpose of image_embeddings field?", "namespace": "open-p", "hash": 47539553, "terms": ["What is the purpose of image_embeddings field?", "is the purpose of image_embeddings field?", "the purpose of image_embeddings field?", "purpose of image_embeddings field?", "of image_embeddings field?", "image_embeddings field?", "field?"], "tokens": ["What", "is", "the", "purpose", "of", "image_embeddings", "field", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2120395573", "fields": {"term": "What is the nearestNeighbor operator?", "namespace": "open-p", "hash": 2120395573, "terms": ["What is the nearestNeighbor operator?", "is the nearestNeighbor operator?", "the nearestNeighbor operator?", "nearestNeighbor operator?", "operator?"], "tokens": ["What", "is", "the", "nearestNeighbor", "operator", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2109904958", "fields": {"term": "What is targetHits used for?", "namespace": "open-p", "hash": -2109904958, "terms": ["What is targetHits used for?", "is targetHits used for?", "targetHits used for?", "used for?", "for?"], "tokens": ["What", "is", "targetHits", "used", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1953335575", "fields": {"term": "What is the constraint for nearestNeighbor query?", "namespace": "open-p", "hash": 1953335575, "terms": ["What is the constraint for nearestNeighbor query?", "is the constraint for nearestNeighbor query?", "the constraint for nearestNeighbor query?", "constraint for nearestNeighbor query?", "for nearestNeighbor query?", "nearestNeighbor query?", "query?"], "tokens": ["What", "is", "the", "constraint", "for", "nearestNeighbor", "query", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1726219672", "fields": {"term": "Where can I specify the query in Vespa?", "namespace": "open-p", "hash": -1726219672, "terms": ["Where can I specify the query in Vespa?", "can I specify the query in Vespa?", "I specify the query in Vespa?", "specify the query in Vespa?", "the query in Vespa?", "query in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["Where", "can", "I", "specify", "the", "query", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::686191508", "fields": {"term": "What is RetrievalModelSearcher?", "namespace": "open-p", "hash": 686191508, "terms": ["What is RetrievalModelSearcher?", "is RetrievalModelSearcher?", "RetrievalModelSearcher?"], "tokens": ["What", "is", "RetrievalModelSearcher", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1641265065", "fields": {"term": "What is hamming distance search?", "namespace": "open-p", "hash": 1641265065, "terms": ["What is hamming distance search?", "is hamming distance search?", "hamming distance search?", "distance search?", "search?"], "tokens": ["What", "is", "hamming", "distance", "search", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1073590430", "fields": {"term": "What is the use of num-threads-per-search?", "namespace": "open-p", "hash": -1073590430, "terms": ["What is the use of num-threads-per-search?", "is the use of num-threads-per-search?", "the use of num-threads-per-search?", "use of num-threads-per-search?", "of num-threads-per-search?", "num-threads-per-search?"], "tokens": ["What", "is", "the", "use", "of", "num", "threads", "per", "search", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2034226061", "fields": {"term": "What are the cpu instructions implemented in hamming distance search?", "namespace": "open-p", "hash": -2034226061, "terms": ["What are the cpu instructions implemented in hamming distance search?", "are the cpu instructions implemented in hamming distance search?", "the cpu instructions implemented in hamming distance search?", "cpu instructions implemented in hamming distance search?", "instructions implemented in hamming distance search?", "implemented in hamming distance search?", "in hamming distance search?", "hamming distance search?", "distance search?", "search?"], "tokens": ["What", "are", "the", "cpu", "instructions", "implemented", "in", "hamming", "distance", "search", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1148220061", "fields": {"term": "Where can I find a detailed introduction to using binary vectors with hamming distance?", "namespace": "open-p", "hash": 1148220061, "terms": ["Where can I find a detailed introduction to using binary vectors with hamming distance?", "can I find a detailed introduction to using binary vectors with hamming distance?", "I find a detailed introduction to using binary vectors with hamming distance?", "find a detailed introduction to using binary vectors with hamming distance?", "a detailed introduction to using binary vectors with hamming distance?", "detailed introduction to using binary vectors with hamming distance?", "introduction to using binary vectors with hamming distance?", "to using binary vectors with hamming distance?", "using binary vectors with hamming distance?", "binary vectors with hamming distance?", "vectors with hamming distance?", "with hamming distance?", "hamming distance?", "distance?"], "tokens": ["Where", "can", "I", "find", "a", "detailed", "introduction", "to", "using", "binary", "vectors", "with", "hamming", "distance", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-632242949", "fields": {"term": "What is the goal of this tutorial?", "namespace": "open-p", "hash": -632242949, "terms": ["What is the goal of this tutorial?", "is the goal of this tutorial?", "the goal of this tutorial?", "goal of this tutorial?", "of this tutorial?", "this tutorial?", "tutorial?"], "tokens": ["What", "is", "the", "goal", "of", "this", "tutorial", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::823331659", "fields": {"term": "What are the different parts of the tutorial?", "namespace": "open-p", "hash": 823331659, "terms": ["What are the different parts of the tutorial?", "are the different parts of the tutorial?", "the different parts of the tutorial?", "different parts of the tutorial?", "parts of the tutorial?", "of the tutorial?", "the tutorial?", "tutorial?"], "tokens": ["What", "are", "the", "different", "parts", "of", "the", "tutorial", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1822916342", "fields": {"term": "What is covered in the getting started part?", "namespace": "open-p", "hash": 1822916342, "terms": ["What is covered in the getting started part?", "is covered in the getting started part?", "covered in the getting started part?", "in the getting started part?", "the getting started part?", "getting started part?", "started part?", "part?"], "tokens": ["What", "is", "covered", "in", "the", "getting", "started", "part", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1749070044", "fields": {"term": "What is the recommended entry point for this tutorial?", "namespace": "open-p", "hash": 1749070044, "terms": ["What is the recommended entry point for this tutorial?", "is the recommended entry point for this tutorial?", "the recommended entry point for this tutorial?", "recommended entry point for this tutorial?", "entry point for this tutorial?", "point for this tutorial?", "for this tutorial?", "this tutorial?", "tutorial?"], "tokens": ["What", "is", "the", "recommended", "entry", "point", "for", "this", "tutorial", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1938083742", "fields": {"term": "What is covered in part 2 of the tutorial?", "namespace": "open-p", "hash": -1938083742, "terms": ["What is covered in part 2 of the tutorial?", "is covered in part 2 of the tutorial?", "covered in part 2 of the tutorial?", "in part 2 of the tutorial?", "part 2 of the tutorial?", "2 of the tutorial?", "of the tutorial?", "the tutorial?", "tutorial?"], "tokens": ["What", "is", "covered", "in", "part", "2", "of", "the", "tutorial", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::527786538", "fields": {"term": "What is covered in part 5 of the tutorial?", "namespace": "open-p", "hash": 527786538, "terms": ["What is covered in part 5 of the tutorial?", "is covered in part 5 of the tutorial?", "covered in part 5 of the tutorial?", "in part 5 of the tutorial?", "part 5 of the tutorial?", "5 of the tutorial?", "of the tutorial?", "the tutorial?", "tutorial?"], "tokens": ["What", "is", "covered", "in", "part", "5", "of", "the", "tutorial", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1078306115", "fields": {"term": "What operating systems is Vespa-CLI available for?", "namespace": "open-p", "hash": 1078306115, "terms": ["What operating systems is Vespa-CLI available for?", "operating systems is Vespa-CLI available for?", "systems is Vespa-CLI available for?", "is Vespa-CLI available for?", "Vespa-CLI available for?", "available for?", "for?"], "tokens": ["What", "operating", "systems", "is", "Vespa", "CLI", "available", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::250041126", "fields": {"term": "How do I install Vespa-CLI?", "namespace": "open-p", "hash": 250041126, "terms": ["How do I install Vespa-CLI?", "do I install Vespa-CLI?", "I install Vespa-CLI?", "install Vespa-CLI?", "Vespa-CLI?"], "tokens": ["How", "do", "I", "install", "Vespa", "CLI", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2043795793", "fields": {"term": "What is the companion sample application?", "namespace": "open-p", "hash": -2043795793, "terms": ["What is the companion sample application?", "is the companion sample application?", "the companion sample application?", "companion sample application?", "sample application?", "application?"], "tokens": ["What", "is", "the", "companion", "sample", "application", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1067315259", "fields": {"term": "How do I clone the sample application?", "namespace": "open-p", "hash": -1067315259, "terms": ["How do I clone the sample application?", "do I clone the sample application?", "I clone the sample application?", "clone the sample application?", "the sample application?", "sample application?", "application?"], "tokens": ["How", "do", "I", "clone", "the", "sample", "application", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1297887570", "fields": {"term": "Where is the news directory located?", "namespace": "open-p", "hash": 1297887570, "terms": ["Where is the news directory located?", "is the news directory located?", "the news directory located?", "news directory located?", "directory located?", "located?"], "tokens": ["Where", "is", "the", "news", "directory", "located", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1983519559", "fields": {"term": "What is contained in the app-1-getting-started directory?", "namespace": "open-p", "hash": -1983519559, "terms": ["What is contained in the app-1-getting-started directory?", "is contained in the app-1-getting-started directory?", "contained in the app-1-getting-started directory?", "in the app-1-getting-started directory?", "the app-1-getting-started directory?", "app-1-getting-started directory?", "directory?"], "tokens": ["What", "is", "contained", "in", "the", "app", "1", "getting", "started", "directory", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::631298674", "fields": {"term": "What happens when you run 'vespa deploy' command?", "namespace": "open-p", "hash": 631298674, "terms": ["What happens when you run 'vespa deploy' command?", "happens when you run 'vespa deploy' command?", "when you run 'vespa deploy' command?", "you run 'vespa deploy' command?", "run 'vespa deploy' command?", "'vespa deploy' command?", "deploy' command?", "command?"], "tokens": ["What", "happens", "when", "you", "run", "'", "vespa", "deploy", "'", "command", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1325184899", "fields": {"term": "Can you deploy a new version of the application without restarting services?", "namespace": "open-p", "hash": 1325184899, "terms": ["Can you deploy a new version of the application without restarting services?", "you deploy a new version of the application without restarting services?", "deploy a new version of the application without restarting services?", "a new version of the application without restarting services?", "new version of the application without restarting services?", "version of the application without restarting services?", "of the application without restarting services?", "the application without restarting services?", "application without restarting services?", "without restarting services?", "restarting services?", "services?"], "tokens": ["Can", "you", "deploy", "a", "new", "version", "of", "the", "application", "without", "restarting", "services", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2104691811", "fields": {"term": "What is feeding in Vespa?", "namespace": "open-p", "hash": 2104691811, "terms": ["What is feeding in Vespa?", "is feeding in Vespa?", "feeding in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["What", "is", "feeding", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::907100869", "fields": {"term": "What is vespa-feed-client?", "namespace": "open-p", "hash": 907100869, "terms": ["What is vespa-feed-client?", "is vespa-feed-client?", "vespa-feed-client?"], "tokens": ["What", "is", "vespa", "feed", "client", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::-96356196", "fields": {"term": "What is the Vespa CLI used for?", "namespace": "open-p", "hash": -96356196, "terms": ["What is the Vespa CLI used for?", "is the Vespa CLI used for?", "the Vespa CLI used for?", "Vespa CLI used for?", "CLI used for?", "used for?", "for?"], "tokens": ["What", "is", "the", "Vespa", "CLI", "used", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2122324709", "fields": {"term": "How to search for a phrase in title?", "namespace": "open-p", "hash": 2122324709, "terms": ["How to search for a phrase in title?", "to search for a phrase in title?", "search for a phrase in title?", "for a phrase in title?", "a phrase in title?", "phrase in title?", "in title?", "title?"], "tokens": ["How", "to", "search", "for", "a", "phrase", "in", "title", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1019243235", "fields": {"term": "What is demonstrated in the next part of the tutorial?", "namespace": "open-p", "hash": -1019243235, "terms": ["What is demonstrated in the next part of the tutorial?", "is demonstrated in the next part of the tutorial?", "demonstrated in the next part of the tutorial?", "in the next part of the tutorial?", "the next part of the tutorial?", "next part of the tutorial?", "part of the tutorial?", "of the tutorial?", "the tutorial?", "tutorial?"], "tokens": ["What", "is", "demonstrated", "in", "the", "next", "part", "of", "the", "tutorial", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1565460656", "fields": {"term": "How to remove document from Vespa?", "namespace": "open-p", "hash": 1565460656, "terms": ["How to remove document from Vespa?", "to remove document from Vespa?", "remove document from Vespa?", "document from Vespa?", "from Vespa?", "Vespa?"], "tokens": ["How", "to", "remove", "document", "from", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1034540464", "fields": {"term": "How to start Vespa services?", "namespace": "open-p", "hash": -1034540464, "terms": ["How to start Vespa services?", "to start Vespa services?", "start Vespa services?", "Vespa services?", "services?"], "tokens": ["How", "to", "start", "Vespa", "services", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1451164430", "fields": {"term": "How to stop Vespa services?", "namespace": "open-p", "hash": -1451164430, "terms": ["How to stop Vespa services?", "to stop Vespa services?", "stop Vespa services?", "Vespa services?", "services?"], "tokens": ["How", "to", "stop", "Vespa", "services", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1435077775", "fields": {"term": "Is the application up and running?", "namespace": "open-p", "hash": 1435077775, "terms": ["Is the application up and running?", "the application up and running?", "application up and running?", "up and running?", "and running?", "running?"], "tokens": ["Is", "the", "application", "up", "and", "running", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::477593669", "fields": {"term": "What is the next tutorial about?", "namespace": "open-p", "hash": 477593669, "terms": ["What is the next tutorial about?", "is the next tutorial about?", "the next tutorial about?", "next tutorial about?", "tutorial about?", "about?"], "tokens": ["What", "is", "the", "next", "tutorial", "about", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::-1698551606", "fields": {"term": "What is the tutorial series about?", "namespace": "open-p", "hash": -1698551606, "terms": ["What is the tutorial series about?", "is the tutorial series about?", "the tutorial series about?", "tutorial series about?", "series about?", "about?"], "tokens": ["What", "is", "the", "tutorial", "series", "about", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1010504909", "fields": {"term": "What is the final state of this tutorial?", "namespace": "open-p", "hash": -1010504909, "terms": ["What is the final state of this tutorial?", "is the final state of this tutorial?", "the final state of this tutorial?", "final state of this tutorial?", "state of this tutorial?", "of this tutorial?", "this tutorial?", "tutorial?"], "tokens": ["What", "is", "the", "final", "state", "of", "this", "tutorial", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1294510266", "fields": {"term": "What is app-2-feed-and-query?", "namespace": "open-p", "hash": -1294510266, "terms": ["What is app-2-feed-and-query?", "is app-2-feed-and-query?", "app-2-feed-and-query?"], "tokens": ["What", "is", "app", "2", "feed", "and", "query", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1688334802", "fields": {"term": "What is the Microsoft News Dataset?", "namespace": "open-p", "hash": 1688334802, "terms": ["What is the Microsoft News Dataset?", "is the Microsoft News Dataset?", "the Microsoft News Dataset?", "Microsoft News Dataset?", "News Dataset?", "Dataset?"], "tokens": ["What", "is", "the", "Microsoft", "News", "Dataset", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1036073582", "fields": {"term": "What does the MIND dataset contain?", "namespace": "open-p", "hash": -1036073582, "terms": ["What does the MIND dataset contain?", "does the MIND dataset contain?", "the MIND dataset contain?", "MIND dataset contain?", "dataset contain?", "contain?"], "tokens": ["What", "does", "the", "MIND", "dataset", "contain", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::809603501", "fields": {"term": "What is included in the news article content?", "namespace": "open-p", "hash": 809603501, "terms": ["What is included in the news article content?", "is included in the news article content?", "included in the news article content?", "in the news article content?", "the news article content?", "news article content?", "article content?", "content?"], "tokens": ["What", "is", "included", "in", "the", "news", "article", "content", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::495029044", "fields": {"term": "What are impressions in the MIND dataset?", "namespace": "open-p", "hash": 495029044, "terms": ["What are impressions in the MIND dataset?", "are impressions in the MIND dataset?", "impressions in the MIND dataset?", "in the MIND dataset?", "the MIND dataset?", "MIND dataset?", "dataset?"], "tokens": ["What", "are", "impressions", "in", "the", "MIND", "dataset", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1816344207", "fields": {"term": "How many articles are in the MIND dataset?", "namespace": "open-p", "hash": -1816344207, "terms": ["How many articles are in the MIND dataset?", "many articles are in the MIND dataset?", "articles are in the MIND dataset?", "are in the MIND dataset?", "in the MIND dataset?", "the MIND dataset?", "MIND dataset?", "dataset?"], "tokens": ["How", "many", "articles", "are", "in", "the", "MIND", "dataset", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1686030070", "fields": {"term": "What is the size of the demo dataset?", "namespace": "open-p", "hash": 1686030070, "terms": ["What is the size of the demo dataset?", "is the size of the demo dataset?", "the size of the demo dataset?", "size of the demo dataset?", "of the demo dataset?", "the demo dataset?", "demo dataset?", "dataset?"], "tokens": ["What", "is", "the", "size", "of", "the", "demo", "dataset", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1569004732", "fields": {"term": "How to download data for the news sample app?", "namespace": "open-p", "hash": -1569004732, "terms": ["How to download data for the news sample app?", "to download data for the news sample app?", "download data for the news sample app?", "data for the news sample app?", "for the news sample app?", "the news sample app?", "news sample app?", "sample app?", "app?"], "tokens": ["How", "to", "download", "data", "for", "the", "news", "sample", "app", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::342662202", "fields": {"term": "What data is in the news.tsv file?", "namespace": "open-p", "hash": 342662202, "terms": ["What data is in the news.tsv file?", "data is in the news.tsv file?", "is in the news.tsv file?", "in the news.tsv file?", "the news.tsv file?", "news.tsv file?", "file?"], "tokens": ["What", "data", "is", "in", "the", "news", "tsv", "file", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::459748702", "fields": {"term": "What is the hosts.xml file?", "namespace": "open-p", "hash": 459748702, "terms": ["What is the hosts.xml file?", "is the hosts.xml file?", "the hosts.xml file?", "hosts.xml file?", "file?"], "tokens": ["What", "is", "the", "hosts", "xml", "file", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-536626094", "fields": {"term": "What is needed for self-hosted multi-node deployments?", "namespace": "open-p", "hash": -536626094, "terms": ["What is needed for self-hosted multi-node deployments?", "is needed for self-hosted multi-node deployments?", "needed for self-hosted multi-node deployments?", "for self-hosted multi-node deployments?", "self-hosted multi-node deployments?", "multi-node deployments?", "deployments?"], "tokens": ["What", "is", "needed", "for", "self", "hosted", "multi", "node", "deployments", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1782874312", "fields": {"term": "What does services.xml define?", "namespace": "open-p", "hash": -1782874312, "terms": ["What does services.xml define?", "does services.xml define?", "services.xml define?", "define?"], "tokens": ["What", "does", "services", "xml", "define", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::1526536415", "fields": {"term": "How is the content cluster capacity increased?", "namespace": "open-p", "hash": 1526536415, "terms": ["How is the content cluster capacity increased?", "is the content cluster capacity increased?", "the content cluster capacity increased?", "content cluster capacity increased?", "cluster capacity increased?", "capacity increased?", "increased?"], "tokens": ["How", "is", "the", "content", "cluster", "capacity", "increased", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2100545942", "fields": {"term": "What is the index property?", "namespace": "open-p", "hash": -2100545942, "terms": ["What is the index property?", "is the index property?", "the index property?", "index property?", "property?"], "tokens": ["What", "is", "the", "index", "property", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1182190735", "fields": {"term": "What files are necessary for deployment?", "namespace": "open-p", "hash": -1182190735, "terms": ["What files are necessary for deployment?", "files are necessary for deployment?", "are necessary for deployment?", "necessary for deployment?", "for deployment?", "deployment?"], "tokens": ["What", "files", "are", "necessary", "for", "deployment", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::967598931", "fields": {"term": "What is the wait time for deployment?", "namespace": "open-p", "hash": 967598931, "terms": ["What is the wait time for deployment?", "is the wait time for deployment?", "the wait time for deployment?", "wait time for deployment?", "time for deployment?", "for deployment?", "deployment?"], "tokens": ["What", "is", "the", "wait", "time", "for", "deployment", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-476854457", "fields": {"term": "How do you convert MIND data for Vespa?", "namespace": "open-p", "hash": -476854457, "terms": ["How do you convert MIND data for Vespa?", "do you convert MIND data for Vespa?", "you convert MIND data for Vespa?", "convert MIND data for Vespa?", "MIND data for Vespa?", "data for Vespa?", "for Vespa?", "Vespa?"], "tokens": ["How", "do", "you", "convert", "MIND", "data", "for", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1313388280", "fields": {"term": "What file contains MIND data for Vespa?", "namespace": "open-p", "hash": -1313388280, "terms": ["What file contains MIND data for Vespa?", "file contains MIND data for Vespa?", "contains MIND data for Vespa?", "MIND data for Vespa?", "data for Vespa?", "for Vespa?", "Vespa?"], "tokens": ["What", "file", "contains", "MIND", "data", "for", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2104370797", "fields": {"term": "How can you verify that specific documents are indexed?", "namespace": "open-p", "hash": 2104370797, "terms": ["How can you verify that specific documents are indexed?", "can you verify that specific documents are indexed?", "you verify that specific documents are indexed?", "verify that specific documents are indexed?", "that specific documents are indexed?", "specific documents are indexed?", "documents are indexed?", "are indexed?", "indexed?"], "tokens": ["How", "can", "you", "verify", "that", "specific", "documents", "are", "indexed", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-890931771", "fields": {"term": "How can the query be executed using both GET and POST?", "namespace": "open-p", "hash": -890931771, "terms": ["How can the query be executed using both GET and POST?", "can the query be executed using both GET and POST?", "the query be executed using both GET and POST?", "query be executed using both GET and POST?", "be executed using both GET and POST?", "executed using both GET and POST?", "using both GET and POST?", "both GET and POST?", "GET and POST?", "and POST?", "POST?"], "tokens": ["How", "can", "the", "query", "be", "executed", "using", "both", "GET", "and", "POST", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::30911894", "fields": {"term": "What does the totalCount field contain?", "namespace": "open-p", "hash": 30911894, "terms": ["What does the totalCount field contain?", "does the totalCount field contain?", "the totalCount field contain?", "totalCount field contain?", "field contain?", "contain?"], "tokens": ["What", "does", "the", "totalCount", "field", "contain", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::266803296", "fields": {"term": "What is query tracing?", "namespace": "open-p", "hash": 266803296, "terms": ["What is query tracing?", "is query tracing?", "query tracing?", "tracing?"], "tokens": ["What", "is", "query", "tracing", "?"], "corpus_count": 3, "document_count": 3}}, -{"put": "id:term:term::356453056", "fields": {"term": "Why should HTTP POST be used over GET in production?", "namespace": "open-p", "hash": 356453056, "terms": ["Why should HTTP POST be used over GET in production?", "should HTTP POST be used over GET in production?", "HTTP POST be used over GET in production?", "POST be used over GET in production?", "be used over GET in production?", "used over GET in production?", "over GET in production?", "GET in production?", "in production?", "production?"], "tokens": ["Why", "should", "HTTP", "POST", "be", "used", "over", "GET", "in", "production", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::154329238", "fields": {"term": "What is type=phrase?", "namespace": "open-p", "hash": 154329238, "terms": ["What is type=phrase?", "is type=phrase?", "type=phrase?"], "tokens": ["What", "is", "type", "=", "phrase", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1271518882", "fields": {"term": "How can you boost documents in a query?", "namespace": "open-p", "hash": -1271518882, "terms": ["How can you boost documents in a query?", "can you boost documents in a query?", "you boost documents in a query?", "boost documents in a query?", "documents in a query?", "in a query?", "a query?", "query?"], "tokens": ["How", "can", "you", "boost", "documents", "in", "a", "query", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1215074561", "fields": {"term": "What is YQL userQuery()?", "namespace": "open-p", "hash": -1215074561, "terms": ["What is YQL userQuery()?", "is YQL userQuery()?", "YQL userQuery()?", "userQuery()?"], "tokens": ["What", "is", "YQL", "userQuery", "()?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1761951361", "fields": {"term": "What will the next part of the tutorial cover?", "namespace": "open-p", "hash": 1761951361, "terms": ["What will the next part of the tutorial cover?", "will the next part of the tutorial cover?", "the next part of the tutorial cover?", "next part of the tutorial cover?", "part of the tutorial cover?", "of the tutorial cover?", "the tutorial cover?", "tutorial cover?", "cover?"], "tokens": ["What", "will", "the", "next", "part", "of", "the", "tutorial", "cover", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1360855343", "fields": {"term": "What is explored in the next part of the tutorial?", "namespace": "open-p", "hash": 1360855343, "terms": ["What is explored in the next part of the tutorial?", "is explored in the next part of the tutorial?", "explored in the next part of the tutorial?", "in the next part of the tutorial?", "the next part of the tutorial?", "next part of the tutorial?", "part of the tutorial?", "of the tutorial?", "the tutorial?", "tutorial?"], "tokens": ["What", "is", "explored", "in", "the", "next", "part", "of", "the", "tutorial", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-839249517", "fields": {"term": "What is a single-term query?", "namespace": "open-p", "hash": -839249517, "terms": ["What is a single-term query?", "is a single-term query?", "a single-term query?", "single-term query?", "query?"], "tokens": ["What", "is", "a", "single", "term", "query", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1187971310", "fields": {"term": "What fields are searched in default fieldset?", "namespace": "open-p", "hash": 1187971310, "terms": ["What fields are searched in default fieldset?", "fields are searched in default fieldset?", "are searched in default fieldset?", "searched in default fieldset?", "in default fieldset?", "default fieldset?", "fieldset?"], "tokens": ["What", "fields", "are", "searched", "in", "default", "fieldset", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-680289852", "fields": {"term": "What fields are searched with =?", "namespace": "open-p", "hash": -680289852, "terms": ["What fields are searched with =?", "fields are searched with =?", "are searched with =?", "searched with =?", "with =?", "=?"], "tokens": ["What", "fields", "are", "searched", "with", "=?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2064634590", "fields": {"term": "How can you combine two terms in Vespa search?", "namespace": "open-p", "hash": -2064634590, "terms": ["How can you combine two terms in Vespa search?", "can you combine two terms in Vespa search?", "you combine two terms in Vespa search?", "combine two terms in Vespa search?", "two terms in Vespa search?", "terms in Vespa search?", "in Vespa search?", "Vespa search?", "search?"], "tokens": ["How", "can", "you", "combine", "two", "terms", "in", "Vespa", "search", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-284958553", "fields": {"term": "How do you query for documents created on a specific date?", "namespace": "open-p", "hash": -284958553, "terms": ["How do you query for documents created on a specific date?", "do you query for documents created on a specific date?", "you query for documents created on a specific date?", "query for documents created on a specific date?", "for documents created on a specific date?", "documents created on a specific date?", "created on a specific date?", "on a specific date?", "a specific date?", "specific date?", "date?"], "tokens": ["How", "do", "you", "query", "for", "documents", "created", "on", "a", "specific", "date", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1476858439", "fields": {"term": "What happens when a field is not included in a fieldset?", "namespace": "open-p", "hash": 1476858439, "terms": ["What happens when a field is not included in a fieldset?", "happens when a field is not included in a fieldset?", "when a field is not included in a fieldset?", "a field is not included in a fieldset?", "field is not included in a fieldset?", "is not included in a fieldset?", "not included in a fieldset?", "included in a fieldset?", "in a fieldset?", "a fieldset?", "fieldset?"], "tokens": ["What", "happens", "when", "a", "field", "is", "not", "included", "in", "a", "fieldset", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-654033056", "fields": {"term": "What are range searches?", "namespace": "open-p", "hash": -654033056, "terms": ["What are range searches?", "are range searches?", "range searches?", "searches?"], "tokens": ["What", "are", "range", "searches", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-142198092", "fields": {"term": "What operators can be used for range searches?", "namespace": "open-p", "hash": -142198092, "terms": ["What operators can be used for range searches?", "operators can be used for range searches?", "can be used for range searches?", "be used for range searches?", "used for range searches?", "for range searches?", "range searches?", "searches?"], "tokens": ["What", "operators", "can", "be", "used", "for", "range", "searches", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::627658809", "fields": {"term": "How do you search for documents before a certain date?", "namespace": "open-p", "hash": 627658809, "terms": ["How do you search for documents before a certain date?", "do you search for documents before a certain date?", "you search for documents before a certain date?", "search for documents before a certain date?", "for documents before a certain date?", "documents before a certain date?", "before a certain date?", "a certain date?", "certain date?", "date?"], "tokens": ["How", "do", "you", "search", "for", "documents", "before", "a", "certain", "date", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::326889176", "fields": {"term": "How do you search for documents within a date range?", "namespace": "open-p", "hash": 326889176, "terms": ["How do you search for documents within a date range?", "do you search for documents within a date range?", "you search for documents within a date range?", "search for documents within a date range?", "for documents within a date range?", "documents within a date range?", "within a date range?", "a date range?", "date range?", "range?"], "tokens": ["How", "do", "you", "search", "for", "documents", "within", "a", "date", "range", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1666085953", "fields": {"term": "How to change hit order?", "namespace": "open-p", "hash": -1666085953, "terms": ["How to change hit order?", "to change hit order?", "change hit order?", "hit order?", "order?"], "tokens": ["How", "to", "change", "hit", "order", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::287318733", "fields": {"term": "How to specify sort order?", "namespace": "open-p", "hash": 287318733, "terms": ["How to specify sort order?", "to specify sort order?", "specify sort order?", "sort order?", "order?"], "tokens": ["How", "to", "specify", "sort", "order", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1127248854", "fields": {"term": "What are some common use cases for grouping?", "namespace": "open-p", "hash": 1127248854, "terms": ["What are some common use cases for grouping?", "are some common use cases for grouping?", "some common use cases for grouping?", "common use cases for grouping?", "use cases for grouping?", "cases for grouping?", "for grouping?", "grouping?"], "tokens": ["What", "are", "some", "common", "use", "cases", "for", "grouping", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2125679583", "fields": {"term": "How do you group documents by time and date?", "namespace": "open-p", "hash": 2125679583, "terms": ["How do you group documents by time and date?", "do you group documents by time and date?", "you group documents by time and date?", "group documents by time and date?", "documents by time and date?", "by time and date?", "time and date?", "and date?", "date?"], "tokens": ["How", "do", "you", "group", "documents", "by", "time", "and", "date", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1299353487", "fields": {"term": "What is result diversification?", "namespace": "open-p", "hash": -1299353487, "terms": ["What is result diversification?", "is result diversification?", "result diversification?", "diversification?"], "tokens": ["What", "is", "result", "diversification", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1293510205", "fields": {"term": "How do you perform a grouping query in Vespa?", "namespace": "open-p", "hash": 1293510205, "terms": ["How do you perform a grouping query in Vespa?", "do you perform a grouping query in Vespa?", "you perform a grouping query in Vespa?", "perform a grouping query in Vespa?", "a grouping query in Vespa?", "grouping query in Vespa?", "query in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["How", "do", "you", "perform", "a", "grouping", "query", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1883117941", "fields": {"term": "What is the output of a grouping query in Vespa?", "namespace": "open-p", "hash": -1883117941, "terms": ["What is the output of a grouping query in Vespa?", "is the output of a grouping query in Vespa?", "the output of a grouping query in Vespa?", "output of a grouping query in Vespa?", "of a grouping query in Vespa?", "a grouping query in Vespa?", "grouping query in Vespa?", "query in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["What", "is", "the", "output", "of", "a", "grouping", "query", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1254203115", "fields": {"term": "What is the default sorting in a grouping query?", "namespace": "open-p", "hash": 1254203115, "terms": ["What is the default sorting in a grouping query?", "is the default sorting in a grouping query?", "the default sorting in a grouping query?", "default sorting in a grouping query?", "sorting in a grouping query?", "in a grouping query?", "a grouping query?", "grouping query?", "query?"], "tokens": ["What", "is", "the", "default", "sorting", "in", "a", "grouping", "query", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::964897856", "fields": {"term": "What are the differences between index and attribute?", "namespace": "open-p", "hash": 964897856, "terms": ["What are the differences between index and attribute?", "are the differences between index and attribute?", "the differences between index and attribute?", "differences between index and attribute?", "between index and attribute?", "index and attribute?", "and attribute?", "attribute?"], "tokens": ["What", "are", "the", "differences", "between", "index", "and", "attribute", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-927501227", "fields": {"term": "How does Vespa tokenize strings?", "namespace": "open-p", "hash": -927501227, "terms": ["How does Vespa tokenize strings?", "does Vespa tokenize strings?", "Vespa tokenize strings?", "tokenize strings?", "strings?"], "tokens": ["How", "does", "Vespa", "tokenize", "strings", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::894957268", "fields": {"term": "What is stemming in linguistic processing?", "namespace": "open-p", "hash": 894957268, "terms": ["What is stemming in linguistic processing?", "is stemming in linguistic processing?", "stemming in linguistic processing?", "in linguistic processing?", "linguistic processing?", "processing?"], "tokens": ["What", "is", "stemming", "in", "linguistic", "processing", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-664582149", "fields": {"term": "What are the search capabilities for exact phrase queries?", "namespace": "open-p", "hash": -664582149, "terms": ["What are the search capabilities for exact phrase queries?", "are the search capabilities for exact phrase queries?", "the search capabilities for exact phrase queries?", "search capabilities for exact phrase queries?", "capabilities for exact phrase queries?", "for exact phrase queries?", "exact phrase queries?", "phrase queries?", "queries?"], "tokens": ["What", "are", "the", "search", "capabilities", "for", "exact", "phrase", "queries", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2059905875", "fields": {"term": "What is the difference between 'match:text' and indexing:attributes?", "namespace": "open-p", "hash": -2059905875, "terms": ["What is the difference between 'match:text' and indexing:attributes?", "is the difference between 'match:text' and indexing:attributes?", "the difference between 'match:text' and indexing:attributes?", "difference between 'match:text' and indexing:attributes?", "between 'match:text' and indexing:attributes?", "'match:text' and indexing:attributes?", "and indexing:attributes?", "indexing:attributes?"], "tokens": ["What", "is", "the", "difference", "between", "'", "match", ":", "text", "'", "and", "indexing", ":", "attributes", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2125205082", "fields": {"term": "What is the role of match mode in Vespa search?", "namespace": "open-p", "hash": 2125205082, "terms": ["What is the role of match mode in Vespa search?", "is the role of match mode in Vespa search?", "the role of match mode in Vespa search?", "role of match mode in Vespa search?", "of match mode in Vespa search?", "match mode in Vespa search?", "mode in Vespa search?", "in Vespa search?", "Vespa search?", "search?"], "tokens": ["What", "is", "the", "role", "of", "match", "mode", "in", "Vespa", "search", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1731228650", "fields": {"term": "How does memory usage affect cost-effectiveness?", "namespace": "open-p", "hash": -1731228650, "terms": ["How does memory usage affect cost-effectiveness?", "does memory usage affect cost-effectiveness?", "memory usage affect cost-effectiveness?", "usage affect cost-effectiveness?", "affect cost-effectiveness?", "cost-effectiveness?"], "tokens": ["How", "does", "memory", "usage", "affect", "cost", "effectiveness", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::139205436", "fields": {"term": "What is the performance impact of storing large flavor types as attributes?", "namespace": "open-p", "hash": 139205436, "terms": ["What is the performance impact of storing large flavor types as attributes?", "is the performance impact of storing large flavor types as attributes?", "the performance impact of storing large flavor types as attributes?", "performance impact of storing large flavor types as attributes?", "impact of storing large flavor types as attributes?", "of storing large flavor types as attributes?", "storing large flavor types as attributes?", "large flavor types as attributes?", "flavor types as attributes?", "types as attributes?", "as attributes?", "attributes?"], "tokens": ["What", "is", "the", "performance", "impact", "of", "storing", "large", "flavor", "types", "as", "attributes", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::317082789", "fields": {"term": "What are the advantages of attributes?", "namespace": "open-p", "hash": 317082789, "terms": ["What are the advantages of attributes?", "are the advantages of attributes?", "the advantages of attributes?", "advantages of attributes?", "of attributes?", "attributes?"], "tokens": ["What", "are", "the", "advantages", "of", "attributes", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::542709757", "fields": {"term": "When to use attributes?", "namespace": "open-p", "hash": 542709757, "terms": ["When to use attributes?", "to use attributes?", "use attributes?", "attributes?"], "tokens": ["When", "to", "use", "attributes", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1288867790", "fields": {"term": "What fields support higher update throughput?", "namespace": "open-p", "hash": 1288867790, "terms": ["What fields support higher update throughput?", "fields support higher update throughput?", "support higher update throughput?", "higher update throughput?", "update throughput?", "throughput?"], "tokens": ["What", "fields", "support", "higher", "update", "throughput", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2079081983", "fields": {"term": "Which fields are used for sorting?", "namespace": "open-p", "hash": 2079081983, "terms": ["Which fields are used for sorting?", "fields are used for sorting?", "are used for sorting?", "used for sorting?", "for sorting?", "sorting?"], "tokens": ["Which", "fields", "are", "used", "for", "sorting", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1030407717", "fields": {"term": "What types of field must be defined with attribute?", "namespace": "open-p", "hash": -1030407717, "terms": ["What types of field must be defined with attribute?", "types of field must be defined with attribute?", "of field must be defined with attribute?", "field must be defined with attribute?", "must be defined with attribute?", "be defined with attribute?", "defined with attribute?", "with attribute?", "attribute?"], "tokens": ["What", "types", "of", "field", "must", "be", "defined", "with", "attribute", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1926452451", "fields": {"term": "What is the category field?", "namespace": "open-p", "hash": -1926452451, "terms": ["What is the category field?", "is the category field?", "the category field?", "category field?", "field?"], "tokens": ["What", "is", "the", "category", "field", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1007445276", "fields": {"term": "What is attribute indexing?", "namespace": "open-p", "hash": 1007445276, "terms": ["What is attribute indexing?", "is attribute indexing?", "attribute indexing?", "indexing?"], "tokens": ["What", "is", "attribute", "indexing", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2060687628", "fields": {"term": "What is index matching?", "namespace": "open-p", "hash": -2060687628, "terms": ["What is index matching?", "is index matching?", "index matching?", "matching?"], "tokens": ["What", "is", "index", "matching", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::783067823", "fields": {"term": "What is relevance?", "namespace": "open-p", "hash": 783067823, "terms": ["What is relevance?", "is relevance?", "relevance?"], "tokens": ["What", "is", "relevance", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1707403022", "fields": {"term": "What is the default ranking function?", "namespace": "open-p", "hash": -1707403022, "terms": ["What is the default ranking function?", "is the default ranking function?", "the default ranking function?", "default ranking function?", "ranking function?", "function?"], "tokens": ["What", "is", "the", "default", "ranking", "function", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1198994942", "fields": {"term": "How can ranking signals be added?", "namespace": "open-p", "hash": 1198994942, "terms": ["How can ranking signals be added?", "can ranking signals be added?", "ranking signals be added?", "signals be added?", "be added?", "added?"], "tokens": ["How", "can", "ranking", "signals", "be", "added", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-722646661", "fields": {"term": "What are the use cases for tweaking relevance?", "namespace": "open-p", "hash": -722646661, "terms": ["What are the use cases for tweaking relevance?", "are the use cases for tweaking relevance?", "the use cases for tweaking relevance?", "use cases for tweaking relevance?", "cases for tweaking relevance?", "for tweaking relevance?", "tweaking relevance?", "relevance?"], "tokens": ["What", "are", "the", "use", "cases", "for", "tweaking", "relevance", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-899429341", "fields": {"term": "What is Learning to Rank?", "namespace": "open-p", "hash": -899429341, "terms": ["What is Learning to Rank?", "is Learning to Rank?", "Learning to Rank?", "to Rank?", "Rank?"], "tokens": ["What", "is", "Learning", "to", "Rank", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2138076207", "fields": {"term": "What is the purpose of a popularity rank profile?", "namespace": "open-p", "hash": -2138076207, "terms": ["What is the purpose of a popularity rank profile?", "is the purpose of a popularity rank profile?", "the purpose of a popularity rank profile?", "purpose of a popularity rank profile?", "of a popularity rank profile?", "a popularity rank profile?", "popularity rank profile?", "rank profile?", "profile?"], "tokens": ["What", "is", "the", "purpose", "of", "a", "popularity", "rank", "profile", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1037807465", "fields": {"term": "What is a two-phase calculation?", "namespace": "open-p", "hash": -1037807465, "terms": ["What is a two-phase calculation?", "is a two-phase calculation?", "a two-phase calculation?", "two-phase calculation?", "calculation?"], "tokens": ["What", "is", "a", "two", "phase", "calculation", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::715645243", "fields": {"term": "What is the function of the popularity method?", "namespace": "open-p", "hash": 715645243, "terms": ["What is the function of the popularity method?", "is the function of the popularity method?", "the function of the popularity method?", "function of the popularity method?", "of the popularity method?", "the popularity method?", "popularity method?", "method?"], "tokens": ["What", "is", "the", "function", "of", "the", "popularity", "method", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::163663020", "fields": {"term": "What are the attributes available for an article in the news dataset?", "namespace": "open-p", "hash": 163663020, "terms": ["What are the attributes available for an article in the news dataset?", "are the attributes available for an article in the news dataset?", "the attributes available for an article in the news dataset?", "attributes available for an article in the news dataset?", "available for an article in the news dataset?", "for an article in the news dataset?", "an article in the news dataset?", "article in the news dataset?", "in the news dataset?", "the news dataset?", "news dataset?", "dataset?"], "tokens": ["What", "are", "the", "attributes", "available", "for", "an", "article", "in", "the", "news", "dataset", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::118077931", "fields": {"term": "What is impressions?", "namespace": "open-p", "hash": 118077931, "terms": ["What is impressions?", "is impressions?", "impressions?"], "tokens": ["What", "is", "impressions", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1452845564", "fields": {"term": "How can we use impressions in ranking?", "namespace": "open-p", "hash": -1452845564, "terms": ["How can we use impressions in ranking?", "can we use impressions in ranking?", "we use impressions in ranking?", "use impressions in ranking?", "impressions in ranking?", "in ranking?", "ranking?"], "tokens": ["How", "can", "we", "use", "impressions", "in", "ranking", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-17599848", "fields": {"term": "What does this tutorial cover?", "namespace": "open-p", "hash": -17599848, "terms": ["What does this tutorial cover?", "does this tutorial cover?", "this tutorial cover?", "tutorial cover?", "cover?"], "tokens": ["What", "does", "this", "tutorial", "cover", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::519216886", "fields": {"term": "How do you load data?", "namespace": "open-p", "hash": 519216886, "terms": ["How do you load data?", "do you load data?", "you load data?", "load data?", "data?"], "tokens": ["How", "do", "you", "load", "data", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-554834590", "fields": {"term": "What is the focus of recommendation systems?", "namespace": "open-p", "hash": -554834590, "terms": ["What is the focus of recommendation systems?", "is the focus of recommendation systems?", "the focus of recommendation systems?", "focus of recommendation systems?", "of recommendation systems?", "recommendation systems?", "systems?"], "tokens": ["What", "is", "the", "focus", "of", "recommendation", "systems", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1956139806", "fields": {"term": "What is the collaborative filtering method?", "namespace": "open-p", "hash": 1956139806, "terms": ["What is the collaborative filtering method?", "is the collaborative filtering method?", "the collaborative filtering method?", "collaborative filtering method?", "filtering method?", "method?"], "tokens": ["What", "is", "the", "collaborative", "filtering", "method", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1534316269", "fields": {"term": "What is the content-based approach?", "namespace": "open-p", "hash": -1534316269, "terms": ["What is the content-based approach?", "is the content-based approach?", "the content-based approach?", "content-based approach?", "approach?"], "tokens": ["What", "is", "the", "content", "based", "approach", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1063402584", "fields": {"term": "How can user profiles improve recommendations?", "namespace": "open-p", "hash": 1063402584, "terms": ["How can user profiles improve recommendations?", "can user profiles improve recommendations?", "user profiles improve recommendations?", "profiles improve recommendations?", "improve recommendations?", "recommendations?"], "tokens": ["How", "can", "user", "profiles", "improve", "recommendations", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-890108130", "fields": {"term": "What is the difference between maximum inner product search and euclidean distance search?", "namespace": "open-p", "hash": -890108130, "terms": ["What is the difference between maximum inner product search and euclidean distance search?", "is the difference between maximum inner product search and euclidean distance search?", "the difference between maximum inner product search and euclidean distance search?", "difference between maximum inner product search and euclidean distance search?", "between maximum inner product search and euclidean distance search?", "maximum inner product search and euclidean distance search?", "inner product search and euclidean distance search?", "product search and euclidean distance search?", "search and euclidean distance search?", "and euclidean distance search?", "euclidean distance search?", "distance search?", "search?"], "tokens": ["What", "is", "the", "difference", "between", "maximum", "inner", "product", "search", "and", "euclidean", "distance", "search", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1983583909", "fields": {"term": "What machine learning tools are needed?", "namespace": "open-p", "hash": 1983583909, "terms": ["What machine learning tools are needed?", "machine learning tools are needed?", "learning tools are needed?", "tools are needed?", "are needed?", "needed?"], "tokens": ["What", "machine", "learning", "tools", "are", "needed", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1526809425", "fields": {"term": "What is Numpy used for?", "namespace": "open-p", "hash": -1526809425, "terms": ["What is Numpy used for?", "is Numpy used for?", "Numpy used for?", "used for?", "for?"], "tokens": ["What", "is", "Numpy", "used", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1586469202", "fields": {"term": "What is Scikit-learn used for?", "namespace": "open-p", "hash": 1586469202, "terms": ["What is Scikit-learn used for?", "is Scikit-learn used for?", "Scikit-learn used for?", "used for?", "for?"], "tokens": ["What", "is", "Scikit", "learn", "used", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::570097584", "fields": {"term": "What is PyTorch used for?", "namespace": "open-p", "hash": 570097584, "terms": ["What is PyTorch used for?", "is PyTorch used for?", "PyTorch used for?", "used for?", "for?"], "tokens": ["What", "is", "PyTorch", "used", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1881178366", "fields": {"term": "What is the HuggingFace Transformers library used for?", "namespace": "open-p", "hash": -1881178366, "terms": ["What is the HuggingFace Transformers library used for?", "is the HuggingFace Transformers library used for?", "the HuggingFace Transformers library used for?", "HuggingFace Transformers library used for?", "Transformers library used for?", "library used for?", "used for?", "for?"], "tokens": ["What", "is", "the", "HuggingFace", "Transformers", "library", "used", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-975168402", "fields": {"term": "What is the command to install necessary dependencies?", "namespace": "open-p", "hash": -975168402, "terms": ["What is the command to install necessary dependencies?", "is the command to install necessary dependencies?", "the command to install necessary dependencies?", "command to install necessary dependencies?", "to install necessary dependencies?", "install necessary dependencies?", "necessary dependencies?", "dependencies?"], "tokens": ["What", "is", "the", "command", "to", "install", "necessary", "dependencies", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::401437374", "fields": {"term": "What is the MIND dataset?", "namespace": "open-p", "hash": 401437374, "terms": ["What is the MIND dataset?", "is the MIND dataset?", "the MIND dataset?", "MIND dataset?", "dataset?"], "tokens": ["What", "is", "the", "MIND", "dataset", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1446968753", "fields": {"term": "What is an impression?", "namespace": "open-p", "hash": -1446968753, "terms": ["What is an impression?", "is an impression?", "an impression?", "impression?"], "tokens": ["What", "is", "an", "impression", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::467600987", "fields": {"term": "What is included in an impression?", "namespace": "open-p", "hash": 467600987, "terms": ["What is included in an impression?", "is included in an impression?", "included in an impression?", "in an impression?", "an impression?", "impression?"], "tokens": ["What", "is", "included", "in", "an", "impression", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-683680820", "fields": {"term": "What is a skip?", "namespace": "open-p", "hash": -683680820, "terms": ["What is a skip?", "is a skip?", "a skip?", "skip?"], "tokens": ["What", "is", "a", "skip", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-438419683", "fields": {"term": "What is implicit feedback?", "namespace": "open-p", "hash": -438419683, "terms": ["What is implicit feedback?", "is implicit feedback?", "implicit feedback?", "feedback?"], "tokens": ["What", "is", "implicit", "feedback", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1362154038", "fields": {"term": "How can users' interests be inferred?", "namespace": "open-p", "hash": -1362154038, "terms": ["How can users' interests be inferred?", "can users' interests be inferred?", "users' interests be inferred?", "interests be inferred?", "be inferred?", "inferred?"], "tokens": ["How", "can", "users", "'", "interests", "be", "inferred", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-811210191", "fields": {"term": "How are clicks and skips used?", "namespace": "open-p", "hash": -811210191, "terms": ["How are clicks and skips used?", "are clicks and skips used?", "clicks and skips used?", "and skips used?", "skips used?", "used?"], "tokens": ["How", "are", "clicks", "and", "skips", "used", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::827249426", "fields": {"term": "What is matrix factorization?", "namespace": "open-p", "hash": 827249426, "terms": ["What is matrix factorization?", "is matrix factorization?", "matrix factorization?", "factorization?"], "tokens": ["What", "is", "matrix", "factorization", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2071516262", "fields": {"term": "What is collaborative filtering?", "namespace": "open-p", "hash": 2071516262, "terms": ["What is collaborative filtering?", "is collaborative filtering?", "collaborative filtering?", "filtering?"], "tokens": ["What", "is", "collaborative", "filtering", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-80900586", "fields": {"term": "What is the 'cold start' problem?", "namespace": "open-p", "hash": -80900586, "terms": ["What is the 'cold start' problem?", "is the 'cold start' problem?", "the 'cold start' problem?", "'cold start' problem?", "start' problem?", "problem?"], "tokens": ["What", "is", "the", "'", "cold", "start", "'", "problem", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-497225857", "fields": {"term": "How is recommendation generated?", "namespace": "open-p", "hash": -497225857, "terms": ["How is recommendation generated?", "is recommendation generated?", "recommendation generated?", "generated?"], "tokens": ["How", "is", "recommendation", "generated", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-100277835", "fields": {"term": "What approach does Vespa use for recommendation?", "namespace": "open-p", "hash": -100277835, "terms": ["What approach does Vespa use for recommendation?", "approach does Vespa use for recommendation?", "does Vespa use for recommendation?", "Vespa use for recommendation?", "use for recommendation?", "for recommendation?", "recommendation?"], "tokens": ["What", "approach", "does", "Vespa", "use", "for", "recommendation", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::257764184", "fields": {"term": "What is side information?", "namespace": "open-p", "hash": 257764184, "terms": ["What is side information?", "is side information?", "side information?", "information?"], "tokens": ["What", "is", "side", "information", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::449071056", "fields": {"term": "How does Vespa handle the 'cold start' problem?", "namespace": "open-p", "hash": 449071056, "terms": ["How does Vespa handle the 'cold start' problem?", "does Vespa handle the 'cold start' problem?", "Vespa handle the 'cold start' problem?", "handle the 'cold start' problem?", "the 'cold start' problem?", "'cold start' problem?", "start' problem?", "problem?"], "tokens": ["How", "does", "Vespa", "handle", "the", "'", "cold", "start", "'", "problem", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1715913975", "fields": {"term": "What is Alternating Least Squares?", "namespace": "open-p", "hash": -1715913975, "terms": ["What is Alternating Least Squares?", "is Alternating Least Squares?", "Alternating Least Squares?", "Least Squares?", "Squares?"], "tokens": ["What", "is", "Alternating", "Least", "Squares", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-933466253", "fields": {"term": "What is PyTorch?", "namespace": "open-p", "hash": -933466253, "terms": ["What is PyTorch?", "is PyTorch?", "PyTorch?"], "tokens": ["What", "is", "PyTorch", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::606385357", "fields": {"term": "What is `Embedding` class?", "namespace": "open-p", "hash": 606385357, "terms": ["What is `Embedding` class?", "is `Embedding` class?", "`Embedding` class?", "class?"], "tokens": ["What", "is", "`", "Embedding", "`", "class", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1307911801", "fields": {"term": "What are user and news embeddings?", "namespace": "open-p", "hash": -1307911801, "terms": ["What are user and news embeddings?", "are user and news embeddings?", "user and news embeddings?", "and news embeddings?", "news embeddings?", "embeddings?"], "tokens": ["What", "are", "user", "and", "news", "embeddings", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::986954409", "fields": {"term": "What is the loss function used?", "namespace": "open-p", "hash": 986954409, "terms": ["What is the loss function used?", "is the loss function used?", "the loss function used?", "loss function used?", "function used?", "used?"], "tokens": ["What", "is", "the", "loss", "function", "used", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1899207861", "fields": {"term": "What is the meaning of AUC?", "namespace": "open-p", "hash": -1899207861, "terms": ["What is the meaning of AUC?", "is the meaning of AUC?", "the meaning of AUC?", "meaning of AUC?", "of AUC?", "AUC?"], "tokens": ["What", "is", "the", "meaning", "of", "AUC", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1329309270", "fields": {"term": "What is content-based recommendation?", "namespace": "open-p", "hash": -1329309270, "terms": ["What is content-based recommendation?", "is content-based recommendation?", "content-based recommendation?", "recommendation?"], "tokens": ["What", "is", "content", "based", "recommendation", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1133314444", "fields": {"term": "How are category embeddings generated?", "namespace": "open-p", "hash": -1133314444, "terms": ["How are category embeddings generated?", "are category embeddings generated?", "category embeddings generated?", "embeddings generated?", "generated?"], "tokens": ["How", "are", "category", "embeddings", "generated", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::978120949", "fields": {"term": "What is the BERT model?", "namespace": "open-p", "hash": 978120949, "terms": ["What is the BERT model?", "is the BERT model?", "the BERT model?", "BERT model?", "model?"], "tokens": ["What", "is", "the", "BERT", "model", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-201196376", "fields": {"term": "What is the hidden dimension size?", "namespace": "open-p", "hash": -201196376, "terms": ["What is the hidden dimension size?", "is the hidden dimension size?", "the hidden dimension size?", "hidden dimension size?", "dimension size?", "size?"], "tokens": ["What", "is", "the", "hidden", "dimension", "size", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1261994314", "fields": {"term": "How do you create embeddings?", "namespace": "open-p", "hash": 1261994314, "terms": ["How do you create embeddings?", "do you create embeddings?", "you create embeddings?", "create embeddings?", "embeddings?"], "tokens": ["How", "do", "you", "create", "embeddings", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1095128103", "fields": {"term": "What is the purpose of the sentence classifier?", "namespace": "open-p", "hash": 1095128103, "terms": ["What is the purpose of the sentence classifier?", "is the purpose of the sentence classifier?", "the purpose of the sentence classifier?", "purpose of the sentence classifier?", "of the sentence classifier?", "the sentence classifier?", "sentence classifier?", "classifier?"], "tokens": ["What", "is", "the", "purpose", "of", "the", "sentence", "classifier", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1538608267", "fields": {"term": "What is the output size of the embedding?", "namespace": "open-p", "hash": 1538608267, "terms": ["What is the output size of the embedding?", "is the output size of the embedding?", "the output size of the embedding?", "output size of the embedding?", "size of the embedding?", "of the embedding?", "the embedding?", "embedding?"], "tokens": ["What", "is", "the", "output", "size", "of", "the", "embedding", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::303371001", "fields": {"term": "What is the format of the generated embeddings file?", "namespace": "open-p", "hash": 303371001, "terms": ["What is the format of the generated embeddings file?", "is the format of the generated embeddings file?", "the format of the generated embeddings file?", "format of the generated embeddings file?", "of the generated embeddings file?", "the generated embeddings file?", "generated embeddings file?", "embeddings file?", "file?"], "tokens": ["What", "is", "the", "format", "of", "the", "generated", "embeddings", "file", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1417633689", "fields": {"term": "What is ContentBasedModel?", "namespace": "open-p", "hash": 1417633689, "terms": ["What is ContentBasedModel?", "is ContentBasedModel?", "ContentBasedModel?"], "tokens": ["What", "is", "ContentBasedModel", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::635577086", "fields": {"term": "What is the purpose of BERT embeddings?", "namespace": "open-p", "hash": 635577086, "terms": ["What is the purpose of BERT embeddings?", "is the purpose of BERT embeddings?", "the purpose of BERT embeddings?", "purpose of BERT embeddings?", "of BERT embeddings?", "BERT embeddings?", "embeddings?"], "tokens": ["What", "is", "the", "purpose", "of", "BERT", "embeddings", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-873731830", "fields": {"term": "What losses are being measured?", "namespace": "open-p", "hash": -873731830, "terms": ["What losses are being measured?", "losses are being measured?", "are being measured?", "being measured?", "measured?"], "tokens": ["What", "losses", "are", "being", "measured", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1026711412", "fields": {"term": "What is the baseline model's AUC score?", "namespace": "open-p", "hash": 1026711412, "terms": ["What is the baseline model's AUC score?", "is the baseline model's AUC score?", "the baseline model's AUC score?", "baseline model's AUC score?", "model's AUC score?", "AUC score?", "score?"], "tokens": ["What", "is", "the", "baseline", "model", "'", "s", "AUC", "score", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2007249020", "fields": {"term": "What is MIPS?", "namespace": "open-p", "hash": -2007249020, "terms": ["What is MIPS?", "is MIPS?", "MIPS?"], "tokens": ["What", "is", "MIPS", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1435503949", "fields": {"term": "What is Euclidean nearest neighbor search?", "namespace": "open-p", "hash": -1435503949, "terms": ["What is Euclidean nearest neighbor search?", "is Euclidean nearest neighbor search?", "Euclidean nearest neighbor search?", "nearest neighbor search?", "neighbor search?", "search?"], "tokens": ["What", "is", "Euclidean", "nearest", "neighbor", "search", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1691824000", "fields": {"term": "What is the technique discussed in the paper?", "namespace": "open-p", "hash": -1691824000, "terms": ["What is the technique discussed in the paper?", "is the technique discussed in the paper?", "the technique discussed in the paper?", "technique discussed in the paper?", "discussed in the paper?", "in the paper?", "the paper?", "paper?"], "tokens": ["What", "is", "the", "technique", "discussed", "in", "the", "paper", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1782900991", "fields": {"term": "How can we map the MIPS problem to a Euclidean nearest neighbor search problem?", "namespace": "open-p", "hash": 1782900991, "terms": ["How can we map the MIPS problem to a Euclidean nearest neighbor search problem?", "can we map the MIPS problem to a Euclidean nearest neighbor search problem?", "we map the MIPS problem to a Euclidean nearest neighbor search problem?", "map the MIPS problem to a Euclidean nearest neighbor search problem?", "the MIPS problem to a Euclidean nearest neighbor search problem?", "MIPS problem to a Euclidean nearest neighbor search problem?", "problem to a Euclidean nearest neighbor search problem?", "to a Euclidean nearest neighbor search problem?", "a Euclidean nearest neighbor search problem?", "Euclidean nearest neighbor search problem?", "nearest neighbor search problem?", "neighbor search problem?", "search problem?", "problem?"], "tokens": ["How", "can", "we", "map", "the", "MIPS", "problem", "to", "a", "Euclidean", "nearest", "neighbor", "search", "problem", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::193693767", "fields": {"term": "What is the purpose of the convert_embeddings_to_vespa_format.py script?", "namespace": "open-p", "hash": 193693767, "terms": ["What is the purpose of the convert_embeddings_to_vespa_format.py script?", "is the purpose of the convert_embeddings_to_vespa_format.py script?", "the purpose of the convert_embeddings_to_vespa_format.py script?", "purpose of the convert_embeddings_to_vespa_format.py script?", "of the convert_embeddings_to_vespa_format.py script?", "the convert_embeddings_to_vespa_format.py script?", "convert_embeddings_to_vespa_format.py script?", "script?"], "tokens": ["What", "is", "the", "purpose", "of", "the", "convert_embeddings_to_vespa_format", "py", "script", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2039922680", "fields": {"term": "What is the output of the convert_embeddings_to_vespa_format.py script?", "namespace": "open-p", "hash": -2039922680, "terms": ["What is the output of the convert_embeddings_to_vespa_format.py script?", "is the output of the convert_embeddings_to_vespa_format.py script?", "the output of the convert_embeddings_to_vespa_format.py script?", "output of the convert_embeddings_to_vespa_format.py script?", "of the convert_embeddings_to_vespa_format.py script?", "the convert_embeddings_to_vespa_format.py script?", "convert_embeddings_to_vespa_format.py script?", "script?"], "tokens": ["What", "is", "the", "output", "of", "the", "convert_embeddings_to_vespa_format", "py", "script", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-516808662", "fields": {"term": "What is discussed in the Nearest Neighbor Search page?", "namespace": "open-p", "hash": -516808662, "terms": ["What is discussed in the Nearest Neighbor Search page?", "is discussed in the Nearest Neighbor Search page?", "discussed in the Nearest Neighbor Search page?", "in the Nearest Neighbor Search page?", "the Nearest Neighbor Search page?", "Nearest Neighbor Search page?", "Neighbor Search page?", "Search page?", "page?"], "tokens": ["What", "is", "discussed", "in", "the", "Nearest", "Neighbor", "Search", "page", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1831755929", "fields": {"term": "What is the next step after creating a feed suitable for Vespa?", "namespace": "open-p", "hash": 1831755929, "terms": ["What is the next step after creating a feed suitable for Vespa?", "is the next step after creating a feed suitable for Vespa?", "the next step after creating a feed suitable for Vespa?", "next step after creating a feed suitable for Vespa?", "step after creating a feed suitable for Vespa?", "after creating a feed suitable for Vespa?", "creating a feed suitable for Vespa?", "a feed suitable for Vespa?", "feed suitable for Vespa?", "suitable for Vespa?", "for Vespa?", "Vespa?"], "tokens": ["What", "is", "the", "next", "step", "after", "creating", "a", "feed", "suitable", "for", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1525782702", "fields": {"term": "How are news items recommended?", "namespace": "open-p", "hash": -1525782702, "terms": ["How are news items recommended?", "are news items recommended?", "news items recommended?", "items recommended?", "recommended?"], "tokens": ["How", "are", "news", "items", "recommended", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1147160060", "fields": {"term": "What are user and document embeddings?", "namespace": "open-p", "hash": 1147160060, "terms": ["What are user and document embeddings?", "are user and document embeddings?", "user and document embeddings?", "and document embeddings?", "document embeddings?", "embeddings?"], "tokens": ["What", "are", "user", "and", "document", "embeddings", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1368142385", "fields": {"term": "What happens in the next part of the tutorial?", "namespace": "open-p", "hash": 1368142385, "terms": ["What happens in the next part of the tutorial?", "happens in the next part of the tutorial?", "in the next part of the tutorial?", "the next part of the tutorial?", "next part of the tutorial?", "part of the tutorial?", "of the tutorial?", "the tutorial?", "tutorial?"], "tokens": ["What", "happens", "in", "the", "next", "part", "of", "the", "tutorial", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1183758504", "fields": {"term": "What are the parts of the tutorial?", "namespace": "open-p", "hash": 1183758504, "terms": ["What are the parts of the tutorial?", "are the parts of the tutorial?", "the parts of the tutorial?", "parts of the tutorial?", "of the tutorial?", "the tutorial?", "tutorial?"], "tokens": ["What", "are", "the", "parts", "of", "the", "tutorial", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1912989358", "fields": {"term": "What is covered in part 5?", "namespace": "open-p", "hash": -1912989358, "terms": ["What is covered in part 5?", "is covered in part 5?", "covered in part 5?", "in part 5?", "part 5?", "5?"], "tokens": ["What", "is", "covered", "in", "part", "5", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1854812964", "fields": {"term": "Where can I find the final state of the tutorial app?", "namespace": "open-p", "hash": -1854812964, "terms": ["Where can I find the final state of the tutorial app?", "can I find the final state of the tutorial app?", "I find the final state of the tutorial app?", "find the final state of the tutorial app?", "the final state of the tutorial app?", "final state of the tutorial app?", "state of the tutorial app?", "of the tutorial app?", "the tutorial app?", "tutorial app?", "app?"], "tokens": ["Where", "can", "I", "find", "the", "final", "state", "of", "the", "tutorial", "app", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::10482019", "fields": {"term": "What is the purpose of 'embedding' field?", "namespace": "open-p", "hash": 10482019, "terms": ["What is the purpose of 'embedding' field?", "is the purpose of 'embedding' field?", "the purpose of 'embedding' field?", "purpose of 'embedding' field?", "of 'embedding' field?", "'embedding' field?", "field?"], "tokens": ["What", "is", "the", "purpose", "of", "'", "embedding", "'", "field", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-728810628", "fields": {"term": "How to feed user and news embeddings?", "namespace": "open-p", "hash": -728810628, "terms": ["How to feed user and news embeddings?", "to feed user and news embeddings?", "feed user and news embeddings?", "user and news embeddings?", "and news embeddings?", "news embeddings?", "embeddings?"], "tokens": ["How", "to", "feed", "user", "and", "news", "embeddings", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::721969032", "fields": {"term": "What is a query profile type?", "namespace": "open-p", "hash": 721969032, "terms": ["What is a query profile type?", "is a query profile type?", "a query profile type?", "query profile type?", "profile type?", "type?"], "tokens": ["What", "is", "a", "query", "profile", "type", "?"], "corpus_count": 3, "document_count": 3}}, -{"put": "id:term:term::803376718", "fields": {"term": "How do you set up query profiles?", "namespace": "open-p", "hash": 803376718, "terms": ["How do you set up query profiles?", "do you set up query profiles?", "you set up query profiles?", "set up query profiles?", "up query profiles?", "query profiles?", "profiles?"], "tokens": ["How", "do", "you", "set", "up", "query", "profiles", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-317954561", "fields": {"term": "What is the expected tensor type?", "namespace": "open-p", "hash": -317954561, "terms": ["What is the expected tensor type?", "is the expected tensor type?", "the expected tensor type?", "expected tensor type?", "tensor type?", "type?"], "tokens": ["What", "is", "the", "expected", "tensor", "type", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::667386106", "fields": {"term": "How do you deploy query profile updates?", "namespace": "open-p", "hash": 667386106, "terms": ["How do you deploy query profile updates?", "do you deploy query profile updates?", "you deploy query profile updates?", "deploy query profile updates?", "query profile updates?", "profile updates?", "updates?"], "tokens": ["How", "do", "you", "deploy", "query", "profile", "updates", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1062543806", "fields": {"term": "What is the nearestNeighbor search operator?", "namespace": "open-p", "hash": -1062543806, "terms": ["What is the nearestNeighbor search operator?", "is the nearestNeighbor search operator?", "the nearestNeighbor search operator?", "nearestNeighbor search operator?", "search operator?", "operator?"], "tokens": ["What", "is", "the", "nearestNeighbor", "search", "operator", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-924726402", "fields": {"term": "What is user_search.py?", "namespace": "open-p", "hash": -924726402, "terms": ["What is user_search.py?", "is user_search.py?", "user_search.py?"], "tokens": ["What", "is", "user_search", "py", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1784246971", "fields": {"term": "What is evaluate.py used for?", "namespace": "open-p", "hash": 1784246971, "terms": ["What is evaluate.py used for?", "is evaluate.py used for?", "evaluate.py used for?", "used for?", "for?"], "tokens": ["What", "is", "evaluate", "py", "used", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-427008496", "fields": {"term": "What is the output of evaluate.py?", "namespace": "open-p", "hash": -427008496, "terms": ["What is the output of evaluate.py?", "is the output of evaluate.py?", "the output of evaluate.py?", "output of evaluate.py?", "of evaluate.py?", "evaluate.py?"], "tokens": ["What", "is", "the", "output", "of", "evaluate", "py", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::1252116154", "fields": {"term": "What is approximate nearest neighbor?", "namespace": "open-p", "hash": 1252116154, "terms": ["What is approximate nearest neighbor?", "is approximate nearest neighbor?", "approximate nearest neighbor?", "nearest neighbor?", "neighbor?"], "tokens": ["What", "is", "approximate", "nearest", "neighbor", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-547499767", "fields": {"term": "What is the trade-off for ANN?", "namespace": "open-p", "hash": -547499767, "terms": ["What is the trade-off for ANN?", "is the trade-off for ANN?", "the trade-off for ANN?", "trade-off for ANN?", "for ANN?", "ANN?"], "tokens": ["What", "is", "the", "trade", "off", "for", "ANN", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::107561329", "fields": {"term": "What is the gain in efficiency?", "namespace": "open-p", "hash": 107561329, "terms": ["What is the gain in efficiency?", "is the gain in efficiency?", "the gain in efficiency?", "gain in efficiency?", "in efficiency?", "efficiency?"], "tokens": ["What", "is", "the", "gain", "in", "efficiency", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::424110727", "fields": {"term": "What is unique about Vespa's ANN implementation?", "namespace": "open-p", "hash": 424110727, "terms": ["What is unique about Vespa's ANN implementation?", "is unique about Vespa's ANN implementation?", "unique about Vespa's ANN implementation?", "about Vespa's ANN implementation?", "Vespa's ANN implementation?", "ANN implementation?", "implementation?"], "tokens": ["What", "is", "unique", "about", "Vespa", "'", "s", "ANN", "implementation", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-722409172", "fields": {"term": "How can query filters be used?", "namespace": "open-p", "hash": -722409172, "terms": ["How can query filters be used?", "can query filters be used?", "query filters be used?", "filters be used?", "be used?", "used?"], "tokens": ["How", "can", "query", "filters", "be", "used", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1037084275", "fields": {"term": "What are the tradeoffs of pre- and post-filtering?", "namespace": "open-p", "hash": -1037084275, "terms": ["What are the tradeoffs of pre- and post-filtering?", "are the tradeoffs of pre- and post-filtering?", "the tradeoffs of pre- and post-filtering?", "tradeoffs of pre- and post-filtering?", "of pre- and post-filtering?", "pre- and post-filtering?", "and post-filtering?", "post-filtering?"], "tokens": ["What", "are", "the", "tradeoffs", "of", "pre", "and", "post", "filtering", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1284695842", "fields": {"term": "How does Vespa handle strict filters?", "namespace": "open-p", "hash": 1284695842, "terms": ["How does Vespa handle strict filters?", "does Vespa handle strict filters?", "Vespa handle strict filters?", "handle strict filters?", "strict filters?", "filters?"], "tokens": ["How", "does", "Vespa", "handle", "strict", "filters", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-760424379", "fields": {"term": "What is the efficiency of Vespa's fallback search?", "namespace": "open-p", "hash": -760424379, "terms": ["What is the efficiency of Vespa's fallback search?", "is the efficiency of Vespa's fallback search?", "the efficiency of Vespa's fallback search?", "efficiency of Vespa's fallback search?", "of Vespa's fallback search?", "Vespa's fallback search?", "fallback search?", "search?"], "tokens": ["What", "is", "the", "efficiency", "of", "Vespa", "'", "s", "fallback", "search", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::791864626", "fields": {"term": "What is the recommendation system?", "namespace": "open-p", "hash": 791864626, "terms": ["What is the recommendation system?", "is the recommendation system?", "the recommendation system?", "recommendation system?", "system?"], "tokens": ["What", "is", "the", "recommendation", "system", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::39320997", "fields": {"term": "What invokes Searchers?", "namespace": "open-p", "hash": 39320997, "terms": ["What invokes Searchers?", "invokes Searchers?", "Searchers?"], "tokens": ["What", "invokes", "Searchers", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1340356055", "fields": {"term": "Can developers create their own Searchers?", "namespace": "open-p", "hash": 1340356055, "terms": ["Can developers create their own Searchers?", "developers create their own Searchers?", "create their own Searchers?", "their own Searchers?", "own Searchers?", "Searchers?"], "tokens": ["Can", "developers", "create", "their", "own", "Searchers", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::817258856", "fields": {"term": "What is the purpose of the user_search.py script?", "namespace": "open-p", "hash": 817258856, "terms": ["What is the purpose of the user_search.py script?", "is the purpose of the user_search.py script?", "the purpose of the user_search.py script?", "purpose of the user_search.py script?", "of the user_search.py script?", "the user_search.py script?", "user_search.py script?", "script?"], "tokens": ["What", "is", "the", "purpose", "of", "the", "user_search", "py", "script", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-235145908", "fields": {"term": "What is the UserProfileSearcher?", "namespace": "open-p", "hash": -235145908, "terms": ["What is the UserProfileSearcher?", "is the UserProfileSearcher?", "the UserProfileSearcher?", "UserProfileSearcher?"], "tokens": ["What", "is", "the", "UserProfileSearcher", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-968643841", "fields": {"term": "What does the UserProfileSearcher do?", "namespace": "open-p", "hash": -968643841, "terms": ["What does the UserProfileSearcher do?", "does the UserProfileSearcher do?", "the UserProfileSearcher do?", "UserProfileSearcher do?", "do?"], "tokens": ["What", "does", "the", "UserProfileSearcher", "do", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1508819958", "fields": {"term": "What does the UserProfileSearcher extend?", "namespace": "open-p", "hash": 1508819958, "terms": ["What does the UserProfileSearcher extend?", "does the UserProfileSearcher extend?", "the UserProfileSearcher extend?", "UserProfileSearcher extend?", "extend?"], "tokens": ["What", "does", "the", "UserProfileSearcher", "extend", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1618491828", "fields": {"term": "What is NearestNeighborItem?", "namespace": "open-p", "hash": 1618491828, "terms": ["What is NearestNeighborItem?", "is NearestNeighborItem?", "NearestNeighborItem?"], "tokens": ["What", "is", "NearestNeighborItem", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-69177868", "fields": {"term": "Where is the user_id retrieved from?", "namespace": "open-p", "hash": -69177868, "terms": ["Where is the user_id retrieved from?", "is the user_id retrieved from?", "the user_id retrieved from?", "user_id retrieved from?", "retrieved from?", "from?"], "tokens": ["Where", "is", "the", "user_id", "retrieved", "from", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::31072801", "fields": {"term": "What happens when no user_id is given?", "namespace": "open-p", "hash": 31072801, "terms": ["What happens when no user_id is given?", "happens when no user_id is given?", "when no user_id is given?", "no user_id is given?", "user_id is given?", "is given?", "given?"], "tokens": ["What", "happens", "when", "no", "user_id", "is", "given", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1246993244", "fields": {"term": "How is Java code added to Vespa?", "namespace": "open-p", "hash": -1246993244, "terms": ["How is Java code added to Vespa?", "is Java code added to Vespa?", "Java code added to Vespa?", "code added to Vespa?", "added to Vespa?", "to Vespa?", "Vespa?"], "tokens": ["How", "is", "Java", "code", "added", "to", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1923852445", "fields": {"term": "What command returns top news articles for user U33527?", "namespace": "open-p", "hash": -1923852445, "terms": ["What command returns top news articles for user U33527?", "command returns top news articles for user U33527?", "returns top news articles for user U33527?", "top news articles for user U33527?", "news articles for user U33527?", "articles for user U33527?", "for user U33527?", "user U33527?", "U33527?"], "tokens": ["What", "command", "returns", "top", "news", "articles", "for", "user", "U33527", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1099873392", "fields": {"term": "What should the `trace.level` be for searcher invocation?", "namespace": "open-p", "hash": -1099873392, "terms": ["What should the `trace.level` be for searcher invocation?", "should the `trace.level` be for searcher invocation?", "the `trace.level` be for searcher invocation?", "`trace.level` be for searcher invocation?", "be for searcher invocation?", "for searcher invocation?", "searcher invocation?", "invocation?"], "tokens": ["What", "should", "the", "`", "trace", "level", "`", "be", "for", "searcher", "invocation", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1842196420", "fields": {"term": "What is the default chain for recommended news search?", "namespace": "open-p", "hash": 1842196420, "terms": ["What is the default chain for recommended news search?", "is the default chain for recommended news search?", "the default chain for recommended news search?", "default chain for recommended news search?", "chain for recommended news search?", "for recommended news search?", "recommended news search?", "news search?", "search?"], "tokens": ["What", "is", "the", "default", "chain", "for", "recommended", "news", "search", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-759114190", "fields": {"term": "What is the architecture overview?", "namespace": "open-p", "hash": -759114190, "terms": ["What is the architecture overview?", "is the architecture overview?", "the architecture overview?", "architecture overview?", "overview?"], "tokens": ["What", "is", "the", "architecture", "overview", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-985537673", "fields": {"term": "What is the difference between Document Processors and Searchers?", "namespace": "open-p", "hash": -985537673, "terms": ["What is the difference between Document Processors and Searchers?", "is the difference between Document Processors and Searchers?", "the difference between Document Processors and Searchers?", "difference between Document Processors and Searchers?", "between Document Processors and Searchers?", "Document Processors and Searchers?", "Processors and Searchers?", "and Searchers?", "Searchers?"], "tokens": ["What", "is", "the", "difference", "between", "Document", "Processors", "and", "Searchers", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-380150837", "fields": {"term": "Can Vespa support custom Document Processors?", "namespace": "open-p", "hash": -380150837, "terms": ["Can Vespa support custom Document Processors?", "Vespa support custom Document Processors?", "support custom Document Processors?", "custom Document Processors?", "Document Processors?", "Processors?"], "tokens": ["Can", "Vespa", "support", "custom", "Document", "Processors", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1799653362", "fields": {"term": "Where can I find more information about custom Document Processors?", "namespace": "open-p", "hash": 1799653362, "terms": ["Where can I find more information about custom Document Processors?", "can I find more information about custom Document Processors?", "I find more information about custom Document Processors?", "find more information about custom Document Processors?", "more information about custom Document Processors?", "information about custom Document Processors?", "about custom Document Processors?", "custom Document Processors?", "Document Processors?", "Processors?"], "tokens": ["Where", "can", "I", "find", "more", "information", "about", "custom", "Document", "Processors", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::658296574", "fields": {"term": "What happens to a document in Vespa before storage and indexing?", "namespace": "open-p", "hash": 658296574, "terms": ["What happens to a document in Vespa before storage and indexing?", "happens to a document in Vespa before storage and indexing?", "to a document in Vespa before storage and indexing?", "a document in Vespa before storage and indexing?", "document in Vespa before storage and indexing?", "in Vespa before storage and indexing?", "Vespa before storage and indexing?", "before storage and indexing?", "storage and indexing?", "and indexing?", "indexing?"], "tokens": ["What", "happens", "to", "a", "document", "in", "Vespa", "before", "storage", "and", "indexing", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-862302976", "fields": {"term": "What is a blending Searcher?", "namespace": "open-p", "hash": -862302976, "terms": ["What is a blending Searcher?", "is a blending Searcher?", "a blending Searcher?", "blending Searcher?", "Searcher?"], "tokens": ["What", "is", "a", "blending", "Searcher", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1437736515", "fields": {"term": "What is reciprocal rank fusion?", "namespace": "open-p", "hash": 1437736515, "terms": ["What is reciprocal rank fusion?", "is reciprocal rank fusion?", "reciprocal rank fusion?", "rank fusion?", "fusion?"], "tokens": ["What", "is", "reciprocal", "rank", "fusion", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::582498707", "fields": {"term": "What is category CTR?", "namespace": "open-p", "hash": 582498707, "terms": ["What is category CTR?", "is category CTR?", "category CTR?", "CTR?"], "tokens": ["What", "is", "category", "CTR", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::321829441", "fields": {"term": "What is parent-child in Vespa?", "namespace": "open-p", "hash": 321829441, "terms": ["What is parent-child in Vespa?", "is parent-child in Vespa?", "parent-child in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["What", "is", "parent", "child", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1973133993", "fields": {"term": "What is the final state of the tutorial?", "namespace": "open-p", "hash": 1973133993, "terms": ["What is the final state of the tutorial?", "is the final state of the tutorial?", "the final state of the tutorial?", "final state of the tutorial?", "state of the tutorial?", "of the tutorial?", "the tutorial?", "tutorial?"], "tokens": ["What", "is", "the", "final", "state", "of", "the", "tutorial", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1024653875", "fields": {"term": "What is the efficient way to update CTR?", "namespace": "open-p", "hash": 1024653875, "terms": ["What is the efficient way to update CTR?", "is the efficient way to update CTR?", "the efficient way to update CTR?", "efficient way to update CTR?", "way to update CTR?", "to update CTR?", "update CTR?", "CTR?"], "tokens": ["What", "is", "the", "efficient", "way", "to", "update", "CTR", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2035084762", "fields": {"term": "What is the recommendation based on?", "namespace": "open-p", "hash": -2035084762, "terms": ["What is the recommendation based on?", "is the recommendation based on?", "the recommendation based on?", "recommendation based on?", "based on?", "on?"], "tokens": ["What", "is", "the", "recommendation", "based", "on", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1563399535", "fields": {"term": "What are parent-child relationships?", "namespace": "open-p", "hash": 1563399535, "terms": ["What are parent-child relationships?", "are parent-child relationships?", "parent-child relationships?", "relationships?"], "tokens": ["What", "are", "parent", "child", "relationships", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::2052846589", "fields": {"term": "How do parent-child relationships work?", "namespace": "open-p", "hash": 2052846589, "terms": ["How do parent-child relationships work?", "do parent-child relationships work?", "parent-child relationships work?", "relationships work?", "work?"], "tokens": ["How", "do", "parent", "child", "relationships", "work", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1902891235", "fields": {"term": "What is the benefit of parent-child relationships?", "namespace": "open-p", "hash": 1902891235, "terms": ["What is the benefit of parent-child relationships?", "is the benefit of parent-child relationships?", "the benefit of parent-child relationships?", "benefit of parent-child relationships?", "of parent-child relationships?", "parent-child relationships?", "relationships?"], "tokens": ["What", "is", "the", "benefit", "of", "parent", "child", "relationships", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1777867331", "fields": {"term": "What is UserProfileSearcher?", "namespace": "open-p", "hash": -1777867331, "terms": ["What is UserProfileSearcher?", "is UserProfileSearcher?", "UserProfileSearcher?"], "tokens": ["What", "is", "UserProfileSearcher", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1272698767", "fields": {"term": "Where can I find more information on parent-child relationships?", "namespace": "open-p", "hash": -1272698767, "terms": ["Where can I find more information on parent-child relationships?", "can I find more information on parent-child relationships?", "I find more information on parent-child relationships?", "find more information on parent-child relationships?", "more information on parent-child relationships?", "information on parent-child relationships?", "on parent-child relationships?", "parent-child relationships?", "relationships?"], "tokens": ["Where", "can", "I", "find", "more", "information", "on", "parent", "child", "relationships", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-706221072", "fields": {"term": "What is global CTR value?", "namespace": "open-p", "hash": -706221072, "terms": ["What is global CTR value?", "is global CTR value?", "global CTR value?", "CTR value?", "value?"], "tokens": ["What", "is", "global", "CTR", "value", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::686331908", "fields": {"term": "What is category_ctr document type?", "namespace": "open-p", "hash": 686331908, "terms": ["What is category_ctr document type?", "is category_ctr document type?", "category_ctr document type?", "document type?", "type?"], "tokens": ["What", "is", "category_ctr", "document", "type", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1005167693", "fields": {"term": "What is category_ctr schema?", "namespace": "open-p", "hash": 1005167693, "terms": ["What is category_ctr schema?", "is category_ctr schema?", "category_ctr schema?", "schema?"], "tokens": ["What", "is", "category_ctr", "schema", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-828162414", "fields": {"term": "What is tensor modify?", "namespace": "open-p", "hash": -828162414, "terms": ["What is tensor modify?", "is tensor modify?", "tensor modify?", "modify?"], "tokens": ["What", "is", "tensor", "modify", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1582751637", "fields": {"term": "What is sparse dimension?", "namespace": "open-p", "hash": 1582751637, "terms": ["What is sparse dimension?", "is sparse dimension?", "sparse dimension?", "dimension?"], "tokens": ["What", "is", "sparse", "dimension", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1167569474", "fields": {"term": "What are the limits of parent-child relationship?", "namespace": "open-p", "hash": 1167569474, "terms": ["What are the limits of parent-child relationship?", "are the limits of parent-child relationship?", "the limits of parent-child relationship?", "limits of parent-child relationship?", "of parent-child relationship?", "parent-child relationship?", "relationship?"], "tokens": ["What", "are", "the", "limits", "of", "parent", "child", "relationship", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1642317247", "fields": {"term": "What is `category_ctr`?", "namespace": "open-p", "hash": 1642317247, "terms": ["What is `category_ctr`?", "is `category_ctr`?", "`category_ctr`?"], "tokens": ["What", "is", "`", "category_ctr", "`?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::156093428", "fields": {"term": "How do we use `category_ctr`?", "namespace": "open-p", "hash": 156093428, "terms": ["How do we use `category_ctr`?", "do we use `category_ctr`?", "we use `category_ctr`?", "use `category_ctr`?", "`category_ctr`?"], "tokens": ["How", "do", "we", "use", "`", "category_ctr", "`?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1206526125", "fields": {"term": "How does the import statement work?", "namespace": "open-p", "hash": -1206526125, "terms": ["How does the import statement work?", "does the import statement work?", "the import statement work?", "import statement work?", "statement work?", "work?"], "tokens": ["How", "does", "the", "import", "statement", "work", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1782860921", "fields": {"term": "What is a tensor expression?", "namespace": "open-p", "hash": 1782860921, "terms": ["What is a tensor expression?", "is a tensor expression?", "a tensor expression?", "tensor expression?", "expression?"], "tokens": ["What", "is", "a", "tensor", "expression", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::-1603022887", "fields": {"term": "How do you calculate global CTR score?", "namespace": "open-p", "hash": -1603022887, "terms": ["How do you calculate global CTR score?", "do you calculate global CTR score?", "you calculate global CTR score?", "calculate global CTR score?", "global CTR score?", "CTR score?", "score?"], "tokens": ["How", "do", "you", "calculate", "global", "CTR", "score", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1662772550", "fields": {"term": "What is a sparse tensor?", "namespace": "open-p", "hash": 1662772550, "terms": ["What is a sparse tensor?", "is a sparse tensor?", "a sparse tensor?", "sparse tensor?", "tensor?"], "tokens": ["What", "is", "a", "sparse", "tensor", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1657127785", "fields": {"term": "What is the conceptually \"inner join\" in tensor multiplication?", "namespace": "open-p", "hash": -1657127785, "terms": ["What is the conceptually \"inner join\" in tensor multiplication?", "is the conceptually \"inner join\" in tensor multiplication?", "the conceptually \"inner join\" in tensor multiplication?", "conceptually \"inner join\" in tensor multiplication?", "\"inner join\" in tensor multiplication?", "join\" in tensor multiplication?", "in tensor multiplication?", "tensor multiplication?", "multiplication?"], "tokens": ["What", "is", "the", "conceptually", "\"", "inner", "join", "\"", "in", "tensor", "multiplication", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1111745450", "fields": {"term": "What is create_category_ctrs.py?", "namespace": "open-p", "hash": 1111745450, "terms": ["What is create_category_ctrs.py?", "is create_category_ctrs.py?", "create_category_ctrs.py?"], "tokens": ["What", "is", "create_category_ctrs", "py", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::794859125", "fields": {"term": "What does create_category_ctrs.py do?", "namespace": "open-p", "hash": 794859125, "terms": ["What does create_category_ctrs.py do?", "does create_category_ctrs.py do?", "create_category_ctrs.py do?", "do?"], "tokens": ["What", "does", "create_category_ctrs", "py", "do", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-645463476", "fields": {"term": "What is the purpose of global_category_ctr.json?", "namespace": "open-p", "hash": -645463476, "terms": ["What is the purpose of global_category_ctr.json?", "is the purpose of global_category_ctr.json?", "the purpose of global_category_ctr.json?", "purpose of global_category_ctr.json?", "of global_category_ctr.json?", "global_category_ctr.json?"], "tokens": ["What", "is", "the", "purpose", "of", "global_category_ctr", "json", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1285976901", "fields": {"term": "What is the purpose of news_category_ctr_update.json?", "namespace": "open-p", "hash": 1285976901, "terms": ["What is the purpose of news_category_ctr_update.json?", "is the purpose of news_category_ctr_update.json?", "the purpose of news_category_ctr_update.json?", "purpose of news_category_ctr_update.json?", "of news_category_ctr_update.json?", "news_category_ctr_update.json?"], "tokens": ["What", "is", "the", "purpose", "of", "news_category_ctr_update", "json", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1751855539", "fields": {"term": "Which file needs to be fed to Vespa first?", "namespace": "open-p", "hash": 1751855539, "terms": ["Which file needs to be fed to Vespa first?", "file needs to be fed to Vespa first?", "needs to be fed to Vespa first?", "to be fed to Vespa first?", "be fed to Vespa first?", "fed to Vespa first?", "to Vespa first?", "Vespa first?", "first?"], "tokens": ["Which", "file", "needs", "to", "be", "fed", "to", "Vespa", "first", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1356682817", "fields": {"term": "What is the command to create feed files?", "namespace": "open-p", "hash": 1356682817, "terms": ["What is the command to create feed files?", "is the command to create feed files?", "the command to create feed files?", "command to create feed files?", "to create feed files?", "create feed files?", "feed files?", "files?"], "tokens": ["What", "is", "the", "command", "to", "create", "feed", "files", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1506752041", "fields": {"term": "What is the category_ctr function?", "namespace": "open-p", "hash": 1506752041, "terms": ["What is the category_ctr function?", "is the category_ctr function?", "the category_ctr function?", "category_ctr function?", "function?"], "tokens": ["What", "is", "the", "category_ctr", "function", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1178425597", "fields": {"term": "What is the purpose of global parameters?", "namespace": "open-p", "hash": -1178425597, "terms": ["What is the purpose of global parameters?", "is the purpose of global parameters?", "the purpose of global parameters?", "purpose of global parameters?", "of global parameters?", "global parameters?", "parameters?"], "tokens": ["What", "is", "the", "purpose", "of", "global", "parameters", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1949441441", "fields": {"term": "How can additional features affect the user experience?", "namespace": "open-p", "hash": 1949441441, "terms": ["How can additional features affect the user experience?", "can additional features affect the user experience?", "additional features affect the user experience?", "features affect the user experience?", "affect the user experience?", "the user experience?", "user experience?", "experience?"], "tokens": ["How", "can", "additional", "features", "affect", "the", "user", "experience", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::716743794", "fields": {"term": "What is a parent-child relationship in Vespa?", "namespace": "open-p", "hash": 716743794, "terms": ["What is a parent-child relationship in Vespa?", "is a parent-child relationship in Vespa?", "a parent-child relationship in Vespa?", "parent-child relationship in Vespa?", "relationship in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["What", "is", "a", "parent", "child", "relationship", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1240437394", "fields": {"term": "What is tensor-based ranking in Vespa?", "namespace": "open-p", "hash": -1240437394, "terms": ["What is tensor-based ranking in Vespa?", "is tensor-based ranking in Vespa?", "tensor-based ranking in Vespa?", "ranking in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["What", "is", "tensor", "based", "ranking", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1474823858", "fields": {"term": "What is the global CTR feature in Vespa?", "namespace": "open-p", "hash": -1474823858, "terms": ["What is the global CTR feature in Vespa?", "is the global CTR feature in Vespa?", "the global CTR feature in Vespa?", "global CTR feature in Vespa?", "CTR feature in Vespa?", "feature in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["What", "is", "the", "global", "CTR", "feature", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1390312387", "fields": {"term": "Where can I find a real-world use case of parent-child tensors in Vespa?", "namespace": "open-p", "hash": -1390312387, "terms": ["Where can I find a real-world use case of parent-child tensors in Vespa?", "can I find a real-world use case of parent-child tensors in Vespa?", "I find a real-world use case of parent-child tensors in Vespa?", "find a real-world use case of parent-child tensors in Vespa?", "a real-world use case of parent-child tensors in Vespa?", "real-world use case of parent-child tensors in Vespa?", "use case of parent-child tensors in Vespa?", "case of parent-child tensors in Vespa?", "of parent-child tensors in Vespa?", "parent-child tensors in Vespa?", "tensors in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["Where", "can", "I", "find", "a", "real", "world", "use", "case", "of", "parent", "child", "tensors", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1732719398", "fields": {"term": "What is Vespa composed of?", "namespace": "open-p", "hash": -1732719398, "terms": ["What is Vespa composed of?", "is Vespa composed of?", "Vespa composed of?", "composed of?", "of?"], "tokens": ["What", "is", "Vespa", "composed", "of", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2139402647", "fields": {"term": "What kind of hardware can Vespa run on?", "namespace": "open-p", "hash": -2139402647, "terms": ["What kind of hardware can Vespa run on?", "kind of hardware can Vespa run on?", "of hardware can Vespa run on?", "hardware can Vespa run on?", "can Vespa run on?", "Vespa run on?", "run on?", "on?"], "tokens": ["What", "kind", "of", "hardware", "can", "Vespa", "run", "on", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1562928790", "fields": {"term": "How does Vespa achieve flexibility?", "namespace": "open-p", "hash": 1562928790, "terms": ["How does Vespa achieve flexibility?", "does Vespa achieve flexibility?", "Vespa achieve flexibility?", "achieve flexibility?", "flexibility?"], "tokens": ["How", "does", "Vespa", "achieve", "flexibility", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::234538677", "fields": {"term": "What is a node?", "namespace": "open-p", "hash": 234538677, "terms": ["What is a node?", "is a node?", "a node?", "node?"], "tokens": ["What", "is", "a", "node", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1712585900", "fields": {"term": "What is network connectivity?", "namespace": "open-p", "hash": 1712585900, "terms": ["What is network connectivity?", "is network connectivity?", "network connectivity?", "connectivity?"], "tokens": ["What", "is", "network", "connectivity", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1682693318", "fields": {"term": "What is the purpose of minimum memory settings?", "namespace": "open-p", "hash": 1682693318, "terms": ["What is the purpose of minimum memory settings?", "is the purpose of minimum memory settings?", "the purpose of minimum memory settings?", "purpose of minimum memory settings?", "of minimum memory settings?", "minimum memory settings?", "memory settings?", "settings?"], "tokens": ["What", "is", "the", "purpose", "of", "minimum", "memory", "settings", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::486711367", "fields": {"term": "What is the recommended memory setting?", "namespace": "open-p", "hash": 486711367, "terms": ["What is the recommended memory setting?", "is the recommended memory setting?", "the recommended memory setting?", "recommended memory setting?", "memory setting?", "setting?"], "tokens": ["What", "is", "the", "recommended", "memory", "setting", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2053370141", "fields": {"term": "How do you find optimal node configuration?", "namespace": "open-p", "hash": 2053370141, "terms": ["How do you find optimal node configuration?", "do you find optimal node configuration?", "you find optimal node configuration?", "find optimal node configuration?", "optimal node configuration?", "node configuration?", "configuration?"], "tokens": ["How", "do", "you", "find", "optimal", "node", "configuration", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-891250161", "fields": {"term": "What is covered in the performance guides?", "namespace": "open-p", "hash": -891250161, "terms": ["What is covered in the performance guides?", "is covered in the performance guides?", "covered in the performance guides?", "in the performance guides?", "the performance guides?", "performance guides?", "guides?"], "tokens": ["What", "is", "covered", "in", "the", "performance", "guides", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1827883976", "fields": {"term": "How does a node identify itself?", "namespace": "open-p", "hash": -1827883976, "terms": ["How does a node identify itself?", "does a node identify itself?", "a node identify itself?", "node identify itself?", "identify itself?", "itself?"], "tokens": ["How", "does", "a", "node", "identify", "itself", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::592261843", "fields": {"term": "What is required for a node to get its configuration?", "namespace": "open-p", "hash": 592261843, "terms": ["What is required for a node to get its configuration?", "is required for a node to get its configuration?", "required for a node to get its configuration?", "for a node to get its configuration?", "a node to get its configuration?", "node to get its configuration?", "to get its configuration?", "get its configuration?", "its configuration?", "configuration?"], "tokens": ["What", "is", "required", "for", "a", "node", "to", "get", "its", "configuration", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-247540141", "fields": {"term": "What hostname is used for identifying a node?", "namespace": "open-p", "hash": -247540141, "terms": ["What hostname is used for identifying a node?", "hostname is used for identifying a node?", "is used for identifying a node?", "used for identifying a node?", "for identifying a node?", "identifying a node?", "a node?", "node?"], "tokens": ["What", "hostname", "is", "used", "for", "identifying", "a", "node", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::742529412", "fields": {"term": "What is the requirement for the hostname configured in hosts.xml?", "namespace": "open-p", "hash": 742529412, "terms": ["What is the requirement for the hostname configured in hosts.xml?", "is the requirement for the hostname configured in hosts.xml?", "the requirement for the hostname configured in hosts.xml?", "requirement for the hostname configured in hosts.xml?", "for the hostname configured in hosts.xml?", "the hostname configured in hosts.xml?", "hostname configured in hosts.xml?", "configured in hosts.xml?", "in hosts.xml?", "hosts.xml?"], "tokens": ["What", "is", "the", "requirement", "for", "the", "hostname", "configured", "in", "hosts", "xml", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1854468760", "fields": {"term": "What is recommended when using IP addresses without DNS names?", "namespace": "open-p", "hash": 1854468760, "terms": ["What is recommended when using IP addresses without DNS names?", "is recommended when using IP addresses without DNS names?", "recommended when using IP addresses without DNS names?", "when using IP addresses without DNS names?", "using IP addresses without DNS names?", "IP addresses without DNS names?", "addresses without DNS names?", "without DNS names?", "DNS names?", "names?"], "tokens": ["What", "is", "recommended", "when", "using", "IP", "addresses", "without", "DNS", "names", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1450258675", "fields": {"term": "What must a node know about its hostname?", "namespace": "open-p", "hash": 1450258675, "terms": ["What must a node know about its hostname?", "must a node know about its hostname?", "a node know about its hostname?", "node know about its hostname?", "know about its hostname?", "about its hostname?", "its hostname?", "hostname?"], "tokens": ["What", "must", "a", "node", "know", "about", "its", "hostname", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::133636830", "fields": {"term": "What command should usually be run to achieve agreement about a hostname?", "namespace": "open-p", "hash": 133636830, "terms": ["What command should usually be run to achieve agreement about a hostname?", "command should usually be run to achieve agreement about a hostname?", "should usually be run to achieve agreement about a hostname?", "usually be run to achieve agreement about a hostname?", "be run to achieve agreement about a hostname?", "run to achieve agreement about a hostname?", "to achieve agreement about a hostname?", "achieve agreement about a hostname?", "agreement about a hostname?", "about a hostname?", "a hostname?", "hostname?"], "tokens": ["What", "command", "should", "usually", "be", "run", "to", "achieve", "agreement", "about", "a", "hostname", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1796911226", "fields": {"term": "What environment variable can be used to set a different hostname value?", "namespace": "open-p", "hash": 1796911226, "terms": ["What environment variable can be used to set a different hostname value?", "environment variable can be used to set a different hostname value?", "variable can be used to set a different hostname value?", "can be used to set a different hostname value?", "be used to set a different hostname value?", "used to set a different hostname value?", "to set a different hostname value?", "set a different hostname value?", "a different hostname value?", "different hostname value?", "hostname value?", "value?"], "tokens": ["What", "environment", "variable", "can", "be", "used", "to", "set", "a", "different", "hostname", "value", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-74154374", "fields": {"term": "How to override VESPA_HOSTNAME?", "namespace": "open-p", "hash": -74154374, "terms": ["How to override VESPA_HOSTNAME?", "to override VESPA_HOSTNAME?", "override VESPA_HOSTNAME?", "VESPA_HOSTNAME?"], "tokens": ["How", "to", "override", "VESPA_HOSTNAME", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1899152394", "fields": {"term": "What is default-env.txt?", "namespace": "open-p", "hash": 1899152394, "terms": ["What is default-env.txt?", "is default-env.txt?", "default-env.txt?"], "tokens": ["What", "is", "default", "env", "txt", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1176410644", "fields": {"term": "How to avoid single-node setup hassle?", "namespace": "open-p", "hash": -1176410644, "terms": ["How to avoid single-node setup hassle?", "to avoid single-node setup hassle?", "avoid single-node setup hassle?", "single-node setup hassle?", "setup hassle?", "hassle?"], "tokens": ["How", "to", "avoid", "single", "node", "setup", "hassle", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::975646305", "fields": {"term": "What is the command to override VESPA_HOSTNAME?", "namespace": "open-p", "hash": 975646305, "terms": ["What is the command to override VESPA_HOSTNAME?", "is the command to override VESPA_HOSTNAME?", "the command to override VESPA_HOSTNAME?", "command to override VESPA_HOSTNAME?", "to override VESPA_HOSTNAME?", "override VESPA_HOSTNAME?", "VESPA_HOSTNAME?"], "tokens": ["What", "is", "the", "command", "to", "override", "VESPA_HOSTNAME", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-456829162", "fields": {"term": "What is endpoint certificate expiry?", "namespace": "open-p", "hash": -456829162, "terms": ["What is endpoint certificate expiry?", "is endpoint certificate expiry?", "endpoint certificate expiry?", "certificate expiry?", "expiry?"], "tokens": ["What", "is", "endpoint", "certificate", "expiry", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::701904785", "fields": {"term": "What unit does node certificate use?", "namespace": "open-p", "hash": 701904785, "terms": ["What unit does node certificate use?", "unit does node certificate use?", "does node certificate use?", "node certificate use?", "certificate use?", "use?"], "tokens": ["What", "unit", "does", "node", "certificate", "use", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-844325079", "fields": {"term": "What does endpoint.certificate.expiry.seconds measure?", "namespace": "open-p", "hash": -844325079, "terms": ["What does endpoint.certificate.expiry.seconds measure?", "does endpoint.certificate.expiry.seconds measure?", "endpoint.certificate.expiry.seconds measure?", "measure?"], "tokens": ["What", "does", "endpoint", "certificate", "expiry", "seconds", "measure", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::-649023307", "fields": {"term": "What is ONNX format?", "namespace": "open-p", "hash": -649023307, "terms": ["What is ONNX format?", "is ONNX format?", "ONNX format?", "format?"], "tokens": ["What", "is", "ONNX", "format", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::467239058", "fields": {"term": "How to implement advanced ranking models in Vespa?", "namespace": "open-p", "hash": 467239058, "terms": ["How to implement advanced ranking models in Vespa?", "to implement advanced ranking models in Vespa?", "implement advanced ranking models in Vespa?", "advanced ranking models in Vespa?", "ranking models in Vespa?", "models in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["How", "to", "implement", "advanced", "ranking", "models", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::157432054", "fields": {"term": "What is ONNX?", "namespace": "open-p", "hash": 157432054, "terms": ["What is ONNX?", "is ONNX?", "ONNX?"], "tokens": ["What", "is", "ONNX", "?"], "corpus_count": 3, "document_count": 3}}, -{"put": "id:term:term::-1836036843", "fields": {"term": "How to add a model?", "namespace": "open-p", "hash": -1836036843, "terms": ["How to add a model?", "to add a model?", "add a model?", "a model?", "model?"], "tokens": ["How", "to", "add", "a", "model", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2097314506", "fields": {"term": "How to deploy remote models?", "namespace": "open-p", "hash": 2097314506, "terms": ["How to deploy remote models?", "to deploy remote models?", "deploy remote models?", "remote models?", "models?"], "tokens": ["How", "to", "deploy", "remote", "models", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::867072091", "fields": {"term": "What is model-exporting notebook?", "namespace": "open-p", "hash": 867072091, "terms": ["What is model-exporting notebook?", "is model-exporting notebook?", "model-exporting notebook?", "notebook?"], "tokens": ["What", "is", "model", "exporting", "notebook", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2059515326", "fields": {"term": "What is model-deployment example?", "namespace": "open-p", "hash": -2059515326, "terms": ["What is model-deployment example?", "is model-deployment example?", "model-deployment example?", "example?"], "tokens": ["What", "is", "model", "deployment", "example", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1597524479", "fields": {"term": "What is the structure for a model file?", "namespace": "open-p", "hash": 1597524479, "terms": ["What is the structure for a model file?", "is the structure for a model file?", "the structure for a model file?", "structure for a model file?", "for a model file?", "a model file?", "model file?", "file?"], "tokens": ["What", "is", "the", "structure", "for", "a", "model", "file", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1587712942", "fields": {"term": "How to download models during deployment?", "namespace": "open-p", "hash": 1587712942, "terms": ["How to download models during deployment?", "to download models during deployment?", "download models during deployment?", "models during deployment?", "during deployment?", "deployment?"], "tokens": ["How", "to", "download", "models", "during", "deployment", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1470040006", "fields": {"term": "What is onnx-model?", "namespace": "open-p", "hash": 1470040006, "terms": ["What is onnx-model?", "is onnx-model?", "onnx-model?"], "tokens": ["What", "is", "onnx", "model", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1819638347", "fields": {"term": "How is my_onnx_model defined?", "namespace": "open-p", "hash": -1819638347, "terms": ["How is my_onnx_model defined?", "is my_onnx_model defined?", "my_onnx_model defined?", "defined?"], "tokens": ["How", "is", "my_onnx_model", "defined", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-182604923", "fields": {"term": "What are inputs to onnx-model?", "namespace": "open-p", "hash": -182604923, "terms": ["What are inputs to onnx-model?", "are inputs to onnx-model?", "inputs to onnx-model?", "to onnx-model?", "onnx-model?"], "tokens": ["What", "are", "inputs", "to", "onnx", "model", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-685368530", "fields": {"term": "What is the output of a model?", "namespace": "open-p", "hash": -685368530, "terms": ["What is the output of a model?", "is the output of a model?", "the output of a model?", "output of a model?", "of a model?", "a model?", "model?"], "tokens": ["What", "is", "the", "output", "of", "a", "model", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-814479197", "fields": {"term": "Are scalars supported in input tensors?", "namespace": "open-p", "hash": -814479197, "terms": ["Are scalars supported in input tensors?", "scalars supported in input tensors?", "supported in input tensors?", "in input tensors?", "input tensors?", "tensors?"], "tokens": ["Are", "scalars", "supported", "in", "input", "tensors", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1748895909", "fields": {"term": "What does -1 denote?", "namespace": "open-p", "hash": -1748895909, "terms": ["What does -1 denote?", "does -1 denote?", "-1 denote?", "denote?"], "tokens": ["What", "does", "1", "denote", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-723979337", "fields": {"term": "What is batch dimension?", "namespace": "open-p", "hash": -723979337, "terms": ["What is batch dimension?", "is batch dimension?", "batch dimension?", "dimension?"], "tokens": ["What", "is", "batch", "dimension", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1855139683", "fields": {"term": "What is batch size?", "namespace": "open-p", "hash": -1855139683, "terms": ["What is batch size?", "is batch size?", "batch size?", "size?"], "tokens": ["What", "is", "batch", "size", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1653725368", "fields": {"term": "What is mini-batch?", "namespace": "open-p", "hash": -1653725368, "terms": ["What is mini-batch?", "is mini-batch?", "mini-batch?"], "tokens": ["What", "is", "mini", "batch", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1671436694", "fields": {"term": "What is Vespa run-time evaluation?", "namespace": "open-p", "hash": 1671436694, "terms": ["What is Vespa run-time evaluation?", "is Vespa run-time evaluation?", "Vespa run-time evaluation?", "run-time evaluation?", "evaluation?"], "tokens": ["What", "is", "Vespa", "run", "time", "evaluation", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1369128867", "fields": {"term": "How does Vespa do inference?", "namespace": "open-p", "hash": -1369128867, "terms": ["How does Vespa do inference?", "does Vespa do inference?", "Vespa do inference?", "do inference?", "inference?"], "tokens": ["How", "does", "Vespa", "do", "inference", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1112169539", "fields": {"term": "What should batch dimension be set to during run-time evaluation?", "namespace": "open-p", "hash": 1112169539, "terms": ["What should batch dimension be set to during run-time evaluation?", "should batch dimension be set to during run-time evaluation?", "batch dimension be set to during run-time evaluation?", "dimension be set to during run-time evaluation?", "be set to during run-time evaluation?", "set to during run-time evaluation?", "to during run-time evaluation?", "during run-time evaluation?", "run-time evaluation?", "evaluation?"], "tokens": ["What", "should", "batch", "dimension", "be", "set", "to", "during", "run", "time", "evaluation", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::766211705", "fields": {"term": "Why set batch dimension to 1 during run-time evaluation?", "namespace": "open-p", "hash": 766211705, "terms": ["Why set batch dimension to 1 during run-time evaluation?", "set batch dimension to 1 during run-time evaluation?", "batch dimension to 1 during run-time evaluation?", "dimension to 1 during run-time evaluation?", "to 1 during run-time evaluation?", "1 during run-time evaluation?", "during run-time evaluation?", "run-time evaluation?", "evaluation?"], "tokens": ["Why", "set", "batch", "dimension", "to", "1", "during", "run", "time", "evaluation", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1921758398", "fields": {"term": "What is ONNX model evaluation?", "namespace": "open-p", "hash": 1921758398, "terms": ["What is ONNX model evaluation?", "is ONNX model evaluation?", "ONNX model evaluation?", "model evaluation?", "evaluation?"], "tokens": ["What", "is", "ONNX", "model", "evaluation", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1001229599", "fields": {"term": "What are the limitations of evaluating ONNX models?", "namespace": "open-p", "hash": 1001229599, "terms": ["What are the limitations of evaluating ONNX models?", "are the limitations of evaluating ONNX models?", "the limitations of evaluating ONNX models?", "limitations of evaluating ONNX models?", "of evaluating ONNX models?", "evaluating ONNX models?", "ONNX models?", "models?"], "tokens": ["What", "are", "the", "limitations", "of", "evaluating", "ONNX", "models", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-97679435", "fields": {"term": "How can deep models be made feasible?", "namespace": "open-p", "hash": -97679435, "terms": ["How can deep models be made feasible?", "can deep models be made feasible?", "deep models be made feasible?", "models be made feasible?", "be made feasible?", "made feasible?", "feasible?"], "tokens": ["How", "can", "deep", "models", "be", "made", "feasible", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2043964679", "fields": {"term": "What are the restrictions of evaluating models?", "namespace": "open-p", "hash": -2043964679, "terms": ["What are the restrictions of evaluating models?", "are the restrictions of evaluating models?", "the restrictions of evaluating models?", "restrictions of evaluating models?", "of evaluating models?", "evaluating models?", "models?"], "tokens": ["What", "are", "the", "restrictions", "of", "evaluating", "models", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1906665588", "fields": {"term": "What is the assumption when evaluating ONNX models in Vespa?", "namespace": "open-p", "hash": -1906665588, "terms": ["What is the assumption when evaluating ONNX models in Vespa?", "is the assumption when evaluating ONNX models in Vespa?", "the assumption when evaluating ONNX models in Vespa?", "assumption when evaluating ONNX models in Vespa?", "when evaluating ONNX models in Vespa?", "evaluating ONNX models in Vespa?", "ONNX models in Vespa?", "models in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["What", "is", "the", "assumption", "when", "evaluating", "ONNX", "models", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1864250708", "fields": {"term": "What is the Transformers sample app?", "namespace": "open-p", "hash": 1864250708, "terms": ["What is the Transformers sample app?", "is the Transformers sample app?", "the Transformers sample app?", "Transformers sample app?", "sample app?", "app?"], "tokens": ["What", "is", "the", "Transformers", "sample", "app", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::832336085", "fields": {"term": "What is the purpose of the ONNX model in the Transformers app?", "namespace": "open-p", "hash": 832336085, "terms": ["What is the purpose of the ONNX model in the Transformers app?", "is the purpose of the ONNX model in the Transformers app?", "the purpose of the ONNX model in the Transformers app?", "purpose of the ONNX model in the Transformers app?", "of the ONNX model in the Transformers app?", "the ONNX model in the Transformers app?", "ONNX model in the Transformers app?", "model in the Transformers app?", "in the Transformers app?", "the Transformers app?", "Transformers app?", "app?"], "tokens": ["What", "is", "the", "purpose", "of", "the", "ONNX", "model", "in", "the", "Transformers", "app", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1069515945", "fields": {"term": "What is the HuggingFace's Transformers library?", "namespace": "open-p", "hash": -1069515945, "terms": ["What is the HuggingFace's Transformers library?", "is the HuggingFace's Transformers library?", "the HuggingFace's Transformers library?", "HuggingFace's Transformers library?", "Transformers library?", "library?"], "tokens": ["What", "is", "the", "HuggingFace", "'", "s", "Transformers", "library", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::621369282", "fields": {"term": "What is the Question-Answering sample app?", "namespace": "open-p", "hash": 621369282, "terms": ["What is the Question-Answering sample app?", "is the Question-Answering sample app?", "the Question-Answering sample app?", "Question-Answering sample app?", "sample app?", "app?"], "tokens": ["What", "is", "the", "Question", "Answering", "sample", "app", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1352064755", "fields": {"term": "What are the two ONNX models used in the Question-Answering app?", "namespace": "open-p", "hash": -1352064755, "terms": ["What are the two ONNX models used in the Question-Answering app?", "are the two ONNX models used in the Question-Answering app?", "the two ONNX models used in the Question-Answering app?", "two ONNX models used in the Question-Answering app?", "ONNX models used in the Question-Answering app?", "models used in the Question-Answering app?", "used in the Question-Answering app?", "in the Question-Answering app?", "the Question-Answering app?", "Question-Answering app?", "app?"], "tokens": ["What", "are", "the", "two", "ONNX", "models", "used", "in", "the", "Question", "Answering", "app", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1388560506", "fields": {"term": "What is vespa-analyze-onnx-model?", "namespace": "open-p", "hash": 1388560506, "terms": ["What is vespa-analyze-onnx-model?", "is vespa-analyze-onnx-model?", "vespa-analyze-onnx-model?"], "tokens": ["What", "is", "vespa", "analyze", "onnx", "model", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::1822165148", "fields": {"term": "What can vespa-analyze-onnx-model find?", "namespace": "open-p", "hash": 1822165148, "terms": ["What can vespa-analyze-onnx-model find?", "can vespa-analyze-onnx-model find?", "vespa-analyze-onnx-model find?", "find?"], "tokens": ["What", "can", "vespa", "analyze", "onnx", "model", "find", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1275211137", "fields": {"term": "What does vespa-analyze-onnx-model output?", "namespace": "open-p", "hash": -1275211137, "terms": ["What does vespa-analyze-onnx-model output?", "does vespa-analyze-onnx-model output?", "vespa-analyze-onnx-model output?", "output?"], "tokens": ["What", "does", "vespa", "analyze", "onnx", "model", "output", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1481002361", "fields": {"term": "What is the format of the input/output tensors?", "namespace": "open-p", "hash": 1481002361, "terms": ["What is the format of the input/output tensors?", "is the format of the input/output tensors?", "the format of the input/output tensors?", "format of the input/output tensors?", "of the input/output tensors?", "the input/output tensors?", "input/output tensors?", "tensors?"], "tokens": ["What", "is", "the", "format", "of", "the", "input", "/", "output", "tensors", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-428896245", "fields": {"term": "What does the flowercategory field represent?", "namespace": "open-p", "hash": -428896245, "terms": ["What does the flowercategory field represent?", "does the flowercategory field represent?", "the flowercategory field represent?", "flowercategory field represent?", "field represent?", "represent?"], "tokens": ["What", "does", "the", "flowercategory", "field", "represent", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1195575168", "fields": {"term": "What indexing options are used for the flowercategory field?", "namespace": "open-p", "hash": -1195575168, "terms": ["What indexing options are used for the flowercategory field?", "indexing options are used for the flowercategory field?", "options are used for the flowercategory field?", "are used for the flowercategory field?", "used for the flowercategory field?", "for the flowercategory field?", "the flowercategory field?", "flowercategory field?", "field?"], "tokens": ["What", "indexing", "options", "are", "used", "for", "the", "flowercategory", "field", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1919676274", "fields": {"term": "How does Vespa enable low-latency computation?", "namespace": "open-p", "hash": -1919676274, "terms": ["How does Vespa enable low-latency computation?", "does Vespa enable low-latency computation?", "Vespa enable low-latency computation?", "enable low-latency computation?", "low-latency computation?", "computation?"], "tokens": ["How", "does", "Vespa", "enable", "low", "latency", "computation", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1744262938", "fields": {"term": "Can Vespa be customized?", "namespace": "open-p", "hash": -1744262938, "terms": ["Can Vespa be customized?", "Vespa be customized?", "be customized?", "customized?"], "tokens": ["Can", "Vespa", "be", "customized", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1328411625", "fields": {"term": "What is machine-learned model inference in Vespa?", "namespace": "open-p", "hash": -1328411625, "terms": ["What is machine-learned model inference in Vespa?", "is machine-learned model inference in Vespa?", "machine-learned model inference in Vespa?", "model inference in Vespa?", "inference in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["What", "is", "machine", "learned", "model", "inference", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-819582965", "fields": {"term": "What operations does Vespa accept?", "namespace": "open-p", "hash": -819582965, "terms": ["What operations does Vespa accept?", "operations does Vespa accept?", "does Vespa accept?", "Vespa accept?", "accept?"], "tokens": ["What", "operations", "does", "Vespa", "accept", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1342105063", "fields": {"term": "What is the visit operation?", "namespace": "open-p", "hash": 1342105063, "terms": ["What is the visit operation?", "is the visit operation?", "the visit operation?", "visit operation?", "operation?"], "tokens": ["What", "is", "the", "visit", "operation", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::819332091", "fields": {"term": "What does dependency injection mean?", "namespace": "open-p", "hash": 819332091, "terms": ["What does dependency injection mean?", "does dependency injection mean?", "dependency injection mean?", "injection mean?", "mean?"], "tokens": ["What", "does", "dependency", "injection", "mean", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-104244419", "fields": {"term": "What does the container provide?", "namespace": "open-p", "hash": -104244419, "terms": ["What does the container provide?", "does the container provide?", "the container provide?", "container provide?", "provide?"], "tokens": ["What", "does", "the", "container", "provide", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1612837366", "fields": {"term": "What protocols/transports can be plugged in?", "namespace": "open-p", "hash": -1612837366, "terms": ["What protocols/transports can be plugged in?", "protocols/transports can be plugged in?", "can be plugged in?", "be plugged in?", "plugged in?", "in?"], "tokens": ["What", "protocols", "/", "transports", "can", "be", "plugged", "in", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1324056344", "fields": {"term": "How can developers make changes to components?", "namespace": "open-p", "hash": 1324056344, "terms": ["How can developers make changes to components?", "can developers make changes to components?", "developers make changes to components?", "make changes to components?", "changes to components?", "to components?", "components?"], "tokens": ["How", "can", "developers", "make", "changes", "to", "components", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-328832395", "fields": {"term": "What does the system take care of?", "namespace": "open-p", "hash": -328832395, "terms": ["What does the system take care of?", "does the system take care of?", "the system take care of?", "system take care of?", "take care of?", "care of?", "of?"], "tokens": ["What", "does", "the", "system", "take", "care", "of", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-90584931", "fields": {"term": "What is the network layer for?", "namespace": "open-p", "hash": -90584931, "terms": ["What is the network layer for?", "is the network layer for?", "the network layer for?", "network layer for?", "layer for?", "for?"], "tokens": ["What", "is", "the", "network", "layer", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1087978673", "fields": {"term": "What are content clusters?", "namespace": "open-p", "hash": 1087978673, "terms": ["What are content clusters?", "are content clusters?", "content clusters?", "clusters?"], "tokens": ["What", "are", "content", "clusters", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-574796463", "fields": {"term": "What is the purpose of data replication?", "namespace": "open-p", "hash": -574796463, "terms": ["What is the purpose of data replication?", "is the purpose of data replication?", "the purpose of data replication?", "purpose of data replication?", "of data replication?", "data replication?", "replication?"], "tokens": ["What", "is", "the", "purpose", "of", "data", "replication", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1078448324", "fields": {"term": "Can clusters grow or shrink while online?", "namespace": "open-p", "hash": -1078448324, "terms": ["Can clusters grow or shrink while online?", "clusters grow or shrink while online?", "grow or shrink while online?", "or shrink while online?", "shrink while online?", "while online?", "online?"], "tokens": ["Can", "clusters", "grow", "or", "shrink", "while", "online", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-186191291", "fields": {"term": "What is the design goal of Vespa?", "namespace": "open-p", "hash": -186191291, "terms": ["What is the design goal of Vespa?", "is the design goal of Vespa?", "the design goal of Vespa?", "design goal of Vespa?", "goal of Vespa?", "of Vespa?", "Vespa?"], "tokens": ["What", "is", "the", "design", "goal", "of", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1991988845", "fields": {"term": "What is the single administration and configuration cluster?", "namespace": "open-p", "hash": 1991988845, "terms": ["What is the single administration and configuration cluster?", "is the single administration and configuration cluster?", "the single administration and configuration cluster?", "single administration and configuration cluster?", "administration and configuration cluster?", "and configuration cluster?", "configuration cluster?", "cluster?"], "tokens": ["What", "is", "the", "single", "administration", "and", "configuration", "cluster", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1283139817", "fields": {"term": "What is the purpose of the administration cluster?", "namespace": "open-p", "hash": 1283139817, "terms": ["What is the purpose of the administration cluster?", "is the purpose of the administration cluster?", "the purpose of the administration cluster?", "purpose of the administration cluster?", "of the administration cluster?", "the administration cluster?", "administration cluster?", "cluster?"], "tokens": ["What", "is", "the", "purpose", "of", "the", "administration", "cluster", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1333974912", "fields": {"term": "How does Vespa handle evolving applications?", "namespace": "open-p", "hash": 1333974912, "terms": ["How does Vespa handle evolving applications?", "does Vespa handle evolving applications?", "Vespa handle evolving applications?", "handle evolving applications?", "evolving applications?", "applications?"], "tokens": ["How", "does", "Vespa", "handle", "evolving", "applications", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::204065562", "fields": {"term": "What does Vespa allow developers to avoid?", "namespace": "open-p", "hash": 204065562, "terms": ["What does Vespa allow developers to avoid?", "does Vespa allow developers to avoid?", "Vespa allow developers to avoid?", "allow developers to avoid?", "developers to avoid?", "to avoid?", "avoid?"], "tokens": ["What", "does", "Vespa", "allow", "developers", "to", "avoid", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1009768743", "fields": {"term": "What is コンテナー・クラスター?", "namespace": "open-p", "hash": 1009768743, "terms": ["What is コンテナー・クラスター?", "is コンテナー・クラスター?", "コンテナー・クラスター?"], "tokens": ["What", "is", "コンテナー", "・", "クラスター", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1711016697", "fields": {"term": "What is Guice?", "namespace": "open-p", "hash": 1711016697, "terms": ["What is Guice?", "is Guice?", "Guice?"], "tokens": ["What", "is", "Guice", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::147196308", "fields": {"term": "What is the page result format?", "namespace": "open-p", "hash": 147196308, "terms": ["What is the page result format?", "is the page result format?", "the page result format?", "page result format?", "result format?", "format?"], "tokens": ["What", "is", "the", "page", "result", "format", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-811272129", "fields": {"term": "When is this format used?", "namespace": "open-p", "hash": -811272129, "terms": ["When is this format used?", "is this format used?", "this format used?", "format used?", "used?"], "tokens": ["When", "is", "this", "format", "used", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1218343049", "fields": {"term": "What is presentation.format?", "namespace": "open-p", "hash": 1218343049, "terms": ["What is presentation.format?", "is presentation.format?", "presentation.format?"], "tokens": ["What", "is", "presentation", "format", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1286200", "fields": {"term": "What is the root tag?", "namespace": "open-p", "hash": 1286200, "terms": ["What is the root tag?", "is the root tag?", "the root tag?", "root tag?", "tag?"], "tokens": ["What", "is", "the", "root", "tag", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::157181493", "fields": {"term": "What does the root tag contain?", "namespace": "open-p", "hash": 157181493, "terms": ["What does the root tag contain?", "does the root tag contain?", "the root tag contain?", "root tag contain?", "tag contain?", "contain?"], "tokens": ["What", "does", "the", "root", "tag", "contain", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::212683004", "fields": {"term": "What is 'layout' attribute used for?", "namespace": "open-p", "hash": 212683004, "terms": ["What is 'layout' attribute used for?", "is 'layout' attribute used for?", "'layout' attribute used for?", "attribute used for?", "used for?", "for?"], "tokens": ["What", "is", "'", "layout", "'", "attribute", "used", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1440185321", "fields": {"term": "What is a layout box?", "namespace": "open-p", "hash": -1440185321, "terms": ["What is a layout box?", "is a layout box?", "a layout box?", "layout box?", "box?"], "tokens": ["What", "is", "a", "layout", "box", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::209211098", "fields": {"term": "What attributes describe a layout box?", "namespace": "open-p", "hash": 209211098, "terms": ["What attributes describe a layout box?", "attributes describe a layout box?", "describe a layout box?", "a layout box?", "layout box?", "box?"], "tokens": ["What", "attributes", "describe", "a", "layout", "box", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1221258976", "fields": {"term": "What are the sub tags of a layout box?", "namespace": "open-p", "hash": -1221258976, "terms": ["What are the sub tags of a layout box?", "are the sub tags of a layout box?", "the sub tags of a layout box?", "sub tags of a layout box?", "tags of a layout box?", "of a layout box?", "a layout box?", "layout box?", "box?"], "tokens": ["What", "are", "the", "sub", "tags", "of", "a", "layout", "box", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1652107883", "fields": {"term": "Can a layout box have nested sections?", "namespace": "open-p", "hash": -1652107883, "terms": ["Can a layout box have nested sections?", "a layout box have nested sections?", "layout box have nested sections?", "box have nested sections?", "have nested sections?", "nested sections?", "sections?"], "tokens": ["Can", "a", "layout", "box", "have", "nested", "sections", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2006771860", "fields": {"term": "What is the 'region' attribute used for?", "namespace": "open-p", "hash": -2006771860, "terms": ["What is the 'region' attribute used for?", "is the 'region' attribute used for?", "the 'region' attribute used for?", "'region' attribute used for?", "attribute used for?", "used for?", "for?"], "tokens": ["What", "is", "the", "'", "region", "'", "attribute", "used", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::755656588", "fields": {"term": "What is a renderer?", "namespace": "open-p", "hash": 755656588, "terms": ["What is a renderer?", "is a renderer?", "a renderer?", "renderer?"], "tokens": ["What", "is", "a", "renderer", "?"], "corpus_count": 3, "document_count": 3}}, -{"put": "id:term:term::-1711156628", "fields": {"term": "What is the 'url' attribute used for?", "namespace": "open-p", "hash": -1711156628, "terms": ["What is the 'url' attribute used for?", "is the 'url' attribute used for?", "the 'url' attribute used for?", "'url' attribute used for?", "attribute used for?", "used for?", "for?"], "tokens": ["What", "is", "the", "'", "url", "'", "attribute", "used", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::924790658", "fields": {"term": "What is 'inline content'?", "namespace": "open-p", "hash": 924790658, "terms": ["What is 'inline content'?", "is 'inline content'?", "'inline content'?", "content'?"], "tokens": ["What", "is", "'", "inline", "content", "'?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1117045488", "fields": {"term": "What is a 'subtag' in this document?", "namespace": "open-p", "hash": -1117045488, "terms": ["What is a 'subtag' in this document?", "is a 'subtag' in this document?", "a 'subtag' in this document?", "'subtag' in this document?", "in this document?", "this document?", "document?"], "tokens": ["What", "is", "a", "'", "subtag", "'", "in", "this", "document", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-221429288", "fields": {"term": "What is the description for the 'parameter' subtag?", "namespace": "open-p", "hash": -221429288, "terms": ["What is the description for the 'parameter' subtag?", "is the description for the 'parameter' subtag?", "the description for the 'parameter' subtag?", "description for the 'parameter' subtag?", "for the 'parameter' subtag?", "the 'parameter' subtag?", "'parameter' subtag?", "subtag?"], "tokens": ["What", "is", "the", "description", "for", "the", "'", "parameter", "'", "subtag", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::204743675", "fields": {"term": "What is a result content item?", "namespace": "open-p", "hash": 204743675, "terms": ["What is a result content item?", "is a result content item?", "a result content item?", "result content item?", "content item?", "item?"], "tokens": ["What", "is", "a", "result", "content", "item", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::294816375", "fields": {"term": "What attributes does a hit have?", "namespace": "open-p", "hash": 294816375, "terms": ["What attributes does a hit have?", "attributes does a hit have?", "does a hit have?", "a hit have?", "hit have?", "have?"], "tokens": ["What", "attributes", "does", "a", "hit", "have", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::267360563", "fields": {"term": "What is a subtag in a hit?", "namespace": "open-p", "hash": 267360563, "terms": ["What is a subtag in a hit?", "is a subtag in a hit?", "a subtag in a hit?", "subtag in a hit?", "in a hit?", "a hit?", "hit?"], "tokens": ["What", "is", "a", "subtag", "in", "a", "hit", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1233682337", "fields": {"term": "What is a nested hit?", "namespace": "open-p", "hash": 1233682337, "terms": ["What is a nested hit?", "is a nested hit?", "a nested hit?", "nested hit?", "hit?"], "tokens": ["What", "is", "a", "nested", "hit", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1968891309", "fields": {"term": "What is a hit hierarchy?", "namespace": "open-p", "hash": -1968891309, "terms": ["What is a hit hierarchy?", "is a hit hierarchy?", "a hit hierarchy?", "hit hierarchy?", "hierarchy?"], "tokens": ["What", "is", "a", "hit", "hierarchy", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::38775550", "fields": {"term": "What is a group tag?", "namespace": "open-p", "hash": 38775550, "terms": ["What is a group tag?", "is a group tag?", "a group tag?", "group tag?", "tag?"], "tokens": ["What", "is", "a", "group", "tag", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-42063149", "fields": {"term": "What is a subtag?", "namespace": "open-p", "hash": -42063149, "terms": ["What is a subtag?", "is a subtag?", "a subtag?", "subtag?"], "tokens": ["What", "is", "a", "subtag", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-407370651", "fields": {"term": "Can a hit have nested hits?", "namespace": "open-p", "hash": -407370651, "terms": ["Can a hit have nested hits?", "a hit have nested hits?", "hit have nested hits?", "have nested hits?", "nested hits?", "hits?"], "tokens": ["Can", "a", "hit", "have", "nested", "hits", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-245310556", "fields": {"term": "What are the tags in a hit?", "namespace": "open-p", "hash": -245310556, "terms": ["What are the tags in a hit?", "are the tags in a hit?", "the tags in a hit?", "tags in a hit?", "in a hit?", "a hit?", "hit?"], "tokens": ["What", "are", "the", "tags", "in", "a", "hit", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::33815848", "fields": {"term": "What is a page renderer?", "namespace": "open-p", "hash": 33815848, "terms": ["What is a page renderer?", "is a page renderer?", "a page renderer?", "page renderer?", "renderer?"], "tokens": ["What", "is", "a", "page", "renderer", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1025444635", "fields": {"term": "What is a two-column renderer?", "namespace": "open-p", "hash": -1025444635, "terms": ["What is a two-column renderer?", "is a two-column renderer?", "a two-column renderer?", "two-column renderer?", "renderer?"], "tokens": ["What", "is", "a", "two", "column", "renderer", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-166550583", "fields": {"term": "What is left region?", "namespace": "open-p", "hash": -166550583, "terms": ["What is left region?", "is left region?", "left region?", "region?"], "tokens": ["What", "is", "left", "region", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1637845324", "fields": {"term": "What is right region?", "namespace": "open-p", "hash": -1637845324, "terms": ["What is right region?", "is right region?", "right region?", "region?"], "tokens": ["What", "is", "right", "region", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1564229516", "fields": {"term": "What is articleBodyRenderer?", "namespace": "open-p", "hash": -1564229516, "terms": ["What is articleBodyRenderer?", "is articleBodyRenderer?", "articleBodyRenderer?"], "tokens": ["What", "is", "articleBodyRenderer", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-256237692", "fields": {"term": "What is newsImageRenderer?", "namespace": "open-p", "hash": -256237692, "terms": ["What is newsImageRenderer?", "is newsImageRenderer?", "newsImageRenderer?"], "tokens": ["What", "is", "newsImageRenderer", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-821515341", "fields": {"term": "What is imageRenderer?", "namespace": "open-p", "hash": -821515341, "terms": ["What is imageRenderer?", "is imageRenderer?", "imageRenderer?"], "tokens": ["What", "is", "imageRenderer", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::733397652", "fields": {"term": "What is htmlRenderer?", "namespace": "open-p", "hash": 733397652, "terms": ["What is htmlRenderer?", "is htmlRenderer?", "htmlRenderer?"], "tokens": ["What", "is", "htmlRenderer", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1823194373", "fields": {"term": "What is the Page Template XML format?", "namespace": "open-p", "hash": -1823194373, "terms": ["What is the Page Template XML format?", "is the Page Template XML format?", "the Page Template XML format?", "Page Template XML format?", "Template XML format?", "XML format?", "format?"], "tokens": ["What", "is", "the", "Page", "Template", "XML", "format", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1522890037", "fields": {"term": "What is a page template?", "namespace": "open-p", "hash": -1522890037, "terms": ["What is a page template?", "is a page template?", "a page template?", "page template?", "template?"], "tokens": ["What", "is", "a", "page", "template", "?"], "corpus_count": 5, "document_count": 5}}, -{"put": "id:term:term::484223460", "fields": {"term": "What is the structure of a page template?", "namespace": "open-p", "hash": 484223460, "terms": ["What is the structure of a page template?", "is the structure of a page template?", "the structure of a page template?", "structure of a page template?", "of a page template?", "a page template?", "page template?", "template?"], "tokens": ["What", "is", "the", "structure", "of", "a", "page", "template", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-598698854", "fields": {"term": "What are the elements of a page template?", "namespace": "open-p", "hash": -598698854, "terms": ["What are the elements of a page template?", "are the elements of a page template?", "the elements of a page template?", "elements of a page template?", "of a page template?", "a page template?", "page template?", "template?"], "tokens": ["What", "are", "the", "elements", "of", "a", "page", "template", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-520564533", "fields": {"term": "What is a page-element?", "namespace": "open-p", "hash": -520564533, "terms": ["What is a page-element?", "is a page-element?", "a page-element?", "page-element?"], "tokens": ["What", "is", "a", "page", "element", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::1978661477", "fields": {"term": "What is a choice?", "namespace": "open-p", "hash": 1978661477, "terms": ["What is a choice?", "is a choice?", "a choice?", "choice?"], "tokens": ["What", "is", "a", "choice", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1511889429", "fields": {"term": "What is the format of an id?", "namespace": "open-p", "hash": -1511889429, "terms": ["What is the format of an id?", "is the format of an id?", "the format of an id?", "format of an id?", "of an id?", "an id?", "id?"], "tokens": ["What", "is", "the", "format", "of", "an", "id", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1250535345", "fields": {"term": "What is the major component?", "namespace": "open-p", "hash": -1250535345, "terms": ["What is the major component?", "is the major component?", "the major component?", "major component?", "component?"], "tokens": ["What", "is", "the", "major", "component", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1999648403", "fields": {"term": "What is the minor component?", "namespace": "open-p", "hash": -1999648403, "terms": ["What is the minor component?", "is the minor component?", "the minor component?", "minor component?", "component?"], "tokens": ["What", "is", "the", "minor", "component", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1017437622", "fields": {"term": "What is the micro component?", "namespace": "open-p", "hash": 1017437622, "terms": ["What is the micro component?", "is the micro component?", "the micro component?", "micro component?", "component?"], "tokens": ["What", "is", "the", "micro", "component", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1849712252", "fields": {"term": "What is the qualifier component?", "namespace": "open-p", "hash": 1849712252, "terms": ["What is the qualifier component?", "is the qualifier component?", "the qualifier component?", "qualifier component?", "component?"], "tokens": ["What", "is", "the", "qualifier", "component", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1370386056", "fields": {"term": "How are omitted numeric components handled?", "namespace": "open-p", "hash": -1370386056, "terms": ["How are omitted numeric components handled?", "are omitted numeric components handled?", "omitted numeric components handled?", "numeric components handled?", "components handled?", "handled?"], "tokens": ["How", "are", "omitted", "numeric", "components", "handled", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1793516704", "fields": {"term": "How is a missing qualifier handled?", "namespace": "open-p", "hash": 1793516704, "terms": ["How is a missing qualifier handled?", "is a missing qualifier handled?", "a missing qualifier handled?", "missing qualifier handled?", "qualifier handled?", "handled?"], "tokens": ["How", "is", "a", "missing", "qualifier", "handled", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1384055693", "fields": {"term": "What are the attributes of a page template?", "namespace": "open-p", "hash": 1384055693, "terms": ["What are the attributes of a page template?", "are the attributes of a page template?", "the attributes of a page template?", "attributes of a page template?", "of a page template?", "a page template?", "page template?", "template?"], "tokens": ["What", "are", "the", "attributes", "of", "a", "page", "template", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2026723496", "fields": {"term": "What is the purpose of a section in a page template?", "namespace": "open-p", "hash": -2026723496, "terms": ["What is the purpose of a section in a page template?", "is the purpose of a section in a page template?", "the purpose of a section in a page template?", "purpose of a section in a page template?", "of a section in a page template?", "a section in a page template?", "section in a page template?", "in a page template?", "a page template?", "page template?", "template?"], "tokens": ["What", "is", "the", "purpose", "of", "a", "section", "in", "a", "page", "template", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1740379368", "fields": {"term": "What is a section?", "namespace": "open-p", "hash": -1740379368, "terms": ["What is a section?", "is a section?", "a section?", "section?"], "tokens": ["What", "is", "a", "section", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-506799399", "fields": {"term": "What is the default layout?", "namespace": "open-p", "hash": -506799399, "terms": ["What is the default layout?", "is the default layout?", "the default layout?", "default layout?", "layout?"], "tokens": ["What", "is", "the", "default", "layout", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::782856570", "fields": {"term": "What is the max number of items for a section?", "namespace": "open-p", "hash": 782856570, "terms": ["What is the max number of items for a section?", "is the max number of items for a section?", "the max number of items for a section?", "max number of items for a section?", "number of items for a section?", "of items for a section?", "items for a section?", "for a section?", "a section?", "section?"], "tokens": ["What", "is", "the", "max", "number", "of", "items", "for", "a", "section", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1859250147", "fields": {"term": "What is the min number of items for a section?", "namespace": "open-p", "hash": 1859250147, "terms": ["What is the min number of items for a section?", "is the min number of items for a section?", "the min number of items for a section?", "min number of items for a section?", "number of items for a section?", "of items for a section?", "items for a section?", "for a section?", "a section?", "section?"], "tokens": ["What", "is", "the", "min", "number", "of", "items", "for", "a", "section", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1228540544", "fields": {"term": "What is the order for displaying items in a section?", "namespace": "open-p", "hash": -1228540544, "terms": ["What is the order for displaying items in a section?", "is the order for displaying items in a section?", "the order for displaying items in a section?", "order for displaying items in a section?", "for displaying items in a section?", "displaying items in a section?", "items in a section?", "in a section?", "a section?", "section?"], "tokens": ["What", "is", "the", "order", "for", "displaying", "items", "in", "a", "section", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::360977802", "fields": {"term": "What is the source attribute for a section?", "namespace": "open-p", "hash": 360977802, "terms": ["What is the source attribute for a section?", "is the source attribute for a section?", "the source attribute for a section?", "source attribute for a section?", "attribute for a section?", "for a section?", "a section?", "section?"], "tokens": ["What", "is", "the", "source", "attribute", "for", "a", "section", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::265766951", "fields": {"term": "What is a subsection?", "namespace": "open-p", "hash": 265766951, "terms": ["What is a subsection?", "is a subsection?", "a subsection?", "subsection?"], "tokens": ["What", "is", "a", "subsection", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1187733293", "fields": {"term": "What is a data source?", "namespace": "open-p", "hash": 1187733293, "terms": ["What is a data source?", "is a data source?", "a data source?", "data source?", "source?"], "tokens": ["What", "is", "a", "data", "source", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1260519919", "fields": {"term": "What are the attributes of a data source?", "namespace": "open-p", "hash": 1260519919, "terms": ["What are the attributes of a data source?", "are the attributes of a data source?", "the attributes of a data source?", "attributes of a data source?", "of a data source?", "a data source?", "data source?", "source?"], "tokens": ["What", "are", "the", "attributes", "of", "a", "data", "source", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-339875325", "fields": {"term": "What is the default value for name in a data source?", "namespace": "open-p", "hash": -339875325, "terms": ["What is the default value for name in a data source?", "is the default value for name in a data source?", "the default value for name in a data source?", "default value for name in a data source?", "value for name in a data source?", "for name in a data source?", "name in a data source?", "in a data source?", "a data source?", "data source?", "source?"], "tokens": ["What", "is", "the", "default", "value", "for", "name", "in", "a", "data", "source", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1039749345", "fields": {"term": "What is the default value for url in a data source?", "namespace": "open-p", "hash": -1039749345, "terms": ["What is the default value for url in a data source?", "is the default value for url in a data source?", "the default value for url in a data source?", "default value for url in a data source?", "value for url in a data source?", "for url in a data source?", "url in a data source?", "in a data source?", "a data source?", "data source?", "source?"], "tokens": ["What", "is", "the", "default", "value", "for", "url", "in", "a", "data", "source", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1154379893", "fields": {"term": "What is the purpose of url in a data source?", "namespace": "open-p", "hash": 1154379893, "terms": ["What is the purpose of url in a data source?", "is the purpose of url in a data source?", "the purpose of url in a data source?", "purpose of url in a data source?", "of url in a data source?", "url in a data source?", "in a data source?", "a data source?", "data source?", "source?"], "tokens": ["What", "is", "the", "purpose", "of", "url", "in", "a", "data", "source", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::104476384", "fields": {"term": "What is an alternative tag?", "namespace": "open-p", "hash": 104476384, "terms": ["What is an alternative tag?", "is an alternative tag?", "an alternative tag?", "alternative tag?", "tag?"], "tokens": ["What", "is", "an", "alternative", "tag", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1041718238", "fields": {"term": "What is the default for method?", "namespace": "open-p", "hash": -1041718238, "terms": ["What is the default for method?", "is the default for method?", "the default for method?", "default for method?", "for method?", "method?"], "tokens": ["What", "is", "the", "default", "for", "method", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::351572084", "fields": {"term": "What does the alternative tag enclose?", "namespace": "open-p", "hash": 351572084, "terms": ["What does the alternative tag enclose?", "does the alternative tag enclose?", "the alternative tag enclose?", "alternative tag enclose?", "tag enclose?", "enclose?"], "tokens": ["What", "does", "the", "alternative", "tag", "enclose", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::67004680", "fields": {"term": "What is an alternative?", "namespace": "open-p", "hash": 67004680, "terms": ["What is an alternative?", "is an alternative?", "an alternative?", "alternative?"], "tokens": ["What", "is", "an", "alternative", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1754969294", "fields": {"term": "What is a mapping function?", "namespace": "open-p", "hash": -1754969294, "terms": ["What is a mapping function?", "is a mapping function?", "a mapping function?", "mapping function?", "function?"], "tokens": ["What", "is", "a", "mapping", "function", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-116524622", "fields": {"term": "What is a map?", "namespace": "open-p", "hash": -116524622, "terms": ["What is a map?", "is a map?", "a map?", "map?"], "tokens": ["What", "is", "a", "map", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-173726775", "fields": {"term": "What is a placeholder?", "namespace": "open-p", "hash": -173726775, "terms": ["What is a placeholder?", "is a placeholder?", "a placeholder?", "placeholder?"], "tokens": ["What", "is", "a", "placeholder", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1966820507", "fields": {"term": "What is a page element?", "namespace": "open-p", "hash": -1966820507, "terms": ["What is a page element?", "is a page element?", "a page element?", "page element?", "element?"], "tokens": ["What", "is", "a", "page", "element", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-861294397", "fields": {"term": "What is an item?", "namespace": "open-p", "hash": -861294397, "terms": ["What is an item?", "is an item?", "an item?", "item?"], "tokens": ["What", "is", "an", "item", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1845602800", "fields": {"term": "What is the purpose of a map?", "namespace": "open-p", "hash": -1845602800, "terms": ["What is the purpose of a map?", "is the purpose of a map?", "the purpose of a map?", "purpose of a map?", "of a map?", "a map?", "map?"], "tokens": ["What", "is", "the", "purpose", "of", "a", "map", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1211211604", "fields": {"term": "How many placeholder ids can 'to' have?", "namespace": "open-p", "hash": -1211211604, "terms": ["How many placeholder ids can 'to' have?", "many placeholder ids can 'to' have?", "placeholder ids can 'to' have?", "ids can 'to' have?", "can 'to' have?", "'to' have?", "have?"], "tokens": ["How", "many", "placeholder", "ids", "can", "'", "to", "'", "have", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2005542173", "fields": {"term": "What is included in the page element?", "namespace": "open-p", "hash": -2005542173, "terms": ["What is included in the page element?", "is included in the page element?", "included in the page element?", "in the page element?", "the page element?", "page element?", "element?"], "tokens": ["What", "is", "included", "in", "the", "page", "element", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1295763669", "fields": {"term": "What is the default of the idref attribute?", "namespace": "open-p", "hash": 1295763669, "terms": ["What is the default of the idref attribute?", "is the default of the idref attribute?", "the default of the idref attribute?", "default of the idref attribute?", "of the idref attribute?", "the idref attribute?", "idref attribute?", "attribute?"], "tokens": ["What", "is", "the", "default", "of", "the", "idref", "attribute", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1562516914", "fields": {"term": "What is the include tag?", "namespace": "open-p", "hash": -1562516914, "terms": ["What is the include tag?", "is the include tag?", "the include tag?", "include tag?", "tag?"], "tokens": ["What", "is", "the", "include", "tag", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-309960298", "fields": {"term": "What is the mandatory attribute?", "namespace": "open-p", "hash": -309960298, "terms": ["What is the mandatory attribute?", "is the mandatory attribute?", "the mandatory attribute?", "mandatory attribute?", "attribute?"], "tokens": ["What", "is", "the", "mandatory", "attribute", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2009419085", "fields": {"term": "What are page templates?", "namespace": "open-p", "hash": 2009419085, "terms": ["What are page templates?", "are page templates?", "page templates?", "templates?"], "tokens": ["What", "are", "page", "templates", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-183805768", "fields": {"term": "What is a resolver?", "namespace": "open-p", "hash": -183805768, "terms": ["What is a resolver?", "is a resolver?", "a resolver?", "resolver?"], "tokens": ["What", "is", "a", "resolver", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1679093917", "fields": {"term": "What is the page template language?", "namespace": "open-p", "hash": -1679093917, "terms": ["What is the page template language?", "is the page template language?", "the page template language?", "page template language?", "template language?", "language?"], "tokens": ["What", "is", "the", "page", "template", "language", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1000587654", "fields": {"term": "What is found in the page template reference?", "namespace": "open-p", "hash": 1000587654, "terms": ["What is found in the page template reference?", "is found in the page template reference?", "found in the page template reference?", "in the page template reference?", "the page template reference?", "page template reference?", "template reference?", "reference?"], "tokens": ["What", "is", "found", "in", "the", "page", "template", "reference", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1930232994", "fields": {"term": "Where should template XML files be placed?", "namespace": "open-p", "hash": 1930232994, "terms": ["Where should template XML files be placed?", "should template XML files be placed?", "template XML files be placed?", "XML files be placed?", "files be placed?", "be placed?", "placed?"], "tokens": ["Where", "should", "template", "XML", "files", "be", "placed", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::66785135", "fields": {"term": "What is the PageTemplateSearcher?", "namespace": "open-p", "hash": 66785135, "terms": ["What is the PageTemplateSearcher?", "is the PageTemplateSearcher?", "the PageTemplateSearcher?", "PageTemplateSearcher?"], "tokens": ["What", "is", "the", "PageTemplateSearcher", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-805174427", "fields": {"term": "What is the purpose of a presentation layer?", "namespace": "open-p", "hash": -805174427, "terms": ["What is the purpose of a presentation layer?", "is the purpose of a presentation layer?", "the purpose of a presentation layer?", "purpose of a presentation layer?", "of a presentation layer?", "a presentation layer?", "presentation layer?", "layer?"], "tokens": ["What", "is", "the", "purpose", "of", "a", "presentation", "layer", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1642856628", "fields": {"term": "How to define sections in templates?", "namespace": "open-p", "hash": 1642856628, "terms": ["How to define sections in templates?", "to define sections in templates?", "define sections in templates?", "sections in templates?", "in templates?", "templates?"], "tokens": ["How", "to", "define", "sections", "in", "templates", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-373150625", "fields": {"term": "What is a nested section?", "namespace": "open-p", "hash": -373150625, "terms": ["What is a nested section?", "is a nested section?", "a nested section?", "nested section?", "section?"], "tokens": ["What", "is", "a", "nested", "section", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::223133038", "fields": {"term": "What is a source in a template?", "namespace": "open-p", "hash": 223133038, "terms": ["What is a source in a template?", "is a source in a template?", "a source in a template?", "source in a template?", "in a template?", "a template?", "template?"], "tokens": ["What", "is", "a", "source", "in", "a", "template", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::868574605", "fields": {"term": "What is a choice in a template?", "namespace": "open-p", "hash": 868574605, "terms": ["What is a choice in a template?", "is a choice in a template?", "a choice in a template?", "choice in a template?", "in a template?", "a template?", "template?"], "tokens": ["What", "is", "a", "choice", "in", "a", "template", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1053939977", "fields": {"term": "How to specify renderers in templates?", "namespace": "open-p", "hash": -1053939977, "terms": ["How to specify renderers in templates?", "to specify renderers in templates?", "specify renderers in templates?", "renderers in templates?", "in templates?", "templates?"], "tokens": ["How", "to", "specify", "renderers", "in", "templates", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1727097469", "fields": {"term": "What parameters can a source or a renderer take?", "namespace": "open-p", "hash": -1727097469, "terms": ["What parameters can a source or a renderer take?", "parameters can a source or a renderer take?", "can a source or a renderer take?", "a source or a renderer take?", "source or a renderer take?", "or a renderer take?", "a renderer take?", "renderer take?", "take?"], "tokens": ["What", "parameters", "can", "a", "source", "or", "a", "renderer", "take", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-64366095", "fields": {"term": "What is an alternative tag in a choice?", "namespace": "open-p", "hash": -64366095, "terms": ["What is an alternative tag in a choice?", "is an alternative tag in a choice?", "an alternative tag in a choice?", "alternative tag in a choice?", "tag in a choice?", "in a choice?", "a choice?", "choice?"], "tokens": ["What", "is", "an", "alternative", "tag", "in", "a", "choice", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1792756433", "fields": {"term": "What are template choices?", "namespace": "open-p", "hash": 1792756433, "terms": ["What are template choices?", "are template choices?", "template choices?", "choices?"], "tokens": ["What", "are", "template", "choices", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-957778213", "fields": {"term": "Why do we need a resolver?", "namespace": "open-p", "hash": -957778213, "terms": ["Why do we need a resolver?", "do we need a resolver?", "we need a resolver?", "need a resolver?", "a resolver?", "resolver?"], "tokens": ["Why", "do", "we", "need", "a", "resolver", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::541172448", "fields": {"term": "How do you create a resolver?", "namespace": "open-p", "hash": 541172448, "terms": ["How do you create a resolver?", "do you create a resolver?", "you create a resolver?", "create a resolver?", "a resolver?", "resolver?"], "tokens": ["How", "do", "you", "create", "a", "resolver", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-312550764", "fields": {"term": "Can demo resolvers be used?", "namespace": "open-p", "hash": -312550764, "terms": ["Can demo resolvers be used?", "demo resolvers be used?", "resolvers be used?", "be used?", "used?"], "tokens": ["Can", "demo", "resolvers", "be", "used", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::516203987", "fields": {"term": "What is resolver deployment?", "namespace": "open-p", "hash": 516203987, "terms": ["What is resolver deployment?", "is resolver deployment?", "resolver deployment?", "deployment?"], "tokens": ["What", "is", "resolver", "deployment", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1572240422", "fields": {"term": "What are resolvers?", "namespace": "open-p", "hash": -1572240422, "terms": ["What are resolvers?", "are resolvers?", "resolvers?"], "tokens": ["What", "are", "resolvers", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1167634634", "fields": {"term": "What is the Resolver API?", "namespace": "open-p", "hash": 1167634634, "terms": ["What is the Resolver API?", "is the Resolver API?", "the Resolver API?", "Resolver API?", "API?"], "tokens": ["What", "is", "the", "Resolver", "API", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-961543950", "fields": {"term": "What does a resolver do?", "namespace": "open-p", "hash": -961543950, "terms": ["What does a resolver do?", "does a resolver do?", "a resolver do?", "resolver do?", "do?"], "tokens": ["What", "does", "a", "resolver", "do", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::176036350", "fields": {"term": "What does a Resolver method do?", "namespace": "open-p", "hash": 176036350, "terms": ["What does a Resolver method do?", "does a Resolver method do?", "a Resolver method do?", "Resolver method do?", "method do?", "do?"], "tokens": ["What", "does", "a", "Resolver", "method", "do", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1110920210", "fields": {"term": "What is Resolution?", "namespace": "open-p", "hash": -1110920210, "terms": ["What is Resolution?", "is Resolution?", "Resolution?"], "tokens": ["What", "is", "Resolution", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-955823439", "fields": {"term": "How does RandomResolver choose alternatives?", "namespace": "open-p", "hash": -955823439, "terms": ["How does RandomResolver choose alternatives?", "does RandomResolver choose alternatives?", "RandomResolver choose alternatives?", "choose alternatives?", "alternatives?"], "tokens": ["How", "does", "RandomResolver", "choose", "alternatives", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1786242263", "fields": {"term": "What is the purpose of the RandomResolver?", "namespace": "open-p", "hash": -1786242263, "terms": ["What is the purpose of the RandomResolver?", "is the purpose of the RandomResolver?", "the purpose of the RandomResolver?", "purpose of the RandomResolver?", "of the RandomResolver?", "the RandomResolver?", "RandomResolver?"], "tokens": ["What", "is", "the", "purpose", "of", "the", "RandomResolver", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1414824261", "fields": {"term": "How are resolvers packaged?", "namespace": "open-p", "hash": -1414824261, "terms": ["How are resolvers packaged?", "are resolvers packaged?", "resolvers packaged?", "packaged?"], "tokens": ["How", "are", "resolvers", "packaged", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1760570173", "fields": {"term": "How is page template searcher configured?", "namespace": "open-p", "hash": -1760570173, "terms": ["How is page template searcher configured?", "is page template searcher configured?", "page template searcher configured?", "template searcher configured?", "searcher configured?", "configured?"], "tokens": ["How", "is", "page", "template", "searcher", "configured", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-13929086", "fields": {"term": "How is the resolver determined?", "namespace": "open-p", "hash": -13929086, "terms": ["How is the resolver determined?", "is the resolver determined?", "the resolver determined?", "resolver determined?", "determined?"], "tokens": ["How", "is", "the", "resolver", "determined", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1766047528", "fields": {"term": "What is the page.resolver property?", "namespace": "open-p", "hash": -1766047528, "terms": ["What is the page.resolver property?", "is the page.resolver property?", "the page.resolver property?", "page.resolver property?", "property?"], "tokens": ["What", "is", "the", "page", "resolver", "property", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1002963852", "fields": {"term": "What happens if page.resolver is not set?", "namespace": "open-p", "hash": 1002963852, "terms": ["What happens if page.resolver is not set?", "happens if page.resolver is not set?", "if page.resolver is not set?", "page.resolver is not set?", "is not set?", "not set?", "set?"], "tokens": ["What", "happens", "if", "page", "resolver", "is", "not", "set", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-565004884", "fields": {"term": "What is the random resolver?", "namespace": "open-p", "hash": -565004884, "terms": ["What is the random resolver?", "is the random resolver?", "the random resolver?", "random resolver?", "resolver?"], "tokens": ["What", "is", "the", "random", "resolver", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::238224345", "fields": {"term": "What is the deterministic resolver?", "namespace": "open-p", "hash": 238224345, "terms": ["What is the deterministic resolver?", "is the deterministic resolver?", "the deterministic resolver?", "deterministic resolver?", "resolver?"], "tokens": ["What", "is", "the", "deterministic", "resolver", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1752945246", "fields": {"term": "What is the default resolver?", "namespace": "open-p", "hash": -1752945246, "terms": ["What is the default resolver?", "is the default resolver?", "the default resolver?", "default resolver?", "resolver?"], "tokens": ["What", "is", "the", "default", "resolver", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::517308886", "fields": {"term": "How can the resolver be set programmatically?", "namespace": "open-p", "hash": 517308886, "terms": ["How can the resolver be set programmatically?", "can the resolver be set programmatically?", "the resolver be set programmatically?", "resolver be set programmatically?", "be set programmatically?", "set programmatically?", "programmatically?"], "tokens": ["How", "can", "the", "resolver", "be", "set", "programmatically", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::296251982", "fields": {"term": "What is a resolver component?", "namespace": "open-p", "hash": 296251982, "terms": ["What is a resolver component?", "is a resolver component?", "a resolver component?", "resolver component?", "component?"], "tokens": ["What", "is", "a", "resolver", "component", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1978322629", "fields": {"term": "What is a blending search result page?", "namespace": "open-p", "hash": 1978322629, "terms": ["What is a blending search result page?", "is a blending search result page?", "a blending search result page?", "blending search result page?", "search result page?", "result page?", "page?"], "tokens": ["What", "is", "a", "blending", "search", "result", "page", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1010734888", "fields": {"term": "What is a richer search result page?", "namespace": "open-p", "hash": 1010734888, "terms": ["What is a richer search result page?", "is a richer search result page?", "a richer search result page?", "richer search result page?", "search result page?", "result page?", "page?"], "tokens": ["What", "is", "a", "richer", "search", "result", "page", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1503477606", "fields": {"term": "What is a page template mapping?", "namespace": "open-p", "hash": 1503477606, "terms": ["What is a page template mapping?", "is a page template mapping?", "a page template mapping?", "page template mapping?", "template mapping?", "mapping?"], "tokens": ["What", "is", "a", "page", "template", "mapping", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-595941914", "fields": {"term": "What is parent/child relationship?", "namespace": "open-p", "hash": -595941914, "terms": ["What is parent/child relationship?", "is parent/child relationship?", "parent/child relationship?", "relationship?"], "tokens": ["What", "is", "parent", "/", "child", "relationship", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2054818287", "fields": {"term": "What is the alternative to parent documents?", "namespace": "open-p", "hash": -2054818287, "terms": ["What is the alternative to parent documents?", "is the alternative to parent documents?", "the alternative to parent documents?", "alternative to parent documents?", "to parent documents?", "parent documents?", "documents?"], "tokens": ["What", "is", "the", "alternative", "to", "parent", "documents", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1414252119", "fields": {"term": "What are the common use cases for document references?", "namespace": "open-p", "hash": 1414252119, "terms": ["What are the common use cases for document references?", "are the common use cases for document references?", "the common use cases for document references?", "common use cases for document references?", "use cases for document references?", "cases for document references?", "for document references?", "document references?", "references?"], "tokens": ["What", "are", "the", "common", "use", "cases", "for", "document", "references", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1234481171", "fields": {"term": "What is the high-level overview of parent/child documents in Vespa?", "namespace": "open-p", "hash": 1234481171, "terms": ["What is the high-level overview of parent/child documents in Vespa?", "is the high-level overview of parent/child documents in Vespa?", "the high-level overview of parent/child documents in Vespa?", "high-level overview of parent/child documents in Vespa?", "overview of parent/child documents in Vespa?", "of parent/child documents in Vespa?", "parent/child documents in Vespa?", "documents in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["What", "is", "the", "high", "level", "overview", "of", "parent", "/", "child", "documents", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::23442501", "fields": {"term": "How are fields imported in Vespa parent-child relationships?", "namespace": "open-p", "hash": 23442501, "terms": ["How are fields imported in Vespa parent-child relationships?", "are fields imported in Vespa parent-child relationships?", "fields imported in Vespa parent-child relationships?", "imported in Vespa parent-child relationships?", "in Vespa parent-child relationships?", "Vespa parent-child relationships?", "parent-child relationships?", "relationships?"], "tokens": ["How", "are", "fields", "imported", "in", "Vespa", "parent", "child", "relationships", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-239250909", "fields": {"term": "How does Vespa handle parent fields updates?", "namespace": "open-p", "hash": -239250909, "terms": ["How does Vespa handle parent fields updates?", "does Vespa handle parent fields updates?", "Vespa handle parent fields updates?", "handle parent fields updates?", "parent fields updates?", "fields updates?", "updates?"], "tokens": ["How", "does", "Vespa", "handle", "parent", "fields", "updates", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2841288", "fields": {"term": "What are the performance notes for Vespa parent-child relationships?", "namespace": "open-p", "hash": 2841288, "terms": ["What are the performance notes for Vespa parent-child relationships?", "are the performance notes for Vespa parent-child relationships?", "the performance notes for Vespa parent-child relationships?", "performance notes for Vespa parent-child relationships?", "notes for Vespa parent-child relationships?", "for Vespa parent-child relationships?", "Vespa parent-child relationships?", "parent-child relationships?", "relationships?"], "tokens": ["What", "are", "the", "performance", "notes", "for", "Vespa", "parent", "child", "relationships", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-977647020", "fields": {"term": "What is a multivalue field?", "namespace": "open-p", "hash": -977647020, "terms": ["What is a multivalue field?", "is a multivalue field?", "a multivalue field?", "multivalue field?", "field?"], "tokens": ["What", "is", "a", "multivalue", "field", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::1196027622", "fields": {"term": "What is a partial update?", "namespace": "open-p", "hash": 1196027622, "terms": ["What is a partial update?", "is a partial update?", "a partial update?", "partial update?", "update?"], "tokens": ["What", "is", "a", "partial", "update", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::-623212282", "fields": {"term": "What is the effect of partial update on throughput?", "namespace": "open-p", "hash": -623212282, "terms": ["What is the effect of partial update on throughput?", "is the effect of partial update on throughput?", "the effect of partial update on throughput?", "effect of partial update on throughput?", "of partial update on throughput?", "partial update on throughput?", "update on throughput?", "on throughput?", "throughput?"], "tokens": ["What", "is", "the", "effect", "of", "partial", "update", "on", "throughput", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-144675941", "fields": {"term": "How are summary and index fields updated?", "namespace": "open-p", "hash": -144675941, "terms": ["How are summary and index fields updated?", "are summary and index fields updated?", "summary and index fields updated?", "and index fields updated?", "index fields updated?", "fields updated?", "updated?"], "tokens": ["How", "are", "summary", "and", "index", "fields", "updated", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1068901744", "fields": {"term": "What is the effect of partial update on write throughput?", "namespace": "open-p", "hash": -1068901744, "terms": ["What is the effect of partial update on write throughput?", "is the effect of partial update on write throughput?", "the effect of partial update on write throughput?", "effect of partial update on write throughput?", "of partial update on write throughput?", "partial update on write throughput?", "update on write throughput?", "on write throughput?", "write throughput?", "throughput?"], "tokens": ["What", "is", "the", "effect", "of", "partial", "update", "on", "write", "throughput", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::196883409", "fields": {"term": "What are use cases for partial updates?", "namespace": "open-p", "hash": 196883409, "terms": ["What are use cases for partial updates?", "are use cases for partial updates?", "use cases for partial updates?", "cases for partial updates?", "for partial updates?", "partial updates?", "updates?"], "tokens": ["What", "are", "use", "cases", "for", "partial", "updates", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::576883528", "fields": {"term": "How do partial updates improve performance?", "namespace": "open-p", "hash": 576883528, "terms": ["How do partial updates improve performance?", "do partial updates improve performance?", "partial updates improve performance?", "updates improve performance?", "improve performance?", "performance?"], "tokens": ["How", "do", "partial", "updates", "improve", "performance", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1071967799", "fields": {"term": "What is the purpose of partial updates?", "namespace": "open-p", "hash": 1071967799, "terms": ["What is the purpose of partial updates?", "is the purpose of partial updates?", "the purpose of partial updates?", "purpose of partial updates?", "of partial updates?", "partial updates?", "updates?"], "tokens": ["What", "is", "the", "purpose", "of", "partial", "updates", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1074260893", "fields": {"term": "What is the difference between partial updates and full updates?", "namespace": "open-p", "hash": -1074260893, "terms": ["What is the difference between partial updates and full updates?", "is the difference between partial updates and full updates?", "the difference between partial updates and full updates?", "difference between partial updates and full updates?", "between partial updates and full updates?", "partial updates and full updates?", "updates and full updates?", "and full updates?", "full updates?", "updates?"], "tokens": ["What", "is", "the", "difference", "between", "partial", "updates", "and", "full", "updates", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1774543455", "fields": {"term": "Does completing an update return a synchronous ACK?", "namespace": "open-p", "hash": 1774543455, "terms": ["Does completing an update return a synchronous ACK?", "completing an update return a synchronous ACK?", "an update return a synchronous ACK?", "update return a synchronous ACK?", "return a synchronous ACK?", "a synchronous ACK?", "synchronous ACK?", "ACK?"], "tokens": ["Does", "completing", "an", "update", "return", "a", "synchronous", "ACK", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::730109342", "fields": {"term": "Are attributes and summary stores updated in memory or on disk?", "namespace": "open-p", "hash": 730109342, "terms": ["Are attributes and summary stores updated in memory or on disk?", "attributes and summary stores updated in memory or on disk?", "and summary stores updated in memory or on disk?", "summary stores updated in memory or on disk?", "stores updated in memory or on disk?", "updated in memory or on disk?", "in memory or on disk?", "memory or on disk?", "or on disk?", "on disk?", "disk?"], "tokens": ["Are", "attributes", "and", "summary", "stores", "updated", "in", "memory", "or", "on", "disk", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::692320361", "fields": {"term": "How can parent attributes help update rates?", "namespace": "open-p", "hash": 692320361, "terms": ["How can parent attributes help update rates?", "can parent attributes help update rates?", "parent attributes help update rates?", "attributes help update rates?", "help update rates?", "update rates?", "rates?"], "tokens": ["How", "can", "parent", "attributes", "help", "update", "rates", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1274939094", "fields": {"term": "What are rank phases?", "namespace": "open-p", "hash": 1274939094, "terms": ["What are rank phases?", "are rank phases?", "rank phases?", "phases?"], "tokens": ["What", "are", "rank", "phases", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-858518826", "fields": {"term": "What is first-phase ranking?", "namespace": "open-p", "hash": -858518826, "terms": ["What is first-phase ranking?", "is first-phase ranking?", "first-phase ranking?", "ranking?"], "tokens": ["What", "is", "first", "phase", "ranking", "?"], "corpus_count": 3, "document_count": 3}}, -{"put": "id:term:term::-833971122", "fields": {"term": "What is second-phase ranking?", "namespace": "open-p", "hash": -833971122, "terms": ["What is second-phase ranking?", "is second-phase ranking?", "second-phase ranking?", "ranking?"], "tokens": ["What", "is", "second", "phase", "ranking", "?"], "corpus_count": 3, "document_count": 3}}, -{"put": "id:term:term::140733251", "fields": {"term": "What is global-phase ranking?", "namespace": "open-p", "hash": 140733251, "terms": ["What is global-phase ranking?", "is global-phase ranking?", "global-phase ranking?", "ranking?"], "tokens": ["What", "is", "global", "phase", "ranking", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::9257127", "fields": {"term": "What is normal ranking?", "namespace": "open-p", "hash": 9257127, "terms": ["What is normal ranking?", "is normal ranking?", "normal ranking?", "ranking?"], "tokens": ["What", "is", "normal", "ranking", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1990331873", "fields": {"term": "What is a first-phase expression?", "namespace": "open-p", "hash": 1990331873, "terms": ["What is a first-phase expression?", "is a first-phase expression?", "a first-phase expression?", "first-phase expression?", "expression?"], "tokens": ["What", "is", "a", "first", "phase", "expression", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1883239350", "fields": {"term": "What is the cost of first-phase?", "namespace": "open-p", "hash": 1883239350, "terms": ["What is the cost of first-phase?", "is the cost of first-phase?", "the cost of first-phase?", "cost of first-phase?", "of first-phase?", "first-phase?"], "tokens": ["What", "is", "the", "cost", "of", "first", "phase", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::808146941", "fields": {"term": "How is the first-phase configured?", "namespace": "open-p", "hash": 808146941, "terms": ["How is the first-phase configured?", "is the first-phase configured?", "the first-phase configured?", "first-phase configured?", "configured?"], "tokens": ["How", "is", "the", "first", "phase", "configured", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1779877094", "fields": {"term": "What is evaluated in the first-phase?", "namespace": "open-p", "hash": 1779877094, "terms": ["What is evaluated in the first-phase?", "is evaluated in the first-phase?", "evaluated in the first-phase?", "in the first-phase?", "the first-phase?", "first-phase?"], "tokens": ["What", "is", "evaluated", "in", "the", "first", "phase", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1784711873", "fields": {"term": "What is the complexity of first-phase?", "namespace": "open-p", "hash": -1784711873, "terms": ["What is the complexity of first-phase?", "is the complexity of first-phase?", "the complexity of first-phase?", "complexity of first-phase?", "of first-phase?", "first-phase?"], "tokens": ["What", "is", "the", "complexity", "of", "first", "phase", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::446985683", "fields": {"term": "How does scalability affect first-phase?", "namespace": "open-p", "hash": 446985683, "terms": ["How does scalability affect first-phase?", "does scalability affect first-phase?", "scalability affect first-phase?", "affect first-phase?", "first-phase?"], "tokens": ["How", "does", "scalability", "affect", "first", "phase", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1547439294", "fields": {"term": "What is freshness ranking?", "namespace": "open-p", "hash": 1547439294, "terms": ["What is freshness ranking?", "is freshness ranking?", "freshness ranking?", "ranking?"], "tokens": ["What", "is", "freshness", "ranking", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::800725657", "fields": {"term": "What is the rerank-count set to?", "namespace": "open-p", "hash": 800725657, "terms": ["What is the rerank-count set to?", "is the rerank-count set to?", "the rerank-count set to?", "rerank-count set to?", "set to?", "to?"], "tokens": ["What", "is", "the", "rerank", "count", "set", "to", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1546688679", "fields": {"term": "What are stateless searchers?", "namespace": "open-p", "hash": 1546688679, "terms": ["What are stateless searchers?", "are stateless searchers?", "stateless searchers?", "searchers?"], "tokens": ["What", "are", "stateless", "searchers", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::132395751", "fields": {"term": "How can hits be re-ranked?", "namespace": "open-p", "hash": 132395751, "terms": ["How can hits be re-ranked?", "can hits be re-ranked?", "hits be re-ranked?", "be re-ranked?", "re-ranked?"], "tokens": ["How", "can", "hits", "be", "re", "ranked", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::890041305", "fields": {"term": "What is ReRankingSearcher?", "namespace": "open-p", "hash": 890041305, "terms": ["What is ReRankingSearcher?", "is ReRankingSearcher?", "ReRankingSearcher?"], "tokens": ["What", "is", "ReRankingSearcher", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2022451197", "fields": {"term": "What is Weak And/WAND?", "namespace": "open-p", "hash": -2022451197, "terms": ["What is Weak And/WAND?", "is Weak And/WAND?", "Weak And/WAND?", "And/WAND?"], "tokens": ["What", "is", "Weak", "And", "/", "WAND", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-408025955", "fields": {"term": "What is the internal operator ranking?", "namespace": "open-p", "hash": -408025955, "terms": ["What is the internal operator ranking?", "is the internal operator ranking?", "the internal operator ranking?", "internal operator ranking?", "operator ranking?", "ranking?"], "tokens": ["What", "is", "the", "internal", "operator", "ranking", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-130610419", "fields": {"term": "How does Weak And/WAND work?", "namespace": "open-p", "hash": -130610419, "terms": ["How does Weak And/WAND work?", "does Weak And/WAND work?", "Weak And/WAND work?", "And/WAND work?", "work?"], "tokens": ["How", "does", "Weak", "And", "/", "WAND", "work", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::937429598", "fields": {"term": "What is the targeted number of hits for Weak And/WAND?", "namespace": "open-p", "hash": 937429598, "terms": ["What is the targeted number of hits for Weak And/WAND?", "is the targeted number of hits for Weak And/WAND?", "the targeted number of hits for Weak And/WAND?", "targeted number of hits for Weak And/WAND?", "number of hits for Weak And/WAND?", "of hits for Weak And/WAND?", "hits for Weak And/WAND?", "for Weak And/WAND?", "Weak And/WAND?", "And/WAND?"], "tokens": ["What", "is", "the", "targeted", "number", "of", "hits", "for", "Weak", "And", "/", "WAND", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::834234993", "fields": {"term": "What is the skipped set in Weak And/WAND?", "namespace": "open-p", "hash": 834234993, "terms": ["What is the skipped set in Weak And/WAND?", "is the skipped set in Weak And/WAND?", "the skipped set in Weak And/WAND?", "skipped set in Weak And/WAND?", "set in Weak And/WAND?", "in Weak And/WAND?", "Weak And/WAND?", "And/WAND?"], "tokens": ["What", "is", "the", "skipped", "set", "in", "Weak", "And", "/", "WAND", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1025228315", "fields": {"term": "What is the effect of not fully evaluating all documents matching the query in Weak And/WAND?", "namespace": "open-p", "hash": 1025228315, "terms": ["What is the effect of not fully evaluating all documents matching the query in Weak And/WAND?", "is the effect of not fully evaluating all documents matching the query in Weak And/WAND?", "the effect of not fully evaluating all documents matching the query in Weak And/WAND?", "effect of not fully evaluating all documents matching the query in Weak And/WAND?", "of not fully evaluating all documents matching the query in Weak And/WAND?", "not fully evaluating all documents matching the query in Weak And/WAND?", "fully evaluating all documents matching the query in Weak And/WAND?", "evaluating all documents matching the query in Weak And/WAND?", "all documents matching the query in Weak And/WAND?", "documents matching the query in Weak And/WAND?", "matching the query in Weak And/WAND?", "the query in Weak And/WAND?", "query in Weak And/WAND?", "in Weak And/WAND?", "Weak And/WAND?", "And/WAND?"], "tokens": ["What", "is", "the", "effect", "of", "not", "fully", "evaluating", "all", "documents", "matching", "the", "query", "in", "Weak", "And", "/", "WAND", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1898590479", "fields": {"term": "What is a good ranking expression?", "namespace": "open-p", "hash": 1898590479, "terms": ["What is a good ranking expression?", "is a good ranking expression?", "a good ranking expression?", "good ranking expression?", "ranking expression?", "expression?"], "tokens": ["What", "is", "a", "good", "ranking", "expression", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2030052299", "fields": {"term": "Why should application ranking be a second phase function?", "namespace": "open-p", "hash": 2030052299, "terms": ["Why should application ranking be a second phase function?", "should application ranking be a second phase function?", "application ranking be a second phase function?", "ranking be a second phase function?", "be a second phase function?", "a second phase function?", "second phase function?", "phase function?", "function?"], "tokens": ["Why", "should", "application", "ranking", "be", "a", "second", "phase", "function", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1065473138", "fields": {"term": "What is the task for finding a first phase function?", "namespace": "open-p", "hash": -1065473138, "terms": ["What is the task for finding a first phase function?", "is the task for finding a first phase function?", "the task for finding a first phase function?", "task for finding a first phase function?", "for finding a first phase function?", "finding a first phase function?", "a first phase function?", "first phase function?", "phase function?", "function?"], "tokens": ["What", "is", "the", "task", "for", "finding", "a", "first", "phase", "function", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1700906478", "fields": {"term": "What is the goal of finding a first phase function?", "namespace": "open-p", "hash": -1700906478, "terms": ["What is the goal of finding a first phase function?", "is the goal of finding a first phase function?", "the goal of finding a first phase function?", "goal of finding a first phase function?", "of finding a first phase function?", "finding a first phase function?", "a first phase function?", "first phase function?", "phase function?", "function?"], "tokens": ["What", "is", "the", "goal", "of", "finding", "a", "first", "phase", "function", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1021339413", "fields": {"term": "How can relevance be ensured without evaluating the real ranking function on all the hits?", "namespace": "open-p", "hash": 1021339413, "terms": ["How can relevance be ensured without evaluating the real ranking function on all the hits?", "can relevance be ensured without evaluating the real ranking function on all the hits?", "relevance be ensured without evaluating the real ranking function on all the hits?", "be ensured without evaluating the real ranking function on all the hits?", "ensured without evaluating the real ranking function on all the hits?", "without evaluating the real ranking function on all the hits?", "evaluating the real ranking function on all the hits?", "the real ranking function on all the hits?", "real ranking function on all the hits?", "ranking function on all the hits?", "function on all the hits?", "on all the hits?", "all the hits?", "the hits?", "hits?"], "tokens": ["How", "can", "relevance", "be", "ensured", "without", "evaluating", "the", "real", "ranking", "function", "on", "all", "the", "hits", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1599805189", "fields": {"term": "What is the common use case for mutable attributes?", "namespace": "open-p", "hash": 1599805189, "terms": ["What is the common use case for mutable attributes?", "is the common use case for mutable attributes?", "the common use case for mutable attributes?", "common use case for mutable attributes?", "use case for mutable attributes?", "case for mutable attributes?", "for mutable attributes?", "mutable attributes?", "attributes?"], "tokens": ["What", "is", "the", "common", "use", "case", "for", "mutable", "attributes", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1022940701", "fields": {"term": "What is the purpose of rank-profile?", "namespace": "open-p", "hash": -1022940701, "terms": ["What is the purpose of rank-profile?", "is the purpose of rank-profile?", "the purpose of rank-profile?", "purpose of rank-profile?", "of rank-profile?", "rank-profile?"], "tokens": ["What", "is", "the", "purpose", "of", "rank", "profile", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1699303217", "fields": {"term": "How to initialize mutable attribute?", "namespace": "open-p", "hash": 1699303217, "terms": ["How to initialize mutable attribute?", "to initialize mutable attribute?", "initialize mutable attribute?", "mutable attribute?", "attribute?"], "tokens": ["How", "to", "initialize", "mutable", "attribute", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1773019827", "fields": {"term": "What is the purpose of searchPath?", "namespace": "open-p", "hash": 1773019827, "terms": ["What is the purpose of searchPath?", "is the purpose of searchPath?", "the purpose of searchPath?", "purpose of searchPath?", "of searchPath?", "searchPath?"], "tokens": ["What", "is", "the", "purpose", "of", "searchPath", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1931930906", "fields": {"term": "What are Service Provider Interfaces (SPI)?", "namespace": "open-p", "hash": -1931930906, "terms": ["What are Service Provider Interfaces (SPI)?", "are Service Provider Interfaces (SPI)?", "Service Provider Interfaces (SPI)?", "Provider Interfaces (SPI)?", "Interfaces (SPI)?", "(SPI)?"], "tokens": ["What", "are", "Service", "Provider", "Interfaces", "(", "SPI", ")?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2098076436", "fields": {"term": "What is a pluggable architecture?", "namespace": "open-p", "hash": 2098076436, "terms": ["What is a pluggable architecture?", "is a pluggable architecture?", "a pluggable architecture?", "pluggable architecture?", "architecture?"], "tokens": ["What", "is", "a", "pluggable", "architecture", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::637778402", "fields": {"term": "How do libraries provide pluggable architectures?", "namespace": "open-p", "hash": 637778402, "terms": ["How do libraries provide pluggable architectures?", "do libraries provide pluggable architectures?", "libraries provide pluggable architectures?", "provide pluggable architectures?", "pluggable architectures?", "architectures?"], "tokens": ["How", "do", "libraries", "provide", "pluggable", "architectures", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1260162343", "fields": {"term": "What is required to use libraries with SPI?", "namespace": "open-p", "hash": -1260162343, "terms": ["What is required to use libraries with SPI?", "is required to use libraries with SPI?", "required to use libraries with SPI?", "to use libraries with SPI?", "use libraries with SPI?", "libraries with SPI?", "with SPI?", "SPI?"], "tokens": ["What", "is", "required", "to", "use", "libraries", "with", "SPI", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1345396050", "fields": {"term": "Is manual setup needed for libraries with SPI?", "namespace": "open-p", "hash": -1345396050, "terms": ["Is manual setup needed for libraries with SPI?", "manual setup needed for libraries with SPI?", "setup needed for libraries with SPI?", "needed for libraries with SPI?", "for libraries with SPI?", "libraries with SPI?", "with SPI?", "SPI?"], "tokens": ["Is", "manual", "setup", "needed", "for", "libraries", "with", "SPI", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-568791785", "fields": {"term": "What kind of libraries provide pluggable architectures?", "namespace": "open-p", "hash": -568791785, "terms": ["What kind of libraries provide pluggable architectures?", "kind of libraries provide pluggable architectures?", "of libraries provide pluggable architectures?", "libraries provide pluggable architectures?", "provide pluggable architectures?", "pluggable architectures?", "architectures?"], "tokens": ["What", "kind", "of", "libraries", "provide", "pluggable", "architectures", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-315389775", "fields": {"term": "How does SPI benefit applications?", "namespace": "open-p", "hash": -315389775, "terms": ["How does SPI benefit applications?", "does SPI benefit applications?", "SPI benefit applications?", "benefit applications?", "applications?"], "tokens": ["How", "does", "SPI", "benefit", "applications", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::764635669", "fields": {"term": "What is the role of SPI in libraries?", "namespace": "open-p", "hash": 764635669, "terms": ["What is the role of SPI in libraries?", "is the role of SPI in libraries?", "the role of SPI in libraries?", "role of SPI in libraries?", "of SPI in libraries?", "SPI in libraries?", "in libraries?", "libraries?"], "tokens": ["What", "is", "the", "role", "of", "SPI", "in", "libraries", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-759921841", "fields": {"term": "What does ClassNotFoundException mean?", "namespace": "open-p", "hash": -759921841, "terms": ["What does ClassNotFoundException mean?", "does ClassNotFoundException mean?", "ClassNotFoundException mean?", "mean?"], "tokens": ["What", "does", "ClassNotFoundException", "mean", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1300373673", "fields": {"term": "How to solve ClassNotFoundException?", "namespace": "open-p", "hash": 1300373673, "terms": ["How to solve ClassNotFoundException?", "to solve ClassNotFoundException?", "solve ClassNotFoundException?", "ClassNotFoundException?"], "tokens": ["How", "to", "solve", "ClassNotFoundException", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1653604278", "fields": {"term": "What is the missing package?", "namespace": "open-p", "hash": -1653604278, "terms": ["What is the missing package?", "is the missing package?", "the missing package?", "missing package?", "package?"], "tokens": ["What", "is", "the", "missing", "package", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-831999221", "fields": {"term": "How do we export packages?", "namespace": "open-p", "hash": -831999221, "terms": ["How do we export packages?", "do we export packages?", "we export packages?", "export packages?", "packages?"], "tokens": ["How", "do", "we", "export", "packages", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-989238791", "fields": {"term": "Where is the export_packages defined?", "namespace": "open-p", "hash": -989238791, "terms": ["Where is the export_packages defined?", "is the export_packages defined?", "the export_packages defined?", "export_packages defined?", "defined?"], "tokens": ["Where", "is", "the", "export_packages", "defined", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2088889075", "fields": {"term": "How is missing package resolved?", "namespace": "open-p", "hash": 2088889075, "terms": ["How is missing package resolved?", "is missing package resolved?", "missing package resolved?", "package resolved?", "resolved?"], "tokens": ["How", "is", "missing", "package", "resolved", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-564693548", "fields": {"term": "How to export missing package in Vespa?", "namespace": "open-p", "hash": -564693548, "terms": ["How to export missing package in Vespa?", "to export missing package in Vespa?", "export missing package in Vespa?", "missing package in Vespa?", "package in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["How", "to", "export", "missing", "package", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::943638456", "fields": {"term": "What is the purpose of this guide?", "namespace": "open-p", "hash": 943638456, "terms": ["What is the purpose of this guide?", "is the purpose of this guide?", "the purpose of this guide?", "purpose of this guide?", "of this guide?", "this guide?", "guide?"], "tokens": ["What", "is", "the", "purpose", "of", "this", "guide", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1778325393", "fields": {"term": "What dataset does this guide use?", "namespace": "open-p", "hash": 1778325393, "terms": ["What dataset does this guide use?", "dataset does this guide use?", "does this guide use?", "this guide use?", "guide use?", "use?"], "tokens": ["What", "dataset", "does", "this", "guide", "use", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::286849789", "fields": {"term": "What query performance aspects are covered?", "namespace": "open-p", "hash": 286849789, "terms": ["What query performance aspects are covered?", "query performance aspects are covered?", "performance aspects are covered?", "aspects are covered?", "are covered?", "covered?"], "tokens": ["What", "query", "performance", "aspects", "are", "covered", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1248588651", "fields": {"term": "What is multithreaded search?", "namespace": "open-p", "hash": 1248588651, "terms": ["What is multithreaded search?", "is multithreaded search?", "multithreaded search?", "search?"], "tokens": ["What", "is", "multithreaded", "search", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-411414181", "fields": {"term": "What is early termination for range search?", "namespace": "open-p", "hash": -411414181, "terms": ["What is early termination for range search?", "is early termination for range search?", "early termination for range search?", "termination for range search?", "for range search?", "range search?", "search?"], "tokens": ["What", "is", "early", "termination", "for", "range", "search", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-445699050", "fields": {"term": "What is advanced query tracing?", "namespace": "open-p", "hash": -445699050, "terms": ["What is advanced query tracing?", "is advanced query tracing?", "advanced query tracing?", "query tracing?", "tracing?"], "tokens": ["What", "is", "advanced", "query", "tracing", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1888443014", "fields": {"term": "What is the official command-line client for Vespa.ai?", "namespace": "open-p", "hash": 1888443014, "terms": ["What is the official command-line client for Vespa.ai?", "is the official command-line client for Vespa.ai?", "the official command-line client for Vespa.ai?", "official command-line client for Vespa.ai?", "command-line client for Vespa.ai?", "client for Vespa.ai?", "for Vespa.ai?", "Vespa.ai?"], "tokens": ["What", "is", "the", "official", "command", "line", "client", "for", "Vespa", "ai", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1169466885", "fields": {"term": "Is Vespa-CLI available for Windows?", "namespace": "open-p", "hash": -1169466885, "terms": ["Is Vespa-CLI available for Windows?", "Vespa-CLI available for Windows?", "available for Windows?", "for Windows?", "Windows?"], "tokens": ["Is", "Vespa", "CLI", "available", "for", "Windows", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::901774194", "fields": {"term": "What are the system requirements for Vespa-CLI installation?", "namespace": "open-p", "hash": 901774194, "terms": ["What are the system requirements for Vespa-CLI installation?", "are the system requirements for Vespa-CLI installation?", "the system requirements for Vespa-CLI installation?", "system requirements for Vespa-CLI installation?", "requirements for Vespa-CLI installation?", "for Vespa-CLI installation?", "Vespa-CLI installation?", "installation?"], "tokens": ["What", "are", "the", "system", "requirements", "for", "Vespa", "CLI", "installation", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-43679848", "fields": {"term": "What is the Last.fm tracks dataset?", "namespace": "open-p", "hash": -43679848, "terms": ["What is the Last.fm tracks dataset?", "is the Last.fm tracks dataset?", "the Last.fm tracks dataset?", "Last.fm tracks dataset?", "tracks dataset?", "dataset?"], "tokens": ["What", "is", "the", "Last", "fm", "tracks", "dataset", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1419499769", "fields": {"term": "What are the terms for using the dataset?", "namespace": "open-p", "hash": 1419499769, "terms": ["What are the terms for using the dataset?", "are the terms for using the dataset?", "the terms for using the dataset?", "terms for using the dataset?", "for using the dataset?", "using the dataset?", "the dataset?", "dataset?"], "tokens": ["What", "are", "the", "terms", "for", "using", "the", "dataset", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-289264191", "fields": {"term": "How can the dataset be downloaded?", "namespace": "open-p", "hash": -289264191, "terms": ["How can the dataset be downloaded?", "can the dataset be downloaded?", "the dataset be downloaded?", "dataset be downloaded?", "be downloaded?", "downloaded?"], "tokens": ["How", "can", "the", "dataset", "be", "downloaded", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1070558228", "fields": {"term": "What is the purpose of the python script?", "namespace": "open-p", "hash": 1070558228, "terms": ["What is the purpose of the python script?", "is the purpose of the python script?", "the purpose of the python script?", "purpose of the python script?", "of the python script?", "the python script?", "python script?", "script?"], "tokens": ["What", "is", "the", "purpose", "of", "the", "python", "script", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2065898519", "fields": {"term": "What is the purpose of the feed.jsonl file?", "namespace": "open-p", "hash": -2065898519, "terms": ["What is the purpose of the feed.jsonl file?", "is the purpose of the feed.jsonl file?", "the purpose of the feed.jsonl file?", "purpose of the feed.jsonl file?", "of the feed.jsonl file?", "the feed.jsonl file?", "feed.jsonl file?", "file?"], "tokens": ["What", "is", "the", "purpose", "of", "the", "feed", "jsonl", "file", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-712465964", "fields": {"term": "What defines Vespa behavior?", "namespace": "open-p", "hash": -712465964, "terms": ["What defines Vespa behavior?", "defines Vespa behavior?", "Vespa behavior?", "behavior?"], "tokens": ["What", "defines", "Vespa", "behavior", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1595915414", "fields": {"term": "What are the minimum required files?", "namespace": "open-p", "hash": 1595915414, "terms": ["What are the minimum required files?", "are the minimum required files?", "the minimum required files?", "minimum required files?", "required files?", "files?"], "tokens": ["What", "are", "the", "minimum", "required", "files", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1032166550", "fields": {"term": "What is track.sd?", "namespace": "open-p", "hash": 1032166550, "terms": ["What is track.sd?", "is track.sd?", "track.sd?"], "tokens": ["What", "is", "track", "sd", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1753600033", "fields": {"term": "What directories are needed?", "namespace": "open-p", "hash": 1753600033, "terms": ["What directories are needed?", "directories are needed?", "are needed?", "needed?"], "tokens": ["What", "directories", "are", "needed", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1098803093", "fields": {"term": "How do the configuration files define data processing?", "namespace": "open-p", "hash": 1098803093, "terms": ["How do the configuration files define data processing?", "do the configuration files define data processing?", "the configuration files define data processing?", "configuration files define data processing?", "files define data processing?", "define data processing?", "data processing?", "processing?"], "tokens": ["How", "do", "the", "configuration", "files", "define", "data", "processing", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1283414794", "fields": {"term": "What is a Vespa schema?", "namespace": "open-p", "hash": 1283414794, "terms": ["What is a Vespa schema?", "is a Vespa schema?", "a Vespa schema?", "Vespa schema?", "schema?"], "tokens": ["What", "is", "a", "Vespa", "schema", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::274360417", "fields": {"term": "What is a track schema?", "namespace": "open-p", "hash": 274360417, "terms": ["What is a track schema?", "is a track schema?", "a track schema?", "track schema?", "schema?"], "tokens": ["What", "is", "a", "track", "schema", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1554651536", "fields": {"term": "What is the 'track_id' field for?", "namespace": "open-p", "hash": 1554651536, "terms": ["What is the 'track_id' field for?", "is the 'track_id' field for?", "the 'track_id' field for?", "'track_id' field for?", "field for?", "for?"], "tokens": ["What", "is", "the", "'", "track_id", "'", "field", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1833508760", "fields": {"term": "How can you override default query settings?", "namespace": "open-p", "hash": 1833508760, "terms": ["How can you override default query settings?", "can you override default query settings?", "you override default query settings?", "override default query settings?", "default query settings?", "query settings?", "settings?"], "tokens": ["How", "can", "you", "override", "default", "query", "settings", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1320699475", "fields": {"term": "What is the purpose of 'presentation.timing' field?", "namespace": "open-p", "hash": -1320699475, "terms": ["What is the purpose of 'presentation.timing' field?", "is the purpose of 'presentation.timing' field?", "the purpose of 'presentation.timing' field?", "purpose of 'presentation.timing' field?", "of 'presentation.timing' field?", "'presentation.timing' field?", "field?"], "tokens": ["What", "is", "the", "purpose", "of", "'", "presentation", "timing", "'", "field", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1743419777", "fields": {"term": "How can you render 'weightedset' fields?", "namespace": "open-p", "hash": -1743419777, "terms": ["How can you render 'weightedset' fields?", "can you render 'weightedset' fields?", "you render 'weightedset' fields?", "render 'weightedset' fields?", "'weightedset' fields?", "fields?"], "tokens": ["How", "can", "you", "render", "'", "weightedset", "'", "fields", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::954072198", "fields": {"term": "How to deploy Vespa instance?", "namespace": "open-p", "hash": 954072198, "terms": ["How to deploy Vespa instance?", "to deploy Vespa instance?", "deploy Vespa instance?", "Vespa instance?", "instance?"], "tokens": ["How", "to", "deploy", "Vespa", "instance", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1850393934", "fields": {"term": "How to formulate query?", "namespace": "open-p", "hash": 1850393934, "terms": ["How to formulate query?", "to formulate query?", "formulate query?", "query?"], "tokens": ["How", "to", "formulate", "query", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1216838000", "fields": {"term": "What factors determine summaryfetchtime?", "namespace": "open-p", "hash": -1216838000, "terms": ["What factors determine summaryfetchtime?", "factors determine summaryfetchtime?", "determine summaryfetchtime?", "summaryfetchtime?"], "tokens": ["What", "factors", "determine", "summaryfetchtime", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1570105138", "fields": {"term": "What can improve summaryfetchtime?", "namespace": "open-p", "hash": 1570105138, "terms": ["What can improve summaryfetchtime?", "can improve summaryfetchtime?", "improve summaryfetchtime?", "summaryfetchtime?"], "tokens": ["What", "can", "improve", "summaryfetchtime", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::864248004", "fields": {"term": "What is the benefit of using a dedicated document-summary?", "namespace": "open-p", "hash": 864248004, "terms": ["What is the benefit of using a dedicated document-summary?", "is the benefit of using a dedicated document-summary?", "the benefit of using a dedicated document-summary?", "benefit of using a dedicated document-summary?", "of using a dedicated document-summary?", "using a dedicated document-summary?", "a dedicated document-summary?", "dedicated document-summary?", "document-summary?"], "tokens": ["What", "is", "the", "benefit", "of", "using", "a", "dedicated", "document", "summary", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1905278705", "fields": {"term": "What is the difference between attribute and non-attribute fields in a summary?", "namespace": "open-p", "hash": -1905278705, "terms": ["What is the difference between attribute and non-attribute fields in a summary?", "is the difference between attribute and non-attribute fields in a summary?", "the difference between attribute and non-attribute fields in a summary?", "difference between attribute and non-attribute fields in a summary?", "between attribute and non-attribute fields in a summary?", "attribute and non-attribute fields in a summary?", "and non-attribute fields in a summary?", "non-attribute fields in a summary?", "fields in a summary?", "in a summary?", "a summary?", "summary?"], "tokens": ["What", "is", "the", "difference", "between", "attribute", "and", "non", "attribute", "fields", "in", "a", "summary", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::730473765", "fields": {"term": "What are summary-features?", "namespace": "open-p", "hash": 730473765, "terms": ["What are summary-features?", "are summary-features?", "summary-features?"], "tokens": ["What", "are", "summary", "features", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::1816228310", "fields": {"term": "What is a summary fill?", "namespace": "open-p", "hash": 1816228310, "terms": ["What is a summary fill?", "is a summary fill?", "a summary fill?", "summary fill?", "fill?"], "tokens": ["What", "is", "a", "summary", "fill", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-266441934", "fields": {"term": "What is a weightedset field?", "namespace": "open-p", "hash": -266441934, "terms": ["What is a weightedset field?", "is a weightedset field?", "a weightedset field?", "weightedset field?", "field?"], "tokens": ["What", "is", "a", "weightedset", "field", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2039966659", "fields": {"term": "How is a tags field matched?", "namespace": "open-p", "hash": -2039966659, "terms": ["How is a tags field matched?", "is a tags field matched?", "a tags field matched?", "tags field matched?", "field matched?", "matched?"], "tokens": ["How", "is", "a", "tags", "field", "matched", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-44125727", "fields": {"term": "What is free text searching?", "namespace": "open-p", "hash": -44125727, "terms": ["What is free text searching?", "is free text searching?", "free text searching?", "text searching?", "searching?"], "tokens": ["What", "is", "free", "text", "searching", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::103970615", "fields": {"term": "What is the reason for attribute fields not having inverted index?", "namespace": "open-p", "hash": 103970615, "terms": ["What is the reason for attribute fields not having inverted index?", "is the reason for attribute fields not having inverted index?", "the reason for attribute fields not having inverted index?", "reason for attribute fields not having inverted index?", "for attribute fields not having inverted index?", "attribute fields not having inverted index?", "fields not having inverted index?", "not having inverted index?", "having inverted index?", "inverted index?", "index?"], "tokens": ["What", "is", "the", "reason", "for", "attribute", "fields", "not", "having", "inverted", "index", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::776593975", "fields": {"term": "How is match:text different from match:word?", "namespace": "open-p", "hash": 776593975, "terms": ["How is match:text different from match:word?", "is match:text different from match:word?", "match:text different from match:word?", "different from match:word?", "from match:word?", "match:word?"], "tokens": ["How", "is", "match", ":", "text", "different", "from", "match", ":", "word", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1335554973", "fields": {"term": "How is a posting list represented in Vespa?", "namespace": "open-p", "hash": 1335554973, "terms": ["How is a posting list represented in Vespa?", "is a posting list represented in Vespa?", "a posting list represented in Vespa?", "posting list represented in Vespa?", "list represented in Vespa?", "represented in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["How", "is", "a", "posting", "list", "represented", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1623321986", "fields": {"term": "What is `fast-search`?", "namespace": "open-p", "hash": 1623321986, "terms": ["What is `fast-search`?", "is `fast-search`?", "`fast-search`?"], "tokens": ["What", "is", "`", "fast", "search", "`?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-573458087", "fields": {"term": "What fields use `fast-search`?", "namespace": "open-p", "hash": -573458087, "terms": ["What fields use `fast-search`?", "fields use `fast-search`?", "use `fast-search`?", "`fast-search`?"], "tokens": ["What", "fields", "use", "`", "fast", "search", "`?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2062858916", "fields": {"term": "How does `fast-search` affect memory usage?", "namespace": "open-p", "hash": 2062858916, "terms": ["How does `fast-search` affect memory usage?", "does `fast-search` affect memory usage?", "`fast-search` affect memory usage?", "affect memory usage?", "memory usage?", "usage?"], "tokens": ["How", "does", "`", "fast", "search", "`", "affect", "memory", "usage", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::335050629", "fields": {"term": "What are the benefits of using `fast-search`?", "namespace": "open-p", "hash": 335050629, "terms": ["What are the benefits of using `fast-search`?", "are the benefits of using `fast-search`?", "the benefits of using `fast-search`?", "benefits of using `fast-search`?", "of using `fast-search`?", "using `fast-search`?", "`fast-search`?"], "tokens": ["What", "are", "the", "benefits", "of", "using", "`", "fast", "search", "`?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::745163050", "fields": {"term": "How do you enable `fast-search`?", "namespace": "open-p", "hash": 745163050, "terms": ["How do you enable `fast-search`?", "do you enable `fast-search`?", "you enable `fast-search`?", "enable `fast-search`?", "`fast-search`?"], "tokens": ["How", "do", "you", "enable", "`", "fast", "search", "`?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1847842002", "fields": {"term": "What is the downside of enabling `fast-search`?", "namespace": "open-p", "hash": -1847842002, "terms": ["What is the downside of enabling `fast-search`?", "is the downside of enabling `fast-search`?", "the downside of enabling `fast-search`?", "downside of enabling `fast-search`?", "of enabling `fast-search`?", "enabling `fast-search`?", "`fast-search`?"], "tokens": ["What", "is", "the", "downside", "of", "enabling", "`", "fast", "search", "`?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1856891272", "fields": {"term": "What is the dotProduct query operator?", "namespace": "open-p", "hash": -1856891272, "terms": ["What is the dotProduct query operator?", "is the dotProduct query operator?", "the dotProduct query operator?", "dotProduct query operator?", "query operator?", "operator?"], "tokens": ["What", "is", "the", "dotProduct", "query", "operator", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-687317418", "fields": {"term": "What is the wand query operator?", "namespace": "open-p", "hash": -687317418, "terms": ["What is the wand query operator?", "is the wand query operator?", "the wand query operator?", "wand query operator?", "query operator?", "operator?"], "tokens": ["What", "is", "the", "wand", "query", "operator", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1680599353", "fields": {"term": "What is rawScore?", "namespace": "open-p", "hash": -1680599353, "terms": ["What is rawScore?", "is rawScore?", "rawScore?"], "tokens": ["What", "is", "rawScore", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-248865481", "fields": {"term": "What is the difference between wand and dotProduct?", "namespace": "open-p", "hash": -248865481, "terms": ["What is the difference between wand and dotProduct?", "is the difference between wand and dotProduct?", "the difference between wand and dotProduct?", "difference between wand and dotProduct?", "between wand and dotProduct?", "wand and dotProduct?", "and dotProduct?", "dotProduct?"], "tokens": ["What", "is", "the", "difference", "between", "wand", "and", "dotProduct", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1504579276", "fields": {"term": "What are multi-value query operators used for?", "namespace": "open-p", "hash": 1504579276, "terms": ["What are multi-value query operators used for?", "are multi-value query operators used for?", "multi-value query operators used for?", "query operators used for?", "operators used for?", "used for?", "for?"], "tokens": ["What", "are", "multi", "value", "query", "operators", "used", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1040413679", "fields": {"term": "What is tensor computation?", "namespace": "open-p", "hash": -1040413679, "terms": ["What is tensor computation?", "is tensor computation?", "tensor computation?", "computation?"], "tokens": ["What", "is", "tensor", "computation", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-532269454", "fields": {"term": "What is an example of tensor computation?", "namespace": "open-p", "hash": -532269454, "terms": ["What is an example of tensor computation?", "is an example of tensor computation?", "an example of tensor computation?", "example of tensor computation?", "of tensor computation?", "tensor computation?", "computation?"], "tokens": ["What", "is", "an", "example", "of", "tensor", "computation", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1356090105", "fields": {"term": "How can tensor computations be used in Vespa?", "namespace": "open-p", "hash": 1356090105, "terms": ["How can tensor computations be used in Vespa?", "can tensor computations be used in Vespa?", "tensor computations be used in Vespa?", "computations be used in Vespa?", "be used in Vespa?", "used in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["How", "can", "tensor", "computations", "be", "used", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-941265959", "fields": {"term": "How can document tensors and query tensors be defined in Vespa?", "namespace": "open-p", "hash": -941265959, "terms": ["How can document tensors and query tensors be defined in Vespa?", "can document tensors and query tensors be defined in Vespa?", "document tensors and query tensors be defined in Vespa?", "tensors and query tensors be defined in Vespa?", "and query tensors be defined in Vespa?", "query tensors be defined in Vespa?", "tensors be defined in Vespa?", "be defined in Vespa?", "defined in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["How", "can", "document", "tensors", "and", "query", "tensors", "be", "defined", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::707014843", "fields": {"term": "What is the purpose of the similar field in the track schema?", "namespace": "open-p", "hash": 707014843, "terms": ["What is the purpose of the similar field in the track schema?", "is the purpose of the similar field in the track schema?", "the purpose of the similar field in the track schema?", "purpose of the similar field in the track schema?", "of the similar field in the track schema?", "the similar field in the track schema?", "similar field in the track schema?", "field in the track schema?", "in the track schema?", "the track schema?", "track schema?", "schema?"], "tokens": ["What", "is", "the", "purpose", "of", "the", "similar", "field", "in", "the", "track", "schema", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-263687673", "fields": {"term": "How can documents be ranked using tensor computations in Vespa?", "namespace": "open-p", "hash": -263687673, "terms": ["How can documents be ranked using tensor computations in Vespa?", "can documents be ranked using tensor computations in Vespa?", "documents be ranked using tensor computations in Vespa?", "be ranked using tensor computations in Vespa?", "ranked using tensor computations in Vespa?", "using tensor computations in Vespa?", "tensor computations in Vespa?", "computations in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["How", "can", "documents", "be", "ranked", "using", "tensor", "computations", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::352007637", "fields": {"term": "What is multithreaded execution?", "namespace": "open-p", "hash": 352007637, "terms": ["What is multithreaded execution?", "is multithreaded execution?", "multithreaded execution?", "execution?"], "tokens": ["What", "is", "multithreaded", "execution", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-137664523", "fields": {"term": "How to enable multithreaded execution?", "namespace": "open-p", "hash": -137664523, "terms": ["How to enable multithreaded execution?", "to enable multithreaded execution?", "enable multithreaded execution?", "multithreaded execution?", "execution?"], "tokens": ["How", "to", "enable", "multithreaded", "execution", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::513786232", "fields": {"term": "What are the benefits of multithreaded search?", "namespace": "open-p", "hash": 513786232, "terms": ["What are the benefits of multithreaded search?", "are the benefits of multithreaded search?", "the benefits of multithreaded search?", "benefits of multithreaded search?", "of multithreaded search?", "multithreaded search?", "search?"], "tokens": ["What", "are", "the", "benefits", "of", "multithreaded", "search", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1446997956", "fields": {"term": "What is requestthreads:persearch?", "namespace": "open-p", "hash": -1446997956, "terms": ["What is requestthreads:persearch?", "is requestthreads:persearch?", "requestthreads:persearch?"], "tokens": ["What", "is", "requestthreads", ":", "persearch", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-267342189", "fields": {"term": "How do you change global threads per search?", "namespace": "open-p", "hash": -267342189, "terms": ["How do you change global threads per search?", "do you change global threads per search?", "you change global threads per search?", "change global threads per search?", "global threads per search?", "threads per search?", "per search?", "search?"], "tokens": ["How", "do", "you", "change", "global", "threads", "per", "search", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1995182233", "fields": {"term": "What happens when you change threads per search?", "namespace": "open-p", "hash": -1995182233, "terms": ["What happens when you change threads per search?", "happens when you change threads per search?", "when you change threads per search?", "you change threads per search?", "change threads per search?", "threads per search?", "per search?", "search?"], "tokens": ["What", "happens", "when", "you", "change", "threads", "per", "search", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::194211136", "fields": {"term": "What is num-threads-per-search?", "namespace": "open-p", "hash": 194211136, "terms": ["What is num-threads-per-search?", "is num-threads-per-search?", "num-threads-per-search?"], "tokens": ["What", "is", "num", "threads", "per", "search", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::-1697969218", "fields": {"term": "What is `popularity`?", "namespace": "open-p", "hash": -1697969218, "terms": ["What is `popularity`?", "is `popularity`?", "`popularity`?"], "tokens": ["What", "is", "`", "popularity", "`?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1391792994", "fields": {"term": "What is the `range()` query operator?", "namespace": "open-p", "hash": 1391792994, "terms": ["What is the `range()` query operator?", "is the `range()` query operator?", "the `range()` query operator?", "`range()` query operator?", "query operator?", "operator?"], "tokens": ["What", "is", "the", "`", "range", "()`", "query", "operator", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::724306920", "fields": {"term": "What is the `hitLimit` annotation?", "namespace": "open-p", "hash": 724306920, "terms": ["What is the `hitLimit` annotation?", "is the `hitLimit` annotation?", "the `hitLimit` annotation?", "`hitLimit` annotation?", "annotation?"], "tokens": ["What", "is", "the", "`", "hitLimit", "`", "annotation", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-472040703", "fields": {"term": "What use cases are suitable for using the `range()` operator?", "namespace": "open-p", "hash": -472040703, "terms": ["What use cases are suitable for using the `range()` operator?", "use cases are suitable for using the `range()` operator?", "cases are suitable for using the `range()` operator?", "are suitable for using the `range()` operator?", "suitable for using the `range()` operator?", "for using the `range()` operator?", "using the `range()` operator?", "the `range()` operator?", "`range()` operator?", "operator?"], "tokens": ["What", "use", "cases", "are", "suitable", "for", "using", "the", "`", "range", "()`", "operator", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-370465386", "fields": {"term": "What is the practical use of the `range()` operator in auto-complete or search suggestions?", "namespace": "open-p", "hash": -370465386, "terms": ["What is the practical use of the `range()` operator in auto-complete or search suggestions?", "is the practical use of the `range()` operator in auto-complete or search suggestions?", "the practical use of the `range()` operator in auto-complete or search suggestions?", "practical use of the `range()` operator in auto-complete or search suggestions?", "use of the `range()` operator in auto-complete or search suggestions?", "of the `range()` operator in auto-complete or search suggestions?", "the `range()` operator in auto-complete or search suggestions?", "`range()` operator in auto-complete or search suggestions?", "operator in auto-complete or search suggestions?", "in auto-complete or search suggestions?", "auto-complete or search suggestions?", "or search suggestions?", "search suggestions?", "suggestions?"], "tokens": ["What", "is", "the", "practical", "use", "of", "the", "`", "range", "()`", "operator", "in", "auto", "complete", "or", "search", "suggestions", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-583882234", "fields": {"term": "What is match-phase early termination?", "namespace": "open-p", "hash": -583882234, "terms": ["What is match-phase early termination?", "is match-phase early termination?", "match-phase early termination?", "early termination?", "termination?"], "tokens": ["What", "is", "match", "phase", "early", "termination", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-499989328", "fields": {"term": "How does match-phase early termination work?", "namespace": "open-p", "hash": -499989328, "terms": ["How does match-phase early termination work?", "does match-phase early termination work?", "match-phase early termination work?", "early termination work?", "termination work?", "work?"], "tokens": ["How", "does", "match", "phase", "early", "termination", "work", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-149601758", "fields": {"term": "What field attribute is required for match-phase early termination?", "namespace": "open-p", "hash": -149601758, "terms": ["What field attribute is required for match-phase early termination?", "field attribute is required for match-phase early termination?", "attribute is required for match-phase early termination?", "is required for match-phase early termination?", "required for match-phase early termination?", "for match-phase early termination?", "match-phase early termination?", "early termination?", "termination?"], "tokens": ["What", "field", "attribute", "is", "required", "for", "match", "phase", "early", "termination", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-759920889", "fields": {"term": "How can match-phase limit be used with text search queries?", "namespace": "open-p", "hash": -759920889, "terms": ["How can match-phase limit be used with text search queries?", "can match-phase limit be used with text search queries?", "match-phase limit be used with text search queries?", "limit be used with text search queries?", "be used with text search queries?", "used with text search queries?", "with text search queries?", "text search queries?", "search queries?", "queries?"], "tokens": ["How", "can", "match", "phase", "limit", "be", "used", "with", "text", "search", "queries", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1932311164", "fields": {"term": "What is the difference between match-phase early termination and range search with hitLimit?", "namespace": "open-p", "hash": -1932311164, "terms": ["What is the difference between match-phase early termination and range search with hitLimit?", "is the difference between match-phase early termination and range search with hitLimit?", "the difference between match-phase early termination and range search with hitLimit?", "difference between match-phase early termination and range search with hitLimit?", "between match-phase early termination and range search with hitLimit?", "match-phase early termination and range search with hitLimit?", "early termination and range search with hitLimit?", "termination and range search with hitLimit?", "and range search with hitLimit?", "range search with hitLimit?", "search with hitLimit?", "with hitLimit?", "hitLimit?"], "tokens": ["What", "is", "the", "difference", "between", "match", "phase", "early", "termination", "and", "range", "search", "with", "hitLimit", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1415306439", "fields": {"term": "What result diversity constraint is supported by match-phase early termination?", "namespace": "open-p", "hash": -1415306439, "terms": ["What result diversity constraint is supported by match-phase early termination?", "result diversity constraint is supported by match-phase early termination?", "diversity constraint is supported by match-phase early termination?", "constraint is supported by match-phase early termination?", "is supported by match-phase early termination?", "supported by match-phase early termination?", "by match-phase early termination?", "match-phase early termination?", "early termination?", "termination?"], "tokens": ["What", "result", "diversity", "constraint", "is", "supported", "by", "match", "phase", "early", "termination", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1432107093", "fields": {"term": "What is the recommended setting for low settings of maxHits in the rank-profile?", "namespace": "open-p", "hash": 1432107093, "terms": ["What is the recommended setting for low settings of maxHits in the rank-profile?", "is the recommended setting for low settings of maxHits in the rank-profile?", "the recommended setting for low settings of maxHits in the rank-profile?", "recommended setting for low settings of maxHits in the rank-profile?", "setting for low settings of maxHits in the rank-profile?", "for low settings of maxHits in the rank-profile?", "low settings of maxHits in the rank-profile?", "settings of maxHits in the rank-profile?", "of maxHits in the rank-profile?", "maxHits in the rank-profile?", "in the rank-profile?", "the rank-profile?", "rank-profile?"], "tokens": ["What", "is", "the", "recommended", "setting", "for", "low", "settings", "of", "maxHits", "in", "the", "rank", "profile", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1360377363", "fields": {"term": "How to enable query tracing?", "namespace": "open-p", "hash": -1360377363, "terms": ["How to enable query tracing?", "to enable query tracing?", "enable query tracing?", "query tracing?", "tracing?"], "tokens": ["How", "to", "enable", "query", "tracing", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1311167874", "fields": {"term": "What are the parameters to enable tracing?", "namespace": "open-p", "hash": 1311167874, "terms": ["What are the parameters to enable tracing?", "are the parameters to enable tracing?", "the parameters to enable tracing?", "parameters to enable tracing?", "to enable tracing?", "enable tracing?", "tracing?"], "tokens": ["What", "are", "the", "parameters", "to", "enable", "tracing", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1144523607", "fields": {"term": "How is tracing implemented?", "namespace": "open-p", "hash": 1144523607, "terms": ["How is tracing implemented?", "is tracing implemented?", "tracing implemented?", "implemented?"], "tokens": ["How", "is", "tracing", "implemented", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::753596450", "fields": {"term": "What information is included in traces?", "namespace": "open-p", "hash": 753596450, "terms": ["What information is included in traces?", "information is included in traces?", "is included in traces?", "included in traces?", "in traces?", "traces?"], "tokens": ["What", "information", "is", "included", "in", "traces", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::478462686", "fields": {"term": "How can traces help in feature tuning decisions?", "namespace": "open-p", "hash": 478462686, "terms": ["How can traces help in feature tuning decisions?", "can traces help in feature tuning decisions?", "traces help in feature tuning decisions?", "help in feature tuning decisions?", "in feature tuning decisions?", "feature tuning decisions?", "tuning decisions?", "decisions?"], "tokens": ["How", "can", "traces", "help", "in", "feature", "tuning", "decisions", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1331182500", "fields": {"term": "What is the Trace Visualizer?", "namespace": "open-p", "hash": -1331182500, "terms": ["What is the Trace Visualizer?", "is the Trace Visualizer?", "the Trace Visualizer?", "Trace Visualizer?", "Visualizer?"], "tokens": ["What", "is", "the", "Trace", "Visualizer", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1689116912", "fields": {"term": "What is the response time for the first protocol phase?", "namespace": "open-p", "hash": -1689116912, "terms": ["What is the response time for the first protocol phase?", "is the response time for the first protocol phase?", "the response time for the first protocol phase?", "response time for the first protocol phase?", "time for the first protocol phase?", "for the first protocol phase?", "the first protocol phase?", "first protocol phase?", "protocol phase?", "phase?"], "tokens": ["What", "is", "the", "response", "time", "for", "the", "first", "protocol", "phase", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::119662227", "fields": {"term": "What does the tutorial conclude?", "namespace": "open-p", "hash": 119662227, "terms": ["What does the tutorial conclude?", "does the tutorial conclude?", "the tutorial conclude?", "tutorial conclude?", "conclude?"], "tokens": ["What", "does", "the", "tutorial", "conclude", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1429199449", "fields": {"term": "What are predicate fields?", "namespace": "open-p", "hash": 1429199449, "terms": ["What are predicate fields?", "are predicate fields?", "predicate fields?", "fields?"], "tokens": ["What", "are", "predicate", "fields", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::686822899", "fields": {"term": "What is a boolean constraint?", "namespace": "open-p", "hash": 686822899, "terms": ["What is a boolean constraint?", "is a boolean constraint?", "a boolean constraint?", "boolean constraint?", "constraint?"], "tokens": ["What", "is", "a", "boolean", "constraint", "?"], "corpus_count": 3, "document_count": 3}}, -{"put": "id:term:term::-1740111882", "fields": {"term": "What is a typical use case for predicate fields?", "namespace": "open-p", "hash": -1740111882, "terms": ["What is a typical use case for predicate fields?", "is a typical use case for predicate fields?", "a typical use case for predicate fields?", "typical use case for predicate fields?", "use case for predicate fields?", "case for predicate fields?", "for predicate fields?", "predicate fields?", "fields?"], "tokens": ["What", "is", "a", "typical", "use", "case", "for", "predicate", "fields", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-802019979", "fields": {"term": "What trade-offs are there when configuring predicate fields?", "namespace": "open-p", "hash": -802019979, "terms": ["What trade-offs are there when configuring predicate fields?", "trade-offs are there when configuring predicate fields?", "are there when configuring predicate fields?", "there when configuring predicate fields?", "when configuring predicate fields?", "configuring predicate fields?", "predicate fields?", "fields?"], "tokens": ["What", "trade", "offs", "are", "there", "when", "configuring", "predicate", "fields", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2020131860", "fields": {"term": "What can be the attributes of a boolean constraint?", "namespace": "open-p", "hash": 2020131860, "terms": ["What can be the attributes of a boolean constraint?", "can be the attributes of a boolean constraint?", "be the attributes of a boolean constraint?", "the attributes of a boolean constraint?", "attributes of a boolean constraint?", "of a boolean constraint?", "a boolean constraint?", "boolean constraint?", "constraint?"], "tokens": ["What", "can", "be", "the", "attributes", "of", "a", "boolean", "constraint", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::938400030", "fields": {"term": "What does a boolean constraint specify?", "namespace": "open-p", "hash": 938400030, "terms": ["What does a boolean constraint specify?", "does a boolean constraint specify?", "a boolean constraint specify?", "boolean constraint specify?", "constraint specify?", "specify?"], "tokens": ["What", "does", "a", "boolean", "constraint", "specify", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2083892428", "fields": {"term": "What are the criteria for attributes in a boolean constraint?", "namespace": "open-p", "hash": -2083892428, "terms": ["What are the criteria for attributes in a boolean constraint?", "are the criteria for attributes in a boolean constraint?", "the criteria for attributes in a boolean constraint?", "criteria for attributes in a boolean constraint?", "for attributes in a boolean constraint?", "attributes in a boolean constraint?", "in a boolean constraint?", "a boolean constraint?", "boolean constraint?", "constraint?"], "tokens": ["What", "are", "the", "criteria", "for", "attributes", "in", "a", "boolean", "constraint", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1087828671", "fields": {"term": "What is a range of values in a boolean constraint?", "namespace": "open-p", "hash": 1087828671, "terms": ["What is a range of values in a boolean constraint?", "is a range of values in a boolean constraint?", "a range of values in a boolean constraint?", "range of values in a boolean constraint?", "of values in a boolean constraint?", "values in a boolean constraint?", "in a boolean constraint?", "a boolean constraint?", "boolean constraint?", "constraint?"], "tokens": ["What", "is", "a", "range", "of", "values", "in", "a", "boolean", "constraint", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::312584590", "fields": {"term": "What is a subset of sets in a boolean constraint?", "namespace": "open-p", "hash": 312584590, "terms": ["What is a subset of sets in a boolean constraint?", "is a subset of sets in a boolean constraint?", "a subset of sets in a boolean constraint?", "subset of sets in a boolean constraint?", "of sets in a boolean constraint?", "sets in a boolean constraint?", "in a boolean constraint?", "a boolean constraint?", "boolean constraint?", "constraint?"], "tokens": ["What", "is", "a", "subset", "of", "sets", "in", "a", "boolean", "constraint", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-601080694", "fields": {"term": "How do queries land in a boolean constraint?", "namespace": "open-p", "hash": -601080694, "terms": ["How do queries land in a boolean constraint?", "do queries land in a boolean constraint?", "queries land in a boolean constraint?", "land in a boolean constraint?", "in a boolean constraint?", "a boolean constraint?", "boolean constraint?", "constraint?"], "tokens": ["How", "do", "queries", "land", "in", "a", "boolean", "constraint", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-86660388", "fields": {"term": "What is a predicate in Vespa.ai?", "namespace": "open-p", "hash": -86660388, "terms": ["What is a predicate in Vespa.ai?", "is a predicate in Vespa.ai?", "a predicate in Vespa.ai?", "predicate in Vespa.ai?", "in Vespa.ai?", "Vespa.ai?"], "tokens": ["What", "is", "a", "predicate", "in", "Vespa", "ai", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::469821376", "fields": {"term": "What is a predicate?", "namespace": "open-p", "hash": 469821376, "terms": ["What is a predicate?", "is a predicate?", "a predicate?", "predicate?"], "tokens": ["What", "is", "a", "predicate", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1524101618", "fields": {"term": "What is the grammar for valid expressions?", "namespace": "open-p", "hash": 1524101618, "terms": ["What is the grammar for valid expressions?", "is the grammar for valid expressions?", "the grammar for valid expressions?", "grammar for valid expressions?", "for valid expressions?", "valid expressions?", "expressions?"], "tokens": ["What", "is", "the", "grammar", "for", "valid", "expressions", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-439049426", "fields": {"term": "What is a leaf in a predicate?", "namespace": "open-p", "hash": -439049426, "terms": ["What is a leaf in a predicate?", "is a leaf in a predicate?", "a leaf in a predicate?", "leaf in a predicate?", "in a predicate?", "a predicate?", "predicate?"], "tokens": ["What", "is", "a", "leaf", "in", "a", "predicate", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1007381533", "fields": {"term": "What is an example of a predicate?", "namespace": "open-p", "hash": -1007381533, "terms": ["What is an example of a predicate?", "is an example of a predicate?", "an example of a predicate?", "example of a predicate?", "of a predicate?", "a predicate?", "predicate?"], "tokens": ["What", "is", "an", "example", "of", "a", "predicate", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-838935284", "fields": {"term": "What is a disjunction in a predicate?", "namespace": "open-p", "hash": -838935284, "terms": ["What is a disjunction in a predicate?", "is a disjunction in a predicate?", "a disjunction in a predicate?", "disjunction in a predicate?", "in a predicate?", "a predicate?", "predicate?"], "tokens": ["What", "is", "a", "disjunction", "in", "a", "predicate", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::191618114", "fields": {"term": "What is a conjunction in a predicate?", "namespace": "open-p", "hash": 191618114, "terms": ["What is a conjunction in a predicate?", "is a conjunction in a predicate?", "a conjunction in a predicate?", "conjunction in a predicate?", "in a predicate?", "a predicate?", "predicate?"], "tokens": ["What", "is", "a", "conjunction", "in", "a", "predicate", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1753516702", "fields": {"term": "What is the correct syntax for a multivalue in a predicate?", "namespace": "open-p", "hash": 1753516702, "terms": ["What is the correct syntax for a multivalue in a predicate?", "is the correct syntax for a multivalue in a predicate?", "the correct syntax for a multivalue in a predicate?", "correct syntax for a multivalue in a predicate?", "syntax for a multivalue in a predicate?", "for a multivalue in a predicate?", "a multivalue in a predicate?", "multivalue in a predicate?", "in a predicate?", "a predicate?", "predicate?"], "tokens": ["What", "is", "the", "correct", "syntax", "for", "a", "multivalue", "in", "a", "predicate", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1997029829", "fields": {"term": "What are regular attributes?", "namespace": "open-p", "hash": -1997029829, "terms": ["What are regular attributes?", "are regular attributes?", "regular attributes?", "attributes?"], "tokens": ["What", "are", "regular", "attributes", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1385835136", "fields": {"term": "What is a range attribute?", "namespace": "open-p", "hash": 1385835136, "terms": ["What is a range attribute?", "is a range attribute?", "a range attribute?", "range attribute?", "attribute?"], "tokens": ["What", "is", "a", "range", "attribute", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1576291039", "fields": {"term": "What values do regular attributes take?", "namespace": "open-p", "hash": 1576291039, "terms": ["What values do regular attributes take?", "values do regular attributes take?", "do regular attributes take?", "regular attributes take?", "attributes take?", "take?"], "tokens": ["What", "values", "do", "regular", "attributes", "take", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2132407900", "fields": {"term": "What values do range attributes take?", "namespace": "open-p", "hash": 2132407900, "terms": ["What values do range attributes take?", "values do range attributes take?", "do range attributes take?", "range attributes take?", "attributes take?", "take?"], "tokens": ["What", "values", "do", "range", "attributes", "take", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-606522868", "fields": {"term": "What is a range expression?", "namespace": "open-p", "hash": -606522868, "terms": ["What is a range expression?", "is a range expression?", "a range expression?", "range expression?", "expression?"], "tokens": ["What", "is", "a", "range", "expression", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1320431387", "fields": {"term": "What are the three types of range expressions?", "namespace": "open-p", "hash": -1320431387, "terms": ["What are the three types of range expressions?", "are the three types of range expressions?", "the three types of range expressions?", "three types of range expressions?", "types of range expressions?", "of range expressions?", "range expressions?", "expressions?"], "tokens": ["What", "are", "the", "three", "types", "of", "range", "expressions", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-395292791", "fields": {"term": "How do you use a regular attribute in a predicate?", "namespace": "open-p", "hash": -395292791, "terms": ["How do you use a regular attribute in a predicate?", "do you use a regular attribute in a predicate?", "you use a regular attribute in a predicate?", "use a regular attribute in a predicate?", "a regular attribute in a predicate?", "regular attribute in a predicate?", "attribute in a predicate?", "in a predicate?", "a predicate?", "predicate?"], "tokens": ["How", "do", "you", "use", "a", "regular", "attribute", "in", "a", "predicate", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1244477485", "fields": {"term": "How do you use a range attribute in a predicate?", "namespace": "open-p", "hash": -1244477485, "terms": ["How do you use a range attribute in a predicate?", "do you use a range attribute in a predicate?", "you use a range attribute in a predicate?", "use a range attribute in a predicate?", "a range attribute in a predicate?", "range attribute in a predicate?", "attribute in a predicate?", "in a predicate?", "a predicate?", "predicate?"], "tokens": ["How", "do", "you", "use", "a", "range", "attribute", "in", "a", "predicate", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1669648774", "fields": {"term": "What is a subset expression?", "namespace": "open-p", "hash": -1669648774, "terms": ["What is a subset expression?", "is a subset expression?", "a subset expression?", "subset expression?", "expression?"], "tokens": ["What", "is", "a", "subset", "expression", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1989984379", "fields": {"term": "How are non-alphanumeric letters used in Vespa predicates?", "namespace": "open-p", "hash": -1989984379, "terms": ["How are non-alphanumeric letters used in Vespa predicates?", "are non-alphanumeric letters used in Vespa predicates?", "non-alphanumeric letters used in Vespa predicates?", "letters used in Vespa predicates?", "used in Vespa predicates?", "in Vespa predicates?", "Vespa predicates?", "predicates?"], "tokens": ["How", "are", "non", "alphanumeric", "letters", "used", "in", "Vespa", "predicates", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1115225187", "fields": {"term": "What is a boolean query?", "namespace": "open-p", "hash": -1115225187, "terms": ["What is a boolean query?", "is a boolean query?", "a boolean query?", "boolean query?", "query?"], "tokens": ["What", "is", "a", "boolean", "query", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-135272412", "fields": {"term": "What are boolean constraints?", "namespace": "open-p", "hash": -135272412, "terms": ["What are boolean constraints?", "are boolean constraints?", "boolean constraints?", "constraints?"], "tokens": ["What", "are", "boolean", "constraints", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-86874377", "fields": {"term": "What is a regular attribute?", "namespace": "open-p", "hash": -86874377, "terms": ["What is a regular attribute?", "is a regular attribute?", "a regular attribute?", "regular attribute?", "attribute?"], "tokens": ["What", "is", "a", "regular", "attribute", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1426826093", "fields": {"term": "What kind of values can regular attributes have?", "namespace": "open-p", "hash": 1426826093, "terms": ["What kind of values can regular attributes have?", "kind of values can regular attributes have?", "of values can regular attributes have?", "values can regular attributes have?", "can regular attributes have?", "regular attributes have?", "attributes have?", "have?"], "tokens": ["What", "kind", "of", "values", "can", "regular", "attributes", "have", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1065136764", "fields": {"term": "What is the bitmap for?", "namespace": "open-p", "hash": -1065136764, "terms": ["What is the bitmap for?", "is the bitmap for?", "the bitmap for?", "bitmap for?", "for?"], "tokens": ["What", "is", "the", "bitmap", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-528292789", "fields": {"term": "Why use multiple queries at once?", "namespace": "open-p", "hash": -528292789, "terms": ["Why use multiple queries at once?", "use multiple queries at once?", "multiple queries at once?", "queries at once?", "at once?", "once?"], "tokens": ["Why", "use", "multiple", "queries", "at", "once", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-501045386", "fields": {"term": "What is a typical use case?", "namespace": "open-p", "hash": -501045386, "terms": ["What is a typical use case?", "is a typical use case?", "a typical use case?", "typical use case?", "use case?", "case?"], "tokens": ["What", "is", "a", "typical", "use", "case", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::361489183", "fields": {"term": "What happens with empty subqueries?", "namespace": "open-p", "hash": 361489183, "terms": ["What happens with empty subqueries?", "happens with empty subqueries?", "with empty subqueries?", "empty subqueries?", "subqueries?"], "tokens": ["What", "happens", "with", "empty", "subqueries", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1616356538", "fields": {"term": "What is a subquery?", "namespace": "open-p", "hash": -1616356538, "terms": ["What is a subquery?", "is a subquery?", "a subquery?", "subquery?"], "tokens": ["What", "is", "a", "subquery", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2138247693", "fields": {"term": "How are subqueries specified?", "namespace": "open-p", "hash": 2138247693, "terms": ["How are subqueries specified?", "are subqueries specified?", "subqueries specified?", "specified?"], "tokens": ["How", "are", "subqueries", "specified", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-660764473", "fields": {"term": "What is a valid subquery bitmap?", "namespace": "open-p", "hash": -660764473, "terms": ["What is a valid subquery bitmap?", "is a valid subquery bitmap?", "a valid subquery bitmap?", "valid subquery bitmap?", "subquery bitmap?", "bitmap?"], "tokens": ["What", "is", "a", "valid", "subquery", "bitmap", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1587718918", "fields": {"term": "What is the format of a subquery?", "namespace": "open-p", "hash": -1587718918, "terms": ["What is the format of a subquery?", "is the format of a subquery?", "the format of a subquery?", "format of a subquery?", "of a subquery?", "a subquery?", "subquery?"], "tokens": ["What", "is", "the", "format", "of", "a", "subquery", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1308191830", "fields": {"term": "How do you construct a query from subqueries?", "namespace": "open-p", "hash": 1308191830, "terms": ["How do you construct a query from subqueries?", "do you construct a query from subqueries?", "you construct a query from subqueries?", "construct a query from subqueries?", "a query from subqueries?", "query from subqueries?", "from subqueries?", "subqueries?"], "tokens": ["How", "do", "you", "construct", "a", "query", "from", "subqueries", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1962272093", "fields": {"term": "What are subqueries?", "namespace": "open-p", "hash": -1962272093, "terms": ["What are subqueries?", "are subqueries?", "subqueries?"], "tokens": ["What", "are", "subqueries", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1619952564", "fields": {"term": "What do you need to add to the schema when using subqueries?", "namespace": "open-p", "hash": -1619952564, "terms": ["What do you need to add to the schema when using subqueries?", "do you need to add to the schema when using subqueries?", "you need to add to the schema when using subqueries?", "need to add to the schema when using subqueries?", "to add to the schema when using subqueries?", "add to the schema when using subqueries?", "to the schema when using subqueries?", "the schema when using subqueries?", "schema when using subqueries?", "when using subqueries?", "using subqueries?", "subqueries?"], "tokens": ["What", "do", "you", "need", "to", "add", "to", "the", "schema", "when", "using", "subqueries", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1296900032", "fields": {"term": "How are subqueries reported?", "namespace": "open-p", "hash": -1296900032, "terms": ["How are subqueries reported?", "are subqueries reported?", "subqueries reported?", "reported?"], "tokens": ["How", "are", "subqueries", "reported", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-585825588", "fields": {"term": "What is the lower 32-bit summary feature?", "namespace": "open-p", "hash": -585825588, "terms": ["What is the lower 32-bit summary feature?", "is the lower 32-bit summary feature?", "the lower 32-bit summary feature?", "lower 32-bit summary feature?", "32-bit summary feature?", "summary feature?", "feature?"], "tokens": ["What", "is", "the", "lower", "32", "bit", "summary", "feature", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1350741390", "fields": {"term": "What is the upper 32-bit summary feature?", "namespace": "open-p", "hash": 1350741390, "terms": ["What is the upper 32-bit summary feature?", "is the upper 32-bit summary feature?", "the upper 32-bit summary feature?", "upper 32-bit summary feature?", "32-bit summary feature?", "summary feature?", "feature?"], "tokens": ["What", "is", "the", "upper", "32", "bit", "summary", "feature", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1859017498", "fields": {"term": "Where can you find an example of configuring a custom searcher for subqueries?", "namespace": "open-p", "hash": 1859017498, "terms": ["Where can you find an example of configuring a custom searcher for subqueries?", "can you find an example of configuring a custom searcher for subqueries?", "you find an example of configuring a custom searcher for subqueries?", "find an example of configuring a custom searcher for subqueries?", "an example of configuring a custom searcher for subqueries?", "example of configuring a custom searcher for subqueries?", "of configuring a custom searcher for subqueries?", "configuring a custom searcher for subqueries?", "a custom searcher for subqueries?", "custom searcher for subqueries?", "searcher for subqueries?", "for subqueries?", "subqueries?"], "tokens": ["Where", "can", "you", "find", "an", "example", "of", "configuring", "a", "custom", "searcher", "for", "subqueries", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-647335027", "fields": {"term": "What is the purpose of the subquery bitmap?", "namespace": "open-p", "hash": -647335027, "terms": ["What is the purpose of the subquery bitmap?", "is the purpose of the subquery bitmap?", "the purpose of the subquery bitmap?", "purpose of the subquery bitmap?", "of the subquery bitmap?", "the subquery bitmap?", "subquery bitmap?", "bitmap?"], "tokens": ["What", "is", "the", "purpose", "of", "the", "subquery", "bitmap", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-552030901", "fields": {"term": "What is required to retrieve the subquery bitmap?", "namespace": "open-p", "hash": -552030901, "terms": ["What is required to retrieve the subquery bitmap?", "is required to retrieve the subquery bitmap?", "required to retrieve the subquery bitmap?", "to retrieve the subquery bitmap?", "retrieve the subquery bitmap?", "the subquery bitmap?", "subquery bitmap?", "bitmap?"], "tokens": ["What", "is", "required", "to", "retrieve", "the", "subquery", "bitmap", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1601142228", "fields": {"term": "What is a subquery feature?", "namespace": "open-p", "hash": -1601142228, "terms": ["What is a subquery feature?", "is a subquery feature?", "a subquery feature?", "subquery feature?", "feature?"], "tokens": ["What", "is", "a", "subquery", "feature", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1457414358", "fields": {"term": "How can subqueries be used?", "namespace": "open-p", "hash": -1457414358, "terms": ["How can subqueries be used?", "can subqueries be used?", "subqueries be used?", "be used?", "used?"], "tokens": ["How", "can", "subqueries", "be", "used", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1198085507", "fields": {"term": "How is the subquery bitmap used?", "namespace": "open-p", "hash": 1198085507, "terms": ["How is the subquery bitmap used?", "is the subquery bitmap used?", "the subquery bitmap used?", "subquery bitmap used?", "bitmap used?", "used?"], "tokens": ["How", "is", "the", "subquery", "bitmap", "used", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::240429889", "fields": {"term": "How many subqueries are used?", "namespace": "open-p", "hash": 240429889, "terms": ["How many subqueries are used?", "many subqueries are used?", "subqueries are used?", "are used?", "used?"], "tokens": ["How", "many", "subqueries", "are", "used", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-38223974", "fields": {"term": "What does the target field represent?", "namespace": "open-p", "hash": -38223974, "terms": ["What does the target field represent?", "does the target field represent?", "the target field represent?", "target field represent?", "field represent?", "represent?"], "tokens": ["What", "does", "the", "target", "field", "represent", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1858499140", "fields": {"term": "What is a predicate field?", "namespace": "open-p", "hash": 1858499140, "terms": ["What is a predicate field?", "is a predicate field?", "a predicate field?", "predicate field?", "field?"], "tokens": ["What", "is", "a", "predicate", "field", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::1394003377", "fields": {"term": "How does dense-posting-list-threshold help?", "namespace": "open-p", "hash": 1394003377, "terms": ["How does dense-posting-list-threshold help?", "does dense-posting-list-threshold help?", "dense-posting-list-threshold help?", "help?"], "tokens": ["How", "does", "dense", "posting", "list", "threshold", "help", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1952291225", "fields": {"term": "What is the JSON format for predicates?", "namespace": "open-p", "hash": -1952291225, "terms": ["What is the JSON format for predicates?", "is the JSON format for predicates?", "the JSON format for predicates?", "JSON format for predicates?", "format for predicates?", "for predicates?", "predicates?"], "tokens": ["What", "is", "the", "JSON", "format", "for", "predicates", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1472499946", "fields": {"term": "What is the index definition for a predicate field?", "namespace": "open-p", "hash": -1472499946, "terms": ["What is the index definition for a predicate field?", "is the index definition for a predicate field?", "the index definition for a predicate field?", "index definition for a predicate field?", "definition for a predicate field?", "for a predicate field?", "a predicate field?", "predicate field?", "field?"], "tokens": ["What", "is", "the", "index", "definition", "for", "a", "predicate", "field", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-93387160", "fields": {"term": "What is the mandatory parameter called?", "namespace": "open-p", "hash": -93387160, "terms": ["What is the mandatory parameter called?", "is the mandatory parameter called?", "the mandatory parameter called?", "mandatory parameter called?", "parameter called?", "called?"], "tokens": ["What", "is", "the", "mandatory", "parameter", "called", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1391884493", "fields": {"term": "What are the lower and upper bounds?", "namespace": "open-p", "hash": 1391884493, "terms": ["What are the lower and upper bounds?", "are the lower and upper bounds?", "the lower and upper bounds?", "lower and upper bounds?", "and upper bounds?", "upper bounds?", "bounds?"], "tokens": ["What", "are", "the", "lower", "and", "upper", "bounds", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1563402367", "fields": {"term": "What is the dense-posting-list-threshold?", "namespace": "open-p", "hash": 1563402367, "terms": ["What is the dense-posting-list-threshold?", "is the dense-posting-list-threshold?", "the dense-posting-list-threshold?", "dense-posting-list-threshold?"], "tokens": ["What", "is", "the", "dense", "posting", "list", "threshold", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-984131783", "fields": {"term": "How is the field used for subquery reporting?", "namespace": "open-p", "hash": -984131783, "terms": ["How is the field used for subquery reporting?", "is the field used for subquery reporting?", "the field used for subquery reporting?", "field used for subquery reporting?", "used for subquery reporting?", "for subquery reporting?", "subquery reporting?", "reporting?"], "tokens": ["How", "is", "the", "field", "used", "for", "subquery", "reporting", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::329950350", "fields": {"term": "What do upper-bound and lower-bound do?", "namespace": "open-p", "hash": 329950350, "terms": ["What do upper-bound and lower-bound do?", "do upper-bound and lower-bound do?", "upper-bound and lower-bound do?", "and lower-bound do?", "lower-bound do?", "do?"], "tokens": ["What", "do", "upper", "bound", "and", "lower", "bound", "do", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2075177926", "fields": {"term": "What happens if the bounds are changed?", "namespace": "open-p", "hash": -2075177926, "terms": ["What happens if the bounds are changed?", "happens if the bounds are changed?", "if the bounds are changed?", "the bounds are changed?", "bounds are changed?", "are changed?", "changed?"], "tokens": ["What", "happens", "if", "the", "bounds", "are", "changed", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-233820616", "fields": {"term": "What type of queries are rejected?", "namespace": "open-p", "hash": -233820616, "terms": ["What type of queries are rejected?", "type of queries are rejected?", "of queries are rejected?", "queries are rejected?", "are rejected?", "rejected?"], "tokens": ["What", "type", "of", "queries", "are", "rejected", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::712042594", "fields": {"term": "What is dense-posting-list-threshold?", "namespace": "open-p", "hash": 712042594, "terms": ["What is dense-posting-list-threshold?", "is dense-posting-list-threshold?", "dense-posting-list-threshold?"], "tokens": ["What", "is", "dense", "posting", "list", "threshold", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::323091928", "fields": {"term": "What impacts the boolean index structure?", "namespace": "open-p", "hash": 323091928, "terms": ["What impacts the boolean index structure?", "impacts the boolean index structure?", "the boolean index structure?", "boolean index structure?", "index structure?", "structure?"], "tokens": ["What", "impacts", "the", "boolean", "index", "structure", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::585018798", "fields": {"term": "What is the default value of dense-posting-list-threshold?", "namespace": "open-p", "hash": 585018798, "terms": ["What is the default value of dense-posting-list-threshold?", "is the default value of dense-posting-list-threshold?", "the default value of dense-posting-list-threshold?", "default value of dense-posting-list-threshold?", "value of dense-posting-list-threshold?", "of dense-posting-list-threshold?", "dense-posting-list-threshold?"], "tokens": ["What", "is", "the", "default", "value", "of", "dense", "posting", "list", "threshold", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1897180982", "fields": {"term": "What is the valid range for dense-posting-list-threshold?", "namespace": "open-p", "hash": -1897180982, "terms": ["What is the valid range for dense-posting-list-threshold?", "is the valid range for dense-posting-list-threshold?", "the valid range for dense-posting-list-threshold?", "valid range for dense-posting-list-threshold?", "range for dense-posting-list-threshold?", "for dense-posting-list-threshold?", "dense-posting-list-threshold?"], "tokens": ["What", "is", "the", "valid", "range", "for", "dense", "posting", "list", "threshold", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1961716319", "fields": {"term": "What are the advantages of using dense posting lists?", "namespace": "open-p", "hash": -1961716319, "terms": ["What are the advantages of using dense posting lists?", "are the advantages of using dense posting lists?", "the advantages of using dense posting lists?", "advantages of using dense posting lists?", "of using dense posting lists?", "using dense posting lists?", "dense posting lists?", "posting lists?", "lists?"], "tokens": ["What", "are", "the", "advantages", "of", "using", "dense", "posting", "lists", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::276516738", "fields": {"term": "What are the disadvantages of using dense posting lists?", "namespace": "open-p", "hash": 276516738, "terms": ["What are the disadvantages of using dense posting lists?", "are the disadvantages of using dense posting lists?", "the disadvantages of using dense posting lists?", "disadvantages of using dense posting lists?", "of using dense posting lists?", "using dense posting lists?", "dense posting lists?", "posting lists?", "lists?"], "tokens": ["What", "are", "the", "disadvantages", "of", "using", "dense", "posting", "lists", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1457279508", "fields": {"term": "What happens if the dense-posting-list-threshold is too low?", "namespace": "open-p", "hash": 1457279508, "terms": ["What happens if the dense-posting-list-threshold is too low?", "happens if the dense-posting-list-threshold is too low?", "if the dense-posting-list-threshold is too low?", "the dense-posting-list-threshold is too low?", "dense-posting-list-threshold is too low?", "is too low?", "too low?", "low?"], "tokens": ["What", "happens", "if", "the", "dense", "posting", "list", "threshold", "is", "too", "low", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::947619162", "fields": {"term": "What happens if the dense-posting-list-threshold is too large?", "namespace": "open-p", "hash": 947619162, "terms": ["What happens if the dense-posting-list-threshold is too large?", "happens if the dense-posting-list-threshold is too large?", "if the dense-posting-list-threshold is too large?", "the dense-posting-list-threshold is too large?", "dense-posting-list-threshold is too large?", "is too large?", "too large?", "large?"], "tokens": ["What", "happens", "if", "the", "dense", "posting", "list", "threshold", "is", "too", "large", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1462531628", "fields": {"term": "What impacts index size?", "namespace": "open-p", "hash": 1462531628, "terms": ["What impacts index size?", "impacts index size?", "index size?", "size?"], "tokens": ["What", "impacts", "index", "size", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-942278487", "fields": {"term": "What is the impact of dense-posting-list-threshold?", "namespace": "open-p", "hash": -942278487, "terms": ["What is the impact of dense-posting-list-threshold?", "is the impact of dense-posting-list-threshold?", "the impact of dense-posting-list-threshold?", "impact of dense-posting-list-threshold?", "of dense-posting-list-threshold?", "dense-posting-list-threshold?"], "tokens": ["What", "is", "the", "impact", "of", "dense", "posting", "list", "threshold", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1471142803", "fields": {"term": "What is the visualization showing?", "namespace": "open-p", "hash": 1471142803, "terms": ["What is the visualization showing?", "is the visualization showing?", "the visualization showing?", "visualization showing?", "showing?"], "tokens": ["What", "is", "the", "visualization", "showing", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::764559021", "fields": {"term": "What is the impact of lower arity on index size?", "namespace": "open-p", "hash": 764559021, "terms": ["What is the impact of lower arity on index size?", "is the impact of lower arity on index size?", "the impact of lower arity on index size?", "impact of lower arity on index size?", "of lower arity on index size?", "lower arity on index size?", "arity on index size?", "on index size?", "index size?", "size?"], "tokens": ["What", "is", "the", "impact", "of", "lower", "arity", "on", "index", "size", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-275100454", "fields": {"term": "How can the interval size be controlled?", "namespace": "open-p", "hash": -275100454, "terms": ["How can the interval size be controlled?", "can the interval size be controlled?", "the interval size be controlled?", "interval size be controlled?", "size be controlled?", "be controlled?", "controlled?"], "tokens": ["How", "can", "the", "interval", "size", "be", "controlled", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::276431588", "fields": {"term": "How to change between index and attribute?", "namespace": "open-p", "hash": 276431588, "terms": ["How to change between index and attribute?", "to change between index and attribute?", "change between index and attribute?", "between index and attribute?", "index and attribute?", "and attribute?", "attribute?"], "tokens": ["How", "to", "change", "between", "index", "and", "attribute", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1544630186", "fields": {"term": "What happens when converting attribute to index?", "namespace": "open-p", "hash": 1544630186, "terms": ["What happens when converting attribute to index?", "happens when converting attribute to index?", "when converting attribute to index?", "converting attribute to index?", "attribute to index?", "to index?", "index?"], "tokens": ["What", "happens", "when", "converting", "attribute", "to", "index", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2024872289", "fields": {"term": "What is the best approach to change between index and attribute?", "namespace": "open-p", "hash": -2024872289, "terms": ["What is the best approach to change between index and attribute?", "is the best approach to change between index and attribute?", "the best approach to change between index and attribute?", "best approach to change between index and attribute?", "approach to change between index and attribute?", "to change between index and attribute?", "change between index and attribute?", "between index and attribute?", "index and attribute?", "and attribute?", "attribute?"], "tokens": ["What", "is", "the", "best", "approach", "to", "change", "between", "index", "and", "attribute", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1458899637", "fields": {"term": "What is 'field artist'?", "namespace": "open-p", "hash": -1458899637, "terms": ["What is 'field artist'?", "is 'field artist'?", "'field artist'?", "artist'?"], "tokens": ["What", "is", "'", "field", "artist", "'?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::80434522", "fields": {"term": "What type is 'field artist'?", "namespace": "open-p", "hash": 80434522, "terms": ["What type is 'field artist'?", "type is 'field artist'?", "is 'field artist'?", "'field artist'?", "artist'?"], "tokens": ["What", "type", "is", "'", "field", "artist", "'?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-970397717", "fields": {"term": "How is 'field artist' indexed?", "namespace": "open-p", "hash": -970397717, "terms": ["How is 'field artist' indexed?", "is 'field artist' indexed?", "'field artist' indexed?", "artist' indexed?", "indexed?"], "tokens": ["How", "is", "'", "field", "artist", "'", "indexed", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::809101318", "fields": {"term": "Is 'field artist' an attribute?", "namespace": "open-p", "hash": 809101318, "terms": ["Is 'field artist' an attribute?", "'field artist' an attribute?", "artist' an attribute?", "an attribute?", "attribute?"], "tokens": ["Is", "'", "field", "artist", "'", "an", "attribute", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2130308611", "fields": {"term": "Is 'field artist' indexed as a summary?", "namespace": "open-p", "hash": 2130308611, "terms": ["Is 'field artist' indexed as a summary?", "'field artist' indexed as a summary?", "artist' indexed as a summary?", "indexed as a summary?", "as a summary?", "a summary?", "summary?"], "tokens": ["Is", "'", "field", "artist", "'", "indexed", "as", "a", "summary", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2005987283", "fields": {"term": "What is the stemming of the field artist?", "namespace": "open-p", "hash": 2005987283, "terms": ["What is the stemming of the field artist?", "is the stemming of the field artist?", "the stemming of the field artist?", "stemming of the field artist?", "of the field artist?", "the field artist?", "field artist?", "artist?"], "tokens": ["What", "is", "the", "stemming", "of", "the", "field", "artist", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1400544173", "fields": {"term": "Is the field artist indexed?", "namespace": "open-p", "hash": 1400544173, "terms": ["Is the field artist indexed?", "the field artist indexed?", "field artist indexed?", "artist indexed?", "indexed?"], "tokens": ["Is", "the", "field", "artist", "indexed", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1565015393", "fields": {"term": "How is the field artist matched?", "namespace": "open-p", "hash": -1565015393, "terms": ["How is the field artist matched?", "is the field artist matched?", "the field artist matched?", "field artist matched?", "artist matched?", "matched?"], "tokens": ["How", "is", "the", "field", "artist", "matched", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-390108771", "fields": {"term": "Does the field artist use stemming?", "namespace": "open-p", "hash": -390108771, "terms": ["Does the field artist use stemming?", "the field artist use stemming?", "field artist use stemming?", "artist use stemming?", "use stemming?", "stemming?"], "tokens": ["Does", "the", "field", "artist", "use", "stemming", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::168341986", "fields": {"term": "How to change artist field to attribute?", "namespace": "open-p", "hash": 168341986, "terms": ["How to change artist field to attribute?", "to change artist field to attribute?", "change artist field to attribute?", "artist field to attribute?", "field to attribute?", "to attribute?", "attribute?"], "tokens": ["How", "to", "change", "artist", "field", "to", "attribute", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::126807354", "fields": {"term": "How to validate queried data?", "namespace": "open-p", "hash": 126807354, "terms": ["How to validate queried data?", "to validate queried data?", "validate queried data?", "queried data?", "data?"], "tokens": ["How", "to", "validate", "queried", "data", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-93945352", "fields": {"term": "How to add an index aspect?", "namespace": "open-p", "hash": -93945352, "terms": ["How to add an index aspect?", "to add an index aspect?", "add an index aspect?", "an index aspect?", "index aspect?", "aspect?"], "tokens": ["How", "to", "add", "an", "index", "aspect", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1222065231", "fields": {"term": "What is vespa-proton-cmd?", "namespace": "open-p", "hash": 1222065231, "terms": ["What is vespa-proton-cmd?", "is vespa-proton-cmd?", "vespa-proton-cmd?"], "tokens": ["What", "is", "vespa", "proton", "cmd", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1764323013", "fields": {"term": "How to inspect attribute data?", "namespace": "open-p", "hash": -1764323013, "terms": ["How to inspect attribute data?", "to inspect attribute data?", "inspect attribute data?", "attribute data?", "data?"], "tokens": ["How", "to", "inspect", "attribute", "data", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1057341527", "fields": {"term": "How to inspect index data?", "namespace": "open-p", "hash": 1057341527, "terms": ["How to inspect index data?", "to inspect index data?", "inspect index data?", "index data?", "data?"], "tokens": ["How", "to", "inspect", "index", "data", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::207194088", "fields": {"term": "What is triggerFlush?", "namespace": "open-p", "hash": 207194088, "terms": ["What is triggerFlush?", "is triggerFlush?", "triggerFlush?"], "tokens": ["What", "is", "triggerFlush", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::948622773", "fields": {"term": "How to troubleshoot with vespa-proton-cmd?", "namespace": "open-p", "hash": 948622773, "terms": ["How to troubleshoot with vespa-proton-cmd?", "to troubleshoot with vespa-proton-cmd?", "troubleshoot with vespa-proton-cmd?", "with vespa-proton-cmd?", "vespa-proton-cmd?"], "tokens": ["How", "to", "troubleshoot", "with", "vespa", "proton", "cmd", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::190943086", "fields": {"term": "What can you use Processing for?", "namespace": "open-p", "hash": 190943086, "terms": ["What can you use Processing for?", "can you use Processing for?", "you use Processing for?", "use Processing for?", "Processing for?", "for?"], "tokens": ["What", "can", "you", "use", "Processing", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2102124970", "fields": {"term": "What is the recommended way to create low-latency request/response processing applications?", "namespace": "open-p", "hash": 2102124970, "terms": ["What is the recommended way to create low-latency request/response processing applications?", "is the recommended way to create low-latency request/response processing applications?", "the recommended way to create low-latency request/response processing applications?", "recommended way to create low-latency request/response processing applications?", "way to create low-latency request/response processing applications?", "to create low-latency request/response processing applications?", "create low-latency request/response processing applications?", "low-latency request/response processing applications?", "request/response processing applications?", "processing applications?", "applications?"], "tokens": ["What", "is", "the", "recommended", "way", "to", "create", "low", "latency", "request", "/", "response", "processing", "applications", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-185161939", "fields": {"term": "What kind of call model do Processors use?", "namespace": "open-p", "hash": -185161939, "terms": ["What kind of call model do Processors use?", "kind of call model do Processors use?", "of call model do Processors use?", "call model do Processors use?", "model do Processors use?", "do Processors use?", "Processors use?", "use?"], "tokens": ["What", "kind", "of", "call", "model", "do", "Processors", "use", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2022849000", "fields": {"term": "What is an example of a Processor task?", "namespace": "open-p", "hash": -2022849000, "terms": ["What is an example of a Processor task?", "is an example of a Processor task?", "an example of a Processor task?", "example of a Processor task?", "of a Processor task?", "a Processor task?", "Processor task?", "task?"], "tokens": ["What", "is", "an", "example", "of", "a", "Processor", "task", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2092816086", "fields": {"term": "Can Processing be used independently of JDisc?", "namespace": "open-p", "hash": 2092816086, "terms": ["Can Processing be used independently of JDisc?", "Processing be used independently of JDisc?", "be used independently of JDisc?", "used independently of JDisc?", "independently of JDisc?", "of JDisc?", "JDisc?"], "tokens": ["Can", "Processing", "be", "used", "independently", "of", "JDisc", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1859525397", "fields": {"term": "How do I add Vespa dependency?", "namespace": "open-p", "hash": -1859525397, "terms": ["How do I add Vespa dependency?", "do I add Vespa dependency?", "I add Vespa dependency?", "add Vespa dependency?", "Vespa dependency?", "dependency?"], "tokens": ["How", "do", "I", "add", "Vespa", "dependency", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1990358762", "fields": {"term": "What is the groupId?", "namespace": "open-p", "hash": 1990358762, "terms": ["What is the groupId?", "is the groupId?", "the groupId?", "groupId?"], "tokens": ["What", "is", "the", "groupId", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1376099702", "fields": {"term": "What is the latest version of container?", "namespace": "open-p", "hash": -1376099702, "terms": ["What is the latest version of container?", "is the latest version of container?", "the latest version of container?", "latest version of container?", "version of container?", "of container?", "container?"], "tokens": ["What", "is", "the", "latest", "version", "of", "container", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-552700307", "fields": {"term": "What is the scope?", "namespace": "open-p", "hash": -552700307, "terms": ["What is the scope?", "is the scope?", "the scope?", "scope?"], "tokens": ["What", "is", "the", "scope", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1998761887", "fields": {"term": "Where can I find the latest version of container?", "namespace": "open-p", "hash": -1998761887, "terms": ["Where can I find the latest version of container?", "can I find the latest version of container?", "I find the latest version of container?", "find the latest version of container?", "the latest version of container?", "latest version of container?", "version of container?", "of container?", "container?"], "tokens": ["Where", "can", "I", "find", "the", "latest", "version", "of", "container", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::378609675", "fields": {"term": "How do I start a deployable project?", "namespace": "open-p", "hash": 378609675, "terms": ["How do I start a deployable project?", "do I start a deployable project?", "I start a deployable project?", "start a deployable project?", "a deployable project?", "deployable project?", "project?"], "tokens": ["How", "do", "I", "start", "a", "deployable", "project", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::641091097", "fields": {"term": "What is a processor?", "namespace": "open-p", "hash": 641091097, "terms": ["What is a processor?", "is a processor?", "a processor?", "processor?"], "tokens": ["What", "is", "a", "processor", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::560683896", "fields": {"term": "What method does a processor implement?", "namespace": "open-p", "hash": 560683896, "terms": ["What method does a processor implement?", "method does a processor implement?", "does a processor implement?", "a processor implement?", "processor implement?", "implement?"], "tokens": ["What", "method", "does", "a", "processor", "implement", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-193308597", "fields": {"term": "What is the structure of an ExampleProcessor class?", "namespace": "open-p", "hash": -193308597, "terms": ["What is the structure of an ExampleProcessor class?", "is the structure of an ExampleProcessor class?", "the structure of an ExampleProcessor class?", "structure of an ExampleProcessor class?", "of an ExampleProcessor class?", "an ExampleProcessor class?", "ExampleProcessor class?", "class?"], "tokens": ["What", "is", "the", "structure", "of", "an", "ExampleProcessor", "class", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::731629273", "fields": {"term": "What does the process() method return?", "namespace": "open-p", "hash": 731629273, "terms": ["What does the process() method return?", "does the process() method return?", "the process() method return?", "process() method return?", "method return?", "return?"], "tokens": ["What", "does", "the", "process", "()", "method", "return", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-311616106", "fields": {"term": "What can the result data from a processor include?", "namespace": "open-p", "hash": -311616106, "terms": ["What can the result data from a processor include?", "can the result data from a processor include?", "the result data from a processor include?", "result data from a processor include?", "data from a processor include?", "from a processor include?", "a processor include?", "processor include?", "include?"], "tokens": ["What", "can", "the", "result", "data", "from", "a", "processor", "include", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1349476498", "fields": {"term": "What packages need to be imported for creating a processor?", "namespace": "open-p", "hash": -1349476498, "terms": ["What packages need to be imported for creating a processor?", "packages need to be imported for creating a processor?", "need to be imported for creating a processor?", "to be imported for creating a processor?", "be imported for creating a processor?", "imported for creating a processor?", "for creating a processor?", "creating a processor?", "a processor?", "processor?"], "tokens": ["What", "packages", "need", "to", "be", "imported", "for", "creating", "a", "processor", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1395774795", "fields": {"term": "What are chains in Vespa?", "namespace": "open-p", "hash": -1395774795, "terms": ["What are chains in Vespa?", "are chains in Vespa?", "chains in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["What", "are", "chains", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-943145839", "fields": {"term": "How to configure chains?", "namespace": "open-p", "hash": -943145839, "terms": ["How to configure chains?", "to configure chains?", "configure chains?", "chains?"], "tokens": ["How", "to", "configure", "chains", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1663283780", "fields": {"term": "What is Processors and Chains?", "namespace": "open-p", "hash": 1663283780, "terms": ["What is Processors and Chains?", "is Processors and Chains?", "Processors and Chains?", "and Chains?", "Chains?"], "tokens": ["What", "is", "Processors", "and", "Chains", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2139853524", "fields": {"term": "How to define chains in services.xml?", "namespace": "open-p", "hash": 2139853524, "terms": ["How to define chains in services.xml?", "to define chains in services.xml?", "define chains in services.xml?", "chains in services.xml?", "in services.xml?", "services.xml?"], "tokens": ["How", "to", "define", "chains", "in", "services", "xml", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::937960854", "fields": {"term": "Can processors be used in many chains at the same time?", "namespace": "open-p", "hash": 937960854, "terms": ["Can processors be used in many chains at the same time?", "processors be used in many chains at the same time?", "be used in many chains at the same time?", "used in many chains at the same time?", "in many chains at the same time?", "many chains at the same time?", "chains at the same time?", "at the same time?", "the same time?", "same time?", "time?"], "tokens": ["Can", "processors", "be", "used", "in", "many", "chains", "at", "the", "same", "time", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::873320375", "fields": {"term": "What is the advantage of chain configuration?", "namespace": "open-p", "hash": 873320375, "terms": ["What is the advantage of chain configuration?", "is the advantage of chain configuration?", "the advantage of chain configuration?", "advantage of chain configuration?", "of chain configuration?", "chain configuration?", "configuration?"], "tokens": ["What", "is", "the", "advantage", "of", "chain", "configuration", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1758239046", "fields": {"term": "What is a partial response?", "namespace": "open-p", "hash": -1758239046, "terms": ["What is a partial response?", "is a partial response?", "a partial response?", "partial response?", "response?"], "tokens": ["What", "is", "a", "partial", "response", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1174575732", "fields": {"term": "What is a placeholder in Vespa?", "namespace": "open-p", "hash": 1174575732, "terms": ["What is a placeholder in Vespa?", "is a placeholder in Vespa?", "a placeholder in Vespa?", "placeholder in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["What", "is", "a", "placeholder", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-724739191", "fields": {"term": "What is ListenableFuture used for?", "namespace": "open-p", "hash": -724739191, "terms": ["What is ListenableFuture used for?", "is ListenableFuture used for?", "ListenableFuture used for?", "used for?", "for?"], "tokens": ["What", "is", "ListenableFuture", "used", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::521974561", "fields": {"term": "What are processor dependencies?", "namespace": "open-p", "hash": 521974561, "terms": ["What are processor dependencies?", "are processor dependencies?", "processor dependencies?", "dependencies?"], "tokens": ["What", "are", "processor", "dependencies", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1655986734", "fields": {"term": "How are dependencies injected?", "namespace": "open-p", "hash": -1655986734, "terms": ["How are dependencies injected?", "are dependencies injected?", "dependencies injected?", "injected?"], "tokens": ["How", "are", "dependencies", "injected", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::915981742", "fields": {"term": "What is the container runtime used?", "namespace": "open-p", "hash": 915981742, "terms": ["What is the container runtime used?", "is the container runtime used?", "the container runtime used?", "container runtime used?", "runtime used?", "used?"], "tokens": ["What", "is", "the", "container", "runtime", "used", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1778025991", "fields": {"term": "What is the recommended immutability?", "namespace": "open-p", "hash": -1778025991, "terms": ["What is the recommended immutability?", "is the recommended immutability?", "the recommended immutability?", "recommended immutability?", "immutability?"], "tokens": ["What", "is", "the", "recommended", "immutability", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1710499264", "fields": {"term": "What is the Guice dependency framework used for?", "namespace": "open-p", "hash": 1710499264, "terms": ["What is the Guice dependency framework used for?", "is the Guice dependency framework used for?", "the Guice dependency framework used for?", "Guice dependency framework used for?", "dependency framework used for?", "framework used for?", "used for?", "for?"], "tokens": ["What", "is", "the", "Guice", "dependency", "framework", "used", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1260018698", "fields": {"term": "How should field values be treated in a processing class?", "namespace": "open-p", "hash": 1260018698, "terms": ["How should field values be treated in a processing class?", "should field values be treated in a processing class?", "field values be treated in a processing class?", "values be treated in a processing class?", "be treated in a processing class?", "treated in a processing class?", "in a processing class?", "a processing class?", "processing class?", "class?"], "tokens": ["How", "should", "field", "values", "be", "treated", "in", "a", "processing", "class", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1987092401", "fields": {"term": "What is the role of constructor arguments in processor dependencies?", "namespace": "open-p", "hash": 1987092401, "terms": ["What is the role of constructor arguments in processor dependencies?", "is the role of constructor arguments in processor dependencies?", "the role of constructor arguments in processor dependencies?", "role of constructor arguments in processor dependencies?", "of constructor arguments in processor dependencies?", "constructor arguments in processor dependencies?", "arguments in processor dependencies?", "in processor dependencies?", "processor dependencies?", "dependencies?"], "tokens": ["What", "is", "the", "role", "of", "constructor", "arguments", "in", "processor", "dependencies", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1643455935", "fields": {"term": "What is the purpose of declaring dependencies in processors?", "namespace": "open-p", "hash": 1643455935, "terms": ["What is the purpose of declaring dependencies in processors?", "is the purpose of declaring dependencies in processors?", "the purpose of declaring dependencies in processors?", "purpose of declaring dependencies in processors?", "of declaring dependencies in processors?", "declaring dependencies in processors?", "dependencies in processors?", "in processors?", "processors?"], "tokens": ["What", "is", "the", "purpose", "of", "declaring", "dependencies", "in", "processors", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::267048607", "fields": {"term": "How are Renderers selected?", "namespace": "open-p", "hash": 267048607, "terms": ["How are Renderers selected?", "are Renderers selected?", "Renderers selected?", "selected?"], "tokens": ["How", "are", "Renderers", "selected", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-196610854", "fields": {"term": "What is the Processing framework?", "namespace": "open-p", "hash": -196610854, "terms": ["What is the Processing framework?", "is the Processing framework?", "the Processing framework?", "Processing framework?", "framework?"], "tokens": ["What", "is", "the", "Processing", "framework", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::4891531", "fields": {"term": "What is subclassing?", "namespace": "open-p", "hash": 4891531, "terms": ["What is subclassing?", "is subclassing?", "subclassing?"], "tokens": ["What", "is", "subclassing", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1735484554", "fields": {"term": "What is the example of subclassing in Search domain?", "namespace": "open-p", "hash": -1735484554, "terms": ["What is the example of subclassing in Search domain?", "is the example of subclassing in Search domain?", "the example of subclassing in Search domain?", "example of subclassing in Search domain?", "of subclassing in Search domain?", "subclassing in Search domain?", "in Search domain?", "Search domain?", "domain?"], "tokens": ["What", "is", "the", "example", "of", "subclassing", "in", "Search", "domain", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1450206845", "fields": {"term": "What is the ChainRegistry in Vespa?", "namespace": "open-p", "hash": 1450206845, "terms": ["What is the ChainRegistry in Vespa?", "is the ChainRegistry in Vespa?", "the ChainRegistry in Vespa?", "ChainRegistry in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["What", "is", "the", "ChainRegistry", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::6957548", "fields": {"term": "What is a ComponentSpecification in Vespa?", "namespace": "open-p", "hash": 6957548, "terms": ["What is a ComponentSpecification in Vespa?", "is a ComponentSpecification in Vespa?", "a ComponentSpecification in Vespa?", "ComponentSpecification in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["What", "is", "a", "ComponentSpecification", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::397131949", "fields": {"term": "What is a processor chain?", "namespace": "open-p", "hash": 397131949, "terms": ["What is a processor chain?", "is a processor chain?", "a processor chain?", "processor chain?", "chain?"], "tokens": ["What", "is", "a", "processor", "chain", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::1191383463", "fields": {"term": "What is the parameter for chain ID?", "namespace": "open-p", "hash": 1191383463, "terms": ["What is the parameter for chain ID?", "is the parameter for chain ID?", "the parameter for chain ID?", "parameter for chain ID?", "for chain ID?", "chain ID?", "ID?"], "tokens": ["What", "is", "the", "parameter", "for", "chain", "ID", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1139315439", "fields": {"term": "What is the default chain ID?", "namespace": "open-p", "hash": 1139315439, "terms": ["What is the default chain ID?", "is the default chain ID?", "the default chain ID?", "default chain ID?", "chain ID?", "ID?"], "tokens": ["What", "is", "the", "default", "chain", "ID", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1591026418", "fields": {"term": "How do you select a non-default chain?", "namespace": "open-p", "hash": -1591026418, "terms": ["How do you select a non-default chain?", "do you select a non-default chain?", "you select a non-default chain?", "select a non-default chain?", "a non-default chain?", "non-default chain?", "chain?"], "tokens": ["How", "do", "you", "select", "a", "non", "default", "chain", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-224194525", "fields": {"term": "What is the syntax for testing a chain from the command line?", "namespace": "open-p", "hash": -224194525, "terms": ["What is the syntax for testing a chain from the command line?", "is the syntax for testing a chain from the command line?", "the syntax for testing a chain from the command line?", "syntax for testing a chain from the command line?", "for testing a chain from the command line?", "testing a chain from the command line?", "a chain from the command line?", "chain from the command line?", "from the command line?", "the command line?", "command line?", "line?"], "tokens": ["What", "is", "the", "syntax", "for", "testing", "a", "chain", "from", "the", "command", "line", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1353233531", "fields": {"term": "What does adding a processor chain enable?", "namespace": "open-p", "hash": -1353233531, "terms": ["What does adding a processor chain enable?", "does adding a processor chain enable?", "adding a processor chain enable?", "a processor chain enable?", "processor chain enable?", "chain enable?", "enable?"], "tokens": ["What", "does", "adding", "a", "processor", "chain", "enable", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-184555647", "fields": {"term": "What is developing web services?", "namespace": "open-p", "hash": -184555647, "terms": ["What is developing web services?", "is developing web services?", "developing web services?", "web services?", "services?"], "tokens": ["What", "is", "developing", "web", "services", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1519294244", "fields": {"term": "What is com.yahoo.processing?", "namespace": "open-p", "hash": -1519294244, "terms": ["What is com.yahoo.processing?", "is com.yahoo.processing?", "com.yahoo.processing?"], "tokens": ["What", "is", "com", "yahoo", "processing", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1543931672", "fields": {"term": "What is Guava?", "namespace": "open-p", "hash": 1543931672, "terms": ["What is Guava?", "is Guava?", "Guava?"], "tokens": ["What", "is", "Guava", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::1931758619", "fields": {"term": "What is jDisc binding?", "namespace": "open-p", "hash": 1931758619, "terms": ["What is jDisc binding?", "is jDisc binding?", "jDisc binding?", "binding?"], "tokens": ["What", "is", "jDisc", "binding", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1637872600", "fields": {"term": "How do I use Processing?", "namespace": "open-p", "hash": -1637872600, "terms": ["How do I use Processing?", "do I use Processing?", "I use Processing?", "use Processing?", "Processing?"], "tokens": ["How", "do", "I", "use", "Processing", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1582143596", "fields": {"term": "Can Processing be used outside jDisc?", "namespace": "open-p", "hash": -1582143596, "terms": ["Can Processing be used outside jDisc?", "Processing be used outside jDisc?", "be used outside jDisc?", "used outside jDisc?", "outside jDisc?", "jDisc?"], "tokens": ["Can", "Processing", "be", "used", "outside", "jDisc", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1626609068", "fields": {"term": "What does Processing do?", "namespace": "open-p", "hash": 1626609068, "terms": ["What does Processing do?", "does Processing do?", "Processing do?", "do?"], "tokens": ["What", "does", "Processing", "do", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::968466862", "fields": {"term": "What are the explanations in this section?", "namespace": "open-p", "hash": 968466862, "terms": ["What are the explanations in this section?", "are the explanations in this section?", "the explanations in this section?", "explanations in this section?", "in this section?", "this section?", "section?"], "tokens": ["What", "are", "the", "explanations", "in", "this", "section", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::14846859", "fields": {"term": "Is jDisc binding required for Processing?", "namespace": "open-p", "hash": 14846859, "terms": ["Is jDisc binding required for Processing?", "jDisc binding required for Processing?", "binding required for Processing?", "required for Processing?", "for Processing?", "Processing?"], "tokens": ["Is", "jDisc", "binding", "required", "for", "Processing", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1324451294", "fields": {"term": "In what environments can Processing be invoked?", "namespace": "open-p", "hash": 1324451294, "terms": ["In what environments can Processing be invoked?", "what environments can Processing be invoked?", "environments can Processing be invoked?", "can Processing be invoked?", "Processing be invoked?", "be invoked?", "invoked?"], "tokens": ["In", "what", "environments", "can", "Processing", "be", "invoked", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1920918001", "fields": {"term": "What does httpRequest contain?", "namespace": "open-p", "hash": 1920918001, "terms": ["What does httpRequest contain?", "does httpRequest contain?", "httpRequest contain?", "contain?"], "tokens": ["What", "does", "httpRequest", "contain", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-499026463", "fields": {"term": "How can httpRequest be accessed?", "namespace": "open-p", "hash": -499026463, "terms": ["How can httpRequest be accessed?", "can httpRequest be accessed?", "httpRequest be accessed?", "be accessed?", "accessed?"], "tokens": ["How", "can", "httpRequest", "be", "accessed", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1781055382", "fields": {"term": "How to add response headers?", "namespace": "open-p", "hash": 1781055382, "terms": ["How to add response headers?", "to add response headers?", "add response headers?", "response headers?", "headers?"], "tokens": ["How", "to", "add", "response", "headers", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1724683995", "fields": {"term": "How does ResponseHeaderSetter work?", "namespace": "open-p", "hash": 1724683995, "terms": ["How does ResponseHeaderSetter work?", "does ResponseHeaderSetter work?", "ResponseHeaderSetter work?", "work?"], "tokens": ["How", "does", "ResponseHeaderSetter", "work", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::591306131", "fields": {"term": "What is the AsyncDataProducer?", "namespace": "open-p", "hash": 591306131, "terms": ["What is the AsyncDataProducer?", "is the AsyncDataProducer?", "the AsyncDataProducer?", "AsyncDataProducer?"], "tokens": ["What", "is", "the", "AsyncDataProducer", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1081145125", "fields": {"term": "What is the AsyncDataProcessingInitiator?", "namespace": "open-p", "hash": -1081145125, "terms": ["What is the AsyncDataProcessingInitiator?", "is the AsyncDataProcessingInitiator?", "the AsyncDataProcessingInitiator?", "AsyncDataProcessingInitiator?"], "tokens": ["What", "is", "the", "AsyncDataProcessingInitiator", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-296457800", "fields": {"term": "What is the purpose of FutureResponse?", "namespace": "open-p", "hash": -296457800, "terms": ["What is the purpose of FutureResponse?", "is the purpose of FutureResponse?", "the purpose of FutureResponse?", "purpose of FutureResponse?", "of FutureResponse?", "FutureResponse?"], "tokens": ["What", "is", "the", "purpose", "of", "FutureResponse", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1855597635", "fields": {"term": "What is the purpose of AsyncExecution?", "namespace": "open-p", "hash": -1855597635, "terms": ["What is the purpose of AsyncExecution?", "is the purpose of AsyncExecution?", "the purpose of AsyncExecution?", "purpose of AsyncExecution?", "of AsyncExecution?", "AsyncExecution?"], "tokens": ["What", "is", "the", "purpose", "of", "AsyncExecution", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1602908715", "fields": {"term": "What are profiling guidelines?", "namespace": "open-p", "hash": -1602908715, "terms": ["What are profiling guidelines?", "are profiling guidelines?", "profiling guidelines?", "guidelines?"], "tokens": ["What", "are", "profiling", "guidelines", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1796285390", "fields": {"term": "How to find a load to profile?", "namespace": "open-p", "hash": -1796285390, "terms": ["How to find a load to profile?", "to find a load to profile?", "find a load to profile?", "a load to profile?", "load to profile?", "to profile?", "profile?"], "tokens": ["How", "to", "find", "a", "load", "to", "profile", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1111550388", "fields": {"term": "What to do if other bottlenecks are found?", "namespace": "open-p", "hash": 1111550388, "terms": ["What to do if other bottlenecks are found?", "to do if other bottlenecks are found?", "do if other bottlenecks are found?", "if other bottlenecks are found?", "other bottlenecks are found?", "bottlenecks are found?", "are found?", "found?"], "tokens": ["What", "to", "do", "if", "other", "bottlenecks", "are", "found", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::924973581", "fields": {"term": "What to do if the system is multithreaded?", "namespace": "open-p", "hash": 924973581, "terms": ["What to do if the system is multithreaded?", "to do if the system is multithreaded?", "do if the system is multithreaded?", "if the system is multithreaded?", "the system is multithreaded?", "system is multithreaded?", "is multithreaded?", "multithreaded?"], "tokens": ["What", "to", "do", "if", "the", "system", "is", "multithreaded", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-755427812", "fields": {"term": "What is vmstat used for?", "namespace": "open-p", "hash": -755427812, "terms": ["What is vmstat used for?", "is vmstat used for?", "vmstat used for?", "used for?", "for?"], "tokens": ["What", "is", "vmstat", "used", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1739655522", "fields": {"term": "What does system column measure in vmstat output?", "namespace": "open-p", "hash": -1739655522, "terms": ["What does system column measure in vmstat output?", "does system column measure in vmstat output?", "system column measure in vmstat output?", "column measure in vmstat output?", "measure in vmstat output?", "in vmstat output?", "vmstat output?", "output?"], "tokens": ["What", "does", "system", "column", "measure", "in", "vmstat", "output", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::342588807", "fields": {"term": "What does iostat show?", "namespace": "open-p", "hash": 342588807, "terms": ["What does iostat show?", "does iostat show?", "iostat show?", "show?"], "tokens": ["What", "does", "iostat", "show", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-918646713", "fields": {"term": "What should swap in/out be according to vmstat?", "namespace": "open-p", "hash": -918646713, "terms": ["What should swap in/out be according to vmstat?", "should swap in/out be according to vmstat?", "swap in/out be according to vmstat?", "in/out be according to vmstat?", "be according to vmstat?", "according to vmstat?", "to vmstat?", "vmstat?"], "tokens": ["What", "should", "swap", "in", "/", "out", "be", "according", "to", "vmstat", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::98840004", "fields": {"term": "What might a maxed out system have?", "namespace": "open-p", "hash": 98840004, "terms": ["What might a maxed out system have?", "might a maxed out system have?", "a maxed out system have?", "maxed out system have?", "out system have?", "system have?", "have?"], "tokens": ["What", "might", "a", "maxed", "out", "system", "have", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::780298743", "fields": {"term": "What is the useless statistic in iostat output?", "namespace": "open-p", "hash": 780298743, "terms": ["What is the useless statistic in iostat output?", "is the useless statistic in iostat output?", "the useless statistic in iostat output?", "useless statistic in iostat output?", "statistic in iostat output?", "in iostat output?", "iostat output?", "output?"], "tokens": ["What", "is", "the", "useless", "statistic", "in", "iostat", "output", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::722079739", "fields": {"term": "How to get a performance profile snapshot?", "namespace": "open-p", "hash": 722079739, "terms": ["How to get a performance profile snapshot?", "to get a performance profile snapshot?", "get a performance profile snapshot?", "a performance profile snapshot?", "performance profile snapshot?", "profile snapshot?", "snapshot?"], "tokens": ["How", "to", "get", "a", "performance", "profile", "snapshot", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1422975915", "fields": {"term": "What is perf?", "namespace": "open-p", "hash": -1422975915, "terms": ["What is perf?", "is perf?", "perf?"], "tokens": ["What", "is", "perf", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1918473095", "fields": {"term": "How to view a performance profile report?", "namespace": "open-p", "hash": -1918473095, "terms": ["How to view a performance profile report?", "to view a performance profile report?", "view a performance profile report?", "a performance profile report?", "performance profile report?", "profile report?", "report?"], "tokens": ["How", "to", "view", "a", "performance", "profile", "report", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-889936632", "fields": {"term": "Why is kernel debug info useful?", "namespace": "open-p", "hash": -889936632, "terms": ["Why is kernel debug info useful?", "is kernel debug info useful?", "kernel debug info useful?", "debug info useful?", "info useful?", "useful?"], "tokens": ["Why", "is", "kernel", "debug", "info", "useful", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::290228425", "fields": {"term": "How to debug an unprivileged container?", "namespace": "open-p", "hash": 290228425, "terms": ["How to debug an unprivileged container?", "to debug an unprivileged container?", "debug an unprivileged container?", "an unprivileged container?", "unprivileged container?", "container?"], "tokens": ["How", "to", "debug", "an", "unprivileged", "container", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::306575115", "fields": {"term": "What is the command to execute perf?", "namespace": "open-p", "hash": 306575115, "terms": ["What is the command to execute perf?", "is the command to execute perf?", "the command to execute perf?", "command to execute perf?", "to execute perf?", "execute perf?", "perf?"], "tokens": ["What", "is", "the", "command", "to", "execute", "perf", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1913461778", "fields": {"term": "What is a privileged container?", "namespace": "open-p", "hash": -1913461778, "terms": ["What is a privileged container?", "is a privileged container?", "a privileged container?", "privileged container?", "container?"], "tokens": ["What", "is", "a", "privileged", "container", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::833448954", "fields": {"term": "How to share pid namespace?", "namespace": "open-p", "hash": 833448954, "terms": ["How to share pid namespace?", "to share pid namespace?", "share pid namespace?", "pid namespace?", "namespace?"], "tokens": ["How", "to", "share", "pid", "namespace", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2030988066", "fields": {"term": "What is the format of the docker ps command?", "namespace": "open-p", "hash": 2030988066, "terms": ["What is the format of the docker ps command?", "is the format of the docker ps command?", "the format of the docker ps command?", "format of the docker ps command?", "of the docker ps command?", "the docker ps command?", "docker ps command?", "ps command?", "command?"], "tokens": ["What", "is", "the", "format", "of", "the", "docker", "ps", "command", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1368998161", "fields": {"term": "How to profile custom searchers?", "namespace": "open-p", "hash": 1368998161, "terms": ["How to profile custom searchers?", "to profile custom searchers?", "profile custom searchers?", "custom searchers?", "searchers?"], "tokens": ["How", "to", "profile", "custom", "searchers", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1111741385", "fields": {"term": "What does profiling identify?", "namespace": "open-p", "hash": 1111741385, "terms": ["What does profiling identify?", "does profiling identify?", "profiling identify?", "identify?"], "tokens": ["What", "does", "profiling", "identify", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-837472874", "fields": {"term": "How to configure Container profiling?", "namespace": "open-p", "hash": -837472874, "terms": ["How to configure Container profiling?", "to configure Container profiling?", "configure Container profiling?", "Container profiling?", "profiling?"], "tokens": ["How", "to", "configure", "Container", "profiling", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-491919931", "fields": {"term": "What is Yourkit?", "namespace": "open-p", "hash": -491919931, "terms": ["What is Yourkit?", "is Yourkit?", "Yourkit?"], "tokens": ["What", "is", "Yourkit", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1769333943", "fields": {"term": "What does Yourkit do?", "namespace": "open-p", "hash": -1769333943, "terms": ["What does Yourkit do?", "does Yourkit do?", "Yourkit do?", "do?"], "tokens": ["What", "does", "Yourkit", "do", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2100917140", "fields": {"term": "What modes does Yourkit support?", "namespace": "open-p", "hash": -2100917140, "terms": ["What modes does Yourkit support?", "modes does Yourkit support?", "does Yourkit support?", "Yourkit support?", "support?"], "tokens": ["What", "modes", "does", "Yourkit", "support", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::776681278", "fields": {"term": "Why use both sampling and tracing in Yourkit?", "namespace": "open-p", "hash": 776681278, "terms": ["Why use both sampling and tracing in Yourkit?", "use both sampling and tracing in Yourkit?", "both sampling and tracing in Yourkit?", "sampling and tracing in Yourkit?", "and tracing in Yourkit?", "tracing in Yourkit?", "in Yourkit?", "Yourkit?"], "tokens": ["Why", "use", "both", "sampling", "and", "tracing", "in", "Yourkit", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1297983803", "fields": {"term": "How do you install Yourkit?", "namespace": "open-p", "hash": 1297983803, "terms": ["How do you install Yourkit?", "do you install Yourkit?", "you install Yourkit?", "install Yourkit?", "Yourkit?"], "tokens": ["How", "do", "you", "install", "Yourkit", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::57180064", "fields": {"term": "How do you modify services.xml?", "namespace": "open-p", "hash": 57180064, "terms": ["How do you modify services.xml?", "do you modify services.xml?", "you modify services.xml?", "modify services.xml?", "services.xml?"], "tokens": ["How", "do", "you", "modify", "services", "xml", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-505064604", "fields": {"term": "How do you re-deploy the application?", "namespace": "open-p", "hash": -505064604, "terms": ["How do you re-deploy the application?", "do you re-deploy the application?", "you re-deploy the application?", "re-deploy the application?", "the application?", "application?"], "tokens": ["How", "do", "you", "re", "deploy", "the", "application", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1585180567", "fields": {"term": "How do you restart Vespa?", "namespace": "open-p", "hash": -1585180567, "terms": ["How do you restart Vespa?", "do you restart Vespa?", "you restart Vespa?", "restart Vespa?", "Vespa?"], "tokens": ["How", "do", "you", "restart", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-820240163", "fields": {"term": "What is YourKit profiler?", "namespace": "open-p", "hash": -820240163, "terms": ["What is YourKit profiler?", "is YourKit profiler?", "YourKit profiler?", "profiler?"], "tokens": ["What", "is", "YourKit", "profiler", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::349062978", "fields": {"term": "How to install YourKit profiler?", "namespace": "open-p", "hash": 349062978, "terms": ["How to install YourKit profiler?", "to install YourKit profiler?", "install YourKit profiler?", "YourKit profiler?", "profiler?"], "tokens": ["How", "to", "install", "YourKit", "profiler", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1012549974", "fields": {"term": "What is the default port for YourKit agent?", "namespace": "open-p", "hash": -1012549974, "terms": ["What is the default port for YourKit agent?", "is the default port for YourKit agent?", "the default port for YourKit agent?", "default port for YourKit agent?", "port for YourKit agent?", "for YourKit agent?", "YourKit agent?", "agent?"], "tokens": ["What", "is", "the", "default", "port", "for", "YourKit", "agent", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1063815634", "fields": {"term": "How to work around remote hosts with Vespa?", "namespace": "open-p", "hash": 1063815634, "terms": ["How to work around remote hosts with Vespa?", "to work around remote hosts with Vespa?", "work around remote hosts with Vespa?", "around remote hosts with Vespa?", "remote hosts with Vespa?", "hosts with Vespa?", "with Vespa?", "Vespa?"], "tokens": ["How", "to", "work", "around", "remote", "hosts", "with", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1381302331", "fields": {"term": "What is the license server?", "namespace": "open-p", "hash": -1381302331, "terms": ["What is the license server?", "is the license server?", "the license server?", "license server?", "server?"], "tokens": ["What", "is", "the", "license", "server", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1802874799", "fields": {"term": "What is the Vespa benchmarking guide?", "namespace": "open-p", "hash": 1802874799, "terms": ["What is the Vespa benchmarking guide?", "is the Vespa benchmarking guide?", "the Vespa benchmarking guide?", "Vespa benchmarking guide?", "benchmarking guide?", "guide?"], "tokens": ["What", "is", "the", "Vespa", "benchmarking", "guide", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2103840819", "fields": {"term": "What is the name of the remote application being profiled?", "namespace": "open-p", "hash": 2103840819, "terms": ["What is the name of the remote application being profiled?", "is the name of the remote application being profiled?", "the name of the remote application being profiled?", "name of the remote application being profiled?", "of the remote application being profiled?", "the remote application being profiled?", "remote application being profiled?", "application being profiled?", "being profiled?", "profiled?"], "tokens": ["What", "is", "the", "name", "of", "the", "remote", "application", "being", "profiled", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::852168422", "fields": {"term": "How do I connect to the remote application for profiling?", "namespace": "open-p", "hash": 852168422, "terms": ["How do I connect to the remote application for profiling?", "do I connect to the remote application for profiling?", "I connect to the remote application for profiling?", "connect to the remote application for profiling?", "to the remote application for profiling?", "the remote application for profiling?", "remote application for profiling?", "application for profiling?", "for profiling?", "profiling?"], "tokens": ["How", "do", "I", "connect", "to", "the", "remote", "application", "for", "profiling", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2009888141", "fields": {"term": "What is the prometheus node metrics API?", "namespace": "open-p", "hash": 2009888141, "terms": ["What is the prometheus node metrics API?", "is the prometheus node metrics API?", "the prometheus node metrics API?", "prometheus node metrics API?", "node metrics API?", "metrics API?", "API?"], "tokens": ["What", "is", "the", "prometheus", "node", "metrics", "API", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::172301457", "fields": {"term": "Where can the prometheus node metrics API be found?", "namespace": "open-p", "hash": 172301457, "terms": ["Where can the prometheus node metrics API be found?", "can the prometheus node metrics API be found?", "the prometheus node metrics API be found?", "prometheus node metrics API be found?", "node metrics API be found?", "metrics API be found?", "API be found?", "be found?", "found?"], "tokens": ["Where", "can", "the", "prometheus", "node", "metrics", "API", "be", "found", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::498628668", "fields": {"term": "What does the prometheus node metrics API offer?", "namespace": "open-p", "hash": 498628668, "terms": ["What does the prometheus node metrics API offer?", "does the prometheus node metrics API offer?", "the prometheus node metrics API offer?", "prometheus node metrics API offer?", "node metrics API offer?", "metrics API offer?", "API offer?", "offer?"], "tokens": ["What", "does", "the", "prometheus", "node", "metrics", "API", "offer", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::848852999", "fields": {"term": "What is the format of the prometheus node metrics API?", "namespace": "open-p", "hash": 848852999, "terms": ["What is the format of the prometheus node metrics API?", "is the format of the prometheus node metrics API?", "the format of the prometheus node metrics API?", "format of the prometheus node metrics API?", "of the prometheus node metrics API?", "the prometheus node metrics API?", "prometheus node metrics API?", "node metrics API?", "metrics API?", "API?"], "tokens": ["What", "is", "the", "format", "of", "the", "prometheus", "node", "metrics", "API", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::934265117", "fields": {"term": "What is included in the monitoring overview?", "namespace": "open-p", "hash": 934265117, "terms": ["What is included in the monitoring overview?", "is included in the monitoring overview?", "included in the monitoring overview?", "in the monitoring overview?", "the monitoring overview?", "monitoring overview?", "overview?"], "tokens": ["What", "is", "included", "in", "the", "monitoring", "overview", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-296558618", "fields": {"term": "What is prometheus/v1?", "namespace": "open-p", "hash": -296558618, "terms": ["What is prometheus/v1?", "is prometheus/v1?", "prometheus/v1?"], "tokens": ["What", "is", "prometheus", "/", "v1", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-849897212", "fields": {"term": "What does status code 200 mean?", "namespace": "open-p", "hash": -849897212, "terms": ["What does status code 200 mean?", "does status code 200 mean?", "status code 200 mean?", "code 200 mean?", "200 mean?", "mean?"], "tokens": ["What", "does", "status", "code", "200", "mean", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1913346736", "fields": {"term": "What is Prometheus format?", "namespace": "open-p", "hash": 1913346736, "terms": ["What is Prometheus format?", "is Prometheus format?", "Prometheus format?", "format?"], "tokens": ["What", "is", "Prometheus", "format", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-259903066", "fields": {"term": "What is in /metrics/v1/values?", "namespace": "open-p", "hash": -259903066, "terms": ["What is in /metrics/v1/values?", "is in /metrics/v1/values?", "in /metrics/v1/values?", "/metrics/v1/values?"], "tokens": ["What", "is", "in", "/", "metrics", "/", "v1", "/", "values", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2088229373", "fields": {"term": "What is Proton?", "namespace": "open-p", "hash": 2088229373, "terms": ["What is Proton?", "is Proton?", "Proton?"], "tokens": ["What", "is", "Proton", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::1754659550", "fields": {"term": "What does Proton maintain?", "namespace": "open-p", "hash": 1754659550, "terms": ["What does Proton maintain?", "does Proton maintain?", "Proton maintain?", "maintain?"], "tokens": ["What", "does", "Proton", "maintain", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-645803336", "fields": {"term": "What are maintenance jobs?", "namespace": "open-p", "hash": -645803336, "terms": ["What are maintenance jobs?", "are maintenance jobs?", "maintenance jobs?", "jobs?"], "tokens": ["What", "are", "maintenance", "jobs", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::1617722407", "fields": {"term": "What is concurrency in Proton?", "namespace": "open-p", "hash": 1617722407, "terms": ["What is concurrency in Proton?", "is concurrency in Proton?", "concurrency in Proton?", "in Proton?", "Proton?"], "tokens": ["What", "is", "concurrency", "in", "Proton", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1280011848", "fields": {"term": "What are the sub-databases in Proton?", "namespace": "open-p", "hash": 1280011848, "terms": ["What are the sub-databases in Proton?", "are the sub-databases in Proton?", "the sub-databases in Proton?", "sub-databases in Proton?", "in Proton?", "Proton?"], "tokens": ["What", "are", "the", "sub", "databases", "in", "Proton", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-347373683", "fields": {"term": "What is the structure of Proton's document databases?", "namespace": "open-p", "hash": -347373683, "terms": ["What is the structure of Proton's document databases?", "is the structure of Proton's document databases?", "the structure of Proton's document databases?", "structure of Proton's document databases?", "of Proton's document databases?", "Proton's document databases?", "document databases?", "databases?"], "tokens": ["What", "is", "the", "structure", "of", "Proton", "'", "s", "document", "databases", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1285825119", "fields": {"term": "What is the role of metadata in starting a node?", "namespace": "open-p", "hash": -1285825119, "terms": ["What is the role of metadata in starting a node?", "is the role of metadata in starting a node?", "the role of metadata in starting a node?", "role of metadata in starting a node?", "of metadata in starting a node?", "metadata in starting a node?", "in starting a node?", "starting a node?", "a node?", "node?"], "tokens": ["What", "is", "the", "role", "of", "metadata", "in", "starting", "a", "node", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1453283247", "fields": {"term": "What is an in-memory bucket database?", "namespace": "open-p", "hash": 1453283247, "terms": ["What is an in-memory bucket database?", "is an in-memory bucket database?", "an in-memory bucket database?", "in-memory bucket database?", "bucket database?", "database?"], "tokens": ["What", "is", "an", "in", "memory", "bucket", "database", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::10345804", "fields": {"term": "How is bucket management implemented with Vespa?", "namespace": "open-p", "hash": 10345804, "terms": ["How is bucket management implemented with Vespa?", "is bucket management implemented with Vespa?", "bucket management implemented with Vespa?", "management implemented with Vespa?", "implemented with Vespa?", "with Vespa?", "Vespa?"], "tokens": ["How", "is", "bucket", "management", "implemented", "with", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-452603776", "fields": {"term": "What is the persistence engine in Vespa?", "namespace": "open-p", "hash": -452603776, "terms": ["What is the persistence engine in Vespa?", "is the persistence engine in Vespa?", "the persistence engine in Vespa?", "persistence engine in Vespa?", "engine in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["What", "is", "the", "persistence", "engine", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-883603242", "fields": {"term": "How does the distributor handle bucket metadata requests?", "namespace": "open-p", "hash": -883603242, "terms": ["How does the distributor handle bucket metadata requests?", "does the distributor handle bucket metadata requests?", "the distributor handle bucket metadata requests?", "distributor handle bucket metadata requests?", "handle bucket metadata requests?", "bucket metadata requests?", "metadata requests?", "requests?"], "tokens": ["How", "does", "the", "distributor", "handle", "bucket", "metadata", "requests", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1843597863", "fields": {"term": "What is meant by elasticity support in Vespa?", "namespace": "open-p", "hash": 1843597863, "terms": ["What is meant by elasticity support in Vespa?", "is meant by elasticity support in Vespa?", "meant by elasticity support in Vespa?", "by elasticity support in Vespa?", "elasticity support in Vespa?", "support in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["What", "is", "meant", "by", "elasticity", "support", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1367458424", "fields": {"term": "What is the SPI in Vespa?", "namespace": "open-p", "hash": -1367458424, "terms": ["What is the SPI in Vespa?", "is the SPI in Vespa?", "the SPI in Vespa?", "SPI in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["What", "is", "the", "SPI", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1444225379", "fields": {"term": "What is a document database?", "namespace": "open-p", "hash": 1444225379, "terms": ["What is a document database?", "is a document database?", "a document database?", "document database?", "database?"], "tokens": ["What", "is", "a", "document", "database", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1597146107", "fields": {"term": "What is a FeedHandler?", "namespace": "open-p", "hash": -1597146107, "terms": ["What is a FeedHandler?", "is a FeedHandler?", "a FeedHandler?", "FeedHandler?"], "tokens": ["What", "is", "a", "FeedHandler", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::304038856", "fields": {"term": "What is a transaction log?", "namespace": "open-p", "hash": 304038856, "terms": ["What is a transaction log?", "is a transaction log?", "a transaction log?", "transaction log?", "log?"], "tokens": ["What", "is", "a", "transaction", "log", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::750630449", "fields": {"term": "What are the sub-databases?", "namespace": "open-p", "hash": 750630449, "terms": ["What are the sub-databases?", "are the sub-databases?", "the sub-databases?", "sub-databases?"], "tokens": ["What", "are", "the", "sub", "databases", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-647278928", "fields": {"term": "What is the document meta store?", "namespace": "open-p", "hash": -647278928, "terms": ["What is the document meta store?", "is the document meta store?", "the document meta store?", "document meta store?", "meta store?", "store?"], "tokens": ["What", "is", "the", "document", "meta", "store", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::-231418653", "fields": {"term": "What is the index maintainer?", "namespace": "open-p", "hash": -231418653, "terms": ["What is the index maintainer?", "is the index maintainer?", "the index maintainer?", "index maintainer?", "maintainer?"], "tokens": ["What", "is", "the", "index", "maintainer", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2040211788", "fields": {"term": "What is the Ready sub-database for?", "namespace": "open-p", "hash": -2040211788, "terms": ["What is the Ready sub-database for?", "is the Ready sub-database for?", "the Ready sub-database for?", "Ready sub-database for?", "sub-database for?", "for?"], "tokens": ["What", "is", "the", "Ready", "sub", "database", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-96670595", "fields": {"term": "What is the difference between active and not active copies?", "namespace": "open-p", "hash": -96670595, "terms": ["What is the difference between active and not active copies?", "is the difference between active and not active copies?", "the difference between active and not active copies?", "difference between active and not active copies?", "between active and not active copies?", "active and not active copies?", "and not active copies?", "not active copies?", "active copies?", "copies?"], "tokens": ["What", "is", "the", "difference", "between", "active", "and", "not", "active", "copies", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1696298996", "fields": {"term": "What is the purpose of the Removed sub-database?", "namespace": "open-p", "hash": 1696298996, "terms": ["What is the purpose of the Removed sub-database?", "is the purpose of the Removed sub-database?", "the purpose of the Removed sub-database?", "purpose of the Removed sub-database?", "of the Removed sub-database?", "the Removed sub-database?", "Removed sub-database?", "sub-database?"], "tokens": ["What", "is", "the", "purpose", "of", "the", "Removed", "sub", "database", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1485528193", "fields": {"term": "What happens when the system is resized?", "namespace": "open-p", "hash": 1485528193, "terms": ["What happens when the system is resized?", "happens when the system is resized?", "when the system is resized?", "the system is resized?", "system is resized?", "is resized?", "resized?"], "tokens": ["What", "happens", "when", "the", "system", "is", "resized", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1532610302", "fields": {"term": "How is transaction log persisted?", "namespace": "open-p", "hash": 1532610302, "terms": ["How is transaction log persisted?", "is transaction log persisted?", "transaction log persisted?", "log persisted?", "persisted?"], "tokens": ["How", "is", "transaction", "log", "persisted", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::430337800", "fields": {"term": "What is the benefit of having a transaction log?", "namespace": "open-p", "hash": 430337800, "terms": ["What is the benefit of having a transaction log?", "is the benefit of having a transaction log?", "the benefit of having a transaction log?", "benefit of having a transaction log?", "of having a transaction log?", "having a transaction log?", "a transaction log?", "transaction log?", "log?"], "tokens": ["What", "is", "the", "benefit", "of", "having", "a", "transaction", "log", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-49200486", "fields": {"term": "What is written to the transaction log?", "namespace": "open-p", "hash": -49200486, "terms": ["What is written to the transaction log?", "is written to the transaction log?", "written to the transaction log?", "to the transaction log?", "the transaction log?", "transaction log?", "log?"], "tokens": ["What", "is", "written", "to", "the", "transaction", "log", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::899920460", "fields": {"term": "What is sequential IO?", "namespace": "open-p", "hash": 899920460, "terms": ["What is sequential IO?", "is sequential IO?", "sequential IO?", "IO?"], "tokens": ["What", "is", "sequential", "IO", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1665362255", "fields": {"term": "What is sync-transactionlog?", "namespace": "open-p", "hash": 1665362255, "terms": ["What is sync-transactionlog?", "is sync-transactionlog?", "sync-transactionlog?"], "tokens": ["What", "is", "sync", "transactionlog", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1817486036", "fields": {"term": "What does proton do on shutdown?", "namespace": "open-p", "hash": -1817486036, "terms": ["What does proton do on shutdown?", "does proton do on shutdown?", "proton do on shutdown?", "do on shutdown?", "on shutdown?", "shutdown?"], "tokens": ["What", "does", "proton", "do", "on", "shutdown", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1440590985", "fields": {"term": "What are index fields?", "namespace": "open-p", "hash": 1440590985, "terms": ["What are index fields?", "are index fields?", "index fields?", "fields?"], "tokens": ["What", "are", "index", "fields", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1671974918", "fields": {"term": "What is a disk index?", "namespace": "open-p", "hash": -1671974918, "terms": ["What is a disk index?", "is a disk index?", "a disk index?", "disk index?", "index?"], "tokens": ["What", "is", "a", "disk", "index", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::749900434", "fields": {"term": "What is stored in a dictionary?", "namespace": "open-p", "hash": 749900434, "terms": ["What is stored in a dictionary?", "is stored in a dictionary?", "stored in a dictionary?", "in a dictionary?", "a dictionary?", "dictionary?"], "tokens": ["What", "is", "stored", "in", "a", "dictionary", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1748891282", "fields": {"term": "What is `posocc`?", "namespace": "open-p", "hash": -1748891282, "terms": ["What is `posocc`?", "is `posocc`?", "`posocc`?"], "tokens": ["What", "is", "`", "posocc", "`?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1191732451", "fields": {"term": "What is the document store?", "namespace": "open-p", "hash": -1191732451, "terms": ["What is the document store?", "is the document store?", "the document store?", "document store?", "store?"], "tokens": ["What", "is", "the", "document", "store", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1202266039", "fields": {"term": "How are files written in document store?", "namespace": "open-p", "hash": -1202266039, "terms": ["How are files written in document store?", "are files written in document store?", "files written in document store?", "written in document store?", "in document store?", "document store?", "store?"], "tokens": ["How", "are", "files", "written", "in", "document", "store", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::151626831", "fields": {"term": "What is the maximum size for a .dat file?", "namespace": "open-p", "hash": 151626831, "terms": ["What is the maximum size for a .dat file?", "is the maximum size for a .dat file?", "the maximum size for a .dat file?", "maximum size for a .dat file?", "size for a .dat file?", "for a .dat file?", "a .dat file?", ".dat file?", "file?"], "tokens": ["What", "is", "the", "maximum", "size", "for", "a", "dat", "file", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::110943069", "fields": {"term": "What happens to old version of documents?", "namespace": "open-p", "hash": 110943069, "terms": ["What happens to old version of documents?", "happens to old version of documents?", "to old version of documents?", "old version of documents?", "version of documents?", "of documents?", "documents?"], "tokens": ["What", "happens", "to", "old", "version", "of", "documents", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-900618791", "fields": {"term": "How are files written using compression settings?", "namespace": "open-p", "hash": -900618791, "terms": ["How are files written using compression settings?", "are files written using compression settings?", "files written using compression settings?", "written using compression settings?", "using compression settings?", "compression settings?", "settings?"], "tokens": ["How", "are", "files", "written", "using", "compression", "settings", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-849489429", "fields": {"term": "What does document store compaction do?", "namespace": "open-p", "hash": -849489429, "terms": ["What does document store compaction do?", "does document store compaction do?", "document store compaction do?", "store compaction do?", "compaction do?", "do?"], "tokens": ["What", "does", "document", "store", "compaction", "do", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1392574431", "fields": {"term": "How is document store compaction triggered?", "namespace": "open-p", "hash": 1392574431, "terms": ["How is document store compaction triggered?", "is document store compaction triggered?", "document store compaction triggered?", "store compaction triggered?", "compaction triggered?", "triggered?"], "tokens": ["How", "is", "document", "store", "compaction", "triggered", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-178626313", "fields": {"term": "What metric tracks proton's compaction jobs?", "namespace": "open-p", "hash": -178626313, "terms": ["What metric tracks proton's compaction jobs?", "metric tracks proton's compaction jobs?", "tracks proton's compaction jobs?", "proton's compaction jobs?", "compaction jobs?", "jobs?"], "tokens": ["What", "metric", "tracks", "proton", "'", "s", "compaction", "jobs", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-144660883", "fields": {"term": "What is a normal pattern for max_bucket_spread?", "namespace": "open-p", "hash": -144660883, "terms": ["What is a normal pattern for max_bucket_spread?", "is a normal pattern for max_bucket_spread?", "a normal pattern for max_bucket_spread?", "normal pattern for max_bucket_spread?", "pattern for max_bucket_spread?", "for max_bucket_spread?", "max_bucket_spread?"], "tokens": ["What", "is", "a", "normal", "pattern", "for", "max_bucket_spread", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1591303707", "fields": {"term": "How can the correct compaction settings be set?", "namespace": "open-p", "hash": -1591303707, "terms": ["How can the correct compaction settings be set?", "can the correct compaction settings be set?", "the correct compaction settings be set?", "correct compaction settings be set?", "compaction settings be set?", "settings be set?", "be set?", "set?"], "tokens": ["How", "can", "the", "correct", "compaction", "settings", "be", "set", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1034942194", "fields": {"term": "How can the defragmentation status be observed?", "namespace": "open-p", "hash": 1034942194, "terms": ["How can the defragmentation status be observed?", "can the defragmentation status be observed?", "the defragmentation status be observed?", "defragmentation status be observed?", "status be observed?", "be observed?", "observed?"], "tokens": ["How", "can", "the", "defragmentation", "status", "be", "observed", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-86312204", "fields": {"term": "What is the purpose of defragmentation?", "namespace": "open-p", "hash": -86312204, "terms": ["What is the purpose of defragmentation?", "is the purpose of defragmentation?", "the purpose of defragmentation?", "purpose of defragmentation?", "of defragmentation?", "defragmentation?"], "tokens": ["What", "is", "the", "purpose", "of", "defragmentation", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::709105904", "fields": {"term": "What is the purpose of maxsize?", "namespace": "open-p", "hash": 709105904, "terms": ["What is the purpose of maxsize?", "is the purpose of maxsize?", "the purpose of maxsize?", "purpose of maxsize?", "of maxsize?", "maxsize?"], "tokens": ["What", "is", "the", "purpose", "of", "maxsize", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-686010428", "fields": {"term": "What is the default chunk size?", "namespace": "open-p", "hash": -686010428, "terms": ["What is the default chunk size?", "is the default chunk size?", "the default chunk size?", "default chunk size?", "chunk size?", "size?"], "tokens": ["What", "is", "the", "default", "chunk", "size", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1725180533", "fields": {"term": "What is bucket-splitting?", "namespace": "open-p", "hash": 1725180533, "terms": ["What is bucket-splitting?", "is bucket-splitting?", "bucket-splitting?"], "tokens": ["What", "is", "bucket", "splitting", "?"], "corpus_count": 3, "document_count": 3}}, -{"put": "id:term:term::-1567579683", "fields": {"term": "How are documents read in a visit operation?", "namespace": "open-p", "hash": -1567579683, "terms": ["How are documents read in a visit operation?", "are documents read in a visit operation?", "documents read in a visit operation?", "read in a visit operation?", "in a visit operation?", "a visit operation?", "visit operation?", "operation?"], "tokens": ["How", "are", "documents", "read", "in", "a", "visit", "operation", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-51805059", "fields": {"term": "How is memory lookup optimized?", "namespace": "open-p", "hash": -51805059, "terms": ["How is memory lookup optimized?", "is memory lookup optimized?", "memory lookup optimized?", "lookup optimized?", "optimized?"], "tokens": ["How", "is", "memory", "lookup", "optimized", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::612083445", "fields": {"term": "What is the impact of increasing maxfilesize on compaction time?", "namespace": "open-p", "hash": 612083445, "terms": ["What is the impact of increasing maxfilesize on compaction time?", "is the impact of increasing maxfilesize on compaction time?", "the impact of increasing maxfilesize on compaction time?", "impact of increasing maxfilesize on compaction time?", "of increasing maxfilesize on compaction time?", "increasing maxfilesize on compaction time?", "maxfilesize on compaction time?", "on compaction time?", "compaction time?", "time?"], "tokens": ["What", "is", "the", "impact", "of", "increasing", "maxfilesize", "on", "compaction", "time", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1391201233", "fields": {"term": "What is the impact of reducing maxsize on wasted IO bytes?", "namespace": "open-p", "hash": 1391201233, "terms": ["What is the impact of reducing maxsize on wasted IO bytes?", "is the impact of reducing maxsize on wasted IO bytes?", "the impact of reducing maxsize on wasted IO bytes?", "impact of reducing maxsize on wasted IO bytes?", "of reducing maxsize on wasted IO bytes?", "reducing maxsize on wasted IO bytes?", "maxsize on wasted IO bytes?", "on wasted IO bytes?", "wasted IO bytes?", "IO bytes?", "bytes?"], "tokens": ["What", "is", "the", "impact", "of", "reducing", "maxsize", "on", "wasted", "IO", "bytes", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1892664317", "fields": {"term": "What are the sizing units to consider when optimizing reads?", "namespace": "open-p", "hash": 1892664317, "terms": ["What are the sizing units to consider when optimizing reads?", "are the sizing units to consider when optimizing reads?", "the sizing units to consider when optimizing reads?", "sizing units to consider when optimizing reads?", "units to consider when optimizing reads?", "to consider when optimizing reads?", "consider when optimizing reads?", "when optimizing reads?", "optimizing reads?", "reads?"], "tokens": ["What", "are", "the", "sizing", "units", "to", "consider", "when", "optimizing", "reads", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1358114732", "fields": {"term": "What is a LID?", "namespace": "open-p", "hash": 1358114732, "terms": ["What is a LID?", "is a LID?", "a LID?", "LID?"], "tokens": ["What", "is", "a", "LID", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::959298219", "fields": {"term": "What is the .idx file?", "namespace": "open-p", "hash": 959298219, "terms": ["What is the .idx file?", "is the .idx file?", "the .idx file?", ".idx file?", "file?"], "tokens": ["What", "is", "the", "idx", "file", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2107001096", "fields": {"term": "What is the memory usage?", "namespace": "open-p", "hash": 2107001096, "terms": ["What is the memory usage?", "is the memory usage?", "the memory usage?", "memory usage?", "usage?"], "tokens": ["What", "is", "the", "memory", "usage", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::-429320004", "fields": {"term": "How is memory usage related to number of documents?", "namespace": "open-p", "hash": -429320004, "terms": ["How is memory usage related to number of documents?", "is memory usage related to number of documents?", "memory usage related to number of documents?", "usage related to number of documents?", "related to number of documents?", "to number of documents?", "number of documents?", "of documents?", "documents?"], "tokens": ["How", "is", "memory", "usage", "related", "to", "number", "of", "documents", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1693267478", "fields": {"term": "What is the mapping in memory?", "namespace": "open-p", "hash": 1693267478, "terms": ["What is the mapping in memory?", "is the mapping in memory?", "the mapping in memory?", "mapping in memory?", "in memory?", "memory?"], "tokens": ["What", "is", "the", "mapping", "in", "memory", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2053520783", "fields": {"term": "What is CPU used for?", "namespace": "open-p", "hash": -2053520783, "terms": ["What is CPU used for?", "is CPU used for?", "CPU used for?", "used for?", "for?"], "tokens": ["What", "is", "CPU", "used", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1116083377", "fields": {"term": "What is disk usage?", "namespace": "open-p", "hash": 1116083377, "terms": ["What is disk usage?", "is disk usage?", "disk usage?", "usage?"], "tokens": ["What", "is", "disk", "usage", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-171003102", "fields": {"term": "What is the purpose of attribute flushing?", "namespace": "open-p", "hash": -171003102, "terms": ["What is the purpose of attribute flushing?", "is the purpose of attribute flushing?", "the purpose of attribute flushing?", "purpose of attribute flushing?", "of attribute flushing?", "attribute flushing?", "flushing?"], "tokens": ["What", "is", "the", "purpose", "of", "attribute", "flushing", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::791574795", "fields": {"term": "What is document store compaction?", "namespace": "open-p", "hash": 791574795, "terms": ["What is document store compaction?", "is document store compaction?", "document store compaction?", "store compaction?", "compaction?"], "tokens": ["What", "is", "document", "store", "compaction", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::269462952", "fields": {"term": "What are available Proton metrics?", "namespace": "open-p", "hash": 269462952, "terms": ["What are available Proton metrics?", "are available Proton metrics?", "available Proton metrics?", "Proton metrics?", "metrics?"], "tokens": ["What", "are", "available", "Proton", "metrics", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1495270431", "fields": {"term": "What is the QR-server?", "namespace": "open-p", "hash": -1495270431, "terms": ["What is the QR-server?", "is the QR-server?", "the QR-server?", "QR-server?"], "tokens": ["What", "is", "the", "QR", "server", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-294365395", "fields": {"term": "How does queries move through Vespa?", "namespace": "open-p", "hash": -294365395, "terms": ["How does queries move through Vespa?", "does queries move through Vespa?", "queries move through Vespa?", "move through Vespa?", "through Vespa?", "Vespa?"], "tokens": ["How", "does", "queries", "move", "through", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::558539182", "fields": {"term": "What is the custom component state API?", "namespace": "open-p", "hash": 558539182, "terms": ["What is the custom component state API?", "is the custom component state API?", "the custom component state API?", "custom component state API?", "component state API?", "state API?", "API?"], "tokens": ["What", "is", "the", "custom", "component", "state", "API", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1285673876", "fields": {"term": "Where can component status be found?", "namespace": "open-p", "hash": -1285673876, "terms": ["Where can component status be found?", "can component status be found?", "component status be found?", "status be found?", "be found?", "found?"], "tokens": ["Where", "can", "component", "status", "be", "found", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2043882604", "fields": {"term": "What is the format of the custom component status?", "namespace": "open-p", "hash": 2043882604, "terms": ["What is the format of the custom component status?", "is the format of the custom component status?", "the format of the custom component status?", "format of the custom component status?", "of the custom component status?", "the custom component status?", "custom component status?", "component status?", "status?"], "tokens": ["What", "is", "the", "format", "of", "the", "custom", "component", "status", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2068683042", "fields": {"term": "What is the purpose of `state/v1/custom/component`?", "namespace": "open-p", "hash": 2068683042, "terms": ["What is the purpose of `state/v1/custom/component`?", "is the purpose of `state/v1/custom/component`?", "the purpose of `state/v1/custom/component`?", "purpose of `state/v1/custom/component`?", "of `state/v1/custom/component`?", "`state/v1/custom/component`?"], "tokens": ["What", "is", "the", "purpose", "of", "`", "state", "/", "v1", "/", "custom", "/", "component", "`?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::185496658", "fields": {"term": "What is the purpose of `documentdb`?", "namespace": "open-p", "hash": 185496658, "terms": ["What is the purpose of `documentdb`?", "is the purpose of `documentdb`?", "the purpose of `documentdb`?", "purpose of `documentdb`?", "of `documentdb`?", "`documentdb`?"], "tokens": ["What", "is", "the", "purpose", "of", "`", "documentdb", "`?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::576497927", "fields": {"term": "What information does the state of `mydoctype` include?", "namespace": "open-p", "hash": 576497927, "terms": ["What information does the state of `mydoctype` include?", "information does the state of `mydoctype` include?", "does the state of `mydoctype` include?", "the state of `mydoctype` include?", "state of `mydoctype` include?", "of `mydoctype` include?", "`mydoctype` include?", "include?"], "tokens": ["What", "information", "does", "the", "state", "of", "`", "mydoctype", "`", "include", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::992705233", "fields": {"term": "What is the structure of the `subdb`?", "namespace": "open-p", "hash": 992705233, "terms": ["What is the structure of the `subdb`?", "is the structure of the `subdb`?", "the structure of the `subdb`?", "structure of the `subdb`?", "of the `subdb`?", "the `subdb`?", "`subdb`?"], "tokens": ["What", "is", "the", "structure", "of", "the", "`", "subdb", "`?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1721513500", "fields": {"term": "What is the structure of the `documentdb`?", "namespace": "open-p", "hash": 1721513500, "terms": ["What is the structure of the `documentdb`?", "is the structure of the `documentdb`?", "the structure of the `documentdb`?", "structure of the `documentdb`?", "of the `documentdb`?", "the `documentdb`?", "`documentdb`?"], "tokens": ["What", "is", "the", "structure", "of", "the", "`", "documentdb", "`?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1585930037", "fields": {"term": "What is the difference between full and abbreviated names?", "namespace": "open-p", "hash": -1585930037, "terms": ["What is the difference between full and abbreviated names?", "is the difference between full and abbreviated names?", "the difference between full and abbreviated names?", "difference between full and abbreviated names?", "between full and abbreviated names?", "full and abbreviated names?", "and abbreviated names?", "abbreviated names?", "names?"], "tokens": ["What", "is", "the", "difference", "between", "full", "and", "abbreviated", "names", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::532013692", "fields": {"term": "Are query profile types case-sensitive?", "namespace": "open-p", "hash": 532013692, "terms": ["Are query profile types case-sensitive?", "query profile types case-sensitive?", "profile types case-sensitive?", "types case-sensitive?", "case-sensitive?"], "tokens": ["Are", "query", "profile", "types", "case", "sensitive", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-720962227", "fields": {"term": "What is the purpose of modeling parameters as query profile types?", "namespace": "open-p", "hash": -720962227, "terms": ["What is the purpose of modeling parameters as query profile types?", "is the purpose of modeling parameters as query profile types?", "the purpose of modeling parameters as query profile types?", "purpose of modeling parameters as query profile types?", "of modeling parameters as query profile types?", "modeling parameters as query profile types?", "parameters as query profile types?", "as query profile types?", "query profile types?", "profile types?", "types?"], "tokens": ["What", "is", "the", "purpose", "of", "modeling", "parameters", "as", "query", "profile", "types", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1141842764", "fields": {"term": "What Java objects can I get from Query to Searcher components?", "namespace": "open-p", "hash": 1141842764, "terms": ["What Java objects can I get from Query to Searcher components?", "Java objects can I get from Query to Searcher components?", "objects can I get from Query to Searcher components?", "can I get from Query to Searcher components?", "I get from Query to Searcher components?", "get from Query to Searcher components?", "from Query to Searcher components?", "Query to Searcher components?", "to Searcher components?", "Searcher components?", "components?"], "tokens": ["What", "Java", "objects", "can", "I", "get", "from", "Query", "to", "Searcher", "components", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-105805688", "fields": {"term": "What are the Native Execution Parameters?", "namespace": "open-p", "hash": -105805688, "terms": ["What are the Native Execution Parameters?", "are the Native Execution Parameters?", "the Native Execution Parameters?", "Native Execution Parameters?", "Execution Parameters?", "Parameters?"], "tokens": ["What", "are", "the", "Native", "Execution", "Parameters", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::625510987", "fields": {"term": "What is the Presentation?", "namespace": "open-p", "hash": 625510987, "terms": ["What is the Presentation?", "is the Presentation?", "the Presentation?", "Presentation?"], "tokens": ["What", "is", "the", "Presentation", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-269189744", "fields": {"term": "What is the YQL query guide?", "namespace": "open-p", "hash": -269189744, "terms": ["What is the YQL query guide?", "is the YQL query guide?", "the YQL query guide?", "YQL query guide?", "query guide?", "guide?"], "tokens": ["What", "is", "the", "YQL", "query", "guide", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1599173528", "fields": {"term": "What is the YQL reference?", "namespace": "open-p", "hash": 1599173528, "terms": ["What is the YQL reference?", "is the YQL reference?", "the YQL reference?", "YQL reference?", "reference?"], "tokens": ["What", "is", "the", "YQL", "reference", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1269065007", "fields": {"term": "What is the default value for 'hits' parameter?", "namespace": "open-p", "hash": 1269065007, "terms": ["What is the default value for 'hits' parameter?", "is the default value for 'hits' parameter?", "the default value for 'hits' parameter?", "default value for 'hits' parameter?", "value for 'hits' parameter?", "for 'hits' parameter?", "'hits' parameter?", "parameter?"], "tokens": ["What", "is", "the", "default", "value", "for", "'", "hits", "'", "parameter", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-18447929", "fields": {"term": "What is the maximum value of 'hits' parameter?", "namespace": "open-p", "hash": -18447929, "terms": ["What is the maximum value of 'hits' parameter?", "is the maximum value of 'hits' parameter?", "the maximum value of 'hits' parameter?", "maximum value of 'hits' parameter?", "value of 'hits' parameter?", "of 'hits' parameter?", "'hits' parameter?", "parameter?"], "tokens": ["What", "is", "the", "maximum", "value", "of", "'", "hits", "'", "parameter", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1027480643", "fields": {"term": "What is the purpose of 'offset' parameter?", "namespace": "open-p", "hash": -1027480643, "terms": ["What is the purpose of 'offset' parameter?", "is the purpose of 'offset' parameter?", "the purpose of 'offset' parameter?", "purpose of 'offset' parameter?", "of 'offset' parameter?", "'offset' parameter?", "parameter?"], "tokens": ["What", "is", "the", "purpose", "of", "'", "offset", "'", "parameter", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1697301429", "fields": {"term": "What is the default value for 'queryProfile' parameter?", "namespace": "open-p", "hash": 1697301429, "terms": ["What is the default value for 'queryProfile' parameter?", "is the default value for 'queryProfile' parameter?", "the default value for 'queryProfile' parameter?", "default value for 'queryProfile' parameter?", "value for 'queryProfile' parameter?", "for 'queryProfile' parameter?", "'queryProfile' parameter?", "parameter?"], "tokens": ["What", "is", "the", "default", "value", "for", "'", "queryProfile", "'", "parameter", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1516361274", "fields": {"term": "What is the purpose of 'groupingSessionCache' parameter?", "namespace": "open-p", "hash": 1516361274, "terms": ["What is the purpose of 'groupingSessionCache' parameter?", "is the purpose of 'groupingSessionCache' parameter?", "the purpose of 'groupingSessionCache' parameter?", "purpose of 'groupingSessionCache' parameter?", "of 'groupingSessionCache' parameter?", "'groupingSessionCache' parameter?", "parameter?"], "tokens": ["What", "is", "the", "purpose", "of", "'", "groupingSessionCache", "'", "parameter", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1859694262", "fields": {"term": "What is the default value of 'searchChain' parameter?", "namespace": "open-p", "hash": 1859694262, "terms": ["What is the default value of 'searchChain' parameter?", "is the default value of 'searchChain' parameter?", "the default value of 'searchChain' parameter?", "default value of 'searchChain' parameter?", "value of 'searchChain' parameter?", "of 'searchChain' parameter?", "'searchChain' parameter?", "parameter?"], "tokens": ["What", "is", "the", "default", "value", "of", "'", "searchChain", "'", "parameter", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::480681662", "fields": {"term": "What is the default unit for 'timeout' parameter?", "namespace": "open-p", "hash": 480681662, "terms": ["What is the default unit for 'timeout' parameter?", "is the default unit for 'timeout' parameter?", "the default unit for 'timeout' parameter?", "default unit for 'timeout' parameter?", "unit for 'timeout' parameter?", "for 'timeout' parameter?", "'timeout' parameter?", "parameter?"], "tokens": ["What", "is", "the", "default", "unit", "for", "'", "timeout", "'", "parameter", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::768045940", "fields": {"term": "What is the purpose of 'timeout' parameter?", "namespace": "open-p", "hash": 768045940, "terms": ["What is the purpose of 'timeout' parameter?", "is the purpose of 'timeout' parameter?", "the purpose of 'timeout' parameter?", "purpose of 'timeout' parameter?", "of 'timeout' parameter?", "'timeout' parameter?", "parameter?"], "tokens": ["What", "is", "the", "purpose", "of", "'", "timeout", "'", "parameter", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1047932755", "fields": {"term": "What is model.defaultIndex?", "namespace": "open-p", "hash": 1047932755, "terms": ["What is model.defaultIndex?", "is model.defaultIndex?", "model.defaultIndex?"], "tokens": ["What", "is", "model", "defaultIndex", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1761231227", "fields": {"term": "What is model.encoding used for?", "namespace": "open-p", "hash": -1761231227, "terms": ["What is model.encoding used for?", "is model.encoding used for?", "model.encoding used for?", "used for?", "for?"], "tokens": ["What", "is", "model", "encoding", "used", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2146471969", "fields": {"term": "What is model.filter in Simple Query Language used for?", "namespace": "open-p", "hash": 2146471969, "terms": ["What is model.filter in Simple Query Language used for?", "is model.filter in Simple Query Language used for?", "model.filter in Simple Query Language used for?", "in Simple Query Language used for?", "Simple Query Language used for?", "Query Language used for?", "Language used for?", "used for?", "for?"], "tokens": ["What", "is", "model", "filter", "in", "Simple", "Query", "Language", "used", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1829605036", "fields": {"term": "What is model.locale used for?", "namespace": "open-p", "hash": -1829605036, "terms": ["What is model.locale used for?", "is model.locale used for?", "model.locale used for?", "used for?", "for?"], "tokens": ["What", "is", "model", "locale", "used", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1334645138", "fields": {"term": "What is model.queryString used for?", "namespace": "open-p", "hash": -1334645138, "terms": ["What is model.queryString used for?", "is model.queryString used for?", "model.queryString used for?", "used for?", "for?"], "tokens": ["What", "is", "model", "queryString", "used", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-677257501", "fields": {"term": "What is model.restrict used for?", "namespace": "open-p", "hash": -677257501, "terms": ["What is model.restrict used for?", "is model.restrict used for?", "model.restrict used for?", "used for?", "for?"], "tokens": ["What", "is", "model", "restrict", "used", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-471393341", "fields": {"term": "What is model.searchPath used for?", "namespace": "open-p", "hash": -471393341, "terms": ["What is model.searchPath used for?", "is model.searchPath used for?", "model.searchPath used for?", "used for?", "for?"], "tokens": ["What", "is", "model", "searchPath", "used", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::213104070", "fields": {"term": "What is model.sources used for?", "namespace": "open-p", "hash": 213104070, "terms": ["What is model.sources used for?", "is model.sources used for?", "model.sources used for?", "used for?", "for?"], "tokens": ["What", "is", "model", "sources", "used", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-924546945", "fields": {"term": "What is ranking.location?", "namespace": "open-p", "hash": -924546945, "terms": ["What is ranking.location?", "is ranking.location?", "ranking.location?"], "tokens": ["What", "is", "ranking", "location", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::951877833", "fields": {"term": "What is ranking.profile for?", "namespace": "open-p", "hash": 951877833, "terms": ["What is ranking.profile for?", "is ranking.profile for?", "ranking.profile for?", "for?"], "tokens": ["What", "is", "ranking", "profile", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1133386188", "fields": {"term": "What is ranking.listFeatures for?", "namespace": "open-p", "hash": 1133386188, "terms": ["What is ranking.listFeatures for?", "is ranking.listFeatures for?", "ranking.listFeatures for?", "for?"], "tokens": ["What", "is", "ranking", "listFeatures", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1735158282", "fields": {"term": "What is ranking.properties.*propertyname* for?", "namespace": "open-p", "hash": 1735158282, "terms": ["What is ranking.properties.*propertyname* for?", "is ranking.properties.*propertyname* for?", "ranking.properties.*propertyname* for?", "for?"], "tokens": ["What", "is", "ranking", "properties", "*", "propertyname", "*", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1201732035", "fields": {"term": "What is ranking.softtimeout.* for?", "namespace": "open-p", "hash": -1201732035, "terms": ["What is ranking.softtimeout.* for?", "is ranking.softtimeout.* for?", "ranking.softtimeout.* for?", "for?"], "tokens": ["What", "is", "ranking", "softtimeout", "*", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::693038876", "fields": {"term": "What is ranking.queryCache for?", "namespace": "open-p", "hash": 693038876, "terms": ["What is ranking.queryCache for?", "is ranking.queryCache for?", "ranking.queryCache for?", "for?"], "tokens": ["What", "is", "ranking", "queryCache", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1894242949", "fields": {"term": "What is ranking.sorting for?", "namespace": "open-p", "hash": 1894242949, "terms": ["What is ranking.sorting for?", "is ranking.sorting for?", "ranking.sorting for?", "for?"], "tokens": ["What", "is", "ranking", "sorting", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1734436846", "fields": {"term": "What is ranking.rerankCount for?", "namespace": "open-p", "hash": 1734436846, "terms": ["What is ranking.rerankCount for?", "is ranking.rerankCount for?", "ranking.rerankCount for?", "for?"], "tokens": ["What", "is", "ranking", "rerankCount", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1244966380", "fields": {"term": "What is numThreadsPerSearch?", "namespace": "open-p", "hash": 1244966380, "terms": ["What is numThreadsPerSearch?", "is numThreadsPerSearch?", "numThreadsPerSearch?"], "tokens": ["What", "is", "numThreadsPerSearch", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-60070629", "fields": {"term": "What is minHitsPerThread?", "namespace": "open-p", "hash": -60070629, "terms": ["What is minHitsPerThread?", "is minHitsPerThread?", "minHitsPerThread?"], "tokens": ["What", "is", "minHitsPerThread", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-317100686", "fields": {"term": "What is numSearchPartitions?", "namespace": "open-p", "hash": -317100686, "terms": ["What is numSearchPartitions?", "is numSearchPartitions?", "numSearchPartitions?"], "tokens": ["What", "is", "numSearchPartitions", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1473243893", "fields": {"term": "What is termwiseLimit?", "namespace": "open-p", "hash": -1473243893, "terms": ["What is termwiseLimit?", "is termwiseLimit?", "termwiseLimit?"], "tokens": ["What", "is", "termwiseLimit", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1497741389", "fields": {"term": "What is postFilterThreshold?", "namespace": "open-p", "hash": 1497741389, "terms": ["What is postFilterThreshold?", "is postFilterThreshold?", "postFilterThreshold?"], "tokens": ["What", "is", "postFilterThreshold", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1833768271", "fields": {"term": "What is approximateThreshold?", "namespace": "open-p", "hash": 1833768271, "terms": ["What is approximateThreshold?", "is approximateThreshold?", "approximateThreshold?"], "tokens": ["What", "is", "approximateThreshold", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1403011506", "fields": {"term": "What are ranking settings for query matching?", "namespace": "open-p", "hash": 1403011506, "terms": ["What are ranking settings for query matching?", "are ranking settings for query matching?", "ranking settings for query matching?", "settings for query matching?", "for query matching?", "query matching?", "matching?"], "tokens": ["What", "are", "ranking", "settings", "for", "query", "matching", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1192066728", "fields": {"term": "What do ranking.matching parameters do?", "namespace": "open-p", "hash": 1192066728, "terms": ["What do ranking.matching parameters do?", "do ranking.matching parameters do?", "ranking.matching parameters do?", "parameters do?", "do?"], "tokens": ["What", "do", "ranking", "matching", "parameters", "do", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::649897485", "fields": {"term": "What are match-phase settings?", "namespace": "open-p", "hash": 649897485, "terms": ["What are match-phase settings?", "are match-phase settings?", "match-phase settings?", "settings?"], "tokens": ["What", "are", "match", "phase", "settings", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1132729271", "fields": {"term": "What is max-hits in match-phase?", "namespace": "open-p", "hash": 1132729271, "terms": ["What is max-hits in match-phase?", "is max-hits in match-phase?", "max-hits in match-phase?", "in match-phase?", "match-phase?"], "tokens": ["What", "is", "max", "hits", "in", "match", "phase", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1601508473", "fields": {"term": "What is diversity attribute in match-phase?", "namespace": "open-p", "hash": -1601508473, "terms": ["What is diversity attribute in match-phase?", "is diversity attribute in match-phase?", "diversity attribute in match-phase?", "attribute in match-phase?", "in match-phase?", "match-phase?"], "tokens": ["What", "is", "diversity", "attribute", "in", "match", "phase", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::231967768", "fields": {"term": "What is min-groups in match-phase?", "namespace": "open-p", "hash": 231967768, "terms": ["What is min-groups in match-phase?", "is min-groups in match-phase?", "min-groups in match-phase?", "in match-phase?", "match-phase?"], "tokens": ["What", "is", "min", "groups", "in", "match", "phase", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::132527619", "fields": {"term": "What is dispatch.topKProbability?", "namespace": "open-p", "hash": 132527619, "terms": ["What is dispatch.topKProbability?", "is dispatch.topKProbability?", "dispatch.topKProbability?"], "tokens": ["What", "is", "dispatch", "topKProbability", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1045749742", "fields": {"term": "What is the type of dispatch.topKProbability?", "namespace": "open-p", "hash": 1045749742, "terms": ["What is the type of dispatch.topKProbability?", "is the type of dispatch.topKProbability?", "the type of dispatch.topKProbability?", "type of dispatch.topKProbability?", "of dispatch.topKProbability?", "dispatch.topKProbability?"], "tokens": ["What", "is", "the", "type", "of", "dispatch", "topKProbability", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2013526750", "fields": {"term": "What is the default value of dispatch.topKProbability?", "namespace": "open-p", "hash": -2013526750, "terms": ["What is the default value of dispatch.topKProbability?", "is the default value of dispatch.topKProbability?", "the default value of dispatch.topKProbability?", "default value of dispatch.topKProbability?", "value of dispatch.topKProbability?", "of dispatch.topKProbability?", "dispatch.topKProbability?"], "tokens": ["What", "is", "the", "default", "value", "of", "dispatch", "topKProbability", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1987995688", "fields": {"term": "Where can I find details about dispatch.topKProbability?", "namespace": "open-p", "hash": -1987995688, "terms": ["Where can I find details about dispatch.topKProbability?", "can I find details about dispatch.topKProbability?", "I find details about dispatch.topKProbability?", "find details about dispatch.topKProbability?", "details about dispatch.topKProbability?", "about dispatch.topKProbability?", "dispatch.topKProbability?"], "tokens": ["Where", "can", "I", "find", "details", "about", "dispatch", "topKProbability", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1886644270", "fields": {"term": "What is the purpose of dispatch.topKProbability?", "namespace": "open-p", "hash": -1886644270, "terms": ["What is the purpose of dispatch.topKProbability?", "is the purpose of dispatch.topKProbability?", "the purpose of dispatch.topKProbability?", "purpose of dispatch.topKProbability?", "of dispatch.topKProbability?", "dispatch.topKProbability?"], "tokens": ["What", "is", "the", "purpose", "of", "dispatch", "topKProbability", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2125052625", "fields": {"term": "What is the alias for dispatch.topKProbability?", "namespace": "open-p", "hash": 2125052625, "terms": ["What is the alias for dispatch.topKProbability?", "is the alias for dispatch.topKProbability?", "the alias for dispatch.topKProbability?", "alias for dispatch.topKProbability?", "for dispatch.topKProbability?", "dispatch.topKProbability?"], "tokens": ["What", "is", "the", "alias", "for", "dispatch", "topKProbability", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-635804597", "fields": {"term": "What is the range of dispatch.topKProbability?", "namespace": "open-p", "hash": -635804597, "terms": ["What is the range of dispatch.topKProbability?", "is the range of dispatch.topKProbability?", "the range of dispatch.topKProbability?", "range of dispatch.topKProbability?", "of dispatch.topKProbability?", "dispatch.topKProbability?"], "tokens": ["What", "is", "the", "range", "of", "dispatch", "topKProbability", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-830069846", "fields": {"term": "What is the importance of dispatch.topKProbability in result sets?", "namespace": "open-p", "hash": -830069846, "terms": ["What is the importance of dispatch.topKProbability in result sets?", "is the importance of dispatch.topKProbability in result sets?", "the importance of dispatch.topKProbability in result sets?", "importance of dispatch.topKProbability in result sets?", "of dispatch.topKProbability in result sets?", "dispatch.topKProbability in result sets?", "in result sets?", "result sets?", "sets?"], "tokens": ["What", "is", "the", "importance", "of", "dispatch", "topKProbability", "in", "result", "sets", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1090572960", "fields": {"term": "What is presentation.bolding?", "namespace": "open-p", "hash": 1090572960, "terms": ["What is presentation.bolding?", "is presentation.bolding?", "presentation.bolding?"], "tokens": ["What", "is", "presentation", "bolding", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-581024591", "fields": {"term": "What is presentation.format used for?", "namespace": "open-p", "hash": -581024591, "terms": ["What is presentation.format used for?", "is presentation.format used for?", "presentation.format used for?", "used for?", "for?"], "tokens": ["What", "is", "presentation", "format", "used", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::287148775", "fields": {"term": "What does presentation.timing control?", "namespace": "open-p", "hash": 287148775, "terms": ["What does presentation.timing control?", "does presentation.timing control?", "presentation.timing control?", "control?"], "tokens": ["What", "does", "presentation", "timing", "control", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-708549593", "fields": {"term": "What are the different values for presentation.format.tensors?", "namespace": "open-p", "hash": -708549593, "terms": ["What are the different values for presentation.format.tensors?", "are the different values for presentation.format.tensors?", "the different values for presentation.format.tensors?", "different values for presentation.format.tensors?", "values for presentation.format.tensors?", "for presentation.format.tensors?", "presentation.format.tensors?"], "tokens": ["What", "are", "the", "different", "values", "for", "presentation", "format", "tensors", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-696990808", "fields": {"term": "What is the default value for presentation.timing?", "namespace": "open-p", "hash": -696990808, "terms": ["What is the default value for presentation.timing?", "is the default value for presentation.timing?", "the default value for presentation.timing?", "default value for presentation.timing?", "value for presentation.timing?", "for presentation.timing?", "presentation.timing?"], "tokens": ["What", "is", "the", "default", "value", "for", "presentation", "timing", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::518582415", "fields": {"term": "What is the default value for presentation.bolding?", "namespace": "open-p", "hash": 518582415, "terms": ["What is the default value for presentation.bolding?", "is the default value for presentation.bolding?", "the default value for presentation.bolding?", "default value for presentation.bolding?", "value for presentation.bolding?", "for presentation.bolding?", "presentation.bolding?"], "tokens": ["What", "is", "the", "default", "value", "for", "presentation", "bolding", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-65654353", "fields": {"term": "What is presentation.summary used for?", "namespace": "open-p", "hash": -65654353, "terms": ["What is presentation.summary used for?", "is presentation.summary used for?", "presentation.summary used for?", "used for?", "for?"], "tokens": ["What", "is", "presentation", "summary", "used", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1027618980", "fields": {"term": "What is the 'grouping' parameter?", "namespace": "open-p", "hash": 1027618980, "terms": ["What is the 'grouping' parameter?", "is the 'grouping' parameter?", "the 'grouping' parameter?", "'grouping' parameter?", "parameter?"], "tokens": ["What", "is", "the", "'", "grouping", "'", "parameter", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::663970738", "fields": {"term": "What is 'collapsing'?", "namespace": "open-p", "hash": 663970738, "terms": ["What is 'collapsing'?", "is 'collapsing'?", "'collapsing'?"], "tokens": ["What", "is", "'", "collapsing", "'?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-207509261", "fields": {"term": "What is the default 'grouping' limit?", "namespace": "open-p", "hash": -207509261, "terms": ["What is the default 'grouping' limit?", "is the default 'grouping' limit?", "the default 'grouping' limit?", "default 'grouping' limit?", "'grouping' limit?", "limit?"], "tokens": ["What", "is", "the", "default", "'", "grouping", "'", "limit", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::731353601", "fields": {"term": "What is the 'collapsesize' parameter for?", "namespace": "open-p", "hash": 731353601, "terms": ["What is the 'collapsesize' parameter for?", "is the 'collapsesize' parameter for?", "the 'collapsesize' parameter for?", "'collapsesize' parameter for?", "parameter for?", "for?"], "tokens": ["What", "is", "the", "'", "collapsesize", "'", "parameter", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1656447161", "fields": {"term": "What is the 'collapsefield' parameter used for?", "namespace": "open-p", "hash": 1656447161, "terms": ["What is the 'collapsefield' parameter used for?", "is the 'collapsefield' parameter used for?", "the 'collapsefield' parameter used for?", "'collapsefield' parameter used for?", "parameter used for?", "used for?", "for?"], "tokens": ["What", "is", "the", "'", "collapsefield", "'", "parameter", "used", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2003172719", "fields": {"term": "What is the 'defaultPrecisionFactor' parameter for?", "namespace": "open-p", "hash": -2003172719, "terms": ["What is the 'defaultPrecisionFactor' parameter for?", "is the 'defaultPrecisionFactor' parameter for?", "the 'defaultPrecisionFactor' parameter for?", "'defaultPrecisionFactor' parameter for?", "parameter for?", "for?"], "tokens": ["What", "is", "the", "'", "defaultPrecisionFactor", "'", "parameter", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-425995325", "fields": {"term": "What is the 'grouping.globalMaxGroups' parameter for?", "namespace": "open-p", "hash": -425995325, "terms": ["What is the 'grouping.globalMaxGroups' parameter for?", "is the 'grouping.globalMaxGroups' parameter for?", "the 'grouping.globalMaxGroups' parameter for?", "'grouping.globalMaxGroups' parameter for?", "parameter for?", "for?"], "tokens": ["What", "is", "the", "'", "grouping", "globalMaxGroups", "'", "parameter", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-623641946", "fields": {"term": "What is trace.level for?", "namespace": "open-p", "hash": -623641946, "terms": ["What is trace.level for?", "is trace.level for?", "trace.level for?", "for?"], "tokens": ["What", "is", "trace", "level", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1861288023", "fields": {"term": "What is trace.explainLevel?", "namespace": "open-p", "hash": 1861288023, "terms": ["What is trace.explainLevel?", "is trace.explainLevel?", "trace.explainLevel?"], "tokens": ["What", "is", "trace", "explainLevel", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::901298184", "fields": {"term": "What is trace.profileDepth?", "namespace": "open-p", "hash": 901298184, "terms": ["What is trace.profileDepth?", "is trace.profileDepth?", "trace.profileDepth?"], "tokens": ["What", "is", "trace", "profileDepth", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1453398621", "fields": {"term": "What is trace.timestamps?", "namespace": "open-p", "hash": 1453398621, "terms": ["What is trace.timestamps?", "is trace.timestamps?", "trace.timestamps?"], "tokens": ["What", "is", "trace", "timestamps", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::143122154", "fields": {"term": "What is trace.query?", "namespace": "open-p", "hash": 143122154, "terms": ["What is trace.query?", "is trace.query?", "trace.query?"], "tokens": ["What", "is", "trace", "query", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1767112830", "fields": {"term": "What is the syntax of trace.level?", "namespace": "open-p", "hash": 1767112830, "terms": ["What is the syntax of trace.level?", "is the syntax of trace.level?", "the syntax of trace.level?", "syntax of trace.level?", "of trace.level?", "trace.level?"], "tokens": ["What", "is", "the", "syntax", "of", "trace", "level", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1298985654", "fields": {"term": "What are semantic rules?", "namespace": "open-p", "hash": -1298985654, "terms": ["What are semantic rules?", "are semantic rules?", "semantic rules?", "rules?"], "tokens": ["What", "are", "semantic", "rules", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::-1384444357", "fields": {"term": "What is the parameter rules.off used for?", "namespace": "open-p", "hash": -1384444357, "terms": ["What is the parameter rules.off used for?", "is the parameter rules.off used for?", "the parameter rules.off used for?", "parameter rules.off used for?", "rules.off used for?", "used for?", "for?"], "tokens": ["What", "is", "the", "parameter", "rules", "off", "used", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1198911406", "fields": {"term": "What is the default value of rules.off?", "namespace": "open-p", "hash": -1198911406, "terms": ["What is the default value of rules.off?", "is the default value of rules.off?", "the default value of rules.off?", "default value of rules.off?", "value of rules.off?", "of rules.off?", "rules.off?"], "tokens": ["What", "is", "the", "default", "value", "of", "rules", "off", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1573861406", "fields": {"term": "What is the parameter rules.rulebase?", "namespace": "open-p", "hash": 1573861406, "terms": ["What is the parameter rules.rulebase?", "is the parameter rules.rulebase?", "the parameter rules.rulebase?", "parameter rules.rulebase?", "rules.rulebase?"], "tokens": ["What", "is", "the", "parameter", "rules", "rulebase", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1252073824", "fields": {"term": "What is the purpose of tracelevel.rules?", "namespace": "open-p", "hash": -1252073824, "terms": ["What is the purpose of tracelevel.rules?", "is the purpose of tracelevel.rules?", "the purpose of tracelevel.rules?", "purpose of tracelevel.rules?", "of tracelevel.rules?", "tracelevel.rules?"], "tokens": ["What", "is", "the", "purpose", "of", "tracelevel", "rules", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-134232407", "fields": {"term": "What does tracelevel.rules control?", "namespace": "open-p", "hash": -134232407, "terms": ["What does tracelevel.rules control?", "does tracelevel.rules control?", "tracelevel.rules control?", "control?"], "tokens": ["What", "does", "tracelevel", "rules", "control", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::592359822", "fields": {"term": "What is the type of rules.off?", "namespace": "open-p", "hash": 592359822, "terms": ["What is the type of rules.off?", "is the type of rules.off?", "the type of rules.off?", "type of rules.off?", "of rules.off?", "rules.off?"], "tokens": ["What", "is", "the", "type", "of", "rules", "off", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::205032572", "fields": {"term": "What is the type of tracelevel.rules?", "namespace": "open-p", "hash": 205032572, "terms": ["What is the type of tracelevel.rules?", "is the type of tracelevel.rules?", "the type of tracelevel.rules?", "type of tracelevel.rules?", "of tracelevel.rules?", "tracelevel.rules?"], "tokens": ["What", "is", "the", "type", "of", "tracelevel", "rules", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1234268152", "fields": {"term": "What is the 'recall' parameter?", "namespace": "open-p", "hash": -1234268152, "terms": ["What is the 'recall' parameter?", "is the 'recall' parameter?", "the 'recall' parameter?", "'recall' parameter?", "parameter?"], "tokens": ["What", "is", "the", "'", "recall", "'", "parameter", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1428067464", "fields": {"term": "What is the 'user' parameter?", "namespace": "open-p", "hash": 1428067464, "terms": ["What is the 'user' parameter?", "is the 'user' parameter?", "the 'user' parameter?", "'user' parameter?", "parameter?"], "tokens": ["What", "is", "the", "'", "user", "'", "parameter", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-508565324", "fields": {"term": "What is the purpose of 'hitcountestimate'?", "namespace": "open-p", "hash": -508565324, "terms": ["What is the purpose of 'hitcountestimate'?", "is the purpose of 'hitcountestimate'?", "the purpose of 'hitcountestimate'?", "purpose of 'hitcountestimate'?", "of 'hitcountestimate'?", "'hitcountestimate'?"], "tokens": ["What", "is", "the", "purpose", "of", "'", "hitcountestimate", "'?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1274332537", "fields": {"term": "Can metric collection be ignored for a query?", "namespace": "open-p", "hash": -1274332537, "terms": ["Can metric collection be ignored for a query?", "metric collection be ignored for a query?", "collection be ignored for a query?", "be ignored for a query?", "ignored for a query?", "for a query?", "a query?", "query?"], "tokens": ["Can", "metric", "collection", "be", "ignored", "for", "a", "query", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1041867363", "fields": {"term": "What happens when weakAnd.replace is set to true?", "namespace": "open-p", "hash": 1041867363, "terms": ["What happens when weakAnd.replace is set to true?", "happens when weakAnd.replace is set to true?", "when weakAnd.replace is set to true?", "weakAnd.replace is set to true?", "is set to true?", "set to true?", "to true?", "true?"], "tokens": ["What", "happens", "when", "weakAnd", "replace", "is", "set", "to", "true", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1671078942", "fields": {"term": "What is the 'wand.hits' parameter used for?", "namespace": "open-p", "hash": -1671078942, "terms": ["What is the 'wand.hits' parameter used for?", "is the 'wand.hits' parameter used for?", "the 'wand.hits' parameter used for?", "'wand.hits' parameter used for?", "parameter used for?", "used for?", "for?"], "tokens": ["What", "is", "the", "'", "wand", "hits", "'", "parameter", "used", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2008399971", "fields": {"term": "What does 'sorting.degrading' optimize?", "namespace": "open-p", "hash": 2008399971, "terms": ["What does 'sorting.degrading' optimize?", "does 'sorting.degrading' optimize?", "'sorting.degrading' optimize?", "optimize?"], "tokens": ["What", "does", "'", "sorting", "degrading", "'", "optimize", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::297849112", "fields": {"term": "What is the purpose of the 'noCache' parameter?", "namespace": "open-p", "hash": 297849112, "terms": ["What is the purpose of the 'noCache' parameter?", "is the purpose of the 'noCache' parameter?", "the purpose of the 'noCache' parameter?", "purpose of the 'noCache' parameter?", "of the 'noCache' parameter?", "the 'noCache' parameter?", "'noCache' parameter?", "parameter?"], "tokens": ["What", "is", "the", "purpose", "of", "the", "'", "noCache", "'", "parameter", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-549941283", "fields": {"term": "What are the status code rules?", "namespace": "open-p", "hash": -549941283, "terms": ["What are the status code rules?", "are the status code rules?", "the status code rules?", "status code rules?", "code rules?", "rules?"], "tokens": ["What", "are", "the", "status", "code", "rules", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::375048215", "fields": {"term": "What are WEB SERVICE ERROR CODES?", "namespace": "open-p", "hash": 375048215, "terms": ["What are WEB SERVICE ERROR CODES?", "are WEB SERVICE ERROR CODES?", "WEB SERVICE ERROR CODES?", "SERVICE ERROR CODES?", "ERROR CODES?", "CODES?"], "tokens": ["What", "are", "WEB", "SERVICE", "ERROR", "CODES", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1230523117", "fields": {"term": "What is returned if there are errors?", "namespace": "open-p", "hash": -1230523117, "terms": ["What is returned if there are errors?", "is returned if there are errors?", "returned if there are errors?", "if there are errors?", "there are errors?", "are errors?", "errors?"], "tokens": ["What", "is", "returned", "if", "there", "are", "errors", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1441489128", "fields": {"term": "What is returned for valid hits?", "namespace": "open-p", "hash": -1441489128, "terms": ["What is returned for valid hits?", "is returned for valid hits?", "returned for valid hits?", "for valid hits?", "valid hits?", "hits?"], "tokens": ["What", "is", "returned", "for", "valid", "hits", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1102562", "fields": {"term": "What errors return 500?", "namespace": "open-p", "hash": -1102562, "terms": ["What errors return 500?", "errors return 500?", "return 500?", "500?"], "tokens": ["What", "errors", "return", "500", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::935710232", "fields": {"term": "What errors return 200?", "namespace": "open-p", "hash": 935710232, "terms": ["What errors return 200?", "errors return 200?", "return 200?", "200?"], "tokens": ["What", "errors", "return", "200", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2064774294", "fields": {"term": "What is INTERNAL_SERVER_ERROR mapped to?", "namespace": "open-p", "hash": -2064774294, "terms": ["What is INTERNAL_SERVER_ERROR mapped to?", "is INTERNAL_SERVER_ERROR mapped to?", "INTERNAL_SERVER_ERROR mapped to?", "mapped to?", "to?"], "tokens": ["What", "is", "INTERNAL_SERVER_ERROR", "mapped", "to", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::497985781", "fields": {"term": "What is a select query?", "namespace": "open-p", "hash": 497985781, "terms": ["What is a select query?", "is a select query?", "a select query?", "select query?", "query?"], "tokens": ["What", "is", "a", "select", "query", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1823130470", "fields": {"term": "What are the subparameters of select query?", "namespace": "open-p", "hash": -1823130470, "terms": ["What are the subparameters of select query?", "are the subparameters of select query?", "the subparameters of select query?", "subparameters of select query?", "of select query?", "select query?", "query?"], "tokens": ["What", "are", "the", "subparameters", "of", "select", "query", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1829613762", "fields": {"term": "What is the Vespa Query API?", "namespace": "open-p", "hash": 1829613762, "terms": ["What is the Vespa Query API?", "is the Vespa Query API?", "the Vespa Query API?", "Vespa Query API?", "Query API?", "API?"], "tokens": ["What", "is", "the", "Vespa", "Query", "API", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1602905316", "fields": {"term": "Where can I find the Query API reference?", "namespace": "open-p", "hash": -1602905316, "terms": ["Where can I find the Query API reference?", "can I find the Query API reference?", "I find the Query API reference?", "find the Query API reference?", "the Query API reference?", "Query API reference?", "API reference?", "reference?"], "tokens": ["Where", "can", "I", "find", "the", "Query", "API", "reference", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1306304755", "fields": {"term": "What is the data flow?", "namespace": "open-p", "hash": -1306304755, "terms": ["What is the data flow?", "is the data flow?", "the data flow?", "data flow?", "flow?"], "tokens": ["What", "is", "the", "data", "flow", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::603283567", "fields": {"term": "What is the structure of input data?", "namespace": "open-p", "hash": 603283567, "terms": ["What is the structure of input data?", "is the structure of input data?", "the structure of input data?", "structure of input data?", "of input data?", "input data?", "data?"], "tokens": ["What", "is", "the", "structure", "of", "input", "data", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::213158454", "fields": {"term": "What is userInput?", "namespace": "open-p", "hash": 213158454, "terms": ["What is userInput?", "is userInput?", "userInput?"], "tokens": ["What", "is", "userInput", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1085660853", "fields": {"term": "How can user input be expressed?", "namespace": "open-p", "hash": 1085660853, "terms": ["How can user input be expressed?", "can user input be expressed?", "user input be expressed?", "input be expressed?", "be expressed?", "expressed?"], "tokens": ["How", "can", "user", "input", "be", "expressed", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1412692724", "fields": {"term": "What is the query feature called user_profile?", "namespace": "open-p", "hash": -1412692724, "terms": ["What is the query feature called user_profile?", "is the query feature called user_profile?", "the query feature called user_profile?", "query feature called user_profile?", "feature called user_profile?", "called user_profile?", "user_profile?"], "tokens": ["What", "is", "the", "query", "feature", "called", "user_profile", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1615892965", "fields": {"term": "What is rank_albums used for?", "namespace": "open-p", "hash": 1615892965, "terms": ["What is rank_albums used for?", "is rank_albums used for?", "rank_albums used for?", "used for?", "for?"], "tokens": ["What", "is", "rank_albums", "used", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::290666649", "fields": {"term": "What is the userInput() function?", "namespace": "open-p", "hash": 290666649, "terms": ["What is the userInput() function?", "is the userInput() function?", "the userInput() function?", "userInput() function?", "function?"], "tokens": ["What", "is", "the", "userInput", "()", "function", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1382889194", "fields": {"term": "What does weakAnd() do?", "namespace": "open-p", "hash": -1382889194, "terms": ["What does weakAnd() do?", "does weakAnd() do?", "weakAnd() do?", "do?"], "tokens": ["What", "does", "weakAnd", "()", "do", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1696162392", "fields": {"term": "How do you combine multiple query properties?", "namespace": "open-p", "hash": 1696162392, "terms": ["How do you combine multiple query properties?", "do you combine multiple query properties?", "you combine multiple query properties?", "combine multiple query properties?", "multiple query properties?", "query properties?", "properties?"], "tokens": ["How", "do", "you", "combine", "multiple", "query", "properties", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1283755904", "fields": {"term": "What is a grammar-annotation?", "namespace": "open-p", "hash": 1283755904, "terms": ["What is a grammar-annotation?", "is a grammar-annotation?", "a grammar-annotation?", "grammar-annotation?"], "tokens": ["What", "is", "a", "grammar", "annotation", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::740714409", "fields": {"term": "How does the linguistic library split input?", "namespace": "open-p", "hash": 740714409, "terms": ["How does the linguistic library split input?", "does the linguistic library split input?", "the linguistic library split input?", "linguistic library split input?", "library split input?", "split input?", "input?"], "tokens": ["How", "does", "the", "linguistic", "library", "split", "input", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-157986058", "fields": {"term": "How can query profiles be nested?", "namespace": "open-p", "hash": -157986058, "terms": ["How can query profiles be nested?", "can query profiles be nested?", "query profiles be nested?", "profiles be nested?", "be nested?", "nested?"], "tokens": ["How", "can", "query", "profiles", "be", "nested", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-266090314", "fields": {"term": "What are query profiles used for?", "namespace": "open-p", "hash": -266090314, "terms": ["What are query profiles used for?", "are query profiles used for?", "query profiles used for?", "profiles used for?", "used for?", "for?"], "tokens": ["What", "are", "query", "profiles", "used", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1393402098", "fields": {"term": "How can query strings be shortened?", "namespace": "open-p", "hash": 1393402098, "terms": ["How can query strings be shortened?", "can query strings be shortened?", "query strings be shortened?", "strings be shortened?", "be shortened?", "shortened?"], "tokens": ["How", "can", "query", "strings", "be", "shortened", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::243798369", "fields": {"term": "Can query profiles be versioned?", "namespace": "open-p", "hash": 243798369, "terms": ["Can query profiles be versioned?", "query profiles be versioned?", "profiles be versioned?", "be versioned?", "versioned?"], "tokens": ["Can", "query", "profiles", "be", "versioned", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1704474830", "fields": {"term": "What is query profile inheritance?", "namespace": "open-p", "hash": 1704474830, "terms": ["What is query profile inheritance?", "is query profile inheritance?", "query profile inheritance?", "profile inheritance?", "inheritance?"], "tokens": ["What", "is", "query", "profile", "inheritance", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::984117780", "fields": {"term": "What is geo search?", "namespace": "open-p", "hash": 984117780, "terms": ["What is geo search?", "is geo search?", "geo search?", "search?"], "tokens": ["What", "is", "geo", "search", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::421737556", "fields": {"term": "What is DistanceToPath?", "namespace": "open-p", "hash": 421737556, "terms": ["What is DistanceToPath?", "is DistanceToPath?", "DistanceToPath?"], "tokens": ["What", "is", "DistanceToPath", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1749958392", "fields": {"term": "What is rank_albums?", "namespace": "open-p", "hash": -1749958392, "terms": ["What is rank_albums?", "is rank_albums?", "rank_albums?"], "tokens": ["What", "is", "rank_albums", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::429035158", "fields": {"term": "What is text ranking?", "namespace": "open-p", "hash": 429035158, "terms": ["What is text ranking?", "is text ranking?", "text ranking?", "ranking?"], "tokens": ["What", "is", "text", "ranking", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-550158678", "fields": {"term": "What is grouping in Vespa?", "namespace": "open-p", "hash": -550158678, "terms": ["What is grouping in Vespa?", "is grouping in Vespa?", "grouping in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["What", "is", "grouping", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-182035963", "fields": {"term": "How does grouping work in Vespa?", "namespace": "open-p", "hash": -182035963, "terms": ["How does grouping work in Vespa?", "does grouping work in Vespa?", "grouping work in Vespa?", "work in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["How", "does", "grouping", "work", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-939230912", "fields": {"term": "What are the benefits of grouping?", "namespace": "open-p", "hash": -939230912, "terms": ["What are the benefits of grouping?", "are the benefits of grouping?", "the benefits of grouping?", "benefits of grouping?", "of grouping?", "grouping?"], "tokens": ["What", "are", "the", "benefits", "of", "grouping", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1291230669", "fields": {"term": "What fields can be used for grouping?", "namespace": "open-p", "hash": -1291230669, "terms": ["What fields can be used for grouping?", "fields can be used for grouping?", "can be used for grouping?", "be used for grouping?", "used for grouping?", "for grouping?", "grouping?"], "tokens": ["What", "fields", "can", "be", "used", "for", "grouping", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1497695634", "fields": {"term": "How is the grouping expression formed?", "namespace": "open-p", "hash": -1497695634, "terms": ["How is the grouping expression formed?", "is the grouping expression formed?", "the grouping expression formed?", "grouping expression formed?", "expression formed?", "formed?"], "tokens": ["How", "is", "the", "grouping", "expression", "formed", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1299529486", "fields": {"term": "What happens when limit 0 is used with grouping?", "namespace": "open-p", "hash": -1299529486, "terms": ["What happens when limit 0 is used with grouping?", "happens when limit 0 is used with grouping?", "when limit 0 is used with grouping?", "limit 0 is used with grouping?", "0 is used with grouping?", "is used with grouping?", "used with grouping?", "with grouping?", "grouping?"], "tokens": ["What", "happens", "when", "limit", "0", "is", "used", "with", "grouping", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-298996433", "fields": {"term": "What are returned in search results by default?", "namespace": "open-p", "hash": -298996433, "terms": ["What are returned in search results by default?", "are returned in search results by default?", "returned in search results by default?", "in search results by default?", "search results by default?", "results by default?", "by default?", "default?"], "tokens": ["What", "are", "returned", "in", "search", "results", "by", "default", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1633061287", "fields": {"term": "What is a dynamic summary?", "namespace": "open-p", "hash": -1633061287, "terms": ["What is a dynamic summary?", "is a dynamic summary?", "a dynamic summary?", "dynamic summary?", "summary?"], "tokens": ["What", "is", "a", "dynamic", "summary", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::729833834", "fields": {"term": "How can summary improve visual relevance?", "namespace": "open-p", "hash": 729833834, "terms": ["How can summary improve visual relevance?", "can summary improve visual relevance?", "summary improve visual relevance?", "improve visual relevance?", "visual relevance?", "relevance?"], "tokens": ["How", "can", "summary", "improve", "visual", "relevance", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1549664807", "fields": {"term": "What is the default output format?", "namespace": "open-p", "hash": -1549664807, "terms": ["What is the default output format?", "is the default output format?", "the default output format?", "default output format?", "output format?", "format?"], "tokens": ["What", "is", "the", "default", "output", "format", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1183179726", "fields": {"term": "How can I write a custom renderer?", "namespace": "open-p", "hash": 1183179726, "terms": ["How can I write a custom renderer?", "can I write a custom renderer?", "I write a custom renderer?", "write a custom renderer?", "a custom renderer?", "custom renderer?", "renderer?"], "tokens": ["How", "can", "I", "write", "a", "custom", "renderer", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::938102022", "fields": {"term": "What are the phases of query execution?", "namespace": "open-p", "hash": 938102022, "terms": ["What are the phases of query execution?", "are the phases of query execution?", "the phases of query execution?", "phases of query execution?", "of query execution?", "query execution?", "execution?"], "tokens": ["What", "are", "the", "phases", "of", "query", "execution", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-914136303", "fields": {"term": "What is trace.explainlevel used for?", "namespace": "open-p", "hash": -914136303, "terms": ["What is trace.explainlevel used for?", "is trace.explainlevel used for?", "trace.explainlevel used for?", "used for?", "for?"], "tokens": ["What", "is", "trace", "explainlevel", "used", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1349816227", "fields": {"term": "How can you modify the query plan?", "namespace": "open-p", "hash": -1349816227, "terms": ["How can you modify the query plan?", "can you modify the query plan?", "you modify the query plan?", "modify the query plan?", "the query plan?", "query plan?", "plan?"], "tokens": ["How", "can", "you", "modify", "the", "query", "plan", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1167009537", "fields": {"term": "What are the benefits of using 'ranked: false' query annotations?", "namespace": "open-p", "hash": -1167009537, "terms": ["What are the benefits of using 'ranked: false' query annotations?", "are the benefits of using 'ranked: false' query annotations?", "the benefits of using 'ranked: false' query annotations?", "benefits of using 'ranked: false' query annotations?", "of using 'ranked: false' query annotations?", "using 'ranked: false' query annotations?", "'ranked: false' query annotations?", "false' query annotations?", "query annotations?", "annotations?"], "tokens": ["What", "are", "the", "benefits", "of", "using", "'", "ranked", ":", "false", "'", "query", "annotations", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-367490385", "fields": {"term": "What is capped range search?", "namespace": "open-p", "hash": -367490385, "terms": ["What is capped range search?", "is capped range search?", "capped range search?", "range search?", "search?"], "tokens": ["What", "is", "capped", "range", "search", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::81427611", "fields": {"term": "How can you efficiently implement top-k selection for ranking a subset of the documents in the index?", "namespace": "open-p", "hash": 81427611, "terms": ["How can you efficiently implement top-k selection for ranking a subset of the documents in the index?", "can you efficiently implement top-k selection for ranking a subset of the documents in the index?", "you efficiently implement top-k selection for ranking a subset of the documents in the index?", "efficiently implement top-k selection for ranking a subset of the documents in the index?", "implement top-k selection for ranking a subset of the documents in the index?", "top-k selection for ranking a subset of the documents in the index?", "selection for ranking a subset of the documents in the index?", "for ranking a subset of the documents in the index?", "ranking a subset of the documents in the index?", "a subset of the documents in the index?", "subset of the documents in the index?", "of the documents in the index?", "the documents in the index?", "documents in the index?", "in the index?", "the index?", "index?"], "tokens": ["How", "can", "you", "efficiently", "implement", "top", "k", "selection", "for", "ranking", "a", "subset", "of", "the", "documents", "in", "the", "index", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1095991237", "fields": {"term": "What is linguistic processing?", "namespace": "open-p", "hash": -1095991237, "terms": ["What is linguistic processing?", "is linguistic processing?", "linguistic processing?", "processing?"], "tokens": ["What", "is", "linguistic", "processing", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1206531576", "fields": {"term": "What does matching do in Vespa?", "namespace": "open-p", "hash": 1206531576, "terms": ["What does matching do in Vespa?", "does matching do in Vespa?", "matching do in Vespa?", "do in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["What", "does", "matching", "do", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-107094042", "fields": {"term": "What is lazy fetching?", "namespace": "open-p", "hash": -107094042, "terms": ["What is lazy fetching?", "is lazy fetching?", "lazy fetching?", "fetching?"], "tokens": ["What", "is", "lazy", "fetching", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2032421317", "fields": {"term": "What happens after content node requests complete?", "namespace": "open-p", "hash": 2032421317, "terms": ["What happens after content node requests complete?", "happens after content node requests complete?", "after content node requests complete?", "content node requests complete?", "node requests complete?", "requests complete?", "complete?"], "tokens": ["What", "happens", "after", "content", "node", "requests", "complete", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::164235405", "fields": {"term": "What HTTP client does Vespa recommend?", "namespace": "open-p", "hash": 164235405, "terms": ["What HTTP client does Vespa recommend?", "HTTP client does Vespa recommend?", "client does Vespa recommend?", "does Vespa recommend?", "Vespa recommend?", "recommend?"], "tokens": ["What", "HTTP", "client", "does", "Vespa", "recommend", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1356023352", "fields": {"term": "What are HTTP best practices?", "namespace": "open-p", "hash": 1356023352, "terms": ["What are HTTP best practices?", "are HTTP best practices?", "HTTP best practices?", "best practices?", "practices?"], "tokens": ["What", "are", "HTTP", "best", "practices", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1099114647", "fields": {"term": "What is the Query API reference?", "namespace": "open-p", "hash": -1099114647, "terms": ["What is the Query API reference?", "is the Query API reference?", "the Query API reference?", "Query API reference?", "API reference?", "reference?"], "tokens": ["What", "is", "the", "Query", "API", "reference", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-93678632", "fields": {"term": "What is Jetty?", "namespace": "open-p", "hash": -93678632, "terms": ["What is Jetty?", "is Jetty?", "Jetty?"], "tokens": ["What", "is", "Jetty", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1556135535", "fields": {"term": "What is requestHeaderSize?", "namespace": "open-p", "hash": -1556135535, "terms": ["What is requestHeaderSize?", "is requestHeaderSize?", "requestHeaderSize?"], "tokens": ["What", "is", "requestHeaderSize", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-200571753", "fields": {"term": "What is URL encoding?", "namespace": "open-p", "hash": -200571753, "terms": ["What is URL encoding?", "is URL encoding?", "URL encoding?", "encoding?"], "tokens": ["What", "is", "URL", "encoding", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-140859092", "fields": {"term": "Which RFC describes URL encoding?", "namespace": "open-p", "hash": -140859092, "terms": ["Which RFC describes URL encoding?", "RFC describes URL encoding?", "describes URL encoding?", "URL encoding?", "encoding?"], "tokens": ["Which", "RFC", "describes", "URL", "encoding", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-204766162", "fields": {"term": "What is the Stack Overflow question about?", "namespace": "open-p", "hash": -204766162, "terms": ["What is the Stack Overflow question about?", "is the Stack Overflow question about?", "the Stack Overflow question about?", "Stack Overflow question about?", "Overflow question about?", "question about?", "about?"], "tokens": ["What", "is", "the", "Stack", "Overflow", "question", "about", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2133882107", "fields": {"term": "What is the query timeout?", "namespace": "open-p", "hash": 2133882107, "terms": ["What is the query timeout?", "is the query timeout?", "the query timeout?", "query timeout?", "timeout?"], "tokens": ["What", "is", "the", "query", "timeout", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1442425289", "fields": {"term": "Does the Query.getTimeLeft() check timeout?", "namespace": "open-p", "hash": 1442425289, "terms": ["Does the Query.getTimeLeft() check timeout?", "the Query.getTimeLeft() check timeout?", "Query.getTimeLeft() check timeout?", "check timeout?", "timeout?"], "tokens": ["Does", "the", "Query", "getTimeLeft", "()", "check", "timeout", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1178306136", "fields": {"term": "How does timeout apply to multiphase searching?", "namespace": "open-p", "hash": 1178306136, "terms": ["How does timeout apply to multiphase searching?", "does timeout apply to multiphase searching?", "timeout apply to multiphase searching?", "apply to multiphase searching?", "to multiphase searching?", "multiphase searching?", "searching?"], "tokens": ["How", "does", "timeout", "apply", "to", "multiphase", "searching", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::554628599", "fields": {"term": "Can we set different timeouts for different chains?", "namespace": "open-p", "hash": 554628599, "terms": ["Can we set different timeouts for different chains?", "we set different timeouts for different chains?", "set different timeouts for different chains?", "different timeouts for different chains?", "timeouts for different chains?", "for different chains?", "different chains?", "chains?"], "tokens": ["Can", "we", "set", "different", "timeouts", "for", "different", "chains", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1150938366", "fields": {"term": "Is the timeout set for the entire search chain?", "namespace": "open-p", "hash": 1150938366, "terms": ["Is the timeout set for the entire search chain?", "the timeout set for the entire search chain?", "timeout set for the entire search chain?", "set for the entire search chain?", "for the entire search chain?", "the entire search chain?", "entire search chain?", "search chain?", "chain?"], "tokens": ["Is", "the", "timeout", "set", "for", "the", "entire", "search", "chain", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::768808636", "fields": {"term": "Can we specify timeout when waiting for responses?", "namespace": "open-p", "hash": 768808636, "terms": ["Can we specify timeout when waiting for responses?", "we specify timeout when waiting for responses?", "specify timeout when waiting for responses?", "timeout when waiting for responses?", "when waiting for responses?", "waiting for responses?", "for responses?", "responses?"], "tokens": ["Can", "we", "specify", "timeout", "when", "waiting", "for", "responses", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1212755664", "fields": {"term": "What is the cooperative timeout handling?", "namespace": "open-p", "hash": 1212755664, "terms": ["What is the cooperative timeout handling?", "is the cooperative timeout handling?", "the cooperative timeout handling?", "cooperative timeout handling?", "timeout handling?", "handling?"], "tokens": ["What", "is", "the", "cooperative", "timeout", "handling", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-623317258", "fields": {"term": "What is the root error element?", "namespace": "open-p", "hash": -623317258, "terms": ["What is the root error element?", "is the root error element?", "the root error element?", "root error element?", "error element?", "element?"], "tokens": ["What", "is", "the", "root", "error", "element", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2046190882", "fields": {"term": "What does a root error mean?", "namespace": "open-p", "hash": -2046190882, "terms": ["What does a root error mean?", "does a root error mean?", "a root error mean?", "root error mean?", "error mean?", "mean?"], "tokens": ["What", "does", "a", "root", "error", "mean", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-746890083", "fields": {"term": "What does the error code 8 mean?", "namespace": "open-p", "hash": -746890083, "terms": ["What does the error code 8 mean?", "does the error code 8 mean?", "the error code 8 mean?", "error code 8 mean?", "code 8 mean?", "8 mean?", "mean?"], "tokens": ["What", "does", "the", "error", "code", "8", "mean", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::709819658", "fields": {"term": "How to locate an attribute vector?", "namespace": "open-p", "hash": 709819658, "terms": ["How to locate an attribute vector?", "to locate an attribute vector?", "locate an attribute vector?", "an attribute vector?", "attribute vector?", "vector?"], "tokens": ["How", "to", "locate", "an", "attribute", "vector", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1886007176", "fields": {"term": "What is the summary of the error?", "namespace": "open-p", "hash": -1886007176, "terms": ["What is the summary of the error?", "is the summary of the error?", "the summary of the error?", "summary of the error?", "of the error?", "the error?", "error?"], "tokens": ["What", "is", "the", "summary", "of", "the", "error", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1946921276", "fields": {"term": "What is the source of the error?", "namespace": "open-p", "hash": 1946921276, "terms": ["What is the source of the error?", "is the source of the error?", "the source of the error?", "source of the error?", "of the error?", "the error?", "error?"], "tokens": ["What", "is", "the", "source", "of", "the", "error", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::888959256", "fields": {"term": "What is the message of the error?", "namespace": "open-p", "hash": 888959256, "terms": ["What is the message of the error?", "is the message of the error?", "the message of the error?", "message of the error?", "of the error?", "the error?", "error?"], "tokens": ["What", "is", "the", "message", "of", "the", "error", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1299520133", "fields": {"term": "What is the error message for a missing yql parameter?", "namespace": "open-p", "hash": -1299520133, "terms": ["What is the error message for a missing yql parameter?", "is the error message for a missing yql parameter?", "the error message for a missing yql parameter?", "error message for a missing yql parameter?", "message for a missing yql parameter?", "for a missing yql parameter?", "a missing yql parameter?", "missing yql parameter?", "yql parameter?", "parameter?"], "tokens": ["What", "is", "the", "error", "message", "for", "a", "missing", "yql", "parameter", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1001843022", "fields": {"term": "How do you enable query tracing?", "namespace": "open-p", "hash": -1001843022, "terms": ["How do you enable query tracing?", "do you enable query tracing?", "you enable query tracing?", "enable query tracing?", "query tracing?", "tracing?"], "tokens": ["How", "do", "you", "enable", "query", "tracing", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::488185169", "fields": {"term": "What is Query.trace used for?", "namespace": "open-p", "hash": 488185169, "terms": ["What is Query.trace used for?", "is Query.trace used for?", "Query.trace used for?", "used for?", "for?"], "tokens": ["What", "is", "Query", "trace", "used", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1576972102", "fields": {"term": "What does Vespa accept?", "namespace": "open-p", "hash": -1576972102, "terms": ["What does Vespa accept?", "does Vespa accept?", "Vespa accept?", "accept?"], "tokens": ["What", "does", "Vespa", "accept", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1280294433", "fields": {"term": "How does Vespa handle human input and structured queries?", "namespace": "open-p", "hash": -1280294433, "terms": ["How does Vespa handle human input and structured queries?", "does Vespa handle human input and structured queries?", "Vespa handle human input and structured queries?", "handle human input and structured queries?", "human input and structured queries?", "input and structured queries?", "and structured queries?", "structured queries?", "queries?"], "tokens": ["How", "does", "Vespa", "handle", "human", "input", "and", "structured", "queries", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::627880", "fields": {"term": "What does a query URL look like?", "namespace": "open-p", "hash": 627880, "terms": ["What does a query URL look like?", "does a query URL look like?", "a query URL look like?", "query URL look like?", "URL look like?", "look like?", "like?"], "tokens": ["What", "does", "a", "query", "URL", "look", "like", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1539951700", "fields": {"term": "What is YQL?", "namespace": "open-p", "hash": -1539951700, "terms": ["What is YQL?", "is YQL?", "YQL?"], "tokens": ["What", "is", "YQL", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::-2079752663", "fields": {"term": "How does YQL match documents?", "namespace": "open-p", "hash": -2079752663, "terms": ["How does YQL match documents?", "does YQL match documents?", "YQL match documents?", "match documents?", "documents?"], "tokens": ["How", "does", "YQL", "match", "documents", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::657116205", "fields": {"term": "How should quote and backslash characters be escaped?", "namespace": "open-p", "hash": 657116205, "terms": ["How should quote and backslash characters be escaped?", "should quote and backslash characters be escaped?", "quote and backslash characters be escaped?", "and backslash characters be escaped?", "backslash characters be escaped?", "characters be escaped?", "be escaped?", "escaped?"], "tokens": ["How", "should", "quote", "and", "backslash", "characters", "be", "escaped", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::789955058", "fields": {"term": "Is a semicolon required at the end of YQL queries?", "namespace": "open-p", "hash": 789955058, "terms": ["Is a semicolon required at the end of YQL queries?", "a semicolon required at the end of YQL queries?", "semicolon required at the end of YQL queries?", "required at the end of YQL queries?", "at the end of YQL queries?", "the end of YQL queries?", "end of YQL queries?", "of YQL queries?", "YQL queries?", "queries?"], "tokens": ["Is", "a", "semicolon", "required", "at", "the", "end", "of", "YQL", "queries", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1160124477", "fields": {"term": "From which version of Vespa do YQL queries not require a semicolon?", "namespace": "open-p", "hash": 1160124477, "terms": ["From which version of Vespa do YQL queries not require a semicolon?", "which version of Vespa do YQL queries not require a semicolon?", "version of Vespa do YQL queries not require a semicolon?", "of Vespa do YQL queries not require a semicolon?", "Vespa do YQL queries not require a semicolon?", "do YQL queries not require a semicolon?", "YQL queries not require a semicolon?", "queries not require a semicolon?", "not require a semicolon?", "require a semicolon?", "a semicolon?", "semicolon?"], "tokens": ["From", "which", "version", "of", "Vespa", "do", "YQL", "queries", "not", "require", "a", "semicolon", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1242811926", "fields": {"term": "How do you select specific fields?", "namespace": "open-p", "hash": 1242811926, "terms": ["How do you select specific fields?", "do you select specific fields?", "you select specific fields?", "select specific fields?", "specific fields?", "fields?"], "tokens": ["How", "do", "you", "select", "specific", "fields", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::47829061", "fields": {"term": "What does Vespa hide in matching documents?", "namespace": "open-p", "hash": 47829061, "terms": ["What does Vespa hide in matching documents?", "does Vespa hide in matching documents?", "Vespa hide in matching documents?", "hide in matching documents?", "in matching documents?", "matching documents?", "documents?"], "tokens": ["What", "does", "Vespa", "hide", "in", "matching", "documents", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1345545182", "fields": {"term": "What is the *sources* keyword for?", "namespace": "open-p", "hash": -1345545182, "terms": ["What is the *sources* keyword for?", "is the *sources* keyword for?", "the *sources* keyword for?", "*sources* keyword for?", "keyword for?", "for?"], "tokens": ["What", "is", "the", "*", "sources", "*", "keyword", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-527260286", "fields": {"term": "How do you restrict the query to a single schema?", "namespace": "open-p", "hash": -527260286, "terms": ["How do you restrict the query to a single schema?", "do you restrict the query to a single schema?", "you restrict the query to a single schema?", "restrict the query to a single schema?", "the query to a single schema?", "query to a single schema?", "to a single schema?", "a single schema?", "single schema?", "schema?"], "tokens": ["How", "do", "you", "restrict", "the", "query", "to", "a", "single", "schema", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1905233514", "fields": {"term": "What is order by in Vespa?", "namespace": "open-p", "hash": 1905233514, "terms": ["What is order by in Vespa?", "is order by in Vespa?", "order by in Vespa?", "by in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["What", "is", "order", "by", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-517569536", "fields": {"term": "How to do primary ordering on rank score in Vespa?", "namespace": "open-p", "hash": -517569536, "terms": ["How to do primary ordering on rank score in Vespa?", "to do primary ordering on rank score in Vespa?", "do primary ordering on rank score in Vespa?", "primary ordering on rank score in Vespa?", "ordering on rank score in Vespa?", "on rank score in Vespa?", "rank score in Vespa?", "score in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["How", "to", "do", "primary", "ordering", "on", "rank", "score", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1298217027", "fields": {"term": "How does Vespa sort documents with the same primary ordering attribute?", "namespace": "open-p", "hash": -1298217027, "terms": ["How does Vespa sort documents with the same primary ordering attribute?", "does Vespa sort documents with the same primary ordering attribute?", "Vespa sort documents with the same primary ordering attribute?", "sort documents with the same primary ordering attribute?", "documents with the same primary ordering attribute?", "with the same primary ordering attribute?", "the same primary ordering attribute?", "same primary ordering attribute?", "primary ordering attribute?", "ordering attribute?", "attribute?"], "tokens": ["How", "does", "Vespa", "sort", "documents", "with", "the", "same", "primary", "ordering", "attribute", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1227330419", "fields": {"term": "What are special sorting attributes in Vespa?", "namespace": "open-p", "hash": -1227330419, "terms": ["What are special sorting attributes in Vespa?", "are special sorting attributes in Vespa?", "special sorting attributes in Vespa?", "sorting attributes in Vespa?", "attributes in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["What", "are", "special", "sorting", "attributes", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-308732345", "fields": {"term": "How to limit the number of hits?", "namespace": "open-p", "hash": -308732345, "terms": ["How to limit the number of hits?", "to limit the number of hits?", "limit the number of hits?", "the number of hits?", "number of hits?", "of hits?", "hits?"], "tokens": ["How", "to", "limit", "the", "number", "of", "hits", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::678359196", "fields": {"term": "What are native execution parameters?", "namespace": "open-p", "hash": 678359196, "terms": ["What are native execution parameters?", "are native execution parameters?", "native execution parameters?", "execution parameters?", "parameters?"], "tokens": ["What", "are", "native", "execution", "parameters", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1070436434", "fields": {"term": "What is maxOffset?", "namespace": "open-p", "hash": 1070436434, "terms": ["What is maxOffset?", "is maxOffset?", "maxOffset?"], "tokens": ["What", "is", "maxOffset", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-327131010", "fields": {"term": "What is queryProfile?", "namespace": "open-p", "hash": -327131010, "terms": ["What is queryProfile?", "is queryProfile?", "queryProfile?"], "tokens": ["What", "is", "queryProfile", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1363794273", "fields": {"term": "How to set query timeout?", "namespace": "open-p", "hash": -1363794273, "terms": ["How to set query timeout?", "to set query timeout?", "set query timeout?", "query timeout?", "timeout?"], "tokens": ["How", "to", "set", "query", "timeout", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2007391248", "fields": {"term": "Can timeout be controlled natively?", "namespace": "open-p", "hash": 2007391248, "terms": ["Can timeout be controlled natively?", "timeout be controlled natively?", "be controlled natively?", "controlled natively?", "natively?"], "tokens": ["Can", "timeout", "be", "controlled", "natively", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::822087185", "fields": {"term": "Which query operators support parameter substitution?", "namespace": "open-p", "hash": 822087185, "terms": ["Which query operators support parameter substitution?", "query operators support parameter substitution?", "operators support parameter substitution?", "support parameter substitution?", "parameter substitution?", "substitution?"], "tokens": ["Which", "query", "operators", "support", "parameter", "substitution", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1927290432", "fields": {"term": "What is the purpose of parameter substitution in query generation?", "namespace": "open-p", "hash": -1927290432, "terms": ["What is the purpose of parameter substitution in query generation?", "is the purpose of parameter substitution in query generation?", "the purpose of parameter substitution in query generation?", "purpose of parameter substitution in query generation?", "of parameter substitution in query generation?", "parameter substitution in query generation?", "substitution in query generation?", "in query generation?", "query generation?", "generation?"], "tokens": ["What", "is", "the", "purpose", "of", "parameter", "substitution", "in", "query", "generation", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1276067280", "fields": {"term": "What is the parameter substitution syntax for the weightedSet operator?", "namespace": "open-p", "hash": 1276067280, "terms": ["What is the parameter substitution syntax for the weightedSet operator?", "is the parameter substitution syntax for the weightedSet operator?", "the parameter substitution syntax for the weightedSet operator?", "parameter substitution syntax for the weightedSet operator?", "substitution syntax for the weightedSet operator?", "syntax for the weightedSet operator?", "for the weightedSet operator?", "the weightedSet operator?", "weightedSet operator?", "operator?"], "tokens": ["What", "is", "the", "parameter", "substitution", "syntax", "for", "the", "weightedSet", "operator", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-430903355", "fields": {"term": "What are the advantages of using parameter substitution in query parsing?", "namespace": "open-p", "hash": -430903355, "terms": ["What are the advantages of using parameter substitution in query parsing?", "are the advantages of using parameter substitution in query parsing?", "the advantages of using parameter substitution in query parsing?", "advantages of using parameter substitution in query parsing?", "of using parameter substitution in query parsing?", "using parameter substitution in query parsing?", "parameter substitution in query parsing?", "substitution in query parsing?", "in query parsing?", "query parsing?", "parsing?"], "tokens": ["What", "are", "the", "advantages", "of", "using", "parameter", "substitution", "in", "query", "parsing", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::551131232", "fields": {"term": "In what formats can the value string be passed when using the weightedSet operator?", "namespace": "open-p", "hash": 551131232, "terms": ["In what formats can the value string be passed when using the weightedSet operator?", "what formats can the value string be passed when using the weightedSet operator?", "formats can the value string be passed when using the weightedSet operator?", "can the value string be passed when using the weightedSet operator?", "the value string be passed when using the weightedSet operator?", "value string be passed when using the weightedSet operator?", "string be passed when using the weightedSet operator?", "be passed when using the weightedSet operator?", "passed when using the weightedSet operator?", "when using the weightedSet operator?", "using the weightedSet operator?", "the weightedSet operator?", "weightedSet operator?", "operator?"], "tokens": ["In", "what", "formats", "can", "the", "value", "string", "be", "passed", "when", "using", "the", "weightedSet", "operator", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::237590212", "fields": {"term": "Which query operator supports parameter substitution for user input?", "namespace": "open-p", "hash": 237590212, "terms": ["Which query operator supports parameter substitution for user input?", "query operator supports parameter substitution for user input?", "operator supports parameter substitution for user input?", "supports parameter substitution for user input?", "parameter substitution for user input?", "substitution for user input?", "for user input?", "user input?", "input?"], "tokens": ["Which", "query", "operator", "supports", "parameter", "substitution", "for", "user", "input", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1396034405", "fields": {"term": "What is the syntax for passing a user input parameter with parameter substitution?", "namespace": "open-p", "hash": -1396034405, "terms": ["What is the syntax for passing a user input parameter with parameter substitution?", "is the syntax for passing a user input parameter with parameter substitution?", "the syntax for passing a user input parameter with parameter substitution?", "syntax for passing a user input parameter with parameter substitution?", "for passing a user input parameter with parameter substitution?", "passing a user input parameter with parameter substitution?", "a user input parameter with parameter substitution?", "user input parameter with parameter substitution?", "input parameter with parameter substitution?", "parameter with parameter substitution?", "with parameter substitution?", "parameter substitution?", "substitution?"], "tokens": ["What", "is", "the", "syntax", "for", "passing", "a", "user", "input", "parameter", "with", "parameter", "substitution", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2131427298", "fields": {"term": "What is the benefit of using parameter substitution for user input?", "namespace": "open-p", "hash": -2131427298, "terms": ["What is the benefit of using parameter substitution for user input?", "is the benefit of using parameter substitution for user input?", "the benefit of using parameter substitution for user input?", "benefit of using parameter substitution for user input?", "of using parameter substitution for user input?", "using parameter substitution for user input?", "parameter substitution for user input?", "substitution for user input?", "for user input?", "user input?", "input?"], "tokens": ["What", "is", "the", "benefit", "of", "using", "parameter", "substitution", "for", "user", "input", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1055189036", "fields": {"term": "What is the distance annotation used for?", "namespace": "open-p", "hash": -1055189036, "terms": ["What is the distance annotation used for?", "is the distance annotation used for?", "the distance annotation used for?", "distance annotation used for?", "annotation used for?", "used for?", "for?"], "tokens": ["What", "is", "the", "distance", "annotation", "used", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::191225089", "fields": {"term": "What is the exploreAdditionalHits annotation used for?", "namespace": "open-p", "hash": 191225089, "terms": ["What is the exploreAdditionalHits annotation used for?", "is the exploreAdditionalHits annotation used for?", "the exploreAdditionalHits annotation used for?", "exploreAdditionalHits annotation used for?", "annotation used for?", "used for?", "for?"], "tokens": ["What", "is", "the", "exploreAdditionalHits", "annotation", "used", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1813924120", "fields": {"term": "What is the hitLimit annotation used for?", "namespace": "open-p", "hash": -1813924120, "terms": ["What is the hitLimit annotation used for?", "is the hitLimit annotation used for?", "the hitLimit annotation used for?", "hitLimit annotation used for?", "annotation used for?", "used for?", "for?"], "tokens": ["What", "is", "the", "hitLimit", "annotation", "used", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-210368877", "fields": {"term": "What is the purpose of the hnsw.exploreAdditionalHits annotation?", "namespace": "open-p", "hash": -210368877, "terms": ["What is the purpose of the hnsw.exploreAdditionalHits annotation?", "is the purpose of the hnsw.exploreAdditionalHits annotation?", "the purpose of the hnsw.exploreAdditionalHits annotation?", "purpose of the hnsw.exploreAdditionalHits annotation?", "of the hnsw.exploreAdditionalHits annotation?", "the hnsw.exploreAdditionalHits annotation?", "hnsw.exploreAdditionalHits annotation?", "annotation?"], "tokens": ["What", "is", "the", "purpose", "of", "the", "hnsw", "exploreAdditionalHits", "annotation", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2004592511", "fields": {"term": "What is the function annotation used for?", "namespace": "open-p", "hash": 2004592511, "terms": ["What is the function annotation used for?", "is the function annotation used for?", "the function annotation used for?", "function annotation used for?", "annotation used for?", "used for?", "for?"], "tokens": ["What", "is", "the", "function", "annotation", "used", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1687172104", "fields": {"term": "What is the scoreThreshold annotation used for?", "namespace": "open-p", "hash": 1687172104, "terms": ["What is the scoreThreshold annotation used for?", "is the scoreThreshold annotation used for?", "the scoreThreshold annotation used for?", "scoreThreshold annotation used for?", "annotation used for?", "used for?", "for?"], "tokens": ["What", "is", "the", "scoreThreshold", "annotation", "used", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1696111091", "fields": {"term": "What is the stem annotation?", "namespace": "open-p", "hash": -1696111091, "terms": ["What is the stem annotation?", "is the stem annotation?", "the stem annotation?", "stem annotation?", "annotation?"], "tokens": ["What", "is", "the", "stem", "annotation", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2083796110", "fields": {"term": "What is the scope of annotations?", "namespace": "open-p", "hash": -2083796110, "terms": ["What is the scope of annotations?", "is the scope of annotations?", "the scope of annotations?", "scope of annotations?", "of annotations?", "annotations?"], "tokens": ["What", "is", "the", "scope", "of", "annotations", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1765877082", "fields": {"term": "What happens when a term in a boolean query is annotated with 'stem: false'?", "namespace": "open-p", "hash": 1765877082, "terms": ["What happens when a term in a boolean query is annotated with 'stem: false'?", "happens when a term in a boolean query is annotated with 'stem: false'?", "when a term in a boolean query is annotated with 'stem: false'?", "a term in a boolean query is annotated with 'stem: false'?", "term in a boolean query is annotated with 'stem: false'?", "in a boolean query is annotated with 'stem: false'?", "a boolean query is annotated with 'stem: false'?", "boolean query is annotated with 'stem: false'?", "query is annotated with 'stem: false'?", "is annotated with 'stem: false'?", "annotated with 'stem: false'?", "with 'stem: false'?", "'stem: false'?", "false'?"], "tokens": ["What", "happens", "when", "a", "term", "in", "a", "boolean", "query", "is", "annotated", "with", "'", "stem", ":", "false", "'?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1478904067", "fields": {"term": "What is YQL variable syntax?", "namespace": "open-p", "hash": -1478904067, "terms": ["What is YQL variable syntax?", "is YQL variable syntax?", "YQL variable syntax?", "variable syntax?", "syntax?"], "tokens": ["What", "is", "YQL", "variable", "syntax", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1134245237", "fields": {"term": "How can I use YQL?", "namespace": "open-p", "hash": 1134245237, "terms": ["How can I use YQL?", "can I use YQL?", "I use YQL?", "use YQL?", "YQL?"], "tokens": ["How", "can", "I", "use", "YQL", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1443222292", "fields": {"term": "What is @animal?", "namespace": "open-p", "hash": 1443222292, "terms": ["What is @animal?", "is @animal?", "@animal?"], "tokens": ["What", "is", "@", "animal", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1342368087", "fields": {"term": "What is YQL used for?", "namespace": "open-p", "hash": -1342368087, "terms": ["What is YQL used for?", "is YQL used for?", "YQL used for?", "used for?", "for?"], "tokens": ["What", "is", "YQL", "used", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-913469779", "fields": {"term": "How do you add a default query profile?", "namespace": "open-p", "hash": -913469779, "terms": ["How do you add a default query profile?", "do you add a default query profile?", "you add a default query profile?", "add a default query profile?", "a default query profile?", "default query profile?", "query profile?", "profile?"], "tokens": ["How", "do", "you", "add", "a", "default", "query", "profile", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2083988467", "fields": {"term": "What is the recommended annotation for modifying textual YQL statement?", "namespace": "open-p", "hash": 2083988467, "terms": ["What is the recommended annotation for modifying textual YQL statement?", "is the recommended annotation for modifying textual YQL statement?", "the recommended annotation for modifying textual YQL statement?", "recommended annotation for modifying textual YQL statement?", "annotation for modifying textual YQL statement?", "for modifying textual YQL statement?", "modifying textual YQL statement?", "textual YQL statement?", "YQL statement?", "statement?"], "tokens": ["What", "is", "the", "recommended", "annotation", "for", "modifying", "textual", "YQL", "statement", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::207670090", "fields": {"term": "What is the recommended annotation for modifying query tree?", "namespace": "open-p", "hash": 207670090, "terms": ["What is the recommended annotation for modifying query tree?", "is the recommended annotation for modifying query tree?", "the recommended annotation for modifying query tree?", "recommended annotation for modifying query tree?", "annotation for modifying query tree?", "for modifying query tree?", "modifying query tree?", "query tree?", "tree?"], "tokens": ["What", "is", "the", "recommended", "annotation", "for", "modifying", "query", "tree", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1235413292", "fields": {"term": "What is the name of the annotation used for modifying textual YQL statement?", "namespace": "open-p", "hash": 1235413292, "terms": ["What is the name of the annotation used for modifying textual YQL statement?", "is the name of the annotation used for modifying textual YQL statement?", "the name of the annotation used for modifying textual YQL statement?", "name of the annotation used for modifying textual YQL statement?", "of the annotation used for modifying textual YQL statement?", "the annotation used for modifying textual YQL statement?", "annotation used for modifying textual YQL statement?", "used for modifying textual YQL statement?", "for modifying textual YQL statement?", "modifying textual YQL statement?", "textual YQL statement?", "YQL statement?", "statement?"], "tokens": ["What", "is", "the", "name", "of", "the", "annotation", "used", "for", "modifying", "textual", "YQL", "statement", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1532059412", "fields": {"term": "How can searchers modify the YQL statement?", "namespace": "open-p", "hash": -1532059412, "terms": ["How can searchers modify the YQL statement?", "can searchers modify the YQL statement?", "searchers modify the YQL statement?", "modify the YQL statement?", "the YQL statement?", "YQL statement?", "statement?"], "tokens": ["How", "can", "searchers", "modify", "the", "YQL", "statement", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1053629386", "fields": {"term": "What should be annotated with @Before(ExternalYql)?", "namespace": "open-p", "hash": -1053629386, "terms": ["What should be annotated with @Before(ExternalYql)?", "should be annotated with @Before(ExternalYql)?", "be annotated with @Before(ExternalYql)?", "annotated with @Before(ExternalYql)?", "with @Before(ExternalYql)?", "@Before(ExternalYql)?"], "tokens": ["What", "should", "be", "annotated", "with", "@", "Before", "(", "ExternalYql", ")?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-791579059", "fields": {"term": "What should be annotated with @After(ExternalYql)?", "namespace": "open-p", "hash": -791579059, "terms": ["What should be annotated with @After(ExternalYql)?", "should be annotated with @After(ExternalYql)?", "be annotated with @After(ExternalYql)?", "annotated with @After(ExternalYql)?", "with @After(ExternalYql)?", "@After(ExternalYql)?"], "tokens": ["What", "should", "be", "annotated", "with", "@", "After", "(", "ExternalYql", ")?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1661365117", "fields": {"term": "What is a grouping expression?", "namespace": "open-p", "hash": -1661365117, "terms": ["What is a grouping expression?", "is a grouping expression?", "a grouping expression?", "grouping expression?", "expression?"], "tokens": ["What", "is", "a", "grouping", "expression", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1593481700", "fields": {"term": "How to run a query?", "namespace": "open-p", "hash": 1593481700, "terms": ["How to run a query?", "to run a query?", "run a query?", "a query?", "query?"], "tokens": ["How", "to", "run", "a", "query", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-395481028", "fields": {"term": "What is encoded YQL string?", "namespace": "open-p", "hash": -395481028, "terms": ["What is encoded YQL string?", "is encoded YQL string?", "encoded YQL string?", "YQL string?", "string?"], "tokens": ["What", "is", "encoded", "YQL", "string", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::74770111", "fields": {"term": "What is a query profile?", "namespace": "open-p", "hash": 74770111, "terms": ["What is a query profile?", "is a query profile?", "a query profile?", "query profile?", "profile?"], "tokens": ["What", "is", "a", "query", "profile", "?"], "corpus_count": 3, "document_count": 3}}, -{"put": "id:term:term::-295065080", "fields": {"term": "What is Query Profile Type?", "namespace": "open-p", "hash": -295065080, "terms": ["What is Query Profile Type?", "is Query Profile Type?", "Query Profile Type?", "Profile Type?", "Type?"], "tokens": ["What", "is", "Query", "Profile", "Type", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1739038459", "fields": {"term": "What does a query profile define?", "namespace": "open-p", "hash": 1739038459, "terms": ["What does a query profile define?", "does a query profile define?", "a query profile define?", "query profile define?", "profile define?", "define?"], "tokens": ["What", "does", "a", "query", "profile", "define", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1540901751", "fields": {"term": "What is the format for id?", "namespace": "open-p", "hash": -1540901751, "terms": ["What is the format for id?", "is the format for id?", "the format for id?", "format for id?", "for id?", "id?"], "tokens": ["What", "is", "the", "format", "for", "id", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-249148046", "fields": {"term": "What is the default profile?", "namespace": "open-p", "hash": -249148046, "terms": ["What is the default profile?", "is the default profile?", "the default profile?", "default profile?", "profile?"], "tokens": ["What", "is", "the", "default", "profile", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1497811744", "fields": {"term": "How are fields included from inherited profiles?", "namespace": "open-p", "hash": 1497811744, "terms": ["How are fields included from inherited profiles?", "are fields included from inherited profiles?", "fields included from inherited profiles?", "included from inherited profiles?", "from inherited profiles?", "inherited profiles?", "profiles?"], "tokens": ["How", "are", "fields", "included", "from", "inherited", "profiles", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-375059992", "fields": {"term": "What happens if a field is present in multiple inherited profiles?", "namespace": "open-p", "hash": -375059992, "terms": ["What happens if a field is present in multiple inherited profiles?", "happens if a field is present in multiple inherited profiles?", "if a field is present in multiple inherited profiles?", "a field is present in multiple inherited profiles?", "field is present in multiple inherited profiles?", "is present in multiple inherited profiles?", "present in multiple inherited profiles?", "in multiple inherited profiles?", "multiple inherited profiles?", "inherited profiles?", "profiles?"], "tokens": ["What", "happens", "if", "a", "field", "is", "present", "in", "multiple", "inherited", "profiles", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::632282941", "fields": {"term": "What is the purpose?", "namespace": "open-p", "hash": 632282941, "terms": ["What is the purpose?", "is the purpose?", "the purpose?", "purpose?"], "tokens": ["What", "is", "the", "purpose", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1135216175", "fields": {"term": "What is a field in a query profile?", "namespace": "open-p", "hash": 1135216175, "terms": ["What is a field in a query profile?", "is a field in a query profile?", "a field in a query profile?", "field in a query profile?", "in a query profile?", "a query profile?", "query profile?", "profile?"], "tokens": ["What", "is", "a", "field", "in", "a", "query", "profile", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::427205877", "fields": {"term": "What is a key-value pair?", "namespace": "open-p", "hash": 427205877, "terms": ["What is a key-value pair?", "is a key-value pair?", "a key-value pair?", "key-value pair?", "pair?"], "tokens": ["What", "is", "a", "key", "value", "pair", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1868364116", "fields": {"term": "What is overridable in Vespa?", "namespace": "open-p", "hash": -1868364116, "terms": ["What is overridable in Vespa?", "is overridable in Vespa?", "overridable in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["What", "is", "overridable", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1561081695", "fields": {"term": "Can you override fields in Vespa?", "namespace": "open-p", "hash": -1561081695, "terms": ["Can you override fields in Vespa?", "you override fields in Vespa?", "override fields in Vespa?", "fields in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["Can", "you", "override", "fields", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2096508023", "fields": {"term": "What are valid field value types?", "namespace": "open-p", "hash": -2096508023, "terms": ["What are valid field value types?", "are valid field value types?", "valid field value types?", "field value types?", "value types?", "types?"], "tokens": ["What", "are", "valid", "field", "value", "types", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::468297932", "fields": {"term": "What is a reference in query profile?", "namespace": "open-p", "hash": 468297932, "terms": ["What is a reference in query profile?", "is a reference in query profile?", "a reference in query profile?", "reference in query profile?", "in query profile?", "query profile?", "profile?"], "tokens": ["What", "is", "a", "reference", "in", "query", "profile", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1198573927", "fields": {"term": "What are nested query profiles?", "namespace": "open-p", "hash": -1198573927, "terms": ["What are nested query profiles?", "are nested query profiles?", "nested query profiles?", "query profiles?", "profiles?"], "tokens": ["What", "are", "nested", "query", "profiles", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1004848140", "fields": {"term": "What is a profile variant?", "namespace": "open-p", "hash": -1004848140, "terms": ["What is a profile variant?", "is a profile variant?", "a profile variant?", "profile variant?", "variant?"], "tokens": ["What", "is", "a", "profile", "variant", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1059628577", "fields": {"term": "How are profile variants triggered?", "namespace": "open-p", "hash": 1059628577, "terms": ["How are profile variants triggered?", "are profile variants triggered?", "profile variants triggered?", "variants triggered?", "triggered?"], "tokens": ["How", "are", "profile", "variants", "triggered", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1285065722", "fields": {"term": "What is a comma-separated list?", "namespace": "open-p", "hash": 1285065722, "terms": ["What is a comma-separated list?", "is a comma-separated list?", "a comma-separated list?", "comma-separated list?", "list?"], "tokens": ["What", "is", "a", "comma", "separated", "list", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1234342972", "fields": {"term": "What is the format for profile dimensions?", "namespace": "open-p", "hash": -1234342972, "terms": ["What is the format for profile dimensions?", "is the format for profile dimensions?", "the format for profile dimensions?", "format for profile dimensions?", "for profile dimensions?", "profile dimensions?", "dimensions?"], "tokens": ["What", "is", "the", "format", "for", "profile", "dimensions", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1471741441", "fields": {"term": "How can profile variants be created?", "namespace": "open-p", "hash": 1471741441, "terms": ["How can profile variants be created?", "can profile variants be created?", "profile variants be created?", "variants be created?", "be created?", "created?"], "tokens": ["How", "can", "profile", "variants", "be", "created", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::772014786", "fields": {"term": "What is a nested query profile?", "namespace": "open-p", "hash": 772014786, "terms": ["What is a nested query profile?", "is a nested query profile?", "a nested query profile?", "nested query profile?", "query profile?", "profile?"], "tokens": ["What", "is", "a", "nested", "query", "profile", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::323035795", "fields": {"term": "What can be set in nested query profiles?", "namespace": "open-p", "hash": 323035795, "terms": ["What can be set in nested query profiles?", "can be set in nested query profiles?", "be set in nested query profiles?", "set in nested query profiles?", "in nested query profiles?", "nested query profiles?", "query profiles?", "profiles?"], "tokens": ["What", "can", "be", "set", "in", "nested", "query", "profiles", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1219238264", "fields": {"term": "What is a variant in a nested query profile?", "namespace": "open-p", "hash": -1219238264, "terms": ["What is a variant in a nested query profile?", "is a variant in a nested query profile?", "a variant in a nested query profile?", "variant in a nested query profile?", "in a nested query profile?", "a nested query profile?", "nested query profile?", "query profile?", "profile?"], "tokens": ["What", "is", "a", "variant", "in", "a", "nested", "query", "profile", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1673975018", "fields": {"term": "What defines the most specific match in a nested query profile?", "namespace": "open-p", "hash": -1673975018, "terms": ["What defines the most specific match in a nested query profile?", "defines the most specific match in a nested query profile?", "the most specific match in a nested query profile?", "most specific match in a nested query profile?", "specific match in a nested query profile?", "match in a nested query profile?", "in a nested query profile?", "a nested query profile?", "nested query profile?", "query profile?", "profile?"], "tokens": ["What", "defines", "the", "most", "specific", "match", "in", "a", "nested", "query", "profile", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1133179458", "fields": {"term": "What does a nested query profile return?", "namespace": "open-p", "hash": 1133179458, "terms": ["What does a nested query profile return?", "does a nested query profile return?", "a nested query profile return?", "nested query profile return?", "query profile return?", "profile return?", "return?"], "tokens": ["What", "does", "a", "nested", "query", "profile", "return", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1879084160", "fields": {"term": "What is the for attribute in a nested query profile?", "namespace": "open-p", "hash": 1879084160, "terms": ["What is the for attribute in a nested query profile?", "is the for attribute in a nested query profile?", "the for attribute in a nested query profile?", "for attribute in a nested query profile?", "attribute in a nested query profile?", "in a nested query profile?", "a nested query profile?", "nested query profile?", "query profile?", "profile?"], "tokens": ["What", "is", "the", "for", "attribute", "in", "a", "nested", "query", "profile", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1849374667", "fields": {"term": "Are other attributes allowed in nested query profiles?", "namespace": "open-p", "hash": 1849374667, "terms": ["Are other attributes allowed in nested query profiles?", "other attributes allowed in nested query profiles?", "attributes allowed in nested query profiles?", "allowed in nested query profiles?", "in nested query profiles?", "nested query profiles?", "query profiles?", "profiles?"], "tokens": ["Are", "other", "attributes", "allowed", "in", "nested", "query", "profiles", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1317487048", "fields": {"term": "What is denoted by a \"*\"?", "namespace": "open-p", "hash": -1317487048, "terms": ["What is denoted by a \"*\"?", "is denoted by a \"*\"?", "denoted by a \"*\"?", "by a \"*\"?", "a \"*\"?", "\"*\"?"], "tokens": ["What", "is", "denoted", "by", "a", "\"*\"?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1944131699", "fields": {"term": "What is the structure of query profile type?", "namespace": "open-p", "hash": 1944131699, "terms": ["What is the structure of query profile type?", "is the structure of query profile type?", "the structure of query profile type?", "structure of query profile type?", "of query profile type?", "query profile type?", "profile type?", "type?"], "tokens": ["What", "is", "the", "structure", "of", "query", "profile", "type", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-525585861", "fields": {"term": "What are the optional attributes for query profile type?", "namespace": "open-p", "hash": -525585861, "terms": ["What are the optional attributes for query profile type?", "are the optional attributes for query profile type?", "the optional attributes for query profile type?", "optional attributes for query profile type?", "attributes for query profile type?", "for query profile type?", "query profile type?", "profile type?", "type?"], "tokens": ["What", "are", "the", "optional", "attributes", "for", "query", "profile", "type", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-628273885", "fields": {"term": "What is a query profile type field?", "namespace": "open-p", "hash": -628273885, "terms": ["What is a query profile type field?", "is a query profile type field?", "a query profile type field?", "query profile type field?", "profile type field?", "type field?", "field?"], "tokens": ["What", "is", "a", "query", "profile", "type", "field", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1518765881", "fields": {"term": "What does optional tag mean?", "namespace": "open-p", "hash": -1518765881, "terms": ["What does optional tag mean?", "does optional tag mean?", "optional tag mean?", "tag mean?", "mean?"], "tokens": ["What", "does", "optional", "tag", "mean", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1819775778", "fields": {"term": "How are fields included in a query profile?", "namespace": "open-p", "hash": 1819775778, "terms": ["How are fields included in a query profile?", "are fields included in a query profile?", "fields included in a query profile?", "included in a query profile?", "in a query profile?", "a query profile?", "query profile?", "profile?"], "tokens": ["How", "are", "fields", "included", "in", "a", "query", "profile", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::793271587", "fields": {"term": "What happens if a field is defined in multiple inherited profiles?", "namespace": "open-p", "hash": 793271587, "terms": ["What happens if a field is defined in multiple inherited profiles?", "happens if a field is defined in multiple inherited profiles?", "if a field is defined in multiple inherited profiles?", "a field is defined in multiple inherited profiles?", "field is defined in multiple inherited profiles?", "is defined in multiple inherited profiles?", "defined in multiple inherited profiles?", "in multiple inherited profiles?", "multiple inherited profiles?", "inherited profiles?", "profiles?"], "tokens": ["What", "happens", "if", "a", "field", "is", "defined", "in", "multiple", "inherited", "profiles", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-984600212", "fields": {"term": "What is ?", "namespace": "open-p", "hash": -984600212, "terms": ["What is ?", "is ?", "?", "path=true>?"], "tokens": ["What", "is", "<", "match", "path", "=", "true", ">?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-153855166", "fields": {"term": "How is query profile name matched?", "namespace": "open-p", "hash": -153855166, "terms": ["How is query profile name matched?", "is query profile name matched?", "query profile name matched?", "profile name matched?", "name matched?", "matched?"], "tokens": ["How", "is", "query", "profile", "name", "matched", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1925814874", "fields": {"term": "What happens if is not specified?", "namespace": "open-p", "hash": 1925814874, "terms": ["What happens if is not specified?", "happens if is not specified?", "if is not specified?", " is not specified?", "is not specified?", "not specified?", "specified?"], "tokens": ["What", "happens", "if", "<", "match", ">", "is", "not", "specified", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1418238496", "fields": {"term": "What happens when strict sub-profiles are added?", "namespace": "open-p", "hash": 1418238496, "terms": ["What happens when strict sub-profiles are added?", "happens when strict sub-profiles are added?", "when strict sub-profiles are added?", "strict sub-profiles are added?", "sub-profiles are added?", "are added?", "added?"], "tokens": ["What", "happens", "when", "strict", "sub", "profiles", "are", "added", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1202182979", "fields": {"term": "What are the rules of using a strict top-level profile?", "namespace": "open-p", "hash": 1202182979, "terms": ["What are the rules of using a strict top-level profile?", "are the rules of using a strict top-level profile?", "the rules of using a strict top-level profile?", "rules of using a strict top-level profile?", "of using a strict top-level profile?", "using a strict top-level profile?", "a strict top-level profile?", "strict top-level profile?", "top-level profile?", "profile?"], "tokens": ["What", "are", "the", "rules", "of", "using", "a", "strict", "top", "level", "profile", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1625170423", "fields": {"term": "Can non-primitive model objects be added to strict top-level profiles?", "namespace": "open-p", "hash": 1625170423, "terms": ["Can non-primitive model objects be added to strict top-level profiles?", "non-primitive model objects be added to strict top-level profiles?", "model objects be added to strict top-level profiles?", "objects be added to strict top-level profiles?", "be added to strict top-level profiles?", "added to strict top-level profiles?", "to strict top-level profiles?", "strict top-level profiles?", "top-level profiles?", "profiles?"], "tokens": ["Can", "non", "primitive", "model", "objects", "be", "added", "to", "strict", "top", "level", "profiles", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1040508439", "fields": {"term": "What is the 'long' type?", "namespace": "open-p", "hash": 1040508439, "terms": ["What is the 'long' type?", "is the 'long' type?", "the 'long' type?", "'long' type?", "type?"], "tokens": ["What", "is", "the", "'", "long", "'", "type", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1398870572", "fields": {"term": "What is the 'double' type?", "namespace": "open-p", "hash": 1398870572, "terms": ["What is the 'double' type?", "is the 'double' type?", "the 'double' type?", "'double' type?", "type?"], "tokens": ["What", "is", "the", "'", "double", "'", "type", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1744434267", "fields": {"term": "What is the 'boolean' type?", "namespace": "open-p", "hash": 1744434267, "terms": ["What is the 'boolean' type?", "is the 'boolean' type?", "the 'boolean' type?", "'boolean' type?", "type?"], "tokens": ["What", "is", "the", "'", "boolean", "'", "type", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2035635863", "fields": {"term": "What is the 'mandatory' field?", "namespace": "open-p", "hash": -2035635863, "terms": ["What is the 'mandatory' field?", "is the 'mandatory' field?", "the 'mandatory' field?", "'mandatory' field?", "field?"], "tokens": ["What", "is", "the", "'", "mandatory", "'", "field", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::613296337", "fields": {"term": "What is the 'overridable' field?", "namespace": "open-p", "hash": 613296337, "terms": ["What is the 'overridable' field?", "is the 'overridable' field?", "the 'overridable' field?", "'overridable' field?", "field?"], "tokens": ["What", "is", "the", "'", "overridable", "'", "field", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-18365063", "fields": {"term": "What is the 'description' field?", "namespace": "open-p", "hash": -18365063, "terms": ["What is the 'description' field?", "is the 'description' field?", "the 'description' field?", "'description' field?", "field?"], "tokens": ["What", "is", "the", "'", "description", "'", "field", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::457261385", "fields": {"term": "What is an identifier pattern?", "namespace": "open-p", "hash": 457261385, "terms": ["What is an identifier pattern?", "is an identifier pattern?", "an identifier pattern?", "identifier pattern?", "pattern?"], "tokens": ["What", "is", "an", "identifier", "pattern", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1985608774", "fields": {"term": "What characters can identifiers contain?", "namespace": "open-p", "hash": 1985608774, "terms": ["What characters can identifiers contain?", "characters can identifiers contain?", "can identifiers contain?", "identifiers contain?", "contain?"], "tokens": ["What", "characters", "can", "identifiers", "contain", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1080154695", "fields": {"term": "What is the syntax for an identifier?", "namespace": "open-p", "hash": -1080154695, "terms": ["What is the syntax for an identifier?", "is the syntax for an identifier?", "the syntax for an identifier?", "syntax for an identifier?", "for an identifier?", "an identifier?", "identifier?"], "tokens": ["What", "is", "the", "syntax", "for", "an", "identifier", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1659142788", "fields": {"term": "What is the format for an identifier?", "namespace": "open-p", "hash": -1659142788, "terms": ["What is the format for an identifier?", "is the format for an identifier?", "the format for an identifier?", "format for an identifier?", "for an identifier?", "an identifier?", "identifier?"], "tokens": ["What", "is", "the", "format", "for", "an", "identifier", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1686752336", "fields": {"term": "What is the pattern for an identifier?", "namespace": "open-p", "hash": -1686752336, "terms": ["What is the pattern for an identifier?", "is the pattern for an identifier?", "the pattern for an identifier?", "pattern for an identifier?", "for an identifier?", "an identifier?", "identifier?"], "tokens": ["What", "is", "the", "pattern", "for", "an", "identifier", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1722671197", "fields": {"term": "What is the definition of an identifier?", "namespace": "open-p", "hash": 1722671197, "terms": ["What is the definition of an identifier?", "is the definition of an identifier?", "the definition of an identifier?", "definition of an identifier?", "of an identifier?", "an identifier?", "identifier?"], "tokens": ["What", "is", "the", "definition", "of", "an", "identifier", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2130948021", "fields": {"term": "What is the format of an identifier?", "namespace": "open-p", "hash": -2130948021, "terms": ["What is the format of an identifier?", "is the format of an identifier?", "the format of an identifier?", "format of an identifier?", "of an identifier?", "an identifier?", "identifier?"], "tokens": ["What", "is", "the", "format", "of", "an", "identifier", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1494165822", "fields": {"term": "How can search request specify Query Profile?", "namespace": "open-p", "hash": -1494165822, "terms": ["How can search request specify Query Profile?", "can search request specify Query Profile?", "search request specify Query Profile?", "request specify Query Profile?", "specify Query Profile?", "Query Profile?", "Profile?"], "tokens": ["How", "can", "search", "request", "specify", "Query", "Profile", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-578579300", "fields": {"term": "What benefits Query Profiles offer?", "namespace": "open-p", "hash": -578579300, "terms": ["What benefits Query Profiles offer?", "benefits Query Profiles offer?", "Query Profiles offer?", "Profiles offer?", "offer?"], "tokens": ["What", "benefits", "Query", "Profiles", "offer", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1716422604", "fields": {"term": "What can Query Profiles differentiate based on?", "namespace": "open-p", "hash": -1716422604, "terms": ["What can Query Profiles differentiate based on?", "can Query Profiles differentiate based on?", "Query Profiles differentiate based on?", "Profiles differentiate based on?", "differentiate based on?", "based on?", "on?"], "tokens": ["What", "can", "Query", "Profiles", "differentiate", "based", "on", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1394562238", "fields": {"term": "How to create Query Profiles?", "namespace": "open-p", "hash": -1394562238, "terms": ["How to create Query Profiles?", "to create Query Profiles?", "create Query Profiles?", "Query Profiles?", "Profiles?"], "tokens": ["How", "to", "create", "Query", "Profiles", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2130827651", "fields": {"term": "How to use Query Profiles?", "namespace": "open-p", "hash": 2130827651, "terms": ["How to use Query Profiles?", "to use Query Profiles?", "use Query Profiles?", "Query Profiles?", "Profiles?"], "tokens": ["How", "to", "use", "Query", "Profiles", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1251220829", "fields": {"term": "Where can I find the full Query Profile syntax reference?", "namespace": "open-p", "hash": -1251220829, "terms": ["Where can I find the full Query Profile syntax reference?", "can I find the full Query Profile syntax reference?", "I find the full Query Profile syntax reference?", "find the full Query Profile syntax reference?", "the full Query Profile syntax reference?", "full Query Profile syntax reference?", "Query Profile syntax reference?", "Profile syntax reference?", "syntax reference?", "reference?"], "tokens": ["Where", "can", "I", "find", "the", "full", "Query", "Profile", "syntax", "reference", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1277639700", "fields": {"term": "How to deploy a Query Profile?", "namespace": "open-p", "hash": 1277639700, "terms": ["How to deploy a Query Profile?", "to deploy a Query Profile?", "deploy a Query Profile?", "a Query Profile?", "Query Profile?", "Profile?"], "tokens": ["How", "to", "deploy", "a", "Query", "Profile", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-965508279", "fields": {"term": "What is the format of a Query Profile?", "namespace": "open-p", "hash": -965508279, "terms": ["What is the format of a Query Profile?", "is the format of a Query Profile?", "the format of a Query Profile?", "format of a Query Profile?", "of a Query Profile?", "a Query Profile?", "Query Profile?", "Profile?"], "tokens": ["What", "is", "the", "format", "of", "a", "Query", "Profile", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-342110894", "fields": {"term": "What is the directory for Query Profiles?", "namespace": "open-p", "hash": -342110894, "terms": ["What is the directory for Query Profiles?", "is the directory for Query Profiles?", "the directory for Query Profiles?", "directory for Query Profiles?", "for Query Profiles?", "Query Profiles?", "Profiles?"], "tokens": ["What", "is", "the", "directory", "for", "Query", "Profiles", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::763962309", "fields": {"term": "What is the default Query Profile name?", "namespace": "open-p", "hash": 763962309, "terms": ["What is the default Query Profile name?", "is the default Query Profile name?", "the default Query Profile name?", "default Query Profile name?", "Query Profile name?", "Profile name?", "name?"], "tokens": ["What", "is", "the", "default", "Query", "Profile", "name", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1369458759", "fields": {"term": "What happens if a Query Profile contains errors?", "namespace": "open-p", "hash": 1369458759, "terms": ["What happens if a Query Profile contains errors?", "happens if a Query Profile contains errors?", "if a Query Profile contains errors?", "a Query Profile contains errors?", "Query Profile contains errors?", "Profile contains errors?", "contains errors?", "errors?"], "tokens": ["What", "happens", "if", "a", "Query", "Profile", "contains", "errors", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-567917005", "fields": {"term": "How to specify a Query Profile in a request?", "namespace": "open-p", "hash": -567917005, "terms": ["How to specify a Query Profile in a request?", "to specify a Query Profile in a request?", "specify a Query Profile in a request?", "a Query Profile in a request?", "Query Profile in a request?", "Profile in a request?", "in a request?", "a request?", "request?"], "tokens": ["How", "to", "specify", "a", "Query", "Profile", "in", "a", "request", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-41292916", "fields": {"term": "What are query properties?", "namespace": "open-p", "hash": -41292916, "terms": ["What are query properties?", "are query properties?", "query properties?", "properties?"], "tokens": ["What", "are", "query", "properties", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2142095909", "fields": {"term": "What is MyProfile.xml?", "namespace": "open-p", "hash": 2142095909, "terms": ["What is MyProfile.xml?", "is MyProfile.xml?", "MyProfile.xml?"], "tokens": ["What", "is", "MyProfile", "xml", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::573395856", "fields": {"term": "How does overridable work?", "namespace": "open-p", "hash": 573395856, "terms": ["How does overridable work?", "does overridable work?", "overridable work?", "work?"], "tokens": ["How", "does", "overridable", "work", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::724304543", "fields": {"term": "What is the precedence of parameter values?", "namespace": "open-p", "hash": 724304543, "terms": ["What is the precedence of parameter values?", "is the precedence of parameter values?", "the precedence of parameter values?", "precedence of parameter values?", "of parameter values?", "parameter values?", "values?"], "tokens": ["What", "is", "the", "precedence", "of", "parameter", "values", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1768944077", "fields": {"term": "How can individual query profile fields be overriden?", "namespace": "open-p", "hash": -1768944077, "terms": ["How can individual query profile fields be overriden?", "can individual query profile fields be overriden?", "individual query profile fields be overriden?", "query profile fields be overriden?", "profile fields be overriden?", "fields be overriden?", "be overriden?", "overriden?"], "tokens": ["How", "can", "individual", "query", "profile", "fields", "be", "overriden", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1006806778", "fields": {"term": "What is the default behavior when same parameter is present in request and profile?", "namespace": "open-p", "hash": 1006806778, "terms": ["What is the default behavior when same parameter is present in request and profile?", "is the default behavior when same parameter is present in request and profile?", "the default behavior when same parameter is present in request and profile?", "default behavior when same parameter is present in request and profile?", "behavior when same parameter is present in request and profile?", "when same parameter is present in request and profile?", "same parameter is present in request and profile?", "parameter is present in request and profile?", "is present in request and profile?", "present in request and profile?", "in request and profile?", "request and profile?", "and profile?", "profile?"], "tokens": ["What", "is", "the", "default", "behavior", "when", "same", "parameter", "is", "present", "in", "request", "and", "profile", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1951452973", "fields": {"term": "What is an example of setting an overridable field?", "namespace": "open-p", "hash": -1951452973, "terms": ["What is an example of setting an overridable field?", "is an example of setting an overridable field?", "an example of setting an overridable field?", "example of setting an overridable field?", "of setting an overridable field?", "setting an overridable field?", "an overridable field?", "overridable field?", "field?"], "tokens": ["What", "is", "an", "example", "of", "setting", "an", "overridable", "field", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::309365928", "fields": {"term": "How are nested profiles referenced?", "namespace": "open-p", "hash": 309365928, "terms": ["How are nested profiles referenced?", "are nested profiles referenced?", "nested profiles referenced?", "profiles referenced?", "referenced?"], "tokens": ["How", "are", "nested", "profiles", "referenced", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1979096060", "fields": {"term": "How can variables be overridden?", "namespace": "open-p", "hash": -1979096060, "terms": ["How can variables be overridden?", "can variables be overridden?", "variables be overridden?", "be overridden?", "overridden?"], "tokens": ["How", "can", "variables", "be", "overridden", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1107901316", "fields": {"term": "What is the structure of a nested profile?", "namespace": "open-p", "hash": -1107901316, "terms": ["What is the structure of a nested profile?", "is the structure of a nested profile?", "the structure of a nested profile?", "structure of a nested profile?", "of a nested profile?", "a nested profile?", "nested profile?", "profile?"], "tokens": ["What", "is", "the", "structure", "of", "a", "nested", "profile", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1149294527", "fields": {"term": "What is the syntax for referencing a subprofile in the request?", "namespace": "open-p", "hash": -1149294527, "terms": ["What is the syntax for referencing a subprofile in the request?", "is the syntax for referencing a subprofile in the request?", "the syntax for referencing a subprofile in the request?", "syntax for referencing a subprofile in the request?", "for referencing a subprofile in the request?", "referencing a subprofile in the request?", "a subprofile in the request?", "subprofile in the request?", "in the request?", "the request?", "request?"], "tokens": ["What", "is", "the", "syntax", "for", "referencing", "a", "subprofile", "in", "the", "request", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-984505202", "fields": {"term": "How does a query profile reference affect search requests?", "namespace": "open-p", "hash": -984505202, "terms": ["How does a query profile reference affect search requests?", "does a query profile reference affect search requests?", "a query profile reference affect search requests?", "query profile reference affect search requests?", "profile reference affect search requests?", "reference affect search requests?", "affect search requests?", "search requests?", "requests?"], "tokens": ["How", "does", "a", "query", "profile", "reference", "affect", "search", "requests", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1760537983", "fields": {"term": "How do you inherit a query profile?", "namespace": "open-p", "hash": 1760537983, "terms": ["How do you inherit a query profile?", "do you inherit a query profile?", "you inherit a query profile?", "inherit a query profile?", "a query profile?", "query profile?", "profile?"], "tokens": ["How", "do", "you", "inherit", "a", "query", "profile", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-923416565", "fields": {"term": "What is the syntax for inheriting multiple query profiles?", "namespace": "open-p", "hash": -923416565, "terms": ["What is the syntax for inheriting multiple query profiles?", "is the syntax for inheriting multiple query profiles?", "the syntax for inheriting multiple query profiles?", "syntax for inheriting multiple query profiles?", "for inheriting multiple query profiles?", "inheriting multiple query profiles?", "multiple query profiles?", "query profiles?", "profiles?"], "tokens": ["What", "is", "the", "syntax", "for", "inheriting", "multiple", "query", "profiles", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::538744202", "fields": {"term": "What happens when a parameter is set in multiple inherited profiles?", "namespace": "open-p", "hash": 538744202, "terms": ["What happens when a parameter is set in multiple inherited profiles?", "happens when a parameter is set in multiple inherited profiles?", "when a parameter is set in multiple inherited profiles?", "a parameter is set in multiple inherited profiles?", "parameter is set in multiple inherited profiles?", "is set in multiple inherited profiles?", "set in multiple inherited profiles?", "in multiple inherited profiles?", "multiple inherited profiles?", "inherited profiles?", "profiles?"], "tokens": ["What", "happens", "when", "a", "parameter", "is", "set", "in", "multiple", "inherited", "profiles", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-727854560", "fields": {"term": "What are query profile values?", "namespace": "open-p", "hash": -727854560, "terms": ["What are query profile values?", "are query profile values?", "query profile values?", "profile values?", "values?"], "tokens": ["What", "are", "query", "profile", "values", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-224748022", "fields": {"term": "Can query profiles contain multiple fields?", "namespace": "open-p", "hash": -224748022, "terms": ["Can query profiles contain multiple fields?", "query profiles contain multiple fields?", "profiles contain multiple fields?", "contain multiple fields?", "multiple fields?", "fields?"], "tokens": ["Can", "query", "profiles", "contain", "multiple", "fields", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2018201856", "fields": {"term": "What will the query profile return?", "namespace": "open-p", "hash": 2018201856, "terms": ["What will the query profile return?", "will the query profile return?", "the query profile return?", "query profile return?", "profile return?", "return?"], "tokens": ["What", "will", "the", "query", "profile", "return", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1305101183", "fields": {"term": "What is query.properties()?", "namespace": "open-p", "hash": -1305101183, "terms": ["What is query.properties()?", "is query.properties()?", "query.properties()?"], "tokens": ["What", "is", "query", "properties", "()?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-339650105", "fields": {"term": "What is a substitution in Vespa?", "namespace": "open-p", "hash": -339650105, "terms": ["What is a substitution in Vespa?", "is a substitution in Vespa?", "a substitution in Vespa?", "substitution in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["What", "is", "a", "substitution", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-157673941", "fields": {"term": "How are substituted values looked up?", "namespace": "open-p", "hash": -157673941, "terms": ["How are substituted values looked up?", "are substituted values looked up?", "substituted values looked up?", "values looked up?", "looked up?", "up?"], "tokens": ["How", "are", "substituted", "values", "looked", "up", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-811051311", "fields": {"term": "What happens when a substitution returns null?", "namespace": "open-p", "hash": -811051311, "terms": ["What happens when a substitution returns null?", "happens when a substitution returns null?", "when a substitution returns null?", "a substitution returns null?", "substitution returns null?", "returns null?", "null?"], "tokens": ["What", "happens", "when", "a", "substitution", "returns", "null", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::505558756", "fields": {"term": "What is local substitution used for?", "namespace": "open-p", "hash": 505558756, "terms": ["What is local substitution used for?", "is local substitution used for?", "local substitution used for?", "substitution used for?", "used for?", "for?"], "tokens": ["What", "is", "local", "substitution", "used", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-255021987", "fields": {"term": "How to substitute a value?", "namespace": "open-p", "hash": -255021987, "terms": ["How to substitute a value?", "to substitute a value?", "substitute a value?", "a value?", "value?"], "tokens": ["How", "to", "substitute", "a", "value", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-485687806", "fields": {"term": "What are query profiles?", "namespace": "open-p", "hash": -485687806, "terms": ["What are query profiles?", "are query profiles?", "query profiles?", "profiles?"], "tokens": ["What", "are", "query", "profiles", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-967035981", "fields": {"term": "What are variants in query profiles?", "namespace": "open-p", "hash": -967035981, "terms": ["What are variants in query profiles?", "are variants in query profiles?", "variants in query profiles?", "in query profiles?", "query profiles?", "profiles?"], "tokens": ["What", "are", "variants", "in", "query", "profiles", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-206868685", "fields": {"term": "How does a query profile vary?", "namespace": "open-p", "hash": -206868685, "terms": ["How does a query profile vary?", "does a query profile vary?", "a query profile vary?", "query profile vary?", "profile vary?", "vary?"], "tokens": ["How", "does", "a", "query", "profile", "vary", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::840248334", "fields": {"term": "What is the purpose of a query profile?", "namespace": "open-p", "hash": 840248334, "terms": ["What is the purpose of a query profile?", "is the purpose of a query profile?", "the purpose of a query profile?", "purpose of a query profile?", "of a query profile?", "a query profile?", "query profile?", "profile?"], "tokens": ["What", "is", "the", "purpose", "of", "a", "query", "profile", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-644018682", "fields": {"term": "What does the 'for' attribute do in a query profile?", "namespace": "open-p", "hash": -644018682, "terms": ["What does the 'for' attribute do in a query profile?", "does the 'for' attribute do in a query profile?", "the 'for' attribute do in a query profile?", "'for' attribute do in a query profile?", "attribute do in a query profile?", "do in a query profile?", "in a query profile?", "a query profile?", "query profile?", "profile?"], "tokens": ["What", "does", "the", "'", "for", "'", "attribute", "do", "in", "a", "query", "profile", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-977332803", "fields": {"term": "How are virtual children created in a query profile?", "namespace": "open-p", "hash": -977332803, "terms": ["How are virtual children created in a query profile?", "are virtual children created in a query profile?", "virtual children created in a query profile?", "children created in a query profile?", "created in a query profile?", "in a query profile?", "a query profile?", "query profile?", "profile?"], "tokens": ["How", "are", "virtual", "children", "created", "in", "a", "query", "profile", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2139398197", "fields": {"term": "Can a query profile filter values according to incoming requests?", "namespace": "open-p", "hash": 2139398197, "terms": ["Can a query profile filter values according to incoming requests?", "a query profile filter values according to incoming requests?", "query profile filter values according to incoming requests?", "profile filter values according to incoming requests?", "filter values according to incoming requests?", "values according to incoming requests?", "according to incoming requests?", "to incoming requests?", "incoming requests?", "requests?"], "tokens": ["Can", "a", "query", "profile", "filter", "values", "according", "to", "incoming", "requests", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1078932977", "fields": {"term": "What is variant resolution?", "namespace": "open-p", "hash": 1078932977, "terms": ["What is variant resolution?", "is variant resolution?", "variant resolution?", "resolution?"], "tokens": ["What", "is", "variant", "resolution", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::649468304", "fields": {"term": "How are variant dimensions inherited?", "namespace": "open-p", "hash": 649468304, "terms": ["How are variant dimensions inherited?", "are variant dimensions inherited?", "variant dimensions inherited?", "dimensions inherited?", "inherited?"], "tokens": ["How", "are", "variant", "dimensions", "inherited", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1736954922", "fields": {"term": "What is variant specificity order?", "namespace": "open-p", "hash": 1736954922, "terms": ["What is variant specificity order?", "is variant specificity order?", "variant specificity order?", "specificity order?", "order?"], "tokens": ["What", "is", "variant", "specificity", "order", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::974475787", "fields": {"term": "Can variants specify inherited profiles?", "namespace": "open-p", "hash": 974475787, "terms": ["Can variants specify inherited profiles?", "variants specify inherited profiles?", "specify inherited profiles?", "inherited profiles?", "profiles?"], "tokens": ["Can", "variants", "specify", "inherited", "profiles", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::428202011", "fields": {"term": "What is the resolution precedence for variants?", "namespace": "open-p", "hash": 428202011, "terms": ["What is the resolution precedence for variants?", "is the resolution precedence for variants?", "the resolution precedence for variants?", "resolution precedence for variants?", "precedence for variants?", "for variants?", "variants?"], "tokens": ["What", "is", "the", "resolution", "precedence", "for", "variants", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::851774730", "fields": {"term": "How are values resolved in variants?", "namespace": "open-p", "hash": 851774730, "terms": ["How are values resolved in variants?", "are values resolved in variants?", "values resolved in variants?", "resolved in variants?", "in variants?", "variants?"], "tokens": ["How", "are", "values", "resolved", "in", "variants", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1172606109", "fields": {"term": "What are variant dimensions?", "namespace": "open-p", "hash": 1172606109, "terms": ["What are variant dimensions?", "are variant dimensions?", "variant dimensions?", "dimensions?"], "tokens": ["What", "are", "variant", "dimensions", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2115731469", "fields": {"term": "What is the syntax for variant profile inheritance?", "namespace": "open-p", "hash": 2115731469, "terms": ["What is the syntax for variant profile inheritance?", "is the syntax for variant profile inheritance?", "the syntax for variant profile inheritance?", "syntax for variant profile inheritance?", "for variant profile inheritance?", "variant profile inheritance?", "profile inheritance?", "inheritance?"], "tokens": ["What", "is", "the", "syntax", "for", "variant", "profile", "inheritance", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1073000783", "fields": {"term": "What is type checking in query profiles?", "namespace": "open-p", "hash": 1073000783, "terms": ["What is type checking in query profiles?", "is type checking in query profiles?", "type checking in query profiles?", "checking in query profiles?", "in query profiles?", "query profiles?", "profiles?"], "tokens": ["What", "is", "type", "checking", "in", "query", "profiles", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-126334086", "fields": {"term": "How do you reference a query profile type?", "namespace": "open-p", "hash": -126334086, "terms": ["How do you reference a query profile type?", "do you reference a query profile type?", "you reference a query profile type?", "reference a query profile type?", "a query profile type?", "query profile type?", "profile type?", "type?"], "tokens": ["How", "do", "you", "reference", "a", "query", "profile", "type", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1879781925", "fields": {"term": "How can query profile types be useful?", "namespace": "open-p", "hash": 1879781925, "terms": ["How can query profile types be useful?", "can query profile types be useful?", "query profile types be useful?", "profile types be useful?", "types be useful?", "be useful?", "useful?"], "tokens": ["How", "can", "query", "profile", "types", "be", "useful", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1734100605", "fields": {"term": "What is the syntax for defining query profile types?", "namespace": "open-p", "hash": -1734100605, "terms": ["What is the syntax for defining query profile types?", "is the syntax for defining query profile types?", "the syntax for defining query profile types?", "syntax for defining query profile types?", "for defining query profile types?", "defining query profile types?", "query profile types?", "profile types?", "types?"], "tokens": ["What", "is", "the", "syntax", "for", "defining", "query", "profile", "types", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-945014078", "fields": {"term": "Where should query profile types be deployed?", "namespace": "open-p", "hash": -945014078, "terms": ["Where should query profile types be deployed?", "should query profile types be deployed?", "query profile types be deployed?", "profile types be deployed?", "types be deployed?", "be deployed?", "deployed?"], "tokens": ["Where", "should", "query", "profile", "types", "be", "deployed", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-139959774", "fields": {"term": "What are the built-in query profile types?", "namespace": "open-p", "hash": -139959774, "terms": ["What are the built-in query profile types?", "are the built-in query profile types?", "the built-in query profile types?", "built-in query profile types?", "query profile types?", "profile types?", "types?"], "tokens": ["What", "are", "the", "built", "in", "query", "profile", "types", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1243541004", "fields": {"term": "What can be specified in a query profile type definition?", "namespace": "open-p", "hash": -1243541004, "terms": ["What can be specified in a query profile type definition?", "can be specified in a query profile type definition?", "be specified in a query profile type definition?", "specified in a query profile type definition?", "in a query profile type definition?", "a query profile type definition?", "query profile type definition?", "profile type definition?", "type definition?", "definition?"], "tokens": ["What", "can", "be", "specified", "in", "a", "query", "profile", "type", "definition", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1235356653", "fields": {"term": "What is path matching?", "namespace": "open-p", "hash": -1235356653, "terms": ["What is path matching?", "is path matching?", "path matching?", "matching?"], "tokens": ["What", "is", "path", "matching", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2054615502", "fields": {"term": "How does path matching work?", "namespace": "open-p", "hash": -2054615502, "terms": ["How does path matching work?", "does path matching work?", "path matching work?", "matching work?", "work?"], "tokens": ["How", "does", "path", "matching", "work", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1770670840", "fields": {"term": "What is ``?", "namespace": "open-p", "hash": -1770670840, "terms": ["What is ``?", "is ``?", "``?", "path='true'>`?"], "tokens": ["What", "is", "`<", "match", "path", "='", "true", "'>`?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1650511535", "fields": {"term": "What are the benefits of path matching?", "namespace": "open-p", "hash": -1650511535, "terms": ["What are the benefits of path matching?", "are the benefits of path matching?", "the benefits of path matching?", "benefits of path matching?", "of path matching?", "path matching?", "matching?"], "tokens": ["What", "are", "the", "benefits", "of", "path", "matching", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2142851599", "fields": {"term": "When is path matching useful?", "namespace": "open-p", "hash": -2142851599, "terms": ["When is path matching useful?", "is path matching useful?", "path matching useful?", "matching useful?", "useful?"], "tokens": ["When", "is", "path", "matching", "useful", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::600070362", "fields": {"term": "How can path matching help with configuration?", "namespace": "open-p", "hash": 600070362, "terms": ["How can path matching help with configuration?", "can path matching help with configuration?", "path matching help with configuration?", "matching help with configuration?", "help with configuration?", "with configuration?", "configuration?"], "tokens": ["How", "can", "path", "matching", "help", "with", "configuration", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1280404133", "fields": {"term": "How is path matching different from exact matching?", "namespace": "open-p", "hash": 1280404133, "terms": ["How is path matching different from exact matching?", "is path matching different from exact matching?", "path matching different from exact matching?", "matching different from exact matching?", "different from exact matching?", "from exact matching?", "exact matching?", "matching?"], "tokens": ["How", "is", "path", "matching", "different", "from", "exact", "matching", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1940991811", "fields": {"term": "What happens if a profile name does not have any subpaths?", "namespace": "open-p", "hash": 1940991811, "terms": ["What happens if a profile name does not have any subpaths?", "happens if a profile name does not have any subpaths?", "if a profile name does not have any subpaths?", "a profile name does not have any subpaths?", "profile name does not have any subpaths?", "name does not have any subpaths?", "does not have any subpaths?", "not have any subpaths?", "have any subpaths?", "any subpaths?", "subpaths?"], "tokens": ["What", "happens", "if", "a", "profile", "name", "does", "not", "have", "any", "subpaths", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1353796029", "fields": {"term": "What is a version string?", "namespace": "open-p", "hash": -1353796029, "terms": ["What is a version string?", "is a version string?", "a version string?", "version string?", "string?"], "tokens": ["What", "is", "a", "version", "string", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::549250553", "fields": {"term": "How are version resolved?", "namespace": "open-p", "hash": 549250553, "terms": ["How are version resolved?", "are version resolved?", "version resolved?", "resolved?"], "tokens": ["How", "are", "version", "resolved", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2060082580", "fields": {"term": "Can query profiles exist in multiple versions?", "namespace": "open-p", "hash": -2060082580, "terms": ["Can query profiles exist in multiple versions?", "query profiles exist in multiple versions?", "profiles exist in multiple versions?", "exist in multiple versions?", "in multiple versions?", "multiple versions?", "versions?"], "tokens": ["Can", "query", "profiles", "exist", "in", "multiple", "versions", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2111807244", "fields": {"term": "How are version numbers specified?", "namespace": "open-p", "hash": -2111807244, "terms": ["How are version numbers specified?", "are version numbers specified?", "version numbers specified?", "numbers specified?", "specified?"], "tokens": ["How", "are", "version", "numbers", "specified", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::521443351", "fields": {"term": "What is component versioning?", "namespace": "open-p", "hash": 521443351, "terms": ["What is component versioning?", "is component versioning?", "component versioning?", "versioning?"], "tokens": ["What", "is", "component", "versioning", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-837033847", "fields": {"term": "What is the file naming convention for query profiles with versioning?", "namespace": "open-p", "hash": -837033847, "terms": ["What is the file naming convention for query profiles with versioning?", "is the file naming convention for query profiles with versioning?", "the file naming convention for query profiles with versioning?", "file naming convention for query profiles with versioning?", "naming convention for query profiles with versioning?", "convention for query profiles with versioning?", "for query profiles with versioning?", "query profiles with versioning?", "profiles with versioning?", "with versioning?", "versioning?"], "tokens": ["What", "is", "the", "file", "naming", "convention", "for", "query", "profiles", "with", "versioning", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::752456535", "fields": {"term": "What is the format for defining Query profile or type id with version?", "namespace": "open-p", "hash": 752456535, "terms": ["What is the format for defining Query profile or type id with version?", "is the format for defining Query profile or type id with version?", "the format for defining Query profile or type id with version?", "format for defining Query profile or type id with version?", "for defining Query profile or type id with version?", "defining Query profile or type id with version?", "Query profile or type id with version?", "profile or type id with version?", "or type id with version?", "type id with version?", "id with version?", "with version?", "version?"], "tokens": ["What", "is", "the", "format", "for", "defining", "Query", "profile", "or", "type", "id", "with", "version", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1153667672", "fields": {"term": "What is vespa-query-profile-dump-tool?", "namespace": "open-p", "hash": 1153667672, "terms": ["What is vespa-query-profile-dump-tool?", "is vespa-query-profile-dump-tool?", "vespa-query-profile-dump-tool?"], "tokens": ["What", "is", "vespa", "query", "profile", "dump", "tool", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1266195963", "fields": {"term": "How to dump resolved query profiles?", "namespace": "open-p", "hash": -1266195963, "terms": ["How to dump resolved query profiles?", "to dump resolved query profiles?", "dump resolved query profiles?", "resolved query profiles?", "query profiles?", "profiles?"], "tokens": ["How", "to", "dump", "resolved", "query", "profiles", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1725570287", "fields": {"term": "What is query rewriting?", "namespace": "open-p", "hash": -1725570287, "terms": ["What is query rewriting?", "is query rewriting?", "query rewriting?", "rewriting?"], "tokens": ["What", "is", "query", "rewriting", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1530602893", "fields": {"term": "What are query focusing techniques?", "namespace": "open-p", "hash": 1530602893, "terms": ["What are query focusing techniques?", "are query focusing techniques?", "query focusing techniques?", "focusing techniques?", "techniques?"], "tokens": ["What", "are", "query", "focusing", "techniques", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::401053541", "fields": {"term": "What is a rewriting searcher?", "namespace": "open-p", "hash": 401053541, "terms": ["What is a rewriting searcher?", "is a rewriting searcher?", "a rewriting searcher?", "rewriting searcher?", "searcher?"], "tokens": ["What", "is", "a", "rewriting", "searcher", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1653031848", "fields": {"term": "How can query enhancing improve search?", "namespace": "open-p", "hash": -1653031848, "terms": ["How can query enhancing improve search?", "can query enhancing improve search?", "query enhancing improve search?", "enhancing improve search?", "improve search?", "search?"], "tokens": ["How", "can", "query", "enhancing", "improve", "search", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-276870128", "fields": {"term": "What is a synonym?", "namespace": "open-p", "hash": -276870128, "terms": ["What is a synonym?", "is a synonym?", "a synonym?", "synonym?"], "tokens": ["What", "is", "a", "synonym", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1760778453", "fields": {"term": "What is the Vespa query rewriting language?", "namespace": "open-p", "hash": 1760778453, "terms": ["What is the Vespa query rewriting language?", "is the Vespa query rewriting language?", "the Vespa query rewriting language?", "Vespa query rewriting language?", "query rewriting language?", "rewriting language?", "language?"], "tokens": ["What", "is", "the", "Vespa", "query", "rewriting", "language", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-532973419", "fields": {"term": "What is EQUIV query operator used for?", "namespace": "open-p", "hash": -532973419, "terms": ["What is EQUIV query operator used for?", "is EQUIV query operator used for?", "EQUIV query operator used for?", "query operator used for?", "operator used for?", "used for?", "for?"], "tokens": ["What", "is", "EQUIV", "query", "operator", "used", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::16125677", "fields": {"term": "How does EQUIV differ from OR?", "namespace": "open-p", "hash": 16125677, "terms": ["How does EQUIV differ from OR?", "does EQUIV differ from OR?", "EQUIV differ from OR?", "differ from OR?", "from OR?", "OR?"], "tokens": ["How", "does", "EQUIV", "differ", "from", "OR", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1188556003", "fields": {"term": "When should EQUIV be used?", "namespace": "open-p", "hash": -1188556003, "terms": ["When should EQUIV be used?", "should EQUIV be used?", "EQUIV be used?", "be used?", "used?"], "tokens": ["When", "should", "EQUIV", "be", "used", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1663039567", "fields": {"term": "How to use EQUIV in YQL?", "namespace": "open-p", "hash": -1663039567, "terms": ["How to use EQUIV in YQL?", "to use EQUIV in YQL?", "use EQUIV in YQL?", "EQUIV in YQL?", "in YQL?", "YQL?"], "tokens": ["How", "to", "use", "EQUIV", "in", "YQL", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1219712305", "fields": {"term": "What is EquivSearcher?", "namespace": "open-p", "hash": 1219712305, "terms": ["What is EquivSearcher?", "is EquivSearcher?", "EquivSearcher?"], "tokens": ["What", "is", "EquivSearcher", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1668406069", "fields": {"term": "How to manipulate the query object?", "namespace": "open-p", "hash": 1668406069, "terms": ["How to manipulate the query object?", "to manipulate the query object?", "manipulate the query object?", "the query object?", "query object?", "object?"], "tokens": ["How", "to", "manipulate", "the", "query", "object", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1052949829", "fields": {"term": "What are the limitations of EQUIV?", "namespace": "open-p", "hash": 1052949829, "terms": ["What are the limitations of EQUIV?", "are the limitations of EQUIV?", "the limitations of EQUIV?", "limitations of EQUIV?", "of EQUIV?", "EQUIV?"], "tokens": ["What", "are", "the", "limitations", "of", "EQUIV", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1445195581", "fields": {"term": "How to add synonyms using EQUIV?", "namespace": "open-p", "hash": 1445195581, "terms": ["How to add synonyms using EQUIV?", "to add synonyms using EQUIV?", "add synonyms using EQUIV?", "synonyms using EQUIV?", "using EQUIV?", "EQUIV?"], "tokens": ["How", "to", "add", "synonyms", "using", "EQUIV", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-972920068", "fields": {"term": "What is a semantic rule?", "namespace": "open-p", "hash": -972920068, "terms": ["What is a semantic rule?", "is a semantic rule?", "a semantic rule?", "semantic rule?", "rule?"], "tokens": ["What", "is", "a", "semantic", "rule", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::1281176493", "fields": {"term": "What is the syntax for semantic rules?", "namespace": "open-p", "hash": 1281176493, "terms": ["What is the syntax for semantic rules?", "is the syntax for semantic rules?", "the syntax for semantic rules?", "syntax for semantic rules?", "for semantic rules?", "semantic rules?", "rules?"], "tokens": ["What", "is", "the", "syntax", "for", "semantic", "rules", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1373656691", "fields": {"term": "What is a condition in a semantic rule?", "namespace": "open-p", "hash": -1373656691, "terms": ["What is a condition in a semantic rule?", "is a condition in a semantic rule?", "a condition in a semantic rule?", "condition in a semantic rule?", "in a semantic rule?", "a semantic rule?", "semantic rule?", "rule?"], "tokens": ["What", "is", "a", "condition", "in", "a", "semantic", "rule", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::418567863", "fields": {"term": "How can adding production rules be written?", "namespace": "open-p", "hash": 418567863, "terms": ["How can adding production rules be written?", "can adding production rules be written?", "adding production rules be written?", "production rules be written?", "rules be written?", "be written?", "written?"], "tokens": ["How", "can", "adding", "production", "rules", "be", "written", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-163939421", "fields": {"term": "What does RANK do in a semantic rule?", "namespace": "open-p", "hash": -163939421, "terms": ["What does RANK do in a semantic rule?", "does RANK do in a semantic rule?", "RANK do in a semantic rule?", "do in a semantic rule?", "in a semantic rule?", "a semantic rule?", "semantic rule?", "rule?"], "tokens": ["What", "does", "RANK", "do", "in", "a", "semantic", "rule", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-999438322", "fields": {"term": "What is the purpose of grouping in semantic rules?", "namespace": "open-p", "hash": -999438322, "terms": ["What is the purpose of grouping in semantic rules?", "is the purpose of grouping in semantic rules?", "the purpose of grouping in semantic rules?", "purpose of grouping in semantic rules?", "of grouping in semantic rules?", "grouping in semantic rules?", "in semantic rules?", "semantic rules?", "rules?"], "tokens": ["What", "is", "the", "purpose", "of", "grouping", "in", "semantic", "rules", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::354682497", "fields": {"term": "What is the format for defining a condition in a semantic rule?", "namespace": "open-p", "hash": 354682497, "terms": ["What is the format for defining a condition in a semantic rule?", "is the format for defining a condition in a semantic rule?", "the format for defining a condition in a semantic rule?", "format for defining a condition in a semantic rule?", "for defining a condition in a semantic rule?", "defining a condition in a semantic rule?", "a condition in a semantic rule?", "condition in a semantic rule?", "in a semantic rule?", "a semantic rule?", "semantic rule?", "rule?"], "tokens": ["What", "is", "the", "format", "for", "defining", "a", "condition", "in", "a", "semantic", "rule", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::719900742", "fields": {"term": "What is a rule base?", "namespace": "open-p", "hash": 719900742, "terms": ["What is a rule base?", "is a rule base?", "a rule base?", "rule base?", "base?"], "tokens": ["What", "is", "a", "rule", "base", "?"], "corpus_count": 3, "document_count": 3}}, -{"put": "id:term:term::-300292133", "fields": {"term": "What is the suffix for rule base files?", "namespace": "open-p", "hash": -300292133, "terms": ["What is the suffix for rule base files?", "is the suffix for rule base files?", "the suffix for rule base files?", "suffix for rule base files?", "for rule base files?", "rule base files?", "base files?", "files?"], "tokens": ["What", "is", "the", "suffix", "for", "rule", "base", "files", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1026497531", "fields": {"term": "What is contained in a rule base file?", "namespace": "open-p", "hash": -1026497531, "terms": ["What is contained in a rule base file?", "is contained in a rule base file?", "contained in a rule base file?", "in a rule base file?", "a rule base file?", "rule base file?", "base file?", "file?"], "tokens": ["What", "is", "contained", "in", "a", "rule", "base", "file", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-534554526", "fields": {"term": "What is a collection of rules?", "namespace": "open-p", "hash": -534554526, "terms": ["What is a collection of rules?", "is a collection of rules?", "a collection of rules?", "collection of rules?", "of rules?", "rules?"], "tokens": ["What", "is", "a", "collection", "of", "rules", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::521283312", "fields": {"term": "What is the file extension for rule bases?", "namespace": "open-p", "hash": 521283312, "terms": ["What is the file extension for rule bases?", "is the file extension for rule bases?", "the file extension for rule bases?", "file extension for rule bases?", "extension for rule bases?", "for rule bases?", "rule bases?", "bases?"], "tokens": ["What", "is", "the", "file", "extension", "for", "rule", "bases", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1468332814", "fields": {"term": "Where are rules and conditions stored?", "namespace": "open-p", "hash": -1468332814, "terms": ["Where are rules and conditions stored?", "are rules and conditions stored?", "rules and conditions stored?", "and conditions stored?", "conditions stored?", "stored?"], "tokens": ["Where", "are", "rules", "and", "conditions", "stored", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::694815223", "fields": {"term": "What is a rule base file used for?", "namespace": "open-p", "hash": 694815223, "terms": ["What is a rule base file used for?", "is a rule base file used for?", "a rule base file used for?", "rule base file used for?", "base file used for?", "file used for?", "used for?", "for?"], "tokens": ["What", "is", "a", "rule", "base", "file", "used", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1399879108", "fields": {"term": "What is the meaning of `colour`?", "namespace": "open-p", "hash": 1399879108, "terms": ["What is the meaning of `colour`?", "is the meaning of `colour`?", "the meaning of `colour`?", "meaning of `colour`?", "of `colour`?", "`colour`?"], "tokens": ["What", "is", "the", "meaning", "of", "`", "colour", "`?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1497840074", "fields": {"term": "What is the replacement for `audi`?", "namespace": "open-p", "hash": 1497840074, "terms": ["What is the replacement for `audi`?", "is the replacement for `audi`?", "the replacement for `audi`?", "replacement for `audi`?", "for `audi`?", "`audi`?"], "tokens": ["What", "is", "the", "replacement", "for", "`", "audi", "`?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::98935246", "fields": {"term": "What are stopwords?", "namespace": "open-p", "hash": 98935246, "terms": ["What are stopwords?", "are stopwords?", "stopwords?"], "tokens": ["What", "are", "stopwords", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1625148480", "fields": {"term": "How do you replace stopwords?", "namespace": "open-p", "hash": 1625148480, "terms": ["How do you replace stopwords?", "do you replace stopwords?", "you replace stopwords?", "replace stopwords?", "stopwords?"], "tokens": ["How", "do", "you", "replace", "stopwords", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1032583818", "fields": {"term": "How to name rule bases?", "namespace": "open-p", "hash": -1032583818, "terms": ["How to name rule bases?", "to name rule bases?", "name rule bases?", "rule bases?", "bases?"], "tokens": ["How", "to", "name", "rule", "bases", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::689150469", "fields": {"term": "How to make a rule base default?", "namespace": "open-p", "hash": 689150469, "terms": ["How to make a rule base default?", "to make a rule base default?", "make a rule base default?", "a rule base default?", "rule base default?", "base default?", "default?"], "tokens": ["How", "to", "make", "a", "rule", "base", "default", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-730358507", "fields": {"term": "How to deactivate default rules?", "namespace": "open-p", "hash": -730358507, "terms": ["How to deactivate default rules?", "to deactivate default rules?", "deactivate default rules?", "default rules?", "rules?"], "tokens": ["How", "to", "deactivate", "default", "rules", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::190761268", "fields": {"term": "How to update rules safely?", "namespace": "open-p", "hash": 190761268, "terms": ["How to update rules safely?", "to update rules safely?", "update rules safely?", "rules safely?", "safely?"], "tokens": ["How", "to", "update", "rules", "safely", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1977681486", "fields": {"term": "What happens if rule bases have errors?", "namespace": "open-p", "hash": -1977681486, "terms": ["What happens if rule bases have errors?", "happens if rule bases have errors?", "if rule bases have errors?", "rule bases have errors?", "bases have errors?", "have errors?", "errors?"], "tokens": ["What", "happens", "if", "rule", "bases", "have", "errors", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1933241593", "fields": {"term": "How to include rulebases?", "namespace": "open-p", "hash": 1933241593, "terms": ["How to include rulebases?", "to include rulebases?", "include rulebases?", "rulebases?"], "tokens": ["How", "to", "include", "rulebases", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-789761532", "fields": {"term": "What is @include used for?", "namespace": "open-p", "hash": -789761532, "terms": ["What is @include used for?", "is @include used for?", "@include used for?", "used for?", "for?"], "tokens": ["What", "is", "@", "include", "used", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1695622546", "fields": {"term": "How can a default rule base be set?", "namespace": "open-p", "hash": -1695622546, "terms": ["How can a default rule base be set?", "can a default rule base be set?", "a default rule base be set?", "default rule base be set?", "rule base be set?", "base be set?", "be set?", "set?"], "tokens": ["How", "can", "a", "default", "rule", "base", "be", "set", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::759628437", "fields": {"term": "How can another rule base be used?", "namespace": "open-p", "hash": 759628437, "terms": ["How can another rule base be used?", "can another rule base be used?", "another rule base be used?", "rule base be used?", "base be used?", "be used?", "used?"], "tokens": ["How", "can", "another", "rule", "base", "be", "used", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1035017020", "fields": {"term": "What is a Finite state automaton?", "namespace": "open-p", "hash": 1035017020, "terms": ["What is a Finite state automaton?", "is a Finite state automaton?", "a Finite state automaton?", "Finite state automaton?", "state automaton?", "automaton?"], "tokens": ["What", "is", "a", "Finite", "state", "automaton", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-208622393", "fields": {"term": "What is vespa-makefsa?", "namespace": "open-p", "hash": -208622393, "terms": ["What is vespa-makefsa?", "is vespa-makefsa?", "vespa-makefsa?"], "tokens": ["What", "is", "vespa", "makefsa", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::-791389571", "fields": {"term": "How are automata used in rule bases?", "namespace": "open-p", "hash": -791389571, "terms": ["How are automata used in rule bases?", "are automata used in rule bases?", "automata used in rule bases?", "used in rule bases?", "in rule bases?", "rule bases?", "bases?"], "tokens": ["How", "are", "automata", "used", "in", "rule", "bases", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::592296828", "fields": {"term": "How to compile an automaton source file?", "namespace": "open-p", "hash": 592296828, "terms": ["How to compile an automaton source file?", "to compile an automaton source file?", "compile an automaton source file?", "an automaton source file?", "automaton source file?", "source file?", "file?"], "tokens": ["How", "to", "compile", "an", "automaton", "source", "file", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-982596421", "fields": {"term": "What is the format of an automaton source file?", "namespace": "open-p", "hash": -982596421, "terms": ["What is the format of an automaton source file?", "is the format of an automaton source file?", "the format of an automaton source file?", "format of an automaton source file?", "of an automaton source file?", "an automaton source file?", "automaton source file?", "source file?", "file?"], "tokens": ["What", "is", "the", "format", "of", "an", "automaton", "source", "file", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1589727867", "fields": {"term": "How to use automata in a rule base?", "namespace": "open-p", "hash": -1589727867, "terms": ["How to use automata in a rule base?", "to use automata in a rule base?", "use automata in a rule base?", "automata in a rule base?", "in a rule base?", "a rule base?", "rule base?", "base?"], "tokens": ["How", "to", "use", "automata", "in", "a", "rule", "base", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1201132277", "fields": {"term": "What is the purpose of using automata in a rule base?", "namespace": "open-p", "hash": -1201132277, "terms": ["What is the purpose of using automata in a rule base?", "is the purpose of using automata in a rule base?", "the purpose of using automata in a rule base?", "purpose of using automata in a rule base?", "of using automata in a rule base?", "using automata in a rule base?", "automata in a rule base?", "in a rule base?", "a rule base?", "rule base?", "base?"], "tokens": ["What", "is", "the", "purpose", "of", "using", "automata", "in", "a", "rule", "base", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1794452301", "fields": {"term": "How to declare automata in a rule base?", "namespace": "open-p", "hash": -1794452301, "terms": ["How to declare automata in a rule base?", "to declare automata in a rule base?", "declare automata in a rule base?", "automata in a rule base?", "in a rule base?", "a rule base?", "rule base?", "base?"], "tokens": ["How", "to", "declare", "automata", "in", "a", "rule", "base", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::595189545", "fields": {"term": "What is the PhrasingSearcher?", "namespace": "open-p", "hash": 595189545, "terms": ["What is the PhrasingSearcher?", "is the PhrasingSearcher?", "the PhrasingSearcher?", "PhrasingSearcher?"], "tokens": ["What", "is", "the", "PhrasingSearcher", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-235926254", "fields": {"term": "How is the PhrasingSearcher configured?", "namespace": "open-p", "hash": -235926254, "terms": ["How is the PhrasingSearcher configured?", "is the PhrasingSearcher configured?", "the PhrasingSearcher configured?", "PhrasingSearcher configured?", "configured?"], "tokens": ["How", "is", "the", "PhrasingSearcher", "configured", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1794694925", "fields": {"term": "What is the purpose of vespa-makefsa?", "namespace": "open-p", "hash": 1794694925, "terms": ["What is the purpose of vespa-makefsa?", "is the purpose of vespa-makefsa?", "the purpose of vespa-makefsa?", "purpose of vespa-makefsa?", "of vespa-makefsa?", "vespa-makefsa?"], "tokens": ["What", "is", "the", "purpose", "of", "vespa", "makefsa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::982186398", "fields": {"term": "What does the PhrasingSearcher do?", "namespace": "open-p", "hash": 982186398, "terms": ["What does the PhrasingSearcher do?", "does the PhrasingSearcher do?", "the PhrasingSearcher do?", "PhrasingSearcher do?", "do?"], "tokens": ["What", "does", "the", "PhrasingSearcher", "do", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1115902144", "fields": {"term": "How should the list of phrases be sorted?", "namespace": "open-p", "hash": -1115902144, "terms": ["How should the list of phrases be sorted?", "should the list of phrases be sorted?", "the list of phrases be sorted?", "list of phrases be sorted?", "of phrases be sorted?", "phrases be sorted?", "be sorted?", "sorted?"], "tokens": ["How", "should", "the", "list", "of", "phrases", "be", "sorted", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-137053004", "fields": {"term": "What is the output of vespa-makefsa?", "namespace": "open-p", "hash": -137053004, "terms": ["What is the output of vespa-makefsa?", "is the output of vespa-makefsa?", "the output of vespa-makefsa?", "output of vespa-makefsa?", "of vespa-makefsa?", "vespa-makefsa?"], "tokens": ["What", "is", "the", "output", "of", "vespa", "makefsa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1106316813", "fields": {"term": "What is the format of the phrase list file?", "namespace": "open-p", "hash": -1106316813, "terms": ["What is the format of the phrase list file?", "is the format of the phrase list file?", "the format of the phrase list file?", "format of the phrase list file?", "of the phrase list file?", "the phrase list file?", "phrase list file?", "list file?", "file?"], "tokens": ["What", "is", "the", "format", "of", "the", "phrase", "list", "file", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2067247487", "fields": {"term": "What happens if the list of phrases contains non-ASCII characters?", "namespace": "open-p", "hash": 2067247487, "terms": ["What happens if the list of phrases contains non-ASCII characters?", "happens if the list of phrases contains non-ASCII characters?", "if the list of phrases contains non-ASCII characters?", "the list of phrases contains non-ASCII characters?", "list of phrases contains non-ASCII characters?", "of phrases contains non-ASCII characters?", "phrases contains non-ASCII characters?", "contains non-ASCII characters?", "non-ASCII characters?", "characters?"], "tokens": ["What", "happens", "if", "the", "list", "of", "phrases", "contains", "non", "ASCII", "characters", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-737376462", "fields": {"term": "What are query tokens?", "namespace": "open-p", "hash": -737376462, "terms": ["What are query tokens?", "are query tokens?", "query tokens?", "tokens?"], "tokens": ["What", "are", "query", "tokens", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1173326248", "fields": {"term": "What are special tokens?", "namespace": "open-p", "hash": -1173326248, "terms": ["What are special tokens?", "are special tokens?", "special tokens?", "tokens?"], "tokens": ["What", "are", "special", "tokens", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1594777435", "fields": {"term": "How do special tokens work?", "namespace": "open-p", "hash": 1594777435, "terms": ["How do special tokens work?", "do special tokens work?", "special tokens work?", "tokens work?", "work?"], "tokens": ["How", "do", "special", "tokens", "work", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-926674244", "fields": {"term": "What is the syntax for special tokens?", "namespace": "open-p", "hash": -926674244, "terms": ["What is the syntax for special tokens?", "is the syntax for special tokens?", "the syntax for special tokens?", "syntax for special tokens?", "for special tokens?", "special tokens?", "tokens?"], "tokens": ["What", "is", "the", "syntax", "for", "special", "tokens", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1004208243", "fields": {"term": "What is specialtokens config?", "namespace": "open-p", "hash": 1004208243, "terms": ["What is specialtokens config?", "is specialtokens config?", "specialtokens config?", "config?"], "tokens": ["What", "is", "specialtokens", "config", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1828706918", "fields": {"term": "What is text character?", "namespace": "open-p", "hash": -1828706918, "terms": ["What is text character?", "is text character?", "text character?", "character?"], "tokens": ["What", "is", "text", "character", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1593198221", "fields": {"term": "What is specialtokens.def?", "namespace": "open-p", "hash": 1593198221, "terms": ["What is specialtokens.def?", "is specialtokens.def?", "specialtokens.def?"], "tokens": ["What", "is", "specialtokens", "def", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::958709369", "fields": {"term": "What is the default token list?", "namespace": "open-p", "hash": 958709369, "terms": ["What is the default token list?", "is the default token list?", "the default token list?", "default token list?", "token list?", "list?"], "tokens": ["What", "is", "the", "default", "token", "list", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-209441693", "fields": {"term": "What are rank features?", "namespace": "open-p", "hash": -209441693, "terms": ["What are rank features?", "are rank features?", "rank features?", "features?"], "tokens": ["What", "are", "rank", "features", "?"], "corpus_count": 3, "document_count": 3}}, -{"put": "id:term:term::-711435063", "fields": {"term": "How do you set configuration variables?", "namespace": "open-p", "hash": -711435063, "terms": ["How do you set configuration variables?", "do you set configuration variables?", "you set configuration variables?", "set configuration variables?", "configuration variables?", "variables?"], "tokens": ["How", "do", "you", "set", "configuration", "variables", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-649321541", "fields": {"term": "What are per field features?", "namespace": "open-p", "hash": -649321541, "terms": ["What are per field features?", "are per field features?", "per field features?", "field features?", "features?"], "tokens": ["What", "are", "per", "field", "features", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1329716679", "fields": {"term": "What is a feature class?", "namespace": "open-p", "hash": -1329716679, "terms": ["What is a feature class?", "is a feature class?", "a feature class?", "feature class?", "class?"], "tokens": ["What", "is", "a", "feature", "class", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1917851007", "fields": {"term": "What are configuration properties?", "namespace": "open-p", "hash": 1917851007, "terms": ["What are configuration properties?", "are configuration properties?", "configuration properties?", "properties?"], "tokens": ["What", "are", "configuration", "properties", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-884483243", "fields": {"term": "What is the syntax for setting rank properties?", "namespace": "open-p", "hash": -884483243, "terms": ["What is the syntax for setting rank properties?", "is the syntax for setting rank properties?", "the syntax for setting rank properties?", "syntax for setting rank properties?", "for setting rank properties?", "setting rank properties?", "rank properties?", "properties?"], "tokens": ["What", "is", "the", "syntax", "for", "setting", "rank", "properties", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::733844074", "fields": {"term": "What is a usage example of rank properties and features?", "namespace": "open-p", "hash": 733844074, "terms": ["What is a usage example of rank properties and features?", "is a usage example of rank properties and features?", "a usage example of rank properties and features?", "usage example of rank properties and features?", "example of rank properties and features?", "of rank properties and features?", "rank properties and features?", "properties and features?", "and features?", "features?"], "tokens": ["What", "is", "a", "usage", "example", "of", "rank", "properties", "and", "features", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-713959723", "fields": {"term": "What is the default numTerms for term feature?", "namespace": "open-p", "hash": -713959723, "terms": ["What is the default numTerms for term feature?", "is the default numTerms for term feature?", "the default numTerms for term feature?", "default numTerms for term feature?", "numTerms for term feature?", "for term feature?", "term feature?", "feature?"], "tokens": ["What", "is", "the", "default", "numTerms", "for", "term", "feature", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1934961244", "fields": {"term": "What is the default b value for bm25 feature?", "namespace": "open-p", "hash": 1934961244, "terms": ["What is the default b value for bm25 feature?", "is the default b value for bm25 feature?", "the default b value for bm25 feature?", "default b value for bm25 feature?", "b value for bm25 feature?", "value for bm25 feature?", "for bm25 feature?", "bm25 feature?", "feature?"], "tokens": ["What", "is", "the", "default", "b", "value", "for", "bm25", "feature", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1321115873", "fields": {"term": "What is the maximum allowed gap in the fieldMatch feature?", "namespace": "open-p", "hash": 1321115873, "terms": ["What is the maximum allowed gap in the fieldMatch feature?", "is the maximum allowed gap in the fieldMatch feature?", "the maximum allowed gap in the fieldMatch feature?", "maximum allowed gap in the fieldMatch feature?", "allowed gap in the fieldMatch feature?", "gap in the fieldMatch feature?", "in the fieldMatch feature?", "the fieldMatch feature?", "fieldMatch feature?", "feature?"], "tokens": ["What", "is", "the", "maximum", "allowed", "gap", "in", "the", "fieldMatch", "feature", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::108148345", "fields": {"term": "What is the maximum number of alternative segmentations allowed?", "namespace": "open-p", "hash": 108148345, "terms": ["What is the maximum number of alternative segmentations allowed?", "is the maximum number of alternative segmentations allowed?", "the maximum number of alternative segmentations allowed?", "maximum number of alternative segmentations allowed?", "number of alternative segmentations allowed?", "of alternative segmentations allowed?", "alternative segmentations allowed?", "segmentations allowed?", "allowed?"], "tokens": ["What", "is", "the", "maximum", "number", "of", "alternative", "segmentations", "allowed", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1220532334", "fields": {"term": "What is the maximal age in seconds used for freshness feature?", "namespace": "open-p", "hash": 1220532334, "terms": ["What is the maximal age in seconds used for freshness feature?", "is the maximal age in seconds used for freshness feature?", "the maximal age in seconds used for freshness feature?", "maximal age in seconds used for freshness feature?", "age in seconds used for freshness feature?", "in seconds used for freshness feature?", "seconds used for freshness feature?", "used for freshness feature?", "for freshness feature?", "freshness feature?", "feature?"], "tokens": ["What", "is", "the", "maximal", "age", "in", "seconds", "used", "for", "freshness", "feature", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2004561414", "fields": {"term": "What is the rank feature?", "namespace": "open-p", "hash": -2004561414, "terms": ["What is the rank feature?", "is the rank feature?", "the rank feature?", "rank feature?", "feature?"], "tokens": ["What", "is", "the", "rank", "feature", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-613282761", "fields": {"term": "What are the available rank features?", "namespace": "open-p", "hash": -613282761, "terms": ["What are the available rank features?", "are the available rank features?", "the available rank features?", "available rank features?", "rank features?", "features?"], "tokens": ["What", "are", "the", "available", "rank", "features", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1562648317", "fields": {"term": "What is the range of normalized features?", "namespace": "open-p", "hash": -1562648317, "terms": ["What is the range of normalized features?", "is the range of normalized features?", "the range of normalized features?", "range of normalized features?", "of normalized features?", "normalized features?", "features?"], "tokens": ["What", "is", "the", "range", "of", "normalized", "features", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::1484381600", "fields": {"term": "Can a query override a rank feature value?", "namespace": "open-p", "hash": 1484381600, "terms": ["Can a query override a rank feature value?", "a query override a rank feature value?", "query override a rank feature value?", "override a rank feature value?", "a rank feature value?", "rank feature value?", "feature value?", "value?"], "tokens": ["Can", "a", "query", "override", "a", "rank", "feature", "value", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1404244389", "fields": {"term": "Do some rank features have parameters?", "namespace": "open-p", "hash": 1404244389, "terms": ["Do some rank features have parameters?", "some rank features have parameters?", "rank features have parameters?", "features have parameters?", "have parameters?", "parameters?"], "tokens": ["Do", "some", "rank", "features", "have", "parameters", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1325319120", "fields": {"term": "How are string literals converted?", "namespace": "open-p", "hash": 1325319120, "terms": ["How are string literals converted?", "are string literals converted?", "string literals converted?", "literals converted?", "converted?"], "tokens": ["How", "are", "string", "literals", "converted", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-268543167", "fields": {"term": "What is the overview of the ranking framework?", "namespace": "open-p", "hash": -268543167, "terms": ["What is the overview of the ranking framework?", "is the overview of the ranking framework?", "the overview of the ranking framework?", "overview of the ranking framework?", "of the ranking framework?", "the ranking framework?", "ranking framework?", "framework?"], "tokens": ["What", "is", "the", "overview", "of", "the", "ranking", "framework", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2107332227", "fields": {"term": "What is shown in the plot?", "namespace": "open-p", "hash": -2107332227, "terms": ["What is shown in the plot?", "is shown in the plot?", "shown in the plot?", "in the plot?", "the plot?", "plot?"], "tokens": ["What", "is", "shown", "in", "the", "plot", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::430827336", "fields": {"term": "What is closeness distance?", "namespace": "open-p", "hash": 430827336, "terms": ["What is closeness distance?", "is closeness distance?", "closeness distance?", "distance?"], "tokens": ["What", "is", "closeness", "distance", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1795832552", "fields": {"term": "What is the default maxDistance?", "namespace": "open-p", "hash": 1795832552, "terms": ["What is the default maxDistance?", "is the default maxDistance?", "the default maxDistance?", "default maxDistance?", "maxDistance?"], "tokens": ["What", "is", "the", "default", "maxDistance", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1388205414", "fields": {"term": "What are possible scaleDistance values?", "namespace": "open-p", "hash": 1388205414, "terms": ["What are possible scaleDistance values?", "are possible scaleDistance values?", "possible scaleDistance values?", "scaleDistance values?", "values?"], "tokens": ["What", "are", "possible", "scaleDistance", "values", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1754727080", "fields": {"term": "What are the corresponding halfResponse parameters?", "namespace": "open-p", "hash": 1754727080, "terms": ["What are the corresponding halfResponse parameters?", "are the corresponding halfResponse parameters?", "the corresponding halfResponse parameters?", "corresponding halfResponse parameters?", "halfResponse parameters?", "parameters?"], "tokens": ["What", "are", "the", "corresponding", "halfResponse", "parameters", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1404038228", "fields": {"term": "How does scaleDistance affect the logscale output?", "namespace": "open-p", "hash": -1404038228, "terms": ["How does scaleDistance affect the logscale output?", "does scaleDistance affect the logscale output?", "scaleDistance affect the logscale output?", "affect the logscale output?", "the logscale output?", "logscale output?", "output?"], "tokens": ["How", "does", "scaleDistance", "affect", "the", "logscale", "output", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::123883550", "fields": {"term": "What is the default value of scaleDistance?", "namespace": "open-p", "hash": 123883550, "terms": ["What is the default value of scaleDistance?", "is the default value of scaleDistance?", "the default value of scaleDistance?", "default value of scaleDistance?", "value of scaleDistance?", "of scaleDistance?", "scaleDistance?"], "tokens": ["What", "is", "the", "default", "value", "of", "scaleDistance", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::944686290", "fields": {"term": "What is the default closeness output graph called?", "namespace": "open-p", "hash": 944686290, "terms": ["What is the default closeness output graph called?", "is the default closeness output graph called?", "the default closeness output graph called?", "default closeness output graph called?", "closeness output graph called?", "output graph called?", "graph called?", "called?"], "tokens": ["What", "is", "the", "default", "closeness", "output", "graph", "called", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-864800466", "fields": {"term": "What does the plot show?", "namespace": "open-p", "hash": -864800466, "terms": ["What does the plot show?", "does the plot show?", "the plot show?", "plot show?", "show?"], "tokens": ["What", "does", "the", "plot", "show", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-707882229", "fields": {"term": "What is freshness rank?", "namespace": "open-p", "hash": -707882229, "terms": ["What is freshness rank?", "is freshness rank?", "freshness rank?", "rank?"], "tokens": ["What", "is", "freshness", "rank", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1329598193", "fields": {"term": "What is the default maxAge?", "namespace": "open-p", "hash": 1329598193, "terms": ["What is the default maxAge?", "is the default maxAge?", "the default maxAge?", "default maxAge?", "maxAge?"], "tokens": ["What", "is", "the", "default", "maxAge", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::893069119", "fields": {"term": "What is the default halfResponse value?", "namespace": "open-p", "hash": 893069119, "terms": ["What is the default halfResponse value?", "is the default halfResponse value?", "the default halfResponse value?", "default halfResponse value?", "halfResponse value?", "value?"], "tokens": ["What", "is", "the", "default", "halfResponse", "value", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1089488757", "fields": {"term": "What are the different logscale outputs for halfResponse parameter?", "namespace": "open-p", "hash": 1089488757, "terms": ["What are the different logscale outputs for halfResponse parameter?", "are the different logscale outputs for halfResponse parameter?", "the different logscale outputs for halfResponse parameter?", "different logscale outputs for halfResponse parameter?", "logscale outputs for halfResponse parameter?", "outputs for halfResponse parameter?", "for halfResponse parameter?", "halfResponse parameter?", "parameter?"], "tokens": ["What", "are", "the", "different", "logscale", "outputs", "for", "halfResponse", "parameter", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1513001071", "fields": {"term": "What is the output for halfResponse=172800s?", "namespace": "open-p", "hash": 1513001071, "terms": ["What is the output for halfResponse=172800s?", "is the output for halfResponse=172800s?", "the output for halfResponse=172800s?", "output for halfResponse=172800s?", "for halfResponse=172800s?", "halfResponse=172800s?"], "tokens": ["What", "is", "the", "output", "for", "halfResponse", "=", "172800s", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::203924003", "fields": {"term": "What is the output for halfResponse=1209600s?", "namespace": "open-p", "hash": 203924003, "terms": ["What is the output for halfResponse=1209600s?", "is the output for halfResponse=1209600s?", "the output for halfResponse=1209600s?", "output for halfResponse=1209600s?", "for halfResponse=1209600s?", "halfResponse=1209600s?"], "tokens": ["What", "is", "the", "output", "for", "halfResponse", "=", "1209600s", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1808714387", "fields": {"term": "What are the supported rank-types?", "namespace": "open-p", "hash": -1808714387, "terms": ["What are the supported rank-types?", "are the supported rank-types?", "the supported rank-types?", "supported rank-types?", "rank-types?"], "tokens": ["What", "are", "the", "supported", "rank", "types", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1182825290", "fields": {"term": "How are ranking features controlled?", "namespace": "open-p", "hash": -1182825290, "terms": ["How are ranking features controlled?", "are ranking features controlled?", "ranking features controlled?", "features controlled?", "controlled?"], "tokens": ["How", "are", "ranking", "features", "controlled", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-130687059", "fields": {"term": "What is the native rank reference?", "namespace": "open-p", "hash": -130687059, "terms": ["What is the native rank reference?", "is the native rank reference?", "the native rank reference?", "native rank reference?", "rank reference?", "reference?"], "tokens": ["What", "is", "the", "native", "rank", "reference", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1364939383", "fields": {"term": "How are ranking types described?", "namespace": "open-p", "hash": -1364939383, "terms": ["How are ranking types described?", "are ranking types described?", "ranking types described?", "types described?", "described?"], "tokens": ["How", "are", "ranking", "types", "described", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::362207182", "fields": {"term": "What are the generic mechanisms for ranking control?", "namespace": "open-p", "hash": 362207182, "terms": ["What are the generic mechanisms for ranking control?", "are the generic mechanisms for ranking control?", "the generic mechanisms for ranking control?", "generic mechanisms for ranking control?", "mechanisms for ranking control?", "for ranking control?", "ranking control?", "control?"], "tokens": ["What", "are", "the", "generic", "mechanisms", "for", "ranking", "control", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::894491767", "fields": {"term": "What are the different ranking controls used?", "namespace": "open-p", "hash": 894491767, "terms": ["What are the different ranking controls used?", "are the different ranking controls used?", "the different ranking controls used?", "different ranking controls used?", "ranking controls used?", "controls used?", "used?"], "tokens": ["What", "are", "the", "different", "ranking", "controls", "used", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1615518938", "fields": {"term": "Where can I find detailed information on nativeRank?", "namespace": "open-p", "hash": 1615518938, "terms": ["Where can I find detailed information on nativeRank?", "can I find detailed information on nativeRank?", "I find detailed information on nativeRank?", "find detailed information on nativeRank?", "detailed information on nativeRank?", "information on nativeRank?", "on nativeRank?", "nativeRank?"], "tokens": ["Where", "can", "I", "find", "detailed", "information", "on", "nativeRank", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1551464677", "fields": {"term": "What is rank-types?", "namespace": "open-p", "hash": -1551464677, "terms": ["What is rank-types?", "is rank-types?", "rank-types?"], "tokens": ["What", "is", "rank", "types", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1308592009", "fields": {"term": "What is rank-profile?", "namespace": "open-p", "hash": -1308592009, "terms": ["What is rank-profile?", "is rank-profile?", "rank-profile?"], "tokens": ["What", "is", "rank", "profile", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1309200284", "fields": {"term": "Can rank-types be controlled per field?", "namespace": "open-p", "hash": 1309200284, "terms": ["Can rank-types be controlled per field?", "rank-types be controlled per field?", "be controlled per field?", "controlled per field?", "per field?", "field?"], "tokens": ["Can", "rank", "types", "be", "controlled", "per", "field", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1581330391", "fields": {"term": "What is 'expression' used for in rank-profile?", "namespace": "open-p", "hash": 1581330391, "terms": ["What is 'expression' used for in rank-profile?", "is 'expression' used for in rank-profile?", "'expression' used for in rank-profile?", "used for in rank-profile?", "for in rank-profile?", "in rank-profile?", "rank-profile?"], "tokens": ["What", "is", "'", "expression", "'", "used", "for", "in", "rank", "profile", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1531647953", "fields": {"term": "What are generic ranking controls?", "namespace": "open-p", "hash": -1531647953, "terms": ["What are generic ranking controls?", "are generic ranking controls?", "generic ranking controls?", "ranking controls?", "controls?"], "tokens": ["What", "are", "generic", "ranking", "controls", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1844044696", "fields": {"term": "What influences ranking type selection?", "namespace": "open-p", "hash": 1844044696, "terms": ["What influences ranking type selection?", "influences ranking type selection?", "ranking type selection?", "type selection?", "selection?"], "tokens": ["What", "influences", "ranking", "type", "selection", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1162452270", "fields": {"term": "What is FirstOcc?", "namespace": "open-p", "hash": 1162452270, "terms": ["What is FirstOcc?", "is FirstOcc?", "FirstOcc?"], "tokens": ["What", "is", "FirstOcc", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::739031952", "fields": {"term": "What is a FirstOcc boost table?", "namespace": "open-p", "hash": 739031952, "terms": ["What is a FirstOcc boost table?", "is a FirstOcc boost table?", "a FirstOcc boost table?", "FirstOcc boost table?", "boost table?", "table?"], "tokens": ["What", "is", "a", "FirstOcc", "boost", "table", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1644809996", "fields": {"term": "What does equation 1 represent?", "namespace": "open-p", "hash": -1644809996, "terms": ["What does equation 1 represent?", "does equation 1 represent?", "equation 1 represent?", "1 represent?", "represent?"], "tokens": ["What", "does", "equation", "1", "represent", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1059908323", "fields": {"term": "What is the weight parameter?", "namespace": "open-p", "hash": -1059908323, "terms": ["What is the weight parameter?", "is the weight parameter?", "the weight parameter?", "weight parameter?", "parameter?"], "tokens": ["What", "is", "the", "weight", "parameter", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::2041419302", "fields": {"term": "What does Figure 1-2 illustrate?", "namespace": "open-p", "hash": 2041419302, "terms": ["What does Figure 1-2 illustrate?", "does Figure 1-2 illustrate?", "Figure 1-2 illustrate?", "1-2 illustrate?", "illustrate?"], "tokens": ["What", "does", "Figure", "1", "2", "illustrate", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-465265317", "fields": {"term": "What does increasing w do in Figure 1?", "namespace": "open-p", "hash": -465265317, "terms": ["What does increasing w do in Figure 1?", "does increasing w do in Figure 1?", "increasing w do in Figure 1?", "w do in Figure 1?", "do in Figure 1?", "in Figure 1?", "Figure 1?", "1?"], "tokens": ["What", "does", "increasing", "w", "do", "in", "Figure", "1", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::245247614", "fields": {"term": "What does increasing t do in Figure 2?", "namespace": "open-p", "hash": 245247614, "terms": ["What does increasing t do in Figure 2?", "does increasing t do in Figure 2?", "increasing t do in Figure 2?", "t do in Figure 2?", "do in Figure 2?", "in Figure 2?", "Figure 2?", "2?"], "tokens": ["What", "does", "increasing", "t", "do", "in", "Figure", "2", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::57201863", "fields": {"term": "What is NumOcc?", "namespace": "open-p", "hash": 57201863, "terms": ["What is NumOcc?", "is NumOcc?", "NumOcc?"], "tokens": ["What", "is", "NumOcc", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1019465366", "fields": {"term": "What is a NumOcc boost table?", "namespace": "open-p", "hash": -1019465366, "terms": ["What is a NumOcc boost table?", "is a NumOcc boost table?", "a NumOcc boost table?", "NumOcc boost table?", "boost table?", "table?"], "tokens": ["What", "is", "a", "NumOcc", "boost", "table", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1206725710", "fields": {"term": "What does the NumOcc boost table function do?", "namespace": "open-p", "hash": 1206725710, "terms": ["What does the NumOcc boost table function do?", "does the NumOcc boost table function do?", "the NumOcc boost table function do?", "NumOcc boost table function do?", "boost table function do?", "table function do?", "function do?", "do?"], "tokens": ["What", "does", "the", "NumOcc", "boost", "table", "function", "do", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-657419507", "fields": {"term": "What is the equation for the NumOcc boost table function?", "namespace": "open-p", "hash": -657419507, "terms": ["What is the equation for the NumOcc boost table function?", "is the equation for the NumOcc boost table function?", "the equation for the NumOcc boost table function?", "equation for the NumOcc boost table function?", "for the NumOcc boost table function?", "the NumOcc boost table function?", "NumOcc boost table function?", "boost table function?", "table function?", "function?"], "tokens": ["What", "is", "the", "equation", "for", "the", "NumOcc", "boost", "table", "function", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-306906808", "fields": {"term": "What is the tune parameter?", "namespace": "open-p", "hash": -306906808, "terms": ["What is the tune parameter?", "is the tune parameter?", "the tune parameter?", "tune parameter?", "parameter?"], "tokens": ["What", "is", "the", "tune", "parameter", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-576023224", "fields": {"term": "What is the scale parameter?", "namespace": "open-p", "hash": -576023224, "terms": ["What is the scale parameter?", "is the scale parameter?", "the scale parameter?", "scale parameter?", "parameter?"], "tokens": ["What", "is", "the", "scale", "parameter", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1539530386", "fields": {"term": "What do the NumOcc Plot weight and NumOcc Plot tune graphs show?", "namespace": "open-p", "hash": 1539530386, "terms": ["What do the NumOcc Plot weight and NumOcc Plot tune graphs show?", "do the NumOcc Plot weight and NumOcc Plot tune graphs show?", "the NumOcc Plot weight and NumOcc Plot tune graphs show?", "NumOcc Plot weight and NumOcc Plot tune graphs show?", "Plot weight and NumOcc Plot tune graphs show?", "weight and NumOcc Plot tune graphs show?", "and NumOcc Plot tune graphs show?", "NumOcc Plot tune graphs show?", "Plot tune graphs show?", "tune graphs show?", "graphs show?", "show?"], "tokens": ["What", "do", "the", "NumOcc", "Plot", "weight", "and", "NumOcc", "Plot", "tune", "graphs", "show", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1467983514", "fields": {"term": "What is pairwise proximity?", "namespace": "open-p", "hash": -1467983514, "terms": ["What is pairwise proximity?", "is pairwise proximity?", "pairwise proximity?", "proximity?"], "tokens": ["What", "is", "pairwise", "proximity", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::437970532", "fields": {"term": "How is proximity calculated?", "namespace": "open-p", "hash": 437970532, "terms": ["How is proximity calculated?", "is proximity calculated?", "proximity calculated?", "calculated?"], "tokens": ["How", "is", "proximity", "calculated", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::687094481", "fields": {"term": "What is the equation for proximity boost?", "namespace": "open-p", "hash": 687094481, "terms": ["What is the equation for proximity boost?", "is the equation for proximity boost?", "the equation for proximity boost?", "equation for proximity boost?", "for proximity boost?", "proximity boost?", "boost?"], "tokens": ["What", "is", "the", "equation", "for", "proximity", "boost", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1132924782", "fields": {"term": "What are proximity tables used for?", "namespace": "open-p", "hash": 1132924782, "terms": ["What are proximity tables used for?", "are proximity tables used for?", "proximity tables used for?", "tables used for?", "used for?", "for?"], "tokens": ["What", "are", "proximity", "tables", "used", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::55516112", "fields": {"term": "What is the difference between forward and reverse proximity?", "namespace": "open-p", "hash": 55516112, "terms": ["What is the difference between forward and reverse proximity?", "is the difference between forward and reverse proximity?", "the difference between forward and reverse proximity?", "difference between forward and reverse proximity?", "between forward and reverse proximity?", "forward and reverse proximity?", "and reverse proximity?", "reverse proximity?", "proximity?"], "tokens": ["What", "is", "the", "difference", "between", "forward", "and", "reverse", "proximity", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1117195820", "fields": {"term": "What are the parameters for the proximity boost equation?", "namespace": "open-p", "hash": -1117195820, "terms": ["What are the parameters for the proximity boost equation?", "are the parameters for the proximity boost equation?", "the parameters for the proximity boost equation?", "parameters for the proximity boost equation?", "for the proximity boost equation?", "the proximity boost equation?", "proximity boost equation?", "boost equation?", "equation?"], "tokens": ["What", "are", "the", "parameters", "for", "the", "proximity", "boost", "equation", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-331897852", "fields": {"term": "What is the effect of weight in the proximity boost equation?", "namespace": "open-p", "hash": -331897852, "terms": ["What is the effect of weight in the proximity boost equation?", "is the effect of weight in the proximity boost equation?", "the effect of weight in the proximity boost equation?", "effect of weight in the proximity boost equation?", "of weight in the proximity boost equation?", "weight in the proximity boost equation?", "in the proximity boost equation?", "the proximity boost equation?", "proximity boost equation?", "boost equation?", "equation?"], "tokens": ["What", "is", "the", "effect", "of", "weight", "in", "the", "proximity", "boost", "equation", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1511020724", "fields": {"term": "What is the effect of tune in the proximity boost equation?", "namespace": "open-p", "hash": -1511020724, "terms": ["What is the effect of tune in the proximity boost equation?", "is the effect of tune in the proximity boost equation?", "the effect of tune in the proximity boost equation?", "effect of tune in the proximity boost equation?", "of tune in the proximity boost equation?", "tune in the proximity boost equation?", "in the proximity boost equation?", "the proximity boost equation?", "proximity boost equation?", "boost equation?", "equation?"], "tokens": ["What", "is", "the", "effect", "of", "tune", "in", "the", "proximity", "boost", "equation", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1677393990", "fields": {"term": "What is the weightboost table?", "namespace": "open-p", "hash": -1677393990, "terms": ["What is the weightboost table?", "is the weightboost table?", "the weightboost table?", "weightboost table?", "table?"], "tokens": ["What", "is", "the", "weightboost", "table", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2005970183", "fields": {"term": "What does equation 4 show?", "namespace": "open-p", "hash": 2005970183, "terms": ["What does equation 4 show?", "does equation 4 show?", "equation 4 show?", "4 show?", "show?"], "tokens": ["What", "does", "equation", "4", "show", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1610103062", "fields": {"term": "How is weight(x) calculated?", "namespace": "open-p", "hash": -1610103062, "terms": ["How is weight(x) calculated?", "is weight(x) calculated?", "weight(x) calculated?", "calculated?"], "tokens": ["How", "is", "weight", "(", "x", ")", "calculated", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::94703977", "fields": {"term": "What is the input for the boost table?", "namespace": "open-p", "hash": 94703977, "terms": ["What is the input for the boost table?", "is the input for the boost table?", "the input for the boost table?", "input for the boost table?", "for the boost table?", "the boost table?", "boost table?", "table?"], "tokens": ["What", "is", "the", "input", "for", "the", "boost", "table", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1960783890", "fields": {"term": "Can attributes have negative rank contributions?", "namespace": "open-p", "hash": 1960783890, "terms": ["Can attributes have negative rank contributions?", "attributes have negative rank contributions?", "have negative rank contributions?", "negative rank contributions?", "rank contributions?", "contributions?"], "tokens": ["Can", "attributes", "have", "negative", "rank", "contributions", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::260624778", "fields": {"term": "How is x defined for weighted sets?", "namespace": "open-p", "hash": 260624778, "terms": ["How is x defined for weighted sets?", "is x defined for weighted sets?", "x defined for weighted sets?", "defined for weighted sets?", "for weighted sets?", "weighted sets?", "sets?"], "tokens": ["How", "is", "x", "defined", "for", "weighted", "sets", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::928479443", "fields": {"term": "How is x defined for arrays?", "namespace": "open-p", "hash": 928479443, "terms": ["How is x defined for arrays?", "is x defined for arrays?", "x defined for arrays?", "defined for arrays?", "for arrays?", "arrays?"], "tokens": ["How", "is", "x", "defined", "for", "arrays", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::956457249", "fields": {"term": "How is x defined for single values?", "namespace": "open-p", "hash": 956457249, "terms": ["How is x defined for single values?", "is x defined for single values?", "x defined for single values?", "defined for single values?", "for single values?", "single values?", "values?"], "tokens": ["How", "is", "x", "defined", "for", "single", "values", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-295239207", "fields": {"term": "What is described in this section?", "namespace": "open-p", "hash": -295239207, "terms": ["What is described in this section?", "is described in this section?", "described in this section?", "in this section?", "this section?", "section?"], "tokens": ["What", "is", "described", "in", "this", "section", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1702611543", "fields": {"term": "What are ranking types?", "namespace": "open-p", "hash": -1702611543, "terms": ["What are ranking types?", "are ranking types?", "ranking types?", "types?"], "tokens": ["What", "are", "ranking", "types", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1646959450", "fields": {"term": "What is FirstOcc in Vespa ranking?", "namespace": "open-p", "hash": 1646959450, "terms": ["What is FirstOcc in Vespa ranking?", "is FirstOcc in Vespa ranking?", "FirstOcc in Vespa ranking?", "in Vespa ranking?", "Vespa ranking?", "ranking?"], "tokens": ["What", "is", "FirstOcc", "in", "Vespa", "ranking", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::884103036", "fields": {"term": "How does nativeProximity.proximityTable work?", "namespace": "open-p", "hash": 884103036, "terms": ["How does nativeProximity.proximityTable work?", "does nativeProximity.proximityTable work?", "nativeProximity.proximityTable work?", "work?"], "tokens": ["How", "does", "nativeProximity", "proximityTable", "work", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::783472152", "fields": {"term": "What types of fields work well with identity term ranking?", "namespace": "open-p", "hash": 783472152, "terms": ["What types of fields work well with identity term ranking?", "types of fields work well with identity term ranking?", "of fields work well with identity term ranking?", "fields work well with identity term ranking?", "work well with identity term ranking?", "well with identity term ranking?", "with identity term ranking?", "identity term ranking?", "term ranking?", "ranking?"], "tokens": ["What", "types", "of", "fields", "work", "well", "with", "identity", "term", "ranking", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1186140435", "fields": {"term": "What is the default ranking type?", "namespace": "open-p", "hash": -1186140435, "terms": ["What is the default ranking type?", "is the default ranking type?", "the default ranking type?", "default ranking type?", "ranking type?", "type?"], "tokens": ["What", "is", "the", "default", "ranking", "type", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1945191702", "fields": {"term": "What is the NumOcc boost table?", "namespace": "open-p", "hash": -1945191702, "terms": ["What is the NumOcc boost table?", "is the NumOcc boost table?", "the NumOcc boost table?", "NumOcc boost table?", "boost table?", "table?"], "tokens": ["What", "is", "the", "NumOcc", "boost", "table", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1825514402", "fields": {"term": "What is the proximity boost?", "namespace": "open-p", "hash": -1825514402, "terms": ["What is the proximity boost?", "is the proximity boost?", "the proximity boost?", "proximity boost?", "boost?"], "tokens": ["What", "is", "the", "proximity", "boost", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1363461795", "fields": {"term": "What is the supported ranking type?", "namespace": "open-p", "hash": -1363461795, "terms": ["What is the supported ranking type?", "is the supported ranking type?", "the supported ranking type?", "supported ranking type?", "ranking type?", "type?"], "tokens": ["What", "is", "the", "supported", "ranking", "type", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-372963603", "fields": {"term": "What is the description field for?", "namespace": "open-p", "hash": -372963603, "terms": ["What is the description field for?", "is the description field for?", "the description field for?", "description field for?", "field for?", "for?"], "tokens": ["What", "is", "the", "description", "field", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1252661344", "fields": {"term": "What is the FirstOcc boost table?", "namespace": "open-p", "hash": -1252661344, "terms": ["What is the FirstOcc boost table?", "is the FirstOcc boost table?", "the FirstOcc boost table?", "FirstOcc boost table?", "boost table?", "table?"], "tokens": ["What", "is", "the", "FirstOcc", "boost", "table", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-556252921", "fields": {"term": "What are typical examples of a description field?", "namespace": "open-p", "hash": -556252921, "terms": ["What are typical examples of a description field?", "are typical examples of a description field?", "typical examples of a description field?", "examples of a description field?", "of a description field?", "a description field?", "description field?", "field?"], "tokens": ["What", "are", "typical", "examples", "of", "a", "description", "field", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1489858605", "fields": {"term": "What is the about-ness of description related to?", "namespace": "open-p", "hash": 1489858605, "terms": ["What is the about-ness of description related to?", "is the about-ness of description related to?", "the about-ness of description related to?", "about-ness of description related to?", "of description related to?", "description related to?", "related to?", "to?"], "tokens": ["What", "is", "the", "about", "ness", "of", "description", "related", "to", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1989499214", "fields": {"term": "What is the 'tags' rank type?", "namespace": "open-p", "hash": 1989499214, "terms": ["What is the 'tags' rank type?", "is the 'tags' rank type?", "the 'tags' rank type?", "'tags' rank type?", "rank type?", "type?"], "tokens": ["What", "is", "the", "'", "tags", "'", "rank", "type", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::841489891", "fields": {"term": "How does the 'tags' rank type work?", "namespace": "open-p", "hash": 841489891, "terms": ["How does the 'tags' rank type work?", "does the 'tags' rank type work?", "the 'tags' rank type work?", "'tags' rank type work?", "rank type work?", "type work?", "work?"], "tokens": ["How", "does", "the", "'", "tags", "'", "rank", "type", "work", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1257810325", "fields": {"term": "What are ranking expressions?", "namespace": "open-p", "hash": -1257810325, "terms": ["What are ranking expressions?", "are ranking expressions?", "ranking expressions?", "expressions?"], "tokens": ["What", "are", "ranking", "expressions", "?"], "corpus_count": 3, "document_count": 3}}, -{"put": "id:term:term::-1612246254", "fields": {"term": "Where to find guides for rank features?", "namespace": "open-p", "hash": -1612246254, "terms": ["Where to find guides for rank features?", "to find guides for rank features?", "find guides for rank features?", "guides for rank features?", "for rank features?", "rank features?", "features?"], "tokens": ["Where", "to", "find", "guides", "for", "rank", "features", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1350893453", "fields": {"term": "What operators do ranking expressions use?", "namespace": "open-p", "hash": -1350893453, "terms": ["What operators do ranking expressions use?", "operators do ranking expressions use?", "do ranking expressions use?", "ranking expressions use?", "expressions use?", "use?"], "tokens": ["What", "operators", "do", "ranking", "expressions", "use", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1613094381", "fields": {"term": "What is two-phase ranking?", "namespace": "open-p", "hash": 1613094381, "terms": ["What is two-phase ranking?", "is two-phase ranking?", "two-phase ranking?", "ranking?"], "tokens": ["What", "is", "two", "phase", "ranking", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-226731242", "fields": {"term": "How can ranking expressions be created?", "namespace": "open-p", "hash": -226731242, "terms": ["How can ranking expressions be created?", "can ranking expressions be created?", "ranking expressions be created?", "expressions be created?", "be created?", "created?"], "tokens": ["How", "can", "ranking", "expressions", "be", "created", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::114290517", "fields": {"term": "What is the resource driver in ranking?", "namespace": "open-p", "hash": 114290517, "terms": ["What is the resource driver in ranking?", "is the resource driver in ranking?", "the resource driver in ranking?", "resource driver in ranking?", "driver in ranking?", "in ranking?", "ranking?"], "tokens": ["What", "is", "the", "resource", "driver", "in", "ranking", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1588453592", "fields": {"term": "What is the purpose of two-phase ranking?", "namespace": "open-p", "hash": -1588453592, "terms": ["What is the purpose of two-phase ranking?", "is the purpose of two-phase ranking?", "the purpose of two-phase ranking?", "purpose of two-phase ranking?", "of two-phase ranking?", "two-phase ranking?", "ranking?"], "tokens": ["What", "is", "the", "purpose", "of", "two", "phase", "ranking", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1127184464", "fields": {"term": "What is the default rank feature?", "namespace": "open-p", "hash": -1127184464, "terms": ["What is the default rank feature?", "is the default rank feature?", "the default rank feature?", "default rank feature?", "rank feature?", "feature?"], "tokens": ["What", "is", "the", "default", "rank", "feature", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::472973087", "fields": {"term": "How are rank features computed?", "namespace": "open-p", "hash": 472973087, "terms": ["How are rank features computed?", "are rank features computed?", "rank features computed?", "features computed?", "computed?"], "tokens": ["How", "are", "rank", "features", "computed", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::625871871", "fields": {"term": "What is the format of defining rank features?", "namespace": "open-p", "hash": 625871871, "terms": ["What is the format of defining rank features?", "is the format of defining rank features?", "the format of defining rank features?", "format of defining rank features?", "of defining rank features?", "defining rank features?", "rank features?", "features?"], "tokens": ["What", "is", "the", "format", "of", "defining", "rank", "features", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-699332491", "fields": {"term": "What is ignore-default-rank-features?", "namespace": "open-p", "hash": -699332491, "terms": ["What is ignore-default-rank-features?", "is ignore-default-rank-features?", "ignore-default-rank-features?"], "tokens": ["What", "is", "ignore", "default", "rank", "features", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::57450689", "fields": {"term": "How can rank features be manually added?", "namespace": "open-p", "hash": 57450689, "terms": ["How can rank features be manually added?", "can rank features be manually added?", "rank features be manually added?", "features be manually added?", "be manually added?", "manually added?", "added?"], "tokens": ["How", "can", "rank", "features", "be", "manually", "added", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::579878370", "fields": {"term": "What is the command for including default rank features?", "namespace": "open-p", "hash": 579878370, "terms": ["What is the command for including default rank features?", "is the command for including default rank features?", "the command for including default rank features?", "command for including default rank features?", "for including default rank features?", "including default rank features?", "default rank features?", "rank features?", "features?"], "tokens": ["What", "is", "the", "command", "for", "including", "default", "rank", "features", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::198455936", "fields": {"term": "What is the use of recording rank feature values?", "namespace": "open-p", "hash": 198455936, "terms": ["What is the use of recording rank feature values?", "is the use of recording rank feature values?", "the use of recording rank feature values?", "use of recording rank feature values?", "of recording rank feature values?", "recording rank feature values?", "rank feature values?", "feature values?", "values?"], "tokens": ["What", "is", "the", "use", "of", "recording", "rank", "feature", "values", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1801112913", "fields": {"term": "Why prefer normalized features?", "namespace": "open-p", "hash": 1801112913, "terms": ["Why prefer normalized features?", "prefer normalized features?", "normalized features?", "features?"], "tokens": ["Why", "prefer", "normalized", "features", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2002703993", "fields": {"term": "How can normalized rank scores be implemented?", "namespace": "open-p", "hash": -2002703993, "terms": ["How can normalized rank scores be implemented?", "can normalized rank scores be implemented?", "normalized rank scores be implemented?", "rank scores be implemented?", "scores be implemented?", "be implemented?", "implemented?"], "tokens": ["How", "can", "normalized", "rank", "scores", "be", "implemented", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-108314155", "fields": {"term": "What are un-normalized features?", "namespace": "open-p", "hash": -108314155, "terms": ["What are un-normalized features?", "are un-normalized features?", "un-normalized features?", "features?"], "tokens": ["What", "are", "un", "normalized", "features", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1241877731", "fields": {"term": "What is relevance based blending?", "namespace": "open-p", "hash": -1241877731, "terms": ["What is relevance based blending?", "is relevance based blending?", "relevance based blending?", "based blending?", "blending?"], "tokens": ["What", "is", "relevance", "based", "blending", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::907277982", "fields": {"term": "What can be triggered if there are no good hits?", "namespace": "open-p", "hash": 907277982, "terms": ["What can be triggered if there are no good hits?", "can be triggered if there are no good hits?", "be triggered if there are no good hits?", "triggered if there are no good hits?", "if there are no good hits?", "there are no good hits?", "are no good hits?", "no good hits?", "good hits?", "hits?"], "tokens": ["What", "can", "be", "triggered", "if", "there", "are", "no", "good", "hits", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1848395818", "fields": {"term": "What is fieldMatch feature?", "namespace": "open-p", "hash": 1848395818, "terms": ["What is fieldMatch feature?", "is fieldMatch feature?", "fieldMatch feature?", "feature?"], "tokens": ["What", "is", "fieldMatch", "feature", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::-9693297", "fields": {"term": "How are feature configurations set?", "namespace": "open-p", "hash": -9693297, "terms": ["How are feature configurations set?", "are feature configurations set?", "feature configurations set?", "configurations set?", "set?"], "tokens": ["How", "are", "feature", "configurations", "set", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1260235454", "fields": {"term": "What are rank-properties?", "namespace": "open-p", "hash": 1260235454, "terms": ["What are rank-properties?", "are rank-properties?", "rank-properties?"], "tokens": ["What", "are", "rank", "properties", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::563922991", "fields": {"term": "What is maxAlternativeSegmentations?", "namespace": "open-p", "hash": 563922991, "terms": ["What is maxAlternativeSegmentations?", "is maxAlternativeSegmentations?", "maxAlternativeSegmentations?"], "tokens": ["What", "is", "maxAlternativeSegmentations", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::-826810967", "fields": {"term": "What is maxOccurrences?", "namespace": "open-p", "hash": -826810967, "terms": ["What is maxOccurrences?", "is maxOccurrences?", "maxOccurrences?"], "tokens": ["What", "is", "maxOccurrences", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::-1678246683", "fields": {"term": "Where can I find rank feature configuration reference?", "namespace": "open-p", "hash": -1678246683, "terms": ["Where can I find rank feature configuration reference?", "can I find rank feature configuration reference?", "I find rank feature configuration reference?", "find rank feature configuration reference?", "rank feature configuration reference?", "feature configuration reference?", "configuration reference?", "reference?"], "tokens": ["Where", "can", "I", "find", "rank", "feature", "configuration", "reference", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-909593521", "fields": {"term": "What are the benefit of feature configuration tuning?", "namespace": "open-p", "hash": -909593521, "terms": ["What are the benefit of feature configuration tuning?", "are the benefit of feature configuration tuning?", "the benefit of feature configuration tuning?", "benefit of feature configuration tuning?", "of feature configuration tuning?", "feature configuration tuning?", "configuration tuning?", "tuning?"], "tokens": ["What", "are", "the", "benefit", "of", "feature", "configuration", "tuning", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::945811350", "fields": {"term": "What is the earliness feature?", "namespace": "open-p", "hash": 945811350, "terms": ["What is the earliness feature?", "is the earliness feature?", "the earliness feature?", "earliness feature?", "feature?"], "tokens": ["What", "is", "the", "earliness", "feature", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1205212980", "fields": {"term": "How are Vespa ranking features linear?", "namespace": "open-p", "hash": 1205212980, "terms": ["How are Vespa ranking features linear?", "are Vespa ranking features linear?", "Vespa ranking features linear?", "ranking features linear?", "features linear?", "linear?"], "tokens": ["How", "are", "Vespa", "ranking", "features", "linear", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1424864856", "fields": {"term": "What is the purpose of adding a specific term to a query?", "namespace": "open-p", "hash": -1424864856, "terms": ["What is the purpose of adding a specific term to a query?", "is the purpose of adding a specific term to a query?", "the purpose of adding a specific term to a query?", "purpose of adding a specific term to a query?", "of adding a specific term to a query?", "adding a specific term to a query?", "a specific term to a query?", "specific term to a query?", "term to a query?", "to a query?", "a query?", "query?"], "tokens": ["What", "is", "the", "purpose", "of", "adding", "a", "specific", "term", "to", "a", "query", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1552608991", "fields": {"term": "What does the 'ranked' parameter do?", "namespace": "open-p", "hash": 1552608991, "terms": ["What does the 'ranked' parameter do?", "does the 'ranked' parameter do?", "the 'ranked' parameter do?", "'ranked' parameter do?", "parameter do?", "do?"], "tokens": ["What", "does", "the", "'", "ranked", "'", "parameter", "do", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1631229412", "fields": {"term": "How can you prevent a term from impacting rank features?", "namespace": "open-p", "hash": -1631229412, "terms": ["How can you prevent a term from impacting rank features?", "can you prevent a term from impacting rank features?", "you prevent a term from impacting rank features?", "prevent a term from impacting rank features?", "a term from impacting rank features?", "term from impacting rank features?", "from impacting rank features?", "impacting rank features?", "rank features?", "features?"], "tokens": ["How", "can", "you", "prevent", "a", "term", "from", "impacting", "rank", "features", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-179523227", "fields": {"term": "Can you search for a document by its ID without affecting rank features?", "namespace": "open-p", "hash": -179523227, "terms": ["Can you search for a document by its ID without affecting rank features?", "you search for a document by its ID without affecting rank features?", "search for a document by its ID without affecting rank features?", "for a document by its ID without affecting rank features?", "a document by its ID without affecting rank features?", "document by its ID without affecting rank features?", "by its ID without affecting rank features?", "its ID without affecting rank features?", "ID without affecting rank features?", "without affecting rank features?", "affecting rank features?", "rank features?", "features?"], "tokens": ["Can", "you", "search", "for", "a", "document", "by", "its", "ID", "without", "affecting", "rank", "features", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1498870840", "fields": {"term": "How to add summary features?", "namespace": "open-p", "hash": 1498870840, "terms": ["How to add summary features?", "to add summary features?", "add summary features?", "summary features?", "features?"], "tokens": ["How", "to", "add", "summary", "features", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1001581631", "fields": {"term": "Why use match-features?", "namespace": "open-p", "hash": -1001581631, "terms": ["Why use match-features?", "use match-features?", "match-features?"], "tokens": ["Why", "use", "match", "features", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1213613469", "fields": {"term": "What is tensor_join?", "namespace": "open-p", "hash": -1213613469, "terms": ["What is tensor_join?", "is tensor_join?", "tensor_join?"], "tokens": ["What", "is", "tensor_join", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1388581676", "fields": {"term": "What data type does featureData return?", "namespace": "open-p", "hash": -1388581676, "terms": ["What data type does featureData return?", "data type does featureData return?", "type does featureData return?", "does featureData return?", "featureData return?", "return?"], "tokens": ["What", "data", "type", "does", "featureData", "return", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1737981364", "fields": {"term": "What is the difference between match-features and summary-features?", "namespace": "open-p", "hash": -1737981364, "terms": ["What is the difference between match-features and summary-features?", "is the difference between match-features and summary-features?", "the difference between match-features and summary-features?", "difference between match-features and summary-features?", "between match-features and summary-features?", "match-features and summary-features?", "and summary-features?", "summary-features?"], "tokens": ["What", "is", "the", "difference", "between", "match", "features", "and", "summary", "features", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1251007130", "fields": {"term": "What is the computation in join_sum()?", "namespace": "open-p", "hash": -1251007130, "terms": ["What is the computation in join_sum()?", "is the computation in join_sum()?", "the computation in join_sum()?", "computation in join_sum()?", "in join_sum()?", "join_sum()?"], "tokens": ["What", "is", "the", "computation", "in", "join_sum", "()?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1079228096", "fields": {"term": "What is the 'if' function used for in Vespa?", "namespace": "open-p", "hash": -1079228096, "terms": ["What is the 'if' function used for in Vespa?", "is the 'if' function used for in Vespa?", "the 'if' function used for in Vespa?", "'if' function used for in Vespa?", "function used for in Vespa?", "used for in Vespa?", "for in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["What", "is", "the", "'", "if", "'", "function", "used", "for", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-484914077", "fields": {"term": "How can 'if' functions be used for ranking in Vespa?", "namespace": "open-p", "hash": -484914077, "terms": ["How can 'if' functions be used for ranking in Vespa?", "can 'if' functions be used for ranking in Vespa?", "'if' functions be used for ranking in Vespa?", "functions be used for ranking in Vespa?", "be used for ranking in Vespa?", "used for ranking in Vespa?", "for ranking in Vespa?", "ranking in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["How", "can", "'", "if", "'", "functions", "be", "used", "for", "ranking", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1133473422", "fields": {"term": "What is string equality in ranking expressions?", "namespace": "open-p", "hash": 1133473422, "terms": ["What is string equality in ranking expressions?", "is string equality in ranking expressions?", "string equality in ranking expressions?", "equality in ranking expressions?", "in ranking expressions?", "ranking expressions?", "expressions?"], "tokens": ["What", "is", "string", "equality", "in", "ranking", "expressions", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1461851885", "fields": {"term": "What is an example of using 'if' for ranking different types of documents?", "namespace": "open-p", "hash": -1461851885, "terms": ["What is an example of using 'if' for ranking different types of documents?", "is an example of using 'if' for ranking different types of documents?", "an example of using 'if' for ranking different types of documents?", "example of using 'if' for ranking different types of documents?", "of using 'if' for ranking different types of documents?", "using 'if' for ranking different types of documents?", "'if' for ranking different types of documents?", "for ranking different types of documents?", "ranking different types of documents?", "different types of documents?", "types of documents?", "of documents?", "documents?"], "tokens": ["What", "is", "an", "example", "of", "using", "'", "if", "'", "for", "ranking", "different", "types", "of", "documents", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1439014515", "fields": {"term": "How is strict tiering implemented using 'if' functions?", "namespace": "open-p", "hash": 1439014515, "terms": ["How is strict tiering implemented using 'if' functions?", "is strict tiering implemented using 'if' functions?", "strict tiering implemented using 'if' functions?", "tiering implemented using 'if' functions?", "implemented using 'if' functions?", "using 'if' functions?", "'if' functions?", "functions?"], "tokens": ["How", "is", "strict", "tiering", "implemented", "using", "'", "if", "'", "functions", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1988247407", "fields": {"term": "Can you give an example of sorting documents by multiple criteria?", "namespace": "open-p", "hash": -1988247407, "terms": ["Can you give an example of sorting documents by multiple criteria?", "you give an example of sorting documents by multiple criteria?", "give an example of sorting documents by multiple criteria?", "an example of sorting documents by multiple criteria?", "example of sorting documents by multiple criteria?", "of sorting documents by multiple criteria?", "sorting documents by multiple criteria?", "documents by multiple criteria?", "by multiple criteria?", "multiple criteria?", "criteria?"], "tokens": ["Can", "you", "give", "an", "example", "of", "sorting", "documents", "by", "multiple", "criteria", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-730040708", "fields": {"term": "What is the benefit of using 'if' functions for ranking in Vespa?", "namespace": "open-p", "hash": -730040708, "terms": ["What is the benefit of using 'if' functions for ranking in Vespa?", "is the benefit of using 'if' functions for ranking in Vespa?", "the benefit of using 'if' functions for ranking in Vespa?", "benefit of using 'if' functions for ranking in Vespa?", "of using 'if' functions for ranking in Vespa?", "using 'if' functions for ranking in Vespa?", "'if' functions for ranking in Vespa?", "functions for ranking in Vespa?", "for ranking in Vespa?", "ranking in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["What", "is", "the", "benefit", "of", "using", "'", "if", "'", "functions", "for", "ranking", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1634852765", "fields": {"term": "How do multiple schemas contribute to the use of 'if' functions in ranking?", "namespace": "open-p", "hash": -1634852765, "terms": ["How do multiple schemas contribute to the use of 'if' functions in ranking?", "do multiple schemas contribute to the use of 'if' functions in ranking?", "multiple schemas contribute to the use of 'if' functions in ranking?", "schemas contribute to the use of 'if' functions in ranking?", "contribute to the use of 'if' functions in ranking?", "to the use of 'if' functions in ranking?", "the use of 'if' functions in ranking?", "use of 'if' functions in ranking?", "of 'if' functions in ranking?", "'if' functions in ranking?", "functions in ranking?", "in ranking?", "ranking?"], "tokens": ["How", "do", "multiple", "schemas", "contribute", "to", "the", "use", "of", "'", "if", "'", "functions", "in", "ranking", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-481365875", "fields": {"term": "What are ranking expressions referencing?", "namespace": "open-p", "hash": -481365875, "terms": ["What are ranking expressions referencing?", "are ranking expressions referencing?", "ranking expressions referencing?", "expressions referencing?", "referencing?"], "tokens": ["What", "are", "ranking", "expressions", "referencing", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1272782625", "fields": {"term": "What is a constants clause?", "namespace": "open-p", "hash": 1272782625, "terms": ["What is a constants clause?", "is a constants clause?", "a constants clause?", "constants clause?", "clause?"], "tokens": ["What", "is", "a", "constants", "clause", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1240097461", "fields": {"term": "How are constants useful in rank profiles?", "namespace": "open-p", "hash": 1240097461, "terms": ["How are constants useful in rank profiles?", "are constants useful in rank profiles?", "constants useful in rank profiles?", "useful in rank profiles?", "in rank profiles?", "rank profiles?", "profiles?"], "tokens": ["How", "are", "constants", "useful", "in", "rank", "profiles", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-673693411", "fields": {"term": "When should query variables be used instead of constants?", "namespace": "open-p", "hash": -673693411, "terms": ["When should query variables be used instead of constants?", "should query variables be used instead of constants?", "query variables be used instead of constants?", "variables be used instead of constants?", "be used instead of constants?", "used instead of constants?", "instead of constants?", "of constants?", "constants?"], "tokens": ["When", "should", "query", "variables", "be", "used", "instead", "of", "constants", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-507941359", "fields": {"term": "What happens to constants during configuration time?", "namespace": "open-p", "hash": -507941359, "terms": ["What happens to constants during configuration time?", "happens to constants during configuration time?", "to constants during configuration time?", "constants during configuration time?", "during configuration time?", "configuration time?", "time?"], "tokens": ["What", "happens", "to", "constants", "during", "configuration", "time", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1109434049", "fields": {"term": "Can constants be overridden in sub-profiles?", "namespace": "open-p", "hash": -1109434049, "terms": ["Can constants be overridden in sub-profiles?", "constants be overridden in sub-profiles?", "be overridden in sub-profiles?", "overridden in sub-profiles?", "in sub-profiles?", "sub-profiles?"], "tokens": ["Can", "constants", "be", "overridden", "in", "sub", "profiles", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2146296499", "fields": {"term": "What is the benefit of using constants in ranking expressions?", "namespace": "open-p", "hash": -2146296499, "terms": ["What is the benefit of using constants in ranking expressions?", "is the benefit of using constants in ranking expressions?", "the benefit of using constants in ranking expressions?", "benefit of using constants in ranking expressions?", "of using constants in ranking expressions?", "using constants in ranking expressions?", "constants in ranking expressions?", "in ranking expressions?", "ranking expressions?", "expressions?"], "tokens": ["What", "is", "the", "benefit", "of", "using", "constants", "in", "ranking", "expressions", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1173760854", "fields": {"term": "What is the performance impact of using constants instead of query variables?", "namespace": "open-p", "hash": -1173760854, "terms": ["What is the performance impact of using constants instead of query variables?", "is the performance impact of using constants instead of query variables?", "the performance impact of using constants instead of query variables?", "performance impact of using constants instead of query variables?", "impact of using constants instead of query variables?", "of using constants instead of query variables?", "using constants instead of query variables?", "constants instead of query variables?", "instead of query variables?", "of query variables?", "query variables?", "variables?"], "tokens": ["What", "is", "the", "performance", "impact", "of", "using", "constants", "instead", "of", "query", "variables", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1393567578", "fields": {"term": "What are query features?", "namespace": "open-p", "hash": -1393567578, "terms": ["What are query features?", "are query features?", "query features?", "features?"], "tokens": ["What", "are", "query", "features", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::117184951", "fields": {"term": "What are ranking variables?", "namespace": "open-p", "hash": 117184951, "terms": ["What are ranking variables?", "are ranking variables?", "ranking variables?", "variables?"], "tokens": ["What", "are", "ranking", "variables", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1896671892", "fields": {"term": "How can query specify importance?", "namespace": "open-p", "hash": 1896671892, "terms": ["How can query specify importance?", "can query specify importance?", "query specify importance?", "specify importance?", "importance?"], "tokens": ["How", "can", "query", "specify", "importance", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1294487752", "fields": {"term": "How to override ranking variables?", "namespace": "open-p", "hash": 1294487752, "terms": ["How to override ranking variables?", "to override ranking variables?", "override ranking variables?", "ranking variables?", "variables?"], "tokens": ["How", "to", "override", "ranking", "variables", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1397978955", "fields": {"term": "What is the default feature type?", "namespace": "open-p", "hash": -1397978955, "terms": ["What is the default feature type?", "is the default feature type?", "the default feature type?", "default feature type?", "feature type?", "type?"], "tokens": ["What", "is", "the", "default", "feature", "type", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::249778356", "fields": {"term": "How to define feature type?", "namespace": "open-p", "hash": 249778356, "terms": ["How to define feature type?", "to define feature type?", "define feature type?", "feature type?", "type?"], "tokens": ["How", "to", "define", "feature", "type", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1344188312", "fields": {"term": "What produces incorrect results?", "namespace": "open-p", "hash": 1344188312, "terms": ["What produces incorrect results?", "produces incorrect results?", "incorrect results?", "results?"], "tokens": ["What", "produces", "incorrect", "results", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::640158168", "fields": {"term": "What is a RankFeatures instance?", "namespace": "open-p", "hash": 640158168, "terms": ["What is a RankFeatures instance?", "is a RankFeatures instance?", "a RankFeatures instance?", "RankFeatures instance?", "instance?"], "tokens": ["What", "is", "a", "RankFeatures", "instance", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::313600569", "fields": {"term": "How to construct tensors programmatically?", "namespace": "open-p", "hash": 313600569, "terms": ["How to construct tensors programmatically?", "to construct tensors programmatically?", "construct tensors programmatically?", "tensors programmatically?", "programmatically?"], "tokens": ["How", "to", "construct", "tensors", "programmatically", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1375026578", "fields": {"term": "What is machine learned ranking?", "namespace": "open-p", "hash": 1375026578, "terms": ["What is machine learned ranking?", "is machine learned ranking?", "machine learned ranking?", "learned ranking?", "ranking?"], "tokens": ["What", "is", "machine", "learned", "ranking", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1200585375", "fields": {"term": "What is function space in MLR?", "namespace": "open-p", "hash": 1200585375, "terms": ["What is function space in MLR?", "is function space in MLR?", "function space in MLR?", "space in MLR?", "in MLR?", "MLR?"], "tokens": ["What", "is", "function", "space", "in", "MLR", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1592601551", "fields": {"term": "How do we increase the space of functions in MLR?", "namespace": "open-p", "hash": 1592601551, "terms": ["How do we increase the space of functions in MLR?", "do we increase the space of functions in MLR?", "we increase the space of functions in MLR?", "increase the space of functions in MLR?", "the space of functions in MLR?", "space of functions in MLR?", "of functions in MLR?", "functions in MLR?", "in MLR?", "MLR?"], "tokens": ["How", "do", "we", "increase", "the", "space", "of", "functions", "in", "MLR", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::547146873", "fields": {"term": "What are primitive features in Vespa?", "namespace": "open-p", "hash": 547146873, "terms": ["What are primitive features in Vespa?", "are primitive features in Vespa?", "primitive features in Vespa?", "features in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["What", "are", "primitive", "features", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1808972439", "fields": {"term": "How can primitive features be used in MLR?", "namespace": "open-p", "hash": 1808972439, "terms": ["How can primitive features be used in MLR?", "can primitive features be used in MLR?", "primitive features be used in MLR?", "features be used in MLR?", "be used in MLR?", "used in MLR?", "in MLR?", "MLR?"], "tokens": ["How", "can", "primitive", "features", "be", "used", "in", "MLR", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1840691109", "fields": {"term": "What are expression functions in Vespa?", "namespace": "open-p", "hash": -1840691109, "terms": ["What are expression functions in Vespa?", "are expression functions in Vespa?", "expression functions in Vespa?", "functions in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["What", "are", "expression", "functions", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-210923515", "fields": {"term": "How can functions with zero arguments be used in ranking?", "namespace": "open-p", "hash": -210923515, "terms": ["How can functions with zero arguments be used in ranking?", "can functions with zero arguments be used in ranking?", "functions with zero arguments be used in ranking?", "with zero arguments be used in ranking?", "zero arguments be used in ranking?", "arguments be used in ranking?", "be used in ranking?", "used in ranking?", "in ranking?", "ranking?"], "tokens": ["How", "can", "functions", "with", "zero", "arguments", "be", "used", "in", "ranking", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::317718353", "fields": {"term": "How do we define summary-features in Vespa?", "namespace": "open-p", "hash": 317718353, "terms": ["How do we define summary-features in Vespa?", "do we define summary-features in Vespa?", "we define summary-features in Vespa?", "define summary-features in Vespa?", "summary-features in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["How", "do", "we", "define", "summary", "features", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1855094700", "fields": {"term": "What are Vespa relevance metrics?", "namespace": "open-p", "hash": -1855094700, "terms": ["What are Vespa relevance metrics?", "are Vespa relevance metrics?", "Vespa relevance metrics?", "relevance metrics?", "metrics?"], "tokens": ["What", "are", "Vespa", "relevance", "metrics", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-818414180", "fields": {"term": "What are the relevance metrics called?", "namespace": "open-p", "hash": -818414180, "terms": ["What are the relevance metrics called?", "are the relevance metrics called?", "the relevance metrics called?", "relevance metrics called?", "metrics called?", "called?"], "tokens": ["What", "are", "the", "relevance", "metrics", "called", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::895520048", "fields": {"term": "Where can I find more information about Vespa relevance metrics?", "namespace": "open-p", "hash": 895520048, "terms": ["Where can I find more information about Vespa relevance metrics?", "can I find more information about Vespa relevance metrics?", "I find more information about Vespa relevance metrics?", "find more information about Vespa relevance metrics?", "more information about Vespa relevance metrics?", "information about Vespa relevance metrics?", "about Vespa relevance metrics?", "Vespa relevance metrics?", "relevance metrics?", "metrics?"], "tokens": ["Where", "can", "I", "find", "more", "information", "about", "Vespa", "relevance", "metrics", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1254743698", "fields": {"term": "How can Vespa's relevance metrics change?", "namespace": "open-p", "hash": -1254743698, "terms": ["How can Vespa's relevance metrics change?", "can Vespa's relevance metrics change?", "Vespa's relevance metrics change?", "relevance metrics change?", "metrics change?", "change?"], "tokens": ["How", "can", "Vespa", "'", "s", "relevance", "metrics", "change", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-361533244", "fields": {"term": "What do Vespa's relevance metrics measure?", "namespace": "open-p", "hash": -361533244, "terms": ["What do Vespa's relevance metrics measure?", "do Vespa's relevance metrics measure?", "Vespa's relevance metrics measure?", "relevance metrics measure?", "metrics measure?", "measure?"], "tokens": ["What", "do", "Vespa", "'", "s", "relevance", "metrics", "measure", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1883383661", "fields": {"term": "What is the formula to assign adult content?", "namespace": "open-p", "hash": -1883383661, "terms": ["What is the formula to assign adult content?", "is the formula to assign adult content?", "the formula to assign adult content?", "formula to assign adult content?", "to assign adult content?", "assign adult content?", "adult content?", "content?"], "tokens": ["What", "is", "the", "formula", "to", "assign", "adult", "content", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1844893581", "fields": {"term": "What is the match quality formula?", "namespace": "open-p", "hash": 1844893581, "terms": ["What is the match quality formula?", "is the match quality formula?", "the match quality formula?", "match quality formula?", "quality formula?", "formula?"], "tokens": ["What", "is", "the", "match", "quality", "formula", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1406898977", "fields": {"term": "What are constants in ranking expressions?", "namespace": "open-p", "hash": 1406898977, "terms": ["What are constants in ranking expressions?", "are constants in ranking expressions?", "constants in ranking expressions?", "in ranking expressions?", "ranking expressions?", "expressions?"], "tokens": ["What", "are", "constants", "in", "ranking", "expressions", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1928502907", "fields": {"term": "What can be combined in ranking expressions?", "namespace": "open-p", "hash": -1928502907, "terms": ["What can be combined in ranking expressions?", "can be combined in ranking expressions?", "be combined in ranking expressions?", "combined in ranking expressions?", "in ranking expressions?", "ranking expressions?", "expressions?"], "tokens": ["What", "can", "be", "combined", "in", "ranking", "expressions", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::32719620", "fields": {"term": "What is the language used in ranking expressions?", "namespace": "open-p", "hash": 32719620, "terms": ["What is the language used in ranking expressions?", "is the language used in ranking expressions?", "the language used in ranking expressions?", "language used in ranking expressions?", "used in ranking expressions?", "in ranking expressions?", "ranking expressions?", "expressions?"], "tokens": ["What", "is", "the", "language", "used", "in", "ranking", "expressions", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-451042011", "fields": {"term": "How are strings used in ranking expressions?", "namespace": "open-p", "hash": -451042011, "terms": ["How are strings used in ranking expressions?", "are strings used in ranking expressions?", "strings used in ranking expressions?", "used in ranking expressions?", "in ranking expressions?", "ranking expressions?", "expressions?"], "tokens": ["How", "are", "strings", "used", "in", "ranking", "expressions", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-841493581", "fields": {"term": "What is the ranking overview?", "namespace": "open-p", "hash": -841493581, "terms": ["What is the ranking overview?", "is the ranking overview?", "the ranking overview?", "ranking overview?", "overview?"], "tokens": ["What", "is", "the", "ranking", "overview", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::277082945", "fields": {"term": "What is in-fix notation?", "namespace": "open-p", "hash": 277082945, "terms": ["What is in-fix notation?", "is in-fix notation?", "in-fix notation?", "notation?"], "tokens": ["What", "is", "in", "fix", "notation", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1679128068", "fields": {"term": "What are arithmetic operators?", "namespace": "open-p", "hash": 1679128068, "terms": ["What are arithmetic operators?", "are arithmetic operators?", "arithmetic operators?", "operators?"], "tokens": ["What", "are", "arithmetic", "operators", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1464304713", "fields": {"term": "What is the function acos?", "namespace": "open-p", "hash": -1464304713, "terms": ["What is the function acos?", "is the function acos?", "the function acos?", "function acos?", "acos?"], "tokens": ["What", "is", "the", "function", "acos", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::693358301", "fields": {"term": "What is the function asin?", "namespace": "open-p", "hash": 693358301, "terms": ["What is the function asin?", "is the function asin?", "the function asin?", "function asin?", "asin?"], "tokens": ["What", "is", "the", "function", "asin", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1758502449", "fields": {"term": "What is the function atan?", "namespace": "open-p", "hash": -1758502449, "terms": ["What is the function atan?", "is the function atan?", "the function atan?", "function atan?", "atan?"], "tokens": ["What", "is", "the", "function", "atan", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2093077121", "fields": {"term": "What is the function atan2?", "namespace": "open-p", "hash": -2093077121, "terms": ["What is the function atan2?", "is the function atan2?", "the function atan2?", "function atan2?", "atan2?"], "tokens": ["What", "is", "the", "function", "atan2", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1552836856", "fields": {"term": "What is the function ceil?", "namespace": "open-p", "hash": 1552836856, "terms": ["What is the function ceil?", "is the function ceil?", "the function ceil?", "function ceil?", "ceil?"], "tokens": ["What", "is", "the", "function", "ceil", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1920633064", "fields": {"term": "What is the function cos?", "namespace": "open-p", "hash": 1920633064, "terms": ["What is the function cos?", "is the function cos?", "the function cos?", "function cos?", "cos?"], "tokens": ["What", "is", "the", "function", "cos", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1613536183", "fields": {"term": "What is the function cosh?", "namespace": "open-p", "hash": -1613536183, "terms": ["What is the function cosh?", "is the function cosh?", "the function cosh?", "function cosh?", "cosh?"], "tokens": ["What", "is", "the", "function", "cosh", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1855321072", "fields": {"term": "What is the function elu?", "namespace": "open-p", "hash": -1855321072, "terms": ["What is the function elu?", "is the function elu?", "the function elu?", "function elu?", "elu?"], "tokens": ["What", "is", "the", "function", "elu", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-965031414", "fields": {"term": "What is the 'if' function?", "namespace": "open-p", "hash": -965031414, "terms": ["What is the 'if' function?", "is the 'if' function?", "the 'if' function?", "'if' function?", "function?"], "tokens": ["What", "is", "the", "'", "if", "'", "function", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::710173935", "fields": {"term": "How does the 'if' function work?", "namespace": "open-p", "hash": 710173935, "terms": ["How does the 'if' function work?", "does the 'if' function work?", "the 'if' function work?", "'if' function work?", "function work?", "work?"], "tokens": ["How", "does", "the", "'", "if", "'", "function", "work", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1383153805", "fields": {"term": "What are the conditional operators in if functions?", "namespace": "open-p", "hash": 1383153805, "terms": ["What are the conditional operators in if functions?", "are the conditional operators in if functions?", "the conditional operators in if functions?", "conditional operators in if functions?", "operators in if functions?", "in if functions?", "if functions?", "functions?"], "tokens": ["What", "are", "the", "conditional", "operators", "in", "if", "functions", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2082894301", "fields": {"term": "What is the membership operator in if functions?", "namespace": "open-p", "hash": 2082894301, "terms": ["What is the membership operator in if functions?", "is the membership operator in if functions?", "the membership operator in if functions?", "membership operator in if functions?", "operator in if functions?", "in if functions?", "if functions?", "functions?"], "tokens": ["What", "is", "the", "membership", "operator", "in", "if", "functions", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1622178628", "fields": {"term": "What is the foreach function?", "namespace": "open-p", "hash": 1622178628, "terms": ["What is the foreach function?", "is the foreach function?", "the foreach function?", "foreach function?", "function?"], "tokens": ["What", "is", "the", "foreach", "function", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-335701655", "fields": {"term": "How is foreach implemented?", "namespace": "open-p", "hash": -335701655, "terms": ["How is foreach implemented?", "is foreach implemented?", "foreach implemented?", "implemented?"], "tokens": ["How", "is", "foreach", "implemented", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2097430223", "fields": {"term": "What are tensor functions?", "namespace": "open-p", "hash": -2097430223, "terms": ["What are tensor functions?", "are tensor functions?", "tensor functions?", "functions?"], "tokens": ["What", "are", "tensor", "functions", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::-843227219", "fields": {"term": "What is a primitive function?", "namespace": "open-p", "hash": -843227219, "terms": ["What is a primitive function?", "is a primitive function?", "a primitive function?", "primitive function?", "function?"], "tokens": ["What", "is", "a", "primitive", "function", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1427589993", "fields": {"term": "What are convenience functions?", "namespace": "open-p", "hash": 1427589993, "terms": ["What are convenience functions?", "are convenience functions?", "convenience functions?", "functions?"], "tokens": ["What", "are", "convenience", "functions", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1553505696", "fields": {"term": "How are the functions grouped?", "namespace": "open-p", "hash": -1553505696, "terms": ["How are the functions grouped?", "are the functions grouped?", "the functions grouped?", "functions grouped?", "grouped?"], "tokens": ["How", "are", "the", "functions", "grouped", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-179383725", "fields": {"term": "Can ranking expressions use tensors?", "namespace": "open-p", "hash": -179383725, "terms": ["Can ranking expressions use tensors?", "ranking expressions use tensors?", "expressions use tensors?", "use tensors?", "tensors?"], "tokens": ["Can", "ranking", "expressions", "use", "tensors", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-960091666", "fields": {"term": "What is the purpose of convenience functions?", "namespace": "open-p", "hash": -960091666, "terms": ["What is the purpose of convenience functions?", "is the purpose of convenience functions?", "the purpose of convenience functions?", "purpose of convenience functions?", "of convenience functions?", "convenience functions?", "functions?"], "tokens": ["What", "is", "the", "purpose", "of", "convenience", "functions", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-625871624", "fields": {"term": "What is the purpose of primitive functions?", "namespace": "open-p", "hash": -625871624, "terms": ["What is the purpose of primitive functions?", "is the purpose of primitive functions?", "the purpose of primitive functions?", "purpose of primitive functions?", "of primitive functions?", "primitive functions?", "functions?"], "tokens": ["What", "is", "the", "purpose", "of", "primitive", "functions", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1989803877", "fields": {"term": "How are convenience functions implemented?", "namespace": "open-p", "hash": 1989803877, "terms": ["How are convenience functions implemented?", "are convenience functions implemented?", "convenience functions implemented?", "functions implemented?", "implemented?"], "tokens": ["How", "are", "convenience", "functions", "implemented", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-738048273", "fields": {"term": "What is map function in Vespa?", "namespace": "open-p", "hash": -738048273, "terms": ["What is map function in Vespa?", "is map function in Vespa?", "map function in Vespa?", "function in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["What", "is", "map", "function", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::790993218", "fields": {"term": "What are available aggregators in Vespa?", "namespace": "open-p", "hash": 790993218, "terms": ["What are available aggregators in Vespa?", "are available aggregators in Vespa?", "available aggregators in Vespa?", "aggregators in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["What", "are", "available", "aggregators", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::702985617", "fields": {"term": "How many arguments does join function take?", "namespace": "open-p", "hash": 702985617, "terms": ["How many arguments does join function take?", "many arguments does join function take?", "arguments does join function take?", "does join function take?", "join function take?", "function take?", "take?"], "tokens": ["How", "many", "arguments", "does", "join", "function", "take", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1362157887", "fields": {"term": "What is the syntax for lambda function in Vespa?", "namespace": "open-p", "hash": -1362157887, "terms": ["What is the syntax for lambda function in Vespa?", "is the syntax for lambda function in Vespa?", "the syntax for lambda function in Vespa?", "syntax for lambda function in Vespa?", "for lambda function in Vespa?", "lambda function in Vespa?", "function in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["What", "is", "the", "syntax", "for", "lambda", "function", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-538356672", "fields": {"term": "What is t1?", "namespace": "open-p", "hash": -538356672, "terms": ["What is t1?", "is t1?", "t1?"], "tokens": ["What", "is", "t1", "?"], "corpus_count": 4, "document_count": 4}}, -{"put": "id:term:term::1783357367", "fields": {"term": "What is t2?", "namespace": "open-p", "hash": 1783357367, "terms": ["What is t2?", "is t2?", "t2?"], "tokens": ["What", "is", "t2", "?"], "corpus_count": 4, "document_count": 4}}, -{"put": "id:term:term::737891407", "fields": {"term": "What is t1 and t2?", "namespace": "open-p", "hash": 737891407, "terms": ["What is t1 and t2?", "is t1 and t2?", "t1 and t2?", "and t2?", "t2?"], "tokens": ["What", "is", "t1", "and", "t2", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-768098811", "fields": {"term": "What arguments does rename take?", "namespace": "open-p", "hash": -768098811, "terms": ["What arguments does rename take?", "arguments does rename take?", "does rename take?", "rename take?", "take?"], "tokens": ["What", "arguments", "does", "rename", "take", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::826927970", "fields": {"term": "What is new-names used for?", "namespace": "open-p", "hash": 826927970, "terms": ["What is new-names used for?", "is new-names used for?", "new-names used for?", "used for?", "for?"], "tokens": ["What", "is", "new", "names", "used", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1442442573", "fields": {"term": "Can I rename multiple dimensions?", "namespace": "open-p", "hash": 1442442573, "terms": ["Can I rename multiple dimensions?", "I rename multiple dimensions?", "rename multiple dimensions?", "multiple dimensions?", "dimensions?"], "tokens": ["Can", "I", "rename", "multiple", "dimensions", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::234129346", "fields": {"term": "What does rename return?", "namespace": "open-p", "hash": 234129346, "terms": ["What does rename return?", "does rename return?", "rename return?", "return?"], "tokens": ["What", "does", "rename", "return", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-599281545", "fields": {"term": "What is partial address?", "namespace": "open-p", "hash": -599281545, "terms": ["What is partial address?", "is partial address?", "partial address?", "address?"], "tokens": ["What", "is", "partial", "address", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2096927562", "fields": {"term": "What are the arguments for Slice()?", "namespace": "open-p", "hash": 2096927562, "terms": ["What are the arguments for Slice()?", "are the arguments for Slice()?", "the arguments for Slice()?", "arguments for Slice()?", "for Slice()?", "Slice()?"], "tokens": ["What", "are", "the", "arguments", "for", "Slice", "()?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1773556242", "fields": {"term": "What is the return type of Slice()?", "namespace": "open-p", "hash": -1773556242, "terms": ["What is the return type of Slice()?", "is the return type of Slice()?", "the return type of Slice()?", "return type of Slice()?", "type of Slice()?", "of Slice()?", "Slice()?"], "tokens": ["What", "is", "the", "return", "type", "of", "Slice", "()?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-334199491", "fields": {"term": "What is a common special case for Slice()?", "namespace": "open-p", "hash": -334199491, "terms": ["What is a common special case for Slice()?", "is a common special case for Slice()?", "a common special case for Slice()?", "common special case for Slice()?", "special case for Slice()?", "case for Slice()?", "for Slice()?", "Slice()?"], "tokens": ["What", "is", "a", "common", "special", "case", "for", "Slice", "()?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1975741815", "fields": {"term": "How can one specify an index label for Slice()?", "namespace": "open-p", "hash": 1975741815, "terms": ["How can one specify an index label for Slice()?", "can one specify an index label for Slice()?", "one specify an index label for Slice()?", "specify an index label for Slice()?", "an index label for Slice()?", "index label for Slice()?", "label for Slice()?", "for Slice()?", "Slice()?"], "tokens": ["How", "can", "one", "specify", "an", "index", "label", "for", "Slice", "()?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1636910809", "fields": {"term": "Can Slice() produce a single value?", "namespace": "open-p", "hash": 1636910809, "terms": ["Can Slice() produce a single value?", "Slice() produce a single value?", "produce a single value?", "a single value?", "single value?", "value?"], "tokens": ["Can", "Slice", "()", "produce", "a", "single", "value", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1122274159", "fields": {"term": "What does t1 hold?", "namespace": "open-p", "hash": 1122274159, "terms": ["What does t1 hold?", "does t1 hold?", "t1 hold?", "hold?"], "tokens": ["What", "does", "t1", "hold", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1166535697", "fields": {"term": "What is the difference between 'float' and 'bfloat16' tensors?", "namespace": "open-p", "hash": -1166535697, "terms": ["What is the difference between 'float' and 'bfloat16' tensors?", "is the difference between 'float' and 'bfloat16' tensors?", "the difference between 'float' and 'bfloat16' tensors?", "difference between 'float' and 'bfloat16' tensors?", "between 'float' and 'bfloat16' tensors?", "'float' and 'bfloat16' tensors?", "and 'bfloat16' tensors?", "'bfloat16' tensors?", "tensors?"], "tokens": ["What", "is", "the", "difference", "between", "'", "float", "'", "and", "'", "bfloat16", "'", "tensors", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1664222095", "fields": {"term": "What is cell_cast?", "namespace": "open-p", "hash": -1664222095, "terms": ["What is cell_cast?", "is cell_cast?", "cell_cast?"], "tokens": ["What", "is", "cell_cast", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1862876301", "fields": {"term": "What is bfloat16?", "namespace": "open-p", "hash": -1862876301, "terms": ["What is bfloat16?", "is bfloat16?", "bfloat16?"], "tokens": ["What", "is", "bfloat16", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::204114212", "fields": {"term": "What are lambda functions?", "namespace": "open-p", "hash": 204114212, "terms": ["What are lambda functions?", "are lambda functions?", "lambda functions?", "functions?"], "tokens": ["What", "are", "lambda", "functions", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-228451117", "fields": {"term": "What are the atoms in a lambda?", "namespace": "open-p", "hash": -228451117, "terms": ["What are the atoms in a lambda?", "are the atoms in a lambda?", "the atoms in a lambda?", "atoms in a lambda?", "in a lambda?", "a lambda?", "lambda?"], "tokens": ["What", "are", "the", "atoms", "in", "a", "lambda", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::325916312", "fields": {"term": "Can expressions access variables outside the lambda?", "namespace": "open-p", "hash": 325916312, "terms": ["Can expressions access variables outside the lambda?", "expressions access variables outside the lambda?", "access variables outside the lambda?", "variables outside the lambda?", "outside the lambda?", "the lambda?", "lambda?"], "tokens": ["Can", "expressions", "access", "variables", "outside", "the", "lambda", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1501702591", "fields": {"term": "What is l1_normalize?", "namespace": "open-p", "hash": 1501702591, "terms": ["What is l1_normalize?", "is l1_normalize?", "l1_normalize?"], "tokens": ["What", "is", "l1_normalize", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-351157223", "fields": {"term": "What is max(t, dim)?", "namespace": "open-p", "hash": -351157223, "terms": ["What is max(t, dim)?", "is max(t, dim)?", "max(t, dim)?", "dim)?"], "tokens": ["What", "is", "max", "(", "t", "dim", ")?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-181968919", "fields": {"term": "What is hamming(t1,t2)?", "namespace": "open-p", "hash": -181968919, "terms": ["What is hamming(t1,t2)?", "is hamming(t1,t2)?", "hamming(t1,t2)?"], "tokens": ["What", "is", "hamming", "(", "t1", "t2", ")?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1634129400", "fields": {"term": "What is random(n1, n2, ...)?", "namespace": "open-p", "hash": -1634129400, "terms": ["What is random(n1, n2, ...)?", "is random(n1, n2, ...)?", "random(n1, n2, ...)?", "n2, ...)?", "...)?"], "tokens": ["What", "is", "random", "(", "n1", "n2", ")?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1818385630", "fields": {"term": "What is xw_plus_b(x,w,b,dim)?", "namespace": "open-p", "hash": -1818385630, "terms": ["What is xw_plus_b(x,w,b,dim)?", "is xw_plus_b(x,w,b,dim)?", "xw_plus_b(x,w,b,dim)?"], "tokens": ["What", "is", "xw_plus_b", "(", "x", "w", "b", "dim", ")?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1599860698", "fields": {"term": "Where are ranking expressions defined?", "namespace": "open-p", "hash": 1599860698, "terms": ["Where are ranking expressions defined?", "are ranking expressions defined?", "ranking expressions defined?", "expressions defined?", "defined?"], "tokens": ["Where", "are", "ranking", "expressions", "defined", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1940247150", "fields": {"term": "How many phases can a rank profile define?", "namespace": "open-p", "hash": -1940247150, "terms": ["How many phases can a rank profile define?", "many phases can a rank profile define?", "phases can a rank profile define?", "can a rank profile define?", "a rank profile define?", "rank profile define?", "profile define?", "define?"], "tokens": ["How", "many", "phases", "can", "a", "rank", "profile", "define", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::23848014", "fields": {"term": "What is evaluated locally on content nodes?", "namespace": "open-p", "hash": 23848014, "terms": ["What is evaluated locally on content nodes?", "is evaluated locally on content nodes?", "evaluated locally on content nodes?", "locally on content nodes?", "on content nodes?", "content nodes?", "nodes?"], "tokens": ["What", "is", "evaluated", "locally", "on", "content", "nodes", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1228090744", "fields": {"term": "How is the second phase executed?", "namespace": "open-p", "hash": -1228090744, "terms": ["How is the second phase executed?", "is the second phase executed?", "the second phase executed?", "second phase executed?", "phase executed?", "executed?"], "tokens": ["How", "is", "the", "second", "phase", "executed", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2067078048", "fields": {"term": "What is rerank-count?", "namespace": "open-p", "hash": -2067078048, "terms": ["What is rerank-count?", "is rerank-count?", "rerank-count?"], "tokens": ["What", "is", "rerank", "count", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-733593248", "fields": {"term": "What is the purpose of phased ranking?", "namespace": "open-p", "hash": -733593248, "terms": ["What is the purpose of phased ranking?", "is the purpose of phased ranking?", "the purpose of phased ranking?", "purpose of phased ranking?", "of phased ranking?", "phased ranking?", "ranking?"], "tokens": ["What", "is", "the", "purpose", "of", "phased", "ranking", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1911471960", "fields": {"term": "What is the global-phase?", "namespace": "open-p", "hash": 1911471960, "terms": ["What is the global-phase?", "is the global-phase?", "the global-phase?", "global-phase?"], "tokens": ["What", "is", "the", "global", "phase", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-427679888", "fields": {"term": "What is second-phase?", "namespace": "open-p", "hash": -427679888, "terms": ["What is second-phase?", "is second-phase?", "second-phase?"], "tokens": ["What", "is", "second", "phase", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-214613498", "fields": {"term": "What is used by my_onnx_model?", "namespace": "open-p", "hash": -214613498, "terms": ["What is used by my_onnx_model?", "is used by my_onnx_model?", "used by my_onnx_model?", "by my_onnx_model?", "my_onnx_model?"], "tokens": ["What", "is", "used", "by", "my_onnx_model", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1755635974", "fields": {"term": "How is the global phase different?", "namespace": "open-p", "hash": -1755635974, "terms": ["How is the global phase different?", "is the global phase different?", "the global phase different?", "global phase different?", "phase different?", "different?"], "tokens": ["How", "is", "the", "global", "phase", "different", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1506183209", "fields": {"term": "How is the global-phase expression executed?", "namespace": "open-p", "hash": 1506183209, "terms": ["How is the global-phase expression executed?", "is the global-phase expression executed?", "the global-phase expression executed?", "global-phase expression executed?", "expression executed?", "executed?"], "tokens": ["How", "is", "the", "global", "phase", "expression", "executed", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-408668930", "fields": {"term": "What ML formats does Vespa support?", "namespace": "open-p", "hash": -408668930, "terms": ["What ML formats does Vespa support?", "ML formats does Vespa support?", "formats does Vespa support?", "does Vespa support?", "Vespa support?", "support?"], "tokens": ["What", "ML", "formats", "does", "Vespa", "support", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2129254724", "fields": {"term": "How can I combine ML models in Vespa for ranking?", "namespace": "open-p", "hash": -2129254724, "terms": ["How can I combine ML models in Vespa for ranking?", "can I combine ML models in Vespa for ranking?", "I combine ML models in Vespa for ranking?", "combine ML models in Vespa for ranking?", "ML models in Vespa for ranking?", "models in Vespa for ranking?", "in Vespa for ranking?", "Vespa for ranking?", "for ranking?", "ranking?"], "tokens": ["How", "can", "I", "combine", "ML", "models", "in", "Vespa", "for", "ranking", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1854783825", "fields": {"term": "What is a model ensemble in Vespa?", "namespace": "open-p", "hash": -1854783825, "terms": ["What is a model ensemble in Vespa?", "is a model ensemble in Vespa?", "a model ensemble in Vespa?", "model ensemble in Vespa?", "ensemble in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["What", "is", "a", "model", "ensemble", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1636016406", "fields": {"term": "What is the syntax for using a model ensemble in a rank-profile in Vespa?", "namespace": "open-p", "hash": 1636016406, "terms": ["What is the syntax for using a model ensemble in a rank-profile in Vespa?", "is the syntax for using a model ensemble in a rank-profile in Vespa?", "the syntax for using a model ensemble in a rank-profile in Vespa?", "syntax for using a model ensemble in a rank-profile in Vespa?", "for using a model ensemble in a rank-profile in Vespa?", "using a model ensemble in a rank-profile in Vespa?", "a model ensemble in a rank-profile in Vespa?", "model ensemble in a rank-profile in Vespa?", "ensemble in a rank-profile in Vespa?", "in a rank-profile in Vespa?", "a rank-profile in Vespa?", "rank-profile in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["What", "is", "the", "syntax", "for", "using", "a", "model", "ensemble", "in", "a", "rank", "profile", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1883726607", "fields": {"term": "What are rank profiles?", "namespace": "open-p", "hash": -1883726607, "terms": ["What are rank profiles?", "are rank profiles?", "rank profiles?", "profiles?"], "tokens": ["What", "are", "rank", "profiles", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::1460118338", "fields": {"term": "What is the default text ranking profile?", "namespace": "open-p", "hash": 1460118338, "terms": ["What is the default text ranking profile?", "is the default text ranking profile?", "the default text ranking profile?", "default text ranking profile?", "text ranking profile?", "ranking profile?", "profile?"], "tokens": ["What", "is", "the", "default", "text", "ranking", "profile", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1578865008", "fields": {"term": "How are rank profiles selected for queries?", "namespace": "open-p", "hash": -1578865008, "terms": ["How are rank profiles selected for queries?", "are rank profiles selected for queries?", "rank profiles selected for queries?", "profiles selected for queries?", "selected for queries?", "for queries?", "queries?"], "tokens": ["How", "are", "rank", "profiles", "selected", "for", "queries", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1825280358", "fields": {"term": "What is inheritance in rank profiles?", "namespace": "open-p", "hash": 1825280358, "terms": ["What is inheritance in rank profiles?", "is inheritance in rank profiles?", "inheritance in rank profiles?", "in rank profiles?", "rank profiles?", "profiles?"], "tokens": ["What", "is", "inheritance", "in", "rank", "profiles", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2063178488", "fields": {"term": "What is the 'default' rank profile?", "namespace": "open-p", "hash": -2063178488, "terms": ["What is the 'default' rank profile?", "is the 'default' rank profile?", "the 'default' rank profile?", "'default' rank profile?", "rank profile?", "profile?"], "tokens": ["What", "is", "the", "'", "default", "'", "rank", "profile", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1449562308", "fields": {"term": "What is the 'unranked' rank profile?", "namespace": "open-p", "hash": -1449562308, "terms": ["What is the 'unranked' rank profile?", "is the 'unranked' rank profile?", "the 'unranked' rank profile?", "'unranked' rank profile?", "rank profile?", "profile?"], "tokens": ["What", "is", "the", "'", "unranked", "'", "rank", "profile", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1225415039", "fields": {"term": "Where can rank profiles be defined?", "namespace": "open-p", "hash": -1225415039, "terms": ["Where can rank profiles be defined?", "can rank profiles be defined?", "rank profiles be defined?", "profiles be defined?", "be defined?", "defined?"], "tokens": ["Where", "can", "rank", "profiles", "be", "defined", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1737324711", "fields": {"term": "How many rank profiles can a schema have?", "namespace": "open-p", "hash": 1737324711, "terms": ["How many rank profiles can a schema have?", "many rank profiles can a schema have?", "rank profiles can a schema have?", "profiles can a schema have?", "can a schema have?", "a schema have?", "schema have?", "have?"], "tokens": ["How", "many", "rank", "profiles", "can", "a", "schema", "have", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1556111511", "fields": {"term": "How are rank profiles evaluated?", "namespace": "open-p", "hash": -1556111511, "terms": ["How are rank profiles evaluated?", "are rank profiles evaluated?", "rank profiles evaluated?", "profiles evaluated?", "evaluated?"], "tokens": ["How", "are", "rank", "profiles", "evaluated", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1384783904", "fields": {"term": "What is the 'foo' function?", "namespace": "open-p", "hash": -1384783904, "terms": ["What is the 'foo' function?", "is the 'foo' function?", "the 'foo' function?", "'foo' function?", "function?"], "tokens": ["What", "is", "the", "'", "foo", "'", "function", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::370589225", "fields": {"term": "What is the 'bar' function?", "namespace": "open-p", "hash": 370589225, "terms": ["What is the 'bar' function?", "is the 'bar' function?", "the 'bar' function?", "'bar' function?", "function?"], "tokens": ["What", "is", "the", "'", "bar", "'", "function", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1031347403", "fields": {"term": "What is the purpose of 'foo' function?", "namespace": "open-p", "hash": 1031347403, "terms": ["What is the purpose of 'foo' function?", "is the purpose of 'foo' function?", "the purpose of 'foo' function?", "purpose of 'foo' function?", "of 'foo' function?", "'foo' function?", "function?"], "tokens": ["What", "is", "the", "purpose", "of", "'", "foo", "'", "function", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1185167355", "fields": {"term": "What arguments can be passed between functions?", "namespace": "open-p", "hash": -1185167355, "terms": ["What arguments can be passed between functions?", "arguments can be passed between functions?", "can be passed between functions?", "be passed between functions?", "passed between functions?", "between functions?", "functions?"], "tokens": ["What", "arguments", "can", "be", "passed", "between", "functions", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::497559591", "fields": {"term": "Can 'sum' be computed lazily in 'bar' function?", "namespace": "open-p", "hash": 497559591, "terms": ["Can 'sum' be computed lazily in 'bar' function?", "'sum' be computed lazily in 'bar' function?", "be computed lazily in 'bar' function?", "computed lazily in 'bar' function?", "lazily in 'bar' function?", "in 'bar' function?", "'bar' function?", "function?"], "tokens": ["Can", "'", "sum", "'", "be", "computed", "lazily", "in", "'", "bar", "'", "function", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1719852298", "fields": {"term": "What is Vespa Rate Limiting Searcher?", "namespace": "open-p", "hash": -1719852298, "terms": ["What is Vespa Rate Limiting Searcher?", "is Vespa Rate Limiting Searcher?", "Vespa Rate Limiting Searcher?", "Rate Limiting Searcher?", "Limiting Searcher?", "Searcher?"], "tokens": ["What", "is", "Vespa", "Rate", "Limiting", "Searcher", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1981005346", "fields": {"term": "What HTTP code is returned for exceeded quota?", "namespace": "open-p", "hash": -1981005346, "terms": ["What HTTP code is returned for exceeded quota?", "HTTP code is returned for exceeded quota?", "code is returned for exceeded quota?", "is returned for exceeded quota?", "returned for exceeded quota?", "for exceeded quota?", "exceeded quota?", "quota?"], "tokens": ["What", "HTTP", "code", "is", "returned", "for", "exceeded", "quota", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::159234916", "fields": {"term": "What is the consequence of exceeding the quota?", "namespace": "open-p", "hash": 159234916, "terms": ["What is the consequence of exceeding the quota?", "is the consequence of exceeding the quota?", "the consequence of exceeding the quota?", "consequence of exceeding the quota?", "of exceeding the quota?", "exceeding the quota?", "the quota?", "quota?"], "tokens": ["What", "is", "the", "consequence", "of", "exceeding", "the", "quota", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::520259324", "fields": {"term": "What is the rate limiting searcher?", "namespace": "open-p", "hash": 520259324, "terms": ["What is the rate limiting searcher?", "is the rate limiting searcher?", "the rate limiting searcher?", "rate limiting searcher?", "limiting searcher?", "searcher?"], "tokens": ["What", "is", "the", "rate", "limiting", "searcher", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1889204644", "fields": {"term": "How to enable rate limiting searcher?", "namespace": "open-p", "hash": 1889204644, "terms": ["How to enable rate limiting searcher?", "to enable rate limiting searcher?", "enable rate limiting searcher?", "rate limiting searcher?", "limiting searcher?", "searcher?"], "tokens": ["How", "to", "enable", "rate", "limiting", "searcher", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1112255768", "fields": {"term": "How to enable the rate limiting searcher on per-request basis?", "namespace": "open-p", "hash": 1112255768, "terms": ["How to enable the rate limiting searcher on per-request basis?", "to enable the rate limiting searcher on per-request basis?", "enable the rate limiting searcher on per-request basis?", "the rate limiting searcher on per-request basis?", "rate limiting searcher on per-request basis?", "limiting searcher on per-request basis?", "searcher on per-request basis?", "on per-request basis?", "per-request basis?", "basis?"], "tokens": ["How", "to", "enable", "the", "rate", "limiting", "searcher", "on", "per", "request", "basis", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1848953730", "fields": {"term": "What is rate.cost?", "namespace": "open-p", "hash": 1848953730, "terms": ["What is rate.cost?", "is rate.cost?", "rate.cost?"], "tokens": ["What", "is", "rate", "cost", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1644381099", "fields": {"term": "What is rate.quota used for?", "namespace": "open-p", "hash": 1644381099, "terms": ["What is rate.quota used for?", "is rate.quota used for?", "rate.quota used for?", "used for?", "for?"], "tokens": ["What", "is", "rate", "quota", "used", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::350101257", "fields": {"term": "How do you set rate.id?", "namespace": "open-p", "hash": 350101257, "terms": ["How do you set rate.id?", "do you set rate.id?", "you set rate.id?", "set rate.id?", "rate.id?"], "tokens": ["How", "do", "you", "set", "rate", "id", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2043097160", "fields": {"term": "How can I enable rate limiting?", "namespace": "open-p", "hash": -2043097160, "terms": ["How can I enable rate limiting?", "can I enable rate limiting?", "I enable rate limiting?", "enable rate limiting?", "rate limiting?", "limiting?"], "tokens": ["How", "can", "I", "enable", "rate", "limiting", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2028163013", "fields": {"term": "What is rate limiting?", "namespace": "open-p", "hash": 2028163013, "terms": ["What is rate limiting?", "is rate limiting?", "rate limiting?", "limiting?"], "tokens": ["What", "is", "rate", "limiting", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::257921810", "fields": {"term": "What is clientId?", "namespace": "open-p", "hash": 257921810, "terms": ["What is clientId?", "is clientId?", "clientId?"], "tokens": ["What", "is", "clientId", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2010614669", "fields": {"term": "What is the syntax for assigning a quota based on clientId?", "namespace": "open-p", "hash": -2010614669, "terms": ["What is the syntax for assigning a quota based on clientId?", "is the syntax for assigning a quota based on clientId?", "the syntax for assigning a quota based on clientId?", "syntax for assigning a quota based on clientId?", "for assigning a quota based on clientId?", "assigning a quota based on clientId?", "a quota based on clientId?", "quota based on clientId?", "based on clientId?", "on clientId?", "clientId?"], "tokens": ["What", "is", "the", "syntax", "for", "assigning", "a", "quota", "based", "on", "clientId", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::201110612", "fields": {"term": "How can we add a specific configuration for a given clientId?", "namespace": "open-p", "hash": 201110612, "terms": ["How can we add a specific configuration for a given clientId?", "can we add a specific configuration for a given clientId?", "we add a specific configuration for a given clientId?", "add a specific configuration for a given clientId?", "a specific configuration for a given clientId?", "specific configuration for a given clientId?", "configuration for a given clientId?", "for a given clientId?", "a given clientId?", "given clientId?", "clientId?"], "tokens": ["How", "can", "we", "add", "a", "specific", "configuration", "for", "a", "given", "clientId", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1912295449", "fields": {"term": "What is the default quota if no clientId is specified?", "namespace": "open-p", "hash": -1912295449, "terms": ["What is the default quota if no clientId is specified?", "is the default quota if no clientId is specified?", "the default quota if no clientId is specified?", "default quota if no clientId is specified?", "quota if no clientId is specified?", "if no clientId is specified?", "no clientId is specified?", "clientId is specified?", "is specified?", "specified?"], "tokens": ["What", "is", "the", "default", "quota", "if", "no", "clientId", "is", "specified", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1136442299", "fields": {"term": "What is the count metric?", "namespace": "open-p", "hash": 1136442299, "terms": ["What is the count metric?", "is the count metric?", "the count metric?", "count metric?", "metric?"], "tokens": ["What", "is", "the", "count", "metric", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1679118797", "fields": {"term": "What is the `requestsOverQuota` metric?", "namespace": "open-p", "hash": 1679118797, "terms": ["What is the `requestsOverQuota` metric?", "is the `requestsOverQuota` metric?", "the `requestsOverQuota` metric?", "`requestsOverQuota` metric?", "metric?"], "tokens": ["What", "is", "the", "`", "requestsOverQuota", "`", "metric", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1505598236", "fields": {"term": "What is rate.idDimension?", "namespace": "open-p", "hash": 1505598236, "terms": ["What is rate.idDimension?", "is rate.idDimension?", "rate.idDimension?"], "tokens": ["What", "is", "rate", "idDimension", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1463271614", "fields": {"term": "What is rate.id?", "namespace": "open-p", "hash": 1463271614, "terms": ["What is rate.id?", "is rate.id?", "rate.id?"], "tokens": ["What", "is", "rate", "id", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1709327100", "fields": {"term": "How are writes and reads processed?", "namespace": "open-p", "hash": 1709327100, "terms": ["How are writes and reads processed?", "are writes and reads processed?", "writes and reads processed?", "and reads processed?", "reads processed?", "processed?"], "tokens": ["How", "are", "writes", "and", "reads", "processed", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1329914047", "fields": {"term": "What is Vespa's consistency model?", "namespace": "open-p", "hash": 1329914047, "terms": ["What is Vespa's consistency model?", "is Vespa's consistency model?", "Vespa's consistency model?", "consistency model?", "model?"], "tokens": ["What", "is", "Vespa", "'", "s", "consistency", "model", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::-530533839", "fields": {"term": "What is a Get operation?", "namespace": "open-p", "hash": -530533839, "terms": ["What is a Get operation?", "is a Get operation?", "a Get operation?", "Get operation?", "operation?"], "tokens": ["What", "is", "a", "Get", "operation", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1838020377", "fields": {"term": "What is a Put operation?", "namespace": "open-p", "hash": 1838020377, "terms": ["What is a Put operation?", "is a Put operation?", "a Put operation?", "Put operation?", "operation?"], "tokens": ["What", "is", "a", "Put", "operation", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1891173348", "fields": {"term": "What is a Remove operation?", "namespace": "open-p", "hash": -1891173348, "terms": ["What is a Remove operation?", "is a Remove operation?", "a Remove operation?", "Remove operation?", "operation?"], "tokens": ["What", "is", "a", "Remove", "operation", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-765727350", "fields": {"term": "What is an Update operation?", "namespace": "open-p", "hash": -765727350, "terms": ["What is an Update operation?", "is an Update operation?", "an Update operation?", "Update operation?", "operation?"], "tokens": ["What", "is", "an", "Update", "operation", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::404525027", "fields": {"term": "Can updates have conditions?", "namespace": "open-p", "hash": 404525027, "terms": ["Can updates have conditions?", "updates have conditions?", "have conditions?", "conditions?"], "tokens": ["Can", "updates", "have", "conditions", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::487218708", "fields": {"term": "What are the composite types?", "namespace": "open-p", "hash": 487218708, "terms": ["What are the composite types?", "are the composite types?", "the composite types?", "composite types?", "types?"], "tokens": ["What", "are", "the", "composite", "types", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1520147196", "fields": {"term": "What is the purpose of vespa-feeder?", "namespace": "open-p", "hash": 1520147196, "terms": ["What is the purpose of vespa-feeder?", "is the purpose of vespa-feeder?", "the purpose of vespa-feeder?", "purpose of vespa-feeder?", "of vespa-feeder?", "vespa-feeder?"], "tokens": ["What", "is", "the", "purpose", "of", "vespa", "feeder", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1254053396", "fields": {"term": "What is vespa-feeder?", "namespace": "open-p", "hash": -1254053396, "terms": ["What is vespa-feeder?", "is vespa-feeder?", "vespa-feeder?"], "tokens": ["What", "is", "vespa", "feeder", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::450458420", "fields": {"term": "What triggers Vespa to refeed data?", "namespace": "open-p", "hash": 450458420, "terms": ["What triggers Vespa to refeed data?", "triggers Vespa to refeed data?", "Vespa to refeed data?", "to refeed data?", "refeed data?", "data?"], "tokens": ["What", "triggers", "Vespa", "to", "refeed", "data", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1859664471", "fields": {"term": "What are changes in the indexing pipeline?", "namespace": "open-p", "hash": -1859664471, "terms": ["What are changes in the indexing pipeline?", "are changes in the indexing pipeline?", "changes in the indexing pipeline?", "in the indexing pipeline?", "the indexing pipeline?", "indexing pipeline?", "pipeline?"], "tokens": ["What", "are", "changes", "in", "the", "indexing", "pipeline", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-726789012", "fields": {"term": "How can reindexing be triggered?", "namespace": "open-p", "hash": -726789012, "terms": ["How can reindexing be triggered?", "can reindexing be triggered?", "reindexing be triggered?", "be triggered?", "triggered?"], "tokens": ["How", "can", "reindexing", "be", "triggered", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2106720342", "fields": {"term": "What is the reindex endpoint?", "namespace": "open-p", "hash": 2106720342, "terms": ["What is the reindex endpoint?", "is the reindex endpoint?", "the reindex endpoint?", "reindex endpoint?", "endpoint?"], "tokens": ["What", "is", "the", "reindex", "endpoint", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::379817493", "fields": {"term": "What is the reindexing endpoint?", "namespace": "open-p", "hash": 379817493, "terms": ["What is the reindexing endpoint?", "is the reindexing endpoint?", "the reindexing endpoint?", "reindexing endpoint?", "endpoint?"], "tokens": ["What", "is", "the", "reindexing", "endpoint", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1389512315", "fields": {"term": "What can cause changes in the indexing pipeline?", "namespace": "open-p", "hash": 1389512315, "terms": ["What can cause changes in the indexing pipeline?", "can cause changes in the indexing pipeline?", "cause changes in the indexing pipeline?", "changes in the indexing pipeline?", "in the indexing pipeline?", "the indexing pipeline?", "indexing pipeline?", "pipeline?"], "tokens": ["What", "can", "cause", "changes", "in", "the", "indexing", "pipeline", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1539099713", "fields": {"term": "What is indexing mode in a content cluster?", "namespace": "open-p", "hash": -1539099713, "terms": ["What is indexing mode in a content cluster?", "is indexing mode in a content cluster?", "indexing mode in a content cluster?", "mode in a content cluster?", "in a content cluster?", "a content cluster?", "content cluster?", "cluster?"], "tokens": ["What", "is", "indexing", "mode", "in", "a", "content", "cluster", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1447884251", "fields": {"term": "What happens when there is a change in the indexing pipeline?", "namespace": "open-p", "hash": -1447884251, "terms": ["What happens when there is a change in the indexing pipeline?", "happens when there is a change in the indexing pipeline?", "when there is a change in the indexing pipeline?", "there is a change in the indexing pipeline?", "is a change in the indexing pipeline?", "a change in the indexing pipeline?", "change in the indexing pipeline?", "in the indexing pipeline?", "the indexing pipeline?", "indexing pipeline?", "pipeline?"], "tokens": ["What", "happens", "when", "there", "is", "a", "change", "in", "the", "indexing", "pipeline", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1616292778", "fields": {"term": "How is a change in indexing pipeline discovered?", "namespace": "open-p", "hash": 1616292778, "terms": ["How is a change in indexing pipeline discovered?", "is a change in indexing pipeline discovered?", "a change in indexing pipeline discovered?", "change in indexing pipeline discovered?", "in indexing pipeline discovered?", "indexing pipeline discovered?", "pipeline discovered?", "discovered?"], "tokens": ["How", "is", "a", "change", "in", "indexing", "pipeline", "discovered", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-483627367", "fields": {"term": "What is a validation override?", "namespace": "open-p", "hash": -483627367, "terms": ["What is a validation override?", "is a validation override?", "a validation override?", "validation override?", "override?"], "tokens": ["What", "is", "a", "validation", "override", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::132897249", "fields": {"term": "What are reindex actions?", "namespace": "open-p", "hash": 132897249, "terms": ["What are reindex actions?", "are reindex actions?", "reindex actions?", "actions?"], "tokens": ["What", "are", "reindex", "actions", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::761164330", "fields": {"term": "When should you trigger reindexing?", "namespace": "open-p", "hash": 761164330, "terms": ["When should you trigger reindexing?", "should you trigger reindexing?", "you trigger reindexing?", "trigger reindexing?", "reindexing?"], "tokens": ["When", "should", "you", "trigger", "reindexing", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1935104865", "fields": {"term": "When does reindexing commence?", "namespace": "open-p", "hash": -1935104865, "terms": ["When does reindexing commence?", "does reindexing commence?", "reindexing commence?", "commence?"], "tokens": ["When", "does", "reindexing", "commence", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::736934618", "fields": {"term": "What is the prepare endpoint?", "namespace": "open-p", "hash": 736934618, "terms": ["What is the prepare endpoint?", "is the prepare endpoint?", "the prepare endpoint?", "prepare endpoint?", "endpoint?"], "tokens": ["What", "is", "the", "prepare", "endpoint", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-953702268", "fields": {"term": "How does reindexing work?", "namespace": "open-p", "hash": -953702268, "terms": ["How does reindexing work?", "does reindexing work?", "reindexing work?", "work?"], "tokens": ["How", "does", "reindexing", "work", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::626992043", "fields": {"term": "What happens during reindexing?", "namespace": "open-p", "hash": 626992043, "terms": ["What happens during reindexing?", "happens during reindexing?", "during reindexing?", "reindexing?"], "tokens": ["What", "happens", "during", "reindexing", "?"], "corpus_count": 3, "document_count": 3}}, -{"put": "id:term:term::-1098741778", "fields": {"term": "What is the purpose of reindexing?", "namespace": "open-p", "hash": -1098741778, "terms": ["What is the purpose of reindexing?", "is the purpose of reindexing?", "the purpose of reindexing?", "purpose of reindexing?", "of reindexing?", "reindexing?"], "tokens": ["What", "is", "the", "purpose", "of", "reindexing", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::146146995", "fields": {"term": "Which component does reindexing?", "namespace": "open-p", "hash": 146146995, "terms": ["Which component does reindexing?", "component does reindexing?", "does reindexing?", "reindexing?"], "tokens": ["Which", "component", "does", "reindexing", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::795568600", "fields": {"term": "What is the progress endpoint for reindexing?", "namespace": "open-p", "hash": 795568600, "terms": ["What is the progress endpoint for reindexing?", "is the progress endpoint for reindexing?", "the progress endpoint for reindexing?", "progress endpoint for reindexing?", "endpoint for reindexing?", "for reindexing?", "reindexing?"], "tokens": ["What", "is", "the", "progress", "endpoint", "for", "reindexing", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1626943696", "fields": {"term": "How is write latency impacted during reindexing?", "namespace": "open-p", "hash": 1626943696, "terms": ["How is write latency impacted during reindexing?", "is write latency impacted during reindexing?", "write latency impacted during reindexing?", "latency impacted during reindexing?", "impacted during reindexing?", "during reindexing?", "reindexing?"], "tokens": ["How", "is", "write", "latency", "impacted", "during", "reindexing", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1356034841", "fields": {"term": "What types of documents are reindexed?", "namespace": "open-p", "hash": 1356034841, "terms": ["What types of documents are reindexed?", "types of documents are reindexed?", "of documents are reindexed?", "documents are reindexed?", "are reindexed?", "reindexed?"], "tokens": ["What", "types", "of", "documents", "are", "reindexed", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1434691261", "fields": {"term": "What is schema changes procedure?", "namespace": "open-p", "hash": -1434691261, "terms": ["What is schema changes procedure?", "is schema changes procedure?", "schema changes procedure?", "changes procedure?", "procedure?"], "tokens": ["What", "is", "schema", "changes", "procedure", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::437158497", "fields": {"term": "How to test reindexing feature?", "namespace": "open-p", "hash": 437158497, "terms": ["How to test reindexing feature?", "to test reindexing feature?", "test reindexing feature?", "reindexing feature?", "feature?"], "tokens": ["How", "to", "test", "reindexing", "feature", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-426938681", "fields": {"term": "What are tools to validate data?", "namespace": "open-p", "hash": -426938681, "terms": ["What are tools to validate data?", "are tools to validate data?", "tools to validate data?", "to validate data?", "validate data?", "data?"], "tokens": ["What", "are", "tools", "to", "validate", "data", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1969750639", "fields": {"term": "What is a synthetic field?", "namespace": "open-p", "hash": -1969750639, "terms": ["What is a synthetic field?", "is a synthetic field?", "a synthetic field?", "synthetic field?", "field?"], "tokens": ["What", "is", "a", "synthetic", "field", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::536700536", "fields": {"term": "Can indexing settings be changed?", "namespace": "open-p", "hash": 536700536, "terms": ["Can indexing settings be changed?", "indexing settings be changed?", "settings be changed?", "be changed?", "changed?"], "tokens": ["Can", "indexing", "settings", "be", "changed", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::10469832", "fields": {"term": "What is a stateless searcher?", "namespace": "open-p", "hash": 10469832, "terms": ["What is a stateless searcher?", "is a stateless searcher?", "a stateless searcher?", "stateless searcher?", "searcher?"], "tokens": ["What", "is", "a", "stateless", "searcher", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1965894921", "fields": {"term": "What is multiphase searching?", "namespace": "open-p", "hash": -1965894921, "terms": ["What is multiphase searching?", "is multiphase searching?", "multiphase searching?", "searching?"], "tokens": ["What", "is", "multiphase", "searching", "?"], "corpus_count": 3, "document_count": 3}}, -{"put": "id:term:term::1483601875", "fields": {"term": "What is the matching query protocol phase?", "namespace": "open-p", "hash": 1483601875, "terms": ["What is the matching query protocol phase?", "is the matching query protocol phase?", "the matching query protocol phase?", "matching query protocol phase?", "query protocol phase?", "protocol phase?", "phase?"], "tokens": ["What", "is", "the", "matching", "query", "protocol", "phase", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1359519438", "fields": {"term": "What is the fill query protocol phase?", "namespace": "open-p", "hash": 1359519438, "terms": ["What is the fill query protocol phase?", "is the fill query protocol phase?", "the fill query protocol phase?", "fill query protocol phase?", "query protocol phase?", "protocol phase?", "phase?"], "tokens": ["What", "is", "the", "fill", "query", "protocol", "phase", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-356049908", "fields": {"term": "What is the life of a query in Vespa?", "namespace": "open-p", "hash": -356049908, "terms": ["What is the life of a query in Vespa?", "is the life of a query in Vespa?", "the life of a query in Vespa?", "life of a query in Vespa?", "of a query in Vespa?", "a query in Vespa?", "query in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["What", "is", "the", "life", "of", "a", "query", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-875630735", "fields": {"term": "What rank profile is used for re-ranking?", "namespace": "open-p", "hash": -875630735, "terms": ["What rank profile is used for re-ranking?", "rank profile is used for re-ranking?", "profile is used for re-ranking?", "is used for re-ranking?", "used for re-ranking?", "for re-ranking?", "re-ranking?"], "tokens": ["What", "rank", "profile", "is", "used", "for", "re", "ranking", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::546156612", "fields": {"term": "What is the purpose of ReRankingSearcher?", "namespace": "open-p", "hash": 546156612, "terms": ["What is the purpose of ReRankingSearcher?", "is the purpose of ReRankingSearcher?", "the purpose of ReRankingSearcher?", "purpose of ReRankingSearcher?", "of ReRankingSearcher?", "ReRankingSearcher?"], "tokens": ["What", "is", "the", "purpose", "of", "ReRankingSearcher", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1854788129", "fields": {"term": "What is normalizedByMax used for?", "namespace": "open-p", "hash": 1854788129, "terms": ["What is normalizedByMax used for?", "is normalizedByMax used for?", "normalizedByMax used for?", "used for?", "for?"], "tokens": ["What", "is", "normalizedByMax", "used", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1088113668", "fields": {"term": "How to install vespa-cli?", "namespace": "open-p", "hash": 1088113668, "terms": ["How to install vespa-cli?", "to install vespa-cli?", "install vespa-cli?", "vespa-cli?"], "tokens": ["How", "to", "install", "vespa", "cli", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::1266728756", "fields": {"term": "What does Maven project do?", "namespace": "open-p", "hash": 1266728756, "terms": ["What does Maven project do?", "does Maven project do?", "Maven project do?", "project do?", "do?"], "tokens": ["What", "does", "Maven", "project", "do", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1891885254", "fields": {"term": "What is reranking searcher?", "namespace": "open-p", "hash": 1891885254, "terms": ["What is reranking searcher?", "is reranking searcher?", "reranking searcher?", "searcher?"], "tokens": ["What", "is", "reranking", "searcher", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-855468063", "fields": {"term": "How to remove app and data?", "namespace": "open-p", "hash": -855468063, "terms": ["How to remove app and data?", "to remove app and data?", "remove app and data?", "app and data?", "and data?", "data?"], "tokens": ["How", "to", "remove", "app", "and", "data", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2012403909", "fields": {"term": "What is the SectionedRenderer class?", "namespace": "open-p", "hash": -2012403909, "terms": ["What is the SectionedRenderer class?", "is the SectionedRenderer class?", "the SectionedRenderer class?", "SectionedRenderer class?", "class?"], "tokens": ["What", "is", "the", "SectionedRenderer", "class", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1737151173", "fields": {"term": "What is the difference between SectionedRenderer and Renderer?", "namespace": "open-p", "hash": -1737151173, "terms": ["What is the difference between SectionedRenderer and Renderer?", "is the difference between SectionedRenderer and Renderer?", "the difference between SectionedRenderer and Renderer?", "difference between SectionedRenderer and Renderer?", "between SectionedRenderer and Renderer?", "SectionedRenderer and Renderer?", "and Renderer?", "Renderer?"], "tokens": ["What", "is", "the", "difference", "between", "SectionedRenderer", "and", "Renderer", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::332618674", "fields": {"term": "How are renderers deployed and configured?", "namespace": "open-p", "hash": 332618674, "terms": ["How are renderers deployed and configured?", "are renderers deployed and configured?", "renderers deployed and configured?", "deployed and configured?", "and configured?", "configured?"], "tokens": ["How", "are", "renderers", "deployed", "and", "configured", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1756745672", "fields": {"term": "What is the AsynchronousSectionedRenderer class?", "namespace": "open-p", "hash": -1756745672, "terms": ["What is the AsynchronousSectionedRenderer class?", "is the AsynchronousSectionedRenderer class?", "the AsynchronousSectionedRenderer class?", "AsynchronousSectionedRenderer class?", "class?"], "tokens": ["What", "is", "the", "AsynchronousSectionedRenderer", "class", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-598686362", "fields": {"term": "What is the contract for creating renderers?", "namespace": "open-p", "hash": -598686362, "terms": ["What is the contract for creating renderers?", "is the contract for creating renderers?", "the contract for creating renderers?", "contract for creating renderers?", "for creating renderers?", "creating renderers?", "renderers?"], "tokens": ["What", "is", "the", "contract", "for", "creating", "renderers", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1950822843", "fields": {"term": "What is the purpose of renderers?", "namespace": "open-p", "hash": -1950822843, "terms": ["What is the purpose of renderers?", "is the purpose of renderers?", "the purpose of renderers?", "purpose of renderers?", "of renderers?", "renderers?"], "tokens": ["What", "is", "the", "purpose", "of", "renderers", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1976286617", "fields": {"term": "Can renderers use and store state during rendering?", "namespace": "open-p", "hash": 1976286617, "terms": ["Can renderers use and store state during rendering?", "renderers use and store state during rendering?", "use and store state during rendering?", "and store state during rendering?", "store state during rendering?", "state during rendering?", "during rendering?", "rendering?"], "tokens": ["Can", "renderers", "use", "and", "store", "state", "during", "rendering", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1201539167", "fields": {"term": "What is the configuration file for enabling a renderer?", "namespace": "open-p", "hash": 1201539167, "terms": ["What is the configuration file for enabling a renderer?", "is the configuration file for enabling a renderer?", "the configuration file for enabling a renderer?", "configuration file for enabling a renderer?", "file for enabling a renderer?", "for enabling a renderer?", "enabling a renderer?", "a renderer?", "renderer?"], "tokens": ["What", "is", "the", "configuration", "file", "for", "enabling", "a", "renderer", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2038191701", "fields": {"term": "What is a renderer in Vespa?", "namespace": "open-p", "hash": 2038191701, "terms": ["What is a renderer in Vespa?", "is a renderer in Vespa?", "a renderer in Vespa?", "renderer in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["What", "is", "a", "renderer", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1370087316", "fields": {"term": "How does a renderer work?", "namespace": "open-p", "hash": -1370087316, "terms": ["How does a renderer work?", "does a renderer work?", "a renderer work?", "renderer work?", "work?"], "tokens": ["How", "does", "a", "renderer", "work", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1474914530", "fields": {"term": "What is the purpose of extending Renderer?", "namespace": "open-p", "hash": -1474914530, "terms": ["What is the purpose of extending Renderer?", "is the purpose of extending Renderer?", "the purpose of extending Renderer?", "purpose of extending Renderer?", "of extending Renderer?", "extending Renderer?", "Renderer?"], "tokens": ["What", "is", "the", "purpose", "of", "extending", "Renderer", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1146447029", "fields": {"term": "What are the entities rendered in a renderer?", "namespace": "open-p", "hash": 1146447029, "terms": ["What are the entities rendered in a renderer?", "are the entities rendered in a renderer?", "the entities rendered in a renderer?", "entities rendered in a renderer?", "rendered in a renderer?", "in a renderer?", "a renderer?", "renderer?"], "tokens": ["What", "are", "the", "entities", "rendered", "in", "a", "renderer", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1973048491", "fields": {"term": "Can you provide a simple example of a renderer?", "namespace": "open-p", "hash": -1973048491, "terms": ["Can you provide a simple example of a renderer?", "you provide a simple example of a renderer?", "provide a simple example of a renderer?", "a simple example of a renderer?", "simple example of a renderer?", "example of a renderer?", "of a renderer?", "a renderer?", "renderer?"], "tokens": ["Can", "you", "provide", "a", "simple", "example", "of", "a", "renderer", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-570407103", "fields": {"term": "Can you provide a more complex example of a renderer?", "namespace": "open-p", "hash": -570407103, "terms": ["Can you provide a more complex example of a renderer?", "you provide a more complex example of a renderer?", "provide a more complex example of a renderer?", "a more complex example of a renderer?", "more complex example of a renderer?", "complex example of a renderer?", "example of a renderer?", "of a renderer?", "a renderer?", "renderer?"], "tokens": ["Can", "you", "provide", "a", "more", "complex", "example", "of", "a", "renderer", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::272805150", "fields": {"term": "What is a SectionedRenderer subclass?", "namespace": "open-p", "hash": 272805150, "terms": ["What is a SectionedRenderer subclass?", "is a SectionedRenderer subclass?", "a SectionedRenderer subclass?", "SectionedRenderer subclass?", "subclass?"], "tokens": ["What", "is", "a", "SectionedRenderer", "subclass", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1423465232", "fields": {"term": "How to create a SectionedRenderer?", "namespace": "open-p", "hash": -1423465232, "terms": ["How to create a SectionedRenderer?", "to create a SectionedRenderer?", "create a SectionedRenderer?", "a SectionedRenderer?", "SectionedRenderer?"], "tokens": ["How", "to", "create", "a", "SectionedRenderer", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1894481827", "fields": {"term": "What are the methods for entities?", "namespace": "open-p", "hash": -1894481827, "terms": ["What are the methods for entities?", "are the methods for entities?", "the methods for entities?", "methods for entities?", "for entities?", "entities?"], "tokens": ["What", "are", "the", "methods", "for", "entities", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1941413847", "fields": {"term": "What callback methods are available?", "namespace": "open-p", "hash": 1941413847, "terms": ["What callback methods are available?", "callback methods are available?", "methods are available?", "are available?", "available?"], "tokens": ["What", "callback", "methods", "are", "available", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1529808537", "fields": {"term": "What is a compound entity?", "namespace": "open-p", "hash": -1529808537, "terms": ["What is a compound entity?", "is a compound entity?", "a compound entity?", "compound entity?", "entity?"], "tokens": ["What", "is", "a", "compound", "entity", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1170109823", "fields": {"term": "What is the result class?", "namespace": "open-p", "hash": -1170109823, "terms": ["What is the result class?", "is the result class?", "the result class?", "result class?", "class?"], "tokens": ["What", "is", "the", "result", "class", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1053369760", "fields": {"term": "What is the call sequence for entities?", "namespace": "open-p", "hash": 1053369760, "terms": ["What is the call sequence for entities?", "is the call sequence for entities?", "the call sequence for entities?", "call sequence for entities?", "sequence for entities?", "for entities?", "entities?"], "tokens": ["What", "is", "the", "call", "sequence", "for", "entities", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-962452647", "fields": {"term": "What are the subclasses of HitGroup?", "namespace": "open-p", "hash": -962452647, "terms": ["What are the subclasses of HitGroup?", "are the subclasses of HitGroup?", "the subclasses of HitGroup?", "subclasses of HitGroup?", "of HitGroup?", "HitGroup?"], "tokens": ["What", "are", "the", "subclasses", "of", "HitGroup", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1160094928", "fields": {"term": "What is the default JSON result format?", "namespace": "open-p", "hash": 1160094928, "terms": ["What is the default JSON result format?", "is the default JSON result format?", "the default JSON result format?", "default JSON result format?", "JSON result format?", "result format?", "format?"], "tokens": ["What", "is", "the", "default", "JSON", "result", "format", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::248792983", "fields": {"term": "What is MyRenderer?", "namespace": "open-p", "hash": 248792983, "terms": ["What is MyRenderer?", "is MyRenderer?", "MyRenderer?"], "tokens": ["What", "is", "MyRenderer", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1992420032", "fields": {"term": "What is SectionedRenderer?", "namespace": "open-p", "hash": 1992420032, "terms": ["What is SectionedRenderer?", "is SectionedRenderer?", "SectionedRenderer?"], "tokens": ["What", "is", "SectionedRenderer", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::617810452", "fields": {"term": "What is getResponse() used for?", "namespace": "open-p", "hash": 617810452, "terms": ["What is getResponse() used for?", "is getResponse() used for?", "getResponse() used for?", "used for?", "for?"], "tokens": ["What", "is", "getResponse", "()", "used", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-371585117", "fields": {"term": "Where can I find an example of DemoRenderer.java?", "namespace": "open-p", "hash": -371585117, "terms": ["Where can I find an example of DemoRenderer.java?", "can I find an example of DemoRenderer.java?", "I find an example of DemoRenderer.java?", "find an example of DemoRenderer.java?", "an example of DemoRenderer.java?", "example of DemoRenderer.java?", "of DemoRenderer.java?", "DemoRenderer.java?"], "tokens": ["Where", "can", "I", "find", "an", "example", "of", "DemoRenderer", "java", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-359960689", "fields": {"term": "What is routing used for?", "namespace": "open-p", "hash": -359960689, "terms": ["What is routing used for?", "is routing used for?", "routing used for?", "used for?", "for?"], "tokens": ["What", "is", "routing", "used", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1533237517", "fields": {"term": "When is explicit routing needed?", "namespace": "open-p", "hash": -1533237517, "terms": ["When is explicit routing needed?", "is explicit routing needed?", "explicit routing needed?", "routing needed?", "needed?"], "tokens": ["When", "is", "explicit", "routing", "needed", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-787983342", "fields": {"term": "What are routing policies?", "namespace": "open-p", "hash": -787983342, "terms": ["What are routing policies?", "are routing policies?", "routing policies?", "policies?"], "tokens": ["What", "are", "routing", "policies", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-675721723", "fields": {"term": "What is Message Bus?", "namespace": "open-p", "hash": -675721723, "terms": ["What is Message Bus?", "is Message Bus?", "Message Bus?", "Bus?"], "tokens": ["What", "is", "Message", "Bus", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::-1952668025", "fields": {"term": "What are the atoms in Vespa routing?", "namespace": "open-p", "hash": -1952668025, "terms": ["What are the atoms in Vespa routing?", "are the atoms in Vespa routing?", "the atoms in Vespa routing?", "atoms in Vespa routing?", "in Vespa routing?", "Vespa routing?", "routing?"], "tokens": ["What", "are", "the", "atoms", "in", "Vespa", "routing", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-22935310", "fields": {"term": "What is a route in Vespa?", "namespace": "open-p", "hash": -22935310, "terms": ["What is a route in Vespa?", "is a route in Vespa?", "a route in Vespa?", "route in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["What", "is", "a", "route", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::541087559", "fields": {"term": "What are hops and selectors?", "namespace": "open-p", "hash": 541087559, "terms": ["What are hops and selectors?", "are hops and selectors?", "hops and selectors?", "and selectors?", "selectors?"], "tokens": ["What", "are", "hops", "and", "selectors", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1364985914", "fields": {"term": "What is a hop?", "namespace": "open-p", "hash": 1364985914, "terms": ["What is a hop?", "is a hop?", "a hop?", "hop?"], "tokens": ["What", "is", "a", "hop", "?"], "corpus_count": 4, "document_count": 4}}, -{"put": "id:term:term::1088000645", "fields": {"term": "Can a route branch in Vespa?", "namespace": "open-p", "hash": 1088000645, "terms": ["Can a route branch in Vespa?", "a route branch in Vespa?", "route branch in Vespa?", "branch in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["Can", "a", "route", "branch", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-679181108", "fields": {"term": "Does Vespa support multicasting?", "namespace": "open-p", "hash": -679181108, "terms": ["Does Vespa support multicasting?", "Vespa support multicasting?", "support multicasting?", "multicasting?"], "tokens": ["Does", "Vespa", "support", "multicasting", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-97112873", "fields": {"term": "What does a node represent in Vespa?", "namespace": "open-p", "hash": -97112873, "terms": ["What does a node represent in Vespa?", "does a node represent in Vespa?", "a node represent in Vespa?", "node represent in Vespa?", "represent in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["What", "does", "a", "node", "represent", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1786520030", "fields": {"term": "How does Message Bus support routing?", "namespace": "open-p", "hash": 1786520030, "terms": ["How does Message Bus support routing?", "does Message Bus support routing?", "Message Bus support routing?", "Bus support routing?", "support routing?", "routing?"], "tokens": ["How", "does", "Message", "Bus", "support", "routing", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1939099745", "fields": {"term": "How can a hop modify a message's journey?", "namespace": "open-p", "hash": -1939099745, "terms": ["How can a hop modify a message's journey?", "can a hop modify a message's journey?", "a hop modify a message's journey?", "hop modify a message's journey?", "modify a message's journey?", "a message's journey?", "message's journey?", "journey?"], "tokens": ["How", "can", "a", "hop", "modify", "a", "message", "'", "s", "journey", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1933844155", "fields": {"term": "What is a recipient?", "namespace": "open-p", "hash": -1933844155, "terms": ["What is a recipient?", "is a recipient?", "a recipient?", "recipient?"], "tokens": ["What", "is", "a", "recipient", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-255439001", "fields": {"term": "What is a selector?", "namespace": "open-p", "hash": -255439001, "terms": ["What is a selector?", "is a selector?", "a selector?", "selector?"], "tokens": ["What", "is", "a", "selector", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2057800723", "fields": {"term": "What is a Message Bus?", "namespace": "open-p", "hash": -2057800723, "terms": ["What is a Message Bus?", "is a Message Bus?", "a Message Bus?", "Message Bus?", "Bus?"], "tokens": ["What", "is", "a", "Message", "Bus", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::817853466", "fields": {"term": "What is the purpose of Vespa's service location broker?", "namespace": "open-p", "hash": 817853466, "terms": ["What is the purpose of Vespa's service location broker?", "is the purpose of Vespa's service location broker?", "the purpose of Vespa's service location broker?", "purpose of Vespa's service location broker?", "of Vespa's service location broker?", "Vespa's service location broker?", "service location broker?", "location broker?", "broker?"], "tokens": ["What", "is", "the", "purpose", "of", "Vespa", "'", "s", "service", "location", "broker", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::525091047", "fields": {"term": "What is the derivation logic for recipient service names?", "namespace": "open-p", "hash": 525091047, "terms": ["What is the derivation logic for recipient service names?", "is the derivation logic for recipient service names?", "the derivation logic for recipient service names?", "derivation logic for recipient service names?", "logic for recipient service names?", "for recipient service names?", "recipient service names?", "service names?", "names?"], "tokens": ["What", "is", "the", "derivation", "logic", "for", "recipient", "service", "names", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::590018616", "fields": {"term": "How can you inspect symbolic names in Vespa?", "namespace": "open-p", "hash": 590018616, "terms": ["How can you inspect symbolic names in Vespa?", "can you inspect symbolic names in Vespa?", "you inspect symbolic names in Vespa?", "inspect symbolic names in Vespa?", "symbolic names in Vespa?", "names in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["How", "can", "you", "inspect", "symbolic", "names", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2000532549", "fields": {"term": "What is a service identifier?", "namespace": "open-p", "hash": -2000532549, "terms": ["What is a service identifier?", "is a service identifier?", "a service identifier?", "service identifier?", "identifier?"], "tokens": ["What", "is", "a", "service", "identifier", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1301711127", "fields": {"term": "What happens when a recipient contains *?", "namespace": "open-p", "hash": -1301711127, "terms": ["What happens when a recipient contains *?", "happens when a recipient contains *?", "when a recipient contains *?", "a recipient contains *?", "recipient contains *?", "contains *?", "*?"], "tokens": ["What", "happens", "when", "a", "recipient", "contains", "*?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1905184041", "fields": {"term": "What is a routing policy?", "namespace": "open-p", "hash": -1905184041, "terms": ["What is a routing policy?", "is a routing policy?", "a routing policy?", "routing policy?", "policy?"], "tokens": ["What", "is", "a", "routing", "policy", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::130308956", "fields": {"term": "What does a routing policy do?", "namespace": "open-p", "hash": 130308956, "terms": ["What does a routing policy do?", "does a routing policy do?", "a routing policy do?", "routing policy do?", "policy do?", "do?"], "tokens": ["What", "does", "a", "routing", "policy", "do", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::619719662", "fields": {"term": "What is the purpose of a routing policy?", "namespace": "open-p", "hash": 619719662, "terms": ["What is the purpose of a routing policy?", "is the purpose of a routing policy?", "the purpose of a routing policy?", "purpose of a routing policy?", "of a routing policy?", "a routing policy?", "routing policy?", "policy?"], "tokens": ["What", "is", "the", "purpose", "of", "a", "routing", "policy", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-640228163", "fields": {"term": "How is merging implemented in a routing policy?", "namespace": "open-p", "hash": -640228163, "terms": ["How is merging implemented in a routing policy?", "is merging implemented in a routing policy?", "merging implemented in a routing policy?", "implemented in a routing policy?", "in a routing policy?", "a routing policy?", "routing policy?", "policy?"], "tokens": ["How", "is", "merging", "implemented", "in", "a", "routing", "policy", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-250821381", "fields": {"term": "What is the 'RoutingContext' object?", "namespace": "open-p", "hash": -250821381, "terms": ["What is the 'RoutingContext' object?", "is the 'RoutingContext' object?", "the 'RoutingContext' object?", "'RoutingContext' object?", "object?"], "tokens": ["What", "is", "the", "'", "RoutingContext", "'", "object", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1923267174", "fields": {"term": "What is the message guarantee of Message Bus?", "namespace": "open-p", "hash": 1923267174, "terms": ["What is the message guarantee of Message Bus?", "is the message guarantee of Message Bus?", "the message guarantee of Message Bus?", "message guarantee of Message Bus?", "guarantee of Message Bus?", "of Message Bus?", "Message Bus?", "Bus?"], "tokens": ["What", "is", "the", "message", "guarantee", "of", "Message", "Bus", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1859223873", "fields": {"term": "What is the routing policy reference?", "namespace": "open-p", "hash": -1859223873, "terms": ["What is the routing policy reference?", "is the routing policy reference?", "the routing policy reference?", "routing policy reference?", "policy reference?", "reference?"], "tokens": ["What", "is", "the", "routing", "policy", "reference", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1153444288", "fields": {"term": "What are the components of a Message Bus protocol?", "namespace": "open-p", "hash": 1153444288, "terms": ["What are the components of a Message Bus protocol?", "are the components of a Message Bus protocol?", "the components of a Message Bus protocol?", "components of a Message Bus protocol?", "of a Message Bus protocol?", "a Message Bus protocol?", "Message Bus protocol?", "Bus protocol?", "protocol?"], "tokens": ["What", "are", "the", "components", "of", "a", "Message", "Bus", "protocol", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1816865174", "fields": {"term": "How does Message Bus resolve recipients?", "namespace": "open-p", "hash": 1816865174, "terms": ["How does Message Bus resolve recipients?", "does Message Bus resolve recipients?", "Message Bus resolve recipients?", "Bus resolve recipients?", "resolve recipients?", "recipients?"], "tokens": ["How", "does", "Message", "Bus", "resolve", "recipients", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::437080872", "fields": {"term": "What happens if there are circular dependencies in Message Bus?", "namespace": "open-p", "hash": 437080872, "terms": ["What happens if there are circular dependencies in Message Bus?", "happens if there are circular dependencies in Message Bus?", "if there are circular dependencies in Message Bus?", "there are circular dependencies in Message Bus?", "are circular dependencies in Message Bus?", "circular dependencies in Message Bus?", "dependencies in Message Bus?", "in Message Bus?", "Message Bus?", "Bus?"], "tokens": ["What", "happens", "if", "there", "are", "circular", "dependencies", "in", "Message", "Bus", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1157173839", "fields": {"term": "What is the purpose of policy directives in Message Bus?", "namespace": "open-p", "hash": -1157173839, "terms": ["What is the purpose of policy directives in Message Bus?", "is the purpose of policy directives in Message Bus?", "the purpose of policy directives in Message Bus?", "purpose of policy directives in Message Bus?", "of policy directives in Message Bus?", "policy directives in Message Bus?", "directives in Message Bus?", "in Message Bus?", "Message Bus?", "Bus?"], "tokens": ["What", "is", "the", "purpose", "of", "policy", "directives", "in", "Message", "Bus", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-269656472", "fields": {"term": "How does Message Bus send messages to recipients?", "namespace": "open-p", "hash": -269656472, "terms": ["How does Message Bus send messages to recipients?", "does Message Bus send messages to recipients?", "Message Bus send messages to recipients?", "Bus send messages to recipients?", "send messages to recipients?", "messages to recipients?", "to recipients?", "recipients?"], "tokens": ["How", "does", "Message", "Bus", "send", "messages", "to", "recipients", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1282714119", "fields": {"term": "What is the routing table in Message Bus?", "namespace": "open-p", "hash": -1282714119, "terms": ["What is the routing table in Message Bus?", "is the routing table in Message Bus?", "the routing table in Message Bus?", "routing table in Message Bus?", "table in Message Bus?", "in Message Bus?", "Message Bus?", "Bus?"], "tokens": ["What", "is", "the", "routing", "table", "in", "Message", "Bus", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-321508886", "fields": {"term": "Does Message Bus support complex routing trees?", "namespace": "open-p", "hash": -321508886, "terms": ["Does Message Bus support complex routing trees?", "Message Bus support complex routing trees?", "Bus support complex routing trees?", "support complex routing trees?", "complex routing trees?", "routing trees?", "trees?"], "tokens": ["Does", "Message", "Bus", "support", "complex", "routing", "trees", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-124957754", "fields": {"term": "What are the safeguards in Message Bus?", "namespace": "open-p", "hash": -124957754, "terms": ["What are the safeguards in Message Bus?", "are the safeguards in Message Bus?", "the safeguards in Message Bus?", "safeguards in Message Bus?", "in Message Bus?", "Message Bus?", "Bus?"], "tokens": ["What", "are", "the", "safeguards", "in", "Message", "Bus", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1359642838", "fields": {"term": "What is the logic run?", "namespace": "open-p", "hash": 1359642838, "terms": ["What is the logic run?", "is the logic run?", "the logic run?", "logic run?", "run?"], "tokens": ["What", "is", "the", "logic", "run", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-805808674", "fields": {"term": "What is a policy directive?", "namespace": "open-p", "hash": -805808674, "terms": ["What is a policy directive?", "is a policy directive?", "a policy directive?", "policy directive?", "directive?"], "tokens": ["What", "is", "a", "policy", "directive", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1010838905", "fields": {"term": "What is the hop string?", "namespace": "open-p", "hash": -1010838905, "terms": ["What is the hop string?", "is the hop string?", "the hop string?", "hop string?", "string?"], "tokens": ["What", "is", "the", "hop", "string", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1245044096", "fields": {"term": "What is a recipient in hop?", "namespace": "open-p", "hash": 1245044096, "terms": ["What is a recipient in hop?", "is a recipient in hop?", "a recipient in hop?", "recipient in hop?", "in hop?", "hop?"], "tokens": ["What", "is", "a", "recipient", "in", "hop", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::702956756", "fields": {"term": "What is the purpose of DocumentRouteSelector?", "namespace": "open-p", "hash": 702956756, "terms": ["What is the purpose of DocumentRouteSelector?", "is the purpose of DocumentRouteSelector?", "the purpose of DocumentRouteSelector?", "purpose of DocumentRouteSelector?", "of DocumentRouteSelector?", "DocumentRouteSelector?"], "tokens": ["What", "is", "the", "purpose", "of", "DocumentRouteSelector", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::565152693", "fields": {"term": "What is merge() method?", "namespace": "open-p", "hash": 565152693, "terms": ["What is merge() method?", "is merge() method?", "merge() method?", "method?"], "tokens": ["What", "is", "merge", "()", "method", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-299921868", "fields": {"term": "What is AND policy?", "namespace": "open-p", "hash": -299921868, "terms": ["What is AND policy?", "is AND policy?", "AND policy?", "policy?"], "tokens": ["What", "is", "AND", "policy", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-256687878", "fields": {"term": "What does the policies article explain?", "namespace": "open-p", "hash": -256687878, "terms": ["What does the policies article explain?", "does the policies article explain?", "the policies article explain?", "policies article explain?", "article explain?", "explain?"], "tokens": ["What", "does", "the", "policies", "article", "explain", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::869662583", "fields": {"term": "What does Message Bus do?", "namespace": "open-p", "hash": 869662583, "terms": ["What does Message Bus do?", "does Message Bus do?", "Message Bus do?", "Bus do?", "do?"], "tokens": ["What", "does", "Message", "Bus", "do", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2091646019", "fields": {"term": "What happens when a string is recognized as a hop name?", "namespace": "open-p", "hash": 2091646019, "terms": ["What happens when a string is recognized as a hop name?", "happens when a string is recognized as a hop name?", "when a string is recognized as a hop name?", "a string is recognized as a hop name?", "string is recognized as a hop name?", "is recognized as a hop name?", "recognized as a hop name?", "as a hop name?", "a hop name?", "hop name?", "name?"], "tokens": ["What", "happens", "when", "a", "string", "is", "recognized", "as", "a", "hop", "name", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-248956580", "fields": {"term": "What is the outcome if the string is recognized as a service name?", "namespace": "open-p", "hash": -248956580, "terms": ["What is the outcome if the string is recognized as a service name?", "is the outcome if the string is recognized as a service name?", "the outcome if the string is recognized as a service name?", "outcome if the string is recognized as a service name?", "if the string is recognized as a service name?", "the string is recognized as a service name?", "string is recognized as a service name?", "is recognized as a service name?", "recognized as a service name?", "as a service name?", "a service name?", "service name?", "name?"], "tokens": ["What", "is", "the", "outcome", "if", "the", "string", "is", "recognized", "as", "a", "service", "name", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1227278103", "fields": {"term": "What is the purpose of the 'route:' prefix in Message Bus?", "namespace": "open-p", "hash": 1227278103, "terms": ["What is the purpose of the 'route:' prefix in Message Bus?", "is the purpose of the 'route:' prefix in Message Bus?", "the purpose of the 'route:' prefix in Message Bus?", "purpose of the 'route:' prefix in Message Bus?", "of the 'route:' prefix in Message Bus?", "the 'route:' prefix in Message Bus?", "'route:' prefix in Message Bus?", "prefix in Message Bus?", "in Message Bus?", "Message Bus?", "Bus?"], "tokens": ["What", "is", "the", "purpose", "of", "the", "'", "route", ":'", "prefix", "in", "Message", "Bus", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-470090037", "fields": {"term": "What is the order of checking for string recognition in Message Bus?", "namespace": "open-p", "hash": -470090037, "terms": ["What is the order of checking for string recognition in Message Bus?", "is the order of checking for string recognition in Message Bus?", "the order of checking for string recognition in Message Bus?", "order of checking for string recognition in Message Bus?", "of checking for string recognition in Message Bus?", "checking for string recognition in Message Bus?", "for string recognition in Message Bus?", "string recognition in Message Bus?", "recognition in Message Bus?", "in Message Bus?", "Message Bus?", "Bus?"], "tokens": ["What", "is", "the", "order", "of", "checking", "for", "string", "recognition", "in", "Message", "Bus", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::97962956", "fields": {"term": "What happens at this route resolver point?", "namespace": "open-p", "hash": 97962956, "terms": ["What happens at this route resolver point?", "happens at this route resolver point?", "at this route resolver point?", "this route resolver point?", "route resolver point?", "resolver point?", "point?"], "tokens": ["What", "happens", "at", "this", "route", "resolver", "point", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1225748402", "fields": {"term": "What does each leaf node represent?", "namespace": "open-p", "hash": -1225748402, "terms": ["What does each leaf node represent?", "does each leaf node represent?", "each leaf node represent?", "leaf node represent?", "node represent?", "represent?"], "tokens": ["What", "does", "each", "leaf", "node", "represent", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-554214380", "fields": {"term": "How is message transmission optimized?", "namespace": "open-p", "hash": -554214380, "terms": ["How is message transmission optimized?", "is message transmission optimized?", "message transmission optimized?", "transmission optimized?", "optimized?"], "tokens": ["How", "is", "message", "transmission", "optimized", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::285079119", "fields": {"term": "When does route resolving occur?", "namespace": "open-p", "hash": 285079119, "terms": ["When does route resolving occur?", "does route resolving occur?", "route resolving occur?", "resolving occur?", "occur?"], "tokens": ["When", "does", "route", "resolving", "occur", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1374767524", "fields": {"term": "What happens if route configuration changes?", "namespace": "open-p", "hash": 1374767524, "terms": ["What happens if route configuration changes?", "happens if route configuration changes?", "if route configuration changes?", "route configuration changes?", "configuration changes?", "changes?"], "tokens": ["What", "happens", "if", "route", "configuration", "changes", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1130274593", "fields": {"term": "What is a hop that ignores results?", "namespace": "open-p", "hash": -1130274593, "terms": ["What is a hop that ignores results?", "is a hop that ignores results?", "a hop that ignores results?", "hop that ignores results?", "that ignores results?", "ignores results?", "results?"], "tokens": ["What", "is", "a", "hop", "that", "ignores", "results", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-355246384", "fields": {"term": "What is Document API?", "namespace": "open-p", "hash": -355246384, "terms": ["What is Document API?", "is Document API?", "Document API?", "API?"], "tokens": ["What", "is", "Document", "API", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::1455694370", "fields": {"term": "What is MessageBusDocumentAccessParams?", "namespace": "open-p", "hash": 1455694370, "terms": ["What is MessageBusDocumentAccessParams?", "is MessageBusDocumentAccessParams?", "MessageBusDocumentAccessParams?"], "tokens": ["What", "is", "MessageBusDocumentAccessParams", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1510555190", "fields": {"term": "What is the purpose of routeName?", "namespace": "open-p", "hash": 1510555190, "terms": ["What is the purpose of routeName?", "is the purpose of routeName?", "the purpose of routeName?", "purpose of routeName?", "of routeName?", "routeName?"], "tokens": ["What", "is", "the", "purpose", "of", "routeName", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1400569382", "fields": {"term": "What is the purpose of traceLevel?", "namespace": "open-p", "hash": -1400569382, "terms": ["What is the purpose of traceLevel?", "is the purpose of traceLevel?", "the purpose of traceLevel?", "purpose of traceLevel?", "of traceLevel?", "traceLevel?"], "tokens": ["What", "is", "the", "purpose", "of", "traceLevel", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::40280055", "fields": {"term": "What is the Document protocol?", "namespace": "open-p", "hash": 40280055, "terms": ["What is the Document protocol?", "is the Document protocol?", "the Document protocol?", "Document protocol?", "protocol?"], "tokens": ["What", "is", "the", "Document", "protocol", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-748291775", "fields": {"term": "What is the AND policy?", "namespace": "open-p", "hash": -748291775, "terms": ["What is the AND policy?", "is the AND policy?", "the AND policy?", "AND policy?", "policy?"], "tokens": ["What", "is", "the", "AND", "policy", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1561233349", "fields": {"term": "What is the Content policy?", "namespace": "open-p", "hash": 1561233349, "terms": ["What is the Content policy?", "is the Content policy?", "the Content policy?", "Content policy?", "policy?"], "tokens": ["What", "is", "the", "Content", "policy", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1658009199", "fields": {"term": "What is the shared merge logic?", "namespace": "open-p", "hash": -1658009199, "terms": ["What is the shared merge logic?", "is the shared merge logic?", "the shared merge logic?", "shared merge logic?", "merge logic?", "logic?"], "tokens": ["What", "is", "the", "shared", "merge", "logic", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-600464273", "fields": {"term": "How does the merge function work?", "namespace": "open-p", "hash": -600464273, "terms": ["How does the merge function work?", "does the merge function work?", "the merge function work?", "merge function work?", "function work?", "work?"], "tokens": ["How", "does", "the", "merge", "function", "work", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1091413490", "fields": {"term": "What happens if one error reply is found?", "namespace": "open-p", "hash": 1091413490, "terms": ["What happens if one error reply is found?", "happens if one error reply is found?", "if one error reply is found?", "one error reply is found?", "error reply is found?", "reply is found?", "is found?", "found?"], "tokens": ["What", "happens", "if", "one", "error", "reply", "is", "found", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1178457291", "fields": {"term": "What happens if at least one OK reply is found?", "namespace": "open-p", "hash": -1178457291, "terms": ["What happens if at least one OK reply is found?", "happens if at least one OK reply is found?", "if at least one OK reply is found?", "at least one OK reply is found?", "least one OK reply is found?", "one OK reply is found?", "OK reply is found?", "reply is found?", "is found?", "found?"], "tokens": ["What", "happens", "if", "at", "least", "one", "OK", "reply", "is", "found", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1554402314", "fields": {"term": "What is the pseudocode for the merge function?", "namespace": "open-p", "hash": -1554402314, "terms": ["What is the pseudocode for the merge function?", "is the pseudocode for the merge function?", "the pseudocode for the merge function?", "pseudocode for the merge function?", "for the merge function?", "the merge function?", "merge function?", "function?"], "tokens": ["What", "is", "the", "pseudocode", "for", "the", "merge", "function", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-540478595", "fields": {"term": "What is stored in the OK list?", "namespace": "open-p", "hash": -540478595, "terms": ["What is stored in the OK list?", "is stored in the OK list?", "stored in the OK list?", "in the OK list?", "the OK list?", "OK list?", "list?"], "tokens": ["What", "is", "stored", "in", "the", "OK", "list", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2046700624", "fields": {"term": "What happens if there is no OK reply?", "namespace": "open-p", "hash": 2046700624, "terms": ["What happens if there is no OK reply?", "happens if there is no OK reply?", "if there is no OK reply?", "there is no OK reply?", "is no OK reply?", "no OK reply?", "OK reply?", "reply?"], "tokens": ["What", "happens", "if", "there", "is", "no", "OK", "reply", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::450690156", "fields": {"term": "What is the DocumentRouteSelector policy?", "namespace": "open-p", "hash": 450690156, "terms": ["What is the DocumentRouteSelector policy?", "is the DocumentRouteSelector policy?", "the DocumentRouteSelector policy?", "DocumentRouteSelector policy?", "policy?"], "tokens": ["What", "is", "the", "DocumentRouteSelector", "policy", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1245580570", "fields": {"term": "What is the Extern policy for?", "namespace": "open-p", "hash": 1245580570, "terms": ["What is the Extern policy for?", "is the Extern policy for?", "the Extern policy for?", "Extern policy for?", "policy for?", "for?"], "tokens": ["What", "is", "the", "Extern", "policy", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1195131418", "fields": {"term": "What is the LoadBalancer policy for?", "namespace": "open-p", "hash": -1195131418, "terms": ["What is the LoadBalancer policy for?", "is the LoadBalancer policy for?", "the LoadBalancer policy for?", "LoadBalancer policy for?", "policy for?", "for?"], "tokens": ["What", "is", "the", "LoadBalancer", "policy", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::992907991", "fields": {"term": "What parameters does the Content policy support?", "namespace": "open-p", "hash": 992907991, "terms": ["What parameters does the Content policy support?", "parameters does the Content policy support?", "does the Content policy support?", "the Content policy support?", "Content policy support?", "policy support?", "support?"], "tokens": ["What", "parameters", "does", "the", "Content", "policy", "support", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1200315217", "fields": {"term": "What is the LocalService policy for?", "namespace": "open-p", "hash": 1200315217, "terms": ["What is the LocalService policy for?", "is the LocalService policy for?", "the LocalService policy for?", "LocalService policy for?", "policy for?", "for?"], "tokens": ["What", "is", "the", "LocalService", "policy", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::88741636", "fields": {"term": "What is the SubsetService policy for?", "namespace": "open-p", "hash": 88741636, "terms": ["What is the SubsetService policy for?", "is the SubsetService policy for?", "the SubsetService policy for?", "SubsetService policy for?", "policy for?", "for?"], "tokens": ["What", "is", "the", "SubsetService", "policy", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1211194010", "fields": {"term": "What is the function of the RoundRobin policy?", "namespace": "open-p", "hash": -1211194010, "terms": ["What is the function of the RoundRobin policy?", "is the function of the RoundRobin policy?", "the function of the RoundRobin policy?", "function of the RoundRobin policy?", "of the RoundRobin policy?", "the RoundRobin policy?", "RoundRobin policy?", "policy?"], "tokens": ["What", "is", "the", "function", "of", "the", "RoundRobin", "policy", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1822349213", "fields": {"term": "What is the MessageType policy used for?", "namespace": "open-p", "hash": -1822349213, "terms": ["What is the MessageType policy used for?", "is the MessageType policy used for?", "the MessageType policy used for?", "MessageType policy used for?", "policy used for?", "used for?", "for?"], "tokens": ["What", "is", "the", "MessageType", "policy", "used", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1883877501", "fields": {"term": "What is the document type syntax?", "namespace": "open-p", "hash": 1883877501, "terms": ["What is the document type syntax?", "is the document type syntax?", "the document type syntax?", "document type syntax?", "type syntax?", "syntax?"], "tokens": ["What", "is", "the", "document", "type", "syntax", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-271422213", "fields": {"term": "What does a string in square brackets represent?", "namespace": "open-p", "hash": -271422213, "terms": ["What does a string in square brackets represent?", "does a string in square brackets represent?", "a string in square brackets represent?", "string in square brackets represent?", "in square brackets represent?", "square brackets represent?", "brackets represent?", "represent?"], "tokens": ["What", "does", "a", "string", "in", "square", "brackets", "represent", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-998289013", "fields": {"term": "What are the constructs in schemas?", "namespace": "open-p", "hash": -998289013, "terms": ["What are the constructs in schemas?", "are the constructs in schemas?", "the constructs in schemas?", "constructs in schemas?", "in schemas?", "schemas?"], "tokens": ["What", "are", "the", "constructs", "in", "schemas", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1046972984", "fields": {"term": "What is the syntax for an element?", "namespace": "open-p", "hash": -1046972984, "terms": ["What is the syntax for an element?", "is the syntax for an element?", "the syntax for an element?", "syntax for an element?", "for an element?", "an element?", "element?"], "tokens": ["What", "is", "the", "syntax", "for", "an", "element", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-947375431", "fields": {"term": "What is a multiline value?", "namespace": "open-p", "hash": -947375431, "terms": ["What is a multiline value?", "is a multiline value?", "a multiline value?", "multiline value?", "value?"], "tokens": ["What", "is", "a", "multiline", "value", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1893337079", "fields": {"term": "What is a constant?", "namespace": "open-p", "hash": 1893337079, "terms": ["What is a constant?", "is a constant?", "a constant?", "constant?"], "tokens": ["What", "is", "a", "constant", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::474397215", "fields": {"term": "What is a stemming setting?", "namespace": "open-p", "hash": 474397215, "terms": ["What is a stemming setting?", "is a stemming setting?", "a stemming setting?", "stemming setting?", "setting?"], "tokens": ["What", "is", "a", "stemming", "setting", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2085185141", "fields": {"term": "What is document inheritance?", "namespace": "open-p", "hash": -2085185141, "terms": ["What is document inheritance?", "is document inheritance?", "document inheritance?", "inheritance?"], "tokens": ["What", "is", "document", "inheritance", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1118718481", "fields": {"term": "What is compression?", "namespace": "open-p", "hash": 1118718481, "terms": ["What is compression?", "is compression?", "compression?"], "tokens": ["What", "is", "compression", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1728137182", "fields": {"term": "What is the default document name?", "namespace": "open-p", "hash": -1728137182, "terms": ["What is the default document name?", "is the default document name?", "the default document name?", "default document name?", "document name?", "name?"], "tokens": ["What", "is", "the", "default", "document", "name", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1481779411", "fields": {"term": "What is a struct?", "namespace": "open-p", "hash": 1481779411, "terms": ["What is a struct?", "is a struct?", "a struct?", "struct?"], "tokens": ["What", "is", "a", "struct", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::270980406", "fields": {"term": "What is a composite type?", "namespace": "open-p", "hash": 270980406, "terms": ["What is a composite type?", "is a composite type?", "a composite type?", "composite type?", "type?"], "tokens": ["What", "is", "a", "composite", "type", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::146655289", "fields": {"term": "How do you define a struct?", "namespace": "open-p", "hash": 146655289, "terms": ["How do you define a struct?", "do you define a struct?", "you define a struct?", "define a struct?", "a struct?", "struct?"], "tokens": ["How", "do", "you", "define", "a", "struct", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::691829330", "fields": {"term": "What is a field in a struct?", "namespace": "open-p", "hash": 691829330, "terms": ["What is a field in a struct?", "is a field in a struct?", "a field in a struct?", "field in a struct?", "in a struct?", "a struct?", "struct?"], "tokens": ["What", "is", "a", "field", "in", "a", "struct", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1548287588", "fields": {"term": "How many fields can a struct have?", "namespace": "open-p", "hash": -1548287588, "terms": ["How many fields can a struct have?", "many fields can a struct have?", "fields can a struct have?", "can a struct have?", "a struct have?", "struct have?", "have?"], "tokens": ["How", "many", "fields", "can", "a", "struct", "have", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-461700710", "fields": {"term": "What are some Vespa field types?", "namespace": "open-p", "hash": -461700710, "terms": ["What are some Vespa field types?", "are some Vespa field types?", "some Vespa field types?", "Vespa field types?", "field types?", "types?"], "tokens": ["What", "are", "some", "Vespa", "field", "types", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1376036193", "fields": {"term": "What is a struct-field?", "namespace": "open-p", "hash": -1376036193, "terms": ["What is a struct-field?", "is a struct-field?", "a struct-field?", "struct-field?"], "tokens": ["What", "is", "a", "struct", "field", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1175990794", "fields": {"term": "What is the body of a struct-field?", "namespace": "open-p", "hash": 1175990794, "terms": ["What is the body of a struct-field?", "is the body of a struct-field?", "the body of a struct-field?", "body of a struct-field?", "of a struct-field?", "a struct-field?", "struct-field?"], "tokens": ["What", "is", "the", "body", "of", "a", "struct", "field", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1821941927", "fields": {"term": "What is indexing in struct fields?", "namespace": "open-p", "hash": 1821941927, "terms": ["What is indexing in struct fields?", "is indexing in struct fields?", "indexing in struct fields?", "in struct fields?", "struct fields?", "fields?"], "tokens": ["What", "is", "indexing", "in", "struct", "fields", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-598534776", "fields": {"term": "What is attribute in struct fields?", "namespace": "open-p", "hash": -598534776, "terms": ["What is attribute in struct fields?", "is attribute in struct fields?", "attribute in struct fields?", "in struct fields?", "struct fields?", "fields?"], "tokens": ["What", "is", "attribute", "in", "struct", "fields", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1956217850", "fields": {"term": "How do fieldsets work?", "namespace": "open-p", "hash": 1956217850, "terms": ["How do fieldsets work?", "do fieldsets work?", "fieldsets work?", "work?"], "tokens": ["How", "do", "fieldsets", "work", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1473895657", "fields": {"term": "What is the purpose of a fieldset?", "namespace": "open-p", "hash": -1473895657, "terms": ["What is the purpose of a fieldset?", "is the purpose of a fieldset?", "the purpose of a fieldset?", "purpose of a fieldset?", "of a fieldset?", "a fieldset?", "fieldset?"], "tokens": ["What", "is", "the", "purpose", "of", "a", "fieldset", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-504219319", "fields": {"term": "Can a fieldset have different match modes?", "namespace": "open-p", "hash": -504219319, "terms": ["Can a fieldset have different match modes?", "a fieldset have different match modes?", "fieldset have different match modes?", "have different match modes?", "different match modes?", "match modes?", "modes?"], "tokens": ["Can", "a", "fieldset", "have", "different", "match", "modes", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1780871322", "fields": {"term": "What query returns all documents containing a fieldset with a specific value?", "namespace": "open-p", "hash": -1780871322, "terms": ["What query returns all documents containing a fieldset with a specific value?", "query returns all documents containing a fieldset with a specific value?", "returns all documents containing a fieldset with a specific value?", "all documents containing a fieldset with a specific value?", "documents containing a fieldset with a specific value?", "containing a fieldset with a specific value?", "a fieldset with a specific value?", "fieldset with a specific value?", "with a specific value?", "a specific value?", "specific value?", "value?"], "tokens": ["What", "query", "returns", "all", "documents", "containing", "a", "fieldset", "with", "a", "specific", "value", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2004297917", "fields": {"term": "Does creating a fieldset create extra index structures?", "namespace": "open-p", "hash": -2004297917, "terms": ["Does creating a fieldset create extra index structures?", "creating a fieldset create extra index structures?", "a fieldset create extra index structures?", "fieldset create extra index structures?", "create extra index structures?", "extra index structures?", "index structures?", "structures?"], "tokens": ["Does", "creating", "a", "fieldset", "create", "extra", "index", "structures", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::939299714", "fields": {"term": "How can specific match settings be applied to a fieldset?", "namespace": "open-p", "hash": 939299714, "terms": ["How can specific match settings be applied to a fieldset?", "can specific match settings be applied to a fieldset?", "specific match settings be applied to a fieldset?", "match settings be applied to a fieldset?", "settings be applied to a fieldset?", "be applied to a fieldset?", "applied to a fieldset?", "to a fieldset?", "a fieldset?", "fieldset?"], "tokens": ["How", "can", "specific", "match", "settings", "be", "applied", "to", "a", "fieldset", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1340813182", "fields": {"term": "Can query-commands be used with a fieldset?", "namespace": "open-p", "hash": 1340813182, "terms": ["Can query-commands be used with a fieldset?", "query-commands be used with a fieldset?", "be used with a fieldset?", "used with a fieldset?", "with a fieldset?", "a fieldset?", "fieldset?"], "tokens": ["Can", "query", "commands", "be", "used", "with", "a", "fieldset", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1821966176", "fields": {"term": "What is enabled by lz4 compression?", "namespace": "open-p", "hash": 1821966176, "terms": ["What is enabled by lz4 compression?", "is enabled by lz4 compression?", "enabled by lz4 compression?", "by lz4 compression?", "lz4 compression?", "compression?"], "tokens": ["What", "is", "enabled", "by", "lz4", "compression", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1403614931", "fields": {"term": "What is the compression type?", "namespace": "open-p", "hash": 1403614931, "terms": ["What is the compression type?", "is the compression type?", "the compression type?", "compression type?", "type?"], "tokens": ["What", "is", "the", "compression", "type", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-308692508", "fields": {"term": "What is the default threshold?", "namespace": "open-p", "hash": -308692508, "terms": ["What is the default threshold?", "is the default threshold?", "the default threshold?", "default threshold?", "threshold?"], "tokens": ["What", "is", "the", "default", "threshold", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1293344532", "fields": {"term": "What is a rank profile?", "namespace": "open-p", "hash": 1293344532, "terms": ["What is a rank profile?", "is a rank profile?", "a rank profile?", "rank profile?", "profile?"], "tokens": ["What", "is", "a", "rank", "profile", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::186004043", "fields": {"term": "How to define rank profiles?", "namespace": "open-p", "hash": 186004043, "terms": ["How to define rank profiles?", "to define rank profiles?", "define rank profiles?", "rank profiles?", "profiles?"], "tokens": ["How", "to", "define", "rank", "profiles", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::99658859", "fields": {"term": "What is match-phase feature?", "namespace": "open-p", "hash": 99658859, "terms": ["What is match-phase feature?", "is match-phase feature?", "match-phase feature?", "feature?"], "tokens": ["What", "is", "match", "phase", "feature", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::971527308", "fields": {"term": "What is max-hits field?", "namespace": "open-p", "hash": 971527308, "terms": ["What is max-hits field?", "is max-hits field?", "max-hits field?", "field?"], "tokens": ["What", "is", "max", "hits", "field", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1753030793", "fields": {"term": "What is diversity field?", "namespace": "open-p", "hash": -1753030793, "terms": ["What is diversity field?", "is diversity field?", "diversity field?", "field?"], "tokens": ["What", "is", "diversity", "field", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::97346337", "fields": {"term": "How does match-phase increase performance?", "namespace": "open-p", "hash": 97346337, "terms": ["How does match-phase increase performance?", "does match-phase increase performance?", "match-phase increase performance?", "increase performance?", "performance?"], "tokens": ["How", "does", "match", "phase", "increase", "performance", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-779865029", "fields": {"term": "What is graceful degradation?", "namespace": "open-p", "hash": -779865029, "terms": ["What is graceful degradation?", "is graceful degradation?", "graceful degradation?", "degradation?"], "tokens": ["What", "is", "graceful", "degradation", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::-331848151", "fields": {"term": "What is loose type checking?", "namespace": "open-p", "hash": -331848151, "terms": ["What is loose type checking?", "is loose type checking?", "loose type checking?", "type checking?", "checking?"], "tokens": ["What", "is", "loose", "type", "checking", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1081680404", "fields": {"term": "What is strict?", "namespace": "open-p", "hash": 1081680404, "terms": ["What is strict?", "is strict?", "strict?"], "tokens": ["What", "is", "strict", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1588872423", "fields": {"term": "What is diversity in Vespa?", "namespace": "open-p", "hash": 1588872423, "terms": ["What is diversity in Vespa?", "is diversity in Vespa?", "diversity in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["What", "is", "diversity", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1028555019", "fields": {"term": "What is min-groups?", "namespace": "open-p", "hash": 1028555019, "terms": ["What is min-groups?", "is min-groups?", "min-groups?"], "tokens": ["What", "is", "min", "groups", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::722670252", "fields": {"term": "What is the diversity attribute?", "namespace": "open-p", "hash": 722670252, "terms": ["What is the diversity attribute?", "is the diversity attribute?", "the diversity attribute?", "diversity attribute?", "attribute?"], "tokens": ["What", "is", "the", "diversity", "attribute", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-713903218", "fields": {"term": "What kind of attribute can be used for diversity in Vespa?", "namespace": "open-p", "hash": -713903218, "terms": ["What kind of attribute can be used for diversity in Vespa?", "kind of attribute can be used for diversity in Vespa?", "of attribute can be used for diversity in Vespa?", "attribute can be used for diversity in Vespa?", "can be used for diversity in Vespa?", "be used for diversity in Vespa?", "used for diversity in Vespa?", "for diversity in Vespa?", "diversity in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["What", "kind", "of", "attribute", "can", "be", "used", "for", "diversity", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-54964567", "fields": {"term": "How does diversity guarantee unique results?", "namespace": "open-p", "hash": -54964567, "terms": ["How does diversity guarantee unique results?", "does diversity guarantee unique results?", "diversity guarantee unique results?", "guarantee unique results?", "unique results?", "results?"], "tokens": ["How", "does", "diversity", "guarantee", "unique", "results", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-404045510", "fields": {"term": "How is min-groups used to reduce max-hits in Vespa?", "namespace": "open-p", "hash": -404045510, "terms": ["How is min-groups used to reduce max-hits in Vespa?", "is min-groups used to reduce max-hits in Vespa?", "min-groups used to reduce max-hits in Vespa?", "used to reduce max-hits in Vespa?", "to reduce max-hits in Vespa?", "reduce max-hits in Vespa?", "max-hits in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["How", "is", "min", "groups", "used", "to", "reduce", "max", "hits", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::246147367", "fields": {"term": "What are the requirements for a document to be considered as a candidate for diversity in Vespa?", "namespace": "open-p", "hash": 246147367, "terms": ["What are the requirements for a document to be considered as a candidate for diversity in Vespa?", "are the requirements for a document to be considered as a candidate for diversity in Vespa?", "the requirements for a document to be considered as a candidate for diversity in Vespa?", "requirements for a document to be considered as a candidate for diversity in Vespa?", "for a document to be considered as a candidate for diversity in Vespa?", "a document to be considered as a candidate for diversity in Vespa?", "document to be considered as a candidate for diversity in Vespa?", "to be considered as a candidate for diversity in Vespa?", "be considered as a candidate for diversity in Vespa?", "considered as a candidate for diversity in Vespa?", "as a candidate for diversity in Vespa?", "a candidate for diversity in Vespa?", "candidate for diversity in Vespa?", "for diversity in Vespa?", "diversity in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["What", "are", "the", "requirements", "for", "a", "document", "to", "be", "considered", "as", "a", "candidate", "for", "diversity", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1366908488", "fields": {"term": "What is keep-rank-count?", "namespace": "open-p", "hash": -1366908488, "terms": ["What is keep-rank-count?", "is keep-rank-count?", "keep-rank-count?"], "tokens": ["What", "is", "keep", "rank", "count", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2107207417", "fields": {"term": "What is rank-score-drop-limit?", "namespace": "open-p", "hash": 2107207417, "terms": ["What is rank-score-drop-limit?", "is rank-score-drop-limit?", "rank-score-drop-limit?"], "tokens": ["What", "is", "rank", "score", "drop", "limit", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1235987194", "fields": {"term": "How do you load a ranking expression file?", "namespace": "open-p", "hash": -1235987194, "terms": ["How do you load a ranking expression file?", "do you load a ranking expression file?", "you load a ranking expression file?", "load a ranking expression file?", "a ranking expression file?", "ranking expression file?", "expression file?", "file?"], "tokens": ["How", "do", "you", "load", "a", "ranking", "expression", "file", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::549147673", "fields": {"term": "What is the syntax for writing a ranking expression?", "namespace": "open-p", "hash": 549147673, "terms": ["What is the syntax for writing a ranking expression?", "is the syntax for writing a ranking expression?", "the syntax for writing a ranking expression?", "syntax for writing a ranking expression?", "for writing a ranking expression?", "writing a ranking expression?", "a ranking expression?", "ranking expression?", "expression?"], "tokens": ["What", "is", "the", "syntax", "for", "writing", "a", "ranking", "expression", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::26633857", "fields": {"term": "When is it useful to load expressions from a separate file?", "namespace": "open-p", "hash": 26633857, "terms": ["When is it useful to load expressions from a separate file?", "is it useful to load expressions from a separate file?", "it useful to load expressions from a separate file?", "useful to load expressions from a separate file?", "to load expressions from a separate file?", "load expressions from a separate file?", "expressions from a separate file?", "from a separate file?", "a separate file?", "separate file?", "file?"], "tokens": ["When", "is", "it", "useful", "to", "load", "expressions", "from", "a", "separate", "file", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1848247732", "fields": {"term": "How to list rank features?", "namespace": "open-p", "hash": 1848247732, "terms": ["How to list rank features?", "to list rank features?", "list rank features?", "rank features?", "features?"], "tokens": ["How", "to", "list", "rank", "features", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1205337416", "fields": {"term": "How can input types be declared?", "namespace": "open-p", "hash": 1205337416, "terms": ["How can input types be declared?", "can input types be declared?", "input types be declared?", "types be declared?", "be declared?", "declared?"], "tokens": ["How", "can", "input", "types", "be", "declared", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1509655732", "fields": {"term": "What are input examples?", "namespace": "open-p", "hash": 1509655732, "terms": ["What are input examples?", "are input examples?", "input examples?", "examples?"], "tokens": ["What", "are", "input", "examples", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2093347140", "fields": {"term": "How are default values specified for inputs?", "namespace": "open-p", "hash": 2093347140, "terms": ["How are default values specified for inputs?", "are default values specified for inputs?", "default values specified for inputs?", "values specified for inputs?", "specified for inputs?", "for inputs?", "inputs?"], "tokens": ["How", "are", "default", "values", "specified", "for", "inputs", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1427299519", "fields": {"term": "What are constants in Vespa?", "namespace": "open-p", "hash": 1427299519, "terms": ["What are constants in Vespa?", "are constants in Vespa?", "constants in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["What", "are", "constants", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1720033078", "fields": {"term": "How are constants defined?", "namespace": "open-p", "hash": -1720033078, "terms": ["How are constants defined?", "are constants defined?", "constants defined?", "defined?"], "tokens": ["How", "are", "constants", "defined", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-142899453", "fields": {"term": "What types of constants can be defined?", "namespace": "open-p", "hash": -142899453, "terms": ["What types of constants can be defined?", "types of constants can be defined?", "of constants can be defined?", "constants can be defined?", "can be defined?", "be defined?", "defined?"], "tokens": ["What", "types", "of", "constants", "can", "be", "defined", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1206613917", "fields": {"term": "How are constants inherited in Vespa?", "namespace": "open-p", "hash": -1206613917, "terms": ["How are constants inherited in Vespa?", "are constants inherited in Vespa?", "constants inherited in Vespa?", "inherited in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["How", "are", "constants", "inherited", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2093618791", "fields": {"term": "What is the format for defining constant values?", "namespace": "open-p", "hash": 2093618791, "terms": ["What is the format for defining constant values?", "is the format for defining constant values?", "the format for defining constant values?", "format for defining constant values?", "for defining constant values?", "defining constant values?", "constant values?", "values?"], "tokens": ["What", "is", "the", "format", "for", "defining", "constant", "values", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2147088725", "fields": {"term": "Can a constant value be stored in a file?", "namespace": "open-p", "hash": -2147088725, "terms": ["Can a constant value be stored in a file?", "a constant value be stored in a file?", "constant value be stored in a file?", "value be stored in a file?", "be stored in a file?", "stored in a file?", "in a file?", "a file?", "file?"], "tokens": ["Can", "a", "constant", "value", "be", "stored", "in", "a", "file", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-366069943", "fields": {"term": "How can a large tensor be defined as a constant?", "namespace": "open-p", "hash": -366069943, "terms": ["How can a large tensor be defined as a constant?", "can a large tensor be defined as a constant?", "a large tensor be defined as a constant?", "large tensor be defined as a constant?", "tensor be defined as a constant?", "be defined as a constant?", "defined as a constant?", "as a constant?", "a constant?", "constant?"], "tokens": ["How", "can", "a", "large", "tensor", "be", "defined", "as", "a", "constant", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::615656331", "fields": {"term": "What are rank properties?", "namespace": "open-p", "hash": 615656331, "terms": ["What are rank properties?", "are rank properties?", "rank properties?", "properties?"], "tokens": ["What", "are", "rank", "properties", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1788993487", "fields": {"term": "What is a key/value pair?", "namespace": "open-p", "hash": 1788993487, "terms": ["What is a key/value pair?", "is a key/value pair?", "a key/value pair?", "key/value pair?", "pair?"], "tokens": ["What", "is", "a", "key", "/", "value", "pair", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2074859246", "fields": {"term": "What is the description of the key in rank-properties?", "namespace": "open-p", "hash": -2074859246, "terms": ["What is the description of the key in rank-properties?", "is the description of the key in rank-properties?", "the description of the key in rank-properties?", "description of the key in rank-properties?", "of the key in rank-properties?", "the key in rank-properties?", "key in rank-properties?", "in rank-properties?", "rank-properties?"], "tokens": ["What", "is", "the", "description", "of", "the", "key", "in", "rank", "properties", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::35815121", "fields": {"term": "How are multiple properties specified in rank-properties?", "namespace": "open-p", "hash": 35815121, "terms": ["How are multiple properties specified in rank-properties?", "are multiple properties specified in rank-properties?", "multiple properties specified in rank-properties?", "properties specified in rank-properties?", "specified in rank-properties?", "in rank-properties?", "rank-properties?"], "tokens": ["How", "are", "multiple", "properties", "specified", "in", "rank", "properties", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1815292684", "fields": {"term": "What is the body of second-phase statement?", "namespace": "open-p", "hash": -1815292684, "terms": ["What is the body of second-phase statement?", "is the body of second-phase statement?", "the body of second-phase statement?", "body of second-phase statement?", "of second-phase statement?", "second-phase statement?", "statement?"], "tokens": ["What", "is", "the", "body", "of", "second", "phase", "statement", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1997952030", "fields": {"term": "What is the default value of rerank-count?", "namespace": "open-p", "hash": 1997952030, "terms": ["What is the default value of rerank-count?", "is the default value of rerank-count?", "the default value of rerank-count?", "default value of rerank-count?", "value of rerank-count?", "of rerank-count?", "rerank-count?"], "tokens": ["What", "is", "the", "default", "value", "of", "rerank", "count", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-21318455", "fields": {"term": "What can be put in the second-phase?", "namespace": "open-p", "hash": -21318455, "terms": ["What can be put in the second-phase?", "can be put in the second-phase?", "be put in the second-phase?", "put in the second-phase?", "in the second-phase?", "the second-phase?", "second-phase?"], "tokens": ["What", "can", "be", "put", "in", "the", "second", "phase", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-766597301", "fields": {"term": "Where can rerank-count be set?", "namespace": "open-p", "hash": -766597301, "terms": ["Where can rerank-count be set?", "can rerank-count be set?", "rerank-count be set?", "be set?", "set?"], "tokens": ["Where", "can", "rerank", "count", "be", "set", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1065906955", "fields": {"term": "How is second-phase ranking performed?", "namespace": "open-p", "hash": -1065906955, "terms": ["How is second-phase ranking performed?", "is second-phase ranking performed?", "second-phase ranking performed?", "ranking performed?", "performed?"], "tokens": ["How", "is", "second", "phase", "ranking", "performed", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1591931673", "fields": {"term": "What is the default rerank-count?", "namespace": "open-p", "hash": 1591931673, "terms": ["What is the default rerank-count?", "is the default rerank-count?", "the default rerank-count?", "default rerank-count?", "rerank-count?"], "tokens": ["What", "is", "the", "default", "rerank", "count", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1420288182", "fields": {"term": "What is the purpose of global-phase ranking?", "namespace": "open-p", "hash": 1420288182, "terms": ["What is the purpose of global-phase ranking?", "is the purpose of global-phase ranking?", "the purpose of global-phase ranking?", "purpose of global-phase ranking?", "of global-phase ranking?", "global-phase ranking?", "ranking?"], "tokens": ["What", "is", "the", "purpose", "of", "global", "phase", "ranking", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::623104196", "fields": {"term": "What is a summary features field?", "namespace": "open-p", "hash": 623104196, "terms": ["What is a summary features field?", "is a summary features field?", "a summary features field?", "summary features field?", "features field?", "field?"], "tokens": ["What", "is", "a", "summary", "features", "field", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-971496229", "fields": {"term": "What is meant by schema inheritance?", "namespace": "open-p", "hash": -971496229, "terms": ["What is meant by schema inheritance?", "is meant by schema inheritance?", "meant by schema inheritance?", "by schema inheritance?", "schema inheritance?", "inheritance?"], "tokens": ["What", "is", "meant", "by", "schema", "inheritance", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::947461930", "fields": {"term": "What is matchfeatures field?", "namespace": "open-p", "hash": 947461930, "terms": ["What is matchfeatures field?", "is matchfeatures field?", "matchfeatures field?", "field?"], "tokens": ["What", "is", "matchfeatures", "field", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::600599830", "fields": {"term": "What is summary-features?", "namespace": "open-p", "hash": 600599830, "terms": ["What is summary-features?", "is summary-features?", "summary-features?"], "tokens": ["What", "is", "summary", "features", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1827380315", "fields": {"term": "What are the rank features?", "namespace": "open-p", "hash": -1827380315, "terms": ["What are the rank features?", "are the rank features?", "the rank features?", "rank features?", "features?"], "tokens": ["What", "are", "the", "rank", "features", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::845390328", "fields": {"term": "What is the match phase?", "namespace": "open-p", "hash": 845390328, "terms": ["What is the match phase?", "is the match phase?", "the match phase?", "match phase?", "phase?"], "tokens": ["What", "is", "the", "match", "phase", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-930592221", "fields": {"term": "What is the format for match-features?", "namespace": "open-p", "hash": -930592221, "terms": ["What is the format for match-features?", "is the format for match-features?", "the format for match-features?", "format for match-features?", "for match-features?", "match-features?"], "tokens": ["What", "is", "the", "format", "for", "match", "features", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1945351773", "fields": {"term": "What is mutate?", "namespace": "open-p", "hash": -1945351773, "terms": ["What is mutate?", "is mutate?", "mutate?"], "tokens": ["What", "is", "mutate", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::155881446", "fields": {"term": "What operations can be done in mutate?", "namespace": "open-p", "hash": 155881446, "terms": ["What operations can be done in mutate?", "operations can be done in mutate?", "can be done in mutate?", "be done in mutate?", "done in mutate?", "in mutate?", "mutate?"], "tokens": ["What", "operations", "can", "be", "done", "in", "mutate", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1137183271", "fields": {"term": "What are the phases in mutate?", "namespace": "open-p", "hash": 1137183271, "terms": ["What are the phases in mutate?", "are the phases in mutate?", "the phases in mutate?", "phases in mutate?", "in mutate?", "mutate?"], "tokens": ["What", "are", "the", "phases", "in", "mutate", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2000176498", "fields": {"term": "What is on-summary phase?", "namespace": "open-p", "hash": -2000176498, "terms": ["What is on-summary phase?", "is on-summary phase?", "on-summary phase?", "phase?"], "tokens": ["What", "is", "on", "summary", "phase", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::185314275", "fields": {"term": "What is constant tensor?", "namespace": "open-p", "hash": 185314275, "terms": ["What is constant tensor?", "is constant tensor?", "constant tensor?", "tensor?"], "tokens": ["What", "is", "constant", "tensor", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1671462837", "fields": {"term": "How to define a constant?", "namespace": "open-p", "hash": 1671462837, "terms": ["How to define a constant?", "to define a constant?", "define a constant?", "a constant?", "constant?"], "tokens": ["How", "to", "define", "a", "constant", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1047163058", "fields": {"term": "What is file in constant?", "namespace": "open-p", "hash": 1047163058, "terms": ["What is file in constant?", "is file in constant?", "file in constant?", "in constant?", "constant?"], "tokens": ["What", "is", "file", "in", "constant", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1516121871", "fields": {"term": "What is type in constant?", "namespace": "open-p", "hash": 1516121871, "terms": ["What is type in constant?", "is type in constant?", "type in constant?", "in constant?", "constant?"], "tokens": ["What", "is", "type", "in", "constant", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1958811795", "fields": {"term": "What is an example of constant tensor?", "namespace": "open-p", "hash": 1958811795, "terms": ["What is an example of constant tensor?", "is an example of constant tensor?", "an example of constant tensor?", "example of constant tensor?", "of constant tensor?", "constant tensor?", "tensor?"], "tokens": ["What", "is", "an", "example", "of", "constant", "tensor", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::963164481", "fields": {"term": "What is the format of a constant tensor file?", "namespace": "open-p", "hash": 963164481, "terms": ["What is the format of a constant tensor file?", "is the format of a constant tensor file?", "the format of a constant tensor file?", "format of a constant tensor file?", "of a constant tensor file?", "a constant tensor file?", "constant tensor file?", "tensor file?", "file?"], "tokens": ["What", "is", "the", "format", "of", "a", "constant", "tensor", "file", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1547914499", "fields": {"term": "How are constant tensors distributed?", "namespace": "open-p", "hash": -1547914499, "terms": ["How are constant tensors distributed?", "are constant tensors distributed?", "constant tensors distributed?", "tensors distributed?", "distributed?"], "tokens": ["How", "are", "constant", "tensors", "distributed", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1148844130", "fields": {"term": "Can incremental changes be made to constant tensors?", "namespace": "open-p", "hash": -1148844130, "terms": ["Can incremental changes be made to constant tensors?", "incremental changes be made to constant tensors?", "changes be made to constant tensors?", "be made to constant tensors?", "made to constant tensors?", "to constant tensors?", "constant tensors?", "tensors?"], "tokens": ["Can", "incremental", "changes", "be", "made", "to", "constant", "tensors", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1862957276", "fields": {"term": "What is the raw field?", "namespace": "open-p", "hash": -1862957276, "terms": ["What is the raw field?", "is the raw field?", "the raw field?", "raw field?", "field?"], "tokens": ["What", "is", "the", "raw", "field", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::249656081", "fields": {"term": "What is base64 encoding?", "namespace": "open-p", "hash": 249656081, "terms": ["What is base64 encoding?", "is base64 encoding?", "base64 encoding?", "encoding?"], "tokens": ["What", "is", "base64", "encoding", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2135509828", "fields": {"term": "What is an ONNX model?", "namespace": "open-p", "hash": -2135509828, "terms": ["What is an ONNX model?", "is an ONNX model?", "an ONNX model?", "ONNX model?", "model?"], "tokens": ["What", "is", "an", "ONNX", "model", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1759381586", "fields": {"term": "How do you define an ONNX model in Vespa?", "namespace": "open-p", "hash": -1759381586, "terms": ["How do you define an ONNX model in Vespa?", "do you define an ONNX model in Vespa?", "you define an ONNX model in Vespa?", "define an ONNX model in Vespa?", "an ONNX model in Vespa?", "ONNX model in Vespa?", "model in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["How", "do", "you", "define", "an", "ONNX", "model", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1753781873", "fields": {"term": "What must the ONNX model body contain?", "namespace": "open-p", "hash": -1753781873, "terms": ["What must the ONNX model body contain?", "must the ONNX model body contain?", "the ONNX model body contain?", "ONNX model body contain?", "model body contain?", "body contain?", "contain?"], "tokens": ["What", "must", "the", "ONNX", "model", "body", "contain", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-819837366", "fields": {"term": "How is the path to the ONNX model defined?", "namespace": "open-p", "hash": -819837366, "terms": ["How is the path to the ONNX model defined?", "is the path to the ONNX model defined?", "the path to the ONNX model defined?", "path to the ONNX model defined?", "to the ONNX model defined?", "the ONNX model defined?", "ONNX model defined?", "model defined?", "defined?"], "tokens": ["How", "is", "the", "path", "to", "the", "ONNX", "model", "defined", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1513577173", "fields": {"term": "What is the maximum number of inputs and outputs for an ONNX model?", "namespace": "open-p", "hash": -1513577173, "terms": ["What is the maximum number of inputs and outputs for an ONNX model?", "is the maximum number of inputs and outputs for an ONNX model?", "the maximum number of inputs and outputs for an ONNX model?", "maximum number of inputs and outputs for an ONNX model?", "number of inputs and outputs for an ONNX model?", "of inputs and outputs for an ONNX model?", "inputs and outputs for an ONNX model?", "and outputs for an ONNX model?", "outputs for an ONNX model?", "for an ONNX model?", "an ONNX model?", "ONNX model?", "model?"], "tokens": ["What", "is", "the", "maximum", "number", "of", "inputs", "and", "outputs", "for", "an", "ONNX", "model", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1004903219", "fields": {"term": "What is the purpose of intraop-threads?", "namespace": "open-p", "hash": -1004903219, "terms": ["What is the purpose of intraop-threads?", "is the purpose of intraop-threads?", "the purpose of intraop-threads?", "purpose of intraop-threads?", "of intraop-threads?", "intraop-threads?"], "tokens": ["What", "is", "the", "purpose", "of", "intraop", "threads", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::-1869952878", "fields": {"term": "What is the execution-mode of an ONNX model?", "namespace": "open-p", "hash": -1869952878, "terms": ["What is the execution-mode of an ONNX model?", "is the execution-mode of an ONNX model?", "the execution-mode of an ONNX model?", "execution-mode of an ONNX model?", "of an ONNX model?", "an ONNX model?", "ONNX model?", "model?"], "tokens": ["What", "is", "the", "execution", "mode", "of", "an", "ONNX", "model", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1341390024", "fields": {"term": "What is the purpose of document summaries?", "namespace": "open-p", "hash": 1341390024, "terms": ["What is the purpose of document summaries?", "is the purpose of document summaries?", "the purpose of document summaries?", "purpose of document summaries?", "of document summaries?", "document summaries?", "summaries?"], "tokens": ["What", "is", "the", "purpose", "of", "document", "summaries", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-6117902", "fields": {"term": "How many occurrences of summary can a document summary have?", "namespace": "open-p", "hash": -6117902, "terms": ["How many occurrences of summary can a document summary have?", "many occurrences of summary can a document summary have?", "occurrences of summary can a document summary have?", "of summary can a document summary have?", "summary can a document summary have?", "can a document summary have?", "a document summary have?", "document summary have?", "summary have?", "have?"], "tokens": ["How", "many", "occurrences", "of", "summary", "can", "a", "document", "summary", "have", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::123652242", "fields": {"term": "What is the use of the 'inherits' attribute in document summary?", "namespace": "open-p", "hash": 123652242, "terms": ["What is the use of the 'inherits' attribute in document summary?", "is the use of the 'inherits' attribute in document summary?", "the use of the 'inherits' attribute in document summary?", "use of the 'inherits' attribute in document summary?", "of the 'inherits' attribute in document summary?", "the 'inherits' attribute in document summary?", "'inherits' attribute in document summary?", "attribute in document summary?", "in document summary?", "document summary?", "summary?"], "tokens": ["What", "is", "the", "use", "of", "the", "'", "inherits", "'", "attribute", "in", "document", "summary", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2018054546", "fields": {"term": "What is the body of a document summary consist of?", "namespace": "open-p", "hash": 2018054546, "terms": ["What is the body of a document summary consist of?", "is the body of a document summary consist of?", "the body of a document summary consist of?", "body of a document summary consist of?", "of a document summary consist of?", "a document summary consist of?", "document summary consist of?", "summary consist of?", "consist of?", "of?"], "tokens": ["What", "is", "the", "body", "of", "a", "document", "summary", "consist", "of", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1337511986", "fields": {"term": "What is the use of [from-disk] and [omit-summary-features] in document summary?", "namespace": "open-p", "hash": 1337511986, "terms": ["What is the use of [from-disk] and [omit-summary-features] in document summary?", "is the use of [from-disk] and [omit-summary-features] in document summary?", "the use of [from-disk] and [omit-summary-features] in document summary?", "use of [from-disk] and [omit-summary-features] in document summary?", "of [from-disk] and [omit-summary-features] in document summary?", "[from-disk] and [omit-summary-features] in document summary?", "and [omit-summary-features] in document summary?", "[omit-summary-features] in document summary?", "in document summary?", "document summary?", "summary?"], "tokens": ["What", "is", "the", "use", "of", "[", "from", "disk", "]", "and", "[", "omit", "summary", "features", "]", "in", "document", "summary", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-273946692", "fields": {"term": "What is the rank-profile in document summary used for?", "namespace": "open-p", "hash": -273946692, "terms": ["What is the rank-profile in document summary used for?", "is the rank-profile in document summary used for?", "the rank-profile in document summary used for?", "rank-profile in document summary used for?", "in document summary used for?", "document summary used for?", "summary used for?", "used for?", "for?"], "tokens": ["What", "is", "the", "rank", "profile", "in", "document", "summary", "used", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1290366801", "fields": {"term": "What is the default stemming type?", "namespace": "open-p", "hash": 1290366801, "terms": ["What is the default stemming type?", "is the default stemming type?", "the default stemming type?", "default stemming type?", "stemming type?", "type?"], "tokens": ["What", "is", "the", "default", "stemming", "type", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1934433066", "fields": {"term": "What is 'none' stemming?", "namespace": "open-p", "hash": 1934433066, "terms": ["What is 'none' stemming?", "is 'none' stemming?", "'none' stemming?", "stemming?"], "tokens": ["What", "is", "'", "none", "'", "stemming", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1544408437", "fields": {"term": "What is 'best' stemming?", "namespace": "open-p", "hash": 1544408437, "terms": ["What is 'best' stemming?", "is 'best' stemming?", "'best' stemming?", "stemming?"], "tokens": ["What", "is", "'", "best", "'", "stemming", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1304559533", "fields": {"term": "What is 'shortest' stemming?", "namespace": "open-p", "hash": -1304559533, "terms": ["What is 'shortest' stemming?", "is 'shortest' stemming?", "'shortest' stemming?", "stemming?"], "tokens": ["What", "is", "'", "shortest", "'", "stemming", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::42609567", "fields": {"term": "What is 'multiple' stemming?", "namespace": "open-p", "hash": 42609567, "terms": ["What is 'multiple' stemming?", "is 'multiple' stemming?", "'multiple' stemming?", "stemming?"], "tokens": ["What", "is", "'", "multiple", "'", "stemming", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1012772927", "fields": {"term": "What is the default normalization?", "namespace": "open-p", "hash": -1012772927, "terms": ["What is the default normalization?", "is the default normalization?", "the default normalization?", "default normalization?", "normalization?"], "tokens": ["What", "is", "the", "default", "normalization", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1889643666", "fields": {"term": "What is a normalizing-type?", "namespace": "open-p", "hash": -1889643666, "terms": ["What is a normalizing-type?", "is a normalizing-type?", "a normalizing-type?", "normalizing-type?"], "tokens": ["What", "is", "a", "normalizing", "type", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-25110841", "fields": {"term": "What field does normalizing occur on?", "namespace": "open-p", "hash": -25110841, "terms": ["What field does normalizing occur on?", "field does normalizing occur on?", "does normalizing occur on?", "normalizing occur on?", "occur on?", "on?"], "tokens": ["What", "field", "does", "normalizing", "occur", "on", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1128452952", "fields": {"term": "What is an alias in Vespa?", "namespace": "open-p", "hash": 1128452952, "terms": ["What is an alias in Vespa?", "is an alias in Vespa?", "an alias in Vespa?", "alias in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["What", "is", "an", "alias", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-553514115", "fields": {"term": "How to make an alias?", "namespace": "open-p", "hash": -553514115, "terms": ["How to make an alias?", "to make an alias?", "make an alias?", "an alias?", "alias?"], "tokens": ["How", "to", "make", "an", "alias", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1995822561", "fields": {"term": "What can be aliased in Vespa?", "namespace": "open-p", "hash": -1995822561, "terms": ["What can be aliased in Vespa?", "can be aliased in Vespa?", "be aliased in Vespa?", "aliased in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["What", "can", "be", "aliased", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1087984222", "fields": {"term": "What is the syntax for making an alias?", "namespace": "open-p", "hash": 1087984222, "terms": ["What is the syntax for making an alias?", "is the syntax for making an alias?", "the syntax for making an alias?", "syntax for making an alias?", "for making an alias?", "making an alias?", "an alias?", "alias?"], "tokens": ["What", "is", "the", "syntax", "for", "making", "an", "alias", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-559541111", "fields": {"term": "Are dots allowed in alias names?", "namespace": "open-p", "hash": -559541111, "terms": ["Are dots allowed in alias names?", "dots allowed in alias names?", "allowed in alias names?", "in alias names?", "alias names?", "names?"], "tokens": ["Are", "dots", "allowed", "in", "alias", "names", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-849581031", "fields": {"term": "How does an alias work?", "namespace": "open-p", "hash": -849581031, "terms": ["How does an alias work?", "does an alias work?", "an alias work?", "alias work?", "work?"], "tokens": ["How", "does", "an", "alias", "work", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1782076021", "fields": {"term": "Can an attribute be aliased?", "namespace": "open-p", "hash": -1782076021, "terms": ["Can an attribute be aliased?", "an attribute be aliased?", "attribute be aliased?", "be aliased?", "aliased?"], "tokens": ["Can", "an", "attribute", "be", "aliased", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1039061656", "fields": {"term": "Can a field be aliased?", "namespace": "open-p", "hash": -1039061656, "terms": ["Can a field be aliased?", "a field be aliased?", "field be aliased?", "be aliased?", "aliased?"], "tokens": ["Can", "a", "field", "be", "aliased", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1199209290", "fields": {"term": "What is the inverted index?", "namespace": "open-p", "hash": 1199209290, "terms": ["What is the inverted index?", "is the inverted index?", "the inverted index?", "inverted index?", "index?"], "tokens": ["What", "is", "the", "inverted", "index", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::171401340", "fields": {"term": "What is the difference between 'hash' and 'btree'?", "namespace": "open-p", "hash": 171401340, "terms": ["What is the difference between 'hash' and 'btree'?", "is the difference between 'hash' and 'btree'?", "the difference between 'hash' and 'btree'?", "difference between 'hash' and 'btree'?", "between 'hash' and 'btree'?", "'hash' and 'btree'?", "and 'btree'?", "'btree'?"], "tokens": ["What", "is", "the", "difference", "between", "'", "hash", "'", "and", "'", "btree", "'?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1637143532", "fields": {"term": "When is 'hash' preferred over 'btree'?", "namespace": "open-p", "hash": 1637143532, "terms": ["When is 'hash' preferred over 'btree'?", "is 'hash' preferred over 'btree'?", "'hash' preferred over 'btree'?", "preferred over 'btree'?", "over 'btree'?", "'btree'?"], "tokens": ["When", "is", "'", "hash", "'", "preferred", "over", "'", "btree", "'?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1585369114", "fields": {"term": "What are attributes?", "namespace": "open-p", "hash": -1585369114, "terms": ["What are attributes?", "are attributes?", "attributes?"], "tokens": ["What", "are", "attributes", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-974699212", "fields": {"term": "What is fast-access?", "namespace": "open-p", "hash": -974699212, "terms": ["What is fast-access?", "is fast-access?", "fast-access?"], "tokens": ["What", "is", "fast", "access", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::-1661906427", "fields": {"term": "What is mutable?", "namespace": "open-p", "hash": -1661906427, "terms": ["What is mutable?", "is mutable?", "mutable?"], "tokens": ["What", "is", "mutable", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::582649645", "fields": {"term": "What is sorting in Vespa?", "namespace": "open-p", "hash": 582649645, "terms": ["What is sorting in Vespa?", "is sorting in Vespa?", "sorting in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["What", "is", "sorting", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::776185020", "fields": {"term": "What is the default sort function?", "namespace": "open-p", "hash": 776185020, "terms": ["What is the default sort function?", "is the default sort function?", "the default sort function?", "default sort function?", "sort function?", "function?"], "tokens": ["What", "is", "the", "default", "sort", "function", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::441205303", "fields": {"term": "What is the default sort order?", "namespace": "open-p", "hash": 441205303, "terms": ["What is the default sort order?", "is the default sort order?", "the default sort order?", "default sort order?", "sort order?", "order?"], "tokens": ["What", "is", "the", "default", "sort", "order", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::-804617783", "fields": {"term": "What is UCA sort strength?", "namespace": "open-p", "hash": -804617783, "terms": ["What is UCA sort strength?", "is UCA sort strength?", "UCA sort strength?", "sort strength?", "strength?"], "tokens": ["What", "is", "UCA", "sort", "strength", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::141840939", "fields": {"term": "What is a UCA locale?", "namespace": "open-p", "hash": 141840939, "terms": ["What is a UCA locale?", "is a UCA locale?", "a UCA locale?", "UCA locale?", "locale?"], "tokens": ["What", "is", "a", "UCA", "locale", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1547575116", "fields": {"term": "What is the default UCA locale?", "namespace": "open-p", "hash": 1547575116, "terms": ["What is the default UCA locale?", "is the default UCA locale?", "the default UCA locale?", "default UCA locale?", "UCA locale?", "locale?"], "tokens": ["What", "is", "the", "default", "UCA", "locale", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1665257015", "fields": {"term": "What is a sort property?", "namespace": "open-p", "hash": -1665257015, "terms": ["What is a sort property?", "is a sort property?", "a sort property?", "sort property?", "property?"], "tokens": ["What", "is", "a", "sort", "property", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1083616043", "fields": {"term": "Where is sorting specified?", "namespace": "open-p", "hash": 1083616043, "terms": ["Where is sorting specified?", "is sorting specified?", "sorting specified?", "specified?"], "tokens": ["Where", "is", "sorting", "specified", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2101712932", "fields": {"term": "What are defining properties of metric space?", "namespace": "open-p", "hash": -2101712932, "terms": ["What are defining properties of metric space?", "are defining properties of metric space?", "defining properties of metric space?", "properties of metric space?", "of metric space?", "metric space?", "space?"], "tokens": ["What", "are", "defining", "properties", "of", "metric", "space", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-946660361", "fields": {"term": "What is Dense passage retrieval sample app?", "namespace": "open-p", "hash": -946660361, "terms": ["What is Dense passage retrieval sample app?", "is Dense passage retrieval sample app?", "Dense passage retrieval sample app?", "passage retrieval sample app?", "retrieval sample app?", "sample app?", "app?"], "tokens": ["What", "is", "Dense", "passage", "retrieval", "sample", "app", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1045796456", "fields": {"term": "What is great-circle distance?", "namespace": "open-p", "hash": 1045796456, "terms": ["What is great-circle distance?", "is great-circle distance?", "great-circle distance?", "distance?"], "tokens": ["What", "is", "great", "circle", "distance", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1240050253", "fields": {"term": "What is Hamming distance metric?", "namespace": "open-p", "hash": -1240050253, "terms": ["What is Hamming distance metric?", "is Hamming distance metric?", "Hamming distance metric?", "distance metric?", "metric?"], "tokens": ["What", "is", "Hamming", "distance", "metric", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2075928891", "fields": {"term": "What is prenormalized-angular distance metric?", "namespace": "open-p", "hash": 2075928891, "terms": ["What is prenormalized-angular distance metric?", "is prenormalized-angular distance metric?", "prenormalized-angular distance metric?", "distance metric?", "metric?"], "tokens": ["What", "is", "prenormalized", "angular", "distance", "metric", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2114185799", "fields": {"term": "What fields support bolding?", "namespace": "open-p", "hash": -2114185799, "terms": ["What fields support bolding?", "fields support bolding?", "support bolding?", "bolding?"], "tokens": ["What", "fields", "support", "bolding", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-133377321", "fields": {"term": "What is the default XML element used for highlighting?", "namespace": "open-p", "hash": -133377321, "terms": ["What is the default XML element used for highlighting?", "is the default XML element used for highlighting?", "the default XML element used for highlighting?", "default XML element used for highlighting?", "XML element used for highlighting?", "element used for highlighting?", "used for highlighting?", "for highlighting?", "highlighting?"], "tokens": ["What", "is", "the", "default", "XML", "element", "used", "for", "highlighting", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2132128275", "fields": {"term": "What happens if both bolding and summary: dynamic are specified?", "namespace": "open-p", "hash": 2132128275, "terms": ["What happens if both bolding and summary: dynamic are specified?", "happens if both bolding and summary: dynamic are specified?", "if both bolding and summary: dynamic are specified?", "both bolding and summary: dynamic are specified?", "bolding and summary: dynamic are specified?", "and summary: dynamic are specified?", "summary: dynamic are specified?", "dynamic are specified?", "are specified?", "specified?"], "tokens": ["What", "happens", "if", "both", "bolding", "and", "summary", ":", "dynamic", "are", "specified", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1210424242", "fields": {"term": "What is the maximum byte length for bolding?", "namespace": "open-p", "hash": -1210424242, "terms": ["What is the maximum byte length for bolding?", "is the maximum byte length for bolding?", "the maximum byte length for bolding?", "maximum byte length for bolding?", "byte length for bolding?", "length for bolding?", "for bolding?", "bolding?"], "tokens": ["What", "is", "the", "maximum", "byte", "length", "for", "bolding", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2009864244", "fields": {"term": "What is summary: dynamic?", "namespace": "open-p", "hash": -2009864244, "terms": ["What is summary: dynamic?", "is summary: dynamic?", "summary: dynamic?", "dynamic?"], "tokens": ["What", "is", "summary", ":", "dynamic", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-352417543", "fields": {"term": "What types of fields support summary: dynamic?", "namespace": "open-p", "hash": -352417543, "terms": ["What types of fields support summary: dynamic?", "types of fields support summary: dynamic?", "of fields support summary: dynamic?", "fields support summary: dynamic?", "support summary: dynamic?", "summary: dynamic?", "dynamic?"], "tokens": ["What", "types", "of", "fields", "support", "summary", ":", "dynamic", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::502747880", "fields": {"term": "What are the id requirements?", "namespace": "open-p", "hash": 502747880, "terms": ["What are the id requirements?", "are the id requirements?", "the id requirements?", "id requirements?", "requirements?"], "tokens": ["What", "are", "the", "id", "requirements", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2063483065", "fields": {"term": "What are the advantages of explicitly setting id's?", "namespace": "open-p", "hash": 2063483065, "terms": ["What are the advantages of explicitly setting id's?", "are the advantages of explicitly setting id's?", "the advantages of explicitly setting id's?", "advantages of explicitly setting id's?", "of explicitly setting id's?", "explicitly setting id's?", "setting id's?", "id's?"], "tokens": ["What", "are", "the", "advantages", "of", "explicitly", "setting", "id", "'", "s", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::418969053", "fields": {"term": "What are index parameters?", "namespace": "open-p", "hash": 418969053, "terms": ["What are index parameters?", "are index parameters?", "index parameters?", "parameters?"], "tokens": ["What", "are", "index", "parameters", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1499592267", "fields": {"term": "What is an alias in index?", "namespace": "open-p", "hash": -1499592267, "terms": ["What is an alias in index?", "is an alias in index?", "an alias in index?", "alias in index?", "in index?", "index?"], "tokens": ["What", "is", "an", "alias", "in", "index", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1798414611", "fields": {"term": "What is stemming setting?", "namespace": "open-p", "hash": 1798414611, "terms": ["What is stemming setting?", "is stemming setting?", "stemming setting?", "setting?"], "tokens": ["What", "is", "stemming", "setting", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1194301835", "fields": {"term": "What is dense posting list threshold value?", "namespace": "open-p", "hash": -1194301835, "terms": ["What is dense posting list threshold value?", "is dense posting list threshold value?", "dense posting list threshold value?", "posting list threshold value?", "list threshold value?", "threshold value?", "value?"], "tokens": ["What", "is", "dense", "posting", "list", "threshold", "value", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1549171922", "fields": {"term": "What query operator uses HNSW?", "namespace": "open-p", "hash": 1549171922, "terms": ["What query operator uses HNSW?", "query operator uses HNSW?", "operator uses HNSW?", "uses HNSW?", "HNSW?"], "tokens": ["What", "query", "operator", "uses", "HNSW", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::271190456", "fields": {"term": "What are the parameters of HNSW?", "namespace": "open-p", "hash": 271190456, "terms": ["What are the parameters of HNSW?", "are the parameters of HNSW?", "the parameters of HNSW?", "parameters of HNSW?", "of HNSW?", "HNSW?"], "tokens": ["What", "are", "the", "parameters", "of", "HNSW", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-364167653", "fields": {"term": "What is the default value of max-links-per-node parameter?", "namespace": "open-p", "hash": -364167653, "terms": ["What is the default value of max-links-per-node parameter?", "is the default value of max-links-per-node parameter?", "the default value of max-links-per-node parameter?", "default value of max-links-per-node parameter?", "value of max-links-per-node parameter?", "of max-links-per-node parameter?", "max-links-per-node parameter?", "parameter?"], "tokens": ["What", "is", "the", "default", "value", "of", "max", "links", "per", "node", "parameter", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1850875967", "fields": {"term": "What is the default value of neighbors-to-explore-at-insert parameter?", "namespace": "open-p", "hash": -1850875967, "terms": ["What is the default value of neighbors-to-explore-at-insert parameter?", "is the default value of neighbors-to-explore-at-insert parameter?", "the default value of neighbors-to-explore-at-insert parameter?", "default value of neighbors-to-explore-at-insert parameter?", "value of neighbors-to-explore-at-insert parameter?", "of neighbors-to-explore-at-insert parameter?", "neighbors-to-explore-at-insert parameter?", "parameter?"], "tokens": ["What", "is", "the", "default", "value", "of", "neighbors", "to", "explore", "at", "insert", "parameter", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1763909985", "fields": {"term": "Where can I find examples of using HNSW in Vespa?", "namespace": "open-p", "hash": 1763909985, "terms": ["Where can I find examples of using HNSW in Vespa?", "can I find examples of using HNSW in Vespa?", "I find examples of using HNSW in Vespa?", "find examples of using HNSW in Vespa?", "examples of using HNSW in Vespa?", "of using HNSW in Vespa?", "using HNSW in Vespa?", "HNSW in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["Where", "can", "I", "find", "examples", "of", "using", "HNSW", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::39742519", "fields": {"term": "Why did Vespa team select HNSW as the baseline algorithm?", "namespace": "open-p", "hash": 39742519, "terms": ["Why did Vespa team select HNSW as the baseline algorithm?", "did Vespa team select HNSW as the baseline algorithm?", "Vespa team select HNSW as the baseline algorithm?", "team select HNSW as the baseline algorithm?", "select HNSW as the baseline algorithm?", "HNSW as the baseline algorithm?", "as the baseline algorithm?", "the baseline algorithm?", "baseline algorithm?", "algorithm?"], "tokens": ["Why", "did", "Vespa", "team", "select", "HNSW", "as", "the", "baseline", "algorithm", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2080734441", "fields": {"term": "What is the indexing language?", "namespace": "open-p", "hash": 2080734441, "terms": ["What is the indexing language?", "is the indexing language?", "the indexing language?", "indexing language?", "language?"], "tokens": ["What", "is", "the", "indexing", "language", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1031141663", "fields": {"term": "What does index statement do?", "namespace": "open-p", "hash": -1031141663, "terms": ["What does index statement do?", "does index statement do?", "index statement do?", "statement do?", "do?"], "tokens": ["What", "does", "index", "statement", "do", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::134997794", "fields": {"term": "What does the 'none' option do?", "namespace": "open-p", "hash": 134997794, "terms": ["What does the 'none' option do?", "does the 'none' option do?", "the 'none' option do?", "'none' option do?", "option do?", "do?"], "tokens": ["What", "does", "the", "'", "none", "'", "option", "do", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1640327664", "fields": {"term": "What is match type \"text\"?", "namespace": "open-p", "hash": -1640327664, "terms": ["What is match type \"text\"?", "is match type \"text\"?", "match type \"text\"?", "type \"text\"?", "\"text\"?"], "tokens": ["What", "is", "match", "type", "\"", "text", "\"?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-824947190", "fields": {"term": "What is match type \"word\"?", "namespace": "open-p", "hash": -824947190, "terms": ["What is match type \"word\"?", "is match type \"word\"?", "match type \"word\"?", "type \"word\"?", "\"word\"?"], "tokens": ["What", "is", "match", "type", "\"", "word", "\"?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1526672042", "fields": {"term": "What is match type \"exact\"?", "namespace": "open-p", "hash": -1526672042, "terms": ["What is match type \"exact\"?", "is match type \"exact\"?", "match type \"exact\"?", "type \"exact\"?", "\"exact\"?"], "tokens": ["What", "is", "match", "type", "\"", "exact", "\"?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1223695210", "fields": {"term": "What is match type \"prefix\"?", "namespace": "open-p", "hash": -1223695210, "terms": ["What is match type \"prefix\"?", "is match type \"prefix\"?", "match type \"prefix\"?", "type \"prefix\"?", "\"prefix\"?"], "tokens": ["What", "is", "match", "type", "\"", "prefix", "\"?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::454727899", "fields": {"term": "What is match type \"cased\"?", "namespace": "open-p", "hash": 454727899, "terms": ["What is match type \"cased\"?", "is match type \"cased\"?", "match type \"cased\"?", "type \"cased\"?", "\"cased\"?"], "tokens": ["What", "is", "match", "type", "\"", "cased", "\"?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::242897946", "fields": {"term": "What is match type \"uncased\"?", "namespace": "open-p", "hash": 242897946, "terms": ["What is match type \"uncased\"?", "is match type \"uncased\"?", "match type \"uncased\"?", "type \"uncased\"?", "\"uncased\"?"], "tokens": ["What", "is", "match", "type", "\"", "uncased", "\"?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1499602810", "fields": {"term": "What is match type \"max-length\"?", "namespace": "open-p", "hash": -1499602810, "terms": ["What is match type \"max-length\"?", "is match type \"max-length\"?", "match type \"max-length\"?", "type \"max-length\"?", "\"max-length\"?"], "tokens": ["What", "is", "match", "type", "\"", "max", "length", "\"?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-630901780", "fields": {"term": "What is match type \"gram\"?", "namespace": "open-p", "hash": -630901780, "terms": ["What is match type \"gram\"?", "is match type \"gram\"?", "match type \"gram\"?", "type \"gram\"?", "\"gram\"?"], "tokens": ["What", "is", "match", "type", "\"", "gram", "\"?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::851337347", "fields": {"term": "What is ranking settings?", "namespace": "open-p", "hash": 851337347, "terms": ["What is ranking settings?", "is ranking settings?", "ranking settings?", "settings?"], "tokens": ["What", "is", "ranking", "settings", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::593227581", "fields": {"term": "What is 'filter' ranking setting?", "namespace": "open-p", "hash": 593227581, "terms": ["What is 'filter' ranking setting?", "is 'filter' ranking setting?", "'filter' ranking setting?", "ranking setting?", "setting?"], "tokens": ["What", "is", "'", "filter", "'", "ranking", "setting", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2074970553", "fields": {"term": "What is 'normal' ranking setting?", "namespace": "open-p", "hash": -2074970553, "terms": ["What is 'normal' ranking setting?", "is 'normal' ranking setting?", "'normal' ranking setting?", "ranking setting?", "setting?"], "tokens": ["What", "is", "'", "normal", "'", "ranking", "setting", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-995162172", "fields": {"term": "What is query-command?", "namespace": "open-p", "hash": -995162172, "terms": ["What is query-command?", "is query-command?", "query-command?"], "tokens": ["What", "is", "query", "command", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1177979765", "fields": {"term": "What is the built-in query-command available?", "namespace": "open-p", "hash": 1177979765, "terms": ["What is the built-in query-command available?", "is the built-in query-command available?", "the built-in query-command available?", "built-in query-command available?", "query-command available?", "available?"], "tokens": ["What", "is", "the", "built", "in", "query", "command", "available", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-837531851", "fields": {"term": "What is the 'rank-type'?", "namespace": "open-p", "hash": -837531851, "terms": ["What is the 'rank-type'?", "is the 'rank-type'?", "the 'rank-type'?", "'rank-type'?"], "tokens": ["What", "is", "the", "'", "rank", "type", "'?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1628587236", "fields": {"term": "What are defined rank types?", "namespace": "open-p", "hash": 1628587236, "terms": ["What are defined rank types?", "are defined rank types?", "defined rank types?", "rank types?", "types?"], "tokens": ["What", "are", "defined", "rank", "types", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1229314028", "fields": {"term": "What is the default rank type?", "namespace": "open-p", "hash": 1229314028, "terms": ["What is the default rank type?", "is the default rank type?", "the default rank type?", "default rank type?", "rank type?", "type?"], "tokens": ["What", "is", "the", "default", "rank", "type", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::549445591", "fields": {"term": "What is the 'empty' rank type?", "namespace": "open-p", "hash": 549445591, "terms": ["What is the 'empty' rank type?", "is the 'empty' rank type?", "the 'empty' rank type?", "'empty' rank type?", "rank type?", "type?"], "tokens": ["What", "is", "the", "'", "empty", "'", "rank", "type", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::997906476", "fields": {"term": "How can you configure native rank features?", "namespace": "open-p", "hash": 997906476, "terms": ["How can you configure native rank features?", "can you configure native rank features?", "you configure native rank features?", "configure native rank features?", "native rank features?", "rank features?", "features?"], "tokens": ["How", "can", "you", "configure", "native", "rank", "features", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1269805723", "fields": {"term": "Where can you find more information on native rank?", "namespace": "open-p", "hash": 1269805723, "terms": ["Where can you find more information on native rank?", "can you find more information on native rank?", "you find more information on native rank?", "find more information on native rank?", "more information on native rank?", "information on native rank?", "on native rank?", "native rank?", "rank?"], "tokens": ["Where", "can", "you", "find", "more", "information", "on", "native", "rank", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::464256942", "fields": {"term": "What does `summary-to` specify?", "namespace": "open-p", "hash": 464256942, "terms": ["What does `summary-to` specify?", "does `summary-to` specify?", "`summary-to` specify?", "specify?"], "tokens": ["What", "does", "`", "summary", "to", "`", "specify", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1745653396", "fields": {"term": "What is the default summary behavior for fields with summary?", "namespace": "open-p", "hash": -1745653396, "terms": ["What is the default summary behavior for fields with summary?", "is the default summary behavior for fields with summary?", "the default summary behavior for fields with summary?", "default summary behavior for fields with summary?", "summary behavior for fields with summary?", "behavior for fields with summary?", "for fields with summary?", "fields with summary?", "with summary?", "summary?"], "tokens": ["What", "is", "the", "default", "summary", "behavior", "for", "fields", "with", "summary", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-100113672", "fields": {"term": "What is a summary field?", "namespace": "open-p", "hash": -100113672, "terms": ["What is a summary field?", "is a summary field?", "a summary field?", "summary field?", "field?"], "tokens": ["What", "is", "a", "summary", "field", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1640627961", "fields": {"term": "What is a full summary?", "namespace": "open-p", "hash": -1640627961, "terms": ["What is a full summary?", "is a full summary?", "a full summary?", "full summary?", "summary?"], "tokens": ["What", "is", "a", "full", "summary", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1223487031", "fields": {"term": "What is 'bolding on' in summary?", "namespace": "open-p", "hash": 1223487031, "terms": ["What is 'bolding on' in summary?", "is 'bolding on' in summary?", "'bolding on' in summary?", "on' in summary?", "in summary?", "summary?"], "tokens": ["What", "is", "'", "bolding", "on", "'", "in", "summary", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::443184129", "fields": {"term": "What is 'to' summary?", "namespace": "open-p", "hash": 443184129, "terms": ["What is 'to' summary?", "is 'to' summary?", "'to' summary?", "summary?"], "tokens": ["What", "is", "'", "to", "'", "summary", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-854480778", "fields": {"term": "What is 'matched elements only'?", "namespace": "open-p", "hash": -854480778, "terms": ["What is 'matched elements only'?", "is 'matched elements only'?", "'matched elements only'?", "elements only'?", "only'?"], "tokens": ["What", "is", "'", "matched", "elements", "only", "'?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1472121896", "fields": {"term": "What are the summary data types available?", "namespace": "open-p", "hash": 1472121896, "terms": ["What are the summary data types available?", "are the summary data types available?", "the summary data types available?", "summary data types available?", "data types available?", "types available?", "available?"], "tokens": ["What", "are", "the", "summary", "data", "types", "available", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1785062282", "fields": {"term": "What is field weight?", "namespace": "open-p", "hash": 1785062282, "terms": ["What is field weight?", "is field weight?", "field weight?", "weight?"], "tokens": ["What", "is", "field", "weight", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-20352691", "fields": {"term": "What is the default field weight?", "namespace": "open-p", "hash": -20352691, "terms": ["What is the default field weight?", "is the default field weight?", "the default field weight?", "default field weight?", "field weight?", "weight?"], "tokens": ["What", "is", "the", "default", "field", "weight", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-506858381", "fields": {"term": "How is field weight used?", "namespace": "open-p", "hash": -506858381, "terms": ["How is field weight used?", "is field weight used?", "field weight used?", "weight used?", "used?"], "tokens": ["How", "is", "field", "weight", "used", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1269887241", "fields": {"term": "What is mandatory for an annotation?", "namespace": "open-p", "hash": -1269887241, "terms": ["What is mandatory for an annotation?", "is mandatory for an annotation?", "mandatory for an annotation?", "for an annotation?", "an annotation?", "annotation?"], "tokens": ["What", "is", "mandatory", "for", "an", "annotation", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::694875113", "fields": {"term": "What is the syntax for defining annotations?", "namespace": "open-p", "hash": 694875113, "terms": ["What is the syntax for defining annotations?", "is the syntax for defining annotations?", "the syntax for defining annotations?", "syntax for defining annotations?", "for defining annotations?", "defining annotations?", "annotations?"], "tokens": ["What", "is", "the", "syntax", "for", "defining", "annotations", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2059766114", "fields": {"term": "Is [body] required for an annotation?", "namespace": "open-p", "hash": -2059766114, "terms": ["Is [body] required for an annotation?", "[body] required for an annotation?", "required for an annotation?", "for an annotation?", "an annotation?", "annotation?"], "tokens": ["Is", "[", "body", "]", "required", "for", "an", "annotation", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2137772257", "fields": {"term": "What is the purpose of the Annotations API?", "namespace": "open-p", "hash": 2137772257, "terms": ["What is the purpose of the Annotations API?", "is the purpose of the Annotations API?", "the purpose of the Annotations API?", "purpose of the Annotations API?", "of the Annotations API?", "the Annotations API?", "Annotations API?", "API?"], "tokens": ["What", "is", "the", "purpose", "of", "the", "Annotations", "API", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::882260934", "fields": {"term": "What is the purpose of defining annotation types in Vespa?", "namespace": "open-p", "hash": 882260934, "terms": ["What is the purpose of defining annotation types in Vespa?", "is the purpose of defining annotation types in Vespa?", "the purpose of defining annotation types in Vespa?", "purpose of defining annotation types in Vespa?", "of defining annotation types in Vespa?", "defining annotation types in Vespa?", "annotation types in Vespa?", "types in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["What", "is", "the", "purpose", "of", "defining", "annotation", "types", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1351227836", "fields": {"term": "What is an imported field?", "namespace": "open-p", "hash": -1351227836, "terms": ["What is an imported field?", "is an imported field?", "an imported field?", "imported field?", "field?"], "tokens": ["What", "is", "an", "imported", "field", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::310572401", "fields": {"term": "What can be imported?", "namespace": "open-p", "hash": 310572401, "terms": ["What can be imported?", "can be imported?", "be imported?", "imported?"], "tokens": ["What", "can", "be", "imported", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1999521797", "fields": {"term": "What is an array of struct?", "namespace": "open-p", "hash": 1999521797, "terms": ["What is an array of struct?", "is an array of struct?", "an array of struct?", "array of struct?", "of struct?", "struct?"], "tokens": ["What", "is", "an", "array", "of", "struct", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2066580044", "fields": {"term": "What is a position field?", "namespace": "open-p", "hash": 2066580044, "terms": ["What is a position field?", "is a position field?", "a position field?", "position field?", "field?"], "tokens": ["What", "is", "a", "position", "field", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1567205894", "fields": {"term": "Can imported fields be used to expire documents?", "namespace": "open-p", "hash": 1567205894, "terms": ["Can imported fields be used to expire documents?", "imported fields be used to expire documents?", "fields be used to expire documents?", "be used to expire documents?", "used to expire documents?", "to expire documents?", "expire documents?", "documents?"], "tokens": ["Can", "imported", "fields", "be", "used", "to", "expire", "documents", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1000364764", "fields": {"term": "What is a string field?", "namespace": "open-p", "hash": 1000364764, "terms": ["What is a string field?", "is a string field?", "a string field?", "string field?", "field?"], "tokens": ["What", "is", "a", "string", "field", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1435857576", "fields": {"term": "What is the syntax for searching in the URL field?", "namespace": "open-p", "hash": 1435857576, "terms": ["What is the syntax for searching in the URL field?", "is the syntax for searching in the URL field?", "the syntax for searching in the URL field?", "syntax for searching in the URL field?", "for searching in the URL field?", "searching in the URL field?", "in the URL field?", "the URL field?", "URL field?", "field?"], "tokens": ["What", "is", "the", "syntax", "for", "searching", "in", "the", "URL", "field", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-302519684", "fields": {"term": "What is an annotationreference field used for?", "namespace": "open-p", "hash": -302519684, "terms": ["What is an annotationreference field used for?", "is an annotationreference field used for?", "an annotationreference field used for?", "annotationreference field used for?", "field used for?", "used for?", "for?"], "tokens": ["What", "is", "an", "annotationreference", "field", "used", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1368284560", "fields": {"term": "What is a reference field?", "namespace": "open-p", "hash": -1368284560, "terms": ["What is a reference field?", "is a reference field?", "a reference field?", "reference field?", "field?"], "tokens": ["What", "is", "a", "reference", "field", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-378754732", "fields": {"term": "Can a reference field be searched using fast-search?", "namespace": "open-p", "hash": -378754732, "terms": ["Can a reference field be searched using fast-search?", "a reference field be searched using fast-search?", "reference field be searched using fast-search?", "field be searched using fast-search?", "be searched using fast-search?", "searched using fast-search?", "using fast-search?", "fast-search?"], "tokens": ["Can", "a", "reference", "field", "be", "searched", "using", "fast", "search", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1539027219", "fields": {"term": "What is a document field?", "namespace": "open-p", "hash": 1539027219, "terms": ["What is a document field?", "is a document field?", "a document field?", "document field?", "field?"], "tokens": ["What", "is", "a", "document", "field", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-85101704", "fields": {"term": "How to make a string attribute?", "namespace": "open-p", "hash": -85101704, "terms": ["How to make a string attribute?", "to make a string attribute?", "make a string attribute?", "a string attribute?", "string attribute?", "attribute?"], "tokens": ["How", "to", "make", "a", "string", "attribute", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1445157402", "fields": {"term": "What is the syntax for creating an integer attribute?", "namespace": "open-p", "hash": 1445157402, "terms": ["What is the syntax for creating an integer attribute?", "is the syntax for creating an integer attribute?", "the syntax for creating an integer attribute?", "syntax for creating an integer attribute?", "for creating an integer attribute?", "creating an integer attribute?", "an integer attribute?", "integer attribute?", "attribute?"], "tokens": ["What", "is", "the", "syntax", "for", "creating", "an", "integer", "attribute", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1802723519", "fields": {"term": "What is example schema?", "namespace": "open-p", "hash": 1802723519, "terms": ["What is example schema?", "is example schema?", "example schema?", "schema?"], "tokens": ["What", "is", "example", "schema", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-355478253", "fields": {"term": "How can a schema be modified?", "namespace": "open-p", "hash": -355478253, "terms": ["How can a schema be modified?", "can a schema be modified?", "a schema be modified?", "schema be modified?", "be modified?", "modified?"], "tokens": ["How", "can", "a", "schema", "be", "modified", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::269430305", "fields": {"term": "What are the categories for schema modification?", "namespace": "open-p", "hash": 269430305, "terms": ["What are the categories for schema modification?", "are the categories for schema modification?", "the categories for schema modification?", "categories for schema modification?", "for schema modification?", "schema modification?", "modification?"], "tokens": ["What", "are", "the", "categories", "for", "schema", "modification", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1970405889", "fields": {"term": "What are the valid changes without restart or re-feed?", "namespace": "open-p", "hash": 1970405889, "terms": ["What are the valid changes without restart or re-feed?", "are the valid changes without restart or re-feed?", "the valid changes without restart or re-feed?", "valid changes without restart or re-feed?", "changes without restart or re-feed?", "without restart or re-feed?", "restart or re-feed?", "or re-feed?", "re-feed?"], "tokens": ["What", "are", "the", "valid", "changes", "without", "restart", "or", "re", "feed", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::734880145", "fields": {"term": "What are the changes that require restart but not re-feed?", "namespace": "open-p", "hash": 734880145, "terms": ["What are the changes that require restart but not re-feed?", "are the changes that require restart but not re-feed?", "the changes that require restart but not re-feed?", "changes that require restart but not re-feed?", "that require restart but not re-feed?", "require restart but not re-feed?", "restart but not re-feed?", "but not re-feed?", "not re-feed?", "re-feed?"], "tokens": ["What", "are", "the", "changes", "that", "require", "restart", "but", "not", "re", "feed", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-955092552", "fields": {"term": "What are the changes that require reindexing?", "namespace": "open-p", "hash": -955092552, "terms": ["What are the changes that require reindexing?", "are the changes that require reindexing?", "the changes that require reindexing?", "changes that require reindexing?", "that require reindexing?", "require reindexing?", "reindexing?"], "tokens": ["What", "are", "the", "changes", "that", "require", "reindexing", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1918117551", "fields": {"term": "What are the changes that require re-feed?", "namespace": "open-p", "hash": 1918117551, "terms": ["What are the changes that require re-feed?", "are the changes that require re-feed?", "the changes that require re-feed?", "changes that require re-feed?", "that require re-feed?", "require re-feed?", "re-feed?"], "tokens": ["What", "are", "the", "changes", "that", "require", "re", "feed", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-138381868", "fields": {"term": "What is compared when running vespa-deploy prepare?", "namespace": "open-p", "hash": -138381868, "terms": ["What is compared when running vespa-deploy prepare?", "is compared when running vespa-deploy prepare?", "compared when running vespa-deploy prepare?", "when running vespa-deploy prepare?", "running vespa-deploy prepare?", "vespa-deploy prepare?", "prepare?"], "tokens": ["What", "is", "compared", "when", "running", "vespa", "deploy", "prepare", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-599821693", "fields": {"term": "What does the output from vespa-deploy prepare specify?", "namespace": "open-p", "hash": -599821693, "terms": ["What does the output from vespa-deploy prepare specify?", "does the output from vespa-deploy prepare specify?", "the output from vespa-deploy prepare specify?", "output from vespa-deploy prepare specify?", "from vespa-deploy prepare specify?", "vespa-deploy prepare specify?", "prepare specify?", "specify?"], "tokens": ["What", "does", "the", "output", "from", "vespa", "deploy", "prepare", "specify", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1880491948", "fields": {"term": "What is a field alias?", "namespace": "open-p", "hash": -1880491948, "terms": ["What is a field alias?", "is a field alias?", "a field alias?", "field alias?", "alias?"], "tokens": ["What", "is", "a", "field", "alias", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1837407289", "fields": {"term": "What are restart actions?", "namespace": "open-p", "hash": 1837407289, "terms": ["What are restart actions?", "are restart actions?", "restart actions?", "actions?"], "tokens": ["What", "are", "restart", "actions", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2103107724", "fields": {"term": "What is a schema change?", "namespace": "open-p", "hash": -2103107724, "terms": ["What is a schema change?", "is a schema change?", "a schema change?", "schema change?", "change?"], "tokens": ["What", "is", "a", "schema", "change", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1787450590", "fields": {"term": "What does the output of vespa-deploy prepare specify?", "namespace": "open-p", "hash": -1787450590, "terms": ["What does the output of vespa-deploy prepare specify?", "does the output of vespa-deploy prepare specify?", "the output of vespa-deploy prepare specify?", "output of vespa-deploy prepare specify?", "of vespa-deploy prepare specify?", "vespa-deploy prepare specify?", "prepare specify?", "specify?"], "tokens": ["What", "does", "the", "output", "of", "vespa", "deploy", "prepare", "specify", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-772831999", "fields": {"term": "What are the changes that can be made to attribute settings?", "namespace": "open-p", "hash": -772831999, "terms": ["What are the changes that can be made to attribute settings?", "are the changes that can be made to attribute settings?", "the changes that can be made to attribute settings?", "changes that can be made to attribute settings?", "that can be made to attribute settings?", "can be made to attribute settings?", "be made to attribute settings?", "made to attribute settings?", "to attribute settings?", "attribute settings?", "settings?"], "tokens": ["What", "are", "the", "changes", "that", "can", "be", "made", "to", "attribute", "settings", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-607830987", "fields": {"term": "What is the procedure for changing attribute aspect of a document field?", "namespace": "open-p", "hash": -607830987, "terms": ["What is the procedure for changing attribute aspect of a document field?", "is the procedure for changing attribute aspect of a document field?", "the procedure for changing attribute aspect of a document field?", "procedure for changing attribute aspect of a document field?", "for changing attribute aspect of a document field?", "changing attribute aspect of a document field?", "attribute aspect of a document field?", "aspect of a document field?", "of a document field?", "a document field?", "document field?", "field?"], "tokens": ["What", "is", "the", "procedure", "for", "changing", "attribute", "aspect", "of", "a", "document", "field", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1321313310", "fields": {"term": "When is reindexing triggered?", "namespace": "open-p", "hash": -1321313310, "terms": ["When is reindexing triggered?", "is reindexing triggered?", "reindexing triggered?", "triggered?"], "tokens": ["When", "is", "reindexing", "triggered", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1371628428", "fields": {"term": "How does reindexing affect annotations?", "namespace": "open-p", "hash": 1371628428, "terms": ["How does reindexing affect annotations?", "does reindexing affect annotations?", "reindexing affect annotations?", "affect annotations?", "annotations?"], "tokens": ["How", "does", "reindexing", "affect", "annotations", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-580718030", "fields": {"term": "What changes need reindexing?", "namespace": "open-p", "hash": -580718030, "terms": ["What changes need reindexing?", "changes need reindexing?", "need reindexing?", "reindexing?"], "tokens": ["What", "changes", "need", "reindexing", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1861755737", "fields": {"term": "How to trigger reindexing?", "namespace": "open-p", "hash": 1861755737, "terms": ["How to trigger reindexing?", "to trigger reindexing?", "trigger reindexing?", "reindexing?"], "tokens": ["How", "to", "trigger", "reindexing", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1894497865", "fields": {"term": "Why does reindexing require disk space?", "namespace": "open-p", "hash": 1894497865, "terms": ["Why does reindexing require disk space?", "does reindexing require disk space?", "reindexing require disk space?", "require disk space?", "disk space?", "space?"], "tokens": ["Why", "does", "reindexing", "require", "disk", "space", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-633831125", "fields": {"term": "What is the output of vespa-deploy prepare?", "namespace": "open-p", "hash": -633831125, "terms": ["What is the output of vespa-deploy prepare?", "is the output of vespa-deploy prepare?", "the output of vespa-deploy prepare?", "output of vespa-deploy prepare?", "of vespa-deploy prepare?", "vespa-deploy prepare?", "prepare?"], "tokens": ["What", "is", "the", "output", "of", "vespa", "deploy", "prepare", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::-637731981", "fields": {"term": "What is re-feeding?", "namespace": "open-p", "hash": -637731981, "terms": ["What is re-feeding?", "is re-feeding?", "re-feeding?"], "tokens": ["What", "is", "re", "feeding", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1624310146", "fields": {"term": "What changes require re-feeding?", "namespace": "open-p", "hash": 1624310146, "terms": ["What changes require re-feeding?", "changes require re-feeding?", "require re-feeding?", "re-feeding?"], "tokens": ["What", "changes", "require", "re", "feeding", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-281127179", "fields": {"term": "How to re-feed documents?", "namespace": "open-p", "hash": -281127179, "terms": ["How to re-feed documents?", "to re-feed documents?", "re-feed documents?", "documents?"], "tokens": ["How", "to", "re", "feed", "documents", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1411932684", "fields": {"term": "Where are schemas stored?", "namespace": "open-p", "hash": 1411932684, "terms": ["Where are schemas stored?", "are schemas stored?", "schemas stored?", "stored?"], "tokens": ["Where", "are", "schemas", "stored", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-416237873", "fields": {"term": "What is the format of a schema file?", "namespace": "open-p", "hash": -416237873, "terms": ["What is the format of a schema file?", "is the format of a schema file?", "the format of a schema file?", "format of a schema file?", "of a schema file?", "a schema file?", "schema file?", "file?"], "tokens": ["What", "is", "the", "format", "of", "a", "schema", "file", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1276009303", "fields": {"term": "What is schema inheritance?", "namespace": "open-p", "hash": 1276009303, "terms": ["What is schema inheritance?", "is schema inheritance?", "schema inheritance?", "inheritance?"], "tokens": ["What", "is", "schema", "inheritance", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::759397861", "fields": {"term": "What is a document type in a schema?", "namespace": "open-p", "hash": 759397861, "terms": ["What is a document type in a schema?", "is a document type in a schema?", "a document type in a schema?", "document type in a schema?", "type in a schema?", "in a schema?", "a schema?", "schema?"], "tokens": ["What", "is", "a", "document", "type", "in", "a", "schema", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::510896693", "fields": {"term": "What is the purpose of a schema?", "namespace": "open-p", "hash": 510896693, "terms": ["What is the purpose of a schema?", "is the purpose of a schema?", "the purpose of a schema?", "purpose of a schema?", "of a schema?", "a schema?", "schema?"], "tokens": ["What", "is", "the", "purpose", "of", "a", "schema", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-447855226", "fields": {"term": "Where can I find a complete schema reference?", "namespace": "open-p", "hash": -447855226, "terms": ["Where can I find a complete schema reference?", "can I find a complete schema reference?", "I find a complete schema reference?", "find a complete schema reference?", "a complete schema reference?", "complete schema reference?", "schema reference?", "reference?"], "tokens": ["Where", "can", "I", "find", "a", "complete", "schema", "reference", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1987630536", "fields": {"term": "Can multivalue fields be used in grouping?", "namespace": "open-p", "hash": -1987630536, "terms": ["Can multivalue fields be used in grouping?", "multivalue fields be used in grouping?", "fields be used in grouping?", "be used in grouping?", "used in grouping?", "in grouping?", "grouping?"], "tokens": ["Can", "multivalue", "fields", "be", "used", "in", "grouping", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1746633353", "fields": {"term": "Can attributes in maps and arrays be used in ranking?", "namespace": "open-p", "hash": -1746633353, "terms": ["Can attributes in maps and arrays be used in ranking?", "attributes in maps and arrays be used in ranking?", "in maps and arrays be used in ranking?", "maps and arrays be used in ranking?", "and arrays be used in ranking?", "arrays be used in ranking?", "be used in ranking?", "used in ranking?", "in ranking?", "ranking?"], "tokens": ["Can", "attributes", "in", "maps", "and", "arrays", "be", "used", "in", "ranking", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1241643150", "fields": {"term": "How to filter based on number of elements?", "namespace": "open-p", "hash": 1241643150, "terms": ["How to filter based on number of elements?", "to filter based on number of elements?", "filter based on number of elements?", "based on number of elements?", "on number of elements?", "number of elements?", "of elements?", "elements?"], "tokens": ["How", "to", "filter", "based", "on", "number", "of", "elements", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-336006840", "fields": {"term": "Is filtering based on number of elements more expensive than using a separate field?", "namespace": "open-p", "hash": -336006840, "terms": ["Is filtering based on number of elements more expensive than using a separate field?", "filtering based on number of elements more expensive than using a separate field?", "based on number of elements more expensive than using a separate field?", "on number of elements more expensive than using a separate field?", "number of elements more expensive than using a separate field?", "of elements more expensive than using a separate field?", "elements more expensive than using a separate field?", "more expensive than using a separate field?", "expensive than using a separate field?", "than using a separate field?", "using a separate field?", "a separate field?", "separate field?", "field?"], "tokens": ["Is", "filtering", "based", "on", "number", "of", "elements", "more", "expensive", "than", "using", "a", "separate", "field", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::595218967", "fields": {"term": "What are the important indexing modes?", "namespace": "open-p", "hash": 595218967, "terms": ["What are the important indexing modes?", "are the important indexing modes?", "the important indexing modes?", "important indexing modes?", "indexing modes?", "modes?"], "tokens": ["What", "are", "the", "important", "indexing", "modes", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::495826099", "fields": {"term": "What is match mode?", "namespace": "open-p", "hash": 495826099, "terms": ["What is match mode?", "is match mode?", "match mode?", "mode?"], "tokens": ["What", "is", "match", "mode", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1037073272", "fields": {"term": "What is sameElement() used for?", "namespace": "open-p", "hash": 1037073272, "terms": ["What is sameElement() used for?", "is sameElement() used for?", "sameElement() used for?", "used for?", "for?"], "tokens": ["What", "is", "sameElement", "()", "used", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::609063698", "fields": {"term": "What is the default fieldset?", "namespace": "open-p", "hash": 609063698, "terms": ["What is the default fieldset?", "is the default fieldset?", "the default fieldset?", "default fieldset?", "fieldset?"], "tokens": ["What", "is", "the", "default", "fieldset", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-712634454", "fields": {"term": "What does a rank profile define?", "namespace": "open-p", "hash": -712634454, "terms": ["What does a rank profile define?", "does a rank profile define?", "a rank profile define?", "rank profile define?", "profile define?", "define?"], "tokens": ["What", "does", "a", "rank", "profile", "define", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::901703440", "fields": {"term": "How does the plugin simplify schema work?", "namespace": "open-p", "hash": 901703440, "terms": ["How does the plugin simplify schema work?", "does the plugin simplify schema work?", "the plugin simplify schema work?", "plugin simplify schema work?", "simplify schema work?", "schema work?", "work?"], "tokens": ["How", "does", "the", "plugin", "simplify", "schema", "work", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-819370963", "fields": {"term": "How does Vespa handle schema modifications?", "namespace": "open-p", "hash": -819370963, "terms": ["How does Vespa handle schema modifications?", "does Vespa handle schema modifications?", "Vespa handle schema modifications?", "handle schema modifications?", "schema modifications?", "modifications?"], "tokens": ["How", "does", "Vespa", "handle", "schema", "modifications", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1248490635", "fields": {"term": "What type of changes can be made to a schema without validation override?", "namespace": "open-p", "hash": -1248490635, "terms": ["What type of changes can be made to a schema without validation override?", "type of changes can be made to a schema without validation override?", "of changes can be made to a schema without validation override?", "changes can be made to a schema without validation override?", "can be made to a schema without validation override?", "be made to a schema without validation override?", "made to a schema without validation override?", "to a schema without validation override?", "a schema without validation override?", "schema without validation override?", "without validation override?", "validation override?", "override?"], "tokens": ["What", "type", "of", "changes", "can", "be", "made", "to", "a", "schema", "without", "validation", "override", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::544514314", "fields": {"term": "How to add a validation override?", "namespace": "open-p", "hash": 544514314, "terms": ["How to add a validation override?", "to add a validation override?", "add a validation override?", "a validation override?", "validation override?", "override?"], "tokens": ["How", "to", "add", "a", "validation", "override", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-167289066", "fields": {"term": "What are schema modifications?", "namespace": "open-p", "hash": -167289066, "terms": ["What are schema modifications?", "are schema modifications?", "schema modifications?", "modifications?"], "tokens": ["What", "are", "schema", "modifications", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1933388257", "fields": {"term": "How do applications map multiple schemas?", "namespace": "open-p", "hash": -1933388257, "terms": ["How do applications map multiple schemas?", "do applications map multiple schemas?", "applications map multiple schemas?", "map multiple schemas?", "multiple schemas?", "schemas?"], "tokens": ["How", "do", "applications", "map", "multiple", "schemas", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-846094013", "fields": {"term": "How to limit a query to a subset of content clusters?", "namespace": "open-p", "hash": -846094013, "terms": ["How to limit a query to a subset of content clusters?", "to limit a query to a subset of content clusters?", "limit a query to a subset of content clusters?", "a query to a subset of content clusters?", "query to a subset of content clusters?", "to a subset of content clusters?", "a subset of content clusters?", "subset of content clusters?", "of content clusters?", "content clusters?", "clusters?"], "tokens": ["How", "to", "limit", "a", "query", "to", "a", "subset", "of", "content", "clusters", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::607559084", "fields": {"term": "What is the use of restrict and sources combined?", "namespace": "open-p", "hash": 607559084, "terms": ["What is the use of restrict and sources combined?", "is the use of restrict and sources combined?", "the use of restrict and sources combined?", "use of restrict and sources combined?", "of restrict and sources combined?", "restrict and sources combined?", "and sources combined?", "sources combined?", "combined?"], "tokens": ["What", "is", "the", "use", "of", "restrict", "and", "sources", "combined", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1360081720", "fields": {"term": "Where can I find more details about content nodes and schemas?", "namespace": "open-p", "hash": 1360081720, "terms": ["Where can I find more details about content nodes and schemas?", "can I find more details about content nodes and schemas?", "I find more details about content nodes and schemas?", "find more details about content nodes and schemas?", "more details about content nodes and schemas?", "details about content nodes and schemas?", "about content nodes and schemas?", "content nodes and schemas?", "nodes and schemas?", "and schemas?", "schemas?"], "tokens": ["Where", "can", "I", "find", "more", "details", "about", "content", "nodes", "and", "schemas", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1867707303", "fields": {"term": "What is document type inheritance?", "namespace": "open-p", "hash": 1867707303, "terms": ["What is document type inheritance?", "is document type inheritance?", "document type inheritance?", "type inheritance?", "inheritance?"], "tokens": ["What", "is", "document", "type", "inheritance", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1523169341", "fields": {"term": "What are the elements that support inheritance?", "namespace": "open-p", "hash": -1523169341, "terms": ["What are the elements that support inheritance?", "are the elements that support inheritance?", "the elements that support inheritance?", "elements that support inheritance?", "that support inheritance?", "support inheritance?", "inheritance?"], "tokens": ["What", "are", "the", "elements", "that", "support", "inheritance", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1558521851", "fields": {"term": "Why is type hierarchy useful for federated queries?", "namespace": "open-p", "hash": 1558521851, "terms": ["Why is type hierarchy useful for federated queries?", "is type hierarchy useful for federated queries?", "type hierarchy useful for federated queries?", "hierarchy useful for federated queries?", "useful for federated queries?", "for federated queries?", "federated queries?", "queries?"], "tokens": ["Why", "is", "type", "hierarchy", "useful", "for", "federated", "queries", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1867550409", "fields": {"term": "What is the purpose of the 'title' field in music and book schemas?", "namespace": "open-p", "hash": 1867550409, "terms": ["What is the purpose of the 'title' field in music and book schemas?", "is the purpose of the 'title' field in music and book schemas?", "the purpose of the 'title' field in music and book schemas?", "purpose of the 'title' field in music and book schemas?", "of the 'title' field in music and book schemas?", "the 'title' field in music and book schemas?", "'title' field in music and book schemas?", "field in music and book schemas?", "in music and book schemas?", "music and book schemas?", "and book schemas?", "book schemas?", "schemas?"], "tokens": ["What", "is", "the", "purpose", "of", "the", "'", "title", "'", "field", "in", "music", "and", "book", "schemas", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1043486100", "fields": {"term": "What is the difference between schemas music and books?", "namespace": "open-p", "hash": -1043486100, "terms": ["What is the difference between schemas music and books?", "is the difference between schemas music and books?", "the difference between schemas music and books?", "difference between schemas music and books?", "between schemas music and books?", "schemas music and books?", "music and books?", "and books?", "books?"], "tokens": ["What", "is", "the", "difference", "between", "schemas", "music", "and", "books", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1520327403", "fields": {"term": "How are rank profiles inherited?", "namespace": "open-p", "hash": 1520327403, "terms": ["How are rank profiles inherited?", "are rank profiles inherited?", "rank profiles inherited?", "profiles inherited?", "inherited?"], "tokens": ["How", "are", "rank", "profiles", "inherited", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1965144403", "fields": {"term": "What is the default rank profile?", "namespace": "open-p", "hash": 1965144403, "terms": ["What is the default rank profile?", "is the default rank profile?", "the default rank profile?", "default rank profile?", "rank profile?", "profile?"], "tokens": ["What", "is", "the", "default", "rank", "profile", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::-567969351", "fields": {"term": "What are document summaries?", "namespace": "open-p", "hash": -567969351, "terms": ["What are document summaries?", "are document summaries?", "document summaries?", "summaries?"], "tokens": ["What", "are", "document", "summaries", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1751088531", "fields": {"term": "How do document summaries inherit?", "namespace": "open-p", "hash": 1751088531, "terms": ["How do document summaries inherit?", "do document summaries inherit?", "document summaries inherit?", "summaries inherit?", "inherit?"], "tokens": ["How", "do", "document", "summaries", "inherit", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1924136957", "fields": {"term": "What is a document summary for 'items_summary_tiny'?", "namespace": "open-p", "hash": -1924136957, "terms": ["What is a document summary for 'items_summary_tiny'?", "is a document summary for 'items_summary_tiny'?", "a document summary for 'items_summary_tiny'?", "document summary for 'items_summary_tiny'?", "summary for 'items_summary_tiny'?", "for 'items_summary_tiny'?", "'items_summary_tiny'?"], "tokens": ["What", "is", "a", "document", "summary", "for", "'", "items_summary_tiny", "'?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2044816070", "fields": {"term": "What is a document summary for 'items_summary_full'?", "namespace": "open-p", "hash": 2044816070, "terms": ["What is a document summary for 'items_summary_full'?", "is a document summary for 'items_summary_full'?", "a document summary for 'items_summary_full'?", "document summary for 'items_summary_full'?", "summary for 'items_summary_full'?", "for 'items_summary_full'?", "'items_summary_full'?"], "tokens": ["What", "is", "a", "document", "summary", "for", "'", "items_summary_full", "'?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::269627389", "fields": {"term": "What is the 'title' field in 'items_summary_tiny' document summary?", "namespace": "open-p", "hash": 269627389, "terms": ["What is the 'title' field in 'items_summary_tiny' document summary?", "is the 'title' field in 'items_summary_tiny' document summary?", "the 'title' field in 'items_summary_tiny' document summary?", "'title' field in 'items_summary_tiny' document summary?", "field in 'items_summary_tiny' document summary?", "in 'items_summary_tiny' document summary?", "'items_summary_tiny' document summary?", "document summary?", "summary?"], "tokens": ["What", "is", "the", "'", "title", "'", "field", "in", "'", "items_summary_tiny", "'", "document", "summary", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1564471836", "fields": {"term": "What is the 'author' field in 'items_summary_full' document summary?", "namespace": "open-p", "hash": -1564471836, "terms": ["What is the 'author' field in 'items_summary_full' document summary?", "is the 'author' field in 'items_summary_full' document summary?", "the 'author' field in 'items_summary_full' document summary?", "'author' field in 'items_summary_full' document summary?", "field in 'items_summary_full' document summary?", "in 'items_summary_full' document summary?", "'items_summary_full' document summary?", "document summary?", "summary?"], "tokens": ["What", "is", "the", "'", "author", "'", "field", "in", "'", "items_summary_full", "'", "document", "summary", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1254367849", "fields": {"term": "How to develop Searcher components?", "namespace": "open-p", "hash": -1254367849, "terms": ["How to develop Searcher components?", "to develop Searcher components?", "develop Searcher components?", "Searcher components?", "components?"], "tokens": ["How", "to", "develop", "Searcher", "components", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-169296797", "fields": {"term": "What is the search method signature?", "namespace": "open-p", "hash": -169296797, "terms": ["What is the search method signature?", "is the search method signature?", "the search method signature?", "search method signature?", "method signature?", "signature?"], "tokens": ["What", "is", "the", "search", "method", "signature", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-611564022", "fields": {"term": "What is a synchronous model?", "namespace": "open-p", "hash": -611564022, "terms": ["What is a synchronous model?", "is a synchronous model?", "a synchronous model?", "synchronous model?", "model?"], "tokens": ["What", "is", "a", "synchronous", "model", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1408278468", "fields": {"term": "What is a Result object?", "namespace": "open-p", "hash": -1408278468, "terms": ["What is a Result object?", "is a Result object?", "a Result object?", "Result object?", "object?"], "tokens": ["What", "is", "a", "Result", "object", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2102436703", "fields": {"term": "What does a Hit object contain?", "namespace": "open-p", "hash": 2102436703, "terms": ["What does a Hit object contain?", "does a Hit object contain?", "a Hit object contain?", "Hit object contain?", "object contain?", "contain?"], "tokens": ["What", "does", "a", "Hit", "object", "contain", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1497556402", "fields": {"term": "How are HitGroups organized?", "namespace": "open-p", "hash": 1497556402, "terms": ["How are HitGroups organized?", "are HitGroups organized?", "HitGroups organized?", "organized?"], "tokens": ["How", "are", "HitGroups", "organized", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::111100508", "fields": {"term": "What is a boolean query tree?", "namespace": "open-p", "hash": 111100508, "terms": ["What is a boolean query tree?", "is a boolean query tree?", "a boolean query tree?", "boolean query tree?", "query tree?", "tree?"], "tokens": ["What", "is", "a", "boolean", "query", "tree", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1858574141", "fields": {"term": "How can structured information be handled in a Searcher?", "namespace": "open-p", "hash": -1858574141, "terms": ["How can structured information be handled in a Searcher?", "can structured information be handled in a Searcher?", "structured information be handled in a Searcher?", "information be handled in a Searcher?", "be handled in a Searcher?", "handled in a Searcher?", "in a Searcher?", "a Searcher?", "Searcher?"], "tokens": ["How", "can", "structured", "information", "be", "handled", "in", "a", "Searcher", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-444855491", "fields": {"term": "What are search chains?", "namespace": "open-p", "hash": -444855491, "terms": ["What are search chains?", "are search chains?", "search chains?", "chains?"], "tokens": ["What", "are", "search", "chains", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::-2146023281", "fields": {"term": "How are search chains executed?", "namespace": "open-p", "hash": -2146023281, "terms": ["How are search chains executed?", "are search chains executed?", "search chains executed?", "chains executed?", "executed?"], "tokens": ["How", "are", "search", "chains", "executed", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1899263408", "fields": {"term": "What is a searcher instance?", "namespace": "open-p", "hash": -1899263408, "terms": ["What is a searcher instance?", "is a searcher instance?", "a searcher instance?", "searcher instance?", "instance?"], "tokens": ["What", "is", "a", "searcher", "instance", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-479156017", "fields": {"term": "Can a Searcher instance exist in multiple search chains?", "namespace": "open-p", "hash": -479156017, "terms": ["Can a Searcher instance exist in multiple search chains?", "a Searcher instance exist in multiple search chains?", "Searcher instance exist in multiple search chains?", "instance exist in multiple search chains?", "exist in multiple search chains?", "in multiple search chains?", "multiple search chains?", "search chains?", "chains?"], "tokens": ["Can", "a", "Searcher", "instance", "exist", "in", "multiple", "search", "chains", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1724057937", "fields": {"term": "What is the purpose of asynchronous execution?", "namespace": "open-p", "hash": 1724057937, "terms": ["What is the purpose of asynchronous execution?", "is the purpose of asynchronous execution?", "the purpose of asynchronous execution?", "purpose of asynchronous execution?", "of asynchronous execution?", "asynchronous execution?", "execution?"], "tokens": ["What", "is", "the", "purpose", "of", "asynchronous", "execution", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1908254558", "fields": {"term": "How is the execution order of searchers determined?", "namespace": "open-p", "hash": -1908254558, "terms": ["How is the execution order of searchers determined?", "is the execution order of searchers determined?", "the execution order of searchers determined?", "execution order of searchers determined?", "order of searchers determined?", "of searchers determined?", "searchers determined?", "determined?"], "tokens": ["How", "is", "the", "execution", "order", "of", "searchers", "determined", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-223748773", "fields": {"term": "Where can I find more information about searchers and search chains?", "namespace": "open-p", "hash": -223748773, "terms": ["Where can I find more information about searchers and search chains?", "can I find more information about searchers and search chains?", "I find more information about searchers and search chains?", "find more information about searchers and search chains?", "more information about searchers and search chains?", "information about searchers and search chains?", "about searchers and search chains?", "searchers and search chains?", "and search chains?", "search chains?", "chains?"], "tokens": ["Where", "can", "I", "find", "more", "information", "about", "searchers", "and", "search", "chains", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1347989894", "fields": {"term": "What is SimpleSearcher?", "namespace": "open-p", "hash": 1347989894, "terms": ["What is SimpleSearcher?", "is SimpleSearcher?", "SimpleSearcher?"], "tokens": ["What", "is", "SimpleSearcher", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1319388227", "fields": {"term": "How is SimpleSearcher used?", "namespace": "open-p", "hash": -1319388227, "terms": ["How is SimpleSearcher used?", "is SimpleSearcher used?", "SimpleSearcher used?", "used?"], "tokens": ["How", "is", "SimpleSearcher", "used", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1218771700", "fields": {"term": "What is the function of the first line in SimpleSearcher?", "namespace": "open-p", "hash": 1218771700, "terms": ["What is the function of the first line in SimpleSearcher?", "is the function of the first line in SimpleSearcher?", "the function of the first line in SimpleSearcher?", "function of the first line in SimpleSearcher?", "of the first line in SimpleSearcher?", "the first line in SimpleSearcher?", "first line in SimpleSearcher?", "line in SimpleSearcher?", "in SimpleSearcher?", "SimpleSearcher?"], "tokens": ["What", "is", "the", "function", "of", "the", "first", "line", "in", "SimpleSearcher", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-914521656", "fields": {"term": "How does SimpleSearcher modify Result?", "namespace": "open-p", "hash": -914521656, "terms": ["How does SimpleSearcher modify Result?", "does SimpleSearcher modify Result?", "SimpleSearcher modify Result?", "modify Result?", "Result?"], "tokens": ["How", "does", "SimpleSearcher", "modify", "Result", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-644772259", "fields": {"term": "When is a new instance of SimpleSearcher created?", "namespace": "open-p", "hash": -644772259, "terms": ["When is a new instance of SimpleSearcher created?", "is a new instance of SimpleSearcher created?", "a new instance of SimpleSearcher created?", "new instance of SimpleSearcher created?", "instance of SimpleSearcher created?", "of SimpleSearcher created?", "SimpleSearcher created?", "created?"], "tokens": ["When", "is", "a", "new", "instance", "of", "SimpleSearcher", "created", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1399982943", "fields": {"term": "What is a search chain?", "namespace": "open-p", "hash": 1399982943, "terms": ["What is a search chain?", "is a search chain?", "a search chain?", "search chain?", "chain?"], "tokens": ["What", "is", "a", "search", "chain", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::-1758475713", "fields": {"term": "What is the SimpleSearcher?", "namespace": "open-p", "hash": -1758475713, "terms": ["What is the SimpleSearcher?", "is the SimpleSearcher?", "the SimpleSearcher?", "SimpleSearcher?"], "tokens": ["What", "is", "the", "SimpleSearcher", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-211544112", "fields": {"term": "What is a mock searcher?", "namespace": "open-p", "hash": -211544112, "terms": ["What is a mock searcher?", "is a mock searcher?", "a mock searcher?", "mock searcher?", "searcher?"], "tokens": ["What", "is", "a", "mock", "searcher", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-191946503", "fields": {"term": "What is Execution.Context?", "namespace": "open-p", "hash": -191946503, "terms": ["What is Execution.Context?", "is Execution.Context?", "Execution.Context?"], "tokens": ["What", "is", "Execution", "Context", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1725364423", "fields": {"term": "What is the Execution class?", "namespace": "open-p", "hash": 1725364423, "terms": ["What is the Execution class?", "is the Execution class?", "the Execution class?", "Execution class?", "class?"], "tokens": ["What", "is", "the", "Execution", "class", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1425529910", "fields": {"term": "What is the purpose of unit tests?", "namespace": "open-p", "hash": 1425529910, "terms": ["What is the purpose of unit tests?", "is the purpose of unit tests?", "the purpose of unit tests?", "purpose of unit tests?", "of unit tests?", "unit tests?", "tests?"], "tokens": ["What", "is", "the", "purpose", "of", "unit", "tests", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1274940389", "fields": {"term": "How to test a searcher?", "namespace": "open-p", "hash": -1274940389, "terms": ["How to test a searcher?", "to test a searcher?", "test a searcher?", "a searcher?", "searcher?"], "tokens": ["How", "to", "test", "a", "searcher", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1599015943", "fields": {"term": "What is the query object?", "namespace": "open-p", "hash": -1599015943, "terms": ["What is the query object?", "is the query object?", "the query object?", "query object?", "object?"], "tokens": ["What", "is", "the", "query", "object", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-339762631", "fields": {"term": "What is the general property store?", "namespace": "open-p", "hash": -339762631, "terms": ["What is the general property store?", "is the general property store?", "the general property store?", "general property store?", "property store?", "store?"], "tokens": ["What", "is", "the", "general", "property", "store", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::186265241", "fields": {"term": "What can be stored in the property store?", "namespace": "open-p", "hash": 186265241, "terms": ["What can be stored in the property store?", "can be stored in the property store?", "be stored in the property store?", "stored in the property store?", "in the property store?", "the property store?", "property store?", "store?"], "tokens": ["What", "can", "be", "stored", "in", "the", "property", "store", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-769805352", "fields": {"term": "What is the purpose of setTo?", "namespace": "open-p", "hash": -769805352, "terms": ["What is the purpose of setTo?", "is the purpose of setTo?", "the purpose of setTo?", "purpose of setTo?", "of setTo?", "setTo?"], "tokens": ["What", "is", "the", "purpose", "of", "setTo", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1241796213", "fields": {"term": "What is the purpose of getFrom?", "namespace": "open-p", "hash": 1241796213, "terms": ["What is the purpose of getFrom?", "is the purpose of getFrom?", "the purpose of getFrom?", "purpose of getFrom?", "of getFrom?", "getFrom?"], "tokens": ["What", "is", "the", "purpose", "of", "getFrom", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::211765479", "fields": {"term": "What is the SomeObject class?", "namespace": "open-p", "hash": 211765479, "terms": ["What is the SomeObject class?", "is the SomeObject class?", "the SomeObject class?", "SomeObject class?", "class?"], "tokens": ["What", "is", "the", "SomeObject", "class", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1126047438", "fields": {"term": "Why should objects enforce immutability?", "namespace": "open-p", "hash": 1126047438, "terms": ["Why should objects enforce immutability?", "should objects enforce immutability?", "objects enforce immutability?", "enforce immutability?", "immutability?"], "tokens": ["Why", "should", "objects", "enforce", "immutability", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1440915720", "fields": {"term": "How should objects support downstream modifications?", "namespace": "open-p", "hash": 1440915720, "terms": ["How should objects support downstream modifications?", "should objects support downstream modifications?", "objects support downstream modifications?", "support downstream modifications?", "downstream modifications?", "modifications?"], "tokens": ["How", "should", "objects", "support", "downstream", "modifications", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::902102404", "fields": {"term": "What is QueryContext?", "namespace": "open-p", "hash": 902102404, "terms": ["What is QueryContext?", "is QueryContext?", "QueryContext?"], "tokens": ["What", "is", "QueryContext", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1832037961", "fields": {"term": "How is QueryContext accessed safely?", "namespace": "open-p", "hash": 1832037961, "terms": ["How is QueryContext accessed safely?", "is QueryContext accessed safely?", "QueryContext accessed safely?", "accessed safely?", "safely?"], "tokens": ["How", "is", "QueryContext", "accessed", "safely", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2091033488", "fields": {"term": "What is a Query object?", "namespace": "open-p", "hash": 2091033488, "terms": ["What is a Query object?", "is a Query object?", "a Query object?", "Query object?", "object?"], "tokens": ["What", "is", "a", "Query", "object", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1498428130", "fields": {"term": "What is the purpose of QueryContext?", "namespace": "open-p", "hash": 1498428130, "terms": ["What is the purpose of QueryContext?", "is the purpose of QueryContext?", "the purpose of QueryContext?", "purpose of QueryContext?", "of QueryContext?", "QueryContext?"], "tokens": ["What", "is", "the", "purpose", "of", "QueryContext", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1052800818", "fields": {"term": "What can be set in QueryContext?", "namespace": "open-p", "hash": 1052800818, "terms": ["What can be set in QueryContext?", "can be set in QueryContext?", "be set in QueryContext?", "set in QueryContext?", "in QueryContext?", "QueryContext?"], "tokens": ["What", "can", "be", "set", "in", "QueryContext", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2029862589", "fields": {"term": "How are properties set in QueryContext?", "namespace": "open-p", "hash": -2029862589, "terms": ["How are properties set in QueryContext?", "are properties set in QueryContext?", "properties set in QueryContext?", "set in QueryContext?", "in QueryContext?", "QueryContext?"], "tokens": ["How", "are", "properties", "set", "in", "QueryContext", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-8500534", "fields": {"term": "What is the caveats in QueryContext?", "namespace": "open-p", "hash": -8500534, "terms": ["What is the caveats in QueryContext?", "is the caveats in QueryContext?", "the caveats in QueryContext?", "caveats in QueryContext?", "in QueryContext?", "QueryContext?"], "tokens": ["What", "is", "the", "caveats", "in", "QueryContext", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1825556265", "fields": {"term": "How is QueryContext accessed from threads?", "namespace": "open-p", "hash": -1825556265, "terms": ["How is QueryContext accessed from threads?", "is QueryContext accessed from threads?", "QueryContext accessed from threads?", "accessed from threads?", "from threads?", "threads?"], "tokens": ["How", "is", "QueryContext", "accessed", "from", "threads", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1877217509", "fields": {"term": "How are parameters passed?", "namespace": "open-p", "hash": -1877217509, "terms": ["How are parameters passed?", "are parameters passed?", "parameters passed?", "passed?"], "tokens": ["How", "are", "parameters", "passed", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1365678640", "fields": {"term": "What happens when simultaneous requests arrive?", "namespace": "open-p", "hash": -1365678640, "terms": ["What happens when simultaneous requests arrive?", "happens when simultaneous requests arrive?", "when simultaneous requests arrive?", "simultaneous requests arrive?", "requests arrive?", "arrive?"], "tokens": ["What", "happens", "when", "simultaneous", "requests", "arrive", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2138191633", "fields": {"term": "What constraints are placed on searcher classes?", "namespace": "open-p", "hash": 2138191633, "terms": ["What constraints are placed on searcher classes?", "constraints are placed on searcher classes?", "are placed on searcher classes?", "placed on searcher classes?", "on searcher classes?", "searcher classes?", "classes?"], "tokens": ["What", "constraints", "are", "placed", "on", "searcher", "classes", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1866582318", "fields": {"term": "What kind of variables are member variables?", "namespace": "open-p", "hash": 1866582318, "terms": ["What kind of variables are member variables?", "kind of variables are member variables?", "of variables are member variables?", "variables are member variables?", "are member variables?", "member variables?", "variables?"], "tokens": ["What", "kind", "of", "variables", "are", "member", "variables", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-837561651", "fields": {"term": "What is a multithread-safe searcher?", "namespace": "open-p", "hash": -837561651, "terms": ["What is a multithread-safe searcher?", "is a multithread-safe searcher?", "a multithread-safe searcher?", "multithread-safe searcher?", "searcher?"], "tokens": ["What", "is", "a", "multithread", "safe", "searcher", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::141832241", "fields": {"term": "What is the restriction for member variables?", "namespace": "open-p", "hash": 141832241, "terms": ["What is the restriction for member variables?", "is the restriction for member variables?", "the restriction for member variables?", "restriction for member variables?", "for member variables?", "member variables?", "variables?"], "tokens": ["What", "is", "the", "restriction", "for", "member", "variables", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1164196739", "fields": {"term": "What is the purpose of allocating worker threads?", "namespace": "open-p", "hash": 1164196739, "terms": ["What is the purpose of allocating worker threads?", "is the purpose of allocating worker threads?", "the purpose of allocating worker threads?", "purpose of allocating worker threads?", "of allocating worker threads?", "allocating worker threads?", "worker threads?", "threads?"], "tokens": ["What", "is", "the", "purpose", "of", "allocating", "worker", "threads", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1768242150", "fields": {"term": "What are the options for implementing a multithread-safe searcher?", "namespace": "open-p", "hash": 1768242150, "terms": ["What are the options for implementing a multithread-safe searcher?", "are the options for implementing a multithread-safe searcher?", "the options for implementing a multithread-safe searcher?", "options for implementing a multithread-safe searcher?", "for implementing a multithread-safe searcher?", "implementing a multithread-safe searcher?", "a multithread-safe searcher?", "multithread-safe searcher?", "searcher?"], "tokens": ["What", "are", "the", "options", "for", "implementing", "a", "multithread", "safe", "searcher", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::860858458", "fields": {"term": "What is the purpose of thread synchronization?", "namespace": "open-p", "hash": 860858458, "terms": ["What is the purpose of thread synchronization?", "is the purpose of thread synchronization?", "the purpose of thread synchronization?", "purpose of thread synchronization?", "of thread synchronization?", "thread synchronization?", "synchronization?"], "tokens": ["What", "is", "the", "purpose", "of", "thread", "synchronization", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-291870127", "fields": {"term": "How does the search container support multiphase searching?", "namespace": "open-p", "hash": -291870127, "terms": ["How does the search container support multiphase searching?", "does the search container support multiphase searching?", "the search container support multiphase searching?", "search container support multiphase searching?", "container support multiphase searching?", "support multiphase searching?", "multiphase searching?", "searching?"], "tokens": ["How", "does", "the", "search", "container", "support", "multiphase", "searching", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1399932607", "fields": {"term": "What are the execution fill methods?", "namespace": "open-p", "hash": 1399932607, "terms": ["What are the execution fill methods?", "are the execution fill methods?", "the execution fill methods?", "execution fill methods?", "fill methods?", "methods?"], "tokens": ["What", "are", "the", "execution", "fill", "methods", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::781948655", "fields": {"term": "What kind of searchers should call fill() method?", "namespace": "open-p", "hash": 781948655, "terms": ["What kind of searchers should call fill() method?", "kind of searchers should call fill() method?", "of searchers should call fill() method?", "searchers should call fill() method?", "should call fill() method?", "call fill() method?", "fill() method?", "method?"], "tokens": ["What", "kind", "of", "searchers", "should", "call", "fill", "()", "method", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::862206874", "fields": {"term": "What is the purpose of fill() method?", "namespace": "open-p", "hash": 862206874, "terms": ["What is the purpose of fill() method?", "is the purpose of fill() method?", "the purpose of fill() method?", "purpose of fill() method?", "of fill() method?", "fill() method?", "method?"], "tokens": ["What", "is", "the", "purpose", "of", "fill", "()", "method", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-5010238", "fields": {"term": "What should federating searchers implement in multi-phase setup?", "namespace": "open-p", "hash": -5010238, "terms": ["What should federating searchers implement in multi-phase setup?", "should federating searchers implement in multi-phase setup?", "federating searchers implement in multi-phase setup?", "searchers implement in multi-phase setup?", "implement in multi-phase setup?", "in multi-phase setup?", "multi-phase setup?", "setup?"], "tokens": ["What", "should", "federating", "searchers", "implement", "in", "multi", "phase", "setup", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2073046079", "fields": {"term": "What should backend searchers implement to support multi-phase searching?", "namespace": "open-p", "hash": -2073046079, "terms": ["What should backend searchers implement to support multi-phase searching?", "should backend searchers implement to support multi-phase searching?", "backend searchers implement to support multi-phase searching?", "searchers implement to support multi-phase searching?", "implement to support multi-phase searching?", "to support multi-phase searching?", "support multi-phase searching?", "multi-phase searching?", "searching?"], "tokens": ["What", "should", "backend", "searchers", "implement", "to", "support", "multi", "phase", "searching", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1675508220", "fields": {"term": "How to signal an error?", "namespace": "open-p", "hash": -1675508220, "terms": ["How to signal an error?", "to signal an error?", "signal an error?", "an error?", "error?"], "tokens": ["How", "to", "signal", "an", "error", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1043502801", "fields": {"term": "How to add error to hits?", "namespace": "open-p", "hash": 1043502801, "terms": ["How to add error to hits?", "to add error to hits?", "add error to hits?", "error to hits?", "to hits?", "hits?"], "tokens": ["How", "to", "add", "error", "to", "hits", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1046708944", "fields": {"term": "What is WordItem?", "namespace": "open-p", "hash": -1046708944, "terms": ["What is WordItem?", "is WordItem?", "WordItem?"], "tokens": ["What", "is", "WordItem", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::265218935", "fields": {"term": "How can I keep linguistic settings?", "namespace": "open-p", "hash": 265218935, "terms": ["How can I keep linguistic settings?", "can I keep linguistic settings?", "I keep linguistic settings?", "keep linguistic settings?", "linguistic settings?", "settings?"], "tokens": ["How", "can", "I", "keep", "linguistic", "settings", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1856307307", "fields": {"term": "What is the purpose of isFromQuery?", "namespace": "open-p", "hash": -1856307307, "terms": ["What is the purpose of isFromQuery?", "is the purpose of isFromQuery?", "the purpose of isFromQuery?", "purpose of isFromQuery?", "of isFromQuery?", "isFromQuery?"], "tokens": ["What", "is", "the", "purpose", "of", "isFromQuery", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1180236767", "fields": {"term": "What field type does the guide use?", "namespace": "open-p", "hash": 1180236767, "terms": ["What field type does the guide use?", "field type does the guide use?", "type does the guide use?", "does the guide use?", "the guide use?", "guide use?", "use?"], "tokens": ["What", "field", "type", "does", "the", "guide", "use", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-113955869", "fields": {"term": "Can the map field type be ranked?", "namespace": "open-p", "hash": -113955869, "terms": ["Can the map field type be ranked?", "the map field type be ranked?", "map field type be ranked?", "field type be ranked?", "type be ranked?", "be ranked?", "ranked?"], "tokens": ["Can", "the", "map", "field", "type", "be", "ranked", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1829662334", "fields": {"term": "How to match a user query?", "namespace": "open-p", "hash": -1829662334, "terms": ["How to match a user query?", "to match a user query?", "match a user query?", "a user query?", "user query?", "query?"], "tokens": ["How", "to", "match", "a", "user", "query", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1566511388", "fields": {"term": "What text matching options exist?", "namespace": "open-p", "hash": -1566511388, "terms": ["What text matching options exist?", "text matching options exist?", "matching options exist?", "options exist?", "exist?"], "tokens": ["What", "text", "matching", "options", "exist", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::166260054", "fields": {"term": "What are the options for string fields?", "namespace": "open-p", "hash": 166260054, "terms": ["What are the options for string fields?", "are the options for string fields?", "the options for string fields?", "options for string fields?", "for string fields?", "string fields?", "fields?"], "tokens": ["What", "are", "the", "options", "for", "string", "fields", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-338309511", "fields": {"term": "What are fieldsets?", "namespace": "open-p", "hash": -338309511, "terms": ["What are fieldsets?", "are fieldsets?", "fieldsets?"], "tokens": ["What", "are", "fieldsets", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::942284118", "fields": {"term": "What are multi-valued string fields?", "namespace": "open-p", "hash": 942284118, "terms": ["What are multi-valued string fields?", "are multi-valued string fields?", "multi-valued string fields?", "string fields?", "fields?"], "tokens": ["What", "are", "multi", "valued", "string", "fields", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1641975130", "fields": {"term": "What is a weightedset field type?", "namespace": "open-p", "hash": -1641975130, "terms": ["What is a weightedset field type?", "is a weightedset field type?", "a weightedset field type?", "weightedset field type?", "field type?", "type?"], "tokens": ["What", "is", "a", "weightedset", "field", "type", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1683065644", "fields": {"term": "How can we search tags?", "namespace": "open-p", "hash": -1683065644, "terms": ["How can we search tags?", "can we search tags?", "we search tags?", "search tags?", "tags?"], "tokens": ["How", "can", "we", "search", "tags", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-226009816", "fields": {"term": "What is the match setting for string fields?", "namespace": "open-p", "hash": -226009816, "terms": ["What is the match setting for string fields?", "is the match setting for string fields?", "the match setting for string fields?", "match setting for string fields?", "setting for string fields?", "for string fields?", "string fields?", "fields?"], "tokens": ["What", "is", "the", "match", "setting", "for", "string", "fields", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::844947500", "fields": {"term": "What is the Vespa container image?", "namespace": "open-p", "hash": 844947500, "terms": ["What is the Vespa container image?", "is the Vespa container image?", "the Vespa container image?", "Vespa container image?", "container image?", "image?"], "tokens": ["What", "is", "the", "Vespa", "container", "image", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1768075626", "fields": {"term": "How to deploy Vespa using vespa-cli?", "namespace": "open-p", "hash": -1768075626, "terms": ["How to deploy Vespa using vespa-cli?", "to deploy Vespa using vespa-cli?", "deploy Vespa using vespa-cli?", "Vespa using vespa-cli?", "using vespa-cli?", "vespa-cli?"], "tokens": ["How", "to", "deploy", "Vespa", "using", "vespa", "cli", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1246673102", "fields": {"term": "What is Homebrew?", "namespace": "open-p", "hash": -1246673102, "terms": ["What is Homebrew?", "is Homebrew?", "Homebrew?"], "tokens": ["What", "is", "Homebrew", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1019687572", "fields": {"term": "What is query type all?", "namespace": "open-p", "hash": -1019687572, "terms": ["What is query type all?", "is query type all?", "query type all?", "type all?", "all?"], "tokens": ["What", "is", "query", "type", "all", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-33072111", "fields": {"term": "What is query type any?", "namespace": "open-p", "hash": -33072111, "terms": ["What is query type any?", "is query type any?", "query type any?", "type any?", "any?"], "tokens": ["What", "is", "query", "type", "any", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2123453858", "fields": {"term": "How does Vespa match set elements?", "namespace": "open-p", "hash": 2123453858, "terms": ["How does Vespa match set elements?", "does Vespa match set elements?", "Vespa match set elements?", "match set elements?", "set elements?", "elements?"], "tokens": ["How", "does", "Vespa", "match", "set", "elements", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1883574846", "fields": {"term": "What is the output of matchfeatures?", "namespace": "open-p", "hash": -1883574846, "terms": ["What is the output of matchfeatures?", "is the output of matchfeatures?", "the output of matchfeatures?", "output of matchfeatures?", "of matchfeatures?", "matchfeatures?"], "tokens": ["What", "is", "the", "output", "of", "matchfeatures", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-673984239", "fields": {"term": "What is the elementSimilarity feature?", "namespace": "open-p", "hash": -673984239, "terms": ["What is the elementSimilarity feature?", "is the elementSimilarity feature?", "the elementSimilarity feature?", "elementSimilarity feature?", "feature?"], "tokens": ["What", "is", "the", "elementSimilarity", "feature", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::158590953", "fields": {"term": "What is the bm25 feature?", "namespace": "open-p", "hash": 158590953, "terms": ["What is the bm25 feature?", "is the bm25 feature?", "the bm25 feature?", "bm25 feature?", "feature?"], "tokens": ["What", "is", "the", "bm25", "feature", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::357042377", "fields": {"term": "How does elementCompleteness work?", "namespace": "open-p", "hash": 357042377, "terms": ["How does elementCompleteness work?", "does elementCompleteness work?", "elementCompleteness work?", "work?"], "tokens": ["How", "does", "elementCompleteness", "work", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1656779105", "fields": {"term": "What are the examples of features for indexed multivalue string fields?", "namespace": "open-p", "hash": -1656779105, "terms": ["What are the examples of features for indexed multivalue string fields?", "are the examples of features for indexed multivalue string fields?", "the examples of features for indexed multivalue string fields?", "examples of features for indexed multivalue string fields?", "of features for indexed multivalue string fields?", "features for indexed multivalue string fields?", "for indexed multivalue string fields?", "indexed multivalue string fields?", "multivalue string fields?", "string fields?", "fields?"], "tokens": ["What", "are", "the", "examples", "of", "features", "for", "indexed", "multivalue", "string", "fields", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2052561912", "fields": {"term": "What is the query retrieval strategy?", "namespace": "open-p", "hash": -2052561912, "terms": ["What is the query retrieval strategy?", "is the query retrieval strategy?", "the query retrieval strategy?", "query retrieval strategy?", "retrieval strategy?", "strategy?"], "tokens": ["What", "is", "the", "query", "retrieval", "strategy", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1276901287", "fields": {"term": "How to change ranking of multi-valued fields?", "namespace": "open-p", "hash": -1276901287, "terms": ["How to change ranking of multi-valued fields?", "to change ranking of multi-valued fields?", "change ranking of multi-valued fields?", "ranking of multi-valued fields?", "of multi-valued fields?", "multi-valued fields?", "fields?"], "tokens": ["How", "to", "change", "ranking", "of", "multi", "valued", "fields", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1591351417", "fields": {"term": "What are the security requirements for Vespa?", "namespace": "open-p", "hash": 1591351417, "terms": ["What are the security requirements for Vespa?", "are the security requirements for Vespa?", "the security requirements for Vespa?", "security requirements for Vespa?", "requirements for Vespa?", "for Vespa?", "Vespa?"], "tokens": ["What", "are", "the", "security", "requirements", "for", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::424447567", "fields": {"term": "How to secure Vespa with TLS?", "namespace": "open-p", "hash": 424447567, "terms": ["How to secure Vespa with TLS?", "to secure Vespa with TLS?", "secure Vespa with TLS?", "Vespa with TLS?", "with TLS?", "TLS?"], "tokens": ["How", "to", "secure", "Vespa", "with", "TLS", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1655068751", "fields": {"term": "Why is it important to isolate Vespa hosts?", "namespace": "open-p", "hash": 1655068751, "terms": ["Why is it important to isolate Vespa hosts?", "is it important to isolate Vespa hosts?", "it important to isolate Vespa hosts?", "important to isolate Vespa hosts?", "to isolate Vespa hosts?", "isolate Vespa hosts?", "Vespa hosts?", "hosts?"], "tokens": ["Why", "is", "it", "important", "to", "isolate", "Vespa", "hosts", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1232477639", "fields": {"term": "How to lockdown directory permissions for Vespa?", "namespace": "open-p", "hash": -1232477639, "terms": ["How to lockdown directory permissions for Vespa?", "to lockdown directory permissions for Vespa?", "lockdown directory permissions for Vespa?", "directory permissions for Vespa?", "permissions for Vespa?", "for Vespa?", "Vespa?"], "tokens": ["How", "to", "lockdown", "directory", "permissions", "for", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1682199635", "fields": {"term": "What is the risk of exposing Vespa?", "namespace": "open-p", "hash": 1682199635, "terms": ["What is the risk of exposing Vespa?", "is the risk of exposing Vespa?", "the risk of exposing Vespa?", "risk of exposing Vespa?", "of exposing Vespa?", "exposing Vespa?", "Vespa?"], "tokens": ["What", "is", "the", "risk", "of", "exposing", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::727196391", "fields": {"term": "How to secure Vespa?", "namespace": "open-p", "hash": 727196391, "terms": ["How to secure Vespa?", "to secure Vespa?", "secure Vespa?", "Vespa?"], "tokens": ["How", "to", "secure", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-207827721", "fields": {"term": "What protocol should be used to secure inter-node communication?", "namespace": "open-p", "hash": -207827721, "terms": ["What protocol should be used to secure inter-node communication?", "protocol should be used to secure inter-node communication?", "should be used to secure inter-node communication?", "be used to secure inter-node communication?", "used to secure inter-node communication?", "to secure inter-node communication?", "secure inter-node communication?", "inter-node communication?", "communication?"], "tokens": ["What", "protocol", "should", "be", "used", "to", "secure", "inter", "node", "communication", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1294255186", "fields": {"term": "What are the consequences of not securing Vespa?", "namespace": "open-p", "hash": 1294255186, "terms": ["What are the consequences of not securing Vespa?", "are the consequences of not securing Vespa?", "the consequences of not securing Vespa?", "consequences of not securing Vespa?", "of not securing Vespa?", "not securing Vespa?", "securing Vespa?", "Vespa?"], "tokens": ["What", "are", "the", "consequences", "of", "not", "securing", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1107284930", "fields": {"term": "How to limit Vespa hosts connections?", "namespace": "open-p", "hash": 1107284930, "terms": ["How to limit Vespa hosts connections?", "to limit Vespa hosts connections?", "limit Vespa hosts connections?", "Vespa hosts connections?", "hosts connections?", "connections?"], "tokens": ["How", "to", "limit", "Vespa", "hosts", "connections", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1812793147", "fields": {"term": "What technologies can be used to secure Vespa?", "namespace": "open-p", "hash": 1812793147, "terms": ["What technologies can be used to secure Vespa?", "technologies can be used to secure Vespa?", "can be used to secure Vespa?", "be used to secure Vespa?", "used to secure Vespa?", "to secure Vespa?", "secure Vespa?", "Vespa?"], "tokens": ["What", "technologies", "can", "be", "used", "to", "secure", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1414933322", "fields": {"term": "How do you protect HTTP container?", "namespace": "open-p", "hash": 1414933322, "terms": ["How do you protect HTTP container?", "do you protect HTTP container?", "you protect HTTP container?", "protect HTTP container?", "HTTP container?", "container?"], "tokens": ["How", "do", "you", "protect", "HTTP", "container", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1704676090", "fields": {"term": "When should you restrict container port?", "namespace": "open-p", "hash": -1704676090, "terms": ["When should you restrict container port?", "should you restrict container port?", "you restrict container port?", "restrict container port?", "container port?", "port?"], "tokens": ["When", "should", "you", "restrict", "container", "port", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1184612029", "fields": {"term": "Under which user do Vespa processes run?", "namespace": "open-p", "hash": 1184612029, "terms": ["Under which user do Vespa processes run?", "which user do Vespa processes run?", "user do Vespa processes run?", "do Vespa processes run?", "Vespa processes run?", "processes run?", "run?"], "tokens": ["Under", "which", "user", "do", "Vespa", "processes", "run", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2132388250", "fields": {"term": "What is stored under $VESPA_HOME?", "namespace": "open-p", "hash": -2132388250, "terms": ["What is stored under $VESPA_HOME?", "is stored under $VESPA_HOME?", "stored under $VESPA_HOME?", "under $VESPA_HOME?", "$VESPA_HOME?"], "tokens": ["What", "is", "stored", "under", "$", "VESPA_HOME", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1123083180", "fields": {"term": "What is the recommended security practice for sensitive data stored in application?", "namespace": "open-p", "hash": 1123083180, "terms": ["What is the recommended security practice for sensitive data stored in application?", "is the recommended security practice for sensitive data stored in application?", "the recommended security practice for sensitive data stored in application?", "recommended security practice for sensitive data stored in application?", "security practice for sensitive data stored in application?", "practice for sensitive data stored in application?", "for sensitive data stored in application?", "sensitive data stored in application?", "data stored in application?", "stored in application?", "in application?", "application?"], "tokens": ["What", "is", "the", "recommended", "security", "practice", "for", "sensitive", "data", "stored", "in", "application", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2037126391", "fields": {"term": "How should private keys be protected in Vespa?", "namespace": "open-p", "hash": -2037126391, "terms": ["How should private keys be protected in Vespa?", "should private keys be protected in Vespa?", "private keys be protected in Vespa?", "keys be protected in Vespa?", "be protected in Vespa?", "protected in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["How", "should", "private", "keys", "be", "protected", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::860853901", "fields": {"term": "Does Vespa have application-level support for encryption?", "namespace": "open-p", "hash": 860853901, "terms": ["Does Vespa have application-level support for encryption?", "Vespa have application-level support for encryption?", "have application-level support for encryption?", "application-level support for encryption?", "support for encryption?", "for encryption?", "encryption?"], "tokens": ["Does", "Vespa", "have", "application", "level", "support", "for", "encryption", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1127731013", "fields": {"term": "How can encryption of on-disk document stores be implemented in Vespa?", "namespace": "open-p", "hash": 1127731013, "terms": ["How can encryption of on-disk document stores be implemented in Vespa?", "can encryption of on-disk document stores be implemented in Vespa?", "encryption of on-disk document stores be implemented in Vespa?", "of on-disk document stores be implemented in Vespa?", "on-disk document stores be implemented in Vespa?", "document stores be implemented in Vespa?", "stores be implemented in Vespa?", "be implemented in Vespa?", "implemented in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["How", "can", "encryption", "of", "on", "disk", "document", "stores", "be", "implemented", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-774945053", "fields": {"term": "How is encryption typically implemented in Vespa?", "namespace": "open-p", "hash": -774945053, "terms": ["How is encryption typically implemented in Vespa?", "is encryption typically implemented in Vespa?", "encryption typically implemented in Vespa?", "typically implemented in Vespa?", "implemented in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["How", "is", "encryption", "typically", "implemented", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1030527801", "fields": {"term": "How can I protect endpoints with mTLS?", "namespace": "open-p", "hash": 1030527801, "terms": ["How can I protect endpoints with mTLS?", "can I protect endpoints with mTLS?", "I protect endpoints with mTLS?", "protect endpoints with mTLS?", "endpoints with mTLS?", "with mTLS?", "mTLS?"], "tokens": ["How", "can", "I", "protect", "endpoints", "with", "mTLS", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1780986398", "fields": {"term": "Where can I find documentation for mTLS?", "namespace": "open-p", "hash": -1780986398, "terms": ["Where can I find documentation for mTLS?", "can I find documentation for mTLS?", "I find documentation for mTLS?", "find documentation for mTLS?", "documentation for mTLS?", "for mTLS?", "mTLS?"], "tokens": ["Where", "can", "I", "find", "documentation", "for", "mTLS", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::446220658", "fields": {"term": "Is mTLS mutually authenticated?", "namespace": "open-p", "hash": 446220658, "terms": ["Is mTLS mutually authenticated?", "mTLS mutually authenticated?", "mutually authenticated?", "authenticated?"], "tokens": ["Is", "mTLS", "mutually", "authenticated", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1907932923", "fields": {"term": "What is the multinode-HA example?", "namespace": "open-p", "hash": 1907932923, "terms": ["What is the multinode-HA example?", "is the multinode-HA example?", "the multinode-HA example?", "multinode-HA example?", "example?"], "tokens": ["What", "is", "the", "multinode", "HA", "example", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1579418119", "fields": {"term": "How do I get started with mTLS?", "namespace": "open-p", "hash": 1579418119, "terms": ["How do I get started with mTLS?", "do I get started with mTLS?", "I get started with mTLS?", "get started with mTLS?", "started with mTLS?", "with mTLS?", "mTLS?"], "tokens": ["How", "do", "I", "get", "started", "with", "mTLS", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-177135578", "fields": {"term": "What protocols does mTLS protect?", "namespace": "open-p", "hash": -177135578, "terms": ["What protocols does mTLS protect?", "protocols does mTLS protect?", "does mTLS protect?", "mTLS protect?", "protect?"], "tokens": ["What", "protocols", "does", "mTLS", "protect", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::281293321", "fields": {"term": "What is the SELECT parameter?", "namespace": "open-p", "hash": 281293321, "terms": ["What is the SELECT parameter?", "is the SELECT parameter?", "the SELECT parameter?", "SELECT parameter?", "parameter?"], "tokens": ["What", "is", "the", "SELECT", "parameter", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-566932552", "fields": {"term": "How is SELECT parameter used?", "namespace": "open-p", "hash": -566932552, "terms": ["How is SELECT parameter used?", "is SELECT parameter used?", "SELECT parameter used?", "parameter used?", "used?"], "tokens": ["How", "is", "SELECT", "parameter", "used", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1366633274", "fields": {"term": "Can SELECT and QUERY be used together?", "namespace": "open-p", "hash": 1366633274, "terms": ["Can SELECT and QUERY be used together?", "SELECT and QUERY be used together?", "and QUERY be used together?", "QUERY be used together?", "be used together?", "used together?", "together?"], "tokens": ["Can", "SELECT", "and", "QUERY", "be", "used", "together", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1426608660", "fields": {"term": "What is YQL equivalent to SELECT?", "namespace": "open-p", "hash": -1426608660, "terms": ["What is YQL equivalent to SELECT?", "is YQL equivalent to SELECT?", "YQL equivalent to SELECT?", "equivalent to SELECT?", "to SELECT?", "SELECT?"], "tokens": ["What", "is", "YQL", "equivalent", "to", "SELECT", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1089998141", "fields": {"term": "What is the syntax for where queries?", "namespace": "open-p", "hash": -1089998141, "terms": ["What is the syntax for where queries?", "is the syntax for where queries?", "the syntax for where queries?", "syntax for where queries?", "for where queries?", "where queries?", "queries?"], "tokens": ["What", "is", "the", "syntax", "for", "where", "queries", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-115955504", "fields": {"term": "What is the difference with YQL?", "namespace": "open-p", "hash": -115955504, "terms": ["What is the difference with YQL?", "is the difference with YQL?", "the difference with YQL?", "difference with YQL?", "with YQL?", "YQL?"], "tokens": ["What", "is", "the", "difference", "with", "YQL", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1595535800", "fields": {"term": "How do you write equivalent queries in JSON?", "namespace": "open-p", "hash": 1595535800, "terms": ["How do you write equivalent queries in JSON?", "do you write equivalent queries in JSON?", "you write equivalent queries in JSON?", "write equivalent queries in JSON?", "equivalent queries in JSON?", "queries in JSON?", "in JSON?", "JSON?"], "tokens": ["How", "do", "you", "write", "equivalent", "queries", "in", "JSON", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::226562250", "fields": {"term": "What is the query-tree?", "namespace": "open-p", "hash": 226562250, "terms": ["What is the query-tree?", "is the query-tree?", "the query-tree?", "query-tree?"], "tokens": ["What", "is", "the", "query", "tree", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-166759645", "fields": {"term": "How is the query-tree built?", "namespace": "open-p", "hash": -166759645, "terms": ["How is the query-tree built?", "is the query-tree built?", "the query-tree built?", "query-tree built?", "built?"], "tokens": ["How", "is", "the", "query", "tree", "built", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::302756856", "fields": {"term": "What is the Vespa query-tree?", "namespace": "open-p", "hash": 302756856, "terms": ["What is the Vespa query-tree?", "is the Vespa query-tree?", "the Vespa query-tree?", "Vespa query-tree?", "query-tree?"], "tokens": ["What", "is", "the", "Vespa", "query", "tree", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-374762368", "fields": {"term": "How is YQL parsed in Vespa?", "namespace": "open-p", "hash": -374762368, "terms": ["How is YQL parsed in Vespa?", "is YQL parsed in Vespa?", "YQL parsed in Vespa?", "parsed in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["How", "is", "YQL", "parsed", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2021024935", "fields": {"term": "What is a grouping statement?", "namespace": "open-p", "hash": -2021024935, "terms": ["What is a grouping statement?", "is a grouping statement?", "a grouping statement?", "grouping statement?", "statement?"], "tokens": ["What", "is", "a", "grouping", "statement", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::424174319", "fields": {"term": "How is grouping represented as JSON?", "namespace": "open-p", "hash": 424174319, "terms": ["How is grouping represented as JSON?", "is grouping represented as JSON?", "grouping represented as JSON?", "represented as JSON?", "as JSON?", "JSON?"], "tokens": ["How", "is", "grouping", "represented", "as", "JSON", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2044076687", "fields": {"term": "What is the structure of a JSON grouping statement?", "namespace": "open-p", "hash": -2044076687, "terms": ["What is the structure of a JSON grouping statement?", "is the structure of a JSON grouping statement?", "the structure of a JSON grouping statement?", "structure of a JSON grouping statement?", "of a JSON grouping statement?", "a JSON grouping statement?", "JSON grouping statement?", "grouping statement?", "statement?"], "tokens": ["What", "is", "the", "structure", "of", "a", "JSON", "grouping", "statement", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-708696236", "fields": {"term": "What is the format of a JSON grouping function?", "namespace": "open-p", "hash": -708696236, "terms": ["What is the format of a JSON grouping function?", "is the format of a JSON grouping function?", "the format of a JSON grouping function?", "format of a JSON grouping function?", "of a JSON grouping function?", "a JSON grouping function?", "JSON grouping function?", "grouping function?", "function?"], "tokens": ["What", "is", "the", "format", "of", "a", "JSON", "grouping", "function", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1344864185", "fields": {"term": "What are the types of arguments that can be used in grouping?", "namespace": "open-p", "hash": 1344864185, "terms": ["What are the types of arguments that can be used in grouping?", "are the types of arguments that can be used in grouping?", "the types of arguments that can be used in grouping?", "types of arguments that can be used in grouping?", "of arguments that can be used in grouping?", "arguments that can be used in grouping?", "that can be used in grouping?", "can be used in grouping?", "be used in grouping?", "used in grouping?", "in grouping?", "grouping?"], "tokens": ["What", "are", "the", "types", "of", "arguments", "that", "can", "be", "used", "in", "grouping", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1928072599", "fields": {"term": "How is bucketing used in a JSON grouping statement?", "namespace": "open-p", "hash": -1928072599, "terms": ["How is bucketing used in a JSON grouping statement?", "is bucketing used in a JSON grouping statement?", "bucketing used in a JSON grouping statement?", "used in a JSON grouping statement?", "in a JSON grouping statement?", "a JSON grouping statement?", "JSON grouping statement?", "grouping statement?", "statement?"], "tokens": ["How", "is", "bucketing", "used", "in", "a", "JSON", "grouping", "statement", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1160896326", "fields": {"term": "What is the purpose of the 'all' keyword in a JSON grouping statement?", "namespace": "open-p", "hash": -1160896326, "terms": ["What is the purpose of the 'all' keyword in a JSON grouping statement?", "is the purpose of the 'all' keyword in a JSON grouping statement?", "the purpose of the 'all' keyword in a JSON grouping statement?", "purpose of the 'all' keyword in a JSON grouping statement?", "of the 'all' keyword in a JSON grouping statement?", "the 'all' keyword in a JSON grouping statement?", "'all' keyword in a JSON grouping statement?", "keyword in a JSON grouping statement?", "in a JSON grouping statement?", "a JSON grouping statement?", "JSON grouping statement?", "grouping statement?", "statement?"], "tokens": ["What", "is", "the", "purpose", "of", "the", "'", "all", "'", "keyword", "in", "a", "JSON", "grouping", "statement", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1808295686", "fields": {"term": "What are the parameters accepted by the range query?", "namespace": "open-p", "hash": 1808295686, "terms": ["What are the parameters accepted by the range query?", "are the parameters accepted by the range query?", "the parameters accepted by the range query?", "parameters accepted by the range query?", "accepted by the range query?", "by the range query?", "the range query?", "range query?", "query?"], "tokens": ["What", "are", "the", "parameters", "accepted", "by", "the", "range", "query", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1548718464", "fields": {"term": "How is the range query formatted in JSON?", "namespace": "open-p", "hash": -1548718464, "terms": ["How is the range query formatted in JSON?", "is the range query formatted in JSON?", "the range query formatted in JSON?", "range query formatted in JSON?", "query formatted in JSON?", "formatted in JSON?", "in JSON?", "JSON?"], "tokens": ["How", "is", "the", "range", "query", "formatted", "in", "JSON", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::95919645", "fields": {"term": "What does the greater-than or equal to (\u2265) operator do?", "namespace": "open-p", "hash": 95919645, "terms": ["What does the greater-than or equal to (\u2265) operator do?", "does the greater-than or equal to (\u2265) operator do?", "the greater-than or equal to (\u2265) operator do?", "greater-than or equal to (\u2265) operator do?", "or equal to (\u2265) operator do?", "equal to (\u2265) operator do?", "to (\u2265) operator do?", "(\u2265) operator do?", "operator do?", "do?"], "tokens": ["What", "does", "the", "greater", "than", "or", "equal", "to", "(\u2265)", "operator", "do", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2097474831", "fields": {"term": "What does the greater-than (>) operator do?", "namespace": "open-p", "hash": -2097474831, "terms": ["What does the greater-than (>) operator do?", "does the greater-than (>) operator do?", "the greater-than (>) operator do?", "greater-than (>) operator do?", "(>) operator do?", "operator do?", "do?"], "tokens": ["What", "does", "the", "greater", "than", "(>)", "operator", "do", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1442214941", "fields": {"term": "What does the less-than (<) operator do?", "namespace": "open-p", "hash": 1442214941, "terms": ["What does the less-than (<) operator do?", "does the less-than (<) operator do?", "the less-than (<) operator do?", "less-than (<) operator do?", "(<) operator do?", "operator do?", "do?"], "tokens": ["What", "does", "the", "less", "than", "(<)", "operator", "do", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1622892178", "fields": {"term": "What does the less-than or equal to (\u2264) operator do?", "namespace": "open-p", "hash": -1622892178, "terms": ["What does the less-than or equal to (\u2264) operator do?", "does the less-than or equal to (\u2264) operator do?", "the less-than or equal to (\u2264) operator do?", "less-than or equal to (\u2264) operator do?", "or equal to (\u2264) operator do?", "equal to (\u2264) operator do?", "to (\u2264) operator do?", "(\u2264) operator do?", "operator do?", "do?"], "tokens": ["What", "does", "the", "less", "than", "or", "equal", "to", "(\u2264)", "operator", "do", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-304475262", "fields": {"term": "What is 'and_not' in YQL?", "namespace": "open-p", "hash": -304475262, "terms": ["What is 'and_not' in YQL?", "is 'and_not' in YQL?", "'and_not' in YQL?", "in YQL?", "YQL?"], "tokens": ["What", "is", "'", "and_not", "'", "in", "YQL", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2081373035", "fields": {"term": "What does 'title contains' do in YQL?", "namespace": "open-p", "hash": -2081373035, "terms": ["What does 'title contains' do in YQL?", "does 'title contains' do in YQL?", "'title contains' do in YQL?", "contains' do in YQL?", "do in YQL?", "in YQL?", "YQL?"], "tokens": ["What", "does", "'", "title", "contains", "'", "do", "in", "YQL", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1823906963", "fields": {"term": "What is the formal structure of YQL?", "namespace": "open-p", "hash": 1823906963, "terms": ["What is the formal structure of YQL?", "is the formal structure of YQL?", "the formal structure of YQL?", "formal structure of YQL?", "structure of YQL?", "of YQL?", "YQL?"], "tokens": ["What", "is", "the", "formal", "structure", "of", "YQL", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::539467495", "fields": {"term": "How is YQL structured?", "namespace": "open-p", "hash": 539467495, "terms": ["How is YQL structured?", "is YQL structured?", "YQL structured?", "structured?"], "tokens": ["How", "is", "YQL", "structured", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-719607363", "fields": {"term": "What does 'phrase' mean in YQL?", "namespace": "open-p", "hash": -719607363, "terms": ["What does 'phrase' mean in YQL?", "does 'phrase' mean in YQL?", "'phrase' mean in YQL?", "mean in YQL?", "in YQL?", "YQL?"], "tokens": ["What", "does", "'", "phrase", "'", "mean", "in", "YQL", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::738729376", "fields": {"term": "What is 'onear'?", "namespace": "open-p", "hash": 738729376, "terms": ["What is 'onear'?", "is 'onear'?", "'onear'?"], "tokens": ["What", "is", "'", "onear", "'?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-98771452", "fields": {"term": "What is sameElement?", "namespace": "open-p", "hash": -98771452, "terms": ["What is sameElement?", "is sameElement?", "sameElement?"], "tokens": ["What", "is", "sameElement", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-470052492", "fields": {"term": "What is equiv() in YQL?", "namespace": "open-p", "hash": -470052492, "terms": ["What is equiv() in YQL?", "is equiv() in YQL?", "equiv() in YQL?", "in YQL?", "YQL?"], "tokens": ["What", "is", "equiv", "()", "in", "YQL", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2129514088", "fields": {"term": "What is fieldName in YQL?", "namespace": "open-p", "hash": -2129514088, "terms": ["What is fieldName in YQL?", "is fieldName in YQL?", "fieldName in YQL?", "in YQL?", "YQL?"], "tokens": ["What", "is", "fieldName", "in", "YQL", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::386312485", "fields": {"term": "What is a weightedSet?", "namespace": "open-p", "hash": 386312485, "terms": ["What is a weightedSet?", "is a weightedSet?", "a weightedSet?", "weightedSet?"], "tokens": ["What", "is", "a", "weightedSet", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::125829491", "fields": {"term": "What is the format for weightedSet in YQL?", "namespace": "open-p", "hash": 125829491, "terms": ["What is the format for weightedSet in YQL?", "is the format for weightedSet in YQL?", "the format for weightedSet in YQL?", "format for weightedSet in YQL?", "for weightedSet in YQL?", "weightedSet in YQL?", "in YQL?", "YQL?"], "tokens": ["What", "is", "the", "format", "for", "weightedSet", "in", "YQL", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::27181273", "fields": {"term": "What is the meaning of {'a':1, 'b':2} in weightedSet?", "namespace": "open-p", "hash": 27181273, "terms": ["What is the meaning of {'a':1, 'b':2} in weightedSet?", "is the meaning of {'a':1, 'b':2} in weightedSet?", "the meaning of {'a':1, 'b':2} in weightedSet?", "meaning of {'a':1, 'b':2} in weightedSet?", "of {'a':1, 'b':2} in weightedSet?", "{'a':1, 'b':2} in weightedSet?", "'b':2} in weightedSet?", "in weightedSet?", "weightedSet?"], "tokens": ["What", "is", "the", "meaning", "of", "{'", "a", "':", "1", "'", "b", "':", "2", "}", "in", "weightedSet", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-580499993", "fields": {"term": "How does YQL handle weightedSet queries?", "namespace": "open-p", "hash": -580499993, "terms": ["How does YQL handle weightedSet queries?", "does YQL handle weightedSet queries?", "YQL handle weightedSet queries?", "handle weightedSet queries?", "weightedSet queries?", "queries?"], "tokens": ["How", "does", "YQL", "handle", "weightedSet", "queries", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1483625652", "fields": {"term": "What is weakAnd?", "namespace": "open-p", "hash": -1483625652, "terms": ["What is weakAnd?", "is weakAnd?", "weakAnd?"], "tokens": ["What", "is", "weakAnd", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1866681915", "fields": {"term": "What does 'scoreThreshold' mean?", "namespace": "open-p", "hash": 1866681915, "terms": ["What does 'scoreThreshold' mean?", "does 'scoreThreshold' mean?", "'scoreThreshold' mean?", "mean?"], "tokens": ["What", "does", "'", "scoreThreshold", "'", "mean", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::38427560", "fields": {"term": "What is the purpose of 'targetHits'?", "namespace": "open-p", "hash": 38427560, "terms": ["What is the purpose of 'targetHits'?", "is the purpose of 'targetHits'?", "the purpose of 'targetHits'?", "purpose of 'targetHits'?", "of 'targetHits'?", "'targetHits'?"], "tokens": ["What", "is", "the", "purpose", "of", "'", "targetHits", "'?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2106957723", "fields": {"term": "What is the syntax for querying gender in YQL?", "namespace": "open-p", "hash": -2106957723, "terms": ["What is the syntax for querying gender in YQL?", "is the syntax for querying gender in YQL?", "the syntax for querying gender in YQL?", "syntax for querying gender in YQL?", "for querying gender in YQL?", "querying gender in YQL?", "gender in YQL?", "in YQL?", "YQL?"], "tokens": ["What", "is", "the", "syntax", "for", "querying", "gender", "in", "YQL", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-847114937", "fields": {"term": "What is the semantic-qa-retrieval sample application?", "namespace": "open-p", "hash": -847114937, "terms": ["What is the semantic-qa-retrieval sample application?", "is the semantic-qa-retrieval sample application?", "the semantic-qa-retrieval sample application?", "semantic-qa-retrieval sample application?", "sample application?", "application?"], "tokens": ["What", "is", "the", "semantic", "qa", "retrieval", "sample", "application", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-892156518", "fields": {"term": "What is the advantage of Vespa's hybrid retrieval?", "namespace": "open-p", "hash": -892156518, "terms": ["What is the advantage of Vespa's hybrid retrieval?", "is the advantage of Vespa's hybrid retrieval?", "the advantage of Vespa's hybrid retrieval?", "advantage of Vespa's hybrid retrieval?", "of Vespa's hybrid retrieval?", "Vespa's hybrid retrieval?", "hybrid retrieval?", "retrieval?"], "tokens": ["What", "is", "the", "advantage", "of", "Vespa", "'", "s", "hybrid", "retrieval", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1943068350", "fields": {"term": "What is the Universal Sentence Encoder?", "namespace": "open-p", "hash": -1943068350, "terms": ["What is the Universal Sentence Encoder?", "is the Universal Sentence Encoder?", "the Universal Sentence Encoder?", "Universal Sentence Encoder?", "Sentence Encoder?", "Encoder?"], "tokens": ["What", "is", "the", "Universal", "Sentence", "Encoder", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::657089465", "fields": {"term": "What tasks can the Universal Sentence Encoder be used for?", "namespace": "open-p", "hash": 657089465, "terms": ["What tasks can the Universal Sentence Encoder be used for?", "tasks can the Universal Sentence Encoder be used for?", "can the Universal Sentence Encoder be used for?", "the Universal Sentence Encoder be used for?", "Universal Sentence Encoder be used for?", "Sentence Encoder be used for?", "Encoder be used for?", "be used for?", "used for?", "for?"], "tokens": ["What", "tasks", "can", "the", "Universal", "Sentence", "Encoder", "be", "used", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1026738267", "fields": {"term": "How does the Universal Sentence Encoder work?", "namespace": "open-p", "hash": -1026738267, "terms": ["How does the Universal Sentence Encoder work?", "does the Universal Sentence Encoder work?", "the Universal Sentence Encoder work?", "Universal Sentence Encoder work?", "Sentence Encoder work?", "Encoder work?", "work?"], "tokens": ["How", "does", "the", "Universal", "Sentence", "Encoder", "work", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::664121612", "fields": {"term": "What is the Multilingual Universal Sentence Encoder for Question-Answer Retrieval?", "namespace": "open-p", "hash": 664121612, "terms": ["What is the Multilingual Universal Sentence Encoder for Question-Answer Retrieval?", "is the Multilingual Universal Sentence Encoder for Question-Answer Retrieval?", "the Multilingual Universal Sentence Encoder for Question-Answer Retrieval?", "Multilingual Universal Sentence Encoder for Question-Answer Retrieval?", "Universal Sentence Encoder for Question-Answer Retrieval?", "Sentence Encoder for Question-Answer Retrieval?", "Encoder for Question-Answer Retrieval?", "for Question-Answer Retrieval?", "Question-Answer Retrieval?", "Retrieval?"], "tokens": ["What", "is", "the", "Multilingual", "Universal", "Sentence", "Encoder", "for", "Question", "Answer", "Retrieval", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1443872259", "fields": {"term": "How can Vespa be used with the Universal Sentence Encoder?", "namespace": "open-p", "hash": 1443872259, "terms": ["How can Vespa be used with the Universal Sentence Encoder?", "can Vespa be used with the Universal Sentence Encoder?", "Vespa be used with the Universal Sentence Encoder?", "be used with the Universal Sentence Encoder?", "used with the Universal Sentence Encoder?", "with the Universal Sentence Encoder?", "the Universal Sentence Encoder?", "Universal Sentence Encoder?", "Sentence Encoder?", "Encoder?"], "tokens": ["How", "can", "Vespa", "be", "used", "with", "the", "Universal", "Sentence", "Encoder", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1488362337", "fields": {"term": "What is a dense tensor representation?", "namespace": "open-p", "hash": 1488362337, "terms": ["What is a dense tensor representation?", "is a dense tensor representation?", "a dense tensor representation?", "dense tensor representation?", "tensor representation?", "representation?"], "tokens": ["What", "is", "a", "dense", "tensor", "representation", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2002586131", "fields": {"term": "What is dense Passage Retrieval?", "namespace": "open-p", "hash": 2002586131, "terms": ["What is dense Passage Retrieval?", "is dense Passage Retrieval?", "dense Passage Retrieval?", "Passage Retrieval?", "Retrieval?"], "tokens": ["What", "is", "dense", "Passage", "Retrieval", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::409483575", "fields": {"term": "What is SQuAD dataset?", "namespace": "open-p", "hash": 409483575, "terms": ["What is SQuAD dataset?", "is SQuAD dataset?", "SQuAD dataset?", "dataset?"], "tokens": ["What", "is", "SQuAD", "dataset", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1953026675", "fields": {"term": "What is SQuAD-explorer?", "namespace": "open-p", "hash": 1953026675, "terms": ["What is SQuAD-explorer?", "is SQuAD-explorer?", "SQuAD-explorer?"], "tokens": ["What", "is", "SQuAD", "explorer", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1663735603", "fields": {"term": "What is Stanford Question Answering Dataset?", "namespace": "open-p", "hash": -1663735603, "terms": ["What is Stanford Question Answering Dataset?", "is Stanford Question Answering Dataset?", "Stanford Question Answering Dataset?", "Question Answering Dataset?", "Answering Dataset?", "Dataset?"], "tokens": ["What", "is", "Stanford", "Question", "Answering", "Dataset", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-119076348", "fields": {"term": "What is the purpose of SQuAD?", "namespace": "open-p", "hash": -119076348, "terms": ["What is the purpose of SQuAD?", "is the purpose of SQuAD?", "the purpose of SQuAD?", "purpose of SQuAD?", "of SQuAD?", "SQuAD?"], "tokens": ["What", "is", "the", "purpose", "of", "SQuAD", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1170123616", "fields": {"term": "What is the format of SQuAD?", "namespace": "open-p", "hash": 1170123616, "terms": ["What is the format of SQuAD?", "is the format of SQuAD?", "the format of SQuAD?", "format of SQuAD?", "of SQuAD?", "SQuAD?"], "tokens": ["What", "is", "the", "format", "of", "SQuAD", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1322393782", "fields": {"term": "How many questions are in SQuAD v1.1?", "namespace": "open-p", "hash": -1322393782, "terms": ["How many questions are in SQuAD v1.1?", "many questions are in SQuAD v1.1?", "questions are in SQuAD v1.1?", "are in SQuAD v1.1?", "in SQuAD v1.1?", "SQuAD v1.1?", "v1.1?"], "tokens": ["How", "many", "questions", "are", "in", "SQuAD", "v1", "1", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1936417119", "fields": {"term": "How many paragraphs are in SQuAD v1.1?", "namespace": "open-p", "hash": 1936417119, "terms": ["How many paragraphs are in SQuAD v1.1?", "many paragraphs are in SQuAD v1.1?", "paragraphs are in SQuAD v1.1?", "are in SQuAD v1.1?", "in SQuAD v1.1?", "SQuAD v1.1?", "v1.1?"], "tokens": ["How", "many", "paragraphs", "are", "in", "SQuAD", "v1", "1", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1634723168", "fields": {"term": "How many sentences can SQuAD's paragraphs be segmented into?", "namespace": "open-p", "hash": -1634723168, "terms": ["How many sentences can SQuAD's paragraphs be segmented into?", "many sentences can SQuAD's paragraphs be segmented into?", "sentences can SQuAD's paragraphs be segmented into?", "can SQuAD's paragraphs be segmented into?", "SQuAD's paragraphs be segmented into?", "paragraphs be segmented into?", "be segmented into?", "segmented into?", "into?"], "tokens": ["How", "many", "sentences", "can", "SQuAD", "'", "s", "paragraphs", "be", "segmented", "into", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1086980587", "fields": {"term": "What is the SQuaD dataset?", "namespace": "open-p", "hash": 1086980587, "terms": ["What is the SQuaD dataset?", "is the SQuaD dataset?", "the SQuaD dataset?", "SQuaD dataset?", "dataset?"], "tokens": ["What", "is", "the", "SQuaD", "dataset", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::982937020", "fields": {"term": "What fields does the context document type have?", "namespace": "open-p", "hash": 982937020, "terms": ["What fields does the context document type have?", "fields does the context document type have?", "does the context document type have?", "the context document type have?", "context document type have?", "document type have?", "type have?", "have?"], "tokens": ["What", "fields", "does", "the", "context", "document", "type", "have", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::592415536", "fields": {"term": "What is the sentence document type?", "namespace": "open-p", "hash": 592415536, "terms": ["What is the sentence document type?", "is the sentence document type?", "the sentence document type?", "sentence document type?", "document type?", "type?"], "tokens": ["What", "is", "the", "sentence", "document", "type", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-149965476", "fields": {"term": "What distance metric does the sentence document type use?", "namespace": "open-p", "hash": -149965476, "terms": ["What distance metric does the sentence document type use?", "distance metric does the sentence document type use?", "metric does the sentence document type use?", "does the sentence document type use?", "the sentence document type use?", "sentence document type use?", "document type use?", "type use?", "use?"], "tokens": ["What", "distance", "metric", "does", "the", "sentence", "document", "type", "use", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::280544760", "fields": {"term": "Where can I find details on the HNSW index settings and distance metric?", "namespace": "open-p", "hash": 280544760, "terms": ["Where can I find details on the HNSW index settings and distance metric?", "can I find details on the HNSW index settings and distance metric?", "I find details on the HNSW index settings and distance metric?", "find details on the HNSW index settings and distance metric?", "details on the HNSW index settings and distance metric?", "on the HNSW index settings and distance metric?", "the HNSW index settings and distance metric?", "HNSW index settings and distance metric?", "index settings and distance metric?", "settings and distance metric?", "and distance metric?", "distance metric?", "metric?"], "tokens": ["Where", "can", "I", "find", "details", "on", "the", "HNSW", "index", "settings", "and", "distance", "metric", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::264381967", "fields": {"term": "What is the SQuAD data?", "namespace": "open-p", "hash": 264381967, "terms": ["What is the SQuAD data?", "is the SQuAD data?", "the SQuAD data?", "SQuAD data?", "data?"], "tokens": ["What", "is", "the", "SQuAD", "data", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1202445785", "fields": {"term": "What is the Vespa json format?", "namespace": "open-p", "hash": -1202445785, "terms": ["What is the Vespa json format?", "is the Vespa json format?", "the Vespa json format?", "Vespa json format?", "json format?", "format?"], "tokens": ["What", "is", "the", "Vespa", "json", "format", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-67393516", "fields": {"term": "What is a sentence tokenizer?", "namespace": "open-p", "hash": -67393516, "terms": ["What is a sentence tokenizer?", "is a sentence tokenizer?", "a sentence tokenizer?", "sentence tokenizer?", "tokenizer?"], "tokens": ["What", "is", "a", "sentence", "tokenizer", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::587648630", "fields": {"term": "How many context documents are in the content db?", "namespace": "open-p", "hash": 587648630, "terms": ["How many context documents are in the content db?", "many context documents are in the content db?", "context documents are in the content db?", "documents are in the content db?", "are in the content db?", "in the content db?", "the content db?", "content db?", "db?"], "tokens": ["How", "many", "context", "documents", "are", "in", "the", "content", "db", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1223048398", "fields": {"term": "What is the ReQA task about?", "namespace": "open-p", "hash": 1223048398, "terms": ["What is the ReQA task about?", "is the ReQA task about?", "the ReQA task about?", "ReQA task about?", "task about?", "about?"], "tokens": ["What", "is", "the", "ReQA", "task", "about", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1100275152", "fields": {"term": "How do we retrieve paragraphs?", "namespace": "open-p", "hash": -1100275152, "terms": ["How do we retrieve paragraphs?", "do we retrieve paragraphs?", "we retrieve paragraphs?", "retrieve paragraphs?", "paragraphs?"], "tokens": ["How", "do", "we", "retrieve", "paragraphs", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1416879229", "fields": {"term": "How do we represent paragraph level scores?", "namespace": "open-p", "hash": 1416879229, "terms": ["How do we represent paragraph level scores?", "do we represent paragraph level scores?", "we represent paragraph level scores?", "represent paragraph level scores?", "paragraph level scores?", "level scores?", "scores?"], "tokens": ["How", "do", "we", "represent", "paragraph", "level", "scores", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1348654579", "fields": {"term": "What is dense retrieval?", "namespace": "open-p", "hash": 1348654579, "terms": ["What is dense retrieval?", "is dense retrieval?", "dense retrieval?", "retrieval?"], "tokens": ["What", "is", "dense", "retrieval", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1397283881", "fields": {"term": "What is sparse retrieval?", "namespace": "open-p", "hash": -1397283881, "terms": ["What is sparse retrieval?", "is sparse retrieval?", "sparse retrieval?", "retrieval?"], "tokens": ["What", "is", "sparse", "retrieval", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-242024131", "fields": {"term": "How do you combine dense and sparse retrieval?", "namespace": "open-p", "hash": -242024131, "terms": ["How do you combine dense and sparse retrieval?", "do you combine dense and sparse retrieval?", "you combine dense and sparse retrieval?", "combine dense and sparse retrieval?", "dense and sparse retrieval?", "and sparse retrieval?", "sparse retrieval?", "retrieval?"], "tokens": ["How", "do", "you", "combine", "dense", "and", "sparse", "retrieval", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2132762377", "fields": {"term": "What is the linear combination in bm25-sentence-semantic-similarity profile?", "namespace": "open-p", "hash": -2132762377, "terms": ["What is the linear combination in bm25-sentence-semantic-similarity profile?", "is the linear combination in bm25-sentence-semantic-similarity profile?", "the linear combination in bm25-sentence-semantic-similarity profile?", "linear combination in bm25-sentence-semantic-similarity profile?", "combination in bm25-sentence-semantic-similarity profile?", "in bm25-sentence-semantic-similarity profile?", "bm25-sentence-semantic-similarity profile?", "profile?"], "tokens": ["What", "is", "the", "linear", "combination", "in", "bm25", "sentence", "semantic", "similarity", "profile", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1232228378", "fields": {"term": "How does Vespa support hybrid retrieval?", "namespace": "open-p", "hash": 1232228378, "terms": ["How does Vespa support hybrid retrieval?", "does Vespa support hybrid retrieval?", "Vespa support hybrid retrieval?", "support hybrid retrieval?", "hybrid retrieval?", "retrieval?"], "tokens": ["How", "does", "Vespa", "support", "hybrid", "retrieval", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1337256071", "fields": {"term": "What is the semantic rule language?", "namespace": "open-p", "hash": 1337256071, "terms": ["What is the semantic rule language?", "is the semantic rule language?", "the semantic rule language?", "semantic rule language?", "rule language?", "language?"], "tokens": ["What", "is", "the", "semantic", "rule", "language", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1089255678", "fields": {"term": "Where can I find a guide to using the language?", "namespace": "open-p", "hash": -1089255678, "terms": ["Where can I find a guide to using the language?", "can I find a guide to using the language?", "I find a guide to using the language?", "find a guide to using the language?", "a guide to using the language?", "guide to using the language?", "to using the language?", "using the language?", "the language?", "language?"], "tokens": ["Where", "can", "I", "find", "a", "guide", "to", "using", "the", "language", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-31042421", "fields": {"term": "What is query rewriting in relation to the language?", "namespace": "open-p", "hash": -31042421, "terms": ["What is query rewriting in relation to the language?", "is query rewriting in relation to the language?", "query rewriting in relation to the language?", "rewriting in relation to the language?", "in relation to the language?", "relation to the language?", "to the language?", "the language?", "language?"], "tokens": ["What", "is", "query", "rewriting", "in", "relation", "to", "the", "language", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-169234119", "fields": {"term": "Which API should I refer to when using semantic rules in queries?", "namespace": "open-p", "hash": -169234119, "terms": ["Which API should I refer to when using semantic rules in queries?", "API should I refer to when using semantic rules in queries?", "should I refer to when using semantic rules in queries?", "I refer to when using semantic rules in queries?", "refer to when using semantic rules in queries?", "to when using semantic rules in queries?", "when using semantic rules in queries?", "using semantic rules in queries?", "semantic rules in queries?", "rules in queries?", "in queries?", "queries?"], "tokens": ["Which", "API", "should", "I", "refer", "to", "when", "using", "semantic", "rules", "in", "queries", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-598982015", "fields": {"term": "What are rule bases?", "namespace": "open-p", "hash": -598982015, "terms": ["What are rule bases?", "are rule bases?", "rule bases?", "bases?"], "tokens": ["What", "are", "rule", "bases", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::843808211", "fields": {"term": "Where are rule bases placed?", "namespace": "open-p", "hash": 843808211, "terms": ["Where are rule bases placed?", "are rule bases placed?", "rule bases placed?", "bases placed?", "placed?"], "tokens": ["Where", "are", "rule", "bases", "placed", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::315706108", "fields": {"term": "What are the constructs in a rule base?", "namespace": "open-p", "hash": 315706108, "terms": ["What are the constructs in a rule base?", "are the constructs in a rule base?", "the constructs in a rule base?", "constructs in a rule base?", "in a rule base?", "a rule base?", "rule base?", "base?"], "tokens": ["What", "are", "the", "constructs", "in", "a", "rule", "base", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1875390927", "fields": {"term": "What are statements in a rule base?", "namespace": "open-p", "hash": -1875390927, "terms": ["What are statements in a rule base?", "are statements in a rule base?", "statements in a rule base?", "in a rule base?", "a rule base?", "rule base?", "base?"], "tokens": ["What", "are", "statements", "in", "a", "rule", "base", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1568315519", "fields": {"term": "What are production rules?", "namespace": "open-p", "hash": -1568315519, "terms": ["What are production rules?", "are production rules?", "production rules?", "rules?"], "tokens": ["What", "are", "production", "rules", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1579938724", "fields": {"term": "What are named conditions?", "namespace": "open-p", "hash": 1579938724, "terms": ["What are named conditions?", "are named conditions?", "named conditions?", "conditions?"], "tokens": ["What", "are", "named", "conditions", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-996298916", "fields": {"term": "How are comments formed in a rule base?", "namespace": "open-p", "hash": -996298916, "terms": ["How are comments formed in a rule base?", "are comments formed in a rule base?", "comments formed in a rule base?", "formed in a rule base?", "in a rule base?", "a rule base?", "rule base?", "base?"], "tokens": ["How", "are", "comments", "formed", "in", "a", "rule", "base", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::197188735", "fields": {"term": "What is the syntax for terminating a statement?", "namespace": "open-p", "hash": 197188735, "terms": ["What is the syntax for terminating a statement?", "is the syntax for terminating a statement?", "the syntax for terminating a statement?", "syntax for terminating a statement?", "for terminating a statement?", "terminating a statement?", "a statement?", "statement?"], "tokens": ["What", "is", "the", "syntax", "for", "terminating", "a", "statement", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1290952428", "fields": {"term": "Can statements span multiple lines?", "namespace": "open-p", "hash": 1290952428, "terms": ["Can statements span multiple lines?", "statements span multiple lines?", "span multiple lines?", "multiple lines?", "lines?"], "tokens": ["Can", "statements", "span", "multiple", "lines", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1284796995", "fields": {"term": "What is a directive?", "namespace": "open-p", "hash": 1284796995, "terms": ["What is a directive?", "is a directive?", "a directive?", "directive?"], "tokens": ["What", "is", "a", "directive", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::632486362", "fields": {"term": "What is the syntax for a statement in directive?", "namespace": "open-p", "hash": 632486362, "terms": ["What is the syntax for a statement in directive?", "is the syntax for a statement in directive?", "the syntax for a statement in directive?", "syntax for a statement in directive?", "for a statement in directive?", "a statement in directive?", "statement in directive?", "in directive?", "directive?"], "tokens": ["What", "is", "the", "syntax", "for", "a", "statement", "in", "directive", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::351026344", "fields": {"term": "What is the usage of @include?", "namespace": "open-p", "hash": 351026344, "terms": ["What is the usage of @include?", "is the usage of @include?", "the usage of @include?", "usage of @include?", "of @include?", "@include?"], "tokens": ["What", "is", "the", "usage", "of", "@", "include", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::431837419", "fields": {"term": "What is a production rule?", "namespace": "open-p", "hash": 431837419, "terms": ["What is a production rule?", "is a production rule?", "a production rule?", "production rule?", "rule?"], "tokens": ["What", "is", "a", "production", "rule", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1640365836", "fields": {"term": "What are the two rule kinds?", "namespace": "open-p", "hash": -1640365836, "terms": ["What are the two rule kinds?", "are the two rule kinds?", "the two rule kinds?", "two rule kinds?", "rule kinds?", "kinds?"], "tokens": ["What", "are", "the", "two", "rule", "kinds", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1937852856", "fields": {"term": "What is the syntax of a production rule?", "namespace": "open-p", "hash": -1937852856, "terms": ["What is the syntax of a production rule?", "is the syntax of a production rule?", "the syntax of a production rule?", "syntax of a production rule?", "of a production rule?", "a production rule?", "production rule?", "rule?"], "tokens": ["What", "is", "the", "syntax", "of", "a", "production", "rule", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1700797057", "fields": {"term": "What does the replacing operator do?", "namespace": "open-p", "hash": -1700797057, "terms": ["What does the replacing operator do?", "does the replacing operator do?", "the replacing operator do?", "replacing operator do?", "operator do?", "do?"], "tokens": ["What", "does", "the", "replacing", "operator", "do", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-871649675", "fields": {"term": "What is the default namespace?", "namespace": "open-p", "hash": -871649675, "terms": ["What is the default namespace?", "is the default namespace?", "the default namespace?", "default namespace?", "namespace?"], "tokens": ["What", "is", "the", "default", "namespace", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1298720284", "fields": {"term": "What is a positional namespace?", "namespace": "open-p", "hash": -1298720284, "terms": ["What is a positional namespace?", "is a positional namespace?", "a positional namespace?", "positional namespace?", "namespace?"], "tokens": ["What", "is", "a", "positional", "namespace", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::591007136", "fields": {"term": "How do you determine the namespace?", "namespace": "open-p", "hash": 591007136, "terms": ["How do you determine the namespace?", "do you determine the namespace?", "you determine the namespace?", "determine the namespace?", "the namespace?", "namespace?"], "tokens": ["How", "do", "you", "determine", "the", "namespace", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::456233870", "fields": {"term": "What is the Query namespace?", "namespace": "open-p", "hash": 456233870, "terms": ["What is the Query namespace?", "is the Query namespace?", "the Query namespace?", "Query namespace?", "namespace?"], "tokens": ["What", "is", "the", "Query", "namespace", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1417301928", "fields": {"term": "What is the Parameter namespace?", "namespace": "open-p", "hash": 1417301928, "terms": ["What is the Parameter namespace?", "is the Parameter namespace?", "the Parameter namespace?", "Parameter namespace?", "namespace?"], "tokens": ["What", "is", "the", "Parameter", "namespace", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-725566538", "fields": {"term": "What is a named condition?", "namespace": "open-p", "hash": -725566538, "terms": ["What is a named condition?", "is a named condition?", "a named condition?", "named condition?", "condition?"], "tokens": ["What", "is", "a", "named", "condition", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1754735699", "fields": {"term": "How do you create a named condition?", "namespace": "open-p", "hash": -1754735699, "terms": ["How do you create a named condition?", "do you create a named condition?", "you create a named condition?", "create a named condition?", "a named condition?", "named condition?", "condition?"], "tokens": ["How", "do", "you", "create", "a", "named", "condition", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-951603999", "fields": {"term": "What is the format of a named condition?", "namespace": "open-p", "hash": -951603999, "terms": ["What is the format of a named condition?", "is the format of a named condition?", "the format of a named condition?", "format of a named condition?", "of a named condition?", "a named condition?", "named condition?", "condition?"], "tokens": ["What", "is", "the", "format", "of", "a", "named", "condition", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2089641903", "fields": {"term": "What is the purpose of a named condition?", "namespace": "open-p", "hash": 2089641903, "terms": ["What is the purpose of a named condition?", "is the purpose of a named condition?", "the purpose of a named condition?", "purpose of a named condition?", "of a named condition?", "a named condition?", "named condition?", "condition?"], "tokens": ["What", "is", "the", "purpose", "of", "a", "named", "condition", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2109998244", "fields": {"term": "How can a named condition be used?", "namespace": "open-p", "hash": 2109998244, "terms": ["How can a named condition be used?", "can a named condition be used?", "a named condition be used?", "named condition be used?", "condition be used?", "be used?", "used?"], "tokens": ["How", "can", "a", "named", "condition", "be", "used", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1919964126", "fields": {"term": "What is the syntax for defining a named condition?", "namespace": "open-p", "hash": 1919964126, "terms": ["What is the syntax for defining a named condition?", "is the syntax for defining a named condition?", "the syntax for defining a named condition?", "syntax for defining a named condition?", "for defining a named condition?", "defining a named condition?", "a named condition?", "named condition?", "condition?"], "tokens": ["What", "is", "the", "syntax", "for", "defining", "a", "named", "condition", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1540677353", "fields": {"term": "What is the significance of assigning a name to a condition?", "namespace": "open-p", "hash": -1540677353, "terms": ["What is the significance of assigning a name to a condition?", "is the significance of assigning a name to a condition?", "the significance of assigning a name to a condition?", "significance of assigning a name to a condition?", "of assigning a name to a condition?", "assigning a name to a condition?", "a name to a condition?", "name to a condition?", "to a condition?", "a condition?", "condition?"], "tokens": ["What", "is", "the", "significance", "of", "assigning", "a", "name", "to", "a", "condition", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::791355424", "fields": {"term": "How is a named condition referred in other conditions and rules?", "namespace": "open-p", "hash": 791355424, "terms": ["How is a named condition referred in other conditions and rules?", "is a named condition referred in other conditions and rules?", "a named condition referred in other conditions and rules?", "named condition referred in other conditions and rules?", "condition referred in other conditions and rules?", "referred in other conditions and rules?", "in other conditions and rules?", "other conditions and rules?", "conditions and rules?", "and rules?", "rules?"], "tokens": ["How", "is", "a", "named", "condition", "referred", "in", "other", "conditions", "and", "rules", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2128877841", "fields": {"term": "What does a condition evaluate?", "namespace": "open-p", "hash": 2128877841, "terms": ["What does a condition evaluate?", "does a condition evaluate?", "a condition evaluate?", "condition evaluate?", "evaluate?"], "tokens": ["What", "does", "a", "condition", "evaluate", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1670942993", "fields": {"term": "What is a comparison condition?", "namespace": "open-p", "hash": -1670942993, "terms": ["What is a comparison condition?", "is a comparison condition?", "a comparison condition?", "comparison condition?", "condition?"], "tokens": ["What", "is", "a", "comparison", "condition", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-339284179", "fields": {"term": "What is a reference name?", "namespace": "open-p", "hash": -339284179, "terms": ["What is a reference name?", "is a reference name?", "a reference name?", "reference name?", "name?"], "tokens": ["What", "is", "a", "reference", "name", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::-935335942", "fields": {"term": "What is a label in a condition?", "namespace": "open-p", "hash": -935335942, "terms": ["What is a label in a condition?", "is a label in a condition?", "a label in a condition?", "label in a condition?", "in a condition?", "a condition?", "condition?"], "tokens": ["What", "is", "a", "label", "in", "a", "condition", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::430689207", "fields": {"term": "What does a condition return?", "namespace": "open-p", "hash": 430689207, "terms": ["What does a condition return?", "does a condition return?", "a condition return?", "condition return?", "return?"], "tokens": ["What", "does", "a", "condition", "return", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1261514486", "fields": {"term": "Why is reference name useful?", "namespace": "open-p", "hash": 1261514486, "terms": ["Why is reference name useful?", "is reference name useful?", "reference name useful?", "name useful?", "useful?"], "tokens": ["Why", "is", "reference", "name", "useful", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-404559889", "fields": {"term": "How can a reference name be set?", "namespace": "open-p", "hash": -404559889, "terms": ["How can a reference name be set?", "can a reference name be set?", "a reference name be set?", "reference name be set?", "name be set?", "be set?", "set?"], "tokens": ["How", "can", "a", "reference", "name", "be", "set", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-258150335", "fields": {"term": "What happens if no reference name is given?", "namespace": "open-p", "hash": -258150335, "terms": ["What happens if no reference name is given?", "happens if no reference name is given?", "if no reference name is given?", "no reference name is given?", "reference name is given?", "name is given?", "is given?", "given?"], "tokens": ["What", "happens", "if", "no", "reference", "name", "is", "given", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1904912403", "fields": {"term": "What is label in Vespa?", "namespace": "open-p", "hash": -1904912403, "terms": ["What is label in Vespa?", "is label in Vespa?", "label in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["What", "is", "label", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1914290927", "fields": {"term": "What is the default label in Vespa?", "namespace": "open-p", "hash": 1914290927, "terms": ["What is the default label in Vespa?", "is the default label in Vespa?", "the default label in Vespa?", "default label in Vespa?", "label in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["What", "is", "the", "default", "label", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2129701748", "fields": {"term": "How does a specified label affect term matching?", "namespace": "open-p", "hash": 2129701748, "terms": ["How does a specified label affect term matching?", "does a specified label affect term matching?", "a specified label affect term matching?", "specified label affect term matching?", "label affect term matching?", "affect term matching?", "term matching?", "matching?"], "tokens": ["How", "does", "a", "specified", "label", "affect", "term", "matching", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::318952366", "fields": {"term": "What is the index of a label?", "namespace": "open-p", "hash": 318952366, "terms": ["What is the index of a label?", "is the index of a label?", "the index of a label?", "index of a label?", "of a label?", "a label?", "label?"], "tokens": ["What", "is", "the", "index", "of", "a", "label", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::814855691", "fields": {"term": "How does not setting a label affect term matching?", "namespace": "open-p", "hash": 814855691, "terms": ["How does not setting a label affect term matching?", "does not setting a label affect term matching?", "not setting a label affect term matching?", "setting a label affect term matching?", "a label affect term matching?", "label affect term matching?", "affect term matching?", "term matching?", "matching?"], "tokens": ["How", "does", "not", "setting", "a", "label", "affect", "term", "matching", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1490682880", "fields": {"term": "What is the significance of default label in Vespa?", "namespace": "open-p", "hash": 1490682880, "terms": ["What is the significance of default label in Vespa?", "is the significance of default label in Vespa?", "the significance of default label in Vespa?", "significance of default label in Vespa?", "of default label in Vespa?", "default label in Vespa?", "label in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["What", "is", "the", "significance", "of", "default", "label", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::630252281", "fields": {"term": "What are the supported kinds of conditions?", "namespace": "open-p", "hash": 630252281, "terms": ["What are the supported kinds of conditions?", "are the supported kinds of conditions?", "the supported kinds of conditions?", "supported kinds of conditions?", "kinds of conditions?", "of conditions?", "conditions?"], "tokens": ["What", "are", "the", "supported", "kinds", "of", "conditions", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1639630439", "fields": {"term": "What is a Term condition?", "namespace": "open-p", "hash": 1639630439, "terms": ["What is a Term condition?", "is a Term condition?", "a Term condition?", "Term condition?", "condition?"], "tokens": ["What", "is", "a", "Term", "condition", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::974437619", "fields": {"term": "What is a Reference condition?", "namespace": "open-p", "hash": 974437619, "terms": ["What is a Reference condition?", "is a Reference condition?", "a Reference condition?", "Reference condition?", "condition?"], "tokens": ["What", "is", "a", "Reference", "condition", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2112191444", "fields": {"term": "What is a Sequence condition?", "namespace": "open-p", "hash": -2112191444, "terms": ["What is a Sequence condition?", "is a Sequence condition?", "a Sequence condition?", "Sequence condition?", "condition?"], "tokens": ["What", "is", "a", "Sequence", "condition", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::326193552", "fields": {"term": "What is a Choice condition?", "namespace": "open-p", "hash": 326193552, "terms": ["What is a Choice condition?", "is a Choice condition?", "a Choice condition?", "Choice condition?", "condition?"], "tokens": ["What", "is", "a", "Choice", "condition", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1282451265", "fields": {"term": "What is a Group condition?", "namespace": "open-p", "hash": 1282451265, "terms": ["What is a Group condition?", "is a Group condition?", "a Group condition?", "Group condition?", "condition?"], "tokens": ["What", "is", "a", "Group", "condition", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-807169080", "fields": {"term": "What is a Literal condition?", "namespace": "open-p", "hash": -807169080, "terms": ["What is a Literal condition?", "is a Literal condition?", "a Literal condition?", "Literal condition?", "condition?"], "tokens": ["What", "is", "a", "Literal", "condition", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2056270596", "fields": {"term": "What are the comparison operators?", "namespace": "open-p", "hash": -2056270596, "terms": ["What are the comparison operators?", "are the comparison operators?", "the comparison operators?", "comparison operators?", "operators?"], "tokens": ["What", "are", "the", "comparison", "operators", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1487553570", "fields": {"term": "What does the operator =~ mean?", "namespace": "open-p", "hash": 1487553570, "terms": ["What does the operator =~ mean?", "does the operator =~ mean?", "the operator =~ mean?", "operator =~ mean?", "=~ mean?", "mean?"], "tokens": ["What", "does", "the", "operator", "=~", "mean", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1939302387", "fields": {"term": "What is the meaning of operator ` element?", "namespace": "open-p", "hash": 380247837, "terms": ["What is the `` element?", "is the `` element?", "the `` element?", "`` element?", "element?"], "tokens": ["What", "is", "the", "`<", "configservers", ">`", "element", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1752684407", "fields": {"term": "Is the `baseport` attribute required?", "namespace": "open-p", "hash": 1752684407, "terms": ["Is the `baseport` attribute required?", "the `baseport` attribute required?", "`baseport` attribute required?", "attribute required?", "required?"], "tokens": ["Is", "the", "`", "baseport", "`", "attribute", "required", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1703985334", "fields": {"term": "Can you have multiple Configuration Server instances?", "namespace": "open-p", "hash": 1703985334, "terms": ["Can you have multiple Configuration Server instances?", "you have multiple Configuration Server instances?", "have multiple Configuration Server instances?", "multiple Configuration Server instances?", "Configuration Server instances?", "Server instances?", "instances?"], "tokens": ["Can", "you", "have", "multiple", "Configuration", "Server", "instances", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-908129036", "fields": {"term": "What is the default value for baseport?", "namespace": "open-p", "hash": -908129036, "terms": ["What is the default value for baseport?", "is the default value for baseport?", "the default value for baseport?", "default value for baseport?", "value for baseport?", "for baseport?", "baseport?"], "tokens": ["What", "is", "the", "default", "value", "for", "baseport", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1415209791", "fields": {"term": "What is the Log Server service?", "namespace": "open-p", "hash": 1415209791, "terms": ["What is the Log Server service?", "is the Log Server service?", "the Log Server service?", "Log Server service?", "Server service?", "service?"], "tokens": ["What", "is", "the", "Log", "Server", "service", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::84401590", "fields": {"term": "Where is the logserver placed?", "namespace": "open-p", "hash": 84401590, "terms": ["Where is the logserver placed?", "is the logserver placed?", "the logserver placed?", "logserver placed?", "placed?"], "tokens": ["Where", "is", "the", "logserver", "placed", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1909778505", "fields": {"term": "What is hostalias?", "namespace": "open-p", "hash": 1909778505, "terms": ["What is hostalias?", "is hostalias?", "hostalias?"], "tokens": ["What", "is", "hostalias", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-453008725", "fields": {"term": "What optional values does the logserver have?", "namespace": "open-p", "hash": -453008725, "terms": ["What optional values does the logserver have?", "optional values does the logserver have?", "values does the logserver have?", "does the logserver have?", "the logserver have?", "logserver have?", "have?"], "tokens": ["What", "optional", "values", "does", "the", "logserver", "have", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1763941003", "fields": {"term": "What is a slobrok element?", "namespace": "open-p", "hash": 1763941003, "terms": ["What is a slobrok element?", "is a slobrok element?", "a slobrok element?", "slobrok element?", "element?"], "tokens": ["What", "is", "a", "slobrok", "element", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1037805552", "fields": {"term": "What is slobrok service?", "namespace": "open-p", "hash": 1037805552, "terms": ["What is slobrok service?", "is slobrok service?", "slobrok service?", "service?"], "tokens": ["What", "is", "slobrok", "service", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1149250173", "fields": {"term": "What is the default baseport value?", "namespace": "open-p", "hash": -1149250173, "terms": ["What is the default baseport value?", "is the default baseport value?", "the default baseport value?", "default baseport value?", "baseport value?", "value?"], "tokens": ["What", "is", "the", "default", "baseport", "value", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1875367440", "fields": {"term": "What is the default system name for monitoring service?", "namespace": "open-p", "hash": -1875367440, "terms": ["What is the default system name for monitoring service?", "is the default system name for monitoring service?", "the default system name for monitoring service?", "default system name for monitoring service?", "system name for monitoring service?", "name for monitoring service?", "for monitoring service?", "monitoring service?", "service?"], "tokens": ["What", "is", "the", "default", "system", "name", "for", "monitoring", "service", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1660041288", "fields": {"term": "What is the element tag for monitoring in Vespa?", "namespace": "open-p", "hash": -1660041288, "terms": ["What is the element tag for monitoring in Vespa?", "is the element tag for monitoring in Vespa?", "the element tag for monitoring in Vespa?", "element tag for monitoring in Vespa?", "tag for monitoring in Vespa?", "for monitoring in Vespa?", "monitoring in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["What", "is", "the", "element", "tag", "for", "monitoring", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1999149139", "fields": {"term": "What attribute specifies the system name for monitoring service?", "namespace": "open-p", "hash": 1999149139, "terms": ["What attribute specifies the system name for monitoring service?", "attribute specifies the system name for monitoring service?", "specifies the system name for monitoring service?", "the system name for monitoring service?", "system name for monitoring service?", "name for monitoring service?", "for monitoring service?", "monitoring service?", "service?"], "tokens": ["What", "attribute", "specifies", "the", "system", "name", "for", "monitoring", "service", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-78974789", "fields": {"term": "What is the purpose of the logging package?", "namespace": "open-p", "hash": -78974789, "terms": ["What is the purpose of the logging package?", "is the purpose of the logging package?", "the purpose of the logging package?", "purpose of the logging package?", "of the logging package?", "the logging package?", "logging package?", "package?"], "tokens": ["What", "is", "the", "purpose", "of", "the", "logging", "package", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::544234646", "fields": {"term": "How can you tune log levels?", "namespace": "open-p", "hash": 544234646, "terms": ["How can you tune log levels?", "can you tune log levels?", "you tune log levels?", "tune log levels?", "log levels?", "levels?"], "tokens": ["How", "can", "you", "tune", "log", "levels", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-878740149", "fields": {"term": "How do you enable debug logging?", "namespace": "open-p", "hash": -878740149, "terms": ["How do you enable debug logging?", "do you enable debug logging?", "you enable debug logging?", "enable debug logging?", "debug logging?", "logging?"], "tokens": ["How", "do", "you", "enable", "debug", "logging", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-85661427", "fields": {"term": "What levels can you modify?", "namespace": "open-p", "hash": -85661427, "terms": ["What levels can you modify?", "levels can you modify?", "can you modify?", "you modify?", "modify?"], "tokens": ["What", "levels", "can", "you", "modify", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1272531006", "fields": {"term": "What is the default level?", "namespace": "open-p", "hash": -1272531006, "terms": ["What is the default level?", "is the default level?", "the default level?", "default level?", "level?"], "tokens": ["What", "is", "the", "default", "level", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2047730295", "fields": {"term": "What happens to sub-packages when tuning levels?", "namespace": "open-p", "hash": -2047730295, "terms": ["What happens to sub-packages when tuning levels?", "happens to sub-packages when tuning levels?", "to sub-packages when tuning levels?", "sub-packages when tuning levels?", "when tuning levels?", "tuning levels?", "levels?"], "tokens": ["What", "happens", "to", "sub", "packages", "when", "tuning", "levels", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-77847982", "fields": {"term": "What is the purpose of the 'consumer' element?", "namespace": "open-p", "hash": -77847982, "terms": ["What is the purpose of the 'consumer' element?", "is the purpose of the 'consumer' element?", "the purpose of the 'consumer' element?", "purpose of the 'consumer' element?", "of the 'consumer' element?", "the 'consumer' element?", "'consumer' element?", "element?"], "tokens": ["What", "is", "the", "purpose", "of", "the", "'", "consumer", "'", "element", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1850331654", "fields": {"term": "What is the 'region' attribute used for in the 'cloudwatch' element?", "namespace": "open-p", "hash": -1850331654, "terms": ["What is the 'region' attribute used for in the 'cloudwatch' element?", "is the 'region' attribute used for in the 'cloudwatch' element?", "the 'region' attribute used for in the 'cloudwatch' element?", "'region' attribute used for in the 'cloudwatch' element?", "attribute used for in the 'cloudwatch' element?", "used for in the 'cloudwatch' element?", "for in the 'cloudwatch' element?", "in the 'cloudwatch' element?", "the 'cloudwatch' element?", "'cloudwatch' element?", "element?"], "tokens": ["What", "is", "the", "'", "region", "'", "attribute", "used", "for", "in", "the", "'", "cloudwatch", "'", "element", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2131619387", "fields": {"term": "What is the 'namespace' attribute used for in the 'cloudwatch' element?", "namespace": "open-p", "hash": 2131619387, "terms": ["What is the 'namespace' attribute used for in the 'cloudwatch' element?", "is the 'namespace' attribute used for in the 'cloudwatch' element?", "the 'namespace' attribute used for in the 'cloudwatch' element?", "'namespace' attribute used for in the 'cloudwatch' element?", "attribute used for in the 'cloudwatch' element?", "used for in the 'cloudwatch' element?", "for in the 'cloudwatch' element?", "in the 'cloudwatch' element?", "the 'cloudwatch' element?", "'cloudwatch' element?", "element?"], "tokens": ["What", "is", "the", "'", "namespace", "'", "attribute", "used", "for", "in", "the", "'", "cloudwatch", "'", "element", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1847475716", "fields": {"term": "What is the 'shared-credentials' element used for in the 'cloudwatch' element?", "namespace": "open-p", "hash": -1847475716, "terms": ["What is the 'shared-credentials' element used for in the 'cloudwatch' element?", "is the 'shared-credentials' element used for in the 'cloudwatch' element?", "the 'shared-credentials' element used for in the 'cloudwatch' element?", "'shared-credentials' element used for in the 'cloudwatch' element?", "element used for in the 'cloudwatch' element?", "used for in the 'cloudwatch' element?", "for in the 'cloudwatch' element?", "in the 'cloudwatch' element?", "the 'cloudwatch' element?", "'cloudwatch' element?", "element?"], "tokens": ["What", "is", "the", "'", "shared", "credentials", "'", "element", "used", "for", "in", "the", "'", "cloudwatch", "'", "element", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1141488261", "fields": {"term": "What is the syntax for adding a custom metric to the 'consumer' element?", "namespace": "open-p", "hash": -1141488261, "terms": ["What is the syntax for adding a custom metric to the 'consumer' element?", "is the syntax for adding a custom metric to the 'consumer' element?", "the syntax for adding a custom metric to the 'consumer' element?", "syntax for adding a custom metric to the 'consumer' element?", "for adding a custom metric to the 'consumer' element?", "adding a custom metric to the 'consumer' element?", "a custom metric to the 'consumer' element?", "custom metric to the 'consumer' element?", "metric to the 'consumer' element?", "to the 'consumer' element?", "the 'consumer' element?", "'consumer' element?", "element?"], "tokens": ["What", "is", "the", "syntax", "for", "adding", "a", "custom", "metric", "to", "the", "'", "consumer", "'", "element", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2133897434", "fields": {"term": "What is a metrics consumer?", "namespace": "open-p", "hash": -2133897434, "terms": ["What is a metrics consumer?", "is a metrics consumer?", "a metrics consumer?", "metrics consumer?", "consumer?"], "tokens": ["What", "is", "a", "metrics", "consumer", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::718563667", "fields": {"term": "How do you export metrics?", "namespace": "open-p", "hash": 718563667, "terms": ["How do you export metrics?", "do you export metrics?", "you export metrics?", "export metrics?", "metrics?"], "tokens": ["How", "do", "you", "export", "metrics", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1496753548", "fields": {"term": "What are built-in metric sets?", "namespace": "open-p", "hash": -1496753548, "terms": ["What are built-in metric sets?", "are built-in metric sets?", "built-in metric sets?", "metric sets?", "sets?"], "tokens": ["What", "are", "built", "in", "metric", "sets", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::165240718", "fields": {"term": "What is metric id?", "namespace": "open-p", "hash": 165240718, "terms": ["What is metric id?", "is metric id?", "metric id?", "id?"], "tokens": ["What", "is", "metric", "id", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-10719710", "fields": {"term": "How to add metric to consumer?", "namespace": "open-p", "hash": -10719710, "terms": ["How to add metric to consumer?", "to add metric to consumer?", "add metric to consumer?", "metric to consumer?", "to consumer?", "consumer?"], "tokens": ["How", "to", "add", "metric", "to", "consumer", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-333789173", "fields": {"term": "What is process metrics api?", "namespace": "open-p", "hash": -333789173, "terms": ["What is process metrics api?", "is process metrics api?", "process metrics api?", "metrics api?", "api?"], "tokens": ["What", "is", "process", "metrics", "api", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1063807124", "fields": {"term": "What is in the /state/v1/metrics endpoint?", "namespace": "open-p", "hash": -1063807124, "terms": ["What is in the /state/v1/metrics endpoint?", "is in the /state/v1/metrics endpoint?", "in the /state/v1/metrics endpoint?", "the /state/v1/metrics endpoint?", "/state/v1/metrics endpoint?", "endpoint?"], "tokens": ["What", "is", "in", "the", "/", "state", "/", "v1", "/", "metrics", "endpoint", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::285196918", "fields": {"term": "What is the format for custom metric ids?", "namespace": "open-p", "hash": 285196918, "terms": ["What is the format for custom metric ids?", "is the format for custom metric ids?", "the format for custom metric ids?", "format for custom metric ids?", "for custom metric ids?", "custom metric ids?", "metric ids?", "ids?"], "tokens": ["What", "is", "the", "format", "for", "custom", "metric", "ids", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-491519833", "fields": {"term": "What metrics are included in the default set?", "namespace": "open-p", "hash": -491519833, "terms": ["What metrics are included in the default set?", "metrics are included in the default set?", "are included in the default set?", "included in the default set?", "in the default set?", "the default set?", "default set?", "set?"], "tokens": ["What", "metrics", "are", "included", "in", "the", "default", "set", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2133954077", "fields": {"term": "What is the prometheus endpoint for custom metrics?", "namespace": "open-p", "hash": 2133954077, "terms": ["What is the prometheus endpoint for custom metrics?", "is the prometheus endpoint for custom metrics?", "the prometheus endpoint for custom metrics?", "prometheus endpoint for custom metrics?", "endpoint for custom metrics?", "for custom metrics?", "custom metrics?", "metrics?"], "tokens": ["What", "is", "the", "prometheus", "endpoint", "for", "custom", "metrics", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1953253414", "fields": {"term": "What is a metric aggregate?", "namespace": "open-p", "hash": 1953253414, "terms": ["What is a metric aggregate?", "is a metric aggregate?", "a metric aggregate?", "metric aggregate?", "aggregate?"], "tokens": ["What", "is", "a", "metric", "aggregate", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::453315499", "fields": {"term": "What is CloudWatch?", "namespace": "open-p", "hash": 453315499, "terms": ["What is CloudWatch?", "is CloudWatch?", "CloudWatch?"], "tokens": ["What", "is", "CloudWatch", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::365031336", "fields": {"term": "What metrics can be forwarded?", "namespace": "open-p", "hash": 365031336, "terms": ["What metrics can be forwarded?", "metrics can be forwarded?", "can be forwarded?", "be forwarded?", "forwarded?"], "tokens": ["What", "metrics", "can", "be", "forwarded", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1811867248", "fields": {"term": "What is the required region?", "namespace": "open-p", "hash": 1811867248, "terms": ["What is the required region?", "is the required region?", "the required region?", "required region?", "region?"], "tokens": ["What", "is", "the", "required", "region", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::519398231", "fields": {"term": "What is the required namespace?", "namespace": "open-p", "hash": 519398231, "terms": ["What is the required namespace?", "is the required namespace?", "the required namespace?", "required namespace?", "namespace?"], "tokens": ["What", "is", "the", "required", "namespace", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1424966198", "fields": {"term": "Is region a required field?", "namespace": "open-p", "hash": 1424966198, "terms": ["Is region a required field?", "region a required field?", "a required field?", "required field?", "field?"], "tokens": ["Is", "region", "a", "required", "field", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::144772647", "fields": {"term": "Is namespace a required field?", "namespace": "open-p", "hash": 144772647, "terms": ["Is namespace a required field?", "namespace a required field?", "a required field?", "required field?", "field?"], "tokens": ["Is", "namespace", "a", "required", "field", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2012287762", "fields": {"term": "What is the required attribute for CloudWatch authentication?", "namespace": "open-p", "hash": 2012287762, "terms": ["What is the required attribute for CloudWatch authentication?", "is the required attribute for CloudWatch authentication?", "the required attribute for CloudWatch authentication?", "required attribute for CloudWatch authentication?", "attribute for CloudWatch authentication?", "for CloudWatch authentication?", "CloudWatch authentication?", "authentication?"], "tokens": ["What", "is", "the", "required", "attribute", "for", "CloudWatch", "authentication", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::108310330", "fields": {"term": "What is the 'file' attribute in CloudWatch authentication?", "namespace": "open-p", "hash": 108310330, "terms": ["What is the 'file' attribute in CloudWatch authentication?", "is the 'file' attribute in CloudWatch authentication?", "the 'file' attribute in CloudWatch authentication?", "'file' attribute in CloudWatch authentication?", "attribute in CloudWatch authentication?", "in CloudWatch authentication?", "CloudWatch authentication?", "authentication?"], "tokens": ["What", "is", "the", "'", "file", "'", "attribute", "in", "CloudWatch", "authentication", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::203969666", "fields": {"term": "What is the default profile for CloudWatch authentication?", "namespace": "open-p", "hash": 203969666, "terms": ["What is the default profile for CloudWatch authentication?", "is the default profile for CloudWatch authentication?", "the default profile for CloudWatch authentication?", "default profile for CloudWatch authentication?", "profile for CloudWatch authentication?", "for CloudWatch authentication?", "CloudWatch authentication?", "authentication?"], "tokens": ["What", "is", "the", "default", "profile", "for", "CloudWatch", "authentication", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-788598838", "fields": {"term": "What is the 'profile' attribute in CloudWatch authentication?", "namespace": "open-p", "hash": -788598838, "terms": ["What is the 'profile' attribute in CloudWatch authentication?", "is the 'profile' attribute in CloudWatch authentication?", "the 'profile' attribute in CloudWatch authentication?", "'profile' attribute in CloudWatch authentication?", "attribute in CloudWatch authentication?", "in CloudWatch authentication?", "CloudWatch authentication?", "authentication?"], "tokens": ["What", "is", "the", "'", "profile", "'", "attribute", "in", "CloudWatch", "authentication", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-970204092", "fields": {"term": "What is a container cluster?", "namespace": "open-p", "hash": -970204092, "terms": ["What is a container cluster?", "is a container cluster?", "a container cluster?", "container cluster?", "cluster?"], "tokens": ["What", "is", "a", "container", "cluster", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-28782598", "fields": {"term": "What is the purpose of the 'id' attribute in container element?", "namespace": "open-p", "hash": -28782598, "terms": ["What is the purpose of the 'id' attribute in container element?", "is the purpose of the 'id' attribute in container element?", "the purpose of the 'id' attribute in container element?", "purpose of the 'id' attribute in container element?", "of the 'id' attribute in container element?", "the 'id' attribute in container element?", "'id' attribute in container element?", "attribute in container element?", "in container element?", "container element?", "element?"], "tokens": ["What", "is", "the", "purpose", "of", "the", "'", "id", "'", "attribute", "in", "container", "element", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1245360465", "fields": {"term": "What is a binding tag?", "namespace": "open-p", "hash": 1245360465, "terms": ["What is a binding tag?", "is a binding tag?", "a binding tag?", "binding tag?", "tag?"], "tokens": ["What", "is", "a", "binding", "tag", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-304074622", "fields": {"term": "What is the server element?", "namespace": "open-p", "hash": -304074622, "terms": ["What is the server element?", "is the server element?", "the server element?", "server element?", "element?"], "tokens": ["What", "is", "the", "server", "element", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2011313667", "fields": {"term": "What attributes does server element have?", "namespace": "open-p", "hash": -2011313667, "terms": ["What attributes does server element have?", "attributes does server element have?", "does server element have?", "server element have?", "element have?", "have?"], "tokens": ["What", "attributes", "does", "server", "element", "have", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-663317627", "fields": {"term": "What are component elements?", "namespace": "open-p", "hash": -663317627, "terms": ["What are component elements?", "are component elements?", "component elements?", "elements?"], "tokens": ["What", "are", "component", "elements", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1121544997", "fields": {"term": "What is modular config?", "namespace": "open-p", "hash": 1121544997, "terms": ["What is modular config?", "is modular config?", "modular config?", "config?"], "tokens": ["What", "is", "modular", "config", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-386012963", "fields": {"term": "What is the purpose of the 'component' element?", "namespace": "open-p", "hash": -386012963, "terms": ["What is the purpose of the 'component' element?", "is the purpose of the 'component' element?", "the purpose of the 'component' element?", "purpose of the 'component' element?", "of the 'component' element?", "the 'component' element?", "'component' element?", "element?"], "tokens": ["What", "is", "the", "purpose", "of", "the", "'", "component", "'", "element", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::656512814", "fields": {"term": "What is a 'nested component'?", "namespace": "open-p", "hash": 656512814, "terms": ["What is a 'nested component'?", "is a 'nested component'?", "a 'nested component'?", "'nested component'?", "component'?"], "tokens": ["What", "is", "a", "'", "nested", "component", "'?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::574934588", "fields": {"term": "What is the purpose of ?", "namespace": "open-p", "hash": 574934588, "terms": ["What is the purpose of ?", "is the purpose of ?", "the purpose of ?", "purpose of ?", "of ?", "?"], "tokens": ["What", "is", "the", "purpose", "of", "<", "abortondocumenterror", ">?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1614997947", "fields": {"term": "What is the parameter for?", "namespace": "open-p", "hash": 1614997947, "terms": ["What is the parameter for?", "is the parameter for?", "the parameter for?", " parameter for?", "parameter for?", "for?"], "tokens": ["What", "is", "the", "<", "maxpendingdocs", ">", "parameter", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1793906319", "fields": {"term": "What is the default value for ?", "namespace": "open-p", "hash": -1793906319, "terms": ["What is the default value for ?", "is the default value for ?", "the default value for ?", "default value for ?", "value for ?", "for ?", "?"], "tokens": ["What", "is", "the", "default", "value", "for", "<", "retrydelay", ">?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::55516693", "fields": {"term": "What is the purpose of ?", "namespace": "open-p", "hash": 55516693, "terms": ["What is the purpose of ?", "is the purpose of ?", "the purpose of ?", "purpose of ?", "of ?", "?"], "tokens": ["What", "is", "the", "purpose", "of", "<", "ignore", "undefined", "fields", ">?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2144238490", "fields": {"term": "What is the maximum number of threads set for ?", "namespace": "open-p", "hash": -2144238490, "terms": ["What is the maximum number of threads set for ?", "is the maximum number of threads set for ?", "the maximum number of threads set for ?", "maximum number of threads set for ?", "number of threads set for ?", "of threads set for ?", "threads set for ?", "set for ?", "for ?", "?"], "tokens": ["What", "is", "the", "maximum", "number", "of", "threads", "set", "for", "<", "threadpool", ">?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::936660471", "fields": {"term": "What is the syntax for setting multiple bindings in ?", "namespace": "open-p", "hash": 936660471, "terms": ["What is the syntax for setting multiple bindings in ?", "is the syntax for setting multiple bindings in ?", "the syntax for setting multiple bindings in ?", "syntax for setting multiple bindings in ?", "for setting multiple bindings in ?", "setting multiple bindings in ?", "multiple bindings in ?", "bindings in ?", "in ?", "?"], "tokens": ["What", "is", "the", "syntax", "for", "setting", "multiple", "bindings", "in", "<", "document", "api", ">?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-389134098", "fields": {"term": "What is http-client-api?", "namespace": "open-p", "hash": -389134098, "terms": ["What is http-client-api?", "is http-client-api?", "http-client-api?"], "tokens": ["What", "is", "http", "client", "api", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1808039141", "fields": {"term": "What is the threadpool configuration?", "namespace": "open-p", "hash": -1808039141, "terms": ["What is the threadpool configuration?", "is the threadpool configuration?", "the threadpool configuration?", "threadpool configuration?", "configuration?"], "tokens": ["What", "is", "the", "threadpool", "configuration", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1433417255", "fields": {"term": "What is the default accesslog type?", "namespace": "open-p", "hash": 1433417255, "terms": ["What is the default accesslog type?", "is the default accesslog type?", "the default accesslog type?", "default accesslog type?", "accesslog type?", "type?"], "tokens": ["What", "is", "the", "default", "accesslog", "type", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1090719909", "fields": {"term": "How can access logging be disabled?", "namespace": "open-p", "hash": 1090719909, "terms": ["How can access logging be disabled?", "can access logging be disabled?", "access logging be disabled?", "logging be disabled?", "be disabled?", "disabled?"], "tokens": ["How", "can", "access", "logging", "be", "disabled", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1126040308", "fields": {"term": "What are the valid values for rotationScheme?", "namespace": "open-p", "hash": 1126040308, "terms": ["What are the valid values for rotationScheme?", "are the valid values for rotationScheme?", "the valid values for rotationScheme?", "valid values for rotationScheme?", "values for rotationScheme?", "for rotationScheme?", "rotationScheme?"], "tokens": ["What", "are", "the", "valid", "values", "for", "rotationScheme", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1264585484", "fields": {"term": "What is the file name pattern for the default type?", "namespace": "open-p", "hash": 1264585484, "terms": ["What is the file name pattern for the default type?", "is the file name pattern for the default type?", "the file name pattern for the default type?", "file name pattern for the default type?", "name pattern for the default type?", "pattern for the default type?", "for the default type?", "the default type?", "default type?", "type?"], "tokens": ["What", "is", "the", "file", "name", "pattern", "for", "the", "default", "type", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-863772115", "fields": {"term": "What is the purpose of the symlink name?", "namespace": "open-p", "hash": -863772115, "terms": ["What is the purpose of the symlink name?", "is the purpose of the symlink name?", "the purpose of the symlink name?", "purpose of the symlink name?", "of the symlink name?", "the symlink name?", "symlink name?", "name?"], "tokens": ["What", "is", "the", "purpose", "of", "the", "symlink", "name", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2018485513", "fields": {"term": "What attribute is required when type is disabled?", "namespace": "open-p", "hash": 2018485513, "terms": ["What attribute is required when type is disabled?", "attribute is required when type is disabled?", "is required when type is disabled?", "required when type is disabled?", "when type is disabled?", "type is disabled?", "is disabled?", "disabled?"], "tokens": ["What", "attribute", "is", "required", "when", "type", "is", "disabled", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-647128670", "fields": {"term": "What does dir specify?", "namespace": "open-p", "hash": -647128670, "terms": ["What does dir specify?", "does dir specify?", "dir specify?", "specify?"], "tokens": ["What", "does", "dir", "specify", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::143233269", "fields": {"term": "What is the inclusion order?", "namespace": "open-p", "hash": 143233269, "terms": ["What is the inclusion order?", "is the inclusion order?", "the inclusion order?", "inclusion order?", "order?"], "tokens": ["What", "is", "the", "inclusion", "order", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-229733793", "fields": {"term": "What is the outer tag restriction?", "namespace": "open-p", "hash": -229733793, "terms": ["What is the outer tag restriction?", "is the outer tag restriction?", "the outer tag restriction?", "outer tag restriction?", "tag restriction?", "restriction?"], "tokens": ["What", "is", "the", "outer", "tag", "restriction", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-468790273", "fields": {"term": "What are node children?", "namespace": "open-p", "hash": -468790273, "terms": ["What are node children?", "are node children?", "node children?", "children?"], "tokens": ["What", "are", "node", "children", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-193629069", "fields": {"term": "What are jvm-gc-options?", "namespace": "open-p", "hash": -193629069, "terms": ["What are jvm-gc-options?", "are jvm-gc-options?", "jvm-gc-options?"], "tokens": ["What", "are", "jvm", "gc", "options", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1993310988", "fields": {"term": "How do you add children elements to nodes?", "namespace": "open-p", "hash": 1993310988, "terms": ["How do you add children elements to nodes?", "do you add children elements to nodes?", "you add children elements to nodes?", "add children elements to nodes?", "children elements to nodes?", "elements to nodes?", "to nodes?", "nodes?"], "tokens": ["How", "do", "you", "add", "children", "elements", "to", "nodes", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1572191068", "fields": {"term": "When are environment variables available?", "namespace": "open-p", "hash": 1572191068, "terms": ["When are environment variables available?", "are environment variables available?", "environment variables available?", "variables available?", "available?"], "tokens": ["When", "are", "environment", "variables", "available", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1577368632", "fields": {"term": "What are JVM settings?", "namespace": "open-p", "hash": -1577368632, "terms": ["What are JVM settings?", "are JVM settings?", "JVM settings?", "settings?"], "tokens": ["What", "are", "JVM", "settings", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1353659177", "fields": {"term": "What is the default allocated-memory value?", "namespace": "open-p", "hash": 1353659177, "terms": ["What is the default allocated-memory value?", "is the default allocated-memory value?", "the default allocated-memory value?", "default allocated-memory value?", "allocated-memory value?", "value?"], "tokens": ["What", "is", "the", "default", "allocated", "memory", "value", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1529533059", "fields": {"term": "What is the required value for 'name' attribute?", "namespace": "open-p", "hash": 1529533059, "terms": ["What is the required value for 'name' attribute?", "is the required value for 'name' attribute?", "the required value for 'name' attribute?", "required value for 'name' attribute?", "value for 'name' attribute?", "for 'name' attribute?", "'name' attribute?", "attribute?"], "tokens": ["What", "is", "the", "required", "value", "for", "'", "name", "'", "attribute", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-573739216", "fields": {"term": "What is 'my-ckms'?", "namespace": "open-p", "hash": -573739216, "terms": ["What is 'my-ckms'?", "is 'my-ckms'?", "'my-ckms'?"], "tokens": ["What", "is", "'", "my", "ckms", "'?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1487273343", "fields": {"term": "What does the zookeeper element do?", "namespace": "open-p", "hash": -1487273343, "terms": ["What does the zookeeper element do?", "does the zookeeper element do?", "the zookeeper element do?", "zookeeper element do?", "element do?", "do?"], "tokens": ["What", "does", "the", "zookeeper", "element", "do", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-785759727", "fields": {"term": "What can be configured with zookeeper?", "namespace": "open-p", "hash": -785759727, "terms": ["What can be configured with zookeeper?", "can be configured with zookeeper?", "be configured with zookeeper?", "configured with zookeeper?", "with zookeeper?", "zookeeper?"], "tokens": ["What", "can", "be", "configured", "with", "zookeeper", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2048477050", "fields": {"term": "What is 'coverage minimum' in search?", "namespace": "open-p", "hash": 2048477050, "terms": ["What is 'coverage minimum' in search?", "is 'coverage minimum' in search?", "'coverage minimum' in search?", "minimum' in search?", "in search?", "search?"], "tokens": ["What", "is", "'", "coverage", "minimum", "'", "in", "search", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-710377607", "fields": {"term": "What is the 'top-k-probability' in tuning?", "namespace": "open-p", "hash": -710377607, "terms": ["What is the 'top-k-probability' in tuning?", "is the 'top-k-probability' in tuning?", "the 'top-k-probability' in tuning?", "'top-k-probability' in tuning?", "in tuning?", "tuning?"], "tokens": ["What", "is", "the", "'", "top", "k", "probability", "'", "in", "tuning", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1696497145", "fields": {"term": "What is dispatch policy in tuning?", "namespace": "open-p", "hash": 1696497145, "terms": ["What is dispatch policy in tuning?", "is dispatch policy in tuning?", "dispatch policy in tuning?", "policy in tuning?", "in tuning?", "tuning?"], "tokens": ["What", "is", "dispatch", "policy", "in", "tuning", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1677291752", "fields": {"term": "What is 'attribute-io-write' in engine?", "namespace": "open-p", "hash": 1677291752, "terms": ["What is 'attribute-io-write' in engine?", "is 'attribute-io-write' in engine?", "'attribute-io-write' in engine?", "in engine?", "engine?"], "tokens": ["What", "is", "'", "attribute", "io", "write", "'", "in", "engine", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2066036884", "fields": {"term": "What is 'init-progress-time' in cluster-controller?", "namespace": "open-p", "hash": 2066036884, "terms": ["What is 'init-progress-time' in cluster-controller?", "is 'init-progress-time' in cluster-controller?", "'init-progress-time' in cluster-controller?", "in cluster-controller?", "cluster-controller?"], "tokens": ["What", "is", "'", "init", "progress", "time", "'", "in", "cluster", "controller", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1719370070", "fields": {"term": "What is 'distributor-base-port' used for?", "namespace": "open-p", "hash": 1719370070, "terms": ["What is 'distributor-base-port' used for?", "is 'distributor-base-port' used for?", "'distributor-base-port' used for?", "used for?", "for?"], "tokens": ["What", "is", "'", "distributor", "base", "port", "'", "used", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1465366818", "fields": {"term": "What are the available subelements of a content cluster?", "namespace": "open-p", "hash": 1465366818, "terms": ["What are the available subelements of a content cluster?", "are the available subelements of a content cluster?", "the available subelements of a content cluster?", "available subelements of a content cluster?", "subelements of a content cluster?", "of a content cluster?", "a content cluster?", "content cluster?", "cluster?"], "tokens": ["What", "are", "the", "available", "subelements", "of", "a", "content", "cluster", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1694365870", "fields": {"term": "What is the content cluster for?", "namespace": "open-p", "hash": 1694365870, "terms": ["What is the content cluster for?", "is the content cluster for?", "the content cluster for?", "content cluster for?", "cluster for?", "for?"], "tokens": ["What", "is", "the", "content", "cluster", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-409573808", "fields": {"term": "How are documents selected for the cluster?", "namespace": "open-p", "hash": -409573808, "terms": ["How are documents selected for the cluster?", "are documents selected for the cluster?", "documents selected for the cluster?", "selected for the cluster?", "for the cluster?", "the cluster?", "cluster?"], "tokens": ["How", "are", "documents", "selected", "for", "the", "cluster", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1160725889", "fields": {"term": "What is a document selection?", "namespace": "open-p", "hash": -1160725889, "terms": ["What is a document selection?", "is a document selection?", "a document selection?", "document selection?", "selection?"], "tokens": ["What", "is", "a", "document", "selection", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1831535875", "fields": {"term": "What is the purpose of garbage collection?", "namespace": "open-p", "hash": -1831535875, "terms": ["What is the purpose of garbage collection?", "is the purpose of garbage collection?", "the purpose of garbage collection?", "purpose of garbage collection?", "of garbage collection?", "garbage collection?", "collection?"], "tokens": ["What", "is", "the", "purpose", "of", "garbage", "collection", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::781156238", "fields": {"term": "What is the default garbage-collection value?", "namespace": "open-p", "hash": 781156238, "terms": ["What is the default garbage-collection value?", "is the default garbage-collection value?", "the default garbage-collection value?", "default garbage-collection value?", "garbage-collection value?", "value?"], "tokens": ["What", "is", "the", "default", "garbage", "collection", "value", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-939639454", "fields": {"term": "What is the garbage-collection interval?", "namespace": "open-p", "hash": -939639454, "terms": ["What is the garbage-collection interval?", "is the garbage-collection interval?", "the garbage-collection interval?", "garbage-collection interval?", "interval?"], "tokens": ["What", "is", "the", "garbage", "collection", "interval", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1478168303", "fields": {"term": "What is the optional document-processing for?", "namespace": "open-p", "hash": -1478168303, "terms": ["What is the optional document-processing for?", "is the optional document-processing for?", "the optional document-processing for?", "optional document-processing for?", "document-processing for?", "for?"], "tokens": ["What", "is", "the", "optional", "document", "processing", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-315994173", "fields": {"term": "What is the required type?", "namespace": "open-p", "hash": -315994173, "terms": ["What is the required type?", "is the required type?", "the required type?", "required type?", "type?"], "tokens": ["What", "is", "the", "required", "type", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1011281587", "fields": {"term": "What is mode?", "namespace": "open-p", "hash": 1011281587, "terms": ["What is mode?", "is mode?", "mode?"], "tokens": ["What", "is", "mode", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::422602809", "fields": {"term": "What is selection?", "namespace": "open-p", "hash": 422602809, "terms": ["What is selection?", "is selection?", "selection?"], "tokens": ["What", "is", "selection", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-854947246", "fields": {"term": "What is global?", "namespace": "open-p", "hash": -854947246, "terms": ["What is global?", "is global?", "global?"], "tokens": ["What", "is", "global", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1922793873", "fields": {"term": "What is the default mode?", "namespace": "open-p", "hash": -1922793873, "terms": ["What is the default mode?", "is the default mode?", "the default mode?", "default mode?", "mode?"], "tokens": ["What", "is", "the", "default", "mode", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2145817991", "fields": {"term": "What is the validation override for changing mode?", "namespace": "open-p", "hash": 2145817991, "terms": ["What is the validation override for changing mode?", "is the validation override for changing mode?", "the validation override for changing mode?", "validation override for changing mode?", "override for changing mode?", "for changing mode?", "changing mode?", "mode?"], "tokens": ["What", "is", "the", "validation", "override", "for", "changing", "mode", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1207961112", "fields": {"term": "What is the validation override for changing global?", "namespace": "open-p", "hash": -1207961112, "terms": ["What is the validation override for changing global?", "is the validation override for changing global?", "the validation override for changing global?", "validation override for changing global?", "override for changing global?", "for changing global?", "changing global?", "global?"], "tokens": ["What", "is", "the", "validation", "override", "for", "changing", "global", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::632540803", "fields": {"term": "Can selection apply to fields from other types?", "namespace": "open-p", "hash": 632540803, "terms": ["Can selection apply to fields from other types?", "selection apply to fields from other types?", "apply to fields from other types?", "to fields from other types?", "fields from other types?", "from other types?", "other types?", "types?"], "tokens": ["Can", "selection", "apply", "to", "fields", "from", "other", "types", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1122568021", "fields": {"term": "What is document preprocessing?", "namespace": "open-p", "hash": 1122568021, "terms": ["What is document preprocessing?", "is document preprocessing?", "document preprocessing?", "preprocessing?"], "tokens": ["What", "is", "document", "preprocessing", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-904115821", "fields": {"term": "What is documentprocessor?", "namespace": "open-p", "hash": -904115821, "terms": ["What is documentprocessor?", "is documentprocessor?", "documentprocessor?"], "tokens": ["What", "is", "documentprocessor", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::219834048", "fields": {"term": "What is before=indexingStart?", "namespace": "open-p", "hash": 219834048, "terms": ["What is before=indexingStart?", "is before=indexingStart?", "before=indexingStart?"], "tokens": ["What", "is", "before", "=", "indexingStart", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1783386325", "fields": {"term": "What is after=indexingEnd?", "namespace": "open-p", "hash": -1783386325, "terms": ["What is after=indexingEnd?", "is after=indexingEnd?", "after=indexingEnd?"], "tokens": ["What", "is", "after", "=", "indexingEnd", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1834557281", "fields": {"term": "What is 'minimum total data'?", "namespace": "open-p", "hash": 1834557281, "terms": ["What is 'minimum total data'?", "is 'minimum total data'?", "'minimum total data'?", "total data'?", "data'?"], "tokens": ["What", "is", "'", "minimum", "total", "data", "'?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::227496604", "fields": {"term": "What is redundancy?", "namespace": "open-p", "hash": 227496604, "terms": ["What is redundancy?", "is redundancy?", "redundancy?"], "tokens": ["What", "is", "redundancy", "?"], "corpus_count": 4, "document_count": 4}}, -{"put": "id:term:term::-382752964", "fields": {"term": "When is 'minimum total data' used?", "namespace": "open-p", "hash": -382752964, "terms": ["When is 'minimum total data' used?", "is 'minimum total data' used?", "'minimum total data' used?", "total data' used?", "data' used?", "used?"], "tokens": ["When", "is", "'", "minimum", "total", "data", "'", "used", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1694921881", "fields": {"term": "How does 'minimum total data' ensure data copies?", "namespace": "open-p", "hash": -1694921881, "terms": ["How does 'minimum total data' ensure data copies?", "does 'minimum total data' ensure data copies?", "'minimum total data' ensure data copies?", "total data' ensure data copies?", "data' ensure data copies?", "ensure data copies?", "data copies?", "copies?"], "tokens": ["How", "does", "'", "minimum", "total", "data", "'", "ensure", "data", "copies", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2046077840", "fields": {"term": "How does redundancy work in Vespa?", "namespace": "open-p", "hash": 2046077840, "terms": ["How does redundancy work in Vespa?", "does redundancy work in Vespa?", "redundancy work in Vespa?", "work in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["How", "does", "redundancy", "work", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1700828548", "fields": {"term": "Can redundancy be changed without node restart?", "namespace": "open-p", "hash": 1700828548, "terms": ["Can redundancy be changed without node restart?", "redundancy be changed without node restart?", "be changed without node restart?", "changed without node restart?", "without node restart?", "node restart?", "restart?"], "tokens": ["Can", "redundancy", "be", "changed", "without", "node", "restart", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1231719596", "fields": {"term": "What is a distribution key?", "namespace": "open-p", "hash": 1231719596, "terms": ["What is a distribution key?", "is a distribution key?", "a distribution key?", "distribution key?", "key?"], "tokens": ["What", "is", "a", "distribution", "key", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-222469740", "fields": {"term": "Are distribution keys required?", "namespace": "open-p", "hash": -222469740, "terms": ["Are distribution keys required?", "distribution keys required?", "keys required?", "required?"], "tokens": ["Are", "distribution", "keys", "required", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::313888246", "fields": {"term": "Can a node change distribution key?", "namespace": "open-p", "hash": 313888246, "terms": ["Can a node change distribution key?", "a node change distribution key?", "node change distribution key?", "change distribution key?", "distribution key?", "key?"], "tokens": ["Can", "a", "node", "change", "distribution", "key", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1992615896", "fields": {"term": "What happens when a node changes distribution key?", "namespace": "open-p", "hash": 1992615896, "terms": ["What happens when a node changes distribution key?", "happens when a node changes distribution key?", "when a node changes distribution key?", "a node changes distribution key?", "node changes distribution key?", "changes distribution key?", "distribution key?", "key?"], "tokens": ["What", "happens", "when", "a", "node", "changes", "distribution", "key", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1413822980", "fields": {"term": "What is the capacity of a node?", "namespace": "open-p", "hash": 1413822980, "terms": ["What is the capacity of a node?", "is the capacity of a node?", "the capacity of a node?", "capacity of a node?", "of a node?", "a node?", "node?"], "tokens": ["What", "is", "the", "capacity", "of", "a", "node", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-465450833", "fields": {"term": "Is adjusting capacity for expert users only?", "namespace": "open-p", "hash": -465450833, "terms": ["Is adjusting capacity for expert users only?", "adjusting capacity for expert users only?", "capacity for expert users only?", "for expert users only?", "expert users only?", "users only?", "only?"], "tokens": ["Is", "adjusting", "capacity", "for", "expert", "users", "only", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-746843321", "fields": {"term": "What is a group in Vespa?", "namespace": "open-p", "hash": -746843321, "terms": ["What is a group in Vespa?", "is a group in Vespa?", "a group in Vespa?", "group in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["What", "is", "a", "group", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1150750643", "fields": {"term": "What is group distribution key?", "namespace": "open-p", "hash": 1150750643, "terms": ["What is group distribution key?", "is group distribution key?", "group distribution key?", "distribution key?", "key?"], "tokens": ["What", "is", "group", "distribution", "key", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1009656406", "fields": {"term": "What's the difference between Vespa Cloud and Open Source Vespa when using groups?", "namespace": "open-p", "hash": -1009656406, "terms": ["What's the difference between Vespa Cloud and Open Source Vespa when using groups?", "the difference between Vespa Cloud and Open Source Vespa when using groups?", "difference between Vespa Cloud and Open Source Vespa when using groups?", "between Vespa Cloud and Open Source Vespa when using groups?", "Vespa Cloud and Open Source Vespa when using groups?", "Cloud and Open Source Vespa when using groups?", "and Open Source Vespa when using groups?", "Open Source Vespa when using groups?", "Source Vespa when using groups?", "Vespa when using groups?", "when using groups?", "using groups?", "groups?"], "tokens": ["What", "'", "s", "the", "difference", "between", "Vespa", "Cloud", "and", "Open", "Source", "Vespa", "when", "using", "groups", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-371404168", "fields": {"term": "What is the purpose of the 'name' attribute in a group?", "namespace": "open-p", "hash": -371404168, "terms": ["What is the purpose of the 'name' attribute in a group?", "is the purpose of the 'name' attribute in a group?", "the purpose of the 'name' attribute in a group?", "purpose of the 'name' attribute in a group?", "of the 'name' attribute in a group?", "the 'name' attribute in a group?", "'name' attribute in a group?", "attribute in a group?", "in a group?", "a group?", "group?"], "tokens": ["What", "is", "the", "purpose", "of", "the", "'", "name", "'", "attribute", "in", "a", "group", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1396167104", "fields": {"term": "What is the maximum replica number when using groups in Vespa Cloud?", "namespace": "open-p", "hash": -1396167104, "terms": ["What is the maximum replica number when using groups in Vespa Cloud?", "is the maximum replica number when using groups in Vespa Cloud?", "the maximum replica number when using groups in Vespa Cloud?", "maximum replica number when using groups in Vespa Cloud?", "replica number when using groups in Vespa Cloud?", "number when using groups in Vespa Cloud?", "when using groups in Vespa Cloud?", "using groups in Vespa Cloud?", "groups in Vespa Cloud?", "in Vespa Cloud?", "Vespa Cloud?", "Cloud?"], "tokens": ["What", "is", "the", "maximum", "replica", "number", "when", "using", "groups", "in", "Vespa", "Cloud", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1698669800", "fields": {"term": "Can a group contain both subgroups and nodes?", "namespace": "open-p", "hash": -1698669800, "terms": ["Can a group contain both subgroups and nodes?", "a group contain both subgroups and nodes?", "group contain both subgroups and nodes?", "contain both subgroups and nodes?", "both subgroups and nodes?", "subgroups and nodes?", "and nodes?", "nodes?"], "tokens": ["Can", "a", "group", "contain", "both", "subgroups", "and", "nodes", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::706270273", "fields": {"term": "What are the benefits of using grouped distribution in Vespa?", "namespace": "open-p", "hash": 706270273, "terms": ["What are the benefits of using grouped distribution in Vespa?", "are the benefits of using grouped distribution in Vespa?", "the benefits of using grouped distribution in Vespa?", "benefits of using grouped distribution in Vespa?", "of using grouped distribution in Vespa?", "using grouped distribution in Vespa?", "grouped distribution in Vespa?", "distribution in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["What", "are", "the", "benefits", "of", "using", "grouped", "distribution", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1092188074", "fields": {"term": "Where can I find examples of Vespa deployments using groups?", "namespace": "open-p", "hash": -1092188074, "terms": ["Where can I find examples of Vespa deployments using groups?", "can I find examples of Vespa deployments using groups?", "I find examples of Vespa deployments using groups?", "find examples of Vespa deployments using groups?", "examples of Vespa deployments using groups?", "of Vespa deployments using groups?", "Vespa deployments using groups?", "deployments using groups?", "using groups?", "groups?"], "tokens": ["Where", "can", "I", "find", "examples", "of", "Vespa", "deployments", "using", "groups", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1527437714", "fields": {"term": "What is distribution in Vespa?", "namespace": "open-p", "hash": 1527437714, "terms": ["What is distribution in Vespa?", "is distribution in Vespa?", "distribution in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["What", "is", "distribution", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1223513017", "fields": {"term": "When is distribution mandatory?", "namespace": "open-p", "hash": 1223513017, "terms": ["When is distribution mandatory?", "is distribution mandatory?", "distribution mandatory?", "mandatory?"], "tokens": ["When", "is", "distribution", "mandatory", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1835606407", "fields": {"term": "What is the partition specification?", "namespace": "open-p", "hash": -1835606407, "terms": ["What is the partition specification?", "is the partition specification?", "the partition specification?", "partition specification?", "specification?"], "tokens": ["What", "is", "the", "partition", "specification", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-159527534", "fields": {"term": "What is the content engine?", "namespace": "open-p", "hash": -159527534, "terms": ["What is the content engine?", "is the content engine?", "the content engine?", "content engine?", "engine?"], "tokens": ["What", "is", "the", "content", "engine", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::429321038", "fields": {"term": "What are the allowed engines?", "namespace": "open-p", "hash": 429321038, "terms": ["What are the allowed engines?", "are the allowed engines?", "the allowed engines?", "allowed engines?", "engines?"], "tokens": ["What", "are", "the", "allowed", "engines", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1435141249", "fields": {"term": "What is the sub-element?", "namespace": "open-p", "hash": -1435141249, "terms": ["What is the sub-element?", "is the sub-element?", "the sub-element?", "sub-element?"], "tokens": ["What", "is", "the", "sub", "element", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-161092045", "fields": {"term": "How do you specify the content engine?", "namespace": "open-p", "hash": -161092045, "terms": ["How do you specify the content engine?", "do you specify the content engine?", "you specify the content engine?", "specify the content engine?", "the content engine?", "content engine?", "engine?"], "tokens": ["How", "do", "you", "specify", "the", "content", "engine", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::347701344", "fields": {"term": "What is Proton engine?", "namespace": "open-p", "hash": 347701344, "terms": ["What is Proton engine?", "is Proton engine?", "Proton engine?", "engine?"], "tokens": ["What", "is", "Proton", "engine", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1382695092", "fields": {"term": "What are the optional sub-elements?", "namespace": "open-p", "hash": -1382695092, "terms": ["What are the optional sub-elements?", "are the optional sub-elements?", "the optional sub-elements?", "optional sub-elements?", "sub-elements?"], "tokens": ["What", "are", "the", "optional", "sub", "elements", "?"], "corpus_count": 3, "document_count": 3}}, -{"put": "id:term:term::-1821812564", "fields": {"term": "What is searchable-copies?", "namespace": "open-p", "hash": -1821812564, "terms": ["What is searchable-copies?", "is searchable-copies?", "searchable-copies?"], "tokens": ["What", "is", "searchable", "copies", "?"], "corpus_count": 3, "document_count": 3}}, -{"put": "id:term:term::-529254218", "fields": {"term": "When to consider fast-access?", "namespace": "open-p", "hash": -529254218, "terms": ["When to consider fast-access?", "to consider fast-access?", "consider fast-access?", "fast-access?"], "tokens": ["When", "to", "consider", "fast", "access", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2139185197", "fields": {"term": "What is the purpose of thread configuration?", "namespace": "open-p", "hash": -2139185197, "terms": ["What is the purpose of thread configuration?", "is the purpose of thread configuration?", "the purpose of thread configuration?", "purpose of thread configuration?", "of thread configuration?", "thread configuration?", "configuration?"], "tokens": ["What", "is", "the", "purpose", "of", "thread", "configuration", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1394894002", "fields": {"term": "What is persearch?", "namespace": "open-p", "hash": 1394894002, "terms": ["What is persearch?", "is persearch?", "persearch?"], "tokens": ["What", "is", "persearch", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::-1397185073", "fields": {"term": "What is the default number of search threads?", "namespace": "open-p", "hash": -1397185073, "terms": ["What is the default number of search threads?", "is the default number of search threads?", "the default number of search threads?", "default number of search threads?", "number of search threads?", "of search threads?", "search threads?", "threads?"], "tokens": ["What", "is", "the", "default", "number", "of", "search", "threads", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1958724681", "fields": {"term": "What is flushstrategy?", "namespace": "open-p", "hash": -1958724681, "terms": ["What is flushstrategy?", "is flushstrategy?", "flushstrategy?"], "tokens": ["What", "is", "flushstrategy", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::928571120", "fields": {"term": "What is memory_limit-factor?", "namespace": "open-p", "hash": 928571120, "terms": ["What is memory_limit-factor?", "is memory_limit-factor?", "memory_limit-factor?"], "tokens": ["What", "is", "memory_limit", "factor", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1616520270", "fields": {"term": "What is diskbloatfactor?", "namespace": "open-p", "hash": 1616520270, "terms": ["What is diskbloatfactor?", "is diskbloatfactor?", "diskbloatfactor?"], "tokens": ["What", "is", "diskbloatfactor", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1047923451", "fields": {"term": "What is transactionlog.maxsize?", "namespace": "open-p", "hash": 1047923451, "terms": ["What is transactionlog.maxsize?", "is transactionlog.maxsize?", "transactionlog.maxsize?"], "tokens": ["What", "is", "transactionlog", "maxsize", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::776546709", "fields": {"term": "What is the flushstrategy sub element?", "namespace": "open-p", "hash": 776546709, "terms": ["What is the flushstrategy sub element?", "is the flushstrategy sub element?", "the flushstrategy sub element?", "flushstrategy sub element?", "sub element?", "element?"], "tokens": ["What", "is", "the", "flushstrategy", "sub", "element", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-997842151", "fields": {"term": "What is downscaled in flushstrategy?", "namespace": "open-p", "hash": -997842151, "terms": ["What is downscaled in flushstrategy?", "is downscaled in flushstrategy?", "downscaled in flushstrategy?", "in flushstrategy?", "flushstrategy?"], "tokens": ["What", "is", "downscaled", "in", "flushstrategy", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::790436207", "fields": {"term": "What is the maximum memory gain in flushstrategy?", "namespace": "open-p", "hash": 790436207, "terms": ["What is the maximum memory gain in flushstrategy?", "is the maximum memory gain in flushstrategy?", "the maximum memory gain in flushstrategy?", "maximum memory gain in flushstrategy?", "memory gain in flushstrategy?", "gain in flushstrategy?", "in flushstrategy?", "flushstrategy?"], "tokens": ["What", "is", "the", "maximum", "memory", "gain", "in", "flushstrategy", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1602784714", "fields": {"term": "What is the max flush age in flushstrategy?", "namespace": "open-p", "hash": 1602784714, "terms": ["What is the max flush age in flushstrategy?", "is the max flush age in flushstrategy?", "the max flush age in flushstrategy?", "max flush age in flushstrategy?", "flush age in flushstrategy?", "age in flushstrategy?", "in flushstrategy?", "flushstrategy?"], "tokens": ["What", "is", "the", "max", "flush", "age", "in", "flushstrategy", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1806228643", "fields": {"term": "What is initialdocumentcount?", "namespace": "open-p", "hash": -1806228643, "terms": ["What is initialdocumentcount?", "is initialdocumentcount?", "initialdocumentcount?"], "tokens": ["What", "is", "initialdocumentcount", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-135838912", "fields": {"term": "What is amortize-count?", "namespace": "open-p", "hash": -135838912, "terms": ["What is amortize-count?", "is amortize-count?", "amortize-count?"], "tokens": ["What", "is", "amortize", "count", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::46021399", "fields": {"term": "What is searchnode?", "namespace": "open-p", "hash": 46021399, "terms": ["What is searchnode?", "is searchnode?", "searchnode?"], "tokens": ["What", "is", "searchnode", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::1982708491", "fields": {"term": "What is the default concurrency value in feeding?", "namespace": "open-p", "hash": 1982708491, "terms": ["What is the default concurrency value in feeding?", "is the default concurrency value in feeding?", "the default concurrency value in feeding?", "default concurrency value in feeding?", "concurrency value in feeding?", "value in feeding?", "in feeding?", "feeding?"], "tokens": ["What", "is", "the", "default", "concurrency", "value", "in", "feeding", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-460060793", "fields": {"term": "What is feeding.concurrency?", "namespace": "open-p", "hash": -460060793, "terms": ["What is feeding.concurrency?", "is feeding.concurrency?", "feeding.concurrency?"], "tokens": ["What", "is", "feeding", "concurrency", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1729171789", "fields": {"term": "How does 'concurrency' affect thread pools in feed operations?", "namespace": "open-p", "hash": 1729171789, "terms": ["How does 'concurrency' affect thread pools in feed operations?", "does 'concurrency' affect thread pools in feed operations?", "'concurrency' affect thread pools in feed operations?", "affect thread pools in feed operations?", "thread pools in feed operations?", "pools in feed operations?", "in feed operations?", "feed operations?", "operations?"], "tokens": ["How", "does", "'", "concurrency", "'", "affect", "thread", "pools", "in", "feed", "operations", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2083231123", "fields": {"term": "What is io write?", "namespace": "open-p", "hash": -2083231123, "terms": ["What is io write?", "is io write?", "io write?", "write?"], "tokens": ["What", "is", "io", "write", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-472780331", "fields": {"term": "What is io read?", "namespace": "open-p", "hash": -472780331, "terms": ["What is io read?", "is io read?", "io read?", "read?"], "tokens": ["What", "is", "io", "read", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::532518139", "fields": {"term": "What is the purpose of warmup?", "namespace": "open-p", "hash": 532518139, "terms": ["What is the purpose of warmup?", "is the purpose of warmup?", "the purpose of warmup?", "purpose of warmup?", "of warmup?", "warmup?"], "tokens": ["What", "is", "the", "purpose", "of", "warmup", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-402465757", "fields": {"term": "How can warmup be controlled?", "namespace": "open-p", "hash": -402465757, "terms": ["How can warmup be controlled?", "can warmup be controlled?", "warmup be controlled?", "be controlled?", "controlled?"], "tokens": ["How", "can", "warmup", "be", "controlled", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1952740640", "fields": {"term": "What are the values for io search?", "namespace": "open-p", "hash": 1952740640, "terms": ["What are the values for io search?", "are the values for io search?", "the values for io search?", "values for io search?", "for io search?", "io search?", "search?"], "tokens": ["What", "are", "the", "values", "for", "io", "search", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1177411062", "fields": {"term": "What is the default for io read?", "namespace": "open-p", "hash": -1177411062, "terms": ["What is the default for io read?", "is the default for io read?", "the default for io read?", "default for io read?", "for io read?", "io read?", "read?"], "tokens": ["What", "is", "the", "default", "for", "io", "read", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::567857387", "fields": {"term": "What is the default for unpack?", "namespace": "open-p", "hash": 567857387, "terms": ["What is the default for unpack?", "is the default for unpack?", "the default for unpack?", "default for unpack?", "for unpack?", "unpack?"], "tokens": ["What", "is", "the", "default", "for", "unpack", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::254206746", "fields": {"term": "What is 'searchnode'?", "namespace": "open-p", "hash": 254206746, "terms": ["What is 'searchnode'?", "is 'searchnode'?", "'searchnode'?"], "tokens": ["What", "is", "'", "searchnode", "'?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1589370546", "fields": {"term": "What is 'attribute vectors'?", "namespace": "open-p", "hash": -1589370546, "terms": ["What is 'attribute vectors'?", "is 'attribute vectors'?", "'attribute vectors'?", "vectors'?"], "tokens": ["What", "is", "'", "attribute", "vectors", "'?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2122130491", "fields": {"term": "What is the default 'write' value?", "namespace": "open-p", "hash": -2122130491, "terms": ["What is the default 'write' value?", "is the default 'write' value?", "the default 'write' value?", "default 'write' value?", "'write' value?", "value?"], "tokens": ["What", "is", "the", "default", "'", "write", "'", "value", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::535851941", "fields": {"term": "What is the purpose of 'removed-db'?", "namespace": "open-p", "hash": 535851941, "terms": ["What is the purpose of 'removed-db'?", "is the purpose of 'removed-db'?", "the purpose of 'removed-db'?", "purpose of 'removed-db'?", "of 'removed-db'?", "'removed-db'?"], "tokens": ["What", "is", "the", "purpose", "of", "'", "removed", "db", "'?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-742627671", "fields": {"term": "What is 'prune' in 'removed-db'?", "namespace": "open-p", "hash": -742627671, "terms": ["What is 'prune' in 'removed-db'?", "is 'prune' in 'removed-db'?", "'prune' in 'removed-db'?", "in 'removed-db'?", "'removed-db'?"], "tokens": ["What", "is", "'", "prune", "'", "in", "'", "removed", "db", "'?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1298037215", "fields": {"term": "What is 'age' in 'prune'?", "namespace": "open-p", "hash": -1298037215, "terms": ["What is 'age' in 'prune'?", "is 'age' in 'prune'?", "'age' in 'prune'?", "in 'prune'?", "'prune'?"], "tokens": ["What", "is", "'", "age", "'", "in", "'", "prune", "'?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-911026461", "fields": {"term": "What is the default value of 'age'?", "namespace": "open-p", "hash": -911026461, "terms": ["What is the default value of 'age'?", "is the default value of 'age'?", "the default value of 'age'?", "default value of 'age'?", "value of 'age'?", "of 'age'?", "'age'?"], "tokens": ["What", "is", "the", "default", "value", "of", "'", "age", "'?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1120405416", "fields": {"term": "Why is 'age' set this way?", "namespace": "open-p", "hash": 1120405416, "terms": ["Why is 'age' set this way?", "is 'age' set this way?", "'age' set this way?", "set this way?", "this way?", "way?"], "tokens": ["Why", "is", "'", "age", "'", "set", "this", "way", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-866850698", "fields": {"term": "What is 'interval' in 'prune'?", "namespace": "open-p", "hash": -866850698, "terms": ["What is 'interval' in 'prune'?", "is 'interval' in 'prune'?", "'interval' in 'prune'?", "in 'prune'?", "'prune'?"], "tokens": ["What", "is", "'", "interval", "'", "in", "'", "prune", "'?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1224889540", "fields": {"term": "What is the default value of 'interval'?", "namespace": "open-p", "hash": -1224889540, "terms": ["What is the default value of 'interval'?", "is the default value of 'interval'?", "the default value of 'interval'?", "default value of 'interval'?", "value of 'interval'?", "of 'interval'?", "'interval'?"], "tokens": ["What", "is", "the", "default", "value", "of", "'", "interval", "'?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1167758754", "fields": {"term": "Where can 'removed-db' be contained in?", "namespace": "open-p", "hash": -1167758754, "terms": ["Where can 'removed-db' be contained in?", "can 'removed-db' be contained in?", "'removed-db' be contained in?", "be contained in?", "contained in?", "in?"], "tokens": ["Where", "can", "'", "removed", "db", "'", "be", "contained", "in", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-387761573", "fields": {"term": "What is document summary?", "namespace": "open-p", "hash": -387761573, "terms": ["What is document summary?", "is document summary?", "document summary?", "summary?"], "tokens": ["What", "is", "document", "summary", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1020693964", "fields": {"term": "What are the options for io write?", "namespace": "open-p", "hash": 1020693964, "terms": ["What are the options for io write?", "are the options for io write?", "the options for io write?", "options for io write?", "for io write?", "io write?", "write?"], "tokens": ["What", "are", "the", "options", "for", "io", "write", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1968250112", "fields": {"term": "What are the options for io read?", "namespace": "open-p", "hash": 1968250112, "terms": ["What are the options for io read?", "are the options for io read?", "the options for io read?", "options for io read?", "for io read?", "io read?", "read?"], "tokens": ["What", "are", "the", "options", "for", "io", "read", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-550905885", "fields": {"term": "What is summary store?", "namespace": "open-p", "hash": -550905885, "terms": ["What is summary store?", "is summary store?", "summary store?", "store?"], "tokens": ["What", "is", "summary", "store", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1451787795", "fields": {"term": "What is document logstore?", "namespace": "open-p", "hash": 1451787795, "terms": ["What is document logstore?", "is document logstore?", "document logstore?", "logstore?"], "tokens": ["What", "is", "document", "logstore", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::753292495", "fields": {"term": "What is the maximum size of a chunk in document logstore?", "namespace": "open-p", "hash": 753292495, "terms": ["What is the maximum size of a chunk in document logstore?", "is the maximum size of a chunk in document logstore?", "the maximum size of a chunk in document logstore?", "maximum size of a chunk in document logstore?", "size of a chunk in document logstore?", "of a chunk in document logstore?", "a chunk in document logstore?", "chunk in document logstore?", "in document logstore?", "document logstore?", "logstore?"], "tokens": ["What", "is", "the", "maximum", "size", "of", "a", "chunk", "in", "document", "logstore", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1594181118", "fields": {"term": "What is the compression level of documents in document logstore?", "namespace": "open-p", "hash": -1594181118, "terms": ["What is the compression level of documents in document logstore?", "is the compression level of documents in document logstore?", "the compression level of documents in document logstore?", "compression level of documents in document logstore?", "level of documents in document logstore?", "of documents in document logstore?", "documents in document logstore?", "in document logstore?", "document logstore?", "logstore?"], "tokens": ["What", "is", "the", "compression", "level", "of", "documents", "in", "document", "logstore", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::142926355", "fields": {"term": "What is flushed before shutdown?", "namespace": "open-p", "hash": 142926355, "terms": ["What is flushed before shutdown?", "is flushed before shutdown?", "flushed before shutdown?", "before shutdown?", "shutdown?"], "tokens": ["What", "is", "flushed", "before", "shutdown", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1847181260", "fields": {"term": "How does flushing reduce replay time?", "namespace": "open-p", "hash": 1847181260, "terms": ["How does flushing reduce replay time?", "does flushing reduce replay time?", "flushing reduce replay time?", "reduce replay time?", "replay time?", "time?"], "tokens": ["How", "does", "flushing", "reduce", "replay", "time", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::331893319", "fields": {"term": "What is transactionlog?", "namespace": "open-p", "hash": 331893319, "terms": ["What is transactionlog?", "is transactionlog?", "transactionlog?"], "tokens": ["What", "is", "transactionlog", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1581475257", "fields": {"term": "What are the advantages of syncing transactionlog to disk?", "namespace": "open-p", "hash": 1581475257, "terms": ["What are the advantages of syncing transactionlog to disk?", "are the advantages of syncing transactionlog to disk?", "the advantages of syncing transactionlog to disk?", "advantages of syncing transactionlog to disk?", "of syncing transactionlog to disk?", "syncing transactionlog to disk?", "transactionlog to disk?", "to disk?", "disk?"], "tokens": ["What", "are", "the", "advantages", "of", "syncing", "transactionlog", "to", "disk", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1830617885", "fields": {"term": "What is the impact of using NAS?", "namespace": "open-p", "hash": 1830617885, "terms": ["What is the impact of using NAS?", "is the impact of using NAS?", "the impact of using NAS?", "impact of using NAS?", "of using NAS?", "using NAS?", "NAS?"], "tokens": ["What", "is", "the", "impact", "of", "using", "NAS", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1581348021", "fields": {"term": "What is EBS?", "namespace": "open-p", "hash": 1581348021, "terms": ["What is EBS?", "is EBS?", "EBS?"], "tokens": ["What", "is", "EBS", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2076918379", "fields": {"term": "How much improvement was seen by turning off sync-transactionlog for EBS?", "namespace": "open-p", "hash": -2076918379, "terms": ["How much improvement was seen by turning off sync-transactionlog for EBS?", "much improvement was seen by turning off sync-transactionlog for EBS?", "improvement was seen by turning off sync-transactionlog for EBS?", "was seen by turning off sync-transactionlog for EBS?", "seen by turning off sync-transactionlog for EBS?", "by turning off sync-transactionlog for EBS?", "turning off sync-transactionlog for EBS?", "off sync-transactionlog for EBS?", "sync-transactionlog for EBS?", "for EBS?", "EBS?"], "tokens": ["How", "much", "improvement", "was", "seen", "by", "turning", "off", "sync", "transactionlog", "for", "EBS", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-387553232", "fields": {"term": "What are proton limits?", "namespace": "open-p", "hash": -387553232, "terms": ["What are proton limits?", "are proton limits?", "proton limits?", "limits?"], "tokens": ["What", "are", "proton", "limits", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-928158816", "fields": {"term": "How does proton set limits?", "namespace": "open-p", "hash": -928158816, "terms": ["How does proton set limits?", "does proton set limits?", "proton set limits?", "set limits?", "limits?"], "tokens": ["How", "does", "proton", "set", "limits", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::932631218", "fields": {"term": "What is disk in proton?", "namespace": "open-p", "hash": 932631218, "terms": ["What is disk in proton?", "is disk in proton?", "disk in proton?", "in proton?", "proton?"], "tokens": ["What", "is", "disk", "in", "proton", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1377081946", "fields": {"term": "What is memory in proton?", "namespace": "open-p", "hash": -1377081946, "terms": ["What is memory in proton?", "is memory in proton?", "memory in proton?", "in proton?", "proton?"], "tokens": ["What", "is", "memory", "in", "proton", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-467829115", "fields": {"term": "What are sub-elements?", "namespace": "open-p", "hash": -467829115, "terms": ["What are sub-elements?", "are sub-elements?", "sub-elements?"], "tokens": ["What", "are", "sub", "elements", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1175648155", "fields": {"term": "What is the default query timeout?", "namespace": "open-p", "hash": 1175648155, "terms": ["What is the default query timeout?", "is the default query timeout?", "the default query timeout?", "default query timeout?", "query timeout?", "timeout?"], "tokens": ["What", "is", "the", "default", "query", "timeout", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::133777924", "fields": {"term": "What is the max query timeout?", "namespace": "open-p", "hash": 133777924, "terms": ["What is the max query timeout?", "is the max query timeout?", "the max query timeout?", "max query timeout?", "query timeout?", "timeout?"], "tokens": ["What", "is", "the", "max", "query", "timeout", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-89487969", "fields": {"term": "What is the search interface?", "namespace": "open-p", "hash": -89487969, "terms": ["What is the search interface?", "is the search interface?", "the search interface?", "search interface?", "interface?"], "tokens": ["What", "is", "the", "search", "interface", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::35741383", "fields": {"term": "Where can I find the request parameter for query timeout?", "namespace": "open-p", "hash": 35741383, "terms": ["Where can I find the request parameter for query timeout?", "can I find the request parameter for query timeout?", "I find the request parameter for query timeout?", "find the request parameter for query timeout?", "the request parameter for query timeout?", "request parameter for query timeout?", "parameter for query timeout?", "for query timeout?", "query timeout?", "timeout?"], "tokens": ["Where", "can", "I", "find", "the", "request", "parameter", "for", "query", "timeout", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1770167221", "fields": {"term": "What is the unit of the max timeout value?", "namespace": "open-p", "hash": -1770167221, "terms": ["What is the unit of the max timeout value?", "is the unit of the max timeout value?", "the unit of the max timeout value?", "unit of the max timeout value?", "of the max timeout value?", "the max timeout value?", "max timeout value?", "timeout value?", "value?"], "tokens": ["What", "is", "the", "unit", "of", "the", "max", "timeout", "value", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2133360729", "fields": {"term": "What happens when the query timeout is exceeded?", "namespace": "open-p", "hash": 2133360729, "terms": ["What happens when the query timeout is exceeded?", "happens when the query timeout is exceeded?", "when the query timeout is exceeded?", "the query timeout is exceeded?", "query timeout is exceeded?", "timeout is exceeded?", "is exceeded?", "exceeded?"], "tokens": ["What", "happens", "when", "the", "query", "timeout", "is", "exceeded", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1742877407", "fields": {"term": "What is the purpose of the query timeout?", "namespace": "open-p", "hash": -1742877407, "terms": ["What is the purpose of the query timeout?", "is the purpose of the query timeout?", "the purpose of the query timeout?", "purpose of the query timeout?", "of the query timeout?", "the query timeout?", "query timeout?", "timeout?"], "tokens": ["What", "is", "the", "purpose", "of", "the", "query", "timeout", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-824593088", "fields": {"term": "What is the timeout in seconds for queries against search interface?", "namespace": "open-p", "hash": -824593088, "terms": ["What is the timeout in seconds for queries against search interface?", "is the timeout in seconds for queries against search interface?", "the timeout in seconds for queries against search interface?", "timeout in seconds for queries against search interface?", "in seconds for queries against search interface?", "seconds for queries against search interface?", "for queries against search interface?", "queries against search interface?", "against search interface?", "search interface?", "interface?"], "tokens": ["What", "is", "the", "timeout", "in", "seconds", "for", "queries", "against", "search", "interface", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1014500736", "fields": {"term": "What is the TTL caching control?", "namespace": "open-p", "hash": -1014500736, "terms": ["What is the TTL caching control?", "is the TTL caching control?", "the TTL caching control?", "TTL caching control?", "caching control?", "control?"], "tokens": ["What", "is", "the", "TTL", "caching", "control", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1355878005", "fields": {"term": "How does search affect TTL?", "namespace": "open-p", "hash": 1355878005, "terms": ["How does search affect TTL?", "does search affect TTL?", "search affect TTL?", "affect TTL?", "TTL?"], "tokens": ["How", "does", "search", "affect", "TTL", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1806461976", "fields": {"term": "What is the max TTL?", "namespace": "open-p", "hash": -1806461976, "terms": ["What is the max TTL?", "is the max TTL?", "the max TTL?", "max TTL?", "TTL?"], "tokens": ["What", "is", "the", "max", "TTL", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1454247275", "fields": {"term": "What is search coverage configuration?", "namespace": "open-p", "hash": -1454247275, "terms": ["What is search coverage configuration?", "is search coverage configuration?", "search coverage configuration?", "coverage configuration?", "configuration?"], "tokens": ["What", "is", "search", "coverage", "configuration", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-392429867", "fields": {"term": "How does search coverage affect search performance?", "namespace": "open-p", "hash": -392429867, "terms": ["How does search coverage affect search performance?", "does search coverage affect search performance?", "search coverage affect search performance?", "coverage affect search performance?", "affect search performance?", "search performance?", "performance?"], "tokens": ["How", "does", "search", "coverage", "affect", "search", "performance", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::492384377", "fields": {"term": "What is minimum search coverage?", "namespace": "open-p", "hash": 492384377, "terms": ["What is minimum search coverage?", "is minimum search coverage?", "minimum search coverage?", "search coverage?", "coverage?"], "tokens": ["What", "is", "minimum", "search", "coverage", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1571160349", "fields": {"term": "What is the default coverage value?", "namespace": "open-p", "hash": -1571160349, "terms": ["What is the default coverage value?", "is the default coverage value?", "the default coverage value?", "default coverage value?", "coverage value?", "value?"], "tokens": ["What", "is", "the", "default", "coverage", "value", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1517131064", "fields": {"term": "What is the default coverage factor?", "namespace": "open-p", "hash": -1517131064, "terms": ["What is the default coverage factor?", "is the default coverage factor?", "the default coverage factor?", "default coverage factor?", "coverage factor?", "factor?"], "tokens": ["What", "is", "the", "default", "coverage", "factor", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1477224737", "fields": {"term": "What happens after reaching minimum coverage?", "namespace": "open-p", "hash": -1477224737, "terms": ["What happens after reaching minimum coverage?", "happens after reaching minimum coverage?", "after reaching minimum coverage?", "reaching minimum coverage?", "minimum coverage?", "coverage?"], "tokens": ["What", "happens", "after", "reaching", "minimum", "coverage", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1484951425", "fields": {"term": "What are maintenance tuning parameters?", "namespace": "open-p", "hash": 1484951425, "terms": ["What are maintenance tuning parameters?", "are maintenance tuning parameters?", "maintenance tuning parameters?", "tuning parameters?", "parameters?"], "tokens": ["What", "are", "maintenance", "tuning", "parameters", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1331182322", "fields": {"term": "What is dispatch tuning?", "namespace": "open-p", "hash": -1331182322, "terms": ["What is dispatch tuning?", "is dispatch tuning?", "dispatch tuning?", "tuning?"], "tokens": ["What", "is", "dispatch", "tuning", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2066135482", "fields": {"term": "What are the optional tuning parameters?", "namespace": "open-p", "hash": 2066135482, "terms": ["What are the optional tuning parameters?", "are the optional tuning parameters?", "the optional tuning parameters?", "optional tuning parameters?", "tuning parameters?", "parameters?"], "tokens": ["What", "are", "the", "optional", "tuning", "parameters", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1359872879", "fields": {"term": "What is distribution_type?", "namespace": "open-p", "hash": -1359872879, "terms": ["What is distribution_type?", "is distribution_type?", "distribution_type?"], "tokens": ["What", "is", "distribution_type", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1771987456", "fields": {"term": "What is persistence-threads?", "namespace": "open-p", "hash": 1771987456, "terms": ["What is persistence-threads?", "is persistence-threads?", "persistence-threads?"], "tokens": ["What", "is", "persistence", "threads", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::434397534", "fields": {"term": "What are the tuning parameters for visitors?", "namespace": "open-p", "hash": 434397534, "terms": ["What are the tuning parameters for visitors?", "are the tuning parameters for visitors?", "the tuning parameters for visitors?", "tuning parameters for visitors?", "parameters for visitors?", "for visitors?", "visitors?"], "tokens": ["What", "are", "the", "tuning", "parameters", "for", "visitors", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1790761226", "fields": {"term": "What is max-size?", "namespace": "open-p", "hash": 1790761226, "terms": ["What is max-size?", "is max-size?", "max-size?"], "tokens": ["What", "is", "max", "size", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::126474321", "fields": {"term": "What is min-node-ratio-per-group?", "namespace": "open-p", "hash": 126474321, "terms": ["What is min-node-ratio-per-group?", "is min-node-ratio-per-group?", "min-node-ratio-per-group?"], "tokens": ["What", "is", "min", "node", "ratio", "per", "group", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::1894703578", "fields": {"term": "What does the tuning parameter allow?", "namespace": "open-p", "hash": 1894703578, "terms": ["What does the tuning parameter allow?", "does the tuning parameter allow?", "the tuning parameter allow?", "tuning parameter allow?", "parameter allow?", "allow?"], "tokens": ["What", "does", "the", "tuning", "parameter", "allow", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1651115928", "fields": {"term": "How does Vespa handle failing nodes in a group?", "namespace": "open-p", "hash": 1651115928, "terms": ["How does Vespa handle failing nodes in a group?", "does Vespa handle failing nodes in a group?", "Vespa handle failing nodes in a group?", "handle failing nodes in a group?", "failing nodes in a group?", "nodes in a group?", "in a group?", "a group?", "group?"], "tokens": ["How", "does", "Vespa", "handle", "failing", "nodes", "in", "a", "group", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1326542167", "fields": {"term": "What is tuning?", "namespace": "open-p", "hash": -1326542167, "terms": ["What is tuning?", "is tuning?", "tuning?"], "tokens": ["What", "is", "tuning", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-691274741", "fields": {"term": "What is the purpose of distribution algorithm?", "namespace": "open-p", "hash": -691274741, "terms": ["What is the purpose of distribution algorithm?", "is the purpose of distribution algorithm?", "the purpose of distribution algorithm?", "purpose of distribution algorithm?", "of distribution algorithm?", "distribution algorithm?", "algorithm?"], "tokens": ["What", "is", "the", "purpose", "of", "distribution", "algorithm", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-195829447", "fields": {"term": "What is the default mode of distribution?", "namespace": "open-p", "hash": -195829447, "terms": ["What is the default mode of distribution?", "is the default mode of distribution?", "the default mode of distribution?", "default mode of distribution?", "mode of distribution?", "of distribution?", "distribution?"], "tokens": ["What", "is", "the", "default", "mode", "of", "distribution", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1666064948", "fields": {"term": "How does bucket-splitting override distribution mode?", "namespace": "open-p", "hash": 1666064948, "terms": ["How does bucket-splitting override distribution mode?", "does bucket-splitting override distribution mode?", "bucket-splitting override distribution mode?", "override distribution mode?", "distribution mode?", "mode?"], "tokens": ["How", "does", "bucket", "splitting", "override", "distribution", "mode", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2049681854", "fields": {"term": "What is the recommended distribution mode?", "namespace": "open-p", "hash": -2049681854, "terms": ["What is the recommended distribution mode?", "is the recommended distribution mode?", "the recommended distribution mode?", "recommended distribution mode?", "distribution mode?", "mode?"], "tokens": ["What", "is", "the", "recommended", "distribution", "mode", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1773191301", "fields": {"term": "What is the impact of altering distribution mode?", "namespace": "open-p", "hash": 1773191301, "terms": ["What is the impact of altering distribution mode?", "is the impact of altering distribution mode?", "the impact of altering distribution mode?", "impact of altering distribution mode?", "of altering distribution mode?", "altering distribution mode?", "distribution mode?", "mode?"], "tokens": ["What", "is", "the", "impact", "of", "altering", "distribution", "mode", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-199325460", "fields": {"term": "What is the significance of specifying minimum-bits?", "namespace": "open-p", "hash": -199325460, "terms": ["What is the significance of specifying minimum-bits?", "is the significance of specifying minimum-bits?", "the significance of specifying minimum-bits?", "significance of specifying minimum-bits?", "of specifying minimum-bits?", "specifying minimum-bits?", "minimum-bits?"], "tokens": ["What", "is", "the", "significance", "of", "specifying", "minimum", "bits", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1096232039", "fields": {"term": "What is bucket maintenance?", "namespace": "open-p", "hash": -1096232039, "terms": ["What is bucket maintenance?", "is bucket maintenance?", "bucket maintenance?", "maintenance?"], "tokens": ["What", "is", "bucket", "maintenance", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::668051077", "fields": {"term": "How do you control bucket maintenance running time?", "namespace": "open-p", "hash": 668051077, "terms": ["How do you control bucket maintenance running time?", "do you control bucket maintenance running time?", "you control bucket maintenance running time?", "control bucket maintenance running time?", "bucket maintenance running time?", "maintenance running time?", "running time?", "time?"], "tokens": ["How", "do", "you", "control", "bucket", "maintenance", "running", "time", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1246623862", "fields": {"term": "What are throttling parameters?", "namespace": "open-p", "hash": 1246623862, "terms": ["What are throttling parameters?", "are throttling parameters?", "throttling parameters?", "parameters?"], "tokens": ["What", "are", "throttling", "parameters", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1573605767", "fields": {"term": "How many threads are used by default?", "namespace": "open-p", "hash": -1573605767, "terms": ["How many threads are used by default?", "many threads are used by default?", "threads are used by default?", "are used by default?", "used by default?", "by default?", "default?"], "tokens": ["How", "many", "threads", "are", "used", "by", "default", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2004594757", "fields": {"term": "What is a persistence thread?", "namespace": "open-p", "hash": -2004594757, "terms": ["What is a persistence thread?", "is a persistence thread?", "a persistence thread?", "persistence thread?", "thread?"], "tokens": ["What", "is", "a", "persistence", "thread", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-430888131", "fields": {"term": "How are bucket operations executed?", "namespace": "open-p", "hash": -430888131, "terms": ["How are bucket operations executed?", "are bucket operations executed?", "bucket operations executed?", "operations executed?", "executed?"], "tokens": ["How", "are", "bucket", "operations", "executed", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1395476135", "fields": {"term": "What is max-concurrent?", "namespace": "open-p", "hash": 1395476135, "terms": ["What is max-concurrent?", "is max-concurrent?", "max-concurrent?"], "tokens": ["What", "is", "max", "concurrent", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1786368031", "fields": {"term": "What is thread-count?", "namespace": "open-p", "hash": -1786368031, "terms": ["What is thread-count?", "is thread-count?", "thread-count?"], "tokens": ["What", "is", "thread", "count", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2117135913", "fields": {"term": "What is the maximum active count?", "namespace": "open-p", "hash": -2117135913, "terms": ["What is the maximum active count?", "is the maximum active count?", "the maximum active count?", "maximum active count?", "active count?", "count?"], "tokens": ["What", "is", "the", "maximum", "active", "count", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1078486694", "fields": {"term": "What is the default fixed component?", "namespace": "open-p", "hash": -1078486694, "terms": ["What is the default fixed component?", "is the default fixed component?", "the default fixed component?", "default fixed component?", "fixed component?", "component?"], "tokens": ["What", "is", "the", "default", "fixed", "component", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-177840301", "fields": {"term": "What is the default variable component?", "namespace": "open-p", "hash": -177840301, "terms": ["What is the default variable component?", "is the default variable component?", "the default variable component?", "default variable component?", "variable component?", "component?"], "tokens": ["What", "is", "the", "default", "variable", "component", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-702855391", "fields": {"term": "What is the purpose of visitors?", "namespace": "open-p", "hash": -702855391, "terms": ["What is the purpose of visitors?", "is the purpose of visitors?", "the purpose of visitors?", "purpose of visitors?", "of visitors?", "visitors?"], "tokens": ["What", "is", "the", "purpose", "of", "visitors", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::297952112", "fields": {"term": "What is the role of priority?", "namespace": "open-p", "hash": 297952112, "terms": ["What is the role of priority?", "is the role of priority?", "the role of priority?", "role of priority?", "of priority?", "priority?"], "tokens": ["What", "is", "the", "role", "of", "priority", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-989146021", "fields": {"term": "How is the maximum active calculated?", "namespace": "open-p", "hash": -989146021, "terms": ["How is the maximum active calculated?", "is the maximum active calculated?", "the maximum active calculated?", "maximum active calculated?", "active calculated?", "calculated?"], "tokens": ["How", "is", "the", "maximum", "active", "calculated", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1268939338", "fields": {"term": "What are the attribute values for visitors?", "namespace": "open-p", "hash": -1268939338, "terms": ["What are the attribute values for visitors?", "are the attribute values for visitors?", "the attribute values for visitors?", "attribute values for visitors?", "values for visitors?", "for visitors?", "visitors?"], "tokens": ["What", "are", "the", "attribute", "values", "for", "visitors", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::647949380", "fields": {"term": "What is the allowed number of visitors?", "namespace": "open-p", "hash": 647949380, "terms": ["What is the allowed number of visitors?", "is the allowed number of visitors?", "the allowed number of visitors?", "allowed number of visitors?", "number of visitors?", "of visitors?", "visitors?"], "tokens": ["What", "is", "the", "allowed", "number", "of", "visitors", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1311634896", "fields": {"term": "What are resource limits?", "namespace": "open-p", "hash": 1311634896, "terms": ["What are resource limits?", "are resource limits?", "resource limits?", "limits?"], "tokens": ["What", "are", "resource", "limits", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2094830724", "fields": {"term": "What is the default disk value?", "namespace": "open-p", "hash": -2094830724, "terms": ["What is the default disk value?", "is the default disk value?", "the default disk value?", "default disk value?", "disk value?", "value?"], "tokens": ["What", "is", "the", "default", "disk", "value", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1384364050", "fields": {"term": "What happens when the disk limit is reached?", "namespace": "open-p", "hash": -1384364050, "terms": ["What happens when the disk limit is reached?", "happens when the disk limit is reached?", "when the disk limit is reached?", "the disk limit is reached?", "disk limit is reached?", "limit is reached?", "is reached?", "reached?"], "tokens": ["What", "happens", "when", "the", "disk", "limit", "is", "reached", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1059439790", "fields": {"term": "What happens when the memory limit is reached?", "namespace": "open-p", "hash": -1059439790, "terms": ["What happens when the memory limit is reached?", "happens when the memory limit is reached?", "when the memory limit is reached?", "the memory limit is reached?", "memory limit is reached?", "limit is reached?", "is reached?", "reached?"], "tokens": ["What", "happens", "when", "the", "memory", "limit", "is", "reached", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::542350348", "fields": {"term": "What should be considered when tuning limits?", "namespace": "open-p", "hash": 542350348, "terms": ["What should be considered when tuning limits?", "should be considered when tuning limits?", "be considered when tuning limits?", "considered when tuning limits?", "when tuning limits?", "tuning limits?", "limits?"], "tokens": ["What", "should", "be", "considered", "when", "tuning", "limits", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::988998544", "fields": {"term": "What is the risk of setting limits too high?", "namespace": "open-p", "hash": 988998544, "terms": ["What is the risk of setting limits too high?", "is the risk of setting limits too high?", "the risk of setting limits too high?", "risk of setting limits too high?", "of setting limits too high?", "setting limits too high?", "limits too high?", "too high?", "high?"], "tokens": ["What", "is", "the", "risk", "of", "setting", "limits", "too", "high", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::321027138", "fields": {"term": "What is max-hits-per-partition?", "namespace": "open-p", "hash": 321027138, "terms": ["What is max-hits-per-partition?", "is max-hits-per-partition?", "max-hits-per-partition?"], "tokens": ["What", "is", "max", "hits", "per", "partition", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1033987518", "fields": {"term": "What is top-k-probability?", "namespace": "open-p", "hash": -1033987518, "terms": ["What is top-k-probability?", "is top-k-probability?", "top-k-probability?"], "tokens": ["What", "is", "top", "k", "probability", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-480407661", "fields": {"term": "What is dispatch-policy?", "namespace": "open-p", "hash": -480407661, "terms": ["What is dispatch-policy?", "is dispatch-policy?", "dispatch-policy?"], "tokens": ["What", "is", "dispatch", "policy", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::410512746", "fields": {"term": "What is min-active-docs-coverage?", "namespace": "open-p", "hash": 410512746, "terms": ["What is min-active-docs-coverage?", "is min-active-docs-coverage?", "min-active-docs-coverage?"], "tokens": ["What", "is", "min", "active", "docs", "coverage", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1646828982", "fields": {"term": "What is init-progress-time?", "namespace": "open-p", "hash": -1646828982, "terms": ["What is init-progress-time?", "is init-progress-time?", "init-progress-time?"], "tokens": ["What", "is", "init", "progress", "time", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::136177186", "fields": {"term": "What is max-premature-crashes?", "namespace": "open-p", "hash": 136177186, "terms": ["What is max-premature-crashes?", "is max-premature-crashes?", "max-premature-crashes?"], "tokens": ["What", "is", "max", "premature", "crashes", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-833712473", "fields": {"term": "What is stable-state-period?", "namespace": "open-p", "hash": -833712473, "terms": ["What is stable-state-period?", "is stable-state-period?", "stable-state-period?"], "tokens": ["What", "is", "stable", "state", "period", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2009397063", "fields": {"term": "What is numnodesperclient?", "namespace": "open-p", "hash": 2009397063, "terms": ["What is numnodesperclient?", "is numnodesperclient?", "numnodesperclient?"], "tokens": ["What", "is", "numnodesperclient", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::699065002", "fields": {"term": "What is maxqueuewait?", "namespace": "open-p", "hash": 699065002, "terms": ["What is maxqueuewait?", "is maxqueuewait?", "maxqueuewait?"], "tokens": ["What", "is", "maxqueuewait", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1945295551", "fields": {"term": "What is maxqueuebytesize?", "namespace": "open-p", "hash": 1945295551, "terms": ["What is maxqueuebytesize?", "is maxqueuebytesize?", "maxqueuebytesize?"], "tokens": ["What", "is", "maxqueuebytesize", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1655054955", "fields": {"term": "What is containercorememory?", "namespace": "open-p", "hash": -1655054955, "terms": ["What is containercorememory?", "is containercorememory?", "containercorememory?"], "tokens": ["What", "is", "containercorememory", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::964260447", "fields": {"term": "What is documentexpansionfactor?", "namespace": "open-p", "hash": 964260447, "terms": ["What is documentexpansionfactor?", "is documentexpansionfactor?", "documentexpansionfactor?"], "tokens": ["What", "is", "documentexpansionfactor", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-961334176", "fields": {"term": "What is a documentprocessor?", "namespace": "open-p", "hash": -961334176, "terms": ["What is a documentprocessor?", "is a documentprocessor?", "a documentprocessor?", "documentprocessor?"], "tokens": ["What", "is", "a", "documentprocessor", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-835641238", "fields": {"term": "What is a docproc chain?", "namespace": "open-p", "hash": -835641238, "terms": ["What is a docproc chain?", "is a docproc chain?", "a docproc chain?", "docproc chain?", "chain?"], "tokens": ["What", "is", "a", "docproc", "chain", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::-754693253", "fields": {"term": "What is a document processor reference?", "namespace": "open-p", "hash": -754693253, "terms": ["What is a document processor reference?", "is a document processor reference?", "a document processor reference?", "document processor reference?", "processor reference?", "reference?"], "tokens": ["What", "is", "a", "document", "processor", "reference", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1388684434", "fields": {"term": "What is an inner document processor?", "namespace": "open-p", "hash": -1388684434, "terms": ["What is an inner document processor?", "is an inner document processor?", "an inner document processor?", "inner document processor?", "document processor?", "processor?"], "tokens": ["What", "is", "an", "inner", "document", "processor", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::607161423", "fields": {"term": "What is an outer document processor?", "namespace": "open-p", "hash": 607161423, "terms": ["What is an outer document processor?", "is an outer document processor?", "an outer document processor?", "outer document processor?", "document processor?", "processor?"], "tokens": ["What", "is", "an", "outer", "document", "processor", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::469573880", "fields": {"term": "What are the optional sub-elements of a documentprocessor element?", "namespace": "open-p", "hash": 469573880, "terms": ["What are the optional sub-elements of a documentprocessor element?", "are the optional sub-elements of a documentprocessor element?", "the optional sub-elements of a documentprocessor element?", "optional sub-elements of a documentprocessor element?", "sub-elements of a documentprocessor element?", "of a documentprocessor element?", "a documentprocessor element?", "documentprocessor element?", "element?"], "tokens": ["What", "are", "the", "optional", "sub", "elements", "of", "a", "documentprocessor", "element", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1051670281", "fields": {"term": "What are the attributes of a documentprocessor element?", "namespace": "open-p", "hash": -1051670281, "terms": ["What are the attributes of a documentprocessor element?", "are the attributes of a documentprocessor element?", "the attributes of a documentprocessor element?", "attributes of a documentprocessor element?", "of a documentprocessor element?", "a documentprocessor element?", "documentprocessor element?", "element?"], "tokens": ["What", "are", "the", "attributes", "of", "a", "documentprocessor", "element", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1815068585", "fields": {"term": "What are documentprocessor attributes?", "namespace": "open-p", "hash": -1815068585, "terms": ["What are documentprocessor attributes?", "are documentprocessor attributes?", "documentprocessor attributes?", "attributes?"], "tokens": ["What", "are", "documentprocessor", "attributes", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-270662681", "fields": {"term": "How are document processors ordered?", "namespace": "open-p", "hash": -270662681, "terms": ["How are document processors ordered?", "are document processors ordered?", "document processors ordered?", "processors ordered?", "ordered?"], "tokens": ["How", "are", "document", "processors", "ordered", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::416050161", "fields": {"term": "What is a chain reference?", "namespace": "open-p", "hash": 416050161, "terms": ["What is a chain reference?", "is a chain reference?", "a chain reference?", "chain reference?", "reference?"], "tokens": ["What", "is", "a", "chain", "reference", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1185314523", "fields": {"term": "How does chain inherit document processors?", "namespace": "open-p", "hash": -1185314523, "terms": ["How does chain inherit document processors?", "does chain inherit document processors?", "chain inherit document processors?", "inherit document processors?", "document processors?", "processors?"], "tokens": ["How", "does", "chain", "inherit", "document", "processors", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1572762009", "fields": {"term": "What is a documentprocessor reference?", "namespace": "open-p", "hash": -1572762009, "terms": ["What is a documentprocessor reference?", "is a documentprocessor reference?", "a documentprocessor reference?", "documentprocessor reference?", "reference?"], "tokens": ["What", "is", "a", "documentprocessor", "reference", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::174482298", "fields": {"term": "What is a documentprocessor definition?", "namespace": "open-p", "hash": 174482298, "terms": ["What is a documentprocessor definition?", "is a documentprocessor definition?", "a documentprocessor definition?", "documentprocessor definition?", "definition?"], "tokens": ["What", "is", "a", "documentprocessor", "definition", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1715072654", "fields": {"term": "What is a phase in chain processing?", "namespace": "open-p", "hash": -1715072654, "terms": ["What is a phase in chain processing?", "is a phase in chain processing?", "a phase in chain processing?", "phase in chain processing?", "in chain processing?", "chain processing?", "processing?"], "tokens": ["What", "is", "a", "phase", "in", "chain", "processing", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-647773973", "fields": {"term": "What is a config in documentprocessor chain?", "namespace": "open-p", "hash": -647773973, "terms": ["What is a config in documentprocessor chain?", "is a config in documentprocessor chain?", "a config in documentprocessor chain?", "config in documentprocessor chain?", "in documentprocessor chain?", "documentprocessor chain?", "chain?"], "tokens": ["What", "is", "a", "config", "in", "documentprocessor", "chain", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::889152394", "fields": {"term": "What is field name mapping?", "namespace": "open-p", "hash": 889152394, "terms": ["What is field name mapping?", "is field name mapping?", "field name mapping?", "name mapping?", "mapping?"], "tokens": ["What", "is", "field", "name", "mapping", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1370117374", "fields": {"term": "How does field name mapping work?", "namespace": "open-p", "hash": 1370117374, "terms": ["How does field name mapping work?", "does field name mapping work?", "field name mapping work?", "name mapping work?", "mapping work?", "work?"], "tokens": ["How", "does", "field", "name", "mapping", "work", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1088711735", "fields": {"term": "What is the purpose of field name mapping?", "namespace": "open-p", "hash": -1088711735, "terms": ["What is the purpose of field name mapping?", "is the purpose of field name mapping?", "the purpose of field name mapping?", "purpose of field name mapping?", "of field name mapping?", "field name mapping?", "name mapping?", "mapping?"], "tokens": ["What", "is", "the", "purpose", "of", "field", "name", "mapping", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::217193851", "fields": {"term": "What does the field mapping example show?", "namespace": "open-p", "hash": 217193851, "terms": ["What does the field mapping example show?", "does the field mapping example show?", "the field mapping example show?", "field mapping example show?", "mapping example show?", "example show?", "show?"], "tokens": ["What", "does", "the", "field", "mapping", "example", "show", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::564835886", "fields": {"term": "How is mapping specified in Vespa?", "namespace": "open-p", "hash": 564835886, "terms": ["How is mapping specified in Vespa?", "is mapping specified in Vespa?", "mapping specified in Vespa?", "specified in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["How", "is", "mapping", "specified", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-811497681", "fields": {"term": "What happens if you specify mappings at different levels?", "namespace": "open-p", "hash": -811497681, "terms": ["What happens if you specify mappings at different levels?", "happens if you specify mappings at different levels?", "if you specify mappings at different levels?", "you specify mappings at different levels?", "specify mappings at different levels?", "mappings at different levels?", "at different levels?", "different levels?", "levels?"], "tokens": ["What", "happens", "if", "you", "specify", "mappings", "at", "different", "levels", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-717732127", "fields": {"term": "What is the syntax for field name mapping?", "namespace": "open-p", "hash": -717732127, "terms": ["What is the syntax for field name mapping?", "is the syntax for field name mapping?", "the syntax for field name mapping?", "syntax for field name mapping?", "for field name mapping?", "field name mapping?", "name mapping?", "mapping?"], "tokens": ["What", "is", "the", "syntax", "for", "field", "name", "mapping", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1102097181", "fields": {"term": "What is the dotted notation used for in mapping?", "namespace": "open-p", "hash": -1102097181, "terms": ["What is the dotted notation used for in mapping?", "is the dotted notation used for in mapping?", "the dotted notation used for in mapping?", "dotted notation used for in mapping?", "notation used for in mapping?", "used for in mapping?", "for in mapping?", "in mapping?", "mapping?"], "tokens": ["What", "is", "the", "dotted", "notation", "used", "for", "in", "mapping", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1127874050", "fields": {"term": "What is the http block?", "namespace": "open-p", "hash": -1127874050, "terms": ["What is the http block?", "is the http block?", "the http block?", "http block?", "block?"], "tokens": ["What", "is", "the", "http", "block", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1989836704", "fields": {"term": "What is the purpose of the http block?", "namespace": "open-p", "hash": -1989836704, "terms": ["What is the purpose of the http block?", "is the purpose of the http block?", "the purpose of the http block?", "purpose of the http block?", "of the http block?", "the http block?", "http block?", "block?"], "tokens": ["What", "is", "the", "purpose", "of", "the", "http", "block", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1662796020", "fields": {"term": "What is the `http` subelement of container?", "namespace": "open-p", "hash": -1662796020, "terms": ["What is the `http` subelement of container?", "is the `http` subelement of container?", "the `http` subelement of container?", "`http` subelement of container?", "subelement of container?", "of container?", "container?"], "tokens": ["What", "is", "the", "`", "http", "`", "subelement", "of", "container", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1673159664", "fields": {"term": "How can you configure http servers and filters with the http block?", "namespace": "open-p", "hash": -1673159664, "terms": ["How can you configure http servers and filters with the http block?", "can you configure http servers and filters with the http block?", "you configure http servers and filters with the http block?", "configure http servers and filters with the http block?", "http servers and filters with the http block?", "servers and filters with the http block?", "and filters with the http block?", "filters with the http block?", "with the http block?", "the http block?", "http block?", "block?"], "tokens": ["How", "can", "you", "configure", "http", "servers", "and", "filters", "with", "the", "http", "block", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2113986105", "fields": {"term": "What happens when the http block is present?", "namespace": "open-p", "hash": -2113986105, "terms": ["What happens when the http block is present?", "happens when the http block is present?", "when the http block is present?", "the http block is present?", "http block is present?", "block is present?", "is present?", "present?"], "tokens": ["What", "happens", "when", "the", "http", "block", "is", "present", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1887507655", "fields": {"term": "What are the elements that the http block takes?", "namespace": "open-p", "hash": 1887507655, "terms": ["What are the elements that the http block takes?", "are the elements that the http block takes?", "the elements that the http block takes?", "elements that the http block takes?", "that the http block takes?", "the http block takes?", "http block takes?", "block takes?", "takes?"], "tokens": ["What", "are", "the", "elements", "that", "the", "http", "block", "takes", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-44779451", "fields": {"term": "What is the purpose of the request-chain and response-chain elements?", "namespace": "open-p", "hash": -44779451, "terms": ["What is the purpose of the request-chain and response-chain elements?", "is the purpose of the request-chain and response-chain elements?", "the purpose of the request-chain and response-chain elements?", "purpose of the request-chain and response-chain elements?", "of the request-chain and response-chain elements?", "the request-chain and response-chain elements?", "request-chain and response-chain elements?", "and response-chain elements?", "response-chain elements?", "elements?"], "tokens": ["What", "is", "the", "purpose", "of", "the", "request", "chain", "and", "response", "chain", "elements", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1438949425", "fields": {"term": "What is the purpose of the binding element inside request-/response-chain?", "namespace": "open-p", "hash": 1438949425, "terms": ["What is the purpose of the binding element inside request-/response-chain?", "is the purpose of the binding element inside request-/response-chain?", "the purpose of the binding element inside request-/response-chain?", "purpose of the binding element inside request-/response-chain?", "of the binding element inside request-/response-chain?", "the binding element inside request-/response-chain?", "binding element inside request-/response-chain?", "element inside request-/response-chain?", "inside request-/response-chain?", "request-/response-chain?"], "tokens": ["What", "is", "the", "purpose", "of", "the", "binding", "element", "inside", "request", "/", "response", "chain", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1286297298", "fields": {"term": "What is a http server?", "namespace": "open-p", "hash": 1286297298, "terms": ["What is a http server?", "is a http server?", "a http server?", "http server?", "server?"], "tokens": ["What", "is", "a", "http", "server", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-65024524", "fields": {"term": "How to configure a server?", "namespace": "open-p", "hash": -65024524, "terms": ["How to configure a server?", "to configure a server?", "configure a server?", "a server?", "server?"], "tokens": ["How", "to", "configure", "a", "server", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::552031583", "fields": {"term": "What is jdisc.http.connector.def?", "namespace": "open-p", "hash": 552031583, "terms": ["What is jdisc.http.connector.def?", "is jdisc.http.connector.def?", "jdisc.http.connector.def?"], "tokens": ["What", "is", "jdisc", "http", "connector", "def", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1391474156", "fields": {"term": "What are the attributes of jdisc.http.connector.def?", "namespace": "open-p", "hash": -1391474156, "terms": ["What are the attributes of jdisc.http.connector.def?", "are the attributes of jdisc.http.connector.def?", "the attributes of jdisc.http.connector.def?", "attributes of jdisc.http.connector.def?", "of jdisc.http.connector.def?", "jdisc.http.connector.def?"], "tokens": ["What", "are", "the", "attributes", "of", "jdisc", "http", "connector", "def", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1026004554", "fields": {"term": "What is the purpose of 'id' attribute in jdisc.http.connector.def?", "namespace": "open-p", "hash": 1026004554, "terms": ["What is the purpose of 'id' attribute in jdisc.http.connector.def?", "is the purpose of 'id' attribute in jdisc.http.connector.def?", "the purpose of 'id' attribute in jdisc.http.connector.def?", "purpose of 'id' attribute in jdisc.http.connector.def?", "of 'id' attribute in jdisc.http.connector.def?", "'id' attribute in jdisc.http.connector.def?", "attribute in jdisc.http.connector.def?", "in jdisc.http.connector.def?", "jdisc.http.connector.def?"], "tokens": ["What", "is", "the", "purpose", "of", "'", "id", "'", "attribute", "in", "jdisc", "http", "connector", "def", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2032207814", "fields": {"term": "What is the purpose of 'port' attribute in jdisc.http.connector.def?", "namespace": "open-p", "hash": 2032207814, "terms": ["What is the purpose of 'port' attribute in jdisc.http.connector.def?", "is the purpose of 'port' attribute in jdisc.http.connector.def?", "the purpose of 'port' attribute in jdisc.http.connector.def?", "purpose of 'port' attribute in jdisc.http.connector.def?", "of 'port' attribute in jdisc.http.connector.def?", "'port' attribute in jdisc.http.connector.def?", "attribute in jdisc.http.connector.def?", "in jdisc.http.connector.def?", "jdisc.http.connector.def?"], "tokens": ["What", "is", "the", "purpose", "of", "'", "port", "'", "attribute", "in", "jdisc", "http", "connector", "def", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1785160368", "fields": {"term": "What is the purpose of 'default-request-chain' attribute in jdisc.http.connector.def?", "namespace": "open-p", "hash": 1785160368, "terms": ["What is the purpose of 'default-request-chain' attribute in jdisc.http.connector.def?", "is the purpose of 'default-request-chain' attribute in jdisc.http.connector.def?", "the purpose of 'default-request-chain' attribute in jdisc.http.connector.def?", "purpose of 'default-request-chain' attribute in jdisc.http.connector.def?", "of 'default-request-chain' attribute in jdisc.http.connector.def?", "'default-request-chain' attribute in jdisc.http.connector.def?", "attribute in jdisc.http.connector.def?", "in jdisc.http.connector.def?", "jdisc.http.connector.def?"], "tokens": ["What", "is", "the", "purpose", "of", "'", "default", "request", "chain", "'", "attribute", "in", "jdisc", "http", "connector", "def", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-218860926", "fields": {"term": "What is the purpose of 'default-response-chain' attribute in jdisc.http.connector.def?", "namespace": "open-p", "hash": -218860926, "terms": ["What is the purpose of 'default-response-chain' attribute in jdisc.http.connector.def?", "is the purpose of 'default-response-chain' attribute in jdisc.http.connector.def?", "the purpose of 'default-response-chain' attribute in jdisc.http.connector.def?", "purpose of 'default-response-chain' attribute in jdisc.http.connector.def?", "of 'default-response-chain' attribute in jdisc.http.connector.def?", "'default-response-chain' attribute in jdisc.http.connector.def?", "attribute in jdisc.http.connector.def?", "in jdisc.http.connector.def?", "jdisc.http.connector.def?"], "tokens": ["What", "is", "the", "purpose", "of", "'", "default", "response", "chain", "'", "attribute", "in", "jdisc", "http", "connector", "def", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::950789930", "fields": {"term": "What is PEM format?", "namespace": "open-p", "hash": 950789930, "terms": ["What is PEM format?", "is PEM format?", "PEM format?", "format?"], "tokens": ["What", "is", "PEM", "format", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-774294666", "fields": {"term": "How to setup TSL?", "namespace": "open-p", "hash": -774294666, "terms": ["How to setup TSL?", "to setup TSL?", "setup TSL?", "TSL?"], "tokens": ["How", "to", "setup", "TSL", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-454699409", "fields": {"term": "What is the format for private keys?", "namespace": "open-p", "hash": -454699409, "terms": ["What is the format for private keys?", "is the format for private keys?", "the format for private keys?", "format for private keys?", "for private keys?", "private keys?", "keys?"], "tokens": ["What", "is", "the", "format", "for", "private", "keys", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::81668130", "fields": {"term": "What is the path for PEM private key?", "namespace": "open-p", "hash": 81668130, "terms": ["What is the path for PEM private key?", "is the path for PEM private key?", "the path for PEM private key?", "path for PEM private key?", "for PEM private key?", "PEM private key?", "private key?", "key?"], "tokens": ["What", "is", "the", "path", "for", "PEM", "private", "key", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1652447476", "fields": {"term": "What is the path of the certificate file?", "namespace": "open-p", "hash": 1652447476, "terms": ["What is the path of the certificate file?", "is the path of the certificate file?", "the path of the certificate file?", "path of the certificate file?", "of the certificate file?", "the certificate file?", "certificate file?", "file?"], "tokens": ["What", "is", "the", "path", "of", "the", "certificate", "file", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1181808208", "fields": {"term": "How to access PEM formatted certificates?", "namespace": "open-p", "hash": -1181808208, "terms": ["How to access PEM formatted certificates?", "to access PEM formatted certificates?", "access PEM formatted certificates?", "PEM formatted certificates?", "formatted certificates?", "certificates?"], "tokens": ["How", "to", "access", "PEM", "formatted", "certificates", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-317297114", "fields": {"term": "What is a PEM file?", "namespace": "open-p", "hash": -317297114, "terms": ["What is a PEM file?", "is a PEM file?", "a PEM file?", "PEM file?", "file?"], "tokens": ["What", "is", "a", "PEM", "file", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1174692887", "fields": {"term": "What is a CA certificate?", "namespace": "open-p", "hash": 1174692887, "terms": ["What is a CA certificate?", "is a CA certificate?", "a CA certificate?", "CA certificate?", "certificate?"], "tokens": ["What", "is", "a", "CA", "certificate", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1757159547", "fields": {"term": "What are supported authentication values?", "namespace": "open-p", "hash": 1757159547, "terms": ["What are supported authentication values?", "are supported authentication values?", "supported authentication values?", "authentication values?", "values?"], "tokens": ["What", "are", "supported", "authentication", "values", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1944447099", "fields": {"term": "What does *need* authentication mean?", "namespace": "open-p", "hash": -1944447099, "terms": ["What does *need* authentication mean?", "does *need* authentication mean?", "*need* authentication mean?", "authentication mean?", "mean?"], "tokens": ["What", "does", "*", "need", "*", "authentication", "mean", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1306120644", "fields": {"term": "What is TLS protocol?", "namespace": "open-p", "hash": -1306120644, "terms": ["What is TLS protocol?", "is TLS protocol?", "TLS protocol?", "protocol?"], "tokens": ["What", "is", "TLS", "protocol", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1110842372", "fields": {"term": "What is a TLS cipher suite?", "namespace": "open-p", "hash": -1110842372, "terms": ["What is a TLS cipher suite?", "is a TLS cipher suite?", "a TLS cipher suite?", "TLS cipher suite?", "cipher suite?", "suite?"], "tokens": ["What", "is", "a", "TLS", "cipher", "suite", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::15421875", "fields": {"term": "How to enable TLS cipher suites?", "namespace": "open-p", "hash": 15421875, "terms": ["How to enable TLS cipher suites?", "to enable TLS cipher suites?", "enable TLS cipher suites?", "TLS cipher suites?", "cipher suites?", "suites?"], "tokens": ["How", "to", "enable", "TLS", "cipher", "suites", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-562846149", "fields": {"term": "What is an example of a TLS cipher suite?", "namespace": "open-p", "hash": -562846149, "terms": ["What is an example of a TLS cipher suite?", "is an example of a TLS cipher suite?", "an example of a TLS cipher suite?", "example of a TLS cipher suite?", "of a TLS cipher suite?", "a TLS cipher suite?", "TLS cipher suite?", "cipher suite?", "suite?"], "tokens": ["What", "is", "an", "example", "of", "a", "TLS", "cipher", "suite", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1074724262", "fields": {"term": "What must be supported by JDK installation?", "namespace": "open-p", "hash": 1074724262, "terms": ["What must be supported by JDK installation?", "must be supported by JDK installation?", "be supported by JDK installation?", "supported by JDK installation?", "by JDK installation?", "JDK installation?", "installation?"], "tokens": ["What", "must", "be", "supported", "by", "JDK", "installation", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2088433708", "fields": {"term": "What is SslProvider?", "namespace": "open-p", "hash": -2088433708, "terms": ["What is SslProvider?", "is SslProvider?", "SslProvider?"], "tokens": ["What", "is", "SslProvider", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1926385943", "fields": {"term": "What is the required 'class' attribute?", "namespace": "open-p", "hash": 1926385943, "terms": ["What is the required 'class' attribute?", "is the required 'class' attribute?", "the required 'class' attribute?", "required 'class' attribute?", "'class' attribute?", "attribute?"], "tokens": ["What", "is", "the", "required", "'", "class", "'", "attribute", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-675066988", "fields": {"term": "What is filtering?", "namespace": "open-p", "hash": -675066988, "terms": ["What is filtering?", "is filtering?", "filtering?"], "tokens": ["What", "is", "filtering", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::57957658", "fields": {"term": "What is response-chain used for?", "namespace": "open-p", "hash": 57957658, "terms": ["What is response-chain used for?", "is response-chain used for?", "response-chain used for?", "used for?", "for?"], "tokens": ["What", "is", "response", "chain", "used", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1260239770", "fields": {"term": "What is a URI pattern?", "namespace": "open-p", "hash": 1260239770, "terms": ["What is a URI pattern?", "is a URI pattern?", "a URI pattern?", "URI pattern?", "pattern?"], "tokens": ["What", "is", "a", "URI", "pattern", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-861428914", "fields": {"term": "How do you specify a URI pattern?", "namespace": "open-p", "hash": -861428914, "terms": ["How do you specify a URI pattern?", "do you specify a URI pattern?", "you specify a URI pattern?", "specify a URI pattern?", "a URI pattern?", "URI pattern?", "pattern?"], "tokens": ["How", "do", "you", "specify", "a", "URI", "pattern", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-315658786", "fields": {"term": "What happens to requests matching a URI pattern?", "namespace": "open-p", "hash": -315658786, "terms": ["What happens to requests matching a URI pattern?", "happens to requests matching a URI pattern?", "to requests matching a URI pattern?", "requests matching a URI pattern?", "matching a URI pattern?", "a URI pattern?", "URI pattern?", "pattern?"], "tokens": ["What", "happens", "to", "requests", "matching", "a", "URI", "pattern", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1691045273", "fields": {"term": "What happens to responses matching a URI pattern?", "namespace": "open-p", "hash": 1691045273, "terms": ["What happens to responses matching a URI pattern?", "happens to responses matching a URI pattern?", "to responses matching a URI pattern?", "responses matching a URI pattern?", "matching a URI pattern?", "a URI pattern?", "URI pattern?", "pattern?"], "tokens": ["What", "happens", "to", "responses", "matching", "a", "URI", "pattern", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1017284635", "fields": {"term": "What are the supported filter types?", "namespace": "open-p", "hash": 1017284635, "terms": ["What are the supported filter types?", "are the supported filter types?", "the supported filter types?", "supported filter types?", "filter types?", "types?"], "tokens": ["What", "are", "the", "supported", "filter", "types", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2078347076", "fields": {"term": "How do you refer to a single filter?", "namespace": "open-p", "hash": 2078347076, "terms": ["How do you refer to a single filter?", "do you refer to a single filter?", "you refer to a single filter?", "refer to a single filter?", "to a single filter?", "a single filter?", "single filter?", "filter?"], "tokens": ["How", "do", "you", "refer", "to", "a", "single", "filter", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::59217242", "fields": {"term": "What is the purpose of defining a filter under `http`?", "namespace": "open-p", "hash": 59217242, "terms": ["What is the purpose of defining a filter under `http`?", "is the purpose of defining a filter under `http`?", "the purpose of defining a filter under `http`?", "purpose of defining a filter under `http`?", "of defining a filter under `http`?", "defining a filter under `http`?", "a filter under `http`?", "filter under `http`?", "under `http`?", "`http`?"], "tokens": ["What", "is", "the", "purpose", "of", "defining", "a", "filter", "under", "`", "http", "`?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::727285474", "fields": {"term": "What are the required sub-elements for a filter?", "namespace": "open-p", "hash": 727285474, "terms": ["What are the required sub-elements for a filter?", "are the required sub-elements for a filter?", "the required sub-elements for a filter?", "required sub-elements for a filter?", "sub-elements for a filter?", "for a filter?", "a filter?", "filter?"], "tokens": ["What", "are", "the", "required", "sub", "elements", "for", "a", "filter", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::6346387", "fields": {"term": "What is the purpose of `Security[Request/Response]Filters`?", "namespace": "open-p", "hash": 6346387, "terms": ["What is the purpose of `Security[Request/Response]Filters`?", "is the purpose of `Security[Request/Response]Filters`?", "the purpose of `Security[Request/Response]Filters`?", "purpose of `Security[Request/Response]Filters`?", "of `Security[Request/Response]Filters`?", "`Security[Request/Response]Filters`?"], "tokens": ["What", "is", "the", "purpose", "of", "`", "Security", "[", "Request", "/", "Response", "]", "Filters", "`?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2048412797", "fields": {"term": "What is the format for defining a filter in XML?", "namespace": "open-p", "hash": 2048412797, "terms": ["What is the format for defining a filter in XML?", "is the format for defining a filter in XML?", "the format for defining a filter in XML?", "format for defining a filter in XML?", "for defining a filter in XML?", "defining a filter in XML?", "a filter in XML?", "filter in XML?", "in XML?", "XML?"], "tokens": ["What", "is", "the", "format", "for", "defining", "a", "filter", "in", "XML", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-283329187", "fields": {"term": "What is a dependency in filters?", "namespace": "open-p", "hash": -283329187, "terms": ["What is a dependency in filters?", "is a dependency in filters?", "a dependency in filters?", "dependency in filters?", "in filters?", "filters?"], "tokens": ["What", "is", "a", "dependency", "in", "filters", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::437409636", "fields": {"term": "What is the purpose of `before` tags?", "namespace": "open-p", "hash": 437409636, "terms": ["What is the purpose of `before` tags?", "is the purpose of `before` tags?", "the purpose of `before` tags?", "purpose of `before` tags?", "of `before` tags?", "`before` tags?", "tags?"], "tokens": ["What", "is", "the", "purpose", "of", "`", "before", "`", "tags", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1934958701", "fields": {"term": "What is the syntax for `before` tags?", "namespace": "open-p", "hash": -1934958701, "terms": ["What is the syntax for `before` tags?", "is the syntax for `before` tags?", "the syntax for `before` tags?", "syntax for `before` tags?", "for `before` tags?", "`before` tags?", "tags?"], "tokens": ["What", "is", "the", "syntax", "for", "`", "before", "`", "tags", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::673307664", "fields": {"term": "How are filters or phases defined?", "namespace": "open-p", "hash": 673307664, "terms": ["How are filters or phases defined?", "are filters or phases defined?", "filters or phases defined?", "or phases defined?", "phases defined?", "defined?"], "tokens": ["How", "are", "filters", "or", "phases", "defined", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-778869913", "fields": {"term": "What is the tag used for `before`?", "namespace": "open-p", "hash": -778869913, "terms": ["What is the tag used for `before`?", "is the tag used for `before`?", "the tag used for `before`?", "tag used for `before`?", "used for `before`?", "for `before`?", "`before`?"], "tokens": ["What", "is", "the", "tag", "used", "for", "`", "before", "`?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1430905589", "fields": {"term": "What does the `before` tag do?", "namespace": "open-p", "hash": -1430905589, "terms": ["What does the `before` tag do?", "does the `before` tag do?", "the `before` tag do?", "`before` tag do?", "tag do?", "do?"], "tokens": ["What", "does", "the", "`", "before", "`", "tag", "do", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1949957223", "fields": {"term": "Where is `before` tag used?", "namespace": "open-p", "hash": 1949957223, "terms": ["Where is `before` tag used?", "is `before` tag used?", "`before` tag used?", "tag used?", "used?"], "tokens": ["Where", "is", "`", "before", "`", "tag", "used", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1745924355", "fields": {"term": "What are 'after' tags used for?", "namespace": "open-p", "hash": -1745924355, "terms": ["What are 'after' tags used for?", "are 'after' tags used for?", "'after' tags used for?", "tags used for?", "used for?", "for?"], "tokens": ["What", "are", "'", "after", "'", "tags", "used", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1252530761", "fields": {"term": "What is FilterConfig?", "namespace": "open-p", "hash": -1252530761, "terms": ["What is FilterConfig?", "is FilterConfig?", "FilterConfig?"], "tokens": ["What", "is", "FilterConfig", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1198231498", "fields": {"term": "What filters can I configure?", "namespace": "open-p", "hash": -1198231498, "terms": ["What filters can I configure?", "filters can I configure?", "can I configure?", "I configure?", "configure?"], "tokens": ["What", "filters", "can", "I", "configure", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1916320726", "fields": {"term": "What is a chain?", "namespace": "open-p", "hash": -1916320726, "terms": ["What is a chain?", "is a chain?", "a chain?", "chain?"], "tokens": ["What", "is", "a", "chain", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::-1635306021", "fields": {"term": "What are dependencies expressed by?", "namespace": "open-p", "hash": -1635306021, "terms": ["What are dependencies expressed by?", "are dependencies expressed by?", "dependencies expressed by?", "expressed by?", "by?"], "tokens": ["What", "are", "dependencies", "expressed", "by", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-768264699", "fields": {"term": "What is a response-chain?", "namespace": "open-p", "hash": -768264699, "terms": ["What is a response-chain?", "is a response-chain?", "a response-chain?", "response-chain?"], "tokens": ["What", "is", "a", "response", "chain", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1128380120", "fields": {"term": "What is inherited chain?", "namespace": "open-p", "hash": -1128380120, "terms": ["What is inherited chain?", "is inherited chain?", "inherited chain?", "chain?"], "tokens": ["What", "is", "inherited", "chain", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1154564907", "fields": {"term": "What is exclude element?", "namespace": "open-p", "hash": 1154564907, "terms": ["What is exclude element?", "is exclude element?", "exclude element?", "element?"], "tokens": ["What", "is", "exclude", "element", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1901531844", "fields": {"term": "What does inherited element contain?", "namespace": "open-p", "hash": -1901531844, "terms": ["What does inherited element contain?", "does inherited element contain?", "inherited element contain?", "element contain?", "contain?"], "tokens": ["What", "does", "inherited", "element", "contain", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1857070154", "fields": {"term": "What does exclude element contain?", "namespace": "open-p", "hash": 1857070154, "terms": ["What does exclude element contain?", "does exclude element contain?", "exclude element contain?", "element contain?", "contain?"], "tokens": ["What", "does", "exclude", "element", "contain", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1939735849", "fields": {"term": "What is the 'inherits' parameter?", "namespace": "open-p", "hash": -1939735849, "terms": ["What is the 'inherits' parameter?", "is the 'inherits' parameter?", "the 'inherits' parameter?", "'inherits' parameter?", "parameter?"], "tokens": ["What", "is", "the", "'", "inherits", "'", "parameter", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-304592748", "fields": {"term": "What is a phase?", "namespace": "open-p", "hash": -304592748, "terms": ["What is a phase?", "is a phase?", "a phase?", "phase?"], "tokens": ["What", "is", "a", "phase", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-540323837", "fields": {"term": "What is a checkpoint?", "namespace": "open-p", "hash": -540323837, "terms": ["What is a checkpoint?", "is a checkpoint?", "a checkpoint?", "checkpoint?"], "tokens": ["What", "is", "a", "checkpoint", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::973907958", "fields": {"term": "What is a successor?", "namespace": "open-p", "hash": 973907958, "terms": ["What is a successor?", "is a successor?", "a successor?", "successor?"], "tokens": ["What", "is", "a", "successor", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1491965238", "fields": {"term": "What is a predecessor?", "namespace": "open-p", "hash": -1491965238, "terms": ["What is a predecessor?", "is a predecessor?", "a predecessor?", "predecessor?"], "tokens": ["What", "is", "a", "predecessor", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1977099252", "fields": {"term": "What is the purpose of a phase?", "namespace": "open-p", "hash": -1977099252, "terms": ["What is the purpose of a phase?", "is the purpose of a phase?", "the purpose of a phase?", "purpose of a phase?", "of a phase?", "a phase?", "phase?"], "tokens": ["What", "is", "the", "purpose", "of", "a", "phase", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::208838292", "fields": {"term": "What can depend on a phase?", "namespace": "open-p", "hash": 208838292, "terms": ["What can depend on a phase?", "can depend on a phase?", "depend on a phase?", "on a phase?", "a phase?", "phase?"], "tokens": ["What", "can", "depend", "on", "a", "phase", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-999150663", "fields": {"term": "What is processing subelement?", "namespace": "open-p", "hash": -999150663, "terms": ["What is processing subelement?", "is processing subelement?", "processing subelement?", "subelement?"], "tokens": ["What", "is", "processing", "subelement", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::560071556", "fields": {"term": "What is the syntax of processing?", "namespace": "open-p", "hash": 560071556, "terms": ["What is the syntax of processing?", "is the syntax of processing?", "the syntax of processing?", "syntax of processing?", "of processing?", "processing?"], "tokens": ["What", "is", "the", "syntax", "of", "processing", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-662857168", "fields": {"term": "What is the semantics of processing?", "namespace": "open-p", "hash": -662857168, "terms": ["What is the semantics of processing?", "is the semantics of processing?", "the semantics of processing?", "semantics of processing?", "of processing?", "processing?"], "tokens": ["What", "is", "the", "semantics", "of", "processing", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::542092048", "fields": {"term": "What is a pure processing application?", "namespace": "open-p", "hash": 542092048, "terms": ["What is a pure processing application?", "is a pure processing application?", "a pure processing application?", "pure processing application?", "processing application?", "application?"], "tokens": ["What", "is", "a", "pure", "processing", "application", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2011479345", "fields": {"term": "What is the default binding for ProcessingHandler?", "namespace": "open-p", "hash": -2011479345, "terms": ["What is the default binding for ProcessingHandler?", "is the default binding for ProcessingHandler?", "the default binding for ProcessingHandler?", "default binding for ProcessingHandler?", "binding for ProcessingHandler?", "for ProcessingHandler?", "ProcessingHandler?"], "tokens": ["What", "is", "the", "default", "binding", "for", "ProcessingHandler", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::951071336", "fields": {"term": "Example of ProcessingHandler binding?", "namespace": "open-p", "hash": 951071336, "terms": ["Example of ProcessingHandler binding?", "of ProcessingHandler binding?", "ProcessingHandler binding?", "binding?"], "tokens": ["Example", "of", "ProcessingHandler", "binding", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2030327690", "fields": {"term": "Are multiple bindings allowed for ProcessingHandler?", "namespace": "open-p", "hash": -2030327690, "terms": ["Are multiple bindings allowed for ProcessingHandler?", "multiple bindings allowed for ProcessingHandler?", "bindings allowed for ProcessingHandler?", "allowed for ProcessingHandler?", "for ProcessingHandler?", "ProcessingHandler?"], "tokens": ["Are", "multiple", "bindings", "allowed", "for", "ProcessingHandler", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::239350029", "fields": {"term": "What is a single processor?", "namespace": "open-p", "hash": 239350029, "terms": ["What is a single processor?", "is a single processor?", "a single processor?", "single processor?", "processor?"], "tokens": ["What", "is", "a", "single", "processor", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1703098294", "fields": {"term": "What is the default value for the class attribute?", "namespace": "open-p", "hash": 1703098294, "terms": ["What is the default value for the class attribute?", "is the default value for the class attribute?", "the default value for the class attribute?", "default value for the class attribute?", "value for the class attribute?", "for the class attribute?", "the class attribute?", "class attribute?", "attribute?"], "tokens": ["What", "is", "the", "default", "value", "for", "the", "class", "attribute", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::72867114", "fields": {"term": "What does the renderer tag look like?", "namespace": "open-p", "hash": 72867114, "terms": ["What does the renderer tag look like?", "does the renderer tag look like?", "the renderer tag look like?", "renderer tag look like?", "tag look like?", "look like?", "like?"], "tokens": ["What", "does", "the", "renderer", "tag", "look", "like", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::772395047", "fields": {"term": "What is the idref attribute?", "namespace": "open-p", "hash": 772395047, "terms": ["What is the idref attribute?", "is the idref attribute?", "the idref attribute?", "idref attribute?", "attribute?"], "tokens": ["What", "is", "the", "idref", "attribute", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1069688335", "fields": {"term": "What is the default chain?", "namespace": "open-p", "hash": 1069688335, "terms": ["What is the default chain?", "is the default chain?", "the default chain?", "default chain?", "chain?"], "tokens": ["What", "is", "the", "default", "chain", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1071841183", "fields": {"term": "How does exclude work in inherited chains?", "namespace": "open-p", "hash": 1071841183, "terms": ["How does exclude work in inherited chains?", "does exclude work in inherited chains?", "exclude work in inherited chains?", "work in inherited chains?", "in inherited chains?", "inherited chains?", "chains?"], "tokens": ["How", "does", "exclude", "work", "in", "inherited", "chains", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1895607435", "fields": {"term": "What is an example of exclude in inherited chains?", "namespace": "open-p", "hash": 1895607435, "terms": ["What is an example of exclude in inherited chains?", "is an example of exclude in inherited chains?", "an example of exclude in inherited chains?", "example of exclude in inherited chains?", "of exclude in inherited chains?", "exclude in inherited chains?", "in inherited chains?", "inherited chains?", "chains?"], "tokens": ["What", "is", "an", "example", "of", "exclude", "in", "inherited", "chains", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::657089674", "fields": {"term": "How to define a phase?", "namespace": "open-p", "hash": 657089674, "terms": ["How to define a phase?", "to define a phase?", "define a phase?", "a phase?", "phase?"], "tokens": ["How", "to", "define", "a", "phase", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1004677744", "fields": {"term": "What is the purpose of before and after?", "namespace": "open-p", "hash": -1004677744, "terms": ["What is the purpose of before and after?", "is the purpose of before and after?", "the purpose of before and after?", "purpose of before and after?", "of before and after?", "before and after?", "and after?", "after?"], "tokens": ["What", "is", "the", "purpose", "of", "before", "and", "after", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1692508210", "fields": {"term": "What is the ID of a phase?", "namespace": "open-p", "hash": 1692508210, "terms": ["What is the ID of a phase?", "is the ID of a phase?", "the ID of a phase?", "ID of a phase?", "of a phase?", "a phase?", "phase?"], "tokens": ["What", "is", "the", "ID", "of", "a", "phase", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1360656514", "fields": {"term": "What does the 'before' element do?", "namespace": "open-p", "hash": 1360656514, "terms": ["What does the 'before' element do?", "does the 'before' element do?", "the 'before' element do?", "'before' element do?", "element do?", "do?"], "tokens": ["What", "does", "the", "'", "before", "'", "element", "do", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::533321085", "fields": {"term": "How does the 'before' element affect component order?", "namespace": "open-p", "hash": 533321085, "terms": ["How does the 'before' element affect component order?", "does the 'before' element affect component order?", "the 'before' element affect component order?", "'before' element affect component order?", "element affect component order?", "affect component order?", "component order?", "order?"], "tokens": ["How", "does", "the", "'", "before", "'", "element", "affect", "component", "order", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-440007761", "fields": {"term": "Can multiple 'before' elements be used?", "namespace": "open-p", "hash": -440007761, "terms": ["Can multiple 'before' elements be used?", "multiple 'before' elements be used?", "'before' elements be used?", "elements be used?", "be used?", "used?"], "tokens": ["Can", "multiple", "'", "before", "'", "elements", "be", "used", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2031613328", "fields": {"term": "What is the purpose of defining a component 'before' another?", "namespace": "open-p", "hash": -2031613328, "terms": ["What is the purpose of defining a component 'before' another?", "is the purpose of defining a component 'before' another?", "the purpose of defining a component 'before' another?", "purpose of defining a component 'before' another?", "of defining a component 'before' another?", "defining a component 'before' another?", "a component 'before' another?", "component 'before' another?", "'before' another?", "another?"], "tokens": ["What", "is", "the", "purpose", "of", "defining", "a", "component", "'", "before", "'", "another", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1961509591", "fields": {"term": "What is the purpose of 'after' elements?", "namespace": "open-p", "hash": -1961509591, "terms": ["What is the purpose of 'after' elements?", "is the purpose of 'after' elements?", "the purpose of 'after' elements?", "purpose of 'after' elements?", "of 'after' elements?", "'after' elements?", "elements?"], "tokens": ["What", "is", "the", "purpose", "of", "'", "after", "'", "elements", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-64947444", "fields": {"term": "How can multiple 'after' elements be used?", "namespace": "open-p", "hash": -64947444, "terms": ["How can multiple 'after' elements be used?", "can multiple 'after' elements be used?", "multiple 'after' elements be used?", "'after' elements be used?", "elements be used?", "be used?", "used?"], "tokens": ["How", "can", "multiple", "'", "after", "'", "elements", "be", "used", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1578560697", "fields": {"term": "What is a phase/component?", "namespace": "open-p", "hash": -1578560697, "terms": ["What is a phase/component?", "is a phase/component?", "a phase/component?", "phase/component?"], "tokens": ["What", "is", "a", "phase", "/", "component", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1494831488", "fields": {"term": "What can be defined using 'after' elements?", "namespace": "open-p", "hash": -1494831488, "terms": ["What can be defined using 'after' elements?", "can be defined using 'after' elements?", "be defined using 'after' elements?", "defined using 'after' elements?", "using 'after' elements?", "'after' elements?", "elements?"], "tokens": ["What", "can", "be", "defined", "using", "'", "after", "'", "elements", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1231203918", "fields": {"term": "How does 'after' define component order?", "namespace": "open-p", "hash": -1231203918, "terms": ["How does 'after' define component order?", "does 'after' define component order?", "'after' define component order?", "define component order?", "component order?", "order?"], "tokens": ["How", "does", "'", "after", "'", "define", "component", "order", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2081243697", "fields": {"term": "What is the structure of a 'after' element?", "namespace": "open-p", "hash": 2081243697, "terms": ["What is the structure of a 'after' element?", "is the structure of a 'after' element?", "the structure of a 'after' element?", "structure of a 'after' element?", "of a 'after' element?", "a 'after' element?", "'after' element?", "element?"], "tokens": ["What", "is", "the", "structure", "of", "a", "'", "after", "'", "element", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1528703433", "fields": {"term": "What is the relationship between 'after' elements and component order?", "namespace": "open-p", "hash": 1528703433, "terms": ["What is the relationship between 'after' elements and component order?", "is the relationship between 'after' elements and component order?", "the relationship between 'after' elements and component order?", "relationship between 'after' elements and component order?", "between 'after' elements and component order?", "'after' elements and component order?", "elements and component order?", "and component order?", "component order?", "order?"], "tokens": ["What", "is", "the", "relationship", "between", "'", "after", "'", "elements", "and", "component", "order", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::238869754", "fields": {"term": "What is the routing section?", "namespace": "open-p", "hash": 238869754, "terms": ["What is the routing section?", "is the routing section?", "the routing section?", "routing section?", "section?"], "tokens": ["What", "is", "the", "routing", "section", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2006729213", "fields": {"term": "What is routingtable?", "namespace": "open-p", "hash": 2006729213, "terms": ["What is routingtable?", "is routingtable?", "routingtable?"], "tokens": ["What", "is", "routingtable", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::496192329", "fields": {"term": "What is the recipient element?", "namespace": "open-p", "hash": 496192329, "terms": ["What is the recipient element?", "is the recipient element?", "the recipient element?", "recipient element?", "element?"], "tokens": ["What", "is", "the", "recipient", "element", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-292508243", "fields": {"term": "What is the route subelement for?", "namespace": "open-p", "hash": -292508243, "terms": ["What is the route subelement for?", "is the route subelement for?", "the route subelement for?", "route subelement for?", "subelement for?", "for?"], "tokens": ["What", "is", "the", "route", "subelement", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-397033035", "fields": {"term": "What is the hop subelement for?", "namespace": "open-p", "hash": -397033035, "terms": ["What is the hop subelement for?", "is the hop subelement for?", "the hop subelement for?", "hop subelement for?", "subelement for?", "for?"], "tokens": ["What", "is", "the", "hop", "subelement", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-347798518", "fields": {"term": "What is a routing table?", "namespace": "open-p", "hash": -347798518, "terms": ["What is a routing table?", "is a routing table?", "a routing table?", "routing table?", "table?"], "tokens": ["What", "is", "a", "routing", "table", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::580629836", "fields": {"term": "What is a route?", "namespace": "open-p", "hash": 580629836, "terms": ["What is a route?", "is a route?", "a route?", "route?"], "tokens": ["What", "is", "a", "route", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-620817642", "fields": {"term": "What attributes does a route have?", "namespace": "open-p", "hash": -620817642, "terms": ["What attributes does a route have?", "attributes does a route have?", "does a route have?", "a route have?", "route have?", "have?"], "tokens": ["What", "attributes", "does", "a", "route", "have", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1701764525", "fields": {"term": "What happens if a hop does not exist?", "namespace": "open-p", "hash": 1701764525, "terms": ["What happens if a hop does not exist?", "happens if a hop does not exist?", "if a hop does not exist?", "a hop does not exist?", "hop does not exist?", "does not exist?", "not exist?", "exist?"], "tokens": ["What", "happens", "if", "a", "hop", "does", "not", "exist", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::768166322", "fields": {"term": "What is the required attribute for a hop?", "namespace": "open-p", "hash": 768166322, "terms": ["What is the required attribute for a hop?", "is the required attribute for a hop?", "the required attribute for a hop?", "required attribute for a hop?", "attribute for a hop?", "for a hop?", "a hop?", "hop?"], "tokens": ["What", "is", "the", "required", "attribute", "for", "a", "hop", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::287958008", "fields": {"term": "What is selector?", "namespace": "open-p", "hash": 287958008, "terms": ["What is selector?", "is selector?", "selector?"], "tokens": ["What", "is", "selector", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1936724397", "fields": {"term": "What is ignore-result?", "namespace": "open-p", "hash": -1936724397, "terms": ["What is ignore-result?", "is ignore-result?", "ignore-result?"], "tokens": ["What", "is", "ignore", "result", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::585958148", "fields": {"term": "What is the example hop?", "namespace": "open-p", "hash": 585958148, "terms": ["What is the example hop?", "is the example hop?", "the example hop?", "example hop?", "hop?"], "tokens": ["What", "is", "the", "example", "hop", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1746368010", "fields": {"term": "What is the example of recipient?", "namespace": "open-p", "hash": -1746368010, "terms": ["What is the example of recipient?", "is the example of recipient?", "the example of recipient?", "example of recipient?", "of recipient?", "recipient?"], "tokens": ["What", "is", "the", "example", "of", "recipient", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1353032068", "fields": {"term": "What is a recipient session?", "namespace": "open-p", "hash": 1353032068, "terms": ["What is a recipient session?", "is a recipient session?", "a recipient session?", "recipient session?", "session?"], "tokens": ["What", "is", "a", "recipient", "session", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::103152905", "fields": {"term": "What is the protocol defined?", "namespace": "open-p", "hash": 103152905, "terms": ["What is the protocol defined?", "is the protocol defined?", "the protocol defined?", "protocol defined?", "defined?"], "tokens": ["What", "is", "the", "protocol", "defined", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-656079559", "fields": {"term": "What is the purpose of services in routing?", "namespace": "open-p", "hash": -656079559, "terms": ["What is the purpose of services in routing?", "is the purpose of services in routing?", "the purpose of services in routing?", "purpose of services in routing?", "of services in routing?", "services in routing?", "in routing?", "routing?"], "tokens": ["What", "is", "the", "purpose", "of", "services", "in", "routing", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2058824483", "fields": {"term": "What is the required attribute in services?", "namespace": "open-p", "hash": -2058824483, "terms": ["What is the required attribute in services?", "is the required attribute in services?", "the required attribute in services?", "required attribute in services?", "attribute in services?", "in services?", "services?"], "tokens": ["What", "is", "the", "required", "attribute", "in", "services", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1057917311", "fields": {"term": "What is the name attribute in services?", "namespace": "open-p", "hash": -1057917311, "terms": ["What is the name attribute in services?", "is the name attribute in services?", "the name attribute in services?", "name attribute in services?", "attribute in services?", "in services?", "services?"], "tokens": ["What", "is", "the", "name", "attribute", "in", "services", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1345094746", "fields": {"term": "What is the default binding for SearchHandler?", "namespace": "open-p", "hash": -1345094746, "terms": ["What is the default binding for SearchHandler?", "is the default binding for SearchHandler?", "the default binding for SearchHandler?", "default binding for SearchHandler?", "binding for SearchHandler?", "for SearchHandler?", "SearchHandler?"], "tokens": ["What", "is", "the", "default", "binding", "for", "SearchHandler", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1403592166", "fields": {"term": "What is the URI for SearchHandler binding?", "namespace": "open-p", "hash": -1403592166, "terms": ["What is the URI for SearchHandler binding?", "is the URI for SearchHandler binding?", "the URI for SearchHandler binding?", "URI for SearchHandler binding?", "for SearchHandler binding?", "SearchHandler binding?", "binding?"], "tokens": ["What", "is", "the", "URI", "for", "SearchHandler", "binding", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1794303419", "fields": {"term": "What is a searcher element?", "namespace": "open-p", "hash": -1794303419, "terms": ["What is a searcher element?", "is a searcher element?", "a searcher element?", "searcher element?", "element?"], "tokens": ["What", "is", "a", "searcher", "element", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2099474585", "fields": {"term": "What is a searcher definition?", "namespace": "open-p", "hash": -2099474585, "terms": ["What is a searcher definition?", "is a searcher definition?", "a searcher definition?", "searcher definition?", "definition?"], "tokens": ["What", "is", "a", "searcher", "definition", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::-1163134115", "fields": {"term": "What is a searcher reference?", "namespace": "open-p", "hash": -1163134115, "terms": ["What is a searcher reference?", "is a searcher reference?", "a searcher reference?", "searcher reference?", "reference?"], "tokens": ["What", "is", "a", "searcher", "reference", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::392487488", "fields": {"term": "What are the attributes of a searcher definition?", "namespace": "open-p", "hash": 392487488, "terms": ["What are the attributes of a searcher definition?", "are the attributes of a searcher definition?", "the attributes of a searcher definition?", "attributes of a searcher definition?", "of a searcher definition?", "a searcher definition?", "searcher definition?", "definition?"], "tokens": ["What", "are", "the", "attributes", "of", "a", "searcher", "definition", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::34126819", "fields": {"term": "What is the difference between an inner and outer searcher?", "namespace": "open-p", "hash": 34126819, "terms": ["What is the difference between an inner and outer searcher?", "is the difference between an inner and outer searcher?", "the difference between an inner and outer searcher?", "difference between an inner and outer searcher?", "between an inner and outer searcher?", "an inner and outer searcher?", "inner and outer searcher?", "and outer searcher?", "outer searcher?", "searcher?"], "tokens": ["What", "is", "the", "difference", "between", "an", "inner", "and", "outer", "searcher", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-28865099", "fields": {"term": "What is a federation?", "namespace": "open-p", "hash": -28865099, "terms": ["What is a federation?", "is a federation?", "a federation?", "federation?"], "tokens": ["What", "is", "a", "federation", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1866308111", "fields": {"term": "What is a target-selector?", "namespace": "open-p", "hash": 1866308111, "terms": ["What is a target-selector?", "is a target-selector?", "a target-selector?", "target-selector?"], "tokens": ["What", "is", "a", "target", "selector", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::495525043", "fields": {"term": "What is TargetSelector?", "namespace": "open-p", "hash": 495525043, "terms": ["What is TargetSelector?", "is TargetSelector?", "TargetSelector?"], "tokens": ["What", "is", "TargetSelector", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-980861139", "fields": {"term": "What must a component inherit from to select search chains in Vespa?", "namespace": "open-p", "hash": -980861139, "terms": ["What must a component inherit from to select search chains in Vespa?", "must a component inherit from to select search chains in Vespa?", "a component inherit from to select search chains in Vespa?", "component inherit from to select search chains in Vespa?", "inherit from to select search chains in Vespa?", "from to select search chains in Vespa?", "to select search chains in Vespa?", "select search chains in Vespa?", "search chains in Vespa?", "chains in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["What", "must", "a", "component", "inherit", "from", "to", "select", "search", "chains", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::119812490", "fields": {"term": "What is 'combinator'?", "namespace": "open-p", "hash": 119812490, "terms": ["What is 'combinator'?", "is 'combinator'?", "'combinator'?"], "tokens": ["What", "is", "'", "combinator", "'?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::755274551", "fields": {"term": "What is source idref?", "namespace": "open-p", "hash": 755274551, "terms": ["What is source idref?", "is source idref?", "source idref?", "idref?"], "tokens": ["What", "is", "source", "idref", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-206241403", "fields": {"term": "What is the default option for optional?", "namespace": "open-p", "hash": -206241403, "terms": ["What is the default option for optional?", "is the default option for optional?", "the default option for optional?", "default option for optional?", "option for optional?", "for optional?", "optional?"], "tokens": ["What", "is", "the", "default", "option", "for", "optional", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1634794843", "fields": {"term": "What is the purpose of requestTimeout?", "namespace": "open-p", "hash": 1634794843, "terms": ["What is the purpose of requestTimeout?", "is the purpose of requestTimeout?", "the purpose of requestTimeout?", "purpose of requestTimeout?", "of requestTimeout?", "requestTimeout?"], "tokens": ["What", "is", "the", "purpose", "of", "requestTimeout", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-539929442", "fields": {"term": "What happens when a federation option overrides another?", "namespace": "open-p", "hash": -539929442, "terms": ["What happens when a federation option overrides another?", "happens when a federation option overrides another?", "when a federation option overrides another?", "a federation option overrides another?", "federation option overrides another?", "option overrides another?", "overrides another?", "another?"], "tokens": ["What", "happens", "when", "a", "federation", "option", "overrides", "another", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1433703399", "fields": {"term": "How does a source reference affect federation options?", "namespace": "open-p", "hash": 1433703399, "terms": ["How does a source reference affect federation options?", "does a source reference affect federation options?", "a source reference affect federation options?", "source reference affect federation options?", "reference affect federation options?", "affect federation options?", "federation options?", "options?"], "tokens": ["How", "does", "a", "source", "reference", "affect", "federation", "options", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-761529133", "fields": {"term": "What is a search result renderer?", "namespace": "open-p", "hash": -761529133, "terms": ["What is a search result renderer?", "is a search result renderer?", "a search result renderer?", "search result renderer?", "result renderer?", "renderer?"], "tokens": ["What", "is", "a", "search", "result", "renderer", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::52980960", "fields": {"term": "What is the required attribute for search result renderer?", "namespace": "open-p", "hash": 52980960, "terms": ["What is the required attribute for search result renderer?", "is the required attribute for search result renderer?", "the required attribute for search result renderer?", "required attribute for search result renderer?", "attribute for search result renderer?", "for search result renderer?", "search result renderer?", "result renderer?", "renderer?"], "tokens": ["What", "is", "the", "required", "attribute", "for", "search", "result", "renderer", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::111373902", "fields": {"term": "What is the optional attribute for search result renderer?", "namespace": "open-p", "hash": 111373902, "terms": ["What is the optional attribute for search result renderer?", "is the optional attribute for search result renderer?", "the optional attribute for search result renderer?", "optional attribute for search result renderer?", "attribute for search result renderer?", "for search result renderer?", "search result renderer?", "result renderer?", "renderer?"], "tokens": ["What", "is", "the", "optional", "attribute", "for", "search", "result", "renderer", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1188155313", "fields": {"term": "What is the default value for 'class' attribute?", "namespace": "open-p", "hash": -1188155313, "terms": ["What is the default value for 'class' attribute?", "is the default value for 'class' attribute?", "the default value for 'class' attribute?", "default value for 'class' attribute?", "value for 'class' attribute?", "for 'class' attribute?", "'class' attribute?", "attribute?"], "tokens": ["What", "is", "the", "default", "value", "for", "'", "class", "'", "attribute", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::858291295", "fields": {"term": "What is the bundle attribute for search result renderer?", "namespace": "open-p", "hash": 858291295, "terms": ["What is the bundle attribute for search result renderer?", "is the bundle attribute for search result renderer?", "the bundle attribute for search result renderer?", "bundle attribute for search result renderer?", "attribute for search result renderer?", "for search result renderer?", "search result renderer?", "result renderer?", "renderer?"], "tokens": ["What", "is", "the", "bundle", "attribute", "for", "search", "result", "renderer", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::803448630", "fields": {"term": "What is the format for specifying renderer in XML?", "namespace": "open-p", "hash": 803448630, "terms": ["What is the format for specifying renderer in XML?", "is the format for specifying renderer in XML?", "the format for specifying renderer in XML?", "format for specifying renderer in XML?", "for specifying renderer in XML?", "specifying renderer in XML?", "renderer in XML?", "in XML?", "XML?"], "tokens": ["What", "is", "the", "format", "for", "specifying", "renderer", "in", "XML", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1800908904", "fields": {"term": "What is the value of 'class' attribute if not provided?", "namespace": "open-p", "hash": 1800908904, "terms": ["What is the value of 'class' attribute if not provided?", "is the value of 'class' attribute if not provided?", "the value of 'class' attribute if not provided?", "value of 'class' attribute if not provided?", "of 'class' attribute if not provided?", "'class' attribute if not provided?", "attribute if not provided?", "if not provided?", "not provided?", "provided?"], "tokens": ["What", "is", "the", "value", "of", "'", "class", "'", "attribute", "if", "not", "provided", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1667621370", "fields": {"term": "How can searchers be ordered?", "namespace": "open-p", "hash": 1667621370, "terms": ["How can searchers be ordered?", "can searchers be ordered?", "searchers be ordered?", "be ordered?", "ordered?"], "tokens": ["How", "can", "searchers", "be", "ordered", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1190348694", "fields": {"term": "What are sub-elements of a chain?", "namespace": "open-p", "hash": -1190348694, "terms": ["What are sub-elements of a chain?", "are sub-elements of a chain?", "sub-elements of a chain?", "of a chain?", "a chain?", "chain?"], "tokens": ["What", "are", "sub", "elements", "of", "a", "chain", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::139339602", "fields": {"term": "Can search config be in separate files?", "namespace": "open-p", "hash": 139339602, "terms": ["Can search config be in separate files?", "search config be in separate files?", "config be in separate files?", "be in separate files?", "in separate files?", "separate files?", "files?"], "tokens": ["Can", "search", "config", "be", "in", "separate", "files", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1109159184", "fields": {"term": "Can searcher elements have the same name?", "namespace": "open-p", "hash": -1109159184, "terms": ["Can searcher elements have the same name?", "searcher elements have the same name?", "elements have the same name?", "have the same name?", "the same name?", "same name?", "name?"], "tokens": ["Can", "searcher", "elements", "have", "the", "same", "name", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1293948649", "fields": {"term": "What is 'excludes' used for?", "namespace": "open-p", "hash": 1293948649, "terms": ["What is 'excludes' used for?", "is 'excludes' used for?", "'excludes' used for?", "used for?", "for?"], "tokens": ["What", "is", "'", "excludes", "'", "used", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-406449167", "fields": {"term": "What are local providers?", "namespace": "open-p", "hash": -406449167, "terms": ["What are local providers?", "are local providers?", "local providers?", "providers?"], "tokens": ["What", "are", "local", "providers", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1147878583", "fields": {"term": "What is a local Vespa cluster?", "namespace": "open-p", "hash": -1147878583, "terms": ["What is a local Vespa cluster?", "is a local Vespa cluster?", "a local Vespa cluster?", "local Vespa cluster?", "Vespa cluster?", "cluster?"], "tokens": ["What", "is", "a", "local", "Vespa", "cluster", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::468661589", "fields": {"term": "How do you define a local provider?", "namespace": "open-p", "hash": 468661589, "terms": ["How do you define a local provider?", "do you define a local provider?", "you define a local provider?", "define a local provider?", "a local provider?", "local provider?", "provider?"], "tokens": ["How", "do", "you", "define", "a", "local", "provider", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1459594195", "fields": {"term": "What is an http provider?", "namespace": "open-p", "hash": 1459594195, "terms": ["What is an http provider?", "is an http provider?", "an http provider?", "http provider?", "provider?"], "tokens": ["What", "is", "an", "http", "provider", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1220043652", "fields": {"term": "What are http providers used for?", "namespace": "open-p", "hash": 1220043652, "terms": ["What are http providers used for?", "are http providers used for?", "http providers used for?", "providers used for?", "used for?", "for?"], "tokens": ["What", "are", "http", "providers", "used", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1346125932", "fields": {"term": "What is read timeout for?", "namespace": "open-p", "hash": -1346125932, "terms": ["What is read timeout for?", "is read timeout for?", "read timeout for?", "timeout for?", "for?"], "tokens": ["What", "is", "read", "timeout", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-949792695", "fields": {"term": "What is connection timeout for?", "namespace": "open-p", "hash": -949792695, "terms": ["What is connection timeout for?", "is connection timeout for?", "connection timeout for?", "timeout for?", "for?"], "tokens": ["What", "is", "connection", "timeout", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1338670784", "fields": {"term": "What is connection pool timeout for?", "namespace": "open-p", "hash": -1338670784, "terms": ["What is connection pool timeout for?", "is connection pool timeout for?", "connection pool timeout for?", "pool timeout for?", "timeout for?", "for?"], "tokens": ["What", "is", "connection", "pool", "timeout", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2111333406", "fields": {"term": "What is the retries attribute for?", "namespace": "open-p", "hash": 2111333406, "terms": ["What is the retries attribute for?", "is the retries attribute for?", "the retries attribute for?", "retries attribute for?", "attribute for?", "for?"], "tokens": ["What", "is", "the", "retries", "attribute", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-802247733", "fields": {"term": "What is a leader search chain?", "namespace": "open-p", "hash": -802247733, "terms": ["What is a leader search chain?", "is a leader search chain?", "a leader search chain?", "leader search chain?", "search chain?", "chain?"], "tokens": ["What", "is", "a", "leader", "search", "chain", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-782861940", "fields": {"term": "What is a participant search chain?", "namespace": "open-p", "hash": -782861940, "terms": ["What is a participant search chain?", "is a participant search chain?", "a participant search chain?", "participant search chain?", "search chain?", "chain?"], "tokens": ["What", "is", "a", "participant", "search", "chain", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1988650269", "fields": {"term": "How can a participant search chain be used?", "namespace": "open-p", "hash": 1988650269, "terms": ["How can a participant search chain be used?", "can a participant search chain be used?", "a participant search chain be used?", "participant search chain be used?", "search chain be used?", "chain be used?", "be used?", "used?"], "tokens": ["How", "can", "a", "participant", "search", "chain", "be", "used", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1041706780", "fields": {"term": "How does a source search chain behave?", "namespace": "open-p", "hash": 1041706780, "terms": ["How does a source search chain behave?", "does a source search chain behave?", "a source search chain behave?", "source search chain behave?", "search chain behave?", "chain behave?", "behave?"], "tokens": ["How", "does", "a", "source", "search", "chain", "behave", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1044248528", "fields": {"term": "What are node elements?", "namespace": "open-p", "hash": 1044248528, "terms": ["What are node elements?", "are node elements?", "node elements?", "elements?"], "tokens": ["What", "are", "node", "elements", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::581039432", "fields": {"term": "What attributes are required for endpoint specification?", "namespace": "open-p", "hash": 581039432, "terms": ["What attributes are required for endpoint specification?", "attributes are required for endpoint specification?", "are required for endpoint specification?", "required for endpoint specification?", "for endpoint specification?", "endpoint specification?", "specification?"], "tokens": ["What", "attributes", "are", "required", "for", "endpoint", "specification", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1270500278", "fields": {"term": "How is the threadpool configured?", "namespace": "open-p", "hash": -1270500278, "terms": ["How is the threadpool configured?", "is the threadpool configured?", "the threadpool configured?", "threadpool configured?", "configured?"], "tokens": ["How", "is", "the", "threadpool", "configured", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1711943745", "fields": {"term": "What happens when the request queue is full?", "namespace": "open-p", "hash": -1711943745, "terms": ["What happens when the request queue is full?", "happens when the request queue is full?", "when the request queue is full?", "the request queue is full?", "request queue is full?", "queue is full?", "is full?", "full?"], "tokens": ["What", "happens", "when", "the", "request", "queue", "is", "full", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1606958740", "fields": {"term": "How are threads created in the pool?", "namespace": "open-p", "hash": -1606958740, "terms": ["How are threads created in the pool?", "are threads created in the pool?", "threads created in the pool?", "created in the pool?", "in the pool?", "the pool?", "pool?"], "tokens": ["How", "are", "threads", "created", "in", "the", "pool", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-963842442", "fields": {"term": "What is the maximum thread count?", "namespace": "open-p", "hash": -963842442, "terms": ["What is the maximum thread count?", "is the maximum thread count?", "the maximum thread count?", "maximum thread count?", "thread count?", "count?"], "tokens": ["What", "is", "the", "maximum", "thread", "count", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1288718671", "fields": {"term": "What is a thread pool?", "namespace": "open-p", "hash": 1288718671, "terms": ["What is a thread pool?", "is a thread pool?", "a thread pool?", "thread pool?", "pool?"], "tokens": ["What", "is", "a", "thread", "pool", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-116207794", "fields": {"term": "What is the thread pool?", "namespace": "open-p", "hash": -116207794, "terms": ["What is the thread pool?", "is the thread pool?", "the thread pool?", "thread pool?", "pool?"], "tokens": ["What", "is", "the", "thread", "pool", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1162018967", "fields": {"term": "What is the minimum number?", "namespace": "open-p", "hash": 1162018967, "terms": ["What is the minimum number?", "is the minimum number?", "the minimum number?", "minimum number?", "number?"], "tokens": ["What", "is", "the", "minimum", "number", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::605884920", "fields": {"term": "What is request queue size?", "namespace": "open-p", "hash": 605884920, "terms": ["What is request queue size?", "is request queue size?", "request queue size?", "queue size?", "size?"], "tokens": ["What", "is", "request", "queue", "size", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1890337319", "fields": {"term": "How to configure request queue size?", "namespace": "open-p", "hash": 1890337319, "terms": ["How to configure request queue size?", "to configure request queue size?", "configure request queue size?", "request queue size?", "queue size?", "size?"], "tokens": ["How", "to", "configure", "request", "queue", "size", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1709072261", "fields": {"term": "What are the subelements of Vespa?", "namespace": "open-p", "hash": 1709072261, "terms": ["What are the subelements of Vespa?", "are the subelements of Vespa?", "the subelements of Vespa?", "subelements of Vespa?", "of Vespa?", "Vespa?"], "tokens": ["What", "are", "the", "subelements", "of", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1605092745", "fields": {"term": "What is a config element?", "namespace": "open-p", "hash": 1605092745, "terms": ["What is a config element?", "is a config element?", "a config element?", "config element?", "element?"], "tokens": ["What", "is", "a", "config", "element", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1254701883", "fields": {"term": "What is an example of generic configuration?", "namespace": "open-p", "hash": -1254701883, "terms": ["What is an example of generic configuration?", "is an example of generic configuration?", "an example of generic configuration?", "example of generic configuration?", "of generic configuration?", "generic configuration?", "configuration?"], "tokens": ["What", "is", "an", "example", "of", "generic", "configuration", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1050970985", "fields": {"term": "What types of elements accept config?", "namespace": "open-p", "hash": 1050970985, "terms": ["What types of elements accept config?", "types of elements accept config?", "of elements accept config?", "elements accept config?", "accept config?", "config?"], "tokens": ["What", "types", "of", "elements", "accept", "config", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2130046980", "fields": {"term": "How are config elements prioritized?", "namespace": "open-p", "hash": -2130046980, "terms": ["How are config elements prioritized?", "are config elements prioritized?", "config elements prioritized?", "elements prioritized?", "prioritized?"], "tokens": ["How", "are", "config", "elements", "prioritized", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::173790698", "fields": {"term": "What does a config element override?", "namespace": "open-p", "hash": 173790698, "terms": ["What does a config element override?", "does a config element override?", "a config element override?", "config element override?", "element override?", "override?"], "tokens": ["What", "does", "a", "config", "element", "override", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1241071184", "fields": {"term": "What files are used for configuration?", "namespace": "open-p", "hash": -1241071184, "terms": ["What files are used for configuration?", "files are used for configuration?", "are used for configuration?", "used for configuration?", "for configuration?", "configuration?"], "tokens": ["What", "files", "are", "used", "for", "configuration", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1448818087", "fields": {"term": "What are the supported services?", "namespace": "open-p", "hash": -1448818087, "terms": ["What are the supported services?", "are the supported services?", "the supported services?", "supported services?", "services?"], "tokens": ["What", "are", "the", "supported", "services", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-212041692", "fields": {"term": "Where can XML files be found?", "namespace": "open-p", "hash": -212041692, "terms": ["Where can XML files be found?", "can XML files be found?", "XML files be found?", "files be found?", "be found?", "found?"], "tokens": ["Where", "can", "XML", "files", "be", "found", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1420299699", "fields": {"term": "What is the simple query language?", "namespace": "open-p", "hash": 1420299699, "terms": ["What is the simple query language?", "is the simple query language?", "the simple query language?", "simple query language?", "query language?", "language?"], "tokens": ["What", "is", "the", "simple", "query", "language", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1035906416", "fields": {"term": "What types are available in simple query language?", "namespace": "open-p", "hash": -1035906416, "terms": ["What types are available in simple query language?", "types are available in simple query language?", "are available in simple query language?", "available in simple query language?", "in simple query language?", "simple query language?", "query language?", "language?"], "tokens": ["What", "types", "are", "available", "in", "simple", "query", "language", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1687360412", "fields": {"term": "What is the difference between tokenize and web types?", "namespace": "open-p", "hash": -1687360412, "terms": ["What is the difference between tokenize and web types?", "is the difference between tokenize and web types?", "the difference between tokenize and web types?", "difference between tokenize and web types?", "between tokenize and web types?", "tokenize and web types?", "and web types?", "web types?", "types?"], "tokens": ["What", "is", "the", "difference", "between", "tokenize", "and", "web", "types", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-663903408", "fields": {"term": "What is Expr?", "namespace": "open-p", "hash": -663903408, "terms": ["What is Expr?", "is Expr?", "Expr?"], "tokens": ["What", "is", "Expr", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1960176301", "fields": {"term": "What is SimpleTerm?", "namespace": "open-p", "hash": -1960176301, "terms": ["What is SimpleTerm?", "is SimpleTerm?", "SimpleTerm?"], "tokens": ["What", "is", "SimpleTerm", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1634716975", "fields": {"term": "What is Prefix?", "namespace": "open-p", "hash": -1634716975, "terms": ["What is Prefix?", "is Prefix?", "Prefix?"], "tokens": ["What", "is", "Prefix", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1676574051", "fields": {"term": "What is CoreTerm?", "namespace": "open-p", "hash": -1676574051, "terms": ["What is CoreTerm?", "is CoreTerm?", "CoreTerm?"], "tokens": ["What", "is", "CoreTerm", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1547433662", "fields": {"term": "What is NumTerm?", "namespace": "open-p", "hash": 1547433662, "terms": ["What is NumTerm?", "is NumTerm?", "NumTerm?"], "tokens": ["What", "is", "NumTerm", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1756241057", "fields": {"term": "What is prefix matching?", "namespace": "open-p", "hash": 1756241057, "terms": ["What is prefix matching?", "is prefix matching?", "prefix matching?", "matching?"], "tokens": ["What", "is", "prefix", "matching", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-440521127", "fields": {"term": "What is a prefix search term?", "namespace": "open-p", "hash": -440521127, "terms": ["What is a prefix search term?", "is a prefix search term?", "a prefix search term?", "prefix search term?", "search term?", "term?"], "tokens": ["What", "is", "a", "prefix", "search", "term", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1657573498", "fields": {"term": "What is a weight expression?", "namespace": "open-p", "hash": -1657573498, "terms": ["What is a weight expression?", "is a weight expression?", "a weight expression?", "weight expression?", "expression?"], "tokens": ["What", "is", "a", "weight", "expression", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1319876973", "fields": {"term": "How are weights calculated?", "namespace": "open-p", "hash": 1319876973, "terms": ["How are weights calculated?", "are weights calculated?", "weights calculated?", "calculated?"], "tokens": ["How", "are", "weights", "calculated", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1631819614", "fields": {"term": "What is a term weight?", "namespace": "open-p", "hash": -1631819614, "terms": ["What is a term weight?", "is a term weight?", "a term weight?", "term weight?", "weight?"], "tokens": ["What", "is", "a", "term", "weight", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1304737199", "fields": {"term": "What is the purpose of term scoring?", "namespace": "open-p", "hash": -1304737199, "terms": ["What is the purpose of term scoring?", "is the purpose of term scoring?", "the purpose of term scoring?", "purpose of term scoring?", "of term scoring?", "term scoring?", "scoring?"], "tokens": ["What", "is", "the", "purpose", "of", "term", "scoring", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-665377175", "fields": {"term": "How can weight be added to important terms?", "namespace": "open-p", "hash": -665377175, "terms": ["How can weight be added to important terms?", "can weight be added to important terms?", "weight be added to important terms?", "be added to important terms?", "added to important terms?", "to important terms?", "important terms?", "terms?"], "tokens": ["How", "can", "weight", "be", "added", "to", "important", "terms", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::696435210", "fields": {"term": "What is the default weight value?", "namespace": "open-p", "hash": 696435210, "terms": ["What is the default weight value?", "is the default weight value?", "the default weight value?", "default weight value?", "weight value?", "value?"], "tokens": ["What", "is", "the", "default", "weight", "value", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-984248891", "fields": {"term": "What is the rank calculation?", "namespace": "open-p", "hash": -984248891, "terms": ["What is the rank calculation?", "is the rank calculation?", "the rank calculation?", "rank calculation?", "calculation?"], "tokens": ["What", "is", "the", "rank", "calculation", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2131635934", "fields": {"term": "What does [x;y] do?", "namespace": "open-p", "hash": 2131635934, "terms": ["What does [x;y] do?", "does [x;y] do?", "[x;y] do?", "do?"], "tokens": ["What", "does", "[", "x", ";", "y", "]", "do", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1804241495", "fields": {"term": "How can we use number in chess kasparov query?", "namespace": "open-p", "hash": -1804241495, "terms": ["How can we use number in chess kasparov query?", "can we use number in chess kasparov query?", "we use number in chess kasparov query?", "use number in chess kasparov query?", "number in chess kasparov query?", "in chess kasparov query?", "chess kasparov query?", "kasparov query?", "query?"], "tokens": ["How", "can", "we", "use", "number", "in", "chess", "kasparov", "query", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2112583704", "fields": {"term": "How does capped range search work?", "namespace": "open-p", "hash": -2112583704, "terms": ["How does capped range search work?", "does capped range search work?", "capped range search work?", "range search work?", "search work?", "work?"], "tokens": ["How", "does", "capped", "range", "search", "work", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-384791819", "fields": {"term": "What is the syntax for capped range search?", "namespace": "open-p", "hash": -384791819, "terms": ["What is the syntax for capped range search?", "is the syntax for capped range search?", "the syntax for capped range search?", "syntax for capped range search?", "for capped range search?", "capped range search?", "range search?", "search?"], "tokens": ["What", "is", "the", "syntax", "for", "capped", "range", "search", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-57265565", "fields": {"term": "How many documents are fetched using capped range search?", "namespace": "open-p", "hash": -57265565, "terms": ["How many documents are fetched using capped range search?", "many documents are fetched using capped range search?", "documents are fetched using capped range search?", "are fetched using capped range search?", "fetched using capped range search?", "using capped range search?", "capped range search?", "range search?", "search?"], "tokens": ["How", "many", "documents", "are", "fetched", "using", "capped", "range", "search", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1603252866", "fields": {"term": "Which attribute is used for range search?", "namespace": "open-p", "hash": -1603252866, "terms": ["Which attribute is used for range search?", "attribute is used for range search?", "is used for range search?", "used for range search?", "for range search?", "range search?", "search?"], "tokens": ["Which", "attribute", "is", "used", "for", "range", "search", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1573331419", "fields": {"term": "Why is capped range search efficient?", "namespace": "open-p", "hash": -1573331419, "terms": ["Why is capped range search efficient?", "is capped range search efficient?", "capped range search efficient?", "range search efficient?", "search efficient?", "efficient?"], "tokens": ["Why", "is", "capped", "range", "search", "efficient", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2073172809", "fields": {"term": "What does a negative number do in capped range search?", "namespace": "open-p", "hash": -2073172809, "terms": ["What does a negative number do in capped range search?", "does a negative number do in capped range search?", "a negative number do in capped range search?", "negative number do in capped range search?", "number do in capped range search?", "do in capped range search?", "in capped range search?", "capped range search?", "range search?", "search?"], "tokens": ["What", "does", "a", "negative", "number", "do", "in", "capped", "range", "search", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1640002165", "fields": {"term": "How many levels of parentheses?", "namespace": "open-p", "hash": -1640002165, "terms": ["How many levels of parentheses?", "many levels of parentheses?", "levels of parentheses?", "of parentheses?", "parentheses?"], "tokens": ["How", "many", "levels", "of", "parentheses", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::246752706", "fields": {"term": "What happens with extra parentheses?", "namespace": "open-p", "hash": 246752706, "terms": ["What happens with extra parentheses?", "happens with extra parentheses?", "with extra parentheses?", "extra parentheses?", "parentheses?"], "tokens": ["What", "happens", "with", "extra", "parentheses", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-154208350", "fields": {"term": "What does '+' do with parentheses?", "namespace": "open-p", "hash": -154208350, "terms": ["What does '+' do with parentheses?", "does '+' do with parentheses?", "'+' do with parentheses?", "do with parentheses?", "with parentheses?", "parentheses?"], "tokens": ["What", "does", "'+'", "do", "with", "parentheses", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1809604064", "fields": {"term": "What does '-' do with parentheses?", "namespace": "open-p", "hash": 1809604064, "terms": ["What does '-' do with parentheses?", "does '-' do with parentheses?", "'-' do with parentheses?", "do with parentheses?", "with parentheses?", "parentheses?"], "tokens": ["What", "does", "'", "'", "do", "with", "parentheses", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-920370181", "fields": {"term": "How can '+' be excluded?", "namespace": "open-p", "hash": -920370181, "terms": ["How can '+' be excluded?", "can '+' be excluded?", "'+' be excluded?", "be excluded?", "excluded?"], "tokens": ["How", "can", "'+'", "be", "excluded", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1093857654", "fields": {"term": "When to use '-' with parentheses?", "namespace": "open-p", "hash": -1093857654, "terms": ["When to use '-' with parentheses?", "to use '-' with parentheses?", "use '-' with parentheses?", "'-' with parentheses?", "with parentheses?", "parentheses?"], "tokens": ["When", "to", "use", "'", "'", "with", "parentheses", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1877155832", "fields": {"term": "What is a URL-field?", "namespace": "open-p", "hash": 1877155832, "terms": ["What is a URL-field?", "is a URL-field?", "a URL-field?", "URL-field?"], "tokens": ["What", "is", "a", "URL", "field", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-397675504", "fields": {"term": "How to create a URL-field?", "namespace": "open-p", "hash": -397675504, "terms": ["How to create a URL-field?", "to create a URL-field?", "create a URL-field?", "a URL-field?", "URL-field?"], "tokens": ["How", "to", "create", "a", "URL", "field", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1887964358", "fields": {"term": "What happens with invalid URLs?", "namespace": "open-p", "hash": -1887964358, "terms": ["What happens with invalid URLs?", "happens with invalid URLs?", "with invalid URLs?", "invalid URLs?", "URLs?"], "tokens": ["What", "happens", "with", "invalid", "URLs", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1938696633", "fields": {"term": "How does substring matching work?", "namespace": "open-p", "hash": 1938696633, "terms": ["How does substring matching work?", "does substring matching work?", "substring matching work?", "matching work?", "work?"], "tokens": ["How", "does", "substring", "matching", "work", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1930594618", "fields": {"term": "Is hostname search anchored by default?", "namespace": "open-p", "hash": 1930594618, "terms": ["Is hostname search anchored by default?", "hostname search anchored by default?", "search anchored by default?", "anchored by default?", "by default?", "default?"], "tokens": ["Is", "hostname", "search", "anchored", "by", "default", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::17859751", "fields": {"term": "How to anchor hostname search at the start?", "namespace": "open-p", "hash": 17859751, "terms": ["How to anchor hostname search at the start?", "to anchor hostname search at the start?", "anchor hostname search at the start?", "hostname search at the start?", "search at the start?", "at the start?", "the start?", "start?"], "tokens": ["How", "to", "anchor", "hostname", "search", "at", "the", "start", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1499886391", "fields": {"term": "How to turn off anchoring at the end?", "namespace": "open-p", "hash": 1499886391, "terms": ["How to turn off anchoring at the end?", "to turn off anchoring at the end?", "turn off anchoring at the end?", "off anchoring at the end?", "anchoring at the end?", "at the end?", "the end?", "end?"], "tokens": ["How", "to", "turn", "off", "anchoring", "at", "the", "end", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2025527367", "fields": {"term": "What is the syntax for streaming search?", "namespace": "open-p", "hash": -2025527367, "terms": ["What is the syntax for streaming search?", "is the syntax for streaming search?", "the syntax for streaming search?", "syntax for streaming search?", "for streaming search?", "streaming search?", "search?"], "tokens": ["What", "is", "the", "syntax", "for", "streaming", "search", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1828550032", "fields": {"term": "What is the use case for map data types?", "namespace": "open-p", "hash": -1828550032, "terms": ["What is the use case for map data types?", "is the use case for map data types?", "the use case for map data types?", "use case for map data types?", "case for map data types?", "for map data types?", "map data types?", "data types?", "types?"], "tokens": ["What", "is", "the", "use", "case", "for", "map", "data", "types", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-143660373", "fields": {"term": "Why remove characters from search terms?", "namespace": "open-p", "hash": -143660373, "terms": ["Why remove characters from search terms?", "remove characters from search terms?", "characters from search terms?", "from search terms?", "search terms?", "terms?"], "tokens": ["Why", "remove", "characters", "from", "search", "terms", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::886035921", "fields": {"term": "What is a safe replacement character?", "namespace": "open-p", "hash": 886035921, "terms": ["What is a safe replacement character?", "is a safe replacement character?", "a safe replacement character?", "safe replacement character?", "replacement character?", "character?"], "tokens": ["What", "is", "a", "safe", "replacement", "character", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1898414293", "fields": {"term": "How to quote URLs?", "namespace": "open-p", "hash": -1898414293, "terms": ["How to quote URLs?", "to quote URLs?", "quote URLs?", "URLs?"], "tokens": ["How", "to", "quote", "URLs", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1551979919", "fields": {"term": "How to handle parentheses in URLs?", "namespace": "open-p", "hash": -1551979919, "terms": ["How to handle parentheses in URLs?", "to handle parentheses in URLs?", "handle parentheses in URLs?", "parentheses in URLs?", "in URLs?", "URLs?"], "tokens": ["How", "to", "handle", "parentheses", "in", "URLs", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::384133606", "fields": {"term": "What is the simple query language syntax?", "namespace": "open-p", "hash": 384133606, "terms": ["What is the simple query language syntax?", "is the simple query language syntax?", "the simple query language syntax?", "simple query language syntax?", "query language syntax?", "language syntax?", "syntax?"], "tokens": ["What", "is", "the", "simple", "query", "language", "syntax", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2121210845", "fields": {"term": "How can phrases be searched?", "namespace": "open-p", "hash": 2121210845, "terms": ["How can phrases be searched?", "can phrases be searched?", "phrases be searched?", "be searched?", "searched?"], "tokens": ["How", "can", "phrases", "be", "searched", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1326667573", "fields": {"term": "What is ignored in noise?", "namespace": "open-p", "hash": 1326667573, "terms": ["What is ignored in noise?", "is ignored in noise?", "ignored in noise?", "in noise?", "noise?"], "tokens": ["What", "is", "ignored", "in", "noise", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-872694090", "fields": {"term": "What is the syntax to get documents with a specific word?", "namespace": "open-p", "hash": -872694090, "terms": ["What is the syntax to get documents with a specific word?", "is the syntax to get documents with a specific word?", "the syntax to get documents with a specific word?", "syntax to get documents with a specific word?", "to get documents with a specific word?", "get documents with a specific word?", "documents with a specific word?", "with a specific word?", "a specific word?", "specific word?", "word?"], "tokens": ["What", "is", "the", "syntax", "to", "get", "documents", "with", "a", "specific", "word", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::638324756", "fields": {"term": "How can phrases and words be excluded?", "namespace": "open-p", "hash": 638324756, "terms": ["How can phrases and words be excluded?", "can phrases and words be excluded?", "phrases and words be excluded?", "and words be excluded?", "words be excluded?", "be excluded?", "excluded?"], "tokens": ["How", "can", "phrases", "and", "words", "be", "excluded", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::683343865", "fields": {"term": "When to use flat or grouped data distribution?", "namespace": "open-p", "hash": 683343865, "terms": ["When to use flat or grouped data distribution?", "to use flat or grouped data distribution?", "use flat or grouped data distribution?", "flat or grouped data distribution?", "or grouped data distribution?", "grouped data distribution?", "data distribution?", "distribution?"], "tokens": ["When", "to", "use", "flat", "or", "grouped", "data", "distribution", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-210414689", "fields": {"term": "What do the examples illustrate?", "namespace": "open-p", "hash": -210414689, "terms": ["What do the examples illustrate?", "do the examples illustrate?", "the examples illustrate?", "examples illustrate?", "illustrate?"], "tokens": ["What", "do", "the", "examples", "illustrate", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1524829711", "fields": {"term": "How to get started from a deployable multinode starting point?", "namespace": "open-p", "hash": -1524829711, "terms": ["How to get started from a deployable multinode starting point?", "to get started from a deployable multinode starting point?", "get started from a deployable multinode starting point?", "started from a deployable multinode starting point?", "from a deployable multinode starting point?", "a deployable multinode starting point?", "deployable multinode starting point?", "multinode starting point?", "starting point?", "point?"], "tokens": ["How", "to", "get", "started", "from", "a", "deployable", "multinode", "starting", "point", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1332372747", "fields": {"term": "Can you provide an example of hosts.xml for testing deployments?", "namespace": "open-p", "hash": -1332372747, "terms": ["Can you provide an example of hosts.xml for testing deployments?", "you provide an example of hosts.xml for testing deployments?", "provide an example of hosts.xml for testing deployments?", "an example of hosts.xml for testing deployments?", "example of hosts.xml for testing deployments?", "of hosts.xml for testing deployments?", "hosts.xml for testing deployments?", "for testing deployments?", "testing deployments?", "deployments?"], "tokens": ["Can", "you", "provide", "an", "example", "of", "hosts", "xml", "for", "testing", "deployments", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1670413280", "fields": {"term": "What is min-redundancy?", "namespace": "open-p", "hash": 1670413280, "terms": ["What is min-redundancy?", "is min-redundancy?", "min-redundancy?"], "tokens": ["What", "is", "min", "redundancy", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2048016776", "fields": {"term": "What are the restrictions of grouped distribution?", "namespace": "open-p", "hash": 2048016776, "terms": ["What are the restrictions of grouped distribution?", "are the restrictions of grouped distribution?", "the restrictions of grouped distribution?", "restrictions of grouped distribution?", "of grouped distribution?", "grouped distribution?", "distribution?"], "tokens": ["What", "are", "the", "restrictions", "of", "grouped", "distribution", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1658031058", "fields": {"term": "What happens when a node fails in grouped distribution?", "namespace": "open-p", "hash": -1658031058, "terms": ["What happens when a node fails in grouped distribution?", "happens when a node fails in grouped distribution?", "when a node fails in grouped distribution?", "a node fails in grouped distribution?", "node fails in grouped distribution?", "fails in grouped distribution?", "in grouped distribution?", "grouped distribution?", "distribution?"], "tokens": ["What", "happens", "when", "a", "node", "fails", "in", "grouped", "distribution", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::841782196", "fields": {"term": "What are the groups?", "namespace": "open-p", "hash": 841782196, "terms": ["What are the groups?", "are the groups?", "the groups?", "groups?"], "tokens": ["What", "are", "the", "groups", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2077084457", "fields": {"term": "How many replicas are there?", "namespace": "open-p", "hash": -2077084457, "terms": ["How many replicas are there?", "many replicas are there?", "replicas are there?", "are there?", "there?"], "tokens": ["How", "many", "replicas", "are", "there", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::689768872", "fields": {"term": "What are soft failing nodes?", "namespace": "open-p", "hash": 689768872, "terms": ["What are soft failing nodes?", "are soft failing nodes?", "soft failing nodes?", "failing nodes?", "nodes?"], "tokens": ["What", "are", "soft", "failing", "nodes", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1804706278", "fields": {"term": "What is adaptive coverage timeout?", "namespace": "open-p", "hash": 1804706278, "terms": ["What is adaptive coverage timeout?", "is adaptive coverage timeout?", "adaptive coverage timeout?", "coverage timeout?", "timeout?"], "tokens": ["What", "is", "adaptive", "coverage", "timeout", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1894562327", "fields": {"term": "What is graceful search degradation?", "namespace": "open-p", "hash": -1894562327, "terms": ["What is graceful search degradation?", "is graceful search degradation?", "graceful search degradation?", "search degradation?", "degradation?"], "tokens": ["What", "is", "graceful", "search", "degradation", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1056102351", "fields": {"term": "What is a dispatch policy?", "namespace": "open-p", "hash": -1056102351, "terms": ["What is a dispatch policy?", "is a dispatch policy?", "a dispatch policy?", "dispatch policy?", "policy?"], "tokens": ["What", "is", "a", "dispatch", "policy", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-582068980", "fields": {"term": "How are slow soft failing nodes prevented?", "namespace": "open-p", "hash": -582068980, "terms": ["How are slow soft failing nodes prevented?", "are slow soft failing nodes prevented?", "slow soft failing nodes prevented?", "soft failing nodes prevented?", "failing nodes prevented?", "nodes prevented?", "prevented?"], "tokens": ["How", "are", "slow", "soft", "failing", "nodes", "prevented", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1389199377", "fields": {"term": "What is the impact of soft failing on grouped distributions?", "namespace": "open-p", "hash": 1389199377, "terms": ["What is the impact of soft failing on grouped distributions?", "is the impact of soft failing on grouped distributions?", "the impact of soft failing on grouped distributions?", "impact of soft failing on grouped distributions?", "of soft failing on grouped distributions?", "soft failing on grouped distributions?", "failing on grouped distributions?", "on grouped distributions?", "grouped distributions?", "distributions?"], "tokens": ["What", "is", "the", "impact", "of", "soft", "failing", "on", "grouped", "distributions", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2125371826", "fields": {"term": "What is the adaptive dispatch policy?", "namespace": "open-p", "hash": 2125371826, "terms": ["What is the adaptive dispatch policy?", "is the adaptive dispatch policy?", "the adaptive dispatch policy?", "adaptive dispatch policy?", "dispatch policy?", "policy?"], "tokens": ["What", "is", "the", "adaptive", "dispatch", "policy", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1400005547", "fields": {"term": "How do you change group topology?", "namespace": "open-p", "hash": -1400005547, "terms": ["How do you change group topology?", "do you change group topology?", "you change group topology?", "change group topology?", "group topology?", "topology?"], "tokens": ["How", "do", "you", "change", "group", "topology", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1821712230", "fields": {"term": "Is service disruption a concern?", "namespace": "open-p", "hash": -1821712230, "terms": ["Is service disruption a concern?", "service disruption a concern?", "disruption a concern?", "a concern?", "concern?"], "tokens": ["Is", "service", "disruption", "a", "concern", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1042533353", "fields": {"term": "What are the caveats of changing topology?", "namespace": "open-p", "hash": -1042533353, "terms": ["What are the caveats of changing topology?", "are the caveats of changing topology?", "the caveats of changing topology?", "caveats of changing topology?", "of changing topology?", "changing topology?", "topology?"], "tokens": ["What", "are", "the", "caveats", "of", "changing", "topology", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-645951338", "fields": {"term": "Where can I find more information about topology changes?", "namespace": "open-p", "hash": -645951338, "terms": ["Where can I find more information about topology changes?", "can I find more information about topology changes?", "I find more information about topology changes?", "find more information about topology changes?", "more information about topology changes?", "information about topology changes?", "about topology changes?", "topology changes?", "changes?"], "tokens": ["Where", "can", "I", "find", "more", "information", "about", "topology", "changes", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1008716517", "fields": {"term": "How does Vespa support feed rates?", "namespace": "open-p", "hash": 1008716517, "terms": ["How does Vespa support feed rates?", "does Vespa support feed rates?", "Vespa support feed rates?", "support feed rates?", "feed rates?", "rates?"], "tokens": ["How", "does", "Vespa", "support", "feed", "rates", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-985896379", "fields": {"term": "What is the stateless container cluster?", "namespace": "open-p", "hash": -985896379, "terms": ["What is the stateless container cluster?", "is the stateless container cluster?", "the stateless container cluster?", "stateless container cluster?", "container cluster?", "cluster?"], "tokens": ["What", "is", "the", "stateless", "container", "cluster", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1910572388", "fields": {"term": "How does NAS attached storage impact feed performance?", "namespace": "open-p", "hash": 1910572388, "terms": ["How does NAS attached storage impact feed performance?", "does NAS attached storage impact feed performance?", "NAS attached storage impact feed performance?", "attached storage impact feed performance?", "storage impact feed performance?", "impact feed performance?", "feed performance?", "performance?"], "tokens": ["How", "does", "NAS", "attached", "storage", "impact", "feed", "performance", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1293616201", "fields": {"term": "What does index structure flushing involve?", "namespace": "open-p", "hash": -1293616201, "terms": ["What does index structure flushing involve?", "does index structure flushing involve?", "index structure flushing involve?", "structure flushing involve?", "flushing involve?", "involve?"], "tokens": ["What", "does", "index", "structure", "flushing", "involve", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1374906038", "fields": {"term": "What is PUT?", "namespace": "open-p", "hash": 1374906038, "terms": ["What is PUT?", "is PUT?", "PUT?"], "tokens": ["What", "is", "PUT", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1270876974", "fields": {"term": "What is the Index Manager?", "namespace": "open-p", "hash": 1270876974, "terms": ["What is the Index Manager?", "is the Index Manager?", "the Index Manager?", "Index Manager?", "Manager?"], "tokens": ["What", "is", "the", "Index", "Manager", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1339774067", "fields": {"term": "What is the memory index?", "namespace": "open-p", "hash": 1339774067, "terms": ["What is the memory index?", "is the memory index?", "the memory index?", "memory index?", "index?"], "tokens": ["What", "is", "the", "memory", "index", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1861789769", "fields": {"term": "What is the difference between PUT and partial updates?", "namespace": "open-p", "hash": 1861789769, "terms": ["What is the difference between PUT and partial updates?", "is the difference between PUT and partial updates?", "the difference between PUT and partial updates?", "difference between PUT and partial updates?", "between PUT and partial updates?", "PUT and partial updates?", "and partial updates?", "partial updates?", "updates?"], "tokens": ["What", "is", "the", "difference", "between", "PUT", "and", "partial", "updates", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-685870641", "fields": {"term": "What are attribute fields?", "namespace": "open-p", "hash": -685870641, "terms": ["What are attribute fields?", "are attribute fields?", "attribute fields?", "fields?"], "tokens": ["What", "are", "attribute", "fields", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::344771860", "fields": {"term": "What is ticker schema?", "namespace": "open-p", "hash": 344771860, "terms": ["What is ticker schema?", "is ticker schema?", "ticker schema?", "schema?"], "tokens": ["What", "is", "ticker", "schema", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1854117679", "fields": {"term": "How to achieve memory-only updates?", "namespace": "open-p", "hash": -1854117679, "terms": ["How to achieve memory-only updates?", "to achieve memory-only updates?", "achieve memory-only updates?", "memory-only updates?", "updates?"], "tokens": ["How", "to", "achieve", "memory", "only", "updates", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1566769116", "fields": {"term": "How to ensure all attributes are ready?", "namespace": "open-p", "hash": -1566769116, "terms": ["How to ensure all attributes are ready?", "to ensure all attributes are ready?", "ensure all attributes are ready?", "all attributes are ready?", "attributes are ready?", "are ready?", "ready?"], "tokens": ["How", "to", "ensure", "all", "attributes", "are", "ready", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-117442535", "fields": {"term": "How to debug update performance?", "namespace": "open-p", "hash": -117442535, "terms": ["How to debug update performance?", "to debug update performance?", "debug update performance?", "update performance?", "performance?"], "tokens": ["How", "to", "debug", "update", "performance", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1382948553", "fields": {"term": "What is SummaryAdapter::put?", "namespace": "open-p", "hash": -1382948553, "terms": ["What is SummaryAdapter::put?", "is SummaryAdapter::put?", "SummaryAdapter::put?"], "tokens": ["What", "is", "SummaryAdapter", "::", "put", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1871563234", "fields": {"term": "How to enable spam log level?", "namespace": "open-p", "hash": -1871563234, "terms": ["How to enable spam log level?", "to enable spam log level?", "enable spam log level?", "spam log level?", "log level?", "level?"], "tokens": ["How", "to", "enable", "spam", "log", "level", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::112427277", "fields": {"term": "What does 'SummaryAdapter::put' log message indicate?", "namespace": "open-p", "hash": 112427277, "terms": ["What does 'SummaryAdapter::put' log message indicate?", "does 'SummaryAdapter::put' log message indicate?", "'SummaryAdapter::put' log message indicate?", "log message indicate?", "message indicate?", "indicate?"], "tokens": ["What", "does", "'", "SummaryAdapter", "::", "put", "'", "log", "message", "indicate", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::201116782", "fields": {"term": "What are multivalued attributes?", "namespace": "open-p", "hash": 201116782, "terms": ["What are multivalued attributes?", "are multivalued attributes?", "multivalued attributes?", "attributes?"], "tokens": ["What", "are", "multivalued", "attributes", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-721007314", "fields": {"term": "How do multivalued fields impact write performance?", "namespace": "open-p", "hash": -721007314, "terms": ["How do multivalued fields impact write performance?", "do multivalued fields impact write performance?", "multivalued fields impact write performance?", "fields impact write performance?", "impact write performance?", "write performance?", "performance?"], "tokens": ["How", "do", "multivalued", "fields", "impact", "write", "performance", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1220319422", "fields": {"term": "What is a weightedset attribute?", "namespace": "open-p", "hash": 1220319422, "terms": ["What is a weightedset attribute?", "is a weightedset attribute?", "a weightedset attribute?", "weightedset attribute?", "attribute?"], "tokens": ["What", "is", "a", "weightedset", "attribute", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1077807567", "fields": {"term": "What is the impact of using numeric types with a weightedset attribute?", "namespace": "open-p", "hash": 1077807567, "terms": ["What is the impact of using numeric types with a weightedset attribute?", "is the impact of using numeric types with a weightedset attribute?", "the impact of using numeric types with a weightedset attribute?", "impact of using numeric types with a weightedset attribute?", "of using numeric types with a weightedset attribute?", "using numeric types with a weightedset attribute?", "numeric types with a weightedset attribute?", "types with a weightedset attribute?", "with a weightedset attribute?", "a weightedset attribute?", "weightedset attribute?", "attribute?"], "tokens": ["What", "is", "the", "impact", "of", "using", "numeric", "types", "with", "a", "weightedset", "attribute", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1008847203", "fields": {"term": "What is the cost of updating array/map of struct/map attributes?", "namespace": "open-p", "hash": 1008847203, "terms": ["What is the cost of updating array/map of struct/map attributes?", "is the cost of updating array/map of struct/map attributes?", "the cost of updating array/map of struct/map attributes?", "cost of updating array/map of struct/map attributes?", "of updating array/map of struct/map attributes?", "updating array/map of struct/map attributes?", "array/map of struct/map attributes?", "of struct/map attributes?", "struct/map attributes?", "attributes?"], "tokens": ["What", "is", "the", "cost", "of", "updating", "array", "/", "map", "of", "struct", "/", "map", "attributes", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::854587189", "fields": {"term": "What is the impact of updating tensor cell values?", "namespace": "open-p", "hash": 854587189, "terms": ["What is the impact of updating tensor cell values?", "is the impact of updating tensor cell values?", "the impact of updating tensor cell values?", "impact of updating tensor cell values?", "of updating tensor cell values?", "updating tensor cell values?", "tensor cell values?", "cell values?", "values?"], "tokens": ["What", "is", "the", "impact", "of", "updating", "tensor", "cell", "values", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1874213071", "fields": {"term": "How does updating tensor cell values impact memory?", "namespace": "open-p", "hash": 1874213071, "terms": ["How does updating tensor cell values impact memory?", "does updating tensor cell values impact memory?", "updating tensor cell values impact memory?", "tensor cell values impact memory?", "cell values impact memory?", "values impact memory?", "impact memory?", "memory?"], "tokens": ["How", "does", "updating", "tensor", "cell", "values", "impact", "memory", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2008993012", "fields": {"term": "What is the recommended approach for using numeric types with a weightedset attribute?", "namespace": "open-p", "hash": -2008993012, "terms": ["What is the recommended approach for using numeric types with a weightedset attribute?", "is the recommended approach for using numeric types with a weightedset attribute?", "the recommended approach for using numeric types with a weightedset attribute?", "recommended approach for using numeric types with a weightedset attribute?", "approach for using numeric types with a weightedset attribute?", "for using numeric types with a weightedset attribute?", "using numeric types with a weightedset attribute?", "numeric types with a weightedset attribute?", "types with a weightedset attribute?", "with a weightedset attribute?", "a weightedset attribute?", "weightedset attribute?", "attribute?"], "tokens": ["What", "is", "the", "recommended", "approach", "for", "using", "numeric", "types", "with", "a", "weightedset", "attribute", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1720270834", "fields": {"term": "What are parent documents?", "namespace": "open-p", "hash": 1720270834, "terms": ["What are parent documents?", "are parent documents?", "parent documents?", "documents?"], "tokens": ["What", "are", "parent", "documents", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1951013459", "fields": {"term": "What simplifies logic in parent documents?", "namespace": "open-p", "hash": 1951013459, "terms": ["What simplifies logic in parent documents?", "simplifies logic in parent documents?", "logic in parent documents?", "in parent documents?", "parent documents?", "documents?"], "tokens": ["What", "simplifies", "logic", "in", "parent", "documents", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-298343450", "fields": {"term": "How does write performance depend on cluster size and child documents?", "namespace": "open-p", "hash": -298343450, "terms": ["How does write performance depend on cluster size and child documents?", "does write performance depend on cluster size and child documents?", "write performance depend on cluster size and child documents?", "performance depend on cluster size and child documents?", "depend on cluster size and child documents?", "on cluster size and child documents?", "cluster size and child documents?", "size and child documents?", "and child documents?", "child documents?", "documents?"], "tokens": ["How", "does", "write", "performance", "depend", "on", "cluster", "size", "and", "child", "documents", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-315632381", "fields": {"term": "What is a conditional update?", "namespace": "open-p", "hash": -315632381, "terms": ["What is a conditional update?", "is a conditional update?", "a conditional update?", "conditional update?", "update?"], "tokens": ["What", "is", "a", "conditional", "update", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-554297447", "fields": {"term": "How should conditions be evaluated?", "namespace": "open-p", "hash": -554297447, "terms": ["How should conditions be evaluated?", "should conditions be evaluated?", "conditions be evaluated?", "be evaluated?", "evaluated?"], "tokens": ["How", "should", "conditions", "be", "evaluated", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-169554903", "fields": {"term": "What is the difference between two fields assignments?", "namespace": "open-p", "hash": -169554903, "terms": ["What is the difference between two fields assignments?", "is the difference between two fields assignments?", "the difference between two fields assignments?", "difference between two fields assignments?", "between two fields assignments?", "two fields assignments?", "fields assignments?", "assignments?"], "tokens": ["What", "is", "the", "difference", "between", "two", "fields", "assignments", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1180058944", "fields": {"term": "How can updates be put in as few operations as possible?", "namespace": "open-p", "hash": -1180058944, "terms": ["How can updates be put in as few operations as possible?", "can updates be put in as few operations as possible?", "updates be put in as few operations as possible?", "be put in as few operations as possible?", "put in as few operations as possible?", "in as few operations as possible?", "as few operations as possible?", "few operations as possible?", "operations as possible?", "as possible?", "possible?"], "tokens": ["How", "can", "updates", "be", "put", "in", "as", "few", "operations", "as", "possible", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-320024249", "fields": {"term": "What is CPU allocation?", "namespace": "open-p", "hash": -320024249, "terms": ["What is CPU allocation?", "is CPU allocation?", "CPU allocation?", "allocation?"], "tokens": ["What", "is", "CPU", "allocation", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::626409192", "fields": {"term": "How can you allocate CPU resources for feeding?", "namespace": "open-p", "hash": 626409192, "terms": ["How can you allocate CPU resources for feeding?", "can you allocate CPU resources for feeding?", "you allocate CPU resources for feeding?", "allocate CPU resources for feeding?", "CPU resources for feeding?", "resources for feeding?", "for feeding?", "feeding?"], "tokens": ["How", "can", "you", "allocate", "CPU", "resources", "for", "feeding", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::453952838", "fields": {"term": "What is the value range for concurrency?", "namespace": "open-p", "hash": 453952838, "terms": ["What is the value range for concurrency?", "is the value range for concurrency?", "the value range for concurrency?", "value range for concurrency?", "range for concurrency?", "for concurrency?", "concurrency?"], "tokens": ["What", "is", "the", "value", "range", "for", "concurrency", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2033876402", "fields": {"term": "What does a higher concurrency value mean?", "namespace": "open-p", "hash": 2033876402, "terms": ["What does a higher concurrency value mean?", "does a higher concurrency value mean?", "a higher concurrency value mean?", "higher concurrency value mean?", "concurrency value mean?", "value mean?", "mean?"], "tokens": ["What", "does", "a", "higher", "concurrency", "value", "mean", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1471773871", "fields": {"term": "What is the relation between CPU allocation and feeding in a content node?", "namespace": "open-p", "hash": 1471773871, "terms": ["What is the relation between CPU allocation and feeding in a content node?", "is the relation between CPU allocation and feeding in a content node?", "the relation between CPU allocation and feeding in a content node?", "relation between CPU allocation and feeding in a content node?", "between CPU allocation and feeding in a content node?", "CPU allocation and feeding in a content node?", "allocation and feeding in a content node?", "and feeding in a content node?", "feeding in a content node?", "in a content node?", "a content node?", "content node?", "node?"], "tokens": ["What", "is", "the", "relation", "between", "CPU", "allocation", "and", "feeding", "in", "a", "content", "node", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2117469677", "fields": {"term": "How do you test feeding capacity?", "namespace": "open-p", "hash": 2117469677, "terms": ["How do you test feeding capacity?", "do you test feeding capacity?", "you test feeding capacity?", "test feeding capacity?", "feeding capacity?", "capacity?"], "tokens": ["How", "do", "you", "test", "feeding", "capacity", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::991512728", "fields": {"term": "What is steady state?", "namespace": "open-p", "hash": 991512728, "terms": ["What is steady state?", "is steady state?", "steady state?", "state?"], "tokens": ["What", "is", "steady", "state", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1593269897", "fields": {"term": "What is feeder instance?", "namespace": "open-p", "hash": -1593269897, "terms": ["What is feeder instance?", "is feeder instance?", "feeder instance?", "instance?"], "tokens": ["What", "is", "feeder", "instance", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::426417901", "fields": {"term": "How do you troubleshoot feed testing?", "namespace": "open-p", "hash": 426417901, "terms": ["How do you troubleshoot feed testing?", "do you troubleshoot feed testing?", "you troubleshoot feed testing?", "troubleshoot feed testing?", "feed testing?", "testing?"], "tokens": ["How", "do", "you", "troubleshoot", "feed", "testing", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1986606832", "fields": {"term": "What are the different scenarios for feed testing?", "namespace": "open-p", "hash": -1986606832, "terms": ["What are the different scenarios for feed testing?", "are the different scenarios for feed testing?", "the different scenarios for feed testing?", "different scenarios for feed testing?", "scenarios for feed testing?", "for feed testing?", "feed testing?", "testing?"], "tokens": ["What", "are", "the", "different", "scenarios", "for", "feed", "testing", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2011595670", "fields": {"term": "What are distributor updates?", "namespace": "open-p", "hash": -2011595670, "terms": ["What are distributor updates?", "are distributor updates?", "distributor updates?", "updates?"], "tokens": ["What", "are", "distributor", "updates", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1174756034", "fields": {"term": "What is concurrent mutations?", "namespace": "open-p", "hash": -1174756034, "terms": ["What is concurrent mutations?", "is concurrent mutations?", "concurrent mutations?", "mutations?"], "tokens": ["What", "is", "concurrent", "mutations", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1143604468", "fields": {"term": "What is wrong distributor?", "namespace": "open-p", "hash": -1143604468, "terms": ["What is wrong distributor?", "is wrong distributor?", "wrong distributor?", "distributor?"], "tokens": ["What", "is", "wrong", "distributor", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::403528174", "fields": {"term": "What is cluster out of sync?", "namespace": "open-p", "hash": 403528174, "terms": ["What is cluster out of sync?", "is cluster out of sync?", "cluster out of sync?", "out of sync?", "of sync?", "sync?"], "tokens": ["What", "is", "cluster", "out", "of", "sync", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-549898435", "fields": {"term": "What is blocked feeding?", "namespace": "open-p", "hash": -549898435, "terms": ["What is blocked feeding?", "is blocked feeding?", "blocked feeding?", "feeding?"], "tokens": ["What", "is", "blocked", "feeding", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-924104966", "fields": {"term": "What is done_ok in merge_bucket?", "namespace": "open-p", "hash": -924104966, "terms": ["What is done_ok in merge_bucket?", "is done_ok in merge_bucket?", "done_ok in merge_bucket?", "in merge_bucket?", "merge_bucket?"], "tokens": ["What", "is", "done_ok", "in", "merge_bucket", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1764724512", "fields": {"term": "What is updates.ok in distributor?", "namespace": "open-p", "hash": -1764724512, "terms": ["What is updates.ok in distributor?", "is updates.ok in distributor?", "updates.ok in distributor?", "in distributor?", "distributor?"], "tokens": ["What", "is", "updates", "ok", "in", "distributor", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-410194502", "fields": {"term": "How is data distributed to nodes?", "namespace": "open-p", "hash": -410194502, "terms": ["How is data distributed to nodes?", "is data distributed to nodes?", "data distributed to nodes?", "distributed to nodes?", "to nodes?", "nodes?"], "tokens": ["How", "is", "data", "distributed", "to", "nodes", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::650628714", "fields": {"term": "What is flat content distribution?", "namespace": "open-p", "hash": 650628714, "terms": ["What is flat content distribution?", "is flat content distribution?", "flat content distribution?", "content distribution?", "distribution?"], "tokens": ["What", "is", "flat", "content", "distribution", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::433305882", "fields": {"term": "How does flat distribution work?", "namespace": "open-p", "hash": 433305882, "terms": ["How does flat distribution work?", "does flat distribution work?", "flat distribution work?", "distribution work?", "work?"], "tokens": ["How", "does", "flat", "distribution", "work", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1864977276", "fields": {"term": "What is ideal state distribution algorithm?", "namespace": "open-p", "hash": -1864977276, "terms": ["What is ideal state distribution algorithm?", "is ideal state distribution algorithm?", "ideal state distribution algorithm?", "state distribution algorithm?", "distribution algorithm?", "algorithm?"], "tokens": ["What", "is", "ideal", "state", "distribution", "algorithm", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2137227195", "fields": {"term": "What is Little's law?", "namespace": "open-p", "hash": -2137227195, "terms": ["What is Little's law?", "is Little's law?", "Little's law?", "law?"], "tokens": ["What", "is", "Little", "'", "s", "law", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1291509382", "fields": {"term": "How to scale query throughput?", "namespace": "open-p", "hash": -1291509382, "terms": ["How to scale query throughput?", "to scale query throughput?", "scale query throughput?", "query throughput?", "throughput?"], "tokens": ["How", "to", "scale", "query", "throughput", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::163986701", "fields": {"term": "What is meant by active part?", "namespace": "open-p", "hash": 163986701, "terms": ["What is meant by active part?", "is meant by active part?", "meant by active part?", "by active part?", "active part?", "part?"], "tokens": ["What", "is", "meant", "by", "active", "part", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1584685099", "fields": {"term": "What is a grouped distribution?", "namespace": "open-p", "hash": -1584685099, "terms": ["What is a grouped distribution?", "is a grouped distribution?", "a grouped distribution?", "grouped distribution?", "distribution?"], "tokens": ["What", "is", "a", "grouped", "distribution", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2121381979", "fields": {"term": "How is content distributed?", "namespace": "open-p", "hash": -2121381979, "terms": ["How is content distributed?", "is content distributed?", "content distributed?", "distributed?"], "tokens": ["How", "is", "content", "distributed", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1969649947", "fields": {"term": "What is high availability?", "namespace": "open-p", "hash": 1969649947, "terms": ["What is high availability?", "is high availability?", "high availability?", "availability?"], "tokens": ["What", "is", "high", "availability", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::906074682", "fields": {"term": "What are the sub-databases inside a Vespa content node?", "namespace": "open-p", "hash": 906074682, "terms": ["What are the sub-databases inside a Vespa content node?", "are the sub-databases inside a Vespa content node?", "the sub-databases inside a Vespa content node?", "sub-databases inside a Vespa content node?", "inside a Vespa content node?", "a Vespa content node?", "Vespa content node?", "content node?", "node?"], "tokens": ["What", "are", "the", "sub", "databases", "inside", "a", "Vespa", "content", "node", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1445355276", "fields": {"term": "What is the purpose of the Ready and Not Ready databases?", "namespace": "open-p", "hash": 1445355276, "terms": ["What is the purpose of the Ready and Not Ready databases?", "is the purpose of the Ready and Not Ready databases?", "the purpose of the Ready and Not Ready databases?", "purpose of the Ready and Not Ready databases?", "of the Ready and Not Ready databases?", "the Ready and Not Ready databases?", "Ready and Not Ready databases?", "and Not Ready databases?", "Not Ready databases?", "Ready databases?", "databases?"], "tokens": ["What", "is", "the", "purpose", "of", "the", "Ready", "and", "Not", "Ready", "databases", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::282321840", "fields": {"term": "What is the purpose of the Removed database?", "namespace": "open-p", "hash": 282321840, "terms": ["What is the purpose of the Removed database?", "is the purpose of the Removed database?", "the purpose of the Removed database?", "purpose of the Removed database?", "of the Removed database?", "the Removed database?", "Removed database?", "database?"], "tokens": ["What", "is", "the", "purpose", "of", "the", "Removed", "database", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1496316954", "fields": {"term": "When is having searchable-copies=1 most optimal?", "namespace": "open-p", "hash": 1496316954, "terms": ["When is having searchable-copies=1 most optimal?", "is having searchable-copies=1 most optimal?", "having searchable-copies=1 most optimal?", "searchable-copies=1 most optimal?", "most optimal?", "optimal?"], "tokens": ["When", "is", "having", "searchable", "copies", "=", "1", "most", "optimal", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::37523423", "fields": {"term": "What happens when searchable-copies=2 and redundancy=2?", "namespace": "open-p", "hash": 37523423, "terms": ["What happens when searchable-copies=2 and redundancy=2?", "happens when searchable-copies=2 and redundancy=2?", "when searchable-copies=2 and redundancy=2?", "searchable-copies=2 and redundancy=2?", "and redundancy=2?", "redundancy=2?"], "tokens": ["What", "happens", "when", "searchable", "copies", "=", "2", "and", "redundancy", "=", "2", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1211610714", "fields": {"term": "How does Vespa execute queries?", "namespace": "open-p", "hash": -1211610714, "terms": ["How does Vespa execute queries?", "does Vespa execute queries?", "Vespa execute queries?", "execute queries?", "queries?"], "tokens": ["How", "does", "Vespa", "execute", "queries", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::880939447", "fields": {"term": "What are the components involved in query execution?", "namespace": "open-p", "hash": 880939447, "terms": ["What are the components involved in query execution?", "are the components involved in query execution?", "the components involved in query execution?", "components involved in query execution?", "involved in query execution?", "in query execution?", "query execution?", "execution?"], "tokens": ["What", "are", "the", "components", "involved", "in", "query", "execution", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1371751272", "fields": {"term": "How does Vespa scale horizontally?", "namespace": "open-p", "hash": -1371751272, "terms": ["How does Vespa scale horizontally?", "does Vespa scale horizontally?", "Vespa scale horizontally?", "scale horizontally?", "horizontally?"], "tokens": ["How", "does", "Vespa", "scale", "horizontally", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::120204943", "fields": {"term": "What is Amdahl's law?", "namespace": "open-p", "hash": 120204943, "terms": ["What is Amdahl's law?", "is Amdahl's law?", "Amdahl's law?", "law?"], "tokens": ["What", "is", "Amdahl", "'", "s", "law", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2109151647", "fields": {"term": "How is query work divided in Vespa?", "namespace": "open-p", "hash": 2109151647, "terms": ["How is query work divided in Vespa?", "is query work divided in Vespa?", "query work divided in Vespa?", "work divided in Vespa?", "divided in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["How", "is", "query", "work", "divided", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1513662049", "fields": {"term": "What is dynamic query work?", "namespace": "open-p", "hash": -1513662049, "terms": ["What is dynamic query work?", "is dynamic query work?", "dynamic query work?", "query work?", "work?"], "tokens": ["What", "is", "dynamic", "query", "work", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1014840066", "fields": {"term": "How does Little's Law apply to Vespa?", "namespace": "open-p", "hash": 1014840066, "terms": ["How does Little's Law apply to Vespa?", "does Little's Law apply to Vespa?", "Little's Law apply to Vespa?", "Law apply to Vespa?", "apply to Vespa?", "to Vespa?", "Vespa?"], "tokens": ["How", "does", "Little", "'", "s", "Law", "apply", "to", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1816864652", "fields": {"term": "How does scaling of static and dynamic query work affect throughput?", "namespace": "open-p", "hash": -1816864652, "terms": ["How does scaling of static and dynamic query work affect throughput?", "does scaling of static and dynamic query work affect throughput?", "scaling of static and dynamic query work affect throughput?", "of static and dynamic query work affect throughput?", "static and dynamic query work affect throughput?", "and dynamic query work affect throughput?", "dynamic query work affect throughput?", "query work affect throughput?", "work affect throughput?", "affect throughput?", "throughput?"], "tokens": ["How", "does", "scaling", "of", "static", "and", "dynamic", "query", "work", "affect", "throughput", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1859452339", "fields": {"term": "How does the number of nodes affect Vespa performance?", "namespace": "open-p", "hash": -1859452339, "terms": ["How does the number of nodes affect Vespa performance?", "does the number of nodes affect Vespa performance?", "the number of nodes affect Vespa performance?", "number of nodes affect Vespa performance?", "of nodes affect Vespa performance?", "nodes affect Vespa performance?", "affect Vespa performance?", "Vespa performance?", "performance?"], "tokens": ["How", "does", "the", "number", "of", "nodes", "affect", "Vespa", "performance", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1281034845", "fields": {"term": "What is a latency SLA and how is it specified?", "namespace": "open-p", "hash": -1281034845, "terms": ["What is a latency SLA and how is it specified?", "is a latency SLA and how is it specified?", "a latency SLA and how is it specified?", "latency SLA and how is it specified?", "SLA and how is it specified?", "and how is it specified?", "how is it specified?", "is it specified?", "it specified?", "specified?"], "tokens": ["What", "is", "a", "latency", "SLA", "and", "how", "is", "it", "specified", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1763412159", "fields": {"term": "What is sub-linear latency scaling?", "namespace": "open-p", "hash": 1763412159, "terms": ["What is sub-linear latency scaling?", "is sub-linear latency scaling?", "sub-linear latency scaling?", "latency scaling?", "scaling?"], "tokens": ["What", "is", "sub", "linear", "latency", "scaling", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1358268626", "fields": {"term": "What are the performance characteristics of different use cases?", "namespace": "open-p", "hash": 1358268626, "terms": ["What are the performance characteristics of different use cases?", "are the performance characteristics of different use cases?", "the performance characteristics of different use cases?", "performance characteristics of different use cases?", "characteristics of different use cases?", "of different use cases?", "different use cases?", "use cases?", "cases?"], "tokens": ["What", "are", "the", "performance", "characteristics", "of", "different", "use", "cases", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-858381683", "fields": {"term": "What query operators have sub-linear scaling properties and what are the examples?", "namespace": "open-p", "hash": -858381683, "terms": ["What query operators have sub-linear scaling properties and what are the examples?", "query operators have sub-linear scaling properties and what are the examples?", "operators have sub-linear scaling properties and what are the examples?", "have sub-linear scaling properties and what are the examples?", "sub-linear scaling properties and what are the examples?", "scaling properties and what are the examples?", "properties and what are the examples?", "and what are the examples?", "what are the examples?", "are the examples?", "the examples?", "examples?"], "tokens": ["What", "query", "operators", "have", "sub", "linear", "scaling", "properties", "and", "what", "are", "the", "examples", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-967371420", "fields": {"term": "What is the relationship between overall latency and the number of documents indexed per node?", "namespace": "open-p", "hash": -967371420, "terms": ["What is the relationship between overall latency and the number of documents indexed per node?", "is the relationship between overall latency and the number of documents indexed per node?", "the relationship between overall latency and the number of documents indexed per node?", "relationship between overall latency and the number of documents indexed per node?", "between overall latency and the number of documents indexed per node?", "overall latency and the number of documents indexed per node?", "latency and the number of documents indexed per node?", "and the number of documents indexed per node?", "the number of documents indexed per node?", "number of documents indexed per node?", "of documents indexed per node?", "documents indexed per node?", "indexed per node?", "per node?", "node?"], "tokens": ["What", "is", "the", "relationship", "between", "overall", "latency", "and", "the", "number", "of", "documents", "indexed", "per", "node", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::46608502", "fields": {"term": "What is the sweet spot when using Vespa to reduce serving latency?", "namespace": "open-p", "hash": 46608502, "terms": ["What is the sweet spot when using Vespa to reduce serving latency?", "is the sweet spot when using Vespa to reduce serving latency?", "the sweet spot when using Vespa to reduce serving latency?", "sweet spot when using Vespa to reduce serving latency?", "spot when using Vespa to reduce serving latency?", "when using Vespa to reduce serving latency?", "using Vespa to reduce serving latency?", "Vespa to reduce serving latency?", "to reduce serving latency?", "reduce serving latency?", "serving latency?", "latency?"], "tokens": ["What", "is", "the", "sweet", "spot", "when", "using", "Vespa", "to", "reduce", "serving", "latency", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1153687135", "fields": {"term": "What model or ranking features breach the targeted latency SLA?", "namespace": "open-p", "hash": 1153687135, "terms": ["What model or ranking features breach the targeted latency SLA?", "model or ranking features breach the targeted latency SLA?", "or ranking features breach the targeted latency SLA?", "ranking features breach the targeted latency SLA?", "features breach the targeted latency SLA?", "breach the targeted latency SLA?", "the targeted latency SLA?", "targeted latency SLA?", "latency SLA?", "SLA?"], "tokens": ["What", "model", "or", "ranking", "features", "breach", "the", "targeted", "latency", "SLA", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1935442717", "fields": {"term": "How can latency be reduced?", "namespace": "open-p", "hash": 1935442717, "terms": ["How can latency be reduced?", "can latency be reduced?", "latency be reduced?", "be reduced?", "reduced?"], "tokens": ["How", "can", "latency", "be", "reduced", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-89674516", "fields": {"term": "What is the impact of using more threads per search?", "namespace": "open-p", "hash": -89674516, "terms": ["What is the impact of using more threads per search?", "is the impact of using more threads per search?", "the impact of using more threads per search?", "impact of using more threads per search?", "of using more threads per search?", "using more threads per search?", "more threads per search?", "threads per search?", "per search?", "search?"], "tokens": ["What", "is", "the", "impact", "of", "using", "more", "threads", "per", "search", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1411349694", "fields": {"term": "How should optimal threads per search be evaluated?", "namespace": "open-p", "hash": -1411349694, "terms": ["How should optimal threads per search be evaluated?", "should optimal threads per search be evaluated?", "optimal threads per search be evaluated?", "threads per search be evaluated?", "per search be evaluated?", "search be evaluated?", "be evaluated?", "evaluated?"], "tokens": ["How", "should", "optimal", "threads", "per", "search", "be", "evaluated", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2129381128", "fields": {"term": "What is the relationship between threads per search and CPU utilization?", "namespace": "open-p", "hash": 2129381128, "terms": ["What is the relationship between threads per search and CPU utilization?", "is the relationship between threads per search and CPU utilization?", "the relationship between threads per search and CPU utilization?", "relationship between threads per search and CPU utilization?", "between threads per search and CPU utilization?", "threads per search and CPU utilization?", "per search and CPU utilization?", "search and CPU utilization?", "and CPU utilization?", "CPU utilization?", "utilization?"], "tokens": ["What", "is", "the", "relationship", "between", "threads", "per", "search", "and", "CPU", "utilization", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1729405644", "fields": {"term": "What type of search benefits from using more threads?", "namespace": "open-p", "hash": -1729405644, "terms": ["What type of search benefits from using more threads?", "type of search benefits from using more threads?", "of search benefits from using more threads?", "search benefits from using more threads?", "benefits from using more threads?", "from using more threads?", "using more threads?", "more threads?", "threads?"], "tokens": ["What", "type", "of", "search", "benefits", "from", "using", "more", "threads", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::862417470", "fields": {"term": "What is intra-node per thread document space partitioning?", "namespace": "open-p", "hash": 862417470, "terms": ["What is intra-node per thread document space partitioning?", "is intra-node per thread document space partitioning?", "intra-node per thread document space partitioning?", "per thread document space partitioning?", "thread document space partitioning?", "document space partitioning?", "space partitioning?", "partitioning?"], "tokens": ["What", "is", "intra", "node", "per", "thread", "document", "space", "partitioning", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1803393786", "fields": {"term": "What is `persearch` and its limitation?", "namespace": "open-p", "hash": 1803393786, "terms": ["What is `persearch` and its limitation?", "is `persearch` and its limitation?", "`persearch` and its limitation?", "and its limitation?", "its limitation?", "limitation?"], "tokens": ["What", "is", "`", "persearch", "`", "and", "its", "limitation", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1445797769", "fields": {"term": "What should `summary` be set to?", "namespace": "open-p", "hash": 1445797769, "terms": ["What should `summary` be set to?", "should `summary` be set to?", "`summary` be set to?", "be set to?", "set to?", "to?"], "tokens": ["What", "should", "`", "summary", "`", "be", "set", "to", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1732653320", "fields": {"term": "What is the default value for `persearch`?", "namespace": "open-p", "hash": -1732653320, "terms": ["What is the default value for `persearch`?", "is the default value for `persearch`?", "the default value for `persearch`?", "default value for `persearch`?", "value for `persearch`?", "for `persearch`?", "`persearch`?"], "tokens": ["What", "is", "the", "default", "value", "for", "`", "persearch", "`?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-839041482", "fields": {"term": "What happens if `persearch` is increased too much?", "namespace": "open-p", "hash": -839041482, "terms": ["What happens if `persearch` is increased too much?", "happens if `persearch` is increased too much?", "if `persearch` is increased too much?", "`persearch` is increased too much?", "is increased too much?", "increased too much?", "too much?", "much?"], "tokens": ["What", "happens", "if", "`", "persearch", "`", "is", "increased", "too", "much", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1907207552", "fields": {"term": "How does `persearch` affect query latency?", "namespace": "open-p", "hash": 1907207552, "terms": ["How does `persearch` affect query latency?", "does `persearch` affect query latency?", "`persearch` affect query latency?", "affect query latency?", "query latency?", "latency?"], "tokens": ["How", "does", "`", "persearch", "`", "affect", "query", "latency", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-9425357", "fields": {"term": "What is the relationship between `persearch` and efficiency?", "namespace": "open-p", "hash": -9425357, "terms": ["What is the relationship between `persearch` and efficiency?", "is the relationship between `persearch` and efficiency?", "the relationship between `persearch` and efficiency?", "relationship between `persearch` and efficiency?", "between `persearch` and efficiency?", "`persearch` and efficiency?", "and efficiency?", "efficiency?"], "tokens": ["What", "is", "the", "relationship", "between", "`", "persearch", "`", "and", "efficiency", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1739534527", "fields": {"term": "What are node constraints?", "namespace": "open-p", "hash": 1739534527, "terms": ["What are node constraints?", "are node constraints?", "node constraints?", "constraints?"], "tokens": ["What", "are", "node", "constraints", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::786558091", "fields": {"term": "What is the targeted search latency SLA?", "namespace": "open-p", "hash": 786558091, "terms": ["What is the targeted search latency SLA?", "is the targeted search latency SLA?", "the targeted search latency SLA?", "targeted search latency SLA?", "search latency SLA?", "latency SLA?", "SLA?"], "tokens": ["What", "is", "the", "targeted", "search", "latency", "SLA", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1830139461", "fields": {"term": "How can cost be reduced when fitting more documents into a node?", "namespace": "open-p", "hash": -1830139461, "terms": ["How can cost be reduced when fitting more documents into a node?", "can cost be reduced when fitting more documents into a node?", "cost be reduced when fitting more documents into a node?", "be reduced when fitting more documents into a node?", "reduced when fitting more documents into a node?", "when fitting more documents into a node?", "fitting more documents into a node?", "more documents into a node?", "documents into a node?", "into a node?", "a node?", "node?"], "tokens": ["How", "can", "cost", "be", "reduced", "when", "fitting", "more", "documents", "into", "a", "node", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::715208314", "fields": {"term": "What is larger fan-out?", "namespace": "open-p", "hash": 715208314, "terms": ["What is larger fan-out?", "is larger fan-out?", "larger fan-out?", "fan-out?"], "tokens": ["What", "is", "larger", "fan", "out", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2079419168", "fields": {"term": "When is using adaptive timeout recommended?", "namespace": "open-p", "hash": -2079419168, "terms": ["When is using adaptive timeout recommended?", "is using adaptive timeout recommended?", "using adaptive timeout recommended?", "adaptive timeout recommended?", "timeout recommended?", "recommended?"], "tokens": ["When", "is", "using", "adaptive", "timeout", "recommended", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1610375387", "fields": {"term": "What happens if resource limits are reached during feed operations?", "namespace": "open-p", "hash": -1610375387, "terms": ["What happens if resource limits are reached during feed operations?", "happens if resource limits are reached during feed operations?", "if resource limits are reached during feed operations?", "resource limits are reached during feed operations?", "limits are reached during feed operations?", "are reached during feed operations?", "reached during feed operations?", "during feed operations?", "feed operations?", "operations?"], "tokens": ["What", "happens", "if", "resource", "limits", "are", "reached", "during", "feed", "operations", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-520233514", "fields": {"term": "What factors affect disk usage?", "namespace": "open-p", "hash": -520233514, "terms": ["What factors affect disk usage?", "factors affect disk usage?", "affect disk usage?", "disk usage?", "usage?"], "tokens": ["What", "factors", "affect", "disk", "usage", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1077080188", "fields": {"term": "How to determine disk usage?", "namespace": "open-p", "hash": 1077080188, "terms": ["How to determine disk usage?", "to determine disk usage?", "determine disk usage?", "disk usage?", "usage?"], "tokens": ["How", "to", "determine", "disk", "usage", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1006256804", "fields": {"term": "What is index fusion?", "namespace": "open-p", "hash": -1006256804, "terms": ["What is index fusion?", "is index fusion?", "index fusion?", "fusion?"], "tokens": ["What", "is", "index", "fusion", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1441790139", "fields": {"term": "What is redundancy impact?", "namespace": "open-p", "hash": -1441790139, "terms": ["What is redundancy impact?", "is redundancy impact?", "redundancy impact?", "impact?"], "tokens": ["What", "is", "redundancy", "impact", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1018654973", "fields": {"term": "How does memory usage increase?", "namespace": "open-p", "hash": 1018654973, "terms": ["How does memory usage increase?", "does memory usage increase?", "memory usage increase?", "usage increase?", "increase?"], "tokens": ["How", "does", "memory", "usage", "increase", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1249144967", "fields": {"term": "What affects memory usage per document?", "namespace": "open-p", "hash": -1249144967, "terms": ["What affects memory usage per document?", "affects memory usage per document?", "memory usage per document?", "usage per document?", "per document?", "document?"], "tokens": ["What", "affects", "memory", "usage", "per", "document", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1967498790", "fields": {"term": "What is vespa-proton-bin?", "namespace": "open-p", "hash": -1967498790, "terms": ["What is vespa-proton-bin?", "is vespa-proton-bin?", "vespa-proton-bin?"], "tokens": ["What", "is", "vespa", "proton", "bin", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1106906127", "fields": {"term": "How is virtual memory usage reported?", "namespace": "open-p", "hash": 1106906127, "terms": ["How is virtual memory usage reported?", "is virtual memory usage reported?", "virtual memory usage reported?", "memory usage reported?", "usage reported?", "reported?"], "tokens": ["How", "is", "virtual", "memory", "usage", "reported", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-856061651", "fields": {"term": "What is mmap?", "namespace": "open-p", "hash": -856061651, "terms": ["What is mmap?", "is mmap?", "mmap?"], "tokens": ["What", "is", "mmap", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::798128231", "fields": {"term": "What are the metrics for capacity planning?", "namespace": "open-p", "hash": 798128231, "terms": ["What are the metrics for capacity planning?", "are the metrics for capacity planning?", "the metrics for capacity planning?", "metrics for capacity planning?", "for capacity planning?", "capacity planning?", "planning?"], "tokens": ["What", "are", "the", "metrics", "for", "capacity", "planning", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1211349206", "fields": {"term": "What is static query work?", "namespace": "open-p", "hash": -1211349206, "terms": ["What is static query work?", "is static query work?", "static query work?", "query work?", "work?"], "tokens": ["What", "is", "static", "query", "work", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1221073441", "fields": {"term": "How to scale throughput?", "namespace": "open-p", "hash": 1221073441, "terms": ["How to scale throughput?", "to scale throughput?", "scale throughput?", "throughput?"], "tokens": ["How", "to", "scale", "throughput", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-170786369", "fields": {"term": "What is throughput threshold?", "namespace": "open-p", "hash": -170786369, "terms": ["What is throughput threshold?", "is throughput threshold?", "throughput threshold?", "threshold?"], "tokens": ["What", "is", "throughput", "threshold", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::847579226", "fields": {"term": "What causes latency to spike up?", "namespace": "open-p", "hash": 847579226, "terms": ["What causes latency to spike up?", "causes latency to spike up?", "latency to spike up?", "to spike up?", "spike up?", "up?"], "tokens": ["What", "causes", "latency", "to", "spike", "up", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::686261161", "fields": {"term": "What happens when saturated?", "namespace": "open-p", "hash": 686261161, "terms": ["What happens when saturated?", "happens when saturated?", "when saturated?", "saturated?"], "tokens": ["What", "happens", "when", "saturated", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-763767912", "fields": {"term": "What is serving availability tuning?", "namespace": "open-p", "hash": -763767912, "terms": ["What is serving availability tuning?", "is serving availability tuning?", "serving availability tuning?", "availability tuning?", "tuning?"], "tokens": ["What", "is", "serving", "availability", "tuning", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1676533659", "fields": {"term": "What is system utilization?", "namespace": "open-p", "hash": -1676533659, "terms": ["What is system utilization?", "is system utilization?", "system utilization?", "utilization?"], "tokens": ["What", "is", "system", "utilization", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-694796242", "fields": {"term": "What is sizing write versus read?", "namespace": "open-p", "hash": -694796242, "terms": ["What is sizing write versus read?", "is sizing write versus read?", "sizing write versus read?", "write versus read?", "versus read?", "read?"], "tokens": ["What", "is", "sizing", "write", "versus", "read", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2106068985", "fields": {"term": "How important is measuring behaviour?", "namespace": "open-p", "hash": -2106068985, "terms": ["How important is measuring behaviour?", "important is measuring behaviour?", "is measuring behaviour?", "measuring behaviour?", "behaviour?"], "tokens": ["How", "important", "is", "measuring", "behaviour", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::735787964", "fields": {"term": "How to verify feeding concurrency?", "namespace": "open-p", "hash": 735787964, "terms": ["How to verify feeding concurrency?", "to verify feeding concurrency?", "verify feeding concurrency?", "feeding concurrency?", "concurrency?"], "tokens": ["How", "to", "verify", "feeding", "concurrency", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1961036080", "fields": {"term": "What are Vespa Metrics for?", "namespace": "open-p", "hash": 1961036080, "terms": ["What are Vespa Metrics for?", "are Vespa Metrics for?", "Vespa Metrics for?", "Metrics for?", "for?"], "tokens": ["What", "are", "Vespa", "Metrics", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::571676834", "fields": {"term": "How can I calculate theoretical speedup?", "namespace": "open-p", "hash": 571676834, "terms": ["How can I calculate theoretical speedup?", "can I calculate theoretical speedup?", "I calculate theoretical speedup?", "calculate theoretical speedup?", "theoretical speedup?", "speedup?"], "tokens": ["How", "can", "I", "calculate", "theoretical", "speedup", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1516574634", "fields": {"term": "Which metrics are used to find number of matches per query?", "namespace": "open-p", "hash": -1516574634, "terms": ["Which metrics are used to find number of matches per query?", "metrics are used to find number of matches per query?", "are used to find number of matches per query?", "used to find number of matches per query?", "to find number of matches per query?", "find number of matches per query?", "number of matches per query?", "of matches per query?", "matches per query?", "per query?", "query?"], "tokens": ["Which", "metrics", "are", "used", "to", "find", "number", "of", "matches", "per", "query", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::100196018", "fields": {"term": "What is the disk usage of documentdb and transaction log?", "namespace": "open-p", "hash": 100196018, "terms": ["What is the disk usage of documentdb and transaction log?", "is the disk usage of documentdb and transaction log?", "the disk usage of documentdb and transaction log?", "disk usage of documentdb and transaction log?", "usage of documentdb and transaction log?", "of documentdb and transaction log?", "documentdb and transaction log?", "and transaction log?", "transaction log?", "log?"], "tokens": ["What", "is", "the", "disk", "usage", "of", "documentdb", "and", "transaction", "log", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::875388590", "fields": {"term": "What is the memory usage of documentdb?", "namespace": "open-p", "hash": 875388590, "terms": ["What is the memory usage of documentdb?", "is the memory usage of documentdb?", "the memory usage of documentdb?", "memory usage of documentdb?", "usage of documentdb?", "of documentdb?", "documentdb?"], "tokens": ["What", "is", "the", "memory", "usage", "of", "documentdb", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::751622740", "fields": {"term": "What is slobrok.heartbeats.failed?", "namespace": "open-p", "hash": 751622740, "terms": ["What is slobrok.heartbeats.failed?", "is slobrok.heartbeats.failed?", "slobrok.heartbeats.failed?"], "tokens": ["What", "is", "slobrok", "heartbeats", "failed", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::1020557802", "fields": {"term": "What is slobrok.requests.register?", "namespace": "open-p", "hash": 1020557802, "terms": ["What is slobrok.requests.register?", "is slobrok.requests.register?", "slobrok.requests.register?"], "tokens": ["What", "is", "slobrok", "requests", "register", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1658032700", "fields": {"term": "What is slobrok.requests.mirror?", "namespace": "open-p", "hash": -1658032700, "terms": ["What is slobrok.requests.mirror?", "is slobrok.requests.mirror?", "slobrok.requests.mirror?"], "tokens": ["What", "is", "slobrok", "requests", "mirror", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1918077493", "fields": {"term": "What is slobrok.requests.admin?", "namespace": "open-p", "hash": 1918077493, "terms": ["What is slobrok.requests.admin?", "is slobrok.requests.admin?", "slobrok.requests.admin?"], "tokens": ["What", "is", "slobrok", "requests", "admin", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1904888270", "fields": {"term": "What is slobrok.missing.consensus?", "namespace": "open-p", "hash": -1904888270, "terms": ["What is slobrok.missing.consensus?", "is slobrok.missing.consensus?", "slobrok.missing.consensus?"], "tokens": ["What", "is", "slobrok", "missing", "consensus", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1183544494", "fields": {"term": "What is Slobrok?", "namespace": "open-p", "hash": 1183544494, "terms": ["What is Slobrok?", "is Slobrok?", "Slobrok?"], "tokens": ["What", "is", "Slobrok", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1238158053", "fields": {"term": "What is the purpose of Slobrok?", "namespace": "open-p", "hash": -1238158053, "terms": ["What is the purpose of Slobrok?", "is the purpose of Slobrok?", "the purpose of Slobrok?", "purpose of Slobrok?", "of Slobrok?", "Slobrok?"], "tokens": ["What", "is", "the", "purpose", "of", "Slobrok", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1235786724", "fields": {"term": "Where is Slobrok hosted?", "namespace": "open-p", "hash": -1235786724, "terms": ["Where is Slobrok hosted?", "is Slobrok hosted?", "Slobrok hosted?", "hosted?"], "tokens": ["Where", "is", "Slobrok", "hosted", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::638910301", "fields": {"term": "How many instances of Slobrok are needed?", "namespace": "open-p", "hash": 638910301, "terms": ["How many instances of Slobrok are needed?", "many instances of Slobrok are needed?", "instances of Slobrok are needed?", "of Slobrok are needed?", "Slobrok are needed?", "are needed?", "needed?"], "tokens": ["How", "many", "instances", "of", "Slobrok", "are", "needed", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2071787152", "fields": {"term": "What is the Slobrok process?", "namespace": "open-p", "hash": -2071787152, "terms": ["What is the Slobrok process?", "is the Slobrok process?", "the Slobrok process?", "Slobrok process?", "process?"], "tokens": ["What", "is", "the", "Slobrok", "process", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::449060290", "fields": {"term": "Does Slobrok impact other services?", "namespace": "open-p", "hash": 449060290, "terms": ["Does Slobrok impact other services?", "Slobrok impact other services?", "impact other services?", "other services?", "services?"], "tokens": ["Does", "Slobrok", "impact", "other", "services", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::284666176", "fields": {"term": "How do clients do lookups on Slobrok?", "namespace": "open-p", "hash": 284666176, "terms": ["How do clients do lookups on Slobrok?", "do clients do lookups on Slobrok?", "clients do lookups on Slobrok?", "do lookups on Slobrok?", "lookups on Slobrok?", "on Slobrok?", "Slobrok?"], "tokens": ["How", "do", "clients", "do", "lookups", "on", "Slobrok", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-350239793", "fields": {"term": "What uses Slobrok to evaluate service availability?", "namespace": "open-p", "hash": -350239793, "terms": ["What uses Slobrok to evaluate service availability?", "uses Slobrok to evaluate service availability?", "Slobrok to evaluate service availability?", "to evaluate service availability?", "evaluate service availability?", "service availability?", "availability?"], "tokens": ["What", "uses", "Slobrok", "to", "evaluate", "service", "availability", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::886473205", "fields": {"term": "What is a sorting specification?", "namespace": "open-p", "hash": 886473205, "terms": ["What is a sorting specification?", "is a sorting specification?", "a sorting specification?", "sorting specification?", "specification?"], "tokens": ["What", "is", "a", "sorting", "specification", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1396618401", "fields": {"term": "How do you specify a sorting expression?", "namespace": "open-p", "hash": 1396618401, "terms": ["How do you specify a sorting expression?", "do you specify a sorting expression?", "you specify a sorting expression?", "specify a sorting expression?", "a sorting expression?", "sorting expression?", "expression?"], "tokens": ["How", "do", "you", "specify", "a", "sorting", "expression", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1191479252", "fields": {"term": "What is multilevel sorting?", "namespace": "open-p", "hash": -1191479252, "terms": ["What is multilevel sorting?", "is multilevel sorting?", "multilevel sorting?", "sorting?"], "tokens": ["What", "is", "multilevel", "sorting", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2031433127", "fields": {"term": "How do you sort in Vespa?", "namespace": "open-p", "hash": 2031433127, "terms": ["How do you sort in Vespa?", "do you sort in Vespa?", "you sort in Vespa?", "sort in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["How", "do", "you", "sort", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1866648450", "fields": {"term": "What is a SortAttribute?", "namespace": "open-p", "hash": 1866648450, "terms": ["What is a SortAttribute?", "is a SortAttribute?", "a SortAttribute?", "SortAttribute?"], "tokens": ["What", "is", "a", "SortAttribute", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-664706173", "fields": {"term": "What are the options for SortOrder?", "namespace": "open-p", "hash": -664706173, "terms": ["What are the options for SortOrder?", "are the options for SortOrder?", "the options for SortOrder?", "options for SortOrder?", "for SortOrder?", "SortOrder?"], "tokens": ["What", "are", "the", "options", "for", "SortOrder", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1507208106", "fields": {"term": "What is a Function in sorting?", "namespace": "open-p", "hash": -1507208106, "terms": ["What is a Function in sorting?", "is a Function in sorting?", "a Function in sorting?", "Function in sorting?", "in sorting?", "sorting?"], "tokens": ["What", "is", "a", "Function", "in", "sorting", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::730281733", "fields": {"term": "What are the sorting references in YQL Vespa?", "namespace": "open-p", "hash": 730281733, "terms": ["What are the sorting references in YQL Vespa?", "are the sorting references in YQL Vespa?", "the sorting references in YQL Vespa?", "sorting references in YQL Vespa?", "references in YQL Vespa?", "in YQL Vespa?", "YQL Vespa?", "Vespa?"], "tokens": ["What", "are", "the", "sorting", "references", "in", "YQL", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1382279523", "fields": {"term": "What does + denote in sorting?", "namespace": "open-p", "hash": 1382279523, "terms": ["What does + denote in sorting?", "does + denote in sorting?", "+ denote in sorting?", "denote in sorting?", "in sorting?", "sorting?"], "tokens": ["What", "does", "+", "denote", "in", "sorting", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-495598837", "fields": {"term": "What does - denote in sorting?", "namespace": "open-p", "hash": -495598837, "terms": ["What does - denote in sorting?", "does - denote in sorting?", "- denote in sorting?", "denote in sorting?", "in sorting?", "sorting?"], "tokens": ["What", "does", "denote", "in", "sorting", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1464241099", "fields": {"term": "How are numerical attributes sorted?", "namespace": "open-p", "hash": -1464241099, "terms": ["How are numerical attributes sorted?", "are numerical attributes sorted?", "numerical attributes sorted?", "attributes sorted?", "sorted?"], "tokens": ["How", "are", "numerical", "attributes", "sorted", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-422294870", "fields": {"term": "How are strings sorted?", "namespace": "open-p", "hash": -422294870, "terms": ["How are strings sorted?", "are strings sorted?", "strings sorted?", "sorted?"], "tokens": ["How", "are", "strings", "sorted", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1186429281", "fields": {"term": "How should + be encoded in query URLs?", "namespace": "open-p", "hash": 1186429281, "terms": ["How should + be encoded in query URLs?", "should + be encoded in query URLs?", "+ be encoded in query URLs?", "be encoded in query URLs?", "encoded in query URLs?", "in query URLs?", "query URLs?", "URLs?"], "tokens": ["How", "should", "+", "be", "encoded", "in", "query", "URLs", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-705630745", "fields": {"term": "How should - be encoded in query URLs?", "namespace": "open-p", "hash": -705630745, "terms": ["How should - be encoded in query URLs?", "should - be encoded in query URLs?", "- be encoded in query URLs?", "be encoded in query URLs?", "encoded in query URLs?", "in query URLs?", "query URLs?", "URLs?"], "tokens": ["How", "should", "be", "encoded", "in", "query", "URLs", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1492930393", "fields": {"term": "What is the default for `+`/`-`?", "namespace": "open-p", "hash": 1492930393, "terms": ["What is the default for `+`/`-`?", "is the default for `+`/`-`?", "the default for `+`/`-`?", "default for `+`/`-`?", "for `+`/`-`?", "`+`/`-`?"], "tokens": ["What", "is", "the", "default", "for", "`+`/`", "`?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1121485368", "fields": {"term": "What is the special builtin for descending sort?", "namespace": "open-p", "hash": -1121485368, "terms": ["What is the special builtin for descending sort?", "is the special builtin for descending sort?", "the special builtin for descending sort?", "special builtin for descending sort?", "builtin for descending sort?", "for descending sort?", "descending sort?", "sort?"], "tokens": ["What", "is", "the", "special", "builtin", "for", "descending", "sort", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-216550379", "fields": {"term": "What is the sorting attribute?", "namespace": "open-p", "hash": -216550379, "terms": ["What is the sorting attribute?", "is the sorting attribute?", "the sorting attribute?", "sorting attribute?", "attribute?"], "tokens": ["What", "is", "the", "sorting", "attribute", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::310627708", "fields": {"term": "What is the recommended rank profile when sorting?", "namespace": "open-p", "hash": 310627708, "terms": ["What is the recommended rank profile when sorting?", "is the recommended rank profile when sorting?", "the recommended rank profile when sorting?", "recommended rank profile when sorting?", "rank profile when sorting?", "profile when sorting?", "when sorting?", "sorting?"], "tokens": ["What", "is", "the", "recommended", "rank", "profile", "when", "sorting", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::944423834", "fields": {"term": "Why is unranked recommended for sorting?", "namespace": "open-p", "hash": 944423834, "terms": ["Why is unranked recommended for sorting?", "is unranked recommended for sorting?", "unranked recommended for sorting?", "recommended for sorting?", "for sorting?", "sorting?"], "tokens": ["Why", "is", "unranked", "recommended", "for", "sorting", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-661210445", "fields": {"term": "What is an attribute in Vespa?", "namespace": "open-p", "hash": -661210445, "terms": ["What is an attribute in Vespa?", "is an attribute in Vespa?", "an attribute in Vespa?", "attribute in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["What", "is", "an", "attribute", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-988458693", "fields": {"term": "How does Vespa execute sorting?", "namespace": "open-p", "hash": -988458693, "terms": ["How does Vespa execute sorting?", "does Vespa execute sorting?", "Vespa execute sorting?", "execute sorting?", "sorting?"], "tokens": ["How", "does", "Vespa", "execute", "sorting", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1338246114", "fields": {"term": "What is the indexing language fragment for an attribute?", "namespace": "open-p", "hash": -1338246114, "terms": ["What is the indexing language fragment for an attribute?", "is the indexing language fragment for an attribute?", "the indexing language fragment for an attribute?", "indexing language fragment for an attribute?", "language fragment for an attribute?", "fragment for an attribute?", "for an attribute?", "an attribute?", "attribute?"], "tokens": ["What", "is", "the", "indexing", "language", "fragment", "for", "an", "attribute", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1825350222", "fields": {"term": "Why is using unranked important for execution speed when sorting?", "namespace": "open-p", "hash": -1825350222, "terms": ["Why is using unranked important for execution speed when sorting?", "is using unranked important for execution speed when sorting?", "using unranked important for execution speed when sorting?", "unranked important for execution speed when sorting?", "important for execution speed when sorting?", "for execution speed when sorting?", "execution speed when sorting?", "speed when sorting?", "when sorting?", "sorting?"], "tokens": ["Why", "is", "using", "unranked", "important", "for", "execution", "speed", "when", "sorting", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-663391096", "fields": {"term": "What are the 3 special attributes?", "namespace": "open-p", "hash": -663391096, "terms": ["What are the 3 special attributes?", "are the 3 special attributes?", "the 3 special attributes?", "3 special attributes?", "special attributes?", "attributes?"], "tokens": ["What", "are", "the", "3", "special", "attributes", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::278915984", "fields": {"term": "How are special attributes useful in sorting?", "namespace": "open-p", "hash": 278915984, "terms": ["How are special attributes useful in sorting?", "are special attributes useful in sorting?", "special attributes useful in sorting?", "attributes useful in sorting?", "useful in sorting?", "in sorting?", "sorting?"], "tokens": ["How", "are", "special", "attributes", "useful", "in", "sorting", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2105467193", "fields": {"term": "What can you sort on?", "namespace": "open-p", "hash": -2105467193, "terms": ["What can you sort on?", "can you sort on?", "you sort on?", "sort on?", "on?"], "tokens": ["What", "can", "you", "sort", "on", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2036762241", "fields": {"term": "What happens when sorting multivalued?", "namespace": "open-p", "hash": 2036762241, "terms": ["What happens when sorting multivalued?", "happens when sorting multivalued?", "when sorting multivalued?", "sorting multivalued?", "multivalued?"], "tokens": ["What", "happens", "when", "sorting", "multivalued", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-34835317", "fields": {"term": "What is match-phase enabled for?", "namespace": "open-p", "hash": -34835317, "terms": ["What is match-phase enabled for?", "is match-phase enabled for?", "match-phase enabled for?", "enabled for?", "for?"], "tokens": ["What", "is", "match", "phase", "enabled", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1104932619", "fields": {"term": "What is fast-search on numeric attributes?", "namespace": "open-p", "hash": 1104932619, "terms": ["What is fast-search on numeric attributes?", "is fast-search on numeric attributes?", "fast-search on numeric attributes?", "on numeric attributes?", "numeric attributes?", "attributes?"], "tokens": ["What", "is", "fast", "search", "on", "numeric", "attributes", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1844502580", "fields": {"term": "How does Vespa optimize sorting results?", "namespace": "open-p", "hash": -1844502580, "terms": ["How does Vespa optimize sorting results?", "does Vespa optimize sorting results?", "Vespa optimize sorting results?", "optimize sorting results?", "sorting results?", "results?"], "tokens": ["How", "does", "Vespa", "optimize", "sorting", "results", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::448801652", "fields": {"term": "How to disable sorting degrading?", "namespace": "open-p", "hash": 448801652, "terms": ["How to disable sorting degrading?", "to disable sorting degrading?", "disable sorting degrading?", "sorting degrading?", "degrading?"], "tokens": ["How", "to", "disable", "sorting", "degrading", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1303679969", "fields": {"term": "What is uca in sorting?", "namespace": "open-p", "hash": -1303679969, "terms": ["What is uca in sorting?", "is uca in sorting?", "uca in sorting?", "in sorting?", "sorting?"], "tokens": ["What", "is", "uca", "in", "sorting", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-787784794", "fields": {"term": "What is the syntax for sorting by surname?", "namespace": "open-p", "hash": -787784794, "terms": ["What is the syntax for sorting by surname?", "is the syntax for sorting by surname?", "the syntax for sorting by surname?", "syntax for sorting by surname?", "for sorting by surname?", "sorting by surname?", "by surname?", "surname?"], "tokens": ["What", "is", "the", "syntax", "for", "sorting", "by", "surname", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::384458958", "fields": {"term": "What does the syntax for sorting by surname with youngest ones first look like?", "namespace": "open-p", "hash": 384458958, "terms": ["What does the syntax for sorting by surname with youngest ones first look like?", "does the syntax for sorting by surname with youngest ones first look like?", "the syntax for sorting by surname with youngest ones first look like?", "syntax for sorting by surname with youngest ones first look like?", "for sorting by surname with youngest ones first look like?", "sorting by surname with youngest ones first look like?", "by surname with youngest ones first look like?", "surname with youngest ones first look like?", "with youngest ones first look like?", "youngest ones first look like?", "ones first look like?", "first look like?", "look like?", "like?"], "tokens": ["What", "does", "the", "syntax", "for", "sorting", "by", "surname", "with", "youngest", "ones", "first", "look", "like", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::426027900", "fields": {"term": "How is sorting prioritized within equal yearofbirth groups?", "namespace": "open-p", "hash": 426027900, "terms": ["How is sorting prioritized within equal yearofbirth groups?", "is sorting prioritized within equal yearofbirth groups?", "sorting prioritized within equal yearofbirth groups?", "prioritized within equal yearofbirth groups?", "within equal yearofbirth groups?", "equal yearofbirth groups?", "yearofbirth groups?", "groups?"], "tokens": ["How", "is", "sorting", "prioritized", "within", "equal", "yearofbirth", "groups", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1804045009", "fields": {"term": "What is the /state/v1 API?", "namespace": "open-p", "hash": 1804045009, "terms": ["What is the /state/v1 API?", "is the /state/v1 API?", "the /state/v1 API?", "/state/v1 API?", "API?"], "tokens": ["What", "is", "the", "/", "state", "/", "v1", "API", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::848002442", "fields": {"term": "What is the multinode-HA sample app?", "namespace": "open-p", "hash": 848002442, "terms": ["What is the multinode-HA sample app?", "is the multinode-HA sample app?", "the multinode-HA sample app?", "multinode-HA sample app?", "sample app?", "app?"], "tokens": ["What", "is", "the", "multinode", "HA", "sample", "app", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2048250550", "fields": {"term": "Where can I find examples of using /state/v1 API?", "namespace": "open-p", "hash": -2048250550, "terms": ["Where can I find examples of using /state/v1 API?", "can I find examples of using /state/v1 API?", "I find examples of using /state/v1 API?", "find examples of using /state/v1 API?", "examples of using /state/v1 API?", "of using /state/v1 API?", "using /state/v1 API?", "/state/v1 API?", "API?"], "tokens": ["Where", "can", "I", "find", "examples", "of", "using", "/", "state", "/", "v1", "API", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1899493109", "fields": {"term": "What is the state/v1/config operation?", "namespace": "open-p", "hash": 1899493109, "terms": ["What is the state/v1/config operation?", "is the state/v1/config operation?", "the state/v1/config operation?", "state/v1/config operation?", "operation?"], "tokens": ["What", "is", "the", "state", "/", "v1", "/", "config", "operation", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1156623179", "fields": {"term": "What is returned by /state/v1/version?", "namespace": "open-p", "hash": -1156623179, "terms": ["What is returned by /state/v1/version?", "is returned by /state/v1/version?", "returned by /state/v1/version?", "by /state/v1/version?", "/state/v1/version?"], "tokens": ["What", "is", "returned", "by", "/", "state", "/", "v1", "/", "version", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-652500655", "fields": {"term": "What does /state/v1/health return?", "namespace": "open-p", "hash": -652500655, "terms": ["What does /state/v1/health return?", "does /state/v1/health return?", "/state/v1/health return?", "return?"], "tokens": ["What", "does", "/", "state", "/", "v1", "/", "health", "return", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2117416981", "fields": {"term": "What are the metrics in /state/v1/metrics?", "namespace": "open-p", "hash": 2117416981, "terms": ["What are the metrics in /state/v1/metrics?", "are the metrics in /state/v1/metrics?", "the metrics in /state/v1/metrics?", "metrics in /state/v1/metrics?", "in /state/v1/metrics?", "/state/v1/metrics?"], "tokens": ["What", "are", "the", "metrics", "in", "/", "state", "/", "v1", "/", "metrics", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2075981344", "fields": {"term": "What is returned by /state/v1/metrics/histograms?", "namespace": "open-p", "hash": 2075981344, "terms": ["What is returned by /state/v1/metrics/histograms?", "is returned by /state/v1/metrics/histograms?", "returned by /state/v1/metrics/histograms?", "by /state/v1/metrics/histograms?", "/state/v1/metrics/histograms?"], "tokens": ["What", "is", "returned", "by", "/", "state", "/", "v1", "/", "metrics", "/", "histograms", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1062060577", "fields": {"term": "What is the implementation of the histograms?", "namespace": "open-p", "hash": 1062060577, "terms": ["What is the implementation of the histograms?", "is the implementation of the histograms?", "the implementation of the histograms?", "implementation of the histograms?", "of the histograms?", "the histograms?", "histograms?"], "tokens": ["What", "is", "the", "implementation", "of", "the", "histograms", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-848953464", "fields": {"term": "What does code 200 mean?", "namespace": "open-p", "hash": -848953464, "terms": ["What does code 200 mean?", "does code 200 mean?", "code 200 mean?", "200 mean?", "mean?"], "tokens": ["What", "does", "code", "200", "mean", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1837398522", "fields": {"term": "What is the JSON response format?", "namespace": "open-p", "hash": -1837398522, "terms": ["What is the JSON response format?", "is the JSON response format?", "the JSON response format?", "JSON response format?", "response format?", "format?"], "tokens": ["What", "is", "the", "JSON", "response", "format", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1215808918", "fields": {"term": "What is the root element for /state/v1/config?", "namespace": "open-p", "hash": -1215808918, "terms": ["What is the root element for /state/v1/config?", "is the root element for /state/v1/config?", "the root element for /state/v1/config?", "root element for /state/v1/config?", "element for /state/v1/config?", "for /state/v1/config?", "/state/v1/config?"], "tokens": ["What", "is", "the", "root", "element", "for", "/", "state", "/", "v1", "/", "config", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::697264866", "fields": {"term": "What is 'code' in the 'status' element?", "namespace": "open-p", "hash": 697264866, "terms": ["What is 'code' in the 'status' element?", "is 'code' in the 'status' element?", "'code' in the 'status' element?", "in the 'status' element?", "the 'status' element?", "'status' element?", "element?"], "tokens": ["What", "is", "'", "code", "'", "in", "the", "'", "status", "'", "element", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1786514081", "fields": {"term": "What is 'message' in the 'status' element?", "namespace": "open-p", "hash": -1786514081, "terms": ["What is 'message' in the 'status' element?", "is 'message' in the 'status' element?", "'message' in the 'status' element?", "in the 'status' element?", "the 'status' element?", "'status' element?", "element?"], "tokens": ["What", "is", "'", "message", "'", "in", "the", "'", "status", "'", "element", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2144276870", "fields": {"term": "What machine-learned models does Vespa support?", "namespace": "open-p", "hash": 2144276870, "terms": ["What machine-learned models does Vespa support?", "machine-learned models does Vespa support?", "models does Vespa support?", "does Vespa support?", "Vespa support?", "support?"], "tokens": ["What", "machine", "learned", "models", "does", "Vespa", "support", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-663052519", "fields": {"term": "How to enable REST API and Java API?", "namespace": "open-p", "hash": -663052519, "terms": ["How to enable REST API and Java API?", "to enable REST API and Java API?", "enable REST API and Java API?", "REST API and Java API?", "API and Java API?", "and Java API?", "Java API?", "API?"], "tokens": ["How", "to", "enable", "REST", "API", "and", "Java", "API", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1666632728", "fields": {"term": "Where should model-evaluation tag be added?", "namespace": "open-p", "hash": -1666632728, "terms": ["Where should model-evaluation tag be added?", "should model-evaluation tag be added?", "model-evaluation tag be added?", "tag be added?", "be added?", "added?"], "tokens": ["Where", "should", "model", "evaluation", "tag", "be", "added", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1447975359", "fields": {"term": "What is the purpose of model-evaluation tag?", "namespace": "open-p", "hash": -1447975359, "terms": ["What is the purpose of model-evaluation tag?", "is the purpose of model-evaluation tag?", "the purpose of model-evaluation tag?", "purpose of model-evaluation tag?", "of model-evaluation tag?", "model-evaluation tag?", "tag?"], "tokens": ["What", "is", "the", "purpose", "of", "model", "evaluation", "tag", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-319494289", "fields": {"term": "What are inference session options for ONNX models?", "namespace": "open-p", "hash": -319494289, "terms": ["What are inference session options for ONNX models?", "are inference session options for ONNX models?", "inference session options for ONNX models?", "session options for ONNX models?", "options for ONNX models?", "for ONNX models?", "ONNX models?", "models?"], "tokens": ["What", "are", "inference", "session", "options", "for", "ONNX", "models", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1964352651", "fields": {"term": "What are ONNX inference options?", "namespace": "open-p", "hash": 1964352651, "terms": ["What are ONNX inference options?", "are ONNX inference options?", "ONNX inference options?", "inference options?", "options?"], "tokens": ["What", "are", "ONNX", "inference", "options", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-294817684", "fields": {"term": "What is the REST API path for model evaluation?", "namespace": "open-p", "hash": -294817684, "terms": ["What is the REST API path for model evaluation?", "is the REST API path for model evaluation?", "the REST API path for model evaluation?", "REST API path for model evaluation?", "API path for model evaluation?", "path for model evaluation?", "for model evaluation?", "model evaluation?", "evaluation?"], "tokens": ["What", "is", "the", "REST", "API", "path", "for", "model", "evaluation", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1579391712", "fields": {"term": "How do you evaluate a model using the REST API?", "namespace": "open-p", "hash": -1579391712, "terms": ["How do you evaluate a model using the REST API?", "do you evaluate a model using the REST API?", "you evaluate a model using the REST API?", "evaluate a model using the REST API?", "a model using the REST API?", "model using the REST API?", "using the REST API?", "the REST API?", "REST API?", "API?"], "tokens": ["How", "do", "you", "evaluate", "a", "model", "using", "the", "REST", "API", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1176085893", "fields": {"term": "What are expected input parameters for model evaluation?", "namespace": "open-p", "hash": 1176085893, "terms": ["What are expected input parameters for model evaluation?", "are expected input parameters for model evaluation?", "expected input parameters for model evaluation?", "input parameters for model evaluation?", "parameters for model evaluation?", "for model evaluation?", "model evaluation?", "evaluation?"], "tokens": ["What", "are", "expected", "input", "parameters", "for", "model", "evaluation", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::404984575", "fields": {"term": "What format should input parameters be in for model evaluation?", "namespace": "open-p", "hash": 404984575, "terms": ["What format should input parameters be in for model evaluation?", "format should input parameters be in for model evaluation?", "should input parameters be in for model evaluation?", "input parameters be in for model evaluation?", "parameters be in for model evaluation?", "be in for model evaluation?", "in for model evaluation?", "for model evaluation?", "model evaluation?", "evaluation?"], "tokens": ["What", "format", "should", "input", "parameters", "be", "in", "for", "model", "evaluation", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1805560016", "fields": {"term": "Where can you find information about models in your application package?", "namespace": "open-p", "hash": 1805560016, "terms": ["Where can you find information about models in your application package?", "can you find information about models in your application package?", "you find information about models in your application package?", "find information about models in your application package?", "information about models in your application package?", "about models in your application package?", "models in your application package?", "in your application package?", "your application package?", "application package?", "package?"], "tokens": ["Where", "can", "you", "find", "information", "about", "models", "in", "your", "application", "package", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-469345162", "fields": {"term": "Can you deploy multiple models in your application package?", "namespace": "open-p", "hash": -469345162, "terms": ["Can you deploy multiple models in your application package?", "you deploy multiple models in your application package?", "deploy multiple models in your application package?", "multiple models in your application package?", "models in your application package?", "in your application package?", "your application package?", "application package?", "package?"], "tokens": ["Can", "you", "deploy", "multiple", "models", "in", "your", "application", "package", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-977285052", "fields": {"term": "What does specify when evaluating a model?", "namespace": "open-p", "hash": -977285052, "terms": ["What does specify when evaluating a model?", "does specify when evaluating a model?", " specify when evaluating a model?", "specify when evaluating a model?", "when evaluating a model?", "evaluating a model?", "a model?", "model?"], "tokens": ["What", "does", "<", "function", ">", "specify", "when", "evaluating", "a", "model", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-169531319", "fields": {"term": "What is the expected format for input parameters for evaluating a model?", "namespace": "open-p", "hash": -169531319, "terms": ["What is the expected format for input parameters for evaluating a model?", "is the expected format for input parameters for evaluating a model?", "the expected format for input parameters for evaluating a model?", "expected format for input parameters for evaluating a model?", "format for input parameters for evaluating a model?", "for input parameters for evaluating a model?", "input parameters for evaluating a model?", "parameters for evaluating a model?", "for evaluating a model?", "evaluating a model?", "a model?", "model?"], "tokens": ["What", "is", "the", "expected", "format", "for", "input", "parameters", "for", "evaluating", "a", "model", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::882055801", "fields": {"term": "What is the format.tensors parameter for?", "namespace": "open-p", "hash": 882055801, "terms": ["What is the format.tensors parameter for?", "is the format.tensors parameter for?", "the format.tensors parameter for?", "format.tensors parameter for?", "parameter for?", "for?"], "tokens": ["What", "is", "the", "format", "tensors", "parameter", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1493318332", "fields": {"term": "What is ModelsEvaluator?", "namespace": "open-p", "hash": -1493318332, "terms": ["What is ModelsEvaluator?", "is ModelsEvaluator?", "ModelsEvaluator?"], "tokens": ["What", "is", "ModelsEvaluator", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1196722604", "fields": {"term": "How do you bind inputs?", "namespace": "open-p", "hash": 1196722604, "terms": ["How do you bind inputs?", "do you bind inputs?", "you bind inputs?", "bind inputs?", "inputs?"], "tokens": ["How", "do", "you", "bind", "inputs", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::635844223", "fields": {"term": "What does Java interface offer?", "namespace": "open-p", "hash": 635844223, "terms": ["What does Java interface offer?", "does Java interface offer?", "Java interface offer?", "interface offer?", "offer?"], "tokens": ["What", "does", "Java", "interface", "offer", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-872338846", "fields": {"term": "How do you evaluate a model?", "namespace": "open-p", "hash": -872338846, "terms": ["How do you evaluate a model?", "do you evaluate a model?", "you evaluate a model?", "evaluate a model?", "a model?", "model?"], "tokens": ["How", "do", "you", "evaluate", "a", "model", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1196114475", "fields": {"term": "What is the model-inference sample app?", "namespace": "open-p", "hash": -1196114475, "terms": ["What is the model-inference sample app?", "is the model-inference sample app?", "the model-inference sample app?", "model-inference sample app?", "sample app?", "app?"], "tokens": ["What", "is", "the", "model", "inference", "sample", "app", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2055038984", "fields": {"term": "What is FunctionEvaluator?", "namespace": "open-p", "hash": 2055038984, "terms": ["What is FunctionEvaluator?", "is FunctionEvaluator?", "FunctionEvaluator?"], "tokens": ["What", "is", "FunctionEvaluator", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1872472028", "fields": {"term": "What is ModelsEvaluatorTester?", "namespace": "open-p", "hash": 1872472028, "terms": ["What is ModelsEvaluatorTester?", "is ModelsEvaluatorTester?", "ModelsEvaluatorTester?"], "tokens": ["What", "is", "ModelsEvaluatorTester", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2024278224", "fields": {"term": "How to create a testable ModelsEvaluator?", "namespace": "open-p", "hash": 2024278224, "terms": ["How to create a testable ModelsEvaluator?", "to create a testable ModelsEvaluator?", "create a testable ModelsEvaluator?", "a testable ModelsEvaluator?", "testable ModelsEvaluator?", "ModelsEvaluator?"], "tokens": ["How", "to", "create", "a", "testable", "ModelsEvaluator", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-721901988", "fields": {"term": "What does ModelsEvaluator object contain?", "namespace": "open-p", "hash": -721901988, "terms": ["What does ModelsEvaluator object contain?", "does ModelsEvaluator object contain?", "ModelsEvaluator object contain?", "object contain?", "contain?"], "tokens": ["What", "does", "ModelsEvaluator", "object", "contain", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1185717113", "fields": {"term": "What is ONNX Runtime?", "namespace": "open-p", "hash": 1185717113, "terms": ["What is ONNX Runtime?", "is ONNX Runtime?", "ONNX Runtime?", "Runtime?"], "tokens": ["What", "is", "ONNX", "Runtime", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1628305413", "fields": {"term": "How do you tune inference?", "namespace": "open-p", "hash": -1628305413, "terms": ["How do you tune inference?", "do you tune inference?", "you tune inference?", "tune inference?", "inference?"], "tokens": ["How", "do", "you", "tune", "inference", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-950200836", "fields": {"term": "What is ``?", "namespace": "open-p", "hash": -950200836, "terms": ["What is ``?", "is ``?", "``?"], "tokens": ["What", "is", "`<", "model", "evaluation", ">`?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1154098809", "fields": {"term": "What is the purpose of gpu-device?", "namespace": "open-p", "hash": 1154098809, "terms": ["What is the purpose of gpu-device?", "is the purpose of gpu-device?", "the purpose of gpu-device?", "purpose of gpu-device?", "of gpu-device?", "gpu-device?"], "tokens": ["What", "is", "the", "purpose", "of", "gpu", "device", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-592744252", "fields": {"term": "What is execution-mode?", "namespace": "open-p", "hash": -592744252, "terms": ["What is execution-mode?", "is execution-mode?", "execution-mode?"], "tokens": ["What", "is", "execution", "mode", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1673759136", "fields": {"term": "What is the purpose of model-evaluation?", "namespace": "open-p", "hash": 1673759136, "terms": ["What is the purpose of model-evaluation?", "is the purpose of model-evaluation?", "the purpose of model-evaluation?", "purpose of model-evaluation?", "of model-evaluation?", "model-evaluation?"], "tokens": ["What", "is", "the", "purpose", "of", "model", "evaluation", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-344434706", "fields": {"term": "What are *.model* files?", "namespace": "open-p", "hash": -344434706, "terms": ["What are *.model* files?", "are *.model* files?", "*.model* files?", "files?"], "tokens": ["What", "are", "*", "model", "*", "files", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::839111094", "fields": {"term": "Where are *.model* files located?", "namespace": "open-p", "hash": 839111094, "terms": ["Where are *.model* files located?", "are *.model* files located?", "*.model* files located?", "files located?", "located?"], "tokens": ["Where", "are", "*", "model", "*", "files", "located", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1751875893", "fields": {"term": "What is the suffix for model files?", "namespace": "open-p", "hash": 1751875893, "terms": ["What is the suffix for model files?", "is the suffix for model files?", "the suffix for model files?", "suffix for model files?", "for model files?", "model files?", "files?"], "tokens": ["What", "is", "the", "suffix", "for", "model", "files", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1604337524", "fields": {"term": "What are the constants?", "namespace": "open-p", "hash": -1604337524, "terms": ["What are the constants?", "are the constants?", "the constants?", "constants?"], "tokens": ["What", "are", "the", "constants", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2087419444", "fields": {"term": "What is a constant in Vespa?", "namespace": "open-p", "hash": -2087419444, "terms": ["What is a constant in Vespa?", "is a constant in Vespa?", "a constant in Vespa?", "constant in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["What", "is", "a", "constant", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1132960168", "fields": {"term": "What can be the type of a constant?", "namespace": "open-p", "hash": 1132960168, "terms": ["What can be the type of a constant?", "can be the type of a constant?", "be the type of a constant?", "the type of a constant?", "type of a constant?", "of a constant?", "a constant?", "constant?"], "tokens": ["What", "can", "be", "the", "type", "of", "a", "constant", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1387047246", "fields": {"term": "What is the format of a tensor constant?", "namespace": "open-p", "hash": -1387047246, "terms": ["What is the format of a tensor constant?", "is the format of a tensor constant?", "the format of a tensor constant?", "format of a tensor constant?", "of a tensor constant?", "a tensor constant?", "tensor constant?", "constant?"], "tokens": ["What", "is", "the", "format", "of", "a", "tensor", "constant", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1709874035", "fields": {"term": "Can a tensor constant be stored in a file?", "namespace": "open-p", "hash": 1709874035, "terms": ["Can a tensor constant be stored in a file?", "a tensor constant be stored in a file?", "tensor constant be stored in a file?", "constant be stored in a file?", "be stored in a file?", "stored in a file?", "in a file?", "a file?", "file?"], "tokens": ["Can", "a", "tensor", "constant", "be", "stored", "in", "a", "file", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1101634264", "fields": {"term": "Can a stored tensor constant be compressed?", "namespace": "open-p", "hash": -1101634264, "terms": ["Can a stored tensor constant be compressed?", "a stored tensor constant be compressed?", "stored tensor constant be compressed?", "tensor constant be compressed?", "constant be compressed?", "be compressed?", "compressed?"], "tokens": ["Can", "a", "stored", "tensor", "constant", "be", "compressed", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1146367692", "fields": {"term": "What is the maximum size of a tensor constant in Vespa?", "namespace": "open-p", "hash": 1146367692, "terms": ["What is the maximum size of a tensor constant in Vespa?", "is the maximum size of a tensor constant in Vespa?", "the maximum size of a tensor constant in Vespa?", "maximum size of a tensor constant in Vespa?", "size of a tensor constant in Vespa?", "of a tensor constant in Vespa?", "a tensor constant in Vespa?", "tensor constant in Vespa?", "constant in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["What", "is", "the", "maximum", "size", "of", "a", "tensor", "constant", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1807238408", "fields": {"term": "What is an example.model file?", "namespace": "open-p", "hash": -1807238408, "terms": ["What is an example.model file?", "is an example.model file?", "an example.model file?", "example.model file?", "file?"], "tokens": ["What", "is", "an", "example", "model", "file", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1546762700", "fields": {"term": "What is the directory tree?", "namespace": "open-p", "hash": 1546762700, "terms": ["What is the directory tree?", "is the directory tree?", "the directory tree?", "directory tree?", "tree?"], "tokens": ["What", "is", "the", "directory", "tree", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1067175581", "fields": {"term": "What inputs must be declared?", "namespace": "open-p", "hash": -1067175581, "terms": ["What inputs must be declared?", "inputs must be declared?", "must be declared?", "be declared?", "declared?"], "tokens": ["What", "inputs", "must", "be", "declared", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-48955201", "fields": {"term": "What are the functions available in the model?", "namespace": "open-p", "hash": -48955201, "terms": ["What are the functions available in the model?", "are the functions available in the model?", "the functions available in the model?", "functions available in the model?", "available in the model?", "in the model?", "the model?", "model?"], "tokens": ["What", "are", "the", "functions", "available", "in", "the", "model", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1591453489", "fields": {"term": "What does foo1 expression do?", "namespace": "open-p", "hash": 1591453489, "terms": ["What does foo1 expression do?", "does foo1 expression do?", "foo1 expression do?", "expression do?", "do?"], "tokens": ["What", "does", "foo1", "expression", "do", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1003127302", "fields": {"term": "What is streaming search?", "namespace": "open-p", "hash": -1003127302, "terms": ["What is streaming search?", "is streaming search?", "streaming search?", "search?"], "tokens": ["What", "is", "streaming", "search", "?"], "corpus_count": 4, "document_count": 4}}, -{"put": "id:term:term::253071612", "fields": {"term": "How does streaming search work?", "namespace": "open-p", "hash": 253071612, "terms": ["How does streaming search work?", "does streaming search work?", "streaming search work?", "search work?", "work?"], "tokens": ["How", "does", "streaming", "search", "work", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::1560450318", "fields": {"term": "When to use streaming search?", "namespace": "open-p", "hash": 1560450318, "terms": ["When to use streaming search?", "to use streaming search?", "use streaming search?", "streaming search?", "search?"], "tokens": ["When", "to", "use", "streaming", "search", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-367338360", "fields": {"term": "What are the benefits of streaming search?", "namespace": "open-p", "hash": -367338360, "terms": ["What are the benefits of streaming search?", "are the benefits of streaming search?", "the benefits of streaming search?", "benefits of streaming search?", "of streaming search?", "streaming search?", "search?"], "tokens": ["What", "are", "the", "benefits", "of", "streaming", "search", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-16576687", "fields": {"term": "What are the limitations of streaming search?", "namespace": "open-p", "hash": -16576687, "terms": ["What are the limitations of streaming search?", "are the limitations of streaming search?", "the limitations of streaming search?", "limitations of streaming search?", "of streaming search?", "streaming search?", "search?"], "tokens": ["What", "are", "the", "limitations", "of", "streaming", "search", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-979867116", "fields": {"term": "Which features are not supported by streaming search?", "namespace": "open-p", "hash": -979867116, "terms": ["Which features are not supported by streaming search?", "features are not supported by streaming search?", "are not supported by streaming search?", "not supported by streaming search?", "supported by streaming search?", "by streaming search?", "streaming search?", "search?"], "tokens": ["Which", "features", "are", "not", "supported", "by", "streaming", "search", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-765251151", "fields": {"term": "How does Vespa handle subsets in streaming search?", "namespace": "open-p", "hash": -765251151, "terms": ["How does Vespa handle subsets in streaming search?", "does Vespa handle subsets in streaming search?", "Vespa handle subsets in streaming search?", "handle subsets in streaming search?", "subsets in streaming search?", "in streaming search?", "streaming search?", "search?"], "tokens": ["How", "does", "Vespa", "handle", "subsets", "in", "streaming", "search", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1387845011", "fields": {"term": "What is the difference between indexed and streaming search?", "namespace": "open-p", "hash": 1387845011, "terms": ["What is the difference between indexed and streaming search?", "is the difference between indexed and streaming search?", "the difference between indexed and streaming search?", "difference between indexed and streaming search?", "between indexed and streaming search?", "indexed and streaming search?", "and streaming search?", "streaming search?", "search?"], "tokens": ["What", "is", "the", "difference", "between", "indexed", "and", "streaming", "search", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1033351007", "fields": {"term": "What are the steps to use streaming search?", "namespace": "open-p", "hash": -1033351007, "terms": ["What are the steps to use streaming search?", "are the steps to use streaming search?", "the steps to use streaming search?", "steps to use streaming search?", "to use streaming search?", "use streaming search?", "streaming search?", "search?"], "tokens": ["What", "are", "the", "steps", "to", "use", "streaming", "search", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1458622184", "fields": {"term": "Where can I find sample apps for streaming search?", "namespace": "open-p", "hash": -1458622184, "terms": ["Where can I find sample apps for streaming search?", "can I find sample apps for streaming search?", "I find sample apps for streaming search?", "find sample apps for streaming search?", "sample apps for streaming search?", "apps for streaming search?", "for streaming search?", "streaming search?", "search?"], "tokens": ["Where", "can", "I", "find", "sample", "apps", "for", "streaming", "search", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2005655138", "fields": {"term": "What is match configuration?", "namespace": "open-p", "hash": 2005655138, "terms": ["What is match configuration?", "is match configuration?", "match configuration?", "configuration?"], "tokens": ["What", "is", "match", "configuration", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1628789108", "fields": {"term": "How to do substring matching?", "namespace": "open-p", "hash": 1628789108, "terms": ["How to do substring matching?", "to do substring matching?", "do substring matching?", "substring matching?", "matching?"], "tokens": ["How", "to", "do", "substring", "matching", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1335668539", "fields": {"term": "How to search a field exactly?", "namespace": "open-p", "hash": -1335668539, "terms": ["How to search a field exactly?", "to search a field exactly?", "search a field exactly?", "a field exactly?", "field exactly?", "exactly?"], "tokens": ["How", "to", "search", "a", "field", "exactly", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1766339169", "fields": {"term": "What is fieldspec?", "namespace": "open-p", "hash": -1766339169, "terms": ["What is fieldspec?", "is fieldspec?", "fieldspec?"], "tokens": ["What", "is", "fieldspec", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-108385367", "fields": {"term": "What is the value of arg1?", "namespace": "open-p", "hash": -108385367, "terms": ["What is the value of arg1?", "is the value of arg1?", "the value of arg1?", "value of arg1?", "of arg1?", "arg1?"], "tokens": ["What", "is", "the", "value", "of", "arg1", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1773436066", "fields": {"term": "What is the disk sizing for streaming search?", "namespace": "open-p", "hash": 1773436066, "terms": ["What is the disk sizing for streaming search?", "is the disk sizing for streaming search?", "the disk sizing for streaming search?", "disk sizing for streaming search?", "sizing for streaming search?", "for streaming search?", "streaming search?", "search?"], "tokens": ["What", "is", "the", "disk", "sizing", "for", "streaming", "search", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1783152275", "fields": {"term": "What determines the disk size for streaming search?", "namespace": "open-p", "hash": -1783152275, "terms": ["What determines the disk size for streaming search?", "determines the disk size for streaming search?", "the disk size for streaming search?", "disk size for streaming search?", "size for streaming search?", "for streaming search?", "streaming search?", "search?"], "tokens": ["What", "determines", "the", "disk", "size", "for", "streaming", "search", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1563710835", "fields": {"term": "How do you estimate the disk used for streaming search?", "namespace": "open-p", "hash": 1563710835, "terms": ["How do you estimate the disk used for streaming search?", "do you estimate the disk used for streaming search?", "you estimate the disk used for streaming search?", "estimate the disk used for streaming search?", "the disk used for streaming search?", "disk used for streaming search?", "used for streaming search?", "for streaming search?", "streaming search?", "search?"], "tokens": ["How", "do", "you", "estimate", "the", "disk", "used", "for", "streaming", "search", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1769078396", "fields": {"term": "What is the relationship between disk size and number of documents in streaming search?", "namespace": "open-p", "hash": 1769078396, "terms": ["What is the relationship between disk size and number of documents in streaming search?", "is the relationship between disk size and number of documents in streaming search?", "the relationship between disk size and number of documents in streaming search?", "relationship between disk size and number of documents in streaming search?", "between disk size and number of documents in streaming search?", "disk size and number of documents in streaming search?", "size and number of documents in streaming search?", "and number of documents in streaming search?", "number of documents in streaming search?", "of documents in streaming search?", "documents in streaming search?", "in streaming search?", "streaming search?", "search?"], "tokens": ["What", "is", "the", "relationship", "between", "disk", "size", "and", "number", "of", "documents", "in", "streaming", "search", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1368173752", "fields": {"term": "How much memory does streaming search require?", "namespace": "open-p", "hash": 1368173752, "terms": ["How much memory does streaming search require?", "much memory does streaming search require?", "memory does streaming search require?", "does streaming search require?", "streaming search require?", "search require?", "require?"], "tokens": ["How", "much", "memory", "does", "streaming", "search", "require", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1539756234", "fields": {"term": "What are searchable and ready copies in Vespa?", "namespace": "open-p", "hash": 1539756234, "terms": ["What are searchable and ready copies in Vespa?", "are searchable and ready copies in Vespa?", "searchable and ready copies in Vespa?", "and ready copies in Vespa?", "ready copies in Vespa?", "copies in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["What", "are", "searchable", "and", "ready", "copies", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1883832984", "fields": {"term": "How can I disable replicas transfer during streaming search?", "namespace": "open-p", "hash": 1883832984, "terms": ["How can I disable replicas transfer during streaming search?", "can I disable replicas transfer during streaming search?", "I disable replicas transfer during streaming search?", "disable replicas transfer during streaming search?", "replicas transfer during streaming search?", "transfer during streaming search?", "during streaming search?", "streaming search?", "search?"], "tokens": ["How", "can", "I", "disable", "replicas", "transfer", "during", "streaming", "search", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1708367848", "fields": {"term": "What happens when redundancy = searchable copies?", "namespace": "open-p", "hash": -1708367848, "terms": ["What happens when redundancy = searchable copies?", "happens when redundancy = searchable copies?", "when redundancy = searchable copies?", "redundancy = searchable copies?", "= searchable copies?", "searchable copies?", "copies?"], "tokens": ["What", "happens", "when", "redundancy", "=", "searchable", "copies", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::3982438", "fields": {"term": "What is the difference in grouping in streaming and indexed search?", "namespace": "open-p", "hash": 3982438, "terms": ["What is the difference in grouping in streaming and indexed search?", "is the difference in grouping in streaming and indexed search?", "the difference in grouping in streaming and indexed search?", "difference in grouping in streaming and indexed search?", "in grouping in streaming and indexed search?", "grouping in streaming and indexed search?", "in streaming and indexed search?", "streaming and indexed search?", "and indexed search?", "indexed search?", "search?"], "tokens": ["What", "is", "the", "difference", "in", "grouping", "in", "streaming", "and", "indexed", "search", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1199902458", "fields": {"term": "What is the use of 'where(true)' in Vespa?", "namespace": "open-p", "hash": 1199902458, "terms": ["What is the use of 'where(true)' in Vespa?", "is the use of 'where(true)' in Vespa?", "the use of 'where(true)' in Vespa?", "use of 'where(true)' in Vespa?", "of 'where(true)' in Vespa?", "'where(true)' in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["What", "is", "the", "use", "of", "'", "where", "(", "true", ")'", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1857074761", "fields": {"term": "What does docidnsspecific return?", "namespace": "open-p", "hash": 1857074761, "terms": ["What does docidnsspecific return?", "does docidnsspecific return?", "docidnsspecific return?", "return?"], "tokens": ["What", "does", "docidnsspecific", "return", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-95012263", "fields": {"term": "What is the routing configuration for indexed search?", "namespace": "open-p", "hash": -95012263, "terms": ["What is the routing configuration for indexed search?", "is the routing configuration for indexed search?", "the routing configuration for indexed search?", "routing configuration for indexed search?", "configuration for indexed search?", "for indexed search?", "indexed search?", "search?"], "tokens": ["What", "is", "the", "routing", "configuration", "for", "indexed", "search", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-869407456", "fields": {"term": "What is the indexing hop for indexed search?", "namespace": "open-p", "hash": -869407456, "terms": ["What is the indexing hop for indexed search?", "is the indexing hop for indexed search?", "the indexing hop for indexed search?", "indexing hop for indexed search?", "hop for indexed search?", "for indexed search?", "indexed search?", "search?"], "tokens": ["What", "is", "the", "indexing", "hop", "for", "indexed", "search", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::905530062", "fields": {"term": "What is the routing configuration for streaming search?", "namespace": "open-p", "hash": 905530062, "terms": ["What is the routing configuration for streaming search?", "is the routing configuration for streaming search?", "the routing configuration for streaming search?", "routing configuration for streaming search?", "configuration for streaming search?", "for streaming search?", "streaming search?", "search?"], "tokens": ["What", "is", "the", "routing", "configuration", "for", "streaming", "search", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2103280900", "fields": {"term": "What is the routing for the msuic index with indexed search?", "namespace": "open-p", "hash": 2103280900, "terms": ["What is the routing for the msuic index with indexed search?", "is the routing for the msuic index with indexed search?", "the routing for the msuic index with indexed search?", "routing for the msuic index with indexed search?", "for the msuic index with indexed search?", "the msuic index with indexed search?", "msuic index with indexed search?", "index with indexed search?", "with indexed search?", "indexed search?", "search?"], "tokens": ["What", "is", "the", "routing", "for", "the", "msuic", "index", "with", "indexed", "search", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1720499018", "fields": {"term": "What is the routing for the msuic index with streaming search?", "namespace": "open-p", "hash": -1720499018, "terms": ["What is the routing for the msuic index with streaming search?", "is the routing for the msuic index with streaming search?", "the routing for the msuic index with streaming search?", "routing for the msuic index with streaming search?", "for the msuic index with streaming search?", "the msuic index with streaming search?", "msuic index with streaming search?", "index with streaming search?", "with streaming search?", "streaming search?", "search?"], "tokens": ["What", "is", "the", "routing", "for", "the", "msuic", "index", "with", "streaming", "search", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-243355247", "fields": {"term": "What is not used in streaming search?", "namespace": "open-p", "hash": -243355247, "terms": ["What is not used in streaming search?", "is not used in streaming search?", "not used in streaming search?", "used in streaming search?", "in streaming search?", "streaming search?", "search?"], "tokens": ["What", "is", "not", "used", "in", "streaming", "search", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1376070356", "fields": {"term": "What is stemming not applicable to?", "namespace": "open-p", "hash": -1376070356, "terms": ["What is stemming not applicable to?", "is stemming not applicable to?", "stemming not applicable to?", "not applicable to?", "applicable to?", "to?"], "tokens": ["What", "is", "stemming", "not", "applicable", "to", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1255378268", "fields": {"term": "What features apply to streaming?", "namespace": "open-p", "hash": 1255378268, "terms": ["What features apply to streaming?", "features apply to streaming?", "apply to streaming?", "to streaming?", "streaming?"], "tokens": ["What", "features", "apply", "to", "streaming", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::95012868", "fields": {"term": "What is the purpose of n= modifier?", "namespace": "open-p", "hash": 95012868, "terms": ["What is the purpose of n= modifier?", "is the purpose of n= modifier?", "the purpose of n= modifier?", "purpose of n= modifier?", "of n= modifier?", "n= modifier?", "modifier?"], "tokens": ["What", "is", "the", "purpose", "of", "n", "=<", "number", ">", "modifier", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1453223645", "fields": {"term": "What is the default value for Alias?", "namespace": "open-p", "hash": 1453223645, "terms": ["What is the default value for Alias?", "is the default value for Alias?", "the default value for Alias?", "default value for Alias?", "value for Alias?", "for Alias?", "Alias?"], "tokens": ["What", "is", "the", "default", "value", "for", "Alias", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1968550573", "fields": {"term": "How does n= modifier help in grouping documents?", "namespace": "open-p", "hash": -1968550573, "terms": ["How does n= modifier help in grouping documents?", "does n= modifier help in grouping documents?", "n= modifier help in grouping documents?", "modifier help in grouping documents?", "help in grouping documents?", "in grouping documents?", "grouping documents?", "documents?"], "tokens": ["How", "does", "n", "=<", "number", ">", "modifier", "help", "in", "grouping", "documents", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::115742107", "fields": {"term": "What is the range of the integer datatype?", "namespace": "open-p", "hash": 115742107, "terms": ["What is the range of the integer datatype?", "is the range of the integer datatype?", "the range of the integer datatype?", "range of the integer datatype?", "of the integer datatype?", "the integer datatype?", "integer datatype?", "datatype?"], "tokens": ["What", "is", "the", "range", "of", "the", "integer", "datatype", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1921214149", "fields": {"term": "What is the format of userid in streaming search?", "namespace": "open-p", "hash": -1921214149, "terms": ["What is the format of userid in streaming search?", "is the format of userid in streaming search?", "the format of userid in streaming search?", "format of userid in streaming search?", "of userid in streaming search?", "userid in streaming search?", "in streaming search?", "streaming search?", "search?"], "tokens": ["What", "is", "the", "format", "of", "userid", "in", "streaming", "search", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2034038364", "fields": {"term": "What is the purpose of g= modifier?", "namespace": "open-p", "hash": -2034038364, "terms": ["What is the purpose of g= modifier?", "is the purpose of g= modifier?", "the purpose of g= modifier?", "purpose of g= modifier?", "of g= modifier?", "g= modifier?", "modifier?"], "tokens": ["What", "is", "the", "purpose", "of", "g", "=<", "groupname", ">", "modifier", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::313766358", "fields": {"term": "How is g= used?", "namespace": "open-p", "hash": 313766358, "terms": ["How is g= used?", "is g= used?", "g= used?", "used?"], "tokens": ["How", "is", "g", "=<", "groupname", ">", "used", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-615306843", "fields": {"term": "Can g= be used for string grouping?", "namespace": "open-p", "hash": -615306843, "terms": ["Can g= be used for string grouping?", "g= be used for string grouping?", "be used for string grouping?", "used for string grouping?", "for string grouping?", "string grouping?", "grouping?"], "tokens": ["Can", "g", "=<", "groupname", ">", "be", "used", "for", "string", "grouping", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1481334999", "fields": {"term": "What kind of documents can streaming search stream through?", "namespace": "open-p", "hash": 1481334999, "terms": ["What kind of documents can streaming search stream through?", "kind of documents can streaming search stream through?", "of documents can streaming search stream through?", "documents can streaming search stream through?", "can streaming search stream through?", "streaming search stream through?", "search stream through?", "stream through?", "through?"], "tokens": ["What", "kind", "of", "documents", "can", "streaming", "search", "stream", "through", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2067825322", "fields": {"term": "What is the effect of using g= modifier on streaming search?", "namespace": "open-p", "hash": 2067825322, "terms": ["What is the effect of using g= modifier on streaming search?", "is the effect of using g= modifier on streaming search?", "the effect of using g= modifier on streaming search?", "effect of using g= modifier on streaming search?", "of using g= modifier on streaming search?", "using g= modifier on streaming search?", "g= modifier on streaming search?", "modifier on streaming search?", "on streaming search?", "streaming search?", "search?"], "tokens": ["What", "is", "the", "effect", "of", "using", "g", "=<", "groupname", ">", "modifier", "on", "streaming", "search", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::291835552", "fields": {"term": "What is document selection?", "namespace": "open-p", "hash": 291835552, "terms": ["What is document selection?", "is document selection?", "document selection?", "selection?"], "tokens": ["What", "is", "document", "selection", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-79720301", "fields": {"term": "What is an advanced expression?", "namespace": "open-p", "hash": -79720301, "terms": ["What is an advanced expression?", "is an advanced expression?", "an advanced expression?", "advanced expression?", "expression?"], "tokens": ["What", "is", "an", "advanced", "expression", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1331146957", "fields": {"term": "Can Alias be used with document selection?", "namespace": "open-p", "hash": 1331146957, "terms": ["Can Alias be used with document selection?", "Alias be used with document selection?", "be used with document selection?", "used with document selection?", "with document selection?", "document selection?", "selection?"], "tokens": ["Can", "Alias", "be", "used", "with", "document", "selection", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1759999953", "fields": {"term": "What is the purpose of restricting streaming search?", "namespace": "open-p", "hash": 1759999953, "terms": ["What is the purpose of restricting streaming search?", "is the purpose of restricting streaming search?", "the purpose of restricting streaming search?", "purpose of restricting streaming search?", "of restricting streaming search?", "restricting streaming search?", "streaming search?", "search?"], "tokens": ["What", "is", "the", "purpose", "of", "restricting", "streaming", "search", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-595813525", "fields": {"term": "How does document selection work with streaming search?", "namespace": "open-p", "hash": -595813525, "terms": ["How does document selection work with streaming search?", "does document selection work with streaming search?", "document selection work with streaming search?", "selection work with streaming search?", "work with streaming search?", "with streaming search?", "streaming search?", "search?"], "tokens": ["How", "does", "document", "selection", "work", "with", "streaming", "search", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::85094575", "fields": {"term": "What is Alias?", "namespace": "open-p", "hash": 85094575, "terms": ["What is Alias?", "is Alias?", "Alias?"], "tokens": ["What", "is", "Alias", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1984230702", "fields": {"term": "How can ordering reduce visiting time?", "namespace": "open-p", "hash": -1984230702, "terms": ["How can ordering reduce visiting time?", "can ordering reduce visiting time?", "ordering reduce visiting time?", "reduce visiting time?", "visiting time?", "time?"], "tokens": ["How", "can", "ordering", "reduce", "visiting", "time", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1147687496", "fields": {"term": "What is substring matching?", "namespace": "open-p", "hash": -1147687496, "terms": ["What is substring matching?", "is substring matching?", "substring matching?", "matching?"], "tokens": ["What", "is", "substring", "matching", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-6610371", "fields": {"term": "Is substring matching supported?", "namespace": "open-p", "hash": -6610371, "terms": ["Is substring matching supported?", "substring matching supported?", "matching supported?", "supported?"], "tokens": ["Is", "substring", "matching", "supported", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::802661994", "fields": {"term": "How does LID work?", "namespace": "open-p", "hash": 802661994, "terms": ["How does LID work?", "does LID work?", "LID work?", "work?"], "tokens": ["How", "does", "LID", "work", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2055958967", "fields": {"term": "What causes random file accesses?", "namespace": "open-p", "hash": 2055958967, "terms": ["What causes random file accesses?", "causes random file accesses?", "random file accesses?", "file accesses?", "accesses?"], "tokens": ["What", "causes", "random", "file", "accesses", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::678360106", "fields": {"term": "What is bucket localization?", "namespace": "open-p", "hash": 678360106, "terms": ["What is bucket localization?", "is bucket localization?", "bucket localization?", "localization?"], "tokens": ["What", "is", "bucket", "localization", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-506511786", "fields": {"term": "How are document IDs generated for a bucket?", "namespace": "open-p", "hash": -506511786, "terms": ["How are document IDs generated for a bucket?", "are document IDs generated for a bucket?", "document IDs generated for a bucket?", "IDs generated for a bucket?", "generated for a bucket?", "for a bucket?", "a bucket?", "bucket?"], "tokens": ["How", "are", "document", "IDs", "generated", "for", "a", "bucket", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::572352661", "fields": {"term": "How much resource does generating a set of document IDs for a bucket take?", "namespace": "open-p", "hash": 572352661, "terms": ["How much resource does generating a set of document IDs for a bucket take?", "much resource does generating a set of document IDs for a bucket take?", "resource does generating a set of document IDs for a bucket take?", "does generating a set of document IDs for a bucket take?", "generating a set of document IDs for a bucket take?", "a set of document IDs for a bucket take?", "set of document IDs for a bucket take?", "of document IDs for a bucket take?", "document IDs for a bucket take?", "IDs for a bucket take?", "for a bucket take?", "a bucket take?", "bucket take?", "take?"], "tokens": ["How", "much", "resource", "does", "generating", "a", "set", "of", "document", "IDs", "for", "a", "bucket", "take", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1457248813", "fields": {"term": "What is a property on a document's ID?", "namespace": "open-p", "hash": 1457248813, "terms": ["What is a property on a document's ID?", "is a property on a document's ID?", "a property on a document's ID?", "property on a document's ID?", "on a document's ID?", "a document's ID?", "document's ID?", "ID?"], "tokens": ["What", "is", "a", "property", "on", "a", "document", "'", "s", "ID", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::974397022", "fields": {"term": "What is a valid streaming search?", "namespace": "open-p", "hash": 974397022, "terms": ["What is a valid streaming search?", "is a valid streaming search?", "a valid streaming search?", "valid streaming search?", "streaming search?", "search?"], "tokens": ["What", "is", "a", "valid", "streaming", "search", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::346458456", "fields": {"term": "How do you create struct fields?", "namespace": "open-p", "hash": 346458456, "terms": ["How do you create struct fields?", "do you create struct fields?", "you create struct fields?", "create struct fields?", "struct fields?", "fields?"], "tokens": ["How", "do", "you", "create", "struct", "fields", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2070764938", "fields": {"term": "How should struct fields be stored and indexed?", "namespace": "open-p", "hash": -2070764938, "terms": ["How should struct fields be stored and indexed?", "should struct fields be stored and indexed?", "struct fields be stored and indexed?", "fields be stored and indexed?", "be stored and indexed?", "stored and indexed?", "and indexed?", "indexed?"], "tokens": ["How", "should", "struct", "fields", "be", "stored", "and", "indexed", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1467920931", "fields": {"term": "Does streaming search use index structures?", "namespace": "open-p", "hash": 1467920931, "terms": ["Does streaming search use index structures?", "streaming search use index structures?", "search use index structures?", "use index structures?", "index structures?", "structures?"], "tokens": ["Does", "streaming", "search", "use", "index", "structures", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::291345845", "fields": {"term": "What rank feature is not supported in streaming search?", "namespace": "open-p", "hash": 291345845, "terms": ["What rank feature is not supported in streaming search?", "rank feature is not supported in streaming search?", "feature is not supported in streaming search?", "is not supported in streaming search?", "not supported in streaming search?", "supported in streaming search?", "in streaming search?", "streaming search?", "search?"], "tokens": ["What", "rank", "feature", "is", "not", "supported", "in", "streaming", "search", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1491220900", "fields": {"term": "What is the second phase ranking in streaming search?", "namespace": "open-p", "hash": 1491220900, "terms": ["What is the second phase ranking in streaming search?", "is the second phase ranking in streaming search?", "the second phase ranking in streaming search?", "second phase ranking in streaming search?", "phase ranking in streaming search?", "ranking in streaming search?", "in streaming search?", "streaming search?", "search?"], "tokens": ["What", "is", "the", "second", "phase", "ranking", "in", "streaming", "search", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1673990539", "fields": {"term": "Are references and imported fields supported in streaming mode?", "namespace": "open-p", "hash": -1673990539, "terms": ["Are references and imported fields supported in streaming mode?", "references and imported fields supported in streaming mode?", "and imported fields supported in streaming mode?", "imported fields supported in streaming mode?", "fields supported in streaming mode?", "supported in streaming mode?", "in streaming mode?", "streaming mode?", "mode?"], "tokens": ["Are", "references", "and", "imported", "fields", "supported", "in", "streaming", "mode", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1561844123", "fields": {"term": "What is PR #1829 about?", "namespace": "open-p", "hash": -1561844123, "terms": ["What is PR #1829 about?", "is PR #1829 about?", "PR #1829 about?", "#1829 about?", "about?"], "tokens": ["What", "is", "PR", "#", "1829", "about", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::921126181", "fields": {"term": "What is the string segment match algorithm?", "namespace": "open-p", "hash": 921126181, "terms": ["What is the string segment match algorithm?", "is the string segment match algorithm?", "the string segment match algorithm?", "string segment match algorithm?", "segment match algorithm?", "match algorithm?", "algorithm?"], "tokens": ["What", "is", "the", "string", "segment", "match", "algorithm", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1414879814", "fields": {"term": "What are string segment match metrics?", "namespace": "open-p", "hash": 1414879814, "terms": ["What are string segment match metrics?", "are string segment match metrics?", "string segment match metrics?", "segment match metrics?", "match metrics?", "metrics?"], "tokens": ["What", "are", "string", "segment", "match", "metrics", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-341916680", "fields": {"term": "What is the purpose of the string segment match algorithm?", "namespace": "open-p", "hash": -341916680, "terms": ["What is the purpose of the string segment match algorithm?", "is the purpose of the string segment match algorithm?", "the purpose of the string segment match algorithm?", "purpose of the string segment match algorithm?", "of the string segment match algorithm?", "the string segment match algorithm?", "string segment match algorithm?", "segment match algorithm?", "match algorithm?", "algorithm?"], "tokens": ["What", "is", "the", "purpose", "of", "the", "string", "segment", "match", "algorithm", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-749705477", "fields": {"term": "How does the string segment match algorithm work?", "namespace": "open-p", "hash": -749705477, "terms": ["How does the string segment match algorithm work?", "does the string segment match algorithm work?", "the string segment match algorithm work?", "string segment match algorithm work?", "segment match algorithm work?", "match algorithm work?", "algorithm work?", "work?"], "tokens": ["How", "does", "the", "string", "segment", "match", "algorithm", "work", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1070453335", "fields": {"term": "What is the format of typical queries for the string segment match algorithm?", "namespace": "open-p", "hash": 1070453335, "terms": ["What is the format of typical queries for the string segment match algorithm?", "is the format of typical queries for the string segment match algorithm?", "the format of typical queries for the string segment match algorithm?", "format of typical queries for the string segment match algorithm?", "of typical queries for the string segment match algorithm?", "typical queries for the string segment match algorithm?", "queries for the string segment match algorithm?", "for the string segment match algorithm?", "the string segment match algorithm?", "string segment match algorithm?", "segment match algorithm?", "match algorithm?", "algorithm?"], "tokens": ["What", "is", "the", "format", "of", "typical", "queries", "for", "the", "string", "segment", "match", "algorithm", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-665954570", "fields": {"term": "How does the algorithm determine the best segmentation?", "namespace": "open-p", "hash": -665954570, "terms": ["How does the algorithm determine the best segmentation?", "does the algorithm determine the best segmentation?", "the algorithm determine the best segmentation?", "algorithm determine the best segmentation?", "determine the best segmentation?", "the best segmentation?", "best segmentation?", "segmentation?"], "tokens": ["How", "does", "the", "algorithm", "determine", "the", "best", "segmentation", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1548262281", "fields": {"term": "What is the role of token proximity in the string segment match algorithm?", "namespace": "open-p", "hash": -1548262281, "terms": ["What is the role of token proximity in the string segment match algorithm?", "is the role of token proximity in the string segment match algorithm?", "the role of token proximity in the string segment match algorithm?", "role of token proximity in the string segment match algorithm?", "of token proximity in the string segment match algorithm?", "token proximity in the string segment match algorithm?", "proximity in the string segment match algorithm?", "in the string segment match algorithm?", "the string segment match algorithm?", "string segment match algorithm?", "segment match algorithm?", "match algorithm?", "algorithm?"], "tokens": ["What", "is", "the", "role", "of", "token", "proximity", "in", "the", "string", "segment", "match", "algorithm", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1178293267", "fields": {"term": "What other information is used to improve the matching of queries in the string segment match algorithm?", "namespace": "open-p", "hash": -1178293267, "terms": ["What other information is used to improve the matching of queries in the string segment match algorithm?", "other information is used to improve the matching of queries in the string segment match algorithm?", "information is used to improve the matching of queries in the string segment match algorithm?", "is used to improve the matching of queries in the string segment match algorithm?", "used to improve the matching of queries in the string segment match algorithm?", "to improve the matching of queries in the string segment match algorithm?", "improve the matching of queries in the string segment match algorithm?", "the matching of queries in the string segment match algorithm?", "matching of queries in the string segment match algorithm?", "of queries in the string segment match algorithm?", "queries in the string segment match algorithm?", "in the string segment match algorithm?", "the string segment match algorithm?", "string segment match algorithm?", "segment match algorithm?", "match algorithm?", "algorithm?"], "tokens": ["What", "other", "information", "is", "used", "to", "improve", "the", "matching", "of", "queries", "in", "the", "string", "segment", "match", "algorithm", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::837205916", "fields": {"term": "How does this algorithm calculate metrics?", "namespace": "open-p", "hash": 837205916, "terms": ["How does this algorithm calculate metrics?", "does this algorithm calculate metrics?", "this algorithm calculate metrics?", "algorithm calculate metrics?", "calculate metrics?", "metrics?"], "tokens": ["How", "does", "this", "algorithm", "calculate", "metrics", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::978321047", "fields": {"term": "What is the weight of query terms?", "namespace": "open-p", "hash": 978321047, "terms": ["What is the weight of query terms?", "is the weight of query terms?", "the weight of query terms?", "weight of query terms?", "of query terms?", "query terms?", "terms?"], "tokens": ["What", "is", "the", "weight", "of", "query", "terms", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-448386693", "fields": {"term": "What is the linguistic connectivity?", "namespace": "open-p", "hash": -448386693, "terms": ["What is the linguistic connectivity?", "is the linguistic connectivity?", "the linguistic connectivity?", "linguistic connectivity?", "connectivity?"], "tokens": ["What", "is", "the", "linguistic", "connectivity", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-391544857", "fields": {"term": "What is the algorithm metrics?", "namespace": "open-p", "hash": -391544857, "terms": ["What is the algorithm metrics?", "is the algorithm metrics?", "the algorithm metrics?", "algorithm metrics?", "metrics?"], "tokens": ["What", "is", "the", "algorithm", "metrics", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-540109868", "fields": {"term": "What is a segment?", "namespace": "open-p", "hash": -540109868, "terms": ["What is a segment?", "is a segment?", "a segment?", "segment?"], "tokens": ["What", "is", "a", "segment", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1935225082", "fields": {"term": "How is a segment start point stored?", "namespace": "open-p", "hash": 1935225082, "terms": ["How is a segment start point stored?", "is a segment start point stored?", "a segment start point stored?", "segment start point stored?", "start point stored?", "point stored?", "stored?"], "tokens": ["How", "is", "a", "segment", "start", "point", "stored", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-638634862", "fields": {"term": "What is the score deciding the best segmentation path?", "namespace": "open-p", "hash": -638634862, "terms": ["What is the score deciding the best segmentation path?", "is the score deciding the best segmentation path?", "the score deciding the best segmentation path?", "score deciding the best segmentation path?", "deciding the best segmentation path?", "the best segmentation path?", "best segmentation path?", "segmentation path?", "path?"], "tokens": ["What", "is", "the", "score", "deciding", "the", "best", "segmentation", "path", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1117258809", "fields": {"term": "What metrics are used to calculate the segment score?", "namespace": "open-p", "hash": -1117258809, "terms": ["What metrics are used to calculate the segment score?", "metrics are used to calculate the segment score?", "are used to calculate the segment score?", "used to calculate the segment score?", "to calculate the segment score?", "calculate the segment score?", "the segment score?", "segment score?", "score?"], "tokens": ["What", "metrics", "are", "used", "to", "calculate", "the", "segment", "score", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1160971126", "fields": {"term": "What technique does the algorithm use?", "namespace": "open-p", "hash": -1160971126, "terms": ["What technique does the algorithm use?", "technique does the algorithm use?", "does the algorithm use?", "the algorithm use?", "algorithm use?", "use?"], "tokens": ["What", "technique", "does", "the", "algorithm", "use", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1541388499", "fields": {"term": "What is the memory complexity?", "namespace": "open-p", "hash": -1541388499, "terms": ["What is the memory complexity?", "is the memory complexity?", "the memory complexity?", "memory complexity?", "complexity?"], "tokens": ["What", "is", "the", "memory", "complexity", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1887657765", "fields": {"term": "What is the time complexity?", "namespace": "open-p", "hash": 1887657765, "terms": ["What is the time complexity?", "is the time complexity?", "the time complexity?", "time complexity?", "complexity?"], "tokens": ["What", "is", "the", "time", "complexity", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::107609274", "fields": {"term": "What is the average time complexity?", "namespace": "open-p", "hash": 107609274, "terms": ["What is the average time complexity?", "is the average time complexity?", "the average time complexity?", "average time complexity?", "time complexity?", "complexity?"], "tokens": ["What", "is", "the", "average", "time", "complexity", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-93365067", "fields": {"term": "What are the string segment match metrics?", "namespace": "open-p", "hash": -93365067, "terms": ["What are the string segment match metrics?", "are the string segment match metrics?", "the string segment match metrics?", "string segment match metrics?", "segment match metrics?", "match metrics?", "metrics?"], "tokens": ["What", "are", "the", "string", "segment", "match", "metrics", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::989960808", "fields": {"term": "How are metrics normalized?", "namespace": "open-p", "hash": 989960808, "terms": ["How are metrics normalized?", "are metrics normalized?", "metrics normalized?", "normalized?"], "tokens": ["How", "are", "metrics", "normalized", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1715705950", "fields": {"term": "What is included in the metric set?", "namespace": "open-p", "hash": 1715705950, "terms": ["What is included in the metric set?", "is included in the metric set?", "included in the metric set?", "in the metric set?", "the metric set?", "metric set?", "set?"], "tokens": ["What", "is", "included", "in", "the", "metric", "set", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-116579483", "fields": {"term": "What are the basic normalized features?", "namespace": "open-p", "hash": -116579483, "terms": ["What are the basic normalized features?", "are the basic normalized features?", "the basic normalized features?", "basic normalized features?", "normalized features?", "features?"], "tokens": ["What", "are", "the", "basic", "normalized", "features", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1077082482", "fields": {"term": "What is the 'match' metric?", "namespace": "open-p", "hash": -1077082482, "terms": ["What is the 'match' metric?", "is the 'match' metric?", "the 'match' metric?", "'match' metric?", "metric?"], "tokens": ["What", "is", "the", "'", "match", "'", "metric", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1279772962", "fields": {"term": "How can applications choose the metrics?", "namespace": "open-p", "hash": 1279772962, "terms": ["How can applications choose the metrics?", "can applications choose the metrics?", "applications choose the metrics?", "choose the metrics?", "the metrics?", "metrics?"], "tokens": ["How", "can", "applications", "choose", "the", "metrics", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1547434922", "fields": {"term": "What are the low level, un-normalized metrics?", "namespace": "open-p", "hash": 1547434922, "terms": ["What are the low level, un-normalized metrics?", "are the low level, un-normalized metrics?", "the low level, un-normalized metrics?", "low level, un-normalized metrics?", "level, un-normalized metrics?", "un-normalized metrics?", "metrics?"], "tokens": ["What", "are", "the", "low", "level", "un", "normalized", "metrics", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1759795945", "fields": {"term": "What is the proximity table?", "namespace": "open-p", "hash": -1759795945, "terms": ["What is the proximity table?", "is the proximity table?", "the proximity table?", "proximity table?", "table?"], "tokens": ["What", "is", "the", "proximity", "table", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-936073199", "fields": {"term": "What is proximityCompletenessImportance?", "namespace": "open-p", "hash": -936073199, "terms": ["What is proximityCompletenessImportance?", "is proximityCompletenessImportance?", "proximityCompletenessImportance?"], "tokens": ["What", "is", "proximityCompletenessImportance", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1457666829", "fields": {"term": "What is relatednessImportance?", "namespace": "open-p", "hash": 1457666829, "terms": ["What is relatednessImportance?", "is relatednessImportance?", "relatednessImportance?"], "tokens": ["What", "is", "relatednessImportance", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::339600178", "fields": {"term": "What is earlinessImportance?", "namespace": "open-p", "hash": 339600178, "terms": ["What is earlinessImportance?", "is earlinessImportance?", "earlinessImportance?"], "tokens": ["What", "is", "earlinessImportance", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1115607834", "fields": {"term": "What is segmentProximityImportance?", "namespace": "open-p", "hash": -1115607834, "terms": ["What is segmentProximityImportance?", "is segmentProximityImportance?", "segmentProximityImportance?"], "tokens": ["What", "is", "segmentProximityImportance", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1796085653", "fields": {"term": "What is occurrenceImportance?", "namespace": "open-p", "hash": -1796085653, "terms": ["What is occurrenceImportance?", "is occurrenceImportance?", "occurrenceImportance?"], "tokens": ["What", "is", "occurrenceImportance", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1240902888", "fields": {"term": "What is fieldCompletenessImportance?", "namespace": "open-p", "hash": -1240902888, "terms": ["What is fieldCompletenessImportance?", "is fieldCompletenessImportance?", "fieldCompletenessImportance?"], "tokens": ["What", "is", "fieldCompletenessImportance", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2097711996", "fields": {"term": "What is the tensor model in Vespa?", "namespace": "open-p", "hash": -2097711996, "terms": ["What is the tensor model in Vespa?", "is the tensor model in Vespa?", "the tensor model in Vespa?", "tensor model in Vespa?", "model in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["What", "is", "the", "tensor", "model", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1103620599", "fields": {"term": "What are sparse dimensions?", "namespace": "open-p", "hash": -1103620599, "terms": ["What are sparse dimensions?", "are sparse dimensions?", "sparse dimensions?", "dimensions?"], "tokens": ["What", "are", "sparse", "dimensions", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1619189946", "fields": {"term": "Can tensors be used for more than neural nets?", "namespace": "open-p", "hash": 1619189946, "terms": ["Can tensors be used for more than neural nets?", "tensors be used for more than neural nets?", "be used for more than neural nets?", "used for more than neural nets?", "for more than neural nets?", "more than neural nets?", "than neural nets?", "neural nets?", "nets?"], "tokens": ["Can", "tensors", "be", "used", "for", "more", "than", "neural", "nets", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1639407530", "fields": {"term": "What does the Tensor Usage Examples page provide?", "namespace": "open-p", "hash": 1639407530, "terms": ["What does the Tensor Usage Examples page provide?", "does the Tensor Usage Examples page provide?", "the Tensor Usage Examples page provide?", "Tensor Usage Examples page provide?", "Usage Examples page provide?", "Examples page provide?", "page provide?", "provide?"], "tokens": ["What", "does", "the", "Tensor", "Usage", "Examples", "page", "provide", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-867117634", "fields": {"term": "Does Vespa support lambda computations?", "namespace": "open-p", "hash": -867117634, "terms": ["Does Vespa support lambda computations?", "Vespa support lambda computations?", "support lambda computations?", "lambda computations?", "computations?"], "tokens": ["Does", "Vespa", "support", "lambda", "computations", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-570674368", "fields": {"term": "What kind of examples are provided in Tensor Usage Examples?", "namespace": "open-p", "hash": -570674368, "terms": ["What kind of examples are provided in Tensor Usage Examples?", "kind of examples are provided in Tensor Usage Examples?", "of examples are provided in Tensor Usage Examples?", "examples are provided in Tensor Usage Examples?", "are provided in Tensor Usage Examples?", "provided in Tensor Usage Examples?", "in Tensor Usage Examples?", "Tensor Usage Examples?", "Usage Examples?", "Examples?"], "tokens": ["What", "kind", "of", "examples", "are", "provided", "in", "Tensor", "Usage", "Examples", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-188732191", "fields": {"term": "What is an ecommerce application?", "namespace": "open-p", "hash": -188732191, "terms": ["What is an ecommerce application?", "is an ecommerce application?", "an ecommerce application?", "ecommerce application?", "application?"], "tokens": ["What", "is", "an", "ecommerce", "application", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-502297105", "fields": {"term": "How do tensors specify prices?", "namespace": "open-p", "hash": -502297105, "terms": ["How do tensors specify prices?", "do tensors specify prices?", "tensors specify prices?", "specify prices?", "prices?"], "tokens": ["How", "do", "tensors", "specify", "prices", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1525679464", "fields": {"term": "What are the three tensors used?", "namespace": "open-p", "hash": 1525679464, "terms": ["What are the three tensors used?", "are the three tensors used?", "the three tensors used?", "three tensors used?", "tensors used?", "used?"], "tokens": ["What", "are", "the", "three", "tensors", "used", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-521375193", "fields": {"term": "How can promotions with a given product price and time interval be included in documents?", "namespace": "open-p", "hash": -521375193, "terms": ["How can promotions with a given product price and time interval be included in documents?", "can promotions with a given product price and time interval be included in documents?", "promotions with a given product price and time interval be included in documents?", "with a given product price and time interval be included in documents?", "a given product price and time interval be included in documents?", "given product price and time interval be included in documents?", "product price and time interval be included in documents?", "price and time interval be included in documents?", "and time interval be included in documents?", "time interval be included in documents?", "interval be included in documents?", "be included in documents?", "included in documents?", "in documents?", "documents?"], "tokens": ["How", "can", "promotions", "with", "a", "given", "product", "price", "and", "time", "interval", "be", "included", "in", "documents", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1194460029", "fields": {"term": "How can you retrieve the currently valid price of a promotion?", "namespace": "open-p", "hash": -1194460029, "terms": ["How can you retrieve the currently valid price of a promotion?", "can you retrieve the currently valid price of a promotion?", "you retrieve the currently valid price of a promotion?", "retrieve the currently valid price of a promotion?", "the currently valid price of a promotion?", "currently valid price of a promotion?", "valid price of a promotion?", "price of a promotion?", "of a promotion?", "a promotion?", "promotion?"], "tokens": ["How", "can", "you", "retrieve", "the", "currently", "valid", "price", "of", "a", "promotion", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1132525623", "fields": {"term": "What is the expression to retrieve the currently valid price?", "namespace": "open-p", "hash": -1132525623, "terms": ["What is the expression to retrieve the currently valid price?", "is the expression to retrieve the currently valid price?", "the expression to retrieve the currently valid price?", "expression to retrieve the currently valid price?", "to retrieve the currently valid price?", "retrieve the currently valid price?", "the currently valid price?", "currently valid price?", "valid price?", "price?"], "tokens": ["What", "is", "the", "expression", "to", "retrieve", "the", "currently", "valid", "price", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1332968612", "fields": {"term": "What will be returned if there is no matching interval?", "namespace": "open-p", "hash": 1332968612, "terms": ["What will be returned if there is no matching interval?", "will be returned if there is no matching interval?", "be returned if there is no matching interval?", "returned if there is no matching interval?", "if there is no matching interval?", "there is no matching interval?", "is no matching interval?", "no matching interval?", "matching interval?", "interval?"], "tokens": ["What", "will", "be", "returned", "if", "there", "is", "no", "matching", "interval", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::409984923", "fields": {"term": "How can tensor be created?", "namespace": "open-p", "hash": 409984923, "terms": ["How can tensor be created?", "can tensor be created?", "tensor be created?", "be created?", "created?"], "tokens": ["How", "can", "tensor", "be", "created", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-795612456", "fields": {"term": "What is a dense vector?", "namespace": "open-p", "hash": -795612456, "terms": ["What is a dense vector?", "is a dense vector?", "a dense vector?", "dense vector?", "vector?"], "tokens": ["What", "is", "a", "dense", "vector", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1375084695", "fields": {"term": "What is a scalar expression?", "namespace": "open-p", "hash": 1375084695, "terms": ["What is a scalar expression?", "is a scalar expression?", "a scalar expression?", "scalar expression?", "expression?"], "tokens": ["What", "is", "a", "scalar", "expression", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1270793991", "fields": {"term": "What does tensor(x[2]):[bm25(title),attribute(popularity)] do?", "namespace": "open-p", "hash": 1270793991, "terms": ["What does tensor(x[2]):[bm25(title),attribute(popularity)] do?", "does tensor(x[2]):[bm25(title),attribute(popularity)] do?", "tensor(x[2]):[bm25(title),attribute(popularity)] do?", "do?"], "tokens": ["What", "does", "tensor", "(", "x", "[", "2", "]):[", "bm25", "(", "title", ")", "attribute", "(", "popularity", ")]", "do", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::341218040", "fields": {"term": "How is the dot product calculated?", "namespace": "open-p", "hash": 341218040, "terms": ["How is the dot product calculated?", "is the dot product calculated?", "the dot product calculated?", "dot product calculated?", "product calculated?", "calculated?"], "tokens": ["How", "is", "the", "dot", "product", "calculated", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-166275846", "fields": {"term": "How to use machine-learning?", "namespace": "open-p", "hash": -166275846, "terms": ["How to use machine-learning?", "to use machine-learning?", "use machine-learning?", "machine-learning?"], "tokens": ["How", "to", "use", "machine", "learning", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1889369665", "fields": {"term": "What are the base features?", "namespace": "open-p", "hash": 1889369665, "terms": ["What are the base features?", "are the base features?", "the base features?", "base features?", "features?"], "tokens": ["What", "are", "the", "base", "features", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::248469255", "fields": {"term": "What is logistic regression?", "namespace": "open-p", "hash": 248469255, "terms": ["What is logistic regression?", "is logistic regression?", "logistic regression?", "regression?"], "tokens": ["What", "is", "logistic", "regression", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::798690492", "fields": {"term": "What is the weight tensor?", "namespace": "open-p", "hash": 798690492, "terms": ["What is the weight tensor?", "is the weight tensor?", "the weight tensor?", "weight tensor?", "tensor?"], "tokens": ["What", "is", "the", "weight", "tensor", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-273986489", "fields": {"term": "What is a constant tensor?", "namespace": "open-p", "hash": -273986489, "terms": ["What is a constant tensor?", "is a constant tensor?", "a constant tensor?", "constant tensor?", "tensor?"], "tokens": ["What", "is", "a", "constant", "tensor", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-701308446", "fields": {"term": "What is cross feature tensor?", "namespace": "open-p", "hash": -701308446, "terms": ["What is cross feature tensor?", "is cross feature tensor?", "cross feature tensor?", "feature tensor?", "tensor?"], "tokens": ["What", "is", "cross", "feature", "tensor", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1237748313", "fields": {"term": "What is a tensor product?", "namespace": "open-p", "hash": 1237748313, "terms": ["What is a tensor product?", "is a tensor product?", "a tensor product?", "tensor product?", "product?"], "tokens": ["What", "is", "a", "tensor", "product", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::988913632", "fields": {"term": "What is the matrix product?", "namespace": "open-p", "hash": 988913632, "terms": ["What is the matrix product?", "is the matrix product?", "the matrix product?", "matrix product?", "product?"], "tokens": ["What", "is", "the", "matrix", "product", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1364189342", "fields": {"term": "What is a sparse tensor product?", "namespace": "open-p", "hash": 1364189342, "terms": ["What is a sparse tensor product?", "is a sparse tensor product?", "a sparse tensor product?", "sparse tensor product?", "tensor product?", "product?"], "tokens": ["What", "is", "a", "sparse", "tensor", "product", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1166329979", "fields": {"term": "What is a query vector?", "namespace": "open-p", "hash": 1166329979, "terms": ["What is a query vector?", "is a query vector?", "a query vector?", "query vector?", "vector?"], "tokens": ["What", "is", "a", "query", "vector", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-107929498", "fields": {"term": "What are tensors?", "namespace": "open-p", "hash": -107929498, "terms": ["What are tensors?", "are tensors?", "tensors?"], "tokens": ["What", "are", "tensors", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1609359740", "fields": {"term": "How do you do a tensor lookup?", "namespace": "open-p", "hash": -1609359740, "terms": ["How do you do a tensor lookup?", "do you do a tensor lookup?", "you do a tensor lookup?", "do a tensor lookup?", "a tensor lookup?", "tensor lookup?", "lookup?"], "tokens": ["How", "do", "you", "do", "a", "tensor", "lookup", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-452204715", "fields": {"term": "What is a tensor attribute?", "namespace": "open-p", "hash": -452204715, "terms": ["What is a tensor attribute?", "is a tensor attribute?", "a tensor attribute?", "tensor attribute?", "attribute?"], "tokens": ["What", "is", "a", "tensor", "attribute", "?"], "corpus_count": 3, "document_count": 3}}, -{"put": "id:term:term::542281092", "fields": {"term": "What is the query syntax for tensors?", "namespace": "open-p", "hash": 542281092, "terms": ["What is the query syntax for tensors?", "is the query syntax for tensors?", "the query syntax for tensors?", "query syntax for tensors?", "syntax for tensors?", "for tensors?", "tensors?"], "tokens": ["What", "is", "the", "query", "syntax", "for", "tensors", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1102730147", "fields": {"term": "What happens if the key does not match anything?", "namespace": "open-p", "hash": -1102730147, "terms": ["What happens if the key does not match anything?", "happens if the key does not match anything?", "if the key does not match anything?", "the key does not match anything?", "key does not match anything?", "does not match anything?", "not match anything?", "match anything?", "anything?"], "tokens": ["What", "happens", "if", "the", "key", "does", "not", "match", "anything", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1405382592", "fields": {"term": "What is a fallback expression for tensor lookups?", "namespace": "open-p", "hash": -1405382592, "terms": ["What is a fallback expression for tensor lookups?", "is a fallback expression for tensor lookups?", "a fallback expression for tensor lookups?", "fallback expression for tensor lookups?", "expression for tensor lookups?", "for tensor lookups?", "tensor lookups?", "lookups?"], "tokens": ["What", "is", "a", "fallback", "expression", "for", "tensor", "lookups", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1080881289", "fields": {"term": "Where can I find more details about tensor performance tuning?", "namespace": "open-p", "hash": -1080881289, "terms": ["Where can I find more details about tensor performance tuning?", "can I find more details about tensor performance tuning?", "I find more details about tensor performance tuning?", "find more details about tensor performance tuning?", "more details about tensor performance tuning?", "details about tensor performance tuning?", "about tensor performance tuning?", "tensor performance tuning?", "performance tuning?", "tuning?"], "tokens": ["Where", "can", "I", "find", "more", "details", "about", "tensor", "performance", "tuning", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1308070406", "fields": {"term": "What is the tensor model?", "namespace": "open-p", "hash": -1308070406, "terms": ["What is the tensor model?", "is the tensor model?", "the tensor model?", "tensor model?", "model?"], "tokens": ["What", "is", "the", "tensor", "model", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::841222786", "fields": {"term": "How does Vespa use tensors?", "namespace": "open-p", "hash": 841222786, "terms": ["How does Vespa use tensors?", "does Vespa use tensors?", "Vespa use tensors?", "use tensors?", "tensors?"], "tokens": ["How", "does", "Vespa", "use", "tensors", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::426597333", "fields": {"term": "How do I query with tensors?", "namespace": "open-p", "hash": 426597333, "terms": ["How do I query with tensors?", "do I query with tensors?", "I query with tensors?", "query with tensors?", "with tensors?", "tensors?"], "tokens": ["How", "do", "I", "query", "with", "tensors", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::640884464", "fields": {"term": "What are tensor concepts?", "namespace": "open-p", "hash": 640884464, "terms": ["What are tensor concepts?", "are tensor concepts?", "tensor concepts?", "concepts?"], "tokens": ["What", "are", "tensor", "concepts", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-638182351", "fields": {"term": "Are there examples of tensor computations?", "namespace": "open-p", "hash": -638182351, "terms": ["Are there examples of tensor computations?", "there examples of tensor computations?", "examples of tensor computations?", "of tensor computations?", "tensor computations?", "computations?"], "tokens": ["Are", "there", "examples", "of", "tensor", "computations", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2140447964", "fields": {"term": "How do I create tensors from document fields?", "namespace": "open-p", "hash": -2140447964, "terms": ["How do I create tensors from document fields?", "do I create tensors from document fields?", "I create tensors from document fields?", "create tensors from document fields?", "tensors from document fields?", "from document fields?", "document fields?", "fields?"], "tokens": ["How", "do", "I", "create", "tensors", "from", "document", "fields", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-510601220", "fields": {"term": "What is a tensor in Vespa?", "namespace": "open-p", "hash": -510601220, "terms": ["What is a tensor in Vespa?", "is a tensor in Vespa?", "a tensor in Vespa?", "tensor in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["What", "is", "a", "tensor", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::448443366", "fields": {"term": "What is the rank of a tensor?", "namespace": "open-p", "hash": 448443366, "terms": ["What is the rank of a tensor?", "is the rank of a tensor?", "the rank of a tensor?", "rank of a tensor?", "of a tensor?", "a tensor?", "tensor?"], "tokens": ["What", "is", "the", "rank", "of", "a", "tensor", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1571662627", "fields": {"term": "What are mapped dimensions?", "namespace": "open-p", "hash": -1571662627, "terms": ["What are mapped dimensions?", "are mapped dimensions?", "mapped dimensions?", "dimensions?"], "tokens": ["What", "are", "mapped", "dimensions", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-29146226", "fields": {"term": "What are indexed dimensions?", "namespace": "open-p", "hash": -29146226, "terms": ["What are indexed dimensions?", "are indexed dimensions?", "indexed dimensions?", "dimensions?"], "tokens": ["What", "are", "indexed", "dimensions", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::904787730", "fields": {"term": "What is a mixed tensor?", "namespace": "open-p", "hash": 904787730, "terms": ["What is a mixed tensor?", "is a mixed tensor?", "a mixed tensor?", "mixed tensor?", "tensor?"], "tokens": ["What", "is", "a", "mixed", "tensor", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::384536424", "fields": {"term": "What is a tensor type?", "namespace": "open-p", "hash": 384536424, "terms": ["What is a tensor type?", "is a tensor type?", "a tensor type?", "tensor type?", "type?"], "tokens": ["What", "is", "a", "tensor", "type", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::810436765", "fields": {"term": "What is a cell in a tensor?", "namespace": "open-p", "hash": 810436765, "terms": ["What is a cell in a tensor?", "is a cell in a tensor?", "a cell in a tensor?", "cell in a tensor?", "in a tensor?", "a tensor?", "tensor?"], "tokens": ["What", "is", "a", "cell", "in", "a", "tensor", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1404430346", "fields": {"term": "What is the value type of a tensor?", "namespace": "open-p", "hash": 1404430346, "terms": ["What is the value type of a tensor?", "is the value type of a tensor?", "the value type of a tensor?", "value type of a tensor?", "type of a tensor?", "of a tensor?", "a tensor?", "tensor?"], "tokens": ["What", "is", "the", "value", "type", "of", "a", "tensor", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::518030332", "fields": {"term": "What are tensor field values?", "namespace": "open-p", "hash": 518030332, "terms": ["What are tensor field values?", "are tensor field values?", "tensor field values?", "field values?", "values?"], "tokens": ["What", "are", "tensor", "field", "values", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-420956827", "fields": {"term": "How can you add tensor cells?", "namespace": "open-p", "hash": -420956827, "terms": ["How can you add tensor cells?", "can you add tensor cells?", "you add tensor cells?", "add tensor cells?", "tensor cells?", "cells?"], "tokens": ["How", "can", "you", "add", "tensor", "cells", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1139448291", "fields": {"term": "What is the JSON format for adding tensor fields?", "namespace": "open-p", "hash": -1139448291, "terms": ["What is the JSON format for adding tensor fields?", "is the JSON format for adding tensor fields?", "the JSON format for adding tensor fields?", "JSON format for adding tensor fields?", "format for adding tensor fields?", "for adding tensor fields?", "adding tensor fields?", "tensor fields?", "fields?"], "tokens": ["What", "is", "the", "JSON", "format", "for", "adding", "tensor", "fields", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2092746566", "fields": {"term": "What is the tensor field in Vespa?", "namespace": "open-p", "hash": -2092746566, "terms": ["What is the tensor field in Vespa?", "is the tensor field in Vespa?", "the tensor field in Vespa?", "tensor field in Vespa?", "field in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["What", "is", "the", "tensor", "field", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1225876089", "fields": {"term": "What can you do with tensor cells?", "namespace": "open-p", "hash": -1225876089, "terms": ["What can you do with tensor cells?", "can you do with tensor cells?", "you do with tensor cells?", "do with tensor cells?", "with tensor cells?", "tensor cells?", "cells?"], "tokens": ["What", "can", "you", "do", "with", "tensor", "cells", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2017690818", "fields": {"term": "How can you create tensor values?", "namespace": "open-p", "hash": 2017690818, "terms": ["How can you create tensor values?", "can you create tensor values?", "you create tensor values?", "create tensor values?", "tensor values?", "values?"], "tokens": ["How", "can", "you", "create", "tensor", "values", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1226289595", "fields": {"term": "What is the tensor Java API?", "namespace": "open-p", "hash": -1226289595, "terms": ["What is the tensor Java API?", "is the tensor Java API?", "the tensor Java API?", "tensor Java API?", "Java API?", "API?"], "tokens": ["What", "is", "the", "tensor", "Java", "API", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-560406566", "fields": {"term": "How can you set tensor values in documents?", "namespace": "open-p", "hash": -560406566, "terms": ["How can you set tensor values in documents?", "can you set tensor values in documents?", "you set tensor values in documents?", "set tensor values in documents?", "tensor values in documents?", "values in documents?", "in documents?", "documents?"], "tokens": ["How", "can", "you", "set", "tensor", "values", "in", "documents", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1074124242", "fields": {"term": "What types of tensors can be indexed?", "namespace": "open-p", "hash": 1074124242, "terms": ["What types of tensors can be indexed?", "types of tensors can be indexed?", "of tensors can be indexed?", "tensors can be indexed?", "can be indexed?", "be indexed?", "indexed?"], "tokens": ["What", "types", "of", "tensors", "can", "be", "indexed", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1633506237", "fields": {"term": "How many vectors per document?", "namespace": "open-p", "hash": 1633506237, "terms": ["How many vectors per document?", "many vectors per document?", "vectors per document?", "per document?", "document?"], "tokens": ["How", "many", "vectors", "per", "document", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1096629973", "fields": {"term": "What is indexed dimension?", "namespace": "open-p", "hash": -1096629973, "terms": ["What is indexed dimension?", "is indexed dimension?", "indexed dimension?", "dimension?"], "tokens": ["What", "is", "indexed", "dimension", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1400482498", "fields": {"term": "What are tensors used for?", "namespace": "open-p", "hash": -1400482498, "terms": ["What are tensors used for?", "are tensors used for?", "tensors used for?", "used for?", "for?"], "tokens": ["What", "are", "tensors", "used", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::94051005", "fields": {"term": "How to pass tensors in queries?", "namespace": "open-p", "hash": 94051005, "terms": ["How to pass tensors in queries?", "to pass tensors in queries?", "pass tensors in queries?", "tensors in queries?", "in queries?", "queries?"], "tokens": ["How", "to", "pass", "tensors", "in", "queries", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1206620996", "fields": {"term": "What is the Tensor class?", "namespace": "open-p", "hash": 1206620996, "terms": ["What is the Tensor class?", "is the Tensor class?", "the Tensor class?", "Tensor class?", "class?"], "tokens": ["What", "is", "the", "Tensor", "class", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::252516225", "fields": {"term": "How can tensors be accessed in ranking expressions?", "namespace": "open-p", "hash": 252516225, "terms": ["How can tensors be accessed in ranking expressions?", "can tensors be accessed in ranking expressions?", "tensors be accessed in ranking expressions?", "be accessed in ranking expressions?", "accessed in ranking expressions?", "in ranking expressions?", "ranking expressions?", "expressions?"], "tokens": ["How", "can", "tensors", "be", "accessed", "in", "ranking", "expressions", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::721633635", "fields": {"term": "What is the full list of tensor functions?", "namespace": "open-p", "hash": 721633635, "terms": ["What is the full list of tensor functions?", "is the full list of tensor functions?", "the full list of tensor functions?", "full list of tensor functions?", "list of tensor functions?", "of tensor functions?", "tensor functions?", "functions?"], "tokens": ["What", "is", "the", "full", "list", "of", "tensor", "functions", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1719147114", "fields": {"term": "What is the syntax to define a tensor type in a rank profile?", "namespace": "open-p", "hash": 1719147114, "terms": ["What is the syntax to define a tensor type in a rank profile?", "is the syntax to define a tensor type in a rank profile?", "the syntax to define a tensor type in a rank profile?", "syntax to define a tensor type in a rank profile?", "to define a tensor type in a rank profile?", "define a tensor type in a rank profile?", "a tensor type in a rank profile?", "tensor type in a rank profile?", "type in a rank profile?", "in a rank profile?", "a rank profile?", "rank profile?", "profile?"], "tokens": ["What", "is", "the", "syntax", "to", "define", "a", "tensor", "type", "in", "a", "rank", "profile", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1272832023", "fields": {"term": "How can single-valued attributes be converted to tensors?", "namespace": "open-p", "hash": -1272832023, "terms": ["How can single-valued attributes be converted to tensors?", "can single-valued attributes be converted to tensors?", "single-valued attributes be converted to tensors?", "attributes be converted to tensors?", "be converted to tensors?", "converted to tensors?", "to tensors?", "tensors?"], "tokens": ["How", "can", "single", "valued", "attributes", "be", "converted", "to", "tensors", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2022630450", "fields": {"term": "What is the tensor generate function?", "namespace": "open-p", "hash": -2022630450, "terms": ["What is the tensor generate function?", "is the tensor generate function?", "the tensor generate function?", "tensor generate function?", "generate function?", "function?"], "tokens": ["What", "is", "the", "tensor", "generate", "function", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1357549123", "fields": {"term": "What is a mapped tensor?", "namespace": "open-p", "hash": 1357549123, "terms": ["What is a mapped tensor?", "is a mapped tensor?", "a mapped tensor?", "mapped tensor?", "tensor?"], "tokens": ["What", "is", "a", "mapped", "tensor", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::518937261", "fields": {"term": "What is the document feature tensorFromLabels used for?", "namespace": "open-p", "hash": 518937261, "terms": ["What is the document feature tensorFromLabels used for?", "is the document feature tensorFromLabels used for?", "the document feature tensorFromLabels used for?", "document feature tensorFromLabels used for?", "feature tensorFromLabels used for?", "tensorFromLabels used for?", "used for?", "for?"], "tokens": ["What", "is", "the", "document", "feature", "tensorFromLabels", "used", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-363693625", "fields": {"term": "What are constant tensors?", "namespace": "open-p", "hash": -363693625, "terms": ["What are constant tensors?", "are constant tensors?", "constant tensors?", "tensors?"], "tokens": ["What", "are", "constant", "tensors", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1642706841", "fields": {"term": "Why are constant tensors used in ranking?", "namespace": "open-p", "hash": 1642706841, "terms": ["Why are constant tensors used in ranking?", "are constant tensors used in ranking?", "constant tensors used in ranking?", "tensors used in ranking?", "used in ranking?", "in ranking?", "ranking?"], "tokens": ["Why", "are", "constant", "tensors", "used", "in", "ranking", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1233119715", "fields": {"term": "How are constant tensors defined?", "namespace": "open-p", "hash": -1233119715, "terms": ["How are constant tensors defined?", "are constant tensors defined?", "constant tensors defined?", "tensors defined?", "defined?"], "tokens": ["How", "are", "constant", "tensors", "defined", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::951026352", "fields": {"term": "What is the format of constant tensor files?", "namespace": "open-p", "hash": 951026352, "terms": ["What is the format of constant tensor files?", "is the format of constant tensor files?", "the format of constant tensor files?", "format of constant tensor files?", "of constant tensor files?", "constant tensor files?", "tensor files?", "files?"], "tokens": ["What", "is", "the", "format", "of", "constant", "tensor", "files", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-584798060", "fields": {"term": "Can constant tensor files be compressed?", "namespace": "open-p", "hash": -584798060, "terms": ["Can constant tensor files be compressed?", "constant tensor files be compressed?", "tensor files be compressed?", "files be compressed?", "be compressed?", "compressed?"], "tokens": ["Can", "constant", "tensor", "files", "be", "compressed", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::510017460", "fields": {"term": "How are constant tensors used in ranking expressions?", "namespace": "open-p", "hash": 510017460, "terms": ["How are constant tensors used in ranking expressions?", "are constant tensors used in ranking expressions?", "constant tensors used in ranking expressions?", "tensors used in ranking expressions?", "used in ranking expressions?", "in ranking expressions?", "ranking expressions?", "expressions?"], "tokens": ["How", "are", "constant", "tensors", "used", "in", "ranking", "expressions", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2077913863", "fields": {"term": "What is rank-profile use_constant_tensor?", "namespace": "open-p", "hash": 2077913863, "terms": ["What is rank-profile use_constant_tensor?", "is rank-profile use_constant_tensor?", "rank-profile use_constant_tensor?", "use_constant_tensor?"], "tokens": ["What", "is", "rank", "profile", "use_constant_tensor", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-726927825", "fields": {"term": "What is the expression in rank-profile use_constant_tensor?", "namespace": "open-p", "hash": -726927825, "terms": ["What is the expression in rank-profile use_constant_tensor?", "is the expression in rank-profile use_constant_tensor?", "the expression in rank-profile use_constant_tensor?", "expression in rank-profile use_constant_tensor?", "in rank-profile use_constant_tensor?", "rank-profile use_constant_tensor?", "use_constant_tensor?"], "tokens": ["What", "is", "the", "expression", "in", "rank", "profile", "use_constant_tensor", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1609559216", "fields": {"term": "Can tensors have string values?", "namespace": "open-p", "hash": 1609559216, "terms": ["Can tensors have string values?", "tensors have string values?", "have string values?", "string values?", "values?"], "tokens": ["Can", "tensors", "have", "string", "values", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-445941972", "fields": {"term": "How can you represent sets of strings in tensors?", "namespace": "open-p", "hash": -445941972, "terms": ["How can you represent sets of strings in tensors?", "can you represent sets of strings in tensors?", "you represent sets of strings in tensors?", "represent sets of strings in tensors?", "sets of strings in tensors?", "of strings in tensors?", "strings in tensors?", "in tensors?", "tensors?"], "tokens": ["How", "can", "you", "represent", "sets", "of", "strings", "in", "tensors", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1525479060", "fields": {"term": "How do you perform set operations on strings using tensors?", "namespace": "open-p", "hash": -1525479060, "terms": ["How do you perform set operations on strings using tensors?", "do you perform set operations on strings using tensors?", "you perform set operations on strings using tensors?", "perform set operations on strings using tensors?", "set operations on strings using tensors?", "operations on strings using tensors?", "on strings using tensors?", "strings using tensors?", "using tensors?", "tensors?"], "tokens": ["How", "do", "you", "perform", "set", "operations", "on", "strings", "using", "tensors", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-754656288", "fields": {"term": "What is a cell?", "namespace": "open-p", "hash": -754656288, "terms": ["What is a cell?", "is a cell?", "a cell?", "cell?"], "tokens": ["What", "is", "a", "cell", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::872629002", "fields": {"term": "What is an address?", "namespace": "open-p", "hash": 872629002, "terms": ["What is an address?", "is an address?", "an address?", "address?"], "tokens": ["What", "is", "an", "address", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1816652249", "fields": {"term": "What are dimensions?", "namespace": "open-p", "hash": -1816652249, "terms": ["What are dimensions?", "are dimensions?", "dimensions?"], "tokens": ["What", "are", "dimensions", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1957752431", "fields": {"term": "What is the difference between mapped and indexed dimensions?", "namespace": "open-p", "hash": 1957752431, "terms": ["What is the difference between mapped and indexed dimensions?", "is the difference between mapped and indexed dimensions?", "the difference between mapped and indexed dimensions?", "difference between mapped and indexed dimensions?", "between mapped and indexed dimensions?", "mapped and indexed dimensions?", "and indexed dimensions?", "indexed dimensions?", "dimensions?"], "tokens": ["What", "is", "the", "difference", "between", "mapped", "and", "indexed", "dimensions", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1389562684", "fields": {"term": "What is the range of indexed dimensions?", "namespace": "open-p", "hash": -1389562684, "terms": ["What is the range of indexed dimensions?", "is the range of indexed dimensions?", "the range of indexed dimensions?", "range of indexed dimensions?", "of indexed dimensions?", "indexed dimensions?", "dimensions?"], "tokens": ["What", "is", "the", "range", "of", "indexed", "dimensions", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1078213180", "fields": {"term": "What are the values located in the space of dimensions?", "namespace": "open-p", "hash": 1078213180, "terms": ["What are the values located in the space of dimensions?", "are the values located in the space of dimensions?", "the values located in the space of dimensions?", "values located in the space of dimensions?", "located in the space of dimensions?", "in the space of dimensions?", "the space of dimensions?", "space of dimensions?", "of dimensions?", "dimensions?"], "tokens": ["What", "are", "the", "values", "located", "in", "the", "space", "of", "dimensions", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-302323111", "fields": {"term": "What is a label in the context of tensor dimensions?", "namespace": "open-p", "hash": -302323111, "terms": ["What is a label in the context of tensor dimensions?", "is a label in the context of tensor dimensions?", "a label in the context of tensor dimensions?", "label in the context of tensor dimensions?", "in the context of tensor dimensions?", "the context of tensor dimensions?", "context of tensor dimensions?", "of tensor dimensions?", "tensor dimensions?", "dimensions?"], "tokens": ["What", "is", "a", "label", "in", "the", "context", "of", "tensor", "dimensions", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1784471874", "fields": {"term": "What is a mapped dimension?", "namespace": "open-p", "hash": -1784471874, "terms": ["What is a mapped dimension?", "is a mapped dimension?", "a mapped dimension?", "mapped dimension?", "dimension?"], "tokens": ["What", "is", "a", "mapped", "dimension", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::-1162249383", "fields": {"term": "What is a indexed dimension?", "namespace": "open-p", "hash": -1162249383, "terms": ["What is a indexed dimension?", "is a indexed dimension?", "a indexed dimension?", "indexed dimension?", "dimension?"], "tokens": ["What", "is", "a", "indexed", "dimension", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2101356702", "fields": {"term": "What is a tensor cell?", "namespace": "open-p", "hash": -2101356702, "terms": ["What is a tensor cell?", "is a tensor cell?", "a tensor cell?", "tensor cell?", "cell?"], "tokens": ["What", "is", "a", "tensor", "cell", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-448451420", "fields": {"term": "What is the format of a tensor?", "namespace": "open-p", "hash": -448451420, "terms": ["What is the format of a tensor?", "is the format of a tensor?", "the format of a tensor?", "format of a tensor?", "of a tensor?", "a tensor?", "tensor?"], "tokens": ["What", "is", "the", "format", "of", "a", "tensor", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::913026709", "fields": {"term": "What is the cell type of a tensor?", "namespace": "open-p", "hash": 913026709, "terms": ["What is the cell type of a tensor?", "is the cell type of a tensor?", "the cell type of a tensor?", "cell type of a tensor?", "type of a tensor?", "of a tensor?", "a tensor?", "tensor?"], "tokens": ["What", "is", "the", "cell", "type", "of", "a", "tensor", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-417148431", "fields": {"term": "What are the valid tensor value types?", "namespace": "open-p", "hash": -417148431, "terms": ["What are the valid tensor value types?", "are the valid tensor value types?", "the valid tensor value types?", "valid tensor value types?", "tensor value types?", "value types?", "types?"], "tokens": ["What", "are", "the", "valid", "tensor", "value", "types", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::188113986", "fields": {"term": "What is the tensor literal form?", "namespace": "open-p", "hash": 188113986, "terms": ["What is the tensor literal form?", "is the tensor literal form?", "the tensor literal form?", "tensor literal form?", "literal form?", "form?"], "tokens": ["What", "is", "the", "tensor", "literal", "form", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2082293393", "fields": {"term": "Where is the tensor literal form used?", "namespace": "open-p", "hash": 2082293393, "terms": ["Where is the tensor literal form used?", "is the tensor literal form used?", "the tensor literal form used?", "tensor literal form used?", "literal form used?", "form used?", "used?"], "tokens": ["Where", "is", "the", "tensor", "literal", "form", "used", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1698129633", "fields": {"term": "How is the tensor literal form used?", "namespace": "open-p", "hash": 1698129633, "terms": ["How is the tensor literal form used?", "is the tensor literal form used?", "the tensor literal form used?", "tensor literal form used?", "literal form used?", "form used?", "used?"], "tokens": ["How", "is", "the", "tensor", "literal", "form", "used", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1128509962", "fields": {"term": "What is the Java Tensor API?", "namespace": "open-p", "hash": -1128509962, "terms": ["What is the Java Tensor API?", "is the Java Tensor API?", "the Java Tensor API?", "Java Tensor API?", "Tensor API?", "API?"], "tokens": ["What", "is", "the", "Java", "Tensor", "API", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1575880048", "fields": {"term": "Where can I find the Java Tensor API documentation?", "namespace": "open-p", "hash": -1575880048, "terms": ["Where can I find the Java Tensor API documentation?", "can I find the Java Tensor API documentation?", "I find the Java Tensor API documentation?", "find the Java Tensor API documentation?", "the Java Tensor API documentation?", "Java Tensor API documentation?", "Tensor API documentation?", "API documentation?", "documentation?"], "tokens": ["Where", "can", "I", "find", "the", "Java", "Tensor", "API", "documentation", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::938445099", "fields": {"term": "What is the general literal form?", "namespace": "open-p", "hash": 938445099, "terms": ["What is the general literal form?", "is the general literal form?", "the general literal form?", "general literal form?", "literal form?", "form?"], "tokens": ["What", "is", "the", "general", "literal", "form", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2041676790", "fields": {"term": "What is EBNF?", "namespace": "open-p", "hash": -2041676790, "terms": ["What is EBNF?", "is EBNF?", "EBNF?"], "tokens": ["What", "is", "EBNF", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-885609092", "fields": {"term": "What is a cell in tensor?", "namespace": "open-p", "hash": -885609092, "terms": ["What is a cell in tensor?", "is a cell in tensor?", "a cell in tensor?", "cell in tensor?", "in tensor?", "tensor?"], "tokens": ["What", "is", "a", "cell", "in", "tensor", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::532734652", "fields": {"term": "What is an address in tensor?", "namespace": "open-p", "hash": 532734652, "terms": ["What is an address in tensor?", "is an address in tensor?", "an address in tensor?", "address in tensor?", "in tensor?", "tensor?"], "tokens": ["What", "is", "an", "address", "in", "tensor", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1320028804", "fields": {"term": "What is a dimension in tensor?", "namespace": "open-p", "hash": 1320028804, "terms": ["What is a dimension in tensor?", "is a dimension in tensor?", "a dimension in tensor?", "dimension in tensor?", "in tensor?", "tensor?"], "tokens": ["What", "is", "a", "dimension", "in", "tensor", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1487875947", "fields": {"term": "What is a label in tensor?", "namespace": "open-p", "hash": -1487875947, "terms": ["What is a label in tensor?", "is a label in tensor?", "a label in tensor?", "label in tensor?", "in tensor?", "tensor?"], "tokens": ["What", "is", "a", "label", "in", "tensor", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-359649179", "fields": {"term": "What is an identifier in tensor?", "namespace": "open-p", "hash": -359649179, "terms": ["What is an identifier in tensor?", "is an identifier in tensor?", "an identifier in tensor?", "identifier in tensor?", "in tensor?", "tensor?"], "tokens": ["What", "is", "an", "identifier", "in", "tensor", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-226214857", "fields": {"term": "How should indexed tensors be written?", "namespace": "open-p", "hash": -226214857, "terms": ["How should indexed tensors be written?", "should indexed tensors be written?", "indexed tensors be written?", "tensors be written?", "be written?", "written?"], "tokens": ["How", "should", "indexed", "tensors", "be", "written", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1103148853", "fields": {"term": "What are the requirements for explicit tensor types?", "namespace": "open-p", "hash": 1103148853, "terms": ["What are the requirements for explicit tensor types?", "are the requirements for explicit tensor types?", "the requirements for explicit tensor types?", "requirements for explicit tensor types?", "for explicit tensor types?", "explicit tensor types?", "tensor types?", "types?"], "tokens": ["What", "are", "the", "requirements", "for", "explicit", "tensor", "types", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1369196564", "fields": {"term": "What is the input format for binary data?", "namespace": "open-p", "hash": 1369196564, "terms": ["What is the input format for binary data?", "is the input format for binary data?", "the input format for binary data?", "input format for binary data?", "format for binary data?", "for binary data?", "binary data?", "data?"], "tokens": ["What", "is", "the", "input", "format", "for", "binary", "data", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-972242458", "fields": {"term": "What is the cell value type for binary tensors?", "namespace": "open-p", "hash": -972242458, "terms": ["What is the cell value type for binary tensors?", "is the cell value type for binary tensors?", "the cell value type for binary tensors?", "cell value type for binary tensors?", "value type for binary tensors?", "type for binary tensors?", "for binary tensors?", "binary tensors?", "tensors?"], "tokens": ["What", "is", "the", "cell", "value", "type", "for", "binary", "tensors", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-69141692", "fields": {"term": "Can fixed size binary data be input using a hexadecimal dump?", "namespace": "open-p", "hash": -69141692, "terms": ["Can fixed size binary data be input using a hexadecimal dump?", "fixed size binary data be input using a hexadecimal dump?", "size binary data be input using a hexadecimal dump?", "binary data be input using a hexadecimal dump?", "data be input using a hexadecimal dump?", "be input using a hexadecimal dump?", "input using a hexadecimal dump?", "using a hexadecimal dump?", "a hexadecimal dump?", "hexadecimal dump?", "dump?"], "tokens": ["Can", "fixed", "size", "binary", "data", "be", "input", "using", "a", "hexadecimal", "dump", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1612540019", "fields": {"term": "What is required for a single mapped dimension?", "namespace": "open-p", "hash": 1612540019, "terms": ["What is required for a single mapped dimension?", "is required for a single mapped dimension?", "required for a single mapped dimension?", "for a single mapped dimension?", "a single mapped dimension?", "single mapped dimension?", "mapped dimension?", "dimension?"], "tokens": ["What", "is", "required", "for", "a", "single", "mapped", "dimension", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1034107159", "fields": {"term": "What is the advantage of a single mapped dimension?", "namespace": "open-p", "hash": 1034107159, "terms": ["What is the advantage of a single mapped dimension?", "is the advantage of a single mapped dimension?", "the advantage of a single mapped dimension?", "advantage of a single mapped dimension?", "of a single mapped dimension?", "a single mapped dimension?", "single mapped dimension?", "mapped dimension?", "dimension?"], "tokens": ["What", "is", "the", "advantage", "of", "a", "single", "mapped", "dimension", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1236235417", "fields": {"term": "What is the map short form?", "namespace": "open-p", "hash": 1236235417, "terms": ["What is the map short form?", "is the map short form?", "the map short form?", "map short form?", "short form?", "form?"], "tokens": ["What", "is", "the", "map", "short", "form", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2133457488", "fields": {"term": "What is the indexed short form?", "namespace": "open-p", "hash": 2133457488, "terms": ["What is the indexed short form?", "is the indexed short form?", "the indexed short form?", "indexed short form?", "short form?", "form?"], "tokens": ["What", "is", "the", "indexed", "short", "form", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::57681516", "fields": {"term": "What does the dense subspace contain?", "namespace": "open-p", "hash": 57681516, "terms": ["What does the dense subspace contain?", "does the dense subspace contain?", "the dense subspace contain?", "dense subspace contain?", "subspace contain?", "contain?"], "tokens": ["What", "does", "the", "dense", "subspace", "contain", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1280582865", "fields": {"term": "Where are tensor functions listed?", "namespace": "open-p", "hash": -1280582865, "terms": ["Where are tensor functions listed?", "are tensor functions listed?", "tensor functions listed?", "functions listed?", "listed?"], "tokens": ["Where", "are", "tensor", "functions", "listed", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1290324929", "fields": {"term": "What is a tensor constant?", "namespace": "open-p", "hash": -1290324929, "terms": ["What is a tensor constant?", "is a tensor constant?", "a tensor constant?", "tensor constant?", "constant?"], "tokens": ["What", "is", "a", "tensor", "constant", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1393127126", "fields": {"term": "What is the Tensor field in schema?", "namespace": "open-p", "hash": -1393127126, "terms": ["What is the Tensor field in schema?", "is the Tensor field in schema?", "the Tensor field in schema?", "Tensor field in schema?", "field in schema?", "in schema?", "schema?"], "tokens": ["What", "is", "the", "Tensor", "field", "in", "schema", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::400187087", "fields": {"term": "What is the ONNX format?", "namespace": "open-p", "hash": 400187087, "terms": ["What is the ONNX format?", "is the ONNX format?", "the ONNX format?", "ONNX format?", "format?"], "tokens": ["What", "is", "the", "ONNX", "format", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1102314918", "fields": {"term": "What is TensorFlow?", "namespace": "open-p", "hash": 1102314918, "terms": ["What is TensorFlow?", "is TensorFlow?", "TensorFlow?"], "tokens": ["What", "is", "TensorFlow", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1315047915", "fields": {"term": "What is tf2onnx library?", "namespace": "open-p", "hash": 1315047915, "terms": ["What is tf2onnx library?", "is tf2onnx library?", "tf2onnx library?", "library?"], "tokens": ["What", "is", "tf2onnx", "library", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-922488303", "fields": {"term": "Can you inspect expected input/output format?", "namespace": "open-p", "hash": -922488303, "terms": ["Can you inspect expected input/output format?", "you inspect expected input/output format?", "inspect expected input/output format?", "expected input/output format?", "input/output format?", "format?"], "tokens": ["Can", "you", "inspect", "expected", "input", "/", "output", "format", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::32596275", "fields": {"term": "What is Vespa Testing reference?", "namespace": "open-p", "hash": 32596275, "terms": ["What is Vespa Testing reference?", "is Vespa Testing reference?", "Vespa Testing reference?", "Testing reference?", "reference?"], "tokens": ["What", "is", "Vespa", "Testing", "reference", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1769260119", "fields": {"term": "What do Vespa system tests verify?", "namespace": "open-p", "hash": 1769260119, "terms": ["What do Vespa system tests verify?", "do Vespa system tests verify?", "Vespa system tests verify?", "system tests verify?", "tests verify?", "verify?"], "tokens": ["What", "do", "Vespa", "system", "tests", "verify", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::837894787", "fields": {"term": "What HTTP interfaces are used?", "namespace": "open-p", "hash": 837894787, "terms": ["What HTTP interfaces are used?", "HTTP interfaces are used?", "interfaces are used?", "are used?", "used?"], "tokens": ["What", "HTTP", "interfaces", "are", "used", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-421262179", "fields": {"term": "What is the basic HTTP test reference?", "namespace": "open-p", "hash": -421262179, "terms": ["What is the basic HTTP test reference?", "is the basic HTTP test reference?", "the basic HTTP test reference?", "basic HTTP test reference?", "HTTP test reference?", "test reference?", "reference?"], "tokens": ["What", "is", "the", "basic", "HTTP", "test", "reference", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::851627653", "fields": {"term": "How to run Vespa tests?", "namespace": "open-p", "hash": 851627653, "terms": ["How to run Vespa tests?", "to run Vespa tests?", "run Vespa tests?", "Vespa tests?", "tests?"], "tokens": ["How", "to", "run", "Vespa", "tests", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1319371497", "fields": {"term": "What is the testing documentation?", "namespace": "open-p", "hash": 1319371497, "terms": ["What is the testing documentation?", "is the testing documentation?", "the testing documentation?", "testing documentation?", "documentation?"], "tokens": ["What", "is", "the", "testing", "documentation", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2067172467", "fields": {"term": "What are the test scenarios and categories?", "namespace": "open-p", "hash": 2067172467, "terms": ["What are the test scenarios and categories?", "are the test scenarios and categories?", "the test scenarios and categories?", "test scenarios and categories?", "scenarios and categories?", "and categories?", "categories?"], "tokens": ["What", "are", "the", "test", "scenarios", "and", "categories", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1224125521", "fields": {"term": "What is the system test framework?", "namespace": "open-p", "hash": 1224125521, "terms": ["What is the system test framework?", "is the system test framework?", "the system test framework?", "system test framework?", "test framework?", "framework?"], "tokens": ["What", "is", "the", "system", "test", "framework", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-957546199", "fields": {"term": "What are the JUnit test tags?", "namespace": "open-p", "hash": -957546199, "terms": ["What are the JUnit test tags?", "are the JUnit test tags?", "the JUnit test tags?", "JUnit test tags?", "test tags?", "tags?"], "tokens": ["What", "are", "the", "JUnit", "test", "tags", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1063411041", "fields": {"term": "How do you run tests with Maven?", "namespace": "open-p", "hash": -1063411041, "terms": ["How do you run tests with Maven?", "do you run tests with Maven?", "you run tests with Maven?", "run tests with Maven?", "tests with Maven?", "with Maven?", "Maven?"], "tokens": ["How", "do", "you", "run", "tests", "with", "Maven", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1859994793", "fields": {"term": "What is the sample application test suite?", "namespace": "open-p", "hash": 1859994793, "terms": ["What is the sample application test suite?", "is the sample application test suite?", "the sample application test suite?", "sample application test suite?", "application test suite?", "test suite?", "suite?"], "tokens": ["What", "is", "the", "sample", "application", "test", "suite", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-217351300", "fields": {"term": "What is a system tests suite?", "namespace": "open-p", "hash": -217351300, "terms": ["What is a system tests suite?", "is a system tests suite?", "a system tests suite?", "system tests suite?", "tests suite?", "suite?"], "tokens": ["What", "is", "a", "system", "tests", "suite", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1049936037", "fields": {"term": "What are the two formalizations?", "namespace": "open-p", "hash": -1049936037, "terms": ["What are the two formalizations?", "are the two formalizations?", "the two formalizations?", "two formalizations?", "formalizations?"], "tokens": ["What", "are", "the", "two", "formalizations", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-924756840", "fields": {"term": "What is a staging test?", "namespace": "open-p", "hash": -924756840, "terms": ["What is a staging test?", "is a staging test?", "a staging test?", "staging test?", "test?"], "tokens": ["What", "is", "a", "staging", "test", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1479972921", "fields": {"term": "What is a production verification test?", "namespace": "open-p", "hash": -1479972921, "terms": ["What is a production verification test?", "is a production verification test?", "a production verification test?", "production verification test?", "verification test?", "test?"], "tokens": ["What", "is", "a", "production", "verification", "test", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::388096015", "fields": {"term": "What is the purpose of HTTP requests in testing?", "namespace": "open-p", "hash": 388096015, "terms": ["What is the purpose of HTTP requests in testing?", "is the purpose of HTTP requests in testing?", "the purpose of HTTP requests in testing?", "purpose of HTTP requests in testing?", "of HTTP requests in testing?", "HTTP requests in testing?", "requests in testing?", "in testing?", "testing?"], "tokens": ["What", "is", "the", "purpose", "of", "HTTP", "requests", "in", "testing", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2071178088", "fields": {"term": "What is the purpose of staging tests?", "namespace": "open-p", "hash": 2071178088, "terms": ["What is the purpose of staging tests?", "is the purpose of staging tests?", "the purpose of staging tests?", "purpose of staging tests?", "of staging tests?", "staging tests?", "tests?"], "tokens": ["What", "is", "the", "purpose", "of", "staging", "tests", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1932914403", "fields": {"term": "What is the purpose of production verification tests?", "namespace": "open-p", "hash": -1932914403, "terms": ["What is the purpose of production verification tests?", "is the purpose of production verification tests?", "the purpose of production verification tests?", "purpose of production verification tests?", "of production verification tests?", "production verification tests?", "verification tests?", "tests?"], "tokens": ["What", "is", "the", "purpose", "of", "production", "verification", "tests", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1974861037", "fields": {"term": "What is A/B-testing?", "namespace": "open-p", "hash": -1974861037, "terms": ["What is A/B-testing?", "is A/B-testing?", "A/B-testing?"], "tokens": ["What", "is", "A", "/", "B", "testing", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1969192758", "fields": {"term": "What are system tests?", "namespace": "open-p", "hash": -1969192758, "terms": ["What are system tests?", "are system tests?", "system tests?", "tests?"], "tokens": ["What", "are", "system", "tests", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::-397884183", "fields": {"term": "How to run system tests?", "namespace": "open-p", "hash": -397884183, "terms": ["How to run system tests?", "to run system tests?", "run system tests?", "system tests?", "tests?"], "tokens": ["How", "to", "run", "system", "tests", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1185731894", "fields": {"term": "When should system tests be updated?", "namespace": "open-p", "hash": -1185731894, "terms": ["When should system tests be updated?", "should system tests be updated?", "system tests be updated?", "tests be updated?", "be updated?", "updated?"], "tokens": ["When", "should", "system", "tests", "be", "updated", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-414722818", "fields": {"term": "Why run system tests?", "namespace": "open-p", "hash": -414722818, "terms": ["Why run system tests?", "run system tests?", "system tests?", "tests?"], "tokens": ["Why", "run", "system", "tests", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::833708282", "fields": {"term": "What do system tests verify?", "namespace": "open-p", "hash": 833708282, "terms": ["What do system tests verify?", "do system tests verify?", "system tests verify?", "tests verify?", "verify?"], "tokens": ["What", "do", "system", "tests", "verify", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::711801239", "fields": {"term": "What does a system test suite include?", "namespace": "open-p", "hash": 711801239, "terms": ["What does a system test suite include?", "does a system test suite include?", "a system test suite include?", "system test suite include?", "test suite include?", "suite include?", "include?"], "tokens": ["What", "does", "a", "system", "test", "suite", "include", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::776080306", "fields": {"term": "How to test a deployed Vespa application?", "namespace": "open-p", "hash": 776080306, "terms": ["How to test a deployed Vespa application?", "to test a deployed Vespa application?", "test a deployed Vespa application?", "a deployed Vespa application?", "deployed Vespa application?", "Vespa application?", "application?"], "tokens": ["How", "to", "test", "a", "deployed", "Vespa", "application", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::786612121", "fields": {"term": "What to verify in a system test?", "namespace": "open-p", "hash": 786612121, "terms": ["What to verify in a system test?", "to verify in a system test?", "verify in a system test?", "in a system test?", "a system test?", "system test?", "test?"], "tokens": ["What", "to", "verify", "in", "a", "system", "test", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1299641946", "fields": {"term": "What tests can Vespa CLI run?", "namespace": "open-p", "hash": 1299641946, "terms": ["What tests can Vespa CLI run?", "tests can Vespa CLI run?", "can Vespa CLI run?", "Vespa CLI run?", "CLI run?", "run?"], "tokens": ["What", "tests", "can", "Vespa", "CLI", "run", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::821402376", "fields": {"term": "How to set up test deployment?", "namespace": "open-p", "hash": 821402376, "terms": ["How to set up test deployment?", "to set up test deployment?", "set up test deployment?", "up test deployment?", "test deployment?", "deployment?"], "tokens": ["How", "to", "set", "up", "test", "deployment", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1986242665", "fields": {"term": "What is test-config.json file?", "namespace": "open-p", "hash": 1986242665, "terms": ["What is test-config.json file?", "is test-config.json file?", "test-config.json file?", "file?"], "tokens": ["What", "is", "test", "config", "json", "file", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1683613893", "fields": {"term": "What is the goal of staging tests?", "namespace": "open-p", "hash": -1683613893, "terms": ["What is the goal of staging tests?", "is the goal of staging tests?", "the goal of staging tests?", "goal of staging tests?", "of staging tests?", "staging tests?", "tests?"], "tokens": ["What", "is", "the", "goal", "of", "staging", "tests", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-914987179", "fields": {"term": "What should staging tests ensure?", "namespace": "open-p", "hash": -914987179, "terms": ["What should staging tests ensure?", "should staging tests ensure?", "staging tests ensure?", "tests ensure?", "ensure?"], "tokens": ["What", "should", "staging", "tests", "ensure", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1093279225", "fields": {"term": "How are staging tests different from system tests?", "namespace": "open-p", "hash": -1093279225, "terms": ["How are staging tests different from system tests?", "are staging tests different from system tests?", "staging tests different from system tests?", "tests different from system tests?", "different from system tests?", "from system tests?", "system tests?", "tests?"], "tokens": ["How", "are", "staging", "tests", "different", "from", "system", "tests", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1566571548", "fields": {"term": "What is controlled during staging tests?", "namespace": "open-p", "hash": 1566571548, "terms": ["What is controlled during staging tests?", "is controlled during staging tests?", "controlled during staging tests?", "during staging tests?", "staging tests?", "tests?"], "tokens": ["What", "is", "controlled", "during", "staging", "tests", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-26770496", "fields": {"term": "What dictates the workflow when deploying a breaking change?", "namespace": "open-p", "hash": -26770496, "terms": ["What dictates the workflow when deploying a breaking change?", "dictates the workflow when deploying a breaking change?", "the workflow when deploying a breaking change?", "workflow when deploying a breaking change?", "when deploying a breaking change?", "deploying a breaking change?", "a breaking change?", "breaking change?", "change?"], "tokens": ["What", "dictates", "the", "workflow", "when", "deploying", "a", "breaking", "change", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1949976119", "fields": {"term": "What are the steps for deploying a breaking change?", "namespace": "open-p", "hash": -1949976119, "terms": ["What are the steps for deploying a breaking change?", "are the steps for deploying a breaking change?", "the steps for deploying a breaking change?", "steps for deploying a breaking change?", "for deploying a breaking change?", "deploying a breaking change?", "a breaking change?", "breaking change?", "change?"], "tokens": ["What", "are", "the", "steps", "for", "deploying", "a", "breaking", "change", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1019028640", "fields": {"term": "What is meant by staging setup?", "namespace": "open-p", "hash": 1019028640, "terms": ["What is meant by staging setup?", "is meant by staging setup?", "meant by staging setup?", "by staging setup?", "staging setup?", "setup?"], "tokens": ["What", "is", "meant", "by", "staging", "setup", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1457565218", "fields": {"term": "What is the purpose of staging test?", "namespace": "open-p", "hash": -1457565218, "terms": ["What is the purpose of staging test?", "is the purpose of staging test?", "the purpose of staging test?", "purpose of staging test?", "of staging test?", "staging test?", "test?"], "tokens": ["What", "is", "the", "purpose", "of", "staging", "test", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1883365845", "fields": {"term": "What is the path for the 'tests/staging-setup' test?", "namespace": "open-p", "hash": 1883365845, "terms": ["What is the path for the 'tests/staging-setup' test?", "is the path for the 'tests/staging-setup' test?", "the path for the 'tests/staging-setup' test?", "path for the 'tests/staging-setup' test?", "for the 'tests/staging-setup' test?", "the 'tests/staging-setup' test?", "'tests/staging-setup' test?", "test?"], "tokens": ["What", "is", "the", "path", "for", "the", "'", "tests", "/", "staging", "setup", "'", "test", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-40345654", "fields": {"term": "How long does vespa deploy wait?", "namespace": "open-p", "hash": -40345654, "terms": ["How long does vespa deploy wait?", "long does vespa deploy wait?", "does vespa deploy wait?", "vespa deploy wait?", "deploy wait?", "wait?"], "tokens": ["How", "long", "does", "vespa", "deploy", "wait", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-204742272", "fields": {"term": "What is tested in staging-test?", "namespace": "open-p", "hash": -204742272, "terms": ["What is tested in staging-test?", "is tested in staging-test?", "tested in staging-test?", "in staging-test?", "staging-test?"], "tokens": ["What", "is", "tested", "in", "staging", "test", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1690498988", "fields": {"term": "How long does 'vespa deploy' wait?", "namespace": "open-p", "hash": 1690498988, "terms": ["How long does 'vespa deploy' wait?", "long does 'vespa deploy' wait?", "does 'vespa deploy' wait?", "'vespa deploy' wait?", "deploy' wait?", "wait?"], "tokens": ["How", "long", "does", "'", "vespa", "deploy", "'", "wait", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1133105294", "fields": {"term": "What is 'mvn test'?", "namespace": "open-p", "hash": -1133105294, "terms": ["What is 'mvn test'?", "is 'mvn test'?", "'mvn test'?", "test'?"], "tokens": ["What", "is", "'", "mvn", "test", "'?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-389565853", "fields": {"term": "What is the mvn command to test staging?", "namespace": "open-p", "hash": -389565853, "terms": ["What is the mvn command to test staging?", "is the mvn command to test staging?", "the mvn command to test staging?", "mvn command to test staging?", "command to test staging?", "to test staging?", "test staging?", "staging?"], "tokens": ["What", "is", "the", "mvn", "command", "to", "test", "staging", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1963767318", "fields": {"term": "What is the path to test-config.json?", "namespace": "open-p", "hash": 1963767318, "terms": ["What is the path to test-config.json?", "is the path to test-config.json?", "the path to test-config.json?", "path to test-config.json?", "to test-config.json?", "test-config.json?"], "tokens": ["What", "is", "the", "path", "to", "test", "config", "json", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::39079655", "fields": {"term": "What is continuous deployment?", "namespace": "open-p", "hash": 39079655, "terms": ["What is continuous deployment?", "is continuous deployment?", "continuous deployment?", "deployment?"], "tokens": ["What", "is", "continuous", "deployment", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2062915812", "fields": {"term": "What are feature switches?", "namespace": "open-p", "hash": 2062915812, "terms": ["What are feature switches?", "are feature switches?", "feature switches?", "switches?"], "tokens": ["What", "are", "feature", "switches", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::238153794", "fields": {"term": "What is bucket testing?", "namespace": "open-p", "hash": 238153794, "terms": ["What is bucket testing?", "is bucket testing?", "bucket testing?", "testing?"], "tokens": ["What", "is", "bucket", "testing", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::328286236", "fields": {"term": "What is an incompatible change?", "namespace": "open-p", "hash": 328286236, "terms": ["What is an incompatible change?", "is an incompatible change?", "an incompatible change?", "incompatible change?", "change?"], "tokens": ["What", "is", "an", "incompatible", "change", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1114797078", "fields": {"term": "Why use precise semantics?", "namespace": "open-p", "hash": 1114797078, "terms": ["Why use precise semantics?", "use precise semantics?", "precise semantics?", "semantics?"], "tokens": ["Why", "use", "precise", "semantics", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-424630308", "fields": {"term": "What are basic HTTP tests?", "namespace": "open-p", "hash": -424630308, "terms": ["What are basic HTTP tests?", "are basic HTTP tests?", "basic HTTP tests?", "HTTP tests?", "tests?"], "tokens": ["What", "are", "basic", "HTTP", "tests", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1615217248", "fields": {"term": "How are basic HTTP tests written?", "namespace": "open-p", "hash": -1615217248, "terms": ["How are basic HTTP tests written?", "are basic HTTP tests written?", "basic HTTP tests written?", "HTTP tests written?", "tests written?", "written?"], "tokens": ["How", "are", "basic", "HTTP", "tests", "written", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1105822504", "fields": {"term": "What is the Java testing reference?", "namespace": "open-p", "hash": 1105822504, "terms": ["What is the Java testing reference?", "is the Java testing reference?", "the Java testing reference?", "Java testing reference?", "testing reference?", "reference?"], "tokens": ["What", "is", "the", "Java", "testing", "reference", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-190266960", "fields": {"term": "What do Vespa application system tests verify?", "namespace": "open-p", "hash": -190266960, "terms": ["What do Vespa application system tests verify?", "do Vespa application system tests verify?", "Vespa application system tests verify?", "application system tests verify?", "system tests verify?", "tests verify?", "verify?"], "tokens": ["What", "do", "Vespa", "application", "system", "tests", "verify", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1060842005", "fields": {"term": "What are the HTTP interfaces used for?", "namespace": "open-p", "hash": 1060842005, "terms": ["What are the HTTP interfaces used for?", "are the HTTP interfaces used for?", "the HTTP interfaces used for?", "HTTP interfaces used for?", "interfaces used for?", "used for?", "for?"], "tokens": ["What", "are", "the", "HTTP", "interfaces", "used", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1945178672", "fields": {"term": "What is testing documentation?", "namespace": "open-p", "hash": 1945178672, "terms": ["What is testing documentation?", "is testing documentation?", "testing documentation?", "documentation?"], "tokens": ["What", "is", "testing", "documentation", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2051820186", "fields": {"term": "How many test scenarios are defined?", "namespace": "open-p", "hash": -2051820186, "terms": ["How many test scenarios are defined?", "many test scenarios are defined?", "test scenarios are defined?", "scenarios are defined?", "are defined?", "defined?"], "tokens": ["How", "many", "test", "scenarios", "are", "defined", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1947637164", "fields": {"term": "What are the four test code categories?", "namespace": "open-p", "hash": 1947637164, "terms": ["What are the four test code categories?", "are the four test code categories?", "the four test code categories?", "four test code categories?", "test code categories?", "code categories?", "categories?"], "tokens": ["What", "are", "the", "four", "test", "code", "categories", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1854153010", "fields": {"term": "What is the file format for each test?", "namespace": "open-p", "hash": 1854153010, "terms": ["What is the file format for each test?", "is the file format for each test?", "the file format for each test?", "file format for each test?", "format for each test?", "for each test?", "each test?", "test?"], "tokens": ["What", "is", "the", "file", "format", "for", "each", "test", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::661024566", "fields": {"term": "How are categories defined for HTTP tests?", "namespace": "open-p", "hash": 661024566, "terms": ["How are categories defined for HTTP tests?", "are categories defined for HTTP tests?", "categories defined for HTTP tests?", "defined for HTTP tests?", "for HTTP tests?", "HTTP tests?", "tests?"], "tokens": ["How", "are", "categories", "defined", "for", "HTTP", "tests", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1475517176", "fields": {"term": "What is the path to the verify-search-still-works.json file?", "namespace": "open-p", "hash": 1475517176, "terms": ["What is the path to the verify-search-still-works.json file?", "is the path to the verify-search-still-works.json file?", "the path to the verify-search-still-works.json file?", "path to the verify-search-still-works.json file?", "to the verify-search-still-works.json file?", "the verify-search-still-works.json file?", "verify-search-still-works.json file?", "file?"], "tokens": ["What", "is", "the", "path", "to", "the", "verify", "search", "still", "works", "json", "file", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1626307287", "fields": {"term": "What is the path to the metrics-test.json file?", "namespace": "open-p", "hash": -1626307287, "terms": ["What is the path to the metrics-test.json file?", "is the path to the metrics-test.json file?", "the path to the metrics-test.json file?", "path to the metrics-test.json file?", "to the metrics-test.json file?", "the metrics-test.json file?", "metrics-test.json file?", "file?"], "tokens": ["What", "is", "the", "path", "to", "the", "metrics", "test", "json", "file", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1543874277", "fields": {"term": "Where can I find an example test suite?", "namespace": "open-p", "hash": 1543874277, "terms": ["Where can I find an example test suite?", "can I find an example test suite?", "I find an example test suite?", "find an example test suite?", "an example test suite?", "example test suite?", "test suite?", "suite?"], "tokens": ["Where", "can", "I", "find", "an", "example", "test", "suite", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::100828111", "fields": {"term": "What does each step of a test contain?", "namespace": "open-p", "hash": 100828111, "terms": ["What does each step of a test contain?", "does each step of a test contain?", "each step of a test contain?", "step of a test contain?", "of a test contain?", "a test contain?", "test contain?", "contain?"], "tokens": ["What", "does", "each", "step", "of", "a", "test", "contain", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-574239994", "fields": {"term": "What is the format of an HTTP request specification in a test?", "namespace": "open-p", "hash": -574239994, "terms": ["What is the format of an HTTP request specification in a test?", "is the format of an HTTP request specification in a test?", "the format of an HTTP request specification in a test?", "format of an HTTP request specification in a test?", "of an HTTP request specification in a test?", "an HTTP request specification in a test?", "HTTP request specification in a test?", "request specification in a test?", "specification in a test?", "in a test?", "a test?", "test?"], "tokens": ["What", "is", "the", "format", "of", "an", "HTTP", "request", "specification", "in", "a", "test", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1534354339", "fields": {"term": "What is the format of an HTTP response specification in a test?", "namespace": "open-p", "hash": -1534354339, "terms": ["What is the format of an HTTP response specification in a test?", "is the format of an HTTP response specification in a test?", "the format of an HTTP response specification in a test?", "format of an HTTP response specification in a test?", "of an HTTP response specification in a test?", "an HTTP response specification in a test?", "HTTP response specification in a test?", "response specification in a test?", "specification in a test?", "in a test?", "a test?", "test?"], "tokens": ["What", "is", "the", "format", "of", "an", "HTTP", "response", "specification", "in", "a", "test", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-91016425", "fields": {"term": "What is the 'uri' field?", "namespace": "open-p", "hash": -91016425, "terms": ["What is the 'uri' field?", "is the 'uri' field?", "the 'uri' field?", "'uri' field?", "field?"], "tokens": ["What", "is", "the", "'", "uri", "'", "field", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1725845607", "fields": {"term": "What is a 'step' field?", "namespace": "open-p", "hash": -1725845607, "terms": ["What is a 'step' field?", "is a 'step' field?", "a 'step' field?", "'step' field?", "field?"], "tokens": ["What", "is", "a", "'", "step", "'", "field", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::780209636", "fields": {"term": "What data format should requests be?", "namespace": "open-p", "hash": 780209636, "terms": ["What data format should requests be?", "data format should requests be?", "format should requests be?", "should requests be?", "requests be?", "be?"], "tokens": ["What", "data", "format", "should", "requests", "be", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1987277583", "fields": {"term": "What is JSON verification?", "namespace": "open-p", "hash": -1987277583, "terms": ["What is JSON verification?", "is JSON verification?", "JSON verification?", "verification?"], "tokens": ["What", "is", "JSON", "verification", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-781157449", "fields": {"term": "What is a JSON template?", "namespace": "open-p", "hash": -781157449, "terms": ["What is a JSON template?", "is a JSON template?", "a JSON template?", "JSON template?", "template?"], "tokens": ["What", "is", "a", "JSON", "template", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1466520734", "fields": {"term": "What is the precision for number matching?", "namespace": "open-p", "hash": -1466520734, "terms": ["What is the precision for number matching?", "is the precision for number matching?", "the precision for number matching?", "precision for number matching?", "for number matching?", "number matching?", "matching?"], "tokens": ["What", "is", "the", "precision", "for", "number", "matching", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1822003173", "fields": {"term": "What is the rule for objects containing unlisted fields?", "namespace": "open-p", "hash": -1822003173, "terms": ["What is the rule for objects containing unlisted fields?", "is the rule for objects containing unlisted fields?", "the rule for objects containing unlisted fields?", "rule for objects containing unlisted fields?", "for objects containing unlisted fields?", "objects containing unlisted fields?", "containing unlisted fields?", "unlisted fields?", "fields?"], "tokens": ["What", "is", "the", "rule", "for", "objects", "containing", "unlisted", "fields", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1130984257", "fields": {"term": "What is the rule for matching arrays?", "namespace": "open-p", "hash": -1130984257, "terms": ["What is the rule for matching arrays?", "is the rule for matching arrays?", "the rule for matching arrays?", "rule for matching arrays?", "for matching arrays?", "matching arrays?", "arrays?"], "tokens": ["What", "is", "the", "rule", "for", "matching", "arrays", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-518026641", "fields": {"term": "What is the empty object used for?", "namespace": "open-p", "hash": -518026641, "terms": ["What is the empty object used for?", "is the empty object used for?", "the empty object used for?", "empty object used for?", "object used for?", "used for?", "for?"], "tokens": ["What", "is", "the", "empty", "object", "used", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-406305901", "fields": {"term": "What are the different match modes?", "namespace": "open-p", "hash": -406305901, "terms": ["What are the different match modes?", "are the different match modes?", "the different match modes?", "different match modes?", "match modes?", "modes?"], "tokens": ["What", "are", "the", "different", "match", "modes", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1641033403", "fields": {"term": "What is Semantic Retrieval for QA?", "namespace": "open-p", "hash": -1641033403, "terms": ["What is Semantic Retrieval for QA?", "is Semantic Retrieval for QA?", "Semantic Retrieval for QA?", "Retrieval for QA?", "for QA?", "QA?"], "tokens": ["What", "is", "Semantic", "Retrieval", "for", "QA", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::418037739", "fields": {"term": "What is the Linguistics guide?", "namespace": "open-p", "hash": 418037739, "terms": ["What is the Linguistics guide?", "is the Linguistics guide?", "the Linguistics guide?", "Linguistics guide?", "guide?"], "tokens": ["What", "is", "the", "Linguistics", "guide", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::829289881", "fields": {"term": "What are linguistic transformations?", "namespace": "open-p", "hash": 829289881, "terms": ["What are linguistic transformations?", "are linguistic transformations?", "linguistic transformations?", "transformations?"], "tokens": ["What", "are", "linguistic", "transformations", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2076025425", "fields": {"term": "How does trace.level help testing?", "namespace": "open-p", "hash": -2076025425, "terms": ["How does trace.level help testing?", "does trace.level help testing?", "trace.level help testing?", "help testing?", "testing?"], "tokens": ["How", "does", "trace", "level", "help", "testing", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-793763297", "fields": {"term": "What is query tracing for?", "namespace": "open-p", "hash": -793763297, "terms": ["What is query tracing for?", "is query tracing for?", "query tracing for?", "tracing for?", "for?"], "tokens": ["What", "is", "query", "tracing", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1723756469", "fields": {"term": "How can query parsing change?", "namespace": "open-p", "hash": -1723756469, "terms": ["How can query parsing change?", "can query parsing change?", "query parsing change?", "parsing change?", "change?"], "tokens": ["How", "can", "query", "parsing", "change", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::567205262", "fields": {"term": "What is prefix search?", "namespace": "open-p", "hash": 567205262, "terms": ["What is prefix search?", "is prefix search?", "prefix search?", "search?"], "tokens": ["What", "is", "prefix", "search", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-977470544", "fields": {"term": "What fields can use prefix search?", "namespace": "open-p", "hash": -977470544, "terms": ["What fields can use prefix search?", "fields can use prefix search?", "can use prefix search?", "use prefix search?", "prefix search?", "search?"], "tokens": ["What", "fields", "can", "use", "prefix", "search", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2091492431", "fields": {"term": "How can prefix search be enabled?", "namespace": "open-p", "hash": 2091492431, "terms": ["How can prefix search be enabled?", "can prefix search be enabled?", "prefix search be enabled?", "search be enabled?", "be enabled?", "enabled?"], "tokens": ["How", "can", "prefix", "search", "be", "enabled", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-801187021", "fields": {"term": "What is the search-suggestions sample application?", "namespace": "open-p", "hash": -801187021, "terms": ["What is the search-suggestions sample application?", "is the search-suggestions sample application?", "the search-suggestions sample application?", "search-suggestions sample application?", "sample application?", "application?"], "tokens": ["What", "is", "the", "search", "suggestions", "sample", "application", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-464183589", "fields": {"term": "What design discussions are present in the search-suggestions README?", "namespace": "open-p", "hash": -464183589, "terms": ["What design discussions are present in the search-suggestions README?", "design discussions are present in the search-suggestions README?", "discussions are present in the search-suggestions README?", "are present in the search-suggestions README?", "present in the search-suggestions README?", "in the search-suggestions README?", "the search-suggestions README?", "search-suggestions README?", "README?"], "tokens": ["What", "design", "discussions", "are", "present", "in", "the", "search", "suggestions", "README", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1205009277", "fields": {"term": "How can I use an attribute with array of strings?", "namespace": "open-p", "hash": 1205009277, "terms": ["How can I use an attribute with array of strings?", "can I use an attribute with array of strings?", "I use an attribute with array of strings?", "use an attribute with array of strings?", "an attribute with array of strings?", "attribute with array of strings?", "with array of strings?", "array of strings?", "of strings?", "strings?"], "tokens": ["How", "can", "I", "use", "an", "attribute", "with", "array", "of", "strings", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1158426004", "fields": {"term": "What is the purpose of the array for the attribute?", "namespace": "open-p", "hash": -1158426004, "terms": ["What is the purpose of the array for the attribute?", "is the purpose of the array for the attribute?", "the purpose of the array for the attribute?", "purpose of the array for the attribute?", "of the array for the attribute?", "the array for the attribute?", "array for the attribute?", "for the attribute?", "the attribute?", "attribute?"], "tokens": ["What", "is", "the", "purpose", "of", "the", "array", "for", "the", "attribute", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1192867786", "fields": {"term": "What is the indexing option for company_name_string?", "namespace": "open-p", "hash": 1192867786, "terms": ["What is the indexing option for company_name_string?", "is the indexing option for company_name_string?", "the indexing option for company_name_string?", "indexing option for company_name_string?", "option for company_name_string?", "for company_name_string?", "company_name_string?"], "tokens": ["What", "is", "the", "indexing", "option", "for", "company_name_string", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::247138497", "fields": {"term": "How does the array support more terms?", "namespace": "open-p", "hash": 247138497, "terms": ["How does the array support more terms?", "does the array support more terms?", "the array support more terms?", "array support more terms?", "support more terms?", "more terms?", "terms?"], "tokens": ["How", "does", "the", "array", "support", "more", "terms", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1610874684", "fields": {"term": "What is the benefit of using array for fields with few terms?", "namespace": "open-p", "hash": -1610874684, "terms": ["What is the benefit of using array for fields with few terms?", "is the benefit of using array for fields with few terms?", "the benefit of using array for fields with few terms?", "benefit of using array for fields with few terms?", "of using array for fields with few terms?", "using array for fields with few terms?", "array for fields with few terms?", "for fields with few terms?", "fields with few terms?", "with few terms?", "few terms?", "terms?"], "tokens": ["What", "is", "the", "benefit", "of", "using", "array", "for", "fields", "with", "few", "terms", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2038708973", "fields": {"term": "What is gram-size used for?", "namespace": "open-p", "hash": -2038708973, "terms": ["What is gram-size used for?", "is gram-size used for?", "gram-size used for?", "used for?", "for?"], "tokens": ["What", "is", "gram", "size", "used", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1027795967", "fields": {"term": "What is the benefit of indexing ngrams?", "namespace": "open-p", "hash": 1027795967, "terms": ["What is the benefit of indexing ngrams?", "is the benefit of indexing ngrams?", "the benefit of indexing ngrams?", "benefit of indexing ngrams?", "of indexing ngrams?", "indexing ngrams?", "ngrams?"], "tokens": ["What", "is", "the", "benefit", "of", "indexing", "ngrams", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1868602368", "fields": {"term": "How does ngram indexing work?", "namespace": "open-p", "hash": -1868602368, "terms": ["How does ngram indexing work?", "does ngram indexing work?", "ngram indexing work?", "indexing work?", "work?"], "tokens": ["How", "does", "ngram", "indexing", "work", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-354997966", "fields": {"term": "What is the difference between gram and phrase?", "namespace": "open-p", "hash": -354997966, "terms": ["What is the difference between gram and phrase?", "is the difference between gram and phrase?", "the difference between gram and phrase?", "difference between gram and phrase?", "between gram and phrase?", "gram and phrase?", "and phrase?", "phrase?"], "tokens": ["What", "is", "the", "difference", "between", "gram", "and", "phrase", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-961263281", "fields": {"term": "When is indexing using ngrams useful?", "namespace": "open-p", "hash": -961263281, "terms": ["When is indexing using ngrams useful?", "is indexing using ngrams useful?", "indexing using ngrams useful?", "using ngrams useful?", "ngrams useful?", "useful?"], "tokens": ["When", "is", "indexing", "using", "ngrams", "useful", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2039499582", "fields": {"term": "What are the limitations of ngram indexing?", "namespace": "open-p", "hash": -2039499582, "terms": ["What are the limitations of ngram indexing?", "are the limitations of ngram indexing?", "the limitations of ngram indexing?", "limitations of ngram indexing?", "of ngram indexing?", "ngram indexing?", "indexing?"], "tokens": ["What", "are", "the", "limitations", "of", "ngram", "indexing", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-83349626", "fields": {"term": "What are regular expressions?", "namespace": "open-p", "hash": -83349626, "terms": ["What are regular expressions?", "are regular expressions?", "regular expressions?", "expressions?"], "tokens": ["What", "are", "regular", "expressions", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1280671005", "fields": {"term": "Is regular expression optimization supported?", "namespace": "open-p", "hash": 1280671005, "terms": ["Is regular expression optimization supported?", "regular expression optimization supported?", "expression optimization supported?", "optimization supported?", "supported?"], "tokens": ["Is", "regular", "expression", "optimization", "supported", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1630024230", "fields": {"term": "How does Vespa handle regular expressions?", "namespace": "open-p", "hash": 1630024230, "terms": ["How does Vespa handle regular expressions?", "does Vespa handle regular expressions?", "Vespa handle regular expressions?", "handle regular expressions?", "regular expressions?", "expressions?"], "tokens": ["How", "does", "Vespa", "handle", "regular", "expressions", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1522853707", "fields": {"term": "Can regular expressions be used in attributes?", "namespace": "open-p", "hash": 1522853707, "terms": ["Can regular expressions be used in attributes?", "regular expressions be used in attributes?", "expressions be used in attributes?", "be used in attributes?", "used in attributes?", "in attributes?", "attributes?"], "tokens": ["Can", "regular", "expressions", "be", "used", "in", "attributes", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1636790664", "fields": {"term": "What is the default ranking?", "namespace": "open-p", "hash": -1636790664, "terms": ["What is the default ranking?", "is the default ranking?", "the default ranking?", "default ranking?", "ranking?"], "tokens": ["What", "is", "the", "default", "ranking", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1384791109", "fields": {"term": "How can weights be set?", "namespace": "open-p", "hash": 1384791109, "terms": ["How can weights be set?", "can weights be set?", "weights be set?", "be set?", "set?"], "tokens": ["How", "can", "weights", "be", "set", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::779608516", "fields": {"term": "What other ranking dimensions are there?", "namespace": "open-p", "hash": 779608516, "terms": ["What other ranking dimensions are there?", "other ranking dimensions are there?", "ranking dimensions are there?", "dimensions are there?", "are there?", "there?"], "tokens": ["What", "other", "ranking", "dimensions", "are", "there", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-721904694", "fields": {"term": "What is weight in Vespa?", "namespace": "open-p", "hash": -721904694, "terms": ["What is weight in Vespa?", "is weight in Vespa?", "weight in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["What", "is", "weight", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::209515108", "fields": {"term": "What is the significance feature?", "namespace": "open-p", "hash": 209515108, "terms": ["What is the significance feature?", "is the significance feature?", "the significance feature?", "significance feature?", "feature?"], "tokens": ["What", "is", "the", "significance", "feature", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1663166395", "fields": {"term": "How is the significance calculated?", "namespace": "open-p", "hash": 1663166395, "terms": ["How is the significance calculated?", "is the significance calculated?", "the significance calculated?", "significance calculated?", "calculated?"], "tokens": ["How", "is", "the", "significance", "calculated", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1853772111", "fields": {"term": "What is connectedness in Vespa?", "namespace": "open-p", "hash": -1853772111, "terms": ["What is connectedness in Vespa?", "is connectedness in Vespa?", "connectedness in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["What", "is", "connectedness", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2018909158", "fields": {"term": "How can connectedness be set?", "namespace": "open-p", "hash": -2018909158, "terms": ["How can connectedness be set?", "can connectedness be set?", "connectedness be set?", "be set?", "set?"], "tokens": ["How", "can", "connectedness", "be", "set", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::861572901", "fields": {"term": "What is fieldMatch?", "namespace": "open-p", "hash": 861572901, "terms": ["What is fieldMatch?", "is fieldMatch?", "fieldMatch?"], "tokens": ["What", "is", "fieldMatch", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::156202935", "fields": {"term": "What is the purpose of inspecting generated configuration?", "namespace": "open-p", "hash": 156202935, "terms": ["What is the purpose of inspecting generated configuration?", "is the purpose of inspecting generated configuration?", "the purpose of inspecting generated configuration?", "purpose of inspecting generated configuration?", "of inspecting generated configuration?", "inspecting generated configuration?", "generated configuration?", "configuration?"], "tokens": ["What", "is", "the", "purpose", "of", "inspecting", "generated", "configuration", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1803589332", "fields": {"term": "What are the differences for artist and album fields?", "namespace": "open-p", "hash": -1803589332, "terms": ["What are the differences for artist and album fields?", "are the differences for artist and album fields?", "the differences for artist and album fields?", "differences for artist and album fields?", "for artist and album fields?", "artist and album fields?", "and album fields?", "album fields?", "fields?"], "tokens": ["What", "are", "the", "differences", "for", "artist", "and", "album", "fields", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1261160096", "fields": {"term": "What is the field indexing for artist and album?", "namespace": "open-p", "hash": 1261160096, "terms": ["What is the field indexing for artist and album?", "is the field indexing for artist and album?", "the field indexing for artist and album?", "field indexing for artist and album?", "indexing for artist and album?", "for artist and album?", "artist and album?", "and album?", "album?"], "tokens": ["What", "is", "the", "field", "indexing", "for", "artist", "and", "album", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::5005427", "fields": {"term": "What is the match for artist field?", "namespace": "open-p", "hash": 5005427, "terms": ["What is the match for artist field?", "is the match for artist field?", "the match for artist field?", "match for artist field?", "for artist field?", "artist field?", "field?"], "tokens": ["What", "is", "the", "match", "for", "artist", "field", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-253620961", "fields": {"term": "What is the indexing for album field?", "namespace": "open-p", "hash": -253620961, "terms": ["What is the indexing for album field?", "is the indexing for album field?", "the indexing for album field?", "indexing for album field?", "for album field?", "album field?", "field?"], "tokens": ["What", "is", "the", "indexing", "for", "album", "field", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::11551547", "fields": {"term": "What is ilscript for?", "namespace": "open-p", "hash": 11551547, "terms": ["What is ilscript for?", "is ilscript for?", "ilscript for?", "for?"], "tokens": ["What", "is", "ilscript", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2110874662", "fields": {"term": "What is the Text Search Tutorial?", "namespace": "open-p", "hash": -2110874662, "terms": ["What is the Text Search Tutorial?", "is the Text Search Tutorial?", "the Text Search Tutorial?", "Text Search Tutorial?", "Search Tutorial?", "Tutorial?"], "tokens": ["What", "is", "the", "Text", "Search", "Tutorial", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::837518760", "fields": {"term": "What is MS MARCO?", "namespace": "open-p", "hash": 837518760, "terms": ["What is MS MARCO?", "is MS MARCO?", "MS MARCO?", "MARCO?"], "tokens": ["What", "is", "MS", "MARCO", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::388296086", "fields": {"term": "What is ML?", "namespace": "open-p", "hash": 388296086, "terms": ["What is ML?", "is ML?", "ML?"], "tokens": ["What", "is", "ML", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-94277828", "fields": {"term": "What is data collection logic?", "namespace": "open-p", "hash": -94277828, "terms": ["What is data collection logic?", "is data collection logic?", "data collection logic?", "collection logic?", "logic?"], "tokens": ["What", "is", "data", "collection", "logic", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1855636872", "fields": {"term": "What are sanity-checks for datasets?", "namespace": "open-p", "hash": -1855636872, "terms": ["What are sanity-checks for datasets?", "are sanity-checks for datasets?", "sanity-checks for datasets?", "for datasets?", "datasets?"], "tokens": ["What", "are", "sanity", "checks", "for", "datasets", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::306401264", "fields": {"term": "What is Vespa's rank feature set?", "namespace": "open-p", "hash": 306401264, "terms": ["What is Vespa's rank feature set?", "is Vespa's rank feature set?", "Vespa's rank feature set?", "rank feature set?", "feature set?", "set?"], "tokens": ["What", "is", "Vespa", "'", "s", "rank", "feature", "set", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::794640097", "fields": {"term": "How can rank features improve ranking?", "namespace": "open-p", "hash": 794640097, "terms": ["How can rank features improve ranking?", "can rank features improve ranking?", "rank features improve ranking?", "features improve ranking?", "improve ranking?", "ranking?"], "tokens": ["How", "can", "rank", "features", "improve", "ranking", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1076622308", "fields": {"term": "What is ranking.listFeatures?", "namespace": "open-p", "hash": -1076622308, "terms": ["What is ranking.listFeatures?", "is ranking.listFeatures?", "ranking.listFeatures?"], "tokens": ["What", "is", "ranking", "listFeatures", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::282030136", "fields": {"term": "How to access default ranking features?", "namespace": "open-p", "hash": 282030136, "terms": ["How to access default ranking features?", "to access default ranking features?", "access default ranking features?", "default ranking features?", "ranking features?", "features?"], "tokens": ["How", "to", "access", "default", "ranking", "features", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-872171979", "fields": {"term": "What is returned by default?", "namespace": "open-p", "hash": -872171979, "terms": ["What is returned by default?", "is returned by default?", "returned by default?", "by default?", "default?"], "tokens": ["What", "is", "returned", "by", "default", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::670841169", "fields": {"term": "Can the default list of rank features change?", "namespace": "open-p", "hash": 670841169, "terms": ["Can the default list of rank features change?", "the default list of rank features change?", "default list of rank features change?", "list of rank features change?", "of rank features change?", "rank features change?", "features change?", "change?"], "tokens": ["Can", "the", "default", "list", "of", "rank", "features", "change", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::595540509", "fields": {"term": "Where can I check the current list of rank features?", "namespace": "open-p", "hash": 595540509, "terms": ["Where can I check the current list of rank features?", "can I check the current list of rank features?", "I check the current list of rank features?", "check the current list of rank features?", "the current list of rank features?", "current list of rank features?", "list of rank features?", "of rank features?", "rank features?", "features?"], "tokens": ["Where", "can", "I", "check", "the", "current", "list", "of", "rank", "features", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::214331442", "fields": {"term": "What is the purpose of the 'collect_rank_features' profile?", "namespace": "open-p", "hash": 214331442, "terms": ["What is the purpose of the 'collect_rank_features' profile?", "is the purpose of the 'collect_rank_features' profile?", "the purpose of the 'collect_rank_features' profile?", "purpose of the 'collect_rank_features' profile?", "of the 'collect_rank_features' profile?", "the 'collect_rank_features' profile?", "'collect_rank_features' profile?", "profile?"], "tokens": ["What", "is", "the", "purpose", "of", "the", "'", "collect_rank_features", "'", "profile", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1813864781", "fields": {"term": "How do you disable the default ranking features?", "namespace": "open-p", "hash": 1813864781, "terms": ["How do you disable the default ranking features?", "do you disable the default ranking features?", "you disable the default ranking features?", "disable the default ranking features?", "the default ranking features?", "default ranking features?", "ranking features?", "features?"], "tokens": ["How", "do", "you", "disable", "the", "default", "ranking", "features", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2100458656", "fields": {"term": "What is the 'random' global feature?", "namespace": "open-p", "hash": -2100458656, "terms": ["What is the 'random' global feature?", "is the 'random' global feature?", "the 'random' global feature?", "'random' global feature?", "global feature?", "feature?"], "tokens": ["What", "is", "the", "'", "random", "'", "global", "feature", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-784403950", "fields": {"term": "What are some advisable use cases for many ranking features?", "namespace": "open-p", "hash": -784403950, "terms": ["What are some advisable use cases for many ranking features?", "are some advisable use cases for many ranking features?", "some advisable use cases for many ranking features?", "advisable use cases for many ranking features?", "use cases for many ranking features?", "cases for many ranking features?", "for many ranking features?", "many ranking features?", "ranking features?", "features?"], "tokens": ["What", "are", "some", "advisable", "use", "cases", "for", "many", "ranking", "features", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1586490627", "fields": {"term": "What is the MS MARCO dataset?", "namespace": "open-p", "hash": -1586490627, "terms": ["What is the MS MARCO dataset?", "is the MS MARCO dataset?", "the MS MARCO dataset?", "MS MARCO dataset?", "MARCO dataset?", "dataset?"], "tokens": ["What", "is", "the", "MS", "MARCO", "dataset", "?"], "corpus_count": 3, "document_count": 3}}, -{"put": "id:term:term::-1140010734", "fields": {"term": "What is collect_training_data.py?", "namespace": "open-p", "hash": -1140010734, "terms": ["What is collect_training_data.py?", "is collect_training_data.py?", "collect_training_data.py?"], "tokens": ["What", "is", "collect_training_data", "py", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::546018252", "fields": {"term": "What are relevant_id pairs?", "namespace": "open-p", "hash": 546018252, "terms": ["What are relevant_id pairs?", "are relevant_id pairs?", "relevant_id pairs?", "pairs?"], "tokens": ["What", "are", "relevant_id", "pairs", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1836060345", "fields": {"term": "What is the collect_rank_features rank-profile?", "namespace": "open-p", "hash": 1836060345, "terms": ["What is the collect_rank_features rank-profile?", "is the collect_rank_features rank-profile?", "the collect_rank_features rank-profile?", "collect_rank_features rank-profile?", "rank-profile?"], "tokens": ["What", "is", "the", "collect_rank_features", "rank", "profile", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-209632807", "fields": {"term": "What is the output folder?", "namespace": "open-p", "hash": -209632807, "terms": ["What is the output folder?", "is the output folder?", "the output folder?", "output folder?", "folder?"], "tokens": ["What", "is", "the", "output", "folder", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1636178908", "fields": {"term": "What does collect_training_data.py do?", "namespace": "open-p", "hash": -1636178908, "terms": ["What does collect_training_data.py do?", "does collect_training_data.py do?", "collect_training_data.py do?", "do?"], "tokens": ["What", "does", "collect_training_data", "py", "do", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2009549197", "fields": {"term": "What does collect_rank_features request?", "namespace": "open-p", "hash": -2009549197, "terms": ["What does collect_rank_features request?", "does collect_rank_features request?", "collect_rank_features request?", "request?"], "tokens": ["What", "does", "collect_rank_features", "request", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1105376994", "fields": {"term": "What is the goal of data collection?", "namespace": "open-p", "hash": 1105376994, "terms": ["What is the goal of data collection?", "is the goal of data collection?", "the goal of data collection?", "goal of data collection?", "of data collection?", "data collection?", "collection?"], "tokens": ["What", "is", "the", "goal", "of", "data", "collection", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-610037249", "fields": {"term": "How is data collected?", "namespace": "open-p", "hash": -610037249, "terms": ["How is data collected?", "is data collected?", "data collected?", "collected?"], "tokens": ["How", "is", "data", "collected", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1989819659", "fields": {"term": "What is the format of collected data?", "namespace": "open-p", "hash": 1989819659, "terms": ["What is the format of collected data?", "is the format of collected data?", "the format of collected data?", "format of collected data?", "of collected data?", "collected data?", "data?"], "tokens": ["What", "is", "the", "format", "of", "collected", "data", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::157161093", "fields": {"term": "What hits are returned by Vespa?", "namespace": "open-p", "hash": 157161093, "terms": ["What hits are returned by Vespa?", "hits are returned by Vespa?", "are returned by Vespa?", "returned by Vespa?", "by Vespa?", "Vespa?"], "tokens": ["What", "hits", "are", "returned", "by", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::396363809", "fields": {"term": "What is the pseudo-code for data collection?", "namespace": "open-p", "hash": 396363809, "terms": ["What is the pseudo-code for data collection?", "is the pseudo-code for data collection?", "the pseudo-code for data collection?", "pseudo-code for data collection?", "for data collection?", "data collection?", "collection?"], "tokens": ["What", "is", "the", "pseudo", "code", "for", "data", "collection", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-547406431", "fields": {"term": "What parameter specifies the desired query string?", "namespace": "open-p", "hash": -547406431, "terms": ["What parameter specifies the desired query string?", "parameter specifies the desired query string?", "specifies the desired query string?", "the desired query string?", "desired query string?", "query string?", "string?"], "tokens": ["What", "parameter", "specifies", "the", "desired", "query", "string", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::220461872", "fields": {"term": "What is the syntax to retrieve more than one document?", "namespace": "open-p", "hash": 220461872, "terms": ["What is the syntax to retrieve more than one document?", "is the syntax to retrieve more than one document?", "the syntax to retrieve more than one document?", "syntax to retrieve more than one document?", "to retrieve more than one document?", "retrieve more than one document?", "more than one document?", "than one document?", "one document?", "document?"], "tokens": ["What", "is", "the", "syntax", "to", "retrieve", "more", "than", "one", "document", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1101294771", "fields": {"term": "How does the recall parameter work?", "namespace": "open-p", "hash": 1101294771, "terms": ["How does the recall parameter work?", "does the recall parameter work?", "the recall parameter work?", "recall parameter work?", "parameter work?", "work?"], "tokens": ["How", "does", "the", "recall", "parameter", "work", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1924602723", "fields": {"term": "What is the purpose of setting 'hits' to 1?", "namespace": "open-p", "hash": 1924602723, "terms": ["What is the purpose of setting 'hits' to 1?", "is the purpose of setting 'hits' to 1?", "the purpose of setting 'hits' to 1?", "purpose of setting 'hits' to 1?", "of setting 'hits' to 1?", "setting 'hits' to 1?", "'hits' to 1?", "to 1?", "1?"], "tokens": ["What", "is", "the", "purpose", "of", "setting", "'", "hits", "'", "to", "1", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::665128746", "fields": {"term": "What is the function call to get relevant hits?", "namespace": "open-p", "hash": 665128746, "terms": ["What is the function call to get relevant hits?", "is the function call to get relevant hits?", "the function call to get relevant hits?", "function call to get relevant hits?", "call to get relevant hits?", "to get relevant hits?", "get relevant hits?", "relevant hits?", "hits?"], "tokens": ["What", "is", "the", "function", "call", "to", "get", "relevant", "hits", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-229975335", "fields": {"term": "What parameters instruct Vespa to return id and rank-features?", "namespace": "open-p", "hash": -229975335, "terms": ["What parameters instruct Vespa to return id and rank-features?", "parameters instruct Vespa to return id and rank-features?", "instruct Vespa to return id and rank-features?", "Vespa to return id and rank-features?", "to return id and rank-features?", "return id and rank-features?", "id and rank-features?", "and rank-features?", "rank-features?"], "tokens": ["What", "parameters", "instruct", "Vespa", "to", "return", "id", "and", "rank", "features", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-832728134", "fields": {"term": "What is the second Vespa request?", "namespace": "open-p", "hash": -832728134, "terms": ["What is the second Vespa request?", "is the second Vespa request?", "the second Vespa request?", "second Vespa request?", "Vespa request?", "request?"], "tokens": ["What", "is", "the", "second", "Vespa", "request", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::958598289", "fields": {"term": "What is the purpose of `get_random_hits` function?", "namespace": "open-p", "hash": 958598289, "terms": ["What is the purpose of `get_random_hits` function?", "is the purpose of `get_random_hits` function?", "the purpose of `get_random_hits` function?", "purpose of `get_random_hits` function?", "of `get_random_hits` function?", "`get_random_hits` function?", "function?"], "tokens": ["What", "is", "the", "purpose", "of", "`", "get_random_hits", "`", "function", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1309891414", "fields": {"term": "What is the `number_random_sample` parameter?", "namespace": "open-p", "hash": -1309891414, "terms": ["What is the `number_random_sample` parameter?", "is the `number_random_sample` parameter?", "the `number_random_sample` parameter?", "`number_random_sample` parameter?", "parameter?"], "tokens": ["What", "is", "the", "`", "number_random_sample", "`", "parameter", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1822291101", "fields": {"term": "What are the changes with respect to `get_relevant_hit`?", "namespace": "open-p", "hash": 1822291101, "terms": ["What are the changes with respect to `get_relevant_hit`?", "are the changes with respect to `get_relevant_hit`?", "the changes with respect to `get_relevant_hit`?", "changes with respect to `get_relevant_hit`?", "with respect to `get_relevant_hit`?", "respect to `get_relevant_hit`?", "to `get_relevant_hit`?", "`get_relevant_hit`?"], "tokens": ["What", "are", "the", "changes", "with", "respect", "to", "`", "get_relevant_hit", "`?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1395168116", "fields": {"term": "What is the `second-phase` configuration parameter?", "namespace": "open-p", "hash": 1395168116, "terms": ["What is the `second-phase` configuration parameter?", "is the `second-phase` configuration parameter?", "the `second-phase` configuration parameter?", "`second-phase` configuration parameter?", "configuration parameter?", "parameter?"], "tokens": ["What", "is", "the", "`", "second", "phase", "`", "configuration", "parameter", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::321993077", "fields": {"term": "What does using `random` as the second-phase ranking function ensure?", "namespace": "open-p", "hash": 321993077, "terms": ["What does using `random` as the second-phase ranking function ensure?", "does using `random` as the second-phase ranking function ensure?", "using `random` as the second-phase ranking function ensure?", "`random` as the second-phase ranking function ensure?", "as the second-phase ranking function ensure?", "the second-phase ranking function ensure?", "second-phase ranking function ensure?", "ranking function ensure?", "function ensure?", "ensure?"], "tokens": ["What", "does", "using", "`", "random", "`", "as", "the", "second", "phase", "ranking", "function", "ensure", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::778403244", "fields": {"term": "How are the top documents returned by Vespa selected?", "namespace": "open-p", "hash": 778403244, "terms": ["How are the top documents returned by Vespa selected?", "are the top documents returned by Vespa selected?", "the top documents returned by Vespa selected?", "top documents returned by Vespa selected?", "documents returned by Vespa selected?", "returned by Vespa selected?", "by Vespa selected?", "Vespa selected?", "selected?"], "tokens": ["How", "are", "the", "top", "documents", "returned", "by", "Vespa", "selected", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-665603673", "fields": {"term": "What is the format of Vespa result parsing file?", "namespace": "open-p", "hash": -665603673, "terms": ["What is the format of Vespa result parsing file?", "is the format of Vespa result parsing file?", "the format of Vespa result parsing file?", "format of Vespa result parsing file?", "of Vespa result parsing file?", "Vespa result parsing file?", "result parsing file?", "parsing file?", "file?"], "tokens": ["What", "is", "the", "format", "of", "Vespa", "result", "parsing", "file", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1727542179", "fields": {"term": "What is the meaning of the 'relevant' column?", "namespace": "open-p", "hash": -1727542179, "terms": ["What is the meaning of the 'relevant' column?", "is the meaning of the 'relevant' column?", "the meaning of the 'relevant' column?", "meaning of the 'relevant' column?", "of the 'relevant' column?", "the 'relevant' column?", "'relevant' column?", "column?"], "tokens": ["What", "is", "the", "meaning", "of", "the", "'", "relevant", "'", "column", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1901183570", "fields": {"term": "What does 'relevant'=1 signify in the file?", "namespace": "open-p", "hash": -1901183570, "terms": ["What does 'relevant'=1 signify in the file?", "does 'relevant'=1 signify in the file?", "'relevant'=1 signify in the file?", "signify in the file?", "in the file?", "the file?", "file?"], "tokens": ["What", "does", "'", "relevant", "'=", "1", "signify", "in", "the", "file", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-165123865", "fields": {"term": "How is the Vespa result parsed and stored?", "namespace": "open-p", "hash": -165123865, "terms": ["How is the Vespa result parsed and stored?", "is the Vespa result parsed and stored?", "the Vespa result parsed and stored?", "Vespa result parsed and stored?", "result parsed and stored?", "parsed and stored?", "and stored?", "stored?"], "tokens": ["How", "is", "the", "Vespa", "result", "parsed", "and", "stored", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1742559908", "fields": {"term": "What are the columns in the Vespa result parsing file?", "namespace": "open-p", "hash": -1742559908, "terms": ["What are the columns in the Vespa result parsing file?", "are the columns in the Vespa result parsing file?", "the columns in the Vespa result parsing file?", "columns in the Vespa result parsing file?", "in the Vespa result parsing file?", "the Vespa result parsing file?", "Vespa result parsing file?", "result parsing file?", "parsing file?", "file?"], "tokens": ["What", "are", "the", "columns", "in", "the", "Vespa", "result", "parsing", "file", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::796622912", "fields": {"term": "What is the meaning of 'bm25' and 'nativeRank' columns in the file?", "namespace": "open-p", "hash": 796622912, "terms": ["What is the meaning of 'bm25' and 'nativeRank' columns in the file?", "is the meaning of 'bm25' and 'nativeRank' columns in the file?", "the meaning of 'bm25' and 'nativeRank' columns in the file?", "meaning of 'bm25' and 'nativeRank' columns in the file?", "of 'bm25' and 'nativeRank' columns in the file?", "'bm25' and 'nativeRank' columns in the file?", "and 'nativeRank' columns in the file?", "'nativeRank' columns in the file?", "columns in the file?", "in the file?", "the file?", "file?"], "tokens": ["What", "is", "the", "meaning", "of", "'", "bm25", "'", "and", "'", "nativeRank", "'", "columns", "in", "the", "file", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::227639511", "fields": {"term": "How is the value in the 'relevant' column determined?", "namespace": "open-p", "hash": 227639511, "terms": ["How is the value in the 'relevant' column determined?", "is the value in the 'relevant' column determined?", "the value in the 'relevant' column determined?", "value in the 'relevant' column determined?", "in the 'relevant' column determined?", "the 'relevant' column determined?", "'relevant' column determined?", "column determined?", "determined?"], "tokens": ["How", "is", "the", "value", "in", "the", "'", "relevant", "'", "column", "determined", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1229175386", "fields": {"term": "What is data collection sanity-check?", "namespace": "open-p", "hash": -1229175386, "terms": ["What is data collection sanity-check?", "is data collection sanity-check?", "data collection sanity-check?", "collection sanity-check?", "sanity-check?"], "tokens": ["What", "is", "data", "collection", "sanity", "check", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1998730418", "fields": {"term": "What is the baseline used?", "namespace": "open-p", "hash": -1998730418, "terms": ["What is the baseline used?", "is the baseline used?", "the baseline used?", "baseline used?", "used?"], "tokens": ["What", "is", "the", "baseline", "used", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-484787964", "fields": {"term": "What is the linear model?", "namespace": "open-p", "hash": -484787964, "terms": ["What is the linear model?", "is the linear model?", "the linear model?", "linear model?", "model?"], "tokens": ["What", "is", "the", "linear", "model", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::948713669", "fields": {"term": "What is the purpose of dataset validation?", "namespace": "open-p", "hash": 948713669, "terms": ["What is the purpose of dataset validation?", "is the purpose of dataset validation?", "the purpose of dataset validation?", "purpose of dataset validation?", "of dataset validation?", "dataset validation?", "validation?"], "tokens": ["What", "is", "the", "purpose", "of", "dataset", "validation", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1193026960", "fields": {"term": "What happened when the model failed on the test set?", "namespace": "open-p", "hash": -1193026960, "terms": ["What happened when the model failed on the test set?", "happened when the model failed on the test set?", "when the model failed on the test set?", "the model failed on the test set?", "model failed on the test set?", "failed on the test set?", "on the test set?", "the test set?", "test set?", "set?"], "tokens": ["What", "happened", "when", "the", "model", "failed", "on", "the", "test", "set", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-369130356", "fields": {"term": "What led to investigating and catching the bug?", "namespace": "open-p", "hash": -369130356, "terms": ["What led to investigating and catching the bug?", "led to investigating and catching the bug?", "to investigating and catching the bug?", "investigating and catching the bug?", "and catching the bug?", "catching the bug?", "the bug?", "bug?"], "tokens": ["What", "led", "to", "investigating", "and", "catching", "the", "bug", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::375317612", "fields": {"term": "What is the MRR?", "namespace": "open-p", "hash": 375317612, "terms": ["What is the MRR?", "is the MRR?", "the MRR?", "MRR?"], "tokens": ["What", "is", "the", "MRR", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-122875397", "fields": {"term": "What is TF-Ranking?", "namespace": "open-p", "hash": -122875397, "terms": ["What is TF-Ranking?", "is TF-Ranking?", "TF-Ranking?"], "tokens": ["What", "is", "TF", "Ranking", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::968415467", "fields": {"term": "What is a listwise loss function?", "namespace": "open-p", "hash": 968415467, "terms": ["What is a listwise loss function?", "is a listwise loss function?", "a listwise loss function?", "listwise loss function?", "loss function?", "function?"], "tokens": ["What", "is", "a", "listwise", "loss", "function", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::782885960", "fields": {"term": "What is a pointwise loss function?", "namespace": "open-p", "hash": 782885960, "terms": ["What is a pointwise loss function?", "is a pointwise loss function?", "a pointwise loss function?", "pointwise loss function?", "loss function?", "function?"], "tokens": ["What", "is", "a", "pointwise", "loss", "function", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1471391789", "fields": {"term": "What is the difference between listwise and pointwise loss?", "namespace": "open-p", "hash": -1471391789, "terms": ["What is the difference between listwise and pointwise loss?", "is the difference between listwise and pointwise loss?", "the difference between listwise and pointwise loss?", "difference between listwise and pointwise loss?", "between listwise and pointwise loss?", "listwise and pointwise loss?", "and pointwise loss?", "pointwise loss?", "loss?"], "tokens": ["What", "is", "the", "difference", "between", "listwise", "and", "pointwise", "loss", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-746491481", "fields": {"term": "What is the best loss function for ranking?", "namespace": "open-p", "hash": -746491481, "terms": ["What is the best loss function for ranking?", "is the best loss function for ranking?", "the best loss function for ranking?", "best loss function for ranking?", "loss function for ranking?", "function for ranking?", "for ranking?", "ranking?"], "tokens": ["What", "is", "the", "best", "loss", "function", "for", "ranking", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1704134717", "fields": {"term": "What is the ranking function plot?", "namespace": "open-p", "hash": 1704134717, "terms": ["What is the ranking function plot?", "is the ranking function plot?", "the ranking function plot?", "ranking function plot?", "function plot?", "plot?"], "tokens": ["What", "is", "the", "ranking", "function", "plot", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::739174104", "fields": {"term": "What are popular ML libraries integrated with Vespa?", "namespace": "open-p", "hash": 739174104, "terms": ["What are popular ML libraries integrated with Vespa?", "are popular ML libraries integrated with Vespa?", "popular ML libraries integrated with Vespa?", "ML libraries integrated with Vespa?", "libraries integrated with Vespa?", "integrated with Vespa?", "with Vespa?", "Vespa?"], "tokens": ["What", "are", "popular", "ML", "libraries", "integrated", "with", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1934778989", "fields": {"term": "What is semantic search?", "namespace": "open-p", "hash": 1934778989, "terms": ["What is semantic search?", "is semantic search?", "semantic search?", "search?"], "tokens": ["What", "is", "semantic", "search", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::1761854453", "fields": {"term": "What is a pre-trained text embedding?", "namespace": "open-p", "hash": 1761854453, "terms": ["What is a pre-trained text embedding?", "is a pre-trained text embedding?", "a pre-trained text embedding?", "pre-trained text embedding?", "text embedding?", "embedding?"], "tokens": ["What", "is", "a", "pre", "trained", "text", "embedding", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1515992158", "fields": {"term": "How effective is the weakAND algorithm?", "namespace": "open-p", "hash": -1515992158, "terms": ["How effective is the weakAND algorithm?", "effective is the weakAND algorithm?", "is the weakAND algorithm?", "the weakAND algorithm?", "weakAND algorithm?", "algorithm?"], "tokens": ["How", "effective", "is", "the", "weakAND", "algorithm", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1454384152", "fields": {"term": "What are the limitations of pre-trained semantic vectors?", "namespace": "open-p", "hash": -1454384152, "terms": ["What are the limitations of pre-trained semantic vectors?", "are the limitations of pre-trained semantic vectors?", "the limitations of pre-trained semantic vectors?", "limitations of pre-trained semantic vectors?", "of pre-trained semantic vectors?", "pre-trained semantic vectors?", "semantic vectors?", "vectors?"], "tokens": ["What", "are", "the", "limitations", "of", "pre", "trained", "semantic", "vectors", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1053984642", "fields": {"term": "What datasets are less biased towards term-matching signals?", "namespace": "open-p", "hash": 1053984642, "terms": ["What datasets are less biased towards term-matching signals?", "datasets are less biased towards term-matching signals?", "are less biased towards term-matching signals?", "less biased towards term-matching signals?", "biased towards term-matching signals?", "towards term-matching signals?", "term-matching signals?", "signals?"], "tokens": ["What", "datasets", "are", "less", "biased", "towards", "term", "matching", "signals", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::258956421", "fields": {"term": "How many queries were randomly selected?", "namespace": "open-p", "hash": 258956421, "terms": ["How many queries were randomly selected?", "many queries were randomly selected?", "queries were randomly selected?", "were randomly selected?", "randomly selected?", "selected?"], "tokens": ["How", "many", "queries", "were", "randomly", "selected", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1303014758", "fields": {"term": "How many documents were randomly selected?", "namespace": "open-p", "hash": 1303014758, "terms": ["How many documents were randomly selected?", "many documents were randomly selected?", "documents were randomly selected?", "were randomly selected?", "randomly selected?", "selected?"], "tokens": ["How", "many", "documents", "were", "randomly", "selected", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::991267704", "fields": {"term": "Are the relevant documents guaranteed to be in the corpus?", "namespace": "open-p", "hash": 991267704, "terms": ["Are the relevant documents guaranteed to be in the corpus?", "the relevant documents guaranteed to be in the corpus?", "relevant documents guaranteed to be in the corpus?", "documents guaranteed to be in the corpus?", "guaranteed to be in the corpus?", "to be in the corpus?", "be in the corpus?", "in the corpus?", "the corpus?", "corpus?"], "tokens": ["Are", "the", "relevant", "documents", "guaranteed", "to", "be", "in", "the", "corpus", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1278220249", "fields": {"term": "Where is the entire dataset and code described?", "namespace": "open-p", "hash": 1278220249, "terms": ["Where is the entire dataset and code described?", "is the entire dataset and code described?", "the entire dataset and code described?", "entire dataset and code described?", "dataset and code described?", "and code described?", "code described?", "described?"], "tokens": ["Where", "is", "the", "entire", "dataset", "and", "code", "described", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::985651621", "fields": {"term": "What are the three metrics?", "namespace": "open-p", "hash": 985651621, "terms": ["What are the three metrics?", "are the three metrics?", "the three metrics?", "three metrics?", "metrics?"], "tokens": ["What", "are", "the", "three", "metrics", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-199356156", "fields": {"term": "What is `Recall @ 100`?", "namespace": "open-p", "hash": -199356156, "terms": ["What is `Recall @ 100`?", "is `Recall @ 100`?", "`Recall @ 100`?", "@ 100`?", "100`?"], "tokens": ["What", "is", "`", "Recall", "@", "100", "`?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-378537666", "fields": {"term": "What is `MRR @ 100`?", "namespace": "open-p", "hash": -378537666, "terms": ["What is `MRR @ 100`?", "is `MRR @ 100`?", "`MRR @ 100`?", "@ 100`?", "100`?"], "tokens": ["What", "is", "`", "MRR", "@", "100", "`?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1644597091", "fields": {"term": "What is the importance of `Matched docs` and `MRR @ 100`?", "namespace": "open-p", "hash": 1644597091, "terms": ["What is the importance of `Matched docs` and `MRR @ 100`?", "is the importance of `Matched docs` and `MRR @ 100`?", "the importance of `Matched docs` and `MRR @ 100`?", "importance of `Matched docs` and `MRR @ 100`?", "of `Matched docs` and `MRR @ 100`?", "`Matched docs` and `MRR @ 100`?", "docs` and `MRR @ 100`?", "and `MRR @ 100`?", "`MRR @ 100`?", "@ 100`?", "100`?"], "tokens": ["What", "is", "the", "importance", "of", "`", "Matched", "docs", "`", "and", "`", "MRR", "@", "100", "`?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::501651349", "fields": {"term": "What is the relationship between `Matched docs` and `Recall @ 100`?", "namespace": "open-p", "hash": 501651349, "terms": ["What is the relationship between `Matched docs` and `Recall @ 100`?", "is the relationship between `Matched docs` and `Recall @ 100`?", "the relationship between `Matched docs` and `Recall @ 100`?", "relationship between `Matched docs` and `Recall @ 100`?", "between `Matched docs` and `Recall @ 100`?", "`Matched docs` and `Recall @ 100`?", "docs` and `Recall @ 100`?", "and `Recall @ 100`?", "`Recall @ 100`?", "@ 100`?", "100`?"], "tokens": ["What", "is", "the", "relationship", "between", "`", "Matched", "docs", "`", "and", "`", "Recall", "@", "100", "`?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1912631219", "fields": {"term": "What is the purpose of a multi-phased ranking system?", "namespace": "open-p", "hash": 1912631219, "terms": ["What is the purpose of a multi-phased ranking system?", "is the purpose of a multi-phased ranking system?", "the purpose of a multi-phased ranking system?", "purpose of a multi-phased ranking system?", "of a multi-phased ranking system?", "a multi-phased ranking system?", "multi-phased ranking system?", "ranking system?", "system?"], "tokens": ["What", "is", "the", "purpose", "of", "a", "multi", "phased", "ranking", "system", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1462961763", "fields": {"term": "What is the assumption made about the 2nd phase in the experiments?", "namespace": "open-p", "hash": -1462961763, "terms": ["What is the assumption made about the 2nd phase in the experiments?", "is the assumption made about the 2nd phase in the experiments?", "the assumption made about the 2nd phase in the experiments?", "assumption made about the 2nd phase in the experiments?", "made about the 2nd phase in the experiments?", "about the 2nd phase in the experiments?", "the 2nd phase in the experiments?", "2nd phase in the experiments?", "phase in the experiments?", "in the experiments?", "the experiments?", "experiments?"], "tokens": ["What", "is", "the", "assumption", "made", "about", "the", "2nd", "phase", "in", "the", "experiments", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1957554705", "fields": {"term": "What is the purpose of using baselines?", "namespace": "open-p", "hash": 1957554705, "terms": ["What is the purpose of using baselines?", "is the purpose of using baselines?", "the purpose of using baselines?", "purpose of using baselines?", "of using baselines?", "using baselines?", "baselines?"], "tokens": ["What", "is", "the", "purpose", "of", "using", "baselines", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-121793733", "fields": {"term": "How many queries result in missing relevant documents with the 'AND' operator?", "namespace": "open-p", "hash": -121793733, "terms": ["How many queries result in missing relevant documents with the 'AND' operator?", "many queries result in missing relevant documents with the 'AND' operator?", "queries result in missing relevant documents with the 'AND' operator?", "result in missing relevant documents with the 'AND' operator?", "in missing relevant documents with the 'AND' operator?", "missing relevant documents with the 'AND' operator?", "relevant documents with the 'AND' operator?", "documents with the 'AND' operator?", "with the 'AND' operator?", "the 'AND' operator?", "'AND' operator?", "operator?"], "tokens": ["How", "many", "queries", "result", "in", "missing", "relevant", "documents", "with", "the", "'", "AND", "'", "operator", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-300011173", "fields": {"term": "What is the consequence of using the 'AND' operator?", "namespace": "open-p", "hash": -300011173, "terms": ["What is the consequence of using the 'AND' operator?", "is the consequence of using the 'AND' operator?", "the consequence of using the 'AND' operator?", "consequence of using the 'AND' operator?", "of using the 'AND' operator?", "using the 'AND' operator?", "the 'AND' operator?", "'AND' operator?", "operator?"], "tokens": ["What", "is", "the", "consequence", "of", "using", "the", "'", "AND", "'", "operator", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1141343776", "fields": {"term": "What is the recall rate for relevant documents with the 'OR' operator?'", "namespace": "open-p", "hash": -1141343776, "terms": ["What is the recall rate for relevant documents with the 'OR' operator?'", "is the recall rate for relevant documents with the 'OR' operator?'", "the recall rate for relevant documents with the 'OR' operator?'", "recall rate for relevant documents with the 'OR' operator?'", "rate for relevant documents with the 'OR' operator?'", "for relevant documents with the 'OR' operator?'", "relevant documents with the 'OR' operator?'", "documents with the 'OR' operator?'", "with the 'OR' operator?'", "the 'OR' operator?'", "'OR' operator?'", "operator?'"], "tokens": ["What", "is", "the", "recall", "rate", "for", "relevant", "documents", "with", "the", "'", "OR", "'", "operator", "?'"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1814867859", "fields": {"term": "Which pre-trained vectors were evaluated?", "namespace": "open-p", "hash": 1814867859, "terms": ["Which pre-trained vectors were evaluated?", "pre-trained vectors were evaluated?", "vectors were evaluated?", "were evaluated?", "evaluated?"], "tokens": ["Which", "pre", "trained", "vectors", "were", "evaluated", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::505891889", "fields": {"term": "Where can Universal sentence encoder be found?", "namespace": "open-p", "hash": 505891889, "terms": ["Where can Universal sentence encoder be found?", "can Universal sentence encoder be found?", "Universal sentence encoder be found?", "sentence encoder be found?", "encoder be found?", "be found?", "found?"], "tokens": ["Where", "can", "Universal", "sentence", "encoder", "be", "found", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1940610303", "fields": {"term": "What is the goal of the experiment?", "namespace": "open-p", "hash": -1940610303, "terms": ["What is the goal of the experiment?", "is the goal of the experiment?", "the goal of the experiment?", "goal of the experiment?", "of the experiment?", "the experiment?", "experiment?"], "tokens": ["What", "is", "the", "goal", "of", "the", "experiment", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-213516943", "fields": {"term": "What is sentence-transformers library?", "namespace": "open-p", "hash": -213516943, "terms": ["What is sentence-transformers library?", "is sentence-transformers library?", "sentence-transformers library?", "library?"], "tokens": ["What", "is", "sentence", "transformers", "library", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1454608874", "fields": {"term": "What is ANN?", "namespace": "open-p", "hash": 1454608874, "terms": ["What is ANN?", "is ANN?", "ANN?"], "tokens": ["What", "is", "ANN", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1149520392", "fields": {"term": "What is the ANN operator?", "namespace": "open-p", "hash": -1149520392, "terms": ["What is the ANN operator?", "is the ANN operator?", "the ANN operator?", "ANN operator?", "operator?"], "tokens": ["What", "is", "the", "ANN", "operator", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::1498141786", "fields": {"term": "How can you match documents based on distance between query and document tensors?", "namespace": "open-p", "hash": 1498141786, "terms": ["How can you match documents based on distance between query and document tensors?", "can you match documents based on distance between query and document tensors?", "you match documents based on distance between query and document tensors?", "match documents based on distance between query and document tensors?", "documents based on distance between query and document tensors?", "based on distance between query and document tensors?", "on distance between query and document tensors?", "distance between query and document tensors?", "between query and document tensors?", "query and document tensors?", "and document tensors?", "document tensors?", "tensors?"], "tokens": ["How", "can", "you", "match", "documents", "based", "on", "distance", "between", "query", "and", "document", "tensors", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-458561050", "fields": {"term": "How to send query embeddings?", "namespace": "open-p", "hash": -458561050, "terms": ["How to send query embeddings?", "to send query embeddings?", "send query embeddings?", "query embeddings?", "embeddings?"], "tokens": ["How", "to", "send", "query", "embeddings", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1187689035", "fields": {"term": "How does nearestNeighbor operator work?", "namespace": "open-p", "hash": 1187689035, "terms": ["How does nearestNeighbor operator work?", "does nearestNeighbor operator work?", "nearestNeighbor operator work?", "operator work?", "work?"], "tokens": ["How", "does", "nearestNeighbor", "operator", "work", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-712583128", "fields": {"term": "What is the targetHits property?", "namespace": "open-p", "hash": -712583128, "terms": ["What is the targetHits property?", "is the targetHits property?", "the targetHits property?", "targetHits property?", "property?"], "tokens": ["What", "is", "the", "targetHits", "property", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1428872132", "fields": {"term": "What is bert_title_body_all?", "namespace": "open-p", "hash": 1428872132, "terms": ["What is bert_title_body_all?", "is bert_title_body_all?", "bert_title_body_all?"], "tokens": ["What", "is", "bert_title_body_all", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::10603836", "fields": {"term": "What models were used?", "namespace": "open-p", "hash": 10603836, "terms": ["What models were used?", "models were used?", "were used?", "used?"], "tokens": ["What", "models", "were", "used", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::942955511", "fields": {"term": "What is the relationship between matched documents and recall?", "namespace": "open-p", "hash": 942955511, "terms": ["What is the relationship between matched documents and recall?", "is the relationship between matched documents and recall?", "the relationship between matched documents and recall?", "relationship between matched documents and recall?", "between matched documents and recall?", "matched documents and recall?", "documents and recall?", "and recall?", "recall?"], "tokens": ["What", "is", "the", "relationship", "between", "matched", "documents", "and", "recall", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1986171213", "fields": {"term": "What is the benefit of combining semantic search with term-matching?", "namespace": "open-p", "hash": -1986171213, "terms": ["What is the benefit of combining semantic search with term-matching?", "is the benefit of combining semantic search with term-matching?", "the benefit of combining semantic search with term-matching?", "benefit of combining semantic search with term-matching?", "of combining semantic search with term-matching?", "combining semantic search with term-matching?", "semantic search with term-matching?", "search with term-matching?", "with term-matching?", "term-matching?"], "tokens": ["What", "is", "the", "benefit", "of", "combining", "semantic", "search", "with", "term", "matching", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::286032968", "fields": {"term": "What features related to term-matching are available in Vespa?", "namespace": "open-p", "hash": 286032968, "terms": ["What features related to term-matching are available in Vespa?", "features related to term-matching are available in Vespa?", "related to term-matching are available in Vespa?", "to term-matching are available in Vespa?", "term-matching are available in Vespa?", "are available in Vespa?", "available in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["What", "features", "related", "to", "term", "matching", "are", "available", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::663782304", "fields": {"term": "What is the weakAnd implementation?", "namespace": "open-p", "hash": 663782304, "terms": ["What is the weakAnd implementation?", "is the weakAnd implementation?", "the weakAnd implementation?", "weakAnd implementation?", "implementation?"], "tokens": ["What", "is", "the", "weakAnd", "implementation", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1235386322", "fields": {"term": "How does the weakAnd operator work?", "namespace": "open-p", "hash": 1235386322, "terms": ["How does the weakAnd operator work?", "does the weakAnd operator work?", "the weakAnd operator work?", "weakAnd operator work?", "operator work?", "work?"], "tokens": ["How", "does", "the", "weakAnd", "operator", "work", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1626159142", "fields": {"term": "What are the two core text rank features used in the weakAnd implementation?", "namespace": "open-p", "hash": 1626159142, "terms": ["What are the two core text rank features used in the weakAnd implementation?", "are the two core text rank features used in the weakAnd implementation?", "the two core text rank features used in the weakAnd implementation?", "two core text rank features used in the weakAnd implementation?", "core text rank features used in the weakAnd implementation?", "text rank features used in the weakAnd implementation?", "rank features used in the weakAnd implementation?", "features used in the weakAnd implementation?", "used in the weakAnd implementation?", "in the weakAnd implementation?", "the weakAnd implementation?", "weakAnd implementation?", "implementation?"], "tokens": ["What", "are", "the", "two", "core", "text", "rank", "features", "used", "in", "the", "weakAnd", "implementation", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::117657078", "fields": {"term": "What is the recall metric for the weakAnd operator?", "namespace": "open-p", "hash": 117657078, "terms": ["What is the recall metric for the weakAnd operator?", "is the recall metric for the weakAnd operator?", "the recall metric for the weakAnd operator?", "recall metric for the weakAnd operator?", "metric for the weakAnd operator?", "for the weakAnd operator?", "the weakAnd operator?", "weakAnd operator?", "operator?"], "tokens": ["What", "is", "the", "recall", "metric", "for", "the", "weakAnd", "operator", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-218092686", "fields": {"term": "How many documents did the weakAnd operator match?", "namespace": "open-p", "hash": -218092686, "terms": ["How many documents did the weakAnd operator match?", "many documents did the weakAnd operator match?", "documents did the weakAnd operator match?", "did the weakAnd operator match?", "the weakAnd operator match?", "weakAnd operator match?", "operator match?", "match?"], "tokens": ["How", "many", "documents", "did", "the", "weakAnd", "operator", "match", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::351713357", "fields": {"term": "What is the difference between the weakAnd and OR operators regarding matched documents?", "namespace": "open-p", "hash": 351713357, "terms": ["What is the difference between the weakAnd and OR operators regarding matched documents?", "is the difference between the weakAnd and OR operators regarding matched documents?", "the difference between the weakAnd and OR operators regarding matched documents?", "difference between the weakAnd and OR operators regarding matched documents?", "between the weakAnd and OR operators regarding matched documents?", "the weakAnd and OR operators regarding matched documents?", "weakAnd and OR operators regarding matched documents?", "and OR operators regarding matched documents?", "OR operators regarding matched documents?", "operators regarding matched documents?", "regarding matched documents?", "matched documents?", "documents?"], "tokens": ["What", "is", "the", "difference", "between", "the", "weakAnd", "and", "OR", "operators", "regarding", "matched", "documents", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-320024724", "fields": {"term": "How do you set up a text search application on Vespa?", "namespace": "open-p", "hash": -320024724, "terms": ["How do you set up a text search application on Vespa?", "do you set up a text search application on Vespa?", "you set up a text search application on Vespa?", "set up a text search application on Vespa?", "up a text search application on Vespa?", "a text search application on Vespa?", "text search application on Vespa?", "search application on Vespa?", "application on Vespa?", "on Vespa?", "Vespa?"], "tokens": ["How", "do", "you", "set", "up", "a", "text", "search", "application", "on", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::681720356", "fields": {"term": "What future improvements will be added in other tutorials?", "namespace": "open-p", "hash": 681720356, "terms": ["What future improvements will be added in other tutorials?", "future improvements will be added in other tutorials?", "improvements will be added in other tutorials?", "will be added in other tutorials?", "be added in other tutorials?", "added in other tutorials?", "in other tutorials?", "other tutorials?", "tutorials?"], "tokens": ["What", "future", "improvements", "will", "be", "added", "in", "other", "tutorials", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1070638119", "fields": {"term": "What is Vespa CLI?", "namespace": "open-p", "hash": -1070638119, "terms": ["What is Vespa CLI?", "is Vespa CLI?", "Vespa CLI?", "CLI?"], "tokens": ["What", "is", "Vespa", "CLI", "?"], "corpus_count": 3, "document_count": 3}}, -{"put": "id:term:term::-177235240", "fields": {"term": "What is vespa-cli used for?", "namespace": "open-p", "hash": -177235240, "terms": ["What is vespa-cli used for?", "is vespa-cli used for?", "vespa-cli used for?", "used for?", "for?"], "tokens": ["What", "is", "vespa", "cli", "used", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-431587842", "fields": {"term": "How do I install Vespa CLI?", "namespace": "open-p", "hash": -431587842, "terms": ["How do I install Vespa CLI?", "do I install Vespa CLI?", "I install Vespa CLI?", "install Vespa CLI?", "Vespa CLI?", "CLI?"], "tokens": ["How", "do", "I", "install", "Vespa", "CLI", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1895888048", "fields": {"term": "How can I clone a Vespa application from GitHub?", "namespace": "open-p", "hash": 1895888048, "terms": ["How can I clone a Vespa application from GitHub?", "can I clone a Vespa application from GitHub?", "I clone a Vespa application from GitHub?", "clone a Vespa application from GitHub?", "a Vespa application from GitHub?", "Vespa application from GitHub?", "application from GitHub?", "from GitHub?", "GitHub?"], "tokens": ["How", "can", "I", "clone", "a", "Vespa", "application", "from", "GitHub", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1227846853", "fields": {"term": "What does the sample-apps repository contain?", "namespace": "open-p", "hash": 1227846853, "terms": ["What does the sample-apps repository contain?", "does the sample-apps repository contain?", "the sample-apps repository contain?", "sample-apps repository contain?", "repository contain?", "contain?"], "tokens": ["What", "does", "the", "sample", "apps", "repository", "contain", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1119168581", "fields": {"term": "What is the purpose of this tutorial?", "namespace": "open-p", "hash": -1119168581, "terms": ["What is the purpose of this tutorial?", "is the purpose of this tutorial?", "the purpose of this tutorial?", "purpose of this tutorial?", "of this tutorial?", "this tutorial?", "tutorial?"], "tokens": ["What", "is", "the", "purpose", "of", "this", "tutorial", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2051032498", "fields": {"term": "What will be covered in this tutorial?", "namespace": "open-p", "hash": -2051032498, "terms": ["What will be covered in this tutorial?", "will be covered in this tutorial?", "be covered in this tutorial?", "covered in this tutorial?", "in this tutorial?", "this tutorial?", "tutorial?"], "tokens": ["What", "will", "be", "covered", "in", "this", "tutorial", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1597453263", "fields": {"term": "What is a dataset?", "namespace": "open-p", "hash": -1597453263, "terms": ["What is a dataset?", "is a dataset?", "a dataset?", "dataset?"], "tokens": ["What", "is", "a", "dataset", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1386895414", "fields": {"term": "What is included in the sample data?", "namespace": "open-p", "hash": 1386895414, "terms": ["What is included in the sample data?", "is included in the sample data?", "included in the sample data?", "in the sample data?", "the sample data?", "sample data?", "data?"], "tokens": ["What", "is", "included", "in", "the", "sample", "data", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2059565973", "fields": {"term": "Where can I download the full MS MARCO dataset?", "namespace": "open-p", "hash": 2059565973, "terms": ["Where can I download the full MS MARCO dataset?", "can I download the full MS MARCO dataset?", "I download the full MS MARCO dataset?", "download the full MS MARCO dataset?", "the full MS MARCO dataset?", "full MS MARCO dataset?", "MS MARCO dataset?", "MARCO dataset?", "dataset?"], "tokens": ["Where", "can", "I", "download", "the", "full", "MS", "MARCO", "dataset", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1193371561", "fields": {"term": "How much disk space does the full MS MARCO dataset require?", "namespace": "open-p", "hash": -1193371561, "terms": ["How much disk space does the full MS MARCO dataset require?", "much disk space does the full MS MARCO dataset require?", "disk space does the full MS MARCO dataset require?", "space does the full MS MARCO dataset require?", "does the full MS MARCO dataset require?", "the full MS MARCO dataset require?", "full MS MARCO dataset require?", "MS MARCO dataset require?", "MARCO dataset require?", "dataset require?", "require?"], "tokens": ["How", "much", "disk", "space", "does", "the", "full", "MS", "MARCO", "dataset", "require", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1496582941", "fields": {"term": "How do I convert the MS MARCO dataset to Vespa format?", "namespace": "open-p", "hash": 1496582941, "terms": ["How do I convert the MS MARCO dataset to Vespa format?", "do I convert the MS MARCO dataset to Vespa format?", "I convert the MS MARCO dataset to Vespa format?", "convert the MS MARCO dataset to Vespa format?", "the MS MARCO dataset to Vespa format?", "MS MARCO dataset to Vespa format?", "MARCO dataset to Vespa format?", "dataset to Vespa format?", "to Vespa format?", "Vespa format?", "format?"], "tokens": ["How", "do", "I", "convert", "the", "MS", "MARCO", "dataset", "to", "Vespa", "format", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-592643245", "fields": {"term": "What is test-queries.tsv?", "namespace": "open-p", "hash": -592643245, "terms": ["What is test-queries.tsv?", "is test-queries.tsv?", "test-queries.tsv?"], "tokens": ["What", "is", "test", "queries", "tsv", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1188280863", "fields": {"term": "What files are required for basic text search?", "namespace": "open-p", "hash": -1188280863, "terms": ["What files are required for basic text search?", "files are required for basic text search?", "are required for basic text search?", "required for basic text search?", "for basic text search?", "basic text search?", "text search?", "search?"], "tokens": ["What", "files", "are", "required", "for", "basic", "text", "search", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1992237516", "fields": {"term": "How do you create a new Vespa application?", "namespace": "open-p", "hash": -1992237516, "terms": ["How do you create a new Vespa application?", "do you create a new Vespa application?", "you create a new Vespa application?", "create a new Vespa application?", "a new Vespa application?", "new Vespa application?", "Vespa application?", "application?"], "tokens": ["How", "do", "you", "create", "a", "new", "Vespa", "application", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::110403091", "fields": {"term": "What is the purpose of `msmarco.sd` file?", "namespace": "open-p", "hash": 110403091, "terms": ["What is the purpose of `msmarco.sd` file?", "is the purpose of `msmarco.sd` file?", "the purpose of `msmarco.sd` file?", "purpose of `msmarco.sd` file?", "of `msmarco.sd` file?", "`msmarco.sd` file?", "file?"], "tokens": ["What", "is", "the", "purpose", "of", "`", "msmarco", "sd", "`", "file", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1591406997", "fields": {"term": "What is the directory structure for a Vespa application?", "namespace": "open-p", "hash": 1591406997, "terms": ["What is the directory structure for a Vespa application?", "is the directory structure for a Vespa application?", "the directory structure for a Vespa application?", "directory structure for a Vespa application?", "structure for a Vespa application?", "for a Vespa application?", "a Vespa application?", "Vespa application?", "application?"], "tokens": ["What", "is", "the", "directory", "structure", "for", "a", "Vespa", "application", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-365338511", "fields": {"term": "What are the configuration files required for defining behavior of a Vespa system?", "namespace": "open-p", "hash": -365338511, "terms": ["What are the configuration files required for defining behavior of a Vespa system?", "are the configuration files required for defining behavior of a Vespa system?", "the configuration files required for defining behavior of a Vespa system?", "configuration files required for defining behavior of a Vespa system?", "files required for defining behavior of a Vespa system?", "required for defining behavior of a Vespa system?", "for defining behavior of a Vespa system?", "defining behavior of a Vespa system?", "behavior of a Vespa system?", "of a Vespa system?", "a Vespa system?", "Vespa system?", "system?"], "tokens": ["What", "are", "the", "configuration", "files", "required", "for", "defining", "behavior", "of", "a", "Vespa", "system", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1868193950", "fields": {"term": "What is the role of Java plugins in a Vespa application package?", "namespace": "open-p", "hash": 1868193950, "terms": ["What is the role of Java plugins in a Vespa application package?", "is the role of Java plugins in a Vespa application package?", "the role of Java plugins in a Vespa application package?", "role of Java plugins in a Vespa application package?", "of Java plugins in a Vespa application package?", "Java plugins in a Vespa application package?", "plugins in a Vespa application package?", "in a Vespa application package?", "a Vespa application package?", "Vespa application package?", "application package?", "package?"], "tokens": ["What", "is", "the", "role", "of", "Java", "plugins", "in", "a", "Vespa", "application", "package", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1057438476", "fields": {"term": "What is a schema configuration?", "namespace": "open-p", "hash": -1057438476, "terms": ["What is a schema configuration?", "is a schema configuration?", "a schema configuration?", "schema configuration?", "configuration?"], "tokens": ["What", "is", "a", "schema", "configuration", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1425723430", "fields": {"term": "What does the bm25 rank profile do?", "namespace": "open-p", "hash": 1425723430, "terms": ["What does the bm25 rank profile do?", "does the bm25 rank profile do?", "the bm25 rank profile do?", "bm25 rank profile do?", "rank profile do?", "profile do?", "do?"], "tokens": ["What", "does", "the", "bm25", "rank", "profile", "do", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1690390010", "fields": {"term": "What is the nativeRank based on?", "namespace": "open-p", "hash": -1690390010, "terms": ["What is the nativeRank based on?", "is the nativeRank based on?", "the nativeRank based on?", "nativeRank based on?", "based on?", "on?"], "tokens": ["What", "is", "the", "nativeRank", "based", "on", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2059936590", "fields": {"term": "What is defined in the container cluster?", "namespace": "open-p", "hash": 2059936590, "terms": ["What is defined in the container cluster?", "is defined in the container cluster?", "defined in the container cluster?", "in the container cluster?", "the container cluster?", "container cluster?", "cluster?"], "tokens": ["What", "is", "defined", "in", "the", "container", "cluster", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2094195354", "fields": {"term": "What is the default port of endpoint?", "namespace": "open-p", "hash": 2094195354, "terms": ["What is the default port of endpoint?", "is the default port of endpoint?", "the default port of endpoint?", "default port of endpoint?", "port of endpoint?", "of endpoint?", " endpoint?", "endpoint?"], "tokens": ["What", "is", "the", "default", "port", "of", "<", "search", ">", "endpoint", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1933925968", "fields": {"term": "What is RAM requirement?", "namespace": "open-p", "hash": -1933925968, "terms": ["What is RAM requirement?", "is RAM requirement?", "RAM requirement?", "requirement?"], "tokens": ["What", "is", "RAM", "requirement", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::825830450", "fields": {"term": "How to deploy Vespa application?", "namespace": "open-p", "hash": 825830450, "terms": ["How to deploy Vespa application?", "to deploy Vespa application?", "deploy Vespa application?", "Vespa application?", "application?"], "tokens": ["How", "to", "deploy", "Vespa", "application", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1518746451", "fields": {"term": "What is the Java feeding API?", "namespace": "open-p", "hash": -1518746451, "terms": ["What is the Java feeding API?", "is the Java feeding API?", "the Java feeding API?", "Java feeding API?", "feeding API?", "API?"], "tokens": ["What", "is", "the", "Java", "feeding", "API", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1510074905", "fields": {"term": "What is simple query language?", "namespace": "open-p", "hash": 1510074905, "terms": ["What is simple query language?", "is simple query language?", "simple query language?", "query language?", "language?"], "tokens": ["What", "is", "simple", "query", "language", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1089044239", "fields": {"term": "What is the default query type?", "namespace": "open-p", "hash": -1089044239, "terms": ["What is the default query type?", "is the default query type?", "the default query type?", "default query type?", "query type?", "type?"], "tokens": ["What", "is", "the", "default", "query", "type", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::188191591", "fields": {"term": "What is the purpose of Vespa WeakAnd query operator?", "namespace": "open-p", "hash": 188191591, "terms": ["What is the purpose of Vespa WeakAnd query operator?", "is the purpose of Vespa WeakAnd query operator?", "the purpose of Vespa WeakAnd query operator?", "purpose of Vespa WeakAnd query operator?", "of Vespa WeakAnd query operator?", "Vespa WeakAnd query operator?", "WeakAnd query operator?", "query operator?", "operator?"], "tokens": ["What", "is", "the", "purpose", "of", "Vespa", "WeakAnd", "query", "operator", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::29766991", "fields": {"term": "How does 'type=weakAnd' query work?", "namespace": "open-p", "hash": 29766991, "terms": ["How does 'type=weakAnd' query work?", "does 'type=weakAnd' query work?", "'type=weakAnd' query work?", "query work?", "work?"], "tokens": ["How", "does", "'", "type", "=", "weakAnd", "'", "query", "work", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1305043918", "fields": {"term": "What is MRR?", "namespace": "open-p", "hash": 1305043918, "terms": ["What is MRR?", "is MRR?", "MRR?"], "tokens": ["What", "is", "MRR", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-997366983", "fields": {"term": "What is evaluate.py?", "namespace": "open-p", "hash": -997366983, "terms": ["What is evaluate.py?", "is evaluate.py?", "evaluate.py?"], "tokens": ["What", "is", "evaluate", "py", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1578527321", "fields": {"term": "What is the difference between bm25 and nativeRank in MSMARCO?", "namespace": "open-p", "hash": 1578527321, "terms": ["What is the difference between bm25 and nativeRank in MSMARCO?", "is the difference between bm25 and nativeRank in MSMARCO?", "the difference between bm25 and nativeRank in MSMARCO?", "difference between bm25 and nativeRank in MSMARCO?", "between bm25 and nativeRank in MSMARCO?", "bm25 and nativeRank in MSMARCO?", "and nativeRank in MSMARCO?", "nativeRank in MSMARCO?", "in MSMARCO?", "MSMARCO?"], "tokens": ["What", "is", "the", "difference", "between", "bm25", "and", "nativeRank", "in", "MSMARCO", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-456386145", "fields": {"term": "What is the probabilistic relevance framework?", "namespace": "open-p", "hash": -456386145, "terms": ["What is the probabilistic relevance framework?", "is the probabilistic relevance framework?", "the probabilistic relevance framework?", "probabilistic relevance framework?", "relevance framework?", "framework?"], "tokens": ["What", "is", "the", "probabilistic", "relevance", "framework", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-440942480", "fields": {"term": "What is UTF-8?", "namespace": "open-p", "hash": -440942480, "terms": ["What is UTF-8?", "is UTF-8?", "UTF-8?"], "tokens": ["What", "is", "UTF", "8", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::1657079270", "fields": {"term": "What is UTF-16?", "namespace": "open-p", "hash": 1657079270, "terms": ["What is UTF-16?", "is UTF-16?", "UTF-16?"], "tokens": ["What", "is", "UTF", "16", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1820044", "fields": {"term": "How is a code point stored in Java?", "namespace": "open-p", "hash": -1820044, "terms": ["How is a code point stored in Java?", "is a code point stored in Java?", "a code point stored in Java?", "code point stored in Java?", "point stored in Java?", "stored in Java?", "in Java?", "Java?"], "tokens": ["How", "is", "a", "code", "point", "stored", "in", "Java", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1672670088", "fields": {"term": "What is a surrogate pair?", "namespace": "open-p", "hash": 1672670088, "terms": ["What is a surrogate pair?", "is a surrogate pair?", "a surrogate pair?", "surrogate pair?", "pair?"], "tokens": ["What", "is", "a", "surrogate", "pair", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-906213161", "fields": {"term": "How to access code point in Java?", "namespace": "open-p", "hash": -906213161, "terms": ["How to access code point in Java?", "to access code point in Java?", "access code point in Java?", "code point in Java?", "point in Java?", "in Java?", "Java?"], "tokens": ["How", "to", "access", "code", "point", "in", "Java", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::933767899", "fields": {"term": "What are the limitations of UTF-16 encoding?", "namespace": "open-p", "hash": 933767899, "terms": ["What are the limitations of UTF-16 encoding?", "are the limitations of UTF-16 encoding?", "the limitations of UTF-16 encoding?", "limitations of UTF-16 encoding?", "of UTF-16 encoding?", "UTF-16 encoding?", "encoding?"], "tokens": ["What", "are", "the", "limitations", "of", "UTF", "16", "encoding", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1143256846", "fields": {"term": "What is the purpose of com.yahoo.text.Utf8 toolbox?", "namespace": "open-p", "hash": 1143256846, "terms": ["What is the purpose of com.yahoo.text.Utf8 toolbox?", "is the purpose of com.yahoo.text.Utf8 toolbox?", "the purpose of com.yahoo.text.Utf8 toolbox?", "purpose of com.yahoo.text.Utf8 toolbox?", "of com.yahoo.text.Utf8 toolbox?", "com.yahoo.text.Utf8 toolbox?", "toolbox?"], "tokens": ["What", "is", "the", "purpose", "of", "com", "yahoo", "text", "Utf8", "toolbox", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1497532359", "fields": {"term": "What is the use of String.codePoints() method?", "namespace": "open-p", "hash": 1497532359, "terms": ["What is the use of String.codePoints() method?", "is the use of String.codePoints() method?", "the use of String.codePoints() method?", "use of String.codePoints() method?", "of String.codePoints() method?", "String.codePoints() method?", "method?"], "tokens": ["What", "is", "the", "use", "of", "String", "codePoints", "()", "method", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::159747346", "fields": {"term": "What is ISO-8859-1?", "namespace": "open-p", "hash": 159747346, "terms": ["What is ISO-8859-1?", "is ISO-8859-1?", "ISO-8859-1?"], "tokens": ["What", "is", "ISO", "8859", "1", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1550607820", "fields": {"term": "What is Windows 1252?", "namespace": "open-p", "hash": -1550607820, "terms": ["What is Windows 1252?", "is Windows 1252?", "Windows 1252?", "1252?"], "tokens": ["What", "is", "Windows", "1252", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::998606877", "fields": {"term": "What is line noise in-between normal ASCII?", "namespace": "open-p", "hash": 998606877, "terms": ["What is line noise in-between normal ASCII?", "is line noise in-between normal ASCII?", "line noise in-between normal ASCII?", "noise in-between normal ASCII?", "in-between normal ASCII?", "normal ASCII?", "ASCII?"], "tokens": ["What", "is", "line", "noise", "in", "between", "normal", "ASCII", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-24937819", "fields": {"term": "How to debug encoding problems?", "namespace": "open-p", "hash": -24937819, "terms": ["How to debug encoding problems?", "to debug encoding problems?", "debug encoding problems?", "encoding problems?", "problems?"], "tokens": ["How", "to", "debug", "encoding", "problems", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1645448394", "fields": {"term": "Why should you never try to debug encoding problems with a web browser?", "namespace": "open-p", "hash": 1645448394, "terms": ["Why should you never try to debug encoding problems with a web browser?", "should you never try to debug encoding problems with a web browser?", "you never try to debug encoding problems with a web browser?", "never try to debug encoding problems with a web browser?", "try to debug encoding problems with a web browser?", "to debug encoding problems with a web browser?", "debug encoding problems with a web browser?", "encoding problems with a web browser?", "problems with a web browser?", "with a web browser?", "a web browser?", "web browser?", "browser?"], "tokens": ["Why", "should", "you", "never", "try", "to", "debug", "encoding", "problems", "with", "a", "web", "browser", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1618695471", "fields": {"term": "What is stripInvalidCharacters used for?", "namespace": "open-p", "hash": 1618695471, "terms": ["What is stripInvalidCharacters used for?", "is stripInvalidCharacters used for?", "stripInvalidCharacters used for?", "used for?", "for?"], "tokens": ["What", "is", "stripInvalidCharacters", "used", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1466528258", "fields": {"term": "What is proper JSON formatting?", "namespace": "open-p", "hash": -1466528258, "terms": ["What is proper JSON formatting?", "is proper JSON formatting?", "proper JSON formatting?", "JSON formatting?", "formatting?"], "tokens": ["What", "is", "proper", "JSON", "formatting", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1949280860", "fields": {"term": "What are ASCII control characters?", "namespace": "open-p", "hash": -1949280860, "terms": ["What are ASCII control characters?", "are ASCII control characters?", "ASCII control characters?", "control characters?", "characters?"], "tokens": ["What", "are", "ASCII", "control", "characters", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1429543997", "fields": {"term": "What is a task in Vespa?", "namespace": "open-p", "hash": -1429543997, "terms": ["What is a task in Vespa?", "is a task in Vespa?", "a task in Vespa?", "task in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["What", "is", "a", "task", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::615517861", "fields": {"term": "What are automated deployments?", "namespace": "open-p", "hash": 615517861, "terms": ["What are automated deployments?", "are automated deployments?", "automated deployments?", "deployments?"], "tokens": ["What", "are", "automated", "deployments", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1594452725", "fields": {"term": "How to implement system verification?", "namespace": "open-p", "hash": 1594452725, "terms": ["How to implement system verification?", "to implement system verification?", "implement system verification?", "system verification?", "verification?"], "tokens": ["How", "to", "implement", "system", "verification", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::667070879", "fields": {"term": "What are production verification tests?", "namespace": "open-p", "hash": 667070879, "terms": ["What are production verification tests?", "are production verification tests?", "production verification tests?", "verification tests?", "tests?"], "tokens": ["What", "are", "production", "verification", "tests", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::166933437", "fields": {"term": "What is the Application class?", "namespace": "open-p", "hash": 166933437, "terms": ["What is the Application class?", "is the Application class?", "the Application class?", "Application class?", "class?"], "tokens": ["What", "is", "the", "Application", "class", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1551134344", "fields": {"term": "How is JDisc accessed?", "namespace": "open-p", "hash": -1551134344, "terms": ["How is JDisc accessed?", "is JDisc accessed?", "JDisc accessed?", "accessed?"], "tokens": ["How", "is", "JDisc", "accessed", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-718853219", "fields": {"term": "What is MetalSearcherTest.java?", "namespace": "open-p", "hash": -718853219, "terms": ["What is MetalSearcherTest.java?", "is MetalSearcherTest.java?", "MetalSearcherTest.java?"], "tokens": ["What", "is", "MetalSearcherTest", "java", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1527911448", "fields": {"term": "How do you disable the network layer?", "namespace": "open-p", "hash": 1527911448, "terms": ["How do you disable the network layer?", "do you disable the network layer?", "you disable the network layer?", "disable the network layer?", "the network layer?", "network layer?", "layer?"], "tokens": ["How", "do", "you", "disable", "the", "network", "layer", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::298132176", "fields": {"term": "What is a builder in Vespa?", "namespace": "open-p", "hash": 298132176, "terms": ["What is a builder in Vespa?", "is a builder in Vespa?", "a builder in Vespa?", "builder in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["What", "is", "a", "builder", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1923348220", "fields": {"term": "How to programmatically instantiate configurations?", "namespace": "open-p", "hash": 1923348220, "terms": ["How to programmatically instantiate configurations?", "to programmatically instantiate configurations?", "programmatically instantiate configurations?", "instantiate configurations?", "configurations?"], "tokens": ["How", "to", "programmatically", "instantiate", "configurations", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-532812568", "fields": {"term": "How can builders help with unit testing?", "namespace": "open-p", "hash": -532812568, "terms": ["How can builders help with unit testing?", "can builders help with unit testing?", "builders help with unit testing?", "help with unit testing?", "with unit testing?", "unit testing?", "testing?"], "tokens": ["How", "can", "builders", "help", "with", "unit", "testing", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1699402414", "fields": {"term": "What is the DemoConfig class?", "namespace": "open-p", "hash": 1699402414, "terms": ["What is the DemoConfig class?", "is the DemoConfig class?", "the DemoConfig class?", "DemoConfig class?", "class?"], "tokens": ["What", "is", "the", "DemoConfig", "class", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1394239434", "fields": {"term": "What does toplevel define?", "namespace": "open-p", "hash": -1394239434, "terms": ["What does toplevel define?", "does toplevel define?", "toplevel define?", "define?"], "tokens": ["What", "does", "toplevel", "define", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1039078420", "fields": {"term": "What is simplenumber?", "namespace": "open-p", "hash": -1039078420, "terms": ["What is simplenumber?", "is simplenumber?", "simplenumber?"], "tokens": ["What", "is", "simplenumber", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1029646975", "fields": {"term": "What is in simplevaluearray?", "namespace": "open-p", "hash": 1029646975, "terms": ["What is in simplevaluearray?", "is in simplevaluearray?", "in simplevaluearray?", "simplevaluearray?"], "tokens": ["What", "is", "in", "simplevaluearray", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-17255255", "fields": {"term": "What is the coordinate structure?", "namespace": "open-p", "hash": -17255255, "terms": ["What is the coordinate structure?", "is the coordinate structure?", "the coordinate structure?", "coordinate structure?", "structure?"], "tokens": ["What", "is", "the", "coordinate", "structure", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1636573700", "fields": {"term": "What is coordinate.x?", "namespace": "open-p", "hash": 1636573700, "terms": ["What is coordinate.x?", "is coordinate.x?", "coordinate.x?"], "tokens": ["What", "is", "coordinate", "x", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2139749826", "fields": {"term": "What is coordinate.y?", "namespace": "open-p", "hash": -2139749826, "terms": ["What is coordinate.y?", "is coordinate.y?", "coordinate.y?"], "tokens": ["What", "is", "coordinate", "y", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::610406893", "fields": {"term": "What is the purpose of DemoConfig?", "namespace": "open-p", "hash": 610406893, "terms": ["What is the purpose of DemoConfig?", "is the purpose of DemoConfig?", "the purpose of DemoConfig?", "purpose of DemoConfig?", "of DemoConfig?", "DemoConfig?"], "tokens": ["What", "is", "the", "purpose", "of", "DemoConfig", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::517616795", "fields": {"term": "What is the coordinate builder?", "namespace": "open-p", "hash": 517616795, "terms": ["What is the coordinate builder?", "is the coordinate builder?", "the coordinate builder?", "coordinate builder?", "builder?"], "tokens": ["What", "is", "the", "coordinate", "builder", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1426846710", "fields": {"term": "How many builder classes are used?", "namespace": "open-p", "hash": 1426846710, "terms": ["How many builder classes are used?", "many builder classes are used?", "builder classes are used?", "classes are used?", "are used?", "used?"], "tokens": ["How", "many", "builder", "classes", "are", "used", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-400499234", "fields": {"term": "What is a nested class?", "namespace": "open-p", "hash": -400499234, "terms": ["What is a nested class?", "is a nested class?", "a nested class?", "nested class?", "class?"], "tokens": ["What", "is", "a", "nested", "class", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1716882084", "fields": {"term": "What is the purpose of DemoTest?", "namespace": "open-p", "hash": 1716882084, "terms": ["What is the purpose of DemoTest?", "is the purpose of DemoTest?", "the purpose of DemoTest?", "purpose of DemoTest?", "of DemoTest?", "DemoTest?"], "tokens": ["What", "is", "the", "purpose", "of", "DemoTest", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1482022098", "fields": {"term": "What is the simplename setter for?", "namespace": "open-p", "hash": -1482022098, "terms": ["What is the simplename setter for?", "is the simplename setter for?", "the simplename setter for?", "simplename setter for?", "setter for?", "for?"], "tokens": ["What", "is", "the", "simplename", "setter", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-985182662", "fields": {"term": "What is the simplenumber setter for?", "namespace": "open-p", "hash": -985182662, "terms": ["What is the simplenumber setter for?", "is the simplenumber setter for?", "the simplenumber setter for?", "simplenumber setter for?", "setter for?", "for?"], "tokens": ["What", "is", "the", "simplenumber", "setter", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2036119933", "fields": {"term": "How are configuration arrays built?", "namespace": "open-p", "hash": 2036119933, "terms": ["How are configuration arrays built?", "are configuration arrays built?", "configuration arrays built?", "arrays built?", "built?"], "tokens": ["How", "are", "configuration", "arrays", "built", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::33527693", "fields": {"term": "What is the open-domain QA use case?", "namespace": "open-p", "hash": 33527693, "terms": ["What is the open-domain QA use case?", "is the open-domain QA use case?", "the open-domain QA use case?", "open-domain QA use case?", "QA use case?", "use case?", "case?"], "tokens": ["What", "is", "the", "open", "domain", "QA", "use", "case", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-225354442", "fields": {"term": "What is Dense Passage Retriever?", "namespace": "open-p", "hash": -225354442, "terms": ["What is Dense Passage Retriever?", "is Dense Passage Retriever?", "Dense Passage Retriever?", "Passage Retriever?", "Retriever?"], "tokens": ["What", "is", "Dense", "Passage", "Retriever", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1117852036", "fields": {"term": "How does the application retrieve passages?", "namespace": "open-p", "hash": 1117852036, "terms": ["How does the application retrieve passages?", "does the application retrieve passages?", "the application retrieve passages?", "application retrieve passages?", "retrieve passages?", "passages?"], "tokens": ["How", "does", "the", "application", "retrieve", "passages", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2123265731", "fields": {"term": "What is the BERT model used for?", "namespace": "open-p", "hash": -2123265731, "terms": ["What is the BERT model used for?", "is the BERT model used for?", "the BERT model used for?", "BERT model used for?", "model used for?", "used for?", "for?"], "tokens": ["What", "is", "the", "BERT", "model", "used", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1550484748", "fields": {"term": "What is the Natural Questions dataset?", "namespace": "open-p", "hash": 1550484748, "terms": ["What is the Natural Questions dataset?", "is the Natural Questions dataset?", "the Natural Questions dataset?", "Natural Questions dataset?", "Questions dataset?", "dataset?"], "tokens": ["What", "is", "the", "Natural", "Questions", "dataset", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::569889068", "fields": {"term": "What kind of questions can be asked with the deployed application?", "namespace": "open-p", "hash": 569889068, "terms": ["What kind of questions can be asked with the deployed application?", "kind of questions can be asked with the deployed application?", "of questions can be asked with the deployed application?", "questions can be asked with the deployed application?", "can be asked with the deployed application?", "be asked with the deployed application?", "asked with the deployed application?", "with the deployed application?", "the deployed application?", "deployed application?", "application?"], "tokens": ["What", "kind", "of", "questions", "can", "be", "asked", "with", "the", "deployed", "application", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1855043085", "fields": {"term": "What is multi-threaded ranking?", "namespace": "open-p", "hash": 1855043085, "terms": ["What is multi-threaded ranking?", "is multi-threaded ranking?", "multi-threaded ranking?", "ranking?"], "tokens": ["What", "is", "multi", "threaded", "ranking", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::950823881", "fields": {"term": "What is multi-phased ranking?", "namespace": "open-p", "hash": 950823881, "terms": ["What is multi-phased ranking?", "is multi-phased ranking?", "multi-phased ranking?", "ranking?"], "tokens": ["What", "is", "multi", "phased", "ranking", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1401898148", "fields": {"term": "What are Transformer models used for?", "namespace": "open-p", "hash": -1401898148, "terms": ["What are Transformer models used for?", "are Transformer models used for?", "Transformer models used for?", "models used for?", "used for?", "for?"], "tokens": ["What", "are", "Transformer", "models", "used", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1857891711", "fields": {"term": "What is the e-commerce use case?", "namespace": "open-p", "hash": -1857891711, "terms": ["What is the e-commerce use case?", "is the e-commerce use case?", "the e-commerce use case?", "e-commerce use case?", "use case?", "case?"], "tokens": ["What", "is", "the", "e", "commerce", "use", "case", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::279700149", "fields": {"term": "What is the shopping sample app about?", "namespace": "open-p", "hash": 279700149, "terms": ["What is the shopping sample app about?", "is the shopping sample app about?", "the shopping sample app about?", "shopping sample app about?", "sample app about?", "app about?", "about?"], "tokens": ["What", "is", "the", "shopping", "sample", "app", "about", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1443829333", "fields": {"term": "Where do I navigate to browse the application?", "namespace": "open-p", "hash": -1443829333, "terms": ["Where do I navigate to browse the application?", "do I navigate to browse the application?", "I navigate to browse the application?", "navigate to browse the application?", "to browse the application?", "browse the application?", "the application?", "application?"], "tokens": ["Where", "do", "I", "navigate", "to", "browse", "the", "application", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::460443160", "fields": {"term": "What is the Amazon product data set?", "namespace": "open-p", "hash": 460443160, "terms": ["What is the Amazon product data set?", "is the Amazon product data set?", "the Amazon product data set?", "Amazon product data set?", "product data set?", "data set?", "set?"], "tokens": ["What", "is", "the", "Amazon", "product", "data", "set", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1970078428", "fields": {"term": "How do I convert data to Vespa format?", "namespace": "open-p", "hash": 1970078428, "terms": ["How do I convert data to Vespa format?", "do I convert data to Vespa format?", "I convert data to Vespa format?", "convert data to Vespa format?", "data to Vespa format?", "to Vespa format?", "Vespa format?", "format?"], "tokens": ["How", "do", "I", "convert", "data", "to", "Vespa", "format", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1775055263", "fields": {"term": "How is the review rating of an item stored?", "namespace": "open-p", "hash": -1775055263, "terms": ["How is the review rating of an item stored?", "is the review rating of an item stored?", "the review rating of an item stored?", "review rating of an item stored?", "rating of an item stored?", "of an item stored?", "an item stored?", "item stored?", "stored?"], "tokens": ["How", "is", "the", "review", "rating", "of", "an", "item", "stored", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-822691092", "fields": {"term": "What is the recommended way to handle reviews?", "namespace": "open-p", "hash": -822691092, "terms": ["What is the recommended way to handle reviews?", "is the recommended way to handle reviews?", "the recommended way to handle reviews?", "recommended way to handle reviews?", "way to handle reviews?", "to handle reviews?", "handle reviews?", "reviews?"], "tokens": ["What", "is", "the", "recommended", "way", "to", "handle", "reviews", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1448679505", "fields": {"term": "What is the text-image use case?", "namespace": "open-p", "hash": -1448679505, "terms": ["What is the text-image use case?", "is the text-image use case?", "the text-image use case?", "text-image use case?", "use case?", "case?"], "tokens": ["What", "is", "the", "text", "image", "use", "case", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::263402578", "fields": {"term": "What does CLIP enable?", "namespace": "open-p", "hash": 263402578, "terms": ["What does CLIP enable?", "does CLIP enable?", "CLIP enable?", "enable?"], "tokens": ["What", "does", "CLIP", "enable", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::134801703", "fields": {"term": "What are the two ways to use the sample application?", "namespace": "open-p", "hash": 134801703, "terms": ["What are the two ways to use the sample application?", "are the two ways to use the sample application?", "the two ways to use the sample application?", "two ways to use the sample application?", "ways to use the sample application?", "to use the sample application?", "use the sample application?", "the sample application?", "sample application?", "application?"], "tokens": ["What", "are", "the", "two", "ways", "to", "use", "the", "sample", "application", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1555682480", "fields": {"term": "What is PyVespa?", "namespace": "open-p", "hash": -1555682480, "terms": ["What is PyVespa?", "is PyVespa?", "PyVespa?"], "tokens": ["What", "is", "PyVespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-684322663", "fields": {"term": "What is the WAND algorithm?", "namespace": "open-p", "hash": -684322663, "terms": ["What is the WAND algorithm?", "is the WAND algorithm?", "the WAND algorithm?", "WAND algorithm?", "algorithm?"], "tokens": ["What", "is", "the", "WAND", "algorithm", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::815025203", "fields": {"term": "What are the benefits of WAND?", "namespace": "open-p", "hash": 815025203, "terms": ["What are the benefits of WAND?", "are the benefits of WAND?", "the benefits of WAND?", "benefits of WAND?", "of WAND?", "WAND?"], "tokens": ["What", "are", "the", "benefits", "of", "WAND", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1281184169", "fields": {"term": "When should WAND be used?", "namespace": "open-p", "hash": -1281184169, "terms": ["When should WAND be used?", "should WAND be used?", "WAND be used?", "be used?", "used?"], "tokens": ["When", "should", "WAND", "be", "used", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1596919494", "fields": {"term": "What are the differences between weakAnd and wand operators?", "namespace": "open-p", "hash": -1596919494, "terms": ["What are the differences between weakAnd and wand operators?", "are the differences between weakAnd and wand operators?", "the differences between weakAnd and wand operators?", "differences between weakAnd and wand operators?", "between weakAnd and wand operators?", "weakAnd and wand operators?", "and wand operators?", "wand operators?", "operators?"], "tokens": ["What", "are", "the", "differences", "between", "weakAnd", "and", "wand", "operators", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1026154398", "fields": {"term": "Does wand operator integrate with linguistic processing?", "namespace": "open-p", "hash": 1026154398, "terms": ["Does wand operator integrate with linguistic processing?", "wand operator integrate with linguistic processing?", "operator integrate with linguistic processing?", "integrate with linguistic processing?", "with linguistic processing?", "linguistic processing?", "processing?"], "tokens": ["Does", "wand", "operator", "integrate", "with", "linguistic", "processing", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-174034342", "fields": {"term": "How can WAND be evaluated?", "namespace": "open-p", "hash": -174034342, "terms": ["How can WAND be evaluated?", "can WAND be evaluated?", "WAND be evaluated?", "be evaluated?", "evaluated?"], "tokens": ["How", "can", "WAND", "be", "evaluated", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::689236877", "fields": {"term": "What is the weakAnd operator?", "namespace": "open-p", "hash": 689236877, "terms": ["What is the weakAnd operator?", "is the weakAnd operator?", "the weakAnd operator?", "weakAnd operator?", "operator?"], "tokens": ["What", "is", "the", "weakAnd", "operator", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1842913726", "fields": {"term": "What fields can weakAnd retrieve?", "namespace": "open-p", "hash": 1842913726, "terms": ["What fields can weakAnd retrieve?", "fields can weakAnd retrieve?", "can weakAnd retrieve?", "weakAnd retrieve?", "retrieve?"], "tokens": ["What", "fields", "can", "weakAnd", "retrieve", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1656879276", "fields": {"term": "How can you specify targetHits?", "namespace": "open-p", "hash": -1656879276, "terms": ["How can you specify targetHits?", "can you specify targetHits?", "you specify targetHits?", "specify targetHits?", "targetHits?"], "tokens": ["How", "can", "you", "specify", "targetHits", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-458772243", "fields": {"term": "What is the effect of tuning targetHits?", "namespace": "open-p", "hash": -458772243, "terms": ["What is the effect of tuning targetHits?", "is the effect of tuning targetHits?", "the effect of tuning targetHits?", "effect of tuning targetHits?", "of tuning targetHits?", "tuning targetHits?", "targetHits?"], "tokens": ["What", "is", "the", "effect", "of", "tuning", "targetHits", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1523784359", "fields": {"term": "What are some ranking features that correlate well with weakAnd scoring?", "namespace": "open-p", "hash": 1523784359, "terms": ["What are some ranking features that correlate well with weakAnd scoring?", "are some ranking features that correlate well with weakAnd scoring?", "some ranking features that correlate well with weakAnd scoring?", "ranking features that correlate well with weakAnd scoring?", "features that correlate well with weakAnd scoring?", "that correlate well with weakAnd scoring?", "correlate well with weakAnd scoring?", "well with weakAnd scoring?", "with weakAnd scoring?", "weakAnd scoring?", "scoring?"], "tokens": ["What", "are", "some", "ranking", "features", "that", "correlate", "well", "with", "weakAnd", "scoring", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::4989897", "fields": {"term": "What should you do to get the best hits possible with weakAnd?", "namespace": "open-p", "hash": 4989897, "terms": ["What should you do to get the best hits possible with weakAnd?", "should you do to get the best hits possible with weakAnd?", "you do to get the best hits possible with weakAnd?", "do to get the best hits possible with weakAnd?", "to get the best hits possible with weakAnd?", "get the best hits possible with weakAnd?", "the best hits possible with weakAnd?", "best hits possible with weakAnd?", "hits possible with weakAnd?", "possible with weakAnd?", "with weakAnd?", "weakAnd?"], "tokens": ["What", "should", "you", "do", "to", "get", "the", "best", "hits", "possible", "with", "weakAnd", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1758015073", "fields": {"term": "What can cause weakAnd queries to become slower?", "namespace": "open-p", "hash": 1758015073, "terms": ["What can cause weakAnd queries to become slower?", "can cause weakAnd queries to become slower?", "cause weakAnd queries to become slower?", "weakAnd queries to become slower?", "queries to become slower?", "to become slower?", "become slower?", "slower?"], "tokens": ["What", "can", "cause", "weakAnd", "queries", "to", "become", "slower", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::738409783", "fields": {"term": "What is the query example expressed in YQL?", "namespace": "open-p", "hash": 738409783, "terms": ["What is the query example expressed in YQL?", "is the query example expressed in YQL?", "the query example expressed in YQL?", "query example expressed in YQL?", "example expressed in YQL?", "expressed in YQL?", "in YQL?", "YQL?"], "tokens": ["What", "is", "the", "query", "example", "expressed", "in", "YQL", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::570793923", "fields": {"term": "What is the weakAnd query operator?", "namespace": "open-p", "hash": 570793923, "terms": ["What is the weakAnd query operator?", "is the weakAnd query operator?", "the weakAnd query operator?", "weakAnd query operator?", "query operator?", "operator?"], "tokens": ["What", "is", "the", "weakAnd", "query", "operator", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::70824084", "fields": {"term": "What ranking features does weakAnd use?", "namespace": "open-p", "hash": 70824084, "terms": ["What ranking features does weakAnd use?", "ranking features does weakAnd use?", "features does weakAnd use?", "does weakAnd use?", "weakAnd use?", "use?"], "tokens": ["What", "ranking", "features", "does", "weakAnd", "use", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::507130039", "fields": {"term": "How is term significance calculated?", "namespace": "open-p", "hash": 507130039, "terms": ["How is term significance calculated?", "is term significance calculated?", "term significance calculated?", "significance calculated?", "calculated?"], "tokens": ["How", "is", "term", "significance", "calculated", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1879993199", "fields": {"term": "What is the purpose of weakAnd.replace?", "namespace": "open-p", "hash": -1879993199, "terms": ["What is the purpose of weakAnd.replace?", "is the purpose of weakAnd.replace?", "the purpose of weakAnd.replace?", "purpose of weakAnd.replace?", "of weakAnd.replace?", "weakAnd.replace?"], "tokens": ["What", "is", "the", "purpose", "of", "weakAnd", "replace", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1663831778", "fields": {"term": "How does weakAnd handle documents that can't compete with top hits?", "namespace": "open-p", "hash": 1663831778, "terms": ["How does weakAnd handle documents that can't compete with top hits?", "does weakAnd handle documents that can't compete with top hits?", "weakAnd handle documents that can't compete with top hits?", "handle documents that can't compete with top hits?", "documents that can't compete with top hits?", "that can't compete with top hits?", "can't compete with top hits?", "compete with top hits?", "with top hits?", "top hits?", "hits?"], "tokens": ["How", "does", "weakAnd", "handle", "documents", "that", "can", "'", "t", "compete", "with", "top", "hits", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1826886953", "fields": {"term": "Does weakAnd use the number of times a term occurs in a document for inner scoring?", "namespace": "open-p", "hash": 1826886953, "terms": ["Does weakAnd use the number of times a term occurs in a document for inner scoring?", "weakAnd use the number of times a term occurs in a document for inner scoring?", "use the number of times a term occurs in a document for inner scoring?", "the number of times a term occurs in a document for inner scoring?", "number of times a term occurs in a document for inner scoring?", "of times a term occurs in a document for inner scoring?", "times a term occurs in a document for inner scoring?", "a term occurs in a document for inner scoring?", "term occurs in a document for inner scoring?", "occurs in a document for inner scoring?", "in a document for inner scoring?", "a document for inner scoring?", "document for inner scoring?", "for inner scoring?", "inner scoring?", "scoring?"], "tokens": ["Does", "weakAnd", "use", "the", "number", "of", "times", "a", "term", "occurs", "in", "a", "document", "for", "inner", "scoring", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1132024344", "fields": {"term": "Can term significance and weight be overridden in the query using annotations?", "namespace": "open-p", "hash": -1132024344, "terms": ["Can term significance and weight be overridden in the query using annotations?", "term significance and weight be overridden in the query using annotations?", "significance and weight be overridden in the query using annotations?", "and weight be overridden in the query using annotations?", "weight be overridden in the query using annotations?", "be overridden in the query using annotations?", "overridden in the query using annotations?", "in the query using annotations?", "the query using annotations?", "query using annotations?", "using annotations?", "annotations?"], "tokens": ["Can", "term", "significance", "and", "weight", "be", "overridden", "in", "the", "query", "using", "annotations", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1456944387", "fields": {"term": "Does weakAnd expose all evaluated hits or just the top k hits?", "namespace": "open-p", "hash": 1456944387, "terms": ["Does weakAnd expose all evaluated hits or just the top k hits?", "weakAnd expose all evaluated hits or just the top k hits?", "expose all evaluated hits or just the top k hits?", "all evaluated hits or just the top k hits?", "evaluated hits or just the top k hits?", "hits or just the top k hits?", "or just the top k hits?", "just the top k hits?", "the top k hits?", "top k hits?", "k hits?", "hits?"], "tokens": ["Does", "weakAnd", "expose", "all", "evaluated", "hits", "or", "just", "the", "top", "k", "hits", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::886123997", "fields": {"term": "What types are supported by wand?", "namespace": "open-p", "hash": 886123997, "terms": ["What types are supported by wand?", "types are supported by wand?", "are supported by wand?", "supported by wand?", "by wand?", "wand?"], "tokens": ["What", "types", "are", "supported", "by", "wand", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-734026700", "fields": {"term": "What is deep_bm25 ranking profile?", "namespace": "open-p", "hash": -734026700, "terms": ["What is deep_bm25 ranking profile?", "is deep_bm25 ranking profile?", "deep_bm25 ranking profile?", "ranking profile?", "profile?"], "tokens": ["What", "is", "deep_bm25", "ranking", "profile", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::643237807", "fields": {"term": "What is a rawScore?", "namespace": "open-p", "hash": 643237807, "terms": ["What is a rawScore?", "is a rawScore?", "a rawScore?", "rawScore?"], "tokens": ["What", "is", "a", "rawScore", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1143624512", "fields": {"term": "How is bm25 calculated for wand hits?", "namespace": "open-p", "hash": 1143624512, "terms": ["How is bm25 calculated for wand hits?", "is bm25 calculated for wand hits?", "bm25 calculated for wand hits?", "calculated for wand hits?", "for wand hits?", "wand hits?", "hits?"], "tokens": ["How", "is", "bm25", "calculated", "for", "wand", "hits", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1343224754", "fields": {"term": "What does Vespa do with ZooKeeper?", "namespace": "open-p", "hash": 1343224754, "terms": ["What does Vespa do with ZooKeeper?", "does Vespa do with ZooKeeper?", "Vespa do with ZooKeeper?", "do with ZooKeeper?", "with ZooKeeper?", "ZooKeeper?"], "tokens": ["What", "does", "Vespa", "do", "with", "ZooKeeper", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::942897753", "fields": {"term": "What is a ZooKeeper ensemble?", "namespace": "open-p", "hash": 942897753, "terms": ["What is a ZooKeeper ensemble?", "is a ZooKeeper ensemble?", "a ZooKeeper ensemble?", "ZooKeeper ensemble?", "ensemble?"], "tokens": ["What", "is", "a", "ZooKeeper", "ensemble", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1293601021", "fields": {"term": "How does Vespa reconfigure ZooKeeper members?", "namespace": "open-p", "hash": 1293601021, "terms": ["How does Vespa reconfigure ZooKeeper members?", "does Vespa reconfigure ZooKeeper members?", "Vespa reconfigure ZooKeeper members?", "reconfigure ZooKeeper members?", "ZooKeeper members?", "members?"], "tokens": ["How", "does", "Vespa", "reconfigure", "ZooKeeper", "members", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1597769286", "fields": {"term": "What is the optimal node limit for ZooKeeper clusters?", "namespace": "open-p", "hash": 1597769286, "terms": ["What is the optimal node limit for ZooKeeper clusters?", "is the optimal node limit for ZooKeeper clusters?", "the optimal node limit for ZooKeeper clusters?", "optimal node limit for ZooKeeper clusters?", "node limit for ZooKeeper clusters?", "limit for ZooKeeper clusters?", "for ZooKeeper clusters?", "ZooKeeper clusters?", "clusters?"], "tokens": ["What", "is", "the", "optimal", "node", "limit", "for", "ZooKeeper", "clusters", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-369602666", "fields": {"term": "What happens to application packages that violate the node count?", "namespace": "open-p", "hash": -369602666, "terms": ["What happens to application packages that violate the node count?", "happens to application packages that violate the node count?", "to application packages that violate the node count?", "application packages that violate the node count?", "packages that violate the node count?", "that violate the node count?", "violate the node count?", "the node count?", "node count?", "count?"], "tokens": ["What", "happens", "to", "application", "packages", "that", "violate", "the", "node", "count", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::147440329", "fields": {"term": "What are the valid node counts for ZooKeeper clusters?", "namespace": "open-p", "hash": 147440329, "terms": ["What are the valid node counts for ZooKeeper clusters?", "are the valid node counts for ZooKeeper clusters?", "the valid node counts for ZooKeeper clusters?", "valid node counts for ZooKeeper clusters?", "node counts for ZooKeeper clusters?", "counts for ZooKeeper clusters?", "for ZooKeeper clusters?", "ZooKeeper clusters?", "clusters?"], "tokens": ["What", "are", "the", "valid", "node", "counts", "for", "ZooKeeper", "clusters", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::137835938", "fields": {"term": "Where can I find more information about node counts for ZooKeeper clusters?", "namespace": "open-p", "hash": 137835938, "terms": ["Where can I find more information about node counts for ZooKeeper clusters?", "can I find more information about node counts for ZooKeeper clusters?", "I find more information about node counts for ZooKeeper clusters?", "find more information about node counts for ZooKeeper clusters?", "more information about node counts for ZooKeeper clusters?", "information about node counts for ZooKeeper clusters?", "about node counts for ZooKeeper clusters?", "node counts for ZooKeeper clusters?", "counts for ZooKeeper clusters?", "for ZooKeeper clusters?", "ZooKeeper clusters?", "clusters?"], "tokens": ["Where", "can", "I", "find", "more", "information", "about", "node", "counts", "for", "ZooKeeper", "clusters", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1316374870", "fields": {"term": "How do I enable ZooKeeper?", "namespace": "open-p", "hash": -1316374870, "terms": ["How do I enable ZooKeeper?", "do I enable ZooKeeper?", "I enable ZooKeeper?", "enable ZooKeeper?", "ZooKeeper?"], "tokens": ["How", "do", "I", "enable", "ZooKeeper", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::748042496", "fields": {"term": "What is zkfacade?", "namespace": "open-p", "hash": 748042496, "terms": ["What is zkfacade?", "is zkfacade?", "zkfacade?"], "tokens": ["What", "is", "zkfacade", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::740611516", "fields": {"term": "What is VespaCurator?", "namespace": "open-p", "hash": 740611516, "terms": ["What is VespaCurator?", "is VespaCurator?", "VespaCurator?"], "tokens": ["What", "is", "VespaCurator", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-376150239", "fields": {"term": "How to use VespaCurator in ZooKeeper?", "namespace": "open-p", "hash": -376150239, "terms": ["How to use VespaCurator in ZooKeeper?", "to use VespaCurator in ZooKeeper?", "use VespaCurator in ZooKeeper?", "VespaCurator in ZooKeeper?", "in ZooKeeper?", "ZooKeeper?"], "tokens": ["How", "to", "use", "VespaCurator", "in", "ZooKeeper", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1002451129", "fields": {"term": "What is an example of using VespaCurator in a request handler?", "namespace": "open-p", "hash": 1002451129, "terms": ["What is an example of using VespaCurator in a request handler?", "is an example of using VespaCurator in a request handler?", "an example of using VespaCurator in a request handler?", "example of using VespaCurator in a request handler?", "of using VespaCurator in a request handler?", "using VespaCurator in a request handler?", "VespaCurator in a request handler?", "in a request handler?", "a request handler?", "request handler?", "handler?"], "tokens": ["What", "is", "an", "example", "of", "using", "VespaCurator", "in", "a", "request", "handler", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-997568338", "fields": {"term": "What is Valgrind?", "namespace": "open-p", "hash": -997568338, "terms": ["What is Valgrind?", "is Valgrind?", "Valgrind?"], "tokens": ["What", "is", "Valgrind", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::364919336", "fields": {"term": "How to run Vespa processes with valgrind?", "namespace": "open-p", "hash": 364919336, "terms": ["How to run Vespa processes with valgrind?", "to run Vespa processes with valgrind?", "run Vespa processes with valgrind?", "Vespa processes with valgrind?", "processes with valgrind?", "with valgrind?", "valgrind?"], "tokens": ["How", "to", "run", "Vespa", "processes", "with", "valgrind", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1715050789", "fields": {"term": "Why is Valgrind useful?", "namespace": "open-p", "hash": 1715050789, "terms": ["Why is Valgrind useful?", "is Valgrind useful?", "Valgrind useful?", "useful?"], "tokens": ["Why", "is", "Valgrind", "useful", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::31134578", "fields": {"term": "How do you install valgrind?", "namespace": "open-p", "hash": 31134578, "terms": ["How do you install valgrind?", "do you install valgrind?", "you install valgrind?", "install valgrind?", "valgrind?"], "tokens": ["How", "do", "you", "install", "valgrind", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1430076146", "fields": {"term": "What is the general use of valgrind?", "namespace": "open-p", "hash": 1430076146, "terms": ["What is the general use of valgrind?", "is the general use of valgrind?", "the general use of valgrind?", "general use of valgrind?", "use of valgrind?", "of valgrind?", "valgrind?"], "tokens": ["What", "is", "the", "general", "use", "of", "valgrind", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1826957716", "fields": {"term": "What is world writeable in Vespa?", "namespace": "open-p", "hash": 1826957716, "terms": ["What is world writeable in Vespa?", "is world writeable in Vespa?", "world writeable in Vespa?", "writeable in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["What", "is", "world", "writeable", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1010034344", "fields": {"term": "How do you use valgrind to show a detailed profiling graph?", "namespace": "open-p", "hash": 1010034344, "terms": ["How do you use valgrind to show a detailed profiling graph?", "do you use valgrind to show a detailed profiling graph?", "you use valgrind to show a detailed profiling graph?", "use valgrind to show a detailed profiling graph?", "valgrind to show a detailed profiling graph?", "to show a detailed profiling graph?", "show a detailed profiling graph?", "a detailed profiling graph?", "detailed profiling graph?", "profiling graph?", "graph?"], "tokens": ["How", "do", "you", "use", "valgrind", "to", "show", "a", "detailed", "profiling", "graph", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2028684253", "fields": {"term": "What should you do after a profiling session is completed?", "namespace": "open-p", "hash": 2028684253, "terms": ["What should you do after a profiling session is completed?", "should you do after a profiling session is completed?", "you do after a profiling session is completed?", "do after a profiling session is completed?", "after a profiling session is completed?", "a profiling session is completed?", "profiling session is completed?", "session is completed?", "is completed?", "completed?"], "tokens": ["What", "should", "you", "do", "after", "a", "profiling", "session", "is", "completed", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2061921251", "fields": {"term": "What tool should be installed to view valgrind results?", "namespace": "open-p", "hash": 2061921251, "terms": ["What tool should be installed to view valgrind results?", "tool should be installed to view valgrind results?", "should be installed to view valgrind results?", "be installed to view valgrind results?", "installed to view valgrind results?", "to view valgrind results?", "view valgrind results?", "valgrind results?", "results?"], "tokens": ["What", "tool", "should", "be", "installed", "to", "view", "valgrind", "results", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::566862644", "fields": {"term": "How do you specify the path to code in kcachegrind?", "namespace": "open-p", "hash": 566862644, "terms": ["How do you specify the path to code in kcachegrind?", "do you specify the path to code in kcachegrind?", "you specify the path to code in kcachegrind?", "specify the path to code in kcachegrind?", "the path to code in kcachegrind?", "path to code in kcachegrind?", "to code in kcachegrind?", "code in kcachegrind?", "in kcachegrind?", "kcachegrind?"], "tokens": ["How", "do", "you", "specify", "the", "path", "to", "code", "in", "kcachegrind", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-962498891", "fields": {"term": "How to start Vespa with valgrind?", "namespace": "open-p", "hash": -962498891, "terms": ["How to start Vespa with valgrind?", "to start Vespa with valgrind?", "start Vespa with valgrind?", "Vespa with valgrind?", "with valgrind?", "valgrind?"], "tokens": ["How", "to", "start", "Vespa", "with", "valgrind", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::152053523", "fields": {"term": "How to check for memory errors?", "namespace": "open-p", "hash": 152053523, "terms": ["How to check for memory errors?", "to check for memory errors?", "check for memory errors?", "for memory errors?", "memory errors?", "errors?"], "tokens": ["How", "to", "check", "for", "memory", "errors", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-374469136", "fields": {"term": "Where are the Valgrind logs located?", "namespace": "open-p", "hash": -374469136, "terms": ["Where are the Valgrind logs located?", "are the Valgrind logs located?", "the Valgrind logs located?", "Valgrind logs located?", "logs located?", "located?"], "tokens": ["Where", "are", "the", "Valgrind", "logs", "located", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-987058877", "fields": {"term": "What is the purpose of validation-overrides.xml?", "namespace": "open-p", "hash": -987058877, "terms": ["What is the purpose of validation-overrides.xml?", "is the purpose of validation-overrides.xml?", "the purpose of validation-overrides.xml?", "purpose of validation-overrides.xml?", "of validation-overrides.xml?", "validation-overrides.xml?"], "tokens": ["What", "is", "the", "purpose", "of", "validation", "overrides", "xml", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2011311626", "fields": {"term": "What is the format for validation messages?", "namespace": "open-p", "hash": -2011311626, "terms": ["What is the format for validation messages?", "is the format for validation messages?", "the format for validation messages?", "format for validation messages?", "for validation messages?", "validation messages?", "messages?"], "tokens": ["What", "is", "the", "format", "for", "validation", "messages", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1642950641", "fields": {"term": "What does a validation-id signify?", "namespace": "open-p", "hash": 1642950641, "terms": ["What does a validation-id signify?", "does a validation-id signify?", "a validation-id signify?", "validation-id signify?", "signify?"], "tokens": ["What", "does", "a", "validation", "id", "signify", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1336146830", "fields": {"term": "What is the location of validation-overrides.xml?", "namespace": "open-p", "hash": -1336146830, "terms": ["What is the location of validation-overrides.xml?", "is the location of validation-overrides.xml?", "the location of validation-overrides.xml?", "location of validation-overrides.xml?", "of validation-overrides.xml?", "validation-overrides.xml?"], "tokens": ["What", "is", "the", "location", "of", "validation", "overrides", "xml", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::158975885", "fields": {"term": "When can overriding validations be useful?", "namespace": "open-p", "hash": 158975885, "terms": ["When can overriding validations be useful?", "can overriding validations be useful?", "overriding validations be useful?", "validations be useful?", "be useful?", "useful?"], "tokens": ["When", "can", "overriding", "validations", "be", "useful", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1475761642", "fields": {"term": "What protections do validations provide?", "namespace": "open-p", "hash": 1475761642, "terms": ["What protections do validations provide?", "protections do validations provide?", "do validations provide?", "validations provide?", "provide?"], "tokens": ["What", "protections", "do", "validations", "provide", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-8142106", "fields": {"term": "What types of validation can be overridden?", "namespace": "open-p", "hash": -8142106, "terms": ["What types of validation can be overridden?", "types of validation can be overridden?", "of validation can be overridden?", "validation can be overridden?", "can be overridden?", "be overridden?", "overridden?"], "tokens": ["What", "types", "of", "validation", "can", "be", "overridden", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1798735054", "fields": {"term": "What happens when a deployment fails validation?", "namespace": "open-p", "hash": 1798735054, "terms": ["What happens when a deployment fails validation?", "happens when a deployment fails validation?", "when a deployment fails validation?", "a deployment fails validation?", "deployment fails validation?", "fails validation?", "validation?"], "tokens": ["What", "happens", "when", "a", "deployment", "fails", "validation", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::237067556", "fields": {"term": "What is the validation-overrides tag?", "namespace": "open-p", "hash": 237067556, "terms": ["What is the validation-overrides tag?", "is the validation-overrides tag?", "the validation-overrides tag?", "validation-overrides tag?", "tag?"], "tokens": ["What", "is", "the", "validation", "overrides", "tag", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2144531636", "fields": {"term": "What is an allow tag used for?", "namespace": "open-p", "hash": 2144531636, "terms": ["What is an allow tag used for?", "is an allow tag used for?", "an allow tag used for?", "allow tag used for?", "tag used for?", "used for?", "for?"], "tokens": ["What", "is", "an", "allow", "tag", "used", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1934632446", "fields": {"term": "What does the example of the validation-overrides tag look like?", "namespace": "open-p", "hash": 1934632446, "terms": ["What does the example of the validation-overrides tag look like?", "does the example of the validation-overrides tag look like?", "the example of the validation-overrides tag look like?", "example of the validation-overrides tag look like?", "of the validation-overrides tag look like?", "the validation-overrides tag look like?", "validation-overrides tag look like?", "tag look like?", "look like?", "like?"], "tokens": ["What", "does", "the", "example", "of", "the", "validation", "overrides", "tag", "look", "like", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1148797705", "fields": {"term": "What does the example of the allow tag look like?", "namespace": "open-p", "hash": 1148797705, "terms": ["What does the example of the allow tag look like?", "does the example of the allow tag look like?", "the example of the allow tag look like?", "example of the allow tag look like?", "of the allow tag look like?", "the allow tag look like?", "allow tag look like?", "tag look like?", "look like?", "like?"], "tokens": ["What", "does", "the", "example", "of", "the", "allow", "tag", "look", "like", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::261432152", "fields": {"term": "What is cluster-size-reduction?", "namespace": "open-p", "hash": 261432152, "terms": ["What is cluster-size-reduction?", "is cluster-size-reduction?", "cluster-size-reduction?"], "tokens": ["What", "is", "cluster", "size", "reduction", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::770300297", "fields": {"term": "What is an 'allow'-tag?", "namespace": "open-p", "hash": 770300297, "terms": ["What is an 'allow'-tag?", "is an 'allow'-tag?", "an 'allow'-tag?", "'allow'-tag?"], "tokens": ["What", "is", "an", "'", "allow", "'", "tag", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2141387688", "fields": {"term": "What does an 'allow'-tag contain?", "namespace": "open-p", "hash": -2141387688, "terms": ["What does an 'allow'-tag contain?", "does an 'allow'-tag contain?", "an 'allow'-tag contain?", "'allow'-tag contain?", "contain?"], "tokens": ["What", "does", "an", "'", "allow", "'", "tag", "contain", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::685534646", "fields": {"term": "What is the format of the 'until' attribute?", "namespace": "open-p", "hash": 685534646, "terms": ["What is the format of the 'until' attribute?", "is the format of the 'until' attribute?", "the format of the 'until' attribute?", "format of the 'until' attribute?", "of the 'until' attribute?", "the 'until' attribute?", "'until' attribute?", "attribute?"], "tokens": ["What", "is", "the", "format", "of", "the", "'", "until", "'", "attribute", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1021031645", "fields": {"term": "What happens with 'allow'-tags with unknown ids?", "namespace": "open-p", "hash": -1021031645, "terms": ["What happens with 'allow'-tags with unknown ids?", "happens with 'allow'-tags with unknown ids?", "with 'allow'-tags with unknown ids?", "'allow'-tags with unknown ids?", "with unknown ids?", "unknown ids?", "ids?"], "tokens": ["What", "happens", "with", "'", "allow", "'", "tags", "with", "unknown", "ids", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1860602135", "fields": {"term": "What happens with 'allow'-tags with dates in the past?", "namespace": "open-p", "hash": 1860602135, "terms": ["What happens with 'allow'-tags with dates in the past?", "happens with 'allow'-tags with dates in the past?", "with 'allow'-tags with dates in the past?", "'allow'-tags with dates in the past?", "with dates in the past?", "dates in the past?", "in the past?", "the past?", "past?"], "tokens": ["What", "happens", "with", "'", "allow", "'", "tags", "with", "dates", "in", "the", "past", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1470716561", "fields": {"term": "Is the 'comment' attribute mandatory?", "namespace": "open-p", "hash": 1470716561, "terms": ["Is the 'comment' attribute mandatory?", "the 'comment' attribute mandatory?", "'comment' attribute mandatory?", "attribute mandatory?", "mandatory?"], "tokens": ["Is", "the", "'", "comment", "'", "attribute", "mandatory", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-846818852", "fields": {"term": "What is vector search?", "namespace": "open-p", "hash": -846818852, "terms": ["What is vector search?", "is vector search?", "vector search?", "search?"], "tokens": ["What", "is", "vector", "search", "?"], "corpus_count": 3, "document_count": 3}}, -{"put": "id:term:term::689403189", "fields": {"term": "How does vector search work?", "namespace": "open-p", "hash": 689403189, "terms": ["How does vector search work?", "does vector search work?", "vector search work?", "search work?", "work?"], "tokens": ["How", "does", "vector", "search", "work", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::-564964360", "fields": {"term": "What is multi-modal search?", "namespace": "open-p", "hash": -564964360, "terms": ["What is multi-modal search?", "is multi-modal search?", "multi-modal search?", "search?"], "tokens": ["What", "is", "multi", "modal", "search", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::41691159", "fields": {"term": "What is metadata?", "namespace": "open-p", "hash": 41691159, "terms": ["What is metadata?", "is metadata?", "metadata?"], "tokens": ["What", "is", "metadata", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1746482566", "fields": {"term": "What is the synonym problem?", "namespace": "open-p", "hash": 1746482566, "terms": ["What is the synonym problem?", "is the synonym problem?", "the synonym problem?", "synonym problem?", "problem?"], "tokens": ["What", "is", "the", "synonym", "problem", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-993885212", "fields": {"term": "What is the solution to the synonym problem?", "namespace": "open-p", "hash": -993885212, "terms": ["What is the solution to the synonym problem?", "is the solution to the synonym problem?", "the solution to the synonym problem?", "solution to the synonym problem?", "to the synonym problem?", "the synonym problem?", "synonym problem?", "problem?"], "tokens": ["What", "is", "the", "solution", "to", "the", "synonym", "problem", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1683376248", "fields": {"term": "What is a vector representation?", "namespace": "open-p", "hash": 1683376248, "terms": ["What is a vector representation?", "is a vector representation?", "a vector representation?", "vector representation?", "representation?"], "tokens": ["What", "is", "a", "vector", "representation", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::142873421", "fields": {"term": "What is the digital domain?", "namespace": "open-p", "hash": 142873421, "terms": ["What is the digital domain?", "is the digital domain?", "the digital domain?", "digital domain?", "domain?"], "tokens": ["What", "is", "the", "digital", "domain", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1515188355", "fields": {"term": "What is the multi-modal problem?", "namespace": "open-p", "hash": -1515188355, "terms": ["What is the multi-modal problem?", "is the multi-modal problem?", "the multi-modal problem?", "multi-modal problem?", "problem?"], "tokens": ["What", "is", "the", "multi", "modal", "problem", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1060589407", "fields": {"term": "How is vector search different from textual search?", "namespace": "open-p", "hash": 1060589407, "terms": ["How is vector search different from textual search?", "is vector search different from textual search?", "vector search different from textual search?", "search different from textual search?", "different from textual search?", "from textual search?", "textual search?", "search?"], "tokens": ["How", "is", "vector", "search", "different", "from", "textual", "search", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-176571460", "fields": {"term": "What are vectors in Vespa?", "namespace": "open-p", "hash": -176571460, "terms": ["What are vectors in Vespa?", "are vectors in Vespa?", "vectors in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["What", "are", "vectors", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2115187650", "fields": {"term": "What is the cost/quality tradeoff?", "namespace": "open-p", "hash": 2115187650, "terms": ["What is the cost/quality tradeoff?", "is the cost/quality tradeoff?", "the cost/quality tradeoff?", "cost/quality tradeoff?", "tradeoff?"], "tokens": ["What", "is", "the", "cost", "/", "quality", "tradeoff", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-653379807", "fields": {"term": "What is PCA in Vespa?", "namespace": "open-p", "hash": -653379807, "terms": ["What is PCA in Vespa?", "is PCA in Vespa?", "PCA in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["What", "is", "PCA", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1008999472", "fields": {"term": "How to select optimal tensor type?", "namespace": "open-p", "hash": 1008999472, "terms": ["How to select optimal tensor type?", "to select optimal tensor type?", "select optimal tensor type?", "optimal tensor type?", "tensor type?", "type?"], "tokens": ["How", "to", "select", "optimal", "tensor", "type", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1928490271", "fields": {"term": "What is the role of vectors in search?", "namespace": "open-p", "hash": 1928490271, "terms": ["What is the role of vectors in search?", "is the role of vectors in search?", "the role of vectors in search?", "role of vectors in search?", "of vectors in search?", "vectors in search?", "in search?", "search?"], "tokens": ["What", "is", "the", "role", "of", "vectors", "in", "search", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1643987830", "fields": {"term": "What are the dimensions and types of a vector?", "namespace": "open-p", "hash": -1643987830, "terms": ["What are the dimensions and types of a vector?", "are the dimensions and types of a vector?", "the dimensions and types of a vector?", "dimensions and types of a vector?", "and types of a vector?", "types of a vector?", "of a vector?", "a vector?", "vector?"], "tokens": ["What", "are", "the", "dimensions", "and", "types", "of", "a", "vector", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1093251345", "fields": {"term": "Can vectors with different values for each dimension be similar?", "namespace": "open-p", "hash": -1093251345, "terms": ["Can vectors with different values for each dimension be similar?", "vectors with different values for each dimension be similar?", "with different values for each dimension be similar?", "different values for each dimension be similar?", "values for each dimension be similar?", "for each dimension be similar?", "each dimension be similar?", "dimension be similar?", "be similar?", "similar?"], "tokens": ["Can", "vectors", "with", "different", "values", "for", "each", "dimension", "be", "similar", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-173738264", "fields": {"term": "When is vector search useful?", "namespace": "open-p", "hash": -173738264, "terms": ["When is vector search useful?", "is vector search useful?", "vector search useful?", "search useful?", "useful?"], "tokens": ["When", "is", "vector", "search", "useful", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::316258637", "fields": {"term": "How do you calculate vector similarity?", "namespace": "open-p", "hash": 316258637, "terms": ["How do you calculate vector similarity?", "do you calculate vector similarity?", "you calculate vector similarity?", "calculate vector similarity?", "vector similarity?", "similarity?"], "tokens": ["How", "do", "you", "calculate", "vector", "similarity", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-102240958", "fields": {"term": "What is vector similarity?", "namespace": "open-p", "hash": -102240958, "terms": ["What is vector similarity?", "is vector similarity?", "vector similarity?", "similarity?"], "tokens": ["What", "is", "vector", "similarity", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1988503318", "fields": {"term": "What is ANN search?", "namespace": "open-p", "hash": -1988503318, "terms": ["What is ANN search?", "is ANN search?", "ANN search?", "search?"], "tokens": ["What", "is", "ANN", "search", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-775768983", "fields": {"term": "What indexing is needed for ANNs?", "namespace": "open-p", "hash": -775768983, "terms": ["What indexing is needed for ANNs?", "indexing is needed for ANNs?", "is needed for ANNs?", "needed for ANNs?", "for ANNs?", "ANNs?"], "tokens": ["What", "indexing", "is", "needed", "for", "ANNs", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1087699379", "fields": {"term": "Does Vespa support vector updates?", "namespace": "open-p", "hash": -1087699379, "terms": ["Does Vespa support vector updates?", "Vespa support vector updates?", "support vector updates?", "vector updates?", "updates?"], "tokens": ["Does", "Vespa", "support", "vector", "updates", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1530899431", "fields": {"term": "Does Vespa support all kinds of ANN indexing?", "namespace": "open-p", "hash": 1530899431, "terms": ["Does Vespa support all kinds of ANN indexing?", "Vespa support all kinds of ANN indexing?", "support all kinds of ANN indexing?", "all kinds of ANN indexing?", "kinds of ANN indexing?", "of ANN indexing?", "ANN indexing?", "indexing?"], "tokens": ["Does", "Vespa", "support", "all", "kinds", "of", "ANN", "indexing", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1902294685", "fields": {"term": "What is the cost of inserts for ANNs?", "namespace": "open-p", "hash": -1902294685, "terms": ["What is the cost of inserts for ANNs?", "is the cost of inserts for ANNs?", "the cost of inserts for ANNs?", "cost of inserts for ANNs?", "of inserts for ANNs?", "inserts for ANNs?", "for ANNs?", "ANNs?"], "tokens": ["What", "is", "the", "cost", "of", "inserts", "for", "ANNs", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-284260779", "fields": {"term": "What is benchmarking in Vespa?", "namespace": "open-p", "hash": -284260779, "terms": ["What is benchmarking in Vespa?", "is benchmarking in Vespa?", "benchmarking in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["What", "is", "benchmarking", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-253269272", "fields": {"term": "What questions can benchmarking answer?", "namespace": "open-p", "hash": -253269272, "terms": ["What questions can benchmarking answer?", "questions can benchmarking answer?", "can benchmarking answer?", "benchmarking answer?", "answer?"], "tokens": ["What", "questions", "can", "benchmarking", "answer", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1873046668", "fields": {"term": "When is benchmarking recommended?", "namespace": "open-p", "hash": -1873046668, "terms": ["When is benchmarking recommended?", "is benchmarking recommended?", "benchmarking recommended?", "recommended?"], "tokens": ["When", "is", "benchmarking", "recommended", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::589453964", "fields": {"term": "What are considerations before benchmarking?", "namespace": "open-p", "hash": 589453964, "terms": ["What are considerations before benchmarking?", "are considerations before benchmarking?", "considerations before benchmarking?", "before benchmarking?", "benchmarking?"], "tokens": ["What", "are", "considerations", "before", "benchmarking", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1560579025", "fields": {"term": "How can you disable soft timeout?", "namespace": "open-p", "hash": 1560579025, "terms": ["How can you disable soft timeout?", "can you disable soft timeout?", "you disable soft timeout?", "disable soft timeout?", "soft timeout?", "timeout?"], "tokens": ["How", "can", "you", "disable", "soft", "timeout", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1312464794", "fields": {"term": "How long should timeout be for benchmarking?", "namespace": "open-p", "hash": -1312464794, "terms": ["How long should timeout be for benchmarking?", "long should timeout be for benchmarking?", "should timeout be for benchmarking?", "timeout be for benchmarking?", "be for benchmarking?", "for benchmarking?", "benchmarking?"], "tokens": ["How", "long", "should", "timeout", "be", "for", "benchmarking", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1769489679", "fields": {"term": "How can you set timeout for benchmarking?", "namespace": "open-p", "hash": -1769489679, "terms": ["How can you set timeout for benchmarking?", "can you set timeout for benchmarking?", "you set timeout for benchmarking?", "set timeout for benchmarking?", "timeout for benchmarking?", "for benchmarking?", "benchmarking?"], "tokens": ["How", "can", "you", "set", "timeout", "for", "benchmarking", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1387227783", "fields": {"term": "Where can I find Vespa Cloud Benchmarking?", "namespace": "open-p", "hash": -1387227783, "terms": ["Where can I find Vespa Cloud Benchmarking?", "can I find Vespa Cloud Benchmarking?", "I find Vespa Cloud Benchmarking?", "find Vespa Cloud Benchmarking?", "Vespa Cloud Benchmarking?", "Cloud Benchmarking?", "Benchmarking?"], "tokens": ["Where", "can", "I", "find", "Vespa", "Cloud", "Benchmarking", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2143033775", "fields": {"term": "What are the vespa-fbench statistics?", "namespace": "open-p", "hash": -2143033775, "terms": ["What are the vespa-fbench statistics?", "are the vespa-fbench statistics?", "the vespa-fbench statistics?", "vespa-fbench statistics?", "statistics?"], "tokens": ["What", "are", "the", "vespa", "fbench", "statistics", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-191838797", "fields": {"term": "What is the purpose of vespa-fbench?", "namespace": "open-p", "hash": -191838797, "terms": ["What is the purpose of vespa-fbench?", "is the purpose of vespa-fbench?", "the purpose of vespa-fbench?", "purpose of vespa-fbench?", "of vespa-fbench?", "vespa-fbench?"], "tokens": ["What", "is", "the", "purpose", "of", "vespa", "fbench", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2086639458", "fields": {"term": "What is the disclaimer for vespa-fbench?", "namespace": "open-p", "hash": -2086639458, "terms": ["What is the disclaimer for vespa-fbench?", "is the disclaimer for vespa-fbench?", "the disclaimer for vespa-fbench?", "disclaimer for vespa-fbench?", "for vespa-fbench?", "vespa-fbench?"], "tokens": ["What", "is", "the", "disclaimer", "for", "vespa", "fbench", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1314157891", "fields": {"term": "What is the format for HTTP POST requests?", "namespace": "open-p", "hash": -1314157891, "terms": ["What is the format for HTTP POST requests?", "is the format for HTTP POST requests?", "the format for HTTP POST requests?", "format for HTTP POST requests?", "for HTTP POST requests?", "HTTP POST requests?", "POST requests?", "requests?"], "tokens": ["What", "is", "the", "format", "for", "HTTP", "POST", "requests", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1650001316", "fields": {"term": "What is summarized in benchmarking?", "namespace": "open-p", "hash": 1650001316, "terms": ["What is summarized in benchmarking?", "is summarized in benchmarking?", "summarized in benchmarking?", "in benchmarking?", "benchmarking?"], "tokens": ["What", "is", "summarized", "in", "benchmarking", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1104718266", "fields": {"term": "How do you measure Vespa latency?", "namespace": "open-p", "hash": 1104718266, "terms": ["How do you measure Vespa latency?", "do you measure Vespa latency?", "you measure Vespa latency?", "measure Vespa latency?", "Vespa latency?", "latency?"], "tokens": ["How", "do", "you", "measure", "Vespa", "latency", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::654762886", "fields": {"term": "What is a latency baseline?", "namespace": "open-p", "hash": 654762886, "terms": ["What is a latency baseline?", "is a latency baseline?", "a latency baseline?", "latency baseline?", "baseline?"], "tokens": ["What", "is", "a", "latency", "baseline", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1352899460", "fields": {"term": "What is requestthreads?", "namespace": "open-p", "hash": 1352899460, "terms": ["What is requestthreads?", "is requestthreads?", "requestthreads?"], "tokens": ["What", "is", "requestthreads", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2093772348", "fields": {"term": "How do you find capacity?", "namespace": "open-p", "hash": -2093772348, "terms": ["How do you find capacity?", "do you find capacity?", "you find capacity?", "find capacity?", "capacity?"], "tokens": ["How", "do", "you", "find", "capacity", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1920228591", "fields": {"term": "How do you measure query latency?", "namespace": "open-p", "hash": -1920228591, "terms": ["How do you measure query latency?", "do you measure query latency?", "you measure query latency?", "measure query latency?", "query latency?", "latency?"], "tokens": ["How", "do", "you", "measure", "query", "latency", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1852318627", "fields": {"term": "What is CPU utilization?", "namespace": "open-p", "hash": 1852318627, "terms": ["What is CPU utilization?", "is CPU utilization?", "CPU utilization?", "utilization?"], "tokens": ["What", "is", "CPU", "utilization", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::924919167", "fields": {"term": "What is the purpose of increasing number of clients?", "namespace": "open-p", "hash": 924919167, "terms": ["What is the purpose of increasing number of clients?", "is the purpose of increasing number of clients?", "the purpose of increasing number of clients?", "purpose of increasing number of clients?", "of increasing number of clients?", "increasing number of clients?", "number of clients?", "of clients?", "clients?"], "tokens": ["What", "is", "the", "purpose", "of", "increasing", "number", "of", "clients", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1008992900", "fields": {"term": "What is the recommended way to increase threads?", "namespace": "open-p", "hash": -1008992900, "terms": ["What is the recommended way to increase threads?", "is the recommended way to increase threads?", "the recommended way to increase threads?", "recommended way to increase threads?", "way to increase threads?", "to increase threads?", "increase threads?", "threads?"], "tokens": ["What", "is", "the", "recommended", "way", "to", "increase", "threads", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::693942258", "fields": {"term": "What is the interface of container nodes?", "namespace": "open-p", "hash": 693942258, "terms": ["What is the interface of container nodes?", "is the interface of container nodes?", "the interface of container nodes?", "interface of container nodes?", "of container nodes?", "container nodes?", "nodes?"], "tokens": ["What", "is", "the", "interface", "of", "container", "nodes", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-672434546", "fields": {"term": "How to output all metrics?", "namespace": "open-p", "hash": -672434546, "terms": ["How to output all metrics?", "to output all metrics?", "output all metrics?", "all metrics?", "metrics?"], "tokens": ["How", "to", "output", "all", "metrics", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::516174236", "fields": {"term": "What is the output format of 'cpu.util' metric?", "namespace": "open-p", "hash": 516174236, "terms": ["What is the output format of 'cpu.util' metric?", "is the output format of 'cpu.util' metric?", "the output format of 'cpu.util' metric?", "output format of 'cpu.util' metric?", "format of 'cpu.util' metric?", "of 'cpu.util' metric?", "'cpu.util' metric?", "metric?"], "tokens": ["What", "is", "the", "output", "format", "of", "'", "cpu", "util", "'", "metric", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1433757796", "fields": {"term": "What is the role of 'content/mysearchcluster/0/0'?", "namespace": "open-p", "hash": 1433757796, "terms": ["What is the role of 'content/mysearchcluster/0/0'?", "is the role of 'content/mysearchcluster/0/0'?", "the role of 'content/mysearchcluster/0/0'?", "role of 'content/mysearchcluster/0/0'?", "of 'content/mysearchcluster/0/0'?", "'content/mysearchcluster/0/0'?"], "tokens": ["What", "is", "the", "role", "of", "'", "content", "/", "mysearchcluster", "/", "0", "/", "0", "'?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1365854973", "fields": {"term": "How to use the 'jq' command?", "namespace": "open-p", "hash": 1365854973, "terms": ["How to use the 'jq' command?", "to use the 'jq' command?", "use the 'jq' command?", "the 'jq' command?", "'jq' command?", "command?"], "tokens": ["How", "to", "use", "the", "'", "jq", "'", "command", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-269811129", "fields": {"term": "What is the 'feed' command?", "namespace": "open-p", "hash": -269811129, "terms": ["What is the 'feed' command?", "is the 'feed' command?", "the 'feed' command?", "'feed' command?", "command?"], "tokens": ["What", "is", "the", "'", "feed", "'", "command", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::691147858", "fields": {"term": "What format are input files expected to be in for 'feed' command?", "namespace": "open-p", "hash": 691147858, "terms": ["What format are input files expected to be in for 'feed' command?", "format are input files expected to be in for 'feed' command?", "are input files expected to be in for 'feed' command?", "input files expected to be in for 'feed' command?", "files expected to be in for 'feed' command?", "expected to be in for 'feed' command?", "to be in for 'feed' command?", "be in for 'feed' command?", "in for 'feed' command?", "for 'feed' command?", "'feed' command?", "command?"], "tokens": ["What", "format", "are", "input", "files", "expected", "to", "be", "in", "for", "'", "feed", "'", "command", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::135400095", "fields": {"term": "Must the Document API be enabled before using 'vespa feed'?", "namespace": "open-p", "hash": 135400095, "terms": ["Must the Document API be enabled before using 'vespa feed'?", "the Document API be enabled before using 'vespa feed'?", "Document API be enabled before using 'vespa feed'?", "API be enabled before using 'vespa feed'?", "be enabled before using 'vespa feed'?", "enabled before using 'vespa feed'?", "before using 'vespa feed'?", "using 'vespa feed'?", "'vespa feed'?", "feed'?"], "tokens": ["Must", "the", "Document", "API", "be", "enabled", "before", "using", "'", "vespa", "feed", "'?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::912894295", "fields": {"term": "How to feed documents?", "namespace": "open-p", "hash": 912894295, "terms": ["How to feed documents?", "to feed documents?", "feed documents?", "documents?"], "tokens": ["How", "to", "feed", "documents", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1646482952", "fields": {"term": "How to display a periodic summary?", "namespace": "open-p", "hash": 1646482952, "terms": ["How to display a periodic summary?", "to display a periodic summary?", "display a periodic summary?", "a periodic summary?", "periodic summary?", "summary?"], "tokens": ["How", "to", "display", "a", "periodic", "summary", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1735519027", "fields": {"term": "How to print successful operations?", "namespace": "open-p", "hash": -1735519027, "terms": ["How to print successful operations?", "to print successful operations?", "print successful operations?", "successful operations?", "operations?"], "tokens": ["How", "to", "print", "successful", "operations", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::979692174", "fields": {"term": "Where to find complete usage details?", "namespace": "open-p", "hash": 979692174, "terms": ["Where to find complete usage details?", "to find complete usage details?", "find complete usage details?", "complete usage details?", "usage details?", "details?"], "tokens": ["Where", "to", "find", "complete", "usage", "details", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-761609354", "fields": {"term": "What can be done with Vespa CLI?", "namespace": "open-p", "hash": -761609354, "terms": ["What can be done with Vespa CLI?", "can be done with Vespa CLI?", "be done with Vespa CLI?", "done with Vespa CLI?", "with Vespa CLI?", "Vespa CLI?", "CLI?"], "tokens": ["What", "can", "be", "done", "with", "Vespa", "CLI", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::621639008", "fields": {"term": "How to install Vespa CLI?", "namespace": "open-p", "hash": 621639008, "terms": ["How to install Vespa CLI?", "to install Vespa CLI?", "install Vespa CLI?", "Vespa CLI?", "CLI?"], "tokens": ["How", "to", "install", "Vespa", "CLI", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-871605835", "fields": {"term": "What platforms support Vespa CLI?", "namespace": "open-p", "hash": -871605835, "terms": ["What platforms support Vespa CLI?", "platforms support Vespa CLI?", "support Vespa CLI?", "Vespa CLI?", "CLI?"], "tokens": ["What", "platforms", "support", "Vespa", "CLI", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1723130915", "fields": {"term": "Where can I find Vespa CLI commands documentation?", "namespace": "open-p", "hash": 1723130915, "terms": ["Where can I find Vespa CLI commands documentation?", "can I find Vespa CLI commands documentation?", "I find Vespa CLI commands documentation?", "find Vespa CLI commands documentation?", "Vespa CLI commands documentation?", "CLI commands documentation?", "commands documentation?", "documentation?"], "tokens": ["Where", "can", "I", "find", "Vespa", "CLI", "commands", "documentation", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-875139445", "fields": {"term": "What is VESPA_CLI_HOME?", "namespace": "open-p", "hash": -875139445, "terms": ["What is VESPA_CLI_HOME?", "is VESPA_CLI_HOME?", "VESPA_CLI_HOME?"], "tokens": ["What", "is", "VESPA_CLI_HOME", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1717937153", "fields": {"term": "How to set VESPA_CLI_HOME?", "namespace": "open-p", "hash": 1717937153, "terms": ["How to set VESPA_CLI_HOME?", "to set VESPA_CLI_HOME?", "set VESPA_CLI_HOME?", "VESPA_CLI_HOME?"], "tokens": ["How", "to", "set", "VESPA_CLI_HOME", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1408202570", "fields": {"term": "How can I get help with vespa document put?", "namespace": "open-p", "hash": -1408202570, "terms": ["How can I get help with vespa document put?", "can I get help with vespa document put?", "I get help with vespa document put?", "get help with vespa document put?", "help with vespa document put?", "with vespa document put?", "vespa document put?", "document put?", "put?"], "tokens": ["How", "can", "I", "get", "help", "with", "vespa", "document", "put", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1586877862", "fields": {"term": "What is vespa auth?", "namespace": "open-p", "hash": 1586877862, "terms": ["What is vespa auth?", "is vespa auth?", "vespa auth?", "auth?"], "tokens": ["What", "is", "vespa", "auth", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-611583237", "fields": {"term": "How do I perform Vespa auth?", "namespace": "open-p", "hash": -611583237, "terms": ["How do I perform Vespa auth?", "do I perform Vespa auth?", "I perform Vespa auth?", "perform Vespa auth?", "Vespa auth?", "auth?"], "tokens": ["How", "do", "I", "perform", "Vespa", "auth", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::10842742", "fields": {"term": "How do I update a document?", "namespace": "open-p", "hash": 10842742, "terms": ["How do I update a document?", "do I update a document?", "I update a document?", "update a document?", "a document?", "document?"], "tokens": ["How", "do", "I", "update", "a", "document", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1005876234", "fields": {"term": "What is 'a-head-full-of-dreams'?", "namespace": "open-p", "hash": -1005876234, "terms": ["What is 'a-head-full-of-dreams'?", "is 'a-head-full-of-dreams'?", "'a-head-full-of-dreams'?"], "tokens": ["What", "is", "'", "a", "head", "full", "of", "dreams", "'?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2123474046", "fields": {"term": "What is slice-id?", "namespace": "open-p", "hash": 2123474046, "terms": ["What is slice-id?", "is slice-id?", "slice-id?"], "tokens": ["What", "is", "slice", "id", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-659117645", "fields": {"term": "What are Vespa command-line tools?", "namespace": "open-p", "hash": -659117645, "terms": ["What are Vespa command-line tools?", "are Vespa command-line tools?", "Vespa command-line tools?", "command-line tools?", "tools?"], "tokens": ["What", "are", "Vespa", "command", "line", "tools", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1932183050", "fields": {"term": "What is vespa-attribute-inspect?", "namespace": "open-p", "hash": 1932183050, "terms": ["What is vespa-attribute-inspect?", "is vespa-attribute-inspect?", "vespa-attribute-inspect?"], "tokens": ["What", "is", "vespa", "attribute", "inspect", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::458112839", "fields": {"term": "What is the use of vespa-attribute-inspect?", "namespace": "open-p", "hash": 458112839, "terms": ["What is the use of vespa-attribute-inspect?", "is the use of vespa-attribute-inspect?", "the use of vespa-attribute-inspect?", "use of vespa-attribute-inspect?", "of vespa-attribute-inspect?", "vespa-attribute-inspect?"], "tokens": ["What", "is", "the", "use", "of", "vespa", "attribute", "inspect", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1699257316", "fields": {"term": "How to use vespa-attribute-inspect?", "namespace": "open-p", "hash": 1699257316, "terms": ["How to use vespa-attribute-inspect?", "to use vespa-attribute-inspect?", "use vespa-attribute-inspect?", "vespa-attribute-inspect?"], "tokens": ["How", "to", "use", "vespa", "attribute", "inspect", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-827780227", "fields": {"term": "What is the purpose of vespa-attribute-inspect?", "namespace": "open-p", "hash": -827780227, "terms": ["What is the purpose of vespa-attribute-inspect?", "is the purpose of vespa-attribute-inspect?", "the purpose of vespa-attribute-inspect?", "purpose of vespa-attribute-inspect?", "of vespa-attribute-inspect?", "vespa-attribute-inspect?"], "tokens": ["What", "is", "the", "purpose", "of", "vespa", "attribute", "inspect", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1330799470", "fields": {"term": "How does vespa-attribute-inspect help with troubleshooting?", "namespace": "open-p", "hash": -1330799470, "terms": ["How does vespa-attribute-inspect help with troubleshooting?", "does vespa-attribute-inspect help with troubleshooting?", "vespa-attribute-inspect help with troubleshooting?", "help with troubleshooting?", "with troubleshooting?", "troubleshooting?"], "tokens": ["How", "does", "vespa", "attribute", "inspect", "help", "with", "troubleshooting", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-420979925", "fields": {"term": "What is vespa-index-inspect?", "namespace": "open-p", "hash": -420979925, "terms": ["What is vespa-index-inspect?", "is vespa-index-inspect?", "vespa-index-inspect?"], "tokens": ["What", "is", "vespa", "index", "inspect", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::1054344383", "fields": {"term": "What is the related tool to vespa-attribute-inspect?", "namespace": "open-p", "hash": 1054344383, "terms": ["What is the related tool to vespa-attribute-inspect?", "is the related tool to vespa-attribute-inspect?", "the related tool to vespa-attribute-inspect?", "related tool to vespa-attribute-inspect?", "tool to vespa-attribute-inspect?", "to vespa-attribute-inspect?", "vespa-attribute-inspect?"], "tokens": ["What", "is", "the", "related", "tool", "to", "vespa", "attribute", "inspect", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-653176942", "fields": {"term": "What is config proxy?", "namespace": "open-p", "hash": -653176942, "terms": ["What is config proxy?", "is config proxy?", "config proxy?", "proxy?"], "tokens": ["What", "is", "config", "proxy", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::-639834972", "fields": {"term": "What does config proxy run?", "namespace": "open-p", "hash": -639834972, "terms": ["What does config proxy run?", "does config proxy run?", "config proxy run?", "proxy run?", "run?"], "tokens": ["What", "does", "config", "proxy", "run", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1280551795", "fields": {"term": "What is the 'sources' method?", "namespace": "open-p", "hash": 1280551795, "terms": ["What is the 'sources' method?", "is the 'sources' method?", "the 'sources' method?", "'sources' method?", "method?"], "tokens": ["What", "is", "the", "'", "sources", "'", "method", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-660838769", "fields": {"term": "How to update the config proxy's upstream sources?", "namespace": "open-p", "hash": -660838769, "terms": ["How to update the config proxy's upstream sources?", "to update the config proxy's upstream sources?", "update the config proxy's upstream sources?", "the config proxy's upstream sources?", "config proxy's upstream sources?", "proxy's upstream sources?", "upstream sources?", "sources?"], "tokens": ["How", "to", "update", "the", "config", "proxy", "'", "s", "upstream", "sources", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1456683448", "fields": {"term": "What happens when vespa-configproxy-cmd is run without arguments?", "namespace": "open-p", "hash": 1456683448, "terms": ["What happens when vespa-configproxy-cmd is run without arguments?", "happens when vespa-configproxy-cmd is run without arguments?", "when vespa-configproxy-cmd is run without arguments?", "vespa-configproxy-cmd is run without arguments?", "is run without arguments?", "run without arguments?", "without arguments?", "arguments?"], "tokens": ["What", "happens", "when", "vespa", "configproxy", "cmd", "is", "run", "without", "arguments", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::386546930", "fields": {"term": "What information does 'get mode' output?", "namespace": "open-p", "hash": 386546930, "terms": ["What information does 'get mode' output?", "information does 'get mode' output?", "does 'get mode' output?", "'get mode' output?", "mode' output?", "output?"], "tokens": ["What", "information", "does", "'", "get", "mode", "'", "output", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::491284505", "fields": {"term": "What is vespa-configserver-remove-state?", "namespace": "open-p", "hash": 491284505, "terms": ["What is vespa-configserver-remove-state?", "is vespa-configserver-remove-state?", "vespa-configserver-remove-state?"], "tokens": ["What", "is", "vespa", "configserver", "remove", "state", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1352907181", "fields": {"term": "What does vespa-config-status output?", "namespace": "open-p", "hash": 1352907181, "terms": ["What does vespa-config-status output?", "does vespa-config-status output?", "vespa-config-status output?", "output?"], "tokens": ["What", "does", "vespa", "config", "status", "output", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-515326395", "fields": {"term": "How do you invoke vespa-config-status?", "namespace": "open-p", "hash": -515326395, "terms": ["How do you invoke vespa-config-status?", "do you invoke vespa-config-status?", "you invoke vespa-config-status?", "invoke vespa-config-status?", "vespa-config-status?"], "tokens": ["How", "do", "you", "invoke", "vespa", "config", "status", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1727713087", "fields": {"term": "What optional arguments does vespa-config-status have?", "namespace": "open-p", "hash": -1727713087, "terms": ["What optional arguments does vespa-config-status have?", "optional arguments does vespa-config-status have?", "arguments does vespa-config-status have?", "does vespa-config-status have?", "vespa-config-status have?", "have?"], "tokens": ["What", "optional", "arguments", "does", "vespa", "config", "status", "have", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1618163791", "fields": {"term": "What is the deploy API?", "namespace": "open-p", "hash": -1618163791, "terms": ["What is the deploy API?", "is the deploy API?", "the deploy API?", "deploy API?", "API?"], "tokens": ["What", "is", "the", "deploy", "API", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1096910633", "fields": {"term": "What does vespa-deploy activate do?", "namespace": "open-p", "hash": 1096910633, "terms": ["What does vespa-deploy activate do?", "does vespa-deploy activate do?", "vespa-deploy activate do?", "activate do?", "do?"], "tokens": ["What", "does", "vespa", "deploy", "activate", "do", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-443905686", "fields": {"term": "What are the possible commands for vespa-deploy?", "namespace": "open-p", "hash": -443905686, "terms": ["What are the possible commands for vespa-deploy?", "are the possible commands for vespa-deploy?", "the possible commands for vespa-deploy?", "possible commands for vespa-deploy?", "commands for vespa-deploy?", "for vespa-deploy?", "vespa-deploy?"], "tokens": ["What", "are", "the", "possible", "commands", "for", "vespa", "deploy", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-569677923", "fields": {"term": "What are the options for vespa-deploy?", "namespace": "open-p", "hash": -569677923, "terms": ["What are the options for vespa-deploy?", "are the options for vespa-deploy?", "the options for vespa-deploy?", "options for vespa-deploy?", "for vespa-deploy?", "vespa-deploy?"], "tokens": ["What", "are", "the", "options", "for", "vespa", "deploy", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1544927372", "fields": {"term": "What is the syntax for vespa-deploy?", "namespace": "open-p", "hash": 1544927372, "terms": ["What is the syntax for vespa-deploy?", "is the syntax for vespa-deploy?", "the syntax for vespa-deploy?", "syntax for vespa-deploy?", "for vespa-deploy?", "vespa-deploy?"], "tokens": ["What", "is", "the", "syntax", "for", "vespa", "deploy", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1354141513", "fields": {"term": "What is the deploy reference?", "namespace": "open-p", "hash": -1354141513, "terms": ["What is the deploy reference?", "is the deploy reference?", "the deploy reference?", "deploy reference?", "reference?"], "tokens": ["What", "is", "the", "deploy", "reference", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-750041267", "fields": {"term": "What is vespa-destination?", "namespace": "open-p", "hash": -750041267, "terms": ["What is vespa-destination?", "is vespa-destination?", "vespa-destination?"], "tokens": ["What", "is", "vespa", "destination", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1876711020", "fields": {"term": "What does vespa-destination output?", "namespace": "open-p", "hash": -1876711020, "terms": ["What does vespa-destination output?", "does vespa-destination output?", "vespa-destination output?", "output?"], "tokens": ["What", "does", "vespa", "destination", "output", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1846598445", "fields": {"term": "What options can be used with vespa-destination?", "namespace": "open-p", "hash": -1846598445, "terms": ["What options can be used with vespa-destination?", "options can be used with vespa-destination?", "can be used with vespa-destination?", "be used with vespa-destination?", "used with vespa-destination?", "with vespa-destination?", "vespa-destination?"], "tokens": ["What", "options", "can", "be", "used", "with", "vespa", "destination", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-140739698", "fields": {"term": "What does vespa-fbench do?", "namespace": "open-p", "hash": -140739698, "terms": ["What does vespa-fbench do?", "does vespa-fbench do?", "vespa-fbench do?", "do?"], "tokens": ["What", "does", "vespa", "fbench", "do", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1626977356", "fields": {"term": "What is the synopsis for vespa-fbench?", "namespace": "open-p", "hash": -1626977356, "terms": ["What is the synopsis for vespa-fbench?", "is the synopsis for vespa-fbench?", "the synopsis for vespa-fbench?", "synopsis for vespa-fbench?", "for vespa-fbench?", "vespa-fbench?"], "tokens": ["What", "is", "the", "synopsis", "for", "vespa", "fbench", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1729990384", "fields": {"term": "What is the default output for vespa-fbench?", "namespace": "open-p", "hash": -1729990384, "terms": ["What is the default output for vespa-fbench?", "is the default output for vespa-fbench?", "the default output for vespa-fbench?", "default output for vespa-fbench?", "output for vespa-fbench?", "for vespa-fbench?", "vespa-fbench?"], "tokens": ["What", "is", "the", "default", "output", "for", "vespa", "fbench", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::911344044", "fields": {"term": "How can vespa-fbench be used?", "namespace": "open-p", "hash": 911344044, "terms": ["How can vespa-fbench be used?", "can vespa-fbench be used?", "vespa-fbench be used?", "be used?", "used?"], "tokens": ["How", "can", "vespa", "fbench", "be", "used", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-567632551", "fields": {"term": "What options are available for vespa-fbench?", "namespace": "open-p", "hash": -567632551, "terms": ["What options are available for vespa-fbench?", "options are available for vespa-fbench?", "are available for vespa-fbench?", "available for vespa-fbench?", "for vespa-fbench?", "vespa-fbench?"], "tokens": ["What", "options", "are", "available", "for", "vespa", "fbench", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1095235820", "fields": {"term": "What is vespa-fbench-filter-file?", "namespace": "open-p", "hash": 1095235820, "terms": ["What is vespa-fbench-filter-file?", "is vespa-fbench-filter-file?", "vespa-fbench-filter-file?"], "tokens": ["What", "is", "vespa", "fbench", "filter", "file", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-266921753", "fields": {"term": "What is vespa-fbench-geturl?", "namespace": "open-p", "hash": -266921753, "terms": ["What is vespa-fbench-geturl?", "is vespa-fbench-geturl?", "vespa-fbench-geturl?"], "tokens": ["What", "is", "vespa", "fbench", "geturl", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::789233270", "fields": {"term": "How is vespa-fbench-geturl used?", "namespace": "open-p", "hash": 789233270, "terms": ["How is vespa-fbench-geturl used?", "is vespa-fbench-geturl used?", "vespa-fbench-geturl used?", "used?"], "tokens": ["How", "is", "vespa", "fbench", "geturl", "used", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-797192847", "fields": {"term": "What does ignored report?", "namespace": "open-p", "hash": -797192847, "terms": ["What does ignored report?", "does ignored report?", "ignored report?", "report?"], "tokens": ["What", "does", "ignored", "report", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2134513868", "fields": {"term": "What is vespa-get?", "namespace": "open-p", "hash": 2134513868, "terms": ["What is vespa-get?", "is vespa-get?", "vespa-get?"], "tokens": ["What", "is", "vespa", "get", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1122896615", "fields": {"term": "What does vespa-get retrieve?", "namespace": "open-p", "hash": -1122896615, "terms": ["What does vespa-get retrieve?", "does vespa-get retrieve?", "vespa-get retrieve?", "retrieve?"], "tokens": ["What", "does", "vespa", "get", "retrieve", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1480086469", "fields": {"term": "What are the options for vespa-get?", "namespace": "open-p", "hash": 1480086469, "terms": ["What are the options for vespa-get?", "are the options for vespa-get?", "the options for vespa-get?", "options for vespa-get?", "for vespa-get?", "vespa-get?"], "tokens": ["What", "are", "the", "options", "for", "vespa", "get", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1772151470", "fields": {"term": "What is the default field set for vespa-get?", "namespace": "open-p", "hash": -1772151470, "terms": ["What is the default field set for vespa-get?", "is the default field set for vespa-get?", "the default field set for vespa-get?", "default field set for vespa-get?", "field set for vespa-get?", "set for vespa-get?", "for vespa-get?", "vespa-get?"], "tokens": ["What", "is", "the", "default", "field", "set", "for", "vespa", "get", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1403799883", "fields": {"term": "What is the default load type for vespa-get?", "namespace": "open-p", "hash": 1403799883, "terms": ["What is the default load type for vespa-get?", "is the default load type for vespa-get?", "the default load type for vespa-get?", "default load type for vespa-get?", "load type for vespa-get?", "type for vespa-get?", "for vespa-get?", "vespa-get?"], "tokens": ["What", "is", "the", "default", "load", "type", "for", "vespa", "get", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1878346532", "fields": {"term": "What are the options for output format?", "namespace": "open-p", "hash": -1878346532, "terms": ["What are the options for output format?", "are the options for output format?", "the options for output format?", "options for output format?", "for output format?", "output format?", "format?"], "tokens": ["What", "are", "the", "options", "for", "output", "format", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2142171626", "fields": {"term": "What is vespa-get-config?", "namespace": "open-p", "hash": 2142171626, "terms": ["What is vespa-get-config?", "is vespa-get-config?", "vespa-get-config?"], "tokens": ["What", "is", "vespa", "get", "config", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1763212314", "fields": {"term": "What is the purpose of vespa-get-config?", "namespace": "open-p", "hash": -1763212314, "terms": ["What is the purpose of vespa-get-config?", "is the purpose of vespa-get-config?", "the purpose of vespa-get-config?", "purpose of vespa-get-config?", "of vespa-get-config?", "vespa-get-config?"], "tokens": ["What", "is", "the", "purpose", "of", "vespa", "get", "config", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-235036059", "fields": {"term": "How to connect to a config proxy?", "namespace": "open-p", "hash": -235036059, "terms": ["How to connect to a config proxy?", "to connect to a config proxy?", "connect to a config proxy?", "to a config proxy?", "a config proxy?", "config proxy?", "proxy?"], "tokens": ["How", "to", "connect", "to", "a", "config", "proxy", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::933554694", "fields": {"term": "How to connect to a config server?", "namespace": "open-p", "hash": 933554694, "terms": ["How to connect to a config server?", "to connect to a config server?", "connect to a config server?", "to a config server?", "a config server?", "config server?", "server?"], "tokens": ["How", "to", "connect", "to", "a", "config", "server", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::851445369", "fields": {"term": "What is vespa-get-node-state?", "namespace": "open-p", "hash": 851445369, "terms": ["What is vespa-get-node-state?", "is vespa-get-node-state?", "vespa-get-node-state?"], "tokens": ["What", "is", "vespa", "get", "node", "state", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-139692914", "fields": {"term": "What is Generated state?", "namespace": "open-p", "hash": -139692914, "terms": ["What is Generated state?", "is Generated state?", "Generated state?", "state?"], "tokens": ["What", "is", "Generated", "state", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1807385302", "fields": {"term": "What does vespa-set-node-state do?", "namespace": "open-p", "hash": -1807385302, "terms": ["What does vespa-set-node-state do?", "does vespa-set-node-state do?", "vespa-set-node-state do?", "do?"], "tokens": ["What", "does", "vespa", "set", "node", "state", "do", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::843012871", "fields": {"term": "How can I inspect indexed data?", "namespace": "open-p", "hash": 843012871, "terms": ["How can I inspect indexed data?", "can I inspect indexed data?", "I inspect indexed data?", "inspect indexed data?", "indexed data?", "data?"], "tokens": ["How", "can", "I", "inspect", "indexed", "data", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::694097435", "fields": {"term": "What is the output of vespa-index-inspect?", "namespace": "open-p", "hash": 694097435, "terms": ["What is the output of vespa-index-inspect?", "is the output of vespa-index-inspect?", "the output of vespa-index-inspect?", "output of vespa-index-inspect?", "of vespa-index-inspect?", "vespa-index-inspect?"], "tokens": ["What", "is", "the", "output", "of", "vespa", "index", "inspect", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1091512488", "fields": {"term": "What are the options for vespa-index-inspect?", "namespace": "open-p", "hash": -1091512488, "terms": ["What are the options for vespa-index-inspect?", "are the options for vespa-index-inspect?", "the options for vespa-index-inspect?", "options for vespa-index-inspect?", "for vespa-index-inspect?", "vespa-index-inspect?"], "tokens": ["What", "are", "the", "options", "for", "vespa", "index", "inspect", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1189902874", "fields": {"term": "How do I show posting lists with vespa-index-inspect?", "namespace": "open-p", "hash": -1189902874, "terms": ["How do I show posting lists with vespa-index-inspect?", "do I show posting lists with vespa-index-inspect?", "I show posting lists with vespa-index-inspect?", "show posting lists with vespa-index-inspect?", "posting lists with vespa-index-inspect?", "lists with vespa-index-inspect?", "with vespa-index-inspect?", "vespa-index-inspect?"], "tokens": ["How", "do", "I", "show", "posting", "lists", "with", "vespa", "index", "inspect", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::660068478", "fields": {"term": "What is the use of showpostings and dumpwords in vespa-index-inspect?", "namespace": "open-p", "hash": 660068478, "terms": ["What is the use of showpostings and dumpwords in vespa-index-inspect?", "is the use of showpostings and dumpwords in vespa-index-inspect?", "the use of showpostings and dumpwords in vespa-index-inspect?", "use of showpostings and dumpwords in vespa-index-inspect?", "of showpostings and dumpwords in vespa-index-inspect?", "showpostings and dumpwords in vespa-index-inspect?", "and dumpwords in vespa-index-inspect?", "dumpwords in vespa-index-inspect?", "in vespa-index-inspect?", "vespa-index-inspect?"], "tokens": ["What", "is", "the", "use", "of", "showpostings", "and", "dumpwords", "in", "vespa", "index", "inspect", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1501054340", "fields": {"term": "What is the syntax for vespa-index-inspect?", "namespace": "open-p", "hash": 1501054340, "terms": ["What is the syntax for vespa-index-inspect?", "is the syntax for vespa-index-inspect?", "the syntax for vespa-index-inspect?", "syntax for vespa-index-inspect?", "for vespa-index-inspect?", "vespa-index-inspect?"], "tokens": ["What", "is", "the", "syntax", "for", "vespa", "index", "inspect", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::408102616", "fields": {"term": "How do I analyze a specific field with vespa-index-inspect?", "namespace": "open-p", "hash": 408102616, "terms": ["How do I analyze a specific field with vespa-index-inspect?", "do I analyze a specific field with vespa-index-inspect?", "I analyze a specific field with vespa-index-inspect?", "analyze a specific field with vespa-index-inspect?", "a specific field with vespa-index-inspect?", "specific field with vespa-index-inspect?", "field with vespa-index-inspect?", "with vespa-index-inspect?", "vespa-index-inspect?"], "tokens": ["How", "do", "I", "analyze", "a", "specific", "field", "with", "vespa", "index", "inspect", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1151490471", "fields": {"term": "What is vespa-jvm-dumper?", "namespace": "open-p", "hash": 1151490471, "terms": ["What is vespa-jvm-dumper?", "is vespa-jvm-dumper?", "vespa-jvm-dumper?"], "tokens": ["What", "is", "vespa", "jvm", "dumper", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::371424623", "fields": {"term": "How do I produce JVM debugging information?", "namespace": "open-p", "hash": 371424623, "terms": ["How do I produce JVM debugging information?", "do I produce JVM debugging information?", "I produce JVM debugging information?", "produce JVM debugging information?", "JVM debugging information?", "debugging information?", "information?"], "tokens": ["How", "do", "I", "produce", "JVM", "debugging", "information", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1890332299", "fields": {"term": "What does vespa-jvm-dumper do?", "namespace": "open-p", "hash": -1890332299, "terms": ["What does vespa-jvm-dumper do?", "does vespa-jvm-dumper do?", "vespa-jvm-dumper do?", "do?"], "tokens": ["What", "does", "vespa", "jvm", "dumper", "do", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1395769806", "fields": {"term": "How do I invoke vespa-jvm-dumper?", "namespace": "open-p", "hash": 1395769806, "terms": ["How do I invoke vespa-jvm-dumper?", "do I invoke vespa-jvm-dumper?", "I invoke vespa-jvm-dumper?", "invoke vespa-jvm-dumper?", "vespa-jvm-dumper?"], "tokens": ["How", "do", "I", "invoke", "vespa", "jvm", "dumper", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1697603308", "fields": {"term": "What are the required arguments for vespa-jvm-dumper?", "namespace": "open-p", "hash": -1697603308, "terms": ["What are the required arguments for vespa-jvm-dumper?", "are the required arguments for vespa-jvm-dumper?", "the required arguments for vespa-jvm-dumper?", "required arguments for vespa-jvm-dumper?", "arguments for vespa-jvm-dumper?", "for vespa-jvm-dumper?", "vespa-jvm-dumper?"], "tokens": ["What", "are", "the", "required", "arguments", "for", "vespa", "jvm", "dumper", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-138885769", "fields": {"term": "Where will the output data be stored after running vespa-jvm-dumper?", "namespace": "open-p", "hash": -138885769, "terms": ["Where will the output data be stored after running vespa-jvm-dumper?", "will the output data be stored after running vespa-jvm-dumper?", "the output data be stored after running vespa-jvm-dumper?", "output data be stored after running vespa-jvm-dumper?", "data be stored after running vespa-jvm-dumper?", "be stored after running vespa-jvm-dumper?", "stored after running vespa-jvm-dumper?", "after running vespa-jvm-dumper?", "running vespa-jvm-dumper?", "vespa-jvm-dumper?"], "tokens": ["Where", "will", "the", "output", "data", "be", "stored", "after", "running", "vespa", "jvm", "dumper", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::476556083", "fields": {"term": "What can I debug with vespa-jvm-dumper?", "namespace": "open-p", "hash": 476556083, "terms": ["What can I debug with vespa-jvm-dumper?", "can I debug with vespa-jvm-dumper?", "I debug with vespa-jvm-dumper?", "debug with vespa-jvm-dumper?", "with vespa-jvm-dumper?", "vespa-jvm-dumper?"], "tokens": ["What", "can", "I", "debug", "with", "vespa", "jvm", "dumper", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2020998591", "fields": {"term": "What is logcontrol file?", "namespace": "open-p", "hash": -2020998591, "terms": ["What is logcontrol file?", "is logcontrol file?", "logcontrol file?", "file?"], "tokens": ["What", "is", "logcontrol", "file", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2096991532", "fields": {"term": "What is level-mods?", "namespace": "open-p", "hash": -2096991532, "terms": ["What is level-mods?", "is level-mods?", "level-mods?"], "tokens": ["What", "is", "level", "mods", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-202109200", "fields": {"term": "What is component-specification?", "namespace": "open-p", "hash": -202109200, "terms": ["What is component-specification?", "is component-specification?", "component-specification?"], "tokens": ["What", "is", "component", "specification", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2067615068", "fields": {"term": "How to set log level?", "namespace": "open-p", "hash": -2067615068, "terms": ["How to set log level?", "to set log level?", "set log level?", "log level?", "level?"], "tokens": ["How", "to", "set", "log", "level", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::330495584", "fields": {"term": "What is the synopsis to show log levels?", "namespace": "open-p", "hash": 330495584, "terms": ["What is the synopsis to show log levels?", "is the synopsis to show log levels?", "the synopsis to show log levels?", "synopsis to show log levels?", "to show log levels?", "show log levels?", "log levels?", "levels?"], "tokens": ["What", "is", "the", "synopsis", "to", "show", "log", "levels", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1123884918", "fields": {"term": "What is the synopsis to set log levels?", "namespace": "open-p", "hash": 1123884918, "terms": ["What is the synopsis to set log levels?", "is the synopsis to set log levels?", "the synopsis to set log levels?", "synopsis to set log levels?", "to set log levels?", "set log levels?", "log levels?", "levels?"], "tokens": ["What", "is", "the", "synopsis", "to", "set", "log", "levels", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1149554568", "fields": {"term": "What does vespa-logfmt do?", "namespace": "open-p", "hash": 1149554568, "terms": ["What does vespa-logfmt do?", "does vespa-logfmt do?", "vespa-logfmt do?", "do?"], "tokens": ["What", "does", "vespa", "logfmt", "do", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1328140011", "fields": {"term": "How to filter messages by log level?", "namespace": "open-p", "hash": -1328140011, "terms": ["How to filter messages by log level?", "to filter messages by log level?", "filter messages by log level?", "messages by log level?", "by log level?", "log level?", "level?"], "tokens": ["How", "to", "filter", "messages", "by", "log", "level", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1209446884", "fields": {"term": "What is an FSA file?", "namespace": "open-p", "hash": 1209446884, "terms": ["What is an FSA file?", "is an FSA file?", "an FSA file?", "FSA file?", "file?"], "tokens": ["What", "is", "an", "FSA", "file", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1031626306", "fields": {"term": "What can be inspected with vespa-model-inspect?", "namespace": "open-p", "hash": -1031626306, "terms": ["What can be inspected with vespa-model-inspect?", "can be inspected with vespa-model-inspect?", "be inspected with vespa-model-inspect?", "inspected with vespa-model-inspect?", "with vespa-model-inspect?", "vespa-model-inspect?"], "tokens": ["What", "can", "be", "inspected", "with", "vespa", "model", "inspect", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-840648814", "fields": {"term": "What are the available commands of vespa-model-inspect?", "namespace": "open-p", "hash": -840648814, "terms": ["What are the available commands of vespa-model-inspect?", "are the available commands of vespa-model-inspect?", "the available commands of vespa-model-inspect?", "available commands of vespa-model-inspect?", "commands of vespa-model-inspect?", "of vespa-model-inspect?", "vespa-model-inspect?"], "tokens": ["What", "are", "the", "available", "commands", "of", "vespa", "model", "inspect", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::663279209", "fields": {"term": "What are the available options of vespa-model-inspect?", "namespace": "open-p", "hash": 663279209, "terms": ["What are the available options of vespa-model-inspect?", "are the available options of vespa-model-inspect?", "the available options of vespa-model-inspect?", "available options of vespa-model-inspect?", "options of vespa-model-inspect?", "of vespa-model-inspect?", "vespa-model-inspect?"], "tokens": ["What", "are", "the", "available", "options", "of", "vespa", "model", "inspect", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2051397985", "fields": {"term": "How to show hosts using vespa-model-inspect?", "namespace": "open-p", "hash": 2051397985, "terms": ["How to show hosts using vespa-model-inspect?", "to show hosts using vespa-model-inspect?", "show hosts using vespa-model-inspect?", "hosts using vespa-model-inspect?", "using vespa-model-inspect?", "vespa-model-inspect?"], "tokens": ["How", "to", "show", "hosts", "using", "vespa", "model", "inspect", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1490428066", "fields": {"term": "How to show all services in a cluster using vespa-model-inspect?", "namespace": "open-p", "hash": -1490428066, "terms": ["How to show all services in a cluster using vespa-model-inspect?", "to show all services in a cluster using vespa-model-inspect?", "show all services in a cluster using vespa-model-inspect?", "all services in a cluster using vespa-model-inspect?", "services in a cluster using vespa-model-inspect?", "in a cluster using vespa-model-inspect?", "a cluster using vespa-model-inspect?", "cluster using vespa-model-inspect?", "using vespa-model-inspect?", "vespa-model-inspect?"], "tokens": ["How", "to", "show", "all", "services", "in", "a", "cluster", "using", "vespa", "model", "inspect", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1017520687", "fields": {"term": "How to show all services using a certain config id using vespa-model-inspect?", "namespace": "open-p", "hash": 1017520687, "terms": ["How to show all services using a certain config id using vespa-model-inspect?", "to show all services using a certain config id using vespa-model-inspect?", "show all services using a certain config id using vespa-model-inspect?", "all services using a certain config id using vespa-model-inspect?", "services using a certain config id using vespa-model-inspect?", "using a certain config id using vespa-model-inspect?", "a certain config id using vespa-model-inspect?", "certain config id using vespa-model-inspect?", "config id using vespa-model-inspect?", "id using vespa-model-inspect?", "using vespa-model-inspect?", "vespa-model-inspect?"], "tokens": ["How", "to", "show", "all", "services", "using", "a", "certain", "config", "id", "using", "vespa", "model", "inspect", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::850977843", "fields": {"term": "How to show all indexes for instances of a service on a given host using vespa-model-inspect?", "namespace": "open-p", "hash": 850977843, "terms": ["How to show all indexes for instances of a service on a given host using vespa-model-inspect?", "to show all indexes for instances of a service on a given host using vespa-model-inspect?", "show all indexes for instances of a service on a given host using vespa-model-inspect?", "all indexes for instances of a service on a given host using vespa-model-inspect?", "indexes for instances of a service on a given host using vespa-model-inspect?", "for instances of a service on a given host using vespa-model-inspect?", "instances of a service on a given host using vespa-model-inspect?", "of a service on a given host using vespa-model-inspect?", "a service on a given host using vespa-model-inspect?", "service on a given host using vespa-model-inspect?", "on a given host using vespa-model-inspect?", "a given host using vespa-model-inspect?", "given host using vespa-model-inspect?", "host using vespa-model-inspect?", "using vespa-model-inspect?", "vespa-model-inspect?"], "tokens": ["How", "to", "show", "all", "indexes", "for", "instances", "of", "a", "service", "on", "a", "given", "host", "using", "vespa", "model", "inspect", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-626218492", "fields": {"term": "What does the internal script do?", "namespace": "open-p", "hash": -626218492, "terms": ["What does the internal script do?", "does the internal script do?", "the internal script do?", "internal script do?", "script do?", "do?"], "tokens": ["What", "does", "the", "internal", "script", "do", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-635553337", "fields": {"term": "What information does the internal script provide?", "namespace": "open-p", "hash": -635553337, "terms": ["What information does the internal script provide?", "information does the internal script provide?", "does the internal script provide?", "the internal script provide?", "internal script provide?", "script provide?", "provide?"], "tokens": ["What", "information", "does", "the", "internal", "script", "provide", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::802332036", "fields": {"term": "Is the internal script for end-users?", "namespace": "open-p", "hash": 802332036, "terms": ["Is the internal script for end-users?", "the internal script for end-users?", "internal script for end-users?", "script for end-users?", "for end-users?", "end-users?"], "tokens": ["Is", "the", "internal", "script", "for", "end", "users", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::152807081", "fields": {"term": "What is vespa-proton-cmd used for?", "namespace": "open-p", "hash": 152807081, "terms": ["What is vespa-proton-cmd used for?", "is vespa-proton-cmd used for?", "vespa-proton-cmd used for?", "used for?", "for?"], "tokens": ["What", "is", "vespa", "proton", "cmd", "used", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1277318679", "fields": {"term": "How do I use vespa-proton-cmd?", "namespace": "open-p", "hash": 1277318679, "terms": ["How do I use vespa-proton-cmd?", "do I use vespa-proton-cmd?", "I use vespa-proton-cmd?", "use vespa-proton-cmd?", "vespa-proton-cmd?"], "tokens": ["How", "do", "I", "use", "vespa", "proton", "cmd", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1932673214", "fields": {"term": "What are the available commands for vespa-proton-cmd?", "namespace": "open-p", "hash": 1932673214, "terms": ["What are the available commands for vespa-proton-cmd?", "are the available commands for vespa-proton-cmd?", "the available commands for vespa-proton-cmd?", "available commands for vespa-proton-cmd?", "commands for vespa-proton-cmd?", "for vespa-proton-cmd?", "vespa-proton-cmd?"], "tokens": ["What", "are", "the", "available", "commands", "for", "vespa", "proton", "cmd", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1869049351", "fields": {"term": "How do I locate the search node ADMIN RPC port?", "namespace": "open-p", "hash": -1869049351, "terms": ["How do I locate the search node ADMIN RPC port?", "do I locate the search node ADMIN RPC port?", "I locate the search node ADMIN RPC port?", "locate the search node ADMIN RPC port?", "the search node ADMIN RPC port?", "search node ADMIN RPC port?", "node ADMIN RPC port?", "ADMIN RPC port?", "RPC port?", "port?"], "tokens": ["How", "do", "I", "locate", "the", "search", "node", "ADMIN", "RPC", "port", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1687551702", "fields": {"term": "What is the command to trigger flush?", "namespace": "open-p", "hash": 1687551702, "terms": ["What is the command to trigger flush?", "is the command to trigger flush?", "the command to trigger flush?", "command to trigger flush?", "to trigger flush?", "trigger flush?", "flush?"], "tokens": ["What", "is", "the", "command", "to", "trigger", "flush", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-129641381", "fields": {"term": "What is the command to get the current proton state and its components?", "namespace": "open-p", "hash": -129641381, "terms": ["What is the command to get the current proton state and its components?", "is the command to get the current proton state and its components?", "the command to get the current proton state and its components?", "command to get the current proton state and its components?", "to get the current proton state and its components?", "get the current proton state and its components?", "the current proton state and its components?", "current proton state and its components?", "proton state and its components?", "state and its components?", "and its components?", "its components?", "components?"], "tokens": ["What", "is", "the", "command", "to", "get", "the", "current", "proton", "state", "and", "its", "components", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1329127023", "fields": {"term": "What is the command to estimate the cost of transaction log replay and flushes data structures?", "namespace": "open-p", "hash": 1329127023, "terms": ["What is the command to estimate the cost of transaction log replay and flushes data structures?", "is the command to estimate the cost of transaction log replay and flushes data structures?", "the command to estimate the cost of transaction log replay and flushes data structures?", "command to estimate the cost of transaction log replay and flushes data structures?", "to estimate the cost of transaction log replay and flushes data structures?", "estimate the cost of transaction log replay and flushes data structures?", "the cost of transaction log replay and flushes data structures?", "cost of transaction log replay and flushes data structures?", "of transaction log replay and flushes data structures?", "transaction log replay and flushes data structures?", "log replay and flushes data structures?", "replay and flushes data structures?", "and flushes data structures?", "flushes data structures?", "data structures?", "structures?"], "tokens": ["What", "is", "the", "command", "to", "estimate", "the", "cost", "of", "transaction", "log", "replay", "and", "flushes", "data", "structures", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1572047393", "fields": {"term": "What is the command to get the current proton state?", "namespace": "open-p", "hash": -1572047393, "terms": ["What is the command to get the current proton state?", "is the command to get the current proton state?", "the command to get the current proton state?", "command to get the current proton state?", "to get the current proton state?", "get the current proton state?", "the current proton state?", "current proton state?", "proton state?", "state?"], "tokens": ["What", "is", "the", "command", "to", "get", "the", "current", "proton", "state", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-366533678", "fields": {"term": "What does vespa-query-profile-dump-tool do?", "namespace": "open-p", "hash": -366533678, "terms": ["What does vespa-query-profile-dump-tool do?", "does vespa-query-profile-dump-tool do?", "vespa-query-profile-dump-tool do?", "do?"], "tokens": ["What", "does", "vespa", "query", "profile", "dump", "tool", "do", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1452712287", "fields": {"term": "What are the options for vespa-query-profile-dump-tool?", "namespace": "open-p", "hash": -1452712287, "terms": ["What are the options for vespa-query-profile-dump-tool?", "are the options for vespa-query-profile-dump-tool?", "the options for vespa-query-profile-dump-tool?", "options for vespa-query-profile-dump-tool?", "for vespa-query-profile-dump-tool?", "vespa-query-profile-dump-tool?"], "tokens": ["What", "are", "the", "options", "for", "vespa", "query", "profile", "dump", "tool", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::295379005", "fields": {"term": "How do I specify a custom output directory for vespa-query-profile-dump-tool?", "namespace": "open-p", "hash": 295379005, "terms": ["How do I specify a custom output directory for vespa-query-profile-dump-tool?", "do I specify a custom output directory for vespa-query-profile-dump-tool?", "I specify a custom output directory for vespa-query-profile-dump-tool?", "specify a custom output directory for vespa-query-profile-dump-tool?", "a custom output directory for vespa-query-profile-dump-tool?", "custom output directory for vespa-query-profile-dump-tool?", "output directory for vespa-query-profile-dump-tool?", "directory for vespa-query-profile-dump-tool?", "for vespa-query-profile-dump-tool?", "vespa-query-profile-dump-tool?"], "tokens": ["How", "do", "I", "specify", "a", "custom", "output", "directory", "for", "vespa", "query", "profile", "dump", "tool", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::248355019", "fields": {"term": "What are dimensions in Vespa query profiles?", "namespace": "open-p", "hash": 248355019, "terms": ["What are dimensions in Vespa query profiles?", "are dimensions in Vespa query profiles?", "dimensions in Vespa query profiles?", "in Vespa query profiles?", "Vespa query profiles?", "query profiles?", "profiles?"], "tokens": ["What", "are", "dimensions", "in", "Vespa", "query", "profiles", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-61399533", "fields": {"term": "How do I specify dimension values for vespa-query-profile-dump-tool?", "namespace": "open-p", "hash": -61399533, "terms": ["How do I specify dimension values for vespa-query-profile-dump-tool?", "do I specify dimension values for vespa-query-profile-dump-tool?", "I specify dimension values for vespa-query-profile-dump-tool?", "specify dimension values for vespa-query-profile-dump-tool?", "dimension values for vespa-query-profile-dump-tool?", "values for vespa-query-profile-dump-tool?", "for vespa-query-profile-dump-tool?", "vespa-query-profile-dump-tool?"], "tokens": ["How", "do", "I", "specify", "dimension", "values", "for", "vespa", "query", "profile", "dump", "tool", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-719822545", "fields": {"term": "What is the default output directory for vespa-query-profile-dump-tool?", "namespace": "open-p", "hash": -719822545, "terms": ["What is the default output directory for vespa-query-profile-dump-tool?", "is the default output directory for vespa-query-profile-dump-tool?", "the default output directory for vespa-query-profile-dump-tool?", "default output directory for vespa-query-profile-dump-tool?", "output directory for vespa-query-profile-dump-tool?", "directory for vespa-query-profile-dump-tool?", "for vespa-query-profile-dump-tool?", "vespa-query-profile-dump-tool?"], "tokens": ["What", "is", "the", "default", "output", "directory", "for", "vespa", "query", "profile", "dump", "tool", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1996565748", "fields": {"term": "What is the format of dimension keys for vespa-query-profile-dump-tool?", "namespace": "open-p", "hash": 1996565748, "terms": ["What is the format of dimension keys for vespa-query-profile-dump-tool?", "is the format of dimension keys for vespa-query-profile-dump-tool?", "the format of dimension keys for vespa-query-profile-dump-tool?", "format of dimension keys for vespa-query-profile-dump-tool?", "of dimension keys for vespa-query-profile-dump-tool?", "dimension keys for vespa-query-profile-dump-tool?", "keys for vespa-query-profile-dump-tool?", "for vespa-query-profile-dump-tool?", "vespa-query-profile-dump-tool?"], "tokens": ["What", "is", "the", "format", "of", "dimension", "keys", "for", "vespa", "query", "profile", "dump", "tool", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-40764593", "fields": {"term": "What is vespa-remove-index tool?", "namespace": "open-p", "hash": -40764593, "terms": ["What is vespa-remove-index tool?", "is vespa-remove-index tool?", "vespa-remove-index tool?", "tool?"], "tokens": ["What", "is", "vespa", "remove", "index", "tool", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::487179118", "fields": {"term": "What does vespa-remove-index do?", "namespace": "open-p", "hash": 487179118, "terms": ["What does vespa-remove-index do?", "does vespa-remove-index do?", "vespa-remove-index do?", "do?"], "tokens": ["What", "does", "vespa", "remove", "index", "do", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-954983184", "fields": {"term": "How can I delete Vespa's index data?", "namespace": "open-p", "hash": -954983184, "terms": ["How can I delete Vespa's index data?", "can I delete Vespa's index data?", "I delete Vespa's index data?", "delete Vespa's index data?", "Vespa's index data?", "index data?", "data?"], "tokens": ["How", "can", "I", "delete", "Vespa", "'", "s", "index", "data", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::241097269", "fields": {"term": "Is vespa-remove-index reversible?", "namespace": "open-p", "hash": 241097269, "terms": ["Is vespa-remove-index reversible?", "vespa-remove-index reversible?", "reversible?"], "tokens": ["Is", "vespa", "remove", "index", "reversible", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::407991782", "fields": {"term": "Can recovered lost Vespa's indexes?", "namespace": "open-p", "hash": 407991782, "terms": ["Can recovered lost Vespa's indexes?", "recovered lost Vespa's indexes?", "lost Vespa's indexes?", "Vespa's indexes?", "indexes?"], "tokens": ["Can", "recovered", "lost", "Vespa", "'", "s", "indexes", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::27905781", "fields": {"term": "What are the options for vespa-remove-index?", "namespace": "open-p", "hash": 27905781, "terms": ["What are the options for vespa-remove-index?", "are the options for vespa-remove-index?", "the options for vespa-remove-index?", "options for vespa-remove-index?", "for vespa-remove-index?", "vespa-remove-index?"], "tokens": ["What", "are", "the", "options", "for", "vespa", "remove", "index", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::955215339", "fields": {"term": "How do I remove index data for a specific cluster using vespa-remove-index?", "namespace": "open-p", "hash": 955215339, "terms": ["How do I remove index data for a specific cluster using vespa-remove-index?", "do I remove index data for a specific cluster using vespa-remove-index?", "I remove index data for a specific cluster using vespa-remove-index?", "remove index data for a specific cluster using vespa-remove-index?", "index data for a specific cluster using vespa-remove-index?", "data for a specific cluster using vespa-remove-index?", "for a specific cluster using vespa-remove-index?", "a specific cluster using vespa-remove-index?", "specific cluster using vespa-remove-index?", "cluster using vespa-remove-index?", "using vespa-remove-index?", "vespa-remove-index?"], "tokens": ["How", "do", "I", "remove", "index", "data", "for", "a", "specific", "cluster", "using", "vespa", "remove", "index", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-26423427", "fields": {"term": "What is vespa-route?", "namespace": "open-p", "hash": -26423427, "terms": ["What is vespa-route?", "is vespa-route?", "vespa-route?"], "tokens": ["What", "is", "vespa", "route", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2111267110", "fields": {"term": "What does vespa-route inspect?", "namespace": "open-p", "hash": -2111267110, "terms": ["What does vespa-route inspect?", "does vespa-route inspect?", "vespa-route inspect?", "inspect?"], "tokens": ["What", "does", "vespa", "route", "inspect", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1689412660", "fields": {"term": "What options does vespa-route have?", "namespace": "open-p", "hash": -1689412660, "terms": ["What options does vespa-route have?", "options does vespa-route have?", "does vespa-route have?", "vespa-route have?", "have?"], "tokens": ["What", "options", "does", "vespa", "route", "have", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-117631567", "fields": {"term": "What is connectivity command?", "namespace": "open-p", "hash": -117631567, "terms": ["What is connectivity command?", "is connectivity command?", "connectivity command?", "command?"], "tokens": ["What", "is", "connectivity", "command", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1253102531", "fields": {"term": "How do I stop a service?", "namespace": "open-p", "hash": -1253102531, "terms": ["How do I stop a service?", "do I stop a service?", "I stop a service?", "stop a service?", "a service?", "service?"], "tokens": ["How", "do", "I", "stop", "a", "service", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::18753486", "fields": {"term": "What is 'vespa-set-node-state'?", "namespace": "open-p", "hash": 18753486, "terms": ["What is 'vespa-set-node-state'?", "is 'vespa-set-node-state'?", "'vespa-set-node-state'?"], "tokens": ["What", "is", "'", "vespa", "set", "node", "state", "'?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-879876160", "fields": {"term": "What does setting user state do?", "namespace": "open-p", "hash": -879876160, "terms": ["What does setting user state do?", "does setting user state do?", "setting user state do?", "user state do?", "state do?", "do?"], "tokens": ["What", "does", "setting", "user", "state", "do", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::963021281", "fields": {"term": "What are the options for node state?", "namespace": "open-p", "hash": 963021281, "terms": ["What are the options for node state?", "are the options for node state?", "the options for node state?", "options for node state?", "for node state?", "node state?", "state?"], "tokens": ["What", "are", "the", "options", "for", "node", "state", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1355336128", "fields": {"term": "What is 'vespa-set-node-state' used for?", "namespace": "open-p", "hash": -1355336128, "terms": ["What is 'vespa-set-node-state' used for?", "is 'vespa-set-node-state' used for?", "'vespa-set-node-state' used for?", "used for?", "for?"], "tokens": ["What", "is", "'", "vespa", "set", "node", "state", "'", "used", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2000230984", "fields": {"term": "What is forced into maintenance state?", "namespace": "open-p", "hash": 2000230984, "terms": ["What is forced into maintenance state?", "is forced into maintenance state?", "forced into maintenance state?", "into maintenance state?", "maintenance state?", "state?"], "tokens": ["What", "is", "forced", "into", "maintenance", "state", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1646123603", "fields": {"term": "What is vespa-start-services?", "namespace": "open-p", "hash": 1646123603, "terms": ["What is vespa-start-services?", "is vespa-start-services?", "vespa-start-services?"], "tokens": ["What", "is", "vespa", "start", "services", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::903189659", "fields": {"term": "How do I start all services on a node?", "namespace": "open-p", "hash": 903189659, "terms": ["How do I start all services on a node?", "do I start all services on a node?", "I start all services on a node?", "start all services on a node?", "all services on a node?", "services on a node?", "on a node?", "a node?", "node?"], "tokens": ["How", "do", "I", "start", "all", "services", "on", "a", "node", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1765950334", "fields": {"term": "How do I debug a failed start?", "namespace": "open-p", "hash": -1765950334, "terms": ["How do I debug a failed start?", "do I debug a failed start?", "I debug a failed start?", "debug a failed start?", "a failed start?", "failed start?", "start?"], "tokens": ["How", "do", "I", "debug", "a", "failed", "start", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-306167692", "fields": {"term": "What is the Safe Cluster Startup mode?", "namespace": "open-p", "hash": -306167692, "terms": ["What is the Safe Cluster Startup mode?", "is the Safe Cluster Startup mode?", "the Safe Cluster Startup mode?", "Safe Cluster Startup mode?", "Cluster Startup mode?", "Startup mode?", "mode?"], "tokens": ["What", "is", "the", "Safe", "Cluster", "Startup", "mode", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-403316478", "fields": {"term": "What is vespa-stat?", "namespace": "open-p", "hash": -403316478, "terms": ["What is vespa-stat?", "is vespa-stat?", "vespa-stat?"], "tokens": ["What", "is", "vespa", "stat", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-857226048", "fields": {"term": "How does vespa-stat work?", "namespace": "open-p", "hash": -857226048, "terms": ["How does vespa-stat work?", "does vespa-stat work?", "vespa-stat work?", "work?"], "tokens": ["How", "does", "vespa", "stat", "work", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-44356041", "fields": {"term": "What are the stages of vespa-stat?", "namespace": "open-p", "hash": -44356041, "terms": ["What are the stages of vespa-stat?", "are the stages of vespa-stat?", "the stages of vespa-stat?", "stages of vespa-stat?", "of vespa-stat?", "vespa-stat?"], "tokens": ["What", "are", "the", "stages", "of", "vespa", "stat", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1350738327", "fields": {"term": "What options does vespa-stat have?", "namespace": "open-p", "hash": -1350738327, "terms": ["What options does vespa-stat have?", "options does vespa-stat have?", "does vespa-stat have?", "vespa-stat have?", "have?"], "tokens": ["What", "options", "does", "vespa", "stat", "have", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::132226706", "fields": {"term": "What does vespa-status-filedistribution do?", "namespace": "open-p", "hash": 132226706, "terms": ["What does vespa-status-filedistribution do?", "does vespa-status-filedistribution do?", "vespa-status-filedistribution do?", "do?"], "tokens": ["What", "does", "vespa", "status", "filedistribution", "do", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1494863508", "fields": {"term": "Where should vespa-status-filedistribution be run?", "namespace": "open-p", "hash": 1494863508, "terms": ["Where should vespa-status-filedistribution be run?", "should vespa-status-filedistribution be run?", "vespa-status-filedistribution be run?", "be run?", "run?"], "tokens": ["Where", "should", "vespa", "status", "filedistribution", "be", "run", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1527769559", "fields": {"term": "What are the options for vespa-status-filedistribution?", "namespace": "open-p", "hash": 1527769559, "terms": ["What are the options for vespa-status-filedistribution?", "are the options for vespa-status-filedistribution?", "the options for vespa-status-filedistribution?", "options for vespa-status-filedistribution?", "for vespa-status-filedistribution?", "vespa-status-filedistribution?"], "tokens": ["What", "are", "the", "options", "for", "vespa", "status", "filedistribution", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::773319383", "fields": {"term": "What information does vespa-status-filedistribution retrieve from the config server?", "namespace": "open-p", "hash": 773319383, "terms": ["What information does vespa-status-filedistribution retrieve from the config server?", "information does vespa-status-filedistribution retrieve from the config server?", "does vespa-status-filedistribution retrieve from the config server?", "vespa-status-filedistribution retrieve from the config server?", "retrieve from the config server?", "from the config server?", "the config server?", "config server?", "server?"], "tokens": ["What", "information", "does", "vespa", "status", "filedistribution", "retrieve", "from", "the", "config", "server", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::930183167", "fields": {"term": "How do I stop a config server on a node?", "namespace": "open-p", "hash": 930183167, "terms": ["How do I stop a config server on a node?", "do I stop a config server on a node?", "I stop a config server on a node?", "stop a config server on a node?", "a config server on a node?", "config server on a node?", "server on a node?", "on a node?", "a node?", "node?"], "tokens": ["How", "do", "I", "stop", "a", "config", "server", "on", "a", "node", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-699556073", "fields": {"term": "How do I stop Vespa services on a node?", "namespace": "open-p", "hash": -699556073, "terms": ["How do I stop Vespa services on a node?", "do I stop Vespa services on a node?", "I stop Vespa services on a node?", "stop Vespa services on a node?", "Vespa services on a node?", "services on a node?", "on a node?", "a node?", "node?"], "tokens": ["How", "do", "I", "stop", "Vespa", "services", "on", "a", "node", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1526444833", "fields": {"term": "What does vespa-stop-services do?", "namespace": "open-p", "hash": -1526444833, "terms": ["What does vespa-stop-services do?", "does vespa-stop-services do?", "vespa-stop-services do?", "do?"], "tokens": ["What", "does", "vespa", "stop", "services", "do", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1490127364", "fields": {"term": "What is rpc docsum interface?", "namespace": "open-p", "hash": -1490127364, "terms": ["What is rpc docsum interface?", "is rpc docsum interface?", "rpc docsum interface?", "docsum interface?", "interface?"], "tokens": ["What", "is", "rpc", "docsum", "interface", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::841279535", "fields": {"term": "What is VespaSummaryBenchmark?", "namespace": "open-p", "hash": 841279535, "terms": ["What is VespaSummaryBenchmark?", "is VespaSummaryBenchmark?", "VespaSummaryBenchmark?"], "tokens": ["What", "is", "VespaSummaryBenchmark", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2032254054", "fields": {"term": "What does VespaSummaryBenchmark test?", "namespace": "open-p", "hash": -2032254054, "terms": ["What does VespaSummaryBenchmark test?", "does VespaSummaryBenchmark test?", "VespaSummaryBenchmark test?", "test?"], "tokens": ["What", "does", "VespaSummaryBenchmark", "test", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-432578248", "fields": {"term": "What does VespaSummaryBenchmark benchmark?", "namespace": "open-p", "hash": -432578248, "terms": ["What does VespaSummaryBenchmark benchmark?", "does VespaSummaryBenchmark benchmark?", "VespaSummaryBenchmark benchmark?", "benchmark?"], "tokens": ["What", "does", "VespaSummaryBenchmark", "benchmark", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-715174090", "fields": {"term": "Where can I find VespaSummaryBenchmark.java?", "namespace": "open-p", "hash": -715174090, "terms": ["Where can I find VespaSummaryBenchmark.java?", "can I find VespaSummaryBenchmark.java?", "I find VespaSummaryBenchmark.java?", "find VespaSummaryBenchmark.java?", "VespaSummaryBenchmark.java?"], "tokens": ["Where", "can", "I", "find", "VespaSummaryBenchmark", "java", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-949649483", "fields": {"term": "What is vespa-visit?", "namespace": "open-p", "hash": -949649483, "terms": ["What is vespa-visit?", "is vespa-visit?", "vespa-visit?"], "tokens": ["What", "is", "vespa", "visit", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::-349250457", "fields": {"term": "How does vespa-visit work?", "namespace": "open-p", "hash": -349250457, "terms": ["How does vespa-visit work?", "does vespa-visit work?", "vespa-visit work?", "work?"], "tokens": ["How", "does", "vespa", "visit", "work", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2027739372", "fields": {"term": "How to use vespa-visit to migrate documents?", "namespace": "open-p", "hash": -2027739372, "terms": ["How to use vespa-visit to migrate documents?", "to use vespa-visit to migrate documents?", "use vespa-visit to migrate documents?", "vespa-visit to migrate documents?", "to migrate documents?", "migrate documents?", "documents?"], "tokens": ["How", "to", "use", "vespa", "visit", "to", "migrate", "documents", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::232165043", "fields": {"term": "What is the purpose of remove-entries?", "namespace": "open-p", "hash": 232165043, "terms": ["What is the purpose of remove-entries?", "is the purpose of remove-entries?", "the purpose of remove-entries?", "purpose of remove-entries?", "of remove-entries?", "remove-entries?"], "tokens": ["What", "is", "the", "purpose", "of", "remove", "entries", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::826343837", "fields": {"term": "How to include remove-entries in vespa-visit?", "namespace": "open-p", "hash": 826343837, "terms": ["How to include remove-entries in vespa-visit?", "to include remove-entries in vespa-visit?", "include remove-entries in vespa-visit?", "remove-entries in vespa-visit?", "in vespa-visit?", "vespa-visit?"], "tokens": ["How", "to", "include", "remove", "entries", "in", "vespa", "visit", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-426055357", "fields": {"term": "How to specify datahandler?", "namespace": "open-p", "hash": -426055357, "terms": ["How to specify datahandler?", "to specify datahandler?", "specify datahandler?", "datahandler?"], "tokens": ["How", "to", "specify", "datahandler", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::249060517", "fields": {"term": "What is the datahandler?", "namespace": "open-p", "hash": 249060517, "terms": ["What is the datahandler?", "is the datahandler?", "the datahandler?", "datahandler?"], "tokens": ["What", "is", "the", "datahandler", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1573867777", "fields": {"term": "What is a dump visitor used for?", "namespace": "open-p", "hash": 1573867777, "terms": ["What is a dump visitor used for?", "is a dump visitor used for?", "a dump visitor used for?", "dump visitor used for?", "visitor used for?", "used for?", "for?"], "tokens": ["What", "is", "a", "dump", "visitor", "used", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::395304205", "fields": {"term": "What are the target types you can specify?", "namespace": "open-p", "hash": 395304205, "terms": ["What are the target types you can specify?", "are the target types you can specify?", "the target types you can specify?", "target types you can specify?", "types you can specify?", "you can specify?", "can specify?", "specify?"], "tokens": ["What", "are", "the", "target", "types", "you", "can", "specify", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1242544910", "fields": {"term": "What is a slobrok address?", "namespace": "open-p", "hash": -1242544910, "terms": ["What is a slobrok address?", "is a slobrok address?", "a slobrok address?", "slobrok address?", "address?"], "tokens": ["What", "is", "a", "slobrok", "address", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-554227308", "fields": {"term": "What is a TCP socket?", "namespace": "open-p", "hash": -554227308, "terms": ["What is a TCP socket?", "is a TCP socket?", "a TCP socket?", "TCP socket?", "socket?"], "tokens": ["What", "is", "a", "TCP", "socket", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::618244777", "fields": {"term": "What options can you specify in vespa-visit command?", "namespace": "open-p", "hash": 618244777, "terms": ["What options can you specify in vespa-visit command?", "options can you specify in vespa-visit command?", "can you specify in vespa-visit command?", "you specify in vespa-visit command?", "specify in vespa-visit command?", "in vespa-visit command?", "vespa-visit command?", "command?"], "tokens": ["What", "options", "can", "you", "specify", "in", "vespa", "visit", "command", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1734967577", "fields": {"term": "What is vespa-visit-target used for?", "namespace": "open-p", "hash": 1734967577, "terms": ["What is vespa-visit-target used for?", "is vespa-visit-target used for?", "vespa-visit-target used for?", "used for?", "for?"], "tokens": ["What", "is", "vespa", "visit", "target", "used", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1291737431", "fields": {"term": "What is JSONL?", "namespace": "open-p", "hash": -1291737431, "terms": ["What is JSONL?", "is JSONL?", "JSONL?"], "tokens": ["What", "is", "JSONL", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::608879182", "fields": {"term": "What is dynamic throttling?", "namespace": "open-p", "hash": 608879182, "terms": ["What is dynamic throttling?", "is dynamic throttling?", "dynamic throttling?", "throttling?"], "tokens": ["What", "is", "dynamic", "throttling", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::872109506", "fields": {"term": "What is Maven Central?", "namespace": "open-p", "hash": 872109506, "terms": ["What is Maven Central?", "is Maven Central?", "Maven Central?", "Central?"], "tokens": ["What", "is", "Maven", "Central", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1937668431", "fields": {"term": "How to install vespa-clients package?", "namespace": "open-p", "hash": -1937668431, "terms": ["How to install vespa-clients package?", "to install vespa-clients package?", "install vespa-clients package?", "vespa-clients package?", "package?"], "tokens": ["How", "to", "install", "vespa", "clients", "package", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1764954810", "fields": {"term": "Where to download vespa-feed-client zip?", "namespace": "open-p", "hash": -1764954810, "terms": ["Where to download vespa-feed-client zip?", "to download vespa-feed-client zip?", "download vespa-feed-client zip?", "vespa-feed-client zip?", "zip?"], "tokens": ["Where", "to", "download", "vespa", "feed", "client", "zip", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1610124221", "fields": {"term": "What is recommended for security?", "namespace": "open-p", "hash": 1610124221, "terms": ["What is recommended for security?", "is recommended for security?", "recommended for security?", "for security?", "security?"], "tokens": ["What", "is", "recommended", "for", "security", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1216724584", "fields": {"term": "What is the purpose of ca-certificates-file?", "namespace": "open-p", "hash": 1216724584, "terms": ["What is the purpose of ca-certificates-file?", "is the purpose of ca-certificates-file?", "the purpose of ca-certificates-file?", "purpose of ca-certificates-file?", "of ca-certificates-file?", "ca-certificates-file?"], "tokens": ["What", "is", "the", "purpose", "of", "ca", "certificates", "file", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1781964663", "fields": {"term": "Where can I find Javadoc for the programmatic API?", "namespace": "open-p", "hash": 1781964663, "terms": ["Where can I find Javadoc for the programmatic API?", "can I find Javadoc for the programmatic API?", "I find Javadoc for the programmatic API?", "find Javadoc for the programmatic API?", "Javadoc for the programmatic API?", "for the programmatic API?", "the programmatic API?", "programmatic API?", "API?"], "tokens": ["Where", "can", "I", "find", "Javadoc", "for", "the", "programmatic", "API", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1973439714", "fields": {"term": "What is the command for running a bandwidth test?", "namespace": "open-p", "hash": -1973439714, "terms": ["What is the command for running a bandwidth test?", "is the command for running a bandwidth test?", "the command for running a bandwidth test?", "command for running a bandwidth test?", "for running a bandwidth test?", "running a bandwidth test?", "a bandwidth test?", "bandwidth test?", "test?"], "tokens": ["What", "is", "the", "command", "for", "running", "a", "bandwidth", "test", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1323687134", "fields": {"term": "What is the Maven dependency for vespa-feed-client?", "namespace": "open-p", "hash": -1323687134, "terms": ["What is the Maven dependency for vespa-feed-client?", "is the Maven dependency for vespa-feed-client?", "the Maven dependency for vespa-feed-client?", "Maven dependency for vespa-feed-client?", "dependency for vespa-feed-client?", "for vespa-feed-client?", "vespa-feed-client?"], "tokens": ["What", "is", "the", "Maven", "dependency", "for", "vespa", "feed", "client", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1240656740", "fields": {"term": "Where can I find code examples for vespa-feed-client?", "namespace": "open-p", "hash": 1240656740, "terms": ["Where can I find code examples for vespa-feed-client?", "can I find code examples for vespa-feed-client?", "I find code examples for vespa-feed-client?", "find code examples for vespa-feed-client?", "code examples for vespa-feed-client?", "examples for vespa-feed-client?", "for vespa-feed-client?", "vespa-feed-client?"], "tokens": ["Where", "can", "I", "find", "code", "examples", "for", "vespa", "feed", "client", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::424612640", "fields": {"term": "What is the latest version of the vespa-feed-client dependency?", "namespace": "open-p", "hash": 424612640, "terms": ["What is the latest version of the vespa-feed-client dependency?", "is the latest version of the vespa-feed-client dependency?", "the latest version of the vespa-feed-client dependency?", "latest version of the vespa-feed-client dependency?", "version of the vespa-feed-client dependency?", "of the vespa-feed-client dependency?", "the vespa-feed-client dependency?", "vespa-feed-client dependency?", "dependency?"], "tokens": ["What", "is", "the", "latest", "version", "of", "the", "vespa", "feed", "client", "dependency", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1457373505", "fields": {"term": "What is the Vespa feed client?", "namespace": "open-p", "hash": 1457373505, "terms": ["What is the Vespa feed client?", "is the Vespa feed client?", "the Vespa feed client?", "Vespa feed client?", "feed client?", "client?"], "tokens": ["What", "is", "the", "Vespa", "feed", "client", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1232915231", "fields": {"term": "What are the parameters for the Vespa feed client?", "namespace": "open-p", "hash": 1232915231, "terms": ["What are the parameters for the Vespa feed client?", "are the parameters for the Vespa feed client?", "the parameters for the Vespa feed client?", "parameters for the Vespa feed client?", "for the Vespa feed client?", "the Vespa feed client?", "Vespa feed client?", "feed client?", "client?"], "tokens": ["What", "are", "the", "parameters", "for", "the", "Vespa", "feed", "client", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-256899079", "fields": {"term": "What is the format for document operations in Vespa feed client?", "namespace": "open-p", "hash": -256899079, "terms": ["What is the format for document operations in Vespa feed client?", "is the format for document operations in Vespa feed client?", "the format for document operations in Vespa feed client?", "format for document operations in Vespa feed client?", "for document operations in Vespa feed client?", "document operations in Vespa feed client?", "operations in Vespa feed client?", "in Vespa feed client?", "Vespa feed client?", "feed client?", "client?"], "tokens": ["What", "is", "the", "format", "for", "document", "operations", "in", "Vespa", "feed", "client", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1309845109", "fields": {"term": "What is the NVIDIA container toolkit installation guide?", "namespace": "open-p", "hash": 1309845109, "terms": ["What is the NVIDIA container toolkit installation guide?", "is the NVIDIA container toolkit installation guide?", "the NVIDIA container toolkit installation guide?", "NVIDIA container toolkit installation guide?", "container toolkit installation guide?", "toolkit installation guide?", "installation guide?", "guide?"], "tokens": ["What", "is", "the", "NVIDIA", "container", "toolkit", "installation", "guide", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1042627478", "fields": {"term": "How does Vespa support using GPUs?", "namespace": "open-p", "hash": 1042627478, "terms": ["How does Vespa support using GPUs?", "does Vespa support using GPUs?", "Vespa support using GPUs?", "support using GPUs?", "using GPUs?", "GPUs?"], "tokens": ["How", "does", "Vespa", "support", "using", "GPUs", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1892282898", "fields": {"term": "What is special configuration required for GPU(s) inside container?", "namespace": "open-p", "hash": -1892282898, "terms": ["What is special configuration required for GPU(s) inside container?", "is special configuration required for GPU(s) inside container?", "special configuration required for GPU(s) inside container?", "configuration required for GPU(s) inside container?", "required for GPU(s) inside container?", "for GPU(s) inside container?", "GPU(s) inside container?", "inside container?", "container?"], "tokens": ["What", "is", "special", "configuration", "required", "for", "GPU", "(", "s", ")", "inside", "container", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::537212141", "fields": {"term": "Which container engines are supported by Vespa?", "namespace": "open-p", "hash": 537212141, "terms": ["Which container engines are supported by Vespa?", "container engines are supported by Vespa?", "engines are supported by Vespa?", "are supported by Vespa?", "supported by Vespa?", "by Vespa?", "Vespa?"], "tokens": ["Which", "container", "engines", "are", "supported", "by", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1783566948", "fields": {"term": "How to make GPU(s) available inside container?", "namespace": "open-p", "hash": 1783566948, "terms": ["How to make GPU(s) available inside container?", "to make GPU(s) available inside container?", "make GPU(s) available inside container?", "GPU(s) available inside container?", "available inside container?", "inside container?", "container?"], "tokens": ["How", "to", "make", "GPU", "(", "s", ")", "available", "inside", "container", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-736257787", "fields": {"term": "How to install Nvidia drivers?", "namespace": "open-p", "hash": -736257787, "terms": ["How to install Nvidia drivers?", "to install Nvidia drivers?", "install Nvidia drivers?", "Nvidia drivers?", "drivers?"], "tokens": ["How", "to", "install", "Nvidia", "drivers", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2124499792", "fields": {"term": "How to expose GPU to container?", "namespace": "open-p", "hash": -2124499792, "terms": ["How to expose GPU to container?", "to expose GPU to container?", "expose GPU to container?", "GPU to container?", "to container?", "container?"], "tokens": ["How", "to", "expose", "GPU", "to", "container", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1301767961", "fields": {"term": "How to configure ONNX runtime?", "namespace": "open-p", "hash": 1301767961, "terms": ["How to configure ONNX runtime?", "to configure ONNX runtime?", "configure ONNX runtime?", "ONNX runtime?", "runtime?"], "tokens": ["How", "to", "configure", "ONNX", "runtime", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-66243080", "fields": {"term": "How to install Vespa with Docker?", "namespace": "open-p", "hash": -66243080, "terms": ["How to install Vespa with Docker?", "to install Vespa with Docker?", "install Vespa with Docker?", "Vespa with Docker?", "with Docker?", "Docker?"], "tokens": ["How", "to", "install", "Vespa", "with", "Docker", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::-768725696", "fields": {"term": "What does the guide show?", "namespace": "open-p", "hash": -768725696, "terms": ["What does the guide show?", "does the guide show?", "the guide show?", "guide show?", "show?"], "tokens": ["What", "does", "the", "guide", "show", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::-426995268", "fields": {"term": "How to start a Vespa Docker container?", "namespace": "open-p", "hash": -426995268, "terms": ["How to start a Vespa Docker container?", "to start a Vespa Docker container?", "start a Vespa Docker container?", "a Vespa Docker container?", "Vespa Docker container?", "Docker container?", "container?"], "tokens": ["How", "to", "start", "a", "Vespa", "Docker", "container", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-42290726", "fields": {"term": "How to get started with Vespa?", "namespace": "open-p", "hash": -42290726, "terms": ["How to get started with Vespa?", "to get started with Vespa?", "get started with Vespa?", "started with Vespa?", "with Vespa?", "Vespa?"], "tokens": ["How", "to", "get", "started", "with", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-63075263", "fields": {"term": "What is the link for getting started with Vespa?", "namespace": "open-p", "hash": -63075263, "terms": ["What is the link for getting started with Vespa?", "is the link for getting started with Vespa?", "the link for getting started with Vespa?", "link for getting started with Vespa?", "for getting started with Vespa?", "getting started with Vespa?", "started with Vespa?", "with Vespa?", "Vespa?"], "tokens": ["What", "is", "the", "link", "for", "getting", "started", "with", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1511235009", "fields": {"term": "How to install Vespa on Kubernetes?", "namespace": "open-p", "hash": -1511235009, "terms": ["How to install Vespa on Kubernetes?", "to install Vespa on Kubernetes?", "install Vespa on Kubernetes?", "Vespa on Kubernetes?", "on Kubernetes?", "Kubernetes?"], "tokens": ["How", "to", "install", "Vespa", "on", "Kubernetes", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1568954737", "fields": {"term": "What is VESPA_SAMPLE_APPS?", "namespace": "open-p", "hash": 1568954737, "terms": ["What is VESPA_SAMPLE_APPS?", "is VESPA_SAMPLE_APPS?", "VESPA_SAMPLE_APPS?"], "tokens": ["What", "is", "VESPA_SAMPLE_APPS", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1774873237", "fields": {"term": "What is the statefulset.yml?", "namespace": "open-p", "hash": 1774873237, "terms": ["What is the statefulset.yml?", "is the statefulset.yml?", "the statefulset.yml?", "statefulset.yml?"], "tokens": ["What", "is", "the", "statefulset", "yml", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::387838176", "fields": {"term": "How to start port forwarding?", "namespace": "open-p", "hash": 387838176, "terms": ["How to start port forwarding?", "to start port forwarding?", "start port forwarding?", "port forwarding?", "forwarding?"], "tokens": ["How", "to", "start", "port", "forwarding", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2007584019", "fields": {"term": "How to set local target?", "namespace": "open-p", "hash": -2007584019, "terms": ["How to set local target?", "to set local target?", "set local target?", "local target?", "target?"], "tokens": ["How", "to", "set", "local", "target", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-148000650", "fields": {"term": "What is Vespa Docker container?", "namespace": "open-p", "hash": -148000650, "terms": ["What is Vespa Docker container?", "is Vespa Docker container?", "Vespa Docker container?", "Docker container?", "container?"], "tokens": ["What", "is", "Vespa", "Docker", "container", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::496280146", "fields": {"term": "What is cluster-controller.up.count?", "namespace": "open-p", "hash": 496280146, "terms": ["What is cluster-controller.up.count?", "is cluster-controller.up.count?", "cluster-controller.up.count?"], "tokens": ["What", "is", "cluster", "controller", "up", "count", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1175010370", "fields": {"term": "What does cluster-controller.resource_usage.nodes_above_limit capture?", "namespace": "open-p", "hash": 1175010370, "terms": ["What does cluster-controller.resource_usage.nodes_above_limit capture?", "does cluster-controller.resource_usage.nodes_above_limit capture?", "cluster-controller.resource_usage.nodes_above_limit capture?", "capture?"], "tokens": ["What", "does", "cluster", "controller", "resource_usage", "nodes_above_limit", "capture", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-623796558", "fields": {"term": "What does cluster-controller.remote-task-queue.size measure?", "namespace": "open-p", "hash": -623796558, "terms": ["What does cluster-controller.remote-task-queue.size measure?", "does cluster-controller.remote-task-queue.size measure?", "cluster-controller.remote-task-queue.size measure?", "measure?"], "tokens": ["What", "does", "cluster", "controller", "remote", "task", "queue", "size", "measure", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1509573081", "fields": {"term": "What is reindexing.progress?", "namespace": "open-p", "hash": -1509573081, "terms": ["What is reindexing.progress?", "is reindexing.progress?", "reindexing.progress?"], "tokens": ["What", "is", "reindexing", "progress", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::943971138", "fields": {"term": "What is cluster-controller.initializing.count?", "namespace": "open-p", "hash": 943971138, "terms": ["What is cluster-controller.initializing.count?", "is cluster-controller.initializing.count?", "cluster-controller.initializing.count?"], "tokens": ["What", "is", "cluster", "controller", "initializing", "count", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1639617774", "fields": {"term": "What is configserver.zkAvgLatency?", "namespace": "open-p", "hash": -1639617774, "terms": ["What is configserver.zkAvgLatency?", "is configserver.zkAvgLatency?", "configserver.zkAvgLatency?"], "tokens": ["What", "is", "configserver", "zkAvgLatency", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2005205624", "fields": {"term": "What is configserver.cacheConfigElems?", "namespace": "open-p", "hash": -2005205624, "terms": ["What is configserver.cacheConfigElems?", "is configserver.cacheConfigElems?", "configserver.cacheConfigElems?"], "tokens": ["What", "is", "configserver", "cacheConfigElems", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-121453607", "fields": {"term": "What is configserver.failedRequests?", "namespace": "open-p", "hash": -121453607, "terms": ["What is configserver.failedRequests?", "is configserver.failedRequests?", "configserver.failedRequests?"], "tokens": ["What", "is", "configserver", "failedRequests", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1590461175", "fields": {"term": "What is configserver.delayedResponses?", "namespace": "open-p", "hash": 1590461175, "terms": ["What is configserver.delayedResponses?", "is configserver.delayedResponses?", "configserver.delayedResponses?"], "tokens": ["What", "is", "configserver", "delayedResponses", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1456568326", "fields": {"term": "What is mem.heap.total?", "namespace": "open-p", "hash": -1456568326, "terms": ["What is mem.heap.total?", "is mem.heap.total?", "mem.heap.total?"], "tokens": ["What", "is", "mem", "heap", "total", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1653413537", "fields": {"term": "What is http.status.2xx?", "namespace": "open-p", "hash": -1653413537, "terms": ["What is http.status.2xx?", "is http.status.2xx?", "http.status.2xx?"], "tokens": ["What", "is", "http", "status", "2xx", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1737294036", "fields": {"term": "What is search_connections?", "namespace": "open-p", "hash": -1737294036, "terms": ["What is search_connections?", "is search_connections?", "search_connections?"], "tokens": ["What", "is", "search_connections", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1174644872", "fields": {"term": "What is totalhits_per_query?", "namespace": "open-p", "hash": -1174644872, "terms": ["What is totalhits_per_query?", "is totalhits_per_query?", "totalhits_per_query?"], "tokens": ["What", "is", "totalhits_per_query", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-573577997", "fields": {"term": "What is error.backend_communication_error?", "namespace": "open-p", "hash": -573577997, "terms": ["What is error.backend_communication_error?", "is error.backend_communication_error?", "error.backend_communication_error?"], "tokens": ["What", "is", "error", "backend_communication_error", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-985878676", "fields": {"term": "What is vds.idealstate.buckets_rechecking?", "namespace": "open-p", "hash": -985878676, "terms": ["What is vds.idealstate.buckets_rechecking?", "is vds.idealstate.buckets_rechecking?", "vds.idealstate.buckets_rechecking?"], "tokens": ["What", "is", "vds", "idealstate", "buckets_rechecking", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::193001944", "fields": {"term": "What is the unit of vds.distributor.puts.latency?", "namespace": "open-p", "hash": 193001944, "terms": ["What is the unit of vds.distributor.puts.latency?", "is the unit of vds.distributor.puts.latency?", "the unit of vds.distributor.puts.latency?", "unit of vds.distributor.puts.latency?", "of vds.distributor.puts.latency?", "vds.distributor.puts.latency?"], "tokens": ["What", "is", "the", "unit", "of", "vds", "distributor", "puts", "latency", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1274625339", "fields": {"term": "What is vds.distributor.gets.ok?", "namespace": "open-p", "hash": -1274625339, "terms": ["What is vds.distributor.gets.ok?", "is vds.distributor.gets.ok?", "vds.distributor.gets.ok?"], "tokens": ["What", "is", "vds", "distributor", "gets", "ok", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1484609252", "fields": {"term": "What is vds.distributor.docsstored?", "namespace": "open-p", "hash": -1484609252, "terms": ["What is vds.distributor.docsstored?", "is vds.distributor.docsstored?", "vds.distributor.docsstored?"], "tokens": ["What", "is", "vds", "distributor", "docsstored", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-439635829", "fields": {"term": "What is vds.idealstate.max_observed_time_since_last_gc_sec?", "namespace": "open-p", "hash": -439635829, "terms": ["What is vds.idealstate.max_observed_time_since_last_gc_sec?", "is vds.idealstate.max_observed_time_since_last_gc_sec?", "vds.idealstate.max_observed_time_since_last_gc_sec?"], "tokens": ["What", "is", "vds", "idealstate", "max_observed_time_since_last_gc_sec", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-102581899", "fields": {"term": "What is vds.distributor.puts.failures.total?", "namespace": "open-p", "hash": -102581899, "terms": ["What is vds.distributor.puts.failures.total?", "is vds.distributor.puts.failures.total?", "vds.distributor.puts.failures.total?"], "tokens": ["What", "is", "vds", "distributor", "puts", "failures", "total", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1416100985", "fields": {"term": "What is vds.distributor.visitor.latency?", "namespace": "open-p", "hash": 1416100985, "terms": ["What is vds.distributor.visitor.latency?", "is vds.distributor.visitor.latency?", "vds.distributor.visitor.latency?"], "tokens": ["What", "is", "vds", "distributor", "visitor", "latency", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2130629295", "fields": {"term": "What is vds.bouncer.clock_skew_aborts?", "namespace": "open-p", "hash": 2130629295, "terms": ["What is vds.bouncer.clock_skew_aborts?", "is vds.bouncer.clock_skew_aborts?", "vds.bouncer.clock_skew_aborts?"], "tokens": ["What", "is", "vds", "bouncer", "clock_skew_aborts", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::662163475", "fields": {"term": "What is node-certificate.expiry.seconds?", "namespace": "open-p", "hash": 662163475, "terms": ["What is node-certificate.expiry.seconds?", "is node-certificate.expiry.seconds?", "node-certificate.expiry.seconds?"], "tokens": ["What", "is", "node", "certificate", "expiry", "seconds", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1268889306", "fields": {"term": "What is the unit for endpoint.certificate.expiry.seconds?", "namespace": "open-p", "hash": 1268889306, "terms": ["What is the unit for endpoint.certificate.expiry.seconds?", "is the unit for endpoint.certificate.expiry.seconds?", "the unit for endpoint.certificate.expiry.seconds?", "unit for endpoint.certificate.expiry.seconds?", "for endpoint.certificate.expiry.seconds?", "endpoint.certificate.expiry.seconds?"], "tokens": ["What", "is", "the", "unit", "for", "endpoint", "certificate", "expiry", "seconds", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1128407372", "fields": {"term": "What is N/A in suffixes?", "namespace": "open-p", "hash": -1128407372, "terms": ["What is N/A in suffixes?", "is N/A in suffixes?", "N/A in suffixes?", "in suffixes?", "suffixes?"], "tokens": ["What", "is", "N", "/", "A", "in", "suffixes", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2099367024", "fields": {"term": "What is the description of node-certificate.expiry.seconds?", "namespace": "open-p", "hash": 2099367024, "terms": ["What is the description of node-certificate.expiry.seconds?", "is the description of node-certificate.expiry.seconds?", "the description of node-certificate.expiry.seconds?", "description of node-certificate.expiry.seconds?", "of node-certificate.expiry.seconds?", "node-certificate.expiry.seconds?"], "tokens": ["What", "is", "the", "description", "of", "node", "certificate", "expiry", "seconds", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1877181829", "fields": {"term": "What is sentinel.restarts in Vespa?", "namespace": "open-p", "hash": -1877181829, "terms": ["What is sentinel.restarts in Vespa?", "is sentinel.restarts in Vespa?", "sentinel.restarts in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["What", "is", "sentinel", "restarts", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-554883248", "fields": {"term": "What does sentinel.restarts count?", "namespace": "open-p", "hash": -554883248, "terms": ["What does sentinel.restarts count?", "does sentinel.restarts count?", "sentinel.restarts count?", "count?"], "tokens": ["What", "does", "sentinel", "restarts", "count", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-213003222", "fields": {"term": "What is sentinel.totalRestarts' unit?", "namespace": "open-p", "hash": -213003222, "terms": ["What is sentinel.totalRestarts' unit?", "is sentinel.totalRestarts' unit?", "sentinel.totalRestarts' unit?", "unit?"], "tokens": ["What", "is", "sentinel", "totalRestarts", "'", "unit", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-282633672", "fields": {"term": "What is the purpose of 'suffixes' in Vespa metrics?", "namespace": "open-p", "hash": -282633672, "terms": ["What is the purpose of 'suffixes' in Vespa metrics?", "is the purpose of 'suffixes' in Vespa metrics?", "the purpose of 'suffixes' in Vespa metrics?", "purpose of 'suffixes' in Vespa metrics?", "of 'suffixes' in Vespa metrics?", "'suffixes' in Vespa metrics?", "in Vespa metrics?", "Vespa metrics?", "metrics?"], "tokens": ["What", "is", "the", "purpose", "of", "'", "suffixes", "'", "in", "Vespa", "metrics", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-300908439", "fields": {"term": "What unit is used for slobrok.heartbeats.failed?", "namespace": "open-p", "hash": -300908439, "terms": ["What unit is used for slobrok.heartbeats.failed?", "unit is used for slobrok.heartbeats.failed?", "is used for slobrok.heartbeats.failed?", "used for slobrok.heartbeats.failed?", "for slobrok.heartbeats.failed?", "slobrok.heartbeats.failed?"], "tokens": ["What", "unit", "is", "used", "for", "slobrok", "heartbeats", "failed", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1559711037", "fields": {"term": "What does slobrok.heartbeats.failed count?", "namespace": "open-p", "hash": -1559711037, "terms": ["What does slobrok.heartbeats.failed count?", "does slobrok.heartbeats.failed count?", "slobrok.heartbeats.failed count?", "count?"], "tokens": ["What", "does", "slobrok", "heartbeats", "failed", "count", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-13666994", "fields": {"term": "What does slobrok.missing.consensus measure?", "namespace": "open-p", "hash": -13666994, "terms": ["What does slobrok.missing.consensus measure?", "does slobrok.missing.consensus measure?", "slobrok.missing.consensus measure?", "measure?"], "tokens": ["What", "does", "slobrok", "missing", "consensus", "measure", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::945869728", "fields": {"term": "What is the unit of slobrok.missing.consensus?", "namespace": "open-p", "hash": 945869728, "terms": ["What is the unit of slobrok.missing.consensus?", "is the unit of slobrok.missing.consensus?", "the unit of slobrok.missing.consensus?", "unit of slobrok.missing.consensus?", "of slobrok.missing.consensus?", "slobrok.missing.consensus?"], "tokens": ["What", "is", "the", "unit", "of", "slobrok", "missing", "consensus", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2084354721", "fields": {"term": "What does slobrok.missing.consensus count?", "namespace": "open-p", "hash": -2084354721, "terms": ["What does slobrok.missing.consensus count?", "does slobrok.missing.consensus count?", "slobrok.missing.consensus count?", "count?"], "tokens": ["What", "does", "slobrok", "missing", "consensus", "count", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1853445402", "fields": {"term": "What is the definition of slobrok.heartbeats.failed?", "namespace": "open-p", "hash": 1853445402, "terms": ["What is the definition of slobrok.heartbeats.failed?", "is the definition of slobrok.heartbeats.failed?", "the definition of slobrok.heartbeats.failed?", "definition of slobrok.heartbeats.failed?", "of slobrok.heartbeats.failed?", "slobrok.heartbeats.failed?"], "tokens": ["What", "is", "the", "definition", "of", "slobrok", "heartbeats", "failed", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1852303892", "fields": {"term": "What is the definition of slobrok.missing.consensus?", "namespace": "open-p", "hash": 1852303892, "terms": ["What is the definition of slobrok.missing.consensus?", "is the definition of slobrok.missing.consensus?", "the definition of slobrok.missing.consensus?", "definition of slobrok.missing.consensus?", "of slobrok.missing.consensus?", "slobrok.missing.consensus?"], "tokens": ["What", "is", "the", "definition", "of", "slobrok", "missing", "consensus", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1854093750", "fields": {"term": "What are the changes between Vespa 6 and 7?", "namespace": "open-p", "hash": 1854093750, "terms": ["What are the changes between Vespa 6 and 7?", "are the changes between Vespa 6 and 7?", "the changes between Vespa 6 and 7?", "changes between Vespa 6 and 7?", "between Vespa 6 and 7?", "Vespa 6 and 7?", "6 and 7?", "and 7?", "7?"], "tokens": ["What", "are", "the", "changes", "between", "Vespa", "6", "and", "7", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-91698930", "fields": {"term": "What is the purpose of major versions in Vespa?", "namespace": "open-p", "hash": -91698930, "terms": ["What is the purpose of major versions in Vespa?", "is the purpose of major versions in Vespa?", "the purpose of major versions in Vespa?", "purpose of major versions in Vespa?", "of major versions in Vespa?", "major versions in Vespa?", "versions in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["What", "is", "the", "purpose", "of", "major", "versions", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2041157414", "fields": {"term": "What is considered breaking compatibility?", "namespace": "open-p", "hash": -2041157414, "terms": ["What is considered breaking compatibility?", "is considered breaking compatibility?", "considered breaking compatibility?", "breaking compatibility?", "compatibility?"], "tokens": ["What", "is", "considered", "breaking", "compatibility", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::303569183", "fields": {"term": "How many tasks are necessary for compatibility with Vespa 7?", "namespace": "open-p", "hash": 303569183, "terms": ["How many tasks are necessary for compatibility with Vespa 7?", "many tasks are necessary for compatibility with Vespa 7?", "tasks are necessary for compatibility with Vespa 7?", "are necessary for compatibility with Vespa 7?", "necessary for compatibility with Vespa 7?", "for compatibility with Vespa 7?", "compatibility with Vespa 7?", "with Vespa 7?", "Vespa 7?", "7?"], "tokens": ["How", "many", "tasks", "are", "necessary", "for", "compatibility", "with", "Vespa", "7", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1868254910", "fields": {"term": "What is meant by removal of deprecated functionality?", "namespace": "open-p", "hash": -1868254910, "terms": ["What is meant by removal of deprecated functionality?", "is meant by removal of deprecated functionality?", "meant by removal of deprecated functionality?", "by removal of deprecated functionality?", "removal of deprecated functionality?", "of deprecated functionality?", "deprecated functionality?", "functionality?"], "tokens": ["What", "is", "meant", "by", "removal", "of", "deprecated", "functionality", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1403149233", "fields": {"term": "What should be done to be compatible with Vespa 7?", "namespace": "open-p", "hash": 1403149233, "terms": ["What should be done to be compatible with Vespa 7?", "should be done to be compatible with Vespa 7?", "be done to be compatible with Vespa 7?", "done to be compatible with Vespa 7?", "to be compatible with Vespa 7?", "be compatible with Vespa 7?", "compatible with Vespa 7?", "with Vespa 7?", "Vespa 7?", "7?"], "tokens": ["What", "should", "be", "done", "to", "be", "compatible", "with", "Vespa", "7", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2098145280", "fields": {"term": "How can deprecation warnings be emitted?", "namespace": "open-p", "hash": 2098145280, "terms": ["How can deprecation warnings be emitted?", "can deprecation warnings be emitted?", "deprecation warnings be emitted?", "warnings be emitted?", "be emitted?", "emitted?"], "tokens": ["How", "can", "deprecation", "warnings", "be", "emitted", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-399786837", "fields": {"term": "What is the significance of changes to defaults in Vespa 7?", "namespace": "open-p", "hash": -399786837, "terms": ["What is the significance of changes to defaults in Vespa 7?", "is the significance of changes to defaults in Vespa 7?", "the significance of changes to defaults in Vespa 7?", "significance of changes to defaults in Vespa 7?", "of changes to defaults in Vespa 7?", "changes to defaults in Vespa 7?", "to defaults in Vespa 7?", "defaults in Vespa 7?", "in Vespa 7?", "Vespa 7?", "7?"], "tokens": ["What", "is", "the", "significance", "of", "changes", "to", "defaults", "in", "Vespa", "7", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1625841728", "fields": {"term": "What is Vespa 7?", "namespace": "open-p", "hash": -1625841728, "terms": ["What is Vespa 7?", "is Vespa 7?", "Vespa 7?", "7?"], "tokens": ["What", "is", "Vespa", "7", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::505276713", "fields": {"term": "What are the changes in stemming?", "namespace": "open-p", "hash": 505276713, "terms": ["What are the changes in stemming?", "are the changes in stemming?", "the changes in stemming?", "changes in stemming?", "in stemming?", "stemming?"], "tokens": ["What", "are", "the", "changes", "in", "stemming", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::710077328", "fields": {"term": "What is the new default linguistics component?", "namespace": "open-p", "hash": 710077328, "terms": ["What is the new default linguistics component?", "is the new default linguistics component?", "the new default linguistics component?", "new default linguistics component?", "default linguistics component?", "linguistics component?", "component?"], "tokens": ["What", "is", "the", "new", "default", "linguistics", "component", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-263080205", "fields": {"term": "What is the new default format for the Java HTTP client?", "namespace": "open-p", "hash": -263080205, "terms": ["What is the new default format for the Java HTTP client?", "is the new default format for the Java HTTP client?", "the new default format for the Java HTTP client?", "new default format for the Java HTTP client?", "default format for the Java HTTP client?", "format for the Java HTTP client?", "for the Java HTTP client?", "the Java HTTP client?", "Java HTTP client?", "HTTP client?", "client?"], "tokens": ["What", "is", "the", "new", "default", "format", "for", "the", "Java", "HTTP", "client", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::321820790", "fields": {"term": "What is ranking.softtimeout.enable status?", "namespace": "open-p", "hash": 321820790, "terms": ["What is ranking.softtimeout.enable status?", "is ranking.softtimeout.enable status?", "ranking.softtimeout.enable status?", "status?"], "tokens": ["What", "is", "ranking", "softtimeout", "enable", "status", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1574731171", "fields": {"term": "What is the default return format for vespa-visit and vespa-get?", "namespace": "open-p", "hash": -1574731171, "terms": ["What is the default return format for vespa-visit and vespa-get?", "is the default return format for vespa-visit and vespa-get?", "the default return format for vespa-visit and vespa-get?", "default return format for vespa-visit and vespa-get?", "return format for vespa-visit and vespa-get?", "format for vespa-visit and vespa-get?", "for vespa-visit and vespa-get?", "vespa-visit and vespa-get?", "and vespa-get?", "vespa-get?"], "tokens": ["What", "is", "the", "default", "return", "format", "for", "vespa", "visit", "and", "vespa", "get", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-820309364", "fields": {"term": "Why must Java components be rebuilt?", "namespace": "open-p", "hash": -820309364, "terms": ["Why must Java components be rebuilt?", "must Java components be rebuilt?", "Java components be rebuilt?", "components be rebuilt?", "be rebuilt?", "rebuilt?"], "tokens": ["Why", "must", "Java", "components", "be", "rebuilt", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-358586115", "fields": {"term": "What is JDK 11?", "namespace": "open-p", "hash": -358586115, "terms": ["What is JDK 11?", "is JDK 11?", "JDK 11?", "11?"], "tokens": ["What", "is", "JDK", "11", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1940789217", "fields": {"term": "What is deprecated in Vespa 6?", "namespace": "open-p", "hash": 1940789217, "terms": ["What is deprecated in Vespa 6?", "is deprecated in Vespa 6?", "deprecated in Vespa 6?", "in Vespa 6?", "Vespa 6?", "6?"], "tokens": ["What", "is", "deprecated", "in", "Vespa", "6", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1811834538", "fields": {"term": "What happens if you get deprecation warnings?", "namespace": "open-p", "hash": 1811834538, "terms": ["What happens if you get deprecation warnings?", "happens if you get deprecation warnings?", "if you get deprecation warnings?", "you get deprecation warnings?", "get deprecation warnings?", "deprecation warnings?", "warnings?"], "tokens": ["What", "happens", "if", "you", "get", "deprecation", "warnings", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1077076924", "fields": {"term": "How to fix Vespa API deprecation?", "namespace": "open-p", "hash": 1077076924, "terms": ["How to fix Vespa API deprecation?", "to fix Vespa API deprecation?", "fix Vespa API deprecation?", "Vespa API deprecation?", "API deprecation?", "deprecation?"], "tokens": ["How", "to", "fix", "Vespa", "API", "deprecation", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-425914373", "fields": {"term": "Which artifacts are no longer provided?", "namespace": "open-p", "hash": -425914373, "terms": ["Which artifacts are no longer provided?", "artifacts are no longer provided?", "are no longer provided?", "no longer provided?", "longer provided?", "provided?"], "tokens": ["Which", "artifacts", "are", "no", "longer", "provided", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::953802445", "fields": {"term": "What HTTP APIs are removed?", "namespace": "open-p", "hash": 953802445, "terms": ["What HTTP APIs are removed?", "HTTP APIs are removed?", "APIs are removed?", "are removed?", "removed?"], "tokens": ["What", "HTTP", "APIs", "are", "removed", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1083093550", "fields": {"term": "Which API replaced the legacy HTTP APIs?", "namespace": "open-p", "hash": 1083093550, "terms": ["Which API replaced the legacy HTTP APIs?", "API replaced the legacy HTTP APIs?", "replaced the legacy HTTP APIs?", "the legacy HTTP APIs?", "legacy HTTP APIs?", "HTTP APIs?", "APIs?"], "tokens": ["Which", "API", "replaced", "the", "legacy", "HTTP", "APIs", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1464704036", "fields": {"term": "What HTTP parameters were removed?", "namespace": "open-p", "hash": 1464704036, "terms": ["What HTTP parameters were removed?", "HTTP parameters were removed?", "parameters were removed?", "were removed?", "removed?"], "tokens": ["What", "HTTP", "parameters", "were", "removed", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1404023895", "fields": {"term": "What is the replacement for defidx parameter?", "namespace": "open-p", "hash": 1404023895, "terms": ["What is the replacement for defidx parameter?", "is the replacement for defidx parameter?", "the replacement for defidx parameter?", "replacement for defidx parameter?", "for defidx parameter?", "defidx parameter?", "parameter?"], "tokens": ["What", "is", "the", "replacement", "for", "defidx", "parameter", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1564639819", "fields": {"term": "What command line tools are removed?", "namespace": "open-p", "hash": -1564639819, "terms": ["What command line tools are removed?", "command line tools are removed?", "line tools are removed?", "tools are removed?", "are removed?", "removed?"], "tokens": ["What", "command", "line", "tools", "are", "removed", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::213210339", "fields": {"term": "What settings are removed?", "namespace": "open-p", "hash": 213210339, "terms": ["What settings are removed?", "settings are removed?", "are removed?", "removed?"], "tokens": ["What", "settings", "are", "removed", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1775487682", "fields": {"term": "What is header setting for?", "namespace": "open-p", "hash": -1775487682, "terms": ["What is header setting for?", "is header setting for?", "header setting for?", "setting for?", "for?"], "tokens": ["What", "is", "header", "setting", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1925904155", "fields": {"term": "What tags are removed?", "namespace": "open-p", "hash": -1925904155, "terms": ["What tags are removed?", "tags are removed?", "are removed?", "removed?"], "tokens": ["What", "tags", "are", "removed", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1816037224", "fields": {"term": "What attributes are removed?", "namespace": "open-p", "hash": 1816037224, "terms": ["What attributes are removed?", "attributes are removed?", "are removed?", "removed?"], "tokens": ["What", "attributes", "are", "removed", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::786776406", "fields": {"term": "What is the replacement for ?", "namespace": "open-p", "hash": 786776406, "terms": ["What is the replacement for ?", "is the replacement for ?", "the replacement for ?", "replacement for ?", "for ?", "?"], "tokens": ["What", "is", "the", "replacement", "for", "<", "container", "><", "filter", ">?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::286940971", "fields": {"term": "What are the metrics removed?", "namespace": "open-p", "hash": 286940971, "terms": ["What are the metrics removed?", "are the metrics removed?", "the metrics removed?", "metrics removed?", "removed?"], "tokens": ["What", "are", "the", "metrics", "removed", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1822101993", "fields": {"term": "What is the replacement for the free/used/totalMemoryBytes metric?", "namespace": "open-p", "hash": 1822101993, "terms": ["What is the replacement for the free/used/totalMemoryBytes metric?", "is the replacement for the free/used/totalMemoryBytes metric?", "the replacement for the free/used/totalMemoryBytes metric?", "replacement for the free/used/totalMemoryBytes metric?", "for the free/used/totalMemoryBytes metric?", "the free/used/totalMemoryBytes metric?", "free/used/totalMemoryBytes metric?", "metric?"], "tokens": ["What", "is", "the", "replacement", "for", "the", "free", "/", "used", "/", "totalMemoryBytes", "metric", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::697917792", "fields": {"term": "What is the replacement for http.in.bytes?", "namespace": "open-p", "hash": 697917792, "terms": ["What is the replacement for http.in.bytes?", "is the replacement for http.in.bytes?", "the replacement for http.in.bytes?", "replacement for http.in.bytes?", "for http.in.bytes?", "http.in.bytes?"], "tokens": ["What", "is", "the", "replacement", "for", "http", "in", "bytes", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::805356308", "fields": {"term": "What is the replacement for http.requests?", "namespace": "open-p", "hash": 805356308, "terms": ["What is the replacement for http.requests?", "is the replacement for http.requests?", "the replacement for http.requests?", "replacement for http.requests?", "for http.requests?", "http.requests?"], "tokens": ["What", "is", "the", "replacement", "for", "http", "requests", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::98444876", "fields": {"term": "What is the replacement for http.latency?", "namespace": "open-p", "hash": 98444876, "terms": ["What is the replacement for http.latency?", "is the replacement for http.latency?", "the replacement for http.latency?", "replacement for http.latency?", "for http.latency?", "http.latency?"], "tokens": ["What", "is", "the", "replacement", "for", "http", "latency", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2030070156", "fields": {"term": "What is VDS filestor.spi?", "namespace": "open-p", "hash": -2030070156, "terms": ["What is VDS filestor.spi?", "is VDS filestor.spi?", "VDS filestor.spi?", "filestor.spi?"], "tokens": ["What", "is", "VDS", "filestor", "spi", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::580559725", "fields": {"term": "What are the metrics that have different structure and names in new namespace?", "namespace": "open-p", "hash": 580559725, "terms": ["What are the metrics that have different structure and names in new namespace?", "are the metrics that have different structure and names in new namespace?", "the metrics that have different structure and names in new namespace?", "metrics that have different structure and names in new namespace?", "that have different structure and names in new namespace?", "have different structure and names in new namespace?", "different structure and names in new namespace?", "structure and names in new namespace?", "and names in new namespace?", "names in new namespace?", "in new namespace?", "new namespace?", "namespace?"], "tokens": ["What", "are", "the", "metrics", "that", "have", "different", "structure", "and", "names", "in", "new", "namespace", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1043208299", "fields": {"term": "What happens to empty fields?", "namespace": "open-p", "hash": 1043208299, "terms": ["What happens to empty fields?", "happens to empty fields?", "to empty fields?", "empty fields?", "fields?"], "tokens": ["What", "happens", "to", "empty", "fields", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2143025187", "fields": {"term": "Which Vespa version excludes empty fields?", "namespace": "open-p", "hash": -2143025187, "terms": ["Which Vespa version excludes empty fields?", "Vespa version excludes empty fields?", "version excludes empty fields?", "excludes empty fields?", "empty fields?", "fields?"], "tokens": ["Which", "Vespa", "version", "excludes", "empty", "fields", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1877996488", "fields": {"term": "What characters require percent-encoding in Vespa 7?", "namespace": "open-p", "hash": -1877996488, "terms": ["What characters require percent-encoding in Vespa 7?", "characters require percent-encoding in Vespa 7?", "require percent-encoding in Vespa 7?", "percent-encoding in Vespa 7?", "in Vespa 7?", "Vespa 7?", "7?"], "tokens": ["What", "characters", "require", "percent", "encoding", "in", "Vespa", "7", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1719664912", "fields": {"term": "What did Vespa 6 allow for special characters?", "namespace": "open-p", "hash": 1719664912, "terms": ["What did Vespa 6 allow for special characters?", "did Vespa 6 allow for special characters?", "Vespa 6 allow for special characters?", "6 allow for special characters?", "allow for special characters?", "for special characters?", "special characters?", "characters?"], "tokens": ["What", "did", "Vespa", "6", "allow", "for", "special", "characters", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1281078426", "fields": {"term": "What is the position field?", "namespace": "open-p", "hash": -1281078426, "terms": ["What is the position field?", "is the position field?", "the position field?", "position field?", "field?"], "tokens": ["What", "is", "the", "position", "field", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::170431353", "fields": {"term": "What is the default JSON query result format?", "namespace": "open-p", "hash": 170431353, "terms": ["What is the default JSON query result format?", "is the default JSON query result format?", "the default JSON query result format?", "default JSON query result format?", "JSON query result format?", "query result format?", "result format?", "format?"], "tokens": ["What", "is", "the", "default", "JSON", "query", "result", "format", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1882873100", "fields": {"term": "What was the rendering format for position fields in Vespa 6?", "namespace": "open-p", "hash": 1882873100, "terms": ["What was the rendering format for position fields in Vespa 6?", "was the rendering format for position fields in Vespa 6?", "the rendering format for position fields in Vespa 6?", "rendering format for position fields in Vespa 6?", "format for position fields in Vespa 6?", "for position fields in Vespa 6?", "position fields in Vespa 6?", "fields in Vespa 6?", "in Vespa 6?", "Vespa 6?", "6?"], "tokens": ["What", "was", "the", "rendering", "format", "for", "position", "fields", "in", "Vespa", "6", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::904448246", "fields": {"term": "What is the new rendering format for position fields?", "namespace": "open-p", "hash": 904448246, "terms": ["What is the new rendering format for position fields?", "is the new rendering format for position fields?", "the new rendering format for position fields?", "new rendering format for position fields?", "rendering format for position fields?", "format for position fields?", "for position fields?", "position fields?", "fields?"], "tokens": ["What", "is", "the", "new", "rendering", "format", "for", "position", "fields", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-7283893", "fields": {"term": "What are renamed Vespa metrics?", "namespace": "open-p", "hash": -7283893, "terms": ["What are renamed Vespa metrics?", "are renamed Vespa metrics?", "renamed Vespa metrics?", "Vespa metrics?", "metrics?"], "tokens": ["What", "are", "renamed", "Vespa", "metrics", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::62562726", "fields": {"term": "Which metric relates to query latency?", "namespace": "open-p", "hash": 62562726, "terms": ["Which metric relates to query latency?", "metric relates to query latency?", "relates to query latency?", "to query latency?", "query latency?", "latency?"], "tokens": ["Which", "metric", "relates", "to", "query", "latency", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::628107449", "fields": {"term": "What is the metric for documents_active?", "namespace": "open-p", "hash": 628107449, "terms": ["What is the metric for documents_active?", "is the metric for documents_active?", "the metric for documents_active?", "metric for documents_active?", "for documents_active?", "documents_active?"], "tokens": ["What", "is", "the", "metric", "for", "documents_active", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1240606527", "fields": {"term": "What is the metric for configserver requests?", "namespace": "open-p", "hash": 1240606527, "terms": ["What is the metric for configserver requests?", "is the metric for configserver requests?", "the metric for configserver requests?", "metric for configserver requests?", "for configserver requests?", "configserver requests?", "requests?"], "tokens": ["What", "is", "the", "metric", "for", "configserver", "requests", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::581743299", "fields": {"term": "What metric is related to error.backend_communication_error?", "namespace": "open-p", "hash": 581743299, "terms": ["What metric is related to error.backend_communication_error?", "metric is related to error.backend_communication_error?", "is related to error.backend_communication_error?", "related to error.backend_communication_error?", "to error.backend_communication_error?", "error.backend_communication_error?"], "tokens": ["What", "metric", "is", "related", "to", "error", "backend_communication_error", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1292193306", "fields": {"term": "What metric is related to content.proton.memoryusage.max?", "namespace": "open-p", "hash": 1292193306, "terms": ["What metric is related to content.proton.memoryusage.max?", "metric is related to content.proton.memoryusage.max?", "is related to content.proton.memoryusage.max?", "related to content.proton.memoryusage.max?", "to content.proton.memoryusage.max?", "content.proton.memoryusage.max?"], "tokens": ["What", "metric", "is", "related", "to", "content", "proton", "memoryusage", "max", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::538364657", "fields": {"term": "What is Vespa 8 release notes?", "namespace": "open-p", "hash": 538364657, "terms": ["What is Vespa 8 release notes?", "is Vespa 8 release notes?", "Vespa 8 release notes?", "8 release notes?", "release notes?", "notes?"], "tokens": ["What", "is", "Vespa", "8", "release", "notes", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::139593017", "fields": {"term": "How to migrate from Vespa 7 to Vespa 8 with position fields?", "namespace": "open-p", "hash": 139593017, "terms": ["How to migrate from Vespa 7 to Vespa 8 with position fields?", "to migrate from Vespa 7 to Vespa 8 with position fields?", "migrate from Vespa 7 to Vespa 8 with position fields?", "from Vespa 7 to Vespa 8 with position fields?", "Vespa 7 to Vespa 8 with position fields?", "7 to Vespa 8 with position fields?", "to Vespa 8 with position fields?", "Vespa 8 with position fields?", "8 with position fields?", "with position fields?", "position fields?", "fields?"], "tokens": ["How", "to", "migrate", "from", "Vespa", "7", "to", "Vespa", "8", "with", "position", "fields", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-752451656", "fields": {"term": "For which application is this guide relevant?", "namespace": "open-p", "hash": -752451656, "terms": ["For which application is this guide relevant?", "which application is this guide relevant?", "application is this guide relevant?", "is this guide relevant?", "this guide relevant?", "guide relevant?", "relevant?"], "tokens": ["For", "which", "application", "is", "this", "guide", "relevant", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2002745938", "fields": {"term": "What are the changes between Vespa 7 and 8?", "namespace": "open-p", "hash": 2002745938, "terms": ["What are the changes between Vespa 7 and 8?", "are the changes between Vespa 7 and 8?", "the changes between Vespa 7 and 8?", "changes between Vespa 7 and 8?", "between Vespa 7 and 8?", "Vespa 7 and 8?", "7 and 8?", "and 8?", "8?"], "tokens": ["What", "are", "the", "changes", "between", "Vespa", "7", "and", "8", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-290619041", "fields": {"term": "What marks a major Vespa release?", "namespace": "open-p", "hash": -290619041, "terms": ["What marks a major Vespa release?", "marks a major Vespa release?", "a major Vespa release?", "major Vespa release?", "Vespa release?", "release?"], "tokens": ["What", "marks", "a", "major", "Vespa", "release", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-266680348", "fields": {"term": "Is Vespa 8 as safe as Vespa 7?", "namespace": "open-p", "hash": -266680348, "terms": ["Is Vespa 8 as safe as Vespa 7?", "Vespa 8 as safe as Vespa 7?", "8 as safe as Vespa 7?", "as safe as Vespa 7?", "safe as Vespa 7?", "as Vespa 7?", "Vespa 7?", "7?"], "tokens": ["Is", "Vespa", "8", "as", "safe", "as", "Vespa", "7", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-613569780", "fields": {"term": "Will there be further releases of Vespa 7?", "namespace": "open-p", "hash": -613569780, "terms": ["Will there be further releases of Vespa 7?", "there be further releases of Vespa 7?", "be further releases of Vespa 7?", "further releases of Vespa 7?", "releases of Vespa 7?", "of Vespa 7?", "Vespa 7?", "7?"], "tokens": ["Will", "there", "be", "further", "releases", "of", "Vespa", "7", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1635203359", "fields": {"term": "What is introduced in Vespa minor versions?", "namespace": "open-p", "hash": 1635203359, "terms": ["What is introduced in Vespa minor versions?", "is introduced in Vespa minor versions?", "introduced in Vespa minor versions?", "in Vespa minor versions?", "Vespa minor versions?", "minor versions?", "versions?"], "tokens": ["What", "is", "introduced", "in", "Vespa", "minor", "versions", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-431311358", "fields": {"term": "What are the compatibility breaking changes in Vespa 8?", "namespace": "open-p", "hash": -431311358, "terms": ["What are the compatibility breaking changes in Vespa 8?", "are the compatibility breaking changes in Vespa 8?", "the compatibility breaking changes in Vespa 8?", "compatibility breaking changes in Vespa 8?", "breaking changes in Vespa 8?", "changes in Vespa 8?", "in Vespa 8?", "Vespa 8?", "8?"], "tokens": ["What", "are", "the", "compatibility", "breaking", "changes", "in", "Vespa", "8", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1949019089", "fields": {"term": "What are the categories of changes?", "namespace": "open-p", "hash": 1949019089, "terms": ["What are the categories of changes?", "are the categories of changes?", "the categories of changes?", "categories of changes?", "of changes?", "changes?"], "tokens": ["What", "are", "the", "categories", "of", "changes", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1010703002", "fields": {"term": "What are the changes to default behaviour?", "namespace": "open-p", "hash": -1010703002, "terms": ["What are the changes to default behaviour?", "are the changes to default behaviour?", "the changes to default behaviour?", "changes to default behaviour?", "to default behaviour?", "default behaviour?", "behaviour?"], "tokens": ["What", "are", "the", "changes", "to", "default", "behaviour", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1573895432", "fields": {"term": "What are the Java API changes?", "namespace": "open-p", "hash": 1573895432, "terms": ["What are the Java API changes?", "are the Java API changes?", "the Java API changes?", "Java API changes?", "API changes?", "changes?"], "tokens": ["What", "are", "the", "Java", "API", "changes", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1833855038", "fields": {"term": "What are the HTTP API changes?", "namespace": "open-p", "hash": -1833855038, "terms": ["What are the HTTP API changes?", "are the HTTP API changes?", "the HTTP API changes?", "HTTP API changes?", "API changes?", "changes?"], "tokens": ["What", "are", "the", "HTTP", "API", "changes", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1846086851", "fields": {"term": "What are the security related changes?", "namespace": "open-p", "hash": -1846086851, "terms": ["What are the security related changes?", "are the security related changes?", "the security related changes?", "security related changes?", "related changes?", "changes?"], "tokens": ["What", "are", "the", "security", "related", "changes", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-36670920", "fields": {"term": "What changes may break clients?", "namespace": "open-p", "hash": -36670920, "terms": ["What changes may break clients?", "changes may break clients?", "may break clients?", "break clients?", "clients?"], "tokens": ["What", "changes", "may", "break", "clients", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1064456120", "fields": {"term": "How to avoid breaking changes?", "namespace": "open-p", "hash": -1064456120, "terms": ["How to avoid breaking changes?", "to avoid breaking changes?", "avoid breaking changes?", "breaking changes?", "changes?"], "tokens": ["How", "to", "avoid", "breaking", "changes", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::335766842", "fields": {"term": "What defaults have changed?", "namespace": "open-p", "hash": 335766842, "terms": ["What defaults have changed?", "defaults have changed?", "have changed?", "changed?"], "tokens": ["What", "defaults", "have", "changed", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::195354243", "fields": {"term": "What fields rendering has changed?", "namespace": "open-p", "hash": 195354243, "terms": ["What fields rendering has changed?", "fields rendering has changed?", "rendering has changed?", "has changed?", "changed?"], "tokens": ["What", "fields", "rendering", "has", "changed", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1922546136", "fields": {"term": "What is default fieldset?", "namespace": "open-p", "hash": -1922546136, "terms": ["What is default fieldset?", "is default fieldset?", "default fieldset?", "fieldset?"], "tokens": ["What", "is", "default", "fieldset", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-895868504", "fields": {"term": "What settings were removed from schema?", "namespace": "open-p", "hash": -895868504, "terms": ["What settings were removed from schema?", "settings were removed from schema?", "were removed from schema?", "removed from schema?", "from schema?", "schema?"], "tokens": ["What", "settings", "were", "removed", "from", "schema", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1560554552", "fields": {"term": "What was the replacement for 'huge' attribute?", "namespace": "open-p", "hash": 1560554552, "terms": ["What was the replacement for 'huge' attribute?", "was the replacement for 'huge' attribute?", "the replacement for 'huge' attribute?", "replacement for 'huge' attribute?", "for 'huge' attribute?", "'huge' attribute?", "attribute?"], "tokens": ["What", "was", "the", "replacement", "for", "'", "huge", "'", "attribute", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1864732544", "fields": {"term": "Why was compression setting removed?", "namespace": "open-p", "hash": 1864732544, "terms": ["Why was compression setting removed?", "was compression setting removed?", "compression setting removed?", "setting removed?", "removed?"], "tokens": ["Why", "was", "compression", "setting", "removed", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::750005052", "fields": {"term": "What is the recommended alternative for weightedset field type?", "namespace": "open-p", "hash": 750005052, "terms": ["What is the recommended alternative for weightedset field type?", "is the recommended alternative for weightedset field type?", "the recommended alternative for weightedset field type?", "recommended alternative for weightedset field type?", "alternative for weightedset field type?", "for weightedset field type?", "weightedset field type?", "field type?", "type?"], "tokens": ["What", "is", "the", "recommended", "alternative", "for", "weightedset", "<", "float", ">", "field", "type", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-292558840", "fields": {"term": "Why is using 'float' as key in a map or set no longer supported?", "namespace": "open-p", "hash": -292558840, "terms": ["Why is using 'float' as key in a map or set no longer supported?", "is using 'float' as key in a map or set no longer supported?", "using 'float' as key in a map or set no longer supported?", "'float' as key in a map or set no longer supported?", "as key in a map or set no longer supported?", "key in a map or set no longer supported?", "in a map or set no longer supported?", "a map or set no longer supported?", "map or set no longer supported?", "or set no longer supported?", "set no longer supported?", "no longer supported?", "longer supported?", "supported?"], "tokens": ["Why", "is", "using", "'", "float", "'", "as", "key", "in", "a", "map", "or", "set", "no", "longer", "supported", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-547031190", "fields": {"term": "When do rank functions with the same name create conflict?", "namespace": "open-p", "hash": -547031190, "terms": ["When do rank functions with the same name create conflict?", "do rank functions with the same name create conflict?", "rank functions with the same name create conflict?", "functions with the same name create conflict?", "with the same name create conflict?", "the same name create conflict?", "same name create conflict?", "name create conflict?", "create conflict?", "conflict?"], "tokens": ["When", "do", "rank", "functions", "with", "the", "same", "name", "create", "conflict", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1212461256", "fields": {"term": "Why should the schema and document have the same name?", "namespace": "open-p", "hash": -1212461256, "terms": ["Why should the schema and document have the same name?", "should the schema and document have the same name?", "the schema and document have the same name?", "schema and document have the same name?", "and document have the same name?", "document have the same name?", "have the same name?", "the same name?", "same name?", "name?"], "tokens": ["Why", "should", "the", "schema", "and", "document", "have", "the", "same", "name", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::566976117", "fields": {"term": "What ML model format is preferred?", "namespace": "open-p", "hash": 566976117, "terms": ["What ML model format is preferred?", "ML model format is preferred?", "model format is preferred?", "format is preferred?", "is preferred?", "preferred?"], "tokens": ["What", "ML", "model", "format", "is", "preferred", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-167425837", "fields": {"term": "What does Vespa 8 remove support for?", "namespace": "open-p", "hash": -167425837, "terms": ["What does Vespa 8 remove support for?", "does Vespa 8 remove support for?", "Vespa 8 remove support for?", "8 remove support for?", "remove support for?", "support for?", "for?"], "tokens": ["What", "does", "Vespa", "8", "remove", "support", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1578700973", "fields": {"term": "What works for ranking and evaluation?", "namespace": "open-p", "hash": -1578700973, "terms": ["What works for ranking and evaluation?", "works for ranking and evaluation?", "for ranking and evaluation?", "ranking and evaluation?", "and evaluation?", "evaluation?"], "tokens": ["What", "works", "for", "ranking", "and", "evaluation", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-432453797", "fields": {"term": "Does Vespa provide TensorFlow conversion?", "namespace": "open-p", "hash": -432453797, "terms": ["Does Vespa provide TensorFlow conversion?", "Vespa provide TensorFlow conversion?", "provide TensorFlow conversion?", "TensorFlow conversion?", "conversion?"], "tokens": ["Does", "Vespa", "provide", "TensorFlow", "conversion", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1652979918", "fields": {"term": "What tools does ONNX contain?", "namespace": "open-p", "hash": 1652979918, "terms": ["What tools does ONNX contain?", "tools does ONNX contain?", "does ONNX contain?", "ONNX contain?", "contain?"], "tokens": ["What", "tools", "does", "ONNX", "contain", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1513785363", "fields": {"term": "How to convert models to ONNX?", "namespace": "open-p", "hash": 1513785363, "terms": ["How to convert models to ONNX?", "to convert models to ONNX?", "convert models to ONNX?", "models to ONNX?", "to ONNX?", "ONNX?"], "tokens": ["How", "to", "convert", "models", "to", "ONNX", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2085624202", "fields": {"term": "What is ?", "namespace": "open-p", "hash": 2085624202, "terms": ["What is ?", "is ?", "?"], "tokens": ["What", "is", "<", "nodes", "><", "redundancy", ">?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-878470891", "fields": {"term": "What happens if node count is lower than redundancy?", "namespace": "open-p", "hash": -878470891, "terms": ["What happens if node count is lower than redundancy?", "happens if node count is lower than redundancy?", "if node count is lower than redundancy?", "node count is lower than redundancy?", "count is lower than redundancy?", "is lower than redundancy?", "lower than redundancy?", "than redundancy?", "redundancy?"], "tokens": ["What", "happens", "if", "node", "count", "is", "lower", "than", "redundancy", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::841033996", "fields": {"term": "What elements are removed?", "namespace": "open-p", "hash": 841033996, "terms": ["What elements are removed?", "elements are removed?", "are removed?", "removed?"], "tokens": ["What", "elements", "are", "removed", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::453454777", "fields": {"term": "What is filedistribution?", "namespace": "open-p", "hash": 453454777, "terms": ["What is filedistribution?", "is filedistribution?", "filedistribution?"], "tokens": ["What", "is", "filedistribution", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2064078435", "fields": {"term": "What is the container jetty?", "namespace": "open-p", "hash": 2064078435, "terms": ["What is the container jetty?", "is the container jetty?", "the container jetty?", "container jetty?", "jetty?"], "tokens": ["What", "is", "the", "container", "jetty", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-4778623", "fields": {"term": "What is the maxentries recommended?", "namespace": "open-p", "hash": -4778623, "terms": ["What is the maxentries recommended?", "is the maxentries recommended?", "the maxentries recommended?", "maxentries recommended?", "recommended?"], "tokens": ["What", "is", "the", "maxentries", "recommended", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::940663562", "fields": {"term": "What folder should the schemas be in?", "namespace": "open-p", "hash": 940663562, "terms": ["What folder should the schemas be in?", "folder should the schemas be in?", "should the schemas be in?", "the schemas be in?", "schemas be in?", "be in?", "in?"], "tokens": ["What", "folder", "should", "the", "schemas", "be", "in", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::181768855", "fields": {"term": "What is the new folder for search schemas?", "namespace": "open-p", "hash": 181768855, "terms": ["What is the new folder for search schemas?", "is the new folder for search schemas?", "the new folder for search schemas?", "new folder for search schemas?", "folder for search schemas?", "for search schemas?", "search schemas?", "schemas?"], "tokens": ["What", "is", "the", "new", "folder", "for", "search", "schemas", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::150406193", "fields": {"term": "What causes a deprecation warning?", "namespace": "open-p", "hash": 150406193, "terms": ["What causes a deprecation warning?", "causes a deprecation warning?", "a deprecation warning?", "deprecation warning?", "warning?"], "tokens": ["What", "causes", "a", "deprecation", "warning", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-351062947", "fields": {"term": "What is com.yahoo.vespa.curator?", "namespace": "open-p", "hash": -351062947, "terms": ["What is com.yahoo.vespa.curator?", "is com.yahoo.vespa.curator?", "com.yahoo.vespa.curator?"], "tokens": ["What", "is", "com", "yahoo", "vespa", "curator", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::643624572", "fields": {"term": "What happened to com.yahoo.statistics?", "namespace": "open-p", "hash": 643624572, "terms": ["What happened to com.yahoo.statistics?", "happened to com.yahoo.statistics?", "to com.yahoo.statistics?", "com.yahoo.statistics?"], "tokens": ["What", "happened", "to", "com", "yahoo", "statistics", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1854438948", "fields": {"term": "What is com.yahoo.documentapi.messagebus?", "namespace": "open-p", "hash": 1854438948, "terms": ["What is com.yahoo.documentapi.messagebus?", "is com.yahoo.documentapi.messagebus?", "com.yahoo.documentapi.messagebus?"], "tokens": ["What", "is", "com", "yahoo", "documentapi", "messagebus", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1315987985", "fields": {"term": "What should be fixed before migrating to Vespa 8?", "namespace": "open-p", "hash": -1315987985, "terms": ["What should be fixed before migrating to Vespa 8?", "should be fixed before migrating to Vespa 8?", "be fixed before migrating to Vespa 8?", "fixed before migrating to Vespa 8?", "before migrating to Vespa 8?", "migrating to Vespa 8?", "to Vespa 8?", "Vespa 8?", "8?"], "tokens": ["What", "should", "be", "fixed", "before", "migrating", "to", "Vespa", "8", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1009833468", "fields": {"term": "What classes are moved to Vespa internal packages?", "namespace": "open-p", "hash": -1009833468, "terms": ["What classes are moved to Vespa internal packages?", "classes are moved to Vespa internal packages?", "are moved to Vespa internal packages?", "moved to Vespa internal packages?", "to Vespa internal packages?", "Vespa internal packages?", "internal packages?", "packages?"], "tokens": ["What", "classes", "are", "moved", "to", "Vespa", "internal", "packages", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::228409831", "fields": {"term": "What packages contain classes moved to internal packages?", "namespace": "open-p", "hash": 228409831, "terms": ["What packages contain classes moved to internal packages?", "packages contain classes moved to internal packages?", "contain classes moved to internal packages?", "classes moved to internal packages?", "moved to internal packages?", "to internal packages?", "internal packages?", "packages?"], "tokens": ["What", "packages", "contain", "classes", "moved", "to", "internal", "packages", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::698404059", "fields": {"term": "What is the migration advice for ConfigGetter class?", "namespace": "open-p", "hash": 698404059, "terms": ["What is the migration advice for ConfigGetter class?", "is the migration advice for ConfigGetter class?", "the migration advice for ConfigGetter class?", "migration advice for ConfigGetter class?", "advice for ConfigGetter class?", "for ConfigGetter class?", "ConfigGetter class?", "class?"], "tokens": ["What", "is", "the", "migration", "advice", "for", "ConfigGetter", "class", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-586724944", "fields": {"term": "What is the migration advice for DocprocExecutor class?", "namespace": "open-p", "hash": -586724944, "terms": ["What is the migration advice for DocprocExecutor class?", "is the migration advice for DocprocExecutor class?", "the migration advice for DocprocExecutor class?", "migration advice for DocprocExecutor class?", "advice for DocprocExecutor class?", "for DocprocExecutor class?", "DocprocExecutor class?", "class?"], "tokens": ["What", "is", "the", "migration", "advice", "for", "DocprocExecutor", "class", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-956049345", "fields": {"term": "What is the migration advice for DocumentAccess class?", "namespace": "open-p", "hash": -956049345, "terms": ["What is the migration advice for DocumentAccess class?", "is the migration advice for DocumentAccess class?", "the migration advice for DocumentAccess class?", "migration advice for DocumentAccess class?", "advice for DocumentAccess class?", "for DocumentAccess class?", "DocumentAccess class?", "class?"], "tokens": ["What", "is", "the", "migration", "advice", "for", "DocumentAccess", "class", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::268146295", "fields": {"term": "What method was removed in Vespa?", "namespace": "open-p", "hash": 268146295, "terms": ["What method was removed in Vespa?", "method was removed in Vespa?", "was removed in Vespa?", "removed in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["What", "method", "was", "removed", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2018409705", "fields": {"term": "What is the replacement for ListenableFuture?", "namespace": "open-p", "hash": 2018409705, "terms": ["What is the replacement for ListenableFuture?", "is the replacement for ListenableFuture?", "the replacement for ListenableFuture?", "replacement for ListenableFuture?", "for ListenableFuture?", "ListenableFuture?"], "tokens": ["What", "is", "the", "replacement", "for", "ListenableFuture", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1900955418", "fields": {"term": "What is the replacement for HttpSearchResponse.waitableRender()?", "namespace": "open-p", "hash": -1900955418, "terms": ["What is the replacement for HttpSearchResponse.waitableRender()?", "is the replacement for HttpSearchResponse.waitableRender()?", "the replacement for HttpSearchResponse.waitableRender()?", "replacement for HttpSearchResponse.waitableRender()?", "for HttpSearchResponse.waitableRender()?", "HttpSearchResponse.waitableRender()?"], "tokens": ["What", "is", "the", "replacement", "for", "HttpSearchResponse", "waitableRender", "()?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::530851689", "fields": {"term": "What is the replacement for Guava?", "namespace": "open-p", "hash": 530851689, "terms": ["What is the replacement for Guava?", "is the replacement for Guava?", "the replacement for Guava?", "replacement for Guava?", "for Guava?", "Guava?"], "tokens": ["What", "is", "the", "replacement", "for", "Guava", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1207454948", "fields": {"term": "What methods were removed from VisitorStatistics?", "namespace": "open-p", "hash": 1207454948, "terms": ["What methods were removed from VisitorStatistics?", "methods were removed from VisitorStatistics?", "were removed from VisitorStatistics?", "removed from VisitorStatistics?", "from VisitorStatistics?", "VisitorStatistics?"], "tokens": ["What", "methods", "were", "removed", "from", "VisitorStatistics", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1596369090", "fields": {"term": "What should not be set by the client in documentapi?", "namespace": "open-p", "hash": 1596369090, "terms": ["What should not be set by the client in documentapi?", "should not be set by the client in documentapi?", "not be set by the client in documentapi?", "be set by the client in documentapi?", "set by the client in documentapi?", "by the client in documentapi?", "the client in documentapi?", "client in documentapi?", "in documentapi?", "documentapi?"], "tokens": ["What", "should", "not", "be", "set", "by", "the", "client", "in", "documentapi", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2091539482", "fields": {"term": "What types were removed from JsonRenderer?", "namespace": "open-p", "hash": -2091539482, "terms": ["What types were removed from JsonRenderer?", "types were removed from JsonRenderer?", "were removed from JsonRenderer?", "removed from JsonRenderer?", "from JsonRenderer?", "JsonRenderer?"], "tokens": ["What", "types", "were", "removed", "from", "JsonRenderer", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::373088997", "fields": {"term": "What collection types were removed in ranking expression rule?", "namespace": "open-p", "hash": 373088997, "terms": ["What collection types were removed in ranking expression rule?", "collection types were removed in ranking expression rule?", "types were removed in ranking expression rule?", "were removed in ranking expression rule?", "removed in ranking expression rule?", "in ranking expression rule?", "ranking expression rule?", "expression rule?", "rule?"], "tokens": ["What", "collection", "types", "were", "removed", "in", "ranking", "expression", "rule", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-454751196", "fields": {"term": "What should documentation of deprecated types include?", "namespace": "open-p", "hash": -454751196, "terms": ["What should documentation of deprecated types include?", "should documentation of deprecated types include?", "documentation of deprecated types include?", "of deprecated types include?", "deprecated types include?", "types include?", "include?"], "tokens": ["What", "should", "documentation", "of", "deprecated", "types", "include", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1490805749", "fields": {"term": "What XML factories were supported?", "namespace": "open-p", "hash": -1490805749, "terms": ["What XML factories were supported?", "XML factories were supported?", "factories were supported?", "were supported?", "supported?"], "tokens": ["What", "XML", "factories", "were", "supported", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::178880705", "fields": {"term": "What providers were injected historically?", "namespace": "open-p", "hash": 178880705, "terms": ["What providers were injected historically?", "providers were injected historically?", "were injected historically?", "injected historically?", "historically?"], "tokens": ["What", "providers", "were", "injected", "historically", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2073971008", "fields": {"term": "What alternatives are there now?", "namespace": "open-p", "hash": -2073971008, "terms": ["What alternatives are there now?", "alternatives are there now?", "are there now?", "there now?", "now?"], "tokens": ["What", "alternatives", "are", "there", "now", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-774408861", "fields": {"term": "What XML processing is no longer supported?", "namespace": "open-p", "hash": -774408861, "terms": ["What XML processing is no longer supported?", "XML processing is no longer supported?", "processing is no longer supported?", "is no longer supported?", "no longer supported?", "longer supported?", "supported?"], "tokens": ["What", "XML", "processing", "is", "no", "longer", "supported", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1492589883", "fields": {"term": "Which APIs are being deprecated and why?", "namespace": "open-p", "hash": -1492589883, "terms": ["Which APIs are being deprecated and why?", "APIs are being deprecated and why?", "are being deprecated and why?", "being deprecated and why?", "deprecated and why?", "and why?", "why?"], "tokens": ["Which", "APIs", "are", "being", "deprecated", "and", "why", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1824606915", "fields": {"term": "What are the replacements for deprecated APIs?", "namespace": "open-p", "hash": -1824606915, "terms": ["What are the replacements for deprecated APIs?", "are the replacements for deprecated APIs?", "the replacements for deprecated APIs?", "replacements for deprecated APIs?", "for deprecated APIs?", "deprecated APIs?", "APIs?"], "tokens": ["What", "are", "the", "replacements", "for", "deprecated", "APIs", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1823871823", "fields": {"term": "How can I reduce future maintenance cost?", "namespace": "open-p", "hash": 1823871823, "terms": ["How can I reduce future maintenance cost?", "can I reduce future maintenance cost?", "I reduce future maintenance cost?", "reduce future maintenance cost?", "future maintenance cost?", "maintenance cost?", "cost?"], "tokens": ["How", "can", "I", "reduce", "future", "maintenance", "cost", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2046671758", "fields": {"term": "What is the JDK version upgrade?", "namespace": "open-p", "hash": 2046671758, "terms": ["What is the JDK version upgrade?", "is the JDK version upgrade?", "the JDK version upgrade?", "JDK version upgrade?", "version upgrade?", "upgrade?"], "tokens": ["What", "is", "the", "JDK", "version", "upgrade", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-399978914", "fields": {"term": "How do I ensure compatibility?", "namespace": "open-p", "hash": -399978914, "terms": ["How do I ensure compatibility?", "do I ensure compatibility?", "I ensure compatibility?", "ensure compatibility?", "compatibility?"], "tokens": ["How", "do", "I", "ensure", "compatibility", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-125223665", "fields": {"term": "Which APIs have been removed from Guava?", "namespace": "open-p", "hash": -125223665, "terms": ["Which APIs have been removed from Guava?", "APIs have been removed from Guava?", "have been removed from Guava?", "been removed from Guava?", "removed from Guava?", "from Guava?", "Guava?"], "tokens": ["Which", "APIs", "have", "been", "removed", "from", "Guava", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-196810589", "fields": {"term": "What Maven artifacts are no longer provided?", "namespace": "open-p", "hash": -196810589, "terms": ["What Maven artifacts are no longer provided?", "Maven artifacts are no longer provided?", "artifacts are no longer provided?", "are no longer provided?", "no longer provided?", "longer provided?", "provided?"], "tokens": ["What", "Maven", "artifacts", "are", "no", "longer", "provided", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-484249621", "fields": {"term": "What is the scope to embed an artifact?", "namespace": "open-p", "hash": -484249621, "terms": ["What is the scope to embed an artifact?", "is the scope to embed an artifact?", "the scope to embed an artifact?", "scope to embed an artifact?", "to embed an artifact?", "embed an artifact?", "an artifact?", "artifact?"], "tokens": ["What", "is", "the", "scope", "to", "embed", "an", "artifact", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2051903774", "fields": {"term": "How can I check for dependencies?", "namespace": "open-p", "hash": -2051903774, "terms": ["How can I check for dependencies?", "can I check for dependencies?", "I check for dependencies?", "check for dependencies?", "for dependencies?", "dependencies?"], "tokens": ["How", "can", "I", "check", "for", "dependencies", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1711388087", "fields": {"term": "How to remove a tansitively included dependency?", "namespace": "open-p", "hash": -1711388087, "terms": ["How to remove a tansitively included dependency?", "to remove a tansitively included dependency?", "remove a tansitively included dependency?", "a tansitively included dependency?", "tansitively included dependency?", "included dependency?", "dependency?"], "tokens": ["How", "to", "remove", "a", "tansitively", "included", "dependency", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::646666299", "fields": {"term": "What HTTP API parameters are removed from query API?", "namespace": "open-p", "hash": 646666299, "terms": ["What HTTP API parameters are removed from query API?", "HTTP API parameters are removed from query API?", "API parameters are removed from query API?", "parameters are removed from query API?", "are removed from query API?", "removed from query API?", "from query API?", "query API?", "API?"], "tokens": ["What", "HTTP", "API", "parameters", "are", "removed", "from", "query", "API", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-874053177", "fields": {"term": "How do you replace pos.ll parameter?", "namespace": "open-p", "hash": -874053177, "terms": ["How do you replace pos.ll parameter?", "do you replace pos.ll parameter?", "you replace pos.ll parameter?", "replace pos.ll parameter?", "pos.ll parameter?", "parameter?"], "tokens": ["How", "do", "you", "replace", "pos", "ll", "parameter", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2035778555", "fields": {"term": "How do you enable geoLocation in query?", "namespace": "open-p", "hash": -2035778555, "terms": ["How do you enable geoLocation in query?", "do you enable geoLocation in query?", "you enable geoLocation in query?", "enable geoLocation in query?", "geoLocation in query?", "in query?", "query?"], "tokens": ["How", "do", "you", "enable", "geoLocation", "in", "query", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-327572940", "fields": {"term": "What is pos.attribute parameter?", "namespace": "open-p", "hash": -327572940, "terms": ["What is pos.attribute parameter?", "is pos.attribute parameter?", "pos.attribute parameter?", "parameter?"], "tokens": ["What", "is", "pos", "attribute", "parameter", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::493341182", "fields": {"term": "Why has support for pos.bb been removed?", "namespace": "open-p", "hash": 493341182, "terms": ["Why has support for pos.bb been removed?", "has support for pos.bb been removed?", "support for pos.bb been removed?", "for pos.bb been removed?", "pos.bb been removed?", "been removed?", "removed?"], "tokens": ["Why", "has", "support", "for", "pos", "bb", "been", "removed", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-970173137", "fields": {"term": "What is vespa-http-client?", "namespace": "open-p", "hash": -970173137, "terms": ["What is vespa-http-client?", "is vespa-http-client?", "vespa-http-client?"], "tokens": ["What", "is", "vespa", "http", "client", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1041461494", "fields": {"term": "What replaces vespa-http-client?", "namespace": "open-p", "hash": -1041461494, "terms": ["What replaces vespa-http-client?", "replaces vespa-http-client?", "vespa-http-client?"], "tokens": ["What", "replaces", "vespa", "http", "client", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-6456742", "fields": {"term": "What API does vespa-feed-client use?", "namespace": "open-p", "hash": -6456742, "terms": ["What API does vespa-feed-client use?", "API does vespa-feed-client use?", "does vespa-feed-client use?", "vespa-feed-client use?", "use?"], "tokens": ["What", "API", "does", "vespa", "feed", "client", "use", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::15098439", "fields": {"term": "Will the rest API used by vespa-http-client be available in Vespa 8?", "namespace": "open-p", "hash": 15098439, "terms": ["Will the rest API used by vespa-http-client be available in Vespa 8?", "the rest API used by vespa-http-client be available in Vespa 8?", "rest API used by vespa-http-client be available in Vespa 8?", "API used by vespa-http-client be available in Vespa 8?", "used by vespa-http-client be available in Vespa 8?", "by vespa-http-client be available in Vespa 8?", "vespa-http-client be available in Vespa 8?", "be available in Vespa 8?", "available in Vespa 8?", "in Vespa 8?", "Vespa 8?", "8?"], "tokens": ["Will", "the", "rest", "API", "used", "by", "vespa", "http", "client", "be", "available", "in", "Vespa", "8", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1167684624", "fields": {"term": "Can an old client be used to feed to a Vespa 8 installation?", "namespace": "open-p", "hash": 1167684624, "terms": ["Can an old client be used to feed to a Vespa 8 installation?", "an old client be used to feed to a Vespa 8 installation?", "old client be used to feed to a Vespa 8 installation?", "client be used to feed to a Vespa 8 installation?", "be used to feed to a Vespa 8 installation?", "used to feed to a Vespa 8 installation?", "to feed to a Vespa 8 installation?", "feed to a Vespa 8 installation?", "to a Vespa 8 installation?", "a Vespa 8 installation?", "Vespa 8 installation?", "8 installation?", "installation?"], "tokens": ["Can", "an", "old", "client", "be", "used", "to", "feed", "to", "a", "Vespa", "8", "installation", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2023907217", "fields": {"term": "Will vespa-http-client receive updates in Vespa 8?", "namespace": "open-p", "hash": 2023907217, "terms": ["Will vespa-http-client receive updates in Vespa 8?", "vespa-http-client receive updates in Vespa 8?", "receive updates in Vespa 8?", "updates in Vespa 8?", "in Vespa 8?", "Vespa 8?", "8?"], "tokens": ["Will", "vespa", "http", "client", "receive", "updates", "in", "Vespa", "8", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1093379760", "fields": {"term": "Why should users migrate from vespa-http-client?", "namespace": "open-p", "hash": -1093379760, "terms": ["Why should users migrate from vespa-http-client?", "should users migrate from vespa-http-client?", "users migrate from vespa-http-client?", "migrate from vespa-http-client?", "from vespa-http-client?", "vespa-http-client?"], "tokens": ["Why", "should", "users", "migrate", "from", "vespa", "http", "client", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1558559824", "fields": {"term": "What will be supported as part of Vespa 8?", "namespace": "open-p", "hash": -1558559824, "terms": ["What will be supported as part of Vespa 8?", "will be supported as part of Vespa 8?", "be supported as part of Vespa 8?", "supported as part of Vespa 8?", "as part of Vespa 8?", "part of Vespa 8?", "of Vespa 8?", "Vespa 8?", "8?"], "tokens": ["What", "will", "be", "supported", "as", "part", "of", "Vespa", "8", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1650835527", "fields": {"term": "What metrics are renamed?", "namespace": "open-p", "hash": 1650835527, "terms": ["What metrics are renamed?", "metrics are renamed?", "are renamed?", "renamed?"], "tokens": ["What", "metrics", "are", "renamed", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-566182910", "fields": {"term": "What metrics are removed?", "namespace": "open-p", "hash": -566182910, "terms": ["What metrics are removed?", "metrics are removed?", "are removed?", "removed?"], "tokens": ["What", "metrics", "are", "removed", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1490945438", "fields": {"term": "What is the document selection language?", "namespace": "open-p", "hash": 1490945438, "terms": ["What is the document selection language?", "is the document selection language?", "the document selection language?", "document selection language?", "selection language?", "language?"], "tokens": ["What", "is", "the", "document", "selection", "language", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1861039104", "fields": {"term": "What is exact matching?", "namespace": "open-p", "hash": -1861039104, "terms": ["What is exact matching?", "is exact matching?", "exact matching?", "matching?"], "tokens": ["What", "is", "exact", "matching", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::785362195", "fields": {"term": "What is inheritance matching?", "namespace": "open-p", "hash": 785362195, "terms": ["What is inheritance matching?", "is inheritance matching?", "inheritance matching?", "matching?"], "tokens": ["What", "is", "inheritance", "matching", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-725076042", "fields": {"term": "How is strict mode enabled by default?", "namespace": "open-p", "hash": -725076042, "terms": ["How is strict mode enabled by default?", "is strict mode enabled by default?", "strict mode enabled by default?", "mode enabled by default?", "enabled by default?", "by default?", "default?"], "tokens": ["How", "is", "strict", "mode", "enabled", "by", "default", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-532548559", "fields": {"term": "What is the strict-mode attribute?", "namespace": "open-p", "hash": -532548559, "terms": ["What is the strict-mode attribute?", "is the strict-mode attribute?", "the strict-mode attribute?", "strict-mode attribute?", "attribute?"], "tokens": ["What", "is", "the", "strict", "mode", "attribute", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::520527267", "fields": {"term": "Where can I find documentation on strict-mode?", "namespace": "open-p", "hash": 520527267, "terms": ["Where can I find documentation on strict-mode?", "can I find documentation on strict-mode?", "I find documentation on strict-mode?", "find documentation on strict-mode?", "documentation on strict-mode?", "on strict-mode?", "strict-mode?"], "tokens": ["Where", "can", "I", "find", "documentation", "on", "strict", "mode", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::732532660", "fields": {"term": "What is the version that enables strict mode by default?", "namespace": "open-p", "hash": 732532660, "terms": ["What is the version that enables strict mode by default?", "is the version that enables strict mode by default?", "the version that enables strict mode by default?", "version that enables strict mode by default?", "that enables strict mode by default?", "enables strict mode by default?", "strict mode by default?", "mode by default?", "by default?", "default?"], "tokens": ["What", "is", "the", "version", "that", "enables", "strict", "mode", "by", "default", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1104869561", "fields": {"term": "How do I configure strict-mode in services.xml?", "namespace": "open-p", "hash": 1104869561, "terms": ["How do I configure strict-mode in services.xml?", "do I configure strict-mode in services.xml?", "I configure strict-mode in services.xml?", "configure strict-mode in services.xml?", "strict-mode in services.xml?", "in services.xml?", "services.xml?"], "tokens": ["How", "do", "I", "configure", "strict", "mode", "in", "services", "xml", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1057127768", "fields": {"term": "What does X-Forwarded-For header do?", "namespace": "open-p", "hash": -1057127768, "terms": ["What does X-Forwarded-For header do?", "does X-Forwarded-For header do?", "X-Forwarded-For header do?", "header do?", "do?"], "tokens": ["What", "does", "X", "Forwarded", "For", "header", "do", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1654663610", "fields": {"term": "What happens to the y-ra header?", "namespace": "open-p", "hash": 1654663610, "terms": ["What happens to the y-ra header?", "happens to the y-ra header?", "to the y-ra header?", "the y-ra header?", "y-ra header?", "header?"], "tokens": ["What", "happens", "to", "the", "y", "ra", "header", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1247805679", "fields": {"term": "What OS replaces CentOS Linux 7?", "namespace": "open-p", "hash": -1247805679, "terms": ["What OS replaces CentOS Linux 7?", "OS replaces CentOS Linux 7?", "replaces CentOS Linux 7?", "CentOS Linux 7?", "Linux 7?", "7?"], "tokens": ["What", "OS", "replaces", "CentOS", "Linux", "7", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1572339513", "fields": {"term": "Where are Vespa RPMs built?", "namespace": "open-p", "hash": 1572339513, "terms": ["Where are Vespa RPMs built?", "are Vespa RPMs built?", "Vespa RPMs built?", "RPMs built?", "built?"], "tokens": ["Where", "are", "Vespa", "RPMs", "built", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::954655934", "fields": {"term": "What do I need to upgrade to Vespa RPMs?", "namespace": "open-p", "hash": 954655934, "terms": ["What do I need to upgrade to Vespa RPMs?", "do I need to upgrade to Vespa RPMs?", "I need to upgrade to Vespa RPMs?", "need to upgrade to Vespa RPMs?", "to upgrade to Vespa RPMs?", "upgrade to Vespa RPMs?", "to Vespa RPMs?", "Vespa RPMs?", "RPMs?"], "tokens": ["What", "do", "I", "need", "to", "upgrade", "to", "Vespa", "RPMs", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1799089811", "fields": {"term": "What is the base image used for Vespa containers?", "namespace": "open-p", "hash": 1799089811, "terms": ["What is the base image used for Vespa containers?", "is the base image used for Vespa containers?", "the base image used for Vespa containers?", "base image used for Vespa containers?", "image used for Vespa containers?", "used for Vespa containers?", "for Vespa containers?", "Vespa containers?", "containers?"], "tokens": ["What", "is", "the", "base", "image", "used", "for", "Vespa", "containers", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-753488662", "fields": {"term": "What is the name of the new base image?", "namespace": "open-p", "hash": -753488662, "terms": ["What is the name of the new base image?", "is the name of the new base image?", "the name of the new base image?", "name of the new base image?", "of the new base image?", "the new base image?", "new base image?", "base image?", "image?"], "tokens": ["What", "is", "the", "name", "of", "the", "new", "base", "image", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::305167960", "fields": {"term": "What kernel version is required for Vespa's container image?", "namespace": "open-p", "hash": 305167960, "terms": ["What kernel version is required for Vespa's container image?", "kernel version is required for Vespa's container image?", "version is required for Vespa's container image?", "is required for Vespa's container image?", "required for Vespa's container image?", "for Vespa's container image?", "Vespa's container image?", "container image?", "image?"], "tokens": ["What", "kernel", "version", "is", "required", "for", "Vespa", "'", "s", "container", "image", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-173559533", "fields": {"term": "What is the reason for changing the base image?", "namespace": "open-p", "hash": -173559533, "terms": ["What is the reason for changing the base image?", "is the reason for changing the base image?", "the reason for changing the base image?", "reason for changing the base image?", "for changing the base image?", "changing the base image?", "the base image?", "base image?", "image?"], "tokens": ["What", "is", "the", "reason", "for", "changing", "the", "base", "image", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-368288407", "fields": {"term": "What should be done if using Vespa's container image?", "namespace": "open-p", "hash": -368288407, "terms": ["What should be done if using Vespa's container image?", "should be done if using Vespa's container image?", "be done if using Vespa's container image?", "done if using Vespa's container image?", "if using Vespa's container image?", "using Vespa's container image?", "Vespa's container image?", "container image?", "image?"], "tokens": ["What", "should", "be", "done", "if", "using", "Vespa", "'", "s", "container", "image", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1709605255", "fields": {"term": "What happens when rank profile doesn't exist?", "namespace": "open-p", "hash": 1709605255, "terms": ["What happens when rank profile doesn't exist?", "happens when rank profile doesn't exist?", "when rank profile doesn't exist?", "rank profile doesn't exist?", "profile doesn't exist?", "doesn't exist?", "exist?"], "tokens": ["What", "happens", "when", "rank", "profile", "doesn", "'", "t", "exist", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1940604588", "fields": {"term": "What is the requirement for using rank profile in multiple schemas?", "namespace": "open-p", "hash": 1940604588, "terms": ["What is the requirement for using rank profile in multiple schemas?", "is the requirement for using rank profile in multiple schemas?", "the requirement for using rank profile in multiple schemas?", "requirement for using rank profile in multiple schemas?", "for using rank profile in multiple schemas?", "using rank profile in multiple schemas?", "rank profile in multiple schemas?", "profile in multiple schemas?", "in multiple schemas?", "multiple schemas?", "schemas?"], "tokens": ["What", "is", "the", "requirement", "for", "using", "rank", "profile", "in", "multiple", "schemas", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::141793038", "fields": {"term": "How to ensure a common rank profile in multiple schemas?", "namespace": "open-p", "hash": 141793038, "terms": ["How to ensure a common rank profile in multiple schemas?", "to ensure a common rank profile in multiple schemas?", "ensure a common rank profile in multiple schemas?", "a common rank profile in multiple schemas?", "common rank profile in multiple schemas?", "rank profile in multiple schemas?", "profile in multiple schemas?", "in multiple schemas?", "multiple schemas?", "schemas?"], "tokens": ["How", "to", "ensure", "a", "common", "rank", "profile", "in", "multiple", "schemas", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2113408871", "fields": {"term": "What happens when summary class is non-existent?", "namespace": "open-p", "hash": 2113408871, "terms": ["What happens when summary class is non-existent?", "happens when summary class is non-existent?", "when summary class is non-existent?", "summary class is non-existent?", "class is non-existent?", "is non-existent?", "non-existent?"], "tokens": ["What", "happens", "when", "summary", "class", "is", "non", "existent", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1718002986", "fields": {"term": "How to ensure summary class in multiple schemas?", "namespace": "open-p", "hash": 1718002986, "terms": ["How to ensure summary class in multiple schemas?", "to ensure summary class in multiple schemas?", "ensure summary class in multiple schemas?", "summary class in multiple schemas?", "class in multiple schemas?", "in multiple schemas?", "multiple schemas?", "schemas?"], "tokens": ["How", "to", "ensure", "summary", "class", "in", "multiple", "schemas", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1314211308", "fields": {"term": "What is qrserver?", "namespace": "open-p", "hash": -1314211308, "terms": ["What is qrserver?", "is qrserver?", "qrserver?"], "tokens": ["What", "is", "qrserver", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-275628386", "fields": {"term": "Where are the container access logs?", "namespace": "open-p", "hash": -275628386, "terms": ["Where are the container access logs?", "are the container access logs?", "the container access logs?", "container access logs?", "access logs?", "logs?"], "tokens": ["Where", "are", "the", "container", "access", "logs", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-205154087", "fields": {"term": "What is the new location for access logs?", "namespace": "open-p", "hash": -205154087, "terms": ["What is the new location for access logs?", "is the new location for access logs?", "the new location for access logs?", "new location for access logs?", "location for access logs?", "for access logs?", "access logs?", "logs?"], "tokens": ["What", "is", "the", "new", "location", "for", "access", "logs", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1940885342", "fields": {"term": "What is the default compression format for access logs?", "namespace": "open-p", "hash": -1940885342, "terms": ["What is the default compression format for access logs?", "is the default compression format for access logs?", "the default compression format for access logs?", "default compression format for access logs?", "compression format for access logs?", "format for access logs?", "for access logs?", "access logs?", "logs?"], "tokens": ["What", "is", "the", "default", "compression", "format", "for", "access", "logs", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1427245475", "fields": {"term": "What is new in Vespa 8?", "namespace": "open-p", "hash": 1427245475, "terms": ["What is new in Vespa 8?", "is new in Vespa 8?", "new in Vespa 8?", "in Vespa 8?", "Vespa 8?", "8?"], "tokens": ["What", "is", "new", "in", "Vespa", "8", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1590115815", "fields": {"term": "What is a summary feature?", "namespace": "open-p", "hash": 1590115815, "terms": ["What is a summary feature?", "is a summary feature?", "a summary feature?", "summary feature?", "feature?"], "tokens": ["What", "is", "a", "summary", "feature", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::899583481", "fields": {"term": "What is the summary feature name?", "namespace": "open-p", "hash": 899583481, "terms": ["What is the summary feature name?", "is the summary feature name?", "the summary feature name?", "summary feature name?", "feature name?", "name?"], "tokens": ["What", "is", "the", "summary", "feature", "name", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::19565870", "fields": {"term": "What was the previous summary feature name?", "namespace": "open-p", "hash": 19565870, "terms": ["What was the previous summary feature name?", "was the previous summary feature name?", "the previous summary feature name?", "previous summary feature name?", "summary feature name?", "feature name?", "name?"], "tokens": ["What", "was", "the", "previous", "summary", "feature", "name", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1341257123", "fields": {"term": "How does Vespa 8 define ONNX model output?", "namespace": "open-p", "hash": 1341257123, "terms": ["How does Vespa 8 define ONNX model output?", "does Vespa 8 define ONNX model output?", "Vespa 8 define ONNX model output?", "8 define ONNX model output?", "define ONNX model output?", "ONNX model output?", "model output?", "output?"], "tokens": ["How", "does", "Vespa", "8", "define", "ONNX", "model", "output", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1888879076", "fields": {"term": "What is different in ONNX model output definition in Vespa 8?", "namespace": "open-p", "hash": -1888879076, "terms": ["What is different in ONNX model output definition in Vespa 8?", "is different in ONNX model output definition in Vespa 8?", "different in ONNX model output definition in Vespa 8?", "in ONNX model output definition in Vespa 8?", "ONNX model output definition in Vespa 8?", "model output definition in Vespa 8?", "output definition in Vespa 8?", "definition in Vespa 8?", "in Vespa 8?", "Vespa 8?", "8?"], "tokens": ["What", "is", "different", "in", "ONNX", "model", "output", "definition", "in", "Vespa", "8", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::385336821", "fields": {"term": "What is the feature name for ONNX model output in Vespa 8?", "namespace": "open-p", "hash": 385336821, "terms": ["What is the feature name for ONNX model output in Vespa 8?", "is the feature name for ONNX model output in Vespa 8?", "the feature name for ONNX model output in Vespa 8?", "feature name for ONNX model output in Vespa 8?", "name for ONNX model output in Vespa 8?", "for ONNX model output in Vespa 8?", "ONNX model output in Vespa 8?", "model output in Vespa 8?", "output in Vespa 8?", "in Vespa 8?", "Vespa 8?", "8?"], "tokens": ["What", "is", "the", "feature", "name", "for", "ONNX", "model", "output", "in", "Vespa", "8", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-747712430", "fields": {"term": "What changes were made to rank-features in Vespa 8?", "namespace": "open-p", "hash": -747712430, "terms": ["What changes were made to rank-features in Vespa 8?", "changes were made to rank-features in Vespa 8?", "were made to rank-features in Vespa 8?", "made to rank-features in Vespa 8?", "to rank-features in Vespa 8?", "rank-features in Vespa 8?", "in Vespa 8?", "Vespa 8?", "8?"], "tokens": ["What", "changes", "were", "made", "to", "rank", "features", "in", "Vespa", "8", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1851634658", "fields": {"term": "What is the 'storage' routing policy?", "namespace": "open-p", "hash": -1851634658, "terms": ["What is the 'storage' routing policy?", "is the 'storage' routing policy?", "the 'storage' routing policy?", "'storage' routing policy?", "routing policy?", "policy?"], "tokens": ["What", "is", "the", "'", "storage", "'", "routing", "policy", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-992410245", "fields": {"term": "What has happened to the 'storage' routing policy in Vespa 7?", "namespace": "open-p", "hash": -992410245, "terms": ["What has happened to the 'storage' routing policy in Vespa 7?", "has happened to the 'storage' routing policy in Vespa 7?", "happened to the 'storage' routing policy in Vespa 7?", "to the 'storage' routing policy in Vespa 7?", "the 'storage' routing policy in Vespa 7?", "'storage' routing policy in Vespa 7?", "routing policy in Vespa 7?", "policy in Vespa 7?", "in Vespa 7?", "Vespa 7?", "7?"], "tokens": ["What", "has", "happened", "to", "the", "'", "storage", "'", "routing", "policy", "in", "Vespa", "7", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1181828480", "fields": {"term": "What will happen to clients using 'storage' routing policy in Vespa 8?", "namespace": "open-p", "hash": -1181828480, "terms": ["What will happen to clients using 'storage' routing policy in Vespa 8?", "will happen to clients using 'storage' routing policy in Vespa 8?", "happen to clients using 'storage' routing policy in Vespa 8?", "to clients using 'storage' routing policy in Vespa 8?", "clients using 'storage' routing policy in Vespa 8?", "using 'storage' routing policy in Vespa 8?", "'storage' routing policy in Vespa 8?", "routing policy in Vespa 8?", "policy in Vespa 8?", "in Vespa 8?", "Vespa 8?", "8?"], "tokens": ["What", "will", "happen", "to", "clients", "using", "'", "storage", "'", "routing", "policy", "in", "Vespa", "8", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2018993152", "fields": {"term": "What should clients replace 'storage' routing policy with?", "namespace": "open-p", "hash": -2018993152, "terms": ["What should clients replace 'storage' routing policy with?", "should clients replace 'storage' routing policy with?", "clients replace 'storage' routing policy with?", "replace 'storage' routing policy with?", "'storage' routing policy with?", "routing policy with?", "policy with?", "with?"], "tokens": ["What", "should", "clients", "replace", "'", "storage", "'", "routing", "policy", "with", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::506952449", "fields": {"term": "How does the 'content' routing policy behave?", "namespace": "open-p", "hash": 506952449, "terms": ["How does the 'content' routing policy behave?", "does the 'content' routing policy behave?", "the 'content' routing policy behave?", "'content' routing policy behave?", "routing policy behave?", "policy behave?", "behave?"], "tokens": ["How", "does", "the", "'", "content", "'", "routing", "policy", "behave", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1741970691", "fields": {"term": "How can clients update their routing policy?", "namespace": "open-p", "hash": -1741970691, "terms": ["How can clients update their routing policy?", "can clients update their routing policy?", "clients update their routing policy?", "update their routing policy?", "their routing policy?", "routing policy?", "policy?"], "tokens": ["How", "can", "clients", "update", "their", "routing", "policy", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1096088615", "fields": {"term": "When was the 'storage' routing policy removed?", "namespace": "open-p", "hash": 1096088615, "terms": ["When was the 'storage' routing policy removed?", "was the 'storage' routing policy removed?", "the 'storage' routing policy removed?", "'storage' routing policy removed?", "routing policy removed?", "policy removed?", "removed?"], "tokens": ["When", "was", "the", "'", "storage", "'", "routing", "policy", "removed", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::152205303", "fields": {"term": "What is the difference between 'storage' and 'content' routing policy?", "namespace": "open-p", "hash": 152205303, "terms": ["What is the difference between 'storage' and 'content' routing policy?", "is the difference between 'storage' and 'content' routing policy?", "the difference between 'storage' and 'content' routing policy?", "difference between 'storage' and 'content' routing policy?", "between 'storage' and 'content' routing policy?", "'storage' and 'content' routing policy?", "and 'content' routing policy?", "'content' routing policy?", "routing policy?", "policy?"], "tokens": ["What", "is", "the", "difference", "between", "'", "storage", "'", "and", "'", "content", "'", "routing", "policy", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-129837589", "fields": {"term": "What is the dispatch functionality?", "namespace": "open-p", "hash": -129837589, "terms": ["What is the dispatch functionality?", "is the dispatch functionality?", "the dispatch functionality?", "dispatch functionality?", "functionality?"], "tokens": ["What", "is", "the", "dispatch", "functionality", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::359734920", "fields": {"term": "Where was the dispatch functionality moved?", "namespace": "open-p", "hash": 359734920, "terms": ["Where was the dispatch functionality moved?", "was the dispatch functionality moved?", "the dispatch functionality moved?", "dispatch functionality moved?", "functionality moved?", "moved?"], "tokens": ["Where", "was", "the", "dispatch", "functionality", "moved", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1999654405", "fields": {"term": "What process was removed?", "namespace": "open-p", "hash": 1999654405, "terms": ["What process was removed?", "process was removed?", "was removed?", "removed?"], "tokens": ["What", "process", "was", "removed", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1578438210", "fields": {"term": "What was the default setting at Vespa-7.109.10?", "namespace": "open-p", "hash": 1578438210, "terms": ["What was the default setting at Vespa-7.109.10?", "was the default setting at Vespa-7.109.10?", "the default setting at Vespa-7.109.10?", "default setting at Vespa-7.109.10?", "setting at Vespa-7.109.10?", "at Vespa-7.109.10?", "Vespa-7.109.10?"], "tokens": ["What", "was", "the", "default", "setting", "at", "Vespa", "7", "109", "10", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1184047134", "fields": {"term": "What does dispatch do between container and content nodes?", "namespace": "open-p", "hash": -1184047134, "terms": ["What does dispatch do between container and content nodes?", "does dispatch do between container and content nodes?", "dispatch do between container and content nodes?", "do between container and content nodes?", "between container and content nodes?", "container and content nodes?", "and content nodes?", "content nodes?", "nodes?"], "tokens": ["What", "does", "dispatch", "do", "between", "container", "and", "content", "nodes", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1377771656", "fields": {"term": "What is the purpose of the vespa-dispatch-bin process?", "namespace": "open-p", "hash": -1377771656, "terms": ["What is the purpose of the vespa-dispatch-bin process?", "is the purpose of the vespa-dispatch-bin process?", "the purpose of the vespa-dispatch-bin process?", "purpose of the vespa-dispatch-bin process?", "of the vespa-dispatch-bin process?", "the vespa-dispatch-bin process?", "vespa-dispatch-bin process?", "process?"], "tokens": ["What", "is", "the", "purpose", "of", "the", "vespa", "dispatch", "bin", "process", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2114370662", "fields": {"term": "What range are dynamically allocated for the content cluster?", "namespace": "open-p", "hash": -2114370662, "terms": ["What range are dynamically allocated for the content cluster?", "range are dynamically allocated for the content cluster?", "are dynamically allocated for the content cluster?", "dynamically allocated for the content cluster?", "allocated for the content cluster?", "for the content cluster?", "the content cluster?", "content cluster?", "cluster?"], "tokens": ["What", "range", "are", "dynamically", "allocated", "for", "the", "content", "cluster", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1287479048", "fields": {"term": "What is the YQL statement output format?", "namespace": "open-p", "hash": 1287479048, "terms": ["What is the YQL statement output format?", "is the YQL statement output format?", "the YQL statement output format?", "YQL statement output format?", "statement output format?", "output format?", "format?"], "tokens": ["What", "is", "the", "YQL", "statement", "output", "format", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1658524757", "fields": {"term": "Are annotations enclosed in brackets?", "namespace": "open-p", "hash": 1658524757, "terms": ["Are annotations enclosed in brackets?", "annotations enclosed in brackets?", "enclosed in brackets?", "in brackets?", "brackets?"], "tokens": ["Are", "annotations", "enclosed", "in", "brackets", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2001029113", "fields": {"term": "Is quoting annotation name necessary?", "namespace": "open-p", "hash": 2001029113, "terms": ["Is quoting annotation name necessary?", "quoting annotation name necessary?", "annotation name necessary?", "name necessary?", "necessary?"], "tokens": ["Is", "quoting", "annotation", "name", "necessary", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1727820614", "fields": {"term": "Is semicolon termination optional?", "namespace": "open-p", "hash": 1727820614, "terms": ["Is semicolon termination optional?", "semicolon termination optional?", "termination optional?", "optional?"], "tokens": ["Is", "semicolon", "termination", "optional", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2038513645", "fields": {"term": "What is the new change in YQL output?", "namespace": "open-p", "hash": 2038513645, "terms": ["What is the new change in YQL output?", "is the new change in YQL output?", "the new change in YQL output?", "new change in YQL output?", "change in YQL output?", "in YQL output?", "YQL output?", "output?"], "tokens": ["What", "is", "the", "new", "change", "in", "YQL", "output", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::486153093", "fields": {"term": "What is a valid input for annotations?", "namespace": "open-p", "hash": 486153093, "terms": ["What is a valid input for annotations?", "is a valid input for annotations?", "a valid input for annotations?", "valid input for annotations?", "input for annotations?", "for annotations?", "annotations?"], "tokens": ["What", "is", "a", "valid", "input", "for", "annotations", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-605324081", "fields": {"term": "What is a near query in text matching?", "namespace": "open-p", "hash": -605324081, "terms": ["What is a near query in text matching?", "is a near query in text matching?", "a near query in text matching?", "near query in text matching?", "query in text matching?", "in text matching?", "text matching?", "matching?"], "tokens": ["What", "is", "a", "near", "query", "in", "text", "matching", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::780431379", "fields": {"term": "What has been simplified in Vespa 8 during upgrades?", "namespace": "open-p", "hash": 780431379, "terms": ["What has been simplified in Vespa 8 during upgrades?", "has been simplified in Vespa 8 during upgrades?", "been simplified in Vespa 8 during upgrades?", "simplified in Vespa 8 during upgrades?", "in Vespa 8 during upgrades?", "Vespa 8 during upgrades?", "8 during upgrades?", "during upgrades?", "upgrades?"], "tokens": ["What", "has", "been", "simplified", "in", "Vespa", "8", "during", "upgrades", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1479176221", "fields": {"term": "How do you upgrade the config servers?", "namespace": "open-p", "hash": -1479176221, "terms": ["How do you upgrade the config servers?", "do you upgrade the config servers?", "you upgrade the config servers?", "upgrade the config servers?", "the config servers?", "config servers?", "servers?"], "tokens": ["How", "do", "you", "upgrade", "the", "config", "servers", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::447358636", "fields": {"term": "What command is used to set all nodes in standalone mode?", "namespace": "open-p", "hash": 447358636, "terms": ["What command is used to set all nodes in standalone mode?", "command is used to set all nodes in standalone mode?", "is used to set all nodes in standalone mode?", "used to set all nodes in standalone mode?", "to set all nodes in standalone mode?", "set all nodes in standalone mode?", "all nodes in standalone mode?", "nodes in standalone mode?", "in standalone mode?", "standalone mode?", "mode?"], "tokens": ["What", "command", "is", "used", "to", "set", "all", "nodes", "in", "standalone", "mode", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-109798835", "fields": {"term": "What must be done before upgrading config servers?", "namespace": "open-p", "hash": -109798835, "terms": ["What must be done before upgrading config servers?", "must be done before upgrading config servers?", "be done before upgrading config servers?", "done before upgrading config servers?", "before upgrading config servers?", "upgrading config servers?", "config servers?", "servers?"], "tokens": ["What", "must", "be", "done", "before", "upgrading", "config", "servers", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2123979198", "fields": {"term": "What is the upgrade procedure when upgrading from Vespa 7.x to Vespa 7.y?", "namespace": "open-p", "hash": -2123979198, "terms": ["What is the upgrade procedure when upgrading from Vespa 7.x to Vespa 7.y?", "is the upgrade procedure when upgrading from Vespa 7.x to Vespa 7.y?", "the upgrade procedure when upgrading from Vespa 7.x to Vespa 7.y?", "upgrade procedure when upgrading from Vespa 7.x to Vespa 7.y?", "procedure when upgrading from Vespa 7.x to Vespa 7.y?", "when upgrading from Vespa 7.x to Vespa 7.y?", "upgrading from Vespa 7.x to Vespa 7.y?", "from Vespa 7.x to Vespa 7.y?", "Vespa 7.x to Vespa 7.y?", "7.x to Vespa 7.y?", "to Vespa 7.y?", "Vespa 7.y?", "7.y?"], "tokens": ["What", "is", "the", "upgrade", "procedure", "when", "upgrading", "from", "Vespa", "7", "x", "to", "Vespa", "7", "y", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1416909252", "fields": {"term": "How do you install the new Vespa version on config servers?", "namespace": "open-p", "hash": -1416909252, "terms": ["How do you install the new Vespa version on config servers?", "do you install the new Vespa version on config servers?", "you install the new Vespa version on config servers?", "install the new Vespa version on config servers?", "the new Vespa version on config servers?", "new Vespa version on config servers?", "Vespa version on config servers?", "version on config servers?", "on config servers?", "config servers?", "servers?"], "tokens": ["How", "do", "you", "install", "the", "new", "Vespa", "version", "on", "config", "servers", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-819720765", "fields": {"term": "What commands are used to redeploy and activate an application?", "namespace": "open-p", "hash": -819720765, "terms": ["What commands are used to redeploy and activate an application?", "commands are used to redeploy and activate an application?", "are used to redeploy and activate an application?", "used to redeploy and activate an application?", "to redeploy and activate an application?", "redeploy and activate an application?", "and activate an application?", "activate an application?", "an application?", "application?"], "tokens": ["What", "commands", "are", "used", "to", "redeploy", "and", "activate", "an", "application", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1859697547", "fields": {"term": "What is visiting in Vespa?", "namespace": "open-p", "hash": -1859697547, "terms": ["What is visiting in Vespa?", "is visiting in Vespa?", "visiting in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["What", "is", "visiting", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-35269990", "fields": {"term": "How is visiting used?", "namespace": "open-p", "hash": -35269990, "terms": ["How is visiting used?", "is visiting used?", "visiting used?", "used?"], "tokens": ["How", "is", "visiting", "used", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1413043417", "fields": {"term": "Are visits time sensitive?", "namespace": "open-p", "hash": -1413043417, "terms": ["Are visits time sensitive?", "visits time sensitive?", "time sensitive?", "sensitive?"], "tokens": ["Are", "visits", "time", "sensitive", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::547069369", "fields": {"term": "What is cloning applications and data?", "namespace": "open-p", "hash": 547069369, "terms": ["What is cloning applications and data?", "is cloning applications and data?", "cloning applications and data?", "applications and data?", "and data?", "data?"], "tokens": ["What", "is", "cloning", "applications", "and", "data", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-521780948", "fields": {"term": "Does visiting have snapshot isolation?", "namespace": "open-p", "hash": -521780948, "terms": ["Does visiting have snapshot isolation?", "visiting have snapshot isolation?", "have snapshot isolation?", "snapshot isolation?", "isolation?"], "tokens": ["Does", "visiting", "have", "snapshot", "isolation", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1695973542", "fields": {"term": "Where can I find information on how visiting works?", "namespace": "open-p", "hash": 1695973542, "terms": ["Where can I find information on how visiting works?", "can I find information on how visiting works?", "I find information on how visiting works?", "find information on how visiting works?", "information on how visiting works?", "on how visiting works?", "how visiting works?", "visiting works?", "works?"], "tokens": ["Where", "can", "I", "find", "information", "on", "how", "visiting", "works", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-227060807", "fields": {"term": "How to export data?", "namespace": "open-p", "hash": -227060807, "terms": ["How to export data?", "to export data?", "export data?", "data?"], "tokens": ["How", "to", "export", "data", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::5521495", "fields": {"term": "What is stdout?", "namespace": "open-p", "hash": 5521495, "terms": ["What is stdout?", "is stdout?", "stdout?"], "tokens": ["What", "is", "stdout", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1193912842", "fields": {"term": "How to visit entire corpus?", "namespace": "open-p", "hash": -1193912842, "terms": ["How to visit entire corpus?", "to visit entire corpus?", "visit entire corpus?", "entire corpus?", "corpus?"], "tokens": ["How", "to", "visit", "entire", "corpus", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1351776231", "fields": {"term": "How to efficiently iterate data?", "namespace": "open-p", "hash": -1351776231, "terms": ["How to efficiently iterate data?", "to efficiently iterate data?", "efficiently iterate data?", "iterate data?", "data?"], "tokens": ["How", "to", "efficiently", "iterate", "data", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::876130307", "fields": {"term": "What is a document selection expression?", "namespace": "open-p", "hash": 876130307, "terms": ["What is a document selection expression?", "is a document selection expression?", "a document selection expression?", "document selection expression?", "selection expression?", "expression?"], "tokens": ["What", "is", "a", "document", "selection", "expression", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1045450359", "fields": {"term": "Where can I find examples of document selection expressions?", "namespace": "open-p", "hash": 1045450359, "terms": ["Where can I find examples of document selection expressions?", "can I find examples of document selection expressions?", "I find examples of document selection expressions?", "find examples of document selection expressions?", "examples of document selection expressions?", "of document selection expressions?", "document selection expressions?", "selection expressions?", "expressions?"], "tokens": ["Where", "can", "I", "find", "examples", "of", "document", "selection", "expressions", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::564939705", "fields": {"term": "How do I select documents based on ID?", "namespace": "open-p", "hash": 564939705, "terms": ["How do I select documents based on ID?", "do I select documents based on ID?", "I select documents based on ID?", "select documents based on ID?", "documents based on ID?", "based on ID?", "on ID?", "ID?"], "tokens": ["How", "do", "I", "select", "documents", "based", "on", "ID", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1858746799", "fields": {"term": "What is the Vespa CLI cheat sheet?", "namespace": "open-p", "hash": -1858746799, "terms": ["What is the Vespa CLI cheat sheet?", "is the Vespa CLI cheat sheet?", "the Vespa CLI cheat sheet?", "Vespa CLI cheat sheet?", "CLI cheat sheet?", "cheat sheet?", "sheet?"], "tokens": ["What", "is", "the", "Vespa", "CLI", "cheat", "sheet", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2144599997", "fields": {"term": "How do I filter documents in Vespa?", "namespace": "open-p", "hash": 2144599997, "terms": ["How do I filter documents in Vespa?", "do I filter documents in Vespa?", "I filter documents in Vespa?", "filter documents in Vespa?", "documents in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["How", "do", "I", "filter", "documents", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-409875863", "fields": {"term": "How do you set a fieldset?", "namespace": "open-p", "hash": -409875863, "terms": ["How do you set a fieldset?", "do you set a fieldset?", "you set a fieldset?", "set a fieldset?", "a fieldset?", "fieldset?"], "tokens": ["How", "do", "you", "set", "a", "fieldset", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::809677803", "fields": {"term": "What are common use cases for a fieldset?", "namespace": "open-p", "hash": 809677803, "terms": ["What are common use cases for a fieldset?", "are common use cases for a fieldset?", "common use cases for a fieldset?", "use cases for a fieldset?", "cases for a fieldset?", "for a fieldset?", "a fieldset?", "fieldset?"], "tokens": ["What", "are", "common", "use", "cases", "for", "a", "fieldset", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1276692360", "fields": {"term": "What does the shorthand [all] do?", "namespace": "open-p", "hash": -1276692360, "terms": ["What does the shorthand [all] do?", "does the shorthand [all] do?", "the shorthand [all] do?", "shorthand [all] do?", "[all] do?", "do?"], "tokens": ["What", "does", "the", "shorthand", "[", "all", "]", "do", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-180393214", "fields": {"term": "How do you specify timestamp range for Document V1?", "namespace": "open-p", "hash": -180393214, "terms": ["How do you specify timestamp range for Document V1?", "do you specify timestamp range for Document V1?", "you specify timestamp range for Document V1?", "specify timestamp range for Document V1?", "timestamp range for Document V1?", "range for Document V1?", "for Document V1?", "Document V1?", "V1?"], "tokens": ["How", "do", "you", "specify", "timestamp", "range", "for", "Document", "V1", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2039913484", "fields": {"term": "How do you specify timestamp range for Vespa CLI?", "namespace": "open-p", "hash": 2039913484, "terms": ["How do you specify timestamp range for Vespa CLI?", "do you specify timestamp range for Vespa CLI?", "you specify timestamp range for Vespa CLI?", "specify timestamp range for Vespa CLI?", "timestamp range for Vespa CLI?", "range for Vespa CLI?", "for Vespa CLI?", "Vespa CLI?", "CLI?"], "tokens": ["How", "do", "you", "specify", "timestamp", "range", "for", "Vespa", "CLI", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-605798979", "fields": {"term": "What does setting a timestamp range do?", "namespace": "open-p", "hash": -605798979, "terms": ["What does setting a timestamp range do?", "does setting a timestamp range do?", "setting a timestamp range do?", "a timestamp range do?", "timestamp range do?", "range do?", "do?"], "tokens": ["What", "does", "setting", "a", "timestamp", "range", "do", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2131244243", "fields": {"term": "Does setting a timestamp range imply snapshot isolation?", "namespace": "open-p", "hash": -2131244243, "terms": ["Does setting a timestamp range imply snapshot isolation?", "setting a timestamp range imply snapshot isolation?", "a timestamp range imply snapshot isolation?", "timestamp range imply snapshot isolation?", "range imply snapshot isolation?", "imply snapshot isolation?", "snapshot isolation?", "isolation?"], "tokens": ["Does", "setting", "a", "timestamp", "range", "imply", "snapshot", "isolation", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-432157974", "fields": {"term": "Can concurrent modifications affect the set of documents returned?", "namespace": "open-p", "hash": -432157974, "terms": ["Can concurrent modifications affect the set of documents returned?", "concurrent modifications affect the set of documents returned?", "modifications affect the set of documents returned?", "affect the set of documents returned?", "the set of documents returned?", "set of documents returned?", "of documents returned?", "documents returned?", "returned?"], "tokens": ["Can", "concurrent", "modifications", "affect", "the", "set", "of", "documents", "returned", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-2129198625", "fields": {"term": "What updates the document timestamp?", "namespace": "open-p", "hash": -2129198625, "terms": ["What updates the document timestamp?", "updates the document timestamp?", "the document timestamp?", "document timestamp?", "timestamp?"], "tokens": ["What", "updates", "the", "document", "timestamp", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::430871167", "fields": {"term": "What is reprocessing used for?", "namespace": "open-p", "hash": 430871167, "terms": ["What is reprocessing used for?", "is reprocessing used for?", "reprocessing used for?", "used for?", "for?"], "tokens": ["What", "is", "reprocessing", "used", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::331891836", "fields": {"term": "How can document identifiers be changed?", "namespace": "open-p", "hash": 331891836, "terms": ["How can document identifiers be changed?", "can document identifiers be changed?", "document identifiers be changed?", "identifiers be changed?", "be changed?", "changed?"], "tokens": ["How", "can", "document", "identifiers", "be", "changed", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::30639734", "fields": {"term": "What can be reindexed with reprocessing?", "namespace": "open-p", "hash": 30639734, "terms": ["What can be reindexed with reprocessing?", "can be reindexed with reprocessing?", "be reindexed with reprocessing?", "reindexed with reprocessing?", "with reprocessing?", "reprocessing?"], "tokens": ["What", "can", "be", "reindexed", "with", "reprocessing", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1916449857", "fields": {"term": "How is reprocessed data written back?", "namespace": "open-p", "hash": -1916449857, "terms": ["How is reprocessed data written back?", "is reprocessed data written back?", "reprocessed data written back?", "data written back?", "written back?", "back?"], "tokens": ["How", "is", "reprocessed", "data", "written", "back", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1345798491", "fields": {"term": "What is reprocessing?", "namespace": "open-p", "hash": 1345798491, "terms": ["What is reprocessing?", "is reprocessing?", "reprocessing?"], "tokens": ["What", "is", "reprocessing", "?"], "corpus_count": 2, "document_count": 2}}, -{"put": "id:term:term::-1556461678", "fields": {"term": "How to select documents for reprocessing?", "namespace": "open-p", "hash": -1556461678, "terms": ["How to select documents for reprocessing?", "to select documents for reprocessing?", "select documents for reprocessing?", "documents for reprocessing?", "for reprocessing?", "reprocessing?"], "tokens": ["How", "to", "select", "documents", "for", "reprocessing", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::440176885", "fields": {"term": "What is selection criteria?", "namespace": "open-p", "hash": 440176885, "terms": ["What is selection criteria?", "is selection criteria?", "selection criteria?", "criteria?"], "tokens": ["What", "is", "selection", "criteria", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-692693607", "fields": {"term": "What is Messagebus route?", "namespace": "open-p", "hash": -692693607, "terms": ["What is Messagebus route?", "is Messagebus route?", "Messagebus route?", "route?"], "tokens": ["What", "is", "Messagebus", "route", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2025713276", "fields": {"term": "How to send documents to default route?", "namespace": "open-p", "hash": 2025713276, "terms": ["How to send documents to default route?", "to send documents to default route?", "send documents to default route?", "documents to default route?", "to default route?", "default route?", "route?"], "tokens": ["How", "to", "send", "documents", "to", "default", "route", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1172910188", "fields": {"term": "Where can I find more details about Message Bus Routing?", "namespace": "open-p", "hash": 1172910188, "terms": ["Where can I find more details about Message Bus Routing?", "can I find more details about Message Bus Routing?", "I find more details about Message Bus Routing?", "find more details about Message Bus Routing?", "more details about Message Bus Routing?", "details about Message Bus Routing?", "about Message Bus Routing?", "Message Bus Routing?", "Bus Routing?", "Routing?"], "tokens": ["Where", "can", "I", "find", "more", "details", "about", "Message", "Bus", "Routing", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::195931480", "fields": {"term": "What adjustments must be made?", "namespace": "open-p", "hash": 195931480, "terms": ["What adjustments must be made?", "adjustments must be made?", "must be made?", "be made?", "made?"], "tokens": ["What", "adjustments", "must", "be", "made", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-646254001", "fields": {"term": "How are documents sent over the network?", "namespace": "open-p", "hash": -646254001, "terms": ["How are documents sent over the network?", "are documents sent over the network?", "documents sent over the network?", "sent over the network?", "over the network?", "the network?", "network?"], "tokens": ["How", "are", "documents", "sent", "over", "the", "network", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-635648948", "fields": {"term": "Can one message contain more than one document type?", "namespace": "open-p", "hash": -635648948, "terms": ["Can one message contain more than one document type?", "one message contain more than one document type?", "message contain more than one document type?", "contain more than one document type?", "more than one document type?", "than one document type?", "one document type?", "document type?", "type?"], "tokens": ["Can", "one", "message", "contain", "more", "than", "one", "document", "type", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1099725549", "fields": {"term": "What are targets in visit command?", "namespace": "open-p", "hash": -1099725549, "terms": ["What are targets in visit command?", "are targets in visit command?", "targets in visit command?", "in visit command?", "visit command?", "command?"], "tokens": ["What", "are", "targets", "in", "visit", "command", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-536834596", "fields": {"term": "What is vespa-visit-target?", "namespace": "open-p", "hash": -536834596, "terms": ["What is vespa-visit-target?", "is vespa-visit-target?", "vespa-visit-target?"], "tokens": ["What", "is", "vespa", "visit", "target", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-707482964", "fields": {"term": "What happens if the selection criteria maps to a specific set of buckets?", "namespace": "open-p", "hash": -707482964, "terms": ["What happens if the selection criteria maps to a specific set of buckets?", "happens if the selection criteria maps to a specific set of buckets?", "if the selection criteria maps to a specific set of buckets?", "the selection criteria maps to a specific set of buckets?", "selection criteria maps to a specific set of buckets?", "criteria maps to a specific set of buckets?", "maps to a specific set of buckets?", "to a specific set of buckets?", "a specific set of buckets?", "specific set of buckets?", "set of buckets?", "of buckets?", "buckets?"], "tokens": ["What", "happens", "if", "the", "selection", "criteria", "maps", "to", "a", "specific", "set", "of", "buckets", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1667852052", "fields": {"term": "What happens if the selection criteria does not map to a specific set of buckets?", "namespace": "open-p", "hash": -1667852052, "terms": ["What happens if the selection criteria does not map to a specific set of buckets?", "happens if the selection criteria does not map to a specific set of buckets?", "if the selection criteria does not map to a specific set of buckets?", "the selection criteria does not map to a specific set of buckets?", "selection criteria does not map to a specific set of buckets?", "criteria does not map to a specific set of buckets?", "does not map to a specific set of buckets?", "not map to a specific set of buckets?", "map to a specific set of buckets?", "to a specific set of buckets?", "a specific set of buckets?", "specific set of buckets?", "set of buckets?", "of buckets?", "buckets?"], "tokens": ["What", "happens", "if", "the", "selection", "criteria", "does", "not", "map", "to", "a", "specific", "set", "of", "buckets", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::908766187", "fields": {"term": "How does the client know if all buckets have been visited?", "namespace": "open-p", "hash": 908766187, "terms": ["How does the client know if all buckets have been visited?", "does the client know if all buckets have been visited?", "the client know if all buckets have been visited?", "client know if all buckets have been visited?", "know if all buckets have been visited?", "if all buckets have been visited?", "all buckets have been visited?", "buckets have been visited?", "have been visited?", "been visited?", "visited?"], "tokens": ["How", "does", "the", "client", "know", "if", "all", "buckets", "have", "been", "visited", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1819743596", "fields": {"term": "What options does the client have for bucket visiting order?", "namespace": "open-p", "hash": 1819743596, "terms": ["What options does the client have for bucket visiting order?", "options does the client have for bucket visiting order?", "does the client have for bucket visiting order?", "the client have for bucket visiting order?", "client have for bucket visiting order?", "have for bucket visiting order?", "for bucket visiting order?", "bucket visiting order?", "visiting order?", "order?"], "tokens": ["What", "options", "does", "the", "client", "have", "for", "bucket", "visiting", "order", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::303507134", "fields": {"term": "What are distributors?", "namespace": "open-p", "hash": 303507134, "terms": ["What are distributors?", "are distributors?", "distributors?"], "tokens": ["What", "are", "distributors", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::909453563", "fields": {"term": "What happens if two buckets are joined?", "namespace": "open-p", "hash": 909453563, "terms": ["What happens if two buckets are joined?", "happens if two buckets are joined?", "if two buckets are joined?", "two buckets are joined?", "buckets are joined?", "are joined?", "joined?"], "tokens": ["What", "happens", "if", "two", "buckets", "are", "joined", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-157687998", "fields": {"term": "How does the distributor reply to requests?", "namespace": "open-p", "hash": -157687998, "terms": ["How does the distributor reply to requests?", "does the distributor reply to requests?", "the distributor reply to requests?", "distributor reply to requests?", "reply to requests?", "to requests?", "requests?"], "tokens": ["How", "does", "the", "distributor", "reply", "to", "requests", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-139508383", "fields": {"term": "How does the progress counter work?", "namespace": "open-p", "hash": -139508383, "terms": ["How does the progress counter work?", "does the progress counter work?", "the progress counter work?", "progress counter work?", "counter work?", "work?"], "tokens": ["How", "does", "the", "progress", "counter", "work", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::665298009", "fields": {"term": "What is a content node?", "namespace": "open-p", "hash": 665298009, "terms": ["What is a content node?", "is a content node?", "a content node?", "content node?", "node?"], "tokens": ["What", "is", "a", "content", "node", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::842496005", "fields": {"term": "What is a visitor layer?", "namespace": "open-p", "hash": 842496005, "terms": ["What is a visitor layer?", "is a visitor layer?", "a visitor layer?", "visitor layer?", "layer?"], "tokens": ["What", "is", "a", "visitor", "layer", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::410395356", "fields": {"term": "How does the default iterator work?", "namespace": "open-p", "hash": 410395356, "terms": ["How does the default iterator work?", "does the default iterator work?", "the default iterator work?", "default iterator work?", "iterator work?", "work?"], "tokens": ["How", "does", "the", "default", "iterator", "work", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1267908271", "fields": {"term": "What is the default bucket space?", "namespace": "open-p", "hash": 1267908271, "terms": ["What is the default bucket space?", "is the default bucket space?", "the default bucket space?", "default bucket space?", "bucket space?", "space?"], "tokens": ["What", "is", "the", "default", "bucket", "space", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::369001427", "fields": {"term": "What is the bucket-space parameter?", "namespace": "open-p", "hash": 369001427, "terms": ["What is the bucket-space parameter?", "is the bucket-space parameter?", "the bucket-space parameter?", "bucket-space parameter?", "parameter?"], "tokens": ["What", "is", "the", "bucket", "space", "parameter", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-712661690", "fields": {"term": "How are documents co-located?", "namespace": "open-p", "hash": -712661690, "terms": ["How are documents co-located?", "are documents co-located?", "documents co-located?", "co-located?"], "tokens": ["How", "are", "documents", "co", "located", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::148114921", "fields": {"term": "What happens when dumping all documents?", "namespace": "open-p", "hash": 148114921, "terms": ["What happens when dumping all documents?", "happens when dumping all documents?", "when dumping all documents?", "dumping all documents?", "all documents?", "documents?"], "tokens": ["What", "happens", "when", "dumping", "all", "documents", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-554590343", "fields": {"term": "What is dumped count?", "namespace": "open-p", "hash": -554590343, "terms": ["What is dumped count?", "is dumped count?", "dumped count?", "count?"], "tokens": ["What", "is", "dumped", "count", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1588873329", "fields": {"term": "What causes slow visiting process?", "namespace": "open-p", "hash": 1588873329, "terms": ["What causes slow visiting process?", "causes slow visiting process?", "slow visiting process?", "visiting process?", "process?"], "tokens": ["What", "causes", "slow", "visiting", "process", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1108435736", "fields": {"term": "How to identify client-side bottleneck?", "namespace": "open-p", "hash": -1108435736, "terms": ["How to identify client-side bottleneck?", "to identify client-side bottleneck?", "identify client-side bottleneck?", "client-side bottleneck?", "bottleneck?"], "tokens": ["How", "to", "identify", "client", "side", "bottleneck", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::594594136", "fields": {"term": "How to identify if visiting processing is the bottleneck?", "namespace": "open-p", "hash": 594594136, "terms": ["How to identify if visiting processing is the bottleneck?", "to identify if visiting processing is the bottleneck?", "identify if visiting processing is the bottleneck?", "if visiting processing is the bottleneck?", "visiting processing is the bottleneck?", "processing is the bottleneck?", "is the bottleneck?", "the bottleneck?", "bottleneck?"], "tokens": ["How", "to", "identify", "if", "visiting", "processing", "is", "the", "bottleneck", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-654511554", "fields": {"term": "What causes visiting performance issues?", "namespace": "open-p", "hash": -654511554, "terms": ["What causes visiting performance issues?", "causes visiting performance issues?", "visiting performance issues?", "performance issues?", "issues?"], "tokens": ["What", "causes", "visiting", "performance", "issues", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-137100010", "fields": {"term": "How are non-attribute fields fetched?", "namespace": "open-p", "hash": -137100010, "terms": ["How are non-attribute fields fetched?", "are non-attribute fields fetched?", "non-attribute fields fetched?", "fields fetched?", "fetched?"], "tokens": ["How", "are", "non", "attribute", "fields", "fetched", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::876256763", "fields": {"term": "What fields may cause visiting to be limited by disk performance?", "namespace": "open-p", "hash": 876256763, "terms": ["What fields may cause visiting to be limited by disk performance?", "fields may cause visiting to be limited by disk performance?", "may cause visiting to be limited by disk performance?", "cause visiting to be limited by disk performance?", "visiting to be limited by disk performance?", "to be limited by disk performance?", "be limited by disk performance?", "limited by disk performance?", "by disk performance?", "disk performance?", "performance?"], "tokens": ["What", "fields", "may", "cause", "visiting", "to", "be", "limited", "by", "disk", "performance", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-699760428", "fields": {"term": "How can you optimize visiting performance?", "namespace": "open-p", "hash": -699760428, "terms": ["How can you optimize visiting performance?", "can you optimize visiting performance?", "you optimize visiting performance?", "optimize visiting performance?", "visiting performance?", "performance?"], "tokens": ["How", "can", "you", "optimize", "visiting", "performance", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1414435251", "fields": {"term": "What causes visit operation stall?", "namespace": "open-p", "hash": -1414435251, "terms": ["What causes visit operation stall?", "causes visit operation stall?", "visit operation stall?", "operation stall?", "stall?"], "tokens": ["What", "causes", "visit", "operation", "stall", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::196029123", "fields": {"term": "How can content cluster be inconsistent?", "namespace": "open-p", "hash": 196029123, "terms": ["How can content cluster be inconsistent?", "can content cluster be inconsistent?", "content cluster be inconsistent?", "cluster be inconsistent?", "be inconsistent?", "inconsistent?"], "tokens": ["How", "can", "content", "cluster", "be", "inconsistent", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-909383925", "fields": {"term": "What happens when replicas are still merging?", "namespace": "open-p", "hash": -909383925, "terms": ["What happens when replicas are still merging?", "happens when replicas are still merging?", "when replicas are still merging?", "replicas are still merging?", "are still merging?", "still merging?", "merging?"], "tokens": ["What", "happens", "when", "replicas", "are", "still", "merging", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::854730993", "fields": {"term": "Why does Vespa visit connect back?", "namespace": "open-p", "hash": 854730993, "terms": ["Why does Vespa visit connect back?", "does Vespa visit connect back?", "Vespa visit connect back?", "visit connect back?", "connect back?", "back?"], "tokens": ["Why", "does", "Vespa", "visit", "connect", "back", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1235926016", "fields": {"term": "What causes HANDSHAKE_FAILED error?", "namespace": "open-p", "hash": 1235926016, "terms": ["What causes HANDSHAKE_FAILED error?", "causes HANDSHAKE_FAILED error?", "HANDSHAKE_FAILED error?", "error?"], "tokens": ["What", "causes", "HANDSHAKE_FAILED", "error", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1295777377", "fields": {"term": "How to fix HANDSHAKE_FAILED error?", "namespace": "open-p", "hash": 1295777377, "terms": ["How to fix HANDSHAKE_FAILED error?", "to fix HANDSHAKE_FAILED error?", "fix HANDSHAKE_FAILED error?", "HANDSHAKE_FAILED error?", "error?"], "tokens": ["How", "to", "fix", "HANDSHAKE_FAILED", "error", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::939396156", "fields": {"term": "What is XGBoost?", "namespace": "open-p", "hash": 939396156, "terms": ["What is XGBoost?", "is XGBoost?", "XGBoost?"], "tokens": ["What", "is", "XGBoost", "?"], "corpus_count": 3, "document_count": 3}}, -{"put": "id:term:term::-1056914808", "fields": {"term": "What is dump_format?", "namespace": "open-p", "hash": -1056914808, "terms": ["What is dump_format?", "is dump_format?", "dump_format?"], "tokens": ["What", "is", "dump_format", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::590909937", "fields": {"term": "What is dump_model?", "namespace": "open-p", "hash": 590909937, "terms": ["What is dump_model?", "is dump_model?", "dump_model?"], "tokens": ["What", "is", "dump_model", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1751587822", "fields": {"term": "What is split_condition?", "namespace": "open-p", "hash": 1751587822, "terms": ["What is split_condition?", "is split_condition?", "split_condition?"], "tokens": ["What", "is", "split_condition", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::396880986", "fields": {"term": "What is fmap?", "namespace": "open-p", "hash": 396880986, "terms": ["What is fmap?", "is fmap?", "fmap?"], "tokens": ["What", "is", "fmap", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::148922173", "fields": {"term": "What is nodeid?", "namespace": "open-p", "hash": 148922173, "terms": ["What is nodeid?", "is nodeid?", "nodeid?"], "tokens": ["What", "is", "nodeid", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::21851739", "fields": {"term": "What is leaf?", "namespace": "open-p", "hash": 21851739, "terms": ["What is leaf?", "is leaf?", "leaf?"], "tokens": ["What", "is", "leaf", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1881667799", "fields": {"term": "What is dtrain in xgboost?", "namespace": "open-p", "hash": -1881667799, "terms": ["What is dtrain in xgboost?", "is dtrain in xgboost?", "dtrain in xgboost?", "in xgboost?", "xgboost?"], "tokens": ["What", "is", "dtrain", "in", "xgboost", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-967243527", "fields": {"term": "What is param in xgboost?", "namespace": "open-p", "hash": -967243527, "terms": ["What is param in xgboost?", "is param in xgboost?", "param in xgboost?", "in xgboost?", "xgboost?"], "tokens": ["What", "is", "param", "in", "xgboost", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1695496599", "fields": {"term": "What is bst.train in xgboost?", "namespace": "open-p", "hash": 1695496599, "terms": ["What is bst.train in xgboost?", "is bst.train in xgboost?", "bst.train in xgboost?", "in xgboost?", "xgboost?"], "tokens": ["What", "is", "bst", "train", "in", "xgboost", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1184093666", "fields": {"term": "What is the dump_model function?", "namespace": "open-p", "hash": -1184093666, "terms": ["What is the dump_model function?", "is the dump_model function?", "the dump_model function?", "dump_model function?", "function?"], "tokens": ["What", "is", "the", "dump_model", "function", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1907342538", "fields": {"term": "What is the input format for training data?", "namespace": "open-p", "hash": 1907342538, "terms": ["What is the input format for training data?", "is the input format for training data?", "the input format for training data?", "input format for training data?", "format for training data?", "for training data?", "training data?", "data?"], "tokens": ["What", "is", "the", "input", "format", "for", "training", "data", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::599113608", "fields": {"term": "What is the 'ranking' objective?", "namespace": "open-p", "hash": 599113608, "terms": ["What is the 'ranking' objective?", "is the 'ranking' objective?", "the 'ranking' objective?", "'ranking' objective?", "objective?"], "tokens": ["What", "is", "the", "'", "ranking", "'", "objective", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1006634903", "fields": {"term": "What is the xgboost training notebook?", "namespace": "open-p", "hash": 1006634903, "terms": ["What is the xgboost training notebook?", "is the xgboost training notebook?", "the xgboost training notebook?", "xgboost training notebook?", "training notebook?", "notebook?"], "tokens": ["What", "is", "the", "xgboost", "training", "notebook", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::808053062", "fields": {"term": "How is XGBoost trained?", "namespace": "open-p", "hash": 808053062, "terms": ["How is XGBoost trained?", "is XGBoost trained?", "XGBoost trained?", "trained?"], "tokens": ["How", "is", "XGBoost", "trained", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1533606721", "fields": {"term": "What is feature mapping?", "namespace": "open-p", "hash": -1533606721, "terms": ["What is feature mapping?", "is feature mapping?", "feature mapping?", "mapping?"], "tokens": ["What", "is", "feature", "mapping", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1789228147", "fields": {"term": "What is the format of feature-map.txt?", "namespace": "open-p", "hash": -1789228147, "terms": ["What is the format of feature-map.txt?", "is the format of feature-map.txt?", "the format of feature-map.txt?", "format of feature-map.txt?", "of feature-map.txt?", "feature-map.txt?"], "tokens": ["What", "is", "the", "format", "of", "feature", "map", "txt", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-642927746", "fields": {"term": "What does 'i' mean in feature mapping?", "namespace": "open-p", "hash": -642927746, "terms": ["What does 'i' mean in feature mapping?", "does 'i' mean in feature mapping?", "'i' mean in feature mapping?", "mean in feature mapping?", "in feature mapping?", "feature mapping?", "mapping?"], "tokens": ["What", "does", "'", "i", "'", "mean", "in", "feature", "mapping", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-468179920", "fields": {"term": "What does 'q' mean in feature mapping?", "namespace": "open-p", "hash": -468179920, "terms": ["What does 'q' mean in feature mapping?", "does 'q' mean in feature mapping?", "'q' mean in feature mapping?", "mean in feature mapping?", "in feature mapping?", "feature mapping?", "mapping?"], "tokens": ["What", "does", "'", "q", "'", "mean", "in", "feature", "mapping", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::270492765", "fields": {"term": "What does 'int' mean in feature mapping?", "namespace": "open-p", "hash": 270492765, "terms": ["What does 'int' mean in feature mapping?", "does 'int' mean in feature mapping?", "'int' mean in feature mapping?", "mean in feature mapping?", "in feature mapping?", "feature mapping?", "mapping?"], "tokens": ["What", "does", "'", "int", "'", "mean", "in", "feature", "mapping", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-380601678", "fields": {"term": "What is the sample demo for binary classification?", "namespace": "open-p", "hash": -380601678, "terms": ["What is the sample demo for binary classification?", "is the sample demo for binary classification?", "the sample demo for binary classification?", "sample demo for binary classification?", "demo for binary classification?", "for binary classification?", "binary classification?", "classification?"], "tokens": ["What", "is", "the", "sample", "demo", "for", "binary", "classification", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1785234475", "fields": {"term": "How to import XGBoost model?", "namespace": "open-p", "hash": -1785234475, "terms": ["How to import XGBoost model?", "to import XGBoost model?", "import XGBoost model?", "XGBoost model?", "model?"], "tokens": ["How", "to", "import", "XGBoost", "model", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::737171542", "fields": {"term": "Where to put XGBoost model for Vespa?", "namespace": "open-p", "hash": 737171542, "terms": ["Where to put XGBoost model for Vespa?", "to put XGBoost model for Vespa?", "put XGBoost model for Vespa?", "XGBoost model for Vespa?", "model for Vespa?", "for Vespa?", "Vespa?"], "tokens": ["Where", "to", "put", "XGBoost", "model", "for", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1438944187", "fields": {"term": "What is the directory structure for models in Vespa?", "namespace": "open-p", "hash": 1438944187, "terms": ["What is the directory structure for models in Vespa?", "is the directory structure for models in Vespa?", "the directory structure for models in Vespa?", "directory structure for models in Vespa?", "structure for models in Vespa?", "for models in Vespa?", "models in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["What", "is", "the", "directory", "structure", "for", "models", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::940161802", "fields": {"term": "Can an application package have multiple models?", "namespace": "open-p", "hash": 940161802, "terms": ["Can an application package have multiple models?", "an application package have multiple models?", "application package have multiple models?", "package have multiple models?", "have multiple models?", "multiple models?", "models?"], "tokens": ["Can", "an", "application", "package", "have", "multiple", "models", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-717141418", "fields": {"term": "How to deploy remote models in Vespa?", "namespace": "open-p", "hash": -717141418, "terms": ["How to deploy remote models in Vespa?", "to deploy remote models in Vespa?", "deploy remote models in Vespa?", "remote models in Vespa?", "models in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["How", "to", "deploy", "remote", "models", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::278491199", "fields": {"term": "What is the xgboost ranking feature?", "namespace": "open-p", "hash": 278491199, "terms": ["What is the xgboost ranking feature?", "is the xgboost ranking feature?", "the xgboost ranking feature?", "xgboost ranking feature?", "ranking feature?", "feature?"], "tokens": ["What", "is", "the", "xgboost", "ranking", "feature", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::879126042", "fields": {"term": "How is xgboost used in Vespa?", "namespace": "open-p", "hash": 879126042, "terms": ["How is xgboost used in Vespa?", "is xgboost used in Vespa?", "xgboost used in Vespa?", "used in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["How", "is", "xgboost", "used", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::492618030", "fields": {"term": "What is the format of the Vespa xgboost schema?", "namespace": "open-p", "hash": 492618030, "terms": ["What is the format of the Vespa xgboost schema?", "is the format of the Vespa xgboost schema?", "the format of the Vespa xgboost schema?", "format of the Vespa xgboost schema?", "of the Vespa xgboost schema?", "the Vespa xgboost schema?", "Vespa xgboost schema?", "xgboost schema?", "schema?"], "tokens": ["What", "is", "the", "format", "of", "the", "Vespa", "xgboost", "schema", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1754837989", "fields": {"term": "How is the Vespa ranking profile used?", "namespace": "open-p", "hash": -1754837989, "terms": ["How is the Vespa ranking profile used?", "is the Vespa ranking profile used?", "the Vespa ranking profile used?", "Vespa ranking profile used?", "ranking profile used?", "profile used?", "used?"], "tokens": ["How", "is", "the", "Vespa", "ranking", "profile", "used", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::2038635919", "fields": {"term": "How do you control the number of documents exposed to the model?", "namespace": "open-p", "hash": 2038635919, "terms": ["How do you control the number of documents exposed to the model?", "do you control the number of documents exposed to the model?", "you control the number of documents exposed to the model?", "control the number of documents exposed to the model?", "the number of documents exposed to the model?", "number of documents exposed to the model?", "of documents exposed to the model?", "documents exposed to the model?", "exposed to the model?", "to the model?", "the model?", "model?"], "tokens": ["How", "do", "you", "control", "the", "number", "of", "documents", "exposed", "to", "the", "model", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1441211023", "fields": {"term": "What factors determine run time complexity?", "namespace": "open-p", "hash": 1441211023, "terms": ["What factors determine run time complexity?", "factors determine run time complexity?", "determine run time complexity?", "run time complexity?", "time complexity?", "complexity?"], "tokens": ["What", "factors", "determine", "run", "time", "complexity", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-158972057", "fields": {"term": "What are the Vespa objective types?", "namespace": "open-p", "hash": -158972057, "terms": ["What are the Vespa objective types?", "are the Vespa objective types?", "the Vespa objective types?", "Vespa objective types?", "objective types?", "types?"], "tokens": ["What", "are", "the", "Vespa", "objective", "types", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1008682959", "fields": {"term": "How can XGBoost be used in Vespa?", "namespace": "open-p", "hash": 1008682959, "terms": ["How can XGBoost be used in Vespa?", "can XGBoost be used in Vespa?", "XGBoost be used in Vespa?", "be used in Vespa?", "used in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["How", "can", "XGBoost", "be", "used", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1028208116", "fields": {"term": "What is the sigmoid function used for?", "namespace": "open-p", "hash": 1028208116, "terms": ["What is the sigmoid function used for?", "is the sigmoid function used for?", "the sigmoid function used for?", "sigmoid function used for?", "function used for?", "used for?", "for?"], "tokens": ["What", "is", "the", "sigmoid", "function", "used", "for", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1005713326", "fields": {"term": "What is the XGBoost classifier?", "namespace": "open-p", "hash": -1005713326, "terms": ["What is the XGBoost classifier?", "is the XGBoost classifier?", "the XGBoost classifier?", "XGBoost classifier?", "classifier?"], "tokens": ["What", "is", "the", "XGBoost", "classifier", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1812492744", "fields": {"term": "How to import the XGBoost model?", "namespace": "open-p", "hash": -1812492744, "terms": ["How to import the XGBoost model?", "to import the XGBoost model?", "import the XGBoost model?", "the XGBoost model?", "XGBoost model?", "model?"], "tokens": ["How", "to", "import", "the", "XGBoost", "model", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::1145746595", "fields": {"term": "What is the dump model format?", "namespace": "open-p", "hash": 1145746595, "terms": ["What is the dump model format?", "is the dump model format?", "the dump model format?", "dump model format?", "model format?", "format?"], "tokens": ["What", "is", "the", "dump", "model", "format", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-956324750", "fields": {"term": "What is the base score?", "namespace": "open-p", "hash": -956324750, "terms": ["What is the base score?", "is the base score?", "the base score?", "base score?", "score?"], "tokens": ["What", "is", "the", "base", "score", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-747177635", "fields": {"term": "What is the predict_proba function equivalent in Vespa?", "namespace": "open-p", "hash": -747177635, "terms": ["What is the predict_proba function equivalent in Vespa?", "is the predict_proba function equivalent in Vespa?", "the predict_proba function equivalent in Vespa?", "predict_proba function equivalent in Vespa?", "function equivalent in Vespa?", "equivalent in Vespa?", "in Vespa?", "Vespa?"], "tokens": ["What", "is", "the", "predict_proba", "function", "equivalent", "in", "Vespa", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-238534676", "fields": {"term": "What information is lost when dumping XGBoost models?", "namespace": "open-p", "hash": -238534676, "terms": ["What information is lost when dumping XGBoost models?", "information is lost when dumping XGBoost models?", "is lost when dumping XGBoost models?", "lost when dumping XGBoost models?", "when dumping XGBoost models?", "dumping XGBoost models?", "XGBoost models?", "models?"], "tokens": ["What", "information", "is", "lost", "when", "dumping", "XGBoost", "models", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-1277516242", "fields": {"term": "How should features be scraped from Vespa for training?", "namespace": "open-p", "hash": -1277516242, "terms": ["How should features be scraped from Vespa for training?", "should features be scraped from Vespa for training?", "features be scraped from Vespa for training?", "be scraped from Vespa for training?", "scraped from Vespa for training?", "from Vespa for training?", "Vespa for training?", "for training?", "training?"], "tokens": ["How", "should", "features", "be", "scraped", "from", "Vespa", "for", "training", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-668122184", "fields": {"term": "What precision should be used when reading Vespa features for training?", "namespace": "open-p", "hash": -668122184, "terms": ["What precision should be used when reading Vespa features for training?", "precision should be used when reading Vespa features for training?", "should be used when reading Vespa features for training?", "be used when reading Vespa features for training?", "used when reading Vespa features for training?", "when reading Vespa features for training?", "reading Vespa features for training?", "Vespa features for training?", "features for training?", "for training?", "training?"], "tokens": ["What", "precision", "should", "be", "used", "when", "reading", "Vespa", "features", "for", "training", "?"], "corpus_count": 1, "document_count": 1}}, -{"put": "id:term:term::-823921928", "fields": {"term": "Why might text matching features differ in a distributed setting?", "namespace": "open-p", "hash": -823921928, "terms": ["Why might text matching features differ in a distributed setting?", "might text matching features differ in a distributed setting?", "text matching features differ in a distributed setting?", "matching features differ in a distributed setting?", "features differ in a distributed setting?", "differ in a distributed setting?", "in a distributed setting?", "a distributed setting?", "distributed setting?", "setting?"], "tokens": ["Why", "might", "text", "matching", "features", "differ", "in", "a", "distributed", "setting", "?"], "corpus_count": 1, "document_count": 1}} -] diff --git a/test/README.md b/test/README.md deleted file mode 100644 index 7055180640..0000000000 --- a/test/README.md +++ /dev/null @@ -1,10 +0,0 @@ - - -# Documentation testing - -The test suite is exactly the same as in [sample-apps](https://github.com/vespa-engine/sample-apps), -refer to [sample-apps/test/README.md](https://github.com/vespa-engine/sample-apps/blob/master/test/README.md). -Only [_test_config.yml](_test_config.yml) should differ. - -Ideally, this directory did not have to be copied, better if test scripts added to a test docker image -or test artifacts pulled from a central location. diff --git a/test/_test_config-large.yml b/test/_test_config-large.yml deleted file mode 100644 index c2c9e386d3..0000000000 --- a/test/_test_config-large.yml +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright Vespa.ai. All rights reserved. - -# Use a comma-separated list of URLs to test multiple pages as one unit, like the news-tutorial - -urls: - - >- - en/learn/tutorials/news-1-deploy-an-application.md, - en/learn/tutorials/news-2-basic-feeding-and-query.md, - en/learn/tutorials/news-3-searching.md, - en/learn/tutorials/news-4-embeddings.md, - en/learn/tutorials/news-5-recommendation.md, - en/learn/tutorials/news-6-recommendation-with-searchers.md, - en/learn/tutorials/news-7-recommendation-with-parent-child.md diff --git a/test/_test_config.yml b/test/_test_config.yml deleted file mode 100644 index 46c6f3f339..0000000000 --- a/test/_test_config.yml +++ /dev/null @@ -1,24 +0,0 @@ -# Copyright Vespa.ai. All rights reserved. - -# Use a comma-separated list of URLs to test multiple pages as one unit, like the news-tutorial - -urls: - - en/learn/tutorials/hybrid-search.md - - en/querying/searching-multivalue-fields.md - - en/ranking/reranking-in-searcher.md - - en/basics/deploy-an-application-local-java.html - - en/performance/practical-search-performance-guide.md - - en/querying/nearest-neighbor-search-guide.md - - >- - en/basics/deploy-an-application-local.html, - en/operations/self-managed/monitoring.html - - # The text-search tutorial is OK but text-search-ml.html is not yet properly tested from scraping to training. - # Need more work with a scraped dataset which we can publish, then train the model and deploy. - - >- - en/learn/tutorials/text-search.md, - en/learn/tutorials/text-search-ml.md - - # https://docs.vespa.ai/en/operations/multinode-systems.html Tests not implemented for AWS EC2/ECS procedures - # Kubernetes testing is blocked on running minikube on Centos, in Docker - needs more work - # - https://docs.vespa.ai/en/operations/using-kubernetes-with-vespa.html diff --git a/test/deduplicate_suggestions.py b/test/deduplicate_suggestions.py deleted file mode 100644 index c9ecae1f29..0000000000 --- a/test/deduplicate_suggestions.py +++ /dev/null @@ -1,100 +0,0 @@ -#!/usr/bin/env python3 -""" -Script to deduplicate suggestions_index.json based on the 'term' field (case-insensitive). -""" - -import json -import sys -from pathlib import Path - - -def deduplicate_suggestions(input_file, output_file): - """ - Read JSON objects from input file (one per line), deduplicate based on - lowercased 'term' field, and write to output file. - - Args: - input_file: Path to input JSON file - output_file: Path to output JSON file - """ - seen_terms = {} # Dictionary to track lowercased terms - deduplicated_items = [] - duplicate_count = 0 - - with open(input_file, 'r', encoding='utf-8') as f: - lines = f.readlines() - - # Check if the file starts with an array bracket - if lines and lines[0].strip() == '[': - # Skip the opening bracket - start_idx = 1 - else: - start_idx = 0 - - for line_num, line in enumerate(lines[start_idx:], start=start_idx + 1): - line = line.strip() - - # Skip empty lines and closing bracket - if not line or line == ']': - continue - - # Remove trailing comma if present - if line.endswith(','): - line = line[:-1] - - try: - item = json.loads(line) - - # Extract the term field - if 'fields' in item and 'term' in item['fields']: - term = item['fields']['term'] - term_lower = term.lower() - - # Check if we've seen this term before - if term_lower not in seen_terms: - seen_terms[term_lower] = term - deduplicated_items.append(item) - else: - duplicate_count += 1 - print(f"Skipping duplicate term: '{term}' (original: '{seen_terms[term_lower]}')") - else: - # If no term field, keep the item anyway - deduplicated_items.append(item) - print(f"Warning: Item at line {line_num} has no 'term' field") - - except json.JSONDecodeError as e: - print(f"Error parsing JSON at line {line_num}: {e}") - continue - - # Write deduplicated items to output file - with open(output_file, 'w', encoding='utf-8') as f: - f.write('[\n') - for i, item in enumerate(deduplicated_items): - json_line = json.dumps(item, ensure_ascii=False) - if i < len(deduplicated_items) - 1: - f.write(json_line + ', \n') - else: - f.write(json_line + '\n') - f.write(']\n') - - print(f"\nDeduplication complete!") - print(f"Original items: {len(lines) - 1}") # Minus 1 for opening bracket - print(f"Unique items: {len(deduplicated_items)}") - print(f"Duplicates removed: {duplicate_count}") - print(f"Output written to: {output_file}") - - -if __name__ == "__main__": - # Get the script directory and go up one level to find the file - script_dir = Path(__file__).parent - project_dir = script_dir.parent - - input_file = project_dir / "suggestions_index.json" - output_file = project_dir / "suggestions_index_deduplicated.json" - - if not input_file.exists(): - print(f"Error: Input file not found: {input_file}") - sys.exit(1) - - deduplicate_suggestions(input_file, output_file) - diff --git a/test/pseudo_terminal.py b/test/pseudo_terminal.py deleted file mode 100644 index 096a9d414c..0000000000 --- a/test/pseudo_terminal.py +++ /dev/null @@ -1,97 +0,0 @@ -#! /usr/bin/env python3 - -# Copyright Vespa.ai. All rights reserved. - -import os -import re -import sys -import random -import pexpect - - -class Log: - def __init__(self): - self._log = None - self._filter_pattern = None - self._verbose = False - self.reset_log() - - def reset_log(self, verbose=False): - self._log = [] - self._verbose = verbose - - def stdout_filter(self, pattern): - self._filter_pattern = pattern - - def get_log(self): - return "".join(self._log) - - def write(self, s): - self._log.append(s) - if re.match(self._filter_pattern, s) is None: - if self._verbose: - sys.stdout.write(s) - - def flush(self): - sys.stdout.flush() - - -class PseudoTerminal: - def __init__(self, timeout=30*60): - self._log = Log() - self._pty = None - self._cmd_timeout = timeout - - def __enter__(self): - self.start() - return self - - def __exit__(self, exc_type, exc_val, exc_tb): - self.stop() - - def start(self): - env = os.environ.copy() - env["PS1"] = "" # remove default terminal prompt - env["PS2"] = "" - self._pty = pexpect.spawn('sh', env=env, echo=False, encoding='utf-8') - self._pty.logfile_read = self._log - - def stop(self): - self._pty.close() - - def run(self, command, verbose): - command_id = random.randint(10000, 100000) - sentinel = "sentinel-{0}> exit code: ".format(command_id) - sentinel_pattern = re.compile(sentinel + "(\\d+)") - - self._log.reset_log(verbose) - self._log.stdout_filter(sentinel_pattern) - self._pty.sendline(command) - self._pty.sendline("echo \"" + sentinel + "$?\"") - - index = self._pty.expect([sentinel_pattern, pexpect.EOF, pexpect.TIMEOUT], timeout=self._cmd_timeout) - if index == 0: - if self._pty.match is not None: - exit_code = int(self._pty.match.group(1)) - output = self._log.get_log() - return exit_code, output - raise RuntimeError("Unexpected state: Found pattern, but not regexp match.") - if index == 1: - raise RuntimeError("Unexpected EOF in pseudo-terminal") - if index == 2: - raise RuntimeError("Timeout in execution of {}".format(command)) - raise RuntimeError("Unexpected state") - - def run_expect(self, command, expect, timeout, verbose): - self._log.reset_log(verbose) - self._pty.sendline(command) - - index = self._pty.expect([expect, pexpect.EOF, pexpect.TIMEOUT], timeout=int(timeout)) - if index == 0: - return 0, self._log.get_log() - if index == 1: - raise RuntimeError("Unexpected EOF in pseudo-terminal") - if index == 2: - raise RuntimeError("Timeout in execution of {}".format(command)) - raise RuntimeError("Unexpected state") - diff --git a/test/requirements.txt b/test/requirements.txt deleted file mode 100644 index 3f8cd8216e..0000000000 --- a/test/requirements.txt +++ /dev/null @@ -1,4 +0,0 @@ -pexpect -pyyaml -beautifulsoup4 -requests diff --git a/test/test.py b/test/test.py deleted file mode 100755 index 7c114bdfce..0000000000 --- a/test/test.py +++ /dev/null @@ -1,394 +0,0 @@ -#! /usr/bin/env python3 -# Copyright Vespa.ai. All rights reserved. - -import io -import os -import sys -import getopt -import json -import time -import yaml -import urllib.request -import tempfile -import re - -from bs4 import BeautifulSoup - -from distutils.command import clean -from pseudo_terminal import PseudoTerminal - - -def remove_liquid_highlight(text): - """Removes Liquid highlighting tags from a string. - - Args: - text: The string to remove highlighting from. - - Returns: - The string with highlighting tags removed. - """ - - text = re.sub(r'\{%\s*highlight\s*(python|xml|.*?)\s*%\}', '', text) - text = re.sub(r'\{%\s*endhighlight\s*%\}', '', text) - return text - - -################################################################################ -# Execution -################################################################################ - -verbose = False -workdir = "." -project_root = os.getcwd() -work_dir = os.path.join(project_root, "_work") -liquid_transforms = {} - -def print_separator(): - print("") - print("*" * 120) - -def print_info(cmd, extra=""): - print("* {0} {1}".format(cmd, extra)) - -def print_cmd(cmd, extra=""): - print("> {0}".format(cmd)) - if len(extra) > 0: - print(": {0}".format(extra)) - - -def exec_wait(cmd, pty): - command = cmd["$"] - expect = cmd["wait-for"] - max_wait = 300 if not ("timeout" in cmd) else int(cmd["timeout"]) - try_interval = 5 # todo: max this configurable too - print_cmd(command, "Waiting for '{0}'".format(expect)) - - waited = 0 - output = "" - while waited < max_wait: - exit_code, output = pty.run(command, verbose) - if output.find(expect) >= 0: - return - else: - time.sleep(try_interval) - waited += try_interval - print("Waited for {0}/{1} seconds...".format(waited, max_wait)) - - if waited >= max_wait: - if not verbose: - print(output) - raise RuntimeError("Expected output '{0}' not found in command '{1}'. Waited for {2} seconds.".format(expect, command, max_wait)) - - -def exec_assert(cmd, pty): - command = cmd["$"] - expect = cmd["contains"] - print_cmd(command, "Expecting '{0}'".format(expect)) - - _, output = pty.run(command, verbose) - if output.find(expect) == -1: - if not verbose: - print(output) - raise RuntimeError("Expected output '{0}' not found in command '{1}'".format(expect, command)) - - -def exec_file(cmd, pty): - path = cmd["path"] - print_cmd(path) - path_array = [] - for dir in path.split(os.path.sep)[:-1]: - path_array.append(dir) - if not os.path.isdir(os.path.sep.join(path_array)): - os.makedirs(os.path.sep.join(path_array)) - with open(str(path), "w") as f: - data = str(cmd["content"]) - clean_data = remove_liquid_highlight(data) - f.write(clean_data) - - print("Wrote " + str(len(cmd["content"])) + " chars to " + path) - - -def exec_expect(cmd, pty): - command = cmd["$"] - expect = cmd["expect"] - timeout = cmd["timeout"] - print_cmd(command, "Expecting '{0}'".format(expect)) - - exit_code, output = pty.run_expect(command, expect, timeout, verbose) - if exit_code != 0: - if not verbose: - print(output) - raise RuntimeError("Command '{0}' returned code {1}".format(command, exit_code)) - - -def exec_default(cmd, pty): - command = cmd["$"] - print_cmd(command) - - exit_code, output = pty.run(command, verbose) - if exit_code != 0: - if not verbose: - print(output) - raise RuntimeError("Command '{0}' returned code {1}".format(command, exit_code)) - - -def exec_step(cmd, pty): - globals()["exec_" + cmd["type"]](cmd, pty) - - -def exec_script(script): - tmpdir = tempfile.mkdtemp(dir=work_dir) - os.chdir(tmpdir) - - failed = False - - with PseudoTerminal(timeout=2*60*60) as pty: - try: - for cmd in script["before"]: - exec_step(cmd, pty) - for cmd in script["steps"]: - exec_step(cmd, pty) - except Exception as e: - sys.stderr.write("ERROR: {0}\n".format(e)) - failed = True - finally: - for cmd in script["after"]: - try: - exec_step(cmd, pty) - except Exception as e: - sys.stderr.write("ERROR: {0}\n".format(e)) - failed = True - - if failed: - raise RuntimeError("One or more commands failed") - - -################################################################################ -# Parsing -################################################################################ - -def parse_cmd(cmd, attrs): - cmd = cmd.strip() - if cmd.startswith("#"): - return None - if cmd.startswith("$"): - cmd = cmd[1:] - cmd = cmd.strip() - if len(cmd) == 0: - return None - - if "data-test-wait-for" in attrs: - if "data-test-timeout" in attrs: - return {"$": cmd, - "type": "wait", - "wait-for": attrs["data-test-wait-for"], - "timeout": attrs["data-test-timeout"]} - else: - return {"$": cmd, "type": "wait", "wait-for": attrs["data-test-wait-for"]} - if "data-test-assert-contains" in attrs: - return {"$": cmd, "type": "assert", "contains": attrs["data-test-assert-contains"]} - if "data-test-expect" in attrs: - return {"$": cmd, "type": "expect", "expect": attrs["data-test-expect"], "timeout": attrs["data-test-timeout"]} - return {"$": cmd, "type": "default"} - - -def process_liquid(command): - for key, value in liquid_transforms.items(): - command = re.sub(key, value, command) - - return command - - -def parse_cmds(pre, attrs): - cmds = [] - line_continuation = "" - line_continuation_delimiter = "\\" - - sanitized_cmd = process_liquid(pre) - - for line in sanitized_cmd.split("\n"): - cmd = "{0} {1}".format(line_continuation, line.strip()) - if cmd.endswith(line_continuation_delimiter): - line_continuation = cmd[:-len(line_continuation_delimiter)] - else: - cmd = parse_cmd(cmd, attrs) - if cmd != None: - cmds.append(cmd) - line_continuation = "" - return cmds - - -def parse_file(pre, attrs): - if not "data-path" in attrs: - raise ValueError("File element does not have required 'data-path' attribute.") - path = attrs["data-path"] - if path[0] == "/": - raise ValueError("Absolute file paths are not permitted") - if ".." in path: - raise ValueError("'..' not permitted in file paths") - content = "" - for line in pre: - if "ProcessingInstruction" in str(type(line)): # xml: - content += "" - else: - content += str(line) - content = content[1:] if content[0] == "\n" else content - return {"type": "file", "content": content, "path": path} - - -def parse_page(html): - script = { - "before": [], - "steps": [], - "after": [] - } - - soup = BeautifulSoup(html, "html.parser") - - for pre in soup.find_all(lambda tag: (tag.name == "pre" or tag.name == "div") and tag.has_attr("data-test")): - if pre.attrs["data-test"] == "before": - script["before"].extend(parse_cmds(pre.string, pre.attrs)) - - if pre.attrs["data-test"] == "exec": - script["steps"].extend(parse_cmds(pre.string, pre.attrs)) - - if pre.attrs["data-test"] == "file": - script["steps"].append(parse_file(pre.contents, pre.attrs)) - - if pre.attrs["data-test"] == "after": - script["after"].extend(parse_cmds(pre.string, pre.attrs)) - - return script - - -def process_page(html, source_name=""): - script = parse_page(html) - - print_info("Script to execute:", extra=source_name) - print(json.dumps(script, indent=2)) - - exec_script(script) - - -################################################################################ -# Running -################################################################################ - -def create_work_dir(): - os.chdir(project_root) - if not os.path.isdir(work_dir): - os.makedirs(work_dir) - - -def run_url(url): - print_separator() - print_info("Testing", url) - allpages = b"" - for page in url.split(","): - page = page.strip() - if page.startswith("http"): - allpages += urllib.request.urlopen(page).read() - else: - with open(workdir + '/' + page, 'rb') as f: - allpages += f.read() - - process_page(allpages, url) - - -def run_config(config_file): - failed = [] - if not os.path.isfile(config_file): - config_file = os.path.join("test", config_file) - if not os.path.isfile(config_file): - raise RuntimeError("Could not find configuration file") - - with open(config_file, "r") as f: - config = yaml.safe_load(f) - for url in config["urls"]: - try: - run_url(url) - except RuntimeError: - failed.append(url) - - if len(failed) > 0: - raise RuntimeError("Tests in " + ", ".join(failed) + " failed") - - -def run_file(file_name): - if file_name.startswith("http"): - run_url(file_name) - elif file_name == "-": - process_page(sys.stdin.read(), "stdin") - else: - with io.open(file_name, 'r', encoding="utf-8") as f: - process_page(f.read(), file_name) - - -def run_with_arguments(): - global verbose - global workdir - config_file = "" - argv = sys.argv[1:] - - try: - opts, args = getopt.getopt(argv, "vc:w:") - except getopt.GetoptError: - print("test.py [-v] [-c configfile] -w [workdir] [file-to-run]") - sys.exit(2) - - for opt, arg in opts: - if opt in "-v": - verbose = True - elif opt in "-c": - config_file = arg - elif opt in "-w": - workdir = arg - - load_liquid_transforms() - - if len(config_file): - run_config(config_file) - elif args: - run_file(args[0]) - else: - run_config("_test_config.yml") - - -def load_liquid_transforms(): - global liquid_transforms - global workdir - site_config_file = "_config.yml" - site_config = "_config.yml" - - if not os.path.isfile(site_config): - site_config = os.path.join("../", site_config_file) - if not os.path.isfile(site_config): - site_config = os.path.join(workdir, site_config_file) - if not os.path.isfile(site_config): - raise RuntimeError("Could not find " + site_config_file) - - # Transforms for site variables like {{site.variables.vespa_version}} - with open(site_config, "r") as f: - config = yaml.safe_load(f) - if "variables" in config: - for key, value in config["variables"].items(): - liquid_transforms[r"{{\s*site.variables."+key+r"\s*}}"] = value - - # Remove liquid macros, e.g.: - # {% highlight shell %} {% endhighlight %} - # {% raw %} {% endraw %} - liquid_transforms[r"{%\s*.*highlight\s*.*%}"] = "" - liquid_transforms[r"{%\s*.*raw\s*%}"] = "" - - -def main(): - create_work_dir() - - try: - run_with_arguments() - except Exception as e: - sys.stderr.write("ERROR: {0}\n".format(e)) - sys.exit(1) - -if __name__ == "__main__": - main() diff --git a/test/test_queries.py b/test/test_queries.py deleted file mode 100755 index d8373569b9..0000000000 --- a/test/test_queries.py +++ /dev/null @@ -1,55 +0,0 @@ -#! /usr/bin/env python3 -# Copyright Vespa.ai. All rights reserved. - -import os -import sys -import re -import requests -from urllib.parse import quote -from bs4 import BeautifulSoup - -links = set() - - -def extract_links(filename): - with open(filename) as fp: - soup = BeautifulSoup(fp, 'html.parser') - for anchor in soup.find_all('a', {"class": "yql-x"}): - sanitized_query = re.sub('\n', '', anchor.text) - links.add((filename, 'https://api.search.vespa.ai/search/?yql=' + quote(sanitized_query))) - for anchor in soup.find_all('a', {"class": "querystring-x"}): - sanitized_query = re.sub('\n', '', anchor.text) - links.add((filename, 'https://api.search.vespa.ai/search/?' + quote(sanitized_query, safe='&='))) - - -def get_links(directory): - print("Scanning files in {}".format(directory)) - for root, dirs, files in os.walk(directory): - for filename in files: - if filename.endswith('.html'): - extract_links(os.path.join(root, filename)) - - -def check_total_count(querytuple): - r = requests.get(querytuple[1]) - d = r.json() - total_count = d["root"]["fields"]["totalCount"] - print("{0} results: {1}".format(total_count, querytuple)) - return total_count > 0 - - -def main(): - if len(sys.argv) <= 1: - sys.exit(("Missing directory argument")) - directory = sys.argv[1] - if not os.path.exists(directory): - sys.exit(("{} does not exist".format(directory))) - get_links(directory) - print("Checking queries: {}".format(links)) - for querytuple in links: - if not check_total_count(querytuple): - sys.exit("Query failed") - - -if __name__ == "__main__": - main() From c9078f881edfc5148748fa42951fe9080d7090a1 Mon Sep 17 00:00:00 2001 From: Trevor Date: Mon, 13 Jul 2026 09:19:42 -0700 Subject: [PATCH 2/2] Sync Mintlify migration with staging Bring the migration PR in line with mintlify-onboarding/vespa-preview@7de97353. Co-authored-by: Cursor --- custom.css | 227 +++++++++++++++-- docs.json | 40 ++- en/operations/automated-deployments.mdx | 279 ++++++++++++++------- index.mdx | 308 ++++++++++++------------ 4 files changed, 566 insertions(+), 288 deletions(-) diff --git a/custom.css b/custom.css index fc31b6ae9c..a00fd38d24 100644 --- a/custom.css +++ b/custom.css @@ -1,28 +1,63 @@ +:root { + --vespa-lyng: #61d790; + --vespa-berg: #2e2f27; + --vespa-vassdrag: #b7e2f1; + --vespa-sno: #ffffff; + --vespa-skjaer: #c4c5b9; +} + #navbar { - border-bottom: 1px solid color-mix(in srgb, currentColor 10%, transparent); + border-bottom: 1px solid var(--vespa-skjaer); backdrop-filter: blur(12px); } +#topbar-cta-button svg { + width: inherit; + height: inherit; + color: inherit; + stroke: currentColor; + margin-left: 5px; +} + +#topbar-cta-button svg path { + stroke: inherit; + stroke-width: inherit; + stroke-linecap: inherit; + stroke-linejoin: inherit; +} + +.dark #topbar-cta-button > a:hover { + color: var(--vespa-sno) !important; +} + +#sidebar { + margin-top: 1px; +} + html:not(.dark) #search-bar-entry-mobile svg { - background-color: #525654 !important; + background-color: var(--vespa-berg) !important; } html:not(.dark) #assistant-entry-mobile svg, html:not(.dark) button[data-component-name="theme-toggle"] svg { - color: #525654 !important; + color: var(--vespa-berg) !important; } .dark #search-bar-entry-mobile svg { - background-color: #d0d4d2 !important; + background-color: var(--vespa-lyng) !important; } .dark #assistant-entry-mobile svg, .dark button[data-component-name="theme-toggle"] svg { - color: #d0d4d2 !important; + color: var(--vespa-lyng) !important; +} + +.dark #theme-preference-menu-content { + background-color: var(--vespa-berg) !important; } mdx-content { - line-height: 1.7; + line-height: 1.2; } mdx-content a { @@ -35,7 +70,7 @@ mdx-content a { max-width: 100%; overflow-x: auto; border-collapse: collapse; - border: 1px solid rgba(20, 130, 76, 0.18); + border: 1px solid var(--vespa-skjaer); border-radius: 0.75rem; font-size: 0.92rem; } @@ -45,15 +80,20 @@ mdx-content a { min-width: 9rem; padding: 0.7rem 0.85rem; vertical-align: top; - border-bottom: 1px solid rgba(20, 130, 76, 0.14); } .vespa-table th { - background: rgba(20, 130, 76, 0.08); - font-weight: 650; + color: var(--vespa-berg); + background: var(--vespa-vassdrag); + font-weight: 600; text-align: left; } +.vespa-table tbody tr:last-child, +.vespa-table tbody tr:last-child > td { + border-bottom: 0 !important; +} + .vespa-table code { white-space: normal; overflow-wrap: anywhere; @@ -67,33 +107,168 @@ mdx-content a { text-align: right; } +.vespa-pipeline-mark { + --mark-color: #796bb3; + position: relative; + display: inline-block; + width: 2rem; + height: 2rem; + box-sizing: border-box; + color: var(--mark-color); + vertical-align: middle; +} + +.vespa-pipeline-mark--square { + background: currentColor; +} + +.vespa-pipeline-mark--square-outline { + border: 2px solid currentColor; +} + +.vespa-pipeline-mark--circle { + border-radius: 50%; + background: currentColor; +} + +.vespa-pipeline-mark--clock { + border: 2px solid currentColor; + border-radius: 50%; +} + +.vespa-pipeline-mark--clock::before, +.vespa-pipeline-mark--clock::after { + position: absolute; + left: 50%; + top: 50%; + display: block; + width: 2px; + background: currentColor; + content: ""; + transform-origin: 50% 0; +} + +.vespa-pipeline-mark--clock::before { + height: 0.55rem; + transform: translate(-1px, -0.55rem); +} + +.vespa-pipeline-mark--clock::after { + height: 0.4rem; + transform: translate(-1px, 0) rotate(125deg); +} + +.vespa-pipeline-mark--running { + --mark-color: #b6aed5; +} + +.vespa-pipeline-mark--failed { + --mark-color: #e97a57; +} + +.vespa-pipeline-mark--unknown { + --mark-color: #c4c5b9; +} + +.vespa-pipeline-mark--pending { + background: currentColor; +} + +.vespa-pipeline-mark--pending::after { + position: absolute; + right: -0.35rem; + bottom: -0.35rem; + display: grid; + width: 1rem; + height: 1rem; + place-items: center; + border-radius: 50%; + color: var(--vespa-berg); + background: var(--vespa-lyng); + content: "+"; + font-size: 0.85rem; + font-weight: 600; + line-height: 1; +} + +.vespa-pipeline-mark--paused { + border-radius: 50%; + background: currentColor; +} + +.vespa-pipeline-mark--paused::before, +.vespa-pipeline-mark--paused::after { + position: absolute; + top: 0.55rem; + width: 0.3rem; + height: 0.9rem; + background: var(--vespa-sno); + content: ""; +} + +.vespa-pipeline-mark--paused::before { + left: 0.55rem; +} + +.vespa-pipeline-mark--paused::after { + right: 0.55rem; +} + +.vespa-pipeline-bars { + display: inline-grid; + width: 2rem; + height: 2rem; + gap: 0.22rem; + vertical-align: middle; +} + +.vespa-pipeline-bars span { + display: block; + background: #796bb3; +} + +.vespa-pipeline-bars--vertical { + grid-template-columns: repeat(3, 1fr); +} + +.vespa-pipeline-bars--horizontal { + grid-template-rows: repeat(3, 1fr); +} + toc-item { border-radius: 0.5rem; + justify-content: center; + display: flex; + align-items: center; +} + +.toc-item > svg { + margin-top: 9px; } toc-item[data-active], toc-item[data-active-deepest] { - color: #14824c; + color: var(--vespa-berg); + font-weight: 600; } -@media (prefers-color-scheme: dark) { - .vespa-table { - border-color: rgba(97, 215, 144, 0.22); - } +.dark .vespa-table { + border-color: var(--vespa-skjaer); +} - .vespa-table th, - .vespa-table td { - border-bottom-color: rgba(97, 215, 144, 0.16); - } +.dark .vespa-table th, +.dark .vespa-table td { + border-bottom-color: var(--vespa-skjaer); +} - .vespa-table th { - background: rgba(97, 215, 144, 0.09); - } +.dark .vespa-table th { + color: var(--vespa-berg); + background: var(--vespa-lyng); +} - toc-item[data-active], - toc-item[data-active-deepest] { - color: #61d790; - } +.dark toc-item[data-active], +.dark toc-item[data-active-deepest] { + color: var(--vespa-lyng); } @media (max-width: 768px) { diff --git a/docs.json b/docs.json index e93d6a4bd3..03b6433831 100644 --- a/docs.json +++ b/docs.json @@ -3,42 +3,42 @@ "theme": "linden", "name": "Vespa Documentation", "colors": { - "primary": "#61D78F", - "light": "#61D78F", - "dark": "#61D78F" + "primary": "#2E2F27", + "light": "#61D790", + "dark": "#7B7D68" }, "background": { "color": { "light": "#FFFFFF", - "dark": "#2E2E26" + "dark": "#2E2F27" } }, "favicon": "/favicon.png", "appearance": { - "default": "system" + "default": "light" }, "fonts": { "heading": { "family": "Roobert", - "source": "https://vespa.ai/vespa-content/themes/website-wp-theme/fonts/Roobert-Medium.woff", - "format": "woff" + "source": "https://vespa.ai/vespa-content/themes/website-wp-theme/fonts/Roobert-SemiBold.woff", + "format": "woff", + "weight": 600 }, "body": { "family": "Roobert", "source": "https://vespa.ai/vespa-content/themes/website-wp-theme/fonts/Roobert-Regular.woff", - "format": "woff" + "format": "woff", + "weight": 400 } }, "navigation": { "tabs": [ { "tab": "Home", - "icon": "house", "pages": ["index"] }, { "tab": "Guides", - "icon": "book-open", "pages": [ { "group": "Vespa Basics", @@ -387,7 +387,6 @@ }, { "tab": "FAQ", - "icon": "circle-question", "pages": [ { "group": "FAQ", @@ -397,7 +396,6 @@ }, { "tab": "Reference", - "icon": "code", "groups": [ { "group": "APIs", @@ -561,18 +559,15 @@ "anchors": [ { "anchor": "Changelog", - "href": "/en/reference/release-notes/vespa9", - "icon": "clock-rotate-left" + "href": "/en/reference/release-notes/vespa9" }, { "anchor": "Vespa 8 release notes", - "href": "/en/reference/release-notes/vespa8", - "icon": "list" + "href": "/en/reference/release-notes/vespa8" }, { "anchor": "Vespa 7 release notes", - "href": "/en/reference/release-notes/vespa7", - "icon": "list" + "href": "/en/reference/release-notes/vespa7" } ] } @@ -586,17 +581,16 @@ "links": [ { "label": "Blog", - "href": "https://blog.vespa.ai/", - "icon": "newspaper" + "href": "https://blog.vespa.ai/" }, { "label": "vespa.ai", - "href": "https://vespa.ai/", - "icon": "external-link" + "href": "https://vespa.ai/" }, { - "label": " ", + "label": "GitHub", "icon": "github", + "iconType": "brands", "href": "https://github.com/vespa-engine/vespa/" } ], diff --git a/en/operations/automated-deployments.mdx b/en/operations/automated-deployments.mdx index d6bbe34cfc..f6b2a3feba 100644 --- a/en/operations/automated-deployments.mdx +++ b/en/operations/automated-deployments.mdx @@ -3,7 +3,8 @@ title: "Automated Deployments" --- -![Picture of an automated deployment](/assets/img/automated-deployments-overview.png) + ![Picture of an automated + deployment](/assets/img/automated-deployments-overview.png) See [pipeline graph](#pipeline-graph) for details on the visual elements. @@ -17,10 +18,10 @@ This guide goes through details of an orchestrated deployment. Read / try [produ ## CD tests -Before deployment in production zones, [system tests](#system-tests) and [staging tests](#staging-tests) are run. Tests are run in a dedicated and [downsized](/en/operations/environments) environment. These tests are optional, see details in the sections below. Status and logs of ongoing tests can be found in the *Deployment* view in the [Vespa Cloud Console](https://console.vespa-cloud.com/): +Before deployment in production zones, [system tests](#system-tests) and [staging tests](#staging-tests) are run. Tests are run in a dedicated and [downsized](/en/operations/environments) environment. These tests are optional, see details in the sections below. Status and logs of ongoing tests can be found in the _Deployment_ view in the [Vespa Cloud Console](https://console.vespa-cloud.com/): -![Minimal deployment pipeline](/assets/img/deployment-with-system-test.png) + ![Minimal deployment pipeline](/assets/img/deployment-with-system-test.png) These tests are also run during [Vespa Cloud upgrades](#vespa-cloud-upgrades). @@ -42,21 +43,27 @@ Read more about [system tests](/en/applications/testing#system-tests). A staging test verifies the transition of a deployment of a new application package - i.e., from application package `Appold` to `Appnew`. A test suite includes at least one [staging setup](/en/applications/testing#staging-tests), and [staging test](/en/applications/testing#staging-tests). - -All production zone deployments are polled for the current versions. As there can be multiple versions already being deployed (i.e. multiple `Appold`), there can be a series of staging test runs. - - -The application at revision `Appold` is deployed in the [staging environment](/en/operations/environments#staging). - - -The staging setup test code is run, typically making the cluster reasonably similar to a production cluster. - - -The test deployment is then upgraded to application revision `Appnew`. - - -Finally, the staging test code is run, to verify the deployment works as expected after the upgrade. - + + All production zone deployments are polled for the current versions. As + there can be multiple versions already being deployed (i.e. multiple `App + old`), there can be a series of staging test runs. + + + The application at revision `Appold` is deployed in the [staging + environment](/en/operations/environments#staging). + + + The staging setup test code is run, typically making the cluster reasonably + similar to a production cluster. + + + The test deployment is then upgraded to application revision `App + new`. + + + Finally, the staging test code is run, to verify the deployment works as + expected after the upgrade. + An application can be deployed to a production zone without staging tests - this step will then only test that the application starts successfully before and after the change. See [production deployment](/en/reference/applications/deployment) for an example without tests. @@ -67,13 +74,13 @@ Read more about [staging tests](/en/applications/testing#staging-tests). ### Disabling tests -To deploy without testing, remove the test files from the application package. Tests are always run, regardless of *deployment.xml*. +To deploy without testing, remove the test files from the application package. Tests are always run, regardless of _deployment.xml_. To temporarily deploy without testing, run `deploy` and hit the "Abort" button (see illustration above, hover over the test step in the Console) - this skips the test step and makes the orchestration progress to the next step. ### Running tests only -To run a system test, without deploying to any nodes after, add a new test instance. In *deployment.xml*, add the instance without `dev` or`prod` elements, like: +To run a system test, without deploying to any nodes after, add a new test instance. In _deployment.xml_, add the instance without `dev` or`prod` elements, like: ```xml @@ -90,10 +97,11 @@ Make sure to run `vespa prod deploy` to invoke the pipeline for testing, and use ## Deployment orchestration -The *deployment orchestration* is flexible. One can configure dependencies between deployments to production zones, production verification tests, and configured delays; by ordering these in parallel and serial blocks of steps: +The _deployment orchestration_ is flexible. One can configure dependencies between deployments to production zones, production verification tests, and configured delays; by ordering these in parallel and serial blocks of steps: -![Picture of a complex automated deployment](/assets/img/automated-deployments-complex.png) + ![Picture of a complex automated + deployment](/assets/img/automated-deployments-complex.png) ### Pipeline graph @@ -112,22 +120,51 @@ The deployment pipeline is visualized as a graph in the [Vespa Cloud Console](ht - + +